diff --git a/Base/etc/FileIconProvider.ini b/Base/etc/FileIconProvider.ini deleted file mode 100644 index 12fd93d3a0c..00000000000 --- a/Base/etc/FileIconProvider.ini +++ /dev/null @@ -1,38 +0,0 @@ -[Icons] -asm=*.S,*.asm -cmake=*CMakeLists.txt,*CMakeCache.txt -cplusplus=*.cpp,*.cxx,*.cc,*.c++ -c=*.c -font=*.font -form=*.frm -git=*.gitignore,*.gitattributes,*.gitcredentials,*.mailmap,*.gitmodules,*.gitconfig -header=*.h,*.hpp,*.hxx,*.hh,*.h++ -html=*.html,*.htm -css=*.css -php=*.php,*.phtml,*.php3,*.php4,*.php5,*.phps -sql=*.sql,*.dsql -db=*.db,*.sqlite,*.sqlite3 -ini=*.ini -java=*.java -javascript=*.js,*.mjs -json=*.json -wasm=*.wasm -library=*.so,*.so.*,*.a -markdown=*.md -music=*.midi -object=*.o,*.obj -gml=*.gml -palette=*.palette -pdf=*.pdf -presenter=*.presenter -python=*.py -ruby=*.rb -shell=*.sh,*.bash,*.zsh -sound=*.wav,*.flac,*.mp3,*.qoa -spreadsheet=*.sheets,*.csv -calendar=*.cal -text=*.txt -truetype=*.ttf -pixelpaint=*.pp -archive=*.zip,*.gz,*.tar -video=*.avi,*.mkv,*.mov,*.mp4,*.webm diff --git a/Base/etc/Keyboard.ini b/Base/etc/Keyboard.ini deleted file mode 100644 index 7507990563d..00000000000 --- a/Base/etc/Keyboard.ini +++ /dev/null @@ -1,2 +0,0 @@ -[Mapping] -Keymaps=en-us diff --git a/Base/etc/LookupServer.ini b/Base/etc/LookupServer.ini deleted file mode 100644 index 410d02a9fa3..00000000000 --- a/Base/etc/LookupServer.ini +++ /dev/null @@ -1,3 +0,0 @@ -[DNS] -Nameservers=1.1.1.1,1.0.0.1 -EnableServer=false diff --git a/Base/etc/Network.ini b/Base/etc/Network.ini deleted file mode 100644 index 1b117086aa0..00000000000 --- a/Base/etc/Network.ini +++ /dev/null @@ -1,2 +0,0 @@ -[ep1s0] -Enabled=false diff --git a/Base/etc/SystemServer.ini b/Base/etc/SystemServer.ini deleted file mode 100644 index c47f777a352..00000000000 --- a/Base/etc/SystemServer.ini +++ /dev/null @@ -1,64 +0,0 @@ -[NetworkServer] -User=root -SystemModes=text,graphical,self-test - -[LookupServer] -Socket=/tmp/portal/lookup -SocketPermissions=660 -Priority=low -KeepAlive=true -User=lookup -SystemModes=text,graphical,self-test - -[WindowServer] -Socket=/tmp/portal/window,/tmp/portal/wm -SocketPermissions=660 -Priority=high -KeepAlive=true -User=window -# Ensure windowserver has a controlling TTY. -StdIO=/dev/tty0 - -[Clipboard] -Socket=/tmp/session/%sid/portal/clipboard -SocketPermissions=600 -Priority=low -User=window - -[Shell@tty0] -Executable=/bin/Shell -StdIO=/dev/tty0 -Environment=TERM=xterm PATH=/bin:/usr/bin:/usr/local/bin -KeepAlive=true -SystemModes=text - -[Shell@tty1] -Executable=/bin/Shell -StdIO=/dev/tty1 -Environment=TERM=xterm PATH=/bin:/usr/bin:/usr/local/bin -KeepAlive=true -SystemModes=text - -[KeyboardPreferenceLoader] -KeepAlive=false -User=anon - -[TestRunner@ttyS0] -Executable=/home/anon/Tests/run-tests-and-shutdown.sh -StdIO=/dev/ttyS0 -Environment=DO_SHUTDOWN_AFTER_TESTS=1 TERM=xterm PATH=/bin:/usr/bin:/usr/local/bin -User=anon -WorkingDirectory=/home/anon -SystemModes=self-test - -[GenerateManpages@ttyS0] -Executable=/root/generate_manpages.sh -StdIO=/dev/ttyS0 -Environment=DO_SHUTDOWN_AFTER_GENERATE=1 TERM=xterm PATH=/bin -User=root -WorkingDirectory=/root/ -SystemModes=generate-manpages - -[LoginServer] -User=root -Arguments=--auto-login anon diff --git a/Base/etc/WindowServer.ini b/Base/etc/WindowServer.ini deleted file mode 100644 index 4055cdbf425..00000000000 --- a/Base/etc/WindowServer.ini +++ /dev/null @@ -1,43 +0,0 @@ -[Screens] -MainScreen=0 - -[Screen0] -Mode=Device -Device=/dev/gpu/connector0 -Left=0 -Top=0 -Width=1024 -Height=768 -ScaleFactor=1 - -[Fonts] -Default=Katica 10 400 0 -WindowTitle=Katica 10 700 0 -FixedWidth=Csilla 10 400 0 - -[Theme] -Name=Default -LoadCustomColorScheme=false - -[Mouse] -AccelerationFactor=1.0 -ScrollStepSize=4 -CursorTheme=Default -ButtonsSwitched=false -NaturalScroll=false - -[Graphics] -OverlayRectShadow=/res/graphics/overlay-rect-shadow.png - -[Input] -DoubleClickSpeed=250 - -[Background] -Mode=Stretch - -[Applet] -Order=WorkspacePicker,CPUGraph,MemoryGraph,NetworkGraph,Network,ClipboardHistory,Audio,Keymap - -[Workspaces] -Rows=2 -Columns=2 diff --git a/Base/etc/fstab b/Base/etc/fstab deleted file mode 100644 index 8a9a086bac9..00000000000 --- a/Base/etc/fstab +++ /dev/null @@ -1,14 +0,0 @@ -# Root file system. This is a fake entry which gets ignored by `mount -a`; -# the actual logic for mounting root is in the kernel. -/dev/hda / ext2 nodev,nosuid,ro -# Remount /bin, /root, and /home while adding the appropriate permissions. -/bin /bin bind bind,nodev,ro -/etc /etc bind bind,nodev,nosuid -/home /home bind bind,nodev,nosuid -/root /root bind bind,nodev,nosuid -/var /var bind bind,nodev,nosuid -/www /www bind bind,nodev,nosuid -/usr/Tests /usr/Tests bind bind,nodev,ro -/usr/local /usr/local bind bind,nodev,nosuid -/usr/Ports /usr/Ports bind bind,nodev,nosuid - diff --git a/Base/etc/group b/Base/etc/group deleted file mode 100644 index 085f87349d1..00000000000 --- a/Base/etc/group +++ /dev/null @@ -1,9 +0,0 @@ -root:x:0: -wheel:x:1:anon -tty:x:2: -phys:x:3:window,anon -audio:x:4:anon -utmp:x:5: -lookup:x:10:anon -window:x:13:anon -users:x:100:anon diff --git a/Base/etc/hosts b/Base/etc/hosts deleted file mode 100644 index ba712fe0334..00000000000 --- a/Base/etc/hosts +++ /dev/null @@ -1 +0,0 @@ -127.0.0.1 localhost diff --git a/Base/etc/passwd b/Base/etc/passwd deleted file mode 100644 index a1a7a5597fe..00000000000 --- a/Base/etc/passwd +++ /dev/null @@ -1,6 +0,0 @@ -root::0:0:root:/root:/bin/Shell -lookup:!:10:10:LookupServer,,,:/:/bin/false -window:!:13:13:WindowServer,,,:/:/bin/false -sshd:!:19:19:OpenSSH privsep,,,:/:/bin/false -anon:!:100:100:Anonymous,,,:/home/anon:/bin/Shell -nona:!:200:100:Nona,,,:/home/nona:/bin/Shell diff --git a/Base/etc/posixshrc b/Base/etc/posixshrc deleted file mode 100644 index 57d9c8c59e8..00000000000 --- a/Base/etc/posixshrc +++ /dev/null @@ -1,37 +0,0 @@ -#!sh - -alias fm=FileManager -alias mag=Magnifier -alias sh=Shell -alias tb=Taskbar -alias te=TextEditor -alias he=HexEditor -alias pp=PixelPaint -alias iv=ImageViewer -alias pi=Piano -alias calc=Calculator -alias calendar=Calendar -alias sp=SoundPlayer -alias help=Help -alias br=Browser -alias hs=HackStudio -alias sdb=Debugger -alias sm=SystemMonitor -alias pv=Profiler -alias ws=WebServer -alias fe=FontEditor -alias ss=Spreadsheet -alias vp=VideoPlayer - -alias ll='ls -l' - -if [ "$(id -u)" = "0" ]; then - prompt_color=31 -else - prompt_color=32 -fi - -export PROMPT="\\X\\u@\\h:\\w\\a\\e[$prompt_color;1m\\h\\e[0m:\\e[34;1m\\w\\e[0m \\p " -export TMPDIR=/tmp - -PROGRAMS_ALLOWED_TO_MODIFY_DEFAULT_TERMIOS="stty" diff --git a/Base/etc/protocols b/Base/etc/protocols deleted file mode 100644 index 1f112b16687..00000000000 --- a/Base/etc/protocols +++ /dev/null @@ -1,64 +0,0 @@ -# Internet (IP) protocols -# -# Updated from http://www.iana.org/assignments/protocol-numbers and other -# sources. -# New protocols will be added on request if they have been officially -# assigned by IANA and are not historical. -# If you need a huge list of used numbers please install the nmap package. - -ip 0 IP # internet protocol, pseudo protocol number -hopopt 0 HOPOPT # IPv6 Hop-by-Hop Option [RFC1883] -icmp 1 ICMP # internet control message protocol -igmp 2 IGMP # Internet Group Management -ggp 3 GGP # gateway-gateway protocol -ipencap 4 IP-ENCAP # IP encapsulated in IP (officially ``IP'') -st 5 ST # ST datagram mode -tcp 6 TCP # transmission control protocol -egp 8 EGP # exterior gateway protocol -igp 9 IGP # any private interior gateway (Cisco) -pup 12 PUP # PARC universal packet protocol -udp 17 UDP # user datagram protocol -hmp 20 HMP # host monitoring protocol -xns-idp 22 XNS-IDP # Xerox NS IDP -rdp 27 RDP # "reliable datagram" protocol -iso-tp4 29 ISO-TP4 # ISO Transport Protocol class 4 [RFC905] -dccp 33 DCCP # Datagram Congestion Control Prot. [RFC4340] -xtp 36 XTP # Xpress Transfer Protocol -ddp 37 DDP # Datagram Delivery Protocol -idpr-cmtp 38 IDPR-CMTP # IDPR Control Message Transport -ipv6 41 IPv6 # Internet Protocol, version 6 -ipv6-route 43 IPv6-Route # Routing Header for IPv6 -ipv6-frag 44 IPv6-Frag # Fragment Header for IPv6 -idrp 45 IDRP # Inter-Domain Routing Protocol -rsvp 46 RSVP # Reservation Protocol -gre 47 GRE # General Routing Encapsulation -esp 50 IPSEC-ESP # Encap Security Payload [RFC2406] -ah 51 IPSEC-AH # Authentication Header [RFC2402] -skip 57 SKIP # SKIP -ipv6-icmp 58 IPv6-ICMP # ICMP for IPv6 -ipv6-nonxt 59 IPv6-NoNxt # No Next Header for IPv6 -ipv6-opts 60 IPv6-Opts # Destination Options for IPv6 -rspf 73 RSPF CPHB # Radio Shortest Path First (officially CPHB) -vmtp 81 VMTP # Versatile Message Transport -eigrp 88 EIGRP # Enhanced Interior Routing Protocol (Cisco) -ospf 89 OSPFIGP # Open Shortest Path First IGP -ax.25 93 AX.25 # AX.25 frames -ipip 94 IPIP # IP-within-IP Encapsulation Protocol -etherip 97 ETHERIP # Ethernet-within-IP Encapsulation [RFC3378] -encap 98 ENCAP # Yet Another IP encapsulation [RFC1241] -# 99 # any private encryption scheme -pim 103 PIM # Protocol Independent Multicast -ipcomp 108 IPCOMP # IP Payload Compression Protocol -vrrp 112 VRRP # Virtual Router Redundancy Protocol [RFC5798] -l2tp 115 L2TP # Layer Two Tunneling Protocol [RFC2661] -isis 124 ISIS # IS-IS over IPv4 -sctp 132 SCTP # Stream Control Transmission Protocol -fc 133 FC # Fibre Channel -mobility-header 135 Mobility-Header # Mobility Support for IPv6 [RFC3775] -udplite 136 UDPLite # UDP-Lite [RFC3828] -mpls-in-ip 137 MPLS-in-IP # MPLS-in-IP [RFC4023] -manet 138 # MANET Protocols [RFC5498] -hip 139 HIP # Host Identity Protocol -shim6 140 Shim6 # Shim6 Protocol [RFC5533] -wesp 141 WESP # Wrapped Encapsulating Security Payload -rohc 142 ROHC # Robust Header Compression diff --git a/Base/etc/services b/Base/etc/services deleted file mode 100644 index a65870775be..00000000000 --- a/Base/etc/services +++ /dev/null @@ -1,578 +0,0 @@ -# Network services, Internet style -# -# Note that it is presently the policy of IANA to assign a single well-known -# port number for both TCP and UDP; hence, officially ports have two entries -# even if the protocol doesn't support UDP operations. -# -# Updated from https://www.iana.org/assignments/service-names-port-numbers/service-names-port-numbers.xhtml . -# -# New ports will be added on request if they have been officially assigned -# by IANA and used in the real-world or are needed by a debian package. -# If you need a huge list of used numbers please install the nmap package. - -tcpmux 1/tcp # TCP port service multiplexer -echo 7/tcp -echo 7/udp -discard 9/tcp sink null -discard 9/udp sink null -systat 11/tcp users -daytime 13/tcp -daytime 13/udp -netstat 15/tcp -qotd 17/tcp quote -msp 18/tcp # message send protocol -msp 18/udp -chargen 19/tcp ttytst source -chargen 19/udp ttytst source -ftp-data 20/tcp -ftp 21/tcp -fsp 21/udp fspd -ssh 22/tcp # SSH Remote Login Protocol -telnet 23/tcp -smtp 25/tcp mail -time 37/tcp timserver -time 37/udp timserver -rlp 39/udp resource # resource location -nameserver 42/tcp name # IEN 116 -whois 43/tcp nicname -tacacs 49/tcp # Login Host Protocol (TACACS) -tacacs 49/udp -domain 53/tcp # Domain Name Server -domain 53/udp -bootps 67/udp -bootpc 68/udp -tftp 69/udp -gopher 70/tcp # Internet Gopher -finger 79/tcp -http 80/tcp www # WorldWideWeb HTTP -link 87/tcp ttylink -kerberos 88/tcp kerberos5 krb5 kerberos-sec # Kerberos v5 -kerberos 88/udp kerberos5 krb5 kerberos-sec # Kerberos v5 -iso-tsap 102/tcp tsap # part of ISODE -acr-nema 104/tcp dicom # Digital Imag. & Comm. 300 -pop3 110/tcp pop-3 # POP version 3 -sunrpc 111/tcp portmapper # RPC 4.0 portmapper -sunrpc 111/udp portmapper -auth 113/tcp authentication tap ident -sftp 115/tcp -nntp 119/tcp readnews untp # USENET News Transfer Protocol -ntp 123/udp # Network Time Protocol -epmap 135/tcp loc-srv # DCE endpoint resolution -epmap 135/udp loc-srv -netbios-ns 137/tcp # NETBIOS Name Service -netbios-ns 137/udp -netbios-dgm 138/tcp # NETBIOS Datagram Service -netbios-dgm 138/udp -netbios-ssn 139/tcp # NETBIOS session service -netbios-ssn 139/udp -imap2 143/tcp imap # Interim Mail Access P 2 and 4 -snmp 161/tcp # Simple Net Mgmt Protocol -snmp 161/udp -snmp-trap 162/tcp snmptrap # Traps for SNMP -snmp-trap 162/udp snmptrap -cmip-man 163/tcp # ISO mgmt over IP (CMOT) -cmip-man 163/udp -cmip-agent 164/tcp -cmip-agent 164/udp -mailq 174/tcp # Mailer transport queue for Zmailer -mailq 174/udp -xdmcp 177/tcp # X Display Mgr. Control Proto -xdmcp 177/udp -nextstep 178/tcp NeXTStep NextStep # NeXTStep window -nextstep 178/udp NeXTStep NextStep # server -bgp 179/tcp # Border Gateway Protocol -irc 194/tcp # Internet Relay Chat -irc 194/udp -smux 199/tcp # SNMP Unix Multiplexer -smux 199/udp -at-rtmp 201/tcp # AppleTalk routing -at-rtmp 201/udp -at-nbp 202/tcp # AppleTalk name binding -at-nbp 202/udp -at-echo 204/tcp # AppleTalk echo -at-echo 204/udp -at-zis 206/tcp # AppleTalk zone information -at-zis 206/udp -qmtp 209/tcp # Quick Mail Transfer Protocol -qmtp 209/udp -z3950 210/tcp wais # NISO Z39.50 database -z3950 210/udp wais -ipx 213/tcp # IPX -ipx 213/udp -ptp-event 319/udp -ptp-general 320/udp -pawserv 345/tcp # Perf Analysis Workbench -pawserv 345/udp -zserv 346/tcp # Zebra server -zserv 346/udp -fatserv 347/tcp # Fatmen Server -fatserv 347/udp -rpc2portmap 369/tcp -rpc2portmap 369/udp # Coda portmapper -codaauth2 370/tcp -codaauth2 370/udp # Coda authentication server -clearcase 371/tcp Clearcase -clearcase 371/udp Clearcase -ulistserv 372/tcp # UNIX Listserv -ulistserv 372/udp -ldap 389/tcp # Lightweight Directory Access Protocol -ldap 389/udp -imsp 406/tcp # Interactive Mail Support Protocol -imsp 406/udp -svrloc 427/tcp # Server Location -svrloc 427/udp -https 443/tcp # http protocol over TLS/SSL -snpp 444/tcp # Simple Network Paging Protocol -snpp 444/udp -microsoft-ds 445/tcp # Microsoft Naked CIFS -microsoft-ds 445/udp -kpasswd 464/tcp -kpasswd 464/udp -submissions 465/tcp ssmtp smtps urd # Submission over TLS [RFC8314] -saft 487/tcp # Simple Asynchronous File Transfer -saft 487/udp -isakmp 500/tcp # IPsec - Internet Security Association -isakmp 500/udp # and Key Management Protocol -rtsp 554/tcp # Real Time Stream Control Protocol -rtsp 554/udp -nqs 607/tcp # Network Queuing system -nqs 607/udp -npmp-local 610/tcp dqs313_qmaster # npmp-local / DQS -npmp-local 610/udp dqs313_qmaster -npmp-gui 611/tcp dqs313_execd # npmp-gui / DQS -npmp-gui 611/udp dqs313_execd -hmmp-ind 612/tcp dqs313_intercell # HMMP Indication / DQS -hmmp-ind 612/udp dqs313_intercell -asf-rmcp 623/udp # ASF Remote Management and Control Protocol -qmqp 628/tcp -qmqp 628/udp -ipp 631/tcp # Internet Printing Protocol -ipp 631/udp -# -# UNIX specific services -# -exec 512/tcp -biff 512/udp comsat -login 513/tcp -who 513/udp whod -shell 514/tcp cmd # no passwords used -syslog 514/udp -printer 515/tcp spooler # line printer spooler -talk 517/udp -ntalk 518/udp -route 520/udp router routed # RIP -timed 525/udp timeserver -tempo 526/tcp newdate -courier 530/tcp rpc -conference 531/tcp chat -netnews 532/tcp readnews -netwall 533/udp # for emergency broadcasts -gdomap 538/tcp # GNUstep distributed objects -gdomap 538/udp -uucp 540/tcp uucpd # uucp daemon -klogin 543/tcp # Kerberized `rlogin' (v5) -kshell 544/tcp krcmd # Kerberized `rsh' (v5) -dhcpv6-client 546/tcp -dhcpv6-client 546/udp -dhcpv6-server 547/tcp -dhcpv6-server 547/udp -afpovertcp 548/tcp # AFP over TCP -afpovertcp 548/udp -idfp 549/tcp -idfp 549/udp -remotefs 556/tcp rfs_server rfs # Brunhoff remote filesystem -nntps 563/tcp snntp # NNTP over SSL -submission 587/tcp # Submission [RFC4409] -ldaps 636/tcp # LDAP over SSL -ldaps 636/udp -tinc 655/tcp # tinc control port -tinc 655/udp -silc 706/tcp -silc 706/udp -kerberos-adm 749/tcp # Kerberos `kadmin' (v5) -# -webster 765/tcp # Network dictionary -webster 765/udp -domain-s 853/tcp # DNS over TLS [RFC7858] -domain-s 853/udp # DNS over DTLS [RFC8094] -rsync 873/tcp -ftps-data 989/tcp # FTP over SSL (data) -ftps 990/tcp -telnets 992/tcp # Telnet over SSL -imaps 993/tcp # IMAP over SSL -pop3s 995/tcp # POP-3 over SSL -# -# From ``Assigned Numbers'': -# -#> The Registered Ports are not controlled by the IANA and on most systems -#> can be used by ordinary user processes or programs executed by ordinary -#> users. -# -#> Ports are used in the TCP [45,106] to name the ends of logical -#> connections which carry long term conversations. For the purpose of -#> providing services to unknown callers, a service contact port is -#> defined. This list specifies the port used by the server process as its -#> contact port. While the IANA can not control uses of these ports it -#> does register or list uses of these ports as a convenience to the -#> community. -# -socks 1080/tcp # socks proxy server -socks 1080/udp -proofd 1093/tcp -proofd 1093/udp -rootd 1094/tcp -rootd 1094/udp -openvpn 1194/tcp -openvpn 1194/udp -rmiregistry 1099/tcp # Java RMI Registry -rmiregistry 1099/udp -kazaa 1214/tcp -kazaa 1214/udp -nessus 1241/tcp # Nessus vulnerability -nessus 1241/udp # assessment scanner -lotusnote 1352/tcp lotusnotes # Lotus Note -lotusnote 1352/udp lotusnotes -ms-sql-s 1433/tcp # Microsoft SQL Server -ms-sql-s 1433/udp -ms-sql-m 1434/tcp # Microsoft SQL Monitor -ms-sql-m 1434/udp -ingreslock 1524/tcp -ingreslock 1524/udp -datametrics 1645/tcp old-radius -datametrics 1645/udp old-radius -sa-msg-port 1646/tcp old-radacct -sa-msg-port 1646/udp old-radacct -kermit 1649/tcp -kermit 1649/udp -groupwise 1677/tcp -groupwise 1677/udp -l2f 1701/tcp l2tp -l2f 1701/udp l2tp -radius 1812/tcp -radius 1812/udp -radius-acct 1813/tcp radacct # Radius Accounting -radius-acct 1813/udp radacct -msnp 1863/tcp # MSN Messenger -msnp 1863/udp -unix-status 1957/tcp # remstats unix-status server -log-server 1958/tcp # remstats log server -remoteping 1959/tcp # remstats remoteping server -cisco-sccp 2000/tcp # Cisco SCCP -cisco-sccp 2000/udp -search 2010/tcp ndtp -pipe-server 2010/tcp pipe_server -nfs 2049/tcp # Network File System -nfs 2049/udp # Network File System -gnunet 2086/tcp -gnunet 2086/udp -rtcm-sc104 2101/tcp # RTCM SC-104 IANA 1/29/99 -rtcm-sc104 2101/udp -gsigatekeeper 2119/tcp -gsigatekeeper 2119/udp -gris 2135/tcp # Grid Resource Information Server -gris 2135/udp -cvspserver 2401/tcp # CVS client/server operations -cvspserver 2401/udp -venus 2430/tcp # codacon port -venus 2430/udp # Venus callback/wbc interface -venus-se 2431/tcp # tcp side effects -venus-se 2431/udp # udp sftp side effect -codasrv 2432/tcp # not used -codasrv 2432/udp # server port -codasrv-se 2433/tcp # tcp side effects -codasrv-se 2433/udp # udp sftp side effect -mon 2583/tcp # MON traps -mon 2583/udp -dict 2628/tcp # Dictionary server -dict 2628/udp -f5-globalsite 2792/tcp -f5-globalsite 2792/udp -gsiftp 2811/tcp -gsiftp 2811/udp -gpsd 2947/tcp -gpsd 2947/udp -gds-db 3050/tcp gds_db # InterBase server -gds-db 3050/udp gds_db -icpv2 3130/tcp icp # Internet Cache Protocol -icpv2 3130/udp icp -isns 3205/tcp # iSNS Server Port -isns 3205/udp # iSNS Server Port -iscsi-target 3260/tcp -mysql 3306/tcp -mysql 3306/udp -nut 3493/tcp # Network UPS Tools -nut 3493/udp -distcc 3632/tcp # distributed compiler -distcc 3632/udp -daap 3689/tcp # Digital Audio Access Protocol -daap 3689/udp -svn 3690/tcp subversion # Subversion protocol -svn 3690/udp subversion -suucp 4031/tcp # UUCP over SSL -suucp 4031/udp -sysrqd 4094/tcp # sysrq daemon -sysrqd 4094/udp -sieve 4190/tcp # ManageSieve Protocol -epmd 4369/tcp # Erlang Port Mapper Daemon -epmd 4369/udp -remctl 4373/tcp # Remote Authenticated Command Service -remctl 4373/udp -f5-iquery 4353/tcp # F5 iQuery -f5-iquery 4353/udp -ipsec-nat-t 4500/udp # IPsec NAT-Traversal [RFC3947] -iax 4569/tcp # Inter-Asterisk eXchange -iax 4569/udp -mtn 4691/tcp # monotone Netsync Protocol -mtn 4691/udp -radmin-port 4899/tcp # RAdmin Port -radmin-port 4899/udp -rfe 5002/udp # Radio Free Ethernet -rfe 5002/tcp -mmcc 5050/tcp # multimedia conference control tool (Yahoo IM) -mmcc 5050/udp -sip 5060/tcp # Session Initiation Protocol -sip 5060/udp -sip-tls 5061/tcp -sip-tls 5061/udp -aol 5190/tcp # AIM -aol 5190/udp -xmpp-client 5222/tcp jabber-client # Jabber Client Connection -xmpp-client 5222/udp jabber-client -xmpp-server 5269/tcp jabber-server # Jabber Server Connection -xmpp-server 5269/udp jabber-server -cfengine 5308/tcp -cfengine 5308/udp -mdns 5353/tcp # Multicast DNS -mdns 5353/udp -postgresql 5432/tcp postgres # PostgreSQL Database -postgresql 5432/udp postgres -freeciv 5556/tcp rptp # Freeciv gameplay -freeciv 5556/udp -amqps 5671/tcp # AMQP protocol over TLS/SSL -amqp 5672/tcp -amqp 5672/udp -amqp 5672/sctp -ggz 5688/tcp # GGZ Gaming Zone -ggz 5688/udp -x11 6000/tcp x11-0 # X Window System -x11 6000/udp x11-0 -x11-1 6001/tcp -x11-1 6001/udp -x11-2 6002/tcp -x11-2 6002/udp -x11-3 6003/tcp -x11-3 6003/udp -x11-4 6004/tcp -x11-4 6004/udp -x11-5 6005/tcp -x11-5 6005/udp -x11-6 6006/tcp -x11-6 6006/udp -x11-7 6007/tcp -x11-7 6007/udp -gnutella-svc 6346/tcp # gnutella -gnutella-svc 6346/udp -gnutella-rtr 6347/tcp # gnutella -gnutella-rtr 6347/udp -sge-qmaster 6444/tcp sge_qmaster # Grid Engine Qmaster Service -sge-qmaster 6444/udp sge_qmaster -sge-execd 6445/tcp sge_execd # Grid Engine Execution Service -sge-execd 6445/udp sge_execd -mysql-proxy 6446/tcp # MySQL Proxy -mysql-proxy 6446/udp -babel 6696/udp # Babel Routing Protocol -ircs-u 6697/tcp # Internet Relay Chat via TLS/SSL -afs3-fileserver 7000/tcp bbs # file server itself -afs3-fileserver 7000/udp bbs -afs3-callback 7001/tcp # callbacks to cache managers -afs3-callback 7001/udp -afs3-prserver 7002/tcp # users & groups database -afs3-prserver 7002/udp -afs3-vlserver 7003/tcp # volume location database -afs3-vlserver 7003/udp -afs3-kaserver 7004/tcp # AFS/Kerberos authentication -afs3-kaserver 7004/udp -afs3-volser 7005/tcp # volume management server -afs3-volser 7005/udp -afs3-errors 7006/tcp # error interpretation service -afs3-errors 7006/udp -afs3-bos 7007/tcp # basic overseer process -afs3-bos 7007/udp -afs3-update 7008/tcp # server-to-server updater -afs3-update 7008/udp -afs3-rmtsys 7009/tcp # remote cache manager service -afs3-rmtsys 7009/udp -font-service 7100/tcp xfs # X Font Service -font-service 7100/udp xfs -http-alt 8080/tcp webcache # WWW caching service -http-alt 8080/udp -puppet 8140/tcp # The Puppet master service -bacula-dir 9101/tcp # Bacula Director -bacula-dir 9101/udp -bacula-fd 9102/tcp # Bacula File Daemon -bacula-fd 9102/udp -bacula-sd 9103/tcp # Bacula Storage Daemon -bacula-sd 9103/udp -xmms2 9667/tcp # Cross-platform Music Multiplexing System -xmms2 9667/udp -nbd 10809/tcp # Linux Network Block Device -zabbix-agent 10050/tcp # Zabbix Agent -zabbix-agent 10050/udp -zabbix-trapper 10051/tcp # Zabbix Trapper -zabbix-trapper 10051/udp -amanda 10080/tcp # amanda backup services -amanda 10080/udp -dicom 11112/tcp -hkp 11371/tcp # OpenPGP HTTP Keyserver -hkp 11371/udp -bprd 13720/tcp # VERITAS NetBackup -bprd 13720/udp -bpdbm 13721/tcp # VERITAS NetBackup -bpdbm 13721/udp -bpjava-msvc 13722/tcp # BP Java MSVC Protocol -bpjava-msvc 13722/udp -vnetd 13724/tcp # Veritas Network Utility -vnetd 13724/udp -bpcd 13782/tcp # VERITAS NetBackup -bpcd 13782/udp -vopied 13783/tcp # VERITAS NetBackup -vopied 13783/udp -db-lsp 17500/tcp # Dropbox LanSync Protocol -dcap 22125/tcp # dCache Access Protocol -gsidcap 22128/tcp # GSI dCache Access Protocol -wnn6 22273/tcp # wnn6 -wnn6 22273/udp - -# -# Datagram Delivery Protocol services -# -rtmp 1/ddp # Routing Table Maintenance Protocol -nbp 2/ddp # Name Binding Protocol -echo 4/ddp # AppleTalk Echo Protocol -zip 6/ddp # Zone Information Protocol - -#========================================================================= -# The remaining port numbers are not as allocated by IANA. -#========================================================================= - -# Kerberos (Project Athena/MIT) services -# Note that these are for Kerberos v4, and are unofficial. Sites running -# v4 should uncomment these and comment out the v5 entries above. -# -kerberos4 750/udp kerberos-iv kdc # Kerberos (server) -kerberos4 750/tcp kerberos-iv kdc -kerberos-master 751/udp kerberos_master # Kerberos authentication -kerberos-master 751/tcp -passwd-server 752/udp passwd_server # Kerberos passwd server -krb-prop 754/tcp krb_prop krb5_prop hprop # Kerberos slave propagation -krbupdate 760/tcp kreg # Kerberos registration -swat 901/tcp # swat -kpop 1109/tcp # Pop with Kerberos -knetd 2053/tcp # Kerberos de-multiplexor -zephyr-srv 2102/udp # Zephyr server -zephyr-clt 2103/udp # Zephyr serv-hm connection -zephyr-hm 2104/udp # Zephyr hostmanager -eklogin 2105/tcp # Kerberos encrypted rlogin -# Hmmm. Are we using Kv4 or Kv5 now? Worrying. -# The following is probably Kerberos v5 --- ajt@debian.org (11/02/2000) -kx 2111/tcp # X over Kerberos -iprop 2121/tcp # incremental propagation -# -# Unofficial but necessary (for NetBSD) services -# -supfilesrv 871/tcp # SUP server -supfiledbg 1127/tcp # SUP debugging - -# -# Services added for the Debian GNU/Linux distribution -# -poppassd 106/tcp # Eudora -poppassd 106/udp -moira-db 775/tcp moira_db # Moira database -moira-update 777/tcp moira_update # Moira update protocol -moira-ureg 779/udp moira_ureg # Moira user registration -spamd 783/tcp # spamassassin daemon -omirr 808/tcp omirrd # online mirror -omirr 808/udp omirrd -customs 1001/tcp # pmake customs server -customs 1001/udp -skkserv 1178/tcp # skk jisho server port -predict 1210/udp # predict -- satellite tracking -rmtcfg 1236/tcp # Gracilis Packeten remote config server -wipld 1300/tcp # Wipl network monitor -xtel 1313/tcp # french minitel -xtelw 1314/tcp # french minitel -support 1529/tcp # GNATS -cfinger 2003/tcp # GNU Finger -frox 2121/tcp # frox: caching ftp proxy -ninstall 2150/tcp # ninstall service -ninstall 2150/udp -zebrasrv 2600/tcp # zebra service -zebra 2601/tcp # zebra vty -ripd 2602/tcp # ripd vty (zebra) -ripngd 2603/tcp # ripngd vty (zebra) -ospfd 2604/tcp # ospfd vty (zebra) -bgpd 2605/tcp # bgpd vty (zebra) -ospf6d 2606/tcp # ospf6d vty (zebra) -ospfapi 2607/tcp # OSPF-API -isisd 2608/tcp # ISISd vty (zebra) -afbackup 2988/tcp # Afbackup system -afbackup 2988/udp -afmbackup 2989/tcp # Afmbackup system -afmbackup 2989/udp -xtell 4224/tcp # xtell server -fax 4557/tcp # FAX transmission service (old) -hylafax 4559/tcp # HylaFAX client-server protocol (new) -distmp3 4600/tcp # distmp3host daemon -munin 4949/tcp lrrd # Munin -enbd-cstatd 5051/tcp # ENBD client statd -enbd-sstatd 5052/tcp # ENBD server statd -pcrd 5151/tcp # PCR-1000 Daemon -noclog 5354/tcp # noclogd with TCP (nocol) -noclog 5354/udp # noclogd with UDP (nocol) -hostmon 5355/tcp # hostmon uses TCP (nocol) -hostmon 5355/udp # hostmon uses UDP (nocol) -rplay 5555/udp # RPlay audio service -nrpe 5666/tcp # Nagios Remote Plugin Executor -nsca 5667/tcp # Nagios Agent - NSCA -mrtd 5674/tcp # MRT Routing Daemon -bgpsim 5675/tcp # MRT Routing Simulator -canna 5680/tcp # cannaserver -syslog-tls 6514/tcp # Syslog over TLS [RFC5425] -sane-port 6566/tcp sane saned # SANE network scanner daemon -ircd 6667/tcp # Internet Relay Chat -zope-ftp 8021/tcp # zope management by ftp -tproxy 8081/tcp # Transparent Proxy -omniorb 8088/tcp # OmniORB -omniorb 8088/udp -clc-build-daemon 8990/tcp # Common lisp build daemon -xinetd 9098/tcp -mandelspawn 9359/udp mandelbrot # network mandelbrot -git 9418/tcp # Git Version Control System -zope 9673/tcp # zope server -webmin 10000/tcp -kamanda 10081/tcp # amanda backup services (Kerberos) -kamanda 10081/udp -amandaidx 10082/tcp # amanda backup services -amidxtape 10083/tcp # amanda backup services -smsqp 11201/tcp # Alamin SMS gateway -smsqp 11201/udp -xpilot 15345/tcp # XPilot Contact Port -xpilot 15345/udp -sgi-cmsd 17001/udp # Cluster membership services daemon -sgi-crsd 17002/udp -sgi-gcd 17003/udp # SGI Group membership daemon -sgi-cad 17004/tcp # Cluster Admin daemon -isdnlog 20011/tcp # isdn logging system -isdnlog 20011/udp -vboxd 20012/tcp # voice box system -vboxd 20012/udp -binkp 24554/tcp # binkp fidonet protocol -asp 27374/tcp # Address Search Protocol -asp 27374/udp -csync2 30865/tcp # cluster synchronization tool -dircproxy 57000/tcp # Detachable IRC Proxy -tfido 60177/tcp # fidonet EMSI over telnet -fido 60179/tcp # fidonet EMSI over TCP - -# Local services diff --git a/Base/etc/shadow b/Base/etc/shadow deleted file mode 100644 index 7e26c6832c8..00000000000 --- a/Base/etc/shadow +++ /dev/null @@ -1,6 +0,0 @@ -root::18727:::::: -lookup:!*:18727:::::: -window:!*:18727:::::: -sshd:!*:18727:::::: -anon:$5$zFiQBeTD88m/mhbU$ecHDSdRd5yNV45BzIRXwtRpxJtMpVI5twjRRXO8X03Q=:18727:0:99999:::: -nona:!*:18727:::::: diff --git a/Base/etc/shellrc b/Base/etc/shellrc deleted file mode 100644 index bdd7b90307f..00000000000 --- a/Base/etc/shellrc +++ /dev/null @@ -1,42 +0,0 @@ -#!sh - -alias fm=FileManager -alias mag=Magnifier -alias sh=Shell -alias tb=Taskbar -alias te=TextEditor -alias he=HexEditor -alias pp=PixelPaint -alias iv=ImageViewer -alias pi=Piano -alias calc=Calculator -alias calendar=Calendar -alias sp=SoundPlayer -alias help=Help -alias br=Browser -alias hs=HackStudio -alias sdb=Debugger -alias sm=SystemMonitor -alias pv=Profiler -alias ws=WebServer -alias fe=FontEditor -alias ss=Spreadsheet -alias vp=VideoPlayer - -alias ll='ls -l' - -if [ "$(id -u)" = "0" ] { - prompt_color=31 -} else { - prompt_color=32 -} - -export PROMPT="\\X\\u@\\h:\\w\\a\\e[$prompt_color;1m\\h\\e[0m:\\e[34;1m\\w\\e[0m \\p " - -export HISTORY_AUTOSAVE_TIME_MS=10000 - -export TMPDIR=/tmp - -PROGRAMS_ALLOWED_TO_MODIFY_DEFAULT_TERMIOS=(stty) - -for /usr/share/shell/completion/*.sh { source $it } diff --git a/Base/etc/timezone b/Base/etc/timezone deleted file mode 100644 index e2e77758157..00000000000 --- a/Base/etc/timezone +++ /dev/null @@ -1 +0,0 @@ -UTC diff --git a/Base/home/anon/.config/CommonLocations.json b/Base/home/anon/.config/CommonLocations.json deleted file mode 100644 index e6ce318fc50..00000000000 --- a/Base/home/anon/.config/CommonLocations.json +++ /dev/null @@ -1,10 +0,0 @@ -[ - { "name": "Root", "path": "/" }, - { "name": "Home", "path": "/home/anon" }, - { "name": "Desktop", "path": "/home/anon/Desktop" }, - { "name": "Documents", "path": "/home/anon/Documents" }, - { "name": "Downloads", "path": "/home/anon/Downloads" }, - { "name": "Music", "path": "/home/anon/Music" }, - { "name": "Pictures", "path": "/home/anon/Pictures" }, - { "name": "Videos", "path": "/home/anon/Videos" } -] diff --git a/Base/home/anon/.config/FileManager.ini b/Base/home/anon/.config/FileManager.ini deleted file mode 100644 index 1dc569206d6..00000000000 --- a/Base/home/anon/.config/FileManager.ini +++ /dev/null @@ -1,9 +0,0 @@ -[DirectoryView] -ViewMode=Icon -ShowDotFiles=false - -[Layout] -ShowToolbar=true -ShowStatusBar=true -ShowLocationBar=true -ShowFolderPane=true diff --git a/Base/home/anon/.config/FontEditor.ini b/Base/home/anon/.config/FontEditor.ini deleted file mode 100644 index 52de60e007a..00000000000 --- a/Base/home/anon/.config/FontEditor.ini +++ /dev/null @@ -1,2 +0,0 @@ -[Defaults] -Font=/res/fonts/KaticaRegular10.font diff --git a/Base/home/anon/.config/LaunchServer.ini b/Base/home/anon/.config/LaunchServer.ini deleted file mode 100644 index cd9dcf4fc8c..00000000000 --- a/Base/home/anon/.config/LaunchServer.ini +++ /dev/null @@ -1,39 +0,0 @@ -[FileType] -dds=/bin/ImageViewer -pbm=/bin/ImageViewer -pgm=/bin/ImageViewer -png=/bin/ImageViewer -ppm=/bin/ImageViewer -gif=/bin/ImageViewer -bmp=/bin/ImageViewer -jpg=/bin/ImageViewer -jpeg=/bin/ImageViewer -jxl=/bin/ImageViewer -qoi=/bin/ImageViewer -webp=/bin/ImageViewer -tga=/bin/ImageViewer -tvg=/bin/ImageViewer -ico=/bin/ImageViewer -html=/bin/Browser -wav=/bin/SoundPlayer -flac=/bin/SoundPlayer -m3u=/bin/SoundPlayer -m3u8=/bin/SoundPlayer -mp3=/bin/SoundPlayer -qoa=/bin/SoundPlayer -txt=/bin/TextEditor -font=/bin/FontEditor -sheets=/bin/Spreadsheet -csv=/bin/Spreadsheet -gml=/bin/GMLPlayground -pdf=/bin/PDFViewer -profile=/bin/Profiler -pp=/bin/PixelPaint -directory=/bin/FileManager -*=/bin/TextEditor - -[Protocol] -gemini=/bin/Browser -http=/bin/Browser -https=/bin/Browser -help=/bin/Help diff --git a/Base/home/anon/.config/PixelPaint.ini b/Base/home/anon/.config/PixelPaint.ini deleted file mode 100644 index 6c1f32a2e6f..00000000000 --- a/Base/home/anon/.config/PixelPaint.ini +++ /dev/null @@ -1,12 +0,0 @@ -[PixelGrid] -Threshold=15 -Show=true - -[Rulers] -Show=true - -[Guides] -Show=true - -[ImageEditor] -ShowActiveLayerBoundary=true diff --git a/Base/home/anon/.config/SystemServer.ini b/Base/home/anon/.config/SystemServer.ini deleted file mode 100644 index 490bb8184bc..00000000000 --- a/Base/home/anon/.config/SystemServer.ini +++ /dev/null @@ -1,143 +0,0 @@ -[Clipboard] -Socket=/tmp/session/%sid/portal/clipboard -SocketPermissions=600 -Priority=low - -[ConfigServer] -Socket=/tmp/session/%sid/portal/config -SocketPermissions=600 - -[RequestServer] -Socket=/tmp/session/%sid/portal/request -SocketPermissions=600 -Lazy=true -Priority=low -SystemModes=text,graphical -MultiInstance=true -AcceptSocketConnections=true - -[WebContent] -Socket=/tmp/session/%sid/portal/webcontent -SocketPermissions=600 -Lazy=true -SystemModes=graphical -MultiInstance=true -AcceptSocketConnections=true - -[WebSocket] -Socket=/tmp/session/%sid/portal/websocket -SocketPermissions=600 -Lazy=true -Priority=low -SystemModes=text,graphical -MultiInstance=true -AcceptSocketConnections=true - -[WebWorker] -Socket=/tmp/session/%sid/portal/webworker -SocketPermissions=600 -Lazy=true -Priority=low -SystemModes=text,graphical -MultiInstance=true -AcceptSocketConnections=true - -[FileSystemAccessServer] -Socket=/tmp/session/%sid/portal/filesystemaccess -SocketPermissions=660 -Lazy=true -Priority=low -SystemModes=text,graphical -MultiInstance=true -AcceptSocketConnections=true - -[ImageDecoder] -Socket=/tmp/session/%sid/portal/image -SocketPermissions=600 -Lazy=true -SystemModes=graphical -MultiInstance=true -AcceptSocketConnections=true - -[NotificationServer] -Socket=/tmp/session/%sid/portal/notify -SocketPermissions=600 -Lazy=true -Priority=low -KeepAlive=true - -[AudioServer] -Socket=/tmp/session/%sid/portal/audio,/tmp/session/%sid/portal/audiomanager -Priority=high -KeepAlive=true -SystemModes=text,graphical - -[CppLanguageServer] -Socket=/tmp/session/%sid/portal/language/cpp -SocketPermissions=600 -Lazy=true -MultiInstance=true -AcceptSocketConnections=true - -[ShellLanguageServer] -Socket=/tmp/session/%sid/portal/language/shell -SocketPermissions=600 -Lazy=true -MultiInstance=true -AcceptSocketConnections=true - -[SQLServer] -Socket=/tmp/session/%sid/portal/sql -SocketPermissions=600 -Priority=low -Lazy=true -KeepAlive=true - -[LaunchServer] -Socket=/tmp/session/%sid/portal/launch -SocketPermissions=600 -Lazy=true -SystemModes=text,graphical - -[WorkspacePicker.Applet] -Priority=low -KeepAlive=true - -[ResourceGraph.Applet] -Arguments=--cpu=CPUGraph,#00bb00 --memory=MemoryGraph,#00bbbb --network=NetworkGraph,#bbbb00 -Priority=low -KeepAlive=true - -[Audio.Applet] -Priority=low -KeepAlive=true - -[Network.Applet] -Arguments=--name=Network -Priority=low -KeepAlive=true - -[ClipboardHistory.Applet] -Priority=low -KeepAlive=true - -[Keymap.Applet] -Priority=low -KeepAlive=true - -[Taskbar] -KeepAlive=true - -[Desktop] -Executable=/bin/FileManager -Arguments=--desktop -KeepAlive=true - -[Terminal] -WorkingDirectory=/home/anon - -[CrashDaemon] -KeepAlive=true - -[SpiceAgent] -KeepAlive=false diff --git a/Base/home/anon/.config/Taskbar.ini b/Base/home/anon/.config/Taskbar.ini deleted file mode 100644 index 18761c3c1b0..00000000000 --- a/Base/home/anon/.config/Taskbar.ini +++ /dev/null @@ -1,8 +0,0 @@ -[Clock] -TimeFormat=%T - -[QuickLaunch_Entries] -Ladybird=0:Browser.af -FileManager=1:FileManager.af -Terminal=2:Terminal.af -TextEditor=3:TextEditor.af diff --git a/Base/home/anon/.config/Terminal.ini b/Base/home/anon/.config/Terminal.ini deleted file mode 100644 index 6f0d38b6f70..00000000000 --- a/Base/home/anon/.config/Terminal.ini +++ /dev/null @@ -1,13 +0,0 @@ -[Startup] -Command= -[Terminal] -ShowScrollBar=true -MaxHistorySize=1024 -AutoMark=MarkInteractiveShellPrompt -[Window] -Opacity=255 -Bell=Visible -ColorScheme=Default -[Cursor] -Shape=Block -Blinking=true diff --git a/Base/home/anon/.config/Tests.ini b/Base/home/anon/.config/Tests.ini deleted file mode 100644 index ea0e04b5462..00000000000 --- a/Base/home/anon/.config/Tests.ini +++ /dev/null @@ -1,11 +0,0 @@ -[Global] -SkipDirectories=Kernel/Legacy Shell -SkipRegex=^ue-.*$ -SkipTests=TestCommonmark function.sh -NotTestsPattern=^.*(txt|frm|inc|so|elf)$ - -[test-js] -Arguments=--show-progress=false - -[test-spreadsheet] -Arguments=--show-progress=false diff --git a/Base/home/anon/.config/TextEditor.ini b/Base/home/anon/.config/TextEditor.ini deleted file mode 100644 index 376c3657f63..00000000000 --- a/Base/home/anon/.config/TextEditor.ini +++ /dev/null @@ -1,4 +0,0 @@ -[Layout] -ShowRuler=true -ShowToolbar=true -ShowStatusBar=true diff --git a/Base/home/anon/Documents/3D Models/ladyball.obj b/Base/home/anon/Documents/3D Models/ladyball.obj deleted file mode 100644 index a1ddaf96c0e..00000000000 --- a/Base/home/anon/Documents/3D Models/ladyball.obj +++ /dev/null @@ -1,13736 +0,0 @@ -# Blender v2.93.1 OBJ File: 'untitled-2.blend' -# www.blender.org -mtllib ladyball.mtl -o spot1_Icosphere -v 1.039965 -0.351612 0.122288 -v 1.095316 -0.135103 0.290586 -v 1.130759 -0.178595 -0.077925 -v 1.043293 0.193183 0.363357 -v 1.100640 0.122812 -0.232912 -v 1.132815 0.158108 0.071062 -v 1.046584 0.352579 0.039817 -v 1.047124 -0.346183 0.147470 -v 1.055099 -0.334184 0.174623 -v 1.063544 -0.314249 0.202281 -v 1.071918 -0.286169 0.228450 -v 1.079621 -0.251366 0.251162 -v 1.086184 -0.212597 0.269136 -v 1.091401 -0.172994 0.282095 -v 1.084194 -0.142361 0.318456 -v 1.071005 -0.148080 0.345717 -v 1.056070 -0.151263 0.369982 -v 1.040145 -0.151115 0.388757 -v 1.024293 -0.147412 0.400314 -v 1.131856 -0.184716 -0.039377 -v 1.131582 -0.188439 0.005969 -v 1.129471 -0.188618 0.056904 -v 1.125286 -0.184471 0.110768 -v 1.119177 -0.175987 0.163936 -v 1.111678 -0.164015 0.212918 -v 1.103505 -0.149927 0.255403 -v 1.051849 -0.351981 0.098341 -v 1.065051 -0.346396 0.071149 -v 1.078985 -0.333197 0.041733 -v 1.092751 -0.311732 0.011846 -v 1.105356 -0.282945 -0.016418 -v 1.116039 -0.249233 -0.041287 -v 1.124477 -0.213582 -0.061814 -v 1.081375 -0.217457 -0.193195 -v 1.094685 -0.218042 -0.178326 -v 1.106790 -0.213501 -0.157375 -v 1.116964 -0.204476 -0.132135 -v 1.124916 -0.192331 -0.104949 -v 1.133776 0.129509 0.099663 -v 1.133315 0.095192 0.131471 -v 1.130965 0.055867 0.165085 -v 1.126495 0.013438 0.198340 -v 1.120073 -0.029295 0.228845 -v 1.112256 -0.069471 0.254753 -v 1.103780 -0.105037 0.275266 -v 1.136856 -0.145625 -0.068644 -v 1.142111 -0.106107 -0.055672 -v 1.145808 -0.060875 -0.038736 -v 1.147327 -0.012126 -0.018264 -v 1.146406 0.036919 0.004537 -v 1.143266 0.082980 0.027997 -v 1.138501 0.123711 0.050534 -v 1.035643 0.164010 0.386369 -v 1.027182 0.128460 0.407018 -v 1.018295 0.087111 0.422824 -v 1.091845 -0.100361 0.314235 -v 1.087119 -0.059623 0.336828 -v 1.081071 -0.014015 0.356191 -v 1.073875 0.034054 0.370149 -v 1.065961 0.081335 0.377327 -v 1.057903 0.124738 0.377695 -v 1.050230 0.162243 0.372446 -v 1.108474 0.090771 -0.233148 -v 1.116163 0.053247 -0.228064 -v 1.123043 0.011289 -0.216185 -v 1.128415 -0.032873 -0.196937 -v 1.131791 -0.076254 -0.171170 -v 1.133077 -0.116019 -0.141026 -v 1.132566 -0.150331 -0.109170 -v 1.076982 -0.040879 -0.269426 -v 1.084920 0.005355 -0.269914 -v 1.091592 0.049422 -0.262693 -v 1.096808 0.088955 -0.249590 -v 1.126840 0.173276 0.109365 -v 1.118707 0.187371 0.151905 -v 1.108300 0.198891 0.196789 -v 1.095916 0.206399 0.241114 -v 1.082298 0.209078 0.281685 -v 1.068432 0.207069 0.316053 -v 1.055229 0.201334 0.343179 -v 1.031628 0.204194 0.365706 -v 1.018725 0.213093 0.363498 -v 1.076985 0.141602 -0.242259 -v 1.089872 0.132722 -0.239888 -v 1.108748 0.135661 -0.213284 -v 1.116740 0.147790 -0.186228 -v 1.123939 0.157981 -0.151274 -v 1.129624 0.165036 -0.109363 -v 1.133284 0.168232 -0.062986 -v 1.134809 0.167612 -0.015522 -v 1.134486 0.163895 0.029871 -v 1.046365 0.354765 0.080206 -v 1.046067 0.351188 0.125584 -v 1.045685 0.340099 0.174101 -v 1.045230 0.320706 0.222748 -v 1.044729 0.293803 0.268072 -v 1.044219 0.261675 0.307279 -v 1.043733 0.227261 0.339013 -v 1.127279 0.194527 0.066231 -v 1.119631 0.232128 0.061058 -v 1.109733 0.268334 0.055833 -v 1.097850 0.300090 0.050942 -v 1.084687 0.324818 0.046758 -v 1.071204 0.341340 0.043511 -v 1.058300 0.350089 0.041238 -v 1.054011 0.345383 0.000712 -v 1.062169 0.331429 -0.041841 -v 1.070669 0.309440 -0.085673 -v 1.078938 0.279343 -0.127728 -v 1.086370 0.242706 -0.164886 -v 1.092527 0.202396 -0.195001 -v 1.097251 0.161588 -0.217450 -v 1.066946 0.340105 -0.000054 -v 1.080903 0.327405 0.000719 -v 1.076117 0.322156 -0.044491 -v 1.095009 0.306048 0.003444 -v 1.090716 0.304601 -0.044540 -v 1.085195 0.295282 -0.089273 -v 1.108160 0.276390 0.008209 -v 1.104858 0.278244 -0.041338 -v 1.099819 0.272717 -0.088937 -v 1.093439 0.260241 -0.130866 -v 1.119379 0.240633 0.014660 -v 1.117363 0.244506 -0.034885 -v 1.113322 0.242265 -0.084045 -v 1.107420 0.233599 -0.128818 -v 1.100237 0.219635 -0.166182 -v 1.128150 0.202079 0.022122 -v 1.127419 0.206460 -0.025904 -v 1.124648 0.206307 -0.074978 -v 1.119741 0.200924 -0.121337 -v 1.113062 0.190711 -0.161536 -v 1.105313 0.177027 -0.193624 -v 1.063602 0.291921 -0.119704 -v 1.071846 0.256880 -0.161298 -v 1.079179 0.216357 -0.195861 -v 1.085215 0.173899 -0.221950 -v 1.031959 0.330751 0.209303 -v 1.031457 0.306442 0.258646 -v 1.031260 0.275158 0.302369 -v 1.031341 0.240008 0.338206 -v 1.058806 0.350094 0.084587 -v 1.059058 0.343089 0.132876 -v 1.072373 0.337872 0.089403 -v 1.058957 0.327479 0.183534 -v 1.072995 0.326347 0.139716 -v 1.086263 0.316780 0.094380 -v 1.058455 0.303170 0.232877 -v 1.072880 0.305774 0.191169 -v 1.086898 0.300282 0.145399 -v 1.099414 0.287122 0.099145 -v 1.057590 0.271967 0.277239 -v 1.071978 0.277091 0.239696 -v 1.086383 0.275323 0.196062 -v 1.099642 0.266252 0.149372 -v 1.110850 0.251100 0.103344 -v 1.056470 0.236963 0.314221 -v 1.070415 0.243043 0.281886 -v 1.084758 0.243853 0.242408 -v 1.098411 0.238504 0.197831 -v 1.110360 0.227393 0.151465 -v 1.120009 0.212068 0.106763 -v 1.140099 0.126678 0.005409 -v 1.144418 0.082628 -0.020364 -v 1.139939 0.127457 -0.043415 -v 1.146673 0.033364 -0.045661 -v 1.143375 0.080365 -0.071269 -v 1.137486 0.125298 -0.092935 -v 1.146359 -0.017986 -0.068383 -v 1.144401 0.028717 -0.096650 -v 1.139685 0.075913 -0.120919 -v 1.132579 0.119915 -0.139294 -v 1.143501 -0.067608 -0.086842 -v 1.142740 -0.023682 -0.117309 -v 1.139363 0.023189 -0.144249 -v 1.133432 0.069458 -0.165203 -v 1.125582 0.111708 -0.179048 -v 1.138639 -0.112299 -0.100336 -v 1.138715 -0.072858 -0.132053 -v 1.136545 -0.028753 -0.161102 -v 1.131952 0.017215 -0.184739 -v 1.125276 0.061630 -0.201208 -v 1.117262 0.101627 -0.210339 -v 1.084743 0.096565 -0.256170 -v 1.078189 0.054300 -0.267572 -v 1.013212 0.134034 0.408031 -v 1.022728 0.172663 0.388628 -v 1.063388 0.167736 0.349819 -v 1.072086 0.126899 0.351835 -v 1.077664 0.170509 0.319184 -v 1.080752 0.080042 0.347613 -v 1.087037 0.126389 0.317189 -v 1.092190 0.169477 0.280653 -v 1.088686 0.029975 0.336515 -v 1.095773 0.076678 0.308973 -v 1.101645 0.122591 0.274598 -v 1.105843 0.164127 0.236075 -v 1.095299 -0.019582 0.319365 -v 1.103114 0.024942 0.294702 -v 1.109791 0.071185 0.263203 -v 1.114702 0.115550 0.226864 -v 1.117609 0.154858 0.188762 -v 1.100312 -0.065268 0.298169 -v 1.108614 -0.024799 0.275891 -v 1.115992 0.019276 0.247359 -v 1.121711 0.063994 0.213879 -v 1.125346 0.106031 0.177939 -v 1.126927 0.142841 0.142360 -v 1.104549 0.051898 -0.248994 -v 1.111956 0.009525 -0.241710 -v 1.098941 0.007499 -0.260053 -v 1.118293 -0.036213 -0.226269 -v 1.105757 -0.038799 -0.249353 -v 1.091603 -0.040368 -0.263886 -v 1.122887 -0.082181 -0.202632 -v 1.111314 -0.086525 -0.229532 -v 1.097612 -0.088658 -0.248844 -v 1.082852 -0.088413 -0.259057 -v 1.125395 -0.124963 -0.172555 -v 1.115064 -0.131939 -0.201459 -v 1.102329 -0.135855 -0.224574 -v 1.088024 -0.136148 -0.239569 -v 1.125926 -0.162028 -0.138991 -v 1.116860 -0.171818 -0.167846 -v 1.105383 -0.178270 -0.192974 -v 1.092038 -0.180347 -0.211783 -v 1.077816 -0.177895 -0.222874 -v 1.042534 0.127996 0.395805 -v 1.033964 0.087233 0.415550 -v 1.050236 0.085261 0.400018 -v 1.024976 0.041391 0.428872 -v 1.041641 0.039878 0.417297 -v 1.058347 0.037347 0.397019 -v 1.016225 -0.006654 0.433701 -v 1.032633 -0.008921 0.426782 -v 1.049765 -0.010997 0.410429 -v 1.066282 -0.012720 0.385921 -v 1.023908 -0.057471 0.427080 -v 1.040779 -0.060327 0.415386 -v 1.057717 -0.061568 0.394809 -v 1.073449 -0.061220 0.367548 -v 1.016097 -0.102159 0.418853 -v 1.032093 -0.106788 0.411504 -v 1.048778 -0.108810 0.395577 -v 1.064915 -0.108075 0.372256 -v 1.079458 -0.105008 0.344155 -v 1.139343 0.090067 0.079720 -v 1.138355 0.050737 0.112306 -v 1.143626 0.044268 0.057497 -v 1.135051 0.007295 0.146579 -v 1.141730 0.000665 0.090500 -v 1.145861 -0.005970 0.034177 -v 1.129332 -0.037422 0.180059 -v 1.137183 -0.045246 0.125002 -v 1.142733 -0.052057 0.067298 -v 1.145547 -0.057321 0.011455 -v 1.121623 -0.080094 0.210257 -v 1.130142 -0.089943 0.158337 -v 1.136861 -0.097970 0.101673 -v 1.141094 -0.103382 0.044461 -v 1.142709 -0.105968 -0.008981 -v 1.112728 -0.118042 0.235528 -v 1.121317 -0.130172 0.188098 -v 1.128704 -0.140123 0.134615 -v 1.134109 -0.146756 0.078414 -v 1.137131 -0.149578 0.023670 -v 1.137883 -0.148910 -0.026024 -v 1.117313 -0.229373 -0.088569 -v 1.107346 -0.266087 -0.066894 -v 1.107835 -0.242379 -0.115015 -v 1.095126 -0.299242 -0.040116 -v 1.096314 -0.278543 -0.091695 -v 1.096130 -0.250623 -0.138802 -v 1.081279 -0.325628 -0.009582 -v 1.082811 -0.309393 -0.062669 -v 1.083326 -0.284433 -0.113332 -v 1.082785 -0.252701 -0.157611 -v 1.066834 -0.343286 0.022440 -v 1.068257 -0.332008 -0.029826 -v 1.069109 -0.311525 -0.081981 -v 1.069274 -0.282752 -0.129805 -v 1.068792 -0.248457 -0.170043 -v 1.052872 -0.352172 0.053533 -v 1.053820 -0.345312 0.004098 -v 1.124703 -0.221265 -0.018752 -v 1.123321 -0.225390 0.031289 -v 1.115089 -0.257592 0.006258 -v 1.119948 -0.224492 0.086227 -v 1.112401 -0.260895 0.060291 -v 1.103065 -0.290532 0.034893 -v 1.114543 -0.217860 0.142428 -v 1.107782 -0.257603 0.117716 -v 1.099115 -0.291506 0.091363 -v 1.089218 -0.316918 0.065427 -v 1.107507 -0.205984 0.195717 -v 1.101449 -0.247455 0.174167 -v 1.093565 -0.284696 0.149067 -v 1.084345 -0.314359 0.122160 -v 1.074577 -0.334791 0.095592 -v 1.099547 -0.190397 0.242800 -v 1.094003 -0.231717 0.225495 -v 1.086850 -0.270633 0.203496 -v 1.078408 -0.303652 0.177829 -v 1.069306 -0.328323 0.150402 -v 1.060262 -0.344065 0.123350 -v 1.015602 -0.183188 0.382997 -v 1.022880 -0.225822 0.352584 -v 1.031585 -0.189873 0.374738 -v 1.031070 -0.264847 0.313534 -v 1.039737 -0.230944 0.339887 -v 1.048271 -0.191895 0.358811 -v 1.031490 -0.317910 0.236275 -v 1.039584 -0.296823 0.268306 -v 1.048202 -0.266923 0.297181 -v 1.056689 -0.229918 0.320314 -v 1.064420 -0.189103 0.336401 -v 1.039700 -0.336009 0.190484 -v 1.047762 -0.319882 0.220743 -v 1.056317 -0.294998 0.249957 -v 1.064759 -0.261978 0.275624 -v 1.072459 -0.223277 0.295837 -v 1.078986 -0.182342 0.309934 -v 1.036569 -0.344904 0.157545 -v 1.030533 0.330983 0.207491 -v 1.029889 0.328147 0.214264 -v 1.050530 -0.346522 0.012094 -v 1.047297 -0.351294 0.045878 -v 1.076982 -0.040879 -0.269426 -v 1.076146 -0.087370 -0.259820 -v 1.075117 -0.124711 -0.248383 -v 1.043408 -0.353295 0.086443 -v 1.074835 -0.133381 -0.245277 -v 1.051562 -0.344235 0.001293 -v 1.026190 0.308715 0.253175 -v 1.049979 0.345871 0.004560 -v 1.024017 0.294522 0.276096 -v 1.074394 -0.143647 -0.240485 -v 1.054291 -0.335806 -0.027303 -v 1.051380 0.342296 -0.009972 -v 1.053742 0.334480 -0.034436 -v 1.072886 -0.175711 -0.224159 -v 1.022173 0.280721 0.295573 -v 1.056813 0.321974 -0.066196 -v 1.057779 0.317234 -0.076174 -v 1.071505 -0.199515 -0.209306 -v 1.070609 -0.213221 -0.199708 -v 1.029736 -0.321006 0.228301 -v 1.028770 -0.316266 0.238279 -v 1.018975 0.251746 0.329462 -v 1.009566 0.041847 0.431531 -v 1.035168 -0.341328 0.172077 -v 1.032807 -0.333513 0.196541 -v 1.009566 0.041847 0.431531 -v 1.009566 0.041847 0.431531 -v 1.009520 -0.005613 0.432928 -v 1.018683 0.248783 0.332561 -v 1.060289 0.302377 -0.102044 -v 1.014629 -0.182488 0.383069 -v 1.014860 -0.186093 0.380739 -v 1.017995 0.240460 0.339883 -v 1.015939 0.214189 0.361813 -v 1.061866 0.292534 -0.118296 -v 1.014112 -0.172688 0.388269 -v 1.009831 -0.044598 0.430430 -v 1.062444 0.288198 -0.124229 -v 1.065696 0.260237 -0.157590 -v 1.009946 -0.053754 0.429412 -v 1.012591 -0.142027 0.403531 -v 1.010186 -0.064857 0.427122 -v 1.011063 -0.099848 0.418649 -v 1.018992 -0.240127 0.338711 -v 1.017447 -0.222167 0.354469 -v 1.076982 -0.040879 -0.269426 -v 1.067557 0.241095 -0.176606 -v 1.077029 0.006580 -0.270823 -v 1.069102 0.223135 -0.192364 -v 1.011971 -0.126460 0.409701 -v 1.076982 -0.040879 -0.269426 -v 1.071689 0.187060 -0.218634 -v 1.068553 -0.239493 -0.177778 -v 1.071919 0.183456 -0.220964 -v 1.024105 -0.287231 0.286334 -v 1.020852 -0.259270 0.319695 -v 1.076363 0.065824 -0.265017 -v 1.075485 0.100815 -0.256544 -v 1.056015 -0.330015 -0.045386 -v 1.056660 -0.327179 -0.052160 -v 1.074578 0.127428 -0.247596 -v 1.072436 0.173656 -0.226164 -v 1.060358 -0.307747 -0.091071 -v 1.026260 -0.301408 0.264150 -v 1.073957 0.142995 -0.241426 -v 1.062532 -0.293554 -0.113991 -v 1.064375 -0.279753 -0.133469 -v 1.024682 -0.291566 0.280401 -v 1.043141 0.354263 0.075662 -v 1.076717 0.045566 -0.268325 -v 1.067574 -0.250778 -0.167357 -v 1.041705 0.354168 0.090624 -v 1.067866 -0.247815 -0.170456 -v 1.076603 0.054722 -0.267307 -v 1.039252 0.352261 0.116227 -v 1.036019 0.347490 0.150010 -v 1.044843 -0.353200 0.071481 -v 1.034986 0.345202 0.160811 -v 1.032258 0.336773 0.189408 -v 1.015044 0.200483 0.371411 -v 1.011714 0.134348 0.407382 -v 1.011431 0.125678 0.410488 -v 1.012155 0.144614 0.402590 -v 1.013662 0.176679 0.386264 -v 1.010403 0.088338 0.421925 -v 0.705609 0.044883 -0.303936 -v 0.705494 0.054039 -0.302918 -v 0.638458 0.041164 0.395920 -v 0.638411 -0.006296 0.397317 -v 0.639955 -0.100530 0.383038 -v 0.640862 -0.127143 0.374090 -v 0.684907 -0.330698 -0.080998 -v 0.685552 -0.327861 -0.087771 -v 0.668143 0.351579 0.080615 -v 0.664910 0.346807 0.114399 -v 0.668857 -0.352294 0.086677 -v 0.665461 -0.345587 0.121933 -v 0.672299 -0.353978 0.050832 -v 0.703286 -0.144330 -0.276096 -v 0.701778 -0.176394 -0.259770 -v 0.638458 0.041164 0.395920 -v 0.639077 -0.065539 0.391511 -v 0.704377 0.100133 -0.292156 -v 0.703469 0.126745 -0.283208 -v 0.655151 -0.302091 0.228538 -v 0.653574 -0.292249 0.244789 -v 0.696448 0.240412 -0.212218 -v 0.697993 0.222452 -0.227976 -v 0.682634 0.333798 -0.070047 -v 0.685704 0.321291 -0.101807 -v 0.647574 0.248100 0.296950 -v 0.646887 0.239778 0.304272 -v 0.702849 0.142312 -0.277038 -v 0.659425 0.330300 0.171879 -v 0.661149 0.336091 0.153797 -v 0.680272 0.341613 -0.045583 -v 0.664060 -0.342011 0.136465 -v 0.638723 -0.045281 0.394818 -v 0.647884 -0.240810 0.303100 -v 0.646338 -0.222850 0.318858 -v 0.649744 -0.259952 0.284084 -v 0.689250 -0.308430 -0.126682 -v 0.663878 0.344520 0.125200 -v 0.673735 -0.353883 0.035870 -v 0.679421 -0.347205 -0.023517 -v 0.676188 -0.351977 0.010267 -v 0.680454 -0.344917 -0.034318 -v 0.651065 0.280038 0.259962 -v 0.647866 0.251063 0.293850 -v 0.643521 -0.183171 0.347458 -v 0.643751 -0.186775 0.345128 -v 0.643004 -0.173371 0.352658 -v 0.691423 -0.294237 -0.149603 -v 0.705254 0.065142 -0.300628 -v 0.699501 -0.213904 -0.235319 -v 0.697445 -0.240175 -0.213390 -v 0.700580 0.186378 -0.254246 -v 0.700811 0.182773 -0.256576 -v 0.658780 0.327464 0.178652 -v 0.655082 0.308033 0.217564 -v 0.700396 -0.200198 -0.244918 -v 0.689180 0.301694 -0.137655 -v 0.690758 0.291851 -0.153907 -v 0.701328 0.172973 -0.261775 -v 0.705874 -0.041562 -0.305038 -v 0.705037 -0.088053 -0.295432 -v 0.686670 0.316551 -0.111785 -v 0.705874 -0.041562 -0.305038 -v 0.640605 0.133665 0.371771 -v 0.640323 0.124996 0.374876 -v 0.641046 0.143932 0.366978 -v 0.705874 -0.041562 -0.305038 -v 0.705920 0.005898 -0.306435 -v 0.693267 -0.280436 -0.169080 -v 0.696465 -0.251461 -0.202969 -v 0.675475 0.351897 0.004205 -v 0.678871 0.345189 -0.031051 -v 0.672032 0.353580 0.040050 -v 0.643935 0.199800 0.335800 -v 0.642554 0.175996 0.350653 -v 0.704009 -0.125394 -0.283994 -v 0.703727 -0.134063 -0.280889 -v 0.691335 0.287516 -0.159840 -v 0.644831 0.213506 0.326201 -v 0.639294 0.087655 0.386314 -v 0.694588 0.259555 -0.193201 -v 0.652996 -0.287913 0.250722 -v 0.638837 -0.054437 0.393800 -v 0.657661 -0.316949 0.202668 -v 0.670597 0.353485 0.055012 -v 0.696757 -0.248497 -0.206068 -v 0.658627 -0.321689 0.192690 -v 0.661698 -0.334196 0.160930 -v 0.641483 -0.142710 0.367920 -v 0.652909 0.293840 0.240484 -v 0.638458 0.041164 0.395920 -v 0.683182 -0.336489 -0.062914 -vt 0.193183 0.019683 -vt 0.176737 0.019683 -vt 0.181819 0.000000 -vt 0.272728 0.157461 -vt 0.284092 0.137778 -vt 0.295455 0.157461 -vt 0.534091 0.019683 -vt 0.545455 0.000000 -vt 0.550537 0.019683 -vt 0.284092 0.177143 -vt 0.454546 0.157461 -vt 0.477273 0.157461 -vt 0.465910 0.177143 -vt 0.261364 0.177143 -vt 0.443182 0.177143 -vt 0.181819 0.314921 -vt 0.204546 0.314921 -vt 0.193183 0.334604 -vt 0.545455 0.314921 -vt 0.568182 0.314921 -vt 0.556819 0.334604 -vt 0.363637 0.314921 -vt 0.386364 0.314921 -vt 0.375001 0.334604 -vt 0.465910 0.452699 -vt 0.454546 0.472382 -vt 0.443182 0.452699 -vt 0.431819 0.433017 -vt 0.454546 0.433017 -vt 0.420455 0.413334 -vt 0.443182 0.413334 -vt 0.409092 0.393652 -vt 0.431819 0.393652 -vt 0.397728 0.373969 -vt 0.420455 0.373969 -vt 0.386364 0.354286 -vt 0.409091 0.354286 -vt 0.397728 0.334604 -vt 0.477273 0.433017 -vt 0.465910 0.413334 -vt 0.488637 0.413334 -vt 0.454546 0.393652 -vt 0.477273 0.393652 -vt 0.500000 0.393652 -vt 0.443182 0.373969 -vt 0.465910 0.373969 -vt 0.488637 0.373969 -vt 0.511364 0.373969 -vt 0.431819 0.354286 -vt 0.454546 0.354286 -vt 0.477273 0.354286 -vt 0.500000 0.354286 -vt 0.522728 0.354286 -vt 0.420455 0.334604 -vt 0.443182 0.334604 -vt 0.465910 0.334604 -vt 0.488637 0.334604 -vt 0.511364 0.334604 -vt 0.534091 0.334604 -vt 0.409091 0.314921 -vt 0.431819 0.314921 -vt 0.454546 0.314921 -vt 0.477273 0.314921 -vt 0.500000 0.314921 -vt 0.522728 0.314921 -vt 0.625000 0.452699 -vt 0.631282 0.452699 -vt 0.636364 0.472382 -vt 0.613637 0.433017 -vt 0.626138 0.433017 -vt 0.629174 0.445471 -vt 0.602273 0.413334 -vt 0.621040 0.413334 -vt 0.622198 0.418189 -vt 0.590910 0.393652 -vt 0.613637 0.393652 -vt 0.579546 0.373969 -vt 0.602273 0.373969 -vt 0.568182 0.354286 -vt 0.590909 0.354286 -vt 0.579546 0.334604 -vt 0.618044 0.401285 -vt 0.616008 0.393652 -vt 0.610813 0.373969 -vt 0.615257 0.390844 -vt 0.605676 0.354286 -vt 0.608234 0.363644 -vt 0.600724 0.334604 -vt 0.601248 0.336379 -vt 0.590909 0.314921 -vt 0.599469 0.329747 -vt 0.595581 0.314921 -vt 0.102274 0.452699 -vt 0.090910 0.472382 -vt 0.095992 0.452699 -vt 0.098100 0.445471 -vt 0.113637 0.433017 -vt 0.101136 0.433017 -vt 0.105076 0.418189 -vt 0.125001 0.413334 -vt 0.106234 0.413334 -vt 0.109230 0.401285 -vt 0.111266 0.393652 -vt 0.113637 0.393652 -vt 0.136365 0.393652 -vt 0.112016 0.390844 -vt 0.125001 0.373969 -vt 0.116461 0.373969 -vt 0.147728 0.373969 -vt 0.119039 0.363644 -vt 0.136364 0.354286 -vt 0.121598 0.354286 -vt 0.159092 0.354286 -vt 0.126026 0.336379 -vt 0.147728 0.334604 -vt 0.126550 0.334604 -vt 0.170455 0.334604 -vt 0.127804 0.329747 -vt 0.131693 0.314921 -vt 0.136364 0.314921 -vt 0.159092 0.314921 -vt 0.272728 0.472382 -vt 0.261364 0.452699 -vt 0.284092 0.452699 -vt 0.250001 0.433017 -vt 0.272728 0.433017 -vt 0.238637 0.413334 -vt 0.261364 0.413334 -vt 0.227273 0.393652 -vt 0.250001 0.393652 -vt 0.215910 0.373969 -vt 0.238637 0.373969 -vt 0.204546 0.354286 -vt 0.227273 0.354286 -vt 0.215910 0.334604 -vt 0.295455 0.433017 -vt 0.284092 0.413334 -vt 0.306819 0.413334 -vt 0.272728 0.393652 -vt 0.295455 0.393652 -vt 0.318182 0.393652 -vt 0.261364 0.373969 -vt 0.284092 0.373969 -vt 0.306819 0.373969 -vt 0.329546 0.373969 -vt 0.250001 0.354286 -vt 0.272728 0.354286 -vt 0.295455 0.354286 -vt 0.318182 0.354286 -vt 0.340910 0.354286 -vt 0.238637 0.334604 -vt 0.261364 0.334604 -vt 0.284092 0.334604 -vt 0.306819 0.334604 -vt 0.329546 0.334604 -vt 0.352273 0.334604 -vt 0.227274 0.314921 -vt 0.250001 0.314921 -vt 0.272728 0.314921 -vt 0.295455 0.314921 -vt 0.318183 0.314921 -vt 0.340910 0.314921 -vt 0.375001 0.295238 -vt 0.386364 0.275556 -vt 0.397728 0.295238 -vt 0.397728 0.255874 -vt 0.409092 0.275556 -vt 0.409092 0.236191 -vt 0.420455 0.255874 -vt 0.420455 0.216509 -vt 0.431819 0.236191 -vt 0.431819 0.196826 -vt 0.443182 0.216509 -vt 0.454546 0.196826 -vt 0.420455 0.295238 -vt 0.431819 0.275556 -vt 0.443182 0.295238 -vt 0.443182 0.255874 -vt 0.454546 0.275556 -vt 0.465910 0.295238 -vt 0.454546 0.236191 -vt 0.465910 0.255874 -vt 0.477273 0.275556 -vt 0.488637 0.295238 -vt 0.465910 0.216509 -vt 0.477273 0.236191 -vt 0.488637 0.255874 -vt 0.500000 0.275556 -vt 0.511364 0.295238 -vt 0.477273 0.196826 -vt 0.488637 0.216508 -vt 0.500000 0.236191 -vt 0.511364 0.255874 -vt 0.522728 0.275556 -vt 0.534091 0.295238 -vt 0.556819 0.295238 -vt 0.568182 0.275556 -vt 0.579546 0.295238 -vt 0.579546 0.255874 -vt 0.590910 0.275556 -vt 0.590910 0.236191 -vt 0.590910 0.236191 -vt 0.591833 0.255874 -vt 0.590910 0.236191 -vt 0.590910 0.236191 -vt 0.593592 0.280202 -vt 0.594268 0.295238 -vt 0.595096 0.307670 -vt 0.593036 0.271873 -vt 0.593228 0.275556 -vt 0.132178 0.307670 -vt 0.134046 0.275556 -vt 0.134238 0.271873 -vt 0.136364 0.275556 -vt 0.133682 0.280202 -vt 0.147728 0.295238 -vt 0.133006 0.295238 -vt 0.135441 0.255874 -vt 0.136365 0.236191 -vt 0.147728 0.255874 -vt 0.159092 0.275556 -vt 0.170455 0.295238 -vt 0.193183 0.295238 -vt 0.204546 0.275556 -vt 0.215910 0.295238 -vt 0.215910 0.255874 -vt 0.227274 0.275556 -vt 0.227273 0.236191 -vt 0.238637 0.255874 -vt 0.238637 0.216509 -vt 0.250001 0.236191 -vt 0.250001 0.196826 -vt 0.261364 0.216509 -vt 0.272728 0.196826 -vt 0.238637 0.295238 -vt 0.250001 0.275556 -vt 0.261364 0.295238 -vt 0.261364 0.255874 -vt 0.272728 0.275556 -vt 0.284092 0.295238 -vt 0.272728 0.236191 -vt 0.284092 0.255874 -vt 0.295455 0.275556 -vt 0.306819 0.295238 -vt 0.284092 0.216509 -vt 0.295455 0.236191 -vt 0.306819 0.255874 -vt 0.318182 0.275556 -vt 0.329546 0.295238 -vt 0.295455 0.196826 -vt 0.306819 0.216508 -vt 0.318182 0.236191 -vt 0.329546 0.255874 -vt 0.340910 0.275556 -vt 0.352273 0.295238 -vt 0.545455 0.275556 -vt 0.534091 0.255874 -vt 0.522728 0.236191 -vt 0.511364 0.216508 -vt 0.500000 0.196826 -vt 0.488637 0.177143 -vt 0.556819 0.255874 -vt 0.545455 0.236191 -vt 0.568182 0.236191 -vt 0.534091 0.216508 -vt 0.556819 0.216508 -vt 0.579546 0.216508 -vt 0.589986 0.216509 -vt 0.522728 0.196826 -vt 0.545455 0.196826 -vt 0.568182 0.196826 -vt 0.588591 0.196826 -vt 0.588783 0.200509 -vt 0.511364 0.177143 -vt 0.534091 0.177143 -vt 0.556819 0.177143 -vt 0.579546 0.177143 -vt 0.588227 0.192180 -vt 0.587551 0.177143 -vt 0.500000 0.157461 -vt 0.522728 0.157461 -vt 0.545455 0.157461 -vt 0.568182 0.157461 -vt 0.586238 0.157461 -vt 0.586723 0.164712 -vt 0.181819 0.275556 -vt 0.170455 0.255874 -vt 0.159092 0.236191 -vt 0.136365 0.236191 -vt 0.136365 0.236191 -vt 0.137288 0.216508 -vt 0.147728 0.216508 -vt 0.193183 0.255874 -vt 0.181819 0.236191 -vt 0.204546 0.236191 -vt 0.170455 0.216508 -vt 0.193183 0.216508 -vt 0.215910 0.216508 -vt 0.138491 0.200509 -vt 0.159092 0.196826 -vt 0.138683 0.196826 -vt 0.181819 0.196826 -vt 0.204546 0.196826 -vt 0.227273 0.196826 -vt 0.139047 0.192180 -vt 0.139723 0.177143 -vt 0.147728 0.177143 -vt 0.170455 0.177143 -vt 0.193183 0.177143 -vt 0.215910 0.177143 -vt 0.238637 0.177143 -vt 0.140551 0.164712 -vt 0.159092 0.157461 -vt 0.141036 0.157461 -vt 0.181819 0.157461 -vt 0.204546 0.157461 -vt 0.227273 0.157461 -vt 0.250001 0.157461 -vt 0.363637 0.275556 -vt 0.352273 0.255874 -vt 0.340910 0.236191 -vt 0.329546 0.216508 -vt 0.318182 0.196826 -vt 0.306819 0.177143 -vt 0.375001 0.255874 -vt 0.363637 0.236191 -vt 0.386364 0.236191 -vt 0.352273 0.216508 -vt 0.375001 0.216508 -vt 0.397728 0.216508 -vt 0.340910 0.196826 -vt 0.363637 0.196826 -vt 0.386364 0.196826 -vt 0.409092 0.196826 -vt 0.329546 0.177143 -vt 0.352273 0.177143 -vt 0.375001 0.177143 -vt 0.397728 0.177143 -vt 0.420455 0.177143 -vt 0.318182 0.157461 -vt 0.340910 0.157461 -vt 0.363637 0.157461 -vt 0.386364 0.157461 -vt 0.409092 0.157461 -vt 0.431819 0.157461 -vt 0.465910 0.137778 -vt 0.477273 0.118096 -vt 0.488637 0.137778 -vt 0.488637 0.098413 -vt 0.500000 0.118096 -vt 0.500000 0.078731 -vt 0.511364 0.098413 -vt 0.511364 0.059048 -vt 0.522728 0.078731 -vt 0.522728 0.039365 -vt 0.534091 0.059048 -vt 0.545455 0.039365 -vt 0.511364 0.137778 -vt 0.522728 0.118096 -vt 0.534091 0.137778 -vt 0.534091 0.098413 -vt 0.545455 0.118096 -vt 0.556819 0.137778 -vt 0.545455 0.078731 -vt 0.556819 0.098413 -vt 0.568182 0.118096 -vt 0.579546 0.137778 -vt 0.582350 0.142635 -vt 0.556819 0.059048 -vt 0.563775 0.071096 -vt 0.565811 0.078731 -vt 0.566562 0.081538 -vt 0.571006 0.098413 -vt 0.573585 0.108738 -vt 0.576143 0.118096 -vt 0.580571 0.136003 -vt 0.581095 0.137778 -vt 0.552645 0.026911 -vt 0.555681 0.039365 -vt 0.559621 0.054193 -vt 0.560779 0.059048 -vt 0.443182 0.137778 -vt 0.420455 0.137778 -vt 0.397728 0.137778 -vt 0.375001 0.137778 -vt 0.352273 0.137778 -vt 0.329546 0.137778 -vt 0.306819 0.137778 -vt 0.431819 0.118096 -vt 0.409091 0.118096 -vt 0.420455 0.098413 -vt 0.386364 0.118096 -vt 0.397728 0.098413 -vt 0.409092 0.078731 -vt 0.363637 0.118096 -vt 0.375001 0.098413 -vt 0.386364 0.078731 -vt 0.397728 0.059048 -vt 0.340910 0.118096 -vt 0.352273 0.098413 -vt 0.363637 0.078731 -vt 0.375001 0.059048 -vt 0.386364 0.039365 -vt 0.318182 0.118096 -vt 0.329546 0.098413 -vt 0.340910 0.078731 -vt 0.352273 0.059048 -vt 0.363637 0.039365 -vt 0.375001 0.019683 -vt 0.295455 0.118096 -vt 0.306819 0.098413 -vt 0.318182 0.078731 -vt 0.329546 0.059048 -vt 0.340910 0.039365 -vt 0.352273 0.019683 -vt 0.363637 0.000000 -vt 0.167653 0.054193 -vt 0.170455 0.059048 -vt 0.166495 0.059048 -vt 0.174629 0.026911 -vt 0.181819 0.039365 -vt 0.171593 0.039365 -vt 0.146178 0.137778 -vt 0.146703 0.136003 -vt 0.147728 0.137778 -vt 0.144924 0.142635 -vt 0.153689 0.108738 -vt 0.159092 0.118096 -vt 0.151131 0.118096 -vt 0.170455 0.137778 -vt 0.160712 0.081537 -vt 0.170455 0.098413 -vt 0.156267 0.098413 -vt 0.181819 0.118096 -vt 0.193183 0.137778 -vt 0.163499 0.071097 -vt 0.181819 0.078731 -vt 0.161463 0.078731 -vt 0.193183 0.098413 -vt 0.204546 0.118096 -vt 0.215910 0.137778 -vt 0.193183 0.059048 -vt 0.204546 0.078731 -vt 0.215910 0.098413 -vt 0.227273 0.118096 -vt 0.238637 0.137778 -vt 0.204546 0.039365 -vt 0.215910 0.059048 -vt 0.227274 0.078731 -vt 0.238637 0.098413 -vt 0.250001 0.118096 -vt 0.261364 0.137778 -vt 0.610813 0.373969 -vt 0.615257 0.390844 -vt 0.500000 0.750000 -vt 0.500000 0.750000 -vt 0.500000 0.750000 -vt 0.516677 0.748536 -vt 0.530226 0.746051 -vt 0.533390 0.745316 -vt 0.537183 0.744053 -vt 0.549086 0.739630 -vt 0.558035 0.735386 -vt 0.563231 0.732574 -vt 0.573337 0.725942 -vt 0.576554 0.723709 -vt 0.577720 0.722741 -vt 0.589267 0.712013 -vt 0.594905 0.705729 -vt 0.600822 0.698251 -vt 0.609235 0.685363 -vt 0.610508 0.683097 -vt 0.613283 0.676974 -vt 0.617416 0.667261 -vt 0.618664 0.663555 -vt 0.621731 0.651867 -vt 0.623457 0.642939 -vt 0.624108 0.637679 -vt 0.625000 0.625000 -vt 0.624108 0.612321 -vt 0.623457 0.607061 -vt 0.621731 0.598133 -vt 0.618664 0.586444 -vt 0.617416 0.582739 -vt 0.613283 0.573026 -vt 0.610508 0.566903 -vt 0.609235 0.564637 -vt 0.600822 0.551749 -vt 0.594905 0.544271 -vt 0.589267 0.537987 -vt 0.577720 0.527259 -vt 0.576554 0.526291 -vt 0.573337 0.524058 -vt 0.563231 0.517426 -vt 0.558035 0.514614 -vt 0.549086 0.510370 -vt 0.537183 0.505947 -vt 0.533390 0.504684 -vt 0.530226 0.503949 -vt 0.516677 0.501464 -vt 0.500000 0.500000 -vt 0.500000 0.500000 -vt 0.500000 0.500000 -vt 0.483323 0.501464 -vt 0.469774 0.503949 -vt 0.466610 0.504684 -vt 0.462817 0.505947 -vt 0.450914 0.510370 -vt 0.441965 0.514614 -vt 0.436769 0.517426 -vt 0.426663 0.524058 -vt 0.423446 0.526291 -vt 0.422280 0.527259 -vt 0.410733 0.537987 -vt 0.405095 0.544271 -vt 0.399178 0.551749 -vt 0.390765 0.564637 -vt 0.389492 0.566903 -vt 0.386717 0.573026 -vt 0.382584 0.582739 -vt 0.381336 0.586444 -vt 0.378269 0.598133 -vt 0.376543 0.607061 -vt 0.375892 0.612321 -vt 0.375000 0.625000 -vt 0.375892 0.637679 -vt 0.376543 0.642939 -vt 0.378269 0.651867 -vt 0.381336 0.663556 -vt 0.382584 0.667261 -vt 0.386717 0.676974 -vt 0.389492 0.683097 -vt 0.390765 0.685363 -vt 0.399178 0.698251 -vt 0.405095 0.705729 -vt 0.410733 0.712013 -vt 0.422280 0.722741 -vt 0.423446 0.723709 -vt 0.426663 0.725942 -vt 0.436769 0.732574 -vt 0.441965 0.735386 -vt 0.450914 0.739630 -vt 0.462817 0.744053 -vt 0.466610 0.745315 -vt 0.469774 0.746051 -vt 0.483323 0.748536 -vt 0.134238 0.271873 -vt 0.135441 0.255874 -vt 0.550537 0.019683 -vt 0.552645 0.026911 -vt 0.161463 0.078731 -vt 0.163499 0.071097 -vt 0.600724 0.334604 -vt 0.601248 0.336379 -vt 0.566562 0.081538 -vt 0.571006 0.098413 -vt 0.588227 0.192180 -vt 0.588591 0.196826 -vt 0.573585 0.108738 -vt 0.098100 0.445471 -vt 0.101136 0.433017 -vt 0.136365 0.236191 -vt 0.137288 0.216508 -vt 0.116461 0.373969 -vt 0.119039 0.363644 -vt 0.555681 0.039365 -vt 0.559621 0.054193 -vt 0.605676 0.354286 -vt 0.608234 0.363644 -vt 0.594268 0.295238 -vt 0.595096 0.307670 -vt 0.112016 0.390844 -vt 0.588783 0.200509 -vt 0.589986 0.216508 -vt 0.090910 0.472382 -vt 0.095992 0.452699 -vt 0.599469 0.329747 -vt 0.591833 0.255874 -vt 0.593036 0.271873 -vt 0.586723 0.164712 -vt 0.587551 0.177143 -vt 0.136365 0.236191 -vt 0.136365 0.236191 -vt 0.156267 0.098413 -vt 0.160712 0.081537 -vt 0.622198 0.418189 -vt 0.626138 0.433017 -vt 0.560779 0.059048 -vt 0.127804 0.329747 -vt 0.131693 0.314921 -vt 0.586238 0.157461 -vt 0.105076 0.418189 -vt 0.106234 0.413334 -vt 0.121598 0.354286 -vt 0.133006 0.295238 -vt 0.133682 0.280202 -vt 0.126550 0.334604 -vt 0.629174 0.445471 -vt 0.111266 0.393652 -vt 0.631282 0.452699 -vt 0.636364 0.472382 -vt 0.140551 0.164712 -vt 0.141036 0.157461 -vt 0.138491 0.200509 -vt 0.138683 0.196826 -vt 0.132178 0.307670 -vt 0.616008 0.393652 -vt 0.618044 0.401285 -vt 0.126026 0.336379 -vt 0.621040 0.413334 -vt 0.134046 0.275556 -vt 0.590910 0.236191 -vt 0.139723 0.177143 -vt 0.593592 0.280202 -vt 0.174629 0.026911 -vt 0.176737 0.019683 -vt 0.595581 0.314921 -vt 0.146178 0.137778 -vt 0.146703 0.136003 -vt 0.565811 0.078731 -vt 0.109230 0.401285 -vt 0.590910 0.236191 -vt 0.144924 0.142635 -vt 0.166495 0.059048 -vt 0.576143 0.118096 -vt 0.580571 0.136003 -vt 0.582350 0.142635 -vt 0.593228 0.275556 -vt 0.171593 0.039365 -vt 0.167653 0.054193 -vt 0.181819 0.000000 -vt 0.151131 0.118096 -vt 0.153689 0.108738 -vt 0.581095 0.137778 -vt 0.545455 0.000000 -vt 0.590910 0.236191 -vt 0.139047 0.192180 -vt 0.563775 0.071096 -vn 0.3019 -0.9300 0.2096 -vn 0.0447 -0.9736 0.2239 -vn 0.0984 -0.9871 0.1259 -vn 0.9411 -0.1420 0.3067 -vn 0.9305 -0.1948 0.3100 -vn 0.9541 -0.1493 0.2594 -vn 0.3152 -0.9463 0.0712 -vn 0.0644 -0.9977 0.0192 -vn 0.9562 -0.1002 0.2749 -vn 0.9786 -0.1880 -0.0831 -vn 0.9641 -0.2270 -0.1376 -vn 0.9798 -0.1599 -0.1201 -vn 0.9359 -0.1061 0.3358 -vn 0.9884 -0.1397 -0.0595 -vn 0.6499 0.3976 0.6477 -vn 0.7733 0.3524 0.5271 -vn 0.6235 0.4858 0.6125 -vn 0.7687 0.2517 -0.5879 -vn 0.5557 0.3317 -0.7623 -vn 0.7382 0.3451 -0.5796 -vn 0.9871 0.1397 0.0784 -vn 0.9881 0.1485 0.0401 -vn 0.9803 0.1839 0.0725 -vn 0.1356 0.9668 -0.2165 -vn 0.1055 0.9887 -0.1065 -vn 0.3974 0.9150 -0.0697 -vn 0.6755 0.7370 -0.0220 -vn 0.5234 0.8372 -0.1582 -vn 0.8304 0.5569 0.0141 -vn 0.7588 0.6443 -0.0948 -vn 0.9082 0.4168 0.0381 -vn 0.8783 0.4759 -0.0458 -vn 0.9477 0.3146 0.0540 -vn 0.9359 0.3521 -0.0120 -vn 0.9686 0.2399 0.0648 -vn 0.9646 0.2633 0.0113 -vn 0.9797 0.1984 0.0278 -vn 0.2765 0.9075 -0.3163 -vn 0.6317 0.7381 -0.2369 -vn 0.4124 0.8145 -0.4079 -vn 0.8215 0.5481 -0.1569 -vn 0.7171 0.6275 -0.3031 -vn 0.5308 0.6976 -0.4812 -vn 0.9120 0.3984 -0.0968 -vn 0.8659 0.4544 -0.2092 -vn 0.7794 0.5164 -0.3547 -vn 0.6243 0.5720 -0.5321 -vn 0.9549 0.2918 -0.0552 -vn 0.9346 0.3264 -0.1409 -vn 0.8952 0.3674 -0.2520 -vn 0.8218 0.4126 -0.3928 -vn 0.6920 0.4518 -0.5630 -vn 0.9761 0.2157 -0.0261 -vn 0.9671 0.2364 -0.0934 -vn 0.9487 0.2609 -0.1786 -vn 0.9133 0.2894 -0.2864 -vn 0.8491 0.3202 -0.4201 -vn 0.9873 0.1585 -0.0048 -vn 0.9836 0.1703 -0.0596 -vn 0.9746 0.1843 -0.1270 -vn 0.9568 0.2005 -0.2105 -vn 0.9239 0.2190 -0.3138 -vn 0.8662 0.2383 -0.4392 -vn -0.0297 0.9775 -0.2085 -vn 0.0496 0.9453 -0.3224 -vn 0.0437 0.9588 -0.2805 -vn 0.1343 0.8876 -0.4405 -vn 0.1295 0.9088 -0.3966 -vn 0.0974 0.8204 -0.5634 -vn 0.2425 0.7125 -0.6584 -vn 0.3725 0.5840 -0.7212 -vn 0.4779 0.4534 -0.7523 -vn 0.1041 0.8638 -0.4930 -vn -0.0481 0.8350 -0.5481 -vn 0.0378 0.7450 -0.6659 -vn 0.0346 0.7799 -0.6249 -vn 0.1215 0.6336 -0.7640 -vn 0.1190 0.6740 -0.7291 -vn 0.1947 0.5119 -0.8367 -vn 0.1926 0.5549 -0.8093 -vn 0.1755 0.3971 -0.9008 -vn 0.1737 0.4626 -0.8694 -vn 0.0145 0.4144 -0.9100 -vn 0.1139 0.9934 0.0091 -vn -0.0467 0.9984 -0.0320 -vn 0.0115 0.9984 0.0548 -vn 0.2322 0.9619 0.1446 -vn 0.0091 0.9950 0.0991 -vn 0.0728 0.9784 0.1933 -vn 0.3463 0.8956 0.2791 -vn 0.0688 0.9681 0.2410 -vn 0.0287 0.9563 0.2910 -vn -0.1317 0.9377 0.3216 -vn 0.0078 0.9302 0.3670 -vn 0.4462 0.8014 0.3983 -vn -0.0661 0.9036 0.4231 -vn 0.1302 0.8504 0.5098 -vn -0.0715 0.8792 0.4710 -vn 0.5256 0.6930 0.4934 -vn -0.0052 0.8265 0.5629 -vn 0.2431 0.7428 0.6238 -vn -0.0103 0.7953 0.6061 -vn 0.5836 0.5848 0.5633 -vn 0.0493 0.7308 0.6808 -vn 0.3379 0.6252 0.7035 -vn 0.0453 0.6953 0.7172 -vn 0.0173 0.6544 0.7559 -vn -0.1476 0.6134 0.7759 -vn 0.0118 0.5980 0.8014 -vn 0.4097 0.5108 0.7557 -vn 0.5028 0.8625 0.0563 -vn 0.5921 0.7866 0.1747 -vn 0.6611 0.6963 0.2795 -vn 0.7101 0.6015 0.3660 -vn 0.7423 0.5102 0.4343 -vn 0.7618 0.4272 0.4869 -vn 0.7416 0.6655 0.0845 -vn 0.7894 0.5876 0.1778 -vn 0.8643 0.4930 0.0990 -vn 0.8210 0.5095 0.2577 -vn 0.8862 0.4302 0.1719 -vn 0.9245 0.3660 0.1062 -vn 0.8398 0.4354 0.3241 -vn 0.8985 0.3708 0.2349 -vn 0.9337 0.3178 0.1649 -vn 0.9551 0.2749 0.1098 -vn 0.8494 0.3678 0.3784 -vn 0.9037 0.3160 0.2887 -vn 0.9373 0.2729 0.2165 -vn 0.9583 0.2375 0.1586 -vn 0.9716 0.2083 0.1118 -vn 0.8530 0.3059 0.4228 -vn 0.9044 0.2649 0.3346 -vn 0.9372 0.2302 0.2620 -vn 0.9583 0.2013 0.2025 -vn 0.9721 0.1772 0.1537 -vn 0.9810 0.1571 0.1132 -vn 0.9926 0.1050 0.0613 -vn 0.9966 0.0692 0.0436 -vn 0.9936 0.1107 0.0203 -vn 0.9992 0.0312 0.0249 -vn 0.9974 0.0716 -0.0004 -vn 0.9999 -0.0088 0.0052 -vn 0.9993 0.0300 -0.0222 -vn 0.9986 -0.0507 -0.0155 -vn 0.9989 -0.0139 -0.0451 -vn 0.9948 -0.0944 -0.0371 -vn 0.9958 -0.0602 -0.0691 -vn 0.9897 -0.1084 -0.0939 -vn 0.9927 0.1170 -0.0280 -vn 0.9959 0.0739 -0.0524 -vn 0.9884 0.1244 -0.0871 -vn 0.9966 0.0280 -0.0780 -vn 0.9903 0.0766 -0.1160 -vn 0.9781 0.1330 -0.1598 -vn 0.9943 -0.0208 -0.1049 -vn 0.9889 0.0253 -0.1464 -vn 0.9777 0.0795 -0.1946 -vn 0.9576 0.1428 -0.2500 -vn 0.9885 -0.0721 -0.1328 -vn 0.9836 -0.0292 -0.1780 -vn 0.9728 0.0219 -0.2307 -vn 0.9529 0.0825 -0.2917 -vn 0.9197 0.1536 -0.3612 -vn 0.9785 -0.1272 -0.1625 -vn 0.9732 -0.0880 -0.2122 -vn 0.9619 -0.0408 -0.2705 -vn 0.9409 0.0160 -0.3382 -vn 0.9057 0.0837 -0.4155 -vn 0.8504 0.1628 -0.5003 -vn 0.6864 0.1983 -0.6996 -vn 0.5537 0.1199 -0.8240 -vn 0.3766 0.2584 -0.8896 -vn 0.3530 0.0111 -0.9355 -vn 0.1340 0.1509 -0.9794 -vn 0.9322 -0.3391 0.1261 -vn 0.0965 -0.0220 -0.9951 -vn 0.1066 0.0198 -0.9941 -vn 0.9991 0.0000 0.0421 -vn 0.0874 -0.1512 -0.9846 -vn 0.1147 0.2275 -0.9670 -vn 0.1311 0.2841 -0.9498 -vn 0.1283 0.3341 -0.9337 -vn 0.1022 0.0774 -0.9917 -vn -0.0136 0.1605 -0.9869 -vn -0.0421 0.5433 0.8385 -vn -0.1954 0.3835 0.9026 -vn -0.0844 0.3064 0.9481 -vn -0.0491 0.3757 0.9254 -vn -0.0641 0.4469 0.8923 -vn 0.2083 0.4649 0.8605 -vn -0.0435 0.4984 0.8658 -vn -0.0817 0.2510 0.9645 -vn 0.0317 0.1302 0.9910 -vn 0.1710 0.2345 0.9569 -vn 0.3915 0.3190 0.8631 -vn 0.5468 0.3696 0.7512 -vn 0.7446 0.2926 0.5999 -vn 0.8131 0.1974 0.5477 -vn 0.8386 0.2532 0.4823 -vn 0.8607 0.1144 0.4961 -vn 0.8827 0.1686 0.4386 -vn 0.8927 0.0440 0.4484 -vn 0.9123 0.0961 0.3981 -vn 0.9138 -0.0150 0.4058 -vn 0.9317 0.0345 0.3616 -vn 0.9274 -0.0645 0.3684 -vn 0.9441 -0.0176 0.3293 -vn 0.9517 -0.0618 0.3007 -vn 0.8965 0.2179 0.3858 -vn 0.9252 0.1439 0.3512 -vn 0.9333 0.1879 0.3058 -vn 0.9440 0.0803 0.3199 -vn 0.9522 0.1232 0.2793 -vn 0.9569 0.1631 0.2402 -vn 0.9561 0.0260 0.2918 -vn 0.9645 0.0673 0.2554 -vn 0.9697 0.1061 0.2202 -vn 0.9721 0.1424 0.1864 -vn 0.9634 -0.0205 0.2670 -vn 0.9720 0.0190 0.2341 -vn 0.9777 0.0565 0.2021 -vn 0.9809 0.0918 0.1713 -vn 0.9819 0.1251 0.1419 -vn 0.9678 -0.0612 0.2442 -vn 0.9765 -0.0236 0.2142 -vn 0.9826 0.0124 0.1850 -vn 0.9865 0.0468 0.1568 -vn 0.9883 0.0793 0.1297 -vn 0.9885 0.1100 0.1038 -vn 0.7959 0.1013 -0.5969 -vn 0.8696 0.0150 -0.4935 -vn 0.9154 -0.0565 -0.3984 -vn 0.9419 -0.1140 -0.3158 -vn 0.9560 -0.1598 -0.2459 -vn 0.9625 -0.1964 -0.1874 -vn 0.7028 0.0142 -0.7112 -vn 0.8053 -0.0781 -0.5876 -vn 0.5480 -0.1044 -0.8300 -vn 0.8694 -0.1511 -0.4704 -vn 0.6915 -0.2021 -0.6935 -vn 0.3207 -0.2451 -0.9149 -vn 0.0825 -0.2465 -0.9656 -vn 0.9064 -0.2064 -0.3686 -vn 0.7862 -0.2741 -0.5538 -vn 0.5072 -0.3507 -0.7872 -vn 0.1975 -0.3754 -0.9055 -vn 0.1978 -0.3239 -0.9252 -vn 0.9263 -0.2478 -0.2839 -vn 0.8430 -0.3232 -0.4300 -vn 0.6452 -0.4222 -0.6367 -vn 0.2583 -0.4941 -0.8301 -vn 0.1821 -0.4307 -0.8839 -vn 0.0494 -0.4881 -0.8713 -vn 0.9357 -0.2807 -0.2137 -vn 0.8744 -0.3584 -0.3270 -vn 0.7340 -0.4664 -0.4937 -vn 0.4348 -0.5765 -0.6918 -vn 0.1562 -0.6018 -0.7832 -vn 0.1588 -0.5610 -0.8124 -vn 0.6715 0.2539 0.6961 -vn 0.5567 0.1935 0.8078 -vn 0.3797 0.1020 0.9195 -vn 0.0305 0.0564 0.9979 -vn 0.0223 0.0407 0.9989 -vn -0.1059 -0.0152 0.9942 -vn 0.1374 -0.0202 0.9903 -vn 0.7614 0.1477 0.6312 -vn 0.6786 0.0775 0.7304 -vn 0.8227 0.0572 0.5655 -vn 0.5441 -0.0212 0.8387 -vn 0.7619 -0.0192 0.6474 -vn 0.8630 -0.0172 0.5049 -vn 0.0131 -0.0972 0.9951 -vn 0.3423 -0.1483 0.9278 -vn 0.0154 -0.1519 0.9883 -vn 0.6618 -0.1214 0.7397 -vn 0.8161 -0.0956 0.5699 -vn 0.8889 -0.0775 0.4514 -vn 0.0031 -0.2111 0.9774 -vn -0.1261 -0.2727 0.9538 -vn 0.0866 -0.2834 0.9551 -vn 0.5044 -0.2495 0.8266 -vn 0.7397 -0.1964 0.6436 -vn 0.8506 -0.1550 0.5023 -vn 0.9055 -0.1264 0.4051 -vn -0.0092 -0.3548 0.9349 -vn 0.2841 -0.3916 0.8752 -vn -0.0071 -0.4012 0.9159 -vn 0.6175 -0.3236 0.7169 -vn 0.7892 -0.2538 0.5593 -vn 0.8723 -0.2030 0.4447 -vn 0.9158 -0.1676 0.3650 -vn 0.9935 0.0735 0.0869 -vn 0.9928 0.0408 0.1129 -vn 0.9901 0.0064 0.1400 -vn 0.9853 -0.0298 0.1683 -vn 0.9779 -0.0676 0.1976 -vn 0.9678 -0.1071 0.2277 -vn 0.9969 0.0357 0.0695 -vn 0.9954 0.0012 0.0956 -vn 0.9987 -0.0041 0.0510 -vn 0.9918 -0.0352 0.1229 -vn 0.9962 -0.0406 0.0772 -vn 0.9984 -0.0459 0.0314 -vn 0.9857 -0.0734 0.1514 -vn 0.9913 -0.0790 0.1046 -vn 0.9948 -0.0844 0.0577 -vn 0.9959 -0.0897 0.0108 -vn 0.9770 -0.1132 0.1808 -vn 0.9839 -0.1191 0.1332 -vn 0.9885 -0.1248 0.0852 -vn 0.9908 -0.1302 0.0371 -vn 0.9908 -0.1352 -0.0108 -vn 0.9648 -0.1560 0.2119 -vn 0.9731 -0.1624 0.1633 -vn 0.9791 -0.1685 0.1140 -vn 0.9826 -0.1741 0.0645 -vn 0.9837 -0.1793 0.0151 -vn 0.9823 -0.1839 -0.0338 -vn 0.9677 -0.2404 -0.0761 -vn 0.9496 -0.3062 -0.0671 -vn 0.9451 -0.2965 -0.1374 -vn 0.9182 -0.3923 -0.0547 -vn 0.9117 -0.3873 -0.1368 -vn 0.8621 -0.5054 -0.0370 -vn 0.8500 -0.5095 -0.1336 -vn 0.7602 -0.6496 -0.0111 -vn 0.7336 -0.6681 -0.1242 -vn 0.5825 -0.8124 0.0257 -vn 0.5260 -0.8444 -0.1017 -vn 0.2207 -0.9734 -0.0613 -vn 0.8981 -0.3767 -0.2268 -vn 0.8277 -0.5060 -0.2425 -vn 0.7899 -0.4935 -0.3639 -vn 0.6919 -0.6755 -0.2550 -vn 0.6283 -0.6665 -0.4011 -vn 0.5410 -0.6350 -0.5514 -vn 0.4502 -0.8572 -0.2500 -vn 0.3513 -0.8409 -0.4116 -vn 0.2339 -0.7885 -0.5687 -vn 0.1081 -0.7040 -0.7019 -vn 0.1328 -0.6464 -0.7513 -vn 0.1124 -0.9709 -0.2112 -vn 0.1137 -0.9510 -0.2873 -vn 0.1540 -0.9282 -0.3388 -vn 0.1591 -0.9084 -0.3866 -vn 0.0936 -0.8679 -0.4877 -vn 0.0993 -0.8406 -0.5324 -vn 0.0282 -0.7836 -0.6207 -vn 0.0338 -0.7506 -0.6599 -vn -0.0334 -0.6843 -0.7284 -vn 0.0648 -0.9977 -0.0207 -vn 0.0127 -0.9933 -0.1147 -vn 0.0149 -0.9872 -0.1585 -vn -0.0456 -0.9648 -0.2589 -vn 0.9715 -0.2360 -0.0202 -vn 0.9723 -0.2311 0.0353 -vn 0.9700 -0.2253 0.0913 -vn 0.9646 -0.2187 0.1472 -vn 0.9562 -0.2114 0.2023 -vn 0.9449 -0.2033 0.2564 -vn 0.9535 -0.3014 -0.0027 -vn 0.9532 -0.2960 0.0612 -vn 0.9219 -0.3868 0.0208 -vn 0.9490 -0.2891 0.1254 -vn 0.9199 -0.3804 0.0953 -vn 0.8651 -0.4988 0.0527 -vn 0.9409 -0.2809 0.1890 -vn 0.9127 -0.3716 0.1697 -vn 0.8599 -0.4906 0.1406 -vn 0.7613 -0.6413 0.0960 -vn 0.9290 -0.2714 0.2514 -vn 0.9006 -0.3606 0.2427 -vn 0.8483 -0.4782 0.2273 -vn 0.7513 -0.6290 0.1994 -vn 0.5800 -0.8006 0.1503 -vn 0.9130 -0.2613 0.3132 -vn 0.8826 -0.3486 0.3153 -vn 0.8283 -0.4639 0.3140 -vn 0.7299 -0.6124 0.3034 -vn 0.5588 -0.7833 0.2724 -vn -0.0370 -0.9235 0.3818 -vn 0.0491 -0.8933 0.4468 -vn -0.1148 -0.8798 0.4612 -vn 0.0376 -0.9643 0.2621 -vn 0.1836 -0.9279 0.3245 -vn -0.0311 -0.9395 0.3411 -vn -0.1850 -0.4984 0.8470 -vn -0.1075 -0.5771 0.8095 -vn -0.0415 -0.5205 0.8528 -vn -0.0251 -0.4526 0.8914 -vn -0.0212 -0.6927 0.7209 -vn 0.1053 -0.6307 0.7689 -vn -0.1064 -0.6184 0.7786 -vn 0.4135 -0.4784 0.7746 -vn 0.0634 -0.7909 0.6085 -vn 0.2489 -0.7153 0.6530 -vn -0.0190 -0.7297 0.6834 -vn 0.5268 -0.5419 0.6548 -vn 0.6962 -0.3786 0.6098 -vn 0.0366 -0.8563 0.5150 -vn 0.3760 -0.7662 0.5210 -vn 0.0670 -0.8213 0.5665 -vn 0.6164 -0.5829 0.5294 -vn 0.7559 -0.4179 0.5039 -vn 0.8309 -0.2942 0.4723 -vn 0.4786 -0.7862 0.3908 -vn 0.6822 -0.6049 0.4106 -vn 0.7982 -0.4459 0.4049 -vn 0.8609 -0.3250 0.3913 -vn 0.8956 -0.2357 0.3772 -vn -0.6485 0.5278 -0.5484 -vn -0.6556 0.5622 -0.5041 -vn -0.7749 0.0091 0.6320 -vn -0.7767 -0.0013 0.6299 -vn -0.7498 0.0732 0.6575 -vn -0.7602 0.1772 0.6250 -vn -0.7636 0.2240 0.6055 -vn -0.7574 0.2726 0.5933 -vn -0.7616 0.3114 0.5683 -vn -0.7544 0.3525 0.5537 -vn -0.7554 0.3932 0.5241 -vn -0.7495 0.4353 0.4986 -vn -0.7532 0.4619 0.4683 -vn -0.7445 0.4959 0.4469 -vn -0.7442 0.5288 0.4080 -vn -0.7368 0.5653 0.3708 -vn -0.7356 0.5946 0.3245 -vn -0.7282 0.6256 0.2798 -vn -0.7260 0.6486 0.2283 -vn -0.7191 0.6714 0.1790 -vn -0.7223 0.6773 0.1393 -vn -0.7132 0.6927 0.1070 -vn -0.7094 0.7026 0.0560 -vn -0.7051 0.7090 0.0089 -vn -0.7004 0.7127 -0.0384 -vn -0.6979 0.7115 -0.0814 -vn -0.6817 0.7160 -0.1501 -vn -0.6848 0.6955 -0.2176 -vn -0.6791 0.6866 -0.2594 -vn -0.6750 0.6720 -0.3046 -vn -0.6704 0.6548 -0.3489 -vn -0.6648 0.6333 -0.3961 -vn -0.6681 0.6107 -0.4251 -vn -0.6576 0.5958 -0.4610 -vn -0.6482 0.4872 -0.5853 -vn -0.6413 0.4480 -0.6229 -vn -0.6424 0.4037 -0.6514 -vn -0.6360 0.3628 -0.6810 -vn -0.6413 0.3246 -0.6952 -vn -0.6326 0.2918 -0.7174 -vn -0.6345 0.2448 -0.7331 -vn -0.6288 0.1984 -0.7518 -vn -0.6341 0.1550 -0.7576 -vn -0.6261 0.1115 -0.7717 -vn -0.6310 0.0613 -0.7733 -vn -0.6250 0.0113 -0.7805 -vn -0.6373 -0.0116 -0.7705 -vn -0.6413 -0.0013 -0.7672 -vn -0.6128 -0.0756 -0.7866 -vn -0.6268 -0.1797 -0.7581 -vn -0.6337 -0.2266 -0.7396 -vn -0.6297 -0.2752 -0.7264 -vn -0.6385 -0.3140 -0.7026 -vn -0.6341 -0.3550 -0.6869 -vn -0.6405 -0.3958 -0.6581 -vn -0.6394 -0.4378 -0.6319 -vn -0.6487 -0.4645 -0.6028 -vn -0.6441 -0.4985 -0.5802 -vn -0.6511 -0.5313 -0.5419 -vn -0.6508 -0.5678 -0.5040 -vn -0.6583 -0.5971 -0.4582 -vn -0.6594 -0.6282 -0.4130 -vn -0.6670 -0.6512 -0.3620 -vn -0.6695 -0.6739 -0.3123 -vn -0.6802 -0.6799 -0.2739 -vn -0.6773 -0.6952 -0.2405 -vn -0.6832 -0.7051 -0.1896 -vn -0.6880 -0.7115 -0.1425 -vn -0.6923 -0.7153 -0.0952 -vn -0.6980 -0.7141 -0.0525 -vn -0.6952 -0.7186 0.0179 -vn -0.7111 -0.6980 0.0836 -vn -0.7136 -0.6892 0.1258 -vn -0.7181 -0.6746 0.1709 -vn -0.7222 -0.6573 0.2153 -vn -0.7257 -0.6359 0.2626 -vn -0.7345 -0.6133 0.2905 -vn -0.7311 -0.5984 0.3277 -vn -0.7374 -0.5648 0.3704 -vn -0.7391 -0.5304 0.4152 -vn -0.7458 -0.4898 0.4515 -vn -0.7464 -0.4505 0.4898 -vn -0.7530 -0.4063 0.5175 -vn -0.7526 -0.3654 0.5478 -vn -0.7606 -0.3272 0.5607 -vn -0.7564 -0.2943 0.5841 -vn -0.7614 -0.2474 0.5991 -vn -0.7596 -0.2009 0.6185 -vn -0.7660 -0.1575 0.6232 -vn -0.7610 -0.1141 0.6386 -vn -0.7663 -0.0638 0.6393 -vn -0.7620 -0.0139 0.6474 -usemtl Material.003 -s 1 -f 8/1/1 323/2/2 1/3/3 -f 2/4/4 14/5/5 26/6/6 -f 27/7/7 1/8/3 331/9/8 -f 2/4/4 26/6/6 45/10/9 -f 3/11/10 38/12/11 69/13/12 -f 2/4/4 45/10/9 56/14/13 -f 3/11/10 69/13/12 46/15/14 -f 4/16/15 80/17/16 98/18/17 -f 5/19/18 84/20/19 112/21/20 -f 6/22/21 91/23/22 99/24/23 -f 106/25/24 7/26/25 105/27/26 -f 104/28/27 113/29/28 105/27/26 -f 103/30/29 114/31/30 104/28/27 -f 102/32/31 116/33/32 103/30/29 -f 101/34/33 119/35/34 102/32/31 -f 100/36/35 123/37/36 101/34/33 -f 99/24/23 128/38/37 100/36/35 -f 105/27/26 113/29/28 106/25/24 -f 113/29/28 107/39/38 106/25/24 -f 104/28/27 114/31/30 113/29/28 -f 114/31/30 115/40/39 113/29/28 -f 113/29/28 115/40/39 107/39/38 -f 115/40/39 108/41/40 107/39/38 -f 103/30/29 116/33/32 114/31/30 -f 116/33/32 117/42/41 114/31/30 -f 114/31/30 117/42/41 115/40/39 -f 117/42/41 118/43/42 115/40/39 -f 115/40/39 118/43/42 108/41/40 -f 118/43/42 109/44/43 108/41/40 -f 102/32/31 119/35/34 116/33/32 -f 119/35/34 120/45/44 116/33/32 -f 116/33/32 120/45/44 117/42/41 -f 120/45/44 121/46/45 117/42/41 -f 117/42/41 121/46/45 118/43/42 -f 121/46/45 122/47/46 118/43/42 -f 118/43/42 122/47/46 109/44/43 -f 122/47/46 110/48/47 109/44/43 -f 101/34/33 123/37/36 119/35/34 -f 123/37/36 124/49/48 119/35/34 -f 119/35/34 124/49/48 120/45/44 -f 124/49/48 125/50/49 120/45/44 -f 120/45/44 125/50/49 121/46/45 -f 125/50/49 126/51/50 121/46/45 -f 121/46/45 126/51/50 122/47/46 -f 126/51/50 127/52/51 122/47/46 -f 122/47/46 127/52/51 110/48/47 -f 127/52/51 111/53/52 110/48/47 -f 100/36/35 128/38/37 123/37/36 -f 128/38/37 129/54/53 123/37/36 -f 123/37/36 129/54/53 124/49/48 -f 129/54/53 130/55/54 124/49/48 -f 124/49/48 130/55/54 125/50/49 -f 130/55/54 131/56/55 125/50/49 -f 125/50/49 131/56/55 126/51/50 -f 131/56/55 132/57/56 126/51/50 -f 126/51/50 132/57/56 127/52/51 -f 132/57/56 133/58/57 127/52/51 -f 127/52/51 133/58/57 111/53/52 -f 133/58/57 112/59/20 111/53/52 -f 99/24/23 91/23/22 128/38/37 -f 91/23/22 90/60/58 128/38/37 -f 128/38/37 90/60/58 129/54/53 -f 90/60/58 89/61/59 129/54/53 -f 129/54/53 89/61/59 130/55/54 -f 89/61/59 88/62/60 130/55/54 -f 130/55/54 88/62/60 131/56/55 -f 88/62/60 87/63/61 131/56/55 -f 131/56/55 87/63/61 132/57/56 -f 87/63/61 86/64/62 132/57/56 -f 132/57/56 86/64/62 133/58/57 -f 86/64/62 85/65/63 133/58/57 -f 133/58/57 85/65/63 112/59/20 -f 85/65/63 5/19/18 112/59/20 -f 106/66/24 335/67/64 7/68/25 -f 106/66/24 107/69/38 340/70/65 339/71/66 -f 107/69/38 108/72/40 344/73/67 343/74/68 -f 109/75/43 134/76/69 108/72/40 -f 110/77/47 135/78/70 109/75/43 -f 111/79/52 136/80/71 110/77/47 -f 112/21/20 137/81/72 111/79/52 -f 339/71/66 335/67/64 106/66/24 -f 343/74/68 340/70/65 107/69/38 -f 108/72/40 134/76/69 357/82/73 344/73/67 -f 362/83/74 357/82/73 134/76/69 -f 109/75/43 135/78/70 134/76/69 -f 134/76/69 135/78/70 366/84/75 365/85/76 -f 365/85/76 362/83/74 134/76/69 -f 110/77/47 136/80/71 135/78/70 -f 135/78/70 136/80/71 376/86/77 374/87/78 -f 374/87/78 366/84/75 135/78/70 -f 111/79/52 137/81/72 136/80/71 -f 136/80/71 137/81/72 381/88/79 379/89/80 -f 376/86/77 136/80/71 379/89/80 -f 112/21/20 84/20/19 137/81/72 -f 84/20/19 83/90/81 137/81/72 -f 137/81/72 83/90/81 389/91/82 381/88/79 -f 389/91/82 83/90/81 392/92/83 -f 92/93/84 7/94/25 396/95/85 -f 396/95/85 399/96/86 92/93/84 -f 93/97/87 92/93/84 399/96/86 402/98/88 -f 402/98/88 403/99/89 93/97/87 -f 94/100/90 93/97/87 403/99/89 405/101/91 -f 406/102/92 324/103/93 138/104/94 -f 138/104/94 94/100/90 405/101/91 406/102/92 -f 138/104/94 95/105/95 94/100/90 -f 324/103/93 325/106/96 138/104/94 -f 139/107/97 138/104/94 325/106/96 334/108/98 -f 138/104/94 139/107/97 95/105/95 -f 139/107/97 96/109/99 95/105/95 -f 334/108/98 336/110/100 139/107/97 -f 140/111/101 139/107/97 336/110/100 342/112/102 -f 139/107/97 140/111/101 96/109/99 -f 140/111/101 97/113/103 96/109/99 -f 140/111/101 342/112/102 349/114/104 -f 141/115/105 140/111/101 349/114/104 356/116/106 -f 140/111/101 141/115/105 97/113/103 -f 141/115/105 98/117/17 97/113/103 -f 360/118/107 361/119/108 82/120/109 -f 82/120/109 141/115/105 356/116/106 360/118/107 -f 82/120/109 81/121/110 141/115/105 -f 141/115/105 81/121/110 98/117/17 -f 81/121/110 4/16/15 98/117/17 -f 7/122/25 92/123/84 105/124/26 -f 93/125/87 142/126/111 92/123/84 -f 94/127/90 143/128/112 93/125/87 -f 95/129/95 145/130/113 94/127/90 -f 96/131/99 148/132/114 95/129/95 -f 97/133/103 152/134/115 96/131/99 -f 98/18/17 157/135/116 97/133/103 -f 92/123/84 142/126/111 105/124/26 -f 142/126/111 104/136/27 105/124/26 -f 93/125/87 143/128/112 142/126/111 -f 143/128/112 144/137/117 142/126/111 -f 142/126/111 144/137/117 104/136/27 -f 144/137/117 103/138/29 104/136/27 -f 94/127/90 145/130/113 143/128/112 -f 145/130/113 146/139/118 143/128/112 -f 143/128/112 146/139/118 144/137/117 -f 146/139/118 147/140/119 144/137/117 -f 144/137/117 147/140/119 103/138/29 -f 147/140/119 102/141/31 103/138/29 -f 95/129/95 148/132/114 145/130/113 -f 148/132/114 149/142/120 145/130/113 -f 145/130/113 149/142/120 146/139/118 -f 149/142/120 150/143/121 146/139/118 -f 146/139/118 150/143/121 147/140/119 -f 150/143/121 151/144/122 147/140/119 -f 147/140/119 151/144/122 102/141/31 -f 151/144/122 101/145/33 102/141/31 -f 96/131/99 152/134/115 148/132/114 -f 152/134/115 153/146/123 148/132/114 -f 148/132/114 153/146/123 149/142/120 -f 153/146/123 154/147/124 149/142/120 -f 149/142/120 154/147/124 150/143/121 -f 154/147/124 155/148/125 150/143/121 -f 150/143/121 155/148/125 151/144/122 -f 155/148/125 156/149/126 151/144/122 -f 151/144/122 156/149/126 101/145/33 -f 156/149/126 100/150/35 101/145/33 -f 97/133/103 157/135/116 152/134/115 -f 157/135/116 158/151/127 152/134/115 -f 152/134/115 158/151/127 153/146/123 -f 158/151/127 159/152/128 153/146/123 -f 153/146/123 159/152/128 154/147/124 -f 159/152/128 160/153/129 154/147/124 -f 154/147/124 160/153/129 155/148/125 -f 160/153/129 161/154/130 155/148/125 -f 155/148/125 161/154/130 156/149/126 -f 161/154/130 162/155/131 156/149/126 -f 156/149/126 162/155/131 100/150/35 -f 162/155/131 99/156/23 100/150/35 -f 98/18/17 80/17/16 157/135/116 -f 80/17/16 79/157/132 157/135/116 -f 157/135/116 79/157/132 158/151/127 -f 79/157/132 78/158/133 158/151/127 -f 158/151/127 78/158/133 159/152/128 -f 78/158/133 77/159/134 159/152/128 -f 159/152/128 77/159/134 160/153/129 -f 77/159/134 76/160/135 160/153/129 -f 160/153/129 76/160/135 161/154/130 -f 76/160/135 75/161/136 161/154/130 -f 161/154/130 75/161/136 162/155/131 -f 75/161/136 74/162/137 162/155/131 -f 162/155/131 74/162/137 99/156/23 -f 74/162/137 6/22/21 99/156/23 -f 52/163/138 91/23/22 6/22/21 -f 51/164/139 163/165/140 52/163/138 -f 50/166/141 164/167/142 51/164/139 -f 49/168/143 166/169/144 50/166/141 -f 48/170/145 169/171/146 49/168/143 -f 47/172/147 173/173/148 48/170/145 -f 46/15/14 178/174/149 47/172/147 -f 52/163/138 163/165/140 91/23/22 -f 163/165/140 90/60/58 91/23/22 -f 51/164/139 164/167/142 163/165/140 -f 164/167/142 165/175/150 163/165/140 -f 163/165/140 165/175/150 90/60/58 -f 165/175/150 89/61/59 90/60/58 -f 50/166/141 166/169/144 164/167/142 -f 166/169/144 167/176/151 164/167/142 -f 164/167/142 167/176/151 165/175/150 -f 167/176/151 168/177/152 165/175/150 -f 165/175/150 168/177/152 89/61/59 -f 168/177/152 88/62/60 89/61/59 -f 49/168/143 169/171/146 166/169/144 -f 169/171/146 170/178/153 166/169/144 -f 166/169/144 170/178/153 167/176/151 -f 170/178/153 171/179/154 167/176/151 -f 167/176/151 171/179/154 168/177/152 -f 171/179/154 172/180/155 168/177/152 -f 168/177/152 172/180/155 88/62/60 -f 172/180/155 87/63/61 88/62/60 -f 48/170/145 173/173/148 169/171/146 -f 173/173/148 174/181/156 169/171/146 -f 169/171/146 174/181/156 170/178/153 -f 174/181/156 175/182/157 170/178/153 -f 170/178/153 175/182/157 171/179/154 -f 175/182/157 176/183/158 171/179/154 -f 171/179/154 176/183/158 172/180/155 -f 176/183/158 177/184/159 172/180/155 -f 172/180/155 177/184/159 87/63/61 -f 177/184/159 86/64/62 87/63/61 -f 47/172/147 178/174/149 173/173/148 -f 178/174/149 179/185/160 173/173/148 -f 173/173/148 179/185/160 174/181/156 -f 179/185/160 180/186/161 174/181/156 -f 174/181/156 180/186/161 175/182/157 -f 180/186/161 181/187/162 175/182/157 -f 175/182/157 181/187/162 176/183/158 -f 181/187/162 182/188/163 176/183/158 -f 176/183/158 182/188/163 177/184/159 -f 182/188/163 183/189/164 177/184/159 -f 177/184/159 183/189/164 86/64/62 -f 183/189/164 85/65/63 86/64/62 -f 46/15/14 69/13/12 178/174/149 -f 69/13/12 68/190/165 178/174/149 -f 178/174/149 68/190/165 179/185/160 -f 68/190/165 67/191/166 179/185/160 -f 179/185/160 67/191/166 180/186/161 -f 67/191/166 66/192/167 180/186/161 -f 180/186/161 66/192/167 181/187/162 -f 66/192/167 65/193/168 181/187/162 -f 181/187/162 65/193/168 182/188/163 -f 65/193/168 64/194/169 182/188/163 -f 182/188/163 64/194/169 183/189/164 -f 64/194/169 63/195/170 183/189/164 -f 183/189/164 63/195/170 85/65/63 -f 63/195/170 5/19/18 85/65/63 -f 73/196/171 84/20/19 5/19/18 -f 72/197/172 184/198/173 73/196/171 -f 71/199/174 185/200/175 72/197/172 -f 71/199/174 70/201/176 373/202/177 375/203/178 -f 378/204/179 70/201/176 328/205/180 -f 73/196/171 184/198/173 84/20/19 -f 184/198/173 83/90/81 84/20/19 -f 72/197/172 185/200/175 184/198/173 -f 184/198/173 185/200/175 384/206/181 385/207/182 -f 83/90/81 184/198/173 385/207/182 388/208/183 -f 392/92/83 83/90/81 388/208/183 -f 185/200/175 71/199/174 375/203/178 397/209/184 -f 401/210/185 185/200/175 397/209/184 -f 384/206/181 185/200/175 401/210/185 -f 373/202/177 70/201/176 378/204/179 -f 361/119/108 407/211/186 82/120/109 -f 408/212/187 409/213/188 186/214/189 -f 410/215/190 408/212/187 186/214/189 -f 186/214/189 187/216/191 411/217/192 410/215/190 -f 187/216/191 82/120/109 407/211/186 411/217/192 -f 187/216/191 81/121/110 82/120/109 -f 412/218/193 350/219/194 55/220/195 -f 55/220/195 186/214/189 409/213/188 412/218/193 -f 55/220/195 54/221/196 186/214/189 -f 186/214/189 54/221/196 187/216/191 -f 54/221/196 53/222/197 187/216/191 -f 187/216/191 53/222/197 81/121/110 -f 53/222/197 4/16/15 81/121/110 -f 62/223/198 80/17/16 4/16/15 -f 61/224/199 188/225/200 62/223/198 -f 60/226/201 189/227/202 61/224/199 -f 59/228/203 191/229/204 60/226/201 -f 58/230/205 194/231/206 59/228/203 -f 57/232/207 198/233/208 58/230/205 -f 56/14/13 203/234/209 57/232/207 -f 62/223/198 188/225/200 80/17/16 -f 188/225/200 79/157/132 80/17/16 -f 61/224/199 189/227/202 188/225/200 -f 189/227/202 190/235/210 188/225/200 -f 188/225/200 190/235/210 79/157/132 -f 190/235/210 78/158/133 79/157/132 -f 60/226/201 191/229/204 189/227/202 -f 191/229/204 192/236/211 189/227/202 -f 189/227/202 192/236/211 190/235/210 -f 192/236/211 193/237/212 190/235/210 -f 190/235/210 193/237/212 78/158/133 -f 193/237/212 77/159/134 78/158/133 -f 59/228/203 194/231/206 191/229/204 -f 194/231/206 195/238/213 191/229/204 -f 191/229/204 195/238/213 192/236/211 -f 195/238/213 196/239/214 192/236/211 -f 192/236/211 196/239/214 193/237/212 -f 196/239/214 197/240/215 193/237/212 -f 193/237/212 197/240/215 77/159/134 -f 197/240/215 76/160/135 77/159/134 -f 58/230/205 198/233/208 194/231/206 -f 198/233/208 199/241/216 194/231/206 -f 194/231/206 199/241/216 195/238/213 -f 199/241/216 200/242/217 195/238/213 -f 195/238/213 200/242/217 196/239/214 -f 200/242/217 201/243/218 196/239/214 -f 196/239/214 201/243/218 197/240/215 -f 201/243/218 202/244/219 197/240/215 -f 197/240/215 202/244/219 76/160/135 -f 202/244/219 75/161/136 76/160/135 -f 57/232/207 203/234/209 198/233/208 -f 203/234/209 204/245/220 198/233/208 -f 198/233/208 204/245/220 199/241/216 -f 204/245/220 205/246/221 199/241/216 -f 199/241/216 205/246/221 200/242/217 -f 205/246/221 206/247/222 200/242/217 -f 200/242/217 206/247/222 201/243/218 -f 206/247/222 207/248/223 201/243/218 -f 201/243/218 207/248/223 202/244/219 -f 207/248/223 208/249/224 202/244/219 -f 202/244/219 208/249/224 75/161/136 -f 208/249/224 74/162/137 75/161/136 -f 56/14/13 45/10/9 203/234/209 -f 45/10/9 44/250/225 203/234/209 -f 203/234/209 44/250/225 204/245/220 -f 44/250/225 43/251/226 204/245/220 -f 204/245/220 43/251/226 205/246/221 -f 43/251/226 42/252/227 205/246/221 -f 205/246/221 42/252/227 206/247/222 -f 42/252/227 41/253/228 206/247/222 -f 206/247/222 41/253/228 207/248/223 -f 41/253/228 40/254/229 207/248/223 -f 207/248/223 40/254/229 208/249/224 -f 40/254/229 39/255/230 208/249/224 -f 208/249/224 39/255/230 74/162/137 -f 39/255/230 6/22/21 74/162/137 -f 63/195/170 73/196/171 5/19/18 -f 64/194/169 209/256/231 63/195/170 -f 65/193/168 210/257/232 64/194/169 -f 66/192/167 212/258/233 65/193/168 -f 67/191/166 215/259/234 66/192/167 -f 68/190/165 219/260/235 67/191/166 -f 69/13/12 223/261/236 68/190/165 -f 63/195/170 209/256/231 73/196/171 -f 209/256/231 72/197/172 73/196/171 -f 64/194/169 210/257/232 209/256/231 -f 210/257/232 211/262/237 209/256/231 -f 209/256/231 211/262/237 72/197/172 -f 211/262/237 71/199/174 72/197/172 -f 65/193/168 212/258/233 210/257/232 -f 212/258/233 213/263/238 210/257/232 -f 210/257/232 213/263/238 211/262/237 -f 213/263/238 214/264/239 211/262/237 -f 211/262/237 214/264/239 71/199/174 -f 71/199/174 214/264/239 70/201/176 -f 66/192/167 215/259/234 212/258/233 -f 215/259/234 216/265/240 212/258/233 -f 212/258/233 216/265/240 213/263/238 -f 216/265/240 217/266/241 213/263/238 -f 213/263/238 217/266/241 214/264/239 -f 217/266/241 218/267/242 214/264/239 -f 70/201/176 214/264/239 218/267/242 -f 70/201/176 218/267/242 329/268/243 328/205/180 -f 67/191/166 219/260/235 215/259/234 -f 219/260/235 220/269/244 215/259/234 -f 215/259/234 220/269/244 216/265/240 -f 220/269/244 221/270/245 216/265/240 -f 216/265/240 221/270/245 217/266/241 -f 221/270/245 222/271/246 217/266/241 -f 217/266/241 222/271/246 218/267/242 -f 218/267/242 222/271/246 332/272/247 330/273/248 -f 329/268/243 218/267/242 330/273/248 -f 68/190/165 223/261/236 219/260/235 -f 223/261/236 224/274/249 219/260/235 -f 219/260/235 224/274/249 220/269/244 -f 224/274/249 225/275/250 220/269/244 -f 220/269/244 225/275/250 221/270/245 -f 225/275/250 226/276/251 221/270/245 -f 221/270/245 226/276/251 222/271/246 -f 226/276/251 227/277/252 222/271/246 -f 222/271/246 227/277/252 337/278/253 332/272/247 -f 337/278/253 227/277/252 341/279/254 -f 69/13/12 38/12/11 223/261/236 -f 38/12/11 37/280/255 223/261/236 -f 223/261/236 37/280/255 224/274/249 -f 37/280/255 36/281/256 224/274/249 -f 224/274/249 36/281/256 225/275/250 -f 36/281/256 35/282/257 225/275/250 -f 225/275/250 35/282/257 226/276/251 -f 35/282/257 34/283/258 226/276/251 -f 226/276/251 34/283/258 227/277/252 -f 227/277/252 34/283/258 346/284/259 345/285/260 -f 341/279/254 227/277/252 345/285/260 -f 53/222/197 62/223/198 4/16/15 -f 54/221/196 228/286/261 53/222/197 -f 55/220/195 229/287/262 54/221/196 -f 231/288/263 55/220/195 350/219/194 353/289/264 -f 354/290/265 355/291/266 234/292/267 -f 53/222/197 228/286/261 62/223/198 -f 228/286/261 61/224/199 62/223/198 -f 54/221/196 229/287/262 228/286/261 -f 229/287/262 230/293/268 228/286/261 -f 228/286/261 230/293/268 61/224/199 -f 230/293/268 60/226/201 61/224/199 -f 55/220/195 231/288/263 229/287/262 -f 231/288/263 232/294/269 229/287/262 -f 229/287/262 232/294/269 230/293/268 -f 232/294/269 233/295/270 230/293/268 -f 230/293/268 233/295/270 60/226/201 -f 233/295/270 59/228/203 60/226/201 -f 234/292/267 231/288/263 353/289/264 354/290/265 -f 234/292/267 235/296/271 231/288/263 -f 231/288/263 235/296/271 232/294/269 -f 235/296/271 236/297/272 232/294/269 -f 232/294/269 236/297/272 233/295/270 -f 236/297/272 237/298/273 233/295/270 -f 233/295/270 237/298/273 59/228/203 -f 237/298/273 58/230/205 59/228/203 -f 355/291/266 364/299/274 234/292/267 -f 238/300/275 234/292/267 364/299/274 367/301/276 -f 234/292/267 238/300/275 235/296/271 -f 238/300/275 239/302/277 235/296/271 -f 235/296/271 239/302/277 236/297/272 -f 239/302/277 240/303/278 236/297/272 -f 236/297/272 240/303/278 237/298/273 -f 240/303/278 241/304/279 237/298/273 -f 237/298/273 241/304/279 58/230/205 -f 241/304/279 57/232/207 58/230/205 -f 369/305/280 370/306/281 242/307/282 -f 242/307/282 238/300/275 367/301/276 369/305/280 -f 242/307/282 243/308/283 238/300/275 -f 238/300/275 243/308/283 239/302/277 -f 243/308/283 244/309/284 239/302/277 -f 239/302/277 244/309/284 240/303/278 -f 244/309/284 245/310/285 240/303/278 -f 240/303/278 245/310/285 241/304/279 -f 245/310/285 246/311/286 241/304/279 -f 241/304/279 246/311/286 57/232/207 -f 246/311/286 56/14/13 57/232/207 -f 370/306/281 377/312/287 242/307/282 -f 19/313/288 242/307/282 377/312/287 368/314/289 -f 242/307/282 19/313/288 243/308/283 -f 19/313/288 18/315/290 243/308/283 -f 243/308/283 18/315/290 244/309/284 -f 18/315/290 17/316/291 244/309/284 -f 244/309/284 17/316/291 245/310/285 -f 17/316/291 16/317/292 245/310/285 -f 245/310/285 16/317/292 246/311/286 -f 16/317/292 15/318/293 246/311/286 -f 246/311/286 15/318/293 56/14/13 -f 15/318/293 2/4/4 56/14/13 -f 39/255/230 52/163/138 6/22/21 -f 40/254/229 247/319/294 39/255/230 -f 41/253/228 248/320/295 40/254/229 -f 42/252/227 250/321/296 41/253/228 -f 43/251/226 253/322/297 42/252/227 -f 44/250/225 257/323/298 43/251/226 -f 45/10/9 262/324/299 44/250/225 -f 39/255/230 247/319/294 52/163/138 -f 247/319/294 51/164/139 52/163/138 -f 40/254/229 248/320/295 247/319/294 -f 248/320/295 249/325/300 247/319/294 -f 247/319/294 249/325/300 51/164/139 -f 249/325/300 50/166/141 51/164/139 -f 41/253/228 250/321/296 248/320/295 -f 250/321/296 251/326/301 248/320/295 -f 248/320/295 251/326/301 249/325/300 -f 251/326/301 252/327/302 249/325/300 -f 249/325/300 252/327/302 50/166/141 -f 252/327/302 49/168/143 50/166/141 -f 42/252/227 253/322/297 250/321/296 -f 253/322/297 254/328/303 250/321/296 -f 250/321/296 254/328/303 251/326/301 -f 254/328/303 255/329/304 251/326/301 -f 251/326/301 255/329/304 252/327/302 -f 255/329/304 256/330/305 252/327/302 -f 252/327/302 256/330/305 49/168/143 -f 256/330/305 48/170/145 49/168/143 -f 43/251/226 257/323/298 253/322/297 -f 257/323/298 258/331/306 253/322/297 -f 253/322/297 258/331/306 254/328/303 -f 258/331/306 259/332/307 254/328/303 -f 254/328/303 259/332/307 255/329/304 -f 259/332/307 260/333/308 255/329/304 -f 255/329/304 260/333/308 256/330/305 -f 260/333/308 261/334/309 256/330/305 -f 256/330/305 261/334/309 48/170/145 -f 261/334/309 47/172/147 48/170/145 -f 44/250/225 262/324/299 257/323/298 -f 262/324/299 263/335/310 257/323/298 -f 257/323/298 263/335/310 258/331/306 -f 263/335/310 264/336/311 258/331/306 -f 258/331/306 264/336/311 259/332/307 -f 264/336/311 265/337/312 259/332/307 -f 259/332/307 265/337/312 260/333/308 -f 265/337/312 266/338/313 260/333/308 -f 260/333/308 266/338/313 261/334/309 -f 266/338/313 267/339/314 261/334/309 -f 261/334/309 267/339/314 47/172/147 -f 267/339/314 46/15/14 47/172/147 -f 45/10/9 26/6/6 262/324/299 -f 26/6/6 25/340/315 262/324/299 -f 262/324/299 25/340/315 263/335/310 -f 25/340/315 24/341/316 263/335/310 -f 263/335/310 24/341/316 264/336/311 -f 24/341/316 23/342/317 264/336/311 -f 264/336/311 23/342/317 265/337/312 -f 23/342/317 22/343/318 265/337/312 -f 265/337/312 22/343/318 266/338/313 -f 22/343/318 21/344/319 266/338/313 -f 266/338/313 21/344/319 267/339/314 -f 21/344/319 20/345/320 267/339/314 -f 267/339/314 20/345/320 46/15/14 -f 20/345/320 3/11/10 46/15/14 -f 33/346/321 38/12/11 3/11/10 -f 32/347/322 268/348/323 33/346/321 -f 31/349/324 269/350/325 32/347/322 -f 30/351/326 271/352/327 31/349/324 -f 29/353/328 274/354/329 30/351/326 -f 28/355/330 278/356/331 29/353/328 -f 27/7/7 283/357/332 28/355/330 -f 33/346/321 268/348/323 38/12/11 -f 268/348/323 37/280/255 38/12/11 -f 32/347/322 269/350/325 268/348/323 -f 269/350/325 270/358/333 268/348/323 -f 268/348/323 270/358/333 37/280/255 -f 270/358/333 36/281/256 37/280/255 -f 31/349/324 271/352/327 269/350/325 -f 271/352/327 272/359/334 269/350/325 -f 269/350/325 272/359/334 270/358/333 -f 272/359/334 273/360/335 270/358/333 -f 270/358/333 273/360/335 36/281/256 -f 273/360/335 35/282/257 36/281/256 -f 30/351/326 274/354/329 271/352/327 -f 274/354/329 275/361/336 271/352/327 -f 271/352/327 275/361/336 272/359/334 -f 275/361/336 276/362/337 272/359/334 -f 272/359/334 276/362/337 273/360/335 -f 276/362/337 277/363/338 273/360/335 -f 273/360/335 277/363/338 35/282/257 -f 277/363/338 34/283/258 35/282/257 -f 29/353/328 278/356/331 274/354/329 -f 278/356/331 279/364/339 274/354/329 -f 274/354/329 279/364/339 275/361/336 -f 279/364/339 280/365/340 275/361/336 -f 275/361/336 280/365/340 276/362/337 -f 280/365/340 281/366/341 276/362/337 -f 276/362/337 281/366/341 277/363/338 -f 281/366/341 282/367/342 277/363/338 -f 277/363/338 282/367/342 34/283/258 -f 34/283/258 282/367/342 380/368/343 346/284/259 -f 28/355/330 283/357/332 278/356/331 -f 283/357/332 284/369/344 278/356/331 -f 278/356/331 284/369/344 279/364/339 -f 279/364/339 284/369/344 338/370/345 386/371/346 -f 280/365/340 279/364/339 386/371/346 387/372/347 -f 390/373/348 280/365/340 387/372/347 -f 281/366/341 280/365/340 390/373/348 393/374/349 -f 394/375/350 281/366/341 393/374/349 -f 282/367/342 281/366/341 394/375/350 398/376/351 -f 400/377/352 282/367/342 398/376/351 -f 380/368/343 282/367/342 400/377/352 -f 283/357/332 27/7/7 331/9/8 404/378/353 -f 327/379/354 283/357/332 404/378/353 -f 284/369/344 283/357/332 327/379/354 326/380/355 -f 333/381/356 284/369/344 326/380/355 -f 338/370/345 284/369/344 333/381/356 -f 20/345/320 33/382/321 3/11/10 -f 21/344/319 285/383/357 20/345/320 -f 22/343/318 286/384/358 21/344/319 -f 23/342/317 288/385/359 22/343/318 -f 24/341/316 291/386/360 23/342/317 -f 25/340/315 295/387/361 24/341/316 -f 26/6/6 300/388/362 25/340/315 -f 20/345/320 285/383/357 33/382/321 -f 285/383/357 32/389/322 33/382/321 -f 21/344/319 286/384/358 285/383/357 -f 286/384/358 287/390/363 285/383/357 -f 285/383/357 287/390/363 32/389/322 -f 287/390/363 31/391/324 32/389/322 -f 22/343/318 288/385/359 286/384/358 -f 288/385/359 289/392/364 286/384/358 -f 286/384/358 289/392/364 287/390/363 -f 289/392/364 290/393/365 287/390/363 -f 287/390/363 290/393/365 31/391/324 -f 290/393/365 30/394/326 31/391/324 -f 23/342/317 291/386/360 288/385/359 -f 291/386/360 292/395/366 288/385/359 -f 288/385/359 292/395/366 289/392/364 -f 292/395/366 293/396/367 289/392/364 -f 289/392/364 293/396/367 290/393/365 -f 293/396/367 294/397/368 290/393/365 -f 290/393/365 294/397/368 30/394/326 -f 294/397/368 29/398/328 30/394/326 -f 24/341/316 295/387/361 291/386/360 -f 295/387/361 296/399/369 291/386/360 -f 291/386/360 296/399/369 292/395/366 -f 296/399/369 297/400/370 292/395/366 -f 292/395/366 297/400/370 293/396/367 -f 297/400/370 298/401/371 293/396/367 -f 293/396/367 298/401/371 294/397/368 -f 298/401/371 299/402/372 294/397/368 -f 294/397/368 299/402/372 29/398/328 -f 299/402/372 28/403/330 29/398/328 -f 25/340/315 300/388/362 295/387/361 -f 300/388/362 301/404/373 295/387/361 -f 295/387/361 301/404/373 296/399/369 -f 301/404/373 302/405/374 296/399/369 -f 296/399/369 302/405/374 297/400/370 -f 302/405/374 303/406/375 297/400/370 -f 297/400/370 303/406/375 298/401/371 -f 303/406/375 304/407/376 298/401/371 -f 298/401/371 304/407/376 299/402/372 -f 304/407/376 305/408/377 299/402/372 -f 299/402/372 305/408/377 28/403/330 -f 305/408/377 27/409/7 28/403/330 -f 26/6/6 14/5/5 300/388/362 -f 14/5/5 13/410/378 300/388/362 -f 300/388/362 13/410/378 301/404/373 -f 13/410/378 12/411/379 301/404/373 -f 301/404/373 12/411/379 302/405/374 -f 12/411/379 11/412/380 302/405/374 -f 302/405/374 11/412/380 303/406/375 -f 11/412/380 10/413/381 303/406/375 -f 303/406/375 10/413/381 304/407/376 -f 10/413/381 9/414/382 304/407/376 -f 304/407/376 9/414/382 305/408/377 -f 9/414/382 8/415/1 305/408/377 -f 305/408/377 8/415/1 27/409/7 -f 1/416/3 27/409/7 8/415/1 -f 347/417/383 312/418/384 348/419/385 -f 351/420/386 317/421/387 352/422/388 -f 358/423/389 359/424/390 306/425/391 -f 363/426/392 358/423/389 306/425/391 -f 306/425/391 19/313/288 368/314/289 363/426/392 -f 371/427/393 307/428/394 372/429/395 -f 307/428/394 306/425/391 359/424/390 372/429/395 -f 307/428/394 308/430/396 306/425/391 -f 306/425/391 308/430/396 19/313/288 -f 308/430/396 18/315/290 19/313/288 -f 382/431/397 309/432/398 383/433/399 -f 309/432/398 307/428/394 371/427/393 383/433/399 -f 309/432/398 310/434/400 307/428/394 -f 307/428/394 310/434/400 308/430/396 -f 310/434/400 311/435/401 308/430/396 -f 308/430/396 311/435/401 18/315/290 -f 311/435/401 17/316/291 18/315/290 -f 348/419/385 312/418/384 391/436/402 -f 312/418/384 313/437/403 395/438/404 391/436/402 -f 313/437/403 309/432/398 382/431/397 395/438/404 -f 313/437/403 314/439/405 309/432/398 -f 309/432/398 314/439/405 310/434/400 -f 314/439/405 315/440/406 310/434/400 -f 310/434/400 315/440/406 311/435/401 -f 315/440/406 316/441/407 311/435/401 -f 311/435/401 316/441/407 17/316/291 -f 316/441/407 16/317/292 17/316/291 -f 317/421/387 312/418/384 347/417/383 352/422/388 -f 317/421/387 318/442/408 312/418/384 -f 312/418/384 318/442/408 313/437/403 -f 318/442/408 319/443/409 313/437/403 -f 313/437/403 319/443/409 314/439/405 -f 319/443/409 320/444/410 314/439/405 -f 314/439/405 320/444/410 315/440/406 -f 320/444/410 321/445/411 315/440/406 -f 315/440/406 321/445/411 316/441/407 -f 321/445/411 322/446/412 316/441/407 -f 316/441/407 322/446/412 16/317/292 -f 322/446/412 15/318/293 16/317/292 -f 8/1/1 317/421/387 351/420/386 323/2/2 -f 8/1/1 9/447/382 317/421/387 -f 317/421/387 9/447/382 318/442/408 -f 9/447/382 10/448/381 318/442/408 -f 318/442/408 10/448/381 319/443/409 -f 10/448/381 11/449/380 319/443/409 -f 319/443/409 11/449/380 320/444/410 -f 11/449/380 12/450/379 320/444/410 -f 320/444/410 12/450/379 321/445/411 -f 12/450/379 13/451/378 321/445/411 -f 321/445/411 13/451/378 322/446/412 -f 13/451/378 14/452/5 322/446/412 -f 322/446/412 14/452/5 15/318/293 -f 14/452/5 2/4/4 15/318/293 -f 365/85/76 366/84/75 493/453/413 490/454/414 -f 415/455/415 428/456/416 503/457/417 492/458/418 477/459/419 476/460/420 478/461/421 487/462/422 486/463/423 491/464/424 439/465/425 438/466/426 456/467/427 455/468/428 502/469/429 467/470/430 466/471/431 441/472/432 442/473/433 450/474/434 422/475/435 421/476/436 497/477/437 485/478/438 483/479/439 484/480/440 443/481/441 436/482/442 437/483/443 474/484/444 469/485/445 470/486/446 490/487/414 493/488/413 434/489/447 435/490/448 464/491/449 465/492/450 471/493/451 440/494/452 431/495/453 430/496/454 461/497/455 414/498/456 413/499/457 480/500/458 479/501/459 475/502/460 472/503/461 473/504/462 488/505/463 489/506/464 426/507/465 427/508/466 468/509/467 462/510/468 463/511/469 498/512/470 482/513/471 481/514/472 460/515/473 449/516/474 420/517/475 419/518/476 504/519/477 454/520/478 452/521/479 453/522/480 451/523/481 425/524/482 423/525/483 424/526/484 444/527/485 500/528/486 499/529/487 496/530/488 432/531/489 433/532/490 494/533/491 448/534/492 446/535/493 447/536/494 458/537/495 457/538/496 459/539/497 501/540/498 418/541/499 417/542/500 429/543/501 495/544/502 445/545/503 416/546/504 -f 412/218/193 409/213/188 477/547/419 492/548/418 -f 404/378/353 331/9/8 425/549/482 451/550/481 -f 391/436/402 395/438/404 433/551/490 432/552/489 -f 379/89/80 381/88/79 465/553/450 464/554/449 -f 390/373/348 387/372/347 420/555/475 449/556/474 -f 332/272/247 337/278/253 426/557/465 489/558/464 -f 393/374/349 390/373/348 449/556/474 460/559/473 -f 402/98/88 399/96/86 497/560/437 421/561/436 -f 355/291/266 354/290/265 415/562/415 416/563/504 -f 336/110/100 334/108/98 467/564/430 502/565/429 -f 326/380/355 327/379/354 453/566/480 452/567/479 -f 374/87/78 376/86/77 435/568/448 434/569/447 -f 388/208/183 385/207/182 430/570/454 431/571/453 -f 334/108/98 325/106/96 466/572/431 467/564/430 -f 329/268/243 330/273/248 488/573/463 473/574/462 -f 396/95/85 7/94/25 483/575/439 485/576/438 -f 381/88/79 389/91/82 471/577/451 465/553/450 -f 397/209/184 375/203/178 480/578/458 413/579/457 -f 341/279/254 345/285/260 468/580/467 427/581/466 -f 353/289/264 350/219/194 503/582/417 428/583/416 -f 382/431/397 383/433/399 448/584/492 494/585/491 -f 366/84/75 374/87/78 434/569/447 493/453/413 -f 340/70/65 343/74/68 437/586/443 436/587/442 -f 333/381/356 326/380/355 452/567/479 454/588/478 -f 361/119/108 360/118/107 439/589/425 491/590/424 -f 345/285/260 346/284/259 462/591/468 468/580/467 -f 405/101/91 403/99/89 422/592/435 450/593/434 -f 342/112/102 336/110/100 502/565/429 455/594/428 -f 410/215/190 411/217/192 487/595/422 478/596/421 -f 330/273/248 332/272/247 489/558/464 488/573/463 -f 360/118/107 356/116/106 438/597/426 439/589/425 -f 327/379/354 404/378/353 451/550/481 453/566/480 -f 339/71/66 340/70/65 436/587/442 443/598/441 -f 325/106/96 324/103/93 441/599/432 466/572/431 -f 7/68/25 335/67/64 484/600/440 483/601/439 -f 368/314/289 377/312/287 418/602/499 501/603/498 -f 367/301/276 364/299/274 445/604/503 495/605/502 -f 411/217/192 407/211/186 486/606/423 487/595/422 -f 357/82/73 362/83/74 470/607/446 469/608/445 -f 349/114/104 342/112/102 455/594/428 456/609/427 -f 343/74/68 344/73/67 474/610/444 437/586/443 -f 409/213/188 408/212/187 476/611/420 477/547/419 -f 328/205/180 329/268/243 473/574/462 472/612/461 -f 377/312/287 370/306/281 417/613/500 418/602/499 -f 385/207/182 384/206/181 461/614/455 430/570/454 -f 323/2/2 351/420/386 444/615/485 424/616/484 -f 408/212/187 410/215/190 478/596/421 476/611/420 -f 392/92/83 388/208/183 431/571/453 440/617/452 -f 376/86/77 379/89/80 464/554/449 435/568/448 -f 359/424/390 358/423/389 457/618/496 458/619/495 -f 335/67/64 339/71/66 443/598/441 484/600/440 -f 387/372/347 386/371/346 419/620/476 420/555/475 -f 324/103/93 406/102/92 442/621/433 441/599/432 -f 375/203/178 373/202/177 479/622/459 480/578/458 -f 364/299/274 355/291/266 416/563/504 445/604/503 -f 358/423/389 363/426/392 459/623/497 457/618/496 -f 395/438/404 382/431/397 494/585/491 433/551/490 -f 348/419/385 391/436/402 432/552/489 496/624/488 -f 398/376/351 394/375/350 481/625/472 482/626/471 -f 346/284/259 380/368/343 463/627/469 462/591/468 -f 384/206/181 401/210/185 414/628/456 461/614/455 -f 344/73/67 357/82/73 469/608/445 474/610/444 -f 351/420/386 352/422/388 500/629/486 444/615/485 -f 347/417/383 348/419/385 496/624/488 499/630/487 -f 362/83/74 365/85/76 490/454/414 470/607/446 -f 399/96/86 396/95/85 485/576/438 497/560/437 -f 1/3/3 323/2/2 424/616/484 423/631/483 -f 371/427/393 372/429/395 447/632/494 446/633/493 -f 352/422/388 347/417/383 499/630/487 500/629/486 -f 394/375/350 393/374/349 460/559/473 481/625/472 -f 372/429/395 359/424/390 458/619/495 447/632/494 -f 400/377/352 398/376/351 482/626/471 498/634/470 -f 380/368/343 400/377/352 498/634/470 463/627/469 -f 331/9/8 1/8/3 423/635/483 425/549/482 -f 373/202/177 378/204/179 475/636/460 479/622/459 -f 389/91/82 392/92/83 440/617/452 471/577/451 -f 337/278/253 341/279/254 427/581/466 426/557/465 -f 403/99/89 402/98/88 421/561/436 422/592/435 -f 369/305/280 367/301/276 495/605/502 429/637/501 -f 338/370/345 333/381/356 454/588/478 504/638/477 -f 350/219/194 412/218/193 492/548/418 503/582/417 -f 354/290/265 353/289/264 428/583/416 415/562/415 -f 363/426/392 368/314/289 501/603/498 459/623/497 -f 401/210/185 397/209/184 413/579/457 414/628/456 -f 370/306/281 369/305/280 429/637/501 417/613/500 -f 407/211/186 361/119/108 491/590/424 486/606/423 -f 386/371/346 338/370/345 504/638/477 419/620/476 -f 406/102/92 405/101/91 450/593/434 442/621/433 -f 383/433/399 371/427/393 446/633/493 448/584/492 -f 378/204/179 328/205/180 472/612/461 475/636/460 -f 356/116/106 349/114/104 456/609/427 438/597/426 -o spot2_Icosphere.002 -v 0.476287 -0.233019 0.961512 -v 0.388001 -0.104953 1.060991 -v 0.608220 -0.104250 0.951316 -v 0.308174 0.104629 1.072588 -v 0.664500 0.105766 0.895127 -v 0.504322 0.104429 1.019964 -v 0.479061 0.234857 0.970078 -v 0.464425 -0.231516 0.974489 -v 0.451326 -0.225872 0.988917 -v 0.437599 -0.215072 1.004159 -v 0.424151 -0.198816 1.019232 -v 0.411957 -0.177876 1.033052 -v 0.401746 -0.153953 1.044783 -v 0.393802 -0.129074 1.054065 -v 0.369079 -0.111863 1.064058 -v 0.349676 -0.117705 1.065829 -v 0.331167 -0.121634 1.065706 -v 0.315152 -0.122938 1.063287 -v 0.302949 -0.121337 1.058582 -v 0.587550 -0.111344 0.964974 -v 0.562463 -0.117400 0.980328 -v 0.533397 -0.121571 0.996755 -v 0.501698 -0.123127 1.013239 -v 0.469436 -0.121775 1.028610 -v 0.438792 -0.117795 1.041920 -v 0.411392 -0.111906 1.052706 -v 0.493784 -0.231423 0.959867 -v 0.513162 -0.225675 0.958121 -v 0.533541 -0.214766 0.956377 -v 0.553591 -0.198403 0.954767 -v 0.571861 -0.177366 0.953415 -v 0.587253 -0.153361 0.952395 -v 0.599320 -0.128418 0.951711 -v 0.657625 -0.120205 0.881943 -v 0.654036 -0.121857 0.894513 -v 0.646311 -0.120629 0.908755 -v 0.635237 -0.116794 0.923612 -v 0.622103 -0.111056 0.938045 -v 0.490588 0.083404 1.029039 -v 0.474942 0.058386 1.038156 -v 0.457956 0.029955 1.046602 -v 0.440629 -0.000469 1.053590 -v 0.424171 -0.030859 1.058518 -v 0.409618 -0.059200 1.061196 -v 0.397540 -0.084085 1.061858 -v 0.603058 -0.083429 0.959504 -v 0.595125 -0.058608 0.968808 -v 0.584075 -0.030349 0.978881 -v 0.570070 -0.000056 0.989124 -v 0.553898 0.030261 0.998820 -v 0.536777 0.058583 1.007360 -v 0.519947 0.083498 1.014418 -v 0.294582 0.083728 1.074880 -v 0.282432 0.058842 1.075597 -v 0.273203 0.030546 1.074127 -v 0.371389 -0.084058 1.068874 -v 0.354541 -0.059144 1.075973 -v 0.338714 -0.030773 1.081445 -v 0.325335 -0.000351 1.084522 -v 0.315529 0.030100 1.084813 -v 0.309712 0.058554 1.082485 -v 0.307534 0.083591 1.078150 -v 0.669433 0.084745 0.897914 -v 0.671708 0.059709 0.902200 -v 0.670207 0.031231 0.908174 -v 0.664222 0.000729 0.915747 -v 0.653861 -0.029768 0.924493 -v 0.640102 -0.058233 0.933755 -v 0.624413 -0.083251 0.942861 -v 0.687117 0.001587 0.861486 -v 0.687177 0.031867 0.867956 -v 0.682646 0.060119 0.876280 -v 0.674626 0.084940 0.885607 -v 0.479945 0.111346 1.030585 -v 0.452528 0.117234 1.041412 -v 0.423178 0.121263 1.051654 -v 0.393708 0.122720 1.060406 -v 0.366208 0.121321 1.066939 -v 0.342374 0.117347 1.070965 -v 0.323042 0.111506 1.072680 -v 0.302085 0.111727 1.067389 -v 0.298236 0.117813 1.059820 -v 0.660231 0.118967 0.879536 -v 0.663983 0.112881 0.887153 -v 0.655580 0.112567 0.907067 -v 0.642534 0.118304 0.921477 -v 0.624941 0.122146 0.938082 -v 0.603150 0.123388 0.956098 -v 0.578417 0.121758 0.974340 -v 0.552580 0.117553 0.991583 -v 0.527450 0.111497 1.006927 -v 0.456371 0.233067 0.984030 -v 0.431249 0.227104 0.999389 -v 0.404836 0.215968 1.015431 -v 0.378861 0.199384 1.031083 -v 0.355203 0.178145 1.045204 -v 0.335282 0.153970 1.056957 -v 0.319675 0.128895 1.066029 -v 0.502728 0.128708 1.016919 -v 0.500511 0.153801 1.012628 -v 0.497628 0.177999 1.006994 -v 0.494152 0.199266 1.000152 -v 0.490291 0.215881 0.992505 -v 0.486325 0.227047 0.984613 -v 0.482521 0.233040 0.977014 -v 0.503876 0.233218 0.960371 -v 0.531301 0.227423 0.949560 -v 0.560075 0.216464 0.938118 -v 0.588302 0.200052 0.926775 -v 0.613935 0.178970 0.916348 -v 0.635440 0.154927 0.907469 -v 0.652213 0.129955 0.900416 -v 0.509158 0.229742 0.967101 -v 0.514419 0.221274 0.974583 -v 0.538242 0.221473 0.956016 -v 0.519205 0.206978 0.982414 -v 0.544498 0.209886 0.963344 -v 0.568061 0.207386 0.944337 -v 0.523057 0.187087 0.990022 -v 0.549353 0.192276 0.971122 -v 0.574435 0.192486 0.951574 -v 0.596343 0.187699 0.932906 -v 0.525690 0.163078 0.996842 -v 0.552304 0.169582 0.978759 -v 0.578391 0.172062 0.959386 -v 0.601802 0.169995 0.940183 -v 0.620985 0.163873 0.922573 -v 0.527087 0.137170 1.002508 -v 0.553271 0.143878 0.985694 -v 0.579619 0.147712 0.967161 -v 0.604048 0.147916 0.948122 -v 0.624742 0.144474 0.929993 -v 0.640774 0.138119 0.913904 -v 0.577673 0.207746 0.921557 -v 0.605954 0.188059 0.910127 -v 0.630359 0.164225 0.900358 -v 0.649721 0.138454 0.892702 -v 0.381061 0.207119 1.019476 -v 0.354999 0.187258 1.035110 -v 0.332620 0.163275 1.048640 -v 0.314976 0.137387 1.059414 -v 0.458578 0.229581 0.992291 -v 0.432249 0.221135 1.008804 -v 0.461422 0.221105 1.000977 -v 0.405034 0.206866 1.025529 -v 0.434388 0.209535 1.018182 -v 0.464862 0.206805 1.009478 -v 0.378972 0.187005 1.041163 -v 0.407046 0.191952 1.034939 -v 0.437761 0.191920 1.026699 -v 0.468715 0.186914 1.017086 -v 0.356000 0.163028 1.054543 -v 0.381581 0.169293 1.049859 -v 0.411002 0.171528 1.042750 -v 0.442194 0.169231 1.033597 -v 0.472693 0.162909 1.023236 -v 0.337290 0.137151 1.065048 -v 0.359757 0.143629 1.061963 -v 0.386677 0.147222 1.056379 -v 0.416591 0.147192 1.048354 -v 0.447277 0.143540 1.038482 -v 0.476506 0.137009 1.027698 -v 0.545421 0.089033 0.999845 -v 0.564111 0.062206 0.991261 -v 0.572481 0.093442 0.982904 -v 0.582151 0.031937 0.981438 -v 0.592216 0.064792 0.972962 -v 0.599317 0.095995 0.964300 -v 0.597996 0.000111 0.970969 -v 0.610028 0.032976 0.962309 -v 0.618841 0.065862 0.953510 -v 0.623746 0.096199 0.945261 -v 0.610470 -0.030906 0.960630 -v 0.624323 0.000307 0.951748 -v 0.635110 0.033185 0.942760 -v 0.641715 0.065206 0.934385 -v 0.643953 0.094038 0.927202 -v 0.619164 -0.059078 0.951121 -v 0.634294 -0.030708 0.942063 -v 0.646854 0.000519 0.932891 -v 0.655438 0.032548 0.924322 -v 0.659406 0.063001 0.916992 -v 0.659109 0.089982 0.911241 -v 0.673585 0.090525 0.876934 -v 0.680367 0.063786 0.867316 -v 0.276633 0.062498 1.068387 -v 0.288259 0.089296 1.068838 -v 0.324363 0.088915 1.077954 -v 0.328912 0.061947 1.081587 -v 0.346213 0.093089 1.075485 -v 0.337309 0.031533 1.082759 -v 0.353441 0.064286 1.077953 -v 0.372789 0.095398 1.070245 -v 0.349483 -0.000430 1.080990 -v 0.364268 0.032321 1.077648 -v 0.382483 0.065107 1.071223 -v 0.402704 0.095368 1.062219 -v 0.364530 -0.031568 1.076413 -v 0.378109 -0.000479 1.074370 -v 0.394983 0.032289 1.069407 -v 0.414054 0.064224 1.061692 -v 0.433734 0.092999 1.052004 -v 0.381020 -0.059838 1.069723 -v 0.393704 -0.031598 1.068586 -v 0.409312 -0.000491 1.064939 -v 0.427052 0.031441 1.058682 -v 0.445606 0.061827 1.050280 -v 0.463580 0.088772 1.040604 -v 0.679387 0.060567 0.888392 -v 0.680653 0.032181 0.893051 -v 0.686447 0.032398 0.879321 -v 0.677224 0.000945 0.899773 -v 0.685859 0.001171 0.884645 -v 0.689105 0.001390 0.871616 -v 0.668641 -0.031085 0.908343 -v 0.679849 -0.031712 0.892350 -v 0.685949 -0.031484 0.877895 -v 0.686463 -0.030417 0.866107 -v 0.655541 -0.061528 0.918123 -v 0.668467 -0.063728 0.902008 -v 0.677136 -0.064370 0.886694 -v 0.680504 -0.063277 0.873481 -v 0.639442 -0.088493 0.928272 -v 0.652964 -0.092547 0.912750 -v 0.663355 -0.094698 0.897324 -v 0.669296 -0.094476 0.883245 -v 0.670345 -0.091896 0.871560 -v 0.294060 0.059338 1.080296 -v 0.282712 0.031110 1.080393 -v 0.297161 0.030957 1.084041 -v 0.275119 0.000070 1.077793 -v 0.287474 -0.000100 1.083052 -v 0.304752 -0.000243 1.085275 -v 0.272476 -0.031737 1.072284 -v 0.282200 -0.032772 1.078974 -v 0.297413 -0.032932 1.082815 -v 0.316926 -0.032206 1.083507 -v 0.282120 -0.064548 1.071888 -v 0.294700 -0.065590 1.077158 -v 0.312142 -0.064865 1.079468 -v 0.332780 -0.062558 1.078867 -v 0.286854 -0.093119 1.062549 -v 0.296825 -0.095664 1.068747 -v 0.311642 -0.095820 1.072487 -v 0.330203 -0.093577 1.073494 -v 0.350718 -0.089414 1.072065 -v 0.506172 0.059122 1.023389 -v 0.490233 0.030761 1.032242 -v 0.522986 0.030865 1.015930 -v 0.472812 -0.000414 1.040187 -v 0.506774 -0.000323 1.024216 -v 0.539982 -0.000200 1.006734 -v 0.455072 -0.032347 1.046444 -v 0.488951 -0.033127 1.031427 -v 0.523435 -0.033017 1.014253 -v 0.555828 -0.032025 0.996264 -v 0.438331 -0.062665 1.050549 -v 0.470828 -0.065026 1.036894 -v 0.505220 -0.065803 1.020678 -v 0.538880 -0.064808 1.003003 -v 0.569345 -0.062247 0.985300 -v 0.423613 -0.089488 1.052508 -v 0.453784 -0.093702 1.040338 -v 0.486762 -0.095998 1.025505 -v 0.520348 -0.095890 1.008778 -v 0.552045 -0.093389 0.991402 -v 0.579914 -0.088989 0.974666 -v 0.612724 -0.136584 0.937696 -v 0.599553 -0.162305 0.937870 -v 0.624970 -0.142935 0.922623 -v 0.582527 -0.186088 0.938716 -v 0.610305 -0.168418 0.922522 -v 0.634651 -0.146366 0.907448 -v 0.562406 -0.205726 0.940271 -v 0.591432 -0.190861 0.923536 -v 0.618191 -0.170469 0.907484 -v 0.640592 -0.146143 0.893370 -v 0.540685 -0.219761 0.942419 -v 0.569536 -0.208209 0.925673 -v 0.597531 -0.190633 0.909081 -v 0.622343 -0.167967 0.893996 -v 0.642351 -0.142284 0.881433 -v 0.519084 -0.227978 0.944932 -v 0.546479 -0.219544 0.928689 -v 0.575934 -0.136891 0.966368 -v 0.547875 -0.143578 0.982708 -v 0.561006 -0.162626 0.967912 -v 0.516072 -0.147354 0.999864 -v 0.530216 -0.169086 0.984940 -v 0.543001 -0.186417 0.969521 -v 0.482487 -0.147461 1.016590 -v 0.496440 -0.171484 1.002372 -v 0.510265 -0.191538 0.986794 -v 0.522880 -0.206055 0.971076 -v 0.449614 -0.143892 1.031645 -v 0.462165 -0.169303 1.018831 -v 0.475781 -0.191648 1.003968 -v 0.489447 -0.208877 0.988091 -v 0.502138 -0.220082 0.972461 -v 0.419633 -0.137389 1.044211 -v 0.429991 -0.163044 1.033161 -v 0.442245 -0.186738 1.019700 -v 0.455709 -0.206269 1.004529 -v 0.469384 -0.220186 0.988774 -v 0.482295 -0.228285 0.973605 -v 0.311858 -0.143339 1.046028 -v 0.334071 -0.168887 1.037563 -v 0.322464 -0.147158 1.051806 -v 0.361175 -0.191387 1.026793 -v 0.347350 -0.171333 1.042371 -v 0.337281 -0.147314 1.055547 -v 0.407732 -0.219986 0.997789 -v 0.391374 -0.208778 1.014402 -v 0.376388 -0.191547 1.030634 -v 0.364092 -0.169203 1.045143 -v 0.355207 -0.143796 1.056973 -v 0.437244 -0.228239 0.985691 -v 0.422181 -0.220139 1.001438 -v 0.407308 -0.206220 1.017514 -v 0.393843 -0.186689 1.032686 -v 0.382788 -0.162996 1.045825 -v 0.374582 -0.137343 1.056298 -v 0.455052 -0.231418 0.972123 -v 0.381557 0.207425 1.018061 -v 0.377746 0.205030 1.019909 -v 0.540959 -0.220955 0.929555 -v 0.521341 -0.226757 0.939204 -v 0.687117 0.001587 0.861486 -v 0.684480 -0.029632 0.862136 -v 0.680165 -0.055001 0.863746 -v 0.497542 -0.231282 0.950962 -v 0.678898 -0.060927 0.864251 -v 0.547182 -0.218600 0.926506 -v 0.356058 0.189217 1.030375 -v 0.500296 0.233256 0.959467 -v 0.343462 0.178103 1.036413 -v 0.676740 -0.068033 0.865175 -v 0.563502 -0.210813 0.918543 -v 0.509101 0.232065 0.955056 -v 0.524042 0.228883 0.947546 -v 0.669198 -0.090333 0.868457 -v 0.332871 0.167518 1.041463 -v 0.543592 0.223205 0.937688 -v 0.549787 0.220890 0.934553 -v 0.661987 -0.107104 0.871692 -v 0.657218 -0.116844 0.873861 -v 0.411756 -0.221367 0.993902 -v 0.405561 -0.219052 0.997037 -v 0.314768 0.145851 1.050020 -v 0.268231 0.000251 1.070104 -v 0.446247 -0.230226 0.976534 -v 0.431306 -0.227044 0.984044 -v 0.268231 0.000251 1.070104 -v 0.268231 0.000251 1.070104 -v 0.270499 -0.030952 1.068311 -v 0.313132 0.143664 1.050788 -v 0.566016 0.213207 0.926306 -v 0.311433 -0.142880 1.045541 -v 0.313043 -0.145057 1.044693 -v 0.309355 0.137631 1.052541 -v 0.298130 0.118682 1.057729 -v 0.576244 0.208046 0.921102 -v 0.307728 -0.136872 1.047515 -v 0.274512 -0.056295 1.065773 -v 0.580026 0.205675 0.919167 -v 0.601528 0.190000 0.908124 -v 0.275709 -0.062213 1.065051 -v 0.296727 -0.117994 1.053396 -v 0.277783 -0.069305 1.063867 -v 0.285061 -0.091558 1.059769 -v 0.341356 -0.177127 1.029909 -v 0.330890 -0.166610 1.035346 -v 0.687117 0.001587 0.861486 -v 0.613993 0.178966 0.901681 -v 0.684850 0.032791 0.863279 -v 0.624458 0.168448 0.896244 -v 0.292073 -0.108284 1.055920 -v 0.687117 0.001587 0.861486 -v 0.642305 0.146895 0.886896 -v 0.645993 -0.135793 0.879049 -v 0.643915 0.144719 0.886048 -v 0.375322 -0.203837 1.012423 -v 0.353820 -0.188162 1.023466 -v 0.677565 0.071144 0.867723 -v 0.670287 0.093396 0.871821 -v 0.573792 -0.205586 0.913528 -v 0.577603 -0.203191 0.911681 -v 0.663275 0.110122 0.875670 -v 0.647620 0.138710 0.884075 -v 0.599291 -0.187378 0.901215 -v 0.389332 -0.211368 1.005284 -v 0.658621 0.119832 0.878194 -v 0.611887 -0.176264 0.895177 -v 0.622478 -0.165679 0.890127 -v 0.379104 -0.206207 1.010488 -v 0.457806 0.233120 0.980628 -v 0.680836 0.058133 0.865817 -v 0.640581 -0.144012 0.881570 -v 0.448986 0.231873 0.984995 -v 0.642216 -0.141825 0.880802 -v 0.679639 0.064051 0.866539 -v 0.434007 0.228596 0.992386 -v 0.414389 0.222793 1.002034 -v 0.506362 -0.230035 0.946595 -v 0.408167 0.220439 1.005084 -v 0.391846 0.212651 1.013047 -v 0.293361 0.108942 1.059897 -v 0.276450 0.062765 1.067339 -v 0.275183 0.056840 1.067844 -v 0.278608 0.069871 1.066415 -v 0.286150 0.092171 1.063133 -v 0.270869 0.031470 1.069454 -v 0.571151 0.062815 0.645609 -v 0.569954 0.068733 0.646331 -v 0.158546 0.004933 0.849896 -v 0.160814 -0.026270 0.848103 -v 0.175376 -0.086876 0.839561 -v 0.182388 -0.103602 0.835712 -v 0.464107 -0.200904 0.693320 -v 0.467918 -0.198509 0.691473 -v 0.324323 0.233278 0.772178 -v 0.304705 0.227475 0.781827 -v 0.366602 -0.228337 0.741304 -v 0.345367 -0.226736 0.751915 -v 0.387857 -0.226600 0.730754 -v 0.567055 -0.063351 0.644967 -v 0.559513 -0.085651 0.648250 -v 0.158546 0.004933 0.849896 -v 0.168099 -0.064623 0.843659 -v 0.560603 0.098078 0.651613 -v 0.553590 0.114804 0.655462 -v 0.279647 -0.206686 0.785076 -v 0.269419 -0.201525 0.790281 -v 0.504308 0.183648 0.681473 -v 0.514774 0.173130 0.676036 -v 0.414357 0.233565 0.727338 -v 0.433907 0.227887 0.717480 -v 0.203448 0.148346 0.830580 -v 0.199670 0.142313 0.832333 -v 0.548936 0.124514 0.657986 -v 0.271872 0.212107 0.797854 -v 0.282162 0.217333 0.792839 -v 0.399417 0.236747 0.734848 -v 0.336562 -0.225544 0.756326 -v 0.164828 -0.051613 0.845565 -v 0.231671 -0.172445 0.809701 -v 0.221205 -0.161928 0.815138 -v 0.244136 -0.183480 0.803258 -v 0.489606 -0.182696 0.681007 -v 0.298482 0.225121 0.784876 -v 0.396677 -0.225352 0.726387 -v 0.431275 -0.216273 0.709348 -v 0.411656 -0.222075 0.718996 -v 0.437497 -0.213918 0.706298 -v 0.223186 0.172200 0.821255 -v 0.205083 0.150533 0.829812 -v 0.201749 -0.138198 0.825334 -v 0.203358 -0.140375 0.824486 -v 0.198043 -0.132189 0.827307 -v 0.502202 -0.171582 0.674969 -v 0.567880 0.075826 0.647515 -v 0.547533 -0.112162 0.653653 -v 0.536308 -0.131111 0.658841 -v 0.532621 0.151577 0.666689 -v 0.534230 0.149401 0.665841 -v 0.268061 0.209712 0.799701 -v 0.246373 0.193899 0.810167 -v 0.552302 -0.102422 0.651485 -v 0.456331 0.217889 0.706098 -v 0.466559 0.212728 0.700894 -v 0.537936 0.143392 0.663867 -v 0.577433 0.006269 0.641278 -v 0.574795 -0.024950 0.641929 -v 0.440102 0.225572 0.714345 -v 0.577433 0.006269 0.641278 -v 0.166765 0.067447 0.847131 -v 0.165498 0.061522 0.847636 -v 0.168924 0.074553 0.846207 -v 0.577433 0.006269 0.641278 -v 0.575165 0.037473 0.643071 -v 0.512793 -0.160997 0.669919 -v 0.530896 -0.139330 0.661362 -v 0.369376 0.239539 0.749870 -v 0.390611 0.237938 0.739259 -v 0.348122 0.237802 0.760420 -v 0.183676 0.113625 0.839690 -v 0.176465 0.096853 0.842925 -v 0.570480 -0.050319 0.643538 -v 0.569213 -0.056245 0.644043 -v 0.470342 0.210357 0.698959 -v 0.188446 0.123364 0.837521 -v 0.161184 0.036152 0.849246 -v 0.491843 0.194682 0.687916 -v 0.265637 -0.199155 0.792215 -v 0.166025 -0.057531 0.844843 -v 0.295876 -0.214370 0.776829 -v 0.339302 0.236555 0.764787 -v 0.532531 -0.137143 0.660594 -v 0.302071 -0.216685 0.773695 -v 0.321621 -0.222362 0.763836 -v 0.187042 -0.113312 0.833188 -v 0.233777 0.182785 0.816205 -v 0.158546 0.004933 0.849896 -v 0.453817 -0.206131 0.698335 -vt 0.193183 0.019683 -vt 0.176737 0.019683 -vt 0.181819 0.000000 -vt 0.272728 0.157461 -vt 0.284092 0.137778 -vt 0.295455 0.157461 -vt 0.534091 0.019683 -vt 0.545455 0.000000 -vt 0.550537 0.019683 -vt 0.284092 0.177143 -vt 0.454546 0.157461 -vt 0.477273 0.157461 -vt 0.465910 0.177143 -vt 0.261364 0.177143 -vt 0.443182 0.177143 -vt 0.181819 0.314921 -vt 0.204546 0.314921 -vt 0.193183 0.334604 -vt 0.545455 0.314921 -vt 0.568182 0.314921 -vt 0.556819 0.334604 -vt 0.363637 0.314921 -vt 0.386364 0.314921 -vt 0.375001 0.334604 -vt 0.465910 0.452699 -vt 0.454546 0.472382 -vt 0.443182 0.452699 -vt 0.431819 0.433017 -vt 0.454546 0.433017 -vt 0.420455 0.413334 -vt 0.443182 0.413334 -vt 0.409092 0.393652 -vt 0.431819 0.393652 -vt 0.397728 0.373969 -vt 0.420455 0.373969 -vt 0.386364 0.354286 -vt 0.409091 0.354286 -vt 0.397728 0.334604 -vt 0.477273 0.433017 -vt 0.465910 0.413334 -vt 0.488637 0.413334 -vt 0.454546 0.393652 -vt 0.477273 0.393652 -vt 0.500000 0.393652 -vt 0.443182 0.373969 -vt 0.465910 0.373969 -vt 0.488637 0.373969 -vt 0.511364 0.373969 -vt 0.431819 0.354286 -vt 0.454546 0.354286 -vt 0.477273 0.354286 -vt 0.500000 0.354286 -vt 0.522728 0.354286 -vt 0.420455 0.334604 -vt 0.443182 0.334604 -vt 0.465910 0.334604 -vt 0.488637 0.334604 -vt 0.511364 0.334604 -vt 0.534091 0.334604 -vt 0.409091 0.314921 -vt 0.431819 0.314921 -vt 0.454546 0.314921 -vt 0.477273 0.314921 -vt 0.500000 0.314921 -vt 0.522728 0.314921 -vt 0.625000 0.452699 -vt 0.631282 0.452699 -vt 0.636364 0.472382 -vt 0.613637 0.433017 -vt 0.626138 0.433017 -vt 0.629174 0.445471 -vt 0.602273 0.413334 -vt 0.621040 0.413334 -vt 0.622198 0.418189 -vt 0.590910 0.393652 -vt 0.613637 0.393652 -vt 0.579546 0.373969 -vt 0.602273 0.373969 -vt 0.568182 0.354286 -vt 0.590909 0.354286 -vt 0.579546 0.334604 -vt 0.618044 0.401285 -vt 0.616008 0.393652 -vt 0.610813 0.373969 -vt 0.615257 0.390844 -vt 0.605676 0.354286 -vt 0.608234 0.363644 -vt 0.600724 0.334604 -vt 0.601248 0.336379 -vt 0.590909 0.314921 -vt 0.599469 0.329747 -vt 0.595581 0.314921 -vt 0.102274 0.452699 -vt 0.090910 0.472382 -vt 0.095992 0.452699 -vt 0.098100 0.445471 -vt 0.113637 0.433017 -vt 0.101136 0.433017 -vt 0.105076 0.418189 -vt 0.125001 0.413334 -vt 0.106234 0.413334 -vt 0.109230 0.401285 -vt 0.111266 0.393652 -vt 0.113637 0.393652 -vt 0.136365 0.393652 -vt 0.112016 0.390844 -vt 0.125001 0.373969 -vt 0.116461 0.373969 -vt 0.147728 0.373969 -vt 0.119039 0.363644 -vt 0.136364 0.354286 -vt 0.121598 0.354286 -vt 0.159092 0.354286 -vt 0.126026 0.336379 -vt 0.147728 0.334604 -vt 0.126550 0.334604 -vt 0.170455 0.334604 -vt 0.127804 0.329747 -vt 0.131693 0.314921 -vt 0.136364 0.314921 -vt 0.159092 0.314921 -vt 0.272728 0.472382 -vt 0.261364 0.452699 -vt 0.284092 0.452699 -vt 0.250001 0.433017 -vt 0.272728 0.433017 -vt 0.238637 0.413334 -vt 0.261364 0.413334 -vt 0.227273 0.393652 -vt 0.250001 0.393652 -vt 0.215910 0.373969 -vt 0.238637 0.373969 -vt 0.204546 0.354286 -vt 0.227273 0.354286 -vt 0.215910 0.334604 -vt 0.295455 0.433017 -vt 0.284092 0.413334 -vt 0.306819 0.413334 -vt 0.272728 0.393652 -vt 0.295455 0.393652 -vt 0.318182 0.393652 -vt 0.261364 0.373969 -vt 0.284092 0.373969 -vt 0.306819 0.373969 -vt 0.329546 0.373969 -vt 0.250001 0.354286 -vt 0.272728 0.354286 -vt 0.295455 0.354286 -vt 0.318182 0.354286 -vt 0.340910 0.354286 -vt 0.238637 0.334604 -vt 0.261364 0.334604 -vt 0.284092 0.334604 -vt 0.306819 0.334604 -vt 0.329546 0.334604 -vt 0.352273 0.334604 -vt 0.227274 0.314921 -vt 0.250001 0.314921 -vt 0.272728 0.314921 -vt 0.295455 0.314921 -vt 0.318183 0.314921 -vt 0.340910 0.314921 -vt 0.375001 0.295238 -vt 0.386364 0.275556 -vt 0.397728 0.295238 -vt 0.397728 0.255874 -vt 0.409092 0.275556 -vt 0.409092 0.236191 -vt 0.420455 0.255874 -vt 0.420455 0.216509 -vt 0.431819 0.236191 -vt 0.431819 0.196826 -vt 0.443182 0.216509 -vt 0.454546 0.196826 -vt 0.420455 0.295238 -vt 0.431819 0.275556 -vt 0.443182 0.295238 -vt 0.443182 0.255874 -vt 0.454546 0.275556 -vt 0.465910 0.295238 -vt 0.454546 0.236191 -vt 0.465910 0.255874 -vt 0.477273 0.275556 -vt 0.488637 0.295238 -vt 0.465910 0.216509 -vt 0.477273 0.236191 -vt 0.488637 0.255874 -vt 0.500000 0.275556 -vt 0.511364 0.295238 -vt 0.477273 0.196826 -vt 0.488637 0.216508 -vt 0.500000 0.236191 -vt 0.511364 0.255874 -vt 0.522728 0.275556 -vt 0.534091 0.295238 -vt 0.556819 0.295238 -vt 0.568182 0.275556 -vt 0.579546 0.295238 -vt 0.579546 0.255874 -vt 0.590910 0.275556 -vt 0.590910 0.236191 -vt 0.590910 0.236191 -vt 0.591833 0.255874 -vt 0.590910 0.236191 -vt 0.590910 0.236191 -vt 0.593592 0.280202 -vt 0.594268 0.295238 -vt 0.595096 0.307670 -vt 0.593036 0.271873 -vt 0.593228 0.275556 -vt 0.132178 0.307670 -vt 0.134046 0.275556 -vt 0.134238 0.271873 -vt 0.136364 0.275556 -vt 0.133682 0.280202 -vt 0.147728 0.295238 -vt 0.133006 0.295238 -vt 0.135441 0.255874 -vt 0.136365 0.236191 -vt 0.147728 0.255874 -vt 0.159092 0.275556 -vt 0.170455 0.295238 -vt 0.193183 0.295238 -vt 0.204546 0.275556 -vt 0.215910 0.295238 -vt 0.215910 0.255874 -vt 0.227274 0.275556 -vt 0.227273 0.236191 -vt 0.238637 0.255874 -vt 0.238637 0.216509 -vt 0.250001 0.236191 -vt 0.250001 0.196826 -vt 0.261364 0.216509 -vt 0.272728 0.196826 -vt 0.238637 0.295238 -vt 0.250001 0.275556 -vt 0.261364 0.295238 -vt 0.261364 0.255874 -vt 0.272728 0.275556 -vt 0.284092 0.295238 -vt 0.272728 0.236191 -vt 0.284092 0.255874 -vt 0.295455 0.275556 -vt 0.306819 0.295238 -vt 0.284092 0.216509 -vt 0.295455 0.236191 -vt 0.306819 0.255874 -vt 0.318182 0.275556 -vt 0.329546 0.295238 -vt 0.295455 0.196826 -vt 0.306819 0.216508 -vt 0.318182 0.236191 -vt 0.329546 0.255874 -vt 0.340910 0.275556 -vt 0.352273 0.295238 -vt 0.545455 0.275556 -vt 0.534091 0.255874 -vt 0.522728 0.236191 -vt 0.511364 0.216508 -vt 0.500000 0.196826 -vt 0.488637 0.177143 -vt 0.556819 0.255874 -vt 0.545455 0.236191 -vt 0.568182 0.236191 -vt 0.534091 0.216508 -vt 0.556819 0.216508 -vt 0.579546 0.216508 -vt 0.589986 0.216509 -vt 0.522728 0.196826 -vt 0.545455 0.196826 -vt 0.568182 0.196826 -vt 0.588591 0.196826 -vt 0.588783 0.200509 -vt 0.511364 0.177143 -vt 0.534091 0.177143 -vt 0.556819 0.177143 -vt 0.579546 0.177143 -vt 0.588227 0.192180 -vt 0.587551 0.177143 -vt 0.500000 0.157461 -vt 0.522728 0.157461 -vt 0.545455 0.157461 -vt 0.568182 0.157461 -vt 0.586238 0.157461 -vt 0.586723 0.164712 -vt 0.181819 0.275556 -vt 0.170455 0.255874 -vt 0.159092 0.236191 -vt 0.136365 0.236191 -vt 0.136365 0.236191 -vt 0.137288 0.216508 -vt 0.147728 0.216508 -vt 0.193183 0.255874 -vt 0.181819 0.236191 -vt 0.204546 0.236191 -vt 0.170455 0.216508 -vt 0.193183 0.216508 -vt 0.215910 0.216508 -vt 0.138491 0.200509 -vt 0.159092 0.196826 -vt 0.138683 0.196826 -vt 0.181819 0.196826 -vt 0.204546 0.196826 -vt 0.227273 0.196826 -vt 0.139047 0.192180 -vt 0.139723 0.177143 -vt 0.147728 0.177143 -vt 0.170455 0.177143 -vt 0.193183 0.177143 -vt 0.215910 0.177143 -vt 0.238637 0.177143 -vt 0.140551 0.164712 -vt 0.159092 0.157461 -vt 0.141036 0.157461 -vt 0.181819 0.157461 -vt 0.204546 0.157461 -vt 0.227273 0.157461 -vt 0.250001 0.157461 -vt 0.363637 0.275556 -vt 0.352273 0.255874 -vt 0.340910 0.236191 -vt 0.329546 0.216508 -vt 0.318182 0.196826 -vt 0.306819 0.177143 -vt 0.375001 0.255874 -vt 0.363637 0.236191 -vt 0.386364 0.236191 -vt 0.352273 0.216508 -vt 0.375001 0.216508 -vt 0.397728 0.216508 -vt 0.340910 0.196826 -vt 0.363637 0.196826 -vt 0.386364 0.196826 -vt 0.409092 0.196826 -vt 0.329546 0.177143 -vt 0.352273 0.177143 -vt 0.375001 0.177143 -vt 0.397728 0.177143 -vt 0.420455 0.177143 -vt 0.318182 0.157461 -vt 0.340910 0.157461 -vt 0.363637 0.157461 -vt 0.386364 0.157461 -vt 0.409092 0.157461 -vt 0.431819 0.157461 -vt 0.465910 0.137778 -vt 0.477273 0.118096 -vt 0.488637 0.137778 -vt 0.488637 0.098413 -vt 0.500000 0.118096 -vt 0.500000 0.078731 -vt 0.511364 0.098413 -vt 0.511364 0.059048 -vt 0.522728 0.078731 -vt 0.522728 0.039365 -vt 0.534091 0.059048 -vt 0.545455 0.039365 -vt 0.511364 0.137778 -vt 0.522728 0.118096 -vt 0.534091 0.137778 -vt 0.534091 0.098413 -vt 0.545455 0.118096 -vt 0.556819 0.137778 -vt 0.545455 0.078731 -vt 0.556819 0.098413 -vt 0.568182 0.118096 -vt 0.579546 0.137778 -vt 0.582350 0.142635 -vt 0.556819 0.059048 -vt 0.563775 0.071096 -vt 0.565811 0.078731 -vt 0.566562 0.081538 -vt 0.571006 0.098413 -vt 0.573585 0.108738 -vt 0.576143 0.118096 -vt 0.580571 0.136003 -vt 0.581095 0.137778 -vt 0.552645 0.026911 -vt 0.555681 0.039365 -vt 0.559621 0.054193 -vt 0.560779 0.059048 -vt 0.443182 0.137778 -vt 0.420455 0.137778 -vt 0.397728 0.137778 -vt 0.375001 0.137778 -vt 0.352273 0.137778 -vt 0.329546 0.137778 -vt 0.306819 0.137778 -vt 0.431819 0.118096 -vt 0.409091 0.118096 -vt 0.420455 0.098413 -vt 0.386364 0.118096 -vt 0.397728 0.098413 -vt 0.409092 0.078731 -vt 0.363637 0.118096 -vt 0.375001 0.098413 -vt 0.386364 0.078731 -vt 0.397728 0.059048 -vt 0.340910 0.118096 -vt 0.352273 0.098413 -vt 0.363637 0.078731 -vt 0.375001 0.059048 -vt 0.386364 0.039365 -vt 0.318182 0.118096 -vt 0.329546 0.098413 -vt 0.340910 0.078731 -vt 0.352273 0.059048 -vt 0.363637 0.039365 -vt 0.375001 0.019683 -vt 0.295455 0.118096 -vt 0.306819 0.098413 -vt 0.318182 0.078731 -vt 0.329546 0.059048 -vt 0.340910 0.039365 -vt 0.352273 0.019683 -vt 0.363637 0.000000 -vt 0.167653 0.054193 -vt 0.170455 0.059048 -vt 0.166495 0.059048 -vt 0.174629 0.026911 -vt 0.181819 0.039365 -vt 0.171593 0.039365 -vt 0.146178 0.137778 -vt 0.146703 0.136003 -vt 0.147728 0.137778 -vt 0.144924 0.142635 -vt 0.153689 0.108738 -vt 0.159092 0.118096 -vt 0.151131 0.118096 -vt 0.170455 0.137778 -vt 0.160712 0.081537 -vt 0.170455 0.098413 -vt 0.156267 0.098413 -vt 0.181819 0.118096 -vt 0.193183 0.137778 -vt 0.163499 0.071097 -vt 0.181819 0.078731 -vt 0.161463 0.078731 -vt 0.193183 0.098413 -vt 0.204546 0.118096 -vt 0.215910 0.137778 -vt 0.193183 0.059048 -vt 0.204546 0.078731 -vt 0.215910 0.098413 -vt 0.227273 0.118096 -vt 0.238637 0.137778 -vt 0.204546 0.039365 -vt 0.215910 0.059048 -vt 0.227274 0.078731 -vt 0.238637 0.098413 -vt 0.250001 0.118096 -vt 0.261364 0.137778 -vt 0.610813 0.373969 -vt 0.615257 0.390844 -vt 0.500000 0.750000 -vt 0.500000 0.750000 -vt 0.500000 0.750000 -vt 0.516677 0.748536 -vt 0.530226 0.746051 -vt 0.533390 0.745316 -vt 0.537183 0.744053 -vt 0.549086 0.739630 -vt 0.558035 0.735386 -vt 0.563231 0.732574 -vt 0.573337 0.725942 -vt 0.576554 0.723709 -vt 0.577720 0.722741 -vt 0.589267 0.712013 -vt 0.594905 0.705729 -vt 0.600822 0.698251 -vt 0.609235 0.685363 -vt 0.610508 0.683097 -vt 0.613283 0.676974 -vt 0.617416 0.667261 -vt 0.618664 0.663555 -vt 0.621731 0.651867 -vt 0.623457 0.642939 -vt 0.624108 0.637679 -vt 0.625000 0.625000 -vt 0.624108 0.612321 -vt 0.623457 0.607061 -vt 0.621731 0.598133 -vt 0.618664 0.586444 -vt 0.617416 0.582739 -vt 0.613283 0.573026 -vt 0.610508 0.566903 -vt 0.609235 0.564637 -vt 0.600822 0.551749 -vt 0.594905 0.544271 -vt 0.589267 0.537987 -vt 0.577720 0.527259 -vt 0.576554 0.526291 -vt 0.573337 0.524058 -vt 0.563231 0.517426 -vt 0.558035 0.514614 -vt 0.549086 0.510370 -vt 0.537183 0.505947 -vt 0.533390 0.504684 -vt 0.530226 0.503949 -vt 0.516677 0.501464 -vt 0.500000 0.500000 -vt 0.500000 0.500000 -vt 0.500000 0.500000 -vt 0.483323 0.501464 -vt 0.469774 0.503949 -vt 0.466610 0.504684 -vt 0.462817 0.505947 -vt 0.450914 0.510370 -vt 0.441965 0.514614 -vt 0.436769 0.517426 -vt 0.426663 0.524058 -vt 0.423446 0.526291 -vt 0.422280 0.527259 -vt 0.410733 0.537987 -vt 0.405095 0.544271 -vt 0.399178 0.551749 -vt 0.390765 0.564637 -vt 0.389492 0.566903 -vt 0.386717 0.573026 -vt 0.382584 0.582739 -vt 0.381336 0.586444 -vt 0.378269 0.598133 -vt 0.376543 0.607061 -vt 0.375892 0.612321 -vt 0.375000 0.625000 -vt 0.375892 0.637679 -vt 0.376543 0.642939 -vt 0.378269 0.651867 -vt 0.381336 0.663556 -vt 0.382584 0.667261 -vt 0.386717 0.676974 -vt 0.389492 0.683097 -vt 0.390765 0.685363 -vt 0.399178 0.698251 -vt 0.405095 0.705729 -vt 0.410733 0.712013 -vt 0.422280 0.722741 -vt 0.423446 0.723709 -vt 0.426663 0.725942 -vt 0.436769 0.732574 -vt 0.441965 0.735386 -vt 0.450914 0.739630 -vt 0.462817 0.744053 -vt 0.466610 0.745315 -vt 0.469774 0.746051 -vt 0.483323 0.748536 -vt 0.134238 0.271873 -vt 0.135441 0.255874 -vt 0.550537 0.019683 -vt 0.552645 0.026911 -vt 0.161463 0.078731 -vt 0.163499 0.071097 -vt 0.600724 0.334604 -vt 0.601248 0.336379 -vt 0.566562 0.081538 -vt 0.571006 0.098413 -vt 0.588227 0.192180 -vt 0.588591 0.196826 -vt 0.573585 0.108738 -vt 0.098100 0.445471 -vt 0.101136 0.433017 -vt 0.136365 0.236191 -vt 0.137288 0.216508 -vt 0.116461 0.373969 -vt 0.119039 0.363644 -vt 0.555681 0.039365 -vt 0.559621 0.054193 -vt 0.605676 0.354286 -vt 0.608234 0.363644 -vt 0.594268 0.295238 -vt 0.595096 0.307670 -vt 0.112016 0.390844 -vt 0.588783 0.200509 -vt 0.589986 0.216508 -vt 0.090910 0.472382 -vt 0.095992 0.452699 -vt 0.599469 0.329747 -vt 0.591833 0.255874 -vt 0.593036 0.271873 -vt 0.586723 0.164712 -vt 0.587551 0.177143 -vt 0.136365 0.236191 -vt 0.136365 0.236191 -vt 0.156267 0.098413 -vt 0.160712 0.081537 -vt 0.622198 0.418189 -vt 0.626138 0.433017 -vt 0.560779 0.059048 -vt 0.127804 0.329747 -vt 0.131693 0.314921 -vt 0.586238 0.157461 -vt 0.105076 0.418189 -vt 0.106234 0.413334 -vt 0.121598 0.354286 -vt 0.133006 0.295238 -vt 0.133682 0.280202 -vt 0.126550 0.334604 -vt 0.629174 0.445471 -vt 0.111266 0.393652 -vt 0.631282 0.452699 -vt 0.636364 0.472382 -vt 0.140551 0.164712 -vt 0.141036 0.157461 -vt 0.138491 0.200509 -vt 0.138683 0.196826 -vt 0.132178 0.307670 -vt 0.616008 0.393652 -vt 0.618044 0.401285 -vt 0.126026 0.336379 -vt 0.621040 0.413334 -vt 0.134046 0.275556 -vt 0.590910 0.236191 -vt 0.139723 0.177143 -vt 0.593592 0.280202 -vt 0.174629 0.026911 -vt 0.176737 0.019683 -vt 0.595581 0.314921 -vt 0.146178 0.137778 -vt 0.146703 0.136003 -vt 0.565811 0.078731 -vt 0.109230 0.401285 -vt 0.590910 0.236191 -vt 0.144924 0.142635 -vt 0.166495 0.059048 -vt 0.576143 0.118096 -vt 0.580571 0.136003 -vt 0.582350 0.142635 -vt 0.593228 0.275556 -vt 0.171593 0.039365 -vt 0.167653 0.054193 -vt 0.181819 0.000000 -vt 0.151131 0.118096 -vt 0.153689 0.108738 -vt 0.581095 0.137778 -vt 0.545455 0.000000 -vt 0.590910 0.236191 -vt 0.139047 0.192180 -vt 0.563775 0.071096 -vn 0.0739 -0.9513 0.2992 -vn -0.0699 -0.9939 0.0854 -vn 0.0423 -0.9960 0.0786 -vn 0.2530 -0.1868 0.9492 -vn 0.2498 -0.2395 0.9382 -vn 0.3005 -0.1885 0.9349 -vn 0.1992 -0.9509 0.2369 -vn 0.1153 -0.9933 -0.0069 -vn 0.2838 -0.1416 0.9484 -vn 0.6061 -0.1857 0.7734 -vn 0.6481 -0.2176 0.7297 -vn 0.6351 -0.1533 0.7570 -vn 0.2223 -0.1546 0.9626 -vn 0.5866 -0.1407 0.7975 -vn -0.2412 0.3102 0.9195 -vn -0.0698 0.2788 0.9578 -vn -0.2341 0.4023 0.8850 -vn 0.8776 0.3138 0.3623 -vn 0.9042 0.4162 0.0959 -vn 0.8454 0.4057 0.3474 -vn 0.4422 0.1200 0.8888 -vn 0.4742 0.1333 0.8702 -vn 0.4393 0.1646 0.8831 -vn 0.1597 0.9846 0.0711 -vn 0.0490 0.9933 0.1048 -vn 0.1796 0.9129 0.3664 -vn 0.3038 0.7279 0.6147 -vn 0.3284 0.8453 0.4214 -vn 0.3728 0.5433 0.7522 -vn 0.4183 0.6439 0.6406 -vn 0.4074 0.4006 0.8207 -vn 0.4566 0.4697 0.7555 -vn 0.4249 0.2969 0.8551 -vn 0.4707 0.3422 0.8132 -vn 0.4342 0.2213 0.8732 -vn 0.4749 0.2510 0.8434 -vn 0.4753 0.1845 0.8603 -vn 0.3242 0.9364 0.1340 -vn 0.4617 0.7554 0.4649 -vn 0.4824 0.8540 0.1947 -vn 0.5132 0.5553 0.6543 -vn 0.5735 0.6529 0.4948 -vn 0.6183 0.7457 0.2484 -vn 0.5250 0.3987 0.7519 -vn 0.5899 0.4682 0.6578 -vn 0.6608 0.5482 0.5126 -vn 0.7228 0.6263 0.2920 -vn 0.5230 0.2874 0.8024 -vn 0.5812 0.3323 0.7428 -vn 0.6500 0.3869 0.6541 -vn 0.7255 0.4494 0.5212 -vn 0.7964 0.5101 0.3246 -vn 0.5171 0.2081 0.8302 -vn 0.5672 0.2369 0.7888 -vn 0.6269 0.2717 0.7302 -vn 0.6962 0.3133 0.6457 -vn 0.7720 0.3607 0.5234 -vn 0.5108 0.1487 0.8467 -vn 0.5538 0.1671 0.8157 -vn 0.6045 0.1892 0.7737 -vn 0.6639 0.2156 0.7160 -vn 0.7318 0.2467 0.6353 -vn 0.8050 0.2816 0.5220 -vn 0.0645 0.9958 -0.0643 -vn 0.2057 0.9768 -0.0590 -vn 0.1659 0.9853 -0.0410 -vn 0.3556 0.9331 -0.0531 -vn 0.3140 0.9489 -0.0327 -vn 0.4463 0.8816 -0.1537 -vn 0.6137 0.7846 -0.0880 -vn 0.7479 0.6634 -0.0199 -vn 0.8427 0.5365 0.0439 -vn 0.3863 0.9160 -0.1079 -vn 0.3550 0.8956 -0.2680 -vn 0.5086 0.8197 -0.2634 -vn 0.4689 0.8494 -0.2420 -vn 0.6464 0.7202 -0.2518 -vn 0.6117 0.7561 -0.2327 -vn 0.7583 0.6075 -0.2365 -vn 0.7299 0.6469 -0.2208 -vn 0.8134 0.5014 -0.2946 -vn 0.7795 0.5627 -0.2750 -vn 0.7343 0.5212 -0.4348 -vn -0.0445 0.9839 0.1728 -vn -0.0953 0.9953 0.0153 -vn -0.1378 0.9843 0.1102 -vn -0.0931 0.9351 0.3419 -vn -0.1760 0.9756 0.1311 -vn -0.2201 0.9471 0.2333 -vn -0.1396 0.8520 0.5045 -vn -0.2614 0.9311 0.2542 -vn -0.3236 0.9138 0.2456 -vn -0.4324 0.8931 0.1241 -vn -0.3961 0.8788 0.2658 -vn -0.1781 0.7431 0.6450 -vn -0.4800 0.8464 0.2305 -vn -0.4440 0.7812 0.4387 -vn -0.5208 0.8164 0.2493 -vn -0.2057 0.6233 0.7544 -vn -0.5581 0.7524 0.3499 -vn -0.4699 0.6596 0.5866 -vn -0.5941 0.7162 0.3660 -vn -0.2234 0.5069 0.8325 -vn -0.6193 0.6426 0.4511 -vn -0.4755 0.5323 0.7004 -vn -0.6487 0.6030 0.4642 -vn -0.6920 0.5580 0.4579 -vn -0.7921 0.5164 0.3254 -vn -0.7278 0.4965 0.4730 -vn -0.4704 0.4118 0.7804 -vn 0.1342 0.8447 0.5181 -vn 0.0890 0.7542 0.6506 -vn 0.0460 0.6512 0.7575 -vn 0.0082 0.5461 0.8376 -vn -0.0234 0.4470 0.8942 -vn -0.0493 0.3580 0.9324 -vn 0.2559 0.6434 0.7214 -vn 0.2102 0.5544 0.8053 -vn 0.3255 0.4693 0.8209 -vn 0.1671 0.4669 0.8683 -vn 0.2817 0.3979 0.8731 -vn 0.3637 0.3418 0.8665 -vn 0.1284 0.3852 0.9138 -vn 0.2409 0.3312 0.9123 -vn 0.3237 0.2868 0.9016 -vn 0.3857 0.2507 0.8879 -vn 0.0942 0.3114 0.9456 -vn 0.2037 0.2704 0.9409 -vn 0.2865 0.2360 0.9285 -vn 0.3499 0.2076 0.9135 -vn 0.3992 0.1842 0.8981 -vn 0.0648 0.2445 0.9674 -vn 0.1703 0.2140 0.9619 -vn 0.2523 0.1881 0.9492 -vn 0.3164 0.1663 0.9339 -vn 0.3673 0.1482 0.9182 -vn 0.4081 0.1331 0.9032 -vn 0.4630 0.0876 0.8820 -vn 0.4835 0.0541 0.8736 -vn 0.4976 0.0982 0.8618 -vn 0.5043 0.0186 0.8633 -vn 0.5208 0.0618 0.8514 -vn 0.5253 -0.0186 0.8507 -vn 0.5443 0.0231 0.8386 -vn 0.5462 -0.0578 0.8357 -vn 0.5677 -0.0177 0.8230 -vn 0.5667 -0.0985 0.8180 -vn 0.5908 -0.0606 0.8045 -vn 0.6132 -0.1055 0.7828 -vn 0.5372 0.1103 0.8361 -vn 0.5636 0.0704 0.8230 -vn 0.5840 0.1248 0.8021 -vn 0.5901 0.0279 0.8068 -vn 0.6141 0.0808 0.7851 -vn 0.6391 0.1422 0.7559 -vn 0.6163 -0.0172 0.7873 -vn 0.6440 0.0336 0.7643 -vn 0.6733 0.0933 0.7334 -vn 0.7033 0.1631 0.6919 -vn 0.6418 -0.0648 0.7641 -vn 0.6731 -0.0166 0.7393 -vn 0.7068 0.0405 0.7062 -vn 0.7418 0.1082 0.6617 -vn 0.7760 0.1876 0.6022 -vn 0.6669 -0.1157 0.7361 -vn 0.7022 -0.0708 0.7084 -vn 0.7407 -0.0168 0.6716 -vn 0.7812 0.0480 0.6224 -vn 0.8211 0.1249 0.5568 -vn 0.8556 0.2141 0.4712 -vn 0.9335 0.2751 0.2300 -vn 0.9758 0.2135 0.0467 -vn 0.9239 0.3605 -0.1280 -vn 0.9743 0.1209 -0.1899 -vn 0.8818 0.2669 -0.3887 -vn 0.7811 -0.0018 0.6244 -vn 0.8897 0.0671 -0.4516 -vn 0.8925 0.1389 -0.4290 -vn 0.8578 0.0000 0.5139 -vn 0.8897 -0.0632 -0.4521 -vn 0.8537 0.3416 -0.3931 -vn 0.8422 0.3956 -0.3661 -vn 0.8224 0.4433 -0.3564 -vn 0.8825 0.1958 -0.4274 -vn 0.8091 0.2788 -0.5173 -vn -0.7823 0.4382 0.4427 -vn -0.9018 0.2733 0.3348 -vn -0.8739 0.1902 0.4473 -vn -0.8429 0.2614 0.4703 -vn -0.8298 0.3363 0.4453 -vn -0.6607 0.3554 0.6611 -vn -0.8017 0.3904 0.4526 -vn -0.8809 0.1333 0.4541 -vn -0.8462 0.0397 0.5314 -vn -0.7392 0.1154 0.6634 -vn -0.5518 0.2086 0.8075 -vn -0.3802 0.2709 0.8843 -vn -0.1405 0.2110 0.9673 -vn -0.0509 0.1221 0.9912 -vn 0.0121 0.1852 0.9826 -vn 0.0257 0.0456 0.9986 -vn 0.0806 0.1061 0.9911 -vn 0.0898 -0.0189 0.9958 -vn 0.1375 0.0387 0.9897 -vn 0.1426 -0.0725 0.9871 -vn 0.1845 -0.0182 0.9826 -vn 0.1862 -0.1173 0.9755 -vn 0.2234 -0.0661 0.9724 -vn 0.2558 -0.1066 0.9608 -vn 0.1276 0.1612 0.9786 -vn 0.1792 0.0917 0.9795 -vn 0.2175 0.1409 0.9658 -vn 0.2218 0.0323 0.9745 -vn 0.2561 0.0797 0.9633 -vn 0.2876 0.1238 0.9497 -vn 0.2571 -0.0184 0.9662 -vn 0.2882 0.0269 0.9572 -vn 0.3168 0.0696 0.9459 -vn 0.3430 0.1097 0.9329 -vn 0.2864 -0.0616 0.9561 -vn 0.3149 -0.0185 0.9489 -vn 0.3412 0.0225 0.9397 -vn 0.3653 0.0613 0.9288 -vn 0.3875 0.0978 0.9167 -vn 0.3119 -0.0993 0.9449 -vn 0.3381 -0.0584 0.9393 -vn 0.3625 -0.0192 0.9318 -vn 0.3850 0.0183 0.9227 -vn 0.4056 0.0538 0.9124 -vn 0.4245 0.0874 0.9012 -vn 0.9142 0.1653 0.3699 -vn 0.8745 0.0665 0.4804 -vn 0.8256 -0.0165 0.5640 -vn 0.7756 -0.0838 0.6256 -vn 0.7286 -0.1378 0.6709 -vn 0.6862 -0.1812 0.7045 -vn 0.9699 0.0935 0.2247 -vn 0.9290 -0.0139 0.3699 -vn 0.9997 -0.0084 0.0226 -vn 0.8711 -0.1013 0.4805 -vn 0.9702 -0.1232 0.2088 -vn 0.9649 -0.1356 -0.2250 -vn 0.8818 -0.1287 -0.4537 -vn 0.8103 -0.1688 0.5612 -vn 0.9095 -0.2124 0.3573 -vn 0.9662 -0.2577 -0.0072 -vn 0.9046 -0.2650 -0.3339 -vn 0.9162 -0.2114 -0.3402 -vn 0.7535 -0.2203 0.6195 -vn 0.8400 -0.2766 0.4668 -vn 0.9193 -0.3481 0.1835 -vn 0.8848 -0.3924 -0.2513 -vn 0.8836 -0.3224 -0.3396 -vn 0.8084 -0.3796 -0.4498 -vn 0.7025 -0.2615 0.6619 -vn 0.7732 -0.3244 0.5449 -vn 0.8499 -0.4101 0.3306 -vn 0.8695 -0.4926 -0.0349 -vn 0.8018 -0.5041 -0.3211 -vn 0.8237 -0.4601 -0.3312 -vn -0.2565 0.1616 0.9529 -vn -0.4055 0.0892 0.9097 -vn -0.5843 -0.0135 0.8114 -vn -0.8176 -0.0048 0.5757 -vn -0.8052 -0.0506 0.5908 -vn -0.8927 -0.1344 0.4300 -vn -0.7602 -0.1411 0.6341 -vn -0.1438 0.0632 0.9876 -vn -0.2644 -0.0178 0.9642 -vn -0.0472 -0.0193 0.9987 -vn -0.4172 -0.1276 0.8998 -vn -0.1409 -0.1045 0.9845 -vn 0.0324 -0.0862 0.9957 -vn -0.8225 -0.2170 0.5257 -vn -0.5866 -0.2626 0.7661 -vn -0.8101 -0.2705 0.5201 -vn -0.2617 -0.2161 0.9406 -vn -0.0394 -0.1715 0.9844 -vn 0.0972 -0.1398 0.9854 -vn -0.8017 -0.3277 0.4998 -vn -0.8441 -0.3849 0.3732 -vn -0.7317 -0.3976 0.5537 -vn -0.4057 -0.3523 0.8434 -vn -0.1321 -0.2797 0.9509 -vn 0.0415 -0.2225 0.9740 -vn 0.1497 -0.1829 0.9716 -vn -0.7584 -0.4651 0.4566 -vn -0.5493 -0.4971 0.6716 -vn -0.7367 -0.5090 0.4451 -vn -0.2461 -0.4136 0.8765 -vn -0.0293 -0.3269 0.9446 -vn 0.1063 -0.2634 0.9588 -vn 0.1930 -0.2191 0.9564 -vn 0.4450 0.0531 0.8939 -vn 0.4259 0.0176 0.9046 -vn 0.4050 -0.0198 0.9141 -vn 0.3821 -0.0591 0.9222 -vn 0.3572 -0.1002 0.9286 -vn 0.3303 -0.1428 0.9330 -vn 0.4652 0.0177 0.8850 -vn 0.4458 -0.0197 0.8949 -vn 0.4856 -0.0195 0.8739 -vn 0.4243 -0.0591 0.9036 -vn 0.4657 -0.0590 0.8829 -vn 0.5061 -0.0587 0.8605 -vn 0.4009 -0.1004 0.9106 -vn 0.4438 -0.1003 0.8905 -vn 0.4857 -0.1001 0.8683 -vn 0.5264 -0.0996 0.8444 -vn 0.3753 -0.1433 0.9157 -vn 0.4197 -0.1436 0.8962 -vn 0.4631 -0.1434 0.8746 -vn 0.5054 -0.1429 0.8509 -vn 0.5463 -0.1422 0.8254 -vn 0.3469 -0.1895 0.9185 -vn 0.3929 -0.1900 0.8997 -vn 0.4381 -0.1901 0.8785 -vn 0.4823 -0.1898 0.8552 -vn 0.5250 -0.1889 0.8298 -vn 0.5660 -0.1877 0.8027 -vn 0.5994 -0.2384 0.7641 -vn 0.5887 -0.3047 0.7487 -vn 0.6447 -0.2865 0.7087 -vn 0.5701 -0.3913 0.7224 -vn 0.6354 -0.3764 0.6742 -vn 0.5365 -0.5052 0.6759 -vn 0.6120 -0.4975 0.6147 -vn 0.4749 -0.6505 0.5926 -vn 0.5580 -0.6550 0.5095 -vn 0.3658 -0.8149 0.4494 -vn 0.4465 -0.8307 0.3324 -vn 0.2635 -0.9608 0.0853 -vn 0.7031 -0.3548 0.6162 -vn 0.6917 -0.4807 0.5390 -vn 0.7728 -0.4533 0.4442 -vn 0.6469 -0.6461 0.4049 -vn 0.7356 -0.6189 0.2751 -vn 0.8131 -0.5686 0.1245 -vn 0.5326 -0.8248 0.1895 -vn 0.6150 -0.7882 0.0220 -vn 0.6803 -0.7161 -0.1562 -vn 0.7177 -0.6149 -0.3267 -vn 0.7668 -0.5520 -0.3274 -vn 0.3324 -0.9393 -0.0850 -vn 0.3953 -0.9103 -0.1223 -vn 0.4578 -0.8818 -0.1135 -vn 0.4989 -0.8564 -0.1328 -vn 0.5455 -0.8034 -0.2385 -vn 0.5835 -0.7709 -0.2551 -vn 0.6147 -0.7030 -0.3576 -vn 0.6476 -0.6655 -0.3710 -vn 0.6633 -0.5908 -0.4593 -vn 0.1492 -0.9884 -0.0275 -vn 0.2005 -0.9722 -0.1207 -vn 0.2380 -0.9609 -0.1413 -vn 0.2884 -0.9260 -0.2436 -vn 0.5539 -0.2409 0.7969 -vn 0.5070 -0.2427 0.8270 -vn 0.4581 -0.2438 0.8548 -vn 0.4075 -0.2439 0.8800 -vn 0.3558 -0.2432 0.9023 -vn 0.3035 -0.2417 0.9217 -vn 0.5360 -0.3077 0.7861 -vn 0.4815 -0.3101 0.8198 -vn 0.5079 -0.3950 0.7655 -vn 0.4245 -0.3110 0.8503 -vn 0.4433 -0.3977 0.8032 -vn 0.4618 -0.5096 0.7259 -vn 0.3657 -0.3104 0.8774 -vn 0.3760 -0.3979 0.8368 -vn 0.3842 -0.5121 0.7682 -vn 0.3844 -0.6553 0.6503 -vn 0.3059 -0.3085 0.9007 -vn 0.3069 -0.3957 0.8656 -vn 0.3037 -0.5101 0.8047 -vn 0.2907 -0.6555 0.6969 -vn 0.2583 -0.8183 0.5135 -vn 0.2444 -0.3058 0.9202 -vn 0.2350 -0.3924 0.8892 -vn 0.2187 -0.5062 0.8342 -vn 0.1901 -0.6514 0.7345 -vn 0.1424 -0.8157 0.5607 -vn -0.2513 -0.9625 0.1023 -vn -0.2633 -0.9412 0.2117 -vn -0.3635 -0.9280 0.0810 -vn -0.1068 -0.9892 0.1000 -vn -0.0858 -0.9620 0.2593 -vn -0.2122 -0.9735 0.0848 -vn -0.7632 -0.5954 0.2511 -vn -0.6829 -0.6698 0.2916 -vn -0.6900 -0.6194 0.3744 -vn -0.7205 -0.5568 0.4133 -vn -0.5513 -0.7746 0.3100 -vn -0.5308 -0.7200 0.4470 -vn -0.6522 -0.7070 0.2734 -vn -0.3876 -0.5725 0.7225 -vn -0.4022 -0.8593 0.3159 -vn -0.3489 -0.7913 0.5021 -vn -0.5149 -0.8068 0.2896 -vn -0.2205 -0.6220 0.7513 -vn -0.1088 -0.4561 0.8832 -vn -0.3311 -0.9127 0.2394 -vn -0.1655 -0.8271 0.5372 -vn -0.3619 -0.8844 0.2947 -vn -0.0633 -0.6484 0.7587 -vn 0.0158 -0.4828 0.8755 -vn 0.0700 -0.3569 0.9315 -vn 0.0005 -0.8321 0.5545 -vn 0.0737 -0.6565 0.7507 -vn 0.1244 -0.4990 0.8576 -vn 0.1572 -0.3779 0.9124 -vn 0.1787 -0.2879 0.9408 -vn 0.0677 0.5963 -0.7999 -vn 0.0233 0.6251 -0.7801 -vn -0.9654 -0.0200 -0.2600 -vn -0.9512 0.0137 -0.3083 -vn -0.9413 0.0426 -0.3348 -vn -0.9463 0.1074 -0.3048 -vn -0.9363 0.1562 -0.3146 -vn -0.9274 0.2059 -0.3122 -vn -0.9123 0.2475 -0.3261 -vn -0.9003 0.2899 -0.3247 -vn -0.8798 0.3340 -0.3381 -vn -0.8594 0.3788 -0.3435 -vn -0.8383 0.4089 -0.3606 -vn -0.8190 0.4452 -0.3620 -vn -0.7893 0.4825 -0.3798 -vn -0.7576 0.5231 -0.3904 -vn -0.7208 0.5578 -0.4115 -vn -0.6822 0.5940 -0.4264 -vn -0.6399 0.6230 -0.4499 -vn -0.5969 0.6515 -0.4682 -vn -0.5657 0.6622 -0.4913 -vn -0.5352 0.6813 -0.4994 -vn -0.4911 0.6972 -0.5222 -vn -0.4497 0.7093 -0.5428 -vn -0.4077 0.7186 -0.5633 -vn -0.3700 0.7226 -0.5838 -vn -0.3040 0.7353 -0.6058 -vn -0.2467 0.7230 -0.6452 -vn -0.2076 0.7193 -0.6630 -vn -0.1659 0.7102 -0.6842 -vn -0.1244 0.6984 -0.7048 -vn -0.0796 0.6827 -0.7263 -vn -0.0547 0.6638 -0.7458 -vn -0.0174 0.6533 -0.7568 -vn 0.1030 0.5604 -0.8218 -vn 0.1422 0.5260 -0.8385 -vn 0.1700 0.4855 -0.8575 -vn 0.2023 0.4484 -0.8706 -vn 0.2152 0.4122 -0.8853 -vn 0.2417 0.3823 -0.8918 -vn 0.2585 0.3376 -0.9051 -vn 0.2818 0.2937 -0.9134 -vn 0.2881 0.2514 -0.9240 -vn 0.3085 0.2099 -0.9278 -vn 0.3122 0.1602 -0.9364 -vn 0.3263 0.1114 -0.9387 -vn 0.3128 0.0451 -0.9487 -vn 0.3028 0.0132 -0.9529 -vn 0.3329 -0.0170 -0.9428 -vn 0.3252 -0.0809 -0.9422 -vn 0.3105 -0.1295 -0.9417 -vn 0.3062 -0.1794 -0.9349 -vn 0.2853 -0.2208 -0.9326 -vn 0.2785 -0.2634 -0.9236 -vn 0.2547 -0.3073 -0.9169 -vn 0.2373 -0.3522 -0.9053 -vn 0.2105 -0.3821 -0.8998 -vn 0.1971 -0.4186 -0.8865 -vn 0.1644 -0.4558 -0.8748 -vn 0.1361 -0.4966 -0.8572 -vn 0.0965 -0.5311 -0.8417 -vn 0.0608 -0.5674 -0.8212 -vn 0.0160 -0.5964 -0.8025 -vn -0.0250 -0.6250 -0.7802 -vn -0.0623 -0.6354 -0.7696 -vn -0.0875 -0.6547 -0.7508 -vn -0.1325 -0.6706 -0.7299 -vn -0.1742 -0.6826 -0.7097 -vn -0.2159 -0.6920 -0.6888 -vn -0.2551 -0.6959 -0.6712 -vn -0.3126 -0.7089 -0.6322 -vn -0.3784 -0.6963 -0.6098 -vn -0.4161 -0.6926 -0.5892 -vn -0.4580 -0.6835 -0.5683 -vn -0.4992 -0.6718 -0.5473 -vn -0.5431 -0.6561 -0.5239 -vn -0.5734 -0.6370 -0.5151 -vn -0.6044 -0.6268 -0.4916 -vn -0.6471 -0.5985 -0.4722 -vn -0.6891 -0.5698 -0.4477 -vn -0.7272 -0.5338 -0.4315 -vn -0.7636 -0.4995 -0.4091 -vn -0.7949 -0.4589 -0.3970 -vn -0.8241 -0.4219 -0.3778 -vn -0.8430 -0.3854 -0.3751 -vn -0.8637 -0.3557 -0.3569 -vn -0.8836 -0.3110 -0.3499 -vn -0.9036 -0.2672 -0.3349 -vn -0.9151 -0.2246 -0.3348 -vn -0.9297 -0.1834 -0.3194 -vn -0.9380 -0.1335 -0.3199 -vn -0.9474 -0.0849 -0.3083 -usemtl Material.003 -s 1 -f 512/639/505 827/640/506 505/641/507 -f 506/642/508 518/643/509 530/644/510 -f 531/645/511 505/646/507 835/647/512 -f 506/642/508 530/644/510 549/648/513 -f 507/649/514 542/650/515 573/651/516 -f 506/642/508 549/648/513 560/652/517 -f 507/649/514 573/651/516 550/653/518 -f 508/654/519 584/655/520 602/656/521 -f 509/657/522 588/658/523 616/659/524 -f 510/660/525 595/661/526 603/662/527 -f 610/663/528 511/664/529 609/665/530 -f 608/666/531 617/667/532 609/665/530 -f 607/668/533 618/669/534 608/666/531 -f 606/670/535 620/671/536 607/668/533 -f 605/672/537 623/673/538 606/670/535 -f 604/674/539 627/675/540 605/672/537 -f 603/662/527 632/676/541 604/674/539 -f 609/665/530 617/667/532 610/663/528 -f 617/667/532 611/677/542 610/663/528 -f 608/666/531 618/669/534 617/667/532 -f 618/669/534 619/678/543 617/667/532 -f 617/667/532 619/678/543 611/677/542 -f 619/678/543 612/679/544 611/677/542 -f 607/668/533 620/671/536 618/669/534 -f 620/671/536 621/680/545 618/669/534 -f 618/669/534 621/680/545 619/678/543 -f 621/680/545 622/681/546 619/678/543 -f 619/678/543 622/681/546 612/679/544 -f 622/681/546 613/682/547 612/679/544 -f 606/670/535 623/673/538 620/671/536 -f 623/673/538 624/683/548 620/671/536 -f 620/671/536 624/683/548 621/680/545 -f 624/683/548 625/684/549 621/680/545 -f 621/680/545 625/684/549 622/681/546 -f 625/684/549 626/685/550 622/681/546 -f 622/681/546 626/685/550 613/682/547 -f 626/685/550 614/686/551 613/682/547 -f 605/672/537 627/675/540 623/673/538 -f 627/675/540 628/687/552 623/673/538 -f 623/673/538 628/687/552 624/683/548 -f 628/687/552 629/688/553 624/683/548 -f 624/683/548 629/688/553 625/684/549 -f 629/688/553 630/689/554 625/684/549 -f 625/684/549 630/689/554 626/685/550 -f 630/689/554 631/690/555 626/685/550 -f 626/685/550 631/690/555 614/686/551 -f 631/690/555 615/691/556 614/686/551 -f 604/674/539 632/676/541 627/675/540 -f 632/676/541 633/692/557 627/675/540 -f 627/675/540 633/692/557 628/687/552 -f 633/692/557 634/693/558 628/687/552 -f 628/687/552 634/693/558 629/688/553 -f 634/693/558 635/694/559 629/688/553 -f 629/688/553 635/694/559 630/689/554 -f 635/694/559 636/695/560 630/689/554 -f 630/689/554 636/695/560 631/690/555 -f 636/695/560 637/696/561 631/690/555 -f 631/690/555 637/696/561 615/691/556 -f 637/696/561 616/697/524 615/691/556 -f 603/662/527 595/661/526 632/676/541 -f 595/661/526 594/698/562 632/676/541 -f 632/676/541 594/698/562 633/692/557 -f 594/698/562 593/699/563 633/692/557 -f 633/692/557 593/699/563 634/693/558 -f 593/699/563 592/700/564 634/693/558 -f 634/693/558 592/700/564 635/694/559 -f 592/700/564 591/701/565 635/694/559 -f 635/694/559 591/701/565 636/695/560 -f 591/701/565 590/702/566 636/695/560 -f 636/695/560 590/702/566 637/696/561 -f 590/702/566 589/703/567 637/696/561 -f 637/696/561 589/703/567 616/697/524 -f 589/703/567 509/657/522 616/697/524 -f 610/704/528 839/705/568 511/706/529 -f 610/704/528 611/707/542 844/708/569 843/709/570 -f 611/707/542 612/710/544 848/711/571 847/712/572 -f 613/713/547 638/714/573 612/710/544 -f 614/715/551 639/716/574 613/713/547 -f 615/717/556 640/718/575 614/715/551 -f 616/659/524 641/719/576 615/717/556 -f 843/709/570 839/705/568 610/704/528 -f 847/712/572 844/708/569 611/707/542 -f 612/710/544 638/714/573 861/720/577 848/711/571 -f 866/721/578 861/720/577 638/714/573 -f 613/713/547 639/716/574 638/714/573 -f 638/714/573 639/716/574 870/722/579 869/723/580 -f 869/723/580 866/721/578 638/714/573 -f 614/715/551 640/718/575 639/716/574 -f 639/716/574 640/718/575 880/724/581 878/725/582 -f 878/725/582 870/722/579 639/716/574 -f 615/717/556 641/719/576 640/718/575 -f 640/718/575 641/719/576 885/726/583 883/727/584 -f 880/724/581 640/718/575 883/727/584 -f 616/659/524 588/658/523 641/719/576 -f 588/658/523 587/728/585 641/719/576 -f 641/719/576 587/728/585 893/729/586 885/726/583 -f 893/729/586 587/728/585 896/730/587 -f 596/731/588 511/732/529 900/733/589 -f 900/733/589 903/734/590 596/731/588 -f 597/735/591 596/731/588 903/734/590 906/736/592 -f 906/736/592 907/737/593 597/735/591 -f 598/738/594 597/735/591 907/737/593 909/739/595 -f 910/740/596 828/741/597 642/742/598 -f 642/742/598 598/738/594 909/739/595 910/740/596 -f 642/742/598 599/743/599 598/738/594 -f 828/741/597 829/744/600 642/742/598 -f 643/745/601 642/742/598 829/744/600 838/746/602 -f 642/742/598 643/745/601 599/743/599 -f 643/745/601 600/747/603 599/743/599 -f 838/746/602 840/748/604 643/745/601 -f 644/749/605 643/745/601 840/748/604 846/750/606 -f 643/745/601 644/749/605 600/747/603 -f 644/749/605 601/751/607 600/747/603 -f 644/749/605 846/750/606 853/752/608 -f 645/753/609 644/749/605 853/752/608 860/754/610 -f 644/749/605 645/753/609 601/751/607 -f 645/753/609 602/755/521 601/751/607 -f 864/756/611 865/757/612 586/758/613 -f 586/758/613 645/753/609 860/754/610 864/756/611 -f 586/758/613 585/759/614 645/753/609 -f 645/753/609 585/759/614 602/755/521 -f 585/759/614 508/654/519 602/755/521 -f 511/760/529 596/761/588 609/762/530 -f 597/763/591 646/764/615 596/761/588 -f 598/765/594 647/766/616 597/763/591 -f 599/767/599 649/768/617 598/765/594 -f 600/769/603 652/770/618 599/767/599 -f 601/771/607 656/772/619 600/769/603 -f 602/656/521 661/773/620 601/771/607 -f 596/761/588 646/764/615 609/762/530 -f 646/764/615 608/774/531 609/762/530 -f 597/763/591 647/766/616 646/764/615 -f 647/766/616 648/775/621 646/764/615 -f 646/764/615 648/775/621 608/774/531 -f 648/775/621 607/776/533 608/774/531 -f 598/765/594 649/768/617 647/766/616 -f 649/768/617 650/777/622 647/766/616 -f 647/766/616 650/777/622 648/775/621 -f 650/777/622 651/778/623 648/775/621 -f 648/775/621 651/778/623 607/776/533 -f 651/778/623 606/779/535 607/776/533 -f 599/767/599 652/770/618 649/768/617 -f 652/770/618 653/780/624 649/768/617 -f 649/768/617 653/780/624 650/777/622 -f 653/780/624 654/781/625 650/777/622 -f 650/777/622 654/781/625 651/778/623 -f 654/781/625 655/782/626 651/778/623 -f 651/778/623 655/782/626 606/779/535 -f 655/782/626 605/783/537 606/779/535 -f 600/769/603 656/772/619 652/770/618 -f 656/772/619 657/784/627 652/770/618 -f 652/770/618 657/784/627 653/780/624 -f 657/784/627 658/785/628 653/780/624 -f 653/780/624 658/785/628 654/781/625 -f 658/785/628 659/786/629 654/781/625 -f 654/781/625 659/786/629 655/782/626 -f 659/786/629 660/787/630 655/782/626 -f 655/782/626 660/787/630 605/783/537 -f 660/787/630 604/788/539 605/783/537 -f 601/771/607 661/773/620 656/772/619 -f 661/773/620 662/789/631 656/772/619 -f 656/772/619 662/789/631 657/784/627 -f 662/789/631 663/790/632 657/784/627 -f 657/784/627 663/790/632 658/785/628 -f 663/790/632 664/791/633 658/785/628 -f 658/785/628 664/791/633 659/786/629 -f 664/791/633 665/792/634 659/786/629 -f 659/786/629 665/792/634 660/787/630 -f 665/792/634 666/793/635 660/787/630 -f 660/787/630 666/793/635 604/788/539 -f 666/793/635 603/794/527 604/788/539 -f 602/656/521 584/655/520 661/773/620 -f 584/655/520 583/795/636 661/773/620 -f 661/773/620 583/795/636 662/789/631 -f 583/795/636 582/796/637 662/789/631 -f 662/789/631 582/796/637 663/790/632 -f 582/796/637 581/797/638 663/790/632 -f 663/790/632 581/797/638 664/791/633 -f 581/797/638 580/798/639 664/791/633 -f 664/791/633 580/798/639 665/792/634 -f 580/798/639 579/799/640 665/792/634 -f 665/792/634 579/799/640 666/793/635 -f 579/799/640 578/800/641 666/793/635 -f 666/793/635 578/800/641 603/794/527 -f 578/800/641 510/660/525 603/794/527 -f 556/801/642 595/661/526 510/660/525 -f 555/802/643 667/803/644 556/801/642 -f 554/804/645 668/805/646 555/802/643 -f 553/806/647 670/807/648 554/804/645 -f 552/808/649 673/809/650 553/806/647 -f 551/810/651 677/811/652 552/808/649 -f 550/653/518 682/812/653 551/810/651 -f 556/801/642 667/803/644 595/661/526 -f 667/803/644 594/698/562 595/661/526 -f 555/802/643 668/805/646 667/803/644 -f 668/805/646 669/813/654 667/803/644 -f 667/803/644 669/813/654 594/698/562 -f 669/813/654 593/699/563 594/698/562 -f 554/804/645 670/807/648 668/805/646 -f 670/807/648 671/814/655 668/805/646 -f 668/805/646 671/814/655 669/813/654 -f 671/814/655 672/815/656 669/813/654 -f 669/813/654 672/815/656 593/699/563 -f 672/815/656 592/700/564 593/699/563 -f 553/806/647 673/809/650 670/807/648 -f 673/809/650 674/816/657 670/807/648 -f 670/807/648 674/816/657 671/814/655 -f 674/816/657 675/817/658 671/814/655 -f 671/814/655 675/817/658 672/815/656 -f 675/817/658 676/818/659 672/815/656 -f 672/815/656 676/818/659 592/700/564 -f 676/818/659 591/701/565 592/700/564 -f 552/808/649 677/811/652 673/809/650 -f 677/811/652 678/819/660 673/809/650 -f 673/809/650 678/819/660 674/816/657 -f 678/819/660 679/820/661 674/816/657 -f 674/816/657 679/820/661 675/817/658 -f 679/820/661 680/821/662 675/817/658 -f 675/817/658 680/821/662 676/818/659 -f 680/821/662 681/822/663 676/818/659 -f 676/818/659 681/822/663 591/701/565 -f 681/822/663 590/702/566 591/701/565 -f 551/810/651 682/812/653 677/811/652 -f 682/812/653 683/823/664 677/811/652 -f 677/811/652 683/823/664 678/819/660 -f 683/823/664 684/824/665 678/819/660 -f 678/819/660 684/824/665 679/820/661 -f 684/824/665 685/825/666 679/820/661 -f 679/820/661 685/825/666 680/821/662 -f 685/825/666 686/826/667 680/821/662 -f 680/821/662 686/826/667 681/822/663 -f 686/826/667 687/827/668 681/822/663 -f 681/822/663 687/827/668 590/702/566 -f 687/827/668 589/703/567 590/702/566 -f 550/653/518 573/651/516 682/812/653 -f 573/651/516 572/828/669 682/812/653 -f 682/812/653 572/828/669 683/823/664 -f 572/828/669 571/829/670 683/823/664 -f 683/823/664 571/829/670 684/824/665 -f 571/829/670 570/830/671 684/824/665 -f 684/824/665 570/830/671 685/825/666 -f 570/830/671 569/831/672 685/825/666 -f 685/825/666 569/831/672 686/826/667 -f 569/831/672 568/832/673 686/826/667 -f 686/826/667 568/832/673 687/827/668 -f 568/832/673 567/833/674 687/827/668 -f 687/827/668 567/833/674 589/703/567 -f 567/833/674 509/657/522 589/703/567 -f 577/834/675 588/658/523 509/657/522 -f 576/835/676 688/836/677 577/834/675 -f 575/837/678 689/838/679 576/835/676 -f 575/837/678 574/839/680 877/840/681 879/841/682 -f 882/842/683 574/839/680 832/843/684 -f 577/834/675 688/836/677 588/658/523 -f 688/836/677 587/728/585 588/658/523 -f 576/835/676 689/838/679 688/836/677 -f 688/836/677 689/838/679 888/844/685 889/845/686 -f 587/728/585 688/836/677 889/845/686 892/846/687 -f 896/730/587 587/728/585 892/846/687 -f 689/838/679 575/837/678 879/841/682 901/847/688 -f 905/848/689 689/838/679 901/847/688 -f 888/844/685 689/838/679 905/848/689 -f 877/840/681 574/839/680 882/842/683 -f 865/757/612 911/849/690 586/758/613 -f 912/850/691 913/851/692 690/852/693 -f 914/853/694 912/850/691 690/852/693 -f 690/852/693 691/854/695 915/855/696 914/853/694 -f 691/854/695 586/758/613 911/849/690 915/855/696 -f 691/854/695 585/759/614 586/758/613 -f 916/856/697 854/857/698 559/858/699 -f 559/858/699 690/852/693 913/851/692 916/856/697 -f 559/858/699 558/859/700 690/852/693 -f 690/852/693 558/859/700 691/854/695 -f 558/859/700 557/860/701 691/854/695 -f 691/854/695 557/860/701 585/759/614 -f 557/860/701 508/654/519 585/759/614 -f 566/861/702 584/655/520 508/654/519 -f 565/862/703 692/863/704 566/861/702 -f 564/864/705 693/865/706 565/862/703 -f 563/866/707 695/867/708 564/864/705 -f 562/868/709 698/869/710 563/866/707 -f 561/870/711 702/871/712 562/868/709 -f 560/652/517 707/872/713 561/870/711 -f 566/861/702 692/863/704 584/655/520 -f 692/863/704 583/795/636 584/655/520 -f 565/862/703 693/865/706 692/863/704 -f 693/865/706 694/873/714 692/863/704 -f 692/863/704 694/873/714 583/795/636 -f 694/873/714 582/796/637 583/795/636 -f 564/864/705 695/867/708 693/865/706 -f 695/867/708 696/874/715 693/865/706 -f 693/865/706 696/874/715 694/873/714 -f 696/874/715 697/875/716 694/873/714 -f 694/873/714 697/875/716 582/796/637 -f 697/875/716 581/797/638 582/796/637 -f 563/866/707 698/869/710 695/867/708 -f 698/869/710 699/876/717 695/867/708 -f 695/867/708 699/876/717 696/874/715 -f 699/876/717 700/877/718 696/874/715 -f 696/874/715 700/877/718 697/875/716 -f 700/877/718 701/878/719 697/875/716 -f 697/875/716 701/878/719 581/797/638 -f 701/878/719 580/798/639 581/797/638 -f 562/868/709 702/871/712 698/869/710 -f 702/871/712 703/879/720 698/869/710 -f 698/869/710 703/879/720 699/876/717 -f 703/879/720 704/880/721 699/876/717 -f 699/876/717 704/880/721 700/877/718 -f 704/880/721 705/881/722 700/877/718 -f 700/877/718 705/881/722 701/878/719 -f 705/881/722 706/882/723 701/878/719 -f 701/878/719 706/882/723 580/798/639 -f 706/882/723 579/799/640 580/798/639 -f 561/870/711 707/872/713 702/871/712 -f 707/872/713 708/883/724 702/871/712 -f 702/871/712 708/883/724 703/879/720 -f 708/883/724 709/884/725 703/879/720 -f 703/879/720 709/884/725 704/880/721 -f 709/884/725 710/885/726 704/880/721 -f 704/880/721 710/885/726 705/881/722 -f 710/885/726 711/886/727 705/881/722 -f 705/881/722 711/886/727 706/882/723 -f 711/886/727 712/887/728 706/882/723 -f 706/882/723 712/887/728 579/799/640 -f 712/887/728 578/800/641 579/799/640 -f 560/652/517 549/648/513 707/872/713 -f 549/648/513 548/888/729 707/872/713 -f 707/872/713 548/888/729 708/883/724 -f 548/888/729 547/889/730 708/883/724 -f 708/883/724 547/889/730 709/884/725 -f 547/889/730 546/890/731 709/884/725 -f 709/884/725 546/890/731 710/885/726 -f 546/890/731 545/891/732 710/885/726 -f 710/885/726 545/891/732 711/886/727 -f 545/891/732 544/892/733 711/886/727 -f 711/886/727 544/892/733 712/887/728 -f 544/892/733 543/893/734 712/887/728 -f 712/887/728 543/893/734 578/800/641 -f 543/893/734 510/660/525 578/800/641 -f 567/833/674 577/834/675 509/657/522 -f 568/832/673 713/894/735 567/833/674 -f 569/831/672 714/895/736 568/832/673 -f 570/830/671 716/896/737 569/831/672 -f 571/829/670 719/897/738 570/830/671 -f 572/828/669 723/898/739 571/829/670 -f 573/651/516 727/899/740 572/828/669 -f 567/833/674 713/894/735 577/834/675 -f 713/894/735 576/835/676 577/834/675 -f 568/832/673 714/895/736 713/894/735 -f 714/895/736 715/900/741 713/894/735 -f 713/894/735 715/900/741 576/835/676 -f 715/900/741 575/837/678 576/835/676 -f 569/831/672 716/896/737 714/895/736 -f 716/896/737 717/901/742 714/895/736 -f 714/895/736 717/901/742 715/900/741 -f 717/901/742 718/902/743 715/900/741 -f 715/900/741 718/902/743 575/837/678 -f 575/837/678 718/902/743 574/839/680 -f 570/830/671 719/897/738 716/896/737 -f 719/897/738 720/903/744 716/896/737 -f 716/896/737 720/903/744 717/901/742 -f 720/903/744 721/904/745 717/901/742 -f 717/901/742 721/904/745 718/902/743 -f 721/904/745 722/905/746 718/902/743 -f 574/839/680 718/902/743 722/905/746 -f 574/839/680 722/905/746 833/906/747 832/843/684 -f 571/829/670 723/898/739 719/897/738 -f 723/898/739 724/907/748 719/897/738 -f 719/897/738 724/907/748 720/903/744 -f 724/907/748 725/908/749 720/903/744 -f 720/903/744 725/908/749 721/904/745 -f 725/908/749 726/909/750 721/904/745 -f 721/904/745 726/909/750 722/905/746 -f 722/905/746 726/909/750 836/910/751 834/911/752 -f 833/906/747 722/905/746 834/911/752 -f 572/828/669 727/899/740 723/898/739 -f 727/899/740 728/912/753 723/898/739 -f 723/898/739 728/912/753 724/907/748 -f 728/912/753 729/913/754 724/907/748 -f 724/907/748 729/913/754 725/908/749 -f 729/913/754 730/914/755 725/908/749 -f 725/908/749 730/914/755 726/909/750 -f 730/914/755 731/915/756 726/909/750 -f 726/909/750 731/915/756 841/916/757 836/910/751 -f 841/916/757 731/915/756 845/917/758 -f 573/651/516 542/650/515 727/899/740 -f 542/650/515 541/918/759 727/899/740 -f 727/899/740 541/918/759 728/912/753 -f 541/918/759 540/919/760 728/912/753 -f 728/912/753 540/919/760 729/913/754 -f 540/919/760 539/920/761 729/913/754 -f 729/913/754 539/920/761 730/914/755 -f 539/920/761 538/921/762 730/914/755 -f 730/914/755 538/921/762 731/915/756 -f 731/915/756 538/921/762 850/922/763 849/923/764 -f 845/917/758 731/915/756 849/923/764 -f 557/860/701 566/861/702 508/654/519 -f 558/859/700 732/924/765 557/860/701 -f 559/858/699 733/925/766 558/859/700 -f 735/926/767 559/858/699 854/857/698 857/927/768 -f 858/928/769 859/929/770 738/930/771 -f 557/860/701 732/924/765 566/861/702 -f 732/924/765 565/862/703 566/861/702 -f 558/859/700 733/925/766 732/924/765 -f 733/925/766 734/931/772 732/924/765 -f 732/924/765 734/931/772 565/862/703 -f 734/931/772 564/864/705 565/862/703 -f 559/858/699 735/926/767 733/925/766 -f 735/926/767 736/932/773 733/925/766 -f 733/925/766 736/932/773 734/931/772 -f 736/932/773 737/933/774 734/931/772 -f 734/931/772 737/933/774 564/864/705 -f 737/933/774 563/866/707 564/864/705 -f 738/930/771 735/926/767 857/927/768 858/928/769 -f 738/930/771 739/934/775 735/926/767 -f 735/926/767 739/934/775 736/932/773 -f 739/934/775 740/935/776 736/932/773 -f 736/932/773 740/935/776 737/933/774 -f 740/935/776 741/936/777 737/933/774 -f 737/933/774 741/936/777 563/866/707 -f 741/936/777 562/868/709 563/866/707 -f 859/929/770 868/937/778 738/930/771 -f 742/938/779 738/930/771 868/937/778 871/939/780 -f 738/930/771 742/938/779 739/934/775 -f 742/938/779 743/940/781 739/934/775 -f 739/934/775 743/940/781 740/935/776 -f 743/940/781 744/941/782 740/935/776 -f 740/935/776 744/941/782 741/936/777 -f 744/941/782 745/942/783 741/936/777 -f 741/936/777 745/942/783 562/868/709 -f 745/942/783 561/870/711 562/868/709 -f 873/943/784 874/944/785 746/945/786 -f 746/945/786 742/938/779 871/939/780 873/943/784 -f 746/945/786 747/946/787 742/938/779 -f 742/938/779 747/946/787 743/940/781 -f 747/946/787 748/947/788 743/940/781 -f 743/940/781 748/947/788 744/941/782 -f 748/947/788 749/948/789 744/941/782 -f 744/941/782 749/948/789 745/942/783 -f 749/948/789 750/949/790 745/942/783 -f 745/942/783 750/949/790 561/870/711 -f 750/949/790 560/652/517 561/870/711 -f 874/944/785 881/950/791 746/945/786 -f 523/951/792 746/945/786 881/950/791 872/952/793 -f 746/945/786 523/951/792 747/946/787 -f 523/951/792 522/953/794 747/946/787 -f 747/946/787 522/953/794 748/947/788 -f 522/953/794 521/954/795 748/947/788 -f 748/947/788 521/954/795 749/948/789 -f 521/954/795 520/955/796 749/948/789 -f 749/948/789 520/955/796 750/949/790 -f 520/955/796 519/956/797 750/949/790 -f 750/949/790 519/956/797 560/652/517 -f 519/956/797 506/642/508 560/652/517 -f 543/893/734 556/801/642 510/660/525 -f 544/892/733 751/957/798 543/893/734 -f 545/891/732 752/958/799 544/892/733 -f 546/890/731 754/959/800 545/891/732 -f 547/889/730 757/960/801 546/890/731 -f 548/888/729 761/961/802 547/889/730 -f 549/648/513 766/962/803 548/888/729 -f 543/893/734 751/957/798 556/801/642 -f 751/957/798 555/802/643 556/801/642 -f 544/892/733 752/958/799 751/957/798 -f 752/958/799 753/963/804 751/957/798 -f 751/957/798 753/963/804 555/802/643 -f 753/963/804 554/804/645 555/802/643 -f 545/891/732 754/959/800 752/958/799 -f 754/959/800 755/964/805 752/958/799 -f 752/958/799 755/964/805 753/963/804 -f 755/964/805 756/965/806 753/963/804 -f 753/963/804 756/965/806 554/804/645 -f 756/965/806 553/806/647 554/804/645 -f 546/890/731 757/960/801 754/959/800 -f 757/960/801 758/966/807 754/959/800 -f 754/959/800 758/966/807 755/964/805 -f 758/966/807 759/967/808 755/964/805 -f 755/964/805 759/967/808 756/965/806 -f 759/967/808 760/968/809 756/965/806 -f 756/965/806 760/968/809 553/806/647 -f 760/968/809 552/808/649 553/806/647 -f 547/889/730 761/961/802 757/960/801 -f 761/961/802 762/969/810 757/960/801 -f 757/960/801 762/969/810 758/966/807 -f 762/969/810 763/970/811 758/966/807 -f 758/966/807 763/970/811 759/967/808 -f 763/970/811 764/971/812 759/967/808 -f 759/967/808 764/971/812 760/968/809 -f 764/971/812 765/972/813 760/968/809 -f 760/968/809 765/972/813 552/808/649 -f 765/972/813 551/810/651 552/808/649 -f 548/888/729 766/962/803 761/961/802 -f 766/962/803 767/973/814 761/961/802 -f 761/961/802 767/973/814 762/969/810 -f 767/973/814 768/974/815 762/969/810 -f 762/969/810 768/974/815 763/970/811 -f 768/974/815 769/975/816 763/970/811 -f 763/970/811 769/975/816 764/971/812 -f 769/975/816 770/976/817 764/971/812 -f 764/971/812 770/976/817 765/972/813 -f 770/976/817 771/977/818 765/972/813 -f 765/972/813 771/977/818 551/810/651 -f 771/977/818 550/653/518 551/810/651 -f 549/648/513 530/644/510 766/962/803 -f 530/644/510 529/978/819 766/962/803 -f 766/962/803 529/978/819 767/973/814 -f 529/978/819 528/979/820 767/973/814 -f 767/973/814 528/979/820 768/974/815 -f 528/979/820 527/980/821 768/974/815 -f 768/974/815 527/980/821 769/975/816 -f 527/980/821 526/981/822 769/975/816 -f 769/975/816 526/981/822 770/976/817 -f 526/981/822 525/982/823 770/976/817 -f 770/976/817 525/982/823 771/977/818 -f 525/982/823 524/983/824 771/977/818 -f 771/977/818 524/983/824 550/653/518 -f 524/983/824 507/649/514 550/653/518 -f 537/984/825 542/650/515 507/649/514 -f 536/985/826 772/986/827 537/984/825 -f 535/987/828 773/988/829 536/985/826 -f 534/989/830 775/990/831 535/987/828 -f 533/991/832 778/992/833 534/989/830 -f 532/993/834 782/994/835 533/991/832 -f 531/645/511 787/995/836 532/993/834 -f 537/984/825 772/986/827 542/650/515 -f 772/986/827 541/918/759 542/650/515 -f 536/985/826 773/988/829 772/986/827 -f 773/988/829 774/996/837 772/986/827 -f 772/986/827 774/996/837 541/918/759 -f 774/996/837 540/919/760 541/918/759 -f 535/987/828 775/990/831 773/988/829 -f 775/990/831 776/997/838 773/988/829 -f 773/988/829 776/997/838 774/996/837 -f 776/997/838 777/998/839 774/996/837 -f 774/996/837 777/998/839 540/919/760 -f 777/998/839 539/920/761 540/919/760 -f 534/989/830 778/992/833 775/990/831 -f 778/992/833 779/999/840 775/990/831 -f 775/990/831 779/999/840 776/997/838 -f 779/999/840 780/1000/841 776/997/838 -f 776/997/838 780/1000/841 777/998/839 -f 780/1000/841 781/1001/842 777/998/839 -f 777/998/839 781/1001/842 539/920/761 -f 781/1001/842 538/921/762 539/920/761 -f 533/991/832 782/994/835 778/992/833 -f 782/994/835 783/1002/843 778/992/833 -f 778/992/833 783/1002/843 779/999/840 -f 783/1002/843 784/1003/844 779/999/840 -f 779/999/840 784/1003/844 780/1000/841 -f 784/1003/844 785/1004/845 780/1000/841 -f 780/1000/841 785/1004/845 781/1001/842 -f 785/1004/845 786/1005/846 781/1001/842 -f 781/1001/842 786/1005/846 538/921/762 -f 538/921/762 786/1005/846 884/1006/847 850/922/763 -f 532/993/834 787/995/836 782/994/835 -f 787/995/836 788/1007/848 782/994/835 -f 782/994/835 788/1007/848 783/1002/843 -f 783/1002/843 788/1007/848 842/1008/849 890/1009/850 -f 784/1003/844 783/1002/843 890/1009/850 891/1010/851 -f 894/1011/852 784/1003/844 891/1010/851 -f 785/1004/845 784/1003/844 894/1011/852 897/1012/853 -f 898/1013/854 785/1004/845 897/1012/853 -f 786/1005/846 785/1004/845 898/1013/854 902/1014/855 -f 904/1015/856 786/1005/846 902/1014/855 -f 884/1006/847 786/1005/846 904/1015/856 -f 787/995/836 531/645/511 835/647/512 908/1016/857 -f 831/1017/858 787/995/836 908/1016/857 -f 788/1007/848 787/995/836 831/1017/858 830/1018/859 -f 837/1019/860 788/1007/848 830/1018/859 -f 842/1008/849 788/1007/848 837/1019/860 -f 524/983/824 537/1020/825 507/649/514 -f 525/982/823 789/1021/861 524/983/824 -f 526/981/822 790/1022/862 525/982/823 -f 527/980/821 792/1023/863 526/981/822 -f 528/979/820 795/1024/864 527/980/821 -f 529/978/819 799/1025/865 528/979/820 -f 530/644/510 804/1026/866 529/978/819 -f 524/983/824 789/1021/861 537/1020/825 -f 789/1021/861 536/1027/826 537/1020/825 -f 525/982/823 790/1022/862 789/1021/861 -f 790/1022/862 791/1028/867 789/1021/861 -f 789/1021/861 791/1028/867 536/1027/826 -f 791/1028/867 535/1029/828 536/1027/826 -f 526/981/822 792/1023/863 790/1022/862 -f 792/1023/863 793/1030/868 790/1022/862 -f 790/1022/862 793/1030/868 791/1028/867 -f 793/1030/868 794/1031/869 791/1028/867 -f 791/1028/867 794/1031/869 535/1029/828 -f 794/1031/869 534/1032/830 535/1029/828 -f 527/980/821 795/1024/864 792/1023/863 -f 795/1024/864 796/1033/870 792/1023/863 -f 792/1023/863 796/1033/870 793/1030/868 -f 796/1033/870 797/1034/871 793/1030/868 -f 793/1030/868 797/1034/871 794/1031/869 -f 797/1034/871 798/1035/872 794/1031/869 -f 794/1031/869 798/1035/872 534/1032/830 -f 798/1035/872 533/1036/832 534/1032/830 -f 528/979/820 799/1025/865 795/1024/864 -f 799/1025/865 800/1037/873 795/1024/864 -f 795/1024/864 800/1037/873 796/1033/870 -f 800/1037/873 801/1038/874 796/1033/870 -f 796/1033/870 801/1038/874 797/1034/871 -f 801/1038/874 802/1039/875 797/1034/871 -f 797/1034/871 802/1039/875 798/1035/872 -f 802/1039/875 803/1040/876 798/1035/872 -f 798/1035/872 803/1040/876 533/1036/832 -f 803/1040/876 532/1041/834 533/1036/832 -f 529/978/819 804/1026/866 799/1025/865 -f 804/1026/866 805/1042/877 799/1025/865 -f 799/1025/865 805/1042/877 800/1037/873 -f 805/1042/877 806/1043/878 800/1037/873 -f 800/1037/873 806/1043/878 801/1038/874 -f 806/1043/878 807/1044/879 801/1038/874 -f 801/1038/874 807/1044/879 802/1039/875 -f 807/1044/879 808/1045/880 802/1039/875 -f 802/1039/875 808/1045/880 803/1040/876 -f 808/1045/880 809/1046/881 803/1040/876 -f 803/1040/876 809/1046/881 532/1041/834 -f 809/1046/881 531/1047/511 532/1041/834 -f 530/644/510 518/643/509 804/1026/866 -f 518/643/509 517/1048/882 804/1026/866 -f 804/1026/866 517/1048/882 805/1042/877 -f 517/1048/882 516/1049/883 805/1042/877 -f 805/1042/877 516/1049/883 806/1043/878 -f 516/1049/883 515/1050/884 806/1043/878 -f 806/1043/878 515/1050/884 807/1044/879 -f 515/1050/884 514/1051/885 807/1044/879 -f 807/1044/879 514/1051/885 808/1045/880 -f 514/1051/885 513/1052/886 808/1045/880 -f 808/1045/880 513/1052/886 809/1046/881 -f 513/1052/886 512/1053/505 809/1046/881 -f 809/1046/881 512/1053/505 531/1047/511 -f 505/1054/507 531/1047/511 512/1053/505 -f 851/1055/887 816/1056/888 852/1057/889 -f 855/1058/890 821/1059/891 856/1060/892 -f 862/1061/893 863/1062/894 810/1063/895 -f 867/1064/896 862/1061/893 810/1063/895 -f 810/1063/895 523/951/792 872/952/793 867/1064/896 -f 875/1065/897 811/1066/898 876/1067/899 -f 811/1066/898 810/1063/895 863/1062/894 876/1067/899 -f 811/1066/898 812/1068/900 810/1063/895 -f 810/1063/895 812/1068/900 523/951/792 -f 812/1068/900 522/953/794 523/951/792 -f 886/1069/901 813/1070/902 887/1071/903 -f 813/1070/902 811/1066/898 875/1065/897 887/1071/903 -f 813/1070/902 814/1072/904 811/1066/898 -f 811/1066/898 814/1072/904 812/1068/900 -f 814/1072/904 815/1073/905 812/1068/900 -f 812/1068/900 815/1073/905 522/953/794 -f 815/1073/905 521/954/795 522/953/794 -f 852/1057/889 816/1056/888 895/1074/906 -f 816/1056/888 817/1075/907 899/1076/908 895/1074/906 -f 817/1075/907 813/1070/902 886/1069/901 899/1076/908 -f 817/1075/907 818/1077/909 813/1070/902 -f 813/1070/902 818/1077/909 814/1072/904 -f 818/1077/909 819/1078/910 814/1072/904 -f 814/1072/904 819/1078/910 815/1073/905 -f 819/1078/910 820/1079/911 815/1073/905 -f 815/1073/905 820/1079/911 521/954/795 -f 820/1079/911 520/955/796 521/954/795 -f 821/1059/891 816/1056/888 851/1055/887 856/1060/892 -f 821/1059/891 822/1080/912 816/1056/888 -f 816/1056/888 822/1080/912 817/1075/907 -f 822/1080/912 823/1081/913 817/1075/907 -f 817/1075/907 823/1081/913 818/1077/909 -f 823/1081/913 824/1082/914 818/1077/909 -f 818/1077/909 824/1082/914 819/1078/910 -f 824/1082/914 825/1083/915 819/1078/910 -f 819/1078/910 825/1083/915 820/1079/911 -f 825/1083/915 826/1084/916 820/1079/911 -f 820/1079/911 826/1084/916 520/955/796 -f 826/1084/916 519/956/797 520/955/796 -f 512/639/505 821/1059/891 855/1058/890 827/640/506 -f 512/639/505 513/1085/886 821/1059/891 -f 821/1059/891 513/1085/886 822/1080/912 -f 513/1085/886 514/1086/885 822/1080/912 -f 822/1080/912 514/1086/885 823/1081/913 -f 514/1086/885 515/1087/884 823/1081/913 -f 823/1081/913 515/1087/884 824/1082/914 -f 515/1087/884 516/1088/883 824/1082/914 -f 824/1082/914 516/1088/883 825/1083/915 -f 516/1088/883 517/1089/882 825/1083/915 -f 825/1083/915 517/1089/882 826/1084/916 -f 517/1089/882 518/1090/509 826/1084/916 -f 826/1084/916 518/1090/509 519/956/797 -f 518/1090/509 506/642/508 519/956/797 -f 869/723/580 870/722/579 997/1091/917 994/1092/918 -f 919/1093/919 932/1094/920 1007/1095/921 996/1096/922 981/1097/923 980/1098/924 982/1099/925 991/1100/926 990/1101/927 995/1102/928 943/1103/929 942/1104/930 960/1105/931 959/1106/932 1006/1107/933 971/1108/934 970/1109/935 945/1110/936 946/1111/937 954/1112/938 926/1113/939 925/1114/940 1001/1115/941 989/1116/942 987/1117/943 988/1118/944 947/1119/945 940/1120/946 941/1121/947 978/1122/948 973/1123/949 974/1124/950 994/1125/918 997/1126/917 938/1127/951 939/1128/952 968/1129/953 969/1130/954 975/1131/955 944/1132/956 935/1133/957 934/1134/958 965/1135/959 918/1136/960 917/1137/961 984/1138/962 983/1139/963 979/1140/964 976/1141/965 977/1142/966 992/1143/967 993/1144/968 930/1145/969 931/1146/970 972/1147/971 966/1148/972 967/1149/973 1002/1150/974 986/1151/975 985/1152/976 964/1153/977 953/1154/978 924/1155/979 923/1156/980 1008/1157/981 958/1158/982 956/1159/983 957/1160/984 955/1161/985 929/1162/986 927/1163/987 928/1164/988 948/1165/989 1004/1166/990 1003/1167/991 1000/1168/992 936/1169/993 937/1170/994 998/1171/995 952/1172/996 950/1173/997 951/1174/998 962/1175/999 961/1176/1000 963/1177/1001 1005/1178/1002 922/1179/1003 921/1180/1004 933/1181/1005 999/1182/1006 949/1183/1007 920/1184/1008 -f 916/856/697 913/851/692 981/1185/923 996/1186/922 -f 908/1016/857 835/647/512 929/1187/986 955/1188/985 -f 895/1074/906 899/1076/908 937/1189/994 936/1190/993 -f 883/727/584 885/726/583 969/1191/954 968/1192/953 -f 894/1011/852 891/1010/851 924/1193/979 953/1194/978 -f 836/910/751 841/916/757 930/1195/969 993/1196/968 -f 897/1012/853 894/1011/852 953/1194/978 964/1197/977 -f 906/736/592 903/734/590 1001/1198/941 925/1199/940 -f 859/929/770 858/928/769 919/1200/919 920/1201/1008 -f 840/748/604 838/746/602 971/1202/934 1006/1203/933 -f 830/1018/859 831/1017/858 957/1204/984 956/1205/983 -f 878/725/582 880/724/581 939/1206/952 938/1207/951 -f 892/846/687 889/845/686 934/1208/958 935/1209/957 -f 838/746/602 829/744/600 970/1210/935 971/1202/934 -f 833/906/747 834/911/752 992/1211/967 977/1212/966 -f 900/733/589 511/732/529 987/1213/943 989/1214/942 -f 885/726/583 893/729/586 975/1215/955 969/1191/954 -f 901/847/688 879/841/682 984/1216/962 917/1217/961 -f 845/917/758 849/923/764 972/1218/971 931/1219/970 -f 857/927/768 854/857/698 1007/1220/921 932/1221/920 -f 886/1069/901 887/1071/903 952/1222/996 998/1223/995 -f 870/722/579 878/725/582 938/1207/951 997/1091/917 -f 844/708/569 847/712/572 941/1224/947 940/1225/946 -f 837/1019/860 830/1018/859 956/1205/983 958/1226/982 -f 865/757/612 864/756/611 943/1227/929 995/1228/928 -f 849/923/764 850/922/763 966/1229/972 972/1218/971 -f 909/739/595 907/737/593 926/1230/939 954/1231/938 -f 846/750/606 840/748/604 1006/1203/933 959/1232/932 -f 914/853/694 915/855/696 991/1233/926 982/1234/925 -f 834/911/752 836/910/751 993/1196/968 992/1211/967 -f 864/756/611 860/754/610 942/1235/930 943/1227/929 -f 831/1017/858 908/1016/857 955/1188/985 957/1204/984 -f 843/709/570 844/708/569 940/1225/946 947/1236/945 -f 829/744/600 828/741/597 945/1237/936 970/1210/935 -f 511/706/529 839/705/568 988/1238/944 987/1239/943 -f 872/952/793 881/950/791 922/1240/1003 1005/1241/1002 -f 871/939/780 868/937/778 949/1242/1007 999/1243/1006 -f 915/855/696 911/849/690 990/1244/927 991/1233/926 -f 861/720/577 866/721/578 974/1245/950 973/1246/949 -f 853/752/608 846/750/606 959/1232/932 960/1247/931 -f 847/712/572 848/711/571 978/1248/948 941/1224/947 -f 913/851/692 912/850/691 980/1249/924 981/1185/923 -f 832/843/684 833/906/747 977/1212/966 976/1250/965 -f 881/950/791 874/944/785 921/1251/1004 922/1240/1003 -f 889/845/686 888/844/685 965/1252/959 934/1208/958 -f 827/640/506 855/1058/890 948/1253/989 928/1254/988 -f 912/850/691 914/853/694 982/1234/925 980/1249/924 -f 896/730/587 892/846/687 935/1209/957 944/1255/956 -f 880/724/581 883/727/584 968/1192/953 939/1206/952 -f 863/1062/894 862/1061/893 961/1256/1000 962/1257/999 -f 839/705/568 843/709/570 947/1236/945 988/1238/944 -f 891/1010/851 890/1009/850 923/1258/980 924/1193/979 -f 828/741/597 910/740/596 946/1259/937 945/1237/936 -f 879/841/682 877/840/681 983/1260/963 984/1216/962 -f 868/937/778 859/929/770 920/1201/1008 949/1242/1007 -f 862/1061/893 867/1064/896 963/1261/1001 961/1256/1000 -f 899/1076/908 886/1069/901 998/1223/995 937/1189/994 -f 852/1057/889 895/1074/906 936/1190/993 1000/1262/992 -f 902/1014/855 898/1013/854 985/1263/976 986/1264/975 -f 850/922/763 884/1006/847 967/1265/973 966/1229/972 -f 888/844/685 905/848/689 918/1266/960 965/1252/959 -f 848/711/571 861/720/577 973/1246/949 978/1248/948 -f 855/1058/890 856/1060/892 1004/1267/990 948/1253/989 -f 851/1055/887 852/1057/889 1000/1262/992 1003/1268/991 -f 866/721/578 869/723/580 994/1092/918 974/1245/950 -f 903/734/590 900/733/589 989/1214/942 1001/1198/941 -f 505/641/507 827/640/506 928/1254/988 927/1269/987 -f 875/1065/897 876/1067/899 951/1270/998 950/1271/997 -f 856/1060/892 851/1055/887 1003/1268/991 1004/1267/990 -f 898/1013/854 897/1012/853 964/1197/977 985/1263/976 -f 876/1067/899 863/1062/894 962/1257/999 951/1270/998 -f 904/1015/856 902/1014/855 986/1264/975 1002/1272/974 -f 884/1006/847 904/1015/856 1002/1272/974 967/1265/973 -f 835/647/512 505/646/507 927/1273/987 929/1187/986 -f 877/840/681 882/842/683 979/1274/964 983/1260/963 -f 893/729/586 896/730/587 944/1255/956 975/1215/955 -f 841/916/757 845/917/758 931/1219/970 930/1195/969 -f 907/737/593 906/736/592 925/1199/940 926/1230/939 -f 873/943/784 871/939/780 999/1243/1006 933/1275/1005 -f 842/1008/849 837/1019/860 958/1226/982 1008/1276/981 -f 854/857/698 916/856/697 996/1186/922 1007/1220/921 -f 858/928/769 857/927/768 932/1221/920 919/1200/919 -f 867/1064/896 872/952/793 1005/1241/1002 963/1261/1001 -f 905/848/689 901/847/688 917/1217/961 918/1266/960 -f 874/944/785 873/943/784 933/1275/1005 921/1251/1004 -f 911/849/690 865/757/612 995/1228/928 990/1244/927 -f 890/1009/850 842/1008/849 1008/1276/981 923/1258/980 -f 910/740/596 909/739/595 954/1231/938 946/1259/937 -f 887/1071/903 875/1065/897 950/1271/997 952/1222/996 -f 882/842/683 832/843/684 976/1250/965 979/1274/964 -f 860/754/610 853/752/608 960/1247/931 942/1235/930 -o spot3_Icosphere.005 -v -0.451172 -0.087323 0.997964 -v -0.461508 0.001602 1.020512 -v -0.375661 -0.081159 1.050718 -v -0.417219 0.104764 1.022486 -v -0.278313 -0.029147 1.071359 -v -0.354676 0.037558 1.065873 -v -0.303999 0.085759 1.053911 -v -0.457448 -0.081228 0.999108 -v -0.463138 -0.073013 1.000850 -v -0.467580 -0.062583 1.003262 -v -0.470167 -0.050249 1.006306 -v -0.470582 -0.036755 1.009811 -v -0.468942 -0.023074 1.013513 -v -0.465716 -0.010095 1.017145 -v -0.469141 0.005224 1.014819 -v -0.476155 0.009190 1.008613 -v -0.481773 0.013306 1.002209 -v -0.485265 0.017291 0.996050 -v -0.486231 0.020852 0.990592 -v -0.386978 -0.075475 1.048340 -v -0.399529 -0.067820 1.045288 -v -0.412750 -0.058106 1.041567 -v -0.425783 -0.046625 1.037314 -v -0.437684 -0.034069 1.032794 -v -0.447739 -0.021343 1.028325 -v -0.455649 -0.009273 1.024178 -v -0.446003 -0.092262 1.003135 -v -0.439033 -0.096251 1.009331 -v -0.430179 -0.098638 1.016421 -v -0.419707 -0.098894 1.024060 -v -0.408247 -0.096848 1.031743 -v -0.396627 -0.092789 1.038957 -v -0.385600 -0.087331 1.045334 -v -0.347969 -0.112438 1.039240 -v -0.353158 -0.110065 1.042532 -v -0.358921 -0.105128 1.045434 -v -0.364835 -0.098126 1.047781 -v -0.370505 -0.089864 1.049523 -v -0.367394 0.035316 1.062484 -v -0.381852 0.032184 1.058055 -v -0.397513 0.028096 1.052572 -v -0.413448 0.023155 1.046222 -v -0.428542 0.017652 1.039397 -v -0.441847 0.011991 1.032581 -v -0.452851 0.006554 1.026199 -v -0.372735 -0.070681 1.054388 -v -0.369531 -0.057724 1.058025 -v -0.366207 -0.042441 1.061329 -v -0.362988 -0.025491 1.063977 -v -0.360112 -0.007960 1.065731 -v -0.357746 0.008945 1.066536 -v -0.355949 0.024282 1.066511 -v -0.427808 0.101499 1.016660 -v -0.438847 0.096091 1.010276 -v -0.449614 0.088214 1.003642 -v -0.461190 0.015514 1.020414 -v -0.459409 0.030862 1.020398 -v -0.455791 0.046932 1.020494 -v -0.450211 0.062658 1.020720 -v -0.442927 0.076895 1.021068 -v -0.434537 0.088797 1.021506 -v -0.425762 0.098036 1.021993 -v -0.284684 -0.037969 1.071631 -v -0.293420 -0.047245 1.071158 -v -0.304664 -0.056396 1.069715 -v -0.318103 -0.064699 1.067201 -v -0.332938 -0.071503 1.063720 -v -0.348089 -0.076454 1.059566 -v -0.362554 -0.079575 1.055119 -v -0.295939 -0.079493 1.054665 -v -0.288235 -0.067361 1.060423 -v -0.282832 -0.054312 1.065169 -v -0.279657 -0.041325 1.068787 -v -0.361489 0.049069 1.062747 -v -0.369416 0.061150 1.058609 -v -0.378217 0.073040 1.053432 -v -0.387415 0.083791 1.047382 -v -0.396383 0.092560 1.040830 -v -0.404539 0.098892 1.034243 -v -0.411519 0.102829 1.028040 -v -0.414830 0.108433 1.019412 -v -0.411512 0.110696 1.016072 -v -0.270397 -0.025345 1.065723 -v -0.273752 -0.027571 1.069050 -v -0.281886 -0.022142 1.073651 -v -0.287528 -0.013910 1.075413 -v -0.295522 -0.004674 1.076318 -v -0.305769 0.005081 1.076109 -v -0.317701 0.014700 1.074724 -v -0.330412 0.023550 1.072332 -v -0.342970 0.031217 1.069263 -v -0.314196 0.094053 1.051583 -v -0.326727 0.101754 1.048532 -v -0.341420 0.108039 1.044746 -v -0.357628 0.112123 1.040351 -v -0.374305 0.113559 1.035618 -v -0.390309 0.112427 1.030885 -v -0.404763 0.109255 1.026446 -v -0.346395 0.046536 1.066936 -v -0.337625 0.055814 1.067433 -v -0.328891 0.064760 1.067122 -v -0.320867 0.072621 1.065853 -v -0.314172 0.078760 1.063648 -v -0.309166 0.082883 1.060715 -v -0.305857 0.085093 1.057368 -v -0.295677 0.076200 1.058099 -v -0.287724 0.064153 1.062256 -v -0.280904 0.049699 1.066038 -v -0.275982 0.033413 1.069078 -v -0.273443 0.016326 1.071106 -v -0.273299 -0.000375 1.072054 -v -0.275130 -0.015715 1.072056 -v -0.297568 0.074341 1.061879 -v -0.301283 0.070761 1.065375 -v -0.289925 0.060840 1.066191 -v -0.307074 0.065227 1.068201 -v -0.294351 0.055786 1.069603 -v -0.283782 0.044881 1.069874 -v -0.314802 0.057875 1.070025 -v -0.301165 0.049000 1.072064 -v -0.289208 0.038555 1.072923 -v -0.279865 0.027357 1.072536 -v -0.323894 0.049254 1.070713 -v -0.310010 0.040891 1.073299 -v -0.297143 0.031007 1.074796 -v -0.286413 0.020278 1.074995 -v -0.278464 0.009570 1.073977 -v -0.333536 0.040130 1.070369 -v -0.320074 0.032164 1.073308 -v -0.306968 0.022827 1.075348 -v -0.295322 0.012654 1.076184 -v -0.286001 0.002401 1.075756 -v -0.279337 -0.007213 1.074263 -v -0.274477 0.038670 1.064611 -v -0.270560 0.021146 1.067272 -v -0.269390 0.003513 1.068844 -v -0.270676 -0.012994 1.069364 -v -0.351122 0.112559 1.037644 -v -0.368389 0.115457 1.032851 -v -0.385456 0.115405 1.028006 -v -0.401169 0.112806 1.023451 -v -0.317286 0.093350 1.054942 -v -0.331244 0.100673 1.051653 -v -0.321942 0.090678 1.058106 -v -0.347335 0.106148 1.047514 -v -0.337275 0.097166 1.054500 -v -0.328258 0.085649 1.060748 -v -0.364602 0.109046 1.042721 -v -0.354461 0.101461 1.049964 -v -0.344667 0.090937 1.056758 -v -0.335987 0.078298 1.062572 -v -0.381763 0.109153 1.037631 -v -0.372261 0.103039 1.044789 -v -0.362396 0.093913 1.051837 -v -0.352935 0.082271 1.058197 -v -0.344554 0.069171 1.063444 -v -0.397644 0.106839 1.032637 -v -0.389300 0.101984 1.039411 -v -0.380059 0.094344 1.046370 -v -0.370521 0.084095 1.052987 -v -0.361393 0.071997 1.058770 -v -0.353254 0.059139 1.063432 -v -0.343708 0.016436 1.069916 -v -0.345211 -0.000398 1.069763 -v -0.330732 0.007338 1.072833 -v -0.347589 -0.018494 1.068565 -v -0.332155 -0.010689 1.072313 -v -0.317897 -0.002629 1.074861 -v -0.350790 -0.036599 1.066253 -v -0.334829 -0.029413 1.070564 -v -0.319586 -0.021268 1.073796 -v -0.306251 -0.012802 1.075697 -v -0.354574 -0.053369 1.063001 -v -0.338639 -0.047375 1.067630 -v -0.322872 -0.039858 1.071423 -v -0.308558 -0.031302 1.074008 -v -0.296659 -0.022425 1.075281 -v -0.358602 -0.067823 1.059160 -v -0.343217 -0.063290 1.063817 -v -0.327497 -0.056945 1.067927 -v -0.312651 -0.049013 1.071075 -v -0.299780 -0.040083 1.073027 -v -0.289509 -0.030908 1.073810 -v -0.275496 -0.040261 1.065884 -v -0.279489 -0.053627 1.061550 -v -0.436876 0.098100 1.006173 -v -0.425707 0.104548 1.013032 -v -0.420003 0.094893 1.027896 -v -0.428616 0.084120 1.027696 -v -0.412727 0.089468 1.034443 -v -0.436667 0.070543 1.027440 -v -0.420935 0.077033 1.034469 -v -0.404081 0.081510 1.041276 -v -0.443421 0.054809 1.027139 -v -0.428454 0.061927 1.034274 -v -0.411725 0.067557 1.041377 -v -0.394542 0.071260 1.047893 -v -0.448378 0.038089 1.026816 -v -0.434621 0.045154 1.033859 -v -0.418660 0.051403 1.041068 -v -0.401608 0.056265 1.047876 -v -0.384820 0.059480 1.053802 -v -0.451437 0.021673 1.026496 -v -0.439076 0.028093 1.033269 -v -0.424344 0.034310 1.040373 -v -0.408051 0.039794 1.047291 -v -0.391407 0.044137 1.053509 -v -0.375612 0.047193 1.058691 -v -0.287135 -0.051332 1.068631 -v -0.297293 -0.061483 1.067601 -v -0.291685 -0.065227 1.064428 -v -0.310101 -0.070957 1.065511 -v -0.303390 -0.075766 1.062734 -v -0.298599 -0.078634 1.059005 -v -0.324947 -0.078889 1.062363 -v -0.317634 -0.084920 1.059997 -v -0.311729 -0.088861 1.056657 -v -0.307694 -0.090405 1.052604 -v -0.340729 -0.084691 1.058390 -v -0.333472 -0.091838 1.056355 -v -0.326973 -0.097006 1.053425 -v -0.321819 -0.099616 1.049764 -v -0.356228 -0.088248 1.053981 -v -0.349600 -0.096170 1.052148 -v -0.343138 -0.102431 1.049561 -v -0.337387 -0.106269 1.046308 -v -0.332774 -0.107401 1.042631 -v -0.437347 0.093477 1.015780 -v -0.449072 0.086500 1.009052 -v -0.446789 0.082636 1.015001 -v -0.459982 0.076946 1.002223 -v -0.458691 0.073951 1.008091 -v -0.455301 0.069021 1.014422 -v -0.469078 0.065175 0.995822 -v -0.469122 0.062871 1.001279 -v -0.466718 0.058803 1.007542 -v -0.462055 0.053288 1.014121 -v -0.477120 0.050100 0.995122 -v -0.476057 0.046717 1.000970 -v -0.472377 0.042072 1.007482 -v -0.466551 0.036605 1.014120 -v -0.482270 0.036718 0.990032 -v -0.482587 0.033708 0.995220 -v -0.480246 0.029746 1.001320 -v -0.475421 0.025126 1.007878 -v -0.468781 0.020257 1.014379 -v -0.369713 0.020801 1.062698 -v -0.385226 0.016484 1.057708 -v -0.372458 0.004175 1.062200 -v -0.401713 0.011439 1.051597 -v -0.388765 -0.001188 1.056600 -v -0.375528 -0.013804 1.060810 -v -0.418006 0.005955 1.044679 -v -0.405644 -0.006824 1.049910 -v -0.392202 -0.019783 1.054640 -v -0.378728 -0.031909 1.058499 -v -0.432894 0.000441 1.037468 -v -0.421778 -0.012299 1.042583 -v -0.408930 -0.025413 1.047537 -v -0.395250 -0.037873 1.051917 -v -0.381821 -0.048796 1.055438 -v -0.445538 -0.004719 1.030503 -v -0.436016 -0.017216 1.035214 -v -0.424407 -0.030255 1.040056 -v -0.411314 -0.042877 1.044663 -v -0.397711 -0.054143 1.048691 -v -0.384607 -0.063458 1.051942 -v -0.380766 -0.096506 1.043562 -v -0.392149 -0.101996 1.036558 -v -0.375309 -0.104823 1.041232 -v -0.404035 -0.105506 1.028782 -v -0.386888 -0.109816 1.033675 -v -0.369501 -0.111303 1.038367 -v -0.415551 -0.106446 1.020737 -v -0.398838 -0.112249 1.025518 -v -0.381109 -0.115225 1.030439 -v -0.363750 -0.115142 1.035115 -v -0.425842 -0.104747 1.013019 -v -0.410222 -0.111721 1.017373 -v -0.392933 -0.116190 1.022179 -v -0.375235 -0.117593 1.027084 -v -0.358484 -0.116053 1.031715 -v -0.434360 -0.100882 1.006120 -v -0.420233 -0.108490 1.009847 -v -0.398305 -0.081186 1.042302 -v -0.412063 -0.072717 1.038591 -v -0.410526 -0.085943 1.035237 -v -0.426031 -0.061922 1.034306 -v -0.425069 -0.076463 1.030932 -v -0.422878 -0.089046 1.027428 -v -0.439123 -0.049301 1.029699 -v -0.439151 -0.064523 1.026269 -v -0.437532 -0.078448 1.022738 -v -0.434394 -0.089986 1.019383 -v -0.450368 -0.035790 1.025114 -v -0.451597 -0.050889 1.021598 -v -0.450975 -0.065488 1.018008 -v -0.448403 -0.078368 1.014630 -v -0.444218 -0.088694 1.011699 -v -0.459235 -0.022446 1.020864 -v -0.461599 -0.036707 1.017267 -v -0.462155 -0.051181 1.013608 -v -0.460579 -0.064743 1.010170 -v -0.456987 -0.076385 1.007206 -v -0.451899 -0.085561 1.004860 -v -0.487319 0.008149 0.986385 -v -0.487612 -0.009258 0.987545 -v -0.487765 0.004413 0.991480 -v -0.485071 -0.027365 0.989760 -v -0.486690 -0.013441 0.993291 -v -0.485424 0.000451 0.997580 -v -0.474321 -0.056348 0.990816 -v -0.479675 -0.044766 0.992936 -v -0.482668 -0.031434 0.996023 -v -0.482869 -0.017287 0.999904 -v -0.480470 -0.003444 1.004231 -v -0.466264 -0.070125 0.994895 -v -0.472038 -0.060212 0.996765 -v -0.476013 -0.048159 0.999463 -v -0.477589 -0.034597 1.002902 -v -0.476650 -0.020534 1.006826 -v -0.473601 -0.007010 1.010898 -v -0.458929 -0.078722 0.995250 -v -0.350478 0.112308 1.037316 -v -0.352707 0.112853 1.036510 -v -0.422258 -0.107215 1.008243 -v -0.431699 -0.101978 1.004870 -v -0.295939 -0.079493 1.054665 -v -0.306713 -0.090117 1.050596 -v -0.316312 -0.097932 1.046993 -v -0.442365 -0.094690 1.001078 -v -0.318655 -0.099660 1.046116 -v -0.419104 -0.108684 1.009374 -v -0.366078 0.115145 1.031666 -v -0.296243 0.077157 1.056625 -v -0.374446 0.115757 1.028623 -v -0.321713 -0.101491 1.044977 -v -0.410326 -0.111939 1.012531 -v -0.293191 0.073394 1.057688 -v -0.288375 0.066578 1.059354 -v -0.331608 -0.106947 1.041297 -v -0.381869 0.115810 1.025917 -v -0.282546 0.057094 1.061354 -v -0.280860 0.053897 1.061926 -v -0.339649 -0.110470 1.038319 -v -0.344545 -0.112297 1.036510 -v -0.472626 -0.058659 0.990521 -v -0.474312 -0.055462 0.989949 -v -0.395674 0.114571 1.020868 -v -0.459233 0.077928 0.997210 -v -0.461981 -0.074959 0.994187 -v -0.466797 -0.068142 0.992521 -v -0.459233 0.077928 0.997210 -v -0.459233 0.077928 0.997210 -v -0.468094 0.065460 0.993814 -v -0.396993 0.114374 1.020384 -v -0.276949 0.044920 1.063233 -v -0.487127 0.008372 0.986120 -v -0.487181 0.006955 0.986082 -v -0.400345 0.113553 1.019151 -v -0.410627 0.110732 1.015365 -v -0.274583 0.039143 1.064019 -v -0.486691 0.012006 0.986327 -v -0.474447 0.054515 0.991354 -v -0.273853 0.036835 1.064255 -v -0.270387 0.022895 1.065335 -v -0.475829 0.051862 0.990815 -v -0.485074 0.023178 0.987065 -v -0.477237 0.048440 0.990256 -v -0.481356 0.037415 0.988608 -v -0.486186 -0.015654 0.986146 -v -0.486971 -0.007794 0.985963 -v -0.295939 -0.079493 1.054665 -v -0.268986 0.014089 1.065729 -v -0.287077 -0.067025 1.058060 -v -0.268200 0.006229 1.065911 -v -0.483852 0.028547 0.987581 -v -0.295939 -0.079493 1.054665 -v -0.267991 -0.008520 1.065793 -v -0.354826 -0.115117 1.032724 -v -0.268045 -0.009937 1.065754 -v -0.481319 -0.038399 0.987620 -v -0.484784 -0.024460 0.986540 -v -0.277934 -0.050005 1.061619 -v -0.273816 -0.038980 1.063266 -v -0.404693 -0.113873 1.014559 -v -0.402464 -0.114418 1.015365 -v -0.271319 -0.030111 1.064294 -v -0.268480 -0.013570 1.065548 -v -0.389093 -0.116709 1.020209 -v -0.478223 -0.046484 0.988642 -v -0.270097 -0.024743 1.064810 -v -0.380725 -0.117322 1.023252 -v -0.373302 -0.117375 1.025958 -v -0.480588 -0.040707 0.987855 -v -0.312806 0.093125 1.050797 -v -0.280725 -0.056080 1.060521 -v -0.359497 -0.116136 1.031007 -v -0.316625 0.095986 1.049443 -v -0.358178 -0.115939 1.031491 -v -0.279342 -0.053427 1.061060 -v -0.323473 0.100413 1.047005 -v -0.332913 0.105650 1.043632 -v -0.438546 -0.097551 1.002432 -v -0.336067 0.107119 1.042501 -v -0.344845 0.110374 1.039344 -v -0.415522 0.108906 1.013556 -v -0.436517 0.098095 1.005759 -v -0.438860 0.096368 1.004882 -v -0.433459 0.099926 1.006898 -v -0.423563 0.105382 1.010578 -v -0.448459 0.088552 1.001279 -v -0.238586 -0.054552 0.944944 -v -0.237204 -0.051898 0.945483 -v -0.417094 0.079456 0.881633 -v -0.425955 0.066989 0.878238 -v -0.439217 0.038943 0.873032 -v -0.441713 0.030075 0.872004 -v -0.362554 -0.112345 0.898982 -v -0.360325 -0.112890 0.899788 -v -0.281334 0.101942 0.931428 -v -0.290774 0.107178 0.928055 -v -0.409033 -0.085795 0.882387 -v -0.416790 -0.077194 0.879673 -v -0.400227 -0.093162 0.885501 -v -0.279574 -0.099963 0.929400 -v -0.289470 -0.105419 0.925720 -v -0.417094 0.079456 0.881633 -v -0.435099 0.049969 0.874679 -v -0.231677 -0.037452 0.947689 -v -0.229181 -0.028583 0.948717 -v -0.436084 -0.044956 0.873065 -v -0.438449 -0.039179 0.872279 -v -0.226847 0.015617 0.950152 -v -0.226062 0.007757 0.950334 -v -0.246236 0.068106 0.943777 -v -0.240407 0.058622 0.945777 -v -0.354854 0.115902 0.904807 -v -0.358207 0.115081 0.903574 -v -0.227958 -0.023215 0.949233 -v -0.308339 0.113836 0.921739 -v -0.302706 0.111902 0.923767 -v -0.251052 0.074922 0.942111 -v -0.419842 -0.073431 0.878610 -v -0.432308 0.056044 0.875777 -v -0.444047 -0.014126 0.870569 -v -0.444832 -0.006266 0.870387 -v -0.442646 -0.022931 0.870963 -v -0.346954 -0.115181 0.904632 -v -0.293929 0.108648 0.926924 -v -0.396407 -0.096023 0.886855 -v -0.380120 -0.105686 0.892666 -v -0.389560 -0.100450 0.889293 -v -0.376965 -0.107156 0.893797 -v -0.339731 0.117339 0.910340 -v -0.353536 0.116099 0.905291 -v -0.444988 0.009901 0.870544 -v -0.445042 0.008484 0.870505 -v -0.444553 0.013534 0.870750 -v -0.338586 -0.115794 0.907675 -v -0.235796 -0.048477 0.946042 -v -0.302406 -0.110769 0.920933 -v -0.312687 -0.113589 0.917147 -v -0.225852 -0.006992 0.950216 -v -0.225906 -0.008409 0.950177 -v -0.310569 0.114382 0.920933 -v -0.323939 0.116673 0.916089 -v -0.297510 -0.108942 0.922742 -v -0.234810 0.046448 0.947656 -v -0.232445 0.040671 0.948442 -v -0.226341 -0.012042 0.949971 -v -0.253800 -0.077965 0.939088 -v -0.264574 -0.088589 0.935019 -v -0.238721 0.055426 0.946349 -v -0.253800 -0.077965 0.939088 -v -0.394378 0.099623 0.890182 -v -0.396721 0.097896 0.889305 -v -0.391320 0.101454 0.891321 -v -0.253800 -0.077965 0.939088 -v -0.244939 -0.065497 0.942483 -v -0.331163 -0.115847 0.910381 -v -0.317358 -0.114608 0.915430 -v -0.261861 0.087287 0.938334 -v -0.254104 0.078685 0.941048 -v -0.270668 0.094653 0.935220 -v -0.373384 0.110434 0.897979 -v -0.381424 0.106910 0.895001 -v -0.274173 -0.096404 0.931416 -v -0.276516 -0.098132 0.930539 -v -0.231714 0.038363 0.948678 -v -0.368488 0.112261 0.899788 -v -0.406320 0.090081 0.885702 -v -0.228248 0.024423 0.949758 -v -0.439180 -0.036871 0.872043 -v -0.433690 0.053390 0.875238 -v -0.432173 -0.053934 0.874372 -v -0.274487 0.097514 0.933866 -v -0.316040 -0.114410 0.915914 -v -0.430487 -0.057131 0.874944 -v -0.424658 -0.066614 0.876944 -v -0.442936 0.024707 0.871488 -v -0.332308 0.117285 0.913046 -v -0.417094 0.079456 0.881633 -v -0.368188 -0.110410 0.896954 -vt 0.193183 0.019683 -vt 0.176737 0.019683 -vt 0.181819 0.000000 -vt 0.272728 0.157461 -vt 0.284092 0.137778 -vt 0.295455 0.157461 -vt 0.534091 0.019683 -vt 0.545455 0.000000 -vt 0.550537 0.019683 -vt 0.284092 0.177143 -vt 0.454546 0.157461 -vt 0.477273 0.157461 -vt 0.465910 0.177143 -vt 0.261364 0.177143 -vt 0.443182 0.177143 -vt 0.181819 0.314921 -vt 0.204546 0.314921 -vt 0.193183 0.334604 -vt 0.545455 0.314921 -vt 0.568182 0.314921 -vt 0.556819 0.334604 -vt 0.363637 0.314921 -vt 0.386364 0.314921 -vt 0.375001 0.334604 -vt 0.465910 0.452699 -vt 0.454546 0.472382 -vt 0.443182 0.452699 -vt 0.431819 0.433017 -vt 0.454546 0.433017 -vt 0.420455 0.413334 -vt 0.443182 0.413334 -vt 0.409092 0.393652 -vt 0.431819 0.393652 -vt 0.397728 0.373969 -vt 0.420455 0.373969 -vt 0.386364 0.354286 -vt 0.409091 0.354286 -vt 0.397728 0.334604 -vt 0.477273 0.433017 -vt 0.465910 0.413334 -vt 0.488637 0.413334 -vt 0.454546 0.393652 -vt 0.477273 0.393652 -vt 0.500000 0.393652 -vt 0.443182 0.373969 -vt 0.465910 0.373969 -vt 0.488637 0.373969 -vt 0.511364 0.373969 -vt 0.431819 0.354286 -vt 0.454546 0.354286 -vt 0.477273 0.354286 -vt 0.500000 0.354286 -vt 0.522728 0.354286 -vt 0.420455 0.334604 -vt 0.443182 0.334604 -vt 0.465910 0.334604 -vt 0.488637 0.334604 -vt 0.511364 0.334604 -vt 0.534091 0.334604 -vt 0.409091 0.314921 -vt 0.431819 0.314921 -vt 0.454546 0.314921 -vt 0.477273 0.314921 -vt 0.500000 0.314921 -vt 0.522728 0.314921 -vt 0.625000 0.452699 -vt 0.631282 0.452699 -vt 0.636364 0.472382 -vt 0.613637 0.433017 -vt 0.626138 0.433017 -vt 0.629174 0.445471 -vt 0.602273 0.413334 -vt 0.621040 0.413334 -vt 0.622198 0.418189 -vt 0.590910 0.393652 -vt 0.613637 0.393652 -vt 0.579546 0.373969 -vt 0.602273 0.373969 -vt 0.568182 0.354286 -vt 0.590909 0.354286 -vt 0.579546 0.334604 -vt 0.618044 0.401285 -vt 0.616008 0.393652 -vt 0.610813 0.373969 -vt 0.615257 0.390844 -vt 0.605676 0.354286 -vt 0.608234 0.363644 -vt 0.600724 0.334604 -vt 0.601248 0.336379 -vt 0.590909 0.314921 -vt 0.599469 0.329747 -vt 0.595581 0.314921 -vt 0.102274 0.452699 -vt 0.090910 0.472382 -vt 0.095992 0.452699 -vt 0.098100 0.445471 -vt 0.113637 0.433017 -vt 0.101136 0.433017 -vt 0.105076 0.418189 -vt 0.125001 0.413334 -vt 0.106234 0.413334 -vt 0.109230 0.401285 -vt 0.111266 0.393652 -vt 0.113637 0.393652 -vt 0.136365 0.393652 -vt 0.112016 0.390844 -vt 0.125001 0.373969 -vt 0.116461 0.373969 -vt 0.147728 0.373969 -vt 0.119039 0.363644 -vt 0.136364 0.354286 -vt 0.121598 0.354286 -vt 0.159092 0.354286 -vt 0.126026 0.336379 -vt 0.147728 0.334604 -vt 0.126550 0.334604 -vt 0.170455 0.334604 -vt 0.127804 0.329747 -vt 0.131693 0.314921 -vt 0.136364 0.314921 -vt 0.159092 0.314921 -vt 0.272728 0.472382 -vt 0.261364 0.452699 -vt 0.284092 0.452699 -vt 0.250001 0.433017 -vt 0.272728 0.433017 -vt 0.238637 0.413334 -vt 0.261364 0.413334 -vt 0.227273 0.393652 -vt 0.250001 0.393652 -vt 0.215910 0.373969 -vt 0.238637 0.373969 -vt 0.204546 0.354286 -vt 0.227273 0.354286 -vt 0.215910 0.334604 -vt 0.295455 0.433017 -vt 0.284092 0.413334 -vt 0.306819 0.413334 -vt 0.272728 0.393652 -vt 0.295455 0.393652 -vt 0.318182 0.393652 -vt 0.261364 0.373969 -vt 0.284092 0.373969 -vt 0.306819 0.373969 -vt 0.329546 0.373969 -vt 0.250001 0.354286 -vt 0.272728 0.354286 -vt 0.295455 0.354286 -vt 0.318182 0.354286 -vt 0.340910 0.354286 -vt 0.238637 0.334604 -vt 0.261364 0.334604 -vt 0.284092 0.334604 -vt 0.306819 0.334604 -vt 0.329546 0.334604 -vt 0.352273 0.334604 -vt 0.227274 0.314921 -vt 0.250001 0.314921 -vt 0.272728 0.314921 -vt 0.295455 0.314921 -vt 0.318183 0.314921 -vt 0.340910 0.314921 -vt 0.375001 0.295238 -vt 0.386364 0.275556 -vt 0.397728 0.295238 -vt 0.397728 0.255874 -vt 0.409092 0.275556 -vt 0.409092 0.236191 -vt 0.420455 0.255874 -vt 0.420455 0.216509 -vt 0.431819 0.236191 -vt 0.431819 0.196826 -vt 0.443182 0.216509 -vt 0.454546 0.196826 -vt 0.420455 0.295238 -vt 0.431819 0.275556 -vt 0.443182 0.295238 -vt 0.443182 0.255874 -vt 0.454546 0.275556 -vt 0.465910 0.295238 -vt 0.454546 0.236191 -vt 0.465910 0.255874 -vt 0.477273 0.275556 -vt 0.488637 0.295238 -vt 0.465910 0.216509 -vt 0.477273 0.236191 -vt 0.488637 0.255874 -vt 0.500000 0.275556 -vt 0.511364 0.295238 -vt 0.477273 0.196826 -vt 0.488637 0.216508 -vt 0.500000 0.236191 -vt 0.511364 0.255874 -vt 0.522728 0.275556 -vt 0.534091 0.295238 -vt 0.556819 0.295238 -vt 0.568182 0.275556 -vt 0.579546 0.295238 -vt 0.579546 0.255874 -vt 0.590910 0.275556 -vt 0.590910 0.236191 -vt 0.590910 0.236191 -vt 0.591833 0.255874 -vt 0.590910 0.236191 -vt 0.590910 0.236191 -vt 0.593592 0.280202 -vt 0.594268 0.295238 -vt 0.595096 0.307670 -vt 0.593036 0.271873 -vt 0.593228 0.275556 -vt 0.132178 0.307670 -vt 0.134046 0.275556 -vt 0.134238 0.271873 -vt 0.136364 0.275556 -vt 0.133682 0.280202 -vt 0.147728 0.295238 -vt 0.133006 0.295238 -vt 0.135441 0.255874 -vt 0.136365 0.236191 -vt 0.147728 0.255874 -vt 0.159092 0.275556 -vt 0.170455 0.295238 -vt 0.193183 0.295238 -vt 0.204546 0.275556 -vt 0.215910 0.295238 -vt 0.215910 0.255874 -vt 0.227274 0.275556 -vt 0.227273 0.236191 -vt 0.238637 0.255874 -vt 0.238637 0.216509 -vt 0.250001 0.236191 -vt 0.250001 0.196826 -vt 0.261364 0.216509 -vt 0.272728 0.196826 -vt 0.238637 0.295238 -vt 0.250001 0.275556 -vt 0.261364 0.295238 -vt 0.261364 0.255874 -vt 0.272728 0.275556 -vt 0.284092 0.295238 -vt 0.272728 0.236191 -vt 0.284092 0.255874 -vt 0.295455 0.275556 -vt 0.306819 0.295238 -vt 0.284092 0.216509 -vt 0.295455 0.236191 -vt 0.306819 0.255874 -vt 0.318182 0.275556 -vt 0.329546 0.295238 -vt 0.295455 0.196826 -vt 0.306819 0.216508 -vt 0.318182 0.236191 -vt 0.329546 0.255874 -vt 0.340910 0.275556 -vt 0.352273 0.295238 -vt 0.545455 0.275556 -vt 0.534091 0.255874 -vt 0.522728 0.236191 -vt 0.511364 0.216508 -vt 0.500000 0.196826 -vt 0.488637 0.177143 -vt 0.556819 0.255874 -vt 0.545455 0.236191 -vt 0.568182 0.236191 -vt 0.534091 0.216508 -vt 0.556819 0.216508 -vt 0.579546 0.216508 -vt 0.589986 0.216509 -vt 0.522728 0.196826 -vt 0.545455 0.196826 -vt 0.568182 0.196826 -vt 0.588591 0.196826 -vt 0.588783 0.200509 -vt 0.511364 0.177143 -vt 0.534091 0.177143 -vt 0.556819 0.177143 -vt 0.579546 0.177143 -vt 0.588227 0.192180 -vt 0.587551 0.177143 -vt 0.500000 0.157461 -vt 0.522728 0.157461 -vt 0.545455 0.157461 -vt 0.568182 0.157461 -vt 0.586238 0.157461 -vt 0.586723 0.164712 -vt 0.181819 0.275556 -vt 0.170455 0.255874 -vt 0.159092 0.236191 -vt 0.136365 0.236191 -vt 0.136365 0.236191 -vt 0.137288 0.216508 -vt 0.147728 0.216508 -vt 0.193183 0.255874 -vt 0.181819 0.236191 -vt 0.204546 0.236191 -vt 0.170455 0.216508 -vt 0.193183 0.216508 -vt 0.215910 0.216508 -vt 0.138491 0.200509 -vt 0.159092 0.196826 -vt 0.138683 0.196826 -vt 0.181819 0.196826 -vt 0.204546 0.196826 -vt 0.227273 0.196826 -vt 0.139047 0.192180 -vt 0.139723 0.177143 -vt 0.147728 0.177143 -vt 0.170455 0.177143 -vt 0.193183 0.177143 -vt 0.215910 0.177143 -vt 0.238637 0.177143 -vt 0.140551 0.164712 -vt 0.159092 0.157461 -vt 0.141036 0.157461 -vt 0.181819 0.157461 -vt 0.204546 0.157461 -vt 0.227273 0.157461 -vt 0.250001 0.157461 -vt 0.363637 0.275556 -vt 0.352273 0.255874 -vt 0.340910 0.236191 -vt 0.329546 0.216508 -vt 0.318182 0.196826 -vt 0.306819 0.177143 -vt 0.375001 0.255874 -vt 0.363637 0.236191 -vt 0.386364 0.236191 -vt 0.352273 0.216508 -vt 0.375001 0.216508 -vt 0.397728 0.216508 -vt 0.340910 0.196826 -vt 0.363637 0.196826 -vt 0.386364 0.196826 -vt 0.409092 0.196826 -vt 0.329546 0.177143 -vt 0.352273 0.177143 -vt 0.375001 0.177143 -vt 0.397728 0.177143 -vt 0.420455 0.177143 -vt 0.318182 0.157461 -vt 0.340910 0.157461 -vt 0.363637 0.157461 -vt 0.386364 0.157461 -vt 0.409092 0.157461 -vt 0.431819 0.157461 -vt 0.465910 0.137778 -vt 0.477273 0.118096 -vt 0.488637 0.137778 -vt 0.488637 0.098413 -vt 0.500000 0.118096 -vt 0.500000 0.078731 -vt 0.511364 0.098413 -vt 0.511364 0.059048 -vt 0.522728 0.078731 -vt 0.522728 0.039365 -vt 0.534091 0.059048 -vt 0.545455 0.039365 -vt 0.511364 0.137778 -vt 0.522728 0.118096 -vt 0.534091 0.137778 -vt 0.534091 0.098413 -vt 0.545455 0.118096 -vt 0.556819 0.137778 -vt 0.545455 0.078731 -vt 0.556819 0.098413 -vt 0.568182 0.118096 -vt 0.579546 0.137778 -vt 0.582350 0.142635 -vt 0.556819 0.059048 -vt 0.563775 0.071096 -vt 0.565811 0.078731 -vt 0.566562 0.081538 -vt 0.571006 0.098413 -vt 0.573585 0.108738 -vt 0.576143 0.118096 -vt 0.580571 0.136003 -vt 0.581095 0.137778 -vt 0.552645 0.026911 -vt 0.555681 0.039365 -vt 0.559621 0.054193 -vt 0.560779 0.059048 -vt 0.443182 0.137778 -vt 0.420455 0.137778 -vt 0.397728 0.137778 -vt 0.375001 0.137778 -vt 0.352273 0.137778 -vt 0.329546 0.137778 -vt 0.306819 0.137778 -vt 0.431819 0.118096 -vt 0.409091 0.118096 -vt 0.420455 0.098413 -vt 0.386364 0.118096 -vt 0.397728 0.098413 -vt 0.409092 0.078731 -vt 0.363637 0.118096 -vt 0.375001 0.098413 -vt 0.386364 0.078731 -vt 0.397728 0.059048 -vt 0.340910 0.118096 -vt 0.352273 0.098413 -vt 0.363637 0.078731 -vt 0.375001 0.059048 -vt 0.386364 0.039365 -vt 0.318182 0.118096 -vt 0.329546 0.098413 -vt 0.340910 0.078731 -vt 0.352273 0.059048 -vt 0.363637 0.039365 -vt 0.375001 0.019683 -vt 0.295455 0.118096 -vt 0.306819 0.098413 -vt 0.318182 0.078731 -vt 0.329546 0.059048 -vt 0.340910 0.039365 -vt 0.352273 0.019683 -vt 0.363637 0.000000 -vt 0.167653 0.054193 -vt 0.170455 0.059048 -vt 0.166495 0.059048 -vt 0.174629 0.026911 -vt 0.181819 0.039365 -vt 0.171593 0.039365 -vt 0.146178 0.137778 -vt 0.146703 0.136003 -vt 0.147728 0.137778 -vt 0.144924 0.142635 -vt 0.153689 0.108738 -vt 0.159092 0.118096 -vt 0.151131 0.118096 -vt 0.170455 0.137778 -vt 0.160712 0.081537 -vt 0.170455 0.098413 -vt 0.156267 0.098413 -vt 0.181819 0.118096 -vt 0.193183 0.137778 -vt 0.163499 0.071097 -vt 0.181819 0.078731 -vt 0.161463 0.078731 -vt 0.193183 0.098413 -vt 0.204546 0.118096 -vt 0.215910 0.137778 -vt 0.193183 0.059048 -vt 0.204546 0.078731 -vt 0.215910 0.098413 -vt 0.227273 0.118096 -vt 0.238637 0.137778 -vt 0.204546 0.039365 -vt 0.215910 0.059048 -vt 0.227274 0.078731 -vt 0.238637 0.098413 -vt 0.250001 0.118096 -vt 0.261364 0.137778 -vt 0.610813 0.373969 -vt 0.615257 0.390844 -vt 0.500000 0.750000 -vt 0.500000 0.750000 -vt 0.500000 0.750000 -vt 0.516677 0.748536 -vt 0.530226 0.746051 -vt 0.533390 0.745316 -vt 0.537183 0.744053 -vt 0.549086 0.739630 -vt 0.558035 0.735386 -vt 0.563231 0.732574 -vt 0.573337 0.725942 -vt 0.576554 0.723709 -vt 0.577720 0.722741 -vt 0.589267 0.712013 -vt 0.594905 0.705729 -vt 0.600822 0.698251 -vt 0.609235 0.685363 -vt 0.610508 0.683097 -vt 0.613283 0.676974 -vt 0.617416 0.667261 -vt 0.618664 0.663555 -vt 0.621731 0.651867 -vt 0.623457 0.642939 -vt 0.624108 0.637679 -vt 0.625000 0.625000 -vt 0.624108 0.612321 -vt 0.623457 0.607061 -vt 0.621731 0.598133 -vt 0.618664 0.586444 -vt 0.617416 0.582739 -vt 0.613283 0.573026 -vt 0.610508 0.566903 -vt 0.609235 0.564637 -vt 0.600822 0.551749 -vt 0.594905 0.544271 -vt 0.589267 0.537987 -vt 0.577720 0.527259 -vt 0.576554 0.526291 -vt 0.573337 0.524058 -vt 0.563231 0.517426 -vt 0.558035 0.514614 -vt 0.549086 0.510370 -vt 0.537183 0.505947 -vt 0.533390 0.504684 -vt 0.530226 0.503949 -vt 0.516677 0.501464 -vt 0.500000 0.500000 -vt 0.500000 0.500000 -vt 0.500000 0.500000 -vt 0.483323 0.501464 -vt 0.469774 0.503949 -vt 0.466610 0.504684 -vt 0.462817 0.505947 -vt 0.450914 0.510370 -vt 0.441965 0.514614 -vt 0.436769 0.517426 -vt 0.426663 0.524058 -vt 0.423446 0.526291 -vt 0.422280 0.527259 -vt 0.410733 0.537987 -vt 0.405095 0.544271 -vt 0.399178 0.551749 -vt 0.390765 0.564637 -vt 0.389492 0.566903 -vt 0.386717 0.573026 -vt 0.382584 0.582739 -vt 0.381336 0.586444 -vt 0.378269 0.598133 -vt 0.376543 0.607061 -vt 0.375892 0.612321 -vt 0.375000 0.625000 -vt 0.375892 0.637679 -vt 0.376543 0.642939 -vt 0.378269 0.651867 -vt 0.381336 0.663556 -vt 0.382584 0.667261 -vt 0.386717 0.676974 -vt 0.389492 0.683097 -vt 0.390765 0.685363 -vt 0.399178 0.698251 -vt 0.405095 0.705729 -vt 0.410733 0.712013 -vt 0.422280 0.722741 -vt 0.423446 0.723709 -vt 0.426663 0.725942 -vt 0.436769 0.732574 -vt 0.441965 0.735386 -vt 0.450914 0.739630 -vt 0.462817 0.744053 -vt 0.466610 0.745315 -vt 0.469774 0.746051 -vt 0.483323 0.748536 -vt 0.134238 0.271873 -vt 0.135441 0.255874 -vt 0.550537 0.019683 -vt 0.552645 0.026911 -vt 0.161463 0.078731 -vt 0.163499 0.071097 -vt 0.600724 0.334604 -vt 0.601248 0.336379 -vt 0.566562 0.081538 -vt 0.571006 0.098413 -vt 0.588227 0.192180 -vt 0.588591 0.196826 -vt 0.573585 0.108738 -vt 0.098100 0.445471 -vt 0.101136 0.433017 -vt 0.136365 0.236191 -vt 0.137288 0.216508 -vt 0.116461 0.373969 -vt 0.119039 0.363644 -vt 0.555681 0.039365 -vt 0.559621 0.054193 -vt 0.605676 0.354286 -vt 0.608234 0.363644 -vt 0.594268 0.295238 -vt 0.595096 0.307670 -vt 0.112016 0.390844 -vt 0.588783 0.200509 -vt 0.589986 0.216508 -vt 0.090910 0.472382 -vt 0.095992 0.452699 -vt 0.599469 0.329747 -vt 0.591833 0.255874 -vt 0.593036 0.271873 -vt 0.586723 0.164712 -vt 0.587551 0.177143 -vt 0.136365 0.236191 -vt 0.136365 0.236191 -vt 0.156267 0.098413 -vt 0.160712 0.081537 -vt 0.622198 0.418189 -vt 0.626138 0.433017 -vt 0.560779 0.059048 -vt 0.127804 0.329747 -vt 0.131693 0.314921 -vt 0.586238 0.157461 -vt 0.105076 0.418189 -vt 0.106234 0.413334 -vt 0.121598 0.354286 -vt 0.133006 0.295238 -vt 0.133682 0.280202 -vt 0.126550 0.334604 -vt 0.629174 0.445471 -vt 0.111266 0.393652 -vt 0.631282 0.452699 -vt 0.636364 0.472382 -vt 0.140551 0.164712 -vt 0.141036 0.157461 -vt 0.138491 0.200509 -vt 0.138683 0.196826 -vt 0.132178 0.307670 -vt 0.616008 0.393652 -vt 0.618044 0.401285 -vt 0.126026 0.336379 -vt 0.621040 0.413334 -vt 0.134046 0.275556 -vt 0.590910 0.236191 -vt 0.139723 0.177143 -vt 0.593592 0.280202 -vt 0.174629 0.026911 -vt 0.176737 0.019683 -vt 0.595581 0.314921 -vt 0.146178 0.137778 -vt 0.146703 0.136003 -vt 0.565811 0.078731 -vt 0.109230 0.401285 -vt 0.590910 0.236191 -vt 0.144924 0.142635 -vt 0.166495 0.059048 -vt 0.576143 0.118096 -vt 0.580571 0.136003 -vt 0.582350 0.142635 -vt 0.593228 0.275556 -vt 0.171593 0.039365 -vt 0.167653 0.054193 -vt 0.181819 0.000000 -vt 0.151131 0.118096 -vt 0.153689 0.108738 -vt 0.581095 0.137778 -vt 0.545455 0.000000 -vt 0.590910 0.236191 -vt 0.139047 0.192180 -vt 0.563775 0.071096 -vn -0.7526 -0.6561 0.0563 -vn -0.7185 -0.6654 -0.2025 -vn -0.6623 -0.7367 -0.1361 -vn -0.5741 -0.0035 0.8188 -vn -0.6025 -0.0438 0.7969 -vn -0.5438 -0.0378 0.8383 -vn -0.6549 -0.7502 0.0907 -vn -0.5740 -0.8047 -0.1516 -vn -0.5301 0.0110 0.8478 -vn -0.2988 -0.2689 0.9156 -vn -0.2727 -0.3310 0.9033 -vn -0.2544 -0.2674 0.9293 -vn -0.5763 0.0428 0.8161 -vn -0.2940 -0.2166 0.9309 -vn -0.4744 0.6525 0.5909 -vn -0.4361 0.5375 0.7218 -vn -0.3914 0.7057 0.5905 -vn 0.3979 -0.1884 0.8978 -vn 0.6373 -0.2094 0.7416 -vn 0.4502 -0.1056 0.8866 -vn -0.2518 0.0904 0.9635 -vn -0.2169 0.0753 0.9733 -vn -0.2214 0.1234 0.9673 -vn 0.6605 0.6517 0.3730 -vn 0.5929 0.7350 0.3288 -vn 0.4452 0.6761 0.5871 -vn 0.2359 0.5397 0.8081 -vn 0.4396 0.5530 0.7078 -vn 0.0678 0.4033 0.9125 -vn 0.2212 0.4164 0.8818 -vn -0.0482 0.2979 0.9534 -vn 0.0587 0.2995 0.9523 -vn -0.1269 0.2213 0.9669 -vn -0.0509 0.2144 0.9754 -vn -0.1817 0.1653 0.9693 -vn -0.1253 0.1536 0.9801 -vn -0.1777 0.1094 0.9780 -vn 0.6698 0.5359 0.5139 -vn 0.4193 0.4194 0.8051 -vn 0.6558 0.3980 0.6415 -vn 0.2015 0.2980 0.9330 -vn 0.3890 0.2853 0.8759 -vn 0.6189 0.2522 0.7439 -vn 0.0482 0.2047 0.9776 -vn 0.1804 0.1885 0.9654 -vn 0.3531 0.1608 0.9217 -vn 0.5661 0.1143 0.8164 -vn -0.0543 0.1391 0.9888 -vn 0.0381 0.1191 0.9921 -vn 0.1594 0.0910 0.9830 -vn 0.3157 0.0515 0.9474 -vn 0.5073 -0.0060 0.8617 -vn -0.1245 0.0926 0.9879 -vn -0.0573 0.0710 0.9958 -vn 0.0288 0.0431 0.9986 -vn 0.1395 0.0063 0.9902 -vn 0.2799 -0.0414 0.9591 -vn -0.1754 0.0575 0.9828 -vn -0.1244 0.0358 0.9916 -vn -0.0608 0.0088 0.9981 -vn 0.0193 -0.0250 0.9995 -vn 0.1203 -0.0674 0.9904 -vn 0.2460 -0.1201 0.9618 -vn 0.7044 0.6764 0.2149 -vn 0.7562 0.5788 0.3050 -vn 0.7318 0.6145 0.2946 -vn 0.7961 0.4579 0.3956 -vn 0.7739 0.5008 0.3875 -vn 0.8674 0.3347 0.3681 -vn 0.8454 0.1820 0.5021 -vn 0.7908 0.0323 0.6112 -vn 0.7163 -0.0993 0.6907 -vn 0.8331 0.4102 0.3710 -vn 0.9022 0.3656 0.2288 -vn 0.9241 0.2183 0.3136 -vn 0.9112 0.2707 0.3105 -vn 0.9192 0.0652 0.3883 -vn 0.9140 0.1185 0.3880 -vn 0.8913 -0.0808 0.4462 -vn 0.8934 -0.0298 0.4482 -vn 0.8852 -0.2098 0.4151 -vn 0.8962 -0.1382 0.4215 -vn 0.9451 -0.1899 0.2657 -vn 0.5012 0.8052 0.3170 -vn 0.5797 0.7966 0.1711 -vn 0.4950 0.8428 0.2113 -vn 0.3445 0.8496 0.3994 -vn 0.4586 0.8657 0.2003 -vn 0.3575 0.9022 0.2409 -vn 0.1708 0.8655 0.4708 -vn 0.3150 0.9217 0.2263 -vn 0.2796 0.9438 0.1763 -vn 0.2883 0.9574 0.0127 -vn 0.2106 0.9679 0.1370 -vn -0.0020 0.8508 0.5254 -vn 0.1714 0.9839 0.0502 -vn 0.0207 0.9771 0.2119 -vn 0.1214 0.9921 0.0312 -vn -0.1578 0.8122 0.5617 -vn 0.0019 0.9977 0.0671 -vn -0.1587 0.9476 0.2771 -vn -0.0480 0.9977 0.0480 -vn -0.2878 0.7606 0.5820 -vn -0.1585 0.9843 0.0781 -vn -0.3114 0.8915 0.3290 -vn -0.2057 0.9767 0.0602 -vn -0.2511 0.9678 0.0178 -vn -0.2449 0.9574 -0.1530 -vn -0.3164 0.9486 -0.0077 -vn -0.4343 0.8237 0.3645 -vn 0.2881 0.6990 0.6545 -vn 0.1287 0.6995 0.7029 -vn -0.0222 0.6818 0.7312 -vn -0.1557 0.6513 0.7426 -vn -0.2682 0.6143 0.7420 -vn -0.3604 0.5759 0.7338 -vn 0.0945 0.5385 0.8373 -vn -0.0347 0.5257 0.8499 -vn -0.0435 0.3981 0.9163 -vn -0.1492 0.5062 0.8494 -vn -0.1415 0.3876 0.9109 -vn -0.1343 0.2948 0.9460 -vn -0.2473 0.4831 0.8399 -vn -0.2272 0.3749 0.8988 -vn -0.2097 0.2889 0.9341 -vn -0.1949 0.2207 0.9557 -vn -0.3298 0.4588 0.8250 -vn -0.3011 0.3611 0.8826 -vn -0.2761 0.2820 0.9188 -vn -0.2549 0.2183 0.9420 -vn -0.2370 0.1666 0.9571 -vn -0.3997 0.4338 0.8075 -vn -0.3656 0.3460 0.8641 -vn -0.3354 0.2735 0.9015 -vn -0.3095 0.2141 0.9265 -vn -0.2873 0.1654 0.9434 -vn -0.2684 0.1250 0.9551 -vn -0.2585 0.0519 0.9646 -vn -0.2650 0.0123 0.9641 -vn -0.2230 0.0328 0.9742 -vn -0.2714 -0.0295 0.9620 -vn -0.2289 -0.0112 0.9734 -vn -0.2776 -0.0735 0.9579 -vn -0.2346 -0.0578 0.9704 -vn -0.2835 -0.1195 0.9515 -vn -0.2401 -0.1068 0.9648 -vn -0.2890 -0.1673 0.9426 -vn -0.2452 -0.1580 0.9565 -vn -0.2500 -0.2111 0.9449 -vn -0.1809 0.0100 0.9834 -vn -0.1858 -0.0393 0.9818 -vn -0.1288 -0.0178 0.9915 -vn -0.1905 -0.0916 0.9774 -vn -0.1322 -0.0735 0.9885 -vn -0.0635 -0.0521 0.9966 -vn -0.1950 -0.1465 0.9698 -vn -0.1354 -0.1327 0.9818 -vn -0.0647 -0.1157 0.9911 -vn 0.0191 -0.0948 0.9953 -vn -0.1991 -0.2040 0.9585 -vn -0.1384 -0.1949 0.9710 -vn -0.0659 -0.1831 0.9809 -vn 0.0208 -0.1676 0.9856 -vn 0.1237 -0.1477 0.9813 -vn -0.2026 -0.2652 0.9426 -vn -0.1404 -0.2617 0.9549 -vn -0.0654 -0.2563 0.9644 -vn 0.0250 -0.2478 0.9685 -vn 0.1327 -0.2349 0.9629 -vn 0.2579 -0.2158 0.9417 -vn 0.4800 -0.2907 0.8277 -vn 0.5720 -0.4172 0.7062 -vn 0.7469 -0.3301 0.5771 -vn 0.6557 -0.5561 0.5106 -vn 0.8253 -0.4527 0.3377 -vn 0.5011 -0.5178 0.6933 -vn 0.4687 -0.3797 0.7976 -vn 0.7732 -0.5658 0.2864 -vn 0.0000 0.0000 1.0000 -vn 0.4119 -0.4710 0.7800 -vn 0.8621 -0.3818 0.3330 -vn 0.8748 -0.3269 0.3575 -vn 0.8899 -0.2768 0.3625 -vn 0.8038 -0.5172 0.2938 -vn 0.8762 -0.4389 0.1991 -vn -0.3612 0.9292 -0.0777 -vn -0.4577 0.8470 -0.2702 -vn -0.5656 0.8029 -0.1880 -vn -0.5194 0.8437 -0.1355 -vn -0.4504 0.8835 -0.1288 -vn -0.4885 0.8609 0.1424 -vn -0.4069 0.9087 -0.0934 -vn -0.6094 0.7672 -0.2000 -vn -0.6527 0.6720 0.3498 -vn -0.6803 0.7318 0.0406 -vn -0.6190 0.7310 0.2871 -vn -0.5442 0.6967 0.4673 -vn -0.5187 0.5330 0.6685 -vn -0.5472 0.4205 0.7237 -vn -0.4718 0.4264 0.7717 -vn -0.5640 0.3200 0.7612 -vn -0.4947 0.3293 0.8042 -vn -0.5729 0.2330 0.7858 -vn -0.5098 0.2448 0.8247 -vn -0.5767 0.1588 0.8014 -vn -0.5194 0.1723 0.8369 -vn -0.5774 0.0960 0.8108 -vn -0.5253 0.1104 0.8437 -vn -0.5286 0.0574 0.8469 -vn -0.4296 0.3378 0.8374 -vn -0.4500 0.2557 0.8556 -vn -0.3921 0.2647 0.8810 -vn -0.4646 0.1846 0.8660 -vn -0.4113 0.1955 0.8903 -vn -0.3598 0.2050 0.9102 -vn -0.4751 0.1234 0.8712 -vn -0.4259 0.1354 0.8946 -vn -0.3782 0.1462 0.9141 -vn -0.3323 0.1560 0.9301 -vn -0.4825 0.0707 0.8730 -vn -0.4372 0.0832 0.8955 -vn -0.3929 0.0949 0.9146 -vn -0.3501 0.1056 0.9307 -vn -0.3089 0.1155 0.9440 -vn -0.4877 0.0242 0.8727 -vn -0.4457 0.0368 0.8944 -vn -0.4045 0.0488 0.9132 -vn -0.3645 0.0602 0.9293 -vn -0.3258 0.0708 0.9428 -vn -0.2885 0.0808 0.9540 -vn 0.3161 -0.3178 0.8939 -vn 0.1672 -0.3333 0.9279 -vn 0.0404 -0.3398 0.9396 -vn -0.0636 -0.3406 0.9380 -vn -0.1479 -0.3384 0.9293 -vn -0.2161 -0.3348 0.9172 -vn 0.3848 -0.4482 0.8068 -vn 0.2089 -0.4590 0.8635 -vn 0.4566 -0.6026 0.6545 -vn 0.0587 -0.4551 0.8885 -vn 0.2564 -0.6131 0.7472 -vn 0.5089 -0.7503 0.4219 -vn 0.6125 -0.7642 0.2020 -vn -0.0622 -0.4438 0.8939 -vn 0.0808 -0.5975 0.7978 -vn 0.2998 -0.7753 0.5558 -vn 0.4638 -0.8423 0.2745 -vn 0.5073 -0.8118 0.2892 -vn -0.1574 -0.4299 0.8890 -vn -0.0596 -0.5698 0.8196 -vn 0.1043 -0.7561 0.6461 -vn 0.3231 -0.8994 0.2943 -vn 0.4207 -0.8737 0.2441 -vn 0.4152 -0.9039 0.1022 -vn -0.2336 -0.4167 0.8785 -vn -0.1692 -0.5411 0.8237 -vn -0.0574 -0.7155 0.6962 -vn 0.1209 -0.8987 0.4214 -vn 0.2548 -0.9528 0.1650 -vn 0.2994 -0.9367 0.1814 -vn -0.5966 0.5621 0.5727 -vn -0.6875 0.5855 0.4295 -vn -0.7784 0.5879 0.2200 -vn -0.5376 0.4524 0.7116 -vn -0.7413 0.5856 0.3280 -vn -0.7710 0.5695 -0.2848 -vn -0.8360 0.5463 -0.0513 -vn -0.6267 0.4320 0.6485 -vn -0.7216 0.4380 0.5361 -vn -0.6401 0.3162 0.7002 -vn -0.8252 0.4296 0.3666 -vn -0.7304 0.3056 0.6109 -vn -0.6430 0.2179 0.7342 -vn -0.8483 0.4951 -0.1878 -vn -0.9108 0.3918 0.1299 -vn -0.8730 0.4465 -0.1959 -vn -0.8324 0.2828 0.4765 -vn -0.7247 0.1949 0.6608 -vn -0.6401 0.1361 0.7561 -vn -0.8932 0.3930 -0.2181 -vn -0.8731 0.3381 -0.3511 -vn -0.9371 0.3155 -0.1491 -vn -0.9288 0.2398 0.2826 -vn -0.8176 0.1608 0.5529 -vn -0.7125 0.1052 0.6937 -vn -0.6343 0.0684 0.7700 -vn -0.9340 0.2524 -0.2526 -vn -0.9853 0.1676 0.0322 -vn -0.9447 0.2035 -0.2570 -vn -0.9120 0.1083 0.3955 -vn -0.7949 0.0620 0.6036 -vn -0.6985 0.0313 0.7149 -vn -0.6275 0.0110 0.7785 -vn -0.2962 0.0410 0.9542 -vn -0.3344 0.0295 0.9420 -vn -0.3740 0.0174 0.9272 -vn -0.4148 0.0046 0.9098 -vn -0.4567 -0.0087 0.8895 -vn -0.4994 -0.0226 0.8661 -vn -0.3038 0.0000 0.9527 -vn -0.3429 -0.0129 0.9393 -vn -0.3111 -0.0432 0.9494 -vn -0.3835 -0.0265 0.9232 -vn -0.3512 -0.0576 0.9345 -vn -0.3182 -0.0885 0.9438 -vn -0.4252 -0.0406 0.9042 -vn -0.3926 -0.0726 0.9168 -vn -0.3591 -0.1044 0.9274 -vn -0.3249 -0.1358 0.9359 -vn -0.4679 -0.0554 0.8820 -vn -0.4351 -0.0882 0.8961 -vn -0.4012 -0.1209 0.9080 -vn -0.3664 -0.1532 0.9177 -vn -0.3310 -0.1849 0.9253 -vn -0.5125 -0.0714 0.8557 -vn -0.4796 -0.1053 0.8712 -vn -0.4453 -0.1390 0.8845 -vn -0.4098 -0.1725 0.8957 -vn -0.3736 -0.2053 0.9045 -vn -0.3369 -0.2373 0.9111 -vn -0.3299 -0.3066 0.8928 -vn -0.3679 -0.3537 0.8599 -vn -0.3055 -0.3861 0.8704 -vn -0.4161 -0.4143 0.8094 -vn -0.3463 -0.4573 0.8191 -vn -0.4764 -0.4921 0.7285 -vn -0.3985 -0.5505 0.7335 -vn -0.5479 -0.5874 0.5956 -vn -0.4608 -0.6659 0.5866 -vn -0.6178 -0.6862 0.3839 -vn -0.5186 -0.7819 0.3459 -vn -0.5387 -0.8422 0.0182 -vn -0.2652 -0.4997 0.8245 -vn -0.3040 -0.6091 0.7325 -vn -0.1905 -0.6665 0.7208 -vn -0.3495 -0.7447 0.5686 -vn -0.2113 -0.8174 0.5358 -vn -0.0511 -0.8726 0.4858 -vn -0.3872 -0.8728 0.2973 -vn -0.2232 -0.9460 0.2349 -vn -0.0381 -0.9861 0.1616 -vn 0.1475 -0.9856 0.0825 -vn 0.2114 -0.9691 0.1274 -vn -0.3890 -0.9194 -0.0582 -vn -0.3180 -0.9473 -0.0390 -vn -0.2755 -0.9612 0.0138 -vn -0.2280 -0.9731 0.0315 -vn -0.1080 -0.9941 -0.0046 -vn -0.0591 -0.9981 0.0147 -vn 0.0612 -0.9978 -0.0256 -vn 0.1087 -0.9940 -0.0062 -vn 0.2174 -0.9750 -0.0445 -vn -0.5423 -0.8277 -0.1440 -vn -0.4511 -0.8749 -0.1762 -vn -0.4136 -0.8954 -0.1649 -vn -0.3053 -0.9310 -0.2001 -vn -0.3730 -0.2710 0.8873 -vn -0.4148 -0.2350 0.8790 -vn -0.4556 -0.1978 0.8679 -vn -0.4950 -0.1598 0.8541 -vn -0.5327 -0.1213 0.8375 -vn -0.5683 -0.0828 0.8186 -vn -0.4176 -0.3128 0.8531 -vn -0.4655 -0.2715 0.8424 -vn -0.4741 -0.3665 0.8005 -vn -0.5117 -0.2285 0.8282 -vn -0.5295 -0.3181 0.7864 -vn -0.5451 -0.4352 0.7165 -vn -0.5557 -0.1845 0.8106 -vn -0.5820 -0.2675 0.7679 -vn -0.6092 -0.3774 0.6974 -vn -0.6289 -0.5188 0.5790 -vn -0.5970 -0.1399 0.7899 -vn -0.6308 -0.2154 0.7454 -vn -0.6684 -0.3164 0.6732 -vn -0.7020 -0.4484 0.5533 -vn -0.7099 -0.6044 0.3615 -vn -0.6364 -0.0949 0.7655 -vn -0.6773 -0.1625 0.7175 -vn -0.7242 -0.2533 0.6413 -vn -0.7699 -0.3733 0.5175 -vn -0.7920 -0.5183 0.3226 -vn -0.7951 -0.5276 -0.2991 -vn -0.8535 -0.4716 -0.2216 -vn -0.8136 -0.4409 -0.3789 -vn -0.7419 -0.6353 -0.2143 -vn -0.8111 -0.5797 -0.0776 -vn -0.7729 -0.5647 -0.2894 -vn -0.8947 0.0971 -0.4358 -vn -0.9286 0.0060 -0.3711 -vn -0.9500 0.0725 -0.3037 -vn -0.9482 0.1488 -0.2806 -vn -0.9439 -0.1451 -0.2966 -vn -0.9824 -0.0758 -0.1706 -vn -0.9266 -0.0455 -0.3731 -vn -0.9872 0.0299 0.1564 -vn -0.9305 -0.2959 -0.2156 -vn -0.9747 -0.2215 -0.0295 -vn -0.9348 -0.1971 -0.2955 -vn -0.9568 -0.0988 0.2736 -vn -0.8778 -0.0038 0.4789 -vn -0.8844 -0.4013 -0.2383 -vn -0.9315 -0.3480 0.1057 -vn -0.9145 -0.3451 -0.2110 -vn -0.9032 -0.2109 0.3737 -vn -0.8309 -0.1011 0.5471 -vn -0.7588 -0.0239 0.6509 -vn -0.8662 -0.4468 0.2236 -vn -0.8384 -0.3020 0.4538 -vn -0.7787 -0.1840 0.5998 -vn -0.7191 -0.0979 0.6879 -vn -0.6688 -0.0358 0.7425 -vn 0.8971 0.1574 -0.4128 -vn 0.8824 0.2113 -0.4203 -vn -0.5307 0.8044 -0.2668 -vn 0.3073 0.0111 0.9515 -vn -0.3627 0.9105 -0.1987 -vn -0.1970 0.5581 -0.8061 -vn -0.1552 0.5851 -0.7960 -vn -0.1207 0.6171 -0.7775 -vn -0.0786 0.6345 -0.7689 -vn -0.0467 0.6590 -0.7507 -vn -0.0007 0.6751 -0.7377 -vn 0.0408 0.6942 -0.7186 -vn 0.0804 0.6985 -0.7111 -vn 0.1135 0.7132 -0.6917 -vn 0.1622 0.7174 -0.6774 -vn 0.2096 0.7251 -0.6559 -vn 0.2621 0.7221 -0.6402 -vn 0.3124 0.7210 -0.6185 -vn 0.3654 0.7098 -0.6022 -vn 0.4151 0.6993 -0.5819 -vn 0.4508 0.6814 -0.5766 -vn 0.4823 0.6746 -0.5587 -vn 0.5273 0.6529 -0.5437 -vn 0.5672 0.6306 -0.5298 -vn 0.6055 0.6059 -0.5159 -vn 0.6384 0.5800 -0.5059 -vn 0.6912 0.5428 -0.4771 -vn 0.7346 0.4873 -0.4721 -vn 0.7615 0.4555 -0.4610 -vn 0.7884 0.4173 -0.4519 -vn 0.8131 0.3773 -0.4431 -vn 0.8375 0.3322 -0.4337 -vn 0.8492 0.2973 -0.4364 -vn 0.8669 0.2638 -0.4229 -vn 0.9043 0.1029 -0.4143 -vn 0.9112 0.0488 -0.4091 -vn 0.9101 -0.0035 -0.4143 -vn 0.9098 -0.0544 -0.4115 -vn 0.9017 -0.0933 -0.4220 -vn 0.8993 -0.1334 -0.4165 -vn 0.8868 -0.1805 -0.4254 -vn 0.8749 -0.2295 -0.4264 -vn 0.8574 -0.2677 -0.4395 -vn 0.8429 -0.3118 -0.4385 -vn 0.8183 -0.3533 -0.4534 -vn 0.7952 -0.3984 -0.4569 -vn 0.8501 -0.4614 0.2537 -vn 0.7847 -0.5769 0.2270 -vn 0.6742 -0.5408 -0.5029 -vn 0.6359 -0.5677 -0.5228 -vn 0.5980 -0.5998 -0.5316 -vn 0.5603 -0.6171 -0.5524 -vn 0.5244 -0.6417 -0.5596 -vn 0.4810 -0.6577 -0.5797 -vn 0.4371 -0.6768 -0.5922 -vn 0.4020 -0.6810 -0.6120 -vn 0.3643 -0.6959 -0.6189 -vn 0.3179 -0.7000 -0.6394 -vn 0.2678 -0.7078 -0.6536 -vn 0.2176 -0.7047 -0.6753 -vn 0.1650 -0.7037 -0.6911 -vn 0.1139 -0.6924 -0.7124 -vn 0.0627 -0.6820 -0.7286 -vn 0.0318 -0.6639 -0.7471 -vn -0.0039 -0.6573 -0.7536 -vn -0.0481 -0.6355 -0.7706 -vn -0.0878 -0.6132 -0.7850 -vn -0.1263 -0.5886 -0.7985 -vn -0.1581 -0.5625 -0.8115 -vn -0.2174 -0.5257 -0.8224 -vn -0.2542 -0.4699 -0.8453 -vn -0.2823 -0.4382 -0.8534 -vn -0.3091 -0.3999 -0.8628 -vn -0.3340 -0.3599 -0.8711 -vn -0.3591 -0.3149 -0.8786 -vn -0.3666 -0.2798 -0.8873 -vn -0.3891 -0.2464 -0.8876 -vn -0.4031 -0.1940 -0.8943 -vn -0.4197 -0.1400 -0.8968 -vn -0.4246 -0.0855 -0.9013 -vn -0.4337 -0.0315 -0.9005 -vn -0.4300 0.0209 -0.9026 -vn -0.4319 0.0717 -0.8990 -vn -0.4194 0.1108 -0.9010 -vn -0.4213 0.1507 -0.8943 -vn -0.4065 0.1979 -0.8919 -vn -0.3971 0.2468 -0.8839 -vn -0.3756 0.2852 -0.8818 -vn -0.3656 0.3291 -0.8706 -vn -0.3374 0.3707 -0.8652 -vn -0.3180 0.4158 -0.8520 -usemtl Material.003 -s 1 -f 1016/1277/1009 1331/1278/1010 1009/1279/1011 -f 1010/1280/1012 1022/1281/1013 1034/1282/1014 -f 1035/1283/1015 1009/1284/1011 1339/1285/1016 -f 1010/1280/1012 1034/1282/1014 1053/1286/1017 -f 1011/1287/1018 1046/1288/1019 1077/1289/1020 -f 1010/1280/1012 1053/1286/1017 1064/1290/1021 -f 1011/1287/1018 1077/1289/1020 1054/1291/1022 -f 1012/1292/1023 1088/1293/1024 1106/1294/1025 -f 1013/1295/1026 1092/1296/1027 1120/1297/1028 -f 1014/1298/1029 1099/1299/1030 1107/1300/1031 -f 1114/1301/1032 1015/1302/1033 1113/1303/1034 -f 1112/1304/1035 1121/1305/1036 1113/1303/1034 -f 1111/1306/1037 1122/1307/1038 1112/1304/1035 -f 1110/1308/1039 1124/1309/1040 1111/1306/1037 -f 1109/1310/1041 1127/1311/1042 1110/1308/1039 -f 1108/1312/1043 1131/1313/1044 1109/1310/1041 -f 1107/1300/1031 1136/1314/1045 1108/1312/1043 -f 1113/1303/1034 1121/1305/1036 1114/1301/1032 -f 1121/1305/1036 1115/1315/1046 1114/1301/1032 -f 1112/1304/1035 1122/1307/1038 1121/1305/1036 -f 1122/1307/1038 1123/1316/1047 1121/1305/1036 -f 1121/1305/1036 1123/1316/1047 1115/1315/1046 -f 1123/1316/1047 1116/1317/1048 1115/1315/1046 -f 1111/1306/1037 1124/1309/1040 1122/1307/1038 -f 1124/1309/1040 1125/1318/1049 1122/1307/1038 -f 1122/1307/1038 1125/1318/1049 1123/1316/1047 -f 1125/1318/1049 1126/1319/1050 1123/1316/1047 -f 1123/1316/1047 1126/1319/1050 1116/1317/1048 -f 1126/1319/1050 1117/1320/1051 1116/1317/1048 -f 1110/1308/1039 1127/1311/1042 1124/1309/1040 -f 1127/1311/1042 1128/1321/1052 1124/1309/1040 -f 1124/1309/1040 1128/1321/1052 1125/1318/1049 -f 1128/1321/1052 1129/1322/1053 1125/1318/1049 -f 1125/1318/1049 1129/1322/1053 1126/1319/1050 -f 1129/1322/1053 1130/1323/1054 1126/1319/1050 -f 1126/1319/1050 1130/1323/1054 1117/1320/1051 -f 1130/1323/1054 1118/1324/1055 1117/1320/1051 -f 1109/1310/1041 1131/1313/1044 1127/1311/1042 -f 1131/1313/1044 1132/1325/1056 1127/1311/1042 -f 1127/1311/1042 1132/1325/1056 1128/1321/1052 -f 1132/1325/1056 1133/1326/1057 1128/1321/1052 -f 1128/1321/1052 1133/1326/1057 1129/1322/1053 -f 1133/1326/1057 1134/1327/1058 1129/1322/1053 -f 1129/1322/1053 1134/1327/1058 1130/1323/1054 -f 1134/1327/1058 1135/1328/1059 1130/1323/1054 -f 1130/1323/1054 1135/1328/1059 1118/1324/1055 -f 1135/1328/1059 1119/1329/1060 1118/1324/1055 -f 1108/1312/1043 1136/1314/1045 1131/1313/1044 -f 1136/1314/1045 1137/1330/1061 1131/1313/1044 -f 1131/1313/1044 1137/1330/1061 1132/1325/1056 -f 1137/1330/1061 1138/1331/1062 1132/1325/1056 -f 1132/1325/1056 1138/1331/1062 1133/1326/1057 -f 1138/1331/1062 1139/1332/1063 1133/1326/1057 -f 1133/1326/1057 1139/1332/1063 1134/1327/1058 -f 1139/1332/1063 1140/1333/1064 1134/1327/1058 -f 1134/1327/1058 1140/1333/1064 1135/1328/1059 -f 1140/1333/1064 1141/1334/1065 1135/1328/1059 -f 1135/1328/1059 1141/1334/1065 1119/1329/1060 -f 1141/1334/1065 1120/1335/1028 1119/1329/1060 -f 1107/1300/1031 1099/1299/1030 1136/1314/1045 -f 1099/1299/1030 1098/1336/1066 1136/1314/1045 -f 1136/1314/1045 1098/1336/1066 1137/1330/1061 -f 1098/1336/1066 1097/1337/1067 1137/1330/1061 -f 1137/1330/1061 1097/1337/1067 1138/1331/1062 -f 1097/1337/1067 1096/1338/1068 1138/1331/1062 -f 1138/1331/1062 1096/1338/1068 1139/1332/1063 -f 1096/1338/1068 1095/1339/1069 1139/1332/1063 -f 1139/1332/1063 1095/1339/1069 1140/1333/1064 -f 1095/1339/1069 1094/1340/1070 1140/1333/1064 -f 1140/1333/1064 1094/1340/1070 1141/1334/1065 -f 1094/1340/1070 1093/1341/1071 1141/1334/1065 -f 1141/1334/1065 1093/1341/1071 1120/1335/1028 -f 1093/1341/1071 1013/1295/1026 1120/1335/1028 -f 1114/1342/1032 1343/1343/1072 1015/1344/1033 -f 1114/1342/1032 1115/1345/1046 1348/1346/1073 1347/1347/1074 -f 1115/1345/1046 1116/1348/1048 1352/1349/1075 1351/1350/1076 -f 1117/1351/1051 1142/1352/1077 1116/1348/1048 -f 1118/1353/1055 1143/1354/1078 1117/1351/1051 -f 1119/1355/1060 1144/1356/1079 1118/1353/1055 -f 1120/1297/1028 1145/1357/1080 1119/1355/1060 -f 1347/1347/1074 1343/1343/1072 1114/1342/1032 -f 1351/1350/1076 1348/1346/1073 1115/1345/1046 -f 1116/1348/1048 1142/1352/1077 1365/1358/1081 1352/1349/1075 -f 1370/1359/1082 1365/1358/1081 1142/1352/1077 -f 1117/1351/1051 1143/1354/1078 1142/1352/1077 -f 1142/1352/1077 1143/1354/1078 1374/1360/1083 1373/1361/1084 -f 1373/1361/1084 1370/1359/1082 1142/1352/1077 -f 1118/1353/1055 1144/1356/1079 1143/1354/1078 -f 1143/1354/1078 1144/1356/1079 1384/1362/1085 1382/1363/1086 -f 1382/1363/1086 1374/1360/1083 1143/1354/1078 -f 1119/1355/1060 1145/1357/1080 1144/1356/1079 -f 1144/1356/1079 1145/1357/1080 1389/1364/1087 1387/1365/1088 -f 1384/1362/1085 1144/1356/1079 1387/1365/1088 -f 1120/1297/1028 1092/1296/1027 1145/1357/1080 -f 1092/1296/1027 1091/1366/1089 1145/1357/1080 -f 1145/1357/1080 1091/1366/1089 1397/1367/1090 1389/1364/1087 -f 1397/1367/1090 1091/1366/1089 1400/1368/1091 -f 1100/1369/1092 1015/1370/1033 1404/1371/1093 -f 1404/1371/1093 1407/1372/1094 1100/1369/1092 -f 1101/1373/1095 1100/1369/1092 1407/1372/1094 1410/1374/1096 -f 1410/1374/1096 1411/1375/1097 1101/1373/1095 -f 1102/1376/1098 1101/1373/1095 1411/1375/1097 1413/1377/1099 -f 1414/1378/1100 1332/1379/1101 1146/1380/1102 -f 1146/1380/1102 1102/1376/1098 1413/1377/1099 1414/1378/1100 -f 1146/1380/1102 1103/1381/1103 1102/1376/1098 -f 1332/1379/1101 1333/1382/1104 1146/1380/1102 -f 1147/1383/1105 1146/1380/1102 1333/1382/1104 1342/1384/1106 -f 1146/1380/1102 1147/1383/1105 1103/1381/1103 -f 1147/1383/1105 1104/1385/1107 1103/1381/1103 -f 1342/1384/1106 1344/1386/1108 1147/1383/1105 -f 1148/1387/1109 1147/1383/1105 1344/1386/1108 1350/1388/1110 -f 1147/1383/1105 1148/1387/1109 1104/1385/1107 -f 1148/1387/1109 1105/1389/1111 1104/1385/1107 -f 1148/1387/1109 1350/1388/1110 1357/1390/1112 -f 1149/1391/1113 1148/1387/1109 1357/1390/1112 1364/1392/1114 -f 1148/1387/1109 1149/1391/1113 1105/1389/1111 -f 1149/1391/1113 1106/1393/1025 1105/1389/1111 -f 1368/1394/1115 1369/1395/1116 1090/1396/1117 -f 1090/1396/1117 1149/1391/1113 1364/1392/1114 1368/1394/1115 -f 1090/1396/1117 1089/1397/1118 1149/1391/1113 -f 1149/1391/1113 1089/1397/1118 1106/1393/1025 -f 1089/1397/1118 1012/1292/1023 1106/1393/1025 -f 1015/1398/1033 1100/1399/1092 1113/1400/1034 -f 1101/1401/1095 1150/1402/1119 1100/1399/1092 -f 1102/1403/1098 1151/1404/1120 1101/1401/1095 -f 1103/1405/1103 1153/1406/1121 1102/1403/1098 -f 1104/1407/1107 1156/1408/1122 1103/1405/1103 -f 1105/1409/1111 1160/1410/1123 1104/1407/1107 -f 1106/1294/1025 1165/1411/1124 1105/1409/1111 -f 1100/1399/1092 1150/1402/1119 1113/1400/1034 -f 1150/1402/1119 1112/1412/1035 1113/1400/1034 -f 1101/1401/1095 1151/1404/1120 1150/1402/1119 -f 1151/1404/1120 1152/1413/1125 1150/1402/1119 -f 1150/1402/1119 1152/1413/1125 1112/1412/1035 -f 1152/1413/1125 1111/1414/1037 1112/1412/1035 -f 1102/1403/1098 1153/1406/1121 1151/1404/1120 -f 1153/1406/1121 1154/1415/1126 1151/1404/1120 -f 1151/1404/1120 1154/1415/1126 1152/1413/1125 -f 1154/1415/1126 1155/1416/1127 1152/1413/1125 -f 1152/1413/1125 1155/1416/1127 1111/1414/1037 -f 1155/1416/1127 1110/1417/1039 1111/1414/1037 -f 1103/1405/1103 1156/1408/1122 1153/1406/1121 -f 1156/1408/1122 1157/1418/1128 1153/1406/1121 -f 1153/1406/1121 1157/1418/1128 1154/1415/1126 -f 1157/1418/1128 1158/1419/1129 1154/1415/1126 -f 1154/1415/1126 1158/1419/1129 1155/1416/1127 -f 1158/1419/1129 1159/1420/1130 1155/1416/1127 -f 1155/1416/1127 1159/1420/1130 1110/1417/1039 -f 1159/1420/1130 1109/1421/1041 1110/1417/1039 -f 1104/1407/1107 1160/1410/1123 1156/1408/1122 -f 1160/1410/1123 1161/1422/1131 1156/1408/1122 -f 1156/1408/1122 1161/1422/1131 1157/1418/1128 -f 1161/1422/1131 1162/1423/1132 1157/1418/1128 -f 1157/1418/1128 1162/1423/1132 1158/1419/1129 -f 1162/1423/1132 1163/1424/1133 1158/1419/1129 -f 1158/1419/1129 1163/1424/1133 1159/1420/1130 -f 1163/1424/1133 1164/1425/1134 1159/1420/1130 -f 1159/1420/1130 1164/1425/1134 1109/1421/1041 -f 1164/1425/1134 1108/1426/1043 1109/1421/1041 -f 1105/1409/1111 1165/1411/1124 1160/1410/1123 -f 1165/1411/1124 1166/1427/1135 1160/1410/1123 -f 1160/1410/1123 1166/1427/1135 1161/1422/1131 -f 1166/1427/1135 1167/1428/1136 1161/1422/1131 -f 1161/1422/1131 1167/1428/1136 1162/1423/1132 -f 1167/1428/1136 1168/1429/1137 1162/1423/1132 -f 1162/1423/1132 1168/1429/1137 1163/1424/1133 -f 1168/1429/1137 1169/1430/1138 1163/1424/1133 -f 1163/1424/1133 1169/1430/1138 1164/1425/1134 -f 1169/1430/1138 1170/1431/1139 1164/1425/1134 -f 1164/1425/1134 1170/1431/1139 1108/1426/1043 -f 1170/1431/1139 1107/1432/1031 1108/1426/1043 -f 1106/1294/1025 1088/1293/1024 1165/1411/1124 -f 1088/1293/1024 1087/1433/1140 1165/1411/1124 -f 1165/1411/1124 1087/1433/1140 1166/1427/1135 -f 1087/1433/1140 1086/1434/1141 1166/1427/1135 -f 1166/1427/1135 1086/1434/1141 1167/1428/1136 -f 1086/1434/1141 1085/1435/1142 1167/1428/1136 -f 1167/1428/1136 1085/1435/1142 1168/1429/1137 -f 1085/1435/1142 1084/1436/1143 1168/1429/1137 -f 1168/1429/1137 1084/1436/1143 1169/1430/1138 -f 1084/1436/1143 1083/1437/1144 1169/1430/1138 -f 1169/1430/1138 1083/1437/1144 1170/1431/1139 -f 1083/1437/1144 1082/1438/1145 1170/1431/1139 -f 1170/1431/1139 1082/1438/1145 1107/1432/1031 -f 1082/1438/1145 1014/1298/1029 1107/1432/1031 -f 1060/1439/1146 1099/1299/1030 1014/1298/1029 -f 1059/1440/1147 1171/1441/1148 1060/1439/1146 -f 1058/1442/1149 1172/1443/1150 1059/1440/1147 -f 1057/1444/1151 1174/1445/1152 1058/1442/1149 -f 1056/1446/1153 1177/1447/1154 1057/1444/1151 -f 1055/1448/1155 1181/1449/1156 1056/1446/1153 -f 1054/1291/1022 1186/1450/1157 1055/1448/1155 -f 1060/1439/1146 1171/1441/1148 1099/1299/1030 -f 1171/1441/1148 1098/1336/1066 1099/1299/1030 -f 1059/1440/1147 1172/1443/1150 1171/1441/1148 -f 1172/1443/1150 1173/1451/1158 1171/1441/1148 -f 1171/1441/1148 1173/1451/1158 1098/1336/1066 -f 1173/1451/1158 1097/1337/1067 1098/1336/1066 -f 1058/1442/1149 1174/1445/1152 1172/1443/1150 -f 1174/1445/1152 1175/1452/1159 1172/1443/1150 -f 1172/1443/1150 1175/1452/1159 1173/1451/1158 -f 1175/1452/1159 1176/1453/1160 1173/1451/1158 -f 1173/1451/1158 1176/1453/1160 1097/1337/1067 -f 1176/1453/1160 1096/1338/1068 1097/1337/1067 -f 1057/1444/1151 1177/1447/1154 1174/1445/1152 -f 1177/1447/1154 1178/1454/1161 1174/1445/1152 -f 1174/1445/1152 1178/1454/1161 1175/1452/1159 -f 1178/1454/1161 1179/1455/1162 1175/1452/1159 -f 1175/1452/1159 1179/1455/1162 1176/1453/1160 -f 1179/1455/1162 1180/1456/1163 1176/1453/1160 -f 1176/1453/1160 1180/1456/1163 1096/1338/1068 -f 1180/1456/1163 1095/1339/1069 1096/1338/1068 -f 1056/1446/1153 1181/1449/1156 1177/1447/1154 -f 1181/1449/1156 1182/1457/1164 1177/1447/1154 -f 1177/1447/1154 1182/1457/1164 1178/1454/1161 -f 1182/1457/1164 1183/1458/1165 1178/1454/1161 -f 1178/1454/1161 1183/1458/1165 1179/1455/1162 -f 1183/1458/1165 1184/1459/1166 1179/1455/1162 -f 1179/1455/1162 1184/1459/1166 1180/1456/1163 -f 1184/1459/1166 1185/1460/1167 1180/1456/1163 -f 1180/1456/1163 1185/1460/1167 1095/1339/1069 -f 1185/1460/1167 1094/1340/1070 1095/1339/1069 -f 1055/1448/1155 1186/1450/1157 1181/1449/1156 -f 1186/1450/1157 1187/1461/1168 1181/1449/1156 -f 1181/1449/1156 1187/1461/1168 1182/1457/1164 -f 1187/1461/1168 1188/1462/1169 1182/1457/1164 -f 1182/1457/1164 1188/1462/1169 1183/1458/1165 -f 1188/1462/1169 1189/1463/1170 1183/1458/1165 -f 1183/1458/1165 1189/1463/1170 1184/1459/1166 -f 1189/1463/1170 1190/1464/1171 1184/1459/1166 -f 1184/1459/1166 1190/1464/1171 1185/1460/1167 -f 1190/1464/1171 1191/1465/1172 1185/1460/1167 -f 1185/1460/1167 1191/1465/1172 1094/1340/1070 -f 1191/1465/1172 1093/1341/1071 1094/1340/1070 -f 1054/1291/1022 1077/1289/1020 1186/1450/1157 -f 1077/1289/1020 1076/1466/1173 1186/1450/1157 -f 1186/1450/1157 1076/1466/1173 1187/1461/1168 -f 1076/1466/1173 1075/1467/1174 1187/1461/1168 -f 1187/1461/1168 1075/1467/1174 1188/1462/1169 -f 1075/1467/1174 1074/1468/1175 1188/1462/1169 -f 1188/1462/1169 1074/1468/1175 1189/1463/1170 -f 1074/1468/1175 1073/1469/1176 1189/1463/1170 -f 1189/1463/1170 1073/1469/1176 1190/1464/1171 -f 1073/1469/1176 1072/1470/1177 1190/1464/1171 -f 1190/1464/1171 1072/1470/1177 1191/1465/1172 -f 1072/1470/1177 1071/1471/1178 1191/1465/1172 -f 1191/1465/1172 1071/1471/1178 1093/1341/1071 -f 1071/1471/1178 1013/1295/1026 1093/1341/1071 -f 1081/1472/1179 1092/1296/1027 1013/1295/1026 -f 1080/1473/1180 1192/1474/1181 1081/1472/1179 -f 1079/1475/1182 1193/1476/1183 1080/1473/1180 -f 1079/1475/1182 1078/1477/1184 1381/1478/1185 1383/1479/1186 -f 1386/1480/1187 1078/1477/1184 1336/1481/1188 -f 1081/1472/1179 1192/1474/1181 1092/1296/1027 -f 1192/1474/1181 1091/1366/1089 1092/1296/1027 -f 1080/1473/1180 1193/1476/1183 1192/1474/1181 -f 1192/1474/1181 1193/1476/1183 1392/1482/1189 1393/1483/1190 -f 1091/1366/1089 1192/1474/1181 1393/1483/1190 1396/1484/1191 -f 1400/1368/1091 1091/1366/1089 1396/1484/1191 -f 1193/1476/1183 1079/1475/1182 1383/1479/1186 1405/1485/1192 -f 1409/1486/1193 1193/1476/1183 1405/1485/1192 -f 1392/1482/1189 1193/1476/1183 1409/1486/1193 -f 1381/1478/1185 1078/1477/1184 1386/1480/1187 -f 1369/1395/1116 1415/1487/1194 1090/1396/1117 -f 1416/1488/1195 1417/1489/1196 1194/1490/1197 -f 1418/1491/1198 1416/1488/1195 1194/1490/1197 -f 1194/1490/1197 1195/1492/1199 1419/1493/1200 1418/1491/1198 -f 1195/1492/1199 1090/1396/1117 1415/1487/1194 1419/1493/1200 -f 1195/1492/1199 1089/1397/1118 1090/1396/1117 -f 1420/1494/1201 1358/1495/1202 1063/1496/1203 -f 1063/1496/1203 1194/1490/1197 1417/1489/1196 1420/1494/1201 -f 1063/1496/1203 1062/1497/1204 1194/1490/1197 -f 1194/1490/1197 1062/1497/1204 1195/1492/1199 -f 1062/1497/1204 1061/1498/1205 1195/1492/1199 -f 1195/1492/1199 1061/1498/1205 1089/1397/1118 -f 1061/1498/1205 1012/1292/1023 1089/1397/1118 -f 1070/1499/1206 1088/1293/1024 1012/1292/1023 -f 1069/1500/1207 1196/1501/1208 1070/1499/1206 -f 1068/1502/1209 1197/1503/1210 1069/1500/1207 -f 1067/1504/1211 1199/1505/1212 1068/1502/1209 -f 1066/1506/1213 1202/1507/1214 1067/1504/1211 -f 1065/1508/1215 1206/1509/1216 1066/1506/1213 -f 1064/1290/1021 1211/1510/1217 1065/1508/1215 -f 1070/1499/1206 1196/1501/1208 1088/1293/1024 -f 1196/1501/1208 1087/1433/1140 1088/1293/1024 -f 1069/1500/1207 1197/1503/1210 1196/1501/1208 -f 1197/1503/1210 1198/1511/1218 1196/1501/1208 -f 1196/1501/1208 1198/1511/1218 1087/1433/1140 -f 1198/1511/1218 1086/1434/1141 1087/1433/1140 -f 1068/1502/1209 1199/1505/1212 1197/1503/1210 -f 1199/1505/1212 1200/1512/1219 1197/1503/1210 -f 1197/1503/1210 1200/1512/1219 1198/1511/1218 -f 1200/1512/1219 1201/1513/1220 1198/1511/1218 -f 1198/1511/1218 1201/1513/1220 1086/1434/1141 -f 1201/1513/1220 1085/1435/1142 1086/1434/1141 -f 1067/1504/1211 1202/1507/1214 1199/1505/1212 -f 1202/1507/1214 1203/1514/1221 1199/1505/1212 -f 1199/1505/1212 1203/1514/1221 1200/1512/1219 -f 1203/1514/1221 1204/1515/1222 1200/1512/1219 -f 1200/1512/1219 1204/1515/1222 1201/1513/1220 -f 1204/1515/1222 1205/1516/1223 1201/1513/1220 -f 1201/1513/1220 1205/1516/1223 1085/1435/1142 -f 1205/1516/1223 1084/1436/1143 1085/1435/1142 -f 1066/1506/1213 1206/1509/1216 1202/1507/1214 -f 1206/1509/1216 1207/1517/1224 1202/1507/1214 -f 1202/1507/1214 1207/1517/1224 1203/1514/1221 -f 1207/1517/1224 1208/1518/1225 1203/1514/1221 -f 1203/1514/1221 1208/1518/1225 1204/1515/1222 -f 1208/1518/1225 1209/1519/1226 1204/1515/1222 -f 1204/1515/1222 1209/1519/1226 1205/1516/1223 -f 1209/1519/1226 1210/1520/1227 1205/1516/1223 -f 1205/1516/1223 1210/1520/1227 1084/1436/1143 -f 1210/1520/1227 1083/1437/1144 1084/1436/1143 -f 1065/1508/1215 1211/1510/1217 1206/1509/1216 -f 1211/1510/1217 1212/1521/1228 1206/1509/1216 -f 1206/1509/1216 1212/1521/1228 1207/1517/1224 -f 1212/1521/1228 1213/1522/1229 1207/1517/1224 -f 1207/1517/1224 1213/1522/1229 1208/1518/1225 -f 1213/1522/1229 1214/1523/1230 1208/1518/1225 -f 1208/1518/1225 1214/1523/1230 1209/1519/1226 -f 1214/1523/1230 1215/1524/1231 1209/1519/1226 -f 1209/1519/1226 1215/1524/1231 1210/1520/1227 -f 1215/1524/1231 1216/1525/1232 1210/1520/1227 -f 1210/1520/1227 1216/1525/1232 1083/1437/1144 -f 1216/1525/1232 1082/1438/1145 1083/1437/1144 -f 1064/1290/1021 1053/1286/1017 1211/1510/1217 -f 1053/1286/1017 1052/1526/1233 1211/1510/1217 -f 1211/1510/1217 1052/1526/1233 1212/1521/1228 -f 1052/1526/1233 1051/1527/1234 1212/1521/1228 -f 1212/1521/1228 1051/1527/1234 1213/1522/1229 -f 1051/1527/1234 1050/1528/1235 1213/1522/1229 -f 1213/1522/1229 1050/1528/1235 1214/1523/1230 -f 1050/1528/1235 1049/1529/1236 1214/1523/1230 -f 1214/1523/1230 1049/1529/1236 1215/1524/1231 -f 1049/1529/1236 1048/1530/1237 1215/1524/1231 -f 1215/1524/1231 1048/1530/1237 1216/1525/1232 -f 1048/1530/1237 1047/1531/1238 1216/1525/1232 -f 1216/1525/1232 1047/1531/1238 1082/1438/1145 -f 1047/1531/1238 1014/1298/1029 1082/1438/1145 -f 1071/1471/1178 1081/1472/1179 1013/1295/1026 -f 1072/1470/1177 1217/1532/1239 1071/1471/1178 -f 1073/1469/1176 1218/1533/1240 1072/1470/1177 -f 1074/1468/1175 1220/1534/1241 1073/1469/1176 -f 1075/1467/1174 1223/1535/1242 1074/1468/1175 -f 1076/1466/1173 1227/1536/1243 1075/1467/1174 -f 1077/1289/1020 1231/1537/1244 1076/1466/1173 -f 1071/1471/1178 1217/1532/1239 1081/1472/1179 -f 1217/1532/1239 1080/1473/1180 1081/1472/1179 -f 1072/1470/1177 1218/1533/1240 1217/1532/1239 -f 1218/1533/1240 1219/1538/1245 1217/1532/1239 -f 1217/1532/1239 1219/1538/1245 1080/1473/1180 -f 1219/1538/1245 1079/1475/1182 1080/1473/1180 -f 1073/1469/1176 1220/1534/1241 1218/1533/1240 -f 1220/1534/1241 1221/1539/1246 1218/1533/1240 -f 1218/1533/1240 1221/1539/1246 1219/1538/1245 -f 1221/1539/1246 1222/1540/1247 1219/1538/1245 -f 1219/1538/1245 1222/1540/1247 1079/1475/1182 -f 1079/1475/1182 1222/1540/1247 1078/1477/1184 -f 1074/1468/1175 1223/1535/1242 1220/1534/1241 -f 1223/1535/1242 1224/1541/1248 1220/1534/1241 -f 1220/1534/1241 1224/1541/1248 1221/1539/1246 -f 1224/1541/1248 1225/1542/1249 1221/1539/1246 -f 1221/1539/1246 1225/1542/1249 1222/1540/1247 -f 1225/1542/1249 1226/1543/1250 1222/1540/1247 -f 1078/1477/1184 1222/1540/1247 1226/1543/1250 -f 1078/1477/1184 1226/1543/1250 1337/1544/1251 1336/1481/1188 -f 1075/1467/1174 1227/1536/1243 1223/1535/1242 -f 1227/1536/1243 1228/1545/1252 1223/1535/1242 -f 1223/1535/1242 1228/1545/1252 1224/1541/1248 -f 1228/1545/1252 1229/1546/1253 1224/1541/1248 -f 1224/1541/1248 1229/1546/1253 1225/1542/1249 -f 1229/1546/1253 1230/1547/1254 1225/1542/1249 -f 1225/1542/1249 1230/1547/1254 1226/1543/1250 -f 1226/1543/1250 1230/1547/1254 1340/1548/1255 1338/1549/1256 -f 1337/1544/1251 1226/1543/1250 1338/1549/1256 -f 1076/1466/1173 1231/1537/1244 1227/1536/1243 -f 1231/1537/1244 1232/1550/1257 1227/1536/1243 -f 1227/1536/1243 1232/1550/1257 1228/1545/1252 -f 1232/1550/1257 1233/1551/1258 1228/1545/1252 -f 1228/1545/1252 1233/1551/1258 1229/1546/1253 -f 1233/1551/1258 1234/1552/1259 1229/1546/1253 -f 1229/1546/1253 1234/1552/1259 1230/1547/1254 -f 1234/1552/1259 1235/1553/1260 1230/1547/1254 -f 1230/1547/1254 1235/1553/1260 1345/1554/1261 1340/1548/1255 -f 1345/1554/1261 1235/1553/1260 1349/1555/1262 -f 1077/1289/1020 1046/1288/1019 1231/1537/1244 -f 1046/1288/1019 1045/1556/1263 1231/1537/1244 -f 1231/1537/1244 1045/1556/1263 1232/1550/1257 -f 1045/1556/1263 1044/1557/1264 1232/1550/1257 -f 1232/1550/1257 1044/1557/1264 1233/1551/1258 -f 1044/1557/1264 1043/1558/1265 1233/1551/1258 -f 1233/1551/1258 1043/1558/1265 1234/1552/1259 -f 1043/1558/1265 1042/1559/1266 1234/1552/1259 -f 1234/1552/1259 1042/1559/1266 1235/1553/1260 -f 1235/1553/1260 1042/1559/1266 1354/1560/1267 1353/1561/1268 -f 1349/1555/1262 1235/1553/1260 1353/1561/1268 -f 1061/1498/1205 1070/1499/1206 1012/1292/1023 -f 1062/1497/1204 1236/1562/1269 1061/1498/1205 -f 1063/1496/1203 1237/1563/1270 1062/1497/1204 -f 1239/1564/1271 1063/1496/1203 1358/1495/1202 1361/1565/1272 -f 1362/1566/1273 1363/1567/1274 1242/1568/1275 -f 1061/1498/1205 1236/1562/1269 1070/1499/1206 -f 1236/1562/1269 1069/1500/1207 1070/1499/1206 -f 1062/1497/1204 1237/1563/1270 1236/1562/1269 -f 1237/1563/1270 1238/1569/1276 1236/1562/1269 -f 1236/1562/1269 1238/1569/1276 1069/1500/1207 -f 1238/1569/1276 1068/1502/1209 1069/1500/1207 -f 1063/1496/1203 1239/1564/1271 1237/1563/1270 -f 1239/1564/1271 1240/1570/1277 1237/1563/1270 -f 1237/1563/1270 1240/1570/1277 1238/1569/1276 -f 1240/1570/1277 1241/1571/1278 1238/1569/1276 -f 1238/1569/1276 1241/1571/1278 1068/1502/1209 -f 1241/1571/1278 1067/1504/1211 1068/1502/1209 -f 1242/1568/1275 1239/1564/1271 1361/1565/1272 1362/1566/1273 -f 1242/1568/1275 1243/1572/1279 1239/1564/1271 -f 1239/1564/1271 1243/1572/1279 1240/1570/1277 -f 1243/1572/1279 1244/1573/1280 1240/1570/1277 -f 1240/1570/1277 1244/1573/1280 1241/1571/1278 -f 1244/1573/1280 1245/1574/1281 1241/1571/1278 -f 1241/1571/1278 1245/1574/1281 1067/1504/1211 -f 1245/1574/1281 1066/1506/1213 1067/1504/1211 -f 1363/1567/1274 1372/1575/1282 1242/1568/1275 -f 1246/1576/1283 1242/1568/1275 1372/1575/1282 1375/1577/1284 -f 1242/1568/1275 1246/1576/1283 1243/1572/1279 -f 1246/1576/1283 1247/1578/1285 1243/1572/1279 -f 1243/1572/1279 1247/1578/1285 1244/1573/1280 -f 1247/1578/1285 1248/1579/1286 1244/1573/1280 -f 1244/1573/1280 1248/1579/1286 1245/1574/1281 -f 1248/1579/1286 1249/1580/1287 1245/1574/1281 -f 1245/1574/1281 1249/1580/1287 1066/1506/1213 -f 1249/1580/1287 1065/1508/1215 1066/1506/1213 -f 1377/1581/1288 1378/1582/1289 1250/1583/1290 -f 1250/1583/1290 1246/1576/1283 1375/1577/1284 1377/1581/1288 -f 1250/1583/1290 1251/1584/1291 1246/1576/1283 -f 1246/1576/1283 1251/1584/1291 1247/1578/1285 -f 1251/1584/1291 1252/1585/1292 1247/1578/1285 -f 1247/1578/1285 1252/1585/1292 1248/1579/1286 -f 1252/1585/1292 1253/1586/1293 1248/1579/1286 -f 1248/1579/1286 1253/1586/1293 1249/1580/1287 -f 1253/1586/1293 1254/1587/1294 1249/1580/1287 -f 1249/1580/1287 1254/1587/1294 1065/1508/1215 -f 1254/1587/1294 1064/1290/1021 1065/1508/1215 -f 1378/1582/1289 1385/1588/1295 1250/1583/1290 -f 1027/1589/1296 1250/1583/1290 1385/1588/1295 1376/1590/1297 -f 1250/1583/1290 1027/1589/1296 1251/1584/1291 -f 1027/1589/1296 1026/1591/1298 1251/1584/1291 -f 1251/1584/1291 1026/1591/1298 1252/1585/1292 -f 1026/1591/1298 1025/1592/1299 1252/1585/1292 -f 1252/1585/1292 1025/1592/1299 1253/1586/1293 -f 1025/1592/1299 1024/1593/1300 1253/1586/1293 -f 1253/1586/1293 1024/1593/1300 1254/1587/1294 -f 1024/1593/1300 1023/1594/1301 1254/1587/1294 -f 1254/1587/1294 1023/1594/1301 1064/1290/1021 -f 1023/1594/1301 1010/1280/1012 1064/1290/1021 -f 1047/1531/1238 1060/1439/1146 1014/1298/1029 -f 1048/1530/1237 1255/1595/1302 1047/1531/1238 -f 1049/1529/1236 1256/1596/1303 1048/1530/1237 -f 1050/1528/1235 1258/1597/1304 1049/1529/1236 -f 1051/1527/1234 1261/1598/1305 1050/1528/1235 -f 1052/1526/1233 1265/1599/1306 1051/1527/1234 -f 1053/1286/1017 1270/1600/1307 1052/1526/1233 -f 1047/1531/1238 1255/1595/1302 1060/1439/1146 -f 1255/1595/1302 1059/1440/1147 1060/1439/1146 -f 1048/1530/1237 1256/1596/1303 1255/1595/1302 -f 1256/1596/1303 1257/1601/1308 1255/1595/1302 -f 1255/1595/1302 1257/1601/1308 1059/1440/1147 -f 1257/1601/1308 1058/1442/1149 1059/1440/1147 -f 1049/1529/1236 1258/1597/1304 1256/1596/1303 -f 1258/1597/1304 1259/1602/1309 1256/1596/1303 -f 1256/1596/1303 1259/1602/1309 1257/1601/1308 -f 1259/1602/1309 1260/1603/1310 1257/1601/1308 -f 1257/1601/1308 1260/1603/1310 1058/1442/1149 -f 1260/1603/1310 1057/1444/1151 1058/1442/1149 -f 1050/1528/1235 1261/1598/1305 1258/1597/1304 -f 1261/1598/1305 1262/1604/1311 1258/1597/1304 -f 1258/1597/1304 1262/1604/1311 1259/1602/1309 -f 1262/1604/1311 1263/1605/1312 1259/1602/1309 -f 1259/1602/1309 1263/1605/1312 1260/1603/1310 -f 1263/1605/1312 1264/1606/1313 1260/1603/1310 -f 1260/1603/1310 1264/1606/1313 1057/1444/1151 -f 1264/1606/1313 1056/1446/1153 1057/1444/1151 -f 1051/1527/1234 1265/1599/1306 1261/1598/1305 -f 1265/1599/1306 1266/1607/1314 1261/1598/1305 -f 1261/1598/1305 1266/1607/1314 1262/1604/1311 -f 1266/1607/1314 1267/1608/1315 1262/1604/1311 -f 1262/1604/1311 1267/1608/1315 1263/1605/1312 -f 1267/1608/1315 1268/1609/1316 1263/1605/1312 -f 1263/1605/1312 1268/1609/1316 1264/1606/1313 -f 1268/1609/1316 1269/1610/1317 1264/1606/1313 -f 1264/1606/1313 1269/1610/1317 1056/1446/1153 -f 1269/1610/1317 1055/1448/1155 1056/1446/1153 -f 1052/1526/1233 1270/1600/1307 1265/1599/1306 -f 1270/1600/1307 1271/1611/1318 1265/1599/1306 -f 1265/1599/1306 1271/1611/1318 1266/1607/1314 -f 1271/1611/1318 1272/1612/1319 1266/1607/1314 -f 1266/1607/1314 1272/1612/1319 1267/1608/1315 -f 1272/1612/1319 1273/1613/1320 1267/1608/1315 -f 1267/1608/1315 1273/1613/1320 1268/1609/1316 -f 1273/1613/1320 1274/1614/1321 1268/1609/1316 -f 1268/1609/1316 1274/1614/1321 1269/1610/1317 -f 1274/1614/1321 1275/1615/1322 1269/1610/1317 -f 1269/1610/1317 1275/1615/1322 1055/1448/1155 -f 1275/1615/1322 1054/1291/1022 1055/1448/1155 -f 1053/1286/1017 1034/1282/1014 1270/1600/1307 -f 1034/1282/1014 1033/1616/1323 1270/1600/1307 -f 1270/1600/1307 1033/1616/1323 1271/1611/1318 -f 1033/1616/1323 1032/1617/1324 1271/1611/1318 -f 1271/1611/1318 1032/1617/1324 1272/1612/1319 -f 1032/1617/1324 1031/1618/1325 1272/1612/1319 -f 1272/1612/1319 1031/1618/1325 1273/1613/1320 -f 1031/1618/1325 1030/1619/1326 1273/1613/1320 -f 1273/1613/1320 1030/1619/1326 1274/1614/1321 -f 1030/1619/1326 1029/1620/1327 1274/1614/1321 -f 1274/1614/1321 1029/1620/1327 1275/1615/1322 -f 1029/1620/1327 1028/1621/1328 1275/1615/1322 -f 1275/1615/1322 1028/1621/1328 1054/1291/1022 -f 1028/1621/1328 1011/1287/1018 1054/1291/1022 -f 1041/1622/1329 1046/1288/1019 1011/1287/1018 -f 1040/1623/1330 1276/1624/1331 1041/1622/1329 -f 1039/1625/1332 1277/1626/1333 1040/1623/1330 -f 1038/1627/1334 1279/1628/1335 1039/1625/1332 -f 1037/1629/1336 1282/1630/1337 1038/1627/1334 -f 1036/1631/1338 1286/1632/1339 1037/1629/1336 -f 1035/1283/1015 1291/1633/1340 1036/1631/1338 -f 1041/1622/1329 1276/1624/1331 1046/1288/1019 -f 1276/1624/1331 1045/1556/1263 1046/1288/1019 -f 1040/1623/1330 1277/1626/1333 1276/1624/1331 -f 1277/1626/1333 1278/1634/1341 1276/1624/1331 -f 1276/1624/1331 1278/1634/1341 1045/1556/1263 -f 1278/1634/1341 1044/1557/1264 1045/1556/1263 -f 1039/1625/1332 1279/1628/1335 1277/1626/1333 -f 1279/1628/1335 1280/1635/1342 1277/1626/1333 -f 1277/1626/1333 1280/1635/1342 1278/1634/1341 -f 1280/1635/1342 1281/1636/1343 1278/1634/1341 -f 1278/1634/1341 1281/1636/1343 1044/1557/1264 -f 1281/1636/1343 1043/1558/1265 1044/1557/1264 -f 1038/1627/1334 1282/1630/1337 1279/1628/1335 -f 1282/1630/1337 1283/1637/1344 1279/1628/1335 -f 1279/1628/1335 1283/1637/1344 1280/1635/1342 -f 1283/1637/1344 1284/1638/1345 1280/1635/1342 -f 1280/1635/1342 1284/1638/1345 1281/1636/1343 -f 1284/1638/1345 1285/1639/1346 1281/1636/1343 -f 1281/1636/1343 1285/1639/1346 1043/1558/1265 -f 1285/1639/1346 1042/1559/1266 1043/1558/1265 -f 1037/1629/1336 1286/1632/1339 1282/1630/1337 -f 1286/1632/1339 1287/1640/1347 1282/1630/1337 -f 1282/1630/1337 1287/1640/1347 1283/1637/1344 -f 1287/1640/1347 1288/1641/1348 1283/1637/1344 -f 1283/1637/1344 1288/1641/1348 1284/1638/1345 -f 1288/1641/1348 1289/1642/1349 1284/1638/1345 -f 1284/1638/1345 1289/1642/1349 1285/1639/1346 -f 1289/1642/1349 1290/1643/1350 1285/1639/1346 -f 1285/1639/1346 1290/1643/1350 1042/1559/1266 -f 1042/1559/1266 1290/1643/1350 1388/1644/1351 1354/1560/1267 -f 1036/1631/1338 1291/1633/1340 1286/1632/1339 -f 1291/1633/1340 1292/1645/1352 1286/1632/1339 -f 1286/1632/1339 1292/1645/1352 1287/1640/1347 -f 1287/1640/1347 1292/1645/1352 1346/1646/1353 1394/1647/1354 -f 1288/1641/1348 1287/1640/1347 1394/1647/1354 1395/1648/1355 -f 1398/1649/1356 1288/1641/1348 1395/1648/1355 -f 1289/1642/1349 1288/1641/1348 1398/1649/1356 1401/1650/1357 -f 1402/1651/1358 1289/1642/1349 1401/1650/1357 -f 1290/1643/1350 1289/1642/1349 1402/1651/1358 1406/1652/1359 -f 1408/1653/1360 1290/1643/1350 1406/1652/1359 -f 1388/1644/1351 1290/1643/1350 1408/1653/1360 -f 1291/1633/1340 1035/1283/1015 1339/1285/1016 1412/1654/1361 -f 1335/1655/1362 1291/1633/1340 1412/1654/1361 -f 1292/1645/1352 1291/1633/1340 1335/1655/1362 1334/1656/1363 -f 1341/1657/1364 1292/1645/1352 1334/1656/1363 -f 1346/1646/1353 1292/1645/1352 1341/1657/1364 -f 1028/1621/1328 1041/1658/1329 1011/1287/1018 -f 1029/1620/1327 1293/1659/1365 1028/1621/1328 -f 1030/1619/1326 1294/1660/1366 1029/1620/1327 -f 1031/1618/1325 1296/1661/1367 1030/1619/1326 -f 1032/1617/1324 1299/1662/1368 1031/1618/1325 -f 1033/1616/1323 1303/1663/1369 1032/1617/1324 -f 1034/1282/1014 1308/1664/1370 1033/1616/1323 -f 1028/1621/1328 1293/1659/1365 1041/1658/1329 -f 1293/1659/1365 1040/1665/1330 1041/1658/1329 -f 1029/1620/1327 1294/1660/1366 1293/1659/1365 -f 1294/1660/1366 1295/1666/1371 1293/1659/1365 -f 1293/1659/1365 1295/1666/1371 1040/1665/1330 -f 1295/1666/1371 1039/1667/1332 1040/1665/1330 -f 1030/1619/1326 1296/1661/1367 1294/1660/1366 -f 1296/1661/1367 1297/1668/1372 1294/1660/1366 -f 1294/1660/1366 1297/1668/1372 1295/1666/1371 -f 1297/1668/1372 1298/1669/1373 1295/1666/1371 -f 1295/1666/1371 1298/1669/1373 1039/1667/1332 -f 1298/1669/1373 1038/1670/1334 1039/1667/1332 -f 1031/1618/1325 1299/1662/1368 1296/1661/1367 -f 1299/1662/1368 1300/1671/1374 1296/1661/1367 -f 1296/1661/1367 1300/1671/1374 1297/1668/1372 -f 1300/1671/1374 1301/1672/1375 1297/1668/1372 -f 1297/1668/1372 1301/1672/1375 1298/1669/1373 -f 1301/1672/1375 1302/1673/1376 1298/1669/1373 -f 1298/1669/1373 1302/1673/1376 1038/1670/1334 -f 1302/1673/1376 1037/1674/1336 1038/1670/1334 -f 1032/1617/1324 1303/1663/1369 1299/1662/1368 -f 1303/1663/1369 1304/1675/1377 1299/1662/1368 -f 1299/1662/1368 1304/1675/1377 1300/1671/1374 -f 1304/1675/1377 1305/1676/1378 1300/1671/1374 -f 1300/1671/1374 1305/1676/1378 1301/1672/1375 -f 1305/1676/1378 1306/1677/1379 1301/1672/1375 -f 1301/1672/1375 1306/1677/1379 1302/1673/1376 -f 1306/1677/1379 1307/1678/1380 1302/1673/1376 -f 1302/1673/1376 1307/1678/1380 1037/1674/1336 -f 1307/1678/1380 1036/1679/1338 1037/1674/1336 -f 1033/1616/1323 1308/1664/1370 1303/1663/1369 -f 1308/1664/1370 1309/1680/1381 1303/1663/1369 -f 1303/1663/1369 1309/1680/1381 1304/1675/1377 -f 1309/1680/1381 1310/1681/1382 1304/1675/1377 -f 1304/1675/1377 1310/1681/1382 1305/1676/1378 -f 1310/1681/1382 1311/1682/1383 1305/1676/1378 -f 1305/1676/1378 1311/1682/1383 1306/1677/1379 -f 1311/1682/1383 1312/1683/1384 1306/1677/1379 -f 1306/1677/1379 1312/1683/1384 1307/1678/1380 -f 1312/1683/1384 1313/1684/1385 1307/1678/1380 -f 1307/1678/1380 1313/1684/1385 1036/1679/1338 -f 1313/1684/1385 1035/1685/1015 1036/1679/1338 -f 1034/1282/1014 1022/1281/1013 1308/1664/1370 -f 1022/1281/1013 1021/1686/1386 1308/1664/1370 -f 1308/1664/1370 1021/1686/1386 1309/1680/1381 -f 1021/1686/1386 1020/1687/1387 1309/1680/1381 -f 1309/1680/1381 1020/1687/1387 1310/1681/1382 -f 1020/1687/1387 1019/1688/1388 1310/1681/1382 -f 1310/1681/1382 1019/1688/1388 1311/1682/1383 -f 1019/1688/1388 1018/1689/1389 1311/1682/1383 -f 1311/1682/1383 1018/1689/1389 1312/1683/1384 -f 1018/1689/1389 1017/1690/1390 1312/1683/1384 -f 1312/1683/1384 1017/1690/1390 1313/1684/1385 -f 1017/1690/1390 1016/1691/1009 1313/1684/1385 -f 1313/1684/1385 1016/1691/1009 1035/1685/1015 -f 1009/1692/1011 1035/1685/1015 1016/1691/1009 -f 1355/1693/1391 1320/1694/1392 1356/1695/1393 -f 1359/1696/1394 1325/1697/1395 1360/1698/1396 -f 1366/1699/1397 1367/1700/1398 1314/1701/1399 -f 1371/1702/1400 1366/1699/1397 1314/1701/1399 -f 1314/1701/1399 1027/1589/1296 1376/1590/1297 1371/1702/1400 -f 1379/1703/1401 1315/1704/1402 1380/1705/1403 -f 1315/1704/1402 1314/1701/1399 1367/1700/1398 1380/1705/1403 -f 1315/1704/1402 1316/1706/1404 1314/1701/1399 -f 1314/1701/1399 1316/1706/1404 1027/1589/1296 -f 1316/1706/1404 1026/1591/1298 1027/1589/1296 -f 1390/1707/1405 1317/1708/1406 1391/1709/1407 -f 1317/1708/1406 1315/1704/1402 1379/1703/1401 1391/1709/1407 -f 1317/1708/1406 1318/1710/1408 1315/1704/1402 -f 1315/1704/1402 1318/1710/1408 1316/1706/1404 -f 1318/1710/1408 1319/1711/1409 1316/1706/1404 -f 1316/1706/1404 1319/1711/1409 1026/1591/1298 -f 1319/1711/1409 1025/1592/1299 1026/1591/1298 -f 1356/1695/1393 1320/1694/1392 1399/1712/1410 -f 1320/1694/1392 1321/1713/1411 1403/1714/1412 1399/1712/1410 -f 1321/1713/1411 1317/1708/1406 1390/1707/1405 1403/1714/1412 -f 1321/1713/1411 1322/1715/1413 1317/1708/1406 -f 1317/1708/1406 1322/1715/1413 1318/1710/1408 -f 1322/1715/1413 1323/1716/1414 1318/1710/1408 -f 1318/1710/1408 1323/1716/1414 1319/1711/1409 -f 1323/1716/1414 1324/1717/1415 1319/1711/1409 -f 1319/1711/1409 1324/1717/1415 1025/1592/1299 -f 1324/1717/1415 1024/1593/1300 1025/1592/1299 -f 1325/1697/1395 1320/1694/1392 1355/1693/1391 1360/1698/1396 -f 1325/1697/1395 1326/1718/1416 1320/1694/1392 -f 1320/1694/1392 1326/1718/1416 1321/1713/1411 -f 1326/1718/1416 1327/1719/1417 1321/1713/1411 -f 1321/1713/1411 1327/1719/1417 1322/1715/1413 -f 1327/1719/1417 1328/1720/1418 1322/1715/1413 -f 1322/1715/1413 1328/1720/1418 1323/1716/1414 -f 1328/1720/1418 1329/1721/1419 1323/1716/1414 -f 1323/1716/1414 1329/1721/1419 1324/1717/1415 -f 1329/1721/1419 1330/1722/1420 1324/1717/1415 -f 1324/1717/1415 1330/1722/1420 1024/1593/1300 -f 1330/1722/1420 1023/1594/1301 1024/1593/1300 -f 1016/1277/1009 1325/1697/1395 1359/1696/1394 1331/1278/1010 -f 1016/1277/1009 1017/1723/1390 1325/1697/1395 -f 1325/1697/1395 1017/1723/1390 1326/1718/1416 -f 1017/1723/1390 1018/1724/1389 1326/1718/1416 -f 1326/1718/1416 1018/1724/1389 1327/1719/1417 -f 1018/1724/1389 1019/1725/1388 1327/1719/1417 -f 1327/1719/1417 1019/1725/1388 1328/1720/1418 -f 1019/1725/1388 1020/1726/1387 1328/1720/1418 -f 1328/1720/1418 1020/1726/1387 1329/1721/1419 -f 1020/1726/1387 1021/1727/1386 1329/1721/1419 -f 1329/1721/1419 1021/1727/1386 1330/1722/1420 -f 1021/1727/1386 1022/1728/1013 1330/1722/1420 -f 1330/1722/1420 1022/1728/1013 1023/1594/1301 -f 1022/1728/1013 1010/1280/1012 1023/1594/1301 -f 1373/1361/1084 1374/1360/1083 1501/1729/1421 1498/1730/1422 -f 1423/1731/1423 1436/1732/1424 1511/1733/1425 1500/1734/1426 1485/1735/1427 1484/1736/1428 1486/1737/1429 1495/1738/1430 1494/1739/1431 1499/1740/1432 1447/1741/1433 1446/1742/1434 1464/1743/1435 1463/1744/1436 1510/1745/1437 1475/1746/1438 1474/1747/1439 1449/1748/1440 1450/1749/1441 1458/1750/1442 1430/1751/1443 1429/1752/1444 1505/1753/1445 1493/1754/1446 1491/1755/1447 1492/1756/1448 1451/1757/1449 1444/1758/1450 1445/1759/1451 1482/1760/1452 1477/1761/1453 1478/1762/1454 1498/1763/1422 1501/1764/1421 1442/1765/1455 1443/1766/1456 1472/1767/1457 1473/1768/1458 1479/1769/1459 1448/1770/1460 1439/1771/1461 1438/1772/1462 1469/1773/1463 1422/1774/1464 1421/1775/1465 1488/1776/1466 1487/1777/1467 1483/1778/1424 1480/1779/1468 1481/1780/1469 1496/1781/1470 1497/1782/1471 1434/1783/1472 1435/1784/1473 1476/1785/1474 1470/1786/1475 1471/1787/1476 1506/1788/1477 1490/1789/1478 1489/1790/1479 1468/1791/1480 1457/1792/1481 1428/1793/1482 1427/1794/1483 1512/1795/1484 1462/1796/1485 1460/1797/1486 1461/1798/1487 1459/1799/1488 1433/1800/1489 1431/1801/1490 1432/1802/1491 1452/1803/1492 1508/1804/1493 1507/1805/1494 1504/1806/1495 1440/1807/1496 1441/1808/1497 1502/1809/1498 1456/1810/1499 1454/1811/1500 1455/1812/1501 1466/1813/1502 1465/1814/1503 1467/1815/1504 1509/1816/1505 1426/1817/1506 1425/1818/1507 1437/1819/1508 1503/1820/1509 1453/1821/1510 1424/1822/1511 -f 1420/1494/1201 1417/1489/1196 1485/1823/1427 1500/1824/1426 -f 1412/1654/1361 1339/1285/1016 1433/1825/1489 1459/1826/1488 -f 1399/1712/1410 1403/1714/1412 1441/1827/1497 1440/1828/1496 -f 1387/1365/1088 1389/1364/1087 1473/1829/1458 1472/1830/1457 -f 1398/1649/1356 1395/1648/1355 1428/1831/1482 1457/1832/1481 -f 1340/1548/1255 1345/1554/1261 1434/1833/1472 1497/1834/1471 -f 1401/1650/1357 1398/1649/1356 1457/1832/1481 1468/1835/1480 -f 1410/1374/1096 1407/1372/1094 1505/1836/1445 1429/1837/1444 -f 1363/1567/1274 1362/1566/1273 1423/1838/1423 1424/1839/1511 -f 1344/1386/1108 1342/1384/1106 1475/1840/1438 1510/1841/1437 -f 1334/1656/1363 1335/1655/1362 1461/1842/1487 1460/1843/1486 -f 1382/1363/1086 1384/1362/1085 1443/1844/1456 1442/1845/1455 -f 1396/1484/1191 1393/1483/1190 1438/1846/1462 1439/1847/1461 -f 1342/1384/1106 1333/1382/1104 1474/1848/1439 1475/1840/1438 -f 1337/1544/1251 1338/1549/1256 1496/1849/1470 1481/1850/1469 -f 1404/1371/1093 1015/1370/1033 1491/1851/1447 1493/1852/1446 -f 1389/1364/1087 1397/1367/1090 1479/1853/1459 1473/1829/1458 -f 1405/1485/1192 1383/1479/1186 1488/1854/1466 1421/1855/1465 -f 1349/1555/1262 1353/1561/1268 1476/1856/1474 1435/1857/1473 -f 1361/1565/1272 1358/1495/1202 1511/1858/1425 1436/1859/1424 -f 1390/1707/1405 1391/1709/1407 1456/1860/1499 1502/1861/1498 -f 1374/1360/1083 1382/1363/1086 1442/1845/1455 1501/1729/1421 -f 1348/1346/1073 1351/1350/1076 1445/1862/1451 1444/1863/1450 -f 1341/1657/1364 1334/1656/1363 1460/1843/1486 1462/1864/1485 -f 1369/1395/1116 1368/1394/1115 1447/1865/1433 1499/1866/1432 -f 1353/1561/1268 1354/1560/1267 1470/1867/1475 1476/1856/1474 -f 1413/1377/1099 1411/1375/1097 1430/1868/1443 1458/1869/1442 -f 1350/1388/1110 1344/1386/1108 1510/1841/1437 1463/1870/1436 -f 1418/1491/1198 1419/1493/1200 1495/1871/1430 1486/1872/1429 -f 1338/1549/1256 1340/1548/1255 1497/1834/1471 1496/1849/1470 -f 1368/1394/1115 1364/1392/1114 1446/1873/1434 1447/1865/1433 -f 1335/1655/1362 1412/1654/1361 1459/1826/1488 1461/1842/1487 -f 1347/1347/1074 1348/1346/1073 1444/1863/1450 1451/1874/1449 -f 1333/1382/1104 1332/1379/1101 1449/1875/1440 1474/1848/1439 -f 1015/1344/1033 1343/1343/1072 1492/1876/1448 1491/1877/1447 -f 1376/1590/1297 1385/1588/1295 1426/1878/1506 1509/1879/1505 -f 1375/1577/1284 1372/1575/1282 1453/1880/1510 1503/1881/1509 -f 1419/1493/1200 1415/1487/1194 1494/1882/1431 1495/1871/1430 -f 1365/1358/1081 1370/1359/1082 1478/1883/1454 1477/1884/1453 -f 1357/1390/1112 1350/1388/1110 1463/1870/1436 1464/1885/1435 -f 1351/1350/1076 1352/1349/1075 1482/1886/1452 1445/1862/1451 -f 1417/1489/1196 1416/1488/1195 1484/1887/1428 1485/1823/1427 -f 1336/1481/1188 1337/1544/1251 1481/1850/1469 1480/1888/1468 -f 1385/1588/1295 1378/1582/1289 1425/1889/1507 1426/1878/1506 -f 1393/1483/1190 1392/1482/1189 1469/1890/1463 1438/1846/1462 -f 1331/1278/1010 1359/1696/1394 1452/1891/1492 1432/1892/1491 -f 1416/1488/1195 1418/1491/1198 1486/1872/1429 1484/1887/1428 -f 1400/1368/1091 1396/1484/1191 1439/1847/1461 1448/1893/1460 -f 1384/1362/1085 1387/1365/1088 1472/1830/1457 1443/1844/1456 -f 1367/1700/1398 1366/1699/1397 1465/1894/1503 1466/1895/1502 -f 1343/1343/1072 1347/1347/1074 1451/1874/1449 1492/1876/1448 -f 1395/1648/1355 1394/1647/1354 1427/1896/1483 1428/1831/1482 -f 1332/1379/1101 1414/1378/1100 1450/1897/1441 1449/1875/1440 -f 1383/1479/1186 1381/1478/1185 1487/1898/1467 1488/1854/1466 -f 1372/1575/1282 1363/1567/1274 1424/1839/1511 1453/1880/1510 -f 1366/1699/1397 1371/1702/1400 1467/1899/1504 1465/1894/1503 -f 1403/1714/1412 1390/1707/1405 1502/1861/1498 1441/1827/1497 -f 1356/1695/1393 1399/1712/1410 1440/1828/1496 1504/1900/1495 -f 1406/1652/1359 1402/1651/1358 1489/1901/1479 1490/1902/1478 -f 1354/1560/1267 1388/1644/1351 1471/1903/1476 1470/1867/1475 -f 1392/1482/1189 1409/1486/1193 1422/1904/1464 1469/1890/1463 -f 1352/1349/1075 1365/1358/1081 1477/1884/1453 1482/1886/1452 -f 1359/1696/1394 1360/1698/1396 1508/1905/1493 1452/1891/1492 -f 1355/1693/1391 1356/1695/1393 1504/1900/1495 1507/1906/1494 -f 1370/1359/1082 1373/1361/1084 1498/1730/1422 1478/1883/1454 -f 1407/1372/1094 1404/1371/1093 1493/1852/1446 1505/1836/1445 -f 1009/1279/1011 1331/1278/1010 1432/1892/1491 1431/1907/1490 -f 1379/1703/1401 1380/1705/1403 1455/1908/1501 1454/1909/1500 -f 1360/1698/1396 1355/1693/1391 1507/1906/1494 1508/1905/1493 -f 1402/1651/1358 1401/1650/1357 1468/1835/1480 1489/1901/1479 -f 1380/1705/1403 1367/1700/1398 1466/1895/1502 1455/1908/1501 -f 1408/1653/1360 1406/1652/1359 1490/1902/1478 1506/1910/1477 -f 1388/1644/1351 1408/1653/1360 1506/1910/1477 1471/1903/1476 -f 1339/1285/1016 1009/1284/1011 1431/1911/1490 1433/1825/1489 -f 1381/1478/1185 1386/1480/1187 1483/1912/1424 1487/1898/1467 -f 1397/1367/1090 1400/1368/1091 1448/1893/1460 1479/1853/1459 -f 1345/1554/1261 1349/1555/1262 1435/1857/1473 1434/1833/1472 -f 1411/1375/1097 1410/1374/1096 1429/1837/1444 1430/1868/1443 -f 1377/1581/1288 1375/1577/1284 1503/1881/1509 1437/1913/1508 -f 1346/1646/1353 1341/1657/1364 1462/1864/1485 1512/1914/1484 -f 1358/1495/1202 1420/1494/1201 1500/1824/1426 1511/1858/1425 -f 1362/1566/1273 1361/1565/1272 1436/1859/1424 1423/1838/1423 -f 1371/1702/1400 1376/1590/1297 1509/1879/1505 1467/1899/1504 -f 1409/1486/1193 1405/1485/1192 1421/1855/1465 1422/1904/1464 -f 1378/1582/1289 1377/1581/1288 1437/1913/1508 1425/1889/1507 -f 1415/1487/1194 1369/1395/1116 1499/1866/1432 1494/1882/1431 -f 1394/1647/1354 1346/1646/1353 1512/1914/1484 1427/1896/1483 -f 1414/1378/1100 1413/1377/1099 1458/1869/1442 1450/1897/1441 -f 1391/1709/1407 1379/1703/1401 1454/1909/1500 1456/1860/1499 -f 1386/1480/1187 1336/1481/1188 1480/1888/1468 1483/1912/1424 -f 1364/1392/1114 1357/1390/1112 1464/1885/1435 1446/1873/1434 -o Center_Icosphere.003 -v 0.705168 -0.706933 0.054627 -v 0.784047 0.195005 0.589273 -v 0.053877 -0.512662 0.856898 -v -0.314930 -0.948761 -0.025924 -v 0.187318 -0.510614 -0.839158 -v 0.866518 0.196272 -0.458938 -v -0.187318 0.510614 0.839158 -v -0.866518 -0.196272 0.458938 -v -0.784047 -0.195005 -0.589273 -v -0.053877 0.512662 -0.856898 -v 0.314930 0.948761 0.025924 -v -0.705168 0.706933 -0.054627 -v 0.144294 -0.572687 0.806975 -v 0.243391 -0.630355 0.737166 -v 0.346342 -0.680241 0.646003 -v 0.446156 -0.716859 0.535777 -v 0.535506 -0.736665 0.412986 -v 0.609144 -0.739453 0.286624 -v 0.665275 -0.728087 0.165221 -v 0.762621 -0.633742 0.129541 -v 0.814168 -0.540747 0.211478 -v 0.853617 -0.428359 0.296391 -v 0.875337 -0.300906 0.378472 -v 0.876527 -0.166395 0.451677 -v 0.858466 -0.034235 0.511726 -v 0.825719 0.087738 0.557216 -v 0.738889 0.113639 0.664176 -v 0.675590 0.020316 0.736998 -v 0.592781 -0.081755 0.801204 -v 0.492519 -0.186714 0.850037 -v 0.380706 -0.287294 0.878934 -v 0.265538 -0.377098 0.887292 -v 0.154810 -0.452440 0.878255 -v 0.913199 0.209167 -0.349737 -v 0.950084 0.220090 -0.221134 -v 0.970774 0.227473 -0.076513 -v 0.970174 0.229986 0.076609 -v 0.946821 0.227105 0.227934 -v 0.903770 0.219378 0.367522 -v 0.847229 0.208154 0.488748 -v 0.773616 -0.633573 -0.010205 -v 0.837325 -0.540392 -0.082848 -v 0.889546 -0.427807 -0.160278 -v 0.923811 -0.300161 -0.237646 -v 0.936410 -0.165475 -0.309441 -v 0.927937 -0.033169 -0.371257 -v 0.902684 0.088919 -0.421016 -v -0.199892 -0.979672 -0.016910 -v -0.067284 -0.997713 -0.006499 -v 0.078545 -0.996898 0.004976 -v 0.229376 -0.973192 0.016871 -v 0.374828 -0.926660 0.028371 -v 0.505586 -0.861906 0.038737 -v 0.616106 -0.786228 0.047524 -v 0.008293 -0.604928 0.796237 -v -0.043046 -0.698260 0.714549 -v -0.098086 -0.785600 0.610911 -v -0.153444 -0.859005 0.488432 -v -0.205204 -0.912262 0.354498 -v -0.250162 -0.943164 0.218770 -v -0.286723 -0.953772 0.090048 -v 0.268827 -0.570775 -0.775853 -v 0.355799 -0.628629 -0.691544 -v 0.443236 -0.678753 -0.585523 -v 0.524591 -0.715655 -0.461132 -v 0.593642 -0.735772 -0.325926 -v 0.646613 -0.738877 -0.189610 -v 0.683066 -0.727814 -0.060895 -v -0.268933 -0.953499 -0.136069 -v -0.212692 -0.942589 -0.257464 -v -0.147068 -0.911370 -0.384417 -v -0.075010 -0.857800 -0.508480 -v -0.001192 -0.784112 -0.620618 -v 0.069362 -0.696534 -0.714164 -v 0.132826 -0.603016 -0.786593 -v 0.290338 -0.450359 -0.844323 -v 0.401103 -0.375016 -0.835751 -v 0.513530 -0.285255 -0.809269 -v 0.619429 -0.184765 -0.763000 -v 0.710800 -0.079943 -0.698836 -v 0.782530 0.021958 -0.622225 -v 0.833644 0.115094 -0.540177 -v 0.398435 0.911422 0.102755 -v 0.485442 0.853970 0.187300 -v 0.570260 0.773870 0.275551 -v 0.645962 0.672292 0.361603 -v 0.706514 0.554945 0.439174 -v 0.748892 0.430674 0.503668 -v 0.773693 0.308477 0.553390 -v 0.850658 0.309659 -0.424844 -v 0.818363 0.431740 -0.379318 -v 0.766397 0.555865 -0.321947 -v 0.694437 0.673036 -0.254518 -v 0.606190 0.774422 -0.181120 -v 0.508599 0.854325 -0.107027 -v 0.409430 0.911591 -0.036992 -v -0.167631 0.408178 0.897380 -v -0.142664 0.286177 0.947496 -v -0.112543 0.147413 0.982651 -v -0.078435 -0.001204 0.996918 -v -0.042488 -0.149794 0.987804 -v -0.007213 -0.288478 0.957459 -v 0.025307 -0.410363 0.911571 -v 0.706734 0.250063 0.661812 -v 0.607867 0.307645 0.732020 -v 0.487702 0.364057 0.793479 -v 0.350750 0.414755 0.839615 -v 0.205572 0.455724 0.866058 -v 0.062362 0.484886 0.872351 -v -0.070284 0.502525 0.861701 -v -0.850658 -0.309659 0.424844 -v -0.818363 -0.431740 0.379318 -v -0.766397 -0.555865 0.321947 -v -0.694437 -0.673036 0.254518 -v -0.606190 -0.774422 0.181120 -v -0.508599 -0.854325 0.107027 -v -0.409430 -0.911591 0.036992 -v -0.065245 -0.504606 0.860881 -v -0.197927 -0.486967 0.850698 -v -0.338396 -0.457764 0.822156 -v -0.477661 -0.416703 0.773433 -v -0.605723 -0.365869 0.706569 -v -0.714808 -0.309287 0.627210 -v -0.801490 -0.251518 0.542543 -v -0.706734 -0.250063 -0.661812 -v -0.607867 -0.307645 -0.732020 -v -0.487702 -0.364057 -0.793479 -v -0.350750 -0.414755 -0.839615 -v -0.205572 -0.455724 -0.866058 -v -0.062362 -0.484886 -0.872351 -v 0.070284 -0.502525 -0.861701 -v -0.398435 -0.911422 -0.102755 -v -0.485442 -0.853970 -0.187300 -v -0.570260 -0.773870 -0.275551 -v -0.645962 -0.672292 -0.361603 -v -0.706514 -0.554945 -0.439174 -v -0.748892 -0.430674 -0.503668 -v -0.773693 -0.308477 -0.553390 -v 0.065245 0.504606 -0.860881 -v 0.197927 0.486967 -0.850698 -v 0.338396 0.457764 -0.822156 -v 0.477661 0.416703 -0.773433 -v 0.605723 0.365869 -0.706569 -v 0.714808 0.309287 -0.627210 -v 0.801490 0.251518 -0.542543 -v 0.167631 -0.408178 -0.897380 -v 0.142664 -0.286177 -0.947496 -v 0.112543 -0.147413 -0.982651 -v 0.078435 0.001204 -0.996918 -v 0.042488 0.149794 -0.987804 -v 0.007213 0.288478 -0.957459 -v -0.025307 0.410363 -0.911571 -v 0.268933 0.953499 0.136069 -v 0.212692 0.942589 0.257464 -v 0.147068 0.911370 0.384417 -v 0.075010 0.857800 0.508480 -v 0.001192 0.784112 0.620618 -v -0.069362 0.696534 0.714164 -v -0.132826 0.603016 0.786593 -v -0.290338 0.450359 0.844323 -v -0.401103 0.375016 0.835751 -v -0.513530 0.285255 0.809269 -v -0.619429 0.184765 0.763000 -v -0.710800 0.079943 0.698836 -v -0.782530 -0.021958 0.622225 -v -0.833644 -0.115094 0.540177 -v -0.913199 -0.209167 0.349737 -v -0.950084 -0.220090 0.221134 -v -0.970774 -0.227473 0.076513 -v -0.970174 -0.229986 -0.076609 -v -0.946821 -0.227105 -0.227934 -v -0.903770 -0.219378 -0.367522 -v -0.847229 -0.208154 -0.488748 -v -0.738889 -0.113639 -0.664176 -v -0.675590 -0.020316 -0.736998 -v -0.592781 0.081755 -0.801204 -v -0.492519 0.186714 -0.850037 -v -0.380706 0.287294 -0.878934 -v -0.265538 0.377098 -0.887292 -v -0.154810 0.452440 -0.878255 -v -0.008293 0.604928 -0.796237 -v 0.043046 0.698260 -0.714549 -v 0.098086 0.785600 -0.610911 -v 0.153444 0.859005 -0.488432 -v 0.205204 0.912262 -0.354498 -v 0.250162 0.943164 -0.218770 -v 0.286723 0.953772 -0.090048 -v -0.683066 0.727814 0.060895 -v -0.646613 0.738877 0.189610 -v -0.593642 0.735772 0.325926 -v -0.524591 0.715655 0.461132 -v -0.443236 0.678753 0.585522 -v -0.355799 0.628629 0.691544 -v -0.268827 0.570775 0.775853 -v 0.199892 0.979672 0.016910 -v 0.067284 0.997713 0.006499 -v -0.078545 0.996898 -0.004976 -v -0.229376 0.973192 -0.016871 -v -0.374828 0.926660 -0.028371 -v -0.505586 0.861906 -0.038737 -v -0.616106 0.786228 -0.047524 -v -0.773616 0.633573 0.010205 -v -0.837325 0.540392 0.082848 -v -0.889546 0.427807 0.160278 -v -0.923811 0.300161 0.237646 -v -0.936410 0.165475 0.309441 -v -0.927937 0.033169 0.371257 -v -0.902684 -0.088919 0.421016 -v -0.762621 0.633742 -0.129541 -v -0.814168 0.540747 -0.211478 -v -0.853617 0.428359 -0.296391 -v -0.875337 0.300906 -0.378472 -v -0.876527 0.166395 -0.451677 -v -0.858466 0.034235 -0.511726 -v -0.825719 -0.087738 -0.557216 -v -0.665275 0.728087 -0.165221 -v -0.609144 0.739453 -0.286624 -v -0.535506 0.736665 -0.412986 -v -0.446156 0.716859 -0.535777 -v -0.346342 0.680241 -0.646003 -v -0.243391 0.630355 -0.737166 -v -0.144294 0.572687 -0.806975 -v -0.563539 0.809549 -0.164483 -v -0.438739 0.884190 -0.160360 -v -0.493593 0.819328 -0.291663 -v -0.294306 0.943523 -0.152147 -v -0.355148 0.888512 -0.290544 -v -0.406799 0.810505 -0.421422 -v -0.138732 0.980404 -0.139863 -v -0.200194 0.938342 -0.281843 -v -0.257946 0.870054 -0.420083 -v -0.307472 0.780877 -0.543776 -v 0.016425 0.992094 -0.124419 -v -0.039921 0.963242 -0.265654 -v -0.098210 0.907922 -0.407470 -v -0.153889 0.828480 -0.538460 -v -0.202990 0.732646 -0.649634 -v 0.160480 0.981188 -0.107312 -v 0.113293 0.963200 -0.243743 -v 0.059924 0.921149 -0.384570 -v 0.003677 0.854640 -0.519208 -v -0.051268 0.768614 -0.637655 -v -0.101283 0.671665 -0.733899 -v -0.719539 0.647191 -0.251810 -v -0.657045 0.649214 -0.383162 -v -0.765644 0.543962 -0.343358 -v -0.574402 0.636071 -0.515244 -v -0.694749 0.535070 -0.480649 -v -0.797115 0.420221 -0.433614 -v -0.475075 0.606443 -0.637598 -v -0.602119 0.511853 -0.612747 -v -0.716455 0.401041 -0.570840 -v -0.809146 0.282668 -0.515153 -v -0.366443 0.562531 -0.741133 -v -0.493491 0.475037 -0.728565 -v -0.614471 0.370620 -0.696467 -v -0.719125 0.256357 -0.645865 -v -0.800841 0.141521 -0.581916 -v -0.257283 0.509306 -0.821226 -v -0.378174 0.428384 -0.820653 -v -0.499134 0.331336 -0.800676 -v -0.610491 0.223411 -0.759861 -v -0.703973 0.112625 -0.701240 -v -0.775525 0.007037 -0.631278 -v -0.834893 0.546558 -0.065027 -v -0.886509 0.438521 -0.147652 -v -0.898775 0.438333 0.008249 -v -0.921049 0.312103 -0.232939 -v -0.945866 0.315999 -0.074037 -v -0.946204 0.311717 0.086781 -v -0.933079 0.174550 -0.314477 -v -0.970953 0.179020 -0.158753 -v -0.983867 0.178822 0.005384 -v -0.970811 0.173970 0.165104 -v -0.921706 0.036080 -0.386209 -v -0.970242 0.037285 -0.239250 -v -0.996218 0.037589 -0.078335 -v -0.995726 0.036894 0.084662 -v -0.970770 0.035326 0.237400 -v -0.890879 -0.093597 -0.444494 -v -0.945703 -0.098257 -0.309825 -v -0.982293 -0.100945 -0.157832 -v -0.994870 -0.101138 0.002029 -v -0.982501 -0.098822 0.157881 -v -0.949413 -0.094496 0.299477 -v -0.750188 0.646720 0.137738 -v -0.810023 0.543280 0.220701 -v -0.709005 0.648416 0.277252 -v -0.855199 0.419329 0.304628 -v -0.761469 0.534046 0.367369 -v -0.648032 0.634940 0.420601 -v -0.879806 0.281583 0.382952 -v -0.796987 0.399805 0.452733 -v -0.690633 0.510494 0.512271 -v -0.569056 0.605000 0.556911 -v -0.882018 0.140274 0.449853 -v -0.811330 0.254941 0.526070 -v -0.715898 0.369063 0.592690 -v -0.601446 0.473380 0.643564 -v -0.477944 0.560819 0.676056 -v -0.864707 0.005667 0.502244 -v -0.804997 0.111074 0.582788 -v -0.721854 0.221701 0.655573 -v -0.618270 0.329507 0.713560 -v -0.501941 0.426484 0.752440 -v -0.382643 0.507382 0.772106 -v -0.582481 0.809258 0.076272 -v -0.533287 0.818719 0.212849 -v -0.458586 0.883886 0.091896 -v -0.467852 0.809568 0.354563 -v -0.396384 0.887879 0.233561 -v -0.314657 0.943210 0.106514 -v -0.388875 0.779628 0.490874 -v -0.320632 0.869092 0.376662 -v -0.241985 0.937701 0.249320 -v -0.159083 0.980091 0.118799 -v -0.302225 0.731123 0.611653 -v -0.236360 0.827214 0.509756 -v -0.160896 0.906960 0.389277 -v -0.081157 0.962609 0.258454 -v -0.003422 0.991789 0.127838 -v -0.214936 0.669920 0.710640 -v -0.150503 0.767091 0.623635 -v -0.077727 0.853390 0.515445 -v -0.001130 0.920211 0.391420 -v 0.073599 0.962591 0.260773 -v 0.141538 0.980897 0.133444 -v 0.383583 0.908701 -0.164703 -v 0.483947 0.840193 -0.244686 -v 0.347055 0.887250 -0.303875 -v 0.580358 0.746767 -0.324845 -v 0.445758 0.805442 -0.390592 -v 0.299621 0.843271 -0.446230 -v 0.664480 0.632005 -0.398792 -v 0.538133 0.698456 -0.471776 -v 0.394009 0.747999 -0.534092 -v 0.243375 0.776762 -0.580869 -v 0.730065 0.504435 -0.461032 -v 0.616210 0.572911 -0.540425 -v 0.480382 0.630168 -0.610018 -v 0.331791 0.670681 -0.663402 -v 0.182495 0.692665 -0.697790 -v 0.775077 0.374618 -0.508838 -v 0.675213 0.439573 -0.592337 -v 0.551989 0.498987 -0.668071 -v 0.411620 0.547240 -0.728764 -v 0.264534 0.580746 -0.769907 -v 0.121821 0.599178 -0.791293 -v -0.130593 0.336475 -0.932593 -v -0.100959 0.200354 -0.974508 -v -0.245432 0.247295 -0.937341 -v -0.066738 0.049383 -0.996548 -v -0.217697 0.098794 -0.971003 -v -0.363022 0.145649 -0.920327 -v -0.029953 -0.106676 -0.993843 -v -0.182860 -0.060109 -0.981300 -v -0.334965 -0.010688 -0.942170 -v -0.474380 0.037723 -0.879512 -v 0.006662 -0.256229 -0.966593 -v -0.143164 -0.217416 -0.965523 -v -0.297197 -0.170923 -0.939393 -v -0.443333 -0.119887 -0.888303 -v -0.571233 -0.068464 -0.817928 -v 0.040596 -0.389802 -0.920003 -v -0.101938 -0.361483 -0.926789 -v -0.252669 -0.322529 -0.912213 -v -0.400812 -0.274395 -0.874103 -v -0.535360 -0.220659 -0.815291 -v -0.648837 -0.165797 -0.742645 -v -0.835485 -0.328627 -0.440420 -v -0.805625 -0.456435 -0.377672 -v -0.887663 -0.344514 -0.305557 -v -0.754537 -0.582979 -0.301346 -v -0.849654 -0.474356 -0.230382 -v -0.922779 -0.353455 -0.153454 -v -0.682992 -0.697934 -0.215430 -v -0.787635 -0.598772 -0.145269 -v -0.874007 -0.480940 -0.069346 -v -0.935357 -0.353648 0.006407 -v -0.596306 -0.792758 -0.126307 -v -0.704689 -0.707279 -0.056297 -v -0.800549 -0.598970 0.018868 -v -0.875139 -0.474746 0.093533 -v -0.924461 -0.345078 0.162150 -v -0.502527 -0.863609 -0.040580 -v -0.608573 -0.792946 0.029595 -v -0.708148 -0.698319 0.104293 -v -0.792270 -0.583557 0.178238 -v -0.854690 -0.457187 0.245939 -v -0.894019 -0.329524 0.303552 -v -0.756962 -0.167457 0.631638 -v -0.656232 -0.222515 0.721003 -v -0.692105 -0.070320 0.718364 -v -0.532527 -0.276418 0.800005 -v -0.576775 -0.121937 0.807751 -v -0.606094 0.035700 0.794591 -v -0.392158 -0.324671 0.860698 -v -0.440417 -0.173122 0.880944 -v -0.478185 -0.012888 0.878165 -v -0.502510 0.143507 0.852578 -v -0.245554 -0.363688 0.898573 -v -0.292357 -0.219707 0.930729 -v -0.334061 -0.062431 0.940481 -v -0.366890 0.096503 0.925245 -v -0.389047 0.245090 0.888016 -v -0.103706 -0.392018 0.914094 -v -0.144534 -0.258551 0.955124 -v -0.184989 -0.109057 0.976671 -v -0.221773 0.047002 0.973965 -v -0.252155 0.198033 0.947207 -v -0.274896 0.334259 0.901501 -v -0.003539 0.597253 0.802045 -v 0.140766 0.578845 0.803196 -v 0.070994 0.690953 0.719406 -v 0.292484 0.545410 0.785482 -v 0.223835 0.669022 0.708736 -v 0.149394 0.775319 0.613647 -v 0.440626 0.497276 0.747372 -v 0.378955 0.628609 0.679150 -v 0.305496 0.746638 0.590934 -v 0.225991 0.842140 0.489621 -v 0.574188 0.438020 0.691698 -v 0.524005 0.571493 0.631518 -v 0.457601 0.697217 0.551805 -v 0.379039 0.804417 0.457431 -v 0.295095 0.886452 0.356542 -v 0.685894 0.373248 0.624688 -v 0.648888 0.503187 0.570742 -v 0.593820 0.630919 0.499319 -v 0.522276 0.745873 0.413402 -v 0.439569 0.839511 0.319376 -v 0.352935 0.908230 0.224846 -v 0.103706 0.392018 -0.914094 -v 0.245554 0.363688 -0.898573 -v 0.144534 0.258551 -0.955124 -v 0.392159 0.324671 -0.860698 -v 0.292357 0.219707 -0.930729 -v 0.184989 0.109057 -0.976671 -v 0.532528 0.276418 -0.800005 -v 0.440417 0.173121 -0.880944 -v 0.334062 0.062431 -0.940481 -v 0.221773 -0.047003 -0.973965 -v 0.656233 0.222515 -0.721003 -v 0.576775 0.121936 -0.807751 -v 0.478185 0.012887 -0.878164 -v 0.366890 -0.096504 -0.925245 -v 0.252155 -0.198033 -0.947207 -v 0.756962 0.167457 -0.631638 -v 0.692105 0.070320 -0.718363 -v 0.606095 -0.035701 -0.794591 -v 0.502510 -0.143507 -0.852578 -v 0.389047 -0.245091 -0.888016 -v 0.274896 -0.334259 -0.901500 -v -0.685894 -0.373249 -0.624688 -v -0.574188 -0.438021 -0.691698 -v -0.648887 -0.503188 -0.570742 -v -0.440625 -0.497277 -0.747372 -v -0.524003 -0.571494 -0.631518 -v -0.593819 -0.630920 -0.499318 -v -0.292482 -0.545410 -0.785482 -v -0.378953 -0.628610 -0.679150 -v -0.457599 -0.697219 -0.551805 -v -0.522274 -0.745875 -0.413402 -v -0.140765 -0.578845 -0.803196 -v -0.223834 -0.669022 -0.708737 -v -0.305494 -0.746639 -0.590935 -v -0.379037 -0.804417 -0.457432 -v -0.439567 -0.839511 -0.319376 -v 0.003539 -0.597253 -0.802045 -v -0.070992 -0.690952 -0.719407 -v -0.149392 -0.775318 -0.613648 -v -0.225989 -0.842139 -0.489622 -v -0.295094 -0.886452 -0.356543 -v -0.352934 -0.908230 -0.224847 -v -0.775077 -0.374618 0.508839 -v -0.730065 -0.504435 0.461032 -v -0.675213 -0.439572 0.592338 -v -0.664480 -0.632005 0.398793 -v -0.616209 -0.572910 0.540426 -v -0.551989 -0.498987 0.668072 -v -0.580357 -0.746766 0.324847 -v -0.538132 -0.698455 0.471778 -v -0.480382 -0.630167 0.610019 -v -0.411620 -0.547239 0.728765 -v -0.483946 -0.840193 0.244687 -v -0.445757 -0.805442 0.390595 -v -0.394008 -0.747998 0.534094 -v -0.331790 -0.670680 0.663403 -v -0.264533 -0.580745 0.769907 -v -0.383583 -0.908701 0.164705 -v -0.347054 -0.887250 0.303878 -v -0.299620 -0.843270 0.446232 -v -0.243374 -0.776761 0.580871 -v -0.182494 -0.692664 0.697792 -v -0.121821 -0.599177 0.791294 -v -0.040596 0.389802 0.920003 -v -0.006662 0.256229 0.966593 -v 0.101939 0.361483 0.926789 -v 0.029954 0.106676 0.993843 -v 0.143165 0.217416 0.965523 -v 0.252670 0.322529 0.912213 -v 0.066738 -0.049383 0.996548 -v 0.182861 0.060109 0.981300 -v 0.297198 0.170923 0.939393 -v 0.400813 0.274396 0.874103 -v 0.100959 -0.200354 0.974508 -v 0.217698 -0.098794 0.971003 -v 0.334966 0.010689 0.942170 -v 0.443334 0.119887 0.888303 -v 0.535361 0.220659 0.815291 -v 0.130594 -0.336474 0.932593 -v 0.245433 -0.247295 0.937341 -v 0.363022 -0.145649 0.920327 -v 0.474380 -0.037723 0.879511 -v 0.571233 0.068464 0.817928 -v 0.648837 0.165797 0.742645 -v 0.502526 0.863609 0.040581 -v 0.596306 0.792758 0.126307 -v 0.608572 0.792947 -0.029595 -v 0.682992 0.697934 0.215430 -v 0.704689 0.707280 0.056298 -v 0.708147 0.698320 -0.104292 -v 0.754537 0.582979 0.301346 -v 0.787635 0.598772 0.145269 -v 0.800549 0.598971 -0.018868 -v 0.792269 0.583559 -0.178238 -v 0.805625 0.456434 0.377672 -v 0.849654 0.474355 0.230381 -v 0.874007 0.480940 0.069346 -v 0.875138 0.474747 -0.093532 -v 0.854689 0.457188 -0.245938 -v 0.835485 0.328626 0.440420 -v 0.887663 0.344514 0.305557 -v 0.922779 0.353454 0.153454 -v 0.935357 0.353648 -0.006407 -v 0.924461 0.345079 -0.162150 -v 0.894019 0.329525 -0.303552 -v 0.864707 -0.005667 -0.502244 -v 0.882018 -0.140274 -0.449853 -v 0.804997 -0.111074 -0.582788 -v 0.879806 -0.281583 -0.382952 -v 0.811329 -0.254941 -0.526071 -v 0.721854 -0.221701 -0.655573 -v 0.855198 -0.419329 -0.304629 -v 0.796987 -0.399805 -0.452735 -v 0.715898 -0.369063 -0.592691 -v 0.618270 -0.329507 -0.713560 -v 0.810023 -0.543280 -0.220702 -v 0.761469 -0.534045 -0.367371 -v 0.690632 -0.510494 -0.512273 -v 0.601445 -0.473379 -0.643565 -v 0.501941 -0.426483 -0.752441 -v 0.750187 -0.646720 -0.137739 -v 0.709004 -0.648416 -0.277254 -v 0.648031 -0.634940 -0.420604 -v 0.569055 -0.604999 -0.556914 -v 0.477942 -0.560819 -0.676057 -v 0.382642 -0.507381 -0.772107 -v 0.214935 -0.669921 -0.710640 -v 0.302224 -0.731123 -0.611653 -v 0.150502 -0.767092 -0.623634 -v 0.388875 -0.779628 -0.490873 -v 0.236359 -0.827215 -0.509755 -v 0.077726 -0.853391 -0.515444 -v 0.467851 -0.809568 -0.354563 -v 0.320631 -0.869093 -0.376662 -v 0.160894 -0.906961 -0.389275 -v 0.001128 -0.920212 -0.391418 -v 0.533286 -0.818719 -0.212849 -v 0.396383 -0.887879 -0.233561 -v 0.241984 -0.937701 -0.249319 -v 0.081155 -0.962609 -0.258453 -v -0.073600 -0.962591 -0.260772 -v 0.582481 -0.809258 -0.076272 -v 0.458586 -0.883886 -0.091896 -v 0.314656 -0.943211 -0.106514 -v 0.159082 -0.980092 -0.118799 -v 0.003421 -0.991789 -0.127838 -v -0.141539 -0.980897 -0.133443 -v -0.160481 -0.981188 0.107311 -v -0.016426 -0.992094 0.124419 -v -0.113294 -0.963200 0.243742 -v 0.138731 -0.980404 0.139863 -v 0.039920 -0.963242 0.265653 -v -0.059925 -0.921149 0.384569 -v 0.294305 -0.943523 0.152148 -v 0.200193 -0.938343 0.281844 -v 0.098208 -0.907923 0.407470 -v -0.003679 -0.854641 0.519207 -v 0.438739 -0.884190 0.160361 -v 0.355147 -0.888512 0.290546 -v 0.257944 -0.870054 0.420084 -v 0.153887 -0.828480 0.538460 -v 0.051266 -0.768615 0.637654 -v 0.563539 -0.809549 0.164484 -v 0.493592 -0.819328 0.291665 -v 0.406797 -0.810505 0.421424 -v 0.307470 -0.780877 0.543777 -v 0.202988 -0.732646 0.649635 -v 0.101281 -0.671665 0.733899 -v 0.949413 0.094496 -0.299477 -v 0.982501 0.098822 -0.157881 -v 0.970770 -0.035326 -0.237399 -v 0.994870 0.101138 -0.002029 -v 0.995726 -0.036893 -0.084662 -v 0.970812 -0.173970 -0.165104 -v 0.982293 0.100945 0.157832 -v 0.996218 -0.037588 0.078335 -v 0.983867 -0.178821 -0.005384 -v 0.946204 -0.311716 -0.086781 -v 0.945703 0.098257 0.309826 -v 0.970242 -0.037284 0.239251 -v 0.970953 -0.179019 0.158753 -v 0.945867 -0.315998 0.074038 -v 0.898775 -0.438332 -0.008249 -v 0.890878 0.093598 0.444494 -v 0.921705 -0.036078 0.386210 -v 0.933079 -0.174547 0.314478 -v 0.921049 -0.312101 0.232940 -v 0.886510 -0.438519 0.147653 -v 0.834893 -0.546557 0.065028 -v 0.257283 -0.509306 0.821226 -v 0.366443 -0.562531 0.741133 -v 0.378174 -0.428383 0.820653 -v 0.475075 -0.606442 0.637598 -v 0.493491 -0.475036 0.728565 -v 0.499134 -0.331335 0.800676 -v 0.574402 -0.636070 0.515245 -v 0.602119 -0.511852 0.612748 -v 0.614471 -0.370619 0.696468 -v 0.610491 -0.223410 0.759861 -v 0.657046 -0.649213 0.383162 -v 0.694750 -0.535069 0.480650 -v 0.716455 -0.401039 0.570841 -v 0.719125 -0.256355 0.645865 -v 0.703973 -0.112624 0.701240 -v 0.719540 -0.647190 0.251810 -v 0.765645 -0.543960 0.343359 -v 0.797116 -0.420219 0.433616 -v 0.809146 -0.282665 0.515154 -v 0.800841 -0.141519 0.581917 -v 0.775524 -0.007035 0.631278 -vt 0.181819 0.000000 -vt 0.193183 0.019683 -vt 0.170455 0.019683 -vt 0.272728 0.157461 -vt 0.284092 0.137778 -vt 0.295455 0.157461 -vt 0.909091 0.000000 -vt 0.920455 0.019683 -vt 0.897727 0.019683 -vt 0.727273 0.000000 -vt 0.738637 0.019683 -vt 0.715909 0.019683 -vt 0.545455 0.000000 -vt 0.556819 0.019683 -vt 0.534091 0.019683 -vt 0.284092 0.177143 -vt 0.090910 0.157461 -vt 0.113637 0.157461 -vt 0.102274 0.177143 -vt 0.818182 0.157461 -vt 0.840909 0.157461 -vt 0.829546 0.177143 -vt 0.636364 0.157461 -vt 0.659091 0.157461 -vt 0.647728 0.177143 -vt 0.454546 0.157461 -vt 0.477273 0.157461 -vt 0.465910 0.177143 -vt 0.261364 0.177143 -vt 0.079546 0.177143 -vt 0.806818 0.177143 -vt 0.625000 0.177143 -vt 0.443182 0.177143 -vt 0.181819 0.314921 -vt 0.204546 0.314921 -vt 0.193183 0.334604 -vt 0.000000 0.314921 -vt 0.022727 0.314921 -vt 0.011364 0.334604 -vt 0.727273 0.314921 -vt 0.750000 0.314921 -vt 0.738637 0.334604 -vt 0.545455 0.314921 -vt 0.568182 0.314921 -vt 0.556819 0.334604 -vt 0.363637 0.314921 -vt 0.386364 0.314921 -vt 0.375001 0.334604 -vt 0.443182 0.452699 -vt 0.465910 0.452699 -vt 0.454546 0.472382 -vt 0.431819 0.433017 -vt 0.454546 0.433017 -vt 0.420455 0.413334 -vt 0.443182 0.413334 -vt 0.409092 0.393652 -vt 0.431819 0.393652 -vt 0.397728 0.373969 -vt 0.420455 0.373969 -vt 0.386364 0.354286 -vt 0.409091 0.354286 -vt 0.397728 0.334604 -vt 0.477273 0.433017 -vt 0.465910 0.413334 -vt 0.488637 0.413334 -vt 0.454546 0.393652 -vt 0.477273 0.393652 -vt 0.500000 0.393652 -vt 0.443182 0.373969 -vt 0.465910 0.373969 -vt 0.488637 0.373969 -vt 0.511364 0.373969 -vt 0.431819 0.354286 -vt 0.454546 0.354286 -vt 0.477273 0.354286 -vt 0.500000 0.354286 -vt 0.522728 0.354286 -vt 0.420455 0.334604 -vt 0.443182 0.334604 -vt 0.465910 0.334604 -vt 0.488637 0.334604 -vt 0.511364 0.334604 -vt 0.534091 0.334604 -vt 0.409091 0.314921 -vt 0.431819 0.314921 -vt 0.454546 0.314921 -vt 0.477273 0.314921 -vt 0.500000 0.314921 -vt 0.522728 0.314921 -vt 0.625000 0.452699 -vt 0.647728 0.452699 -vt 0.636364 0.472382 -vt 0.613637 0.433017 -vt 0.636364 0.433017 -vt 0.602273 0.413334 -vt 0.625000 0.413334 -vt 0.590910 0.393652 -vt 0.613637 0.393652 -vt 0.579546 0.373969 -vt 0.602273 0.373969 -vt 0.568182 0.354286 -vt 0.590909 0.354286 -vt 0.579546 0.334604 -vt 0.659091 0.433017 -vt 0.647728 0.413334 -vt 0.670455 0.413334 -vt 0.636364 0.393652 -vt 0.659091 0.393652 -vt 0.681818 0.393652 -vt 0.625000 0.373969 -vt 0.647728 0.373969 -vt 0.670455 0.373969 -vt 0.693182 0.373969 -vt 0.613637 0.354286 -vt 0.636364 0.354286 -vt 0.659091 0.354286 -vt 0.681818 0.354286 -vt 0.704546 0.354286 -vt 0.602273 0.334604 -vt 0.625000 0.334604 -vt 0.647728 0.334604 -vt 0.670455 0.334604 -vt 0.693182 0.334604 -vt 0.715909 0.334604 -vt 0.590909 0.314921 -vt 0.613637 0.314921 -vt 0.636364 0.314921 -vt 0.659091 0.314921 -vt 0.681819 0.314921 -vt 0.704546 0.314921 -vt 0.806818 0.452699 -vt 0.829546 0.452699 -vt 0.818182 0.472382 -vt 0.795455 0.433017 -vt 0.818182 0.433017 -vt 0.784091 0.413334 -vt 0.806818 0.413334 -vt 0.772727 0.393652 -vt 0.795455 0.393652 -vt 0.761364 0.373969 -vt 0.784091 0.373969 -vt 0.750000 0.354286 -vt 0.772727 0.354286 -vt 0.761364 0.334604 -vt 0.840909 0.433017 -vt 0.829546 0.413334 -vt 0.852273 0.413334 -vt 0.818182 0.393652 -vt 0.840909 0.393652 -vt 0.863636 0.393652 -vt 0.806818 0.373969 -vt 0.829546 0.373969 -vt 0.852273 0.373969 -vt 0.875000 0.373969 -vt 0.795455 0.354286 -vt 0.818182 0.354286 -vt 0.840909 0.354286 -vt 0.863636 0.354286 -vt 0.886364 0.354286 -vt 0.784091 0.334604 -vt 0.806818 0.334604 -vt 0.829546 0.334604 -vt 0.852273 0.334604 -vt 0.875000 0.334604 -vt 0.897727 0.334604 -vt 0.772727 0.314921 -vt 0.795455 0.314921 -vt 0.818182 0.314921 -vt 0.840909 0.314921 -vt 0.863636 0.314921 -vt 0.886364 0.314921 -vt 0.909091 0.314921 -vt 0.079546 0.452699 -vt 0.102274 0.452699 -vt 0.090910 0.472382 -vt 0.068182 0.433017 -vt 0.090910 0.433017 -vt 0.056819 0.413334 -vt 0.079546 0.413334 -vt 0.045455 0.393652 -vt 0.068182 0.393652 -vt 0.034091 0.373969 -vt 0.056819 0.373969 -vt 0.022727 0.354286 -vt 0.045455 0.354286 -vt 0.034091 0.334604 -vt 0.113637 0.433017 -vt 0.102274 0.413334 -vt 0.125001 0.413334 -vt 0.090910 0.393652 -vt 0.113637 0.393652 -vt 0.136365 0.393652 -vt 0.079546 0.373969 -vt 0.102273 0.373969 -vt 0.125001 0.373969 -vt 0.147728 0.373969 -vt 0.068182 0.354286 -vt 0.090910 0.354286 -vt 0.113637 0.354286 -vt 0.136364 0.354286 -vt 0.159092 0.354286 -vt 0.056819 0.334604 -vt 0.079546 0.334604 -vt 0.102273 0.334604 -vt 0.125001 0.334604 -vt 0.147728 0.334604 -vt 0.170455 0.334604 -vt 0.045455 0.314921 -vt 0.068182 0.314921 -vt 0.090910 0.314921 -vt 0.113637 0.314921 -vt 0.136364 0.314921 -vt 0.159092 0.314921 -vt 0.261364 0.452699 -vt 0.284092 0.452699 -vt 0.272728 0.472382 -vt 0.250001 0.433017 -vt 0.272728 0.433017 -vt 0.238637 0.413334 -vt 0.261364 0.413334 -vt 0.227273 0.393652 -vt 0.250001 0.393652 -vt 0.215910 0.373969 -vt 0.238637 0.373969 -vt 0.204546 0.354286 -vt 0.227273 0.354286 -vt 0.215910 0.334604 -vt 0.295455 0.433017 -vt 0.284092 0.413334 -vt 0.306819 0.413334 -vt 0.272728 0.393652 -vt 0.295455 0.393652 -vt 0.318182 0.393652 -vt 0.261364 0.373969 -vt 0.284092 0.373969 -vt 0.306819 0.373969 -vt 0.329546 0.373969 -vt 0.250001 0.354286 -vt 0.272728 0.354286 -vt 0.295455 0.354286 -vt 0.318182 0.354286 -vt 0.340910 0.354286 -vt 0.238637 0.334604 -vt 0.261364 0.334604 -vt 0.284092 0.334604 -vt 0.306819 0.334604 -vt 0.329546 0.334604 -vt 0.352273 0.334604 -vt 0.227274 0.314921 -vt 0.250001 0.314921 -vt 0.272728 0.314921 -vt 0.295455 0.314921 -vt 0.318183 0.314921 -vt 0.340910 0.314921 -vt 0.375001 0.295238 -vt 0.386364 0.275556 -vt 0.397728 0.295238 -vt 0.397728 0.255874 -vt 0.409092 0.275556 -vt 0.409092 0.236191 -vt 0.420455 0.255874 -vt 0.420455 0.216509 -vt 0.431819 0.236191 -vt 0.431819 0.196826 -vt 0.443182 0.216509 -vt 0.454546 0.196826 -vt 0.420455 0.295238 -vt 0.431819 0.275556 -vt 0.443182 0.295238 -vt 0.443182 0.255874 -vt 0.454546 0.275556 -vt 0.465910 0.295238 -vt 0.454546 0.236191 -vt 0.465910 0.255874 -vt 0.477273 0.275556 -vt 0.488637 0.295238 -vt 0.465910 0.216509 -vt 0.477273 0.236191 -vt 0.488637 0.255874 -vt 0.500000 0.275556 -vt 0.511364 0.295238 -vt 0.477273 0.196826 -vt 0.488637 0.216508 -vt 0.500000 0.236191 -vt 0.511364 0.255874 -vt 0.522728 0.275556 -vt 0.534091 0.295238 -vt 0.556819 0.295238 -vt 0.568182 0.275556 -vt 0.579546 0.295238 -vt 0.579546 0.255874 -vt 0.590910 0.275556 -vt 0.590910 0.236191 -vt 0.602273 0.255874 -vt 0.602273 0.216509 -vt 0.613637 0.236191 -vt 0.613637 0.196826 -vt 0.625000 0.216509 -vt 0.636364 0.196826 -vt 0.602273 0.295238 -vt 0.613637 0.275556 -vt 0.625000 0.295238 -vt 0.625000 0.255874 -vt 0.636364 0.275556 -vt 0.647728 0.295238 -vt 0.636364 0.236191 -vt 0.647728 0.255874 -vt 0.659091 0.275556 -vt 0.670455 0.295238 -vt 0.647728 0.216509 -vt 0.659091 0.236191 -vt 0.670455 0.255874 -vt 0.681818 0.275556 -vt 0.693182 0.295238 -vt 0.659091 0.196826 -vt 0.670455 0.216508 -vt 0.681818 0.236191 -vt 0.693182 0.255874 -vt 0.704546 0.275556 -vt 0.715909 0.295238 -vt 0.738637 0.295238 -vt 0.750000 0.275556 -vt 0.761364 0.295238 -vt 0.761364 0.255874 -vt 0.772727 0.275556 -vt 0.772727 0.236191 -vt 0.784091 0.255874 -vt 0.784091 0.216509 -vt 0.795455 0.236191 -vt 0.795455 0.196826 -vt 0.806818 0.216509 -vt 0.818182 0.196826 -vt 0.784091 0.295238 -vt 0.795455 0.275556 -vt 0.806818 0.295238 -vt 0.806818 0.255874 -vt 0.818182 0.275556 -vt 0.829546 0.295238 -vt 0.818182 0.236191 -vt 0.829546 0.255874 -vt 0.840909 0.275556 -vt 0.852273 0.295238 -vt 0.829546 0.216509 -vt 0.840909 0.236191 -vt 0.852273 0.255874 -vt 0.863636 0.275556 -vt 0.875000 0.295238 -vt 0.840909 0.196826 -vt 0.852273 0.216508 -vt 0.863636 0.236191 -vt 0.875000 0.255874 -vt 0.886364 0.275556 -vt 0.897727 0.295238 -vt 0.011364 0.295238 -vt 0.022727 0.275556 -vt 0.034091 0.295238 -vt 0.034091 0.255874 -vt 0.045455 0.275556 -vt 0.045455 0.236191 -vt 0.056819 0.255874 -vt 0.056819 0.216509 -vt 0.068182 0.236191 -vt 0.068182 0.196826 -vt 0.079546 0.216509 -vt 0.090910 0.196826 -vt 0.056819 0.295238 -vt 0.068182 0.275556 -vt 0.079546 0.295238 -vt 0.079546 0.255874 -vt 0.090910 0.275556 -vt 0.102273 0.295238 -vt 0.090910 0.236191 -vt 0.102273 0.255874 -vt 0.113637 0.275556 -vt 0.125001 0.295238 -vt 0.102274 0.216509 -vt 0.113637 0.236191 -vt 0.125001 0.255874 -vt 0.136364 0.275556 -vt 0.147728 0.295238 -vt 0.113637 0.196826 -vt 0.125001 0.216508 -vt 0.136365 0.236191 -vt 0.147728 0.255874 -vt 0.159092 0.275556 -vt 0.170455 0.295238 -vt 0.193183 0.295238 -vt 0.204546 0.275556 -vt 0.215910 0.295238 -vt 0.215910 0.255874 -vt 0.227274 0.275556 -vt 0.227273 0.236191 -vt 0.238637 0.255874 -vt 0.238637 0.216509 -vt 0.250001 0.236191 -vt 0.250001 0.196826 -vt 0.261364 0.216509 -vt 0.272728 0.196826 -vt 0.238637 0.295238 -vt 0.250001 0.275556 -vt 0.261364 0.295238 -vt 0.261364 0.255874 -vt 0.272728 0.275556 -vt 0.284092 0.295238 -vt 0.272728 0.236191 -vt 0.284092 0.255874 -vt 0.295455 0.275556 -vt 0.306819 0.295238 -vt 0.284092 0.216509 -vt 0.295455 0.236191 -vt 0.306819 0.255874 -vt 0.318182 0.275556 -vt 0.329546 0.295238 -vt 0.295455 0.196826 -vt 0.306819 0.216508 -vt 0.318182 0.236191 -vt 0.329546 0.255874 -vt 0.340910 0.275556 -vt 0.352273 0.295238 -vt 0.545455 0.275556 -vt 0.534091 0.255874 -vt 0.522728 0.236191 -vt 0.511364 0.216508 -vt 0.500000 0.196826 -vt 0.488637 0.177143 -vt 0.556819 0.255874 -vt 0.545455 0.236191 -vt 0.568182 0.236191 -vt 0.534091 0.216508 -vt 0.556819 0.216508 -vt 0.579546 0.216508 -vt 0.522728 0.196826 -vt 0.545455 0.196826 -vt 0.568182 0.196826 -vt 0.590909 0.196826 -vt 0.511364 0.177143 -vt 0.534091 0.177143 -vt 0.556819 0.177143 -vt 0.579546 0.177143 -vt 0.602273 0.177143 -vt 0.500000 0.157461 -vt 0.522728 0.157461 -vt 0.545455 0.157461 -vt 0.568182 0.157461 -vt 0.590909 0.157461 -vt 0.613637 0.157461 -vt 0.727273 0.275556 -vt 0.715909 0.255874 -vt 0.704546 0.236191 -vt 0.693182 0.216508 -vt 0.681818 0.196826 -vt 0.670455 0.177143 -vt 0.738637 0.255874 -vt 0.727273 0.236191 -vt 0.750000 0.236191 -vt 0.715909 0.216508 -vt 0.738637 0.216508 -vt 0.761364 0.216508 -vt 0.704546 0.196826 -vt 0.727273 0.196826 -vt 0.750000 0.196826 -vt 0.772727 0.196826 -vt 0.693182 0.177143 -vt 0.715909 0.177143 -vt 0.738637 0.177143 -vt 0.761364 0.177143 -vt 0.784091 0.177143 -vt 0.681818 0.157461 -vt 0.704546 0.157461 -vt 0.727273 0.157461 -vt 0.750000 0.157461 -vt 0.772727 0.157461 -vt 0.795455 0.157461 -vt 0.920455 0.295238 -vt 0.909091 0.275556 -vt 0.897727 0.255874 -vt 0.886364 0.236191 -vt 0.875000 0.216508 -vt 0.863636 0.196826 -vt 0.852273 0.177143 -vt 0.931818 0.275556 -vt 0.920455 0.255874 -vt 0.943182 0.255874 -vt 0.909091 0.236191 -vt 0.931818 0.236191 -vt 0.954545 0.236191 -vt 0.897727 0.216508 -vt 0.920455 0.216508 -vt 0.943182 0.216508 -vt 0.965909 0.216509 -vt 0.886364 0.196826 -vt 0.909091 0.196826 -vt 0.931818 0.196826 -vt 0.954545 0.196826 -vt 0.977273 0.196826 -vt 0.875000 0.177143 -vt 0.897727 0.177143 -vt 0.920455 0.177143 -vt 0.943182 0.177143 -vt 0.965909 0.177143 -vt 0.988636 0.177143 -vt 0.863636 0.157461 -vt 0.886364 0.157461 -vt 0.909091 0.157461 -vt 0.931818 0.157461 -vt 0.954545 0.157461 -vt 0.977273 0.157461 -vt 1.000000 0.157461 -vt 0.181819 0.275556 -vt 0.170455 0.255874 -vt 0.159092 0.236191 -vt 0.147728 0.216508 -vt 0.136364 0.196826 -vt 0.125001 0.177143 -vt 0.193183 0.255874 -vt 0.181819 0.236191 -vt 0.204546 0.236191 -vt 0.170455 0.216508 -vt 0.193183 0.216508 -vt 0.215910 0.216508 -vt 0.159092 0.196826 -vt 0.181819 0.196826 -vt 0.204546 0.196826 -vt 0.227273 0.196826 -vt 0.147728 0.177143 -vt 0.170455 0.177143 -vt 0.193183 0.177143 -vt 0.215910 0.177143 -vt 0.238637 0.177143 -vt 0.136364 0.157461 -vt 0.159092 0.157461 -vt 0.181819 0.157461 -vt 0.204546 0.157461 -vt 0.227273 0.157461 -vt 0.250001 0.157461 -vt 0.363637 0.275556 -vt 0.352273 0.255874 -vt 0.340910 0.236191 -vt 0.329546 0.216508 -vt 0.318182 0.196826 -vt 0.306819 0.177143 -vt 0.375001 0.255874 -vt 0.363637 0.236191 -vt 0.386364 0.236191 -vt 0.352273 0.216508 -vt 0.375001 0.216508 -vt 0.397728 0.216508 -vt 0.340910 0.196826 -vt 0.363637 0.196826 -vt 0.386364 0.196826 -vt 0.409092 0.196826 -vt 0.329546 0.177143 -vt 0.352273 0.177143 -vt 0.375001 0.177143 -vt 0.397728 0.177143 -vt 0.420455 0.177143 -vt 0.318182 0.157461 -vt 0.340910 0.157461 -vt 0.363637 0.157461 -vt 0.386364 0.157461 -vt 0.409092 0.157461 -vt 0.431819 0.157461 -vt 0.465910 0.137778 -vt 0.477273 0.118096 -vt 0.488637 0.137778 -vt 0.488637 0.098413 -vt 0.500000 0.118096 -vt 0.500000 0.078731 -vt 0.511364 0.098413 -vt 0.511364 0.059048 -vt 0.522728 0.078731 -vt 0.522728 0.039365 -vt 0.534091 0.059048 -vt 0.545455 0.039365 -vt 0.511364 0.137778 -vt 0.522728 0.118096 -vt 0.534091 0.137778 -vt 0.534091 0.098413 -vt 0.545455 0.118096 -vt 0.556819 0.137778 -vt 0.545455 0.078731 -vt 0.556819 0.098413 -vt 0.568182 0.118096 -vt 0.579546 0.137778 -vt 0.556819 0.059048 -vt 0.568182 0.078731 -vt 0.579546 0.098413 -vt 0.590909 0.118096 -vt 0.602273 0.137778 -vt 0.568182 0.039365 -vt 0.579546 0.059048 -vt 0.590910 0.078731 -vt 0.602273 0.098413 -vt 0.613637 0.118096 -vt 0.625000 0.137778 -vt 0.647728 0.137778 -vt 0.659091 0.118096 -vt 0.670455 0.137778 -vt 0.670455 0.098413 -vt 0.681818 0.118096 -vt 0.681818 0.078731 -vt 0.693182 0.098413 -vt 0.693182 0.059048 -vt 0.704546 0.078731 -vt 0.704546 0.039365 -vt 0.715909 0.059048 -vt 0.727273 0.039365 -vt 0.693182 0.137778 -vt 0.704546 0.118096 -vt 0.715909 0.137778 -vt 0.715909 0.098413 -vt 0.727273 0.118096 -vt 0.738637 0.137778 -vt 0.727273 0.078731 -vt 0.738637 0.098413 -vt 0.750000 0.118096 -vt 0.761364 0.137778 -vt 0.738637 0.059048 -vt 0.750000 0.078731 -vt 0.761364 0.098413 -vt 0.772727 0.118096 -vt 0.784091 0.137778 -vt 0.750000 0.039365 -vt 0.761364 0.059048 -vt 0.772727 0.078731 -vt 0.784091 0.098413 -vt 0.795455 0.118096 -vt 0.806818 0.137778 -vt 0.829546 0.137778 -vt 0.840909 0.118096 -vt 0.852273 0.137778 -vt 0.852273 0.098413 -vt 0.863636 0.118096 -vt 0.863636 0.078731 -vt 0.875000 0.098413 -vt 0.875000 0.059048 -vt 0.886364 0.078731 -vt 0.886364 0.039365 -vt 0.897727 0.059048 -vt 0.909091 0.039365 -vt 0.875000 0.137778 -vt 0.886364 0.118096 -vt 0.897727 0.137778 -vt 0.897727 0.098413 -vt 0.909091 0.118096 -vt 0.920455 0.137778 -vt 0.909091 0.078731 -vt 0.920455 0.098413 -vt 0.931818 0.118096 -vt 0.943182 0.137778 -vt 0.920455 0.059048 -vt 0.931818 0.078731 -vt 0.943182 0.098413 -vt 0.954545 0.118096 -vt 0.965909 0.137778 -vt 0.931818 0.039365 -vt 0.943182 0.059048 -vt 0.954545 0.078731 -vt 0.965909 0.098413 -vt 0.977273 0.118096 -vt 0.988636 0.137778 -vt 0.443182 0.137778 -vt 0.420455 0.137778 -vt 0.397728 0.137778 -vt 0.375001 0.137778 -vt 0.352273 0.137778 -vt 0.329546 0.137778 -vt 0.306819 0.137778 -vt 0.431819 0.118096 -vt 0.409091 0.118096 -vt 0.420455 0.098413 -vt 0.386364 0.118096 -vt 0.397728 0.098413 -vt 0.409092 0.078731 -vt 0.363637 0.118096 -vt 0.375001 0.098413 -vt 0.386364 0.078731 -vt 0.397728 0.059048 -vt 0.340910 0.118096 -vt 0.352273 0.098413 -vt 0.363637 0.078731 -vt 0.375001 0.059048 -vt 0.386364 0.039365 -vt 0.318182 0.118096 -vt 0.329546 0.098413 -vt 0.340910 0.078731 -vt 0.352273 0.059048 -vt 0.363637 0.039365 -vt 0.375001 0.019683 -vt 0.295455 0.118096 -vt 0.306819 0.098413 -vt 0.318182 0.078731 -vt 0.329546 0.059048 -vt 0.340910 0.039365 -vt 0.352273 0.019683 -vt 0.363637 0.000000 -vt 0.102274 0.137778 -vt 0.113637 0.118096 -vt 0.125001 0.137778 -vt 0.125001 0.098413 -vt 0.136365 0.118096 -vt 0.136365 0.078731 -vt 0.147728 0.098413 -vt 0.147728 0.059048 -vt 0.159092 0.078731 -vt 0.159092 0.039365 -vt 0.170455 0.059048 -vt 0.181819 0.039365 -vt 0.147728 0.137778 -vt 0.159092 0.118096 -vt 0.170455 0.137778 -vt 0.170455 0.098413 -vt 0.181819 0.118096 -vt 0.193183 0.137778 -vt 0.181819 0.078731 -vt 0.193183 0.098413 -vt 0.204546 0.118096 -vt 0.215910 0.137778 -vt 0.193183 0.059048 -vt 0.204546 0.078731 -vt 0.215910 0.098413 -vt 0.227273 0.118096 -vt 0.238637 0.137778 -vt 0.204546 0.039365 -vt 0.215910 0.059048 -vt 0.227274 0.078731 -vt 0.238637 0.098413 -vt 0.250001 0.118096 -vt 0.261364 0.137778 -vn 0.7052 -0.7069 0.0546 -vn 0.7638 -0.6319 0.1313 -vn 0.6642 -0.7284 0.1678 -vn 0.7840 0.1950 0.5893 -vn 0.8265 0.0852 0.5564 -vn 0.8486 0.2084 0.4863 -vn 0.6139 -0.7880 0.0473 -vn 0.6824 -0.7282 -0.0636 -vn 0.7751 -0.6317 -0.0117 -vn 0.7733 0.3111 0.5524 -vn 0.0539 -0.5126 0.8569 -vn 0.1571 -0.4509 0.8786 -vn 0.0246 -0.4079 0.9127 -vn -0.3149 -0.9488 -0.0259 -vn -0.2860 -0.9537 0.0927 -vn -0.4116 -0.9106 0.0385 -vn 0.1873 -0.5106 -0.8391 -vn 0.1315 -0.6051 -0.7852 -vn 0.0675 -0.5022 -0.8621 -vn 0.8665 0.1963 -0.4589 -vn 0.8327 0.1132 -0.5420 -vn 0.7998 0.2528 -0.5444 -vn 0.7048 0.2513 0.6634 -vn -0.0680 -0.5043 0.8608 -vn -0.4003 -0.9104 -0.1045 -vn 0.1671 -0.4057 -0.8986 -vn 0.8501 0.3123 -0.4240 -vn -0.1873 0.5106 0.8391 -vn -0.1315 0.6051 0.7852 -vn -0.2707 0.5721 0.7742 -vn -0.8665 -0.1963 0.4589 -vn -0.8327 -0.1132 0.5420 -vn -0.9034 -0.0864 0.4200 -vn -0.7840 -0.1950 -0.5893 -vn -0.8486 -0.2084 -0.4863 -vn -0.8265 -0.0852 -0.5564 -vn -0.0539 0.5126 -0.8569 -vn -0.1571 0.4509 -0.8786 -vn -0.1464 0.5740 -0.8057 -vn 0.3149 0.9488 0.0259 -vn 0.2860 0.9537 -0.0927 -vn 0.1971 0.9802 0.0167 -vn -0.6139 0.7879 -0.0473 -vn -0.6642 0.7284 -0.1678 -vn -0.7052 0.7069 -0.0546 -vn -0.5037 0.8630 -0.0386 -vn -0.5602 0.8114 -0.1668 -vn -0.3737 0.9271 -0.0283 -vn -0.4360 0.8850 -0.1629 -vn -0.2293 0.9732 -0.0168 -vn -0.2931 0.9434 -0.1549 -vn -0.0797 0.9968 -0.0051 -vn -0.1393 0.9799 -0.1428 -vn 0.0652 0.9978 0.0063 -vn 0.0141 0.9917 -0.1274 -vn 0.1570 0.9814 -0.1102 -vn -0.6081 0.7395 -0.2885 -vn -0.4903 0.8209 -0.2928 -vn -0.5349 0.7366 -0.4140 -vn -0.3531 0.8890 -0.2916 -vn -0.4042 0.8120 -0.4209 -vn -0.4462 0.7168 -0.5358 -vn -0.2000 0.9380 -0.2830 -vn -0.2570 0.8706 -0.4194 -vn -0.3061 0.7827 -0.5418 -vn -0.3471 0.6806 -0.6452 -vn -0.0418 0.9628 -0.2670 -vn -0.0994 0.9080 -0.4070 -vn -0.1543 0.8297 -0.5364 -vn -0.2029 0.7352 -0.6468 -vn -0.2449 0.6312 -0.7359 -vn 0.1099 0.9632 -0.2454 -vn 0.0569 0.9213 -0.3845 -vn 0.0013 0.8556 -0.5176 -vn -0.0529 0.7707 -0.6349 -vn -0.1023 0.6748 -0.7308 -vn 0.2495 0.9428 -0.2208 -vn 0.2048 0.9119 -0.3555 -vn 0.1534 0.8590 -0.4884 -vn 0.0985 0.7862 -0.6100 -vn 0.0439 0.6996 -0.7131 -vn -0.0072 0.6070 -0.7947 -vn -0.7638 0.6319 -0.1313 -vn -0.7195 0.6456 -0.2560 -vn -0.6573 0.6471 -0.3862 -vn -0.5755 0.6337 -0.5168 -vn -0.4774 0.6045 -0.6377 -vn -0.3699 0.5613 -0.7403 -vn -0.2616 0.5090 -0.8200 -vn -0.8148 0.5392 -0.2127 -vn -0.7647 0.5430 -0.3469 -vn -0.8538 0.4274 -0.2970 -vn -0.6943 0.5338 -0.4827 -vn -0.7956 0.4205 -0.4362 -vn -0.8753 0.3009 -0.3785 -vn -0.6027 0.5108 -0.6130 -vn -0.7156 0.4014 -0.5716 -vn -0.8074 0.2846 -0.5167 -vn -0.8766 0.1674 -0.4512 -vn -0.4956 0.4746 -0.7274 -vn -0.6149 0.3714 -0.6956 -vn -0.7183 0.2586 -0.6458 -vn -0.7996 0.1450 -0.5828 -vn -0.8589 0.0362 -0.5109 -vn -0.3815 0.4287 -0.8189 -vn -0.5011 0.3327 -0.7989 -vn -0.6112 0.2260 -0.7585 -vn -0.7037 0.1164 -0.7008 -vn -0.7750 0.0114 -0.6319 -vn -0.2673 0.3758 -0.8873 -vn -0.3816 0.2865 -0.8788 -vn -0.4925 0.1867 -0.8500 -vn -0.5921 0.0826 -0.8016 -vn -0.6745 -0.0188 -0.7380 -vn -0.7377 -0.1117 -0.6658 -vn -0.7751 0.6317 0.0117 -vn -0.8373 0.5428 -0.0652 -vn -0.8882 0.4352 -0.1469 -vn -0.9222 0.3100 -0.2311 -vn -0.9341 0.1741 -0.3117 -vn -0.9230 0.0372 -0.3829 -vn -0.8928 -0.0913 -0.4411 -vn -0.8382 0.5388 0.0840 -vn -0.9004 0.4350 0.0072 -vn -0.8899 0.4269 0.1609 -vn -0.9466 0.3137 -0.0741 -vn -0.9471 0.3096 0.0848 -vn -0.9238 0.3001 0.2376 -vn -0.9712 0.1784 -0.1577 -vn -0.9840 0.1782 0.0043 -vn -0.9714 0.1735 0.1621 -vn -0.9364 0.1665 0.3089 -vn -0.9707 0.0385 -0.2372 -vn -0.9962 0.0388 -0.0783 -vn -0.9958 0.0381 0.0826 -vn -0.9716 0.0365 0.2339 -vn -0.9282 0.0351 0.3704 -vn -0.9468 -0.0956 -0.3073 -vn -0.9827 -0.0981 -0.1569 -vn -0.9951 -0.0983 0.0010 -vn -0.9832 -0.0962 0.1552 -vn -0.9508 -0.0922 0.2958 -vn -0.9045 -0.2195 -0.3655 -vn -0.9471 -0.2271 -0.2268 -vn -0.9702 -0.2300 -0.0766 -vn -0.9709 -0.2275 0.0754 -vn -0.9505 -0.2202 0.2190 -vn -0.9141 -0.2094 0.3471 -vn -0.6824 0.7282 0.0636 -vn -0.7508 0.6451 0.1419 -vn -0.8097 0.5423 0.2243 -vn -0.8541 0.4196 0.3074 -vn -0.8784 0.2835 0.3848 -vn -0.8809 0.1437 0.4509 -vn -0.8643 0.0101 0.5029 -vn -0.6459 0.7389 0.1916 -vn -0.7098 0.6463 0.2803 -vn -0.5932 0.7357 0.3270 -vn -0.7613 0.5328 0.3694 -vn -0.6494 0.6326 0.4220 -vn -0.5246 0.7156 0.4611 -vn -0.7962 0.4002 0.4537 -vn -0.6913 0.5094 0.5124 -vn -0.5713 0.6030 0.5567 -vn -0.4439 0.6791 0.5846 -vn -0.8106 0.2572 0.5261 -vn -0.7162 0.3699 0.5918 -vn -0.6033 0.4729 0.6421 -vn -0.4812 0.5596 0.6747 -vn -0.3571 0.6294 0.6901 -vn -0.8047 0.1148 0.5824 -vn -0.7223 0.2243 0.6541 -vn -0.6200 0.3309 0.7114 -vn -0.5050 0.4268 0.7502 -vn -0.3867 0.5071 0.7703 -vn -0.7816 -0.0204 0.6234 -vn -0.7102 0.0807 0.6994 -vn -0.6194 0.1848 0.7630 -vn -0.5144 0.2845 0.8090 -vn -0.4028 0.3738 0.8355 -vn -0.2927 0.4488 0.8443 -vn -0.5795 0.8111 0.0791 -vn -0.5302 0.8203 0.2145 -vn -0.4652 0.8111 0.3545 -vn -0.3872 0.7815 0.4892 -vn -0.3017 0.7336 0.6088 -vn -0.2154 0.6731 0.7075 -vn -0.4563 0.8847 0.0948 -vn -0.3945 0.8883 0.2350 -vn -0.3139 0.9431 0.1094 -vn -0.3196 0.8697 0.3762 -vn -0.2420 0.9373 0.2505 -vn -0.1602 0.9796 0.1216 -vn -0.2365 0.8284 0.5077 -vn -0.1620 0.9070 0.3886 -vn -0.0832 0.9621 0.2595 -vn -0.0062 0.9914 0.1304 -vn -0.1516 0.7692 0.6207 -vn -0.0798 0.8544 0.5134 -vn -0.0041 0.9204 0.3909 -vn 0.0699 0.9626 0.2618 -vn 0.1377 0.9811 0.1357 -vn -0.0683 0.6979 0.7129 -vn 0.0017 0.7847 0.6198 -vn 0.0750 0.8578 0.5085 -vn 0.1465 0.9110 0.3854 -vn 0.2117 0.9423 0.2593 -vn 0.2678 0.9534 0.1386 -vn 0.4116 0.9106 -0.0385 -vn 0.5101 0.8533 -0.1081 -vn 0.3849 0.9074 -0.1688 -vn 0.6069 0.7737 -0.1817 -vn 0.4841 0.8390 -0.2483 -vn 0.6944 0.6730 -0.2545 -vn 0.5792 0.7464 -0.3276 -vn 0.7659 0.5568 -0.3215 -vn 0.6623 0.6330 -0.4007 -vn 0.8177 0.4336 -0.3785 -vn 0.7275 0.5070 -0.4622 -vn 0.7727 0.3783 -0.5097 -vn 0.3487 0.8856 -0.3068 -vn 0.4462 0.8042 -0.3926 -vn 0.3019 0.8417 -0.4476 -vn 0.5373 0.6984 -0.4727 -vn 0.3951 0.7473 -0.5342 -vn 0.2463 0.7760 -0.5806 -vn 0.6145 0.5746 -0.5405 -vn 0.4803 0.6311 -0.6091 -vn 0.3337 0.6711 -0.6620 -vn 0.1860 0.6931 -0.6964 -vn 0.6733 0.4428 -0.5921 -vn 0.5512 0.5016 -0.6667 -vn 0.4125 0.5493 -0.7267 -vn 0.2671 0.5824 -0.7677 -vn 0.1256 0.6008 -0.7895 -vn 0.7133 0.3102 -0.6284 -vn 0.6048 0.3663 -0.7071 -vn 0.4776 0.4167 -0.7734 -vn 0.3395 0.4575 -0.8218 -vn 0.2000 0.4866 -0.8504 -vn 0.0680 0.5043 -0.8608 -vn -0.0246 0.4079 -0.9127 -vn 0.0077 0.2865 -0.9580 -vn -0.1322 0.3324 -0.9338 -vn 0.0428 0.1487 -0.9879 -vn -0.1030 0.1972 -0.9749 -vn 0.0784 0.0012 -0.9969 -vn -0.0693 0.0478 -0.9964 -vn 0.1123 -0.1463 -0.9828 -vn -0.0330 -0.1064 -0.9938 -vn 0.1422 -0.2842 -0.9481 -vn 0.0034 -0.2543 -0.9671 -vn 0.0374 -0.3869 -0.9213 -vn -0.2459 0.2436 -0.9382 -vn -0.2184 0.0965 -0.9710 -vn -0.3620 0.1429 -0.9211 -vn -0.1840 -0.0604 -0.9810 -vn -0.3341 -0.0116 -0.9424 -vn -0.4721 0.0363 -0.8808 -vn -0.1448 -0.2157 -0.9656 -vn -0.2969 -0.1697 -0.9397 -vn -0.4412 -0.1194 -0.8894 -vn -0.5681 -0.0686 -0.8201 -vn -0.1040 -0.3584 -0.9277 -vn -0.2530 -0.3197 -0.9131 -vn -0.3994 -0.2721 -0.8755 -vn -0.5326 -0.2192 -0.8175 -vn -0.6456 -0.1651 -0.7456 -vn -0.0645 -0.4845 -0.8724 -vn -0.2067 -0.4554 -0.8659 -vn -0.3507 -0.4147 -0.8396 -vn -0.4867 -0.3645 -0.7939 -vn -0.6062 -0.3085 -0.7330 -vn -0.7048 -0.2513 -0.6634 -vn -0.7733 -0.3111 -0.5524 -vn -0.7484 -0.4325 -0.5028 -vn -0.8362 -0.3314 -0.4369 -vn -0.7061 -0.5559 -0.4386 -vn -0.8063 -0.4579 -0.3742 -vn -0.6460 -0.6723 -0.3616 -vn -0.7557 -0.5829 -0.2985 -vn -0.5709 -0.7732 -0.2762 -vn -0.6850 -0.6965 -0.2136 -vn -0.4867 -0.8529 -0.1886 -vn -0.5993 -0.7906 -0.1257 -vn -0.5064 -0.8613 -0.0409 -vn -0.8875 -0.3472 -0.3029 -vn -0.8495 -0.4756 -0.2283 -vn -0.9219 -0.3562 -0.1524 -vn -0.7881 -0.5983 -0.1442 -vn -0.8734 -0.4820 -0.0693 -vn -0.9343 -0.3563 0.0055 -vn -0.7064 -0.7056 -0.0564 -vn -0.8009 -0.5985 0.0177 -vn -0.8747 -0.4759 0.0915 -vn -0.9239 -0.3478 0.1596 -vn -0.6114 -0.7907 0.0285 -vn -0.7098 -0.6969 0.1022 -vn -0.7930 -0.5835 0.1753 -vn -0.8549 -0.4587 0.2424 -vn -0.8942 -0.3323 0.3000 -vn -0.5101 -0.8533 0.1081 -vn -0.6069 -0.7737 0.1817 -vn -0.6944 -0.6730 0.2545 -vn -0.7659 -0.5568 0.3215 -vn -0.8177 -0.4336 0.3785 -vn -0.8501 -0.3123 0.4240 -vn -0.7998 -0.2528 0.5444 -vn -0.7133 -0.3102 0.6284 -vn -0.7542 -0.1667 0.6351 -vn -0.6048 -0.3663 0.7071 -vn -0.6539 -0.2210 0.7236 -vn -0.4776 -0.4167 0.7734 -vn -0.5313 -0.2741 0.8016 -vn -0.3395 -0.4575 0.8218 -vn -0.3926 -0.3218 0.8615 -vn -0.2000 -0.4866 0.8504 -vn -0.2477 -0.3606 0.8992 -vn -0.1071 -0.3892 0.9149 -vn -0.6894 -0.0705 0.7210 -vn -0.5749 -0.1215 0.8091 -vn -0.6040 0.0342 0.7962 -vn -0.4401 -0.1719 0.8813 -vn -0.4774 -0.0138 0.8785 -vn -0.5017 0.1408 0.8535 -vn -0.2940 -0.2180 0.9306 -vn -0.3352 -0.0627 0.9400 -vn -0.3676 0.0942 0.9252 -vn -0.3896 0.2414 0.8887 -vn -0.1478 -0.2567 0.9551 -vn -0.1879 -0.1088 0.9761 -vn -0.2243 0.0454 0.9734 -vn -0.2542 0.1949 0.9473 -vn -0.2767 0.3302 0.9024 -vn -0.0077 -0.2865 0.9580 -vn -0.0428 -0.1487 0.9879 -vn -0.0784 -0.0012 0.9969 -vn -0.1123 0.1463 0.9828 -vn -0.1422 0.2842 0.9481 -vn -0.1671 0.4057 0.8986 -vn -0.0675 0.5022 0.8621 -vn 0.0645 0.4845 0.8724 -vn 0.0005 0.5989 0.8008 -vn 0.2067 0.4554 0.8659 -vn 0.1437 0.5805 0.8014 -vn 0.3507 0.4147 0.8396 -vn 0.2937 0.5474 0.7836 -vn 0.4867 0.3645 0.7939 -vn 0.4400 0.4999 0.7459 -vn 0.6062 0.3085 0.7330 -vn 0.5723 0.4413 0.6912 -vn 0.6834 0.3770 0.6251 -vn 0.0746 0.6914 0.7186 -vn 0.2259 0.6695 0.7076 -vn 0.1523 0.7746 0.6138 -vn 0.3790 0.6295 0.6783 -vn 0.3065 0.7460 0.5912 -vn 0.2280 0.8406 0.4913 -vn 0.5223 0.5732 0.6314 -vn 0.4566 0.6972 0.5526 -vn 0.3792 0.8032 0.4595 -vn 0.2963 0.8848 0.3597 -vn 0.6462 0.5057 0.5716 -vn 0.5914 0.6319 0.5009 -vn 0.5207 0.7455 0.4160 -vn 0.4392 0.8383 0.3229 -vn 0.3536 0.9069 0.2291 -vn 0.7484 0.4325 0.5028 -vn 0.7061 0.5559 0.4386 -vn 0.6460 0.6723 0.3616 -vn 0.5709 0.7732 0.2762 -vn 0.4867 0.8529 0.1886 -vn 0.4003 0.9104 0.1045 -vn 0.1071 0.3892 -0.9149 -vn 0.2477 0.3606 -0.8992 -vn 0.3926 0.3218 -0.8615 -vn 0.5313 0.2741 -0.8016 -vn 0.6539 0.2210 -0.7236 -vn 0.7542 0.1667 -0.6351 -vn 0.1478 0.2567 -0.9551 -vn 0.2940 0.2180 -0.9306 -vn 0.1879 0.1088 -0.9761 -vn 0.4401 0.1719 -0.8813 -vn 0.3352 0.0627 -0.9400 -vn 0.2243 -0.0454 -0.9734 -vn 0.5749 0.1215 -0.8091 -vn 0.4774 0.0138 -0.8785 -vn 0.3676 -0.0942 -0.9252 -vn 0.2542 -0.1949 -0.9473 -vn 0.6894 0.0705 -0.7210 -vn 0.6040 -0.0342 -0.7962 -vn 0.5017 -0.1408 -0.8535 -vn 0.3896 -0.2414 -0.8887 -vn 0.2767 -0.3302 -0.9024 -vn 0.7816 0.0204 -0.6234 -vn 0.7102 -0.0807 -0.6994 -vn 0.6194 -0.1848 -0.7630 -vn 0.5144 -0.2845 -0.8090 -vn 0.4028 -0.3738 -0.8355 -vn 0.2927 -0.4488 -0.8443 -vn -0.6834 -0.3770 -0.6251 -vn -0.5723 -0.4413 -0.6912 -vn -0.4400 -0.4999 -0.7459 -vn -0.2937 -0.5474 -0.7836 -vn -0.1437 -0.5805 -0.8014 -vn -0.0005 -0.5989 -0.8008 -vn -0.6462 -0.5057 -0.5716 -vn -0.5223 -0.5732 -0.6314 -vn -0.5914 -0.6319 -0.5009 -vn -0.3790 -0.6295 -0.6783 -vn -0.4566 -0.6972 -0.5526 -vn -0.5207 -0.7455 -0.4160 -vn -0.2259 -0.6695 -0.7076 -vn -0.3065 -0.7460 -0.5912 -vn -0.3792 -0.8032 -0.4595 -vn -0.4392 -0.8383 -0.3229 -vn -0.0746 -0.6914 -0.7186 -vn -0.1523 -0.7746 -0.6138 -vn -0.2280 -0.8406 -0.4913 -vn -0.2963 -0.8848 -0.3597 -vn -0.3536 -0.9069 -0.2291 -vn 0.0683 -0.6979 -0.7129 -vn -0.0017 -0.7847 -0.6198 -vn -0.0750 -0.8578 -0.5085 -vn -0.1465 -0.9110 -0.3854 -vn -0.2117 -0.9423 -0.2593 -vn -0.2678 -0.9534 -0.1386 -vn -0.7727 -0.3783 0.5097 -vn -0.7275 -0.5070 0.4622 -vn -0.6623 -0.6330 0.4007 -vn -0.5792 -0.7464 0.3276 -vn -0.4841 -0.8390 0.2483 -vn -0.3849 -0.9074 0.1688 -vn -0.6733 -0.4428 0.5921 -vn -0.6145 -0.5746 0.5405 -vn -0.5512 -0.5016 0.6667 -vn -0.5373 -0.6984 0.4727 -vn -0.4803 -0.6310 0.6091 -vn -0.4125 -0.5493 0.7267 -vn -0.4462 -0.8042 0.3926 -vn -0.3951 -0.7473 0.5342 -vn -0.3337 -0.6711 0.6620 -vn -0.2671 -0.5824 0.7677 -vn -0.3487 -0.8856 0.3068 -vn -0.3019 -0.8417 0.4476 -vn -0.2463 -0.7760 0.5806 -vn -0.1860 -0.6931 0.6964 -vn -0.1256 -0.6008 0.7895 -vn -0.2495 -0.9428 0.2208 -vn -0.2048 -0.9119 0.3555 -vn -0.1534 -0.8590 0.4884 -vn -0.0985 -0.7862 0.6100 -vn -0.0439 -0.6996 0.7131 -vn 0.0072 -0.6070 0.7947 -vn -0.0374 0.3869 0.9213 -vn -0.0034 0.2543 0.9671 -vn 0.0330 0.1064 0.9938 -vn 0.0693 -0.0478 0.9964 -vn 0.1030 -0.1972 0.9749 -vn 0.1322 -0.3324 0.9338 -vn 0.1040 0.3584 0.9277 -vn 0.1448 0.2157 0.9656 -vn 0.2530 0.3197 0.9131 -vn 0.1840 0.0604 0.9810 -vn 0.2969 0.1697 0.9397 -vn 0.3994 0.2721 0.8755 -vn 0.2184 -0.0965 0.9710 -vn 0.3341 0.0116 0.9424 -vn 0.4412 0.1194 0.8894 -vn 0.5326 0.2192 0.8175 -vn 0.2459 -0.2436 0.9382 -vn 0.3620 -0.1429 0.9211 -vn 0.4721 -0.0363 0.8808 -vn 0.5681 0.0686 0.8201 -vn 0.6456 0.1651 0.7456 -vn 0.2673 -0.3758 0.8873 -vn 0.3816 -0.2865 0.8788 -vn 0.4925 -0.1867 0.8500 -vn 0.5921 -0.0826 0.8016 -vn 0.6745 0.0188 0.7380 -vn 0.7377 0.1117 0.6658 -vn 0.5064 0.8613 0.0409 -vn 0.5993 0.7906 0.1257 -vn 0.6850 0.6965 0.2136 -vn 0.7557 0.5829 0.2985 -vn 0.8063 0.4579 0.3742 -vn 0.8362 0.3314 0.4369 -vn 0.6114 0.7907 -0.0285 -vn 0.7064 0.7056 0.0564 -vn 0.7098 0.6969 -0.1022 -vn 0.7881 0.5983 0.1442 -vn 0.8009 0.5985 -0.0177 -vn 0.7929 0.5835 -0.1753 -vn 0.8495 0.4756 0.2283 -vn 0.8734 0.4820 0.0693 -vn 0.8747 0.4759 -0.0915 -vn 0.8549 0.4587 -0.2424 -vn 0.8875 0.3472 0.3029 -vn 0.9219 0.3562 0.1524 -vn 0.9343 0.3563 -0.0055 -vn 0.9239 0.3478 -0.1596 -vn 0.8942 0.3323 -0.3000 -vn 0.9045 0.2195 0.3655 -vn 0.9471 0.2271 0.2268 -vn 0.9702 0.2300 0.0766 -vn 0.9709 0.2275 -0.0754 -vn 0.9505 0.2202 -0.2190 -vn 0.9141 0.2094 -0.3471 -vn 0.9034 0.0864 -0.4200 -vn 0.9282 -0.0351 -0.3704 -vn 0.8643 -0.0101 -0.5029 -vn 0.9364 -0.1665 -0.3089 -vn 0.8809 -0.1437 -0.4509 -vn 0.9238 -0.3001 -0.2376 -vn 0.8784 -0.2835 -0.3848 -vn 0.8899 -0.4269 -0.1609 -vn 0.8541 -0.4196 -0.3074 -vn 0.8382 -0.5388 -0.0840 -vn 0.8097 -0.5423 -0.2243 -vn 0.7508 -0.6451 -0.1419 -vn 0.8047 -0.1148 -0.5824 -vn 0.8106 -0.2572 -0.5261 -vn 0.7223 -0.2243 -0.6541 -vn 0.7962 -0.4002 -0.4537 -vn 0.7162 -0.3699 -0.5918 -vn 0.6200 -0.3308 -0.7114 -vn 0.7613 -0.5328 -0.3694 -vn 0.6913 -0.5094 -0.5124 -vn 0.6033 -0.4729 -0.6421 -vn 0.5050 -0.4268 -0.7502 -vn 0.7098 -0.6463 -0.2803 -vn 0.6494 -0.6326 -0.4220 -vn 0.5713 -0.6030 -0.5567 -vn 0.4812 -0.5596 -0.6747 -vn 0.3867 -0.5071 -0.7703 -vn 0.6459 -0.7389 -0.1916 -vn 0.5932 -0.7357 -0.3270 -vn 0.5246 -0.7156 -0.4611 -vn 0.4439 -0.6791 -0.5846 -vn 0.3571 -0.6294 -0.6901 -vn 0.2707 -0.5721 -0.7742 -vn 0.2154 -0.6731 -0.7075 -vn 0.3017 -0.7336 -0.6088 -vn 0.3872 -0.7815 -0.4892 -vn 0.4652 -0.8111 -0.3545 -vn 0.5302 -0.8203 -0.2145 -vn 0.5795 -0.8111 -0.0791 -vn 0.1516 -0.7692 -0.6207 -vn 0.2365 -0.8284 -0.5077 -vn 0.0798 -0.8544 -0.5134 -vn 0.3196 -0.8697 -0.3762 -vn 0.1620 -0.9070 -0.3886 -vn 0.0041 -0.9204 -0.3909 -vn 0.3945 -0.8883 -0.2350 -vn 0.2420 -0.9373 -0.2505 -vn 0.0832 -0.9621 -0.2595 -vn -0.0699 -0.9626 -0.2618 -vn 0.4563 -0.8847 -0.0948 -vn 0.3139 -0.9431 -0.1094 -vn 0.1602 -0.9796 -0.1216 -vn 0.0062 -0.9914 -0.1304 -vn -0.1377 -0.9811 -0.1357 -vn 0.5037 -0.8630 0.0386 -vn 0.3737 -0.9271 0.0283 -vn 0.2293 -0.9732 0.0168 -vn 0.0797 -0.9968 0.0051 -vn -0.0652 -0.9978 -0.0063 -vn -0.1971 -0.9802 -0.0167 -vn -0.1570 -0.9814 0.1102 -vn -0.0141 -0.9917 0.1274 -vn 0.1393 -0.9799 0.1428 -vn 0.2931 -0.9434 0.1549 -vn 0.4360 -0.8850 0.1629 -vn 0.5602 -0.8114 0.1668 -vn -0.1099 -0.9632 0.2454 -vn 0.0418 -0.9628 0.2670 -vn -0.0569 -0.9213 0.3845 -vn 0.2000 -0.9380 0.2830 -vn 0.0994 -0.9080 0.4070 -vn -0.0013 -0.8556 0.5176 -vn 0.3531 -0.8890 0.2916 -vn 0.2570 -0.8706 0.4194 -vn 0.1543 -0.8297 0.5364 -vn 0.0529 -0.7707 0.6349 -vn 0.4903 -0.8209 0.2928 -vn 0.4042 -0.8120 0.4209 -vn 0.3061 -0.7827 0.5418 -vn 0.2029 -0.7352 0.6468 -vn 0.1023 -0.6748 0.7308 -vn 0.6081 -0.7395 0.2885 -vn 0.5349 -0.7366 0.4140 -vn 0.4462 -0.7168 0.5358 -vn 0.3471 -0.6806 0.6452 -vn 0.2449 -0.6312 0.7359 -vn 0.1464 -0.5740 0.8057 -vn 0.9508 0.0922 -0.2958 -vn 0.9832 0.0962 -0.1552 -vn 0.9951 0.0983 -0.0010 -vn 0.9827 0.0981 0.1569 -vn 0.9468 0.0956 0.3073 -vn 0.8928 0.0913 0.4411 -vn 0.9716 -0.0365 -0.2339 -vn 0.9958 -0.0381 -0.0826 -vn 0.9714 -0.1735 -0.1621 -vn 0.9962 -0.0388 0.0783 -vn 0.9840 -0.1782 -0.0043 -vn 0.9471 -0.3096 -0.0848 -vn 0.9707 -0.0385 0.2372 -vn 0.9712 -0.1784 0.1577 -vn 0.9466 -0.3137 0.0741 -vn 0.9004 -0.4350 -0.0072 -vn 0.9230 -0.0372 0.3829 -vn 0.9341 -0.1741 0.3117 -vn 0.9222 -0.3100 0.2311 -vn 0.8882 -0.4352 0.1469 -vn 0.8373 -0.5428 0.0652 -vn 0.8589 -0.0362 0.5109 -vn 0.8766 -0.1674 0.4512 -vn 0.8753 -0.3009 0.3785 -vn 0.8538 -0.4274 0.2970 -vn 0.8148 -0.5392 0.2127 -vn 0.2616 -0.5090 0.8200 -vn 0.3699 -0.5613 0.7403 -vn 0.4774 -0.6045 0.6377 -vn 0.5755 -0.6337 0.5168 -vn 0.6573 -0.6471 0.3862 -vn 0.7195 -0.6456 0.2560 -vn 0.3815 -0.4287 0.8189 -vn 0.4956 -0.4746 0.7274 -vn 0.5011 -0.3327 0.7989 -vn 0.6027 -0.5108 0.6130 -vn 0.6149 -0.3714 0.6956 -vn 0.6112 -0.2260 0.7585 -vn 0.6943 -0.5338 0.4827 -vn 0.7156 -0.4014 0.5716 -vn 0.7183 -0.2586 0.6458 -vn 0.7037 -0.1164 0.7008 -vn 0.7647 -0.5430 0.3469 -vn 0.7956 -0.4205 0.4362 -vn 0.8074 -0.2846 0.5167 -vn 0.7996 -0.1450 0.5828 -vn 0.7750 -0.0114 0.6319 -usemtl Material.001 -s 1 -f 1513/1915/1512 1532/1916/1513 1531/1917/1514 -f 1514/1918/1515 1538/1919/1516 1552/1920/1517 -f 1513/1921/1512 1531/1922/1514 1566/1923/1518 -f 1513/1924/1512 1566/1925/1518 1580/1926/1519 -f 1513/1927/1512 1580/1928/1519 1553/1929/1520 -f 1514/1918/1515 1552/1920/1517 1601/1930/1521 -f 1515/1931/1522 1545/1932/1523 1615/1933/1524 -f 1516/1934/1525 1573/1935/1526 1629/1936/1527 -f 1517/1937/1528 1587/1938/1529 1643/1939/1530 -f 1518/1940/1531 1594/1941/1532 1657/1942/1533 -f 1514/1918/1515 1601/1930/1521 1616/1943/1534 -f 1515/1931/1522 1615/1933/1524 1630/1944/1535 -f 1516/1934/1525 1629/1936/1527 1644/1945/1536 -f 1517/1937/1528 1643/1939/1530 1658/1946/1537 -f 1518/1940/1531 1657/1942/1533 1602/1947/1538 -f 1519/1948/1539 1671/1949/1540 1706/1950/1541 -f 1520/1951/1542 1678/1952/1543 1720/1953/1544 -f 1521/1954/1545 1685/1955/1546 1727/1956/1547 -f 1522/1957/1548 1692/1958/1549 1734/1959/1550 -f 1523/1960/1551 1699/1961/1552 1707/1962/1553 -f 1713/1963/1554 1728/1964/1555 1524/1965/1556 -f 1712/1966/1557 1735/1967/1558 1713/1963/1554 -f 1711/1968/1559 1736/1969/1560 1712/1966/1557 -f 1710/1970/1561 1738/1971/1562 1711/1968/1559 -f 1709/1972/1563 1741/1973/1564 1710/1970/1561 -f 1708/1974/1565 1745/1975/1566 1709/1972/1563 -f 1707/1962/1553 1750/1976/1567 1708/1974/1565 -f 1713/1963/1554 1735/1967/1558 1728/1964/1555 -f 1735/1967/1558 1729/1977/1568 1728/1964/1555 -f 1712/1966/1557 1736/1969/1560 1735/1967/1558 -f 1736/1969/1560 1737/1978/1569 1735/1967/1558 -f 1735/1967/1558 1737/1978/1569 1729/1977/1568 -f 1737/1978/1569 1730/1979/1570 1729/1977/1568 -f 1711/1968/1559 1738/1971/1562 1736/1969/1560 -f 1738/1971/1562 1739/1980/1571 1736/1969/1560 -f 1736/1969/1560 1739/1980/1571 1737/1978/1569 -f 1739/1980/1571 1740/1981/1572 1737/1978/1569 -f 1737/1978/1569 1740/1981/1572 1730/1979/1570 -f 1740/1981/1572 1731/1982/1573 1730/1979/1570 -f 1710/1970/1561 1741/1973/1564 1738/1971/1562 -f 1741/1973/1564 1742/1983/1574 1738/1971/1562 -f 1738/1971/1562 1742/1983/1574 1739/1980/1571 -f 1742/1983/1574 1743/1984/1575 1739/1980/1571 -f 1739/1980/1571 1743/1984/1575 1740/1981/1572 -f 1743/1984/1575 1744/1985/1576 1740/1981/1572 -f 1740/1981/1572 1744/1985/1576 1731/1982/1573 -f 1744/1985/1576 1732/1986/1577 1731/1982/1573 -f 1709/1972/1563 1745/1975/1566 1741/1973/1564 -f 1745/1975/1566 1746/1987/1578 1741/1973/1564 -f 1741/1973/1564 1746/1987/1578 1742/1983/1574 -f 1746/1987/1578 1747/1988/1579 1742/1983/1574 -f 1742/1983/1574 1747/1988/1579 1743/1984/1575 -f 1747/1988/1579 1748/1989/1580 1743/1984/1575 -f 1743/1984/1575 1748/1989/1580 1744/1985/1576 -f 1748/1989/1580 1749/1990/1581 1744/1985/1576 -f 1744/1985/1576 1749/1990/1581 1732/1986/1577 -f 1749/1990/1581 1733/1991/1582 1732/1986/1577 -f 1708/1974/1565 1750/1976/1567 1745/1975/1566 -f 1750/1976/1567 1751/1992/1583 1745/1975/1566 -f 1745/1975/1566 1751/1992/1583 1746/1987/1578 -f 1751/1992/1583 1752/1993/1584 1746/1987/1578 -f 1746/1987/1578 1752/1993/1584 1747/1988/1579 -f 1752/1993/1584 1753/1994/1585 1747/1988/1579 -f 1747/1988/1579 1753/1994/1585 1748/1989/1580 -f 1753/1994/1585 1754/1995/1586 1748/1989/1580 -f 1748/1989/1580 1754/1995/1586 1749/1990/1581 -f 1754/1995/1586 1755/1996/1587 1749/1990/1581 -f 1749/1990/1581 1755/1996/1587 1733/1991/1582 -f 1755/1996/1587 1734/1997/1550 1733/1991/1582 -f 1707/1962/1553 1699/1961/1552 1750/1976/1567 -f 1699/1961/1552 1698/1998/1588 1750/1976/1567 -f 1750/1976/1567 1698/1998/1588 1751/1992/1583 -f 1698/1998/1588 1697/1999/1589 1751/1992/1583 -f 1751/1992/1583 1697/1999/1589 1752/1993/1584 -f 1697/1999/1589 1696/2000/1590 1752/1993/1584 -f 1752/1993/1584 1696/2000/1590 1753/1994/1585 -f 1696/2000/1590 1695/2001/1591 1753/1994/1585 -f 1753/1994/1585 1695/2001/1591 1754/1995/1586 -f 1695/2001/1591 1694/2002/1592 1754/1995/1586 -f 1754/1995/1586 1694/2002/1592 1755/1996/1587 -f 1694/2002/1592 1693/2003/1593 1755/1996/1587 -f 1755/1996/1587 1693/2003/1593 1734/1997/1550 -f 1693/2003/1593 1522/1957/1548 1734/1997/1550 -f 1728/2004/1555 1721/2005/1594 1524/2006/1556 -f 1729/2007/1568 1756/2008/1595 1728/2004/1555 -f 1730/2009/1570 1757/2010/1596 1729/2007/1568 -f 1731/2011/1573 1759/2012/1597 1730/2009/1570 -f 1732/2013/1577 1762/2014/1598 1731/2011/1573 -f 1733/2015/1582 1766/2016/1599 1732/2013/1577 -f 1734/1959/1550 1771/2017/1600 1733/2015/1582 -f 1728/2004/1555 1756/2008/1595 1721/2005/1594 -f 1756/2008/1595 1722/2018/1601 1721/2005/1594 -f 1729/2007/1568 1757/2010/1596 1756/2008/1595 -f 1757/2010/1596 1758/2019/1602 1756/2008/1595 -f 1756/2008/1595 1758/2019/1602 1722/2018/1601 -f 1758/2019/1602 1723/2020/1603 1722/2018/1601 -f 1730/2009/1570 1759/2012/1597 1757/2010/1596 -f 1759/2012/1597 1760/2021/1604 1757/2010/1596 -f 1757/2010/1596 1760/2021/1604 1758/2019/1602 -f 1760/2021/1604 1761/2022/1605 1758/2019/1602 -f 1758/2019/1602 1761/2022/1605 1723/2020/1603 -f 1761/2022/1605 1724/2023/1606 1723/2020/1603 -f 1731/2011/1573 1762/2014/1598 1759/2012/1597 -f 1762/2014/1598 1763/2024/1607 1759/2012/1597 -f 1759/2012/1597 1763/2024/1607 1760/2021/1604 -f 1763/2024/1607 1764/2025/1608 1760/2021/1604 -f 1760/2021/1604 1764/2025/1608 1761/2022/1605 -f 1764/2025/1608 1765/2026/1609 1761/2022/1605 -f 1761/2022/1605 1765/2026/1609 1724/2023/1606 -f 1765/2026/1609 1725/2027/1610 1724/2023/1606 -f 1732/2013/1577 1766/2016/1599 1762/2014/1598 -f 1766/2016/1599 1767/2028/1611 1762/2014/1598 -f 1762/2014/1598 1767/2028/1611 1763/2024/1607 -f 1767/2028/1611 1768/2029/1612 1763/2024/1607 -f 1763/2024/1607 1768/2029/1612 1764/2025/1608 -f 1768/2029/1612 1769/2030/1613 1764/2025/1608 -f 1764/2025/1608 1769/2030/1613 1765/2026/1609 -f 1769/2030/1613 1770/2031/1614 1765/2026/1609 -f 1765/2026/1609 1770/2031/1614 1725/2027/1610 -f 1770/2031/1614 1726/2032/1615 1725/2027/1610 -f 1733/2015/1582 1771/2017/1600 1766/2016/1599 -f 1771/2017/1600 1772/2033/1616 1766/2016/1599 -f 1766/2016/1599 1772/2033/1616 1767/2028/1611 -f 1772/2033/1616 1773/2034/1617 1767/2028/1611 -f 1767/2028/1611 1773/2034/1617 1768/2029/1612 -f 1773/2034/1617 1774/2035/1618 1768/2029/1612 -f 1768/2029/1612 1774/2035/1618 1769/2030/1613 -f 1774/2035/1618 1775/2036/1619 1769/2030/1613 -f 1769/2030/1613 1775/2036/1619 1770/2031/1614 -f 1775/2036/1619 1776/2037/1620 1770/2031/1614 -f 1770/2031/1614 1776/2037/1620 1726/2032/1615 -f 1776/2037/1620 1727/2038/1547 1726/2032/1615 -f 1734/1959/1550 1692/1958/1549 1771/2017/1600 -f 1692/1958/1549 1691/2039/1621 1771/2017/1600 -f 1771/2017/1600 1691/2039/1621 1772/2033/1616 -f 1691/2039/1621 1690/2040/1622 1772/2033/1616 -f 1772/2033/1616 1690/2040/1622 1773/2034/1617 -f 1690/2040/1622 1689/2041/1623 1773/2034/1617 -f 1773/2034/1617 1689/2041/1623 1774/2035/1618 -f 1689/2041/1623 1688/2042/1624 1774/2035/1618 -f 1774/2035/1618 1688/2042/1624 1775/2036/1619 -f 1688/2042/1624 1687/2043/1625 1775/2036/1619 -f 1775/2036/1619 1687/2043/1625 1776/2037/1620 -f 1687/2043/1625 1686/2044/1626 1776/2037/1620 -f 1776/2037/1620 1686/2044/1626 1727/2038/1547 -f 1686/2044/1626 1521/1954/1545 1727/2038/1547 -f 1721/2045/1594 1714/2046/1627 1524/2047/1556 -f 1722/2048/1601 1777/2049/1628 1721/2045/1594 -f 1723/2050/1603 1778/2051/1629 1722/2048/1601 -f 1724/2052/1606 1780/2053/1630 1723/2050/1603 -f 1725/2054/1610 1783/2055/1631 1724/2052/1606 -f 1726/2056/1615 1787/2057/1632 1725/2054/1610 -f 1727/1956/1547 1792/2058/1633 1726/2056/1615 -f 1721/2045/1594 1777/2049/1628 1714/2046/1627 -f 1777/2049/1628 1715/2059/1634 1714/2046/1627 -f 1722/2048/1601 1778/2051/1629 1777/2049/1628 -f 1778/2051/1629 1779/2060/1635 1777/2049/1628 -f 1777/2049/1628 1779/2060/1635 1715/2059/1634 -f 1779/2060/1635 1716/2061/1636 1715/2059/1634 -f 1723/2050/1603 1780/2053/1630 1778/2051/1629 -f 1780/2053/1630 1781/2062/1637 1778/2051/1629 -f 1778/2051/1629 1781/2062/1637 1779/2060/1635 -f 1781/2062/1637 1782/2063/1638 1779/2060/1635 -f 1779/2060/1635 1782/2063/1638 1716/2061/1636 -f 1782/2063/1638 1717/2064/1639 1716/2061/1636 -f 1724/2052/1606 1783/2055/1631 1780/2053/1630 -f 1783/2055/1631 1784/2065/1640 1780/2053/1630 -f 1780/2053/1630 1784/2065/1640 1781/2062/1637 -f 1784/2065/1640 1785/2066/1641 1781/2062/1637 -f 1781/2062/1637 1785/2066/1641 1782/2063/1638 -f 1785/2066/1641 1786/2067/1642 1782/2063/1638 -f 1782/2063/1638 1786/2067/1642 1717/2064/1639 -f 1786/2067/1642 1718/2068/1643 1717/2064/1639 -f 1725/2054/1610 1787/2057/1632 1783/2055/1631 -f 1787/2057/1632 1788/2069/1644 1783/2055/1631 -f 1783/2055/1631 1788/2069/1644 1784/2065/1640 -f 1788/2069/1644 1789/2070/1645 1784/2065/1640 -f 1784/2065/1640 1789/2070/1645 1785/2066/1641 -f 1789/2070/1645 1790/2071/1646 1785/2066/1641 -f 1785/2066/1641 1790/2071/1646 1786/2067/1642 -f 1790/2071/1646 1791/2072/1647 1786/2067/1642 -f 1786/2067/1642 1791/2072/1647 1718/2068/1643 -f 1791/2072/1647 1719/2073/1648 1718/2068/1643 -f 1726/2056/1615 1792/2058/1633 1787/2057/1632 -f 1792/2058/1633 1793/2074/1649 1787/2057/1632 -f 1787/2057/1632 1793/2074/1649 1788/2069/1644 -f 1793/2074/1649 1794/2075/1650 1788/2069/1644 -f 1788/2069/1644 1794/2075/1650 1789/2070/1645 -f 1794/2075/1650 1795/2076/1651 1789/2070/1645 -f 1789/2070/1645 1795/2076/1651 1790/2071/1646 -f 1795/2076/1651 1796/2077/1652 1790/2071/1646 -f 1790/2071/1646 1796/2077/1652 1791/2072/1647 -f 1796/2077/1652 1797/2078/1653 1791/2072/1647 -f 1791/2072/1647 1797/2078/1653 1719/2073/1648 -f 1797/2078/1653 1720/2079/1544 1719/2073/1648 -f 1727/1956/1547 1685/1955/1546 1792/2058/1633 -f 1685/1955/1546 1684/2080/1654 1792/2058/1633 -f 1792/2058/1633 1684/2080/1654 1793/2074/1649 -f 1684/2080/1654 1683/2081/1655 1793/2074/1649 -f 1793/2074/1649 1683/2081/1655 1794/2075/1650 -f 1683/2081/1655 1682/2082/1656 1794/2075/1650 -f 1794/2075/1650 1682/2082/1656 1795/2076/1651 -f 1682/2082/1656 1681/2083/1657 1795/2076/1651 -f 1795/2076/1651 1681/2083/1657 1796/2077/1652 -f 1681/2083/1657 1680/2084/1658 1796/2077/1652 -f 1796/2077/1652 1680/2084/1658 1797/2078/1653 -f 1680/2084/1658 1679/2085/1659 1797/2078/1653 -f 1797/2078/1653 1679/2085/1659 1720/2079/1544 -f 1679/2085/1659 1520/2086/1542 1720/2079/1544 -f 1714/2087/1627 1700/2088/1660 1524/2089/1556 -f 1715/2090/1634 1798/2091/1661 1714/2087/1627 -f 1716/2092/1636 1799/2093/1662 1715/2090/1634 -f 1717/2094/1639 1801/2095/1663 1716/2092/1636 -f 1718/2096/1643 1804/2097/1664 1717/2094/1639 -f 1719/2098/1648 1808/2099/1665 1718/2096/1643 -f 1720/1953/1544 1813/2100/1666 1719/2098/1648 -f 1714/2087/1627 1798/2091/1661 1700/2088/1660 -f 1798/2091/1661 1701/2101/1667 1700/2088/1660 -f 1715/2090/1634 1799/2093/1662 1798/2091/1661 -f 1799/2093/1662 1800/2102/1668 1798/2091/1661 -f 1798/2091/1661 1800/2102/1668 1701/2101/1667 -f 1800/2102/1668 1702/2103/1669 1701/2101/1667 -f 1716/2092/1636 1801/2095/1663 1799/2093/1662 -f 1801/2095/1663 1802/2104/1670 1799/2093/1662 -f 1799/2093/1662 1802/2104/1670 1800/2102/1668 -f 1802/2104/1670 1803/2105/1671 1800/2102/1668 -f 1800/2102/1668 1803/2105/1671 1702/2103/1669 -f 1803/2105/1671 1703/2106/1672 1702/2103/1669 -f 1717/2094/1639 1804/2097/1664 1801/2095/1663 -f 1804/2097/1664 1805/2107/1673 1801/2095/1663 -f 1801/2095/1663 1805/2107/1673 1802/2104/1670 -f 1805/2107/1673 1806/2108/1674 1802/2104/1670 -f 1802/2104/1670 1806/2108/1674 1803/2105/1671 -f 1806/2108/1674 1807/2109/1675 1803/2105/1671 -f 1803/2105/1671 1807/2109/1675 1703/2106/1672 -f 1807/2109/1675 1704/2110/1676 1703/2106/1672 -f 1718/2096/1643 1808/2099/1665 1804/2097/1664 -f 1808/2099/1665 1809/2111/1677 1804/2097/1664 -f 1804/2097/1664 1809/2111/1677 1805/2107/1673 -f 1809/2111/1677 1810/2112/1678 1805/2107/1673 -f 1805/2107/1673 1810/2112/1678 1806/2108/1674 -f 1810/2112/1678 1811/2113/1679 1806/2108/1674 -f 1806/2108/1674 1811/2113/1679 1807/2109/1675 -f 1811/2113/1679 1812/2114/1680 1807/2109/1675 -f 1807/2109/1675 1812/2114/1680 1704/2110/1676 -f 1812/2114/1680 1705/2115/1681 1704/2110/1676 -f 1719/2098/1648 1813/2100/1666 1808/2099/1665 -f 1813/2100/1666 1814/2116/1682 1808/2099/1665 -f 1808/2099/1665 1814/2116/1682 1809/2111/1677 -f 1814/2116/1682 1815/2117/1683 1809/2111/1677 -f 1809/2111/1677 1815/2117/1683 1810/2112/1678 -f 1815/2117/1683 1816/2118/1684 1810/2112/1678 -f 1810/2112/1678 1816/2118/1684 1811/2113/1679 -f 1816/2118/1684 1817/2119/1685 1811/2113/1679 -f 1811/2113/1679 1817/2119/1685 1812/2114/1680 -f 1817/2119/1685 1818/2120/1686 1812/2114/1680 -f 1812/2114/1680 1818/2120/1686 1705/2115/1681 -f 1818/2120/1686 1706/2121/1541 1705/2115/1681 -f 1720/1953/1544 1678/1952/1543 1813/2100/1666 -f 1678/1952/1543 1677/2122/1687 1813/2100/1666 -f 1813/2100/1666 1677/2122/1687 1814/2116/1682 -f 1677/2122/1687 1676/2123/1688 1814/2116/1682 -f 1814/2116/1682 1676/2123/1688 1815/2117/1683 -f 1676/2123/1688 1675/2124/1689 1815/2117/1683 -f 1815/2117/1683 1675/2124/1689 1816/2118/1684 -f 1675/2124/1689 1674/2125/1690 1816/2118/1684 -f 1816/2118/1684 1674/2125/1690 1817/2119/1685 -f 1674/2125/1690 1673/2126/1691 1817/2119/1685 -f 1817/2119/1685 1673/2126/1691 1818/2120/1686 -f 1673/2126/1691 1672/2127/1692 1818/2120/1686 -f 1818/2120/1686 1672/2127/1692 1706/2121/1541 -f 1672/2127/1692 1519/1948/1539 1706/2121/1541 -f 1700/2128/1660 1713/2129/1554 1524/2130/1556 -f 1701/2131/1667 1819/2132/1693 1700/2128/1660 -f 1702/2133/1669 1820/2134/1694 1701/2131/1667 -f 1703/2135/1672 1822/2136/1695 1702/2133/1669 -f 1704/2137/1676 1825/2138/1696 1703/2135/1672 -f 1705/2139/1681 1829/2140/1697 1704/2137/1676 -f 1706/1950/1541 1834/2141/1698 1705/2139/1681 -f 1700/2128/1660 1819/2132/1693 1713/2129/1554 -f 1819/2132/1693 1712/2142/1557 1713/2129/1554 -f 1701/2131/1667 1820/2134/1694 1819/2132/1693 -f 1820/2134/1694 1821/2143/1699 1819/2132/1693 -f 1819/2132/1693 1821/2143/1699 1712/2142/1557 -f 1821/2143/1699 1711/2144/1559 1712/2142/1557 -f 1702/2133/1669 1822/2136/1695 1820/2134/1694 -f 1822/2136/1695 1823/2145/1700 1820/2134/1694 -f 1820/2134/1694 1823/2145/1700 1821/2143/1699 -f 1823/2145/1700 1824/2146/1701 1821/2143/1699 -f 1821/2143/1699 1824/2146/1701 1711/2144/1559 -f 1824/2146/1701 1710/2147/1561 1711/2144/1559 -f 1703/2135/1672 1825/2138/1696 1822/2136/1695 -f 1825/2138/1696 1826/2148/1702 1822/2136/1695 -f 1822/2136/1695 1826/2148/1702 1823/2145/1700 -f 1826/2148/1702 1827/2149/1703 1823/2145/1700 -f 1823/2145/1700 1827/2149/1703 1824/2146/1701 -f 1827/2149/1703 1828/2150/1704 1824/2146/1701 -f 1824/2146/1701 1828/2150/1704 1710/2147/1561 -f 1828/2150/1704 1709/2151/1563 1710/2147/1561 -f 1704/2137/1676 1829/2140/1697 1825/2138/1696 -f 1829/2140/1697 1830/2152/1705 1825/2138/1696 -f 1825/2138/1696 1830/2152/1705 1826/2148/1702 -f 1830/2152/1705 1831/2153/1706 1826/2148/1702 -f 1826/2148/1702 1831/2153/1706 1827/2149/1703 -f 1831/2153/1706 1832/2154/1707 1827/2149/1703 -f 1827/2149/1703 1832/2154/1707 1828/2150/1704 -f 1832/2154/1707 1833/2155/1708 1828/2150/1704 -f 1828/2150/1704 1833/2155/1708 1709/2151/1563 -f 1833/2155/1708 1708/2156/1565 1709/2151/1563 -f 1705/2139/1681 1834/2141/1698 1829/2140/1697 -f 1834/2141/1698 1835/2157/1709 1829/2140/1697 -f 1829/2140/1697 1835/2157/1709 1830/2152/1705 -f 1835/2157/1709 1836/2158/1710 1830/2152/1705 -f 1830/2152/1705 1836/2158/1710 1831/2153/1706 -f 1836/2158/1710 1837/2159/1711 1831/2153/1706 -f 1831/2153/1706 1837/2159/1711 1832/2154/1707 -f 1837/2159/1711 1838/2160/1712 1832/2154/1707 -f 1832/2154/1707 1838/2160/1712 1833/2155/1708 -f 1838/2160/1712 1839/2161/1713 1833/2155/1708 -f 1833/2155/1708 1839/2161/1713 1708/2156/1565 -f 1839/2161/1713 1707/2162/1553 1708/2156/1565 -f 1706/1950/1541 1671/1949/1540 1834/2141/1698 -f 1671/1949/1540 1670/2163/1714 1834/2141/1698 -f 1834/2141/1698 1670/2163/1714 1835/2157/1709 -f 1670/2163/1714 1669/2164/1715 1835/2157/1709 -f 1835/2157/1709 1669/2164/1715 1836/2158/1710 -f 1669/2164/1715 1668/2165/1716 1836/2158/1710 -f 1836/2158/1710 1668/2165/1716 1837/2159/1711 -f 1668/2165/1716 1667/2166/1717 1837/2159/1711 -f 1837/2159/1711 1667/2166/1717 1838/2160/1712 -f 1667/2166/1717 1666/2167/1718 1838/2160/1712 -f 1838/2160/1712 1666/2167/1718 1839/2161/1713 -f 1666/2167/1718 1665/2168/1719 1839/2161/1713 -f 1839/2161/1713 1665/2168/1719 1707/2162/1553 -f 1665/2168/1719 1523/1960/1551 1707/2162/1553 -f 1608/2169/1720 1699/1961/1552 1523/1960/1551 -f 1607/2170/1721 1840/2171/1722 1608/2169/1720 -f 1606/2172/1723 1841/2173/1724 1607/2170/1721 -f 1605/2174/1725 1843/2175/1726 1606/2172/1723 -f 1604/2176/1727 1846/2177/1728 1605/2174/1725 -f 1603/2178/1729 1850/2179/1730 1604/2176/1727 -f 1602/1947/1538 1855/2180/1731 1603/2178/1729 -f 1608/2169/1720 1840/2171/1722 1699/1961/1552 -f 1840/2171/1722 1698/1998/1588 1699/1961/1552 -f 1607/2170/1721 1841/2173/1724 1840/2171/1722 -f 1841/2173/1724 1842/2181/1732 1840/2171/1722 -f 1840/2171/1722 1842/2181/1732 1698/1998/1588 -f 1842/2181/1732 1697/1999/1589 1698/1998/1588 -f 1606/2172/1723 1843/2175/1726 1841/2173/1724 -f 1843/2175/1726 1844/2182/1733 1841/2173/1724 -f 1841/2173/1724 1844/2182/1733 1842/2181/1732 -f 1844/2182/1733 1845/2183/1734 1842/2181/1732 -f 1842/2181/1732 1845/2183/1734 1697/1999/1589 -f 1845/2183/1734 1696/2000/1590 1697/1999/1589 -f 1605/2174/1725 1846/2177/1728 1843/2175/1726 -f 1846/2177/1728 1847/2184/1735 1843/2175/1726 -f 1843/2175/1726 1847/2184/1735 1844/2182/1733 -f 1847/2184/1735 1848/2185/1736 1844/2182/1733 -f 1844/2182/1733 1848/2185/1736 1845/2183/1734 -f 1848/2185/1736 1849/2186/1737 1845/2183/1734 -f 1845/2183/1734 1849/2186/1737 1696/2000/1590 -f 1849/2186/1737 1695/2001/1591 1696/2000/1590 -f 1604/2176/1727 1850/2179/1730 1846/2177/1728 -f 1850/2179/1730 1851/2187/1738 1846/2177/1728 -f 1846/2177/1728 1851/2187/1738 1847/2184/1735 -f 1851/2187/1738 1852/2188/1739 1847/2184/1735 -f 1847/2184/1735 1852/2188/1739 1848/2185/1736 -f 1852/2188/1739 1853/2189/1740 1848/2185/1736 -f 1848/2185/1736 1853/2189/1740 1849/2186/1737 -f 1853/2189/1740 1854/2190/1741 1849/2186/1737 -f 1849/2186/1737 1854/2190/1741 1695/2001/1591 -f 1854/2190/1741 1694/2002/1592 1695/2001/1591 -f 1603/2178/1729 1855/2180/1731 1850/2179/1730 -f 1855/2180/1731 1856/2191/1742 1850/2179/1730 -f 1850/2179/1730 1856/2191/1742 1851/2187/1738 -f 1856/2191/1742 1857/2192/1743 1851/2187/1738 -f 1851/2187/1738 1857/2192/1743 1852/2188/1739 -f 1857/2192/1743 1858/2193/1744 1852/2188/1739 -f 1852/2188/1739 1858/2193/1744 1853/2189/1740 -f 1858/2193/1744 1859/2194/1745 1853/2189/1740 -f 1853/2189/1740 1859/2194/1745 1854/2190/1741 -f 1859/2194/1745 1860/2195/1746 1854/2190/1741 -f 1854/2190/1741 1860/2195/1746 1694/2002/1592 -f 1860/2195/1746 1693/2003/1593 1694/2002/1592 -f 1602/1947/1538 1657/1942/1533 1855/2180/1731 -f 1657/1942/1533 1656/2196/1747 1855/2180/1731 -f 1855/2180/1731 1656/2196/1747 1856/2191/1742 -f 1656/2196/1747 1655/2197/1748 1856/2191/1742 -f 1856/2191/1742 1655/2197/1748 1857/2192/1743 -f 1655/2197/1748 1654/2198/1749 1857/2192/1743 -f 1857/2192/1743 1654/2198/1749 1858/2193/1744 -f 1654/2198/1749 1653/2199/1750 1858/2193/1744 -f 1858/2193/1744 1653/2199/1750 1859/2194/1745 -f 1653/2199/1750 1652/2200/1751 1859/2194/1745 -f 1859/2194/1745 1652/2200/1751 1860/2195/1746 -f 1652/2200/1751 1651/2201/1752 1860/2195/1746 -f 1860/2195/1746 1651/2201/1752 1693/2003/1593 -f 1651/2201/1752 1522/1957/1548 1693/2003/1593 -f 1664/2202/1753 1692/1958/1549 1522/1957/1548 -f 1663/2203/1754 1861/2204/1755 1664/2202/1753 -f 1662/2205/1756 1862/2206/1757 1663/2203/1754 -f 1661/2207/1758 1864/2208/1759 1662/2205/1756 -f 1660/2209/1760 1867/2210/1761 1661/2207/1758 -f 1659/2211/1762 1871/2212/1763 1660/2209/1760 -f 1658/1946/1537 1876/2213/1764 1659/2211/1762 -f 1664/2202/1753 1861/2204/1755 1692/1958/1549 -f 1861/2204/1755 1691/2039/1621 1692/1958/1549 -f 1663/2203/1754 1862/2206/1757 1861/2204/1755 -f 1862/2206/1757 1863/2214/1765 1861/2204/1755 -f 1861/2204/1755 1863/2214/1765 1691/2039/1621 -f 1863/2214/1765 1690/2040/1622 1691/2039/1621 -f 1662/2205/1756 1864/2208/1759 1862/2206/1757 -f 1864/2208/1759 1865/2215/1766 1862/2206/1757 -f 1862/2206/1757 1865/2215/1766 1863/2214/1765 -f 1865/2215/1766 1866/2216/1767 1863/2214/1765 -f 1863/2214/1765 1866/2216/1767 1690/2040/1622 -f 1866/2216/1767 1689/2041/1623 1690/2040/1622 -f 1661/2207/1758 1867/2210/1761 1864/2208/1759 -f 1867/2210/1761 1868/2217/1768 1864/2208/1759 -f 1864/2208/1759 1868/2217/1768 1865/2215/1766 -f 1868/2217/1768 1869/2218/1769 1865/2215/1766 -f 1865/2215/1766 1869/2218/1769 1866/2216/1767 -f 1869/2218/1769 1870/2219/1770 1866/2216/1767 -f 1866/2216/1767 1870/2219/1770 1689/2041/1623 -f 1870/2219/1770 1688/2042/1624 1689/2041/1623 -f 1660/2209/1760 1871/2212/1763 1867/2210/1761 -f 1871/2212/1763 1872/2220/1771 1867/2210/1761 -f 1867/2210/1761 1872/2220/1771 1868/2217/1768 -f 1872/2220/1771 1873/2221/1772 1868/2217/1768 -f 1868/2217/1768 1873/2221/1772 1869/2218/1769 -f 1873/2221/1772 1874/2222/1773 1869/2218/1769 -f 1869/2218/1769 1874/2222/1773 1870/2219/1770 -f 1874/2222/1773 1875/2223/1774 1870/2219/1770 -f 1870/2219/1770 1875/2223/1774 1688/2042/1624 -f 1875/2223/1774 1687/2043/1625 1688/2042/1624 -f 1659/2211/1762 1876/2213/1764 1871/2212/1763 -f 1876/2213/1764 1877/2224/1775 1871/2212/1763 -f 1871/2212/1763 1877/2224/1775 1872/2220/1771 -f 1877/2224/1775 1878/2225/1776 1872/2220/1771 -f 1872/2220/1771 1878/2225/1776 1873/2221/1772 -f 1878/2225/1776 1879/2226/1777 1873/2221/1772 -f 1873/2221/1772 1879/2226/1777 1874/2222/1773 -f 1879/2226/1777 1880/2227/1778 1874/2222/1773 -f 1874/2222/1773 1880/2227/1778 1875/2223/1774 -f 1880/2227/1778 1881/2228/1779 1875/2223/1774 -f 1875/2223/1774 1881/2228/1779 1687/2043/1625 -f 1881/2228/1779 1686/2044/1626 1687/2043/1625 -f 1658/1946/1537 1643/1939/1530 1876/2213/1764 -f 1643/1939/1530 1642/2229/1780 1876/2213/1764 -f 1876/2213/1764 1642/2229/1780 1877/2224/1775 -f 1642/2229/1780 1641/2230/1781 1877/2224/1775 -f 1877/2224/1775 1641/2230/1781 1878/2225/1776 -f 1641/2230/1781 1640/2231/1782 1878/2225/1776 -f 1878/2225/1776 1640/2231/1782 1879/2226/1777 -f 1640/2231/1782 1639/2232/1783 1879/2226/1777 -f 1879/2226/1777 1639/2232/1783 1880/2227/1778 -f 1639/2232/1783 1638/2233/1784 1880/2227/1778 -f 1880/2227/1778 1638/2233/1784 1881/2228/1779 -f 1638/2233/1784 1637/2234/1785 1881/2228/1779 -f 1881/2228/1779 1637/2234/1785 1686/2044/1626 -f 1637/2234/1785 1521/1954/1545 1686/2044/1626 -f 1650/2235/1786 1685/1955/1546 1521/1954/1545 -f 1649/2236/1787 1882/2237/1788 1650/2235/1786 -f 1648/2238/1789 1883/2239/1790 1649/2236/1787 -f 1647/2240/1791 1885/2241/1792 1648/2238/1789 -f 1646/2242/1793 1888/2243/1794 1647/2240/1791 -f 1645/2244/1795 1892/2245/1796 1646/2242/1793 -f 1644/1945/1536 1897/2246/1797 1645/2244/1795 -f 1650/2235/1786 1882/2237/1788 1685/1955/1546 -f 1882/2237/1788 1684/2080/1654 1685/1955/1546 -f 1649/2236/1787 1883/2239/1790 1882/2237/1788 -f 1883/2239/1790 1884/2247/1798 1882/2237/1788 -f 1882/2237/1788 1884/2247/1798 1684/2080/1654 -f 1884/2247/1798 1683/2081/1655 1684/2080/1654 -f 1648/2238/1789 1885/2241/1792 1883/2239/1790 -f 1885/2241/1792 1886/2248/1799 1883/2239/1790 -f 1883/2239/1790 1886/2248/1799 1884/2247/1798 -f 1886/2248/1799 1887/2249/1800 1884/2247/1798 -f 1884/2247/1798 1887/2249/1800 1683/2081/1655 -f 1887/2249/1800 1682/2082/1656 1683/2081/1655 -f 1647/2240/1791 1888/2243/1794 1885/2241/1792 -f 1888/2243/1794 1889/2250/1801 1885/2241/1792 -f 1885/2241/1792 1889/2250/1801 1886/2248/1799 -f 1889/2250/1801 1890/2251/1802 1886/2248/1799 -f 1886/2248/1799 1890/2251/1802 1887/2249/1800 -f 1890/2251/1802 1891/2252/1803 1887/2249/1800 -f 1887/2249/1800 1891/2252/1803 1682/2082/1656 -f 1891/2252/1803 1681/2083/1657 1682/2082/1656 -f 1646/2242/1793 1892/2245/1796 1888/2243/1794 -f 1892/2245/1796 1893/2253/1804 1888/2243/1794 -f 1888/2243/1794 1893/2253/1804 1889/2250/1801 -f 1893/2253/1804 1894/2254/1805 1889/2250/1801 -f 1889/2250/1801 1894/2254/1805 1890/2251/1802 -f 1894/2254/1805 1895/2255/1806 1890/2251/1802 -f 1890/2251/1802 1895/2255/1806 1891/2252/1803 -f 1895/2255/1806 1896/2256/1807 1891/2252/1803 -f 1891/2252/1803 1896/2256/1807 1681/2083/1657 -f 1896/2256/1807 1680/2084/1658 1681/2083/1657 -f 1645/2244/1795 1897/2246/1797 1892/2245/1796 -f 1897/2246/1797 1898/2257/1808 1892/2245/1796 -f 1892/2245/1796 1898/2257/1808 1893/2253/1804 -f 1898/2257/1808 1899/2258/1809 1893/2253/1804 -f 1893/2253/1804 1899/2258/1809 1894/2254/1805 -f 1899/2258/1809 1900/2259/1810 1894/2254/1805 -f 1894/2254/1805 1900/2259/1810 1895/2255/1806 -f 1900/2259/1810 1901/2260/1811 1895/2255/1806 -f 1895/2255/1806 1901/2260/1811 1896/2256/1807 -f 1901/2260/1811 1902/2261/1812 1896/2256/1807 -f 1896/2256/1807 1902/2261/1812 1680/2084/1658 -f 1902/2261/1812 1679/2085/1659 1680/2084/1658 -f 1644/1945/1536 1629/1936/1527 1897/2246/1797 -f 1629/1936/1527 1628/2262/1813 1897/2246/1797 -f 1897/2246/1797 1628/2262/1813 1898/2257/1808 -f 1628/2262/1813 1627/2263/1814 1898/2257/1808 -f 1898/2257/1808 1627/2263/1814 1899/2258/1809 -f 1627/2263/1814 1626/2264/1815 1899/2258/1809 -f 1899/2258/1809 1626/2264/1815 1900/2259/1810 -f 1626/2264/1815 1625/2265/1816 1900/2259/1810 -f 1900/2259/1810 1625/2265/1816 1901/2260/1811 -f 1625/2265/1816 1624/2266/1817 1901/2260/1811 -f 1901/2260/1811 1624/2266/1817 1902/2261/1812 -f 1624/2266/1817 1623/2267/1818 1902/2261/1812 -f 1902/2261/1812 1623/2267/1818 1679/2085/1659 -f 1623/2267/1818 1520/2086/1542 1679/2085/1659 -f 1636/2268/1819 1678/1952/1543 1520/1951/1542 -f 1635/2269/1820 1903/2270/1821 1636/2268/1819 -f 1634/2271/1822 1904/2272/1823 1635/2269/1820 -f 1633/2273/1824 1906/2274/1825 1634/2271/1822 -f 1632/2275/1826 1909/2276/1827 1633/2273/1824 -f 1631/2277/1828 1913/2278/1829 1632/2275/1826 -f 1630/1944/1535 1918/2279/1830 1631/2277/1828 -f 1636/2268/1819 1903/2270/1821 1678/1952/1543 -f 1903/2270/1821 1677/2122/1687 1678/1952/1543 -f 1635/2269/1820 1904/2272/1823 1903/2270/1821 -f 1904/2272/1823 1905/2280/1831 1903/2270/1821 -f 1903/2270/1821 1905/2280/1831 1677/2122/1687 -f 1905/2280/1831 1676/2123/1688 1677/2122/1687 -f 1634/2271/1822 1906/2274/1825 1904/2272/1823 -f 1906/2274/1825 1907/2281/1832 1904/2272/1823 -f 1904/2272/1823 1907/2281/1832 1905/2280/1831 -f 1907/2281/1832 1908/2282/1833 1905/2280/1831 -f 1905/2280/1831 1908/2282/1833 1676/2123/1688 -f 1908/2282/1833 1675/2124/1689 1676/2123/1688 -f 1633/2273/1824 1909/2276/1827 1906/2274/1825 -f 1909/2276/1827 1910/2283/1834 1906/2274/1825 -f 1906/2274/1825 1910/2283/1834 1907/2281/1832 -f 1910/2283/1834 1911/2284/1835 1907/2281/1832 -f 1907/2281/1832 1911/2284/1835 1908/2282/1833 -f 1911/2284/1835 1912/2285/1836 1908/2282/1833 -f 1908/2282/1833 1912/2285/1836 1675/2124/1689 -f 1912/2285/1836 1674/2125/1690 1675/2124/1689 -f 1632/2275/1826 1913/2278/1829 1909/2276/1827 -f 1913/2278/1829 1914/2286/1837 1909/2276/1827 -f 1909/2276/1827 1914/2286/1837 1910/2283/1834 -f 1914/2286/1837 1915/2287/1838 1910/2283/1834 -f 1910/2283/1834 1915/2287/1838 1911/2284/1835 -f 1915/2287/1838 1916/2288/1839 1911/2284/1835 -f 1911/2284/1835 1916/2288/1839 1912/2285/1836 -f 1916/2288/1839 1917/2289/1840 1912/2285/1836 -f 1912/2285/1836 1917/2289/1840 1674/2125/1690 -f 1917/2289/1840 1673/2126/1691 1674/2125/1690 -f 1631/2277/1828 1918/2279/1830 1913/2278/1829 -f 1918/2279/1830 1919/2290/1841 1913/2278/1829 -f 1913/2278/1829 1919/2290/1841 1914/2286/1837 -f 1919/2290/1841 1920/2291/1842 1914/2286/1837 -f 1914/2286/1837 1920/2291/1842 1915/2287/1838 -f 1920/2291/1842 1921/2292/1843 1915/2287/1838 -f 1915/2287/1838 1921/2292/1843 1916/2288/1839 -f 1921/2292/1843 1922/2293/1844 1916/2288/1839 -f 1916/2288/1839 1922/2293/1844 1917/2289/1840 -f 1922/2293/1844 1923/2294/1845 1917/2289/1840 -f 1917/2289/1840 1923/2294/1845 1673/2126/1691 -f 1923/2294/1845 1672/2127/1692 1673/2126/1691 -f 1630/1944/1535 1615/1933/1524 1918/2279/1830 -f 1615/1933/1524 1614/2295/1846 1918/2279/1830 -f 1918/2279/1830 1614/2295/1846 1919/2290/1841 -f 1614/2295/1846 1613/2296/1847 1919/2290/1841 -f 1919/2290/1841 1613/2296/1847 1920/2291/1842 -f 1613/2296/1847 1612/2297/1848 1920/2291/1842 -f 1920/2291/1842 1612/2297/1848 1921/2292/1843 -f 1612/2297/1848 1611/2298/1849 1921/2292/1843 -f 1921/2292/1843 1611/2298/1849 1922/2293/1844 -f 1611/2298/1849 1610/2299/1850 1922/2293/1844 -f 1922/2293/1844 1610/2299/1850 1923/2294/1845 -f 1610/2299/1850 1609/2300/1851 1923/2294/1845 -f 1923/2294/1845 1609/2300/1851 1672/2127/1692 -f 1609/2300/1851 1519/1948/1539 1672/2127/1692 -f 1622/2301/1852 1671/1949/1540 1519/1948/1539 -f 1621/2302/1853 1924/2303/1854 1622/2301/1852 -f 1620/2304/1855 1925/2305/1856 1621/2302/1853 -f 1619/2306/1857 1927/2307/1858 1620/2304/1855 -f 1618/2308/1859 1930/2309/1860 1619/2306/1857 -f 1617/2310/1861 1934/2311/1862 1618/2308/1859 -f 1616/1943/1534 1939/2312/1863 1617/2310/1861 -f 1622/2301/1852 1924/2303/1854 1671/1949/1540 -f 1924/2303/1854 1670/2163/1714 1671/1949/1540 -f 1621/2302/1853 1925/2305/1856 1924/2303/1854 -f 1925/2305/1856 1926/2313/1864 1924/2303/1854 -f 1924/2303/1854 1926/2313/1864 1670/2163/1714 -f 1926/2313/1864 1669/2164/1715 1670/2163/1714 -f 1620/2304/1855 1927/2307/1858 1925/2305/1856 -f 1927/2307/1858 1928/2314/1865 1925/2305/1856 -f 1925/2305/1856 1928/2314/1865 1926/2313/1864 -f 1928/2314/1865 1929/2315/1866 1926/2313/1864 -f 1926/2313/1864 1929/2315/1866 1669/2164/1715 -f 1929/2315/1866 1668/2165/1716 1669/2164/1715 -f 1619/2306/1857 1930/2309/1860 1927/2307/1858 -f 1930/2309/1860 1931/2316/1867 1927/2307/1858 -f 1927/2307/1858 1931/2316/1867 1928/2314/1865 -f 1931/2316/1867 1932/2317/1868 1928/2314/1865 -f 1928/2314/1865 1932/2317/1868 1929/2315/1866 -f 1932/2317/1868 1933/2318/1869 1929/2315/1866 -f 1929/2315/1866 1933/2318/1869 1668/2165/1716 -f 1933/2318/1869 1667/2166/1717 1668/2165/1716 -f 1618/2308/1859 1934/2311/1862 1930/2309/1860 -f 1934/2311/1862 1935/2319/1870 1930/2309/1860 -f 1930/2309/1860 1935/2319/1870 1931/2316/1867 -f 1935/2319/1870 1936/2320/1871 1931/2316/1867 -f 1931/2316/1867 1936/2320/1871 1932/2317/1868 -f 1936/2320/1871 1937/2321/1872 1932/2317/1868 -f 1932/2317/1868 1937/2321/1872 1933/2318/1869 -f 1937/2321/1872 1938/2322/1873 1933/2318/1869 -f 1933/2318/1869 1938/2322/1873 1667/2166/1717 -f 1938/2322/1873 1666/2167/1718 1667/2166/1717 -f 1617/2310/1861 1939/2312/1863 1934/2311/1862 -f 1939/2312/1863 1940/2323/1874 1934/2311/1862 -f 1934/2311/1862 1940/2323/1874 1935/2319/1870 -f 1940/2323/1874 1941/2324/1875 1935/2319/1870 -f 1935/2319/1870 1941/2324/1875 1936/2320/1871 -f 1941/2324/1875 1942/2325/1876 1936/2320/1871 -f 1936/2320/1871 1942/2325/1876 1937/2321/1872 -f 1942/2325/1876 1943/2326/1877 1937/2321/1872 -f 1937/2321/1872 1943/2326/1877 1938/2322/1873 -f 1943/2326/1877 1944/2327/1878 1938/2322/1873 -f 1938/2322/1873 1944/2327/1878 1666/2167/1718 -f 1944/2327/1878 1665/2168/1719 1666/2167/1718 -f 1616/1943/1534 1601/1930/1521 1939/2312/1863 -f 1601/1930/1521 1600/2328/1879 1939/2312/1863 -f 1939/2312/1863 1600/2328/1879 1940/2323/1874 -f 1600/2328/1879 1599/2329/1880 1940/2323/1874 -f 1940/2323/1874 1599/2329/1880 1941/2324/1875 -f 1599/2329/1880 1598/2330/1881 1941/2324/1875 -f 1941/2324/1875 1598/2330/1881 1942/2325/1876 -f 1598/2330/1881 1597/2331/1882 1942/2325/1876 -f 1942/2325/1876 1597/2331/1882 1943/2326/1877 -f 1597/2331/1882 1596/2332/1883 1943/2326/1877 -f 1943/2326/1877 1596/2332/1883 1944/2327/1878 -f 1596/2332/1883 1595/2333/1884 1944/2327/1878 -f 1944/2327/1878 1595/2333/1884 1665/2168/1719 -f 1595/2333/1884 1523/1960/1551 1665/2168/1719 -f 1651/2201/1752 1664/2202/1753 1522/1957/1548 -f 1652/2200/1751 1945/2334/1885 1651/2201/1752 -f 1653/2199/1750 1946/2335/1886 1652/2200/1751 -f 1654/2198/1749 1948/2336/1887 1653/2199/1750 -f 1655/2197/1748 1951/2337/1888 1654/2198/1749 -f 1656/2196/1747 1955/2338/1889 1655/2197/1748 -f 1657/1942/1533 1960/2339/1890 1656/2196/1747 -f 1651/2201/1752 1945/2334/1885 1664/2202/1753 -f 1945/2334/1885 1663/2203/1754 1664/2202/1753 -f 1652/2200/1751 1946/2335/1886 1945/2334/1885 -f 1946/2335/1886 1947/2340/1891 1945/2334/1885 -f 1945/2334/1885 1947/2340/1891 1663/2203/1754 -f 1947/2340/1891 1662/2205/1756 1663/2203/1754 -f 1653/2199/1750 1948/2336/1887 1946/2335/1886 -f 1948/2336/1887 1949/2341/1892 1946/2335/1886 -f 1946/2335/1886 1949/2341/1892 1947/2340/1891 -f 1949/2341/1892 1950/2342/1893 1947/2340/1891 -f 1947/2340/1891 1950/2342/1893 1662/2205/1756 -f 1950/2342/1893 1661/2207/1758 1662/2205/1756 -f 1654/2198/1749 1951/2337/1888 1948/2336/1887 -f 1951/2337/1888 1952/2343/1894 1948/2336/1887 -f 1948/2336/1887 1952/2343/1894 1949/2341/1892 -f 1952/2343/1894 1953/2344/1895 1949/2341/1892 -f 1949/2341/1892 1953/2344/1895 1950/2342/1893 -f 1953/2344/1895 1954/2345/1896 1950/2342/1893 -f 1950/2342/1893 1954/2345/1896 1661/2207/1758 -f 1954/2345/1896 1660/2209/1760 1661/2207/1758 -f 1655/2197/1748 1955/2338/1889 1951/2337/1888 -f 1955/2338/1889 1956/2346/1897 1951/2337/1888 -f 1951/2337/1888 1956/2346/1897 1952/2343/1894 -f 1956/2346/1897 1957/2347/1898 1952/2343/1894 -f 1952/2343/1894 1957/2347/1898 1953/2344/1895 -f 1957/2347/1898 1958/2348/1899 1953/2344/1895 -f 1953/2344/1895 1958/2348/1899 1954/2345/1896 -f 1958/2348/1899 1959/2349/1900 1954/2345/1896 -f 1954/2345/1896 1959/2349/1900 1660/2209/1760 -f 1959/2349/1900 1659/2211/1762 1660/2209/1760 -f 1656/2196/1747 1960/2339/1890 1955/2338/1889 -f 1960/2339/1890 1961/2350/1901 1955/2338/1889 -f 1955/2338/1889 1961/2350/1901 1956/2346/1897 -f 1961/2350/1901 1962/2351/1902 1956/2346/1897 -f 1956/2346/1897 1962/2351/1902 1957/2347/1898 -f 1962/2351/1902 1963/2352/1903 1957/2347/1898 -f 1957/2347/1898 1963/2352/1903 1958/2348/1899 -f 1963/2352/1903 1964/2353/1904 1958/2348/1899 -f 1958/2348/1899 1964/2353/1904 1959/2349/1900 -f 1964/2353/1904 1965/2354/1905 1959/2349/1900 -f 1959/2349/1900 1965/2354/1905 1659/2211/1762 -f 1965/2354/1905 1658/1946/1537 1659/2211/1762 -f 1657/1942/1533 1594/1941/1532 1960/2339/1890 -f 1594/1941/1532 1593/2355/1906 1960/2339/1890 -f 1960/2339/1890 1593/2355/1906 1961/2350/1901 -f 1593/2355/1906 1592/2356/1907 1961/2350/1901 -f 1961/2350/1901 1592/2356/1907 1962/2351/1902 -f 1592/2356/1907 1591/2357/1908 1962/2351/1902 -f 1962/2351/1902 1591/2357/1908 1963/2352/1903 -f 1591/2357/1908 1590/2358/1909 1963/2352/1903 -f 1963/2352/1903 1590/2358/1909 1964/2353/1904 -f 1590/2358/1909 1589/2359/1910 1964/2353/1904 -f 1964/2353/1904 1589/2359/1910 1965/2354/1905 -f 1589/2359/1910 1588/2360/1911 1965/2354/1905 -f 1965/2354/1905 1588/2360/1911 1658/1946/1537 -f 1588/2360/1911 1517/1937/1528 1658/1946/1537 -f 1637/2234/1785 1650/2235/1786 1521/1954/1545 -f 1638/2233/1784 1966/2361/1912 1637/2234/1785 -f 1639/2232/1783 1967/2362/1913 1638/2233/1784 -f 1640/2231/1782 1969/2363/1914 1639/2232/1783 -f 1641/2230/1781 1972/2364/1915 1640/2231/1782 -f 1642/2229/1780 1976/2365/1916 1641/2230/1781 -f 1643/1939/1530 1981/2366/1917 1642/2229/1780 -f 1637/2234/1785 1966/2361/1912 1650/2235/1786 -f 1966/2361/1912 1649/2236/1787 1650/2235/1786 -f 1638/2233/1784 1967/2362/1913 1966/2361/1912 -f 1967/2362/1913 1968/2367/1918 1966/2361/1912 -f 1966/2361/1912 1968/2367/1918 1649/2236/1787 -f 1968/2367/1918 1648/2238/1789 1649/2236/1787 -f 1639/2232/1783 1969/2363/1914 1967/2362/1913 -f 1969/2363/1914 1970/2368/1919 1967/2362/1913 -f 1967/2362/1913 1970/2368/1919 1968/2367/1918 -f 1970/2368/1919 1971/2369/1920 1968/2367/1918 -f 1968/2367/1918 1971/2369/1920 1648/2238/1789 -f 1971/2369/1920 1647/2240/1791 1648/2238/1789 -f 1640/2231/1782 1972/2364/1915 1969/2363/1914 -f 1972/2364/1915 1973/2370/1921 1969/2363/1914 -f 1969/2363/1914 1973/2370/1921 1970/2368/1919 -f 1973/2370/1921 1974/2371/1922 1970/2368/1919 -f 1970/2368/1919 1974/2371/1922 1971/2369/1920 -f 1974/2371/1922 1975/2372/1923 1971/2369/1920 -f 1971/2369/1920 1975/2372/1923 1647/2240/1791 -f 1975/2372/1923 1646/2242/1793 1647/2240/1791 -f 1641/2230/1781 1976/2365/1916 1972/2364/1915 -f 1976/2365/1916 1977/2373/1924 1972/2364/1915 -f 1972/2364/1915 1977/2373/1924 1973/2370/1921 -f 1977/2373/1924 1978/2374/1925 1973/2370/1921 -f 1973/2370/1921 1978/2374/1925 1974/2371/1922 -f 1978/2374/1925 1979/2375/1926 1974/2371/1922 -f 1974/2371/1922 1979/2375/1926 1975/2372/1923 -f 1979/2375/1926 1980/2376/1927 1975/2372/1923 -f 1975/2372/1923 1980/2376/1927 1646/2242/1793 -f 1980/2376/1927 1645/2244/1795 1646/2242/1793 -f 1642/2229/1780 1981/2366/1917 1976/2365/1916 -f 1981/2366/1917 1982/2377/1928 1976/2365/1916 -f 1976/2365/1916 1982/2377/1928 1977/2373/1924 -f 1982/2377/1928 1983/2378/1929 1977/2373/1924 -f 1977/2373/1924 1983/2378/1929 1978/2374/1925 -f 1983/2378/1929 1984/2379/1930 1978/2374/1925 -f 1978/2374/1925 1984/2379/1930 1979/2375/1926 -f 1984/2379/1930 1985/2380/1931 1979/2375/1926 -f 1979/2375/1926 1985/2380/1931 1980/2376/1927 -f 1985/2380/1931 1986/2381/1932 1980/2376/1927 -f 1980/2376/1927 1986/2381/1932 1645/2244/1795 -f 1986/2381/1932 1644/1945/1536 1645/2244/1795 -f 1643/1939/1530 1587/1938/1529 1981/2366/1917 -f 1587/1938/1529 1586/2382/1933 1981/2366/1917 -f 1981/2366/1917 1586/2382/1933 1982/2377/1928 -f 1586/2382/1933 1585/2383/1934 1982/2377/1928 -f 1982/2377/1928 1585/2383/1934 1983/2378/1929 -f 1585/2383/1934 1584/2384/1935 1983/2378/1929 -f 1983/2378/1929 1584/2384/1935 1984/2379/1930 -f 1584/2384/1935 1583/2385/1936 1984/2379/1930 -f 1984/2379/1930 1583/2385/1936 1985/2380/1931 -f 1583/2385/1936 1582/2386/1937 1985/2380/1931 -f 1985/2380/1931 1582/2386/1937 1986/2381/1932 -f 1582/2386/1937 1581/2387/1938 1986/2381/1932 -f 1986/2381/1932 1581/2387/1938 1644/1945/1536 -f 1581/2387/1938 1516/1934/1525 1644/1945/1536 -f 1623/2267/1818 1636/2388/1819 1520/2086/1542 -f 1624/2266/1817 1987/2389/1939 1623/2267/1818 -f 1625/2265/1816 1988/2390/1940 1624/2266/1817 -f 1626/2264/1815 1990/2391/1941 1625/2265/1816 -f 1627/2263/1814 1993/2392/1942 1626/2264/1815 -f 1628/2262/1813 1997/2393/1943 1627/2263/1814 -f 1629/1936/1527 2002/2394/1944 1628/2262/1813 -f 1623/2267/1818 1987/2389/1939 1636/2388/1819 -f 1987/2389/1939 1635/2395/1820 1636/2388/1819 -f 1624/2266/1817 1988/2390/1940 1987/2389/1939 -f 1988/2390/1940 1989/2396/1945 1987/2389/1939 -f 1987/2389/1939 1989/2396/1945 1635/2395/1820 -f 1989/2396/1945 1634/2397/1822 1635/2395/1820 -f 1625/2265/1816 1990/2391/1941 1988/2390/1940 -f 1990/2391/1941 1991/2398/1946 1988/2390/1940 -f 1988/2390/1940 1991/2398/1946 1989/2396/1945 -f 1991/2398/1946 1992/2399/1947 1989/2396/1945 -f 1989/2396/1945 1992/2399/1947 1634/2397/1822 -f 1992/2399/1947 1633/2400/1824 1634/2397/1822 -f 1626/2264/1815 1993/2392/1942 1990/2391/1941 -f 1993/2392/1942 1994/2401/1948 1990/2391/1941 -f 1990/2391/1941 1994/2401/1948 1991/2398/1946 -f 1994/2401/1948 1995/2402/1949 1991/2398/1946 -f 1991/2398/1946 1995/2402/1949 1992/2399/1947 -f 1995/2402/1949 1996/2403/1950 1992/2399/1947 -f 1992/2399/1947 1996/2403/1950 1633/2400/1824 -f 1996/2403/1950 1632/2404/1826 1633/2400/1824 -f 1627/2263/1814 1997/2393/1943 1993/2392/1942 -f 1997/2393/1943 1998/2405/1951 1993/2392/1942 -f 1993/2392/1942 1998/2405/1951 1994/2401/1948 -f 1998/2405/1951 1999/2406/1952 1994/2401/1948 -f 1994/2401/1948 1999/2406/1952 1995/2402/1949 -f 1999/2406/1952 2000/2407/1953 1995/2402/1949 -f 1995/2402/1949 2000/2407/1953 1996/2403/1950 -f 2000/2407/1953 2001/2408/1954 1996/2403/1950 -f 1996/2403/1950 2001/2408/1954 1632/2404/1826 -f 2001/2408/1954 1631/2409/1828 1632/2404/1826 -f 1628/2262/1813 2002/2394/1944 1997/2393/1943 -f 2002/2394/1944 2003/2410/1955 1997/2393/1943 -f 1997/2393/1943 2003/2410/1955 1998/2405/1951 -f 2003/2410/1955 2004/2411/1956 1998/2405/1951 -f 1998/2405/1951 2004/2411/1956 1999/2406/1952 -f 2004/2411/1956 2005/2412/1957 1999/2406/1952 -f 1999/2406/1952 2005/2412/1957 2000/2407/1953 -f 2005/2412/1957 2006/2413/1958 2000/2407/1953 -f 2000/2407/1953 2006/2413/1958 2001/2408/1954 -f 2006/2413/1958 2007/2414/1959 2001/2408/1954 -f 2001/2408/1954 2007/2414/1959 1631/2409/1828 -f 2007/2414/1959 1630/2415/1535 1631/2409/1828 -f 1629/1936/1527 1573/1935/1526 2002/2394/1944 -f 1573/1935/1526 1572/2416/1960 2002/2394/1944 -f 2002/2394/1944 1572/2416/1960 2003/2410/1955 -f 1572/2416/1960 1571/2417/1961 2003/2410/1955 -f 2003/2410/1955 1571/2417/1961 2004/2411/1956 -f 1571/2417/1961 1570/2418/1962 2004/2411/1956 -f 2004/2411/1956 1570/2418/1962 2005/2412/1957 -f 1570/2418/1962 1569/2419/1963 2005/2412/1957 -f 2005/2412/1957 1569/2419/1963 2006/2413/1958 -f 1569/2419/1963 1568/2420/1964 2006/2413/1958 -f 2006/2413/1958 1568/2420/1964 2007/2414/1959 -f 1568/2420/1964 1567/2421/1965 2007/2414/1959 -f 2007/2414/1959 1567/2421/1965 1630/2415/1535 -f 1567/2421/1965 1515/2422/1522 1630/2415/1535 -f 1609/2300/1851 1622/2301/1852 1519/1948/1539 -f 1610/2299/1850 2008/2423/1966 1609/2300/1851 -f 1611/2298/1849 2009/2424/1967 1610/2299/1850 -f 1612/2297/1848 2011/2425/1968 1611/2298/1849 -f 1613/2296/1847 2014/2426/1969 1612/2297/1848 -f 1614/2295/1846 2018/2427/1970 1613/2296/1847 -f 1615/1933/1524 2023/2428/1971 1614/2295/1846 -f 1609/2300/1851 2008/2423/1966 1622/2301/1852 -f 2008/2423/1966 1621/2302/1853 1622/2301/1852 -f 1610/2299/1850 2009/2424/1967 2008/2423/1966 -f 2009/2424/1967 2010/2429/1972 2008/2423/1966 -f 2008/2423/1966 2010/2429/1972 1621/2302/1853 -f 2010/2429/1972 1620/2304/1855 1621/2302/1853 -f 1611/2298/1849 2011/2425/1968 2009/2424/1967 -f 2011/2425/1968 2012/2430/1973 2009/2424/1967 -f 2009/2424/1967 2012/2430/1973 2010/2429/1972 -f 2012/2430/1973 2013/2431/1974 2010/2429/1972 -f 2010/2429/1972 2013/2431/1974 1620/2304/1855 -f 2013/2431/1974 1619/2306/1857 1620/2304/1855 -f 1612/2297/1848 2014/2426/1969 2011/2425/1968 -f 2014/2426/1969 2015/2432/1975 2011/2425/1968 -f 2011/2425/1968 2015/2432/1975 2012/2430/1973 -f 2015/2432/1975 2016/2433/1976 2012/2430/1973 -f 2012/2430/1973 2016/2433/1976 2013/2431/1974 -f 2016/2433/1976 2017/2434/1977 2013/2431/1974 -f 2013/2431/1974 2017/2434/1977 1619/2306/1857 -f 2017/2434/1977 1618/2308/1859 1619/2306/1857 -f 1613/2296/1847 2018/2427/1970 2014/2426/1969 -f 2018/2427/1970 2019/2435/1978 2014/2426/1969 -f 2014/2426/1969 2019/2435/1978 2015/2432/1975 -f 2019/2435/1978 2020/2436/1979 2015/2432/1975 -f 2015/2432/1975 2020/2436/1979 2016/2433/1976 -f 2020/2436/1979 2021/2437/1980 2016/2433/1976 -f 2016/2433/1976 2021/2437/1980 2017/2434/1977 -f 2021/2437/1980 2022/2438/1981 2017/2434/1977 -f 2017/2434/1977 2022/2438/1981 1618/2308/1859 -f 2022/2438/1981 1617/2310/1861 1618/2308/1859 -f 1614/2295/1846 2023/2428/1971 2018/2427/1970 -f 2023/2428/1971 2024/2439/1982 2018/2427/1970 -f 2018/2427/1970 2024/2439/1982 2019/2435/1978 -f 2024/2439/1982 2025/2440/1983 2019/2435/1978 -f 2019/2435/1978 2025/2440/1983 2020/2436/1979 -f 2025/2440/1983 2026/2441/1984 2020/2436/1979 -f 2020/2436/1979 2026/2441/1984 2021/2437/1980 -f 2026/2441/1984 2027/2442/1985 2021/2437/1980 -f 2021/2437/1980 2027/2442/1985 2022/2438/1981 -f 2027/2442/1985 2028/2443/1986 2022/2438/1981 -f 2022/2438/1981 2028/2443/1986 1617/2310/1861 -f 2028/2443/1986 1616/1943/1534 1617/2310/1861 -f 1615/1933/1524 1545/1932/1523 2023/2428/1971 -f 1545/1932/1523 1544/2444/1987 2023/2428/1971 -f 2023/2428/1971 1544/2444/1987 2024/2439/1982 -f 1544/2444/1987 1543/2445/1988 2024/2439/1982 -f 2024/2439/1982 1543/2445/1988 2025/2440/1983 -f 1543/2445/1988 1542/2446/1989 2025/2440/1983 -f 2025/2440/1983 1542/2446/1989 2026/2441/1984 -f 1542/2446/1989 1541/2447/1990 2026/2441/1984 -f 2026/2441/1984 1541/2447/1990 2027/2442/1985 -f 1541/2447/1990 1540/2448/1991 2027/2442/1985 -f 2027/2442/1985 1540/2448/1991 2028/2443/1986 -f 1540/2448/1991 1539/2449/1992 2028/2443/1986 -f 2028/2443/1986 1539/2449/1992 1616/1943/1534 -f 1539/2449/1992 1514/1918/1515 1616/1943/1534 -f 1595/2333/1884 1608/2169/1720 1523/1960/1551 -f 1596/2332/1883 2029/2450/1993 1595/2333/1884 -f 1597/2331/1882 2030/2451/1994 1596/2332/1883 -f 1598/2330/1881 2032/2452/1995 1597/2331/1882 -f 1599/2329/1880 2035/2453/1996 1598/2330/1881 -f 1600/2328/1879 2039/2454/1997 1599/2329/1880 -f 1601/1930/1521 2044/2455/1998 1600/2328/1879 -f 1595/2333/1884 2029/2450/1993 1608/2169/1720 -f 2029/2450/1993 1607/2170/1721 1608/2169/1720 -f 1596/2332/1883 2030/2451/1994 2029/2450/1993 -f 2030/2451/1994 2031/2456/1999 2029/2450/1993 -f 2029/2450/1993 2031/2456/1999 1607/2170/1721 -f 2031/2456/1999 1606/2172/1723 1607/2170/1721 -f 1597/2331/1882 2032/2452/1995 2030/2451/1994 -f 2032/2452/1995 2033/2457/2000 2030/2451/1994 -f 2030/2451/1994 2033/2457/2000 2031/2456/1999 -f 2033/2457/2000 2034/2458/2001 2031/2456/1999 -f 2031/2456/1999 2034/2458/2001 1606/2172/1723 -f 2034/2458/2001 1605/2174/1725 1606/2172/1723 -f 1598/2330/1881 2035/2453/1996 2032/2452/1995 -f 2035/2453/1996 2036/2459/2002 2032/2452/1995 -f 2032/2452/1995 2036/2459/2002 2033/2457/2000 -f 2036/2459/2002 2037/2460/2003 2033/2457/2000 -f 2033/2457/2000 2037/2460/2003 2034/2458/2001 -f 2037/2460/2003 2038/2461/2004 2034/2458/2001 -f 2034/2458/2001 2038/2461/2004 1605/2174/1725 -f 2038/2461/2004 1604/2176/1727 1605/2174/1725 -f 1599/2329/1880 2039/2454/1997 2035/2453/1996 -f 2039/2454/1997 2040/2462/2005 2035/2453/1996 -f 2035/2453/1996 2040/2462/2005 2036/2459/2002 -f 2040/2462/2005 2041/2463/2006 2036/2459/2002 -f 2036/2459/2002 2041/2463/2006 2037/2460/2003 -f 2041/2463/2006 2042/2464/2007 2037/2460/2003 -f 2037/2460/2003 2042/2464/2007 2038/2461/2004 -f 2042/2464/2007 2043/2465/2008 2038/2461/2004 -f 2038/2461/2004 2043/2465/2008 1604/2176/1727 -f 2043/2465/2008 1603/2178/1729 1604/2176/1727 -f 1600/2328/1879 2044/2455/1998 2039/2454/1997 -f 2044/2455/1998 2045/2466/2009 2039/2454/1997 -f 2039/2454/1997 2045/2466/2009 2040/2462/2005 -f 2045/2466/2009 2046/2467/2010 2040/2462/2005 -f 2040/2462/2005 2046/2467/2010 2041/2463/2006 -f 2046/2467/2010 2047/2468/2011 2041/2463/2006 -f 2041/2463/2006 2047/2468/2011 2042/2464/2007 -f 2047/2468/2011 2048/2469/2012 2042/2464/2007 -f 2042/2464/2007 2048/2469/2012 2043/2465/2008 -f 2048/2469/2012 2049/2470/2013 2043/2465/2008 -f 2043/2465/2008 2049/2470/2013 1603/2178/1729 -f 2049/2470/2013 1602/1947/1538 1603/2178/1729 -f 1601/1930/1521 1552/1920/1517 2044/2455/1998 -f 1552/1920/1517 1551/2471/2014 2044/2455/1998 -f 2044/2455/1998 1551/2471/2014 2045/2466/2009 -f 1551/2471/2014 1550/2472/2015 2045/2466/2009 -f 2045/2466/2009 1550/2472/2015 2046/2467/2010 -f 1550/2472/2015 1549/2473/2016 2046/2467/2010 -f 2046/2467/2010 1549/2473/2016 2047/2468/2011 -f 1549/2473/2016 1548/2474/2017 2047/2468/2011 -f 2047/2468/2011 1548/2474/2017 2048/2469/2012 -f 1548/2474/2017 1547/2475/2018 2048/2469/2012 -f 2048/2469/2012 1547/2475/2018 2049/2470/2013 -f 1547/2475/2018 1546/2476/2019 2049/2470/2013 -f 2049/2470/2013 1546/2476/2019 1602/1947/1538 -f 1546/2476/2019 1518/1940/1531 1602/1947/1538 -f 1559/2477/2020 1594/1941/1532 1518/1940/1531 -f 1558/2478/2021 2050/2479/2022 1559/2477/2020 -f 1557/2480/2023 2051/2481/2024 1558/2478/2021 -f 1556/2482/2025 2053/2483/2026 1557/2480/2023 -f 1555/2484/2027 2056/2485/2028 1556/2482/2025 -f 1554/2486/2029 2060/2487/2030 1555/2484/2027 -f 1553/1929/1520 2065/2488/2031 1554/2486/2029 -f 1559/2477/2020 2050/2479/2022 1594/1941/1532 -f 2050/2479/2022 1593/2355/1906 1594/1941/1532 -f 1558/2478/2021 2051/2481/2024 2050/2479/2022 -f 2051/2481/2024 2052/2489/2032 2050/2479/2022 -f 2050/2479/2022 2052/2489/2032 1593/2355/1906 -f 2052/2489/2032 1592/2356/1907 1593/2355/1906 -f 1557/2480/2023 2053/2483/2026 2051/2481/2024 -f 2053/2483/2026 2054/2490/2033 2051/2481/2024 -f 2051/2481/2024 2054/2490/2033 2052/2489/2032 -f 2054/2490/2033 2055/2491/2034 2052/2489/2032 -f 2052/2489/2032 2055/2491/2034 1592/2356/1907 -f 2055/2491/2034 1591/2357/1908 1592/2356/1907 -f 1556/2482/2025 2056/2485/2028 2053/2483/2026 -f 2056/2485/2028 2057/2492/2035 2053/2483/2026 -f 2053/2483/2026 2057/2492/2035 2054/2490/2033 -f 2057/2492/2035 2058/2493/2036 2054/2490/2033 -f 2054/2490/2033 2058/2493/2036 2055/2491/2034 -f 2058/2493/2036 2059/2494/2037 2055/2491/2034 -f 2055/2491/2034 2059/2494/2037 1591/2357/1908 -f 2059/2494/2037 1590/2358/1909 1591/2357/1908 -f 1555/2484/2027 2060/2487/2030 2056/2485/2028 -f 2060/2487/2030 2061/2495/2038 2056/2485/2028 -f 2056/2485/2028 2061/2495/2038 2057/2492/2035 -f 2061/2495/2038 2062/2496/2039 2057/2492/2035 -f 2057/2492/2035 2062/2496/2039 2058/2493/2036 -f 2062/2496/2039 2063/2497/2040 2058/2493/2036 -f 2058/2493/2036 2063/2497/2040 2059/2494/2037 -f 2063/2497/2040 2064/2498/2041 2059/2494/2037 -f 2059/2494/2037 2064/2498/2041 1590/2358/1909 -f 2064/2498/2041 1589/2359/1910 1590/2358/1909 -f 1554/2486/2029 2065/2488/2031 2060/2487/2030 -f 2065/2488/2031 2066/2499/2042 2060/2487/2030 -f 2060/2487/2030 2066/2499/2042 2061/2495/2038 -f 2066/2499/2042 2067/2500/2043 2061/2495/2038 -f 2061/2495/2038 2067/2500/2043 2062/2496/2039 -f 2067/2500/2043 2068/2501/2044 2062/2496/2039 -f 2062/2496/2039 2068/2501/2044 2063/2497/2040 -f 2068/2501/2044 2069/2502/2045 2063/2497/2040 -f 2063/2497/2040 2069/2502/2045 2064/2498/2041 -f 2069/2502/2045 2070/2503/2046 2064/2498/2041 -f 2064/2498/2041 2070/2503/2046 1589/2359/1910 -f 2070/2503/2046 1588/2360/1911 1589/2359/1910 -f 1553/1929/1520 1580/1928/1519 2065/2488/2031 -f 1580/1928/1519 1579/2504/2047 2065/2488/2031 -f 2065/2488/2031 1579/2504/2047 2066/2499/2042 -f 1579/2504/2047 1578/2505/2048 2066/2499/2042 -f 2066/2499/2042 1578/2505/2048 2067/2500/2043 -f 1578/2505/2048 1577/2506/2049 2067/2500/2043 -f 2067/2500/2043 1577/2506/2049 2068/2501/2044 -f 1577/2506/2049 1576/2507/2050 2068/2501/2044 -f 2068/2501/2044 1576/2507/2050 2069/2502/2045 -f 1576/2507/2050 1575/2508/2051 2069/2502/2045 -f 2069/2502/2045 1575/2508/2051 2070/2503/2046 -f 1575/2508/2051 1574/2509/2052 2070/2503/2046 -f 2070/2503/2046 1574/2509/2052 1588/2360/1911 -f 1574/2509/2052 1517/1937/1528 1588/2360/1911 -f 1574/2510/2052 1587/1938/1529 1517/1937/1528 -f 1575/2511/2051 2071/2512/2053 1574/2510/2052 -f 1576/2513/2050 2072/2514/2054 1575/2511/2051 -f 1577/2515/2049 2074/2516/2055 1576/2513/2050 -f 1578/2517/2048 2077/2518/2056 1577/2515/2049 -f 1579/2519/2047 2081/2520/2057 1578/2517/2048 -f 1580/1926/1519 2086/2521/2058 1579/2519/2047 -f 1574/2510/2052 2071/2512/2053 1587/1938/1529 -f 2071/2512/2053 1586/2382/1933 1587/1938/1529 -f 1575/2511/2051 2072/2514/2054 2071/2512/2053 -f 2072/2514/2054 2073/2522/2059 2071/2512/2053 -f 2071/2512/2053 2073/2522/2059 1586/2382/1933 -f 2073/2522/2059 1585/2383/1934 1586/2382/1933 -f 1576/2513/2050 2074/2516/2055 2072/2514/2054 -f 2074/2516/2055 2075/2523/2060 2072/2514/2054 -f 2072/2514/2054 2075/2523/2060 2073/2522/2059 -f 2075/2523/2060 2076/2524/2061 2073/2522/2059 -f 2073/2522/2059 2076/2524/2061 1585/2383/1934 -f 2076/2524/2061 1584/2384/1935 1585/2383/1934 -f 1577/2515/2049 2077/2518/2056 2074/2516/2055 -f 2077/2518/2056 2078/2525/2062 2074/2516/2055 -f 2074/2516/2055 2078/2525/2062 2075/2523/2060 -f 2078/2525/2062 2079/2526/2063 2075/2523/2060 -f 2075/2523/2060 2079/2526/2063 2076/2524/2061 -f 2079/2526/2063 2080/2527/2064 2076/2524/2061 -f 2076/2524/2061 2080/2527/2064 1584/2384/1935 -f 2080/2527/2064 1583/2385/1936 1584/2384/1935 -f 1578/2517/2048 2081/2520/2057 2077/2518/2056 -f 2081/2520/2057 2082/2528/2065 2077/2518/2056 -f 2077/2518/2056 2082/2528/2065 2078/2525/2062 -f 2082/2528/2065 2083/2529/2066 2078/2525/2062 -f 2078/2525/2062 2083/2529/2066 2079/2526/2063 -f 2083/2529/2066 2084/2530/2067 2079/2526/2063 -f 2079/2526/2063 2084/2530/2067 2080/2527/2064 -f 2084/2530/2067 2085/2531/2068 2080/2527/2064 -f 2080/2527/2064 2085/2531/2068 1583/2385/1936 -f 2085/2531/2068 1582/2386/1937 1583/2385/1936 -f 1579/2519/2047 2086/2521/2058 2081/2520/2057 -f 2086/2521/2058 2087/2532/2069 2081/2520/2057 -f 2081/2520/2057 2087/2532/2069 2082/2528/2065 -f 2087/2532/2069 2088/2533/2070 2082/2528/2065 -f 2082/2528/2065 2088/2533/2070 2083/2529/2066 -f 2088/2533/2070 2089/2534/2071 2083/2529/2066 -f 2083/2529/2066 2089/2534/2071 2084/2530/2067 -f 2089/2534/2071 2090/2535/2072 2084/2530/2067 -f 2084/2530/2067 2090/2535/2072 2085/2531/2068 -f 2090/2535/2072 2091/2536/2073 2085/2531/2068 -f 2085/2531/2068 2091/2536/2073 1582/2386/1937 -f 2091/2536/2073 1581/2387/1938 1582/2386/1937 -f 1580/1926/1519 1566/1925/1518 2086/2521/2058 -f 1566/1925/1518 1565/2537/2074 2086/2521/2058 -f 2086/2521/2058 1565/2537/2074 2087/2532/2069 -f 1565/2537/2074 1564/2538/2075 2087/2532/2069 -f 2087/2532/2069 1564/2538/2075 2088/2533/2070 -f 1564/2538/2075 1563/2539/2076 2088/2533/2070 -f 2088/2533/2070 1563/2539/2076 2089/2534/2071 -f 1563/2539/2076 1562/2540/2077 2089/2534/2071 -f 2089/2534/2071 1562/2540/2077 2090/2535/2072 -f 1562/2540/2077 1561/2541/2078 2090/2535/2072 -f 2090/2535/2072 1561/2541/2078 2091/2536/2073 -f 1561/2541/2078 1560/2542/2079 2091/2536/2073 -f 2091/2536/2073 1560/2542/2079 1581/2387/1938 -f 1560/2542/2079 1516/1934/1525 1581/2387/1938 -f 1560/2543/2079 1573/1935/1526 1516/1934/1525 -f 1561/2544/2078 2092/2545/2080 1560/2543/2079 -f 1562/2546/2077 2093/2547/2081 1561/2544/2078 -f 1563/2548/2076 2095/2549/2082 1562/2546/2077 -f 1564/2550/2075 2098/2551/2083 1563/2548/2076 -f 1565/2552/2074 2102/2553/2084 1564/2550/2075 -f 1566/1923/1518 2107/2554/2085 1565/2552/2074 -f 1560/2543/2079 2092/2545/2080 1573/1935/1526 -f 2092/2545/2080 1572/2416/1960 1573/1935/1526 -f 1561/2544/2078 2093/2547/2081 2092/2545/2080 -f 2093/2547/2081 2094/2555/2086 2092/2545/2080 -f 2092/2545/2080 2094/2555/2086 1572/2416/1960 -f 2094/2555/2086 1571/2417/1961 1572/2416/1960 -f 1562/2546/2077 2095/2549/2082 2093/2547/2081 -f 2095/2549/2082 2096/2556/2087 2093/2547/2081 -f 2093/2547/2081 2096/2556/2087 2094/2555/2086 -f 2096/2556/2087 2097/2557/2088 2094/2555/2086 -f 2094/2555/2086 2097/2557/2088 1571/2417/1961 -f 2097/2557/2088 1570/2418/1962 1571/2417/1961 -f 1563/2548/2076 2098/2551/2083 2095/2549/2082 -f 2098/2551/2083 2099/2558/2089 2095/2549/2082 -f 2095/2549/2082 2099/2558/2089 2096/2556/2087 -f 2099/2558/2089 2100/2559/2090 2096/2556/2087 -f 2096/2556/2087 2100/2559/2090 2097/2557/2088 -f 2100/2559/2090 2101/2560/2091 2097/2557/2088 -f 2097/2557/2088 2101/2560/2091 1570/2418/1962 -f 2101/2560/2091 1569/2419/1963 1570/2418/1962 -f 1564/2550/2075 2102/2553/2084 2098/2551/2083 -f 2102/2553/2084 2103/2561/2092 2098/2551/2083 -f 2098/2551/2083 2103/2561/2092 2099/2558/2089 -f 2103/2561/2092 2104/2562/2093 2099/2558/2089 -f 2099/2558/2089 2104/2562/2093 2100/2559/2090 -f 2104/2562/2093 2105/2563/2094 2100/2559/2090 -f 2100/2559/2090 2105/2563/2094 2101/2560/2091 -f 2105/2563/2094 2106/2564/2095 2101/2560/2091 -f 2101/2560/2091 2106/2564/2095 1569/2419/1963 -f 2106/2564/2095 1568/2420/1964 1569/2419/1963 -f 1565/2552/2074 2107/2554/2085 2102/2553/2084 -f 2107/2554/2085 2108/2565/2096 2102/2553/2084 -f 2102/2553/2084 2108/2565/2096 2103/2561/2092 -f 2108/2565/2096 2109/2566/2097 2103/2561/2092 -f 2103/2561/2092 2109/2566/2097 2104/2562/2093 -f 2109/2566/2097 2110/2567/2098 2104/2562/2093 -f 2104/2562/2093 2110/2567/2098 2105/2563/2094 -f 2110/2567/2098 2111/2568/2099 2105/2563/2094 -f 2105/2563/2094 2111/2568/2099 2106/2564/2095 -f 2111/2568/2099 2112/2569/2100 2106/2564/2095 -f 2106/2564/2095 2112/2569/2100 1568/2420/1964 -f 2112/2569/2100 1567/2421/1965 1568/2420/1964 -f 1566/1923/1518 1531/1922/1514 2107/2554/2085 -f 1531/1922/1514 1530/2570/2101 2107/2554/2085 -f 2107/2554/2085 1530/2570/2101 2108/2565/2096 -f 1530/2570/2101 1529/2571/2102 2108/2565/2096 -f 2108/2565/2096 1529/2571/2102 2109/2566/2097 -f 1529/2571/2102 1528/2572/2103 2109/2566/2097 -f 2109/2566/2097 1528/2572/2103 2110/2567/2098 -f 1528/2572/2103 1527/2573/2104 2110/2567/2098 -f 2110/2567/2098 1527/2573/2104 2111/2568/2099 -f 1527/2573/2104 1526/2574/2105 2111/2568/2099 -f 2111/2568/2099 1526/2574/2105 2112/2569/2100 -f 1526/2574/2105 1525/2575/2106 2112/2569/2100 -f 2112/2569/2100 1525/2575/2106 1567/2421/1965 -f 1525/2575/2106 1515/2422/1522 1567/2421/1965 -f 1546/2476/2019 1559/2576/2020 1518/1940/1531 -f 1547/2475/2018 2113/2577/2107 1546/2476/2019 -f 1548/2474/2017 2114/2578/2108 1547/2475/2018 -f 1549/2473/2016 2116/2579/2109 1548/2474/2017 -f 1550/2472/2015 2119/2580/2110 1549/2473/2016 -f 1551/2471/2014 2123/2581/2111 1550/2472/2015 -f 1552/1920/1517 2128/2582/2112 1551/2471/2014 -f 1546/2476/2019 2113/2577/2107 1559/2576/2020 -f 2113/2577/2107 1558/2583/2021 1559/2576/2020 -f 1547/2475/2018 2114/2578/2108 2113/2577/2107 -f 2114/2578/2108 2115/2584/2113 2113/2577/2107 -f 2113/2577/2107 2115/2584/2113 1558/2583/2021 -f 2115/2584/2113 1557/2585/2023 1558/2583/2021 -f 1548/2474/2017 2116/2579/2109 2114/2578/2108 -f 2116/2579/2109 2117/2586/2114 2114/2578/2108 -f 2114/2578/2108 2117/2586/2114 2115/2584/2113 -f 2117/2586/2114 2118/2587/2115 2115/2584/2113 -f 2115/2584/2113 2118/2587/2115 1557/2585/2023 -f 2118/2587/2115 1556/2588/2025 1557/2585/2023 -f 1549/2473/2016 2119/2580/2110 2116/2579/2109 -f 2119/2580/2110 2120/2589/2116 2116/2579/2109 -f 2116/2579/2109 2120/2589/2116 2117/2586/2114 -f 2120/2589/2116 2121/2590/2117 2117/2586/2114 -f 2117/2586/2114 2121/2590/2117 2118/2587/2115 -f 2121/2590/2117 2122/2591/2118 2118/2587/2115 -f 2118/2587/2115 2122/2591/2118 1556/2588/2025 -f 2122/2591/2118 1555/2592/2027 1556/2588/2025 -f 1550/2472/2015 2123/2581/2111 2119/2580/2110 -f 2123/2581/2111 2124/2593/2119 2119/2580/2110 -f 2119/2580/2110 2124/2593/2119 2120/2589/2116 -f 2124/2593/2119 2125/2594/2120 2120/2589/2116 -f 2120/2589/2116 2125/2594/2120 2121/2590/2117 -f 2125/2594/2120 2126/2595/2121 2121/2590/2117 -f 2121/2590/2117 2126/2595/2121 2122/2591/2118 -f 2126/2595/2121 2127/2596/2122 2122/2591/2118 -f 2122/2591/2118 2127/2596/2122 1555/2592/2027 -f 2127/2596/2122 1554/2597/2029 1555/2592/2027 -f 1551/2471/2014 2128/2582/2112 2123/2581/2111 -f 2128/2582/2112 2129/2598/2123 2123/2581/2111 -f 2123/2581/2111 2129/2598/2123 2124/2593/2119 -f 2129/2598/2123 2130/2599/2124 2124/2593/2119 -f 2124/2593/2119 2130/2599/2124 2125/2594/2120 -f 2130/2599/2124 2131/2600/2125 2125/2594/2120 -f 2125/2594/2120 2131/2600/2125 2126/2595/2121 -f 2131/2600/2125 2132/2601/2126 2126/2595/2121 -f 2126/2595/2121 2132/2601/2126 2127/2596/2122 -f 2132/2601/2126 2133/2602/2127 2127/2596/2122 -f 2127/2596/2122 2133/2602/2127 1554/2597/2029 -f 2133/2602/2127 1553/2603/1520 1554/2597/2029 -f 1552/1920/1517 1538/1919/1516 2128/2582/2112 -f 1538/1919/1516 1537/2604/2128 2128/2582/2112 -f 2128/2582/2112 1537/2604/2128 2129/2598/2123 -f 1537/2604/2128 1536/2605/2129 2129/2598/2123 -f 2129/2598/2123 1536/2605/2129 2130/2599/2124 -f 1536/2605/2129 1535/2606/2130 2130/2599/2124 -f 2130/2599/2124 1535/2606/2130 2131/2600/2125 -f 1535/2606/2130 1534/2607/2131 2131/2600/2125 -f 2131/2600/2125 1534/2607/2131 2132/2601/2126 -f 1534/2607/2131 1533/2608/2132 2132/2601/2126 -f 2132/2601/2126 1533/2608/2132 2133/2602/2127 -f 1533/2608/2132 1532/2609/1513 2133/2602/2127 -f 2133/2602/2127 1532/2609/1513 1553/2603/1520 -f 1532/2609/1513 1513/2610/1512 1553/2603/1520 -f 1525/2611/2106 1545/1932/1523 1515/1931/1522 -f 1526/2612/2105 2134/2613/2133 1525/2611/2106 -f 1527/2614/2104 2135/2615/2134 1526/2612/2105 -f 1528/2616/2103 2137/2617/2135 1527/2614/2104 -f 1529/2618/2102 2140/2619/2136 1528/2616/2103 -f 1530/2620/2101 2144/2621/2137 1529/2618/2102 -f 1531/1917/1514 2149/2622/2138 1530/2620/2101 -f 1525/2611/2106 2134/2613/2133 1545/1932/1523 -f 2134/2613/2133 1544/2444/1987 1545/1932/1523 -f 1526/2612/2105 2135/2615/2134 2134/2613/2133 -f 2135/2615/2134 2136/2623/2139 2134/2613/2133 -f 2134/2613/2133 2136/2623/2139 1544/2444/1987 -f 2136/2623/2139 1543/2445/1988 1544/2444/1987 -f 1527/2614/2104 2137/2617/2135 2135/2615/2134 -f 2137/2617/2135 2138/2624/2140 2135/2615/2134 -f 2135/2615/2134 2138/2624/2140 2136/2623/2139 -f 2138/2624/2140 2139/2625/2141 2136/2623/2139 -f 2136/2623/2139 2139/2625/2141 1543/2445/1988 -f 2139/2625/2141 1542/2446/1989 1543/2445/1988 -f 1528/2616/2103 2140/2619/2136 2137/2617/2135 -f 2140/2619/2136 2141/2626/2142 2137/2617/2135 -f 2137/2617/2135 2141/2626/2142 2138/2624/2140 -f 2141/2626/2142 2142/2627/2143 2138/2624/2140 -f 2138/2624/2140 2142/2627/2143 2139/2625/2141 -f 2142/2627/2143 2143/2628/2144 2139/2625/2141 -f 2139/2625/2141 2143/2628/2144 1542/2446/1989 -f 2143/2628/2144 1541/2447/1990 1542/2446/1989 -f 1529/2618/2102 2144/2621/2137 2140/2619/2136 -f 2144/2621/2137 2145/2629/2145 2140/2619/2136 -f 2140/2619/2136 2145/2629/2145 2141/2626/2142 -f 2145/2629/2145 2146/2630/2146 2141/2626/2142 -f 2141/2626/2142 2146/2630/2146 2142/2627/2143 -f 2146/2630/2146 2147/2631/2147 2142/2627/2143 -f 2142/2627/2143 2147/2631/2147 2143/2628/2144 -f 2147/2631/2147 2148/2632/2148 2143/2628/2144 -f 2143/2628/2144 2148/2632/2148 1541/2447/1990 -f 2148/2632/2148 1540/2448/1991 1541/2447/1990 -f 1530/2620/2101 2149/2622/2138 2144/2621/2137 -f 2149/2622/2138 2150/2633/2149 2144/2621/2137 -f 2144/2621/2137 2150/2633/2149 2145/2629/2145 -f 2150/2633/2149 2151/2634/2150 2145/2629/2145 -f 2145/2629/2145 2151/2634/2150 2146/2630/2146 -f 2151/2634/2150 2152/2635/2151 2146/2630/2146 -f 2146/2630/2146 2152/2635/2151 2147/2631/2147 -f 2152/2635/2151 2153/2636/2152 2147/2631/2147 -f 2147/2631/2147 2153/2636/2152 2148/2632/2148 -f 2153/2636/2152 2154/2637/2153 2148/2632/2148 -f 2148/2632/2148 2154/2637/2153 1540/2448/1991 -f 2154/2637/2153 1539/2449/1992 1540/2448/1991 -f 1531/1917/1514 1532/1916/1513 2149/2622/2138 -f 1532/1916/1513 1533/2638/2132 2149/2622/2138 -f 2149/2622/2138 1533/2638/2132 2150/2633/2149 -f 1533/2638/2132 1534/2639/2131 2150/2633/2149 -f 2150/2633/2149 1534/2639/2131 2151/2634/2150 -f 1534/2639/2131 1535/2640/2130 2151/2634/2150 -f 2151/2634/2150 1535/2640/2130 2152/2635/2151 -f 1535/2640/2130 1536/2641/2129 2152/2635/2151 -f 2152/2635/2151 1536/2641/2129 2153/2636/2152 -f 1536/2641/2129 1537/2642/2128 2153/2636/2152 -f 2153/2636/2152 1537/2642/2128 2154/2637/2153 -f 1537/2642/2128 1538/2643/1516 2154/2637/2153 -f 2154/2637/2153 1538/2643/1516 1539/2449/1992 -f 1538/2643/1516 1514/1918/1515 1539/2449/1992 -o Surface_Icosphere.004 -v 0.775685 -0.777626 0.060090 -v 0.862452 0.214506 0.648200 -v 0.059264 -0.563929 0.942588 -v -0.206050 0.561675 0.923073 -v -0.953170 -0.215899 0.504831 -v -0.775685 0.777626 -0.060090 -v 0.158724 -0.629955 0.887673 -v 0.267730 -0.693390 0.810883 -v 0.380976 -0.748265 0.710603 -v 0.838883 -0.697116 0.142496 -v 0.895585 -0.594822 0.232626 -v 0.938978 -0.471194 0.326031 -v 0.962870 -0.330996 0.416320 -v 0.964180 -0.183034 0.496845 -v 0.944313 -0.037659 0.562899 -v 0.908291 0.096512 0.612938 -v 0.812778 0.125003 0.730594 -v 0.743149 0.022348 0.810698 -v 0.652059 -0.089930 0.881324 -v 0.541771 -0.205385 0.935041 -v 0.418776 -0.316023 0.966828 -v 0.292092 -0.414807 0.976021 -v 0.170290 -0.497684 0.966081 -v 1.067191 0.252984 0.084270 -v 1.041503 0.249816 0.250728 -v 0.994147 0.241316 0.404274 -v 0.931952 0.228969 0.537623 -v 0.009122 -0.665421 0.875861 -v -0.047351 -0.768086 0.786004 -v 0.710558 0.739521 0.397763 -v 0.777166 0.610440 0.483091 -v 0.823781 0.473741 0.554035 -v 0.851062 0.339325 0.608730 -v -0.184394 0.448996 0.987118 -v -0.156931 0.314795 1.042246 -v -0.123797 0.162155 1.080917 -v -0.086279 -0.001325 1.096610 -v -0.046737 -0.164773 1.086584 -v -0.007934 -0.317326 1.053205 -v 0.027837 -0.451400 1.002728 -v 0.777408 0.275069 0.727994 -v 0.668653 0.338409 0.805222 -v 0.536473 0.400462 0.872827 -v 0.385825 0.456230 0.923576 -v 0.226129 0.501297 0.952663 -v 0.068598 0.533374 0.959587 -v -0.077312 0.552778 0.947871 -v -0.935724 -0.340625 0.467328 -v -0.900199 -0.474914 0.417250 -v -0.843037 -0.611451 0.354142 -v -0.763881 -0.740340 0.279970 -v -0.071769 -0.555067 0.946969 -v -0.217720 -0.535664 0.935768 -v -0.372236 -0.503540 0.904372 -v -0.525427 -0.458373 0.850776 -v -0.666295 -0.402456 0.777226 -v -0.786288 -0.340215 0.689931 -v -0.881639 -0.276669 0.596797 -v -0.076298 0.766187 0.785580 -v -0.146109 0.663318 0.865252 -v -0.319372 0.495395 0.928755 -v -0.441214 0.412518 0.919326 -v -0.564883 0.313780 0.890197 -v -0.681372 0.203242 0.839300 -v -0.781881 0.087937 0.768720 -v -0.860783 -0.024154 0.684448 -v -0.917009 -0.126603 0.594194 -v -1.004519 -0.230083 0.384711 -v -1.045093 -0.242098 0.243247 -v -1.067851 -0.250220 0.084165 -v -0.487559 0.746628 0.644075 -v -0.391378 0.691492 0.760698 -v -0.295710 0.627852 0.853438 -v -0.850977 0.696930 0.011225 -v -0.921057 0.594431 0.091133 -v -0.978501 0.470588 0.176306 -v -1.016193 0.330177 0.261410 -v -1.030051 0.182023 0.340385 -v -1.020731 0.036485 0.408383 -v -0.992953 -0.097811 0.463118 -v -0.918382 0.601213 -0.071530 -v -0.988653 0.482166 0.009074 -v -1.040453 0.347599 -0.081440 -v -1.040824 0.342888 0.095460 -v -1.082253 0.196704 0.005923 -v -1.067893 0.191367 0.181615 -v -1.095840 0.041348 -0.086168 -v -1.095299 0.040583 0.093129 -v -1.067847 0.038859 0.261140 -v -1.094357 -0.111252 0.002232 -v -1.080751 -0.108704 0.173669 -v -1.044354 -0.103945 0.329425 -v -0.825207 0.711392 0.151512 -v -0.891026 0.597608 0.242771 -v -0.779906 0.713258 0.304977 -v -0.940719 0.461262 0.335091 -v -0.837616 0.587450 0.404106 -v -0.712835 0.698434 0.462661 -v -0.967787 0.309741 0.421247 -v -0.876686 0.439785 0.498007 -v -0.759696 0.561544 0.563498 -v -0.625962 0.665500 0.612602 -v -0.970220 0.154302 0.494838 -v -0.892463 0.280435 0.578677 -v -0.787488 0.405970 0.651959 -v -0.661591 0.520718 0.707920 -v -0.525738 0.616901 0.743661 -v -0.951178 0.006234 0.552468 -v -0.885497 0.122182 0.641067 -v -0.794039 0.243871 0.721130 -v -0.680097 0.362458 0.784916 -v -0.552135 0.469132 0.827684 -v -0.420907 0.558120 0.849317 -v -0.236429 0.736912 0.781704 -v -0.961408 -0.529034 -0.076280 -v -1.028893 -0.389012 0.007047 -v -0.775158 -0.778007 -0.061927 -v -0.880604 -0.658867 0.020755 -v -0.962653 -0.522221 0.102886 -v -1.016907 -0.379586 0.178366 -v -0.778963 -0.768151 0.114722 -v -0.871497 -0.641913 0.196062 -v -0.940159 -0.502905 0.270533 -v -0.983421 -0.362477 0.333907 -v -0.832658 -0.184203 0.694802 -v -0.721856 -0.244767 0.793104 -v -0.761315 -0.077352 0.790200 -v -0.585780 -0.304060 0.880005 -v -0.634452 -0.134131 0.888527 -v -0.666704 0.039270 0.874050 -v -0.431374 -0.357138 0.946768 -v -0.484459 -0.190435 0.969038 -v -0.526003 -0.014177 0.965981 -v -0.552761 0.157858 0.937836 -v -0.270109 -0.400057 0.988431 -v -0.321592 -0.241678 1.023802 -v -0.367467 -0.068675 1.034530 -v -0.403579 0.106154 1.017770 -v -0.427952 0.269599 0.976818 -v -0.114077 -0.431219 1.005503 -v -0.158988 -0.284406 1.050636 -v -0.203488 -0.119963 1.074338 -v -0.243951 0.051702 1.071361 -v -0.277370 0.217836 1.041928 -v -0.302385 0.367685 0.991651 -v -0.003893 0.656979 0.882249 -v 0.154843 0.636729 0.883515 -v 0.078093 0.760048 0.791346 -v 0.321732 0.599951 0.864030 -v 0.246219 0.735925 0.779610 -v 0.484689 0.547004 0.822109 -v 0.416850 0.691470 0.747065 -v 0.631607 0.481822 0.760868 -v 0.576405 0.628643 0.694670 -v 0.503361 0.766939 0.606986 -v 0.754484 0.410573 0.687157 -v 0.713777 0.553505 0.627817 -v 0.653202 0.694010 0.549251 -v -0.852585 -0.412080 0.559723 -v -0.803071 -0.554878 0.507136 -v -0.742734 -0.483530 0.651572 -v -0.730928 -0.695206 0.438672 -v -0.677830 -0.630201 0.594469 -v -0.607188 -0.548885 0.734879 -v -0.591945 -0.768301 0.518956 -v -0.528420 -0.693183 0.671021 -v -0.452782 -0.601963 0.801642 -v -0.364969 -0.737748 0.729744 -v -0.290987 -0.638819 0.846898 -v -0.200743 -0.761930 0.767571 -v -0.134003 -0.659095 0.870423 -v -0.044656 0.428782 1.012004 -v -0.007328 0.281852 1.063252 -v 0.112133 0.397631 1.019468 -v 0.032949 0.117344 1.093227 -v 0.157481 0.239158 1.062075 -v 0.277937 0.354782 1.003435 -v 0.073412 -0.054321 1.096203 -v 0.201147 0.066120 1.079430 -v 0.326917 0.188015 1.033332 -v 0.440894 0.301835 0.961514 -v 0.111055 -0.220390 1.071959 -v 0.239468 -0.108674 1.068104 -v 0.368462 0.011757 1.036387 -v 0.487667 0.131876 0.977133 -v 0.588897 0.242725 0.896820 -v 0.143653 -0.370122 1.025853 -v 0.269976 -0.272025 1.031075 -v 0.399325 -0.160214 1.012359 -v 0.521818 -0.041496 0.967463 -v 0.628356 0.075311 0.899720 -v 0.713721 0.182377 0.816910 -v 0.751291 0.767727 0.236973 -v 0.829991 0.641277 0.331480 -v 0.866398 0.658650 0.159796 -v 0.886188 0.502078 0.415439 -v 0.934619 0.521791 0.253420 -v 0.961407 0.529034 0.076280 -v 0.919033 0.361489 0.484462 -v 0.976429 0.378965 0.336113 -v 1.015057 0.388800 0.168800 -v 0.111410 -0.738832 0.807289 -v 1.080522 0.111040 0.173615 -v 1.095840 -0.041347 0.086169 -v 1.040273 0.108083 0.340808 -v 1.067266 -0.041012 0.263176 -v 1.068048 -0.196921 0.174629 -v 1.040453 -0.347598 0.081441 -v 0.979966 0.102958 0.488944 -v 1.013876 -0.039686 0.424831 -v 1.026387 -0.192002 0.345926 -v 1.013154 -0.343311 0.256234 -v 0.975160 -0.482371 0.162418 -v 0.918383 -0.601213 0.071531 -v 0.283012 -0.560237 0.903349 -v 0.403087 -0.618784 0.815247 -v 0.415992 -0.471222 0.902719 -v 0.522583 -0.667086 0.701358 -v 0.542840 -0.522540 0.801422 -v 0.549047 -0.364469 0.880744 -v 0.631843 -0.699677 0.566769 -v 0.662331 -0.563037 0.674023 -v 0.675918 -0.407681 0.766115 -v 0.671540 -0.245751 0.835847 -v 0.722750 -0.714134 0.421479 -v 0.764225 -0.588575 0.528715 -v 0.788100 -0.441143 0.627925 -v 0.791037 -0.281990 0.710452 -v 0.774370 -0.123887 0.771364 -v 0.791493 -0.711909 0.276991 -v 0.842210 -0.598356 0.377695 -v 0.876827 -0.462241 0.476977 -v 0.890060 -0.310932 0.566669 -v 0.880925 -0.155670 0.640108 -v 0.853077 -0.007739 0.694406 -v -1.095840 0.041348 -0.086168 -v -1.067191 -0.252984 -0.084270 -v 1.067191 0.252984 0.084270 -v 1.095840 -0.041346 0.086168 -v 1.095840 -0.041347 0.086168 -v 1.095840 -0.041348 0.086168 -v -0.961407 -0.529034 -0.076280 -v -0.961408 -0.529034 -0.076280 -v -0.961408 -0.529034 -0.076280 -v -1.040453 0.347599 -0.081441 -v -1.040453 0.347599 -0.081441 -v 1.040454 -0.347596 0.081441 -v 0.961407 0.529034 0.076280 -v 1.040453 -0.347597 0.081441 -v 0.961407 0.529034 0.076280 -v 1.040453 -0.347599 0.081440 -v -1.040453 0.347599 -0.081441 -v 0.961407 0.529034 0.076280 -v -0.775157 -0.778008 -0.061927 -v -0.775158 -0.778007 -0.061927 -v -0.775158 -0.778007 -0.061927 -v 0.918383 -0.601212 0.071530 -v 0.918383 -0.601213 0.071530 -v 0.918382 -0.601213 0.071530 -v -0.775685 0.777626 -0.060089 -v -0.773502 0.777564 0.054597 -v -0.771529 0.776242 0.090061 -v -0.775685 0.777626 -0.060090 -v 0.742167 0.777708 0.227592 -v 0.364608 0.775409 0.684328 -v 0.775157 0.778007 0.061930 -v 0.472113 0.777092 0.615025 -v -0.751351 0.777106 0.188499 -v -0.068059 0.776414 0.774656 -v 0.065591 0.776733 0.773653 -v -0.736856 0.775674 0.244505 -v -0.696148 0.776681 0.340316 -v 0.496019 0.776958 0.598166 -v 0.218386 0.775668 0.744057 -v 0.092623 0.775684 0.771746 -v -0.671078 0.775847 0.389976 -v -0.593809 0.776262 0.501742 -v -0.211180 0.774990 0.747610 -v -0.088813 0.777070 0.771617 -v 0.683157 0.776289 0.366615 -v -0.554385 0.776940 0.541899 -v -0.581175 0.776954 0.516131 -v 0.775158 0.778008 0.061927 -v 0.735782 0.778174 0.245244 -v 0.288578 0.776186 0.718503 -v 0.516162 0.776570 0.579591 -v 0.602425 0.775597 0.488274 -v -0.407240 0.776458 0.658959 -v -0.471859 0.775762 0.616739 -v 0.775158 0.778007 0.061927 -v -0.293184 0.776706 0.717344 -v -0.347361 0.775703 0.695059 -v 0.647354 0.777121 0.424233 -v 0.755593 -0.777839 0.173023 -v 0.601981 -0.776908 0.488261 -v 0.609485 -0.776111 0.479332 -v 0.634508 -0.777628 0.443132 -v 0.177538 -0.778481 0.752501 -v 0.091811 -0.776823 0.769575 -v -0.331911 -0.777411 0.698885 -v -0.351631 -0.776084 0.689755 -v -0.397243 -0.777855 0.662668 -v 0.091481 -0.776796 0.769620 -v -0.212027 -0.777517 0.745901 -v -0.775157 -0.778008 -0.061926 -v -0.032804 -0.778937 0.774144 -v 0.689699 -0.776398 0.354869 -v 0.691626 -0.776106 0.351330 -v 0.712775 -0.777698 0.301816 -v -0.775157 -0.778008 -0.061927 -v -0.053778 -0.778285 0.773902 -v -0.705587 -0.778016 0.315908 -v -0.731938 -0.777038 0.253402 -v -0.768481 -0.778112 0.106859 -v -0.770669 -0.776684 0.090347 -v 0.748099 -0.776602 0.207746 -v -0.185624 -0.778576 0.752009 -v -0.764934 -0.778622 0.125293 -v 0.369749 -0.777330 0.681079 -v 0.379362 -0.776493 0.675976 -v 0.462965 -0.778343 0.620063 -v -0.737142 -0.775891 0.238701 -v -0.060661 -0.777678 0.773432 -v -0.671225 -0.776653 0.386192 -v -0.676166 -0.775967 0.377923 -v 0.299322 -0.778115 0.712672 -v 0.234534 -0.777435 0.738966 -v 0.240127 -0.776948 0.737247 -v -0.600302 -0.777862 0.489877 -v -0.583313 -0.778299 0.511369 -v -0.590363 -0.777537 0.503549 -v 0.493455 -0.778300 0.598802 -v 0.747541 -0.776419 0.209818 -v -0.562340 -0.778478 0.531757 -v 0.508184 -0.777576 0.586567 -v -0.466994 -0.776982 0.617026 -v -0.479467 -0.775794 0.607648 -v 0.504804 -0.777380 0.589794 -v 0.953170 0.215899 -0.504831 -v 0.850977 -0.696930 -0.011225 -v 0.921057 -0.594431 -0.091133 -v 0.978501 -0.470588 -0.176306 -v 1.016193 -0.330177 -0.261410 -v 1.030051 -0.182023 -0.340385 -v 1.020731 -0.036485 -0.408383 -v 0.992953 0.097811 -0.463118 -v 0.917009 0.126603 -0.594194 -v 0.860783 0.024154 -0.684448 -v 1.067191 0.252984 0.084270 -v 1.067851 0.250220 -0.084165 -v 1.045093 0.242098 -0.243247 -v 1.004519 0.230083 -0.384711 -v 0.843037 0.611451 -0.354142 -v 0.900199 0.474914 -0.417250 -v 0.935724 0.340625 -0.467328 -v 0.881639 0.276669 -0.596797 -v 0.852585 0.412079 -0.559723 -v 0.871496 0.641914 -0.196061 -v 0.880604 0.658868 -0.020755 -v 0.940158 0.502907 -0.270532 -v 0.962652 0.522221 -0.102886 -v 0.961407 0.529034 0.076280 -v 0.983421 0.362478 -0.333907 -v 1.016907 0.379586 -0.178365 -v 1.028893 0.389012 -0.007047 -v 1.094357 0.111252 -0.002232 -v 1.095840 -0.041347 0.086168 -v 1.080751 0.108705 -0.173669 -v 1.095299 -0.040582 -0.093129 -v 1.082254 -0.196703 -0.005924 -v 1.040454 -0.347598 0.081440 -v 1.044354 0.103946 -0.329425 -v 1.067846 -0.038857 -0.261140 -v 1.067893 -0.191365 -0.181616 -v 1.040825 -0.342886 -0.095461 -v 0.988653 -0.482164 -0.009075 -v 0.918383 -0.601213 0.071529 -v 0.837617 -0.587449 -0.404107 -v 0.876686 -0.439783 -0.498008 -v 0.892463 -0.280433 -0.578678 -v 0.885497 -0.122181 -0.641067 -v 0.825207 -0.711391 -0.151512 -v 0.891026 -0.597607 -0.242773 -v 0.940719 -0.461260 -0.335093 -v 0.967787 -0.309739 -0.421248 -v 0.970220 -0.154300 -0.494839 -v 0.951178 -0.006233 -0.552469 -v 0.784063 -0.766572 -0.048684 -v 0.836055 0.037379 -0.710739 -v 0.835014 -0.012455 -0.711971 -v 0.834957 0.171123 -0.693956 -v 0.806571 -0.655127 -0.350780 -v 0.806841 -0.643871 -0.369323 -v 0.813249 -0.609122 -0.415542 -v 0.835552 0.182227 -0.691351 -v 0.785676 -0.760654 -0.090949 -v 0.815270 -0.580019 -0.449819 -v 0.835451 0.189473 -0.689215 -v 0.827720 0.385126 -0.608558 -v 0.829970 0.311104 -0.647265 -v 0.804556 0.674109 -0.318084 -v 0.815227 -0.546786 -0.485849 -v 0.821170 -0.497563 -0.529086 -v 0.798808 0.708396 -0.252737 -v 0.796782 -0.716056 -0.235921 -v 0.793725 -0.739403 -0.167279 -v 0.788145 -0.756168 -0.109083 -v 0.823209 -0.429553 -0.582059 -v 0.824533 -0.420012 -0.588023 -v 0.824535 0.430323 -0.583176 -v 0.790144 0.752897 -0.124550 -v 0.828718 -0.356663 -0.623178 -v 0.797990 -0.712512 -0.243714 -v 0.803561 -0.688638 -0.291736 -v 0.821425 0.501947 -0.526628 -v 0.788306 0.758105 -0.106082 -v 0.831785 -0.257892 -0.666499 -v 0.775158 0.778007 0.061927 -v 0.775158 0.778007 0.061927 -v 0.833461 -0.191417 -0.686620 -v 0.818339 0.542308 -0.493008 -v 0.834881 -0.105453 -0.703424 -v 0.815582 0.572585 -0.459247 -v 0.808790 0.637036 -0.379965 -v -0.862452 -0.214506 -0.648200 -v -0.838883 0.697116 -0.142496 -v -0.895585 0.594822 -0.232626 -v -0.938978 0.471194 -0.326031 -v -0.962870 0.330996 -0.416320 -v -0.964180 0.183034 -0.496845 -v -0.944313 0.037659 -0.562899 -v -0.908291 -0.096512 -0.612938 -v -0.812778 -0.125003 -0.730594 -v -0.743149 -0.022348 -0.810698 -v -1.067191 -0.252984 -0.084270 -v -1.041503 -0.249816 -0.250728 -v -0.994147 -0.241316 -0.404274 -v -0.931952 -0.228969 -0.537623 -v -0.777166 -0.610440 -0.483091 -v -0.823781 -0.473741 -0.554035 -v -0.851062 -0.339325 -0.608730 -v -0.777408 -0.275069 -0.727994 -v -0.754484 -0.410573 -0.687157 -v -0.829991 -0.641277 -0.331480 -v -0.866398 -0.658650 -0.159796 -v -0.886188 -0.502078 -0.415439 -v -0.934619 -0.521791 -0.253420 -v -0.961407 -0.529034 -0.076280 -v -0.919033 -0.361489 -0.484462 -v -0.976429 -0.378965 -0.336113 -v -1.015057 -0.388800 -0.168800 -v -1.080522 -0.111040 -0.173615 -v -1.095840 0.041347 -0.086169 -v -1.040273 -0.108083 -0.340808 -v -1.067266 0.041012 -0.263176 -v -1.068048 0.196921 -0.174629 -v -1.040453 0.347598 -0.081441 -v -0.979966 -0.102958 -0.488944 -v -1.013876 0.039686 -0.424831 -v -1.026387 0.192002 -0.345926 -v -1.013154 0.343311 -0.256234 -v -0.975160 0.482371 -0.162418 -v -0.918383 0.601213 -0.071531 -v -0.764225 0.588575 -0.528715 -v -0.788100 0.441143 -0.627925 -v -0.791037 0.281990 -0.710452 -v -0.774370 0.123887 -0.771364 -v -0.791493 0.711909 -0.276991 -v -0.842210 0.598356 -0.377695 -v -0.876827 0.462241 -0.476977 -v -0.890060 0.310932 -0.566669 -v -0.880925 0.155670 -0.640108 -v -0.853077 0.007739 -0.694406 -v -1.095840 0.041346 -0.086168 -v -1.095840 0.041347 -0.086168 -v -1.095840 0.041348 -0.086168 -v -1.040454 0.347596 -0.081441 -v -0.961407 -0.529034 -0.076280 -v -1.040453 0.347597 -0.081441 -v -0.961407 -0.529034 -0.076280 -v -1.040453 0.347599 -0.081440 -v -0.961407 -0.529034 -0.076280 -v -0.918383 0.601212 -0.071530 -v -0.918383 0.601213 -0.071530 -v -0.918382 0.601213 -0.071530 -v -0.775684 0.777626 -0.060090 -v -0.766947 0.766835 -0.168862 -v -0.714610 -0.035515 -0.832830 -v -0.713399 0.014322 -0.833764 -v -0.775685 0.777626 -0.060090 -v -0.716125 -0.169299 -0.816402 -v -0.741914 0.656120 -0.471027 -v -0.738373 0.610272 -0.536147 -v -0.717119 -0.180408 -0.813949 -v -0.735002 0.581251 -0.570388 -v -0.717351 -0.187660 -0.811842 -v -0.722293 -0.383507 -0.731438 -v -0.718475 -0.309392 -0.769842 -v -0.744785 -0.673191 -0.441610 -v -0.728418 0.498987 -0.649800 -v -0.749320 -0.707636 -0.376251 -v -0.757939 0.739952 -0.287573 -v -0.761531 0.756577 -0.229180 -v -0.722508 0.421578 -0.708723 -v -0.723108 -0.428766 -0.705979 -v -0.749326 -0.716047 -0.357403 -v -0.760801 -0.752447 -0.248393 -v -0.721132 0.358315 -0.744253 -v -0.750193 0.713246 -0.363799 -v -0.748180 0.689488 -0.412158 -v -0.728866 -0.500526 -0.649812 -v -0.761874 -0.757699 -0.229878 -v -0.717368 0.259649 -0.787757 -v -0.775158 -0.778007 -0.061927 -v -0.761752 -0.761371 -0.212765 -v -0.775158 -0.778007 -0.061928 -v -0.715864 0.193223 -0.808053 -v -0.731068 -0.540968 -0.616220 -v -0.775158 -0.778007 -0.061927 -v -0.714622 0.107299 -0.825079 -v -0.733619 -0.571327 -0.582516 -v -0.739297 -0.635969 -0.503302 -v -0.739181 -0.644546 -0.491160 -v -1.095840 0.041347 -0.086168 -v -0.918382 0.601213 -0.071530 -v -1.040453 0.347598 -0.081441 -v -0.775158 -0.778007 -0.061927 -v 0.471716 -0.512109 0.044641 -v -0.468017 0.429976 -0.028156 -v -0.466695 0.429938 0.041314 -v -0.465500 0.429138 0.062796 -v 0.451413 0.430025 0.146105 -v 0.222708 0.428633 0.422770 -v 0.471396 0.430207 0.045756 -v 0.287829 0.429653 0.380790 -v -0.453277 0.429661 0.122425 -v -0.039377 0.429242 0.477486 -v 0.041581 0.429435 0.476878 -v -0.444497 0.428794 0.156350 -v -0.419838 0.429404 0.214387 -v 0.302310 0.429571 0.370577 -v 0.134135 0.428790 0.458950 -v 0.057955 0.428799 0.475723 -v -0.404652 0.428898 0.244468 -v -0.357847 0.429150 0.312169 -v -0.126072 0.428379 0.461102 -v -0.051949 0.429639 0.475645 -v 0.415667 0.429166 0.230317 -v -0.333966 0.429560 0.336494 -v -0.350194 0.429569 0.320886 -v 0.447545 0.430308 0.156797 -v 0.176654 0.429104 0.443471 -v 0.314511 0.429336 0.359326 -v 0.366765 0.428747 0.304011 -v -0.244834 0.429268 0.407403 -v -0.283977 0.428847 0.381828 -v -0.175745 0.429418 0.442769 -v -0.208563 0.428811 0.429270 -v 0.393980 0.429670 0.265219 -v 0.459545 -0.512238 0.113050 -v 0.366495 -0.511674 0.304004 -v 0.371041 -0.511191 0.298594 -v 0.386199 -0.512110 0.276667 -v 0.109392 -0.512627 0.464065 -v 0.057463 -0.511622 0.474407 -v -0.199204 -0.511978 0.431588 -v -0.211149 -0.511174 0.426057 -v -0.238779 -0.512247 0.409649 -v 0.057263 -0.511606 0.474435 -v -0.126585 -0.512043 0.460067 -v -0.467698 -0.512340 -0.029269 -v -0.018022 -0.512903 0.477176 -v 0.419630 -0.511365 0.223202 -v 0.420797 -0.511188 0.221058 -v 0.433608 -0.512152 0.191065 -v -0.030726 -0.512508 0.477029 -v -0.425556 -0.512345 0.199601 -v -0.441518 -0.511753 0.161739 -v -0.463654 -0.512403 0.072971 -v -0.464979 -0.511538 0.062969 -v 0.455006 -0.511488 0.134083 -v -0.110592 -0.512684 0.463767 -v -0.461505 -0.512712 0.084138 -v 0.225823 -0.511929 0.420802 -v 0.231646 -0.511422 0.417711 -v 0.282287 -0.512543 0.383842 -v -0.444670 -0.511058 0.152834 -v -0.034896 -0.512140 0.476744 -v -0.404741 -0.511519 0.242175 -v -0.407734 -0.511104 0.237167 -v 0.183162 -0.512405 0.439939 -v 0.143917 -0.511993 0.455866 -v 0.147305 -0.511698 0.454825 -v -0.361780 -0.512251 0.304982 -v -0.351489 -0.512516 0.318001 -v -0.355760 -0.512055 0.313264 -v 0.300757 -0.512517 0.370963 -v 0.454668 -0.511377 0.135338 -v -0.338785 -0.512625 0.330351 -v 0.309679 -0.512079 0.363552 -v -0.281029 -0.511718 0.382002 -v -0.288585 -0.510999 0.376321 -v 0.307631 -0.511960 0.365506 -v 0.476791 -0.505413 -0.021248 -v 0.508284 -0.018424 -0.422284 -v 0.507654 -0.048611 -0.423030 -v 0.507619 0.062591 -0.412117 -v 0.490425 -0.437906 -0.204241 -v 0.490588 -0.431087 -0.215473 -v 0.494470 -0.410038 -0.243470 -v 0.507980 0.069316 -0.410539 -v 0.477768 -0.501828 -0.046850 -v 0.495694 -0.392409 -0.264233 -v 0.507918 0.073706 -0.409246 -v 0.503236 0.192221 -0.360388 -v 0.504598 0.147383 -0.383835 -v 0.489204 0.367271 -0.184435 -v 0.495668 -0.372279 -0.286058 -v 0.499268 -0.342462 -0.312249 -v 0.485722 0.388040 -0.144852 -v 0.484495 -0.474813 -0.134665 -v 0.482644 -0.488955 -0.093086 -v 0.479263 -0.499111 -0.057834 -v 0.500503 -0.301265 -0.344337 -v 0.501305 -0.295486 -0.347949 -v 0.501307 0.219599 -0.345013 -v 0.480474 0.414997 -0.067203 -v 0.503840 -0.257113 -0.369244 -v 0.485227 -0.472666 -0.139386 -v 0.488601 -0.458205 -0.168475 -v 0.499422 0.262985 -0.310759 -v 0.479361 0.418151 -0.056016 -v 0.505698 -0.197283 -0.395486 -v 0.506713 -0.157016 -0.407674 -v 0.497553 0.287433 -0.290394 -v 0.471396 0.430207 0.045754 -v 0.507573 -0.104944 -0.417853 -v 0.495883 0.305773 -0.269944 -v 0.491769 0.344814 -0.221919 -v -0.468017 0.429976 -0.028157 -v -0.462724 0.423439 -0.094045 -v -0.431022 -0.062579 -0.496239 -v -0.430288 -0.032391 -0.496806 -v -0.431939 -0.143618 -0.486289 -v -0.447560 0.356374 -0.277080 -v -0.445416 0.328602 -0.316525 -v -0.432541 -0.150348 -0.484803 -v -0.443374 0.311023 -0.337267 -v -0.432682 -0.154740 -0.483526 -v -0.435675 -0.273374 -0.434822 -v -0.433363 -0.228479 -0.458085 -v -0.449300 -0.448848 -0.259260 -v -0.439386 0.261192 -0.385370 -v -0.452047 -0.469713 -0.219670 -v -0.457268 0.407155 -0.165953 -v -0.459444 0.417226 -0.130582 -v -0.435806 0.214302 -0.421063 -v -0.436169 -0.300789 -0.419400 -v -0.452050 -0.474808 -0.208253 -v -0.459002 -0.496857 -0.142221 -v -0.434972 0.175981 -0.442584 -v -0.452575 0.390978 -0.212127 -v -0.451356 0.376587 -0.241420 -v -0.439657 -0.344257 -0.385377 -v -0.459651 -0.500038 -0.131005 -v -0.432692 0.116214 -0.468937 -v -0.459577 -0.502262 -0.120639 -v -0.467698 -0.512340 -0.029270 -v -0.431781 0.075977 -0.481231 -v -0.440991 -0.368755 -0.365029 -v -0.431029 0.023929 -0.491544 -v -0.442536 -0.387144 -0.344613 -v -0.445975 -0.426301 -0.296630 -v -0.445905 -0.431496 -0.289275 -vt 0.181819 0.000000 -vt 0.193183 0.019683 -vt 0.175505 0.019683 -vt 0.193183 0.019683 -vt 0.000000 0.000000 -vt 0.177915 0.019683 -vt 0.272728 0.157461 -vt 0.284092 0.137778 -vt 0.295455 0.157461 -vt 0.284092 0.177143 -vt 0.102274 0.177143 -vt 0.090910 0.157461 -vt 0.113637 0.157461 -vt 0.261364 0.177143 -vt 0.079546 0.177143 -vt 0.193183 0.334604 -vt 0.181819 0.314921 -vt 0.204546 0.314921 -vt 0.000000 0.314921 -vt 0.022727 0.314921 -vt 0.011364 0.334604 -vt 0.363637 0.039365 -vt 0.352273 0.059048 -vt 0.352273 0.059048 -vt 0.363637 0.039365 -vt 0.818182 0.433017 -vt 0.840909 0.433017 -vt 0.829546 0.452699 -vt 0.829546 0.413334 -vt 0.852273 0.413334 -vt 0.818182 0.393652 -vt 0.818182 0.393652 -vt 0.818182 0.393652 -vt 0.840909 0.393652 -vt 0.863636 0.393652 -vt 0.818182 0.393652 -vt 0.829546 0.373969 -vt 0.852273 0.373969 -vt 0.875000 0.373969 -vt 0.818182 0.354286 -vt 0.840909 0.354286 -vt 0.863636 0.354286 -vt 0.886364 0.354286 -vt 0.829546 0.334604 -vt 0.852273 0.334604 -vt 0.875000 0.334604 -vt 0.897727 0.334604 -vt 0.818182 0.314921 -vt 0.840909 0.314921 -vt 0.863636 0.314921 -vt 0.886364 0.314921 -vt 0.909091 0.314921 -vt 0.090910 0.472382 -vt 0.079546 0.452699 -vt 0.097224 0.452699 -vt 0.090910 0.472382 -vt 0.068182 0.433017 -vt 0.090910 0.433017 -vt 0.056819 0.413334 -vt 0.079546 0.413334 -vt 0.045455 0.393652 -vt 0.068182 0.393652 -vt 0.034091 0.373969 -vt 0.056819 0.373969 -vt 0.022727 0.354286 -vt 0.045455 0.354286 -vt 0.034091 0.334604 -vt 0.099171 0.447326 -vt 0.105643 0.433017 -vt 0.102274 0.413334 -vt 0.109401 0.425680 -vt 0.117274 0.413334 -vt 0.090910 0.393652 -vt 0.113637 0.393652 -vt 0.121568 0.407389 -vt 0.133559 0.393652 -vt 0.079546 0.373969 -vt 0.102273 0.373969 -vt 0.125001 0.373969 -vt 0.135406 0.391991 -vt 0.147728 0.373969 -vt 0.139243 0.388667 -vt 0.068182 0.354286 -vt 0.090910 0.354286 -vt 0.113637 0.354286 -vt 0.136364 0.354286 -vt 0.159092 0.354286 -vt 0.056819 0.334604 -vt 0.079546 0.334604 -vt 0.102273 0.334604 -vt 0.125001 0.334604 -vt 0.147728 0.334604 -vt 0.170455 0.334604 -vt 0.045455 0.314921 -vt 0.068182 0.314921 -vt 0.090910 0.314921 -vt 0.113637 0.314921 -vt 0.136364 0.314921 -vt 0.159092 0.314921 -vt 0.303347 0.222521 -vt 0.312343 0.226077 -vt 0.306819 0.216508 -vt 0.224396 0.388667 -vt 0.215910 0.373969 -vt 0.221877 0.373969 -vt 0.210359 0.147393 -vt 0.215910 0.137778 -vt 0.202979 0.137778 -vt 0.000000 0.000000 -vt 0.237708 0.175535 -vt 0.227273 0.157461 -vt 0.204546 0.354286 -vt 0.215910 0.334604 -vt 0.221794 0.363777 -vt 0.221522 0.354286 -vt 0.222627 0.346238 -vt 0.224006 0.334604 -vt 0.227274 0.314921 -vt 0.229686 0.314921 -vt 0.228867 0.317681 -vt 0.818182 0.275556 -vt 0.818182 0.275556 -vt 0.818182 0.275556 -vt 0.818182 0.275556 -vt 0.829546 0.295238 -vt 0.818182 0.236191 -vt 0.818182 0.236191 -vt 0.818182 0.236191 -vt 0.818182 0.236191 -vt 0.818182 0.236191 -vt 0.840909 0.275556 -vt 0.829546 0.255874 -vt 0.852273 0.295238 -vt 0.818182 0.236191 -vt 0.818182 0.236191 -vt 0.840909 0.236191 -vt 0.837716 0.234772 -vt 0.839822 0.234307 -vt 0.852273 0.255874 -vt 0.863636 0.275556 -vt 0.875000 0.295238 -vt 0.842331 0.233729 -vt 0.863636 0.236191 -vt 0.857885 0.230442 -vt 0.859897 0.229714 -vt 0.875000 0.255874 -vt 0.886364 0.275556 -vt 0.897727 0.295238 -vt 0.011364 0.295238 -vt 0.022727 0.275556 -vt 0.034091 0.295238 -vt 0.034091 0.255874 -vt 0.045455 0.275556 -vt 0.045455 0.236191 -vt 0.056819 0.255874 -vt 0.056819 0.216509 -vt 0.068182 0.236191 -vt 0.068182 0.196826 -vt 0.079546 0.216509 -vt 0.090910 0.196826 -vt 0.056819 0.295238 -vt 0.068182 0.275556 -vt 0.079546 0.295238 -vt 0.079546 0.255874 -vt 0.090910 0.275556 -vt 0.102273 0.295238 -vt 0.090910 0.236191 -vt 0.102273 0.255874 -vt 0.113637 0.275556 -vt 0.125001 0.295238 -vt 0.102274 0.216509 -vt 0.113637 0.236191 -vt 0.125001 0.255874 -vt 0.136364 0.275556 -vt 0.147728 0.295238 -vt 0.113637 0.196826 -vt 0.125001 0.216508 -vt 0.136365 0.236191 -vt 0.147728 0.255874 -vt 0.159092 0.275556 -vt 0.170455 0.295238 -vt 0.193183 0.295238 -vt 0.204546 0.275556 -vt 0.215910 0.295238 -vt 0.215910 0.255874 -vt 0.227274 0.275556 -vt 0.227273 0.236191 -vt 0.238637 0.255874 -vt 0.238637 0.216509 -vt 0.250001 0.236191 -vt 0.250001 0.196826 -vt 0.261364 0.216509 -vt 0.272728 0.196826 -vt 0.238637 0.295238 -vt 0.250001 0.275556 -vt 0.242466 0.295238 -vt 0.253863 0.282246 -vt 0.240487 0.298443 -vt 0.261364 0.255874 -vt 0.268711 0.268599 -vt 0.260718 0.275556 -vt 0.219851 0.157461 -vt 0.272728 0.236191 -vt 0.284092 0.255874 -vt 0.285057 0.257546 -vt 0.281969 0.259549 -vt 0.284092 0.216509 -vt 0.295455 0.236191 -vt 0.288181 0.255874 -vt 0.302787 0.248890 -vt 0.239933 0.177143 -vt 0.255207 0.187809 -vt 0.261364 0.177143 -vt 0.295455 0.196826 -vt 0.306819 0.216508 -vt 0.318182 0.236191 -vt 0.321871 0.229803 -vt 0.339537 0.233813 -vt 0.329546 0.216508 -vt 0.321922 0.242668 -vt 0.312904 0.245335 -vt 0.920455 0.295238 -vt 0.909091 0.275556 -vt 0.897727 0.255874 -vt 0.886364 0.236191 -vt 0.868915 0.227048 -vt 0.931818 0.275556 -vt 0.920455 0.255874 -vt 0.943182 0.255874 -vt 0.909091 0.236191 -vt 0.931818 0.236191 -vt 0.954545 0.236191 -vt 0.877840 0.224072 -vt 0.879032 0.223492 -vt 0.897727 0.216508 -vt 0.893638 0.216508 -vt 0.920455 0.216508 -vt 0.943182 0.216508 -vt 0.965909 0.216509 -vt 0.895617 0.215579 -vt 0.896762 0.214836 -vt 0.899849 0.212833 -vt 0.911633 0.205080 -vt 0.913108 0.203783 -vt 0.921101 0.196826 -vt 0.931818 0.196826 -vt 0.954545 0.196826 -vt 0.977273 0.196826 -vt 0.926155 0.192276 -vt 0.927956 0.190136 -vt 0.939353 0.177143 -vt 0.943182 0.177143 -vt 0.965909 0.177143 -vt 0.988636 0.177143 -vt 0.941332 0.173939 -vt 0.341955 0.234381 -vt 0.000000 0.000000 -vt 0.352273 0.216508 -vt 0.951684 0.158370 -vt 0.952133 0.157461 -vt 0.954545 0.157461 -vt 0.977273 0.157461 -vt 1.000000 0.157461 -vt 0.181819 0.275556 -vt 0.170455 0.255874 -vt 0.159092 0.236191 -vt 0.147728 0.216508 -vt 0.136364 0.196826 -vt 0.125001 0.177143 -vt 0.193183 0.255874 -vt 0.181819 0.236191 -vt 0.204546 0.236191 -vt 0.170455 0.216508 -vt 0.193183 0.216508 -vt 0.215910 0.216508 -vt 0.159092 0.196826 -vt 0.181819 0.196826 -vt 0.204546 0.196826 -vt 0.227273 0.196826 -vt 0.147728 0.177143 -vt 0.170455 0.177143 -vt 0.193183 0.177143 -vt 0.215910 0.177143 -vt 0.238637 0.177143 -vt 0.136364 0.157461 -vt 0.159092 0.157461 -vt 0.181819 0.157461 -vt 0.204546 0.157461 -vt 0.227273 0.157461 -vt 0.250001 0.157461 -vt 0.190745 0.118096 -vt 0.197541 0.130230 -vt 0.204546 0.118096 -vt 0.329546 0.216508 -vt 0.318182 0.196826 -vt 0.306819 0.177143 -vt 0.340910 0.236191 -vt 0.341997 0.238074 -vt 0.339489 0.238653 -vt 0.363637 0.236191 -vt 0.352273 0.216508 -vt 0.363637 0.236191 -vt 0.269826 0.201852 -vt 0.279880 0.209213 -vt 0.272728 0.196826 -vt 0.340910 0.196826 -vt 0.363637 0.196826 -vt 0.363637 0.196826 -vt 0.363637 0.196826 -vt 0.329546 0.177143 -vt 0.352273 0.177143 -vt 0.363637 0.196826 -vt 0.318182 0.157461 -vt 0.340910 0.157461 -vt 0.363637 0.157461 -vt 0.952952 0.154701 -vt 0.959192 0.126144 -vt 0.965909 0.137778 -vt 0.957813 0.137778 -vt 0.957806 0.137825 -vt 0.965909 0.098413 -vt 0.959942 0.098413 -vt 0.959884 0.096955 -vt 0.957423 0.083715 -vt 0.960025 0.108605 -vt 0.977273 0.118096 -vt 0.960297 0.118096 -vt 0.960384 0.117308 -vt 0.988636 0.137778 -vt 0.352273 0.137778 -vt 0.329546 0.137778 -vt 0.306819 0.137778 -vt 0.363637 0.118096 -vt 0.363637 0.118096 -vt 0.363637 0.118096 -vt 0.363637 0.118096 -vt 0.340910 0.118096 -vt 0.352273 0.098413 -vt 0.363637 0.078731 -vt 0.363637 0.078731 -vt 0.363637 0.078731 -vt 0.363637 0.078730 -vt 0.318182 0.118096 -vt 0.329546 0.098413 -vt 0.340910 0.078731 -vt 0.363637 0.039365 -vt 0.363637 0.039365 -vt 0.363637 0.039365 -vt 0.295455 0.118096 -vt 0.306819 0.098413 -vt 0.318182 0.078731 -vt 0.329546 0.059048 -vt 0.340910 0.039365 -vt 0.352273 0.019683 -vt 0.102274 0.137778 -vt 0.113637 0.118096 -vt 0.125001 0.137778 -vt 0.125001 0.098413 -vt 0.136365 0.118096 -vt 0.147728 0.098413 -vt 0.133486 0.083715 -vt 0.137323 0.080391 -vt 0.159092 0.078731 -vt 0.139170 0.078731 -vt 0.151161 0.064993 -vt 0.163327 0.046702 -vt 0.170455 0.059048 -vt 0.155455 0.059048 -vt 0.173558 0.025056 -vt 0.181819 0.039365 -vt 0.167086 0.039365 -vt 0.173457 0.025383 -vt 0.147728 0.137778 -vt 0.159092 0.118096 -vt 0.170455 0.137778 -vt 0.138768 0.079182 -vt 0.170455 0.098413 -vt 0.181819 0.118096 -vt 0.193183 0.137778 -vt 0.152201 0.063859 -vt 0.181819 0.078731 -vt 0.193183 0.098413 -vt 0.204546 0.118096 -vt 0.215910 0.137778 -vt 0.163652 0.046232 -vt 0.193183 0.059048 -vt 0.204546 0.078731 -vt 0.215910 0.098413 -vt 0.227273 0.118096 -vt 0.238637 0.137778 -vt 0.204546 0.039365 -vt 0.215910 0.059048 -vt 0.227274 0.078731 -vt 0.238637 0.098413 -vt 0.250001 0.118096 -vt 0.261364 0.137778 -vt 0.272728 0.157461 -vt 0.295455 0.157461 -vt 0.284092 0.137778 -vt 0.352273 0.019683 -vt 0.284092 0.177143 -vt 0.264204 0.196826 -vt 0.239933 0.177143 -vt 0.255207 0.187809 -vt 0.261364 0.177143 -vt 0.295455 0.196826 -vt 0.285878 0.213415 -vt 0.197541 0.130230 -vt 0.204546 0.118096 -vt 0.190745 0.118096 -vt 0.291206 0.216509 -vt 0.000000 0.000000 -vt 0.186538 0.109921 -vt 0.193183 0.098413 -vt 0.239027 0.176468 -vt 0.250001 0.157461 -vt 0.318182 0.196826 -vt 0.306819 0.177143 -vt 0.269826 0.201852 -vt 0.279880 0.209213 -vt 0.272728 0.196826 -vt 0.219851 0.157461 -vt 0.000000 0.000000 -vt 0.227273 0.157461 -vt 0.340910 0.196826 -vt 0.363637 0.196826 -vt 0.329546 0.177143 -vt 0.352273 0.177143 -vt 0.318182 0.157461 -vt 0.340910 0.157461 -vt 0.363637 0.157461 -vt 0.352273 0.137778 -vt 0.329546 0.137778 -vt 0.306819 0.137778 -vt 0.363637 0.118096 -vt 0.340910 0.118096 -vt 0.352273 0.098413 -vt 0.363637 0.078731 -vt 0.318182 0.118096 -vt 0.329546 0.098413 -vt 0.340910 0.078731 -vt 0.295455 0.118096 -vt 0.306819 0.098413 -vt 0.318182 0.078731 -vt 0.329546 0.059048 -vt 0.340910 0.039365 -vt 0.176115 0.029485 -vt 0.175539 0.039365 -vt 0.181819 0.039365 -vt 0.175881 0.071054 -vt 0.177381 0.078731 -vt 0.181819 0.078731 -vt 0.175706 0.068142 -vt 0.178560 0.084375 -vt 0.182398 0.098413 -vt 0.193183 0.098413 -vt 0.179725 0.090497 -vt 0.185897 0.108653 -vt 0.186538 0.109921 -vt 0.237708 0.175535 -vt 0.202979 0.137778 -vt 0.210359 0.147393 -vt 0.215910 0.137778 -vt 0.174900 0.049927 -vt 0.174992 0.051191 -vt 0.193183 0.059048 -vt 0.175293 0.059048 -vt 0.204546 0.078731 -vt 0.215910 0.098413 -vt 0.227273 0.118096 -vt 0.238637 0.137778 -vt 0.176402 0.026433 -vt 0.204546 0.039365 -vt 0.215910 0.059048 -vt 0.227274 0.078731 -vt 0.238637 0.098413 -vt 0.250001 0.118096 -vt 0.261364 0.137778 -vt 0.177915 0.019683 -vt 0.193183 0.019683 -vt 0.181819 0.000000 -vt 0.181819 0.000000 -vt 0.272728 0.157461 -vt 0.295455 0.157461 -vt 0.284092 0.137778 -vt 0.284092 0.177143 -vt 0.264204 0.196826 -vt 0.295455 0.196826 -vt 0.285878 0.213415 -vt 0.291206 0.216509 -vt 0.306819 0.216508 -vt 0.303347 0.222521 -vt 0.305101 0.223459 -vt 0.312343 0.226077 -vt 0.181819 0.078731 -vt 0.178560 0.084375 -vt 0.175706 0.068142 -vt 0.177381 0.078731 -vt 0.239027 0.176468 -vt 0.250001 0.157461 -vt 0.321871 0.229803 -vt 0.329546 0.216508 -vt 0.318182 0.196826 -vt 0.306819 0.177143 -vt 0.324442 0.230718 -vt 0.339537 0.233813 -vt 0.341955 0.234381 -vt 0.352273 0.216508 -vt 0.344145 0.234844 -vt 0.363637 0.236191 -vt 0.340910 0.196826 -vt 0.363637 0.196826 -vt 0.363637 0.196826 -vt 0.363637 0.196826 -vt 0.329546 0.177143 -vt 0.352273 0.177143 -vt 0.363637 0.196826 -vt 0.318182 0.157461 -vt 0.340910 0.157461 -vt 0.363637 0.157461 -vt 0.352273 0.137778 -vt 0.329546 0.137778 -vt 0.306819 0.137778 -vt 0.363637 0.118096 -vt 0.363637 0.118096 -vt 0.363637 0.118096 -vt 0.363637 0.118096 -vt 0.363637 0.118096 -vt 0.340910 0.118096 -vt 0.352273 0.098413 -vt 0.363637 0.078731 -vt 0.363637 0.078731 -vt 0.363637 0.078731 -vt 0.363637 0.078730 -vt 0.363637 0.078730 -vt 0.318182 0.118096 -vt 0.329546 0.098413 -vt 0.340910 0.078731 -vt 0.352273 0.059048 -vt 0.363637 0.039365 -vt 0.363637 0.039365 -vt 0.363637 0.039365 -vt 0.363637 0.039365 -vt 0.363637 0.039365 -vt 0.295455 0.118096 -vt 0.306819 0.098413 -vt 0.318182 0.078731 -vt 0.329546 0.059048 -vt 0.340910 0.039365 -vt 0.352273 0.019683 -vt 0.176115 0.029485 -vt 0.175539 0.039365 -vt 0.181819 0.039365 -vt 0.174992 0.051191 -vt 0.193183 0.059048 -vt 0.175293 0.059048 -vt 0.204546 0.078731 -vt 0.215910 0.098413 -vt 0.227273 0.118096 -vt 0.238637 0.137778 -vt 0.204546 0.039365 -vt 0.215910 0.059048 -vt 0.227274 0.078731 -vt 0.238637 0.098413 -vt 0.250001 0.118096 -vt 0.261364 0.137778 -vt 0.000000 0.000000 -vt 0.859897 0.229714 -vt 0.868915 0.227048 -vt 0.174992 0.051191 -vt 0.175293 0.059048 -vt 0.341997 0.238074 -vt 0.339489 0.238653 -vt 0.175706 0.068142 -vt 0.175881 0.071054 -vt 0.960025 0.108605 -vt 0.959942 0.098413 -vt 0.303347 0.222521 -vt 0.305101 0.223459 -vt 0.000000 0.000000 -vt 0.237708 0.175535 -vt 0.926155 0.192276 -vt 0.927956 0.190136 -vt 0.837716 0.234772 -vt 0.839822 0.234307 -vt 0.302787 0.248890 -vt 0.288181 0.255874 -vt 0.877840 0.224072 -vt 0.202979 0.137778 -vt 0.210359 0.147393 -vt 0.960297 0.118096 -vt 0.960384 0.117308 -vt 0.939353 0.177143 -vt 0.255207 0.187809 -vt 0.264204 0.196826 -vt 0.239027 0.176468 -vt 0.321922 0.242668 -vt 0.312904 0.245335 -vt 0.842331 0.233729 -vt 0.175505 0.019683 -vt 0.181819 0.000000 -vt 0.291206 0.216509 -vt 0.303347 0.222521 -vt 0.285878 0.213415 -vt 0.291206 0.216509 -vt 0.339537 0.233813 -vt 0.341955 0.234381 -vt 0.151161 0.064993 -vt 0.152201 0.063859 -vt 0.186538 0.109921 -vt 0.190745 0.118096 -vt 0.105643 0.433017 -vt 0.099171 0.447326 -vt 0.268711 0.268599 -vt 0.260718 0.275556 -vt 0.269826 0.201852 -vt 0.135406 0.391991 -vt 0.133559 0.393652 -vt 0.959192 0.126144 -vt 0.239933 0.177143 -vt 0.255207 0.187809 -vt 0.181819 0.000000 -vt 0.177915 0.019683 -vt 0.177381 0.078731 -vt 0.952133 0.157461 -vt 0.952952 0.154701 -vt 0.229686 0.314921 -vt 0.228867 0.317681 -vt 0.363637 0.236191 -vt 0.137323 0.080391 -vt 0.138768 0.079182 -vt 0.000000 0.000000 -vt 0.186538 0.109921 -vt 0.178560 0.084375 -vt 0.179725 0.090497 -vt 0.312343 0.226077 -vt 0.321871 0.229803 -vt 0.219851 0.157461 -vt 0.175539 0.039365 -vt 0.174900 0.049927 -vt 0.264204 0.196826 -vt 0.339537 0.233813 -vt 0.341955 0.234381 -vt 0.242466 0.295238 -vt 0.240487 0.298443 -vt 0.285057 0.257546 -vt 0.139243 0.388667 -vt 0.178560 0.084375 -vt 0.279880 0.209213 -vt 0.173558 0.025056 -vt 0.221877 0.373969 -vt 0.224396 0.388667 -vt 0.951684 0.158370 -vt 0.269826 0.201852 -vt 0.175706 0.068142 -vt 0.222627 0.346238 -vt 0.221522 0.354286 -vt 0.174992 0.051191 -vt 0.913108 0.203783 -vt 0.921101 0.196826 -vt 0.175539 0.039365 -vt 0.155455 0.059048 -vt 0.163327 0.046702 -vt 0.121568 0.407389 -vt 0.117274 0.413334 -vt 0.899849 0.212833 -vt 0.911633 0.205080 -vt 0.139170 0.078731 -vt 0.177915 0.019683 -vt 0.176402 0.026433 -vt 0.857885 0.230442 -vt 0.957806 0.137825 -vt 0.239933 0.177143 -vt 0.818182 0.236191 -vt 0.197541 0.130230 -vt 0.237708 0.175535 -vt 0.239027 0.176468 -vt 0.210359 0.147393 -vt 0.219851 0.157461 -vt 0.163652 0.046232 -vt 0.167086 0.039365 -vt 0.097224 0.452699 -vt 0.176115 0.029485 -vt 0.344145 0.234844 -vt 0.363637 0.236191 -vt 0.182398 0.098413 -vt 0.176115 0.029485 -vt 0.253863 0.282246 -vt 0.185897 0.108653 -vt 0.175293 0.059048 -vt 0.893638 0.216508 -vt 0.895617 0.215579 -vt 0.000000 0.000000 -vt 0.177381 0.078731 -vt 0.190745 0.118096 -vt 0.133486 0.083715 -vt 0.896762 0.214836 -vt 0.312343 0.226077 -vt 0.197541 0.130230 -vt 0.202979 0.137778 -vt 0.090910 0.472382 -vt 0.285878 0.213415 -vt 0.321871 0.229803 -vt 0.000000 0.000000 -vt 0.221794 0.363777 -vt 0.324442 0.230718 -vt 0.959884 0.096955 -vt 0.957423 0.083715 -vt 0.279880 0.209213 -vt 0.281969 0.259549 -vt 0.224006 0.334604 -vt 0.879032 0.223492 -vt 0.941332 0.173939 -vt 0.109401 0.425680 -vt 0.173457 0.025383 -vt 0.957813 0.137778 -vn 0.7138 -0.6890 0.1257 -vn 0.7248 -0.6888 -0.0142 -vn 0.8263 0.1596 0.5402 -vn 0.8064 0.2439 0.5387 -vn 0.0817 -0.4519 0.8883 -vn 0.7527 0.2600 0.6049 -vn -0.0024 -0.4719 0.8817 -vn -0.1982 0.5703 0.7972 -vn -0.8701 -0.1247 0.4768 -vn 0.8788 -0.4723 0.0686 -vn -0.8217 0.5698 0.0102 -vn -0.8590 0.5119 0.0072 -vn -0.8800 0.4672 0.0858 -vn -1.0000 0.0000 0.0000 -vn -0.9349 0.3548 0.0075 -vn -0.9153 0.3942 0.0829 -vn -0.9234 0.3471 0.1636 -vn -0.2425 0.9701 0.0000 -vn -0.9629 0.2697 0.0052 -vn -0.9711 0.2225 0.0861 -vn -0.9516 0.2617 0.1613 -vn -0.9469 0.2156 0.2384 -vn -0.9964 0.0851 0.0034 -vn -0.9879 0.1299 0.0847 -vn -0.9833 0.0839 0.1615 -vn -0.9637 0.1233 0.2369 -vn -0.9489 0.0817 0.3049 -vn -1.0000 -0.0080 0.0030 -vn -0.9954 -0.0539 0.0798 -vn -0.9869 -0.0092 0.1612 -vn -0.9722 -0.0506 0.2287 -vn -0.9524 -0.0110 0.3046 -vn -0.9317 -0.0459 0.3603 -vn -0.9822 -0.1878 -0.0008 -vn -0.9868 -0.1408 0.0804 -vn -0.9720 -0.1824 0.1484 -vn -0.9635 -0.1379 0.2294 -vn -0.9428 -0.1728 0.2851 -vn -0.9230 -0.1333 0.3610 -vn -0.9006 -0.1607 0.4039 -vn -0.7248 0.6888 0.0142 -vn -0.7914 0.6057 0.0829 -vn -0.8499 0.5027 0.1578 -vn -0.8936 0.3824 0.2351 -vn -0.9171 0.2509 0.3098 -vn -0.9188 0.1173 0.3770 -vn -0.9013 -0.0100 0.4330 -vn -0.7370 0.6726 0.0661 -vn -0.6958 0.7052 0.1364 -vn -0.8008 0.5807 0.1464 -vn -0.7596 0.6132 0.2168 -vn -0.6510 0.7098 0.2691 -vn -0.8536 0.4672 0.2305 -vn -0.8125 0.4996 0.3004 -vn -0.7632 0.5779 0.2890 -vn -0.7087 0.6076 0.3586 -vn -0.5898 0.6986 0.4052 -vn -0.8886 0.3362 0.3121 -vn -0.8476 0.3685 0.3817 -vn -0.8075 0.4535 0.3772 -vn -0.7530 0.4832 0.4466 -vn -0.7037 0.5615 0.4353 -vn -0.6386 0.5860 0.4987 -vn -0.5144 0.6700 0.5352 -vn -0.9020 0.1962 0.3846 -vn -0.8609 0.2286 0.4545 -vn -0.8325 0.3136 0.4567 -vn -0.7780 0.3434 0.5262 -vn -0.7378 0.4284 0.5216 -vn -0.6728 0.4529 0.5850 -vn -0.6235 0.5311 0.5737 -vn -0.5534 0.5487 0.6266 -vn -0.4994 0.6152 0.6100 -vn -0.4300 0.6260 0.6506 -vn -0.8943 0.0577 0.4438 -vn -0.8530 0.0904 0.5141 -vn -0.8363 0.1688 0.5216 -vn -0.7818 0.1986 0.5911 -vn -0.7534 0.2836 0.5933 -vn -0.6883 0.3081 0.6568 -vn -0.6482 0.3931 0.6522 -vn -0.5780 0.4107 0.7052 -vn -0.5287 0.4890 0.6938 -vn -0.4598 0.4996 0.7342 -vn -0.4055 0.5665 0.7174 -vn -0.3428 0.5714 0.7457 -vn -0.8697 -0.0703 0.4885 -vn -0.8285 -0.0376 0.5588 -vn -0.8214 0.0301 0.5695 -vn -0.7671 0.0598 0.6387 -vn -0.7504 0.1387 0.6463 -vn -0.6854 0.1631 0.7096 -vn -0.6570 0.2484 0.7118 -vn -0.5866 0.2660 0.7649 -vn -0.5466 0.3508 0.7604 -vn -0.4773 0.3615 0.8010 -vn -0.4281 0.4394 0.7897 -vn -0.3655 0.4443 0.8180 -vn -0.3111 0.5112 0.8012 -vn -0.2584 0.5120 0.8192 -vn 0.7110 0.6236 -0.3250 -vn -0.4554 0.7273 0.5136 -vn 0.7504 -0.1387 -0.6463 -vn 0.7474 0.0888 -0.6584 -vn -0.2827 0.6296 0.7236 -vn -0.3772 0.7336 0.5652 -vn -0.2903 0.6781 0.6752 -vn -0.2238 0.7283 0.6477 -vn -0.2056 0.6134 0.7626 -vn -0.1391 0.6637 0.7350 -vn -0.0717 0.7545 0.6523 -vn -0.8944 -0.4472 0.0000 -vn -0.9627 -0.2706 0.0007 -vn -0.4961 -0.8505 -0.1745 -vn -0.8496 -0.5274 0.0000 -vn -0.8535 -0.5208 0.0139 -vn -0.8983 -0.4392 0.0105 -vn -0.9153 -0.3935 0.0857 -vn -0.9464 -0.3120 0.0834 -vn -0.9523 -0.2659 0.1498 -vn -0.5905 -0.8021 -0.0891 -vn -0.6782 -0.7344 0.0264 -vn -0.7412 -0.6709 0.0215 -vn -0.7703 -0.6297 0.1009 -vn -0.8266 -0.5544 0.0966 -vn -0.8436 -0.5087 0.1718 -vn -0.8884 -0.4270 0.1684 -vn -0.8942 -0.3813 0.2345 -vn -0.9255 -0.2993 0.2322 -vn -0.9229 -0.2570 0.2865 -vn -0.6436 -0.7584 0.1028 -vn -0.6726 -0.7173 0.1821 -vn -0.7359 -0.6535 0.1772 -vn -0.7529 -0.6078 0.2527 -vn -0.8091 -0.5327 0.2483 -vn -0.8149 -0.4867 0.3148 -vn -0.8595 -0.4053 0.3114 -vn -0.8569 -0.3631 0.3658 -vn -0.8882 -0.2810 0.3635 -vn -0.8807 -0.2451 0.4054 -vn -0.8315 -0.1869 0.5232 -vn -0.7559 -0.2436 0.6077 -vn -0.6582 -0.3014 0.6899 -vn -0.5396 -0.3556 0.7631 -vn -0.4057 -0.4016 0.8211 -vn -0.2654 -0.4364 0.8597 -vn -0.1285 -0.4593 0.8789 -vn -0.8020 -0.1791 0.5698 -vn -0.7898 -0.0996 0.6052 -vn -0.7135 -0.2327 0.6609 -vn -0.7014 -0.1532 0.6961 -vn -0.7474 -0.0888 0.6584 -vn -0.7288 -0.0017 0.6847 -vn -0.6013 -0.2869 0.7457 -vn -0.5892 -0.2079 0.7808 -vn -0.6448 -0.1389 0.7517 -vn -0.6261 -0.0515 0.7781 -vn -0.6719 0.0126 0.7405 -vn -0.6475 0.1021 0.7552 -vn -0.4690 -0.3380 0.8160 -vn -0.4570 -0.2592 0.8509 -vn -0.5188 -0.1904 0.8334 -vn -0.5002 -0.1030 0.8598 -vn -0.5557 -0.0340 0.8307 -vn -0.5312 0.0557 0.8454 -vn -0.5769 0.1196 0.8080 -vn -0.5487 0.2050 0.8105 -vn -0.3251 -0.3819 0.8651 -vn -0.3131 -0.3028 0.9001 -vn -0.3763 -0.2394 0.8950 -vn -0.3576 -0.1520 0.9214 -vn -0.4195 -0.0832 0.9040 -vn -0.3950 0.0065 0.9187 -vn -0.4505 0.0755 0.8896 -vn -0.4224 0.1605 0.8921 -vn -0.4681 0.2247 0.8546 -vn -0.4390 0.3000 0.8469 -vn -0.1806 -0.4159 0.8913 -vn -0.1686 -0.3364 0.9265 -vn -0.2279 -0.2822 0.9319 -vn -0.2093 -0.1948 0.9583 -vn -0.2725 -0.1314 0.9532 -vn -0.2480 -0.0417 0.9679 -vn -0.3098 0.0271 0.9504 -vn -0.2817 0.1122 0.9529 -vn -0.3372 0.1812 0.9238 -vn -0.3082 0.2560 0.9162 -vn -0.3542 0.3205 0.8785 -vn -0.3269 0.3822 0.8643 -vn -0.0449 -0.4394 0.8972 -vn -0.0329 -0.3598 0.9324 -vn -0.0850 -0.3165 0.9448 -vn -0.0665 -0.2294 0.9711 -vn -0.1263 -0.1749 0.9765 -vn -0.1019 -0.0854 0.9911 -vn -0.1652 -0.0219 0.9860 -vn -0.1370 0.0635 0.9885 -vn -0.1987 0.1321 0.9711 -vn -0.1694 0.2073 0.9635 -vn -0.2247 0.2760 0.9345 -vn -0.1973 0.3377 0.9203 -vn -0.2433 0.4023 0.8826 -vn -0.2197 0.4498 0.8657 -vn -0.1223 0.5442 0.8300 -vn 0.0027 0.5315 0.8470 -vn 0.1408 0.5080 0.8498 -vn 0.2853 0.4727 0.8338 -vn 0.4266 0.4267 0.7974 -vn 0.5553 0.3733 0.7431 -vn 0.6648 0.3164 0.6767 -vn -0.0727 0.5677 0.8200 -vn -0.0633 0.6376 0.7678 -vn 0.0643 0.5562 0.8286 -vn 0.0737 0.6261 0.7763 -vn -0.0017 0.6622 0.7493 -vn 0.2128 0.5308 0.8203 -vn 0.2220 0.6003 0.7683 -vn 0.1452 0.6487 0.7470 -vn 0.1503 0.7155 0.6822 -vn 0.0754 0.7515 0.6554 -vn 0.3631 0.4902 0.7924 -vn 0.3723 0.5595 0.7405 -vn 0.2996 0.6178 0.7270 -vn 0.3047 0.6846 0.6622 -vn 0.2279 0.7330 0.6409 -vn 0.7288 0.0017 -0.6847 -vn 0.5042 0.4361 0.7454 -vn 0.5134 0.5056 0.6934 -vn 0.4500 0.5689 0.6883 -vn 0.4551 0.6357 0.6235 -vn 0.3824 0.7524 0.5363 -vn 0.6268 0.3734 0.6839 -vn 0.6360 0.4434 0.6316 -vn 0.5851 0.5058 0.6339 -vn 0.5901 0.5726 0.5691 -vn 0.5268 0.6359 0.5640 -vn 0.5268 0.6943 0.4903 -vn 0.4542 0.7526 0.4768 -vn 0.7559 0.2436 -0.6077 -vn 0.7263 0.3081 0.6145 -vn 0.7355 0.3781 0.5623 -vn 0.6974 0.4351 0.5695 -vn 0.7024 0.5016 0.5050 -vn 0.6512 0.5644 0.5073 -vn 0.6513 0.6227 0.4337 -vn 0.7359 0.6535 -0.1772 -vn 0.5830 0.7324 0.3517 -vn -0.8380 -0.2613 0.4791 -vn -0.8143 -0.3793 0.4394 -vn -0.7727 -0.5027 0.3877 -vn -0.7110 -0.6236 0.3250 -vn -0.6307 -0.7331 0.2544 -vn -0.8134 -0.3094 0.4926 -vn -0.7624 -0.3179 0.5636 -vn -0.7778 -0.4363 0.4524 -vn -0.7269 -0.4448 0.5233 -vn -0.7259 -0.3749 0.5766 -vn -0.6646 -0.3750 0.6463 -vn -0.7224 -0.5655 0.3978 -vn -0.6718 -0.5739 0.4684 -vn -0.6770 -0.5072 0.5333 -vn -0.6154 -0.5072 0.6034 -vn -0.6145 -0.4378 0.6563 -vn -0.5460 -0.4286 0.7199 -vn -0.6474 -0.6871 0.3298 -vn -0.5969 -0.6954 0.4002 -vn -0.6084 -0.6371 0.4732 -vn -0.5469 -0.6371 0.5432 -vn -0.5521 -0.5705 0.6081 -vn -0.4834 -0.5612 0.6718 -vn -0.4825 -0.4921 0.7246 -vn -0.4121 -0.4746 0.7778 -vn -0.5230 -0.7537 0.3981 -vn -0.4614 -0.7536 0.4681 -vn -0.4730 -0.6954 0.5411 -vn -0.4043 -0.6861 0.6048 -vn -0.4095 -0.6195 0.6697 -vn -0.3393 -0.6021 0.7227 -vn -0.3383 -0.5328 0.7757 -vn -0.2719 -0.5100 0.8161 -vn -0.3252 -0.7345 0.5956 -vn -0.2550 -0.7171 0.6486 -vn -0.2601 -0.6505 0.7136 -vn -0.1940 -0.6279 0.7537 -vn -0.1930 -0.5581 0.8070 -vn -0.1350 -0.5336 0.8349 -vn -0.1768 -0.7531 0.6337 -vn 0.7412 0.6709 -0.0215 -vn -0.1103 -0.7304 0.6741 -vn -0.1154 -0.6640 0.7388 -vn -0.0574 -0.6395 0.7667 -vn -0.0563 -0.5696 0.8200 -vn -0.0089 -0.5463 0.8376 -vn -0.1356 0.4698 0.8723 -vn -0.1134 0.3576 0.9270 -vn -0.0862 0.2270 0.9701 -vn -0.0544 0.0830 0.9951 -vn -0.0193 -0.0659 0.9976 -vn 0.0167 -0.2097 0.9776 -vn 0.0511 -0.3399 0.9391 -vn -0.0960 0.4372 0.8942 -vn -0.0106 0.4572 0.8893 -vn -0.0638 0.3142 0.9472 -vn 0.0215 0.3341 0.9423 -vn 0.0389 0.4137 0.9096 -vn 0.1276 0.4343 0.8917 -vn -0.0280 0.1725 0.9846 -vn 0.0568 0.1924 0.9797 -vn 0.0794 0.2799 0.9567 -vn 0.1685 0.3006 0.9388 -vn 0.1858 0.3797 0.9063 -vn 0.2722 0.3996 0.8754 -vn 0.0090 0.0195 0.9998 -vn 0.0936 0.0393 0.9948 -vn 0.1200 0.1290 0.9844 -vn 0.2091 0.1497 0.9664 -vn 0.2316 0.2372 0.9434 -vn 0.3183 0.2571 0.9125 -vn 0.3356 0.3359 0.8801 -vn 0.4136 0.3537 0.8390 -vn 0.0444 -0.1345 0.9899 -vn 0.1292 -0.1145 0.9850 -vn 0.1574 -0.0295 0.9871 -vn 0.2465 -0.0088 0.9691 -vn 0.2729 0.0809 0.9586 -vn 0.3595 0.1008 0.9277 -vn 0.3821 0.1883 0.9048 -vn 0.4598 0.2059 0.8638 -vn 0.4772 0.2850 0.8313 -vn 0.5422 0.2996 0.7850 -vn 0.0758 -0.2783 0.9575 -vn 0.1611 -0.2582 0.9525 -vn 0.1886 -0.1835 0.9648 -vn 0.2777 -0.1628 0.9468 -vn 0.3058 -0.0777 0.9489 -vn 0.3925 -0.0578 0.9179 -vn 0.4189 0.0319 0.9075 -vn 0.4967 0.0495 0.8665 -vn 0.5193 0.1370 0.8436 -vn 0.5838 0.1514 0.7976 -vn 0.6013 0.2310 0.7649 -vn 0.6515 0.2420 0.7190 -vn 0.1024 -0.4045 0.9088 -vn 0.1877 -0.3843 0.9039 -vn 0.2125 -0.3227 0.9223 -vn 0.3012 -0.3021 0.9045 -vn 0.3287 -0.2269 0.9168 -vn 0.4152 -0.2070 0.8859 -vn 0.4434 -0.1217 0.8880 -vn 0.5214 -0.1041 0.8469 -vn 0.5478 -0.0145 0.8365 -vn 0.6128 -0.0001 0.7902 -vn 0.6353 0.0870 0.7674 -vn 0.6855 0.0980 0.7215 -vn 0.7030 0.1776 0.6887 -vn 0.7394 0.1855 0.6472 -vn 0.7534 -0.2836 -0.5933 -vn 0.7040 0.6070 0.3687 -vn 0.7556 0.4858 0.4395 -vn 0.7891 0.3621 0.4962 -vn 0.6194 0.7581 0.2040 -vn 0.6655 0.7343 0.1339 -vn 0.6990 0.6530 0.2916 -vn 0.7449 0.6293 0.2216 -vn 0.7286 0.6707 0.1387 -vn 0.7269 0.4448 -0.5233 -vn 0.7602 0.5319 0.3731 -vn 0.8063 0.5082 0.3028 -vn 0.8013 0.5540 0.2260 -vn 0.8408 0.5206 0.1484 -vn 0.2425 0.9701 0.0000 -vn 0.8001 0.4044 0.4430 -vn 0.8465 0.3806 0.3723 -vn 0.8511 0.4265 0.3063 -vn 0.8906 0.3931 0.2287 -vn 0.8856 0.4390 0.1519 -vn 0.8203 0.2800 0.4986 -vn 0.8667 0.2562 0.4280 -vn 0.8777 0.2986 0.3748 -vn 0.9171 0.2654 0.2975 -vn 0.9217 0.3116 0.2311 -vn 0.9507 0.2704 0.1519 -vn -0.0311 -0.7561 0.6537 -vn 0.1199 -0.7299 0.6729 -vn 0.0378 -0.7138 0.6994 -vn 0.0226 -0.6654 0.7461 -vn 0.3696 -0.7286 0.5767 -vn 0.2843 -0.7351 0.6155 -vn 0.2677 -0.6853 0.6773 -vn 0.1813 -0.6797 0.7107 -vn 0.1661 -0.6314 0.7574 -vn 0.0840 -0.6152 0.7839 -vn 0.0712 -0.5723 0.8170 -vn 0.9702 0.1876 0.1533 -vn 0.9368 0.1819 0.2990 -vn 0.8866 0.1719 0.4294 -vn 1.0000 0.0000 0.0000 -vn 0.9620 0.1404 0.2341 -vn 0.9706 0.0536 0.2346 -vn 0.9872 0.0078 0.1593 -vn 0.9836 -0.0853 0.1589 -vn 0.9195 -0.3923 -0.0245 -vn 0.9533 -0.3010 -0.0251 -vn 0.9158 0.1372 0.3776 -vn 0.9244 0.0498 0.3781 -vn 0.9495 0.0087 0.3135 -vn 0.9459 -0.0845 0.3131 -vn 0.9625 -0.1302 0.2378 -vn 0.9458 -0.2229 0.2364 -vn 0.9503 -0.2699 0.1550 -vn 0.9223 -0.3550 0.1527 -vn 0.9095 -0.4158 0.0000 -vn 0.7494 -0.6570 -0.0821 -vn 0.8552 0.1322 0.5012 -vn 0.8639 0.0448 0.5017 -vn 0.8931 0.0101 0.4498 -vn 0.8895 -0.0826 0.4494 -vn 0.9148 -0.1240 0.3844 -vn 0.8980 -0.2164 0.3830 -vn 0.9147 -0.2623 0.3075 -vn 0.8866 -0.3476 0.3052 -vn 0.8911 -0.3946 0.2240 -vn 0.8558 -0.4675 0.2212 -vn 0.8474 -0.5121 0.1402 -vn 0.8101 -0.5700 0.1373 -vn 0.1272 -0.5140 0.8483 -vn 0.2221 -0.5732 0.7887 -vn 0.3231 -0.6276 0.7083 -vn 0.4245 -0.6714 0.6075 -vn 0.5193 -0.6996 0.4907 -vn 0.6010 -0.7105 0.3659 -vn 0.6660 -0.7056 0.2419 -vn -0.6656 0.7061 -0.2417 -vn 0.1820 -0.5131 0.8388 -vn 0.2332 -0.4463 0.8640 -vn 0.2884 -0.5683 0.7707 -vn 0.3394 -0.5014 0.7958 -vn 0.2995 -0.4414 0.8459 -vn 0.3462 -0.3635 0.8649 -vn 0.3979 -0.6168 0.6791 -vn 0.4487 -0.5503 0.7042 -vn 0.4138 -0.4907 0.7668 -vn 0.4607 -0.4125 0.7859 -vn 0.4210 -0.3527 0.8357 -vn 0.4598 -0.2679 0.8466 -vn 0.5024 -0.6536 0.5661 -vn 0.5529 -0.5873 0.5911 -vn 0.5262 -0.5326 0.6629 -vn 0.5731 -0.4544 0.6820 -vn 0.5383 -0.3948 0.7446 -vn 0.5772 -0.3098 0.7556 -vn 0.5376 -0.2502 0.8052 -vn 0.5661 -0.1650 0.8077 -vn 0.5933 -0.6751 0.4384 -vn 0.6440 -0.6086 0.4635 -vn 0.6271 -0.5627 0.5386 -vn 0.6740 -0.4845 0.5577 -vn 0.6473 -0.4298 0.6295 -vn 0.6862 -0.3448 0.6405 -vn 0.6514 -0.2852 0.7031 -vn 0.6797 -0.2002 0.7056 -vn 0.6401 -0.1404 0.7554 -vn 0.6578 -0.0615 0.7507 -vn 0.6655 -0.6808 0.3061 -vn 0.7164 -0.6139 0.3314 -vn 0.7087 -0.5787 0.4035 -vn 0.7556 -0.5004 0.4226 -vn 0.7387 -0.4546 0.4977 -vn 0.7776 -0.3696 0.5087 -vn 0.7509 -0.3149 0.5805 -vn 0.7793 -0.2299 0.5830 -vn 0.7445 -0.1702 0.6456 -vn 0.7621 -0.0918 0.6409 -vn 0.7222 -0.0316 0.6909 -vn 0.7309 0.0361 0.6816 -vn 0.7178 -0.6729 0.1789 -vn 0.7688 -0.6061 0.2042 -vn 0.7682 -0.5812 0.2685 -vn 0.8149 -0.5033 0.2876 -vn 0.8071 -0.4679 0.3601 -vn 0.8459 -0.3831 0.3710 -vn 0.8289 -0.3371 0.4464 -vn 0.8574 -0.2519 0.4488 -vn 0.8308 -0.1973 0.5205 -vn 0.8485 -0.1184 0.5157 -vn 0.8139 -0.0590 0.5781 -vn 0.8225 0.0087 0.5687 -vn 0.7826 0.0689 0.6187 -vn 0.7849 0.1234 0.6073 -vn 0.7552 -0.6528 0.0586 -vn 0.9006 0.1607 -0.4039 -vn 0.7903 -0.6097 0.0614 -vn 0.8807 0.2451 -0.4054 -vn 0.8380 0.2613 -0.4791 -vn 0.7624 0.3179 -0.5636 -vn -0.6515 -0.2420 -0.7190 -vn 0.8134 0.3094 -0.4926 -vn 0.8143 0.3793 -0.4394 -vn 0.7778 0.4363 -0.4524 -vn -0.6514 0.2852 -0.7031 -vn 0.7224 0.5655 -0.3978 -vn -0.6473 0.4298 -0.6295 -vn 0.5877 0.6862 0.4287 -vn 0.6357 0.7167 0.2868 -vn 0.7898 0.0996 -0.6052 -vn 0.8020 0.1791 -0.5698 -vn 0.8315 0.1869 -0.5232 -vn 0.7529 0.6078 -0.2527 -vn 0.8149 0.4867 -0.3148 -vn 0.8569 0.3631 -0.3658 -vn -0.6360 -0.4434 -0.6316 -vn 0.7703 0.6297 -0.1009 -vn -0.6128 0.0001 -0.7902 -vn 0.8091 0.5327 -0.2483 -vn 0.8436 0.5087 -0.1718 -vn 0.8266 0.5544 -0.0966 -vn 0.8535 0.5208 -0.0139 -vn 0.8595 0.4053 -0.3114 -vn 0.8942 0.3813 -0.2345 -vn 0.8884 0.4270 -0.1684 -vn 0.9153 0.3935 -0.0857 -vn 0.8983 0.4392 -0.0105 -vn 0.8882 0.2810 -0.3635 -vn 0.9229 0.2570 -0.2865 -vn 0.9255 0.2993 -0.2322 -vn 0.9523 0.2659 -0.1498 -vn 0.9464 0.3120 -0.0834 -vn 0.9627 0.2706 -0.0007 -vn 0.9822 0.1878 0.0008 -vn 0.9720 0.1824 -0.1484 -vn 0.9428 0.1728 -0.2851 -vn 0.9868 0.1408 -0.0804 -vn 0.9953 0.0539 -0.0798 -vn 1.0000 0.0080 -0.0030 -vn 0.9964 -0.0851 -0.0034 -vn 0.9326 -0.3277 0.1512 -vn 0.9046 -0.3928 0.1657 -vn 0.9635 0.1379 -0.2294 -vn 0.9722 0.0506 -0.2287 -vn 0.9869 0.0092 -0.1612 -vn 0.9833 -0.0839 -0.1615 -vn 0.9879 -0.1299 -0.0847 -vn 0.9711 -0.2225 -0.0861 -vn 0.9629 -0.2697 -0.0052 -vn 0.9349 -0.3548 -0.0075 -vn 0.7826 -0.5929 0.1897 -vn 0.7228 -0.6470 0.2426 -vn 0.9230 0.1333 -0.3610 -vn 0.9317 0.0459 -0.3603 -vn 0.9524 0.0110 -0.3046 -vn 0.9489 -0.0817 -0.3049 -vn 0.9637 -0.1233 -0.2369 -vn 0.9469 -0.2156 -0.2384 -vn 0.9516 -0.2617 -0.1613 -vn 0.9234 -0.3471 -0.1636 -vn 0.9153 -0.3942 -0.0829 -vn 0.8800 -0.4672 -0.0858 -vn 0.8590 -0.5119 -0.0072 -vn 0.8217 -0.5698 -0.0102 -vn 0.6958 -0.7052 -0.1364 -vn 0.7087 -0.6076 -0.3586 -vn 0.7037 -0.5615 -0.4353 -vn 0.7530 -0.4832 -0.4466 -vn 0.7378 -0.4284 -0.5216 -vn 0.7780 -0.3434 -0.5262 -vn -0.6353 -0.0870 -0.7674 -vn 0.7818 -0.1986 -0.5911 -vn -0.6401 0.1404 -0.7554 -vn 0.7671 -0.0598 -0.6387 -vn 0.7053 -0.6801 -0.1999 -vn 0.7596 -0.6132 -0.2168 -vn 0.7632 -0.5779 -0.2891 -vn 0.8125 -0.4996 -0.3004 -vn 0.8075 -0.4535 -0.3772 -vn 0.8476 -0.3685 -0.3817 -vn 0.8325 -0.3136 -0.4567 -vn 0.8609 -0.2286 -0.4545 -vn 0.8363 -0.1688 -0.5216 -vn 0.8530 -0.0904 -0.5141 -vn 0.8214 -0.0301 -0.5695 -vn 0.8285 0.0376 -0.5588 -vn 0.7370 -0.6726 -0.0661 -vn 0.7914 -0.6057 -0.0829 -vn 0.8008 -0.5807 -0.1464 -vn 0.8499 -0.5027 -0.1578 -vn 0.8536 -0.4672 -0.2305 -vn 0.8936 -0.3824 -0.2351 -vn 0.8886 -0.3362 -0.3121 -vn 0.9171 -0.2509 -0.3098 -vn 0.9020 -0.1962 -0.3846 -vn 0.9188 -0.1173 -0.3770 -vn 0.8943 -0.0577 -0.4438 -vn 0.9013 0.0100 -0.4330 -vn 0.8697 0.0703 -0.4885 -vn 0.8701 0.1247 -0.4768 -vn -0.7138 0.6890 -0.1257 -vn -0.8263 -0.1596 -0.5402 -vn 0.7727 0.5027 -0.3877 -vn -0.8064 -0.2439 -0.5387 -vn -0.7527 -0.2600 -0.6049 -vn -0.6648 -0.3164 -0.6767 -vn -0.6268 -0.3734 -0.6839 -vn -0.1468 0.7121 0.6866 -vn -0.7263 -0.3081 -0.6145 -vn -0.7355 -0.3781 -0.5623 -vn -0.6974 -0.4351 -0.5695 -vn -0.7024 -0.5016 -0.5050 -vn -0.6512 -0.5644 -0.5073 -vn -0.6513 -0.6227 -0.4337 -vn -0.3704 0.6837 0.6287 -vn -0.6740 0.4845 -0.5577 -vn -0.6440 0.6086 -0.4635 -vn -0.6855 -0.0980 -0.7215 -vn -0.7030 -0.1776 -0.6887 -vn -0.7394 -0.1855 -0.6472 -vn -0.7040 -0.6070 -0.3687 -vn -0.7556 -0.4858 -0.4395 -vn -0.7891 -0.3621 -0.4962 -vn -0.6990 -0.6530 -0.2916 -vn -0.7449 -0.6293 -0.2216 -vn -0.7286 -0.6707 -0.1387 -vn -0.7602 -0.5319 -0.3731 -vn -0.8063 -0.5082 -0.3028 -vn -0.8013 -0.5540 -0.2260 -vn -0.8408 -0.5206 -0.1484 -vn -0.2425 -0.9701 0.0000 -vn -0.8001 -0.4044 -0.4430 -vn -0.8465 -0.3806 -0.3723 -vn -0.8511 -0.4265 -0.3063 -vn -0.8906 -0.3931 -0.2287 -vn -0.8856 -0.4390 -0.1519 -vn -0.8203 -0.2800 -0.4986 -vn -0.8667 -0.2562 -0.4280 -vn -0.8777 -0.2986 -0.3748 -vn -0.9171 -0.2654 -0.2975 -vn -0.9217 -0.3116 -0.2311 -vn -0.9507 -0.2704 -0.1519 -vn -0.9702 -0.1876 -0.1533 -vn -0.9368 -0.1819 -0.2990 -vn -0.8866 -0.1719 -0.4294 -vn -0.9908 0.0000 -0.1351 -vn -0.9620 -0.1404 -0.2341 -vn -0.9706 -0.0536 -0.2346 -vn -0.9872 -0.0078 -0.1593 -vn -0.9836 0.0853 -0.1589 -vn -0.9282 0.3713 0.0248 -vn -0.9526 0.3033 -0.0253 -vn -0.9158 -0.1372 -0.3776 -vn -0.9244 -0.0498 -0.3781 -vn -0.9495 -0.0087 -0.3135 -vn -0.9459 0.0845 -0.3131 -vn -0.9625 0.1302 -0.2378 -vn -0.9458 0.2229 -0.2364 -vn -0.9503 0.2699 -0.1550 -vn -0.9223 0.3550 -0.1527 -vn -0.9171 0.3987 0.0000 -vn -0.8321 0.5471 0.0912 -vn -0.8552 -0.1322 -0.5012 -vn -0.8639 -0.0448 -0.5017 -vn -0.8931 -0.0101 -0.4498 -vn -0.8895 0.0826 -0.4494 -vn -0.9148 0.1240 -0.3844 -vn -0.8980 0.2164 -0.3830 -vn -0.9147 0.2623 -0.3075 -vn -0.8866 0.3476 -0.3052 -vn -0.8911 0.3946 -0.2240 -vn -0.8558 0.4675 -0.2212 -vn -0.8474 0.5121 -0.1402 -vn -0.8101 0.5700 -0.1373 -vn -0.6660 0.7056 -0.2419 -vn -0.6655 0.6808 -0.3061 -vn -0.6271 0.5627 -0.5386 -vn -0.7053 0.6801 0.1999 -vn -0.5848 0.6523 0.4821 -vn -0.6862 0.3448 -0.6405 -vn 0.0034 0.7287 0.6848 -vn -0.6797 0.2002 -0.7056 -vn 0.3824 0.6940 0.6101 -vn -0.6578 0.0615 -0.7507 -vn -0.6547 0.6741 0.3419 -vn -0.7164 0.6139 -0.3314 -vn -0.7087 0.5787 -0.4035 -vn -0.7556 0.5004 -0.4226 -vn -0.7387 0.4546 -0.4977 -vn -0.7776 0.3696 -0.5087 -vn -0.7509 0.3149 -0.5805 -vn -0.7793 0.2299 -0.5830 -vn -0.7445 0.1702 -0.6456 -vn -0.7621 0.0918 -0.6409 -vn -0.7222 0.0316 -0.6909 -vn -0.7309 -0.0361 -0.6816 -vn -0.7178 0.6729 -0.1789 -vn -0.7688 0.6061 -0.2042 -vn -0.7682 0.5812 -0.2685 -vn -0.8149 0.5033 -0.2876 -vn -0.8071 0.4679 -0.3601 -vn -0.8459 0.3831 -0.3710 -vn -0.8289 0.3371 -0.4464 -vn -0.8574 0.2519 -0.4488 -vn -0.8308 0.1973 -0.5205 -vn -0.8485 0.1184 -0.5157 -vn -0.8139 0.0590 -0.5781 -vn -0.8225 -0.0087 -0.5687 -vn -0.7826 -0.0689 -0.6187 -vn -0.7849 -0.1234 -0.6073 -vn 0.9138 -0.3998 0.0714 -vn 0.9714 -0.2250 0.0762 -vn 0.9882 -0.1324 0.0776 -vn 0.9954 0.0551 0.0784 -vn 0.9869 0.1417 0.0778 -vn 0.9457 0.3165 0.0748 -vn 0.9146 0.3977 0.0724 -vn 0.7681 0.6374 0.0612 -vn 0.8245 0.5621 0.0655 -vn -0.8245 -0.5621 -0.0655 -vn -0.7681 -0.6374 -0.0612 -vn -0.9146 -0.3977 -0.0724 -vn -0.9457 -0.3165 -0.0748 -vn -0.9869 -0.1417 -0.0778 -vn -0.9954 -0.0551 -0.0784 -vn -0.9882 0.1324 -0.0776 -vn -0.9714 0.2250 -0.0762 -vn -0.9138 0.3998 -0.0714 -vn -0.8788 0.4723 -0.0686 -vn -0.7903 0.6097 -0.0614 -vn -0.7552 0.6528 -0.0586 -vn 0.7259 0.3749 -0.5766 -vn 0.6030 -0.7521 -0.2660 -vn 0.7672 0.5877 -0.2568 -vn -0.7057 0.6540 -0.2725 -vn -0.6379 -0.6542 -0.4064 -vn -0.2663 -0.7446 -0.6121 -vn 0.7041 -0.5768 -0.4142 -vn -0.6665 0.0874 -0.7404 -vn 0.2379 -0.7457 -0.6224 -vn 0.6401 -0.7535 -0.1500 -vn -0.5482 0.6512 -0.5248 -vn 0.6047 -0.7521 -0.2620 -vn -0.6515 -0.1352 -0.7465 -vn -0.1362 -0.7441 -0.6541 -vn 0.2426 -0.7459 -0.6203 -vn -0.6882 0.3197 -0.6513 -vn -0.6766 0.2023 -0.7080 -vn -0.6394 0.6524 -0.4068 -vn 0.6411 -0.7538 -0.1442 -vn -0.6500 -0.7509 -0.1170 -vn -0.7121 0.5137 -0.4786 -vn 0.7089 -0.5499 -0.4417 -vn -0.7345 0.6306 -0.2509 -vn -0.4755 -0.7460 -0.4662 -vn -0.6416 -0.3393 -0.6879 -vn 0.7347 0.6601 -0.1564 -vn -0.3060 0.6499 -0.6956 -vn -0.6880 0.3179 -0.6524 -vn 0.5511 0.6562 -0.5155 -vn -0.1413 -0.7443 -0.6527 -vn 0.7475 -0.2228 -0.6258 -vn 0.7491 0.6624 -0.0055 -vn -0.6365 -0.5715 -0.5179 -vn -0.0154 -0.7444 -0.6676 -vn 0.1301 0.6509 -0.7479 -vn -0.7431 0.6553 -0.1358 -vn -0.6984 0.6531 -0.2927 -vn -0.3766 -0.7450 -0.5506 -vn 0.7760 0.4185 -0.4719 -vn -0.6365 -0.5673 -0.5225 -vn 0.6875 -0.6611 -0.3007 -vn -0.6634 0.0323 -0.7476 -vn -0.6420 -0.7167 -0.2724 -vn 0.7361 -0.3409 -0.5847 -vn 0.6707 -0.7229 -0.1662 -vn -0.0283 0.6505 -0.7590 -vn -0.6560 -0.0682 -0.7517 -vn -0.5071 0.6520 -0.5636 -vn 0.5242 0.6547 -0.5447 -vn 0.7743 0.4705 -0.4232 -vn -0.6999 0.4236 -0.5751 -vn -0.6506 -0.7509 -0.1137 -vn 0.5132 0.6542 -0.5556 -vn -0.0200 -0.7441 -0.6677 -vn 0.7360 -0.3419 -0.5843 -vn 0.7670 0.5898 -0.2526 -vn 0.2781 0.6518 -0.7055 -vn -0.6381 -0.6587 -0.3986 -vn 0.3574 -0.7471 -0.5604 -vn 0.7599 0.6337 -0.1448 -vn -0.5584 -0.7476 -0.3596 -vn -0.2684 -0.7446 -0.6111 -vn 0.7045 -0.5738 -0.4177 -vn 0.6765 0.6581 -0.3305 -vn 0.6879 -0.6594 -0.3034 -vn 0.4608 -0.7486 -0.4767 -vn -0.4790 -0.7463 -0.4621 -vn -0.6488 -0.7498 -0.1298 -vn 0.3606 -0.7471 -0.5584 -vn 0.6010 -0.7517 -0.2714 -vn -0.0115 -0.7445 -0.6675 -vn -0.6765 0.1995 -0.7089 -vn 0.6545 -0.7559 -0.0127 -vn -0.6478 -0.2057 -0.7335 -vn 0.7495 -0.2059 -0.6292 -vn 0.7685 0.0695 -0.6361 -vn -0.6175 -0.7492 -0.2397 -vn 0.7493 0.6620 -0.0170 -vn 0.7525 0.6571 -0.0442 -vn -0.6364 -0.5691 -0.5207 -vn 0.6551 -0.7551 -0.0251 -vn -0.6377 -0.4634 -0.6152 -vn 0.6419 -0.7540 -0.1392 -vn -0.6421 -0.7181 -0.2684 -vn -0.1634 0.6498 -0.7423 -vn -0.6378 -0.4613 -0.6168 -vn -0.6379 -0.6575 -0.4009 -vn -0.6416 -0.3405 -0.6873 -vn 0.5432 -0.7502 -0.3770 -vn -0.6490 -0.7503 -0.1262 -vn 0.7715 0.5392 -0.3377 -vn 0.7764 0.3214 -0.5422 -vn -0.3807 -0.7453 -0.5474 -vn 0.4549 -0.7482 -0.4830 -vn 0.3528 -0.7468 -0.5638 -vn 0.6195 0.6563 -0.4307 -vn -0.0073 0.6499 -0.7600 -vn -0.6772 0.2124 -0.7044 -vn -0.7216 0.5711 -0.3915 -vn 0.7739 0.2010 -0.6006 -vn -0.2642 0.6499 -0.7126 -vn -0.6378 -0.6554 -0.4045 -vn 0.7494 0.6620 -0.0139 -vn -0.7098 0.4946 -0.5015 -vn -0.7326 0.6261 -0.2669 -vn 0.7583 -0.0879 -0.6460 -vn 0.3971 0.6530 -0.6449 -vn 0.7494 -0.2068 -0.6290 -vn 0.6870 -0.6636 -0.2960 -vn -0.7237 0.5829 -0.3695 -vn -0.6278 0.6522 -0.4249 -vn -0.3820 -0.7453 -0.5465 -vn 0.6710 -0.7216 -0.1703 -vn -0.6429 -0.2908 -0.7086 -vn -0.7000 0.4247 -0.5741 -vn 0.7214 -0.4618 -0.5161 -vn -0.4353 0.6505 -0.6224 -vn 0.1355 0.6508 -0.7471 -vn 0.5386 -0.7499 -0.3841 -vn 0.7592 0.6368 -0.1346 -vn 0.7632 -0.0307 -0.6455 -vn 0.7752 0.2794 -0.5665 -vn 0.6702 -0.7246 -0.1603 -vn 0.4093 0.6531 -0.6372 -vn 0.1179 -0.7450 -0.6565 -vn 0.6904 0.6582 -0.3003 -vn -0.7417 0.6546 -0.1465 -vn -0.4190 0.6498 -0.6342 -vn 0.7312 0.6601 -0.1724 -vn 0.5416 -0.7503 -0.3792 -vn -0.6424 -0.7190 -0.2653 -vn 0.2746 0.6518 -0.7070 -vn -0.5606 -0.7476 -0.3562 -vn 0.1218 -0.7452 -0.6557 -vn -0.6161 -0.7492 -0.2432 -vn 0.7209 -0.4656 -0.5133 -vn 0.7728 0.5197 -0.3644 -vn 0.7713 0.1344 -0.6222 -vn -0.2614 -0.7444 -0.6145 -vn -0.1443 -0.7444 -0.6520 -vn 0.4589 -0.7485 -0.4787 -vn -0.5558 -0.7473 -0.3642 -vn -0.6516 -0.7507 -0.1090 -vn -0.4819 -0.7462 -0.4594 -usemtl Material.002 -s off -f 2155/2644/2154 2164/2645/2154 2448/2646/2154 -f 2494/2647/2155 2155/2648/2155 2542/2649/2155 -f 2156/2650/2156 2170/2651/2156 2181/2652/2156 -f 2156/2650/2157 2181/2652/2157 2187/2653/2157 -f 2194/2654/2158 2157/2655/2158 2177/2656/2158 -f 2156/2650/2159 2187/2653/2159 2195/2657/2159 -f 2206/2658/2160 2157/2655/2160 2194/2654/2160 -f 2227/2659/2161 2158/2660/2161 2214/2661/2161 -f 2159/2662/2162 2221/2663/2162 2234/2664/2162 -f 2531/2665/2163 2530/2666/2163 2367/2667/2163 2368/2668/2163 -f 2235/2669/2164 2229/2670/2164 2228/2671/2164 -f 2235/2669/2165 2236/2672/2165 2229/2670/2165 -f 2236/2672/2166 2230/2673/2166 2229/2670/2166 -f 2400/2674/2167 2237/2675/2167 2399/2676/2167 -f 2237/2675/2168 2238/2677/2168 2236/2672/2168 -f 2236/2672/2169 2238/2677/2169 2230/2673/2169 -f 2238/2677/2170 2231/2678/2170 2230/2673/2170 -f 2406/2679/2171 2237/2675/2171 2400/2674/2171 -f 2237/2675/2172 2239/2680/2172 2238/2677/2172 -f 2239/2680/2173 2240/2681/2173 2238/2677/2173 -f 2238/2677/2174 2240/2681/2174 2231/2678/2174 -f 2240/2681/2175 2232/2682/2175 2231/2678/2175 -f 2241/2683/2176 2242/2684/2176 2239/2680/2176 -f 2239/2680/2177 2242/2684/2177 2240/2681/2177 -f 2242/2684/2178 2243/2685/2178 2240/2681/2178 -f 2240/2681/2179 2243/2685/2179 2232/2682/2179 -f 2243/2685/2180 2233/2686/2180 2232/2682/2180 -f 2242/2684/2181 2241/2683/2181 2244/2687/2181 -f 2244/2687/2182 2245/2688/2182 2242/2684/2182 -f 2242/2684/2183 2245/2688/2183 2243/2685/2183 -f 2245/2688/2184 2246/2689/2184 2243/2685/2184 -f 2243/2685/2185 2246/2689/2185 2233/2686/2185 -f 2246/2689/2186 2234/2690/2186 2233/2686/2186 -f 2244/2687/2187 2391/2691/2187 2224/2692/2187 -f 2244/2687/2188 2224/2692/2188 2245/2688/2188 -f 2224/2692/2189 2223/2693/2189 2245/2688/2189 -f 2245/2688/2190 2223/2693/2190 2246/2689/2190 -f 2223/2693/2191 2222/2694/2191 2246/2689/2191 -f 2246/2689/2192 2222/2694/2192 2234/2690/2192 -f 2222/2694/2193 2159/2695/2193 2234/2690/2193 -f 2160/2696/2194 2228/2697/2194 2415/2698/2194 2414/2699/2194 -f 2229/2700/2195 2247/2701/2195 2228/2697/2195 -f 2230/2702/2196 2248/2703/2196 2229/2700/2196 -f 2231/2704/2197 2250/2705/2197 2230/2702/2197 -f 2232/2706/2198 2253/2707/2198 2231/2704/2198 -f 2233/2708/2199 2257/2709/2199 2232/2706/2199 -f 2234/2664/2200 2262/2710/2200 2233/2708/2200 -f 2416/2711/2201 2415/2698/2201 2228/2697/2201 2247/2701/2201 -f 2416/2711/2202 2247/2701/2202 2422/2712/2202 -f 2229/2700/2203 2248/2703/2203 2247/2701/2203 -f 2248/2703/2204 2249/2713/2204 2247/2701/2204 -f 2425/2714/2205 2249/2713/2205 2426/2715/2205 -f 2230/2702/2206 2250/2705/2206 2248/2703/2206 -f 2250/2705/2207 2251/2716/2207 2248/2703/2207 -f 2248/2703/2208 2251/2716/2208 2249/2713/2208 -f 2249/2713/2209 2251/2716/2209 2252/2717/2209 -f 2430/2718/2210 2252/2717/2210 2431/2719/2210 -f 2231/2704/2211 2253/2707/2211 2250/2705/2211 -f 2253/2707/2212 2254/2720/2212 2250/2705/2212 -f 2250/2705/2213 2254/2720/2213 2251/2716/2213 -f 2254/2720/2214 2255/2721/2214 2251/2716/2214 -f 2251/2716/2215 2255/2721/2215 2252/2717/2215 -f 2252/2717/2216 2255/2721/2216 2256/2722/2216 -f 2436/2723/2217 2256/2722/2217 2225/2724/2217 2435/2725/2217 -f 2232/2706/2218 2257/2709/2218 2253/2707/2218 -f 2257/2709/2219 2258/2726/2219 2253/2707/2219 -f 2253/2707/2220 2258/2726/2220 2254/2720/2220 -f 2258/2726/2221 2259/2727/2221 2254/2720/2221 -f 2254/2720/2222 2259/2727/2222 2255/2721/2222 -f 2259/2727/2223 2260/2728/2223 2255/2721/2223 -f 2255/2721/2224 2260/2728/2224 2256/2722/2224 -f 2256/2722/2225 2260/2728/2225 2261/2729/2225 -f 2256/2722/2226 2261/2729/2226 2225/2724/2226 -f 2225/2724/2227 2261/2729/2227 2226/2730/2227 -f 2233/2708/2228 2262/2710/2228 2257/2709/2228 -f 2262/2710/2229 2263/2731/2229 2257/2709/2229 -f 2257/2709/2230 2263/2731/2230 2258/2726/2230 -f 2263/2731/2231 2264/2732/2231 2258/2726/2231 -f 2258/2726/2232 2264/2732/2232 2259/2727/2232 -f 2264/2732/2233 2265/2733/2233 2259/2727/2233 -f 2259/2727/2234 2265/2733/2234 2260/2728/2234 -f 2265/2733/2235 2266/2734/2235 2260/2728/2235 -f 2260/2728/2236 2266/2734/2236 2261/2729/2236 -f 2261/2729/2237 2266/2734/2237 2267/2735/2237 -f 2261/2729/2238 2267/2735/2238 2226/2730/2238 -f 2226/2730/2239 2267/2735/2239 2227/2736/2239 -f 2234/2664/2240 2221/2663/2240 2262/2710/2240 -f 2221/2663/2241 2220/2737/2241 2262/2710/2241 -f 2262/2710/2242 2220/2737/2242 2263/2731/2242 -f 2220/2737/2243 2219/2738/2243 2263/2731/2243 -f 2263/2731/2244 2219/2738/2244 2264/2732/2244 -f 2219/2738/2245 2218/2739/2245 2264/2732/2245 -f 2264/2732/2246 2218/2739/2246 2265/2733/2246 -f 2218/2739/2247 2217/2740/2247 2265/2733/2247 -f 2265/2733/2248 2217/2740/2248 2266/2734/2248 -f 2266/2734/2249 2217/2740/2249 2216/2741/2249 -f 2266/2734/2250 2216/2741/2250 2267/2735/2250 -f 2267/2735/2251 2216/2741/2251 2215/2742/2251 -f 2267/2735/2252 2215/2742/2252 2227/2736/2252 -f 2227/2736/2253 2215/2742/2253 2158/2660/2253 -f 2578/2743/2254 2555/2744/2254 2507/2745/2254 -f 2435/2746/2255 2225/2747/2255 2443/2748/2255 -f 2576/2749/2256 2535/2750/2256 2574/2751/2256 -f 2543/2752/2257 2545/2753/2257 2502/2754/2257 -f 2226/2755/2258 2227/2659/2258 2268/2756/2258 -f 2443/2748/2259 2225/2747/2259 2442/2757/2259 -f 2446/2758/2260 2226/2755/2260 2268/2756/2260 2445/2759/2260 -f 2268/2756/2261 2432/2760/2261 2445/2759/2261 -f 2268/2756/2262 2227/2659/2262 2214/2661/2262 -f 2268/2756/2263 2214/2661/2263 2213/2761/2263 -f 2213/2761/2264 2423/2762/2264 2433/2763/2264 -f 2269/2764/2265 2397/2765/2265 2396/2766/2265 -f 2269/2764/2167 2398/2767/2167 2397/2765/2167 -f 2224/2692/2266 2391/2691/2266 2270/2768/2266 -f 2271/2769/2267 2409/2770/2267 2681/2771/2267 2408/2772/2267 -f 2271/2769/2268 2410/2773/2268 2409/2770/2268 -f 2273/2774/2269 2269/2764/2269 2272/2775/2269 -f 2270/2768/2270 2269/2764/2270 2273/2774/2270 -f 2273/2774/2271 2274/2776/2271 2270/2768/2271 -f 2270/2768/2272 2274/2776/2272 2224/2692/2272 -f 2274/2776/2273 2223/2693/2273 2224/2692/2273 -f 2459/2777/2274 2271/2769/2274 2408/2772/2274 2464/2778/2274 -f 2275/2779/2275 2271/2769/2275 2459/2777/2275 2469/2780/2275 2468/2781/2275 -f 2272/2775/2276 2271/2769/2276 2275/2779/2276 -f 2276/2782/2277 2272/2775/2277 2275/2779/2277 -f 2272/2775/2278 2276/2782/2278 2273/2774/2278 -f 2276/2782/2279 2277/2783/2279 2273/2774/2279 -f 2273/2774/2280 2277/2783/2280 2274/2776/2280 -f 2277/2783/2281 2278/2784/2281 2274/2776/2281 -f 2274/2776/2282 2278/2784/2282 2223/2693/2282 -f 2278/2784/2283 2222/2694/2283 2223/2693/2283 -f 2468/2781/2284 2472/2785/2284 2275/2779/2284 -f 2205/2786/2285 2275/2779/2285 2472/2785/2285 2476/2787/2285 2467/2788/2285 -f 2276/2782/2286 2275/2779/2286 2205/2786/2286 -f 2204/2789/2287 2276/2782/2287 2205/2786/2287 -f 2276/2782/2288 2204/2789/2288 2277/2783/2288 -f 2204/2789/2289 2203/2790/2289 2277/2783/2289 -f 2277/2783/2290 2203/2790/2290 2278/2784/2290 -f 2203/2790/2291 2202/2791/2291 2278/2784/2291 -f 2278/2784/2292 2202/2791/2292 2222/2694/2292 -f 2202/2791/2293 2159/2695/2293 2222/2694/2293 -f 2212/2792/2294 2221/2663/2294 2159/2662/2294 -f 2211/2793/2295 2279/2794/2295 2212/2792/2295 -f 2210/2795/2296 2280/2796/2296 2211/2793/2296 -f 2210/2795/2297 2209/2797/2297 2282/2798/2297 -f 2209/2797/2298 2208/2799/2298 2285/2800/2298 -f 2208/2799/2299 2207/2801/2299 2289/2802/2299 -f 2207/2801/2300 2206/2658/2300 2294/2803/2300 -f 2212/2792/2301 2279/2794/2301 2221/2663/2301 -f 2279/2794/2302 2220/2737/2302 2221/2663/2302 -f 2211/2793/2303 2280/2796/2303 2279/2794/2303 -f 2280/2796/2304 2281/2804/2304 2279/2794/2304 -f 2279/2794/2305 2281/2804/2305 2220/2737/2305 -f 2281/2804/2306 2219/2738/2306 2220/2737/2306 -f 2210/2795/2307 2282/2798/2307 2280/2796/2307 -f 2282/2798/2308 2283/2805/2308 2280/2796/2308 -f 2280/2796/2309 2283/2805/2309 2281/2804/2309 -f 2283/2805/2310 2284/2806/2310 2281/2804/2310 -f 2281/2804/2311 2284/2806/2311 2219/2738/2311 -f 2284/2806/2312 2218/2739/2312 2219/2738/2312 -f 2282/2798/2313 2209/2797/2313 2285/2800/2313 -f 2282/2798/2314 2285/2800/2314 2286/2807/2314 -f 2282/2798/2315 2286/2807/2315 2283/2805/2315 -f 2286/2807/2316 2287/2808/2316 2283/2805/2316 -f 2283/2805/2317 2287/2808/2317 2284/2806/2317 -f 2287/2808/2318 2288/2809/2318 2284/2806/2318 -f 2284/2806/2319 2288/2809/2319 2218/2739/2319 -f 2288/2809/2320 2217/2740/2320 2218/2739/2320 -f 2285/2800/2321 2208/2799/2321 2289/2802/2321 -f 2285/2800/2322 2289/2802/2322 2290/2810/2322 -f 2286/2807/2323 2285/2800/2323 2290/2810/2323 -f 2286/2807/2324 2290/2810/2324 2291/2811/2324 -f 2286/2807/2325 2291/2811/2325 2287/2808/2325 -f 2291/2811/2326 2292/2812/2326 2287/2808/2326 -f 2287/2808/2327 2292/2812/2327 2288/2809/2327 -f 2292/2812/2328 2293/2813/2328 2288/2809/2328 -f 2288/2809/2329 2293/2813/2329 2217/2740/2329 -f 2217/2740/2330 2293/2813/2330 2216/2741/2330 -f 2289/2802/2331 2207/2801/2331 2294/2803/2331 -f 2289/2802/2332 2294/2803/2332 2295/2814/2332 -f 2290/2810/2333 2289/2802/2333 2295/2814/2333 -f 2290/2810/2334 2295/2814/2334 2296/2815/2334 -f 2291/2811/2335 2290/2810/2335 2296/2815/2335 -f 2291/2811/2336 2296/2815/2336 2297/2816/2336 -f 2291/2811/2337 2297/2816/2337 2292/2812/2337 -f 2292/2812/2338 2297/2816/2338 2298/2817/2338 -f 2293/2813/2339 2292/2812/2339 2298/2817/2339 -f 2293/2813/2340 2298/2817/2340 2299/2818/2340 -f 2216/2741/2341 2293/2813/2341 2299/2818/2341 -f 2216/2741/2342 2299/2818/2342 2215/2742/2342 -f 2294/2803/2343 2206/2658/2343 2194/2654/2343 -f 2294/2803/2344 2194/2654/2344 2193/2819/2344 -f 2295/2814/2345 2294/2803/2345 2193/2819/2345 -f 2295/2814/2346 2193/2819/2346 2192/2820/2346 -f 2296/2815/2347 2295/2814/2347 2192/2820/2347 -f 2296/2815/2348 2192/2820/2348 2191/2821/2348 -f 2297/2816/2349 2296/2815/2349 2191/2821/2349 -f 2297/2816/2350 2191/2821/2350 2190/2822/2350 -f 2298/2817/2351 2297/2816/2351 2190/2822/2351 -f 2298/2817/2352 2190/2822/2352 2189/2823/2352 -f 2299/2818/2353 2298/2817/2353 2189/2823/2353 -f 2299/2818/2354 2189/2823/2354 2188/2824/2354 -f 2215/2742/2355 2299/2818/2355 2188/2824/2355 -f 2215/2742/2356 2188/2824/2356 2158/2660/2356 -f 2158/2660/2357 2201/2825/2357 2214/2661/2357 -f 2201/2825/2358 2200/2826/2358 2300/2827/2358 -f 2200/2826/2359 2199/2828/2359 2301/2829/2359 -f 2199/2828/2360 2198/2830/2360 2303/2831/2360 -f 2198/2830/2361 2197/2832/2361 2305/2833/2361 -f 2196/2834/2362 2307/2835/2362 2197/2832/2362 -f 2195/2657/2363 2310/2836/2363 2196/2834/2363 -f 2214/2661/2364 2201/2825/2364 2300/2827/2364 -f 2214/2661/2365 2300/2827/2365 2213/2761/2365 -f 2300/2827/2366 2200/2826/2366 2301/2829/2366 -f 2300/2827/2367 2301/2829/2367 2302/2837/2367 -f 2213/2761/2368 2300/2827/2368 2302/2837/2368 -f 2301/2829/2369 2199/2828/2369 2303/2831/2369 -f 2301/2829/2370 2303/2831/2370 2304/2838/2370 -f 2302/2837/2371 2301/2829/2371 2304/2838/2371 -f 2429/2839/2372 2302/2837/2372 2304/2838/2372 2428/2840/2372 -f 2424/2841/2373 2302/2837/2373 2429/2839/2373 -f 2303/2831/2374 2198/2830/2374 2305/2833/2374 -f 2303/2831/2375 2305/2833/2375 2306/2842/2375 -f 2304/2838/2376 2303/2831/2376 2306/2842/2376 -f 2419/2843/2377 2439/2844/2377 2304/2838/2377 2306/2842/2377 -f 2428/2840/2378 2304/2838/2378 2439/2844/2378 -f 2543/2752/2379 2502/2754/2379 2544/2845/2379 -f 2305/2833/2380 2197/2832/2380 2307/2835/2380 -f 2305/2833/2381 2307/2835/2381 2308/2846/2381 -f 2306/2842/2382 2305/2833/2382 2308/2846/2382 -f 2306/2842/2383 2308/2846/2383 2309/2847/2383 -f 2309/2847/2384 2427/2848/2384 2421/2849/2384 -f 2196/2834/2385 2310/2836/2385 2307/2835/2385 -f 2310/2836/2386 2311/2850/2386 2307/2835/2386 -f 2308/2846/2387 2307/2835/2387 2311/2850/2387 -f 2308/2846/2388 2311/2850/2388 2312/2851/2388 -f 2309/2847/2389 2308/2846/2389 2312/2851/2389 -f 2440/2852/2390 2309/2847/2390 2312/2851/2390 2441/2853/2390 -f 2427/2848/2391 2309/2847/2391 2440/2852/2391 -f 2552/2854/2392 2554/2855/2392 2510/2856/2392 -f 2195/2657/2393 2187/2653/2393 2310/2836/2393 -f 2187/2653/2394 2186/2857/2394 2310/2836/2394 -f 2310/2836/2395 2186/2857/2395 2311/2850/2395 -f 2186/2857/2396 2185/2858/2396 2311/2850/2396 -f 2312/2851/2397 2311/2850/2397 2185/2858/2397 -f 2312/2851/2398 2185/2858/2398 2184/2859/2398 -f 2558/2860/2399 2565/2861/2399 2512/2862/2399 -f 2184/2859/2400 2434/2863/2400 2447/2864/2400 -f 2202/2791/2401 2212/2865/2401 2159/2695/2401 -f 2203/2790/2402 2313/2866/2402 2202/2791/2402 -f 2204/2789/2403 2314/2867/2403 2203/2790/2403 -f 2204/2789/2404 2205/2786/2404 2316/2868/2404 -f 2467/2788/2405 2466/2869/2405 2205/2786/2405 -f 2202/2791/2406 2313/2866/2406 2212/2865/2406 -f 2313/2866/2407 2211/2870/2407 2212/2865/2407 -f 2203/2790/2408 2314/2867/2408 2313/2866/2408 -f 2314/2867/2409 2315/2871/2409 2313/2866/2409 -f 2313/2866/2410 2315/2871/2410 2211/2870/2410 -f 2315/2871/2411 2210/2872/2411 2211/2870/2411 -f 2314/2867/2412 2204/2789/2412 2316/2868/2412 -f 2314/2867/2413 2316/2868/2413 2317/2873/2413 -f 2315/2871/2414 2314/2867/2414 2317/2873/2414 -f 2315/2871/2415 2317/2873/2415 2318/2874/2415 -f 2210/2872/2416 2315/2871/2416 2318/2874/2416 -f 2210/2872/2417 2318/2874/2417 2209/2875/2417 -f 2316/2868/2418 2205/2786/2418 2466/2869/2418 2479/2876/2418 2478/2877/2418 -f 2319/2878/2419 2316/2868/2419 2478/2877/2419 2483/2879/2419 -f 2317/2873/2420 2316/2868/2420 2319/2878/2420 -f 2317/2873/2421 2319/2878/2421 2320/2880/2421 -f 2318/2874/2422 2317/2873/2422 2320/2880/2422 -f 2318/2874/2423 2320/2880/2423 2321/2881/2423 -f 2318/2874/2424 2321/2881/2424 2209/2875/2424 -f 2209/2875/2425 2321/2881/2425 2208/2882/2425 -f 2319/2878/2426 2483/2879/2426 2485/2883/2426 2484/2884/2426 -f 2484/2884/2427 2488/2885/2427 2319/2878/2427 -f 2320/2880/2428 2319/2878/2428 2488/2885/2428 2491/2886/2428 2490/2887/2428 -f 2456/2888/2429 2322/2889/2429 2320/2880/2429 2490/2887/2429 -f 2320/2880/2430 2322/2889/2430 2321/2881/2430 -f 2321/2881/2431 2322/2889/2431 2323/2890/2431 -f 2321/2881/2432 2323/2890/2432 2208/2882/2432 -f 2208/2882/2433 2323/2890/2433 2207/2891/2433 -f 2322/2889/2434 2456/2888/2434 2455/2892/2434 2454/2893/2434 -f 2458/2894/2435 2324/2895/2435 2322/2889/2435 2454/2893/2435 -f 2322/2889/2436 2324/2895/2436 2323/2890/2436 -f 2323/2890/2437 2324/2895/2437 2325/2896/2437 -f 2323/2890/2438 2325/2896/2438 2207/2891/2438 -f 2207/2891/2439 2325/2896/2439 2206/2897/2439 -f 2471/2898/2440 2324/2895/2440 2458/2894/2440 -f 2570/2899/2441 2573/2900/2441 2513/2901/2441 -f 2477/2902/2442 2465/2903/2442 2183/2904/2442 2324/2895/2442 2471/2898/2442 -f 2324/2895/2443 2183/2904/2443 2325/2896/2443 -f 2325/2896/2444 2183/2904/2444 2182/2905/2444 -f 2325/2896/2445 2182/2905/2445 2206/2897/2445 -f 2206/2897/2446 2182/2905/2446 2157/2906/2446 -f 2158/2660/2447 2188/2824/2447 2201/2825/2447 -f 2188/2824/2448 2189/2823/2448 2326/2907/2448 -f 2189/2823/2449 2190/2822/2449 2327/2908/2449 -f 2190/2822/2450 2191/2821/2450 2329/2909/2450 -f 2191/2821/2451 2192/2820/2451 2332/2910/2451 -f 2192/2820/2452 2193/2819/2452 2336/2911/2452 -f 2193/2819/2453 2194/2654/2453 2341/2912/2453 -f 2188/2824/2454 2326/2907/2454 2201/2825/2454 -f 2201/2825/2455 2326/2907/2455 2200/2826/2455 -f 2189/2823/2456 2327/2908/2456 2326/2907/2456 -f 2326/2907/2457 2327/2908/2457 2328/2913/2457 -f 2326/2907/2458 2328/2913/2458 2200/2826/2458 -f 2200/2826/2459 2328/2913/2459 2199/2828/2459 -f 2190/2822/2460 2329/2909/2460 2327/2908/2460 -f 2327/2908/2461 2329/2909/2461 2330/2914/2461 -f 2327/2908/2462 2330/2914/2462 2328/2913/2462 -f 2328/2913/2463 2330/2914/2463 2331/2915/2463 -f 2328/2913/2464 2331/2915/2464 2199/2828/2464 -f 2199/2828/2465 2331/2915/2465 2198/2830/2465 -f 2191/2821/2466 2332/2910/2466 2329/2909/2466 -f 2329/2909/2467 2332/2910/2467 2333/2916/2467 -f 2329/2909/2468 2333/2916/2468 2330/2914/2468 -f 2330/2914/2469 2333/2916/2469 2334/2917/2469 -f 2330/2914/2470 2334/2917/2470 2331/2915/2470 -f 2331/2915/2471 2334/2917/2471 2335/2918/2471 -f 2331/2915/2472 2335/2918/2472 2198/2830/2472 -f 2198/2830/2473 2335/2918/2473 2197/2832/2473 -f 2192/2820/2474 2336/2911/2474 2332/2910/2474 -f 2332/2910/2475 2336/2911/2475 2337/2919/2475 -f 2332/2910/2476 2337/2919/2476 2333/2916/2476 -f 2337/2919/2477 2338/2920/2477 2333/2916/2477 -f 2333/2916/2478 2338/2920/2478 2334/2917/2478 -f 2338/2920/2479 2339/2921/2479 2334/2917/2479 -f 2334/2917/2480 2339/2921/2480 2335/2918/2480 -f 2339/2921/2481 2340/2922/2481 2335/2918/2481 -f 2335/2918/2482 2340/2922/2482 2197/2832/2482 -f 2340/2922/2483 2196/2834/2483 2197/2832/2483 -f 2193/2819/2484 2341/2912/2484 2336/2911/2484 -f 2336/2911/2485 2341/2912/2485 2342/2923/2485 -f 2336/2911/2486 2342/2923/2486 2337/2919/2486 -f 2342/2923/2487 2343/2924/2487 2337/2919/2487 -f 2337/2919/2488 2343/2924/2488 2338/2920/2488 -f 2343/2924/2489 2344/2925/2489 2338/2920/2489 -f 2338/2920/2490 2344/2925/2490 2339/2921/2490 -f 2344/2925/2491 2345/2926/2491 2339/2921/2491 -f 2339/2921/2492 2345/2926/2492 2340/2922/2492 -f 2345/2926/2493 2346/2927/2493 2340/2922/2493 -f 2340/2922/2494 2346/2927/2494 2196/2834/2494 -f 2346/2927/2495 2195/2657/2495 2196/2834/2495 -f 2194/2654/2496 2177/2656/2496 2341/2912/2496 -f 2341/2912/2497 2177/2656/2497 2176/2928/2497 -f 2341/2912/2498 2176/2928/2498 2342/2923/2498 -f 2342/2923/2499 2176/2928/2499 2175/2929/2499 -f 2342/2923/2500 2175/2929/2500 2343/2924/2500 -f 2175/2929/2501 2174/2930/2501 2343/2924/2501 -f 2343/2924/2502 2174/2930/2502 2344/2925/2502 -f 2174/2930/2503 2173/2931/2503 2344/2925/2503 -f 2344/2925/2504 2173/2931/2504 2345/2926/2504 -f 2173/2931/2505 2172/2932/2505 2345/2926/2505 -f 2345/2926/2506 2172/2932/2506 2346/2927/2506 -f 2172/2932/2507 2171/2933/2507 2346/2927/2507 -f 2346/2927/2508 2171/2933/2508 2195/2657/2508 -f 2171/2933/2509 2156/2650/2509 2195/2657/2509 -f 2566/2934/2510 2571/2935/2510 2534/2936/2510 -f 2184/2859/2511 2185/2858/2511 2348/2937/2511 -f 2186/2857/2512 2350/2938/2512 2185/2858/2512 -f 2187/2653/2513 2353/2939/2513 2186/2857/2513 -f 2347/2940/2514 2418/2941/2514 2438/2942/2514 -f 2418/2941/2515 2347/2940/2515 2420/2943/2515 -f 2184/2859/2516 2348/2937/2516 2347/2940/2516 -f 2347/2940/2517 2348/2937/2517 2349/2944/2517 -f 2347/2940/2518 2349/2944/2518 2437/2945/2518 2420/2943/2518 -f 2564/2946/2519 2569/2947/2519 2511/2948/2519 -f 2185/2858/2520 2350/2938/2520 2348/2937/2520 -f 2350/2938/2521 2351/2949/2521 2348/2937/2521 -f 2348/2937/2522 2351/2949/2522 2349/2944/2522 -f 2349/2944/2523 2351/2949/2523 2352/2950/2523 -f 2404/2951/2524 2402/2952/2524 2352/2950/2524 -f 2186/2857/2525 2353/2939/2525 2350/2938/2525 -f 2353/2939/2526 2354/2953/2526 2350/2938/2526 -f 2350/2938/2527 2354/2953/2527 2351/2949/2527 -f 2354/2953/2528 2355/2954/2528 2351/2949/2528 -f 2351/2949/2529 2355/2954/2529 2352/2950/2529 -f 2352/2950/2524 2407/2955/2524 2404/2951/2524 -f 2187/2653/2530 2181/2652/2530 2353/2939/2530 -f 2181/2652/2531 2180/2956/2531 2353/2939/2531 -f 2353/2939/2532 2180/2956/2532 2354/2953/2532 -f 2180/2956/2533 2179/2957/2533 2354/2953/2533 -f 2354/2953/2534 2179/2957/2534 2355/2954/2534 -f 2355/2954/2535 2179/2957/2535 2178/2958/2535 -f 2460/2959/2536 2183/2904/2536 2465/2903/2536 -f 2452/2960/2537 2356/2961/2537 2453/2962/2537 -f 2457/2963/2538 2453/2962/2538 2356/2961/2538 2183/2904/2538 2460/2959/2538 -f 2182/2905/2539 2183/2904/2539 2356/2961/2539 -f 2163/2964/2540 2473/2965/2540 2474/2966/2540 2475/2967/2540 -f 2473/2965/2541 2163/2964/2541 2480/2968/2541 -f 2480/2968/2542 2163/2964/2542 2162/2969/2542 2481/2970/2542 2482/2971/2542 -f 2481/2970/2543 2162/2969/2543 2356/2961/2543 2452/2960/2543 -f 2161/2972/2544 2356/2961/2544 2162/2969/2544 -f 2182/2905/2545 2356/2961/2545 2161/2972/2545 -f 2157/2906/2546 2182/2905/2546 2161/2972/2546 -f 2179/2957/2547 2357/2973/2547 2178/2958/2547 -f 2180/2956/2548 2359/2974/2548 2179/2957/2548 -f 2181/2652/2549 2363/2975/2549 2180/2956/2549 -f 2358/2976/2550 2394/2977/2550 2393/2978/2550 -f 2395/2979/2550 2394/2977/2550 2358/2976/2550 -f 2179/2957/2551 2359/2974/2551 2357/2973/2551 -f 2359/2974/2552 2360/2980/2552 2357/2973/2552 -f 2357/2973/2553 2360/2980/2553 2358/2976/2553 -f 2361/2981/2554 2358/2976/2554 2360/2980/2554 -f 2403/2982/2555 2401/2983/2555 2362/2984/2555 -f 2403/2982/2556 2362/2984/2556 2405/2985/2556 -f 2180/2956/2557 2363/2975/2557 2359/2974/2557 -f 2363/2975/2558 2364/2986/2558 2359/2974/2558 -f 2359/2974/2559 2364/2986/2559 2360/2980/2559 -f 2364/2986/2560 2365/2987/2560 2360/2980/2560 -f 2360/2980/2561 2365/2987/2561 2361/2981/2561 -f 2365/2987/2562 2366/2988/2562 2361/2981/2562 -f 2362/2984/2563 2361/2981/2563 2366/2988/2563 -f 2367/2667/2564 2362/2984/2564 2366/2988/2564 -f 2412/2989/2565 2411/2990/2565 2368/2668/2565 -f 2412/2989/2566 2368/2668/2566 2413/2991/2566 -f 2181/2652/2567 2170/2651/2567 2363/2975/2567 -f 2170/2651/2568 2169/2992/2568 2363/2975/2568 -f 2363/2975/2569 2169/2992/2569 2364/2986/2569 -f 2169/2992/2570 2168/2993/2570 2364/2986/2570 -f 2364/2986/2571 2168/2993/2571 2365/2987/2571 -f 2168/2993/2572 2167/2994/2572 2365/2987/2572 -f 2365/2987/2573 2167/2994/2573 2366/2988/2573 -f 2167/2994/2574 2166/2995/2574 2366/2988/2574 -f 2366/2988/2575 2166/2995/2575 2367/2667/2575 -f 2166/2995/2576 2165/2996/2576 2367/2667/2576 -f 2368/2668/2577 2367/2667/2577 2165/2996/2577 -f 2164/2997/2578 2368/2668/2578 2165/2996/2578 -f 2157/2655/2579 2161/2998/2579 2177/2656/2579 -f 2161/2998/2580 2162/2999/2580 2369/3000/2580 -f 2163/3001/2581 2370/3002/2581 2162/2999/2581 -f 2372/3003/2582 2163/3001/2582 2475/3004/2582 2486/3005/2582 -f 2375/3006/2583 2489/3007/2583 2449/3008/2583 -f 2461/3009/2584 2379/3010/2584 2451/3011/2584 -f 2470/3012/2585 2384/3013/2585 2463/3014/2585 -f 2470/3012/2586 2463/3014/2586 2487/3015/2586 -f 2177/2656/2587 2161/2998/2587 2369/3000/2587 -f 2177/2656/2588 2369/3000/2588 2176/2928/2588 -f 2369/3000/2589 2162/2999/2589 2370/3002/2589 -f 2369/3000/2590 2370/3002/2590 2371/3016/2590 -f 2176/2928/2591 2369/3000/2591 2371/3016/2591 -f 2176/2928/2592 2371/3016/2592 2175/2929/2592 -f 2163/3001/2593 2372/3003/2593 2370/3002/2593 -f 2372/3003/2594 2373/3017/2594 2370/3002/2594 -f 2371/3016/2595 2370/3002/2595 2373/3017/2595 -f 2373/3017/2596 2374/3018/2596 2371/3016/2596 -f 2371/3016/2597 2374/3018/2597 2175/2929/2597 -f 2374/3018/2598 2174/2930/2598 2175/2929/2598 -f 2489/3007/2599 2375/3006/2599 2372/3003/2599 2486/3005/2599 2492/3019/2599 -f 2375/3006/2600 2376/3020/2600 2372/3003/2600 -f 2372/3003/2601 2376/3020/2601 2373/3017/2601 -f 2376/3020/2602 2377/3021/2602 2373/3017/2602 -f 2373/3017/2603 2377/3021/2603 2374/3018/2603 -f 2377/3021/2604 2378/3022/2604 2374/3018/2604 -f 2374/3018/2605 2378/3022/2605 2174/2930/2605 -f 2378/3022/2606 2173/2931/2606 2174/2930/2606 -f 2451/3011/2607 2379/3010/2607 2375/3006/2607 2449/3008/2607 2450/3023/2607 -f 2379/3010/2608 2380/3024/2608 2375/3006/2608 -f 2375/3006/2609 2380/3024/2609 2376/3020/2609 -f 2380/3024/2610 2381/3025/2610 2376/3020/2610 -f 2376/3020/2611 2381/3025/2611 2377/3021/2611 -f 2381/3025/2612 2382/3026/2612 2377/3021/2612 -f 2377/3021/2613 2382/3026/2613 2378/3022/2613 -f 2382/3026/2614 2383/3027/2614 2378/3022/2614 -f 2378/3022/2615 2383/3027/2615 2173/2931/2615 -f 2383/3027/2616 2172/2932/2616 2173/2931/2616 -f 2463/3014/2617 2384/3013/2617 2379/3010/2617 2461/3009/2617 2462/3028/2617 -f 2384/3013/2618 2385/3029/2618 2379/3010/2618 -f 2379/3010/2619 2385/3029/2619 2380/3024/2619 -f 2385/3029/2620 2386/3030/2620 2380/3024/2620 -f 2380/3024/2621 2386/3030/2621 2381/3025/2621 -f 2386/3030/2622 2387/3031/2622 2381/3025/2622 -f 2381/3025/2623 2387/3031/2623 2382/3026/2623 -f 2387/3031/2624 2388/3032/2624 2382/3026/2624 -f 2382/3026/2625 2388/3032/2625 2383/3027/2625 -f 2388/3032/2626 2389/3033/2626 2383/3027/2626 -f 2383/3027/2627 2389/3033/2627 2172/2932/2627 -f 2389/3033/2628 2171/2933/2628 2172/2932/2628 -f 2164/2645/2629 2384/3013/2629 2470/3012/2629 2448/2646/2629 -f 2164/2645/2630 2165/3034/2630 2384/3013/2630 -f 2384/3013/2631 2165/3034/2631 2385/3029/2631 -f 2165/3034/2632 2166/3035/2632 2385/3029/2632 -f 2385/3029/2633 2166/3035/2633 2386/3030/2633 -f 2166/3035/2634 2167/3036/2634 2386/3030/2634 -f 2386/3030/2635 2167/3036/2635 2387/3031/2635 -f 2167/3036/2636 2168/3037/2636 2387/3031/2636 -f 2387/3031/2637 2168/3037/2637 2388/3032/2637 -f 2168/3037/2638 2169/3038/2638 2388/3032/2638 -f 2388/3032/2639 2169/3038/2639 2389/3033/2639 -f 2169/3038/2640 2170/3039/2640 2389/3033/2640 -f 2389/3033/2641 2170/3039/2641 2171/2933/2641 -f 2170/3039/2642 2156/2650/2642 2171/2933/2642 -f 2164/2645/2643 2155/2648/2643 2494/2647/2643 -f 2493/3040/2644 2506/3041/2644 2500/3042/2644 -f 2531/2665/2645 2368/2668/2645 2164/2645/2645 2494/3043/2645 -f 2509/3044/2646 2506/3041/2646 2493/3040/2646 -f 2510/2856/2647 2509/3044/2647 2493/3040/2647 -f 2510/2856/2648 2554/2855/2648 2553/3045/2648 2511/2948/2648 -f 2650/3046/2649 2652/3047/2649 2596/3048/2649 -f 2511/2948/2650 2509/3044/2650 2510/2856/2650 -f 2511/2948/2651 2508/3049/2651 2509/3044/2651 -f 2569/2947/2652 2575/3050/2652 2508/3049/2652 2511/2948/2652 -f 2667/3051/2653 2620/3052/2653 2662/3053/2653 -f 2507/2745/2654 2577/3054/2654 2578/2743/2654 -f 2654/3055/2655 2658/3056/2655 2619/3057/2655 -f 2441/2853/2656 2312/2851/2656 2184/2859/2656 2447/2864/2656 -f 2438/2942/2657 2434/2863/2657 2184/2859/2657 2347/2940/2657 -f 2545/2753/2658 2549/3058/2658 2501/3059/2658 2502/2754/2658 -f 2501/3059/2659 2549/3058/2659 2552/2854/2659 2510/2856/2659 -f 2510/2856/2660 2493/3040/2660 2501/3059/2660 -f 2555/2744/2661 2558/2860/2661 2512/2862/2661 2507/2745/2661 -f 2507/2745/2662 2514/3060/2662 2508/3049/2662 -f 2508/3049/2663 2517/3061/2663 2509/3044/2663 -f 2659/3062/2664 2665/3063/2664 2597/3064/2664 -f 2565/2861/2665 2570/2899/2665 2513/2901/2665 2512/2862/2665 -f 2643/3065/2666 2642/3066/2666 2588/3067/2666 -f 2507/2745/2667 2512/2862/2667 2514/3060/2667 -f 2512/2862/2668 2515/3068/2668 2514/3060/2668 -f 2512/2862/2669 2513/2901/2669 2515/3068/2669 -f 2513/2901/2670 2516/3069/2670 2515/3068/2670 -f 2402/2952/2524 2404/2951/2524 2516/3069/2524 -f 2508/3049/2671 2514/3060/2671 2517/3061/2671 -f 2514/3060/2672 2518/3070/2672 2517/3061/2672 -f 2514/3060/2673 2515/3068/2673 2518/3070/2673 -f 2515/3068/2674 2519/3071/2674 2518/3070/2674 -f 2516/3069/2675 2519/3071/2675 2515/3068/2675 -f 2516/3069/2524 2404/2951/2524 2407/2955/2524 -f 2509/3044/2676 2517/3061/2676 2506/3041/2676 -f 2517/3061/2677 2505/3072/2677 2506/3041/2677 -f 2517/3061/2678 2518/3070/2678 2505/3072/2678 -f 2518/3070/2679 2504/3073/2679 2505/3072/2679 -f 2518/3070/2680 2519/3071/2680 2504/3073/2680 -f 2519/3071/2681 2503/3074/2681 2504/3073/2681 -f 2503/3074/2682 2520/3075/2682 2504/3073/2682 -f 2505/3072/2683 2504/3073/2683 2522/3076/2683 -f 2506/3041/2684 2505/3072/2684 2526/3077/2684 -f 2521/3078/2550 2393/2978/2550 2394/2977/2550 -f 2394/2977/2550 2395/2979/2550 2521/3078/2550 -f 2504/3073/2685 2520/3075/2685 2522/3076/2685 -f 2522/3076/2686 2520/3075/2686 2523/3079/2686 -f 2520/3075/2687 2521/3078/2687 2523/3079/2687 -f 2523/3079/2688 2521/3078/2688 2524/3080/2688 -f 2525/3081/2689 2401/2983/2689 2403/2982/2689 -f 2405/2985/2690 2525/3081/2690 2403/2982/2690 -f 2505/3072/2691 2522/3076/2691 2526/3077/2691 -f 2526/3077/2692 2522/3076/2692 2527/3082/2692 -f 2522/3076/2693 2523/3079/2693 2527/3082/2693 -f 2528/3083/2694 2527/3082/2694 2523/3079/2694 -f 2524/3080/2695 2528/3083/2695 2523/3079/2695 -f 2529/3084/2696 2528/3083/2696 2524/3080/2696 -f 2529/3084/2697 2524/3080/2697 2525/3081/2697 -f 2529/3084/2698 2525/3081/2698 2530/2666/2698 -f 2531/2665/2699 2411/2990/2699 2412/2989/2699 -f 2413/2991/2700 2531/2665/2700 2412/2989/2700 -f 2506/3041/2701 2526/3077/2701 2500/3042/2701 -f 2500/3042/2702 2526/3077/2702 2499/3085/2702 -f 2526/3077/2703 2527/3082/2703 2499/3085/2703 -f 2498/3086/2704 2499/3085/2704 2527/3082/2704 -f 2528/3083/2705 2498/3086/2705 2527/3082/2705 -f 2497/3087/2706 2498/3086/2706 2528/3083/2706 -f 2529/3084/2707 2497/3087/2707 2528/3083/2707 -f 2496/3088/2708 2497/3087/2708 2529/3084/2708 -f 2530/2666/2709 2496/3088/2709 2529/3084/2709 -f 2495/3089/2710 2496/3088/2710 2530/2666/2710 -f 2495/3089/2711 2530/2666/2711 2531/2665/2711 -f 2495/3089/2712 2531/2665/2712 2494/3043/2712 -f 2561/3090/2713 2560/3091/2713 2536/3092/2713 -f 2547/3093/2714 2548/3094/2714 2532/3095/2714 2546/3096/2714 -f 2548/3094/2715 2551/3097/2715 2532/3095/2715 -f 2557/3098/2716 2533/3099/2716 2532/3095/2716 2551/3097/2716 2556/3100/2716 -f 2562/3101/2717 2563/3102/2717 2533/3099/2717 2557/3098/2717 -f 2566/2934/2718 2534/2936/2718 2533/3099/2718 2563/3102/2718 -f 2645/3103/2719 2588/3067/2719 2642/3066/2719 -f 2574/2751/2720 2535/2750/2720 2534/2936/2720 2571/2935/2720 -f 2671/3104/2721 2674/3105/2721 2621/3106/2721 -f 2535/2750/2722 2576/2749/2722 2544/2845/2722 2502/2754/2722 -f 2559/3107/2723 2567/3108/2723 2536/3092/2723 2560/3091/2723 -f 2537/3109/2724 2536/3092/2724 2567/3108/2724 2568/3110/2724 -f 2546/3096/2725 2532/3095/2725 2537/3109/2725 2568/3110/2725 -f 2537/3109/2726 2532/3095/2726 2538/3111/2726 -f 2532/3095/2727 2533/3099/2727 2538/3111/2727 -f 2538/3111/2728 2533/3099/2728 2539/3112/2728 -f 2533/3099/2729 2534/2936/2729 2539/3112/2729 -f 2539/3112/2730 2534/2936/2730 2540/3113/2730 -f 2534/2936/2731 2535/2750/2731 2540/3113/2731 -f 2540/3113/2732 2535/2750/2732 2541/3114/2732 -f 2502/2754/2733 2541/3114/2733 2535/2750/2733 -f 2502/2754/2734 2501/3059/2734 2541/3114/2734 -f 2536/3092/2735 2494/2647/2735 2542/2649/2735 2550/3115/2735 2561/3090/2735 -f 2494/2647/2736 2536/3092/2736 2495/3116/2736 -f 2536/3092/2737 2537/3109/2737 2495/3116/2737 -f 2495/3116/2738 2537/3109/2738 2496/3117/2738 -f 2537/3109/2739 2538/3111/2739 2496/3117/2739 -f 2496/3117/2740 2538/3111/2740 2497/3118/2740 -f 2538/3111/2741 2539/3112/2741 2497/3118/2741 -f 2497/3118/2742 2539/3112/2742 2498/3119/2742 -f 2539/3112/2743 2540/3113/2743 2498/3119/2743 -f 2498/3119/2744 2540/3113/2744 2499/3120/2744 -f 2540/3113/2745 2541/3114/2745 2499/3120/2745 -f 2500/3121/2746 2499/3120/2746 2541/3114/2746 -f 2501/3059/2747 2500/3121/2747 2541/3114/2747 -f 2493/3040/2748 2500/3121/2748 2501/3059/2748 -f 2641/3122/2749 2580/3123/2749 2160/3124/2749 2640/3125/2749 -f 2579/3126/2750 2592/3127/2750 2586/3128/2750 -f 2575/3050/2751 2577/3054/2751 2507/2745/2751 2508/3049/2751 -f 2595/3129/2752 2592/3127/2752 2579/3126/2752 -f 2596/3048/2753 2595/3129/2753 2579/3126/2753 -f 2596/3048/2754 2652/3047/2754 2651/3130/2754 2597/3064/2754 -f 2659/3062/2755 2597/3064/2755 2651/3130/2755 -f 2432/2760/2756 2268/2756/2756 2213/2761/2756 2433/2763/2756 -f 2597/3064/2757 2595/3129/2757 2596/3048/2757 -f 2597/3064/2758 2594/3131/2758 2595/3129/2758 -f 2665/3063/2759 2672/3132/2759 2594/3131/2759 2597/3064/2759 -f 2594/3131/2760 2672/3132/2760 2675/3133/2760 2593/3134/2760 -f 2593/3134/2761 2675/3133/2761 2676/3135/2761 -f 2677/3136/2762 2653/3137/2762 2593/3134/2762 2676/3135/2762 -f 2446/2758/2763 2442/2757/2763 2225/2724/2763 2226/2755/2763 -f 2619/3057/2764 2618/3138/2764 2649/3139/2764 2654/3055/2764 -f 2646/3140/2765 2647/3141/2765 2618/3138/2765 -f 2645/3103/2766 2648/3142/2766 2587/3143/2766 2588/3067/2766 -f 2587/3143/2767 2648/3142/2767 2650/3046/2767 2596/3048/2767 -f 2596/3048/2768 2579/3126/2768 2587/3143/2768 -f 2653/3137/2769 2655/3144/2769 2598/3145/2769 2593/3134/2769 -f 2593/3134/2770 2600/3146/2770 2594/3131/2770 -f 2594/3131/2771 2603/3147/2771 2595/3129/2771 -f 2660/3148/2772 2661/3149/2772 2598/3145/2772 2655/3144/2772 -f 2661/3149/2773 2666/3150/2773 2599/3151/2773 2598/3145/2773 -f 2599/3151/2774 2666/3150/2774 2669/3152/2774 2670/3153/2774 -f 2593/3134/2775 2598/3145/2775 2600/3146/2775 -f 2598/3145/2776 2601/3154/2776 2600/3146/2776 -f 2598/3145/2777 2599/3151/2777 2601/3154/2777 -f 2602/3155/2778 2601/3154/2778 2599/3151/2778 -f 2634/3156/2779 2602/3155/2779 2632/3157/2779 -f 2594/3131/2780 2600/3146/2780 2603/3147/2780 -f 2600/3146/2781 2604/3158/2781 2603/3147/2781 -f 2600/3146/2782 2601/3154/2782 2604/3158/2782 -f 2601/3154/2783 2605/3159/2783 2604/3158/2783 -f 2605/3159/2784 2601/3154/2784 2602/3155/2784 -f 2602/3155/2779 2634/3156/2779 2636/3160/2779 -f 2595/3129/2785 2603/3147/2785 2592/3127/2785 -f 2603/3147/2786 2591/3161/2786 2592/3127/2786 -f 2603/3147/2787 2604/3158/2787 2591/3161/2787 -f 2604/3158/2788 2590/3162/2788 2591/3161/2788 -f 2604/3158/2789 2605/3159/2789 2590/3162/2789 -f 2589/3163/2790 2590/3162/2790 2605/3159/2790 -f 2606/3164/2791 2590/3162/2791 2589/3163/2791 -f 2591/3161/2792 2590/3162/2792 2608/3165/2792 -f 2592/3127/2793 2591/3161/2793 2612/3166/2793 -f 2629/3167/2167 2607/3168/2167 2628/3169/2167 -f 2630/3170/2794 2607/3168/2794 2629/3167/2794 2678/3171/2794 -f 2590/3162/2795 2606/3164/2795 2608/3165/2795 -f 2608/3165/2796 2606/3164/2796 2609/3172/2796 -f 2607/3168/2797 2609/3172/2797 2606/3164/2797 -f 2609/3172/2798 2607/3168/2798 2610/3173/2798 -f 2611/3174/2799 2631/3175/2799 2633/3176/2799 -f 2635/3177/2800 2611/3174/2800 2633/3176/2800 2680/3178/2800 -f 2591/3161/2801 2608/3165/2801 2612/3166/2801 -f 2612/3166/2802 2608/3165/2802 2613/3179/2802 -f 2608/3165/2803 2609/3172/2803 2613/3179/2803 -f 2614/3180/2804 2613/3179/2804 2609/3172/2804 -f 2610/3173/2805 2614/3180/2805 2609/3172/2805 -f 2615/3181/2806 2614/3180/2806 2610/3173/2806 -f 2610/3173/2807 2611/3174/2807 2615/3181/2807 -f 2615/3181/2808 2611/3174/2808 2616/3182/2808 -f 2617/3183/2809 2637/3184/2809 2638/3185/2809 -f 2639/3186/2810 2617/3183/2810 2638/3185/2810 2679/3187/2810 -f 2592/3127/2811 2612/3166/2811 2586/3128/2811 -f 2586/3128/2812 2612/3166/2812 2585/3188/2812 -f 2612/3166/2813 2613/3179/2813 2585/3188/2813 -f 2584/3189/2814 2585/3188/2814 2613/3179/2814 -f 2614/3180/2815 2584/3189/2815 2613/3179/2815 -f 2583/3190/2816 2584/3189/2816 2614/3180/2816 -f 2615/3181/2817 2583/3190/2817 2614/3180/2817 -f 2582/3191/2818 2583/3190/2818 2615/3181/2818 -f 2616/3182/2819 2582/3191/2819 2615/3181/2819 -f 2581/3192/2820 2582/3191/2820 2616/3182/2820 -f 2616/3182/2821 2617/3183/2821 2581/3192/2821 -f 2581/3192/2822 2617/3183/2822 2580/3193/2822 -f 2657/3194/2823 2656/3195/2823 2622/3196/2823 -f 2656/3195/2824 2663/3197/2824 2622/3196/2824 -f 2647/3141/2825 2649/3139/2825 2618/3138/2825 -f 2249/2713/2826 2425/2714/2826 2422/2712/2826 2247/2701/2826 -f 2431/2719/2827 2252/2717/2827 2256/2722/2827 2436/2723/2827 -f 2662/3053/2828 2620/3052/2828 2619/3057/2828 2658/3056/2828 -f 2424/2841/2829 2423/2762/2829 2213/2761/2829 2302/2837/2829 -f 2671/3104/2830 2621/3106/2830 2620/3052/2830 2667/3051/2830 -f 2419/2843/2831 2306/2842/2831 2309/2847/2831 2421/2849/2831 -f 2621/3106/2832 2674/3105/2832 2643/3065/2832 2588/3067/2832 -f 2430/2718/2833 2426/2715/2833 2249/2713/2833 2252/2717/2833 -f 2623/3198/2834 2622/3196/2834 2663/3197/2834 2664/3199/2834 -f 2646/3140/2835 2618/3138/2835 2623/3198/2835 2664/3199/2835 -f 2623/3198/2836 2618/3138/2836 2624/3200/2836 -f 2618/3138/2837 2619/3057/2837 2624/3200/2837 -f 2624/3200/2838 2619/3057/2838 2625/3201/2838 -f 2619/3057/2839 2620/3052/2839 2625/3201/2839 -f 2625/3201/2840 2620/3052/2840 2626/3202/2840 -f 2620/3052/2841 2621/3106/2841 2626/3202/2841 -f 2626/3202/2842 2621/3106/2842 2627/3203/2842 -f 2588/3067/2843 2627/3203/2843 2621/3106/2843 -f 2588/3067/2844 2587/3143/2844 2627/3203/2844 -f 2657/3194/2845 2622/3196/2845 2580/3123/2845 2641/3122/2845 -f 2580/3123/2846 2622/3196/2846 2581/3204/2846 -f 2622/3196/2847 2623/3198/2847 2581/3204/2847 -f 2581/3204/2848 2623/3198/2848 2582/3205/2848 -f 2623/3198/2849 2624/3200/2849 2582/3205/2849 -f 2582/3205/2850 2624/3200/2850 2583/3206/2850 -f 2624/3200/2851 2625/3201/2851 2583/3206/2851 -f 2583/3206/2852 2625/3201/2852 2584/3207/2852 -f 2625/3201/2853 2626/3202/2853 2584/3207/2853 -f 2584/3207/2854 2626/3202/2854 2585/3208/2854 -f 2626/3202/2855 2627/3203/2855 2585/3208/2855 -f 2586/3209/2856 2585/3208/2856 2627/3203/2856 -f 2587/3143/2857 2586/3209/2857 2627/3203/2857 -f 2579/3126/2858 2586/3209/2858 2587/3143/2858 -f 2530/2666/2859 2525/3081/2859 2362/2984/2859 2367/2667/2859 -f 2525/3081/2860 2524/3080/2860 2361/2981/2860 2362/2984/2860 -f 2524/3080/2861 2521/3078/2861 2358/2976/2861 2361/2981/2861 -f 2520/3075/2862 2357/2973/2862 2358/2976/2862 2521/3078/2862 -f 2357/2973/2863 2520/3075/2863 2503/3074/2863 2178/2958/2863 -f 2355/2954/2864 2178/2958/2864 2503/3074/2864 2519/3071/2864 -f 2355/2954/2865 2519/3071/2865 2516/3069/2865 2352/2950/2865 -f 2437/2945/2866 2349/2944/2866 2513/2901/2866 2573/2900/2866 -f 2352/2950/2867 2516/3069/2867 2513/2901/2867 2349/3210/2867 -f 2269/2764/2868 2602/3155/2868 2599/3151/2868 2272/2775/2868 -f 2271/2769/2869 2272/2775/2869 2599/3151/2869 2670/3153/2869 -f 2270/2768/2870 2605/3159/2870 2602/3155/2870 2269/2764/2870 -f 2589/3163/2871 2605/3159/2871 2270/2768/2871 2391/2691/2871 -f 2589/3163/2872 2391/2691/2872 2244/2687/2872 2606/3164/2872 -f 2606/3164/2873 2244/2687/2873 2241/2683/2873 2607/3168/2873 -f 2607/3168/2874 2241/2683/2874 2239/2680/2874 2610/3173/2874 -f 2237/2675/2875 2611/3174/2875 2610/3173/2875 2239/2680/2875 -f 2236/2672/2876 2616/3182/2876 2611/3174/2876 2237/2675/2876 -f 2235/2669/2877 2617/3183/2877 2616/3182/2877 2236/2672/2877 -f 2617/3183/2878 2235/2669/2878 2228/2671/2878 2580/3193/2878 -f 2580/3193/2879 2228/2671/2879 2160/2696/2879 -f 2553/3045/2880 2564/2946/2880 2511/2948/2880 -f 2466/2869/2881 2467/2788/2881 2732/3211/2881 2731/3212/2881 -f 2664/3199/2882 2663/3197/2882 2816/3213/2882 2817/3214/2882 -f 2438/2942/2883 2418/2941/2883 2686/3215/2883 2705/3216/2883 -f 2547/3093/2884 2546/3096/2884 2762/3217/2884 2763/3218/2884 -f 2473/2965/2885 2480/2968/2885 2745/3219/2885 2738/3220/2885 -f 2677/3136/2886 2676/3135/2886 2827/3221/2886 2828/3222/2886 -f 2545/2753/2887 2543/2752/2887 2759/3223/2887 2761/3224/2887 -f 2454/2893/2888 2455/2892/2888 2721/3225/2888 2720/3226/2888 -f 2468/2781/2889 2469/2780/2889 2734/3227/2889 2733/3228/2889 -f 2440/2852/2890 2441/2853/2890 2708/3229/2890 2707/3230/2890 -f 2479/2876/2891 2466/2869/2891 2731/3212/2891 2744/3231/2891 -f 2576/2749/2892 2574/2751/2892 2788/3232/2892 2791/3233/2892 -f 2482/2971/2893 2481/2970/2893 2746/3234/2893 2747/3235/2893 -f 2458/2894/2894 2454/2893/2894 2720/3226/2894 2724/3236/2894 -f 2553/3045/2895 2554/2855/2895 2770/3237/2895 2769/3238/2895 -f 2549/3058/2896 2545/2753/2896 2761/3224/2896 2765/3239/2896 -f 2447/2864/2897 2434/2863/2897 2702/3240/2897 2713/3241/2897 -f 2472/2785/2898 2468/2781/2898 2733/3228/2898 2737/3242/2898 -f 2155/2644/2899 2448/2646/2899 2714/3243/2899 2682/3244/2899 -f 2578/2743/2900 2577/3054/2900 2792/3245/2900 2793/3246/2900 -f 2675/3133/2901 2672/3132/2901 2824/3247/2901 2826/3248/2901 -f 2570/2899/2902 2565/2861/2902 2781/3249/2902 2786/3250/2902 -f 2450/3023/2903 2449/3008/2903 2715/3251/2903 2716/3252/2903 -f 2566/2934/2904 2563/3102/2904 2779/3253/2904 2782/3254/2904 -f 2416/2711/2905 2422/2712/2905 2690/3255/2905 2685/3256/2905 -f 2439/2844/2906 2419/2843/2906 2687/3257/2906 2706/3258/2906 -f 2564/2946/2907 2553/3045/2907 2769/3238/2907 2780/3259/2907 -f 2431/2719/2908 2436/2723/2908 2704/3260/2908 2699/3261/2908 -f 2481/2970/2909 2452/2960/2909 2718/3262/2909 2746/3234/2909 -f 2652/3047/2910 2650/3046/2910 2803/3263/2910 2805/3264/2910 -f 2641/3122/2911 2640/3125/2911 2794/3265/2911 2795/3266/2911 -f 2548/3094/2912 2547/3093/2912 2763/3218/2912 2764/3267/2912 -f 2460/2959/2913 2465/2903/2913 2730/3268/2913 2726/3269/2913 -f 2433/2763/2914 2423/2762/2914 2691/3270/2914 2701/3271/2914 -f 2420/2943/2915 2570/2899/2915 2786/3250/2915 2688/3272/2915 -f 2434/2863/2916 2438/2942/2916 2705/3216/2916 2702/3240/2916 -f 2492/3019/2917 2486/3005/2917 2751/3273/2917 2757/3274/2917 -f 2658/3056/2918 2654/3055/2918 2807/3275/2918 2811/3276/2918 -f 2556/3100/2919 2551/3097/2919 2767/3277/2919 2772/3278/2919 -f 2655/3144/2920 2653/3137/2920 2806/3279/2920 2808/3280/2920 -f 2543/2752/2921 2544/2845/2921 2760/3281/2921 2759/3223/2921 -f 2559/3107/2922 2560/3091/2922 2776/3282/2922 2775/3283/2922 -f 2651/3130/2923 2652/3047/2923 2805/3264/2923 2804/3284/2923 -f 2666/3150/2924 2661/3149/2924 2814/3285/2924 2819/3286/2924 -f 2424/2841/2925 2429/2839/2925 2697/3287/2925 2692/3288/2925 -f 2544/2845/2926 2576/2749/2926 2791/3233/2926 2760/3281/2926 -f 2427/2848/2927 2440/2852/2927 2707/3230/2927 2695/3289/2927 -f 2436/2723/2928 2435/2725/2928 2703/3290/2928 2704/3260/2928 -f 2654/3055/2929 2649/3139/2929 2802/3291/2929 2807/3275/2929 -f 2569/2947/2930 2564/2946/2930 2780/3259/2930 2785/3292/2930 -f 2448/2646/2931 2470/3012/2931 2735/3293/2931 2714/3243/2931 -f 2435/2746/2932 2443/2748/2932 2710/3294/2932 2703/3295/2932 -f 2465/2903/2933 2477/2902/2933 2742/3296/2933 2730/3268/2933 -f 2659/3062/2934 2651/3130/2934 2804/3284/2934 2812/3297/2934 -f 2646/3140/2935 2664/3199/2935 2817/3214/2935 2799/3298/2935 -f 2446/2758/2936 2445/2759/2936 2711/3299/2936 2712/3300/2936 -f 2567/3108/2937 2559/3107/2937 2775/3283/2937 2783/3301/2937 -f 2456/2888/2938 2490/2887/2938 2755/3302/2938 2722/3303/2938 -f 2663/3197/2939 2656/3195/2939 2809/3304/2939 2816/3213/2939 -f 2461/3009/2940 2451/3011/2940 2717/3305/2940 2727/3306/2940 -f 2480/2968/2941 2482/2971/2941 2747/3235/2941 2745/3219/2941 -f 2676/3135/2942 2675/3133/2942 2826/3248/2942 2827/3221/2942 -f 2426/2715/2943 2430/2718/2943 2698/3307/2943 2694/3308/2943 -f 2653/3137/2944 2677/3136/2944 2828/3222/2944 2806/3279/2944 -f 2491/2886/2945 2488/2885/2945 2753/3309/2945 2756/3310/2945 -f 2449/3008/2946 2489/3007/2946 2754/3311/2946 2715/3251/2946 -f 2550/3115/2947 2542/2649/2947 2758/3312/2947 2766/3313/2947 -f 2455/2892/2948 2456/2888/2948 2722/3303/2948 2721/3225/2948 -f 2467/2788/2949 2476/2787/2949 2741/3314/2949 2732/3211/2949 -f 2457/2963/2950 2460/2959/2950 2726/3269/2950 2723/3315/2950 -f 2552/2854/2951 2549/3058/2951 2765/3239/2951 2768/3316/2951 -f 2469/2780/2952 2459/2777/2952 2725/3317/2952 2734/3227/2952 -f 2574/2751/2953 2571/2935/2953 2787/3318/2953 2788/3232/2953 -f 2648/3142/2954 2645/3103/2954 2798/3319/2954 2801/3320/2954 -f 2643/3065/2955 2674/3105/2955 2825/3321/2955 2797/3322/2955 -f 2463/3014/2956 2462/3028/2956 2728/3323/2956 2729/3324/2956 -f 2415/2698/2957 2416/2711/2957 2685/3256/2957 2684/3325/2957 -f 2657/3194/2958 2641/3122/2958 2795/3266/2958 2810/3326/2958 -f 2551/3097/2959 2548/3094/2959 2764/3267/2959 2767/3277/2959 -f 2670/3153/2960 2669/3152/2960 2821/3327/2960 2822/3328/2960 -f 2557/3098/2961 2556/3100/2961 2772/3278/2961 2773/3329/2961 -f 2476/2787/2962 2472/2785/2962 2737/3242/2962 2741/3314/2962 -f 2560/3091/2963 2561/3090/2963 2777/3330/2963 2776/3282/2963 -f 2429/2839/2964 2428/2840/2964 2696/3331/2964 2697/3287/2964 -f 2562/3101/2965 2557/3098/2965 2773/3329/2965 2778/3332/2965 -f 2568/3110/2966 2567/3108/2966 2783/3301/2966 2784/3333/2966 -f 2563/3102/2967 2562/3101/2967 2778/3332/2967 2779/3253/2967 -f 2485/2883/2968 2483/2879/2968 2748/3334/2968 2750/3335/2968 -f 2542/2649/2969 2155/2648/2969 2682/3336/2969 2758/3312/2969 -f 2647/3141/2970 2646/3140/2970 2799/3298/2970 2800/3337/2970 -f 2662/3053/2971 2658/3056/2971 2811/3276/2971 2815/3338/2971 -f 2486/3005/2972 2475/3004/2972 2740/3339/2972 2751/3273/2972 -f 2484/2884/2973 2485/2883/2973 2750/3335/2973 2749/3340/2973 -f 2490/2887/2974 2491/2886/2974 2756/3310/2974 2755/3302/2974 -f 2430/2718/2975 2431/2719/2975 2699/3261/2975 2698/3307/2975 -f 2423/2762/2976 2424/2841/2976 2692/3288/2976 2691/3270/2976 -f 2554/2855/2977 2552/2854/2977 2768/3316/2977 2770/3237/2977 -f 2555/2744/2978 2578/2743/2978 2793/3246/2978 2771/3341/2978 -f 2671/3104/2979 2667/3051/2979 2820/3342/2979 2823/3343/2979 -f 2428/2840/2980 2439/2844/2980 2706/3258/2980 2696/3331/2980 -f 2546/3096/2981 2568/3110/2981 2784/3333/2981 2762/3217/2981 -f 2414/2699/2982 2415/2698/2982 2684/3325/2982 2683/3344/2982 -f 2577/3054/2983 2575/3050/2983 2789/3345/2983 2792/3245/2983 -f 2565/2861/2984 2558/2860/2984 2774/3346/2984 2781/3249/2984 -f 2645/3103/2985 2642/3066/2985 2796/3347/2985 2798/3319/2985 -f 2442/2757/2986 2446/2758/2986 2712/3300/2986 2709/3348/2986 -f 2650/3046/2987 2648/3142/2987 2801/3320/2987 2803/3263/2987 -f 2660/3148/2988 2655/3144/2988 2808/3280/2988 2813/3349/2988 -f 2558/2860/2989 2555/2744/2989 2771/3341/2989 2774/3346/2989 -f 2447/2864/2990 2713/3241/2990 2708/3229/2990 2441/2853/2990 -f 2475/2967/2991 2474/2966/2991 2739/3350/2991 2740/3351/2991 -f 2661/3149/2992 2660/3148/2992 2813/3349/2992 2814/3285/2992 -f 2571/2935/2993 2566/2934/2993 2782/3254/2993 2787/3318/2993 -f 2575/3050/2994 2569/2947/2994 2785/3292/2994 2789/3345/2994 -f 2665/3063/2995 2659/3062/2995 2812/3297/2995 2818/3352/2995 -f 2421/2849/2996 2427/2848/2996 2695/3289/2996 2689/3353/2996 -f 2432/2760/2997 2433/2763/2997 2701/3271/2997 2700/3354/2997 -f 2478/2877/2998 2479/2876/2998 2744/3231/2998 2743/3355/2998 -f 2656/3195/2999 2657/3194/2999 2810/3326/2999 2809/3304/2999 -f 2642/3066/3000 2643/3065/3000 2797/3322/3000 2796/3347/3000 -f 2667/3051/3001 2662/3053/3001 2815/3338/3001 2820/3342/3001 -f 2669/3152/3002 2666/3150/3002 2819/3286/3002 2821/3327/3002 -f 2443/2748/3003 2442/2757/3003 2709/3348/3003 2710/3294/3003 -f 2477/2902/3004 2471/2898/3004 2736/3356/3004 2742/3296/3004 -f 2425/2714/3005 2426/2715/3005 2694/3308/3005 2693/3357/3005 -f 2418/2941/3006 2420/2943/3006 2688/3272/3006 2686/3215/3006 -f 2419/2843/3007 2421/2849/3007 2689/3353/3007 2687/3257/3007 -f 2422/2712/3008 2425/2714/3008 2693/3357/3008 2690/3255/3008 -f 2483/2879/3009 2478/2877/3009 2743/3355/3009 2748/3334/3009 -f 2561/3090/3010 2550/3115/3010 2766/3313/3010 2777/3330/3010 -f 2445/2759/3011 2432/2760/3011 2700/3354/3011 2711/3299/3011 -f 2451/3011/3012 2450/3023/3012 2716/3252/3012 2717/3305/3012 -f 2471/2898/3013 2458/2894/3013 2724/3236/3013 2736/3356/3013 -f 2487/3015/3014 2463/3014/3014 2729/3324/3014 2752/3358/3014 -f 2672/3132/3015 2665/3063/3015 2818/3352/3015 2824/3247/3015 -f 2649/3139/3016 2647/3141/3016 2800/3337/3016 2802/3291/3016 -f 2674/3105/3017 2671/3104/3017 2823/3343/3017 2825/3321/3017 -f 2474/2966/3018 2473/2965/3018 2738/3220/3018 2739/3350/3018 -f 2452/2960/3019 2453/2962/3019 2719/3359/3019 2718/3262/3019 -f 2488/2885/3020 2484/2884/3020 2749/3340/3020 2753/3309/3020 -f 2462/3028/3021 2461/3009/3021 2727/3306/3021 2728/3323/3021 -f 2487/3015/3022 2752/3358/3022 2735/3293/3022 2470/3012/3022 -f 2489/3007/3023 2492/3019/3023 2757/3274/3023 2754/3311/3023 -l 2406 2680 -l 2414 2417 -l 2235 2679 -l 2670 2673 -l 2668 2681 -l 2241 2390 -l 2178 2392 -l 2390 2678 -l 2437 2444 -l 2668 2673 -l 2392 2503 -l 2790 2786 diff --git a/Base/home/anon/Documents/3D Models/teapot.bmp b/Base/home/anon/Documents/3D Models/teapot.bmp deleted file mode 100644 index 6d2030eaed5..00000000000 Binary files a/Base/home/anon/Documents/3D Models/teapot.bmp and /dev/null differ diff --git a/Base/home/anon/Documents/3D Models/teapot.obj b/Base/home/anon/Documents/3D Models/teapot.obj deleted file mode 100644 index 9638d5561dd..00000000000 --- a/Base/home/anon/Documents/3D Models/teapot.obj +++ /dev/null @@ -1,16475 +0,0 @@ -# Copyright (c) 2021-2022, Jesse Buhagiar -# Copyright (c) 2022 Sahan Fernando -# SPDX-License-Identifier: BSD-2-Clause -# -# Blender 3.1.0 -# www.blender.org -# Utah teapot with normals and texture coordinates - -o teapot -v 1.368074 2.435437 -0.227403 -v 1.381968 2.400000 -0.229712 -v 1.400000 2.400000 0.000000 -v 1.385925 2.435437 0.000000 -v 1.316296 2.435437 -0.442166 -v 1.329664 2.400000 -0.446656 -v 1.233252 2.435437 -0.641628 -v 1.245776 2.400000 -0.648144 -v 1.121601 2.435437 -0.823129 -v 1.132992 2.400000 -0.831488 -v 0.984007 2.435437 -0.984007 -v 0.994000 2.400000 -0.994000 -v 0.823129 2.435437 -1.121601 -v 0.831488 2.400000 -1.132992 -v 0.641628 2.435437 -1.233252 -v 0.648144 2.400000 -1.245776 -v 0.442166 2.435437 -1.316296 -v 0.446656 2.400000 -1.329664 -v 0.227403 2.435437 -1.368074 -v 0.229712 2.400000 -1.381968 -v 0.000000 2.400000 -1.400000 -v 1.362620 2.463000 -0.226496 -v 1.380400 2.463000 0.000000 -v 1.311049 2.463000 -0.440403 -v 1.228335 2.463000 -0.639070 -v 1.117130 2.463000 -0.819847 -v 0.980084 2.463000 -0.980084 -v 0.819847 2.463000 -1.117130 -v 0.639070 2.463000 -1.228335 -v 0.440403 2.463000 -1.311049 -v 0.226496 2.463000 -1.362620 -v 0.000000 2.463000 -1.380400 -v 1.364422 2.482687 -0.226795 -v 1.312782 2.482687 -0.440985 -v 1.229959 2.482687 -0.639915 -v 1.118607 2.482687 -0.820931 -v 0.981380 2.482687 -0.981380 -v 0.820931 2.482687 -1.118607 -v 0.639915 2.482687 -1.229959 -v 0.440985 2.482687 -1.312782 -v 0.226795 2.482687 -1.364422 -v 1.372294 2.494500 -0.228104 -v 1.320356 2.494500 -0.443529 -v 1.237056 2.494500 -0.643607 -v 1.125061 2.494500 -0.825668 -v 0.987042 2.494500 -0.987042 -v 0.825668 2.494500 -1.125061 -v 0.643607 2.494500 -1.237056 -v 0.443529 2.494500 -1.320356 -v 0.228104 2.494500 -1.372294 -v 0.000000 2.494500 -1.390200 -v 1.385053 2.498438 -0.230225 -v 1.332632 2.498438 -0.447653 -v 1.248557 2.498438 -0.649591 -v 1.135521 2.498438 -0.833344 -v 0.996219 2.498438 -0.996219 -v 0.833344 2.498438 -1.135521 -v 0.649591 2.498438 -1.248557 -v 0.447653 2.498438 -1.332632 -v 0.230225 2.498438 -1.385053 -v 0.000000 2.498438 -1.403125 -v 1.401513 2.494500 -0.232961 -v 1.348469 2.494500 -0.452973 -v 1.263395 2.494500 -0.657311 -v 1.149016 2.494500 -0.843248 -v 1.008058 2.494500 -1.008058 -v 0.843248 2.494500 -1.149016 -v 0.657311 2.494500 -1.263395 -v 0.452973 2.494500 -1.348469 -v 0.232961 2.494500 -1.401513 -v 1.420490 2.482687 -0.236115 -v 1.439025 2.482687 0.000000 -v 1.366728 2.482687 -0.459107 -v 1.280502 2.482687 -0.666211 -v 1.164574 2.482687 -0.854666 -v 1.021708 2.482687 -1.021708 -v 0.854666 2.482687 -1.164574 -v 0.666211 2.482687 -1.280502 -v 0.459107 2.482687 -1.366728 -v 0.236115 2.482687 -1.420490 -v 0.000000 2.482687 -1.439025 -v 1.440800 2.463000 -0.239491 -v 1.386270 2.463000 -0.465671 -v 1.298810 2.463000 -0.675736 -v 1.181225 2.463000 -0.866886 -v 1.036316 2.463000 -1.036316 -v 0.866886 2.463000 -1.181225 -v 0.675736 2.463000 -1.298810 -v 0.465671 2.463000 -1.386270 -v 0.239491 2.463000 -1.440800 -v 0.000000 2.463000 -1.459600 -v 1.461258 2.435437 -0.242892 -v 1.480325 2.435437 0.000000 -v 1.405953 2.435437 -0.472283 -v 1.317252 2.435437 -0.685331 -v 1.197997 2.435437 -0.879195 -v 1.051031 2.435437 -1.051031 -v 0.879195 2.435437 -1.197997 -v 0.685331 2.435437 -1.317252 -v 0.472283 2.435437 -1.405953 -v 0.242892 2.435437 -1.461258 -v 0.000000 2.435437 -1.480325 -v 1.480680 2.400000 -0.246120 -v 1.424640 2.400000 -0.478560 -v 1.334760 2.400000 -0.694440 -v 1.213920 2.400000 -0.890880 -v 1.065000 2.400000 -1.065000 -v 0.890880 2.400000 -1.213920 -v 0.694440 2.400000 -1.334760 -v 0.478560 2.400000 -1.424640 -v 0.246120 2.400000 -1.480680 -v -0.227403 2.435437 -1.368074 -v -0.229712 2.400000 -1.381968 -v 0.000000 2.435437 -1.385925 -v -0.442166 2.435437 -1.316296 -v -0.446656 2.400000 -1.329664 -v -0.641628 2.435437 -1.233252 -v -0.648144 2.400000 -1.245776 -v -0.823129 2.435437 -1.121601 -v -0.831488 2.400000 -1.132992 -v -0.984007 2.435437 -0.984007 -v -0.994000 2.400000 -0.994000 -v -1.121601 2.435437 -0.823129 -v -1.132992 2.400000 -0.831488 -v -1.233252 2.435437 -0.641628 -v -1.245776 2.400000 -0.648144 -v -1.316296 2.435437 -0.442166 -v -1.329664 2.400000 -0.446656 -v -1.368074 2.435437 -0.227403 -v -1.381968 2.400000 -0.229712 -v -1.385925 2.435437 0.000000 -v -0.226496 2.463000 -1.362620 -v -0.440403 2.463000 -1.311049 -v -0.639070 2.463000 -1.228335 -v -0.819847 2.463000 -1.117130 -v -0.980084 2.463000 -0.980084 -v -1.117130 2.463000 -0.819847 -v -1.228335 2.463000 -0.639070 -v -1.311049 2.463000 -0.440403 -v -1.362620 2.463000 -0.226496 -v -0.226795 2.482687 -1.364422 -v 0.000000 2.482687 -1.382225 -v -0.440985 2.482687 -1.312782 -v -0.639915 2.482687 -1.229959 -v -0.820931 2.482687 -1.118607 -v -0.981380 2.482687 -0.981380 -v -1.118607 2.482687 -0.820931 -v -1.229959 2.482687 -0.639915 -v -1.312782 2.482687 -0.440985 -v -1.364422 2.482687 -0.226795 -v -1.382225 2.482687 0.000000 -v -0.228104 2.494500 -1.372294 -v -0.443529 2.494500 -1.320356 -v -0.643607 2.494500 -1.237056 -v -0.825668 2.494500 -1.125061 -v -0.987042 2.494500 -0.987042 -v -1.125061 2.494500 -0.825668 -v -1.237056 2.494500 -0.643607 -v -1.320356 2.494500 -0.443529 -v -1.372294 2.494500 -0.228104 -v -0.230225 2.498438 -1.385053 -v -0.447653 2.498438 -1.332632 -v -0.649591 2.498438 -1.248557 -v -0.833344 2.498438 -1.135521 -v -0.996219 2.498438 -0.996219 -v -1.135521 2.498438 -0.833344 -v -1.248557 2.498438 -0.649591 -v -1.332632 2.498438 -0.447653 -v -1.385053 2.498438 -0.230225 -v -1.403125 2.498438 0.000000 -v -0.232961 2.494500 -1.401513 -v 0.000000 2.494500 -1.419800 -v -0.452973 2.494500 -1.348469 -v -0.657311 2.494500 -1.263395 -v -0.843248 2.494500 -1.149016 -v -1.008058 2.494500 -1.008058 -v -1.149016 2.494500 -0.843248 -v -1.263395 2.494500 -0.657311 -v -1.348469 2.494500 -0.452973 -v -1.401513 2.494500 -0.232961 -v -0.236115 2.482687 -1.420490 -v -0.459107 2.482687 -1.366728 -v -0.666211 2.482687 -1.280502 -v -0.854666 2.482687 -1.164574 -v -1.021708 2.482687 -1.021708 -v -1.164574 2.482687 -0.854666 -v -1.280502 2.482687 -0.666211 -v -1.366728 2.482687 -0.459107 -v -1.420490 2.482687 -0.236115 -v -1.439025 2.482687 0.000000 -v -0.239491 2.463000 -1.440800 -v -0.465671 2.463000 -1.386270 -v -0.675736 2.463000 -1.298810 -v -0.866886 2.463000 -1.181225 -v -1.036316 2.463000 -1.036316 -v -1.181225 2.463000 -0.866886 -v -1.298810 2.463000 -0.675736 -v -1.386270 2.463000 -0.465671 -v -1.440800 2.463000 -0.239491 -v -0.242892 2.435437 -1.461258 -v -0.472283 2.435437 -1.405953 -v -0.685331 2.435437 -1.317252 -v -0.879195 2.435437 -1.197997 -v -1.051031 2.435437 -1.051031 -v -1.197997 2.435437 -0.879195 -v -1.317252 2.435437 -0.685331 -v -1.405953 2.435437 -0.472283 -v -1.461258 2.435437 -0.242892 -v -0.246120 2.400000 -1.480680 -v 0.000000 2.400000 -1.500000 -v -0.478560 2.400000 -1.424640 -v -0.694440 2.400000 -1.334760 -v -0.890880 2.400000 -1.213920 -v -1.065000 2.400000 -1.065000 -v -1.213920 2.400000 -0.890880 -v -1.334760 2.400000 -0.694440 -v -1.424640 2.400000 -0.478560 -v -1.480680 2.400000 -0.246120 -v -1.368074 2.435437 0.227403 -v -1.381968 2.400000 0.229712 -v -1.400000 2.400000 0.000000 -v -1.316296 2.435437 0.442166 -v -1.329664 2.400000 0.446656 -v -1.233252 2.435437 0.641628 -v -1.245776 2.400000 0.648144 -v -1.121601 2.435437 0.823129 -v -1.132992 2.400000 0.831488 -v -0.984007 2.435437 0.984007 -v -0.994000 2.400000 0.994000 -v -0.823129 2.435437 1.121601 -v -0.831488 2.400000 1.132992 -v -0.641628 2.435437 1.233252 -v -0.648144 2.400000 1.245776 -v -0.442166 2.435437 1.316296 -v -0.446656 2.400000 1.329664 -v -0.227403 2.435437 1.368074 -v -0.229712 2.400000 1.381968 -v 0.000000 2.435437 1.385925 -v -1.362620 2.463000 0.226496 -v -1.380400 2.463000 0.000000 -v -1.311049 2.463000 0.440403 -v -1.228335 2.463000 0.639070 -v -1.117130 2.463000 0.819847 -v -0.980084 2.463000 0.980084 -v -0.819847 2.463000 1.117130 -v -0.639070 2.463000 1.228335 -v -0.440403 2.463000 1.311049 -v -0.226496 2.463000 1.362620 -v 0.000000 2.463000 1.380400 -v -1.364422 2.482687 0.226795 -v -1.312782 2.482687 0.440985 -v -1.229959 2.482687 0.639915 -v -1.118607 2.482687 0.820931 -v -0.981380 2.482687 0.981380 -v -0.820931 2.482687 1.118607 -v -0.639915 2.482687 1.229959 -v -0.440985 2.482687 1.312782 -v -0.226795 2.482687 1.364422 -v -1.372294 2.494500 0.228104 -v -1.390200 2.494500 0.000000 -v -1.320356 2.494500 0.443529 -v -1.237056 2.494500 0.643607 -v -1.125061 2.494500 0.825668 -v -0.987042 2.494500 0.987042 -v -0.825668 2.494500 1.125061 -v -0.643607 2.494500 1.237056 -v -0.443529 2.494500 1.320356 -v -0.228104 2.494500 1.372294 -v -1.385053 2.498438 0.230225 -v -1.332632 2.498438 0.447653 -v -1.248557 2.498438 0.649591 -v -1.135521 2.498438 0.833344 -v -0.996219 2.498438 0.996219 -v -0.833344 2.498438 1.135521 -v -0.649591 2.498438 1.248557 -v -0.447653 2.498438 1.332632 -v -0.230225 2.498438 1.385053 -v 0.000000 2.498438 1.403125 -v -1.401513 2.494500 0.232961 -v -1.419800 2.494500 0.000000 -v -1.348469 2.494500 0.452973 -v -1.263395 2.494500 0.657311 -v -1.149016 2.494500 0.843248 -v -1.008058 2.494500 1.008058 -v -0.843248 2.494500 1.149016 -v -0.657311 2.494500 1.263395 -v -0.452973 2.494500 1.348469 -v -0.232961 2.494500 1.401513 -v 0.000000 2.494500 1.419800 -v -1.420490 2.482687 0.236115 -v -1.366728 2.482687 0.459107 -v -1.280502 2.482687 0.666211 -v -1.164574 2.482687 0.854666 -v -1.021708 2.482687 1.021708 -v -0.854666 2.482687 1.164574 -v -0.666211 2.482687 1.280502 -v -0.459107 2.482687 1.366728 -v -0.236115 2.482687 1.420490 -v 0.000000 2.482687 1.439025 -v -1.440800 2.463000 0.239491 -v -1.459600 2.463000 0.000000 -v -1.386270 2.463000 0.465671 -v -1.298810 2.463000 0.675736 -v -1.181225 2.463000 0.866886 -v -1.036316 2.463000 1.036316 -v -0.866886 2.463000 1.181225 -v -0.675736 2.463000 1.298810 -v -0.465671 2.463000 1.386270 -v -0.239491 2.463000 1.440800 -v 0.000000 2.463000 1.459600 -v -1.461258 2.435437 0.242892 -v -1.480325 2.435437 0.000000 -v -1.405953 2.435437 0.472283 -v -1.317252 2.435437 0.685331 -v -1.197997 2.435437 0.879195 -v -1.051031 2.435437 1.051031 -v -0.879195 2.435437 1.197997 -v -0.685331 2.435437 1.317252 -v -0.472283 2.435437 1.405953 -v -0.242892 2.435437 1.461258 -v 0.000000 2.435437 1.480325 -v -1.480680 2.400000 0.246120 -v -1.500000 2.400000 0.000000 -v -1.424640 2.400000 0.478560 -v -1.334760 2.400000 0.694440 -v -1.213920 2.400000 0.890880 -v -1.065000 2.400000 1.065000 -v -0.890880 2.400000 1.213920 -v -0.694440 2.400000 1.334760 -v -0.478560 2.400000 1.424640 -v -0.246120 2.400000 1.480680 -v 0.000000 2.400000 1.500000 -v 0.227403 2.435437 1.368074 -v 0.229712 2.400000 1.381968 -v 0.000000 2.400000 1.400000 -v 0.442166 2.435437 1.316296 -v 0.446656 2.400000 1.329664 -v 0.641628 2.435437 1.233252 -v 0.648144 2.400000 1.245776 -v 0.823129 2.435437 1.121601 -v 0.831488 2.400000 1.132992 -v 0.984007 2.435437 0.984007 -v 0.994000 2.400000 0.994000 -v 1.121601 2.435437 0.823129 -v 1.132992 2.400000 0.831488 -v 1.233252 2.435437 0.641628 -v 1.245776 2.400000 0.648144 -v 1.316296 2.435437 0.442166 -v 1.329664 2.400000 0.446656 -v 1.368074 2.435437 0.227403 -v 1.381968 2.400000 0.229712 -v 0.226496 2.463000 1.362620 -v 0.440403 2.463000 1.311049 -v 0.639070 2.463000 1.228335 -v 0.819847 2.463000 1.117130 -v 0.980084 2.463000 0.980084 -v 1.117130 2.463000 0.819847 -v 1.228335 2.463000 0.639070 -v 1.311049 2.463000 0.440403 -v 1.362620 2.463000 0.226496 -v 0.226795 2.482687 1.364422 -v 0.000000 2.482687 1.382225 -v 0.440985 2.482687 1.312782 -v 0.639915 2.482687 1.229959 -v 0.820931 2.482687 1.118607 -v 0.981380 2.482687 0.981380 -v 1.118607 2.482687 0.820931 -v 1.229959 2.482687 0.639915 -v 1.312782 2.482687 0.440985 -v 1.364422 2.482687 0.226795 -v 1.382225 2.482687 0.000000 -v 0.228104 2.494500 1.372294 -v 0.000000 2.494500 1.390200 -v 0.443529 2.494500 1.320356 -v 0.643607 2.494500 1.237056 -v 0.825668 2.494500 1.125061 -v 0.987042 2.494500 0.987042 -v 1.125061 2.494500 0.825668 -v 1.237056 2.494500 0.643607 -v 1.320356 2.494500 0.443529 -v 1.372294 2.494500 0.228104 -v 1.390200 2.494500 0.000000 -v 0.230225 2.498438 1.385053 -v 0.447653 2.498438 1.332632 -v 0.649591 2.498438 1.248557 -v 0.833344 2.498438 1.135521 -v 0.996219 2.498438 0.996219 -v 1.135521 2.498438 0.833344 -v 1.248557 2.498438 0.649591 -v 1.332632 2.498438 0.447653 -v 1.385053 2.498438 0.230225 -v 1.403125 2.498438 0.000000 -v 0.232961 2.494500 1.401513 -v 0.452973 2.494500 1.348469 -v 0.657311 2.494500 1.263395 -v 0.843248 2.494500 1.149016 -v 1.008058 2.494500 1.008058 -v 1.149016 2.494500 0.843248 -v 1.263395 2.494500 0.657311 -v 1.348469 2.494500 0.452973 -v 1.401513 2.494500 0.232961 -v 1.419800 2.494500 0.000000 -v 0.236115 2.482687 1.420490 -v 0.459107 2.482687 1.366728 -v 0.666211 2.482687 1.280502 -v 0.854666 2.482687 1.164574 -v 1.021708 2.482687 1.021708 -v 1.164574 2.482687 0.854666 -v 1.280502 2.482687 0.666211 -v 1.366728 2.482687 0.459107 -v 1.420490 2.482687 0.236115 -v 0.239491 2.463000 1.440800 -v 0.465671 2.463000 1.386270 -v 0.675736 2.463000 1.298810 -v 0.866886 2.463000 1.181225 -v 1.036316 2.463000 1.036316 -v 1.181225 2.463000 0.866886 -v 1.298810 2.463000 0.675736 -v 1.386270 2.463000 0.465671 -v 1.440800 2.463000 0.239491 -v 1.459600 2.463000 0.000000 -v 0.242892 2.435437 1.461258 -v 0.472283 2.435437 1.405953 -v 0.685331 2.435437 1.317252 -v 0.879195 2.435437 1.197997 -v 1.051031 2.435437 1.051031 -v 1.197997 2.435437 0.879195 -v 1.317252 2.435437 0.685331 -v 1.405953 2.435437 0.472283 -v 1.461258 2.435437 0.242892 -v 0.246120 2.400000 1.480680 -v 0.478560 2.400000 1.424640 -v 0.694440 2.400000 1.334760 -v 0.890880 2.400000 1.213920 -v 1.065000 2.400000 1.065000 -v 1.213920 2.400000 0.890880 -v 1.334760 2.400000 0.694440 -v 1.424640 2.400000 0.478560 -v 1.480680 2.400000 0.246120 -v 1.500000 2.400000 0.000000 -v 1.554467 2.242575 -0.258385 -v 1.574750 2.242575 0.000000 -v 1.495635 2.242575 -0.502408 -v 1.401276 2.242575 -0.729046 -v 1.274414 2.242575 -0.935276 -v 1.118073 2.242575 -1.118073 -v 0.935276 2.242575 -1.274414 -v 0.729046 2.242575 -1.401276 -v 0.502408 2.242575 -1.495635 -v 0.258385 2.242575 -1.554467 -v 0.000000 2.242575 -1.574750 -v 1.626774 2.085600 -0.270404 -v 1.648000 2.085600 0.000000 -v 1.565204 2.085600 -0.525778 -v 1.466456 2.085600 -0.762958 -v 1.333693 2.085600 -0.978780 -v 1.170080 2.085600 -1.170080 -v 0.978780 2.085600 -1.333693 -v 0.762958 2.085600 -1.466456 -v 0.525778 2.085600 -1.565204 -v 0.270404 2.085600 -1.626774 -v 0.000000 2.085600 -1.648000 -v 1.696119 1.929525 -0.281930 -v 1.718250 1.929525 0.000000 -v 1.631925 1.929525 -0.548190 -v 1.528968 1.929525 -0.795481 -v 1.390545 1.929525 -1.020503 -v 1.219958 1.929525 -1.219958 -v 1.020503 1.929525 -1.390545 -v 0.795481 1.929525 -1.528968 -v 0.548190 1.929525 -1.631925 -v 0.281930 1.929525 -1.696119 -v 0.000000 1.929525 -1.718250 -v 1.761022 1.774800 -0.292719 -v 1.784000 1.774800 0.000000 -v 1.694372 1.774800 -0.569167 -v 1.587475 1.774800 -0.825921 -v 1.443756 1.774800 -1.059553 -v 1.266640 1.774800 -1.266640 -v 1.059553 1.774800 -1.443756 -v 0.825921 1.774800 -1.587475 -v 0.569167 1.774800 -1.694372 -v 0.292719 1.774800 -1.761022 -v 1.820003 1.621875 -0.302523 -v 1.843750 1.621875 0.000000 -v 1.751120 1.621875 -0.588230 -v 1.640643 1.621875 -0.853583 -v 1.492110 1.621875 -1.095040 -v 1.309063 1.621875 -1.309063 -v 1.095040 1.621875 -1.492110 -v 0.853583 1.621875 -1.640643 -v 0.588230 1.621875 -1.751120 -v 0.302522 1.621875 -1.820003 -v 1.871580 1.471200 -0.311096 -v 1.896000 1.471200 0.000000 -v 1.800745 1.471200 -0.604900 -v 1.687137 1.471200 -0.877772 -v 1.534395 1.471200 -1.126072 -v 1.346160 1.471200 -1.346160 -v 1.126072 1.471200 -1.534395 -v 0.877772 1.471200 -1.687137 -v 0.604900 1.471200 -1.800745 -v 0.311096 1.471200 -1.871580 -v 1.914272 1.323225 -0.318192 -v 1.939250 1.323225 0.000000 -v 1.841822 1.323225 -0.618698 -v 1.725622 1.323225 -0.897795 -v 1.569396 1.323225 -1.151759 -v 1.376867 1.323225 -1.376868 -v 1.151759 1.323225 -1.569396 -v 0.897795 1.323225 -1.725622 -v 0.618698 1.323225 -1.841822 -v 0.318192 1.323225 -1.914272 -v 0.000000 1.323225 -1.939250 -v 1.946601 1.178400 -0.323566 -v 1.872927 1.178400 -0.629147 -v 1.754764 1.178400 -0.912957 -v 1.595900 1.178400 -1.171210 -v 1.400120 1.178400 -1.400120 -v 1.171210 1.178400 -1.595900 -v 0.912957 1.178400 -1.754764 -v 0.629147 1.178400 -1.872927 -v 0.323566 1.178400 -1.946601 -v 0.000000 1.178400 -1.972000 -v 1.967083 1.037175 -0.326970 -v 1.892634 1.037175 -0.635767 -v 1.773229 1.037175 -0.922564 -v 1.612693 1.037175 -1.183534 -v 1.414853 1.037175 -1.414853 -v 1.183534 1.037175 -1.612693 -v 0.922564 1.037175 -1.773229 -v 0.635767 1.037175 -1.892634 -v 0.326970 1.037175 -1.967083 -v 1.974240 0.900000 -0.328160 -v 2.000000 0.900000 0.000000 -v 1.618560 0.900000 -1.187840 -v 0.328160 0.900000 -1.974240 -v 0.000000 0.900000 -2.000000 -v -0.258385 2.242575 -1.554467 -v -0.502408 2.242575 -1.495635 -v -0.729046 2.242575 -1.401276 -v -0.935276 2.242575 -1.274414 -v -1.118073 2.242575 -1.118073 -v -1.274414 2.242575 -0.935276 -v -1.401276 2.242575 -0.729046 -v -1.495635 2.242575 -0.502408 -v -1.554467 2.242575 -0.258385 -v -0.270404 2.085600 -1.626774 -v -0.525778 2.085600 -1.565204 -v -0.762958 2.085600 -1.466456 -v -0.978780 2.085600 -1.333693 -v -1.170080 2.085600 -1.170080 -v -1.333693 2.085600 -0.978780 -v -1.466456 2.085600 -0.762958 -v -1.565204 2.085600 -0.525778 -v -1.626774 2.085600 -0.270404 -v -1.648000 2.085600 0.000000 -v -0.281930 1.929525 -1.696119 -v -0.548190 1.929525 -1.631925 -v -0.795481 1.929525 -1.528968 -v -1.020503 1.929525 -1.390545 -v -1.219958 1.929525 -1.219958 -v -1.390545 1.929525 -1.020503 -v -1.528968 1.929525 -0.795481 -v -1.631925 1.929525 -0.548190 -v -1.696119 1.929525 -0.281930 -v -1.718250 1.929525 0.000000 -v -0.292719 1.774800 -1.761022 -v 0.000000 1.774800 -1.784000 -v -0.569167 1.774800 -1.694372 -v -0.825921 1.774800 -1.587475 -v -1.059553 1.774800 -1.443756 -v -1.266640 1.774800 -1.266640 -v -1.443756 1.774800 -1.059553 -v -1.587475 1.774800 -0.825921 -v -1.694372 1.774800 -0.569167 -v -1.761022 1.774800 -0.292719 -v -0.302523 1.621875 -1.820003 -v 0.000000 1.621875 -1.843750 -v -0.588230 1.621875 -1.751120 -v -0.853583 1.621875 -1.640643 -v -1.095040 1.621875 -1.492110 -v -1.309063 1.621875 -1.309063 -v -1.492110 1.621875 -1.095040 -v -1.640643 1.621875 -0.853583 -v -1.751120 1.621875 -0.588230 -v -1.820003 1.621875 -0.302522 -v -0.311096 1.471200 -1.871580 -v 0.000000 1.471200 -1.896000 -v -0.604900 1.471200 -1.800745 -v -0.877772 1.471200 -1.687137 -v -1.126072 1.471200 -1.534395 -v -1.346160 1.471200 -1.346160 -v -1.534395 1.471200 -1.126072 -v -1.687137 1.471200 -0.877772 -v -1.800745 1.471200 -0.604900 -v -1.871580 1.471200 -0.311096 -v -1.896000 1.471200 0.000000 -v -0.318192 1.323225 -1.914272 -v -0.618698 1.323225 -1.841822 -v -0.897795 1.323225 -1.725622 -v -1.151759 1.323225 -1.569396 -v -1.376868 1.323225 -1.376867 -v -1.569396 1.323225 -1.151759 -v -1.725622 1.323225 -0.897795 -v -1.841822 1.323225 -0.618698 -v -1.914272 1.323225 -0.318192 -v -0.323566 1.178400 -1.946601 -v -0.629147 1.178400 -1.872927 -v -0.912957 1.178400 -1.754764 -v -1.171210 1.178400 -1.595900 -v -1.400120 1.178400 -1.400120 -v -1.595900 1.178400 -1.171210 -v -1.754764 1.178400 -0.912957 -v -1.872927 1.178400 -0.629147 -v -1.946601 1.178400 -0.323566 -v -1.972000 1.178400 0.000000 -v -0.326970 1.037175 -1.967083 -v 0.000000 1.037175 -1.992750 -v -0.635767 1.037175 -1.892634 -v -0.922564 1.037175 -1.773229 -v -1.183534 1.037175 -1.612693 -v -1.414853 1.037175 -1.414853 -v -1.612693 1.037175 -1.183534 -v -1.773229 1.037175 -0.922564 -v -1.892634 1.037175 -0.635767 -v -1.967083 1.037175 -0.326970 -v -0.328160 0.900000 -1.974240 -v -0.925920 0.900000 -1.779680 -v -1.779680 0.900000 -0.925920 -v -1.899520 0.900000 -0.638080 -v -1.554467 2.242575 0.258385 -v -1.574750 2.242575 0.000000 -v -1.495635 2.242575 0.502408 -v -1.401276 2.242575 0.729046 -v -1.274414 2.242575 0.935276 -v -1.118073 2.242575 1.118073 -v -0.935276 2.242575 1.274414 -v -0.729046 2.242575 1.401276 -v -0.502408 2.242575 1.495635 -v -0.258385 2.242575 1.554467 -v -1.626774 2.085600 0.270404 -v -1.565204 2.085600 0.525778 -v -1.466456 2.085600 0.762958 -v -1.333693 2.085600 0.978780 -v -1.170080 2.085600 1.170080 -v -0.978780 2.085600 1.333693 -v -0.762958 2.085600 1.466456 -v -0.525778 2.085600 1.565204 -v -0.270404 2.085600 1.626774 -v -1.696119 1.929525 0.281930 -v -1.631925 1.929525 0.548190 -v -1.528968 1.929525 0.795481 -v -1.390545 1.929525 1.020503 -v -1.219958 1.929525 1.219958 -v -1.020503 1.929525 1.390545 -v -0.795481 1.929525 1.528968 -v -0.548190 1.929525 1.631925 -v -0.281930 1.929525 1.696119 -v 0.000000 1.929525 1.718250 -v -1.761022 1.774800 0.292719 -v -1.784000 1.774800 0.000000 -v -1.694372 1.774800 0.569167 -v -1.587475 1.774800 0.825921 -v -1.443756 1.774800 1.059553 -v -1.266640 1.774800 1.266640 -v -1.059553 1.774800 1.443756 -v -0.825921 1.774800 1.587475 -v -0.569167 1.774800 1.694372 -v -0.292719 1.774800 1.761022 -v -1.820003 1.621875 0.302523 -v -1.843750 1.621875 0.000000 -v -1.751120 1.621875 0.588230 -v -1.640643 1.621875 0.853583 -v -1.492110 1.621875 1.095040 -v -1.309063 1.621875 1.309063 -v -1.095040 1.621875 1.492110 -v -0.853583 1.621875 1.640643 -v -0.588230 1.621875 1.751120 -v -0.302522 1.621875 1.820003 -v -1.871580 1.471200 0.311096 -v -1.800745 1.471200 0.604900 -v -1.687137 1.471200 0.877772 -v -1.534395 1.471200 1.126072 -v -1.346160 1.471200 1.346160 -v -1.126072 1.471200 1.534395 -v -0.877772 1.471200 1.687137 -v -0.604900 1.471200 1.800745 -v -0.311096 1.471200 1.871580 -v -1.914272 1.323225 0.318192 -v -1.939250 1.323225 0.000000 -v -1.841822 1.323225 0.618698 -v -1.725622 1.323225 0.897795 -v -1.569396 1.323225 1.151759 -v -1.376867 1.323225 1.376868 -v -1.151759 1.323225 1.569396 -v -0.897795 1.323225 1.725622 -v -0.618698 1.323225 1.841822 -v -0.318192 1.323225 1.914272 -v -1.946601 1.178400 0.323566 -v -1.872927 1.178400 0.629147 -v -1.754764 1.178400 0.912957 -v -1.595900 1.178400 1.171210 -v -1.400120 1.178400 1.400120 -v -1.171210 1.178400 1.595900 -v -0.912957 1.178400 1.754764 -v -0.629147 1.178400 1.872927 -v -0.323566 1.178400 1.946601 -v 0.000000 1.178400 1.972000 -v -1.967083 1.037175 0.326970 -v -1.992750 1.037175 0.000000 -v -1.892634 1.037175 0.635767 -v -1.773229 1.037175 0.922564 -v -1.612693 1.037175 1.183534 -v -1.414853 1.037175 1.414853 -v -1.183534 1.037175 1.612693 -v -0.922564 1.037175 1.773229 -v -0.635767 1.037175 1.892634 -v -0.326970 1.037175 1.967083 -v 0.000000 1.037175 1.992750 -v -1.974240 0.900000 0.328160 -v -2.000000 0.900000 0.000000 -v -1.618560 0.900000 1.187840 -v -1.420000 0.900000 1.420000 -v -0.925920 0.900000 1.779680 -v -0.638080 0.900000 1.899520 -v 0.000000 0.900000 2.000000 -v 0.258385 2.242575 1.554467 -v 0.000000 2.242575 1.574750 -v 0.502408 2.242575 1.495635 -v 0.729046 2.242575 1.401276 -v 0.935276 2.242575 1.274414 -v 1.118073 2.242575 1.118073 -v 1.274414 2.242575 0.935276 -v 1.401276 2.242575 0.729046 -v 1.495635 2.242575 0.502408 -v 1.554467 2.242575 0.258385 -v 0.270404 2.085600 1.626774 -v 0.000000 2.085600 1.648000 -v 0.525778 2.085600 1.565204 -v 0.762958 2.085600 1.466456 -v 0.978780 2.085600 1.333693 -v 1.170080 2.085600 1.170080 -v 1.333693 2.085600 0.978780 -v 1.466456 2.085600 0.762958 -v 1.565204 2.085600 0.525778 -v 1.626774 2.085600 0.270404 -v 0.281930 1.929525 1.696119 -v 0.548190 1.929525 1.631925 -v 0.795481 1.929525 1.528968 -v 1.020503 1.929525 1.390545 -v 1.219958 1.929525 1.219958 -v 1.390545 1.929525 1.020503 -v 1.528968 1.929525 0.795481 -v 1.631925 1.929525 0.548190 -v 1.696119 1.929525 0.281930 -v 0.292719 1.774800 1.761022 -v 0.000000 1.774800 1.784000 -v 0.569167 1.774800 1.694372 -v 0.825921 1.774800 1.587475 -v 1.059553 1.774800 1.443756 -v 1.266640 1.774800 1.266640 -v 1.443756 1.774800 1.059553 -v 1.587475 1.774800 0.825921 -v 1.694372 1.774800 0.569167 -v 1.761022 1.774800 0.292719 -v 0.302523 1.621875 1.820003 -v 0.000000 1.621875 1.843750 -v 0.588230 1.621875 1.751120 -v 0.853583 1.621875 1.640643 -v 1.095040 1.621875 1.492110 -v 1.309063 1.621875 1.309063 -v 1.492110 1.621875 1.095040 -v 1.640643 1.621875 0.853583 -v 1.751120 1.621875 0.588230 -v 1.820003 1.621875 0.302522 -v 0.311096 1.471200 1.871580 -v 0.000000 1.471200 1.896000 -v 0.604900 1.471200 1.800745 -v 0.877772 1.471200 1.687137 -v 1.126072 1.471200 1.534395 -v 1.346160 1.471200 1.346160 -v 1.534395 1.471200 1.126072 -v 1.687137 1.471200 0.877772 -v 1.800745 1.471200 0.604900 -v 1.871580 1.471200 0.311096 -v 0.318192 1.323225 1.914272 -v 0.000000 1.323225 1.939250 -v 0.618698 1.323225 1.841822 -v 0.897795 1.323225 1.725622 -v 1.151759 1.323225 1.569396 -v 1.376868 1.323225 1.376867 -v 1.569396 1.323225 1.151759 -v 1.725622 1.323225 0.897795 -v 1.841822 1.323225 0.618698 -v 1.914272 1.323225 0.318192 -v 0.323566 1.178400 1.946601 -v 0.629147 1.178400 1.872927 -v 0.912957 1.178400 1.754764 -v 1.171210 1.178400 1.595900 -v 1.400120 1.178400 1.400120 -v 1.595900 1.178400 1.171210 -v 1.754764 1.178400 0.912957 -v 1.872927 1.178400 0.629147 -v 1.946601 1.178400 0.323566 -v 1.972000 1.178400 0.000000 -v 0.326970 1.037175 1.967083 -v 0.635767 1.037175 1.892634 -v 0.922564 1.037175 1.773229 -v 1.183534 1.037175 1.612693 -v 1.414853 1.037175 1.414853 -v 1.612693 1.037175 1.183534 -v 1.773229 1.037175 0.922564 -v 1.892634 1.037175 0.635767 -v 1.967083 1.037175 0.326970 -v 1.992750 1.037175 0.000000 -v 0.328160 0.900000 1.974240 -v 1.420000 0.900000 1.420000 -v 1.779680 0.900000 0.925920 -v 1.899520 0.900000 0.638080 -v 1.974240 0.900000 0.328160 -v 1.960420 0.771675 -0.325863 -v 1.886223 0.771675 -0.633613 -v 1.899520 0.900000 -0.638080 -v 1.767222 0.771675 -0.919439 -v 1.779680 0.900000 -0.925920 -v 1.607230 0.771675 -1.179525 -v 1.410060 0.771675 -1.410060 -v 1.420000 0.900000 -1.420000 -v 1.179525 0.771675 -1.607230 -v 1.187840 0.900000 -1.618560 -v 0.919439 0.771675 -1.767222 -v 0.925920 0.900000 -1.779680 -v 0.633613 0.771675 -1.886223 -v 0.638080 0.900000 -1.899520 -v 0.325863 0.771675 -1.960420 -v 1.922910 0.656400 -0.319628 -v 1.850132 0.656400 -0.621490 -v 1.733408 0.656400 -0.901846 -v 1.576477 0.656400 -1.156956 -v 1.383080 0.656400 -1.383080 -v 1.156956 0.656400 -1.576477 -v 0.901846 0.656400 -1.733408 -v 0.621490 0.656400 -1.850132 -v 0.319628 0.656400 -1.922910 -v 1.867631 0.553725 -0.310439 -v 1.892000 0.553725 0.000000 -v 1.796946 0.553725 -0.603624 -v 1.683577 0.553725 -0.875920 -v 1.531158 0.553725 -1.123697 -v 1.343320 0.553725 -1.343320 -v 1.123697 0.553725 -1.531158 -v 0.875920 0.553725 -1.683577 -v 0.603624 0.553725 -1.796946 -v 0.310439 0.553725 -1.867631 -v 1.800507 0.463200 -0.299282 -v 1.824000 0.463200 0.000000 -v 1.732362 0.463200 -0.581929 -v 1.623068 0.463200 -0.844439 -v 1.476127 0.463200 -1.083310 -v 1.295040 0.463200 -1.295040 -v 1.083310 0.463200 -1.476127 -v 0.844439 0.463200 -1.623068 -v 0.581929 0.463200 -1.732362 -v 0.299282 0.463200 -1.800507 -v 0.000000 0.463200 -1.824000 -v 1.727460 0.384375 -0.287140 -v 1.750000 0.384375 0.000000 -v 1.662080 0.384375 -0.558320 -v 1.557220 0.384375 -0.810180 -v 1.416240 0.384375 -1.039360 -v 1.242500 0.384375 -1.242500 -v 1.039360 0.384375 -1.416240 -v 0.810180 0.384375 -1.557220 -v 0.558320 0.384375 -1.662080 -v 0.287140 0.384375 -1.727460 -v 0.000000 0.384375 -1.750000 -v 1.654413 0.316800 -0.274998 -v 1.676000 0.316800 0.000000 -v 1.591798 0.316800 -0.534711 -v 1.491372 0.316800 -0.775921 -v 1.356353 0.316800 -0.995410 -v 1.189960 0.316800 -1.189960 -v 0.995410 0.316800 -1.356353 -v 0.775921 0.316800 -1.491372 -v 0.534711 0.316800 -1.591798 -v 0.274998 0.316800 -1.654413 -v 0.000000 0.316800 -1.676000 -v 1.587289 0.260025 -0.263841 -v 1.608000 0.260025 0.000000 -v 1.527214 0.260025 -0.513016 -v 1.430863 0.260025 -0.744440 -v 1.301322 0.260025 -0.955023 -v 1.141680 0.260025 -1.141680 -v 0.955023 0.260025 -1.301322 -v 0.744440 0.260025 -1.430863 -v 0.513016 0.260025 -1.527214 -v 0.263841 0.260025 -1.587289 -v 0.000000 0.260025 -1.608000 -v 1.532010 0.213600 -0.254652 -v 1.552000 0.213600 0.000000 -v 1.474028 0.213600 -0.495150 -v 1.381032 0.213600 -0.718514 -v 1.256003 0.213600 -0.921764 -v 1.101920 0.213600 -1.101920 -v 0.921764 0.213600 -1.256003 -v 0.718514 0.213600 -1.381032 -v 0.495150 0.213600 -1.474028 -v 0.254652 0.213600 -1.532010 -v 0.000000 0.213600 -1.552000 -v 1.494500 0.177075 -0.248417 -v 1.514000 0.177075 0.000000 -v 1.437937 0.177075 -0.483027 -v 1.347218 0.177075 -0.700921 -v 1.225250 0.177075 -0.899195 -v 1.074940 0.177075 -1.074940 -v 0.899195 0.177075 -1.225250 -v 0.700921 0.177075 -1.347218 -v 0.483027 0.177075 -1.437937 -v 0.248417 0.177075 -1.494500 -v 0.000000 0.177075 -1.514000 -v 1.500000 0.150000 0.000000 -v 1.424640 0.150000 -0.478560 -v 1.065000 0.150000 -1.065000 -v 0.890880 0.150000 -1.213920 -v 0.000000 0.150000 -1.500000 -v -0.325863 0.771675 -1.960420 -v 0.000000 0.771675 -1.986000 -v -0.633613 0.771675 -1.886223 -v -0.638080 0.900000 -1.899520 -v -0.919439 0.771675 -1.767222 -v -1.179525 0.771675 -1.607230 -v -1.187840 0.900000 -1.618560 -v -1.410060 0.771675 -1.410060 -v -1.420000 0.900000 -1.420000 -v -1.607230 0.771675 -1.179525 -v -1.618560 0.900000 -1.187840 -v -1.767222 0.771675 -0.919439 -v -1.886223 0.771675 -0.633613 -v -1.960420 0.771675 -0.325863 -v -1.974240 0.900000 -0.328160 -v -0.319628 0.656400 -1.922910 -v 0.000000 0.656400 -1.948000 -v -0.621490 0.656400 -1.850132 -v -0.901846 0.656400 -1.733408 -v -1.156956 0.656400 -1.576477 -v -1.383080 0.656400 -1.383080 -v -1.576477 0.656400 -1.156956 -v -1.733408 0.656400 -0.901846 -v -1.850132 0.656400 -0.621490 -v -1.922910 0.656400 -0.319628 -v -0.310439 0.553725 -1.867631 -v 0.000000 0.553725 -1.892000 -v -0.603624 0.553725 -1.796946 -v -0.875920 0.553725 -1.683577 -v -1.123697 0.553725 -1.531158 -v -1.343320 0.553725 -1.343320 -v -1.531158 0.553725 -1.123697 -v -1.683577 0.553725 -0.875920 -v -1.796946 0.553725 -0.603624 -v -1.867631 0.553725 -0.310439 -v -0.299282 0.463200 -1.800507 -v -0.581929 0.463200 -1.732362 -v -0.844439 0.463200 -1.623068 -v -1.083310 0.463200 -1.476127 -v -1.295040 0.463200 -1.295040 -v -1.476127 0.463200 -1.083310 -v -1.623068 0.463200 -0.844439 -v -1.732362 0.463200 -0.581929 -v -1.800507 0.463200 -0.299282 -v -0.287140 0.384375 -1.727460 -v -0.558320 0.384375 -1.662080 -v -0.810180 0.384375 -1.557220 -v -1.039360 0.384375 -1.416240 -v -1.242500 0.384375 -1.242500 -v -1.416240 0.384375 -1.039360 -v -1.557220 0.384375 -0.810180 -v -1.662080 0.384375 -0.558320 -v -1.727460 0.384375 -0.287140 -v -1.750000 0.384375 0.000000 -v -0.274998 0.316800 -1.654413 -v -0.534711 0.316800 -1.591798 -v -0.775921 0.316800 -1.491372 -v -0.995410 0.316800 -1.356353 -v -1.189960 0.316800 -1.189960 -v -1.356353 0.316800 -0.995410 -v -1.491372 0.316800 -0.775921 -v -1.591798 0.316800 -0.534711 -v -1.654413 0.316800 -0.274998 -v -1.676000 0.316800 0.000000 -v -0.263841 0.260025 -1.587289 -v -0.513016 0.260025 -1.527214 -v -0.744440 0.260025 -1.430863 -v -0.955023 0.260025 -1.301322 -v -1.141680 0.260025 -1.141680 -v -1.301322 0.260025 -0.955023 -v -1.430863 0.260025 -0.744440 -v -1.527214 0.260025 -0.513016 -v -1.587289 0.260025 -0.263841 -v -0.254652 0.213600 -1.532010 -v -0.495150 0.213600 -1.474028 -v -0.718514 0.213600 -1.381032 -v -0.921764 0.213600 -1.256003 -v -1.101920 0.213600 -1.101920 -v -1.256003 0.213600 -0.921764 -v -1.381032 0.213600 -0.718514 -v -1.474028 0.213600 -0.495150 -v -1.532010 0.213600 -0.254652 -v -0.248417 0.177075 -1.494500 -v -0.483027 0.177075 -1.437937 -v -0.700921 0.177075 -1.347218 -v -0.899195 0.177075 -1.225250 -v -1.074940 0.177075 -1.074940 -v -1.225250 0.177075 -0.899195 -v -1.347218 0.177075 -0.700921 -v -1.437937 0.177075 -0.483027 -v -1.494500 0.177075 -0.248417 -v -1.514000 0.177075 0.000000 -v -0.478560 0.150000 -1.424640 -v -0.694440 0.150000 -1.334760 -v -0.890880 0.150000 -1.213920 -v -1.065000 0.150000 -1.065000 -v -1.213920 0.150000 -0.890880 -v -1.334760 0.150000 -0.694440 -v -1.424640 0.150000 -0.478560 -v -1.480680 0.150000 -0.246120 -v -1.960420 0.771675 0.325863 -v -1.986000 0.771675 0.000000 -v -1.886223 0.771675 0.633613 -v -1.899520 0.900000 0.638080 -v -1.767222 0.771675 0.919439 -v -1.779680 0.900000 0.925920 -v -1.607230 0.771675 1.179525 -v -1.410060 0.771675 1.410060 -v -1.179525 0.771675 1.607230 -v -1.187840 0.900000 1.618560 -v -0.919439 0.771675 1.767222 -v -0.633613 0.771675 1.886223 -v -0.325863 0.771675 1.960420 -v -0.328160 0.900000 1.974240 -v -1.922910 0.656400 0.319628 -v -1.948000 0.656400 0.000000 -v -1.850132 0.656400 0.621490 -v -1.733408 0.656400 0.901846 -v -1.576477 0.656400 1.156956 -v -1.383080 0.656400 1.383080 -v -1.156956 0.656400 1.576477 -v -0.901846 0.656400 1.733408 -v -0.621490 0.656400 1.850132 -v -0.319628 0.656400 1.922910 -v -1.867631 0.553725 0.310439 -v -1.892000 0.553725 0.000000 -v -1.796946 0.553725 0.603624 -v -1.683577 0.553725 0.875920 -v -1.531158 0.553725 1.123697 -v -1.343320 0.553725 1.343320 -v -1.123697 0.553725 1.531158 -v -0.875920 0.553725 1.683577 -v -0.603624 0.553725 1.796946 -v -0.310439 0.553725 1.867631 -v -1.800507 0.463200 0.299282 -v -1.824000 0.463200 0.000000 -v -1.732362 0.463200 0.581929 -v -1.623068 0.463200 0.844439 -v -1.476127 0.463200 1.083310 -v -1.295040 0.463200 1.295040 -v -1.083310 0.463200 1.476127 -v -0.844439 0.463200 1.623068 -v -0.581929 0.463200 1.732362 -v -0.299282 0.463200 1.800507 -v 0.000000 0.463200 1.824000 -v -1.727460 0.384375 0.287140 -v -1.662080 0.384375 0.558320 -v -1.557220 0.384375 0.810180 -v -1.416240 0.384375 1.039360 -v -1.242500 0.384375 1.242500 -v -1.039360 0.384375 1.416240 -v -0.810180 0.384375 1.557220 -v -0.558320 0.384375 1.662080 -v -0.287140 0.384375 1.727460 -v -1.654413 0.316800 0.274998 -v -1.591798 0.316800 0.534711 -v -1.491372 0.316800 0.775921 -v -1.356353 0.316800 0.995410 -v -1.189960 0.316800 1.189960 -v -0.995410 0.316800 1.356353 -v -0.775921 0.316800 1.491372 -v -0.534711 0.316800 1.591798 -v -0.274998 0.316800 1.654413 -v -1.587289 0.260025 0.263841 -v -1.608000 0.260025 0.000000 -v -1.527214 0.260025 0.513016 -v -1.430863 0.260025 0.744440 -v -1.301322 0.260025 0.955023 -v -1.141680 0.260025 1.141680 -v -0.955023 0.260025 1.301322 -v -0.744440 0.260025 1.430863 -v -0.513016 0.260025 1.527214 -v -0.263841 0.260025 1.587289 -v -1.532010 0.213600 0.254652 -v -1.552000 0.213600 0.000000 -v -1.474028 0.213600 0.495150 -v -1.381032 0.213600 0.718514 -v -1.256003 0.213600 0.921764 -v -1.101920 0.213600 1.101920 -v -0.921764 0.213600 1.256003 -v -0.718514 0.213600 1.381032 -v -0.495150 0.213600 1.474028 -v -0.254652 0.213600 1.532010 -v -1.494500 0.177075 0.248417 -v -1.437937 0.177075 0.483027 -v -1.347218 0.177075 0.700921 -v -1.225250 0.177075 0.899195 -v -1.074940 0.177075 1.074940 -v -0.899195 0.177075 1.225250 -v -0.700921 0.177075 1.347218 -v -0.483027 0.177075 1.437937 -v -0.248417 0.177075 1.494500 -v -1.480680 0.150000 0.246120 -v -1.500000 0.150000 0.000000 -v -1.213920 0.150000 0.890880 -v -1.065000 0.150000 1.065000 -v -0.694440 0.150000 1.334760 -v -0.246120 0.150000 1.480680 -v 0.000000 0.150000 1.500000 -v 0.325863 0.771675 1.960420 -v 0.000000 0.771675 1.986000 -v 0.633613 0.771675 1.886223 -v 0.638080 0.900000 1.899520 -v 0.919439 0.771675 1.767222 -v 0.925920 0.900000 1.779680 -v 1.179525 0.771675 1.607230 -v 1.187840 0.900000 1.618560 -v 1.410060 0.771675 1.410060 -v 1.607230 0.771675 1.179525 -v 1.618560 0.900000 1.187840 -v 1.767222 0.771675 0.919439 -v 1.886223 0.771675 0.633613 -v 1.960420 0.771675 0.325863 -v 1.986000 0.771675 0.000000 -v 0.319628 0.656400 1.922910 -v 0.000000 0.656400 1.948000 -v 0.621490 0.656400 1.850132 -v 0.901846 0.656400 1.733408 -v 1.156956 0.656400 1.576477 -v 1.383080 0.656400 1.383080 -v 1.576477 0.656400 1.156956 -v 1.733408 0.656400 0.901846 -v 1.850132 0.656400 0.621490 -v 1.922910 0.656400 0.319628 -v 1.948000 0.656400 0.000000 -v 0.310439 0.553725 1.867631 -v 0.000000 0.553725 1.892000 -v 0.603624 0.553725 1.796946 -v 0.875920 0.553725 1.683577 -v 1.123697 0.553725 1.531158 -v 1.343320 0.553725 1.343320 -v 1.531158 0.553725 1.123697 -v 1.683577 0.553725 0.875920 -v 1.796946 0.553725 0.603624 -v 1.867631 0.553725 0.310439 -v 0.299282 0.463200 1.800507 -v 0.581929 0.463200 1.732362 -v 0.844439 0.463200 1.623068 -v 1.083310 0.463200 1.476127 -v 1.295040 0.463200 1.295040 -v 1.476127 0.463200 1.083310 -v 1.623068 0.463200 0.844439 -v 1.732362 0.463200 0.581929 -v 1.800507 0.463200 0.299282 -v 0.287140 0.384375 1.727460 -v 0.000000 0.384375 1.750000 -v 0.558320 0.384375 1.662080 -v 0.810180 0.384375 1.557220 -v 1.039360 0.384375 1.416240 -v 1.242500 0.384375 1.242500 -v 1.416240 0.384375 1.039360 -v 1.557220 0.384375 0.810180 -v 1.662080 0.384375 0.558320 -v 1.727460 0.384375 0.287140 -v 0.274998 0.316800 1.654413 -v 0.000000 0.316800 1.676000 -v 0.534711 0.316800 1.591798 -v 0.775921 0.316800 1.491372 -v 0.995410 0.316800 1.356353 -v 1.189960 0.316800 1.189960 -v 1.356353 0.316800 0.995410 -v 1.491372 0.316800 0.775921 -v 1.591798 0.316800 0.534711 -v 1.654413 0.316800 0.274998 -v 0.263841 0.260025 1.587289 -v 0.000000 0.260025 1.608000 -v 0.513016 0.260025 1.527214 -v 0.744440 0.260025 1.430863 -v 0.955023 0.260025 1.301322 -v 1.141680 0.260025 1.141680 -v 1.301322 0.260025 0.955023 -v 1.430863 0.260025 0.744440 -v 1.527214 0.260025 0.513016 -v 1.587289 0.260025 0.263841 -v 0.254652 0.213600 1.532010 -v 0.000000 0.213600 1.552000 -v 0.495150 0.213600 1.474028 -v 0.718514 0.213600 1.381032 -v 0.921764 0.213600 1.256003 -v 1.101920 0.213600 1.101920 -v 1.256003 0.213600 0.921764 -v 1.381032 0.213600 0.718514 -v 1.474028 0.213600 0.495150 -v 1.532010 0.213600 0.254652 -v 0.248417 0.177075 1.494500 -v 0.000000 0.177075 1.514000 -v 0.483027 0.177075 1.437937 -v 0.700921 0.177075 1.347218 -v 0.899195 0.177075 1.225250 -v 1.074940 0.177075 1.074940 -v 1.225250 0.177075 0.899195 -v 1.347218 0.177075 0.700921 -v 1.437937 0.177075 0.483027 -v 1.494500 0.177075 0.248417 -v 0.246120 0.150000 1.480680 -v 0.694440 0.150000 1.334760 -v 1.065000 0.150000 1.065000 -v 1.480680 0.150000 0.246120 -v -1.800455 2.031069 -0.081000 -v -1.597200 2.031300 -0.081000 -v -1.600000 2.025000 0.000000 -v -1.800900 2.024775 0.000000 -v -1.799246 2.048152 -0.144000 -v -1.589600 2.048400 -0.144000 -v -1.797466 2.073326 -0.189000 -v -1.578400 2.073600 -0.189000 -v -1.795303 2.103896 -0.216000 -v -1.564800 2.104200 -0.216000 -v -1.792950 2.137163 -0.225000 -v -1.550000 2.137500 -0.225000 -v -1.790597 2.170429 -0.216000 -v -1.535200 2.170800 -0.216000 -v -1.788434 2.200999 -0.189000 -v -1.521600 2.201400 -0.189000 -v -1.786654 2.226173 -0.144000 -v -1.510400 2.226600 -0.144000 -v -1.785445 2.243256 -0.081000 -v -1.502800 2.243700 -0.081000 -v -1.785000 2.249550 0.000000 -v -1.500000 2.250000 0.000000 -v -1.984790 2.029450 -0.081000 -v -1.983200 2.023200 0.000000 -v -1.989107 2.046413 -0.144000 -v -1.995469 2.071411 -0.189000 -v -2.003194 2.101766 -0.216000 -v -2.011600 2.134800 -0.225000 -v -2.020006 2.167834 -0.216000 -v -2.027731 2.198189 -0.189000 -v -2.034093 2.223187 -0.144000 -v -2.038410 2.240150 -0.081000 -v -2.149624 2.025055 -0.081000 -v -2.146300 2.018925 0.000000 -v -2.158645 2.041693 -0.144000 -v -2.171939 2.066213 -0.189000 -v -2.188082 2.095987 -0.216000 -v -2.205650 2.128388 -0.225000 -v -2.223218 2.160788 -0.216000 -v -2.239361 2.190562 -0.189000 -v -2.252655 2.215082 -0.144000 -v -2.261676 2.231720 -0.081000 -v -2.265000 2.237850 0.000000 -v -2.294371 2.016497 -0.081000 -v -2.307322 2.032502 -0.144000 -v -2.326406 2.056090 -0.189000 -v -2.349581 2.084731 -0.216000 -v -2.374800 2.115900 -0.225000 -v -2.400019 2.147069 -0.216000 -v -2.423194 2.175710 -0.189000 -v -2.442278 2.199298 -0.144000 -v -2.455229 2.215303 -0.081000 -v -2.418450 2.002387 -0.081000 -v -2.434600 2.017350 -0.144000 -v -2.458400 2.039400 -0.189000 -v -2.487300 2.066175 -0.216000 -v -2.518750 2.095312 -0.225000 -v -2.550200 2.124450 -0.216000 -v -2.579100 2.151225 -0.189000 -v -2.602900 2.173275 -0.144000 -v -2.619050 2.188238 -0.081000 -v -2.625000 2.193750 0.000000 -v -2.521277 1.981339 -0.081000 -v -2.539942 1.994746 -0.144000 -v -2.567450 2.014502 -0.189000 -v -2.600851 2.038493 -0.216000 -v -2.637200 2.064600 -0.225000 -v -2.673549 2.090707 -0.216000 -v -2.706950 2.114698 -0.189000 -v -2.734458 2.134454 -0.144000 -v -2.753123 2.147861 -0.081000 -v -2.602268 1.951964 -0.081000 -v -2.594700 1.947825 0.000000 -v -2.622811 1.963199 -0.144000 -v -2.653085 1.979755 -0.189000 -v -2.689846 1.999859 -0.216000 -v -2.729850 2.021737 -0.225000 -v -2.769854 2.043616 -0.216000 -v -2.806615 2.063720 -0.189000 -v -2.836889 2.080276 -0.144000 -v -2.857432 2.091511 -0.081000 -v -2.865000 2.095650 0.000000 -v -2.660842 1.912874 -0.081000 -v -2.682669 1.921219 -0.144000 -v -2.714835 1.933517 -0.189000 -v -2.753894 1.948450 -0.216000 -v -2.796400 1.964700 -0.225000 -v -2.838906 1.980950 -0.216000 -v -2.877965 1.995883 -0.189000 -v -2.910131 2.008181 -0.144000 -v -2.931958 2.016526 -0.081000 -v -2.940000 2.019600 0.000000 -v -2.696413 1.862682 -0.081000 -v -2.688100 1.860975 0.000000 -v -2.718978 1.867316 -0.144000 -v -2.752230 1.874146 -0.189000 -v -2.792609 1.882438 -0.216000 -v -2.836550 1.891463 -0.225000 -v -2.880491 1.900487 -0.216000 -v -2.920870 1.908779 -0.189000 -v -2.954122 1.915609 -0.144000 -v -2.976687 1.920243 -0.081000 -v -2.708400 1.800000 -0.081000 -v -2.700000 1.800000 0.000000 -v -2.731200 1.800000 -0.144000 -v -2.764800 1.800000 -0.189000 -v -2.805600 1.800000 -0.216000 -v -2.850000 1.800000 -0.225000 -v -2.894400 1.800000 -0.216000 -v -2.935200 1.800000 -0.189000 -v -2.968800 1.800000 -0.144000 -v -2.991600 1.800000 -0.081000 -v -3.000000 1.800000 0.000000 -v -1.785445 2.243256 0.081000 -v -1.502800 2.243700 0.081000 -v -1.786654 2.226173 0.144000 -v -1.510400 2.226600 0.144000 -v -1.788434 2.200999 0.189000 -v -1.521600 2.201400 0.189000 -v -1.790597 2.170429 0.216000 -v -1.535200 2.170800 0.216000 -v -1.792950 2.137163 0.225000 -v -1.550000 2.137500 0.225000 -v -1.795303 2.103896 0.216000 -v -1.564800 2.104200 0.216000 -v -1.797466 2.073326 0.189000 -v -1.578400 2.073600 0.189000 -v -1.799246 2.048152 0.144000 -v -1.589600 2.048400 0.144000 -v -1.800455 2.031069 0.081000 -v -1.597200 2.031300 0.081000 -v -2.038410 2.240150 0.081000 -v -2.040000 2.246400 0.000000 -v -2.034093 2.223187 0.144000 -v -2.027731 2.198189 0.189000 -v -2.020006 2.167834 0.216000 -v -2.011600 2.134800 0.225000 -v -2.003194 2.101766 0.216000 -v -1.995469 2.071411 0.189000 -v -1.989107 2.046413 0.144000 -v -1.984790 2.029450 0.081000 -v -2.261676 2.231720 0.081000 -v -2.252655 2.215082 0.144000 -v -2.239361 2.190562 0.189000 -v -2.223218 2.160788 0.216000 -v -2.205650 2.128387 0.225000 -v -2.188082 2.095987 0.216000 -v -2.171939 2.066213 0.189000 -v -2.158645 2.041693 0.144000 -v -2.149624 2.025055 0.081000 -v -2.455229 2.215303 0.081000 -v -2.460000 2.221200 0.000000 -v -2.442278 2.199298 0.144000 -v -2.423194 2.175710 0.189000 -v -2.400019 2.147069 0.216000 -v -2.374800 2.115900 0.225000 -v -2.349581 2.084731 0.216000 -v -2.326406 2.056090 0.189000 -v -2.307322 2.032502 0.144000 -v -2.294371 2.016497 0.081000 -v -2.289600 2.010600 0.000000 -v -2.619050 2.188238 0.081000 -v -2.602900 2.173275 0.144000 -v -2.579100 2.151225 0.189000 -v -2.550200 2.124450 0.216000 -v -2.518750 2.095312 0.225000 -v -2.487300 2.066175 0.216000 -v -2.458400 2.039400 0.189000 -v -2.434600 2.017350 0.144000 -v -2.418450 2.002388 0.081000 -v -2.412500 1.996875 0.000000 -v -2.753123 2.147861 0.081000 -v -2.760000 2.152800 0.000000 -v -2.734458 2.134454 0.144000 -v -2.706950 2.114698 0.189000 -v -2.673549 2.090707 0.216000 -v -2.637200 2.064600 0.225000 -v -2.600851 2.038493 0.216000 -v -2.567450 2.014502 0.189000 -v -2.539942 1.994746 0.144000 -v -2.521277 1.981339 0.081000 -v -2.514400 1.976400 0.000000 -v -2.857432 2.091511 0.081000 -v -2.836889 2.080276 0.144000 -v -2.806615 2.063720 0.189000 -v -2.769854 2.043616 0.216000 -v -2.729850 2.021737 0.225000 -v -2.689846 1.999859 0.216000 -v -2.653085 1.979755 0.189000 -v -2.622811 1.963199 0.144000 -v -2.602268 1.951964 0.081000 -v -2.931958 2.016526 0.081000 -v -2.910131 2.008181 0.144000 -v -2.877965 1.995883 0.189000 -v -2.838906 1.980950 0.216000 -v -2.796400 1.964700 0.225000 -v -2.753894 1.948450 0.216000 -v -2.714835 1.933517 0.189000 -v -2.682669 1.921219 0.144000 -v -2.660842 1.912874 0.081000 -v -2.652800 1.909800 0.000000 -v -2.976687 1.920243 0.081000 -v -2.985000 1.921950 0.000000 -v -2.954122 1.915609 0.144000 -v -2.920870 1.908779 0.189000 -v -2.880491 1.900487 0.216000 -v -2.836550 1.891463 0.225000 -v -2.792609 1.882438 0.216000 -v -2.752230 1.874146 0.189000 -v -2.718978 1.867316 0.144000 -v -2.696413 1.862682 0.081000 -v -2.991600 1.800000 0.081000 -v -2.968800 1.800000 0.144000 -v -2.935200 1.800000 0.189000 -v -2.894400 1.800000 0.216000 -v -2.850000 1.800000 0.225000 -v -2.805600 1.800000 0.216000 -v -2.764800 1.800000 0.189000 -v -2.731200 1.800000 0.144000 -v -2.708400 1.800000 0.081000 -v -2.702175 1.724519 -0.081000 -v -2.693900 1.726200 0.000000 -v -2.724637 1.719956 -0.144000 -v -2.757739 1.713232 -0.189000 -v -2.797934 1.705067 -0.216000 -v -2.841675 1.696181 -0.225000 -v -2.885416 1.687296 -0.216000 -v -2.925611 1.679131 -0.189000 -v -2.958713 1.672406 -0.144000 -v -2.981175 1.667844 -0.081000 -v -2.989450 1.666162 0.000000 -v -2.683107 1.638610 -0.081000 -v -2.704570 1.630493 -0.144000 -v -2.736198 1.618531 -0.189000 -v -2.774605 1.604006 -0.216000 -v -2.816400 1.588200 -0.225000 -v -2.858195 1.572394 -0.216000 -v -2.896602 1.557869 -0.189000 -v -2.928230 1.545907 -0.144000 -v -2.949693 1.537790 -0.081000 -v -2.957600 1.534800 0.000000 -v -2.650604 1.544903 -0.081000 -v -2.643300 1.548900 0.000000 -v -2.670428 1.534053 -0.144000 -v -2.699644 1.518063 -0.189000 -v -2.735119 1.498648 -0.216000 -v -2.773725 1.477519 -0.225000 -v -2.812331 1.456390 -0.216000 -v -2.847806 1.436974 -0.189000 -v -2.877022 1.420985 -0.144000 -v -2.896846 1.410135 -0.081000 -v -2.604074 1.446029 -0.081000 -v -2.597600 1.450800 0.000000 -v -2.621645 1.433078 -0.144000 -v -2.647539 1.413994 -0.189000 -v -2.678982 1.390819 -0.216000 -v -2.713200 1.365600 -0.225000 -v -2.747418 1.340381 -0.216000 -v -2.778861 1.317206 -0.189000 -v -2.804755 1.298122 -0.144000 -v -2.822326 1.285171 -0.081000 -v -2.828800 1.280400 0.000000 -v -2.542925 1.344619 -0.081000 -v -2.537500 1.350000 0.000000 -v -2.557650 1.330013 -0.144000 -v -2.579350 1.308488 -0.189000 -v -2.605700 1.282350 -0.216000 -v -2.634375 1.253906 -0.225000 -v -2.663050 1.225463 -0.216000 -v -2.689400 1.199325 -0.189000 -v -2.711100 1.177800 -0.144000 -v -2.725825 1.163194 -0.081000 -v -2.731250 1.157813 0.000000 -v -2.466566 1.243303 -0.081000 -v -2.462400 1.249200 0.000000 -v -2.477875 1.227298 -0.144000 -v -2.494541 1.203710 -0.189000 -v -2.514778 1.175069 -0.216000 -v -2.536800 1.143900 -0.225000 -v -2.558822 1.112731 -0.216000 -v -2.579059 1.084090 -0.189000 -v -2.595725 1.060502 -0.144000 -v -2.607034 1.044497 -0.081000 -v -2.374406 1.144713 -0.081000 -v -2.371700 1.151100 0.000000 -v -2.381752 1.127376 -0.144000 -v -2.392576 1.101828 -0.189000 -v -2.405721 1.070804 -0.216000 -v -2.420025 1.037044 -0.225000 -v -2.434329 1.003283 -0.216000 -v -2.447474 0.972260 -0.189000 -v -2.458298 0.946711 -0.144000 -v -2.465644 0.929375 -0.081000 -v -2.468350 0.922987 0.000000 -v -2.265853 1.051478 -0.081000 -v -2.268710 1.032691 -0.144000 -v -2.272922 1.005005 -0.189000 -v -2.278035 0.971386 -0.216000 -v -2.283600 0.934800 -0.225000 -v -2.289165 0.898214 -0.216000 -v -2.294278 0.864595 -0.189000 -v -2.298490 0.836909 -0.144000 -v -2.301347 0.818122 -0.081000 -v -2.302400 0.811200 0.000000 -v -2.140315 0.966231 -0.081000 -v -2.138183 0.945685 -0.144000 -v -2.135041 0.915407 -0.189000 -v -2.131226 0.878641 -0.216000 -v -2.127075 0.838631 -0.225000 -v -2.122924 0.798621 -0.216000 -v -2.119109 0.761855 -0.189000 -v -2.115967 0.731578 -0.144000 -v -2.113835 0.711032 -0.081000 -v -1.997200 0.891600 -0.081000 -v -1.989600 0.868800 -0.144000 -v -1.978400 0.835200 -0.189000 -v -1.964800 0.794400 -0.216000 -v -1.950000 0.750000 -0.225000 -v -1.935200 0.705600 -0.216000 -v -1.921600 0.664800 -0.189000 -v -1.910400 0.631200 -0.144000 -v -1.902800 0.608400 -0.081000 -v -2.981175 1.667844 0.081000 -v -2.958713 1.672406 0.144000 -v -2.925611 1.679131 0.189000 -v -2.885416 1.687296 0.216000 -v -2.841675 1.696181 0.225000 -v -2.797934 1.705067 0.216000 -v -2.757739 1.713232 0.189000 -v -2.724637 1.719956 0.144000 -v -2.702175 1.724519 0.081000 -v -2.949693 1.537790 0.081000 -v -2.928230 1.545907 0.144000 -v -2.896602 1.557869 0.189000 -v -2.858195 1.572394 0.216000 -v -2.816400 1.588200 0.225000 -v -2.774605 1.604006 0.216000 -v -2.736198 1.618531 0.189000 -v -2.704570 1.630493 0.144000 -v -2.683107 1.638610 0.081000 -v -2.675200 1.641600 0.000000 -v -2.896846 1.410135 0.081000 -v -2.904150 1.406137 0.000000 -v -2.877022 1.420985 0.144000 -v -2.847806 1.436974 0.189000 -v -2.812331 1.456390 0.216000 -v -2.773725 1.477519 0.225000 -v -2.735119 1.498648 0.216000 -v -2.699644 1.518063 0.189000 -v -2.670428 1.534053 0.144000 -v -2.650604 1.544903 0.081000 -v -2.822326 1.285171 0.081000 -v -2.804755 1.298122 0.144000 -v -2.778861 1.317206 0.189000 -v -2.747418 1.340381 0.216000 -v -2.713200 1.365600 0.225000 -v -2.678982 1.390819 0.216000 -v -2.647539 1.413994 0.189000 -v -2.621645 1.433078 0.144000 -v -2.604074 1.446029 0.081000 -v -2.725825 1.163194 0.081000 -v -2.711100 1.177800 0.144000 -v -2.689400 1.199325 0.189000 -v -2.663050 1.225463 0.216000 -v -2.634375 1.253906 0.225000 -v -2.605700 1.282350 0.216000 -v -2.579350 1.308488 0.189000 -v -2.557650 1.330013 0.144000 -v -2.542925 1.344619 0.081000 -v -2.607034 1.044497 0.081000 -v -2.611200 1.038600 0.000000 -v -2.595725 1.060502 0.144000 -v -2.579059 1.084090 0.189000 -v -2.558822 1.112731 0.216000 -v -2.536800 1.143900 0.225000 -v -2.514778 1.175069 0.216000 -v -2.494541 1.203710 0.189000 -v -2.477875 1.227298 0.144000 -v -2.466566 1.243303 0.081000 -v -2.465644 0.929375 0.081000 -v -2.458298 0.946711 0.144000 -v -2.447474 0.972260 0.189000 -v -2.434329 1.003283 0.216000 -v -2.420025 1.037044 0.225000 -v -2.405721 1.070804 0.216000 -v -2.392576 1.101828 0.189000 -v -2.381752 1.127376 0.144000 -v -2.374406 1.144713 0.081000 -v -2.301347 0.818122 0.081000 -v -2.298490 0.836909 0.144000 -v -2.294278 0.864595 0.189000 -v -2.289165 0.898214 0.216000 -v -2.283600 0.934800 0.225000 -v -2.278035 0.971386 0.216000 -v -2.272922 1.005005 0.189000 -v -2.268710 1.032691 0.144000 -v -2.265853 1.051478 0.081000 -v -2.264800 1.058400 0.000000 -v -2.113835 0.711032 0.081000 -v -2.113050 0.703463 0.000000 -v -2.115967 0.731578 0.144000 -v -2.119109 0.761855 0.189000 -v -2.122924 0.798621 0.216000 -v -2.127075 0.838631 0.225000 -v -2.131226 0.878641 0.216000 -v -2.135041 0.915407 0.189000 -v -2.138183 0.945685 0.144000 -v -2.140315 0.966231 0.081000 -v -2.141100 0.973800 0.000000 -v -1.902800 0.608400 0.081000 -v -1.900000 0.600000 0.000000 -v -1.910400 0.631200 0.144000 -v -1.921600 0.664800 0.189000 -v -1.935200 0.705600 0.216000 -v -1.950000 0.750000 0.225000 -v -1.964800 0.794400 0.216000 -v -1.978400 0.835200 0.189000 -v -1.989600 0.868800 0.144000 -v -1.997200 0.891600 0.081000 -v 1.939394 1.423221 -0.175100 -v 1.700000 1.401900 -0.178200 -v 1.700000 1.425000 0.000000 -v 1.935900 1.444200 0.000000 -v 1.948879 1.366278 -0.311290 -v 1.700000 1.339200 -0.316800 -v 1.962857 1.282362 -0.408568 -v 1.700000 1.246800 -0.415800 -v 1.979830 1.180464 -0.466934 -v 1.700000 1.134600 -0.475200 -v 1.998300 1.069575 -0.486390 -v 1.700000 1.012500 -0.495000 -v 2.016770 0.958686 -0.466934 -v 1.700000 0.890400 -0.475200 -v 2.033743 0.856788 -0.408568 -v 1.700000 0.778200 -0.415800 -v 2.047721 0.772872 -0.311290 -v 1.700000 0.685800 -0.316800 -v 2.057206 0.715929 -0.175100 -v 1.700000 0.623100 -0.178200 -v 2.116979 1.479120 -0.166687 -v 2.111200 1.497600 0.000000 -v 2.132666 1.428960 -0.296333 -v 2.155782 1.355040 -0.388937 -v 2.183853 1.265280 -0.444499 -v 2.214400 1.167600 -0.463020 -v 2.244947 1.069920 -0.444499 -v 2.273018 0.980160 -0.388937 -v 2.296134 0.906240 -0.296333 -v 2.311821 0.856080 -0.166687 -v 2.244457 1.563171 -0.154289 -v 2.263882 1.520478 -0.274291 -v 2.292510 1.457562 -0.360007 -v 2.327271 1.381164 -0.411437 -v 2.365100 1.298025 -0.428580 -v 2.402929 1.214886 -0.411437 -v 2.437690 1.138488 -0.360007 -v 2.466318 1.075572 -0.274291 -v 2.485743 1.032879 -0.154289 -v 2.333530 1.668948 -0.139234 -v 2.355053 1.634064 -0.247526 -v 2.386771 1.582656 -0.324878 -v 2.425286 1.520232 -0.371290 -v 2.467200 1.452300 -0.386760 -v 2.509114 1.384368 -0.371290 -v 2.547629 1.321944 -0.324878 -v 2.579347 1.270536 -0.247526 -v 2.600870 1.235652 -0.139234 -v 2.395900 1.790025 -0.122850 -v 2.418700 1.762950 -0.218400 -v 2.452300 1.723050 -0.286650 -v 2.493100 1.674600 -0.327600 -v 2.537500 1.621875 -0.341250 -v 2.581900 1.569150 -0.327600 -v 2.622700 1.520700 -0.286650 -v 2.656300 1.480800 -0.218400 -v 2.679100 1.453725 -0.122850 -v 2.443270 1.919976 -0.106466 -v 2.434400 1.927200 0.000000 -v 2.467347 1.900368 -0.189274 -v 2.502829 1.871472 -0.248422 -v 2.545914 1.836384 -0.283910 -v 2.592800 1.798200 -0.295740 -v 2.639686 1.760016 -0.283910 -v 2.682771 1.724928 -0.248422 -v 2.718253 1.696032 -0.189274 -v 2.742330 1.676424 -0.106466 -v 2.487343 2.052375 -0.091411 -v 2.513518 2.039550 -0.162509 -v 2.552090 2.020650 -0.213293 -v 2.598929 1.997700 -0.243763 -v 2.649900 1.972725 -0.253920 -v 2.700871 1.947750 -0.243763 -v 2.747710 1.924800 -0.213293 -v 2.786282 1.905900 -0.162509 -v 2.812457 1.893075 -0.091411 -v 2.539821 2.180796 -0.079013 -v 2.528800 2.183400 0.000000 -v 2.569734 2.173728 -0.140467 -v 2.613818 2.163312 -0.184363 -v 2.667347 2.150664 -0.210701 -v 2.725600 2.136900 -0.219480 -v 2.783853 2.123136 -0.210701 -v 2.837382 2.110488 -0.184363 -v 2.881466 2.100072 -0.140467 -v 2.911379 2.093004 -0.079013 -v 2.922400 2.090400 0.000000 -v 2.612406 2.298813 -0.070600 -v 2.648521 2.296134 -0.125510 -v 2.701743 2.292186 -0.164732 -v 2.766370 2.287392 -0.188266 -v 2.836700 2.282175 -0.196110 -v 2.907030 2.276958 -0.188266 -v 2.971657 2.272164 -0.164732 -v 3.024879 2.268216 -0.125510 -v 3.060994 2.265537 -0.070600 -v 3.074300 2.264550 0.000000 -v 2.762400 2.400000 -0.120000 -v 3.000000 2.400000 -0.187500 -v 3.237600 2.400000 -0.120000 -v 3.283200 2.400000 -0.067500 -v 2.057206 0.715929 0.175100 -v 1.700000 0.623100 0.178200 -v 1.700000 0.600000 0.000000 -v 2.060700 0.694950 0.000000 -v 2.047721 0.772872 0.311290 -v 1.700000 0.685800 0.316800 -v 2.033743 0.856788 0.408568 -v 1.700000 0.778200 0.415800 -v 2.016770 0.958686 0.466934 -v 1.700000 0.890400 0.475200 -v 1.998300 1.069575 0.486390 -v 1.700000 1.012500 0.495000 -v 1.979830 1.180464 0.466934 -v 1.700000 1.134600 0.475200 -v 1.962857 1.282362 0.408568 -v 1.700000 1.246800 0.415800 -v 1.948879 1.366278 0.311290 -v 1.700000 1.339200 0.316800 -v 1.939394 1.423221 0.175100 -v 1.700000 1.401900 0.178200 -v 2.311821 0.856080 0.166687 -v 2.317600 0.837600 0.000000 -v 2.296134 0.906240 0.296333 -v 2.273018 0.980160 0.388937 -v 2.244947 1.069920 0.444499 -v 2.214400 1.167600 0.463020 -v 2.183853 1.265280 0.444499 -v 2.155782 1.355040 0.388937 -v 2.132666 1.428960 0.296333 -v 2.116979 1.479120 0.166687 -v 2.485743 1.032879 0.154289 -v 2.492900 1.017150 0.000000 -v 2.466318 1.075572 0.274291 -v 2.437690 1.138488 0.360007 -v 2.402929 1.214886 0.411437 -v 2.365100 1.298025 0.428580 -v 2.327271 1.381164 0.411437 -v 2.292510 1.457562 0.360007 -v 2.263882 1.520478 0.274291 -v 2.244457 1.563171 0.154289 -v 2.237300 1.578900 0.000000 -v 2.600870 1.235652 0.139234 -v 2.608800 1.222800 0.000000 -v 2.579347 1.270536 0.247526 -v 2.547629 1.321944 0.324878 -v 2.509114 1.384368 0.371290 -v 2.467200 1.452300 0.386760 -v 2.425286 1.520232 0.371290 -v 2.386771 1.582656 0.324878 -v 2.355053 1.634064 0.247526 -v 2.333530 1.668948 0.139234 -v 2.325600 1.681800 0.000000 -v 2.679100 1.453725 0.122850 -v 2.687500 1.443750 0.000000 -v 2.656300 1.480800 0.218400 -v 2.622700 1.520700 0.286650 -v 2.581900 1.569150 0.327600 -v 2.537500 1.621875 0.341250 -v 2.493100 1.674600 0.327600 -v 2.452300 1.723050 0.286650 -v 2.418700 1.762950 0.218400 -v 2.395900 1.790025 0.122850 -v 2.387500 1.800000 0.000000 -v 2.742330 1.676424 0.106466 -v 2.751200 1.669200 0.000000 -v 2.718253 1.696032 0.189274 -v 2.682771 1.724928 0.248422 -v 2.639686 1.760016 0.283910 -v 2.592800 1.798200 0.295740 -v 2.545914 1.836384 0.283910 -v 2.502829 1.871472 0.248422 -v 2.467347 1.900368 0.189274 -v 2.443270 1.919976 0.106466 -v 2.812457 1.893075 0.091411 -v 2.822100 1.888350 0.000000 -v 2.786282 1.905900 0.162509 -v 2.747710 1.924800 0.213293 -v 2.700871 1.947750 0.243763 -v 2.649900 1.972725 0.253920 -v 2.598929 1.997700 0.243763 -v 2.552090 2.020650 0.213293 -v 2.513518 2.039550 0.162509 -v 2.487343 2.052375 0.091411 -v 2.477700 2.057100 0.000000 -v 2.911379 2.093004 0.079013 -v 2.881466 2.100072 0.140467 -v 2.837382 2.110488 0.184363 -v 2.783853 2.123136 0.210701 -v 2.725600 2.136900 0.219480 -v 2.667347 2.150664 0.210701 -v 2.613818 2.163312 0.184363 -v 2.569734 2.173728 0.140467 -v 2.539821 2.180796 0.079013 -v 3.060994 2.265537 0.070600 -v 3.024879 2.268216 0.125510 -v 2.971657 2.272164 0.164732 -v 2.907030 2.276958 0.188266 -v 2.836700 2.282175 0.196110 -v 2.766370 2.287392 0.188266 -v 2.701743 2.292186 0.164732 -v 2.648521 2.296134 0.125510 -v 2.612406 2.298813 0.070600 -v 2.599100 2.299800 0.000000 -v 3.283200 2.400000 0.067500 -v 3.300000 2.400000 0.000000 -v 3.237600 2.400000 0.120000 -v 3.170400 2.400000 0.157500 -v 3.088800 2.400000 0.180000 -v 3.000000 2.400000 0.187500 -v 2.911200 2.400000 0.180000 -v 2.829600 2.400000 0.157500 -v 2.762400 2.400000 0.120000 -v 2.716800 2.400000 0.067500 -v 2.747407 2.420406 -0.066744 -v 2.716800 2.400000 -0.067500 -v 2.700000 2.400000 0.000000 -v 2.729800 2.420250 0.000000 -v 2.795198 2.420829 -0.118656 -v 2.865626 2.421453 -0.155736 -v 2.829600 2.400000 -0.157500 -v 2.951146 2.422210 -0.177984 -v 2.911200 2.400000 -0.180000 -v 3.044212 2.423034 -0.185400 -v 3.137279 2.423859 -0.177984 -v 3.088800 2.400000 -0.180000 -v 3.222799 2.424616 -0.155736 -v 3.170400 2.400000 -0.157500 -v 3.293227 2.425240 -0.118656 -v 3.341018 2.425663 -0.066744 -v 3.358625 2.425819 0.000000 -v 2.776365 2.436302 -0.064692 -v 2.758400 2.436000 0.000000 -v 2.825126 2.437123 -0.115008 -v 2.896986 2.438333 -0.150948 -v 2.984243 2.439802 -0.172512 -v 3.079200 2.441400 -0.179700 -v 3.174157 2.442998 -0.172512 -v 3.261414 2.444467 -0.150948 -v 3.333274 2.445677 -0.115008 -v 3.382035 2.446498 -0.064692 -v 3.400000 2.446800 0.000000 -v 2.802528 2.447680 -0.061668 -v 2.784600 2.447250 0.000000 -v 2.851189 2.448847 -0.109632 -v 2.922899 2.450567 -0.143892 -v 3.009977 2.452655 -0.164448 -v 3.104737 2.454928 -0.171300 -v 3.199498 2.457201 -0.164448 -v 3.286576 2.459289 -0.143892 -v 3.358286 2.461009 -0.109632 -v 3.406947 2.462176 -0.061668 -v 3.424875 2.462606 0.000000 -v 2.824750 2.454529 -0.057996 -v 2.807200 2.454000 0.000000 -v 2.872387 2.455966 -0.103104 -v 2.942589 2.458082 -0.135324 -v 3.027834 2.460653 -0.154656 -v 3.120600 2.463450 -0.161100 -v 3.213366 2.466247 -0.154656 -v 3.298611 2.468818 -0.135324 -v 3.368813 2.470934 -0.103104 -v 3.416450 2.472371 -0.057996 -v 3.434000 2.472900 0.000000 -v 2.841887 2.456841 -0.054000 -v 2.887725 2.458444 -0.096000 -v 2.955275 2.460806 -0.126000 -v 3.037300 2.463675 -0.144000 -v 3.126562 2.466797 -0.150000 -v 3.215825 2.469919 -0.144000 -v 3.297850 2.472787 -0.126000 -v 3.365400 2.475150 -0.096000 -v 3.411237 2.476753 -0.054000 -v 2.852794 2.454605 -0.050004 -v 2.896205 2.456246 -0.088896 -v 2.960179 2.458666 -0.116676 -v 3.037862 2.461603 -0.133344 -v 3.122400 2.464800 -0.138900 -v 3.206938 2.467997 -0.133344 -v 3.284621 2.470934 -0.116676 -v 3.348595 2.473354 -0.088896 -v 3.392006 2.474995 -0.050004 -v 2.856323 2.447812 -0.046332 -v 2.896829 2.449338 -0.082368 -v 2.956523 2.451588 -0.108108 -v 3.029007 2.454319 -0.123552 -v 3.107887 2.457291 -0.128700 -v 3.186768 2.460263 -0.123552 -v 3.259252 2.462994 -0.108108 -v 3.318946 2.465243 -0.082368 -v 3.359452 2.466769 -0.046332 -v 3.374375 2.467331 0.000000 -v 2.851331 2.436454 -0.043308 -v 2.888602 2.437685 -0.076992 -v 2.943526 2.439499 -0.101052 -v 3.010221 2.441702 -0.115488 -v 3.082800 2.444100 -0.120300 -v 3.155379 2.446498 -0.115488 -v 3.222074 2.448701 -0.101052 -v 3.276998 2.450515 -0.076992 -v 3.314269 2.451746 -0.043308 -v 2.836672 2.420519 -0.041256 -v 2.870524 2.421250 -0.073344 -v 2.920412 2.422328 -0.096264 -v 2.980990 2.423636 -0.110016 -v 3.046912 2.425059 -0.114600 -v 3.112835 2.426483 -0.110016 -v 3.173413 2.427791 -0.096264 -v 3.223301 2.428868 -0.073344 -v 3.257153 2.429599 -0.041256 -v 2.811200 2.400000 -0.040500 -v 2.800000 2.400000 0.000000 -v 2.841600 2.400000 -0.072000 -v 2.886400 2.400000 -0.094500 -v 2.940800 2.400000 -0.108000 -v 3.000000 2.400000 -0.112500 -v 3.059200 2.400000 -0.108000 -v 3.113600 2.400000 -0.094500 -v 3.158400 2.400000 -0.072000 -v 3.188800 2.400000 -0.040500 -v 3.200000 2.400000 0.000000 -v 3.341018 2.425663 0.066744 -v 3.293227 2.425240 0.118656 -v 3.222799 2.424616 0.155736 -v 3.137279 2.423859 0.177984 -v 3.044212 2.423034 0.185400 -v 2.951146 2.422210 0.177984 -v 2.865626 2.421453 0.155736 -v 2.795198 2.420829 0.118656 -v 2.747407 2.420406 0.066744 -v 3.382035 2.446498 0.064692 -v 3.333274 2.445677 0.115008 -v 3.261414 2.444467 0.150948 -v 3.174157 2.442998 0.172512 -v 3.079200 2.441400 0.179700 -v 2.984243 2.439802 0.172512 -v 2.896986 2.438333 0.150948 -v 2.825126 2.437123 0.115008 -v 2.776365 2.436302 0.064692 -v 3.406947 2.462176 0.061668 -v 3.358286 2.461009 0.109632 -v 3.286576 2.459289 0.143892 -v 3.199498 2.457201 0.164448 -v 3.104738 2.454928 0.171300 -v 3.009977 2.452655 0.164448 -v 2.922899 2.450567 0.143892 -v 2.851189 2.448847 0.109632 -v 2.802528 2.447680 0.061668 -v 3.416450 2.472371 0.057996 -v 3.368813 2.470934 0.103104 -v 3.298611 2.468818 0.135324 -v 3.213366 2.466247 0.154656 -v 3.120600 2.463450 0.161100 -v 3.027834 2.460653 0.154656 -v 2.942589 2.458082 0.135324 -v 2.872387 2.455966 0.103104 -v 2.824750 2.454529 0.057996 -v 3.411237 2.476753 0.054000 -v 3.428125 2.477344 0.000000 -v 3.365400 2.475150 0.096000 -v 3.297850 2.472788 0.126000 -v 3.215825 2.469919 0.144000 -v 3.126562 2.466797 0.150000 -v 3.037300 2.463675 0.144000 -v 2.955275 2.460806 0.126000 -v 2.887725 2.458444 0.096000 -v 2.841887 2.456841 0.054000 -v 2.825000 2.456250 0.000000 -v 3.392006 2.474995 0.050004 -v 3.408000 2.475600 0.000000 -v 3.348595 2.473354 0.088896 -v 3.284621 2.470934 0.116676 -v 3.206938 2.467997 0.133344 -v 3.122400 2.464800 0.138900 -v 3.037862 2.461603 0.133344 -v 2.960179 2.458666 0.116676 -v 2.896205 2.456246 0.088896 -v 2.852794 2.454605 0.050004 -v 2.836800 2.454000 0.000000 -v 3.359452 2.466769 0.046332 -v 3.318946 2.465243 0.082368 -v 3.259252 2.462994 0.108108 -v 3.186768 2.460263 0.123552 -v 3.107887 2.457291 0.128700 -v 3.029007 2.454319 0.123552 -v 2.956523 2.451588 0.108108 -v 2.896829 2.449338 0.082368 -v 2.856323 2.447812 0.046332 -v 2.841400 2.447250 0.000000 -v 3.314269 2.451746 0.043308 -v 3.328000 2.452200 0.000000 -v 3.276998 2.450515 0.076992 -v 3.222074 2.448701 0.101052 -v 3.155379 2.446498 0.115488 -v 3.082800 2.444100 0.120300 -v 3.010221 2.441702 0.115488 -v 2.943526 2.439499 0.101052 -v 2.888602 2.437685 0.076992 -v 2.851331 2.436454 0.043308 -v 2.837600 2.436000 0.000000 -v 3.257153 2.429599 0.041256 -v 3.269625 2.429869 0.000000 -v 3.223301 2.428868 0.073344 -v 3.173413 2.427791 0.096264 -v 3.112835 2.426483 0.110016 -v 3.046912 2.425059 0.114600 -v 2.980990 2.423636 0.110016 -v 2.920412 2.422328 0.096264 -v 2.870524 2.421250 0.073344 -v 2.836672 2.420519 0.041256 -v 2.824200 2.420250 0.000000 -v 3.188800 2.400000 0.040500 -v 3.158400 2.400000 0.072000 -v 3.113600 2.400000 0.094500 -v 3.059200 2.400000 0.108000 -v 3.000000 2.400000 0.112500 -v 2.940800 2.400000 0.108000 -v 2.886400 2.400000 0.094500 -v 2.841600 2.400000 0.072000 -v 2.811200 2.400000 0.040500 -v 0.000000 3.150000 0.000000 -v 0.194600 3.141450 0.000000 -v 0.192107 3.141450 -0.032048 -v 0.184870 3.141450 -0.062272 -v 0.173255 3.141450 -0.090306 -v 0.157626 3.141450 -0.115787 -v 0.138348 3.141450 -0.138348 -v 0.115787 3.141450 -0.157626 -v 0.090306 3.141450 -0.173255 -v 0.062272 3.141450 -0.184870 -v 0.032048 3.141450 -0.192107 -v 0.304843 3.117600 -0.050855 -v 0.308800 3.117600 0.000000 -v 0.293360 3.117600 -0.098814 -v 0.274928 3.117600 -0.143301 -v 0.250127 3.117600 -0.183734 -v 0.219536 3.117600 -0.219536 -v 0.183734 3.117600 -0.250127 -v 0.143301 3.117600 -0.274928 -v 0.098814 3.117600 -0.293360 -v 0.050855 3.117600 -0.304843 -v 0.353610 3.081150 -0.058988 -v 0.340289 3.081150 -0.114619 -v 0.318907 3.081150 -0.166221 -v 0.290138 3.081150 -0.213123 -v 0.254653 3.081150 -0.254653 -v 0.213123 3.081150 -0.290138 -v 0.166221 3.081150 -0.318907 -v 0.114619 3.081150 -0.340289 -v 0.058988 3.081150 -0.353610 -v 0.353807 3.034800 -0.059016 -v 0.340477 3.034800 -0.114676 -v 0.319082 3.034800 -0.166306 -v 0.290295 3.034800 -0.213234 -v 0.254788 3.034800 -0.254788 -v 0.213234 3.034800 -0.290295 -v 0.166306 3.034800 -0.319082 -v 0.114676 3.034800 -0.340477 -v 0.059016 3.034800 -0.353807 -v 0.320834 2.981250 -0.053508 -v 0.325000 2.981250 0.000000 -v 0.308744 2.981250 -0.103976 -v 0.289340 2.981250 -0.150793 -v 0.263232 2.981250 -0.193348 -v 0.231031 2.981250 -0.231031 -v 0.193348 2.981250 -0.263232 -v 0.150793 2.981250 -0.289340 -v 0.103976 2.981250 -0.308744 -v 0.053508 2.981250 -0.320834 -v 0.270092 2.923200 -0.045032 -v 0.259910 2.923200 -0.087511 -v 0.243569 2.923200 -0.126920 -v 0.221585 2.923200 -0.162745 -v 0.194472 2.923200 -0.194472 -v 0.162745 2.923200 -0.221585 -v 0.126920 2.923200 -0.243569 -v 0.087511 2.923200 -0.259910 -v 0.045032 2.923200 -0.270092 -v 0.216979 2.863350 -0.036157 -v 0.208794 2.863350 -0.070270 -v 0.195658 2.863350 -0.101925 -v 0.177989 2.863350 -0.130707 -v 0.156200 2.863350 -0.156200 -v 0.130707 2.863350 -0.177989 -v 0.101925 2.863350 -0.195658 -v 0.070270 2.863350 -0.208794 -v 0.036157 2.863350 -0.216979 -v 0.000000 2.863350 -0.219800 -v 0.176897 2.804400 -0.029450 -v 0.170215 2.804400 -0.057246 -v 0.159496 2.804400 -0.083047 -v 0.145078 2.804400 -0.106513 -v 0.127304 2.804400 -0.127304 -v 0.106513 2.804400 -0.145078 -v 0.083047 2.804400 -0.159496 -v 0.057246 2.804400 -0.170215 -v 0.029450 2.804400 -0.176897 -v 0.000000 2.804400 -0.179200 -v 0.165245 2.749050 -0.027480 -v 0.158995 2.749050 -0.053428 -v 0.148969 2.749050 -0.077523 -v 0.135489 2.749050 -0.099446 -v 0.118874 2.749050 -0.118874 -v 0.099446 2.749050 -0.135489 -v 0.077523 2.749050 -0.148969 -v 0.053428 2.749050 -0.158995 -v 0.027480 2.749050 -0.165245 -v 0.000000 2.749050 -0.167400 -v 0.197424 2.700000 -0.032816 -v 0.200000 2.700000 0.000000 -v 0.161856 2.700000 -0.118784 -v 0.092592 2.700000 -0.177968 -v 0.063808 2.700000 -0.189952 -v 0.032816 2.700000 -0.197424 -v 0.000000 2.700000 -0.200000 -v 0.000000 3.141450 -0.194600 -v -0.032048 3.141450 -0.192107 -v -0.062272 3.141450 -0.184870 -v -0.090306 3.141450 -0.173255 -v -0.115787 3.141450 -0.157626 -v -0.138348 3.141450 -0.138348 -v -0.157626 3.141450 -0.115787 -v -0.173255 3.141450 -0.090306 -v -0.184870 3.141450 -0.062272 -v -0.192107 3.141450 -0.032048 -v -0.050855 3.117600 -0.304843 -v 0.000000 3.117600 -0.308800 -v -0.098814 3.117600 -0.293360 -v -0.143301 3.117600 -0.274928 -v -0.183734 3.117600 -0.250127 -v -0.219536 3.117600 -0.219536 -v -0.250127 3.117600 -0.183734 -v -0.274928 3.117600 -0.143301 -v -0.293360 3.117600 -0.098814 -v -0.304843 3.117600 -0.050855 -v -0.308800 3.117600 0.000000 -v -0.058988 3.081150 -0.353610 -v 0.000000 3.081150 -0.358200 -v -0.114619 3.081150 -0.340289 -v -0.166221 3.081150 -0.318907 -v -0.213123 3.081150 -0.290138 -v -0.254653 3.081150 -0.254653 -v -0.290138 3.081150 -0.213123 -v -0.318907 3.081150 -0.166221 -v -0.340289 3.081150 -0.114619 -v -0.353610 3.081150 -0.058988 -v -0.358200 3.081150 0.000000 -v -0.059016 3.034800 -0.353807 -v 0.000000 3.034800 -0.358400 -v -0.114676 3.034800 -0.340477 -v -0.166306 3.034800 -0.319082 -v -0.213234 3.034800 -0.290295 -v -0.254788 3.034800 -0.254788 -v -0.290295 3.034800 -0.213234 -v -0.319082 3.034800 -0.166306 -v -0.340477 3.034800 -0.114676 -v -0.353807 3.034800 -0.059016 -v -0.358400 3.034800 0.000000 -v -0.053508 2.981250 -0.320834 -v 0.000000 2.981250 -0.325000 -v -0.103976 2.981250 -0.308744 -v -0.150793 2.981250 -0.289340 -v -0.193348 2.981250 -0.263232 -v -0.231031 2.981250 -0.231031 -v -0.263232 2.981250 -0.193348 -v -0.289340 2.981250 -0.150793 -v -0.308744 2.981250 -0.103976 -v -0.320834 2.981250 -0.053508 -v -0.325000 2.981250 0.000000 -v -0.045032 2.923200 -0.270092 -v 0.000000 2.923200 -0.273600 -v -0.087511 2.923200 -0.259910 -v -0.126920 2.923200 -0.243569 -v -0.162745 2.923200 -0.221585 -v -0.194472 2.923200 -0.194472 -v -0.221585 2.923200 -0.162745 -v -0.243569 2.923200 -0.126920 -v -0.259910 2.923200 -0.087511 -v -0.270092 2.923200 -0.045032 -v -0.273600 2.923200 0.000000 -v -0.036157 2.863350 -0.216979 -v -0.070270 2.863350 -0.208794 -v -0.101925 2.863350 -0.195658 -v -0.130707 2.863350 -0.177989 -v -0.156200 2.863350 -0.156200 -v -0.177989 2.863350 -0.130707 -v -0.195658 2.863350 -0.101925 -v -0.208794 2.863350 -0.070270 -v -0.216979 2.863350 -0.036157 -v -0.219800 2.863350 0.000000 -v -0.029450 2.804400 -0.176897 -v -0.057246 2.804400 -0.170215 -v -0.083047 2.804400 -0.159496 -v -0.106513 2.804400 -0.145078 -v -0.127304 2.804400 -0.127304 -v -0.145078 2.804400 -0.106513 -v -0.159496 2.804400 -0.083047 -v -0.170215 2.804400 -0.057246 -v -0.176897 2.804400 -0.029450 -v -0.179200 2.804400 0.000000 -v -0.027480 2.749050 -0.165245 -v -0.053428 2.749050 -0.158995 -v -0.077523 2.749050 -0.148969 -v -0.099446 2.749050 -0.135489 -v -0.118874 2.749050 -0.118874 -v -0.135489 2.749050 -0.099446 -v -0.148969 2.749050 -0.077523 -v -0.158995 2.749050 -0.053428 -v -0.165245 2.749050 -0.027480 -v -0.167400 2.749050 0.000000 -v -0.032816 2.700000 -0.197424 -v -0.063808 2.700000 -0.189952 -v -0.092592 2.700000 -0.177968 -v -0.161856 2.700000 -0.118784 -v -0.177968 2.700000 -0.092592 -v -0.189952 2.700000 -0.063808 -v -0.197424 2.700000 -0.032816 -v -0.194600 3.141450 0.000000 -v -0.192107 3.141450 0.032048 -v -0.184870 3.141450 0.062272 -v -0.173255 3.141450 0.090306 -v -0.157626 3.141450 0.115787 -v -0.138348 3.141450 0.138348 -v -0.115787 3.141450 0.157626 -v -0.090306 3.141450 0.173255 -v -0.062272 3.141450 0.184870 -v -0.032048 3.141450 0.192107 -v 0.000000 3.141450 0.194600 -v -0.304843 3.117600 0.050855 -v -0.293360 3.117600 0.098814 -v -0.274928 3.117600 0.143301 -v -0.250127 3.117600 0.183734 -v -0.219536 3.117600 0.219536 -v -0.183734 3.117600 0.250127 -v -0.143301 3.117600 0.274928 -v -0.098814 3.117600 0.293360 -v -0.050855 3.117600 0.304843 -v 0.000000 3.117600 0.308800 -v -0.353610 3.081150 0.058988 -v -0.340289 3.081150 0.114619 -v -0.318907 3.081150 0.166221 -v -0.290138 3.081150 0.213123 -v -0.254653 3.081150 0.254653 -v -0.213123 3.081150 0.290138 -v -0.166221 3.081150 0.318907 -v -0.114619 3.081150 0.340289 -v -0.058988 3.081150 0.353610 -v -0.353807 3.034800 0.059016 -v -0.340477 3.034800 0.114676 -v -0.319082 3.034800 0.166306 -v -0.290295 3.034800 0.213234 -v -0.254788 3.034800 0.254788 -v -0.213234 3.034800 0.290295 -v -0.166306 3.034800 0.319082 -v -0.114676 3.034800 0.340477 -v -0.059016 3.034800 0.353807 -v -0.320834 2.981250 0.053508 -v -0.308744 2.981250 0.103976 -v -0.289340 2.981250 0.150793 -v -0.263232 2.981250 0.193348 -v -0.231031 2.981250 0.231031 -v -0.193348 2.981250 0.263232 -v -0.150793 2.981250 0.289340 -v -0.103976 2.981250 0.308744 -v -0.053508 2.981250 0.320834 -v 0.000000 2.981250 0.325000 -v -0.270092 2.923200 0.045032 -v -0.259910 2.923200 0.087511 -v -0.243569 2.923200 0.126920 -v -0.221585 2.923200 0.162745 -v -0.194472 2.923200 0.194472 -v -0.162745 2.923200 0.221585 -v -0.126920 2.923200 0.243569 -v -0.087511 2.923200 0.259910 -v -0.045032 2.923200 0.270092 -v 0.000000 2.923200 0.273600 -v -0.216979 2.863350 0.036157 -v -0.208794 2.863350 0.070270 -v -0.195658 2.863350 0.101925 -v -0.177989 2.863350 0.130707 -v -0.156200 2.863350 0.156200 -v -0.130707 2.863350 0.177989 -v -0.101925 2.863350 0.195658 -v -0.070270 2.863350 0.208794 -v -0.036157 2.863350 0.216979 -v 0.000000 2.863350 0.219800 -v -0.176897 2.804400 0.029450 -v -0.170215 2.804400 0.057246 -v -0.159496 2.804400 0.083047 -v -0.145078 2.804400 0.106513 -v -0.127304 2.804400 0.127304 -v -0.106513 2.804400 0.145078 -v -0.083047 2.804400 0.159496 -v -0.057246 2.804400 0.170215 -v -0.029450 2.804400 0.176897 -v 0.000000 2.804400 0.179200 -v -0.165245 2.749050 0.027480 -v -0.158995 2.749050 0.053428 -v -0.148969 2.749050 0.077523 -v -0.135489 2.749050 0.099446 -v -0.118874 2.749050 0.118874 -v -0.099446 2.749050 0.135489 -v -0.077523 2.749050 0.148969 -v -0.053428 2.749050 0.158995 -v -0.027480 2.749050 0.165245 -v 0.000000 2.749050 0.167400 -v -0.189952 2.700000 0.063808 -v -0.161856 2.700000 0.118784 -v -0.092592 2.700000 0.177968 -v 0.000000 2.700000 0.200000 -v 0.032048 3.141450 0.192107 -v 0.062272 3.141450 0.184870 -v 0.090306 3.141450 0.173255 -v 0.115787 3.141450 0.157626 -v 0.138348 3.141450 0.138348 -v 0.157626 3.141450 0.115787 -v 0.173255 3.141450 0.090306 -v 0.184870 3.141450 0.062272 -v 0.192107 3.141450 0.032048 -v 0.050855 3.117600 0.304843 -v 0.098814 3.117600 0.293360 -v 0.143301 3.117600 0.274928 -v 0.183734 3.117600 0.250127 -v 0.219536 3.117600 0.219536 -v 0.250127 3.117600 0.183734 -v 0.274928 3.117600 0.143301 -v 0.293360 3.117600 0.098814 -v 0.304843 3.117600 0.050855 -v 0.058988 3.081150 0.353610 -v 0.000000 3.081150 0.358200 -v 0.114619 3.081150 0.340289 -v 0.166221 3.081150 0.318907 -v 0.213123 3.081150 0.290138 -v 0.254653 3.081150 0.254653 -v 0.290138 3.081150 0.213123 -v 0.318907 3.081150 0.166221 -v 0.340289 3.081150 0.114619 -v 0.353610 3.081150 0.058988 -v 0.358200 3.081150 0.000000 -v 0.059016 3.034800 0.353807 -v 0.000000 3.034800 0.358400 -v 0.114676 3.034800 0.340477 -v 0.166306 3.034800 0.319082 -v 0.213234 3.034800 0.290295 -v 0.254788 3.034800 0.254788 -v 0.290295 3.034800 0.213234 -v 0.319082 3.034800 0.166306 -v 0.340477 3.034800 0.114676 -v 0.353807 3.034800 0.059016 -v 0.358400 3.034800 0.000000 -v 0.053508 2.981250 0.320834 -v 0.103976 2.981250 0.308744 -v 0.150793 2.981250 0.289340 -v 0.193348 2.981250 0.263232 -v 0.231031 2.981250 0.231031 -v 0.263232 2.981250 0.193348 -v 0.289340 2.981250 0.150793 -v 0.308744 2.981250 0.103976 -v 0.320834 2.981250 0.053508 -v 0.045032 2.923200 0.270092 -v 0.087511 2.923200 0.259910 -v 0.126920 2.923200 0.243569 -v 0.162745 2.923200 0.221585 -v 0.194472 2.923200 0.194472 -v 0.221585 2.923200 0.162745 -v 0.243569 2.923200 0.126920 -v 0.259910 2.923200 0.087511 -v 0.270092 2.923200 0.045032 -v 0.273600 2.923200 0.000000 -v 0.036157 2.863350 0.216979 -v 0.070270 2.863350 0.208794 -v 0.101925 2.863350 0.195658 -v 0.130707 2.863350 0.177989 -v 0.156200 2.863350 0.156200 -v 0.177989 2.863350 0.130707 -v 0.195658 2.863350 0.101925 -v 0.208794 2.863350 0.070270 -v 0.216979 2.863350 0.036157 -v 0.219800 2.863350 0.000000 -v 0.029450 2.804400 0.176897 -v 0.057246 2.804400 0.170215 -v 0.083047 2.804400 0.159496 -v 0.106513 2.804400 0.145078 -v 0.127304 2.804400 0.127304 -v 0.145078 2.804400 0.106513 -v 0.159496 2.804400 0.083047 -v 0.170215 2.804400 0.057246 -v 0.176897 2.804400 0.029450 -v 0.179200 2.804400 0.000000 -v 0.027480 2.749050 0.165245 -v 0.053428 2.749050 0.158995 -v 0.077523 2.749050 0.148969 -v 0.099446 2.749050 0.135489 -v 0.118874 2.749050 0.118874 -v 0.135489 2.749050 0.099446 -v 0.148969 2.749050 0.077523 -v 0.158995 2.749050 0.053428 -v 0.165245 2.749050 0.027480 -v 0.167400 2.749050 0.000000 -v 0.032816 2.700000 0.197424 -v 0.118784 2.700000 0.161856 -v 0.142000 2.700000 0.142000 -v 0.275801 2.659200 -0.045844 -v 0.279400 2.659200 0.000000 -v 0.265363 2.659200 -0.089140 -v 0.189952 2.700000 -0.063808 -v 0.248621 2.659200 -0.129351 -v 0.177968 2.700000 -0.092592 -v 0.226113 2.659200 -0.165941 -v 0.198374 2.659200 -0.198374 -v 0.142000 2.700000 -0.142000 -v 0.165941 2.659200 -0.226113 -v 0.118784 2.700000 -0.161856 -v 0.129351 2.659200 -0.248621 -v 0.089140 2.659200 -0.265363 -v 0.045844 2.659200 -0.275801 -v 0.000000 2.659200 -0.279400 -v 0.386161 2.625600 -0.064188 -v 0.391200 2.625600 0.000000 -v 0.371546 2.625600 -0.124808 -v 0.348105 2.625600 -0.181110 -v 0.316590 2.625600 -0.232342 -v 0.277752 2.625600 -0.277752 -v 0.232342 2.625600 -0.316590 -v 0.181110 2.625600 -0.348105 -v 0.124808 2.625600 -0.371546 -v 0.064188 2.625600 -0.386161 -v 0.519028 2.597400 -0.086273 -v 0.499384 2.597400 -0.167751 -v 0.467878 2.597400 -0.243424 -v 0.425519 2.597400 -0.312283 -v 0.373318 2.597400 -0.373318 -v 0.312283 2.597400 -0.425519 -v 0.243424 2.597400 -0.467878 -v 0.167751 2.597400 -0.499384 -v 0.086273 2.597400 -0.519028 -v 0.664924 2.572800 -0.110524 -v 0.673600 2.572800 0.000000 -v 0.639758 2.572800 -0.214905 -v 0.599396 2.572800 -0.311850 -v 0.545131 2.572800 -0.400065 -v 0.478256 2.572800 -0.478256 -v 0.400065 2.572800 -0.545131 -v 0.311850 2.572800 -0.599396 -v 0.214905 2.572800 -0.639758 -v 0.110524 2.572800 -0.664924 -v 0.814374 2.550000 -0.135366 -v 0.825000 2.550000 0.000000 -v 0.783552 2.550000 -0.263208 -v 0.734118 2.550000 -0.381942 -v 0.667656 2.550000 -0.489984 -v 0.585750 2.550000 -0.585750 -v 0.489984 2.550000 -0.667656 -v 0.381942 2.550000 -0.734118 -v 0.263208 2.550000 -0.783552 -v 0.135366 2.550000 -0.814374 -v 0.957901 2.527200 -0.159223 -v 0.921647 2.527200 -0.309596 -v 0.863501 2.527200 -0.449256 -v 0.785325 2.527200 -0.576340 -v 0.688984 2.527200 -0.688984 -v 0.576340 2.527200 -0.785325 -v 0.449256 2.527200 -0.863501 -v 0.309596 2.527200 -0.921647 -v 0.159223 2.527200 -0.957901 -v 1.086029 2.502600 -0.180521 -v 1.044926 2.502600 -0.351008 -v 0.979002 2.502600 -0.509349 -v 0.890370 2.502600 -0.653431 -v 0.781142 2.502600 -0.781142 -v 0.653431 2.502600 -0.890370 -v 0.509349 2.502600 -0.979002 -v 0.351008 2.502600 -1.044926 -v 0.180521 2.502600 -1.086029 -v 1.189282 2.474400 -0.197684 -v 1.144271 2.474400 -0.384379 -v 1.072079 2.474400 -0.557774 -v 0.975021 2.474400 -0.715555 -v 0.855408 2.474400 -0.855408 -v 0.715555 2.474400 -0.975021 -v 0.557774 2.474400 -1.072079 -v 0.384379 2.474400 -1.144271 -v 0.197684 2.474400 -1.189282 -v 1.258183 2.440800 -0.209136 -v 1.210564 2.440800 -0.406648 -v 1.134190 2.440800 -0.590089 -v 1.031508 2.440800 -0.757010 -v 0.904966 2.440800 -0.904966 -v 0.757010 2.440800 -1.031508 -v 0.590089 2.440800 -1.134190 -v 0.406648 2.440800 -1.210564 -v 0.209136 2.440800 -1.258183 -v 0.000000 2.440800 -1.274600 -v 1.283256 2.400000 -0.213304 -v 1.234688 2.400000 -0.414752 -v 1.156792 2.400000 -0.601848 -v 1.052064 2.400000 -0.772096 -v 0.923000 2.400000 -0.923000 -v 0.772096 2.400000 -1.052064 -v 0.601848 2.400000 -1.156792 -v 0.414752 2.400000 -1.234688 -v 0.213304 2.400000 -1.283256 -v 0.000000 2.400000 -1.300000 -v -0.045844 2.659200 -0.275801 -v -0.089140 2.659200 -0.265363 -v -0.129351 2.659200 -0.248621 -v -0.165941 2.659200 -0.226113 -v -0.118784 2.700000 -0.161856 -v -0.198374 2.659200 -0.198374 -v -0.142000 2.700000 -0.142000 -v -0.226113 2.659200 -0.165941 -v -0.248621 2.659200 -0.129351 -v -0.265363 2.659200 -0.089140 -v -0.275801 2.659200 -0.045844 -v -0.279400 2.659200 0.000000 -v -0.200000 2.700000 0.000000 -v -0.064188 2.625600 -0.386161 -v 0.000000 2.625600 -0.391200 -v -0.124808 2.625600 -0.371546 -v -0.181110 2.625600 -0.348105 -v -0.232342 2.625600 -0.316590 -v -0.277752 2.625600 -0.277752 -v -0.316590 2.625600 -0.232342 -v -0.348105 2.625600 -0.181110 -v -0.371546 2.625600 -0.124808 -v -0.386161 2.625600 -0.064188 -v -0.086273 2.597400 -0.519028 -v 0.000000 2.597400 -0.525800 -v -0.167751 2.597400 -0.499384 -v -0.243424 2.597400 -0.467878 -v -0.312283 2.597400 -0.425519 -v -0.373318 2.597400 -0.373318 -v -0.425519 2.597400 -0.312283 -v -0.467878 2.597400 -0.243424 -v -0.499384 2.597400 -0.167751 -v -0.519028 2.597400 -0.086273 -v -0.525800 2.597400 0.000000 -v -0.110524 2.572800 -0.664924 -v 0.000000 2.572800 -0.673600 -v -0.214905 2.572800 -0.639758 -v -0.311850 2.572800 -0.599396 -v -0.400065 2.572800 -0.545131 -v -0.478256 2.572800 -0.478256 -v -0.545131 2.572800 -0.400065 -v -0.599396 2.572800 -0.311850 -v -0.639758 2.572800 -0.214905 -v -0.664924 2.572800 -0.110524 -v -0.135366 2.550000 -0.814374 -v 0.000000 2.550000 -0.825000 -v -0.263208 2.550000 -0.783552 -v -0.381942 2.550000 -0.734118 -v -0.489984 2.550000 -0.667656 -v -0.585750 2.550000 -0.585750 -v -0.667656 2.550000 -0.489984 -v -0.734118 2.550000 -0.381942 -v -0.783552 2.550000 -0.263208 -v -0.814374 2.550000 -0.135366 -v -0.825000 2.550000 0.000000 -v -0.159223 2.527200 -0.957901 -v 0.000000 2.527200 -0.970400 -v -0.309596 2.527200 -0.921647 -v -0.449256 2.527200 -0.863501 -v -0.576340 2.527200 -0.785325 -v -0.688984 2.527200 -0.688984 -v -0.785325 2.527200 -0.576340 -v -0.863501 2.527200 -0.449256 -v -0.921647 2.527200 -0.309596 -v -0.957901 2.527200 -0.159223 -v -0.180521 2.502600 -1.086029 -v 0.000000 2.502600 -1.100200 -v -0.351008 2.502600 -1.044926 -v -0.509349 2.502600 -0.979002 -v -0.653431 2.502600 -0.890370 -v -0.781142 2.502600 -0.781142 -v -0.890370 2.502600 -0.653431 -v -0.979002 2.502600 -0.509349 -v -1.044926 2.502600 -0.351008 -v -1.086029 2.502600 -0.180521 -v -0.197684 2.474400 -1.189282 -v 0.000000 2.474400 -1.204800 -v -0.384379 2.474400 -1.144271 -v -0.557774 2.474400 -1.072079 -v -0.715555 2.474400 -0.975021 -v -0.855408 2.474400 -0.855408 -v -0.975021 2.474400 -0.715555 -v -1.072079 2.474400 -0.557774 -v -1.144271 2.474400 -0.384379 -v -1.189282 2.474400 -0.197684 -v -0.209136 2.440800 -1.258183 -v -0.406648 2.440800 -1.210564 -v -0.590089 2.440800 -1.134190 -v -0.757010 2.440800 -1.031508 -v -0.904966 2.440800 -0.904966 -v -1.031508 2.440800 -0.757010 -v -1.134190 2.440800 -0.590089 -v -1.210564 2.440800 -0.406648 -v -1.258183 2.440800 -0.209136 -v -1.274600 2.440800 0.000000 -v -0.213304 2.400000 -1.283256 -v -0.414752 2.400000 -1.234688 -v -0.601848 2.400000 -1.156792 -v -0.772096 2.400000 -1.052064 -v -0.923000 2.400000 -0.923000 -v -1.052064 2.400000 -0.772096 -v -1.156792 2.400000 -0.601848 -v -1.234688 2.400000 -0.414752 -v -1.283256 2.400000 -0.213304 -v -0.275801 2.659200 0.045844 -v -0.197424 2.700000 0.032816 -v -0.265363 2.659200 0.089140 -v -0.248621 2.659200 0.129351 -v -0.177968 2.700000 0.092592 -v -0.226113 2.659200 0.165941 -v -0.198374 2.659200 0.198374 -v -0.142000 2.700000 0.142000 -v -0.165941 2.659200 0.226113 -v -0.118784 2.700000 0.161856 -v -0.129351 2.659200 0.248621 -v -0.089140 2.659200 0.265363 -v -0.063808 2.700000 0.189952 -v -0.045844 2.659200 0.275801 -v -0.032816 2.700000 0.197424 -v 0.000000 2.659200 0.279400 -v -0.386161 2.625600 0.064188 -v -0.391200 2.625600 0.000000 -v -0.371546 2.625600 0.124808 -v -0.348105 2.625600 0.181110 -v -0.316590 2.625600 0.232342 -v -0.277752 2.625600 0.277752 -v -0.232342 2.625600 0.316590 -v -0.181110 2.625600 0.348105 -v -0.124808 2.625600 0.371546 -v -0.064188 2.625600 0.386161 -v 0.000000 2.625600 0.391200 -v -0.519028 2.597400 0.086273 -v -0.499384 2.597400 0.167751 -v -0.467878 2.597400 0.243424 -v -0.425519 2.597400 0.312283 -v -0.373318 2.597400 0.373318 -v -0.312283 2.597400 0.425519 -v -0.243424 2.597400 0.467878 -v -0.167751 2.597400 0.499384 -v -0.086273 2.597400 0.519028 -v 0.000000 2.597400 0.525800 -v -0.664924 2.572800 0.110524 -v -0.673600 2.572800 0.000000 -v -0.639758 2.572800 0.214905 -v -0.599396 2.572800 0.311850 -v -0.545131 2.572800 0.400065 -v -0.478256 2.572800 0.478256 -v -0.400065 2.572800 0.545131 -v -0.311850 2.572800 0.599396 -v -0.214905 2.572800 0.639758 -v -0.110524 2.572800 0.664924 -v 0.000000 2.572800 0.673600 -v -0.814374 2.550000 0.135366 -v -0.783552 2.550000 0.263208 -v -0.734118 2.550000 0.381942 -v -0.667656 2.550000 0.489984 -v -0.585750 2.550000 0.585750 -v -0.489984 2.550000 0.667656 -v -0.381942 2.550000 0.734118 -v -0.263208 2.550000 0.783552 -v -0.135366 2.550000 0.814374 -v 0.000000 2.550000 0.825000 -v -0.957901 2.527200 0.159223 -v -0.970400 2.527200 0.000000 -v -0.921647 2.527200 0.309596 -v -0.863501 2.527200 0.449256 -v -0.785325 2.527200 0.576340 -v -0.688984 2.527200 0.688984 -v -0.576340 2.527200 0.785325 -v -0.449256 2.527200 0.863501 -v -0.309596 2.527200 0.921647 -v -0.159223 2.527200 0.957901 -v 0.000000 2.527200 0.970400 -v -1.086029 2.502600 0.180521 -v -1.100200 2.502600 0.000000 -v -1.044926 2.502600 0.351008 -v -0.979002 2.502600 0.509349 -v -0.890370 2.502600 0.653431 -v -0.781142 2.502600 0.781142 -v -0.653431 2.502600 0.890370 -v -0.509349 2.502600 0.979002 -v -0.351008 2.502600 1.044926 -v -0.180521 2.502600 1.086029 -v -1.189282 2.474400 0.197684 -v -1.204800 2.474400 0.000000 -v -1.144271 2.474400 0.384379 -v -1.072079 2.474400 0.557774 -v -0.975021 2.474400 0.715555 -v -0.855408 2.474400 0.855408 -v -0.715555 2.474400 0.975021 -v -0.557774 2.474400 1.072079 -v -0.384379 2.474400 1.144271 -v -0.197684 2.474400 1.189282 -v -1.258183 2.440800 0.209136 -v -1.210564 2.440800 0.406648 -v -1.134190 2.440800 0.590089 -v -1.031508 2.440800 0.757010 -v -0.904966 2.440800 0.904966 -v -0.757010 2.440800 1.031508 -v -0.590089 2.440800 1.134190 -v -0.406648 2.440800 1.210564 -v -0.209136 2.440800 1.258183 -v -1.283256 2.400000 0.213304 -v -1.300000 2.400000 0.000000 -v -1.234688 2.400000 0.414752 -v -1.156792 2.400000 0.601848 -v -1.052064 2.400000 0.772096 -v -0.923000 2.400000 0.923000 -v -0.772096 2.400000 1.052064 -v -0.601848 2.400000 1.156792 -v -0.414752 2.400000 1.234688 -v -0.213304 2.400000 1.283256 -v 0.000000 2.400000 1.300000 -v 0.045844 2.659200 0.275801 -v 0.089140 2.659200 0.265363 -v 0.063808 2.700000 0.189952 -v 0.129351 2.659200 0.248621 -v 0.092592 2.700000 0.177968 -v 0.165941 2.659200 0.226113 -v 0.198374 2.659200 0.198374 -v 0.226113 2.659200 0.165941 -v 0.161856 2.700000 0.118784 -v 0.248621 2.659200 0.129351 -v 0.177968 2.700000 0.092592 -v 0.265363 2.659200 0.089140 -v 0.189952 2.700000 0.063808 -v 0.275801 2.659200 0.045844 -v 0.197424 2.700000 0.032816 -v 0.064188 2.625600 0.386161 -v 0.124808 2.625600 0.371546 -v 0.181110 2.625600 0.348105 -v 0.232342 2.625600 0.316590 -v 0.277752 2.625600 0.277752 -v 0.316590 2.625600 0.232342 -v 0.348105 2.625600 0.181110 -v 0.371546 2.625600 0.124808 -v 0.386161 2.625600 0.064188 -v 0.086273 2.597400 0.519028 -v 0.167751 2.597400 0.499384 -v 0.243424 2.597400 0.467878 -v 0.312283 2.597400 0.425519 -v 0.373318 2.597400 0.373318 -v 0.425519 2.597400 0.312283 -v 0.467878 2.597400 0.243424 -v 0.499384 2.597400 0.167751 -v 0.519028 2.597400 0.086273 -v 0.525800 2.597400 0.000000 -v 0.110524 2.572800 0.664924 -v 0.214905 2.572800 0.639758 -v 0.311850 2.572800 0.599396 -v 0.400065 2.572800 0.545131 -v 0.478256 2.572800 0.478256 -v 0.545131 2.572800 0.400065 -v 0.599396 2.572800 0.311850 -v 0.639758 2.572800 0.214905 -v 0.664924 2.572800 0.110524 -v 0.135366 2.550000 0.814374 -v 0.263208 2.550000 0.783552 -v 0.381942 2.550000 0.734118 -v 0.489984 2.550000 0.667656 -v 0.585750 2.550000 0.585750 -v 0.667656 2.550000 0.489984 -v 0.734118 2.550000 0.381942 -v 0.783552 2.550000 0.263208 -v 0.814374 2.550000 0.135366 -v 0.159223 2.527200 0.957901 -v 0.309596 2.527200 0.921647 -v 0.449256 2.527200 0.863501 -v 0.576340 2.527200 0.785325 -v 0.688984 2.527200 0.688984 -v 0.785325 2.527200 0.576340 -v 0.863501 2.527200 0.449256 -v 0.921647 2.527200 0.309596 -v 0.957901 2.527200 0.159223 -v 0.970400 2.527200 0.000000 -v 0.180521 2.502600 1.086029 -v 0.000000 2.502600 1.100200 -v 0.351008 2.502600 1.044926 -v 0.509349 2.502600 0.979002 -v 0.653431 2.502600 0.890370 -v 0.781142 2.502600 0.781142 -v 0.890370 2.502600 0.653431 -v 0.979002 2.502600 0.509349 -v 1.044926 2.502600 0.351008 -v 1.086029 2.502600 0.180521 -v 1.100200 2.502600 0.000000 -v 0.197684 2.474400 1.189282 -v 0.000000 2.474400 1.204800 -v 0.384379 2.474400 1.144271 -v 0.557774 2.474400 1.072079 -v 0.715555 2.474400 0.975021 -v 0.855408 2.474400 0.855408 -v 0.975021 2.474400 0.715555 -v 1.072079 2.474400 0.557774 -v 1.144271 2.474400 0.384379 -v 1.189282 2.474400 0.197684 -v 1.204800 2.474400 0.000000 -v 0.209136 2.440800 1.258183 -v 0.000000 2.440800 1.274600 -v 0.406648 2.440800 1.210564 -v 0.590089 2.440800 1.134190 -v 0.757010 2.440800 1.031508 -v 0.904966 2.440800 0.904966 -v 1.031508 2.440800 0.757010 -v 1.134190 2.440800 0.590089 -v 1.210564 2.440800 0.406648 -v 1.258183 2.440800 0.209136 -v 1.274600 2.440800 0.000000 -v 0.213304 2.400000 1.283256 -v 0.414752 2.400000 1.234688 -v 0.601848 2.400000 1.156792 -v 0.772096 2.400000 1.052064 -v 0.923000 2.400000 0.923000 -v 1.052064 2.400000 0.772096 -v 1.156792 2.400000 0.601848 -v 1.234688 2.400000 0.414752 -v 1.283256 2.400000 0.213304 -v 1.300000 2.400000 0.000000 -v 0.000000 0.000000 0.000000 -v 0.388275 0.002175 0.000000 -v 0.383274 0.002175 0.063708 -v 0.368768 0.002175 0.123875 -v 0.345503 0.002175 0.179756 -v 0.314223 0.002175 0.230604 -v 0.275675 0.002175 0.275675 -v 0.230604 0.002175 0.314223 -v 0.179756 0.002175 0.345503 -v 0.123875 0.002175 0.368768 -v 0.063708 0.002175 0.383274 -v 0.000000 0.002175 0.388275 -v 0.694143 0.008400 0.115381 -v 0.703200 0.008400 0.000000 -v 0.667871 0.008400 0.224349 -v 0.625735 0.008400 0.325553 -v 0.569086 0.008400 0.417645 -v 0.499272 0.008400 0.499272 -v 0.417645 0.008400 0.569086 -v 0.325553 0.008400 0.625735 -v 0.224349 0.008400 0.667871 -v 0.115381 0.008400 0.694143 -v 0.000000 0.008400 0.703200 -v 0.940158 0.018225 0.156274 -v 0.904575 0.018225 0.303862 -v 0.847506 0.018225 0.440935 -v 0.770779 0.018225 0.565664 -v 0.676222 0.018225 0.676222 -v 0.565664 0.018225 0.770779 -v 0.440935 0.018225 0.847506 -v 0.303862 0.018225 0.904575 -v 0.156274 0.018225 0.940158 -v 0.000000 0.018225 0.952425 -v 1.128870 0.031200 0.187642 -v 1.143600 0.031200 0.000000 -v 1.086146 0.031200 0.364854 -v 1.017621 0.031200 0.529441 -v 0.925493 0.031200 0.679207 -v 0.811956 0.031200 0.811956 -v 0.679207 0.031200 0.925493 -v 0.529441 0.031200 1.017621 -v 0.364854 0.031200 1.086146 -v 0.187642 0.031200 1.128870 -v 0.000000 0.031200 1.143600 -v 1.267832 0.046875 0.210740 -v 1.219848 0.046875 0.409767 -v 1.142888 0.046875 0.594614 -v 1.039419 0.046875 0.762816 -v 0.911906 0.046875 0.911906 -v 0.762816 0.046875 1.039419 -v 0.594614 0.046875 1.142888 -v 0.409767 0.046875 1.219848 -v 0.210740 0.046875 1.267832 -v 1.364595 0.064800 0.226824 -v 1.312948 0.064800 0.441041 -v 1.230115 0.064800 0.639996 -v 1.118749 0.064800 0.821035 -v 0.981504 0.064800 0.981504 -v 0.821035 0.064800 1.118749 -v 0.639996 0.064800 1.230115 -v 0.441041 0.064800 1.312948 -v 0.226824 0.064800 1.364595 -v 1.426709 0.084525 0.237149 -v 1.372712 0.084525 0.461116 -v 1.286108 0.084525 0.669128 -v 1.169673 0.084525 0.858407 -v 1.026181 0.084525 1.026181 -v 0.858407 0.084525 1.169673 -v 0.669128 0.084525 1.286108 -v 0.461116 0.084525 1.372712 -v 0.237149 0.084525 1.426709 -v 0.000000 0.084525 1.445325 -v 1.461727 0.105600 0.242970 -v 1.406405 0.105600 0.472434 -v 1.317675 0.105600 0.685551 -v 1.198382 0.105600 0.879477 -v 1.051368 0.105600 1.051368 -v 0.879477 0.105600 1.198382 -v 0.685551 0.105600 1.317675 -v 0.472434 0.105600 1.406405 -v 0.242970 0.105600 1.461727 -v 0.000000 0.105600 1.480800 -v 1.477200 0.127575 0.245542 -v 1.496475 0.127575 0.000000 -v 1.421292 0.127575 0.477435 -v 1.331623 0.127575 0.692808 -v 1.211067 0.127575 0.888786 -v 1.062497 0.127575 1.062497 -v 0.888786 0.127575 1.211067 -v 0.692808 0.127575 1.331623 -v 0.477435 0.127575 1.421292 -v 0.245542 0.127575 1.477200 -v 0.000000 0.127575 1.496475 -v 1.424640 0.150000 0.478560 -v 1.334760 0.150000 0.694440 -v 1.213920 0.150000 0.890880 -v 0.890880 0.150000 1.213920 -v 0.478560 0.150000 1.424640 -v -0.063708 0.002175 0.383274 -v -0.123875 0.002175 0.368768 -v -0.179756 0.002175 0.345503 -v -0.230604 0.002175 0.314223 -v -0.275675 0.002175 0.275675 -v -0.314223 0.002175 0.230604 -v -0.345503 0.002175 0.179756 -v -0.368768 0.002175 0.123875 -v -0.383274 0.002175 0.063708 -v -0.388275 0.002175 0.000000 -v -0.115381 0.008400 0.694143 -v -0.224349 0.008400 0.667871 -v -0.325553 0.008400 0.625735 -v -0.417645 0.008400 0.569086 -v -0.499272 0.008400 0.499272 -v -0.569086 0.008400 0.417645 -v -0.625735 0.008400 0.325553 -v -0.667871 0.008400 0.224349 -v -0.694143 0.008400 0.115381 -v -0.703200 0.008400 0.000000 -v -0.156274 0.018225 0.940158 -v -0.303862 0.018225 0.904575 -v -0.440935 0.018225 0.847506 -v -0.565664 0.018225 0.770779 -v -0.676222 0.018225 0.676222 -v -0.770779 0.018225 0.565664 -v -0.847506 0.018225 0.440935 -v -0.904575 0.018225 0.303862 -v -0.940158 0.018225 0.156274 -v -0.952425 0.018225 0.000000 -v -0.187642 0.031200 1.128870 -v -0.364854 0.031200 1.086146 -v -0.529441 0.031200 1.017621 -v -0.679207 0.031200 0.925493 -v -0.811956 0.031200 0.811956 -v -0.925493 0.031200 0.679207 -v -1.017621 0.031200 0.529441 -v -1.086146 0.031200 0.364854 -v -1.128870 0.031200 0.187642 -v -1.143600 0.031200 0.000000 -v -0.210740 0.046875 1.267832 -v 0.000000 0.046875 1.284375 -v -0.409767 0.046875 1.219848 -v -0.594614 0.046875 1.142888 -v -0.762816 0.046875 1.039419 -v -0.911906 0.046875 0.911906 -v -1.039419 0.046875 0.762816 -v -1.142888 0.046875 0.594614 -v -1.219848 0.046875 0.409767 -v -1.267832 0.046875 0.210740 -v -1.284375 0.046875 0.000000 -v -0.226824 0.064800 1.364595 -v 0.000000 0.064800 1.382400 -v -0.441041 0.064800 1.312948 -v -0.639996 0.064800 1.230115 -v -0.821035 0.064800 1.118749 -v -0.981504 0.064800 0.981504 -v -1.118749 0.064800 0.821035 -v -1.230115 0.064800 0.639996 -v -1.312948 0.064800 0.441041 -v -1.364595 0.064800 0.226824 -v -1.382400 0.064800 0.000000 -v -0.237149 0.084525 1.426709 -v -0.461116 0.084525 1.372712 -v -0.669128 0.084525 1.286108 -v -0.858407 0.084525 1.169673 -v -1.026181 0.084525 1.026181 -v -1.169673 0.084525 0.858407 -v -1.286108 0.084525 0.669128 -v -1.372712 0.084525 0.461116 -v -1.426709 0.084525 0.237149 -v -0.242970 0.105600 1.461727 -v -0.472434 0.105600 1.406405 -v -0.685551 0.105600 1.317675 -v -0.879477 0.105600 1.198382 -v -1.051368 0.105600 1.051368 -v -1.198382 0.105600 0.879477 -v -1.317675 0.105600 0.685551 -v -1.406405 0.105600 0.472434 -v -1.461727 0.105600 0.242970 -v -0.245542 0.127575 1.477200 -v -0.477435 0.127575 1.421292 -v -0.692808 0.127575 1.331623 -v -0.888786 0.127575 1.211067 -v -1.062497 0.127575 1.062497 -v -1.211067 0.127575 0.888786 -v -1.331623 0.127575 0.692808 -v -1.421292 0.127575 0.477435 -v -1.477200 0.127575 0.245542 -v -0.478560 0.150000 1.424640 -v -0.890880 0.150000 1.213920 -v -1.334760 0.150000 0.694440 -v -1.424640 0.150000 0.478560 -v -0.383274 0.002175 -0.063708 -v -0.368768 0.002175 -0.123875 -v -0.345503 0.002175 -0.179756 -v -0.314223 0.002175 -0.230604 -v -0.275675 0.002175 -0.275675 -v -0.230604 0.002175 -0.314223 -v -0.179756 0.002175 -0.345503 -v -0.123875 0.002175 -0.368768 -v -0.063708 0.002175 -0.383274 -v -0.694143 0.008400 -0.115381 -v -0.667871 0.008400 -0.224349 -v -0.625735 0.008400 -0.325553 -v -0.569086 0.008400 -0.417645 -v -0.499272 0.008400 -0.499272 -v -0.417645 0.008400 -0.569086 -v -0.325553 0.008400 -0.625735 -v -0.224349 0.008400 -0.667871 -v -0.115381 0.008400 -0.694143 -v 0.000000 0.008400 -0.703200 -v -0.940158 0.018225 -0.156274 -v -0.904575 0.018225 -0.303862 -v -0.847506 0.018225 -0.440935 -v -0.770779 0.018225 -0.565664 -v -0.676222 0.018225 -0.676222 -v -0.565664 0.018225 -0.770779 -v -0.440935 0.018225 -0.847506 -v -0.303862 0.018225 -0.904575 -v -0.156274 0.018225 -0.940158 -v 0.000000 0.018225 -0.952425 -v -1.128870 0.031200 -0.187642 -v -1.086146 0.031200 -0.364854 -v -1.017621 0.031200 -0.529441 -v -0.925493 0.031200 -0.679207 -v -0.811956 0.031200 -0.811956 -v -0.679207 0.031200 -0.925493 -v -0.529441 0.031200 -1.017621 -v -0.364854 0.031200 -1.086146 -v -0.187642 0.031200 -1.128870 -v 0.000000 0.031200 -1.143600 -v -1.267832 0.046875 -0.210740 -v -1.219848 0.046875 -0.409767 -v -1.142888 0.046875 -0.594614 -v -1.039419 0.046875 -0.762816 -v -0.911906 0.046875 -0.911906 -v -0.762816 0.046875 -1.039419 -v -0.594614 0.046875 -1.142888 -v -0.409767 0.046875 -1.219848 -v -0.210740 0.046875 -1.267832 -v 0.000000 0.046875 -1.284375 -v -1.364595 0.064800 -0.226824 -v -1.312948 0.064800 -0.441041 -v -1.230115 0.064800 -0.639996 -v -1.118749 0.064800 -0.821035 -v -0.981504 0.064800 -0.981504 -v -0.821035 0.064800 -1.118749 -v -0.639996 0.064800 -1.230115 -v -0.441041 0.064800 -1.312948 -v -0.226824 0.064800 -1.364595 -v 0.000000 0.064800 -1.382400 -v -1.426709 0.084525 -0.237149 -v -1.445325 0.084525 0.000000 -v -1.372712 0.084525 -0.461116 -v -1.286108 0.084525 -0.669128 -v -1.169673 0.084525 -0.858407 -v -1.026181 0.084525 -1.026181 -v -0.858407 0.084525 -1.169673 -v -0.669128 0.084525 -1.286108 -v -0.461116 0.084525 -1.372712 -v -0.237149 0.084525 -1.426709 -v -1.461727 0.105600 -0.242970 -v -1.480800 0.105600 0.000000 -v -1.406405 0.105600 -0.472434 -v -1.317675 0.105600 -0.685551 -v -1.198382 0.105600 -0.879477 -v -1.051368 0.105600 -1.051368 -v -0.879477 0.105600 -1.198382 -v -0.685551 0.105600 -1.317675 -v -0.472434 0.105600 -1.406405 -v -0.242970 0.105600 -1.461727 -v 0.000000 0.105600 -1.480800 -v -1.477200 0.127575 -0.245542 -v -1.496475 0.127575 0.000000 -v -1.421292 0.127575 -0.477435 -v -1.331623 0.127575 -0.692808 -v -1.211067 0.127575 -0.888786 -v -1.062497 0.127575 -1.062497 -v -0.888786 0.127575 -1.211067 -v -0.692808 0.127575 -1.331623 -v -0.477435 0.127575 -1.421292 -v -0.245542 0.127575 -1.477200 -v -0.246120 0.150000 -1.480680 -v 0.000000 0.002175 -0.388275 -v 0.063708 0.002175 -0.383274 -v 0.123875 0.002175 -0.368768 -v 0.179756 0.002175 -0.345503 -v 0.230604 0.002175 -0.314223 -v 0.275675 0.002175 -0.275675 -v 0.314223 0.002175 -0.230604 -v 0.345503 0.002175 -0.179756 -v 0.368768 0.002175 -0.123875 -v 0.383274 0.002175 -0.063708 -v 0.115381 0.008400 -0.694143 -v 0.224349 0.008400 -0.667871 -v 0.325553 0.008400 -0.625735 -v 0.417645 0.008400 -0.569086 -v 0.499272 0.008400 -0.499272 -v 0.569086 0.008400 -0.417645 -v 0.625735 0.008400 -0.325553 -v 0.667871 0.008400 -0.224349 -v 0.694143 0.008400 -0.115381 -v 0.156274 0.018225 -0.940158 -v 0.303862 0.018225 -0.904575 -v 0.440935 0.018225 -0.847506 -v 0.565664 0.018225 -0.770779 -v 0.676222 0.018225 -0.676222 -v 0.770779 0.018225 -0.565664 -v 0.847506 0.018225 -0.440935 -v 0.904575 0.018225 -0.303862 -v 0.940158 0.018225 -0.156274 -v 0.952425 0.018225 0.000000 -v 0.187642 0.031200 -1.128870 -v 0.364854 0.031200 -1.086146 -v 0.529441 0.031200 -1.017621 -v 0.679207 0.031200 -0.925493 -v 0.811956 0.031200 -0.811956 -v 0.925493 0.031200 -0.679207 -v 1.017621 0.031200 -0.529441 -v 1.086146 0.031200 -0.364854 -v 1.128870 0.031200 -0.187642 -v 0.210740 0.046875 -1.267832 -v 0.409767 0.046875 -1.219848 -v 0.594614 0.046875 -1.142888 -v 0.762816 0.046875 -1.039419 -v 0.911906 0.046875 -0.911906 -v 1.039419 0.046875 -0.762816 -v 1.142888 0.046875 -0.594614 -v 1.219848 0.046875 -0.409767 -v 1.267832 0.046875 -0.210740 -v 1.284375 0.046875 0.000000 -v 0.226824 0.064800 -1.364595 -v 0.441041 0.064800 -1.312948 -v 0.639996 0.064800 -1.230115 -v 0.821035 0.064800 -1.118749 -v 0.981504 0.064800 -0.981504 -v 1.118749 0.064800 -0.821035 -v 1.230115 0.064800 -0.639996 -v 1.312948 0.064800 -0.441041 -v 1.364595 0.064800 -0.226824 -v 1.382400 0.064800 0.000000 -v 0.237149 0.084525 -1.426709 -v 0.000000 0.084525 -1.445325 -v 0.461116 0.084525 -1.372712 -v 0.669128 0.084525 -1.286108 -v 0.858407 0.084525 -1.169673 -v 1.026181 0.084525 -1.026181 -v 1.169673 0.084525 -0.858407 -v 1.286108 0.084525 -0.669128 -v 1.372712 0.084525 -0.461116 -v 1.426709 0.084525 -0.237149 -v 1.445325 0.084525 0.000000 -v 0.242970 0.105600 -1.461727 -v 0.472434 0.105600 -1.406405 -v 0.685551 0.105600 -1.317675 -v 0.879477 0.105600 -1.198382 -v 1.051368 0.105600 -1.051368 -v 1.198382 0.105600 -0.879477 -v 1.317675 0.105600 -0.685551 -v 1.406405 0.105600 -0.472434 -v 1.461727 0.105600 -0.242970 -v 1.480800 0.105600 0.000000 -v 0.245542 0.127575 -1.477200 -v 0.000000 0.127575 -1.496475 -v 0.477435 0.127575 -1.421292 -v 0.692808 0.127575 -1.331623 -v 0.888786 0.127575 -1.211067 -v 1.062497 0.127575 -1.062497 -v 1.211067 0.127575 -0.888786 -v 1.331623 0.127575 -0.692808 -v 1.421292 0.127575 -0.477435 -v 1.477200 0.127575 -0.245542 -v 0.246120 0.150000 -1.480680 -v 0.478560 0.150000 -1.424640 -v 0.694440 0.150000 -1.334760 -v 1.213920 0.150000 -0.890880 -v 1.334760 0.150000 -0.694440 -v 1.480680 0.150000 -0.246120 -vn -0.9466 -0.2854 0.1503 -vn -0.9178 -0.3693 0.1458 -vn -0.9294 -0.3691 -0.0000 -vn -0.9585 -0.2852 -0.0000 -vn -0.9110 -0.2857 0.2974 -vn -0.8832 -0.3697 0.2885 -vn -0.8530 -0.2860 0.4365 -vn -0.8269 -0.3701 0.4233 -vn -0.7745 -0.2862 0.5641 -vn -0.7508 -0.3704 0.5469 -vn -0.6775 -0.2863 0.6775 -vn -0.6568 -0.3704 0.6568 -vn -0.5641 -0.2862 0.7745 -vn -0.5469 -0.3704 0.7508 -vn -0.4365 -0.2860 0.8530 -vn -0.4233 -0.3701 0.8269 -vn -0.2974 -0.2857 0.9110 -vn -0.2885 -0.3697 0.8832 -vn -0.1503 -0.2854 0.9466 -vn -0.1458 -0.3693 0.9178 -vn -0.0000 -0.2852 0.9585 -vn -0.0000 -0.3691 0.9294 -vn -0.9862 -0.0531 0.1565 -vn -0.9986 -0.0531 -0.0000 -vn -0.9493 -0.0532 0.3098 -vn -0.8890 -0.0532 0.4549 -vn -0.8072 -0.0532 0.5879 -vn -0.7061 -0.0533 0.7061 -vn -0.5879 -0.0532 0.8072 -vn -0.4549 -0.0532 0.8890 -vn -0.3098 -0.0532 0.9493 -vn -0.1565 -0.0531 0.9862 -vn -0.0000 -0.0531 0.9986 -vn -0.9286 0.3406 0.1474 -vn -0.9403 0.3404 -0.0000 -vn -0.8937 0.3409 0.2917 -vn -0.8368 0.3413 0.4282 -vn -0.7598 0.3414 0.5533 -vn -0.6646 0.3415 0.6646 -vn -0.5533 0.3414 0.7598 -vn -0.4281 0.3413 0.8368 -vn -0.2917 0.3409 0.8937 -vn -0.1474 0.3406 0.9286 -vn -0.0000 0.3404 0.9403 -vn -0.5758 0.8125 0.0914 -vn -0.5833 0.8123 -0.0000 -vn -0.5539 0.8127 0.1808 -vn -0.5184 0.8129 0.2653 -vn -0.4706 0.8131 0.3428 -vn -0.4116 0.8131 0.4116 -vn -0.3428 0.8131 0.4706 -vn -0.2653 0.8129 0.5184 -vn -0.1808 0.8127 0.5539 -vn -0.0914 0.8125 0.5758 -vn -0.0000 0.8123 0.5833 -vn -0.0185 0.9998 0.0030 -vn -0.0189 0.9998 -0.0000 -vn -0.0178 0.9998 0.0059 -vn -0.0167 0.9998 0.0087 -vn -0.0153 0.9998 0.0112 -vn -0.0134 0.9998 0.0134 -vn -0.0112 0.9998 0.0153 -vn -0.0087 0.9998 0.0167 -vn -0.0059 0.9998 0.0178 -vn -0.0030 0.9998 0.0185 -vn -0.0000 0.9998 0.0189 -vn 0.3833 0.9216 -0.0608 -vn 0.3883 0.9216 -0.0000 -vn 0.3686 0.9218 -0.1203 -vn 0.3448 0.9219 -0.1764 -vn 0.3129 0.9221 -0.2278 -vn 0.2736 0.9221 -0.2736 -vn 0.2278 0.9221 -0.3129 -vn 0.1764 0.9219 -0.3448 -vn 0.1203 0.9218 -0.3686 -vn 0.0608 0.9216 -0.3833 -vn -0.0000 0.9216 -0.3883 -vn 0.6064 0.7893 -0.0962 -vn 0.6142 0.7892 -0.0000 -vn 0.5832 0.7897 -0.1904 -vn 0.5457 0.7900 -0.2793 -vn 0.4953 0.7903 -0.3607 -vn 0.4332 0.7904 -0.4332 -vn 0.3607 0.7903 -0.4953 -vn 0.2793 0.7900 -0.5457 -vn 0.1904 0.7897 -0.5832 -vn 0.0962 0.7893 -0.6064 -vn -0.0000 0.7892 -0.6142 -vn 0.7401 0.6622 -0.1175 -vn 0.7496 0.6619 -0.0000 -vn 0.7120 0.6626 -0.2324 -vn 0.6664 0.6630 -0.3410 -vn 0.6049 0.6633 -0.4406 -vn 0.5291 0.6634 -0.5291 -vn 0.4406 0.6633 -0.6049 -vn 0.3410 0.6630 -0.6664 -vn 0.2324 0.6626 -0.7120 -vn 0.1175 0.6622 -0.7401 -vn -0.0000 0.6619 -0.7496 -vn 0.8292 0.5432 -0.1317 -vn 0.8398 0.5429 -0.0000 -vn 0.7979 0.5436 -0.2605 -vn 0.7469 0.5441 -0.3822 -vn 0.6780 0.5444 -0.4939 -vn 0.5931 0.5445 -0.5931 -vn 0.4939 0.5444 -0.6780 -vn 0.3822 0.5441 -0.7469 -vn 0.2605 0.5436 -0.7979 -vn 0.1317 0.5432 -0.8292 -vn -0.0000 0.5429 -0.8398 -vn 0.8785 0.4570 -0.1395 -vn 0.8896 0.4568 -0.0000 -vn 0.8453 0.4574 -0.2760 -vn 0.7914 0.4579 -0.4050 -vn 0.7185 0.4581 -0.5233 -vn 0.6285 0.4582 -0.6285 -vn 0.5233 0.4581 -0.7185 -vn 0.4050 0.4579 -0.7914 -vn 0.2760 0.4574 -0.8453 -vn 0.1395 0.4570 -0.8785 -vn -0.0000 0.4568 -0.8896 -vn 0.1503 -0.2854 0.9466 -vn 0.1458 -0.3693 0.9178 -vn 0.2974 -0.2857 0.9110 -vn 0.2885 -0.3697 0.8832 -vn 0.4365 -0.2860 0.8530 -vn 0.4233 -0.3701 0.8269 -vn 0.5641 -0.2862 0.7745 -vn 0.5469 -0.3704 0.7508 -vn 0.6775 -0.2863 0.6775 -vn 0.6568 -0.3704 0.6568 -vn 0.7745 -0.2862 0.5641 -vn 0.7508 -0.3704 0.5469 -vn 0.8530 -0.2860 0.4365 -vn 0.8269 -0.3701 0.4233 -vn 0.9110 -0.2857 0.2974 -vn 0.8832 -0.3697 0.2885 -vn 0.9466 -0.2854 0.1503 -vn 0.9178 -0.3693 0.1458 -vn 0.9585 -0.2852 -0.0000 -vn 0.9294 -0.3691 -0.0000 -vn 0.1565 -0.0531 0.9862 -vn 0.3098 -0.0532 0.9493 -vn 0.4549 -0.0532 0.8890 -vn 0.5879 -0.0532 0.8072 -vn 0.7061 -0.0533 0.7061 -vn 0.8072 -0.0532 0.5879 -vn 0.8890 -0.0532 0.4549 -vn 0.9493 -0.0532 0.3098 -vn 0.9862 -0.0531 0.1565 -vn 0.9986 -0.0531 -0.0000 -vn 0.1474 0.3406 0.9286 -vn 0.2917 0.3409 0.8937 -vn 0.4282 0.3413 0.8368 -vn 0.5533 0.3414 0.7598 -vn 0.6646 0.3415 0.6646 -vn 0.7598 0.3414 0.5533 -vn 0.8368 0.3413 0.4281 -vn 0.8937 0.3409 0.2917 -vn 0.9286 0.3406 0.1474 -vn 0.9403 0.3404 -0.0000 -vn 0.0914 0.8125 0.5758 -vn 0.1808 0.8127 0.5539 -vn 0.2653 0.8130 0.5184 -vn 0.3428 0.8131 0.4706 -vn 0.4116 0.8131 0.4116 -vn 0.4706 0.8131 0.3428 -vn 0.5184 0.8129 0.2653 -vn 0.5539 0.8127 0.1808 -vn 0.5758 0.8125 0.0914 -vn 0.5833 0.8123 -0.0000 -vn 0.0030 0.9998 0.0185 -vn 0.0059 0.9998 0.0178 -vn 0.0087 0.9998 0.0167 -vn 0.0112 0.9998 0.0153 -vn 0.0134 0.9998 0.0134 -vn 0.0153 0.9998 0.0112 -vn 0.0167 0.9998 0.0087 -vn 0.0178 0.9998 0.0059 -vn 0.0185 0.9998 0.0030 -vn 0.0189 0.9998 -0.0000 -vn -0.0608 0.9216 -0.3833 -vn -0.1203 0.9218 -0.3686 -vn -0.1764 0.9219 -0.3448 -vn -0.2278 0.9221 -0.3129 -vn -0.2736 0.9221 -0.2736 -vn -0.3129 0.9221 -0.2278 -vn -0.3448 0.9219 -0.1764 -vn -0.3686 0.9218 -0.1203 -vn -0.3833 0.9216 -0.0608 -vn -0.3883 0.9216 -0.0000 -vn -0.0962 0.7893 -0.6064 -vn -0.1904 0.7897 -0.5832 -vn -0.2793 0.7900 -0.5457 -vn -0.3607 0.7903 -0.4953 -vn -0.4332 0.7904 -0.4332 -vn -0.4953 0.7903 -0.3607 -vn -0.5457 0.7901 -0.2793 -vn -0.5832 0.7897 -0.1904 -vn -0.6064 0.7893 -0.0962 -vn -0.6142 0.7892 -0.0000 -vn -0.1175 0.6622 -0.7401 -vn -0.2324 0.6626 -0.7120 -vn -0.3410 0.6630 -0.6664 -vn -0.4406 0.6633 -0.6049 -vn -0.5291 0.6634 -0.5291 -vn -0.6049 0.6633 -0.4406 -vn -0.6664 0.6631 -0.3410 -vn -0.7120 0.6626 -0.2324 -vn -0.7401 0.6622 -0.1175 -vn -0.7496 0.6619 -0.0000 -vn -0.1317 0.5432 -0.8292 -vn -0.2605 0.5436 -0.7979 -vn -0.3822 0.5441 -0.7469 -vn -0.4939 0.5444 -0.6780 -vn -0.5931 0.5445 -0.5931 -vn -0.6780 0.5444 -0.4939 -vn -0.7469 0.5441 -0.3822 -vn -0.7979 0.5436 -0.2605 -vn -0.8292 0.5432 -0.1317 -vn -0.8398 0.5429 -0.0000 -vn -0.1395 0.4570 -0.8785 -vn -0.2760 0.4574 -0.8453 -vn -0.4050 0.4579 -0.7914 -vn -0.5233 0.4581 -0.7185 -vn -0.6285 0.4582 -0.6285 -vn -0.7185 0.4581 -0.5233 -vn -0.7914 0.4579 -0.4050 -vn -0.8453 0.4574 -0.2760 -vn -0.8785 0.4570 -0.1395 -vn -0.8896 0.4568 -0.0000 -vn 0.9466 -0.2854 -0.1503 -vn 0.9178 -0.3693 -0.1458 -vn 0.9110 -0.2857 -0.2974 -vn 0.8832 -0.3697 -0.2885 -vn 0.8530 -0.2860 -0.4365 -vn 0.8269 -0.3701 -0.4233 -vn 0.7745 -0.2862 -0.5641 -vn 0.7508 -0.3704 -0.5469 -vn 0.6775 -0.2863 -0.6775 -vn 0.6568 -0.3704 -0.6568 -vn 0.5641 -0.2862 -0.7745 -vn 0.5469 -0.3704 -0.7508 -vn 0.4365 -0.2860 -0.8530 -vn 0.4233 -0.3701 -0.8269 -vn 0.2974 -0.2857 -0.9110 -vn 0.2885 -0.3697 -0.8832 -vn 0.1503 -0.2854 -0.9466 -vn 0.1458 -0.3693 -0.9178 -vn -0.0000 -0.2852 -0.9585 -vn -0.0000 -0.3691 -0.9294 -vn 0.9862 -0.0531 -0.1565 -vn 0.9493 -0.0532 -0.3098 -vn 0.8890 -0.0532 -0.4549 -vn 0.8072 -0.0532 -0.5879 -vn 0.7061 -0.0533 -0.7061 -vn 0.5879 -0.0532 -0.8072 -vn 0.4549 -0.0532 -0.8890 -vn 0.3098 -0.0532 -0.9493 -vn 0.1565 -0.0531 -0.9862 -vn -0.0000 -0.0531 -0.9986 -vn 0.9286 0.3406 -0.1474 -vn 0.8937 0.3409 -0.2917 -vn 0.8368 0.3413 -0.4282 -vn 0.7598 0.3414 -0.5533 -vn 0.6646 0.3415 -0.6646 -vn 0.5533 0.3414 -0.7598 -vn 0.4281 0.3413 -0.8368 -vn 0.2917 0.3409 -0.8937 -vn 0.1474 0.3406 -0.9286 -vn -0.0000 0.3404 -0.9403 -vn 0.5758 0.8125 -0.0914 -vn 0.5539 0.8127 -0.1808 -vn 0.5184 0.8129 -0.2653 -vn 0.4706 0.8131 -0.3428 -vn 0.4116 0.8131 -0.4116 -vn 0.3428 0.8131 -0.4706 -vn 0.2653 0.8129 -0.5184 -vn 0.1808 0.8127 -0.5539 -vn 0.0914 0.8125 -0.5758 -vn -0.0000 0.8123 -0.5833 -vn 0.0185 0.9998 -0.0030 -vn 0.0178 0.9998 -0.0059 -vn 0.0167 0.9998 -0.0087 -vn 0.0153 0.9998 -0.0112 -vn 0.0134 0.9998 -0.0134 -vn 0.0112 0.9998 -0.0153 -vn 0.0087 0.9998 -0.0167 -vn 0.0059 0.9998 -0.0178 -vn 0.0030 0.9998 -0.0185 -vn -0.0000 0.9998 -0.0189 -vn -0.3833 0.9216 0.0608 -vn -0.3686 0.9218 0.1203 -vn -0.3448 0.9219 0.1764 -vn -0.3129 0.9221 0.2278 -vn -0.2736 0.9221 0.2736 -vn -0.2278 0.9221 0.3129 -vn -0.1764 0.9219 0.3448 -vn -0.1203 0.9218 0.3686 -vn -0.0608 0.9216 0.3833 -vn -0.0000 0.9216 0.3883 -vn -0.6064 0.7893 0.0962 -vn -0.5832 0.7897 0.1904 -vn -0.5457 0.7900 0.2793 -vn -0.4953 0.7903 0.3607 -vn -0.4332 0.7904 0.4332 -vn -0.3607 0.7903 0.4953 -vn -0.2793 0.7900 0.5457 -vn -0.1904 0.7897 0.5832 -vn -0.0962 0.7893 0.6064 -vn -0.0000 0.7892 0.6142 -vn -0.7401 0.6622 0.1175 -vn -0.7120 0.6626 0.2324 -vn -0.6664 0.6630 0.3410 -vn -0.6049 0.6633 0.4406 -vn -0.5291 0.6634 0.5291 -vn -0.4406 0.6633 0.6049 -vn -0.3410 0.6630 0.6664 -vn -0.2324 0.6626 0.7120 -vn -0.1175 0.6622 0.7401 -vn -0.0000 0.6619 0.7496 -vn -0.8292 0.5432 0.1317 -vn -0.7979 0.5436 0.2605 -vn -0.7469 0.5441 0.3822 -vn -0.6780 0.5444 0.4939 -vn -0.5931 0.5445 0.5931 -vn -0.4939 0.5444 0.6780 -vn -0.3822 0.5441 0.7469 -vn -0.2605 0.5436 0.7979 -vn -0.1317 0.5432 0.8292 -vn -0.0000 0.5429 0.8398 -vn -0.8785 0.4570 0.1395 -vn -0.8453 0.4574 0.2760 -vn -0.7914 0.4579 0.4050 -vn -0.7185 0.4581 0.5233 -vn -0.6285 0.4582 0.6285 -vn -0.5233 0.4581 0.7185 -vn -0.4050 0.4579 0.7914 -vn -0.2760 0.4574 0.8453 -vn -0.1395 0.4570 0.8785 -vn -0.0000 0.4568 0.8896 -vn -0.1503 -0.2854 -0.9466 -vn -0.1458 -0.3693 -0.9178 -vn -0.2974 -0.2857 -0.9110 -vn -0.2885 -0.3697 -0.8832 -vn -0.4365 -0.2860 -0.8530 -vn -0.4233 -0.3701 -0.8269 -vn -0.5641 -0.2862 -0.7745 -vn -0.5469 -0.3704 -0.7508 -vn -0.6775 -0.2863 -0.6775 -vn -0.6568 -0.3704 -0.6568 -vn -0.7745 -0.2862 -0.5641 -vn -0.7508 -0.3704 -0.5469 -vn -0.8530 -0.2860 -0.4365 -vn -0.8269 -0.3701 -0.4233 -vn -0.9110 -0.2857 -0.2974 -vn -0.8832 -0.3697 -0.2885 -vn -0.9466 -0.2854 -0.1503 -vn -0.9178 -0.3693 -0.1458 -vn -0.1565 -0.0531 -0.9862 -vn -0.3098 -0.0532 -0.9493 -vn -0.4549 -0.0532 -0.8890 -vn -0.5879 -0.0532 -0.8072 -vn -0.7061 -0.0533 -0.7061 -vn -0.8072 -0.0532 -0.5879 -vn -0.8890 -0.0532 -0.4549 -vn -0.9493 -0.0532 -0.3098 -vn -0.9862 -0.0531 -0.1565 -vn -0.1474 0.3406 -0.9286 -vn -0.2917 0.3409 -0.8937 -vn -0.4282 0.3413 -0.8368 -vn -0.5533 0.3414 -0.7598 -vn -0.6646 0.3415 -0.6646 -vn -0.7598 0.3414 -0.5533 -vn -0.8368 0.3413 -0.4281 -vn -0.8937 0.3409 -0.2917 -vn -0.9286 0.3406 -0.1474 -vn -0.0914 0.8125 -0.5758 -vn -0.1808 0.8127 -0.5539 -vn -0.2653 0.8130 -0.5184 -vn -0.3428 0.8131 -0.4706 -vn -0.4116 0.8131 -0.4116 -vn -0.4706 0.8131 -0.3428 -vn -0.5184 0.8129 -0.2653 -vn -0.5539 0.8127 -0.1808 -vn -0.5758 0.8125 -0.0914 -vn -0.0030 0.9998 -0.0185 -vn -0.0059 0.9998 -0.0178 -vn -0.0087 0.9998 -0.0167 -vn -0.0112 0.9998 -0.0153 -vn -0.0134 0.9998 -0.0134 -vn -0.0153 0.9998 -0.0112 -vn -0.0167 0.9998 -0.0087 -vn -0.0178 0.9998 -0.0059 -vn -0.0185 0.9998 -0.0030 -vn 0.0608 0.9216 0.3833 -vn 0.1203 0.9218 0.3686 -vn 0.1764 0.9219 0.3448 -vn 0.2278 0.9221 0.3129 -vn 0.2736 0.9221 0.2736 -vn 0.3129 0.9221 0.2278 -vn 0.3448 0.9219 0.1764 -vn 0.3686 0.9218 0.1203 -vn 0.3833 0.9216 0.0608 -vn 0.0962 0.7893 0.6064 -vn 0.1904 0.7897 0.5832 -vn 0.2793 0.7900 0.5457 -vn 0.3607 0.7903 0.4953 -vn 0.4332 0.7904 0.4332 -vn 0.4953 0.7903 0.3607 -vn 0.5457 0.7901 0.2793 -vn 0.5832 0.7897 0.1904 -vn 0.6064 0.7893 0.0962 -vn 0.1175 0.6622 0.7401 -vn 0.2324 0.6626 0.7120 -vn 0.3410 0.6630 0.6664 -vn 0.4406 0.6633 0.6049 -vn 0.5291 0.6634 0.5291 -vn 0.6049 0.6633 0.4406 -vn 0.6664 0.6631 0.3410 -vn 0.7120 0.6626 0.2324 -vn 0.7401 0.6622 0.1175 -vn 0.1317 0.5432 0.8292 -vn 0.2605 0.5436 0.7979 -vn 0.3822 0.5441 0.7469 -vn 0.4939 0.5444 0.6780 -vn 0.5931 0.5445 0.5931 -vn 0.6780 0.5444 0.4939 -vn 0.7469 0.5441 0.3822 -vn 0.7979 0.5436 0.2605 -vn 0.8292 0.5432 0.1317 -vn 0.1395 0.4570 0.8785 -vn 0.2760 0.4574 0.8453 -vn 0.4050 0.4579 0.7914 -vn 0.5233 0.4581 0.7185 -vn 0.6285 0.4582 0.6285 -vn 0.7185 0.4581 0.5233 -vn 0.7914 0.4579 0.4050 -vn 0.8453 0.4574 0.2760 -vn 0.8785 0.4570 0.1395 -vn 0.8935 0.4261 -0.1419 -vn 0.9048 0.4258 -0.0000 -vn 0.8598 0.4265 -0.2807 -vn 0.8050 0.4269 -0.4120 -vn 0.7309 0.4272 -0.5323 -vn 0.6393 0.4273 -0.6393 -vn 0.5323 0.4272 -0.7309 -vn 0.4120 0.4269 -0.8050 -vn 0.2807 0.4265 -0.8598 -vn 0.1419 0.4261 -0.8935 -vn -0.0000 0.4258 -0.9048 -vn 0.8978 0.4168 -0.1425 -vn 0.9091 0.4165 -0.0000 -vn 0.8640 0.4172 -0.2820 -vn 0.8089 0.4176 -0.4139 -vn 0.7344 0.4178 -0.5349 -vn 0.6424 0.4179 -0.6424 -vn 0.5349 0.4178 -0.7344 -vn 0.4139 0.4176 -0.8089 -vn 0.2820 0.4172 -0.8640 -vn 0.1425 0.4168 -0.8978 -vn -0.0000 0.4165 -0.9091 -vn 0.9048 0.4008 -0.1437 -vn 0.9163 0.4006 -0.0000 -vn 0.8708 0.4012 -0.2843 -vn 0.8153 0.4016 -0.4172 -vn 0.7402 0.4019 -0.5391 -vn 0.6475 0.4020 -0.6475 -vn 0.5391 0.4019 -0.7402 -vn 0.4172 0.4016 -0.8153 -vn 0.2843 0.4012 -0.8708 -vn 0.1437 0.4008 -0.9048 -vn -0.0000 0.4006 -0.9163 -vn 0.9146 0.3775 -0.1452 -vn 0.9261 0.3773 -0.0000 -vn 0.8801 0.3779 -0.2873 -vn 0.8241 0.3783 -0.4217 -vn 0.7482 0.3785 -0.5449 -vn 0.6545 0.3786 -0.6545 -vn 0.5449 0.3785 -0.7482 -vn 0.4217 0.3783 -0.8241 -vn 0.2873 0.3779 -0.8801 -vn 0.1452 0.3775 -0.9146 -vn -0.0000 0.3773 -0.9261 -vn 0.9267 0.3457 -0.1471 -vn 0.9384 0.3455 -0.0000 -vn 0.8919 0.3461 -0.2912 -vn 0.8351 0.3465 -0.4273 -vn 0.7582 0.3467 -0.5522 -vn 0.6632 0.3468 -0.6632 -vn 0.5522 0.3467 -0.7582 -vn 0.4273 0.3465 -0.8351 -vn 0.2912 0.3461 -0.8919 -vn 0.1471 0.3457 -0.9267 -vn -0.0000 0.3455 -0.9384 -vn 0.9409 0.3040 -0.1494 -vn 0.9527 0.3038 -0.0000 -vn 0.9055 0.3043 -0.2956 -vn 0.8479 0.3047 -0.4339 -vn 0.7698 0.3049 -0.5607 -vn 0.6734 0.3049 -0.6734 -vn 0.5607 0.3049 -0.7698 -vn 0.4339 0.3047 -0.8479 -vn 0.2956 0.3043 -0.9055 -vn 0.1494 0.3040 -0.9409 -vn -0.0000 0.3038 -0.9527 -vn 0.9562 0.2504 -0.1518 -vn 0.9682 0.2503 -0.0000 -vn 0.9203 0.2507 -0.3004 -vn 0.8617 0.2510 -0.4410 -vn 0.7824 0.2512 -0.5699 -vn 0.6844 0.2513 -0.6844 -vn 0.5699 0.2512 -0.7824 -vn 0.4410 0.2510 -0.8617 -vn 0.3004 0.2507 -0.9203 -vn 0.1518 0.2504 -0.9562 -vn -0.0000 0.2503 -0.9682 -vn 0.9710 0.1829 -0.1542 -vn 0.9832 0.1828 -0.0000 -vn 0.9346 0.1831 -0.3051 -vn 0.8751 0.1833 -0.4479 -vn 0.7946 0.1834 -0.5788 -vn 0.6951 0.1835 -0.6951 -vn 0.5788 0.1834 -0.7946 -vn 0.4478 0.1833 -0.8751 -vn 0.3051 0.1831 -0.9346 -vn 0.1542 0.1829 -0.9710 -vn -0.0000 0.1828 -0.9832 -vn 0.9828 0.0990 -0.1561 -vn 0.9951 0.0990 -0.0000 -vn 0.9459 0.0991 -0.3088 -vn 0.8858 0.0993 -0.4533 -vn 0.8043 0.0993 -0.5858 -vn 0.7036 0.0994 -0.7036 -vn 0.5858 0.0993 -0.8043 -vn 0.4533 0.0993 -0.8858 -vn 0.3088 0.0991 -0.9459 -vn 0.1561 0.0990 -0.9828 -vn -0.0000 0.0990 -0.9951 -vn 0.9872 -0.0278 -0.1568 -vn 0.9996 -0.0278 -0.0000 -vn 0.9502 -0.0279 -0.3102 -vn 0.8898 -0.0279 -0.4554 -vn 0.8080 -0.0279 -0.5885 -vn 0.7068 -0.0279 -0.7068 -vn 0.5885 -0.0279 -0.8080 -vn 0.4554 -0.0279 -0.8898 -vn 0.3102 -0.0279 -0.9502 -vn 0.1568 -0.0278 -0.9872 -vn -0.0000 -0.0278 -0.9996 -vn -0.1419 0.4261 -0.8935 -vn -0.2807 0.4265 -0.8598 -vn -0.4120 0.4269 -0.8050 -vn -0.5323 0.4272 -0.7309 -vn -0.6393 0.4273 -0.6393 -vn -0.7309 0.4272 -0.5323 -vn -0.8050 0.4269 -0.4120 -vn -0.8598 0.4265 -0.2807 -vn -0.8935 0.4261 -0.1419 -vn -0.9048 0.4258 -0.0000 -vn -0.1425 0.4168 -0.8978 -vn -0.2820 0.4172 -0.8640 -vn -0.4139 0.4176 -0.8089 -vn -0.5349 0.4178 -0.7344 -vn -0.6424 0.4179 -0.6424 -vn -0.7344 0.4178 -0.5349 -vn -0.8089 0.4176 -0.4139 -vn -0.8640 0.4172 -0.2820 -vn -0.8978 0.4168 -0.1425 -vn -0.9091 0.4165 -0.0000 -vn -0.1437 0.4008 -0.9048 -vn -0.2843 0.4012 -0.8708 -vn -0.4172 0.4016 -0.8153 -vn -0.5391 0.4019 -0.7402 -vn -0.6475 0.4020 -0.6475 -vn -0.7402 0.4019 -0.5391 -vn -0.8153 0.4016 -0.4172 -vn -0.8708 0.4012 -0.2843 -vn -0.9048 0.4008 -0.1437 -vn -0.9163 0.4006 -0.0000 -vn -0.1452 0.3775 -0.9146 -vn -0.2873 0.3779 -0.8801 -vn -0.4217 0.3783 -0.8241 -vn -0.5449 0.3785 -0.7482 -vn -0.6545 0.3786 -0.6545 -vn -0.7482 0.3785 -0.5449 -vn -0.8241 0.3783 -0.4217 -vn -0.8801 0.3779 -0.2873 -vn -0.9146 0.3775 -0.1452 -vn -0.9261 0.3773 -0.0000 -vn -0.1471 0.3457 -0.9267 -vn -0.2912 0.3461 -0.8919 -vn -0.4273 0.3465 -0.8351 -vn -0.5522 0.3467 -0.7582 -vn -0.6632 0.3468 -0.6632 -vn -0.7582 0.3467 -0.5522 -vn -0.8351 0.3465 -0.4273 -vn -0.8919 0.3461 -0.2912 -vn -0.9267 0.3457 -0.1471 -vn -0.9384 0.3455 -0.0000 -vn -0.1494 0.3040 -0.9409 -vn -0.2956 0.3043 -0.9055 -vn -0.4339 0.3047 -0.8479 -vn -0.5607 0.3049 -0.7698 -vn -0.6734 0.3049 -0.6734 -vn -0.7698 0.3049 -0.5607 -vn -0.8479 0.3047 -0.4339 -vn -0.9055 0.3043 -0.2956 -vn -0.9409 0.3040 -0.1494 -vn -0.9527 0.3038 -0.0000 -vn -0.1518 0.2504 -0.9562 -vn -0.3004 0.2507 -0.9203 -vn -0.4410 0.2510 -0.8617 -vn -0.5699 0.2512 -0.7824 -vn -0.6844 0.2513 -0.6844 -vn -0.7824 0.2512 -0.5699 -vn -0.8617 0.2510 -0.4410 -vn -0.9203 0.2507 -0.3004 -vn -0.9562 0.2504 -0.1518 -vn -0.9682 0.2503 -0.0000 -vn -0.1542 0.1829 -0.9710 -vn -0.3051 0.1831 -0.9346 -vn -0.4479 0.1833 -0.8751 -vn -0.5788 0.1834 -0.7946 -vn -0.6951 0.1835 -0.6951 -vn -0.7946 0.1834 -0.5788 -vn -0.8751 0.1833 -0.4478 -vn -0.9346 0.1831 -0.3051 -vn -0.9710 0.1829 -0.1542 -vn -0.9832 0.1828 -0.0000 -vn -0.1561 0.0990 -0.9828 -vn -0.3088 0.0991 -0.9459 -vn -0.4533 0.0993 -0.8858 -vn -0.5858 0.0993 -0.8043 -vn -0.7036 0.0994 -0.7036 -vn -0.8043 0.0993 -0.5858 -vn -0.8858 0.0993 -0.4533 -vn -0.9459 0.0991 -0.3088 -vn -0.9828 0.0990 -0.1561 -vn -0.9951 0.0990 -0.0000 -vn -0.1568 -0.0278 -0.9872 -vn -0.3102 -0.0279 -0.9502 -vn -0.4554 -0.0279 -0.8898 -vn -0.5885 -0.0279 -0.8080 -vn -0.7068 -0.0279 -0.7068 -vn -0.8080 -0.0279 -0.5885 -vn -0.8898 -0.0279 -0.4554 -vn -0.9502 -0.0279 -0.3102 -vn -0.9872 -0.0278 -0.1568 -vn -0.8635 0.5044 0.0002 -vn -0.8935 0.4261 0.1419 -vn -0.8598 0.4265 0.2807 -vn -0.8050 0.4269 0.4120 -vn -0.7309 0.4272 0.5323 -vn -0.6393 0.4273 0.6393 -vn -0.5323 0.4272 0.7309 -vn -0.4120 0.4269 0.8050 -vn -0.2807 0.4265 0.8598 -vn -0.1419 0.4261 0.8935 -vn -0.0000 0.4258 0.9048 -vn -0.8978 0.4168 0.1425 -vn -0.8640 0.4172 0.2820 -vn -0.8089 0.4176 0.4139 -vn -0.7344 0.4178 0.5349 -vn -0.6424 0.4179 0.6424 -vn -0.5349 0.4178 0.7344 -vn -0.4139 0.4176 0.8089 -vn -0.2820 0.4172 0.8640 -vn -0.1425 0.4168 0.8978 -vn -0.0000 0.4165 0.9091 -vn -0.9048 0.4008 0.1437 -vn -0.8708 0.4012 0.2843 -vn -0.8153 0.4016 0.4172 -vn -0.7402 0.4019 0.5391 -vn -0.6475 0.4020 0.6475 -vn -0.5391 0.4019 0.7402 -vn -0.4172 0.4016 0.8153 -vn -0.2843 0.4012 0.8708 -vn -0.1437 0.4008 0.9048 -vn -0.0000 0.4006 0.9163 -vn -0.9146 0.3775 0.1452 -vn -0.8801 0.3779 0.2873 -vn -0.8241 0.3783 0.4217 -vn -0.7482 0.3785 0.5449 -vn -0.6545 0.3786 0.6545 -vn -0.5449 0.3785 0.7482 -vn -0.4217 0.3783 0.8241 -vn -0.2873 0.3779 0.8801 -vn -0.1452 0.3775 0.9146 -vn -0.0000 0.3773 0.9261 -vn -0.9267 0.3457 0.1471 -vn -0.8919 0.3461 0.2912 -vn -0.8351 0.3465 0.4273 -vn -0.7582 0.3467 0.5522 -vn -0.6632 0.3468 0.6632 -vn -0.5522 0.3467 0.7582 -vn -0.4273 0.3465 0.8351 -vn -0.2912 0.3461 0.8919 -vn -0.1471 0.3457 0.9267 -vn -0.0000 0.3455 0.9384 -vn -0.9409 0.3040 0.1494 -vn -0.9055 0.3043 0.2956 -vn -0.8479 0.3047 0.4339 -vn -0.7698 0.3049 0.5607 -vn -0.6734 0.3049 0.6734 -vn -0.5607 0.3049 0.7698 -vn -0.4339 0.3047 0.8479 -vn -0.2956 0.3043 0.9055 -vn -0.1494 0.3040 0.9409 -vn -0.0000 0.3038 0.9527 -vn -0.9562 0.2504 0.1518 -vn -0.9203 0.2507 0.3004 -vn -0.8617 0.2510 0.4410 -vn -0.7824 0.2512 0.5699 -vn -0.6844 0.2513 0.6844 -vn -0.5699 0.2512 0.7824 -vn -0.4410 0.2510 0.8617 -vn -0.3004 0.2507 0.9203 -vn -0.1518 0.2504 0.9562 -vn -0.0000 0.2503 0.9682 -vn -0.9710 0.1829 0.1542 -vn -0.9346 0.1831 0.3051 -vn -0.8751 0.1833 0.4479 -vn -0.7946 0.1834 0.5788 -vn -0.6951 0.1835 0.6951 -vn -0.5788 0.1834 0.7946 -vn -0.4478 0.1833 0.8751 -vn -0.3051 0.1831 0.9346 -vn -0.1542 0.1829 0.9710 -vn -0.0000 0.1828 0.9832 -vn -0.9828 0.0990 0.1561 -vn -0.9459 0.0991 0.3088 -vn -0.8858 0.0993 0.4533 -vn -0.8043 0.0993 0.5858 -vn -0.7036 0.0994 0.7036 -vn -0.5858 0.0993 0.8043 -vn -0.4533 0.0993 0.8858 -vn -0.3088 0.0991 0.9459 -vn -0.1561 0.0990 0.9828 -vn -0.0000 0.0990 0.9951 -vn -0.9872 -0.0278 0.1568 -vn -0.9502 -0.0279 0.3102 -vn -0.8898 -0.0279 0.4554 -vn -0.8080 -0.0279 0.5885 -vn -0.7068 -0.0279 0.7068 -vn -0.5885 -0.0279 0.8080 -vn -0.4554 -0.0279 0.8898 -vn -0.3102 -0.0279 0.9502 -vn -0.1568 -0.0278 0.9872 -vn -0.0000 -0.0278 0.9996 -vn 0.1419 0.4261 0.8935 -vn 0.2807 0.4265 0.8598 -vn 0.4120 0.4269 0.8050 -vn 0.5323 0.4272 0.7309 -vn 0.6393 0.4273 0.6393 -vn 0.7309 0.4272 0.5323 -vn 0.8050 0.4269 0.4120 -vn 0.8598 0.4265 0.2807 -vn 0.8935 0.4261 0.1419 -vn 0.1425 0.4168 0.8978 -vn 0.2820 0.4172 0.8640 -vn 0.4139 0.4176 0.8089 -vn 0.5349 0.4178 0.7344 -vn 0.6424 0.4179 0.6424 -vn 0.7344 0.4178 0.5349 -vn 0.8089 0.4176 0.4139 -vn 0.8640 0.4172 0.2820 -vn 0.8978 0.4168 0.1425 -vn 0.1437 0.4008 0.9048 -vn 0.2843 0.4012 0.8708 -vn 0.4172 0.4016 0.8153 -vn 0.5391 0.4019 0.7402 -vn 0.6475 0.4020 0.6475 -vn 0.7402 0.4019 0.5391 -vn 0.8153 0.4016 0.4172 -vn 0.8708 0.4012 0.2843 -vn 0.9048 0.4008 0.1437 -vn 0.1452 0.3775 0.9146 -vn 0.2873 0.3779 0.8801 -vn 0.4217 0.3783 0.8241 -vn 0.5449 0.3785 0.7482 -vn 0.6545 0.3786 0.6545 -vn 0.7482 0.3785 0.5449 -vn 0.8241 0.3783 0.4217 -vn 0.8801 0.3779 0.2873 -vn 0.9146 0.3775 0.1452 -vn 0.1471 0.3457 0.9267 -vn 0.2912 0.3461 0.8919 -vn 0.4273 0.3465 0.8351 -vn 0.5522 0.3467 0.7582 -vn 0.6632 0.3468 0.6632 -vn 0.7582 0.3467 0.5522 -vn 0.8351 0.3465 0.4273 -vn 0.8919 0.3461 0.2912 -vn 0.9267 0.3457 0.1471 -vn 0.1494 0.3040 0.9409 -vn 0.2956 0.3043 0.9055 -vn 0.4339 0.3047 0.8479 -vn 0.5607 0.3049 0.7698 -vn 0.6734 0.3049 0.6734 -vn 0.7698 0.3049 0.5607 -vn 0.8479 0.3047 0.4339 -vn 0.9055 0.3043 0.2956 -vn 0.9409 0.3040 0.1494 -vn 0.1518 0.2504 0.9562 -vn 0.3004 0.2507 0.9203 -vn 0.4410 0.2510 0.8617 -vn 0.5699 0.2512 0.7824 -vn 0.6844 0.2513 0.6844 -vn 0.7824 0.2512 0.5699 -vn 0.8617 0.2510 0.4410 -vn 0.9203 0.2507 0.3004 -vn 0.9562 0.2504 0.1518 -vn 0.1542 0.1829 0.9710 -vn 0.3051 0.1831 0.9346 -vn 0.4479 0.1833 0.8751 -vn 0.5788 0.1834 0.7946 -vn 0.6951 0.1835 0.6951 -vn 0.7946 0.1834 0.5788 -vn 0.8751 0.1833 0.4478 -vn 0.9346 0.1831 0.3051 -vn 0.9710 0.1829 0.1542 -vn 0.1561 0.0990 0.9828 -vn 0.3088 0.0991 0.9459 -vn 0.4533 0.0993 0.8858 -vn 0.5858 0.0993 0.8043 -vn 0.7036 0.0994 0.7036 -vn 0.8043 0.0993 0.5858 -vn 0.8858 0.0993 0.4533 -vn 0.9459 0.0991 0.3088 -vn 0.9828 0.0990 0.1561 -vn 0.1568 -0.0278 0.9872 -vn 0.3102 -0.0279 0.9502 -vn 0.4554 -0.0279 0.8898 -vn 0.5885 -0.0279 0.8080 -vn 0.7068 -0.0279 0.7068 -vn 0.8080 -0.0279 0.5885 -vn 0.8898 -0.0279 0.4554 -vn 0.9502 -0.0279 0.3102 -vn 0.9872 -0.0278 0.1568 -vn 0.9654 -0.2110 -0.1533 -vn 0.9775 -0.2109 -0.0000 -vn 0.9292 -0.2112 -0.3034 -vn 0.8701 -0.2115 -0.4453 -vn 0.7900 -0.2116 -0.5754 -vn 0.6911 -0.2117 -0.6911 -vn 0.5754 -0.2116 -0.7900 -vn 0.4453 -0.2115 -0.8701 -vn 0.3034 -0.2112 -0.9292 -vn 0.1533 -0.2110 -0.9654 -vn -0.0000 -0.2109 -0.9775 -vn 0.9068 -0.3961 -0.1440 -vn 0.9183 -0.3959 -0.0000 -vn 0.8727 -0.3965 -0.2849 -vn 0.8171 -0.3969 -0.4181 -vn 0.7418 -0.3972 -0.5403 -vn 0.6489 -0.3973 -0.6489 -vn 0.5403 -0.3972 -0.7418 -vn 0.4181 -0.3969 -0.8171 -vn 0.2849 -0.3965 -0.8727 -vn 0.1440 -0.3961 -0.9068 -vn -0.0000 -0.3959 -0.9183 -vn 0.8314 -0.5397 -0.1320 -vn 0.8420 -0.5395 -0.0000 -vn 0.8000 -0.5402 -0.2612 -vn 0.7489 -0.5406 -0.3832 -vn 0.6798 -0.5409 -0.4952 -vn 0.5947 -0.5410 -0.5947 -vn 0.4952 -0.5409 -0.6798 -vn 0.3832 -0.5406 -0.7489 -vn 0.2612 -0.5402 -0.8000 -vn 0.1320 -0.5397 -0.8314 -vn -0.0000 -0.5395 -0.8420 -vn 0.7569 -0.6424 -0.1202 -vn 0.7666 -0.6421 -0.0000 -vn 0.7282 -0.6428 -0.2377 -vn 0.6816 -0.6433 -0.3488 -vn 0.6187 -0.6436 -0.4506 -vn 0.5411 -0.6437 -0.5411 -vn 0.4506 -0.6436 -0.6187 -vn 0.3488 -0.6433 -0.6816 -vn 0.2377 -0.6428 -0.7282 -vn 0.1202 -0.6424 -0.7569 -vn -0.0000 -0.6421 -0.7666 -vn 0.6943 -0.7112 -0.1102 -vn 0.7032 -0.7110 -0.0000 -vn 0.6678 -0.7117 -0.2180 -vn 0.6250 -0.7121 -0.3198 -vn 0.5673 -0.7123 -0.4132 -vn 0.4962 -0.7124 -0.4962 -vn 0.4132 -0.7123 -0.5673 -vn 0.3198 -0.7121 -0.6250 -vn 0.2180 -0.7117 -0.6678 -vn 0.1102 -0.7112 -0.6943 -vn -0.0000 -0.7110 -0.7032 -vn 0.6500 -0.7529 -0.1032 -vn 0.6584 -0.7527 -0.0000 -vn 0.6252 -0.7533 -0.2041 -vn 0.5851 -0.7537 -0.2994 -vn 0.5310 -0.7539 -0.3868 -vn 0.4645 -0.7540 -0.4645 -vn 0.3868 -0.7539 -0.5310 -vn 0.2994 -0.7537 -0.5851 -vn 0.2041 -0.7533 -0.6252 -vn 0.1032 -0.7529 -0.6500 -vn -0.0000 -0.7527 -0.6584 -vn 0.6315 -0.7689 -0.1002 -vn 0.6396 -0.7687 -0.0000 -vn 0.6074 -0.7693 -0.1983 -vn 0.5684 -0.7696 -0.2908 -vn 0.5158 -0.7699 -0.3757 -vn 0.4512 -0.7700 -0.4512 -vn 0.3757 -0.7699 -0.5158 -vn 0.2908 -0.7696 -0.5684 -vn 0.1983 -0.7693 -0.6074 -vn 0.1002 -0.7689 -0.6315 -vn -0.0000 -0.7687 -0.6396 -vn 0.6566 -0.7470 -0.1042 -vn 0.6650 -0.7468 -0.0000 -vn 0.6315 -0.7474 -0.2061 -vn 0.5910 -0.7478 -0.3024 -vn 0.5364 -0.7481 -0.3907 -vn 0.4692 -0.7482 -0.4692 -vn 0.3907 -0.7481 -0.5364 -vn 0.3024 -0.7478 -0.5910 -vn 0.2061 -0.7474 -0.6315 -vn 0.1042 -0.7470 -0.6566 -vn -0.0000 -0.7468 -0.6650 -vn 0.7884 -0.6024 -0.1251 -vn 0.7984 -0.6021 -0.0000 -vn 0.7585 -0.6028 -0.2476 -vn 0.7100 -0.6032 -0.3633 -vn 0.6445 -0.6035 -0.4695 -vn 0.5638 -0.6036 -0.5638 -vn 0.4695 -0.6035 -0.6445 -vn 0.3633 -0.6032 -0.7100 -vn 0.2476 -0.6028 -0.7585 -vn 0.1251 -0.6024 -0.7884 -vn -0.0000 -0.6021 -0.7984 -vn 0.9377 -0.3139 -0.1488 -vn 0.9495 -0.3137 -0.0000 -vn 0.9025 -0.3142 -0.2946 -vn 0.8450 -0.3145 -0.4324 -vn 0.7673 -0.3147 -0.5588 -vn 0.6712 -0.3148 -0.6712 -vn 0.5588 -0.3147 -0.7673 -vn 0.4324 -0.3145 -0.8450 -vn 0.2946 -0.3142 -0.9025 -vn 0.1488 -0.3139 -0.9377 -vn -0.0000 -0.3137 -0.9495 -vn -0.1533 -0.2110 -0.9654 -vn -0.3034 -0.2112 -0.9292 -vn -0.4453 -0.2115 -0.8701 -vn -0.5754 -0.2116 -0.7900 -vn -0.6911 -0.2117 -0.6911 -vn -0.7900 -0.2116 -0.5754 -vn -0.8701 -0.2115 -0.4453 -vn -0.9292 -0.2112 -0.3034 -vn -0.9654 -0.2110 -0.1533 -vn -0.9775 -0.2109 -0.0000 -vn -0.1440 -0.3961 -0.9068 -vn -0.2849 -0.3965 -0.8727 -vn -0.4181 -0.3969 -0.8171 -vn -0.5403 -0.3972 -0.7418 -vn -0.6489 -0.3973 -0.6489 -vn -0.7418 -0.3972 -0.5403 -vn -0.8171 -0.3969 -0.4181 -vn -0.8727 -0.3965 -0.2849 -vn -0.9068 -0.3961 -0.1440 -vn -0.9183 -0.3959 -0.0000 -vn -0.1320 -0.5397 -0.8314 -vn -0.2612 -0.5402 -0.8000 -vn -0.3832 -0.5406 -0.7489 -vn -0.4952 -0.5409 -0.6798 -vn -0.5947 -0.5410 -0.5947 -vn -0.6798 -0.5409 -0.4952 -vn -0.7489 -0.5406 -0.3832 -vn -0.8000 -0.5402 -0.2612 -vn -0.8314 -0.5397 -0.1320 -vn -0.8420 -0.5395 -0.0000 -vn -0.1202 -0.6424 -0.7569 -vn -0.2377 -0.6428 -0.7282 -vn -0.3488 -0.6433 -0.6816 -vn -0.4506 -0.6436 -0.6187 -vn -0.5411 -0.6437 -0.5411 -vn -0.6187 -0.6436 -0.4506 -vn -0.6816 -0.6433 -0.3488 -vn -0.7282 -0.6428 -0.2377 -vn -0.7569 -0.6424 -0.1202 -vn -0.7666 -0.6421 -0.0000 -vn -0.1102 -0.7112 -0.6943 -vn -0.2180 -0.7117 -0.6678 -vn -0.3198 -0.7121 -0.6250 -vn -0.4132 -0.7123 -0.5673 -vn -0.4962 -0.7124 -0.4962 -vn -0.5673 -0.7123 -0.4132 -vn -0.6250 -0.7121 -0.3198 -vn -0.6678 -0.7117 -0.2180 -vn -0.6943 -0.7112 -0.1102 -vn -0.7032 -0.7110 -0.0000 -vn -0.1032 -0.7529 -0.6500 -vn -0.2041 -0.7533 -0.6252 -vn -0.2994 -0.7537 -0.5851 -vn -0.3868 -0.7539 -0.5310 -vn -0.4645 -0.7540 -0.4645 -vn -0.5310 -0.7539 -0.3868 -vn -0.5851 -0.7537 -0.2994 -vn -0.6252 -0.7533 -0.2041 -vn -0.6500 -0.7529 -0.1032 -vn -0.6584 -0.7527 -0.0000 -vn -0.1002 -0.7689 -0.6315 -vn -0.1983 -0.7693 -0.6074 -vn -0.2908 -0.7696 -0.5684 -vn -0.3757 -0.7699 -0.5158 -vn -0.4512 -0.7700 -0.4512 -vn -0.5158 -0.7699 -0.3757 -vn -0.5684 -0.7696 -0.2908 -vn -0.6074 -0.7693 -0.1983 -vn -0.6315 -0.7689 -0.1002 -vn -0.6396 -0.7687 -0.0000 -vn -0.1042 -0.7470 -0.6566 -vn -0.2061 -0.7474 -0.6315 -vn -0.3024 -0.7478 -0.5910 -vn -0.3907 -0.7481 -0.5364 -vn -0.4692 -0.7482 -0.4692 -vn -0.5364 -0.7481 -0.3907 -vn -0.5910 -0.7478 -0.3024 -vn -0.6315 -0.7474 -0.2061 -vn -0.6566 -0.7470 -0.1042 -vn -0.6650 -0.7468 -0.0000 -vn -0.1251 -0.6024 -0.7884 -vn -0.2476 -0.6028 -0.7585 -vn -0.3633 -0.6032 -0.7100 -vn -0.4694 -0.6035 -0.6445 -vn -0.5638 -0.6036 -0.5638 -vn -0.6445 -0.6035 -0.4694 -vn -0.7100 -0.6032 -0.3633 -vn -0.7585 -0.6028 -0.2476 -vn -0.7884 -0.6024 -0.1251 -vn -0.7984 -0.6021 -0.0000 -vn -0.1488 -0.3139 -0.9377 -vn -0.2946 -0.3142 -0.9025 -vn -0.4324 -0.3145 -0.8450 -vn -0.5588 -0.3147 -0.7673 -vn -0.6712 -0.3148 -0.6712 -vn -0.7673 -0.3147 -0.5588 -vn -0.8450 -0.3145 -0.4324 -vn -0.9025 -0.3142 -0.2946 -vn -0.9377 -0.3139 -0.1488 -vn -0.9495 -0.3137 -0.0000 -vn -0.9654 -0.2110 0.1533 -vn -0.9292 -0.2112 0.3034 -vn -0.8701 -0.2115 0.4453 -vn -0.7900 -0.2116 0.5754 -vn -0.6911 -0.2117 0.6911 -vn -0.5754 -0.2116 0.7900 -vn -0.4453 -0.2115 0.8701 -vn -0.3034 -0.2112 0.9292 -vn -0.1533 -0.2110 0.9654 -vn -0.0000 -0.2109 0.9775 -vn -0.9068 -0.3961 0.1440 -vn -0.8727 -0.3965 0.2849 -vn -0.8171 -0.3969 0.4181 -vn -0.7418 -0.3972 0.5403 -vn -0.6489 -0.3973 0.6489 -vn -0.5403 -0.3972 0.7418 -vn -0.4181 -0.3969 0.8171 -vn -0.2849 -0.3965 0.8727 -vn -0.1440 -0.3961 0.9068 -vn -0.0000 -0.3959 0.9183 -vn -0.8314 -0.5397 0.1320 -vn -0.8000 -0.5402 0.2612 -vn -0.7489 -0.5406 0.3832 -vn -0.6798 -0.5409 0.4952 -vn -0.5947 -0.5410 0.5947 -vn -0.4952 -0.5409 0.6798 -vn -0.3832 -0.5406 0.7489 -vn -0.2612 -0.5402 0.8000 -vn -0.1320 -0.5397 0.8314 -vn -0.0000 -0.5395 0.8420 -vn -0.7569 -0.6424 0.1202 -vn -0.7282 -0.6428 0.2377 -vn -0.6816 -0.6433 0.3488 -vn -0.6187 -0.6436 0.4506 -vn -0.5411 -0.6437 0.5411 -vn -0.4506 -0.6436 0.6187 -vn -0.3488 -0.6433 0.6816 -vn -0.2377 -0.6428 0.7282 -vn -0.1202 -0.6424 0.7569 -vn -0.0000 -0.6421 0.7666 -vn -0.6943 -0.7112 0.1102 -vn -0.6678 -0.7117 0.2180 -vn -0.6250 -0.7121 0.3198 -vn -0.5673 -0.7123 0.4132 -vn -0.4962 -0.7124 0.4962 -vn -0.4132 -0.7123 0.5673 -vn -0.3198 -0.7121 0.6250 -vn -0.2180 -0.7117 0.6678 -vn -0.1102 -0.7112 0.6943 -vn -0.0000 -0.7110 0.7032 -vn -0.6500 -0.7529 0.1032 -vn -0.6252 -0.7533 0.2041 -vn -0.5851 -0.7537 0.2994 -vn -0.5310 -0.7539 0.3868 -vn -0.4645 -0.7540 0.4645 -vn -0.3868 -0.7539 0.5310 -vn -0.2994 -0.7537 0.5851 -vn -0.2041 -0.7533 0.6252 -vn -0.1032 -0.7529 0.6500 -vn -0.0000 -0.7527 0.6584 -vn -0.6315 -0.7689 0.1002 -vn -0.6074 -0.7693 0.1983 -vn -0.5684 -0.7696 0.2908 -vn -0.5158 -0.7699 0.3757 -vn -0.4512 -0.7700 0.4512 -vn -0.3757 -0.7699 0.5158 -vn -0.2908 -0.7696 0.5684 -vn -0.1983 -0.7693 0.6074 -vn -0.1002 -0.7689 0.6315 -vn -0.0000 -0.7687 0.6396 -vn -0.6566 -0.7470 0.1042 -vn -0.6315 -0.7474 0.2061 -vn -0.5910 -0.7478 0.3024 -vn -0.5364 -0.7481 0.3907 -vn -0.4692 -0.7482 0.4692 -vn -0.3907 -0.7481 0.5364 -vn -0.3024 -0.7478 0.5910 -vn -0.2061 -0.7474 0.6315 -vn -0.1042 -0.7470 0.6566 -vn -0.0000 -0.7468 0.6650 -vn -0.7884 -0.6024 0.1251 -vn -0.7585 -0.6028 0.2476 -vn -0.7100 -0.6032 0.3633 -vn -0.6445 -0.6035 0.4695 -vn -0.5638 -0.6036 0.5638 -vn -0.4695 -0.6035 0.6445 -vn -0.3633 -0.6032 0.7100 -vn -0.2476 -0.6028 0.7585 -vn -0.1251 -0.6024 0.7884 -vn -0.0000 -0.6021 0.7984 -vn -0.9377 -0.3139 0.1488 -vn -0.9025 -0.3142 0.2946 -vn -0.8450 -0.3145 0.4324 -vn -0.7673 -0.3147 0.5588 -vn -0.6712 -0.3148 0.6712 -vn -0.5588 -0.3147 0.7673 -vn -0.4324 -0.3145 0.8450 -vn -0.2946 -0.3142 0.9025 -vn -0.1488 -0.3139 0.9377 -vn -0.0000 -0.3137 0.9495 -vn 0.1533 -0.2110 0.9654 -vn 0.3034 -0.2112 0.9292 -vn 0.4453 -0.2115 0.8701 -vn 0.5754 -0.2116 0.7900 -vn 0.6911 -0.2117 0.6911 -vn 0.7900 -0.2116 0.5754 -vn 0.8701 -0.2115 0.4453 -vn 0.9292 -0.2112 0.3034 -vn 0.9654 -0.2110 0.1533 -vn 0.1440 -0.3961 0.9068 -vn 0.2849 -0.3965 0.8727 -vn 0.4181 -0.3969 0.8171 -vn 0.5403 -0.3972 0.7418 -vn 0.6489 -0.3973 0.6489 -vn 0.7418 -0.3972 0.5403 -vn 0.8171 -0.3969 0.4181 -vn 0.8727 -0.3965 0.2849 -vn 0.9068 -0.3961 0.1440 -vn 0.1320 -0.5397 0.8314 -vn 0.2612 -0.5402 0.8000 -vn 0.3832 -0.5406 0.7489 -vn 0.4952 -0.5409 0.6798 -vn 0.5947 -0.5410 0.5947 -vn 0.6798 -0.5409 0.4952 -vn 0.7489 -0.5406 0.3832 -vn 0.8000 -0.5402 0.2612 -vn 0.8314 -0.5397 0.1320 -vn 0.1202 -0.6424 0.7569 -vn 0.2377 -0.6428 0.7282 -vn 0.3488 -0.6433 0.6816 -vn 0.4506 -0.6436 0.6187 -vn 0.5411 -0.6437 0.5411 -vn 0.6187 -0.6436 0.4506 -vn 0.6816 -0.6433 0.3488 -vn 0.7282 -0.6428 0.2377 -vn 0.7569 -0.6424 0.1202 -vn 0.1102 -0.7112 0.6943 -vn 0.2180 -0.7117 0.6678 -vn 0.3198 -0.7121 0.6250 -vn 0.4132 -0.7123 0.5673 -vn 0.4962 -0.7124 0.4962 -vn 0.5673 -0.7123 0.4132 -vn 0.6250 -0.7121 0.3198 -vn 0.6678 -0.7117 0.2180 -vn 0.6943 -0.7112 0.1102 -vn 0.1032 -0.7529 0.6500 -vn 0.2041 -0.7533 0.6252 -vn 0.2994 -0.7537 0.5851 -vn 0.3868 -0.7539 0.5310 -vn 0.4645 -0.7540 0.4645 -vn 0.5310 -0.7539 0.3868 -vn 0.5851 -0.7537 0.2994 -vn 0.6252 -0.7533 0.2041 -vn 0.6500 -0.7529 0.1032 -vn 0.1002 -0.7689 0.6315 -vn 0.1983 -0.7693 0.6074 -vn 0.2908 -0.7696 0.5684 -vn 0.3757 -0.7699 0.5158 -vn 0.4512 -0.7700 0.4512 -vn 0.5158 -0.7699 0.3757 -vn 0.5684 -0.7696 0.2908 -vn 0.6074 -0.7693 0.1983 -vn 0.6315 -0.7689 0.1002 -vn 0.1042 -0.7470 0.6566 -vn 0.2061 -0.7474 0.6315 -vn 0.3024 -0.7478 0.5910 -vn 0.3907 -0.7481 0.5364 -vn 0.4692 -0.7482 0.4692 -vn 0.5364 -0.7481 0.3907 -vn 0.5910 -0.7478 0.3024 -vn 0.6315 -0.7474 0.2061 -vn 0.6566 -0.7470 0.1042 -vn 0.1251 -0.6024 0.7884 -vn 0.2476 -0.6028 0.7585 -vn 0.3633 -0.6032 0.7100 -vn 0.4694 -0.6035 0.6445 -vn 0.5638 -0.6036 0.5638 -vn 0.6445 -0.6035 0.4694 -vn 0.7100 -0.6032 0.3633 -vn 0.7585 -0.6028 0.2476 -vn 0.7884 -0.6024 0.1251 -vn 0.1488 -0.3139 0.9377 -vn 0.2946 -0.3142 0.9025 -vn 0.4324 -0.3145 0.8450 -vn 0.5588 -0.3147 0.7673 -vn 0.6712 -0.3148 0.6712 -vn 0.7673 -0.3147 0.5588 -vn 0.8450 -0.3145 0.4324 -vn 0.9025 -0.3142 0.2946 -vn 0.9377 -0.3139 0.1488 -vn 0.0050 -0.9854 -0.1703 -vn 0.0011 -0.9846 -0.1747 -vn 0.0011 -1.0000 -0.0000 -vn 0.0049 -1.0000 -0.0000 -vn 0.0049 -0.9259 -0.3777 -vn 0.0011 -0.9210 -0.3896 -vn 0.0043 -0.7784 -0.6278 -vn 0.0010 -0.7603 -0.6496 -vn 0.0027 -0.4737 -0.8807 -vn 0.0006 -0.4268 -0.9044 -vn -0.0001 0.0008 -1.0000 -vn -0.0001 0.0699 -0.9976 -vn -0.0031 0.4748 -0.8801 -vn -0.0008 0.5222 -0.8528 -vn -0.0052 0.7788 -0.6273 -vn -0.0012 0.7969 -0.6041 -vn -0.0063 0.9260 -0.3775 -vn -0.0015 0.9308 -0.3655 -vn -0.0068 0.9854 -0.1702 -vn -0.0016 0.9862 -0.1658 -vn -0.0069 1.0000 -0.0000 -vn -0.0016 1.0000 -0.0000 -vn 0.0175 -0.9853 -0.1698 -vn 0.0174 -0.9998 0.0001 -vn 0.0172 -0.9260 -0.3771 -vn 0.0153 -0.7786 -0.6274 -vn 0.0100 -0.4734 -0.8808 -vn 0.0003 0.0020 -1.0000 -vn -0.0109 0.4758 -0.8795 -vn -0.0188 0.7791 -0.6266 -vn -0.0230 0.9259 -0.3771 -vn -0.0248 0.9851 -0.1701 -vn -0.0252 0.9997 -0.0001 -vn 0.0421 -0.9846 -0.1695 -vn 0.0420 -0.9991 0.0002 -vn 0.0414 -0.9253 -0.3769 -vn 0.0369 -0.7776 -0.6277 -vn 0.0244 -0.4713 -0.8816 -vn 0.0011 0.0042 -1.0000 -vn -0.0261 0.4754 -0.8794 -vn -0.0457 0.7770 -0.6278 -vn -0.0562 0.9239 -0.3785 -vn -0.0608 0.9834 -0.1709 -vn -0.0619 0.9981 -0.0001 -vn 0.0844 -0.9819 -0.1697 -vn 0.0841 -0.9965 0.0004 -vn 0.0828 -0.9220 -0.3782 -vn 0.0739 -0.7726 -0.6306 -vn 0.0487 -0.4644 -0.8843 -vn 0.0020 0.0080 -1.0000 -vn -0.0521 0.4706 -0.8808 -vn -0.0917 0.7686 -0.6331 -vn -0.1135 0.9163 -0.3841 -vn -0.1232 0.9770 -0.1741 -vn -0.1256 0.9921 -0.0003 -vn 0.1536 -0.9731 -0.1718 -vn 0.1533 -0.9882 0.0008 -vn 0.1504 -0.9112 -0.3836 -vn 0.1334 -0.7577 -0.6388 -vn 0.0866 -0.4473 -0.8902 -vn 0.0023 0.0137 -0.9999 -vn -0.0931 0.4562 -0.8850 -vn -0.1647 0.7459 -0.6454 -vn -0.2057 0.8946 -0.3966 -vn -0.2246 0.9575 -0.1811 -vn -0.2294 0.9733 -0.0005 -vn 0.2652 -0.9477 -0.1776 -vn 0.2652 -0.9642 0.0012 -vn 0.2580 -0.8809 -0.3968 -vn 0.2251 -0.7200 -0.6565 -vn 0.1411 -0.4109 -0.9007 -vn 0.0004 0.0208 -0.9998 -vn -0.1538 0.4234 -0.8928 -vn -0.2729 0.6940 -0.6662 -vn -0.3450 0.8404 -0.4180 -vn -0.3796 0.9048 -0.1933 -vn -0.3887 0.9214 -0.0007 -vn 0.4400 -0.8777 -0.1900 -vn 0.4421 -0.8970 0.0018 -vn 0.4209 -0.8028 -0.4223 -vn 0.3551 -0.6350 -0.6860 -vn 0.2105 -0.3440 -0.9151 -vn -0.0064 0.0257 -0.9996 -vn -0.2343 0.3592 -0.9034 -vn -0.4152 0.5900 -0.6925 -vn -0.5306 0.7214 -0.4450 -vn -0.5882 0.7813 -0.2088 -vn -0.6039 0.7971 -0.0010 -vn 0.6785 -0.7043 -0.2091 -vn 0.6881 -0.7256 0.0022 -vn 0.6308 -0.6270 -0.4571 -vn 0.5072 -0.4739 -0.7198 -vn 0.2827 -0.2429 -0.9279 -vn -0.0169 0.0217 -0.9996 -vn -0.3198 0.2537 -0.9129 -vn -0.5629 0.4173 -0.7134 -vn -0.7212 0.5127 -0.4659 -vn -0.8012 0.5563 -0.2206 -vn -0.8234 0.5675 -0.0009 -vn 0.8952 -0.3849 -0.2246 -vn 0.9167 -0.3995 0.0016 -vn 0.8106 -0.3343 -0.4808 -vn 0.6294 -0.2442 -0.7377 -vn 0.3396 -0.1221 -0.9326 -vn -0.0218 0.0092 -0.9997 -vn -0.3789 0.1227 -0.9173 -vn -0.6631 0.2027 -0.7206 -vn -0.8460 0.2487 -0.4717 -vn -0.9370 0.2689 -0.2232 -vn -0.9619 0.2734 -0.0005 -vn 0.9727 -0.0529 -0.2259 -vn 0.9985 -0.0552 0.0004 -vn 0.8761 -0.0444 -0.4801 -vn 0.6782 -0.0309 -0.7342 -vn 0.3690 -0.0146 -0.9293 -vn -0.0146 0.0007 -0.9999 -vn -0.3932 0.0121 -0.9194 -vn -0.6922 0.0187 -0.7215 -vn -0.8819 0.0215 -0.4709 -vn -0.9748 0.0221 -0.2220 -vn -0.9998 0.0219 -0.0001 -vn -0.0068 0.9854 0.1702 -vn -0.0015 0.9862 0.1658 -vn -0.0062 0.9260 0.3775 -vn -0.0014 0.9308 0.3655 -vn -0.0051 0.7788 0.6273 -vn -0.0012 0.7969 0.6041 -vn -0.0030 0.4747 0.8801 -vn -0.0008 0.5222 0.8528 -vn -0.0001 0.0008 1.0000 -vn -0.0001 0.0698 0.9976 -vn 0.0026 -0.4737 0.8807 -vn 0.0005 -0.4268 0.9044 -vn 0.0042 -0.7784 0.6278 -vn 0.0009 -0.7603 0.6496 -vn 0.0047 -0.9259 0.3778 -vn 0.0011 -0.9210 0.3896 -vn 0.0049 -0.9854 0.1703 -vn 0.0011 -0.9846 0.1747 -vn -0.0247 0.9851 0.1700 -vn -0.0227 0.9259 0.3770 -vn -0.0185 0.7791 0.6266 -vn -0.0107 0.4757 0.8795 -vn 0.0002 0.0018 1.0000 -vn 0.0098 -0.4735 0.8807 -vn 0.0150 -0.7786 0.6273 -vn 0.0169 -0.9260 0.3771 -vn 0.0173 -0.9853 0.1700 -vn -0.0606 0.9835 0.1707 -vn -0.0557 0.9240 0.3783 -vn -0.0452 0.7772 0.6276 -vn -0.0258 0.4753 0.8795 -vn 0.0009 0.0036 1.0000 -vn 0.0239 -0.4718 0.8814 -vn 0.0363 -0.7778 0.6275 -vn 0.0408 -0.9254 0.3769 -vn 0.0418 -0.9846 0.1698 -vn -0.1228 0.9771 0.1736 -vn -0.1127 0.9166 0.3836 -vn -0.0909 0.7690 0.6327 -vn -0.0516 0.4704 0.8809 -vn 0.0016 0.0067 1.0000 -vn 0.0479 -0.4656 0.8837 -vn 0.0730 -0.7731 0.6301 -vn 0.0821 -0.9220 0.3783 -vn 0.0840 -0.9818 0.1704 -vn -0.2240 0.9578 0.1802 -vn -0.2046 0.8953 0.3957 -vn -0.1636 0.7467 0.6448 -vn -0.0926 0.4558 0.8852 -vn 0.0019 0.0112 0.9999 -vn 0.0856 -0.4496 0.8891 -vn 0.1324 -0.7585 0.6381 -vn 0.1495 -0.9112 0.3839 -vn 0.1531 -0.9730 0.1729 -vn -0.3789 0.9053 0.1918 -vn -0.3438 0.8415 0.4168 -vn -0.2718 0.6950 0.6657 -vn -0.1533 0.4224 0.8934 -vn 0.0003 0.0168 0.9999 -vn 0.1406 -0.4143 0.8992 -vn 0.2242 -0.7210 0.6556 -vn 0.2571 -0.8808 0.3976 -vn 0.2646 -0.9475 0.1796 -vn -0.5879 0.7820 0.2070 -vn -0.5302 0.7224 0.4439 -vn -0.4148 0.5903 0.6925 -vn -0.2336 0.3570 0.9044 -vn -0.0053 0.0212 0.9998 -vn 0.2114 -0.3473 0.9136 -vn 0.3552 -0.6356 0.6855 -vn 0.4204 -0.8023 0.4238 -vn 0.4395 -0.8773 0.1931 -vn -0.8016 0.5563 0.2190 -vn -0.7218 0.5124 0.4653 -vn -0.5631 0.4161 0.7140 -vn -0.3188 0.2509 0.9140 -vn -0.0147 0.0184 0.9997 -vn 0.2850 -0.2440 0.9270 -vn 0.5085 -0.4723 0.7199 -vn 0.6311 -0.6249 0.4595 -vn 0.6784 -0.7031 0.2130 -vn -0.9374 0.2679 0.2223 -vn -0.8467 0.2468 0.4714 -vn -0.6634 0.2002 0.7210 -vn -0.3782 0.1202 0.9179 -vn -0.0199 0.0080 0.9998 -vn 0.3416 -0.1206 0.9321 -vn 0.6307 -0.2401 0.7379 -vn 0.8114 -0.3298 0.4826 -vn 0.8956 -0.3823 0.2275 -vn -0.9749 0.0214 0.2218 -vn -0.8820 0.0203 0.4708 -vn -0.6922 0.0172 0.7215 -vn -0.3929 0.0110 0.9195 -vn -0.0140 0.0006 0.9999 -vn 0.3695 -0.0133 0.9291 -vn 0.6784 -0.0283 0.7342 -vn 0.8760 -0.0417 0.4804 -vn 0.9726 -0.0513 0.2266 -vn 0.9633 0.1460 -0.2251 -vn 0.9888 0.1493 0.0001 -vn 0.8682 0.1326 -0.4782 -vn 0.6735 0.1038 -0.7319 -vn 0.3691 0.0576 -0.9276 -vn -0.0095 -0.0011 -1.0000 -vn -0.3844 -0.0604 -0.9212 -vn -0.6814 -0.1080 -0.7239 -vn -0.8702 -0.1387 -0.4728 -vn -0.9626 -0.1538 -0.2229 -vn -0.9875 -0.1578 -0.0000 -vn 0.9375 0.2662 -0.2242 -vn 0.9625 0.2712 0.0004 -vn 0.8443 0.2439 -0.4772 -vn 0.6543 0.1932 -0.7312 -vn 0.3582 0.1084 -0.9273 -vn -0.0087 -0.0026 -1.0000 -vn -0.3708 -0.1168 -0.9213 -vn -0.6568 -0.2104 -0.7242 -vn -0.8381 -0.2716 -0.4730 -vn -0.9267 -0.3023 -0.2231 -vn -0.9506 -0.3106 -0.0002 -vn 0.9031 0.3688 -0.2201 -vn 0.9271 0.3748 0.0009 -vn 0.8140 0.3404 -0.4708 -vn 0.6319 0.2726 -0.7255 -vn 0.3464 0.1545 -0.9253 -vn -0.0079 -0.0040 -1.0000 -vn -0.3548 -0.1693 -0.9195 -vn -0.6241 -0.3046 -0.7195 -vn -0.7915 -0.3930 -0.4681 -vn -0.8718 -0.4375 -0.2204 -vn -0.8932 -0.4497 -0.0005 -vn 0.8612 0.4619 -0.2121 -vn 0.8834 0.4686 0.0012 -vn 0.7788 0.4293 -0.4574 -vn 0.6083 0.3479 -0.7134 -vn 0.3356 0.1995 -0.9206 -vn -0.0071 -0.0053 -1.0000 -vn -0.3388 -0.2192 -0.9150 -vn -0.5877 -0.3908 -0.7085 -vn -0.7359 -0.5000 -0.4565 -vn -0.8045 -0.5541 -0.2139 -vn -0.8223 -0.5690 -0.0007 -vn 0.8112 0.5493 -0.2007 -vn 0.8309 0.5564 0.0015 -vn 0.7377 0.5141 -0.4377 -vn 0.5825 0.4221 -0.6946 -vn 0.3251 0.2457 -0.9132 -vn -0.0065 -0.0063 -1.0000 -vn -0.3231 -0.2678 -0.9077 -vn -0.5493 -0.4694 -0.6913 -vn -0.6759 -0.5919 -0.4391 -vn -0.7315 -0.6506 -0.2041 -vn -0.7454 -0.6666 -0.0009 -vn 0.7516 0.6324 -0.1875 -vn 0.7685 0.6398 0.0016 -vn 0.6885 0.5956 -0.4138 -vn 0.5515 0.4960 -0.6707 -vn 0.3128 0.2937 -0.9033 -vn -0.0062 -0.0067 -1.0000 -vn -0.3067 -0.3152 -0.8981 -vn -0.5090 -0.5404 -0.6700 -vn -0.6143 -0.6689 -0.4184 -vn -0.6578 -0.7281 -0.1928 -vn -0.6683 -0.7439 -0.0009 -vn 0.6814 0.7107 -0.1749 -vn 0.6953 0.7187 0.0015 -vn 0.6289 0.6725 -0.3901 -vn 0.5117 0.5670 -0.6456 -vn 0.2954 0.3415 -0.8923 -vn -0.0064 -0.0068 -1.0000 -vn -0.2875 -0.3595 -0.8877 -vn -0.4660 -0.6022 -0.6482 -vn -0.5529 -0.7319 -0.3983 -vn -0.5869 -0.7889 -0.1821 -vn -0.5948 -0.8039 -0.0008 -vn 0.6000 0.7825 -0.1661 -vn 0.6110 0.7917 0.0012 -vn 0.5576 0.7417 -0.3728 -vn 0.4598 0.6297 -0.6262 -vn 0.2693 0.3836 -0.8833 -vn -0.0073 -0.0068 -1.0000 -vn -0.2634 -0.3967 -0.8794 -vn -0.4196 -0.6520 -0.6315 -vn -0.4926 -0.7812 -0.3835 -vn -0.5206 -0.8358 -0.1742 -vn -0.5269 -0.8499 -0.0006 -vn 0.5080 0.8454 -0.1653 -vn 0.5160 0.8566 0.0007 -vn 0.4745 0.7987 -0.3699 -vn 0.3942 0.6766 -0.6219 -vn 0.2323 0.4117 -0.8812 -vn -0.0085 -0.0072 -0.9999 -vn -0.2326 -0.4207 -0.8769 -vn -0.3696 -0.6862 -0.6265 -vn -0.4342 -0.8173 -0.3788 -vn -0.4595 -0.8715 -0.1715 -vn -0.4653 -0.8851 -0.0003 -vn 0.4555 0.8726 -0.1762 -vn 0.4227 0.8150 -0.3964 -vn 0.3409 0.6637 -0.6658 -vn 0.1747 0.3436 -0.9227 -vn -0.0588 -0.1181 -0.9913 -vn -0.2519 -0.5079 -0.8238 -vn -0.3602 -0.7317 -0.5787 -vn -0.4113 -0.8408 -0.3521 -vn -0.4321 -0.8873 -0.1613 -vn -0.4370 -0.8994 -0.0002 -vn -0.9627 -0.1535 0.2228 -vn -0.8703 -0.1382 0.4727 -vn -0.6815 -0.1075 0.7239 -vn -0.3844 -0.0600 0.9212 -vn -0.0093 -0.0011 1.0000 -vn 0.3693 0.0571 0.9276 -vn 0.6737 0.1030 0.7318 -vn 0.8683 0.1317 0.4783 -vn 0.9634 0.1455 0.2252 -vn -0.9270 -0.3018 0.2228 -vn -0.8385 -0.2707 0.4728 -vn -0.6571 -0.2093 0.7242 -vn -0.3706 -0.1160 0.9215 -vn -0.0080 -0.0024 1.0000 -vn 0.3591 0.1077 0.9271 -vn 0.6549 0.1919 0.7309 -vn 0.8446 0.2425 0.4774 -vn 0.9376 0.2653 0.2249 -vn -0.8722 -0.4370 0.2196 -vn -0.7921 -0.3921 0.4677 -vn -0.6245 -0.3035 0.7196 -vn -0.3544 -0.1682 0.9199 -vn -0.0065 -0.0035 1.0000 -vn 0.3480 0.1542 0.9247 -vn 0.6329 0.2714 0.7251 -vn 0.8143 0.3390 0.4712 -vn 0.9031 0.3679 0.2215 -vn -0.8050 -0.5539 0.2127 -vn -0.7367 -0.4994 0.4559 -vn -0.5881 -0.3899 0.7087 -vn -0.3381 -0.2180 0.9155 -vn -0.0052 -0.0043 1.0000 -vn 0.3376 0.1997 0.9199 -vn 0.6094 0.3471 0.7128 -vn 0.7790 0.4282 0.4581 -vn 0.8611 0.4611 0.2141 -vn -0.7319 -0.6506 0.2027 -vn -0.6765 -0.5917 0.4384 -vn -0.5495 -0.4689 0.6915 -vn -0.3221 -0.2666 0.9084 -vn -0.0041 -0.0048 1.0000 -vn 0.3272 0.2466 0.9122 -vn 0.5833 0.4220 0.6940 -vn 0.7376 0.5134 0.4386 -vn 0.8109 0.5488 0.2031 -vn -0.6581 -0.7283 0.1913 -vn -0.6146 -0.6692 0.4176 -vn -0.5089 -0.5403 0.6701 -vn -0.3053 -0.3142 0.8989 -vn -0.0035 -0.0049 1.0000 -vn 0.3146 0.2951 0.9022 -vn 0.5517 0.4964 0.6702 -vn 0.6879 0.5956 0.4149 -vn 0.7511 0.6322 0.1901 -vn -0.5869 -0.7892 0.1807 -vn -0.5528 -0.7324 0.3976 -vn -0.4655 -0.6025 0.6483 -vn -0.2858 -0.3588 0.8886 -vn -0.0037 -0.0049 1.0000 -vn 0.2966 0.3432 0.8912 -vn 0.5109 0.5679 0.6454 -vn 0.6276 0.6730 0.3913 -vn 0.6806 0.7109 0.1773 -vn -0.5203 -0.8362 0.1732 -vn -0.4921 -0.7818 0.3830 -vn -0.4186 -0.6524 0.6318 -vn -0.2613 -0.3961 0.8802 -vn -0.0048 -0.0050 1.0000 -vn 0.2695 0.3851 0.8826 -vn 0.4578 0.6308 0.6265 -vn 0.5556 0.7425 0.3742 -vn 0.5989 0.7830 0.1682 -vn -0.4590 -0.8718 0.1711 -vn -0.4332 -0.8178 0.3788 -vn -0.3680 -0.6865 0.6271 -vn -0.2304 -0.4202 0.8777 -vn -0.0069 -0.0060 1.0000 -vn 0.2312 0.4125 0.8811 -vn 0.3914 0.6773 0.6230 -vn 0.4718 0.7997 0.3713 -vn 0.5065 0.8460 0.1666 -vn -0.4314 -0.8876 0.1612 -vn -0.4099 -0.8413 0.3525 -vn -0.3582 -0.7319 0.5797 -vn -0.2497 -0.5073 0.8248 -vn -0.0579 -0.1173 0.9914 -vn 0.1730 0.3435 0.9231 -vn 0.3378 0.6638 0.6673 -vn 0.4199 0.8157 0.3979 -vn 0.4539 0.8733 0.1772 -vn -0.1823 0.9479 -0.2611 -vn -0.0867 0.9585 -0.2716 -vn -0.0829 0.9966 0.0016 -vn -0.1889 0.9820 0.0013 -vn -0.1566 0.8282 -0.5381 -vn -0.0837 0.8273 -0.5554 -vn -0.1041 0.6129 -0.7833 -vn -0.0639 0.5991 -0.7981 -vn -0.0258 0.3233 -0.9459 -vn -0.0238 0.3035 -0.9525 -vn 0.0692 -0.0012 -0.9976 -vn 0.0331 -0.0195 -0.9993 -vn 0.1682 -0.3200 -0.9324 -vn 0.0994 -0.3366 -0.9364 -vn 0.2569 -0.5945 -0.7619 -vn 0.1645 -0.6148 -0.7713 -vn 0.3226 -0.7917 -0.5188 -vn 0.2160 -0.8199 -0.5302 -vn 0.3594 -0.8986 -0.2516 -vn 0.2458 -0.9340 -0.2592 -vn 0.3706 -0.9288 -0.0004 -vn 0.2538 -0.9672 -0.0006 -vn -0.4027 0.8804 -0.2503 -vn -0.4225 0.9064 0.0007 -vn -0.3347 0.7847 -0.5218 -vn -0.2103 0.6003 -0.7716 -vn -0.0409 0.3362 -0.9409 -vn 0.1455 0.0308 -0.9889 -vn 0.3184 -0.2667 -0.9097 -vn 0.4553 -0.5135 -0.7274 -vn 0.5455 -0.6831 -0.4856 -vn 0.5913 -0.7722 -0.2325 -vn 0.6041 -0.7969 0.0001 -vn -0.6308 0.7394 -0.2355 -vn -0.6594 0.7518 -0.0001 -vn -0.5305 0.6843 -0.5003 -vn -0.3401 0.5564 -0.7581 -vn -0.0742 0.3451 -0.9356 -vn 0.2100 0.0835 -0.9741 -vn 0.4537 -0.1714 -0.8745 -vn 0.6289 -0.3761 -0.6804 -vn 0.7347 -0.5123 -0.4447 -vn 0.7847 -0.5831 -0.2102 -vn 0.7979 -0.6028 0.0006 -vn -0.7988 0.5604 -0.2189 -vn -0.8290 0.5592 -0.0009 -vn -0.6889 0.5476 -0.4748 -vn -0.4640 0.4854 -0.7410 -vn -0.1286 0.3430 -0.9305 -vn 0.2341 0.1390 -0.9622 -vn 0.5315 -0.0677 -0.8443 -vn 0.7311 -0.2328 -0.6413 -vn 0.8445 -0.3422 -0.4121 -vn 0.8960 -0.4000 -0.1930 -vn 0.9090 -0.4167 0.0009 -vn -0.8844 0.4156 -0.2122 -vn -0.9141 0.4056 -0.0017 -vn -0.7753 0.4297 -0.4629 -vn -0.5447 0.4130 -0.7299 -vn -0.1882 0.3279 -0.9258 -vn 0.2086 0.1771 -0.9618 -vn 0.5375 0.0087 -0.8432 -vn 0.7575 -0.1336 -0.6390 -vn 0.8820 -0.2323 -0.4101 -vn 0.9385 -0.2870 -0.1919 -vn 0.9528 -0.3035 0.0013 -vn -0.9106 0.3477 -0.2233 -vn -0.9428 0.3334 -0.0026 -vn -0.7961 0.3714 -0.4779 -vn -0.5669 0.3699 -0.7360 -vn -0.2289 0.3106 -0.9226 -vn 0.1489 0.1909 -0.9703 -vn 0.4813 0.0418 -0.8756 -vn 0.7232 -0.0987 -0.6836 -vn 0.8702 -0.2053 -0.4479 -vn 0.9400 -0.2678 -0.2115 -vn 0.9578 -0.2873 0.0018 -vn -0.8971 0.3631 -0.2516 -vn -0.9368 0.3499 -0.0036 -vn -0.7672 0.3800 -0.5167 -vn -0.5392 0.3675 -0.7577 -vn -0.2405 0.3031 -0.9221 -vn 0.0818 0.1859 -0.9792 -vn 0.3864 0.0324 -0.9218 -vn 0.6398 -0.1292 -0.7576 -vn 0.8145 -0.2646 -0.5162 -vn 0.9039 -0.3481 -0.2486 -vn 0.9273 -0.3742 0.0023 -vn -0.8431 0.4541 -0.2881 -vn -0.8927 0.4507 -0.0046 -vn -0.6966 0.4452 -0.5626 -vn -0.4780 0.4001 -0.7819 -vn -0.2283 0.3082 -0.9235 -vn 0.0291 0.1683 -0.9853 -vn 0.2860 -0.0149 -0.9581 -vn 0.5246 -0.2214 -0.8221 -vn 0.7067 -0.4058 -0.5796 -vn 0.8049 -0.5212 -0.2836 -vn 0.8310 -0.5562 0.0026 -vn -0.7348 0.6012 -0.3141 -vn -0.7876 0.6161 -0.0049 -vn -0.5895 0.5517 -0.5900 -vn -0.4002 0.4595 -0.7930 -vn -0.2023 0.3215 -0.9250 -vn -0.0004 0.1311 -0.9914 -vn 0.2078 -0.1139 -0.9715 -vn 0.4040 -0.3845 -0.8300 -vn 0.5467 -0.6107 -0.5729 -vn 0.6161 -0.7384 -0.2743 -vn 0.6336 -0.7737 0.0020 -vn -0.5962 0.7361 -0.3205 -vn -0.6414 0.7672 -0.0038 -vn -0.4733 0.6503 -0.5942 -vn -0.3221 0.5179 -0.7925 -vn -0.1660 0.3391 -0.9260 -vn -0.0032 0.1002 -0.9950 -vn 0.1660 -0.2026 -0.9651 -vn 0.3158 -0.5170 -0.7956 -vn 0.4092 -0.7482 -0.5222 -vn 0.4458 -0.8621 -0.2409 -vn 0.4533 -0.8914 0.0013 -vn 0.3606 -0.8983 0.2510 -vn 0.2432 -0.9349 0.2585 -vn 0.3246 -0.7911 0.5184 -vn 0.2115 -0.8209 0.5305 -vn 0.2593 -0.5936 0.7619 -vn 0.1597 -0.6148 0.7723 -vn 0.1702 -0.3186 0.9325 -vn 0.0964 -0.3355 0.9371 -vn 0.0703 0.0004 0.9975 -vn 0.0339 -0.0178 0.9993 -vn -0.0259 0.3244 0.9456 -vn -0.0181 0.3051 0.9521 -vn -0.1052 0.6130 0.7831 -vn -0.0542 0.6000 0.7982 -vn -0.1581 0.8274 0.5389 -vn -0.0733 0.8275 0.5567 -vn -0.1834 0.9472 0.2632 -vn -0.0801 0.9583 0.2743 -vn 0.5921 -0.7715 0.2326 -vn 0.5470 -0.6819 0.4856 -vn 0.4572 -0.5121 0.7272 -vn 0.3203 -0.2655 0.9093 -vn 0.1470 0.0315 0.9886 -vn -0.0404 0.3363 0.9409 -vn -0.2110 0.5997 0.7719 -vn -0.3360 0.7836 0.5225 -vn -0.4037 0.8797 0.2514 -vn 0.7850 -0.5824 0.2111 -vn 0.7352 -0.5112 0.4452 -vn 0.6298 -0.3748 0.6804 -vn 0.4553 -0.1703 0.8739 -vn 0.2123 0.0839 0.9736 -vn -0.0724 0.3446 0.9359 -vn -0.3402 0.5554 0.7589 -vn -0.5319 0.6830 0.5006 -vn -0.6319 0.7385 0.2354 -vn 0.8958 -0.3995 0.1946 -vn 0.8442 -0.3413 0.4132 -vn 0.7311 -0.2318 0.6417 -vn 0.5324 -0.0668 0.8438 -vn 0.2370 0.1394 0.9615 -vn -0.1253 0.3426 0.9311 -vn -0.4637 0.4841 0.7421 -vn -0.6907 0.5457 0.4745 -vn -0.8002 0.5590 0.2174 -vn 0.9382 -0.2864 0.1942 -vn 0.8814 -0.2315 0.4118 -vn 0.7570 -0.1328 0.6397 -vn 0.5382 0.0092 0.8428 -vn 0.2120 0.1773 0.9611 -vn -0.1837 0.3276 0.9268 -vn -0.5437 0.4116 0.7315 -vn -0.7772 0.4270 0.4621 -vn -0.8861 0.4135 0.2094 -vn 0.9395 -0.2670 0.2146 -vn 0.8694 -0.2043 0.4499 -vn 0.7226 -0.0982 0.6842 -vn 0.4822 0.0415 0.8751 -vn 0.1525 0.1901 0.9699 -vn -0.2239 0.3098 0.9241 -vn -0.5649 0.3685 0.7383 -vn -0.7977 0.3685 0.4773 -vn -0.9126 0.3451 0.2194 -vn 0.9033 -0.3469 0.2525 -vn 0.8136 -0.2632 0.5184 -vn 0.6395 -0.1290 0.7579 -vn 0.3879 0.0307 0.9212 -vn 0.0855 0.1831 0.9794 -vn -0.2356 0.3007 0.9242 -vn -0.5363 0.3658 0.7607 -vn -0.7681 0.3779 0.5169 -vn -0.8993 0.3609 0.2468 -vn 0.8042 -0.5200 0.2879 -vn 0.7057 -0.4043 0.5818 -vn 0.5245 -0.2219 0.8220 -vn 0.2879 -0.0186 0.9575 -vn 0.0326 0.1625 0.9862 -vn -0.2241 0.3027 0.9264 -vn -0.4748 0.3971 0.7854 -vn -0.6964 0.4447 0.5634 -vn -0.8449 0.4541 0.2827 -vn 0.6153 -0.7377 0.2777 -vn 0.5456 -0.6099 0.5748 -vn 0.4038 -0.3859 0.8295 -vn 0.2096 -0.1200 0.9704 -vn 0.0028 0.1209 0.9927 -vn -0.1994 0.3114 0.9291 -vn -0.3985 0.4540 0.7969 -vn -0.5888 0.5527 0.5898 -vn -0.7347 0.6046 0.3076 -vn 0.4449 -0.8618 0.2437 -vn 0.4065 -0.7482 0.5243 -vn 0.3128 -0.5205 0.7945 -vn 0.1658 -0.2111 0.9633 -vn -0.0008 0.0919 0.9958 -vn -0.1629 0.3367 0.9274 -vn -0.3189 0.5225 0.7908 -vn -0.4691 0.6592 0.5877 -vn -0.5917 0.7433 0.3121 -vn -0.4927 0.8231 -0.2824 -vn -0.5270 0.8499 -0.0020 -vn -0.3999 0.7425 -0.5374 -vn -0.2792 0.6144 -0.7379 -vn -0.1461 0.4359 -0.8880 -vn 0.0017 0.1873 -0.9823 -vn 0.1625 -0.1395 -0.9768 -vn 0.3065 -0.4866 -0.8181 -vn 0.3942 -0.7445 -0.5389 -vn 0.4266 -0.8698 -0.2478 -vn 0.4324 -0.9017 0.0012 -vn -0.4151 0.8759 -0.2458 -vn -0.4448 0.8956 -0.0015 -vn -0.3368 0.8176 -0.4669 -vn -0.2374 0.7267 -0.6447 -vn -0.1276 0.5984 -0.7909 -vn 0.0004 0.4069 -0.9135 -vn 0.1583 0.1155 -0.9806 -vn 0.3287 -0.2663 -0.9061 -vn 0.4512 -0.6183 -0.6435 -vn 0.4964 -0.8137 -0.3025 -vn 0.5022 -0.8647 0.0017 -vn -0.3255 0.9250 -0.1963 -vn -0.3495 0.9369 -0.0009 -vn -0.2639 0.8905 -0.3705 -vn -0.1883 0.8384 -0.5115 -vn -0.1056 0.7651 -0.6352 -vn -0.0051 0.6512 -0.7589 -vn 0.1361 0.4536 -0.8808 -vn 0.3342 0.1215 -0.9346 -vn 0.5381 -0.2994 -0.7879 -vn 0.6487 -0.6365 -0.4172 -vn 0.6642 -0.7475 0.0026 -vn -0.2056 0.9710 -0.1220 -vn -0.2211 0.9753 -0.0003 -vn -0.1673 0.9594 -0.2270 -vn -0.1234 0.9420 -0.3121 -vn -0.0757 0.9174 -0.3907 -vn -0.0155 0.8793 -0.4761 -vn 0.0776 0.8117 -0.5789 -vn 0.2484 0.6739 -0.6958 -vn 0.5446 0.4169 -0.7277 -vn 0.8605 0.1751 -0.4784 -vn 0.9957 0.0929 0.0039 -vn -0.0038 0.9999 0.0119 -vn 0.0007 1.0000 0.0005 -vn -0.0145 0.9997 0.0177 -vn -0.0285 0.9996 0.0040 -vn -0.0358 0.9990 -0.0266 -vn -0.0328 0.9972 -0.0669 -vn -0.0165 0.9934 -0.1132 -vn 0.0218 0.9865 -0.1626 -vn 0.1031 0.9742 -0.2009 -vn 0.2459 0.9548 -0.1673 -vn 0.3363 0.9417 0.0025 -vn 0.3961 0.8692 0.2960 -vn 0.4923 0.8704 0.0011 -vn 0.2255 0.8729 0.4327 -vn 0.0957 0.8913 0.4432 -vn 0.0133 0.9133 0.4071 -vn -0.0422 0.9337 0.3556 -vn -0.0832 0.9519 0.2949 -vn -0.1147 0.9676 0.2249 -vn -0.1365 0.9798 0.1461 -vn -0.1460 0.9870 0.0676 -vn -0.1481 0.9890 0.0001 -vn 0.7991 0.2306 0.5552 -vn 0.9894 0.1451 -0.0005 -vn 0.4700 0.3843 0.7946 -vn 0.2326 0.5187 0.8227 -vn 0.0779 0.6278 0.7744 -vn -0.0311 0.7199 0.6934 -vn -0.1145 0.8004 0.5884 -vn -0.1805 0.8695 0.4597 -vn -0.2294 0.9227 0.3099 -vn -0.2582 0.9542 0.1510 -vn -0.2677 0.9635 -0.0001 -vn 0.7640 -0.4209 0.4890 -vn 0.8591 -0.5118 -0.0026 -vn 0.5348 -0.1961 0.8219 -vn 0.3053 0.0431 0.9513 -vn 0.1250 0.2559 0.9586 -vn -0.0163 0.4467 0.8945 -vn -0.1301 0.6183 0.7751 -vn -0.2197 0.7632 0.6076 -vn -0.2831 0.8686 0.4068 -vn -0.3186 0.9273 0.1966 -vn -0.3296 0.9441 0.0001 -vn 0.6368 -0.6641 0.3918 -vn 0.6939 -0.7200 -0.0033 -vn 0.4885 -0.5004 0.7148 -vn 0.3111 -0.2827 0.9073 -vn 0.1444 -0.0463 0.9884 -vn -0.0062 0.2016 0.9794 -vn -0.1396 0.4487 0.8827 -vn -0.2481 0.6655 0.7039 -vn -0.3229 0.8210 0.4708 -vn -0.3621 0.9044 0.2256 -vn -0.3731 0.9278 0.0004 -vn 0.6057 -0.7346 0.3057 -vn 0.6394 -0.7689 -0.0026 -vn 0.4987 -0.6265 0.5990 -vn 0.3509 -0.4564 0.8177 -vn 0.1982 -0.2470 0.9485 -vn 0.0491 -0.0041 0.9988 -vn -0.0965 0.2671 0.9588 -vn -0.2299 0.5392 0.8102 -vn -0.3301 0.7589 0.5613 -vn -0.3826 0.8836 0.2700 -vn -0.3950 0.9187 0.0008 -vn 0.4272 -0.8684 0.2518 -vn 0.3935 -0.7414 0.5436 -vn 0.3056 -0.4856 0.8190 -vn 0.1638 -0.1433 0.9760 -vn 0.0037 0.1832 0.9831 -vn -0.1451 0.4348 0.8887 -vn -0.2791 0.6158 0.7368 -vn -0.4001 0.7450 0.5338 -vn -0.4922 0.8250 0.2778 -vn 0.4969 -0.8114 0.3077 -vn 0.4500 -0.6142 0.6483 -vn 0.3284 -0.2666 0.9061 -vn 0.1605 0.1107 0.9808 -vn 0.0024 0.4036 0.9149 -vn -0.1268 0.5978 0.7916 -vn -0.2372 0.7276 0.6437 -vn -0.3368 0.8192 0.4642 -vn -0.4147 0.8771 0.2424 -vn 0.6484 -0.6323 0.4241 -vn 0.5367 -0.2965 0.7900 -vn 0.3368 0.1158 0.9344 -vn 0.1398 0.4476 0.8832 -vn -0.0031 0.6486 0.7611 -vn -0.1047 0.7646 0.6359 -vn -0.1880 0.8389 0.5108 -vn -0.2636 0.8914 0.3686 -vn -0.3250 0.9256 0.1940 -vn 0.8597 0.1747 0.4799 -vn 0.5520 0.4093 0.7265 -vn 0.2570 0.6663 0.7000 -vn 0.0822 0.8082 0.5831 -vn -0.0134 0.8779 0.4786 -vn -0.0748 0.9170 0.3917 -vn -0.1229 0.9422 0.3118 -vn -0.1668 0.9598 0.2259 -vn -0.2050 0.9713 0.1210 -vn 0.2471 0.9540 0.1699 -vn 0.1056 0.9735 0.2026 -vn 0.0239 0.9862 0.1640 -vn -0.0148 0.9933 0.1145 -vn -0.0315 0.9972 0.0680 -vn -0.0349 0.9990 0.0275 -vn -0.0280 0.9996 -0.0035 -vn -0.0139 0.9997 -0.0178 -vn -0.0030 0.9999 -0.0115 -vn -0.1457 0.9871 -0.0669 -vn -0.1363 0.9800 -0.1446 -vn -0.1150 0.9680 -0.2231 -vn -0.0838 0.9523 -0.2933 -vn -0.0428 0.9341 -0.3543 -vn 0.0129 0.9137 -0.4063 -vn 0.0956 0.8917 -0.4424 -vn 0.2256 0.8733 -0.4317 -vn 0.3964 0.8696 -0.2944 -vn -0.2584 0.9541 -0.1515 -vn -0.2294 0.9225 -0.3105 -vn -0.1804 0.8694 -0.4599 -vn -0.1146 0.8008 -0.5878 -vn -0.0318 0.7212 -0.6920 -vn 0.0764 0.6301 -0.7727 -vn 0.2304 0.5220 -0.8213 -vn 0.4674 0.3883 -0.7942 -vn 0.7975 0.2341 -0.5560 -vn -0.3190 0.9271 -0.1968 -vn -0.2835 0.8677 -0.4083 -vn -0.2195 0.7613 -0.6102 -vn -0.1292 0.6161 -0.7770 -vn -0.0154 0.4456 -0.8951 -vn 0.1252 0.2569 -0.9583 -vn 0.3047 0.0464 -0.9513 -vn 0.5337 -0.1912 -0.8238 -vn 0.7634 -0.4165 -0.4936 -vn -0.3624 0.9045 -0.2249 -vn -0.3239 0.8203 -0.4714 -vn -0.2486 0.6619 -0.7071 -vn -0.1382 0.4422 -0.8862 -vn -0.0036 0.1953 -0.9807 -vn 0.1466 -0.0497 -0.9880 -vn 0.3120 -0.2825 -0.9071 -vn 0.4882 -0.4969 -0.7175 -vn 0.6369 -0.6602 -0.3981 -vn -0.3801 0.8859 -0.2660 -vn -0.3280 0.7656 -0.5535 -vn -0.2301 0.5484 -0.8039 -vn -0.0966 0.2699 -0.9580 -vn 0.0523 -0.0139 -0.9985 -vn 0.2037 -0.2671 -0.9419 -vn 0.3541 -0.4789 -0.8033 -vn 0.4952 -0.6424 -0.5849 -vn 0.5995 -0.7406 -0.3035 -vn -0.0000 1.0000 -0.0000 -vn 0.1285 0.9917 -0.0000 -vn 0.1269 0.9917 -0.0200 -vn 0.1220 0.9917 -0.0396 -vn 0.1141 0.9918 -0.0581 -vn 0.1035 0.9918 -0.0752 -vn 0.0904 0.9918 -0.0904 -vn 0.0752 0.9918 -0.1035 -vn 0.0581 0.9918 -0.1141 -vn 0.0396 0.9917 -0.1220 -vn 0.0200 0.9917 -0.1269 -vn -0.0000 0.9917 -0.1285 -vn 0.4120 0.9089 -0.0649 -vn 0.4172 0.9088 -0.0000 -vn 0.3963 0.9091 -0.1285 -vn 0.3707 0.9093 -0.1889 -vn 0.3363 0.9095 -0.2444 -vn 0.2939 0.9095 -0.2939 -vn 0.2444 0.9095 -0.3363 -vn 0.1889 0.9093 -0.3707 -vn 0.1285 0.9091 -0.3963 -vn 0.0649 0.9089 -0.4120 -vn -0.0000 0.9088 -0.4172 -vn 0.8851 0.4440 -0.1396 -vn 0.8961 0.4439 -0.0000 -vn 0.8521 0.4442 -0.2768 -vn 0.7980 0.4445 -0.4070 -vn 0.7244 0.4448 -0.5267 -vn 0.6333 0.4448 -0.6333 -vn 0.5267 0.4448 -0.7244 -vn 0.4070 0.4445 -0.7980 -vn 0.2768 0.4442 -0.8521 -vn 0.1396 0.4440 -0.8851 -vn -0.0000 0.4439 -0.8961 -vn 0.9512 -0.2698 -0.1501 -vn 0.9630 -0.2696 -0.0000 -vn 0.9157 -0.2702 -0.2975 -vn 0.8576 -0.2705 -0.4374 -vn 0.7786 -0.2708 -0.5661 -vn 0.6807 -0.2709 -0.6807 -vn 0.5661 -0.2708 -0.7786 -vn 0.4374 -0.2705 -0.8576 -vn 0.2975 -0.2702 -0.9157 -vn 0.1501 -0.2698 -0.9512 -vn -0.0000 -0.2696 -0.9630 -vn 0.7928 -0.5965 -0.1250 -vn 0.8028 -0.5962 -0.0000 -vn 0.7629 -0.5971 -0.2478 -vn 0.7142 -0.5978 -0.3642 -vn 0.6481 -0.5982 -0.4713 -vn 0.5665 -0.5984 -0.5665 -vn 0.4713 -0.5982 -0.6481 -vn 0.3642 -0.5978 -0.7142 -vn 0.2478 -0.5971 -0.7629 -vn 0.1250 -0.5965 -0.7928 -vn -0.0000 -0.5962 -0.8028 -vn 0.7369 -0.6660 -0.1163 -vn 0.7463 -0.6656 -0.0000 -vn 0.7089 -0.6666 -0.2303 -vn 0.6635 -0.6672 -0.3385 -vn 0.6021 -0.6677 -0.4378 -vn 0.5263 -0.6678 -0.5263 -vn 0.4378 -0.6677 -0.6021 -vn 0.3385 -0.6672 -0.6635 -vn 0.2303 -0.6666 -0.7089 -vn 0.1163 -0.6659 -0.7369 -vn -0.0000 -0.6656 -0.7463 -vn 0.7741 -0.6211 -0.1223 -vn 0.7840 -0.6207 -0.0000 -vn 0.7448 -0.6218 -0.2422 -vn 0.6971 -0.6225 -0.3558 -vn 0.6326 -0.6229 -0.4602 -vn 0.5531 -0.6231 -0.5531 -vn 0.4602 -0.6229 -0.6326 -vn 0.3558 -0.6224 -0.6971 -vn 0.2422 -0.6218 -0.7448 -vn 0.1223 -0.6211 -0.7742 -vn -0.0000 -0.6207 -0.7840 -vn 0.9055 -0.3994 -0.1433 -vn 0.9169 -0.3991 -0.0000 -vn 0.8714 -0.4001 -0.2837 -vn 0.8159 -0.4008 -0.4168 -vn 0.7406 -0.4012 -0.5390 -vn 0.6477 -0.4014 -0.6476 -vn 0.5390 -0.4012 -0.7406 -vn 0.4168 -0.4008 -0.8159 -vn 0.2837 -0.4001 -0.8714 -vn 0.1433 -0.3994 -0.9055 -vn -0.0000 -0.3991 -0.9169 -vn 0.9695 0.1908 -0.1536 -vn 0.9816 0.1908 -0.0000 -vn 0.9333 0.1908 -0.3042 -vn 0.8740 0.1907 -0.4469 -vn 0.7936 0.1906 -0.5778 -vn 0.6942 0.1905 -0.6941 -vn 0.5778 0.1905 -0.7936 -vn 0.4469 0.1906 -0.8740 -vn 0.3042 0.1907 -0.9333 -vn 0.1536 0.1908 -0.9695 -vn -0.0000 0.1908 -0.9816 -vn 0.6509 0.7521 -0.1033 -vn 0.6594 0.7518 -0.0000 -vn 0.6262 0.7524 -0.2044 -vn 0.5861 0.7526 -0.2999 -vn 0.5321 0.7528 -0.3875 -vn 0.4654 0.7528 -0.4654 -vn 0.3875 0.7528 -0.5321 -vn 0.2999 0.7526 -0.5862 -vn 0.2044 0.7524 -0.6262 -vn 0.1033 0.7521 -0.6509 -vn -0.0000 0.7518 -0.6594 -vn -0.0200 0.9917 -0.1269 -vn -0.0396 0.9917 -0.1220 -vn -0.0581 0.9918 -0.1141 -vn -0.0752 0.9918 -0.1035 -vn -0.0904 0.9918 -0.0904 -vn -0.1035 0.9918 -0.0752 -vn -0.1141 0.9918 -0.0581 -vn -0.1220 0.9917 -0.0396 -vn -0.1269 0.9917 -0.0200 -vn -0.1285 0.9917 -0.0000 -vn -0.0649 0.9089 -0.4120 -vn -0.1285 0.9091 -0.3963 -vn -0.1889 0.9093 -0.3707 -vn -0.2444 0.9095 -0.3363 -vn -0.2939 0.9096 -0.2939 -vn -0.3363 0.9095 -0.2444 -vn -0.3707 0.9093 -0.1889 -vn -0.3963 0.9091 -0.1285 -vn -0.4120 0.9089 -0.0649 -vn -0.4172 0.9088 -0.0000 -vn -0.1396 0.4439 -0.8851 -vn -0.2768 0.4442 -0.8521 -vn -0.4070 0.4445 -0.7980 -vn -0.5267 0.4448 -0.7244 -vn -0.6333 0.4448 -0.6333 -vn -0.7244 0.4448 -0.5267 -vn -0.7980 0.4445 -0.4070 -vn -0.8521 0.4442 -0.2768 -vn -0.8851 0.4440 -0.1396 -vn -0.8961 0.4439 -0.0000 -vn -0.1501 -0.2698 -0.9512 -vn -0.2975 -0.2702 -0.9157 -vn -0.4374 -0.2705 -0.8576 -vn -0.5661 -0.2708 -0.7786 -vn -0.6807 -0.2709 -0.6807 -vn -0.7786 -0.2708 -0.5661 -vn -0.8576 -0.2705 -0.4374 -vn -0.9157 -0.2702 -0.2975 -vn -0.9512 -0.2698 -0.1501 -vn -0.9630 -0.2696 -0.0000 -vn -0.1250 -0.5965 -0.7928 -vn -0.2478 -0.5971 -0.7629 -vn -0.3642 -0.5978 -0.7142 -vn -0.4713 -0.5982 -0.6481 -vn -0.5665 -0.5984 -0.5665 -vn -0.6481 -0.5982 -0.4713 -vn -0.7142 -0.5978 -0.3642 -vn -0.7629 -0.5971 -0.2478 -vn -0.7928 -0.5965 -0.1250 -vn -0.8028 -0.5962 -0.0000 -vn -0.1163 -0.6660 -0.7369 -vn -0.2303 -0.6666 -0.7089 -vn -0.3385 -0.6672 -0.6635 -vn -0.4378 -0.6677 -0.6021 -vn -0.5263 -0.6678 -0.5263 -vn -0.6021 -0.6677 -0.4379 -vn -0.6635 -0.6672 -0.3385 -vn -0.7089 -0.6666 -0.2303 -vn -0.7369 -0.6660 -0.1163 -vn -0.7463 -0.6656 -0.0000 -vn -0.1223 -0.6211 -0.7741 -vn -0.2422 -0.6218 -0.7448 -vn -0.3558 -0.6225 -0.6971 -vn -0.4602 -0.6229 -0.6326 -vn -0.5531 -0.6231 -0.5531 -vn -0.6326 -0.6229 -0.4602 -vn -0.6971 -0.6224 -0.3558 -vn -0.7448 -0.6218 -0.2422 -vn -0.7742 -0.6211 -0.1223 -vn -0.7840 -0.6207 -0.0000 -vn -0.1433 -0.3994 -0.9055 -vn -0.2837 -0.4001 -0.8714 -vn -0.4168 -0.4008 -0.8159 -vn -0.5390 -0.4012 -0.7406 -vn -0.6476 -0.4014 -0.6477 -vn -0.7406 -0.4012 -0.5390 -vn -0.8159 -0.4008 -0.4168 -vn -0.8714 -0.4001 -0.2837 -vn -0.9055 -0.3994 -0.1433 -vn -0.9169 -0.3991 -0.0000 -vn -0.1536 0.1908 -0.9695 -vn -0.3043 0.1908 -0.9333 -vn -0.4469 0.1907 -0.8740 -vn -0.5778 0.1906 -0.7936 -vn -0.6941 0.1905 -0.6942 -vn -0.7936 0.1905 -0.5778 -vn -0.8740 0.1906 -0.4469 -vn -0.9333 0.1907 -0.3042 -vn -0.9695 0.1908 -0.1536 -vn -0.9816 0.1908 -0.0000 -vn -0.1033 0.7521 -0.6509 -vn -0.2044 0.7524 -0.6262 -vn -0.2999 0.7526 -0.5861 -vn -0.3875 0.7528 -0.5321 -vn -0.4654 0.7528 -0.4654 -vn -0.5321 0.7528 -0.3875 -vn -0.5862 0.7526 -0.2999 -vn -0.6262 0.7524 -0.2044 -vn -0.6509 0.7521 -0.1033 -vn -0.6594 0.7518 -0.0000 -vn -0.1269 0.9917 0.0200 -vn -0.1220 0.9917 0.0396 -vn -0.1141 0.9918 0.0581 -vn -0.1035 0.9918 0.0752 -vn -0.0904 0.9918 0.0904 -vn -0.0752 0.9918 0.1035 -vn -0.0581 0.9918 0.1141 -vn -0.0396 0.9917 0.1220 -vn -0.0200 0.9917 0.1269 -vn -0.0000 0.9917 0.1285 -vn -0.4120 0.9089 0.0649 -vn -0.3963 0.9091 0.1285 -vn -0.3707 0.9093 0.1889 -vn -0.3363 0.9095 0.2444 -vn -0.2939 0.9095 0.2939 -vn -0.2444 0.9095 0.3363 -vn -0.1889 0.9093 0.3707 -vn -0.1285 0.9091 0.3963 -vn -0.0649 0.9089 0.4120 -vn -0.0000 0.9088 0.4172 -vn -0.8851 0.4440 0.1396 -vn -0.8521 0.4442 0.2768 -vn -0.7980 0.4445 0.4070 -vn -0.7244 0.4448 0.5267 -vn -0.6333 0.4448 0.6333 -vn -0.5267 0.4448 0.7244 -vn -0.4070 0.4445 0.7980 -vn -0.2768 0.4442 0.8521 -vn -0.1396 0.4440 0.8851 -vn -0.0000 0.4439 0.8961 -vn -0.9512 -0.2698 0.1501 -vn -0.9157 -0.2702 0.2975 -vn -0.8576 -0.2705 0.4374 -vn -0.7786 -0.2708 0.5661 -vn -0.6807 -0.2709 0.6807 -vn -0.5661 -0.2708 0.7786 -vn -0.4374 -0.2705 0.8576 -vn -0.2975 -0.2702 0.9157 -vn -0.1501 -0.2698 0.9512 -vn -0.0000 -0.2696 0.9630 -vn -0.7928 -0.5965 0.1250 -vn -0.7629 -0.5971 0.2478 -vn -0.7142 -0.5978 0.3642 -vn -0.6481 -0.5982 0.4713 -vn -0.5665 -0.5984 0.5665 -vn -0.4713 -0.5982 0.6481 -vn -0.3642 -0.5978 0.7142 -vn -0.2478 -0.5971 0.7629 -vn -0.1250 -0.5965 0.7928 -vn -0.0000 -0.5962 0.8028 -vn -0.7369 -0.6660 0.1163 -vn -0.7089 -0.6666 0.2303 -vn -0.6635 -0.6672 0.3385 -vn -0.6021 -0.6677 0.4378 -vn -0.5263 -0.6678 0.5263 -vn -0.4378 -0.6677 0.6021 -vn -0.3385 -0.6672 0.6635 -vn -0.2303 -0.6666 0.7089 -vn -0.1163 -0.6659 0.7369 -vn -0.0000 -0.6656 0.7463 -vn -0.7741 -0.6211 0.1223 -vn -0.7448 -0.6218 0.2422 -vn -0.6971 -0.6225 0.3558 -vn -0.6326 -0.6229 0.4602 -vn -0.5531 -0.6231 0.5531 -vn -0.4602 -0.6229 0.6326 -vn -0.3558 -0.6224 0.6971 -vn -0.2422 -0.6218 0.7448 -vn -0.1223 -0.6211 0.7742 -vn -0.0000 -0.6207 0.7840 -vn -0.9055 -0.3994 0.1433 -vn -0.8714 -0.4001 0.2837 -vn -0.8159 -0.4008 0.4168 -vn -0.7406 -0.4012 0.5390 -vn -0.6477 -0.4014 0.6476 -vn -0.5390 -0.4012 0.7406 -vn -0.4168 -0.4008 0.8159 -vn -0.2837 -0.4001 0.8714 -vn -0.1433 -0.3994 0.9055 -vn -0.0000 -0.3991 0.9169 -vn -0.9695 0.1908 0.1536 -vn -0.9333 0.1908 0.3042 -vn -0.8740 0.1907 0.4469 -vn -0.7936 0.1906 0.5778 -vn -0.6942 0.1905 0.6941 -vn -0.5778 0.1905 0.7936 -vn -0.4469 0.1906 0.8740 -vn -0.3042 0.1907 0.9333 -vn -0.1536 0.1908 0.9695 -vn -0.0000 0.1908 0.9816 -vn -0.6509 0.7521 0.1033 -vn -0.6262 0.7524 0.2044 -vn -0.5861 0.7526 0.2999 -vn -0.5321 0.7528 0.3875 -vn -0.4654 0.7528 0.4654 -vn -0.3875 0.7528 0.5321 -vn -0.2999 0.7526 0.5862 -vn -0.2044 0.7524 0.6262 -vn -0.1033 0.7521 0.6509 -vn -0.0000 0.7518 0.6594 -vn 0.0200 0.9917 0.1269 -vn 0.0396 0.9917 0.1220 -vn 0.0581 0.9918 0.1141 -vn 0.0752 0.9918 0.1035 -vn 0.0904 0.9918 0.0904 -vn 0.1035 0.9918 0.0752 -vn 0.1141 0.9918 0.0581 -vn 0.1220 0.9917 0.0396 -vn 0.1269 0.9917 0.0200 -vn 0.0649 0.9089 0.4120 -vn 0.1285 0.9091 0.3963 -vn 0.1889 0.9093 0.3707 -vn 0.2444 0.9095 0.3363 -vn 0.2939 0.9096 0.2939 -vn 0.3363 0.9095 0.2444 -vn 0.3707 0.9093 0.1889 -vn 0.3963 0.9091 0.1285 -vn 0.4120 0.9089 0.0649 -vn 0.1396 0.4439 0.8851 -vn 0.2768 0.4442 0.8521 -vn 0.4070 0.4445 0.7980 -vn 0.5267 0.4448 0.7244 -vn 0.6333 0.4448 0.6333 -vn 0.7244 0.4448 0.5267 -vn 0.7980 0.4445 0.4070 -vn 0.8521 0.4442 0.2768 -vn 0.8851 0.4440 0.1396 -vn 0.1501 -0.2698 0.9512 -vn 0.2975 -0.2702 0.9157 -vn 0.4374 -0.2705 0.8576 -vn 0.5661 -0.2708 0.7786 -vn 0.6807 -0.2709 0.6807 -vn 0.7786 -0.2708 0.5661 -vn 0.8576 -0.2705 0.4374 -vn 0.9157 -0.2702 0.2975 -vn 0.9512 -0.2698 0.1501 -vn 0.1250 -0.5965 0.7928 -vn 0.2478 -0.5971 0.7629 -vn 0.3642 -0.5978 0.7142 -vn 0.4713 -0.5982 0.6481 -vn 0.5665 -0.5984 0.5665 -vn 0.6481 -0.5982 0.4713 -vn 0.7142 -0.5978 0.3642 -vn 0.7629 -0.5971 0.2478 -vn 0.7928 -0.5965 0.1250 -vn 0.1163 -0.6660 0.7369 -vn 0.2303 -0.6666 0.7089 -vn 0.3385 -0.6672 0.6635 -vn 0.4378 -0.6677 0.6021 -vn 0.5263 -0.6678 0.5263 -vn 0.6021 -0.6677 0.4378 -vn 0.6635 -0.6672 0.3385 -vn 0.7089 -0.6666 0.2303 -vn 0.7369 -0.6660 0.1163 -vn 0.1223 -0.6211 0.7741 -vn 0.2422 -0.6218 0.7448 -vn 0.3558 -0.6225 0.6971 -vn 0.4602 -0.6229 0.6326 -vn 0.5531 -0.6231 0.5531 -vn 0.6326 -0.6229 0.4602 -vn 0.6971 -0.6224 0.3558 -vn 0.7448 -0.6218 0.2422 -vn 0.7742 -0.6211 0.1223 -vn 0.1433 -0.3994 0.9055 -vn 0.2837 -0.4001 0.8714 -vn 0.4168 -0.4008 0.8159 -vn 0.5390 -0.4012 0.7406 -vn 0.6476 -0.4014 0.6477 -vn 0.7406 -0.4012 0.5390 -vn 0.8159 -0.4008 0.4168 -vn 0.8714 -0.4001 0.2837 -vn 0.9055 -0.3994 0.1433 -vn 0.1536 0.1908 0.9695 -vn 0.3043 0.1908 0.9333 -vn 0.4469 0.1907 0.8740 -vn 0.5778 0.1906 0.7936 -vn 0.6941 0.1905 0.6942 -vn 0.7936 0.1905 0.5778 -vn 0.8740 0.1906 0.4469 -vn 0.9333 0.1907 0.3042 -vn 0.9695 0.1908 0.1536 -vn 0.1033 0.7521 0.6509 -vn 0.2044 0.7524 0.6262 -vn 0.2999 0.7526 0.5861 -vn 0.3875 0.7528 0.5321 -vn 0.4654 0.7528 0.4654 -vn 0.5321 0.7528 0.3875 -vn 0.5862 0.7526 0.2999 -vn 0.6262 0.7524 0.2044 -vn 0.6509 0.7521 0.1033 -vn 0.3652 0.9291 -0.0580 -vn 0.3701 0.9290 -0.0000 -vn 0.3512 0.9293 -0.1147 -vn 0.3286 0.9294 -0.1682 -vn 0.2982 0.9295 -0.2172 -vn 0.2608 0.9295 -0.2608 -vn 0.2172 0.9295 -0.2982 -vn 0.1682 0.9294 -0.3286 -vn 0.1147 0.9293 -0.3512 -vn 0.0580 0.9291 -0.3652 -vn -0.0000 0.9290 -0.3701 -vn 0.2415 0.9697 -0.0383 -vn 0.2447 0.9696 -0.0000 -vn 0.2322 0.9697 -0.0758 -vn 0.2172 0.9698 -0.1111 -vn 0.1971 0.9698 -0.1435 -vn 0.1723 0.9698 -0.1723 -vn 0.1435 0.9698 -0.1971 -vn 0.1111 0.9698 -0.2172 -vn 0.0758 0.9697 -0.2322 -vn 0.0383 0.9697 -0.2415 -vn -0.0000 0.9696 -0.2447 -vn 0.1813 0.9830 -0.0288 -vn 0.1837 0.9830 -0.0000 -vn 0.1743 0.9831 -0.0569 -vn 0.1630 0.9831 -0.0834 -vn 0.1479 0.9831 -0.1077 -vn 0.1294 0.9831 -0.1294 -vn 0.1077 0.9831 -0.1479 -vn 0.0834 0.9831 -0.1630 -vn 0.0569 0.9831 -0.1743 -vn 0.0288 0.9830 -0.1813 -vn -0.0000 0.9830 -0.1837 -vn 0.1541 0.9877 -0.0245 -vn 0.1562 0.9877 -0.0000 -vn 0.1482 0.9878 -0.0484 -vn 0.1386 0.9878 -0.0709 -vn 0.1258 0.9878 -0.0916 -vn 0.1100 0.9878 -0.1100 -vn 0.0916 0.9878 -0.1258 -vn 0.0709 0.9878 -0.1386 -vn 0.0484 0.9878 -0.1482 -vn 0.0245 0.9877 -0.1541 -vn -0.0000 0.9877 -0.1562 -vn 0.1501 0.9884 -0.0238 -vn 0.1521 0.9884 -0.0000 -vn 0.1443 0.9884 -0.0471 -vn 0.1350 0.9884 -0.0691 -vn 0.1225 0.9885 -0.0892 -vn 0.1071 0.9885 -0.1071 -vn 0.0892 0.9885 -0.1225 -vn 0.0691 0.9884 -0.1350 -vn 0.0471 0.9884 -0.1443 -vn 0.0238 0.9884 -0.1501 -vn -0.0000 0.9884 -0.1521 -vn 0.1691 0.9852 -0.0268 -vn 0.1714 0.9852 -0.0000 -vn 0.1626 0.9853 -0.0531 -vn 0.1521 0.9853 -0.0778 -vn 0.1380 0.9853 -0.1005 -vn 0.1207 0.9853 -0.1207 -vn 0.1005 0.9853 -0.1380 -vn 0.0778 0.9853 -0.1521 -vn 0.0531 0.9853 -0.1626 -vn 0.0268 0.9852 -0.1691 -vn -0.0000 0.9852 -0.1714 -vn 0.2223 0.9743 -0.0353 -vn 0.2252 0.9743 -0.0000 -vn 0.2137 0.9744 -0.0697 -vn 0.1999 0.9745 -0.1023 -vn 0.1814 0.9745 -0.1321 -vn 0.1586 0.9745 -0.1586 -vn 0.1321 0.9745 -0.1814 -vn 0.1023 0.9745 -0.1999 -vn 0.0697 0.9744 -0.2137 -vn 0.0353 0.9743 -0.2223 -vn -0.0000 0.9743 -0.2252 -vn 0.3481 0.9358 -0.0552 -vn 0.3526 0.9358 -0.0000 -vn 0.3347 0.9360 -0.1092 -vn 0.3131 0.9361 -0.1602 -vn 0.2840 0.9362 -0.2069 -vn 0.2484 0.9363 -0.2484 -vn 0.2069 0.9362 -0.2840 -vn 0.1602 0.9361 -0.3131 -vn 0.1092 0.9360 -0.3347 -vn 0.0552 0.9358 -0.3481 -vn -0.0000 0.9358 -0.3526 -vn 0.6669 0.7376 -0.1059 -vn 0.6754 0.7375 -0.0000 -vn 0.6416 0.7379 -0.2094 -vn 0.6004 0.7383 -0.3072 -vn 0.5450 0.7386 -0.3969 -vn 0.4767 0.7386 -0.4767 -vn 0.3969 0.7386 -0.5450 -vn 0.3072 0.7383 -0.6004 -vn 0.2094 0.7379 -0.6416 -vn 0.1059 0.7376 -0.6669 -vn -0.0000 0.7375 -0.6754 -vn 0.8383 0.5287 -0.1333 -vn 0.8489 0.5285 -0.0000 -vn 0.8065 0.5292 -0.2635 -vn 0.7550 0.5297 -0.3865 -vn 0.6854 0.5300 -0.4993 -vn 0.5996 0.5301 -0.5996 -vn 0.4993 0.5300 -0.6854 -vn 0.3865 0.5297 -0.7550 -vn 0.2635 0.5292 -0.8065 -vn 0.1333 0.5287 -0.8383 -vn -0.0000 0.5285 -0.8489 -vn -0.0580 0.9291 -0.3653 -vn -0.1147 0.9293 -0.3512 -vn -0.1682 0.9294 -0.3286 -vn -0.2172 0.9295 -0.2982 -vn -0.2608 0.9295 -0.2608 -vn -0.2982 0.9295 -0.2172 -vn -0.3286 0.9294 -0.1682 -vn -0.3512 0.9293 -0.1147 -vn -0.3652 0.9291 -0.0580 -vn -0.3701 0.9290 -0.0000 -vn -0.0383 0.9697 -0.2415 -vn -0.0758 0.9697 -0.2322 -vn -0.1111 0.9698 -0.2172 -vn -0.1435 0.9698 -0.1971 -vn -0.1723 0.9698 -0.1724 -vn -0.1971 0.9698 -0.1435 -vn -0.2172 0.9698 -0.1111 -vn -0.2322 0.9697 -0.0758 -vn -0.2415 0.9697 -0.0383 -vn -0.2447 0.9696 -0.0000 -vn -0.0288 0.9830 -0.1813 -vn -0.0569 0.9831 -0.1743 -vn -0.0834 0.9831 -0.1630 -vn -0.1077 0.9831 -0.1479 -vn -0.1294 0.9831 -0.1294 -vn -0.1479 0.9831 -0.1077 -vn -0.1630 0.9831 -0.0834 -vn -0.1743 0.9831 -0.0569 -vn -0.1813 0.9830 -0.0288 -vn -0.1837 0.9830 -0.0000 -vn -0.0245 0.9877 -0.1541 -vn -0.0484 0.9878 -0.1482 -vn -0.0709 0.9878 -0.1386 -vn -0.0916 0.9878 -0.1258 -vn -0.1100 0.9878 -0.1100 -vn -0.1258 0.9878 -0.0916 -vn -0.1386 0.9878 -0.0709 -vn -0.1482 0.9878 -0.0484 -vn -0.1541 0.9877 -0.0245 -vn -0.1562 0.9877 -0.0000 -vn -0.0238 0.9884 -0.1501 -vn -0.0471 0.9884 -0.1443 -vn -0.0691 0.9884 -0.1350 -vn -0.0892 0.9885 -0.1225 -vn -0.1071 0.9885 -0.1071 -vn -0.1225 0.9885 -0.0892 -vn -0.1350 0.9884 -0.0691 -vn -0.1443 0.9884 -0.0471 -vn -0.1501 0.9884 -0.0238 -vn -0.1521 0.9884 -0.0000 -vn -0.0268 0.9852 -0.1691 -vn -0.0531 0.9853 -0.1626 -vn -0.0778 0.9853 -0.1521 -vn -0.1005 0.9853 -0.1380 -vn -0.1207 0.9853 -0.1207 -vn -0.1380 0.9853 -0.1005 -vn -0.1521 0.9853 -0.0778 -vn -0.1626 0.9853 -0.0531 -vn -0.1691 0.9852 -0.0268 -vn -0.1714 0.9852 -0.0000 -vn -0.0353 0.9743 -0.2223 -vn -0.0697 0.9744 -0.2137 -vn -0.1023 0.9745 -0.1999 -vn -0.1321 0.9745 -0.1814 -vn -0.1586 0.9745 -0.1586 -vn -0.1814 0.9745 -0.1321 -vn -0.1999 0.9745 -0.1023 -vn -0.2137 0.9744 -0.0697 -vn -0.2223 0.9743 -0.0353 -vn -0.2252 0.9743 -0.0000 -vn -0.0552 0.9358 -0.3481 -vn -0.1092 0.9360 -0.3347 -vn -0.1602 0.9361 -0.3131 -vn -0.2069 0.9362 -0.2840 -vn -0.2484 0.9363 -0.2484 -vn -0.2840 0.9362 -0.2069 -vn -0.3131 0.9361 -0.1602 -vn -0.3347 0.9360 -0.1092 -vn -0.3481 0.9358 -0.0552 -vn -0.3526 0.9358 -0.0000 -vn -0.1059 0.7376 -0.6669 -vn -0.2094 0.7379 -0.6416 -vn -0.3072 0.7383 -0.6004 -vn -0.3969 0.7386 -0.5450 -vn -0.4767 0.7386 -0.4767 -vn -0.5450 0.7386 -0.3969 -vn -0.6004 0.7383 -0.3072 -vn -0.6416 0.7379 -0.2094 -vn -0.6669 0.7376 -0.1059 -vn -0.6754 0.7375 -0.0000 -vn -0.1333 0.5287 -0.8383 -vn -0.2635 0.5292 -0.8065 -vn -0.3865 0.5297 -0.7550 -vn -0.4993 0.5300 -0.6854 -vn -0.5996 0.5301 -0.5996 -vn -0.6854 0.5300 -0.4993 -vn -0.7550 0.5297 -0.3865 -vn -0.8065 0.5292 -0.2635 -vn -0.8383 0.5288 -0.1333 -vn -0.8489 0.5285 -0.0000 -vn -0.3652 0.9291 0.0580 -vn -0.3512 0.9293 0.1147 -vn -0.3286 0.9294 0.1682 -vn -0.2982 0.9295 0.2172 -vn -0.2608 0.9295 0.2608 -vn -0.2172 0.9295 0.2982 -vn -0.1682 0.9294 0.3286 -vn -0.1147 0.9293 0.3512 -vn -0.0580 0.9291 0.3652 -vn -0.0000 0.9290 0.3701 -vn -0.2415 0.9697 0.0383 -vn -0.2322 0.9697 0.0758 -vn -0.2172 0.9698 0.1111 -vn -0.1971 0.9698 0.1435 -vn -0.1723 0.9698 0.1723 -vn -0.1435 0.9698 0.1971 -vn -0.1111 0.9698 0.2172 -vn -0.0758 0.9697 0.2322 -vn -0.0383 0.9697 0.2415 -vn -0.0000 0.9696 0.2447 -vn -0.1813 0.9830 0.0288 -vn -0.1743 0.9831 0.0569 -vn -0.1630 0.9831 0.0834 -vn -0.1479 0.9831 0.1077 -vn -0.1294 0.9831 0.1294 -vn -0.1077 0.9831 0.1479 -vn -0.0834 0.9831 0.1630 -vn -0.0569 0.9831 0.1743 -vn -0.0288 0.9830 0.1813 -vn -0.0000 0.9830 0.1837 -vn -0.1541 0.9877 0.0245 -vn -0.1482 0.9878 0.0484 -vn -0.1386 0.9878 0.0709 -vn -0.1258 0.9878 0.0916 -vn -0.1100 0.9878 0.1100 -vn -0.0916 0.9878 0.1258 -vn -0.0709 0.9878 0.1386 -vn -0.0484 0.9878 0.1482 -vn -0.0245 0.9877 0.1541 -vn -0.0000 0.9877 0.1562 -vn -0.1501 0.9884 0.0238 -vn -0.1443 0.9884 0.0471 -vn -0.1350 0.9884 0.0691 -vn -0.1225 0.9885 0.0892 -vn -0.1071 0.9885 0.1071 -vn -0.0892 0.9885 0.1225 -vn -0.0691 0.9884 0.1350 -vn -0.0471 0.9884 0.1443 -vn -0.0238 0.9884 0.1501 -vn -0.0000 0.9884 0.1521 -vn -0.1691 0.9852 0.0268 -vn -0.1626 0.9853 0.0531 -vn -0.1521 0.9853 0.0778 -vn -0.1380 0.9853 0.1005 -vn -0.1207 0.9853 0.1207 -vn -0.1005 0.9853 0.1380 -vn -0.0778 0.9853 0.1521 -vn -0.0531 0.9853 0.1626 -vn -0.0268 0.9852 0.1691 -vn -0.0000 0.9852 0.1714 -vn -0.2223 0.9743 0.0353 -vn -0.2137 0.9744 0.0697 -vn -0.1999 0.9745 0.1023 -vn -0.1814 0.9745 0.1321 -vn -0.1586 0.9745 0.1586 -vn -0.1321 0.9745 0.1814 -vn -0.1023 0.9745 0.1999 -vn -0.0697 0.9744 0.2137 -vn -0.0353 0.9743 0.2223 -vn -0.0000 0.9743 0.2252 -vn -0.3481 0.9358 0.0552 -vn -0.3347 0.9360 0.1092 -vn -0.3131 0.9361 0.1602 -vn -0.2840 0.9362 0.2069 -vn -0.2484 0.9363 0.2484 -vn -0.2069 0.9362 0.2840 -vn -0.1602 0.9361 0.3131 -vn -0.1092 0.9360 0.3347 -vn -0.0552 0.9358 0.3481 -vn -0.0000 0.9358 0.3526 -vn -0.6669 0.7376 0.1059 -vn -0.6416 0.7379 0.2094 -vn -0.6004 0.7383 0.3072 -vn -0.5450 0.7386 0.3969 -vn -0.4767 0.7386 0.4767 -vn -0.3969 0.7386 0.5450 -vn -0.3072 0.7383 0.6004 -vn -0.2094 0.7379 0.6416 -vn -0.1059 0.7376 0.6669 -vn -0.0000 0.7375 0.6754 -vn -0.8383 0.5287 0.1333 -vn -0.8065 0.5292 0.2635 -vn -0.7550 0.5297 0.3865 -vn -0.6854 0.5300 0.4993 -vn -0.5996 0.5301 0.5996 -vn -0.4993 0.5300 0.6854 -vn -0.3865 0.5297 0.7550 -vn -0.2635 0.5292 0.8065 -vn -0.1333 0.5287 0.8383 -vn -0.0000 0.5285 0.8489 -vn 0.0580 0.9291 0.3653 -vn 0.1147 0.9293 0.3512 -vn 0.1682 0.9294 0.3286 -vn 0.2172 0.9295 0.2982 -vn 0.2608 0.9295 0.2608 -vn 0.2982 0.9295 0.2172 -vn 0.3286 0.9294 0.1682 -vn 0.3512 0.9293 0.1147 -vn 0.3652 0.9291 0.0580 -vn 0.0383 0.9697 0.2415 -vn 0.0758 0.9697 0.2322 -vn 0.1111 0.9698 0.2172 -vn 0.1435 0.9698 0.1971 -vn 0.1723 0.9698 0.1724 -vn 0.1971 0.9698 0.1435 -vn 0.2172 0.9698 0.1111 -vn 0.2322 0.9697 0.0758 -vn 0.2415 0.9697 0.0383 -vn 0.0288 0.9830 0.1813 -vn 0.0569 0.9831 0.1743 -vn 0.0834 0.9831 0.1630 -vn 0.1077 0.9831 0.1479 -vn 0.1294 0.9831 0.1294 -vn 0.1479 0.9831 0.1077 -vn 0.1630 0.9831 0.0834 -vn 0.1743 0.9831 0.0569 -vn 0.1813 0.9830 0.0288 -vn 0.0245 0.9877 0.1541 -vn 0.0484 0.9878 0.1482 -vn 0.0709 0.9878 0.1386 -vn 0.0916 0.9878 0.1258 -vn 0.1100 0.9878 0.1100 -vn 0.1258 0.9878 0.0916 -vn 0.1386 0.9878 0.0709 -vn 0.1482 0.9878 0.0484 -vn 0.1541 0.9877 0.0245 -vn 0.0238 0.9884 0.1501 -vn 0.0471 0.9884 0.1443 -vn 0.0691 0.9884 0.1350 -vn 0.0892 0.9885 0.1225 -vn 0.1071 0.9885 0.1071 -vn 0.1225 0.9885 0.0892 -vn 0.1350 0.9884 0.0691 -vn 0.1443 0.9884 0.0471 -vn 0.1501 0.9884 0.0238 -vn 0.0268 0.9852 0.1691 -vn 0.0531 0.9853 0.1626 -vn 0.0778 0.9853 0.1521 -vn 0.1005 0.9853 0.1380 -vn 0.1207 0.9853 0.1207 -vn 0.1380 0.9853 0.1005 -vn 0.1521 0.9853 0.0778 -vn 0.1626 0.9853 0.0531 -vn 0.1691 0.9852 0.0268 -vn 0.0353 0.9743 0.2223 -vn 0.0697 0.9744 0.2137 -vn 0.1023 0.9745 0.1999 -vn 0.1321 0.9745 0.1814 -vn 0.1586 0.9745 0.1586 -vn 0.1814 0.9745 0.1321 -vn 0.1999 0.9745 0.1023 -vn 0.2137 0.9744 0.0697 -vn 0.2223 0.9743 0.0353 -vn 0.0552 0.9358 0.3481 -vn 0.1092 0.9360 0.3347 -vn 0.1602 0.9361 0.3131 -vn 0.2069 0.9362 0.2840 -vn 0.2484 0.9363 0.2484 -vn 0.2840 0.9362 0.2069 -vn 0.3131 0.9361 0.1602 -vn 0.3347 0.9360 0.1092 -vn 0.3481 0.9358 0.0552 -vn 0.1059 0.7376 0.6669 -vn 0.2094 0.7379 0.6416 -vn 0.3072 0.7383 0.6004 -vn 0.3969 0.7386 0.5450 -vn 0.4767 0.7386 0.4767 -vn 0.5450 0.7386 0.3969 -vn 0.6004 0.7383 0.3072 -vn 0.6416 0.7379 0.2094 -vn 0.6669 0.7376 0.1059 -vn 0.1333 0.5287 0.8383 -vn 0.2635 0.5292 0.8065 -vn 0.3865 0.5297 0.7550 -vn 0.4993 0.5300 0.6854 -vn 0.5996 0.5301 0.5996 -vn 0.6854 0.5300 0.4993 -vn 0.7550 0.5297 0.3865 -vn 0.8065 0.5292 0.2635 -vn 0.8383 0.5288 0.1333 -vn -0.0000 -1.0000 -0.0000 -vn 0.0130 -0.9999 -0.0000 -vn 0.0129 -0.9999 0.0020 -vn 0.0124 -0.9999 0.0040 -vn 0.0116 -0.9999 0.0059 -vn 0.0105 -0.9999 0.0076 -vn 0.0092 -0.9999 0.0092 -vn 0.0076 -0.9999 0.0105 -vn 0.0059 -0.9999 0.0116 -vn 0.0040 -0.9999 0.0124 -vn 0.0020 -0.9999 0.0129 -vn -0.0000 -0.9999 0.0130 -vn 0.0297 -0.9995 0.0047 -vn 0.0301 -0.9995 -0.0000 -vn 0.0285 -0.9995 0.0093 -vn 0.0267 -0.9996 0.0137 -vn 0.0242 -0.9996 0.0176 -vn 0.0212 -0.9996 0.0212 -vn 0.0176 -0.9996 0.0242 -vn 0.0137 -0.9996 0.0267 -vn 0.0093 -0.9995 0.0285 -vn 0.0047 -0.9995 0.0297 -vn -0.0000 -0.9995 0.0301 -vn 0.0536 -0.9985 0.0085 -vn 0.0543 -0.9985 -0.0000 -vn 0.0515 -0.9985 0.0168 -vn 0.0482 -0.9985 0.0246 -vn 0.0437 -0.9985 0.0318 -vn 0.0382 -0.9985 0.0382 -vn 0.0318 -0.9985 0.0437 -vn 0.0246 -0.9985 0.0482 -vn 0.0168 -0.9985 0.0515 -vn 0.0085 -0.9985 0.0536 -vn -0.0000 -0.9985 0.0543 -vn 0.0891 -0.9959 0.0141 -vn 0.0903 -0.9959 -0.0000 -vn 0.0857 -0.9959 0.0280 -vn 0.0801 -0.9959 0.0410 -vn 0.0727 -0.9959 0.0529 -vn 0.0636 -0.9960 0.0636 -vn 0.0529 -0.9959 0.0727 -vn 0.0410 -0.9959 0.0801 -vn 0.0280 -0.9959 0.0857 -vn 0.0141 -0.9959 0.0891 -vn -0.0000 -0.9959 0.0903 -vn 0.1452 -0.9891 0.0230 -vn 0.1471 -0.9891 -0.0000 -vn 0.1396 -0.9892 0.0455 -vn 0.1305 -0.9892 0.0668 -vn 0.1184 -0.9892 0.0863 -vn 0.1036 -0.9892 0.1036 -vn 0.0863 -0.9892 0.1184 -vn 0.0668 -0.9892 0.1305 -vn 0.0455 -0.9892 0.1396 -vn 0.0230 -0.9891 0.1452 -vn -0.0000 -0.9891 0.1471 -vn 0.2397 -0.9701 0.0380 -vn 0.2428 -0.9701 -0.0000 -vn 0.2305 -0.9702 0.0752 -vn 0.2156 -0.9702 0.1103 -vn 0.1956 -0.9703 0.1424 -vn 0.1710 -0.9703 0.1710 -vn 0.1424 -0.9703 0.1956 -vn 0.1103 -0.9702 0.2156 -vn 0.0752 -0.9702 0.2305 -vn 0.0380 -0.9701 0.2397 -vn -0.0000 -0.9701 0.2428 -vn 0.4072 -0.9110 0.0646 -vn 0.4125 -0.9110 -0.0000 -vn 0.3916 -0.9112 0.1278 -vn 0.3663 -0.9114 0.1874 -vn 0.3324 -0.9116 0.2421 -vn 0.2907 -0.9116 0.2907 -vn 0.2421 -0.9116 0.3324 -vn 0.1874 -0.9114 0.3663 -vn 0.1278 -0.9112 0.3916 -vn 0.0646 -0.9110 0.4072 -vn -0.0000 -0.9110 0.4125 -vn 0.6739 -0.7311 0.1070 -vn 0.6824 -0.7309 -0.0000 -vn 0.6482 -0.7315 0.2116 -vn 0.6066 -0.7319 0.3104 -vn 0.5506 -0.7321 0.4010 -vn 0.4816 -0.7322 0.4816 -vn 0.4010 -0.7321 0.5506 -vn 0.3104 -0.7319 0.6066 -vn 0.2116 -0.7315 0.6482 -vn 0.1070 -0.7311 0.6739 -vn -0.0000 -0.7309 0.6824 -vn 0.9158 -0.3743 0.1455 -vn 0.9274 -0.3742 -0.0000 -vn 0.8814 -0.3747 0.2878 -vn 0.8252 -0.3750 0.4224 -vn 0.7492 -0.3753 0.5457 -vn 0.6554 -0.3754 0.6554 -vn 0.5457 -0.3753 0.7492 -vn 0.4224 -0.3750 0.8252 -vn 0.2878 -0.3747 0.8814 -vn 0.1455 -0.3743 0.9158 -vn -0.0000 -0.3742 0.9274 -vn -0.0020 -0.9999 0.0129 -vn -0.0040 -0.9999 0.0124 -vn -0.0059 -0.9999 0.0116 -vn -0.0076 -0.9999 0.0105 -vn -0.0092 -0.9999 0.0092 -vn -0.0105 -0.9999 0.0076 -vn -0.0116 -0.9999 0.0059 -vn -0.0124 -0.9999 0.0040 -vn -0.0129 -0.9999 0.0020 -vn -0.0130 -0.9999 -0.0000 -vn -0.0047 -0.9995 0.0297 -vn -0.0093 -0.9995 0.0285 -vn -0.0137 -0.9996 0.0267 -vn -0.0176 -0.9996 0.0242 -vn -0.0212 -0.9996 0.0212 -vn -0.0242 -0.9996 0.0176 -vn -0.0267 -0.9996 0.0137 -vn -0.0285 -0.9995 0.0093 -vn -0.0297 -0.9995 0.0047 -vn -0.0301 -0.9995 -0.0000 -vn -0.0085 -0.9985 0.0536 -vn -0.0168 -0.9985 0.0515 -vn -0.0246 -0.9985 0.0482 -vn -0.0318 -0.9985 0.0437 -vn -0.0382 -0.9985 0.0382 -vn -0.0437 -0.9985 0.0318 -vn -0.0482 -0.9985 0.0246 -vn -0.0515 -0.9985 0.0168 -vn -0.0536 -0.9985 0.0085 -vn -0.0543 -0.9985 -0.0000 -vn -0.0141 -0.9959 0.0891 -vn -0.0280 -0.9959 0.0857 -vn -0.0410 -0.9959 0.0801 -vn -0.0529 -0.9959 0.0727 -vn -0.0636 -0.9960 0.0636 -vn -0.0727 -0.9959 0.0529 -vn -0.0801 -0.9959 0.0410 -vn -0.0857 -0.9959 0.0280 -vn -0.0891 -0.9959 0.0141 -vn -0.0903 -0.9959 -0.0000 -vn -0.0230 -0.9891 0.1452 -vn -0.0455 -0.9892 0.1396 -vn -0.0668 -0.9892 0.1305 -vn -0.0863 -0.9892 0.1184 -vn -0.1036 -0.9892 0.1036 -vn -0.1184 -0.9892 0.0863 -vn -0.1305 -0.9892 0.0668 -vn -0.1396 -0.9892 0.0455 -vn -0.1452 -0.9891 0.0230 -vn -0.1471 -0.9891 -0.0000 -vn -0.0380 -0.9701 0.2397 -vn -0.0752 -0.9702 0.2305 -vn -0.1103 -0.9702 0.2156 -vn -0.1424 -0.9703 0.1956 -vn -0.1710 -0.9703 0.1710 -vn -0.1956 -0.9703 0.1424 -vn -0.2156 -0.9702 0.1103 -vn -0.2305 -0.9702 0.0752 -vn -0.2397 -0.9701 0.0380 -vn -0.2428 -0.9701 -0.0000 -vn -0.0646 -0.9110 0.4072 -vn -0.1278 -0.9112 0.3916 -vn -0.1874 -0.9114 0.3663 -vn -0.2421 -0.9116 0.3324 -vn -0.2907 -0.9116 0.2907 -vn -0.3324 -0.9116 0.2421 -vn -0.3663 -0.9114 0.1874 -vn -0.3916 -0.9112 0.1278 -vn -0.4072 -0.9110 0.0646 -vn -0.4125 -0.9110 -0.0000 -vn -0.1070 -0.7311 0.6739 -vn -0.2116 -0.7315 0.6482 -vn -0.3104 -0.7319 0.6066 -vn -0.4010 -0.7321 0.5506 -vn -0.4816 -0.7322 0.4816 -vn -0.5506 -0.7321 0.4010 -vn -0.6066 -0.7319 0.3104 -vn -0.6482 -0.7315 0.2116 -vn -0.6739 -0.7311 0.1070 -vn -0.6824 -0.7309 -0.0000 -vn -0.1455 -0.3743 0.9158 -vn -0.2878 -0.3747 0.8814 -vn -0.4224 -0.3750 0.8252 -vn -0.5457 -0.3753 0.7492 -vn -0.6554 -0.3754 0.6554 -vn -0.7492 -0.3753 0.5457 -vn -0.8252 -0.3750 0.4224 -vn -0.8814 -0.3747 0.2878 -vn -0.9158 -0.3743 0.1455 -vn -0.9274 -0.3742 -0.0000 -vn -0.0129 -0.9999 -0.0020 -vn -0.0124 -0.9999 -0.0040 -vn -0.0116 -0.9999 -0.0059 -vn -0.0105 -0.9999 -0.0076 -vn -0.0092 -0.9999 -0.0092 -vn -0.0076 -0.9999 -0.0105 -vn -0.0059 -0.9999 -0.0116 -vn -0.0040 -0.9999 -0.0124 -vn -0.0020 -0.9999 -0.0129 -vn -0.0000 -0.9999 -0.0130 -vn -0.0297 -0.9995 -0.0047 -vn -0.0285 -0.9995 -0.0093 -vn -0.0267 -0.9996 -0.0137 -vn -0.0242 -0.9996 -0.0176 -vn -0.0212 -0.9996 -0.0212 -vn -0.0176 -0.9996 -0.0242 -vn -0.0137 -0.9996 -0.0267 -vn -0.0093 -0.9995 -0.0285 -vn -0.0047 -0.9995 -0.0297 -vn -0.0000 -0.9995 -0.0301 -vn -0.0536 -0.9985 -0.0085 -vn -0.0515 -0.9985 -0.0168 -vn -0.0482 -0.9985 -0.0246 -vn -0.0437 -0.9985 -0.0318 -vn -0.0382 -0.9985 -0.0382 -vn -0.0318 -0.9985 -0.0437 -vn -0.0246 -0.9985 -0.0482 -vn -0.0168 -0.9985 -0.0515 -vn -0.0085 -0.9985 -0.0536 -vn -0.0000 -0.9985 -0.0543 -vn -0.0891 -0.9959 -0.0141 -vn -0.0857 -0.9959 -0.0280 -vn -0.0801 -0.9959 -0.0410 -vn -0.0727 -0.9959 -0.0529 -vn -0.0636 -0.9960 -0.0636 -vn -0.0529 -0.9959 -0.0727 -vn -0.0410 -0.9959 -0.0801 -vn -0.0280 -0.9959 -0.0857 -vn -0.0141 -0.9959 -0.0891 -vn -0.0000 -0.9959 -0.0903 -vn -0.1452 -0.9891 -0.0230 -vn -0.1396 -0.9892 -0.0455 -vn -0.1305 -0.9892 -0.0668 -vn -0.1184 -0.9892 -0.0863 -vn -0.1036 -0.9892 -0.1036 -vn -0.0863 -0.9892 -0.1184 -vn -0.0668 -0.9892 -0.1305 -vn -0.0455 -0.9892 -0.1396 -vn -0.0230 -0.9891 -0.1452 -vn -0.0000 -0.9891 -0.1471 -vn -0.2397 -0.9701 -0.0380 -vn -0.2305 -0.9702 -0.0752 -vn -0.2156 -0.9702 -0.1103 -vn -0.1956 -0.9703 -0.1424 -vn -0.1710 -0.9703 -0.1710 -vn -0.1424 -0.9703 -0.1956 -vn -0.1103 -0.9702 -0.2156 -vn -0.0752 -0.9702 -0.2305 -vn -0.0380 -0.9701 -0.2397 -vn -0.0000 -0.9701 -0.2428 -vn -0.4072 -0.9110 -0.0646 -vn -0.3916 -0.9112 -0.1278 -vn -0.3663 -0.9114 -0.1874 -vn -0.3324 -0.9116 -0.2421 -vn -0.2907 -0.9116 -0.2907 -vn -0.2421 -0.9116 -0.3324 -vn -0.1874 -0.9114 -0.3663 -vn -0.1278 -0.9112 -0.3916 -vn -0.0646 -0.9110 -0.4072 -vn -0.0000 -0.9110 -0.4125 -vn -0.6739 -0.7311 -0.1070 -vn -0.6482 -0.7315 -0.2116 -vn -0.6066 -0.7319 -0.3104 -vn -0.5506 -0.7321 -0.4010 -vn -0.4816 -0.7322 -0.4816 -vn -0.4010 -0.7321 -0.5506 -vn -0.3104 -0.7319 -0.6066 -vn -0.2116 -0.7315 -0.6482 -vn -0.1070 -0.7311 -0.6739 -vn -0.0000 -0.7309 -0.6825 -vn -0.9158 -0.3743 -0.1455 -vn -0.8814 -0.3747 -0.2878 -vn -0.8252 -0.3750 -0.4224 -vn -0.7492 -0.3753 -0.5457 -vn -0.6554 -0.3754 -0.6554 -vn -0.5457 -0.3753 -0.7492 -vn -0.4224 -0.3750 -0.8252 -vn -0.2878 -0.3747 -0.8814 -vn -0.1455 -0.3743 -0.9158 -vn -0.0000 -0.3742 -0.9274 -vn 0.0020 -0.9999 -0.0129 -vn 0.0040 -0.9999 -0.0124 -vn 0.0059 -0.9999 -0.0116 -vn 0.0076 -0.9999 -0.0105 -vn 0.0092 -0.9999 -0.0092 -vn 0.0105 -0.9999 -0.0076 -vn 0.0116 -0.9999 -0.0059 -vn 0.0124 -0.9999 -0.0040 -vn 0.0129 -0.9999 -0.0020 -vn 0.0047 -0.9995 -0.0297 -vn 0.0093 -0.9995 -0.0285 -vn 0.0137 -0.9996 -0.0267 -vn 0.0176 -0.9996 -0.0242 -vn 0.0212 -0.9996 -0.0212 -vn 0.0242 -0.9996 -0.0176 -vn 0.0267 -0.9996 -0.0137 -vn 0.0285 -0.9995 -0.0093 -vn 0.0297 -0.9995 -0.0047 -vn 0.0085 -0.9985 -0.0536 -vn 0.0168 -0.9985 -0.0515 -vn 0.0246 -0.9985 -0.0482 -vn 0.0318 -0.9985 -0.0437 -vn 0.0382 -0.9985 -0.0382 -vn 0.0437 -0.9985 -0.0318 -vn 0.0482 -0.9985 -0.0246 -vn 0.0515 -0.9985 -0.0168 -vn 0.0536 -0.9985 -0.0085 -vn 0.0141 -0.9959 -0.0891 -vn 0.0280 -0.9959 -0.0857 -vn 0.0410 -0.9959 -0.0801 -vn 0.0529 -0.9959 -0.0727 -vn 0.0636 -0.9960 -0.0636 -vn 0.0727 -0.9959 -0.0529 -vn 0.0801 -0.9959 -0.0410 -vn 0.0857 -0.9959 -0.0280 -vn 0.0891 -0.9959 -0.0141 -vn 0.0230 -0.9891 -0.1452 -vn 0.0455 -0.9892 -0.1396 -vn 0.0668 -0.9892 -0.1305 -vn 0.0863 -0.9892 -0.1184 -vn 0.1036 -0.9892 -0.1036 -vn 0.1184 -0.9892 -0.0863 -vn 0.1305 -0.9892 -0.0668 -vn 0.1396 -0.9892 -0.0455 -vn 0.1452 -0.9891 -0.0230 -vn 0.0380 -0.9701 -0.2397 -vn 0.0752 -0.9702 -0.2305 -vn 0.1103 -0.9702 -0.2156 -vn 0.1424 -0.9703 -0.1956 -vn 0.1710 -0.9703 -0.1710 -vn 0.1956 -0.9703 -0.1424 -vn 0.2156 -0.9702 -0.1103 -vn 0.2305 -0.9702 -0.0752 -vn 0.2397 -0.9701 -0.0380 -vn 0.0646 -0.9110 -0.4072 -vn 0.1278 -0.9112 -0.3916 -vn 0.1874 -0.9114 -0.3663 -vn 0.2421 -0.9116 -0.3324 -vn 0.2907 -0.9116 -0.2907 -vn 0.3324 -0.9116 -0.2421 -vn 0.3663 -0.9114 -0.1874 -vn 0.3916 -0.9112 -0.1278 -vn 0.4072 -0.9110 -0.0646 -vn 0.1070 -0.7311 -0.6739 -vn 0.2116 -0.7315 -0.6482 -vn 0.3104 -0.7319 -0.6066 -vn 0.4010 -0.7321 -0.5506 -vn 0.4816 -0.7322 -0.4816 -vn 0.5506 -0.7321 -0.4010 -vn 0.6066 -0.7319 -0.3104 -vn 0.6482 -0.7315 -0.2116 -vn 0.6739 -0.7311 -0.1070 -vn 0.1455 -0.3743 -0.9158 -vn 0.2878 -0.3747 -0.8814 -vn 0.4224 -0.3750 -0.8252 -vn 0.5457 -0.3753 -0.7492 -vn 0.6554 -0.3754 -0.6554 -vn 0.7492 -0.3753 -0.5457 -vn 0.8252 -0.3750 -0.4224 -vn 0.8814 -0.3747 -0.2878 -vn 0.9158 -0.3743 -0.1455 -vt 0.298804 0.491375 -vt 0.298972 0.487366 -vt 0.323069 0.487658 -vt 0.433122 0.000360 -vt 0.322913 0.491632 -vt 0.431018 0.003748 -vt 0.275395 0.489629 -vt 0.275813 0.485651 -vt 0.252713 0.486441 -vt 0.253388 0.482508 -vt 0.230630 0.481891 -vt 0.231554 0.478015 -vt 0.209043 0.476015 -vt 0.210212 0.472209 -vt 0.187872 0.468808 -vt 0.189282 0.465084 -vt 0.167052 0.460219 -vt 0.168703 0.456593 -vt 0.146524 0.450153 -vt 0.148416 0.446637 -vt 0.126224 0.438467 -vt 0.128347 0.435060 -vt 0.108251 0.421760 -vt 0.323069 0.066259 -vt 0.298676 0.494371 -vt 0.322847 0.494565 -vt 0.429431 0.006219 -vt 0.275077 0.492601 -vt 0.252206 0.489379 -vt 0.229938 0.484787 -vt 0.208170 0.478859 -vt 0.186820 0.471591 -vt 0.165822 0.462932 -vt 0.145115 0.452782 -vt 0.124640 0.441011 -vt 0.104560 0.427619 -vt 0.322847 0.073166 -vt 0.298583 0.496487 -vt 0.274848 0.494700 -vt 0.251842 0.491455 -vt 0.229446 0.486832 -vt 0.207553 0.480869 -vt 0.186080 0.473559 -vt 0.164957 0.464850 -vt 0.144124 0.454643 -vt 0.123524 0.442812 -vt 0.298512 0.498013 -vt 0.274676 0.496212 -vt 0.251576 0.492950 -vt 0.229089 0.488306 -vt 0.207108 0.482318 -vt 0.185548 0.474979 -vt 0.164338 0.466235 -vt 0.143416 0.455987 -vt 0.122725 0.444108 -vt 0.102517 0.430530 -vt 0.322912 0.076724 -vt 0.298441 0.499459 -vt 0.274507 0.497646 -vt 0.251318 0.494370 -vt 0.228747 0.489707 -vt 0.206685 0.483695 -vt 0.185044 0.476328 -vt 0.163753 0.467552 -vt 0.142748 0.457266 -vt 0.121973 0.445335 -vt 0.101642 0.431641 -vt 0.323016 0.078151 -vt 0.298353 0.501287 -vt 0.274294 0.499466 -vt 0.250992 0.496170 -vt 0.228315 0.491482 -vt 0.206150 0.485441 -vt 0.184407 0.478040 -vt 0.163013 0.469223 -vt 0.141902 0.458887 -vt 0.121020 0.446890 -vt 0.298242 0.503684 -vt 0.323307 0.503751 -vt 0.423926 0.013542 -vt 0.274018 0.501850 -vt 0.250568 0.498528 -vt 0.227750 0.493807 -vt 0.205449 0.487728 -vt 0.183572 0.480281 -vt 0.162043 0.471412 -vt 0.140792 0.461012 -vt 0.119766 0.448929 -vt 0.099056 0.434941 -vt 0.323307 0.082352 -vt 0.298106 0.506694 -vt 0.273675 0.504841 -vt 0.250038 0.501484 -vt 0.227045 0.496721 -vt 0.204573 0.490593 -vt 0.182526 0.483089 -vt 0.160825 0.474154 -vt 0.139398 0.463676 -vt 0.118189 0.451490 -vt 0.097232 0.437334 -vt 0.323474 0.085365 -vt 0.297944 0.510326 -vt 0.323640 0.510415 -vt 0.419922 0.018851 -vt 0.273265 0.508444 -vt 0.249403 0.505043 -vt 0.226197 0.500229 -vt 0.203518 0.494041 -vt 0.181266 0.486470 -vt 0.159357 0.477456 -vt 0.137717 0.466885 -vt 0.116285 0.454579 -vt 0.095051 0.440251 -vt 0.323640 0.089016 -vt 0.297756 0.514584 -vt 0.272786 0.512663 -vt 0.248661 0.509209 -vt 0.225205 0.504332 -vt 0.202284 0.498075 -vt 0.179791 0.490424 -vt 0.157639 0.481320 -vt 0.135747 0.470642 -vt 0.114051 0.458203 -vt 0.298804 0.069975 -vt 0.298972 0.065966 -vt 0.106147 0.425147 -vt 0.322913 0.070232 -vt 0.275395 0.068230 -vt 0.275813 0.064252 -vt 0.252714 0.065042 -vt 0.253388 0.061108 -vt 0.230630 0.060491 -vt 0.231554 0.056615 -vt 0.209043 0.054616 -vt 0.210212 0.050809 -vt 0.187872 0.047409 -vt 0.189282 0.043685 -vt 0.167052 0.038820 -vt 0.168703 0.035193 -vt 0.146524 0.028753 -vt 0.148416 0.025238 -vt 0.126224 0.017067 -vt 0.128347 0.013661 -vt 0.106147 0.003748 -vt 0.972654 0.070232 -vt 0.298676 0.072972 -vt 0.275078 0.071201 -vt 0.252206 0.067980 -vt 0.229938 0.063387 -vt 0.208170 0.057460 -vt 0.186820 0.050192 -vt 0.165822 0.041532 -vt 0.145115 0.031383 -vt 0.124641 0.019612 -vt 0.298583 0.075088 -vt 0.103399 0.429331 -vt 0.322852 0.075230 -vt 0.274848 0.073300 -vt 0.251842 0.070056 -vt 0.229446 0.065433 -vt 0.207553 0.059470 -vt 0.186080 0.052159 -vt 0.164957 0.043451 -vt 0.144124 0.033244 -vt 0.123524 0.021412 -vt 0.103399 0.007931 -vt 0.972593 0.075230 -vt 0.298512 0.076613 -vt 0.274676 0.074812 -vt 0.251576 0.071551 -vt 0.229089 0.066907 -vt 0.207108 0.060919 -vt 0.185548 0.053579 -vt 0.164338 0.044836 -vt 0.143416 0.034588 -vt 0.122725 0.022709 -vt 0.298441 0.078059 -vt 0.274507 0.076247 -vt 0.251318 0.072970 -vt 0.228747 0.068307 -vt 0.206685 0.062296 -vt 0.185044 0.054929 -vt 0.163753 0.046153 -vt 0.142748 0.035866 -vt 0.121973 0.023935 -vt 0.101642 0.010242 -vt 0.972757 0.078151 -vt 0.298353 0.079888 -vt 0.100520 0.433058 -vt 0.323151 0.079964 -vt 0.274294 0.078066 -vt 0.250992 0.074771 -vt 0.228315 0.070083 -vt 0.206150 0.064042 -vt 0.184407 0.056641 -vt 0.163013 0.047824 -vt 0.141902 0.037488 -vt 0.121020 0.025490 -vt 0.298242 0.082284 -vt 0.274018 0.080451 -vt 0.250568 0.077129 -vt 0.227750 0.072408 -vt 0.205449 0.066328 -vt 0.183572 0.058882 -vt 0.162043 0.050012 -vt 0.140792 0.039613 -vt 0.119766 0.027530 -vt 0.099056 0.013542 -vt 0.973048 0.082352 -vt 0.298106 0.085294 -vt 0.273675 0.083442 -vt 0.250038 0.080085 -vt 0.227045 0.075322 -vt 0.204573 0.069193 -vt 0.182526 0.061690 -vt 0.160825 0.052755 -vt 0.139398 0.042277 -vt 0.118189 0.030091 -vt 0.297945 0.088926 -vt 0.273265 0.087045 -vt 0.249403 0.083644 -vt 0.226197 0.078829 -vt 0.203518 0.072642 -vt 0.181266 0.065070 -vt 0.159357 0.056056 -vt 0.137717 0.045485 -vt 0.116285 0.033180 -vt 0.297756 0.093184 -vt 0.092520 0.443705 -vt 0.323791 0.093306 -vt 0.272786 0.091264 -vt 0.248661 0.087809 -vt 0.225205 0.082933 -vt 0.202284 0.076675 -vt 0.179791 0.069025 -vt 0.157639 0.059920 -vt 0.135747 0.049243 -vt 0.114051 0.036804 -vt 0.948545 0.069975 -vt 0.948713 0.065966 -vt 0.108251 0.000360 -vt 0.972810 0.066258 -vt 0.925136 0.068229 -vt 0.925554 0.064252 -vt 0.902454 0.065042 -vt 0.903129 0.061108 -vt 0.880371 0.060491 -vt 0.881295 0.056615 -vt 0.858784 0.054616 -vt 0.859953 0.050809 -vt 0.837613 0.047408 -vt 0.839023 0.043685 -vt 0.816793 0.038820 -vt 0.818444 0.035193 -vt 0.796265 0.028753 -vt 0.798157 0.025238 -vt 0.775965 0.017067 -vt 0.778088 0.013661 -vt 0.755888 0.003748 -vt 0.647784 0.070232 -vt 0.948417 0.072971 -vt 0.104560 0.006219 -vt 0.972588 0.073166 -vt 0.924818 0.071201 -vt 0.901947 0.067980 -vt 0.879679 0.063387 -vt 0.857911 0.057460 -vt 0.836561 0.050192 -vt 0.815563 0.041532 -vt 0.794856 0.031383 -vt 0.774381 0.019612 -vt 0.754301 0.006219 -vt 0.647717 0.073165 -vt 0.948324 0.075088 -vt 0.924589 0.073300 -vt 0.901583 0.070055 -vt 0.879187 0.065433 -vt 0.857294 0.059469 -vt 0.835820 0.052159 -vt 0.814698 0.043451 -vt 0.793865 0.033244 -vt 0.773265 0.021412 -vt 0.948253 0.076613 -vt 0.102517 0.009131 -vt 0.972653 0.076724 -vt 0.924417 0.074812 -vt 0.901317 0.071551 -vt 0.878830 0.066907 -vt 0.856849 0.060919 -vt 0.835289 0.053579 -vt 0.814079 0.044836 -vt 0.793157 0.034588 -vt 0.772466 0.022709 -vt 0.948182 0.078059 -vt 0.924248 0.076247 -vt 0.901059 0.072970 -vt 0.878488 0.068307 -vt 0.856426 0.062296 -vt 0.834785 0.054929 -vt 0.813494 0.046153 -vt 0.792489 0.035866 -vt 0.771714 0.023935 -vt 0.751383 0.010242 -vt 0.647886 0.078151 -vt 0.948094 0.079888 -vt 0.100520 0.011659 -vt 0.972892 0.079964 -vt 0.924035 0.078066 -vt 0.900733 0.074770 -vt 0.878056 0.070083 -vt 0.855891 0.064042 -vt 0.834148 0.056640 -vt 0.812754 0.047824 -vt 0.791643 0.037488 -vt 0.770761 0.025490 -vt 0.750261 0.011659 -vt 0.648021 0.079964 -vt 0.947983 0.082284 -vt 0.923759 0.080451 -vt 0.900309 0.077129 -vt 0.877491 0.072408 -vt 0.855190 0.066328 -vt 0.833313 0.058882 -vt 0.811784 0.050012 -vt 0.790533 0.039613 -vt 0.769507 0.027530 -vt 0.748797 0.013542 -vt 0.648178 0.082352 -vt 0.947847 0.085294 -vt 0.097232 0.015934 -vt 0.973215 0.085365 -vt 0.923416 0.083442 -vt 0.899779 0.080084 -vt 0.876785 0.075322 -vt 0.854314 0.069193 -vt 0.832267 0.061690 -vt 0.810566 0.052754 -vt 0.789139 0.042277 -vt 0.767930 0.030091 -vt 0.746973 0.015934 -vt 0.648345 0.085365 -vt 0.947685 0.088926 -vt 0.095051 0.018852 -vt 0.973381 0.089016 -vt 0.923006 0.087045 -vt 0.899144 0.083644 -vt 0.875937 0.078829 -vt 0.853259 0.072641 -vt 0.831007 0.065070 -vt 0.809098 0.056056 -vt 0.787458 0.045485 -vt 0.766026 0.033180 -vt 0.744792 0.018851 -vt 0.648510 0.089016 -vt 0.947497 0.093184 -vt 0.092520 0.022306 -vt 0.973532 0.093305 -vt 0.922527 0.091264 -vt 0.898402 0.087809 -vt 0.874946 0.082933 -vt 0.852025 0.076675 -vt 0.829532 0.069025 -vt 0.807380 0.059920 -vt 0.785488 0.049243 -vt 0.763791 0.036804 -vt 0.742261 0.022306 -vt 0.648662 0.093305 -vt 0.623674 0.069975 -vt 0.623842 0.065966 -vt 0.757992 0.000360 -vt 0.647940 0.066258 -vt 0.600265 0.068229 -vt 0.600684 0.064252 -vt 0.577584 0.065042 -vt 0.578258 0.061108 -vt 0.555501 0.060491 -vt 0.556425 0.056615 -vt 0.533914 0.054616 -vt 0.535082 0.050809 -vt 0.512743 0.047408 -vt 0.514153 0.043685 -vt 0.491922 0.038820 -vt 0.493573 0.035193 -vt 0.471395 0.028753 -vt 0.473286 0.025238 -vt 0.451094 0.017067 -vt 0.453217 0.013661 -vt 0.623546 0.072971 -vt 0.599948 0.071201 -vt 0.577076 0.067980 -vt 0.554808 0.063387 -vt 0.533040 0.057460 -vt 0.511691 0.050192 -vt 0.490692 0.041532 -vt 0.469986 0.031383 -vt 0.449511 0.019612 -vt 0.623453 0.075088 -vt 0.753140 0.007931 -vt 0.647722 0.075230 -vt 0.599718 0.073300 -vt 0.576713 0.070055 -vt 0.554317 0.065433 -vt 0.532423 0.059469 -vt 0.510950 0.052159 -vt 0.489827 0.043451 -vt 0.468995 0.033244 -vt 0.448394 0.021412 -vt 0.322852 0.496629 -vt 0.428270 0.007931 -vt 0.623382 0.076613 -vt 0.752258 0.009131 -vt 0.647783 0.076723 -vt 0.599546 0.074812 -vt 0.576446 0.071551 -vt 0.553959 0.066907 -vt 0.531979 0.060919 -vt 0.510418 0.053579 -vt 0.489208 0.044836 -vt 0.468287 0.034588 -vt 0.447595 0.022709 -vt 0.322912 0.498123 -vt 0.427387 0.009131 -vt 0.623312 0.078059 -vt 0.599378 0.076247 -vt 0.576189 0.072970 -vt 0.553618 0.068307 -vt 0.531556 0.062296 -vt 0.509915 0.054929 -vt 0.488624 0.046153 -vt 0.467619 0.035866 -vt 0.446843 0.023935 -vt 0.323016 0.499551 -vt 0.426512 0.010242 -vt 0.623224 0.079887 -vt 0.599165 0.078066 -vt 0.575863 0.074770 -vt 0.553185 0.070083 -vt 0.531020 0.064042 -vt 0.509277 0.056640 -vt 0.487884 0.047824 -vt 0.466773 0.037488 -vt 0.445890 0.025490 -vt 0.323151 0.501364 -vt 0.425390 0.011659 -vt 0.623112 0.082284 -vt 0.598889 0.080451 -vt 0.575438 0.077128 -vt 0.552621 0.072408 -vt 0.530320 0.066328 -vt 0.508443 0.058882 -vt 0.486913 0.050012 -vt 0.465663 0.039613 -vt 0.444637 0.027530 -vt 0.622976 0.085294 -vt 0.598546 0.083442 -vt 0.574909 0.080084 -vt 0.551915 0.075322 -vt 0.529443 0.069193 -vt 0.507396 0.061690 -vt 0.485695 0.052754 -vt 0.464269 0.042277 -vt 0.443060 0.030091 -vt 0.323474 0.506764 -vt 0.422102 0.015934 -vt 0.622815 0.088926 -vt 0.598135 0.087045 -vt 0.574273 0.083644 -vt 0.551067 0.078829 -vt 0.528388 0.072641 -vt 0.506136 0.065070 -vt 0.484228 0.056056 -vt 0.462588 0.045485 -vt 0.441156 0.033180 -vt 0.622627 0.093184 -vt 0.597656 0.091264 -vt 0.573531 0.087809 -vt 0.550076 0.082933 -vt 0.527154 0.076675 -vt 0.504662 0.069025 -vt 0.482509 0.059920 -vt 0.460618 0.049242 -vt 0.438921 0.036804 -vt 0.323791 0.514705 -vt 0.417391 0.022306 -vt 0.296861 0.532871 -vt 0.324267 0.533282 -vt 0.406551 0.037332 -vt 0.270694 0.530714 -vt 0.245466 0.527014 -vt 0.220960 0.521870 -vt 0.197012 0.515316 -vt 0.173498 0.507335 -vt 0.150312 0.497857 -vt 0.127351 0.486752 -vt 0.104512 0.473815 -vt 0.081680 0.458732 -vt 0.324267 0.111883 -vt 0.295828 0.551037 -vt 0.324510 0.551782 -vt 0.395910 0.052416 -vt 0.268528 0.548596 -vt 0.242248 0.544627 -vt 0.216733 0.539213 -vt 0.191795 0.532373 -vt 0.167291 0.524080 -vt 0.143097 0.514259 -vt 0.119089 0.502773 -vt 0.095125 0.489402 -vt 0.071039 0.473815 -vt 0.324510 0.130383 -vt 0.294654 0.568942 -vt 0.324510 0.570062 -vt 0.385543 0.067467 -vt 0.266299 0.566192 -vt 0.239029 0.561949 -vt 0.212553 0.556267 -vt 0.186667 0.549154 -vt 0.161213 0.540572 -vt 0.136049 0.530439 -vt 0.111029 0.518614 -vt 0.085976 0.504874 -vt 0.060672 0.488867 -vt 0.324510 0.148662 -vt 0.293342 0.586470 -vt 0.324262 0.587995 -vt 0.375525 0.082403 -vt 0.264020 0.583403 -vt 0.235826 0.578888 -vt 0.208445 0.572952 -vt 0.181658 0.565581 -vt 0.155295 0.556730 -vt 0.129206 0.546314 -vt 0.103221 0.534193 -vt 0.077130 0.520142 -vt 0.291909 0.603528 -vt 0.323770 0.605474 -vt 0.365922 0.097145 -vt 0.261711 0.600149 -vt 0.232661 0.595378 -vt 0.204428 0.589204 -vt 0.176786 0.581594 -vt 0.149561 0.572495 -vt 0.122594 0.561821 -vt 0.095700 0.549437 -vt 0.068635 0.535129 -vt 0.290376 0.620045 -vt 0.323045 0.622407 -vt 0.356786 0.111624 -vt 0.259395 0.616377 -vt 0.229555 0.611373 -vt 0.200520 0.604982 -vt 0.172066 0.597152 -vt 0.144022 0.587823 -vt 0.116225 0.576910 -vt 0.088482 0.564290 -vt 0.060520 0.549770 -vt 0.288773 0.635980 -vt 0.322110 0.638729 -vt 0.348150 0.125780 -vt 0.257096 0.632060 -vt 0.226523 0.626854 -vt 0.196731 0.620271 -vt 0.167505 0.612241 -vt 0.138681 0.602697 -vt 0.110100 0.591560 -vt 0.081567 0.578720 -vt 0.052791 0.564016 -vt 0.023279 0.547179 -vt 0.322111 0.217329 -vt 0.287131 0.651325 -vt 0.254834 0.647206 -vt 0.223577 0.641836 -vt 0.193064 0.635086 -vt 0.163098 0.626873 -vt 0.133528 0.617127 -vt 0.104203 0.605774 -vt 0.074935 0.592722 -vt 0.045433 0.577846 -vt 0.015156 0.560965 -vt 0.320995 0.233004 -vt 0.285484 0.666120 -vt 0.252628 0.661867 -vt 0.220722 0.656373 -vt 0.189513 0.649482 -vt 0.158831 0.641101 -vt 0.128540 0.631161 -vt 0.098503 0.619594 -vt 0.068545 0.606325 -vt 0.038399 0.591269 -vt 0.283857 0.680446 -vt 0.325231 0.165969 -vt 0.318376 0.683843 -vt 0.335053 0.346322 -vt 0.186054 0.663548 -vt 0.185899 0.685725 -vt 0.031601 0.604343 -vt 0.037861 0.747790 -vt 0.000360 0.587369 -vt 0.010182 0.767721 -vt 0.318377 0.262444 -vt 0.296861 0.111472 -vt 0.270694 0.109315 -vt 0.245466 0.105614 -vt 0.220960 0.100470 -vt 0.197013 0.093917 -vt 0.173499 0.085936 -vt 0.150312 0.076458 -vt 0.127351 0.065353 -vt 0.104512 0.052416 -vt 0.295828 0.129637 -vt 0.268528 0.127196 -vt 0.242248 0.123228 -vt 0.216733 0.117813 -vt 0.191795 0.110973 -vt 0.167291 0.102681 -vt 0.143097 0.092860 -vt 0.119089 0.081373 -vt 0.095125 0.068003 -vt 0.071039 0.052416 -vt 0.974251 0.130383 -vt 0.294654 0.147542 -vt 0.266299 0.144793 -vt 0.239029 0.140549 -vt 0.212554 0.134868 -vt 0.186668 0.127754 -vt 0.161213 0.119172 -vt 0.136049 0.109039 -vt 0.111029 0.097215 -vt 0.085976 0.083474 -vt 0.060672 0.067467 -vt 0.974251 0.148662 -vt 0.293342 0.165071 -vt 0.050654 0.503802 -vt 0.324262 0.166596 -vt 0.264020 0.162004 -vt 0.235826 0.157489 -vt 0.208445 0.151553 -vt 0.181658 0.144182 -vt 0.155295 0.135331 -vt 0.129206 0.124915 -vt 0.103222 0.112794 -vt 0.077130 0.098742 -vt 0.291909 0.182129 -vt 0.041051 0.518545 -vt 0.323770 0.184075 -vt 0.261711 0.178750 -vt 0.232661 0.173979 -vt 0.204428 0.167804 -vt 0.176786 0.160195 -vt 0.149561 0.151096 -vt 0.122594 0.140421 -vt 0.095700 0.128037 -vt 0.068635 0.113729 -vt 0.290376 0.198646 -vt 0.031915 0.533024 -vt 0.323045 0.201008 -vt 0.259395 0.194978 -vt 0.229555 0.189973 -vt 0.200520 0.183582 -vt 0.172066 0.175753 -vt 0.144022 0.166424 -vt 0.116225 0.155511 -vt 0.088482 0.142891 -vt 0.060520 0.128370 -vt 0.031915 0.111624 -vt 0.972786 0.201008 -vt 0.288773 0.214580 -vt 0.257096 0.210660 -vt 0.226523 0.205455 -vt 0.196731 0.198871 -vt 0.167505 0.190842 -vt 0.138681 0.181298 -vt 0.110100 0.170161 -vt 0.081567 0.157321 -vt 0.052791 0.142617 -vt 0.287131 0.229926 -vt 0.254834 0.225807 -vt 0.223577 0.220437 -vt 0.193064 0.213687 -vt 0.163098 0.205474 -vt 0.133528 0.195728 -vt 0.104203 0.184374 -vt 0.074935 0.171322 -vt 0.045433 0.156447 -vt 0.015156 0.139566 -vt 0.970736 0.233003 -vt 0.285484 0.244721 -vt 0.007532 0.574359 -vt 0.319735 0.248033 -vt 0.252628 0.240468 -vt 0.220722 0.234973 -vt 0.189513 0.228082 -vt 0.158831 0.219702 -vt 0.128540 0.209762 -vt 0.098503 0.198195 -vt 0.068545 0.184926 -vt 0.038399 0.169870 -vt 0.283857 0.259047 -vt 0.217944 0.249158 -vt 0.217788 0.259862 -vt 0.092944 0.211698 -vt 0.094714 0.293693 -vt 0.062331 0.198201 -vt 0.065908 0.308758 -vt 0.946602 0.111472 -vt 0.081680 0.037332 -vt 0.974008 0.111883 -vt 0.920435 0.109315 -vt 0.895207 0.105614 -vt 0.870701 0.100470 -vt 0.846753 0.093917 -vt 0.823240 0.085936 -vt 0.800053 0.076458 -vt 0.777092 0.065353 -vt 0.754253 0.052416 -vt 0.945569 0.129637 -vt 0.918269 0.127196 -vt 0.891989 0.123228 -vt 0.866474 0.117813 -vt 0.841536 0.110973 -vt 0.817032 0.102681 -vt 0.792838 0.092860 -vt 0.768830 0.081373 -vt 0.744866 0.068003 -vt 0.944395 0.147542 -vt 0.916040 0.144792 -vt 0.888770 0.140549 -vt 0.862294 0.134868 -vt 0.836409 0.127754 -vt 0.810954 0.119172 -vt 0.785790 0.109039 -vt 0.760770 0.097215 -vt 0.735717 0.083474 -vt 0.710413 0.067467 -vt 0.649381 0.148662 -vt 0.943083 0.165071 -vt 0.050654 0.082403 -vt 0.974003 0.166596 -vt 0.913761 0.162003 -vt 0.885567 0.157489 -vt 0.858186 0.151552 -vt 0.831399 0.144182 -vt 0.805036 0.135331 -vt 0.778947 0.124915 -vt 0.752962 0.112794 -vt 0.726871 0.098742 -vt 0.941650 0.182128 -vt 0.041051 0.097145 -vt 0.973511 0.184074 -vt 0.911452 0.178750 -vt 0.882402 0.173979 -vt 0.854169 0.167804 -vt 0.826527 0.160194 -vt 0.799302 0.151095 -vt 0.772335 0.140421 -vt 0.745441 0.128037 -vt 0.718376 0.113729 -vt 0.940117 0.198645 -vt 0.909136 0.194977 -vt 0.879296 0.189973 -vt 0.850261 0.183582 -vt 0.821807 0.175753 -vt 0.793763 0.166424 -vt 0.765966 0.155511 -vt 0.738223 0.142891 -vt 0.710261 0.128370 -vt 0.938514 0.214580 -vt 0.023279 0.125780 -vt 0.971852 0.217329 -vt 0.906837 0.210660 -vt 0.876264 0.205454 -vt 0.846472 0.198871 -vt 0.817246 0.190841 -vt 0.788422 0.181298 -vt 0.759841 0.170161 -vt 0.731308 0.157321 -vt 0.702532 0.142617 -vt 0.936872 0.229926 -vt 0.904575 0.225807 -vt 0.873318 0.220436 -vt 0.842805 0.213687 -vt 0.812839 0.205474 -vt 0.783269 0.195728 -vt 0.753944 0.184374 -vt 0.724676 0.171322 -vt 0.695174 0.156447 -vt 0.664897 0.139566 -vt 0.645866 0.233003 -vt 0.935225 0.244721 -vt 0.007532 0.152959 -vt 0.969476 0.248033 -vt 0.902369 0.240468 -vt 0.870463 0.234973 -vt 0.839254 0.228082 -vt 0.808572 0.219702 -vt 0.778281 0.209762 -vt 0.748244 0.198194 -vt 0.718286 0.184926 -vt 0.688140 0.169870 -vt 0.657273 0.152959 -vt 0.644605 0.248033 -vt 0.933598 0.259046 -vt 0.000360 0.165969 -vt 0.010182 0.346322 -vt 0.968118 0.262444 -vt 0.851170 0.630048 -vt 0.848291 0.514149 -vt 0.835795 0.242148 -vt 0.835640 0.264326 -vt 0.804410 0.233613 -vt 0.804479 0.271466 -vt 0.742685 0.211698 -vt 0.744455 0.293693 -vt 0.712072 0.198201 -vt 0.715649 0.308757 -vt 0.650101 0.165969 -vt 0.659923 0.346322 -vt 0.643247 0.262444 -vt 0.621732 0.111471 -vt 0.731421 0.037332 -vt 0.649138 0.111883 -vt 0.595564 0.109315 -vt 0.570337 0.105614 -vt 0.545831 0.100470 -vt 0.521883 0.093917 -vt 0.498369 0.085936 -vt 0.475183 0.076458 -vt 0.452222 0.065353 -vt 0.429383 0.052415 -vt 0.620699 0.129637 -vt 0.720780 0.052416 -vt 0.649381 0.130382 -vt 0.593398 0.127196 -vt 0.567119 0.123228 -vt 0.541603 0.117813 -vt 0.516665 0.110973 -vt 0.492161 0.102681 -vt 0.467968 0.092860 -vt 0.443959 0.081373 -vt 0.419995 0.068003 -vt 0.619524 0.147542 -vt 0.591169 0.144792 -vt 0.563899 0.140549 -vt 0.537424 0.134868 -vt 0.511538 0.127754 -vt 0.486083 0.119172 -vt 0.460920 0.109039 -vt 0.435899 0.097215 -vt 0.410846 0.083474 -vt 0.618213 0.165070 -vt 0.700395 0.082403 -vt 0.649133 0.166596 -vt 0.588891 0.162003 -vt 0.560697 0.157489 -vt 0.533316 0.151552 -vt 0.506528 0.144182 -vt 0.480166 0.135331 -vt 0.454077 0.124915 -vt 0.428092 0.112794 -vt 0.402001 0.098742 -vt 0.616780 0.182128 -vt 0.690793 0.097145 -vt 0.648640 0.184074 -vt 0.586582 0.178750 -vt 0.557532 0.173978 -vt 0.529299 0.167804 -vt 0.501656 0.160194 -vt 0.474431 0.151095 -vt 0.447464 0.140421 -vt 0.420571 0.128037 -vt 0.393506 0.113729 -vt 0.615247 0.198645 -vt 0.681656 0.111624 -vt 0.647916 0.201007 -vt 0.584266 0.194977 -vt 0.554425 0.189973 -vt 0.525391 0.183582 -vt 0.496937 0.175753 -vt 0.468893 0.166424 -vt 0.441096 0.155511 -vt 0.413353 0.142891 -vt 0.385390 0.128370 -vt 0.613643 0.214580 -vt 0.673020 0.125780 -vt 0.646981 0.217329 -vt 0.581966 0.210660 -vt 0.551394 0.205454 -vt 0.521601 0.198871 -vt 0.492376 0.190841 -vt 0.463551 0.181298 -vt 0.434971 0.170160 -vt 0.406437 0.157321 -vt 0.377661 0.142617 -vt 0.612001 0.229926 -vt 0.579705 0.225807 -vt 0.548448 0.220436 -vt 0.517935 0.213687 -vt 0.487969 0.205474 -vt 0.458399 0.195728 -vt 0.429074 0.184374 -vt 0.399805 0.171322 -vt 0.370303 0.156447 -vt 0.320995 0.654403 -vt 0.340027 0.139566 -vt 0.610354 0.244721 -vt 0.577499 0.240468 -vt 0.545593 0.234973 -vt 0.514384 0.228082 -vt 0.483702 0.219702 -vt 0.453410 0.209762 -vt 0.423373 0.198194 -vt 0.393416 0.184926 -vt 0.363269 0.169870 -vt 0.319735 0.669432 -vt 0.332402 0.152959 -vt 0.608728 0.259046 -vt 0.479539 0.233613 -vt 0.479608 0.271466 -vt 0.417815 0.211698 -vt 0.419584 0.293693 -vt 0.387202 0.198201 -vt 0.390778 0.308758 -vt 0.356472 0.182944 -vt 0.362731 0.326391 -vt 0.283018 0.693884 -vt 0.250609 0.692895 -vt 0.250480 0.676135 -vt 0.250415 0.679506 -vt 0.219098 0.694561 -vt 0.217944 0.670557 -vt 0.217788 0.681262 -vt 0.188336 0.698850 -vt 0.158288 0.705724 -vt 0.154669 0.655012 -vt 0.154738 0.692866 -vt 0.128964 0.715156 -vt 0.123672 0.644882 -vt 0.124330 0.702659 -vt 0.100382 0.727139 -vt 0.092944 0.633097 -vt 0.094714 0.715093 -vt 0.072529 0.741683 -vt 0.062331 0.619600 -vt 0.065908 0.730157 -vt 0.045281 0.758794 -vt 0.282204 0.706306 -vt 0.250796 0.705246 -vt 0.220319 0.706811 -vt 0.190592 0.710933 -vt 0.161563 0.717560 -vt 0.133229 0.726665 -vt 0.105599 0.738247 -vt 0.078644 0.752336 -vt 0.052213 0.769008 -vt 0.281349 0.718182 -vt 0.313193 0.722182 -vt 0.358285 0.376653 -vt 0.250953 0.717032 -vt 0.221476 0.718494 -vt 0.192737 0.722454 -vt 0.164676 0.728849 -vt 0.137285 0.737647 -vt 0.110567 0.748854 -vt 0.084489 0.762519 -vt 0.058911 0.778777 -vt 0.280439 0.729658 -vt 0.311071 0.733828 -vt 0.365878 0.385716 -vt 0.251075 0.728426 -vt 0.222583 0.729787 -vt 0.194801 0.733592 -vt 0.167676 0.739764 -vt 0.141197 0.748269 -vt 0.115366 0.759117 -vt 0.090156 0.772373 -vt 0.065460 0.788210 -vt 0.041007 0.807115 -vt 0.311071 0.312428 -vt 0.279496 0.740641 -vt 0.308880 0.744885 -vt 0.373284 0.394254 -vt 0.251163 0.739350 -vt 0.223630 0.740621 -vt 0.196774 0.744280 -vt 0.170548 0.750240 -vt 0.144946 0.758466 -vt 0.119971 0.768969 -vt 0.095610 0.781827 -vt 0.071798 0.797226 -vt 0.048413 0.815653 -vt 0.308880 0.323486 -vt 0.278566 0.750870 -vt 0.306749 0.755090 -vt 0.380254 0.402082 -vt 0.251221 0.749552 -vt 0.224598 0.750748 -vt 0.198612 0.754275 -vt 0.173229 0.760039 -vt 0.148449 0.768003 -vt 0.124281 0.778184 -vt 0.100723 0.790658 -vt 0.077755 0.805613 -vt 0.055383 0.823481 -vt 0.306749 0.333691 -vt 0.277711 0.759967 -vt 0.304823 0.764086 -vt 0.386470 0.408948 -vt 0.251257 0.758652 -vt 0.225454 0.759790 -vt 0.200249 0.763203 -vt 0.175622 0.768794 -vt 0.151578 0.776526 -vt 0.128133 0.786414 -vt 0.105299 0.798537 -vt 0.083084 0.813066 -vt 0.061599 0.830347 -vt 0.304823 0.342687 -vt 0.276994 0.767480 -vt 0.303240 0.771462 -vt 0.391586 0.414566 -vt 0.251278 0.766185 -vt 0.226159 0.767284 -vt 0.201604 0.770605 -vt 0.177604 0.776053 -vt 0.154172 0.783592 -vt 0.131329 0.793236 -vt 0.109095 0.805061 -vt 0.087501 0.819223 -vt 0.066715 0.835965 -vt 0.303240 0.350062 -vt 0.276470 0.772936 -vt 0.302125 0.776790 -vt 0.395274 0.418645 -vt 0.251287 0.771664 -vt 0.226666 0.772738 -vt 0.202586 0.775994 -vt 0.179045 0.781339 -vt 0.156061 0.788737 -vt 0.133656 0.798202 -vt 0.111861 0.809806 -vt 0.090717 0.823702 -vt 0.070403 0.840044 -vt 0.302125 0.355390 -vt 0.301543 0.779904 -vt 0.397346 0.421039 -vt 0.332023 0.993359 -vt 0.808825 0.732426 -vt 0.251287 0.774836 -vt 0.759863 0.743906 -vt 0.179883 0.784400 -vt 0.700505 0.781982 -vt 0.157160 0.791715 -vt 0.685130 0.799600 -vt 0.072475 0.842438 -vt 0.301543 0.358505 -vt 0.490746 0.672376 -vt 0.650101 0.889417 -vt 0.283018 0.272485 -vt 0.018090 0.778509 -vt 0.316878 0.276005 -vt 0.250610 0.271496 -vt 0.250480 0.254736 -vt 0.250415 0.258106 -vt 0.219098 0.273162 -vt 0.188336 0.277451 -vt 0.186054 0.242148 -vt 0.185899 0.264326 -vt 0.158288 0.284324 -vt 0.154669 0.233613 -vt 0.154738 0.271467 -vt 0.128964 0.293756 -vt 0.123672 0.223482 -vt 0.124330 0.281260 -vt 0.100382 0.305740 -vt 0.072529 0.320284 -vt 0.045281 0.337395 -vt 0.031601 0.182944 -vt 0.037861 0.326391 -vt 0.282204 0.284907 -vt 0.025778 0.788542 -vt 0.315148 0.288670 -vt 0.250796 0.283846 -vt 0.220319 0.285411 -vt 0.190592 0.289533 -vt 0.161563 0.296161 -vt 0.133229 0.305266 -vt 0.105599 0.316847 -vt 0.078644 0.330936 -vt 0.052213 0.347609 -vt 0.281349 0.296783 -vt 0.033414 0.798052 -vt 0.313193 0.300782 -vt 0.250953 0.295633 -vt 0.221477 0.297094 -vt 0.192737 0.301055 -vt 0.164676 0.307449 -vt 0.137285 0.316248 -vt 0.110567 0.327454 -vt 0.084489 0.341120 -vt 0.058911 0.357378 -vt 0.280439 0.308259 -vt 0.251075 0.307027 -vt 0.222583 0.308388 -vt 0.194802 0.312193 -vt 0.167676 0.318364 -vt 0.141197 0.326869 -vt 0.115366 0.337717 -vt 0.090156 0.350974 -vt 0.065460 0.366811 -vt 0.279496 0.319242 -vt 0.251163 0.317951 -vt 0.223630 0.319222 -vt 0.196774 0.322881 -vt 0.170548 0.328841 -vt 0.144946 0.337066 -vt 0.119971 0.347570 -vt 0.095610 0.360427 -vt 0.071798 0.375827 -vt 0.048413 0.394254 -vt 0.958621 0.323486 -vt 0.278566 0.329471 -vt 0.251221 0.328153 -vt 0.224598 0.329349 -vt 0.198612 0.332875 -vt 0.173229 0.338639 -vt 0.148449 0.346604 -vt 0.124281 0.356784 -vt 0.100723 0.369259 -vt 0.077755 0.384213 -vt 0.055383 0.402081 -vt 0.956491 0.333691 -vt 0.277711 0.338568 -vt 0.251257 0.337253 -vt 0.225454 0.338391 -vt 0.200249 0.341803 -vt 0.175622 0.347394 -vt 0.151578 0.355126 -vt 0.128133 0.365015 -vt 0.105299 0.377138 -vt 0.083085 0.391667 -vt 0.276995 0.346081 -vt 0.251279 0.344786 -vt 0.226159 0.345885 -vt 0.201604 0.349206 -vt 0.177604 0.354654 -vt 0.154173 0.362193 -vt 0.131329 0.371837 -vt 0.109095 0.383662 -vt 0.087501 0.397824 -vt 0.276470 0.351536 -vt 0.251287 0.350264 -vt 0.226666 0.351338 -vt 0.202586 0.354594 -vt 0.179045 0.359940 -vt 0.156061 0.367338 -vt 0.133656 0.376803 -vt 0.111861 0.388407 -vt 0.090717 0.402303 -vt 0.070403 0.418645 -vt 0.951867 0.355390 -vt 0.251287 0.353437 -vt 0.441784 0.683857 -vt 0.226957 0.354497 -vt 0.419988 0.693879 -vt 0.203155 0.357715 -vt 0.400139 0.706637 -vt 0.179883 0.363000 -vt 0.382427 0.721933 -vt 0.157160 0.370316 -vt 0.367052 0.739551 -vt 0.135013 0.379675 -vt 0.354208 0.759274 -vt 0.113472 0.391149 -vt 0.344063 0.780901 -vt 0.092581 0.404883 -vt 0.336719 0.804260 -vt 0.932759 0.272485 -vt 0.018090 0.357110 -vt 0.966620 0.276005 -vt 0.900351 0.271496 -vt 0.900221 0.254736 -vt 0.900156 0.258106 -vt 0.868839 0.273162 -vt 0.867685 0.249158 -vt 0.867529 0.259862 -vt 0.838077 0.277451 -vt 0.808029 0.284324 -vt 0.778705 0.293756 -vt 0.773413 0.223482 -vt 0.774071 0.281260 -vt 0.750123 0.305740 -vt 0.722270 0.320284 -vt 0.695022 0.337395 -vt 0.681342 0.182944 -vt 0.687602 0.326391 -vt 0.931945 0.284907 -vt 0.025778 0.367143 -vt 0.964889 0.288669 -vt 0.900537 0.283846 -vt 0.870060 0.285411 -vt 0.840333 0.289533 -vt 0.811304 0.296160 -vt 0.782971 0.305266 -vt 0.755341 0.316847 -vt 0.728385 0.330936 -vt 0.701954 0.347609 -vt 0.931090 0.296783 -vt 0.033414 0.376652 -vt 0.962934 0.300782 -vt 0.900694 0.295633 -vt 0.871218 0.297094 -vt 0.842478 0.301055 -vt 0.814417 0.307449 -vt 0.787026 0.316247 -vt 0.760308 0.327454 -vt 0.734230 0.341120 -vt 0.708652 0.357378 -vt 0.930180 0.308259 -vt 0.041007 0.385716 -vt 0.960812 0.312428 -vt 0.900816 0.307026 -vt 0.872324 0.308387 -vt 0.844543 0.312193 -vt 0.817417 0.318364 -vt 0.790938 0.326869 -vt 0.765107 0.337717 -vt 0.739897 0.350974 -vt 0.715201 0.366811 -vt 0.690748 0.385716 -vt 0.635941 0.312428 -vt 0.929237 0.319242 -vt 0.900904 0.317951 -vt 0.873371 0.319222 -vt 0.846515 0.322881 -vt 0.820289 0.328841 -vt 0.794687 0.337066 -vt 0.769713 0.347570 -vt 0.745351 0.360427 -vt 0.721539 0.375827 -vt 0.928307 0.329471 -vt 0.900962 0.328153 -vt 0.874339 0.329349 -vt 0.848353 0.332875 -vt 0.822970 0.338639 -vt 0.798190 0.346604 -vt 0.774022 0.356784 -vt 0.750465 0.369259 -vt 0.727496 0.384213 -vt 0.927452 0.338568 -vt 0.061599 0.408948 -vt 0.954564 0.342687 -vt 0.900998 0.337252 -vt 0.875195 0.338391 -vt 0.849990 0.341803 -vt 0.825363 0.347394 -vt 0.801319 0.355126 -vt 0.777874 0.365014 -vt 0.755040 0.377137 -vt 0.732826 0.391667 -vt 0.926736 0.346081 -vt 0.066715 0.414566 -vt 0.952981 0.350062 -vt 0.901020 0.344786 -vt 0.875900 0.345885 -vt 0.851345 0.349205 -vt 0.827345 0.354654 -vt 0.803914 0.362193 -vt 0.781070 0.371837 -vt 0.758837 0.383661 -vt 0.737242 0.397824 -vt 0.926211 0.351536 -vt 0.901028 0.350264 -vt 0.876407 0.351338 -vt 0.852327 0.354594 -vt 0.828786 0.359940 -vt 0.805802 0.367338 -vt 0.783398 0.376802 -vt 0.761602 0.388407 -vt 0.740459 0.402303 -vt 0.925904 0.354700 -vt 0.947708 0.736767 -vt 0.072475 0.421039 -vt 0.951284 0.358505 -vt 0.973088 0.732426 -vt 0.332023 0.829367 -vt 0.852896 0.357715 -vt 0.882480 0.766687 -vt 0.829624 0.363000 -vt 0.864768 0.781982 -vt 0.784754 0.379675 -vt 0.836550 0.819324 -vt 0.742323 0.404883 -vt 0.819060 0.864309 -vt 0.722216 0.421039 -vt 0.626414 0.358505 -vt 0.490746 0.836368 -vt 0.814364 0.889417 -vt 0.607888 0.272484 -vt 0.667831 0.357109 -vt 0.641749 0.276005 -vt 0.575480 0.271496 -vt 0.575351 0.254736 -vt 0.575285 0.258106 -vt 0.543968 0.273162 -vt 0.542814 0.249157 -vt 0.542659 0.259862 -vt 0.513207 0.277451 -vt 0.510925 0.242148 -vt 0.510770 0.264326 -vt 0.483159 0.284324 -vt 0.453835 0.293756 -vt 0.448543 0.223482 -vt 0.449201 0.281260 -vt 0.425252 0.305740 -vt 0.397400 0.320284 -vt 0.370152 0.337395 -vt 0.316878 0.697404 -vt 0.342961 0.357110 -vt 0.607075 0.284907 -vt 0.675519 0.367143 -vt 0.640018 0.288669 -vt 0.575667 0.283846 -vt 0.545190 0.285411 -vt 0.515463 0.289533 -vt 0.486433 0.296160 -vt 0.458100 0.305266 -vt 0.430470 0.316847 -vt 0.403515 0.330936 -vt 0.377084 0.347609 -vt 0.315148 0.710069 -vt 0.350648 0.367143 -vt 0.606220 0.296783 -vt 0.683155 0.376652 -vt 0.638063 0.300782 -vt 0.575824 0.295633 -vt 0.546347 0.297094 -vt 0.517608 0.301055 -vt 0.489546 0.307449 -vt 0.462156 0.316248 -vt 0.435438 0.327454 -vt 0.409359 0.341120 -vt 0.383781 0.357378 -vt 0.605310 0.308258 -vt 0.575946 0.307026 -vt 0.547453 0.308387 -vt 0.519672 0.312193 -vt 0.492547 0.318364 -vt 0.466068 0.326869 -vt 0.440237 0.337717 -vt 0.415026 0.350974 -vt 0.390331 0.366811 -vt 0.604367 0.319242 -vt 0.698154 0.394254 -vt 0.633751 0.323486 -vt 0.576034 0.317951 -vt 0.548501 0.319222 -vt 0.521645 0.322881 -vt 0.495419 0.328841 -vt 0.469816 0.337066 -vt 0.444842 0.347570 -vt 0.420481 0.360428 -vt 0.396669 0.375827 -vt 0.603437 0.329470 -vt 0.705124 0.402081 -vt 0.631620 0.333691 -vt 0.576092 0.328153 -vt 0.549469 0.329349 -vt 0.523483 0.332875 -vt 0.498100 0.338639 -vt 0.473320 0.346604 -vt 0.449151 0.356784 -vt 0.425594 0.369259 -vt 0.402626 0.384214 -vt 0.602582 0.338567 -vt 0.711340 0.408948 -vt 0.629694 0.342687 -vt 0.576128 0.337252 -vt 0.550325 0.338391 -vt 0.525120 0.341803 -vt 0.500492 0.347394 -vt 0.476449 0.355126 -vt 0.453004 0.365015 -vt 0.430169 0.377138 -vt 0.407955 0.391667 -vt 0.601865 0.346081 -vt 0.716456 0.414566 -vt 0.628110 0.350062 -vt 0.576149 0.344786 -vt 0.551030 0.345885 -vt 0.526474 0.349205 -vt 0.502475 0.354654 -vt 0.479043 0.362193 -vt 0.456200 0.371837 -vt 0.433966 0.383662 -vt 0.412372 0.397824 -vt 0.601341 0.351536 -vt 0.720144 0.418645 -vt 0.626996 0.355390 -vt 0.576158 0.350264 -vt 0.551537 0.351338 -vt 0.527457 0.354594 -vt 0.503916 0.359940 -vt 0.480931 0.367338 -vt 0.458527 0.376803 -vt 0.436731 0.388407 -vt 0.415588 0.402303 -vt 0.601034 0.354700 -vt 0.465367 0.840710 -vt 0.551828 0.354497 -vt 0.419988 0.857871 -vt 0.504754 0.363000 -vt 0.382427 0.885925 -vt 0.417452 0.404883 -vt 0.336719 0.968252 -vt 0.687546 0.562885 -vt 0.687543 0.562881 -vt 0.687544 0.562881 -vt 0.687548 0.562882 -vt 0.687544 0.562886 -vt 0.687543 0.562882 -vt 0.687542 0.562886 -vt 0.687542 0.562882 -vt 0.687541 0.562886 -vt 0.687542 0.562882 -vt 0.687540 0.562885 -vt 0.687542 0.562882 -vt 0.687539 0.562885 -vt 0.687542 0.562881 -vt 0.687539 0.562884 -vt 0.687542 0.562881 -vt 0.687538 0.562883 -vt 0.687542 0.562881 -vt 0.687537 0.562881 -vt 0.687542 0.562881 -vt 0.687538 0.562879 -vt 0.687542 0.562880 -vt 0.687556 0.562896 -vt 0.687562 0.562886 -vt 0.687548 0.562900 -vt 0.687542 0.562902 -vt 0.687537 0.562902 -vt 0.687533 0.562902 -vt 0.687529 0.562900 -vt 0.687524 0.562898 -vt 0.687520 0.562893 -vt 0.687516 0.562885 -vt 0.687582 0.562926 -vt 0.687598 0.562898 -vt 0.687561 0.562941 -vt 0.687542 0.562948 -vt 0.687526 0.562952 -vt 0.687512 0.562954 -vt 0.687496 0.562953 -vt 0.687479 0.562948 -vt 0.687459 0.562932 -vt 0.687443 0.562903 -vt 0.687441 0.562859 -vt 0.687645 0.562996 -vt 0.687592 0.563038 -vt 0.687543 0.563063 -vt 0.687500 0.563079 -vt 0.687458 0.563090 -vt 0.687412 0.563094 -vt 0.687358 0.563082 -vt 0.687297 0.563041 -vt 0.687245 0.562959 -vt 0.687777 0.563142 -vt 0.687659 0.563243 -vt 0.687545 0.563306 -vt 0.687440 0.563349 -vt 0.687338 0.563380 -vt 0.687225 0.563391 -vt 0.687092 0.563362 -vt 0.686950 0.563265 -vt 0.686835 0.563074 -vt 0.686809 0.562788 -vt 0.688023 0.563407 -vt 0.687777 0.563618 -vt 0.687537 0.563746 -vt 0.687316 0.563830 -vt 0.687099 0.563883 -vt 0.686867 0.563888 -vt 0.686610 0.563808 -vt 0.686355 0.563600 -vt 0.686164 0.563237 -vt 0.688429 0.563834 -vt 0.688771 0.563173 -vt 0.687964 0.564223 -vt 0.687511 0.564442 -vt 0.687097 0.564567 -vt 0.686702 0.564619 -vt 0.686298 0.564575 -vt 0.685886 0.564386 -vt 0.685508 0.564008 -vt 0.685245 0.563417 -vt 0.685221 0.562626 -vt 0.689051 0.564444 -vt 0.688268 0.565105 -vt 0.687502 0.565452 -vt 0.686809 0.565610 -vt 0.686166 0.565626 -vt 0.685541 0.565482 -vt 0.684936 0.565136 -vt 0.684393 0.564540 -vt 0.684010 0.563653 -vt 0.683970 0.562479 -vt 0.689996 0.565249 -vt 0.690803 0.563481 -vt 0.688838 0.566325 -vt 0.687665 0.566900 -vt 0.686584 0.567141 -vt 0.685581 0.567134 -vt 0.684618 0.566890 -vt 0.683676 0.566370 -vt 0.682788 0.565488 -vt 0.682088 0.564126 -vt 0.691581 0.566327 -vt 0.692522 0.563722 -vt 0.690045 0.568073 -vt 0.688379 0.569126 -vt 0.686743 0.569675 -vt 0.685144 0.569840 -vt 0.683521 0.569653 -vt 0.681805 0.569031 -vt 0.679989 0.567742 -vt 0.678336 0.565368 -vt 0.677931 0.561633 -vt 0.687539 0.562877 -vt 0.687542 0.562880 -vt 0.687540 0.562876 -vt 0.687542 0.562880 -vt 0.687541 0.562875 -vt 0.687542 0.562879 -vt 0.687542 0.562875 -vt 0.687543 0.562879 -vt 0.687544 0.562875 -vt 0.687543 0.562879 -vt 0.687544 0.562875 -vt 0.687543 0.562879 -vt 0.687546 0.562876 -vt 0.687543 0.562880 -vt 0.687547 0.562877 -vt 0.687543 0.562880 -vt 0.687548 0.562879 -vt 0.687544 0.562880 -vt 0.687522 0.562863 -vt 0.687516 0.562873 -vt 0.687530 0.562858 -vt 0.687536 0.562856 -vt 0.687542 0.562856 -vt 0.687546 0.562857 -vt 0.687550 0.562859 -vt 0.687554 0.562862 -vt 0.687558 0.562867 -vt 0.687562 0.562875 -vt 0.687462 0.562818 -vt 0.687490 0.562797 -vt 0.687516 0.562791 -vt 0.687536 0.562794 -vt 0.687551 0.562801 -vt 0.687564 0.562810 -vt 0.687577 0.562822 -vt 0.687590 0.562839 -vt 0.687600 0.562864 -vt 0.687289 0.562707 -vt 0.687235 0.562826 -vt 0.687371 0.562642 -vt 0.687449 0.562622 -vt 0.687512 0.562632 -vt 0.687558 0.562656 -vt 0.687596 0.562687 -vt 0.687631 0.562724 -vt 0.687664 0.562771 -vt 0.687689 0.562836 -vt 0.687686 0.562921 -vt 0.686917 0.562508 -vt 0.687090 0.562346 -vt 0.687270 0.562285 -vt 0.687424 0.562296 -vt 0.687545 0.562348 -vt 0.687643 0.562421 -vt 0.687732 0.562511 -vt 0.687813 0.562623 -vt 0.687873 0.562773 -vt 0.687870 0.562968 -vt 0.686303 0.562229 -vt 0.686130 0.562713 -vt 0.686588 0.561919 -vt 0.686904 0.561768 -vt 0.687202 0.561747 -vt 0.687459 0.561817 -vt 0.687680 0.561942 -vt 0.687883 0.562110 -vt 0.688070 0.562334 -vt 0.688208 0.562643 -vt 0.688211 0.563047 -vt 0.685464 0.561878 -vt 0.685873 0.561382 -vt 0.686340 0.561105 -vt 0.686808 0.561006 -vt 0.687252 0.561050 -vt 0.687670 0.561204 -vt 0.688074 0.561458 -vt 0.688457 0.561841 -vt 0.688749 0.562405 -vt 0.684344 0.561377 -vt 0.684947 0.560645 -vt 0.685617 0.560218 -vt 0.686295 0.560024 -vt 0.686967 0.560016 -vt 0.687643 0.560175 -vt 0.688333 0.560517 -vt 0.689012 0.561107 -vt 0.689539 0.562037 -vt 0.689611 0.563304 -vt 0.682634 0.560452 -vt 0.681891 0.562177 -vt 0.683663 0.559366 -vt 0.684731 0.558765 -vt 0.685764 0.558499 -vt 0.686770 0.558485 -vt 0.687787 0.558707 -vt 0.688837 0.559214 -vt 0.689869 0.560119 -vt 0.690666 0.561547 -vt 0.679472 0.558276 -vt 0.681630 0.556541 -vt 0.683641 0.555804 -vt 0.685411 0.555637 -vt 0.687010 0.555841 -vt 0.688531 0.556375 -vt 0.690020 0.557304 -vt 0.691412 0.558772 -vt 0.692432 0.560924 -vt 0.694505 0.567959 -vt 0.695713 0.564089 -vt 0.692515 0.570758 -vt 0.690252 0.572691 -vt 0.687898 0.573988 -vt 0.685441 0.574809 -vt 0.682734 0.575206 -vt 0.679518 0.575067 -vt 0.675384 0.573976 -vt 0.669583 0.570651 -vt 0.660073 0.559294 -vt 0.699630 0.570433 -vt 0.697064 0.574750 -vt 0.694180 0.578001 -vt 0.691156 0.580516 -vt 0.687936 0.582545 -vt 0.684283 0.584243 -vt 0.679786 0.585661 -vt 0.673746 0.586704 -vt 0.664843 0.587033 -vt 0.650101 0.585930 -vt 0.659188 0.533229 -vt 0.707848 0.573874 -vt 0.709782 0.565378 -vt 0.704734 0.580248 -vt 0.701346 0.585180 -vt 0.697901 0.589202 -vt 0.694307 0.592710 -vt 0.690277 0.596015 -vt 0.685359 0.599380 -vt 0.678875 0.603060 -vt 0.669738 0.607385 -vt 0.720872 0.579115 -vt 0.725314 0.566583 -vt 0.716598 0.587694 -vt 0.712678 0.594272 -vt 0.709054 0.599688 -vt 0.705492 0.604502 -vt 0.701626 0.609186 -vt 0.696992 0.614190 -vt 0.691017 0.619979 -vt 0.682937 0.627139 -vt 0.671690 0.636611 -vt 0.686635 0.494746 -vt 0.736899 0.588057 -vt 0.743303 0.576010 -vt 0.744407 0.559450 -vt 0.731892 0.597564 -vt 0.727835 0.605182 -vt 0.724434 0.611533 -vt 0.721318 0.617200 -vt 0.718033 0.622784 -vt 0.714098 0.628881 -vt 0.709029 0.636068 -vt 0.702295 0.644971 -vt 0.693281 0.656431 -vt 0.707039 0.480266 -vt 0.755031 0.599024 -vt 0.761714 0.586932 -vt 0.763254 0.550229 -vt 0.750028 0.609044 -vt 0.746325 0.617283 -vt 0.743546 0.624174 -vt 0.741225 0.630296 -vt 0.738820 0.636363 -vt 0.735815 0.643099 -vt 0.731775 0.651130 -vt 0.726301 0.661040 -vt 0.775057 0.610618 -vt 0.781354 0.598221 -vt 0.782605 0.540572 -vt 0.770611 0.621095 -vt 0.767687 0.629792 -vt 0.765871 0.637039 -vt 0.764629 0.643420 -vt 0.763339 0.649758 -vt 0.761434 0.656884 -vt 0.758485 0.665441 -vt 0.754137 0.675943 -vt 0.748048 0.688908 -vt 0.757519 0.455353 -vt 0.797020 0.622099 -vt 0.793472 0.633022 -vt 0.791633 0.642134 -vt 0.791044 0.649709 -vt 0.791111 0.656336 -vt 0.791137 0.662921 -vt 0.790495 0.670366 -vt 0.788722 0.679311 -vt 0.785441 0.690208 -vt 0.780291 0.703466 -vt 0.787381 0.443872 -vt 0.820990 0.633086 -vt 0.818532 0.644415 -vt 0.818003 0.653926 -vt 0.818856 0.661869 -vt 0.820423 0.668828 -vt 0.821953 0.675747 -vt 0.822760 0.683561 -vt 0.822300 0.692891 -vt 0.820109 0.704133 -vt 0.846998 0.643401 -vt 0.845669 0.654882 -vt 0.846594 0.664648 -vt 0.849102 0.672966 -vt 0.852449 0.680412 -vt 0.855818 0.687907 -vt 0.858395 0.696341 -vt 0.859457 0.706265 -vt 0.858378 0.717987 -vt 0.672543 0.551257 -vt 0.678767 0.549682 -vt 0.682869 0.549675 -vt 0.685909 0.550291 -vt 0.688378 0.551272 -vt 0.690544 0.552595 -vt 0.692534 0.554367 -vt 0.694305 0.556773 -vt 0.695567 0.560005 -vt 0.672206 0.535859 -vt 0.680039 0.538042 -vt 0.685281 0.540217 -vt 0.689120 0.542409 -vt 0.692180 0.544692 -vt 0.694817 0.547214 -vt 0.697213 0.550186 -vt 0.699352 0.553887 -vt 0.700953 0.558620 -vt 0.701383 0.564714 -vt 0.701393 0.564601 -vt 0.680443 0.519236 -vt 0.656090 0.613049 -vt 0.670053 0.511905 -vt 0.687600 0.524630 -vt 0.692721 0.529022 -vt 0.696594 0.532893 -vt 0.699739 0.536587 -vt 0.702505 0.540429 -vt 0.705095 0.544777 -vt 0.707520 0.550053 -vt 0.709449 0.556776 -vt 0.694818 0.504308 -vt 0.700820 0.511663 -vt 0.705295 0.517630 -vt 0.708764 0.522775 -vt 0.711642 0.527575 -vt 0.714270 0.532500 -vt 0.716912 0.538057 -vt 0.719710 0.544832 -vt 0.722615 0.553683 -vt 0.713669 0.491048 -vt 0.718641 0.499584 -vt 0.722383 0.506544 -vt 0.725274 0.512477 -vt 0.727671 0.517924 -vt 0.729940 0.523476 -vt 0.732440 0.529749 -vt 0.735477 0.537352 -vt 0.739323 0.546957 -vt 0.736253 0.478884 -vt 0.718985 0.673494 -vt 0.730717 0.467362 -vt 0.740348 0.488159 -vt 0.743326 0.495726 -vt 0.745496 0.502091 -vt 0.747197 0.507832 -vt 0.748837 0.513650 -vt 0.750862 0.520260 -vt 0.753684 0.528257 -vt 0.757675 0.538117 -vt 0.762217 0.467340 -vt 0.765481 0.477091 -vt 0.767604 0.485046 -vt 0.768877 0.491664 -vt 0.769649 0.497540 -vt 0.770383 0.503468 -vt 0.771604 0.510259 -vt 0.773785 0.518501 -vt 0.777333 0.528546 -vt 0.791354 0.456139 -vt 0.793747 0.466211 -vt 0.794874 0.474454 -vt 0.795054 0.481286 -vt 0.794676 0.487301 -vt 0.794265 0.493357 -vt 0.794407 0.500329 -vt 0.795634 0.508801 -vt 0.798383 0.519057 -vt 0.802621 0.609301 -vt 0.803002 0.531178 -vt 0.823554 0.445194 -vt 0.815740 0.717614 -vt 0.820244 0.432754 -vt 0.824991 0.455523 -vt 0.824948 0.464042 -vt 0.823830 0.471132 -vt 0.822098 0.477387 -vt 0.820333 0.483680 -vt 0.819179 0.490898 -vt 0.819223 0.499615 -vt 0.820970 0.510088 -vt 0.825792 0.619879 -vt 0.824808 0.522364 -vt 0.858989 0.434366 -vt 0.854570 0.731705 -vt 0.856156 0.421760 -vt 0.859457 0.445057 -vt 0.858059 0.454030 -vt 0.855338 0.461602 -vt 0.851910 0.468316 -vt 0.848485 0.474995 -vt 0.845781 0.482479 -vt 0.844447 0.491334 -vt 0.845095 0.501847 -vt 0.471148 0.482063 -vt 0.478445 0.458354 -vt 0.495566 0.463867 -vt 0.649381 0.636751 -vt 0.488727 0.486632 -vt 0.642048 0.611577 -vt 0.456151 0.477630 -vt 0.464056 0.452846 -vt 0.443313 0.473454 -vt 0.451351 0.447356 -vt 0.431903 0.469650 -vt 0.439492 0.441970 -vt 0.421236 0.466287 -vt 0.427891 0.436822 -vt 0.410737 0.463430 -vt 0.416134 0.432099 -vt 0.399841 0.461158 -vt 0.403824 0.428012 -vt 0.387850 0.459582 -vt 0.390410 0.424787 -vt 0.373813 0.458872 -vt 0.375114 0.422659 -vt 0.467526 0.501736 -vt 0.484919 0.504752 -vt 0.637805 0.591821 -vt 0.452665 0.498323 -vt 0.440224 0.494855 -vt 0.429511 0.491681 -vt 0.419740 0.489023 -vt 0.410223 0.487022 -vt 0.400319 0.485797 -vt 0.389313 0.485511 -vt 0.376261 0.486438 -vt 0.466883 0.518545 -vt 0.452756 0.516206 -vt 0.441016 0.513367 -vt 0.431057 0.510583 -vt 0.422062 0.508303 -vt 0.413299 0.506828 -vt 0.404140 0.506364 -vt 0.393946 0.507138 -vt 0.381921 0.509519 -vt 0.468446 0.533842 -vt 0.455607 0.532595 -vt 0.444873 0.530537 -vt 0.435739 0.528292 -vt 0.427454 0.526462 -vt 0.419323 0.525501 -vt 0.410796 0.525708 -vt 0.401369 0.527357 -vt 0.390456 0.530836 -vt 0.471568 0.548568 -vt 0.460348 0.548333 -vt 0.450854 0.547256 -vt 0.442638 0.545898 -vt 0.435083 0.544847 -vt 0.427609 0.544584 -vt 0.419770 0.545455 -vt 0.411185 0.547752 -vt 0.401447 0.551819 -vt 0.475672 0.563069 -vt 0.486938 0.561491 -vt 0.639112 0.531541 -vt 0.466111 0.563700 -vt 0.457903 0.563745 -vt 0.450620 0.563630 -vt 0.443802 0.563775 -vt 0.437043 0.564531 -vt 0.430000 0.566172 -vt 0.422352 0.568936 -vt 0.413762 0.573075 -vt 0.480128 0.577356 -vt 0.472033 0.578699 -vt 0.464950 0.579922 -vt 0.458472 0.581286 -vt 0.452298 0.582984 -vt 0.446199 0.585133 -vt 0.439936 0.587804 -vt 0.433196 0.591063 -vt 0.425604 0.594990 -vt 0.484207 0.591451 -vt 0.492526 0.589779 -vt 0.644104 0.502128 -vt 0.477218 0.593429 -vt 0.470948 0.595884 -vt 0.465039 0.598899 -vt 0.459313 0.602439 -vt 0.453663 0.606355 -vt 0.447932 0.610426 -vt 0.441829 0.614416 -vt 0.434945 0.618117 -vt 0.426860 0.621378 -vt 0.579311 0.470250 -vt 0.487165 0.605726 -vt 0.480785 0.608447 -vt 0.474849 0.612335 -vt 0.469063 0.617268 -vt 0.463347 0.622975 -vt 0.457693 0.629009 -vt 0.452033 0.634777 -vt 0.446156 0.639677 -vt 0.439703 0.643224 -vt 0.432269 0.645037 -vt 0.587762 0.448347 -vt 0.481657 0.625231 -vt 0.645517 0.431305 -vt 0.462293 0.645988 -vt 0.625641 0.422099 -vt 0.444680 0.666594 -vt 0.612105 0.421889 -vt 0.438711 0.670351 -vt 0.607930 0.421760 -vt 0.516318 0.641832 -vt 0.518464 0.683334 -vt 0.357069 0.421760 -vt 0.497333 0.684810 -vt 0.356430 0.459256 -vt 0.496286 0.641120 -vt 0.532165 0.641188 -vt 0.535874 0.680257 -vt 0.545511 0.639483 -vt 0.550743 0.675999 -vt 0.557485 0.636960 -vt 0.564065 0.670934 -vt 0.568893 0.633781 -vt 0.576556 0.665372 -vt 0.580382 0.630058 -vt 0.588752 0.659582 -vt 0.592621 0.625884 -vt 0.601211 0.653782 -vt 0.606427 0.621348 -vt 0.614710 0.648096 -vt 0.622690 0.616557 -vt 0.630328 0.642517 -vt 0.519404 0.610843 -vt 0.359802 0.489080 -vt 0.501056 0.607180 -vt 0.533866 0.612358 -vt 0.546010 0.612352 -vt 0.556885 0.611192 -vt 0.567271 0.609092 -vt 0.577855 0.606212 -vt 0.589413 0.602739 -vt 0.602840 0.598942 -vt 0.618911 0.595187 -vt 0.526378 0.585527 -vt 0.366946 0.514211 -vt 0.510252 0.579631 -vt 0.539400 0.588670 -vt 0.550472 0.589883 -vt 0.560433 0.589626 -vt 0.569953 0.588174 -vt 0.579696 0.585773 -vt 0.590457 0.582781 -vt 0.603116 0.579706 -vt 0.618294 0.577114 -vt 0.483365 0.519941 -vt 0.635962 0.575468 -vt 0.536493 0.562734 -vt 0.377307 0.536839 -vt 0.522704 0.555656 -vt 0.548073 0.566936 -vt 0.558137 0.569033 -vt 0.567277 0.569492 -vt 0.576027 0.568630 -vt 0.584967 0.566780 -vt 0.594814 0.564441 -vt 0.606319 0.562259 -vt 0.619985 0.560850 -vt 0.483411 0.533841 -vt 0.635829 0.560674 -vt 0.549113 0.540743 -vt 0.390100 0.558176 -vt 0.537421 0.533522 -vt 0.559277 0.545417 -vt 0.568300 0.548118 -vt 0.576582 0.549248 -vt 0.584528 0.549134 -vt 0.592602 0.548143 -vt 0.601371 0.546785 -vt 0.611430 0.545664 -vt 0.623240 0.545326 -vt 0.484694 0.547558 -vt 0.636996 0.546188 -vt 0.562972 0.518835 -vt 0.403893 0.578907 -vt 0.552972 0.512321 -vt 0.571789 0.523466 -vt 0.579697 0.526578 -vt 0.587022 0.528464 -vt 0.594091 0.529383 -vt 0.601248 0.529621 -vt 0.608885 0.529540 -vt 0.617453 0.529568 -vt 0.627405 0.530118 -vt 0.576200 0.496694 -vt 0.416792 0.599695 -vt 0.567509 0.491426 -vt 0.583771 0.500995 -vt 0.590554 0.504514 -vt 0.596913 0.507367 -vt 0.603149 0.509634 -vt 0.609486 0.511393 -vt 0.616149 0.512771 -vt 0.623440 0.513964 -vt 0.631783 0.515224 -vt 0.489739 0.575597 -vt 0.641686 0.516802 -vt 0.586867 0.474132 -vt 0.593273 0.478088 -vt 0.599043 0.482144 -vt 0.604618 0.486192 -vt 0.610249 0.490042 -vt 0.616030 0.493496 -vt 0.622039 0.496425 -vt 0.628441 0.498795 -vt 0.635593 0.500665 -vt 0.594017 0.451639 -vt 0.598986 0.455477 -vt 0.603582 0.459921 -vt 0.608418 0.464861 -vt 0.613796 0.470059 -vt 0.619554 0.475243 -vt 0.625435 0.479851 -vt 0.631492 0.483528 -vt 0.638018 0.486076 -vt 0.494644 0.604217 -vt 0.645701 0.487341 -vt 0.600066 0.432298 -vt 0.431856 0.671656 -vt 0.599232 0.423063 -vt 0.602432 0.436861 -vt 0.604837 0.440900 -vt 0.607869 0.445339 -vt 0.612105 0.450131 -vt 0.617982 0.454888 -vt 0.624776 0.461491 -vt 0.625670 0.457679 -vt 0.631487 0.466790 -vt 0.634073 0.458360 -vt 0.638322 0.470327 -vt 0.642244 0.455667 -vt 0.644796 0.438455 -vt 0.488243 0.621303 -vt 0.649381 0.439227 -vt 0.495566 0.619571 -vt 0.645901 0.471688 -vt 0.648233 0.448598 -vt 0.643899 0.446794 -vt 0.641090 0.431423 -vt 0.635023 0.426791 -vt 0.475213 0.630983 -vt 0.639030 0.426048 -vt 0.628519 0.424363 -vt 0.468721 0.638111 -vt 0.632037 0.423251 -vt 0.622559 0.423418 -vt 0.617525 0.423331 -vt 0.456134 0.653849 -vt 0.620255 0.421829 -vt 0.613446 0.423685 -vt 0.450331 0.660925 -vt 0.615873 0.421878 -vt 0.610141 0.424262 -vt 0.607248 0.425087 -vt 0.604389 0.426940 -vt 0.641080 0.437887 -vt 0.640305 0.445300 -vt 0.637612 0.431600 -vt 0.632009 0.427450 -vt 0.625989 0.425262 -vt 0.620427 0.424426 -vt 0.615722 0.424432 -vt 0.611988 0.424945 -vt 0.609204 0.425746 -vt 0.607215 0.426693 -vt 0.605704 0.427982 -vt 0.638144 0.437468 -vt 0.637545 0.444179 -vt 0.634933 0.431795 -vt 0.629792 0.428017 -vt 0.624217 0.425984 -vt 0.619004 0.425190 -vt 0.614592 0.425209 -vt 0.611162 0.425749 -vt 0.608756 0.426576 -vt 0.607255 0.427452 -vt 0.606270 0.428400 -vt 0.635874 0.437169 -vt 0.635343 0.443244 -vt 0.632943 0.432003 -vt 0.628239 0.428518 -vt 0.623059 0.426588 -vt 0.618154 0.425790 -vt 0.613990 0.425757 -vt 0.610772 0.426235 -vt 0.608574 0.426999 -vt 0.607295 0.427778 -vt 0.606507 0.428558 -vt 0.634236 0.436996 -vt 0.631578 0.432250 -vt 0.627264 0.429000 -vt 0.622429 0.427145 -vt 0.617800 0.426323 -vt 0.613846 0.426207 -vt 0.610768 0.426577 -vt 0.608642 0.427225 -vt 0.607387 0.427916 -vt 0.633174 0.436989 -vt 0.630777 0.432603 -vt 0.626815 0.429543 -vt 0.622298 0.427745 -vt 0.617923 0.426889 -vt 0.614145 0.426686 -vt 0.611163 0.426950 -vt 0.609051 0.427499 -vt 0.607745 0.428136 -vt 0.632480 0.437231 -vt 0.630413 0.433201 -vt 0.626833 0.430282 -vt 0.622653 0.428503 -vt 0.618527 0.427591 -vt 0.614904 0.427287 -vt 0.612000 0.427439 -vt 0.609870 0.427898 -vt 0.608466 0.428517 -vt 0.607575 0.429359 -vt 0.631624 0.437796 -vt 0.630196 0.434239 -vt 0.627196 0.431403 -vt 0.623460 0.429565 -vt 0.619635 0.428559 -vt 0.616160 0.428114 -vt 0.613336 0.428122 -vt 0.611188 0.428491 -vt 0.609671 0.429132 -vt 0.630378 0.438510 -vt 0.629779 0.435803 -vt 0.627703 0.433103 -vt 0.624631 0.431113 -vt 0.621232 0.429925 -vt 0.617964 0.429340 -vt 0.615199 0.429127 -vt 0.613065 0.429358 -vt 0.611503 0.430011 -vt 0.629116 0.439037 -vt 0.628846 0.440172 -vt 0.628892 0.437458 -vt 0.627892 0.435339 -vt 0.625847 0.433323 -vt 0.623134 0.431873 -vt 0.620294 0.431067 -vt 0.617605 0.430784 -vt 0.615408 0.430700 -vt 0.613875 0.431196 -vt 0.613021 0.432353 -vt 0.603268 0.430372 -vt 0.603669 0.433813 -vt 0.604969 0.437632 -vt 0.607331 0.441974 -vt 0.611010 0.446660 -vt 0.616228 0.451189 -vt 0.623172 0.454418 -vt 0.630993 0.455270 -vt 0.638497 0.453039 -vt 0.604895 0.429833 -vt 0.604698 0.432213 -vt 0.605291 0.435514 -vt 0.607126 0.439629 -vt 0.610384 0.444130 -vt 0.615116 0.448464 -vt 0.621386 0.451670 -vt 0.628599 0.452703 -vt 0.635474 0.450825 -vt 0.605656 0.429628 -vt 0.605312 0.431421 -vt 0.605610 0.434286 -vt 0.607132 0.438090 -vt 0.610092 0.442332 -vt 0.614455 0.446439 -vt 0.620169 0.449525 -vt 0.626783 0.450587 -vt 0.633070 0.448987 -vt 0.605967 0.429555 -vt 0.605627 0.431097 -vt 0.605883 0.433676 -vt 0.607296 0.437179 -vt 0.610081 0.441138 -vt 0.614169 0.444980 -vt 0.619419 0.447907 -vt 0.625489 0.448926 -vt 0.631224 0.447501 -vt 0.606132 0.429604 -vt 0.606638 0.428647 -vt 0.605884 0.431086 -vt 0.606233 0.433512 -vt 0.607664 0.436779 -vt 0.610363 0.440458 -vt 0.614244 0.444011 -vt 0.619094 0.446740 -vt 0.624685 0.447683 -vt 0.629916 0.446363 -vt 0.633743 0.442550 -vt 0.606465 0.429892 -vt 0.606947 0.428894 -vt 0.606328 0.431399 -vt 0.606822 0.433741 -vt 0.608335 0.436807 -vt 0.611000 0.440211 -vt 0.614713 0.443463 -vt 0.619205 0.445947 -vt 0.624370 0.446784 -vt 0.629152 0.445538 -vt 0.632676 0.442079 -vt 0.607110 0.430474 -vt 0.607110 0.432052 -vt 0.607787 0.434332 -vt 0.609419 0.437198 -vt 0.612083 0.440310 -vt 0.615642 0.443218 -vt 0.619794 0.445381 -vt 0.624543 0.446025 -vt 0.628828 0.444829 -vt 0.631892 0.441729 -vt 0.608234 0.431381 -vt 0.608679 0.430113 -vt 0.608390 0.433039 -vt 0.609274 0.435243 -vt 0.611049 0.437878 -vt 0.613724 0.440634 -vt 0.617121 0.443097 -vt 0.620889 0.444783 -vt 0.625155 0.445014 -vt 0.628632 0.443794 -vt 0.630947 0.441325 -vt 0.610030 0.432584 -vt 0.610401 0.431166 -vt 0.610365 0.434293 -vt 0.611463 0.436374 -vt 0.613372 0.438702 -vt 0.616031 0.440990 -vt 0.619185 0.442863 -vt 0.622313 0.443920 -vt 0.626003 0.443552 -vt 0.628378 0.442413 -vt 0.629773 0.440782 -vt 0.612736 0.433926 -vt 0.613238 0.435623 -vt 0.614463 0.437488 -vt 0.616362 0.439392 -vt 0.618854 0.441101 -vt 0.621596 0.442330 -vt 0.624315 0.442893 -vt 0.626772 0.441750 -vt 0.628040 0.441130 -vt 0.860178 0.445484 -vt 0.860178 0.496446 -vt 0.860178 0.547410 -vt 0.860178 0.598374 -vt 0.875040 0.431748 -vt 0.874330 0.612693 -vt 0.876905 0.434605 -vt 0.878223 0.437479 -vt 0.879086 0.440337 -vt 0.879555 0.443173 -vt 0.879662 0.445989 -vt 0.879418 0.448793 -vt 0.878811 0.451591 -vt 0.877805 0.454378 -vt 0.876338 0.457135 -vt 0.887877 0.429714 -vt 0.885926 0.424539 -vt 0.884235 0.620460 -vt 0.889059 0.434291 -vt 0.889753 0.438490 -vt 0.890084 0.442466 -vt 0.890106 0.446338 -vt 0.889828 0.450204 -vt 0.889222 0.454159 -vt 0.888212 0.458303 -vt 0.886647 0.462748 -vt 0.893782 0.428370 -vt 0.894418 0.433479 -vt 0.894794 0.438046 -vt 0.894958 0.442321 -vt 0.894927 0.446471 -vt 0.894696 0.450626 -vt 0.894241 0.454917 -vt 0.893504 0.459504 -vt 0.892363 0.464622 -vt 0.898205 0.427953 -vt 0.898388 0.433177 -vt 0.898512 0.437865 -vt 0.898549 0.442262 -vt 0.898485 0.446538 -vt 0.898308 0.450830 -vt 0.898002 0.455277 -vt 0.897542 0.460058 -vt 0.896888 0.465444 -vt 0.904405 0.428149 -vt 0.905079 0.422494 -vt 0.903801 0.624891 -vt 0.904081 0.433209 -vt 0.903908 0.437873 -vt 0.903797 0.442308 -vt 0.903706 0.446650 -vt 0.903618 0.451020 -vt 0.903534 0.455544 -vt 0.903476 0.460380 -vt 0.903508 0.465751 -vt 0.912388 0.429307 -vt 0.911753 0.433879 -vt 0.911375 0.438267 -vt 0.911166 0.442540 -vt 0.911083 0.446771 -vt 0.911113 0.451035 -vt 0.911270 0.455411 -vt 0.911603 0.459990 -vt 0.912207 0.464880 -vt 0.921128 0.431017 -vt 0.920552 0.434991 -vt 0.920199 0.438939 -vt 0.920020 0.442867 -vt 0.919992 0.446794 -vt 0.920110 0.450744 -vt 0.920389 0.454741 -vt 0.920866 0.458812 -vt 0.921606 0.462978 -vt 0.922712 0.467255 -vt 0.921997 0.477989 -vt 0.929478 0.432313 -vt 0.929221 0.435862 -vt 0.929071 0.439455 -vt 0.929018 0.443070 -vt 0.929058 0.446696 -vt 0.929192 0.450328 -vt 0.929432 0.453959 -vt 0.929793 0.457580 -vt 0.930300 0.461173 -vt 0.930989 0.464721 -vt 0.929869 0.479812 -vt 0.936541 0.432589 -vt 0.936616 0.436058 -vt 0.936664 0.439558 -vt 0.936712 0.443066 -vt 0.936780 0.446572 -vt 0.936879 0.450068 -vt 0.937013 0.453549 -vt 0.937180 0.457002 -vt 0.937365 0.460405 -vt 0.937527 0.463705 -vt 0.936407 0.480159 -vt 0.943889 0.432020 -vt 0.122072 0.981500 -vt 0.943431 0.428071 -vt 0.944125 0.617093 -vt 0.121491 0.984890 -vt 0.639503 0.949232 -vt 0.944269 0.442996 -vt 0.126387 0.972753 -vt 0.944356 0.453492 -vt 0.133857 0.966775 -vt 0.944356 0.457002 -vt 0.136888 0.965545 -vt 0.944301 0.460559 -vt 0.140137 0.964747 -vt 0.944125 0.464204 -vt 0.943431 0.479035 -vt 0.143576 0.964410 -vt 0.265428 0.984890 -vt 0.874330 0.459803 -vt 0.875040 0.482710 -vt 0.876905 0.485568 -vt 0.878223 0.488442 -vt 0.879086 0.491300 -vt 0.879555 0.494135 -vt 0.879662 0.496952 -vt 0.879418 0.499756 -vt 0.878811 0.502554 -vt 0.877805 0.505341 -vt 0.876338 0.508098 -vt 0.887878 0.480677 -vt 0.884234 0.467571 -vt 0.885926 0.475502 -vt 0.889059 0.485254 -vt 0.889753 0.489453 -vt 0.890084 0.493429 -vt 0.890106 0.497301 -vt 0.889828 0.501167 -vt 0.889222 0.505122 -vt 0.888212 0.509266 -vt 0.886647 0.513711 -vt 0.884234 0.518533 -vt 0.885925 0.526465 -vt 0.893782 0.479333 -vt 0.890573 0.470720 -vt 0.892747 0.473181 -vt 0.894418 0.484442 -vt 0.894794 0.489009 -vt 0.894958 0.493284 -vt 0.894927 0.497434 -vt 0.894696 0.501589 -vt 0.894241 0.505880 -vt 0.893503 0.510467 -vt 0.892363 0.515585 -vt 0.890573 0.521683 -vt 0.892747 0.524144 -vt 0.898205 0.478916 -vt 0.896001 0.471963 -vt 0.898038 0.472723 -vt 0.898389 0.484140 -vt 0.898512 0.488828 -vt 0.898549 0.493225 -vt 0.898485 0.497501 -vt 0.898308 0.501793 -vt 0.898002 0.506240 -vt 0.897542 0.511021 -vt 0.896888 0.516407 -vt 0.896000 0.522926 -vt 0.898038 0.523686 -vt 0.904405 0.479112 -vt 0.903800 0.472002 -vt 0.905079 0.473457 -vt 0.904081 0.484172 -vt 0.903908 0.488836 -vt 0.903797 0.493271 -vt 0.903706 0.497613 -vt 0.903618 0.501983 -vt 0.903534 0.506507 -vt 0.903476 0.511343 -vt 0.903508 0.516714 -vt 0.903800 0.522965 -vt 0.905079 0.524420 -vt 0.912389 0.480271 -vt 0.913261 0.470199 -vt 0.913426 0.475468 -vt 0.911754 0.484842 -vt 0.911375 0.489230 -vt 0.911166 0.493503 -vt 0.911083 0.497734 -vt 0.911113 0.501998 -vt 0.911270 0.506374 -vt 0.911603 0.510953 -vt 0.912206 0.515843 -vt 0.913261 0.521163 -vt 0.913425 0.526431 -vt 0.921128 0.481980 -vt 0.920552 0.485955 -vt 0.920199 0.489902 -vt 0.920020 0.493830 -vt 0.919992 0.497757 -vt 0.920110 0.501707 -vt 0.920389 0.505705 -vt 0.920866 0.509776 -vt 0.921606 0.513941 -vt 0.922712 0.518218 -vt 0.921997 0.528952 -vt 0.929478 0.483277 -vt 0.929221 0.486826 -vt 0.929072 0.490418 -vt 0.929018 0.494033 -vt 0.929058 0.497660 -vt 0.929192 0.501291 -vt 0.929432 0.504922 -vt 0.929793 0.508543 -vt 0.930300 0.512136 -vt 0.930989 0.515684 -vt 0.929869 0.530775 -vt 0.936541 0.483553 -vt 0.936616 0.487022 -vt 0.936664 0.490521 -vt 0.936712 0.494030 -vt 0.936780 0.497535 -vt 0.936879 0.501032 -vt 0.937013 0.504512 -vt 0.937180 0.507966 -vt 0.937365 0.511368 -vt 0.937527 0.514668 -vt 0.936407 0.531121 -vt 0.943889 0.482984 -vt 0.266008 0.981500 -vt 0.944106 0.486747 -vt 0.267039 0.978328 -vt 0.944213 0.490393 -vt 0.268489 0.975403 -vt 0.944334 0.500968 -vt 0.275010 0.968408 -vt 0.944356 0.504456 -vt 0.277793 0.966775 -vt 0.944356 0.507965 -vt 0.280825 0.965545 -vt 0.944301 0.511523 -vt 0.284074 0.964747 -vt 0.874330 0.510766 -vt 0.875040 0.533674 -vt 0.876905 0.536531 -vt 0.878223 0.539406 -vt 0.879086 0.542264 -vt 0.879555 0.545099 -vt 0.879662 0.547915 -vt 0.879419 0.550720 -vt 0.878812 0.553517 -vt 0.877805 0.556305 -vt 0.876338 0.559061 -vt 0.874330 0.561729 -vt 0.875040 0.584637 -vt 0.887877 0.531640 -vt 0.889059 0.536217 -vt 0.889753 0.540416 -vt 0.890084 0.544392 -vt 0.890106 0.548264 -vt 0.889828 0.552131 -vt 0.889222 0.556085 -vt 0.888213 0.560229 -vt 0.886647 0.564674 -vt 0.884234 0.569497 -vt 0.885925 0.577428 -vt 0.893782 0.530296 -vt 0.894418 0.535405 -vt 0.894794 0.539972 -vt 0.894958 0.544247 -vt 0.894927 0.548397 -vt 0.894696 0.552552 -vt 0.894241 0.556844 -vt 0.893504 0.561430 -vt 0.892364 0.566548 -vt 0.898205 0.529879 -vt 0.898388 0.535103 -vt 0.898512 0.539791 -vt 0.898549 0.544189 -vt 0.898485 0.548464 -vt 0.898308 0.552756 -vt 0.898002 0.557204 -vt 0.897543 0.561984 -vt 0.896889 0.567370 -vt 0.904405 0.530075 -vt 0.904081 0.535135 -vt 0.903908 0.539799 -vt 0.903797 0.544234 -vt 0.903706 0.548576 -vt 0.903618 0.552946 -vt 0.903534 0.557470 -vt 0.903476 0.562306 -vt 0.903508 0.567677 -vt 0.903800 0.573928 -vt 0.905079 0.575383 -vt 0.912388 0.531234 -vt 0.911753 0.535805 -vt 0.911375 0.540193 -vt 0.911166 0.544466 -vt 0.911083 0.548697 -vt 0.911113 0.552961 -vt 0.911271 0.557337 -vt 0.911603 0.561916 -vt 0.912207 0.566806 -vt 0.913261 0.572125 -vt 0.913425 0.577393 -vt 0.921128 0.532943 -vt 0.920552 0.536917 -vt 0.920199 0.540865 -vt 0.920020 0.544793 -vt 0.919992 0.548720 -vt 0.920110 0.552670 -vt 0.920389 0.556667 -vt 0.920866 0.560739 -vt 0.921606 0.564904 -vt 0.922712 0.569181 -vt 0.921997 0.579914 -vt 0.929478 0.534239 -vt 0.929221 0.537789 -vt 0.929071 0.541381 -vt 0.929018 0.544996 -vt 0.929058 0.548622 -vt 0.929192 0.552254 -vt 0.929432 0.555885 -vt 0.929793 0.559506 -vt 0.930300 0.563099 -vt 0.930989 0.566647 -vt 0.929869 0.581737 -vt 0.936541 0.534516 -vt 0.936616 0.537984 -vt 0.936664 0.541484 -vt 0.936712 0.544992 -vt 0.936780 0.548498 -vt 0.936879 0.551994 -vt 0.937013 0.555475 -vt 0.937180 0.558928 -vt 0.937365 0.562331 -vt 0.937527 0.565631 -vt 0.936407 0.582084 -vt 0.944106 0.537709 -vt 0.619029 0.820699 -vt 0.944269 0.544922 -vt 0.622313 0.815125 -vt 0.944356 0.555418 -vt 0.629783 0.809146 -vt 0.944125 0.566130 -vt 0.943431 0.580960 -vt 0.639503 0.806781 -vt 0.617417 0.969712 -vt 0.876905 0.587495 -vt 0.878223 0.590369 -vt 0.879086 0.593227 -vt 0.879555 0.596062 -vt 0.879662 0.598879 -vt 0.879419 0.601683 -vt 0.878812 0.604481 -vt 0.877805 0.607268 -vt 0.876338 0.610025 -vt 0.887877 0.582603 -vt 0.889059 0.587181 -vt 0.889753 0.591379 -vt 0.890084 0.595355 -vt 0.890106 0.599228 -vt 0.889828 0.603094 -vt 0.889223 0.607048 -vt 0.888213 0.611192 -vt 0.886648 0.615638 -vt 0.893782 0.581259 -vt 0.890574 0.572647 -vt 0.892746 0.575107 -vt 0.894417 0.586369 -vt 0.894794 0.590935 -vt 0.894958 0.595211 -vt 0.894927 0.599360 -vt 0.894696 0.603515 -vt 0.894242 0.607807 -vt 0.893504 0.612393 -vt 0.892364 0.617511 -vt 0.892747 0.422218 -vt 0.890574 0.623610 -vt 0.898205 0.580842 -vt 0.896001 0.573889 -vt 0.898037 0.574649 -vt 0.898388 0.586066 -vt 0.898512 0.590754 -vt 0.898549 0.595152 -vt 0.898485 0.599428 -vt 0.898308 0.603719 -vt 0.898002 0.608167 -vt 0.897543 0.612947 -vt 0.896889 0.618333 -vt 0.898038 0.421760 -vt 0.896001 0.624852 -vt 0.904405 0.581038 -vt 0.904081 0.586098 -vt 0.903908 0.590762 -vt 0.903797 0.595197 -vt 0.903706 0.599539 -vt 0.903618 0.603909 -vt 0.903534 0.608433 -vt 0.903476 0.613269 -vt 0.903508 0.618640 -vt 0.912388 0.582196 -vt 0.911753 0.586768 -vt 0.911375 0.591156 -vt 0.911166 0.595429 -vt 0.911083 0.599660 -vt 0.911113 0.603924 -vt 0.911271 0.608300 -vt 0.911603 0.612879 -vt 0.912207 0.617769 -vt 0.913425 0.424505 -vt 0.913262 0.623088 -vt 0.921128 0.583905 -vt 0.920552 0.587880 -vt 0.920199 0.591827 -vt 0.920020 0.595756 -vt 0.919993 0.599683 -vt 0.920110 0.603632 -vt 0.920389 0.607630 -vt 0.920866 0.611701 -vt 0.921607 0.615867 -vt 0.921997 0.427026 -vt 0.922713 0.620143 -vt 0.929478 0.585202 -vt 0.929221 0.588751 -vt 0.929072 0.592344 -vt 0.929018 0.595959 -vt 0.929058 0.599585 -vt 0.929193 0.603217 -vt 0.929432 0.606848 -vt 0.929793 0.610468 -vt 0.930300 0.614062 -vt 0.929869 0.428849 -vt 0.930989 0.617609 -vt 0.936541 0.585478 -vt 0.936616 0.588947 -vt 0.936664 0.592447 -vt 0.936712 0.595955 -vt 0.936780 0.599460 -vt 0.936879 0.602957 -vt 0.937013 0.606438 -vt 0.937180 0.609891 -vt 0.937365 0.613293 -vt 0.936407 0.429195 -vt 0.937527 0.616593 -vt 0.943889 0.584909 -vt 0.617998 0.966322 -vt 0.944269 0.595885 -vt 0.622313 0.957576 -vt 0.944305 0.599401 -vt 0.624498 0.955234 -vt 0.112986 0.979204 -vt 0.112211 0.983972 -vt 0.639238 0.939864 -vt 0.114416 0.974718 -vt 0.944106 0.435783 -vt 0.123103 0.978328 -vt 0.116451 0.970570 -vt 0.944213 0.439430 -vt 0.124552 0.975403 -vt 0.119041 0.966808 -vt 0.122133 0.963481 -vt 0.944305 0.446513 -vt 0.128572 0.970411 -vt 0.125677 0.960638 -vt 0.944334 0.450004 -vt 0.131074 0.968408 -vt 0.129619 0.958326 -vt 0.133907 0.956591 -vt 0.138490 0.955481 -vt 0.143312 0.955041 -vt 0.256148 0.983972 -vt 0.101002 0.976322 -vt 0.099998 0.983021 -vt 0.639136 0.927628 -vt 0.102951 0.970039 -vt 0.105763 0.964240 -vt 0.109360 0.958989 -vt 0.113664 0.954354 -vt 0.118602 0.950401 -vt 0.124099 0.947199 -vt 0.130082 0.944818 -vt 0.136478 0.943329 -vt 0.086890 0.973030 -vt 0.089478 0.964611 -vt 0.093225 0.956857 -vt 0.098020 0.949850 -vt 0.103760 0.943673 -vt 0.110347 0.938414 -vt 0.117686 0.934161 -vt 0.125681 0.931011 -vt 0.134241 0.929065 -vt 0.071560 0.969458 -vt 0.069858 0.980971 -vt 0.639327 0.897610 -vt 0.074883 0.958727 -vt 0.079667 0.948867 -vt 0.085773 0.939969 -vt 0.093072 0.932131 -vt 0.101443 0.925456 -vt 0.110773 0.920057 -vt 0.120949 0.916055 -vt 0.131863 0.913583 -vt 0.055971 0.965755 -vt 0.053799 0.979808 -vt 0.639454 0.881628 -vt 0.060082 0.952705 -vt 0.065939 0.940737 -vt 0.073379 0.929943 -vt 0.082253 0.920433 -vt 0.092422 0.912328 -vt 0.103755 0.905759 -vt 0.116127 0.900870 -vt 0.129424 0.897822 -vt 0.041085 0.962110 -vt 0.045983 0.946892 -vt 0.052871 0.932947 -vt 0.061578 0.920368 -vt 0.071943 0.909278 -vt 0.083811 0.899815 -vt 0.097033 0.892129 -vt 0.111476 0.886378 -vt 0.127022 0.882738 -vt 0.027836 0.958754 -vt 0.033448 0.941656 -vt 0.041247 0.925980 -vt 0.051072 0.911827 -vt 0.062754 0.899337 -vt 0.076123 0.888668 -vt 0.091013 0.879987 -vt 0.107273 0.873464 -vt 0.124788 0.869271 -vt 0.017063 0.955943 -vt 0.023246 0.937360 -vt 0.031773 0.920288 -vt 0.042500 0.904856 -vt 0.055249 0.891225 -vt 0.069837 0.879575 -vt 0.086078 0.870086 -vt 0.103803 0.862936 -vt 0.122882 0.858289 -vt 0.009380 0.953907 -vt 0.015949 0.934289 -vt 0.024984 0.916223 -vt 0.036350 0.899873 -vt 0.049861 0.885423 -vt 0.065320 0.873068 -vt 0.082527 0.862999 -vt 0.101294 0.855405 -vt 0.121469 0.850458 -vt 0.143106 0.848201 -vt 0.149215 0.975125 -vt 0.004602 0.952638 -vt 0.011382 0.932379 -vt 0.020731 0.913689 -vt 0.032499 0.896760 -vt 0.046490 0.881794 -vt 0.062498 0.868993 -vt 0.080314 0.858560 -vt 0.099734 0.850688 -vt 0.120580 0.845543 -vt 0.142784 0.843159 -vt 0.144297 0.974375 -vt 0.256923 0.979204 -vt 0.258352 0.974718 -vt 0.260388 0.970570 -vt 0.262978 0.966808 -vt 0.944269 0.493960 -vt 0.270324 0.972753 -vt 0.266070 0.963481 -vt 0.944305 0.497476 -vt 0.272509 0.970411 -vt 0.269613 0.960638 -vt 0.273555 0.958326 -vt 0.277844 0.956591 -vt 0.282427 0.955481 -vt 0.287249 0.955041 -vt 0.608137 0.826343 -vt 0.944125 0.515168 -vt 0.943431 0.529997 -vt 0.287513 0.964410 -vt 0.617417 0.827261 -vt 0.244939 0.976322 -vt 0.143210 0.942806 -vt 0.243935 0.983021 -vt 0.246888 0.970039 -vt 0.249700 0.964240 -vt 0.253296 0.958989 -vt 0.257601 0.954354 -vt 0.262538 0.950401 -vt 0.268035 0.947199 -vt 0.274019 0.944818 -vt 0.280415 0.943329 -vt 0.230827 0.973030 -vt 0.143266 0.928430 -vt 0.229515 0.982034 -vt 0.233415 0.964611 -vt 0.237161 0.956857 -vt 0.241957 0.949850 -vt 0.247697 0.943673 -vt 0.254284 0.938414 -vt 0.261622 0.934161 -vt 0.269618 0.931011 -vt 0.278177 0.929065 -vt 0.287202 0.928430 -vt 0.581505 0.824405 -vt 0.215497 0.969458 -vt 0.143401 0.912787 -vt 0.213795 0.980971 -vt 0.218819 0.958727 -vt 0.223603 0.948867 -vt 0.229709 0.939969 -vt 0.237009 0.932131 -vt 0.245380 0.925456 -vt 0.254709 0.920057 -vt 0.264886 0.916055 -vt 0.275799 0.913583 -vt 0.199907 0.965755 -vt 0.143528 0.896805 -vt 0.197735 0.979808 -vt 0.204018 0.952705 -vt 0.209875 0.940737 -vt 0.217315 0.929943 -vt 0.226190 0.920433 -vt 0.236359 0.912328 -vt 0.247691 0.905759 -vt 0.260064 0.900870 -vt 0.273360 0.897822 -vt 0.287465 0.896805 -vt 0.549725 0.822179 -vt 0.185022 0.962111 -vt 0.143576 0.881435 -vt 0.182322 0.978554 -vt 0.189920 0.946892 -vt 0.196808 0.932947 -vt 0.205515 0.920368 -vt 0.215880 0.909278 -vt 0.227747 0.899815 -vt 0.240970 0.892129 -vt 0.255412 0.886377 -vt 0.270959 0.882738 -vt 0.171772 0.958754 -vt 0.143509 0.867627 -vt 0.168528 0.977273 -vt 0.177384 0.941656 -vt 0.185184 0.925980 -vt 0.195009 0.911827 -vt 0.206691 0.899337 -vt 0.220060 0.888668 -vt 0.234950 0.879987 -vt 0.251210 0.873464 -vt 0.268724 0.869271 -vt 0.161000 0.955943 -vt 0.143337 0.856300 -vt 0.157263 0.976086 -vt 0.167182 0.937360 -vt 0.175710 0.920288 -vt 0.186436 0.904856 -vt 0.199186 0.891225 -vt 0.213774 0.879575 -vt 0.230015 0.870086 -vt 0.247740 0.862936 -vt 0.266819 0.858289 -vt 0.153317 0.953908 -vt 0.159885 0.934289 -vt 0.168921 0.916223 -vt 0.180287 0.899873 -vt 0.193798 0.885423 -vt 0.209257 0.873068 -vt 0.226464 0.862999 -vt 0.245230 0.855405 -vt 0.265405 0.850458 -vt 0.287043 0.848201 -vt 0.501204 0.817496 -vt 0.148538 0.952638 -vt 0.155319 0.932379 -vt 0.164668 0.913689 -vt 0.176435 0.896760 -vt 0.190427 0.881794 -vt 0.206435 0.868993 -vt 0.224250 0.858560 -vt 0.243670 0.850688 -vt 0.264517 0.845543 -vt 0.608912 0.821575 -vt 0.943889 0.533946 -vt 0.617998 0.823871 -vt 0.610342 0.817089 -vt 0.612377 0.812941 -vt 0.944213 0.541356 -vt 0.620479 0.817774 -vt 0.614967 0.809179 -vt 0.618059 0.805853 -vt 0.944305 0.548439 -vt 0.624499 0.812782 -vt 0.621603 0.803010 -vt 0.944334 0.551930 -vt 0.627000 0.810779 -vt 0.625545 0.800697 -vt 0.629833 0.798963 -vt 0.944356 0.558928 -vt 0.632815 0.807916 -vt 0.634416 0.797853 -vt 0.944301 0.562485 -vt 0.636063 0.807119 -vt 0.639239 0.797412 -vt 0.608137 0.968794 -vt 0.596928 0.818693 -vt 0.287147 0.942806 -vt 0.595924 0.825392 -vt 0.598877 0.812411 -vt 0.601689 0.806611 -vt 0.605286 0.801360 -vt 0.609590 0.796725 -vt 0.614528 0.792772 -vt 0.620025 0.789570 -vt 0.626008 0.787189 -vt 0.632404 0.785700 -vt 0.639136 0.785177 -vt 0.595924 0.967844 -vt 0.582816 0.815401 -vt 0.585404 0.806982 -vt 0.589151 0.799228 -vt 0.593946 0.792221 -vt 0.599687 0.786044 -vt 0.606274 0.780785 -vt 0.613612 0.776532 -vt 0.621607 0.773383 -vt 0.630167 0.771437 -vt 0.639192 0.770801 -vt 0.581505 0.966856 -vt 0.567486 0.811829 -vt 0.287337 0.912787 -vt 0.565784 0.823342 -vt 0.570809 0.801098 -vt 0.575593 0.791238 -vt 0.581699 0.782341 -vt 0.588998 0.774502 -vt 0.597369 0.767827 -vt 0.606699 0.762428 -vt 0.616875 0.758426 -vt 0.627789 0.755954 -vt 0.639327 0.755158 -vt 0.565784 0.965794 -vt 0.551897 0.808127 -vt 0.556008 0.795077 -vt 0.561865 0.783108 -vt 0.569305 0.772314 -vt 0.578179 0.762804 -vt 0.588348 0.754699 -vt 0.599681 0.748130 -vt 0.612053 0.743241 -vt 0.625350 0.740193 -vt 0.639454 0.739176 -vt 0.549725 0.964630 -vt 0.537011 0.804482 -vt 0.287513 0.881435 -vt 0.534311 0.820925 -vt 0.541909 0.789264 -vt 0.548797 0.775318 -vt 0.557504 0.762740 -vt 0.567869 0.751649 -vt 0.579737 0.742186 -vt 0.592959 0.734500 -vt 0.607402 0.728749 -vt 0.622948 0.725110 -vt 0.639503 0.723806 -vt 0.534311 0.963376 -vt 0.523762 0.801125 -vt 0.287446 0.867627 -vt 0.520517 0.819644 -vt 0.529374 0.784028 -vt 0.537173 0.768351 -vt 0.546998 0.754198 -vt 0.558680 0.741708 -vt 0.572049 0.731039 -vt 0.586939 0.722359 -vt 0.603199 0.715835 -vt 0.620714 0.711642 -vt 0.512989 0.798314 -vt 0.287274 0.856300 -vt 0.509252 0.818458 -vt 0.519172 0.779731 -vt 0.527699 0.762660 -vt 0.538426 0.747227 -vt 0.551175 0.733596 -vt 0.565763 0.721946 -vt 0.582004 0.712457 -vt 0.599729 0.705307 -vt 0.618808 0.700660 -vt 0.505306 0.796279 -vt 0.511875 0.776660 -vt 0.520910 0.758594 -vt 0.532276 0.742244 -vt 0.545787 0.727794 -vt 0.561246 0.715439 -vt 0.578453 0.705370 -vt 0.597220 0.697776 -vt 0.617395 0.692829 -vt 0.500528 0.795010 -vt 0.286721 0.843159 -vt 0.496286 0.816746 -vt 0.507308 0.774750 -vt 0.516657 0.756060 -vt 0.528425 0.739131 -vt 0.542416 0.724165 -vt 0.558425 0.711364 -vt 0.576240 0.700932 -vt 0.595660 0.693059 -vt 0.616507 0.687914 -vt 0.638710 0.685530 -vt 0.496286 0.959197 -vt 0.608912 0.964026 -vt 0.610342 0.959541 -vt 0.944107 0.588672 -vt 0.619029 0.963151 -vt 0.612377 0.955392 -vt 0.944213 0.592318 -vt 0.620479 0.960225 -vt 0.614967 0.951631 -vt 0.618059 0.948304 -vt 0.621603 0.945461 -vt 0.944334 0.602893 -vt 0.627000 0.953230 -vt 0.625545 0.943148 -vt 0.944356 0.606381 -vt 0.629783 0.951598 -vt 0.629833 0.941414 -vt 0.944356 0.609890 -vt 0.632815 0.950367 -vt 0.634416 0.940304 -vt 0.944301 0.613448 -vt 0.636063 0.949570 -vt 0.596928 0.961144 -vt 0.598877 0.954862 -vt 0.601689 0.949062 -vt 0.605286 0.943811 -vt 0.609590 0.939176 -vt 0.614528 0.935224 -vt 0.620025 0.932022 -vt 0.626008 0.929641 -vt 0.632404 0.928152 -vt 0.582816 0.957853 -vt 0.585404 0.949433 -vt 0.589151 0.941679 -vt 0.593946 0.934672 -vt 0.599687 0.928496 -vt 0.606274 0.923236 -vt 0.613612 0.918984 -vt 0.621607 0.915834 -vt 0.630167 0.913888 -vt 0.085579 0.982034 -vt 0.639192 0.913253 -vt 0.567486 0.954280 -vt 0.570809 0.943550 -vt 0.575593 0.933690 -vt 0.581699 0.924792 -vt 0.588998 0.916953 -vt 0.597369 0.910279 -vt 0.606699 0.904880 -vt 0.616875 0.900877 -vt 0.627789 0.898405 -vt 0.551897 0.950578 -vt 0.556008 0.937528 -vt 0.561865 0.925559 -vt 0.569305 0.914765 -vt 0.578179 0.905255 -vt 0.588348 0.897150 -vt 0.599681 0.890582 -vt 0.612053 0.885693 -vt 0.625350 0.882644 -vt 0.537011 0.946933 -vt 0.541909 0.931715 -vt 0.548797 0.917770 -vt 0.557504 0.905191 -vt 0.567869 0.894101 -vt 0.579737 0.884638 -vt 0.592959 0.876951 -vt 0.607402 0.871200 -vt 0.622948 0.867561 -vt 0.038385 0.978554 -vt 0.639503 0.866257 -vt 0.523762 0.943576 -vt 0.639435 0.709998 -vt 0.520517 0.962096 -vt 0.529374 0.926479 -vt 0.537173 0.910803 -vt 0.546998 0.896649 -vt 0.558680 0.884159 -vt 0.572049 0.873491 -vt 0.586939 0.864810 -vt 0.603199 0.858286 -vt 0.620714 0.854093 -vt 0.024591 0.977273 -vt 0.639435 0.852450 -vt 0.512989 0.940766 -vt 0.639263 0.698671 -vt 0.509252 0.960909 -vt 0.519172 0.922182 -vt 0.527699 0.905111 -vt 0.538426 0.889678 -vt 0.551175 0.876048 -vt 0.565763 0.864397 -vt 0.582004 0.854908 -vt 0.599729 0.847759 -vt 0.618808 0.843112 -vt 0.013326 0.976086 -vt 0.639263 0.841122 -vt 0.505306 0.938730 -vt 0.639032 0.690572 -vt 0.501204 0.959947 -vt 0.511875 0.919111 -vt 0.520910 0.901045 -vt 0.532276 0.884695 -vt 0.545787 0.870246 -vt 0.561246 0.857890 -vt 0.578453 0.847822 -vt 0.597220 0.840227 -vt 0.617395 0.835281 -vt 0.005278 0.975125 -vt 0.639032 0.833023 -vt 0.500528 0.937461 -vt 0.507308 0.917202 -vt 0.516657 0.898511 -vt 0.528425 0.881583 -vt 0.542416 0.866616 -vt 0.558424 0.853816 -vt 0.576240 0.843383 -vt 0.595660 0.835511 -vt 0.616507 0.830365 -vt 0.000360 0.974375 -vt 0.638710 0.827981 -vt 0.495566 0.999640 -vt 0.977907 0.895697 -vt 0.495566 0.835648 -vt 0.813644 0.895697 -vt 0.454450 0.999573 -vt 0.813644 0.854597 -vt 0.455022 0.992822 -vt 0.456594 0.986466 -vt 0.459080 0.980576 -vt 0.462399 0.975224 -vt 0.466471 0.970481 -vt 0.471223 0.966420 -vt 0.476580 0.963115 -vt 0.482471 0.960645 -vt 0.488824 0.959092 -vt 0.495566 0.958540 -vt 0.936792 0.895631 -vt 0.422140 0.987012 -vt 0.420937 0.999251 -vt 0.813431 0.821205 -vt 0.425117 0.975566 -vt 0.429692 0.965009 -vt 0.435712 0.955436 -vt 0.443043 0.946950 -vt 0.451563 0.939662 -vt 0.461159 0.933694 -vt 0.471726 0.929180 -vt 0.483159 0.926273 -vt 0.495353 0.925148 -vt 0.903279 0.895309 -vt 0.396236 0.981994 -vt 0.400481 0.966671 -vt 0.406769 0.952592 -vt 0.414909 0.939840 -vt 0.424741 0.928522 -vt 0.436123 0.918766 -vt 0.448928 0.910718 -vt 0.463042 0.904543 -vt 0.478371 0.900437 -vt 0.494826 0.898651 -vt 0.876617 0.894564 -vt 0.376558 0.977804 -vt 0.373800 0.997445 -vt 0.812133 0.774341 -vt 0.381857 0.959672 -vt 0.389459 0.943033 -vt 0.399184 0.927951 -vt 0.410869 0.914542 -vt 0.424363 0.902950 -vt 0.439524 0.893336 -vt 0.456238 0.885877 -vt 0.474425 0.880771 -vt 0.494055 0.878284 -vt 0.856141 0.893503 -vt 0.362222 0.974520 -vt 0.368303 0.954434 -vt 0.376846 0.935983 -vt 0.387704 0.919233 -vt 0.400720 0.904316 -vt 0.415730 0.891397 -vt 0.432580 0.880652 -vt 0.451140 0.872260 -vt 0.471338 0.866397 -vt 0.352272 0.972129 -vt 0.358877 0.950734 -vt 0.368056 0.931042 -vt 0.379692 0.913138 -vt 0.393626 0.897175 -vt 0.409685 0.883334 -vt 0.427701 0.871806 -vt 0.447526 0.862774 -vt 0.469076 0.856390 -vt 0.345743 0.970514 -vt 0.352676 0.948282 -vt 0.362264 0.927780 -vt 0.374407 0.909117 -vt 0.388942 0.892465 -vt 0.405691 0.878018 -vt 0.424472 0.865977 -vt 0.445123 0.856528 -vt 0.467538 0.849815 -vt 0.491878 0.845845 -vt 0.823757 0.890704 -vt 0.341662 0.969490 -vt 0.348791 0.946746 -vt 0.358633 0.925737 -vt 0.371091 0.906599 -vt 0.386003 0.889513 -vt 0.403184 0.874686 -vt 0.422445 0.862322 -vt 0.443611 0.852614 -vt 0.466558 0.845707 -vt 0.491462 0.841541 -vt 0.819473 0.890211 -vt 0.338980 0.968816 -vt 0.334337 0.993763 -vt 0.809197 0.734786 -vt 0.346230 0.945738 -vt 0.356237 0.924396 -vt 0.368903 0.904942 -vt 0.384065 0.887569 -vt 0.401533 0.872489 -vt 0.421111 0.859913 -vt 0.442617 0.850034 -vt 0.465911 0.843005 -vt 0.491119 0.838729 -vt 0.816678 0.889820 -vt 0.438343 0.391149 -vt 0.344063 0.944893 -vt 0.459884 0.379675 -vt 0.354208 0.923266 -vt 0.482031 0.370316 -vt 0.367052 0.903543 -vt 0.528025 0.357715 -vt 0.400139 0.870629 -vt 0.576158 0.353437 -vt 0.441784 0.847849 -vt 0.937363 0.888879 -vt 0.938935 0.882523 -vt 0.941422 0.876633 -vt 0.944740 0.871282 -vt 0.948813 0.866539 -vt 0.953564 0.862478 -vt 0.958921 0.859173 -vt 0.964812 0.856703 -vt 0.971166 0.855149 -vt 0.977907 0.854597 -vt 0.454450 0.835581 -vt 0.904481 0.883070 -vt 0.907458 0.871624 -vt 0.912033 0.861066 -vt 0.918054 0.851493 -vt 0.925385 0.843008 -vt 0.933905 0.835720 -vt 0.943501 0.829751 -vt 0.954067 0.825237 -vt 0.965501 0.822330 -vt 0.977694 0.821205 -vt 0.420937 0.835259 -vt 0.878577 0.878051 -vt 0.882823 0.862728 -vt 0.889110 0.848650 -vt 0.897250 0.835898 -vt 0.907082 0.824580 -vt 0.918465 0.814824 -vt 0.931269 0.806775 -vt 0.945383 0.800600 -vt 0.960712 0.796495 -vt 0.977167 0.794708 -vt 0.394276 0.834514 -vt 0.858899 0.873862 -vt 0.864198 0.855730 -vt 0.871800 0.839090 -vt 0.881525 0.824009 -vt 0.893211 0.810600 -vt 0.906704 0.799007 -vt 0.921866 0.789394 -vt 0.938579 0.781935 -vt 0.956767 0.776829 -vt 0.976396 0.774341 -vt 0.373800 0.833453 -vt 0.844564 0.870578 -vt 0.493211 0.863267 -vt 0.841105 0.892379 -vt 0.850644 0.850492 -vt 0.859187 0.832041 -vt 0.870046 0.815290 -vt 0.883061 0.800374 -vt 0.898071 0.787454 -vt 0.914921 0.776709 -vt 0.933481 0.768318 -vt 0.953679 0.762454 -vt 0.975553 0.759325 -vt 0.358763 0.832329 -vt 0.834614 0.868186 -vt 0.492458 0.852758 -vt 0.830629 0.891415 -vt 0.841218 0.846791 -vt 0.850398 0.827099 -vt 0.862033 0.809195 -vt 0.875967 0.793232 -vt 0.892026 0.779391 -vt 0.910042 0.767864 -vt 0.929867 0.758831 -vt 0.951417 0.752447 -vt 0.974799 0.748815 -vt 0.348288 0.831366 -vt 0.828085 0.866571 -vt 0.835017 0.844340 -vt 0.844606 0.823837 -vt 0.856748 0.805175 -vt 0.871284 0.788523 -vt 0.888033 0.774076 -vt 0.906814 0.762034 -vt 0.927464 0.752585 -vt 0.949879 0.745872 -vt 0.824003 0.865548 -vt 0.831132 0.842803 -vt 0.840974 0.821795 -vt 0.853432 0.802656 -vt 0.868344 0.785571 -vt 0.885525 0.770743 -vt 0.904786 0.758380 -vt 0.925953 0.748672 -vt 0.948899 0.741764 -vt 0.821321 0.864874 -vt 0.828571 0.841796 -vt 0.838578 0.820453 -vt 0.851245 0.800999 -vt 0.866406 0.783627 -vt 0.883874 0.768547 -vt 0.903452 0.755970 -vt 0.924959 0.746091 -vt 0.948253 0.739062 -vt 0.763213 0.391149 -vt 0.826404 0.840950 -vt 0.806901 0.370315 -vt 0.849393 0.799600 -vt 0.876698 0.354497 -vt 0.902330 0.753929 -vt 0.901028 0.353437 -vt 0.924126 0.743906 -vt 0.455022 0.828830 -vt 0.456594 0.822473 -vt 0.459080 0.816584 -vt 0.462399 0.811232 -vt 0.466471 0.806489 -vt 0.471223 0.802428 -vt 0.476580 0.799123 -vt 0.482471 0.796653 -vt 0.488824 0.795100 -vt 0.422140 0.823020 -vt 0.425117 0.811574 -vt 0.429692 0.801017 -vt 0.435712 0.791444 -vt 0.443043 0.782958 -vt 0.451563 0.775670 -vt 0.461159 0.769702 -vt 0.471726 0.765188 -vt 0.483159 0.762281 -vt 0.495353 0.761156 -vt 0.739016 0.895309 -vt 0.396236 0.818002 -vt 0.400481 0.802679 -vt 0.406769 0.788600 -vt 0.414909 0.775848 -vt 0.424741 0.764530 -vt 0.436123 0.754774 -vt 0.448928 0.746726 -vt 0.463042 0.740550 -vt 0.478371 0.736445 -vt 0.494826 0.734659 -vt 0.712354 0.894564 -vt 0.376558 0.813812 -vt 0.381857 0.795680 -vt 0.389459 0.779041 -vt 0.399184 0.763959 -vt 0.410869 0.750550 -vt 0.424363 0.738958 -vt 0.439524 0.729344 -vt 0.456238 0.721885 -vt 0.474426 0.716779 -vt 0.494055 0.714292 -vt 0.691878 0.893503 -vt 0.362222 0.810528 -vt 0.368303 0.790442 -vt 0.376846 0.771991 -vt 0.387704 0.755241 -vt 0.400720 0.740324 -vt 0.415730 0.727405 -vt 0.432580 0.716660 -vt 0.451140 0.708268 -vt 0.471338 0.702404 -vt 0.493211 0.699275 -vt 0.676841 0.892379 -vt 0.352273 0.808137 -vt 0.358877 0.786742 -vt 0.368056 0.767050 -vt 0.379692 0.749145 -vt 0.393626 0.733182 -vt 0.409685 0.719342 -vt 0.427701 0.707814 -vt 0.447526 0.698782 -vt 0.469076 0.692398 -vt 0.492458 0.688766 -vt 0.666366 0.891415 -vt 0.345743 0.806522 -vt 0.974220 0.741903 -vt 0.341416 0.830655 -vt 0.352676 0.784290 -vt 0.362264 0.763788 -vt 0.374407 0.745125 -vt 0.388942 0.728473 -vt 0.405691 0.714026 -vt 0.424472 0.701985 -vt 0.445123 0.692536 -vt 0.467538 0.685823 -vt 0.341662 0.805498 -vt 0.973803 0.737598 -vt 0.337131 0.830161 -vt 0.348791 0.782754 -vt 0.358633 0.761745 -vt 0.371091 0.742607 -vt 0.386003 0.725521 -vt 0.403184 0.710694 -vt 0.422445 0.698330 -vt 0.443611 0.688622 -vt 0.466558 0.681715 -vt 0.491462 0.677548 -vt 0.655210 0.890211 -vt 0.338980 0.804824 -vt 0.973460 0.734786 -vt 0.334337 0.829771 -vt 0.346230 0.781746 -vt 0.356237 0.760404 -vt 0.368903 0.740950 -vt 0.384065 0.723577 -vt 0.401533 0.708497 -vt 0.421111 0.695920 -vt 0.442617 0.686042 -vt 0.465911 0.679013 -vt 0.276163 0.354700 -vt 0.465367 0.676718 -vt 0.495566 0.794548 -vt 0.772529 0.895631 -vt 0.773100 0.888879 -vt 0.774672 0.882523 -vt 0.777159 0.876633 -vt 0.780477 0.871282 -vt 0.784550 0.866539 -vt 0.789301 0.862478 -vt 0.794658 0.859173 -vt 0.800549 0.856703 -vt 0.806903 0.855149 -vt 0.740218 0.883070 -vt 0.743195 0.871624 -vt 0.747770 0.861066 -vt 0.753790 0.851493 -vt 0.761122 0.843008 -vt 0.769642 0.835720 -vt 0.779238 0.829751 -vt 0.789804 0.825237 -vt 0.801238 0.822330 -vt 0.714314 0.878051 -vt 0.718560 0.862728 -vt 0.724847 0.848650 -vt 0.732987 0.835898 -vt 0.742819 0.824580 -vt 0.754202 0.814824 -vt 0.767006 0.806775 -vt 0.781120 0.800600 -vt 0.796449 0.796495 -vt 0.394276 0.998506 -vt 0.812904 0.794709 -vt 0.694636 0.873862 -vt 0.699935 0.855730 -vt 0.707537 0.839090 -vt 0.717262 0.824009 -vt 0.728948 0.810600 -vt 0.742441 0.799007 -vt 0.757602 0.789394 -vt 0.774316 0.781935 -vt 0.792504 0.776829 -vt 0.680301 0.870578 -vt 0.686381 0.850492 -vt 0.694924 0.832041 -vt 0.705783 0.815290 -vt 0.718798 0.800374 -vt 0.733808 0.787454 -vt 0.750658 0.776709 -vt 0.769218 0.768318 -vt 0.789416 0.762454 -vt 0.358763 0.996321 -vt 0.811290 0.759325 -vt 0.670351 0.868186 -vt 0.676955 0.846791 -vt 0.686134 0.827099 -vt 0.697770 0.809195 -vt 0.711704 0.793232 -vt 0.727763 0.779391 -vt 0.745779 0.767864 -vt 0.765604 0.758831 -vt 0.787154 0.752447 -vt 0.348288 0.995358 -vt 0.810536 0.748815 -vt 0.663822 0.866571 -vt 0.491878 0.681853 -vt 0.659494 0.890704 -vt 0.670754 0.844340 -vt 0.680343 0.823837 -vt 0.692485 0.805175 -vt 0.707020 0.788522 -vt 0.723770 0.774076 -vt 0.742550 0.762034 -vt 0.763201 0.752585 -vt 0.785616 0.745872 -vt 0.341416 0.994647 -vt 0.809957 0.741903 -vt 0.659740 0.865548 -vt 0.666869 0.842803 -vt 0.676711 0.821795 -vt 0.689169 0.802656 -vt 0.704081 0.785571 -vt 0.721262 0.770743 -vt 0.740523 0.758380 -vt 0.761689 0.748672 -vt 0.784636 0.741764 -vt 0.337131 0.994153 -vt 0.809540 0.737598 -vt 0.657058 0.864874 -vt 0.491119 0.674737 -vt 0.652415 0.889820 -vt 0.664308 0.841796 -vt 0.674315 0.820453 -vt 0.686981 0.800999 -vt 0.702143 0.783627 -vt 0.719611 0.768547 -vt 0.739189 0.755970 -vt 0.760696 0.746091 -vt 0.783990 0.739062 -vt 0.092581 0.826282 -vt 0.654797 0.864309 -vt 0.113472 0.812549 -vt 0.662141 0.840950 -vt 0.135013 0.801074 -vt 0.672287 0.819324 -vt 0.203155 0.779115 -vt 0.718217 0.766687 -vt 0.226957 0.775896 -vt 0.738066 0.753929 -vt 0.276163 0.776099 -vt 0.783445 0.736767 -s 1 -f 1/1/1 2/2/2 3/3/3 -f 3/3/3 4/5/4 1/1/1 -f 5/7/5 6/8/6 2/2/2 -f 2/2/2 1/1/1 5/7/5 -f 7/9/7 8/10/8 6/8/6 -f 6/8/6 5/7/5 7/9/7 -f 9/11/9 10/12/10 8/10/8 -f 8/10/8 7/9/7 9/11/9 -f 11/13/11 12/14/12 10/12/10 -f 10/12/10 9/11/9 11/13/11 -f 13/15/13 14/16/14 12/14/12 -f 12/14/12 11/13/11 13/15/13 -f 15/17/15 16/18/16 14/16/14 -f 14/16/14 13/15/13 15/17/15 -f 17/19/17 18/20/18 16/18/16 -f 16/18/16 15/17/15 17/19/17 -f 19/21/19 20/22/20 18/20/18 -f 18/20/18 17/19/17 19/21/19 -f 114/126/21 21/23/22 20/22/20 -f 20/22/20 19/21/19 114/126/21 -f 22/25/23 1/1/1 4/5/4 -f 4/5/4 23/26/24 22/25/23 -f 24/28/25 5/7/5 1/1/1 -f 1/1/1 22/25/23 24/28/25 -f 25/29/26 7/9/7 5/7/5 -f 5/7/5 24/28/25 25/29/26 -f 26/30/27 9/11/9 7/9/7 -f 7/9/7 25/29/26 26/30/27 -f 27/31/28 11/13/11 9/11/9 -f 9/11/9 26/30/27 27/31/28 -f 28/32/29 13/15/13 11/13/11 -f 11/13/11 27/31/28 28/32/29 -f 29/33/30 15/17/15 13/15/13 -f 13/15/13 28/32/29 29/33/30 -f 30/34/31 17/19/17 15/17/15 -f 15/17/15 29/33/30 30/34/31 -f 31/35/32 19/21/19 17/19/17 -f 17/19/17 30/34/31 31/35/32 -f 32/36/33 114/126/21 19/21/19 -f 19/21/19 31/35/32 32/36/33 -f 33/38/34 22/25/23 23/26/24 -f 23/26/24 371/408/35 33/38/34 -f 34/39/36 24/28/25 22/25/23 -f 22/25/23 33/38/34 34/39/36 -f 35/40/37 25/29/26 24/28/25 -f 24/28/25 34/39/36 35/40/37 -f 36/41/38 26/30/27 25/29/26 -f 25/29/26 35/40/37 36/41/38 -f 37/42/39 27/31/28 26/30/27 -f 26/30/27 36/41/38 37/42/39 -f 38/43/40 28/32/29 27/31/28 -f 27/31/28 37/42/39 38/43/40 -f 39/44/41 29/33/30 28/32/29 -f 28/32/29 38/43/40 39/44/41 -f 40/45/42 30/34/31 29/33/30 -f 29/33/30 39/44/41 40/45/42 -f 41/46/43 31/35/32 30/34/31 -f 30/34/31 40/45/42 41/46/43 -f 142/156/44 32/36/33 31/35/32 -f 31/35/32 41/46/43 142/156/44 -f 42/47/45 33/38/34 371/408/35 -f 371/408/35 382/421/46 42/47/45 -f 43/48/47 34/39/36 33/38/34 -f 33/38/34 42/47/45 43/48/47 -f 44/49/48 35/40/37 34/39/36 -f 34/39/36 43/48/47 44/49/48 -f 45/50/49 36/41/38 35/40/37 -f 35/40/37 44/49/48 45/50/49 -f 46/51/50 37/42/39 36/41/38 -f 36/41/38 45/50/49 46/51/50 -f 47/52/51 38/43/40 37/42/39 -f 37/42/39 46/51/50 47/52/51 -f 48/53/52 39/44/41 38/43/40 -f 38/43/40 47/52/51 48/53/52 -f 49/54/53 40/45/42 39/44/41 -f 39/44/41 48/53/52 49/54/53 -f 50/55/54 41/46/43 40/45/42 -f 40/45/42 49/54/53 50/55/54 -f 51/56/55 142/156/44 41/46/43 -f 41/46/43 50/55/54 51/56/55 -f 52/58/56 42/47/45 382/421/46 -f 382/421/46 392/432/57 52/58/56 -f 53/59/58 43/48/47 42/47/45 -f 42/47/45 52/58/56 53/59/58 -f 54/60/59 44/49/48 43/48/47 -f 43/48/47 53/59/58 54/60/59 -f 55/61/60 45/50/49 44/49/48 -f 44/49/48 54/60/59 55/61/60 -f 56/62/61 46/51/50 45/50/49 -f 45/50/49 55/61/60 56/62/61 -f 57/63/62 47/52/51 46/51/50 -f 46/51/50 56/62/61 57/63/62 -f 58/64/63 48/53/52 47/52/51 -f 47/52/51 57/63/62 58/64/63 -f 59/65/64 49/54/53 48/53/52 -f 48/53/52 58/64/63 59/65/64 -f 60/66/65 50/55/54 49/54/53 -f 49/54/53 59/65/64 60/66/65 -f 61/67/66 51/56/55 50/55/54 -f 50/55/54 60/66/65 61/67/66 -f 62/69/67 52/58/56 392/432/57 -f 392/432/57 402/443/68 62/69/67 -f 63/70/69 53/59/58 52/58/56 -f 52/58/56 62/69/67 63/70/69 -f 64/71/70 54/60/59 53/59/58 -f 53/59/58 63/70/69 64/71/70 -f 65/72/71 55/61/60 54/60/59 -f 54/60/59 64/71/70 65/72/71 -f 66/73/72 56/62/61 55/61/60 -f 55/61/60 65/72/71 66/73/72 -f 67/74/73 57/63/62 56/62/61 -f 56/62/61 66/73/72 67/74/73 -f 68/75/74 58/64/63 57/63/62 -f 57/63/62 67/74/73 68/75/74 -f 69/76/75 59/65/64 58/64/63 -f 58/64/63 68/75/74 69/76/75 -f 70/77/76 60/66/65 59/65/64 -f 59/65/64 69/76/75 70/77/76 -f 172/189/77 61/67/66 60/66/65 -f 60/66/65 70/77/76 172/189/77 -f 71/78/78 62/69/67 402/443/68 -f 402/443/68 72/79/79 71/78/78 -f 73/81/80 63/70/69 62/69/67 -f 62/69/67 71/78/78 73/81/80 -f 74/82/81 64/71/70 63/70/69 -f 63/70/69 73/81/80 74/82/81 -f 75/83/82 65/72/71 64/71/70 -f 64/71/70 74/82/81 75/83/82 -f 76/84/83 66/73/72 65/72/71 -f 65/72/71 75/83/82 76/84/83 -f 77/85/84 67/74/73 66/73/72 -f 66/73/72 76/84/83 77/85/84 -f 78/86/85 68/75/74 67/74/73 -f 67/74/73 77/85/84 78/86/85 -f 79/87/86 69/76/75 68/75/74 -f 68/75/74 78/86/85 79/87/86 -f 80/88/87 70/77/76 69/76/75 -f 69/76/75 79/87/86 80/88/87 -f 81/89/88 172/189/77 70/77/76 -f 70/77/76 80/88/87 81/89/88 -f 82/91/89 71/78/78 72/79/79 -f 72/79/79 421/463/90 82/91/89 -f 83/92/91 73/81/80 71/78/78 -f 71/78/78 82/91/89 83/92/91 -f 84/93/92 74/82/81 73/81/80 -f 73/81/80 83/92/91 84/93/92 -f 85/94/93 75/83/82 74/82/81 -f 74/82/81 84/93/92 85/94/93 -f 86/95/94 76/84/83 75/83/82 -f 75/83/82 85/94/93 86/95/94 -f 87/96/95 77/85/84 76/84/83 -f 76/84/83 86/95/94 87/96/95 -f 88/97/96 78/86/85 77/85/84 -f 77/85/84 87/96/95 88/97/96 -f 89/98/97 79/87/86 78/86/85 -f 78/86/85 88/97/96 89/98/97 -f 90/99/98 80/88/87 79/87/86 -f 79/87/86 89/98/97 90/99/98 -f 91/100/99 81/89/88 80/88/87 -f 80/88/87 90/99/98 91/100/99 -f 92/102/100 82/91/89 421/463/90 -f 421/463/90 93/103/101 92/102/100 -f 94/105/102 83/92/91 82/91/89 -f 82/91/89 92/102/100 94/105/102 -f 95/106/103 84/93/92 83/92/91 -f 83/92/91 94/105/102 95/106/103 -f 96/107/104 85/94/93 84/93/92 -f 84/93/92 95/106/103 96/107/104 -f 97/108/105 86/95/94 85/94/93 -f 85/94/93 96/107/104 97/108/105 -f 98/109/106 87/96/95 86/95/94 -f 86/95/94 97/108/105 98/109/106 -f 99/110/107 88/97/96 87/96/95 -f 87/96/95 98/109/106 99/110/107 -f 100/111/108 89/98/97 88/97/96 -f 88/97/96 99/110/107 100/111/108 -f 101/112/109 90/99/98 89/98/97 -f 89/98/97 100/111/108 101/112/109 -f 102/113/110 91/100/99 90/99/98 -f 90/99/98 101/112/109 102/113/110 -f 103/115/111 92/102/100 93/103/101 -f 93/103/101 440/483/112 103/115/111 -f 104/116/113 94/105/102 92/102/100 -f 92/102/100 103/115/111 104/116/113 -f 105/117/114 95/106/103 94/105/102 -f 94/105/102 104/116/113 105/117/114 -f 106/118/115 96/107/104 95/106/103 -f 95/106/103 105/117/114 106/118/115 -f 107/119/116 97/108/105 96/107/104 -f 96/107/104 106/118/115 107/119/116 -f 108/120/117 98/109/106 97/108/105 -f 97/108/105 107/119/116 108/120/117 -f 109/121/118 99/110/107 98/109/106 -f 98/109/106 108/120/117 109/121/118 -f 110/122/119 100/111/108 99/110/107 -f 99/110/107 109/121/118 110/122/119 -f 111/123/120 101/112/109 100/111/108 -f 100/111/108 110/122/119 111/123/120 -f 210/229/121 102/113/110 101/112/109 -f 101/112/109 111/123/120 210/229/121 -f 112/124/122 113/125/123 21/24/22 -f 21/24/22 114/127/21 112/124/122 -f 115/128/124 116/129/125 113/125/123 -f 113/125/123 112/124/122 115/128/124 -f 117/130/126 118/131/127 116/129/125 -f 116/129/125 115/128/124 117/130/126 -f 119/132/128 120/133/129 118/131/127 -f 118/131/127 117/130/126 119/132/128 -f 121/134/130 122/135/131 120/133/129 -f 120/133/129 119/132/128 121/134/130 -f 123/136/132 124/137/133 122/135/131 -f 122/135/131 121/134/130 123/136/132 -f 125/138/134 126/139/135 124/137/133 -f 124/137/133 123/136/132 125/138/134 -f 127/140/136 128/141/137 126/139/135 -f 126/139/135 125/138/134 127/140/136 -f 129/142/138 130/143/139 128/141/137 -f 128/141/137 127/140/136 129/142/138 -f 131/144/140 221/241/141 130/143/139 -f 130/143/139 129/142/138 131/144/140 -f 132/146/142 112/124/122 114/127/21 -f 114/127/21 32/37/33 132/146/142 -f 133/147/143 115/128/124 112/124/122 -f 112/124/122 132/146/142 133/147/143 -f 134/148/144 117/130/126 115/128/124 -f 115/128/124 133/147/143 134/148/144 -f 135/149/145 119/132/128 117/130/126 -f 117/130/126 134/148/144 135/149/145 -f 136/150/146 121/134/130 119/132/128 -f 119/132/128 135/149/145 136/150/146 -f 137/151/147 123/136/132 121/134/130 -f 121/134/130 136/150/146 137/151/147 -f 138/152/148 125/138/134 123/136/132 -f 123/136/132 137/151/147 138/152/148 -f 139/153/149 127/140/136 125/138/134 -f 125/138/134 138/152/148 139/153/149 -f 140/154/150 129/142/138 127/140/136 -f 127/140/136 139/153/149 140/154/150 -f 240/262/151 131/144/140 129/142/138 -f 129/142/138 140/154/150 240/262/151 -f 141/155/152 132/146/142 32/37/33 -f 32/37/33 142/157/44 141/155/152 -f 143/158/153 133/147/143 132/146/142 -f 132/146/142 141/155/152 143/158/153 -f 144/159/154 134/148/144 133/147/143 -f 133/147/143 143/158/153 144/159/154 -f 145/160/155 135/149/145 134/148/144 -f 134/148/144 144/159/154 145/160/155 -f 146/161/156 136/150/146 135/149/145 -f 135/149/145 145/160/155 146/161/156 -f 147/162/157 137/151/147 136/150/146 -f 136/150/146 146/161/156 147/162/157 -f 148/163/158 138/152/148 137/151/147 -f 137/151/147 147/162/157 148/163/158 -f 149/164/159 139/153/149 138/152/148 -f 138/152/148 148/163/158 149/164/159 -f 150/165/160 140/154/150 139/153/149 -f 139/153/149 149/164/159 150/165/160 -f 151/166/161 240/262/151 140/154/150 -f 140/154/150 150/165/160 151/166/161 -f 152/168/162 141/155/152 142/157/44 -f 142/157/44 51/57/55 152/168/162 -f 153/169/163 143/158/153 141/155/152 -f 141/155/152 152/168/162 153/169/163 -f 154/170/164 144/159/154 143/158/153 -f 143/158/153 153/169/163 154/170/164 -f 155/171/165 145/160/155 144/159/154 -f 144/159/154 154/170/164 155/171/165 -f 156/172/166 146/161/156 145/160/155 -f 145/160/155 155/171/165 156/172/166 -f 157/173/167 147/162/157 146/161/156 -f 146/161/156 156/172/166 157/173/167 -f 158/174/168 148/163/158 147/162/157 -f 147/162/157 157/173/167 158/174/168 -f 159/175/169 149/164/159 148/163/158 -f 148/163/158 158/174/168 159/175/169 -f 160/176/170 150/165/160 149/164/159 -f 149/164/159 159/175/169 160/176/170 -f 260/284/171 151/166/161 150/165/160 -f 150/165/160 160/176/170 260/284/171 -f 161/177/172 152/168/162 51/57/55 -f 51/57/55 61/68/66 161/177/172 -f 162/178/173 153/169/163 152/168/162 -f 152/168/162 161/177/172 162/178/173 -f 163/179/174 154/170/164 153/169/163 -f 153/169/163 162/178/173 163/179/174 -f 164/180/175 155/171/165 154/170/164 -f 154/170/164 163/179/174 164/180/175 -f 165/181/176 156/172/166 155/171/165 -f 155/171/165 164/180/175 165/181/176 -f 166/182/177 157/173/167 156/172/166 -f 156/172/166 165/181/176 166/182/177 -f 167/183/178 158/174/168 157/173/167 -f 157/173/167 166/182/177 167/183/178 -f 168/184/179 159/175/169 158/174/168 -f 158/174/168 167/183/178 168/184/179 -f 169/185/180 160/176/170 159/175/169 -f 159/175/169 168/184/179 169/185/180 -f 170/186/181 260/284/171 160/176/170 -f 160/176/170 169/185/180 170/186/181 -f 171/188/182 161/177/172 61/68/66 -f 61/68/66 172/190/77 171/188/182 -f 173/191/183 162/178/173 161/177/172 -f 161/177/172 171/188/182 173/191/183 -f 174/192/184 163/179/174 162/178/173 -f 162/178/173 173/191/183 174/192/184 -f 175/193/185 164/180/175 163/179/174 -f 163/179/174 174/192/184 175/193/185 -f 176/194/186 165/181/176 164/180/175 -f 164/180/175 175/193/185 176/194/186 -f 177/195/187 166/182/177 165/181/176 -f 165/181/176 176/194/186 177/195/187 -f 178/196/188 167/183/178 166/182/177 -f 166/182/177 177/195/187 178/196/188 -f 179/197/189 168/184/179 167/183/178 -f 167/183/178 178/196/188 179/197/189 -f 180/198/190 169/185/180 168/184/179 -f 168/184/179 179/197/189 180/198/190 -f 280/306/191 170/186/181 169/185/180 -f 169/185/180 180/198/190 280/306/191 -f 181/199/192 171/188/182 172/190/77 -f 172/190/77 81/90/88 181/199/192 -f 182/200/193 173/191/183 171/188/182 -f 171/188/182 181/199/192 182/200/193 -f 183/201/194 174/192/184 173/191/183 -f 173/191/183 182/200/193 183/201/194 -f 184/202/195 175/193/185 174/192/184 -f 174/192/184 183/201/194 184/202/195 -f 185/203/196 176/194/186 175/193/185 -f 175/193/185 184/202/195 185/203/196 -f 186/204/197 177/195/187 176/194/186 -f 176/194/186 185/203/196 186/204/197 -f 187/205/198 178/196/188 177/195/187 -f 177/195/187 186/204/197 187/205/198 -f 188/206/199 179/197/189 178/196/188 -f 178/196/188 187/205/198 188/206/199 -f 189/207/200 180/198/190 179/197/189 -f 179/197/189 188/206/199 189/207/200 -f 190/208/201 280/306/191 180/198/190 -f 180/198/190 189/207/200 190/208/201 -f 191/210/202 181/199/192 81/90/88 -f 81/90/88 91/101/99 191/210/202 -f 192/211/203 182/200/193 181/199/192 -f 181/199/192 191/210/202 192/211/203 -f 193/212/204 183/201/194 182/200/193 -f 182/200/193 192/211/203 193/212/204 -f 194/213/205 184/202/195 183/201/194 -f 183/201/194 193/212/204 194/213/205 -f 195/214/206 185/203/196 184/202/195 -f 184/202/195 194/213/205 195/214/206 -f 196/215/207 186/204/197 185/203/196 -f 185/203/196 195/214/206 196/215/207 -f 197/216/208 187/205/198 186/204/197 -f 186/204/197 196/215/207 197/216/208 -f 198/217/209 188/206/199 187/205/198 -f 187/205/198 197/216/208 198/217/209 -f 199/218/210 189/207/200 188/206/199 -f 188/206/199 198/217/209 199/218/210 -f 301/330/211 190/208/201 189/207/200 -f 189/207/200 199/218/210 301/330/211 -f 200/219/212 191/210/202 91/101/99 -f 91/101/99 102/114/110 200/219/212 -f 201/220/213 192/211/203 191/210/202 -f 191/210/202 200/219/212 201/220/213 -f 202/221/214 193/212/204 192/211/203 -f 192/211/203 201/220/213 202/221/214 -f 203/222/215 194/213/205 193/212/204 -f 193/212/204 202/221/214 203/222/215 -f 204/223/216 195/214/206 194/213/205 -f 194/213/205 203/222/215 204/223/216 -f 205/224/217 196/215/207 195/214/206 -f 195/214/206 204/223/216 205/224/217 -f 206/225/218 197/216/208 196/215/207 -f 196/215/207 205/224/217 206/225/218 -f 207/226/219 198/217/209 197/216/208 -f 197/216/208 206/225/218 207/226/219 -f 208/227/220 199/218/210 198/217/209 -f 198/217/209 207/226/219 208/227/220 -f 312/343/221 301/330/211 199/218/210 -f 199/218/210 208/227/220 312/343/221 -f 209/228/222 200/219/212 102/114/110 -f 102/114/110 210/230/121 209/228/222 -f 211/231/223 201/220/213 200/219/212 -f 200/219/212 209/228/222 211/231/223 -f 212/232/224 202/221/214 201/220/213 -f 201/220/213 211/231/223 212/232/224 -f 213/233/225 203/222/215 202/221/214 -f 202/221/214 212/232/224 213/233/225 -f 214/234/226 204/223/216 203/222/215 -f 203/222/215 213/233/225 214/234/226 -f 215/235/227 205/224/217 204/223/216 -f 204/223/216 214/234/226 215/235/227 -f 216/236/228 206/225/218 205/224/217 -f 205/224/217 215/235/227 216/236/228 -f 217/237/229 207/226/219 206/225/218 -f 206/225/218 216/236/228 217/237/229 -f 218/238/230 208/227/220 207/226/219 -f 207/226/219 217/237/229 218/238/230 -f 323/356/231 312/343/221 208/227/220 -f 208/227/220 218/238/230 323/356/231 -f 219/239/232 220/240/233 221/242/141 -f 221/242/141 131/145/140 219/239/232 -f 222/243/234 223/244/235 220/240/233 -f 220/240/233 219/239/232 222/243/234 -f 224/245/236 225/246/237 223/244/235 -f 223/244/235 222/243/234 224/245/236 -f 226/247/238 227/248/239 225/246/237 -f 225/246/237 224/245/236 226/247/238 -f 228/249/240 229/250/241 227/248/239 -f 227/248/239 226/247/238 228/249/240 -f 230/251/242 231/252/243 229/250/241 -f 229/250/241 228/249/240 230/251/242 -f 232/253/244 233/254/245 231/252/243 -f 231/252/243 230/251/242 232/253/244 -f 234/255/246 235/256/247 233/254/245 -f 233/254/245 232/253/244 234/255/246 -f 236/257/248 237/258/249 235/256/247 -f 235/256/247 234/255/246 236/257/248 -f 238/259/250 335/370/251 237/258/249 -f 237/258/249 236/257/248 238/259/250 -f 239/261/252 219/239/232 131/145/140 -f 131/145/140 240/263/151 239/261/252 -f 241/264/253 222/243/234 219/239/232 -f 219/239/232 239/261/252 241/264/253 -f 242/265/254 224/245/236 222/243/234 -f 222/243/234 241/264/253 242/265/254 -f 243/266/255 226/247/238 224/245/236 -f 224/245/236 242/265/254 243/266/255 -f 244/267/256 228/249/240 226/247/238 -f 226/247/238 243/266/255 244/267/256 -f 245/268/257 230/251/242 228/249/240 -f 228/249/240 244/267/256 245/268/257 -f 246/269/258 232/253/244 230/251/242 -f 230/251/242 245/268/257 246/269/258 -f 247/270/259 234/255/246 232/253/244 -f 232/253/244 246/269/258 247/270/259 -f 248/271/260 236/257/248 234/255/246 -f 234/255/246 247/270/259 248/271/260 -f 249/272/261 238/259/250 236/257/248 -f 236/257/248 248/271/260 249/272/261 -f 250/274/262 239/261/252 240/263/151 -f 240/263/151 151/167/161 250/274/262 -f 251/275/263 241/264/253 239/261/252 -f 239/261/252 250/274/262 251/275/263 -f 252/276/264 242/265/254 241/264/253 -f 241/264/253 251/275/263 252/276/264 -f 253/277/265 243/266/255 242/265/254 -f 242/265/254 252/276/264 253/277/265 -f 254/278/266 244/267/256 243/266/255 -f 243/266/255 253/277/265 254/278/266 -f 255/279/267 245/268/257 244/267/256 -f 244/267/256 254/278/266 255/279/267 -f 256/280/268 246/269/258 245/268/257 -f 245/268/257 255/279/267 256/280/268 -f 257/281/269 247/270/259 246/269/258 -f 246/269/258 256/280/268 257/281/269 -f 258/282/270 248/271/260 247/270/259 -f 247/270/259 257/281/269 258/282/270 -f 362/398/271 249/272/261 248/271/260 -f 248/271/260 258/282/270 362/398/271 -f 259/283/272 250/274/262 151/167/161 -f 151/167/161 260/285/171 259/283/272 -f 261/286/273 251/275/263 250/274/262 -f 250/274/262 259/283/272 261/286/273 -f 262/287/274 252/276/264 251/275/263 -f 251/275/263 261/286/273 262/287/274 -f 263/288/275 253/277/265 252/276/264 -f 252/276/264 262/287/274 263/288/275 -f 264/289/276 254/278/266 253/277/265 -f 253/277/265 263/288/275 264/289/276 -f 265/290/277 255/279/267 254/278/266 -f 254/278/266 264/289/276 265/290/277 -f 266/291/278 256/280/268 255/279/267 -f 255/279/267 265/290/277 266/291/278 -f 267/292/279 257/281/269 256/280/268 -f 256/280/268 266/291/278 267/292/279 -f 268/293/280 258/282/270 257/281/269 -f 257/281/269 267/292/279 268/293/280 -f 373/411/281 362/398/271 258/282/270 -f 258/282/270 268/293/280 373/411/281 -f 269/294/282 259/283/272 260/285/171 -f 260/285/171 170/187/181 269/294/282 -f 270/295/283 261/286/273 259/283/272 -f 259/283/272 269/294/282 270/295/283 -f 271/296/284 262/287/274 261/286/273 -f 261/286/273 270/295/283 271/296/284 -f 272/297/285 263/288/275 262/287/274 -f 262/287/274 271/296/284 272/297/285 -f 273/298/286 264/289/276 263/288/275 -f 263/288/275 272/297/285 273/298/286 -f 274/299/287 265/290/277 264/289/276 -f 264/289/276 273/298/286 274/299/287 -f 275/300/288 266/291/278 265/290/277 -f 265/290/277 274/299/287 275/300/288 -f 276/301/289 267/292/279 266/291/278 -f 266/291/278 275/300/288 276/301/289 -f 277/302/290 268/293/280 267/292/279 -f 267/292/279 276/301/289 277/302/290 -f 278/303/291 373/411/281 268/293/280 -f 268/293/280 277/302/290 278/303/291 -f 279/305/292 269/294/282 170/187/181 -f 170/187/181 280/307/191 279/305/292 -f 281/308/293 270/295/283 269/294/282 -f 269/294/282 279/305/292 281/308/293 -f 282/309/294 271/296/284 270/295/283 -f 270/295/283 281/308/293 282/309/294 -f 283/310/295 272/297/285 271/296/284 -f 271/296/284 282/309/294 283/310/295 -f 284/311/296 273/298/286 272/297/285 -f 272/297/285 283/310/295 284/311/296 -f 285/312/297 274/299/287 273/298/286 -f 273/298/286 284/311/296 285/312/297 -f 286/313/298 275/300/288 274/299/287 -f 274/299/287 285/312/297 286/313/298 -f 287/314/299 276/301/289 275/300/288 -f 275/300/288 286/313/298 287/314/299 -f 288/315/300 277/302/290 276/301/289 -f 276/301/289 287/314/299 288/315/300 -f 289/316/301 278/303/291 277/302/290 -f 277/302/290 288/315/300 289/316/301 -f 290/318/302 279/305/292 280/307/191 -f 280/307/191 190/209/201 290/318/302 -f 291/319/303 281/308/293 279/305/292 -f 279/305/292 290/318/302 291/319/303 -f 292/320/304 282/309/294 281/308/293 -f 281/308/293 291/319/303 292/320/304 -f 293/321/305 283/310/295 282/309/294 -f 282/309/294 292/320/304 293/321/305 -f 294/322/306 284/311/296 283/310/295 -f 283/310/295 293/321/305 294/322/306 -f 295/323/307 285/312/297 284/311/296 -f 284/311/296 294/322/306 295/323/307 -f 296/324/308 286/313/298 285/312/297 -f 285/312/297 295/323/307 296/324/308 -f 297/325/309 287/314/299 286/313/298 -f 286/313/298 296/324/308 297/325/309 -f 298/326/310 288/315/300 287/314/299 -f 287/314/299 297/325/309 298/326/310 -f 299/327/311 289/316/301 288/315/300 -f 288/315/300 298/326/310 299/327/311 -f 300/329/312 290/318/302 190/209/201 -f 190/209/201 301/331/211 300/329/312 -f 302/332/313 291/319/303 290/318/302 -f 290/318/302 300/329/312 302/332/313 -f 303/333/314 292/320/304 291/319/303 -f 291/319/303 302/332/313 303/333/314 -f 304/334/315 293/321/305 292/320/304 -f 292/320/304 303/333/314 304/334/315 -f 305/335/316 294/322/306 293/321/305 -f 293/321/305 304/334/315 305/335/316 -f 306/336/317 295/323/307 294/322/306 -f 294/322/306 305/335/316 306/336/317 -f 307/337/318 296/324/308 295/323/307 -f 295/323/307 306/336/317 307/337/318 -f 308/338/319 297/325/309 296/324/308 -f 296/324/308 307/337/318 308/338/319 -f 309/339/320 298/326/310 297/325/309 -f 297/325/309 308/338/319 309/339/320 -f 310/340/321 299/327/311 298/326/310 -f 298/326/310 309/339/320 310/340/321 -f 311/342/322 300/329/312 301/331/211 -f 301/331/211 312/344/221 311/342/322 -f 313/345/323 302/332/313 300/329/312 -f 300/329/312 311/342/322 313/345/323 -f 314/346/324 303/333/314 302/332/313 -f 302/332/313 313/345/323 314/346/324 -f 315/347/325 304/334/315 303/333/314 -f 303/333/314 314/346/324 315/347/325 -f 316/348/326 305/335/316 304/334/315 -f 304/334/315 315/347/325 316/348/326 -f 317/349/327 306/336/317 305/335/316 -f 305/335/316 316/348/326 317/349/327 -f 318/350/328 307/337/318 306/336/317 -f 306/336/317 317/349/327 318/350/328 -f 319/351/329 308/338/319 307/337/318 -f 307/337/318 318/350/328 319/351/329 -f 320/352/330 309/339/320 308/338/319 -f 308/338/319 319/351/329 320/352/330 -f 321/353/331 310/340/321 309/339/320 -f 309/339/320 320/352/330 321/353/331 -f 322/355/332 311/342/322 312/344/221 -f 312/344/221 323/357/231 322/355/332 -f 324/358/333 313/345/323 311/342/322 -f 311/342/322 322/355/332 324/358/333 -f 325/359/334 314/346/324 313/345/323 -f 313/345/323 324/358/333 325/359/334 -f 326/360/335 315/347/325 314/346/324 -f 314/346/324 325/359/334 326/360/335 -f 327/361/336 316/348/326 315/347/325 -f 315/347/325 326/360/335 327/361/336 -f 328/362/337 317/349/327 316/348/326 -f 316/348/326 327/361/336 328/362/337 -f 329/363/338 318/350/328 317/349/327 -f 317/349/327 328/362/337 329/363/338 -f 330/364/339 319/351/329 318/350/328 -f 318/350/328 329/363/338 330/364/339 -f 331/365/340 320/352/330 319/351/329 -f 319/351/329 330/364/339 331/365/340 -f 332/366/341 321/353/331 320/352/330 -f 320/352/330 331/365/340 332/366/341 -f 333/368/342 334/369/343 335/371/251 -f 335/371/251 238/260/250 333/368/342 -f 336/372/344 337/373/345 334/369/343 -f 334/369/343 333/368/342 336/372/344 -f 338/374/346 339/375/347 337/373/345 -f 337/373/345 336/372/344 338/374/346 -f 340/376/348 341/377/349 339/375/347 -f 339/375/347 338/374/346 340/376/348 -f 342/378/350 343/379/351 341/377/349 -f 341/377/349 340/376/348 342/378/350 -f 344/380/352 345/381/353 343/379/351 -f 343/379/351 342/378/350 344/380/352 -f 346/382/354 347/383/355 345/381/353 -f 345/381/353 344/380/352 346/382/354 -f 348/384/356 349/385/357 347/383/355 -f 347/383/355 346/382/354 348/384/356 -f 350/386/358 351/387/359 349/385/357 -f 349/385/357 348/384/356 350/386/358 -f 4/6/4 3/4/3 351/387/359 -f 351/387/359 350/386/358 4/6/4 -f 352/388/360 333/368/342 238/260/250 -f 238/260/250 249/273/261 352/388/360 -f 353/389/361 336/372/344 333/368/342 -f 333/368/342 352/388/360 353/389/361 -f 354/390/362 338/374/346 336/372/344 -f 336/372/344 353/389/361 354/390/362 -f 355/391/363 340/376/348 338/374/346 -f 338/374/346 354/390/362 355/391/363 -f 356/392/364 342/378/350 340/376/348 -f 340/376/348 355/391/363 356/392/364 -f 357/393/365 344/380/352 342/378/350 -f 342/378/350 356/392/364 357/393/365 -f 358/394/366 346/382/354 344/380/352 -f 344/380/352 357/393/365 358/394/366 -f 359/395/367 348/384/356 346/382/354 -f 346/382/354 358/394/366 359/395/367 -f 360/396/368 350/386/358 348/384/356 -f 348/384/356 359/395/367 360/396/368 -f 23/27/24 4/6/4 350/386/358 -f 350/386/358 360/396/368 23/27/24 -f 361/397/369 352/388/360 249/273/261 -f 249/273/261 362/399/271 361/397/369 -f 363/400/370 353/389/361 352/388/360 -f 352/388/360 361/397/369 363/400/370 -f 364/401/371 354/390/362 353/389/361 -f 353/389/361 363/400/370 364/401/371 -f 365/402/372 355/391/363 354/390/362 -f 354/390/362 364/401/371 365/402/372 -f 366/403/373 356/392/364 355/391/363 -f 355/391/363 365/402/372 366/403/373 -f 367/404/374 357/393/365 356/392/364 -f 356/392/364 366/403/373 367/404/374 -f 368/405/375 358/394/366 357/393/365 -f 357/393/365 367/404/374 368/405/375 -f 369/406/376 359/395/367 358/394/366 -f 358/394/366 368/405/375 369/406/376 -f 370/407/377 360/396/368 359/395/367 -f 359/395/367 369/406/376 370/407/377 -f 371/409/35 23/27/24 360/396/368 -f 360/396/368 370/407/377 371/409/35 -f 372/410/378 361/397/369 362/399/271 -f 362/399/271 373/412/281 372/410/378 -f 374/413/379 363/400/370 361/397/369 -f 361/397/369 372/410/378 374/413/379 -f 375/414/380 364/401/371 363/400/370 -f 363/400/370 374/413/379 375/414/380 -f 376/415/381 365/402/372 364/401/371 -f 364/401/371 375/414/380 376/415/381 -f 377/416/382 366/403/373 365/402/372 -f 365/402/372 376/415/381 377/416/382 -f 378/417/383 367/404/374 366/403/373 -f 366/403/373 377/416/382 378/417/383 -f 379/418/384 368/405/375 367/404/374 -f 367/404/374 378/417/383 379/418/384 -f 380/419/385 369/406/376 368/405/375 -f 368/405/375 379/418/384 380/419/385 -f 381/420/386 370/407/377 369/406/376 -f 369/406/376 380/419/385 381/420/386 -f 382/422/46 371/409/35 370/407/377 -f 370/407/377 381/420/386 382/422/46 -f 383/423/387 372/410/378 373/412/281 -f 373/412/281 278/304/291 383/423/387 -f 384/424/388 374/413/379 372/410/378 -f 372/410/378 383/423/387 384/424/388 -f 385/425/389 375/414/380 374/413/379 -f 374/413/379 384/424/388 385/425/389 -f 386/426/390 376/415/381 375/414/380 -f 375/414/380 385/425/389 386/426/390 -f 387/427/391 377/416/382 376/415/381 -f 376/415/381 386/426/390 387/427/391 -f 388/428/392 378/417/383 377/416/382 -f 377/416/382 387/427/391 388/428/392 -f 389/429/393 379/418/384 378/417/383 -f 378/417/383 388/428/392 389/429/393 -f 390/430/394 380/419/385 379/418/384 -f 379/418/384 389/429/393 390/430/394 -f 391/431/395 381/420/386 380/419/385 -f 380/419/385 390/430/394 391/431/395 -f 392/433/57 382/422/46 381/420/386 -f 381/420/386 391/431/395 392/433/57 -f 393/434/396 383/423/387 278/304/291 -f 278/304/291 289/317/301 393/434/396 -f 394/435/397 384/424/388 383/423/387 -f 383/423/387 393/434/396 394/435/397 -f 395/436/398 385/425/389 384/424/388 -f 384/424/388 394/435/397 395/436/398 -f 396/437/399 386/426/390 385/425/389 -f 385/425/389 395/436/398 396/437/399 -f 397/438/400 387/427/391 386/426/390 -f 386/426/390 396/437/399 397/438/400 -f 398/439/401 388/428/392 387/427/391 -f 387/427/391 397/438/400 398/439/401 -f 399/440/402 389/429/393 388/428/392 -f 388/428/392 398/439/401 399/440/402 -f 400/441/403 390/430/394 389/429/393 -f 389/429/393 399/440/402 400/441/403 -f 401/442/404 391/431/395 390/430/394 -f 390/430/394 400/441/403 401/442/404 -f 402/444/68 392/433/57 391/431/395 -f 391/431/395 401/442/404 402/444/68 -f 403/445/405 393/434/396 289/317/301 -f 289/317/301 299/328/311 403/445/405 -f 404/446/406 394/435/397 393/434/396 -f 393/434/396 403/445/405 404/446/406 -f 405/447/407 395/436/398 394/435/397 -f 394/435/397 404/446/406 405/447/407 -f 406/448/408 396/437/399 395/436/398 -f 395/436/398 405/447/407 406/448/408 -f 407/449/409 397/438/400 396/437/399 -f 396/437/399 406/448/408 407/449/409 -f 408/450/410 398/439/401 397/438/400 -f 397/438/400 407/449/409 408/450/410 -f 409/451/411 399/440/402 398/439/401 -f 398/439/401 408/450/410 409/451/411 -f 410/452/412 400/441/403 399/440/402 -f 399/440/402 409/451/411 410/452/412 -f 411/453/413 401/442/404 400/441/403 -f 400/441/403 410/452/412 411/453/413 -f 72/80/79 402/444/68 401/442/404 -f 401/442/404 411/453/413 72/80/79 -f 412/454/414 403/445/405 299/328/311 -f 299/328/311 310/341/321 412/454/414 -f 413/455/415 404/446/406 403/445/405 -f 403/445/405 412/454/414 413/455/415 -f 414/456/416 405/447/407 404/446/406 -f 404/446/406 413/455/415 414/456/416 -f 415/457/417 406/448/408 405/447/407 -f 405/447/407 414/456/416 415/457/417 -f 416/458/418 407/449/409 406/448/408 -f 406/448/408 415/457/417 416/458/418 -f 417/459/419 408/450/410 407/449/409 -f 407/449/409 416/458/418 417/459/419 -f 418/460/420 409/451/411 408/450/410 -f 408/450/410 417/459/419 418/460/420 -f 419/461/421 410/452/412 409/451/411 -f 409/451/411 418/460/420 419/461/421 -f 420/462/422 411/453/413 410/452/412 -f 410/452/412 419/461/421 420/462/422 -f 421/464/90 72/80/79 411/453/413 -f 411/453/413 420/462/422 421/464/90 -f 422/465/423 412/454/414 310/341/321 -f 310/341/321 321/354/331 422/465/423 -f 423/466/424 413/455/415 412/454/414 -f 412/454/414 422/465/423 423/466/424 -f 424/467/425 414/456/416 413/455/415 -f 413/455/415 423/466/424 424/467/425 -f 425/468/426 415/457/417 414/456/416 -f 414/456/416 424/467/425 425/468/426 -f 426/469/427 416/458/418 415/457/417 -f 415/457/417 425/468/426 426/469/427 -f 427/470/428 417/459/419 416/458/418 -f 416/458/418 426/469/427 427/470/428 -f 428/471/429 418/460/420 417/459/419 -f 417/459/419 427/470/428 428/471/429 -f 429/472/430 419/461/421 418/460/420 -f 418/460/420 428/471/429 429/472/430 -f 430/473/431 420/462/422 419/461/421 -f 419/461/421 429/472/430 430/473/431 -f 93/104/101 421/464/90 420/462/422 -f 420/462/422 430/473/431 93/104/101 -f 431/474/432 422/465/423 321/354/331 -f 321/354/331 332/367/341 431/474/432 -f 432/475/433 423/466/424 422/465/423 -f 422/465/423 431/474/432 432/475/433 -f 433/476/434 424/467/425 423/466/424 -f 423/466/424 432/475/433 433/476/434 -f 434/477/435 425/468/426 424/467/425 -f 424/467/425 433/476/434 434/477/435 -f 435/478/436 426/469/427 425/468/426 -f 425/468/426 434/477/435 435/478/436 -f 436/479/437 427/470/428 426/469/427 -f 426/469/427 435/478/436 436/479/437 -f 437/480/438 428/471/429 427/470/428 -f 427/470/428 436/479/437 437/480/438 -f 438/481/439 429/472/430 428/471/429 -f 428/471/429 437/480/438 438/481/439 -f 439/482/440 430/473/431 429/472/430 -f 429/472/430 438/481/439 439/482/440 -f 440/484/112 93/104/101 430/473/431 -f 430/473/431 439/482/440 440/484/112 -f 441/485/441 103/115/111 440/483/112 -f 440/483/112 442/486/442 441/485/441 -f 443/488/443 104/116/113 103/115/111 -f 103/115/111 441/485/441 443/488/443 -f 444/489/444 105/117/114 104/116/113 -f 104/116/113 443/488/443 444/489/444 -f 445/490/445 106/118/115 105/117/114 -f 105/117/114 444/489/444 445/490/445 -f 446/491/446 107/119/116 106/118/115 -f 106/118/115 445/490/445 446/491/446 -f 447/492/447 108/120/117 107/119/116 -f 107/119/116 446/491/446 447/492/447 -f 448/493/448 109/121/118 108/120/117 -f 108/120/117 447/492/447 448/493/448 -f 449/494/449 110/122/119 109/121/118 -f 109/121/118 448/493/448 449/494/449 -f 450/495/450 111/123/120 110/122/119 -f 110/122/119 449/494/449 450/495/450 -f 451/496/451 210/229/121 111/123/120 -f 111/123/120 450/495/450 451/496/451 -f 452/498/452 441/485/441 442/486/442 -f 442/486/442 453/499/453 452/498/452 -f 454/501/454 443/488/443 441/485/441 -f 441/485/441 452/498/452 454/501/454 -f 455/502/455 444/489/444 443/488/443 -f 443/488/443 454/501/454 455/502/455 -f 456/503/456 445/490/445 444/489/444 -f 444/489/444 455/502/455 456/503/456 -f 457/504/457 446/491/446 445/490/445 -f 445/490/445 456/503/456 457/504/457 -f 458/505/458 447/492/447 446/491/446 -f 446/491/446 457/504/457 458/505/458 -f 459/506/459 448/493/448 447/492/447 -f 447/492/447 458/505/458 459/506/459 -f 460/507/460 449/494/449 448/493/448 -f 448/493/448 459/506/459 460/507/460 -f 461/508/461 450/495/450 449/494/449 -f 449/494/449 460/507/460 461/508/461 -f 462/509/462 451/496/451 450/495/450 -f 450/495/450 461/508/461 462/509/462 -f 463/511/463 452/498/452 453/499/453 -f 453/499/453 464/512/464 463/511/463 -f 465/514/465 454/501/454 452/498/452 -f 452/498/452 463/511/463 465/514/465 -f 466/515/466 455/502/455 454/501/454 -f 454/501/454 465/514/465 466/515/466 -f 467/516/467 456/503/456 455/502/455 -f 455/502/455 466/515/466 467/516/467 -f 468/517/468 457/504/457 456/503/456 -f 456/503/456 467/516/467 468/517/468 -f 469/518/469 458/505/458 457/504/457 -f 457/504/457 468/517/468 469/518/469 -f 470/519/470 459/506/459 458/505/458 -f 458/505/458 469/518/469 470/519/470 -f 471/520/471 460/507/460 459/506/459 -f 459/506/459 470/519/470 471/520/471 -f 472/521/472 461/508/461 460/507/460 -f 460/507/460 471/520/471 472/521/472 -f 473/522/473 462/509/462 461/508/461 -f 461/508/461 472/521/472 473/522/473 -f 474/524/474 463/511/463 464/512/464 -f 464/512/464 475/525/475 474/524/474 -f 476/527/476 465/514/465 463/511/463 -f 463/511/463 474/524/474 476/527/476 -f 477/528/477 466/515/466 465/514/465 -f 465/514/465 476/527/476 477/528/477 -f 478/529/478 467/516/467 466/515/466 -f 466/515/466 477/528/477 478/529/478 -f 479/530/479 468/517/468 467/516/467 -f 467/516/467 478/529/478 479/530/479 -f 480/531/480 469/518/469 468/517/468 -f 468/517/468 479/530/479 480/531/480 -f 481/532/481 470/519/470 469/518/469 -f 469/518/469 480/531/480 481/532/481 -f 482/533/482 471/520/471 470/519/470 -f 470/519/470 481/532/481 482/533/482 -f 483/534/483 472/521/472 471/520/471 -f 471/520/471 482/533/482 483/534/483 -f 569/633/484 473/522/473 472/521/472 -f 472/521/472 483/534/483 569/633/484 -f 484/535/485 474/524/474 475/525/475 -f 475/525/475 485/536/486 484/535/485 -f 486/538/487 476/527/476 474/524/474 -f 474/524/474 484/535/485 486/538/487 -f 487/539/488 477/528/477 476/527/476 -f 476/527/476 486/538/487 487/539/488 -f 488/540/489 478/529/478 477/528/477 -f 477/528/477 487/539/488 488/540/489 -f 489/541/490 479/530/479 478/529/478 -f 478/529/478 488/540/489 489/541/490 -f 490/542/491 480/531/480 479/530/479 -f 479/530/479 489/541/490 490/542/491 -f 491/543/492 481/532/481 480/531/480 -f 480/531/480 490/542/491 491/543/492 -f 492/544/493 482/533/482 481/532/481 -f 481/532/481 491/543/492 492/544/493 -f 493/545/494 483/534/483 482/533/482 -f 482/533/482 492/544/493 493/545/494 -f 579/644/495 569/633/484 483/534/483 -f 483/534/483 493/545/494 579/644/495 -f 494/546/496 484/535/485 485/536/486 -f 485/536/486 495/547/497 494/546/496 -f 496/549/498 486/538/487 484/535/485 -f 484/535/485 494/546/496 496/549/498 -f 497/550/499 487/539/488 486/538/487 -f 486/538/487 496/549/498 497/550/499 -f 498/551/500 488/540/489 487/539/488 -f 487/539/488 497/550/499 498/551/500 -f 499/552/501 489/541/490 488/540/489 -f 488/540/489 498/551/500 499/552/501 -f 500/553/502 490/542/491 489/541/490 -f 489/541/490 499/552/501 500/553/502 -f 501/554/503 491/543/492 490/542/491 -f 490/542/491 500/553/502 501/554/503 -f 502/555/504 492/544/493 491/543/492 -f 491/543/492 501/554/503 502/555/504 -f 503/556/505 493/545/494 492/544/493 -f 492/544/493 502/555/504 503/556/505 -f 589/655/506 579/644/495 493/545/494 -f 493/545/494 503/556/505 589/655/506 -f 504/557/507 494/546/496 495/547/497 -f 495/547/497 505/558/508 504/557/507 -f 506/560/509 496/549/498 494/546/496 -f 494/546/496 504/557/507 506/560/509 -f 507/561/510 497/550/499 496/549/498 -f 496/549/498 506/560/509 507/561/510 -f 508/562/511 498/551/500 497/550/499 -f 497/550/499 507/561/510 508/562/511 -f 509/563/512 499/552/501 498/551/500 -f 498/551/500 508/562/511 509/563/512 -f 510/564/513 500/553/502 499/552/501 -f 499/552/501 509/563/512 510/564/513 -f 511/565/514 501/554/503 500/553/502 -f 500/553/502 510/564/513 511/565/514 -f 512/566/515 502/555/504 501/554/503 -f 501/554/503 511/565/514 512/566/515 -f 513/567/516 503/556/505 502/555/504 -f 502/555/504 512/566/515 513/567/516 -f 514/568/517 589/655/506 503/556/505 -f 503/556/505 513/567/516 514/568/517 -f 515/570/518 504/557/507 505/558/508 -f 505/558/508 806/903/519 515/570/518 -f 516/571/520 506/560/509 504/557/507 -f 504/557/507 515/570/518 516/571/520 -f 517/572/521 507/561/510 506/560/509 -f 506/560/509 516/571/520 517/572/521 -f 518/573/522 508/562/511 507/561/510 -f 507/561/510 517/572/521 518/573/522 -f 519/574/523 509/563/512 508/562/511 -f 508/562/511 518/573/522 519/574/523 -f 520/575/524 510/564/513 509/563/512 -f 509/563/512 519/574/523 520/575/524 -f 521/576/525 511/565/514 510/564/513 -f 510/564/513 520/575/524 521/576/525 -f 522/577/526 512/566/515 511/565/514 -f 511/565/514 521/576/525 522/577/526 -f 523/578/527 513/567/516 512/566/515 -f 512/566/515 522/577/526 523/578/527 -f 524/579/528 514/568/517 513/567/516 -f 513/567/516 523/578/527 524/579/528 -f 525/581/529 515/570/518 806/903/519 -f 806/903/519 816/914/530 525/581/529 -f 526/582/531 516/571/520 515/570/518 -f 515/570/518 525/581/529 526/582/531 -f 527/583/532 517/572/521 516/571/520 -f 516/571/520 526/582/531 527/583/532 -f 528/584/533 518/573/522 517/572/521 -f 517/572/521 527/583/532 528/584/533 -f 529/585/534 519/574/523 518/573/522 -f 518/573/522 528/584/533 529/585/534 -f 530/586/535 520/575/524 519/574/523 -f 519/574/523 529/585/534 530/586/535 -f 531/587/536 521/576/525 520/575/524 -f 520/575/524 530/586/535 531/587/536 -f 532/588/537 522/577/526 521/576/525 -f 521/576/525 531/587/536 532/588/537 -f 533/589/538 523/578/527 522/577/526 -f 522/577/526 532/588/537 533/589/538 -f 619/688/539 524/579/528 523/578/527 -f 523/578/527 533/589/538 619/688/539 -f 534/590/540 525/581/529 816/914/530 -f 816/914/530 535/592/541 534/590/540 -f 824/927/542 526/582/531 525/581/529 -f 525/581/529 534/590/540 824/927/542 -f 826/930/543 527/583/532 526/582/531 -f 526/582/531 824/927/542 826/930/543 -f 536/594/544 528/584/533 527/583/532 -f 527/583/532 826/930/543 536/594/544 -f 829/934/545 529/585/534 528/584/533 -f 528/584/533 536/594/544 829/934/545 -f 831/937/546 530/586/535 529/585/534 -f 529/585/534 829/934/545 831/937/546 -f 833/940/547 531/587/536 530/586/535 -f 530/586/535 831/937/546 833/940/547 -f 835/943/548 532/588/537 531/587/536 -f 531/587/536 833/940/547 835/943/548 -f 537/596/549 533/589/538 532/588/537 -f 532/588/537 835/943/548 537/596/549 -f 538/598/550 619/688/539 533/589/538 -f 533/589/538 537/596/549 538/598/550 -f 539/601/551 209/228/222 210/230/121 -f 210/230/121 451/497/451 539/601/551 -f 540/602/552 211/231/223 209/228/222 -f 209/228/222 539/601/551 540/602/552 -f 541/603/553 212/232/224 211/231/223 -f 211/231/223 540/602/552 541/603/553 -f 542/604/554 213/233/225 212/232/224 -f 212/232/224 541/603/553 542/604/554 -f 543/605/555 214/234/226 213/233/225 -f 213/233/225 542/604/554 543/605/555 -f 544/606/556 215/235/227 214/234/226 -f 214/234/226 543/605/555 544/606/556 -f 545/607/557 216/236/228 215/235/227 -f 215/235/227 544/606/556 545/607/557 -f 546/608/558 217/237/229 216/236/228 -f 216/236/228 545/607/557 546/608/558 -f 547/609/559 218/238/230 217/237/229 -f 217/237/229 546/608/558 547/609/559 -f 633/706/560 323/356/231 218/238/230 -f 218/238/230 547/609/559 633/706/560 -f 548/610/561 539/601/551 451/497/451 -f 451/497/451 462/510/462 548/610/561 -f 549/611/562 540/602/552 539/601/551 -f 539/601/551 548/610/561 549/611/562 -f 550/612/563 541/603/553 540/602/552 -f 540/602/552 549/611/562 550/612/563 -f 551/613/564 542/604/554 541/603/553 -f 541/603/553 550/612/563 551/613/564 -f 552/614/565 543/605/555 542/604/554 -f 542/604/554 551/613/564 552/614/565 -f 553/615/566 544/606/556 543/605/555 -f 543/605/555 552/614/565 553/615/566 -f 554/616/567 545/607/557 544/606/556 -f 544/606/556 553/615/566 554/616/567 -f 555/617/568 546/608/558 545/607/557 -f 545/607/557 554/616/567 555/617/568 -f 556/618/569 547/609/559 546/608/558 -f 546/608/558 555/617/568 556/618/569 -f 557/619/570 633/706/560 547/609/559 -f 547/609/559 556/618/569 557/619/570 -f 558/621/571 548/610/561 462/510/462 -f 462/510/462 473/523/473 558/621/571 -f 559/622/572 549/611/562 548/610/561 -f 548/610/561 558/621/571 559/622/572 -f 560/623/573 550/612/563 549/611/562 -f 549/611/562 559/622/572 560/623/573 -f 561/624/574 551/613/564 550/612/563 -f 550/612/563 560/623/573 561/624/574 -f 562/625/575 552/614/565 551/613/564 -f 551/613/564 561/624/574 562/625/575 -f 563/626/576 553/615/566 552/614/565 -f 552/614/565 562/625/575 563/626/576 -f 564/627/577 554/616/567 553/615/566 -f 553/615/566 563/626/576 564/627/577 -f 565/628/578 555/617/568 554/616/567 -f 554/616/567 564/627/577 565/628/578 -f 566/629/579 556/618/569 555/617/568 -f 555/617/568 565/628/578 566/629/579 -f 567/630/580 557/619/570 556/618/569 -f 556/618/569 566/629/579 567/630/580 -f 568/632/581 558/621/571 473/523/473 -f 473/523/473 569/634/484 568/632/581 -f 570/635/582 559/622/572 558/621/571 -f 558/621/571 568/632/581 570/635/582 -f 571/636/583 560/623/573 559/622/572 -f 559/622/572 570/635/582 571/636/583 -f 572/637/584 561/624/574 560/623/573 -f 560/623/573 571/636/583 572/637/584 -f 573/638/585 562/625/575 561/624/574 -f 561/624/574 572/637/584 573/638/585 -f 574/639/586 563/626/576 562/625/575 -f 562/625/575 573/638/585 574/639/586 -f 575/640/587 564/627/577 563/626/576 -f 563/626/576 574/639/586 575/640/587 -f 576/641/588 565/628/578 564/627/577 -f 564/627/577 575/640/587 576/641/588 -f 577/642/589 566/629/579 565/628/578 -f 565/628/578 576/641/588 577/642/589 -f 662/737/590 567/630/580 566/629/579 -f 566/629/579 577/642/589 662/737/590 -f 578/643/591 568/632/581 569/634/484 -f 569/634/484 579/645/495 578/643/591 -f 580/646/592 570/635/582 568/632/581 -f 568/632/581 578/643/591 580/646/592 -f 581/647/593 571/636/583 570/635/582 -f 570/635/582 580/646/592 581/647/593 -f 582/648/594 572/637/584 571/636/583 -f 571/636/583 581/647/593 582/648/594 -f 583/649/595 573/638/585 572/637/584 -f 572/637/584 582/648/594 583/649/595 -f 584/650/596 574/639/586 573/638/585 -f 573/638/585 583/649/595 584/650/596 -f 585/651/597 575/640/587 574/639/586 -f 574/639/586 584/650/596 585/651/597 -f 586/652/598 576/641/588 575/640/587 -f 575/640/587 585/651/597 586/652/598 -f 587/653/599 577/642/589 576/641/588 -f 576/641/588 586/652/598 587/653/599 -f 672/748/600 662/737/590 577/642/589 -f 577/642/589 587/653/599 672/748/600 -f 588/654/601 578/643/591 579/645/495 -f 579/645/495 589/656/506 588/654/601 -f 590/657/602 580/646/592 578/643/591 -f 578/643/591 588/654/601 590/657/602 -f 591/658/603 581/647/593 580/646/592 -f 580/646/592 590/657/602 591/658/603 -f 592/659/604 582/648/594 581/647/593 -f 581/647/593 591/658/603 592/659/604 -f 593/660/605 583/649/595 582/648/594 -f 582/648/594 592/659/604 593/660/605 -f 594/661/606 584/650/596 583/649/595 -f 583/649/595 593/660/605 594/661/606 -f 595/662/607 585/651/597 584/650/596 -f 584/650/596 594/661/606 595/662/607 -f 596/663/608 586/652/598 585/651/597 -f 585/651/597 595/662/607 596/663/608 -f 597/664/609 587/653/599 586/652/598 -f 586/652/598 596/663/608 597/664/609 -f 598/665/610 672/748/600 587/653/599 -f 587/653/599 597/664/609 598/665/610 -f 599/667/611 588/654/601 589/656/506 -f 589/656/506 514/569/517 599/667/611 -f 600/668/612 590/657/602 588/654/601 -f 588/654/601 599/667/611 600/668/612 -f 601/669/613 591/658/603 590/657/602 -f 590/657/602 600/668/612 601/669/613 -f 602/670/614 592/659/604 591/658/603 -f 591/658/603 601/669/613 602/670/614 -f 603/671/615 593/660/605 592/659/604 -f 592/659/604 602/670/614 603/671/615 -f 604/672/616 594/661/606 593/660/605 -f 593/660/605 603/671/615 604/672/616 -f 605/673/617 595/662/607 594/661/606 -f 594/661/606 604/672/616 605/673/617 -f 606/674/618 596/663/608 595/662/607 -f 595/662/607 605/673/617 606/674/618 -f 607/675/619 597/664/609 596/663/608 -f 596/663/608 606/674/618 607/675/619 -f 691/768/620 598/665/610 597/664/609 -f 597/664/609 607/675/619 691/768/620 -f 608/676/621 599/667/611 514/569/517 -f 514/569/517 524/580/528 608/676/621 -f 609/677/622 600/668/612 599/667/611 -f 599/667/611 608/676/621 609/677/622 -f 610/678/623 601/669/613 600/668/612 -f 600/668/612 609/677/622 610/678/623 -f 611/679/624 602/670/614 601/669/613 -f 601/669/613 610/678/623 611/679/624 -f 612/680/625 603/671/615 602/670/614 -f 602/670/614 611/679/624 612/680/625 -f 613/681/626 604/672/616 603/671/615 -f 603/671/615 612/680/625 613/681/626 -f 614/682/627 605/673/617 604/672/616 -f 604/672/616 613/681/626 614/682/627 -f 615/683/628 606/674/618 605/673/617 -f 605/673/617 614/682/627 615/683/628 -f 616/684/629 607/675/619 606/674/618 -f 606/674/618 615/683/628 616/684/629 -f 617/685/630 691/768/620 607/675/619 -f 607/675/619 616/684/629 617/685/630 -f 618/687/631 608/676/621 524/580/528 -f 524/580/528 619/689/539 618/687/631 -f 620/690/632 609/677/622 608/676/621 -f 608/676/621 618/687/631 620/690/632 -f 621/691/633 610/678/623 609/677/622 -f 609/677/622 620/690/632 621/691/633 -f 622/692/634 611/679/624 610/678/623 -f 610/678/623 621/691/633 622/692/634 -f 623/693/635 612/680/625 611/679/624 -f 611/679/624 622/692/634 623/693/635 -f 624/694/636 613/681/626 612/680/625 -f 612/680/625 623/693/635 624/694/636 -f 625/695/637 614/682/627 613/681/626 -f 613/681/626 624/694/636 625/695/637 -f 626/696/638 615/683/628 614/682/627 -f 614/682/627 625/695/637 626/696/638 -f 627/697/639 616/684/629 615/683/628 -f 615/683/628 626/696/638 627/697/639 -f 711/790/640 617/685/630 616/684/629 -f 616/684/629 627/697/639 711/790/640 -f 628/698/641 618/687/631 619/689/539 -f 619/689/539 538/600/550 628/698/641 -f 930/1062/642 620/690/632 618/687/631 -f 618/687/631 628/698/641 930/1062/642 -f 629/699/643 621/691/633 620/690/632 -f 620/690/632 930/1062/642 629/699/643 -f 933/1066/644 622/692/634 621/691/633 -f 621/691/633 629/699/643 933/1066/644 -f 935/1069/645 623/693/635 622/692/634 -f 622/692/634 933/1066/644 935/1069/645 -f 937/1072/646 624/694/636 623/693/635 -f 623/693/635 935/1069/645 937/1072/646 -f 630/701/647 625/695/637 624/694/636 -f 624/694/636 937/1072/646 630/701/647 -f 631/703/648 626/696/638 625/695/637 -f 625/695/637 630/701/647 631/703/648 -f 941/1077/649 627/697/639 626/696/638 -f 626/696/638 631/703/648 941/1077/649 -f 722/803/650 711/790/640 627/697/639 -f 627/697/639 941/1077/649 722/803/650 -f 632/705/651 322/355/332 323/357/231 -f 323/357/231 633/707/560 632/705/651 -f 634/708/652 324/358/333 322/355/332 -f 322/355/332 632/705/651 634/708/652 -f 635/709/653 325/359/334 324/358/333 -f 324/358/333 634/708/652 635/709/653 -f 636/710/654 326/360/335 325/359/334 -f 325/359/334 635/709/653 636/710/654 -f 637/711/655 327/361/336 326/360/335 -f 326/360/335 636/710/654 637/711/655 -f 638/712/656 328/362/337 327/361/336 -f 327/361/336 637/711/655 638/712/656 -f 639/713/657 329/363/338 328/362/337 -f 328/362/337 638/712/656 639/713/657 -f 640/714/658 330/364/339 329/363/338 -f 329/363/338 639/713/657 640/714/658 -f 641/715/659 331/365/340 330/364/339 -f 330/364/339 640/714/658 641/715/659 -f 729/820/660 332/366/341 331/365/340 -f 331/365/340 641/715/659 729/820/660 -f 642/716/661 632/705/651 633/707/560 -f 633/707/560 557/620/570 642/716/661 -f 643/717/662 634/708/652 632/705/651 -f 632/705/651 642/716/661 643/717/662 -f 644/718/663 635/709/653 634/708/652 -f 634/708/652 643/717/662 644/718/663 -f 645/719/664 636/710/654 635/709/653 -f 635/709/653 644/718/663 645/719/664 -f 646/720/665 637/711/655 636/710/654 -f 636/710/654 645/719/664 646/720/665 -f 647/721/666 638/712/656 637/711/655 -f 637/711/655 646/720/665 647/721/666 -f 648/722/667 639/713/657 638/712/656 -f 638/712/656 647/721/666 648/722/667 -f 649/723/668 640/714/658 639/713/657 -f 639/713/657 648/722/667 649/723/668 -f 650/724/669 641/715/659 640/714/658 -f 640/714/658 649/723/668 650/724/669 -f 739/831/670 729/820/660 641/715/659 -f 641/715/659 650/724/669 739/831/670 -f 651/725/671 642/716/661 557/620/570 -f 557/620/570 567/631/580 651/725/671 -f 652/726/672 643/717/662 642/716/661 -f 642/716/661 651/725/671 652/726/672 -f 653/727/673 644/718/663 643/717/662 -f 643/717/662 652/726/672 653/727/673 -f 654/728/674 645/719/664 644/718/663 -f 644/718/663 653/727/673 654/728/674 -f 655/729/675 646/720/665 645/719/664 -f 645/719/664 654/728/674 655/729/675 -f 656/730/676 647/721/666 646/720/665 -f 646/720/665 655/729/675 656/730/676 -f 657/731/677 648/722/667 647/721/666 -f 647/721/666 656/730/676 657/731/677 -f 658/732/678 649/723/668 648/722/667 -f 648/722/667 657/731/677 658/732/678 -f 659/733/679 650/724/669 649/723/668 -f 649/723/668 658/732/678 659/733/679 -f 660/734/680 739/831/670 650/724/669 -f 650/724/669 659/733/679 660/734/680 -f 661/736/681 651/725/671 567/631/580 -f 567/631/580 662/738/590 661/736/681 -f 663/739/682 652/726/672 651/725/671 -f 651/725/671 661/736/681 663/739/682 -f 664/740/683 653/727/673 652/726/672 -f 652/726/672 663/739/682 664/740/683 -f 665/741/684 654/728/674 653/727/673 -f 653/727/673 664/740/683 665/741/684 -f 666/742/685 655/729/675 654/728/674 -f 654/728/674 665/741/684 666/742/685 -f 667/743/686 656/730/676 655/729/675 -f 655/729/675 666/742/685 667/743/686 -f 668/744/687 657/731/677 656/730/676 -f 656/730/676 667/743/686 668/744/687 -f 669/745/688 658/732/678 657/731/677 -f 657/731/677 668/744/687 669/745/688 -f 670/746/689 659/733/679 658/732/678 -f 658/732/678 669/745/688 670/746/689 -f 758/851/690 660/734/680 659/733/679 -f 659/733/679 670/746/689 758/851/690 -f 671/747/691 661/736/681 662/738/590 -f 662/738/590 672/749/600 671/747/691 -f 673/750/692 663/739/682 661/736/681 -f 661/736/681 671/747/691 673/750/692 -f 674/751/693 664/740/683 663/739/682 -f 663/739/682 673/750/692 674/751/693 -f 675/752/694 665/741/684 664/740/683 -f 664/740/683 674/751/693 675/752/694 -f 676/753/695 666/742/685 665/741/684 -f 665/741/684 675/752/694 676/753/695 -f 677/754/696 667/743/686 666/742/685 -f 666/742/685 676/753/695 677/754/696 -f 678/755/697 668/744/687 667/743/686 -f 667/743/686 677/754/696 678/755/697 -f 679/756/698 669/745/688 668/744/687 -f 668/744/687 678/755/697 679/756/698 -f 680/757/699 670/746/689 669/745/688 -f 669/745/688 679/756/698 680/757/699 -f 768/862/700 758/851/690 670/746/689 -f 670/746/689 680/757/699 768/862/700 -f 681/758/701 671/747/691 672/749/600 -f 672/749/600 598/666/610 681/758/701 -f 682/759/702 673/750/692 671/747/691 -f 671/747/691 681/758/701 682/759/702 -f 683/760/703 674/751/693 673/750/692 -f 673/750/692 682/759/702 683/760/703 -f 684/761/704 675/752/694 674/751/693 -f 674/751/693 683/760/703 684/761/704 -f 685/762/705 676/753/695 675/752/694 -f 675/752/694 684/761/704 685/762/705 -f 686/763/706 677/754/696 676/753/695 -f 676/753/695 685/762/705 686/763/706 -f 687/764/707 678/755/697 677/754/696 -f 677/754/696 686/763/706 687/764/707 -f 688/765/708 679/756/698 678/755/697 -f 678/755/697 687/764/707 688/765/708 -f 689/766/709 680/757/699 679/756/698 -f 679/756/698 688/765/708 689/766/709 -f 778/873/710 768/862/700 680/757/699 -f 680/757/699 689/766/709 778/873/710 -f 690/767/711 681/758/701 598/666/610 -f 598/666/610 691/769/620 690/767/711 -f 692/770/712 682/759/702 681/758/701 -f 681/758/701 690/767/711 692/770/712 -f 693/771/713 683/760/703 682/759/702 -f 682/759/702 692/770/712 693/771/713 -f 694/772/714 684/761/704 683/760/703 -f 683/760/703 693/771/713 694/772/714 -f 695/773/715 685/762/705 684/761/704 -f 684/761/704 694/772/714 695/773/715 -f 696/774/716 686/763/706 685/762/705 -f 685/762/705 695/773/715 696/774/716 -f 697/775/717 687/764/707 686/763/706 -f 686/763/706 696/774/716 697/775/717 -f 698/776/718 688/765/708 687/764/707 -f 687/764/707 697/775/717 698/776/718 -f 699/777/719 689/766/709 688/765/708 -f 688/765/708 698/776/718 699/777/719 -f 788/884/720 778/873/710 689/766/709 -f 689/766/709 699/777/719 788/884/720 -f 700/778/721 690/767/711 691/769/620 -f 691/769/620 617/686/630 700/778/721 -f 701/779/722 692/770/712 690/767/711 -f 690/767/711 700/778/721 701/779/722 -f 702/780/723 693/771/713 692/770/712 -f 692/770/712 701/779/722 702/780/723 -f 703/781/724 694/772/714 693/771/713 -f 693/771/713 702/780/723 703/781/724 -f 704/782/725 695/773/715 694/772/714 -f 694/772/714 703/781/724 704/782/725 -f 705/783/726 696/774/716 695/773/715 -f 695/773/715 704/782/725 705/783/726 -f 706/784/727 697/775/717 696/774/716 -f 696/774/716 705/783/726 706/784/727 -f 707/785/728 698/776/718 697/775/717 -f 697/775/717 706/784/727 707/785/728 -f 708/786/729 699/777/719 698/776/718 -f 698/776/718 707/785/728 708/786/729 -f 709/787/730 788/884/720 699/777/719 -f 699/777/719 708/786/729 709/787/730 -f 710/789/731 700/778/721 617/686/630 -f 617/686/630 711/791/640 710/789/731 -f 712/792/732 701/779/722 700/778/721 -f 700/778/721 710/789/731 712/792/732 -f 713/793/733 702/780/723 701/779/722 -f 701/779/722 712/792/732 713/793/733 -f 714/794/734 703/781/724 702/780/723 -f 702/780/723 713/793/733 714/794/734 -f 715/795/735 704/782/725 703/781/724 -f 703/781/724 714/794/734 715/795/735 -f 716/796/736 705/783/726 704/782/725 -f 704/782/725 715/795/735 716/796/736 -f 717/797/737 706/784/727 705/783/726 -f 705/783/726 716/796/736 717/797/737 -f 718/798/738 707/785/728 706/784/727 -f 706/784/727 717/797/737 718/798/738 -f 719/799/739 708/786/729 707/785/728 -f 707/785/728 718/798/738 719/799/739 -f 720/800/740 709/787/730 708/786/729 -f 708/786/729 719/799/739 720/800/740 -f 721/802/741 710/789/731 711/791/640 -f 711/791/640 722/805/650 721/802/741 -f 1030/1181/742 712/792/732 710/789/731 -f 710/789/731 721/802/741 1030/1181/742 -f 1032/1184/743 713/793/733 712/792/732 -f 712/792/732 1030/1181/742 1032/1184/743 -f 723/808/744 714/794/734 713/793/733 -f 713/793/733 1032/1184/743 723/808/744 -f 724/810/745 715/795/735 714/794/734 -f 714/794/734 723/808/744 724/810/745 -f 1036/1189/746 716/796/736 715/795/735 -f 715/795/735 724/810/745 1036/1189/746 -f 725/812/747 717/797/737 716/796/736 -f 716/796/736 1036/1189/746 725/812/747 -f 726/814/748 718/798/738 717/797/737 -f 717/797/737 725/812/747 726/814/748 -f 1040/1194/749 719/799/739 718/798/738 -f 718/798/738 726/814/748 1040/1194/749 -f 727/816/750 720/800/740 719/799/739 -f 719/799/739 1040/1194/749 727/816/750 -f 728/819/751 431/474/432 332/367/341 -f 332/367/341 729/821/660 728/819/751 -f 730/822/752 432/475/433 431/474/432 -f 431/474/432 728/819/751 730/822/752 -f 731/823/753 433/476/434 432/475/433 -f 432/475/433 730/822/752 731/823/753 -f 732/824/754 434/477/435 433/476/434 -f 433/476/434 731/823/753 732/824/754 -f 733/825/755 435/478/436 434/477/435 -f 434/477/435 732/824/754 733/825/755 -f 734/826/756 436/479/437 435/478/436 -f 435/478/436 733/825/755 734/826/756 -f 735/827/757 437/480/438 436/479/437 -f 436/479/437 734/826/756 735/827/757 -f 736/828/758 438/481/439 437/480/438 -f 437/480/438 735/827/757 736/828/758 -f 737/829/759 439/482/440 438/481/439 -f 438/481/439 736/828/758 737/829/759 -f 442/487/442 440/484/112 439/482/440 -f 439/482/440 737/829/759 442/487/442 -f 738/830/760 728/819/751 729/821/660 -f 729/821/660 739/832/670 738/830/760 -f 740/833/761 730/822/752 728/819/751 -f 728/819/751 738/830/760 740/833/761 -f 741/834/762 731/823/753 730/822/752 -f 730/822/752 740/833/761 741/834/762 -f 742/835/763 732/824/754 731/823/753 -f 731/823/753 741/834/762 742/835/763 -f 743/836/764 733/825/755 732/824/754 -f 732/824/754 742/835/763 743/836/764 -f 744/837/765 734/826/756 733/825/755 -f 733/825/755 743/836/764 744/837/765 -f 745/838/766 735/827/757 734/826/756 -f 734/826/756 744/837/765 745/838/766 -f 746/839/767 736/828/758 735/827/757 -f 735/827/757 745/838/766 746/839/767 -f 747/840/768 737/829/759 736/828/758 -f 736/828/758 746/839/767 747/840/768 -f 453/500/453 442/487/442 737/829/759 -f 737/829/759 747/840/768 453/500/453 -f 748/841/769 738/830/760 739/832/670 -f 739/832/670 660/735/680 748/841/769 -f 749/842/770 740/833/761 738/830/760 -f 738/830/760 748/841/769 749/842/770 -f 750/843/771 741/834/762 740/833/761 -f 740/833/761 749/842/770 750/843/771 -f 751/844/772 742/835/763 741/834/762 -f 741/834/762 750/843/771 751/844/772 -f 752/845/773 743/836/764 742/835/763 -f 742/835/763 751/844/772 752/845/773 -f 753/846/774 744/837/765 743/836/764 -f 743/836/764 752/845/773 753/846/774 -f 754/847/775 745/838/766 744/837/765 -f 744/837/765 753/846/774 754/847/775 -f 755/848/776 746/839/767 745/838/766 -f 745/838/766 754/847/775 755/848/776 -f 756/849/777 747/840/768 746/839/767 -f 746/839/767 755/848/776 756/849/777 -f 464/513/464 453/500/453 747/840/768 -f 747/840/768 756/849/777 464/513/464 -f 757/850/778 748/841/769 660/735/680 -f 660/735/680 758/852/690 757/850/778 -f 759/853/779 749/842/770 748/841/769 -f 748/841/769 757/850/778 759/853/779 -f 760/854/780 750/843/771 749/842/770 -f 749/842/770 759/853/779 760/854/780 -f 761/855/781 751/844/772 750/843/771 -f 750/843/771 760/854/780 761/855/781 -f 762/856/782 752/845/773 751/844/772 -f 751/844/772 761/855/781 762/856/782 -f 763/857/783 753/846/774 752/845/773 -f 752/845/773 762/856/782 763/857/783 -f 764/858/784 754/847/775 753/846/774 -f 753/846/774 763/857/783 764/858/784 -f 765/859/785 755/848/776 754/847/775 -f 754/847/775 764/858/784 765/859/785 -f 766/860/786 756/849/777 755/848/776 -f 755/848/776 765/859/785 766/860/786 -f 475/526/475 464/513/464 756/849/777 -f 756/849/777 766/860/786 475/526/475 -f 767/861/787 757/850/778 758/852/690 -f 758/852/690 768/863/700 767/861/787 -f 769/864/788 759/853/779 757/850/778 -f 757/850/778 767/861/787 769/864/788 -f 770/865/789 760/854/780 759/853/779 -f 759/853/779 769/864/788 770/865/789 -f 771/866/790 761/855/781 760/854/780 -f 760/854/780 770/865/789 771/866/790 -f 772/867/791 762/856/782 761/855/781 -f 761/855/781 771/866/790 772/867/791 -f 773/868/792 763/857/783 762/856/782 -f 762/856/782 772/867/791 773/868/792 -f 774/869/793 764/858/784 763/857/783 -f 763/857/783 773/868/792 774/869/793 -f 775/870/794 765/859/785 764/858/784 -f 764/858/784 774/869/793 775/870/794 -f 776/871/795 766/860/786 765/859/785 -f 765/859/785 775/870/794 776/871/795 -f 485/537/486 475/526/475 766/860/786 -f 766/860/786 776/871/795 485/537/486 -f 777/872/796 767/861/787 768/863/700 -f 768/863/700 778/874/710 777/872/796 -f 779/875/797 769/864/788 767/861/787 -f 767/861/787 777/872/796 779/875/797 -f 780/876/798 770/865/789 769/864/788 -f 769/864/788 779/875/797 780/876/798 -f 781/877/799 771/866/790 770/865/789 -f 770/865/789 780/876/798 781/877/799 -f 782/878/800 772/867/791 771/866/790 -f 771/866/790 781/877/799 782/878/800 -f 783/879/801 773/868/792 772/867/791 -f 772/867/791 782/878/800 783/879/801 -f 784/880/802 774/869/793 773/868/792 -f 773/868/792 783/879/801 784/880/802 -f 785/881/803 775/870/794 774/869/793 -f 774/869/793 784/880/802 785/881/803 -f 786/882/804 776/871/795 775/870/794 -f 775/870/794 785/881/803 786/882/804 -f 495/548/497 485/537/486 776/871/795 -f 776/871/795 786/882/804 495/548/497 -f 787/883/805 777/872/796 778/874/710 -f 778/874/710 788/885/720 787/883/805 -f 789/886/806 779/875/797 777/872/796 -f 777/872/796 787/883/805 789/886/806 -f 790/887/807 780/876/798 779/875/797 -f 779/875/797 789/886/806 790/887/807 -f 791/888/808 781/877/799 780/876/798 -f 780/876/798 790/887/807 791/888/808 -f 792/889/809 782/878/800 781/877/799 -f 781/877/799 791/888/808 792/889/809 -f 793/890/810 783/879/801 782/878/800 -f 782/878/800 792/889/809 793/890/810 -f 794/891/811 784/880/802 783/879/801 -f 783/879/801 793/890/810 794/891/811 -f 795/892/812 785/881/803 784/880/802 -f 784/880/802 794/891/811 795/892/812 -f 796/893/813 786/882/804 785/881/803 -f 785/881/803 795/892/812 796/893/813 -f 505/559/508 495/548/497 786/882/804 -f 786/882/804 796/893/813 505/559/508 -f 797/894/814 787/883/805 788/885/720 -f 788/885/720 709/788/730 797/894/814 -f 798/895/815 789/886/806 787/883/805 -f 787/883/805 797/894/814 798/895/815 -f 799/896/816 790/887/807 789/886/806 -f 789/886/806 798/895/815 799/896/816 -f 800/897/817 791/888/808 790/887/807 -f 790/887/807 799/896/816 800/897/817 -f 801/898/818 792/889/809 791/888/808 -f 791/888/808 800/897/817 801/898/818 -f 802/899/819 793/890/810 792/889/809 -f 792/889/809 801/898/818 802/899/819 -f 803/900/820 794/891/811 793/890/810 -f 793/890/810 802/899/819 803/900/820 -f 804/901/821 795/892/812 794/891/811 -f 794/891/811 803/900/820 804/901/821 -f 805/902/822 796/893/813 795/892/812 -f 795/892/812 804/901/821 805/902/822 -f 806/904/519 505/559/508 796/893/813 -f 796/893/813 805/902/822 806/904/519 -f 807/905/823 797/894/814 709/788/730 -f 709/788/730 720/801/740 807/905/823 -f 808/906/824 798/895/815 797/894/814 -f 797/894/814 807/905/823 808/906/824 -f 809/907/825 799/896/816 798/895/815 -f 798/895/815 808/906/824 809/907/825 -f 810/908/826 800/897/817 799/896/816 -f 799/896/816 809/907/825 810/908/826 -f 811/909/827 801/898/818 800/897/817 -f 800/897/817 810/908/826 811/909/827 -f 812/910/828 802/899/819 801/898/818 -f 801/898/818 811/909/827 812/910/828 -f 813/911/829 803/900/820 802/899/819 -f 802/899/819 812/910/828 813/911/829 -f 814/912/830 804/901/821 803/900/820 -f 803/900/820 813/911/829 814/912/830 -f 815/913/831 805/902/822 804/901/821 -f 804/901/821 814/912/830 815/913/831 -f 816/915/530 806/904/519 805/902/822 -f 805/902/822 815/913/831 816/915/530 -f 817/916/832 807/905/823 720/801/740 -f 720/801/740 727/818/750 817/916/832 -f 1129/1302/833 808/906/824 807/905/823 -f 807/905/823 817/916/832 1129/1302/833 -f 1131/1305/834 809/907/825 808/906/824 -f 808/906/824 1129/1302/833 1131/1305/834 -f 1133/1308/835 810/908/826 809/907/825 -f 809/907/825 1131/1305/834 1133/1308/835 -f 818/917/836 811/909/827 810/908/826 -f 810/908/826 1133/1308/835 818/917/836 -f 1136/1312/837 812/910/828 811/909/827 -f 811/909/827 818/917/836 1136/1312/837 -f 819/919/838 813/911/829 812/910/828 -f 812/910/828 1136/1312/837 819/919/838 -f 820/921/839 814/912/830 813/911/829 -f 813/911/829 819/919/838 820/921/839 -f 821/923/840 815/913/831 814/912/830 -f 814/912/830 820/921/839 821/923/840 -f 535/591/541 816/915/530 815/913/831 -f 815/913/831 821/923/840 535/591/541 -f 822/925/841 534/590/540 535/592/541 -f 535/592/541 1140/1317/842 822/925/841 -f 823/926/843 824/928/542 534/590/540 -f 534/590/540 822/925/841 823/926/843 -f 825/929/844 826/931/543 824/928/542 -f 824/928/542 823/926/843 825/929/844 -f 827/932/845 536/595/544 826/931/543 -f 826/931/543 825/929/844 827/932/845 -f 828/933/846 829/935/545 536/595/544 -f 536/595/544 827/932/845 828/933/846 -f 830/936/847 831/938/546 829/935/545 -f 829/935/545 828/933/846 830/936/847 -f 832/939/848 833/941/547 831/938/546 -f 831/938/546 830/936/847 832/939/848 -f 834/942/849 835/944/548 833/941/547 -f 833/941/547 832/939/848 834/942/849 -f 836/945/850 537/597/549 835/944/548 -f 835/944/548 834/942/849 836/945/850 -f 928/1059/851 538/599/550 537/597/549 -f 537/597/549 836/945/850 928/1059/851 -f 837/946/852 822/925/841 1140/1317/842 -f 1140/1317/842 1151/1330/853 837/946/852 -f 838/947/854 823/926/843 822/925/841 -f 822/925/841 837/946/852 838/947/854 -f 839/948/855 825/929/844 823/926/843 -f 823/926/843 838/947/854 839/948/855 -f 840/949/856 827/932/845 825/929/844 -f 825/929/844 839/948/855 840/949/856 -f 841/950/857 828/933/846 827/932/845 -f 827/932/845 840/949/856 841/950/857 -f 842/951/858 830/936/847 828/933/846 -f 828/933/846 841/950/857 842/951/858 -f 843/952/859 832/939/848 830/936/847 -f 830/936/847 842/951/858 843/952/859 -f 844/953/860 834/942/849 832/939/848 -f 832/939/848 843/952/859 844/953/860 -f 845/954/861 836/945/850 834/942/849 -f 834/942/849 844/953/860 845/954/861 -f 943/1080/862 928/1059/851 836/945/850 -f 836/945/850 845/954/861 943/1080/862 -f 846/955/863 837/946/852 1151/1330/853 -f 1151/1330/853 847/956/864 846/955/863 -f 848/958/865 838/947/854 837/946/852 -f 837/946/852 846/955/863 848/958/865 -f 849/959/866 839/948/855 838/947/854 -f 838/947/854 848/958/865 849/959/866 -f 850/960/867 840/949/856 839/948/855 -f 839/948/855 849/959/866 850/960/867 -f 851/961/868 841/950/857 840/949/856 -f 840/949/856 850/960/867 851/961/868 -f 852/962/869 842/951/858 841/950/857 -f 841/950/857 851/961/868 852/962/869 -f 853/963/870 843/952/859 842/951/858 -f 842/951/858 852/962/869 853/963/870 -f 854/964/871 844/953/860 843/952/859 -f 843/952/859 853/963/870 854/964/871 -f 855/965/872 845/954/861 844/953/860 -f 844/953/860 854/964/871 855/965/872 -f 953/1091/873 943/1080/862 845/954/861 -f 845/954/861 855/965/872 953/1091/873 -f 856/966/874 846/955/863 847/956/864 -f 847/956/864 857/967/875 856/966/874 -f 858/969/876 848/958/865 846/955/863 -f 846/955/863 856/966/874 858/969/876 -f 859/970/877 849/959/866 848/958/865 -f 848/958/865 858/969/876 859/970/877 -f 860/971/878 850/960/867 849/959/866 -f 849/959/866 859/970/877 860/971/878 -f 861/972/879 851/961/868 850/960/867 -f 850/960/867 860/971/878 861/972/879 -f 862/973/880 852/962/869 851/961/868 -f 851/961/868 861/972/879 862/973/880 -f 863/974/881 853/963/870 852/962/869 -f 852/962/869 862/973/880 863/974/881 -f 864/975/882 854/964/871 853/963/870 -f 853/963/870 863/974/881 864/975/882 -f 865/976/883 855/965/872 854/964/871 -f 854/964/871 864/975/882 865/976/883 -f 866/977/884 953/1091/873 855/965/872 -f 855/965/872 865/976/883 866/977/884 -f 867/979/885 856/966/874 857/967/875 -f 857/967/875 868/980/886 867/979/885 -f 869/982/887 858/969/876 856/966/874 -f 856/966/874 867/979/885 869/982/887 -f 870/983/888 859/970/877 858/969/876 -f 858/969/876 869/982/887 870/983/888 -f 871/984/889 860/971/878 859/970/877 -f 859/970/877 870/983/888 871/984/889 -f 872/985/890 861/972/879 860/971/878 -f 860/971/878 871/984/889 872/985/890 -f 873/986/891 862/973/880 861/972/879 -f 861/972/879 872/985/890 873/986/891 -f 874/987/892 863/974/881 862/973/880 -f 862/973/880 873/986/891 874/987/892 -f 875/988/893 864/975/882 863/974/881 -f 863/974/881 874/987/892 875/988/893 -f 876/989/894 865/976/883 864/975/882 -f 864/975/882 875/988/893 876/989/894 -f 877/990/895 866/977/884 865/976/883 -f 865/976/883 876/989/894 877/990/895 -f 878/992/896 867/979/885 868/980/886 -f 868/980/886 879/993/897 878/992/896 -f 880/995/898 869/982/887 867/979/885 -f 867/979/885 878/992/896 880/995/898 -f 881/996/899 870/983/888 869/982/887 -f 869/982/887 880/995/898 881/996/899 -f 882/997/900 871/984/889 870/983/888 -f 870/983/888 881/996/899 882/997/900 -f 883/998/901 872/985/890 871/984/889 -f 871/984/889 882/997/900 883/998/901 -f 884/999/902 873/986/891 872/985/890 -f 872/985/890 883/998/901 884/999/902 -f 885/1000/903 874/987/892 873/986/891 -f 873/986/891 884/999/902 885/1000/903 -f 886/1001/904 875/988/893 874/987/892 -f 874/987/892 885/1000/903 886/1001/904 -f 887/1002/905 876/989/894 875/988/893 -f 875/988/893 886/1001/904 887/1002/905 -f 888/1003/906 877/990/895 876/989/894 -f 876/989/894 887/1002/905 888/1003/906 -f 889/1005/907 878/992/896 879/993/897 -f 879/993/897 890/1006/908 889/1005/907 -f 891/1008/909 880/995/898 878/992/896 -f 878/992/896 889/1005/907 891/1008/909 -f 892/1009/910 881/996/899 880/995/898 -f 880/995/898 891/1008/909 892/1009/910 -f 893/1010/911 882/997/900 881/996/899 -f 881/996/899 892/1009/910 893/1010/911 -f 894/1011/912 883/998/901 882/997/900 -f 882/997/900 893/1010/911 894/1011/912 -f 895/1012/913 884/999/902 883/998/901 -f 883/998/901 894/1011/912 895/1012/913 -f 896/1013/914 885/1000/903 884/999/902 -f 884/999/902 895/1012/913 896/1013/914 -f 897/1014/915 886/1001/904 885/1000/903 -f 885/1000/903 896/1013/914 897/1014/915 -f 898/1015/916 887/1002/905 886/1001/904 -f 886/1001/904 897/1014/915 898/1015/916 -f 899/1016/917 888/1003/906 887/1002/905 -f 887/1002/905 898/1015/916 899/1016/917 -f 900/1018/918 889/1005/907 890/1006/908 -f 890/1006/908 901/1019/919 900/1018/918 -f 902/1021/920 891/1008/909 889/1005/907 -f 889/1005/907 900/1018/918 902/1021/920 -f 903/1022/921 892/1009/910 891/1008/909 -f 891/1008/909 902/1021/920 903/1022/921 -f 904/1023/922 893/1010/911 892/1009/910 -f 892/1009/910 903/1022/921 904/1023/922 -f 905/1024/923 894/1011/912 893/1010/911 -f 893/1010/911 904/1023/922 905/1024/923 -f 906/1025/924 895/1012/913 894/1011/912 -f 894/1011/912 905/1024/923 906/1025/924 -f 907/1026/925 896/1013/914 895/1012/913 -f 895/1012/913 906/1025/924 907/1026/925 -f 908/1027/926 897/1014/915 896/1013/914 -f 896/1013/914 907/1026/925 908/1027/926 -f 909/1028/927 898/1015/916 897/1014/915 -f 897/1014/915 908/1027/926 909/1028/927 -f 910/1029/928 899/1016/917 898/1015/916 -f 898/1015/916 909/1028/927 910/1029/928 -f 911/1031/929 900/1018/918 901/1019/919 -f 901/1019/919 912/1032/930 911/1031/929 -f 913/1034/931 902/1021/920 900/1018/918 -f 900/1018/918 911/1031/929 913/1034/931 -f 914/1035/932 903/1022/921 902/1021/920 -f 902/1021/920 913/1034/931 914/1035/932 -f 915/1036/933 904/1023/922 903/1022/921 -f 903/1022/921 914/1035/932 915/1036/933 -f 916/1037/934 905/1024/923 904/1023/922 -f 904/1023/922 915/1036/933 916/1037/934 -f 917/1038/935 906/1025/924 905/1024/923 -f 905/1024/923 916/1037/934 917/1038/935 -f 918/1039/936 907/1026/925 906/1025/924 -f 906/1025/924 917/1038/935 918/1039/936 -f 919/1040/937 908/1027/926 907/1026/925 -f 907/1026/925 918/1039/936 919/1040/937 -f 920/1041/938 909/1028/927 908/1027/926 -f 908/1027/926 919/1040/937 920/1041/938 -f 921/1042/939 910/1029/928 909/1028/927 -f 909/1028/927 920/1041/938 921/1042/939 -f 3241/3662/940 911/1031/929 912/1032/930 -f 912/1032/930 922/1044/941 3241/3662/940 -f 923/1048/942 913/1034/931 911/1031/929 -f 911/1031/929 3241/3662/940 923/1048/942 -f 3240/3660/943 914/1035/932 913/1034/931 -f 913/1034/931 923/1048/942 3240/3660/943 -f 3239/3658/944 915/1036/933 914/1035/932 -f 914/1035/932 3240/3660/943 3239/3658/944 -f 924/1050/945 916/1037/934 915/1036/933 -f 915/1036/933 3239/3658/944 924/1050/945 -f 925/1052/946 917/1038/935 916/1037/934 -f 916/1037/934 924/1050/945 925/1052/946 -f 3238/3656/947 918/1039/936 917/1038/935 -f 917/1038/935 925/1052/946 3238/3656/947 -f 3237/3654/948 919/1040/937 918/1039/936 -f 918/1039/936 3238/3656/947 3237/3654/948 -f 3236/3652/949 920/1041/938 919/1040/937 -f 919/1040/937 3237/3654/948 3236/3652/949 -f 926/1054/950 921/1042/939 920/1041/938 -f 920/1041/938 3236/3652/949 926/1054/950 -f 927/1058/951 628/698/641 538/600/550 -f 538/600/550 928/1060/851 927/1058/951 -f 929/1061/952 930/1063/642 628/698/641 -f 628/698/641 927/1058/951 929/1061/952 -f 931/1064/953 629/700/643 930/1063/642 -f 930/1063/642 929/1061/952 931/1064/953 -f 932/1065/954 933/1067/644 629/700/643 -f 629/700/643 931/1064/953 932/1065/954 -f 934/1068/955 935/1070/645 933/1067/644 -f 933/1067/644 932/1065/954 934/1068/955 -f 936/1071/956 937/1073/646 935/1070/645 -f 935/1070/645 934/1068/955 936/1071/956 -f 938/1074/957 630/702/647 937/1073/646 -f 937/1073/646 936/1071/956 938/1074/957 -f 939/1075/958 631/704/648 630/702/647 -f 630/702/647 938/1074/957 939/1075/958 -f 940/1076/959 941/1078/649 631/704/648 -f 631/704/648 939/1075/958 940/1076/959 -f 1028/1178/960 722/804/650 941/1078/649 -f 941/1078/649 940/1076/959 1028/1178/960 -f 942/1079/961 927/1058/951 928/1060/851 -f 928/1060/851 943/1081/862 942/1079/961 -f 944/1082/962 929/1061/952 927/1058/951 -f 927/1058/951 942/1079/961 944/1082/962 -f 945/1083/963 931/1064/953 929/1061/952 -f 929/1061/952 944/1082/962 945/1083/963 -f 946/1084/964 932/1065/954 931/1064/953 -f 931/1064/953 945/1083/963 946/1084/964 -f 947/1085/965 934/1068/955 932/1065/954 -f 932/1065/954 946/1084/964 947/1085/965 -f 948/1086/966 936/1071/956 934/1068/955 -f 934/1068/955 947/1085/965 948/1086/966 -f 949/1087/967 938/1074/957 936/1071/956 -f 936/1071/956 948/1086/966 949/1087/967 -f 950/1088/968 939/1075/958 938/1074/957 -f 938/1074/957 949/1087/967 950/1088/968 -f 951/1089/969 940/1076/959 939/1075/958 -f 939/1075/958 950/1088/968 951/1089/969 -f 1042/1197/970 1028/1178/960 940/1076/959 -f 940/1076/959 951/1089/969 1042/1197/970 -f 952/1090/971 942/1079/961 943/1081/862 -f 943/1081/862 953/1092/873 952/1090/971 -f 954/1093/972 944/1082/962 942/1079/961 -f 942/1079/961 952/1090/971 954/1093/972 -f 955/1094/973 945/1083/963 944/1082/962 -f 944/1082/962 954/1093/972 955/1094/973 -f 956/1095/974 946/1084/964 945/1083/963 -f 945/1083/963 955/1094/973 956/1095/974 -f 957/1096/975 947/1085/965 946/1084/964 -f 946/1084/964 956/1095/974 957/1096/975 -f 958/1097/976 948/1086/966 947/1085/965 -f 947/1085/965 957/1096/975 958/1097/976 -f 959/1098/977 949/1087/967 948/1086/966 -f 948/1086/966 958/1097/976 959/1098/977 -f 960/1099/978 950/1088/968 949/1087/967 -f 949/1087/967 959/1098/977 960/1099/978 -f 961/1100/979 951/1089/969 950/1088/968 -f 950/1088/968 960/1099/978 961/1100/979 -f 1052/1208/980 1042/1197/970 951/1089/969 -f 951/1089/969 961/1100/979 1052/1208/980 -f 962/1101/981 952/1090/971 953/1092/873 -f 953/1092/873 866/978/884 962/1101/981 -f 963/1102/982 954/1093/972 952/1090/971 -f 952/1090/971 962/1101/981 963/1102/982 -f 964/1103/983 955/1094/973 954/1093/972 -f 954/1093/972 963/1102/982 964/1103/983 -f 965/1104/984 956/1095/974 955/1094/973 -f 955/1094/973 964/1103/983 965/1104/984 -f 966/1105/985 957/1096/975 956/1095/974 -f 956/1095/974 965/1104/984 966/1105/985 -f 967/1106/986 958/1097/976 957/1096/975 -f 957/1096/975 966/1105/985 967/1106/986 -f 968/1107/987 959/1098/977 958/1097/976 -f 958/1097/976 967/1106/986 968/1107/987 -f 969/1108/988 960/1099/978 959/1098/977 -f 959/1098/977 968/1107/987 969/1108/988 -f 970/1109/989 961/1100/979 960/1099/978 -f 960/1099/978 969/1108/988 970/1109/989 -f 1062/1219/990 1052/1208/980 961/1100/979 -f 961/1100/979 970/1109/989 1062/1219/990 -f 971/1110/991 962/1101/981 866/978/884 -f 866/978/884 877/991/895 971/1110/991 -f 972/1111/992 963/1102/982 962/1101/981 -f 962/1101/981 971/1110/991 972/1111/992 -f 973/1112/993 964/1103/983 963/1102/982 -f 963/1102/982 972/1111/992 973/1112/993 -f 974/1113/994 965/1104/984 964/1103/983 -f 964/1103/983 973/1112/993 974/1113/994 -f 975/1114/995 966/1105/985 965/1104/984 -f 965/1104/984 974/1113/994 975/1114/995 -f 976/1115/996 967/1106/986 966/1105/985 -f 966/1105/985 975/1114/995 976/1115/996 -f 977/1116/997 968/1107/987 967/1106/986 -f 967/1106/986 976/1115/996 977/1116/997 -f 978/1117/998 969/1108/988 968/1107/987 -f 968/1107/987 977/1116/997 978/1117/998 -f 979/1118/999 970/1109/989 969/1108/988 -f 969/1108/988 978/1117/998 979/1118/999 -f 980/1119/1000 1062/1219/990 970/1109/989 -f 970/1109/989 979/1118/999 980/1119/1000 -f 981/1121/1001 971/1110/991 877/991/895 -f 877/991/895 888/1004/906 981/1121/1001 -f 982/1122/1002 972/1111/992 971/1110/991 -f 971/1110/991 981/1121/1001 982/1122/1002 -f 983/1123/1003 973/1112/993 972/1111/992 -f 972/1111/992 982/1122/1002 983/1123/1003 -f 984/1124/1004 974/1113/994 973/1112/993 -f 973/1112/993 983/1123/1003 984/1124/1004 -f 985/1125/1005 975/1114/995 974/1113/994 -f 974/1113/994 984/1124/1004 985/1125/1005 -f 986/1126/1006 976/1115/996 975/1114/995 -f 975/1114/995 985/1125/1005 986/1126/1006 -f 987/1127/1007 977/1116/997 976/1115/996 -f 976/1115/996 986/1126/1006 987/1127/1007 -f 988/1128/1008 978/1117/998 977/1116/997 -f 977/1116/997 987/1127/1007 988/1128/1008 -f 989/1129/1009 979/1118/999 978/1117/998 -f 978/1117/998 988/1128/1008 989/1129/1009 -f 990/1130/1010 980/1119/1000 979/1118/999 -f 979/1118/999 989/1129/1009 990/1130/1010 -f 991/1132/1011 981/1121/1001 888/1004/906 -f 888/1004/906 899/1017/917 991/1132/1011 -f 992/1133/1012 982/1122/1002 981/1121/1001 -f 981/1121/1001 991/1132/1011 992/1133/1012 -f 993/1134/1013 983/1123/1003 982/1122/1002 -f 982/1122/1002 992/1133/1012 993/1134/1013 -f 994/1135/1014 984/1124/1004 983/1123/1003 -f 983/1123/1003 993/1134/1013 994/1135/1014 -f 995/1136/1015 985/1125/1005 984/1124/1004 -f 984/1124/1004 994/1135/1014 995/1136/1015 -f 996/1137/1016 986/1126/1006 985/1125/1005 -f 985/1125/1005 995/1136/1015 996/1137/1016 -f 997/1138/1017 987/1127/1007 986/1126/1006 -f 986/1126/1006 996/1137/1016 997/1138/1017 -f 998/1139/1018 988/1128/1008 987/1127/1007 -f 987/1127/1007 997/1138/1017 998/1139/1018 -f 999/1140/1019 989/1129/1009 988/1128/1008 -f 988/1128/1008 998/1139/1018 999/1140/1019 -f 1091/1250/1020 990/1130/1010 989/1129/1009 -f 989/1129/1009 999/1140/1019 1091/1250/1020 -f 1000/1141/1021 991/1132/1011 899/1017/917 -f 899/1017/917 910/1030/928 1000/1141/1021 -f 1001/1142/1022 992/1133/1012 991/1132/1011 -f 991/1132/1011 1000/1141/1021 1001/1142/1022 -f 1002/1143/1023 993/1134/1013 992/1133/1012 -f 992/1133/1012 1001/1142/1022 1002/1143/1023 -f 1003/1144/1024 994/1135/1014 993/1134/1013 -f 993/1134/1013 1002/1143/1023 1003/1144/1024 -f 1004/1145/1025 995/1136/1015 994/1135/1014 -f 994/1135/1014 1003/1144/1024 1004/1145/1025 -f 1005/1146/1026 996/1137/1016 995/1136/1015 -f 995/1136/1015 1004/1145/1025 1005/1146/1026 -f 1006/1147/1027 997/1138/1017 996/1137/1016 -f 996/1137/1016 1005/1146/1026 1006/1147/1027 -f 1007/1148/1028 998/1139/1018 997/1138/1017 -f 997/1138/1017 1006/1147/1027 1007/1148/1028 -f 1008/1149/1029 999/1140/1019 998/1139/1018 -f 998/1139/1018 1007/1148/1028 1008/1149/1029 -f 1101/1261/1030 1091/1250/1020 999/1140/1019 -f 999/1140/1019 1008/1149/1029 1101/1261/1030 -f 1009/1150/1031 1000/1141/1021 910/1030/928 -f 910/1030/928 921/1043/939 1009/1150/1031 -f 1010/1151/1032 1001/1142/1022 1000/1141/1021 -f 1000/1141/1021 1009/1150/1031 1010/1151/1032 -f 1011/1152/1033 1002/1143/1023 1001/1142/1022 -f 1001/1142/1022 1010/1151/1032 1011/1152/1033 -f 1012/1153/1034 1003/1144/1024 1002/1143/1023 -f 1002/1143/1023 1011/1152/1033 1012/1153/1034 -f 1013/1154/1035 1004/1145/1025 1003/1144/1024 -f 1003/1144/1024 1012/1153/1034 1013/1154/1035 -f 1014/1155/1036 1005/1146/1026 1004/1145/1025 -f 1004/1145/1025 1013/1154/1035 1014/1155/1036 -f 1015/1156/1037 1006/1147/1027 1005/1146/1026 -f 1005/1146/1026 1014/1155/1036 1015/1156/1037 -f 1016/1157/1038 1007/1148/1028 1006/1147/1027 -f 1006/1147/1027 1015/1156/1037 1016/1157/1038 -f 1017/1158/1039 1008/1149/1029 1007/1148/1028 -f 1007/1148/1028 1016/1157/1038 1017/1158/1039 -f 1018/1159/1040 1101/1261/1030 1008/1149/1029 -f 1008/1149/1029 1017/1158/1039 1018/1159/1040 -f 3146/3553/1041 1009/1150/1031 921/1043/939 -f 921/1043/939 926/1055/950 3146/3553/1041 -f 1019/1161/1042 1010/1151/1032 1009/1150/1031 -f 1009/1150/1031 3146/3553/1041 1019/1161/1042 -f 1020/1163/1043 1011/1152/1033 1010/1151/1032 -f 1010/1151/1032 1019/1161/1042 1020/1163/1043 -f 1021/1165/1044 1012/1153/1034 1011/1152/1033 -f 1011/1152/1033 1020/1163/1043 1021/1165/1044 -f 1022/1167/1045 1013/1154/1035 1012/1153/1034 -f 1012/1153/1034 1021/1165/1044 1022/1167/1045 -f 1023/1169/1046 1014/1155/1036 1013/1154/1035 -f 1013/1154/1035 1022/1167/1045 1023/1169/1046 -f 1024/1171/1047 1015/1156/1037 1014/1155/1036 -f 1014/1155/1036 1023/1169/1046 1024/1171/1047 -f 1025/1173/1048 1016/1157/1038 1015/1156/1037 -f 1015/1156/1037 1024/1171/1047 1025/1173/1048 -f 1026/1175/1049 1017/1158/1039 1016/1157/1038 -f 1016/1157/1038 1025/1173/1048 1026/1175/1049 -f 1120/1282/1050 1018/1159/1040 1017/1158/1039 -f 1017/1158/1039 1026/1175/1049 1120/1282/1050 -f 1027/1177/1051 721/802/741 722/805/650 -f 722/805/650 1028/1179/960 1027/1177/1051 -f 1029/1180/1052 1030/1182/742 721/802/741 -f 721/802/741 1027/1177/1051 1029/1180/1052 -f 1031/1183/1053 1032/1185/743 1030/1182/742 -f 1030/1182/742 1029/1180/1052 1031/1183/1053 -f 1033/1186/1054 723/809/744 1032/1185/743 -f 1032/1185/743 1031/1183/1053 1033/1186/1054 -f 1034/1187/1055 724/811/745 723/809/744 -f 723/809/744 1033/1186/1054 1034/1187/1055 -f 1035/1188/1056 1036/1190/746 724/811/745 -f 724/811/745 1034/1187/1055 1035/1188/1056 -f 1037/1191/1057 725/813/747 1036/1190/746 -f 1036/1190/746 1035/1188/1056 1037/1191/1057 -f 1038/1192/1058 726/815/748 725/813/747 -f 725/813/747 1037/1191/1057 1038/1192/1058 -f 1039/1193/1059 1040/1195/749 726/815/748 -f 726/815/748 1038/1192/1058 1039/1193/1059 -f 1127/1299/1060 727/817/750 1040/1195/749 -f 1040/1195/749 1039/1193/1059 1127/1299/1060 -f 1041/1196/1061 1027/1177/1051 1028/1179/960 -f 1028/1179/960 1042/1198/970 1041/1196/1061 -f 1043/1199/1062 1029/1180/1052 1027/1177/1051 -f 1027/1177/1051 1041/1196/1061 1043/1199/1062 -f 1044/1200/1063 1031/1183/1053 1029/1180/1052 -f 1029/1180/1052 1043/1199/1062 1044/1200/1063 -f 1045/1201/1064 1033/1186/1054 1031/1183/1053 -f 1031/1183/1053 1044/1200/1063 1045/1201/1064 -f 1046/1202/1065 1034/1187/1055 1033/1186/1054 -f 1033/1186/1054 1045/1201/1064 1046/1202/1065 -f 1047/1203/1066 1035/1188/1056 1034/1187/1055 -f 1034/1187/1055 1046/1202/1065 1047/1203/1066 -f 1048/1204/1067 1037/1191/1057 1035/1188/1056 -f 1035/1188/1056 1047/1203/1066 1048/1204/1067 -f 1049/1205/1068 1038/1192/1058 1037/1191/1057 -f 1037/1191/1057 1048/1204/1067 1049/1205/1068 -f 1050/1206/1069 1039/1193/1059 1038/1192/1058 -f 1038/1192/1058 1049/1205/1068 1050/1206/1069 -f 1142/1320/1070 1127/1299/1060 1039/1193/1059 -f 1039/1193/1059 1050/1206/1069 1142/1320/1070 -f 1051/1207/1071 1041/1196/1061 1042/1198/970 -f 1042/1198/970 1052/1209/980 1051/1207/1071 -f 1053/1210/1072 1043/1199/1062 1041/1196/1061 -f 1041/1196/1061 1051/1207/1071 1053/1210/1072 -f 1054/1211/1073 1044/1200/1063 1043/1199/1062 -f 1043/1199/1062 1053/1210/1072 1054/1211/1073 -f 1055/1212/1074 1045/1201/1064 1044/1200/1063 -f 1044/1200/1063 1054/1211/1073 1055/1212/1074 -f 1056/1213/1075 1046/1202/1065 1045/1201/1064 -f 1045/1201/1064 1055/1212/1074 1056/1213/1075 -f 1057/1214/1076 1047/1203/1066 1046/1202/1065 -f 1046/1202/1065 1056/1213/1075 1057/1214/1076 -f 1058/1215/1077 1048/1204/1067 1047/1203/1066 -f 1047/1203/1066 1057/1214/1076 1058/1215/1077 -f 1059/1216/1078 1049/1205/1068 1048/1204/1067 -f 1048/1204/1067 1058/1215/1077 1059/1216/1078 -f 1060/1217/1079 1050/1206/1069 1049/1205/1068 -f 1049/1205/1068 1059/1216/1078 1060/1217/1079 -f 1153/1333/1080 1142/1320/1070 1050/1206/1069 -f 1050/1206/1069 1060/1217/1079 1153/1333/1080 -f 1061/1218/1081 1051/1207/1071 1052/1209/980 -f 1052/1209/980 1062/1220/990 1061/1218/1081 -f 1063/1221/1082 1053/1210/1072 1051/1207/1071 -f 1051/1207/1071 1061/1218/1081 1063/1221/1082 -f 1064/1222/1083 1054/1211/1073 1053/1210/1072 -f 1053/1210/1072 1063/1221/1082 1064/1222/1083 -f 1065/1223/1084 1055/1212/1074 1054/1211/1073 -f 1054/1211/1073 1064/1222/1083 1065/1223/1084 -f 1066/1224/1085 1056/1213/1075 1055/1212/1074 -f 1055/1212/1074 1065/1223/1084 1066/1224/1085 -f 1067/1225/1086 1057/1214/1076 1056/1213/1075 -f 1056/1213/1075 1066/1224/1085 1067/1225/1086 -f 1068/1226/1087 1058/1215/1077 1057/1214/1076 -f 1057/1214/1076 1067/1225/1086 1068/1226/1087 -f 1069/1227/1088 1059/1216/1078 1058/1215/1077 -f 1058/1215/1077 1068/1226/1087 1069/1227/1088 -f 1070/1228/1089 1060/1217/1079 1059/1216/1078 -f 1059/1216/1078 1069/1227/1088 1070/1228/1089 -f 1071/1229/1090 1153/1333/1080 1060/1217/1079 -f 1060/1217/1079 1070/1228/1089 1071/1229/1090 -f 1072/1231/1091 1061/1218/1081 1062/1220/990 -f 1062/1220/990 980/1120/1000 1072/1231/1091 -f 1073/1232/1092 1063/1221/1082 1061/1218/1081 -f 1061/1218/1081 1072/1231/1091 1073/1232/1092 -f 1074/1233/1093 1064/1222/1083 1063/1221/1082 -f 1063/1221/1082 1073/1232/1092 1074/1233/1093 -f 1075/1234/1094 1065/1223/1084 1064/1222/1083 -f 1064/1222/1083 1074/1233/1093 1075/1234/1094 -f 1076/1235/1095 1066/1224/1085 1065/1223/1084 -f 1065/1223/1084 1075/1234/1094 1076/1235/1095 -f 1077/1236/1096 1067/1225/1086 1066/1224/1085 -f 1066/1224/1085 1076/1235/1095 1077/1236/1096 -f 1078/1237/1097 1068/1226/1087 1067/1225/1086 -f 1067/1225/1086 1077/1236/1096 1078/1237/1097 -f 1079/1238/1098 1069/1227/1088 1068/1226/1087 -f 1068/1226/1087 1078/1237/1097 1079/1238/1098 -f 1080/1239/1099 1070/1228/1089 1069/1227/1088 -f 1069/1227/1088 1079/1238/1098 1080/1239/1099 -f 1172/1353/1100 1071/1229/1090 1070/1228/1089 -f 1070/1228/1089 1080/1239/1099 1172/1353/1100 -f 1081/1240/1101 1072/1231/1091 980/1120/1000 -f 980/1120/1000 990/1131/1010 1081/1240/1101 -f 1082/1241/1102 1073/1232/1092 1072/1231/1091 -f 1072/1231/1091 1081/1240/1101 1082/1241/1102 -f 1083/1242/1103 1074/1233/1093 1073/1232/1092 -f 1073/1232/1092 1082/1241/1102 1083/1242/1103 -f 1084/1243/1104 1075/1234/1094 1074/1233/1093 -f 1074/1233/1093 1083/1242/1103 1084/1243/1104 -f 1085/1244/1105 1076/1235/1095 1075/1234/1094 -f 1075/1234/1094 1084/1243/1104 1085/1244/1105 -f 1086/1245/1106 1077/1236/1096 1076/1235/1095 -f 1076/1235/1095 1085/1244/1105 1086/1245/1106 -f 1087/1246/1107 1078/1237/1097 1077/1236/1096 -f 1077/1236/1096 1086/1245/1106 1087/1246/1107 -f 1088/1247/1108 1079/1238/1098 1078/1237/1097 -f 1078/1237/1097 1087/1246/1107 1088/1247/1108 -f 1089/1248/1109 1080/1239/1099 1079/1238/1098 -f 1079/1238/1098 1088/1247/1108 1089/1248/1109 -f 1182/1364/1110 1172/1353/1100 1080/1239/1099 -f 1080/1239/1099 1089/1248/1109 1182/1364/1110 -f 1090/1249/1111 1081/1240/1101 990/1131/1010 -f 990/1131/1010 1091/1251/1020 1090/1249/1111 -f 1092/1252/1112 1082/1241/1102 1081/1240/1101 -f 1081/1240/1101 1090/1249/1111 1092/1252/1112 -f 1093/1253/1113 1083/1242/1103 1082/1241/1102 -f 1082/1241/1102 1092/1252/1112 1093/1253/1113 -f 1094/1254/1114 1084/1243/1104 1083/1242/1103 -f 1083/1242/1103 1093/1253/1113 1094/1254/1114 -f 1095/1255/1115 1085/1244/1105 1084/1243/1104 -f 1084/1243/1104 1094/1254/1114 1095/1255/1115 -f 1096/1256/1116 1086/1245/1106 1085/1244/1105 -f 1085/1244/1105 1095/1255/1115 1096/1256/1116 -f 1097/1257/1117 1087/1246/1107 1086/1245/1106 -f 1086/1245/1106 1096/1256/1116 1097/1257/1117 -f 1098/1258/1118 1088/1247/1108 1087/1246/1107 -f 1087/1246/1107 1097/1257/1117 1098/1258/1118 -f 1099/1259/1119 1089/1248/1109 1088/1247/1108 -f 1088/1247/1108 1098/1258/1118 1099/1259/1119 -f 1192/1375/1120 1182/1364/1110 1089/1248/1109 -f 1089/1248/1109 1099/1259/1119 1192/1375/1120 -f 1100/1260/1121 1090/1249/1111 1091/1251/1020 -f 1091/1251/1020 1101/1262/1030 1100/1260/1121 -f 1102/1263/1122 1092/1252/1112 1090/1249/1111 -f 1090/1249/1111 1100/1260/1121 1102/1263/1122 -f 1103/1264/1123 1093/1253/1113 1092/1252/1112 -f 1092/1252/1112 1102/1263/1122 1103/1264/1123 -f 1104/1265/1124 1094/1254/1114 1093/1253/1113 -f 1093/1253/1113 1103/1264/1123 1104/1265/1124 -f 1105/1266/1125 1095/1255/1115 1094/1254/1114 -f 1094/1254/1114 1104/1265/1124 1105/1266/1125 -f 1106/1267/1126 1096/1256/1116 1095/1255/1115 -f 1095/1255/1115 1105/1266/1125 1106/1267/1126 -f 1107/1268/1127 1097/1257/1117 1096/1256/1116 -f 1096/1256/1116 1106/1267/1126 1107/1268/1127 -f 1108/1269/1128 1098/1258/1118 1097/1257/1117 -f 1097/1257/1117 1107/1268/1127 1108/1269/1128 -f 1109/1270/1129 1099/1259/1119 1098/1258/1118 -f 1098/1258/1118 1108/1269/1128 1109/1270/1129 -f 1202/1386/1130 1192/1375/1120 1099/1259/1119 -f 1099/1259/1119 1109/1270/1129 1202/1386/1130 -f 1110/1271/1131 1100/1260/1121 1101/1262/1030 -f 1101/1262/1030 1018/1160/1040 1110/1271/1131 -f 1111/1272/1132 1102/1263/1122 1100/1260/1121 -f 1100/1260/1121 1110/1271/1131 1111/1272/1132 -f 1112/1273/1133 1103/1264/1123 1102/1263/1122 -f 1102/1263/1122 1111/1272/1132 1112/1273/1133 -f 1113/1274/1134 1104/1265/1124 1103/1264/1123 -f 1103/1264/1123 1112/1273/1133 1113/1274/1134 -f 1114/1275/1135 1105/1266/1125 1104/1265/1124 -f 1104/1265/1124 1113/1274/1134 1114/1275/1135 -f 1115/1276/1136 1106/1267/1126 1105/1266/1125 -f 1105/1266/1125 1114/1275/1135 1115/1276/1136 -f 1116/1277/1137 1107/1268/1127 1106/1267/1126 -f 1106/1267/1126 1115/1276/1136 1116/1277/1137 -f 1117/1278/1138 1108/1269/1128 1107/1268/1127 -f 1107/1268/1127 1116/1277/1137 1117/1278/1138 -f 1118/1279/1139 1109/1270/1129 1108/1269/1128 -f 1108/1269/1128 1117/1278/1138 1118/1279/1139 -f 1212/1397/1140 1202/1386/1130 1109/1270/1129 -f 1109/1270/1129 1118/1279/1139 1212/1397/1140 -f 1119/1280/1141 1110/1271/1131 1018/1160/1040 -f 1018/1160/1040 1120/1283/1050 1119/1280/1141 -f 3055/3452/1142 1111/1272/1132 1110/1271/1131 -f 1110/1271/1131 1119/1280/1141 3055/3452/1142 -f 3054/3450/1143 1112/1273/1133 1111/1272/1132 -f 1111/1272/1132 3055/3452/1142 3054/3450/1143 -f 1121/1286/1144 1113/1274/1134 1112/1273/1133 -f 1112/1273/1133 3054/3450/1143 1121/1286/1144 -f 1122/1288/1145 1114/1275/1135 1113/1274/1134 -f 1113/1274/1134 1121/1286/1144 1122/1288/1145 -f 3053/3448/1146 1115/1276/1136 1114/1275/1135 -f 1114/1275/1135 1122/1288/1145 3053/3448/1146 -f 1123/1290/1147 1116/1277/1137 1115/1276/1136 -f 1115/1276/1136 3053/3448/1146 1123/1290/1147 -f 3052/3446/1148 1117/1278/1138 1116/1277/1137 -f 1116/1277/1137 1123/1290/1147 3052/3446/1148 -f 1124/1292/1149 1118/1279/1139 1117/1278/1138 -f 1117/1278/1138 3052/3446/1148 1124/1292/1149 -f 1125/1294/1150 1212/1397/1140 1118/1279/1139 -f 1118/1279/1139 1124/1292/1149 1125/1294/1150 -f 1126/1298/1151 817/916/832 727/818/750 -f 727/818/750 1127/1300/1060 1126/1298/1151 -f 1128/1301/1152 1129/1303/833 817/916/832 -f 817/916/832 1126/1298/1151 1128/1301/1152 -f 1130/1304/1153 1131/1306/834 1129/1303/833 -f 1129/1303/833 1128/1301/1152 1130/1304/1153 -f 1132/1307/1154 1133/1309/835 1131/1306/834 -f 1131/1306/834 1130/1304/1153 1132/1307/1154 -f 1134/1310/1155 818/918/836 1133/1309/835 -f 1133/1309/835 1132/1307/1154 1134/1310/1155 -f 1135/1311/1156 1136/1313/837 818/918/836 -f 818/918/836 1134/1310/1155 1135/1311/1156 -f 1137/1314/1157 819/920/838 1136/1313/837 -f 1136/1313/837 1135/1311/1156 1137/1314/1157 -f 1138/1315/1158 820/922/839 819/920/838 -f 819/920/838 1137/1314/1157 1138/1315/1158 -f 1139/1316/1159 821/924/840 820/922/839 -f 820/922/839 1138/1315/1158 1139/1316/1159 -f 1140/1318/842 535/593/541 821/924/840 -f 821/924/840 1139/1316/1159 1140/1318/842 -f 1141/1319/1160 1126/1298/1151 1127/1300/1060 -f 1127/1300/1060 1142/1321/1070 1141/1319/1160 -f 1143/1322/1161 1128/1301/1152 1126/1298/1151 -f 1126/1298/1151 1141/1319/1160 1143/1322/1161 -f 1144/1323/1162 1130/1304/1153 1128/1301/1152 -f 1128/1301/1152 1143/1322/1161 1144/1323/1162 -f 1145/1324/1163 1132/1307/1154 1130/1304/1153 -f 1130/1304/1153 1144/1323/1162 1145/1324/1163 -f 1146/1325/1164 1134/1310/1155 1132/1307/1154 -f 1132/1307/1154 1145/1324/1163 1146/1325/1164 -f 1147/1326/1165 1135/1311/1156 1134/1310/1155 -f 1134/1310/1155 1146/1325/1164 1147/1326/1165 -f 1148/1327/1166 1137/1314/1157 1135/1311/1156 -f 1135/1311/1156 1147/1326/1165 1148/1327/1166 -f 1149/1328/1167 1138/1315/1158 1137/1314/1157 -f 1137/1314/1157 1148/1327/1166 1149/1328/1167 -f 1150/1329/1168 1139/1316/1159 1138/1315/1158 -f 1138/1315/1158 1149/1328/1167 1150/1329/1168 -f 1151/1331/853 1140/1318/842 1139/1316/1159 -f 1139/1316/1159 1150/1329/1168 1151/1331/853 -f 1152/1332/1169 1141/1319/1160 1142/1321/1070 -f 1142/1321/1070 1153/1334/1080 1152/1332/1169 -f 1154/1335/1170 1143/1322/1161 1141/1319/1160 -f 1141/1319/1160 1152/1332/1169 1154/1335/1170 -f 1155/1336/1171 1144/1323/1162 1143/1322/1161 -f 1143/1322/1161 1154/1335/1170 1155/1336/1171 -f 1156/1337/1172 1145/1324/1163 1144/1323/1162 -f 1144/1323/1162 1155/1336/1171 1156/1337/1172 -f 1157/1338/1173 1146/1325/1164 1145/1324/1163 -f 1145/1324/1163 1156/1337/1172 1157/1338/1173 -f 1158/1339/1174 1147/1326/1165 1146/1325/1164 -f 1146/1325/1164 1157/1338/1173 1158/1339/1174 -f 1159/1340/1175 1148/1327/1166 1147/1326/1165 -f 1147/1326/1165 1158/1339/1174 1159/1340/1175 -f 1160/1341/1176 1149/1328/1167 1148/1327/1166 -f 1148/1327/1166 1159/1340/1175 1160/1341/1176 -f 1161/1342/1177 1150/1329/1168 1149/1328/1167 -f 1149/1328/1167 1160/1341/1176 1161/1342/1177 -f 847/957/864 1151/1331/853 1150/1329/1168 -f 1150/1329/1168 1161/1342/1177 847/957/864 -f 1162/1343/1178 1152/1332/1169 1153/1334/1080 -f 1153/1334/1080 1071/1230/1090 1162/1343/1178 -f 1163/1344/1179 1154/1335/1170 1152/1332/1169 -f 1152/1332/1169 1162/1343/1178 1163/1344/1179 -f 1164/1345/1180 1155/1336/1171 1154/1335/1170 -f 1154/1335/1170 1163/1344/1179 1164/1345/1180 -f 1165/1346/1181 1156/1337/1172 1155/1336/1171 -f 1155/1336/1171 1164/1345/1180 1165/1346/1181 -f 1166/1347/1182 1157/1338/1173 1156/1337/1172 -f 1156/1337/1172 1165/1346/1181 1166/1347/1182 -f 1167/1348/1183 1158/1339/1174 1157/1338/1173 -f 1157/1338/1173 1166/1347/1182 1167/1348/1183 -f 1168/1349/1184 1159/1340/1175 1158/1339/1174 -f 1158/1339/1174 1167/1348/1183 1168/1349/1184 -f 1169/1350/1185 1160/1341/1176 1159/1340/1175 -f 1159/1340/1175 1168/1349/1184 1169/1350/1185 -f 1170/1351/1186 1161/1342/1177 1160/1341/1176 -f 1160/1341/1176 1169/1350/1185 1170/1351/1186 -f 857/968/875 847/957/864 1161/1342/1177 -f 1161/1342/1177 1170/1351/1186 857/968/875 -f 1171/1352/1187 1162/1343/1178 1071/1230/1090 -f 1071/1230/1090 1172/1354/1100 1171/1352/1187 -f 1173/1355/1188 1163/1344/1179 1162/1343/1178 -f 1162/1343/1178 1171/1352/1187 1173/1355/1188 -f 1174/1356/1189 1164/1345/1180 1163/1344/1179 -f 1163/1344/1179 1173/1355/1188 1174/1356/1189 -f 1175/1357/1190 1165/1346/1181 1164/1345/1180 -f 1164/1345/1180 1174/1356/1189 1175/1357/1190 -f 1176/1358/1191 1166/1347/1182 1165/1346/1181 -f 1165/1346/1181 1175/1357/1190 1176/1358/1191 -f 1177/1359/1192 1167/1348/1183 1166/1347/1182 -f 1166/1347/1182 1176/1358/1191 1177/1359/1192 -f 1178/1360/1193 1168/1349/1184 1167/1348/1183 -f 1167/1348/1183 1177/1359/1192 1178/1360/1193 -f 1179/1361/1194 1169/1350/1185 1168/1349/1184 -f 1168/1349/1184 1178/1360/1193 1179/1361/1194 -f 1180/1362/1195 1170/1351/1186 1169/1350/1185 -f 1169/1350/1185 1179/1361/1194 1180/1362/1195 -f 868/981/886 857/968/875 1170/1351/1186 -f 1170/1351/1186 1180/1362/1195 868/981/886 -f 1181/1363/1196 1171/1352/1187 1172/1354/1100 -f 1172/1354/1100 1182/1365/1110 1181/1363/1196 -f 1183/1366/1197 1173/1355/1188 1171/1352/1187 -f 1171/1352/1187 1181/1363/1196 1183/1366/1197 -f 1184/1367/1198 1174/1356/1189 1173/1355/1188 -f 1173/1355/1188 1183/1366/1197 1184/1367/1198 -f 1185/1368/1199 1175/1357/1190 1174/1356/1189 -f 1174/1356/1189 1184/1367/1198 1185/1368/1199 -f 1186/1369/1200 1176/1358/1191 1175/1357/1190 -f 1175/1357/1190 1185/1368/1199 1186/1369/1200 -f 1187/1370/1201 1177/1359/1192 1176/1358/1191 -f 1176/1358/1191 1186/1369/1200 1187/1370/1201 -f 1188/1371/1202 1178/1360/1193 1177/1359/1192 -f 1177/1359/1192 1187/1370/1201 1188/1371/1202 -f 1189/1372/1203 1179/1361/1194 1178/1360/1193 -f 1178/1360/1193 1188/1371/1202 1189/1372/1203 -f 1190/1373/1204 1180/1362/1195 1179/1361/1194 -f 1179/1361/1194 1189/1372/1203 1190/1373/1204 -f 879/994/897 868/981/886 1180/1362/1195 -f 1180/1362/1195 1190/1373/1204 879/994/897 -f 1191/1374/1205 1181/1363/1196 1182/1365/1110 -f 1182/1365/1110 1192/1376/1120 1191/1374/1205 -f 1193/1377/1206 1183/1366/1197 1181/1363/1196 -f 1181/1363/1196 1191/1374/1205 1193/1377/1206 -f 1194/1378/1207 1184/1367/1198 1183/1366/1197 -f 1183/1366/1197 1193/1377/1206 1194/1378/1207 -f 1195/1379/1208 1185/1368/1199 1184/1367/1198 -f 1184/1367/1198 1194/1378/1207 1195/1379/1208 -f 1196/1380/1209 1186/1369/1200 1185/1368/1199 -f 1185/1368/1199 1195/1379/1208 1196/1380/1209 -f 1197/1381/1210 1187/1370/1201 1186/1369/1200 -f 1186/1369/1200 1196/1380/1209 1197/1381/1210 -f 1198/1382/1211 1188/1371/1202 1187/1370/1201 -f 1187/1370/1201 1197/1381/1210 1198/1382/1211 -f 1199/1383/1212 1189/1372/1203 1188/1371/1202 -f 1188/1371/1202 1198/1382/1211 1199/1383/1212 -f 1200/1384/1213 1190/1373/1204 1189/1372/1203 -f 1189/1372/1203 1199/1383/1212 1200/1384/1213 -f 890/1007/908 879/994/897 1190/1373/1204 -f 1190/1373/1204 1200/1384/1213 890/1007/908 -f 1201/1385/1214 1191/1374/1205 1192/1376/1120 -f 1192/1376/1120 1202/1387/1130 1201/1385/1214 -f 1203/1388/1215 1193/1377/1206 1191/1374/1205 -f 1191/1374/1205 1201/1385/1214 1203/1388/1215 -f 1204/1389/1216 1194/1378/1207 1193/1377/1206 -f 1193/1377/1206 1203/1388/1215 1204/1389/1216 -f 1205/1390/1217 1195/1379/1208 1194/1378/1207 -f 1194/1378/1207 1204/1389/1216 1205/1390/1217 -f 1206/1391/1218 1196/1380/1209 1195/1379/1208 -f 1195/1379/1208 1205/1390/1217 1206/1391/1218 -f 1207/1392/1219 1197/1381/1210 1196/1380/1209 -f 1196/1380/1209 1206/1391/1218 1207/1392/1219 -f 1208/1393/1220 1198/1382/1211 1197/1381/1210 -f 1197/1381/1210 1207/1392/1219 1208/1393/1220 -f 1209/1394/1221 1199/1383/1212 1198/1382/1211 -f 1198/1382/1211 1208/1393/1220 1209/1394/1221 -f 1210/1395/1222 1200/1384/1213 1199/1383/1212 -f 1199/1383/1212 1209/1394/1221 1210/1395/1222 -f 901/1020/919 890/1007/908 1200/1384/1213 -f 1200/1384/1213 1210/1395/1222 901/1020/919 -f 1211/1396/1223 1201/1385/1214 1202/1387/1130 -f 1202/1387/1130 1212/1398/1140 1211/1396/1223 -f 1213/1399/1224 1203/1388/1215 1201/1385/1214 -f 1201/1385/1214 1211/1396/1223 1213/1399/1224 -f 1214/1400/1225 1204/1389/1216 1203/1388/1215 -f 1203/1388/1215 1213/1399/1224 1214/1400/1225 -f 1215/1401/1226 1205/1390/1217 1204/1389/1216 -f 1204/1389/1216 1214/1400/1225 1215/1401/1226 -f 1216/1402/1227 1206/1391/1218 1205/1390/1217 -f 1205/1390/1217 1215/1401/1226 1216/1402/1227 -f 1217/1403/1228 1207/1392/1219 1206/1391/1218 -f 1206/1391/1218 1216/1402/1227 1217/1403/1228 -f 1218/1404/1229 1208/1393/1220 1207/1392/1219 -f 1207/1392/1219 1217/1403/1228 1218/1404/1229 -f 1219/1405/1230 1209/1394/1221 1208/1393/1220 -f 1208/1393/1220 1218/1404/1229 1219/1405/1230 -f 1220/1406/1231 1210/1395/1222 1209/1394/1221 -f 1209/1394/1221 1219/1405/1230 1220/1406/1231 -f 912/1033/930 901/1020/919 1210/1395/1222 -f 1210/1395/1222 1220/1406/1231 912/1033/930 -f 1221/1407/1232 1211/1396/1223 1212/1398/1140 -f 1212/1398/1140 1125/1295/1150 1221/1407/1232 -f 2962/3347/1233 1213/1399/1224 1211/1396/1223 -f 1211/1396/1223 1221/1407/1232 2962/3347/1233 -f 1222/1409/1234 1214/1400/1225 1213/1399/1224 -f 1213/1399/1224 2962/3347/1233 1222/1409/1234 -f 2961/3345/1235 1215/1401/1226 1214/1400/1225 -f 1214/1400/1225 1222/1409/1234 2961/3345/1235 -f 1223/1411/1236 1216/1402/1227 1215/1401/1226 -f 1215/1401/1226 2961/3345/1235 1223/1411/1236 -f 2960/3343/1237 1217/1403/1228 1216/1402/1227 -f 1216/1402/1227 1223/1411/1236 2960/3343/1237 -f 2959/3341/1238 1218/1404/1229 1217/1403/1228 -f 1217/1403/1228 2960/3343/1237 2959/3341/1238 -f 2958/3339/1239 1219/1405/1230 1218/1404/1229 -f 1218/1404/1229 2959/3341/1238 2958/3339/1239 -f 1224/1413/1240 1220/1406/1231 1219/1405/1230 -f 1219/1405/1230 2958/3339/1239 1224/1413/1240 -f 922/1045/941 912/1033/930 1220/1406/1231 -f 1220/1406/1231 1224/1413/1240 922/1045/941 -f 1225/1415/1241 1226/1416/1242 1227/1417/1243 -f 1227/1417/1243 1228/1418/1244 1225/1415/1241 -f 1229/1419/1245 1230/1420/1246 1226/1416/1242 -f 1226/1416/1242 1225/1415/1241 1229/1419/1245 -f 1231/1421/1247 1232/1422/1248 1230/1420/1246 -f 1230/1420/1246 1229/1419/1245 1231/1421/1247 -f 1233/1423/1249 1234/1424/1250 1232/1422/1248 -f 1232/1422/1248 1231/1421/1247 1233/1423/1249 -f 1235/1425/1251 1236/1426/1252 1234/1424/1250 -f 1234/1424/1250 1233/1423/1249 1235/1425/1251 -f 1237/1427/1253 1238/1428/1254 1236/1426/1252 -f 1236/1426/1252 1235/1425/1251 1237/1427/1253 -f 1239/1429/1255 1240/1430/1256 1238/1428/1254 -f 1238/1428/1254 1237/1427/1253 1239/1429/1255 -f 1241/1431/1257 1242/1432/1258 1240/1430/1256 -f 1240/1430/1256 1239/1429/1255 1241/1431/1257 -f 1243/1433/1259 1244/1434/1260 1242/1432/1258 -f 1242/1432/1258 1241/1431/1257 1243/1433/1259 -f 1245/1435/1261 1246/1436/1262 1244/1434/1260 -f 1244/1434/1260 1243/1433/1259 1245/1435/1261 -f 1247/1437/1263 1225/1415/1241 1228/1418/1244 -f 1228/1418/1244 1248/1438/1264 1247/1437/1263 -f 1249/1439/1265 1229/1419/1245 1225/1415/1241 -f 1225/1415/1241 1247/1437/1263 1249/1439/1265 -f 1250/1440/1266 1231/1421/1247 1229/1419/1245 -f 1229/1419/1245 1249/1439/1265 1250/1440/1266 -f 1251/1441/1267 1233/1423/1249 1231/1421/1247 -f 1231/1421/1247 1250/1440/1266 1251/1441/1267 -f 1252/1442/1268 1235/1425/1251 1233/1423/1249 -f 1233/1423/1249 1251/1441/1267 1252/1442/1268 -f 1253/1443/1269 1237/1427/1253 1235/1425/1251 -f 1235/1425/1251 1252/1442/1268 1253/1443/1269 -f 1254/1444/1270 1239/1429/1255 1237/1427/1253 -f 1237/1427/1253 1253/1443/1269 1254/1444/1270 -f 1255/1445/1271 1241/1431/1257 1239/1429/1255 -f 1239/1429/1255 1254/1444/1270 1255/1445/1271 -f 1256/1446/1272 1243/1433/1259 1241/1431/1257 -f 1241/1431/1257 1255/1445/1271 1256/1446/1272 -f 1357/1547/1273 1245/1435/1261 1243/1433/1259 -f 1243/1433/1259 1256/1446/1272 1357/1547/1273 -f 1257/1447/1274 1247/1437/1263 1248/1438/1264 -f 1248/1438/1264 1258/1448/1275 1257/1447/1274 -f 1259/1449/1276 1249/1439/1265 1247/1437/1263 -f 1247/1437/1263 1257/1447/1274 1259/1449/1276 -f 1260/1450/1277 1250/1440/1266 1249/1439/1265 -f 1249/1439/1265 1259/1449/1276 1260/1450/1277 -f 1261/1451/1278 1251/1441/1267 1250/1440/1266 -f 1250/1440/1266 1260/1450/1277 1261/1451/1278 -f 1262/1452/1279 1252/1442/1268 1251/1441/1267 -f 1251/1441/1267 1261/1451/1278 1262/1452/1279 -f 1263/1453/1280 1253/1443/1269 1252/1442/1268 -f 1252/1442/1268 1262/1452/1279 1263/1453/1280 -f 1264/1454/1281 1254/1444/1270 1253/1443/1269 -f 1253/1443/1269 1263/1453/1280 1264/1454/1281 -f 1265/1455/1282 1255/1445/1271 1254/1444/1270 -f 1254/1444/1270 1264/1454/1281 1265/1455/1282 -f 1266/1456/1283 1256/1446/1272 1255/1445/1271 -f 1255/1445/1271 1265/1455/1282 1266/1456/1283 -f 1267/1457/1284 1357/1547/1273 1256/1446/1272 -f 1256/1446/1272 1266/1456/1283 1267/1457/1284 -f 1268/1458/1285 1257/1447/1274 1258/1448/1275 -f 1258/1448/1275 1385/1575/1286 1268/1458/1285 -f 1269/1459/1287 1259/1449/1276 1257/1447/1274 -f 1257/1447/1274 1268/1458/1285 1269/1459/1287 -f 1270/1460/1288 1260/1450/1277 1259/1449/1276 -f 1259/1449/1276 1269/1459/1287 1270/1460/1288 -f 1271/1461/1289 1261/1451/1278 1260/1450/1277 -f 1260/1450/1277 1270/1460/1288 1271/1461/1289 -f 1272/1462/1290 1262/1452/1279 1261/1451/1278 -f 1261/1451/1278 1271/1461/1289 1272/1462/1290 -f 1273/1463/1291 1263/1453/1280 1262/1452/1279 -f 1262/1452/1279 1272/1462/1290 1273/1463/1291 -f 1274/1464/1292 1264/1454/1281 1263/1453/1280 -f 1263/1453/1280 1273/1463/1291 1274/1464/1292 -f 1275/1465/1293 1265/1455/1282 1264/1454/1281 -f 1264/1454/1281 1274/1464/1292 1275/1465/1293 -f 1276/1466/1294 1266/1456/1283 1265/1455/1282 -f 1265/1455/1282 1275/1465/1293 1276/1466/1294 -f 1376/1566/1295 1267/1457/1284 1266/1456/1283 -f 1266/1456/1283 1276/1466/1294 1376/1566/1295 -f 1277/1467/1296 1268/1458/1285 1385/1575/1286 -f 1385/1575/1286 1395/1585/1297 1277/1467/1296 -f 1278/1468/1298 1269/1459/1287 1268/1458/1285 -f 1268/1458/1285 1277/1467/1296 1278/1468/1298 -f 1279/1469/1299 1270/1460/1288 1269/1459/1287 -f 1269/1459/1287 1278/1468/1298 1279/1469/1299 -f 1280/1470/1300 1271/1461/1289 1270/1460/1288 -f 1270/1460/1288 1279/1469/1299 1280/1470/1300 -f 1281/1471/1301 1272/1462/1290 1271/1461/1289 -f 1271/1461/1289 1280/1470/1300 1281/1471/1301 -f 1282/1472/1302 1273/1463/1291 1272/1462/1290 -f 1272/1462/1290 1281/1471/1301 1282/1472/1302 -f 1283/1473/1303 1274/1464/1292 1273/1463/1291 -f 1273/1463/1291 1282/1472/1302 1283/1473/1303 -f 1284/1474/1304 1275/1465/1293 1274/1464/1292 -f 1274/1464/1292 1283/1473/1303 1284/1474/1304 -f 1285/1475/1305 1276/1466/1294 1275/1465/1293 -f 1275/1465/1293 1284/1474/1304 1285/1475/1305 -f 1286/1476/1306 1376/1566/1295 1276/1466/1294 -f 1276/1466/1294 1285/1475/1305 1286/1476/1306 -f 1287/1477/1307 1277/1467/1296 1395/1585/1297 -f 1395/1585/1297 1406/1596/1308 1287/1477/1307 -f 1288/1478/1309 1278/1468/1298 1277/1467/1296 -f 1277/1467/1296 1287/1477/1307 1288/1478/1309 -f 1289/1479/1310 1279/1469/1299 1278/1468/1298 -f 1278/1468/1298 1288/1478/1309 1289/1479/1310 -f 1290/1480/1311 1280/1470/1300 1279/1469/1299 -f 1279/1469/1299 1289/1479/1310 1290/1480/1311 -f 1291/1481/1312 1281/1471/1301 1280/1470/1300 -f 1280/1470/1300 1290/1480/1311 1291/1481/1312 -f 1292/1482/1313 1282/1472/1302 1281/1471/1301 -f 1281/1471/1301 1291/1481/1312 1292/1482/1313 -f 1293/1483/1314 1283/1473/1303 1282/1472/1302 -f 1282/1472/1302 1292/1482/1313 1293/1483/1314 -f 1294/1484/1315 1284/1474/1304 1283/1473/1303 -f 1283/1473/1303 1293/1483/1314 1294/1484/1315 -f 1295/1485/1316 1285/1475/1305 1284/1474/1304 -f 1284/1474/1304 1294/1484/1315 1295/1485/1316 -f 1397/1587/1317 1286/1476/1306 1285/1475/1305 -f 1285/1475/1305 1295/1485/1316 1397/1587/1317 -f 1296/1486/1318 1287/1477/1307 1406/1596/1308 -f 1406/1596/1308 1297/1487/1319 1296/1486/1318 -f 1298/1488/1320 1288/1478/1309 1287/1477/1307 -f 1287/1477/1307 1296/1486/1318 1298/1488/1320 -f 1299/1489/1321 1289/1479/1310 1288/1478/1309 -f 1288/1478/1309 1298/1488/1320 1299/1489/1321 -f 1300/1490/1322 1290/1480/1311 1289/1479/1310 -f 1289/1479/1310 1299/1489/1321 1300/1490/1322 -f 1301/1491/1323 1291/1481/1312 1290/1480/1311 -f 1290/1480/1311 1300/1490/1322 1301/1491/1323 -f 1302/1492/1324 1292/1482/1313 1291/1481/1312 -f 1291/1481/1312 1301/1491/1323 1302/1492/1324 -f 1303/1493/1325 1293/1483/1314 1292/1482/1313 -f 1292/1482/1313 1302/1492/1324 1303/1493/1325 -f 1304/1494/1326 1294/1484/1315 1293/1483/1314 -f 1293/1483/1314 1303/1493/1325 1304/1494/1326 -f 1305/1495/1327 1295/1485/1316 1294/1484/1315 -f 1294/1484/1315 1304/1494/1326 1305/1495/1327 -f 1306/1496/1328 1397/1587/1317 1295/1485/1316 -f 1295/1485/1316 1305/1495/1327 1306/1496/1328 -f 1307/1497/1329 1296/1486/1318 1297/1487/1319 -f 1297/1487/1319 1425/1615/1330 1307/1497/1329 -f 1308/1498/1331 1298/1488/1320 1296/1486/1318 -f 1296/1486/1318 1307/1497/1329 1308/1498/1331 -f 1309/1499/1332 1299/1489/1321 1298/1488/1320 -f 1298/1488/1320 1308/1498/1331 1309/1499/1332 -f 1310/1500/1333 1300/1490/1322 1299/1489/1321 -f 1299/1489/1321 1309/1499/1332 1310/1500/1333 -f 1311/1501/1334 1301/1491/1323 1300/1490/1322 -f 1300/1490/1322 1310/1500/1333 1311/1501/1334 -f 1312/1502/1335 1302/1492/1324 1301/1491/1323 -f 1301/1491/1323 1311/1501/1334 1312/1502/1335 -f 1313/1503/1336 1303/1493/1325 1302/1492/1324 -f 1302/1492/1324 1312/1502/1335 1313/1503/1336 -f 1314/1504/1337 1304/1494/1326 1303/1493/1325 -f 1303/1493/1325 1313/1503/1336 1314/1504/1337 -f 1315/1505/1338 1305/1495/1327 1304/1494/1326 -f 1304/1494/1326 1314/1504/1337 1315/1505/1338 -f 1316/1506/1339 1306/1496/1328 1305/1495/1327 -f 1305/1495/1327 1315/1505/1338 1316/1506/1339 -f 1317/1507/1340 1307/1497/1329 1425/1615/1330 -f 1425/1615/1330 1318/1508/1341 1317/1507/1340 -f 1319/1509/1342 1308/1498/1331 1307/1497/1329 -f 1307/1497/1329 1317/1507/1340 1319/1509/1342 -f 1320/1510/1343 1309/1499/1332 1308/1498/1331 -f 1308/1498/1331 1319/1509/1342 1320/1510/1343 -f 1321/1511/1344 1310/1500/1333 1309/1499/1332 -f 1309/1499/1332 1320/1510/1343 1321/1511/1344 -f 1322/1512/1345 1311/1501/1334 1310/1500/1333 -f 1310/1500/1333 1321/1511/1344 1322/1512/1345 -f 1323/1513/1346 1312/1502/1335 1311/1501/1334 -f 1311/1501/1334 1322/1512/1345 1323/1513/1346 -f 1324/1514/1347 1313/1503/1336 1312/1502/1335 -f 1312/1502/1335 1323/1513/1346 1324/1514/1347 -f 1325/1515/1348 1314/1504/1337 1313/1503/1336 -f 1313/1503/1336 1324/1514/1347 1325/1515/1348 -f 1326/1516/1349 1315/1505/1338 1314/1504/1337 -f 1314/1504/1337 1325/1515/1348 1326/1516/1349 -f 1427/1617/1350 1316/1506/1339 1315/1505/1338 -f 1315/1505/1338 1326/1516/1349 1427/1617/1350 -f 1327/1517/1351 1317/1507/1340 1318/1508/1341 -f 1318/1508/1341 1328/1518/1352 1327/1517/1351 -f 1329/1519/1353 1319/1509/1342 1317/1507/1340 -f 1317/1507/1340 1327/1517/1351 1329/1519/1353 -f 1330/1520/1354 1320/1510/1343 1319/1509/1342 -f 1319/1509/1342 1329/1519/1353 1330/1520/1354 -f 1331/1521/1355 1321/1511/1344 1320/1510/1343 -f 1320/1510/1343 1330/1520/1354 1331/1521/1355 -f 1332/1522/1356 1322/1512/1345 1321/1511/1344 -f 1321/1511/1344 1331/1521/1355 1332/1522/1356 -f 1333/1523/1357 1323/1513/1346 1322/1512/1345 -f 1322/1512/1345 1332/1522/1356 1333/1523/1357 -f 1334/1524/1358 1324/1514/1347 1323/1513/1346 -f 1323/1513/1346 1333/1523/1357 1334/1524/1358 -f 1335/1525/1359 1325/1515/1348 1324/1514/1347 -f 1324/1514/1347 1334/1524/1358 1335/1525/1359 -f 1336/1526/1360 1326/1516/1349 1325/1515/1348 -f 1325/1515/1348 1335/1525/1359 1336/1526/1360 -f 1337/1527/1361 1427/1617/1350 1326/1516/1349 -f 1326/1516/1349 1336/1526/1360 1337/1527/1361 -f 1338/1528/1362 1339/1529/1363 1246/1436/1262 -f 1246/1436/1262 1245/1435/1261 1338/1528/1362 -f 1340/1530/1364 1341/1531/1365 1339/1529/1363 -f 1339/1529/1363 1338/1528/1362 1340/1530/1364 -f 1342/1532/1366 1343/1533/1367 1341/1531/1365 -f 1341/1531/1365 1340/1530/1364 1342/1532/1366 -f 1344/1534/1368 1345/1535/1369 1343/1533/1367 -f 1343/1533/1367 1342/1532/1366 1344/1534/1368 -f 1346/1536/1370 1347/1537/1371 1345/1535/1369 -f 1345/1535/1369 1344/1534/1368 1346/1536/1370 -f 1348/1538/1372 1349/1539/1373 1347/1537/1371 -f 1347/1537/1371 1346/1536/1370 1348/1538/1372 -f 1350/1540/1374 1351/1541/1375 1349/1539/1373 -f 1349/1539/1373 1348/1538/1372 1350/1540/1374 -f 1352/1542/1376 1353/1543/1377 1351/1541/1375 -f 1351/1541/1375 1350/1540/1374 1352/1542/1376 -f 1354/1544/1378 1355/1545/1379 1353/1543/1377 -f 1353/1543/1377 1352/1542/1376 1354/1544/1378 -f 1228/1418/1244 1227/1417/1243 1355/1545/1379 -f 1355/1545/1379 1354/1544/1378 1228/1418/1244 -f 1356/1546/1380 1338/1528/1362 1245/1435/1261 -f 1245/1435/1261 1357/1547/1273 1356/1546/1380 -f 1358/1548/1381 1340/1530/1364 1338/1528/1362 -f 1338/1528/1362 1356/1546/1380 1358/1548/1381 -f 1359/1549/1382 1342/1532/1366 1340/1530/1364 -f 1340/1530/1364 1358/1548/1381 1359/1549/1382 -f 1360/1550/1383 1344/1534/1368 1342/1532/1366 -f 1342/1532/1366 1359/1549/1382 1360/1550/1383 -f 1361/1551/1384 1346/1536/1370 1344/1534/1368 -f 1344/1534/1368 1360/1550/1383 1361/1551/1384 -f 1362/1552/1385 1348/1538/1372 1346/1536/1370 -f 1346/1536/1370 1361/1551/1384 1362/1552/1385 -f 1363/1553/1386 1350/1540/1374 1348/1538/1372 -f 1348/1538/1372 1362/1552/1385 1363/1553/1386 -f 1364/1554/1387 1352/1542/1376 1350/1540/1374 -f 1350/1540/1374 1363/1553/1386 1364/1554/1387 -f 1365/1555/1388 1354/1544/1378 1352/1542/1376 -f 1352/1542/1376 1364/1554/1387 1365/1555/1388 -f 1248/1438/1264 1228/1418/1244 1354/1544/1378 -f 1354/1544/1378 1365/1555/1388 1248/1438/1264 -f 1366/1556/1389 1356/1546/1380 1357/1547/1273 -f 1357/1547/1273 1267/1457/1284 1366/1556/1389 -f 1367/1557/1390 1358/1548/1381 1356/1546/1380 -f 1356/1546/1380 1366/1556/1389 1367/1557/1390 -f 1368/1558/1391 1359/1549/1382 1358/1548/1381 -f 1358/1548/1381 1367/1557/1390 1368/1558/1391 -f 1369/1559/1392 1360/1550/1383 1359/1549/1382 -f 1359/1549/1382 1368/1558/1391 1369/1559/1392 -f 1370/1560/1393 1361/1551/1384 1360/1550/1383 -f 1360/1550/1383 1369/1559/1392 1370/1560/1393 -f 1371/1561/1394 1362/1552/1385 1361/1551/1384 -f 1361/1551/1384 1370/1560/1393 1371/1561/1394 -f 1372/1562/1395 1363/1553/1386 1362/1552/1385 -f 1362/1552/1385 1371/1561/1394 1372/1562/1395 -f 1373/1563/1396 1364/1554/1387 1363/1553/1386 -f 1363/1553/1386 1372/1562/1395 1373/1563/1396 -f 1374/1564/1397 1365/1555/1388 1364/1554/1387 -f 1364/1554/1387 1373/1563/1396 1374/1564/1397 -f 1258/1448/1275 1248/1438/1264 1365/1555/1388 -f 1365/1555/1388 1374/1564/1397 1258/1448/1275 -f 1375/1565/1398 1366/1556/1389 1267/1457/1284 -f 1267/1457/1284 1376/1566/1295 1375/1565/1398 -f 1377/1567/1399 1367/1557/1390 1366/1556/1389 -f 1366/1556/1389 1375/1565/1398 1377/1567/1399 -f 1378/1568/1400 1368/1558/1391 1367/1557/1390 -f 1367/1557/1390 1377/1567/1399 1378/1568/1400 -f 1379/1569/1401 1369/1559/1392 1368/1558/1391 -f 1368/1558/1391 1378/1568/1400 1379/1569/1401 -f 1380/1570/1402 1370/1560/1393 1369/1559/1392 -f 1369/1559/1392 1379/1569/1401 1380/1570/1402 -f 1381/1571/1403 1371/1561/1394 1370/1560/1393 -f 1370/1560/1393 1380/1570/1402 1381/1571/1403 -f 1382/1572/1404 1372/1562/1395 1371/1561/1394 -f 1371/1561/1394 1381/1571/1403 1382/1572/1404 -f 1383/1573/1405 1373/1563/1396 1372/1562/1395 -f 1372/1562/1395 1382/1572/1404 1383/1573/1405 -f 1384/1574/1406 1374/1564/1397 1373/1563/1396 -f 1373/1563/1396 1383/1573/1405 1384/1574/1406 -f 1385/1575/1286 1258/1448/1275 1374/1564/1397 -f 1374/1564/1397 1384/1574/1406 1385/1575/1286 -f 1386/1576/1407 1375/1565/1398 1376/1566/1295 -f 1376/1566/1295 1286/1476/1306 1386/1576/1407 -f 1387/1577/1408 1377/1567/1399 1375/1565/1398 -f 1375/1565/1398 1386/1576/1407 1387/1577/1408 -f 1388/1578/1409 1378/1568/1400 1377/1567/1399 -f 1377/1567/1399 1387/1577/1408 1388/1578/1409 -f 1389/1579/1410 1379/1569/1401 1378/1568/1400 -f 1378/1568/1400 1388/1578/1409 1389/1579/1410 -f 1390/1580/1411 1380/1570/1402 1379/1569/1401 -f 1379/1569/1401 1389/1579/1410 1390/1580/1411 -f 1391/1581/1412 1381/1571/1403 1380/1570/1402 -f 1380/1570/1402 1390/1580/1411 1391/1581/1412 -f 1392/1582/1413 1382/1572/1404 1381/1571/1403 -f 1381/1571/1403 1391/1581/1412 1392/1582/1413 -f 1393/1583/1414 1383/1573/1405 1382/1572/1404 -f 1382/1572/1404 1392/1582/1413 1393/1583/1414 -f 1394/1584/1415 1384/1574/1406 1383/1573/1405 -f 1383/1573/1405 1393/1583/1414 1394/1584/1415 -f 1395/1585/1297 1385/1575/1286 1384/1574/1406 -f 1384/1574/1406 1394/1584/1415 1395/1585/1297 -f 1396/1586/1416 1386/1576/1407 1286/1476/1306 -f 1286/1476/1306 1397/1587/1317 1396/1586/1416 -f 1398/1588/1417 1387/1577/1408 1386/1576/1407 -f 1386/1576/1407 1396/1586/1416 1398/1588/1417 -f 1399/1589/1418 1388/1578/1409 1387/1577/1408 -f 1387/1577/1408 1398/1588/1417 1399/1589/1418 -f 1400/1590/1419 1389/1579/1410 1388/1578/1409 -f 1388/1578/1409 1399/1589/1418 1400/1590/1419 -f 1401/1591/1420 1390/1580/1411 1389/1579/1410 -f 1389/1579/1410 1400/1590/1419 1401/1591/1420 -f 1402/1592/1421 1391/1581/1412 1390/1580/1411 -f 1390/1580/1411 1401/1591/1420 1402/1592/1421 -f 1403/1593/1422 1392/1582/1413 1391/1581/1412 -f 1391/1581/1412 1402/1592/1421 1403/1593/1422 -f 1404/1594/1423 1393/1583/1414 1392/1582/1413 -f 1392/1582/1413 1403/1593/1422 1404/1594/1423 -f 1405/1595/1424 1394/1584/1415 1393/1583/1414 -f 1393/1583/1414 1404/1594/1423 1405/1595/1424 -f 1406/1596/1308 1395/1585/1297 1394/1584/1415 -f 1394/1584/1415 1405/1595/1424 1406/1596/1308 -f 1407/1597/1425 1396/1586/1416 1397/1587/1317 -f 1397/1587/1317 1306/1496/1328 1407/1597/1425 -f 1408/1598/1426 1398/1588/1417 1396/1586/1416 -f 1396/1586/1416 1407/1597/1425 1408/1598/1426 -f 1409/1599/1427 1399/1589/1418 1398/1588/1417 -f 1398/1588/1417 1408/1598/1426 1409/1599/1427 -f 1410/1600/1428 1400/1590/1419 1399/1589/1418 -f 1399/1589/1418 1409/1599/1427 1410/1600/1428 -f 1411/1601/1429 1401/1591/1420 1400/1590/1419 -f 1400/1590/1419 1410/1600/1428 1411/1601/1429 -f 1412/1602/1430 1402/1592/1421 1401/1591/1420 -f 1401/1591/1420 1411/1601/1429 1412/1602/1430 -f 1413/1603/1431 1403/1593/1422 1402/1592/1421 -f 1402/1592/1421 1412/1602/1430 1413/1603/1431 -f 1414/1604/1432 1404/1594/1423 1403/1593/1422 -f 1403/1593/1422 1413/1603/1431 1414/1604/1432 -f 1415/1605/1433 1405/1595/1424 1404/1594/1423 -f 1404/1594/1423 1414/1604/1432 1415/1605/1433 -f 1297/1487/1319 1406/1596/1308 1405/1595/1424 -f 1405/1595/1424 1415/1605/1433 1297/1487/1319 -f 1416/1606/1434 1407/1597/1425 1306/1496/1328 -f 1306/1496/1328 1316/1506/1339 1416/1606/1434 -f 1417/1607/1435 1408/1598/1426 1407/1597/1425 -f 1407/1597/1425 1416/1606/1434 1417/1607/1435 -f 1418/1608/1436 1409/1599/1427 1408/1598/1426 -f 1408/1598/1426 1417/1607/1435 1418/1608/1436 -f 1419/1609/1437 1410/1600/1428 1409/1599/1427 -f 1409/1599/1427 1418/1608/1436 1419/1609/1437 -f 1420/1610/1438 1411/1601/1429 1410/1600/1428 -f 1410/1600/1428 1419/1609/1437 1420/1610/1438 -f 1421/1611/1439 1412/1602/1430 1411/1601/1429 -f 1411/1601/1429 1420/1610/1438 1421/1611/1439 -f 1422/1612/1440 1413/1603/1431 1412/1602/1430 -f 1412/1602/1430 1421/1611/1439 1422/1612/1440 -f 1423/1613/1441 1414/1604/1432 1413/1603/1431 -f 1413/1603/1431 1422/1612/1440 1423/1613/1441 -f 1424/1614/1442 1415/1605/1433 1414/1604/1432 -f 1414/1604/1432 1423/1613/1441 1424/1614/1442 -f 1425/1615/1330 1297/1487/1319 1415/1605/1433 -f 1415/1605/1433 1424/1614/1442 1425/1615/1330 -f 1426/1616/1443 1416/1606/1434 1316/1506/1339 -f 1316/1506/1339 1427/1617/1350 1426/1616/1443 -f 1428/1618/1444 1417/1607/1435 1416/1606/1434 -f 1416/1606/1434 1426/1616/1443 1428/1618/1444 -f 1429/1619/1445 1418/1608/1436 1417/1607/1435 -f 1417/1607/1435 1428/1618/1444 1429/1619/1445 -f 1430/1620/1446 1419/1609/1437 1418/1608/1436 -f 1418/1608/1436 1429/1619/1445 1430/1620/1446 -f 1431/1621/1447 1420/1610/1438 1419/1609/1437 -f 1419/1609/1437 1430/1620/1446 1431/1621/1447 -f 1432/1622/1448 1421/1611/1439 1420/1610/1438 -f 1420/1610/1438 1431/1621/1447 1432/1622/1448 -f 1433/1623/1449 1422/1612/1440 1421/1611/1439 -f 1421/1611/1439 1432/1622/1448 1433/1623/1449 -f 1434/1624/1450 1423/1613/1441 1422/1612/1440 -f 1422/1612/1440 1433/1623/1449 1434/1624/1450 -f 1435/1625/1451 1424/1614/1442 1423/1613/1441 -f 1423/1613/1441 1434/1624/1450 1435/1625/1451 -f 1318/1508/1341 1425/1615/1330 1424/1614/1442 -f 1424/1614/1442 1435/1625/1451 1318/1508/1341 -f 1436/1626/1452 1426/1616/1443 1427/1617/1350 -f 1427/1617/1350 1337/1527/1361 1436/1626/1452 -f 1437/1627/1453 1428/1618/1444 1426/1616/1443 -f 1426/1616/1443 1436/1626/1452 1437/1627/1453 -f 1438/1628/1454 1429/1619/1445 1428/1618/1444 -f 1428/1618/1444 1437/1627/1453 1438/1628/1454 -f 1439/1629/1455 1430/1620/1446 1429/1619/1445 -f 1429/1619/1445 1438/1628/1454 1439/1629/1455 -f 1440/1630/1456 1431/1621/1447 1430/1620/1446 -f 1430/1620/1446 1439/1629/1455 1440/1630/1456 -f 1441/1631/1457 1432/1622/1448 1431/1621/1447 -f 1431/1621/1447 1440/1630/1456 1441/1631/1457 -f 1442/1632/1458 1433/1623/1449 1432/1622/1448 -f 1432/1622/1448 1441/1631/1457 1442/1632/1458 -f 1443/1633/1459 1434/1624/1450 1433/1623/1449 -f 1433/1623/1449 1442/1632/1458 1443/1633/1459 -f 1444/1634/1460 1435/1625/1451 1434/1624/1450 -f 1434/1624/1450 1443/1633/1459 1444/1634/1460 -f 1328/1518/1352 1318/1508/1341 1435/1625/1451 -f 1435/1625/1451 1444/1634/1460 1328/1518/1352 -f 1445/1635/1461 1327/1517/1351 1328/1518/1352 -f 1328/1518/1352 1446/1636/1462 1445/1635/1461 -f 1447/1637/1463 1329/1519/1353 1327/1517/1351 -f 1327/1517/1351 1445/1635/1461 1447/1637/1463 -f 1448/1638/1464 1330/1520/1354 1329/1519/1353 -f 1329/1519/1353 1447/1637/1463 1448/1638/1464 -f 1449/1639/1465 1331/1521/1355 1330/1520/1354 -f 1330/1520/1354 1448/1638/1464 1449/1639/1465 -f 1450/1640/1466 1332/1522/1356 1331/1521/1355 -f 1331/1521/1355 1449/1639/1465 1450/1640/1466 -f 1451/1641/1467 1333/1523/1357 1332/1522/1356 -f 1332/1522/1356 1450/1640/1466 1451/1641/1467 -f 1452/1642/1468 1334/1524/1358 1333/1523/1357 -f 1333/1523/1357 1451/1641/1467 1452/1642/1468 -f 1453/1643/1469 1335/1525/1359 1334/1524/1358 -f 1334/1524/1358 1452/1642/1468 1453/1643/1469 -f 1454/1644/1470 1336/1526/1360 1335/1525/1359 -f 1335/1525/1359 1453/1643/1469 1454/1644/1470 -f 1455/1645/1471 1337/1527/1361 1336/1526/1360 -f 1336/1526/1360 1454/1644/1470 1455/1645/1471 -f 1456/1646/1472 1445/1635/1461 1446/1636/1462 -f 1446/1636/1462 1565/1763/1473 1456/1646/1472 -f 1457/1647/1474 1447/1637/1463 1445/1635/1461 -f 1445/1635/1461 1456/1646/1472 1457/1647/1474 -f 1458/1648/1475 1448/1638/1464 1447/1637/1463 -f 1447/1637/1463 1457/1647/1474 1458/1648/1475 -f 1459/1649/1476 1449/1639/1465 1448/1638/1464 -f 1448/1638/1464 1458/1648/1475 1459/1649/1476 -f 1460/1650/1477 1450/1640/1466 1449/1639/1465 -f 1449/1639/1465 1459/1649/1476 1460/1650/1477 -f 1461/1651/1478 1451/1641/1467 1450/1640/1466 -f 1450/1640/1466 1460/1650/1477 1461/1651/1478 -f 1462/1652/1479 1452/1642/1468 1451/1641/1467 -f 1451/1641/1467 1461/1651/1478 1462/1652/1479 -f 1463/1653/1480 1453/1643/1469 1452/1642/1468 -f 1452/1642/1468 1462/1652/1479 1463/1653/1480 -f 1464/1654/1481 1454/1644/1470 1453/1643/1469 -f 1453/1643/1469 1463/1653/1480 1464/1654/1481 -f 1465/1655/1482 1455/1645/1471 1454/1644/1470 -f 1454/1644/1470 1464/1654/1481 1465/1655/1482 -f 1466/1657/1483 1456/1646/1472 1565/1763/1473 -f 1565/1763/1473 1467/1658/1484 1466/1657/1483 -f 1468/1659/1485 1457/1647/1474 1456/1646/1472 -f 1456/1646/1472 1466/1657/1483 1468/1659/1485 -f 1469/1660/1486 1458/1648/1475 1457/1647/1474 -f 1457/1647/1474 1468/1659/1485 1469/1660/1486 -f 1470/1661/1487 1459/1649/1476 1458/1648/1475 -f 1458/1648/1475 1469/1660/1486 1470/1661/1487 -f 1471/1662/1488 1460/1650/1477 1459/1649/1476 -f 1459/1649/1476 1470/1661/1487 1471/1662/1488 -f 1472/1663/1489 1461/1651/1478 1460/1650/1477 -f 1460/1650/1477 1471/1662/1488 1472/1663/1489 -f 1473/1664/1490 1462/1652/1479 1461/1651/1478 -f 1461/1651/1478 1472/1663/1489 1473/1664/1490 -f 1474/1665/1491 1463/1653/1480 1462/1652/1479 -f 1462/1652/1479 1473/1664/1490 1474/1665/1491 -f 1475/1666/1492 1464/1654/1481 1463/1653/1480 -f 1463/1653/1480 1474/1665/1491 1475/1666/1492 -f 1567/1766/1493 1465/1655/1482 1464/1654/1481 -f 1464/1654/1481 1475/1666/1492 1567/1766/1493 -f 1476/1667/1494 1466/1657/1483 1467/1658/1484 -f 1467/1658/1484 1477/1668/1495 1476/1667/1494 -f 1478/1669/1496 1468/1659/1485 1466/1657/1483 -f 1466/1657/1483 1476/1667/1494 1478/1669/1496 -f 1479/1670/1497 1469/1660/1486 1468/1659/1485 -f 1468/1659/1485 1478/1669/1496 1479/1670/1497 -f 1480/1671/1498 1470/1661/1487 1469/1660/1486 -f 1469/1660/1486 1479/1670/1497 1480/1671/1498 -f 1481/1672/1499 1471/1662/1488 1470/1661/1487 -f 1470/1661/1487 1480/1671/1498 1481/1672/1499 -f 1482/1673/1500 1472/1663/1489 1471/1662/1488 -f 1471/1662/1488 1481/1672/1499 1482/1673/1500 -f 1483/1674/1501 1473/1664/1490 1472/1663/1489 -f 1472/1663/1489 1482/1673/1500 1483/1674/1501 -f 1484/1675/1502 1474/1665/1491 1473/1664/1490 -f 1473/1664/1490 1483/1674/1501 1484/1675/1502 -f 1485/1676/1503 1475/1666/1492 1474/1665/1491 -f 1474/1665/1491 1484/1675/1502 1485/1676/1503 -f 1486/1677/1504 1567/1766/1493 1475/1666/1492 -f 1475/1666/1492 1485/1676/1503 1486/1677/1504 -f 1487/1679/1505 1476/1667/1494 1477/1668/1495 -f 1477/1668/1495 1488/1680/1506 1487/1679/1505 -f 1489/1682/1507 1478/1669/1496 1476/1667/1494 -f 1476/1667/1494 1487/1679/1505 1489/1682/1507 -f 1490/1683/1508 1479/1670/1497 1478/1669/1496 -f 1478/1669/1496 1489/1682/1507 1490/1683/1508 -f 1491/1684/1509 1480/1671/1498 1479/1670/1497 -f 1479/1670/1497 1490/1683/1508 1491/1684/1509 -f 1492/1685/1510 1481/1672/1499 1480/1671/1498 -f 1480/1671/1498 1491/1684/1509 1492/1685/1510 -f 1493/1686/1511 1482/1673/1500 1481/1672/1499 -f 1481/1672/1499 1492/1685/1510 1493/1686/1511 -f 1494/1687/1512 1483/1674/1501 1482/1673/1500 -f 1482/1673/1500 1493/1686/1511 1494/1687/1512 -f 1495/1688/1513 1484/1675/1502 1483/1674/1501 -f 1483/1674/1501 1494/1687/1512 1495/1688/1513 -f 1496/1689/1514 1485/1676/1503 1484/1675/1502 -f 1484/1675/1502 1495/1688/1513 1496/1689/1514 -f 1497/1690/1515 1486/1677/1504 1485/1676/1503 -f 1485/1676/1503 1496/1689/1514 1497/1690/1515 -f 1498/1692/1516 1487/1679/1505 1488/1680/1506 -f 1488/1680/1506 1499/1693/1517 1498/1692/1516 -f 1500/1695/1518 1489/1682/1507 1487/1679/1505 -f 1487/1679/1505 1498/1692/1516 1500/1695/1518 -f 1501/1696/1519 1490/1683/1508 1489/1682/1507 -f 1489/1682/1507 1500/1695/1518 1501/1696/1519 -f 1502/1697/1520 1491/1684/1509 1490/1683/1508 -f 1490/1683/1508 1501/1696/1519 1502/1697/1520 -f 1503/1698/1521 1492/1685/1510 1491/1684/1509 -f 1491/1684/1509 1502/1697/1520 1503/1698/1521 -f 1504/1699/1522 1493/1686/1511 1492/1685/1510 -f 1492/1685/1510 1503/1698/1521 1504/1699/1522 -f 1505/1700/1523 1494/1687/1512 1493/1686/1511 -f 1493/1686/1511 1504/1699/1522 1505/1700/1523 -f 1506/1701/1524 1495/1688/1513 1494/1687/1512 -f 1494/1687/1512 1505/1700/1523 1506/1701/1524 -f 1507/1702/1525 1496/1689/1514 1495/1688/1513 -f 1495/1688/1513 1506/1701/1524 1507/1702/1525 -f 1595/1795/1526 1497/1690/1515 1496/1689/1514 -f 1496/1689/1514 1507/1702/1525 1595/1795/1526 -f 1508/1703/1527 1498/1692/1516 1499/1693/1517 -f 1499/1693/1517 1509/1704/1528 1508/1703/1527 -f 1510/1706/1529 1500/1695/1518 1498/1692/1516 -f 1498/1692/1516 1508/1703/1527 1510/1706/1529 -f 1511/1707/1530 1501/1696/1519 1500/1695/1518 -f 1500/1695/1518 1510/1706/1529 1511/1707/1530 -f 1512/1708/1531 1502/1697/1520 1501/1696/1519 -f 1501/1696/1519 1511/1707/1530 1512/1708/1531 -f 1513/1709/1532 1503/1698/1521 1502/1697/1520 -f 1502/1697/1520 1512/1708/1531 1513/1709/1532 -f 1514/1710/1533 1504/1699/1522 1503/1698/1521 -f 1503/1698/1521 1513/1709/1532 1514/1710/1533 -f 1515/1711/1534 1505/1700/1523 1504/1699/1522 -f 1504/1699/1522 1514/1710/1533 1515/1711/1534 -f 1516/1712/1535 1506/1701/1524 1505/1700/1523 -f 1505/1700/1523 1515/1711/1534 1516/1712/1535 -f 1517/1713/1536 1507/1702/1525 1506/1701/1524 -f 1506/1701/1524 1516/1712/1535 1517/1713/1536 -f 1518/1714/1537 1595/1795/1526 1507/1702/1525 -f 1507/1702/1525 1517/1713/1536 1518/1714/1537 -f 1519/1716/1538 1508/1703/1527 1509/1704/1528 -f 1509/1704/1528 1622/1823/1539 1519/1716/1538 -f 1520/1717/1540 1510/1706/1529 1508/1703/1527 -f 1508/1703/1527 1519/1716/1538 1520/1717/1540 -f 1521/1718/1541 1511/1707/1530 1510/1706/1529 -f 1510/1706/1529 1520/1717/1540 1521/1718/1541 -f 1522/1719/1542 1512/1708/1531 1511/1707/1530 -f 1511/1707/1530 1521/1718/1541 1522/1719/1542 -f 1523/1720/1543 1513/1709/1532 1512/1708/1531 -f 1512/1708/1531 1522/1719/1542 1523/1720/1543 -f 1524/1721/1544 1514/1710/1533 1513/1709/1532 -f 1513/1709/1532 1523/1720/1543 1524/1721/1544 -f 1525/1722/1545 1515/1711/1534 1514/1710/1533 -f 1514/1710/1533 1524/1721/1544 1525/1722/1545 -f 1526/1723/1546 1516/1712/1535 1515/1711/1534 -f 1515/1711/1534 1525/1722/1545 1526/1723/1546 -f 1527/1724/1547 1517/1713/1536 1516/1712/1535 -f 1516/1712/1535 1526/1723/1546 1527/1724/1547 -f 1528/1725/1548 1518/1714/1537 1517/1713/1536 -f 1517/1713/1536 1527/1724/1547 1528/1725/1548 -f 1529/1727/1549 1519/1716/1538 1622/1823/1539 -f 1622/1823/1539 1633/1836/1550 1529/1727/1549 -f 1530/1728/1551 1520/1717/1540 1519/1716/1538 -f 1519/1716/1538 1529/1727/1549 1530/1728/1551 -f 1531/1729/1552 1521/1718/1541 1520/1717/1540 -f 1520/1717/1540 1530/1728/1551 1531/1729/1552 -f 1532/1730/1553 1522/1719/1542 1521/1718/1541 -f 1521/1718/1541 1531/1729/1552 1532/1730/1553 -f 1533/1731/1554 1523/1720/1543 1522/1719/1542 -f 1522/1719/1542 1532/1730/1553 1533/1731/1554 -f 1534/1732/1555 1524/1721/1544 1523/1720/1543 -f 1523/1720/1543 1533/1731/1554 1534/1732/1555 -f 1535/1733/1556 1525/1722/1545 1524/1721/1544 -f 1524/1721/1544 1534/1732/1555 1535/1733/1556 -f 1536/1734/1557 1526/1723/1546 1525/1722/1545 -f 1525/1722/1545 1535/1733/1556 1536/1734/1557 -f 1537/1735/1558 1527/1724/1547 1526/1723/1546 -f 1526/1723/1546 1536/1734/1557 1537/1735/1558 -f 1624/1826/1559 1528/1725/1548 1527/1724/1547 -f 1527/1724/1547 1537/1735/1558 1624/1826/1559 -f 1538/1736/1560 1529/1727/1549 1633/1836/1550 -f 1633/1836/1550 722/806/650 1538/1736/1560 -f 1539/1737/1561 1530/1728/1551 1529/1727/1549 -f 1529/1727/1549 1538/1736/1560 1539/1737/1561 -f 1540/1738/1562 1531/1729/1552 1530/1728/1551 -f 1530/1728/1551 1539/1737/1561 1540/1738/1562 -f 1541/1739/1563 1532/1730/1553 1531/1729/1552 -f 1531/1729/1552 1540/1738/1562 1541/1739/1563 -f 1542/1740/1564 1533/1731/1554 1532/1730/1553 -f 1532/1730/1553 1541/1739/1563 1542/1740/1564 -f 1543/1741/1565 1534/1732/1555 1533/1731/1554 -f 1533/1731/1554 1542/1740/1564 1543/1741/1565 -f 1544/1742/1566 1535/1733/1556 1534/1732/1555 -f 1534/1732/1555 1543/1741/1565 1544/1742/1566 -f 1545/1743/1567 1536/1734/1557 1535/1733/1556 -f 1535/1733/1556 1544/1742/1566 1545/1743/1567 -f 1546/1744/1568 1537/1735/1558 1536/1734/1557 -f 1536/1734/1557 1545/1743/1567 1546/1744/1568 -f 1635/1839/1569 1624/1826/1559 1537/1735/1558 -f 1537/1735/1558 1546/1744/1568 1635/1839/1569 -f 1547/1745/1570 1436/1626/1452 1337/1527/1361 -f 1337/1527/1361 1455/1645/1471 1547/1745/1570 -f 1548/1746/1571 1437/1627/1453 1436/1626/1452 -f 1436/1626/1452 1547/1745/1570 1548/1746/1571 -f 1549/1747/1572 1438/1628/1454 1437/1627/1453 -f 1437/1627/1453 1548/1746/1571 1549/1747/1572 -f 1550/1748/1573 1439/1629/1455 1438/1628/1454 -f 1438/1628/1454 1549/1747/1572 1550/1748/1573 -f 1551/1749/1574 1440/1630/1456 1439/1629/1455 -f 1439/1629/1455 1550/1748/1573 1551/1749/1574 -f 1552/1750/1575 1441/1631/1457 1440/1630/1456 -f 1440/1630/1456 1551/1749/1574 1552/1750/1575 -f 1553/1751/1576 1442/1632/1458 1441/1631/1457 -f 1441/1631/1457 1552/1750/1575 1553/1751/1576 -f 1554/1752/1577 1443/1633/1459 1442/1632/1458 -f 1442/1632/1458 1553/1751/1576 1554/1752/1577 -f 1555/1753/1578 1444/1634/1460 1443/1633/1459 -f 1443/1633/1459 1554/1752/1577 1555/1753/1578 -f 1446/1636/1462 1328/1518/1352 1444/1634/1460 -f 1444/1634/1460 1555/1753/1578 1446/1636/1462 -f 1556/1754/1579 1547/1745/1570 1455/1645/1471 -f 1455/1645/1471 1465/1656/1482 1556/1754/1579 -f 1557/1755/1580 1548/1746/1571 1547/1745/1570 -f 1547/1745/1570 1556/1754/1579 1557/1755/1580 -f 1558/1756/1581 1549/1747/1572 1548/1746/1571 -f 1548/1746/1571 1557/1755/1580 1558/1756/1581 -f 1559/1757/1582 1550/1748/1573 1549/1747/1572 -f 1549/1747/1572 1558/1756/1581 1559/1757/1582 -f 1560/1758/1583 1551/1749/1574 1550/1748/1573 -f 1550/1748/1573 1559/1757/1582 1560/1758/1583 -f 1561/1759/1584 1552/1750/1575 1551/1749/1574 -f 1551/1749/1574 1560/1758/1583 1561/1759/1584 -f 1562/1760/1585 1553/1751/1576 1552/1750/1575 -f 1552/1750/1575 1561/1759/1584 1562/1760/1585 -f 1563/1761/1586 1554/1752/1577 1553/1751/1576 -f 1553/1751/1576 1562/1760/1585 1563/1761/1586 -f 1564/1762/1587 1555/1753/1578 1554/1752/1577 -f 1554/1752/1577 1563/1761/1586 1564/1762/1587 -f 1565/1764/1473 1446/1636/1462 1555/1753/1578 -f 1555/1753/1578 1564/1762/1587 1565/1764/1473 -f 1566/1765/1588 1556/1754/1579 1465/1656/1482 -f 1465/1656/1482 1567/1767/1493 1566/1765/1588 -f 1568/1768/1589 1557/1755/1580 1556/1754/1579 -f 1556/1754/1579 1566/1765/1588 1568/1768/1589 -f 1569/1769/1590 1558/1756/1581 1557/1755/1580 -f 1557/1755/1580 1568/1768/1589 1569/1769/1590 -f 1570/1770/1591 1559/1757/1582 1558/1756/1581 -f 1558/1756/1581 1569/1769/1590 1570/1770/1591 -f 1571/1771/1592 1560/1758/1583 1559/1757/1582 -f 1559/1757/1582 1570/1770/1591 1571/1771/1592 -f 1572/1772/1593 1561/1759/1584 1560/1758/1583 -f 1560/1758/1583 1571/1771/1592 1572/1772/1593 -f 1573/1773/1594 1562/1760/1585 1561/1759/1584 -f 1561/1759/1584 1572/1772/1593 1573/1773/1594 -f 1574/1774/1595 1563/1761/1586 1562/1760/1585 -f 1562/1760/1585 1573/1773/1594 1574/1774/1595 -f 1575/1775/1596 1564/1762/1587 1563/1761/1586 -f 1563/1761/1586 1574/1774/1595 1575/1775/1596 -f 1467/1658/1484 1565/1764/1473 1564/1762/1587 -f 1564/1762/1587 1575/1775/1596 1467/1658/1484 -f 1576/1776/1597 1566/1765/1588 1567/1767/1493 -f 1567/1767/1493 1486/1678/1504 1576/1776/1597 -f 1577/1777/1598 1568/1768/1589 1566/1765/1588 -f 1566/1765/1588 1576/1776/1597 1577/1777/1598 -f 1578/1778/1599 1569/1769/1590 1568/1768/1589 -f 1568/1768/1589 1577/1777/1598 1578/1778/1599 -f 1579/1779/1600 1570/1770/1591 1569/1769/1590 -f 1569/1769/1590 1578/1778/1599 1579/1779/1600 -f 1580/1780/1601 1571/1771/1592 1570/1770/1591 -f 1570/1770/1591 1579/1779/1600 1580/1780/1601 -f 1581/1781/1602 1572/1772/1593 1571/1771/1592 -f 1571/1771/1592 1580/1780/1601 1581/1781/1602 -f 1582/1782/1603 1573/1773/1594 1572/1772/1593 -f 1572/1772/1593 1581/1781/1602 1582/1782/1603 -f 1583/1783/1604 1574/1774/1595 1573/1773/1594 -f 1573/1773/1594 1582/1782/1603 1583/1783/1604 -f 1584/1784/1605 1575/1775/1596 1574/1774/1595 -f 1574/1774/1595 1583/1783/1604 1584/1784/1605 -f 1477/1668/1495 1467/1658/1484 1575/1775/1596 -f 1575/1775/1596 1584/1784/1605 1477/1668/1495 -f 1585/1785/1606 1576/1776/1597 1486/1678/1504 -f 1486/1678/1504 1497/1691/1515 1585/1785/1606 -f 1586/1786/1607 1577/1777/1598 1576/1776/1597 -f 1576/1776/1597 1585/1785/1606 1586/1786/1607 -f 1587/1787/1608 1578/1778/1599 1577/1777/1598 -f 1577/1777/1598 1586/1786/1607 1587/1787/1608 -f 1588/1788/1609 1579/1779/1600 1578/1778/1599 -f 1578/1778/1599 1587/1787/1608 1588/1788/1609 -f 1589/1789/1610 1580/1780/1601 1579/1779/1600 -f 1579/1779/1600 1588/1788/1609 1589/1789/1610 -f 1590/1790/1611 1581/1781/1602 1580/1780/1601 -f 1580/1780/1601 1589/1789/1610 1590/1790/1611 -f 1591/1791/1612 1582/1782/1603 1581/1781/1602 -f 1581/1781/1602 1590/1790/1611 1591/1791/1612 -f 1592/1792/1613 1583/1783/1604 1582/1782/1603 -f 1582/1782/1603 1591/1791/1612 1592/1792/1613 -f 1593/1793/1614 1584/1784/1605 1583/1783/1604 -f 1583/1783/1604 1592/1792/1613 1593/1793/1614 -f 1488/1681/1506 1477/1668/1495 1584/1784/1605 -f 1584/1784/1605 1593/1793/1614 1488/1681/1506 -f 1594/1794/1615 1585/1785/1606 1497/1691/1515 -f 1497/1691/1515 1595/1796/1526 1594/1794/1615 -f 1596/1797/1616 1586/1786/1607 1585/1785/1606 -f 1585/1785/1606 1594/1794/1615 1596/1797/1616 -f 1597/1798/1617 1587/1787/1608 1586/1786/1607 -f 1586/1786/1607 1596/1797/1616 1597/1798/1617 -f 1598/1799/1618 1588/1788/1609 1587/1787/1608 -f 1587/1787/1608 1597/1798/1617 1598/1799/1618 -f 1599/1800/1619 1589/1789/1610 1588/1788/1609 -f 1588/1788/1609 1598/1799/1618 1599/1800/1619 -f 1600/1801/1620 1590/1790/1611 1589/1789/1610 -f 1589/1789/1610 1599/1800/1619 1600/1801/1620 -f 1601/1802/1621 1591/1791/1612 1590/1790/1611 -f 1590/1790/1611 1600/1801/1620 1601/1802/1621 -f 1602/1803/1622 1592/1792/1613 1591/1791/1612 -f 1591/1791/1612 1601/1802/1621 1602/1803/1622 -f 1603/1804/1623 1593/1793/1614 1592/1792/1613 -f 1592/1792/1613 1602/1803/1622 1603/1804/1623 -f 1499/1694/1517 1488/1681/1506 1593/1793/1614 -f 1593/1793/1614 1603/1804/1623 1499/1694/1517 -f 1604/1805/1624 1594/1794/1615 1595/1796/1526 -f 1595/1796/1526 1518/1715/1537 1604/1805/1624 -f 1605/1806/1625 1596/1797/1616 1594/1794/1615 -f 1594/1794/1615 1604/1805/1624 1605/1806/1625 -f 1606/1807/1626 1597/1798/1617 1596/1797/1616 -f 1596/1797/1616 1605/1806/1625 1606/1807/1626 -f 1607/1808/1627 1598/1799/1618 1597/1798/1617 -f 1597/1798/1617 1606/1807/1626 1607/1808/1627 -f 1608/1809/1628 1599/1800/1619 1598/1799/1618 -f 1598/1799/1618 1607/1808/1627 1608/1809/1628 -f 1609/1810/1629 1600/1801/1620 1599/1800/1619 -f 1599/1800/1619 1608/1809/1628 1609/1810/1629 -f 1610/1811/1630 1601/1802/1621 1600/1801/1620 -f 1600/1801/1620 1609/1810/1629 1610/1811/1630 -f 1611/1812/1631 1602/1803/1622 1601/1802/1621 -f 1601/1802/1621 1610/1811/1630 1611/1812/1631 -f 1612/1813/1632 1603/1804/1623 1602/1803/1622 -f 1602/1803/1622 1611/1812/1631 1612/1813/1632 -f 1509/1705/1528 1499/1694/1517 1603/1804/1623 -f 1603/1804/1623 1612/1813/1632 1509/1705/1528 -f 1613/1814/1633 1604/1805/1624 1518/1715/1537 -f 1518/1715/1537 1528/1726/1548 1613/1814/1633 -f 1614/1815/1634 1605/1806/1625 1604/1805/1624 -f 1604/1805/1624 1613/1814/1633 1614/1815/1634 -f 1615/1816/1635 1606/1807/1626 1605/1806/1625 -f 1605/1806/1625 1614/1815/1634 1615/1816/1635 -f 1616/1817/1636 1607/1808/1627 1606/1807/1626 -f 1606/1807/1626 1615/1816/1635 1616/1817/1636 -f 1617/1818/1637 1608/1809/1628 1607/1808/1627 -f 1607/1808/1627 1616/1817/1636 1617/1818/1637 -f 1618/1819/1638 1609/1810/1629 1608/1809/1628 -f 1608/1809/1628 1617/1818/1637 1618/1819/1638 -f 1619/1820/1639 1610/1811/1630 1609/1810/1629 -f 1609/1810/1629 1618/1819/1638 1619/1820/1639 -f 1620/1821/1640 1611/1812/1631 1610/1811/1630 -f 1610/1811/1630 1619/1820/1639 1620/1821/1640 -f 1621/1822/1641 1612/1813/1632 1611/1812/1631 -f 1611/1812/1631 1620/1821/1640 1621/1822/1641 -f 1622/1824/1539 1509/1705/1528 1612/1813/1632 -f 1612/1813/1632 1621/1822/1641 1622/1824/1539 -f 1623/1825/1642 1613/1814/1633 1528/1726/1548 -f 1528/1726/1548 1624/1827/1559 1623/1825/1642 -f 1625/1828/1643 1614/1815/1634 1613/1814/1633 -f 1613/1814/1633 1623/1825/1642 1625/1828/1643 -f 1626/1829/1644 1615/1816/1635 1614/1815/1634 -f 1614/1815/1634 1625/1828/1643 1626/1829/1644 -f 1627/1830/1645 1616/1817/1636 1615/1816/1635 -f 1615/1816/1635 1626/1829/1644 1627/1830/1645 -f 1628/1831/1646 1617/1818/1637 1616/1817/1636 -f 1616/1817/1636 1627/1830/1645 1628/1831/1646 -f 1629/1832/1647 1618/1819/1638 1617/1818/1637 -f 1617/1818/1637 1628/1831/1646 1629/1832/1647 -f 1630/1833/1648 1619/1820/1639 1618/1819/1638 -f 1618/1819/1638 1629/1832/1647 1630/1833/1648 -f 1631/1834/1649 1620/1821/1640 1619/1820/1639 -f 1619/1820/1639 1630/1833/1648 1631/1834/1649 -f 1632/1835/1650 1621/1822/1641 1620/1821/1640 -f 1620/1821/1640 1631/1834/1649 1632/1835/1650 -f 1633/1837/1550 1622/1824/1539 1621/1822/1641 -f 1621/1822/1641 1632/1835/1650 1633/1837/1550 -f 1634/1838/1651 1623/1825/1642 1624/1827/1559 -f 1624/1827/1559 1635/1840/1569 1634/1838/1651 -f 1636/1841/1652 1625/1828/1643 1623/1825/1642 -f 1623/1825/1642 1634/1838/1651 1636/1841/1652 -f 1637/1842/1653 1626/1829/1644 1625/1828/1643 -f 1625/1828/1643 1636/1841/1652 1637/1842/1653 -f 1638/1843/1654 1627/1830/1645 1626/1829/1644 -f 1626/1829/1644 1637/1842/1653 1638/1843/1654 -f 1639/1844/1655 1628/1831/1646 1627/1830/1645 -f 1627/1830/1645 1638/1843/1654 1639/1844/1655 -f 1640/1845/1656 1629/1832/1647 1628/1831/1646 -f 1628/1831/1646 1639/1844/1655 1640/1845/1656 -f 1641/1846/1657 1630/1833/1648 1629/1832/1647 -f 1629/1832/1647 1640/1845/1656 1641/1846/1657 -f 1642/1847/1658 1631/1834/1649 1630/1833/1648 -f 1630/1833/1648 1641/1846/1657 1642/1847/1658 -f 1643/1848/1659 1632/1835/1650 1631/1834/1649 -f 1631/1834/1649 1642/1847/1658 1643/1848/1659 -f 722/807/650 1633/1837/1550 1632/1835/1650 -f 1632/1835/1650 1643/1848/1659 722/807/650 -f 1644/1849/1660 1645/1850/1661 1646/1851/1662 -f 1646/1851/1662 1647/1853/1663 1644/1849/1660 -f 1648/1855/1664 1649/1856/1665 1645/1850/1661 -f 1645/1850/1661 1644/1849/1660 1648/1855/1664 -f 1650/1857/1666 1651/1858/1667 1649/1856/1665 -f 1649/1856/1665 1648/1855/1664 1650/1857/1666 -f 1652/1859/1668 1653/1860/1669 1651/1858/1667 -f 1651/1858/1667 1650/1857/1666 1652/1859/1668 -f 1654/1861/1670 1655/1862/1671 1653/1860/1669 -f 1653/1860/1669 1652/1859/1668 1654/1861/1670 -f 1656/1863/1672 1657/1864/1673 1655/1862/1671 -f 1655/1862/1671 1654/1861/1670 1656/1863/1672 -f 1658/1865/1674 1659/1866/1675 1657/1864/1673 -f 1657/1864/1673 1656/1863/1672 1658/1865/1674 -f 1660/1867/1676 1661/1868/1677 1659/1866/1675 -f 1659/1866/1675 1658/1865/1674 1660/1867/1676 -f 1662/1869/1678 1663/1870/1679 1661/1868/1677 -f 1661/1868/1677 1660/1867/1676 1662/1869/1678 -f 1748/1965/1680 1747/1963/1681 1663/1870/1679 -f 1663/1870/1679 1662/1869/1678 1748/1965/1680 -f 1664/1871/1682 1644/1849/1660 1647/1853/1663 -f 1647/1853/1663 1665/1872/1683 1664/1871/1682 -f 1666/1874/1684 1648/1855/1664 1644/1849/1660 -f 1644/1849/1660 1664/1871/1682 1666/1874/1684 -f 1667/1875/1685 1650/1857/1666 1648/1855/1664 -f 1648/1855/1664 1666/1874/1684 1667/1875/1685 -f 1668/1876/1686 1652/1859/1668 1650/1857/1666 -f 1650/1857/1666 1667/1875/1685 1668/1876/1686 -f 1669/1877/1687 1654/1861/1670 1652/1859/1668 -f 1652/1859/1668 1668/1876/1686 1669/1877/1687 -f 1670/1878/1688 1656/1863/1672 1654/1861/1670 -f 1654/1861/1670 1669/1877/1687 1670/1878/1688 -f 1671/1879/1689 1658/1865/1674 1656/1863/1672 -f 1656/1863/1672 1670/1878/1688 1671/1879/1689 -f 1672/1880/1690 1660/1867/1676 1658/1865/1674 -f 1658/1865/1674 1671/1879/1689 1672/1880/1690 -f 1673/1881/1691 1662/1869/1678 1660/1867/1676 -f 1660/1867/1676 1672/1880/1690 1673/1881/1691 -f 1766/1984/1692 1748/1965/1680 1662/1869/1678 -f 1662/1869/1678 1673/1881/1691 1766/1984/1692 -f 1674/1882/1693 1664/1871/1682 1665/1872/1683 -f 1665/1872/1683 1785/2005/1694 1674/1882/1693 -f 1675/1883/1695 1666/1874/1684 1664/1871/1682 -f 1664/1871/1682 1674/1882/1693 1675/1883/1695 -f 1676/1884/1696 1667/1875/1685 1666/1874/1684 -f 1666/1874/1684 1675/1883/1695 1676/1884/1696 -f 1677/1885/1697 1668/1876/1686 1667/1875/1685 -f 1667/1875/1685 1676/1884/1696 1677/1885/1697 -f 1678/1886/1698 1669/1877/1687 1668/1876/1686 -f 1668/1876/1686 1677/1885/1697 1678/1886/1698 -f 1679/1887/1699 1670/1878/1688 1669/1877/1687 -f 1669/1877/1687 1678/1886/1698 1679/1887/1699 -f 1680/1888/1700 1671/1879/1689 1670/1878/1688 -f 1670/1878/1688 1679/1887/1699 1680/1888/1700 -f 1681/1889/1701 1672/1880/1690 1671/1879/1689 -f 1671/1879/1689 1680/1888/1700 1681/1889/1701 -f 1682/1890/1702 1673/1881/1691 1672/1880/1690 -f 1672/1880/1690 1681/1889/1701 1682/1890/1702 -f 1776/1995/1703 1766/1984/1692 1673/1881/1691 -f 1673/1881/1691 1682/1890/1702 1776/1995/1703 -f 1683/1891/1704 1674/1882/1693 1785/2005/1694 -f 1785/2005/1694 1796/2018/1705 1683/1891/1704 -f 1684/1892/1706 1675/1883/1695 1674/1882/1693 -f 1674/1882/1693 1683/1891/1704 1684/1892/1706 -f 1685/1893/1707 1676/1884/1696 1675/1883/1695 -f 1675/1883/1695 1684/1892/1706 1685/1893/1707 -f 1686/1894/1708 1677/1885/1697 1676/1884/1696 -f 1676/1884/1696 1685/1893/1707 1686/1894/1708 -f 1687/1895/1709 1678/1886/1698 1677/1885/1697 -f 1677/1885/1697 1686/1894/1708 1687/1895/1709 -f 1688/1896/1710 1679/1887/1699 1678/1886/1698 -f 1678/1886/1698 1687/1895/1709 1688/1896/1710 -f 1689/1897/1711 1680/1888/1700 1679/1887/1699 -f 1679/1887/1699 1688/1896/1710 1689/1897/1711 -f 1690/1898/1712 1681/1889/1701 1680/1888/1700 -f 1680/1888/1700 1689/1897/1711 1690/1898/1712 -f 1691/1899/1713 1682/1890/1702 1681/1889/1701 -f 1681/1889/1701 1690/1898/1712 1691/1899/1713 -f 1787/2008/1714 1776/1995/1703 1682/1890/1702 -f 1682/1890/1702 1691/1899/1713 1787/2008/1714 -f 1692/1900/1715 1683/1891/1704 1796/2018/1705 -f 1796/2018/1705 1807/2031/1716 1692/1900/1715 -f 1693/1901/1717 1684/1892/1706 1683/1891/1704 -f 1683/1891/1704 1692/1900/1715 1693/1901/1717 -f 1694/1902/1718 1685/1893/1707 1684/1892/1706 -f 1684/1892/1706 1693/1901/1717 1694/1902/1718 -f 1695/1903/1719 1686/1894/1708 1685/1893/1707 -f 1685/1893/1707 1694/1902/1718 1695/1903/1719 -f 1696/1904/1720 1687/1895/1709 1686/1894/1708 -f 1686/1894/1708 1695/1903/1719 1696/1904/1720 -f 1697/1905/1721 1688/1896/1710 1687/1895/1709 -f 1687/1895/1709 1696/1904/1720 1697/1905/1721 -f 1698/1906/1722 1689/1897/1711 1688/1896/1710 -f 1688/1896/1710 1697/1905/1721 1698/1906/1722 -f 1699/1907/1723 1690/1898/1712 1689/1897/1711 -f 1689/1897/1711 1698/1906/1722 1699/1907/1723 -f 1700/1908/1724 1691/1899/1713 1690/1898/1712 -f 1690/1898/1712 1699/1907/1723 1700/1908/1724 -f 1798/2021/1725 1787/2008/1714 1691/1899/1713 -f 1691/1899/1713 1700/1908/1724 1798/2021/1725 -f 1701/1909/1726 1692/1900/1715 1807/2031/1716 -f 1807/2031/1716 1702/1910/1727 1701/1909/1726 -f 1703/1912/1728 1693/1901/1717 1692/1900/1715 -f 1692/1900/1715 1701/1909/1726 1703/1912/1728 -f 1704/1913/1729 1694/1902/1718 1693/1901/1717 -f 1693/1901/1717 1703/1912/1728 1704/1913/1729 -f 1705/1914/1730 1695/1903/1719 1694/1902/1718 -f 1694/1902/1718 1704/1913/1729 1705/1914/1730 -f 1706/1915/1731 1696/1904/1720 1695/1903/1719 -f 1695/1903/1719 1705/1914/1730 1706/1915/1731 -f 1707/1916/1732 1697/1905/1721 1696/1904/1720 -f 1696/1904/1720 1706/1915/1731 1707/1916/1732 -f 1708/1917/1733 1698/1906/1722 1697/1905/1721 -f 1697/1905/1721 1707/1916/1732 1708/1917/1733 -f 1709/1918/1734 1699/1907/1723 1698/1906/1722 -f 1698/1906/1722 1708/1917/1733 1709/1918/1734 -f 1710/1919/1735 1700/1908/1724 1699/1907/1723 -f 1699/1907/1723 1709/1918/1734 1710/1919/1735 -f 1809/2034/1736 1798/2021/1725 1700/1908/1724 -f 1700/1908/1724 1710/1919/1735 1809/2034/1736 -f 1711/1920/1737 1701/1909/1726 1702/1910/1727 -f 1702/1910/1727 1828/2055/1738 1711/1920/1737 -f 1712/1921/1739 1703/1912/1728 1701/1909/1726 -f 1701/1909/1726 1711/1920/1737 1712/1921/1739 -f 1713/1922/1740 1704/1913/1729 1703/1912/1728 -f 1703/1912/1728 1712/1921/1739 1713/1922/1740 -f 1714/1923/1741 1705/1914/1730 1704/1913/1729 -f 1704/1913/1729 1713/1922/1740 1714/1923/1741 -f 1715/1924/1742 1706/1915/1731 1705/1914/1730 -f 1705/1914/1730 1714/1923/1741 1715/1924/1742 -f 1716/1925/1743 1707/1916/1732 1706/1915/1731 -f 1706/1915/1731 1715/1924/1742 1716/1925/1743 -f 1717/1926/1744 1708/1917/1733 1707/1916/1732 -f 1707/1916/1732 1716/1925/1743 1717/1926/1744 -f 1718/1927/1745 1709/1918/1734 1708/1917/1733 -f 1708/1917/1733 1717/1926/1744 1718/1927/1745 -f 1719/1928/1746 1710/1919/1735 1709/1918/1734 -f 1709/1918/1734 1718/1927/1745 1719/1928/1746 -f 1819/2045/1747 1809/2034/1736 1710/1919/1735 -f 1710/1919/1735 1719/1928/1746 1819/2045/1747 -f 1720/1929/1748 1711/1920/1737 1828/2055/1738 -f 1828/2055/1738 1721/1930/1749 1720/1929/1748 -f 1722/1932/1750 1712/1921/1739 1711/1920/1737 -f 1711/1920/1737 1720/1929/1748 1722/1932/1750 -f 1723/1933/1751 1713/1922/1740 1712/1921/1739 -f 1712/1921/1739 1722/1932/1750 1723/1933/1751 -f 1724/1934/1752 1714/1923/1741 1713/1922/1740 -f 1713/1922/1740 1723/1933/1751 1724/1934/1752 -f 1725/1935/1753 1715/1924/1742 1714/1923/1741 -f 1714/1923/1741 1724/1934/1752 1725/1935/1753 -f 1726/1936/1754 1716/1925/1743 1715/1924/1742 -f 1715/1924/1742 1725/1935/1753 1726/1936/1754 -f 1727/1937/1755 1717/1926/1744 1716/1925/1743 -f 1716/1925/1743 1726/1936/1754 1727/1937/1755 -f 1728/1938/1756 1718/1927/1745 1717/1926/1744 -f 1717/1926/1744 1727/1937/1755 1728/1938/1756 -f 1729/1939/1757 1719/1928/1746 1718/1927/1745 -f 1718/1927/1745 1728/1938/1756 1729/1939/1757 -f 1730/1940/1758 1819/2045/1747 1719/1928/1746 -f 1719/1928/1746 1729/1939/1757 1730/1940/1758 -f 1731/1942/1759 1720/1929/1748 1721/1930/1749 -f 1721/1930/1749 1847/2075/1760 1731/1942/1759 -f 1732/1943/1761 1722/1932/1750 1720/1929/1748 -f 1720/1929/1748 1731/1942/1759 1732/1943/1761 -f 1733/1944/1762 1723/1933/1751 1722/1932/1750 -f 1722/1932/1750 1732/1943/1761 1733/1944/1762 -f 1734/1945/1763 1724/1934/1752 1723/1933/1751 -f 1723/1933/1751 1733/1944/1762 1734/1945/1763 -f 1735/1946/1764 1725/1935/1753 1724/1934/1752 -f 1724/1934/1752 1734/1945/1763 1735/1946/1764 -f 1736/1947/1765 1726/1936/1754 1725/1935/1753 -f 1725/1935/1753 1735/1946/1764 1736/1947/1765 -f 1737/1948/1766 1727/1937/1755 1726/1936/1754 -f 1726/1936/1754 1736/1947/1765 1737/1948/1766 -f 1738/1949/1767 1728/1938/1756 1727/1937/1755 -f 1727/1937/1755 1737/1948/1766 1738/1949/1767 -f 1739/1950/1768 1729/1939/1757 1728/1938/1756 -f 1728/1938/1756 1738/1949/1767 1739/1950/1768 -f 1740/1951/1769 1730/1940/1758 1729/1939/1757 -f 1729/1939/1757 1739/1950/1768 1740/1951/1769 -f 1859/2092/1770 1731/1942/1759 1847/2075/1760 -f 1847/2075/1760 1860/2094/1771 1859/2092/1770 -f 1741/1953/1772 1732/1943/1761 1731/1942/1759 -f 1731/1942/1759 1859/2092/1770 1741/1953/1772 -f 1864/2100/1773 1733/1944/1762 1732/1943/1761 -f 1732/1943/1761 1741/1953/1772 1864/2100/1773 -f 1866/2103/1774 1734/1945/1763 1733/1944/1762 -f 1733/1944/1762 1864/2100/1773 1866/2103/1774 -f 1742/1955/1775 1735/1946/1764 1734/1945/1763 -f 1734/1945/1763 1866/2103/1774 1742/1955/1775 -f 1869/2107/1776 1736/1947/1765 1735/1946/1764 -f 1735/1946/1764 1742/1955/1775 1869/2107/1776 -f 1871/2110/1777 1737/1948/1766 1736/1947/1765 -f 1736/1947/1765 1869/2107/1776 1871/2110/1777 -f 1743/1957/1778 1738/1949/1767 1737/1948/1766 -f 1737/1948/1766 1871/2110/1777 1743/1957/1778 -f 1744/1959/1779 1739/1950/1768 1738/1949/1767 -f 1738/1949/1767 1743/1957/1778 1744/1959/1779 -f 1849/2078/1780 1740/1951/1769 1739/1950/1768 -f 1739/1950/1768 1744/1959/1779 1849/2078/1780 -f 1745/1961/1781 1746/1962/1782 1747/1964/1681 -f 1747/1964/1681 1748/1966/1680 1745/1961/1781 -f 1749/1967/1783 1750/1968/1784 1746/1962/1782 -f 1746/1962/1782 1745/1961/1781 1749/1967/1783 -f 1751/1969/1785 1752/1970/1786 1750/1968/1784 -f 1750/1968/1784 1749/1967/1783 1751/1969/1785 -f 1753/1971/1787 1754/1972/1788 1752/1970/1786 -f 1752/1970/1786 1751/1969/1785 1753/1971/1787 -f 1755/1973/1789 1756/1974/1790 1754/1972/1788 -f 1754/1972/1788 1753/1971/1787 1755/1973/1789 -f 1757/1975/1791 1758/1976/1792 1756/1974/1790 -f 1756/1974/1790 1755/1973/1789 1757/1975/1791 -f 1759/1977/1793 1760/1978/1794 1758/1976/1792 -f 1758/1976/1792 1757/1975/1791 1759/1977/1793 -f 1761/1979/1795 1762/1980/1796 1760/1978/1794 -f 1760/1978/1794 1759/1977/1793 1761/1979/1795 -f 1763/1981/1797 1764/1982/1798 1762/1980/1796 -f 1762/1980/1796 1761/1979/1795 1763/1981/1797 -f 1647/1854/1663 1646/1852/1662 1764/1982/1798 -f 1764/1982/1798 1763/1981/1797 1647/1854/1663 -f 1765/1983/1799 1745/1961/1781 1748/1966/1680 -f 1748/1966/1680 1766/1985/1692 1765/1983/1799 -f 1767/1986/1800 1749/1967/1783 1745/1961/1781 -f 1745/1961/1781 1765/1983/1799 1767/1986/1800 -f 1768/1987/1801 1751/1969/1785 1749/1967/1783 -f 1749/1967/1783 1767/1986/1800 1768/1987/1801 -f 1769/1988/1802 1753/1971/1787 1751/1969/1785 -f 1751/1969/1785 1768/1987/1801 1769/1988/1802 -f 1770/1989/1803 1755/1973/1789 1753/1971/1787 -f 1753/1971/1787 1769/1988/1802 1770/1989/1803 -f 1771/1990/1804 1757/1975/1791 1755/1973/1789 -f 1755/1973/1789 1770/1989/1803 1771/1990/1804 -f 1772/1991/1805 1759/1977/1793 1757/1975/1791 -f 1757/1975/1791 1771/1990/1804 1772/1991/1805 -f 1773/1992/1806 1761/1979/1795 1759/1977/1793 -f 1759/1977/1793 1772/1991/1805 1773/1992/1806 -f 1774/1993/1807 1763/1981/1797 1761/1979/1795 -f 1761/1979/1795 1773/1992/1806 1774/1993/1807 -f 1665/1873/1683 1647/1854/1663 1763/1981/1797 -f 1763/1981/1797 1774/1993/1807 1665/1873/1683 -f 1775/1994/1808 1765/1983/1799 1766/1985/1692 -f 1766/1985/1692 1776/1996/1703 1775/1994/1808 -f 1777/1997/1809 1767/1986/1800 1765/1983/1799 -f 1765/1983/1799 1775/1994/1808 1777/1997/1809 -f 1778/1998/1810 1768/1987/1801 1767/1986/1800 -f 1767/1986/1800 1777/1997/1809 1778/1998/1810 -f 1779/1999/1811 1769/1988/1802 1768/1987/1801 -f 1768/1987/1801 1778/1998/1810 1779/1999/1811 -f 1780/2000/1812 1770/1989/1803 1769/1988/1802 -f 1769/1988/1802 1779/1999/1811 1780/2000/1812 -f 1781/2001/1813 1771/1990/1804 1770/1989/1803 -f 1770/1989/1803 1780/2000/1812 1781/2001/1813 -f 1782/2002/1814 1772/1991/1805 1771/1990/1804 -f 1771/1990/1804 1781/2001/1813 1782/2002/1814 -f 1783/2003/1815 1773/1992/1806 1772/1991/1805 -f 1772/1991/1805 1782/2002/1814 1783/2003/1815 -f 1784/2004/1816 1774/1993/1807 1773/1992/1806 -f 1773/1992/1806 1783/2003/1815 1784/2004/1816 -f 1785/2006/1694 1665/1873/1683 1774/1993/1807 -f 1774/1993/1807 1784/2004/1816 1785/2006/1694 -f 1786/2007/1817 1775/1994/1808 1776/1996/1703 -f 1776/1996/1703 1787/2009/1714 1786/2007/1817 -f 1788/2010/1818 1777/1997/1809 1775/1994/1808 -f 1775/1994/1808 1786/2007/1817 1788/2010/1818 -f 1789/2011/1819 1778/1998/1810 1777/1997/1809 -f 1777/1997/1809 1788/2010/1818 1789/2011/1819 -f 1790/2012/1820 1779/1999/1811 1778/1998/1810 -f 1778/1998/1810 1789/2011/1819 1790/2012/1820 -f 1791/2013/1821 1780/2000/1812 1779/1999/1811 -f 1779/1999/1811 1790/2012/1820 1791/2013/1821 -f 1792/2014/1822 1781/2001/1813 1780/2000/1812 -f 1780/2000/1812 1791/2013/1821 1792/2014/1822 -f 1793/2015/1823 1782/2002/1814 1781/2001/1813 -f 1781/2001/1813 1792/2014/1822 1793/2015/1823 -f 1794/2016/1824 1783/2003/1815 1782/2002/1814 -f 1782/2002/1814 1793/2015/1823 1794/2016/1824 -f 1795/2017/1825 1784/2004/1816 1783/2003/1815 -f 1783/2003/1815 1794/2016/1824 1795/2017/1825 -f 1796/2019/1705 1785/2006/1694 1784/2004/1816 -f 1784/2004/1816 1795/2017/1825 1796/2019/1705 -f 1797/2020/1826 1786/2007/1817 1787/2009/1714 -f 1787/2009/1714 1798/2022/1725 1797/2020/1826 -f 1799/2023/1827 1788/2010/1818 1786/2007/1817 -f 1786/2007/1817 1797/2020/1826 1799/2023/1827 -f 1800/2024/1828 1789/2011/1819 1788/2010/1818 -f 1788/2010/1818 1799/2023/1827 1800/2024/1828 -f 1801/2025/1829 1790/2012/1820 1789/2011/1819 -f 1789/2011/1819 1800/2024/1828 1801/2025/1829 -f 1802/2026/1830 1791/2013/1821 1790/2012/1820 -f 1790/2012/1820 1801/2025/1829 1802/2026/1830 -f 1803/2027/1831 1792/2014/1822 1791/2013/1821 -f 1791/2013/1821 1802/2026/1830 1803/2027/1831 -f 1804/2028/1832 1793/2015/1823 1792/2014/1822 -f 1792/2014/1822 1803/2027/1831 1804/2028/1832 -f 1805/2029/1833 1794/2016/1824 1793/2015/1823 -f 1793/2015/1823 1804/2028/1832 1805/2029/1833 -f 1806/2030/1834 1795/2017/1825 1794/2016/1824 -f 1794/2016/1824 1805/2029/1833 1806/2030/1834 -f 1807/2032/1716 1796/2019/1705 1795/2017/1825 -f 1795/2017/1825 1806/2030/1834 1807/2032/1716 -f 1808/2033/1835 1797/2020/1826 1798/2022/1725 -f 1798/2022/1725 1809/2035/1736 1808/2033/1835 -f 1810/2036/1836 1799/2023/1827 1797/2020/1826 -f 1797/2020/1826 1808/2033/1835 1810/2036/1836 -f 1811/2037/1837 1800/2024/1828 1799/2023/1827 -f 1799/2023/1827 1810/2036/1836 1811/2037/1837 -f 1812/2038/1838 1801/2025/1829 1800/2024/1828 -f 1800/2024/1828 1811/2037/1837 1812/2038/1838 -f 1813/2039/1839 1802/2026/1830 1801/2025/1829 -f 1801/2025/1829 1812/2038/1838 1813/2039/1839 -f 1814/2040/1840 1803/2027/1831 1802/2026/1830 -f 1802/2026/1830 1813/2039/1839 1814/2040/1840 -f 1815/2041/1841 1804/2028/1832 1803/2027/1831 -f 1803/2027/1831 1814/2040/1840 1815/2041/1841 -f 1816/2042/1842 1805/2029/1833 1804/2028/1832 -f 1804/2028/1832 1815/2041/1841 1816/2042/1842 -f 1817/2043/1843 1806/2030/1834 1805/2029/1833 -f 1805/2029/1833 1816/2042/1842 1817/2043/1843 -f 1702/1911/1727 1807/2032/1716 1806/2030/1834 -f 1806/2030/1834 1817/2043/1843 1702/1911/1727 -f 1818/2044/1844 1808/2033/1835 1809/2035/1736 -f 1809/2035/1736 1819/2046/1747 1818/2044/1844 -f 1820/2047/1845 1810/2036/1836 1808/2033/1835 -f 1808/2033/1835 1818/2044/1844 1820/2047/1845 -f 1821/2048/1846 1811/2037/1837 1810/2036/1836 -f 1810/2036/1836 1820/2047/1845 1821/2048/1846 -f 1822/2049/1847 1812/2038/1838 1811/2037/1837 -f 1811/2037/1837 1821/2048/1846 1822/2049/1847 -f 1823/2050/1848 1813/2039/1839 1812/2038/1838 -f 1812/2038/1838 1822/2049/1847 1823/2050/1848 -f 1824/2051/1849 1814/2040/1840 1813/2039/1839 -f 1813/2039/1839 1823/2050/1848 1824/2051/1849 -f 1825/2052/1850 1815/2041/1841 1814/2040/1840 -f 1814/2040/1840 1824/2051/1849 1825/2052/1850 -f 1826/2053/1851 1816/2042/1842 1815/2041/1841 -f 1815/2041/1841 1825/2052/1850 1826/2053/1851 -f 1827/2054/1852 1817/2043/1843 1816/2042/1842 -f 1816/2042/1842 1826/2053/1851 1827/2054/1852 -f 1828/2056/1738 1702/1911/1727 1817/2043/1843 -f 1817/2043/1843 1827/2054/1852 1828/2056/1738 -f 1829/2057/1853 1818/2044/1844 1819/2046/1747 -f 1819/2046/1747 1730/1941/1758 1829/2057/1853 -f 1830/2058/1854 1820/2047/1845 1818/2044/1844 -f 1818/2044/1844 1829/2057/1853 1830/2058/1854 -f 1831/2059/1855 1821/2048/1846 1820/2047/1845 -f 1820/2047/1845 1830/2058/1854 1831/2059/1855 -f 1832/2060/1856 1822/2049/1847 1821/2048/1846 -f 1821/2048/1846 1831/2059/1855 1832/2060/1856 -f 1833/2061/1857 1823/2050/1848 1822/2049/1847 -f 1822/2049/1847 1832/2060/1856 1833/2061/1857 -f 1834/2062/1858 1824/2051/1849 1823/2050/1848 -f 1823/2050/1848 1833/2061/1857 1834/2062/1858 -f 1835/2063/1859 1825/2052/1850 1824/2051/1849 -f 1824/2051/1849 1834/2062/1858 1835/2063/1859 -f 1836/2064/1860 1826/2053/1851 1825/2052/1850 -f 1825/2052/1850 1835/2063/1859 1836/2064/1860 -f 1837/2065/1861 1827/2054/1852 1826/2053/1851 -f 1826/2053/1851 1836/2064/1860 1837/2065/1861 -f 1721/1931/1749 1828/2056/1738 1827/2054/1852 -f 1827/2054/1852 1837/2065/1861 1721/1931/1749 -f 1838/2066/1862 1829/2057/1853 1730/1941/1758 -f 1730/1941/1758 1740/1952/1769 1838/2066/1862 -f 1839/2067/1863 1830/2058/1854 1829/2057/1853 -f 1829/2057/1853 1838/2066/1862 1839/2067/1863 -f 1840/2068/1864 1831/2059/1855 1830/2058/1854 -f 1830/2058/1854 1839/2067/1863 1840/2068/1864 -f 1841/2069/1865 1832/2060/1856 1831/2059/1855 -f 1831/2059/1855 1840/2068/1864 1841/2069/1865 -f 1842/2070/1866 1833/2061/1857 1832/2060/1856 -f 1832/2060/1856 1841/2069/1865 1842/2070/1866 -f 1843/2071/1867 1834/2062/1858 1833/2061/1857 -f 1833/2061/1857 1842/2070/1866 1843/2071/1867 -f 1844/2072/1868 1835/2063/1859 1834/2062/1858 -f 1834/2062/1858 1843/2071/1867 1844/2072/1868 -f 1845/2073/1869 1836/2064/1860 1835/2063/1859 -f 1835/2063/1859 1844/2072/1868 1845/2073/1869 -f 1846/2074/1870 1837/2065/1861 1836/2064/1860 -f 1836/2064/1860 1845/2073/1869 1846/2074/1870 -f 1847/2076/1760 1721/1931/1749 1837/2065/1861 -f 1837/2065/1861 1846/2074/1870 1847/2076/1760 -f 1848/2077/1871 1838/2066/1862 1740/1952/1769 -f 1740/1952/1769 1849/2079/1780 1848/2077/1871 -f 1850/2080/1872 1839/2067/1863 1838/2066/1862 -f 1838/2066/1862 1848/2077/1871 1850/2080/1872 -f 1851/2081/1873 1840/2068/1864 1839/2067/1863 -f 1839/2067/1863 1850/2080/1872 1851/2081/1873 -f 1852/2082/1874 1841/2069/1865 1840/2068/1864 -f 1840/2068/1864 1851/2081/1873 1852/2082/1874 -f 1853/2083/1875 1842/2070/1866 1841/2069/1865 -f 1841/2069/1865 1852/2082/1874 1853/2083/1875 -f 1854/2084/1876 1843/2071/1867 1842/2070/1866 -f 1842/2070/1866 1853/2083/1875 1854/2084/1876 -f 1855/2085/1877 1844/2072/1868 1843/2071/1867 -f 1843/2071/1867 1854/2084/1876 1855/2085/1877 -f 1856/2087/1878 1845/2073/1869 1844/2072/1868 -f 1844/2072/1868 1855/2085/1877 1856/2087/1878 -f 1857/2089/1879 1846/2074/1870 1845/2073/1869 -f 1845/2073/1869 1856/2087/1878 1857/2089/1879 -f 1860/2095/1771 1847/2076/1760 1846/2074/1870 -f 1846/2074/1870 1857/2089/1879 1860/2095/1771 -f 1858/2091/1880 1859/2093/1770 1860/2096/1771 -f 1860/2096/1771 1861/2097/1881 1858/2091/1880 -f 1862/2098/1882 1741/1954/1772 1859/2093/1770 -f 1859/2093/1770 1858/2091/1880 1862/2098/1882 -f 1863/2099/1883 1864/2101/1773 1741/1954/1772 -f 1741/1954/1772 1862/2098/1882 1863/2099/1883 -f 1865/2102/1884 1866/2104/1774 1864/2101/1773 -f 1864/2101/1773 1863/2099/1883 1865/2102/1884 -f 1867/2105/1885 1742/1956/1775 1866/2104/1774 -f 1866/2104/1774 1865/2102/1884 1867/2105/1885 -f 1868/2106/1886 1869/2108/1776 1742/1956/1775 -f 1742/1956/1775 1867/2105/1885 1868/2106/1886 -f 1870/2109/1887 1871/2111/1777 1869/2108/1776 -f 1869/2108/1776 1868/2106/1886 1870/2109/1887 -f 1872/2112/1888 1743/1958/1778 1871/2111/1777 -f 1871/2111/1777 1870/2109/1887 1872/2112/1888 -f 1873/2113/1889 1744/1960/1779 1743/1958/1778 -f 1743/1958/1778 1872/2112/1888 1873/2113/1889 -f 1874/2114/1890 1849/2079/1780 1744/1960/1779 -f 1744/1960/1779 1873/2113/1889 1874/2114/1890 -f 1875/2115/1891 1858/2091/1880 1861/2097/1881 -f 1861/2097/1881 1876/2116/1892 1875/2115/1891 -f 1877/2117/1893 1862/2098/1882 1858/2091/1880 -f 1858/2091/1880 1875/2115/1891 1877/2117/1893 -f 1878/2118/1894 1863/2099/1883 1862/2098/1882 -f 1862/2098/1882 1877/2117/1893 1878/2118/1894 -f 1879/2119/1895 1865/2102/1884 1863/2099/1883 -f 1863/2099/1883 1878/2118/1894 1879/2119/1895 -f 1880/2120/1896 1867/2105/1885 1865/2102/1884 -f 1865/2102/1884 1879/2119/1895 1880/2120/1896 -f 1881/2121/1897 1868/2106/1886 1867/2105/1885 -f 1867/2105/1885 1880/2120/1896 1881/2121/1897 -f 1882/2122/1898 1870/2109/1887 1868/2106/1886 -f 1868/2106/1886 1881/2121/1897 1882/2122/1898 -f 1883/2123/1899 1872/2112/1888 1870/2109/1887 -f 1870/2109/1887 1882/2122/1898 1883/2123/1899 -f 1884/2124/1900 1873/2113/1889 1872/2112/1888 -f 1872/2112/1888 1883/2123/1899 1884/2124/1900 -f 1885/2125/1901 1874/2114/1890 1873/2113/1889 -f 1873/2113/1889 1884/2124/1900 1885/2125/1901 -f 1886/2126/1902 1875/2115/1891 1876/2116/1892 -f 1876/2116/1892 1887/2127/1903 1886/2126/1902 -f 1888/2128/1904 1877/2117/1893 1875/2115/1891 -f 1875/2115/1891 1886/2126/1902 1888/2128/1904 -f 1889/2129/1905 1878/2118/1894 1877/2117/1893 -f 1877/2117/1893 1888/2128/1904 1889/2129/1905 -f 1890/2130/1906 1879/2119/1895 1878/2118/1894 -f 1878/2118/1894 1889/2129/1905 1890/2130/1906 -f 1891/2131/1907 1880/2120/1896 1879/2119/1895 -f 1879/2119/1895 1890/2130/1906 1891/2131/1907 -f 1892/2132/1908 1881/2121/1897 1880/2120/1896 -f 1880/2120/1896 1891/2131/1907 1892/2132/1908 -f 1893/2133/1909 1882/2122/1898 1881/2121/1897 -f 1881/2121/1897 1892/2132/1908 1893/2133/1909 -f 1894/2134/1910 1883/2123/1899 1882/2122/1898 -f 1882/2122/1898 1893/2133/1909 1894/2134/1910 -f 1895/2135/1911 1884/2124/1900 1883/2123/1899 -f 1883/2123/1899 1894/2134/1910 1895/2135/1911 -f 1896/2136/1912 1885/2125/1901 1884/2124/1900 -f 1884/2124/1900 1895/2135/1911 1896/2136/1912 -f 1897/2137/1913 1886/2126/1902 1887/2127/1903 -f 1887/2127/1903 1898/2138/1914 1897/2137/1913 -f 1899/2139/1915 1888/2128/1904 1886/2126/1902 -f 1886/2126/1902 1897/2137/1913 1899/2139/1915 -f 1900/2140/1916 1889/2129/1905 1888/2128/1904 -f 1888/2128/1904 1899/2139/1915 1900/2140/1916 -f 1901/2141/1917 1890/2130/1906 1889/2129/1905 -f 1889/2129/1905 1900/2140/1916 1901/2141/1917 -f 1902/2142/1918 1891/2131/1907 1890/2130/1906 -f 1890/2130/1906 1901/2141/1917 1902/2142/1918 -f 1903/2143/1919 1892/2132/1908 1891/2131/1907 -f 1891/2131/1907 1902/2142/1918 1903/2143/1919 -f 1904/2144/1920 1893/2133/1909 1892/2132/1908 -f 1892/2132/1908 1903/2143/1919 1904/2144/1920 -f 1905/2145/1921 1894/2134/1910 1893/2133/1909 -f 1893/2133/1909 1904/2144/1920 1905/2145/1921 -f 1906/2146/1922 1895/2135/1911 1894/2134/1910 -f 1894/2134/1910 1905/2145/1921 1906/2146/1922 -f 1907/2147/1923 1896/2136/1912 1895/2135/1911 -f 1895/2135/1911 1906/2146/1922 1907/2147/1923 -f 1908/2148/1924 1897/2137/1913 1898/2138/1914 -f 1898/2138/1914 2011/2251/1925 1908/2148/1924 -f 1909/2149/1926 1899/2139/1915 1897/2137/1913 -f 1897/2137/1913 1908/2148/1924 1909/2149/1926 -f 1910/2150/1927 1900/2140/1916 1899/2139/1915 -f 1899/2139/1915 1909/2149/1926 1910/2150/1927 -f 1911/2151/1928 1901/2141/1917 1900/2140/1916 -f 1900/2140/1916 1910/2150/1927 1911/2151/1928 -f 1912/2152/1929 1902/2142/1918 1901/2141/1917 -f 1901/2141/1917 1911/2151/1928 1912/2152/1929 -f 1913/2153/1930 1903/2143/1919 1902/2142/1918 -f 1902/2142/1918 1912/2152/1929 1913/2153/1930 -f 1914/2154/1931 1904/2144/1920 1903/2143/1919 -f 1903/2143/1919 1913/2153/1930 1914/2154/1931 -f 1915/2155/1932 1905/2145/1921 1904/2144/1920 -f 1904/2144/1920 1914/2154/1931 1915/2155/1932 -f 1916/2156/1933 1906/2146/1922 1905/2145/1921 -f 1905/2145/1921 1915/2155/1932 1916/2156/1933 -f 2002/2242/1934 1907/2147/1923 1906/2146/1922 -f 1906/2146/1922 1916/2156/1933 2002/2242/1934 -f 1917/2157/1935 1908/2148/1924 2011/2251/1925 -f 2011/2251/1925 2022/2262/1936 1917/2157/1935 -f 1918/2158/1937 1909/2149/1926 1908/2148/1924 -f 1908/2148/1924 1917/2157/1935 1918/2158/1937 -f 1919/2159/1938 1910/2150/1927 1909/2149/1926 -f 1909/2149/1926 1918/2158/1937 1919/2159/1938 -f 1920/2160/1939 1911/2151/1928 1910/2150/1927 -f 1910/2150/1927 1919/2159/1938 1920/2160/1939 -f 1921/2161/1940 1912/2152/1929 1911/2151/1928 -f 1911/2151/1928 1920/2160/1939 1921/2161/1940 -f 1922/2162/1941 1913/2153/1930 1912/2152/1929 -f 1912/2152/1929 1921/2161/1940 1922/2162/1941 -f 1923/2163/1942 1914/2154/1931 1913/2153/1930 -f 1913/2153/1930 1922/2162/1941 1923/2163/1942 -f 1924/2164/1943 1915/2155/1932 1914/2154/1931 -f 1914/2154/1931 1923/2163/1942 1924/2164/1943 -f 1925/2165/1944 1916/2156/1933 1915/2155/1932 -f 1915/2155/1932 1924/2164/1943 1925/2165/1944 -f 2013/2253/1945 2002/2242/1934 1916/2156/1933 -f 1916/2156/1933 1925/2165/1944 2013/2253/1945 -f 1926/2166/1946 1917/2157/1935 2022/2262/1936 -f 2022/2262/1936 2032/2272/1947 1926/2166/1946 -f 1927/2167/1948 1918/2158/1937 1917/2157/1935 -f 1917/2157/1935 1926/2166/1946 1927/2167/1948 -f 1928/2168/1949 1919/2159/1938 1918/2158/1937 -f 1918/2158/1937 1927/2167/1948 1928/2168/1949 -f 1929/2169/1950 1920/2160/1939 1919/2159/1938 -f 1919/2159/1938 1928/2168/1949 1929/2169/1950 -f 1930/2170/1951 1921/2161/1940 1920/2160/1939 -f 1920/2160/1939 1929/2169/1950 1930/2170/1951 -f 1931/2171/1952 1922/2162/1941 1921/2161/1940 -f 1921/2161/1940 1930/2170/1951 1931/2171/1952 -f 1932/2172/1953 1923/2163/1942 1922/2162/1941 -f 1922/2162/1941 1931/2171/1952 1932/2172/1953 -f 1933/2173/1954 1924/2164/1943 1923/2163/1942 -f 1923/2163/1942 1932/2172/1953 1933/2173/1954 -f 1934/2174/1955 1925/2165/1944 1924/2164/1943 -f 1924/2164/1943 1933/2173/1954 1934/2174/1955 -f 1935/2175/1956 2013/2253/1945 1925/2165/1944 -f 1925/2165/1944 1934/2174/1955 1935/2175/1956 -f 1936/2176/1957 1926/2166/1946 2032/2272/1947 -f 2032/2272/1947 2043/2283/1958 1936/2176/1957 -f 1937/2177/1959 1927/2167/1948 1926/2166/1946 -f 1926/2166/1946 1936/2176/1957 1937/2177/1959 -f 1938/2178/1960 1928/2168/1949 1927/2167/1948 -f 1927/2167/1948 1937/2177/1959 1938/2178/1960 -f 1939/2179/1961 1929/2169/1950 1928/2168/1949 -f 1928/2168/1949 1938/2178/1960 1939/2179/1961 -f 1940/2180/1962 1930/2170/1951 1929/2169/1950 -f 1929/2169/1950 1939/2179/1961 1940/2180/1962 -f 1941/2181/1963 1931/2171/1952 1930/2170/1951 -f 1930/2170/1951 1940/2180/1962 1941/2181/1963 -f 1942/2182/1964 1932/2172/1953 1931/2171/1952 -f 1931/2171/1952 1941/2181/1963 1942/2182/1964 -f 1943/2183/1965 1933/2173/1954 1932/2172/1953 -f 1932/2172/1953 1942/2182/1964 1943/2183/1965 -f 1944/2184/1966 1934/2174/1955 1933/2173/1954 -f 1933/2173/1954 1943/2183/1965 1944/2184/1966 -f 2034/2274/1967 1935/2175/1956 1934/2174/1955 -f 1934/2174/1955 1944/2184/1966 2034/2274/1967 -f 1945/2185/1968 1936/2176/1957 2043/2283/1958 -f 2043/2283/1958 2054/2294/1969 1945/2185/1968 -f 1946/2186/1970 1937/2177/1959 1936/2176/1957 -f 1936/2176/1957 1945/2185/1968 1946/2186/1970 -f 1947/2187/1971 1938/2178/1960 1937/2177/1959 -f 1937/2177/1959 1946/2186/1970 1947/2187/1971 -f 1948/2188/1972 1939/2179/1961 1938/2178/1960 -f 1938/2178/1960 1947/2187/1971 1948/2188/1972 -f 1949/2189/1973 1940/2180/1962 1939/2179/1961 -f 1939/2179/1961 1948/2188/1972 1949/2189/1973 -f 1950/2190/1974 1941/2181/1963 1940/2180/1962 -f 1940/2180/1962 1949/2189/1973 1950/2190/1974 -f 1951/2191/1975 1942/2182/1964 1941/2181/1963 -f 1941/2181/1963 1950/2190/1974 1951/2191/1975 -f 1952/2192/1976 1943/2183/1965 1942/2182/1964 -f 1942/2182/1964 1951/2191/1975 1952/2192/1976 -f 1953/2193/1977 1944/2184/1966 1943/2183/1965 -f 1943/2183/1965 1952/2192/1976 1953/2193/1977 -f 2045/2285/1978 2034/2274/1967 1944/2184/1966 -f 1944/2184/1966 1953/2193/1977 2045/2285/1978 -f 1954/2194/1979 1945/2185/1968 2054/2294/1969 -f 2054/2294/1969 1955/2195/1980 1954/2194/1979 -f 1956/2196/1981 1946/2186/1970 1945/2185/1968 -f 1945/2185/1968 1954/2194/1979 1956/2196/1981 -f 1957/2197/1982 1947/2187/1971 1946/2186/1970 -f 1946/2186/1970 1956/2196/1981 1957/2197/1982 -f 1958/2198/1983 1948/2188/1972 1947/2187/1971 -f 1947/2187/1971 1957/2197/1982 1958/2198/1983 -f 1959/2199/1984 1949/2189/1973 1948/2188/1972 -f 1948/2188/1972 1958/2198/1983 1959/2199/1984 -f 1960/2200/1985 1950/2190/1974 1949/2189/1973 -f 1949/2189/1973 1959/2199/1984 1960/2200/1985 -f 1961/2201/1986 1951/2191/1975 1950/2190/1974 -f 1950/2190/1974 1960/2200/1985 1961/2201/1986 -f 1962/2202/1987 1952/2192/1976 1951/2191/1975 -f 1951/2191/1975 1961/2201/1986 1962/2202/1987 -f 1963/2203/1988 1953/2193/1977 1952/2192/1976 -f 1952/2192/1976 1962/2202/1987 1963/2203/1988 -f 1964/2204/1989 2045/2285/1978 1953/2193/1977 -f 1953/2193/1977 1963/2203/1988 1964/2204/1989 -f 1965/2205/1990 1848/2077/1871 1849/2079/1780 -f 1849/2079/1780 1874/2114/1890 1965/2205/1990 -f 1966/2206/1991 1850/2080/1872 1848/2077/1871 -f 1848/2077/1871 1965/2205/1990 1966/2206/1991 -f 1967/2207/1992 1851/2081/1873 1850/2080/1872 -f 1850/2080/1872 1966/2206/1991 1967/2207/1992 -f 1968/2208/1993 1852/2082/1874 1851/2081/1873 -f 1851/2081/1873 1967/2207/1992 1968/2208/1993 -f 1969/2209/1994 1853/2083/1875 1852/2082/1874 -f 1852/2082/1874 1968/2208/1993 1969/2209/1994 -f 1970/2210/1995 1854/2084/1876 1853/2083/1875 -f 1853/2083/1875 1969/2209/1994 1970/2210/1995 -f 1971/2211/1996 1855/2086/1877 1854/2084/1876 -f 1854/2084/1876 1970/2210/1995 1971/2211/1996 -f 1972/2212/1997 1856/2088/1878 1855/2086/1877 -f 1855/2086/1877 1971/2211/1996 1972/2212/1997 -f 1973/2213/1998 1857/2090/1879 1856/2088/1878 -f 1856/2088/1878 1972/2212/1997 1973/2213/1998 -f 1861/2097/1881 1860/2096/1771 1857/2090/1879 -f 1857/2090/1879 1973/2213/1998 1861/2097/1881 -f 1974/2214/1999 1965/2205/1990 1874/2114/1890 -f 1874/2114/1890 1885/2125/1901 1974/2214/1999 -f 1975/2215/2000 1966/2206/1991 1965/2205/1990 -f 1965/2205/1990 1974/2214/1999 1975/2215/2000 -f 1976/2216/2001 1967/2207/1992 1966/2206/1991 -f 1966/2206/1991 1975/2215/2000 1976/2216/2001 -f 1977/2217/2002 1968/2208/1993 1967/2207/1992 -f 1967/2207/1992 1976/2216/2001 1977/2217/2002 -f 1978/2218/2003 1969/2209/1994 1968/2208/1993 -f 1968/2208/1993 1977/2217/2002 1978/2218/2003 -f 1979/2219/2004 1970/2210/1995 1969/2209/1994 -f 1969/2209/1994 1978/2218/2003 1979/2219/2004 -f 1980/2220/2005 1971/2211/1996 1970/2210/1995 -f 1970/2210/1995 1979/2219/2004 1980/2220/2005 -f 1981/2221/2006 1972/2212/1997 1971/2211/1996 -f 1971/2211/1996 1980/2220/2005 1981/2221/2006 -f 1982/2222/2007 1973/2213/1998 1972/2212/1997 -f 1972/2212/1997 1981/2221/2006 1982/2222/2007 -f 1876/2116/1892 1861/2097/1881 1973/2213/1998 -f 1973/2213/1998 1982/2222/2007 1876/2116/1892 -f 1983/2223/2008 1974/2214/1999 1885/2125/1901 -f 1885/2125/1901 1896/2136/1912 1983/2223/2008 -f 1984/2224/2009 1975/2215/2000 1974/2214/1999 -f 1974/2214/1999 1983/2223/2008 1984/2224/2009 -f 1985/2225/2010 1976/2216/2001 1975/2215/2000 -f 1975/2215/2000 1984/2224/2009 1985/2225/2010 -f 1986/2226/2011 1977/2217/2002 1976/2216/2001 -f 1976/2216/2001 1985/2225/2010 1986/2226/2011 -f 1987/2227/2012 1978/2218/2003 1977/2217/2002 -f 1977/2217/2002 1986/2226/2011 1987/2227/2012 -f 1988/2228/2013 1979/2219/2004 1978/2218/2003 -f 1978/2218/2003 1987/2227/2012 1988/2228/2013 -f 1989/2229/2014 1980/2220/2005 1979/2219/2004 -f 1979/2219/2004 1988/2228/2013 1989/2229/2014 -f 1990/2230/2015 1981/2221/2006 1980/2220/2005 -f 1980/2220/2005 1989/2229/2014 1990/2230/2015 -f 1991/2231/2016 1982/2222/2007 1981/2221/2006 -f 1981/2221/2006 1990/2230/2015 1991/2231/2016 -f 1887/2127/1903 1876/2116/1892 1982/2222/2007 -f 1982/2222/2007 1991/2231/2016 1887/2127/1903 -f 1992/2232/2017 1983/2223/2008 1896/2136/1912 -f 1896/2136/1912 1907/2147/1923 1992/2232/2017 -f 1993/2233/2018 1984/2224/2009 1983/2223/2008 -f 1983/2223/2008 1992/2232/2017 1993/2233/2018 -f 1994/2234/2019 1985/2225/2010 1984/2224/2009 -f 1984/2224/2009 1993/2233/2018 1994/2234/2019 -f 1995/2235/2020 1986/2226/2011 1985/2225/2010 -f 1985/2225/2010 1994/2234/2019 1995/2235/2020 -f 1996/2236/2021 1987/2227/2012 1986/2226/2011 -f 1986/2226/2011 1995/2235/2020 1996/2236/2021 -f 1997/2237/2022 1988/2228/2013 1987/2227/2012 -f 1987/2227/2012 1996/2236/2021 1997/2237/2022 -f 1998/2238/2023 1989/2229/2014 1988/2228/2013 -f 1988/2228/2013 1997/2237/2022 1998/2238/2023 -f 1999/2239/2024 1990/2230/2015 1989/2229/2014 -f 1989/2229/2014 1998/2238/2023 1999/2239/2024 -f 2000/2240/2025 1991/2231/2016 1990/2230/2015 -f 1990/2230/2015 1999/2239/2024 2000/2240/2025 -f 1898/2138/1914 1887/2127/1903 1991/2231/2016 -f 1991/2231/2016 2000/2240/2025 1898/2138/1914 -f 2001/2241/2026 1992/2232/2017 1907/2147/1923 -f 1907/2147/1923 2002/2242/1934 2001/2241/2026 -f 2003/2243/2027 1993/2233/2018 1992/2232/2017 -f 1992/2232/2017 2001/2241/2026 2003/2243/2027 -f 2004/2244/2028 1994/2234/2019 1993/2233/2018 -f 1993/2233/2018 2003/2243/2027 2004/2244/2028 -f 2005/2245/2029 1995/2235/2020 1994/2234/2019 -f 1994/2234/2019 2004/2244/2028 2005/2245/2029 -f 2006/2246/2030 1996/2236/2021 1995/2235/2020 -f 1995/2235/2020 2005/2245/2029 2006/2246/2030 -f 2007/2247/2031 1997/2237/2022 1996/2236/2021 -f 1996/2236/2021 2006/2246/2030 2007/2247/2031 -f 2008/2248/2032 1998/2238/2023 1997/2237/2022 -f 1997/2237/2022 2007/2247/2031 2008/2248/2032 -f 2009/2249/2033 1999/2239/2024 1998/2238/2023 -f 1998/2238/2023 2008/2248/2032 2009/2249/2033 -f 2010/2250/2034 2000/2240/2025 1999/2239/2024 -f 1999/2239/2024 2009/2249/2033 2010/2250/2034 -f 2011/2251/1925 1898/2138/1914 2000/2240/2025 -f 2000/2240/2025 2010/2250/2034 2011/2251/1925 -f 2012/2252/2035 2001/2241/2026 2002/2242/1934 -f 2002/2242/1934 2013/2253/1945 2012/2252/2035 -f 2014/2254/2036 2003/2243/2027 2001/2241/2026 -f 2001/2241/2026 2012/2252/2035 2014/2254/2036 -f 2015/2255/2037 2004/2244/2028 2003/2243/2027 -f 2003/2243/2027 2014/2254/2036 2015/2255/2037 -f 2016/2256/2038 2005/2245/2029 2004/2244/2028 -f 2004/2244/2028 2015/2255/2037 2016/2256/2038 -f 2017/2257/2039 2006/2246/2030 2005/2245/2029 -f 2005/2245/2029 2016/2256/2038 2017/2257/2039 -f 2018/2258/2040 2007/2247/2031 2006/2246/2030 -f 2006/2246/2030 2017/2257/2039 2018/2258/2040 -f 2019/2259/2041 2008/2248/2032 2007/2247/2031 -f 2007/2247/2031 2018/2258/2040 2019/2259/2041 -f 2020/2260/2042 2009/2249/2033 2008/2248/2032 -f 2008/2248/2032 2019/2259/2041 2020/2260/2042 -f 2021/2261/2043 2010/2250/2034 2009/2249/2033 -f 2009/2249/2033 2020/2260/2042 2021/2261/2043 -f 2022/2262/1936 2011/2251/1925 2010/2250/2034 -f 2010/2250/2034 2021/2261/2043 2022/2262/1936 -f 2023/2263/2044 2012/2252/2035 2013/2253/1945 -f 2013/2253/1945 1935/2175/1956 2023/2263/2044 -f 2024/2264/2045 2014/2254/2036 2012/2252/2035 -f 2012/2252/2035 2023/2263/2044 2024/2264/2045 -f 2025/2265/2046 2015/2255/2037 2014/2254/2036 -f 2014/2254/2036 2024/2264/2045 2025/2265/2046 -f 2026/2266/2047 2016/2256/2038 2015/2255/2037 -f 2015/2255/2037 2025/2265/2046 2026/2266/2047 -f 2027/2267/2048 2017/2257/2039 2016/2256/2038 -f 2016/2256/2038 2026/2266/2047 2027/2267/2048 -f 2028/2268/2049 2018/2258/2040 2017/2257/2039 -f 2017/2257/2039 2027/2267/2048 2028/2268/2049 -f 2029/2269/2050 2019/2259/2041 2018/2258/2040 -f 2018/2258/2040 2028/2268/2049 2029/2269/2050 -f 2030/2270/2051 2020/2260/2042 2019/2259/2041 -f 2019/2259/2041 2029/2269/2050 2030/2270/2051 -f 2031/2271/2052 2021/2261/2043 2020/2260/2042 -f 2020/2260/2042 2030/2270/2051 2031/2271/2052 -f 2032/2272/1947 2022/2262/1936 2021/2261/2043 -f 2021/2261/2043 2031/2271/2052 2032/2272/1947 -f 2033/2273/2053 2023/2263/2044 1935/2175/1956 -f 1935/2175/1956 2034/2274/1967 2033/2273/2053 -f 2035/2275/2054 2024/2264/2045 2023/2263/2044 -f 2023/2263/2044 2033/2273/2053 2035/2275/2054 -f 2036/2276/2055 2025/2265/2046 2024/2264/2045 -f 2024/2264/2045 2035/2275/2054 2036/2276/2055 -f 2037/2277/2056 2026/2266/2047 2025/2265/2046 -f 2025/2265/2046 2036/2276/2055 2037/2277/2056 -f 2038/2278/2057 2027/2267/2048 2026/2266/2047 -f 2026/2266/2047 2037/2277/2056 2038/2278/2057 -f 2039/2279/2058 2028/2268/2049 2027/2267/2048 -f 2027/2267/2048 2038/2278/2057 2039/2279/2058 -f 2040/2280/2059 2029/2269/2050 2028/2268/2049 -f 2028/2268/2049 2039/2279/2058 2040/2280/2059 -f 2041/2281/2060 2030/2270/2051 2029/2269/2050 -f 2029/2269/2050 2040/2280/2059 2041/2281/2060 -f 2042/2282/2061 2031/2271/2052 2030/2270/2051 -f 2030/2270/2051 2041/2281/2060 2042/2282/2061 -f 2043/2283/1958 2032/2272/1947 2031/2271/2052 -f 2031/2271/2052 2042/2282/2061 2043/2283/1958 -f 2044/2284/2062 2033/2273/2053 2034/2274/1967 -f 2034/2274/1967 2045/2285/1978 2044/2284/2062 -f 2046/2286/2063 2035/2275/2054 2033/2273/2053 -f 2033/2273/2053 2044/2284/2062 2046/2286/2063 -f 2047/2287/2064 2036/2276/2055 2035/2275/2054 -f 2035/2275/2054 2046/2286/2063 2047/2287/2064 -f 2048/2288/2065 2037/2277/2056 2036/2276/2055 -f 2036/2276/2055 2047/2287/2064 2048/2288/2065 -f 2049/2289/2066 2038/2278/2057 2037/2277/2056 -f 2037/2277/2056 2048/2288/2065 2049/2289/2066 -f 2050/2290/2067 2039/2279/2058 2038/2278/2057 -f 2038/2278/2057 2049/2289/2066 2050/2290/2067 -f 2051/2291/2068 2040/2280/2059 2039/2279/2058 -f 2039/2279/2058 2050/2290/2067 2051/2291/2068 -f 2052/2292/2069 2041/2281/2060 2040/2280/2059 -f 2040/2280/2059 2051/2291/2068 2052/2292/2069 -f 2053/2293/2070 2042/2282/2061 2041/2281/2060 -f 2041/2281/2060 2052/2292/2069 2053/2293/2070 -f 2054/2294/1969 2043/2283/1958 2042/2282/2061 -f 2042/2282/2061 2053/2293/2070 2054/2294/1969 -f 2055/2295/2071 2044/2284/2062 2045/2285/1978 -f 2045/2285/1978 1964/2204/1989 2055/2295/2071 -f 2056/2296/2072 2046/2286/2063 2044/2284/2062 -f 2044/2284/2062 2055/2295/2071 2056/2296/2072 -f 2057/2297/2073 2047/2287/2064 2046/2286/2063 -f 2046/2286/2063 2056/2296/2072 2057/2297/2073 -f 2058/2298/2074 2048/2288/2065 2047/2287/2064 -f 2047/2287/2064 2057/2297/2073 2058/2298/2074 -f 2059/2299/2075 2049/2289/2066 2048/2288/2065 -f 2048/2288/2065 2058/2298/2074 2059/2299/2075 -f 2060/2300/2076 2050/2290/2067 2049/2289/2066 -f 2049/2289/2066 2059/2299/2075 2060/2300/2076 -f 2061/2301/2077 2051/2291/2068 2050/2290/2067 -f 2050/2290/2067 2060/2300/2076 2061/2301/2077 -f 2062/2302/2078 2052/2292/2069 2051/2291/2068 -f 2051/2291/2068 2061/2301/2077 2062/2302/2078 -f 2063/2303/2079 2053/2293/2070 2052/2292/2069 -f 2052/2292/2069 2062/2302/2078 2063/2303/2079 -f 1955/2195/1980 2054/2294/1969 2053/2293/2070 -f 2053/2293/2070 2063/2303/2079 1955/2195/1980 -f 2064/2304/2080 2065/2308/2081 2066/2310/2082 -f 2064/2304/2080 2066/2310/2082 2067/2311/2083 -f 2064/2304/2080 2067/2311/2083 2068/2312/2084 -f 2064/2304/2080 2068/2312/2084 2069/2313/2085 -f 2064/2304/2080 2069/2313/2085 2070/2314/2086 -f 2064/2304/2080 2070/2314/2086 2071/2315/2087 -f 2064/2304/2080 2071/2315/2087 2072/2316/2088 -f 2064/2304/2080 2072/2316/2088 2073/2317/2089 -f 2064/2304/2080 2073/2317/2089 2074/2318/2090 -f 2064/2304/2080 2074/2318/2090 2159/2419/2091 -f 2075/2319/2092 2066/2310/2082 2065/2308/2081 -f 2065/2308/2081 2076/2320/2093 2075/2319/2092 -f 2077/2322/2094 2067/2311/2083 2066/2310/2082 -f 2066/2310/2082 2075/2319/2092 2077/2322/2094 -f 2078/2323/2095 2068/2312/2084 2067/2311/2083 -f 2067/2311/2083 2077/2322/2094 2078/2323/2095 -f 2079/2324/2096 2069/2313/2085 2068/2312/2084 -f 2068/2312/2084 2078/2323/2095 2079/2324/2096 -f 2080/2325/2097 2070/2314/2086 2069/2313/2085 -f 2069/2313/2085 2079/2324/2096 2080/2325/2097 -f 2081/2326/2098 2071/2315/2087 2070/2314/2086 -f 2070/2314/2086 2080/2325/2097 2081/2326/2098 -f 2082/2327/2099 2072/2316/2088 2071/2315/2087 -f 2071/2315/2087 2081/2326/2098 2082/2327/2099 -f 2083/2328/2100 2073/2317/2089 2072/2316/2088 -f 2072/2316/2088 2082/2327/2099 2083/2328/2100 -f 2084/2329/2101 2074/2318/2090 2073/2317/2089 -f 2073/2317/2089 2083/2328/2100 2084/2329/2101 -f 2170/2431/2102 2159/2419/2091 2074/2318/2090 -f 2074/2318/2090 2084/2329/2101 2170/2431/2102 -f 2085/2330/2103 2075/2319/2092 2076/2320/2093 -f 2076/2320/2093 2382/2678/2104 2085/2330/2103 -f 2086/2331/2105 2077/2322/2094 2075/2319/2092 -f 2075/2319/2092 2085/2330/2103 2086/2331/2105 -f 2087/2332/2106 2078/2323/2095 2077/2322/2094 -f 2077/2322/2094 2086/2331/2105 2087/2332/2106 -f 2088/2333/2107 2079/2324/2096 2078/2323/2095 -f 2078/2323/2095 2087/2332/2106 2088/2333/2107 -f 2089/2334/2108 2080/2325/2097 2079/2324/2096 -f 2079/2324/2096 2088/2333/2107 2089/2334/2108 -f 2090/2335/2109 2081/2326/2098 2080/2325/2097 -f 2080/2325/2097 2089/2334/2108 2090/2335/2109 -f 2091/2336/2110 2082/2327/2099 2081/2326/2098 -f 2081/2326/2098 2090/2335/2109 2091/2336/2110 -f 2092/2337/2111 2083/2328/2100 2082/2327/2099 -f 2082/2327/2099 2091/2336/2110 2092/2337/2111 -f 2093/2338/2112 2084/2329/2101 2083/2328/2100 -f 2083/2328/2100 2092/2337/2111 2093/2338/2112 -f 2181/2444/2113 2170/2431/2102 2084/2329/2101 -f 2084/2329/2101 2093/2338/2112 2181/2444/2113 -f 2094/2339/2114 2085/2330/2103 2382/2678/2104 -f 2382/2678/2104 2393/2691/2115 2094/2339/2114 -f 2095/2340/2116 2086/2331/2105 2085/2330/2103 -f 2085/2330/2103 2094/2339/2114 2095/2340/2116 -f 2096/2341/2117 2087/2332/2106 2086/2331/2105 -f 2086/2331/2105 2095/2340/2116 2096/2341/2117 -f 2097/2342/2118 2088/2333/2107 2087/2332/2106 -f 2087/2332/2106 2096/2341/2117 2097/2342/2118 -f 2098/2343/2119 2089/2334/2108 2088/2333/2107 -f 2088/2333/2107 2097/2342/2118 2098/2343/2119 -f 2099/2344/2120 2090/2335/2109 2089/2334/2108 -f 2089/2334/2108 2098/2343/2119 2099/2344/2120 -f 2100/2345/2121 2091/2336/2110 2090/2335/2109 -f 2090/2335/2109 2099/2344/2120 2100/2345/2121 -f 2101/2346/2122 2092/2337/2111 2091/2336/2110 -f 2091/2336/2110 2100/2345/2121 2101/2346/2122 -f 2102/2347/2123 2093/2338/2112 2092/2337/2111 -f 2092/2337/2111 2101/2346/2122 2102/2347/2123 -f 2192/2457/2124 2181/2444/2113 2093/2338/2112 -f 2093/2338/2112 2102/2347/2123 2192/2457/2124 -f 2103/2348/2125 2094/2339/2114 2393/2691/2115 -f 2393/2691/2115 2104/2349/2126 2103/2348/2125 -f 2105/2351/2127 2095/2340/2116 2094/2339/2114 -f 2094/2339/2114 2103/2348/2125 2105/2351/2127 -f 2106/2352/2128 2096/2341/2117 2095/2340/2116 -f 2095/2340/2116 2105/2351/2127 2106/2352/2128 -f 2107/2353/2129 2097/2342/2118 2096/2341/2117 -f 2096/2341/2117 2106/2352/2128 2107/2353/2129 -f 2108/2354/2130 2098/2343/2119 2097/2342/2118 -f 2097/2342/2118 2107/2353/2129 2108/2354/2130 -f 2109/2355/2131 2099/2344/2120 2098/2343/2119 -f 2098/2343/2119 2108/2354/2130 2109/2355/2131 -f 2110/2356/2132 2100/2345/2121 2099/2344/2120 -f 2099/2344/2120 2109/2355/2131 2110/2356/2132 -f 2111/2357/2133 2101/2346/2122 2100/2345/2121 -f 2100/2345/2121 2110/2356/2132 2111/2357/2133 -f 2112/2358/2134 2102/2347/2123 2101/2346/2122 -f 2101/2346/2122 2111/2357/2133 2112/2358/2134 -f 2203/2470/2135 2192/2457/2124 2102/2347/2123 -f 2102/2347/2123 2112/2358/2134 2203/2470/2135 -f 2113/2359/2136 2103/2348/2125 2104/2349/2126 -f 2104/2349/2126 2412/2711/2137 2113/2359/2136 -f 2114/2360/2138 2105/2351/2127 2103/2348/2125 -f 2103/2348/2125 2113/2359/2136 2114/2360/2138 -f 2115/2361/2139 2106/2352/2128 2105/2351/2127 -f 2105/2351/2127 2114/2360/2138 2115/2361/2139 -f 2116/2362/2140 2107/2353/2129 2106/2352/2128 -f 2106/2352/2128 2115/2361/2139 2116/2362/2140 -f 2117/2363/2141 2108/2354/2130 2107/2353/2129 -f 2107/2353/2129 2116/2362/2140 2117/2363/2141 -f 2118/2364/2142 2109/2355/2131 2108/2354/2130 -f 2108/2354/2130 2117/2363/2141 2118/2364/2142 -f 2119/2365/2143 2110/2356/2132 2109/2355/2131 -f 2109/2355/2131 2118/2364/2142 2119/2365/2143 -f 2120/2366/2144 2111/2357/2133 2110/2356/2132 -f 2110/2356/2132 2119/2365/2143 2120/2366/2144 -f 2121/2367/2145 2112/2358/2134 2111/2357/2133 -f 2111/2357/2133 2120/2366/2144 2121/2367/2145 -f 2214/2483/2146 2203/2470/2135 2112/2358/2134 -f 2112/2358/2134 2121/2367/2145 2214/2483/2146 -f 2122/2368/2147 2113/2359/2136 2412/2711/2137 -f 2412/2711/2137 2422/2722/2148 2122/2368/2147 -f 2123/2369/2149 2114/2360/2138 2113/2359/2136 -f 2113/2359/2136 2122/2368/2147 2123/2369/2149 -f 2124/2370/2150 2115/2361/2139 2114/2360/2138 -f 2114/2360/2138 2123/2369/2149 2124/2370/2150 -f 2125/2371/2151 2116/2362/2140 2115/2361/2139 -f 2115/2361/2139 2124/2370/2150 2125/2371/2151 -f 2126/2372/2152 2117/2363/2141 2116/2362/2140 -f 2116/2362/2140 2125/2371/2151 2126/2372/2152 -f 2127/2373/2153 2118/2364/2142 2117/2363/2141 -f 2117/2363/2141 2126/2372/2152 2127/2373/2153 -f 2128/2374/2154 2119/2365/2143 2118/2364/2142 -f 2118/2364/2142 2127/2373/2153 2128/2374/2154 -f 2129/2375/2155 2120/2366/2144 2119/2365/2143 -f 2119/2365/2143 2128/2374/2154 2129/2375/2155 -f 2130/2376/2156 2121/2367/2145 2120/2366/2144 -f 2120/2366/2144 2129/2375/2155 2130/2376/2156 -f 2131/2377/2157 2214/2483/2146 2121/2367/2145 -f 2121/2367/2145 2130/2376/2156 2131/2377/2157 -f 2132/2379/2158 2122/2368/2147 2422/2722/2148 -f 2422/2722/2148 2432/2733/2159 2132/2379/2158 -f 2133/2380/2160 2123/2369/2149 2122/2368/2147 -f 2122/2368/2147 2132/2379/2158 2133/2380/2160 -f 2134/2381/2161 2124/2370/2150 2123/2369/2149 -f 2123/2369/2149 2133/2380/2160 2134/2381/2161 -f 2135/2382/2162 2125/2371/2151 2124/2370/2150 -f 2124/2370/2150 2134/2381/2161 2135/2382/2162 -f 2136/2383/2163 2126/2372/2152 2125/2371/2151 -f 2125/2371/2151 2135/2382/2162 2136/2383/2163 -f 2137/2384/2164 2127/2373/2153 2126/2372/2152 -f 2126/2372/2152 2136/2383/2163 2137/2384/2164 -f 2138/2385/2165 2128/2374/2154 2127/2373/2153 -f 2127/2373/2153 2137/2384/2164 2138/2385/2165 -f 2139/2386/2166 2129/2375/2155 2128/2374/2154 -f 2128/2374/2154 2138/2385/2165 2139/2386/2166 -f 2140/2387/2167 2130/2376/2156 2129/2375/2155 -f 2129/2375/2155 2139/2386/2166 2140/2387/2167 -f 2141/2388/2168 2131/2377/2157 2130/2376/2156 -f 2130/2376/2156 2140/2387/2167 2141/2388/2168 -f 2142/2390/2169 2132/2379/2158 2432/2733/2159 -f 2432/2733/2159 2442/2744/2170 2142/2390/2169 -f 2143/2391/2171 2133/2380/2160 2132/2379/2158 -f 2132/2379/2158 2142/2390/2169 2143/2391/2171 -f 2144/2392/2172 2134/2381/2161 2133/2380/2160 -f 2133/2380/2160 2143/2391/2171 2144/2392/2172 -f 2145/2393/2173 2135/2382/2162 2134/2381/2161 -f 2134/2381/2161 2144/2392/2172 2145/2393/2173 -f 2146/2394/2174 2136/2383/2163 2135/2382/2162 -f 2135/2382/2162 2145/2393/2173 2146/2394/2174 -f 2147/2395/2175 2137/2384/2164 2136/2383/2163 -f 2136/2383/2163 2146/2394/2174 2147/2395/2175 -f 2148/2396/2176 2138/2385/2165 2137/2384/2164 -f 2137/2384/2164 2147/2395/2175 2148/2396/2176 -f 2149/2397/2177 2139/2386/2166 2138/2385/2165 -f 2138/2385/2165 2148/2396/2176 2149/2397/2177 -f 2150/2398/2178 2140/2387/2167 2139/2386/2166 -f 2139/2386/2166 2149/2397/2177 2150/2398/2178 -f 2151/2399/2179 2141/2388/2168 2140/2387/2167 -f 2140/2387/2167 2150/2398/2178 2151/2399/2179 -f 2152/2401/2180 2142/2390/2169 2442/2744/2170 -f 2442/2744/2170 2153/2403/2181 2152/2401/2180 -f 2449/2756/2182 2143/2391/2171 2142/2390/2169 -f 2142/2390/2169 2152/2401/2180 2449/2756/2182 -f 2451/2759/2183 2144/2392/2172 2143/2391/2171 -f 2143/2391/2171 2449/2756/2182 2451/2759/2183 -f 2154/2407/2184 2145/2393/2173 2144/2392/2172 -f 2144/2392/2172 2451/2759/2183 2154/2407/2184 -f 2454/2763/2185 2146/2394/2174 2145/2393/2173 -f 2145/2393/2173 2154/2407/2184 2454/2763/2185 -f 2456/2766/2186 2147/2395/2175 2146/2394/2174 -f 2146/2394/2174 2454/2763/2185 2456/2766/2186 -f 2155/2409/2187 2148/2396/2176 2147/2395/2175 -f 2147/2395/2175 2456/2766/2186 2155/2409/2187 -f 2156/2411/2188 2149/2397/2177 2148/2396/2176 -f 2148/2396/2176 2155/2409/2187 2156/2411/2188 -f 2157/2413/2189 2150/2398/2178 2149/2397/2177 -f 2149/2397/2177 2156/2411/2188 2157/2413/2189 -f 2158/2415/2190 2151/2399/2179 2150/2398/2178 -f 2150/2398/2178 2157/2413/2189 2158/2415/2190 -f 2064/2305/2080 2159/2420/2091 2160/2421/2191 -f 2064/2305/2080 2160/2421/2191 2161/2422/2192 -f 2064/2305/2080 2161/2422/2192 2162/2423/2193 -f 2064/2305/2080 2162/2423/2193 2163/2424/2194 -f 2064/2305/2080 2163/2424/2194 2164/2425/2195 -f 2064/2305/2080 2164/2425/2195 2165/2426/2196 -f 2064/2305/2080 2165/2426/2196 2166/2427/2197 -f 2064/2305/2080 2166/2427/2197 2167/2428/2198 -f 2064/2305/2080 2167/2428/2198 2168/2429/2199 -f 2064/2305/2080 2168/2429/2199 2261/2542/2200 -f 2169/2430/2201 2160/2421/2191 2159/2420/2091 -f 2159/2420/2091 2170/2432/2102 2169/2430/2201 -f 2171/2433/2202 2161/2422/2192 2160/2421/2191 -f 2160/2421/2191 2169/2430/2201 2171/2433/2202 -f 2172/2434/2203 2162/2423/2193 2161/2422/2192 -f 2161/2422/2192 2171/2433/2202 2172/2434/2203 -f 2173/2435/2204 2163/2424/2194 2162/2423/2193 -f 2162/2423/2193 2172/2434/2203 2173/2435/2204 -f 2174/2436/2205 2164/2425/2195 2163/2424/2194 -f 2163/2424/2194 2173/2435/2204 2174/2436/2205 -f 2175/2437/2206 2165/2426/2196 2164/2425/2195 -f 2164/2425/2195 2174/2436/2205 2175/2437/2206 -f 2176/2438/2207 2166/2427/2197 2165/2426/2196 -f 2165/2426/2196 2175/2437/2206 2176/2438/2207 -f 2177/2439/2208 2167/2428/2198 2166/2427/2197 -f 2166/2427/2197 2176/2438/2207 2177/2439/2208 -f 2178/2440/2209 2168/2429/2199 2167/2428/2198 -f 2167/2428/2198 2177/2439/2208 2178/2440/2209 -f 2179/2441/2210 2261/2542/2200 2168/2429/2199 -f 2168/2429/2199 2178/2440/2209 2179/2441/2210 -f 2180/2443/2211 2169/2430/2201 2170/2432/2102 -f 2170/2432/2102 2181/2445/2113 2180/2443/2211 -f 2182/2446/2212 2171/2433/2202 2169/2430/2201 -f 2169/2430/2201 2180/2443/2211 2182/2446/2212 -f 2183/2447/2213 2172/2434/2203 2171/2433/2202 -f 2171/2433/2202 2182/2446/2212 2183/2447/2213 -f 2184/2448/2214 2173/2435/2204 2172/2434/2203 -f 2172/2434/2203 2183/2447/2213 2184/2448/2214 -f 2185/2449/2215 2174/2436/2205 2173/2435/2204 -f 2173/2435/2204 2184/2448/2214 2185/2449/2215 -f 2186/2450/2216 2175/2437/2206 2174/2436/2205 -f 2174/2436/2205 2185/2449/2215 2186/2450/2216 -f 2187/2451/2217 2176/2438/2207 2175/2437/2206 -f 2175/2437/2206 2186/2450/2216 2187/2451/2217 -f 2188/2452/2218 2177/2439/2208 2176/2438/2207 -f 2176/2438/2207 2187/2451/2217 2188/2452/2218 -f 2189/2453/2219 2178/2440/2209 2177/2439/2208 -f 2177/2439/2208 2188/2452/2218 2189/2453/2219 -f 2190/2454/2220 2179/2441/2210 2178/2440/2209 -f 2178/2440/2209 2189/2453/2219 2190/2454/2220 -f 2191/2456/2221 2180/2443/2211 2181/2445/2113 -f 2181/2445/2113 2192/2458/2124 2191/2456/2221 -f 2193/2459/2222 2182/2446/2212 2180/2443/2211 -f 2180/2443/2211 2191/2456/2221 2193/2459/2222 -f 2194/2460/2223 2183/2447/2213 2182/2446/2212 -f 2182/2446/2212 2193/2459/2222 2194/2460/2223 -f 2195/2461/2224 2184/2448/2214 2183/2447/2213 -f 2183/2447/2213 2194/2460/2223 2195/2461/2224 -f 2196/2462/2225 2185/2449/2215 2184/2448/2214 -f 2184/2448/2214 2195/2461/2224 2196/2462/2225 -f 2197/2463/2226 2186/2450/2216 2185/2449/2215 -f 2185/2449/2215 2196/2462/2225 2197/2463/2226 -f 2198/2464/2227 2187/2451/2217 2186/2450/2216 -f 2186/2450/2216 2197/2463/2226 2198/2464/2227 -f 2199/2465/2228 2188/2452/2218 2187/2451/2217 -f 2187/2451/2217 2198/2464/2227 2199/2465/2228 -f 2200/2466/2229 2189/2453/2219 2188/2452/2218 -f 2188/2452/2218 2199/2465/2228 2200/2466/2229 -f 2201/2467/2230 2190/2454/2220 2189/2453/2219 -f 2189/2453/2219 2200/2466/2229 2201/2467/2230 -f 2202/2469/2231 2191/2456/2221 2192/2458/2124 -f 2192/2458/2124 2203/2471/2135 2202/2469/2231 -f 2204/2472/2232 2193/2459/2222 2191/2456/2221 -f 2191/2456/2221 2202/2469/2231 2204/2472/2232 -f 2205/2473/2233 2194/2460/2223 2193/2459/2222 -f 2193/2459/2222 2204/2472/2232 2205/2473/2233 -f 2206/2474/2234 2195/2461/2224 2194/2460/2223 -f 2194/2460/2223 2205/2473/2233 2206/2474/2234 -f 2207/2475/2235 2196/2462/2225 2195/2461/2224 -f 2195/2461/2224 2206/2474/2234 2207/2475/2235 -f 2208/2476/2236 2197/2463/2226 2196/2462/2225 -f 2196/2462/2225 2207/2475/2235 2208/2476/2236 -f 2209/2477/2237 2198/2464/2227 2197/2463/2226 -f 2197/2463/2226 2208/2476/2236 2209/2477/2237 -f 2210/2478/2238 2199/2465/2228 2198/2464/2227 -f 2198/2464/2227 2209/2477/2237 2210/2478/2238 -f 2211/2479/2239 2200/2466/2229 2199/2465/2228 -f 2199/2465/2228 2210/2478/2238 2211/2479/2239 -f 2212/2480/2240 2201/2467/2230 2200/2466/2229 -f 2200/2466/2229 2211/2479/2239 2212/2480/2240 -f 2213/2482/2241 2202/2469/2231 2203/2471/2135 -f 2203/2471/2135 2214/2484/2146 2213/2482/2241 -f 2215/2485/2242 2204/2472/2232 2202/2469/2231 -f 2202/2469/2231 2213/2482/2241 2215/2485/2242 -f 2216/2486/2243 2205/2473/2233 2204/2472/2232 -f 2204/2472/2232 2215/2485/2242 2216/2486/2243 -f 2217/2487/2244 2206/2474/2234 2205/2473/2233 -f 2205/2473/2233 2216/2486/2243 2217/2487/2244 -f 2218/2488/2245 2207/2475/2235 2206/2474/2234 -f 2206/2474/2234 2217/2487/2244 2218/2488/2245 -f 2219/2489/2246 2208/2476/2236 2207/2475/2235 -f 2207/2475/2235 2218/2488/2245 2219/2489/2246 -f 2220/2490/2247 2209/2477/2237 2208/2476/2236 -f 2208/2476/2236 2219/2489/2246 2220/2490/2247 -f 2221/2491/2248 2210/2478/2238 2209/2477/2237 -f 2209/2477/2237 2220/2490/2247 2221/2491/2248 -f 2222/2492/2249 2211/2479/2239 2210/2478/2238 -f 2210/2478/2238 2221/2491/2248 2222/2492/2249 -f 2223/2493/2250 2212/2480/2240 2211/2479/2239 -f 2211/2479/2239 2222/2492/2249 2223/2493/2250 -f 2224/2495/2251 2213/2482/2241 2214/2484/2146 -f 2214/2484/2146 2131/2378/2157 2224/2495/2251 -f 2225/2496/2252 2215/2485/2242 2213/2482/2241 -f 2213/2482/2241 2224/2495/2251 2225/2496/2252 -f 2226/2497/2253 2216/2486/2243 2215/2485/2242 -f 2215/2485/2242 2225/2496/2252 2226/2497/2253 -f 2227/2498/2254 2217/2487/2244 2216/2486/2243 -f 2216/2486/2243 2226/2497/2253 2227/2498/2254 -f 2228/2499/2255 2218/2488/2245 2217/2487/2244 -f 2217/2487/2244 2227/2498/2254 2228/2499/2255 -f 2229/2500/2256 2219/2489/2246 2218/2488/2245 -f 2218/2488/2245 2228/2499/2255 2229/2500/2256 -f 2230/2501/2257 2220/2490/2247 2219/2489/2246 -f 2219/2489/2246 2229/2500/2256 2230/2501/2257 -f 2231/2502/2258 2221/2491/2248 2220/2490/2247 -f 2220/2490/2247 2230/2501/2257 2231/2502/2258 -f 2232/2503/2259 2222/2492/2249 2221/2491/2248 -f 2221/2491/2248 2231/2502/2258 2232/2503/2259 -f 2233/2504/2260 2223/2493/2250 2222/2492/2249 -f 2222/2492/2249 2232/2503/2259 2233/2504/2260 -f 2234/2506/2261 2224/2495/2251 2131/2378/2157 -f 2131/2378/2157 2141/2389/2168 2234/2506/2261 -f 2235/2507/2262 2225/2496/2252 2224/2495/2251 -f 2224/2495/2251 2234/2506/2261 2235/2507/2262 -f 2236/2508/2263 2226/2497/2253 2225/2496/2252 -f 2225/2496/2252 2235/2507/2262 2236/2508/2263 -f 2237/2509/2264 2227/2498/2254 2226/2497/2253 -f 2226/2497/2253 2236/2508/2263 2237/2509/2264 -f 2238/2510/2265 2228/2499/2255 2227/2498/2254 -f 2227/2498/2254 2237/2509/2264 2238/2510/2265 -f 2239/2511/2266 2229/2500/2256 2228/2499/2255 -f 2228/2499/2255 2238/2510/2265 2239/2511/2266 -f 2240/2512/2267 2230/2501/2257 2229/2500/2256 -f 2229/2500/2256 2239/2511/2266 2240/2512/2267 -f 2241/2513/2268 2231/2502/2258 2230/2501/2257 -f 2230/2501/2257 2240/2512/2267 2241/2513/2268 -f 2242/2514/2269 2232/2503/2259 2231/2502/2258 -f 2231/2502/2258 2241/2513/2268 2242/2514/2269 -f 2243/2515/2270 2233/2504/2260 2232/2503/2259 -f 2232/2503/2259 2242/2514/2269 2243/2515/2270 -f 2244/2517/2271 2234/2506/2261 2141/2389/2168 -f 2141/2389/2168 2151/2400/2179 2244/2517/2271 -f 2245/2518/2272 2235/2507/2262 2234/2506/2261 -f 2234/2506/2261 2244/2517/2271 2245/2518/2272 -f 2246/2519/2273 2236/2508/2263 2235/2507/2262 -f 2235/2507/2262 2245/2518/2272 2246/2519/2273 -f 2247/2520/2274 2237/2509/2264 2236/2508/2263 -f 2236/2508/2263 2246/2519/2273 2247/2520/2274 -f 2248/2521/2275 2238/2510/2265 2237/2509/2264 -f 2237/2509/2264 2247/2520/2274 2248/2521/2275 -f 2249/2522/2276 2239/2511/2266 2238/2510/2265 -f 2238/2510/2265 2248/2521/2275 2249/2522/2276 -f 2250/2523/2277 2240/2512/2267 2239/2511/2266 -f 2239/2511/2266 2249/2522/2276 2250/2523/2277 -f 2251/2524/2278 2241/2513/2268 2240/2512/2267 -f 2240/2512/2267 2250/2523/2277 2251/2524/2278 -f 2252/2525/2279 2242/2514/2269 2241/2513/2268 -f 2241/2513/2268 2251/2524/2278 2252/2525/2279 -f 2253/2526/2280 2243/2515/2270 2242/2514/2269 -f 2242/2514/2269 2252/2525/2279 2253/2526/2280 -f 2254/2528/2281 2244/2517/2271 2151/2400/2179 -f 2151/2400/2179 2158/2416/2190 2254/2528/2281 -f 2255/2530/2282 2245/2518/2272 2244/2517/2271 -f 2244/2517/2271 2254/2528/2281 2255/2530/2282 -f 2256/2532/2283 2246/2519/2273 2245/2518/2272 -f 2245/2518/2272 2255/2530/2282 2256/2532/2283 -f 2551/2868/2284 2247/2520/2274 2246/2519/2273 -f 2246/2519/2273 2256/2532/2283 2551/2868/2284 -f 2553/2871/2285 2248/2521/2275 2247/2520/2274 -f 2247/2520/2274 2551/2868/2284 2553/2871/2285 -f 2257/2534/2286 2249/2522/2276 2248/2521/2275 -f 2248/2521/2275 2553/2871/2285 2257/2534/2286 -f 2258/2536/2287 2250/2523/2277 2249/2522/2276 -f 2249/2522/2276 2257/2534/2286 2258/2536/2287 -f 2259/2538/2288 2251/2524/2278 2250/2523/2277 -f 2250/2523/2277 2258/2536/2287 2259/2538/2288 -f 2260/2540/2289 2252/2525/2279 2251/2524/2278 -f 2251/2524/2278 2259/2538/2288 2260/2540/2289 -f 2559/2879/2290 2253/2526/2280 2252/2525/2279 -f 2252/2525/2279 2260/2540/2289 2559/2879/2290 -f 2064/2306/2080 2261/2543/2200 2262/2544/2291 -f 2064/2306/2080 2262/2544/2291 2263/2545/2292 -f 2064/2306/2080 2263/2545/2292 2264/2546/2293 -f 2064/2306/2080 2264/2546/2293 2265/2547/2294 -f 2064/2306/2080 2265/2547/2294 2266/2548/2295 -f 2064/2306/2080 2266/2548/2295 2267/2549/2296 -f 2064/2306/2080 2267/2549/2296 2268/2550/2297 -f 2064/2306/2080 2268/2550/2297 2269/2551/2298 -f 2064/2306/2080 2269/2551/2298 2270/2552/2299 -f 2064/2306/2080 2270/2552/2299 2271/2553/2300 -f 2272/2555/2301 2262/2544/2291 2261/2543/2200 -f 2261/2543/2200 2179/2442/2210 2272/2555/2301 -f 2273/2556/2302 2263/2545/2292 2262/2544/2291 -f 2262/2544/2291 2272/2555/2301 2273/2556/2302 -f 2274/2557/2303 2264/2546/2293 2263/2545/2292 -f 2263/2545/2292 2273/2556/2302 2274/2557/2303 -f 2275/2558/2304 2265/2547/2294 2264/2546/2293 -f 2264/2546/2293 2274/2557/2303 2275/2558/2304 -f 2276/2559/2305 2266/2548/2295 2265/2547/2294 -f 2265/2547/2294 2275/2558/2304 2276/2559/2305 -f 2277/2560/2306 2267/2549/2296 2266/2548/2295 -f 2266/2548/2295 2276/2559/2305 2277/2560/2306 -f 2278/2561/2307 2268/2550/2297 2267/2549/2296 -f 2267/2549/2296 2277/2560/2306 2278/2561/2307 -f 2279/2562/2308 2269/2551/2298 2268/2550/2297 -f 2268/2550/2297 2278/2561/2307 2279/2562/2308 -f 2280/2563/2309 2270/2552/2299 2269/2551/2298 -f 2269/2551/2298 2279/2562/2308 2280/2563/2309 -f 2281/2564/2310 2271/2553/2300 2270/2552/2299 -f 2270/2552/2299 2280/2563/2309 2281/2564/2310 -f 2282/2566/2311 2272/2555/2301 2179/2442/2210 -f 2179/2442/2210 2190/2455/2220 2282/2566/2311 -f 2283/2567/2312 2273/2556/2302 2272/2555/2301 -f 2272/2555/2301 2282/2566/2311 2283/2567/2312 -f 2284/2568/2313 2274/2557/2303 2273/2556/2302 -f 2273/2556/2302 2283/2567/2312 2284/2568/2313 -f 2285/2569/2314 2275/2558/2304 2274/2557/2303 -f 2274/2557/2303 2284/2568/2313 2285/2569/2314 -f 2286/2570/2315 2276/2559/2305 2275/2558/2304 -f 2275/2558/2304 2285/2569/2314 2286/2570/2315 -f 2287/2571/2316 2277/2560/2306 2276/2559/2305 -f 2276/2559/2305 2286/2570/2315 2287/2571/2316 -f 2288/2572/2317 2278/2561/2307 2277/2560/2306 -f 2277/2560/2306 2287/2571/2316 2288/2572/2317 -f 2289/2573/2318 2279/2562/2308 2278/2561/2307 -f 2278/2561/2307 2288/2572/2317 2289/2573/2318 -f 2290/2574/2319 2280/2563/2309 2279/2562/2308 -f 2279/2562/2308 2289/2573/2318 2290/2574/2319 -f 2373/2668/2320 2281/2564/2310 2280/2563/2309 -f 2280/2563/2309 2290/2574/2319 2373/2668/2320 -f 2291/2575/2321 2282/2566/2311 2190/2455/2220 -f 2190/2455/2220 2201/2468/2230 2291/2575/2321 -f 2292/2576/2322 2283/2567/2312 2282/2566/2311 -f 2282/2566/2311 2291/2575/2321 2292/2576/2322 -f 2293/2577/2323 2284/2568/2313 2283/2567/2312 -f 2283/2567/2312 2292/2576/2322 2293/2577/2323 -f 2294/2578/2324 2285/2569/2314 2284/2568/2313 -f 2284/2568/2313 2293/2577/2323 2294/2578/2324 -f 2295/2579/2325 2286/2570/2315 2285/2569/2314 -f 2285/2569/2314 2294/2578/2324 2295/2579/2325 -f 2296/2580/2326 2287/2571/2316 2286/2570/2315 -f 2286/2570/2315 2295/2579/2325 2296/2580/2326 -f 2297/2581/2327 2288/2572/2317 2287/2571/2316 -f 2287/2571/2316 2296/2580/2326 2297/2581/2327 -f 2298/2582/2328 2289/2573/2318 2288/2572/2317 -f 2288/2572/2317 2297/2581/2327 2298/2582/2328 -f 2299/2583/2329 2290/2574/2319 2289/2573/2318 -f 2289/2573/2318 2298/2582/2328 2299/2583/2329 -f 2384/2681/2330 2373/2668/2320 2290/2574/2319 -f 2290/2574/2319 2299/2583/2329 2384/2681/2330 -f 2300/2584/2331 2291/2575/2321 2201/2468/2230 -f 2201/2468/2230 2212/2481/2240 2300/2584/2331 -f 2301/2585/2332 2292/2576/2322 2291/2575/2321 -f 2291/2575/2321 2300/2584/2331 2301/2585/2332 -f 2302/2586/2333 2293/2577/2323 2292/2576/2322 -f 2292/2576/2322 2301/2585/2332 2302/2586/2333 -f 2303/2587/2334 2294/2578/2324 2293/2577/2323 -f 2293/2577/2323 2302/2586/2333 2303/2587/2334 -f 2304/2588/2335 2295/2579/2325 2294/2578/2324 -f 2294/2578/2324 2303/2587/2334 2304/2588/2335 -f 2305/2589/2336 2296/2580/2326 2295/2579/2325 -f 2295/2579/2325 2304/2588/2335 2305/2589/2336 -f 2306/2590/2337 2297/2581/2327 2296/2580/2326 -f 2296/2580/2326 2305/2589/2336 2306/2590/2337 -f 2307/2591/2338 2298/2582/2328 2297/2581/2327 -f 2297/2581/2327 2306/2590/2337 2307/2591/2338 -f 2308/2592/2339 2299/2583/2329 2298/2582/2328 -f 2298/2582/2328 2307/2591/2338 2308/2592/2339 -f 2309/2593/2340 2384/2681/2330 2299/2583/2329 -f 2299/2583/2329 2308/2592/2339 2309/2593/2340 -f 2310/2595/2341 2300/2584/2331 2212/2481/2240 -f 2212/2481/2240 2223/2494/2250 2310/2595/2341 -f 2311/2596/2342 2301/2585/2332 2300/2584/2331 -f 2300/2584/2331 2310/2595/2341 2311/2596/2342 -f 2312/2597/2343 2302/2586/2333 2301/2585/2332 -f 2301/2585/2332 2311/2596/2342 2312/2597/2343 -f 2313/2598/2344 2303/2587/2334 2302/2586/2333 -f 2302/2586/2333 2312/2597/2343 2313/2598/2344 -f 2314/2599/2345 2304/2588/2335 2303/2587/2334 -f 2303/2587/2334 2313/2598/2344 2314/2599/2345 -f 2315/2600/2346 2305/2589/2336 2304/2588/2335 -f 2304/2588/2335 2314/2599/2345 2315/2600/2346 -f 2316/2601/2347 2306/2590/2337 2305/2589/2336 -f 2305/2589/2336 2315/2600/2346 2316/2601/2347 -f 2317/2602/2348 2307/2591/2338 2306/2590/2337 -f 2306/2590/2337 2316/2601/2347 2317/2602/2348 -f 2318/2603/2349 2308/2592/2339 2307/2591/2338 -f 2307/2591/2338 2317/2602/2348 2318/2603/2349 -f 2319/2604/2350 2309/2593/2340 2308/2592/2339 -f 2308/2592/2339 2318/2603/2349 2319/2604/2350 -f 2320/2606/2351 2310/2595/2341 2223/2494/2250 -f 2223/2494/2250 2233/2505/2260 2320/2606/2351 -f 2321/2607/2352 2311/2596/2342 2310/2595/2341 -f 2310/2595/2341 2320/2606/2351 2321/2607/2352 -f 2322/2608/2353 2312/2597/2343 2311/2596/2342 -f 2311/2596/2342 2321/2607/2352 2322/2608/2353 -f 2323/2609/2354 2313/2598/2344 2312/2597/2343 -f 2312/2597/2343 2322/2608/2353 2323/2609/2354 -f 2324/2610/2355 2314/2599/2345 2313/2598/2344 -f 2313/2598/2344 2323/2609/2354 2324/2610/2355 -f 2325/2611/2356 2315/2600/2346 2314/2599/2345 -f 2314/2599/2345 2324/2610/2355 2325/2611/2356 -f 2326/2612/2357 2316/2601/2347 2315/2600/2346 -f 2315/2600/2346 2325/2611/2356 2326/2612/2357 -f 2327/2613/2358 2317/2602/2348 2316/2601/2347 -f 2316/2601/2347 2326/2612/2357 2327/2613/2358 -f 2328/2614/2359 2318/2603/2349 2317/2602/2348 -f 2317/2602/2348 2327/2613/2358 2328/2614/2359 -f 2329/2615/2360 2319/2604/2350 2318/2603/2349 -f 2318/2603/2349 2328/2614/2359 2329/2615/2360 -f 2330/2617/2361 2320/2606/2351 2233/2505/2260 -f 2233/2505/2260 2243/2516/2270 2330/2617/2361 -f 2331/2618/2362 2321/2607/2352 2320/2606/2351 -f 2320/2606/2351 2330/2617/2361 2331/2618/2362 -f 2332/2619/2363 2322/2608/2353 2321/2607/2352 -f 2321/2607/2352 2331/2618/2362 2332/2619/2363 -f 2333/2620/2364 2323/2609/2354 2322/2608/2353 -f 2322/2608/2353 2332/2619/2363 2333/2620/2364 -f 2334/2621/2365 2324/2610/2355 2323/2609/2354 -f 2323/2609/2354 2333/2620/2364 2334/2621/2365 -f 2335/2622/2366 2325/2611/2356 2324/2610/2355 -f 2324/2610/2355 2334/2621/2365 2335/2622/2366 -f 2336/2623/2367 2326/2612/2357 2325/2611/2356 -f 2325/2611/2356 2335/2622/2366 2336/2623/2367 -f 2337/2624/2368 2327/2613/2358 2326/2612/2357 -f 2326/2612/2357 2336/2623/2367 2337/2624/2368 -f 2338/2625/2369 2328/2614/2359 2327/2613/2358 -f 2327/2613/2358 2337/2624/2368 2338/2625/2369 -f 2339/2626/2370 2329/2615/2360 2328/2614/2359 -f 2328/2614/2359 2338/2625/2369 2339/2626/2370 -f 2340/2628/2371 2330/2617/2361 2243/2516/2270 -f 2243/2516/2270 2253/2527/2280 2340/2628/2371 -f 2341/2629/2372 2331/2618/2362 2330/2617/2361 -f 2330/2617/2361 2340/2628/2371 2341/2629/2372 -f 2342/2630/2373 2332/2619/2363 2331/2618/2362 -f 2331/2618/2362 2341/2629/2372 2342/2630/2373 -f 2343/2631/2374 2333/2620/2364 2332/2619/2363 -f 2332/2619/2363 2342/2630/2373 2343/2631/2374 -f 2344/2632/2375 2334/2621/2365 2333/2620/2364 -f 2333/2620/2364 2343/2631/2374 2344/2632/2375 -f 2345/2633/2376 2335/2622/2366 2334/2621/2365 -f 2334/2621/2365 2344/2632/2375 2345/2633/2376 -f 2346/2634/2377 2336/2623/2367 2335/2622/2366 -f 2335/2622/2366 2345/2633/2376 2346/2634/2377 -f 2347/2635/2378 2337/2624/2368 2336/2623/2367 -f 2336/2623/2367 2346/2634/2377 2347/2635/2378 -f 2348/2636/2379 2338/2625/2369 2337/2624/2368 -f 2337/2624/2368 2347/2635/2378 2348/2636/2379 -f 2349/2637/2380 2339/2626/2370 2338/2625/2369 -f 2338/2625/2369 2348/2636/2379 2349/2637/2380 -f 2652/2985/2381 2340/2628/2371 2253/2527/2280 -f 2253/2527/2280 2559/2880/2290 2652/2985/2381 -f 2350/2639/2382 2341/2629/2372 2340/2628/2371 -f 2340/2628/2371 2652/2985/2381 2350/2639/2382 -f 2655/2989/2383 2342/2630/2373 2341/2629/2372 -f 2341/2629/2372 2350/2639/2382 2655/2989/2383 -f 2351/2641/2384 2343/2631/2374 2342/2630/2373 -f 2342/2630/2373 2655/2989/2383 2351/2641/2384 -f 2658/2993/2385 2344/2632/2375 2343/2631/2374 -f 2343/2631/2374 2351/2641/2384 2658/2993/2385 -f 2660/2996/2386 2345/2633/2376 2344/2632/2375 -f 2344/2632/2375 2658/2993/2385 2660/2996/2386 -f 2352/2643/2387 2346/2634/2377 2345/2633/2376 -f 2345/2633/2376 2660/2996/2386 2352/2643/2387 -f 2663/3000/2388 2347/2635/2378 2346/2634/2377 -f 2346/2634/2377 2352/2643/2387 2663/3000/2388 -f 2665/3003/2389 2348/2636/2379 2347/2635/2378 -f 2347/2635/2378 2663/3000/2388 2665/3003/2389 -f 2353/2645/2390 2349/2637/2380 2348/2636/2379 -f 2348/2636/2379 2665/3003/2389 2353/2645/2390 -f 2064/2307/2080 2271/2554/2300 2354/2649/2391 -f 2064/2307/2080 2354/2649/2391 2355/2650/2392 -f 2064/2307/2080 2355/2650/2392 2356/2651/2393 -f 2064/2307/2080 2356/2651/2393 2357/2652/2394 -f 2064/2307/2080 2357/2652/2394 2358/2653/2395 -f 2064/2307/2080 2358/2653/2395 2359/2654/2396 -f 2064/2307/2080 2359/2654/2396 2360/2655/2397 -f 2064/2307/2080 2360/2655/2397 2361/2656/2398 -f 2064/2307/2080 2361/2656/2398 2362/2657/2399 -f 2064/2307/2080 2362/2657/2399 2065/2309/2081 -f 2363/2658/2400 2354/2649/2391 2271/2554/2300 -f 2271/2554/2300 2281/2565/2310 2363/2658/2400 -f 2364/2659/2401 2355/2650/2392 2354/2649/2391 -f 2354/2649/2391 2363/2658/2400 2364/2659/2401 -f 2365/2660/2402 2356/2651/2393 2355/2650/2392 -f 2355/2650/2392 2364/2659/2401 2365/2660/2402 -f 2366/2661/2403 2357/2652/2394 2356/2651/2393 -f 2356/2651/2393 2365/2660/2402 2366/2661/2403 -f 2367/2662/2404 2358/2653/2395 2357/2652/2394 -f 2357/2652/2394 2366/2661/2403 2367/2662/2404 -f 2368/2663/2405 2359/2654/2396 2358/2653/2395 -f 2358/2653/2395 2367/2662/2404 2368/2663/2405 -f 2369/2664/2406 2360/2655/2397 2359/2654/2396 -f 2359/2654/2396 2368/2663/2405 2369/2664/2406 -f 2370/2665/2407 2361/2656/2398 2360/2655/2397 -f 2360/2655/2397 2369/2664/2406 2370/2665/2407 -f 2371/2666/2408 2362/2657/2399 2361/2656/2398 -f 2361/2656/2398 2370/2665/2407 2371/2666/2408 -f 2076/2321/2093 2065/2309/2081 2362/2657/2399 -f 2362/2657/2399 2371/2666/2408 2076/2321/2093 -f 2372/2667/2409 2363/2658/2400 2281/2565/2310 -f 2281/2565/2310 2373/2669/2320 2372/2667/2409 -f 2374/2670/2410 2364/2659/2401 2363/2658/2400 -f 2363/2658/2400 2372/2667/2409 2374/2670/2410 -f 2375/2671/2411 2365/2660/2402 2364/2659/2401 -f 2364/2659/2401 2374/2670/2410 2375/2671/2411 -f 2376/2672/2412 2366/2661/2403 2365/2660/2402 -f 2365/2660/2402 2375/2671/2411 2376/2672/2412 -f 2377/2673/2413 2367/2662/2404 2366/2661/2403 -f 2366/2661/2403 2376/2672/2412 2377/2673/2413 -f 2378/2674/2414 2368/2663/2405 2367/2662/2404 -f 2367/2662/2404 2377/2673/2413 2378/2674/2414 -f 2379/2675/2415 2369/2664/2406 2368/2663/2405 -f 2368/2663/2405 2378/2674/2414 2379/2675/2415 -f 2380/2676/2416 2370/2665/2407 2369/2664/2406 -f 2369/2664/2406 2379/2675/2415 2380/2676/2416 -f 2381/2677/2417 2371/2666/2408 2370/2665/2407 -f 2370/2665/2407 2380/2676/2416 2381/2677/2417 -f 2382/2679/2104 2076/2321/2093 2371/2666/2408 -f 2371/2666/2408 2381/2677/2417 2382/2679/2104 -f 2383/2680/2418 2372/2667/2409 2373/2669/2320 -f 2373/2669/2320 2384/2682/2330 2383/2680/2418 -f 2385/2683/2419 2374/2670/2410 2372/2667/2409 -f 2372/2667/2409 2383/2680/2418 2385/2683/2419 -f 2386/2684/2420 2375/2671/2411 2374/2670/2410 -f 2374/2670/2410 2385/2683/2419 2386/2684/2420 -f 2387/2685/2421 2376/2672/2412 2375/2671/2411 -f 2375/2671/2411 2386/2684/2420 2387/2685/2421 -f 2388/2686/2422 2377/2673/2413 2376/2672/2412 -f 2376/2672/2412 2387/2685/2421 2388/2686/2422 -f 2389/2687/2423 2378/2674/2414 2377/2673/2413 -f 2377/2673/2413 2388/2686/2422 2389/2687/2423 -f 2390/2688/2424 2379/2675/2415 2378/2674/2414 -f 2378/2674/2414 2389/2687/2423 2390/2688/2424 -f 2391/2689/2425 2380/2676/2416 2379/2675/2415 -f 2379/2675/2415 2390/2688/2424 2391/2689/2425 -f 2392/2690/2426 2381/2677/2417 2380/2676/2416 -f 2380/2676/2416 2391/2689/2425 2392/2690/2426 -f 2393/2692/2115 2382/2679/2104 2381/2677/2417 -f 2381/2677/2417 2392/2690/2426 2393/2692/2115 -f 2394/2693/2427 2383/2680/2418 2384/2682/2330 -f 2384/2682/2330 2309/2594/2340 2394/2693/2427 -f 2395/2694/2428 2385/2683/2419 2383/2680/2418 -f 2383/2680/2418 2394/2693/2427 2395/2694/2428 -f 2396/2695/2429 2386/2684/2420 2385/2683/2419 -f 2385/2683/2419 2395/2694/2428 2396/2695/2429 -f 2397/2696/2430 2387/2685/2421 2386/2684/2420 -f 2386/2684/2420 2396/2695/2429 2397/2696/2430 -f 2398/2697/2431 2388/2686/2422 2387/2685/2421 -f 2387/2685/2421 2397/2696/2430 2398/2697/2431 -f 2399/2698/2432 2389/2687/2423 2388/2686/2422 -f 2388/2686/2422 2398/2697/2431 2399/2698/2432 -f 2400/2699/2433 2390/2688/2424 2389/2687/2423 -f 2389/2687/2423 2399/2698/2432 2400/2699/2433 -f 2401/2700/2434 2391/2689/2425 2390/2688/2424 -f 2390/2688/2424 2400/2699/2433 2401/2700/2434 -f 2402/2701/2435 2392/2690/2426 2391/2689/2425 -f 2391/2689/2425 2401/2700/2434 2402/2701/2435 -f 2104/2350/2126 2393/2692/2115 2392/2690/2426 -f 2392/2690/2426 2402/2701/2435 2104/2350/2126 -f 2403/2702/2436 2394/2693/2427 2309/2594/2340 -f 2309/2594/2340 2319/2605/2350 2403/2702/2436 -f 2404/2703/2437 2395/2694/2428 2394/2693/2427 -f 2394/2693/2427 2403/2702/2436 2404/2703/2437 -f 2405/2704/2438 2396/2695/2429 2395/2694/2428 -f 2395/2694/2428 2404/2703/2437 2405/2704/2438 -f 2406/2705/2439 2397/2696/2430 2396/2695/2429 -f 2396/2695/2429 2405/2704/2438 2406/2705/2439 -f 2407/2706/2440 2398/2697/2431 2397/2696/2430 -f 2397/2696/2430 2406/2705/2439 2407/2706/2440 -f 2408/2707/2441 2399/2698/2432 2398/2697/2431 -f 2398/2697/2431 2407/2706/2440 2408/2707/2441 -f 2409/2708/2442 2400/2699/2433 2399/2698/2432 -f 2399/2698/2432 2408/2707/2441 2409/2708/2442 -f 2410/2709/2443 2401/2700/2434 2400/2699/2433 -f 2400/2699/2433 2409/2708/2442 2410/2709/2443 -f 2411/2710/2444 2402/2701/2435 2401/2700/2434 -f 2401/2700/2434 2410/2709/2443 2411/2710/2444 -f 2412/2712/2137 2104/2350/2126 2402/2701/2435 -f 2402/2701/2435 2411/2710/2444 2412/2712/2137 -f 2413/2713/2445 2403/2702/2436 2319/2605/2350 -f 2319/2605/2350 2329/2616/2360 2413/2713/2445 -f 2414/2714/2446 2404/2703/2437 2403/2702/2436 -f 2403/2702/2436 2413/2713/2445 2414/2714/2446 -f 2415/2715/2447 2405/2704/2438 2404/2703/2437 -f 2404/2703/2437 2414/2714/2446 2415/2715/2447 -f 2416/2716/2448 2406/2705/2439 2405/2704/2438 -f 2405/2704/2438 2415/2715/2447 2416/2716/2448 -f 2417/2717/2449 2407/2706/2440 2406/2705/2439 -f 2406/2705/2439 2416/2716/2448 2417/2717/2449 -f 2418/2718/2450 2408/2707/2441 2407/2706/2440 -f 2407/2706/2440 2417/2717/2449 2418/2718/2450 -f 2419/2719/2451 2409/2708/2442 2408/2707/2441 -f 2408/2707/2441 2418/2718/2450 2419/2719/2451 -f 2420/2720/2452 2410/2709/2443 2409/2708/2442 -f 2409/2708/2442 2419/2719/2451 2420/2720/2452 -f 2421/2721/2453 2411/2710/2444 2410/2709/2443 -f 2410/2709/2443 2420/2720/2452 2421/2721/2453 -f 2422/2723/2148 2412/2712/2137 2411/2710/2444 -f 2411/2710/2444 2421/2721/2453 2422/2723/2148 -f 2423/2724/2454 2413/2713/2445 2329/2616/2360 -f 2329/2616/2360 2339/2627/2370 2423/2724/2454 -f 2424/2725/2455 2414/2714/2446 2413/2713/2445 -f 2413/2713/2445 2423/2724/2454 2424/2725/2455 -f 2425/2726/2456 2415/2715/2447 2414/2714/2446 -f 2414/2714/2446 2424/2725/2455 2425/2726/2456 -f 2426/2727/2457 2416/2716/2448 2415/2715/2447 -f 2415/2715/2447 2425/2726/2456 2426/2727/2457 -f 2427/2728/2458 2417/2717/2449 2416/2716/2448 -f 2416/2716/2448 2426/2727/2457 2427/2728/2458 -f 2428/2729/2459 2418/2718/2450 2417/2717/2449 -f 2417/2717/2449 2427/2728/2458 2428/2729/2459 -f 2429/2730/2460 2419/2719/2451 2418/2718/2450 -f 2418/2718/2450 2428/2729/2459 2429/2730/2460 -f 2430/2731/2461 2420/2720/2452 2419/2719/2451 -f 2419/2719/2451 2429/2730/2460 2430/2731/2461 -f 2431/2732/2462 2421/2721/2453 2420/2720/2452 -f 2420/2720/2452 2430/2731/2461 2431/2732/2462 -f 2432/2734/2159 2422/2723/2148 2421/2721/2453 -f 2421/2721/2453 2431/2732/2462 2432/2734/2159 -f 2433/2735/2463 2423/2724/2454 2339/2627/2370 -f 2339/2627/2370 2349/2638/2380 2433/2735/2463 -f 2434/2736/2464 2424/2725/2455 2423/2724/2454 -f 2423/2724/2454 2433/2735/2463 2434/2736/2464 -f 2435/2737/2465 2425/2726/2456 2424/2725/2455 -f 2424/2725/2455 2434/2736/2464 2435/2737/2465 -f 2436/2738/2466 2426/2727/2457 2425/2726/2456 -f 2425/2726/2456 2435/2737/2465 2436/2738/2466 -f 2437/2739/2467 2427/2728/2458 2426/2727/2457 -f 2426/2727/2457 2436/2738/2466 2437/2739/2467 -f 2438/2740/2468 2428/2729/2459 2427/2728/2458 -f 2427/2728/2458 2437/2739/2467 2438/2740/2468 -f 2439/2741/2469 2429/2730/2460 2428/2729/2459 -f 2428/2729/2459 2438/2740/2468 2439/2741/2469 -f 2440/2742/2470 2430/2731/2461 2429/2730/2460 -f 2429/2730/2460 2439/2741/2469 2440/2742/2470 -f 2441/2743/2471 2431/2732/2462 2430/2731/2461 -f 2430/2731/2461 2440/2742/2470 2441/2743/2471 -f 2442/2745/2170 2432/2734/2159 2431/2732/2462 -f 2431/2732/2462 2441/2743/2471 2442/2745/2170 -f 2443/2746/2472 2433/2735/2463 2349/2638/2380 -f 2349/2638/2380 2353/2646/2390 2443/2746/2472 -f 2762/3114/2473 2434/2736/2464 2433/2735/2463 -f 2433/2735/2463 2443/2746/2472 2762/3114/2473 -f 2764/3117/2474 2435/2737/2465 2434/2736/2464 -f 2434/2736/2464 2762/3114/2473 2764/3117/2474 -f 2444/2748/2475 2436/2738/2466 2435/2737/2465 -f 2435/2737/2465 2764/3117/2474 2444/2748/2475 -f 2445/2750/2476 2437/2739/2467 2436/2738/2466 -f 2436/2738/2466 2444/2748/2475 2445/2750/2476 -f 2768/3122/2477 2438/2740/2468 2437/2739/2467 -f 2437/2739/2467 2445/2750/2476 2768/3122/2477 -f 2770/3125/2478 2439/2741/2469 2438/2740/2468 -f 2438/2740/2468 2768/3122/2477 2770/3125/2478 -f 2772/3128/2479 2440/2742/2470 2439/2741/2469 -f 2439/2741/2469 2770/3125/2478 2772/3128/2479 -f 2774/3131/2480 2441/2743/2471 2440/2742/2470 -f 2440/2742/2470 2772/3128/2479 2774/3131/2480 -f 2153/2404/2181 2442/2745/2170 2441/2743/2471 -f 2441/2743/2471 2774/3131/2480 2153/2404/2181 -f 2446/2752/2481 2152/2402/2180 2153/2405/2181 -f 2153/2405/2181 2447/2753/2482 2446/2752/2481 -f 2448/2755/2483 2449/2757/2182 2152/2402/2180 -f 2152/2402/2180 2446/2752/2481 2448/2755/2483 -f 2450/2758/2484 2451/2760/2183 2449/2757/2182 -f 2449/2757/2182 2448/2755/2483 2450/2758/2484 -f 2452/2761/2485 2154/2408/2184 2451/2760/2183 -f 2451/2760/2183 2450/2758/2484 2452/2761/2485 -f 2453/2762/2486 2454/2764/2185 2154/2408/2184 -f 2154/2408/2184 2452/2761/2485 2453/2762/2486 -f 2455/2765/2487 2456/2767/2186 2454/2764/2185 -f 2454/2764/2185 2453/2762/2486 2455/2765/2487 -f 2457/2768/2488 2155/2410/2187 2456/2767/2186 -f 2456/2767/2186 2455/2765/2487 2457/2768/2488 -f 2458/2769/2489 2156/2412/2188 2155/2410/2187 -f 2155/2410/2187 2457/2768/2488 2458/2769/2489 -f 2459/2770/2490 2157/2414/2189 2156/2412/2188 -f 2156/2412/2188 2458/2769/2489 2459/2770/2490 -f 2460/2771/2491 2158/2417/2190 2157/2414/2189 -f 2157/2414/2189 2459/2770/2490 2460/2771/2491 -f 2461/2773/2492 2446/2752/2481 2447/2753/2482 -f 2447/2753/2482 2462/2774/2493 2461/2773/2492 -f 2463/2776/2494 2448/2755/2483 2446/2752/2481 -f 2446/2752/2481 2461/2773/2492 2463/2776/2494 -f 2464/2777/2495 2450/2758/2484 2448/2755/2483 -f 2448/2755/2483 2463/2776/2494 2464/2777/2495 -f 2465/2778/2496 2452/2761/2485 2450/2758/2484 -f 2450/2758/2484 2464/2777/2495 2465/2778/2496 -f 2466/2779/2497 2453/2762/2486 2452/2761/2485 -f 2452/2761/2485 2465/2778/2496 2466/2779/2497 -f 2467/2780/2498 2455/2765/2487 2453/2762/2486 -f 2453/2762/2486 2466/2779/2497 2467/2780/2498 -f 2468/2781/2499 2457/2768/2488 2455/2765/2487 -f 2455/2765/2487 2467/2780/2498 2468/2781/2499 -f 2469/2782/2500 2458/2769/2489 2457/2768/2488 -f 2457/2768/2488 2468/2781/2499 2469/2782/2500 -f 2470/2783/2501 2459/2770/2490 2458/2769/2489 -f 2458/2769/2489 2469/2782/2500 2470/2783/2501 -f 2561/2884/2502 2460/2771/2491 2459/2770/2490 -f 2459/2770/2490 2470/2783/2501 2561/2884/2502 -f 2471/2784/2503 2461/2773/2492 2462/2774/2493 -f 2462/2774/2493 2793/3151/2504 2471/2784/2503 -f 2472/2785/2505 2463/2776/2494 2461/2773/2492 -f 2461/2773/2492 2471/2784/2503 2472/2785/2505 -f 2473/2786/2506 2464/2777/2495 2463/2776/2494 -f 2463/2776/2494 2472/2785/2505 2473/2786/2506 -f 2474/2787/2507 2465/2778/2496 2464/2777/2495 -f 2464/2777/2495 2473/2786/2506 2474/2787/2507 -f 2475/2788/2508 2466/2779/2497 2465/2778/2496 -f 2465/2778/2496 2474/2787/2507 2475/2788/2508 -f 2476/2789/2509 2467/2780/2498 2466/2779/2497 -f 2466/2779/2497 2475/2788/2508 2476/2789/2509 -f 2477/2790/2510 2468/2781/2499 2467/2780/2498 -f 2467/2780/2498 2476/2789/2509 2477/2790/2510 -f 2478/2791/2511 2469/2782/2500 2468/2781/2499 -f 2468/2781/2499 2477/2790/2510 2478/2791/2511 -f 2479/2792/2512 2470/2783/2501 2469/2782/2500 -f 2469/2782/2500 2478/2791/2511 2479/2792/2512 -f 2571/2895/2513 2561/2884/2502 2470/2783/2501 -f 2470/2783/2501 2479/2792/2512 2571/2895/2513 -f 2480/2793/2514 2471/2784/2503 2793/3151/2504 -f 2793/3151/2504 2481/2794/2515 2480/2793/2514 -f 2482/2796/2516 2472/2785/2505 2471/2784/2503 -f 2471/2784/2503 2480/2793/2514 2482/2796/2516 -f 2483/2797/2517 2473/2786/2506 2472/2785/2505 -f 2472/2785/2505 2482/2796/2516 2483/2797/2517 -f 2484/2798/2518 2474/2787/2507 2473/2786/2506 -f 2473/2786/2506 2483/2797/2517 2484/2798/2518 -f 2485/2799/2519 2475/2788/2508 2474/2787/2507 -f 2474/2787/2507 2484/2798/2518 2485/2799/2519 -f 2486/2800/2520 2476/2789/2509 2475/2788/2508 -f 2475/2788/2508 2485/2799/2519 2486/2800/2520 -f 2487/2801/2521 2477/2790/2510 2476/2789/2509 -f 2476/2789/2509 2486/2800/2520 2487/2801/2521 -f 2488/2802/2522 2478/2791/2511 2477/2790/2510 -f 2477/2790/2510 2487/2801/2521 2488/2802/2522 -f 2489/2803/2523 2479/2792/2512 2478/2791/2511 -f 2478/2791/2511 2488/2802/2522 2489/2803/2523 -f 2582/2908/2524 2571/2895/2513 2479/2792/2512 -f 2479/2792/2512 2489/2803/2523 2582/2908/2524 -f 2490/2804/2525 2480/2793/2514 2481/2794/2515 -f 2481/2794/2515 2491/2805/2526 2490/2804/2525 -f 2492/2807/2527 2482/2796/2516 2480/2793/2514 -f 2480/2793/2514 2490/2804/2525 2492/2807/2527 -f 2493/2808/2528 2483/2797/2517 2482/2796/2516 -f 2482/2796/2516 2492/2807/2527 2493/2808/2528 -f 2494/2809/2529 2484/2798/2518 2483/2797/2517 -f 2483/2797/2517 2493/2808/2528 2494/2809/2529 -f 2495/2810/2530 2485/2799/2519 2484/2798/2518 -f 2484/2798/2518 2494/2809/2529 2495/2810/2530 -f 2496/2811/2531 2486/2800/2520 2485/2799/2519 -f 2485/2799/2519 2495/2810/2530 2496/2811/2531 -f 2497/2812/2532 2487/2801/2521 2486/2800/2520 -f 2486/2800/2520 2496/2811/2531 2497/2812/2532 -f 2498/2813/2533 2488/2802/2522 2487/2801/2521 -f 2487/2801/2521 2497/2812/2532 2498/2813/2533 -f 2499/2814/2534 2489/2803/2523 2488/2802/2522 -f 2488/2802/2522 2498/2813/2533 2499/2814/2534 -f 2592/2919/2535 2582/2908/2524 2489/2803/2523 -f 2489/2803/2523 2499/2814/2534 2592/2919/2535 -f 2500/2815/2536 2490/2804/2525 2491/2805/2526 -f 2491/2805/2526 2821/3180/2537 2500/2815/2536 -f 2501/2816/2538 2492/2807/2527 2490/2804/2525 -f 2490/2804/2525 2500/2815/2536 2501/2816/2538 -f 2502/2817/2539 2493/2808/2528 2492/2807/2527 -f 2492/2807/2527 2501/2816/2538 2502/2817/2539 -f 2503/2818/2540 2494/2809/2529 2493/2808/2528 -f 2493/2808/2528 2502/2817/2539 2503/2818/2540 -f 2504/2819/2541 2495/2810/2530 2494/2809/2529 -f 2494/2809/2529 2503/2818/2540 2504/2819/2541 -f 2505/2820/2542 2496/2811/2531 2495/2810/2530 -f 2495/2810/2530 2504/2819/2541 2505/2820/2542 -f 2506/2821/2543 2497/2812/2532 2496/2811/2531 -f 2496/2811/2531 2505/2820/2542 2506/2821/2543 -f 2507/2822/2544 2498/2813/2533 2497/2812/2532 -f 2497/2812/2532 2506/2821/2543 2507/2822/2544 -f 2508/2823/2545 2499/2814/2534 2498/2813/2533 -f 2498/2813/2533 2507/2822/2544 2508/2823/2545 -f 2603/2932/2546 2592/2919/2535 2499/2814/2534 -f 2499/2814/2534 2508/2823/2545 2603/2932/2546 -f 2509/2824/2547 2500/2815/2536 2821/3180/2537 -f 2821/3180/2537 2832/3193/2548 2509/2824/2547 -f 2510/2825/2549 2501/2816/2538 2500/2815/2536 -f 2500/2815/2536 2509/2824/2547 2510/2825/2549 -f 2511/2826/2550 2502/2817/2539 2501/2816/2538 -f 2501/2816/2538 2510/2825/2549 2511/2826/2550 -f 2512/2827/2551 2503/2818/2540 2502/2817/2539 -f 2502/2817/2539 2511/2826/2550 2512/2827/2551 -f 2513/2828/2552 2504/2819/2541 2503/2818/2540 -f 2503/2818/2540 2512/2827/2551 2513/2828/2552 -f 2514/2829/2553 2505/2820/2542 2504/2819/2541 -f 2504/2819/2541 2513/2828/2552 2514/2829/2553 -f 2515/2830/2554 2506/2821/2543 2505/2820/2542 -f 2505/2820/2542 2514/2829/2553 2515/2830/2554 -f 2516/2831/2555 2507/2822/2544 2506/2821/2543 -f 2506/2821/2543 2515/2830/2554 2516/2831/2555 -f 2517/2832/2556 2508/2823/2545 2507/2822/2544 -f 2507/2822/2544 2516/2831/2555 2517/2832/2556 -f 2613/2943/2557 2603/2932/2546 2508/2823/2545 -f 2508/2823/2545 2517/2832/2556 2613/2943/2557 -f 2518/2833/2558 2509/2824/2547 2832/3193/2548 -f 2832/3193/2548 2843/3206/2559 2518/2833/2558 -f 2519/2834/2560 2510/2825/2549 2509/2824/2547 -f 2509/2824/2547 2518/2833/2558 2519/2834/2560 -f 2520/2835/2561 2511/2826/2550 2510/2825/2549 -f 2510/2825/2549 2519/2834/2560 2520/2835/2561 -f 2521/2836/2562 2512/2827/2551 2511/2826/2550 -f 2511/2826/2550 2520/2835/2561 2521/2836/2562 -f 2522/2837/2563 2513/2828/2552 2512/2827/2551 -f 2512/2827/2551 2521/2836/2562 2522/2837/2563 -f 2523/2838/2564 2514/2829/2553 2513/2828/2552 -f 2513/2828/2552 2522/2837/2563 2523/2838/2564 -f 2524/2839/2565 2515/2830/2554 2514/2829/2553 -f 2514/2829/2553 2523/2838/2564 2524/2839/2565 -f 2525/2840/2566 2516/2831/2555 2515/2830/2554 -f 2515/2830/2554 2524/2839/2565 2525/2840/2566 -f 2526/2841/2567 2517/2832/2556 2516/2831/2555 -f 2516/2831/2555 2525/2840/2566 2526/2841/2567 -f 2623/2954/2568 2613/2943/2557 2517/2832/2556 -f 2517/2832/2556 2526/2841/2567 2623/2954/2568 -f 2527/2842/2569 2518/2833/2558 2843/3206/2559 -f 2843/3206/2559 2854/3219/2570 2527/2842/2569 -f 2528/2843/2571 2519/2834/2560 2518/2833/2558 -f 2518/2833/2558 2527/2842/2569 2528/2843/2571 -f 2529/2844/2572 2520/2835/2561 2519/2834/2560 -f 2519/2834/2560 2528/2843/2571 2529/2844/2572 -f 2530/2845/2573 2521/2836/2562 2520/2835/2561 -f 2520/2835/2561 2529/2844/2572 2530/2845/2573 -f 2531/2846/2574 2522/2837/2563 2521/2836/2562 -f 2521/2836/2562 2530/2845/2573 2531/2846/2574 -f 2532/2847/2575 2523/2838/2564 2522/2837/2563 -f 2522/2837/2563 2531/2846/2574 2532/2847/2575 -f 2533/2848/2576 2524/2839/2565 2523/2838/2564 -f 2523/2838/2564 2532/2847/2575 2533/2848/2576 -f 2534/2849/2577 2525/2840/2566 2524/2839/2565 -f 2524/2839/2565 2533/2848/2576 2534/2849/2577 -f 2535/2850/2578 2526/2841/2567 2525/2840/2566 -f 2525/2840/2566 2534/2849/2577 2535/2850/2578 -f 2536/2851/2579 2623/2954/2568 2526/2841/2567 -f 2526/2841/2567 2535/2850/2578 2536/2851/2579 -f 2537/2853/2580 2527/2842/2569 2854/3219/2570 -f 2854/3219/2570 2864/3230/2581 2537/2853/2580 -f 2538/2854/2582 2528/2843/2571 2527/2842/2569 -f 2527/2842/2569 2537/2853/2580 2538/2854/2582 -f 2539/2855/2583 2529/2844/2572 2528/2843/2571 -f 2528/2843/2571 2538/2854/2582 2539/2855/2583 -f 2540/2856/2584 2530/2845/2573 2529/2844/2572 -f 2529/2844/2572 2539/2855/2583 2540/2856/2584 -f 2541/2857/2585 2531/2846/2574 2530/2845/2573 -f 2530/2845/2573 2540/2856/2584 2541/2857/2585 -f 2542/2858/2586 2532/2847/2575 2531/2846/2574 -f 2531/2846/2574 2541/2857/2585 2542/2858/2586 -f 2543/2859/2587 2533/2848/2576 2532/2847/2575 -f 2532/2847/2575 2542/2858/2586 2543/2859/2587 -f 2544/2860/2588 2534/2849/2577 2533/2848/2576 -f 2533/2848/2576 2543/2859/2587 2544/2860/2588 -f 2545/2861/2589 2535/2850/2578 2534/2849/2577 -f 2534/2849/2577 2544/2860/2588 2545/2861/2589 -f 2546/2862/2590 2536/2851/2579 2535/2850/2578 -f 2535/2850/2578 2545/2861/2589 2546/2862/2590 -f 2547/2864/2591 2254/2529/2281 2158/2418/2190 -f 2158/2418/2190 2460/2772/2491 2547/2864/2591 -f 2548/2865/2592 2255/2531/2282 2254/2529/2281 -f 2254/2529/2281 2547/2864/2591 2548/2865/2592 -f 2549/2866/2593 2256/2533/2283 2255/2531/2282 -f 2255/2531/2282 2548/2865/2592 2549/2866/2593 -f 2550/2867/2594 2551/2869/2284 2256/2533/2283 -f 2256/2533/2283 2549/2866/2593 2550/2867/2594 -f 2552/2870/2595 2553/2872/2285 2551/2869/2284 -f 2551/2869/2284 2550/2867/2594 2552/2870/2595 -f 2554/2873/2596 2257/2535/2286 2553/2872/2285 -f 2553/2872/2285 2552/2870/2595 2554/2873/2596 -f 2555/2874/2597 2258/2537/2287 2257/2535/2286 -f 2257/2535/2286 2554/2873/2596 2555/2874/2597 -f 2556/2875/2598 2259/2539/2288 2258/2537/2287 -f 2258/2537/2287 2555/2874/2597 2556/2875/2598 -f 2557/2876/2599 2260/2541/2289 2259/2539/2288 -f 2259/2539/2288 2556/2875/2598 2557/2876/2599 -f 2558/2877/2600 2559/2881/2290 2260/2541/2289 -f 2260/2541/2289 2557/2876/2599 2558/2877/2600 -f 2560/2883/2601 2547/2864/2591 2460/2772/2491 -f 2460/2772/2491 2561/2885/2502 2560/2883/2601 -f 2562/2886/2602 2548/2865/2592 2547/2864/2591 -f 2547/2864/2591 2560/2883/2601 2562/2886/2602 -f 2563/2887/2603 2549/2866/2593 2548/2865/2592 -f 2548/2865/2592 2562/2886/2602 2563/2887/2603 -f 2564/2888/2604 2550/2867/2594 2549/2866/2593 -f 2549/2866/2593 2563/2887/2603 2564/2888/2604 -f 2565/2889/2605 2552/2870/2595 2550/2867/2594 -f 2550/2867/2594 2564/2888/2604 2565/2889/2605 -f 2566/2890/2606 2554/2873/2596 2552/2870/2595 -f 2552/2870/2595 2565/2889/2605 2566/2890/2606 -f 2567/2891/2607 2555/2874/2597 2554/2873/2596 -f 2554/2873/2596 2566/2890/2606 2567/2891/2607 -f 2568/2892/2608 2556/2875/2598 2555/2874/2597 -f 2555/2874/2597 2567/2891/2607 2568/2892/2608 -f 2569/2893/2609 2557/2876/2599 2556/2875/2598 -f 2556/2875/2598 2568/2892/2608 2569/2893/2609 -f 2668/3008/2610 2558/2877/2600 2557/2876/2599 -f 2557/2876/2599 2569/2893/2609 2668/3008/2610 -f 2570/2894/2611 2560/2883/2601 2561/2885/2502 -f 2561/2885/2502 2571/2896/2513 2570/2894/2611 -f 2572/2897/2612 2562/2886/2602 2560/2883/2601 -f 2560/2883/2601 2570/2894/2611 2572/2897/2612 -f 2573/2898/2613 2563/2887/2603 2562/2886/2602 -f 2562/2886/2602 2572/2897/2612 2573/2898/2613 -f 2574/2899/2614 2564/2888/2604 2563/2887/2603 -f 2563/2887/2603 2573/2898/2613 2574/2899/2614 -f 2575/2900/2615 2565/2889/2605 2564/2888/2604 -f 2564/2888/2604 2574/2899/2614 2575/2900/2615 -f 2576/2901/2616 2566/2890/2606 2565/2889/2605 -f 2565/2889/2605 2575/2900/2615 2576/2901/2616 -f 2577/2902/2617 2567/2891/2607 2566/2890/2606 -f 2566/2890/2606 2576/2901/2616 2577/2902/2617 -f 2578/2903/2618 2568/2892/2608 2567/2891/2607 -f 2567/2891/2607 2577/2902/2617 2578/2903/2618 -f 2579/2904/2619 2569/2893/2609 2568/2892/2608 -f 2568/2892/2608 2578/2903/2618 2579/2904/2619 -f 2580/2905/2620 2668/3008/2610 2569/2893/2609 -f 2569/2893/2609 2579/2904/2619 2580/2905/2620 -f 2581/2907/2621 2570/2894/2611 2571/2896/2513 -f 2571/2896/2513 2582/2909/2524 2581/2907/2621 -f 2583/2910/2622 2572/2897/2612 2570/2894/2611 -f 2570/2894/2611 2581/2907/2621 2583/2910/2622 -f 2584/2911/2623 2573/2898/2613 2572/2897/2612 -f 2572/2897/2612 2583/2910/2622 2584/2911/2623 -f 2585/2912/2624 2574/2899/2614 2573/2898/2613 -f 2573/2898/2613 2584/2911/2623 2585/2912/2624 -f 2586/2913/2625 2575/2900/2615 2574/2899/2614 -f 2574/2899/2614 2585/2912/2624 2586/2913/2625 -f 2587/2914/2626 2576/2901/2616 2575/2900/2615 -f 2575/2900/2615 2586/2913/2625 2587/2914/2626 -f 2588/2915/2627 2577/2902/2617 2576/2901/2616 -f 2576/2901/2616 2587/2914/2626 2588/2915/2627 -f 2589/2916/2628 2578/2903/2618 2577/2902/2617 -f 2577/2902/2617 2588/2915/2627 2589/2916/2628 -f 2590/2917/2629 2579/2904/2619 2578/2903/2618 -f 2578/2903/2618 2589/2916/2628 2590/2917/2629 -f 2689/3032/2630 2580/2905/2620 2579/2904/2619 -f 2579/2904/2619 2590/2917/2629 2689/3032/2630 -f 2591/2918/2631 2581/2907/2621 2582/2909/2524 -f 2582/2909/2524 2592/2920/2535 2591/2918/2631 -f 2593/2921/2632 2583/2910/2622 2581/2907/2621 -f 2581/2907/2621 2591/2918/2631 2593/2921/2632 -f 2594/2922/2633 2584/2911/2623 2583/2910/2622 -f 2583/2910/2622 2593/2921/2632 2594/2922/2633 -f 2595/2923/2634 2585/2912/2624 2584/2911/2623 -f 2584/2911/2623 2594/2922/2633 2595/2923/2634 -f 2596/2924/2635 2586/2913/2625 2585/2912/2624 -f 2585/2912/2624 2595/2923/2634 2596/2924/2635 -f 2597/2925/2636 2587/2914/2626 2586/2913/2625 -f 2586/2913/2625 2596/2924/2635 2597/2925/2636 -f 2598/2926/2637 2588/2915/2627 2587/2914/2626 -f 2587/2914/2626 2597/2925/2636 2598/2926/2637 -f 2599/2927/2638 2589/2916/2628 2588/2915/2627 -f 2588/2915/2627 2598/2926/2637 2599/2927/2638 -f 2600/2928/2639 2590/2917/2629 2589/2916/2628 -f 2589/2916/2628 2599/2927/2638 2600/2928/2639 -f 2601/2929/2640 2689/3032/2630 2590/2917/2629 -f 2590/2917/2629 2600/2928/2639 2601/2929/2640 -f 2602/2931/2641 2591/2918/2631 2592/2920/2535 -f 2592/2920/2535 2603/2933/2546 2602/2931/2641 -f 2604/2934/2642 2593/2921/2632 2591/2918/2631 -f 2591/2918/2631 2602/2931/2641 2604/2934/2642 -f 2605/2935/2643 2594/2922/2633 2593/2921/2632 -f 2593/2921/2632 2604/2934/2642 2605/2935/2643 -f 2606/2936/2644 2595/2923/2634 2594/2922/2633 -f 2594/2922/2633 2605/2935/2643 2606/2936/2644 -f 2607/2937/2645 2596/2924/2635 2595/2923/2634 -f 2595/2923/2634 2606/2936/2644 2607/2937/2645 -f 2608/2938/2646 2597/2925/2636 2596/2924/2635 -f 2596/2924/2635 2607/2937/2645 2608/2938/2646 -f 2609/2939/2647 2598/2926/2637 2597/2925/2636 -f 2597/2925/2636 2608/2938/2646 2609/2939/2647 -f 2610/2940/2648 2599/2927/2638 2598/2926/2637 -f 2598/2926/2637 2609/2939/2647 2610/2940/2648 -f 2611/2941/2649 2600/2928/2639 2599/2927/2638 -f 2599/2927/2638 2610/2940/2648 2611/2941/2649 -f 2710/3056/2650 2601/2929/2640 2600/2928/2639 -f 2600/2928/2639 2611/2941/2649 2710/3056/2650 -f 2612/2942/2651 2602/2931/2641 2603/2933/2546 -f 2603/2933/2546 2613/2944/2557 2612/2942/2651 -f 2614/2945/2652 2604/2934/2642 2602/2931/2641 -f 2602/2931/2641 2612/2942/2651 2614/2945/2652 -f 2615/2946/2653 2605/2935/2643 2604/2934/2642 -f 2604/2934/2642 2614/2945/2652 2615/2946/2653 -f 2616/2947/2654 2606/2936/2644 2605/2935/2643 -f 2605/2935/2643 2615/2946/2653 2616/2947/2654 -f 2617/2948/2655 2607/2937/2645 2606/2936/2644 -f 2606/2936/2644 2616/2947/2654 2617/2948/2655 -f 2618/2949/2656 2608/2938/2646 2607/2937/2645 -f 2607/2937/2645 2617/2948/2655 2618/2949/2656 -f 2619/2950/2657 2609/2939/2647 2608/2938/2646 -f 2608/2938/2646 2618/2949/2656 2619/2950/2657 -f 2620/2951/2658 2610/2940/2648 2609/2939/2647 -f 2609/2939/2647 2619/2950/2657 2620/2951/2658 -f 2621/2952/2659 2611/2941/2649 2610/2940/2648 -f 2610/2940/2648 2620/2951/2658 2621/2952/2659 -f 2721/3069/2660 2710/3056/2650 2611/2941/2649 -f 2611/2941/2649 2621/2952/2659 2721/3069/2660 -f 2622/2953/2661 2612/2942/2651 2613/2944/2557 -f 2613/2944/2557 2623/2955/2568 2622/2953/2661 -f 2624/2956/2662 2614/2945/2652 2612/2942/2651 -f 2612/2942/2651 2622/2953/2661 2624/2956/2662 -f 2625/2957/2663 2615/2946/2653 2614/2945/2652 -f 2614/2945/2652 2624/2956/2662 2625/2957/2663 -f 2626/2958/2664 2616/2947/2654 2615/2946/2653 -f 2615/2946/2653 2625/2957/2663 2626/2958/2664 -f 2627/2959/2665 2617/2948/2655 2616/2947/2654 -f 2616/2947/2654 2626/2958/2664 2627/2959/2665 -f 2628/2960/2666 2618/2949/2656 2617/2948/2655 -f 2617/2948/2655 2627/2959/2665 2628/2960/2666 -f 2629/2961/2667 2619/2950/2657 2618/2949/2656 -f 2618/2949/2656 2628/2960/2666 2629/2961/2667 -f 2630/2962/2668 2620/2951/2658 2619/2950/2657 -f 2619/2950/2657 2629/2961/2667 2630/2962/2668 -f 2631/2963/2669 2621/2952/2659 2620/2951/2658 -f 2620/2951/2658 2630/2962/2668 2631/2963/2669 -f 2731/3080/2670 2721/3069/2660 2621/2952/2659 -f 2621/2952/2659 2631/2963/2669 2731/3080/2670 -f 2632/2964/2671 2622/2953/2661 2623/2955/2568 -f 2623/2955/2568 2536/2852/2579 2632/2964/2671 -f 2633/2965/2672 2624/2956/2662 2622/2953/2661 -f 2622/2953/2661 2632/2964/2671 2633/2965/2672 -f 2634/2966/2673 2625/2957/2663 2624/2956/2662 -f 2624/2956/2662 2633/2965/2672 2634/2966/2673 -f 2635/2967/2674 2626/2958/2664 2625/2957/2663 -f 2625/2957/2663 2634/2966/2673 2635/2967/2674 -f 2636/2968/2675 2627/2959/2665 2626/2958/2664 -f 2626/2958/2664 2635/2967/2674 2636/2968/2675 -f 2637/2969/2676 2628/2960/2666 2627/2959/2665 -f 2627/2959/2665 2636/2968/2675 2637/2969/2676 -f 2638/2970/2677 2629/2961/2667 2628/2960/2666 -f 2628/2960/2666 2637/2969/2676 2638/2970/2677 -f 2639/2971/2678 2630/2962/2668 2629/2961/2667 -f 2629/2961/2667 2638/2970/2677 2639/2971/2678 -f 2640/2972/2679 2631/2963/2669 2630/2962/2668 -f 2630/2962/2668 2639/2971/2678 2640/2972/2679 -f 2641/2973/2680 2731/3080/2670 2631/2963/2669 -f 2631/2963/2669 2640/2972/2679 2641/2973/2680 -f 2642/2975/2681 2632/2964/2671 2536/2852/2579 -f 2536/2852/2579 2546/2863/2590 2642/2975/2681 -f 2643/2976/2682 2633/2965/2672 2632/2964/2671 -f 2632/2964/2671 2642/2975/2681 2643/2976/2682 -f 2644/2977/2683 2634/2966/2673 2633/2965/2672 -f 2633/2965/2672 2643/2976/2682 2644/2977/2683 -f 2645/2978/2684 2635/2967/2674 2634/2966/2673 -f 2634/2966/2673 2644/2977/2683 2645/2978/2684 -f 2646/2979/2685 2636/2968/2675 2635/2967/2674 -f 2635/2967/2674 2645/2978/2684 2646/2979/2685 -f 2647/2980/2686 2637/2969/2676 2636/2968/2675 -f 2636/2968/2675 2646/2979/2685 2647/2980/2686 -f 2648/2981/2687 2638/2970/2677 2637/2969/2676 -f 2637/2969/2676 2647/2980/2686 2648/2981/2687 -f 2649/2982/2688 2639/2971/2678 2638/2970/2677 -f 2638/2970/2677 2648/2981/2687 2649/2982/2688 -f 2650/2983/2689 2640/2972/2679 2639/2971/2678 -f 2639/2971/2678 2649/2982/2688 2650/2983/2689 -f 2750/3100/2690 2641/2973/2680 2640/2972/2679 -f 2640/2972/2679 2650/2983/2689 2750/3100/2690 -f 2651/2984/2691 2652/2986/2381 2559/2882/2290 -f 2559/2882/2290 2558/2878/2600 2651/2984/2691 -f 2653/2987/2692 2350/2640/2382 2652/2986/2381 -f 2652/2986/2381 2651/2984/2691 2653/2987/2692 -f 2654/2988/2693 2655/2990/2383 2350/2640/2382 -f 2350/2640/2382 2653/2987/2692 2654/2988/2693 -f 2656/2991/2694 2351/2642/2384 2655/2990/2383 -f 2655/2990/2383 2654/2988/2693 2656/2991/2694 -f 2657/2992/2695 2658/2994/2385 2351/2642/2384 -f 2351/2642/2384 2656/2991/2694 2657/2992/2695 -f 2659/2995/2696 2660/2997/2386 2658/2994/2385 -f 2658/2994/2385 2657/2992/2695 2659/2995/2696 -f 2661/2998/2697 2352/2644/2387 2660/2997/2386 -f 2660/2997/2386 2659/2995/2696 2661/2998/2697 -f 2662/2999/2698 2663/3001/2388 2352/2644/2387 -f 2352/2644/2387 2661/2998/2697 2662/2999/2698 -f 2664/3002/2699 2665/3004/2389 2663/3001/2388 -f 2663/3001/2388 2662/2999/2698 2664/3002/2699 -f 2666/3005/2700 2353/2647/2390 2665/3004/2389 -f 2665/3004/2389 2664/3002/2699 2666/3005/2700 -f 2667/3007/2701 2651/2984/2691 2558/2878/2600 -f 2558/2878/2600 2668/3009/2610 2667/3007/2701 -f 2669/3010/2702 2653/2987/2692 2651/2984/2691 -f 2651/2984/2691 2667/3007/2701 2669/3010/2702 -f 2670/3011/2703 2654/2988/2693 2653/2987/2692 -f 2653/2987/2692 2669/3010/2702 2670/3011/2703 -f 2671/3012/2704 2656/2991/2694 2654/2988/2693 -f 2654/2988/2693 2670/3011/2703 2671/3012/2704 -f 2672/3013/2705 2657/2992/2695 2656/2991/2694 -f 2656/2991/2694 2671/3012/2704 2672/3013/2705 -f 2673/3014/2706 2659/2995/2696 2657/2992/2695 -f 2657/2992/2695 2672/3013/2705 2673/3014/2706 -f 2674/3015/2707 2661/2998/2697 2659/2995/2696 -f 2659/2995/2696 2673/3014/2706 2674/3015/2707 -f 2675/3016/2708 2662/2999/2698 2661/2998/2697 -f 2661/2998/2697 2674/3015/2707 2675/3016/2708 -f 2676/3017/2709 2664/3002/2699 2662/2999/2698 -f 2662/2999/2698 2675/3016/2708 2676/3017/2709 -f 2677/3018/2710 2666/3005/2700 2664/3002/2699 -f 2664/3002/2699 2676/3017/2709 2677/3018/2710 -f 2678/3020/2711 2667/3007/2701 2668/3009/2610 -f 2668/3009/2610 2580/2906/2620 2678/3020/2711 -f 2679/3021/2712 2669/3010/2702 2667/3007/2701 -f 2667/3007/2701 2678/3020/2711 2679/3021/2712 -f 2680/3022/2713 2670/3011/2703 2669/3010/2702 -f 2669/3010/2702 2679/3021/2712 2680/3022/2713 -f 2681/3023/2714 2671/3012/2704 2670/3011/2703 -f 2670/3011/2703 2680/3022/2713 2681/3023/2714 -f 2682/3024/2715 2672/3013/2705 2671/3012/2704 -f 2671/3012/2704 2681/3023/2714 2682/3024/2715 -f 2683/3025/2716 2673/3014/2706 2672/3013/2705 -f 2672/3013/2705 2682/3024/2715 2683/3025/2716 -f 2684/3026/2717 2674/3015/2707 2673/3014/2706 -f 2673/3014/2706 2683/3025/2716 2684/3026/2717 -f 2685/3027/2718 2675/3016/2708 2674/3015/2707 -f 2674/3015/2707 2684/3026/2717 2685/3027/2718 -f 2686/3028/2719 2676/3017/2709 2675/3016/2708 -f 2675/3016/2708 2685/3027/2718 2686/3028/2719 -f 2687/3029/2720 2677/3018/2710 2676/3017/2709 -f 2676/3017/2709 2686/3028/2719 2687/3029/2720 -f 2688/3031/2721 2678/3020/2711 2580/2906/2620 -f 2580/2906/2620 2689/3033/2630 2688/3031/2721 -f 2690/3034/2722 2679/3021/2712 2678/3020/2711 -f 2678/3020/2711 2688/3031/2721 2690/3034/2722 -f 2691/3035/2723 2680/3022/2713 2679/3021/2712 -f 2679/3021/2712 2690/3034/2722 2691/3035/2723 -f 2692/3036/2724 2681/3023/2714 2680/3022/2713 -f 2680/3022/2713 2691/3035/2723 2692/3036/2724 -f 2693/3037/2725 2682/3024/2715 2681/3023/2714 -f 2681/3023/2714 2692/3036/2724 2693/3037/2725 -f 2694/3038/2726 2683/3025/2716 2682/3024/2715 -f 2682/3024/2715 2693/3037/2725 2694/3038/2726 -f 2695/3039/2727 2684/3026/2717 2683/3025/2716 -f 2683/3025/2716 2694/3038/2726 2695/3039/2727 -f 2696/3040/2728 2685/3027/2718 2684/3026/2717 -f 2684/3026/2717 2695/3039/2727 2696/3040/2728 -f 2697/3041/2729 2686/3028/2719 2685/3027/2718 -f 2685/3027/2718 2696/3040/2728 2697/3041/2729 -f 2698/3042/2730 2687/3029/2720 2686/3028/2719 -f 2686/3028/2719 2697/3041/2729 2698/3042/2730 -f 2699/3044/2731 2688/3031/2721 2689/3033/2630 -f 2689/3033/2630 2601/2930/2640 2699/3044/2731 -f 2700/3045/2732 2690/3034/2722 2688/3031/2721 -f 2688/3031/2721 2699/3044/2731 2700/3045/2732 -f 2701/3046/2733 2691/3035/2723 2690/3034/2722 -f 2690/3034/2722 2700/3045/2732 2701/3046/2733 -f 2702/3047/2734 2692/3036/2724 2691/3035/2723 -f 2691/3035/2723 2701/3046/2733 2702/3047/2734 -f 2703/3048/2735 2693/3037/2725 2692/3036/2724 -f 2692/3036/2724 2702/3047/2734 2703/3048/2735 -f 2704/3049/2736 2694/3038/2726 2693/3037/2725 -f 2693/3037/2725 2703/3048/2735 2704/3049/2736 -f 2705/3050/2737 2695/3039/2727 2694/3038/2726 -f 2694/3038/2726 2704/3049/2736 2705/3050/2737 -f 2706/3051/2738 2696/3040/2728 2695/3039/2727 -f 2695/3039/2727 2705/3050/2737 2706/3051/2738 -f 2707/3052/2739 2697/3041/2729 2696/3040/2728 -f 2696/3040/2728 2706/3051/2738 2707/3052/2739 -f 2708/3053/2740 2698/3042/2730 2697/3041/2729 -f 2697/3041/2729 2707/3052/2739 2708/3053/2740 -f 2709/3055/2741 2699/3044/2731 2601/2930/2640 -f 2601/2930/2640 2710/3057/2650 2709/3055/2741 -f 2711/3058/2742 2700/3045/2732 2699/3044/2731 -f 2699/3044/2731 2709/3055/2741 2711/3058/2742 -f 2712/3059/2743 2701/3046/2733 2700/3045/2732 -f 2700/3045/2732 2711/3058/2742 2712/3059/2743 -f 2713/3060/2744 2702/3047/2734 2701/3046/2733 -f 2701/3046/2733 2712/3059/2743 2713/3060/2744 -f 2714/3061/2745 2703/3048/2735 2702/3047/2734 -f 2702/3047/2734 2713/3060/2744 2714/3061/2745 -f 2715/3062/2746 2704/3049/2736 2703/3048/2735 -f 2703/3048/2735 2714/3061/2745 2715/3062/2746 -f 2716/3063/2747 2705/3050/2737 2704/3049/2736 -f 2704/3049/2736 2715/3062/2746 2716/3063/2747 -f 2717/3064/2748 2706/3051/2738 2705/3050/2737 -f 2705/3050/2737 2716/3063/2747 2717/3064/2748 -f 2718/3065/2749 2707/3052/2739 2706/3051/2738 -f 2706/3051/2738 2717/3064/2748 2718/3065/2749 -f 2719/3066/2750 2708/3053/2740 2707/3052/2739 -f 2707/3052/2739 2718/3065/2749 2719/3066/2750 -f 2720/3068/2751 2709/3055/2741 2710/3057/2650 -f 2710/3057/2650 2721/3070/2660 2720/3068/2751 -f 2722/3071/2752 2711/3058/2742 2709/3055/2741 -f 2709/3055/2741 2720/3068/2751 2722/3071/2752 -f 2723/3072/2753 2712/3059/2743 2711/3058/2742 -f 2711/3058/2742 2722/3071/2752 2723/3072/2753 -f 2724/3073/2754 2713/3060/2744 2712/3059/2743 -f 2712/3059/2743 2723/3072/2753 2724/3073/2754 -f 2725/3074/2755 2714/3061/2745 2713/3060/2744 -f 2713/3060/2744 2724/3073/2754 2725/3074/2755 -f 2726/3075/2756 2715/3062/2746 2714/3061/2745 -f 2714/3061/2745 2725/3074/2755 2726/3075/2756 -f 2727/3076/2757 2716/3063/2747 2715/3062/2746 -f 2715/3062/2746 2726/3075/2756 2727/3076/2757 -f 2728/3077/2758 2717/3064/2748 2716/3063/2747 -f 2716/3063/2747 2727/3076/2757 2728/3077/2758 -f 2729/3078/2759 2718/3065/2749 2717/3064/2748 -f 2717/3064/2748 2728/3077/2758 2729/3078/2759 -f 2823/3183/2760 2719/3066/2750 2718/3065/2749 -f 2718/3065/2749 2729/3078/2759 2823/3183/2760 -f 2730/3079/2761 2720/3068/2751 2721/3070/2660 -f 2721/3070/2660 2731/3081/2670 2730/3079/2761 -f 2732/3082/2762 2722/3071/2752 2720/3068/2751 -f 2720/3068/2751 2730/3079/2761 2732/3082/2762 -f 2733/3083/2763 2723/3072/2753 2722/3071/2752 -f 2722/3071/2752 2732/3082/2762 2733/3083/2763 -f 2734/3084/2764 2724/3073/2754 2723/3072/2753 -f 2723/3072/2753 2733/3083/2763 2734/3084/2764 -f 2735/3085/2765 2725/3074/2755 2724/3073/2754 -f 2724/3073/2754 2734/3084/2764 2735/3085/2765 -f 2736/3086/2766 2726/3075/2756 2725/3074/2755 -f 2725/3074/2755 2735/3085/2765 2736/3086/2766 -f 2737/3087/2767 2727/3076/2757 2726/3075/2756 -f 2726/3075/2756 2736/3086/2766 2737/3087/2767 -f 2738/3088/2768 2728/3077/2758 2727/3076/2757 -f 2727/3076/2757 2737/3087/2767 2738/3088/2768 -f 2739/3089/2769 2729/3078/2759 2728/3077/2758 -f 2728/3077/2758 2738/3088/2768 2739/3089/2769 -f 2834/3196/2770 2823/3183/2760 2729/3078/2759 -f 2729/3078/2759 2739/3089/2769 2834/3196/2770 -f 2740/3090/2771 2730/3079/2761 2731/3081/2670 -f 2731/3081/2670 2641/2974/2680 2740/3090/2771 -f 2741/3091/2772 2732/3082/2762 2730/3079/2761 -f 2730/3079/2761 2740/3090/2771 2741/3091/2772 -f 2742/3092/2773 2733/3083/2763 2732/3082/2762 -f 2732/3082/2762 2741/3091/2772 2742/3092/2773 -f 2743/3093/2774 2734/3084/2764 2733/3083/2763 -f 2733/3083/2763 2742/3092/2773 2743/3093/2774 -f 2744/3094/2775 2735/3085/2765 2734/3084/2764 -f 2734/3084/2764 2743/3093/2774 2744/3094/2775 -f 2745/3095/2776 2736/3086/2766 2735/3085/2765 -f 2735/3085/2765 2744/3094/2775 2745/3095/2776 -f 2746/3096/2777 2737/3087/2767 2736/3086/2766 -f 2736/3086/2766 2745/3095/2776 2746/3096/2777 -f 2747/3097/2778 2738/3088/2768 2737/3087/2767 -f 2737/3087/2767 2746/3096/2777 2747/3097/2778 -f 2748/3098/2779 2739/3089/2769 2738/3088/2768 -f 2738/3088/2768 2747/3097/2778 2748/3098/2779 -f 2845/3209/2780 2834/3196/2770 2739/3089/2769 -f 2739/3089/2769 2748/3098/2779 2845/3209/2780 -f 2749/3099/2781 2740/3090/2771 2641/2974/2680 -f 2641/2974/2680 2750/3101/2690 2749/3099/2781 -f 2751/3102/2782 2741/3091/2772 2740/3090/2771 -f 2740/3090/2771 2749/3099/2781 2751/3102/2782 -f 2752/3103/2783 2742/3092/2773 2741/3091/2772 -f 2741/3091/2772 2751/3102/2782 2752/3103/2783 -f 2753/3104/2784 2743/3093/2774 2742/3092/2773 -f 2742/3092/2773 2752/3103/2783 2753/3104/2784 -f 2754/3105/2785 2744/3094/2775 2743/3093/2774 -f 2743/3093/2774 2753/3104/2784 2754/3105/2785 -f 2755/3106/2786 2745/3095/2776 2744/3094/2775 -f 2744/3094/2775 2754/3105/2785 2755/3106/2786 -f 2756/3107/2787 2746/3096/2777 2745/3095/2776 -f 2745/3095/2776 2755/3106/2786 2756/3107/2787 -f 2757/3108/2788 2747/3097/2778 2746/3096/2777 -f 2746/3096/2777 2756/3107/2787 2757/3108/2788 -f 2758/3109/2789 2748/3098/2779 2747/3097/2778 -f 2747/3097/2778 2757/3108/2788 2758/3109/2789 -f 2759/3110/2790 2845/3209/2780 2748/3098/2779 -f 2748/3098/2779 2758/3109/2789 2759/3110/2790 -f 2760/3112/2791 2443/2747/2472 2353/2648/2390 -f 2353/2648/2390 2666/3006/2700 2760/3112/2791 -f 2761/3113/2792 2762/3115/2473 2443/2747/2472 -f 2443/2747/2472 2760/3112/2791 2761/3113/2792 -f 2763/3116/2793 2764/3118/2474 2762/3115/2473 -f 2762/3115/2473 2761/3113/2792 2763/3116/2793 -f 2765/3119/2794 2444/2749/2475 2764/3118/2474 -f 2764/3118/2474 2763/3116/2793 2765/3119/2794 -f 2766/3120/2795 2445/2751/2476 2444/2749/2475 -f 2444/2749/2475 2765/3119/2794 2766/3120/2795 -f 2767/3121/2796 2768/3123/2477 2445/2751/2476 -f 2445/2751/2476 2766/3120/2795 2767/3121/2796 -f 2769/3124/2797 2770/3126/2478 2768/3123/2477 -f 2768/3123/2477 2767/3121/2796 2769/3124/2797 -f 2771/3127/2798 2772/3129/2479 2770/3126/2478 -f 2770/3126/2478 2769/3124/2797 2771/3127/2798 -f 2773/3130/2799 2774/3132/2480 2772/3129/2479 -f 2772/3129/2479 2771/3127/2798 2773/3130/2799 -f 2447/2754/2482 2153/2406/2181 2774/3132/2480 -f 2774/3132/2480 2773/3130/2799 2447/2754/2482 -f 2775/3133/2800 2760/3112/2791 2666/3006/2700 -f 2666/3006/2700 2677/3019/2710 2775/3133/2800 -f 2776/3134/2801 2761/3113/2792 2760/3112/2791 -f 2760/3112/2791 2775/3133/2800 2776/3134/2801 -f 2777/3135/2802 2763/3116/2793 2761/3113/2792 -f 2761/3113/2792 2776/3134/2801 2777/3135/2802 -f 2778/3136/2803 2765/3119/2794 2763/3116/2793 -f 2763/3116/2793 2777/3135/2802 2778/3136/2803 -f 2779/3137/2804 2766/3120/2795 2765/3119/2794 -f 2765/3119/2794 2778/3136/2803 2779/3137/2804 -f 2780/3138/2805 2767/3121/2796 2766/3120/2795 -f 2766/3120/2795 2779/3137/2804 2780/3138/2805 -f 2781/3139/2806 2769/3124/2797 2767/3121/2796 -f 2767/3121/2796 2780/3138/2805 2781/3139/2806 -f 2782/3140/2807 2771/3127/2798 2769/3124/2797 -f 2769/3124/2797 2781/3139/2806 2782/3140/2807 -f 2783/3141/2808 2773/3130/2799 2771/3127/2798 -f 2771/3127/2798 2782/3140/2807 2783/3141/2808 -f 2462/2775/2493 2447/2754/2482 2773/3130/2799 -f 2773/3130/2799 2783/3141/2808 2462/2775/2493 -f 2784/3142/2809 2775/3133/2800 2677/3019/2710 -f 2677/3019/2710 2687/3030/2720 2784/3142/2809 -f 2785/3143/2810 2776/3134/2801 2775/3133/2800 -f 2775/3133/2800 2784/3142/2809 2785/3143/2810 -f 2786/3144/2811 2777/3135/2802 2776/3134/2801 -f 2776/3134/2801 2785/3143/2810 2786/3144/2811 -f 2787/3145/2812 2778/3136/2803 2777/3135/2802 -f 2777/3135/2802 2786/3144/2811 2787/3145/2812 -f 2788/3146/2813 2779/3137/2804 2778/3136/2803 -f 2778/3136/2803 2787/3145/2812 2788/3146/2813 -f 2789/3147/2814 2780/3138/2805 2779/3137/2804 -f 2779/3137/2804 2788/3146/2813 2789/3147/2814 -f 2790/3148/2815 2781/3139/2806 2780/3138/2805 -f 2780/3138/2805 2789/3147/2814 2790/3148/2815 -f 2791/3149/2816 2782/3140/2807 2781/3139/2806 -f 2781/3139/2806 2790/3148/2815 2791/3149/2816 -f 2792/3150/2817 2783/3141/2808 2782/3140/2807 -f 2782/3140/2807 2791/3149/2816 2792/3150/2817 -f 2793/3152/2504 2462/2775/2493 2783/3141/2808 -f 2783/3141/2808 2792/3150/2817 2793/3152/2504 -f 2794/3153/2818 2784/3142/2809 2687/3030/2720 -f 2687/3030/2720 2698/3043/2730 2794/3153/2818 -f 2795/3154/2819 2785/3143/2810 2784/3142/2809 -f 2784/3142/2809 2794/3153/2818 2795/3154/2819 -f 2796/3155/2820 2786/3144/2811 2785/3143/2810 -f 2785/3143/2810 2795/3154/2819 2796/3155/2820 -f 2797/3156/2821 2787/3145/2812 2786/3144/2811 -f 2786/3144/2811 2796/3155/2820 2797/3156/2821 -f 2798/3157/2822 2788/3146/2813 2787/3145/2812 -f 2787/3145/2812 2797/3156/2821 2798/3157/2822 -f 2799/3158/2823 2789/3147/2814 2788/3146/2813 -f 2788/3146/2813 2798/3157/2822 2799/3158/2823 -f 2800/3159/2824 2790/3148/2815 2789/3147/2814 -f 2789/3147/2814 2799/3158/2823 2800/3159/2824 -f 2801/3160/2825 2791/3149/2816 2790/3148/2815 -f 2790/3148/2815 2800/3159/2824 2801/3160/2825 -f 2802/3161/2826 2792/3150/2817 2791/3149/2816 -f 2791/3149/2816 2801/3160/2825 2802/3161/2826 -f 2481/2795/2515 2793/3152/2504 2792/3150/2817 -f 2792/3150/2817 2802/3161/2826 2481/2795/2515 -f 2803/3162/2827 2794/3153/2818 2698/3043/2730 -f 2698/3043/2730 2708/3054/2740 2803/3162/2827 -f 2804/3163/2828 2795/3154/2819 2794/3153/2818 -f 2794/3153/2818 2803/3162/2827 2804/3163/2828 -f 2805/3164/2829 2796/3155/2820 2795/3154/2819 -f 2795/3154/2819 2804/3163/2828 2805/3164/2829 -f 2806/3165/2830 2797/3156/2821 2796/3155/2820 -f 2796/3155/2820 2805/3164/2829 2806/3165/2830 -f 2807/3166/2831 2798/3157/2822 2797/3156/2821 -f 2797/3156/2821 2806/3165/2830 2807/3166/2831 -f 2808/3167/2832 2799/3158/2823 2798/3157/2822 -f 2798/3157/2822 2807/3166/2831 2808/3167/2832 -f 2809/3168/2833 2800/3159/2824 2799/3158/2823 -f 2799/3158/2823 2808/3167/2832 2809/3168/2833 -f 2810/3169/2834 2801/3160/2825 2800/3159/2824 -f 2800/3159/2824 2809/3168/2833 2810/3169/2834 -f 2811/3170/2835 2802/3161/2826 2801/3160/2825 -f 2801/3160/2825 2810/3169/2834 2811/3170/2835 -f 2491/2806/2526 2481/2795/2515 2802/3161/2826 -f 2802/3161/2826 2811/3170/2835 2491/2806/2526 -f 2812/3171/2836 2803/3162/2827 2708/3054/2740 -f 2708/3054/2740 2719/3067/2750 2812/3171/2836 -f 2813/3172/2837 2804/3163/2828 2803/3162/2827 -f 2803/3162/2827 2812/3171/2836 2813/3172/2837 -f 2814/3173/2838 2805/3164/2829 2804/3163/2828 -f 2804/3163/2828 2813/3172/2837 2814/3173/2838 -f 2815/3174/2839 2806/3165/2830 2805/3164/2829 -f 2805/3164/2829 2814/3173/2838 2815/3174/2839 -f 2816/3175/2840 2807/3166/2831 2806/3165/2830 -f 2806/3165/2830 2815/3174/2839 2816/3175/2840 -f 2817/3176/2841 2808/3167/2832 2807/3166/2831 -f 2807/3166/2831 2816/3175/2840 2817/3176/2841 -f 2818/3177/2842 2809/3168/2833 2808/3167/2832 -f 2808/3167/2832 2817/3176/2841 2818/3177/2842 -f 2819/3178/2843 2810/3169/2834 2809/3168/2833 -f 2809/3168/2833 2818/3177/2842 2819/3178/2843 -f 2820/3179/2844 2811/3170/2835 2810/3169/2834 -f 2810/3169/2834 2819/3178/2843 2820/3179/2844 -f 2821/3181/2537 2491/2806/2526 2811/3170/2835 -f 2811/3170/2835 2820/3179/2844 2821/3181/2537 -f 2822/3182/2845 2812/3171/2836 2719/3067/2750 -f 2719/3067/2750 2823/3184/2760 2822/3182/2845 -f 2824/3185/2846 2813/3172/2837 2812/3171/2836 -f 2812/3171/2836 2822/3182/2845 2824/3185/2846 -f 2825/3186/2847 2814/3173/2838 2813/3172/2837 -f 2813/3172/2837 2824/3185/2846 2825/3186/2847 -f 2826/3187/2848 2815/3174/2839 2814/3173/2838 -f 2814/3173/2838 2825/3186/2847 2826/3187/2848 -f 2827/3188/2849 2816/3175/2840 2815/3174/2839 -f 2815/3174/2839 2826/3187/2848 2827/3188/2849 -f 2828/3189/2850 2817/3176/2841 2816/3175/2840 -f 2816/3175/2840 2827/3188/2849 2828/3189/2850 -f 2829/3190/2851 2818/3177/2842 2817/3176/2841 -f 2817/3176/2841 2828/3189/2850 2829/3190/2851 -f 2830/3191/2852 2819/3178/2843 2818/3177/2842 -f 2818/3177/2842 2829/3190/2851 2830/3191/2852 -f 2831/3192/2853 2820/3179/2844 2819/3178/2843 -f 2819/3178/2843 2830/3191/2852 2831/3192/2853 -f 2832/3194/2548 2821/3181/2537 2820/3179/2844 -f 2820/3179/2844 2831/3192/2853 2832/3194/2548 -f 2833/3195/2854 2822/3182/2845 2823/3184/2760 -f 2823/3184/2760 2834/3197/2770 2833/3195/2854 -f 2835/3198/2855 2824/3185/2846 2822/3182/2845 -f 2822/3182/2845 2833/3195/2854 2835/3198/2855 -f 2836/3199/2856 2825/3186/2847 2824/3185/2846 -f 2824/3185/2846 2835/3198/2855 2836/3199/2856 -f 2837/3200/2857 2826/3187/2848 2825/3186/2847 -f 2825/3186/2847 2836/3199/2856 2837/3200/2857 -f 2838/3201/2858 2827/3188/2849 2826/3187/2848 -f 2826/3187/2848 2837/3200/2857 2838/3201/2858 -f 2839/3202/2859 2828/3189/2850 2827/3188/2849 -f 2827/3188/2849 2838/3201/2858 2839/3202/2859 -f 2840/3203/2860 2829/3190/2851 2828/3189/2850 -f 2828/3189/2850 2839/3202/2859 2840/3203/2860 -f 2841/3204/2861 2830/3191/2852 2829/3190/2851 -f 2829/3190/2851 2840/3203/2860 2841/3204/2861 -f 2842/3205/2862 2831/3192/2853 2830/3191/2852 -f 2830/3191/2852 2841/3204/2861 2842/3205/2862 -f 2843/3207/2559 2832/3194/2548 2831/3192/2853 -f 2831/3192/2853 2842/3205/2862 2843/3207/2559 -f 2844/3208/2863 2833/3195/2854 2834/3197/2770 -f 2834/3197/2770 2845/3210/2780 2844/3208/2863 -f 2846/3211/2864 2835/3198/2855 2833/3195/2854 -f 2833/3195/2854 2844/3208/2863 2846/3211/2864 -f 2847/3212/2865 2836/3199/2856 2835/3198/2855 -f 2835/3198/2855 2846/3211/2864 2847/3212/2865 -f 2848/3213/2866 2837/3200/2857 2836/3199/2856 -f 2836/3199/2856 2847/3212/2865 2848/3213/2866 -f 2849/3214/2867 2838/3201/2858 2837/3200/2857 -f 2837/3200/2857 2848/3213/2866 2849/3214/2867 -f 2850/3215/2868 2839/3202/2859 2838/3201/2858 -f 2838/3201/2858 2849/3214/2867 2850/3215/2868 -f 2851/3216/2869 2840/3203/2860 2839/3202/2859 -f 2839/3202/2859 2850/3215/2868 2851/3216/2869 -f 2852/3217/2870 2841/3204/2861 2840/3203/2860 -f 2840/3203/2860 2851/3216/2869 2852/3217/2870 -f 2853/3218/2871 2842/3205/2862 2841/3204/2861 -f 2841/3204/2861 2852/3217/2870 2853/3218/2871 -f 2854/3220/2570 2843/3207/2559 2842/3205/2862 -f 2842/3205/2862 2853/3218/2871 2854/3220/2570 -f 2855/3221/2872 2844/3208/2863 2845/3210/2780 -f 2845/3210/2780 2759/3111/2790 2855/3221/2872 -f 2856/3222/2873 2846/3211/2864 2844/3208/2863 -f 2844/3208/2863 2855/3221/2872 2856/3222/2873 -f 2857/3223/2874 2847/3212/2865 2846/3211/2864 -f 2846/3211/2864 2856/3222/2873 2857/3223/2874 -f 2858/3224/2875 2848/3213/2866 2847/3212/2865 -f 2847/3212/2865 2857/3223/2874 2858/3224/2875 -f 2859/3225/2876 2849/3214/2867 2848/3213/2866 -f 2848/3213/2866 2858/3224/2875 2859/3225/2876 -f 2860/3226/2877 2850/3215/2868 2849/3214/2867 -f 2849/3214/2867 2859/3225/2876 2860/3226/2877 -f 2861/3227/2878 2851/3216/2869 2850/3215/2868 -f 2850/3215/2868 2860/3226/2877 2861/3227/2878 -f 2862/3228/2879 2852/3217/2870 2851/3216/2869 -f 2851/3216/2869 2861/3227/2878 2862/3228/2879 -f 2863/3229/2880 2853/3218/2871 2852/3217/2870 -f 2852/3217/2870 2862/3228/2879 2863/3229/2880 -f 2864/3231/2581 2854/3220/2570 2853/3218/2871 -f 2853/3218/2871 2863/3229/2880 2864/3231/2581 -f 2865/3232/2881 2866/3236/2882 2867/3238/2883 -f 2865/3232/2881 2867/3238/2883 2868/3239/2884 -f 2865/3232/2881 2868/3239/2884 2869/3240/2885 -f 2865/3232/2881 2869/3240/2885 2870/3241/2886 -f 2865/3232/2881 2870/3241/2886 2871/3242/2887 -f 2865/3232/2881 2871/3242/2887 2872/3243/2888 -f 2865/3232/2881 2872/3243/2888 2873/3244/2889 -f 2865/3232/2881 2873/3244/2889 2874/3245/2890 -f 2865/3232/2881 2874/3245/2890 2875/3246/2891 -f 2865/3232/2881 2875/3246/2891 2876/3247/2892 -f 2877/3249/2893 2867/3238/2883 2866/3236/2882 -f 2866/3236/2882 2878/3250/2894 2877/3249/2893 -f 2879/3252/2895 2868/3239/2884 2867/3238/2883 -f 2867/3238/2883 2877/3249/2893 2879/3252/2895 -f 2880/3253/2896 2869/3240/2885 2868/3239/2884 -f 2868/3239/2884 2879/3252/2895 2880/3253/2896 -f 2881/3254/2897 2870/3241/2886 2869/3240/2885 -f 2869/3240/2885 2880/3253/2896 2881/3254/2897 -f 2882/3255/2898 2871/3242/2887 2870/3241/2886 -f 2870/3241/2886 2881/3254/2897 2882/3255/2898 -f 2883/3256/2899 2872/3243/2888 2871/3242/2887 -f 2871/3242/2887 2882/3255/2898 2883/3256/2899 -f 2884/3257/2900 2873/3244/2889 2872/3243/2888 -f 2872/3243/2888 2883/3256/2899 2884/3257/2900 -f 2885/3258/2901 2874/3245/2890 2873/3244/2889 -f 2873/3244/2889 2884/3257/2900 2885/3258/2901 -f 2886/3259/2902 2875/3246/2891 2874/3245/2890 -f 2874/3245/2890 2885/3258/2901 2886/3259/2902 -f 2887/3260/2903 2876/3247/2892 2875/3246/2891 -f 2875/3246/2891 2886/3259/2902 2887/3260/2903 -f 2888/3262/2904 2877/3249/2893 2878/3250/2894 -f 2878/3250/2894 3175/3584/2905 2888/3262/2904 -f 2889/3263/2906 2879/3252/2895 2877/3249/2893 -f 2877/3249/2893 2888/3262/2904 2889/3263/2906 -f 2890/3264/2907 2880/3253/2896 2879/3252/2895 -f 2879/3252/2895 2889/3263/2906 2890/3264/2907 -f 2891/3265/2908 2881/3254/2897 2880/3253/2896 -f 2880/3253/2896 2890/3264/2907 2891/3265/2908 -f 2892/3266/2909 2882/3255/2898 2881/3254/2897 -f 2881/3254/2897 2891/3265/2908 2892/3266/2909 -f 2893/3267/2910 2883/3256/2899 2882/3255/2898 -f 2882/3255/2898 2892/3266/2909 2893/3267/2910 -f 2894/3268/2911 2884/3257/2900 2883/3256/2899 -f 2883/3256/2899 2893/3267/2910 2894/3268/2911 -f 2895/3269/2912 2885/3258/2901 2884/3257/2900 -f 2884/3257/2900 2894/3268/2911 2895/3269/2912 -f 2896/3270/2913 2886/3259/2902 2885/3258/2901 -f 2885/3258/2901 2895/3269/2912 2896/3270/2913 -f 2897/3271/2914 2887/3260/2903 2886/3259/2902 -f 2886/3259/2902 2896/3270/2913 2897/3271/2914 -f 2898/3273/2915 2888/3262/2904 3175/3584/2905 -f 3175/3584/2905 2899/3274/2916 2898/3273/2915 -f 2900/3276/2917 2889/3263/2906 2888/3262/2904 -f 2888/3262/2904 2898/3273/2915 2900/3276/2917 -f 2901/3277/2918 2890/3264/2907 2889/3263/2906 -f 2889/3263/2906 2900/3276/2917 2901/3277/2918 -f 2902/3278/2919 2891/3265/2908 2890/3264/2907 -f 2890/3264/2907 2901/3277/2918 2902/3278/2919 -f 2903/3279/2920 2892/3266/2909 2891/3265/2908 -f 2891/3265/2908 2902/3278/2919 2903/3279/2920 -f 2904/3280/2921 2893/3267/2910 2892/3266/2909 -f 2892/3266/2909 2903/3279/2920 2904/3280/2921 -f 2905/3281/2922 2894/3268/2911 2893/3267/2910 -f 2893/3267/2910 2904/3280/2921 2905/3281/2922 -f 2906/3282/2923 2895/3269/2912 2894/3268/2911 -f 2894/3268/2911 2905/3281/2922 2906/3282/2923 -f 2907/3283/2924 2896/3270/2913 2895/3269/2912 -f 2895/3269/2912 2906/3282/2923 2907/3283/2924 -f 2908/3284/2925 2897/3271/2914 2896/3270/2913 -f 2896/3270/2913 2907/3283/2924 2908/3284/2925 -f 2909/3286/2926 2898/3273/2915 2899/3274/2916 -f 2899/3274/2916 3194/3604/2927 2909/3286/2926 -f 2910/3287/2928 2900/3276/2917 2898/3273/2915 -f 2898/3273/2915 2909/3286/2926 2910/3287/2928 -f 2911/3288/2929 2901/3277/2918 2900/3276/2917 -f 2900/3276/2917 2910/3287/2928 2911/3288/2929 -f 2912/3289/2930 2902/3278/2919 2901/3277/2918 -f 2901/3277/2918 2911/3288/2929 2912/3289/2930 -f 2913/3290/2931 2903/3279/2920 2902/3278/2919 -f 2902/3278/2919 2912/3289/2930 2913/3290/2931 -f 2914/3291/2932 2904/3280/2921 2903/3279/2920 -f 2903/3279/2920 2913/3290/2931 2914/3291/2932 -f 2915/3292/2933 2905/3281/2922 2904/3280/2921 -f 2904/3280/2921 2914/3291/2932 2915/3292/2933 -f 2916/3293/2934 2906/3282/2923 2905/3281/2922 -f 2905/3281/2922 2915/3292/2933 2916/3293/2934 -f 2917/3294/2935 2907/3283/2924 2906/3282/2923 -f 2906/3282/2923 2916/3293/2934 2917/3294/2935 -f 3004/3394/2936 2908/3284/2925 2907/3283/2924 -f 2907/3283/2924 2917/3294/2935 3004/3394/2936 -f 2918/3295/2937 2909/3286/2926 3194/3604/2927 -f 3194/3604/2927 3204/3615/2938 2918/3295/2937 -f 2919/3296/2939 2910/3287/2928 2909/3286/2926 -f 2909/3286/2926 2918/3295/2937 2919/3296/2939 -f 2920/3297/2940 2911/3288/2929 2910/3287/2928 -f 2910/3287/2928 2919/3296/2939 2920/3297/2940 -f 2921/3298/2941 2912/3289/2930 2911/3288/2929 -f 2911/3288/2929 2920/3297/2940 2921/3298/2941 -f 2922/3299/2942 2913/3290/2931 2912/3289/2930 -f 2912/3289/2930 2921/3298/2941 2922/3299/2942 -f 2923/3300/2943 2914/3291/2932 2913/3290/2931 -f 2913/3290/2931 2922/3299/2942 2923/3300/2943 -f 2924/3301/2944 2915/3292/2933 2914/3291/2932 -f 2914/3291/2932 2923/3300/2943 2924/3301/2944 -f 2925/3302/2945 2916/3293/2934 2915/3292/2933 -f 2915/3292/2933 2924/3301/2944 2925/3302/2945 -f 2926/3303/2946 2917/3294/2935 2916/3293/2934 -f 2916/3293/2934 2925/3302/2945 2926/3303/2946 -f 3015/3407/2947 3004/3394/2936 2917/3294/2935 -f 2917/3294/2935 2926/3303/2946 3015/3407/2947 -f 2927/3304/2948 2918/3295/2937 3204/3615/2938 -f 3204/3615/2938 3215/3628/2949 2927/3304/2948 -f 2928/3305/2950 2919/3296/2939 2918/3295/2937 -f 2918/3295/2937 2927/3304/2948 2928/3305/2950 -f 2929/3306/2951 2920/3297/2940 2919/3296/2939 -f 2919/3296/2939 2928/3305/2950 2929/3306/2951 -f 2930/3307/2952 2921/3298/2941 2920/3297/2940 -f 2920/3297/2940 2929/3306/2951 2930/3307/2952 -f 2931/3308/2953 2922/3299/2942 2921/3298/2941 -f 2921/3298/2941 2930/3307/2952 2931/3308/2953 -f 2932/3309/2954 2923/3300/2943 2922/3299/2942 -f 2922/3299/2942 2931/3308/2953 2932/3309/2954 -f 2933/3310/2955 2924/3301/2944 2923/3300/2943 -f 2923/3300/2943 2932/3309/2954 2933/3310/2955 -f 2934/3311/2956 2925/3302/2945 2924/3301/2944 -f 2924/3301/2944 2933/3310/2955 2934/3311/2956 -f 2935/3312/2957 2926/3303/2946 2925/3302/2945 -f 2925/3302/2945 2934/3311/2956 2935/3312/2957 -f 2936/3313/2958 3015/3407/2947 2926/3303/2946 -f 2926/3303/2946 2935/3312/2957 2936/3313/2958 -f 2937/3315/2959 2927/3304/2948 3215/3628/2949 -f 3215/3628/2949 3225/3639/2960 2937/3315/2959 -f 2938/3316/2961 2928/3305/2950 2927/3304/2948 -f 2927/3304/2948 2937/3315/2959 2938/3316/2961 -f 2939/3317/2962 2929/3306/2951 2928/3305/2950 -f 2928/3305/2950 2938/3316/2961 2939/3317/2962 -f 2940/3318/2963 2930/3307/2952 2929/3306/2951 -f 2929/3306/2951 2939/3317/2962 2940/3318/2963 -f 2941/3319/2964 2931/3308/2953 2930/3307/2952 -f 2930/3307/2952 2940/3318/2963 2941/3319/2964 -f 2942/3320/2965 2932/3309/2954 2931/3308/2953 -f 2931/3308/2953 2941/3319/2964 2942/3320/2965 -f 2943/3321/2966 2933/3310/2955 2932/3309/2954 -f 2932/3309/2954 2942/3320/2965 2943/3321/2966 -f 2944/3322/2967 2934/3311/2956 2933/3310/2955 -f 2933/3310/2955 2943/3321/2966 2944/3322/2967 -f 2945/3323/2968 2935/3312/2957 2934/3311/2956 -f 2934/3311/2956 2944/3322/2967 2945/3323/2968 -f 2946/3324/2969 2936/3313/2958 2935/3312/2957 -f 2935/3312/2957 2945/3323/2968 2946/3324/2969 -f 2947/3326/2970 2937/3315/2959 3225/3639/2960 -f 3225/3639/2960 2948/3327/2971 2947/3326/2970 -f 2949/3329/2972 2938/3316/2961 2937/3315/2959 -f 2937/3315/2959 2947/3326/2970 2949/3329/2972 -f 2950/3330/2973 2939/3317/2962 2938/3316/2961 -f 2938/3316/2961 2949/3329/2972 2950/3330/2973 -f 2951/3331/2974 2940/3318/2963 2939/3317/2962 -f 2939/3317/2962 2950/3330/2973 2951/3331/2974 -f 2952/3332/2975 2941/3319/2964 2940/3318/2963 -f 2940/3318/2963 2951/3331/2974 2952/3332/2975 -f 2953/3333/2976 2942/3320/2965 2941/3319/2964 -f 2941/3319/2964 2952/3332/2975 2953/3333/2976 -f 2954/3334/2977 2943/3321/2966 2942/3320/2965 -f 2942/3320/2965 2953/3333/2976 2954/3334/2977 -f 2955/3335/2978 2944/3322/2967 2943/3321/2966 -f 2943/3321/2966 2954/3334/2977 2955/3335/2978 -f 2956/3336/2979 2945/3323/2968 2944/3322/2967 -f 2944/3322/2967 2955/3335/2978 2956/3336/2979 -f 2957/3337/2980 2946/3324/2969 2945/3323/2968 -f 2945/3323/2968 2956/3336/2979 2957/3337/2980 -f 1224/1414/1240 2947/3326/2970 2948/3327/2971 -f 2948/3327/2971 922/1046/941 1224/1414/1240 -f 2958/3340/1239 2949/3329/2972 2947/3326/2970 -f 2947/3326/2970 1224/1414/1240 2958/3340/1239 -f 2959/3342/1238 2950/3330/2973 2949/3329/2972 -f 2949/3329/2972 2958/3340/1239 2959/3342/1238 -f 2960/3344/1237 2951/3331/2974 2950/3330/2973 -f 2950/3330/2973 2959/3342/1238 2960/3344/1237 -f 1223/1412/1236 2952/3332/2975 2951/3331/2974 -f 2951/3331/2974 2960/3344/1237 1223/1412/1236 -f 2961/3346/1235 2953/3333/2976 2952/3332/2975 -f 2952/3332/2975 1223/1412/1236 2961/3346/1235 -f 1222/1410/1234 2954/3334/2977 2953/3333/2976 -f 2953/3333/2976 2961/3346/1235 1222/1410/1234 -f 2962/3348/1233 2955/3335/2978 2954/3334/2977 -f 2954/3334/2977 1222/1410/1234 2962/3348/1233 -f 1221/1408/1232 2956/3336/2979 2955/3335/2978 -f 2955/3335/2978 2962/3348/1233 1221/1408/1232 -f 1125/1296/1150 2957/3337/2980 2956/3336/2979 -f 2956/3336/2979 1221/1408/1232 1125/1296/1150 -f 2865/3233/2881 2876/3248/2892 2963/3349/2981 -f 2865/3233/2881 2963/3349/2981 2964/3350/2982 -f 2865/3233/2881 2964/3350/2982 2965/3351/2983 -f 2865/3233/2881 2965/3351/2983 2966/3352/2984 -f 2865/3233/2881 2966/3352/2984 2967/3353/2985 -f 2865/3233/2881 2967/3353/2985 2968/3354/2986 -f 2865/3233/2881 2968/3354/2986 2969/3355/2987 -f 2865/3233/2881 2969/3355/2987 2970/3356/2988 -f 2865/3233/2881 2970/3356/2988 2971/3357/2989 -f 2865/3233/2881 2971/3357/2989 2972/3358/2990 -f 2973/3360/2991 2963/3349/2981 2876/3248/2892 -f 2876/3248/2892 2887/3261/2903 2973/3360/2991 -f 2974/3361/2992 2964/3350/2982 2963/3349/2981 -f 2963/3349/2981 2973/3360/2991 2974/3361/2992 -f 2975/3362/2993 2965/3351/2983 2964/3350/2982 -f 2964/3350/2982 2974/3361/2992 2975/3362/2993 -f 2976/3363/2994 2966/3352/2984 2965/3351/2983 -f 2965/3351/2983 2975/3362/2993 2976/3363/2994 -f 2977/3364/2995 2967/3353/2985 2966/3352/2984 -f 2966/3352/2984 2976/3363/2994 2977/3364/2995 -f 2978/3365/2996 2968/3354/2986 2967/3353/2985 -f 2967/3353/2985 2977/3364/2995 2978/3365/2996 -f 2979/3366/2997 2969/3355/2987 2968/3354/2986 -f 2968/3354/2986 2978/3365/2996 2979/3366/2997 -f 2980/3367/2998 2970/3356/2988 2969/3355/2987 -f 2969/3355/2987 2979/3366/2997 2980/3367/2998 -f 2981/3368/2999 2971/3357/2989 2970/3356/2988 -f 2970/3356/2988 2980/3367/2998 2981/3368/2999 -f 2982/3369/3000 2972/3358/2990 2971/3357/2989 -f 2971/3357/2989 2981/3368/2999 2982/3369/3000 -f 2983/3371/3001 2973/3360/2991 2887/3261/2903 -f 2887/3261/2903 2897/3272/2914 2983/3371/3001 -f 2984/3372/3002 2974/3361/2992 2973/3360/2991 -f 2973/3360/2991 2983/3371/3001 2984/3372/3002 -f 2985/3373/3003 2975/3362/2993 2974/3361/2992 -f 2974/3361/2992 2984/3372/3002 2985/3373/3003 -f 2986/3374/3004 2976/3363/2994 2975/3362/2993 -f 2975/3362/2993 2985/3373/3003 2986/3374/3004 -f 2987/3375/3005 2977/3364/2995 2976/3363/2994 -f 2976/3363/2994 2986/3374/3004 2987/3375/3005 -f 2988/3376/3006 2978/3365/2996 2977/3364/2995 -f 2977/3364/2995 2987/3375/3005 2988/3376/3006 -f 2989/3377/3007 2979/3366/2997 2978/3365/2996 -f 2978/3365/2996 2988/3376/3006 2989/3377/3007 -f 2990/3378/3008 2980/3367/2998 2979/3366/2997 -f 2979/3366/2997 2989/3377/3007 2990/3378/3008 -f 2991/3379/3009 2981/3368/2999 2980/3367/2998 -f 2980/3367/2998 2990/3378/3008 2991/3379/3009 -f 2992/3380/3010 2982/3369/3000 2981/3368/2999 -f 2981/3368/2999 2991/3379/3009 2992/3380/3010 -f 2993/3382/3011 2983/3371/3001 2897/3272/2914 -f 2897/3272/2914 2908/3285/2925 2993/3382/3011 -f 2994/3383/3012 2984/3372/3002 2983/3371/3001 -f 2983/3371/3001 2993/3382/3011 2994/3383/3012 -f 2995/3384/3013 2985/3373/3003 2984/3372/3002 -f 2984/3372/3002 2994/3383/3012 2995/3384/3013 -f 2996/3385/3014 2986/3374/3004 2985/3373/3003 -f 2985/3373/3003 2995/3384/3013 2996/3385/3014 -f 2997/3386/3015 2987/3375/3005 2986/3374/3004 -f 2986/3374/3004 2996/3385/3014 2997/3386/3015 -f 2998/3387/3016 2988/3376/3006 2987/3375/3005 -f 2987/3375/3005 2997/3386/3015 2998/3387/3016 -f 2999/3388/3017 2989/3377/3007 2988/3376/3006 -f 2988/3376/3006 2998/3387/3016 2999/3388/3017 -f 3000/3389/3018 2990/3378/3008 2989/3377/3007 -f 2989/3377/3007 2999/3388/3017 3000/3389/3018 -f 3001/3390/3019 2991/3379/3009 2990/3378/3008 -f 2990/3378/3008 3000/3389/3018 3001/3390/3019 -f 3002/3391/3020 2992/3380/3010 2991/3379/3009 -f 2991/3379/3009 3001/3390/3019 3002/3391/3020 -f 3003/3393/3021 2993/3382/3011 2908/3285/2925 -f 2908/3285/2925 3004/3395/2936 3003/3393/3021 -f 3005/3396/3022 2994/3383/3012 2993/3382/3011 -f 2993/3382/3011 3003/3393/3021 3005/3396/3022 -f 3006/3397/3023 2995/3384/3013 2994/3383/3012 -f 2994/3383/3012 3005/3396/3022 3006/3397/3023 -f 3007/3398/3024 2996/3385/3014 2995/3384/3013 -f 2995/3384/3013 3006/3397/3023 3007/3398/3024 -f 3008/3399/3025 2997/3386/3015 2996/3385/3014 -f 2996/3385/3014 3007/3398/3024 3008/3399/3025 -f 3009/3400/3026 2998/3387/3016 2997/3386/3015 -f 2997/3386/3015 3008/3399/3025 3009/3400/3026 -f 3010/3401/3027 2999/3388/3017 2998/3387/3016 -f 2998/3387/3016 3009/3400/3026 3010/3401/3027 -f 3011/3402/3028 3000/3389/3018 2999/3388/3017 -f 2999/3388/3017 3010/3401/3027 3011/3402/3028 -f 3012/3403/3029 3001/3390/3019 3000/3389/3018 -f 3000/3389/3018 3011/3402/3028 3012/3403/3029 -f 3013/3404/3030 3002/3391/3020 3001/3390/3019 -f 3001/3390/3019 3012/3403/3029 3013/3404/3030 -f 3014/3406/3031 3003/3393/3021 3004/3395/2936 -f 3004/3395/2936 3015/3408/2947 3014/3406/3031 -f 3016/3409/3032 3005/3396/3022 3003/3393/3021 -f 3003/3393/3021 3014/3406/3031 3016/3409/3032 -f 3017/3410/3033 3006/3397/3023 3005/3396/3022 -f 3005/3396/3022 3016/3409/3032 3017/3410/3033 -f 3018/3411/3034 3007/3398/3024 3006/3397/3023 -f 3006/3397/3023 3017/3410/3033 3018/3411/3034 -f 3019/3412/3035 3008/3399/3025 3007/3398/3024 -f 3007/3398/3024 3018/3411/3034 3019/3412/3035 -f 3020/3413/3036 3009/3400/3026 3008/3399/3025 -f 3008/3399/3025 3019/3412/3035 3020/3413/3036 -f 3021/3414/3037 3010/3401/3027 3009/3400/3026 -f 3009/3400/3026 3020/3413/3036 3021/3414/3037 -f 3022/3415/3038 3011/3402/3028 3010/3401/3027 -f 3010/3401/3027 3021/3414/3037 3022/3415/3038 -f 3023/3416/3039 3012/3403/3029 3011/3402/3028 -f 3011/3402/3028 3022/3415/3038 3023/3416/3039 -f 3024/3417/3040 3013/3404/3030 3012/3403/3029 -f 3012/3403/3029 3023/3416/3039 3024/3417/3040 -f 3025/3419/3041 3014/3406/3031 3015/3408/2947 -f 3015/3408/2947 2936/3314/2958 3025/3419/3041 -f 3026/3420/3042 3016/3409/3032 3014/3406/3031 -f 3014/3406/3031 3025/3419/3041 3026/3420/3042 -f 3027/3421/3043 3017/3410/3033 3016/3409/3032 -f 3016/3409/3032 3026/3420/3042 3027/3421/3043 -f 3028/3422/3044 3018/3411/3034 3017/3410/3033 -f 3017/3410/3033 3027/3421/3043 3028/3422/3044 -f 3029/3423/3045 3019/3412/3035 3018/3411/3034 -f 3018/3411/3034 3028/3422/3044 3029/3423/3045 -f 3030/3424/3046 3020/3413/3036 3019/3412/3035 -f 3019/3412/3035 3029/3423/3045 3030/3424/3046 -f 3031/3425/3047 3021/3414/3037 3020/3413/3036 -f 3020/3413/3036 3030/3424/3046 3031/3425/3047 -f 3032/3426/3048 3022/3415/3038 3021/3414/3037 -f 3021/3414/3037 3031/3425/3047 3032/3426/3048 -f 3033/3427/3049 3023/3416/3039 3022/3415/3038 -f 3022/3415/3038 3032/3426/3048 3033/3427/3049 -f 3116/3519/3050 3024/3417/3040 3023/3416/3039 -f 3023/3416/3039 3033/3427/3049 3116/3519/3050 -f 3034/3428/3051 3025/3419/3041 2936/3314/2958 -f 2936/3314/2958 2946/3325/2969 3034/3428/3051 -f 3035/3429/3052 3026/3420/3042 3025/3419/3041 -f 3025/3419/3041 3034/3428/3051 3035/3429/3052 -f 3036/3430/3053 3027/3421/3043 3026/3420/3042 -f 3026/3420/3042 3035/3429/3052 3036/3430/3053 -f 3037/3431/3054 3028/3422/3044 3027/3421/3043 -f 3027/3421/3043 3036/3430/3053 3037/3431/3054 -f 3038/3432/3055 3029/3423/3045 3028/3422/3044 -f 3028/3422/3044 3037/3431/3054 3038/3432/3055 -f 3039/3433/3056 3030/3424/3046 3029/3423/3045 -f 3029/3423/3045 3038/3432/3055 3039/3433/3056 -f 3040/3434/3057 3031/3425/3047 3030/3424/3046 -f 3030/3424/3046 3039/3433/3056 3040/3434/3057 -f 3041/3435/3058 3032/3426/3048 3031/3425/3047 -f 3031/3425/3047 3040/3434/3057 3041/3435/3058 -f 3042/3436/3059 3033/3427/3049 3032/3426/3048 -f 3032/3426/3048 3041/3435/3058 3042/3436/3059 -f 3126/3530/3060 3116/3519/3050 3033/3427/3049 -f 3033/3427/3049 3042/3436/3059 3126/3530/3060 -f 3043/3437/3061 3034/3428/3051 2946/3325/2969 -f 2946/3325/2969 2957/3338/2980 3043/3437/3061 -f 3044/3438/3062 3035/3429/3052 3034/3428/3051 -f 3034/3428/3051 3043/3437/3061 3044/3438/3062 -f 3045/3439/3063 3036/3430/3053 3035/3429/3052 -f 3035/3429/3052 3044/3438/3062 3045/3439/3063 -f 3046/3440/3064 3037/3431/3054 3036/3430/3053 -f 3036/3430/3053 3045/3439/3063 3046/3440/3064 -f 3047/3441/3065 3038/3432/3055 3037/3431/3054 -f 3037/3431/3054 3046/3440/3064 3047/3441/3065 -f 3048/3442/3066 3039/3433/3056 3038/3432/3055 -f 3038/3432/3055 3047/3441/3065 3048/3442/3066 -f 3049/3443/3067 3040/3434/3057 3039/3433/3056 -f 3039/3433/3056 3048/3442/3066 3049/3443/3067 -f 3050/3444/3068 3041/3435/3058 3040/3434/3057 -f 3040/3434/3057 3049/3443/3067 3050/3444/3068 -f 3051/3445/3069 3042/3436/3059 3041/3435/3058 -f 3041/3435/3058 3050/3444/3068 3051/3445/3069 -f 3137/3543/3070 3126/3530/3060 3042/3436/3059 -f 3042/3436/3059 3051/3445/3069 3137/3543/3070 -f 1124/1293/1149 3043/3437/3061 2957/3338/2980 -f 2957/3338/2980 1125/1297/1150 1124/1293/1149 -f 3052/3447/1148 3044/3438/3062 3043/3437/3061 -f 3043/3437/3061 1124/1293/1149 3052/3447/1148 -f 1123/1291/1147 3045/3439/3063 3044/3438/3062 -f 3044/3438/3062 3052/3447/1148 1123/1291/1147 -f 3053/3449/1146 3046/3440/3064 3045/3439/3063 -f 3045/3439/3063 1123/1291/1147 3053/3449/1146 -f 1122/1289/1145 3047/3441/3065 3046/3440/3064 -f 3046/3440/3064 3053/3449/1146 1122/1289/1145 -f 1121/1287/1144 3048/3442/3066 3047/3441/3065 -f 3047/3441/3065 1122/1289/1145 1121/1287/1144 -f 3054/3451/1143 3049/3443/3067 3048/3442/3066 -f 3048/3442/3066 1121/1287/1144 3054/3451/1143 -f 3055/3453/1142 3050/3444/3068 3049/3443/3067 -f 3049/3443/3067 3054/3451/1143 3055/3453/1142 -f 1119/1281/1141 3051/3445/3069 3050/3444/3068 -f 3050/3444/3068 3055/3453/1142 1119/1281/1141 -f 1120/1284/1050 3137/3543/3070 3051/3445/3069 -f 3051/3445/3069 1119/1281/1141 1120/1284/1050 -f 2865/3234/2881 2972/3359/2990 3056/3454/3071 -f 2865/3234/2881 3056/3454/3071 3057/3455/3072 -f 2865/3234/2881 3057/3455/3072 3058/3456/3073 -f 2865/3234/2881 3058/3456/3073 3059/3457/3074 -f 2865/3234/2881 3059/3457/3074 3060/3458/3075 -f 2865/3234/2881 3060/3458/3075 3061/3459/3076 -f 2865/3234/2881 3061/3459/3076 3062/3460/3077 -f 2865/3234/2881 3062/3460/3077 3063/3461/3078 -f 2865/3234/2881 3063/3461/3078 3064/3462/3079 -f 2865/3234/2881 3064/3462/3079 3147/3555/3080 -f 3065/3463/3081 3056/3454/3071 2972/3359/2990 -f 2972/3359/2990 2982/3370/3000 3065/3463/3081 -f 3066/3464/3082 3057/3455/3072 3056/3454/3071 -f 3056/3454/3071 3065/3463/3081 3066/3464/3082 -f 3067/3465/3083 3058/3456/3073 3057/3455/3072 -f 3057/3455/3072 3066/3464/3082 3067/3465/3083 -f 3068/3466/3084 3059/3457/3074 3058/3456/3073 -f 3058/3456/3073 3067/3465/3083 3068/3466/3084 -f 3069/3467/3085 3060/3458/3075 3059/3457/3074 -f 3059/3457/3074 3068/3466/3084 3069/3467/3085 -f 3070/3468/3086 3061/3459/3076 3060/3458/3075 -f 3060/3458/3075 3069/3467/3085 3070/3468/3086 -f 3071/3469/3087 3062/3460/3077 3061/3459/3076 -f 3061/3459/3076 3070/3468/3086 3071/3469/3087 -f 3072/3470/3088 3063/3461/3078 3062/3460/3077 -f 3062/3460/3077 3071/3469/3087 3072/3470/3088 -f 3073/3471/3089 3064/3462/3079 3063/3461/3078 -f 3063/3461/3078 3072/3470/3088 3073/3471/3089 -f 3074/3472/3090 3147/3555/3080 3064/3462/3079 -f 3064/3462/3079 3073/3471/3089 3074/3472/3090 -f 3075/3474/3091 3065/3463/3081 2982/3370/3000 -f 2982/3370/3000 2992/3381/3010 3075/3474/3091 -f 3076/3475/3092 3066/3464/3082 3065/3463/3081 -f 3065/3463/3081 3075/3474/3091 3076/3475/3092 -f 3077/3476/3093 3067/3465/3083 3066/3464/3082 -f 3066/3464/3082 3076/3475/3092 3077/3476/3093 -f 3078/3477/3094 3068/3466/3084 3067/3465/3083 -f 3067/3465/3083 3077/3476/3093 3078/3477/3094 -f 3079/3478/3095 3069/3467/3085 3068/3466/3084 -f 3068/3466/3084 3078/3477/3094 3079/3478/3095 -f 3080/3479/3096 3070/3468/3086 3069/3467/3085 -f 3069/3467/3085 3079/3478/3095 3080/3479/3096 -f 3081/3480/3097 3071/3469/3087 3070/3468/3086 -f 3070/3468/3086 3080/3479/3096 3081/3480/3097 -f 3082/3481/3098 3072/3470/3088 3071/3469/3087 -f 3071/3469/3087 3081/3480/3097 3082/3481/3098 -f 3083/3482/3099 3073/3471/3089 3072/3470/3088 -f 3072/3470/3088 3082/3481/3098 3083/3482/3099 -f 3084/3483/3100 3074/3472/3090 3073/3471/3089 -f 3073/3471/3089 3083/3482/3099 3084/3483/3100 -f 3085/3485/3101 3075/3474/3091 2992/3381/3010 -f 2992/3381/3010 3002/3392/3020 3085/3485/3101 -f 3086/3486/3102 3076/3475/3092 3075/3474/3091 -f 3075/3474/3091 3085/3485/3101 3086/3486/3102 -f 3087/3487/3103 3077/3476/3093 3076/3475/3092 -f 3076/3475/3092 3086/3486/3102 3087/3487/3103 -f 3088/3488/3104 3078/3477/3094 3077/3476/3093 -f 3077/3476/3093 3087/3487/3103 3088/3488/3104 -f 3089/3489/3105 3079/3478/3095 3078/3477/3094 -f 3078/3477/3094 3088/3488/3104 3089/3489/3105 -f 3090/3490/3106 3080/3479/3096 3079/3478/3095 -f 3079/3478/3095 3089/3489/3105 3090/3490/3106 -f 3091/3491/3107 3081/3480/3097 3080/3479/3096 -f 3080/3479/3096 3090/3490/3106 3091/3491/3107 -f 3092/3492/3108 3082/3481/3098 3081/3480/3097 -f 3081/3480/3097 3091/3491/3107 3092/3492/3108 -f 3093/3493/3109 3083/3482/3099 3082/3481/3098 -f 3082/3481/3098 3092/3492/3108 3093/3493/3109 -f 3094/3494/3110 3084/3483/3100 3083/3482/3099 -f 3083/3482/3099 3093/3493/3109 3094/3494/3110 -f 3095/3496/3111 3085/3485/3101 3002/3392/3020 -f 3002/3392/3020 3013/3405/3030 3095/3496/3111 -f 3096/3497/3112 3086/3486/3102 3085/3485/3101 -f 3085/3485/3101 3095/3496/3111 3096/3497/3112 -f 3097/3498/3113 3087/3487/3103 3086/3486/3102 -f 3086/3486/3102 3096/3497/3112 3097/3498/3113 -f 3098/3499/3114 3088/3488/3104 3087/3487/3103 -f 3087/3487/3103 3097/3498/3113 3098/3499/3114 -f 3099/3500/3115 3089/3489/3105 3088/3488/3104 -f 3088/3488/3104 3098/3499/3114 3099/3500/3115 -f 3100/3501/3116 3090/3490/3106 3089/3489/3105 -f 3089/3489/3105 3099/3500/3115 3100/3501/3116 -f 3101/3502/3117 3091/3491/3107 3090/3490/3106 -f 3090/3490/3106 3100/3501/3116 3101/3502/3117 -f 3102/3503/3118 3092/3492/3108 3091/3491/3107 -f 3091/3491/3107 3101/3502/3117 3102/3503/3118 -f 3103/3504/3119 3093/3493/3109 3092/3492/3108 -f 3092/3492/3108 3102/3503/3118 3103/3504/3119 -f 3104/3505/3120 3094/3494/3110 3093/3493/3109 -f 3093/3493/3109 3103/3504/3119 3104/3505/3120 -f 3105/3507/3121 3095/3496/3111 3013/3405/3030 -f 3013/3405/3030 3024/3418/3040 3105/3507/3121 -f 3106/3508/3122 3096/3497/3112 3095/3496/3111 -f 3095/3496/3111 3105/3507/3121 3106/3508/3122 -f 3107/3509/3123 3097/3498/3113 3096/3497/3112 -f 3096/3497/3112 3106/3508/3122 3107/3509/3123 -f 3108/3510/3124 3098/3499/3114 3097/3498/3113 -f 3097/3498/3113 3107/3509/3123 3108/3510/3124 -f 3109/3511/3125 3099/3500/3115 3098/3499/3114 -f 3098/3499/3114 3108/3510/3124 3109/3511/3125 -f 3110/3512/3126 3100/3501/3116 3099/3500/3115 -f 3099/3500/3115 3109/3511/3125 3110/3512/3126 -f 3111/3513/3127 3101/3502/3117 3100/3501/3116 -f 3100/3501/3116 3110/3512/3126 3111/3513/3127 -f 3112/3514/3128 3102/3503/3118 3101/3502/3117 -f 3101/3502/3117 3111/3513/3127 3112/3514/3128 -f 3113/3515/3129 3103/3504/3119 3102/3503/3118 -f 3102/3503/3118 3112/3514/3128 3113/3515/3129 -f 3114/3516/3130 3104/3505/3120 3103/3504/3119 -f 3103/3504/3119 3113/3515/3129 3114/3516/3130 -f 3115/3518/3131 3105/3507/3121 3024/3418/3040 -f 3024/3418/3040 3116/3520/3050 3115/3518/3131 -f 3117/3521/3132 3106/3508/3122 3105/3507/3121 -f 3105/3507/3121 3115/3518/3131 3117/3521/3132 -f 3118/3522/3133 3107/3509/3123 3106/3508/3122 -f 3106/3508/3122 3117/3521/3132 3118/3522/3133 -f 3119/3523/3134 3108/3510/3124 3107/3509/3123 -f 3107/3509/3123 3118/3522/3133 3119/3523/3134 -f 3120/3524/3135 3109/3511/3125 3108/3510/3124 -f 3108/3510/3124 3119/3523/3134 3120/3524/3135 -f 3121/3525/3136 3110/3512/3126 3109/3511/3125 -f 3109/3511/3125 3120/3524/3135 3121/3525/3136 -f 3122/3526/3137 3111/3513/3127 3110/3512/3126 -f 3110/3512/3126 3121/3525/3136 3122/3526/3137 -f 3123/3527/3138 3112/3514/3128 3111/3513/3127 -f 3111/3513/3127 3122/3526/3137 3123/3527/3138 -f 3124/3528/3139 3113/3515/3129 3112/3514/3128 -f 3112/3514/3128 3123/3527/3138 3124/3528/3139 -f 3206/3618/3140 3114/3516/3130 3113/3515/3129 -f 3113/3515/3129 3124/3528/3139 3206/3618/3140 -f 3125/3529/3141 3115/3518/3131 3116/3520/3050 -f 3116/3520/3050 3126/3531/3060 3125/3529/3141 -f 3127/3532/3142 3117/3521/3132 3115/3518/3131 -f 3115/3518/3131 3125/3529/3141 3127/3532/3142 -f 3128/3533/3143 3118/3522/3133 3117/3521/3132 -f 3117/3521/3132 3127/3532/3142 3128/3533/3143 -f 3129/3534/3144 3119/3523/3134 3118/3522/3133 -f 3118/3522/3133 3128/3533/3143 3129/3534/3144 -f 3130/3535/3145 3120/3524/3135 3119/3523/3134 -f 3119/3523/3134 3129/3534/3144 3130/3535/3145 -f 3131/3536/3146 3121/3525/3136 3120/3524/3135 -f 3120/3524/3135 3130/3535/3145 3131/3536/3146 -f 3132/3537/3147 3122/3526/3137 3121/3525/3136 -f 3121/3525/3136 3131/3536/3146 3132/3537/3147 -f 3133/3538/3148 3123/3527/3138 3122/3526/3137 -f 3122/3526/3137 3132/3537/3147 3133/3538/3148 -f 3134/3539/3149 3124/3528/3139 3123/3527/3138 -f 3123/3527/3138 3133/3538/3148 3134/3539/3149 -f 3135/3540/3150 3206/3618/3140 3124/3528/3139 -f 3124/3528/3139 3134/3539/3149 3135/3540/3150 -f 3136/3542/3151 3125/3529/3141 3126/3531/3060 -f 3126/3531/3060 3137/3544/3070 3136/3542/3151 -f 3138/3545/3152 3127/3532/3142 3125/3529/3141 -f 3125/3529/3141 3136/3542/3151 3138/3545/3152 -f 3139/3546/3153 3128/3533/3143 3127/3532/3142 -f 3127/3532/3142 3138/3545/3152 3139/3546/3153 -f 3140/3547/3154 3129/3534/3144 3128/3533/3143 -f 3128/3533/3143 3139/3546/3153 3140/3547/3154 -f 3141/3548/3155 3130/3535/3145 3129/3534/3144 -f 3129/3534/3144 3140/3547/3154 3141/3548/3155 -f 3142/3549/3156 3131/3536/3146 3130/3535/3145 -f 3130/3535/3145 3141/3548/3155 3142/3549/3156 -f 3143/3550/3157 3132/3537/3147 3131/3536/3146 -f 3131/3536/3146 3142/3549/3156 3143/3550/3157 -f 3144/3551/3158 3133/3538/3148 3132/3537/3147 -f 3132/3537/3147 3143/3550/3157 3144/3551/3158 -f 3145/3552/3159 3134/3539/3149 3133/3538/3148 -f 3133/3538/3148 3144/3551/3158 3145/3552/3159 -f 3227/3642/3160 3135/3540/3150 3134/3539/3149 -f 3134/3539/3149 3145/3552/3159 3227/3642/3160 -f 1026/1176/1049 3136/3542/3151 3137/3544/3070 -f 3137/3544/3070 1120/1285/1050 1026/1176/1049 -f 1025/1174/1048 3138/3545/3152 3136/3542/3151 -f 3136/3542/3151 1026/1176/1049 1025/1174/1048 -f 1024/1172/1047 3139/3546/3153 3138/3545/3152 -f 3138/3545/3152 1025/1174/1048 1024/1172/1047 -f 1023/1170/1046 3140/3547/3154 3139/3546/3153 -f 3139/3546/3153 1024/1172/1047 1023/1170/1046 -f 1022/1168/1045 3141/3548/3155 3140/3547/3154 -f 3140/3547/3154 1023/1170/1046 1022/1168/1045 -f 1021/1166/1044 3142/3549/3156 3141/3548/3155 -f 3141/3548/3155 1022/1168/1045 1021/1166/1044 -f 1020/1164/1043 3143/3550/3157 3142/3549/3156 -f 3142/3549/3156 1021/1166/1044 1020/1164/1043 -f 1019/1162/1042 3144/3551/3158 3143/3550/3157 -f 3143/3550/3157 1020/1164/1043 1019/1162/1042 -f 3146/3554/1041 3145/3552/3159 3144/3551/3158 -f 3144/3551/3158 1019/1162/1042 3146/3554/1041 -f 926/1056/950 3227/3642/3160 3145/3552/3159 -f 3145/3552/3159 3146/3554/1041 926/1056/950 -f 2865/3235/2881 3147/3556/3080 3148/3557/3161 -f 2865/3235/2881 3148/3557/3161 3149/3558/3162 -f 2865/3235/2881 3149/3558/3162 3150/3559/3163 -f 2865/3235/2881 3150/3559/3163 3151/3560/3164 -f 2865/3235/2881 3151/3560/3164 3152/3561/3165 -f 2865/3235/2881 3152/3561/3165 3153/3562/3166 -f 2865/3235/2881 3153/3562/3166 3154/3563/3167 -f 2865/3235/2881 3154/3563/3167 3155/3564/3168 -f 2865/3235/2881 3155/3564/3168 3156/3565/3169 -f 2865/3235/2881 3156/3565/3169 2866/3237/2882 -f 3157/3566/3170 3148/3557/3161 3147/3556/3080 -f 3147/3556/3080 3074/3473/3090 3157/3566/3170 -f 3158/3567/3171 3149/3558/3162 3148/3557/3161 -f 3148/3557/3161 3157/3566/3170 3158/3567/3171 -f 3159/3568/3172 3150/3559/3163 3149/3558/3162 -f 3149/3558/3162 3158/3567/3171 3159/3568/3172 -f 3160/3569/3173 3151/3560/3164 3150/3559/3163 -f 3150/3559/3163 3159/3568/3172 3160/3569/3173 -f 3161/3570/3174 3152/3561/3165 3151/3560/3164 -f 3151/3560/3164 3160/3569/3173 3161/3570/3174 -f 3162/3571/3175 3153/3562/3166 3152/3561/3165 -f 3152/3561/3165 3161/3570/3174 3162/3571/3175 -f 3163/3572/3176 3154/3563/3167 3153/3562/3166 -f 3153/3562/3166 3162/3571/3175 3163/3572/3176 -f 3164/3573/3177 3155/3564/3168 3154/3563/3167 -f 3154/3563/3167 3163/3572/3176 3164/3573/3177 -f 3165/3574/3178 3156/3565/3169 3155/3564/3168 -f 3155/3564/3168 3164/3573/3177 3165/3574/3178 -f 2878/3251/2894 2866/3237/2882 3156/3565/3169 -f 3156/3565/3169 3165/3574/3178 2878/3251/2894 -f 3166/3575/3179 3157/3566/3170 3074/3473/3090 -f 3074/3473/3090 3084/3484/3100 3166/3575/3179 -f 3167/3576/3180 3158/3567/3171 3157/3566/3170 -f 3157/3566/3170 3166/3575/3179 3167/3576/3180 -f 3168/3577/3181 3159/3568/3172 3158/3567/3171 -f 3158/3567/3171 3167/3576/3180 3168/3577/3181 -f 3169/3578/3182 3160/3569/3173 3159/3568/3172 -f 3159/3568/3172 3168/3577/3181 3169/3578/3182 -f 3170/3579/3183 3161/3570/3174 3160/3569/3173 -f 3160/3569/3173 3169/3578/3182 3170/3579/3183 -f 3171/3580/3184 3162/3571/3175 3161/3570/3174 -f 3161/3570/3174 3170/3579/3183 3171/3580/3184 -f 3172/3581/3185 3163/3572/3176 3162/3571/3175 -f 3162/3571/3175 3171/3580/3184 3172/3581/3185 -f 3173/3582/3186 3164/3573/3177 3163/3572/3176 -f 3163/3572/3176 3172/3581/3185 3173/3582/3186 -f 3174/3583/3187 3165/3574/3178 3164/3573/3177 -f 3164/3573/3177 3173/3582/3186 3174/3583/3187 -f 3175/3585/2905 2878/3251/2894 3165/3574/3178 -f 3165/3574/3178 3174/3583/3187 3175/3585/2905 -f 3176/3586/3188 3166/3575/3179 3084/3484/3100 -f 3084/3484/3100 3094/3495/3110 3176/3586/3188 -f 3177/3587/3189 3167/3576/3180 3166/3575/3179 -f 3166/3575/3179 3176/3586/3188 3177/3587/3189 -f 3178/3588/3190 3168/3577/3181 3167/3576/3180 -f 3167/3576/3180 3177/3587/3189 3178/3588/3190 -f 3179/3589/3191 3169/3578/3182 3168/3577/3181 -f 3168/3577/3181 3178/3588/3190 3179/3589/3191 -f 3180/3590/3192 3170/3579/3183 3169/3578/3182 -f 3169/3578/3182 3179/3589/3191 3180/3590/3192 -f 3181/3591/3193 3171/3580/3184 3170/3579/3183 -f 3170/3579/3183 3180/3590/3192 3181/3591/3193 -f 3182/3592/3194 3172/3581/3185 3171/3580/3184 -f 3171/3580/3184 3181/3591/3193 3182/3592/3194 -f 3183/3593/3195 3173/3582/3186 3172/3581/3185 -f 3172/3581/3185 3182/3592/3194 3183/3593/3195 -f 3184/3594/3196 3174/3583/3187 3173/3582/3186 -f 3173/3582/3186 3183/3593/3195 3184/3594/3196 -f 2899/3275/2916 3175/3585/2905 3174/3583/3187 -f 3174/3583/3187 3184/3594/3196 2899/3275/2916 -f 3185/3595/3197 3176/3586/3188 3094/3495/3110 -f 3094/3495/3110 3104/3506/3120 3185/3595/3197 -f 3186/3596/3198 3177/3587/3189 3176/3586/3188 -f 3176/3586/3188 3185/3595/3197 3186/3596/3198 -f 3187/3597/3199 3178/3588/3190 3177/3587/3189 -f 3177/3587/3189 3186/3596/3198 3187/3597/3199 -f 3188/3598/3200 3179/3589/3191 3178/3588/3190 -f 3178/3588/3190 3187/3597/3199 3188/3598/3200 -f 3189/3599/3201 3180/3590/3192 3179/3589/3191 -f 3179/3589/3191 3188/3598/3200 3189/3599/3201 -f 3190/3600/3202 3181/3591/3193 3180/3590/3192 -f 3180/3590/3192 3189/3599/3201 3190/3600/3202 -f 3191/3601/3203 3182/3592/3194 3181/3591/3193 -f 3181/3591/3193 3190/3600/3202 3191/3601/3203 -f 3192/3602/3204 3183/3593/3195 3182/3592/3194 -f 3182/3592/3194 3191/3601/3203 3192/3602/3204 -f 3193/3603/3205 3184/3594/3196 3183/3593/3195 -f 3183/3593/3195 3192/3602/3204 3193/3603/3205 -f 3194/3605/2927 2899/3275/2916 3184/3594/3196 -f 3184/3594/3196 3193/3603/3205 3194/3605/2927 -f 3195/3606/3206 3185/3595/3197 3104/3506/3120 -f 3104/3506/3120 3114/3517/3130 3195/3606/3206 -f 3196/3607/3207 3186/3596/3198 3185/3595/3197 -f 3185/3595/3197 3195/3606/3206 3196/3607/3207 -f 3197/3608/3208 3187/3597/3199 3186/3596/3198 -f 3186/3596/3198 3196/3607/3207 3197/3608/3208 -f 3198/3609/3209 3188/3598/3200 3187/3597/3199 -f 3187/3597/3199 3197/3608/3208 3198/3609/3209 -f 3199/3610/3210 3189/3599/3201 3188/3598/3200 -f 3188/3598/3200 3198/3609/3209 3199/3610/3210 -f 3200/3611/3211 3190/3600/3202 3189/3599/3201 -f 3189/3599/3201 3199/3610/3210 3200/3611/3211 -f 3201/3612/3212 3191/3601/3203 3190/3600/3202 -f 3190/3600/3202 3200/3611/3211 3201/3612/3212 -f 3202/3613/3213 3192/3602/3204 3191/3601/3203 -f 3191/3601/3203 3201/3612/3212 3202/3613/3213 -f 3203/3614/3214 3193/3603/3205 3192/3602/3204 -f 3192/3602/3204 3202/3613/3213 3203/3614/3214 -f 3204/3616/2938 3194/3605/2927 3193/3603/3205 -f 3193/3603/3205 3203/3614/3214 3204/3616/2938 -f 3205/3617/3215 3195/3606/3206 3114/3517/3130 -f 3114/3517/3130 3206/3619/3140 3205/3617/3215 -f 3207/3620/3216 3196/3607/3207 3195/3606/3206 -f 3195/3606/3206 3205/3617/3215 3207/3620/3216 -f 3208/3621/3217 3197/3608/3208 3196/3607/3207 -f 3196/3607/3207 3207/3620/3216 3208/3621/3217 -f 3209/3622/3218 3198/3609/3209 3197/3608/3208 -f 3197/3608/3208 3208/3621/3217 3209/3622/3218 -f 3210/3623/3219 3199/3610/3210 3198/3609/3209 -f 3198/3609/3209 3209/3622/3218 3210/3623/3219 -f 3211/3624/3220 3200/3611/3211 3199/3610/3210 -f 3199/3610/3210 3210/3623/3219 3211/3624/3220 -f 3212/3625/3221 3201/3612/3212 3200/3611/3211 -f 3200/3611/3211 3211/3624/3220 3212/3625/3221 -f 3213/3626/3222 3202/3613/3213 3201/3612/3212 -f 3201/3612/3212 3212/3625/3221 3213/3626/3222 -f 3214/3627/3223 3203/3614/3214 3202/3613/3213 -f 3202/3613/3213 3213/3626/3222 3214/3627/3223 -f 3215/3629/2949 3204/3616/2938 3203/3614/3214 -f 3203/3614/3214 3214/3627/3223 3215/3629/2949 -f 3216/3630/3224 3205/3617/3215 3206/3619/3140 -f 3206/3619/3140 3135/3541/3150 3216/3630/3224 -f 3217/3631/3225 3207/3620/3216 3205/3617/3215 -f 3205/3617/3215 3216/3630/3224 3217/3631/3225 -f 3218/3632/3226 3208/3621/3217 3207/3620/3216 -f 3207/3620/3216 3217/3631/3225 3218/3632/3226 -f 3219/3633/3227 3209/3622/3218 3208/3621/3217 -f 3208/3621/3217 3218/3632/3226 3219/3633/3227 -f 3220/3634/3228 3210/3623/3219 3209/3622/3218 -f 3209/3622/3218 3219/3633/3227 3220/3634/3228 -f 3221/3635/3229 3211/3624/3220 3210/3623/3219 -f 3210/3623/3219 3220/3634/3228 3221/3635/3229 -f 3222/3636/3230 3212/3625/3221 3211/3624/3220 -f 3211/3624/3220 3221/3635/3229 3222/3636/3230 -f 3223/3637/3231 3213/3626/3222 3212/3625/3221 -f 3212/3625/3221 3222/3636/3230 3223/3637/3231 -f 3224/3638/3232 3214/3627/3223 3213/3626/3222 -f 3213/3626/3222 3223/3637/3231 3224/3638/3232 -f 3225/3640/2960 3215/3629/2949 3214/3627/3223 -f 3214/3627/3223 3224/3638/3232 3225/3640/2960 -f 3226/3641/3233 3216/3630/3224 3135/3541/3150 -f 3135/3541/3150 3227/3643/3160 3226/3641/3233 -f 3228/3644/3234 3217/3631/3225 3216/3630/3224 -f 3216/3630/3224 3226/3641/3233 3228/3644/3234 -f 3229/3645/3235 3218/3632/3226 3217/3631/3225 -f 3217/3631/3225 3228/3644/3234 3229/3645/3235 -f 3230/3646/3236 3219/3633/3227 3218/3632/3226 -f 3218/3632/3226 3229/3645/3235 3230/3646/3236 -f 3231/3647/3237 3220/3634/3228 3219/3633/3227 -f 3219/3633/3227 3230/3646/3236 3231/3647/3237 -f 3232/3648/3238 3221/3635/3229 3220/3634/3228 -f 3220/3634/3228 3231/3647/3237 3232/3648/3238 -f 3233/3649/3239 3222/3636/3230 3221/3635/3229 -f 3221/3635/3229 3232/3648/3238 3233/3649/3239 -f 3234/3650/3240 3223/3637/3231 3222/3636/3230 -f 3222/3636/3230 3233/3649/3239 3234/3650/3240 -f 3235/3651/3241 3224/3638/3232 3223/3637/3231 -f 3223/3637/3231 3234/3650/3240 3235/3651/3241 -f 2948/3328/2971 3225/3640/2960 3224/3638/3232 -f 3224/3638/3232 3235/3651/3241 2948/3328/2971 -f 3236/3653/949 3226/3641/3233 3227/3643/3160 -f 3227/3643/3160 926/1057/950 3236/3653/949 -f 3237/3655/948 3228/3644/3234 3226/3641/3233 -f 3226/3641/3233 3236/3653/949 3237/3655/948 -f 3238/3657/947 3229/3645/3235 3228/3644/3234 -f 3228/3644/3234 3237/3655/948 3238/3657/947 -f 925/1053/946 3230/3646/3236 3229/3645/3235 -f 3229/3645/3235 3238/3657/947 925/1053/946 -f 924/1051/945 3231/3647/3237 3230/3646/3236 -f 3230/3646/3236 925/1053/946 924/1051/945 -f 3239/3659/944 3232/3648/3238 3231/3647/3237 -f 3231/3647/3237 924/1051/945 3239/3659/944 -f 3240/3661/943 3233/3649/3239 3232/3648/3238 -f 3232/3648/3238 3239/3659/944 3240/3661/943 -f 923/1049/942 3234/3650/3240 3233/3649/3239 -f 3233/3649/3239 3240/3661/943 923/1049/942 -f 3241/3663/940 3235/3651/3241 3234/3650/3240 -f 3234/3650/3240 923/1049/942 3241/3663/940 -f 922/1047/941 2948/3328/2971 3235/3651/3241 -f 3235/3651/3241 3241/3663/940 922/1047/941 diff --git a/Base/home/anon/Documents/3D Models/tuba.obj b/Base/home/anon/Documents/3D Models/tuba.obj deleted file mode 100644 index b994db39b2c..00000000000 --- a/Base/home/anon/Documents/3D Models/tuba.obj +++ /dev/null @@ -1,36421 +0,0 @@ -# Blender v3.0.1 OBJ File: 'tubamesh.blend' -# www.blender.org -o Cube.003_Cube -v 0.290317 -0.645140 0.183919 -v 0.273365 -0.745061 0.163782 -v 0.280679 -0.650382 0.164041 -v 0.388786 -0.758644 0.153591 -v 0.401417 -0.663405 0.154539 -v 0.395488 -0.662726 0.132447 -v 0.269640 -0.684065 0.165444 -v 0.268972 -0.724157 0.164271 -v 0.409796 -0.732438 0.126306 -v 0.428245 -0.692112 0.146382 -v 0.416192 -0.732833 0.148621 -v 0.383109 -0.757838 0.134117 -v 0.430611 -0.737673 0.140640 -v 0.268680 -0.683612 0.169411 -v 0.579221 -0.717040 0.099012 -v 0.278905 -0.746766 0.161055 -v 0.296612 -0.644538 0.183327 -v 0.274015 -0.180405 0.185223 -v 0.272467 -0.179121 0.162954 -v 0.386558 -0.173189 0.155845 -v 0.380626 -0.173891 0.133754 -v 0.413279 -0.195918 0.125710 -v 0.409868 -0.238850 0.127657 -v 0.419173 -0.195265 0.147662 -v 0.415992 -0.237713 0.150069 -v 0.569474 -0.190052 0.089318 -v 0.390246 -0.269025 0.135304 -v 0.269180 -0.260781 0.169600 -v 0.572063 -0.186165 0.100264 -v 0.285663 -0.281778 0.162356 -v 0.280427 -0.178158 0.184569 -v 0.302505 -0.334804 0.183320 -v 0.413119 -0.436379 0.153082 -v 0.415216 -0.340034 0.153910 -v 0.409273 -0.340042 0.131814 -v 0.439225 -0.365525 0.123791 -v 0.431254 -0.407754 0.125703 -v 0.445155 -0.365525 0.145742 -v 0.437988 -0.407265 0.147927 -v 0.408287 -0.435610 0.133359 -v 0.452668 -0.410524 0.140009 -v 0.288860 -0.414128 0.167680 -v 0.287360 -0.364831 0.164920 -v 0.598101 -0.373354 0.098359 -v 0.302927 -0.436807 0.160437 -v 0.309141 -0.333240 0.182649 -v 0.293032 -0.493032 0.185879 -v 0.403646 -0.594607 0.155641 -v 0.405743 -0.498262 0.156469 -v 0.399800 -0.498270 0.134373 -v 0.429752 -0.523753 0.126350 -v 0.421781 -0.565981 0.128262 -v 0.435682 -0.523753 0.148301 -v 0.428515 -0.565493 0.150486 -v 0.398814 -0.593838 0.135918 -v 0.443195 -0.568751 0.142568 -v 0.279387 -0.572356 0.170239 -v 0.277887 -0.523059 0.167479 -v 0.588628 -0.531581 0.100918 -v 0.293454 -0.595035 0.162996 -v 0.299668 -0.491468 0.185208 -v 0.284174 -0.656076 0.185960 -v 0.275025 -0.724850 0.186821 -v 0.275631 -0.684750 0.187768 -v 0.400608 -0.708354 0.128157 -v 0.407344 -0.709339 0.153049 -v 0.283226 -0.731279 0.185760 -v 0.283178 -0.683709 0.187170 -v 0.435642 -0.687088 0.140991 -v 0.377372 -0.743704 0.133300 -v 0.577330 -0.735768 0.099343 -v 0.486485 -0.732215 0.126987 -v 0.427402 -0.694480 0.123297 -v 0.278600 -0.745887 0.183788 -v 0.285792 -0.643238 0.166073 -v 0.395394 -0.655958 0.135920 -v 0.399726 -0.656237 0.151751 -v 0.426249 -0.737545 0.125823 -v 0.431323 -0.685356 0.126688 -v 0.272010 -0.724508 0.184373 -v 0.272549 -0.684912 0.184749 -v 0.573679 -0.740395 0.088399 -v 0.576712 -0.741627 0.097680 -v 0.577390 -0.715928 0.089416 -v 0.570143 -0.709290 0.091548 -v 0.576934 -0.724200 0.087760 -v 0.579820 -0.729079 0.097175 -v 0.267251 -0.721784 0.168341 -v 0.568144 -0.746475 0.099835 -v 0.396834 -0.747352 0.132638 -v 0.401431 -0.747496 0.148937 -v 0.280836 -0.752782 0.163516 -v 0.405476 -0.669792 0.132658 -v 0.409862 -0.670492 0.149086 -v 0.290642 -0.643855 0.161086 -v 0.284886 -0.747450 0.183337 -v 0.276488 -0.682944 0.162245 -v 0.294196 -0.638228 0.163817 -v 0.297729 -0.756072 0.167671 -v 0.276505 -0.730511 0.160717 -v 0.298010 -0.638895 0.179732 -v 0.284939 -0.753308 0.179919 -v 0.291898 -0.658307 0.185544 -v 0.285241 -0.657546 0.160741 -v 0.383200 -0.654412 0.135850 -v 0.377629 -0.758153 0.158171 -v 0.381245 -0.647979 0.140083 -v 0.386252 -0.648758 0.155814 -v 0.371622 -0.757466 0.135788 -v 0.389204 -0.655099 0.158222 -v 0.395620 -0.670946 0.157403 -v 0.369476 -0.763023 0.139597 -v 0.372535 -0.763623 0.155854 -v 0.384063 -0.744469 0.158232 -v 0.388920 -0.670180 0.132441 -v 0.484708 -0.731834 0.109076 -v 0.568994 -0.740831 0.101651 -v 0.492361 -0.705492 0.126142 -v 0.568762 -0.714872 0.089877 -v 0.487099 -0.698325 0.112090 -v 0.490511 -0.698929 0.124252 -v 0.485810 -0.738525 0.124259 -v 0.482719 -0.737958 0.112010 -v 0.567285 -0.727547 0.089271 -v 0.570724 -0.728185 0.102187 -v 0.572181 -0.709666 0.099808 -v 0.565802 -0.746064 0.091658 -v 0.270382 -0.192606 0.187270 -v 0.278669 -0.279698 0.164747 -v 0.277157 -0.261492 0.188084 -v 0.267571 -0.216053 0.188920 -v 0.264444 -0.199783 0.165467 -v 0.263044 -0.234803 0.167181 -v 0.396401 -0.217786 0.129339 -v 0.402800 -0.216176 0.154243 -v 0.280078 -0.245489 0.188220 -v 0.425268 -0.188722 0.142270 -v 0.380982 -0.256833 0.134592 -v 0.574359 -0.205882 0.100582 -v 0.484873 -0.221414 0.128267 -v 0.477230 -0.194698 0.111036 -v 0.285305 -0.281056 0.185057 -v 0.269395 -0.179308 0.167283 -v 0.394859 -0.269813 0.153030 -v 0.379065 -0.167238 0.137231 -v 0.383440 -0.166669 0.153029 -v 0.426896 -0.239916 0.127090 -v 0.431571 -0.239437 0.141818 -v 0.420557 -0.187994 0.127945 -v 0.571801 -0.209798 0.089639 -v 0.574917 -0.210563 0.098870 -v 0.569905 -0.185280 0.090797 -v 0.561045 -0.180513 0.092877 -v 0.572881 -0.197788 0.090228 -v 0.575181 -0.197678 0.098455 -v 0.266902 -0.231829 0.186339 -v 0.273571 -0.260759 0.185525 -v 0.567351 -0.217194 0.101222 -v 0.400444 -0.256305 0.133940 -v 0.405421 -0.255421 0.150119 -v 0.391996 -0.178777 0.133770 -v 0.396442 -0.178104 0.150190 -v 0.262813 -0.202712 0.169499 -v 0.291709 -0.281096 0.184616 -v 0.269332 -0.217676 0.163412 -v 0.276365 -0.172559 0.165135 -v 0.280496 -0.172364 0.180998 -v 0.289056 -0.287514 0.164997 -v 0.293940 -0.287170 0.180723 -v 0.279246 -0.266067 0.162121 -v 0.287131 -0.268231 0.186741 -v 0.277698 -0.193910 0.187167 -v 0.272058 -0.193346 0.162022 -v 0.366869 -0.168526 0.137131 -v 0.387902 -0.270407 0.158469 -v 0.378505 -0.271572 0.137066 -v 0.372895 -0.167859 0.159497 -v 0.382713 -0.181940 0.158671 -v 0.377344 -0.277469 0.140925 -v 0.381335 -0.277294 0.156979 -v 0.368249 -0.162201 0.157064 -v 0.387883 -0.256193 0.159469 -v 0.363637 -0.162823 0.141236 -v 0.375898 -0.182554 0.133736 -v 0.483058 -0.221436 0.110355 -v 0.567744 -0.211482 0.102752 -v 0.484659 -0.194053 0.127422 -v 0.561751 -0.186082 0.091054 -v 0.477902 -0.188245 0.113366 -v 0.481581 -0.188050 0.125467 -v 0.486401 -0.227572 0.125321 -v 0.480482 -0.228189 0.113793 -v 0.562311 -0.198731 0.090603 -v 0.565861 -0.198980 0.103563 -v 0.563662 -0.180295 0.100964 -v 0.565607 -0.217262 0.092892 -v 0.297568 -0.346486 0.185354 -v 0.296174 -0.433898 0.162835 -v 0.296678 -0.415583 0.186173 -v 0.292188 -0.369480 0.187001 -v 0.292290 -0.345709 0.163660 -v 0.285625 -0.387706 0.165255 -v 0.419992 -0.385381 0.127431 -v 0.426520 -0.384523 0.152341 -v 0.301373 -0.400097 0.186301 -v 0.451934 -0.359693 0.140351 -v 0.400433 -0.422532 0.132669 -v 0.598233 -0.393073 0.098669 -v 0.507575 -0.398751 0.126347 -v 0.502921 -0.371356 0.109117 -v 0.302669 -0.436093 0.183123 -v 0.297412 -0.333905 0.163161 -v 0.408325 -0.333204 0.135368 -v 0.412789 -0.333168 0.151114 -v 0.448342 -0.410892 0.125188 -v 0.447390 -0.358457 0.126036 -v 0.595236 -0.396754 0.087715 -v 0.598266 -0.397885 0.096949 -v 0.596025 -0.372196 0.088787 -v 0.587797 -0.366488 0.090952 -v 0.596434 -0.379976 0.087147 -v 0.599949 -0.385106 0.096536 -v 0.289783 -0.385096 0.184420 -v 0.293225 -0.414581 0.183607 -v 0.590039 -0.403623 0.099299 -v 0.420051 -0.423844 0.131922 -v 0.424723 -0.423888 0.148193 -v 0.419941 -0.346125 0.131883 -v 0.424442 -0.345958 0.148285 -v 0.302966 -0.333216 0.160444 -v 0.309013 -0.436795 0.182697 -v 0.293749 -0.371375 0.161495 -v 0.305716 -0.327226 0.163222 -v 0.309848 -0.327491 0.179083 -v 0.305789 -0.442958 0.163091 -v 0.310500 -0.443034 0.178843 -v 0.298298 -0.420527 0.160198 -v 0.305882 -0.423507 0.184821 -v 0.304717 -0.348545 0.185240 -v 0.299148 -0.347423 0.160102 -v 0.396114 -0.333188 0.135212 -v 0.402535 -0.436902 0.157484 -v 0.398183 -0.327056 0.155151 -v 0.396326 -0.436889 0.135148 -v 0.402168 -0.333188 0.157580 -v 0.410364 -0.348239 0.156756 -v 0.394004 -0.442858 0.139283 -v 0.407270 -0.422600 0.157572 -v 0.393530 -0.327161 0.139321 -v 0.398892 -0.442733 0.155037 -v 0.403566 -0.348164 0.131810 -v 0.505771 -0.398574 0.108435 -v 0.591047 -0.398005 0.100829 -v 0.510380 -0.371534 0.125501 -v 0.587383 -0.372124 0.089246 -v 0.504318 -0.365014 0.111448 -v 0.507891 -0.365226 0.123578 -v 0.507609 -0.405094 0.123630 -v 0.504587 -0.404882 0.111351 -v 0.587053 -0.384733 0.088681 -v 0.590773 -0.385207 0.101561 -v 0.590416 -0.366564 0.099050 -v 0.588238 -0.403543 0.090988 -v 0.288095 -0.504714 0.187913 -v 0.286701 -0.592126 0.165394 -v 0.287205 -0.573811 0.188732 -v 0.282715 -0.527708 0.189560 -v 0.282817 -0.503937 0.166219 -v 0.276152 -0.545933 0.167814 -v 0.410519 -0.543609 0.129990 -v 0.417047 -0.542750 0.154900 -v 0.291900 -0.558325 0.188860 -v 0.442461 -0.517921 0.142910 -v 0.390960 -0.580760 0.135228 -v 0.588760 -0.551301 0.101228 -v 0.498102 -0.556979 0.128906 -v 0.493448 -0.529584 0.111676 -v 0.293196 -0.594320 0.185682 -v 0.287939 -0.492133 0.165720 -v 0.398852 -0.491432 0.137927 -v 0.403316 -0.491395 0.153673 -v 0.438869 -0.569120 0.127747 -v 0.437917 -0.516685 0.128595 -v 0.585763 -0.554982 0.090274 -v 0.588793 -0.556113 0.099508 -v 0.586552 -0.530424 0.091346 -v 0.578324 -0.524716 0.093511 -v 0.586961 -0.538204 0.089706 -v 0.590476 -0.543334 0.099095 -v 0.280310 -0.543324 0.186979 -v 0.283752 -0.572809 0.186166 -v 0.580566 -0.561851 0.101858 -v 0.410578 -0.582072 0.134481 -v 0.415250 -0.582116 0.150752 -v 0.388913 -0.485368 0.157707 -v 0.410468 -0.504353 0.134442 -v 0.414969 -0.504186 0.150844 -v 0.293493 -0.491444 0.163003 -v 0.299540 -0.595023 0.185256 -v 0.284276 -0.529603 0.164054 -v 0.296243 -0.485454 0.165781 -v 0.300375 -0.485719 0.181642 -v 0.296499 -0.601310 0.165679 -v 0.300838 -0.601064 0.181574 -v 0.288826 -0.578755 0.162757 -v 0.296409 -0.581735 0.187380 -v 0.295244 -0.506773 0.187799 -v 0.289675 -0.505651 0.162661 -v 0.386641 -0.491415 0.137771 -v 0.393062 -0.595130 0.160043 -v 0.383876 -0.485296 0.141986 -v 0.386853 -0.595117 0.137707 -v 0.392696 -0.491416 0.160139 -v 0.400891 -0.506467 0.159315 -v 0.384531 -0.601086 0.141842 -v 0.397797 -0.580828 0.160131 -v 0.389419 -0.600961 0.157596 -v 0.394093 -0.506391 0.134369 -v 0.496298 -0.556801 0.110994 -v 0.581574 -0.556233 0.103388 -v 0.500907 -0.529761 0.128060 -v 0.577910 -0.530352 0.091805 -v 0.494845 -0.523241 0.114007 -v 0.498418 -0.523453 0.126137 -v 0.498136 -0.563321 0.126189 -v 0.495114 -0.563109 0.113910 -v 0.577580 -0.542961 0.091240 -v 0.581300 -0.543435 0.104120 -v 0.580943 -0.524792 0.101609 -v 0.578765 -0.561771 0.093547 -v -0.428680 2.381835 -0.900214 -v -0.305157 2.383952 -0.913043 -v -0.183907 2.385983 -0.909286 -v -0.066189 2.387909 -0.889835 -v 0.046738 2.389711 -0.855577 -v 0.153615 2.391371 -0.807402 -v 0.253182 2.392870 -0.746198 -v 0.344181 2.394189 -0.672855 -v 0.425353 2.395310 -0.588261 -v 0.495438 2.396215 -0.493305 -v 0.553178 2.396884 -0.388877 -v 0.597313 2.397300 -0.275865 -v 0.626585 2.397442 -0.155158 -v 0.639417 2.397300 -0.031618 -v 0.635668 2.396884 0.089649 -v 0.616225 2.396215 0.207382 -v 0.581977 2.395311 0.320323 -v 0.533814 2.394189 0.427212 -v 0.472623 2.392870 0.526790 -v 0.399295 2.391371 0.617798 -v 0.314716 2.389711 0.698977 -v 0.219777 2.387909 0.769067 -v 0.115366 2.385983 0.826810 -v 0.002372 2.383953 0.870946 -v -0.118317 2.381835 0.900215 -v -0.241840 2.379717 0.913043 -v -0.363090 2.377687 0.909287 -v -0.480808 2.375761 0.889836 -v -0.593958 2.373469 0.855596 -v -0.701017 2.371577 0.807417 -v -0.800179 2.370800 0.746199 -v -0.891178 2.369480 0.672856 -v -0.972350 2.368359 0.588262 -v -1.042435 2.367454 0.493306 -v -1.100175 2.366785 0.388878 -v -1.144310 2.366370 0.275866 -v -1.173582 2.366227 0.155159 -v -1.186414 2.366370 0.031619 -v -1.182665 2.366785 -0.089648 -v -1.163222 2.367454 -0.207381 -v -1.128974 2.368359 -0.320322 -v -1.081277 2.368615 -0.427000 -v -1.019941 2.370131 -0.526659 -v -0.946458 2.371869 -0.617746 -v -0.861738 2.373809 -0.698983 -v -0.766774 2.375761 -0.769066 -v -0.662363 2.377687 -0.826809 -v -0.549369 2.379717 -0.870945 -v -0.414836 2.343231 -0.819019 -v -0.301155 2.348427 -0.832787 -v -0.188240 2.366368 -0.860412 -v -0.026658 2.370997 -0.830241 -v 0.176253 2.376631 -0.740110 -v 0.349176 2.380973 -0.601935 -v 0.482470 2.383825 -0.422724 -v 0.570095 2.385767 -0.205257 -v 0.592036 2.384270 0.032155 -v 0.554460 2.381786 0.254536 -v 0.462507 2.377752 0.456452 -v 0.322751 2.372379 0.628093 -v 0.193709 2.367917 0.727547 -v 0.081922 2.349665 0.753285 -v -0.026664 2.344952 0.794615 -v -0.136432 2.334119 0.808372 -v -0.249322 2.327847 0.816025 -v -0.356137 2.302170 0.774225 -v -0.469377 2.329074 0.808762 -v -0.771009 2.338786 0.705851 -v -0.857086 2.336614 0.636453 -v -0.933031 2.334609 0.557026 -v -0.998122 2.332164 0.466494 -v -1.051776 2.330395 0.367423 -v -1.096538 2.331724 0.260836 -v -1.120958 2.329285 0.145193 -v -1.133715 2.330165 0.028678 -v -1.131253 2.331798 -0.085866 -v -1.114338 2.334112 -0.197349 -v -1.082078 2.335728 -0.303233 -v -0.699276 2.301498 -0.652708 -v -0.624493 2.326676 -0.737532 -v -0.529648 2.337727 -0.788771 -v -0.192200 2.341880 -0.811626 -v -0.087214 2.345550 -0.794259 -v 0.013501 2.348961 -0.763682 -v 0.108822 2.352080 -0.720687 -v 0.197626 2.354872 -0.666068 -v 0.278790 2.357302 -0.600618 -v 0.351190 2.359336 -0.525131 -v 0.413705 2.360940 -0.440399 -v 0.465211 2.362078 -0.347217 -v 0.504585 2.362717 -0.246377 -v 0.530704 2.362822 -0.138672 -v 0.542163 2.362376 -0.028441 -v 0.538833 2.361409 0.079760 -v 0.521507 2.359960 0.184807 -v 0.490977 2.358065 0.285577 -v 0.448036 2.355762 0.380945 -v 0.393477 2.353090 0.469789 -v 0.328091 2.350086 0.550985 -v 0.252671 2.346786 0.623410 -v 0.168010 2.343228 0.685940 -v -0.541227 2.290240 0.721338 -v -0.630520 2.285973 0.678749 -v -0.713501 2.282273 0.625648 -v -0.789271 2.279229 0.562856 -v -0.999430 2.273438 0.230292 -v -0.988427 2.278642 -0.268355 -v -0.949189 2.281634 -0.358959 -v -0.899203 2.285281 -0.443887 -v -0.839029 2.289482 -0.522076 -v -0.769204 2.294106 -0.592394 -v -0.400975 2.302420 -0.755321 -v -0.297459 2.306087 -0.766067 -v -0.195846 2.309586 -0.762908 -v -0.097192 2.312884 -0.746591 -v -0.002551 2.315951 -0.717860 -v 0.087021 2.318756 -0.677462 -v 0.170468 2.321267 -0.626140 -v 0.246736 2.323454 -0.564642 -v 0.314770 2.325285 -0.493711 -v 0.373514 2.326731 -0.414095 -v 0.421912 2.327759 -0.326537 -v 0.458910 2.328339 -0.231783 -v 0.483452 2.328439 -0.130580 -v 0.494219 2.328044 -0.027002 -v 0.491088 2.327182 0.074668 -v 0.474806 2.325885 0.173375 -v 0.446116 2.324189 0.268063 -v 0.405763 2.322126 0.357675 -v 0.354493 2.319730 0.441158 -v 0.293049 2.317035 0.517454 -v 0.222177 2.314074 0.585508 -v 0.142621 2.310881 0.644265 -v 0.055125 2.307490 0.692669 -v -0.039564 2.303934 0.729664 -v -0.140704 2.300247 0.754194 -v -0.244220 2.296580 0.764940 -v -0.444487 2.289783 0.745464 -v -0.856449 2.277382 0.492584 -v -0.915192 2.275936 0.412968 -v -0.963591 2.274908 0.325410 -v -1.025132 2.274228 0.129453 -v -1.035898 2.274623 0.025875 -v -1.032767 2.275486 -0.075795 -v -1.016485 2.276782 -0.174502 -v -0.596804 2.295177 -0.693796 -v -0.502114 2.298733 -0.730790 -v -0.391300 2.261786 -0.707116 -v -0.294417 2.264678 -0.717175 -v -0.199315 2.267440 -0.714222 -v -0.106982 2.270047 -0.698957 -v -0.018407 2.272473 -0.672075 -v 0.065424 2.274695 -0.634274 -v 0.143523 2.276688 -0.586251 -v 0.214901 2.278426 -0.528705 -v 0.278573 2.279886 -0.462333 -v 0.333549 2.281043 -0.387832 -v 0.378843 2.281872 -0.305900 -v 0.413466 2.282348 -0.217233 -v 0.436432 2.282447 -0.122531 -v 0.446504 2.282156 -0.025606 -v 0.443570 2.281496 0.069535 -v 0.428327 2.280492 0.161902 -v 0.401471 2.279172 0.250509 -v 0.363701 2.277562 0.334367 -v 0.315712 2.275687 0.412489 -v 0.258202 2.273575 0.483887 -v 0.191868 2.271252 0.547573 -v 0.117407 2.268744 0.602559 -v 0.035516 2.266076 0.647856 -v -0.053108 2.263277 0.682479 -v -0.147768 2.260371 0.705437 -v -0.244651 2.257479 0.715496 -v -0.339753 2.254717 0.712544 -v -0.432086 2.252111 0.697278 -v -0.520661 2.249684 0.670396 -v -0.604492 2.247463 0.632595 -v -0.682591 2.245470 0.584573 -v -0.753969 2.243731 0.527027 -v -0.817641 2.242271 0.460654 -v -0.872617 2.241115 0.386153 -v -0.917911 2.240285 0.304221 -v -0.952534 2.239809 0.215555 -v -0.975500 2.239710 0.120852 -v -0.985572 2.240001 0.023927 -v -0.982638 2.240662 -0.071213 -v -0.967394 2.241665 -0.163581 -v -0.940539 2.242985 -0.252187 -v -0.902769 2.244596 -0.336046 -v -0.854780 2.246470 -0.414168 -v -0.797270 2.248582 -0.485566 -v -0.730936 2.250906 -0.549251 -v -0.656475 2.253414 -0.604237 -v -0.574584 2.256081 -0.649535 -v -0.485960 2.258880 -0.684157 -v -0.381565 2.208848 -0.658908 -v -0.291317 2.210951 -0.668280 -v -0.202729 2.212963 -0.665533 -v -0.116721 2.214865 -0.651318 -v -0.034213 2.216639 -0.626284 -v 0.043874 2.218267 -0.591080 -v 0.116622 2.219732 -0.546357 -v 0.183110 2.221014 -0.492763 -v 0.242417 2.222095 -0.430948 -v 0.293625 2.222959 -0.361563 -v 0.335814 2.223585 -0.285255 -v 0.368062 2.223956 -0.202676 -v 0.389451 2.224055 -0.114475 -v 0.398830 2.223869 -0.024203 -v 0.396093 2.223415 0.064407 -v 0.381890 2.222710 0.150435 -v 0.356870 2.221773 0.232961 -v 0.321683 2.220623 0.311065 -v 0.276978 2.219280 0.383826 -v 0.223405 2.217762 0.450325 -v 0.161612 2.216087 0.509641 -v 0.092249 2.214276 0.560855 -v 0.015965 2.212347 0.603046 -v -0.066590 2.210318 0.635295 -v -0.154768 2.208208 0.656680 -v -0.245016 2.206105 0.666052 -v -0.333604 2.204093 0.663305 -v -0.419612 2.202190 0.649090 -v -0.502119 2.200416 0.624056 -v -0.580207 2.198788 0.588852 -v -0.652955 2.197323 0.544129 -v -0.719442 2.196042 0.490535 -v -0.778750 2.194960 0.428720 -v -0.829958 2.194097 0.359334 -v -0.872146 2.193470 0.283027 -v -0.904395 2.193099 0.200448 -v -0.925784 2.193001 0.112247 -v -0.935162 2.193186 0.021975 -v -0.932425 2.193641 -0.066635 -v -0.918222 2.194345 -0.152663 -v -0.893203 2.195282 -0.235189 -v -0.858016 2.196432 -0.313293 -v -0.813311 2.197775 -0.386054 -v -0.759737 2.199294 -0.452553 -v -0.697944 2.200968 -0.511869 -v -0.628581 2.202780 -0.563083 -v -0.552297 2.204709 -0.605274 -v -0.469742 2.206738 -0.637523 -v -0.371859 2.140331 -0.610673 -v -0.288253 2.141767 -0.619355 -v -0.206185 2.143145 -0.616813 -v -0.126507 2.144450 -0.603647 -v -0.050072 2.145672 -0.580460 -v 0.022267 2.146798 -0.547852 -v 0.089660 2.147814 -0.506426 -v 0.151252 2.148709 -0.456784 -v 0.206194 2.149469 -0.399526 -v 0.253631 2.150083 -0.335255 -v 0.292712 2.150536 -0.264573 -v 0.322585 2.150817 -0.188081 -v 0.342398 2.150914 -0.106380 -v 0.351083 2.150817 -0.022762 -v 0.348545 2.150536 0.059318 -v 0.335385 2.150081 0.139006 -v 0.312205 2.149468 0.215450 -v 0.279605 2.148707 0.287798 -v 0.238188 2.147812 0.355197 -v 0.188556 2.146796 0.416796 -v 0.131309 2.145670 0.471742 -v 0.067049 2.144448 0.519183 -v -0.003622 2.143142 0.558266 -v -0.080102 2.141764 0.588139 -v -0.161790 2.140328 0.607951 -v -0.245397 2.138893 0.616634 -v -0.327465 2.137516 0.614091 -v -0.407142 2.136209 0.600925 -v -0.483577 2.134987 0.577738 -v -0.555917 2.133862 0.545130 -v -0.623309 2.132845 0.503704 -v -0.684902 2.131951 0.454062 -v -0.739843 2.131190 0.396804 -v -0.787281 2.130577 0.332534 -v -0.826362 2.130124 0.261851 -v -0.856235 2.129842 0.185359 -v -0.876047 2.129746 0.103658 -v -0.884733 2.129843 0.020040 -v -0.882195 2.130124 -0.062040 -v -0.869035 2.130579 -0.141728 -v -0.845855 2.131192 -0.218172 -v -0.813255 2.131953 -0.290520 -v -0.771838 2.132848 -0.357919 -v -0.722205 2.133864 -0.419518 -v -0.664958 2.134990 -0.474464 -v -0.600699 2.136212 -0.521905 -v -0.530028 2.137518 -0.560988 -v -0.453548 2.138896 -0.590861 -v -0.362271 2.052901 -0.562381 -v -0.285312 2.053815 -0.570374 -v -0.209768 2.054696 -0.568035 -v -0.136426 2.055535 -0.555917 -v -0.066068 2.056325 -0.534576 -v 0.000520 2.057056 -0.504564 -v 0.062553 2.057722 -0.466435 -v 0.119249 2.058312 -0.420743 -v 0.169821 2.058820 -0.368042 -v 0.213486 2.059237 -0.308885 -v 0.249458 2.059554 -0.243828 -v 0.276955 2.059762 -0.173422 -v 0.295191 2.059855 -0.098222 -v 0.303185 2.059826 -0.021257 -v 0.300847 2.059677 0.054291 -v 0.288732 2.059418 0.127638 -v 0.267393 2.059055 0.198000 -v 0.237384 2.058597 0.264592 -v 0.199259 2.058051 0.326629 -v 0.153571 2.057425 0.383327 -v 0.100875 2.056727 0.433902 -v 0.041723 2.055964 0.477568 -v -0.023330 2.055145 0.513542 -v -0.093730 2.054276 0.541040 -v -0.168924 2.053367 0.559275 -v -0.245884 2.052453 0.567268 -v -0.321427 2.051572 0.564929 -v -0.394770 2.050733 0.552812 -v -0.465127 2.049942 0.531470 -v -0.531715 2.049211 0.501458 -v -0.593749 2.048546 0.463329 -v -0.650444 2.047956 0.417637 -v -0.701016 2.047448 0.364936 -v -0.744681 2.047031 0.305780 -v -0.780654 2.046714 0.240722 -v -0.808151 2.046505 0.170316 -v -0.826386 2.046412 0.095117 -v -0.834380 2.046442 0.018152 -v -0.832043 2.046590 -0.057397 -v -0.819928 2.046850 -0.130744 -v -0.798589 2.047213 -0.201106 -v -0.768580 2.047671 -0.267698 -v -0.730454 2.048216 -0.329735 -v -0.684767 2.048842 -0.386433 -v -0.632070 2.049541 -0.437008 -v -0.572919 2.050303 -0.480674 -v -0.507866 2.051123 -0.516648 -v -0.437465 2.051991 -0.544146 -v -0.352888 1.943196 -0.514008 -v -0.282578 1.943719 -0.521310 -v -0.213563 1.944227 -0.519173 -v -0.146558 1.944716 -0.508104 -v -0.082281 1.945181 -0.488607 -v -0.021447 1.945616 -0.461190 -v 0.035225 1.946017 -0.426357 -v 0.087021 1.946378 -0.384616 -v 0.133223 1.946695 -0.336471 -v 0.173114 1.946962 -0.282429 -v 0.205977 1.947175 -0.222995 -v 0.231097 1.947327 -0.158676 -v 0.247757 1.947415 -0.089978 -v 0.255059 1.947434 -0.019666 -v 0.252923 1.947383 0.049351 -v 0.241854 1.947267 0.116357 -v 0.222358 1.947091 0.180637 -v 0.194942 1.946857 0.241472 -v 0.160110 1.946571 0.298146 -v 0.118370 1.946237 0.349943 -v 0.070227 1.945858 0.396145 -v 0.016186 1.945439 0.436037 -v -0.043246 1.944984 0.468902 -v -0.107563 1.944496 0.494022 -v -0.176260 1.943980 0.510682 -v -0.246569 1.943458 0.517984 -v -0.315584 1.942949 0.515847 -v -0.382589 1.942460 0.504777 -v -0.446867 1.941995 0.485281 -v -0.507700 1.941560 0.457864 -v -0.564373 1.941160 0.423031 -v -0.616169 1.940798 0.381290 -v -0.662370 1.940482 0.333145 -v -0.702261 1.940215 0.279103 -v -0.735125 1.940002 0.219669 -v -0.760245 1.939849 0.155350 -v -0.776904 1.939761 0.086652 -v -0.784206 1.939743 0.016340 -v -0.782070 1.939793 -0.052677 -v -0.771001 1.939909 -0.119684 -v -0.751505 1.940086 -0.183963 -v -0.724089 1.940319 -0.244798 -v -0.689258 1.940605 -0.301472 -v -0.647517 1.940939 -0.353269 -v -0.599374 1.941318 -0.399471 -v -0.545334 1.941738 -0.439363 -v -0.485902 1.942193 -0.472228 -v -0.421584 1.942681 -0.497348 -v -0.343796 1.807670 -0.465525 -v -0.280139 1.808037 -0.472136 -v -0.217654 1.808401 -0.470202 -v -0.156990 1.808758 -0.460180 -v -0.098794 1.809104 -0.442528 -v -0.043717 1.809435 -0.417706 -v -0.007247 1.737050 -0.366988 -v 0.037337 1.737388 -0.331302 -v 0.077139 1.737688 -0.290092 -v 0.111542 1.737959 -0.243797 -v 0.139928 1.738210 -0.192851 -v 0.161678 1.738443 -0.137691 -v 0.175412 1.734795 -0.077239 -v 0.182652 1.738515 -0.015682 -v 0.180614 1.738508 0.043575 -v 0.170912 1.738464 0.101081 -v 0.153980 1.738372 0.156216 -v 0.152193 1.810858 0.218462 -v 0.120657 1.810698 0.269773 -v 0.082866 1.810499 0.316668 -v 0.039277 1.810265 0.358499 -v -0.009650 1.809996 0.394616 -v -0.063458 1.809697 0.424370 -v -0.121690 1.809369 0.447113 -v -0.183886 1.809014 0.462196 -v -0.247543 1.808648 0.468807 -v -0.310028 1.808284 0.466873 -v -0.370692 1.807927 0.456851 -v -0.428888 1.807580 0.439200 -v -0.483965 1.807249 0.414377 -v -0.535275 1.806937 0.382841 -v -0.582169 1.806648 0.345049 -v -0.604780 1.737445 0.287538 -v -0.639341 1.737435 0.241153 -v -0.667087 1.736078 0.190656 -v -0.689027 1.734151 0.134215 -v -0.704658 1.737274 0.073155 -v -0.710739 1.737061 0.012593 -v -0.708670 1.736841 -0.046837 -v -0.698891 1.736610 -0.104499 -v -0.704697 1.805707 -0.166713 -v -0.679875 1.805827 -0.221791 -v -0.648339 1.805987 -0.273102 -v -0.610548 1.806185 -0.319997 -v -0.566960 1.806420 -0.361827 -v -0.518032 1.806688 -0.397944 -v -0.464224 1.806988 -0.427699 -v -0.405992 1.807316 -0.450442 -v -0.338420 1.718724 -0.437461 -v -0.278604 1.719137 -0.443673 -v -0.219889 1.719551 -0.441855 -v -0.162885 1.719962 -0.432438 -v -0.108201 1.720363 -0.415852 -v -0.056447 1.720752 -0.392526 -v 0.127640 1.722690 0.205262 -v 0.098007 1.722534 0.253477 -v 0.062496 1.722332 0.297543 -v 0.021537 1.722087 0.336850 -v -0.024438 1.721801 0.370788 -v -0.075000 1.721477 0.398748 -v -0.129718 1.721115 0.420119 -v -0.188162 1.720721 0.434292 -v -0.247978 1.720308 0.440504 -v -0.306693 1.719893 0.438687 -v -0.363698 1.719483 0.429270 -v -0.418381 1.719082 0.412683 -v -0.470135 1.718693 0.389358 -v -0.518350 1.718323 0.359724 -v -0.562414 1.717975 0.324213 -v -0.677548 1.716647 -0.156675 -v -0.654223 1.716755 -0.208430 -v -0.624589 1.716911 -0.256646 -v -0.589078 1.717112 -0.300712 -v -0.548120 1.717358 -0.340019 -v -0.502144 1.717644 -0.373957 -v -0.451582 1.717968 -0.401917 -v -0.396864 1.718329 -0.423288 -v -0.332567 1.587305 -0.409311 -v -0.276591 1.587726 -0.415125 -v -0.221646 1.588149 -0.413424 -v -0.168302 1.588569 -0.404611 -v -0.117129 1.588980 -0.389090 -v -0.068698 1.589378 -0.367262 -v -0.023580 1.589758 -0.339531 -v 0.017656 1.590115 -0.306300 -v 0.054438 1.590444 -0.267970 -v 0.086196 1.590741 -0.224946 -v 0.112359 1.591000 -0.177629 -v 0.132357 1.591217 -0.126423 -v 0.145619 1.591387 -0.071730 -v 0.151432 1.591501 -0.015753 -v 0.149731 1.591557 0.039194 -v 0.140918 1.591556 0.092540 -v 0.125396 1.591501 0.143714 -v 0.103569 1.591394 0.192147 -v 0.075838 1.591238 0.237267 -v 0.042607 1.591035 0.278504 -v 0.004278 1.590787 0.315287 -v -0.038745 1.590497 0.347046 -v -0.086061 1.590167 0.373211 -v -0.137266 1.589801 0.393210 -v -0.191957 1.589398 0.406473 -v -0.247933 1.588977 0.412287 -v -0.302878 1.588554 0.410586 -v -0.356222 1.588135 0.401773 -v -0.407395 1.587724 0.386252 -v -0.455826 1.587325 0.364424 -v -0.500944 1.586946 0.336693 -v -0.542180 1.586589 0.303461 -v -0.578962 1.586259 0.265132 -v -0.610720 1.585962 0.222107 -v -0.636883 1.585703 0.174791 -v -0.656881 1.585486 0.123584 -v -0.670143 1.585316 0.068892 -v -0.675956 1.585202 0.012915 -v -0.674255 1.585147 -0.042032 -v -0.665442 1.585147 -0.095378 -v -0.649920 1.585202 -0.146553 -v -0.628093 1.585309 -0.194985 -v -0.600362 1.585466 -0.240105 -v -0.567131 1.585668 -0.281342 -v -0.528802 1.585916 -0.318125 -v -0.485779 1.586206 -0.349885 -v -0.438463 1.586536 -0.376049 -v -0.387258 1.586903 -0.396048 -v -0.326382 1.405271 -0.381103 -v -0.274246 1.405627 -0.386517 -v -0.223070 1.405985 -0.384933 -v -0.173386 1.406340 -0.376725 -v -0.125723 1.406688 -0.362268 -v -0.080615 1.407025 -0.341938 -v -0.038591 1.407347 -0.316109 -v -0.000184 1.407650 -0.285158 -v 0.034074 1.407930 -0.249458 -v 0.063653 1.408182 -0.209385 -v 0.088022 1.408402 -0.165315 -v 0.106648 1.408587 -0.117621 -v 0.119001 1.408732 -0.066681 -v 0.124415 1.408830 -0.014544 -v 0.122830 1.408878 0.036633 -v 0.114622 1.408878 0.086319 -v 0.100165 1.408833 0.133982 -v 0.079835 1.408744 0.179092 -v 0.054007 1.408613 0.221117 -v 0.023055 1.408442 0.259525 -v -0.012644 1.408233 0.293784 -v -0.052716 1.407988 0.323365 -v -0.096786 1.407710 0.347734 -v -0.144478 1.407400 0.366361 -v -0.195417 1.407060 0.378714 -v -0.247553 1.406704 0.384129 -v -0.298729 1.406346 0.382545 -v -0.348413 1.405991 0.374337 -v -0.396076 1.405643 0.359880 -v -0.441184 1.405306 0.339550 -v -0.483208 1.404984 0.313721 -v -0.521615 1.404681 0.282769 -v -0.555873 1.404401 0.247069 -v -0.585452 1.404149 0.206997 -v -0.609821 1.403929 0.162926 -v -0.628447 1.403744 0.115233 -v -0.640799 1.403599 0.064292 -v -0.646213 1.403501 0.012156 -v -0.644629 1.403453 -0.039021 -v -0.636421 1.403453 -0.088707 -v -0.621964 1.403498 -0.136371 -v -0.601634 1.403587 -0.181481 -v -0.575805 1.403719 -0.223505 -v -0.544854 1.403889 -0.261913 -v -0.509155 1.404098 -0.296173 -v -0.469083 1.404343 -0.325753 -v -0.425013 1.404621 -0.350122 -v -0.377321 1.404931 -0.368749 -v -0.320009 1.164405 -0.352859 -v -0.271714 1.164680 -0.357875 -v -0.224307 1.164956 -0.356407 -v -0.178282 1.165230 -0.348804 -v -0.134130 1.165499 -0.335412 -v -0.092344 1.165760 -0.316579 -v -0.053415 1.166009 -0.292653 -v -0.017837 1.166243 -0.263981 -v 0.013898 1.166459 -0.230911 -v 0.041299 1.166654 -0.193790 -v 0.063872 1.166825 -0.152965 -v 0.081127 1.166968 -0.108785 -v 0.092570 1.167081 -0.061597 -v 0.097585 1.167157 -0.013301 -v 0.096117 1.167195 0.034107 -v 0.088513 1.167195 0.080133 -v 0.075122 1.167161 0.124286 -v 0.056289 1.167092 0.166073 -v 0.032363 1.166992 0.205001 -v 0.003691 1.166860 0.240580 -v -0.029379 1.166699 0.272316 -v -0.066499 1.166511 0.299718 -v -0.107323 1.166296 0.322292 -v -0.151502 1.166057 0.339547 -v -0.198690 1.165794 0.350990 -v -0.246986 1.165519 0.356006 -v -0.294392 1.165243 0.354538 -v -0.340418 1.164969 0.346935 -v -0.384569 1.164700 0.333543 -v -0.426356 1.164439 0.314710 -v -0.465284 1.164190 0.290784 -v -0.500862 1.163956 0.262112 -v -0.532597 1.163740 0.229042 -v -0.559998 1.163545 0.191921 -v -0.582572 1.163374 0.151096 -v -0.599826 1.163231 0.106916 -v -0.611269 1.163118 0.059728 -v -0.616284 1.163042 0.011432 -v -0.614816 1.163005 -0.035976 -v -0.607213 1.163004 -0.082002 -v -0.593821 1.163038 -0.126154 -v -0.574988 1.163107 -0.167941 -v -0.551062 1.163208 -0.206870 -v -0.522391 1.163339 -0.242449 -v -0.489321 1.163500 -0.274185 -v -0.452200 1.163688 -0.301587 -v -0.411376 1.163903 -0.324161 -v -0.367197 1.164142 -0.341416 -v -0.313595 0.856588 -0.324606 -v -0.269140 0.856785 -0.329223 -v -0.225503 0.856984 -0.327872 -v -0.183137 0.857181 -0.320873 -v -0.142495 0.857374 -0.308546 -v -0.104032 0.857561 -0.291211 -v -0.068199 0.857740 -0.269187 -v -0.035450 0.857909 -0.242795 -v -0.006237 0.858064 -0.212354 -v 0.029860 0.851186 -0.160290 -v 0.044912 0.713777 -0.095622 -v 0.055043 0.714646 -0.053763 -v 0.059466 0.715441 -0.010914 -v 0.056793 0.697276 0.032392 -v 0.062446 0.858595 0.073956 -v 0.050119 0.858570 0.114598 -v 0.032783 0.858521 0.153062 -v 0.010760 0.858449 0.188895 -v -0.015632 0.858355 0.221645 -v -0.046073 0.858240 0.250858 -v -0.080242 0.858104 0.276080 -v -0.117819 0.857950 0.296859 -v -0.158486 0.857779 0.312742 -v -0.201922 0.857590 0.323275 -v -0.246377 0.857393 0.327892 -v -0.290015 0.857194 0.326541 -v -0.332380 0.856997 0.319542 -v -0.373022 0.856804 0.307215 -v -0.411485 0.856617 0.289880 -v -0.447318 0.856438 0.267856 -v -0.480068 0.856270 0.241464 -v -0.509280 0.856114 0.211024 -v -0.547414 0.862721 0.157344 -v -0.571163 0.855748 0.098609 -v -0.581696 0.855667 0.055173 -v -0.586313 0.855612 0.010717 -v -0.584962 0.855585 -0.032921 -v -0.577963 0.855584 -0.075287 -v -0.565636 0.855608 -0.115929 -v -0.548300 0.855657 -0.154393 -v -0.526277 0.855729 -0.190226 -v -0.499885 0.855823 -0.222976 -v -0.469445 0.855939 -0.252188 -v -0.435276 0.856074 -0.277411 -v -0.397698 0.856228 -0.298190 -v -0.357031 0.856400 -0.314073 -v -0.307286 0.473738 -0.296369 -v -0.266671 0.473868 -0.300587 -v -0.226803 0.473999 -0.299353 -v -0.188097 0.474129 -0.292958 -v -0.150966 0.474257 -0.281696 -v -0.115825 0.474380 -0.265858 -v -0.083087 0.474498 -0.245737 -v -0.040208 0.465788 -0.209070 -v 0.006368 0.464715 -0.146191 -v 0.033077 0.501275 0.088368 -v 0.009172 0.475015 0.140035 -v -0.010949 0.474967 0.172773 -v -0.035061 0.474905 0.202694 -v -0.062872 0.474830 0.229383 -v -0.094090 0.474741 0.252426 -v -0.128421 0.474639 0.271410 -v -0.165575 0.474526 0.285921 -v -0.205259 0.474402 0.295545 -v -0.245874 0.474271 0.299763 -v -0.285742 0.474141 0.298528 -v -0.324448 0.474011 0.292134 -v -0.361579 0.473883 0.280872 -v -0.396720 0.473760 0.265034 -v -0.429458 0.473642 0.244913 -v -0.459378 0.473530 0.220801 -v -0.499709 0.481198 0.177333 -v -0.536064 0.464877 0.109098 -v -0.552228 0.473132 0.050602 -v -0.556588 0.465234 -0.010677 -v -0.543519 0.466065 -0.088689 -v -0.521717 0.473125 -0.140860 -v -0.501596 0.473172 -0.173598 -v -0.477484 0.473234 -0.203518 -v -0.449673 0.473310 -0.230207 -v -0.418455 0.473399 -0.253251 -v -0.384124 0.473501 -0.272235 -v -0.346970 0.473614 -0.286746 -v -0.301228 0.007785 -0.268173 -v -0.264453 0.007860 -0.271992 -v -0.228355 0.007935 -0.270875 -v -0.193308 0.008010 -0.265085 -v -0.159688 0.008084 -0.254887 -v -0.114529 -0.002903 -0.232927 -v -0.059505 -0.003642 -0.189052 -v -0.035245 -0.198606 -0.140291 -v -0.018689 -0.195399 -0.110568 -v -0.006023 -0.192603 -0.078360 -v 0.002381 -0.190326 -0.043918 -v 0.006060 -0.188675 -0.008646 -v 0.004966 -0.187712 0.025993 -v -0.002092 -0.214426 0.060284 -v -0.011465 -0.193306 0.093662 -v -0.025991 -0.217758 0.122083 -v -0.032910 0.008495 0.156610 -v -0.054742 0.008459 0.183702 -v -0.079923 0.008415 0.207867 -v -0.108189 0.008364 0.228732 -v -0.139275 0.008306 0.245921 -v -0.172916 0.008241 0.259059 -v -0.208847 0.008169 0.267773 -v -0.245622 0.008094 0.271592 -v -0.281720 0.008019 0.270474 -v -0.316767 0.007944 0.264684 -v -0.350387 0.007870 0.254487 -v -0.382205 0.007799 0.240147 -v -0.411848 0.007731 0.221928 -v -0.452636 0.016316 0.187997 -v -0.492976 -0.002562 0.131586 -v -0.519300 -0.003432 0.063913 -v -0.526882 -0.002549 -0.009503 -v -0.515056 -0.001514 -0.080122 -v -0.495384 0.007432 -0.127368 -v -0.477165 0.007460 -0.157010 -v -0.455333 0.007495 -0.184102 -v -0.430152 0.007539 -0.208267 -v -0.401886 0.007590 -0.229132 -v -0.370800 0.007648 -0.246321 -v -0.337159 0.007714 -0.259460 -v -0.295567 -0.549335 -0.240043 -v -0.262632 -0.549304 -0.243464 -v -0.230303 -0.549272 -0.242463 -v -0.198916 -0.549241 -0.237278 -v -0.168807 -0.549210 -0.228145 -v -0.128349 -0.563205 -0.208341 -v -0.079169 -0.565494 -0.169012 -v -0.055268 -0.549035 0.140381 -v -0.074820 -0.549050 0.164643 -v -0.097372 -0.549069 0.186285 -v -0.122686 -0.549090 0.204970 -v -0.150525 -0.549115 0.220365 -v -0.180653 -0.549142 0.232131 -v -0.212832 -0.549172 0.239935 -v -0.245767 -0.549204 0.243355 -v -0.278096 -0.549235 0.242354 -v -0.309483 -0.549267 0.237169 -v -0.339592 -0.549298 0.228036 -v -0.381079 -0.520048 0.209978 -v -0.429504 -0.559612 0.169022 -v -0.467273 -0.560547 0.117984 -v -0.490850 -0.561590 0.057365 -v -0.497637 -0.560708 -0.008395 -v -0.487033 -0.559703 -0.071647 -v -0.460681 -0.559632 -0.129165 -v -0.433579 -0.549457 -0.164751 -v -0.411027 -0.549439 -0.186393 -v -0.385713 -0.549417 -0.205079 -v -0.357874 -0.549393 -0.220473 -v -0.327746 -0.549366 -0.232240 -v -0.290427 -1.206953 -0.212005 -v -0.251862 -1.396574 -0.206613 -v -0.229155 -1.343520 -0.208146 -v -0.205098 -1.204015 -0.209562 -v -0.178515 -1.203100 -0.201495 -v -0.142238 -1.203687 -0.184284 -v -0.098680 -1.201061 -0.149489 -v -0.065324 -1.199632 -0.104386 -v -0.044484 -1.198897 -0.050844 -v -0.038512 -1.198682 0.007251 -v -0.047866 -1.198977 0.063130 -v -0.070428 -1.196897 0.112868 -v -0.095535 -1.200228 0.145492 -v -0.115446 -1.200913 0.164610 -v -0.137795 -1.201682 0.181117 -v -0.162373 -1.202528 0.194716 -v -0.188972 -1.203443 0.205111 -v -0.217383 -1.204421 0.212005 -v -0.239620 -1.373875 0.207689 -v -0.275002 -1.206404 0.214142 -v -0.302712 -1.207359 0.209562 -v -0.329295 -1.208274 0.201494 -v -0.365811 -1.215319 0.183681 -v -0.408747 -1.212266 0.149786 -v -0.442291 -1.212868 0.104653 -v -0.463263 -1.213326 0.051005 -v -0.469293 -1.213310 -0.007249 -v -0.459910 -1.212780 -0.063277 -v -0.436619 -1.211716 -0.114234 -v -0.412274 -1.211145 -0.145492 -v -0.392364 -1.210461 -0.164610 -v -0.370015 -1.209692 -0.181117 -v -0.345436 -1.208846 -0.194717 -v -0.318837 -1.207931 -0.205111 -v -0.273580 -1.427292 -0.202419 -v -0.181692 -1.415841 -0.197489 -v -0.133416 -1.406178 -0.175883 -v -0.092370 -1.399641 -0.142618 -v -0.060933 -1.394815 -0.099564 -v -0.041291 -1.391864 -0.048483 -v -0.035661 -1.391055 0.006929 -v -0.044478 -1.392439 0.060231 -v -0.066363 -1.395785 0.108764 -v -0.089530 -1.399139 0.138913 -v -0.108332 -1.402015 0.157167 -v -0.129438 -1.405243 0.172928 -v -0.152649 -1.408794 0.185912 -v -0.177768 -1.412636 0.195837 -v -0.204598 -1.416740 0.202419 -v -0.259011 -1.425063 0.204460 -v -0.285180 -1.429066 0.200087 -v -0.310284 -1.432906 0.192384 -v -0.343799 -1.438999 0.176324 -v -0.385203 -1.444868 0.143141 -v -0.416935 -1.449512 0.100027 -v -0.436785 -1.452430 0.048765 -v -0.442498 -1.453198 -0.006910 -v -0.433630 -1.451736 -0.060454 -v -0.411615 -1.448232 -0.109128 -v -0.388648 -1.444892 -0.138913 -v -0.369846 -1.442016 -0.157167 -v -0.348740 -1.438788 -0.172928 -v -0.325529 -1.435238 -0.185912 -v -0.300410 -1.431395 -0.195837 -v -0.228886 -1.606479 -0.192833 -v -0.203925 -1.597691 -0.195582 -v -0.157159 -1.652870 -0.190576 -v -0.145065 -1.578127 -0.188110 -v -0.101327 -1.561566 -0.167450 -v -0.064065 -1.548378 -0.135735 -v -0.035534 -1.538387 -0.094735 -v -0.017706 -1.532207 -0.046127 -v -0.012583 -1.530497 0.006583 -v -0.020559 -1.533378 0.057295 -v -0.040400 -1.540402 0.103496 -v -0.061578 -1.547578 0.132335 -v -0.078670 -1.553595 0.149724 -v -0.097856 -1.560349 0.164739 -v -0.118956 -1.567778 0.177108 -v -0.141790 -1.575816 0.186563 -v -0.166179 -1.584403 0.192833 -v -0.191140 -1.593191 0.195582 -v -0.215643 -1.601817 0.194777 -v -0.247696 -1.615366 0.188434 -v -0.292481 -1.629758 0.168027 -v -0.330215 -1.642634 0.136421 -v -0.359120 -1.652617 0.095346 -v -0.377203 -1.658869 0.046501 -v -0.382428 -1.660604 -0.006550 -v -0.374392 -1.657670 -0.057564 -v -0.354416 -1.650502 -0.103926 -v -0.323911 -1.639535 -0.143322 -v -0.297209 -1.630533 -0.164739 -v -0.276110 -1.623104 -0.177108 -v -0.253275 -1.615066 -0.186563 -v -0.160354 -1.747396 -0.183248 -v -0.139053 -1.734030 -0.185860 -v -0.088735 -1.702910 -0.178728 -v -0.051475 -1.678908 -0.159067 -v -0.019695 -1.658986 -0.128929 -v 0.004646 -1.643802 -0.089986 -v 0.019867 -1.634368 -0.043829 -v 0.024263 -1.631725 0.006221 -v 0.017493 -1.636065 0.054381 -v 0.000604 -1.646721 0.098266 -v -0.025367 -1.662992 0.135751 -v -0.048538 -1.677230 0.156549 -v -0.066544 -1.688529 0.168304 -v -0.086030 -1.700756 0.177289 -v -0.106842 -1.713817 0.183247 -v -0.128144 -1.727184 0.185859 -v -0.149053 -1.740305 0.185095 -v -0.175884 -1.759406 0.179090 -v -0.214348 -1.782199 0.159737 -v -0.246653 -1.802064 0.129719 -v -0.271386 -1.817386 0.090686 -v -0.286866 -1.826977 0.044261 -v -0.291365 -1.829686 -0.006167 -v -0.284542 -1.825289 -0.054657 -v -0.267545 -1.814475 -0.098704 -v -0.241620 -1.797960 -0.136104 -v -0.218659 -1.783983 -0.156550 -v -0.200653 -1.772684 -0.168304 -v -0.181167 -1.760457 -0.177289 -v -0.072500 -1.853635 -0.173662 -v -0.055900 -1.836535 -0.176137 -v -0.039605 -1.819749 -0.175413 -v -0.016654 -1.796415 -0.169368 -v 0.012274 -1.766114 -0.150777 -v 0.037045 -1.740640 -0.122253 -v 0.056045 -1.721167 -0.085365 -v 0.067948 -1.709031 -0.041620 -v 0.071408 -1.705583 0.005840 -v 0.066152 -1.711090 0.051506 -v 0.053004 -1.724691 0.093100 -v 0.032782 -1.745490 0.128603 -v 0.014640 -1.763872 0.148360 -v 0.000608 -1.778326 0.159500 -v -0.014578 -1.793969 0.168015 -v -0.030798 -1.810677 0.173662 -v -0.047398 -1.827777 0.176137 -v -0.063693 -1.844562 0.175413 -v -0.084006 -1.868191 0.169716 -v -0.114295 -1.897795 0.151417 -v -0.139579 -1.923361 0.122988 -v -0.158916 -1.943045 0.086005 -v -0.171026 -1.955370 0.042014 -v -0.174576 -1.958885 -0.005778 -v -0.169307 -1.953310 -0.051729 -v -0.156124 -1.939544 -0.093457 -v -0.136037 -1.918545 -0.128873 -v -0.117937 -1.900440 -0.148361 -v -0.103905 -1.885985 -0.159500 -v -0.088720 -1.870343 -0.168015 -v 0.029406 -1.928988 -0.164076 -v 0.075707 -1.928927 -0.163169 -v 0.052355 -1.890723 -0.165731 -v 0.068541 -1.864466 -0.160022 -v 0.088491 -1.830376 -0.142543 -v 0.105765 -1.801563 -0.115642 -v 0.119053 -1.779497 -0.080803 -v 0.127398 -1.765709 -0.039446 -v 0.129839 -1.761752 0.005444 -v 0.126182 -1.767935 0.048635 -v 0.117011 -1.783273 0.087946 -v 0.102907 -1.806730 0.121463 -v 0.090199 -1.827623 0.140171 -v 0.080409 -1.843946 0.150696 -v 0.069815 -1.861610 0.158741 -v 0.058500 -1.880477 0.164076 -v 0.046919 -1.899788 0.166415 -v 0.035551 -1.918742 0.165730 -v 0.024514 -1.937145 0.162185 -v 0.010860 -1.962239 0.152989 -v -0.008798 -1.993849 0.130739 -v -0.024558 -2.019672 0.099673 -v -0.035698 -2.037986 0.061242 -v -0.041317 -2.047154 0.017153 -v -0.040697 -2.045924 -0.027465 -v -0.034238 -2.034926 -0.069109 -v -0.022632 -2.015241 -0.105789 -v -0.006676 -1.987989 -0.135516 -v 0.007497 -1.965520 -0.150696 -v 0.018091 -1.947855 -0.158741 -v 0.139894 -1.978062 -0.154491 -v 0.159876 -1.930963 -0.154938 -v 0.170414 -1.893342 -0.143695 -v 0.181848 -1.860616 -0.122683 -v 0.191315 -1.834042 -0.093468 -v 0.198059 -1.815231 -0.057407 -v 0.201466 -1.805807 -0.016073 -v 0.201064 -1.807035 0.025753 -v 0.197072 -1.818276 0.064837 -v 0.189861 -1.838447 0.099349 -v 0.179742 -1.866439 0.127455 -v 0.171277 -1.890123 0.141892 -v 0.164758 -1.908389 0.149467 -v 0.157795 -1.927899 0.154490 -v 0.150669 -1.947867 0.156692 -v 0.143675 -1.967467 0.156048 -v 0.136883 -1.986497 0.152710 -v 0.129129 -2.012266 0.144025 -v 0.116694 -2.045024 0.123096 -v 0.106874 -2.071759 0.093860 -v 0.099954 -2.090719 0.057695 -v 0.096444 -2.100224 0.016206 -v 0.096770 -2.098989 -0.025786 -v 0.100678 -2.087666 -0.064979 -v 0.107725 -2.067385 -0.099506 -v 0.117365 -2.039307 -0.127512 -v 0.126413 -2.015838 -0.141892 -v 0.132931 -1.997572 -0.149467 -v 0.253991 -2.006080 -0.144905 -v 0.284561 -1.991648 -0.144681 -v 0.279659 -1.971470 -0.144738 -v 0.292387 -1.954420 -0.140635 -v 0.270427 -1.923376 -0.134798 -v 0.276042 -1.891330 -0.115152 -v 0.280912 -1.865278 -0.087767 -v 0.284421 -1.846822 -0.053936 -v 0.286202 -1.837562 -0.015146 -v 0.285991 -1.838738 0.024111 -v 0.283900 -1.849721 0.060784 -v 0.280112 -1.869439 0.093153 -v 0.274744 -1.896797 0.119505 -v 0.270373 -1.920048 0.133088 -v 0.266970 -1.937919 0.140193 -v 0.299713 -1.964656 0.141650 -v 0.296211 -1.983519 0.143741 -v 0.313788 -2.004909 0.141472 -v 0.252419 -2.014332 0.143235 -v 0.249295 -2.039387 0.135057 -v 0.242303 -2.071501 0.115453 -v 0.237002 -2.097685 0.088042 -v 0.233303 -2.116250 0.054139 -v 0.231405 -2.125570 0.015245 -v 0.231509 -2.124392 -0.024122 -v 0.233474 -2.113357 -0.060870 -v 0.237047 -2.093575 -0.093249 -v 0.241879 -2.066188 -0.119534 -v 0.247049 -2.032436 -0.138003 -v 0.362291 -2.025720 -0.134445 -v 0.374366 -1.939754 -0.125769 -v 0.375802 -1.909411 -0.107428 -v 0.377547 -1.884767 -0.081848 -v 0.378904 -1.867334 -0.050287 -v 0.379651 -1.858600 -0.014138 -v 0.379643 -1.859713 0.022431 -v 0.378916 -1.870081 0.056606 -v 0.377521 -1.888707 0.086801 -v 0.375426 -1.914584 0.111425 -v 0.371771 -1.946524 0.128862 -v 0.366692 -2.026145 0.133760 -v 0.367180 -2.049653 0.126104 -v 0.363653 -2.080198 0.107842 -v 0.361327 -2.105078 0.082251 -v 0.359766 -2.122708 0.050591 -v 0.358939 -2.131566 0.014275 -v 0.358876 -2.130473 -0.022487 -v 0.359510 -2.120038 -0.056807 -v 0.360703 -2.101308 -0.087061 -v 0.362224 -2.075374 -0.111633 -v 0.371394 -2.056985 -0.122070 -v 0.473869 -2.027451 -0.124686 -v 0.475878 -2.002596 -0.127526 -v 0.476217 -1.978250 -0.126213 -v 0.472840 -1.947058 -0.117146 -v 0.471175 -1.918749 -0.100065 -v 0.469877 -1.895692 -0.076254 -v 0.468982 -1.879374 -0.046869 -v 0.468552 -1.871185 -0.013212 -v 0.468634 -1.872199 0.020842 -v 0.469182 -1.881862 0.052663 -v 0.470140 -1.899239 0.080764 -v 0.471437 -1.923393 0.103647 -v 0.472959 -1.953310 0.119744 -v 0.474418 -1.976545 0.125733 -v 0.475383 -1.993773 0.127525 -v 0.476331 -2.010684 0.127001 -v 0.482094 -2.033514 0.122539 -v 0.487333 -2.063138 0.109286 -v 0.481864 -2.089706 0.088980 -v 0.482250 -2.109679 0.062262 -v 0.482740 -2.122160 0.030520 -v 0.482826 -2.125814 -0.003952 -v 0.482418 -2.120377 -0.037103 -v 0.481465 -2.106771 -0.067246 -v 0.479550 -2.085988 -0.092935 -v 0.479486 -2.066979 -0.107415 -v 0.608158 -2.007680 -0.122664 -v 0.603176 -1.984396 -0.125479 -v 0.598490 -1.968403 -0.124963 -v 0.592876 -1.946283 -0.120786 -v 0.583974 -1.918115 -0.107889 -v 0.576666 -1.893631 -0.087679 -v 0.571107 -1.874788 -0.061380 -v 0.567633 -1.862968 -0.030113 -v 0.566610 -1.859507 0.003862 -v 0.568109 -1.864671 0.036531 -v 0.571865 -1.877587 0.066210 -v 0.577580 -1.897335 0.091450 -v 0.584869 -1.923010 0.110780 -v 0.592418 -1.953737 0.122669 -v 0.600731 -1.976052 0.125478 -v 0.605416 -1.992045 0.124962 -v 0.612621 -2.013599 0.120810 -v 0.620378 -2.042158 0.107914 -v 0.627433 -2.066731 0.087710 -v 0.632902 -2.085615 0.061409 -v 0.636338 -2.097457 0.030136 -v 0.637326 -2.100933 -0.003855 -v 0.635784 -2.095777 -0.036540 -v 0.631959 -2.082870 -0.066228 -v 0.626099 -2.063159 -0.091463 -v 0.618381 -2.037632 -0.110773 -v 0.720546 -1.956339 -0.120692 -v 0.709818 -1.935582 -0.123432 -v 0.701075 -1.921714 -0.122924 -v 0.689837 -1.901218 -0.118472 -v 0.673922 -1.878156 -0.106222 -v 0.660356 -1.856932 -0.086357 -v 0.649979 -1.840572 -0.060479 -v 0.643477 -1.830304 -0.029716 -v 0.641553 -1.827291 0.003718 -v 0.644348 -1.831760 0.035864 -v 0.651371 -1.842955 0.065069 -v 0.662091 -1.860082 0.089906 -v 0.675906 -1.882396 0.108939 -v 0.691363 -1.909558 0.120689 -v 0.705256 -1.928346 0.123431 -v 0.714000 -1.942214 0.122924 -v 0.722489 -1.955677 0.120294 -v 0.730633 -1.968592 0.115664 -v 0.741623 -1.985466 0.106224 -v 0.754943 -2.006855 0.086354 -v 0.765222 -2.023281 0.060473 -v 0.771672 -2.033582 0.029701 -v 0.773545 -2.036622 -0.003743 -v 0.770695 -2.032175 -0.035901 -v 0.763593 -2.021014 -0.065107 -v 0.752726 -2.003967 -0.089938 -v 0.738508 -1.981918 -0.108951 -v 0.811843 -1.880153 -0.118709 -v 0.823736 -1.836022 -0.120689 -v 0.785316 -1.851489 -0.120886 -v 0.774010 -1.840666 -0.118300 -v 0.763164 -1.830282 -0.113746 -v 0.749176 -1.816445 -0.104538 -v 0.731107 -1.799410 -0.085001 -v 0.717271 -1.786253 -0.059538 -v 0.708602 -1.777995 -0.029279 -v 0.706034 -1.775572 0.003606 -v 0.709752 -1.779166 0.035223 -v 0.719105 -1.788172 0.063946 -v 0.733394 -1.801957 0.088375 -v 0.751859 -1.819976 0.107106 -v 0.772966 -1.842418 0.118699 -v 0.826463 -1.819265 0.120445 -v 0.802531 -1.867970 0.120885 -v 0.813838 -1.878793 0.118299 -v 0.824684 -1.889176 0.113745 -v 0.839129 -1.902516 0.104543 -v 0.856957 -1.919818 0.085002 -v 0.870698 -1.933080 0.059536 -v 0.879317 -1.941394 0.029266 -v 0.881834 -1.943865 -0.003632 -v 0.878059 -1.940315 -0.035264 -v 0.868624 -1.931371 -0.063992 -v 0.854189 -1.917714 -0.088417 -v 0.835333 -1.900102 -0.107131 -v 0.879772 -1.780723 -0.117661 -v 0.852767 -1.763561 -0.118847 -v 0.839779 -1.755308 -0.116305 -v 0.823000 -1.743769 -0.109794 -v 0.800374 -1.729981 -0.094033 -v 0.781887 -1.718382 -0.071748 -v 0.768817 -1.710139 -0.044206 -v 0.762230 -1.705992 -0.012633 -v 0.762969 -1.706494 0.019330 -v 0.770571 -1.711368 0.049161 -v 0.784250 -1.720137 0.075462 -v 0.803191 -1.732366 0.096857 -v 0.826410 -1.747934 0.111960 -v 0.845536 -1.758966 0.117660 -v 0.872541 -1.776128 0.118847 -v 0.885529 -1.784382 0.116305 -v 0.897988 -1.792299 0.111827 -v 0.909779 -1.799792 0.105531 -v 0.925205 -1.809232 0.094051 -v 0.943560 -1.821065 0.071763 -v 0.956572 -1.829418 0.044211 -v 0.963117 -1.833639 0.012622 -v 0.962333 -1.833198 -0.019356 -v 0.954667 -1.828399 -0.049201 -v 0.940886 -1.819757 -0.075507 -v 0.921730 -1.807840 -0.096894 -v 0.897710 -1.793528 -0.111981 -v 0.933538 -1.676610 -0.115642 -v 0.935232 -1.634143 -0.116587 -v 0.904717 -1.664027 -0.116808 -v 0.890856 -1.657975 -0.114310 -v 0.877560 -1.652169 -0.109909 -v 0.864976 -1.646675 -0.103721 -v 0.848828 -1.639301 -0.092456 -v 0.829081 -1.630848 -0.070541 -v 0.815126 -1.624826 -0.043464 -v 0.808094 -1.621799 -0.012432 -v 0.808881 -1.622178 0.018983 -v 0.816993 -1.625771 0.048304 -v 0.831590 -1.632233 0.074154 -v 0.851810 -1.641288 0.095185 -v 0.876636 -1.653067 0.110038 -v 0.917066 -1.614399 0.114700 -v 0.932040 -1.619762 0.116367 -v 0.925821 -1.673241 0.116808 -v 0.939682 -1.679293 0.114310 -v 0.952978 -1.685098 0.109909 -v 0.965562 -1.690593 0.103721 -v 0.981943 -1.697334 0.092486 -v 1.001574 -1.706100 0.070567 -v 1.015481 -1.712269 0.043480 -v 1.022479 -1.715391 0.012430 -v 1.021654 -1.715091 -0.019004 -v 1.013484 -1.711601 -0.048338 -v 0.998791 -1.705311 -0.074196 -v 0.978371 -1.696676 -0.095222 -v 0.952809 -1.686562 -0.110061 -v 0.985007 -1.523554 -0.112876 -v 0.942466 -1.559198 -0.114770 -v 0.928222 -1.554963 -0.112315 -v 0.914558 -1.550900 -0.107991 -v 0.901626 -1.547055 -0.101911 -v 0.885040 -1.541744 -0.090862 -v 0.864738 -1.535907 -0.069321 -v 0.850396 -1.531725 -0.042714 -v 0.843170 -1.529625 -0.012221 -v 0.843979 -1.529906 0.018644 -v 0.852316 -1.532443 0.047452 -v 0.867319 -1.537009 0.072852 -v 0.888107 -1.543455 0.093518 -v 0.913665 -1.552173 0.108119 -v 0.964154 -1.565646 0.114769 -v 0.978398 -1.569881 0.112315 -v 0.992063 -1.573943 0.107991 -v 1.004995 -1.577788 0.101910 -v 1.021765 -1.582308 0.090899 -v 1.041972 -1.588541 0.069355 -v 1.056279 -1.592903 0.042736 -v 1.063481 -1.595117 0.012226 -v 1.062640 -1.594934 -0.018661 -v 1.054254 -1.592525 -0.047485 -v 1.039167 -1.588175 -0.072893 -v 1.018206 -1.582249 -0.093555 -v 0.992022 -1.575596 -0.108142 -v 0.990365 -1.414549 -0.112347 -v 0.976755 -1.404289 -0.111690 -v 0.953055 -1.452776 -0.110320 -v 0.939294 -1.450185 -0.106073 -v 0.921611 -1.446163 -0.097606 -v 0.898653 -1.442246 -0.079358 -v 0.881072 -1.439074 -0.055582 -v 0.870069 -1.437060 -0.027354 -v 0.866810 -1.436496 0.003312 -v 0.873373 -1.439438 0.040688 -v 0.892558 -1.441375 0.072499 -v 0.913759 -1.445372 0.092537 -v 0.939926 -1.450296 0.106554 -v 0.966866 -1.411273 0.110645 -v 0.983738 -1.406219 0.112215 -v 0.989239 -1.459588 0.112731 -v 1.003584 -1.462289 0.110320 -v 1.017344 -1.464880 0.106073 -v 1.034092 -1.468017 0.098212 -v 1.057137 -1.472350 0.080246 -v 1.074933 -1.475699 0.056712 -v 1.086237 -1.477824 0.028668 -v 1.090204 -1.476991 -0.009847 -v 1.079983 -1.476764 -0.046638 -v 1.064804 -1.474046 -0.071595 -v 1.043721 -1.470393 -0.091894 -v 1.017455 -1.466586 -0.106225 -v 1.011601 -1.360082 -0.109359 -v 0.966696 -1.358009 -0.108325 -v 0.953004 -1.356767 -0.104155 -v 0.940044 -1.355592 -0.098290 -v 0.923394 -1.353611 -0.087653 -v 0.903066 -1.352013 -0.066876 -v 0.888335 -1.351793 -0.040675 -v 0.881340 -1.350232 -0.010460 -v 0.882511 -1.350343 0.019244 -v 0.890903 -1.351976 0.046426 -v 0.905743 -1.352220 0.070263 -v 0.926682 -1.353766 0.090178 -v 0.952754 -1.354543 0.104202 -v 1.003363 -1.354281 0.110405 -v 1.017672 -1.360729 0.108159 -v 1.031524 -1.357309 0.103969 -v 1.044471 -1.354109 0.098222 -v 1.060256 -1.366877 0.087702 -v 1.080593 -1.368516 0.066915 -v 1.094975 -1.369715 0.041234 -v 1.102134 -1.369424 0.012596 -v 1.101577 -1.370210 -0.016727 -v 1.093514 -1.369472 -0.044633 -v 1.078715 -1.368121 -0.069303 -v 1.056477 -1.355696 -0.091282 -v 1.034082 -1.353359 -0.103296 -v 0.998828 -1.265355 -0.108870 -v 0.983648 -1.253133 -0.107966 -v 0.970191 -1.272087 -0.106068 -v 0.956769 -1.275919 -0.102075 -v 0.943916 -1.277572 -0.096314 -v 0.931955 -1.278584 -0.088973 -v 0.920981 -1.279974 -0.080176 -v 0.911266 -1.280757 -0.070066 -v 0.899326 -1.282699 -0.053257 -v 0.888651 -1.281647 -0.026184 -v 0.885503 -1.281774 0.003321 -v 0.890360 -1.279790 0.031800 -v 0.902036 -1.279536 0.057512 -v 0.919830 -1.278876 0.079345 -v 0.942352 -1.277443 0.095764 -v 0.961913 -1.274587 0.103823 -v 0.976604 -1.274349 0.107446 -v 0.991624 -1.276953 0.109102 -v 1.058793 -1.280516 0.088682 -v 1.073661 -1.282602 0.076200 -v 1.090466 -1.284135 0.053903 -v 1.101573 -1.278943 0.026374 -v 1.104760 -1.278814 -0.003198 -v 1.100122 -1.278535 -0.031625 -v 1.088006 -1.282167 -0.057845 -v 1.070114 -1.280963 -0.079678 -v 1.011680 -0.950017 -0.103511 -v 0.989993 -0.940216 0.104963 -v 1.004397 -0.906062 0.104050 -v 1.017689 -0.977664 0.102600 -v 1.030669 -0.980489 0.098703 -v 1.043020 -0.992612 0.093298 -v 1.089515 -1.019142 0.045123 -v 1.096893 -1.027677 0.025413 -v 1.099815 -1.020048 -0.003050 -v 1.095329 -1.019760 -0.030609 -v 1.025609 -0.995680 -0.100669 -v 0.998181 -0.667445 -0.102274 -v 0.977078 -0.461527 0.099035 -v 1.054052 -0.891240 0.085406 -v 1.064505 -0.896824 0.077052 -v 1.077612 -0.895366 0.062910 -v 1.082992 -0.900142 -0.055050 -v 1.065853 -0.893390 -0.076057 -v 1.043595 -0.880004 -0.092075 -v 0.971279 -0.409045 -0.097567 -v 0.958872 -0.408061 -0.093791 -v 0.947238 -0.385851 -0.088349 -v 0.936424 -0.367468 -0.081530 -v 0.898096 -0.189953 0.022132 -v 0.904034 -0.191608 0.040381 -v 0.917355 -0.200555 0.062103 -v 0.930415 -0.216348 0.075307 -v 0.946318 -0.396021 0.088196 -v 0.964445 -0.153432 0.093405 -v 0.984345 -0.272050 -0.098594 -v 0.927437 -0.164820 -0.072319 -v 0.918731 -0.159975 -0.063207 -v 0.911215 -0.156495 -0.052994 -v 0.905025 -0.154007 -0.041774 -v 0.898398 -0.150210 -0.023692 -v 0.895539 -0.149124 0.002862 -v 1.084920 0.073411 0.029270 -v 0.990407 -0.076115 0.097452 -v 1.048156 0.115565 0.078407 -v 1.057760 0.112231 0.070718 -v 1.066319 0.110239 0.061836 -v 1.073702 0.109066 0.051859 -v 1.079782 0.108439 0.040883 -v 1.088574 0.108897 0.010179 -v 1.087858 0.109148 -0.015525 -v 1.080587 0.110236 -0.039510 -v 1.067491 0.113357 -0.060644 -v 1.049281 0.122683 -0.077784 -v 1.027295 0.044243 -0.090499 -v 1.004567 0.585835 -0.091085 -v 1.014009 0.163851 0.093163 -v 1.025742 0.174439 0.089500 -v 1.036946 0.175188 0.084439 -v 0.969861 0.249680 -0.092542 -v 0.958192 0.232735 -0.089088 -v 0.947086 0.229087 -0.084097 -v 0.936719 0.227756 -0.077729 -v 0.946147 0.240954 0.083807 -v 0.997222 0.588269 0.092007 -v 0.990723 0.590258 -0.092380 -v 0.976246 0.621826 -0.091276 -v 0.921429 0.579687 -0.064585 -v 0.906900 0.579297 -0.045071 -v 0.897867 0.578884 -0.021972 -v 0.895272 0.578729 0.003065 -v 0.899276 0.578905 0.027095 -v 0.909186 0.579350 0.048892 -v 0.924283 0.579872 0.067413 -v 0.968921 0.634037 0.090133 -v 1.020231 0.590756 -0.086726 -v 0.961493 0.636415 -0.088629 -v 0.950695 0.631733 -0.085221 -v 0.940217 0.585425 -0.079656 -v 0.942139 0.626406 0.081322 -v 0.955577 0.633135 0.086909 -v 0.983926 0.602170 0.092050 -v 1.008228 0.591506 0.090042 -v 1.023879 0.592297 0.085050 -v 1.044318 0.593965 0.072877 -v 1.061216 0.595273 0.055603 -v 1.073171 0.596183 0.034257 -v 1.079190 0.596650 0.009794 -v 1.078500 0.596626 -0.014966 -v 1.071516 0.596161 -0.038074 -v 1.058951 0.595318 -0.058444 -v 1.041514 0.594191 -0.075006 -v 0.984707 0.654428 -0.090320 -v 0.939106 0.632328 -0.080178 -v 0.925779 0.629082 -0.071483 -v 0.909603 0.625287 -0.054525 -v 0.898174 0.622581 -0.033593 -v 0.892419 0.621215 -0.009610 -v 0.893075 0.621371 0.014663 -v 0.899737 0.622966 0.037315 -v 0.911721 0.625845 0.057285 -v 0.928337 0.629881 0.073531 -v 0.996077 0.642609 0.089675 -v 1.015538 0.650032 0.083446 -v 1.035002 0.654993 0.071501 -v 1.051129 0.658948 0.054570 -v 1.062562 0.661730 0.033625 -v 1.068319 0.663142 0.009608 -v 1.067648 0.663015 -0.014708 -v 1.060946 0.661459 -0.037398 -v 1.048895 0.658654 -0.057389 -v 1.032173 0.654819 -0.073630 -v 1.011376 0.650418 -0.085057 -v 0.962017 0.729969 -0.086827 -v 0.946321 0.708839 -0.087951 -v 0.943555 0.686523 -0.086684 -v 0.929984 0.679912 -0.081895 -v 0.911750 0.672122 -0.070093 -v 0.896870 0.665520 -0.053460 -v 0.886360 0.660826 -0.032929 -v 0.881070 0.658461 -0.009406 -v 0.881676 0.658738 0.014400 -v 0.887803 0.661505 0.036617 -v 0.898821 0.666491 0.056207 -v 0.914090 0.673455 0.072144 -v 0.932889 0.682348 0.083380 -v 0.947006 0.712466 0.088221 -v 0.956751 0.726570 0.087686 -v 0.980406 0.703084 0.086684 -v 0.994553 0.708819 0.081820 -v 1.012365 0.717209 0.070071 -v 1.027131 0.723975 0.053475 -v 1.037609 0.728750 0.032945 -v 1.042883 0.731167 0.009402 -v 1.042255 0.730923 -0.014433 -v 1.036084 0.728200 -0.036678 -v 1.024991 0.723295 -0.056282 -v 1.009579 0.716548 -0.072216 -v 0.990318 0.708564 -0.083429 -v 0.917905 0.774497 -0.086311 -v 0.916824 0.735238 -0.085005 -v 0.904730 0.726595 -0.080287 -v 0.888306 0.716198 -0.068694 -v 0.874972 0.707475 -0.052395 -v 0.865555 0.701276 -0.032267 -v 0.860816 0.698155 -0.009203 -v 0.861361 0.698524 0.014138 -v 0.866851 0.702181 0.035923 -v 0.876721 0.708766 0.055133 -v 0.890382 0.717950 0.070758 -v 0.907113 0.729608 0.081768 -v 0.907019 0.759117 0.085200 -v 0.949852 0.757119 0.085005 -v 0.962713 0.764905 0.080213 -v 0.978597 0.775890 0.068663 -v 0.991777 0.784772 0.052396 -v 1.001141 0.791050 0.032273 -v 1.005850 0.794222 0.009195 -v 1.005275 0.793885 -0.014170 -v 0.999734 0.790271 -0.035976 -v 0.989774 0.783767 -0.055198 -v 0.975920 0.774805 -0.070821 -v 0.958512 0.764080 -0.081813 -v 0.908128 0.809181 -0.083520 -v 0.887657 0.782475 -0.084648 -v 0.870740 0.771374 -0.078680 -v 0.856317 0.758928 -0.067296 -v 0.844672 0.748562 -0.051326 -v 0.836450 0.741199 -0.031603 -v 0.832314 0.737492 -0.008999 -v 0.832791 0.737934 0.013876 -v 0.837584 0.742279 0.035228 -v 0.846197 0.750101 0.054056 -v 0.858103 0.760998 0.069370 -v 0.872593 0.774762 0.080154 -v 0.876829 0.811479 0.084805 -v 0.881054 0.822341 0.084353 -v 0.914724 0.808899 0.082429 -v 0.928557 0.823756 0.073571 -v 0.941358 0.835579 0.059816 -v 0.951264 0.844627 0.041890 -v 0.957484 0.850296 0.020580 -v 0.959294 0.851982 -0.002580 -v 0.956552 0.849566 -0.024849 -v 0.949710 0.843478 -0.045077 -v 0.939235 0.834190 -0.062276 -v 0.925510 0.822256 -0.075447 -v 0.860222 0.854172 -0.081836 -v 0.822828 0.864134 -0.082766 -v 0.816640 0.854906 -0.082430 -v 0.836130 0.819483 -0.080697 -v 0.822821 0.804968 -0.072103 -v 0.811741 0.791919 -0.058585 -v 0.803302 0.781846 -0.041012 -v 0.798029 0.775524 -0.020141 -v 0.796474 0.773672 0.002529 -v 0.798747 0.776428 0.024323 -v 0.804446 0.783332 0.044125 -v 0.813139 0.793910 0.060972 -v 0.824317 0.807737 0.073881 -v 0.836686 0.824919 0.081806 -v 0.866194 0.855517 0.080661 -v 0.877959 0.871246 0.072059 -v 0.888697 0.884500 0.058575 -v 0.897020 0.894655 0.041014 -v 0.902246 0.901019 0.020138 -v 0.903761 0.902902 -0.002550 -v 0.901440 0.900169 -0.024365 -v 0.895661 0.893299 -0.044184 -v 0.886811 0.882813 -0.061033 -v 0.875184 0.869305 -0.073935 -v 0.805613 0.892350 -0.080152 -v 0.787117 0.855388 -0.079050 -v 0.776303 0.839306 -0.070586 -v 0.767473 0.825078 -0.057343 -v 0.760766 0.814115 -0.040135 -v 0.756577 0.807237 -0.019696 -v 0.755346 0.805223 0.002499 -v 0.757153 0.808224 0.023839 -v 0.761680 0.815737 0.043230 -v 0.768579 0.827239 0.059728 -v 0.777420 0.842242 0.072368 -v 0.786960 0.860599 0.080118 -v 0.772175 0.888160 0.081288 -v 0.779272 0.896549 0.081027 -v 0.810982 0.894570 0.079018 -v 0.820188 0.911564 0.070553 -v 0.828675 0.925942 0.057340 -v 0.835266 0.936967 0.040142 -v 0.839407 0.943878 0.019696 -v 0.840600 0.945914 -0.002520 -v 0.838746 0.942925 -0.023883 -v 0.834141 0.935430 -0.043291 -v 0.827085 0.923985 -0.059793 -v 0.817791 0.909207 -0.072424 -v 0.745404 0.922071 -0.078466 -v 0.703909 0.921879 -0.079252 -v 0.701963 0.911333 -0.078974 -v 0.732970 0.883588 -0.077399 -v 0.725041 0.866267 -0.069073 -v 0.718729 0.851155 -0.056102 -v 0.713954 0.839532 -0.039257 -v 0.710978 0.832244 -0.019252 -v 0.710105 0.830113 0.002472 -v 0.711393 0.833297 0.023356 -v 0.714611 0.841262 0.042337 -v 0.719507 0.853450 0.058487 -v 0.725749 0.869314 0.070856 -v 0.732238 0.888432 0.078430 -v 0.749953 0.925089 0.077373 -v 0.756303 0.942996 0.069050 -v 0.762288 0.958200 0.056106 -v 0.766951 0.969865 0.039269 -v 0.769883 0.977176 0.019255 -v 0.770720 0.979322 -0.002492 -v 0.769391 0.976139 -0.023404 -v 0.766105 0.968174 -0.042401 -v 0.761062 0.956007 -0.058556 -v 0.754390 0.940269 -0.070916 -v 0.674935 0.942446 -0.076748 -v 0.674982 0.902447 -0.075751 -v 0.670371 0.884325 -0.067578 -v 0.666883 0.868694 -0.054888 -v 0.664266 0.856685 -0.038404 -v 0.662638 0.849158 -0.018820 -v 0.662127 0.847310 0.012946 -v 0.664594 0.858507 0.041481 -v 0.667237 0.871105 0.057281 -v 0.670559 0.887457 0.069365 -v 0.673664 0.906876 0.076742 -v 0.632910 0.929692 0.077271 -v 0.652697 0.940304 0.077119 -v 0.684294 0.945246 0.075726 -v 0.687432 0.963633 0.067556 -v 0.690616 0.979296 0.054884 -v 0.693123 0.991324 0.038407 -v 0.694699 0.998865 0.018818 -v 0.694969 1.000759 -0.012983 -v 0.692590 0.989519 -0.041526 -v 0.689812 0.976925 -0.057329 -v 0.686097 0.960615 -0.069413 -v 0.614003 0.934407 -0.076885 -v 0.614554 0.920057 -0.076224 -v 0.612971 0.901010 -0.070690 -v 0.612116 0.883889 -0.060395 -v 0.611503 0.869950 -0.046042 -v 0.611083 0.860073 -0.028304 -v 0.610867 0.855115 -0.007964 -v 0.610899 0.857486 0.022871 -v 0.611463 0.872113 0.048769 -v 0.611956 0.886681 0.062521 -v 0.612402 0.904605 0.072144 -v 0.613381 0.918689 0.075805 -v 0.616169 0.962537 0.070652 -v 0.616070 0.979656 0.060341 -v 0.616456 0.993569 0.045995 -v 0.616780 1.003427 0.028273 -v 0.616937 1.008376 0.007955 -v 0.616700 1.006016 -0.022844 -v 0.616109 0.991417 -0.048730 -v 0.615311 0.976875 -0.062485 -v 0.613233 0.958986 -0.072116 -v 0.475110 0.931214 -0.076143 -v 0.505726 0.916175 -0.075709 -v 0.504755 0.896883 -0.070172 -v 0.506253 0.879935 -0.059929 -v 0.507673 0.866176 -0.045671 -v 0.508710 0.856434 -0.028061 -v 0.509318 0.850486 0.002421 -v 0.508464 0.857949 0.031617 -v 0.507253 0.868325 0.048435 -v 0.505504 0.882700 0.062099 -v 0.502937 0.900315 0.071650 -v 0.502228 0.914353 0.075289 -v 0.445500 0.918632 0.076070 -v 0.502924 0.939135 0.075704 -v 0.497901 0.957785 0.070150 -v 0.495689 0.974634 0.059908 -v 0.494062 0.988356 0.045656 -v 0.492938 0.998078 0.028054 -v 0.492238 1.004013 -0.002420 -v 0.493029 0.996549 -0.031606 -v 0.494129 0.986169 -0.048418 -v 0.495600 0.971778 -0.062077 -v 0.497063 0.954050 -0.071634 -v 0.401374 0.921995 -0.074773 -v 0.410751 0.899378 -0.075203 -v 0.413303 0.880352 -0.069685 -v 0.417473 0.863974 -0.059503 -v 0.421022 0.850709 -0.045340 -v 0.423562 0.841323 -0.027847 -v 0.425102 0.835598 0.002479 -v 0.423099 0.842795 0.031427 -v 0.420304 0.852799 0.048134 -v 0.416375 0.866652 0.061704 -v 0.411218 0.883537 0.071165 -v 0.408204 0.897138 0.074774 -v 0.404550 0.921339 0.075199 -v 0.397122 0.939054 0.069666 -v 0.392383 0.955258 0.059485 -v 0.388671 0.968466 0.045327 -v 0.386063 0.977826 0.027841 -v 0.384479 0.983534 -0.002479 -v 0.386412 0.976324 -0.031417 -v 0.389118 0.966305 -0.048115 -v 0.392824 0.952412 -0.061678 -v 0.397106 0.935304 -0.071145 -v 0.319304 0.891540 -0.074258 -v 0.291396 0.866868 -0.075080 -v 0.332212 0.871069 -0.074697 -v 0.338533 0.853040 -0.069198 -v 0.345654 0.837837 -0.059077 -v 0.351564 0.825553 -0.045007 -v 0.355769 0.816866 -0.027631 -v 0.358332 0.811580 0.002549 -v 0.355042 0.818244 0.031246 -v 0.350477 0.827514 0.047841 -v 0.344101 0.840343 0.061320 -v 0.336035 0.855874 0.070682 -v 0.330533 0.868533 0.074258 -v 0.283709 0.855903 0.075014 -v 0.322269 0.891272 0.074695 -v 0.311979 0.907394 0.069184 -v 0.304404 0.922359 0.059065 -v 0.298366 0.934570 0.045000 -v 0.294107 0.943225 0.027631 -v 0.291525 0.948501 -0.002542 -v 0.294736 0.941806 -0.031232 -v 0.299228 0.932515 -0.047818 -v 0.305420 0.919623 -0.061288 -v 0.312792 0.903780 -0.070655 -v 0.247931 0.852367 -0.072926 -v 0.268929 0.832706 -0.074189 -v 0.278969 0.816546 -0.068711 -v 0.289042 0.803260 -0.058654 -v 0.297316 0.792552 -0.044678 -v 0.304404 0.783464 -0.022775 -v 0.306116 0.781221 0.012754 -v 0.299337 0.789818 0.039644 -v 0.291695 0.799515 0.054699 -v 0.281713 0.811993 0.066168 -v 0.269276 0.825098 0.072920 -v 0.255257 0.850240 0.074189 -v 0.241991 0.863909 0.068705 -v 0.231554 0.876900 0.058649 -v 0.223173 0.887520 0.044678 -v 0.216072 0.896597 0.022775 -v 0.214309 0.898804 -0.012733 -v 0.220992 0.890146 -0.039623 -v 0.228538 0.880398 -0.054667 -v 0.238244 0.867748 -0.066130 -v 0.194993 0.800328 -0.072361 -v 0.215234 0.795621 -0.074501 -v 0.226086 0.778265 -0.071562 -v 0.239472 0.766502 -0.063755 -v 0.251048 0.756814 -0.051739 -v 0.263715 0.746404 -0.027504 -v 0.267123 0.743468 0.002486 -v 0.264612 0.745515 0.021826 -v 0.258393 0.750607 0.039409 -v 0.248874 0.758379 0.054368 -v 0.236481 0.768349 0.065752 -v 0.221873 0.778259 0.072358 -v 0.214090 0.798070 0.074507 -v 0.194245 0.804555 0.071561 -v 0.180133 0.815436 0.063757 -v 0.168408 0.824941 0.051744 -v 0.155755 0.835373 0.027507 -v 0.152223 0.838161 -0.002474 -v 0.154706 0.836088 -0.021811 -v 0.160884 0.830960 -0.039389 -v 0.170321 0.823114 -0.054339 -v 0.182485 0.812918 -0.065717 -v 0.155918 0.740034 -0.071795 -v 0.157989 0.712640 -0.073545 -v 0.163057 0.698781 -0.073153 -v 0.190925 0.725084 -0.071057 -v 0.206363 0.716420 -0.063299 -v 0.219615 0.709420 -0.051365 -v 0.234091 0.701954 -0.027267 -v 0.237988 0.699809 0.002486 -v 0.235118 0.701274 0.021693 -v 0.228010 0.704924 0.039157 -v 0.217129 0.710495 0.054021 -v 0.202987 0.717605 0.065326 -v 0.186904 0.723940 0.071794 -v 0.158238 0.699687 0.073470 -v 0.154078 0.714870 0.073279 -v 0.154558 0.744011 0.071061 -v 0.138556 0.751593 0.063307 -v 0.125185 0.758368 0.051375 -v 0.110722 0.765871 0.027278 -v 0.106718 0.767821 -0.002476 -v 0.109566 0.766319 -0.021683 -v 0.116643 0.762617 -0.039146 -v 0.127461 0.756946 -0.054005 -v 0.141426 0.749544 -0.065304 -v 0.129869 0.673086 -0.071249 -v 0.167052 0.665422 -0.070553 -v 0.183715 0.659803 -0.062844 -v 0.197943 0.655455 -0.050993 -v 0.213464 0.650903 -0.027049 -v 0.217652 0.649529 0.002476 -v 0.214576 0.650416 0.021547 -v 0.206955 0.652637 0.038889 -v 0.195287 0.656021 0.053653 -v 0.180141 0.660293 0.064881 -v 0.163365 0.663167 0.071247 -v 0.128042 0.677067 0.070559 -v 0.110984 0.681363 0.062856 -v 0.096668 0.685438 0.051007 -v 0.081154 0.690041 0.027062 -v 0.076887 0.691167 -0.002470 -v 0.079950 0.690233 -0.021545 -v 0.087551 0.687946 -0.038889 -v 0.099181 0.684435 -0.053653 -v 0.114212 0.679798 -0.064877 -v 0.116091 0.603871 -0.070663 -v 0.127343 0.570067 -0.072553 -v 0.139308 0.569009 -0.071890 -v 0.151349 0.588706 -0.070076 -v 0.168395 0.586026 -0.062706 -v 0.183989 0.584452 -0.050340 -v 0.200887 0.593349 -0.028133 -v 0.205577 0.592782 0.011212 -v 0.195203 0.594082 0.037927 -v 0.183383 0.595551 0.052731 -v 0.167762 0.598029 0.064248 -v 0.153337 0.589952 0.069391 -v 0.144510 0.588092 0.071462 -v 0.133569 0.582984 0.072685 -v 0.122711 0.573814 0.072238 -v 0.112598 0.604365 0.069806 -v 0.095385 0.606490 0.061966 -v 0.080859 0.608294 0.050036 -v 0.064991 0.610231 0.025549 -v 0.061792 0.610638 -0.013906 -v 0.072559 0.609342 -0.039292 -v 0.084673 0.607841 -0.053794 -v 0.100283 0.605913 -0.064759 -v 0.112713 0.534748 -0.070279 -v 0.199096 0.527947 0.021235 -v 0.191279 0.528266 0.038333 -v 0.178822 0.531357 0.053497 -v 0.165373 0.531360 0.063169 -v 0.109806 0.528349 0.069520 -v 0.092515 0.529668 0.061937 -v 0.077860 0.530264 0.050259 -v 0.061918 0.530319 0.026653 -v 0.057978 0.530590 -0.012481 -v 0.068563 0.530856 -0.038333 -v 0.080537 0.530735 -0.052889 -v 0.096043 0.530717 -0.063957 -v 0.118458 0.339321 -0.068640 -v 0.127899 0.375579 -0.070036 -v 0.189172 0.515644 -0.042153 -v 0.198102 0.515436 -0.025599 -v 0.202498 0.515435 -0.006579 -v 0.199989 0.403762 0.011635 -v 0.194571 0.402927 0.029246 -v 0.184826 0.400945 0.044789 -v 0.158845 0.281882 0.062509 -v 0.132946 0.371568 0.069964 -v 0.123539 0.380199 0.069803 -v 0.110902 0.343702 0.067110 -v 0.095428 0.331096 0.060379 -v 0.086410 0.407300 0.055249 -v 0.073677 0.406770 0.042110 -v 0.062786 0.405887 0.021405 -v 0.059777 0.406130 -0.002263 -v 0.062825 0.405989 -0.020604 -v 0.070433 0.405803 -0.037298 -v 0.082105 0.405461 -0.051523 -v 0.092184 0.406583 -0.059233 -v 0.100246 0.407148 -0.063719 -v 0.109078 0.415253 -0.067277 -v 0.150674 -0.014535 -0.060969 -v 0.169376 0.180917 -0.054080 -v 0.170743 0.143914 0.051898 -v 0.163383 0.130933 0.057007 -v 0.145403 0.044793 0.063806 -v 0.177032 -0.025999 -0.039597 -v 0.185770 -0.017560 -0.024314 -v 0.190121 -0.014181 -0.006743 -v 0.189533 -0.014593 0.011044 -v 0.184287 -0.019095 0.027643 -v 0.174832 -0.029322 0.042240 -v 0.077370 0.116463 0.046851 -v 0.066937 0.104652 0.032829 -v 0.060344 0.102640 0.015999 -v 0.058451 0.102095 -0.002264 -v 0.061400 0.102597 -0.019830 -v 0.068724 0.104026 -0.035817 -v 0.079956 0.106774 -0.049449 -v 0.089829 0.117338 -0.056930 -v 0.097686 0.124693 -0.061274 -v 0.106270 0.138055 -0.064657 -v 0.112381 -0.418072 -0.062560 -v 0.132410 -0.277838 -0.062377 -v 0.128718 -0.277521 0.063115 -v 0.105641 -0.433345 0.061954 -v 0.120524 -0.420020 -0.062220 -v 0.140782 -0.415925 0.056769 -v 0.119008 -0.316038 0.063592 -v 0.088803 -0.440173 0.057149 -v 0.103237 -0.442139 -0.061260 -v 0.139250 -0.421304 -0.057294 -v 0.159303 -0.253045 -0.049735 -v 0.146908 -0.442842 0.052554 -v 0.132401 -0.447714 0.059151 -v 0.074541 -0.445561 0.048441 -v 0.063588 -0.447927 0.037055 -v 0.055283 -0.528817 0.023302 -v 0.051023 -0.513393 0.005952 -v 0.051503 -0.523395 -0.010163 -v 0.056129 -0.527183 -0.025278 -v 0.065487 -0.448884 -0.039464 -v 0.077385 -0.447517 -0.050605 -v 0.087072 -0.445481 -0.056156 -v 0.095040 -0.436181 -0.059329 -v 0.164109 -0.557588 -0.028644 -v 0.170437 -0.537334 -0.014003 -v 0.172089 -0.535706 0.002333 -v 0.169400 -0.534461 0.018063 -v 0.162788 -0.533546 0.032362 -v 0.155190 -0.451028 0.046073 -v 0.128527 -0.534451 -0.058038 -v 0.123361 -0.473530 0.060632 -v 0.114285 -0.531003 0.060209 -v 0.106261 -0.531170 0.060049 -v 0.101324 -0.537068 -0.059203 -v 0.112509 -0.537043 -0.060213 -v 0.143811 -0.536242 -0.050962 -v 0.152965 -0.530174 0.043721 -v 0.140199 -0.530350 0.053023 -v 0.125648 -0.529470 0.058519 -v 0.094552 -0.529902 0.057751 -v 0.080415 -0.530873 0.051574 -v 0.068434 -0.550504 0.042735 -v 0.056346 -0.587881 0.023122 -v 0.064977 -0.536260 -0.038190 -v 0.076451 -0.536730 -0.048939 -v 0.090210 -0.536319 -0.056338 -v 0.107334 -0.584484 -0.053988 -v 0.123024 -0.582717 -0.053883 -v 0.137989 -0.580172 -0.049771 -v 0.155102 -0.572279 -0.039784 -v 0.172884 -0.578501 -0.007652 -v 0.172861 -0.561989 0.013543 -v 0.163838 -0.557384 0.034613 -v 0.153801 -0.555611 0.046587 -v 0.140856 -0.554777 0.055771 -v 0.126844 -0.554694 0.061065 -v 0.111576 -0.556298 0.063084 -v 0.095823 -0.558581 0.060984 -v 0.081588 -0.561627 0.055034 -v 0.053647 -0.579114 -0.004655 -v 0.062523 -0.582684 -0.025916 -v 0.072447 -0.584400 -0.037822 -v 0.085117 -0.585177 -0.046928 -v 0.095664 -0.585056 -0.051318 -v 0.107241 -0.628888 -0.036883 -v 0.123033 -0.626756 -0.039016 -v 0.144814 -0.620706 -0.035885 -v 0.165749 -0.621621 -0.019112 -v 0.180743 -0.627405 0.003701 -v 0.177286 -0.585849 0.022380 -v 0.162909 -0.576143 0.049091 -v 0.144728 -0.573702 0.062626 -v 0.130205 -0.573979 0.068219 -v 0.114911 -0.576990 0.070782 -v 0.093288 -0.584292 0.068287 -v 0.077523 -0.606746 0.062083 -v 0.073601 -0.631211 0.060293 -v 0.060670 -0.619060 0.009952 -v 0.074748 -0.627990 -0.016994 -v 0.092688 -0.630004 -0.030755 -v 0.127529 -0.668884 -0.014489 -v 0.142931 -0.665513 -0.014712 -v 0.160223 -0.657375 -0.011298 -v 0.188120 -0.624732 0.027367 -v 0.185984 -0.614992 0.038675 -v 0.157764 -0.594232 0.070491 -v 0.143973 -0.593937 0.078007 -v 0.122879 -0.602228 0.086171 -v 0.107989 -0.607830 0.085428 -v 0.094793 -0.615163 0.081382 -v 0.072669 -0.650119 0.053508 -v 0.073471 -0.659089 0.034090 -v 0.078928 -0.665602 0.020805 -v 0.087721 -0.670166 0.008753 -v 0.099251 -0.672466 -0.001468 -v 0.112907 -0.672331 -0.009228 -v 0.213193 -0.721432 0.065751 -v 0.181306 -0.607520 0.049109 -v 0.174789 -0.604405 0.061259 -v 0.174594 -0.620229 0.084775 -v 0.162424 -0.619666 0.094525 -v 0.137326 -0.600605 0.084782 -v 0.115535 -0.716835 0.137491 -v 0.146836 -0.742354 0.045912 -v 0.165346 -0.737023 0.041064 -v 0.179769 -0.730698 0.041207 -v 0.192578 -0.722438 0.044551 -v 0.230778 -0.741522 0.096567 -v 0.212308 -0.676445 0.099752 -v 0.165500 -0.668755 0.140591 -v 0.144559 -0.672292 0.141076 -v 0.130540 -0.678881 0.138713 -v 0.115321 -0.688292 0.130843 -v 0.108106 -0.735141 0.115220 -v 0.109361 -0.742500 0.100618 -v 0.108953 -0.734862 0.076900 -v 0.117808 -0.739648 0.064982 -v 0.129380 -0.742118 0.054848 -v 0.226555 -0.716485 0.096438 -v 0.225677 -0.708304 0.106583 -v 0.201347 -0.670596 0.120018 -v 0.183262 -0.668378 0.134018 -v 0.116594 -0.753809 0.135771 -v 0.166115 -0.761751 0.056647 -v 0.181127 -0.757193 0.055540 -v 0.195485 -0.751399 0.058335 -v 0.205439 -0.732054 0.059452 -v 0.217904 -0.705480 0.134954 -v 0.201833 -0.702406 0.150990 -v 0.188268 -0.702470 0.158752 -v 0.173124 -0.705157 0.163581 -v 0.157598 -0.708843 0.164191 -v 0.136823 -0.716966 0.158963 -v 0.120953 -0.760001 0.084400 -v 0.137281 -0.763613 0.068651 -v 0.150962 -0.763519 0.060894 -v 0.169979 -0.778508 0.063464 -v 0.185520 -0.775562 0.063010 -v 0.200595 -0.771963 0.066687 -v 0.216224 -0.774240 0.076795 -v 0.225350 -0.764637 0.085486 -v 0.235931 -0.749272 0.128638 -v 0.227366 -0.745725 0.150176 -v 0.217622 -0.744267 0.162343 -v 0.201351 -0.743938 0.173973 -v 0.183154 -0.746096 0.179576 -v 0.167274 -0.748367 0.179818 -v 0.145785 -0.753109 0.173536 -v 0.124538 -0.757225 0.153300 -v 0.118055 -0.779628 0.124047 -v 0.121713 -0.776889 0.098761 -v 0.135525 -0.779672 0.080393 -v 0.152393 -0.779970 0.068533 -v 0.168578 -0.800449 0.066601 -v 0.177082 -0.800317 0.065554 -v 0.187374 -0.799256 0.065721 -v 0.203955 -0.798834 0.070176 -v 0.237083 -0.791309 0.104479 -v 0.240569 -0.790660 0.126091 -v 0.238732 -0.792997 0.141736 -v 0.232122 -0.789489 0.156376 -v 0.222426 -0.789367 0.168810 -v 0.209747 -0.789600 0.178408 -v 0.199566 -0.789941 0.182868 -v 0.188327 -0.790977 0.185727 -v 0.176284 -0.792698 0.186260 -v 0.164655 -0.791768 0.184375 -v 0.149541 -0.792129 0.178129 -v 0.134436 -0.792865 0.166188 -v 0.122470 -0.813058 0.142065 -v 0.124079 -0.799023 0.101285 -v 0.137803 -0.802931 0.082961 -v 0.150395 -0.799779 0.073015 -v 0.160305 -0.801509 0.068921 -v 0.240571 -0.878740 0.125797 -v 0.232176 -0.864371 0.155821 -v 0.223290 -0.861292 0.167330 -v 0.210160 -0.854321 0.177404 -v 0.200532 -0.850251 0.181672 -v 0.192844 -0.849904 0.183760 -v 0.184776 -0.844405 0.184966 -v 0.151691 -0.925948 0.175215 -v 0.139559 -0.897523 0.166676 -v 0.129706 -0.867491 0.155184 -v 0.121991 -0.893751 0.123938 -v 0.124190 -0.890574 0.108542 -v 0.129484 -0.853778 0.094952 -v 0.151403 -0.838091 0.073148 -v 0.169138 -0.915755 0.066368 -v 0.177060 -0.916866 0.065328 -v 0.188683 -0.875916 0.065936 -v 0.230029 -0.938513 0.091757 -v 0.238137 -0.896303 0.108703 -v 0.237141 -0.931381 0.139548 -v 0.163414 -1.075282 0.176627 -v 0.123532 -0.967000 0.138099 -v 0.139177 -0.927111 0.081934 -v 0.151594 -0.924450 0.072613 -v 0.161359 -0.920277 0.068414 -v 0.216822 -1.126965 0.079188 -v 0.172918 -1.133222 0.176706 -v 0.129096 -0.988996 0.093290 -v 0.203446 -1.197155 0.069592 -v 0.209404 -1.200077 0.163865 -v 0.200686 -1.189120 0.169557 -v 0.192514 -1.240320 0.171183 -v 0.185405 -1.244977 0.172965 -v 0.178390 -1.230467 0.174303 -v 0.128115 -1.073432 0.149808 -v 0.163966 -1.207888 0.063281 -v 0.168756 -1.346606 0.061170 -v 0.176192 -1.343545 0.061229 -v 0.186987 -1.319938 0.062948 -v 0.215978 -1.341927 0.150886 -v 0.146108 -1.265037 0.166442 -v 0.134181 -1.269629 0.157460 -v 0.128955 -1.301732 0.082824 -v 0.139497 -1.293539 0.072629 -v 0.148186 -1.288128 0.067461 -v 0.156306 -1.218754 0.065199 -v 0.221755 -1.559373 0.107366 -v 0.221840 -1.557574 0.122022 -v 0.223419 -1.334459 0.138805 -v 0.158037 -1.348199 0.170463 -v 0.113998 -1.562992 0.114367 -v 0.118842 -1.398690 0.097559 -v 0.160304 -1.402893 0.062130 -v 0.201137 -1.566795 0.071993 -v 0.204214 -1.408360 0.160202 -v 0.195714 -1.408084 0.165356 -v 0.167011 -1.406479 0.171094 -v 0.116082 -1.566085 0.129271 -v 0.189153 -1.566792 0.065293 -v 0.211442 -1.563899 0.082353 -v 0.218372 -1.562634 0.094367 -v 0.217093 -1.563008 0.139850 -v 0.186536 -1.559283 0.166311 -v 0.178938 -1.559005 0.168436 -v 0.171222 -1.548220 0.169466 -v 0.152416 -1.571707 0.167192 -v 0.133710 -1.568379 0.157034 -v 0.121870 -1.567497 0.142833 -v 0.123981 -1.564215 0.084260 -v 0.133708 -1.562778 0.073624 -v 0.150724 -1.424875 0.064686 -v 0.160816 -1.563008 0.061901 -v 0.175304 -1.567895 0.061742 -v 0.206562 -1.563647 0.154069 -v 0.195161 -1.564062 0.162677 -v 0.166498 -1.629682 0.168984 -v 0.116480 -1.567875 0.100974 -v 0.141991 -1.566389 0.068331 -v 0.150984 -1.566193 0.064412 -v 0.161340 -1.627469 0.063207 -v 0.175563 -1.629081 0.061354 -v 0.188544 -1.622472 0.063087 -v 0.206656 -1.617732 0.071532 -v 0.224066 -1.613279 0.094197 -v 0.227679 -1.612345 0.123418 -v 0.220448 -1.614061 0.143005 -v 0.212014 -1.616096 0.154116 -v 0.200913 -1.618713 0.162702 -v 0.192276 -1.621000 0.166586 -v 0.182209 -1.622361 0.169107 -v 0.143762 -1.633149 0.159446 -v 0.126291 -1.637262 0.136795 -v 0.122655 -1.638113 0.107611 -v 0.129845 -1.636298 0.088026 -v 0.138243 -1.634211 0.076923 -v 0.149262 -1.631439 0.068343 -v 0.187561 -1.677542 0.061858 -v 0.201526 -1.674081 0.061745 -v 0.212850 -1.666081 0.065334 -v 0.228180 -1.657599 0.076197 -v 0.239933 -1.651007 0.097726 -v 0.242282 -1.649695 0.123360 -v 0.232049 -1.655250 0.149851 -v 0.214723 -1.664262 0.164769 -v 0.200667 -1.671101 0.169138 -v 0.189917 -1.680450 0.169249 -v 0.177021 -1.685633 0.165647 -v 0.161610 -1.693967 0.154764 -v 0.149760 -1.700319 0.133254 -v 0.147397 -1.701600 0.107662 -v 0.157547 -1.696019 0.081228 -v 0.174502 -1.686537 0.066307 -v 0.214152 -1.725621 0.063130 -v 0.231518 -1.714395 0.061752 -v 0.244813 -1.700100 0.067562 -v 0.259883 -1.685718 0.087544 -v 0.265505 -1.680450 0.115950 -v 0.259619 -1.685781 0.143752 -v 0.249192 -1.695199 0.158696 -v 0.239865 -1.703385 0.165722 -v 0.229338 -1.711453 0.169172 -v 0.221699 -1.723427 0.169241 -v 0.206162 -1.735348 0.163398 -v 0.190614 -1.749151 0.143415 -v 0.185179 -1.754027 0.118749 -v 0.188257 -1.751195 0.093759 -v 0.197064 -1.743028 0.077000 -v 0.205331 -1.735340 0.068426 -v 0.248805 -1.747509 0.061678 -v 0.269795 -1.747025 0.061761 -v 0.279592 -1.730209 0.067549 -v 0.291357 -1.713011 0.087503 -v 0.295794 -1.706682 0.115886 -v 0.291135 -1.713044 0.143659 -v 0.282835 -1.724382 0.158655 -v 0.275331 -1.734275 0.165705 -v 0.288306 -1.755876 0.168567 -v 0.282277 -1.760850 0.169439 -v 0.261973 -1.757979 0.169232 -v 0.248930 -1.772519 0.163412 -v 0.236454 -1.789156 0.143457 -v 0.231888 -1.795366 0.115113 -v 0.236441 -1.788966 0.087387 -v 0.244560 -1.777532 0.072384 -v 0.251615 -1.767359 0.065328 -v 0.306372 -1.783284 0.061684 -v 0.325381 -1.778971 0.061709 -v 0.317752 -1.759168 0.065327 -v 0.322888 -1.748084 0.072257 -v 0.329197 -1.735230 0.087465 -v 0.332523 -1.728227 0.115831 -v 0.328983 -1.735223 0.143585 -v 0.322661 -1.747777 0.158624 -v 0.316839 -1.758753 0.165693 -v 0.317066 -1.788548 0.169318 -v 0.299639 -1.795710 0.165651 -v 0.293999 -1.806534 0.158715 -v 0.287647 -1.819353 0.143498 -v 0.284141 -1.826248 0.115165 -v 0.287545 -1.819211 0.087450 -v 0.293662 -1.806579 0.072404 -v 0.298882 -1.795333 0.065330 -v 0.371502 -1.812894 0.062457 -v 0.372656 -1.785093 0.063690 -v 0.368583 -1.760931 0.076072 -v 0.373121 -1.746706 0.101002 -v 0.372988 -1.746779 0.130246 -v 0.368075 -1.760730 0.154897 -v 0.362140 -1.777135 0.165684 -v 0.381672 -1.793965 0.168481 -v 0.420801 -1.814592 0.169602 -v 0.362853 -1.815349 0.167613 -v 0.344445 -1.832561 0.154889 -v 0.339522 -1.846625 0.129976 -v 0.339476 -1.846492 0.100765 -v 0.344016 -1.832455 0.076140 -v 0.349223 -1.815816 0.065332 -v 0.422866 -1.813614 0.061348 -v 0.410141 -1.790107 0.065324 -v 0.413496 -1.773047 0.076044 -v 0.416390 -1.758377 0.100966 -v 0.416257 -1.758441 0.130205 -v 0.412908 -1.772812 0.154853 -v 0.408801 -1.789800 0.165678 -v 0.401493 -1.829939 0.165656 -v 0.397544 -1.846868 0.154918 -v 0.394194 -1.861410 0.130014 -v 0.394116 -1.861302 0.100804 -v 0.397013 -1.846864 0.076176 -v 0.400290 -1.829705 0.065333 -v 0.477780 -1.834019 0.063062 -v 0.481984 -1.806882 0.063117 -v 0.456932 -1.785696 0.071371 -v 0.458512 -1.767680 0.093917 -v 0.458832 -1.763786 0.123037 -v 0.457404 -1.775032 0.149410 -v 0.455407 -1.790804 0.162517 -v 0.481198 -1.806941 0.167909 -v 0.518610 -1.821038 0.169614 -v 0.482808 -1.834373 0.167902 -v 0.449695 -1.849593 0.159596 -v 0.447281 -1.867484 0.137063 -v 0.446734 -1.871349 0.107967 -v 0.447869 -1.860091 0.081613 -v 0.449459 -1.844282 0.068495 -v 0.515740 -1.819923 0.061350 -v 0.496626 -1.793697 0.068369 -v 0.496378 -1.782480 0.076794 -v 0.496233 -1.770162 0.093858 -v 0.496111 -1.766226 0.123001 -v 0.496083 -1.777549 0.149388 -v 0.496087 -1.793463 0.162517 -v 0.497039 -1.847048 0.162597 -v 0.497003 -1.858247 0.154173 -v 0.497062 -1.870548 0.137109 -v 0.497025 -1.874475 0.108006 -v 0.496842 -1.863183 0.081647 -v 0.496588 -1.847283 0.068503 -v 0.541992 -1.830265 0.063049 -v 0.539667 -1.803057 0.063121 -v 0.536935 -1.790458 0.068350 -v 0.535020 -1.779396 0.076768 -v 0.533020 -1.767242 0.093791 -v 0.532309 -1.763332 0.122951 -v 0.533974 -1.774508 0.149349 -v 0.536382 -1.790258 0.162509 -v 0.537681 -1.803676 0.167956 -v 0.543888 -1.830284 0.167857 -v 0.545278 -1.843097 0.162610 -v 0.546928 -1.854177 0.154191 -v 0.548817 -1.866328 0.137164 -v 0.549377 -1.870245 0.108050 -v 0.547507 -1.859130 0.081682 -v 0.544896 -1.843425 0.068508 -v 0.592718 -1.818221 0.063044 -v 0.612488 -1.797360 0.061355 -v 0.586674 -1.791547 0.063134 -v 0.582064 -1.779564 0.068340 -v 0.577117 -1.764439 0.081204 -v 0.573501 -1.753361 0.108120 -v 0.574607 -1.757117 0.136874 -v 0.579610 -1.774232 0.159458 -v 0.584573 -1.792568 0.167959 -v 0.616872 -1.796797 0.169626 -v 0.594848 -1.817937 0.167845 -v 0.597999 -1.830378 0.162622 -v 0.602628 -1.845585 0.149739 -v 0.606027 -1.856692 0.122856 -v 0.604796 -1.852980 0.094136 -v 0.599154 -1.836100 0.071568 -v 0.645358 -1.797730 0.063040 -v 0.635900 -1.771625 0.063265 -v 0.629513 -1.760938 0.068324 -v 0.622528 -1.746660 0.081146 -v 0.617420 -1.736140 0.108074 -v 0.619025 -1.739686 0.136836 -v 0.626318 -1.755936 0.159432 -v 0.633775 -1.773453 0.167961 -v 0.647874 -1.796764 0.167901 -v 0.652245 -1.809035 0.162637 -v 0.658890 -1.823452 0.149798 -v 0.663806 -1.834022 0.122903 -v 0.662090 -1.830532 0.094172 -v 0.654192 -1.814601 0.071589 -v 0.697337 -1.768732 0.063037 -v 0.724962 -1.722380 0.061870 -v 0.684695 -1.744282 0.063177 -v 0.676329 -1.734679 0.068305 -v 0.667383 -1.721562 0.081081 -v 0.660820 -1.711827 0.108026 -v 0.662910 -1.715086 0.136800 -v 0.672458 -1.730100 0.159410 -v 0.682417 -1.746367 0.167964 -v 0.734987 -1.733211 0.169234 -v 0.700027 -1.767500 0.167816 -v 0.705656 -1.778986 0.162653 -v 0.714276 -1.792292 0.149860 -v 0.720686 -1.802081 0.122950 -v 0.718510 -1.798884 0.094206 -v 0.708449 -1.784240 0.071610 -v 0.729526 -1.735673 0.061602 -v 0.724144 -1.705454 0.065281 -v 0.715689 -1.696720 0.072113 -v 0.708773 -1.689329 0.081942 -v 0.701878 -1.681966 0.100616 -v 0.701783 -1.681934 0.129922 -v 0.711546 -1.692780 0.154625 -v 0.723371 -1.705883 0.165656 -v 0.720471 -1.729340 0.169364 -v 0.751549 -1.735360 0.165676 -v 0.759618 -1.744398 0.158843 -v 0.766417 -1.751858 0.149023 -v 0.773185 -1.759316 0.130353 -v 0.773212 -1.759403 0.101078 -v 0.763231 -1.748786 0.076392 -v 0.751139 -1.735935 0.065349 -v 0.781351 -1.682102 0.061682 -v 0.772943 -1.670192 0.061926 -v 0.762329 -1.663716 0.065269 -v 0.752454 -1.656639 0.072081 -v 0.744301 -1.650602 0.081918 -v 0.736184 -1.644605 0.100563 -v 0.736072 -1.644568 0.129889 -v 0.747642 -1.653439 0.154603 -v 0.761678 -1.664165 0.165653 -v 0.771698 -1.674815 0.169313 -v 0.785768 -1.679181 0.169068 -v 0.794647 -1.688125 0.165685 -v 0.804231 -1.695514 0.158875 -v 0.812292 -1.701625 0.149048 -v 0.820304 -1.707734 0.130407 -v 0.820371 -1.707823 0.101110 -v 0.808651 -1.699183 0.076412 -v 0.794435 -1.688701 0.065350 -v 0.814611 -1.628407 0.061678 -v 0.803576 -1.618935 0.061949 -v 0.792117 -1.614674 0.065260 -v 0.781017 -1.609756 0.072053 -v 0.771781 -1.605508 0.081896 -v 0.762600 -1.601307 0.100517 -v 0.762467 -1.601280 0.129864 -v 0.775614 -1.607559 0.154587 -v 0.791577 -1.615154 0.165652 -v 0.803709 -1.623299 0.169317 -v 0.818211 -1.625337 0.169044 -v 0.828705 -1.631862 0.165700 -v 0.839607 -1.637103 0.158911 -v 0.848779 -1.641433 0.149077 -v 0.857885 -1.645765 0.130458 -v 0.857990 -1.645844 0.101134 -v 0.844750 -1.639794 0.076422 -v 0.828677 -1.632439 0.065349 -v 0.841053 -1.547934 0.061687 -v 0.823561 -1.559664 0.061901 -v 0.811238 -1.558173 0.065252 -v 0.799015 -1.556127 0.072189 -v 0.788989 -1.553768 0.082339 -v 0.779192 -1.551687 0.101500 -v 0.779609 -1.551755 0.130844 -v 0.794286 -1.554856 0.155258 -v 0.811418 -1.558509 0.165858 -v 0.824043 -1.545029 0.168432 -v 0.831177 -1.544178 0.169354 -v 0.839432 -1.563365 0.168938 -v 0.850650 -1.566691 0.165727 -v 0.862058 -1.569611 0.159171 -v 0.871994 -1.571523 0.149584 -v 0.881964 -1.573633 0.131489 -v 0.882647 -1.573769 0.102110 -v 0.868850 -1.570829 0.077130 -v 0.851433 -1.567113 0.065586 -v 0.831680 -1.496470 0.061771 -v 0.824901 -1.502735 0.062687 -v 0.816894 -1.493545 0.065380 -v 0.804911 -1.493016 0.072220 -v 0.794990 -1.491765 0.081894 -v 0.787054 -1.494195 0.094724 -v 0.783309 -1.493982 0.109010 -v 0.783510 -1.493959 0.123492 -v 0.787391 -1.494110 0.137013 -v 0.799338 -1.491287 0.154568 -v 0.815612 -1.501439 0.165592 -v 0.845660 -1.494380 0.168938 -v 0.857171 -1.495433 0.165846 -v 0.869512 -1.496077 0.159054 -v 0.879756 -1.499021 0.148611 -v 0.886964 -1.499330 0.136174 -v 0.890683 -1.499482 0.121885 -v 0.890457 -1.499448 0.107398 -v 0.886548 -1.499231 0.093874 -v 0.879408 -1.498844 0.081953 -v 0.869476 -1.498286 0.072223 -v 0.857148 -1.497425 0.065259 -v 0.844886 -1.312005 0.062143 -v 0.821416 -1.308375 0.064877 -v 0.814957 -1.315493 0.067520 -v 0.806783 -1.314465 0.072424 -v 0.797508 -1.331767 0.080870 -v 0.794703 -1.309511 0.146086 -v 0.805953 -1.333296 0.158196 -v 0.843041 -1.298462 0.168918 -v 0.858098 -1.377095 0.165507 -v 0.870222 -1.383803 0.158708 -v 0.796993 -1.124873 0.088111 -v 0.791371 -1.125120 0.101276 -v 0.789430 -1.125550 0.115684 -v 0.791324 -1.126243 0.129631 -v 0.853714 -1.125591 0.167758 -v 0.863135 -1.114202 0.165063 -v 0.874977 -1.118675 0.158383 -v 0.884966 -1.120972 0.148669 -v 0.892174 -1.122164 0.136533 -v 0.896007 -1.122782 0.122513 -v 0.895968 -1.123137 0.108221 -v 0.892274 -1.123260 0.094793 -v 0.885319 -1.123238 0.082862 -v 0.875502 -1.123208 0.073053 -v 0.863199 -1.123746 0.065995 -v 0.863770 0.270140 0.064653 -v 0.820075 -0.146642 0.141893 -v 0.828374 -0.145852 0.152639 -v 0.839360 -0.141999 0.160991 -v 0.862260 0.272069 0.166052 -v 0.880525 0.304285 0.063876 -v 0.853304 0.085742 0.066262 -v 0.847058 0.082160 0.068862 -v 0.839073 0.079830 0.073622 -v 0.827901 0.093780 0.085094 -v 0.815391 -0.119390 0.101599 -v 0.813720 -0.110260 0.122481 -v 0.872931 0.437981 0.167012 -v 0.880560 0.351207 0.167026 -v 0.880543 0.065907 0.166564 -v 0.887406 0.082198 0.164737 -v 0.893656 0.085087 0.162120 -v 0.897300 -0.119670 0.157547 -v 0.911368 0.083920 0.147844 -v 0.918418 0.083739 0.135971 -v 0.922152 0.083697 0.122264 -v 0.922100 0.083783 0.108303 -v 0.918479 0.084091 0.095195 -v 0.911686 0.084759 0.083561 -v 0.898013 -0.111743 0.073844 -v 0.890208 0.090384 0.067136 -v 0.826429 0.334630 0.095307 -v 0.822762 0.334899 0.108937 -v 0.822863 0.335243 0.122799 -v 0.826506 0.335665 0.135793 -v 0.833287 0.336164 0.147306 -v 0.842809 0.336776 0.156748 -v 0.854706 0.338323 0.163532 -v 0.905376 0.338626 0.156952 -v 0.866453 0.440684 0.064521 -v 0.854792 0.355416 0.067467 -v 0.842762 0.437525 0.074433 -v 0.833127 0.436386 0.083969 -v 0.879946 0.444216 0.166560 -v 0.893459 0.357585 0.163519 -v 0.914744 0.358698 0.147621 -v 0.924386 0.359307 0.129836 -v 0.924523 0.359511 0.101787 -v 0.909728 0.353848 0.077195 -v 0.893578 0.427595 0.067790 -v 0.875407 0.463653 0.063902 -v 0.853960 0.441595 0.067763 -v 0.823237 0.439757 0.101284 -v 0.823086 0.440301 0.129204 -v 0.836839 0.442424 0.152691 -v 0.852881 0.478799 0.163617 -v 0.860894 0.480375 0.165319 -v 0.892196 0.446758 0.163200 -v 0.903623 0.447704 0.156751 -v 0.913235 0.448424 0.147396 -v 0.922789 0.449125 0.129688 -v 0.922924 0.448724 0.101788 -v 0.909087 0.447665 0.078289 -v 0.866801 0.474556 0.062919 -v 0.857763 0.472179 0.064155 -v 0.849166 0.470193 0.066798 -v 0.837869 0.468105 0.073317 -v 0.828388 0.466560 0.082709 -v 0.818833 0.465792 0.100442 -v 0.818445 0.467787 0.128257 -v 0.827753 0.479931 0.148356 -v 0.837134 0.494559 0.157473 -v 0.874521 0.482080 0.165122 -v 0.885936 0.485388 0.161745 -v 0.897115 0.487553 0.155238 -v 0.906542 0.489141 0.145862 -v 0.915971 0.490360 0.128107 -v 0.916273 0.488708 0.100295 -v 0.902833 0.503693 0.081145 -v 0.893170 0.502671 0.071693 -v 0.883832 0.492989 0.065716 -v 0.858068 0.523192 0.057830 -v 0.858130 0.503447 0.059943 -v 0.844356 0.496480 0.062947 -v 0.830008 0.493721 0.070741 -v 0.820760 0.491541 0.080173 -v 0.811253 0.490540 0.097939 -v 0.810433 0.493966 0.125573 -v 0.813714 0.512492 0.141137 -v 0.842478 0.527003 0.159482 -v 0.857243 0.516130 0.162138 -v 0.872341 0.519671 0.159580 -v 0.885850 0.524932 0.151407 -v 0.895036 0.527161 0.141983 -v 0.904370 0.528558 0.124174 -v 0.905078 0.525413 0.096538 -v 0.841142 0.526371 0.056617 -v 0.829516 0.522461 0.060103 -v 0.818974 0.518421 0.066878 -v 0.806247 0.514722 0.081330 -v 0.798357 0.515575 0.108177 -v 0.795581 0.532221 0.125513 -v 0.807666 0.545420 0.145194 -v 0.806787 0.570055 0.147673 -v 0.835175 0.556830 0.155214 -v 0.849821 0.548656 0.156764 -v 0.860336 0.554947 0.152811 -v 0.875091 0.560907 0.142358 -v 0.888769 0.563737 0.118588 -v 0.889892 0.559555 0.091143 -v 0.873802 0.568740 0.070897 -v 0.865081 0.564122 0.061732 -v 0.852486 0.561832 0.054703 -v 0.820926 0.561343 0.049490 -v 0.815052 0.547084 0.055175 -v 0.797707 0.549813 0.061920 -v 0.789020 0.547168 0.072073 -v 0.782705 0.546343 0.084352 -v 0.779213 0.547681 0.097986 -v 0.778848 0.551160 0.111340 -v 0.786361 0.563771 0.133939 -v 0.830195 0.583317 0.149764 -v 0.835346 0.595097 0.145008 -v 0.856816 0.592237 0.136020 -v 0.869944 0.595867 0.112110 -v 0.871372 0.591093 0.084803 -v 0.777935 0.621183 0.035550 -v 0.790563 0.579967 0.047625 -v 0.782356 0.580143 0.050397 -v 0.764922 0.579838 0.121063 -v 0.776694 0.597053 0.137649 -v 0.819239 0.627391 0.134102 -v 0.836386 0.620436 0.128823 -v 0.834729 0.634573 0.115428 -v 0.850110 0.624685 0.098498 -v 0.848694 0.617531 0.071972 -v 0.828644 0.622125 0.053330 -v 0.819840 0.615054 0.045837 -v 0.809700 0.606602 0.041485 -v 0.758923 0.590899 0.054812 -v 0.750938 0.588427 0.065603 -v 0.745686 0.587648 0.078374 -v 0.743167 0.589284 0.092063 -v 0.742311 0.597335 0.110414 -v 0.750672 0.607581 0.126044 -v 0.782999 0.609957 0.141000 -v 0.773671 0.639093 0.136929 -v 0.760330 0.667376 0.130148 -v 0.803176 0.658088 0.119480 -v 0.826786 0.651162 0.092258 -v 0.825371 0.643969 0.065744 -v 0.777486 0.638357 0.034579 -v 0.761056 0.617582 0.037540 -v 0.736646 0.635290 0.129488 -v 0.743804 0.646485 0.133217 -v 0.756638 0.684496 0.123027 -v 0.788396 0.680323 0.105224 -v 0.790030 0.684321 0.091914 -v 0.790247 0.684771 0.077852 -v 0.800273 0.668192 0.060471 -v 0.789539 0.652573 0.040937 -v 0.751825 0.659743 0.031052 -v 0.734862 0.660577 0.029598 -v 0.728928 0.652795 0.030860 -v 0.728616 0.637735 0.035964 -v 0.702263 0.629033 0.059240 -v 0.701065 0.624959 0.072490 -v 0.701214 0.624413 0.086490 -v 0.703208 0.626986 0.099625 -v 0.706980 0.632188 0.111262 -v 0.712368 0.639597 0.120870 -v 0.733951 0.666411 0.132229 -v 0.741242 0.673377 0.131043 -v 0.773817 0.690311 0.056789 -v 0.763193 0.674513 0.037263 -v 0.718348 0.684382 0.029577 -v 0.683503 0.653313 0.044632 -v 0.699784 0.662298 0.127610 -v 0.715280 0.667658 0.131550 -v 0.687250 0.720426 0.126937 -v 0.707947 0.716048 0.122883 -v 0.720586 0.720775 0.113174 -v 0.727173 0.725060 0.101816 -v 0.731175 0.727253 0.088698 -v 0.749093 0.714703 0.068136 -v 0.741866 0.703513 0.044334 -v 0.732651 0.690022 0.033500 -v 0.634047 0.710012 0.031451 -v 0.637549 0.701377 0.032926 -v 0.666475 0.677515 0.035662 -v 0.656591 0.655650 0.062555 -v 0.655296 0.652715 0.076148 -v 0.655722 0.653123 0.090090 -v 0.657745 0.656738 0.103047 -v 0.661132 0.663228 0.114405 -v 0.665586 0.672300 0.123569 -v 0.679241 0.690338 0.132006 -v 0.664543 0.709379 0.132704 -v 0.639805 0.728341 0.132545 -v 0.686506 0.711964 0.130042 -v 0.717245 0.734603 0.068318 -v 0.710275 0.723205 0.044514 -v 0.687765 0.712879 0.032259 -v 0.648691 0.713077 0.030308 -v 0.634786 0.680120 0.044151 -v 0.631875 0.672078 0.054426 -v 0.657793 0.692403 0.130942 -v 0.663684 0.744014 0.119578 -v 0.666727 0.752004 0.109330 -v 0.685224 0.751950 0.090019 -v 0.683297 0.749583 0.062397 -v 0.675767 0.735023 0.040545 -v 0.572495 0.734789 0.033087 -v 0.609617 0.700932 0.037361 -v 0.603226 0.676565 0.067607 -v 0.602993 0.674241 0.081361 -v 0.603907 0.675451 0.094995 -v 0.605848 0.679917 0.107472 -v 0.608691 0.687238 0.118223 -v 0.612344 0.696992 0.126668 -v 0.616863 0.708632 0.132191 -v 0.604629 0.723958 0.134541 -v 0.627741 0.745656 0.128338 -v 0.634715 0.769843 0.098116 -v 0.635052 0.772151 0.084320 -v 0.649588 0.763460 0.063373 -v 0.629667 0.759003 0.047378 -v 0.626216 0.749195 0.038954 -v 0.623711 0.740953 0.034827 -v 0.614711 0.732892 0.032120 -v 0.577925 0.698548 0.046105 -v 0.576329 0.690101 0.056330 -v 0.597177 0.765646 0.121826 -v 0.598978 0.774048 0.111610 -v 0.615135 0.774584 0.064527 -v 0.550890 0.728055 0.034894 -v 0.549690 0.715543 0.039477 -v 0.546796 0.690400 0.069595 -v 0.546912 0.687999 0.083341 -v 0.547677 0.689260 0.096989 -v 0.549019 0.693895 0.109493 -v 0.550890 0.701485 0.120279 -v 0.553318 0.711588 0.128762 -v 0.535329 0.724125 0.133730 -v 0.542594 0.730013 0.135653 -v 0.517699 0.746785 0.137675 -v 0.559217 0.749640 0.135260 -v 0.560980 0.762049 0.130666 -v 0.564702 0.787056 0.100540 -v 0.564769 0.789433 0.086759 -v 0.564202 0.788130 0.073077 -v 0.563082 0.783441 0.060553 -v 0.561513 0.775791 0.049768 -v 0.559580 0.765611 0.041289 -v 0.556863 0.753479 0.035726 -v 0.434845 0.754767 0.036830 -v 0.518053 0.708908 0.048077 -v 0.517527 0.700297 0.058263 -v 0.527631 0.777745 0.124075 -v 0.528439 0.786329 0.113888 -v 0.488148 0.741306 0.035590 -v 0.484770 0.728999 0.038359 -v 0.487263 0.721490 0.041613 -v 0.487262 0.696881 0.071233 -v 0.487711 0.694467 0.084977 -v 0.488425 0.695764 0.098632 -v 0.489398 0.700479 0.111152 -v 0.490707 0.708186 0.121958 -v 0.492698 0.718434 0.130460 -v 0.438982 0.743462 0.138791 -v 0.490626 0.757063 0.137124 -v 0.491499 0.769596 0.132542 -v 0.492574 0.794941 0.102457 -v 0.492433 0.797339 0.088679 -v 0.492094 0.796009 0.075000 -v 0.491602 0.791254 0.062471 -v 0.490993 0.783505 0.051669 -v 0.489009 0.769927 0.041069 -v 0.466937 0.712404 0.048530 -v 0.454525 0.700414 0.063722 -v 0.469359 0.781618 0.124687 -v 0.450158 0.793117 0.110396 -v 0.436042 0.741244 0.036141 -v 0.437767 0.728595 0.038905 -v 0.441796 0.694510 0.092374 -v 0.442218 0.704379 0.117937 -v 0.441793 0.718932 0.130918 -v 0.441342 0.731272 0.136540 -v 0.440512 0.757197 0.137568 -v 0.398942 0.769463 0.133978 -v 0.435914 0.797783 0.082246 -v 0.435239 0.787919 0.056704 -v 0.435205 0.773357 0.043738 -v 0.436337 0.760698 0.037936 -v 0.381097 0.751402 0.041591 -v 0.383566 0.736935 0.040443 -v 0.386442 0.723967 0.042924 -v 0.389466 0.708904 0.050857 -v 0.376441 0.694653 0.066599 -v 0.382234 0.689450 0.079113 -v 0.401975 0.690591 0.094321 -v 0.404731 0.700266 0.119887 -v 0.404374 0.714926 0.133362 -v 0.403112 0.727193 0.139222 -v 0.401622 0.736999 0.141283 -v 0.399779 0.747121 0.141591 -v 0.396882 0.759919 0.138839 -v 0.328570 0.762840 0.142083 -v 0.386835 0.788258 0.112987 -v 0.381305 0.792051 0.085850 -v 0.378802 0.781474 0.060702 -v 0.379292 0.766988 0.048145 -v 0.337027 0.733151 0.053612 -v 0.340253 0.723890 0.052857 -v 0.345669 0.711798 0.054772 -v 0.345726 0.697592 0.062966 -v 0.375776 0.683201 0.102781 -v 0.381500 0.694233 0.127318 -v 0.381184 0.708897 0.140300 -v 0.378916 0.721033 0.146305 -v 0.375634 0.733242 0.148867 -v 0.370353 0.745061 0.148812 -v 0.364245 0.756651 0.145336 -v 0.343745 0.761819 0.143879 -v 0.345249 0.778491 0.118767 -v 0.337045 0.779594 0.099529 -v 0.331583 0.768685 0.074935 -v 0.332161 0.754181 0.061860 -v 0.334436 0.742185 0.055860 -v 0.304668 0.712943 0.070704 -v 0.310724 0.701215 0.070495 -v 0.318436 0.690659 0.073107 -v 0.322794 0.673020 0.087919 -v 0.337715 0.670350 0.095610 -v 0.348990 0.670155 0.110040 -v 0.355364 0.673475 0.121804 -v 0.359437 0.679560 0.133055 -v 0.361218 0.687984 0.143187 -v 0.360697 0.698301 0.151623 -v 0.357874 0.709973 0.157778 -v 0.353107 0.721845 0.161031 -v 0.346602 0.733018 0.161607 -v 0.338908 0.743793 0.158897 -v 0.308468 0.759332 0.138744 -v 0.301433 0.760099 0.126500 -v 0.298243 0.757828 0.105372 -v 0.295244 0.740868 0.084230 -v 0.299212 0.724600 0.074496 -v 0.274248 0.702696 0.092531 -v 0.284100 0.690172 0.087672 -v 0.268149 0.660674 0.107433 -v 0.263243 0.640745 0.121650 -v 0.283334 0.626341 0.142479 -v 0.269297 0.702731 0.203767 -v 0.271495 0.738612 0.125378 -v 0.253232 0.715578 0.123050 -v 0.269582 0.717033 0.100752 -v 0.252696 0.685144 0.110157 -v 0.259719 0.670668 0.107482 -v 0.272587 0.631273 0.130174 -v 0.289062 0.623927 0.156499 -v 0.319624 0.645715 0.158242 -v 0.322454 0.662434 0.179487 -v 0.317685 0.678131 0.189874 -v 0.311187 0.689117 0.194472 -v 0.304639 0.700626 0.194928 -v 0.295961 0.710480 0.193001 -v 0.250079 0.712159 0.188782 -v 0.240610 0.713227 0.173769 -v 0.235260 0.710603 0.161219 -v 0.231962 0.705011 0.149403 -v 0.247958 0.699552 0.118421 -v 0.232077 0.669889 0.124410 -v 0.238611 0.655835 0.121909 -v 0.246423 0.645151 0.122493 -v 0.290352 0.624968 0.178271 -v 0.292478 0.641350 0.200006 -v 0.287026 0.660713 0.211423 -v 0.279466 0.674478 0.214558 -v 0.272656 0.685903 0.213358 -v 0.221646 0.693904 0.207955 -v 0.226791 0.689635 0.135706 -v 0.211175 0.648705 0.133747 -v 0.216505 0.636403 0.133823 -v 0.222559 0.624822 0.137273 -v 0.211600 0.608307 0.149088 -v 0.192209 0.589711 0.167484 -v 0.239784 0.603346 0.161160 -v 0.248494 0.601240 0.183999 -v 0.252589 0.611750 0.208959 -v 0.251186 0.625696 0.222065 -v 0.248322 0.637526 0.228315 -v 0.244475 0.649054 0.231132 -v 0.239646 0.660366 0.231249 -v 0.233634 0.672046 0.228076 -v 0.208008 0.673839 0.228536 -v 0.207715 0.696785 0.182833 -v 0.203153 0.687119 0.157525 -v 0.204328 0.672989 0.143521 -v 0.207062 0.661273 0.137018 -v 0.178249 0.637609 0.141677 -v 0.180782 0.625044 0.142222 -v 0.184357 0.612570 0.146143 -v 0.198792 0.584620 0.194861 -v 0.200515 0.593839 0.220687 -v 0.199207 0.607868 0.234255 -v 0.197111 0.619849 0.240449 -v 0.194726 0.632450 0.243140 -v 0.190987 0.644856 0.242769 -v 0.187126 0.657187 0.238900 -v 0.175385 0.679280 0.217539 -v 0.172480 0.685061 0.190221 -v 0.168298 0.677536 0.169819 -v 0.171528 0.670882 0.158640 -v 0.172863 0.661970 0.150675 -v 0.175030 0.649922 0.144457 -v 0.123764 0.627823 0.145254 -v 0.126602 0.615264 0.145774 -v 0.130123 0.597512 0.152124 -v 0.134693 0.579116 0.172031 -v 0.138489 0.573652 0.199266 -v 0.139676 0.579505 0.218667 -v 0.092761 0.579381 0.233364 -v 0.138509 0.609160 0.244577 -v 0.134215 0.621493 0.247447 -v 0.133926 0.634442 0.246691 -v 0.131072 0.652306 0.240286 -v 0.126556 0.670736 0.220375 -v 0.123617 0.676335 0.193048 -v 0.122748 0.653038 0.153770 -v 0.123264 0.640820 0.147768 -v 0.068454 0.622010 0.153283 -v 0.069685 0.609840 0.153874 -v 0.071879 0.597109 0.157792 -v 0.076157 0.581608 0.168243 -v 0.083807 0.569152 0.191604 -v 0.071313 0.568562 0.219496 -v 0.096510 0.589748 0.242216 -v 0.097084 0.604236 0.249544 -v 0.096687 0.617722 0.252172 -v 0.095288 0.631096 0.251312 -v 0.091810 0.649163 0.244540 -v 0.084685 0.666879 0.224743 -v 0.061620 0.669640 0.211574 -v 0.063257 0.668568 0.195089 -v 0.070807 0.661266 0.173649 -v 0.068530 0.646871 0.161147 -v 0.068016 0.634436 0.155498 -v 0.017351 0.625372 0.176177 -v 0.018271 0.609464 0.175409 -v 0.021189 0.593106 0.180196 -v 0.027430 0.577653 0.190370 -v 0.039306 0.566000 0.212380 -v 0.050097 0.571822 0.244281 -v 0.063968 0.585314 0.255205 -v 0.068264 0.602888 0.262579 -v 0.069204 0.615906 0.264359 -v 0.067821 0.628700 0.263729 -v 0.063206 0.646565 0.257873 -v 0.051839 0.664318 0.240294 -v 0.013286 0.663441 0.213122 -v 0.023205 0.654416 0.189689 -v 0.018852 0.638273 0.179726 -v -0.024148 0.620173 0.205447 -v -0.025077 0.604552 0.208003 -v -0.022667 0.592177 0.212103 -v -0.018429 0.581389 0.218605 -v -0.012796 0.572861 0.227299 -v -0.006538 0.567195 0.238234 -v -0.004025 0.564877 0.259346 -v 0.033760 0.584500 0.277499 -v 0.039885 0.601791 0.283445 -v 0.041380 0.615423 0.284923 -v 0.039481 0.630169 0.284243 -v 0.035215 0.642927 0.280689 -v 0.029257 0.653424 0.274847 -v 0.021292 0.661521 0.267379 -v 0.002124 0.667773 0.260548 -v -0.070492 0.663254 0.283893 -v -0.015438 0.650642 0.214930 -v -0.021212 0.637514 0.208926 -v -0.032591 0.581207 0.332454 -v -0.028074 0.591544 0.339554 -v -0.023911 0.616793 0.345973 -v -0.079597 0.669012 0.309664 -v -0.087412 0.639370 0.270570 -v -0.090533 0.627539 0.268525 -v -0.090308 0.613197 0.267694 -v -0.088556 0.600228 0.270282 -v -0.081566 0.583432 0.277314 -v -0.099684 0.573181 0.314451 -v -0.114160 0.569700 0.345637 -v -0.040403 0.570514 0.318671 -v -0.024524 0.603908 0.343771 -v -0.023379 0.630639 0.343701 -v -0.026169 0.643014 0.339823 -v -0.032666 0.658369 0.329501 -v -0.074702 0.669183 0.341403 -v -0.081962 0.650835 0.275159 -v -0.122539 0.631107 0.294391 -v -0.122702 0.614974 0.293666 -v -0.121261 0.601937 0.296457 -v -0.115706 0.585171 0.304659 -v -0.109655 0.571326 0.362091 -v -0.076832 0.578933 0.362279 -v -0.069902 0.593710 0.372987 -v -0.066547 0.605982 0.377214 -v -0.065452 0.619275 0.378902 -v -0.065490 0.632491 0.377203 -v -0.067827 0.644897 0.372987 -v -0.073706 0.660231 0.362383 -v -0.109167 0.672557 0.345840 -v -0.109405 0.661629 0.308643 -v -0.117757 0.646849 0.298878 -v -0.158868 0.629059 0.314425 -v -0.158807 0.613815 0.314338 -v -0.155522 0.592057 0.322225 -v -0.146892 0.574491 0.341564 -v -0.125928 0.580714 0.390162 -v -0.121203 0.595593 0.401904 -v -0.118839 0.608069 0.406689 -v -0.118115 0.621044 0.408396 -v -0.118132 0.634062 0.406849 -v -0.120528 0.651763 0.399601 -v -0.128003 0.669235 0.379806 -v -0.146619 0.666845 0.336449 -v -0.153755 0.653685 0.323639 -v -0.156783 0.642018 0.317415 -v -0.203247 0.629779 0.328404 -v -0.203360 0.619823 0.328144 -v -0.203136 0.609993 0.329534 -v -0.201642 0.592036 0.337439 -v -0.197406 0.574925 0.358549 -v -0.244138 0.571401 0.385502 -v -0.200830 0.573708 0.395205 -v -0.186814 0.582255 0.410273 -v -0.184356 0.597089 0.422184 -v -0.183263 0.609741 0.427237 -v -0.182935 0.622000 0.428810 -v -0.183084 0.634379 0.427541 -v -0.184425 0.651921 0.420422 -v -0.188493 0.669738 0.400003 -v -0.193442 0.674371 0.376413 -v -0.198052 0.668118 0.353712 -v -0.201012 0.654882 0.338991 -v -0.202462 0.643278 0.332127 -v -0.261476 0.623201 0.333416 -v -0.261544 0.610244 0.334830 -v -0.261210 0.592450 0.342453 -v -0.260598 0.574968 0.363580 -v -0.261587 0.581107 0.416463 -v -0.259959 0.596012 0.429110 -v -0.260266 0.608387 0.434436 -v -0.260218 0.621537 0.436338 -v -0.261105 0.634661 0.434884 -v -0.261598 0.647117 0.430018 -v -0.262575 0.662411 0.417880 -v -0.264134 0.673659 0.392736 -v -0.265756 0.663424 0.353356 -v -0.262937 0.648621 0.340626 -v -0.262490 0.636118 0.335278 -v -0.341581 0.618312 0.327937 -v -0.341057 0.605154 0.329360 -v -0.340560 0.587360 0.337034 -v -0.341755 0.569903 0.357951 -v -0.346215 0.565375 0.385148 -v -0.395950 0.567008 0.397684 -v -0.352979 0.584895 0.419567 -v -0.354070 0.602318 0.427988 -v -0.354959 0.615655 0.429988 -v -0.356694 0.628867 0.428358 -v -0.357643 0.646544 0.420583 -v -0.356785 0.663865 0.399678 -v -0.348951 0.668763 0.372479 -v -0.397242 0.656467 0.346727 -v -0.349266 0.648455 0.337574 -v -0.343914 0.631345 0.329699 -v -0.421934 0.603692 0.311864 -v -0.421197 0.587217 0.315415 -v -0.421226 0.570415 0.325402 -v -0.425054 0.555627 0.347984 -v -0.432530 0.553840 0.371166 -v -0.442774 0.571883 0.401352 -v -0.446420 0.589103 0.409574 -v -0.448625 0.605706 0.411667 -v -0.451463 0.621951 0.407520 -v -0.451939 0.638615 0.397380 -v -0.448911 0.653201 0.374642 -v -0.444509 0.654453 0.350670 -v -0.433291 0.636531 0.320694 -v -0.426391 0.619776 0.313319 -v -0.501130 0.583786 0.286211 -v -0.499291 0.565122 0.291852 -v -0.502030 0.545049 0.309619 -v -0.511872 0.535801 0.333805 -v -0.594299 0.521825 0.322599 -v -0.527736 0.548839 0.368144 -v -0.533543 0.564538 0.378210 -v -0.536937 0.577209 0.381581 -v -0.540635 0.590466 0.381092 -v -0.542848 0.609007 0.375243 -v -0.540595 0.628860 0.357319 -v -0.534546 0.637205 0.331658 -v -0.517671 0.624372 0.297682 -v -0.504320 0.603723 0.287329 -v -0.574134 0.564864 0.251190 -v -0.572700 0.539952 0.260784 -v -0.578790 0.521801 0.279777 -v -0.587541 0.516340 0.297779 -v -0.663831 0.497282 0.271254 -v -0.611628 0.531999 0.333395 -v -0.618838 0.548798 0.340833 -v -0.625574 0.568007 0.341378 -v -0.627320 0.592695 0.331557 -v -0.622156 0.610507 0.312094 -v -0.624164 0.612300 0.277900 -v -0.597361 0.608244 0.265719 -v -0.581148 0.590361 0.254204 -v -0.639764 0.550697 0.208964 -v -0.637162 0.525349 0.215257 -v -0.642981 0.504559 0.231157 -v -0.652510 0.496081 0.247115 -v -0.699266 0.481144 0.228272 -v -0.681824 0.504393 0.281549 -v -0.694387 0.525213 0.292317 -v -0.701751 0.544448 0.291212 -v -0.703755 0.563239 0.285307 -v -0.698576 0.583722 0.268949 -v -0.694566 0.590745 0.239908 -v -0.669514 0.589925 0.225605 -v -0.651009 0.574671 0.212953 -v -0.697109 0.540860 0.161144 -v -0.690820 0.518543 0.162699 -v -0.693323 0.494150 0.173367 -v -0.706449 0.476852 0.189424 -v -0.735267 0.475329 0.217272 -v -0.750329 0.487790 0.228781 -v -0.761443 0.504106 0.233203 -v -0.768668 0.527067 0.230937 -v -0.766400 0.551250 0.219985 -v -0.755252 0.567740 0.201824 -v -0.725654 0.569542 0.174762 -v -0.707949 0.557312 0.165190 -v -0.731070 0.508489 0.109613 -v -0.731955 0.482699 0.116884 -v -0.744609 0.462313 0.128781 -v -0.798509 0.437595 0.079053 -v -0.771373 0.453312 0.147166 -v -0.787700 0.457524 0.156875 -v -0.807578 0.473752 0.164228 -v -0.819259 0.498816 0.163685 -v -0.818470 0.524460 0.156183 -v -0.806636 0.544428 0.142942 -v -0.791453 0.551713 0.124964 -v -0.767926 0.551747 0.118687 -v -0.748704 0.540561 0.112115 -v -0.738831 0.527514 0.109083 -v -0.755680 0.499345 0.055519 -v -0.754889 0.476144 0.059195 -v -0.764230 0.456199 0.064869 -v -0.778229 0.443859 0.071399 -v -0.822684 0.442506 0.084337 -v -0.841521 0.456661 0.086775 -v -0.852159 0.478074 0.085949 -v -0.852996 0.501202 0.082089 -v -0.843423 0.521697 0.075446 -v -0.831137 0.532400 0.070733 -v -0.812448 0.539515 0.064269 -v -0.787048 0.536167 0.058491 -v -0.766309 0.520805 0.054853 -v -0.764139 0.495008 -0.000165 -v -0.761948 0.481820 0.000810 -v -0.762479 0.472238 0.001433 -v -0.763897 0.465812 0.001857 -v -0.766117 0.459637 0.002250 -v -0.770753 0.451318 0.002798 -v -0.779314 0.441450 0.003311 -v -0.790434 0.433996 0.003631 -v -0.803366 0.429549 0.003702 -v -0.816707 0.428630 0.003524 -v -0.839676 0.435205 0.002683 -v -0.852826 0.447129 0.001583 -v -0.859937 0.458472 0.000652 -v -0.863908 0.471463 -0.000341 -v -0.864358 0.484567 -0.001274 -v -0.862630 0.494022 -0.001857 -v -0.859028 0.502994 -0.002468 -v -0.851794 0.513597 -0.003094 -v -0.841983 0.522345 -0.003507 -v -0.829874 0.528394 -0.003702 -v -0.816908 0.531089 -0.003649 -v -0.795089 0.528769 -0.003106 -v -0.778319 0.517657 -0.002023 -v -0.769836 0.507459 -0.001151 -v -0.798171 0.527461 -0.090988 -v -0.808024 0.532953 0.005735 -v -0.778664 0.517040 -0.090172 -v -0.764673 0.499970 -0.088832 -v -0.773126 0.513028 0.007308 -v -0.758324 0.478900 -0.087175 -v -0.763363 0.485240 0.009501 -v -0.761804 0.451665 -0.085809 -v -0.766873 0.461442 0.011379 -v -0.778619 0.430056 -0.083320 -v -0.781272 0.442138 0.012903 -v -0.798054 0.419633 -0.082491 -v -0.820056 0.417427 -0.082310 -v -0.807888 0.429370 0.013911 -v -0.841240 0.423811 -0.082807 -v -0.858341 0.437810 -0.083907 -v -0.846697 0.439883 0.013081 -v -0.868736 0.457260 -0.085437 -v -0.870859 0.479167 -0.087163 -v -0.866569 0.474638 0.010338 -v -0.861778 0.505023 -0.088260 -v -0.857828 0.509914 0.007554 -v -0.841340 0.523194 -0.090641 -v -0.838067 0.526754 0.006224 -v -0.820192 0.529618 -0.091153 -v -0.795100 0.535475 -0.115839 -v -0.772020 0.523129 -0.114871 -v -0.755471 0.502926 -0.113285 -v -0.747959 0.478012 -0.111328 -v -0.750008 0.451973 -0.110348 -v -0.762777 0.429373 -0.107493 -v -0.782881 0.412852 -0.106181 -v -0.807871 0.405225 -0.105570 -v -0.833933 0.407730 -0.105760 -v -0.857049 0.420018 -0.106723 -v -0.873662 0.440200 -0.108312 -v -0.881235 0.465171 -0.110280 -v -0.878129 0.490914 -0.111306 -v -0.866417 0.513891 -0.114124 -v -0.846215 0.530460 -0.115434 -v -0.821166 0.538048 -0.116037 -v -0.789581 0.550978 -0.146143 -v -0.760171 0.535234 -0.144908 -v -0.739052 0.509507 -0.142885 -v -0.729398 0.477749 -0.140388 -v -0.731016 0.452772 -0.138424 -v -0.739226 0.429265 -0.136097 -v -0.760310 0.403345 -0.134515 -v -0.789663 0.387693 -0.133271 -v -0.822846 0.384389 -0.133002 -v -0.854810 0.393988 -0.133753 -v -0.880667 0.415050 -0.135408 -v -0.896475 0.444365 -0.137716 -v -0.899837 0.477471 -0.140323 -v -0.893349 0.501862 -0.142298 -v -0.880633 0.523689 -0.143971 -v -0.854775 0.544728 -0.145637 -v -0.822804 0.554313 -0.146400 -v -0.791209 0.549828 -0.166519 -v -0.761444 0.534625 -0.165320 -v -0.739805 0.509230 -0.163315 -v -0.728370 0.469803 -0.160203 -v -0.738976 0.427457 -0.156861 -v -0.761433 0.400874 -0.154763 -v -0.791141 0.385680 -0.153564 -v -0.824456 0.382977 -0.153351 -v -0.856286 0.393210 -0.154158 -v -0.881749 0.414815 -0.155863 -v -0.896959 0.444469 -0.158204 -v -0.899627 0.477633 -0.160821 -v -0.886165 0.515350 -0.163798 -v -0.856329 0.542253 -0.165922 -v -0.824539 0.552500 -0.166730 -v -0.809460 0.530864 -0.165023 -v -0.785588 0.524078 -0.164487 -v -0.766156 0.508700 -0.163273 -v -0.754125 0.487102 -0.161569 -v -0.751295 0.462598 -0.159635 -v -0.761187 0.432235 -0.157238 -v -0.785569 0.411421 -0.155595 -v -0.809397 0.404630 -0.155059 -v -0.834047 0.407455 -0.155282 -v -0.855739 0.419484 -0.156232 -v -0.871146 0.438876 -0.157762 -v -0.877925 0.462654 -0.159639 -v -0.873592 0.492101 -0.161963 -v -0.855768 0.515978 -0.163848 -v -0.834109 0.528013 -0.164798 -v -0.787228 0.508628 -0.039513 -v -0.773144 0.480219 -0.037270 -v -0.780492 0.454778 -0.035263 -v -0.801261 0.438214 -0.033955 -v -0.833080 0.440330 -0.034122 -v -0.854029 0.464320 -0.036016 -v -0.853719 0.489280 -0.037986 -v -0.842956 0.507139 -0.039396 -v -0.817485 0.518790 -0.040315 -v 0.823378 -0.207632 0.134936 -v 0.823276 -0.202248 0.134541 -v 0.823636 -0.195934 0.133033 -v 0.824086 -0.186279 0.128702 -v 0.825732 -0.175955 0.119751 -v 0.830179 -0.167462 0.098850 -v 0.835402 -0.172698 0.076809 -v 0.838084 -0.181993 0.066420 -v 0.839506 -0.190896 0.061029 -v 0.840378 -0.197085 0.058815 -v 0.840788 -0.202248 0.057825 -v 0.841052 -0.207632 0.057513 -v 0.841154 -0.213015 0.057908 -v 0.841094 -0.218179 0.058978 -v 0.840725 -0.224497 0.061295 -v 0.839835 -0.233315 0.066833 -v 0.837773 -0.242711 0.077453 -v 0.832554 -0.247605 0.100901 -v 0.829291 -0.242793 0.114566 -v 0.827251 -0.236889 0.122646 -v 0.825571 -0.228919 0.129041 -v 0.824597 -0.223076 0.132013 -v 0.824052 -0.218179 0.133635 -v 0.823642 -0.213015 0.134625 -v 0.633644 -0.206092 0.093459 -v 0.637399 -0.193625 0.090252 -v 0.639487 -0.181405 0.081655 -v 0.643480 -0.171854 0.063310 -v 0.647369 -0.172704 0.044909 -v 0.653846 -0.181493 0.029670 -v 0.564646 -0.192980 0.002587 -v 0.653820 -0.203358 0.017334 -v 0.649505 -0.216240 0.015853 -v 0.651474 -0.228777 0.020244 -v 0.649845 -0.240990 0.028941 -v 0.646173 -0.250525 0.047334 -v 0.642526 -0.249679 0.065772 -v 0.642509 -0.240940 0.082332 -v 0.551346 -0.227801 0.072940 -v 0.638210 -0.219024 0.093794 -v 0.509680 -0.201783 0.069959 -v 0.513049 -0.189523 0.066615 -v 0.515604 -0.177352 0.058131 -v 0.519554 -0.167777 0.039767 -v 0.522667 -0.168583 0.021202 -v 0.525964 -0.177402 0.005269 -v 0.521602 -0.201566 -0.008349 -v 0.522864 -0.219265 -0.006692 -v 0.519853 -0.236598 0.004203 -v 0.516457 -0.245319 0.020065 -v 0.513476 -0.246188 0.038627 -v 0.512138 -0.236746 0.057458 -v 0.511951 -0.214882 0.069741 -v 0.432507 -0.194034 0.058830 -v 0.435510 -0.184401 0.057282 -v 0.439224 -0.171525 0.050755 -v 0.443807 -0.159625 0.033970 -v 0.446661 -0.157997 0.012670 -v 0.447216 -0.167293 -0.006292 -v 0.450220 -0.185497 -0.017472 -v 0.440434 -0.198545 -0.020219 -v 0.438789 -0.208602 -0.018476 -v 0.435276 -0.221486 -0.011887 -v 0.430790 -0.233322 0.004857 -v 0.428079 -0.234973 0.026128 -v 0.428292 -0.225868 0.045165 -v 0.431194 -0.214254 0.054514 -v 0.409950 -0.200068 0.055536 -v 0.387099 -0.179460 0.055279 -v 0.391684 -0.170742 0.053864 -v 0.395854 -0.162238 0.049852 -v 0.401078 -0.151935 0.040176 -v 0.405270 -0.144835 0.020424 -v 0.404828 -0.148191 -0.000809 -v 0.400596 -0.160953 -0.017052 -v 0.371993 -0.162318 -0.023810 -v 0.389433 -0.182352 -0.024203 -v 0.384159 -0.195663 -0.021535 -v 0.376818 -0.210580 -0.008937 -v 0.372723 -0.217643 0.010738 -v 0.373288 -0.214359 0.031923 -v 0.378731 -0.202170 0.048231 -v 0.335690 -0.148027 0.054791 -v 0.361911 -0.155222 0.054294 -v 0.370304 -0.146655 0.049634 -v 0.377387 -0.137544 0.040058 -v 0.382516 -0.130984 0.020360 -v 0.380774 -0.133528 -0.000907 -v 0.373013 -0.144330 -0.017333 -v 0.354122 -0.166687 -0.024152 -v 0.349204 -0.175091 -0.021457 -v 0.341697 -0.185131 -0.013529 -v 0.335141 -0.193711 0.004739 -v 0.335192 -0.193526 0.026305 -v 0.341823 -0.184788 0.044314 -v 0.352034 -0.172686 0.053381 -v 0.339372 -0.136752 0.054566 -v 0.347071 -0.131324 0.051964 -v 0.356765 -0.123422 0.044159 -v 0.365203 -0.116427 0.025826 -v 0.365126 -0.116540 0.004175 -v 0.356768 -0.123672 -0.013880 -v 0.346913 -0.132064 -0.021778 -v 0.344070 -0.146358 -0.024449 -v 0.330146 -0.144080 -0.024124 -v 0.323663 -0.151100 -0.021483 -v 0.314178 -0.159218 -0.013642 -v 0.305854 -0.166242 0.004649 -v 0.305977 -0.166173 0.026253 -v 0.314419 -0.159186 0.044288 -v 0.324393 -0.150938 0.052204 -v 0.313743 -0.115209 0.054812 -v 0.323057 -0.110914 0.054539 -v 0.331607 -0.107472 0.051975 -v 0.342841 -0.102104 0.044244 -v 0.352735 -0.097217 0.025885 -v 0.352659 -0.097300 0.004213 -v 0.342889 -0.102295 -0.013855 -v 0.328958 -0.109930 -0.022954 -v 0.312089 -0.116053 -0.024099 -v 0.304331 -0.121271 -0.021498 -v 0.293281 -0.126964 -0.013734 -v 0.283478 -0.131895 0.004592 -v 0.283596 -0.131873 0.026227 -v 0.293435 -0.127056 0.044277 -v 0.305068 -0.121345 0.052206 -v 0.310848 -0.076589 0.054519 -v 0.319872 -0.075210 0.051972 -v 0.328506 -0.073026 0.046841 -v 0.338444 -0.070373 0.035849 -v 0.343979 -0.068856 0.015008 -v 0.338387 -0.070489 -0.005534 -v 0.323707 -0.075100 -0.020158 -v 0.313619 -0.096101 -0.024388 -v 0.298877 -0.079677 -0.024080 -v 0.290512 -0.083599 -0.021511 -v 0.278648 -0.087191 -0.013802 -v 0.268032 -0.090202 0.004555 -v 0.268139 -0.090219 0.026202 -v 0.278714 -0.087395 0.044262 -v 0.294247 -0.083937 0.053387 -v 0.289811 -0.022328 0.054788 -v 0.299807 -0.016600 0.054495 -v 0.311173 -0.031196 0.051969 -v 0.320022 -0.030211 0.046843 -v 0.330221 -0.028884 0.035858 -v 0.335907 -0.028065 0.015017 -v 0.330170 -0.029035 -0.005522 -v 0.316871 -0.013209 -0.016736 -v 0.307865 -0.014282 -0.021789 -v 0.297663 -0.016666 -0.024348 -v 0.289867 -0.030445 -0.024170 -v 0.281024 -0.035900 -0.021521 -v 0.272284 -0.037533 -0.016394 -v 0.262156 -0.039288 -0.005406 -v 0.256492 -0.040203 0.015428 -v 0.262258 -0.039423 0.035961 -v 0.270012 -0.020209 0.047249 -v 0.279052 -0.019638 0.052257 -v 0.304983 0.027117 0.051969 -v 0.313897 0.027232 0.046845 -v 0.324177 0.027670 0.035863 -v 0.329912 0.028057 0.015021 -v 0.324129 0.027428 -0.005518 -v 0.274588 0.024579 -0.021525 -v 0.265773 0.023308 -0.016401 -v 0.255558 0.022100 -0.005419 -v 0.249839 0.021555 0.015421 -v 0.255648 0.021863 0.035957 -v 0.280486 0.123176 0.054788 -v 0.290911 0.131963 0.054496 -v 0.301106 0.097677 0.051969 -v 0.308671 0.134264 0.046838 -v 0.320318 0.097029 0.035866 -v 0.326067 0.097135 0.015022 -v 0.320265 0.096704 -0.005516 -v 0.308367 0.133829 -0.016643 -v 0.299265 0.133780 -0.021755 -v 0.288844 0.130840 -0.024348 -v 0.282257 0.050606 -0.024232 -v 0.276028 0.139981 -0.023595 -v 0.270635 0.096502 -0.021527 -v 0.260382 0.132589 -0.016393 -v 0.251525 0.094490 -0.005425 -v 0.245785 0.094172 0.015418 -v 0.251603 0.094169 0.035955 -v 0.260750 0.131848 0.047108 -v 0.269875 0.130928 0.052209 -v 0.299757 0.173803 0.051969 -v 0.318933 0.172408 0.035868 -v 0.324683 0.172303 0.015024 -v 0.318870 0.172077 -0.005515 -v 0.269268 0.173760 -0.021528 -v 0.250096 0.172383 -0.005428 -v 0.244346 0.172272 0.015416 -v 0.250160 0.172053 0.035954 -v 0.283198 0.294499 0.054762 -v 0.293883 0.293155 0.054527 -v 0.301367 0.249915 0.051866 -v 0.309979 0.247857 0.046848 -v 0.320235 0.246997 0.035869 -v 0.325978 0.246688 0.015024 -v 0.320163 0.246740 -0.005513 -v 0.311944 0.292207 -0.016348 -v 0.302666 0.293036 -0.021682 -v 0.291990 0.294686 -0.024362 -v 0.281638 0.294868 -0.024057 -v 0.270818 0.251103 -0.021587 -v 0.261745 0.249775 -0.016407 -v 0.251454 0.249730 -0.005429 -v 0.245704 0.249871 0.015415 -v 0.251505 0.249466 0.035952 -v 0.263257 0.295940 0.046529 -v 0.272480 0.295194 0.051963 -v 0.305586 0.316518 0.051886 -v 0.314481 0.314942 0.046581 -v 0.324749 0.313953 0.035219 -v 0.330077 0.313432 0.014249 -v 0.323926 0.314019 -0.006121 -v 0.275281 0.319398 -0.021623 -v 0.266424 0.319605 -0.016644 -v 0.256233 0.320585 -0.006016 -v 0.250149 0.321167 0.014651 -v 0.255544 0.320646 0.035299 -v 0.310373 0.404712 0.054574 -v 0.309571 0.368984 0.053069 -v 0.321178 0.368397 0.046845 -v 0.331315 0.366677 0.035866 -v 0.336970 0.365638 0.015026 -v 0.331273 0.366774 -0.005507 -v 0.316287 0.370238 -0.020144 -v 0.306709 0.398349 -0.024347 -v 0.300972 0.412166 -0.024186 -v 0.284436 0.373322 -0.022523 -v 0.273735 0.377361 -0.016402 -v 0.263666 0.379433 -0.005421 -v 0.258025 0.380523 0.015417 -v 0.263748 0.379519 0.035950 -v 0.274097 0.377564 0.047089 -v 0.286175 0.376675 0.053388 -v 0.322164 0.409744 0.051972 -v 0.330683 0.407146 0.046842 -v 0.340484 0.404035 0.035858 -v 0.345941 0.402245 0.015022 -v 0.340439 0.404111 -0.005510 -v 0.325988 0.409368 -0.020147 -v 0.293233 0.419429 -0.021519 -v 0.284846 0.422375 -0.016391 -v 0.275129 0.425717 -0.005402 -v 0.269692 0.427539 0.015426 -v 0.275224 0.425756 0.035953 -v 0.289955 0.421355 0.050584 -v 0.303653 0.415436 0.054816 -v 0.327642 0.465209 0.054828 -v 0.325613 0.441521 0.054519 -v 0.333953 0.437895 0.051975 -v 0.344967 0.432163 0.044280 -v 0.354690 0.426877 0.025902 -v 0.354624 0.426948 0.004243 -v 0.345068 0.432294 -0.013827 -v 0.339637 0.448960 -0.021685 -v 0.335850 0.462992 -0.024334 -v 0.314636 0.447205 -0.024079 -v 0.307212 0.452639 -0.021508 -v 0.296426 0.458747 -0.013794 -v 0.286759 0.464054 0.004559 -v 0.286858 0.464031 0.026197 -v 0.296501 0.458864 0.044257 -v 0.315899 0.466475 0.052312 -v 0.346108 0.470314 0.054543 -v 0.347298 0.457391 0.051971 -v 0.356915 0.449474 0.044229 -v 0.365328 0.442355 0.025874 -v 0.365274 0.442460 0.004207 -v 0.357006 0.449674 -0.013856 -v 0.333087 0.473089 -0.024208 -v 0.324101 0.477317 -0.021497 -v 0.314756 0.485516 -0.013730 -v 0.306435 0.492664 0.004592 -v 0.306541 0.492614 0.026222 -v 0.314925 0.485569 0.044273 -v 0.362266 0.471573 0.051955 -v 0.370319 0.462012 0.044145 -v 0.377252 0.453540 0.025820 -v 0.377208 0.453671 0.004190 -v 0.370423 0.462290 -0.013859 -v 0.369726 0.478223 -0.021730 -v 0.354615 0.495750 -0.023657 -v 0.343095 0.495489 -0.021488 -v 0.335420 0.505331 -0.013660 -v 0.328615 0.513854 0.004636 -v 0.328731 0.513778 0.026242 -v 0.335694 0.505322 0.044281 -v 0.353465 0.502879 0.052232 -v 0.364133 0.497702 0.054809 -v 0.383603 0.496639 0.054580 -v 0.387571 0.487853 0.051902 -v 0.391617 0.479830 0.046661 -v 0.394960 0.473326 0.039312 -v 0.390917 0.462807 0.025782 -v 0.390889 0.462960 0.004165 -v 0.385521 0.472540 -0.013877 -v 0.383883 0.498352 -0.024329 -v 0.389446 0.509494 -0.024369 -v 0.363624 0.509546 -0.021482 -v 0.357544 0.520492 -0.013604 -v 0.352169 0.529928 0.004677 -v 0.352283 0.529819 0.026270 -v 0.357872 0.520387 0.044297 -v 0.405680 0.469922 0.025745 -v 0.405671 0.470098 0.004132 -v 0.410200 0.483840 -0.013904 -v 0.397253 0.492483 -0.021776 -v 0.397336 0.519682 -0.023163 -v 0.395321 0.528892 -0.019252 -v 0.380074 0.535386 -0.009587 -v 0.377096 0.543137 0.010080 -v 0.378259 0.540292 0.031329 -v 0.383278 0.528010 0.047744 -v 0.400672 0.519759 0.053842 -v 0.426243 0.514606 0.055001 -v 0.414581 0.502136 0.053603 -v 0.425585 0.491276 0.047634 -v 0.419745 0.481733 0.040020 -v 0.421615 0.473645 0.020333 -v 0.421004 0.476682 -0.000937 -v 0.427042 0.495643 -0.019460 -v 0.425610 0.505385 -0.023377 -v 0.442205 0.517711 -0.024506 -v 0.406101 0.543352 -0.009542 -v 0.404361 0.551439 0.010119 -v 0.405077 0.548431 0.031359 -v 0.408146 0.535510 0.047762 -v 0.446578 0.505967 0.053595 -v 0.446524 0.496377 0.049557 -v 0.438094 0.478826 0.030771 -v 0.438338 0.476125 0.009335 -v 0.437861 0.484677 -0.010043 -v 0.445412 0.527021 -0.023178 -v 0.433914 0.539674 -0.016939 -v 0.433205 0.552711 -0.000307 -v 0.433078 0.555411 0.021097 -v 0.433711 0.546891 0.040453 -v 0.447924 0.535891 0.050014 -v 0.448719 0.526186 0.053831 -v 0.473778 0.513805 0.055004 -v 0.467116 0.486629 0.043207 -v 0.456643 0.478641 0.030730 -v 0.456503 0.475955 0.009298 -v 0.457270 0.484507 -0.010075 -v 0.469013 0.495257 -0.019481 -v 0.470346 0.504939 -0.023368 -v 0.490470 0.511905 -0.024527 -v 0.461262 0.539514 -0.016911 -v 0.462466 0.552491 -0.000271 -v 0.462732 0.555160 0.021132 -v 0.462130 0.546617 0.040484 -v 0.493923 0.499058 0.053602 -v 0.491276 0.489807 0.049545 -v 0.476614 0.475635 0.030693 -v 0.476124 0.473011 0.009264 -v 0.478034 0.481403 -0.010106 -v 0.498376 0.519640 -0.023181 -v 0.489312 0.535398 -0.016888 -v 0.492258 0.548073 -0.000239 -v 0.492881 0.550663 0.021167 -v 0.491142 0.542256 0.040515 -v 0.488776 0.530762 0.049967 -v 0.487432 0.521133 0.053813 -v 0.524888 0.498776 0.055006 -v 0.498948 0.474892 0.039870 -v 0.496534 0.467728 0.022844 -v 0.496803 0.468352 0.003957 -v 0.500643 0.478775 -0.014049 -v 0.517245 0.486371 -0.021764 -v 0.520989 0.495591 -0.024279 -v 0.516127 0.523688 -0.019138 -v 0.520059 0.534607 -0.009409 -v 0.522656 0.541689 0.007600 -v 0.522466 0.541041 0.026466 -v 0.518885 0.530557 0.044455 -v 0.520598 0.512665 0.053602 -v 0.539996 0.477167 0.053102 -v 0.521304 0.469242 0.043824 -v 0.516621 0.459629 0.025520 -v 0.516810 0.459857 0.003924 -v 0.521948 0.469739 -0.014086 -v 0.547164 0.493841 -0.024219 -v 0.554022 0.501506 -0.021487 -v 0.546676 0.519475 -0.013362 -v 0.551589 0.528950 0.004928 -v 0.551510 0.528669 0.026499 -v 0.546600 0.518708 0.044489 -v 0.555675 0.499597 0.052249 -v 0.575575 0.473544 0.054700 -v 0.581372 0.454817 0.054669 -v 0.541137 0.457577 0.043799 -v 0.535194 0.448709 0.025488 -v 0.535414 0.448931 0.003890 -v 0.541844 0.458067 -0.014127 -v 0.560952 0.460171 -0.021771 -v 0.566695 0.468063 -0.024253 -v 0.573111 0.503951 -0.013339 -v 0.579243 0.512666 0.004959 -v 0.579110 0.512388 0.026533 -v 0.572864 0.503168 0.044526 -v 0.567313 0.452989 0.051908 -v 0.558615 0.443658 0.043779 -v 0.551363 0.435842 0.025457 -v 0.551622 0.436049 0.003847 -v 0.559409 0.444131 -0.014185 -v 0.595600 0.452728 -0.024299 -v 0.586273 0.473658 -0.022672 -v 0.597425 0.484541 -0.013318 -v 0.604814 0.492201 0.004989 -v 0.604618 0.491939 0.026572 -v 0.596969 0.483788 0.044575 -v 0.588404 0.474448 0.052215 -v 0.602710 0.442526 0.054745 -v 0.580099 0.432425 0.050214 -v 0.568106 0.423257 0.035354 -v 0.563747 0.420004 0.014538 -v 0.568695 0.423570 -0.005984 -v 0.577189 0.429659 -0.016806 -v 0.584629 0.434810 -0.021812 -v 0.597174 0.429524 -0.023749 -v 0.612083 0.421100 -0.024634 -v 0.611856 0.455568 -0.019757 -v 0.624200 0.464219 -0.004899 -v 0.628613 0.467383 0.015891 -v 0.623724 0.463783 0.036378 -v 0.615349 0.457568 0.047209 -v 0.608151 0.452137 0.052220 -v 0.626553 0.372357 0.054777 -v 0.599676 0.413326 0.053125 -v 0.589348 0.407614 0.046666 -v 0.579994 0.403146 0.035329 -v 0.575127 0.400753 0.014502 -v 0.580672 0.403365 -0.006034 -v 0.590147 0.407769 -0.016827 -v 0.598404 0.411439 -0.021816 -v 0.628933 0.426930 -0.019753 -v 0.642678 0.433123 -0.004882 -v 0.647591 0.435411 0.015924 -v 0.642093 0.432756 0.036433 -v 0.632724 0.428169 0.047235 -v 0.622903 0.422002 0.053235 -v 0.631257 0.376225 0.054737 -v 0.618157 0.367098 0.053619 -v 0.600313 0.378826 0.047234 -v 0.587958 0.374927 0.030503 -v 0.585530 0.374146 0.009016 -v 0.594019 0.376623 -0.010436 -v 0.605268 0.379708 -0.019649 -v 0.614426 0.381018 -0.023273 -v 0.629591 0.356134 -0.024447 -v 0.635398 0.389904 -0.022680 -v 0.646943 0.392402 -0.016217 -v 0.656881 0.395346 -0.004868 -v 0.662089 0.396811 0.015949 -v 0.656217 0.395047 0.036475 -v 0.646250 0.391959 0.047257 -v 0.637679 0.389212 0.052235 -v 0.608857 0.340288 0.047227 -v 0.596145 0.337807 0.030492 -v 0.593652 0.337302 0.009004 -v 0.602391 0.338747 -0.010457 -v 0.613940 0.340371 -0.019655 -v 0.626269 0.329387 -0.023543 -v 0.638913 0.347700 -0.024199 -v 0.656557 0.348376 -0.016785 -v 0.669374 0.350213 -0.000053 -v 0.671897 0.350560 0.021427 -v 0.663179 0.349059 0.040879 -v 0.651741 0.346829 0.050083 -v 0.643313 0.338351 0.053895 -v 0.630379 0.267828 0.053563 -v 0.619932 0.279873 0.049563 -v 0.607976 0.282175 0.039692 -v 0.600364 0.283617 0.022723 -v 0.600978 0.285422 0.003661 -v 0.608218 0.287718 -0.009705 -v 0.615160 0.289798 -0.016599 -v 0.624582 0.289194 -0.021801 -v 0.650679 0.276950 -0.023235 -v 0.659480 0.281109 -0.019563 -v 0.667722 0.286534 -0.012970 -v 0.676498 0.289468 -0.000165 -v 0.679039 0.291554 0.018458 -v 0.674455 0.292747 0.033732 -v 0.666386 0.294346 0.043933 -v 0.658059 0.294335 0.050028 -v 0.651356 0.293821 0.052861 -v 0.647896 0.184207 0.055376 -v 0.640956 0.170106 0.055272 -v 0.627660 0.167594 -0.018962 -v 0.635696 0.171845 -0.022382 -v 0.644044 0.180338 -0.023909 -v 0.651826 0.171490 -0.023619 -v 0.631377 0.106557 0.051409 -v 0.619303 0.110099 0.041492 -v 0.612494 0.111121 0.027325 -v 0.607311 0.163416 0.009802 -v 0.616159 0.163804 -0.009652 -v 0.687649 0.118698 0.001548 -v 0.686455 0.169246 0.022231 -v 0.676917 0.171536 0.042174 -v 0.665889 0.169361 0.050863 -v 0.655846 0.176558 0.054437 -v 0.652351 0.103902 0.057135 -v 0.642755 0.040637 0.055216 -v 0.612820 0.098763 0.011169 -v 0.621642 0.099203 -0.008266 -v 0.633176 0.100651 -0.017489 -v 0.642061 0.103036 -0.021164 -v 0.652281 0.101544 -0.022415 -v 0.675654 0.113699 -0.014580 -v 0.691604 0.108402 0.023507 -v 0.682773 0.107928 0.042953 -v 0.671132 0.107286 0.052148 -v 0.661926 0.107869 0.055716 -v 0.665651 0.050628 0.058917 -v 0.664242 0.010874 0.061316 -v 0.626922 0.039510 0.039887 -v 0.621764 0.037332 0.019142 -v 0.627863 0.037498 -0.001330 -v 0.638070 0.039235 -0.012160 -v 0.646831 0.040886 -0.017176 -v 0.657761 0.045012 -0.020158 -v 0.681862 0.019977 -0.015725 -v 0.691646 0.020041 -0.010125 -v 0.695076 0.052344 -0.000550 -v 0.700160 0.054835 0.020197 -v 0.694031 0.054673 0.040689 -v 0.683742 0.053214 0.051492 -v 0.674897 0.051676 0.056508 -v 0.678475 0.005982 0.062394 -v 0.672155 -0.028541 0.063834 -v 0.651949 -0.007609 0.055224 -v 0.642759 -0.013095 0.044194 -v 0.638325 -0.017629 0.023700 -v 0.644388 -0.017625 0.003311 -v 0.654169 -0.014708 -0.007693 -v 0.662462 -0.011738 -0.012860 -v 0.671232 -0.007683 -0.015657 -v 0.679050 -0.000581 -0.016389 -v 0.707433 0.010153 0.002703 -v 0.711767 0.014918 0.023228 -v 0.705629 0.014909 0.043681 -v 0.695747 0.012096 0.054642 -v 0.687326 0.009130 0.059810 -v 0.689479 -0.032535 0.069193 -v 0.673612 -0.049974 0.060441 -v 0.667066 -0.060668 0.043496 -v 0.669271 -0.065285 0.022614 -v 0.679203 -0.062158 0.004429 -v 0.689503 -0.055611 -0.004135 -v 0.697151 -0.049414 -0.007499 -v 0.705330 -0.042865 -0.008119 -v 0.721609 -0.026079 0.000767 -v 0.727567 -0.014679 0.017485 -v 0.725249 -0.009929 0.038468 -v 0.715099 -0.013115 0.056798 -v 0.700449 -0.022030 0.067324 -v 0.723222 -0.048390 0.079591 -v 0.716502 -0.064875 0.080804 -v 0.709681 -0.082264 0.071547 -v 0.709443 -0.095149 0.054829 -v 0.715473 -0.099255 0.034793 -v 0.725569 -0.093470 0.017557 -v 0.736801 -0.079251 0.006999 -v 0.736086 -0.060362 0.002478 -v 0.762014 -0.064806 0.013515 -v 0.751528 -0.048453 0.012681 -v 0.753336 -0.037044 0.022736 -v 0.750614 -0.028673 0.039403 -v 0.743968 -0.027683 0.056679 -v 0.733065 -0.035793 0.072574 -v 0.764428 -0.066921 0.099656 -v 0.763258 -0.082344 0.100927 -v 0.762110 -0.099048 0.094465 -v 0.765351 -0.113261 0.079328 -v 0.773105 -0.118047 0.060112 -v 0.782355 -0.112045 0.042506 -v 0.790554 -0.097017 0.030639 -v 0.794077 -0.082089 0.027699 -v 0.797786 -0.058516 0.035130 -v 0.793102 -0.044004 0.049569 -v 0.785083 -0.039142 0.068721 -v 0.775670 -0.045163 0.086332 -v 0.769324 -0.055670 0.095449 -v 0.829173 -0.067738 0.128579 -v 0.828254 -0.072782 0.130271 -v 0.827657 -0.077916 0.131316 -v 0.827371 -0.083078 0.131737 -v 0.827389 -0.088205 0.131553 -v 0.827699 -0.093234 0.130786 -v 0.828292 -0.098102 0.129458 -v 0.829644 -0.104181 0.126839 -v 0.832259 -0.112413 0.121206 -v 0.837212 -0.120582 0.111047 -v 0.847719 -0.124081 0.089869 -v 0.854249 -0.117948 0.076973 -v 0.857852 -0.111290 0.069916 -v 0.860575 -0.102444 0.064534 -v 0.861965 -0.095916 0.062179 -v 0.862563 -0.090781 0.061133 -v 0.862848 -0.085619 0.060713 -v 0.862831 -0.080492 0.060897 -v 0.862521 -0.075464 0.061663 -v 0.861927 -0.070596 0.062992 -v 0.861060 -0.065952 0.064861 -v 0.859419 -0.060112 0.068208 -v 0.856249 -0.052723 0.074841 -v 0.850612 -0.045787 0.086386 -v 0.839387 -0.046588 0.108707 -v 0.834044 -0.053945 0.119283 -v 0.830879 -0.061700 0.125507 -v 0.828973 -0.448207 0.132335 -v 0.828965 -0.442823 0.131932 -v 0.829672 -0.436304 0.130429 -v 0.831019 -0.426809 0.126342 -v 0.834422 -0.416741 0.118082 -v 0.843136 -0.407947 0.098226 -v 0.850077 -0.410847 0.083279 -v 0.853993 -0.415628 0.075068 -v 0.857309 -0.422580 0.068282 -v 0.859728 -0.431382 0.063349 -v 0.861101 -0.437658 0.061263 -v 0.861693 -0.442823 0.060372 -v 0.862003 -0.448207 0.060115 -v 0.862011 -0.453590 0.060517 -v 0.861724 -0.458755 0.061547 -v 0.860829 -0.465136 0.063795 -v 0.858846 -0.473879 0.068982 -v 0.854771 -0.483073 0.078684 -v 0.844641 -0.488175 0.101126 -v 0.838824 -0.483341 0.113571 -v 0.835225 -0.477457 0.121035 -v 0.832336 -0.469543 0.126918 -v 0.830728 -0.463652 0.129705 -v 0.829875 -0.458755 0.131187 -v 0.829283 -0.453590 0.132078 -v 0.638594 -0.446512 0.049064 -v 0.642808 -0.429770 0.044265 -v 0.648969 -0.415932 0.030080 -v 0.659443 -0.411715 0.008834 -v 0.561774 -0.416978 -0.047903 -v 0.551252 -0.425614 -0.062417 -v 0.671893 -0.439027 -0.021207 -v 0.668472 -0.456963 -0.024265 -v 0.667884 -0.473783 -0.017952 -v 0.662110 -0.487584 -0.003627 -v 0.656117 -0.491460 0.010547 -v 0.653076 -0.488905 0.029424 -v 0.643238 -0.464483 0.049305 -v 0.513327 -0.452435 0.001652 -v 0.516377 -0.434818 0.001148 -v 0.521478 -0.417632 -0.008987 -v 0.527267 -0.408938 -0.024072 -v 0.533772 -0.408101 -0.041689 -v 0.539097 -0.441895 -0.073204 -v 0.538699 -0.459852 -0.071761 -v 0.533924 -0.476977 -0.061511 -v 0.528143 -0.485629 -0.046464 -v 0.522544 -0.486541 -0.028572 -v 0.525532 -0.477756 -0.007879 -v 0.515894 -0.465032 -0.001664 -v 0.437260 -0.439139 -0.019682 -v 0.443103 -0.420324 -0.021887 -v 0.449544 -0.404645 -0.033922 -v 0.455494 -0.397389 -0.052981 -v 0.459550 -0.401255 -0.073631 -v 0.465247 -0.415784 -0.088488 -v 0.455532 -0.433169 -0.097583 -v 0.451753 -0.452666 -0.094799 -v 0.445511 -0.468193 -0.082750 -v 0.439721 -0.475418 -0.063736 -v 0.436338 -0.471717 -0.042975 -v 0.441686 -0.459743 -0.025669 -v 0.398373 -0.415493 -0.026107 -v 0.402883 -0.406979 -0.028292 -v 0.409049 -0.395573 -0.035620 -v 0.415336 -0.385647 -0.053421 -v 0.417261 -0.385740 -0.074962 -v 0.414976 -0.395919 -0.093255 -v 0.384042 -0.394057 -0.103302 -v 0.405553 -0.416362 -0.104679 -v 0.400596 -0.425493 -0.105023 -v 0.396503 -0.434501 -0.102698 -v 0.390531 -0.445913 -0.095282 -v 0.384403 -0.455730 -0.077587 -v 0.382644 -0.455706 -0.056116 -v 0.386314 -0.446205 -0.037703 -v 0.394738 -0.430287 -0.026836 -v 0.345521 -0.388277 -0.026756 -v 0.370697 -0.397618 -0.026486 -v 0.377368 -0.390721 -0.028616 -v 0.385457 -0.380716 -0.035777 -v 0.392871 -0.371471 -0.053480 -v 0.393640 -0.370810 -0.075082 -v 0.387736 -0.378905 -0.093675 -v 0.357730 -0.383819 -0.105448 -v 0.366369 -0.404043 -0.105685 -v 0.361460 -0.412488 -0.103495 -v 0.353662 -0.422701 -0.096289 -v 0.346393 -0.431964 -0.078626 -v 0.345698 -0.432667 -0.057071 -v 0.351780 -0.424748 -0.038505 -v 0.361678 -0.413043 -0.028594 -v 0.343398 -0.366461 -0.026888 -v 0.356785 -0.371305 -0.028600 -v 0.366647 -0.363152 -0.035676 -v 0.375750 -0.355427 -0.053392 -v 0.376561 -0.354780 -0.075016 -v 0.369055 -0.361362 -0.093626 -v 0.359612 -0.369596 -0.102233 -v 0.341273 -0.379394 -0.105690 -v 0.336534 -0.388919 -0.103515 -v 0.326975 -0.397407 -0.096403 -v 0.318008 -0.405191 -0.078720 -v 0.317249 -0.405884 -0.057135 -v 0.324842 -0.399436 -0.038546 -v 0.334376 -0.391317 -0.029927 -v 0.329487 -0.360999 -0.026251 -v 0.341427 -0.349051 -0.028600 -v 0.349492 -0.344887 -0.033223 -v 0.359028 -0.339738 -0.043636 -v 0.365039 -0.336465 -0.064147 -v 0.360922 -0.338788 -0.084975 -v 0.348318 -0.346065 -0.100376 -v 0.336085 -0.353871 -0.105356 -v 0.319652 -0.360030 -0.104418 -v 0.310177 -0.366657 -0.098886 -v 0.300752 -0.371961 -0.088460 -v 0.294803 -0.375279 -0.067982 -v 0.298953 -0.373027 -0.047183 -v 0.311799 -0.366226 -0.031787 -v 0.313834 -0.319564 -0.026296 -v 0.319628 -0.313683 -0.026849 -v 0.330561 -0.322624 -0.028715 -v 0.339084 -0.320390 -0.033209 -v 0.349407 -0.317108 -0.043597 -v 0.355938 -0.314978 -0.064119 -v 0.351482 -0.316510 -0.084950 -v 0.337788 -0.321417 -0.100363 -v 0.319963 -0.313987 -0.105354 -v 0.309863 -0.316284 -0.105647 -v 0.305048 -0.331205 -0.103581 -v 0.296508 -0.334586 -0.098912 -v 0.286280 -0.338109 -0.088514 -v 0.279793 -0.340296 -0.068013 -v 0.284278 -0.338856 -0.047200 -v 0.298173 -0.334592 -0.031793 -v 0.305141 -0.319538 -0.027475 -v 0.322802 -0.290951 -0.028630 -v 0.331874 -0.290594 -0.033203 -v 0.342560 -0.288839 -0.043580 -v 0.349334 -0.287634 -0.064106 -v 0.344720 -0.288547 -0.084935 -v 0.330513 -0.291649 -0.100353 -v 0.296414 -0.295910 -0.103519 -v 0.287772 -0.298730 -0.098930 -v 0.277163 -0.300864 -0.088548 -v 0.270415 -0.302141 -0.068033 -v 0.275052 -0.301357 -0.047212 -v 0.289430 -0.299211 -0.031797 -v 0.297481 -0.244381 -0.026786 -v 0.307948 -0.239808 -0.026523 -v 0.318289 -0.253579 -0.028631 -v 0.326041 -0.238574 -0.033248 -v 0.338217 -0.254083 -0.043572 -v 0.345081 -0.253534 -0.064100 -v 0.340408 -0.254034 -0.084928 -v 0.329036 -0.238765 -0.096655 -v 0.320247 -0.239279 -0.102222 -v 0.310004 -0.240430 -0.105356 -v 0.299033 -0.234323 -0.105677 -v 0.291547 -0.255999 -0.103517 -v 0.282751 -0.258635 -0.098940 -v 0.271994 -0.259863 -0.088567 -v 0.265144 -0.260511 -0.068043 -v 0.269833 -0.260198 -0.047219 -v 0.278282 -0.242471 -0.035436 -v 0.287082 -0.242368 -0.029900 -v 0.316398 -0.212062 -0.028631 -v 0.336262 -0.214584 -0.043567 -v 0.343150 -0.214501 -0.064097 -v 0.338457 -0.214720 -0.084926 -v 0.289555 -0.212682 -0.103516 -v 0.277083 -0.215777 -0.096645 -v 0.266707 -0.195537 -0.083294 -v 0.262927 -0.216189 -0.068048 -v 0.267626 -0.216211 -0.047221 -v 0.297491 -0.154212 -0.026788 -v 0.308642 -0.101359 -0.026362 -v 0.310684 -0.148091 -0.026881 -v 0.317510 -0.151341 -0.028635 -v 0.326317 -0.151916 -0.033096 -v 0.334004 -0.152422 -0.039885 -v 0.339744 -0.152794 -0.048488 -v 0.343113 -0.153014 -0.058547 -v 0.343184 -0.152672 -0.074991 -v 0.333273 -0.152468 -0.093543 -v 0.320771 -0.151486 -0.102200 -v 0.310419 -0.151706 -0.105355 -v 0.297609 -0.152988 -0.105283 -v 0.291005 -0.148702 -0.103596 -v 0.282212 -0.149154 -0.099280 -v 0.274484 -0.148692 -0.092612 -v 0.263245 -0.171320 -0.068050 -v 0.267934 -0.171605 -0.047223 -v 0.287382 -0.150398 -0.029931 -v 0.268058 -0.128312 -0.079552 -v 0.266568 -0.128186 -0.057951 -v 0.275878 -0.140834 -0.038148 -v 0.306429 -0.074310 -0.026841 -v 0.319531 -0.077241 -0.026910 -v 0.326442 -0.076387 -0.028547 -v 0.327582 -0.098459 -0.030570 -v 0.339428 -0.099826 -0.039290 -v 0.349163 -0.100296 -0.058938 -v 0.346807 -0.101051 -0.080748 -v 0.339520 -0.082765 -0.095055 -v 0.325138 -0.097699 -0.103132 -v 0.309501 -0.071192 -0.105666 -v 0.296200 -0.094542 -0.103418 -v 0.287717 -0.092130 -0.098946 -v 0.277062 -0.090204 -0.088577 -v 0.270262 -0.089118 -0.068050 -v 0.274890 -0.088874 -0.047615 -v 0.287488 -0.070138 -0.036029 -v 0.296174 -0.072075 -0.030178 -v 0.337246 -0.071900 -0.033202 -v 0.347775 -0.074423 -0.043575 -v 0.354440 -0.076137 -0.064097 -v 0.349909 -0.074926 -0.084914 -v 0.335212 -0.057216 -0.102203 -v 0.324209 -0.056979 -0.105348 -v 0.302276 -0.063977 -0.103516 -v 0.293852 -0.060627 -0.098937 -v 0.283419 -0.057750 -0.088562 -v 0.276768 -0.056006 -0.068043 -v 0.281326 -0.057131 -0.047226 -v 0.327919 -0.030479 -0.026296 -v 0.332716 -0.036657 -0.026863 -v 0.334914 -0.049769 -0.028632 -v 0.343495 -0.052562 -0.033207 -v 0.353497 -0.056711 -0.043590 -v 0.359815 -0.059415 -0.064105 -v 0.355511 -0.057542 -0.084919 -v 0.346851 -0.042455 -0.100127 -v 0.321889 -0.031011 -0.105323 -v 0.310276 -0.039077 -0.103519 -v 0.302285 -0.034822 -0.098921 -v 0.292389 -0.030455 -0.088533 -v 0.286094 -0.027734 -0.068028 -v 0.290425 -0.029557 -0.047219 -v 0.303891 -0.035010 -0.031802 -v 0.327257 -0.019240 -0.026876 -v 0.343062 -0.034768 -0.028710 -v 0.350508 -0.039486 -0.033211 -v 0.359861 -0.039989 -0.040401 -v 0.364161 -0.043620 -0.049090 -v 0.365068 -0.049546 -0.064128 -v 0.361227 -0.046805 -0.084961 -v 0.342003 -0.023778 -0.105345 -v 0.344894 -0.007117 -0.105713 -v 0.321062 -0.019266 -0.103580 -v 0.313749 -0.013694 -0.098898 -v 0.304940 -0.007423 -0.088489 -v 0.299358 -0.003488 -0.068001 -v 0.303232 -0.006169 -0.047198 -v 0.315259 -0.014324 -0.031793 -v 0.351140 -0.012225 -0.026488 -v 0.356323 -0.020181 -0.028623 -v 0.362019 -0.027404 -0.033323 -v 0.372346 -0.040470 -0.058958 -v 0.372415 -0.040420 -0.074959 -v 0.368475 -0.035177 -0.088836 -v 0.364302 -0.029571 -0.096471 -v 0.358970 -0.022426 -0.102154 -v 0.334448 -0.003821 -0.103516 -v 0.328490 0.003008 -0.098872 -v 0.321311 0.011097 -0.088430 -v 0.316788 0.016164 -0.067965 -v 0.319973 0.012671 -0.047172 -v 0.329852 0.002010 -0.031783 -v 0.362670 0.006666 -0.026802 -v 0.377449 -0.027087 -0.040244 -v 0.380124 -0.032092 -0.048927 -v 0.367191 -0.005533 -0.105338 -v 0.350320 0.007640 -0.103519 -v 0.343992 0.018794 -0.096377 -v 0.338018 0.029027 -0.078701 -v 0.337534 0.029923 -0.057120 -v 0.342684 0.021388 -0.038535 -v 0.356981 0.015256 -0.029903 -v 0.398201 0.009013 -0.026433 -v 0.385304 -0.005780 -0.028622 -v 0.388287 -0.014464 -0.033334 -v 0.393799 -0.030132 -0.059014 -v 0.393886 -0.030070 -0.075052 -v 0.390885 -0.020527 -0.093631 -v 0.386991 -0.008665 -0.102202 -v 0.377874 0.012536 -0.105299 -v 0.376259 0.019565 -0.103543 -v 0.363780 0.028426 -0.096327 -v 0.359514 0.039433 -0.078658 -v 0.359193 0.040378 -0.057089 -v 0.362978 0.031141 -0.038514 -v 0.401346 0.018711 -0.026812 -v 0.405940 -0.019433 -0.041995 -v 0.407066 -0.024542 -0.051176 -v 0.402148 0.005625 -0.105330 -v 0.419260 0.016428 -0.105763 -v 0.384804 0.035211 -0.096289 -v 0.382050 0.046655 -0.078622 -v 0.381868 0.047617 -0.057061 -v 0.384420 0.037952 -0.038493 -v 0.395289 0.027185 -0.029620 -v 0.419758 0.005724 -0.027209 -v 0.412234 -0.008899 -0.032623 -v 0.420748 -0.025718 -0.070353 -v 0.421637 -0.018208 -0.089586 -v 0.421089 -0.004391 -0.101361 -v 0.417569 0.022735 -0.104945 -v 0.417953 0.032420 -0.101485 -v 0.406099 0.043078 -0.092484 -v 0.405039 0.052328 -0.073290 -v 0.405337 0.050395 -0.051916 -v 0.418477 0.043422 -0.038913 -v 0.419117 0.035510 -0.032364 -v 0.455449 0.014145 -0.026362 -v 0.436008 -0.004041 -0.030717 -v 0.435424 -0.012545 -0.036626 -v 0.434970 -0.019234 -0.044497 -v 0.434552 -0.025320 -0.058922 -v 0.437771 0.007562 -0.105316 -v 0.428405 0.044481 -0.092449 -v 0.428536 0.053766 -0.073255 -v 0.428577 0.051793 -0.051887 -v 0.439877 0.025724 -0.028019 -v 0.451951 0.003480 -0.027194 -v 0.440330 -0.024163 -0.080312 -v 0.441595 -0.015887 -0.093145 -v 0.450441 -0.006633 -0.101382 -v 0.454656 0.020290 -0.104962 -v 0.457566 0.029575 -0.101497 -v 0.449694 0.039357 -0.095589 -v 0.451117 0.048735 -0.083520 -v 0.451733 0.052506 -0.062289 -v 0.450680 0.045067 -0.042515 -v 0.459561 0.032190 -0.032335 -v 0.462683 -0.009997 -0.030720 -v 0.459657 -0.017963 -0.036628 -v 0.456269 -0.026798 -0.048685 -v 0.454996 -0.030378 -0.069914 -v 0.456825 -0.025919 -0.084679 -v 0.461994 -0.017436 -0.097175 -v 0.479463 0.001386 -0.105802 -v 0.478850 0.031922 -0.095707 -v 0.472645 0.043879 -0.083486 -v 0.473817 0.047495 -0.062252 -v 0.471644 0.040277 -0.042478 -v 0.474924 0.017444 -0.028030 -v 0.477516 -0.007651 -0.027424 -v 0.466615 -0.004264 -0.104173 -v 0.486759 0.008137 -0.104973 -v 0.492234 0.016244 -0.101489 -v 0.492651 0.036029 -0.083455 -v 0.494346 0.039410 -0.062214 -v 0.491104 0.032576 -0.042437 -v 0.486317 0.022430 -0.032390 -v 0.488233 -0.030678 -0.033497 -v 0.471935 -0.032321 -0.044043 -v 0.472479 -0.040622 -0.064520 -v 0.475562 -0.036794 -0.085272 -v 0.481672 -0.029078 -0.096690 -v 0.487176 -0.022155 -0.102249 -v 0.490937 -0.016293 -0.104591 -v 0.507553 0.020717 -0.092336 -v 0.512347 0.027652 -0.075741 -v 0.512545 0.027897 -0.056856 -v 0.506948 0.019445 -0.038297 -v 0.507667 0.003978 -0.029879 -v 0.502183 -0.004760 -0.026729 -v 0.517114 -0.036795 -0.026896 -v 0.499862 -0.029213 -0.028632 -v 0.485699 -0.043539 -0.044103 -v 0.527658 -0.036205 -0.105768 -v 0.516038 -0.011320 -0.104590 -v 0.525223 -0.003432 -0.098815 -v 0.530712 0.001884 -0.091948 -v 0.528607 0.014375 -0.078391 -v 0.529211 0.014969 -0.056822 -v 0.532561 0.002997 -0.042873 -v 0.527723 -0.002127 -0.035355 -v 0.488908 -0.057047 -0.064586 -v 0.491245 -0.055344 -0.080066 -v 0.495135 -0.052424 -0.089160 -v 0.500798 -0.048208 -0.096737 -v 0.507896 -0.042932 -0.102273 -v 0.515529 -0.037213 -0.105230 -v 0.543526 -0.000491 -0.078368 -v 0.544187 0.000014 -0.056791 -v 0.534237 -0.022898 -0.029856 -v 0.529067 -0.027806 -0.027491 -v 0.529305 -0.040936 -0.026340 -v 0.518545 -0.053223 -0.028636 -v 0.510592 -0.057962 -0.033426 -v 0.501194 -0.063488 -0.044142 -v 0.541473 -0.038985 -0.103537 -v 0.549564 -0.034608 -0.098817 -v 0.556208 -0.030836 -0.091954 -v 0.556824 -0.017521 -0.078349 -v 0.557530 -0.017107 -0.056762 -v 0.549094 -0.022919 -0.038166 -v 0.501289 -0.078109 -0.064629 -v 0.503982 -0.077050 -0.080066 -v 0.508487 -0.075206 -0.089165 -v 0.515054 -0.072561 -0.096757 -v 0.523273 -0.069256 -0.102289 -v 0.528973 -0.065123 -0.104602 -v 0.539938 -0.076871 -0.105572 -v 0.568215 -0.036750 -0.078331 -v 0.568957 -0.036435 -0.056734 -v 0.559803 -0.041119 -0.038119 -v 0.553715 -0.056507 -0.029835 -v 0.547912 -0.060592 -0.027530 -v 0.553950 -0.088562 -0.026929 -v 0.541056 -0.079332 -0.026402 -v 0.533107 -0.088789 -0.028642 -v 0.522438 -0.085706 -0.033422 -v 0.509563 -0.081942 -0.044133 -v 0.546489 -0.075757 -0.105756 -v 0.559680 -0.083745 -0.103599 -v 0.566346 -0.061780 -0.096066 -v 0.577350 -0.058080 -0.078318 -v 0.578117 -0.057868 -0.056710 -v 0.568394 -0.061332 -0.038073 -v 0.515048 -0.102649 -0.044155 -v 0.509549 -0.103489 -0.059062 -v 0.508427 -0.097892 -0.075472 -v 0.517044 -0.105740 -0.089266 -v 0.523948 -0.103667 -0.096900 -v 0.532109 -0.099836 -0.102335 -v 0.538728 -0.096372 -0.104721 -v 0.570489 -0.092993 -0.098840 -v 0.581245 -0.090577 -0.088059 -v 0.585742 -0.080806 -0.067502 -v 0.580850 -0.081900 -0.046687 -v 0.572899 -0.093687 -0.034923 -v 0.564054 -0.093267 -0.029732 -v 0.542564 -0.120247 -0.027099 -v 0.528163 -0.119456 -0.033334 -v 0.512051 -0.120301 -0.076140 -v 0.560474 -0.131238 -0.104775 -v 0.590072 -0.111502 -0.070094 -v 0.589712 -0.112093 -0.058656 -v 0.584665 -0.107984 -0.046031 -v 0.554961 -0.141264 -0.025992 -v 0.533126 -0.152793 -0.030722 -v 0.520158 -0.138473 -0.041848 -v 0.512388 -0.136523 -0.059017 -v 0.541419 -0.170339 -0.102598 -v 0.550412 -0.140633 -0.105494 -v 0.572453 -0.145853 -0.100258 -v 0.582626 -0.136328 -0.091309 -v 0.589217 -0.131346 -0.079878 -v 0.561498 -0.164088 -0.026009 -v 0.551512 -0.178267 -0.024559 -v 0.544875 -0.164687 -0.025998 -v 0.519981 -0.196089 -0.082175 -v 0.525874 -0.194430 -0.090657 -v 0.532892 -0.175811 -0.098244 -v 0.562566 -0.175609 -0.103296 -v 0.593364 -0.208960 -0.077045 -v 0.594938 -0.190857 -0.059327 -v 0.589676 -0.186516 -0.044835 -v 0.583336 -0.184138 -0.036480 -v 0.574458 -0.167721 -0.030833 -v 0.568217 -0.167999 -0.027757 -v 0.518424 -0.200855 -0.048023 -v 0.515368 -0.198347 -0.067800 -v 0.552445 -0.175204 -0.104196 -v 0.587852 -0.215777 -0.086702 -v 0.559358 -0.209422 -0.021727 -v 0.526955 -0.219812 -0.035553 -v 0.535182 -0.206720 -0.095938 -v 0.544201 -0.205092 -0.100236 -v 0.557449 -0.209361 -0.101789 -v 0.580386 -0.232505 -0.093546 -v 0.567340 -0.195423 -0.024730 -v 0.560557 -0.262061 -0.015516 -v 0.540863 -0.238530 -0.024096 -v 0.521647 -0.230920 -0.047187 -v 0.521115 -0.233648 -0.068597 -v 0.531374 -0.233912 -0.087142 -v 0.545710 -0.230957 -0.096855 -v 0.557827 -0.230341 -0.099184 -v 0.598609 -0.216868 -0.061750 -v 0.593451 -0.215242 -0.041076 -v 0.579699 -0.207471 -0.028907 -v 0.572284 -0.215757 -0.023142 -v 0.568926 -0.240157 -0.017375 -v 0.536176 -0.253049 -0.030112 -v 0.529053 -0.257935 -0.043166 -v 0.528744 -0.261575 -0.064410 -v 0.538681 -0.261052 -0.083021 -v 0.552534 -0.256665 -0.093104 -v 0.568667 -0.264308 -0.093947 -v 0.579269 -0.260118 -0.093181 -v 0.591729 -0.255615 -0.087517 -v 0.598284 -0.250632 -0.081048 -v 0.601468 -0.235759 -0.069928 -v 0.601723 -0.232002 -0.048668 -v 0.591702 -0.232505 -0.030015 -v 0.579928 -0.235199 -0.021436 -v 0.578965 -0.255547 -0.014795 -v 0.596298 -0.296886 -0.005103 -v 0.551287 -0.275627 -0.021341 -v 0.541990 -0.284838 -0.037923 -v 0.542067 -0.289368 -0.058965 -v 0.551327 -0.287683 -0.077764 -v 0.564368 -0.281312 -0.088477 -v 0.608078 -0.249870 -0.067274 -v 0.607938 -0.245250 -0.046190 -v 0.598566 -0.246913 -0.027343 -v 0.587688 -0.251615 -0.018298 -v 0.601460 -0.286666 -0.006101 -v 0.572157 -0.294226 -0.012968 -v 0.563174 -0.307138 -0.026921 -v 0.561826 -0.314486 -0.047101 -v 0.568329 -0.313645 -0.067102 -v 0.580464 -0.305244 -0.081731 -v 0.590533 -0.295037 -0.086647 -v 0.609939 -0.280135 -0.082296 -v 0.616356 -0.264219 -0.069093 -v 0.617601 -0.256709 -0.048862 -v 0.611000 -0.257542 -0.028785 -v 0.598531 -0.265780 -0.014231 -v 0.619924 -0.320133 0.001242 -v 0.592770 -0.320266 -0.012196 -v 0.589668 -0.333624 -0.031089 -v 0.595248 -0.336065 -0.054262 -v 0.604637 -0.328299 -0.071169 -v 0.612644 -0.317669 -0.078482 -v 0.617422 -0.307544 -0.081099 -v 0.623340 -0.300073 -0.080131 -v 0.632894 -0.283245 -0.070471 -v 0.632285 -0.272546 -0.060798 -v 0.629616 -0.265613 -0.040556 -v 0.621869 -0.268807 -0.021207 -v 0.612666 -0.278760 -0.009272 -v 0.634784 -0.291783 0.001332 -v 0.627638 -0.307874 0.005364 -v 0.625575 -0.340123 -0.007707 -v 0.628210 -0.349019 -0.020598 -v 0.635255 -0.351586 -0.040350 -v 0.644254 -0.343776 -0.057383 -v 0.651691 -0.330023 -0.066549 -v 0.655535 -0.317745 -0.068868 -v 0.660562 -0.303726 -0.066118 -v 0.658532 -0.279730 -0.047094 -v 0.651623 -0.274223 -0.027661 -v 0.642679 -0.279576 -0.009527 -v 0.674648 -0.305788 0.023914 -v 0.671137 -0.318174 0.025702 -v 0.673621 -0.335891 0.024311 -v 0.710909 -0.353478 0.033151 -v 0.681436 -0.359255 0.001773 -v 0.691360 -0.360035 -0.019763 -v 0.700582 -0.349969 -0.035406 -v 0.705649 -0.336966 -0.042436 -v 0.707391 -0.324299 -0.045186 -v 0.710487 -0.307191 -0.040875 -v 0.706687 -0.293102 -0.032109 -v 0.701394 -0.283709 -0.019029 -v 0.689348 -0.282684 0.001435 -v 0.679879 -0.292780 0.017002 -v 0.739476 -0.311569 0.062907 -v 0.739012 -0.329664 0.064580 -v 0.744017 -0.344485 0.062690 -v 0.753695 -0.362993 0.043208 -v 0.762299 -0.365303 0.027199 -v 0.772333 -0.357754 0.010700 -v 0.779352 -0.341338 -0.000341 -v 0.779911 -0.323517 -0.003542 -v 0.781702 -0.306499 0.002924 -v 0.769648 -0.287944 0.020919 -v 0.759204 -0.285582 0.035797 -v 0.748541 -0.293135 0.051938 -v 0.837319 -0.309946 0.126163 -v 0.836042 -0.316588 0.127986 -v 0.835699 -0.321719 0.128668 -v 0.835744 -0.326848 0.128794 -v 0.836162 -0.331910 0.128381 -v 0.836942 -0.336842 0.127444 -v 0.838735 -0.343070 0.125361 -v 0.842087 -0.351540 0.120711 -v 0.846709 -0.358323 0.114264 -v 0.852219 -0.362831 0.106488 -v 0.861669 -0.365009 0.092997 -v 0.870204 -0.359522 0.080537 -v 0.874771 -0.353311 0.073726 -v 0.878273 -0.344882 0.068301 -v 0.879982 -0.338327 0.065715 -v 0.880724 -0.333258 0.064463 -v 0.881066 -0.328128 0.063782 -v 0.881022 -0.322999 0.063656 -v 0.880604 -0.317937 0.064069 -v 0.879824 -0.313005 0.065006 -v 0.878695 -0.308266 0.066450 -v 0.876544 -0.302289 0.069189 -v 0.872490 -0.294565 0.074809 -v 0.865388 -0.287128 0.084785 -v 0.850996 -0.286455 0.105355 -v 0.844116 -0.293283 0.115562 -v 0.840086 -0.300599 0.121678 -v 0.800434 -0.682145 0.120029 -v 0.801285 -0.674549 0.119716 -v 0.803912 -0.664894 0.117990 -v 0.808410 -0.656413 0.114844 -v 0.814416 -0.649645 0.110531 -v 0.824421 -0.643273 0.103185 -v 0.844926 -0.645064 0.087806 -v 0.853966 -0.652689 0.080790 -v 0.859087 -0.660398 0.076706 -v 0.862434 -0.669546 0.073891 -v 0.863782 -0.676759 0.072740 -v 0.863996 -0.682145 0.072420 -v 0.863629 -0.687530 0.072536 -v 0.862098 -0.694714 0.073405 -v 0.858484 -0.703884 0.075878 -v 0.853142 -0.711595 0.079669 -v 0.843863 -0.719292 0.086418 -v 0.823141 -0.720882 0.101855 -v 0.813519 -0.714662 0.109284 -v 0.807695 -0.707889 0.113862 -v 0.803439 -0.699416 0.117328 -v 0.801086 -0.689796 0.119419 -v 0.736577 -0.669278 0.047870 -v 0.746292 -0.649681 0.040048 -v 0.764674 -0.639696 0.028165 -v 0.776868 -0.643624 0.007699 -v 0.786625 -0.657139 -0.004348 -v 0.790086 -0.671384 -0.008908 -v 0.789102 -0.689780 -0.009972 -v 0.781252 -0.709504 -0.000372 -v 0.770861 -0.718062 0.011771 -v 0.715756 -0.717679 -0.015668 -v 0.751490 -0.715545 0.032694 -v 0.740708 -0.701923 0.043847 -v 0.737014 -0.687651 0.048148 -v 0.672461 -0.669710 0.000067 -v 0.678319 -0.651264 -0.006704 -v 0.688400 -0.639424 -0.020554 -v 0.702036 -0.639157 -0.043071 -v 0.709686 -0.650577 -0.058595 -v 0.713520 -0.666275 -0.066106 -v 0.712965 -0.684541 -0.067628 -v 0.708685 -0.703143 -0.059819 -v 0.700446 -0.714969 -0.044922 -v 0.681501 -0.710557 -0.014579 -v 0.675799 -0.699591 -0.005608 -v 0.673160 -0.687854 -0.000629 -v 0.610528 -0.670392 -0.028798 -v 0.612585 -0.657985 -0.031985 -v 0.616163 -0.645601 -0.039947 -v 0.631761 -0.635334 -0.057077 -v 0.631497 -0.638916 -0.082063 -v 0.635781 -0.652466 -0.097152 -v 0.637135 -0.666408 -0.102691 -v 0.636570 -0.679519 -0.103745 -v 0.634828 -0.696438 -0.098227 -v 0.629891 -0.710510 -0.083833 -v 0.629270 -0.715079 -0.064926 -v 0.618108 -0.711082 -0.049577 -v 0.612651 -0.697375 -0.034823 -v 0.610913 -0.683347 -0.029469 -v 0.552395 -0.666545 -0.042585 -v 0.555109 -0.647879 -0.050688 -v 0.558743 -0.635552 -0.067440 -v 0.562214 -0.636966 -0.098774 -v 0.563349 -0.650638 -0.114579 -v 0.563555 -0.664420 -0.120207 -v 0.563086 -0.674814 -0.121187 -v 0.562721 -0.689469 -0.118081 -v 0.560865 -0.705647 -0.105038 -v 0.551609 -0.711980 -0.090704 -v 0.559412 -0.712658 -0.074265 -v 0.554368 -0.703387 -0.055723 -v 0.552979 -0.685991 -0.044103 -v 0.500496 -0.675031 -0.044028 -v 0.500986 -0.665194 -0.044400 -v 0.501019 -0.655107 -0.047453 -v 0.501189 -0.642615 -0.055950 -v 0.546953 -0.632875 -0.083881 -v 0.499253 -0.633372 -0.096045 -v 0.497039 -0.644960 -0.113607 -v 0.496046 -0.657955 -0.120933 -v 0.495450 -0.668055 -0.123119 -v 0.495571 -0.678113 -0.122748 -v 0.495872 -0.692146 -0.117936 -v 0.497425 -0.706831 -0.102730 -v 0.499655 -0.709762 -0.071297 -v 0.500168 -0.698095 -0.053577 -v 0.500528 -0.685007 -0.046206 -v 0.456216 -0.674429 -0.036605 -v 0.455738 -0.661937 -0.037277 -v 0.453317 -0.645489 -0.044844 -v 0.491122 -0.634554 -0.067892 -v 0.445342 -0.630565 -0.074571 -v 0.439834 -0.637884 -0.096709 -v 0.437505 -0.648622 -0.106701 -v 0.436338 -0.661147 -0.112379 -v 0.436335 -0.672300 -0.113453 -v 0.437254 -0.685958 -0.111103 -v 0.440972 -0.702929 -0.098679 -v 0.445588 -0.710287 -0.082125 -v 0.450049 -0.709380 -0.063875 -v 0.454248 -0.698012 -0.046444 -v 0.455871 -0.684815 -0.038928 -v 0.418940 -0.676501 -0.023373 -v 0.416277 -0.664771 -0.023056 -v 0.412271 -0.652431 -0.027237 -v 0.371957 -0.651727 -0.014967 -v 0.402637 -0.636415 -0.044386 -v 0.394293 -0.634603 -0.064220 -v 0.387824 -0.643938 -0.082116 -v 0.385779 -0.655944 -0.090968 -v 0.385580 -0.665734 -0.094623 -v 0.386800 -0.675548 -0.095979 -v 0.388544 -0.689866 -0.093439 -v 0.401399 -0.712240 -0.071473 -v 0.411784 -0.711397 -0.049486 -v 0.417462 -0.699595 -0.032803 -v 0.418942 -0.686501 -0.025595 -v 0.387457 -0.683849 -0.006651 -v 0.381922 -0.672816 -0.004659 -v 0.376417 -0.661008 -0.008059 -v 0.362091 -0.646130 -0.022851 -v 0.350735 -0.645040 -0.041108 -v 0.343858 -0.654412 -0.058767 -v 0.342899 -0.666118 -0.068252 -v 0.344063 -0.675511 -0.072631 -v 0.347263 -0.685018 -0.075088 -v 0.349258 -0.695230 -0.073789 -v 0.358447 -0.706744 -0.070862 -v 0.370053 -0.718178 -0.057396 -v 0.380190 -0.719490 -0.041557 -v 0.387191 -0.713007 -0.025599 -v 0.389607 -0.697380 -0.011427 -v 0.358084 -0.695127 0.013156 -v 0.352171 -0.687087 0.014300 -v 0.345083 -0.679767 0.013231 -v 0.333764 -0.671388 0.008022 -v 0.318934 -0.666534 -0.006455 -v 0.309413 -0.670885 -0.025180 -v 0.308006 -0.682898 -0.042430 -v 0.312444 -0.694344 -0.051200 -v 0.317965 -0.702286 -0.055216 -v 0.325952 -0.714338 -0.055950 -v 0.341888 -0.726317 -0.049908 -v 0.356823 -0.731183 -0.035421 -v 0.366409 -0.726776 -0.016650 -v 0.367906 -0.714601 0.000540 -v 0.363716 -0.702956 0.009069 -v 0.333748 -0.739915 0.032557 -v 0.333224 -0.720636 0.030980 -v 0.325036 -0.715172 0.032343 -v 0.311776 -0.708973 0.030637 -v 0.294278 -0.704491 0.019440 -v 0.283174 -0.706236 0.000987 -v 0.281937 -0.713508 -0.018756 -v 0.287420 -0.721028 -0.030715 -v 0.287176 -0.739318 -0.032045 -v 0.301001 -0.733087 -0.040701 -v 0.309415 -0.737681 -0.042375 -v 0.322732 -0.743768 -0.040814 -v 0.340421 -0.748257 -0.029634 -v 0.351620 -0.746458 -0.011131 -v 0.352929 -0.739001 0.008585 -v 0.347557 -0.731255 0.020424 -v 0.319326 -0.761965 0.042267 -v 0.310467 -0.758540 0.044275 -v 0.296228 -0.755396 0.043539 -v 0.277729 -0.753345 0.033094 -v 0.266746 -0.754587 0.014511 -v 0.266614 -0.758599 -0.006197 -v 0.273342 -0.762614 -0.019228 -v 0.289231 -0.769755 -0.030979 -v 0.298293 -0.771587 -0.033276 -v 0.308334 -0.773627 -0.033169 -v 0.322546 -0.775783 -0.028693 -v 0.338005 -0.776163 -0.013593 -v 0.343698 -0.773295 0.006788 -v 0.338495 -0.767745 0.026584 -v 0.323083 -0.806965 0.043120 -v 0.314136 -0.805738 0.048096 -v 0.304891 -0.807171 0.050783 -v 0.294572 -0.806151 0.050851 -v 0.280141 -0.805898 0.046659 -v 0.264538 -0.806697 0.031746 -v 0.258985 -0.808431 0.011229 -v 0.264630 -0.810408 -0.009088 -v 0.275123 -0.811344 -0.020249 -v 0.284502 -0.811616 -0.025513 -v 0.293563 -0.814449 -0.027632 -v 0.303529 -0.814161 -0.027806 -v 0.317976 -0.814176 -0.023653 -v 0.333615 -0.813286 -0.008740 -v 0.339179 -0.811476 0.011790 -v 0.333535 -0.809064 0.032083 -v 0.324890 -0.852372 0.045866 -v 0.315453 -0.851722 0.051106 -v 0.307073 -0.856847 0.053725 -v 0.296719 -0.856151 0.053886 -v 0.282356 -0.857921 0.049857 -v 0.266781 -0.860330 0.035057 -v 0.261198 -0.861806 0.014530 -v 0.266716 -0.861870 -0.005896 -v 0.277063 -0.860889 -0.017186 -v 0.286137 -0.858573 -0.022556 -v 0.295550 -0.861534 -0.024860 -v 0.305373 -0.858258 -0.025183 -v 0.319692 -0.856093 -0.021182 -v 0.335263 -0.853457 -0.006381 -v 0.340840 -0.851923 0.014159 -v 0.335289 -0.851723 0.034598 -v 0.333251 -0.892637 0.046595 -v 0.324302 -0.895188 0.051973 -v 0.313744 -0.898733 0.054864 -v 0.297402 -0.904923 0.052961 -v 0.280547 -0.910089 0.040588 -v 0.272385 -0.912893 0.020885 -v 0.274906 -0.912277 -0.000014 -v 0.283139 -0.909677 -0.012605 -v 0.293835 -0.906070 -0.020849 -v 0.304153 -0.901552 -0.024166 -v 0.314316 -0.900590 -0.024407 -v 0.327881 -0.895468 -0.020480 -v 0.342851 -0.890203 -0.005738 -v 0.348320 -0.888184 0.014781 -v 0.343154 -0.889640 0.035241 -v 0.342993 -0.925440 0.049684 -v 0.334789 -0.930019 0.053584 -v 0.328862 -0.938324 0.054981 -v 0.315415 -0.944131 0.053028 -v 0.300336 -0.953281 0.040718 -v 0.293026 -0.957962 0.021032 -v 0.295154 -0.956590 0.000138 -v 0.302373 -0.951886 -0.012494 -v 0.309548 -0.947206 -0.019184 -v 0.317190 -0.941560 -0.023148 -v 0.326680 -0.938839 -0.024541 -v 0.337941 -0.929666 -0.022605 -v 0.352458 -0.919598 -0.010298 -v 0.359723 -0.914816 0.009422 -v 0.357561 -0.916181 0.030361 -v 0.350271 -0.920825 0.042985 -v 0.363698 -0.942922 0.047435 -v 0.355011 -0.953018 0.053538 -v 0.361512 -0.970979 0.054984 -v 0.340555 -0.972285 0.053021 -v 0.329078 -0.985625 0.040762 -v 0.323535 -0.992343 0.021090 -v 0.325101 -0.990405 0.000216 -v 0.333361 -0.980228 -0.016887 -v 0.341773 -0.969280 -0.023222 -v 0.358715 -0.970616 -0.024539 -v 0.357566 -0.951637 -0.022606 -v 0.368282 -0.937643 -0.010368 -v 0.373764 -0.930827 0.009350 -v 0.372154 -0.932761 0.030286 -v 0.381902 -0.957937 0.049614 -v 0.378239 -0.966915 0.053650 -v 0.372013 -0.985325 0.053948 -v 0.366895 -0.998180 0.048173 -v 0.361915 -1.011053 0.031469 -v 0.360793 -1.013963 0.010561 -v 0.363554 -1.006609 -0.009041 -v 0.367716 -0.995483 -0.019122 -v 0.370961 -0.986350 -0.023186 -v 0.378489 -0.968395 -0.023522 -v 0.383181 -0.955410 -0.017798 -v 0.387945 -0.942382 -0.001075 -v 0.389024 -0.939437 0.019897 -v 0.386181 -0.946820 0.039560 -v 0.403658 -0.962868 0.050270 -v 0.416154 -0.977545 0.054609 -v 0.421197 -0.986817 0.054782 -v 0.402306 -1.000704 0.050940 -v 0.401364 -1.016545 0.036397 -v 0.401048 -1.022529 0.015923 -v 0.401278 -1.017403 -0.004449 -v 0.401861 -1.002123 -0.019767 -v 0.401611 -0.987614 -0.024232 -v 0.416706 -0.978453 -0.024277 -v 0.404248 -0.964348 -0.020563 -v 0.404718 -0.948407 -0.006023 -v 0.404971 -0.942382 0.014509 -v 0.404684 -0.947532 0.034938 -v 0.426750 -0.964800 0.051967 -v 0.434433 -0.993073 0.052458 -v 0.437622 -1.005679 0.044932 -v 0.440482 -1.016837 0.026533 -v 0.440567 -1.017222 0.005485 -v 0.437901 -1.007198 -0.013027 -v 0.434507 -0.994445 -0.021503 -v 0.431710 -0.983540 -0.024409 -v 0.427489 -0.966046 -0.022059 -v 0.424094 -0.953485 -0.014587 -v 0.421077 -0.942280 0.003859 -v 0.420948 -0.941897 0.024969 -v 0.423496 -0.952001 0.043519 -v 0.447833 -0.956465 0.052567 -v 0.465862 -0.962710 0.054996 -v 0.462787 -0.977982 0.053442 -v 0.470143 -0.991613 0.045044 -v 0.476206 -1.001523 0.026622 -v 0.476438 -1.001889 0.005542 -v 0.470985 -0.993094 -0.012987 -v 0.460076 -0.977550 -0.023730 -v 0.451960 -0.960374 -0.023456 -v 0.444523 -0.949425 -0.018030 -v 0.436968 -0.937504 -0.001352 -v 0.435200 -0.934731 0.019675 -v 0.439201 -0.941390 0.039339 -v 0.466506 -0.940992 0.052605 -v 0.485181 -0.956365 0.053941 -v 0.491344 -0.963250 0.050490 -v 0.497569 -0.969118 0.044436 -v 0.504483 -0.975591 0.031886 -v 0.506956 -0.977795 0.010898 -v 0.501359 -0.972498 -0.008742 -v 0.488398 -0.960975 -0.022031 -v 0.485348 -0.943135 -0.024472 -v 0.471828 -0.943303 -0.023368 -v 0.464633 -0.937645 -0.019971 -v 0.458274 -0.931867 -0.013919 -v 0.451249 -0.925475 -0.001382 -v 0.448725 -0.923286 0.019651 -v 0.454256 -0.928674 0.039322 -v 0.478408 -0.915666 0.049761 -v 0.493975 -0.908555 0.054035 -v 0.501695 -0.916653 0.055389 -v 0.504567 -0.929658 0.054163 -v 0.511817 -0.935187 0.050772 -v 0.519189 -0.939584 0.044737 -v 0.527386 -0.944384 0.032232 -v 0.530398 -0.945925 0.011226 -v 0.523890 -0.941887 -0.008437 -v 0.508765 -0.933436 -0.021780 -v 0.488871 -0.919367 -0.023120 -v 0.480605 -0.915661 -0.019782 -v 0.473100 -0.911437 -0.013749 -v 0.464796 -0.906788 -0.001252 -v 0.461747 -0.905276 0.019778 -v 0.468192 -0.909431 0.039458 -v 0.496894 -0.873759 0.050429 -v 0.520592 -0.893307 0.054913 -v 0.527725 -0.901389 0.051339 -v 0.535753 -0.904511 0.045327 -v 0.544694 -0.907829 0.032861 -v 0.548046 -0.908719 0.011850 -v 0.541061 -0.905711 -0.007836 -v 0.524778 -0.899888 -0.021240 -v 0.523278 -0.853626 -0.022853 -v 0.510501 -0.876083 -0.022793 -v 0.504298 -0.879093 -0.021713 -v 0.494101 -0.886918 -0.019324 -v 0.485943 -0.884089 -0.013316 -v 0.476906 -0.880997 -0.000860 -v 0.473527 -0.880149 0.020164 -v 0.480458 -0.883304 0.039859 -v 0.511968 -0.868255 0.055799 -v 0.535301 -0.812692 0.057898 -v 0.537497 -0.860062 0.053649 -v 0.547911 -0.864568 0.046307 -v 0.557259 -0.866658 0.033881 -v 0.560813 -0.866957 0.012873 -v 0.553599 -0.864727 -0.006838 -v 0.542085 -0.861668 -0.017222 -v 0.526300 -0.869515 -0.022772 -v 0.504717 -0.851868 -0.018477 -v 0.496212 -0.850229 -0.012503 -v 0.486782 -0.848454 -0.000088 -v 0.483208 -0.848217 0.020926 -v 0.490379 -0.850628 0.040638 -v 0.511771 -0.797654 0.052865 -v 0.521599 -0.798095 0.057166 -v 0.529095 -0.795705 0.058540 -v 0.542948 -0.806171 0.056696 -v 0.549313 -0.808598 0.054058 -v 0.558515 -0.805480 0.048285 -v 0.564904 -0.807191 0.040534 -v 0.569312 -0.810565 0.029866 -v 0.571121 -0.810148 0.014688 -v 0.563792 -0.808378 -0.005016 -v 0.551989 -0.806389 -0.015458 -v 0.539038 -0.803915 -0.020597 -v 0.522505 -0.801614 -0.019925 -v 0.513642 -0.803761 -0.016966 -v 0.490307 -0.809938 0.022181 -v 0.497577 -0.811875 0.041905 -v 0.548161 -0.664823 0.064271 -v 0.507631 -0.798876 -0.012772 -v 0.499746 -0.799150 -0.004791 -v 0.494850 -0.796287 0.005120 -v 0.495216 -0.766682 0.023871 -v 0.502572 -0.767850 0.043626 -v 0.555610 -0.659950 0.063053 -v 0.561750 -0.660422 0.060717 -v 0.568347 -0.661672 0.056709 -v 0.581465 -0.662983 0.038756 -v 0.581840 -0.686053 0.020117 -v 0.579485 -0.660022 0.006478 -v 0.573531 -0.658537 -0.002040 -v 0.562747 -0.655916 -0.010448 -v 0.551053 -0.651569 -0.014042 -v 0.545959 -0.632301 -0.013571 -v 0.536063 -0.654088 -0.013676 -v 0.524096 -0.681159 -0.011603 -v 0.518160 -0.651345 -0.004367 -v 0.506563 -0.676192 0.006243 -v 0.527283 -0.650533 0.060992 -v 0.539750 -0.650782 0.064768 -v 0.576866 -0.659322 0.048573 -v 0.505638 -0.646608 0.029316 -v 0.512977 -0.648380 0.049001 -v 0.585792 -0.652345 0.021632 -v 0.528734 -0.641607 -0.009825 -v 0.510676 -0.638985 0.008555 -v 0.533160 -0.611850 0.060961 -v 0.547748 -0.600002 0.065794 -v 0.557696 -0.602720 0.067136 -v 0.561029 -0.622402 0.065329 -v 0.569851 -0.624070 0.062057 -v 0.578169 -0.626376 0.056036 -v 0.587410 -0.628732 0.043577 -v 0.590915 -0.629033 0.022593 -v 0.583923 -0.625947 0.002933 -v 0.572674 -0.621875 -0.007408 -v 0.567129 -0.607233 -0.011123 -v 0.565706 -0.586449 -0.011768 -v 0.544012 -0.613800 -0.011877 -v 0.535773 -0.610159 -0.008501 -v 0.527541 -0.607671 -0.002479 -v 0.518366 -0.605135 0.009992 -v 0.514869 -0.604848 0.030973 -v 0.521902 -0.607837 0.050629 -v 0.543964 -0.586888 0.061950 -v 0.568966 -0.602929 0.066112 -v 0.577096 -0.606866 0.062715 -v 0.584621 -0.610975 0.056616 -v 0.592989 -0.615325 0.044054 -v 0.596150 -0.616337 0.023051 -v 0.589832 -0.611722 0.003443 -v 0.581141 -0.597703 -0.008017 -v 0.553831 -0.590671 -0.010998 -v 0.546288 -0.585555 -0.007502 -v 0.538867 -0.581365 -0.001409 -v 0.530581 -0.576914 0.011165 -v 0.527436 -0.575919 0.032150 -v 0.533798 -0.580457 0.051734 -v 0.566292 -0.565468 0.064841 -v 0.570682 -0.571410 0.067202 -v 0.581119 -0.575873 0.068378 -v 0.582073 -0.584058 0.067416 -v 0.581607 -0.593647 0.065259 -v 0.587958 -0.600035 0.060364 -v 0.595653 -0.607634 0.049202 -v 0.600273 -0.611376 0.028488 -v 0.599132 -0.605112 0.008290 -v 0.590387 -0.598761 -0.003373 -v 0.586024 -0.576452 -0.010947 -v 0.566905 -0.574070 -0.010283 -v 0.558202 -0.564138 -0.004552 -v 0.548401 -0.554487 0.012277 -v 0.545805 -0.552874 0.033272 -v 0.550739 -0.559247 0.052761 -v 0.595586 -0.585222 0.065838 -v 0.598925 -0.593585 0.060674 -v 0.602036 -0.600429 0.053175 -v 0.604463 -0.605177 0.043840 -v 0.606414 -0.607795 0.028647 -v 0.602504 -0.592507 -0.002191 -v 0.596636 -0.583518 -0.008155 -v 0.592501 -0.563687 -0.010119 -v 0.581231 -0.558642 -0.007875 -v 0.575280 -0.547372 -0.000015 -v 0.569602 -0.537912 0.018652 -v 0.568776 -0.538525 0.039689 -v 0.572900 -0.548618 0.057889 -v 0.594483 -0.545017 0.061703 -v 0.609829 -0.552187 0.068219 -v 0.610478 -0.572067 0.069711 -v 0.616121 -0.600311 0.011305 -v 0.599520 -0.547746 -0.004957 -v 0.594866 -0.533320 0.010105 -v 0.592185 -0.528678 0.030741 -v 0.599299 -0.530127 0.045474 -v 0.595725 -0.536378 0.054090 -v 0.618037 -0.585317 0.065789 -v 0.621848 -0.599922 0.050583 -v 0.633145 -0.603398 0.029273 -v 0.643100 -0.576026 -0.003286 -v 0.625938 -0.567450 -0.007794 -v 0.622947 -0.553202 -0.006847 -v 0.620850 -0.535577 0.003765 -v 0.617335 -0.525586 0.022535 -v 0.620817 -0.524811 0.037788 -v 0.652212 -0.543085 0.072811 -v 0.623321 -0.558990 0.071737 -v 0.637049 -0.570285 0.074379 -v 0.641649 -0.588511 0.066743 -v 0.660982 -0.600699 0.051253 -v 0.674107 -0.584692 0.008371 -v 0.654715 -0.565991 -0.002727 -v 0.653539 -0.551394 -0.001653 -v 0.651609 -0.533620 0.009244 -v 0.652217 -0.523410 0.029382 -v 0.674619 -0.554608 0.083660 -v 0.695643 -0.569000 0.089051 -v 0.695327 -0.578642 0.085761 -v 0.695412 -0.587376 0.080035 -v 0.695151 -0.594598 0.071782 -v 0.692156 -0.595791 0.024505 -v 0.695880 -0.569677 0.007782 -v 0.698679 -0.549923 0.009794 -v 0.712317 -0.536263 0.021367 -v 0.707101 -0.527601 0.030247 -v 0.714370 -0.525706 0.071817 -v 0.658173 -0.534144 0.067368 -v 0.735277 -0.553498 0.101663 -v 0.849191 -0.597787 0.087208 -v 0.763308 -0.587326 0.037467 -v 0.759271 -0.568599 0.026606 -v 0.764485 -0.548711 0.029517 -v 0.744281 -0.534528 0.094909 -v 0.833315 -0.542250 0.130198 -v 0.832557 -0.548610 0.132484 -v 0.832351 -0.553696 0.133479 -v 0.832363 -0.558814 0.133834 -v 0.832585 -0.563898 0.133566 -v 0.833011 -0.568884 0.132688 -v 0.833634 -0.573707 0.131217 -v 0.834857 -0.579596 0.128415 -v 0.837067 -0.587762 0.122264 -v 0.839969 -0.593996 0.114098 -v 0.844607 -0.598828 0.100688 -v 0.853021 -0.591836 0.075395 -v 0.855360 -0.584832 0.067909 -v 0.856944 -0.575604 0.062269 -v 0.857662 -0.569114 0.059966 -v 0.857868 -0.564027 0.058971 -v 0.857857 -0.558910 0.058615 -v 0.857635 -0.553825 0.058884 -v 0.857209 -0.548839 0.059762 -v 0.856586 -0.544016 0.061232 -v 0.855386 -0.538011 0.064062 -v 0.853144 -0.529830 0.070263 -v 0.849077 -0.521609 0.081737 -v 0.843088 -0.519402 0.099177 -v 0.837996 -0.523999 0.114567 -v 0.834862 -0.533030 0.124606 -v 0.792485 -0.924379 0.131535 -v 0.792581 -0.918992 0.131177 -v 0.792997 -0.913826 0.130203 -v 0.794155 -0.907441 0.128019 -v 0.796490 -0.898740 0.123024 -v 0.799795 -0.891868 0.116323 -v 0.805313 -0.885601 0.105297 -v 0.817169 -0.887223 0.082412 -v 0.822510 -0.895054 0.072178 -v 0.825553 -0.902959 0.066367 -v 0.827179 -0.908927 0.063587 -v 0.828022 -0.913826 0.062105 -v 0.828572 -0.918992 0.061201 -v 0.828807 -0.924379 0.060914 -v 0.828712 -0.929765 0.061273 -v 0.828296 -0.934931 0.062246 -v 0.827581 -0.939830 0.063793 -v 0.826128 -0.945949 0.066703 -v 0.823242 -0.953797 0.072646 -v 0.818048 -0.961557 0.083054 -v 0.806364 -0.963260 0.105717 -v 0.800591 -0.956799 0.116855 -v 0.797141 -0.949876 0.123439 -v 0.794682 -0.941128 0.128263 -v 0.793271 -0.934931 0.130344 -v 0.792721 -0.929765 0.131249 -v 0.710394 -0.926911 0.085127 -v 0.710693 -0.910243 0.078166 -v 0.716631 -0.896681 0.063758 -v 0.726990 -0.892413 0.045864 -v 0.738515 -0.899009 0.029715 -v 0.745173 -0.912582 0.019344 -v 0.754416 -0.928715 0.018725 -v 0.754927 -0.948558 0.023516 -v 0.750205 -0.964033 0.036412 -v 0.740714 -0.970955 0.053804 -v 0.728811 -0.967034 0.070827 -v 0.717359 -0.954380 0.081587 -v 0.713589 -0.939977 0.086241 -v 0.660209 -0.950390 0.048891 -v 0.656064 -0.937217 0.041723 -v 0.657978 -0.923034 0.026649 -v 0.667603 -0.916135 0.009260 -v 0.682254 -0.918719 -0.005494 -v 0.693709 -0.926495 -0.012135 -v 0.704569 -0.937212 -0.014340 -v 0.712155 -0.950444 -0.011417 -v 0.716263 -0.963613 -0.004349 -v 0.714262 -0.977967 0.010691 -v 0.704493 -0.984928 0.028185 -v 0.687893 -0.981785 0.044037 -v 0.675189 -0.971747 0.050485 -v 0.668232 -0.960908 0.052939 -v 0.625824 -0.967195 -0.002022 -v 0.632657 -0.959629 -0.020476 -v 0.648075 -0.957903 -0.034817 -v 0.661822 -0.960948 -0.040076 -v 0.675917 -0.965778 -0.040662 -v 0.691238 -0.978009 -0.034439 -v 0.700506 -0.989127 -0.018409 -v 0.698718 -0.998976 0.000130 -v 0.686635 -1.003944 0.017543 -v 0.673765 -1.003190 0.025574 -v 0.660092 -1.000029 0.029376 -v 0.643419 -0.987588 0.028673 -v 0.629783 -0.979190 0.016487 -v 0.618434 -1.022666 -0.007893 -v 0.619735 -1.014984 -0.038054 -v 0.629283 -1.012444 -0.050139 -v 0.637916 -1.011487 -0.056058 -v 0.647194 -1.011537 -0.059543 -v 0.657082 -1.011851 -0.060618 -v 0.671730 -1.013256 -0.058012 -v 0.694380 -1.017737 -0.033901 -v 0.695214 -1.028014 -0.016521 -v 0.685894 -1.032740 0.002355 -v 0.673531 -1.034551 0.011284 -v 0.664511 -1.034932 0.014495 -v 0.654791 -1.034072 0.015746 -v 0.640137 -1.031132 0.013596 -v 0.619920 -1.076930 -0.013938 -v 0.616098 -1.019855 -0.021492 -v 0.624789 -1.073009 -0.058233 -v 0.632828 -1.072075 -0.065488 -v 0.641898 -1.071974 -0.070136 -v 0.651054 -1.071216 -0.072155 -v 0.665647 -1.070948 -0.071345 -v 0.684377 -1.071949 -0.060350 -v 0.693569 -1.095820 -0.047325 -v 0.693614 -1.076366 -0.020736 -v 0.685874 -1.080359 -0.007733 -v 0.677808 -1.081417 -0.000482 -v 0.668783 -1.082652 0.003974 -v 0.659724 -1.082381 0.006133 -v 0.649473 -1.082270 0.005966 -v 0.634976 -1.080926 0.001370 -v 0.629979 -1.143205 -0.007708 -v 0.617808 -1.167354 -0.026334 -v 0.615849 -1.082036 -0.039526 -v 0.618034 -1.082002 -0.048489 -v 0.622167 -1.166826 -0.062017 -v 0.629107 -1.139843 -0.068445 -v 0.637951 -1.139517 -0.074355 -v 0.646395 -1.139328 -0.077321 -v 0.655723 -1.139284 -0.078401 -v 0.666000 -1.139356 -0.077000 -v 0.679769 -1.139704 -0.070612 -v 0.690314 -1.166836 -0.059503 -v 0.693005 -1.149166 -0.025526 -v 0.685693 -1.168470 -0.014274 -v 0.678282 -1.143272 -0.006128 -v 0.668904 -1.143558 -0.001283 -v 0.657243 -1.143679 0.001103 -v 0.644102 -1.143595 -0.000615 -v 0.630475 -1.214979 -0.008840 -v 0.617648 -1.310100 -0.052682 -v 0.628660 -1.213755 -0.069855 -v 0.637457 -1.213633 -0.075887 -v 0.646335 -1.212272 -0.079078 -v 0.656048 -1.213557 -0.080133 -v 0.666331 -1.213635 -0.078616 -v 0.680277 -1.215017 -0.071875 -v 0.695221 -1.249311 -0.044074 -v 0.677837 -1.213861 -0.007498 -v 0.668771 -1.212343 -0.002902 -v 0.657778 -1.214006 -0.000532 -v 0.644429 -1.213663 -0.002096 -v 0.622607 -1.318800 -0.014549 -v 0.615853 -1.324621 -0.028455 -v 0.615240 -1.496994 -0.035039 -v 0.624423 -1.324972 -0.062943 -v 0.632643 -1.290603 -0.071586 -v 0.641716 -1.288857 -0.076255 -v 0.650740 -1.290694 -0.078275 -v 0.665343 -1.292095 -0.077491 -v 0.679979 -1.324308 -0.068455 -v 0.690371 -1.326153 -0.056320 -v 0.694252 -1.423106 -0.030221 -v 0.692796 -1.271504 -0.026908 -v 0.688799 -1.328491 -0.016362 -v 0.681842 -1.288593 -0.009279 -v 0.670567 -1.285037 -0.002113 -v 0.659520 -1.287739 0.000736 -v 0.649276 -1.287783 0.000562 -v 0.634784 -1.289491 -0.004099 -v 0.627550 -1.402751 -0.002686 -v 0.632261 -1.366899 -0.067447 -v 0.641326 -1.364879 -0.072196 -v 0.650398 -1.367168 -0.074147 -v 0.663087 -1.400427 -0.071362 -v 0.672963 -1.404497 -0.067492 -v 0.679399 -1.366486 -0.003061 -v 0.668211 -1.359916 0.002511 -v 0.659137 -1.361835 0.004745 -v 0.646885 -1.392122 0.006313 -v 0.636618 -1.397366 0.003189 -v 0.615793 -1.483915 -0.011100 -v 0.613748 -1.491430 -0.024961 -v 0.622563 -1.502551 -0.047699 -v 0.634332 -1.441646 -0.063076 -v 0.644976 -1.440296 -0.067323 -v 0.655042 -1.439171 -0.068530 -v 0.686850 -1.439275 -0.052231 -v 0.692365 -1.493638 -0.031732 -v 0.687118 -1.497005 -0.000949 -v 0.676767 -1.480840 0.008168 -v 0.667864 -1.429358 0.008085 -v 0.656653 -1.432055 0.010760 -v 0.620548 -1.495001 0.001100 -v 0.630266 -1.552077 -0.050648 -v 0.635894 -1.558526 -0.052521 -v 0.650779 -1.502062 -0.061924 -v 0.668407 -1.504445 -0.058866 -v 0.685476 -1.503957 -0.045338 -v 0.692736 -1.499291 -0.017201 -v 0.658858 -1.544051 0.022413 -v 0.652402 -1.494014 0.017286 -v 0.637651 -1.494077 0.014578 -v 0.616093 -1.546750 0.007804 -v 0.608712 -1.548391 -0.011964 -v 0.612239 -1.551874 -0.032553 -v 0.621624 -1.555200 -0.044562 -v 0.635282 -1.604177 -0.047704 -v 0.648896 -1.559466 -0.054451 -v 0.663337 -1.562481 -0.051324 -v 0.680206 -1.563571 -0.037570 -v 0.687570 -1.562004 -0.017793 -v 0.684036 -1.558547 0.002795 -v 0.674622 -1.555452 0.014827 -v 0.665886 -1.552405 0.020661 -v 0.643240 -1.576853 0.028396 -v 0.633082 -1.547177 0.021465 -v 0.606688 -1.586746 0.013890 -v 0.599608 -1.587306 -0.006036 -v 0.602849 -1.591875 -0.026457 -v 0.611660 -1.597249 -0.038144 -v 0.621666 -1.603171 -0.044600 -v 0.651130 -1.613087 -0.043607 -v 0.667120 -1.617229 -0.029367 -v 0.674179 -1.616723 -0.009423 -v 0.670935 -1.612127 0.011001 -v 0.662087 -1.606874 0.022687 -v 0.653965 -1.602908 0.028196 -v 0.645417 -1.600131 0.031079 -v 0.622906 -1.590320 0.028040 -v 0.591969 -1.614952 0.019492 -v 0.585640 -1.613421 -0.000694 -v 0.587873 -1.619053 -0.021066 -v 0.595084 -1.627201 -0.032633 -v 0.601698 -1.633662 -0.037790 -v 0.608703 -1.639983 -0.040261 -v 0.616632 -1.646621 -0.040303 -v 0.628336 -1.655499 -0.035899 -v 0.641579 -1.663687 -0.020643 -v 0.647327 -1.664688 -0.000295 -v 0.644508 -1.658598 0.019838 -v 0.637259 -1.650545 0.030684 -v 0.630541 -1.644031 0.035634 -v 0.624224 -1.638382 0.037731 -v 0.603095 -1.647531 0.040786 -v 0.605541 -1.623544 0.034111 -v 0.568202 -1.633850 0.022686 -v 0.563997 -1.631035 0.002066 -v 0.565323 -1.637928 -0.018022 -v 0.569877 -1.648301 -0.028764 -v 0.574206 -1.656798 -0.033418 -v 0.578850 -1.664704 -0.035327 -v 0.581693 -1.674351 -0.034715 -v 0.590526 -1.685710 -0.030125 -v 0.599661 -1.697758 -0.014445 -v 0.603933 -1.700516 0.006141 -v 0.602646 -1.693628 0.026185 -v 0.598219 -1.683218 0.036912 -v 0.594046 -1.674684 0.041549 -v 0.590261 -1.666211 0.043376 -v 0.576881 -1.646165 0.038409 -v 0.534937 -1.648505 0.030808 -v 0.531554 -1.642401 0.010596 -v 0.530602 -1.647218 -0.009922 -v 0.531716 -1.657169 -0.021742 -v 0.533321 -1.666242 -0.027536 -v 0.536203 -1.675568 -0.030587 -v 0.535332 -1.687437 -0.030652 -v 0.541629 -1.703402 -0.025076 -v 0.546512 -1.717143 -0.008910 -v 0.549390 -1.720452 0.011811 -v 0.549744 -1.712983 0.031630 -v 0.548286 -1.701457 0.042052 -v 0.547428 -1.689023 0.047108 -v 0.541771 -1.678510 0.048225 -v 0.539638 -1.664398 0.044843 -v 0.493364 -1.652651 0.034963 -v 0.490934 -1.646437 0.014650 -v 0.488503 -1.651409 -0.005698 -v 0.487109 -1.661651 -0.017261 -v 0.474528 -1.672234 -0.021964 -v 0.486344 -1.680617 -0.025505 -v 0.485195 -1.690052 -0.025669 -v 0.486105 -1.704268 -0.022125 -v 0.487829 -1.720455 -0.007763 -v 0.490289 -1.726661 0.012535 -v 0.492857 -1.721695 0.032854 -v 0.495788 -1.706299 0.048180 -v 0.496707 -1.692117 0.052540 -v 0.494845 -1.682676 0.052938 -v 0.494964 -1.668874 0.049351 -v 0.452293 -1.648138 0.040765 -v 0.449853 -1.641914 0.020471 -v 0.445090 -1.646624 0.000471 -v 0.440832 -1.656580 -0.010681 -v 0.434879 -1.675278 -0.018179 -v 0.432175 -1.684181 -0.017948 -v 0.430267 -1.697941 -0.014072 -v 0.429395 -1.713894 0.000702 -v 0.431846 -1.720121 0.021003 -v 0.436870 -1.715469 0.040950 -v 0.446930 -1.701375 0.055038 -v 0.447391 -1.686854 0.059540 -v 0.448570 -1.677647 0.059605 -v 0.451461 -1.664113 0.055544 -v 0.418609 -1.635814 0.048100 -v 0.415029 -1.629590 0.028006 -v 0.406436 -1.633233 0.009155 -v 0.394723 -1.646296 -0.003890 -v 0.387274 -1.658402 -0.006526 -v 0.382950 -1.665896 -0.005773 -v 0.377859 -1.678207 -0.001377 -v 0.374647 -1.693523 0.013674 -v 0.377820 -1.700436 0.033669 -v 0.386142 -1.697463 0.052775 -v 0.397574 -1.684796 0.066504 -v 0.406142 -1.672447 0.069476 -v 0.410335 -1.663745 0.068870 -v 0.415806 -1.650776 0.063761 -v 0.395680 -1.615740 0.054756 -v 0.388933 -1.610138 0.035399 -v 0.375069 -1.612306 0.019596 -v 0.361877 -1.618042 0.013551 -v 0.354541 -1.625309 0.011171 -v 0.347611 -1.632254 0.011674 -v 0.341166 -1.639081 0.014649 -v 0.334103 -1.649626 0.022513 -v 0.331647 -1.662013 0.040195 -v 0.338322 -1.667542 0.059518 -v 0.351828 -1.664977 0.075572 -v 0.363012 -1.656822 0.083035 -v 0.372404 -1.651960 0.084006 -v 0.380129 -1.646038 0.082947 -v 0.386126 -1.638954 0.080291 -v 0.393191 -1.628273 0.072516 -v 0.381181 -1.588977 0.069898 -v 0.373636 -1.582027 0.051369 -v 0.357695 -1.580545 0.037575 -v 0.337631 -1.583971 0.032889 -v 0.323072 -1.591066 0.035708 -v 0.317299 -1.596789 0.038539 -v 0.308740 -1.604854 0.047903 -v 0.305511 -1.615692 0.066307 -v 0.313010 -1.622563 0.084830 -v 0.328808 -1.623761 0.098778 -v 0.343206 -1.620346 0.103585 -v 0.352838 -1.616068 0.104256 -v 0.359507 -1.611858 0.103300 -v 0.369397 -1.608136 0.097595 -v 0.377952 -1.599950 0.088306 -v 0.372180 -1.553392 0.099519 -v 0.370022 -1.544829 0.079919 -v 0.357618 -1.539323 0.063851 -v 0.338049 -1.537873 0.055350 -v 0.323098 -1.539155 0.056109 -v 0.306122 -1.546785 0.063036 -v 0.298410 -1.548136 0.072306 -v 0.293641 -1.556830 0.082994 -v 0.295767 -1.565163 0.102665 -v 0.308126 -1.570577 0.118753 -v 0.327582 -1.571599 0.127462 -v 0.342307 -1.568229 0.127653 -v 0.362981 -1.561246 0.117030 -v 0.368678 -1.495032 0.124261 -v 0.366876 -1.490977 0.101540 -v 0.354457 -1.485468 0.085462 -v 0.334739 -1.483032 0.077351 -v 0.319974 -1.482776 0.078634 -v 0.306940 -1.488828 0.082340 -v 0.289990 -1.497842 0.107332 -v 0.291908 -1.502197 0.127971 -v 0.304296 -1.507460 0.144120 -v 0.323924 -1.509856 0.152248 -v 0.338748 -1.508193 0.151666 -v 0.351988 -1.508976 0.145527 -v 0.370416 -1.350686 0.162097 -v 0.369945 -1.397542 0.140178 -v 0.365144 -1.422525 0.117567 -v 0.350539 -1.418023 0.102963 -v 0.335993 -1.416120 0.098388 -v 0.325319 -1.415854 0.098250 -v 0.315953 -1.415673 0.100650 -v 0.307082 -1.417524 0.104990 -v 0.296348 -1.422324 0.114621 -v 0.294634 -1.431124 0.154913 -v 0.309229 -1.434658 0.169755 -v 0.323757 -1.435679 0.174566 -v 0.334362 -1.435901 0.174739 -v 0.345928 -1.434163 0.172095 -v 0.360156 -1.434275 0.161500 -v 0.366846 -1.342626 0.134604 -v 0.352194 -1.338950 0.119747 -v 0.337640 -1.337241 0.115100 -v 0.326925 -1.336913 0.114990 -v 0.315761 -1.336091 0.118101 -v 0.304799 -1.338261 0.125075 -v 0.295404 -1.342568 0.136520 -v 0.290480 -1.402085 0.145882 -v 0.296419 -1.347350 0.172790 -v 0.311094 -1.349343 0.187919 -v 0.325652 -1.349931 0.192757 -v 0.336303 -1.350210 0.192892 -v 0.347586 -1.348875 0.190145 -v 0.361888 -1.349725 0.179614 -v 0.374386 -1.207152 0.175585 -v 0.375232 -1.192333 0.164291 -v 0.368993 -1.253504 0.145637 -v 0.354285 -1.251385 0.130524 -v 0.339719 -1.250107 0.125787 -v 0.329232 -1.249457 0.125698 -v 0.318249 -1.249319 0.128495 -v 0.303562 -1.251472 0.138762 -v 0.297107 -1.206919 0.156756 -v 0.298680 -1.254310 0.184316 -v 0.313412 -1.255370 0.199505 -v 0.327998 -1.255696 0.204316 -v 0.338875 -1.255742 0.204433 -v 0.348020 -1.255824 0.202101 -v 0.356737 -1.256101 0.197448 -v 0.367363 -1.257522 0.186760 -v 0.372524 -1.162345 0.148967 -v 0.358043 -1.162114 0.133537 -v 0.343278 -1.161592 0.128441 -v 0.332354 -1.160860 0.128224 -v 0.320812 -1.161095 0.131313 -v 0.306329 -1.159486 0.142038 -v 0.295288 -1.206055 0.170595 -v 0.300092 -1.157988 0.183024 -v 0.312964 -1.158103 0.199689 -v 0.326540 -1.158525 0.206053 -v 0.340392 -1.158683 0.207465 -v 0.350867 -1.159903 0.205048 -v 0.359753 -1.160214 0.200445 -v 0.370335 -1.160991 0.190239 -v 0.382257 -1.065205 0.165265 -v 0.379297 -1.076397 0.145507 -v 0.366567 -1.078671 0.128840 -v 0.352796 -1.079033 0.122246 -v 0.338936 -1.078271 0.120623 -v 0.327624 -1.077382 0.123263 -v 0.318874 -1.075468 0.127804 -v 0.308458 -1.071305 0.137952 -v 0.302382 -1.064766 0.157696 -v 0.307310 -1.065333 0.178819 -v 0.322097 -1.064049 0.193820 -v 0.336636 -1.064487 0.198632 -v 0.347229 -1.065221 0.198804 -v 0.358520 -1.067221 0.195971 -v 0.372761 -1.068583 0.185585 -v 0.389501 -0.991778 0.144572 -v 0.386188 -1.003423 0.126347 -v 0.373246 -1.008363 0.110523 -v 0.353338 -1.009123 0.102462 -v 0.338714 -1.007202 0.103481 -v 0.329793 -1.004520 0.106490 -v 0.318465 -0.998854 0.114402 -v 0.310615 -0.986648 0.130454 -v 0.312815 -0.983517 0.152120 -v 0.325639 -0.978973 0.168054 -v 0.345262 -0.978270 0.176150 -v 0.360164 -0.980830 0.175435 -v 0.369337 -0.983065 0.172123 -v 0.380675 -0.987417 0.163748 -v 0.398855 -0.941781 0.103314 -v 0.389973 -0.952197 0.087337 -v 0.372961 -0.956850 0.075902 -v 0.352682 -0.955045 0.072612 -v 0.335036 -0.947161 0.077047 -v 0.324045 -0.930926 0.085979 -v 0.321115 -0.921743 0.105571 -v 0.329818 -0.911991 0.122004 -v 0.346666 -0.907440 0.133473 -v 0.367564 -0.909926 0.137291 -v 0.385215 -0.917908 0.132312 -v 0.397403 -0.926367 0.118255 -v 0.412978 -0.896608 0.066186 -v 0.405773 -0.910621 0.052149 -v 0.390232 -0.917915 0.040295 -v 0.375595 -0.917635 0.035041 -v 0.360611 -0.913480 0.033661 -v 0.348420 -0.904395 0.035811 -v 0.341199 -0.889954 0.037366 -v 0.336156 -0.876689 0.053851 -v 0.343212 -0.863019 0.068255 -v 0.358654 -0.855817 0.080171 -v 0.378657 -0.857059 0.086830 -v 0.392790 -0.863735 0.086974 -v 0.404292 -0.873128 0.083215 -v 0.411066 -0.884221 0.075688 -v 0.429704 -0.872517 0.020987 -v 0.419730 -0.885716 0.008112 -v 0.402919 -0.890597 -0.003341 -v 0.388519 -0.887487 -0.009043 -v 0.374444 -0.880944 -0.010031 -v 0.365554 -0.868282 -0.010788 -v 0.359912 -0.857447 -0.006765 -v 0.358257 -0.845918 -0.000729 -v 0.361031 -0.833276 0.008219 -v 0.372953 -0.822058 0.021113 -v 0.390696 -0.819751 0.032045 -v 0.408227 -0.828944 0.040654 -v 0.420981 -0.837471 0.037072 -v 0.429931 -0.852080 0.032085 -v 0.452412 -0.850446 -0.011879 -v 0.447876 -0.862107 -0.021099 -v 0.435140 -0.870566 -0.035503 -v 0.416458 -0.869894 -0.046166 -v 0.402896 -0.857000 -0.056499 -v 0.392971 -0.839666 -0.057483 -v 0.389731 -0.825464 -0.052773 -v 0.390979 -0.811895 -0.045130 -v 0.400225 -0.799214 -0.031170 -v 0.414643 -0.796166 -0.016136 -v 0.430711 -0.803799 -0.004197 -v 0.445979 -0.818153 -0.002102 -v 0.452778 -0.836318 -0.005213 -v 0.477444 -0.835698 -0.031908 -v 0.475649 -0.848134 -0.041365 -v 0.468248 -0.857347 -0.058926 -v 0.456516 -0.856354 -0.076426 -v 0.446648 -0.844016 -0.091244 -v 0.439421 -0.828535 -0.096178 -v 0.436466 -0.817088 -0.095697 -v 0.434736 -0.802938 -0.091021 -v 0.437866 -0.786991 -0.076733 -v 0.445503 -0.780588 -0.058214 -v 0.456194 -0.784789 -0.040391 -v 0.467976 -0.798777 -0.028708 -v 0.473766 -0.812282 -0.026186 -v 0.476694 -0.823295 -0.027219 -v 0.506386 -0.830187 -0.040866 -v 0.507477 -0.841945 -0.051583 -v 0.507170 -0.849152 -0.071376 -v 0.504727 -0.845610 -0.091999 -v 0.501356 -0.831610 -0.108121 -v 0.498386 -0.815007 -0.113977 -v 0.496727 -0.804219 -0.113678 -v 0.494866 -0.790814 -0.109444 -v 0.493639 -0.775398 -0.094111 -v 0.494254 -0.770109 -0.073824 -v 0.496993 -0.775653 -0.053540 -v 0.500524 -0.791164 -0.038920 -v 0.502935 -0.805505 -0.035012 -v 0.504715 -0.817481 -0.035518 -v 0.551966 -0.833652 -0.040979 -v 0.556485 -0.841620 -0.053401 -v 0.566424 -0.842733 -0.072282 -v 0.566432 -0.833612 -0.092053 -v 0.567901 -0.816140 -0.103408 -v 0.566980 -0.801386 -0.105271 -v 0.565675 -0.789032 -0.102911 -v 0.562974 -0.777716 -0.096285 -v 0.559319 -0.767996 -0.084797 -v 0.555428 -0.763739 -0.064624 -v 0.549774 -0.770586 -0.045441 -v 0.546280 -0.786885 -0.032071 -v 0.545826 -0.806379 -0.028422 -v 0.548913 -0.823079 -0.032789 -v 0.614581 -0.827305 -0.009640 -v 0.623424 -0.838899 -0.024847 -v 0.665633 -0.839349 -0.026491 -v 0.641319 -0.830537 -0.060214 -v 0.645269 -0.810969 -0.071667 -v 0.644464 -0.788282 -0.071113 -v 0.639310 -0.770530 -0.060478 -v 0.631318 -0.761429 -0.043444 -v 0.618091 -0.767742 -0.016176 -v 0.610915 -0.783916 -0.004573 -v 0.609223 -0.798813 -0.001662 -v 0.609940 -0.811526 -0.002744 -v 0.699266 -0.826763 0.042210 -v 0.710147 -0.838263 0.028383 -v 0.731794 -0.829876 -0.004679 -v 0.737193 -0.812407 -0.014541 -v 0.736233 -0.792153 -0.016533 -v 0.732765 -0.773496 -0.007689 -v 0.723981 -0.762030 0.007518 -v 0.677417 -0.760467 -0.003323 -v 0.709029 -0.764318 0.034376 -v 0.696633 -0.780738 0.046878 -v 0.691593 -0.805666 0.048982 -v 0.804917 -0.826956 0.119818 -v 0.811223 -0.835731 0.111556 -v 0.825222 -0.841317 0.093396 -v 0.834582 -0.836350 0.081443 -v 0.839959 -0.830126 0.074610 -v 0.844053 -0.821963 0.069424 -v 0.846096 -0.815727 0.066998 -v 0.847097 -0.810877 0.065794 -v 0.847710 -0.805883 0.065090 -v 0.847930 -0.800811 0.064893 -v 0.847752 -0.795727 0.065209 -v 0.847169 -0.790699 0.066042 -v 0.846176 -0.785792 0.067400 -v 0.844077 -0.779500 0.070079 -v 0.839918 -0.771301 0.075652 -v 0.832815 -0.763758 0.084955 -v 0.817439 -0.762237 0.104675 -v 0.809643 -0.768778 0.114660 -v 0.804885 -0.776057 0.120701 -v 0.801591 -0.784870 0.124890 -v 0.799985 -0.791314 0.126655 -v 0.799372 -0.796308 0.127359 -v 0.799152 -0.801380 0.127556 -v 0.799330 -0.806464 0.127241 -v 0.799913 -0.811492 0.126407 -v 0.801567 -0.817971 0.124424 -v 0.864661 -0.070831 0.152564 -v 0.867422 -0.071103 0.175803 -v 0.893014 -0.078872 0.152564 -v 0.895454 -0.080179 0.175803 -v 0.916114 -0.097154 0.152564 -v 0.917854 -0.099282 0.175803 -v 0.930443 -0.122871 0.152564 -v 0.933857 -0.153432 0.152564 -v 0.933568 -0.133154 0.175803 -v 0.924547 -0.182739 0.152564 -v 0.928728 -0.174947 0.175803 -v 0.905476 -0.205166 0.152564 -v 0.907597 -0.203420 0.175803 -v 0.879255 -0.218591 0.152564 -v 0.881902 -0.217785 0.175803 -v 0.849879 -0.220947 0.152564 -v 0.852639 -0.221219 0.175803 -v 0.821847 -0.211870 0.152564 -v 0.824288 -0.213178 0.175803 -v 0.799447 -0.192768 0.152564 -v 0.801187 -0.194895 0.175803 -v 0.783733 -0.158895 0.152563 -v 0.786858 -0.169178 0.175803 -v 0.788573 -0.117102 0.152564 -v 0.783444 -0.138617 0.175803 -v 0.792754 -0.109310 0.175803 -v 0.809704 -0.088629 0.152564 -v 0.811825 -0.086883 0.175803 -v 0.835399 -0.074265 0.152564 -v 0.838046 -0.073459 0.175803 -v 0.866228 -0.069081 0.164181 -v 0.895109 -0.077845 0.164242 -v 0.918423 -0.096985 0.164297 -v 0.932631 -0.123565 0.164339 -v 0.935602 -0.146025 0.164367 -v 0.932625 -0.168443 0.165355 -v 0.922633 -0.188777 0.164343 -v 0.907706 -0.205784 0.164301 -v 0.881107 -0.220007 0.164247 -v 0.851073 -0.222968 0.164186 -v 0.822192 -0.214205 0.164125 -v 0.798878 -0.195065 0.164069 -v 0.784669 -0.168485 0.164028 -v 0.781699 -0.146025 0.164000 -v 0.784676 -0.123606 0.163012 -v 0.794668 -0.103273 0.164024 -v 0.809595 -0.086265 0.164065 -v 0.836194 -0.072042 0.164120 -v 0.869404 -0.109177 0.175808 -v 0.892300 -0.127618 0.175810 -v 0.896207 -0.149732 0.175804 -v 0.891650 -0.164203 0.175804 -v 0.876482 -0.180004 0.175809 -v 0.846972 -0.182594 0.175807 -v 0.824389 -0.163343 0.175805 -v 0.822070 -0.134867 0.175805 -v 0.839905 -0.112522 0.175806 -v 0.857464 -0.125197 0.188053 -v 0.872639 -0.130561 0.188055 -v 0.879642 -0.146144 0.188062 -v 0.872631 -0.161489 0.188055 -v 0.857515 -0.166854 0.188054 -v 0.843002 -0.159823 0.188053 -v 0.837950 -0.146037 0.188053 -v 0.842972 -0.132261 0.188053 -v 0.861133 -0.309003 0.152564 -v 0.863893 -0.309275 0.175803 -v 0.889485 -0.317044 0.152564 -v 0.891925 -0.318352 0.175803 -v 0.912585 -0.335327 0.152564 -v 0.914326 -0.337454 0.175803 -v 0.926915 -0.361044 0.152564 -v 0.930328 -0.391605 0.152564 -v 0.930040 -0.371327 0.175803 -v 0.921019 -0.420912 0.152564 -v 0.925200 -0.413120 0.175803 -v 0.901948 -0.443339 0.152564 -v 0.904068 -0.441593 0.175803 -v 0.875726 -0.456763 0.152564 -v 0.878374 -0.455957 0.175803 -v 0.846351 -0.459120 0.152564 -v 0.849111 -0.459391 0.175803 -v 0.818319 -0.450043 0.152564 -v 0.820759 -0.451350 0.175803 -v 0.795918 -0.430940 0.152564 -v 0.797659 -0.433068 0.175803 -v 0.780204 -0.397068 0.152563 -v 0.783329 -0.407350 0.175803 -v 0.785044 -0.355275 0.152563 -v 0.779916 -0.376790 0.175803 -v 0.789226 -0.347483 0.175803 -v 0.806176 -0.326802 0.152564 -v 0.808296 -0.325056 0.175803 -v 0.831870 -0.312438 0.152564 -v 0.834518 -0.311631 0.175803 -v 0.862700 -0.307254 0.164181 -v 0.891580 -0.316017 0.164242 -v 0.914894 -0.335157 0.164297 -v 0.929103 -0.361738 0.164339 -v 0.932073 -0.384197 0.164367 -v 0.929097 -0.406616 0.165355 -v 0.919104 -0.426950 0.164343 -v 0.904178 -0.443957 0.164301 -v 0.877579 -0.458180 0.164247 -v 0.847544 -0.461141 0.164186 -v 0.818664 -0.452378 0.164125 -v 0.795350 -0.433238 0.164069 -v 0.781141 -0.406657 0.164028 -v 0.778171 -0.384198 0.164000 -v 0.781147 -0.361779 0.163012 -v 0.791140 -0.341445 0.164024 -v 0.806066 -0.324438 0.164065 -v 0.832665 -0.310215 0.164120 -v 0.865876 -0.347349 0.175807 -v 0.888772 -0.365790 0.175810 -v 0.892679 -0.387904 0.175804 -v 0.888122 -0.402375 0.175804 -v 0.872954 -0.418177 0.175809 -v 0.843444 -0.420767 0.175807 -v 0.820860 -0.401516 0.175805 -v 0.818542 -0.373040 0.175804 -v 0.836376 -0.350695 0.175806 -v 0.853935 -0.363370 0.188053 -v 0.869111 -0.368734 0.188055 -v 0.876113 -0.384317 0.188062 -v 0.869102 -0.399661 0.188055 -v 0.853987 -0.405026 0.188054 -v 0.839473 -0.397995 0.188053 -v 0.834422 -0.384210 0.188053 -v 0.839443 -0.370433 0.188053 -v 0.852312 -0.548940 0.152564 -v 0.855072 -0.549212 0.175803 -v 0.880664 -0.556981 0.152564 -v 0.883104 -0.558289 0.175803 -v 0.903764 -0.575264 0.152564 -v 0.905505 -0.577391 0.175803 -v 0.918094 -0.600981 0.152564 -v 0.921507 -0.631542 0.152564 -v 0.921219 -0.611264 0.175803 -v 0.912197 -0.660849 0.152564 -v 0.916378 -0.653057 0.175803 -v 0.893127 -0.683275 0.152564 -v 0.895247 -0.681530 0.175803 -v 0.866905 -0.696700 0.152564 -v 0.869553 -0.695894 0.175803 -v 0.837530 -0.699056 0.152564 -v 0.840290 -0.699328 0.175803 -v 0.809497 -0.689980 0.152564 -v 0.811938 -0.691287 0.175803 -v 0.787097 -0.670877 0.152564 -v 0.788838 -0.673005 0.175803 -v 0.771383 -0.637004 0.152563 -v 0.774508 -0.647287 0.175803 -v 0.776223 -0.595212 0.152563 -v 0.771095 -0.616727 0.175803 -v 0.780404 -0.587420 0.175803 -v 0.797355 -0.566739 0.152564 -v 0.799475 -0.564993 0.175803 -v 0.823049 -0.552374 0.152564 -v 0.825696 -0.551568 0.175803 -v 0.853878 -0.547190 0.164181 -v 0.882759 -0.555954 0.164242 -v 0.906073 -0.575094 0.164297 -v 0.920282 -0.601674 0.164339 -v 0.923252 -0.624134 0.164367 -v 0.920275 -0.646552 0.165354 -v 0.910283 -0.666886 0.164343 -v 0.895357 -0.683894 0.164301 -v 0.868758 -0.698117 0.164247 -v 0.838723 -0.701078 0.164186 -v 0.809843 -0.692314 0.164125 -v 0.786528 -0.673174 0.164069 -v 0.772320 -0.646594 0.164028 -v 0.769349 -0.624134 0.163999 -v 0.772326 -0.601716 0.163012 -v 0.782318 -0.581382 0.164024 -v 0.797245 -0.564375 0.164065 -v 0.823844 -0.550152 0.164120 -v 0.857054 -0.587286 0.175807 -v 0.879950 -0.605727 0.175810 -v 0.883857 -0.627841 0.175804 -v 0.879301 -0.642312 0.175803 -v 0.864132 -0.658114 0.175808 -v 0.834623 -0.660704 0.175807 -v 0.812039 -0.641453 0.175805 -v 0.809721 -0.612976 0.175804 -v 0.827555 -0.590631 0.175806 -v 0.845114 -0.603307 0.188053 -v 0.860290 -0.608670 0.188055 -v 0.867292 -0.624254 0.188062 -v 0.860281 -0.639598 0.188055 -v 0.845166 -0.644963 0.188054 -v 0.830652 -0.637932 0.188053 -v 0.825601 -0.624147 0.188053 -v 0.830622 -0.610370 0.188053 -v 0.854076 -0.781820 0.152564 -v 0.856836 -0.782092 0.175803 -v 0.882428 -0.789861 0.152564 -v 0.884869 -0.791168 0.175803 -v 0.905528 -0.808143 0.152564 -v 0.907269 -0.810271 0.175803 -v 0.919858 -0.833861 0.152564 -v 0.923271 -0.864422 0.152564 -v 0.922983 -0.844144 0.175803 -v 0.913962 -0.893728 0.152564 -v 0.918143 -0.885937 0.175803 -v 0.894891 -0.916155 0.152564 -v 0.897012 -0.914409 0.175803 -v 0.868670 -0.929580 0.152564 -v 0.871317 -0.928774 0.175803 -v 0.839294 -0.931936 0.152564 -v 0.842054 -0.932208 0.175803 -v 0.811262 -0.922860 0.152564 -v 0.813702 -0.924167 0.175803 -v 0.788861 -0.903757 0.152564 -v 0.790602 -0.905885 0.175803 -v 0.773147 -0.869884 0.152563 -v 0.776272 -0.880167 0.175803 -v 0.777987 -0.828092 0.152563 -v 0.772859 -0.849607 0.175803 -v 0.782169 -0.820300 0.175803 -v 0.799119 -0.799619 0.152564 -v 0.801239 -0.797873 0.175803 -v 0.824813 -0.785254 0.152564 -v 0.827461 -0.784448 0.175803 -v 0.855643 -0.780070 0.164181 -v 0.884523 -0.788834 0.164242 -v 0.907837 -0.807974 0.164297 -v 0.922046 -0.834554 0.164339 -v 0.925017 -0.857014 0.164367 -v 0.922040 -0.879432 0.165354 -v 0.912048 -0.899766 0.164343 -v 0.897121 -0.916774 0.164301 -v 0.870522 -0.930997 0.164247 -v 0.840487 -0.933958 0.164186 -v 0.811607 -0.925194 0.164125 -v 0.788293 -0.906054 0.164069 -v 0.774084 -0.879474 0.164028 -v 0.771114 -0.857014 0.163999 -v 0.774090 -0.834596 0.163012 -v 0.784083 -0.814262 0.164024 -v 0.799010 -0.797254 0.164065 -v 0.825608 -0.783032 0.164120 -v 0.858819 -0.820166 0.175807 -v 0.881715 -0.838607 0.175810 -v 0.885622 -0.860721 0.175804 -v 0.881065 -0.875192 0.175803 -v 0.865897 -0.890993 0.175808 -v 0.836387 -0.893584 0.175807 -v 0.813803 -0.874333 0.175805 -v 0.811485 -0.845856 0.175804 -v 0.829319 -0.823511 0.175806 -v 0.846878 -0.836186 0.188053 -v 0.862054 -0.841550 0.188055 -v 0.869057 -0.857134 0.188062 -v 0.862045 -0.872478 0.188055 -v 0.846930 -0.877843 0.188054 -v 0.832416 -0.870812 0.188053 -v 0.827365 -0.857027 0.188053 -v 0.832386 -0.843250 0.188053 -v 0.549407 -0.752283 0.060389 -v 0.549483 -0.171365 0.060452 -v 0.561803 -0.752283 0.063645 -v 0.561095 -0.171365 0.063121 -v 0.567863 -0.752283 0.075335 -v 0.567783 -0.171365 0.074421 -v 0.563145 -0.752283 0.087748 -v 0.563708 -0.171365 0.087180 -v 0.553862 -0.752283 0.092180 -v 0.553938 -0.171365 0.092243 -v 0.542251 -0.752283 0.089511 -v 0.541542 -0.171365 0.088987 -v 0.535562 -0.752283 0.078211 -v 0.535483 -0.171365 0.077297 -v 0.539637 -0.752283 0.065452 -v 0.540200 -0.171365 0.064884 -v -0.274652 2.376465 0.911280 -v -0.274883 2.382449 0.919966 -v -0.274381 2.392164 0.924861 -v -0.274708 2.400415 0.924382 -v -0.274722 2.407608 0.920589 -v -0.275377 2.413649 0.912441 -v -0.276175 2.414717 0.897430 -v -0.274861 2.407642 0.874755 -v -0.274682 2.384140 0.834044 -v -0.274670 2.343477 0.776663 -v -0.274676 2.290448 0.710274 -v -0.274683 2.230371 0.643883 -v -0.274682 2.160406 0.577087 -v -0.274678 2.076008 0.507878 -v -0.274682 1.972518 0.434566 -v -0.274654 1.881815 0.378435 -v -0.274650 1.808710 0.337234 -v -0.274688 1.725403 0.295006 -v -0.274648 1.644114 0.260936 -v -0.274661 1.560635 0.230545 -v -0.274665 1.441607 0.191637 -v -0.274666 1.271739 0.140486 -v -0.274675 1.033134 0.072728 -v -0.274652 0.789891 0.006229 -v -0.439179 2.376465 0.896525 -v -0.440916 2.382348 0.904939 -v -0.441471 2.389592 0.909097 -v -0.441385 2.396311 0.910163 -v -0.441152 2.407419 0.906250 -v -0.440084 2.414630 0.893916 -v -0.436329 2.413602 0.876779 -v -0.431647 2.404425 0.854440 -v -0.425484 2.384161 0.820545 -v -0.415270 2.343510 0.764093 -v -0.403473 2.290491 0.698768 -v -0.391676 2.230432 0.633446 -v -0.379800 2.160490 0.567726 -v -0.367494 2.076130 0.499639 -v -0.354466 1.972696 0.427517 -v -0.344449 1.881902 0.372220 -v -0.337119 1.808829 0.331689 -v -0.329651 1.725685 0.290195 -v -0.323561 1.644328 0.256625 -v -0.318178 1.560945 0.226739 -v -0.311281 1.442088 0.188492 -v -0.302231 1.272716 0.138286 -v -0.291752 0.988295 0.058477 -v -0.278381 0.789899 0.005873 -v -0.594038 2.376465 0.853988 -v -0.597194 2.382250 0.861876 -v -0.598416 2.391865 0.866815 -v -0.598427 2.404142 0.865214 -v -0.595070 2.413669 0.854772 -v -0.590688 2.414719 0.840473 -v -0.581539 2.407673 0.819742 -v -0.567189 2.384179 0.781632 -v -0.547194 2.343525 0.727853 -v -0.524082 2.290514 0.665632 -v -0.500977 2.230483 0.603431 -v -0.477720 2.160563 0.540840 -v -0.453626 2.076233 0.475996 -v -0.428109 1.972844 0.407312 -v -0.400876 1.844437 0.333916 -v -0.379518 1.725966 0.276535 -v -0.362738 1.608273 0.231269 -v -0.336122 1.374520 0.159621 -v -0.736650 2.376465 0.786257 -v -0.741082 2.382170 0.793415 -v -0.743150 2.391800 0.798143 -v -0.743260 2.400727 0.797469 -v -0.740483 2.410278 0.792250 -v -0.735976 2.414971 0.780413 -v -0.727445 2.413680 0.769816 -v -0.717365 2.406672 0.753008 -v -0.697715 2.384198 0.719656 -v -0.668726 2.343544 0.670135 -v -0.635219 2.290547 0.612856 -v -0.601718 2.230533 0.555595 -v -0.568004 2.160637 0.497976 -v -0.533067 2.076326 0.438276 -v -0.496073 1.972968 0.375044 -v -0.456589 1.844655 0.307493 -v -0.425629 1.726186 0.254646 -v -0.401301 1.608676 0.213000 -v -0.362691 1.374987 0.147029 -v -0.309636 0.978913 0.047080 -v -0.864428 2.376465 0.695921 -v -0.869973 2.382111 0.702180 -v -0.872840 2.391764 0.706535 -v -0.872259 2.404409 0.704909 -v -0.865630 2.413692 0.696246 -v -0.856031 2.414635 0.683519 -v -0.839784 2.406687 0.666465 -v -0.814743 2.384216 0.636940 -v -0.777747 2.343561 0.593071 -v -0.734984 2.290570 0.542333 -v -0.692237 2.230572 0.491618 -v -0.649216 2.160701 0.440590 -v -0.604631 2.076398 0.387703 -v -0.557419 1.973059 0.331693 -v -0.507032 1.844808 0.271867 -v -0.467523 1.726339 0.225046 -v -0.436479 1.608948 0.188170 -v -0.387184 1.375244 0.129719 -v -0.284766 0.789880 0.000207 -v -0.974781 2.376465 0.585568 -v -0.981270 2.382069 0.590778 -v -0.984861 2.391744 0.594611 -v -0.983985 2.404478 0.593090 -v -0.976006 2.413700 0.585661 -v -0.964406 2.414637 0.574785 -v -0.945523 2.406699 0.560729 -v -0.915848 2.384232 0.535863 -v -0.871963 2.343575 0.498879 -v -0.821233 2.290589 0.456110 -v -0.770533 2.230608 0.413368 -v -0.719502 2.160746 0.370357 -v -0.666611 2.076450 0.325776 -v -0.610606 1.973123 0.278563 -v -0.550840 1.844920 0.228136 -v -0.503972 1.726449 0.188665 -v -0.467148 1.609139 0.157588 -v -0.408649 1.375393 0.108310 -v -0.324017 0.973380 0.030835 -v -1.065445 2.375990 0.457752 -v -1.075366 2.386872 0.463686 -v -1.076877 2.396420 0.465059 -v -1.073489 2.407841 0.462441 -v -1.063691 2.414641 0.455301 -v -1.045264 2.412887 0.445262 -v -1.013127 2.394799 0.427329 -v -0.969266 2.360837 0.401694 -v -0.913430 2.311344 0.369021 -v -0.855942 2.254003 0.335381 -v -0.798750 2.188456 0.301920 -v -0.739987 2.110070 0.267546 -v -0.678081 2.014229 0.231324 -v -0.613103 1.897836 0.193301 -v -0.560194 1.788186 0.162364 -v -0.524512 1.703153 0.141446 -v -0.485323 1.586823 0.118513 -v -0.426070 1.375574 0.083866 -v -1.132848 2.376465 0.315178 -v -1.140301 2.381702 0.317948 -v -1.145444 2.391569 0.320093 -v -1.145044 2.400776 0.319686 -v -1.139470 2.410398 0.317334 -v -1.128033 2.415112 0.310133 -v -1.106357 2.411210 0.305566 -v -1.070583 2.391034 0.292047 -v -1.020482 2.354437 0.273432 -v -0.959069 2.303482 0.250612 -v -0.896731 2.245159 0.227451 -v -0.834475 2.178027 0.204323 -v -0.770310 2.097467 0.180483 -v -0.702510 1.998786 0.155293 -v -0.631457 1.879011 0.128894 -v -0.573474 1.765494 0.107344 -v -0.533508 1.672596 0.092501 -v -0.493715 1.557456 0.077719 -v -0.435554 1.364082 0.056112 -v -0.332060 0.970217 0.013096 -v -1.175385 2.376465 0.160319 -v -1.183508 2.382035 0.161970 -v -1.188701 2.391725 0.162747 -v -1.188180 2.400899 0.162475 -v -1.182018 2.410569 0.161513 -v -1.168918 2.415130 0.159129 -v -1.147470 2.411149 0.155880 -v -1.110900 2.391593 0.148699 -v -1.058666 2.355363 0.139243 -v -0.994280 2.304594 0.127612 -v -0.928822 2.246411 0.115789 -v -0.863502 2.179520 0.103984 -v -0.796198 2.099276 0.091820 -v -0.725103 2.000996 0.078975 -v -0.650590 1.881721 0.065516 -v -0.589451 1.768092 0.054453 -v -0.547900 1.676814 0.046994 -v -0.505959 1.562016 0.039401 -v -0.447063 1.376564 0.028788 -v -1.190408 2.375993 -0.004035 -v -1.201966 2.386877 -0.004127 -v -1.203720 2.400455 -0.004798 -v -1.194266 2.412298 -0.003801 -v -1.179775 2.415118 -0.000952 -v -1.158661 2.409745 -0.004004 -v -1.124603 2.391584 -0.004178 -v -1.071509 2.355346 -0.004197 -v -1.006075 2.304569 -0.004192 -v -0.939557 2.246382 -0.004188 -v -0.873174 2.179481 -0.004191 -v -0.804776 2.099226 -0.004196 -v -0.732527 2.000933 -0.004195 -v -0.656799 1.881632 -0.004190 -v -0.594677 1.768008 -0.004205 -v -0.552446 1.676675 -0.004162 -v -0.493201 1.513851 -0.004146 -v -0.392463 1.179933 -0.004157 -v -1.175673 2.375975 -0.168539 -v -1.187049 2.386899 -0.170753 -v -1.188927 2.396426 -0.171508 -v -1.184868 2.407809 -0.170236 -v -1.173197 2.414637 -0.166982 -v -1.151242 2.412613 -0.163572 -v -1.110884 2.391576 -0.157052 -v -1.058645 2.355344 -0.147631 -v -0.994259 2.304572 -0.135992 -v -0.928790 2.246374 -0.124159 -v -0.863460 2.179467 -0.112358 -v -0.796154 2.099213 -0.100201 -v -0.725060 2.000923 -0.087353 -v -0.650539 1.881621 -0.073884 -v -0.589399 1.767985 -0.062849 -v -0.547822 1.676580 -0.055297 -v -0.505925 1.561879 -0.047741 -v -0.447062 1.376476 -0.037083 -v -0.332773 0.973189 -0.021766 -v -0.284666 0.789909 -0.007962 -v -1.133158 2.375947 -0.323389 -v -1.143999 2.386942 -0.327630 -v -1.145690 2.396424 -0.328708 -v -1.141968 2.407749 -0.326751 -v -1.131029 2.414634 -0.321474 -v -1.115578 2.413834 -0.316485 -v -1.094917 2.405609 -0.309405 -v -1.060582 2.384229 -0.296723 -v -1.006801 2.343587 -0.276751 -v -0.944604 2.290601 -0.253634 -v -0.882431 2.230609 -0.230533 -v -0.819862 2.160740 -0.207291 -v -0.755025 2.076451 -0.183206 -v -0.686369 1.973137 -0.157694 -v -0.613060 1.844898 -0.130418 -v -0.555596 1.726385 -0.109096 -v -0.510428 1.609027 -0.092285 -v -0.438919 1.375920 -0.065720 -v -1.065118 2.376465 -0.466207 -v -1.072489 2.382170 -0.470278 -v -1.076612 2.391835 -0.473391 -v -1.075549 2.404314 -0.472067 -v -1.066402 2.413672 -0.466152 -v -1.053050 2.414637 -0.457351 -v -1.032025 2.406679 -0.446682 -v -0.998553 2.384206 -0.427233 -v -0.949026 2.343556 -0.398266 -v -0.891748 2.290557 -0.364744 -v -0.834491 2.230546 -0.331241 -v -0.776873 2.160656 -0.297532 -v -0.717163 2.076340 -0.262600 -v -0.653936 1.972988 -0.225603 -v -0.586418 1.844673 -0.186055 -v -0.533532 1.726190 -0.155147 -v -0.491913 1.608669 -0.130763 -v -0.425972 1.375121 -0.092189 -v -0.325020 0.978823 -0.040471 -v -0.282303 0.789857 -0.011364 -v -0.974782 2.376465 -0.593985 -v -0.981413 2.382244 -0.599290 -v -0.985255 2.394022 -0.603354 -v -0.983550 2.404490 -0.601306 -v -0.978360 2.412195 -0.596445 -v -0.969087 2.415121 -0.584655 -v -0.951884 2.410216 -0.574414 -v -0.928666 2.394740 -0.555048 -v -0.889807 2.360771 -0.522333 -v -0.840338 2.311262 -0.480629 -v -0.789379 2.253867 -0.437665 -v -0.738687 2.188258 -0.394934 -v -0.686615 2.109813 -0.351047 -v -0.631759 2.013900 -0.304805 -v -0.574169 1.897387 -0.256252 -v -0.527357 1.787847 -0.216831 -v -0.495659 1.702445 -0.190055 -v -0.460937 1.585939 -0.160793 -v -0.408375 1.374157 -0.116475 -v -0.864428 2.376465 -0.704338 -v -0.870128 2.382329 -0.710745 -v -0.872682 2.389606 -0.714102 -v -0.873126 2.396396 -0.715064 -v -0.871002 2.407391 -0.711795 -v -0.863976 2.414624 -0.701615 -v -0.853618 2.414024 -0.690547 -v -0.839743 2.406651 -0.674818 -v -0.814692 2.384162 -0.645291 -v -0.777691 2.343493 -0.601419 -v -0.734909 2.290469 -0.550656 -v -0.692139 2.230419 -0.499911 -v -0.649093 2.160473 -0.448848 -v -0.604489 2.076097 -0.395940 -v -0.557250 1.972648 -0.339899 -v -0.521001 1.881823 -0.296936 -v -0.494456 1.808762 -0.265457 -v -0.467331 1.725687 -0.233229 -v -0.445314 1.644311 -0.207155 -v -0.425738 1.560861 -0.183911 -v -0.400648 1.441781 -0.154142 -v -0.367615 1.271598 -0.114962 -v -0.323436 1.030279 -0.062595 -v -0.736650 2.376465 -0.794675 -v -0.741232 2.382411 -0.802019 -v -0.743233 2.392255 -0.806585 -v -0.743320 2.400452 -0.805937 -v -0.741421 2.407593 -0.802682 -v -0.737904 2.413635 -0.795329 -v -0.731038 2.414721 -0.781939 -v -0.718407 2.407657 -0.763071 -v -0.697678 2.384147 -0.728003 -v -0.668683 2.343477 -0.678476 -v -0.635157 2.290442 -0.621166 -v -0.601645 2.230383 -0.563877 -v -0.567909 2.160414 -0.506219 -v -0.532954 2.076015 -0.446485 -v -0.495934 1.972529 -0.383208 -v -0.467550 1.881783 -0.334752 -v -0.446741 1.808693 -0.299203 -v -0.425473 1.725474 -0.262771 -v -0.408228 1.644164 -0.233373 -v -0.388630 1.536334 -0.199804 -v -0.361170 1.363781 -0.152926 -v -0.331219 1.160495 -0.101773 -v -0.278274 0.790295 -0.014201 -v -0.594038 2.376465 -0.862405 -v -0.597332 2.382470 -0.870473 -v -0.598517 2.389773 -0.874440 -v -0.598677 2.396358 -0.875383 -v -0.598135 2.404229 -0.873220 -v -0.596486 2.410151 -0.868706 -v -0.594553 2.414619 -0.859710 -v -0.588134 2.413824 -0.844633 -v -0.579996 2.405560 -0.824311 -v -0.567173 2.384146 -0.790001 -v -0.547171 2.343477 -0.736211 -v -0.524055 2.290446 -0.673973 -v -0.500946 2.230385 -0.611752 -v -0.477679 2.160412 -0.549129 -v -0.453575 2.076021 -0.484259 -v -0.428047 1.972538 -0.415538 -v -0.408474 1.881857 -0.362942 -v -0.394125 1.808783 -0.324336 -v -0.379468 1.725494 -0.284737 -v -0.367570 1.644280 -0.252835 -v -0.354094 1.536586 -0.216418 -v -0.335162 1.364299 -0.165571 -v -0.314531 1.161331 -0.110087 -v -0.439179 2.376465 -0.904942 -v -0.440800 2.382811 -0.913802 -v -0.441568 2.393469 -0.918530 -v -0.441390 2.403527 -0.916834 -v -0.439758 2.412816 -0.908029 -v -0.436996 2.414978 -0.892895 -v -0.432787 2.407900 -0.869562 -v -0.425618 2.384766 -0.829883 -v -0.415478 2.344466 -0.773766 -v -0.403687 2.291622 -0.708508 -v -0.391882 2.231715 -0.643174 -v -0.380010 2.162004 -0.577472 -v -0.367719 2.077969 -0.509445 -v -0.354703 1.974946 -0.437405 -v -0.344705 1.884370 -0.382071 -v -0.337417 1.811888 -0.341739 -v -0.329900 1.728995 -0.300146 -v -0.323790 1.647648 -0.266324 -v -0.318449 1.565621 -0.236760 -v -0.311624 1.448762 -0.198987 -v -0.302662 1.281987 -0.149389 -v -0.290775 1.047245 -0.083624 -v -0.274652 2.376465 -0.919697 -v -0.274417 2.382412 -0.928351 -v -0.274643 2.389693 -0.932511 -v -0.274831 2.396372 -0.933492 -v -0.274436 2.407213 -0.929779 -v -0.273289 2.414621 -0.917341 -v -0.273860 2.412841 -0.896065 -v -0.274602 2.394711 -0.859295 -v -0.274635 2.360746 -0.808511 -v -0.274632 2.311234 -0.743809 -v -0.274623 2.253805 -0.677123 -v -0.274622 2.188160 -0.610798 -v -0.274626 2.109682 -0.542685 -v -0.274625 2.013714 -0.470914 -v -0.274618 1.897099 -0.395546 -v -0.274654 1.787687 -0.334435 -v -0.274596 1.701885 -0.292779 -v -0.274643 1.606108 -0.255066 -v -0.274634 1.506626 -0.220846 -v -0.274619 1.324438 -0.164229 -v -0.274623 1.033622 -0.081281 -v -0.274652 0.789891 -0.014646 -v -0.110124 2.376465 -0.904942 -v -0.108388 2.382325 -0.913336 -v -0.107834 2.389603 -0.917519 -v -0.107913 2.396396 -0.918573 -v -0.108158 2.407394 -0.914684 -v -0.109231 2.414624 -0.902368 -v -0.113528 2.412848 -0.881570 -v -0.120809 2.394728 -0.845519 -v -0.129868 2.360772 -0.795559 -v -0.141370 2.311265 -0.731890 -v -0.153218 2.253855 -0.666281 -v -0.165008 2.188227 -0.601022 -v -0.177120 2.109775 -0.534006 -v -0.189878 2.013848 -0.463397 -v -0.203268 1.897301 -0.389256 -v -0.214182 1.787805 -0.329067 -v -0.221526 1.702187 -0.288136 -v -0.229603 1.585648 -0.243463 -v -0.238012 1.442206 -0.196945 -v -0.247059 1.272910 -0.146759 -v -0.258773 1.037988 -0.081054 -v -0.270931 0.789902 -0.014290 -v 0.044735 2.376465 -0.862405 -v 0.047887 2.382238 -0.870285 -v 0.049107 2.391910 -0.875245 -v 0.049126 2.404163 -0.873612 -v 0.045769 2.413659 -0.863217 -v 0.040976 2.414636 -0.847909 -v 0.031482 2.406666 -0.826311 -v 0.017891 2.384191 -0.790067 -v -0.002104 2.343537 -0.736285 -v -0.025215 2.290530 -0.674067 -v -0.048318 2.230505 -0.611870 -v -0.071573 2.160594 -0.549282 -v -0.095669 2.076267 -0.484437 -v -0.121186 1.972879 -0.415751 -v -0.148419 1.844475 -0.342353 -v -0.169777 1.726002 -0.284967 -v -0.186556 1.608309 -0.239697 -v -0.213167 1.374610 -0.168062 -v -0.248353 0.981115 -0.061305 -v 0.187347 2.376465 -0.794675 -v 0.191775 2.382164 -0.801831 -v 0.193846 2.391830 -0.806569 -v 0.193566 2.404324 -0.804890 -v 0.188489 2.413673 -0.795255 -v 0.181168 2.414637 -0.781041 -v 0.168068 2.406680 -0.761440 -v 0.148419 2.384209 -0.728087 -v 0.119430 2.343554 -0.678565 -v 0.085920 2.290556 -0.621283 -v 0.052423 2.230548 -0.564026 -v 0.018708 2.160656 -0.506408 -v -0.016229 2.076344 -0.446705 -v -0.053224 1.972985 -0.383471 -v -0.092709 1.844674 -0.315919 -v -0.123670 1.726199 -0.263068 -v -0.147998 1.608685 -0.221419 -v -0.186603 1.375027 -0.155455 -v 0.315496 2.375949 -0.704314 -v 0.322846 2.386940 -0.713337 -v 0.323778 2.396424 -0.715117 -v 0.321513 2.407753 -0.711568 -v 0.314613 2.414634 -0.701571 -v 0.299847 2.412603 -0.684897 -v 0.272882 2.391554 -0.654169 -v 0.238639 2.355304 -0.613582 -v 0.196460 2.304511 -0.563539 -v 0.153579 2.246299 -0.512664 -v 0.110785 2.179374 -0.461902 -v 0.066691 2.099088 -0.409601 -v 0.020116 2.000752 -0.354348 -v -0.028697 1.881402 -0.296436 -v -0.068730 1.767806 -0.248976 -v -0.095943 1.676325 -0.216627 -v -0.123394 1.561571 -0.184086 -v -0.162113 1.375266 -0.138141 -v -0.231337 0.978306 -0.048347 -v -0.265316 0.789885 -0.009549 -v 0.425827 2.375975 -0.593956 -v 0.434589 2.386900 -0.601543 -v 0.435476 2.400424 -0.603200 -v 0.428928 2.412282 -0.596342 -v 0.419768 2.415119 -0.584677 -v 0.401536 2.409737 -0.573517 -v 0.375368 2.391567 -0.551704 -v 0.334751 2.355316 -0.517490 -v 0.284715 2.304526 -0.475305 -v 0.233853 2.246327 -0.432427 -v 0.183092 2.179412 -0.389641 -v 0.130786 2.099135 -0.345555 -v 0.075537 2.000812 -0.298981 -v 0.017634 1.881481 -0.250166 -v -0.029856 1.767878 -0.210153 -v -0.062131 1.676478 -0.182895 -v -0.094718 1.561661 -0.155447 -v -0.140647 1.375420 -0.116732 -v 0.516135 2.375992 -0.466188 -v 0.526065 2.386878 -0.472107 -v 0.527573 2.396427 -0.473476 -v 0.524184 2.407844 -0.470855 -v 0.514386 2.414639 -0.463737 -v 0.500047 2.413837 -0.456228 -v 0.479488 2.404861 -0.444838 -v 0.443932 2.380142 -0.424131 -v 0.392748 2.337426 -0.394189 -v 0.335300 2.283499 -0.360569 -v 0.278147 2.222565 -0.327127 -v 0.220344 2.151124 -0.293313 -v 0.160250 2.064687 -0.258157 -v 0.096648 1.958852 -0.220940 -v 0.026747 1.823175 -0.179982 -v -0.024788 1.703159 -0.149865 -v -0.063977 1.586833 -0.126933 -v -0.123227 1.375600 -0.092287 -v -0.223191 0.960430 -0.028882 -v 0.583838 2.376001 -0.323600 -v 0.594654 2.386870 -0.327641 -v 0.596128 2.400618 -0.328660 -v 0.587174 2.412433 -0.324953 -v 0.574307 2.415095 -0.318133 -v 0.554114 2.409748 -0.312445 -v 0.522125 2.391587 -0.300746 -v 0.472340 2.355342 -0.282270 -v 0.411004 2.304565 -0.259475 -v 0.348657 2.246382 -0.236307 -v 0.286431 2.179485 -0.213193 -v 0.222313 2.099230 -0.189376 -v 0.154583 2.000930 -0.164211 -v 0.083597 1.881629 -0.137834 -v 0.025367 1.768017 -0.116209 -v -0.014188 1.676732 -0.101472 -v -0.054162 1.561879 -0.086642 -v -0.110377 1.375996 -0.065752 -v 0.626146 2.376349 -0.168761 -v 0.635975 2.384098 -0.170524 -v 0.640089 2.398037 -0.170983 -v 0.631500 2.411422 -0.170060 -v 0.617439 2.415187 -0.169208 -v 0.597707 2.410780 -0.163612 -v 0.565983 2.394308 -0.157879 -v 0.515653 2.360002 -0.148784 -v 0.451895 2.310327 -0.137264 -v 0.386359 2.252856 -0.125423 -v 0.321138 2.187109 -0.113639 -v 0.254103 2.108450 -0.101526 -v 0.183458 2.012264 -0.088762 -v 0.109324 1.895469 -0.075367 -v 0.049164 1.785780 -0.064496 -v 0.008259 1.699795 -0.057106 -v -0.052618 1.536631 -0.046104 -v -0.162701 1.165861 -0.025627 -v 0.640837 2.376465 -0.004209 -v 0.649131 2.382043 -0.004393 -v 0.654370 2.391732 -0.003807 -v 0.652702 2.404519 -0.004370 -v 0.641813 2.413705 -0.004902 -v 0.625964 2.414638 -0.005685 -v 0.602481 2.406710 -0.004334 -v 0.563772 2.384252 -0.004230 -v 0.506390 2.343605 -0.004221 -v 0.440051 2.290634 -0.004227 -v 0.373742 2.230664 -0.004228 -v 0.307009 2.160817 -0.004225 -v 0.237848 2.076549 -0.004221 -v 0.164605 1.973249 -0.004224 -v 0.086423 1.845094 -0.004257 -v 0.025117 1.726594 -0.004233 -v -0.023036 1.609408 -0.004268 -v -0.078298 1.443229 -0.004248 -v -0.156849 1.179901 -0.004260 -v -0.264363 0.789912 -0.001014 -v 0.626367 2.375971 0.160097 -v 0.637741 2.386890 0.162333 -v 0.639355 2.400397 0.163305 -v 0.630237 2.412287 0.160662 -v 0.617445 2.415085 0.155189 -v 0.596876 2.410518 0.154735 -v 0.561572 2.391570 0.148632 -v 0.509336 2.355340 0.139213 -v 0.444944 2.304562 0.127572 -v 0.379477 2.246365 0.115740 -v 0.314149 2.179458 0.103939 -v 0.246844 2.099204 0.091783 -v 0.175750 2.000914 0.078936 -v 0.101232 1.881615 0.065467 -v 0.040092 1.767977 0.054431 -v -0.001484 1.676574 0.046881 -v -0.043386 1.561857 0.039323 -v -0.102249 1.376453 0.028665 -v -0.216531 0.973210 0.013372 -v 0.583855 2.375940 0.314942 -v 0.594690 2.386930 0.319209 -v 0.596101 2.400327 0.320448 -v 0.587613 2.412266 0.316257 -v 0.574981 2.415118 0.308415 -v 0.554064 2.409724 0.303991 -v 0.522070 2.391550 0.292302 -v 0.472305 2.355314 0.273834 -v 0.410957 2.304523 0.251036 -v 0.348587 2.246312 0.227859 -v 0.286352 2.179390 0.204740 -v 0.222233 2.099119 0.180923 -v 0.154504 2.000804 0.155758 -v 0.083512 1.881470 0.129377 -v 0.025293 1.767859 0.107759 -v -0.014329 1.676367 0.093000 -v -0.054222 1.561682 0.078193 -v -0.110392 1.375894 0.057301 -v 0.516142 2.375901 0.457562 -v 0.526130 2.386997 0.463691 -v 0.527581 2.396387 0.465042 -v 0.524316 2.407664 0.462506 -v 0.514409 2.414635 0.455356 -v 0.495890 2.412857 0.445166 -v 0.463760 2.394752 0.427281 -v 0.419909 2.360791 0.401659 -v 0.364072 2.311291 0.368984 -v 0.306553 2.253909 0.335324 -v 0.249340 2.188321 0.301850 -v 0.190575 2.109908 0.267473 -v 0.128662 2.014031 0.231248 -v 0.063673 1.897585 0.193219 -v 0.010802 1.787992 0.162311 -v -0.024954 1.702720 0.141348 -v -0.064138 1.586304 0.118420 -v -0.123342 1.375086 0.083768 -v -0.224276 0.978996 0.032127 -v -0.266951 0.789850 0.002916 -v 0.425478 2.376465 0.585568 -v 0.432117 2.382255 0.590878 -v 0.435596 2.391870 0.594588 -v 0.434834 2.404131 0.593177 -v 0.426781 2.413668 0.585725 -v 0.415939 2.414719 0.575418 -v 0.397738 2.407671 0.561907 -v 0.366478 2.384171 0.535797 -v 0.322591 2.343506 0.498818 -v 0.271847 2.290493 0.456033 -v 0.221114 2.230456 0.413262 -v 0.170060 2.160534 0.370230 -v 0.117159 2.076188 0.325642 -v 0.061136 1.972785 0.278413 -v 0.001300 1.844361 0.227927 -v -0.045518 1.725925 0.188508 -v -0.082423 1.608185 0.157364 -v -0.140948 1.374081 0.108046 -v 0.315125 2.376465 0.695921 -v 0.320835 2.382350 0.702344 -v 0.323378 2.389594 0.705679 -v 0.323822 2.396311 0.706656 -v 0.321696 2.407416 0.703361 -v 0.314663 2.414630 0.693164 -v 0.302841 2.413601 0.680198 -v 0.287624 2.404424 0.663183 -v 0.265377 2.384150 0.636858 -v 0.228374 2.343476 0.592983 -v 0.185590 2.290449 0.542220 -v 0.142816 2.230387 0.491467 -v 0.099767 2.160435 0.440404 -v 0.055163 2.076052 0.387497 -v 0.007923 1.972593 0.331453 -v -0.028316 1.881787 0.288502 -v -0.054865 1.808713 0.257019 -v -0.081993 1.725624 0.224791 -v -0.104007 1.644249 0.198721 -v -0.123586 1.560783 0.175475 -v -0.148681 1.441663 0.145698 -v -0.181725 1.271419 0.106507 -v -0.225910 1.030063 0.054134 -v 0.187347 2.376465 0.786257 -v 0.191941 2.382448 0.793631 -v 0.193958 2.392155 0.798123 -v 0.194015 2.400419 0.797535 -v 0.192111 2.407608 0.794255 -v 0.188584 2.413649 0.786878 -v 0.181720 2.414717 0.773502 -v 0.169092 2.407642 0.754621 -v 0.148363 2.384131 0.719565 -v 0.119367 2.343458 0.670036 -v 0.085843 2.290422 0.612728 -v 0.052322 2.230343 0.555423 -v 0.018586 2.160367 0.497765 -v -0.016370 2.075957 0.438028 -v -0.053394 1.972451 0.374745 -v -0.081769 1.881731 0.326308 -v -0.102581 1.808625 0.290754 -v -0.123860 1.725358 0.254309 -v -0.141101 1.644055 0.224921 -v -0.160720 1.536105 0.191324 -v -0.188178 1.363521 0.144444 -v -0.218144 1.160119 0.093267 -v -0.271040 0.790261 0.005796 -v 0.044735 2.376465 0.853988 -v 0.048046 2.382518 0.862095 -v 0.049215 2.392305 0.866823 -v 0.049378 2.400253 0.866288 -v 0.048061 2.407579 0.862709 -v 0.045869 2.413643 0.854822 -v 0.040936 2.414629 0.839451 -v 0.033085 2.408632 0.822373 -v 0.023737 2.394688 0.797310 -v 0.006017 2.360716 0.749716 -v -0.016522 2.311189 0.689051 -v -0.039740 2.253751 0.626534 -v -0.062838 2.188087 0.564352 -v -0.086567 2.109582 0.500492 -v -0.111565 2.013587 0.433207 -v -0.130809 1.929353 0.381492 -v -0.144231 1.865080 0.345367 -v -0.159114 1.787611 0.305331 -v -0.169221 1.729097 0.278191 -v -0.177241 1.676567 0.256544 -v -0.186733 1.606051 0.230942 -v -0.198646 1.506554 0.198860 -v -0.214167 1.364084 0.157094 -v -0.234808 1.161017 0.101588 -v -0.110124 2.376465 0.896525 -v -0.108491 2.382857 0.905422 -v -0.107821 2.390014 0.909229 -v -0.107726 2.396214 0.910085 -v -0.108217 2.406891 0.906681 -v -0.110483 2.414565 0.894472 -v -0.113605 2.413696 0.877283 -v -0.119237 2.400270 0.846096 -v -0.129687 2.361578 0.788250 -v -0.141161 2.312311 0.724751 -v -0.153020 2.255017 0.659116 -v -0.164812 2.189571 0.593854 -v -0.176914 2.111384 0.526873 -v -0.189656 2.015832 0.456350 -v -0.199489 1.931672 0.401933 -v -0.206331 1.867685 0.364064 -v -0.213903 1.790718 0.322163 -v -0.221298 1.705653 0.281242 -v -0.228038 1.610101 0.243932 -v -0.234047 1.512364 0.210673 -v -0.243791 1.336864 0.156744 -v -0.258529 1.047236 0.075204 -vt 0.559615 0.965097 -vt 0.556870 0.965458 -vt 0.556909 0.964261 -vt 0.021736 0.959197 -vt 0.022363 0.959680 -vt 0.020259 0.962597 -vt 0.206674 0.966890 -vt 0.206911 0.964726 -vt 0.212334 0.964716 -vt 0.169062 0.966497 -vt 0.169611 0.967421 -vt 0.168435 0.967737 -vt 0.329172 0.957164 -vt 0.329943 0.956449 -vt 0.330339 0.956961 -vt 0.015734 0.959047 -vt 0.018771 0.960505 -vt 0.016755 0.961591 -vt 0.793632 0.698886 -vt 0.792763 0.698496 -vt 0.794150 0.698339 -vt 0.794613 0.698877 -vt 0.028326 0.956840 -vt 0.027294 0.950027 -vt 0.028156 0.949550 -vt 0.017559 0.967664 -vt 0.018602 0.974463 -vt 0.017744 0.974935 -vt 0.792611 0.701965 -vt 0.793189 0.702386 -vt 0.556868 0.958297 -vt 0.553852 0.964235 -vt 0.553691 0.958257 -vt 0.162385 0.965402 -vt 0.161764 0.966511 -vt 0.323183 0.957176 -vt 0.323281 0.956477 -vt 0.161272 0.967436 -vt 0.162500 0.967670 -vt 0.496975 0.762479 -vt 0.498133 0.762278 -vt 0.498132 0.768438 -vt 0.556880 0.957035 -vt 0.559106 0.957190 -vt 0.162441 0.975106 -vt 0.165450 0.973938 -vt 0.168413 0.975105 -vt 0.030062 0.965340 -vt 0.024243 0.974127 -vt 0.024066 0.965464 -vt 0.030787 0.964848 -vt 0.028716 0.962389 -vt 0.025191 0.962886 -vt 0.027095 0.964014 -vt 0.015911 0.950382 -vt 0.162645 0.962950 -vt 0.167687 0.958230 -vt 0.168230 0.962942 -vt 0.163111 0.958238 -vt 0.207492 0.960131 -vt 0.212068 0.960130 -vt 0.205667 0.965425 -vt 0.205621 0.967213 -vt 0.206591 0.960012 -vt 0.208190 0.953223 -vt 0.162202 0.957997 -vt 0.163804 0.951299 -vt 0.767196 0.946038 -vt 0.767850 0.946499 -vt 0.765732 0.948969 -vt 0.758473 0.946635 -vt 0.758460 0.949043 -vt 0.753005 0.948910 -vt 0.060981 0.951382 -vt 0.061103 0.951888 -vt 0.058661 0.952366 -vt 0.759505 0.936394 -vt 0.759856 0.936893 -vt 0.758564 0.936954 -vt 0.056817 0.951702 -vt 0.055475 0.952096 -vt 0.054831 0.951421 -vt 0.761088 0.945887 -vt 0.764162 0.947391 -vt 0.762111 0.948416 -vt 0.346678 0.575110 -vt 0.345843 0.574929 -vt 0.347350 0.574405 -vt 0.347699 0.574869 -vt 0.059183 0.934079 -vt 0.058112 0.927289 -vt 0.058977 0.926756 -vt 0.762947 0.954483 -vt 0.764020 0.961248 -vt 0.763156 0.961753 -vt 0.756689 0.960687 -vt 0.753686 0.961301 -vt 0.753527 0.960597 -vt 0.346678 0.578200 -vt 0.345676 0.578406 -vt 0.179935 0.951215 -vt 0.182886 0.950595 -vt 0.183100 0.951219 -vt 0.305044 0.964833 -vt 0.304584 0.965723 -vt 0.303746 0.964632 -vt 0.249170 0.767927 -vt 0.248761 0.767059 -vt 0.249734 0.766881 -vt 0.295282 0.956471 -vt 0.295371 0.958378 -vt 0.288577 0.959305 -vt 0.288753 0.961483 -vt 0.303557 0.957202 -vt 0.297047 0.956451 -vt 0.304488 0.956525 -vt 0.752597 0.936838 -vt 0.758117 0.944356 -vt 0.752225 0.944135 -vt 0.766765 0.936595 -vt 0.765290 0.936867 -vt 0.763450 0.936257 -vt 0.760473 0.936909 -vt 0.761299 0.937208 -vt 0.759899 0.937540 -vt 0.060975 0.942579 -vt 0.054861 0.942790 -vt 0.768309 0.937447 -vt 0.767291 0.937270 -vt 0.061702 0.942062 -vt 0.059603 0.939641 -vt 0.055983 0.940223 -vt 0.057963 0.941277 -vt 0.297778 0.964559 -vt 0.296801 0.964993 -vt 0.297679 0.957221 -vt 0.757740 0.953745 -vt 0.753170 0.953627 -vt 0.758764 0.953993 -vt 0.179446 0.958159 -vt 0.178415 0.958018 -vt 0.966664 0.832965 -vt 0.967304 0.833438 -vt 0.965216 0.835902 -vt 0.475073 0.967171 -vt 0.475122 0.964870 -vt 0.480565 0.964862 -vt 0.243593 0.963273 -vt 0.243715 0.963784 -vt 0.241297 0.964240 -vt 0.032478 0.957472 -vt 0.031856 0.958597 -vt 0.031332 0.957677 -vt 0.313086 0.956445 -vt 0.313470 0.956955 -vt 0.312243 0.957096 -vt 0.239466 0.963558 -vt 0.238150 0.963948 -vt 0.237491 0.963260 -vt 0.960606 0.832824 -vt 0.963663 0.834319 -vt 0.961627 0.835358 -vt 0.793621 0.693330 -vt 0.794475 0.693618 -vt 0.792981 0.693951 -vt 0.792613 0.693440 -vt 0.241823 0.945979 -vt 0.240771 0.939169 -vt 0.241635 0.938673 -vt 0.962448 0.841428 -vt 0.963507 0.848196 -vt 0.962646 0.848697 -vt 0.476420 0.953208 -vt 0.479491 0.952561 -vt 0.479588 0.953210 -vt 0.794082 0.689758 -vt 0.794636 0.690121 -vt 0.037014 0.973841 -vt 0.033965 0.974402 -vt 0.033848 0.973724 -vt 0.038675 0.959856 -vt 0.038442 0.962203 -vt 0.032552 0.959643 -vt 0.305599 0.956445 -vt 0.306269 0.957137 -vt 0.305137 0.956942 -vt 0.039662 0.957956 -vt 0.039155 0.958853 -vt 0.038432 0.957634 -vt 0.338483 0.956556 -vt 0.338787 0.957459 -vt 0.330753 0.958184 -vt 0.038602 0.950243 -vt 0.032050 0.949374 -vt 0.039495 0.949635 -vt 0.312143 0.964535 -vt 0.306239 0.964463 -vt 0.965223 0.823274 -vt 0.964775 0.823797 -vt 0.962950 0.823195 -vt 0.960000 0.823855 -vt 0.960815 0.824148 -vt 0.959433 0.824485 -vt 0.243588 0.954479 -vt 0.237525 0.954639 -vt 0.244315 0.953974 -vt 0.242229 0.951534 -vt 0.238671 0.952041 -vt 0.240596 0.953156 -vt 0.032706 0.950094 -vt 0.033334 0.966726 -vt 0.032868 0.962005 -vt 0.037907 0.966886 -vt 0.475677 0.960185 -vt 0.480253 0.960185 -vt 0.474708 0.960013 -vt 0.038878 0.967113 -vt 0.007263 0.965273 -vt 0.006624 0.964801 -vt 0.008712 0.962337 -vt 0.466618 0.967171 -vt 0.466667 0.964870 -vt 0.472110 0.964862 -vt 0.789884 0.966598 -vt 0.790006 0.967110 -vt 0.787588 0.967566 -vt 0.041034 0.957472 -vt 0.040412 0.958597 -vt 0.039888 0.957677 -vt 0.321542 0.956445 -vt 0.321926 0.956955 -vt 0.320698 0.957096 -vt 0.785757 0.966884 -vt 0.784441 0.967273 -vt 0.783782 0.966586 -vt 0.013322 0.965414 -vt 0.010265 0.963920 -vt 0.012300 0.962882 -vt 0.793629 0.694573 -vt 0.792774 0.694286 -vt 0.794268 0.693953 -vt 0.794637 0.694464 -vt 0.788114 0.949304 -vt 0.787062 0.942494 -vt 0.787926 0.941999 -vt 0.011480 0.956811 -vt 0.010421 0.950043 -vt 0.011282 0.949542 -vt 0.467966 0.953208 -vt 0.471036 0.952561 -vt 0.471133 0.953210 -vt 0.793167 0.698146 -vt 0.792614 0.697783 -vt 0.045570 0.973841 -vt 0.042521 0.974402 -vt 0.042404 0.973724 -vt 0.047231 0.959856 -vt 0.046998 0.962203 -vt 0.041108 0.959643 -vt 0.314055 0.956445 -vt 0.314725 0.957137 -vt 0.313592 0.956942 -vt 0.048218 0.957956 -vt 0.047710 0.958853 -vt 0.046988 0.957634 -vt 0.636876 0.956587 -vt 0.637180 0.957490 -vt 0.629145 0.958216 -vt 0.047073 0.950281 -vt 0.040605 0.949374 -vt 0.048051 0.949635 -vt 0.008705 0.974965 -vt 0.009153 0.974442 -vt 0.010978 0.975043 -vt 0.013928 0.974385 -vt 0.013113 0.974090 -vt 0.014495 0.973754 -vt 0.789879 0.957805 -vt 0.783816 0.957964 -vt 0.790606 0.957300 -vt 0.788520 0.954859 -vt 0.784963 0.955366 -vt 0.786887 0.956482 -vt 0.041181 0.950056 -vt 0.041890 0.966726 -vt 0.041423 0.962005 -vt 0.046462 0.966886 -vt 0.467222 0.960185 -vt 0.471798 0.960185 -vt 0.466253 0.960013 -vt 0.047433 0.967113 -vt 0.762705 0.060966 -vt 0.755966 0.063355 -vt 0.754477 0.038452 -vt 0.776855 0.118484 -vt 0.770305 0.113691 -vt 0.770191 0.087784 -vt 0.787384 0.189781 -vt 0.781329 0.178087 -vt 0.782614 0.152626 -vt 0.793634 0.271446 -vt 0.788424 0.253171 -vt 0.791084 0.229538 -vt 0.790983 0.337388 -vt 0.794950 0.315094 -vt 0.794950 0.360046 -vt 0.788353 0.422473 -vt 0.793602 0.404457 -vt 0.790995 0.446526 -vt 0.781126 0.496747 -vt 0.787220 0.485901 -vt 0.782373 0.522209 -vt 0.769980 0.558781 -vt 0.776546 0.555107 -vt 0.769835 0.584236 -vt 0.755611 0.605715 -vt 0.762333 0.609230 -vt 0.754131 0.629738 -vt 0.679701 0.599787 -vt 0.691060 0.595700 -vt 0.687925 0.622314 -vt 0.700728 0.063147 -vt 0.688270 0.031027 -vt 0.697075 0.015366 -vt 0.658496 0.464029 -vt 0.669880 0.480335 -vt 0.663105 0.498985 -vt 0.670049 0.170882 -vt 0.658642 0.183414 -vt 0.663332 0.149300 -vt 0.700547 0.594529 -vt 0.708765 0.601677 -vt 0.707918 0.618728 -vt 0.686260 0.558759 -vt 0.693380 0.576233 -vt 0.659554 0.344364 -vt 0.660553 0.380974 -vt 0.652786 0.386504 -vt 0.659451 0.306686 -vt 0.651631 0.302854 -vt 0.708858 0.058533 -vt 0.708004 0.040245 -vt 0.306703 0.082746 -vt 0.305471 0.069160 -vt 0.312616 0.060722 -vt 0.305071 0.295425 -vt 0.306331 0.280558 -vt 0.312138 0.301521 -vt 0.128766 0.677083 -vt 0.146061 0.675385 -vt 0.150543 0.682594 -vt 0.845778 0.069557 -vt 0.830772 0.075668 -vt 0.848508 0.059845 -vt 0.260305 0.169830 -vt 0.235432 0.167132 -vt 0.260357 0.153419 -vt 0.097896 0.605565 -vt 0.105721 0.630489 -vt 0.089306 0.630503 -vt 0.235518 0.145424 -vt 0.204587 0.147188 -vt 0.235586 0.131989 -vt 0.223648 0.192270 -vt 0.204471 0.173624 -vt 0.204398 0.257607 -vt 0.235346 0.251915 -vt 0.235373 0.264900 -vt 0.113301 0.574538 -vt 0.120361 0.605534 -vt 0.086169 0.574572 -vt 0.075191 0.605581 -vt 0.827404 0.193966 -vt 0.812104 0.166992 -vt 0.825403 0.159836 -vt 0.850289 0.181670 -vt 0.837838 0.153152 -vt 0.849296 0.147011 -vt 0.204693 0.130173 -vt 0.167046 0.136357 -vt 0.204773 0.119995 -vt 0.126232 0.536779 -vt 0.132427 0.574497 -vt 0.102875 0.536824 -vt 0.076741 0.536846 -vt 0.064117 0.574579 -vt 0.150030 0.177968 -vt 0.121853 0.167646 -vt 0.166904 0.156972 -vt 0.121761 0.277710 -vt 0.148481 0.270844 -vt 0.166809 0.279991 -vt 0.142090 0.536730 -vt 0.146166 0.491597 -vt 0.151291 0.536692 -vt 0.885940 0.256615 -vt 0.871959 0.212891 -vt 0.879543 0.208839 -vt 0.150256 0.189939 -vt 0.069210 0.197819 -vt 0.150626 0.215259 -vt 0.069207 0.218492 -vt 0.150457 0.202412 -vt 0.150838 0.239974 -vt 0.069201 0.239327 -vt 0.150755 0.227953 -vt 0.150421 0.261556 -vt 0.069195 0.257916 -vt 0.148686 0.251248 -vt 0.069187 0.273061 -vt 0.927016 0.248499 -vt 0.924820 0.250996 -vt 0.924497 0.232670 -vt 0.069208 0.153061 -vt 0.052202 0.156286 -vt 0.069206 0.147690 -vt 0.914508 0.464150 -vt 0.916318 0.460186 -vt 0.917909 0.477287 -vt 0.165134 0.420540 -vt 0.167939 0.424673 -vt 0.164772 0.438261 -vt 0.162503 0.405322 -vt 0.161137 0.420258 -vt 0.156078 0.419987 -vt 0.911060 0.343523 -vt 0.909428 0.326401 -vt 0.913379 0.324043 -vt 0.033255 0.163773 -vt 0.026142 0.167595 -vt 0.037350 0.156593 -vt 0.040545 0.288689 -vt 0.031518 0.289999 -vt 0.040987 0.284210 -vt 0.178447 0.376774 -vt 0.174210 0.384600 -vt 0.165259 0.382151 -vt 0.169140 0.374189 -vt 0.153181 0.380084 -vt 0.927183 0.750562 -vt 0.922514 0.741694 -vt 0.931256 0.730092 -vt 0.936283 0.739584 -vt 0.941751 0.717531 -vt 0.946828 0.727766 -vt 0.950345 0.707998 -vt 0.850150 0.753012 -vt 0.844104 0.742288 -vt 0.853420 0.729657 -vt 0.858672 0.741716 -vt 0.862102 0.719128 -vt 0.866367 0.732399 -vt 0.869712 0.711202 -vt 0.872820 0.725568 -vt 0.874004 0.707535 -vt 0.014974 0.197963 -vt 0.009555 0.209079 -vt 0.012951 0.194626 -vt 0.017663 0.205625 -vt 0.012354 0.213758 -vt 0.019957 0.216375 -vt 0.014971 0.221846 -vt 0.058079 0.837005 -vt 0.060893 0.831116 -vt 0.065890 0.847643 -vt 0.048674 0.825193 -vt 0.052746 0.820930 -vt 0.038354 0.813096 -vt 0.043293 0.809693 -vt 0.028321 0.802118 -vt 0.033628 0.798713 -vt 0.019090 0.792771 -vt 0.024504 0.788821 -vt 0.011117 0.785501 -vt 0.016355 0.780462 -vt 0.006612 0.781913 -vt 0.009622 0.774094 -vt 0.877549 0.756033 -vt 0.876201 0.739971 -vt 0.877971 0.738411 -vt 0.001529 0.237145 -vt 0.001541 0.223969 -vt 0.003075 0.222481 -vt 0.006506 0.759524 -vt 0.009582 0.749641 -vt 0.008298 0.761112 -vt 0.006129 0.754928 -vt 0.206573 0.370878 -vt 0.203951 0.372888 -vt 0.198795 0.372021 -vt 0.007286 0.227152 -vt 0.004727 0.250142 -vt 0.005942 0.224675 -vt 0.921125 0.817936 -vt 0.918677 0.807066 -vt 0.920234 0.805187 -vt 0.923753 0.815315 -vt 0.924394 0.800456 -vt 0.928770 0.810166 -vt 0.930352 0.793894 -vt 0.935064 0.803627 -vt 0.937830 0.785808 -vt 0.942608 0.795728 -vt 0.946506 0.776554 -vt 0.854195 0.823731 -vt 0.850188 0.811298 -vt 0.855639 0.803469 -vt 0.860854 0.814098 -vt 0.862592 0.793583 -vt 0.866971 0.805193 -vt 0.868811 0.784863 -vt 0.872271 0.797424 -vt 0.873968 0.777773 -vt 0.878297 0.788437 -vt 0.876692 0.775640 -vt 0.878145 0.772251 -vt 0.066673 0.802128 -vt 0.067981 0.788465 -vt 0.068187 0.803738 -vt 0.062963 0.798052 -vt 0.065244 0.786344 -vt 0.057646 0.792112 -vt 0.060753 0.782097 -vt 0.050987 0.784600 -vt 0.054810 0.775986 -vt 0.043280 0.775845 -vt 0.047672 0.768258 -vt 0.035413 0.766855 -vt 0.039855 0.759456 -vt 0.027998 0.758332 -vt 0.032283 0.750625 -vt 0.021386 0.750684 -vt 0.025353 0.742250 -vt 0.015934 0.744320 -vt 0.019408 0.734754 -vt 0.012022 0.739682 -vt 0.014802 0.728576 -vt 0.010425 0.737734 -vt 0.011933 0.724228 -vt 0.878632 0.808586 -vt 0.875251 0.793015 -vt 0.021093 0.286911 -vt 0.015717 0.283886 -vt 0.014158 0.280563 -vt 0.065970 0.766012 -vt 0.068027 0.758904 -vt 0.068050 0.766687 -vt 0.061533 0.763211 -vt 0.064447 0.757630 -vt 0.055703 0.758294 -vt 0.059321 0.754115 -vt 0.730095 0.883018 -vt 0.729588 0.877645 -vt 0.737304 0.882627 -vt 0.718942 0.884096 -vt 0.719274 0.878480 -vt 0.707624 0.885696 -vt 0.708122 0.879803 -vt 0.696770 0.887693 -vt 0.697054 0.881483 -vt 0.686934 0.889973 -vt 0.686629 0.883419 -vt 0.678674 0.892413 -vt 0.677402 0.885502 -vt 0.672618 0.894872 -vt 0.669947 0.887626 -vt 0.667241 0.893370 -vt 0.666133 0.889030 -vt 0.929855 0.863390 -vt 0.926542 0.856261 -vt 0.931080 0.853648 -vt 0.935110 0.860086 -vt 0.937090 0.848973 -vt 0.820920 0.685550 -vt 0.823565 0.677611 -vt 0.833228 0.677800 -vt 0.831251 0.686141 -vt 0.841704 0.678356 -vt 0.015177 0.332868 -vt 0.006444 0.331692 -vt 0.006745 0.320259 -vt 0.015594 0.321675 -vt 0.007502 0.309242 -vt 0.016341 0.311084 -vt 0.008660 0.299200 -vt 0.017379 0.301669 -vt 0.010168 0.290690 -vt 0.018672 0.293964 -vt 0.012000 0.284243 -vt 0.037388 0.294952 -vt 0.031513 0.294100 -vt 0.029007 0.291641 -vt 0.660718 0.877860 -vt 0.663113 0.872550 -vt 0.665900 0.880417 -vt 0.049011 0.298798 -vt 0.040244 0.299173 -vt 0.734806 0.863540 -vt 0.733715 0.855962 -vt 0.737085 0.863688 -vt 0.729289 0.863647 -vt 0.729604 0.856042 -vt 0.721438 0.864178 -vt 0.723045 0.856498 -vt 0.711558 0.865128 -vt 0.714311 0.857315 -vt 0.700780 0.866390 -vt 0.703992 0.858447 -vt 0.689999 0.867847 -vt 0.690392 0.860145 -vt 0.795685 0.712379 -vt 0.796861 0.703632 -vt 0.799110 0.703414 -vt 0.799677 0.712329 -vt 0.804619 0.703374 -vt 0.806129 0.712601 -vt 0.812476 0.703710 -vt 0.814778 0.713193 -vt 0.822373 0.704425 -vt 0.043531 0.344973 -vt 0.034132 0.348434 -vt 0.034177 0.340416 -vt 0.886910 0.720715 -vt 0.886334 0.717000 -vt 0.888181 0.713173 -vt 0.730181 0.848809 -vt 0.724928 0.849228 -vt 0.717241 0.849979 -vt 0.707445 0.851043 -vt 0.697036 0.852252 -vt 0.653923 0.857733 -vt 0.655369 0.861753 -vt 0.654388 0.866238 -vt 0.797065 0.720679 -vt 0.802315 0.720956 -vt 0.809848 0.721504 -vt 0.823048 0.713884 -vt 0.819114 0.722283 -vt 0.061010 0.299526 -vt 0.053320 0.301381 -vt 0.720643 0.843923 -vt 0.712308 0.844908 -vt 0.702364 0.846060 -vt 0.692103 0.847097 -vt 0.686805 0.853508 -vt 0.682021 0.848256 -vt 0.677142 0.854754 -vt 0.672685 0.849305 -vt 0.668250 0.855969 -vt 0.664826 0.850104 -vt 0.660930 0.857048 -vt 0.799782 0.727812 -vt 0.805886 0.728374 -vt 0.814603 0.729295 -vt 0.052243 0.342618 -vt 0.059620 0.346732 -vt 0.052227 0.349892 -vt 0.052327 0.332101 -vt 0.059642 0.336374 -vt 0.052478 0.322058 -vt 0.059441 0.326447 -vt 0.053729 0.312710 -vt 0.059534 0.317023 -vt 0.086481 0.299414 -vt 0.657275 0.823580 -vt 0.657503 0.850758 -vt 0.655553 0.851231 -vt 0.796911 0.727673 -vt 0.797562 0.751158 -vt 0.795021 0.721515 -vt 0.080522 0.333686 -vt 0.062034 0.298209 -vt 0.109373 0.295627 -vt 0.660967 0.784147 -vt 0.660084 0.850443 -vt 0.805147 0.759093 -vt 0.809836 0.749997 -vt 0.053707 0.305528 -vt 0.091490 0.305777 -vt 0.082753 0.302255 -vt 0.695394 0.757935 -vt 0.688522 0.759044 -vt 0.679747 0.760883 -vt 0.664437 0.758643 -vt 0.668125 0.778241 -vt 0.884394 0.797667 -vt 0.883025 0.808725 -vt 0.885240 0.729089 -vt 0.725463 0.752482 -vt 0.727637 0.768752 -vt 0.727937 0.843023 -vt 0.163887 0.293997 -vt 0.660650 0.752831 -vt 0.818713 0.839567 -vt 0.815184 0.842113 -vt 0.823274 0.751017 -vt 0.825034 0.842729 -vt 0.816109 0.749531 -vt 0.171631 0.326952 -vt 0.080467 0.343752 -vt 0.089934 0.322771 -vt 0.171609 0.317858 -vt 0.090480 0.313704 -vt 0.172156 0.309170 -vt 0.172760 0.301301 -vt 0.210759 0.287744 -vt 0.620173 0.733268 -vt 0.616897 0.820605 -vt 0.615532 0.826433 -vt 0.805188 0.841858 -vt 0.804295 0.846652 -vt 0.669921 0.727013 -vt 0.673856 0.762619 -vt 0.662377 0.698524 -vt 0.656229 0.820939 -vt 0.211115 0.290942 -vt 0.882935 0.851183 -vt 0.881673 0.881369 -vt 0.723927 0.691973 -vt 0.726681 0.720373 -vt 0.723131 0.752341 -vt 0.718623 0.692616 -vt 0.720303 0.752370 -vt 0.716998 0.752538 -vt 0.694410 0.695455 -vt 0.685878 0.696422 -vt 0.677967 0.697328 -vt 0.666104 0.694500 -vt 0.727192 0.691085 -vt 0.727904 0.720142 -vt 0.670980 0.694326 -vt 0.663708 0.697272 -vt 0.622190 0.698347 -vt 0.623125 0.698640 -vt 0.618216 0.732169 -vt 0.620921 0.698166 -vt 0.619218 0.732340 -vt 0.806932 0.841721 -vt 0.808645 0.881073 -vt 0.811953 0.841883 -vt 0.813014 0.881530 -vt 0.809194 0.841745 -vt 0.819279 0.882145 -vt 0.211326 0.297000 -vt 0.211166 0.286049 -vt 0.216615 0.285716 -vt 0.213734 0.284670 -vt 0.884608 0.882315 -vt 0.883675 0.882276 -vt 0.884295 0.878605 -vt 0.725790 0.687746 -vt 0.721463 0.688534 -vt 0.715296 0.689449 -vt 0.711530 0.693467 -vt 0.707530 0.690445 -vt 0.703124 0.694454 -vt 0.699054 0.691411 -vt 0.690569 0.692270 -vt 0.682509 0.692986 -vt 0.675301 0.693521 -vt 0.663719 0.693787 -vt 0.623267 0.693846 -vt 0.885135 0.886364 -vt 0.671499 0.689718 -vt 0.668298 0.694206 -vt 0.668187 0.687880 -vt 0.222404 0.283721 -vt 0.225213 0.284949 -vt 0.220649 0.288003 -vt 0.220757 0.282033 -vt 0.226105 0.279223 -vt 0.627023 0.684515 -vt 0.626584 0.687264 -vt 0.624593 0.689070 -vt 0.228870 0.279094 -vt 0.886003 0.891010 -vt 0.888363 0.895349 -vt 0.883793 0.888550 -vt 0.676498 0.678662 -vt 0.673444 0.683297 -vt 0.626266 0.683968 -vt 0.629942 0.680045 -vt 0.122569 0.948994 -vt 0.123638 0.943829 -vt 0.125359 0.950160 -vt 0.118735 0.946203 -vt 0.120351 0.941656 -vt 0.115177 0.943022 -vt 0.116162 0.938011 -vt 0.834927 0.899835 -vt 0.835747 0.904591 -vt 0.827357 0.898895 -vt 0.227578 0.312737 -vt 0.232398 0.311768 -vt 0.227631 0.318802 -vt 0.227300 0.304631 -vt 0.232211 0.303758 -vt 0.226796 0.297032 -vt 0.231733 0.296106 -vt 0.226092 0.290342 -vt 0.231001 0.289193 -vt 0.230044 0.283400 -vt 0.892271 0.898575 -vt 0.889408 0.894482 -vt 0.844165 0.900367 -vt 0.842918 0.895920 -vt 0.845731 0.894682 -vt 0.847515 0.898176 -vt 0.849653 0.891960 -vt 0.851823 0.894652 -vt 0.854456 0.887884 -vt 0.856893 0.889930 -vt 0.859920 0.882602 -vt 0.712561 0.677270 -vt 0.713121 0.680214 -vt 0.705146 0.681100 -vt 0.704792 0.677946 -vt 0.697291 0.681661 -vt 0.697390 0.678247 -vt 0.689951 0.681886 -vt 0.690732 0.678169 -vt 0.683520 0.681764 -vt 0.685207 0.677709 -vt 0.678418 0.681290 -vt 0.681287 0.676892 -vt 0.233596 0.270449 -vt 0.235897 0.269579 -vt 0.232645 0.274625 -vt 0.895869 0.902178 -vt 0.893677 0.901433 -vt 0.238546 0.259671 -vt 0.240468 0.257598 -vt 0.238487 0.264078 -vt 0.904716 0.906944 -vt 0.902583 0.906941 -vt 0.900128 0.904928 -vt 0.864138 0.898657 -vt 0.861815 0.895345 -vt 0.866805 0.889893 -vt 0.972627 0.673611 -vt 0.974943 0.667003 -vt 0.977619 0.679088 -vt 0.909519 0.908595 -vt 0.843603 0.926423 -vt 0.842426 0.920184 -vt 0.844505 0.918480 -vt 0.846216 0.923764 -vt 0.847683 0.915547 -vt 0.849784 0.920072 -vt 0.851747 0.911572 -vt 0.854133 0.915525 -vt 0.856519 0.906721 -vt 0.859063 0.910329 -vt 0.866435 0.902488 -vt 0.870922 0.891192 -vt 0.872536 0.895934 -vt 0.874599 0.886941 -vt 0.875700 0.892483 -vt 0.877341 0.883580 -vt 0.877828 0.890112 -vt 0.878605 0.881881 -vt 0.122749 0.911805 -vt 0.123944 0.903898 -vt 0.124769 0.913645 -vt 0.119741 0.908705 -vt 0.121453 0.901064 -vt 0.115906 0.904518 -vt 0.118095 0.897182 -vt 0.111409 0.899414 -vt 0.114009 0.892410 -vt 0.107748 0.895148 -vt 0.109376 0.886957 -vt 0.968169 0.659517 -vt 0.972519 0.654911 -vt 0.964478 0.655238 -vt 0.966418 0.648333 -vt 0.961708 0.651832 -vt 0.963228 0.644844 -vt 0.960477 0.650173 -vt 0.961021 0.642377 -vt 0.240019 0.251732 -vt 0.240102 0.238702 -vt 0.861579 0.920060 -vt 0.976801 0.644282 -vt 0.978613 0.661425 -vt 0.237603 0.232985 -vt 0.238707 0.241752 -vt 0.985872 0.868345 -vt 0.980992 0.872033 -vt 0.985358 0.867733 -vt 0.232584 0.220381 -vt 0.235094 0.222052 -vt 0.235524 0.225905 -vt 0.856101 0.950996 -vt 0.854621 0.946202 -vt 0.861954 0.938193 -vt 0.864681 0.941751 -vt 0.868753 0.931300 -vt 0.871015 0.935744 -vt 0.872653 0.927704 -vt 0.874539 0.932923 -vt 0.875765 0.925153 -vt 0.877163 0.931326 -vt 0.877836 0.923863 -vt 0.195841 0.914720 -vt 0.200157 0.917755 -vt 0.199636 0.918371 -vt 0.736199 0.667323 -vt 0.738188 0.668863 -vt 0.732091 0.666531 -vt 0.111857 0.847727 -vt 0.116108 0.842898 -vt 0.116378 0.853347 -vt 0.970998 0.613905 -vt 0.975726 0.609161 -vt 0.977206 0.620184 -vt 0.967092 0.610291 -vt 0.969389 0.603128 -vt 0.963963 0.607715 -vt 0.965857 0.600288 -vt 0.961851 0.606398 -vt 0.963211 0.598669 -vt 0.229338 0.215355 -vt 0.228397 0.217736 -vt 0.231387 0.223453 -vt 0.227444 0.221867 -vt 0.230294 0.228061 -vt 0.226632 0.227268 -vt 0.229406 0.233837 -vt 0.225822 0.237046 -vt 0.228893 0.238808 -vt 0.739376 0.667273 -vt 0.743375 0.671472 -vt 0.742460 0.665618 -vt 0.745981 0.670000 -vt 0.746936 0.664005 -vt 0.750015 0.668575 -vt 0.752581 0.662469 -vt 0.755297 0.667161 -vt 0.759131 0.661088 -vt 0.764919 0.665251 -vt 0.219394 0.213519 -vt 0.222008 0.212135 -vt 0.224174 0.215053 -vt 0.189413 0.903769 -vt 0.190173 0.901282 -vt 0.191092 0.906242 -vt 0.213483 0.246449 -vt 0.218030 0.244019 -vt 0.218111 0.250363 -vt 0.472789 0.885249 -vt 0.471820 0.878669 -vt 0.478646 0.882787 -vt 0.975845 0.895670 -vt 0.975099 0.899118 -vt 0.974253 0.893143 -vt 0.754486 0.691173 -vt 0.752147 0.687883 -vt 0.754191 0.685144 -vt 0.211708 0.211309 -vt 0.196092 0.212694 -vt 0.211851 0.210155 -vt 0.208230 0.249978 -vt 0.198265 0.247985 -vt 0.206630 0.243366 -vt 0.208262 0.255182 -vt 0.198247 0.253604 -vt 0.208459 0.259302 -vt 0.198201 0.258090 -vt 0.190572 0.895228 -vt 0.195227 0.870721 -vt 0.191967 0.890474 -vt 0.191856 0.877540 -vt 0.189832 0.894958 -vt 0.189103 0.894455 -vt 0.761641 0.700293 -vt 0.758762 0.690619 -vt 0.764205 0.689941 -vt 0.767428 0.699636 -vt 0.773902 0.688774 -vt 0.213065 0.217283 -vt 0.164617 0.222490 -vt 0.213321 0.212984 -vt 0.212886 0.222734 -vt 0.180241 0.224234 -vt 0.206844 0.229507 -vt 0.188428 0.262918 -vt 0.176202 0.262923 -vt 0.208460 0.261276 -vt 0.978387 0.914367 -vt 0.980516 0.930654 -vt 0.976533 0.908386 -vt 0.177233 0.261720 -vt 0.163340 0.260472 -vt 0.765498 0.723151 -vt 0.771006 0.723715 -vt 0.775653 0.698696 -vt 0.131948 0.221888 -vt 0.143228 0.222405 -vt 0.163546 0.232096 -vt 0.145098 0.229579 -vt 0.200689 0.825620 -vt 0.197460 0.851567 -vt 0.757730 0.769164 -vt 0.752039 0.704383 -vt 0.752991 0.703555 -vt 0.131765 0.222795 -vt 0.203706 0.814786 -vt 0.200492 0.822460 -vt 0.760174 0.769455 -vt 0.755128 0.706236 -vt 0.758701 0.706750 -vt 0.764506 0.769409 -vt 0.989458 0.975142 -vt 0.989109 0.977179 -vt 0.132009 0.265976 -vt 0.129816 0.265391 -vt 0.769482 0.768970 -vt 0.772865 0.774243 -vt 0.777464 0.723112 -vt 0.781183 0.772849 -vt 0.782363 0.722585 -vt 0.446082 0.789873 -vt 0.463533 0.837563 -vt 0.458196 0.839948 -vt 0.441574 0.791478 -vt 0.453792 0.842046 -vt 0.439572 0.799255 -vt 0.450579 0.843829 -vt 0.437102 0.800428 -vt 0.449090 0.844989 -vt 0.131790 0.226806 -vt 0.164230 0.238186 -vt 0.121189 0.240681 -vt 0.164504 0.244699 -vt 0.122141 0.245369 -vt 0.164469 0.250849 -vt 0.122246 0.251193 -vt 0.164128 0.256179 -vt 0.122349 0.256380 -vt 0.122442 0.260696 -vt 0.122477 0.226081 -vt 0.203068 0.809930 -vt 0.203422 0.812103 -vt 0.122310 0.223828 -vt 0.122787 0.265851 -vt 0.204085 0.805494 -vt 0.203153 0.805285 -vt 0.759260 0.776952 -vt 0.762738 0.776569 -vt 0.766552 0.777439 -vt 0.437865 0.792240 -vt 0.989525 0.985013 -vt 0.987840 0.977718 -vt 0.988357 0.976821 -vt 0.991045 0.984755 -vt 0.991481 0.988741 -vt 0.990395 0.984848 -vt 0.118583 0.227491 -vt 0.122495 0.229997 -vt 0.118753 0.230334 -vt 0.120166 0.255572 -vt 0.755794 0.779500 -vt 0.756640 0.777342 -vt 0.757483 0.777246 -vt 0.446639 0.784707 -vt 0.450144 0.789058 -vt 0.440506 0.786999 -vt 0.437530 0.788124 -vt 0.435428 0.793234 -vt 0.435728 0.788818 -vt 0.434494 0.793606 -vt 0.970135 0.217864 -vt 0.969443 0.224440 -vt 0.969217 0.217607 -vt 0.971096 0.218937 -vt 0.970652 0.225078 -vt 0.115493 0.236261 -vt 0.119618 0.235425 -vt 0.120678 0.265202 -vt 0.119001 0.267110 -vt 0.120513 0.262017 -vt 0.755737 0.781663 -vt 0.757997 0.779398 -vt 0.761405 0.779222 -vt 0.775295 0.781824 -vt 0.774500 0.779260 -vt 0.780657 0.778128 -vt 0.443434 0.781742 -vt 0.969950 0.234815 -vt 0.971526 0.226446 -vt 0.971180 0.235933 -vt 0.115562 0.244151 -vt 0.112573 0.246675 -vt 0.119052 0.248467 -vt 0.114967 0.253588 -vt 0.118231 0.259314 -vt 0.115063 0.262523 -vt 0.116644 0.269363 -vt 0.608670 0.971973 -vt 0.609299 0.967736 -vt 0.609944 0.973818 -vt 0.607430 0.971391 -vt 0.608012 0.966487 -vt 0.762683 0.786177 -vt 0.760363 0.784933 -vt 0.455036 0.773439 -vt 0.451009 0.779287 -vt 0.967471 0.236390 -vt 0.966916 0.226900 -vt 0.968179 0.225046 -vt 0.968759 0.235044 -vt 0.106794 0.279231 -vt 0.609917 0.960839 -vt 0.610573 0.968951 -vt 0.608654 0.958645 -vt 0.729504 0.795789 -vt 0.759213 0.782829 -vt 0.969026 0.258240 -vt 0.970629 0.258413 -vt 0.106423 0.267612 -vt 0.112033 0.244350 -vt 0.106056 0.276373 -vt 0.107048 0.269919 -vt 0.105343 0.292055 -vt 0.116947 0.272958 -vt 0.110654 0.290589 -vt 0.605594 0.943627 -vt 0.606191 0.965611 -vt 0.605307 0.966822 -vt 0.727666 0.792823 -vt 0.748329 0.784492 -vt 0.750974 0.784760 -vt 0.731838 0.793132 -vt 0.115880 0.266169 -vt 0.108257 0.294626 -vt 0.610338 0.945126 -vt 0.607161 0.942052 -vt 0.608665 0.942356 -vt 0.723441 0.800066 -vt 0.970075 0.264123 -vt 0.971496 0.259360 -vt 0.971194 0.265193 -vt 0.104717 0.275290 -vt 0.111133 0.296155 -vt 0.108328 0.303682 -vt 0.609330 0.934271 -vt 0.608044 0.932919 -vt 0.720020 0.796801 -vt 0.476853 0.755413 -vt 0.481294 0.754872 -vt 0.473952 0.758914 -vt 0.471092 0.759631 -vt 0.103203 0.287386 -vt 0.104724 0.303673 -vt 0.107413 0.291224 -vt 0.108622 0.307121 -vt 0.105229 0.313612 -vt 0.608293 0.925989 -vt 0.488483 0.748397 -vt 0.484919 0.753714 -vt 0.481108 0.752057 -vt 0.476366 0.753874 -vt 0.473713 0.756485 -vt 0.968678 0.267302 -vt 0.967534 0.265568 -vt 0.968825 0.264224 -vt 0.102383 0.283455 -vt 0.100825 0.280439 -vt 0.103039 0.278901 -vt 0.708543 0.804235 -vt 0.709866 0.800530 -vt 0.714092 0.800435 -vt 0.717687 0.803373 -vt 0.727502 0.803915 -vt 0.730193 0.798965 -vt 0.481598 0.749855 -vt 0.094535 0.278854 -vt 0.100885 0.277489 -vt 0.100946 0.295846 -vt 0.089082 0.292839 -vt 0.101229 0.308961 -vt 0.094244 0.304238 -vt 0.101405 0.303651 -vt 0.712855 0.814601 -vt 0.712163 0.803868 -vt 0.717049 0.811845 -vt 0.721828 0.808572 -vt 0.732104 0.809757 -vt 0.732706 0.800522 -vt 0.474124 0.749722 -vt 0.476733 0.751637 -vt 0.474861 0.752729 -vt 0.095453 0.313758 -vt 0.090097 0.309392 -vt 0.711301 0.826925 -vt 0.706882 0.804509 -vt 0.728231 0.816040 -vt 0.473745 0.742046 -vt 0.478699 0.746338 -vt 0.074247 0.288307 -vt 0.710076 0.831863 -vt 0.475206 0.736084 -vt 0.481707 0.741810 -vt 0.068508 0.317847 -vt 0.095708 0.317038 -vt 0.096390 0.319139 -vt 0.710248 0.839773 -vt 0.706365 0.808784 -vt 0.724241 0.825550 -vt 0.056828 0.315527 -vt 0.715829 0.841861 -vt 0.719937 0.841513 -vt 0.464659 0.713062 -vt 0.462484 0.714658 -vt 0.471953 0.743077 -vt 0.039459 0.308515 -vt 0.713590 0.848930 -vt 0.735414 0.863829 -vt 0.466356 0.703933 -vt 0.981810 0.045396 -vt 0.980200 0.049709 -vt 0.983729 0.034277 -vt 0.038747 0.288814 -vt 0.068688 0.284232 -vt 0.039326 0.303650 -vt 0.039029 0.298257 -vt 0.092414 0.299261 -vt 0.787906 0.825410 -vt 0.786502 0.811952 -vt 0.789134 0.829445 -vt 0.712361 0.853849 -vt 0.731519 0.864548 -vt 0.979062 0.051290 -vt 0.981089 0.040396 -vt 0.982925 0.035008 -vt 0.038786 0.285163 -vt 0.058794 0.281652 -vt 0.714969 0.866449 -vt 0.726344 0.865274 -vt 0.459521 0.692332 -vt 0.977455 0.062688 -vt 0.038948 0.282636 -vt 0.056880 0.280478 -vt 0.039010 0.313893 -vt 0.057847 0.312302 -vt 0.051585 0.318214 -vt 0.038962 0.317793 -vt 0.051583 0.319209 -vt 0.038947 0.319682 -vt 0.784092 0.799889 -vt 0.784617 0.799425 -vt 0.713247 0.871395 -vt 0.712341 0.865400 -vt 0.463093 0.690119 -vt 0.467569 0.688648 -vt 0.454930 0.693737 -vt 0.456759 0.693526 -vt 0.457346 0.702571 -vt 0.976475 0.067744 -vt 0.976687 0.062555 -vt 0.034078 0.283104 -vt 0.038937 0.281578 -vt 0.034304 0.284925 -vt 0.034597 0.289580 -vt 0.038943 0.293350 -vt 0.034897 0.299103 -vt 0.034955 0.309623 -vt 0.783026 0.794724 -vt 0.783507 0.799990 -vt 0.783071 0.800964 -vt 0.718514 0.871524 -vt 0.720273 0.866010 -vt 0.728012 0.870752 -vt 0.464154 0.683474 -vt 0.977338 0.067740 -vt 0.977620 0.072373 -vt 0.030194 0.286204 -vt 0.030681 0.288483 -vt 0.031283 0.293672 -vt 0.034698 0.318861 -vt 0.031427 0.319663 -vt 0.034845 0.315783 -vt 0.783838 0.794800 -vt 0.784988 0.790902 -vt 0.784608 0.794907 -vt 0.713155 0.876484 -vt 0.718389 0.876506 -vt 0.457461 0.681253 -vt 0.458541 0.686041 -vt 0.455775 0.687414 -vt 0.976579 0.072680 -vt 0.975579 0.067788 -vt 0.978910 0.076745 -vt 0.027790 0.292701 -vt 0.031794 0.302240 -vt 0.028897 0.300962 -vt 0.031869 0.311382 -vt 0.029300 0.311349 -vt 0.028946 0.319928 -vt 0.942792 0.274925 -vt 0.943922 0.271931 -vt 0.946177 0.276425 -vt 0.711144 0.880729 -vt 0.719516 0.880907 -vt 0.726960 0.876049 -vt 0.727721 0.880387 -vt 0.732210 0.875565 -vt 0.462865 0.673986 -vt 0.464874 0.677539 -vt 0.796500 0.925321 -vt 0.796317 0.919737 -vt 0.801409 0.914592 -vt 0.802667 0.919122 -vt 0.805401 0.911210 -vt 0.806285 0.916095 -vt 0.807503 0.909953 -vt 0.808049 0.915084 -vt 0.808502 0.909653 -vt 0.940941 0.264182 -vt 0.941546 0.261453 -vt 0.941844 0.266702 -vt 0.698529 0.886861 -vt 0.701697 0.883954 -vt 0.703868 0.883383 -vt 0.022863 0.299518 -vt 0.020611 0.304501 -vt 0.021248 0.299148 -vt 0.960812 0.279717 -vt 0.963283 0.277575 -vt 0.964487 0.284183 -vt 0.953919 0.272266 -vt 0.957299 0.270882 -vt 0.947074 0.265718 -vt 0.950140 0.263585 -vt 0.943329 0.262603 -vt 0.944015 0.257935 -vt 0.694756 0.889452 -vt 0.701416 0.886987 -vt 0.239771 0.878227 -vt 0.239086 0.872836 -vt 0.242676 0.868315 -vt 0.245643 0.871427 -vt 0.247669 0.862619 -vt 0.799221 0.935610 -vt 0.796327 0.931952 -vt 0.802486 0.925611 -vt 0.805146 0.929694 -vt 0.806130 0.922328 -vt 0.991775 0.087936 -vt 0.989286 0.087098 -vt 0.984642 0.083579 -vt 0.018217 0.308935 -vt 0.020647 0.298904 -vt 0.940379 0.252590 -vt 0.941067 0.249991 -vt 0.941286 0.255784 -vt 0.808523 0.942717 -vt 0.807837 0.933745 -vt 0.808574 0.929292 -vt 0.020111 0.308733 -vt 0.018677 0.315033 -vt 0.963078 0.272008 -vt 0.963993 0.267783 -vt 0.965592 0.274999 -vt 0.957106 0.265520 -vt 0.958651 0.262068 -vt 0.949950 0.258223 -vt 0.951555 0.254773 -vt 0.943822 0.252374 -vt 0.945018 0.248277 -vt 0.941693 0.245105 -vt 0.940215 0.240417 -vt 0.683238 0.892406 -vt 0.688520 0.890245 -vt 0.691708 0.891055 -vt 0.238398 0.892367 -vt 0.237120 0.887871 -vt 0.239590 0.884819 -vt 0.243663 0.886533 -vt 0.245457 0.878222 -vt 0.797533 0.950763 -vt 0.794950 0.947023 -vt 0.799195 0.942405 -vt 0.803828 0.943988 -vt 0.805120 0.936283 -vt 0.807062 0.940651 -vt 0.017527 0.317946 -vt 0.016714 0.314811 -vt 0.941559 0.240126 -vt 0.237645 0.898918 -vt 0.236788 0.898009 -vt 0.679210 0.891905 -vt 0.677938 0.890779 -vt 0.683009 0.890545 -vt 0.677457 0.892963 -vt 0.958511 0.252981 -vt 0.961506 0.250744 -vt 0.962575 0.256928 -vt 0.951405 0.245807 -vt 0.955020 0.244541 -vt 0.944852 0.238927 -vt 0.947944 0.237195 -vt 0.941480 0.235228 -vt 0.942294 0.230808 -vt 0.240679 0.908508 -vt 0.239573 0.902942 -vt 0.243566 0.898931 -vt 0.246657 0.902623 -vt 0.247978 0.894370 -vt 0.801264 0.966303 -vt 0.797729 0.963291 -vt 0.804017 0.956141 -vt 0.806699 0.959646 -vt 0.807265 0.952284 -vt 0.669783 0.891170 -vt 0.668813 0.889501 -vt 0.673480 0.890447 -vt 0.667551 0.891460 -vt 0.672507 0.892471 -vt 0.964633 0.242089 -vt 0.965472 0.237206 -vt 0.965734 0.242431 -vt 0.961670 0.240017 -vt 0.963914 0.236585 -vt 0.955168 0.234170 -vt 0.957127 0.231202 -vt 0.948084 0.226706 -vt 0.949941 0.223899 -vt 0.942430 0.219771 -vt 0.945250 0.218403 -vt 0.664209 0.887904 -vt 0.661156 0.885066 -vt 0.668173 0.886228 -vt 0.237748 0.929855 -vt 0.236966 0.924194 -vt 0.238104 0.923854 -vt 0.239298 0.929216 -vt 0.241039 0.921730 -vt 0.245989 0.923725 -vt 0.247048 0.916189 -vt 0.800807 0.987887 -vt 0.797019 0.985534 -vt 0.801960 0.979753 -vt 0.806318 0.980728 -vt 0.807366 0.972557 -vt 0.023628 0.341100 -vt 0.019964 0.339325 -vt 0.021172 0.335457 -vt 0.038495 0.346430 -vt 0.042539 0.349016 -vt 0.036977 0.349171 -vt 0.648784 0.869436 -vt 0.649920 0.873366 -vt 0.649364 0.875023 -vt 0.042743 0.345336 -vt 0.058254 0.345080 -vt 0.660892 0.852674 -vt 0.665321 0.867479 -vt 0.657943 0.868144 -vt 0.648848 0.853498 -vt 0.961567 0.554614 -vt 0.961798 0.563676 -vt 0.960886 0.563537 -vt 0.219557 0.729606 -vt 0.216713 0.738451 -vt 0.217108 0.729468 -vt 0.073163 0.346084 -vt 0.681457 0.835680 -vt 0.683509 0.852188 -vt 0.679967 0.865781 -vt 0.677422 0.836099 -vt 0.675413 0.866291 -vt 0.672605 0.836639 -vt 0.670393 0.866872 -vt 0.667584 0.837237 -vt 0.963804 0.533629 -vt 0.963371 0.547684 -vt 0.661362 0.758368 -vt 0.656878 0.759009 -vt 0.656131 0.855372 -vt 0.653090 0.759719 -vt 0.652417 0.869671 -vt 0.649080 0.725578 -vt 0.650878 0.873308 -vt 0.981917 0.272236 -vt 0.980219 0.256107 -vt 0.971842 0.128978 -vt 0.981389 0.257240 -vt 0.974105 0.144150 -vt 0.681213 0.736429 -vt 0.668801 0.755056 -vt 0.648377 0.712199 -vt 0.677315 0.717161 -vt 0.675792 0.754254 -vt 0.672962 0.717649 -vt 0.663311 0.718727 -vt 0.658662 0.719260 -vt 0.654467 0.719781 -vt 0.651000 0.720164 -vt 0.210377 0.858008 -vt 0.206751 0.878264 -vt 0.209190 0.841295 -vt 0.170707 0.336961 -vt 0.191573 0.340037 -vt 0.154225 0.342097 -vt 0.188302 0.333426 -vt 0.198027 0.334072 -vt 0.982814 0.286239 -vt 0.983145 0.279376 -vt 0.981899 0.256880 -vt 0.647745 0.711847 -vt 0.647515 0.739863 -vt 0.969330 0.435222 -vt 0.971341 0.413033 -vt 0.969859 0.435533 -vt 0.205764 0.879725 -vt 0.207165 0.857482 -vt 0.218135 0.859238 -vt 0.214178 0.881134 -vt 0.213784 0.858521 -vt 0.170335 0.349559 -vt 0.192611 0.349706 -vt 0.170340 0.354484 -vt 0.675516 0.708808 -vt 0.680748 0.708509 -vt 0.665865 0.709882 -vt 0.668161 0.718183 -vt 0.656431 0.710857 -vt 0.199761 0.339164 -vt 0.200994 0.331111 -vt 0.203404 0.331822 -vt 0.199532 0.330472 -vt 0.202087 0.329487 -vt 0.654909 0.707005 -vt 0.652087 0.708933 -vt 0.984900 0.290782 -vt 0.983661 0.288656 -vt 0.984345 0.288443 -vt 0.653755 0.704461 -vt 0.649821 0.708739 -vt 0.651277 0.705458 -vt 0.648815 0.708511 -vt 0.972737 0.402715 -vt 0.973988 0.399778 -vt 0.973619 0.402988 -vt 0.204234 0.338865 -vt 0.206078 0.344357 -vt 0.203085 0.347110 -vt 0.206045 0.326317 -vt 0.209143 0.324297 -vt 0.985269 0.293368 -vt 0.983973 0.291174 -vt 0.684089 0.701642 -vt 0.683118 0.703697 -vt 0.677851 0.704362 -vt 0.675554 0.702561 -vt 0.668366 0.705194 -vt 0.661908 0.701839 -vt 0.662733 0.704424 -vt 0.658793 0.707653 -vt 0.655870 0.701710 -vt 0.321441 0.377251 -vt 0.320073 0.374159 -vt 0.320783 0.373594 -vt 0.212060 0.894650 -vt 0.214106 0.897827 -vt 0.209710 0.894077 -vt 0.667911 0.699598 -vt 0.669853 0.701924 -vt 0.657782 0.699654 -vt 0.654152 0.702448 -vt 0.209631 0.331938 -vt 0.211478 0.336365 -vt 0.208878 0.340674 -vt 0.209108 0.320006 -vt 0.214276 0.310048 -vt 0.343109 0.838667 -vt 0.342543 0.842966 -vt 0.341133 0.843910 -vt 0.342260 0.838321 -vt 0.339542 0.843520 -vt 0.674325 0.697449 -vt 0.667474 0.696879 -vt 0.661228 0.699966 -vt 0.665718 0.695937 -vt 0.668066 0.693402 -vt 0.326141 0.382366 -vt 0.326374 0.387706 -vt 0.324039 0.382518 -vt 0.329538 0.385225 -vt 0.330723 0.390981 -vt 0.214190 0.336332 -vt 0.211813 0.342117 -vt 0.213677 0.329439 -vt 0.209249 0.327949 -vt 0.214120 0.320963 -vt 0.213554 0.317579 -vt 0.345816 0.832231 -vt 0.341369 0.836557 -vt 0.673963 0.695194 -vt 0.327099 0.396152 -vt 0.322909 0.381672 -vt 0.329510 0.393908 -vt 0.327745 0.386970 -vt 0.215883 0.324756 -vt 0.215574 0.309354 -vt 0.212748 0.315459 -vt 0.346274 0.832614 -vt 0.674110 0.692877 -vt 0.216657 0.312587 -vt 0.347400 0.826436 -vt 0.346093 0.827603 -vt 0.343903 0.824157 -vt 0.673415 0.690416 -vt 0.672155 0.692205 -vt 0.326665 0.398179 -vt 0.325520 0.392272 -vt 0.328569 0.398763 -vt 0.217527 0.303301 -vt 0.219380 0.301473 -vt 0.217381 0.305433 -vt 0.678291 0.690156 -vt 0.679390 0.691870 -vt 0.429259 0.680467 -vt 0.427071 0.681129 -vt 0.424782 0.673300 -vt 0.218502 0.308546 -vt 0.220908 0.308654 -vt 0.219776 0.316200 -vt 0.339858 0.825643 -vt 0.340511 0.830503 -vt 0.678656 0.687734 -vt 0.675494 0.690053 -vt 0.327285 0.406444 -vt 0.326582 0.405565 -vt 0.330557 0.406143 -vt 0.328354 0.404947 -vt 0.221900 0.299242 -vt 0.219886 0.304118 -vt 0.344337 0.817919 -vt 0.346347 0.819559 -vt 0.341869 0.820315 -vt 0.681175 0.687320 -vt 0.328842 0.411489 -vt 0.333143 0.408485 -vt 0.331169 0.413989 -vt 0.336064 0.411620 -vt 0.335537 0.417054 -vt 0.338286 0.416087 -vt 0.432370 0.692156 -vt 0.428354 0.690910 -vt 0.427042 0.685098 -vt 0.223303 0.299419 -vt 0.222303 0.303949 -vt 0.222304 0.294854 -vt 0.223852 0.291924 -vt 0.346008 0.812883 -vt 0.347090 0.815111 -vt 0.338750 0.820227 -vt 0.326563 0.413495 -vt 0.223736 0.288048 -vt 0.343885 0.811341 -vt 0.333411 0.421098 -vt 0.330144 0.421768 -vt 0.431626 0.701488 -vt 0.427735 0.698836 -vt 0.428076 0.694862 -vt 0.348817 0.761803 -vt 0.348118 0.760122 -vt 0.348466 0.754769 -vt 0.346615 0.804805 -vt 0.347486 0.814225 -vt 0.338248 0.813747 -vt 0.341426 0.813889 -vt 0.692277 0.681570 -vt 0.685066 0.684430 -vt 0.679961 0.686303 -vt 0.327836 0.419122 -vt 0.325506 0.421087 -vt 0.225367 0.287373 -vt 0.224280 0.293557 -vt 0.225681 0.275837 -vt 0.343350 0.804464 -vt 0.345485 0.806012 -vt 0.332295 0.429076 -vt 0.329018 0.429813 -vt 0.226229 0.276368 -vt 0.349371 0.765450 -vt 0.337759 0.806857 -vt 0.340900 0.807133 -vt 0.699281 0.680779 -vt 0.690616 0.683168 -vt 0.326728 0.427051 -vt 0.324453 0.429071 -vt 0.433633 0.712860 -vt 0.432547 0.720263 -vt 0.432767 0.711669 -vt 0.342996 0.798375 -vt 0.344738 0.798937 -vt 0.331195 0.437379 -vt 0.328968 0.440340 -vt 0.332634 0.806821 -vt 0.334529 0.810520 -vt 0.331354 0.814165 -vt 0.326773 0.813462 -vt 0.328481 0.817518 -vt 0.326047 0.820448 -vt 0.322689 0.818003 -vt 0.323586 0.828801 -vt 0.322846 0.824896 -vt 0.325924 0.433963 -vt 0.323446 0.439944 -vt 0.424931 0.711227 -vt 0.423360 0.716651 -vt 0.422607 0.708508 -vt 0.430194 0.717431 -vt 0.428744 0.722947 -vt 0.427769 0.714565 -vt 0.644228 0.895410 -vt 0.643041 0.899649 -vt 0.643700 0.895322 -vt 0.342090 0.789351 -vt 0.792974 0.681321 -vt 0.792904 0.678173 -vt 0.793985 0.681187 -vt 0.322035 0.438753 -vt 0.322735 0.433814 -vt 0.793684 0.675925 -vt 0.793713 0.678179 -vt 0.319508 0.439125 -vt 0.321484 0.437783 -vt 0.320282 0.440891 -vt 0.644190 0.903513 -vt 0.644522 0.906866 -vt 0.643421 0.903585 -vt 0.341294 0.785160 -vt 0.337418 0.785093 -vt 0.341746 0.784191 -vt 0.339321 0.787567 -vt 0.336773 0.786802 -vt 0.338302 0.792582 -vt 0.333184 0.791400 -vt 0.335550 0.796162 -vt 0.331513 0.795204 -vt 0.321196 0.449392 -vt 0.321162 0.445462 -vt 0.325995 0.450675 -vt 0.416745 0.726513 -vt 0.418802 0.723995 -vt 0.423954 0.730586 -vt 0.421440 0.732323 -vt 0.426820 0.733899 -vt 0.483337 0.965396 -vt 0.483750 0.969953 -vt 0.482675 0.970221 -vt 0.332800 0.786532 -vt 0.337464 0.784451 -vt 0.328313 0.789866 -vt 0.324714 0.792927 -vt 0.328054 0.800331 -vt 0.319830 0.801043 -vt 0.306212 0.435995 -vt 0.317023 0.440101 -vt 0.320510 0.442767 -vt 0.411900 0.724323 -vt 0.412647 0.721472 -vt 0.328512 0.788761 -vt 0.322607 0.796372 -vt 0.322789 0.807529 -vt 0.316408 0.809459 -vt 0.325350 0.804144 -vt 0.501390 0.969892 -vt 0.502057 0.963010 -vt 0.503045 0.972297 -vt 0.309151 0.442660 -vt 0.485176 0.959232 -vt 0.486833 0.957805 -vt 0.485173 0.963712 -vt 0.324575 0.791258 -vt 0.498854 0.960139 -vt 0.499777 0.955693 -vt 0.499962 0.960517 -vt 0.303835 0.431590 -vt 0.308564 0.433502 -vt 0.308467 0.434105 -vt 0.304021 0.432804 -vt 0.308840 0.435757 -vt 0.304999 0.440650 -vt 0.408884 0.726676 -vt 0.405851 0.722629 -vt 0.410460 0.725920 -vt 0.485611 0.955030 -vt 0.347424 0.571776 -vt 0.348718 0.573952 -vt 0.347700 0.574281 -vt 0.321769 0.790648 -vt 0.324839 0.790103 -vt 0.320896 0.792369 -vt 0.318341 0.793918 -vt 0.315179 0.800874 -vt 0.311494 0.811300 -vt 0.309846 0.807902 -vt 0.316534 0.805305 -vt 0.306580 0.817073 -vt 0.304129 0.814818 -vt 0.912183 0.822247 -vt 0.910731 0.825932 -vt 0.911175 0.821963 -vt 0.299615 0.431707 -vt 0.300315 0.433390 -vt 0.398505 0.717227 -vt 0.401047 0.716212 -vt 0.403201 0.719144 -vt 0.403750 0.723787 -vt 0.300508 0.437013 -vt 0.297381 0.435951 -vt 0.316872 0.785182 -vt 0.318627 0.790141 -vt 0.296463 0.442389 -vt 0.400357 0.724703 -vt 0.395456 0.724556 -vt 0.396490 0.719333 -vt 0.312851 0.782940 -vt 0.312101 0.791070 -vt 0.307319 0.790063 -vt 0.305697 0.798667 -vt 0.300359 0.796415 -vt 0.301225 0.803833 -vt 0.294161 0.806504 -vt 0.295599 0.804681 -vt 0.295433 0.810151 -vt 0.402909 0.727211 -vt 0.399181 0.735493 -vt 0.348658 0.555685 -vt 0.348646 0.560135 -vt 0.347833 0.560052 -vt 0.346998 0.559969 -vt 0.347615 0.555527 -vt 0.302413 0.790152 -vt 0.298511 0.801922 -vt 0.295065 0.799884 -vt 0.292633 0.804113 -vt 0.390022 0.731595 -vt 0.394467 0.729969 -vt 0.395250 0.736973 -vt 0.401928 0.738986 -vt 0.397353 0.740056 -vt 0.403115 0.740440 -vt 0.911730 0.790955 -vt 0.912127 0.797525 -vt 0.910972 0.797462 -vt 0.291225 0.797092 -vt 0.291487 0.451983 -vt 0.295749 0.452067 -vt 0.302115 0.458366 -vt 0.373581 0.723614 -vt 0.277248 0.808214 -vt 0.643656 0.867165 -vt 0.642807 0.880766 -vt 0.642872 0.865875 -vt 0.367174 0.717978 -vt 0.380887 0.723673 -vt 0.389179 0.735015 -vt 0.376072 0.728996 -vt 0.390459 0.737204 -vt 0.910446 0.790309 -vt 0.910991 0.775814 -vt 0.909601 0.789003 -vt 0.909921 0.774658 -vt 0.300863 0.784605 -vt 0.286844 0.792115 -vt 0.302452 0.782294 -vt 0.298783 0.787637 -vt 0.296211 0.791307 -vt 0.278049 0.799563 -vt 0.270500 0.805801 -vt 0.644512 0.882534 -vt 0.643577 0.881980 -vt 0.273113 0.434058 -vt 0.280231 0.438351 -vt 0.286676 0.442662 -vt 0.275293 0.437868 -vt 0.287995 0.444981 -vt 0.289625 0.447980 -vt 0.272290 0.439960 -vt 0.375062 0.727186 -vt 0.280429 0.795095 -vt 0.274187 0.812505 -vt 0.267044 0.814886 -vt 0.270373 0.442934 -vt 0.367251 0.721779 -vt 0.369417 0.725238 -vt 0.912308 0.769737 -vt 0.913028 0.774778 -vt 0.912127 0.775725 -vt 0.282517 0.792153 -vt 0.278079 0.792455 -vt 0.283117 0.791351 -vt 0.276276 0.794778 -vt 0.271608 0.800969 -vt 0.259212 0.429203 -vt 0.265496 0.430020 -vt 0.267809 0.433802 -vt 0.263501 0.435695 -vt 0.360730 0.719011 -vt 0.363615 0.723127 -vt 0.364933 0.725049 -vt 0.370261 0.726655 -vt 0.348746 0.777243 -vt 0.349113 0.781959 -vt 0.348351 0.781978 -vt 0.254043 0.806605 -vt 0.259705 0.808288 -vt 0.255594 0.813993 -vt 0.258552 0.437464 -vt 0.253875 0.441492 -vt 0.254047 0.431240 -vt 0.263291 0.444053 -vt 0.259447 0.448627 -vt 0.356617 0.720662 -vt 0.352388 0.733098 -vt 0.352320 0.715191 -vt 0.359876 0.724833 -vt 0.356479 0.728830 -vt 0.361347 0.726733 -vt 0.359153 0.731899 -vt 0.361950 0.727525 -vt 0.360264 0.733156 -vt 0.260931 0.788219 -vt 0.261864 0.800475 -vt 0.253075 0.796219 -vt 0.251100 0.809883 -vt 0.252191 0.452206 -vt 0.251113 0.437928 -vt 0.256857 0.457956 -vt 0.359550 0.743021 -vt 0.494893 0.813220 -vt 0.495111 0.826474 -vt 0.494045 0.827137 -vt 0.493515 0.814661 -vt 0.493135 0.828480 -vt 0.274588 0.763349 -vt 0.272253 0.776107 -vt 0.267245 0.781556 -vt 0.269218 0.768913 -vt 0.256926 0.780593 -vt 0.258658 0.785878 -vt 0.344346 0.382875 -vt 0.343235 0.397634 -vt 0.343242 0.382183 -vt 0.345232 0.384214 -vt 0.344741 0.399160 -vt 0.256188 0.469981 -vt 0.261308 0.463293 -vt 0.261276 0.475941 -vt 0.357959 0.746208 -vt 0.356156 0.751303 -vt 0.496017 0.796705 -vt 0.496180 0.813297 -vt 0.492934 0.799112 -vt 0.492649 0.816399 -vt 0.277831 0.749572 -vt 0.272223 0.754907 -vt 0.263880 0.774181 -vt 0.274384 0.741971 -vt 0.264240 0.761778 -vt 0.341139 0.416889 -vt 0.341324 0.398691 -vt 0.342060 0.397775 -vt 0.342116 0.416315 -vt 0.345583 0.400890 -vt 0.344736 0.419111 -vt 0.263897 0.491683 -vt 0.264901 0.480016 -vt 0.269199 0.497384 -vt 0.368289 0.787500 -vt 0.362886 0.757661 -vt 0.367688 0.773935 -vt 0.496935 0.814240 -vt 0.494328 0.777849 -vt 0.494470 0.796840 -vt 0.287856 0.729349 -vt 0.281128 0.746206 -vt 0.284388 0.732620 -vt 0.286005 0.720327 -vt 0.271904 0.742507 -vt 0.262187 0.763283 -vt 0.341061 0.438177 -vt 0.343187 0.416739 -vt 0.342481 0.438689 -vt 0.269273 0.510171 -vt 0.260711 0.488006 -vt 0.272753 0.513870 -vt 0.374523 0.791979 -vt 0.369169 0.775036 -vt 0.376519 0.793255 -vt 0.494438 0.756790 -vt 0.496166 0.777673 -vt 0.492338 0.759389 -vt 0.492093 0.781355 -vt 0.295378 0.712216 -vt 0.291593 0.715315 -vt 0.280156 0.736329 -vt 0.296439 0.705633 -vt 0.283551 0.720637 -vt 0.340464 0.462582 -vt 0.340305 0.438721 -vt 0.344704 0.442312 -vt 0.343575 0.465582 -vt 0.281119 0.534831 -vt 0.276833 0.517892 -vt 0.286906 0.541908 -vt 0.383239 0.811232 -vt 0.385772 0.812518 -vt 0.493253 0.735986 -vt 0.491088 0.739553 -vt 0.490736 0.763177 -vt 0.304770 0.693942 -vt 0.298169 0.697661 -vt 0.339002 0.489253 -vt 0.339304 0.463288 -vt 0.341966 0.490560 -vt 0.341989 0.463210 -vt 0.345113 0.469337 -vt 0.344129 0.494252 -vt 0.295079 0.559906 -vt 0.495034 0.734623 -vt 0.496235 0.756867 -vt 0.493781 0.713671 -vt 0.491576 0.716228 -vt 0.318614 0.673508 -vt 0.307107 0.692272 -vt 0.329355 0.653797 -vt 0.314194 0.675141 -vt 0.311937 0.675305 -vt 0.295687 0.698447 -vt 0.340063 0.489114 -vt 0.339081 0.517456 -vt 0.341155 0.518673 -vt 0.343370 0.521314 -vt 0.307093 0.582195 -vt 0.292085 0.557526 -vt 0.310294 0.583663 -vt 0.492338 0.693943 -vt 0.490711 0.695218 -vt 0.490328 0.718277 -vt 0.331751 0.654396 -vt 0.342979 0.550279 -vt 0.344766 0.523749 -vt 0.344732 0.551680 -vt 0.406805 0.851333 -vt 0.419057 0.871232 -vt 0.404967 0.851114 -vt 0.408288 0.851146 -vt 0.420510 0.869867 -vt 0.496069 0.691093 -vt 0.496205 0.712411 -vt 0.495350 0.712593 -vt 0.493981 0.672867 -vt 0.494267 0.692456 -vt 0.347638 0.636551 -vt 0.333096 0.654488 -vt 0.328228 0.651118 -vt 0.347572 0.629107 -vt 0.328743 0.652331 -vt 0.341034 0.548748 -vt 0.340934 0.579096 -vt 0.339235 0.547356 -vt 0.344585 0.580359 -vt 0.737162 0.972337 -vt 0.735104 0.939404 -vt 0.736859 0.937305 -vt 0.697104 0.977262 -vt 0.694739 0.944610 -vt 0.695807 0.943100 -vt 0.745640 0.941352 -vt 0.747678 0.908666 -vt 0.747400 0.943279 -vt 0.749074 0.945475 -vt 0.750596 0.912387 -vt 0.750126 0.947094 -vt 0.834454 0.948741 -vt 0.835870 0.915126 -vt 0.836215 0.949752 -vt 0.749628 0.954727 -vt 0.749515 0.964201 -vt 0.748162 0.952686 -vt 0.689529 0.946365 -vt 0.688861 0.939144 -vt 0.691001 0.946718 -vt 0.838254 0.949767 -vt 0.837140 0.959492 -vt 0.833055 0.957088 -vt 0.832867 0.947029 -vt 0.839024 0.959169 -vt 0.744079 0.948231 -vt 0.744072 0.939992 -vt 0.835084 0.958762 -vt 0.831406 0.954737 -vt 0.831928 0.945446 -vt 0.746084 0.950202 -vt 0.831124 0.966913 -vt 0.833518 0.969507 -vt 0.690658 0.939361 -vt 0.688420 0.931358 -vt 0.690250 0.931244 -vt 0.836177 0.971042 -vt 0.741967 0.955920 -vt 0.742467 0.947075 -vt 0.744336 0.957980 -vt 0.746990 0.960887 -vt 0.691603 0.906902 -vt 0.692731 0.908898 -vt 0.690467 0.909585 -vt 0.694651 0.909528 -vt 0.695928 0.954296 -vt 0.685827 0.923272 -vt 0.688349 0.921875 -vt 0.687949 0.924301 -vt 0.684573 0.921058 -vt 0.686603 0.920534 -vt 0.697434 0.914324 -vt 0.695915 0.913349 -vt 0.697582 0.911014 -vt 0.696710 0.908390 -vt 0.694475 0.906764 -vt 0.684294 0.917973 -vt 0.686021 0.918529 -vt 0.696289 0.917809 -vt 0.694244 0.918420 -vt 0.695424 0.915921 -vt 0.695642 0.911102 -vt 0.685043 0.914493 -vt 0.686196 0.916047 -vt 0.687107 0.913468 -vt 0.694323 0.920923 -vt 0.692563 0.920452 -vt 0.686702 0.911161 -vt 0.688609 0.911198 -vt 0.691204 0.923503 -vt 0.690651 0.921711 -vt 0.689014 0.908496 -vt 0.693921 0.961919 -vt 0.692034 0.962305 -vt 0.690791 0.960998 -vt 0.537365 0.967731 -vt 0.540273 0.926100 -vt 0.538629 0.969632 -vt 0.540538 0.972076 -vt 0.542516 0.928967 -vt 0.542531 0.974205 -vt 0.574712 0.966410 -vt 0.576569 0.926080 -vt 0.576665 0.967605 -vt 0.122616 0.829889 -vt 0.125126 0.797771 -vt 0.123034 0.830126 -vt 0.122157 0.829290 -vt 0.982068 0.576506 -vt 0.980820 0.542702 -vt 0.982649 0.577435 -vt 0.980403 0.573881 -vt 0.979212 0.540202 -vt 0.977632 0.569554 -vt 0.976108 0.535352 -vt 0.979138 0.482456 -vt 0.979268 0.450391 -vt 0.982724 0.488056 -vt 0.977336 0.479674 -vt 0.976764 0.446389 -vt 0.186407 0.806709 -vt 0.186982 0.838978 -vt 0.185980 0.806468 -vt 0.187906 0.839103 -vt 0.186844 0.806724 -vt 0.187273 0.806509 -vt 0.187677 0.806077 -vt 0.412687 0.942450 -vt 0.413282 0.908424 -vt 0.415209 0.905316 -vt 0.415977 0.937166 -vt 0.419355 0.898700 -vt 0.419204 0.931960 -vt 0.421732 0.894940 -vt 0.422167 0.927157 -vt 0.423121 0.892763 -vt 0.424203 0.891083 -vt 0.127394 0.782460 -vt 0.124767 0.828856 -vt 0.124342 0.829483 -vt 0.126158 0.798047 -vt 0.123909 0.829914 -vt 0.123470 0.830133 -vt 0.974946 0.426730 -vt 0.976192 0.423876 -vt 0.187616 0.860101 -vt 0.188491 0.859640 -vt 0.188911 0.838033 -vt 0.418477 0.955584 -vt 0.127306 0.777571 -vt 0.980289 0.520700 -vt 0.980872 0.508600 -vt 0.980788 0.521402 -vt 0.978685 0.518294 -vt 0.979576 0.506914 -vt 0.975572 0.513446 -vt 0.976481 0.502261 -vt 0.978859 0.428338 -vt 0.979590 0.416626 -vt 0.981162 0.432104 -vt 0.185797 0.871014 -vt 0.185719 0.853031 -vt 0.187221 0.872423 -vt 0.412120 0.980530 -vt 0.410464 0.968401 -vt 0.412396 0.965457 -vt 0.415161 0.975831 -vt 0.415234 0.960920 -vt 0.125824 0.760182 -vt 0.126865 0.762711 -vt 0.126586 0.766041 -vt 0.978747 0.498996 -vt 0.981814 0.413556 -vt 0.981902 0.420534 -vt 0.976425 0.410964 -vt 0.978476 0.407458 -vt 0.975154 0.408498 -vt 0.976079 0.402411 -vt 0.411858 0.989715 -vt 0.410819 0.982269 -vt 0.414290 0.986654 -vt 0.417788 0.981049 -vt 0.418850 0.969621 -vt 0.421331 0.974526 -vt 0.422013 0.963872 -vt 0.423709 0.969429 -vt 0.423209 0.961511 -vt 0.497422 0.837481 -vt 0.495406 0.839067 -vt 0.497226 0.837092 -vt 0.184064 0.880811 -vt 0.182898 0.878752 -vt 0.184256 0.876014 -vt 0.888533 0.964193 -vt 0.891200 0.965851 -vt 0.891014 0.966242 -vt 0.414385 0.992446 -vt 0.417734 0.987562 -vt 0.421378 0.980953 -vt 0.424007 0.975160 -vt 0.125407 0.755967 -vt 0.127681 0.757444 -vt 0.122910 0.754623 -vt 0.123686 0.754517 -vt 0.124430 0.757105 -vt 0.981209 0.498202 -vt 0.982182 0.496014 -vt 0.982054 0.498764 -vt 0.979335 0.496014 -vt 0.980566 0.494669 -vt 0.976621 0.492059 -vt 0.977305 0.490382 -vt 0.979088 0.404446 -vt 0.981157 0.405673 -vt 0.982396 0.410707 -vt 0.181192 0.879382 -vt 0.181839 0.872426 -vt 0.181923 0.882987 -vt 0.182999 0.884004 -vt 0.180130 0.885398 -vt 0.118269 0.750780 -vt 0.121115 0.751536 -vt 0.120928 0.752558 -vt 0.342845 0.624452 -vt 0.344548 0.622411 -vt 0.347864 0.625429 -vt 0.178849 0.873815 -vt 0.176429 0.871253 -vt 0.178612 0.868158 -vt 0.179460 0.880984 -vt 0.176792 0.878925 -vt 0.177307 0.885367 -vt 0.118706 0.748982 -vt 0.121730 0.750259 -vt 0.113704 0.748105 -vt 0.117116 0.724772 -vt 0.121063 0.724253 -vt 0.120851 0.729638 -vt 0.116625 0.731475 -vt 0.120230 0.737168 -vt 0.115930 0.739147 -vt 0.119406 0.744403 -vt 0.172256 0.885513 -vt 0.168883 0.881530 -vt 0.173443 0.880236 -vt 0.345506 0.605777 -vt 0.346152 0.602695 -vt 0.346315 0.608908 -vt 0.160427 0.888211 -vt 0.157218 0.883861 -vt 0.163293 0.882776 -vt 0.881697 0.936016 -vt 0.882294 0.938653 -vt 0.881822 0.942311 -vt 0.100301 0.720079 -vt 0.103757 0.717971 -vt 0.106507 0.721516 -vt 0.882716 0.926070 -vt 0.882702 0.945953 -vt 0.491827 0.878070 -vt 0.491035 0.876289 -vt 0.491434 0.872569 -vt 0.147718 0.889958 -vt 0.145824 0.885372 -vt 0.151211 0.884676 -vt 0.492190 0.882479 -vt 0.141511 0.891691 -vt 0.147683 0.892603 -vt 0.882133 0.924146 -vt 0.882899 0.919582 -vt 0.088661 0.742228 -vt 0.082224 0.740546 -vt 0.088675 0.740267 -vt 0.078656 0.739187 -vt 0.082291 0.737197 -vt 0.347873 0.581802 -vt 0.347436 0.583793 -vt 0.344559 0.583721 -vt 0.342830 0.581677 -vt 0.340459 0.583397 -vt 0.136572 0.880226 -vt 0.138453 0.876999 -vt 0.138546 0.884702 -vt 0.136358 0.887505 -vt 0.138324 0.891250 -vt 0.079965 0.718260 -vt 0.077459 0.716855 -vt 0.080458 0.715412 -vt 0.079252 0.724937 -vt 0.076556 0.722189 -vt 0.078772 0.732628 -vt 0.075932 0.729714 -vt 0.075777 0.736996 -vt 0.132907 0.892118 -vt 0.134369 0.892287 -vt 0.135338 0.893992 -vt 0.074875 0.741589 -vt 0.073541 0.738320 -vt 0.496159 0.890399 -vt 0.495008 0.890129 -vt 0.495124 0.889275 -vt 0.131327 0.888263 -vt 0.132819 0.888448 -vt 0.071740 0.744520 -vt 0.070704 0.743618 -vt 0.071847 0.739982 -vt 0.974225 0.828835 -vt 0.971230 0.830683 -vt 0.974167 0.827081 -vt 0.682741 0.959026 -vt 0.683508 0.955404 -vt 0.683186 0.959158 -vt 0.681058 0.957679 -vt 0.682687 0.954714 -vt 0.677090 0.953423 -vt 0.680509 0.953972 -vt 0.672383 0.947498 -vt 0.676252 0.949018 -vt 0.668446 0.941812 -vt 0.671612 0.943037 -vt 0.666959 0.939338 -vt 0.668000 0.937849 -vt 0.071837 0.746435 -vt 0.070086 0.749282 -vt 0.071916 0.748320 -vt 0.070238 0.752162 -vt 0.968087 0.836493 -vt 0.967905 0.833133 -vt 0.968370 0.832859 -vt 0.969498 0.836192 -vt 0.969530 0.832081 -vt 0.971151 0.833788 -vt 0.523705 0.942744 -vt 0.524049 0.944296 -vt 0.520220 0.948464 -vt 0.518939 0.947933 -vt 0.515859 0.952617 -vt 0.514326 0.952082 -vt 0.513831 0.956302 -vt 0.130562 0.884107 -vt 0.131598 0.886006 -vt 0.666968 0.934451 -vt 0.668291 0.934886 -vt 0.069315 0.753808 -vt 0.070827 0.753856 -vt 0.071079 0.750975 -vt 0.973345 0.833727 -vt 0.974613 0.830369 -vt 0.521381 0.943176 -vt 0.516685 0.948260 -vt 0.680810 0.950862 -vt 0.682662 0.949464 -vt 0.676569 0.945760 -vt 0.679131 0.945120 -vt 0.671926 0.939834 -vt 0.674485 0.939296 -vt 0.670213 0.933858 -vt 0.970341 0.840360 -vt 0.968931 0.839381 -vt 0.668186 0.929427 -vt 0.670481 0.930397 -vt 0.069972 0.756724 -vt 0.069441 0.757612 -vt 0.071101 0.758503 -vt 0.973302 0.838453 -vt 0.973338 0.835992 -vt 0.971057 0.843513 -vt 0.968822 0.844890 -vt 0.524230 0.932999 -vt 0.524598 0.935081 -vt 0.521364 0.938540 -vt 0.520126 0.937328 -vt 0.516670 0.943326 -vt 0.515742 0.941541 -vt 0.514312 0.946967 -vt 0.513849 0.941609 -vt 0.513331 0.947907 -vt 0.131141 0.875774 -vt 0.130462 0.879897 -vt 0.130004 0.878208 -vt 0.679660 0.937891 -vt 0.681830 0.936918 -vt 0.683160 0.942694 -vt 0.675021 0.931975 -vt 0.678213 0.931996 -vt 0.670725 0.926831 -vt 0.674086 0.926855 -vt 0.668557 0.924434 -vt 0.670050 0.922303 -vt 0.667616 0.923555 -vt 0.668032 0.919441 -vt 0.072555 0.763074 -vt 0.070837 0.761878 -vt 0.071764 0.757361 -vt 0.970032 0.847294 -vt 0.974058 0.842502 -vt 0.974475 0.839631 -vt 0.129517 0.877040 -vt 0.131580 0.872390 -vt 0.129160 0.874379 -vt 0.130899 0.870895 -vt 0.682838 0.934648 -vt 0.683854 0.939969 -vt 0.678977 0.929119 -vt 0.594700 0.926908 -vt 0.591832 0.930245 -vt 0.594458 0.926527 -vt 0.074264 0.760277 -vt 0.073140 0.759314 -vt 0.133426 0.870143 -vt 0.668495 0.915485 -vt 0.670361 0.915520 -vt 0.670227 0.918838 -vt 0.133127 0.867807 -vt 0.590989 0.930647 -vt 0.592142 0.928637 -vt 0.075877 0.766656 -vt 0.074169 0.767984 -vt 0.074293 0.765695 -vt 0.075276 0.762179 -vt 0.077097 0.760505 -vt 0.076420 0.755722 -vt 0.711146 0.952299 -vt 0.712207 0.953134 -vt 0.708876 0.954878 -vt 0.518100 0.930697 -vt 0.520350 0.930383 -vt 0.515971 0.934084 -vt 0.134686 0.867222 -vt 0.137246 0.865126 -vt 0.132104 0.863635 -vt 0.134518 0.864109 -vt 0.131262 0.856649 -vt 0.133610 0.857869 -vt 0.131017 0.849611 -vt 0.133114 0.850245 -vt 0.670442 0.912433 -vt 0.672345 0.911254 -vt 0.674507 0.916466 -vt 0.080557 0.773291 -vt 0.075481 0.770597 -vt 0.077258 0.769871 -vt 0.590612 0.933550 -vt 0.589693 0.935529 -vt 0.589988 0.932857 -vt 0.590186 0.939519 -vt 0.080900 0.772508 -vt 0.080142 0.768459 -vt 0.077923 0.766740 -vt 0.078642 0.761973 -vt 0.080994 0.761530 -vt 0.079201 0.757278 -vt 0.709960 0.948664 -vt 0.711541 0.950300 -vt 0.705745 0.953558 -vt 0.704751 0.951599 -vt 0.703310 0.955190 -vt 0.001405 0.959066 -vt 0.001859 0.961658 -vt 0.001185 0.961800 -vt 0.000634 0.959097 -vt 0.000926 0.963197 -vt 0.136806 0.862102 -vt 0.139842 0.862028 -vt 0.083185 0.769919 -vt 0.000205 0.956588 -vt 0.000961 0.954739 -vt 0.143549 0.861054 -vt 0.139479 0.858718 -vt 0.143061 0.857422 -vt 0.139121 0.853839 -vt 0.138757 0.846107 -vt 0.142687 0.850389 -vt 0.138670 0.839512 -vt 0.142406 0.842671 -vt 0.138774 0.836668 -vt 0.142312 0.836737 -vt 0.138868 0.835935 -vt 0.142359 0.834596 -vt 0.590097 0.942802 -vt 0.589330 0.938569 -vt 0.088502 0.775369 -vt 0.088044 0.768152 -vt 0.083918 0.762931 -vt 0.712269 0.939145 -vt 0.713897 0.943218 -vt 0.709587 0.945468 -vt 0.707037 0.942060 -vt 0.704366 0.948312 -vt 0.702130 0.944966 -vt 0.702534 0.949411 -vt 0.001870 0.951564 -vt 0.001483 0.955740 -vt 0.146870 0.837342 -vt 0.698996 0.935444 -vt 0.701475 0.945351 -vt 0.148942 0.859326 -vt 0.157309 0.857953 -vt 0.146546 0.833340 -vt 0.156747 0.832705 -vt 0.146646 0.834649 -vt 0.101505 0.777508 -vt 0.087882 0.772790 -vt 0.706063 0.931487 -vt 0.700819 0.934237 -vt 0.703617 0.944045 -vt 0.002778 0.942425 -vt 0.003726 0.941888 -vt 0.148487 0.857093 -vt 0.170952 0.850561 -vt 0.147943 0.854187 -vt 0.161971 0.851710 -vt 0.156906 0.838705 -vt 0.147144 0.842188 -vt 0.101376 0.781438 -vt 0.095124 0.779154 -vt 0.096278 0.778720 -vt 0.106086 0.780554 -vt 0.705087 0.926032 -vt 0.709569 0.924802 -vt 0.162872 0.856692 -vt 0.156628 0.858981 -vt 0.595150 0.966096 -vt 0.594026 0.961506 -vt 0.594644 0.961386 -vt 0.108397 0.785808 -vt 0.101562 0.772813 -vt 0.107228 0.774118 -vt 0.101983 0.767190 -vt 0.706555 0.919640 -vt 0.700753 0.922668 -vt 0.699851 0.928787 -vt 0.698294 0.924066 -vt 0.697987 0.929830 -vt 0.005712 0.931259 -vt 0.005297 0.936308 -vt 0.004671 0.936219 -vt 0.004039 0.936100 -vt 0.004957 0.931206 -vt 0.168935 0.850198 -vt 0.161203 0.844977 -vt 0.166640 0.844327 -vt 0.161844 0.836994 -vt 0.166210 0.836643 -vt 0.161665 0.831086 -vt 0.165992 0.830105 -vt 0.161647 0.828980 -vt 0.165994 0.827302 -vt 0.594405 0.966152 -vt 0.593389 0.961599 -vt 0.111142 0.788001 -vt 0.113432 0.787210 -vt 0.110401 0.782636 -vt 0.111200 0.778010 -vt 0.114790 0.781306 -vt 0.111860 0.773420 -vt 0.708727 0.909084 -vt 0.707800 0.913954 -vt 0.702016 0.916961 -vt 0.220133 0.963878 -vt 0.221048 0.969574 -vt 0.219871 0.971232 -vt 0.170013 0.841468 -vt 0.172927 0.840317 -vt 0.169131 0.847363 -vt 0.169401 0.833962 -vt 0.171857 0.834061 -vt 0.169176 0.827440 -vt 0.171244 0.827038 -vt 0.635560 0.917003 -vt 0.635009 0.920086 -vt 0.634358 0.915363 -vt 0.637298 0.917709 -vt 0.636905 0.921947 -vt 0.636580 0.917784 -vt 0.112688 0.798689 -vt 0.111338 0.791817 -vt 0.111744 0.791465 -vt 0.113638 0.798366 -vt 0.115457 0.795059 -vt 0.224260 0.943862 -vt 0.224584 0.954019 -vt 0.222691 0.958934 -vt 0.221884 0.949396 -vt 0.219416 0.954039 -vt 0.218672 0.965670 -vt 0.217719 0.956207 -vt 0.217905 0.965935 -vt 0.174397 0.844762 -vt 0.176793 0.835931 -vt 0.174805 0.845175 -vt 0.175334 0.831656 -vt 0.175451 0.837804 -vt 0.173596 0.829457 -vt 0.632366 0.915153 -vt 0.631265 0.918493 -vt 0.630392 0.910221 -vt 0.633598 0.923442 -vt 0.636006 0.927531 -vt 0.113190 0.809899 -vt 0.177122 0.822951 -vt 0.175106 0.820603 -vt 0.174573 0.833003 -vt 0.630420 0.926747 -vt 0.629388 0.913716 -vt 0.632989 0.932137 -vt 0.635506 0.936959 -vt 0.637149 0.928840 -vt 0.636909 0.939381 -vt 0.497718 0.710714 -vt 0.498067 0.728706 -vt 0.497202 0.730881 -vt 0.224316 0.910348 -vt 0.224247 0.929293 -vt 0.221883 0.934395 -vt 0.221440 0.916166 -vt 0.219420 0.939264 -vt 0.218637 0.921805 -vt 0.217708 0.942230 -vt 0.631805 0.945371 -vt 0.634936 0.951499 -vt 0.097401 0.968620 -vt 0.102010 0.921936 -vt 0.097815 0.968859 -vt 0.096936 0.968041 -vt 0.096392 0.966848 -vt 0.101235 0.920779 -vt 0.791147 0.775337 -vt 0.789640 0.729420 -vt 0.792447 0.777726 -vt 0.788086 0.769821 -vt 0.787576 0.725628 -vt 0.786247 0.766550 -vt 0.784532 0.720017 -vt 0.280188 0.854990 -vt 0.283800 0.901976 -vt 0.279639 0.854494 -vt 0.280623 0.855155 -vt 0.285241 0.902501 -vt 0.281059 0.855123 -vt 0.281476 0.854891 -vt 0.281857 0.854473 -vt 0.237522 0.780116 -vt 0.239438 0.731444 -vt 0.240010 0.730356 -vt 0.244612 0.766683 -vt 0.244869 0.721356 -vt 0.246710 0.718014 -vt 0.247805 0.716056 -vt 0.244982 0.793299 -vt 0.248656 0.714557 -vt 0.103200 0.922068 -vt 0.099548 0.967876 -vt 0.099121 0.968393 -vt 0.098686 0.968739 -vt 0.098249 0.968897 -vt 0.105282 0.893525 -vt 0.103985 0.893076 -vt 0.787945 0.698433 -vt 0.785653 0.694139 -vt 0.064235 0.885347 -vt 0.065324 0.882679 -vt 0.065823 0.912623 -vt 0.286643 0.932277 -vt 0.287746 0.931784 -vt 0.286291 0.901203 -vt 0.237812 0.809710 -vt 0.239591 0.776209 -vt 0.240054 0.805389 -vt 0.241691 0.772232 -vt 0.242646 0.800211 -vt 0.246088 0.793004 -vt 0.246616 0.762861 -vt 0.105543 0.878385 -vt 0.789646 0.685276 -vt 0.789226 0.700728 -vt 0.787723 0.682021 -vt 0.783240 0.689466 -vt 0.784908 0.676463 -vt 0.065256 0.865240 -vt 0.287220 0.948958 -vt 0.237078 0.830070 -vt 0.236529 0.812073 -vt 0.238979 0.826757 -vt 0.241731 0.821160 -vt 0.244559 0.814842 -vt 0.246628 0.809661 -vt 0.104442 0.878219 -vt 0.104241 0.872052 -vt 0.789236 0.676746 -vt 0.786554 0.671622 -vt 0.783030 0.672485 -vt 0.784289 0.666547 -vt 0.063555 0.861041 -vt 0.064762 0.853726 -vt 0.284164 0.958664 -vt 0.285603 0.947130 -vt 0.286957 0.957231 -vt 0.288167 0.948724 -vt 0.239689 0.837297 -vt 0.242304 0.832151 -vt 0.245157 0.825371 -vt 0.247345 0.819092 -vt 0.105168 0.871494 -vt 0.106844 0.876995 -vt 0.283021 0.955400 -vt 0.284502 0.953511 -vt 0.100479 0.864368 -vt 0.102546 0.866271 -vt 0.103109 0.869021 -vt 0.283658 0.962269 -vt 0.282652 0.961250 -vt 0.508459 0.952013 -vt 0.510226 0.950892 -vt 0.511820 0.952678 -vt 0.502910 0.951234 -vt 0.503712 0.949857 -vt 0.281719 0.957482 -vt 0.279690 0.955650 -vt 0.280635 0.950890 -vt 0.280800 0.962078 -vt 0.282910 0.964529 -vt 0.281542 0.964884 -vt 0.953125 0.853618 -vt 0.954682 0.855247 -vt 0.954334 0.856044 -vt 0.103690 0.840302 -vt 0.105923 0.842484 -vt 0.105844 0.845925 -vt 0.103290 0.846887 -vt 0.105153 0.853350 -vt 0.102348 0.854457 -vt 0.103918 0.860402 -vt 0.101135 0.860887 -vt 0.102922 0.864165 -vt 0.952316 0.849808 -vt 0.624178 0.945253 -vt 0.623956 0.943544 -vt 0.624757 0.942681 -vt 0.278376 0.967017 -vt 0.276514 0.964716 -vt 0.278872 0.963510 -vt 0.091349 0.859105 -vt 0.097352 0.860974 -vt 0.096997 0.861973 -vt 0.623051 0.949810 -vt 0.623532 0.943600 -vt 0.623666 0.951613 -vt 0.623829 0.948271 -vt 0.624435 0.949705 -vt 0.272196 0.964415 -vt 0.269879 0.959909 -vt 0.273012 0.959053 -vt 0.091786 0.832401 -vt 0.095252 0.832883 -vt 0.095044 0.835486 -vt 0.264707 0.957519 -vt 0.269471 0.955212 -vt 0.265192 0.964422 -vt 0.269959 0.970702 -vt 0.265412 0.970710 -vt 0.272512 0.969236 -vt 0.087356 0.840147 -vt 0.089797 0.835669 -vt 0.090924 0.840932 -vt 0.084320 0.835842 -vt 0.086319 0.832442 -vt 0.083563 0.843186 -vt 0.086600 0.847823 -vt 0.623823 0.962744 -vt 0.623199 0.960798 -vt 0.623196 0.957043 -vt 0.624510 0.961110 -vt 0.623757 0.956615 -vt 0.624437 0.956771 -vt 0.509768 0.931183 -vt 0.510701 0.935534 -vt 0.509034 0.935396 -vt 0.506546 0.935150 -vt 0.503908 0.930638 -vt 0.503338 0.934799 -vt 0.499673 0.934365 -vt 0.260652 0.959585 -vt 0.265425 0.968648 -vt 0.261185 0.969482 -vt 0.951826 0.836464 -vt 0.952924 0.829907 -vt 0.951748 0.843069 -vt 0.081547 0.832891 -vt 0.080796 0.839329 -vt 0.083839 0.851193 -vt 0.080095 0.846862 -vt 0.511683 0.929124 -vt 0.511395 0.931258 -vt 0.509085 0.929046 -vt 0.502632 0.928444 -vt 0.499203 0.930090 -vt 0.258680 0.961176 -vt 0.259742 0.965647 -vt 0.952362 0.829210 -vt 0.951803 0.831638 -vt 0.078044 0.852004 -vt 0.077439 0.847030 -vt 0.079988 0.857975 -vt 0.074455 0.858207 -vt 0.077898 0.856917 -vt 0.077929 0.854809 -vt 0.074998 0.854130 -vt 0.953930 0.825814 -vt 0.953986 0.826788 -vt 0.953047 0.827294 -vt 0.073431 0.857795 -vt 0.073270 0.854843 -vt 0.624817 0.965897 -vt 0.626022 0.967651 -vt 0.624427 0.966056 -vt 0.619330 0.939704 -vt 0.616829 0.941224 -vt 0.619272 0.938764 -vt 0.255692 0.956890 -vt 0.256163 0.955896 -vt 0.256348 0.960581 -vt 0.071869 0.855944 -vt 0.072649 0.860082 -vt 0.071087 0.860741 -vt 0.614824 0.944892 -vt 0.613165 0.944742 -vt 0.614736 0.942836 -vt 0.253773 0.967775 -vt 0.255008 0.967178 -vt 0.255386 0.969857 -vt 0.719240 0.951627 -vt 0.721300 0.951874 -vt 0.720612 0.954284 -vt 0.716952 0.945321 -vt 0.719229 0.946730 -vt 0.715022 0.937969 -vt 0.717035 0.939559 -vt 0.714264 0.934076 -vt 0.715420 0.932685 -vt 0.619375 0.942224 -vt 0.616906 0.943090 -vt 0.172541 0.967843 -vt 0.173757 0.969231 -vt 0.171914 0.971742 -vt 0.715281 0.928153 -vt 0.716552 0.930561 -vt 0.070593 0.864217 -vt 0.069824 0.864189 -vt 0.615305 0.947819 -vt 0.613228 0.947842 -vt 0.252677 0.964616 -vt 0.253641 0.965120 -vt 0.070650 0.866098 -vt 0.071706 0.862665 -vt 0.613080 0.950878 -vt 0.612185 0.948609 -vt 0.621690 0.942814 -vt 0.622023 0.939570 -vt 0.173449 0.963056 -vt 0.176729 0.962968 -vt 0.170546 0.966262 -vt 0.170399 0.970613 -vt 0.251425 0.965749 -vt 0.251209 0.963217 -vt 0.252217 0.967037 -vt 0.723561 0.947561 -vt 0.725861 0.945670 -vt 0.723764 0.950749 -vt 0.721675 0.942073 -vt 0.724359 0.944052 -vt 0.719501 0.934876 -vt 0.722408 0.937833 -vt 0.717737 0.928283 -vt 0.720269 0.930618 -vt 0.717376 0.924005 -vt 0.718993 0.922412 -vt 0.069768 0.869612 -vt 0.612550 0.954241 -vt 0.611991 0.952933 -vt 0.619275 0.947182 -vt 0.617483 0.945724 -vt 0.169655 0.965237 -vt 0.169699 0.969477 -vt 0.719667 0.924759 -vt 0.721532 0.928086 -vt 0.717393 0.922756 -vt 0.719611 0.918628 -vt 0.622588 0.944481 -vt 0.175691 0.958337 -vt 0.176973 0.959238 -vt 0.723683 0.935272 -vt 0.725916 0.936199 -vt 0.725567 0.941621 -vt 0.723834 0.929053 -vt 0.721760 0.922470 -vt 0.721179 0.917964 -vt 0.058568 0.953172 -vt 0.057216 0.953884 -vt 0.058733 0.952431 -vt 0.071660 0.869249 -vt 0.070581 0.869212 -vt 0.071208 0.867594 -vt 0.616702 0.952408 -vt 0.615769 0.950784 -vt 0.621566 0.947314 -vt 0.169794 0.961399 -vt 0.170526 0.962654 -vt 0.252702 0.957278 -vt 0.252290 0.961225 -vt 0.251862 0.963259 -vt 0.251192 0.959632 -vt 0.722870 0.920124 -vt 0.071372 0.871883 -vt 0.728559 0.937090 -vt 0.729373 0.937642 -vt 0.727995 0.941332 -vt 0.721482 0.914744 -vt 0.722595 0.914954 -vt 0.073229 0.871104 -vt 0.072479 0.868214 -vt 0.615501 0.955540 -vt 0.613792 0.955752 -vt 0.620447 0.950550 -vt 0.174556 0.955192 -vt 0.176978 0.955103 -vt 0.174636 0.957444 -vt 0.171495 0.959819 -vt 0.170429 0.958290 -vt 0.728107 0.931521 -vt 0.729742 0.932719 -vt 0.726081 0.924249 -vt 0.728385 0.926700 -vt 0.723887 0.917912 -vt 0.726482 0.920354 -vt 0.724268 0.914677 -vt 0.723410 0.911637 -vt 0.073463 0.877641 -vt 0.070980 0.874165 -vt 0.074563 0.867406 -vt 0.073682 0.866268 -vt 0.251173 0.954768 -vt 0.253384 0.953060 -vt 0.251971 0.956354 -vt 0.250448 0.950182 -vt 0.252454 0.950339 -vt 0.729461 0.925686 -vt 0.725047 0.911211 -vt 0.076339 0.860843 -vt 0.075355 0.859967 -vt 0.910202 0.816073 -vt 0.911364 0.818412 -vt 0.911789 0.819744 -vt 0.255271 0.951984 -vt 0.255222 0.952906 -vt 0.726098 0.913256 -vt 0.728079 0.916605 -vt 0.727330 0.918397 -vt 0.055087 0.960372 -vt 0.054849 0.959042 -vt 0.056088 0.956408 -vt 0.074591 0.876085 -vt 0.073378 0.876189 -vt 0.072827 0.873871 -vt 0.726362 0.909624 -vt 0.724747 0.908478 -vt 0.077934 0.862385 -vt 0.076040 0.869237 -vt 0.258487 0.950158 -vt 0.055031 0.964487 -vt 0.054603 0.962158 -vt 0.076576 0.879491 -vt 0.254233 0.944508 -vt 0.256771 0.944419 -vt 0.254794 0.947476 -vt 0.254329 0.923073 -vt 0.254948 0.924130 -vt 0.253399 0.925313 -vt 0.079072 0.871235 -vt 0.077299 0.875378 -vt 0.077441 0.870378 -vt 0.587040 0.949866 -vt 0.588513 0.950717 -vt 0.586622 0.952367 -vt 0.909669 0.812149 -vt 0.910229 0.810734 -vt 0.910027 0.813421 -vt 0.255856 0.937267 -vt 0.258707 0.939704 -vt 0.255174 0.929699 -vt 0.257279 0.932320 -vt 0.256761 0.925555 -vt 0.081161 0.882105 -vt 0.076430 0.880161 -vt 0.079693 0.880136 -vt 0.077356 0.877925 -vt 0.079922 0.876581 -vt 0.081578 0.862029 -vt 0.079712 0.865537 -vt 0.079649 0.861554 -vt 0.258849 0.947387 -vt 0.262497 0.946760 -vt 0.258450 0.949464 -vt 0.257422 0.922123 -vt 0.259057 0.924602 -vt 0.082528 0.878501 -vt 0.081795 0.873258 -vt 0.082224 0.866483 -vt 0.259297 0.944467 -vt 0.263370 0.943655 -vt 0.260034 0.932458 -vt 0.261556 0.935397 -vt 0.087388 0.865470 -vt 0.582172 0.943631 -vt 0.586306 0.948114 -vt 0.583842 0.951653 -vt 0.580402 0.945264 -vt 0.582420 0.953064 -vt 0.579591 0.947811 -vt 0.581854 0.953744 -vt 0.910960 0.806325 -vt 0.912111 0.804923 -vt 0.259813 0.928582 -vt 0.265681 0.926642 -vt 0.056856 0.970529 -vt 0.056432 0.970947 -vt 0.054433 0.965122 -vt 0.086951 0.872045 -vt 0.266027 0.945326 -vt 0.267746 0.932569 -vt 0.264894 0.922144 -vt 0.578982 0.945037 -vt 0.268609 0.944687 -vt 0.266098 0.946550 -vt 0.268536 0.936425 -vt 0.262390 0.939867 -vt 0.057467 0.973145 -vt 0.089276 0.873179 -vt 0.087775 0.877713 -vt 0.583820 0.938220 -vt 0.584486 0.941588 -vt 0.579899 0.941455 -vt 0.913589 0.799965 -vt 0.913588 0.802222 -vt 0.912939 0.802061 -vt 0.270041 0.939341 -vt 0.272655 0.938943 -vt 0.267855 0.926773 -vt 0.267131 0.920128 -vt 0.264542 0.919932 -vt 0.057830 0.974883 -vt 0.056828 0.973317 -vt 0.058766 0.976487 -vt 0.058337 0.973846 -vt 0.090339 0.880898 -vt 0.088661 0.883165 -vt 0.269606 0.929012 -vt 0.268664 0.921673 -vt 0.266224 0.917402 -vt 0.268208 0.916175 -vt 0.092305 0.896123 -vt 0.088730 0.888194 -vt 0.090267 0.888120 -vt 0.091653 0.885779 -vt 0.343049 0.950534 -vt 0.343759 0.948820 -vt 0.343823 0.951892 -vt 0.271699 0.935696 -vt 0.091568 0.896123 -vt 0.089815 0.890429 -vt 0.092621 0.890992 -vt 0.094263 0.885561 -vt 0.093065 0.879317 -vt 0.095522 0.879002 -vt 0.093952 0.873877 -vt 0.345878 0.940978 -vt 0.347476 0.942845 -vt 0.345576 0.946682 -vt 0.343674 0.944567 -vt 0.273252 0.931831 -vt 0.271109 0.932920 -vt 0.270630 0.927211 -vt 0.271796 0.926797 -vt 0.269626 0.919985 -vt 0.270580 0.919782 -vt 0.430818 0.900158 -vt 0.430022 0.901161 -vt 0.429165 0.896964 -vt 0.432286 0.902071 -vt 0.432210 0.904697 -vt 0.433098 0.902388 -vt 0.432891 0.908077 -vt 0.431980 0.908081 -vt 0.093755 0.900949 -vt 0.094606 0.893725 -vt 0.911872 0.756524 -vt 0.913039 0.760866 -vt 0.911897 0.764727 -vt 0.345851 0.933493 -vt 0.347725 0.936605 -vt 0.343581 0.938188 -vt 0.342388 0.939892 -vt 0.342671 0.945150 -vt 0.274879 0.929376 -vt 0.275603 0.930016 -vt 0.273233 0.925386 -vt 0.427602 0.895165 -vt 0.425658 0.892937 -vt 0.426251 0.890889 -vt 0.427832 0.899354 -vt 0.430244 0.905004 -vt 0.431915 0.914188 -vt 0.092758 0.902950 -vt 0.912293 0.744700 -vt 0.913129 0.752190 -vt 0.276567 0.921484 -vt 0.276172 0.929663 -vt 0.275797 0.921896 -vt 0.274494 0.920877 -vt 0.425699 0.900462 -vt 0.427997 0.906170 -vt 0.430349 0.911307 -vt 0.091556 0.913852 -vt 0.091827 0.902447 -vt 0.093614 0.914088 -vt 0.346092 0.907358 -vt 0.346678 0.920791 -vt 0.345191 0.924287 -vt 0.344304 0.911157 -vt 0.342934 0.929077 -vt 0.342166 0.915478 -vt 0.341730 0.931214 -vt 0.275197 0.908357 -vt 0.273668 0.907295 -vt 0.424849 0.910667 -vt 0.424573 0.897335 -vt 0.426368 0.914558 -vt 0.428979 0.920132 -vt 0.431105 0.924393 -vt 0.090130 0.933760 -vt 0.091063 0.912851 -vt 0.092502 0.914521 -vt 0.091501 0.934637 -vt 0.092335 0.934167 -vt 0.095049 0.911974 -vt 0.345303 0.887616 -vt 0.343020 0.892103 -vt 0.274736 0.885833 -vt 0.276702 0.906692 -vt 0.276195 0.907686 -vt 0.273438 0.886732 -vt 0.272731 0.905755 -vt 0.271765 0.885323 -vt 0.428792 0.939313 -vt 0.431005 0.943630 -vt 0.913006 0.677803 -vt 0.912155 0.709971 -vt 0.912324 0.721909 -vt 0.912143 0.679389 -vt 0.910461 0.713450 -vt 0.428052 0.967418 -vt 0.426825 0.935445 -vt 0.430925 0.972715 -vt 0.432021 0.945593 -vt 0.433257 0.977123 -vt 0.645752 0.775688 -vt 0.645926 0.806954 -vt 0.645145 0.775560 -vt 0.646521 0.776137 -vt 0.959506 0.106785 -vt 0.963664 0.137842 -vt 0.958696 0.105951 -vt 0.963574 0.111007 -vt 0.965386 0.140353 -vt 0.960860 0.108186 -vt 0.964534 0.112010 -vt 0.968725 0.145285 -vt 0.646509 0.774901 -vt 0.646497 0.739622 -vt 0.647049 0.775558 -vt 0.645756 0.774347 -vt 0.645174 0.774139 -vt 0.645375 0.738514 -vt 0.644151 0.738624 -vt 0.644737 0.774107 -vt 0.644303 0.774170 -vt 0.643736 0.774418 -vt 0.643013 0.775027 -vt 0.966080 0.783939 -vt 0.957752 0.817292 -vt 0.957166 0.817774 -vt 0.955765 0.818928 -vt 0.960013 0.790982 -vt 0.952712 0.821446 -vt 0.951280 0.822631 -vt 0.643406 0.807736 -vt 0.643045 0.776741 -vt 0.643744 0.776037 -vt 0.647048 0.808189 -vt 0.646893 0.831237 -vt 0.967223 0.164551 -vt 0.970378 0.170521 -vt 0.961885 0.939882 -vt 0.955389 0.913604 -vt 0.962652 0.940709 -vt 0.645634 0.711158 -vt 0.643077 0.712497 -vt 0.642807 0.739994 -vt 0.969373 0.760402 -vt 0.963775 0.786598 -vt 0.964301 0.767833 -vt 0.958588 0.792660 -vt 0.644364 0.829896 -vt 0.644482 0.806700 -vt 0.646618 0.846920 -vt 0.645749 0.829847 -vt 0.965690 0.178891 -vt 0.965264 0.160902 -vt 0.971859 0.173353 -vt 0.971068 0.191263 -vt 0.646642 0.692472 -vt 0.646629 0.712453 -vt 0.643597 0.691985 -vt 0.644377 0.711006 -vt 0.972425 0.739849 -vt 0.972028 0.756574 -vt 0.969106 0.745581 -vt 0.965074 0.771259 -vt 0.966943 0.749359 -vt 0.964855 0.753050 -vt 0.963102 0.769615 -vt 0.643616 0.847362 -vt 0.643360 0.831190 -vt 0.645861 0.856388 -vt 0.645634 0.845870 -vt 0.963704 0.189202 -vt 0.964615 0.176488 -vt 0.968176 0.183288 -vt 0.965680 0.194508 -vt 0.644201 0.678202 -vt 0.644651 0.690894 -vt 0.972909 0.725073 -vt 0.974838 0.735744 -vt 0.970210 0.728647 -vt 0.968099 0.734634 -vt 0.965465 0.739959 -vt 0.644517 0.856665 -vt 0.644625 0.845942 -vt 0.645297 0.861710 -vt 0.960549 0.194011 -vt 0.962865 0.187010 -vt 0.961499 0.196963 -vt 0.969324 0.204506 -vt 0.965767 0.210604 -vt 0.966317 0.201141 -vt 0.951145 0.878831 -vt 0.953481 0.871282 -vt 0.952636 0.881563 -vt 0.950389 0.877418 -vt 0.952387 0.869007 -vt 0.645020 0.677559 -vt 0.645616 0.670268 -vt 0.645813 0.677773 -vt 0.644855 0.670479 -vt 0.972374 0.713334 -vt 0.974994 0.721024 -vt 0.970068 0.718452 -vt 0.965525 0.728721 -vt 0.963095 0.734309 -vt 0.963945 0.743104 -vt 0.957166 0.195922 -vt 0.960267 0.193161 -vt 0.957852 0.198525 -vt 0.960740 0.209427 -vt 0.962267 0.201917 -vt 0.966929 0.214389 -vt 0.962696 0.216791 -vt 0.793882 0.660815 -vt 0.792829 0.665609 -vt 0.793244 0.660764 -vt 0.793592 0.665662 -vt 0.794653 0.660905 -vt 0.969152 0.708684 -vt 0.973060 0.711848 -vt 0.967384 0.713197 -vt 0.965085 0.719060 -vt 0.955276 0.204038 -vt 0.956971 0.211583 -vt 0.962039 0.868006 -vt 0.957551 0.868659 -vt 0.956571 0.866467 -vt 0.960870 0.865359 -vt 0.955919 0.865012 -vt 0.961881 0.717385 -vt 0.950750 0.201270 -vt 0.950596 0.197188 -vt 0.959017 0.725543 -vt 0.956568 0.724491 -vt 0.956865 0.731366 -vt 0.954532 0.730297 -vt 0.955936 0.733702 -vt 0.952636 0.734878 -vt 0.159274 0.914502 -vt 0.156535 0.917758 -vt 0.158726 0.914225 -vt 0.945833 0.193886 -vt 0.946659 0.200381 -vt 0.952156 0.208606 -vt 0.948190 0.207616 -vt 0.953277 0.213558 -vt 0.210194 0.917969 -vt 0.211810 0.913612 -vt 0.213329 0.912847 -vt 0.960187 0.712694 -vt 0.956562 0.713644 -vt 0.960386 0.710416 -vt 0.958497 0.718268 -vt 0.956225 0.717471 -vt 0.954659 0.723848 -vt 0.952309 0.730877 -vt 0.950580 0.735061 -vt 0.156121 0.916854 -vt 0.158294 0.912598 -vt 0.150995 0.920816 -vt 0.525593 0.943928 -vt 0.526590 0.947259 -vt 0.526202 0.947443 -vt 0.942521 0.186346 -vt 0.946029 0.189640 -vt 0.945779 0.190819 -vt 0.942109 0.188431 -vt 0.942419 0.194117 -vt 0.970892 0.097410 -vt 0.974300 0.098110 -vt 0.975269 0.103511 -vt 0.207662 0.923975 -vt 0.207065 0.920088 -vt 0.210597 0.921847 -vt 0.212603 0.916807 -vt 0.212232 0.922925 -vt 0.213560 0.916821 -vt 0.971491 0.269537 -vt 0.969281 0.271384 -vt 0.971277 0.269162 -vt 0.954004 0.718330 -vt 0.953050 0.723963 -vt 0.157420 0.908273 -vt 0.155699 0.908359 -vt 0.159077 0.903026 -vt 0.156186 0.914229 -vt 0.153769 0.915029 -vt 0.153053 0.918551 -vt 0.212822 0.924963 -vt 0.212597 0.920968 -vt 0.158296 0.901315 -vt 0.156646 0.899489 -vt 0.160719 0.896222 -vt 0.154147 0.905756 -vt 0.151606 0.912944 -vt 0.149738 0.919010 -vt 0.959140 0.083168 -vt 0.963076 0.082281 -vt 0.964562 0.087485 -vt 0.960690 0.089663 -vt 0.966271 0.094806 -vt 0.962282 0.096675 -vt 0.967179 0.099212 -vt 0.211323 0.931361 -vt 0.207873 0.928902 -vt 0.210819 0.926376 -vt 0.950101 0.078305 -vt 0.953988 0.078967 -vt 0.954096 0.079646 -vt 0.950254 0.080367 -vt 0.954632 0.082368 -vt 0.951226 0.086156 -vt 0.956032 0.088838 -vt 0.952824 0.093612 -vt 0.957637 0.095857 -vt 0.213551 0.940801 -vt 0.209561 0.939049 -vt 0.212989 0.935566 -vt 0.215975 0.938084 -vt 0.215035 0.933421 -vt 0.216926 0.936832 -vt 0.215550 0.932842 -vt 0.530967 0.930032 -vt 0.533403 0.928432 -vt 0.531477 0.930573 -vt 0.530419 0.929495 -vt 0.532979 0.927713 -vt 0.967619 0.288921 -vt 0.966305 0.285640 -vt 0.967058 0.285516 -vt 0.146588 0.897683 -vt 0.143241 0.897972 -vt 0.146681 0.897005 -vt 0.145925 0.900260 -vt 0.143111 0.899797 -vt 0.143932 0.906476 -vt 0.141804 0.905262 -vt 0.141319 0.913674 -vt 0.139445 0.912414 -vt 0.138830 0.919828 -vt 0.136790 0.918998 -vt 0.137542 0.922594 -vt 0.135132 0.922382 -vt 0.562108 0.946769 -vt 0.560331 0.947797 -vt 0.561681 0.944977 -vt 0.569874 0.932076 -vt 0.569008 0.929128 -vt 0.570639 0.928248 -vt 0.536157 0.925818 -vt 0.537330 0.926039 -vt 0.535211 0.926601 -vt 0.941812 0.073873 -vt 0.943371 0.074324 -vt 0.943795 0.076988 -vt 0.941417 0.076191 -vt 0.943945 0.082305 -vt 0.600733 0.935087 -vt 0.598765 0.933251 -vt 0.603434 0.927630 -vt 0.563433 0.942419 -vt 0.562608 0.938699 -vt 0.565737 0.935528 -vt 0.567833 0.937951 -vt 0.139776 0.906285 -vt 0.138755 0.904630 -vt 0.140611 0.901101 -vt 0.560782 0.935248 -vt 0.559839 0.932873 -vt 0.562033 0.928028 -vt 0.560158 0.942425 -vt 0.558956 0.940224 -vt 0.558494 0.946912 -vt 0.558485 0.950195 -vt 0.556430 0.952174 -vt 0.558651 0.951398 -vt 0.941415 0.069407 -vt 0.942925 0.073116 -vt 0.940591 0.069153 -vt 0.940271 0.071650 -vt 0.599459 0.941181 -vt 0.596840 0.939824 -vt 0.604026 0.936315 -vt 0.604455 0.930989 -vt 0.566711 0.943810 -vt 0.570444 0.939829 -vt 0.570303 0.935742 -vt 0.571342 0.935048 -vt 0.136403 0.903896 -vt 0.137662 0.901384 -vt 0.139467 0.900320 -vt 0.138673 0.902321 -vt 0.136711 0.905280 -vt 0.137511 0.905056 -vt 0.558446 0.932552 -vt 0.560917 0.926830 -vt 0.557658 0.939648 -vt 0.556924 0.946787 -vt 0.555090 0.954693 -vt 0.554226 0.954660 -vt 0.939849 0.068466 -vt 0.597467 0.947656 -vt 0.596123 0.944825 -vt 0.601789 0.943443 -vt 0.564436 0.951162 -vt 0.563100 0.947740 -vt 0.568871 0.945968 -vt 0.556942 0.931181 -vt 0.559050 0.927659 -vt 0.556540 0.937223 -vt 0.555769 0.944553 -vt 0.554833 0.951311 -vt 0.552301 0.956118 -vt 0.942303 0.067058 -vt 0.942010 0.068965 -vt 0.941546 0.064469 -vt 0.771829 0.967891 -vt 0.773710 0.969053 -vt 0.773476 0.969364 -vt 0.135094 0.909493 -vt 0.134551 0.907213 -vt 0.135566 0.906600 -vt 0.205513 0.921383 -vt 0.203843 0.922323 -vt 0.205296 0.921062 -vt 0.555143 0.931587 -vt 0.556935 0.928970 -vt 0.555237 0.936624 -vt 0.554629 0.943768 -vt 0.553520 0.950998 -vt 0.943125 0.065153 -vt 0.596474 0.955304 -vt 0.595867 0.952532 -vt 0.597528 0.951518 -vt 0.600804 0.952260 -vt 0.601869 0.947693 -vt 0.604642 0.948579 -vt 0.605100 0.944417 -vt 0.567980 0.954942 -vt 0.564614 0.955421 -vt 0.569036 0.949889 -vt 0.135086 0.912816 -vt 0.132177 0.913264 -vt 0.137071 0.912195 -vt 0.201879 0.926037 -vt 0.201749 0.924650 -vt 0.202675 0.923937 -vt 0.945094 0.061827 -vt 0.948091 0.062441 -vt 0.945145 0.064741 -vt 0.950871 0.066108 -vt 0.948521 0.067606 -vt 0.769459 0.960517 -vt 0.770263 0.954181 -vt 0.770255 0.962544 -vt 0.769024 0.957851 -vt 0.768695 0.960975 -vt 0.949470 0.057005 -vt 0.952912 0.055950 -vt 0.947727 0.059121 -vt 0.201964 0.935778 -vt 0.200992 0.931849 -vt 0.201237 0.929871 -vt 0.201652 0.929507 -vt 0.953666 0.059491 -vt 0.957046 0.063184 -vt 0.953740 0.064640 -vt 0.196288 0.948833 -vt 0.192230 0.938023 -vt 0.195046 0.936814 -vt 0.154853 0.929201 -vt 0.144055 0.923619 -vt 0.145574 0.921096 -vt 0.157621 0.927295 -vt 0.142503 0.917511 -vt 0.203371 0.938168 -vt 0.204535 0.945032 -vt 0.202576 0.935347 -vt 0.958570 0.079811 -vt 0.970042 0.076813 -vt 0.959023 0.081858 -vt 0.958085 0.076759 -vt 0.967258 0.074639 -vt 0.953078 0.057124 -vt 0.964458 0.053905 -vt 0.963778 0.051450 -vt 0.961632 0.051535 -vt 0.193492 0.949987 -vt 0.190162 0.938783 -vt 0.204165 0.947360 -vt 0.199906 0.948650 -vt 0.199654 0.945867 -vt 0.971043 0.077353 -vt 0.968120 0.078748 -vt 0.967943 0.066581 -vt 0.970163 0.073072 -vt 0.967567 0.049849 -vt 0.966861 0.048574 -vt 0.772505 0.942151 -vt 0.773095 0.939121 -vt 0.772850 0.942708 -vt 0.194928 0.952228 -vt 0.154821 0.936007 -vt 0.151805 0.936109 -vt 0.153206 0.932836 -vt 0.158570 0.930331 -vt 0.156031 0.927043 -vt 0.204525 0.950672 -vt 0.203721 0.949175 -vt 0.969141 0.051410 -vt 0.967467 0.052984 -vt 0.225328 0.919432 -vt 0.224862 0.918419 -vt 0.225444 0.918235 -vt 0.226245 0.918695 -vt 0.226608 0.917232 -vt 0.228115 0.916105 -vt 0.228081 0.915300 -vt 0.198932 0.952441 -vt 0.194703 0.953438 -vt 0.199668 0.951758 -vt 0.155284 0.939135 -vt 0.152798 0.943383 -vt 0.155678 0.936988 -vt 0.157513 0.935063 -vt 0.158478 0.932898 -vt 0.160210 0.932575 -vt 0.160253 0.930462 -vt 0.203606 0.954021 -vt 0.203286 0.953294 -vt 0.204268 0.952568 -vt 0.790203 0.940567 -vt 0.791570 0.939440 -vt 0.791335 0.941618 -vt 0.970135 0.048220 -vt 0.971293 0.048651 -vt 0.971936 0.053647 -vt 0.225794 0.921181 -vt 0.225072 0.920129 -vt 0.493960 0.954973 -vt 0.495631 0.952053 -vt 0.494523 0.955968 -vt 0.160909 0.933881 -vt 0.161075 0.931221 -vt 0.792962 0.937973 -vt 0.792630 0.940182 -vt 0.788863 0.935306 -vt 0.791707 0.936220 -vt 0.785838 0.928883 -vt 0.788918 0.930989 -vt 0.783309 0.921997 -vt 0.785955 0.924324 -vt 0.969169 0.047015 -vt 0.969766 0.042851 -vt 0.968033 0.042410 -vt 0.968447 0.045660 -vt 0.967915 0.045973 -vt 0.495580 0.949127 -vt 0.497618 0.946283 -vt 0.793367 0.934996 -vt 0.791159 0.930878 -vt 0.788201 0.924607 -vt 0.968580 0.040485 -vt 0.967477 0.042393 -vt 0.967749 0.044174 -vt 0.226666 0.923712 -vt 0.226622 0.920025 -vt 0.228968 0.919526 -vt 0.227821 0.918085 -vt 0.229299 0.915456 -vt 0.492383 0.950546 -vt 0.493413 0.957136 -vt 0.161161 0.935597 -vt 0.160056 0.937807 -vt 0.793082 0.929908 -vt 0.790377 0.924391 -vt 0.786222 0.918996 -vt 0.788175 0.919071 -vt 0.783556 0.911668 -vt 0.784649 0.904060 -vt 0.783841 0.914241 -vt 0.967200 0.038329 -vt 0.227362 0.926794 -vt 0.229861 0.924127 -vt 0.231839 0.914458 -vt 0.494377 0.936180 -vt 0.157635 0.941739 -vt 0.157296 0.940259 -vt 0.159222 0.937431 -vt 0.791114 0.918116 -vt 0.785673 0.905217 -vt 0.784867 0.916508 -vt 0.966793 0.031321 -vt 0.969200 0.038917 -vt 0.964165 0.029200 -vt 0.966482 0.039625 -vt 0.228497 0.934695 -vt 0.497110 0.940321 -vt 0.492111 0.939846 -vt 0.492786 0.943514 -vt 0.154704 0.949265 -vt 0.792782 0.911708 -vt 0.793576 0.924648 -vt 0.229888 0.961296 -vt 0.493630 0.923963 -vt 0.794320 0.913762 -vt 0.794920 0.904406 -vt 0.794925 0.918115 -vt 0.787397 0.897439 -vt 0.789208 0.873863 -vt 0.785417 0.886571 -vt 0.963864 0.019199 -vt 0.960021 0.000995 -vt 0.965043 0.020343 -vt 0.225424 0.968736 -vt 0.226730 0.938662 -vt 0.227339 0.937275 -vt 0.226385 0.967134 -vt 0.227681 0.964952 -vt 0.495586 0.901377 -vt 0.141218 0.980629 -vt 0.147937 0.962622 -vt 0.150179 0.961369 -vt 0.794417 0.883561 -vt 0.793499 0.881905 -vt 0.791775 0.878756 -vt 0.786749 0.869461 -vt 0.340553 0.933619 -vt 0.321623 0.918939 -vt 0.340949 0.933370 -vt 0.340022 0.933746 -vt 0.337746 0.933439 -vt 0.318795 0.918521 -vt 0.339140 0.933725 -vt 0.776965 0.910122 -vt 0.774910 0.933564 -vt 0.775710 0.906884 -vt 0.779521 0.916641 -vt 0.777001 0.938596 -vt 0.780652 0.919489 -vt 0.779014 0.942770 -vt 0.351103 0.892657 -vt 0.350380 0.915265 -vt 0.350659 0.891876 -vt 0.351894 0.893290 -vt 0.351561 0.915256 -vt 0.351485 0.893060 -vt 0.352329 0.893332 -vt 0.352772 0.893182 -vt 0.353150 0.913291 -vt 0.353204 0.892849 -vt 0.353620 0.892348 -vt 0.354140 0.891428 -vt 0.643478 0.926348 -vt 0.644067 0.946369 -vt 0.642725 0.949393 -vt 0.641235 0.931235 -vt 0.639870 0.955756 -vt 0.638947 0.935614 -vt 0.638487 0.958807 -vt 0.322920 0.917291 -vt 0.340609 0.930672 -vt 0.341132 0.931822 -vt 0.322759 0.918501 -vt 0.341268 0.932543 -vt 0.341189 0.933004 -vt 0.305796 0.906359 -vt 0.348577 0.694069 -vt 0.348822 0.713112 -vt 0.347958 0.692475 -vt 0.776303 0.957113 -vt 0.778384 0.960518 -vt 0.779860 0.944249 -vt 0.779543 0.961476 -vt 0.351359 0.931338 -vt 0.639185 0.922250 -vt 0.637667 0.937715 -vt 0.310962 0.907521 -vt 0.308856 0.907316 -vt 0.304058 0.899627 -vt 0.299191 0.897056 -vt 0.302121 0.904814 -vt 0.293157 0.894072 -vt 0.776466 0.970502 -vt 0.774380 0.952471 -vt 0.778585 0.973688 -vt 0.779709 0.974045 -vt 0.353129 0.943140 -vt 0.350593 0.931756 -vt 0.354178 0.939536 -vt 0.352557 0.929599 -vt 0.353721 0.926647 -vt 0.355503 0.932909 -vt 0.355093 0.921514 -vt 0.643467 0.910102 -vt 0.643897 0.913923 -vt 0.641620 0.918810 -vt 0.641533 0.913455 -vt 0.638055 0.922996 -vt 0.639171 0.914586 -vt 0.293742 0.888904 -vt 0.447339 0.950093 -vt 0.441703 0.944215 -vt 0.451648 0.950677 -vt 0.444931 0.949675 -vt 0.438411 0.943656 -vt 0.780230 0.981723 -vt 0.780910 0.981093 -vt 0.780231 0.973589 -vt 0.356730 0.949306 -vt 0.357857 0.939073 -vt 0.358681 0.932521 -vt 0.356564 0.926568 -vt 0.359279 0.926843 -vt 0.357113 0.921957 -vt 0.641357 0.910303 -vt 0.640128 0.914793 -vt 0.640572 0.910416 -vt 0.301913 0.894054 -vt 0.305680 0.900262 -vt 0.300868 0.888325 -vt 0.295260 0.884443 -vt 0.442719 0.938832 -vt 0.445782 0.944891 -vt 0.436941 0.938281 -vt 0.437200 0.943436 -vt 0.435212 0.937947 -vt 0.829182 0.963060 -vt 0.827331 0.958842 -vt 0.829717 0.962784 -vt 0.826698 0.959163 -vt 0.828593 0.963290 -vt 0.361156 0.954148 -vt 0.356423 0.951630 -vt 0.361580 0.948543 -vt 0.362700 0.933853 -vt 0.363130 0.928270 -vt 0.359440 0.925153 -vt 0.664100 0.909995 -vt 0.665574 0.906663 -vt 0.666242 0.907012 -vt 0.666819 0.907224 -vt 0.664738 0.910307 -vt 0.303346 0.890050 -vt 0.303796 0.895217 -vt 0.301401 0.882529 -vt 0.289049 0.886056 -vt 0.296654 0.876668 -vt 0.438475 0.930894 -vt 0.439640 0.938161 -vt 0.435759 0.932451 -vt 0.434608 0.937834 -vt 0.434586 0.932191 -vt 0.824561 0.954210 -vt 0.366495 0.957142 -vt 0.361045 0.955627 -vt 0.366811 0.952116 -vt 0.369237 0.946557 -vt 0.363744 0.942893 -vt 0.375636 0.941328 -vt 0.305779 0.885660 -vt 0.304007 0.890514 -vt 0.304523 0.884763 -vt 0.445001 0.920727 -vt 0.437316 0.926696 -vt 0.372801 0.952018 -vt 0.370034 0.931612 -vt 0.378120 0.934114 -vt 0.368452 0.934961 -vt 0.308825 0.870482 -vt 0.305083 0.878166 -vt 0.304368 0.867383 -vt 0.288678 0.879214 -vt 0.442545 0.918770 -vt 0.439272 0.920755 -vt 0.436374 0.926464 -vt 0.819479 0.942987 -vt 0.822549 0.948499 -vt 0.821909 0.948829 -vt 0.821322 0.949086 -vt 0.818850 0.943275 -vt 0.372602 0.955728 -vt 0.378880 0.954893 -vt 0.372506 0.957235 -vt 0.381979 0.944737 -vt 0.382901 0.930825 -vt 0.373767 0.929860 -vt 0.379827 0.929609 -vt 0.659425 0.919434 -vt 0.656782 0.924914 -vt 0.658825 0.919109 -vt 0.313347 0.876765 -vt 0.309285 0.881210 -vt 0.308063 0.880333 -vt 0.310917 0.874988 -vt 0.442791 0.915241 -vt 0.381769 0.949985 -vt 0.388219 0.949825 -vt 0.379838 0.928652 -vt 0.386148 0.927254 -vt 0.317303 0.860644 -vt 0.314899 0.857618 -vt 0.301471 0.863980 -vt 0.452433 0.906264 -vt 0.814759 0.931585 -vt 0.817403 0.936953 -vt 0.816797 0.937262 -vt 0.816170 0.937543 -vt 0.813858 0.931827 -vt 0.387796 0.952175 -vt 0.391020 0.951837 -vt 0.385064 0.954478 -vt 0.391392 0.943110 -vt 0.396249 0.935108 -vt 0.390101 0.934416 -vt 0.397021 0.924859 -vt 0.324612 0.870573 -vt 0.319261 0.873662 -vt 0.320660 0.871691 -vt 0.322893 0.863018 -vt 0.317171 0.868083 -vt 0.453691 0.902454 -vt 0.446787 0.910215 -vt 0.813414 0.932181 -vt 0.811458 0.927117 -vt 0.396557 0.946758 -vt 0.396827 0.940426 -vt 0.395513 0.922676 -vt 0.649239 0.945086 -vt 0.652168 0.936038 -vt 0.652912 0.936345 -vt 0.327961 0.866786 -vt 0.319263 0.870152 -vt 0.321306 0.856494 -vt 0.457997 0.903411 -vt 0.455898 0.907603 -vt 0.454876 0.902414 -vt 0.396910 0.929973 -vt 0.401973 0.930261 -vt 0.401973 0.923464 -vt 0.401843 0.920189 -vt 0.649339 0.948349 -vt 0.651167 0.941370 -vt 0.809986 0.918639 -vt 0.811148 0.922771 -vt 0.810443 0.922509 -vt 0.809141 0.918915 -vt 0.336017 0.863030 -vt 0.336927 0.864933 -vt 0.332536 0.864797 -vt 0.809706 0.915371 -vt 0.650046 0.954654 -vt 0.650325 0.956713 -vt 0.648752 0.953534 -vt 0.626259 0.918558 -vt 0.623458 0.918837 -vt 0.626283 0.912124 -vt 0.626757 0.926169 -vt 0.624008 0.926259 -vt 0.753509 0.916152 -vt 0.756447 0.916356 -vt 0.753125 0.920428 -vt 0.753578 0.912883 -vt 0.756867 0.912437 -vt 0.753374 0.911712 -vt 0.756973 0.910654 -vt 0.811065 0.912525 -vt 0.813899 0.910339 -vt 0.811655 0.912917 -vt 0.813415 0.909737 -vt 0.810528 0.912130 -vt 0.652787 0.959502 -vt 0.649742 0.957099 -vt 0.653285 0.958857 -vt 0.623251 0.913355 -vt 0.626573 0.909632 -vt 0.479134 0.949154 -vt 0.474762 0.947652 -vt 0.474461 0.945328 -vt 0.478950 0.943817 -vt 0.473998 0.939095 -vt 0.478664 0.936427 -vt 0.473671 0.931553 -vt 0.478362 0.929154 -vt 0.473558 0.924684 -vt 0.478114 0.923761 -vt 0.473622 0.921294 -vt 0.473742 0.920037 -vt 0.762413 0.908305 -vt 0.764960 0.909191 -vt 0.760875 0.910564 -vt 0.769022 0.907894 -vt 0.612331 0.924465 -vt 0.614242 0.924334 -vt 0.614789 0.931733 -vt 0.612620 0.931913 -vt 0.614919 0.936041 -vt 0.769841 0.914124 -vt 0.767342 0.913161 -vt 0.772099 0.909734 -vt 0.769621 0.906673 -vt 0.825586 0.905994 -vt 0.829526 0.906914 -vt 0.825561 0.906650 -vt 0.204964 0.792987 -vt 0.202069 0.790938 -vt 0.205621 0.791927 -vt 0.202538 0.795344 -vt 0.200486 0.792639 -vt 0.488037 0.939241 -vt 0.491851 0.941475 -vt 0.488373 0.946596 -vt 0.487245 0.931862 -vt 0.491218 0.934062 -vt 0.486163 0.926241 -vt 0.489975 0.927298 -vt 0.485636 0.924733 -vt 0.488207 0.922716 -vt 0.666597 0.961940 -vt 0.663571 0.962685 -vt 0.663538 0.961964 -vt 0.046119 0.851148 -vt 0.048193 0.849904 -vt 0.049157 0.851019 -vt 0.047887 0.852606 -vt 0.051699 0.852871 -vt 0.772730 0.915530 -vt 0.771193 0.910996 -vt 0.039538 0.856218 -vt 0.043548 0.853588 -vt 0.049102 0.856481 -vt 0.046024 0.859236 -vt 0.055748 0.859439 -vt 0.178697 0.903630 -vt 0.182339 0.899342 -vt 0.186215 0.898516 -vt 0.771932 0.922795 -vt 0.770826 0.918456 -vt 0.773815 0.915487 -vt 0.773100 0.921861 -vt 0.188393 0.795728 -vt 0.182566 0.793336 -vt 0.193513 0.792119 -vt 0.182203 0.799535 -vt 0.176521 0.797201 -vt 0.176578 0.802545 -vt 0.170635 0.800724 -vt 0.490560 0.918790 -vt 0.491613 0.912292 -vt 0.491716 0.921456 -vt 0.034763 0.854022 -vt 0.040381 0.851702 -vt 0.041380 0.852182 -vt 0.187399 0.789938 -vt 0.179256 0.788631 -vt 0.195570 0.789713 -vt 0.185239 0.790970 -vt 0.025498 0.855358 -vt 0.030706 0.858307 -vt 0.023795 0.864552 -vt 0.038238 0.861152 -vt 0.031898 0.864855 -vt 0.042936 0.863188 -vt 0.169003 0.915844 -vt 0.173073 0.909351 -vt 0.177420 0.908623 -vt 0.771166 0.935522 -vt 0.771599 0.929335 -vt 0.772469 0.928916 -vt 0.773164 0.929282 -vt 0.771922 0.935581 -vt 0.168638 0.788035 -vt 0.173486 0.792444 -vt 0.167221 0.796211 -vt 0.158653 0.791059 -vt 0.161314 0.799741 -vt 0.156613 0.795124 -vt 0.157235 0.802069 -vt 0.150895 0.798441 -vt 0.490670 0.903782 -vt 0.490869 0.896853 -vt 0.491519 0.904300 -vt 0.490027 0.896904 -vt 0.489939 0.904108 -vt 0.018115 0.859039 -vt 0.024726 0.869218 -vt 0.170711 0.786783 -vt 0.163530 0.783191 -vt 0.171230 0.786458 -vt 0.162459 0.783793 -vt 0.159700 0.785342 -vt 0.155814 0.779182 -vt 0.150138 0.778016 -vt 0.003505 0.869066 -vt 0.008660 0.862859 -vt 0.010532 0.863593 -vt 0.006044 0.869977 -vt 0.015486 0.864963 -vt 0.016374 0.881604 -vt 0.012035 0.877560 -vt 0.019356 0.874929 -vt 0.152058 0.772885 -vt 0.142205 0.786428 -vt 0.139838 0.778981 -vt 0.146163 0.780192 -vt 0.136299 0.789619 -vt 0.133582 0.782213 -vt 0.133592 0.791101 -vt 0.130461 0.783868 -vt 0.132858 0.791512 -vt 0.129121 0.784615 -vt 0.008626 0.886455 -vt 0.008939 0.875496 -vt 0.015411 0.888575 -vt 0.020520 0.883091 -vt 0.020374 0.890208 -vt 0.167920 0.937298 -vt 0.171361 0.943360 -vt 0.164768 0.937907 -vt 0.168819 0.937149 -vt 0.172920 0.943095 -vt 0.153093 0.764742 -vt 0.153969 0.771999 -vt 0.150750 0.765699 -vt 0.145068 0.768233 -vt 0.137968 0.771570 -vt 0.132025 0.774503 -vt 0.129297 0.775939 -vt 0.000568 0.876243 -vt 0.000776 0.884308 -vt 0.000000 0.876088 -vt 0.002996 0.877009 -vt 0.002716 0.884762 -vt 0.179618 0.947350 -vt 0.914175 0.930071 -vt 0.916557 0.923754 -vt 0.914798 0.930475 -vt 0.156800 0.756415 -vt 0.153683 0.764544 -vt 0.155161 0.756833 -vt 0.150685 0.758412 -vt 0.143515 0.761310 -vt 0.137355 0.764054 -vt 0.133412 0.766033 -vt 0.959968 0.378065 -vt 0.960722 0.386421 -vt 0.960002 0.387130 -vt 0.958827 0.379164 -vt 0.959420 0.387262 -vt 0.019806 0.896391 -vt 0.025167 0.903523 -vt 0.013394 0.894722 -vt 0.024621 0.897781 -vt 0.031389 0.904891 -vt 0.188179 0.949787 -vt 0.175605 0.948225 -vt 0.920069 0.914185 -vt 0.917372 0.925011 -vt 0.163733 0.747608 -vt 0.161710 0.747858 -vt 0.155561 0.749589 -vt 0.149441 0.751805 -vt 0.144640 0.753881 -vt 0.957991 0.365613 -vt 0.007754 0.893518 -vt 0.016638 0.902625 -vt 0.005933 0.893305 -vt 0.020172 0.902749 -vt 0.035314 0.910971 -vt 0.040812 0.911754 -vt 0.921000 0.917421 -vt 0.925152 0.906680 -vt 0.921219 0.920952 -vt 0.924749 0.903336 -vt 0.919108 0.913615 -vt 0.923997 0.901245 -vt 0.174070 0.738157 -vt 0.169097 0.738607 -vt 0.163646 0.740128 -vt 0.958713 0.344394 -vt 0.960799 0.359205 -vt 0.959787 0.362534 -vt 0.957022 0.348478 -vt 0.955891 0.349823 -vt 0.957018 0.365829 -vt 0.030175 0.911299 -vt 0.048247 0.917607 -vt 0.960939 0.329107 -vt 0.965735 0.318926 -vt 0.960846 0.331163 -vt 0.929651 0.892859 -vt 0.929301 0.889352 -vt 0.923259 0.900558 -vt 0.928820 0.887395 -vt 0.186307 0.729048 -vt 0.174395 0.738346 -vt 0.179963 0.729130 -vt 0.957450 0.324631 -vt 0.959758 0.340351 -vt 0.955949 0.329998 -vt 0.953986 0.334227 -vt 0.032141 0.910857 -vt 0.044254 0.917891 -vt 0.970172 0.311831 -vt 0.965454 0.322228 -vt 0.934369 0.884693 -vt 0.929778 0.896356 -vt 0.934440 0.879481 -vt 0.197576 0.720725 -vt 0.185343 0.728570 -vt 0.196250 0.720104 -vt 0.183315 0.728511 -vt 0.193410 0.720390 -vt 0.955116 0.308509 -vt 0.958260 0.320546 -vt 0.953829 0.315001 -vt 0.952353 0.320588 -vt 0.951456 0.322803 -vt 0.953671 0.334591 -vt 0.053521 0.923857 -vt 0.043162 0.918764 -vt 0.056632 0.922345 -vt 0.060710 0.924988 -vt 0.054210 0.922755 -vt 0.973711 0.307828 -vt 0.969748 0.314717 -vt 0.970670 0.307232 -vt 0.974843 0.301644 -vt 0.939178 0.873297 -vt 0.934149 0.874418 -vt 0.939698 0.867067 -vt 0.933889 0.873194 -vt 0.939752 0.864729 -vt 0.203705 0.714447 -vt 0.951934 0.295783 -vt 0.955855 0.303287 -vt 0.951255 0.302869 -vt 0.950345 0.310095 -vt 0.949460 0.315359 -vt 0.979416 0.312418 -vt 0.976491 0.307747 -vt 0.978142 0.301655 -vt 0.944947 0.886049 -vt 0.942082 0.878560 -vt 0.943275 0.872975 -vt 0.947000 0.879364 -vt 0.944437 0.866990 -vt 0.348761 0.458229 -vt 0.349563 0.467257 -vt 0.348619 0.468627 -vt 0.347677 0.459111 -vt 0.347820 0.468834 -vt 0.347168 0.468333 -vt 0.346740 0.458570 -vt 0.348803 0.470368 -vt 0.348011 0.476672 -vt 0.347925 0.470333 -vt 0.977202 0.318340 -vt 0.975064 0.312352 -vt 0.975494 0.311125 -vt 0.978751 0.333282 -vt 0.978084 0.316056 -vt 0.348282 0.439602 -vt 0.346833 0.439749 -vt 0.941829 0.303595 -vt 0.935198 0.319287 -vt 0.941508 0.299996 -vt 0.942339 0.310250 -vt 0.935962 0.324416 -vt 0.348429 0.491949 -vt 0.349222 0.475876 -vt 0.949719 0.926566 -vt 0.947354 0.912105 -vt 0.948877 0.898098 -vt 0.347245 0.411496 -vt 0.345685 0.438219 -vt 0.345832 0.410210 -vt 0.928348 0.350526 -vt 0.937145 0.332810 -vt 0.929897 0.358469 -vt 0.932918 0.342567 -vt 0.937508 0.335546 -vt 0.930310 0.360623 -vt 0.347900 0.518084 -vt 0.347419 0.492139 -vt 0.978644 0.395597 -vt 0.978848 0.359688 -vt 0.980433 0.356512 -vt 0.980414 0.392332 -vt 0.982018 0.330123 -vt 0.948969 0.966942 -vt 0.349348 0.371303 -vt 0.349526 0.409505 -vt 0.348566 0.410965 -vt 0.920437 0.392936 -vt 0.348947 0.554794 -vt 0.349317 0.516890 -vt 0.026885 0.948750 -vt 0.025401 0.945415 -vt 0.027079 0.944591 -vt 0.023739 0.945687 -vt 0.023705 0.949180 -vt 0.021908 0.945086 -vt 0.295856 0.937712 -vt 0.294661 0.941873 -vt 0.294190 0.937441 -vt 0.292285 0.942108 -vt 0.292352 0.938039 -vt 0.288400 0.948347 -vt 0.291459 0.949353 -vt 0.288777 0.951200 -vt 0.024196 0.941257 -vt 0.021828 0.941021 -vt 0.298826 0.944712 -vt 0.295989 0.946310 -vt 0.296987 0.942820 -vt 0.292517 0.955830 -vt 0.294188 0.952731 -vt 0.295702 0.956258 -vt 0.300279 0.950149 -vt 0.297220 0.948784 -vt 0.299986 0.947288 -vt 0.295360 0.952618 -vt 0.290010 0.953727 -vt 0.292623 0.951843 -vt 0.494546 0.894519 -vt 0.494246 0.891961 -vt 0.495685 0.894807 -vt 0.293975 0.951431 -vt 0.293949 0.945540 -vt 0.235763 0.914697 -vt 0.232862 0.915567 -vt 0.235697 0.913114 -vt 0.292909 0.951622 -vt 0.909577 0.867116 -vt 0.912600 0.868990 -vt 0.912606 0.870837 -vt 0.232854 0.913814 -vt 0.235738 0.909858 -vt 0.909656 0.863837 -vt 0.912647 0.867776 -vt 0.909654 0.869862 -vt 0.912647 0.871525 -vt 0.294933 0.952180 -vt 0.014971 0.948750 -vt 0.013486 0.945415 -vt 0.015164 0.944591 -vt 0.011824 0.945687 -vt 0.011791 0.949180 -vt 0.009993 0.945086 -vt 0.331683 0.937712 -vt 0.330487 0.941873 -vt 0.330017 0.937441 -vt 0.328112 0.942108 -vt 0.328179 0.938039 -vt 0.324226 0.948347 -vt 0.327286 0.949353 -vt 0.324604 0.951200 -vt 0.012281 0.941257 -vt 0.009913 0.941021 -vt 0.334653 0.944712 -vt 0.331815 0.946310 -vt 0.332813 0.942820 -vt 0.328344 0.955830 -vt 0.330015 0.952731 -vt 0.331529 0.956258 -vt 0.336106 0.950149 -vt 0.333047 0.948784 -vt 0.335813 0.947288 -vt 0.331187 0.952618 -vt 0.325837 0.953727 -vt 0.328449 0.951843 -vt 0.491707 0.894519 -vt 0.491407 0.891961 -vt 0.492845 0.894807 -vt 0.329802 0.951431 -vt 0.329776 0.945540 -vt 0.235763 0.928102 -vt 0.232862 0.928973 -vt 0.235697 0.926519 -vt 0.328735 0.951622 -vt 0.909577 0.875314 -vt 0.912600 0.877189 -vt 0.912606 0.879036 -vt 0.232854 0.927220 -vt 0.235738 0.923264 -vt 0.909656 0.872036 -vt 0.912647 0.875975 -vt 0.909654 0.878061 -vt 0.912647 0.879724 -vt 0.330760 0.952180 -vt 0.050714 0.948750 -vt 0.049230 0.945415 -vt 0.050908 0.944591 -vt 0.047568 0.945687 -vt 0.047534 0.949180 -vt 0.045737 0.945086 -vt 0.307798 0.937712 -vt 0.306603 0.941873 -vt 0.306132 0.937441 -vt 0.304227 0.942108 -vt 0.304295 0.938039 -vt 0.300341 0.948347 -vt 0.303401 0.949353 -vt 0.300719 0.951200 -vt 0.048025 0.941257 -vt 0.045657 0.941021 -vt 0.310768 0.944712 -vt 0.307931 0.946310 -vt 0.308929 0.942820 -vt 0.304459 0.955830 -vt 0.306130 0.952731 -vt 0.307644 0.956258 -vt 0.312221 0.950149 -vt 0.309162 0.948784 -vt 0.311928 0.947288 -vt 0.307302 0.952618 -vt 0.301952 0.953727 -vt 0.304565 0.951843 -vt 0.911039 0.908478 -vt 0.910739 0.905920 -vt 0.912177 0.908766 -vt 0.305917 0.951431 -vt 0.305891 0.945540 -vt 0.912495 0.901575 -vt 0.909595 0.902445 -vt 0.912429 0.899992 -vt 0.304851 0.951622 -vt 0.909577 0.891711 -vt 0.912600 0.893585 -vt 0.912606 0.895432 -vt 0.909586 0.900693 -vt 0.912470 0.896737 -vt 0.909656 0.888432 -vt 0.912647 0.892371 -vt 0.909654 0.894458 -vt 0.912647 0.896120 -vt 0.306875 0.952180 -vt 0.038800 0.948749 -vt 0.037316 0.945414 -vt 0.038993 0.944590 -vt 0.035653 0.945686 -vt 0.035620 0.949180 -vt 0.033823 0.945086 -vt 0.319740 0.937711 -vt 0.318545 0.941872 -vt 0.318074 0.937440 -vt 0.316170 0.942107 -vt 0.316237 0.938038 -vt 0.312283 0.948347 -vt 0.315343 0.949353 -vt 0.312661 0.951199 -vt 0.036111 0.941256 -vt 0.033742 0.941020 -vt 0.322710 0.944712 -vt 0.319873 0.946309 -vt 0.320871 0.942819 -vt 0.316401 0.955830 -vt 0.318072 0.952730 -vt 0.319587 0.956258 -vt 0.324163 0.950148 -vt 0.321104 0.948784 -vt 0.323870 0.947288 -vt 0.319244 0.952617 -vt 0.313894 0.953727 -vt 0.316507 0.951843 -vt 0.911039 0.905445 -vt 0.910739 0.902887 -vt 0.912177 0.905733 -vt 0.317859 0.951431 -vt 0.317833 0.945540 -vt 0.235763 0.921400 -vt 0.232863 0.922270 -vt 0.235697 0.919817 -vt 0.316793 0.951621 -vt 0.912641 0.884131 -vt 0.909618 0.882257 -vt 0.909611 0.880410 -vt 0.232854 0.920518 -vt 0.235738 0.916562 -vt 0.912562 0.887411 -vt 0.909571 0.883471 -vt 0.912564 0.881384 -vt 0.909571 0.879723 -vt 0.318817 0.952180 -vt 0.990541 0.755004 -vt 0.990521 0.733843 -vt 0.990992 0.755059 -vt 0.404466 0.011867 -vt 0.391808 0.032542 -vt 0.404379 0.010563 -vt 0.404653 0.014836 -vt 0.404980 0.020154 -vt 0.392266 0.036797 -vt 0.405447 0.027895 -vt 0.392866 0.042421 -vt 0.990538 0.703667 -vt 0.991007 0.733650 -vt 0.989963 0.733319 -vt 0.989804 0.703232 -vt 0.380963 0.065040 -vt 0.381693 0.069598 -vt 0.393639 0.049703 -vt 0.382710 0.075949 -vt 0.877706 0.649853 -vt 0.880639 0.644176 -vt 0.881693 0.658161 -vt 0.857538 0.617586 -vt 0.861348 0.614500 -vt 0.864321 0.626644 -vt 0.834125 0.591190 -vt 0.842327 0.594468 -vt 0.847880 0.606116 -vt 0.814934 0.567495 -vt 0.370923 0.100549 -vt 0.380466 0.061934 -vt 0.371752 0.104209 -vt 0.372430 0.107184 -vt 0.794960 0.552145 -vt 0.814770 0.561739 -vt 0.989279 0.664873 -vt 0.989408 0.620948 -vt 0.989750 0.665597 -vt 0.363284 0.145886 -vt 0.364396 0.149341 -vt 0.842545 0.579097 -vt 0.842444 0.587509 -vt 0.794970 0.549551 -vt 0.814655 0.554543 -vt 0.501410 0.422498 -vt 0.505701 0.475677 -vt 0.505168 0.476751 -vt 0.990486 0.517382 -vt 0.990900 0.570925 -vt 0.990124 0.571539 -vt 0.989240 0.571131 -vt 0.989774 0.517490 -vt 0.357068 0.194909 -vt 0.352233 0.247395 -vt 0.356396 0.193467 -vt 0.358276 0.197441 -vt 0.353197 0.248612 -vt 0.359868 0.200711 -vt 0.354679 0.250395 -vt 0.362661 0.206391 -vt 0.357414 0.253592 -vt 0.366808 0.214774 -vt 0.361325 0.258083 -vt 0.371595 0.224401 -vt 0.366299 0.263739 -vt 0.376365 0.233945 -vt 0.371406 0.269489 -vt 0.907113 0.622923 -vt 0.909516 0.591125 -vt 0.911463 0.629737 -vt 0.900131 0.613331 -vt 0.903006 0.585599 -vt 0.891595 0.603321 -vt 0.895076 0.579910 -vt 0.881017 0.592828 -vt 0.885469 0.574106 -vt 0.871331 0.584753 -vt 0.876488 0.569485 -vt 0.861702 0.578714 -vt 0.869481 0.566540 -vt 0.842709 0.559285 -vt 0.842633 0.569535 -vt 0.990819 0.460350 -vt 0.990993 0.517014 -vt 0.990091 0.460408 -vt 0.988929 0.517037 -vt 0.989362 0.460305 -vt 0.350060 0.304062 -vt 0.351461 0.304690 -vt 0.352733 0.305179 -vt 0.355811 0.306271 -vt 0.360060 0.307690 -vt 0.365253 0.309360 -vt 0.913058 0.554761 -vt 0.913117 0.594601 -vt 0.908709 0.553723 -vt 0.902020 0.552413 -vt 0.893855 0.551163 -vt 0.883972 0.550024 -vt 0.874654 0.549234 -vt 0.859979 0.563484 -vt 0.867019 0.548866 -vt 0.813822 0.548922 -vt 0.498746 0.307943 -vt 0.501555 0.249259 -vt 0.499166 0.308144 -vt 0.990096 0.401434 -vt 0.990539 0.341971 -vt 0.990829 0.401677 -vt 0.989369 0.401356 -vt 0.989551 0.341726 -vt 0.988837 0.401403 -vt 0.352634 0.421639 -vt 0.350050 0.362574 -vt 0.351041 0.362244 -vt 0.354021 0.419995 -vt 0.352810 0.361530 -vt 0.853974 0.520516 -vt 0.857988 0.534069 -vt 0.842813 0.537888 -vt 0.826728 0.533610 -vt 0.989110 0.285303 -vt 0.988823 0.342041 -vt 0.357909 0.476773 -vt 0.359435 0.473495 -vt 0.355354 0.418325 -vt 0.842698 0.514840 -vt 0.366845 0.519062 -vt 0.362303 0.467137 -vt 0.369208 0.511175 -vt 0.366115 0.458552 -vt 0.372908 0.498709 -vt 0.370805 0.447901 -vt 0.377178 0.484228 -vt 0.375561 0.437005 -vt 0.381433 0.469682 -vt 0.379985 0.426757 -vt 0.906399 0.413077 -vt 0.908660 0.440399 -vt 0.902006 0.451937 -vt 0.900000 0.427180 -vt 0.893883 0.464261 -vt 0.891480 0.443735 -vt 0.884044 0.477335 -vt 0.880910 0.461588 -vt 0.874754 0.488256 -vt 0.871235 0.475779 -vt 0.867214 0.495770 -vt 0.861594 0.487130 -vt 0.857905 0.503663 -vt 0.512262 0.140953 -vt 0.520737 0.097283 -vt 0.512863 0.142995 -vt 0.990064 0.234136 -vt 0.990502 0.190288 -vt 0.990772 0.234633 -vt 0.989746 0.190570 -vt 0.989414 0.234532 -vt 0.372507 0.571283 -vt 0.363641 0.529353 -vt 0.364468 0.526802 -vt 0.373447 0.567011 -vt 0.365470 0.523586 -vt 0.374697 0.561170 -vt 0.990537 0.153800 -vt 0.382128 0.608545 -vt 0.372007 0.573462 -vt 0.382892 0.603649 -vt 0.383695 0.598342 -vt 0.385051 0.589225 -vt 0.376842 0.550984 -vt 0.387323 0.573775 -vt 0.380018 0.535773 -vt 0.390208 0.554023 -vt 0.383681 0.518114 -vt 0.393169 0.533611 -vt 0.387331 0.500392 -vt 0.396100 0.513220 -vt 0.390988 0.482460 -vt 0.399093 0.492161 -vt 0.394759 0.463754 -vt 0.402225 0.469835 -vt 0.398464 0.445143 -vt 0.884984 0.420936 -vt 0.891336 0.423178 -vt 0.880779 0.444757 -vt 0.876101 0.440337 -vt 0.871126 0.461848 -vt 0.869061 0.453552 -vt 0.861474 0.475498 -vt 0.392944 0.638712 -vt 0.381795 0.610567 -vt 0.393389 0.634608 -vt 0.393889 0.629760 -vt 0.394580 0.622877 -vt 0.395855 0.609961 -vt 0.397734 0.590714 -vt 0.399898 0.568387 -vt 0.402049 0.545996 -vt 0.404199 0.523364 -vt 0.406407 0.499789 -vt 0.408723 0.474675 -vt 0.405487 0.446235 -vt 0.411763 0.441000 -vt 0.408120 0.426872 -vt 0.870860 0.441006 -vt 0.864307 0.453077 -vt 0.859598 0.468288 -vt 0.990688 0.125415 -vt 0.991150 0.105903 -vt 0.991204 0.125735 -vt 0.990650 0.105986 -vt 0.990164 0.125803 -vt 0.405166 0.655515 -vt 0.405486 0.650218 -vt 0.405940 0.642348 -vt 0.853857 0.464592 -vt 0.857520 0.463912 -vt 0.847846 0.477918 -vt 0.841440 0.482938 -vt 0.834027 0.496492 -vt 0.824960 0.505725 -vt 0.814440 0.521461 -vt 0.794947 0.545270 -vt 0.794957 0.545766 -vt 0.497244 0.696966 -vt 0.498190 0.709649 -vt 0.497777 0.709646 -vt 0.417664 0.669507 -vt 0.404900 0.659506 -vt 0.404984 0.658348 -vt 0.417708 0.667848 -vt 0.417779 0.664566 -vt 0.417894 0.658689 -vt 0.497367 0.683784 -vt 0.497696 0.696961 -vt 0.430819 0.669990 -vt 0.417646 0.670033 -vt 0.430770 0.666895 -vt 0.430663 0.661374 -vt 0.430480 0.652750 -vt 0.418035 0.650732 -vt 0.424023 0.396799 -vt 0.421227 0.407848 -vt 0.421336 0.386443 -vt 0.423392 0.376860 -vt 0.421429 0.362966 -vt 0.422532 0.350279 -vt 0.421595 0.322117 -vt 0.496937 0.683785 -vt 0.497970 0.670364 -vt 0.444099 0.658877 -vt 0.443841 0.654535 -vt 0.443385 0.647140 -vt 0.430153 0.637956 -vt 0.442573 0.634154 -vt 0.429677 0.616956 -vt 0.441431 0.616042 -vt 0.429114 0.592474 -vt 0.439966 0.592896 -vt 0.428538 0.567892 -vt 0.438444 0.568975 -vt 0.427944 0.543072 -vt 0.436918 0.545097 -vt 0.427310 0.517250 -vt 0.435333 0.520458 -vt 0.426616 0.489760 -vt 0.433641 0.494357 -vt 0.426066 0.468497 -vt 0.431840 0.466788 -vt 0.425653 0.452951 -vt 0.425215 0.436859 -vt 0.430354 0.444249 -vt 0.424836 0.423576 -vt 0.429325 0.428817 -vt 0.424488 0.411875 -vt 0.428362 0.414587 -vt 0.427469 0.401579 -vt 0.425965 0.379907 -vt 0.431595 0.406120 -vt 0.429710 0.389104 -vt 0.987271 0.157476 -vt 0.987361 0.127502 -vt 0.987878 0.127185 -vt 0.988494 0.127737 -vt 0.988007 0.157782 -vt 0.456311 0.633886 -vt 0.467611 0.605618 -vt 0.456743 0.637976 -vt 0.455576 0.627008 -vt 0.466833 0.600759 -vt 0.454273 0.614887 -vt 0.465749 0.593985 -vt 0.452444 0.597908 -vt 0.463888 0.582349 -vt 0.450104 0.576206 -vt 0.461112 0.564994 -vt 0.447681 0.553776 -vt 0.457891 0.544854 -vt 0.445259 0.531378 -vt 0.454661 0.524649 -vt 0.442754 0.508263 -vt 0.451395 0.504218 -vt 0.440092 0.483767 -vt 0.447992 0.482925 -vt 0.437273 0.457889 -vt 0.444366 0.460227 -vt 0.434958 0.436691 -vt 0.440463 0.435793 -vt 0.433370 0.422211 -vt 0.438003 0.420385 -vt 0.562767 0.876108 -vt 0.564468 0.870941 -vt 0.572138 0.892112 -vt 0.537589 0.840823 -vt 0.545626 0.846143 -vt 0.551251 0.859189 -vt 0.498638 0.791943 -vt 0.518185 0.808276 -vt 0.518620 0.816637 -vt 0.545641 0.839197 -vt 0.633913 0.145963 -vt 0.625896 0.102333 -vt 0.626381 0.100183 -vt 0.986254 0.240305 -vt 0.986382 0.196091 -vt 0.986958 0.195409 -vt 0.987720 0.195681 -vt 0.986956 0.239818 -vt 0.477276 0.568446 -vt 0.485827 0.526403 -vt 0.477753 0.570523 -vt 0.476316 0.564227 -vt 0.485002 0.523873 -vt 0.475012 0.558466 -vt 0.483572 0.519417 -vt 0.472741 0.548410 -vt 0.480876 0.510942 -vt 0.469355 0.533388 -vt 0.477280 0.499585 -vt 0.465430 0.515953 -vt 0.472837 0.485517 -vt 0.461495 0.498452 -vt 0.468311 0.471151 -vt 0.457520 0.480743 -vt 0.463781 0.456724 -vt 0.453383 0.462268 -vt 0.459095 0.441740 -vt 0.593779 0.911961 -vt 0.595935 0.895040 -vt 0.599229 0.924400 -vt 0.583449 0.890895 -vt 0.586357 0.878316 -vt 0.573973 0.874205 -vt 0.577301 0.864413 -vt 0.564460 0.860868 -vt 0.569892 0.854861 -vt 0.987760 0.240427 -vt 0.986953 0.289754 -vt 0.491773 0.476284 -vt 0.490409 0.473451 -vt 0.518134 0.802005 -vt 0.545674 0.821254 -vt 0.545654 0.830800 -vt 0.987755 0.290321 -vt 0.987474 0.344013 -vt 0.495385 0.422064 -vt 0.489185 0.470855 -vt 0.494280 0.420702 -vt 0.486676 0.465482 -vt 0.492521 0.418477 -vt 0.482720 0.456951 -vt 0.489279 0.414306 -vt 0.477833 0.446371 -vt 0.484578 0.408195 -vt 0.472857 0.435550 -vt 0.479293 0.401275 -vt 0.467878 0.424661 -vt 0.474027 0.394317 -vt 0.603743 0.882343 -vt 0.607844 0.856038 -vt 0.608507 0.890453 -vt 0.595886 0.870399 -vt 0.600958 0.848660 -vt 0.586325 0.857722 -vt 0.592503 0.840708 -vt 0.577271 0.847132 -vt 0.581621 0.831778 -vt 0.569886 0.839841 -vt 0.572035 0.824975 -vt 0.560726 0.832165 -vt 0.562711 0.819542 -vt 0.498632 0.789748 -vt 0.518172 0.794481 -vt 0.986868 0.400860 -vt 0.986651 0.343749 -vt 0.498334 0.366466 -vt 0.496330 0.423183 -vt 0.497025 0.365941 -vt 0.495511 0.365257 -vt 0.492759 0.363930 -vt 0.488423 0.361760 -vt 0.483072 0.359022 -vt 0.477627 0.356172 -vt 0.609999 0.821962 -vt 0.611225 0.860034 -vt 0.603700 0.819032 -vt 0.595862 0.815752 -vt 0.586326 0.812152 -vt 0.577258 0.809026 -vt 0.569919 0.806777 -vt 0.545754 0.800487 -vt 0.545704 0.811022 -vt 0.647343 0.362581 -vt 0.646695 0.304272 -vt 0.647453 0.303879 -vt 0.986641 0.459818 -vt 0.985808 0.401024 -vt 0.987566 0.459546 -vt 0.987604 0.400912 -vt 0.497437 0.308461 -vt 0.495745 0.309049 -vt 0.493125 0.309853 -vt 0.488949 0.311044 -vt 0.483653 0.312483 -vt 0.478203 0.313892 -vt 0.473736 0.354088 -vt 0.474159 0.314883 -vt 0.604436 0.785378 -vt 0.596769 0.786723 -vt 0.587437 0.787970 -vt 0.578686 0.788853 -vt 0.571781 0.789309 -vt 0.560724 0.804248 -vt 0.558660 0.789624 -vt 0.528858 0.789509 -vt 0.644911 0.421944 -vt 0.646703 0.362411 -vt 0.986291 0.519536 -vt 0.985653 0.459859 -vt 0.987244 0.519205 -vt 0.495635 0.249352 -vt 0.498388 0.308070 -vt 0.494394 0.250853 -vt 0.492671 0.252832 -vt 0.489633 0.256216 -vt 0.485094 0.261180 -vt 0.479838 0.266853 -vt 0.474576 0.272445 -vt 0.608616 0.746852 -vt 0.610370 0.784059 -vt 0.601905 0.752627 -vt 0.593656 0.758597 -vt 0.583392 0.764777 -vt 0.573930 0.769475 -vt 0.564508 0.772868 -vt 0.639639 0.477422 -vt 0.644331 0.421318 -vt 0.986051 0.576085 -vt 0.985687 0.519307 -vt 0.986920 0.576197 -vt 0.491255 0.192600 -vt 0.496289 0.248511 -vt 0.489981 0.195338 -vt 0.488803 0.197770 -vt 0.486193 0.203046 -vt 0.482265 0.210869 -vt 0.477412 0.220454 -vt 0.472468 0.230124 -vt 0.467522 0.239678 -vt 0.470787 0.276407 -vt 0.603750 0.718487 -vt 0.595896 0.728784 -vt 0.586338 0.739402 -vt 0.577282 0.748003 -vt 0.569901 0.753629 -vt 0.545770 0.766568 -vt 0.551194 0.776555 -vt 0.529979 0.781348 -vt 0.483440 0.145988 -vt 0.476522 0.097169 -vt 0.484999 0.140892 -vt 0.484611 0.142204 -vt 0.475828 0.100448 -vt 0.474661 0.105725 -vt 0.482282 0.149609 -vt 0.472597 0.114832 -vt 0.480033 0.156519 -vt 0.469675 0.127556 -vt 0.476492 0.167275 -vt 0.465941 0.143707 -vt 0.472113 0.180475 -vt 0.462083 0.160275 -vt 0.467653 0.193822 -vt 0.458233 0.176661 -vt 0.463189 0.207045 -vt 0.454263 0.193374 -vt 0.458647 0.220331 -vt 0.597081 0.680285 -vt 0.603812 0.689740 -vt 0.595942 0.704013 -vt 0.587720 0.698593 -vt 0.586363 0.718799 -vt 0.578991 0.713376 -vt 0.577308 0.730809 -vt 0.572066 0.723198 -vt 0.569897 0.738760 -vt 0.562727 0.733783 -vt 0.560758 0.746669 -vt 0.625273 0.573613 -vt 0.615296 0.609681 -vt 0.624676 0.571057 -vt 0.987204 0.670839 -vt 0.987283 0.707203 -vt 0.986554 0.670437 -vt 0.988019 0.706760 -vt 0.987892 0.670174 -vt 0.466282 0.063101 -vt 0.465578 0.067693 -vt 0.464568 0.074089 -vt 0.462670 0.085916 -vt 0.459975 0.102545 -vt 0.456849 0.121714 -vt 0.453712 0.140802 -vt 0.450540 0.159907 -vt 0.447235 0.179577 -vt 0.450058 0.210843 -vt 0.443712 0.200259 -vt 0.447361 0.221923 -vt 0.583494 0.691911 -vt 0.574008 0.708971 -vt 0.564458 0.722112 -vt 0.987867 0.735494 -vt 0.455602 0.031807 -vt 0.466739 0.059970 -vt 0.455182 0.036075 -vt 0.454597 0.041703 -vt 0.988022 0.754936 -vt 0.987383 0.735294 -vt 0.988559 0.754769 -vt 0.988427 0.734967 -vt 0.443403 0.015147 -vt 0.443088 0.020462 -vt 0.442609 0.028187 -vt 0.453820 0.048993 -vt 0.558827 0.717098 -vt 0.560826 0.719368 -vt 0.551214 0.730956 -vt 0.544952 0.734391 -vt 0.537476 0.746077 -vt 0.528522 0.753065 -vt 0.517998 0.766149 -vt 0.498639 0.785066 -vt 0.498640 0.785567 -vt 0.430705 0.010637 -vt 0.430352 0.026783 -vt 0.441722 0.042148 -vt 0.429938 0.044735 -vt 0.440457 0.061766 -vt 0.429400 0.067547 -vt 0.438985 0.084398 -vt 0.428835 0.090983 -vt 0.437501 0.106950 -vt 0.428259 0.114204 -vt 0.435992 0.129544 -vt 0.427650 0.137934 -vt 0.434410 0.152825 -vt 0.426988 0.162797 -vt 0.432712 0.177336 -vt 0.426463 0.181827 -vt 0.431393 0.196014 -vt 0.426086 0.195043 -vt 0.430415 0.209629 -vt 0.425659 0.209619 -vt 0.429400 0.223459 -vt 0.425112 0.227220 -vt 0.428557 0.234539 -vt 0.424805 0.236319 -vt 0.427571 0.246939 -vt 0.348182 0.653928 -vt 0.347990 0.640741 -vt 0.348414 0.640739 -vt 0.417736 0.001105 -vt 0.430881 0.000433 -vt 0.430866 0.001775 -vt 0.430818 0.004801 -vt 0.417838 0.005636 -vt 0.417963 0.011926 -vt 0.430564 0.017290 -vt 0.418169 0.023211 -vt 0.421190 0.222978 -vt 0.425359 0.219457 -vt 0.421510 0.264746 -vt 0.423858 0.262171 -vt 0.423113 0.281390 -vt 0.421644 0.291652 -vt 0.421754 0.314411 -vt 0.348802 0.666813 -vt 0.347778 0.653919 -vt 0.417706 0.000000 -vt 0.418531 0.044022 -vt 0.406281 0.041870 -vt 0.418918 0.066787 -vt 0.407445 0.061507 -vt 0.419306 0.090248 -vt 0.408781 0.084165 -vt 0.419679 0.113485 -vt 0.410107 0.106746 -vt 0.420044 0.137219 -vt 0.411424 0.129364 -vt 0.420407 0.162066 -vt 0.412771 0.152673 -vt 0.420672 0.181158 -vt 0.414174 0.177212 -vt 0.420847 0.194368 -vt 0.415233 0.195913 -vt 0.421029 0.208915 -vt 0.415997 0.209547 -vt 0.416765 0.223402 -vt 0.421312 0.235702 -vt 0.417367 0.234494 -vt 0.421399 0.246866 -vt 0.417883 0.244240 -vt 0.418525 0.256593 -vt 0.022893 0.958527 -vt 0.497517 0.761388 -vt 0.031091 0.974292 -vt 0.030169 0.974106 -vt 0.031290 0.973806 -vt 0.030185 0.974648 -vt 0.028005 0.974515 -vt 0.552047 0.957533 -vt 0.552943 0.956621 -vt 0.553659 0.957062 -vt 0.024697 0.974784 -vt 0.027893 0.975082 -vt 0.014780 0.959436 -vt 0.168516 0.965392 -vt 0.169540 0.964071 -vt 0.163449 0.950718 -vt 0.553650 0.965431 -vt 0.552045 0.964975 -vt 0.211339 0.952631 -vt 0.212013 0.953304 -vt 0.211365 0.953226 -vt 0.207842 0.952792 -vt 0.026308 0.950026 -vt 0.026775 0.949407 -vt 0.019592 0.974457 -vt 0.019132 0.975075 -vt 0.794447 0.702353 -vt 0.166950 0.950585 -vt 0.167620 0.951256 -vt 0.166979 0.951297 -vt 0.161282 0.964120 -vt 0.322002 0.956961 -vt 0.212838 0.966888 -vt 0.213827 0.964972 -vt 0.213940 0.967213 -vt 0.560151 0.964105 -vt 0.559978 0.957446 -vt 0.497517 0.769425 -vt 0.496977 0.768420 -vt 0.161700 0.975655 -vt 0.323267 0.964461 -vt 0.329138 0.964541 -vt 0.161274 0.975186 -vt 0.019686 0.950010 -vt 0.016294 0.949717 -vt 0.019497 0.949433 -vt 0.014780 0.950223 -vt 0.021843 0.950427 -vt 0.022984 0.965962 -vt 0.031292 0.966006 -vt 0.169610 0.975171 -vt 0.169143 0.975642 -vt 0.330338 0.964701 -vt 0.329101 0.965655 -vt 0.022403 0.950129 -vt 0.022891 0.950723 -vt 0.023224 0.965059 -vt 0.323156 0.965654 -vt 0.321997 0.964701 -vt 0.014576 0.958536 -vt 0.168632 0.958220 -vt 0.026118 0.957070 -vt 0.296508 0.957833 -vt 0.296276 0.956454 -vt 0.759857 0.948479 -vt 0.759368 0.945848 -vt 0.768366 0.945367 -vt 0.758152 0.945573 -vt 0.062230 0.951015 -vt 0.062022 0.951515 -vt 0.053998 0.951604 -vt 0.297963 0.965702 -vt 0.497194 0.777857 -vt 0.498078 0.776772 -vt 0.498124 0.777853 -vt 0.185423 0.965022 -vt 0.184783 0.962793 -vt 0.186144 0.963984 -vt 0.760107 0.946263 -vt 0.179385 0.950742 -vt 0.753022 0.960651 -vt 0.753201 0.961228 -vt 0.295379 0.963225 -vt 0.296312 0.961136 -vt 0.296111 0.964405 -vt 0.757185 0.961267 -vt 0.057102 0.927231 -vt 0.057467 0.926655 -vt 0.765029 0.961308 -vt 0.764670 0.961872 -vt 0.347541 0.578357 -vt 0.346031 0.578880 -vt 0.288394 0.965059 -vt 0.289589 0.962262 -vt 0.289573 0.964678 -vt 0.288821 0.966474 -vt 0.183594 0.951229 -vt 0.183365 0.950694 -vt 0.496976 0.776908 -vt 0.498078 0.770660 -vt 0.497124 0.770292 -vt 0.498124 0.769551 -vt 0.177902 0.963989 -vt 0.179205 0.962795 -vt 0.179309 0.965023 -vt 0.752804 0.936211 -vt 0.751606 0.936559 -vt 0.752324 0.946474 -vt 0.751636 0.948267 -vt 0.751322 0.946139 -vt 0.752226 0.945420 -vt 0.288820 0.958251 -vt 0.304893 0.957032 -vt 0.053760 0.951120 -vt 0.062232 0.943222 -vt 0.296573 0.956954 -vt 0.759441 0.944649 -vt 0.751117 0.944433 -vt 0.053823 0.943047 -vt 0.759897 0.945367 -vt 0.184024 0.958159 -vt 0.184841 0.958253 -vt 0.752358 0.953593 -vt 0.765196 0.954252 -vt 0.056932 0.934342 -vt 0.473811 0.965523 -vt 0.473950 0.967491 -vt 0.967828 0.832293 -vt 0.793675 0.682432 -vt 0.792613 0.682699 -vt 0.793147 0.681541 -vt 0.244833 0.962922 -vt 0.244629 0.963421 -vt 0.236673 0.963441 -vt 0.959637 0.833208 -vt 0.031608 0.960819 -vt 0.249762 0.765780 -vt 0.249799 0.766881 -vt 0.248891 0.766428 -vt 0.037459 0.974387 -vt 0.480159 0.953236 -vt 0.337565 0.963004 -vt 0.338569 0.961081 -vt 0.338291 0.964327 -vt 0.475971 0.952705 -vt 0.239772 0.939124 -vt 0.240147 0.938553 -vt 0.964508 0.848214 -vt 0.964060 0.848827 -vt 0.792777 0.689969 -vt 0.330388 0.963766 -vt 0.331697 0.961162 -vt 0.331603 0.963560 -vt 0.338756 0.958942 -vt 0.330796 0.965287 -vt 0.033290 0.973744 -vt 0.033459 0.974309 -vt 0.345741 0.579545 -vt 0.346621 0.578898 -vt 0.346621 0.580015 -vt 0.039853 0.961061 -vt 0.481230 0.967173 -vt 0.482045 0.965523 -vt 0.482266 0.967491 -vt 0.793676 0.688585 -vt 0.792615 0.688620 -vt 0.793129 0.689577 -vt 0.331029 0.957231 -vt 0.039884 0.950135 -vt 0.236434 0.955127 -vt 0.966765 0.824197 -vt 0.244834 0.955135 -vt 0.031557 0.949850 -vt 0.967590 0.823996 -vt 0.967826 0.824490 -vt 0.313418 0.964746 -vt 0.312177 0.965707 -vt 0.236673 0.954234 -vt 0.306253 0.965706 -vt 0.305081 0.964745 -vt 0.959431 0.832310 -vt 0.032461 0.966652 -vt 0.481135 0.960249 -vt 0.964679 0.841191 -vt 0.239591 0.946222 -vt 0.465357 0.965523 -vt 0.465495 0.967491 -vt 0.006100 0.965945 -vt 0.498038 0.778968 -vt 0.496977 0.779235 -vt 0.497511 0.778078 -vt 0.791124 0.966247 -vt 0.790920 0.966746 -vt 0.782964 0.966767 -vt 0.014290 0.965031 -vt 0.040164 0.960819 -vt 0.249762 0.763206 -vt 0.249799 0.764306 -vt 0.248891 0.763853 -vt 0.046015 0.974387 -vt 0.471704 0.953236 -vt 0.635958 0.963036 -vt 0.636962 0.961113 -vt 0.636684 0.964359 -vt 0.467516 0.952705 -vt 0.786063 0.942449 -vt 0.786438 0.941878 -vt 0.009420 0.950025 -vt 0.009868 0.949412 -vt 0.794472 0.697935 -vt 0.628781 0.963797 -vt 0.630090 0.961194 -vt 0.629996 0.963591 -vt 0.637149 0.958974 -vt 0.629188 0.965319 -vt 0.041846 0.973744 -vt 0.042014 0.974309 -vt 0.346728 0.579545 -vt 0.347608 0.578898 -vt 0.347608 0.580015 -vt 0.048409 0.961061 -vt 0.472775 0.967173 -vt 0.473590 0.965523 -vt 0.473811 0.967491 -vt 0.498039 0.785122 -vt 0.496978 0.785156 -vt 0.497493 0.786114 -vt 0.629422 0.957263 -vt 0.320600 0.964522 -vt 0.048440 0.950135 -vt 0.314711 0.964444 -vt 0.782725 0.958452 -vt 0.007163 0.974041 -vt 0.791125 0.958461 -vt 0.040113 0.949850 -vt 0.006338 0.974243 -vt 0.006102 0.973749 -vt 0.321874 0.964746 -vt 0.320633 0.965707 -vt 0.782964 0.957559 -vt 0.314708 0.965706 -vt 0.313536 0.964745 -vt 0.014497 0.965929 -vt 0.041017 0.966652 -vt 0.472680 0.960249 -vt 0.009249 0.957047 -vt 0.785882 0.949548 -vt 0.716613 0.031070 -vt 0.726166 0.001015 -vt 0.725744 0.031162 -vt 0.736126 0.008049 -vt 0.734834 0.036906 -vt 0.745590 0.020668 -vt 0.744282 0.037590 -vt 0.744036 0.628677 -vt 0.745329 0.645401 -vt 0.734727 0.627464 -vt 0.736015 0.655861 -vt 0.725859 0.631818 -vt 0.716238 0.659744 -vt 0.716781 0.626508 -vt 0.706278 0.652718 -vt 0.696814 0.640091 -vt 0.668554 0.531281 -vt 0.659790 0.508141 -vt 0.665548 0.542282 -vt 0.672216 0.572947 -vt 0.655020 0.470978 -vt 0.651318 0.431229 -vt 0.655086 0.426237 -vt 0.648769 0.389313 -vt 0.647453 0.345673 -vt 0.651434 0.345284 -vt 0.647453 0.300713 -vt 0.652787 0.260822 -vt 0.648800 0.256302 -vt 0.655160 0.220900 -vt 0.651407 0.214233 -vt 0.655182 0.174866 -vt 0.665867 0.105699 -vt 0.660030 0.138557 -vt 0.706387 0.004898 -vt 0.742996 0.054573 -vt 0.750925 0.070419 -vt 0.758266 0.090485 -vt 0.764946 0.114395 -vt 0.770891 0.141762 -vt 0.776030 0.172211 -vt 0.780285 0.205360 -vt 0.783587 0.240822 -vt 0.785862 0.278214 -vt 0.787035 0.317154 -vt 0.785833 0.396896 -vt 0.787035 0.357262 -vt 0.780140 0.469582 -vt 0.783506 0.434441 -vt 0.770617 0.531364 -vt 0.775814 0.501991 -vt 0.757935 0.579697 -vt 0.764627 0.557373 -vt 0.742764 0.612022 -vt 0.750618 0.598015 -vt 0.680065 0.535573 -vt 0.674587 0.509305 -vt 0.674815 0.142849 -vt 0.680338 0.117783 -vt 0.672572 0.076556 -vt 0.686548 0.096007 -vt 0.680072 0.051546 -vt 0.693386 0.077885 -vt 0.717014 0.054398 -vt 0.725439 0.055229 -vt 0.733789 0.061096 -vt 0.741724 0.071655 -vt 0.749174 0.086548 -vt 0.756071 0.105400 -vt 0.762348 0.127869 -vt 0.767935 0.153587 -vt 0.772762 0.182205 -vt 0.776762 0.213348 -vt 0.779865 0.246666 -vt 0.782001 0.281807 -vt 0.783106 0.318398 -vt 0.783106 0.356087 -vt 0.781977 0.393326 -vt 0.779789 0.428604 -vt 0.776625 0.461624 -vt 0.772562 0.492081 -vt 0.767677 0.519676 -vt 0.762051 0.544113 -vt 0.755760 0.565086 -vt 0.748884 0.582298 -vt 0.741505 0.595451 -vt 0.733698 0.604248 -vt 0.725540 0.608376 -vt 0.717115 0.607552 -vt 0.665792 0.449425 -vt 0.662689 0.416107 -vt 0.660580 0.269447 -vt 0.662766 0.234169 -vt 0.665930 0.201157 -vt 0.717323 0.072693 -vt 0.725208 0.073471 -vt 0.733021 0.078972 -vt 0.740446 0.088860 -vt 0.747419 0.102798 -vt 0.753874 0.120453 -vt 0.759748 0.141479 -vt 0.764977 0.165550 -vt 0.769493 0.192329 -vt 0.773237 0.221481 -vt 0.776140 0.252663 -vt 0.778141 0.285538 -vt 0.779173 0.319786 -vt 0.779173 0.355049 -vt 0.778116 0.389900 -vt 0.776070 0.422905 -vt 0.773109 0.453804 -vt 0.769306 0.482300 -vt 0.764734 0.508118 -vt 0.759468 0.530983 -vt 0.753582 0.550606 -vt 0.747149 0.566704 -vt 0.740242 0.579010 -vt 0.732935 0.587234 -vt 0.725304 0.591087 -vt 0.717419 0.590309 -vt 0.709604 0.584808 -vt 0.702179 0.574921 -vt 0.695208 0.560982 -vt 0.688751 0.543327 -vt 0.682878 0.522301 -vt 0.677650 0.498230 -vt 0.673132 0.471451 -vt 0.669390 0.442299 -vt 0.666487 0.411118 -vt 0.664486 0.378242 -vt 0.663454 0.343994 -vt 0.663454 0.308731 -vt 0.664511 0.273880 -vt 0.666557 0.240875 -vt 0.669518 0.209976 -vt 0.673321 0.181480 -vt 0.677893 0.155663 -vt 0.683157 0.132797 -vt 0.689045 0.113174 -vt 0.695477 0.097076 -vt 0.702385 0.084770 -vt 0.709690 0.076553 -vt 0.717615 0.091133 -vt 0.724960 0.091873 -vt 0.732237 0.097000 -vt 0.739153 0.106216 -vt 0.745647 0.119209 -vt 0.751659 0.135651 -vt 0.757130 0.155243 -vt 0.761999 0.177666 -vt 0.766207 0.202614 -vt 0.769691 0.229759 -vt 0.345240 0.079605 -vt 0.349760 0.102844 -vt 0.345165 0.108734 -vt 0.349697 0.135788 -vt 0.345095 0.139420 -vt 0.349634 0.170052 -vt 0.345030 0.171333 -vt 0.349569 0.205299 -vt 0.344971 0.204163 -vt 0.344921 0.236557 -vt 0.349508 0.240082 -vt 0.344881 0.267204 -vt 0.349453 0.272995 -vt 0.344852 0.295853 -vt 0.766031 0.472672 -vt 0.769573 0.446136 -vt 0.761774 0.496719 -vt 0.756868 0.518005 -vt 0.751387 0.536270 -vt 0.745396 0.551262 -vt 0.738962 0.562714 -vt 0.732157 0.570366 -vt 0.725048 0.573952 -vt 0.717705 0.573219 -vt 0.710426 0.568085 -vt 0.703510 0.558868 -vt 0.697018 0.545883 -vt 0.691006 0.529434 -vt 0.685534 0.509850 -vt 0.680664 0.487419 -vt 0.676458 0.462479 -vt 0.672972 0.435326 -vt 0.670267 0.406281 -vt 0.668404 0.375656 -vt 0.667442 0.343765 -vt 0.667442 0.310921 -vt 0.668427 0.278465 -vt 0.670332 0.247726 -vt 0.673090 0.218956 -vt 0.676632 0.192413 -vt 0.680891 0.168373 -vt 0.685795 0.147087 -vt 0.691277 0.128815 -vt 0.697269 0.113823 -vt 0.703701 0.102371 -vt 0.710506 0.094719 -vt 0.717878 0.109772 -vt 0.724680 0.110458 -vt 0.731421 0.115219 -vt 0.737827 0.123764 -vt 0.743843 0.135796 -vt 0.749413 0.151039 -vt 0.754478 0.169189 -vt 0.339748 0.018341 -vt 0.345398 0.027237 -vt 0.339647 0.039276 -vt 0.345318 0.052330 -vt 0.339548 0.062531 -vt 0.339457 0.087791 -vt 0.339373 0.114777 -vt 0.339296 0.143204 -vt 0.339231 0.172760 -vt 0.339178 0.203171 -vt 0.339138 0.233177 -vt 0.339111 0.261566 -vt 0.339102 0.288101 -vt 0.339106 0.312538 -vt 0.344835 0.322235 -vt 0.339125 0.334641 -vt 0.344828 0.346100 -vt 0.339155 0.354164 -vt 0.749159 0.522133 -vt 0.754238 0.505211 -vt 0.743608 0.536011 -vt 0.737650 0.546616 -vt 0.731348 0.553696 -vt 0.724762 0.557014 -vt 0.717960 0.556320 -vt 0.711220 0.551567 -vt 0.704813 0.543022 -vt 0.698797 0.530983 -vt 0.693228 0.515747 -vt 0.688162 0.497597 -vt 0.219593 0.709072 -vt 0.209290 0.714363 -vt 0.198677 0.709089 -vt 0.184216 0.714355 -vt 0.175449 0.709106 -vt 0.156960 0.714352 -vt 0.150200 0.709122 -vt 0.127838 0.714350 -vt 0.123219 0.709135 -vt 0.097160 0.714350 -vt 0.094803 0.709146 -vt 0.065247 0.714355 -vt 0.065239 0.709156 -vt 0.034824 0.709164 -vt 0.032410 0.714363 -vt 0.004803 0.709167 -vt 0.798376 0.050789 -vt 0.821623 0.044167 -vt 0.796618 0.057648 -vt 0.825365 0.036224 -vt 0.844998 0.031570 -vt 0.850595 0.022598 -vt 0.866533 0.019958 -vt 0.683861 0.181290 -vt 0.679916 0.203552 -vt 0.688402 0.161575 -vt 0.693481 0.144653 -vt 0.699032 0.130775 -vt 0.704990 0.120171 -vt 0.711292 0.113091 -vt 0.718096 0.128647 -vt 0.724358 0.129280 -vt 0.730562 0.133667 -vt 0.736458 0.141541 -vt 0.741993 0.152618 -vt 0.747120 0.166649 -vt 0.332664 0.015350 -vt 0.332542 0.032234 -vt 0.332426 0.051514 -vt 0.332317 0.072914 -vt 0.332216 0.096169 -vt 0.332127 0.121010 -vt 0.332048 0.147171 -vt 0.331985 0.174377 -vt 0.331936 0.202370 -vt 0.331903 0.229988 -vt 0.331888 0.256119 -vt 0.331892 0.280540 -vt 0.331913 0.303032 -vt 0.331949 0.323372 -vt 0.331999 0.341339 -vt 0.332066 0.356720 -vt 0.741779 0.520988 -vt 0.746887 0.508217 -vt 0.736294 0.530739 -vt 0.730494 0.537254 -vt 0.724432 0.540298 -vt 0.718172 0.539665 -vt 0.711967 0.535278 -vt 0.706072 0.527412 -vt 0.700535 0.516327 -vt 0.695410 0.502296 -vt 0.224384 0.702133 -vt 0.207527 0.702175 -vt 0.188271 0.702213 -vt 0.166889 0.702246 -vt 0.143646 0.702276 -vt 0.118813 0.702301 -vt 0.092659 0.702318 -vt 0.065445 0.702332 -vt 0.037449 0.702337 -vt 0.009819 0.702337 -vt 0.818463 0.053642 -vt 0.795445 0.066040 -vt 0.839983 0.042053 -vt 0.859811 0.031387 -vt 0.877750 0.021744 -vt 0.886017 0.009460 -vt 0.893609 0.013229 -vt 0.695641 0.160736 -vt 0.690968 0.176300 -vt 0.700750 0.147957 -vt 0.706234 0.138206 -vt 0.712036 0.131691 -vt 0.718256 0.147797 -vt 0.723976 0.148376 -vt 0.729643 0.152390 -vt 0.735029 0.159584 -vt 0.740088 0.169708 -vt 0.323847 0.017929 -vt 0.323709 0.030930 -vt 0.323580 0.046349 -vt 0.323456 0.063965 -vt 0.323341 0.083519 -vt 0.323238 0.104767 -vt 0.323149 0.127457 -vt 0.323071 0.151360 -vt 0.323009 0.176216 -vt 0.322964 0.201790 -vt 0.322939 0.227020 -vt 0.322931 0.250885 -vt 0.322945 0.273193 -vt 0.322975 0.293739 -vt 0.323023 0.312317 -vt 0.323088 0.328735 -vt 0.323166 0.342781 -vt 0.323259 0.354256 -vt 0.734880 0.515137 -vt 0.739891 0.506233 -vt 0.729582 0.521080 -vt 0.724045 0.523865 -vt 0.718327 0.523277 -vt 0.712658 0.519264 -vt 0.707272 0.512077 -vt 0.234364 0.693230 -vt 0.238575 0.702087 -vt 0.224075 0.693296 -vt 0.211105 0.693359 -vt 0.195702 0.693415 -vt 0.178112 0.693468 -vt 0.158573 0.693512 -vt 0.137341 0.693552 -vt 0.114651 0.693583 -vt 0.090755 0.693605 -vt 0.065899 0.693621 -vt 0.040321 0.693626 -vt 0.015079 0.693623 -vt 0.815981 0.064888 -vt 0.794950 0.076210 -vt 0.835645 0.054314 -vt 0.853762 0.044579 -vt 0.870156 0.035782 -vt 0.884650 0.028023 -vt 0.897065 0.021385 -vt 0.907191 0.005943 -vt 0.907224 0.015976 -vt 0.707420 0.156517 -vt 0.702412 0.165428 -vt 0.712721 0.150574 -vt 0.718346 0.167259 -vt 0.723524 0.167793 -vt 0.728655 0.171425 -vt 0.733532 0.177940 -vt 0.313004 0.025650 -vt 0.312868 0.034996 -vt 0.312738 0.046761 -vt 0.306642 0.099548 -vt 0.306580 0.117827 -vt 0.306514 0.137373 -vt 0.306437 0.157969 -vt 0.306345 0.179389 -vt 0.305929 0.201401 -vt 0.306259 0.223118 -vt 0.306291 0.243676 -vt 0.306314 0.262881 -vt 0.312202 0.316383 -vt 0.312283 0.329094 -vt 0.312376 0.339485 -vt 0.312481 0.347374 -vt 0.728600 0.505226 -vt 0.733398 0.499840 -vt 0.723587 0.507736 -vt 0.718409 0.507202 -vt 0.713278 0.503571 -vt 0.708403 0.497063 -vt 0.219162 0.682314 -vt 0.209846 0.682381 -vt 0.198105 0.682444 -vt 0.184158 0.682501 -vt 0.168232 0.682550 -vt 0.109179 0.677113 -vt 0.088536 0.677024 -vt 0.067109 0.676811 -vt 0.044988 0.677187 -vt 0.023176 0.677196 -vt 0.813791 0.084748 -vt 0.795620 0.094511 -vt 0.863354 0.051888 -vt 0.876479 0.044868 -vt 0.887722 0.038872 -vt 0.896922 0.033981 -vt 0.708536 0.175156 -vt 0.704000 0.183220 -vt 0.713333 0.169777 -vt 0.718388 0.178665 -vt 0.723253 0.179169 -vt 0.728075 0.182579 -vt 0.732656 0.188698 -vt 0.305817 0.036201 -vt 0.305696 0.044983 -vt 0.305580 0.056038 -vt 0.305132 0.309387 -vt 0.305206 0.321335 -vt 0.305290 0.331100 -vt 0.305387 0.338509 -vt 0.728024 0.496231 -vt 0.732533 0.491173 -vt 0.723312 0.498596 -vt 0.718447 0.498093 -vt 0.713625 0.494682 -vt 0.216801 0.675091 -vt 0.210537 0.675150 -vt 0.201786 0.675207 -vt 0.190750 0.675259 -vt 0.177647 0.675306 -vt 0.162682 0.675348 -vt 0.859729 0.062080 -vt 0.872062 0.055481 -vt 0.882627 0.049843 -vt 0.891271 0.045242 -vt 0.897844 0.041763 -vt 0.713676 0.181030 -vt 0.709167 0.186089 -vt 0.718388 0.190651 -vt 0.722940 0.191116 -vt 0.727453 0.194313 -vt 0.295300 0.041367 -vt 0.295187 0.047256 -vt 0.295076 0.055473 -vt 0.294971 0.065819 -vt 0.294872 0.078094 -vt 0.294779 0.092117 -vt 0.294693 0.107681 -vt 0.294619 0.124596 -vt 0.294552 0.142670 -vt 0.294498 0.161690 -vt 0.294456 0.181480 -vt 0.294428 0.201836 -vt 0.294416 0.221924 -vt 0.294420 0.240929 -vt 0.294437 0.258682 -vt 0.294468 0.275040 -vt 0.294514 0.289833 -vt 0.294569 0.302902 -vt 0.294638 0.314079 -vt 0.294716 0.323219 -vt 0.294804 0.330154 -vt 0.294901 0.334717 -vt 0.960477 0.705601 -vt 0.954439 0.696356 -vt 0.958922 0.695810 -vt 0.950919 0.706995 -vt 0.949978 0.697109 -vt 0.946374 0.707998 -vt 0.945723 0.698044 -vt 0.208286 0.664457 -vt 0.202423 0.664511 -vt 0.194233 0.664560 -vt 0.183907 0.664608 -vt 0.171646 0.664648 -vt 0.157642 0.664684 -vt 0.142090 0.664715 -vt 0.125183 0.664740 -vt 0.107121 0.664759 -vt 0.088100 0.664768 -vt 0.047947 0.664770 -vt 0.068310 0.664774 -vt 0.027855 0.664757 -vt 0.814150 0.098671 -vt 0.797404 0.107681 -vt 0.829807 0.090263 -vt 0.844234 0.082520 -vt 0.857288 0.075523 -vt 0.868829 0.069344 -vt 0.878715 0.064072 -vt 0.886804 0.059761 -vt 0.892955 0.056511 -vt 0.713980 0.192863 -vt 0.709761 0.197594 -vt 0.930368 0.015568 -vt 0.935349 0.000225 -vt 0.934605 0.015766 -vt 0.939850 0.000622 -vt 0.938798 0.016136 -vt 0.280558 0.053383 -vt 0.280449 0.058868 -vt 0.280344 0.066521 -vt 0.280243 0.076157 -vt 0.280148 0.087593 -vt 0.280058 0.100655 -vt 0.279976 0.115150 -vt 0.279903 0.130905 -vt 0.279840 0.147736 -vt 0.279787 0.165451 -vt 0.279747 0.183884 -vt 0.279720 0.202843 -vt 0.279709 0.221558 -vt 0.279711 0.239250 -vt 0.279728 0.255791 -vt 0.279757 0.271027 -vt 0.279800 0.284805 -vt 0.279854 0.296974 -vt 0.279919 0.307388 -vt 0.279993 0.315895 -vt 0.280077 0.322357 -vt 0.280170 0.326607 -vt 0.952518 0.682411 -vt 0.956692 0.681900 -vt 0.948361 0.683117 -vt 0.944400 0.683990 -vt 0.200333 0.649700 -vt 0.194874 0.649754 -vt 0.187244 0.649803 -vt 0.177628 0.649849 -vt 0.166206 0.649891 -vt 0.153164 0.649927 -vt 0.138680 0.649958 -vt 0.122932 0.649982 -vt 0.106110 0.650002 -vt 0.088390 0.650015 -vt 0.069958 0.650019 -vt 0.050995 0.650017 -vt 0.032280 0.650007 -vt 0.816106 0.114372 -vt 0.800509 0.122765 -vt 0.830689 0.106537 -vt 0.844126 0.099327 -vt 0.856285 0.092812 -vt 0.867035 0.087067 -vt 0.876243 0.082146 -vt 0.883778 0.078140 -vt 0.889507 0.075111 -vt 0.893300 0.073128 -vt 0.926389 0.000000 -vt 0.926261 0.015553 -vt 0.929736 0.035912 -vt 0.933661 0.036095 -vt 0.937545 0.036446 -vt 0.261051 0.066010 -vt 0.260946 0.071091 -vt 0.260845 0.078178 -vt 0.260748 0.087105 -vt 0.260654 0.097702 -vt 0.260569 0.109802 -vt 0.260489 0.123230 -vt 0.260418 0.137825 -vt 0.260265 0.186905 -vt 0.260239 0.204468 -vt 0.260227 0.221802 -vt 0.260229 0.238197 -vt 0.260244 0.253517 -vt 0.260271 0.267632 -vt 0.260313 0.280388 -vt 0.260365 0.291664 -vt 0.260426 0.301308 -vt 0.260498 0.309196 -vt 0.260578 0.315178 -vt 0.260668 0.319115 -vt 0.949804 0.663729 -vt 0.953671 0.663252 -vt 0.945955 0.664387 -vt 0.942286 0.665199 -vt 0.193008 0.630178 -vt 0.187950 0.630232 -vt 0.180885 0.630281 -vt 0.171974 0.630329 -vt 0.161396 0.630371 -vt 0.149311 0.630409 -vt 0.135891 0.630442 -vt 0.121304 0.630468 -vt 0.072231 0.630510 -vt 0.054665 0.630508 -vt 0.037327 0.630501 -vt 0.819770 0.134560 -vt 0.805322 0.142326 -vt 0.833279 0.127296 -vt 0.845726 0.120621 -vt 0.856990 0.114586 -vt 0.866948 0.109261 -vt 0.875479 0.104713 -vt 0.882460 0.100998 -vt 0.887769 0.098198 -vt 0.891283 0.096359 -vt 0.925931 0.035889 -vt 0.928862 0.061699 -vt 0.932474 0.061874 -vt 0.936049 0.062199 -vt 0.236130 0.079315 -vt 0.236029 0.083992 -vt 0.235931 0.090515 -vt 0.235836 0.098732 -vt 0.235746 0.108490 -vt 0.235662 0.119621 -vt 0.223703 0.207900 -vt 0.223762 0.223333 -vt 0.222290 0.237953 -vt 0.235411 0.276649 -vt 0.235458 0.287025 -vt 0.235518 0.295906 -vt 0.235588 0.303162 -vt 0.235664 0.308662 -vt 0.235750 0.312294 -vt 0.946181 0.639668 -vt 0.949739 0.639225 -vt 0.942639 0.640278 -vt 0.939261 0.641031 -vt 0.186367 0.605238 -vt 0.181713 0.605291 -vt 0.175205 0.605343 -vt 0.167004 0.605391 -vt 0.157265 0.605434 -vt 0.146141 0.605473 -vt 0.133789 0.605505 -vt 0.059021 0.605581 -vt 0.043060 0.605576 -vt 0.859664 0.141457 -vt 0.868832 0.136551 -vt 0.876684 0.132370 -vt 0.883111 0.128952 -vt 0.887999 0.126373 -vt 0.891234 0.124687 -vt 0.925360 0.061676 -vt 0.927706 0.093594 -vt 0.931005 0.093758 -vt 0.934272 0.094059 -vt 0.205139 0.093338 -vt 0.205042 0.097618 -vt 0.204948 0.103577 -vt 0.204859 0.111084 -vt 0.204445 0.273636 -vt 0.204491 0.283119 -vt 0.204548 0.291229 -vt 0.204613 0.297859 -vt 0.204687 0.302887 -vt 0.204769 0.306198 -vt 0.941529 0.609587 -vt 0.944779 0.609179 -vt 0.938293 0.610147 -vt 0.935209 0.610840 -vt 0.180470 0.574230 -vt 0.176216 0.574284 -vt 0.170269 0.574333 -vt 0.162777 0.574381 -vt 0.153877 0.574425 -vt 0.143715 0.574463 -vt 0.049538 0.574575 -vt 0.864568 0.174019 -vt 0.872944 0.169548 -vt 0.880119 0.165726 -vt 0.885991 0.162605 -vt 0.890457 0.160255 -vt 0.893415 0.158714 -vt 0.924506 0.093567 -vt 0.926226 0.132244 -vt 0.929214 0.132397 -vt 0.932171 0.132675 -vt 0.167425 0.108154 -vt 0.167334 0.112030 -vt 0.167246 0.117424 -vt 0.167160 0.124222 -vt 0.166862 0.287338 -vt 0.166924 0.293335 -vt 0.166992 0.297890 -vt 0.167068 0.300888 -vt 0.935730 0.572840 -vt 0.938672 0.572468 -vt 0.932800 0.573351 -vt 0.930008 0.573982 -vt 0.175373 0.536501 -vt 0.171516 0.536552 -vt 0.166134 0.536602 -vt 0.159351 0.536648 -vt 0.056816 0.536844 -vt 0.858999 0.219836 -vt 0.838279 0.230968 -vt 0.886041 0.205383 -vt 0.891358 0.202560 -vt 0.895403 0.200432 -vt 0.898082 0.199043 -vt 0.923329 0.132217 -vt 0.924383 0.178310 -vt 0.927059 0.178448 -vt 0.929707 0.178699 -vt 0.122335 0.123817 -vt 0.122252 0.127281 -vt 0.122169 0.132118 -vt 0.122091 0.138206 -vt 0.121985 0.149159 -vt 0.121809 0.284286 -vt 0.121866 0.289665 -vt 0.121931 0.293739 -vt 0.122002 0.296425 -vt 0.928662 0.528786 -vt 0.931297 0.528448 -vt 0.926040 0.529245 -vt 0.923540 0.529814 -vt 0.171131 0.491400 -vt 0.167683 0.491449 -vt 0.162861 0.491497 -vt 0.156784 0.491541 -vt 0.127656 0.491665 -vt 0.106232 0.491711 -vt 0.082825 0.491734 -vt 0.064953 0.491734 -vt 0.870472 0.264893 -vt 0.851913 0.274868 -vt 0.894707 0.251945 -vt 0.899469 0.249420 -vt 0.903093 0.247520 -vt 0.905493 0.246277 -vt 0.921789 0.178284 -vt 0.922131 0.232624 -vt 0.058185 0.147759 -vt 0.053896 0.146481 -vt 0.069210 0.162585 -vt 0.069212 0.178887 -vt 0.069180 0.282074 -vt 0.069176 0.286827 -vt 0.069172 0.290443 -vt 0.069168 0.292824 -vt 0.920237 0.476957 -vt 0.922576 0.476732 -vt 0.160522 0.438229 -vt 0.155167 0.438194 -vt 0.145501 0.438150 -vt 0.110564 0.438043 -vt 0.129527 0.438093 -vt 0.089837 0.438006 -vt 0.074097 0.437990 -vt 0.885285 0.317928 -vt 0.868865 0.326782 -vt 0.898964 0.310534 -vt 0.906529 0.306435 -vt 0.910718 0.304153 -vt 0.913898 0.302414 -vt 0.915995 0.301254 -vt 0.919837 0.232674 -vt 0.922572 0.251213 -vt 0.052669 0.168375 -vt 0.053066 0.183867 -vt 0.053367 0.201862 -vt 0.053551 0.221541 -vt 0.053599 0.241402 -vt 0.053507 0.259153 -vt 0.053289 0.273993 -vt 0.053072 0.282333 -vt 0.052887 0.286919 -vt 0.052681 0.290413 -vt 0.052454 0.292740 -vt 0.920816 0.460159 -vt 0.918563 0.460131 -vt 0.147301 0.419631 -vt 0.131817 0.419178 -vt 0.113773 0.418828 -vt 0.094011 0.418603 -vt 0.078976 0.418530 -vt 0.889266 0.337792 -vt 0.873597 0.346335 -vt 0.902273 0.330516 -vt 0.916361 0.322182 -vt 0.918303 0.320854 -vt 0.920393 0.251507 -vt 0.925407 0.266853 -vt 0.927474 0.266254 -vt 0.929516 0.265728 -vt 0.038833 0.164708 -vt 0.039969 0.175957 -vt 0.040930 0.190472 -vt 0.041664 0.207420 -vt 0.042117 0.226032 -vt 0.042243 0.244889 -vt 0.042030 0.261834 -vt 0.041509 0.276096 -vt 0.040047 0.292137 -vt 0.039501 0.294495 -vt 0.921421 0.446917 -vt 0.919293 0.446516 -vt 0.917174 0.446171 -vt 0.915154 0.445892 -vt 0.170246 0.406824 -vt 0.167702 0.406166 -vt 0.151256 0.404114 -vt 0.136744 0.403094 -vt 0.119740 0.402308 -vt 0.101025 0.401813 -vt 0.086712 0.401655 -vt 0.889491 0.356692 -vt 0.874574 0.364971 -vt 0.901849 0.349428 -vt 0.915189 0.340538 -vt 0.918303 0.251865 -vt 0.923401 0.267502 -vt 0.921475 0.268185 -vt 0.932136 0.278641 -vt 0.930339 0.279633 -vt 0.933905 0.277710 -vt 0.028504 0.174660 -vt 0.030312 0.184855 -vt 0.031844 0.198226 -vt 0.033012 0.213999 -vt 0.033736 0.231472 -vt 0.033941 0.249324 -vt 0.033606 0.265506 -vt 0.032782 0.279284 -vt 0.030458 0.295204 -vt 0.925807 0.437185 -vt 0.923889 0.436380 -vt 0.921963 0.435589 -vt 0.920053 0.434820 -vt 0.918238 0.434105 -vt 0.172112 0.394850 -vt 0.174255 0.395878 -vt 0.167579 0.393546 -vt 0.157397 0.391660 -vt 0.144032 0.390066 -vt 0.128198 0.388843 -vt 0.110608 0.388076 -vt 0.097031 0.387833 -vt 0.886018 0.374125 -vt 0.871819 0.382152 -vt 0.897805 0.366817 -vt 0.906622 0.360605 -vt 0.872755 0.680759 -vt 0.874735 0.691944 -vt 0.870206 0.695276 -vt 0.876149 0.678692 -vt 0.877492 0.690632 -vt 0.879054 0.677616 -vt 0.879730 0.690289 -vt 0.880233 0.705315 -vt 0.878685 0.705132 -vt 0.938437 0.288403 -vt 0.939850 0.287121 -vt 0.018940 0.181679 -vt 0.017626 0.180023 -vt 0.020711 0.185811 -vt 0.023071 0.194763 -vt 0.025075 0.206836 -vt 0.026604 0.221336 -vt 0.027554 0.237620 -vt 0.027825 0.254469 -vt 0.027392 0.269942 -vt 0.026318 0.283311 -vt 0.024669 0.293935 -vt 0.009628 0.792645 -vt 0.003401 0.795630 -vt 0.004892 0.789443 -vt 0.000525 0.794647 -vt 0.002150 0.788114 -vt 0.927835 0.428043 -vt 0.926190 0.426903 -vt 0.924572 0.425758 -vt 0.923049 0.424656 -vt 0.177979 0.386274 -vt 0.179626 0.387606 -vt 0.138624 0.378502 -vt 0.122236 0.377508 -vt 0.109417 0.377195 -vt 0.875892 0.391777 -vt 0.865732 0.397516 -vt 0.852940 0.713728 -vt 0.862131 0.703023 -vt 0.876592 0.705891 -vt 0.879563 0.721073 -vt 0.878149 0.721413 -vt 0.009916 0.193375 -vt 0.010044 0.198194 -vt 0.021709 0.229589 -vt 0.022802 0.244676 -vt 0.023140 0.256592 -vt 0.027831 0.809114 -vt 0.018068 0.799667 -vt 0.004015 0.780213 -vt 0.000000 0.787608 -vt 0.001977 0.779257 -vt 0.931695 0.420019 -vt 0.930411 0.418612 -vt 0.929224 0.417236 -vt 0.184872 0.380110 -vt 0.186008 0.381626 -vt 0.182762 0.378637 -vt 0.876216 0.722610 -vt 0.919923 0.760170 -vt 0.843462 0.762459 -vt 0.879250 0.737629 -vt 0.006077 0.771106 -vt 0.005650 0.207855 -vt 0.067383 0.839774 -vt 0.004160 0.769771 -vt 0.938241 0.414606 -vt 0.937273 0.413067 -vt 0.936401 0.411556 -vt 0.935658 0.410131 -vt 0.190735 0.374460 -vt 0.192360 0.375990 -vt 0.188244 0.372993 -vt 0.914838 0.767943 -vt 0.921215 0.773939 -vt 0.916456 0.780468 -vt 0.928026 0.765534 -vt 0.936574 0.755653 -vt 0.946489 0.744758 -vt 0.851858 0.770579 -vt 0.845589 0.779495 -vt 0.859848 0.759678 -vt 0.867036 0.750424 -vt 0.873038 0.743310 -vt 0.879165 0.754425 -vt 0.004314 0.225374 -vt 0.009678 0.226795 -vt 0.011742 0.232040 -vt 0.065807 0.826069 -vt 0.059720 0.818448 -vt 0.052071 0.809240 -vt 0.043198 0.798870 -vt 0.034129 0.788542 -vt 0.025573 0.779054 -vt 0.017939 0.770844 -vt 0.011638 0.764356 -vt 0.002821 0.769154 -vt 0.947530 0.407819 -vt 0.946934 0.406309 -vt 0.914599 0.783506 -vt 0.918279 0.793041 -vt 0.916565 0.795425 -vt 0.922731 0.787528 -vt 0.929109 0.780140 -vt 0.937115 0.771226 -vt 0.946407 0.761187 -vt 0.853737 0.787561 -vt 0.847881 0.795937 -vt 0.861201 0.777131 -vt 0.867894 0.768077 -vt 0.873461 0.760883 -vt 0.068252 0.816558 -vt 0.064309 0.811724 -vt 0.008098 0.240115 -vt 0.058615 0.804948 -vt 0.051471 0.796597 -vt 0.043195 0.787049 -vt 0.034743 0.777409 -vt 0.026770 0.768420 -vt 0.019654 0.760500 -vt 0.013777 0.754078 -vt 0.002762 0.249908 -vt 0.001040 0.251201 -vt 0.006739 0.251953 -vt 0.960477 0.403591 -vt 0.960239 0.402151 -vt 0.948016 0.404310 -vt 0.960098 0.400785 -vt 0.951752 0.401539 -vt 0.003893 0.262718 -vt 0.002190 0.264530 -vt 0.948875 0.789135 -vt 0.005199 0.262283 -vt 0.688345 0.901905 -vt 0.697757 0.903893 -vt 0.686554 0.903950 -vt 0.006468 0.262627 -vt 0.697836 0.905262 -vt 0.686275 0.905293 -vt 0.686758 0.906509 -vt 0.924612 0.831541 -vt 0.921796 0.833534 -vt 0.929250 0.827487 -vt 0.935412 0.821627 -vt 0.942837 0.814195 -vt 0.949030 0.807785 -vt 0.863670 0.832555 -vt 0.857131 0.842191 -vt 0.869630 0.823434 -vt 0.874716 0.815289 -vt 0.007557 0.273232 -vt 0.006059 0.275517 -vt 0.008703 0.272430 -vt 0.009815 0.272392 -vt 0.068195 0.776395 -vt 0.065557 0.775203 -vt 0.061095 0.771791 -vt 0.055210 0.766364 -vt 0.048163 0.759128 -vt 0.040462 0.750575 -vt 0.033022 0.741730 -vt 0.026228 0.733094 -vt 0.020418 0.725111 -vt 0.015932 0.718238 -vt 0.013759 0.714375 -vt 0.679647 0.899439 -vt 0.677355 0.901299 -vt 0.676688 0.902519 -vt 0.642806 0.908194 -vt 0.634100 0.905512 -vt 0.634876 0.904463 -vt 0.925452 0.845450 -vt 0.923353 0.846457 -vt 0.922359 0.846539 -vt 0.930034 0.842230 -vt 0.936116 0.837048 -vt 0.943433 0.830099 -vt 0.949551 0.823887 -vt 0.866445 0.848448 -vt 0.860039 0.858084 -vt 0.872236 0.839085 -vt 0.877130 0.830476 -vt 0.880842 0.823099 -vt 0.000042 0.268654 -vt 0.003809 0.281532 -vt 0.908888 0.677580 -vt 0.903051 0.683407 -vt 0.902141 0.682486 -vt 0.069067 0.766488 -vt 0.048748 0.751441 -vt 0.041166 0.743109 -vt 0.033857 0.734297 -vt 0.027201 0.725519 -vt 0.022944 0.719427 -vt 0.685223 0.896635 -vt 0.669026 0.897503 -vt 0.628534 0.898674 -vt 0.627562 0.899519 -vt 0.626555 0.900269 -vt 0.924480 0.856831 -vt 0.944304 0.842402 -vt 0.950346 0.836357 -vt 0.868947 0.860757 -vt 0.862670 0.870345 -vt 0.874584 0.851270 -vt 0.000803 0.297226 -vt 0.001958 0.289740 -vt 0.897715 0.689713 -vt 0.896667 0.688984 -vt 0.019632 0.290092 -vt 0.903965 0.684240 -vt 0.899108 0.690555 -vt 0.623639 0.891388 -vt 0.622553 0.892031 -vt 0.054474 0.749961 -vt 0.663139 0.890818 -vt 0.621441 0.892588 -vt 0.927219 0.864429 -vt 0.925776 0.864531 -vt 0.027912 0.294952 -vt 0.027190 0.298879 -vt 0.812625 0.685501 -vt 0.015097 0.341171 -vt 0.893500 0.697126 -vt 0.892379 0.696568 -vt 0.894615 0.697613 -vt 0.067580 0.752965 -vt 0.065756 0.752361 -vt 0.895710 0.698027 -vt 0.738734 0.870976 -vt 0.737882 0.877391 -vt 0.733037 0.870893 -vt 0.724986 0.871299 -vt 0.714901 0.872197 -vt 0.703935 0.873488 -vt 0.692996 0.875051 -vt 0.682641 0.876789 -vt 0.673421 0.878610 -vt 0.658740 0.878540 -vt 0.658853 0.883020 -vt 0.620031 0.883144 -vt 0.618891 0.883614 -vt 0.617727 0.884003 -vt 0.616555 0.884314 -vt 0.804169 0.686520 -vt 0.802084 0.694465 -vt 0.799727 0.694834 -vt 0.806693 0.685965 -vt 0.807773 0.694230 -vt 0.815825 0.694435 -vt 0.825928 0.695094 -vt 0.024569 0.337539 -vt 0.024508 0.345703 -vt 0.024881 0.326485 -vt 0.025441 0.315991 -vt 0.026220 0.306606 -vt 0.036073 0.302228 -vt 0.890347 0.705126 -vt 0.889193 0.704712 -vt 0.891495 0.705477 -vt 0.892618 0.705761 -vt 0.679762 0.869417 -vt 0.670615 0.871012 -vt 0.617552 0.874464 -vt 0.616392 0.874786 -vt 0.655991 0.874649 -vt 0.615211 0.875040 -vt 0.614023 0.875219 -vt 0.034403 0.329536 -vt 0.034809 0.319178 -vt 0.035372 0.309893 -vt 0.044824 0.304263 -vt 0.889338 0.713406 -vt 0.890470 0.713581 -vt 0.677554 0.861931 -vt 0.668590 0.863325 -vt 0.661245 0.864619 -vt 0.657385 0.861906 -vt 0.616050 0.865862 -vt 0.614890 0.866062 -vt 0.613710 0.866198 -vt 0.612526 0.866266 -vt 0.043697 0.331161 -vt 0.043972 0.320970 -vt 0.044352 0.311822 -vt 0.888063 0.720840 -vt 0.889192 0.720913 -vt 0.732314 0.848705 -vt 0.614233 0.857787 -vt 0.615380 0.857273 -vt 0.613069 0.857437 -vt 0.611905 0.857059 -vt 0.049878 0.297610 -vt 0.887573 0.727336 -vt 0.886426 0.727623 -vt 0.888691 0.727180 -vt 0.729938 0.842696 -vt 0.731338 0.842472 -vt 0.725356 0.843368 -vt 0.822165 0.729977 -vt 0.614414 0.826596 -vt 0.613253 0.827468 -vt 0.799573 0.759539 -vt 0.801462 0.759230 -vt 0.885437 0.797768 -vt 0.728965 0.770115 -vt 0.141409 0.291755 -vt 0.711360 0.752997 -vt 0.702420 0.753935 -vt 0.171638 0.333666 -vt 0.883960 0.849826 -vt 0.886403 0.799551 -vt 0.882761 0.882640 -vt 0.806879 0.880867 -vt 0.827199 0.882894 -vt 0.211439 0.327904 -vt 0.211437 0.321477 -vt 0.211417 0.312748 -vt 0.211379 0.304445 -vt 0.215902 0.290040 -vt 0.621731 0.693392 -vt 0.727570 0.687317 -vt 0.808396 0.885817 -vt 0.810169 0.886189 -vt 0.814499 0.886861 -vt 0.820680 0.887622 -vt 0.828472 0.888435 -vt 0.216850 0.326180 -vt 0.216832 0.319864 -vt 0.216723 0.311318 -vt 0.216525 0.303214 -vt 0.216248 0.295976 -vt 0.883995 0.886843 -vt 0.726676 0.684103 -vt 0.728510 0.683483 -vt 0.722334 0.685126 -vt 0.716218 0.686201 -vt 0.708566 0.687268 -vt 0.700256 0.688205 -vt 0.691974 0.688945 -vt 0.684139 0.689462 -vt 0.677169 0.689728 -vt 0.623482 0.688499 -vt 0.811154 0.890989 -vt 0.812989 0.891554 -vt 0.817340 0.892461 -vt 0.823474 0.893385 -vt 0.831150 0.894271 -vt 0.222427 0.323021 -vt 0.222389 0.316833 -vt 0.222183 0.308509 -vt 0.221813 0.300654 -vt 0.221294 0.293679 -vt 0.887046 0.890363 -vt 0.843762 0.888699 -vt 0.845784 0.890786 -vt 0.842940 0.891808 -vt 0.724079 0.681465 -vt 0.728456 0.680261 -vt 0.717997 0.682660 -vt 0.710451 0.683777 -vt 0.702309 0.684689 -vt 0.694237 0.685332 -vt 0.686644 0.685689 -vt 0.679937 0.685742 -vt 0.674543 0.685475 -vt 0.123116 0.953924 -vt 0.817982 0.897100 -vt 0.821254 0.897850 -vt 0.227514 0.276855 -vt 0.720564 0.679059 -vt 0.726622 0.677770 -vt 0.667883 0.686704 -vt 0.632418 0.679072 -vt 0.125828 0.944393 -vt 0.830645 0.903839 -vt 0.841704 0.905281 -vt 0.232878 0.270064 -vt 0.125387 0.937736 -vt 0.123259 0.936907 -vt 0.842032 0.901100 -vt 0.862357 0.884331 -vt 0.633797 0.676311 -vt 0.120035 0.934541 -vt 0.115931 0.930792 -vt 0.111260 0.933030 -vt 0.111128 0.925799 -vt 0.107236 0.928525 -vt 0.107193 0.921360 -vt 0.978222 0.695854 -vt 0.974580 0.698498 -vt 0.973010 0.690023 -vt 0.236450 0.298115 -vt 0.235908 0.290710 -vt 0.235074 0.284091 -vt 0.233984 0.278613 -vt 0.844211 0.905940 -vt 0.842091 0.906958 -vt 0.847505 0.903533 -vt 0.851734 0.899883 -vt 0.856705 0.895133 -vt 0.862056 0.889614 -vt 0.867157 0.883980 -vt 0.867569 0.878523 -vt 0.871749 0.878545 -vt 0.695426 0.674833 -vt 0.701792 0.675025 -vt 0.690218 0.674233 -vt 0.686624 0.673265 -vt 0.687708 0.670977 -vt 0.687129 0.670367 -vt 0.640152 0.672632 -vt 0.637952 0.672930 -vt 0.125083 0.930292 -vt 0.122980 0.929166 -vt 0.119823 0.926585 -vt 0.115806 0.922715 -vt 0.111104 0.917701 -vt 0.107259 0.913323 -vt 0.977882 0.687847 -vt 0.972781 0.682190 -vt 0.968310 0.684307 -vt 0.968185 0.676728 -vt 0.965359 0.680429 -vt 0.964317 0.671737 -vt 0.238565 0.278354 -vt 0.237367 0.273207 -vt 0.237712 0.259687 -vt 0.844320 0.912034 -vt 0.842218 0.913375 -vt 0.847559 0.909380 -vt 0.851706 0.905580 -vt 0.856577 0.900790 -vt 0.871293 0.884726 -vt 0.875060 0.880108 -vt 0.695679 0.671302 -vt 0.699364 0.671917 -vt 0.692434 0.670207 -vt 0.124875 0.922211 -vt 0.122807 0.920748 -vt 0.642497 0.670216 -vt 0.119721 0.917924 -vt 0.115794 0.913908 -vt 0.111194 0.908859 -vt 0.107441 0.904540 -vt 0.968134 0.668418 -vt 0.964355 0.663769 -vt 0.962155 0.668682 -vt 0.961514 0.659926 -vt 0.240053 0.267311 -vt 0.698605 0.667870 -vt 0.700720 0.668867 -vt 0.700984 0.665314 -vt 0.702337 0.665716 -vt 0.691082 0.667631 -vt 0.697853 0.665125 -vt 0.241636 0.260406 -vt 0.238871 0.252160 -vt 0.241680 0.252846 -vt 0.241606 0.241821 -vt 0.842437 0.927577 -vt 0.704681 0.666079 -vt 0.705567 0.666803 -vt 0.124979 0.905042 -vt 0.843752 0.939498 -vt 0.842566 0.940908 -vt 0.846341 0.936552 -vt 0.849883 0.932633 -vt 0.854198 0.927940 -vt 0.872462 0.908718 -vt 0.868519 0.912774 -vt 0.875603 0.905559 -vt 0.877699 0.903545 -vt 0.715432 0.665007 -vt 0.716167 0.665741 -vt 0.715502 0.663216 -vt 0.720553 0.664316 -vt 0.125642 0.890828 -vt 0.124592 0.889423 -vt 0.122155 0.886316 -vt 0.118837 0.882209 -vt 0.114791 0.877292 -vt 0.110196 0.871787 -vt 0.970483 0.637633 -vt 0.966532 0.633553 -vt 0.963368 0.630362 -vt 0.961212 0.628289 -vt 0.240374 0.232582 -vt 0.239584 0.232086 -vt 0.843988 0.950408 -vt 0.842816 0.952045 -vt 0.846563 0.947161 -vt 0.850079 0.943010 -vt 0.854364 0.938168 -vt 0.861720 0.930229 -vt 0.868590 0.923132 -vt 0.872510 0.919296 -vt 0.875634 0.916427 -vt 0.877717 0.914757 -vt 0.724544 0.665201 -vt 0.725127 0.665926 -vt 0.126299 0.878798 -vt 0.125261 0.877158 -vt 0.724956 0.663408 -vt 0.122863 0.873755 -vt 0.119585 0.869421 -vt 0.115578 0.864355 -vt 0.111019 0.858798 -vt 0.976946 0.631277 -vt 0.970684 0.624806 -vt 0.966758 0.620950 -vt 0.963613 0.618052 -vt 0.961481 0.616341 -vt 0.991859 0.865215 -vt 0.237339 0.224655 -vt 0.234187 0.228665 -vt 0.846882 0.955619 -vt 0.844329 0.959198 -vt 0.850369 0.951212 -vt 0.205391 0.920381 -vt 0.732888 0.664796 -vt 0.726852 0.661816 -vt 0.735670 0.662980 -vt 0.125930 0.866892 -vt 0.123576 0.863165 -vt 0.120343 0.858577 -vt 0.984198 0.870840 -vt 0.847282 0.962154 -vt 0.845374 0.965013 -vt 0.850733 0.957496 -vt 0.878326 0.930987 -vt 0.738880 0.661589 -vt 0.124289 0.854512 -vt 0.121105 0.849676 -vt 0.978621 0.877195 -vt 0.977565 0.876607 -vt 0.111575 0.837221 -vt 0.962051 0.598326 -vt 0.862500 0.948781 -vt 0.855895 0.956207 -vt 0.871305 0.940499 -vt 0.867128 0.944177 -vt 0.874824 0.937919 -vt 0.877450 0.936624 -vt 0.878605 0.936508 -vt 0.191908 0.911367 -vt 0.192948 0.910746 -vt 0.115816 0.834367 -vt 0.112428 0.830133 -vt 0.974088 0.599961 -vt 0.969912 0.596269 -vt 0.978710 0.604579 -vt 0.966386 0.593672 -vt 0.963743 0.592360 -vt 0.962589 0.592237 -vt 0.975362 0.881741 -vt 0.223139 0.211385 -vt 0.223472 0.219329 -vt 0.222876 0.224838 -vt 0.222279 0.234709 -vt 0.222049 0.245484 -vt 0.225508 0.247863 -vt 0.222174 0.251890 -vt 0.225681 0.254343 -vt 0.222492 0.257260 -vt 0.226110 0.259822 -vt 0.222978 0.261330 -vt 0.877800 0.940434 -vt 0.875168 0.941512 -vt 0.194041 0.910235 -vt 0.192483 0.905912 -vt 0.748894 0.676973 -vt 0.749881 0.674416 -vt 0.748395 0.678292 -vt 0.754079 0.673233 -vt 0.759487 0.672024 -vt 0.769228 0.670318 -vt 0.773876 0.663900 -vt 0.778158 0.669058 -vt 0.483015 0.887591 -vt 0.482592 0.893708 -vt 0.477085 0.890035 -vt 0.477391 0.895506 -vt 0.472033 0.891894 -vt 0.473257 0.896593 -vt 0.468099 0.893085 -vt 0.966433 0.588667 -vt 0.964344 0.587903 -vt 0.974226 0.881462 -vt 0.218944 0.217868 -vt 0.976155 0.884141 -vt 0.974298 0.887297 -vt 0.218562 0.223411 -vt 0.218179 0.233304 -vt 0.218312 0.255656 -vt 0.218624 0.259637 -vt 0.219027 0.262021 -vt 0.191579 0.901196 -vt 0.752527 0.679525 -vt 0.756806 0.678592 -vt 0.762274 0.677593 -vt 0.772058 0.676098 -vt 0.780946 0.674929 -vt 0.467854 0.887183 -vt 0.464073 0.888500 -vt 0.465564 0.893513 -vt 0.461721 0.889097 -vt 0.973147 0.887224 -vt 0.213547 0.232287 -vt 0.213597 0.254766 -vt 0.213748 0.258766 -vt 0.213948 0.261214 -vt 0.191267 0.896036 -vt 0.751067 0.687280 -vt 0.758540 0.684438 -vt 0.764057 0.683631 -vt 0.774056 0.682306 -vt 0.782393 0.681284 -vt 0.464601 0.881689 -vt 0.460990 0.883105 -vt 0.458824 0.883846 -vt 0.973117 0.893248 -vt 0.206651 0.236369 -vt 0.782679 0.687742 -vt 0.462747 0.875569 -vt 0.470113 0.872398 -vt 0.459136 0.877096 -vt 0.456968 0.877980 -vt 0.973988 0.899330 -vt 0.978631 0.911338 -vt 0.782429 0.697919 -vt 0.460291 0.865656 -vt 0.464989 0.863641 -vt 0.470589 0.861205 -vt 0.456787 0.867162 -vt 0.455191 0.867849 -vt 0.975920 0.909207 -vt 0.454479 0.868194 -vt 0.448558 0.845856 -vt 0.979998 0.931889 -vt 0.755101 0.760022 -vt 0.129287 0.263847 -vt 0.436189 0.800949 -vt 0.122719 0.263871 -vt 0.205062 0.805677 -vt 0.990575 0.988953 -vt 0.118505 0.226265 -vt 0.120773 0.267125 -vt 0.205391 0.803598 -vt 0.204436 0.803383 -vt 0.203484 0.803185 -vt 0.989951 0.989105 -vt 0.115093 0.233521 -vt 0.608673 0.974940 -vt 0.607490 0.974468 -vt 0.606244 0.972007 -vt 0.753220 0.781567 -vt 0.967283 0.219551 -vt 0.968319 0.218154 -vt 0.607166 0.964536 -vt 0.462242 0.771408 -vt 0.458960 0.772325 -vt 0.451973 0.774367 -vt 0.966251 0.238792 -vt 0.725959 0.792632 -vt 0.967484 0.259742 -vt 0.966265 0.262109 -vt 0.605638 0.933915 -vt 0.606832 0.932783 -vt 0.737952 0.796359 -vt 0.104295 0.273002 -vt 0.610306 0.936117 -vt 0.716235 0.796561 -vt 0.966547 0.267439 -vt 0.970046 0.267135 -vt 0.971010 0.268068 -vt 0.102792 0.276212 -vt 0.606888 0.925754 -vt 0.105084 0.310331 -vt 0.609740 0.927790 -vt 0.605898 0.926708 -vt 0.990138 0.000034 -vt 0.990852 0.001985 -vt 0.990184 0.001937 -vt 0.967170 0.269161 -vt 0.100802 0.276417 -vt 0.991394 0.000172 -vt 0.991494 0.002146 -vt 0.101532 0.313383 -vt 0.101569 0.316778 -vt 0.105283 0.315679 -vt 0.101584 0.318883 -vt 0.792959 0.861391 -vt 0.792447 0.865315 -vt 0.792076 0.861479 -vt 0.793720 0.865270 -vt 0.793622 0.861355 -vt 0.708740 0.800520 -vt 0.706100 0.804441 -vt 0.989580 0.001670 -vt 0.792387 0.856538 -vt 0.793004 0.856243 -vt 0.791740 0.856655 -vt 0.486369 0.739498 -vt 0.791174 0.857209 -vt 0.988954 0.011175 -vt 0.988241 0.011400 -vt 0.989013 0.004700 -vt 0.989560 0.011423 -vt 0.091385 0.277544 -vt 0.987581 0.011497 -vt 0.069283 0.318963 -vt 0.787238 0.825121 -vt 0.786821 0.826389 -vt 0.056652 0.279768 -vt 0.461645 0.715386 -vt 0.976117 0.062486 -vt 0.034505 0.320791 -vt 0.711755 0.871693 -vt 0.734610 0.870077 -vt 0.454073 0.688375 -vt 0.029829 0.285255 -vt 0.031044 0.322357 -vt 0.783827 0.790467 -vt 0.710709 0.876224 -vt 0.709876 0.875986 -vt 0.454704 0.683051 -vt 0.026862 0.289480 -vt 0.025248 0.290190 -vt 0.981053 0.079111 -vt 0.786219 0.787191 -vt 0.785475 0.786692 -vt 0.947591 0.274515 -vt 0.942178 0.271257 -vt 0.707682 0.880098 -vt 0.707001 0.879715 -vt 0.805013 0.906996 -vt 0.469332 0.670872 -vt 0.024144 0.293466 -vt 0.025241 0.296353 -vt 0.808358 0.905345 -vt 0.981089 0.080754 -vt 0.026588 0.304256 -vt 0.961075 0.284489 -vt 0.954433 0.280768 -vt 0.954167 0.276895 -vt 0.947323 0.270487 -vt 0.943612 0.267626 -vt 0.788470 0.784046 -vt 0.789289 0.782370 -vt 0.788755 0.782067 -vt 0.707048 0.884275 -vt 0.715112 0.884735 -vt 0.245812 0.852901 -vt 0.247987 0.856009 -vt 0.243003 0.861826 -vt 0.808645 0.920789 -vt 0.807910 0.921082 -vt 0.023730 0.302325 -vt 0.239956 0.865772 -vt 0.704339 0.887547 -vt 0.807844 0.927397 -vt 0.965452 0.280272 -vt 0.794019 0.778496 -vt 0.793737 0.777958 -vt 0.237900 0.874541 -vt 0.237681 0.880922 -vt 0.794961 0.940211 -vt 0.017696 0.307358 -vt 0.965574 0.266452 -vt 0.962644 0.261621 -vt 0.958556 0.257449 -vt 0.248034 0.881850 -vt 0.964607 0.263626 -vt 0.951454 0.250206 -vt 0.944907 0.243534 -vt 0.243586 0.892641 -vt 0.239576 0.896875 -vt 0.247973 0.888013 -vt 0.803889 0.949989 -vt 0.797598 0.956931 -vt 0.807129 0.946420 -vt 0.965672 0.259670 -vt 0.964533 0.258728 -vt 0.016599 0.321116 -vt 0.018707 0.320569 -vt 0.940071 0.233563 -vt 0.237644 0.904776 -vt 0.236500 0.905751 -vt 0.808674 0.950475 -vt 0.019060 0.327048 -vt 0.017450 0.325879 -vt 0.019274 0.324821 -vt 0.965635 0.254016 -vt 0.964505 0.253293 -vt 0.940345 0.228360 -vt 0.237719 0.911111 -vt 0.236580 0.911850 -vt 0.808528 0.957154 -vt 0.796293 0.971967 -vt 0.965628 0.248149 -vt 0.964536 0.247627 -vt 0.018970 0.330740 -vt 0.020712 0.329399 -vt 0.961557 0.245321 -vt 0.955065 0.239299 -vt 0.947985 0.231893 -vt 0.942332 0.225228 -vt 0.940371 0.222558 -vt 0.237871 0.917567 -vt 0.236715 0.918105 -vt 0.240819 0.915208 -vt 0.246810 0.909497 -vt 0.801572 0.973119 -vt 0.796616 0.978843 -vt 0.806994 0.966185 -vt 0.808830 0.963471 -vt 0.985219 0.105814 -vt 0.982026 0.108721 -vt 0.978885 0.110670 -vt 0.024179 0.336675 -vt 0.023638 0.337742 -vt 0.660136 0.886994 -vt 0.974854 0.587341 -vt 0.970814 0.585643 -vt 0.968452 0.582892 -vt 0.659095 0.887334 -vt 0.018101 0.332779 -vt 0.028101 0.342259 -vt 0.027329 0.344805 -vt 0.978938 0.111746 -vt 0.961587 0.235026 -vt 0.666132 0.883478 -vt 0.967184 0.582869 -vt 0.241595 0.927622 -vt 0.796696 0.992628 -vt 0.022599 0.346251 -vt 0.028545 0.341469 -vt 0.975483 0.114748 -vt 0.976347 0.115249 -vt 0.965715 0.232819 -vt 0.964153 0.232374 -vt 0.961815 0.230957 -vt 0.957362 0.227273 -vt 0.682540 0.877707 -vt 0.675621 0.881486 -vt 0.672395 0.878858 -vt 0.662731 0.880710 -vt 0.657544 0.882146 -vt 0.655263 0.883104 -vt 0.654351 0.883724 -vt 0.964989 0.578712 -vt 0.239703 0.934284 -vt 0.238149 0.934751 -vt 0.242011 0.932834 -vt 0.246400 0.929077 -vt 0.021544 0.362167 -vt 0.025726 0.359958 -vt 0.025604 0.366219 -vt 0.021692 0.355912 -vt 0.026476 0.350142 -vt 0.032330 0.344905 -vt 0.031782 0.347544 -vt 0.032663 0.344002 -vt 0.974325 0.119238 -vt 0.973403 0.118921 -vt 0.965623 0.229544 -vt 0.964468 0.229210 -vt 0.688178 0.874249 -vt 0.688231 0.877507 -vt 0.685656 0.874172 -vt 0.680070 0.874489 -vt 0.669921 0.875645 -vt 0.660106 0.877304 -vt 0.654735 0.878524 -vt 0.652274 0.879317 -vt 0.651306 0.879786 -vt 0.962032 0.574641 -vt 0.962927 0.574342 -vt 0.240198 0.938152 -vt 0.239049 0.938503 -vt 0.224857 0.714397 -vt 0.223102 0.718587 -vt 0.220583 0.718525 -vt 0.226812 0.714398 -vt 0.228657 0.719227 -vt 0.030625 0.362979 -vt 0.030537 0.369240 -vt 0.031166 0.353032 -vt 0.037458 0.345367 -vt 0.973199 0.123830 -vt 0.972229 0.123715 -vt 0.687441 0.870115 -vt 0.686450 0.870079 -vt 0.683901 0.870178 -vt 0.678184 0.870669 -vt 0.667985 0.871846 -vt 0.658134 0.873264 -vt 0.652820 0.874186 -vt 0.960863 0.569380 -vt 0.961771 0.569280 -vt 0.219556 0.718637 -vt 0.218082 0.723584 -vt 0.217169 0.723574 -vt 0.220440 0.723796 -vt 0.225731 0.724545 -vt 0.036365 0.364979 -vt 0.036322 0.370880 -vt 0.036645 0.354917 -vt 0.972371 0.128445 -vt 0.973199 0.129168 -vt 0.685922 0.864949 -vt 0.686913 0.864882 -vt 0.683528 0.865166 -vt 0.216196 0.729361 -vt 0.223087 0.730183 -vt 0.042366 0.370874 -vt 0.042371 0.367142 -vt 0.227598 0.730953 -vt 0.042394 0.362039 -vt 0.042434 0.357140 -vt 0.042485 0.352712 -vt 0.685784 0.850636 -vt 0.686715 0.850481 -vt 0.974613 0.143502 -vt 0.215772 0.738316 -vt 0.963029 0.533153 -vt 0.215248 0.759857 -vt 0.214309 0.759775 -vt 0.217634 0.760158 -vt 0.221118 0.760697 -vt 0.225570 0.761446 -vt 0.072748 0.367624 -vt 0.072750 0.363943 -vt 0.072778 0.358901 -vt 0.072839 0.354066 -vt 0.072946 0.349699 -vt 0.683368 0.737110 -vt 0.684285 0.736966 -vt 0.970310 0.436906 -vt 0.972022 0.413743 -vt 0.170357 0.358071 -vt 0.170337 0.344837 -vt 0.683937 0.714466 -vt 0.683068 0.708152 -vt 0.208964 0.880241 -vt 0.192602 0.355698 -vt 0.683976 0.707735 -vt 0.973016 0.406173 -vt 0.972046 0.405889 -vt 0.206518 0.887135 -vt 0.205621 0.886950 -vt 0.208810 0.887569 -vt 0.214001 0.888495 -vt 0.199886 0.354630 -vt 0.199855 0.348670 -vt 0.983781 0.286096 -vt 0.983143 0.288799 -vt 0.683840 0.705551 -vt 0.684780 0.705341 -vt 0.681494 0.705934 -vt 0.676245 0.706590 -vt 0.666655 0.707552 -vt 0.206555 0.890329 -vt 0.207486 0.890588 -vt 0.209800 0.891101 -vt 0.215018 0.892060 -vt 0.203207 0.353050 -vt 0.204089 0.334636 -vt 0.204425 0.327347 -vt 0.337427 0.851753 -vt 0.337094 0.850254 -vt 0.338935 0.849935 -vt 0.685502 0.703256 -vt 0.321343 0.371125 -vt 0.322663 0.374010 -vt 0.974854 0.400150 -vt 0.217306 0.895622 -vt 0.206295 0.350235 -vt 0.338178 0.847694 -vt 0.339812 0.847182 -vt 0.206606 0.324242 -vt 0.340730 0.847546 -vt 0.687309 0.701094 -vt 0.322322 0.376990 -vt 0.324725 0.378162 -vt 0.220552 0.899116 -vt 0.209166 0.346481 -vt 0.337139 0.845125 -vt 0.681004 0.699337 -vt 0.685380 0.698942 -vt 0.687518 0.699352 -vt 0.676422 0.699590 -vt 0.219921 0.901642 -vt 0.224421 0.902482 -vt 0.338829 0.838442 -vt 0.230330 0.905965 -vt 0.225781 0.905169 -vt 0.335836 0.841147 -vt 0.686445 0.695228 -vt 0.688291 0.695862 -vt 0.680363 0.694902 -vt 0.216408 0.331619 -vt 0.333054 0.392054 -vt 0.335433 0.398046 -vt 0.332711 0.399780 -vt 0.335667 0.403257 -vt 0.218429 0.327152 -vt 0.217914 0.320254 -vt 0.338021 0.406202 -vt 0.217011 0.303393 -vt 0.337385 0.833863 -vt 0.334118 0.837545 -vt 0.333045 0.843970 -vt 0.331077 0.841198 -vt 0.683234 0.692027 -vt 0.686392 0.692760 -vt 0.672976 0.690200 -vt 0.426624 0.673867 -vt 0.338519 0.412172 -vt 0.432091 0.687741 -vt 0.348156 0.751103 -vt 0.349529 0.747179 -vt 0.336690 0.829026 -vt 0.333465 0.832679 -vt 0.330508 0.836241 -vt 0.327962 0.839537 -vt 0.328721 0.844242 -vt 0.325953 0.842387 -vt 0.683140 0.688796 -vt 0.497858 0.761237 -vt 0.497692 0.756011 -vt 0.498398 0.761157 -vt 0.325709 0.410712 -vt 0.335513 0.823810 -vt 0.424557 0.686252 -vt 0.433203 0.697488 -vt 0.332338 0.827474 -vt 0.329469 0.830934 -vt 0.327039 0.834032 -vt 0.325182 0.836611 -vt 0.684652 0.685562 -vt 0.685328 0.686592 -vt 0.424100 0.692105 -vt 0.336424 0.424865 -vt 0.224304 0.295998 -vt 0.433472 0.704056 -vt 0.335015 0.817372 -vt 0.331840 0.821020 -vt 0.328967 0.824413 -vt 0.326532 0.827401 -vt 0.324673 0.829848 -vt 0.497612 0.747172 -vt 0.498163 0.747296 -vt 0.691716 0.683498 -vt 0.324401 0.420020 -vt 0.335304 0.432872 -vt 0.423338 0.700150 -vt 0.425653 0.702886 -vt 0.428484 0.706301 -vt 0.430897 0.709285 -vt 0.434339 0.705412 -vt 0.345735 0.797684 -vt 0.224555 0.280663 -vt 0.346298 0.797279 -vt 0.324189 0.822852 -vt 0.323368 0.427870 -vt 0.690463 0.681385 -vt 0.694929 0.680418 -vt 0.334201 0.441200 -vt 0.339241 0.800533 -vt 0.226458 0.283310 -vt 0.345396 0.792101 -vt 0.346031 0.791332 -vt 0.224903 0.275998 -vt 0.323878 0.816702 -vt 0.699474 0.679756 -vt 0.699645 0.681419 -vt 0.333468 0.447914 -vt 0.431623 0.726273 -vt 0.644056 0.899716 -vt 0.432914 0.727501 -vt 0.344288 0.786945 -vt 0.344817 0.786133 -vt 0.332039 0.802509 -vt 0.326171 0.809428 -vt 0.323156 0.812797 -vt 0.321907 0.814054 -vt 0.794636 0.681154 -vt 0.794252 0.678215 -vt 0.327758 0.447287 -vt 0.332039 0.453300 -vt 0.422154 0.722200 -vt 0.427526 0.728705 -vt 0.430292 0.731880 -vt 0.431475 0.733090 -vt 0.642885 0.903634 -vt 0.329929 0.801586 -vt 0.324309 0.808577 -vt 0.321410 0.811823 -vt 0.320117 0.812996 -vt 0.794485 0.676088 -vt 0.792835 0.675782 -vt 0.428104 0.735102 -vt 0.484035 0.973510 -vt 0.645338 0.906660 -vt 0.320568 0.810257 -vt 0.318938 0.812258 -vt 0.499757 0.970789 -vt 0.499938 0.968516 -vt 0.498964 0.970557 -vt 0.498874 0.968207 -vt 0.793568 0.673598 -vt 0.794364 0.673865 -vt 0.316424 0.438379 -vt 0.323779 0.453611 -vt 0.485216 0.972521 -vt 0.485818 0.967616 -vt 0.414772 0.728102 -vt 0.482412 0.965538 -vt 0.482412 0.961081 -vt 0.483336 0.960935 -vt 0.504402 0.967004 -vt 0.316315 0.437696 -vt 0.312431 0.447857 -vt 0.488192 0.954257 -vt 0.488192 0.958776 -vt 0.482265 0.957617 -vt 0.483163 0.957566 -vt 0.313439 0.813213 -vt 0.502222 0.958265 -vt 0.498844 0.955626 -vt 0.348225 0.571613 -vt 0.346586 0.571941 -vt 0.313890 0.797240 -vt 0.301201 0.817980 -vt 0.304781 0.818810 -vt 0.299848 0.819149 -vt 0.308699 0.447178 -vt 0.909924 0.825788 -vt 0.911575 0.826043 -vt 0.483188 0.955466 -vt 0.484327 0.954439 -vt 0.406842 0.727270 -vt 0.347870 0.568801 -vt 0.347049 0.568869 -vt 0.346025 0.568921 -vt 0.319616 0.788584 -vt 0.307048 0.804830 -vt 0.301101 0.811686 -vt 0.298048 0.814962 -vt 0.296711 0.816207 -vt 0.910389 0.830267 -vt 0.909571 0.830218 -vt 0.911240 0.830260 -vt 0.296495 0.434343 -vt 0.302241 0.443281 -vt 0.306510 0.449270 -vt 0.404958 0.729138 -vt 0.408247 0.728553 -vt 0.406289 0.730416 -vt 0.348133 0.564626 -vt 0.347309 0.564612 -vt 0.346288 0.564557 -vt 0.318534 0.782969 -vt 0.910874 0.834738 -vt 0.910065 0.834767 -vt 0.911720 0.834645 -vt 0.294975 0.440123 -vt 0.300843 0.448539 -vt 0.305248 0.454501 -vt 0.403891 0.734408 -vt 0.405202 0.735795 -vt 0.315424 0.779560 -vt 0.316433 0.778179 -vt 0.911417 0.837780 -vt 0.910608 0.837878 -vt 0.912249 0.837646 -vt 0.293533 0.443542 -vt 0.295110 0.445968 -vt 0.299444 0.452390 -vt 0.391195 0.727095 -vt 0.310994 0.777832 -vt 0.310010 0.779256 -vt 0.307532 0.782819 -vt 0.291021 0.806397 -vt 0.910910 0.840454 -vt 0.911705 0.840293 -vt 0.912539 0.840135 -vt 0.291914 0.445757 -vt 0.290525 0.443453 -vt 0.303623 0.780952 -vt 0.397924 0.740884 -vt 0.287399 0.806269 -vt 0.286293 0.808064 -vt 0.644775 0.863222 -vt 0.644699 0.867756 -vt 0.645815 0.867230 -vt 0.286073 0.441559 -vt 0.912968 0.796643 -vt 0.913041 0.789936 -vt 0.272650 0.814842 -vt 0.645674 0.881863 -vt 0.288395 0.789780 -vt 0.911084 0.769872 -vt 0.646528 0.880626 -vt 0.359701 0.713938 -vt 0.910006 0.768677 -vt 0.266778 0.810338 -vt 0.264739 0.818445 -vt 0.271944 0.815971 -vt 0.263882 0.819840 -vt 0.643652 0.889577 -vt 0.644662 0.890076 -vt 0.645725 0.889472 -vt 0.646591 0.888164 -vt 0.912163 0.766222 -vt 0.911120 0.766139 -vt 0.260412 0.816157 -vt 0.257852 0.819717 -vt 0.256867 0.821137 -vt 0.643991 0.895011 -vt 0.644807 0.895311 -vt 0.645664 0.894947 -vt 0.264523 0.428497 -vt 0.257552 0.426760 -vt 0.349225 0.785748 -vt 0.365433 0.725804 -vt 0.274614 0.790736 -vt 0.272614 0.793136 -vt 0.348179 0.785753 -vt 0.347778 0.781989 -vt 0.267562 0.799279 -vt 0.252944 0.817292 -vt 0.251891 0.818626 -vt 0.347778 0.771087 -vt 0.348738 0.765575 -vt 0.348580 0.771125 -vt 0.349453 0.771128 -vt 0.252384 0.428972 -vt 0.347879 0.777282 -vt 0.349556 0.777173 -vt 0.271281 0.787182 -vt 0.273202 0.784979 -vt 0.266232 0.792906 -vt 0.249977 0.811114 -vt 0.348735 0.777150 -vt 0.347937 0.777066 -vt 0.349613 0.777173 -vt 0.249982 0.436449 -vt 0.496117 0.826862 -vt 0.494753 0.836101 -vt 0.349822 0.721126 -vt 0.273915 0.774223 -vt 0.251382 0.797840 -vt 0.342043 0.371399 -vt 0.342244 0.382590 -vt 0.344102 0.371483 -vt 0.250609 0.450171 -vt 0.360860 0.744307 -vt 0.255267 0.781937 -vt 0.276686 0.761044 -vt 0.364145 0.758700 -vt 0.254181 0.467483 -vt 0.361260 0.768374 -vt 0.281921 0.721317 -vt 0.386795 0.820567 -vt 0.394392 0.831449 -vt 0.396231 0.832034 -vt 0.496272 0.734792 -vt 0.310811 0.675049 -vt 0.322325 0.605114 -vt 0.323411 0.604859 -vt 0.321468 0.605138 -vt 0.495088 0.672253 -vt 0.496166 0.671301 -vt 0.421120 0.868598 -vt 0.491548 0.672876 -vt 0.492189 0.673103 -vt 0.493200 0.673103 -vt 0.492682 0.673149 -vt 0.347463 0.635345 -vt 0.347367 0.633823 -vt 0.490942 0.672447 -vt 0.347360 0.632157 -vt 0.342030 0.579897 -vt 0.343110 0.580341 -vt 0.347778 0.627548 -vt 0.339950 0.578025 -vt 0.338242 0.626784 -vt 0.338064 0.627546 -vt 0.343874 0.580456 -vt 0.345412 0.579955 -vt 0.338459 0.625415 -vt 0.434417 0.887037 -vt 0.434483 0.889833 -vt 0.434445 0.890889 -vt 0.434198 0.884602 -vt 0.733570 0.941586 -vt 0.735005 0.975251 -vt 0.496935 0.670361 -vt 0.693095 0.946069 -vt 0.695153 0.979195 -vt 0.693313 0.979750 -vt 0.691969 0.979427 -vt 0.745561 0.906688 -vt 0.833083 0.912051 -vt 0.838515 0.915630 -vt 0.738563 0.935617 -vt 0.740576 0.968975 -vt 0.739529 0.969803 -vt 0.839893 0.914487 -vt 0.732673 0.933441 -vt 0.736559 0.928366 -vt 0.731688 0.935131 -vt 0.732710 0.943054 -vt 0.734481 0.930858 -vt 0.738585 0.926344 -vt 0.739645 0.934765 -vt 0.692612 0.938598 -vt 0.831125 0.974149 -vt 0.833548 0.976698 -vt 0.692191 0.930316 -vt 0.694557 0.936889 -vt 0.829716 0.964531 -vt 0.839818 0.970931 -vt 0.740576 0.955082 -vt 0.838131 0.971405 -vt 0.733694 0.920636 -vt 0.738917 0.914912 -vt 0.739920 0.925289 -vt 0.731380 0.923928 -vt 0.736343 0.917468 -vt 0.730074 0.926159 -vt 0.836210 0.978167 -vt 0.686768 0.930786 -vt 0.839098 0.978178 -vt 0.741789 0.963077 -vt 0.744188 0.965191 -vt 0.733573 0.913369 -vt 0.738796 0.907701 -vt 0.740576 0.913605 -vt 0.746852 0.968140 -vt 0.731227 0.916675 -vt 0.736233 0.910215 -vt 0.749366 0.971463 -vt 0.696272 0.925838 -vt 0.694668 0.928108 -vt 0.751114 0.966663 -vt 0.750845 0.973751 -vt 0.545898 0.932794 -vt 0.547825 0.930887 -vt 0.548440 0.973632 -vt 0.544314 0.930666 -vt 0.550951 0.970708 -vt 0.549803 0.928529 -vt 0.578433 0.927721 -vt 0.551522 0.926094 -vt 0.692713 0.955000 -vt 0.572395 0.966317 -vt 0.574664 0.925753 -vt 0.694570 0.953954 -vt 0.544603 0.933807 -vt 0.976692 0.478691 -vt 0.124504 0.796871 -vt 0.981556 0.454036 -vt 0.185466 0.805851 -vt 0.412256 0.910102 -vt 0.411067 0.945038 -vt 0.422895 0.942716 -vt 0.126358 0.777505 -vt 0.125631 0.776640 -vt 0.421699 0.950124 -vt 0.125988 0.765749 -vt 0.187834 0.872163 -vt 0.128191 0.764633 -vt 0.125257 0.760082 -vt 0.981456 0.502236 -vt 0.980605 0.501439 -vt 0.186037 0.878967 -vt 0.976068 0.494926 -vt 0.186569 0.878888 -vt 0.977135 0.400081 -vt 0.412266 0.994340 -vt 0.413385 0.997369 -vt 0.412850 0.997462 -vt 0.184639 0.882528 -vt 0.412780 0.994107 -vt 0.126748 0.752083 -vt 0.494199 0.841164 -vt 0.493698 0.840768 -vt 0.983003 0.408989 -vt 0.180409 0.872381 -vt 0.182875 0.885085 -vt 0.415007 0.995981 -vt 0.418406 0.991319 -vt 0.421234 0.986378 -vt 0.127969 0.745796 -vt 0.124892 0.749026 -vt 0.126031 0.742073 -vt 0.123971 0.752601 -vt 0.492516 0.843004 -vt 0.493200 0.843329 -vt 0.982488 0.493967 -vt 0.980972 0.492966 -vt 0.344034 0.626209 -vt 0.178995 0.887520 -vt 0.180677 0.887151 -vt 0.886607 0.962236 -vt 0.884506 0.960169 -vt 0.885145 0.959851 -vt 0.415285 0.998918 -vt 0.413767 0.999951 -vt 0.126681 0.730247 -vt 0.124069 0.731824 -vt 0.124126 0.726337 -vt 0.126654 0.734558 -vt 0.123447 0.739350 -vt 0.122473 0.746479 -vt 0.492374 0.846053 -vt 0.491656 0.845864 -vt 0.348171 0.623137 -vt 0.347420 0.622935 -vt 0.883414 0.957026 -vt 0.884082 0.956837 -vt 0.340462 0.621819 -vt 0.177832 0.888854 -vt 0.123962 0.724831 -vt 0.121012 0.722849 -vt 0.491789 0.849699 -vt 0.490989 0.850988 -vt 0.347329 0.619471 -vt 0.346592 0.619329 -vt 0.343750 0.618902 -vt 0.339671 0.618366 -vt 0.173135 0.872543 -vt 0.172371 0.888150 -vt 0.172339 0.889959 -vt 0.173003 0.890643 -vt 0.882661 0.953100 -vt 0.883307 0.952589 -vt 0.117254 0.721883 -vt 0.117254 0.721134 -vt 0.113718 0.746139 -vt 0.114017 0.743511 -vt 0.346716 0.614677 -vt 0.345985 0.614569 -vt 0.491411 0.854475 -vt 0.343153 0.614198 -vt 0.339076 0.613693 -vt 0.168607 0.873828 -vt 0.882146 0.948166 -vt 0.112450 0.720244 -vt 0.112482 0.719507 -vt 0.112259 0.723112 -vt 0.111735 0.729805 -vt 0.111072 0.737489 -vt 0.112923 0.748551 -vt 0.101993 0.744829 -vt 0.490495 0.863136 -vt 0.491217 0.860231 -vt 0.342758 0.608483 -vt 0.338681 0.608000 -vt 0.163037 0.875070 -vt 0.160414 0.890872 -vt 0.160247 0.892611 -vt 0.161160 0.893401 -vt 0.106783 0.717930 -vt 0.105962 0.728203 -vt 0.105319 0.735894 -vt 0.101921 0.740080 -vt 0.101712 0.742736 -vt 0.342597 0.602315 -vt 0.338519 0.601849 -vt 0.491214 0.866442 -vt 0.156980 0.876151 -vt 0.099739 0.726763 -vt 0.100620 0.716503 -vt 0.099112 0.734455 -vt 0.100918 0.745204 -vt 0.345521 0.596530 -vt 0.346248 0.596582 -vt 0.342691 0.596252 -vt 0.338614 0.595804 -vt 0.150990 0.876963 -vt 0.147646 0.894499 -vt 0.147680 0.895192 -vt 0.881802 0.929790 -vt 0.094405 0.716077 -vt 0.094492 0.715353 -vt 0.093542 0.725598 -vt 0.094124 0.718918 -vt 0.092934 0.733294 -vt 0.088832 0.737591 -vt 0.345834 0.591085 -vt 0.346621 0.591114 -vt 0.342870 0.590835 -vt 0.338980 0.590433 -vt 0.145635 0.877679 -vt 0.088785 0.715274 -vt 0.088890 0.714603 -vt 0.088470 0.717990 -vt 0.087866 0.724570 -vt 0.087281 0.732268 -vt 0.080178 0.742688 -vt 0.079050 0.741740 -vt 0.346545 0.586820 -vt 0.347281 0.586805 -vt 0.492280 0.885185 -vt 0.343700 0.586652 -vt 0.339614 0.586271 -vt 0.141571 0.885115 -vt 0.141418 0.877401 -vt 0.139494 0.894286 -vt 0.084269 0.714375 -vt 0.084114 0.715095 -vt 0.884132 0.916662 -vt 0.083722 0.717928 -vt 0.083070 0.724601 -vt 0.082530 0.732299 -vt 0.348191 0.583721 -vt 0.493192 0.885608 -vt 0.080673 0.714684 -vt 0.883544 0.916054 -vt 0.076450 0.743507 -vt 0.074878 0.743626 -vt 0.493457 0.888140 -vt 0.349289 0.581633 -vt 0.135208 0.890936 -vt 0.136448 0.874568 -vt 0.885258 0.913767 -vt 0.884639 0.913452 -vt 0.077904 0.715485 -vt 0.074258 0.744938 -vt 0.968291 0.826673 -vt 0.969881 0.827369 -vt 0.968137 0.828154 -vt 0.343839 0.580548 -vt 0.347673 0.580652 -vt 0.135305 0.879406 -vt 0.135203 0.873747 -vt 0.134938 0.886582 -vt 0.885965 0.911559 -vt 0.886616 0.911820 -vt 0.682106 0.963591 -vt 0.680182 0.964764 -vt 0.680420 0.962631 -vt 0.074853 0.719451 -vt 0.074161 0.723607 -vt 0.073539 0.731131 -vt 0.970010 0.828902 -vt 0.968255 0.829880 -vt 0.974087 0.825480 -vt 0.524323 0.948963 -vt 0.520404 0.951807 -vt 0.524213 0.947612 -vt 0.133905 0.885448 -vt 0.134319 0.881104 -vt 0.887548 0.910294 -vt 0.888374 0.910250 -vt 0.682432 0.961415 -vt 0.680747 0.960260 -vt 0.676393 0.958712 -vt 0.676752 0.956171 -vt 0.671940 0.953165 -vt 0.672042 0.950243 -vt 0.071659 0.734742 -vt 0.497083 0.891715 -vt 0.520304 0.950211 -vt 0.524122 0.946030 -vt 0.515924 0.955562 -vt 0.132780 0.885893 -vt 0.890356 0.909675 -vt 0.889146 0.909296 -vt 0.668124 0.944393 -vt 0.891036 0.908934 -vt 0.069921 0.746750 -vt 0.513347 0.953377 -vt 0.129550 0.882939 -vt 0.130429 0.887833 -vt 0.968158 0.840269 -vt 0.129652 0.886823 -vt 0.128937 0.882219 -vt 0.524582 0.939747 -vt 0.683964 0.949518 -vt 0.667262 0.928274 -vt 0.521360 0.940958 -vt 0.524578 0.937514 -vt 0.516664 0.945888 -vt 0.682921 0.946137 -vt 0.679407 0.941554 -vt 0.674766 0.935682 -vt 0.968172 0.844419 -vt 0.684235 0.944326 -vt 0.069868 0.760231 -vt 0.520181 0.934818 -vt 0.524079 0.930740 -vt 0.513288 0.941951 -vt 0.128550 0.877115 -vt 0.968422 0.849478 -vt 0.515798 0.938865 -vt 0.674273 0.923221 -vt 0.684235 0.936961 -vt 0.973944 0.844861 -vt 0.970159 0.849453 -vt 0.072639 0.766785 -vt 0.520257 0.932461 -vt 0.524167 0.928397 -vt 0.515876 0.936340 -vt 0.514062 0.936069 -vt 0.513440 0.937633 -vt 0.682757 0.930929 -vt 0.679121 0.925625 -vt 0.129582 0.870112 -vt 0.130563 0.866230 -vt 0.674417 0.919733 -vt 0.668052 0.914052 -vt 0.524274 0.926333 -vt 0.129869 0.862028 -vt 0.679214 0.922353 -vt 0.668653 0.911290 -vt 0.514266 0.932937 -vt 0.515495 0.932522 -vt 0.077721 0.755854 -vt 0.521227 0.927931 -vt 0.002342 0.967091 -vt 0.001706 0.964010 -vt 0.002836 0.966676 -vt 0.669889 0.909223 -vt 0.668690 0.908714 -vt 0.000423 0.962927 -vt 0.133177 0.843544 -vt 0.705331 0.955656 -vt 0.514792 0.931068 -vt 0.515680 0.930768 -vt 0.136094 0.855690 -vt 0.135666 0.848001 -vt 0.135653 0.841363 -vt 0.133415 0.840999 -vt 0.135871 0.838435 -vt 0.669841 0.906877 -vt 0.668982 0.906509 -vt 0.081037 0.774294 -vt 0.714264 0.946415 -vt 0.702923 0.952811 -vt 0.588607 0.938532 -vt 0.142588 0.862993 -vt 0.589334 0.942232 -vt 0.088380 0.762518 -vt 0.147546 0.848902 -vt 0.590048 0.946452 -vt 0.590486 0.946456 -vt 0.593264 0.954994 -vt 0.593018 0.955873 -vt 0.004219 0.941801 -vt 0.592322 0.956570 -vt 0.156671 0.830716 -vt 0.167573 0.854260 -vt 0.105730 0.784126 -vt 0.107756 0.769413 -vt 0.697677 0.924475 -vt 0.166042 0.826573 -vt 0.109110 0.787348 -vt 0.699499 0.918493 -vt 0.219732 0.975300 -vt 0.218863 0.972038 -vt 0.005047 0.926714 -vt 0.005798 0.926630 -vt 0.171610 0.850943 -vt 0.169267 0.824526 -vt 0.593912 0.969799 -vt 0.594627 0.969880 -vt 0.595386 0.969960 -vt 0.713460 0.906854 -vt 0.705661 0.910735 -vt 0.171175 0.823598 -vt 0.115740 0.808278 -vt 0.114050 0.810224 -vt 0.116702 0.790950 -vt 0.637425 0.940151 -vt 0.498055 0.743167 -vt 0.178072 0.822351 -vt 0.111692 0.825378 -vt 0.112013 0.826136 -vt 0.628962 0.923409 -vt 0.112757 0.827112 -vt 0.113159 0.827316 -vt 0.112373 0.826719 -vt 0.114418 0.826862 -vt 0.113995 0.827187 -vt 0.113573 0.827341 -vt 0.177380 0.804860 -vt 0.177782 0.804489 -vt 0.114961 0.826176 -vt 0.498222 0.709654 -vt 0.496935 0.712355 -vt 0.219640 0.919791 -vt 0.218090 0.922900 -vt 0.178272 0.803712 -vt 0.176554 0.805047 -vt 0.176970 0.805044 -vt 0.176140 0.804873 -vt 0.175734 0.804528 -vt 0.629322 0.940483 -vt 0.630216 0.942248 -vt 0.175339 0.804018 -vt 0.174961 0.803350 -vt 0.636461 0.954473 -vt 0.637376 0.956250 -vt 0.066548 0.958630 -vt 0.065558 0.956863 -vt 0.062816 0.879631 -vt 0.278930 0.853434 -vt 0.282857 0.900650 -vt 0.241501 0.727560 -vt 0.285315 0.931036 -vt 0.106268 0.892617 -vt 0.790495 0.678556 -vt 0.790895 0.678865 -vt 0.066738 0.859456 -vt 0.286194 0.956874 -vt 0.238428 0.839170 -vt 0.238015 0.839504 -vt 0.106727 0.868477 -vt 0.104299 0.867588 -vt 0.791927 0.675558 -vt 0.791477 0.675391 -vt 0.790184 0.673856 -vt 0.787465 0.668903 -vt 0.785210 0.663731 -vt 0.067720 0.856566 -vt 0.065921 0.850796 -vt 0.285355 0.960998 -vt 0.239768 0.843628 -vt 0.239325 0.843799 -vt 0.241067 0.842045 -vt 0.243732 0.837052 -vt 0.246550 0.830115 -vt 0.248521 0.823918 -vt 0.105698 0.863448 -vt 0.627735 0.936204 -vt 0.626704 0.938552 -vt 0.625697 0.939117 -vt 0.625202 0.939159 -vt 0.792284 0.672862 -vt 0.790948 0.671566 -vt 0.788156 0.666822 -vt 0.785880 0.661657 -vt 0.956713 0.857598 -vt 0.242097 0.845371 -vt 0.240753 0.846706 -vt 0.244836 0.840586 -vt 0.247048 0.835405 -vt 0.106986 0.857038 -vt 0.792572 0.670814 -vt 0.791882 0.670281 -vt 0.625556 0.940471 -vt 0.790654 0.668790 -vt 0.279111 0.951014 -vt 0.242413 0.848460 -vt 0.241728 0.849005 -vt 0.243629 0.846948 -vt 0.626173 0.940822 -vt 0.625387 0.942913 -vt 0.511941 0.949441 -vt 0.509313 0.948898 -vt 0.502847 0.947965 -vt 0.277437 0.952264 -vt 0.277954 0.956931 -vt 0.278872 0.967737 -vt 0.951334 0.851342 -vt 0.951996 0.851117 -vt 0.103596 0.837481 -vt 0.101130 0.838287 -vt 0.101163 0.835584 -vt 0.100641 0.844808 -vt 0.099759 0.852429 -vt 0.098736 0.859010 -vt 0.624841 0.945407 -vt 0.511244 0.946912 -vt 0.508656 0.946483 -vt 0.502213 0.945632 -vt 0.275284 0.953370 -vt 0.275751 0.958053 -vt 0.950728 0.848371 -vt 0.951398 0.848236 -vt 0.098254 0.836729 -vt 0.098391 0.834093 -vt 0.097691 0.843212 -vt 0.096857 0.850862 -vt 0.095989 0.857531 -vt 0.090812 0.859826 -vt 0.508234 0.943579 -vt 0.510723 0.942588 -vt 0.501799 0.942783 -vt 0.272580 0.954362 -vt 0.272426 0.967194 -vt 0.951096 0.845008 -vt 0.950420 0.845082 -vt 0.094428 0.841949 -vt 0.093627 0.849614 -vt 0.091730 0.854222 -vt 0.091420 0.856995 -vt 0.508036 0.940323 -vt 0.501602 0.939567 -vt 0.950345 0.841556 -vt 0.951020 0.841524 -vt 0.090147 0.848604 -vt 0.084156 0.857476 -vt 0.951287 0.836261 -vt 0.950617 0.836171 -vt 0.086548 0.830822 -vt 0.084107 0.855351 -vt 0.624398 0.962897 -vt 0.258341 0.971838 -vt 0.258192 0.970864 -vt 0.951170 0.831566 -vt 0.081947 0.830331 -vt 0.951718 0.829075 -vt 0.625181 0.963272 -vt 0.258373 0.956475 -vt 0.257929 0.968832 -vt 0.079403 0.830350 -vt 0.078923 0.832916 -vt 0.078115 0.839353 -vt 0.625010 0.965011 -vt 0.509623 0.927666 -vt 0.512272 0.927635 -vt 0.503138 0.927143 -vt 0.257249 0.960935 -vt 0.256996 0.956240 -vt 0.256371 0.967258 -vt 0.952430 0.827071 -vt 0.077291 0.830694 -vt 0.076692 0.833288 -vt 0.075801 0.839738 -vt 0.075180 0.847402 -vt 0.626172 0.965858 -vt 0.625606 0.966146 -vt 0.511161 0.926836 -vt 0.512938 0.926665 -vt 0.508596 0.926812 -vt 0.253793 0.969466 -vt 0.720603 0.956407 -vt 0.719205 0.956968 -vt 0.719746 0.955736 -vt 0.955752 0.825207 -vt 0.075525 0.831414 -vt 0.074774 0.834086 -vt 0.073779 0.840577 -vt 0.073229 0.848210 -vt 0.612065 0.943617 -vt 0.612126 0.945174 -vt 0.255321 0.964843 -vt 0.255756 0.961778 -vt 0.613079 0.943425 -vt 0.502158 0.925744 -vt 0.504911 0.926072 -vt 0.503631 0.926396 -vt 0.955095 0.824789 -vt 0.721379 0.955091 -vt 0.718294 0.953248 -vt 0.716451 0.948703 -vt 0.072021 0.842404 -vt 0.071608 0.849479 -vt 0.722298 0.953471 -vt 0.956348 0.824011 -vt 0.070577 0.859137 -vt 0.070527 0.855777 -vt 0.628133 0.968135 -vt 0.627952 0.968499 -vt 0.176839 0.965507 -vt 0.958445 0.823725 -vt 0.958388 0.823331 -vt 0.722401 0.949798 -vt 0.720428 0.944473 -vt 0.718246 0.937289 -vt 0.069489 0.862022 -vt 0.613905 0.952754 -vt 0.726093 0.946499 -vt 0.069084 0.867500 -vt 0.172566 0.961553 -vt 0.611915 0.955853 -vt 0.727700 0.943269 -vt 0.727025 0.940313 -vt 0.612585 0.957303 -vt 0.727059 0.933791 -vt 0.912752 0.820329 -vt 0.914005 0.821701 -vt 0.725005 0.926581 -vt 0.250399 0.958477 -vt 0.613318 0.958282 -vt 0.177691 0.952207 -vt 0.171797 0.957472 -vt 0.169803 0.958479 -vt 0.250228 0.953813 -vt 0.722967 0.911187 -vt 0.616573 0.955293 -vt 0.251778 0.947511 -vt 0.250392 0.943213 -vt 0.175941 0.952366 -vt 0.177786 0.950578 -vt 0.171944 0.955001 -vt 0.173785 0.953716 -vt 0.170593 0.955601 -vt 0.169930 0.956285 -vt 0.251485 0.941517 -vt 0.249836 0.938469 -vt 0.250781 0.933974 -vt 0.723916 0.908981 -vt 0.075185 0.873754 -vt 0.255368 0.949821 -vt 0.252758 0.939941 -vt 0.252062 0.932389 -vt 0.250681 0.930304 -vt 0.251982 0.927028 -vt 0.588551 0.952954 -vt 0.586669 0.954647 -vt 0.584591 0.954245 -vt 0.584872 0.956392 -vt 0.583162 0.955711 -vt 0.170743 0.953991 -vt 0.172081 0.953524 -vt 0.910814 0.815853 -vt 0.910589 0.813284 -vt 0.254217 0.938513 -vt 0.253527 0.930952 -vt 0.726477 0.907614 -vt 0.725475 0.906603 -vt 0.054032 0.961998 -vt 0.910837 0.810725 -vt 0.257225 0.921391 -vt 0.263377 0.947834 -vt 0.056629 0.968986 -vt 0.083078 0.881592 -vt 0.083970 0.883109 -vt 0.263121 0.919141 -vt 0.912814 0.804730 -vt 0.056000 0.971013 -vt 0.083796 0.883455 -vt 0.911747 0.803896 -vt 0.086306 0.885295 -vt 0.912356 0.801895 -vt 0.912867 0.799828 -vt 0.090036 0.867565 -vt 0.578737 0.942492 -vt 0.091223 0.875773 -vt 0.092045 0.870234 -vt 0.580342 0.938223 -vt 0.584264 0.934897 -vt 0.344839 0.953465 -vt 0.345176 0.950782 -vt 0.913602 0.797728 -vt 0.913167 0.797706 -vt 0.270275 0.943460 -vt 0.272701 0.939957 -vt 0.266839 0.916243 -vt 0.268185 0.914528 -vt 0.432459 0.898397 -vt 0.433126 0.900113 -vt 0.432318 0.900002 -vt 0.581736 0.934196 -vt 0.585464 0.930953 -vt 0.269246 0.914681 -vt 0.274777 0.934675 -vt 0.347357 0.929716 -vt 0.270157 0.915774 -vt 0.095775 0.889444 -vt 0.341952 0.940157 -vt 0.272190 0.922601 -vt 0.910896 0.748641 -vt 0.431984 0.926032 -vt 0.095568 0.899254 -vt 0.910386 0.735064 -vt 0.341268 0.917167 -vt 0.087834 0.965933 -vt 0.088237 0.966074 -vt 0.346713 0.884827 -vt 0.341961 0.894166 -vt 0.086982 0.964995 -vt 0.086450 0.963804 -vt 0.087447 0.965635 -vt 0.089069 0.965900 -vt 0.088651 0.966063 -vt 0.346561 0.854661 -vt 0.348021 0.851949 -vt 0.089618 0.965452 -vt 0.090302 0.964550 -vt 0.911092 0.681350 -vt 0.344804 0.857961 -vt 0.343285 0.860875 -vt 0.343835 0.859811 -vt 0.271263 0.854053 -vt 0.271653 0.853762 -vt 0.272023 0.853312 -vt 0.272465 0.852461 -vt 0.270445 0.854171 -vt 0.270027 0.854005 -vt 0.270858 0.854189 -vt 0.424726 0.931274 -vt 0.426672 0.964925 -vt 0.426124 0.963951 -vt 0.269612 0.853692 -vt 0.269074 0.853040 -vt 0.644528 0.775645 -vt 0.432379 0.975447 -vt 0.647083 0.776728 -vt 0.972830 0.973784 -vt 0.645616 0.691000 -vt 0.951663 0.892978 -vt 0.952986 0.895095 -vt 0.646612 0.678856 -vt 0.533781 0.953312 -vt 0.536727 0.955459 -vt 0.533389 0.954068 -vt 0.646053 0.861904 -vt 0.646293 0.670715 -vt 0.962129 0.736581 -vt 0.644631 0.862155 -vt 0.794303 0.665710 -vt 0.960296 0.731272 -vt 0.962610 0.725374 -vt 0.959335 0.733720 -vt 0.537365 0.954218 -vt 0.534102 0.952763 -vt 0.530516 0.952277 -vt 0.530980 0.951590 -vt 0.953845 0.197110 -vt 0.953574 0.195539 -vt 0.793565 0.669935 -vt 0.792829 0.669955 -vt 0.958148 0.216553 -vt 0.960476 0.864532 -vt 0.794271 0.669896 -vt 0.964720 0.708718 -vt 0.531344 0.951075 -vt 0.528599 0.949656 -vt 0.949833 0.194619 -vt 0.949798 0.193006 -vt 0.966009 0.867486 -vt 0.967305 0.870194 -vt 0.965507 0.866605 -vt 0.216932 0.909251 -vt 0.215577 0.912732 -vt 0.214792 0.912459 -vt 0.794288 0.673547 -vt 0.794937 0.673373 -vt 0.160250 0.911717 -vt 0.528114 0.950123 -vt 0.972122 0.872898 -vt 0.973117 0.874899 -vt 0.956280 0.712890 -vt 0.525179 0.939977 -vt 0.525872 0.940116 -vt 0.153086 0.920771 -vt 0.942907 0.185925 -vt 0.969229 0.090118 -vt 0.971641 0.101684 -vt 0.953831 0.716240 -vt 0.953501 0.715785 -vt 0.149033 0.922357 -vt 0.939849 0.184442 -vt 0.940288 0.183570 -vt 0.967500 0.084771 -vt 0.967717 0.274297 -vt 0.969767 0.271791 -vt 0.968402 0.274600 -vt 0.951859 0.718202 -vt 0.952193 0.719120 -vt 0.145074 0.922239 -vt 0.144866 0.922961 -vt 0.157898 0.896799 -vt 0.527099 0.936368 -vt 0.962559 0.080914 -vt 0.958431 0.080402 -vt 0.958225 0.079693 -vt 0.213437 0.929401 -vt 0.207825 0.934702 -vt 0.966716 0.277893 -vt 0.967409 0.278046 -vt 0.154500 0.896637 -vt 0.154199 0.897316 -vt 0.153133 0.899959 -vt 0.150754 0.906206 -vt 0.148116 0.913410 -vt 0.145963 0.919540 -vt 0.141118 0.922406 -vt 0.140808 0.923122 -vt 0.528858 0.932904 -vt 0.526427 0.936079 -vt 0.528272 0.932518 -vt 0.966229 0.281778 -vt 0.150530 0.896791 -vt 0.150325 0.897464 -vt 0.149438 0.900084 -vt 0.147211 0.906322 -vt 0.144552 0.913527 -vt 0.142210 0.919671 -vt 0.137117 0.923322 -vt 0.966942 0.281807 -vt 0.210546 0.943957 -vt 0.946665 0.079045 -vt 0.946825 0.077132 -vt 0.134465 0.923467 -vt 0.532671 0.927204 -vt 0.947251 0.084682 -vt 0.948673 0.092071 -vt 0.214407 0.945066 -vt 0.211438 0.948292 -vt 0.216810 0.942142 -vt 0.217527 0.941072 -vt 0.562490 0.947423 -vt 0.560804 0.949363 -vt 0.966994 0.289208 -vt 0.968148 0.288693 -vt 0.945154 0.089544 -vt 0.212945 0.950691 -vt 0.211163 0.952611 -vt 0.566235 0.931367 -vt 0.571442 0.931368 -vt 0.969182 0.291562 -vt 0.970398 0.291942 -vt 0.137650 0.913319 -vt 0.135702 0.918302 -vt 0.571560 0.942442 -vt 0.570994 0.943265 -vt 0.971279 0.293318 -vt 0.571473 0.938774 -vt 0.595774 0.949092 -vt 0.604984 0.940133 -vt 0.136763 0.906995 -vt 0.940444 0.065493 -vt 0.571159 0.946761 -vt 0.945505 0.070658 -vt 0.136416 0.908904 -vt 0.554875 0.930450 -vt 0.203395 0.921926 -vt 0.770596 0.966004 -vt 0.771677 0.966548 -vt 0.595878 0.955383 -vt 0.597871 0.954588 -vt 0.563938 0.959978 -vt 0.131983 0.918013 -vt 0.553078 0.931843 -vt 0.553404 0.932354 -vt 0.553765 0.933884 -vt 0.553898 0.937622 -vt 0.553366 0.944715 -vt 0.552526 0.950176 -vt 0.946726 0.069132 -vt 0.944035 0.060741 -vt 0.769788 0.963688 -vt 0.769054 0.963807 -vt 0.598050 0.957069 -vt 0.596645 0.957649 -vt 0.599590 0.956052 -vt 0.194949 0.923116 -vt 0.198703 0.924342 -vt 0.193746 0.926053 -vt 0.129058 0.924343 -vt 0.133518 0.920719 -vt 0.130660 0.927158 -vt 0.136469 0.915248 -vt 0.203230 0.924178 -vt 0.202597 0.926177 -vt 0.551917 0.933461 -vt 0.552386 0.934908 -vt 0.552611 0.936745 -vt 0.950385 0.083204 -vt 0.947710 0.077780 -vt 0.948852 0.076353 -vt 0.189574 0.927984 -vt 0.189917 0.930543 -vt 0.189030 0.930996 -vt 0.190482 0.927451 -vt 0.193147 0.929273 -vt 0.198103 0.927624 -vt 0.135658 0.923156 -vt 0.132863 0.929662 -vt 0.138454 0.917508 -vt 0.140870 0.917730 -vt 0.202193 0.928618 -vt 0.952811 0.086114 -vt 0.950871 0.086716 -vt 0.952737 0.085117 -vt 0.952158 0.081698 -vt 0.950577 0.074888 -vt 0.189794 0.933924 -vt 0.188913 0.934313 -vt 0.193013 0.932755 -vt 0.197974 0.931159 -vt 0.138301 0.925405 -vt 0.135558 0.931954 -vt 0.140264 0.921056 -vt 0.955285 0.084527 -vt 0.955160 0.083566 -vt 0.202157 0.931494 -vt 0.954498 0.080208 -vt 0.952871 0.073426 -vt 0.769950 0.953508 -vt 0.189234 0.938740 -vt 0.769383 0.953584 -vt 0.142124 0.928055 -vt 0.139443 0.934586 -vt 0.958255 0.083001 -vt 0.955701 0.071977 -vt 0.958945 0.070540 -vt 0.772012 0.941996 -vt 0.771280 0.943982 -vt 0.191434 0.950544 -vt 0.148155 0.939748 -vt 0.966010 0.059302 -vt 0.968912 0.057498 -vt 0.150492 0.941271 -vt 0.205078 0.948139 -vt 0.772353 0.939085 -vt 0.773625 0.938052 -vt 0.190834 0.951658 -vt 0.191702 0.953439 -vt 0.190808 0.953862 -vt 0.199889 0.950623 -vt 0.152034 0.942507 -vt 0.156839 0.931711 -vt 0.205246 0.950654 -vt 0.973220 0.074954 -vt 0.973322 0.075942 -vt 0.972587 0.071553 -vt 0.970964 0.064775 -vt 0.970734 0.055763 -vt 0.772239 0.937662 -vt 0.772978 0.937551 -vt 0.191423 0.954814 -vt 0.190500 0.955343 -vt 0.204991 0.952701 -vt 0.975006 0.074367 -vt 0.974979 0.073293 -vt 0.974461 0.069759 -vt 0.972889 0.062938 -vt 0.773120 0.936200 -vt 0.772766 0.936414 -vt 0.772402 0.936598 -vt 0.194601 0.953939 -vt 0.152887 0.944310 -vt 0.788317 0.937756 -vt 0.975434 0.065731 -vt 0.974199 0.060639 -vt 0.231202 0.910526 -vt 0.159019 0.934353 -vt 0.157589 0.936017 -vt 0.497864 0.947122 -vt 0.782700 0.915127 -vt 0.793615 0.937209 -vt 0.783553 0.917915 -vt 0.225679 0.922586 -vt 0.158444 0.942298 -vt 0.794281 0.932204 -vt 0.226534 0.928264 -vt 0.794803 0.927028 -vt 0.155593 0.949992 -vt 0.964658 0.027981 -vt 0.784566 0.898122 -vt 0.784560 0.884927 -vt 0.492431 0.926131 -vt 0.232375 0.938279 -vt 0.497006 0.897999 -vt 0.150866 0.962248 -vt 0.958012 0.000119 -vt 0.958407 0.000000 -vt 0.784550 0.865332 -vt 0.785171 0.866506 -vt 0.959341 0.000343 -vt 0.958851 0.000073 -vt 0.957387 0.000911 -vt 0.957671 0.000424 -vt 0.142954 0.979840 -vt 0.143331 0.980009 -vt 0.957165 0.001574 -vt 0.494357 0.903559 -vt 0.493692 0.904748 -vt 0.141985 0.980061 -vt 0.142504 0.979856 -vt 0.143631 0.980355 -vt 0.143851 0.980874 -vt 0.143988 0.981560 -vt 0.349041 0.668720 -vt 0.781096 0.920596 -vt 0.644617 0.945120 -vt 0.644537 0.923777 -vt 0.311629 0.907227 -vt 0.464350 0.957103 -vt 0.362995 0.930017 -vt 0.779430 0.981554 -vt 0.778632 0.980778 -vt 0.642217 0.909551 -vt 0.639938 0.909777 -vt 0.663525 0.909641 -vt 0.825168 0.953883 -vt 0.823977 0.954473 -vt 0.366592 0.955675 -vt 0.368069 0.929794 -vt 0.661872 0.914320 -vt 0.661275 0.913978 -vt 0.662577 0.914647 -vt 0.820074 0.942672 -vt 0.378789 0.956374 -vt 0.660119 0.919744 -vt 0.314004 0.877234 -vt 0.657562 0.925267 -vt 0.654453 0.930623 -vt 0.653866 0.930328 -vt 0.650192 0.939348 -vt 0.655121 0.930911 -vt 0.396383 0.949165 -vt 0.329907 0.868155 -vt 0.648509 0.945347 -vt 0.327340 0.861088 -vt 0.458312 0.908595 -vt 0.462088 0.904587 -vt 0.401059 0.946737 -vt 0.401353 0.944245 -vt 0.809636 0.922692 -vt 0.401750 0.937843 -vt 0.401746 0.919279 -vt 0.330567 0.859449 -vt 0.324451 0.854903 -vt 0.460321 0.900624 -vt 0.464362 0.901880 -vt 0.457312 0.899491 -vt 0.456522 0.899073 -vt 0.404957 0.945001 -vt 0.405475 0.942407 -vt 0.406129 0.935898 -vt 0.406441 0.928281 -vt 0.406346 0.921520 -vt 0.406056 0.918326 -vt 0.405862 0.917456 -vt 0.648435 0.949826 -vt 0.647732 0.949784 -vt 0.332582 0.858110 -vt 0.338224 0.861363 -vt 0.326335 0.853674 -vt 0.461518 0.898647 -vt 0.465357 0.899942 -vt 0.458477 0.897291 -vt 0.457690 0.896727 -vt 0.408014 0.944367 -vt 0.408837 0.941640 -vt 0.810416 0.915546 -vt 0.809053 0.915175 -vt 0.409840 0.935016 -vt 0.410274 0.927374 -vt 0.410049 0.920684 -vt 0.409555 0.917646 -vt 0.409253 0.916868 -vt 0.648108 0.953701 -vt 0.340804 0.861297 -vt 0.628177 0.917541 -vt 0.628503 0.925195 -vt 0.751503 0.916648 -vt 0.751371 0.913280 -vt 0.751115 0.912313 -vt 0.467931 0.944388 -vt 0.470344 0.943004 -vt 0.470966 0.945524 -vt 0.467182 0.942059 -vt 0.469522 0.936585 -vt 0.465989 0.935472 -vt 0.469075 0.928975 -vt 0.465414 0.927836 -vt 0.469106 0.922165 -vt 0.465581 0.921120 -vt 0.469401 0.918917 -vt 0.466091 0.918050 -vt 0.469732 0.917785 -vt 0.339724 0.859807 -vt 0.624424 0.930700 -vt 0.756956 0.910008 -vt 0.620224 0.921417 -vt 0.620853 0.928781 -vt 0.623289 0.911914 -vt 0.621229 0.933274 -vt 0.760242 0.914588 -vt 0.761297 0.907983 -vt 0.817371 0.908423 -vt 0.656288 0.961238 -vt 0.656585 0.960600 -vt 0.479168 0.950462 -vt 0.478046 0.922514 -vt 0.619627 0.914835 -vt 0.619751 0.916120 -vt 0.616983 0.923464 -vt 0.617615 0.930821 -vt 0.617905 0.935384 -vt 0.764079 0.913299 -vt 0.821376 0.907123 -vt 0.817059 0.907782 -vt 0.821217 0.906444 -vt 0.765728 0.906508 -vt 0.483801 0.951322 -vt 0.483681 0.952545 -vt 0.483839 0.946074 -vt 0.483557 0.938685 -vt 0.483050 0.931322 -vt 0.482413 0.925703 -vt 0.482167 0.924436 -vt 0.659996 0.962334 -vt 0.616034 0.917097 -vt 0.616300 0.918277 -vt 0.825580 0.905280 -vt 0.488128 0.951726 -vt 0.660149 0.961658 -vt 0.660279 0.960968 -vt 0.663514 0.961265 -vt 0.613374 0.919044 -vt 0.611895 0.920640 -vt 0.768494 0.918206 -vt 0.829305 0.907465 -vt 0.829717 0.906301 -vt 0.491892 0.945352 -vt 0.488882 0.923830 -vt 0.666819 0.962482 -vt 0.666389 0.961324 -vt 0.053256 0.855584 -vt 0.185702 0.896141 -vt 0.195491 0.790415 -vt 0.195576 0.796292 -vt 0.189171 0.800089 -vt 0.184622 0.802366 -vt 0.489680 0.917887 -vt 0.487776 0.922418 -vt 0.489079 0.917526 -vt 0.045902 0.850515 -vt 0.050727 0.861295 -vt 0.182885 0.902843 -vt 0.490474 0.911232 -vt 0.166610 0.802923 -vt 0.028501 0.870721 -vt 0.035602 0.866400 -vt 0.171991 0.915350 -vt 0.172836 0.915203 -vt 0.772447 0.936199 -vt 0.148214 0.799973 -vt 0.147461 0.800392 -vt 0.016269 0.858239 -vt 0.168266 0.922691 -vt 0.165261 0.923215 -vt 0.169101 0.922548 -vt 0.348454 0.747034 -vt 0.348910 0.740984 -vt 0.349208 0.746991 -vt 0.148554 0.791593 -vt 0.142711 0.794867 -vt 0.140010 0.796379 -vt 0.139268 0.796793 -vt 0.879571 0.879031 -vt 0.881111 0.872654 -vt 0.880336 0.879073 -vt 0.023222 0.876394 -vt 0.166776 0.930227 -vt 0.163750 0.930784 -vt 0.167607 0.930084 -vt 0.348112 0.741012 -vt 0.348771 0.734161 -vt 0.157567 0.778276 -vt 0.879054 0.886350 -vt 0.879762 0.886370 -vt 0.002901 0.868862 -vt 0.348043 0.726989 -vt 0.348831 0.727018 -vt 0.879558 0.894125 -vt 0.878817 0.894141 -vt 0.348474 0.720019 -vt 0.349263 0.720103 -vt 0.168097 0.944050 -vt 0.880021 0.901943 -vt 0.879243 0.902005 -vt 0.128550 0.776376 -vt 0.348997 0.713748 -vt 0.349399 0.713835 -vt 0.881330 0.909408 -vt 0.880920 0.909483 -vt 0.185171 0.950579 -vt 0.958106 0.378897 -vt 0.044214 0.912503 -vt 0.160340 0.741325 -vt 0.955441 0.349916 -vt 0.052271 0.918098 -vt 0.186427 0.729508 -vt 0.176611 0.730090 -vt 0.059060 0.922505 -vt 0.197626 0.721722 -vt 0.205365 0.714772 -vt 0.062505 0.924990 -vt 0.938573 0.878645 -vt 0.205579 0.715524 -vt 0.058787 0.926275 -vt 0.952189 0.290715 -vt 0.949127 0.316729 -vt 0.059081 0.925496 -vt 0.944778 0.864783 -vt 0.347972 0.579545 -vt 0.348735 0.574392 -vt 0.348736 0.579590 -vt 0.952138 0.289484 -vt 0.947163 0.289501 -vt 0.947075 0.288404 -vt 0.947273 0.294551 -vt 0.947227 0.301647 -vt 0.947043 0.309046 -vt 0.946770 0.314613 -vt 0.946623 0.316193 -vt 0.348726 0.788337 -vt 0.347977 0.785830 -vt 0.348782 0.785787 -vt 0.972986 0.311085 -vt 0.980803 0.308540 -vt 0.947903 0.876263 -vt 0.941331 0.298363 -vt 0.942749 0.316523 -vt 0.942966 0.320787 -vt 0.346619 0.475439 -vt 0.980543 0.329238 -vt 0.949766 0.895774 -vt 0.934870 0.317198 -vt 0.346360 0.491044 -vt 0.349367 0.490780 -vt 0.349576 0.437603 -vt 0.346252 0.517189 -vt 0.927518 0.346341 -vt 0.347669 0.372742 -vt 0.348078 0.372641 -vt 0.349579 0.553845 -vt 0.977550 0.397612 -vt 0.947563 0.969752 -vt 0.949881 0.965119 -vt 0.950389 0.964103 -vt 0.348865 0.372026 -vt 0.348479 0.372401 -vt 0.346445 0.372196 -vt 0.345936 0.371522 -vt 0.347257 0.372703 -vt 0.346847 0.372522 -vt 0.918551 0.384877 -vt 0.919148 0.387427 -vt 0.918303 0.383820 -vt 0.921109 0.395810 -vt 0.921522 0.397576 -vt 0.346792 0.555153 -vt 0.347194 0.555392 -vt 0.346284 0.554614 -vt 0.345686 0.553629 -vt 0.121605 0.968717 -vt 0.124312 0.968268 -vt 0.124384 0.971740 -vt 0.347604 0.555493 -vt 0.348015 0.555453 -vt 0.348424 0.555271 -vt 0.121725 0.971035 -vt 0.124480 0.973841 -vt 0.029398 0.946634 -vt 0.021534 0.948228 -vt 0.107230 0.970330 -vt 0.110080 0.970412 -vt 0.107359 0.973334 -vt 0.107199 0.966798 -vt 0.109974 0.967292 -vt 0.107271 0.963287 -vt 0.109971 0.963656 -vt 0.107391 0.960868 -vt 0.110074 0.960068 -vt 0.299428 0.940442 -vt 0.297533 0.938531 -vt 0.291324 0.938707 -vt 0.290307 0.943421 -vt 0.121511 0.961499 -vt 0.124437 0.961625 -vt 0.124330 0.964712 -vt 0.121511 0.965137 -vt 0.019862 0.939700 -vt 0.026522 0.940305 -vt 0.118736 0.961993 -vt 0.118764 0.958459 -vt 0.121602 0.958366 -vt 0.291960 0.934888 -vt 0.297328 0.934369 -vt 0.294143 0.933941 -vt 0.299835 0.936472 -vt 0.110207 0.957739 -vt 0.112854 0.957044 -vt 0.112772 0.960524 -vt 0.112794 0.964078 -vt 0.112914 0.967154 -vt 0.020885 0.944413 -vt 0.028980 0.942668 -vt 0.118907 0.967905 -vt 0.118800 0.965497 -vt 0.296664 0.951472 -vt 0.299657 0.952850 -vt 0.298221 0.954972 -vt 0.288938 0.945613 -vt 0.292075 0.946683 -vt 0.295312 0.953674 -vt 0.294713 0.954957 -vt 0.292428 0.954328 -vt 0.191786 0.969127 -vt 0.194492 0.968678 -vt 0.194565 0.972150 -vt 0.495126 0.892121 -vt 0.493341 0.894806 -vt 0.191905 0.971445 -vt 0.194660 0.974251 -vt 0.017484 0.946634 -vt 0.009619 0.948228 -vt 0.341304 0.969628 -vt 0.344155 0.969710 -vt 0.341433 0.972632 -vt 0.341273 0.966096 -vt 0.344048 0.966590 -vt 0.341345 0.962585 -vt 0.344046 0.962954 -vt 0.341466 0.960166 -vt 0.344148 0.959366 -vt 0.335255 0.940442 -vt 0.333360 0.938531 -vt 0.327150 0.938707 -vt 0.326133 0.943421 -vt 0.191692 0.961909 -vt 0.194618 0.962035 -vt 0.194511 0.965122 -vt 0.191692 0.965547 -vt 0.007948 0.939700 -vt 0.014607 0.940305 -vt 0.188917 0.962403 -vt 0.188945 0.958869 -vt 0.191783 0.958776 -vt 0.327786 0.934888 -vt 0.333155 0.934369 -vt 0.329970 0.933941 -vt 0.335661 0.936472 -vt 0.344282 0.957037 -vt 0.346928 0.956342 -vt 0.346847 0.959822 -vt 0.346868 0.963376 -vt 0.346989 0.966452 -vt 0.008971 0.944413 -vt 0.017066 0.942668 -vt 0.189087 0.968315 -vt 0.188981 0.965907 -vt 0.332491 0.951472 -vt 0.335484 0.952850 -vt 0.334047 0.954972 -vt 0.324764 0.945613 -vt 0.327901 0.946683 -vt 0.331139 0.953674 -vt 0.330540 0.954957 -vt 0.328255 0.954328 -vt 0.197534 0.969127 -vt 0.200240 0.968678 -vt 0.200312 0.972150 -vt 0.492286 0.892121 -vt 0.490502 0.894806 -vt 0.197653 0.971445 -vt 0.200408 0.974251 -vt 0.053228 0.946634 -vt 0.045363 0.948228 -vt 0.112999 0.970330 -vt 0.115849 0.970412 -vt 0.113128 0.973334 -vt 0.112968 0.966798 -vt 0.115743 0.967292 -vt 0.113040 0.963287 -vt 0.115741 0.963656 -vt 0.113160 0.960868 -vt 0.115843 0.960068 -vt 0.311370 0.940442 -vt 0.309475 0.938531 -vt 0.303266 0.938707 -vt 0.302249 0.943421 -vt 0.197440 0.961909 -vt 0.200366 0.962035 -vt 0.200259 0.965122 -vt 0.197440 0.965547 -vt 0.043692 0.939700 -vt 0.050351 0.940305 -vt 0.194665 0.962403 -vt 0.194693 0.958869 -vt 0.197531 0.958776 -vt 0.303902 0.934888 -vt 0.309270 0.934369 -vt 0.306085 0.933941 -vt 0.311777 0.936472 -vt 0.115976 0.957739 -vt 0.118623 0.957044 -vt 0.118542 0.960524 -vt 0.118563 0.964078 -vt 0.118684 0.967154 -vt 0.044715 0.944413 -vt 0.052810 0.942668 -vt 0.194835 0.968315 -vt 0.194729 0.965907 -vt 0.308606 0.951472 -vt 0.311599 0.952850 -vt 0.310162 0.954972 -vt 0.300879 0.945613 -vt 0.304017 0.946683 -vt 0.307254 0.953674 -vt 0.306655 0.954957 -vt 0.304370 0.954328 -vt 0.527789 0.969528 -vt 0.530496 0.969078 -vt 0.530568 0.972551 -vt 0.911618 0.906080 -vt 0.909834 0.908765 -vt 0.527909 0.971846 -vt 0.530664 0.974651 -vt 0.041313 0.946634 -vt 0.033448 0.948228 -vt 0.504440 0.968074 -vt 0.507290 0.968157 -vt 0.504569 0.971079 -vt 0.504409 0.964543 -vt 0.507184 0.965037 -vt 0.504481 0.961032 -vt 0.507182 0.961401 -vt 0.504601 0.958612 -vt 0.507284 0.957813 -vt 0.323312 0.940442 -vt 0.321417 0.938530 -vt 0.315208 0.938707 -vt 0.314191 0.943421 -vt 0.527696 0.962309 -vt 0.530622 0.962436 -vt 0.530515 0.965523 -vt 0.527696 0.965947 -vt 0.031777 0.939699 -vt 0.038436 0.940305 -vt 0.524921 0.962804 -vt 0.524948 0.959269 -vt 0.527787 0.959177 -vt 0.315844 0.934887 -vt 0.321212 0.934369 -vt 0.318027 0.933941 -vt 0.323719 0.936471 -vt 0.507417 0.955484 -vt 0.510064 0.954788 -vt 0.509982 0.958269 -vt 0.510004 0.961822 -vt 0.510125 0.964898 -vt 0.032800 0.944413 -vt 0.040895 0.942668 -vt 0.525091 0.968716 -vt 0.524985 0.966308 -vt 0.320548 0.951472 -vt 0.323542 0.952849 -vt 0.322105 0.954971 -vt 0.312822 0.945612 -vt 0.315959 0.946683 -vt 0.319196 0.953673 -vt 0.318598 0.954957 -vt 0.316312 0.954328 -vt 0.904146 0.909181 -vt 0.902452 0.956308 -vt 0.902399 0.908945 -vt 0.911618 0.903048 -vt 0.909834 0.905732 -vt 0.908681 0.909757 -vt 0.904394 0.956568 -vt 0.912676 0.910237 -vt 0.909005 0.957152 -vt 0.249027 0.761712 -vt 0.249508 0.714399 -vt 0.249508 0.761732 -vt 0.893019 0.957458 -vt 0.891328 0.910310 -vt 0.893268 0.910054 -vt 0.897554 0.956884 -vt 0.897878 0.909471 -vt 0.901553 0.956394 -vt 0.901696 0.909005 -vt 0.902324 0.956306 -vt 0.902324 0.908935 -vt 0.912074 0.845316 -vt 0.911810 0.849883 -vt 0.909633 0.847355 -vt 0.909618 0.856752 -vt 0.910270 0.852710 -vt 0.911851 0.854149 -vt 0.553326 0.655357 -vt 0.540527 0.637429 -vt 0.540810 0.634735 -vt 0.553153 0.658288 -vt 0.540405 0.638603 -vt 0.394809 0.060761 -vt 0.391678 0.031361 -vt 0.396749 0.079116 -vt 0.398983 0.100292 -vt 0.401205 0.121390 -vt 0.403426 0.142515 -vt 0.405707 0.164276 -vt 0.408101 0.187175 -vt 0.409922 0.204646 -vt 0.411243 0.217356 -vt 0.412578 0.230250 -vt 0.413431 0.238518 -vt 0.857408 0.623113 -vt 0.847753 0.610748 -vt 0.833980 0.594622 -vt 0.814638 0.573396 -vt 0.794952 0.552628 -vt 0.529263 0.607690 -vt 0.529644 0.605309 -vt 0.991325 0.733101 -vt 0.991108 0.703033 -vt 0.384591 0.087685 -vt 0.387236 0.104186 -vt 0.390286 0.123212 -vt 0.393323 0.142157 -vt 0.396364 0.161118 -vt 0.399495 0.180636 -vt 0.402788 0.201159 -vt 0.405304 0.216832 -vt 0.870867 0.636981 -vt 0.871008 0.627406 -vt 0.519512 0.570234 -vt 0.519974 0.568222 -vt 0.990359 0.665792 -vt 0.991015 0.665070 -vt 0.373541 0.112039 -vt 0.375738 0.121623 -vt 0.378993 0.135789 -vt 0.382748 0.152111 -vt 0.386490 0.168350 -vt 0.390240 0.184589 -vt 0.394105 0.201290 -vt 0.899855 0.665448 -vt 0.891186 0.665822 -vt 0.891346 0.648016 -vt 0.880784 0.629590 -vt 0.871132 0.615325 -vt 0.861481 0.604408 -vt 0.511454 0.526117 -vt 0.511944 0.524635 -vt 0.990221 0.621360 -vt 0.990870 0.620790 -vt 0.365848 0.153805 -vt 0.362695 0.144032 -vt 0.368383 0.161545 -vt 0.372141 0.172983 -vt 0.376477 0.186147 -vt 0.380799 0.199226 -vt 0.385132 0.212286 -vt 0.906888 0.653998 -vt 0.900007 0.640908 -vt 0.891482 0.626981 -vt 0.880909 0.612304 -vt 0.871239 0.600961 -vt 0.861598 0.592351 -vt 0.379318 0.239822 -vt 0.374203 0.272614 -vt 0.857620 0.548686 -vt 0.841804 0.548742 -vt 0.498762 0.366421 -vt 0.499166 0.366287 -vt 0.988828 0.460128 -vt 0.370143 0.310877 -vt 0.359958 0.358230 -vt 0.355740 0.360218 -vt 0.365143 0.355716 -vt 0.370055 0.353267 -vt 0.908832 0.515874 -vt 0.913156 0.513700 -vt 0.902169 0.518933 -vt 0.894036 0.522299 -vt 0.884191 0.525990 -vt 0.874866 0.529192 -vt 0.867357 0.531490 -vt 0.357955 0.414969 -vt 0.991047 0.342476 -vt 0.362035 0.409602 -vt 0.367055 0.402921 -vt 0.372143 0.396064 -vt 0.908767 0.477195 -vt 0.912937 0.472032 -vt 0.902106 0.484597 -vt 0.893975 0.492552 -vt 0.884129 0.501050 -vt 0.874821 0.508210 -vt 0.867296 0.513175 -vt 0.506230 0.193136 -vt 0.500841 0.248291 -vt 0.505562 0.191605 -vt 0.990636 0.285254 -vt 0.989903 0.284850 -vt 0.356955 0.478720 -vt 0.815485 0.534958 -vt 0.842583 0.505633 -vt 0.385699 0.454947 -vt 0.521187 0.099457 -vt 0.991176 0.191209 -vt 0.899382 0.404338 -vt 0.842446 0.497653 -vt 0.815274 0.528375 -vt 0.530968 0.063271 -vt 0.530601 0.060737 -vt 0.991295 0.154625 -vt 0.842292 0.490891 -vt 0.989839 0.154264 -vt 0.541939 0.035065 -vt 0.541666 0.032227 -vt 0.530430 0.059494 -vt 0.541531 0.030726 -vt 0.410486 0.455267 -vt 0.413052 0.426341 -vt 0.553839 0.015488 -vt 0.553672 0.012432 -vt 0.553596 0.010863 -vt 0.406753 0.627889 -vt 0.407887 0.607417 -vt 0.409189 0.583670 -vt 0.410478 0.559860 -vt 0.411761 0.535796 -vt 0.413071 0.510741 -vt 0.414436 0.484054 -vt 0.415466 0.463467 -vt 0.416208 0.448318 -vt 0.416952 0.432734 -vt 0.417538 0.419950 -vt 0.414083 0.414269 -vt 0.418173 0.405283 -vt 0.566410 0.005211 -vt 0.566363 0.002025 -vt 0.566346 0.000432 -vt 0.418260 0.637306 -vt 0.418601 0.616188 -vt 0.418985 0.591696 -vt 0.419359 0.567138 -vt 0.419718 0.542322 -vt 0.420073 0.516490 -vt 0.420425 0.488980 -vt 0.420679 0.467775 -vt 0.420852 0.452162 -vt 0.421015 0.436093 -vt 0.421127 0.422937 -vt 0.419034 0.384459 -vt 0.579399 0.004890 -vt 0.579476 0.001580 -vt 0.419945 0.361620 -vt 0.579517 0.000084 -vt 0.592741 0.012135 -vt 0.592546 0.015198 -vt 0.592845 0.010573 -vt 0.423719 0.347801 -vt 0.498421 0.670372 -vt 0.444157 0.659912 -vt 0.421898 0.321915 -vt 0.605367 0.032988 -vt 0.605061 0.035820 -vt 0.605531 0.031490 -vt 0.987843 0.106064 -vt 0.988743 0.106171 -vt 0.518302 0.818181 -vt 0.541733 0.850128 -vt 0.498644 0.792425 -vt 0.616629 0.062728 -vt 0.616226 0.065247 -vt 0.616827 0.061493 -vt 0.468004 0.608070 -vt 0.574013 0.886269 -vt 0.986691 0.158257 -vt 0.448979 0.442555 -vt 0.446465 0.431282 -vt 0.560745 0.844913 -vt 0.634616 0.143735 -vt 0.456352 0.432941 -vt 0.640854 0.193390 -vt 0.640152 0.194914 -vt 0.986041 0.290072 -vt 0.464683 0.417642 -vt 0.645205 0.247086 -vt 0.644461 0.248062 -vt 0.985889 0.344036 -vt 0.470212 0.389233 -vt 0.498634 0.786682 -vt 0.560747 0.759198 -vt 0.640335 0.478790 -vt 0.987762 0.575394 -vt 0.608702 0.711041 -vt 0.633009 0.527562 -vt 0.633635 0.529434 -vt 0.518862 0.778511 -vt 0.986276 0.626653 -vt 0.987103 0.626846 -vt 0.987972 0.625778 -vt 0.545714 0.757333 -vt 0.545651 0.749326 -vt 0.601671 0.670216 -vt 0.518614 0.772136 -vt 0.545586 0.742534 -vt 0.614902 0.607285 -vt 0.986717 0.706558 -vt 0.590492 0.677595 -vt 0.603933 0.635490 -vt 0.604232 0.638190 -vt 0.615486 0.610779 -vt 0.604370 0.639366 -vt 0.452620 0.060076 -vt 0.450610 0.078468 -vt 0.448275 0.099681 -vt 0.445929 0.120812 -vt 0.443553 0.141973 -vt 0.441073 0.163774 -vt 0.438423 0.186713 -vt 0.436373 0.204212 -vt 0.439920 0.222157 -vt 0.433300 0.229849 -vt 0.434860 0.216943 -vt 0.567564 0.710230 -vt 0.574052 0.699807 -vt 0.592033 0.655067 -vt 0.592225 0.657989 -vt 0.592324 0.659372 -vt 0.443568 0.012177 -vt 0.443631 0.010873 -vt 0.432013 0.240217 -vt 0.579460 0.665344 -vt 0.579535 0.668400 -vt 0.579577 0.669802 -vt 0.348802 0.627548 -vt 0.426158 0.264148 -vt 0.424403 0.247607 -vt 0.566471 0.665665 -vt 0.566420 0.668831 -vt 0.424591 0.282725 -vt 0.566405 0.670152 -vt 0.553077 0.659670 -vt 0.419348 0.272696 -vt 0.420415 0.293884 -vt 0.349234 0.666809 -vt 0.421452 0.314613 -vt 0.690879 0.957919 -vt 0.696089 0.959501 -vt 0.696819 0.956234 -vt 0.551829 0.957058 -vt 0.211855 0.952739 -vt 0.167458 0.950634 -vt 0.322221 0.956443 -vt 0.014578 0.950717 -vt 0.022986 0.973778 -vt 0.330116 0.965579 -vt 0.322217 0.965579 -vt 0.751345 0.945369 -vt 0.236435 0.962955 -vt 0.479977 0.952645 -vt 0.330853 0.960307 -vt 0.966709 0.823643 -vt 0.313220 0.965655 -vt 0.305324 0.965655 -vt 0.782726 0.966280 -vt 0.471522 0.952645 -vt 0.629245 0.960339 -vt 0.007218 0.974596 -vt 0.321675 0.965655 -vt 0.313780 0.965655 -vt 0.716116 0.000000 -vt 0.726288 0.660767 -vt 0.349823 0.071564 -vt 0.349401 0.303757 -vt 0.345478 0.004631 -vt 0.344830 0.367188 -vt 0.231865 0.714375 -vt 0.000000 0.714375 -vt 0.873836 0.010048 -vt 0.339855 0.000000 -vt 0.339199 0.370880 -vt 0.237904 0.709053 -vt 0.903237 0.000191 -vt 0.332790 0.001129 -vt 0.332144 0.369286 -vt 0.249836 0.702040 -vt 0.918303 0.000000 -vt 0.323988 0.007614 -vt 0.323366 0.362968 -vt 0.225830 0.682243 -vt 0.903919 0.030281 -vt 0.305941 0.029915 -vt 0.305492 0.343391 -vt 0.955688 0.706188 -vt 0.930799 0.000015 -vt 0.897026 0.054375 -vt 0.923533 0.447365 -vt 0.881413 0.677578 -vt 0.881393 0.690968 -vt 0.006843 0.797468 -vt 0.016287 0.179451 -vt 0.038437 0.820544 -vt 0.928178 0.415951 -vt 0.069067 0.830353 -vt 0.960050 0.399506 -vt 0.698483 0.902403 -vt 0.698679 0.906504 -vt 0.880479 0.805103 -vt 0.642313 0.909395 -vt 0.909572 0.678690 -vt 0.693146 0.893883 -vt 0.633283 0.906456 -vt 0.743827 0.877698 -vt 0.620317 0.893055 -vt 0.741127 0.871277 -vt 0.884939 0.849510 -vt 0.845228 0.888228 -vt 0.125935 0.954893 -vt 0.848862 0.888885 -vt 0.676247 0.677781 -vt 0.978623 0.702985 -vt 0.871119 0.874287 -vt 0.875605 0.873587 -vt 0.877876 0.876312 -vt 0.991469 0.864531 -vt 0.236475 0.234764 -vt 0.204987 0.921062 -vt 0.194942 0.915534 -vt 0.226540 0.262932 -vt 0.488610 0.891276 -vt 0.976479 0.876097 -vt 0.223379 0.263214 -vt 0.609749 0.976373 -vt 0.606328 0.975223 -vt 0.605350 0.973409 -vt 0.102669 0.275131 -vt 0.474440 0.754667 -vt 0.958910 0.285542 -vt 0.248692 0.849635 -vt 0.808784 0.914954 -vt 0.964200 0.288395 -vt 0.965719 0.285887 -vt 0.692593 0.887948 -vt 0.985516 0.106300 -vt 0.670609 0.888494 -vt 0.974553 0.587826 -vt 0.681816 0.880627 -vt 0.964167 0.579173 -vt 0.690181 0.877615 -vt 0.232483 0.714929 -vt 0.689197 0.874414 -vt 0.336624 0.851952 -vt 0.320555 0.370878 -vt 0.335908 0.849104 -vt 0.334112 0.846393 -vt 0.330905 0.845161 -vt 0.348993 0.747063 -vt 0.326932 0.845570 -vt 0.497076 0.754543 -vt 0.324279 0.838025 -vt 0.644996 0.895566 -vt 0.328711 0.454872 -vt 0.483225 0.973754 -vt 0.500956 0.971793 -vt 0.504402 0.971634 -vt 0.643778 0.862615 -vt 0.645789 0.862771 -vt 0.347937 0.765570 -vt 0.349613 0.765575 -vt 0.495732 0.836596 -vt 0.493704 0.836663 -vt 0.343035 0.370891 -vt 0.832188 0.908019 -vt 0.840190 0.911833 -vt 0.833012 0.905923 -vt 0.838536 0.907825 -vt 0.834543 0.905348 -vt 0.836411 0.905819 -vt 0.740322 0.906509 -vt 0.544295 0.975693 -vt 0.578403 0.969575 -vt 0.547188 0.974701 -vt 0.188051 0.805444 -vt 0.347879 0.626982 -vt 0.180146 0.866731 -vt 0.886137 0.962624 -vt 0.349289 0.625877 -vt 0.088210 0.742924 -vt 0.138867 0.895008 -vt 0.494105 0.887800 -vt 0.969590 0.826132 -vt 0.681437 0.965432 -vt 0.973986 0.824110 -vt 0.520518 0.953177 -vt 0.497422 0.891193 -vt 0.516030 0.957325 -vt 0.514225 0.957932 -vt 0.075165 0.756134 -vt 0.703241 0.957113 -vt 0.588743 0.935350 -vt 0.219219 0.975679 -vt 0.593696 0.966177 -vt 0.218140 0.971998 -vt 0.497399 0.745162 -vt 0.112513 0.808996 -vt 0.067756 0.960814 -vt 0.282291 0.853664 -vt 0.628133 0.936707 -vt 0.281827 0.948983 -vt 0.956326 0.858058 -vt 0.512940 0.951584 -vt 0.952332 0.854044 -vt 0.625644 0.964819 -vt 0.719663 0.957298 -vt 0.626488 0.967273 -vt 0.621984 0.937073 -vt 0.170491 0.973099 -vt 0.583627 0.957261 -vt 0.054442 0.958844 -vt 0.582581 0.956427 -vt 0.343717 0.954172 -vt 0.431278 0.899002 -vt 0.950670 0.823137 -vt 0.537054 0.954760 -vt 0.217527 0.909461 -vt 0.159798 0.911043 -vt 0.960379 0.709803 -vt 0.524984 0.943981 -vt 0.213972 0.928939 -vt 0.158309 0.896097 -vt 0.571391 0.928094 -vt 0.561511 0.941424 -vt 0.944713 0.071853 -vt 0.770011 0.966234 -vt 0.199917 0.921338 -vt 0.949200 0.084688 -vt 0.950886 0.087769 -vt 0.198313 0.935838 -vt 0.791917 0.941828 -vt 0.231461 0.911278 -vt 0.348363 0.667117 -vt 0.637681 0.960567 -vt 0.354164 0.910815 -vt 0.628595 0.911053 -vt 0.751239 0.920713 -vt 0.466415 0.917255 -vt 0.821054 0.905764 -vt 0.060123 0.858508 -vt 0.188178 0.895526 -vt 0.772588 0.909454 -vt 0.202212 0.790256 -vt 0.880343 0.872591 -vt 0.347994 0.734163 -vt 0.915803 0.923820 -vt 0.347938 0.574431 -vt 0.347943 0.788298 -vt 0.972613 0.312268 -vt 0.347132 0.469298 -vt 0.018510 0.937494 -vt 0.017990 0.934743 -vt 0.029541 0.935809 -vt 0.018385 0.931873 -vt 0.029851 0.932932 -vt 0.019630 0.929331 -vt 0.029248 0.930214 -vt 0.022143 0.927216 -vt 0.025324 0.926785 -vt 0.027828 0.928079 -vt 0.028368 0.938401 -vt 0.118878 0.955443 -vt 0.112963 0.954933 -vt 0.293528 0.955228 -vt 0.292188 0.952804 -vt 0.006596 0.937494 -vt 0.006076 0.934743 -vt 0.017627 0.935809 -vt 0.006470 0.931874 -vt 0.017937 0.932932 -vt 0.007716 0.929331 -vt 0.017333 0.930214 -vt 0.010229 0.927216 -vt 0.013409 0.926785 -vt 0.015913 0.928079 -vt 0.016454 0.938401 -vt 0.189059 0.955853 -vt 0.347037 0.954231 -vt 0.329354 0.955228 -vt 0.328015 0.952804 -vt 0.042339 0.937494 -vt 0.041819 0.934743 -vt 0.053371 0.935809 -vt 0.042214 0.931874 -vt 0.053680 0.932932 -vt 0.043460 0.929331 -vt 0.053077 0.930214 -vt 0.045973 0.927216 -vt 0.049153 0.926785 -vt 0.051657 0.928079 -vt 0.052197 0.938401 -vt 0.194807 0.955853 -vt 0.118732 0.954933 -vt 0.305469 0.955228 -vt 0.304130 0.952804 -vt 0.030425 0.937494 -vt 0.029905 0.934742 -vt 0.041456 0.935809 -vt 0.030300 0.931873 -vt 0.041766 0.932931 -vt 0.031545 0.929331 -vt 0.041163 0.930214 -vt 0.034059 0.927215 -vt 0.037239 0.926785 -vt 0.039743 0.928079 -vt 0.040283 0.938401 -vt 0.525063 0.956254 -vt 0.510173 0.952678 -vt 0.317412 0.955228 -vt 0.316072 0.952803 -vt 0.912820 0.957608 -vt 0.249004 0.714380 -vt 0.891275 0.957689 -vt 0.910984 0.851801 -vt 0.910324 0.851216 -vt 0.909841 0.842712 -vt 0.910727 0.840540 -vt 0.911422 0.841274 -vt 0.910965 0.851977 -vt 0.912059 0.858792 -vt 0.911368 0.862652 -vt 0.909883 0.861318 -vt 0.910708 0.863236 -vt 0.864185 0.632865 -vt 0.896275 0.677578 -vt 0.903996 0.674911 -vt 0.374950 0.392242 -vt 0.794967 0.546714 -vt 0.388845 0.443971 -vt 0.414978 0.403431 -vt 0.988280 0.105864 -vt 0.583502 0.905462 -vt 0.603701 0.910612 -vt 0.464676 0.245117 -vt 0.437843 0.233978 -vt 0.431164 0.246876 -vt 0.349234 0.627548 -vn -0.8112 0.4167 -0.4102 -vn -0.7814 0.1265 -0.6110 -vn -0.9803 0.1968 0.0169 -vn -0.2109 0.0908 -0.9733 -vn 0.0677 0.3233 -0.9439 -vn -0.1531 0.2073 -0.9662 -vn 0.7231 0.6643 0.1893 -vn 0.4423 0.7261 0.5265 -vn 0.1804 0.7812 -0.5977 -vn 0.5189 -0.7267 0.4502 -vn 0.2775 -0.3635 0.8893 -vn 0.1606 -0.8998 0.4058 -vn -0.0489 0.8661 0.4974 -vn -0.4042 0.5459 0.7339 -vn 0.1674 0.3161 0.9338 -vn -0.2202 -0.0485 -0.9743 -vn -0.2288 0.0254 -0.9731 -vn -0.1724 -0.1521 -0.9732 -vn 0.9824 -0.1112 0.1499 -vn 0.8305 0.3040 0.4667 -vn 0.6517 -0.1710 0.7389 -vn 0.7789 -0.5721 0.2571 -vn 0.3158 0.1852 0.9306 -vn 0.3197 -0.0241 0.9472 -vn -0.2391 -0.1937 -0.9515 -vn -0.2006 0.0120 -0.9796 -vn 0.4184 -0.4309 -0.7995 -vn 0.6332 0.4105 -0.6561 -vn 0.5246 0.0013 -0.8513 -vn -0.8598 0.1602 0.4848 -vn -0.9952 -0.0892 -0.0411 -vn -0.8408 -0.1364 0.5238 -vn 0.4248 -0.7796 -0.4601 -vn 0.2009 -0.6793 -0.7058 -vn -0.2607 0.8944 -0.3634 -vn -0.6907 0.7096 -0.1395 -vn -0.2134 -0.3021 -0.9291 -vn -0.0554 -0.8715 -0.4873 -vn 0.6747 0.6806 0.2857 -vn 0.5545 0.7037 -0.4443 -vn -0.3663 0.0801 0.9270 -vn -0.5200 0.3246 0.7901 -vn -0.4828 -0.7930 -0.3715 -vn -0.1286 -0.9917 -0.0008 -vn -0.2530 -0.8222 0.5099 -vn 0.3053 0.0319 0.9517 -vn 0.1466 -0.0901 0.9851 -vn 0.2969 -0.1088 0.9487 -vn 0.5369 0.2716 0.7988 -vn 0.3653 0.1353 0.9210 -vn 0.3339 -0.2062 0.9198 -vn 0.2886 -0.0344 0.9568 -vn -0.3665 -0.0307 -0.9299 -vn -0.0266 -0.8346 -0.5502 -vn 0.1125 -0.8451 0.5226 -vn 0.2400 -0.8537 0.4623 -vn -0.1784 -0.8045 -0.5665 -vn 0.3045 0.7895 0.5329 -vn 0.0270 0.8628 -0.5048 -vn 0.4778 0.7403 0.4729 -vn 0.0090 -0.7960 -0.6053 -vn -0.2259 0.0422 -0.9732 -vn -0.0082 0.3267 -0.9451 -vn -0.1764 0.1716 -0.9692 -vn 0.5553 0.8105 0.1861 -vn 0.2702 0.8060 0.5267 -vn -0.0036 0.8196 -0.5729 -vn 0.1432 0.0872 0.9858 -vn -0.5761 0.2218 0.7867 -vn -0.5744 0.0374 0.8177 -vn -0.5191 0.4441 0.7303 -vn 0.0925 0.3452 0.9339 -vn -0.2368 0.8329 0.5001 -vn 0.1453 0.0055 0.9894 -vn -0.2858 -0.1822 0.9408 -vn 0.1613 -0.0668 0.9846 -vn -0.2037 -0.0963 -0.9743 -vn -0.2284 -0.0273 -0.9732 -vn -0.1343 -0.1853 -0.9735 -vn 0.9778 0.1100 0.1781 -vn 0.7507 0.4673 0.4669 -vn 0.6950 -0.0436 0.7177 -vn 0.8861 -0.3990 0.2358 -vn 0.2669 0.2517 0.9303 -vn 0.3175 0.0462 0.9471 -vn -0.1908 -0.2421 -0.9513 -vn -0.1974 -0.0330 -0.9798 -vn 0.5023 -0.3216 -0.8026 -vn 0.3086 0.8319 0.4611 -vn 0.5397 0.5631 -0.6258 -vn 0.0061 0.8107 -0.5855 -vn 0.7474 0.0643 -0.6612 -vn 0.1993 -0.7740 -0.6010 -vn 0.4624 -0.7603 0.4562 -vn -0.1354 -0.3454 -0.9286 -vn 0.3483 -0.6071 -0.7142 -vn 0.1240 -0.8621 -0.4913 -vn 0.2752 0.7825 -0.5586 -vn 0.3696 0.7883 -0.4919 -vn -0.8651 0.4831 -0.1354 -vn -0.9929 0.1177 -0.0159 -vn -0.2976 -0.8865 -0.3542 -vn -0.4199 -0.5669 0.7087 -vn -0.7305 -0.5379 -0.4207 -vn -0.4482 0.8176 -0.3614 -vn 0.1702 0.8959 0.4102 -vn -0.0809 0.8636 -0.4977 -vn -0.7734 0.1128 -0.6238 -vn -0.3619 -0.0276 -0.9318 -vn -0.8951 -0.1167 -0.4303 -vn -0.3846 -0.1032 -0.9173 -vn -0.3175 -0.3705 -0.8729 -vn 0.2905 0.0986 0.9518 -vn 0.2998 -0.0279 0.9536 -vn -0.5453 0.3309 -0.7701 -vn -0.3542 0.0332 -0.9346 -vn 0.4623 0.3839 0.7993 -vn 0.3257 0.2143 0.9209 -vn 0.3706 -0.1252 0.9203 -vn 0.2892 0.0311 0.9568 -vn 0.3510 -0.8424 0.4088 -vn 0.4340 -0.3570 0.8272 -vn -0.0276 -0.8688 0.4943 -vn 0.1205 0.8379 0.5324 -vn -0.1736 0.8107 -0.5592 -vn 0.0109 -0.8243 -0.5660 -vn -0.2200 0.0665 -0.9732 -vn 0.0327 0.3303 -0.9433 -vn -0.1561 0.1907 -0.9692 -vn 0.6412 0.7442 0.1871 -vn 0.3576 0.7711 0.5268 -vn 0.0867 0.8159 -0.5716 -vn 0.1523 0.0709 0.9858 -vn -0.5639 0.2761 0.7783 -vn -0.5735 0.1130 0.8114 -vn 0.2717 -0.8756 0.3994 -vn 0.5950 -0.6658 0.4502 -vn 0.3141 -0.3237 0.8925 -vn -0.4728 0.5025 0.7238 -vn 0.1299 0.3330 0.9339 -vn -0.1529 0.8539 0.4975 -vn 0.1453 -0.0105 0.9893 -vn -0.3039 -0.1484 0.9411 -vn 0.1523 -0.0843 0.9847 -vn -0.2133 -0.0730 -0.9743 -vn -0.2301 -0.0020 -0.9732 -vn -0.1539 -0.1699 -0.9734 -vn 0.9891 -0.0005 0.1475 -vn 0.7976 0.3809 0.4677 -vn 0.6856 -0.1184 0.7183 -vn 0.8372 -0.4936 0.2356 -vn 0.2929 0.2205 0.9304 -vn 0.3210 0.0115 0.9470 -vn -0.2154 -0.2203 -0.9514 -vn -0.2017 -0.0091 -0.9794 -vn 0.4686 -0.3643 -0.8048 -vn 0.3993 0.7923 0.4613 -vn 0.5815 0.4827 -0.6548 -vn 0.0959 0.8041 -0.5866 -vn 0.5162 0.0692 -0.8536 -vn 0.1122 -0.7924 -0.5996 -vn 0.3765 -0.8058 0.4571 -vn 0.5223 -0.7272 -0.4454 -vn 0.0696 -0.8325 -0.5497 -vn 0.6729 -0.7308 0.1146 -vn -0.7664 0.5207 -0.3762 -vn -0.3667 0.8646 -0.3436 -vn -0.3504 0.3274 -0.8775 -vn -0.1711 -0.3322 -0.9276 -vn 0.2855 -0.6496 -0.7046 -vn 0.0320 -0.8757 -0.4818 -vn -0.8683 0.2741 -0.4135 -vn -0.3916 -0.8497 -0.3531 -vn -0.4800 -0.5181 0.7079 -vn -0.7858 -0.4534 -0.4207 -vn 0.2678 0.8715 0.4108 -vn 0.0152 0.8674 -0.4973 -vn -0.9041 0.1431 -0.4027 -vn -0.3593 0.0135 -0.9331 -vn -0.9018 -0.0221 -0.4315 -vn -0.3929 -0.0602 -0.9176 -vn -0.3586 -0.3356 -0.8711 -vn 0.2997 0.0662 0.9517 -vn 0.3074 -0.0743 0.9487 -vn 0.5020 0.3305 0.7993 -vn 0.3474 0.1769 0.9209 -vn 0.3549 -0.1669 0.9199 -vn 0.2909 -0.0010 0.9567 -vn -0.1244 -0.8593 0.4962 -vn 0.2081 -0.8265 0.5230 -vn 0.3379 -0.8183 0.4649 -vn -0.0860 -0.8199 -0.5660 -vn 0.2124 0.8192 0.5327 -vn -0.0834 0.8252 -0.5586 -vn 0.6412 0.7442 0.1870 -vn 0.2715 -0.8757 0.3994 -vn -0.1527 0.8540 0.4974 -vn -0.3039 -0.1485 0.9411 -vn 0.6855 -0.1184 0.7183 -vn 0.1122 -0.7924 -0.5995 -vn -0.3665 0.8647 -0.3436 -vn 0.0318 -0.8758 -0.4817 -vn -0.3860 -0.8539 -0.3491 -vn -0.4804 -0.5180 0.7077 -vn -0.7853 -0.4543 -0.4206 -vn -0.3597 -0.3380 -0.8697 -vn -0.1260 -0.8552 0.5028 -vn 0.1380 -0.9446 -0.2979 -vn 0.1334 -0.9214 -0.3651 -vn 0.0936 -0.9374 -0.3355 -vn 0.1842 -0.9568 -0.2249 -vn 0.2048 -0.9314 -0.3008 -vn 0.1500 -0.9533 -0.2623 -vn 0.2095 -0.9655 -0.1549 -vn 0.2568 -0.9389 -0.2292 -vn 0.1875 -0.9628 -0.1948 -vn 0.2229 -0.9706 -0.0911 -vn 0.2933 -0.9434 -0.1546 -vn 0.2099 -0.9687 -0.1327 -vn 0.3151 -0.9461 -0.0754 -vn 0.2218 -0.9723 -0.0741 -vn 0.2318 -0.9722 -0.0337 -vn 0.3329 -0.9429 0.0057 -vn 0.2412 -0.9703 -0.0190 -vn 0.2494 -0.9681 0.0248 -vn 0.3351 -0.9379 0.0903 -vn 0.2599 -0.9646 0.0453 -vn 0.2587 -0.9616 0.0919 -vn 0.3214 -0.9299 0.1790 -vn 0.2699 -0.9552 0.1211 -vn 0.2563 -0.9514 0.1706 -vn 0.2872 -0.9193 0.2690 -vn 0.2619 -0.9423 0.2084 -vn 0.2392 -0.9349 0.2620 -vn -0.1596 -0.8665 0.4730 -vn -0.0922 -0.8658 0.4919 -vn -0.0933 -0.8696 0.4848 -vn -0.2740 -0.8392 -0.4697 -vn -0.3017 -0.8722 -0.3850 -vn -0.2415 -0.8779 -0.4135 -vn -0.3966 -0.8483 0.3508 -vn -0.3510 -0.8434 0.4068 -vn -0.3443 -0.8506 0.3973 -vn -0.4968 -0.8439 -0.2025 -vn -0.5114 -0.8493 -0.1308 -vn -0.4832 -0.8535 -0.1952 -vn -0.0305 -0.8378 0.5451 -vn 0.0482 -0.8174 0.5740 -vn 0.0428 -0.8672 0.4961 -vn -0.1715 -0.8369 0.5197 -vn -0.0998 -0.8295 0.5495 -vn -0.5043 -0.8482 0.1617 -vn -0.4763 -0.8483 0.2313 -vn -0.4791 -0.8466 0.2320 -vn -0.5209 -0.8490 0.0882 -vn -0.5249 -0.8465 0.0891 -vn -0.2197 -0.8179 -0.5317 -vn -0.1878 -0.8688 -0.4581 -vn 0.5548 -0.2710 -0.7866 -vn 0.4551 -0.2524 -0.8539 -vn 0.4406 -0.3229 -0.8376 -vn 0.8563 -0.2494 0.4523 -vn 0.9013 -0.2684 0.3399 -vn 0.8349 -0.3213 0.4469 -vn -0.7318 -0.2808 0.6210 -vn -0.6519 -0.2572 0.7133 -vn -0.6332 -0.3307 0.6998 -vn -0.9040 -0.2594 -0.3397 -vn -0.9338 -0.2823 -0.2199 -vn -0.8815 -0.3315 -0.3363 -vn 0.9016 -0.0996 -0.4210 -vn 0.8707 -0.0829 -0.4847 -vn 0.8373 -0.0990 -0.5377 -vn -0.8736 -0.0875 0.4788 -vn -0.8361 -0.1081 0.5378 -vn -0.9000 -0.1086 0.4220 -vn 0.7693 -0.0812 -0.6338 -vn 0.7258 -0.0665 -0.6847 -vn 0.6736 -0.0796 -0.7348 -vn 0.9503 -0.0715 -0.3032 -vn 0.8631 -0.0624 -0.5012 -vn 0.9523 -0.0634 0.2985 -vn 0.9686 -0.0796 0.2357 -vn 0.9333 -0.0776 0.3506 -vn -0.8101 -0.0716 0.5819 -vn -0.7694 -0.0855 0.6330 -vn -0.9260 -0.0693 0.3712 -vn -0.9474 -0.0863 0.3083 -vn -0.9965 -0.0713 -0.0427 -vn -0.9955 -0.0875 0.0373 -vn -0.9913 -0.0876 -0.0979 -vn -0.9569 -0.0718 -0.2814 -vn -0.9704 -0.0877 -0.2252 -vn -0.9325 -0.0867 -0.3505 -vn 0.5836 -0.0669 -0.8093 -vn 0.5277 -0.0551 -0.8476 -vn 0.4671 -0.0658 -0.8818 -vn -0.7284 -0.0598 0.6825 -vn -0.6829 -0.0688 0.7273 -vn -0.8740 -0.0593 0.4823 -vn -0.9704 -0.0597 0.2339 -vn -0.9832 -0.0710 0.1679 -vn 0.8388 -0.0504 -0.5422 -vn 0.7096 -0.0463 -0.7031 -vn 0.7115 -0.0548 -0.7005 -vn 0.8063 -0.0448 0.5898 -vn 0.8863 -0.0492 0.4605 -vn 0.8108 -0.0552 0.5827 -vn -0.5857 -0.0565 0.8086 -vn -0.5270 -0.0499 0.8484 -vn -0.4669 -0.0566 0.8825 -vn -0.8539 -0.0478 -0.5183 -vn -0.8867 -0.0583 -0.4586 -vn -0.8154 -0.0574 -0.5761 -vn 0.9053 -0.0510 -0.4217 -vn 0.8749 -0.0183 -0.4839 -vn 0.9845 -0.0519 -0.1674 -vn 0.9719 -0.0162 -0.2349 -vn 0.9528 -0.0509 -0.2992 -vn 0.9938 -0.0518 0.0988 -vn 0.9994 -0.0156 0.0319 -vn 0.9981 -0.0518 -0.0345 -vn 0.9311 -0.0537 0.3607 -vn 0.9561 -0.0164 0.2925 -vn 0.9716 -0.0484 0.2314 -vn 0.8568 -0.0201 0.5153 -vn -0.0399 -0.0519 -0.9979 -vn -0.1629 -0.0767 -0.9837 -vn -0.1593 -0.0482 -0.9861 -vn 0.3608 -0.0333 -0.9321 -vn 0.3013 -0.0016 -0.9535 -vn 0.2361 -0.0376 -0.9710 -vn 0.0302 -0.0437 0.9986 -vn 0.1731 -0.0241 0.9846 -vn 0.1803 -0.0393 0.9828 -vn -0.0893 -0.0655 0.9938 -vn -0.0943 -0.0464 0.9945 -vn -0.2592 -0.1530 0.9536 -vn -0.2194 -0.0817 0.9722 -vn -0.3510 -0.1006 0.9310 -vn -0.6398 -0.2803 -0.7156 -vn -0.7291 -0.1586 -0.6658 -vn -0.6310 -0.1418 -0.7627 -vn 0.1122 -0.0213 -0.9935 -vn 0.0039 -0.0798 -0.9968 -vn -0.0162 -0.0678 -0.9976 -vn 0.6266 0.1438 0.7660 -vn 0.6304 0.2878 0.7209 -vn 0.7225 0.1764 0.6685 -vn -0.1482 -0.3967 0.9059 -vn -0.1551 -0.2606 0.9529 -vn -0.3165 -0.4174 0.8518 -vn -0.2592 -0.5728 0.7777 -vn -0.4593 -0.5585 0.6907 -vn -0.3543 -0.7242 0.5916 -vn -0.5691 -0.6669 0.4810 -vn -0.4216 -0.8317 0.3612 -vn -0.6372 -0.7342 0.2341 -vn -0.4547 -0.8847 0.1026 -vn -0.6569 -0.7534 -0.0305 -vn -0.4505 -0.8780 -0.1617 -vn -0.6269 -0.7232 -0.2897 -vn -0.4106 -0.8143 -0.4103 -vn -0.5520 -0.6483 -0.5244 -vn -0.3391 -0.7003 -0.6282 -vn -0.4436 -0.5409 -0.7146 -vn -0.2462 -0.5495 -0.7984 -vn -0.3362 -0.4381 -0.8337 -vn 0.2223 0.2000 -0.9543 -vn 0.1360 0.1246 -0.9828 -vn 0.1244 0.0430 -0.9913 -vn 0.3433 0.3895 -0.8547 -vn 0.2244 0.3416 -0.9127 -vn 0.4524 0.5595 -0.6945 -vn 0.3046 0.5457 -0.7807 -vn 0.5369 0.6907 -0.4845 -vn 0.3698 0.7136 -0.5951 -vn 0.5894 0.7726 -0.2360 -vn 0.4165 0.8331 -0.3640 -vn 0.6040 0.7963 0.0312 -vn 0.4397 0.8922 -0.1034 -vn 0.5798 0.7603 0.2927 -vn 0.4368 0.8846 0.1632 -vn 0.5205 0.6701 0.5292 -vn 0.4092 0.8133 0.4136 -vn 0.4373 0.5394 0.7196 -vn 0.3599 0.6856 0.6327 -vn 0.3606 0.4118 0.8369 -vn 0.2924 0.5183 0.8036 -vn 0.0078 -0.3774 -0.9260 -vn -0.0734 -0.4374 -0.8963 -vn -0.0289 -0.3104 -0.9502 -vn 0.0706 -0.2365 -0.9691 -vn 0.0459 -0.1884 -0.9810 -vn 0.1488 0.2791 0.9487 -vn 0.1213 0.3570 0.9262 -vn 0.1703 0.4040 0.8988 -vn 0.1130 0.1481 0.9825 -vn 0.0696 -0.2349 0.9695 -vn 0.0728 -0.1196 0.9901 -vn 0.0368 -0.2388 0.9704 -vn 0.1219 0.2150 -0.9690 -vn 0.0426 0.1610 -0.9860 -vn 0.1051 0.0776 -0.9914 -vn 0.0697 -0.2930 0.9536 -vn 0.0515 -0.4171 0.9074 -vn 0.0848 -0.5159 0.8525 -vn 0.0321 -0.6246 0.7803 -vn 0.0939 -0.7160 0.6918 -vn 0.0161 -0.8042 0.5942 -vn 0.1026 -0.8701 0.4821 -vn 0.0048 -0.9316 0.3634 -vn 0.1080 -0.9658 0.2355 -vn -0.0008 -0.9946 0.1040 -vn 0.1094 -0.9936 -0.0287 -vn -0.0003 -0.9869 -0.1612 -vn 0.1069 -0.9518 -0.2875 -vn 0.0059 -0.9119 -0.4105 -vn 0.1010 -0.8468 -0.5222 -vn 0.0171 -0.7774 -0.6288 -vn 0.0926 -0.6940 -0.7140 -vn 0.0307 -0.6029 -0.7972 -vn 0.0655 -0.2287 -0.9713 -vn 0.0563 -0.4361 -0.8981 -vn 0.0344 0.3974 -0.9170 -vn -0.0600 0.2755 -0.9594 -vn 0.0246 0.6189 -0.7851 -vn -0.1193 0.4922 -0.8623 -vn 0.0179 0.8008 -0.5986 -vn -0.1745 0.6893 -0.7032 -vn 0.0132 0.9303 -0.3665 -vn -0.2171 0.8430 -0.4922 -vn 0.0108 0.9944 -0.1048 -vn -0.2436 0.9394 -0.2412 -vn 0.0108 0.9866 0.1628 -vn -0.2513 0.9675 0.0292 -vn 0.0130 0.9101 0.4142 -vn -0.2398 0.9253 0.2937 -vn 0.0175 0.7729 0.6343 -vn -0.2106 0.8201 0.5320 -vn 0.0245 0.5817 0.8131 -vn -0.1657 0.6599 0.7329 -vn 0.0357 0.3599 0.9323 -vn -0.1070 0.4513 0.8859 -vn 0.0464 0.1737 0.9837 -vn -0.0424 0.2215 0.9742 -vn 0.1508 -0.4584 -0.8758 -vn 0.0843 -0.5404 -0.8372 -vn -0.0769 0.0688 -0.9947 -vn 0.0628 -0.0273 -0.9977 -vn -0.0542 0.0792 -0.9954 -vn -0.3627 0.3700 -0.8553 -vn -0.3298 0.2329 -0.9149 -vn -0.2418 0.2582 -0.9354 -vn -0.5023 0.5030 -0.7033 -vn -0.5065 0.3483 -0.7888 -vn -0.6178 0.6132 -0.4923 -vn -0.6601 0.4479 -0.6030 -vn -0.6902 0.6821 -0.2417 -vn -0.7707 0.5186 -0.3702 -vn -0.7113 0.7023 0.0280 -vn -0.8257 0.5538 -0.1074 -vn -0.6800 0.6727 0.2918 -vn -0.8196 0.5498 0.1615 -vn -0.6015 0.5979 0.5297 -vn -0.7551 0.5086 0.4138 -vn -0.4820 0.4835 0.7306 -vn -0.6393 0.4350 0.6341 -vn -0.3264 0.3339 0.8843 -vn -0.4782 0.3323 0.8130 -vn -0.1556 0.1793 0.9714 -vn -0.2905 0.2124 0.9330 -vn -0.0051 0.0258 0.9997 -vn -0.1537 0.1143 0.9815 -vn 0.5437 -0.3237 0.7743 -vn 0.3919 -0.3560 0.8483 -vn 0.5277 -0.4894 0.6943 -vn 0.6870 -0.4154 0.5962 -vn 0.6390 -0.5977 0.4843 -vn 0.7949 -0.4846 0.3651 -vn 0.7082 -0.6650 0.2372 -vn 0.8482 -0.5190 0.1057 -vn 0.7283 -0.6847 -0.0276 -vn 0.8421 -0.5152 -0.1594 -vn 0.6984 -0.6557 -0.2870 -vn 0.7793 -0.4750 -0.4088 -vn 0.6229 -0.5826 -0.5220 -vn 0.6665 -0.4024 -0.6275 -vn 0.5073 -0.4708 -0.7219 -vn 0.5090 -0.3011 -0.8064 -vn 0.3555 -0.3241 -0.8767 -vn 0.3226 -0.1830 -0.9287 -vn 0.1815 -0.1588 -0.9705 -vn -0.0890 0.0453 -0.9950 -vn 0.0583 -0.0014 -0.9983 -vn -0.0812 0.0566 -0.9951 -vn -0.1489 0.0743 0.9861 -vn -0.3561 0.1204 0.9266 -vn -0.3305 0.1706 0.9283 -vn 0.0587 0.0121 -0.9982 -vn 0.1781 -0.0246 -0.9837 -vn -0.5952 0.1943 -0.7797 -vn -0.5054 0.1164 -0.8550 -vn -0.4469 0.1522 -0.8816 -vn -0.7605 0.2429 -0.6022 -vn -0.6960 0.1527 -0.7016 -vn -0.8860 0.2802 -0.3695 -vn -0.8521 0.1825 -0.4906 -vn -0.9483 0.2987 -0.1073 -vn -0.9495 0.2012 -0.2409 -vn -0.9412 0.2971 0.1607 -vn -0.9778 0.2059 0.0383 -vn -0.8669 0.2770 0.4144 -vn -0.9119 0.1963 0.3604 -vn 0.5111 -0.0769 0.8561 -vn 0.4605 -0.1195 0.8796 -vn 0.6086 -0.1651 0.7761 -vn 0.6994 -0.1131 0.7057 -vn 0.7719 -0.2155 0.5981 -vn 0.8557 -0.1429 0.4973 -vn 0.8954 -0.2527 0.3665 -vn 0.9569 -0.1624 0.2408 -vn 0.9566 -0.2713 0.1067 -vn 0.9825 -0.1649 -0.0867 -vn 0.9499 -0.2678 -0.1611 -vn -0.2300 0.0414 -0.9723 -vn -0.0855 0.0332 -0.9958 -vn -0.2256 0.0652 -0.9720 -vn -0.6203 0.0809 -0.7802 -vn -0.7926 0.0959 -0.6021 -vn -0.9249 0.1092 -0.3642 -vn -0.9890 0.1128 -0.0961 -vn -0.9788 0.1122 0.1715 -vn 0.0988 0.0148 0.9950 -vn -0.0233 0.0249 0.9994 -vn 0.0990 0.0007 0.9951 -vn 0.6286 -0.0388 0.7768 -vn 0.7993 -0.0522 0.5987 -vn 0.9277 -0.0646 0.3676 -vn 0.9912 -0.0691 0.1131 -vn 0.0152 0.0181 -0.9997 -vn 0.1824 0.0059 -0.9832 -vn -0.8781 0.0415 -0.4767 -vn -0.9700 0.0428 -0.2392 -vn -0.9986 0.0443 0.0299 -vn -0.9555 0.0416 0.2919 -vn -0.9012 0.1051 0.4204 -vn -0.8491 0.0401 0.5268 -vn -0.7694 0.0888 0.6326 -vn -0.6873 0.0368 0.7255 -vn -0.5800 0.0726 0.8114 -vn -0.4890 0.0318 0.8717 -vn -0.3653 0.0517 0.9294 -vn 0.7259 0.0060 0.6878 -vn 0.8711 0.0034 0.4911 -vn 0.9702 0.0041 0.2424 -vn 0.9860 -0.0714 -0.1508 -vn 0.9996 0.0032 -0.0272 -vn 0.9145 -0.0664 -0.3990 -vn 0.9562 0.0047 -0.2926 -vn 0.7796 -0.0555 -0.6239 -vn 0.8482 0.0053 -0.5296 -vn 0.5867 -0.0282 -0.8093 -vn 0.6931 0.0099 -0.7208 -vn 0.1850 0.0103 -0.9827 -vn -0.0200 0.0112 0.9997 -vn -0.1728 0.0210 0.9847 -vn -0.0586 0.0183 0.9981 -vn 0.5776 0.0107 0.8162 -vn 0.4556 0.0128 0.8901 -vn 0.4777 -0.0112 0.8784 -vn 0.9600 0.0143 -0.2795 -vn -0.1023 0.0149 -0.9946 -vn 0.0463 0.0090 -0.9989 -vn -0.1683 0.0091 0.9857 -vn -0.3103 0.0242 0.9503 -vn 0.8015 0.0111 0.5979 -vn 0.9133 0.0153 0.4070 -vn 0.3726 -0.0079 -0.9279 -vn 0.4899 0.0096 -0.8717 -vn 0.2970 0.0141 -0.9548 -vn -0.9734 0.0060 0.2291 -vn -0.9076 0.0043 0.4198 -vn -0.7851 0.0049 0.6194 -vn -0.3218 0.0060 0.9468 -vn -0.4818 0.0091 0.8762 -vn -0.2338 0.0089 -0.9722 -vn -0.1015 0.0074 -0.9948 -vn -0.6889 0.0057 -0.7248 -vn -0.5680 0.0078 -0.8230 -vn -0.6769 0.0323 -0.7354 -vn 0.3592 0.0096 -0.9332 -vn -0.0431 0.0078 0.9990 -vn 0.9556 0.0109 0.2944 -vn 0.9054 0.0151 0.4243 -vn 0.9995 0.0135 -0.0277 -vn 0.9951 0.0166 0.0973 -vn 0.9737 0.0150 0.2272 -vn 0.9870 0.0147 -0.1599 -vn 0.8500 0.0106 -0.5267 -vn 0.9117 0.0141 -0.4106 -vn 0.6884 0.0116 -0.7252 -vn 0.7764 0.0132 -0.6301 -vn 0.5845 0.0128 -0.8113 -vn 0.1830 0.0283 -0.9827 -vn 0.2219 0.0091 0.9750 -vn 0.0988 0.0091 0.9951 -vn 0.2345 0.0122 0.9720 -vn 0.5714 0.0108 0.8206 -vn 0.4716 0.0121 0.8817 -vn -0.4901 0.0033 0.8717 -vn -0.6502 0.0065 0.7598 -vn 0.0951 0.0206 0.9953 -vn 0.3719 0.0464 -0.9271 -vn -0.2346 0.0054 -0.9721 -vn -0.0883 0.0157 -0.9960 -vn -0.7153 -0.0275 -0.6983 -vn -0.5663 0.0033 -0.8242 -vn -0.7648 0.0051 -0.6443 -vn -0.8725 -0.0383 -0.4870 -vn -0.8415 0.0050 -0.5403 -vn -0.9096 0.0044 -0.4154 -vn -0.9552 -0.0425 0.2929 -vn -0.8477 -0.0362 0.5292 -vn -0.6900 -0.0201 0.7235 -vn -0.2018 -0.0201 0.9792 -vn -0.5388 -0.0146 -0.8423 -vn -0.4681 0.0040 -0.8837 -vn -0.4452 -0.0546 0.8938 -vn -0.0307 0.0227 0.9993 -vn 0.2394 0.0385 0.9702 -vn 0.4095 0.0497 0.9109 -vn 0.3511 0.0103 0.9363 -vn 0.6762 0.0123 0.7366 -vn 0.6173 0.0636 0.7842 -vn 0.8419 0.0133 0.5395 -vn 0.7980 0.0765 0.5978 -vn 0.7644 0.0128 0.6446 -vn 0.9266 0.0857 0.3661 -vn 0.5880 0.0621 -0.8065 -vn 0.0265 0.0198 -0.9995 -vn 0.1010 0.0628 -0.9929 -vn -0.4625 -0.0763 -0.8834 -vn -0.3434 -0.0366 -0.9385 -vn -0.6160 -0.1011 -0.7812 -vn -0.7855 -0.1362 -0.6037 -vn -0.9141 -0.1639 -0.3708 -vn -0.9705 -0.0439 -0.2369 -vn -0.9782 -0.1778 -0.1077 -vn -0.9985 -0.0454 0.0312 -vn -0.9710 -0.1765 0.1614 -vn -0.8961 -0.1609 0.4137 -vn -0.7620 -0.1322 0.6339 -vn -0.5824 -0.1068 0.8058 -vn 0.1642 0.0697 0.9840 -vn -0.3823 -0.1318 -0.9146 -vn -0.3350 -0.1048 0.9364 -vn -0.2903 -0.0339 0.9563 -vn -0.0876 -0.0137 0.9961 -vn 0.1465 0.1134 -0.9827 -vn 0.2901 0.2258 -0.9300 -vn 0.3204 0.1702 -0.9319 -vn -0.1013 -0.0198 -0.9947 -vn 0.0217 0.0540 -0.9983 -vn 0.1735 0.1503 0.9733 -vn 0.0833 0.0784 0.9934 -vn 0.2019 0.1196 0.9721 -vn 0.1531 0.1816 -0.9714 -vn -0.2313 -0.1249 -0.9648 -vn -0.1411 -0.0894 -0.9860 -vn -0.0412 -0.0014 0.9991 -vn -0.1948 -0.1119 0.9745 -vn 0.3345 0.2476 0.9093 -vn 0.1966 0.2110 0.9575 -vn 0.5081 0.3610 0.7820 -vn 0.3649 0.3683 0.8551 -vn 0.6586 0.4595 0.5959 -vn 0.5149 0.5021 0.6948 -vn 0.7657 0.5297 0.3650 -vn 0.6308 0.6058 0.4849 -vn 0.8187 0.5645 0.1055 -vn 0.7030 0.6704 0.2375 -vn 0.8124 0.5607 -0.1598 -vn 0.7240 0.6892 -0.0279 -vn 0.7497 0.5200 -0.4093 -vn 0.6928 0.6611 -0.2879 -vn 0.6372 0.4466 -0.6281 -vn 0.6141 0.5908 -0.5233 -vn 0.4802 0.3439 -0.8069 -vn 0.4934 0.4832 -0.7232 -vn 0.3352 0.3424 -0.8777 -vn -0.2237 -0.2095 -0.9519 -vn -0.3123 -0.2457 -0.9177 -vn -0.3521 -0.3689 -0.8602 -vn -0.4748 -0.3923 -0.7878 -vn -0.4838 -0.5248 -0.7004 -vn -0.6115 -0.5141 -0.6014 -vn -0.5859 -0.6457 -0.4896 -vn -0.7090 -0.6010 -0.3689 -vn -0.6498 -0.7212 -0.2401 -vn -0.7575 -0.6441 -0.1067 -vn -0.6683 -0.7434 0.0283 -vn -0.7521 -0.6389 0.1616 -vn -0.6403 -0.7109 0.2908 -vn -0.6952 -0.5881 0.4133 -vn -0.5708 -0.6289 0.5279 -vn -0.5931 -0.4971 0.6333 -vn -0.4648 -0.5035 0.7283 -vn -0.4509 -0.3705 0.8121 -vn -0.3268 -0.3395 0.8820 -vn -0.2936 -0.2383 0.9257 -vn -0.1747 -0.1692 0.9700 -vn 0.0105 0.0598 -0.9982 -vn 0.0977 0.2185 -0.9709 -vn 0.1309 0.1975 -0.9715 -vn -0.1723 -0.2371 -0.9561 -vn -0.0758 -0.0619 -0.9952 -vn -0.0086 0.0643 -0.9979 -vn 0.0191 0.2367 -0.9714 -vn 0.0653 0.2271 -0.9717 -vn -0.0811 -0.2818 -0.9560 -vn -0.0537 -0.0789 -0.9954 -vn -0.1477 -0.2697 -0.9516 -vn -0.2350 -0.9590 0.1584 -vn -0.4100 -0.9119 0.0201 -vn -0.3872 -0.8733 0.2956 -vn 0.3382 0.8941 -0.2935 -vn 0.1884 0.9690 -0.1596 -vn 0.3581 0.9333 -0.0260 -vn -0.0257 -0.1550 -0.9876 -vn -0.0367 -0.3926 -0.9190 -vn -0.1312 -0.4924 -0.8604 -vn -0.0464 -0.6154 -0.7869 -vn -0.1751 -0.6919 -0.7004 -vn -0.0543 -0.7979 -0.6003 -vn -0.2092 -0.8465 -0.4896 -vn -0.0627 -0.9286 -0.3657 -vn -0.2297 -0.9483 -0.2192 -vn -0.0641 -0.9939 -0.0895 -vn -0.0607 -0.9523 0.2991 -vn -0.2077 -0.8310 0.5161 -vn -0.0549 -0.7818 0.6211 -vn -0.1689 -0.6618 0.7304 -vn -0.0446 -0.5789 0.8142 -vn -0.1228 -0.4521 0.8835 -vn -0.0332 -0.3572 0.9334 -vn -0.0825 -0.2385 0.9676 -vn 0.0896 0.5132 0.8536 -vn -0.0022 0.4005 0.9163 -vn 0.0432 0.2838 0.9579 -vn 0.1323 0.7089 0.6928 -vn 0.0076 0.6260 0.7797 -vn 0.1652 0.8599 0.4830 -vn 0.0153 0.8049 0.5932 -vn 0.1855 0.9585 0.2166 -vn 0.0206 0.9317 0.3625 -vn 0.0248 0.9961 0.0846 -vn 0.1638 0.8467 -0.5063 -vn 0.0229 0.9567 -0.2903 -vn 0.1249 0.6788 -0.7236 -vn 0.0148 0.7894 -0.6137 -vn 0.0807 0.4712 -0.8783 -vn 0.0058 0.5900 -0.8074 -vn -0.0068 0.3555 -0.9347 -vn -0.0120 0.0161 -0.9998 -vn -0.0221 0.1073 -0.9940 -vn 0.1013 -0.9945 0.0266 -vn -0.1197 0.9924 -0.0277 -vn 0.0384 -0.1593 -0.9865 -vn 0.0092 -0.1525 -0.9883 -vn -0.0771 0.1537 -0.9851 -vn -0.1571 0.1799 -0.9711 -vn -0.1665 0.3309 -0.9288 -vn 0.0975 -0.1317 -0.9865 -vn -0.0420 0.0539 -0.9977 -vn 0.0613 -0.1504 -0.9867 -vn 0.5845 -0.7592 -0.2862 -vn 0.4040 -0.8453 -0.3497 -vn 0.4357 -0.8996 0.0293 -vn 0.5963 -0.7864 0.1611 -vn 0.3995 -0.8250 0.3996 -vn 0.5153 -0.6837 0.5168 -vn 0.3340 -0.6941 0.6377 -vn 0.4081 -0.5431 0.7339 -vn 0.2501 -0.5212 0.8159 -vn 0.2766 -0.3740 0.8852 -vn 0.1538 -0.3220 0.9342 -vn 0.1371 -0.1852 0.9731 -vn 0.0827 -0.2002 0.9763 -vn 0.0009 -0.0160 0.9999 -vn -0.4396 0.8977 -0.0291 -vn -0.6006 0.7514 0.2732 -vn -0.4156 0.8432 0.3411 -vn -0.4049 0.8261 -0.3919 -vn -0.6179 0.7720 -0.1492 -vn -0.3438 0.6969 -0.6294 -vn -0.5365 0.6736 -0.5084 -vn -0.2615 0.5285 -0.8077 -vn -0.4317 0.5373 -0.7245 -vn -0.3023 0.3720 -0.8776 -vn -0.0019 -0.0107 -0.9999 -vn 0.1998 -0.1788 -0.9634 -vn 0.2391 -0.3121 -0.9194 -vn 0.3851 -0.3339 -0.8603 -vn 0.3764 -0.4891 -0.7869 -vn 0.5524 -0.4745 -0.6853 -vn 0.4925 -0.6377 -0.5922 -vn 0.7081 -0.6028 -0.3677 -vn -0.1089 0.1341 0.9850 -vn -0.0265 0.0101 0.9996 -vn -0.2538 0.3198 0.9129 -vn -0.2259 0.1727 0.9587 -vn -0.3890 0.4932 0.7781 -vn -0.4097 0.3249 0.8524 -vn -0.5026 0.6381 0.5833 -vn -0.5764 0.4611 0.6746 -vn -0.7244 0.5917 0.3537 -vn 0.2911 -0.0848 -0.9529 -vn 0.0839 -0.0388 -0.9957 -vn 0.2547 -0.1450 -0.9561 -vn 0.0285 -0.0215 0.9994 -vn 0.2364 -0.0712 0.9690 -vn 0.2111 -0.1193 0.9701 -vn 0.9831 -0.1254 0.1331 -vn 0.9572 -0.2894 0.0046 -vn 0.9140 -0.2746 0.2986 -vn -0.9173 0.2686 -0.2938 -vn -0.9805 0.1124 -0.1609 -vn -0.9597 0.2810 -0.0070 -vn -0.0540 -0.0061 -0.9985 -vn -0.2437 -0.0054 -0.9698 -vn -0.2419 0.0256 -0.9700 -vn -0.2878 -0.0068 0.9577 -vn -0.1259 -0.0011 0.9920 -vn -0.2977 0.0340 0.9541 -vn 0.1120 -0.0168 -0.9936 -vn -0.0538 -0.0103 -0.9985 -vn 0.9574 -0.0375 0.2864 -vn 0.9889 -0.0212 0.1468 -vn 0.9981 -0.0282 -0.0547 -vn 0.8462 -0.0273 0.5321 -vn 0.9099 -0.0196 0.4145 -vn 0.6796 -0.0270 0.7331 -vn 0.7730 -0.0184 0.6341 -vn 0.2967 -0.0403 0.9541 -vn 0.4341 -0.0153 0.9007 -vn 0.4960 -0.0196 0.8681 -vn 0.0261 -0.0106 0.9996 -vn 0.1741 -0.0266 0.9844 -vn 0.0353 -0.0095 0.9993 -vn -0.6520 -0.0122 0.7581 -vn -0.5179 0.0024 0.8554 -vn -0.7351 0.0091 0.6778 -vn -0.8089 -0.0096 0.5879 -vn -0.9404 0.0145 0.3398 -vn 0.5013 -0.0598 -0.8632 -vn 0.4119 -0.0179 -0.9110 -vn 0.3031 -0.0490 -0.9517 -vn 0.7139 -0.0719 -0.6965 -vn 0.6268 -0.0188 -0.7790 -vn 0.8197 -0.0265 -0.5721 -vn 0.5261 -0.0205 0.8501 -vn -0.1605 -0.0074 -0.9870 -vn -0.3027 -0.0043 -0.9531 -vn -0.3160 -0.0136 -0.9487 -vn 0.6612 -0.0190 0.7500 -vn 0.7707 -0.0267 0.6366 -vn -0.7257 0.0039 0.6880 -vn -0.8756 0.0063 0.4831 -vn -0.9544 -0.0069 0.2985 -vn -0.0410 -0.0115 -0.9991 -vn 0.2322 -0.0152 -0.9726 -vn 0.7987 -0.0259 -0.6011 -vn 0.6430 -0.0265 -0.7654 -vn 0.1857 -0.0140 0.9825 -vn 0.3138 -0.0158 0.9494 -vn -0.1286 -0.0117 0.9916 -vn -0.0997 -0.0124 0.9949 -vn 0.1306 -0.0179 -0.9913 -vn 0.4203 -0.0205 0.9071 -vn 0.0304 -0.0119 0.9995 -vn -0.3996 -0.0052 0.9167 -vn -0.2930 -0.0078 0.9561 -vn -0.4720 -0.0058 0.8816 -vn -0.6255 0.0011 0.7802 -vn -0.1665 -0.0159 -0.9859 -vn 0.5379 -0.0296 0.8425 -vn -0.8069 0.0063 0.5906 -vn -0.9203 -0.0028 0.3913 -vn -0.9719 0.0083 0.2350 -vn -0.9945 -0.0026 0.1046 -vn -0.9995 0.0094 -0.0313 -vn -0.9873 -0.0225 -0.1570 -vn -0.9560 0.0081 -0.2933 -vn -0.9053 -0.0347 -0.4233 -vn -0.8479 0.0062 -0.5301 -vn -0.7756 0.0033 -0.6312 -vn -0.6935 0.0038 -0.7205 -vn -0.5930 -0.0042 -0.8052 -vn -0.5452 0.0002 -0.8383 -vn 0.4116 -0.0233 -0.9111 -vn 0.9312 -0.0270 -0.3636 -vn 0.8639 -0.0256 -0.5031 -vn 0.9943 -0.0286 -0.1027 -vn 0.9702 -0.0126 -0.2420 -vn 0.9859 -0.0291 0.1647 -vn 0.9996 0.0121 0.0247 -vn 0.9090 -0.0276 0.4159 -vn 0.9527 0.0161 0.3035 -vn 0.8466 0.0239 0.5317 -vn 0.2774 -0.0592 -0.9589 -vn 0.1495 -0.0220 0.9885 -vn 0.2994 -0.0253 0.9538 -vn 0.0240 -0.0704 -0.9972 -vn 0.4759 0.0389 0.8786 -vn 0.2475 0.0329 0.9683 -vn 0.0617 0.0433 0.9972 -vn -0.2996 0.0346 0.9534 -vn -0.5085 0.0203 0.8608 -vn -0.7227 0.0681 0.6878 -vn -0.7680 -0.0620 -0.6375 -vn -0.3643 -0.0679 -0.9288 -vn -0.4311 -0.0073 -0.9023 -vn -0.3008 -0.0061 -0.9537 -vn -0.0919 -0.2387 -0.9667 -vn -0.1739 -0.0725 -0.9821 -vn 0.1570 -0.2119 -0.9646 -vn 0.5311 -0.0495 -0.8459 -vn 0.4147 -0.1685 -0.8942 -vn 0.9818 0.1270 0.1411 -vn -0.0365 0.2167 0.9756 -vn -0.1067 0.0456 0.9933 -vn -0.9756 -0.1493 -0.1607 -vn -0.8504 -0.2139 -0.4806 -vn -0.6713 -0.2399 -0.7013 -vn -0.5819 -0.0648 -0.8107 -vn -0.4734 -0.2557 -0.8429 -vn -0.1949 -0.4486 -0.8722 -vn -0.2911 -0.2522 -0.9229 -vn 0.0791 -0.4073 -0.9099 -vn 0.4195 -0.3005 -0.8566 -vn 0.6829 -0.0977 -0.7239 -vn 0.6651 0.2360 0.7085 -vn 0.7333 0.4364 0.5214 -vn 0.8576 0.2023 0.4729 -vn -0.4315 0.3002 0.8507 -vn -0.2859 0.1894 0.9393 -vn -0.5268 0.1605 0.8347 -vn -0.9510 -0.2823 -0.1257 -vn -0.9713 -0.0837 0.2225 -vn -0.7386 -0.4179 -0.5290 -vn -0.0224 -0.5717 -0.8202 -vn 0.2340 -0.4979 -0.8351 -vn 0.7209 -0.1613 -0.6740 -vn 0.5186 -0.3571 -0.7769 -vn 0.9699 0.0910 -0.2257 -vn 0.8925 0.0059 -0.4510 -vn 0.9485 0.2998 0.1019 -vn 0.9561 0.2164 -0.1974 -vn 0.8571 0.4699 0.2109 -vn 0.4410 0.4820 0.7571 -vn 0.5109 0.5996 0.6160 -vn 0.1824 0.4640 0.8669 -vn 0.2954 0.5952 0.7473 -vn -0.9539 -0.1936 0.2292 -vn -0.8901 -0.0018 0.4557 -vn -0.8331 -0.4873 -0.2616 -vn -0.4927 -0.6128 -0.6178 -vn -0.4472 -0.4613 -0.7663 -vn -0.2669 -0.6156 -0.7415 -vn 0.8080 -0.0870 -0.5827 -vn 0.5671 0.6495 0.5065 -vn 0.7293 0.5629 0.3889 -vn 0.3676 0.6672 0.6478 -vn -0.8036 0.0707 0.5909 -vn -0.7307 0.1559 0.6646 -vn -0.2062 -0.6197 -0.7573 -vn 0.0981 -0.5526 -0.8277 -vn 0.3495 -0.4495 -0.8221 -vn 0.6708 -0.1979 -0.7147 -vn 0.5440 -0.3518 -0.7617 -vn 0.9490 0.0938 -0.3011 -vn 0.8734 0.4644 0.1466 -vn -0.2148 0.5087 0.8337 -vn -0.1113 0.5455 0.8307 -vn -0.3475 0.4484 0.8235 -vn -0.4603 0.3892 0.7979 -vn -0.5689 0.3322 0.7523 -vn -0.6930 0.2271 0.6842 -vn 0.9365 0.3503 0.0152 -vn 0.9452 0.3241 0.0390 -vn 0.6740 0.5902 0.4442 -vn 0.0934 0.6032 0.7921 -vn 0.3940 0.6353 0.6642 -vn -0.8161 0.0236 0.5775 -vn -0.0529 -0.4736 -0.8791 -vn 0.2003 -0.4090 -0.8903 -vn 0.4372 -0.2991 -0.8482 -vn 0.7995 0.4397 0.4092 -vn 0.5475 0.4990 0.6717 -vn 0.3087 0.4964 0.8114 -vn -0.5300 0.2502 0.8103 -vn -0.7944 -0.4450 -0.4135 -vn -0.9371 -0.3441 -0.0587 -vn -0.8286 -0.5089 -0.2334 -vn -0.6799 -0.5843 -0.4431 -vn 0.8127 -0.0138 -0.5825 -vn 0.9729 0.1998 0.1165 -vn 0.9600 0.2008 -0.1952 -vn 0.6677 0.2900 0.6857 -vn 0.4105 0.2968 0.8622 -vn -0.9911 -0.1328 -0.0100 -vn -0.9531 -0.2878 0.0934 -vn -0.8988 -0.2682 -0.3467 -vn -0.6835 -0.2927 -0.6687 -vn -0.5413 -0.5100 -0.6684 -vn -0.4004 -0.2892 -0.8695 -vn -0.3022 -0.5095 -0.8057 -vn 0.6321 -0.0675 -0.7720 -vn 0.3828 -0.0251 -0.9235 -vn 0.4106 -0.1716 -0.8955 -vn -0.2655 0.0388 0.9633 -vn -0.1615 0.2280 0.9602 -vn -0.5029 0.1457 0.8520 -vn -0.7285 -0.0058 0.6851 -vn -0.9621 -0.0384 0.2700 -vn -0.9411 -0.1048 0.3215 -vn -0.9122 -0.0714 -0.4034 -vn 0.1440 0.0039 -0.9896 -vn 0.1448 -0.0527 -0.9881 -vn 0.9428 0.0366 -0.3312 -vn 0.8316 -0.0106 -0.5553 -vn 0.9608 0.0576 0.2710 -vn 0.9997 -0.0103 0.0239 -vn 0.9980 0.0598 -0.0204 -vn -0.5019 -0.0185 0.8647 -vn -0.5008 0.0138 0.8654 -vn -0.6989 -0.0195 0.7150 -vn -0.8582 -0.0254 0.5127 -vn -0.9995 -0.0083 -0.0320 -vn -0.5088 -0.0016 -0.8609 -vn -0.7121 -0.0725 -0.6983 -vn -0.5064 -0.0910 -0.8575 -vn 0.8697 -0.0186 0.4932 -vn 0.9604 -0.0290 0.2770 -vn -0.2588 -0.0222 0.9657 -vn -0.0577 0.0474 0.9972 -vn -0.9671 0.0030 0.2545 -vn -0.6949 0.0112 -0.7190 -vn -0.8562 -0.0170 -0.5164 -vn 0.6992 -0.0094 -0.7149 -vn -0.0847 -0.0235 0.9961 -vn -0.8607 0.0189 -0.5088 -vn -0.9673 -0.0050 -0.2536 -vn 0.6043 -0.0327 0.7961 -vn 0.7018 -0.0201 0.7121 -vn 0.5035 -0.0214 0.8637 -vn 0.0907 -0.0248 0.9956 -vn 0.0580 -0.0262 0.9980 -vn -0.8686 0.0012 0.4956 -vn 0.7928 -0.0280 0.6088 -vn -0.5012 -0.0098 0.8653 -vn -0.6931 -0.0019 0.7209 -vn -0.7941 0.0155 -0.6077 -vn -0.6125 0.0135 -0.7904 -vn -0.5035 0.0115 -0.8639 -vn 0.9911 0.0322 0.1288 -vn -0.2640 -0.0108 0.9645 -vn -0.9990 -0.0411 -0.0174 -vn -0.9432 0.0139 -0.3320 -vn -0.0806 0.0050 -0.9967 -vn -0.2001 0.0051 -0.9798 -vn -0.1832 0.0128 -0.9830 -vn 0.5974 0.0331 -0.8012 -vn 0.4960 -0.0040 -0.8683 -vn 0.9900 0.0360 -0.1365 -vn 0.9203 0.0451 -0.3885 -vn 0.9631 -0.0136 -0.2689 -vn 0.3064 -0.0269 0.9515 -vn 0.4863 -0.0222 0.8735 -vn 0.4529 -0.0328 0.8910 -vn -0.1061 -0.0124 0.9943 -vn -0.9658 -0.0538 0.2536 -vn -0.3904 0.0049 -0.9206 -vn -0.4434 0.0131 -0.8962 -vn -0.3285 0.0140 -0.9444 -vn 0.3670 0.0182 -0.9301 -vn 0.2781 0.0000 -0.9606 -vn -0.3313 -0.0207 0.9433 -vn -0.8532 -0.0482 0.5193 -vn -0.8330 -0.0386 -0.5520 -vn -0.1336 -0.0043 -0.9910 -vn 0.1143 0.0082 -0.9934 -vn 0.0830 0.0015 -0.9966 -vn 0.8951 0.0416 0.4439 -vn 0.9034 -0.0313 0.4277 -vn 0.6050 -0.0209 0.7959 -vn 0.7062 0.0262 0.7075 -vn 0.4999 0.0212 0.8658 -vn 0.3280 0.0030 0.9447 -vn -0.2037 -0.0523 0.9776 -vn 0.0858 -0.0101 0.9963 -vn -0.9551 -0.0557 -0.2911 -vn -0.4788 -0.0291 -0.8774 -vn -0.6465 -0.0247 -0.7626 -vn -0.2614 -0.0595 -0.9634 -vn -0.3265 -0.0188 -0.9450 -vn -0.0193 -0.0039 -0.9998 -vn 0.2490 0.0602 -0.9666 -vn 0.5807 0.1425 -0.8016 -vn 0.7890 0.0328 -0.6135 -vn 0.8941 0.2124 -0.3944 -vn 0.9657 0.2279 0.1248 -vn 0.1071 0.0243 0.9940 -vn 0.2079 0.0067 0.9781 -vn -0.5993 -0.1447 0.7873 -vn -0.6441 -0.0397 0.7639 -vn -0.8980 -0.2241 0.3786 -vn -0.9615 -0.2476 -0.1191 -vn -0.1324 -0.0723 -0.9886 -vn 0.0988 0.0521 -0.9937 -vn 0.3330 0.1806 -0.9255 -vn 0.6085 0.3367 -0.7186 -vn 0.6714 0.1624 0.7231 -vn 0.6783 0.3764 0.6310 -vn 0.8445 0.2045 0.4949 -vn 0.3040 0.0768 0.9496 -vn 0.3769 0.2074 0.9027 -vn 0.4857 0.1165 0.8663 -vn -0.3498 -0.1932 0.9167 -vn -0.6279 -0.3407 0.6998 -vn -0.6993 -0.3754 -0.6083 -vn -0.8498 -0.2172 -0.4802 -vn -0.6836 -0.1705 -0.7097 -vn -0.4038 -0.2203 -0.8879 -vn -0.4874 -0.1235 -0.8644 -vn -0.1929 -0.1723 -0.9660 -vn 0.3641 0.3231 -0.8735 -vn 0.8257 0.4603 -0.3260 -vn 0.6391 0.5687 -0.5177 -vn 0.8635 0.4803 0.1540 -vn 0.7443 0.6678 0.0067 -vn 0.6423 0.5793 0.5018 -vn 0.4522 0.4085 0.7929 -vn -0.3732 -0.3512 0.8587 -vn -0.6276 -0.5892 0.5090 -vn -0.8367 -0.4474 0.3158 -vn -0.7317 -0.6785 0.0647 -vn -0.8735 -0.4638 -0.1477 -vn -0.6820 -0.6255 -0.3790 -vn -0.5825 -0.8128 -0.0020 -vn -0.5057 -0.7119 -0.4874 -vn -0.5321 -0.4817 -0.6963 -vn -0.3623 -0.5089 -0.7808 -vn -0.3753 -0.3370 -0.8634 -vn -0.2169 -0.3089 -0.9260 -vn 0.0911 0.1560 0.9835 -vn 0.1744 0.3687 0.9130 -vn 0.2323 0.3129 0.9209 -vn -0.0584 -0.1263 0.9903 -vn 0.0216 0.0333 0.9992 -vn -0.0938 -0.1264 0.9875 -vn 0.1605 0.3237 -0.9325 -vn 0.0976 0.3245 -0.9408 -vn 0.0381 0.1038 -0.9939 -vn 0.3737 0.7834 -0.4965 -vn 0.2326 0.6562 -0.7178 -vn 0.2695 0.5499 -0.7906 -vn 0.4288 0.9034 0.0045 -vn 0.3193 0.9092 -0.2673 -vn 0.3742 0.7822 0.4982 -vn 0.3191 0.9079 0.2718 -vn 0.2694 0.5476 0.7922 -vn 0.2308 0.6640 0.7112 -vn -0.0938 -0.3125 0.9453 -vn -0.1640 -0.3429 0.9249 -vn -0.2178 -0.6692 0.7104 -vn -0.2818 -0.5564 0.7817 -vn -0.3986 -0.7758 0.4892 -vn -0.2969 -0.9183 0.2618 -vn -0.4540 -0.8910 -0.0044 -vn -0.2967 -0.9171 -0.2661 -vn -0.3952 -0.7785 -0.4877 -vn -0.2203 -0.6795 -0.6998 -vn -0.2801 -0.5558 -0.7827 -vn -0.0026 -0.0177 -0.9998 -vn -0.0540 -0.1912 -0.9801 -vn -0.0579 -0.1170 -0.9914 -vn 0.0684 0.2138 0.9745 -vn 0.0770 0.4128 0.9076 -vn 0.1419 0.3942 0.9080 -vn -0.0137 -0.2608 -0.9653 -vn -0.0831 -0.3858 -0.9188 -vn 0.1042 0.5457 -0.8315 -vn 0.0099 0.2631 -0.9647 -vn 0.1401 0.6928 -0.7074 -vn 0.0751 0.6061 -0.7918 -vn 0.1920 0.9444 -0.2670 -vn 0.1088 0.9128 -0.3936 -vn 0.1919 0.9433 0.2709 -vn 0.1221 0.9831 0.1366 -vn 0.1402 0.6929 0.7073 -vn 0.0981 0.7909 0.6041 -vn 0.0710 0.5173 0.8528 -vn 0.0123 0.2461 0.9692 -vn -0.0147 -0.2691 0.9630 -vn -0.0073 -0.0488 0.9988 -vn -0.0844 -0.3841 0.9194 -vn -0.0639 -0.6103 0.7896 -vn -0.1592 -0.6958 0.7004 -vn -0.0926 -0.9158 0.3907 -vn -0.2124 -0.9412 0.2626 -vn -0.1008 -0.9858 -0.1345 -vn -0.2125 -0.9403 -0.2658 -vn -0.0819 -0.7982 -0.5968 -vn -0.1591 -0.6975 -0.6987 -vn -0.0555 -0.5095 -0.8587 -vn 0.0007 -0.0144 -0.9999 -vn -0.0114 0.5129 0.8584 -vn 0.0145 -0.4918 0.8706 -vn 0.0022 -0.0284 0.9996 -vn -0.0371 0.2447 0.9689 -vn 0.0430 -0.2549 0.9660 -vn -0.1562 0.9122 -0.3789 -vn -0.2279 0.7557 -0.6140 -vn -0.1164 0.6968 -0.7078 -vn -0.1701 0.9760 0.1362 -vn -0.2842 0.9489 -0.1375 -vn -0.1345 0.7834 0.6068 -vn -0.2626 0.8801 0.3955 -vn -0.0777 0.4953 0.8652 -vn -0.1737 0.5774 0.7978 -vn 0.2461 -0.7585 0.6034 -vn 0.1107 -0.7050 0.7005 -vn 0.1375 -0.9191 0.3693 -vn 0.3084 -0.9418 0.1341 -vn 0.1445 -0.9806 -0.1326 -vn 0.2877 -0.8761 -0.3868 -vn 0.1176 -0.7953 -0.5947 -vn 0.1933 -0.5876 -0.7858 -vn 0.0764 -0.5097 -0.8570 -vn 0.0085 -0.0220 0.9997 -vn -0.1163 0.2398 0.9638 -vn -0.0765 0.2568 0.9634 -vn 0.1125 -0.2270 0.9674 -vn 0.0754 -0.2452 0.9665 -vn -0.2800 0.4157 -0.8654 -vn -0.2539 0.2715 -0.9283 -vn -0.1933 0.2890 -0.9376 -vn -0.4424 0.6535 -0.6142 -vn -0.3999 0.4389 -0.8046 -vn -0.5552 0.8199 -0.1392 -vn -0.6509 0.7155 -0.2536 -vn -0.5156 0.7606 0.3945 -vn -0.6453 0.7162 0.2657 -vn -0.3414 0.5018 0.7948 -vn -0.4742 0.5273 0.7051 -vn -0.1691 0.2559 0.9518 -vn -0.2616 0.2821 0.9230 -vn 0.2653 -0.2781 0.9232 -vn 0.1382 -0.2141 0.9670 -vn 0.2798 -0.4291 0.8588 -vn 0.4116 -0.4415 0.7973 -vn 0.4363 -0.6687 0.6021 -vn 0.6610 -0.7089 0.2459 -vn 0.5413 -0.8298 0.1357 -vn 0.6611 -0.7043 -0.2586 -vn 0.5037 -0.7733 -0.3851 -vn 0.4943 -0.5261 -0.6920 -vn 0.3375 -0.5221 -0.7833 -vn 0.2729 -0.2861 -0.9185 -vn 0.1426 -0.2277 -0.9632 -vn 0.1484 -0.0206 -0.9887 -vn 0.3654 -0.0147 -0.9307 -vn 0.4000 -0.0939 -0.9117 -vn 0.1475 -0.0056 0.9890 -vn -0.0384 0.0067 0.9992 -vn 0.1540 -0.0331 0.9875 -vn -0.0678 0.0043 -0.9977 -vn 0.1212 -0.0005 -0.9926 -vn -0.8340 0.0171 0.5515 -vn -0.9041 0.0490 0.4244 -vn -0.7017 0.0290 0.7119 -vn 0.0548 0.0010 0.9985 -vn 0.3924 -0.0043 0.9198 -vn 0.3755 -0.0176 0.9266 -vn 0.7933 -0.0438 0.6072 -vn 0.5841 -0.0064 0.8116 -vn 0.6037 -0.0274 0.7967 -vn 0.3776 -0.0065 -0.9259 -vn -0.8674 0.0195 -0.4972 -vn -0.7872 0.0123 -0.6165 -vn -0.9180 0.0531 -0.3930 -vn -0.9641 0.0210 -0.2648 -vn -0.9920 0.0507 -0.1160 -vn -0.9998 0.0214 0.0011 -vn -0.9887 0.0502 0.1414 -vn -0.9592 0.0212 0.2819 -vn 0.2121 -0.0032 0.9772 -vn -0.8743 0.0199 0.4850 -vn -0.7059 0.0155 0.7081 -vn -0.6046 0.0130 0.7964 -vn -0.4984 0.0115 0.8669 -vn -0.4129 0.0212 0.9105 -vn -0.2223 0.0042 0.9750 -vn -0.1809 0.0296 0.9831 -vn -0.1901 0.0048 -0.9817 -vn 0.1333 -0.0238 -0.9908 -vn -0.3319 0.0074 -0.9433 -vn -0.3184 0.0078 -0.9479 -vn -0.8035 0.0132 -0.5951 -vn -0.9914 0.0194 0.1295 -vn -0.0342 0.0107 0.9994 -vn -0.9374 0.0076 -0.3482 -vn -0.9675 0.0220 -0.2520 -vn -0.9921 0.0072 -0.1251 -vn -0.9188 0.0067 0.3946 -vn -0.7908 0.0062 0.6121 -vn -0.6026 0.0049 0.7980 -vn -0.4023 0.0043 0.9155 -vn 0.7740 -0.0127 0.6330 -vn 0.6027 -0.0030 0.7979 -vn 0.6065 -0.0128 0.7949 -vn 0.3905 -0.0058 -0.9206 -vn 0.6996 0.0005 -0.7145 -vn 0.6083 -0.0127 -0.7936 -vn 0.1585 -0.0001 -0.9874 -vn 0.3930 0.0144 -0.9194 -vn -0.1499 -0.0206 -0.9885 -vn -0.3694 0.0049 -0.9293 -vn -0.4527 0.0099 -0.8916 -vn 0.1521 0.0347 0.9878 -vn 0.1377 0.0012 0.9905 -vn 0.4734 -0.0072 0.8808 -vn 0.3796 0.0053 0.9252 -vn 0.3260 -0.0043 0.9454 -vn 0.9914 -0.0165 0.1295 -vn 0.9688 0.0043 0.2480 -vn 0.9204 -0.0162 0.3907 -vn 0.9208 -0.0154 -0.3898 -vn 0.9643 0.0055 -0.2649 -vn 0.9907 -0.0164 -0.1352 -vn -0.9639 -0.0890 -0.2511 -vn -0.7918 -0.0665 -0.6071 -vn -0.9610 -0.0777 0.2653 -vn -0.9905 0.0072 0.1373 -vn -0.7069 -0.0456 0.7059 -vn 0.7206 0.0567 -0.6910 -vn 0.3916 0.0241 -0.9198 -vn -0.0560 -0.0956 -0.9938 -vn -0.4873 -0.0800 0.8695 -vn -0.3186 -0.0169 0.9477 -vn -0.2758 -0.2170 -0.9364 -vn -0.1857 -0.1085 -0.9766 -vn -0.3677 -0.1493 -0.9179 -vn -0.1918 0.0675 0.9791 -vn -0.1023 0.0427 0.9938 -vn 0.0059 0.1282 0.9917 -vn 0.1344 0.0928 0.9866 -vn 0.3692 0.1492 0.9173 -vn 0.2852 0.2208 0.9327 -vn 0.7859 0.1833 -0.5906 -vn 0.9128 0.3170 -0.2575 -vn 0.9491 0.1949 -0.2476 -vn 0.1558 -0.0804 -0.9845 -vn 0.3720 0.0429 -0.9273 -vn -0.0750 -0.2112 -0.9746 -vn -0.0564 -0.1372 -0.9889 -vn -0.7468 -0.4708 -0.4697 -vn -0.7250 -0.3481 -0.5943 -vn -0.8982 -0.3755 -0.2283 -vn -0.8915 -0.4529 0.0086 -vn -0.9125 -0.3137 0.2626 -vn -0.5592 -0.1568 0.8141 -vn -0.7926 -0.2532 0.5547 -vn -0.7391 -0.1450 0.6577 -vn -0.0767 0.1505 0.9856 -vn 0.0954 0.2199 0.9708 -vn 0.7382 0.3541 0.5742 -vn 0.6044 0.4350 0.6674 -vn 0.5368 0.2953 0.7904 -vn -0.6437 -0.3139 0.6980 -vn -0.8203 -0.4013 0.4074 -vn 0.0767 0.2564 0.9635 -vn 0.7287 0.3007 -0.6152 -vn 0.8300 0.4814 -0.2816 -vn 0.8785 0.3924 -0.2725 -vn -0.0970 -0.2850 -0.9536 -vn 0.0467 -0.1541 -0.9870 -vn -0.2334 -0.4061 -0.8835 -vn -0.2747 -0.3635 -0.8902 -vn -0.3348 -0.4826 -0.8093 -vn -0.4755 -0.5059 -0.7197 -vn -0.6781 -0.4704 0.5647 -vn -0.4149 -0.1639 0.8950 -vn -0.4104 -0.0939 0.9071 -vn -0.2443 0.0008 0.9697 -vn -0.0360 0.1728 0.9843 -vn 0.5638 0.4994 0.6578 -vn 0.3392 0.4892 0.8035 -vn 0.2731 0.3867 0.8808 -vn 0.7997 0.5592 0.2185 -vn 0.6347 0.6486 0.4200 -vn 0.7723 0.6342 0.0369 -vn 0.7485 0.5036 -0.4314 -vn 0.5654 0.1749 -0.8061 -vn 0.5873 0.3468 -0.7313 -vn 0.4205 0.1710 -0.8911 -vn -0.1324 -0.3239 -0.9368 -vn -0.4685 -0.6208 -0.6286 -vn -0.5392 -0.4094 0.7360 -vn 0.1815 0.3528 0.9179 -vn 0.4748 0.6247 0.6199 -vn 0.5211 0.5589 0.6450 -vn 0.6913 0.5701 -0.4440 -vn 0.2129 0.0556 -0.9755 -vn 0.2391 -0.0041 -0.9710 -vn -0.3207 -0.2408 0.9161 -vn 0.4402 0.3297 -0.8352 -vn -0.1062 -0.2178 -0.9702 -vn -0.2443 -0.4107 -0.8785 -vn -0.3809 -0.6161 -0.6894 -vn -0.0052 0.0693 0.9976 -vn -0.1776 -0.0706 0.9816 -vn 0.0798 0.1884 0.9788 -vn 0.3239 0.5036 0.8009 -vn 0.0211 -0.0475 -0.9986 -vn 0.0829 0.1226 -0.9890 -vn 0.1701 0.0835 -0.9819 -vn -0.2005 -0.3182 0.9266 -vn -0.4021 -0.4966 0.7692 -vn 0.5660 0.6525 -0.5039 -vn 0.5472 0.7993 -0.2484 -vn 0.6459 0.7528 -0.1274 -vn 0.3832 0.3712 -0.8458 -vn 0.4125 0.6046 -0.6814 -vn -0.4229 -0.8289 -0.3662 -vn -0.5252 -0.7435 -0.4140 -vn -0.0498 -0.1312 0.9901 -vn -0.1073 -0.1355 0.9850 -vn 0.2246 0.4212 0.8787 -vn 0.1600 0.2667 0.9504 -vn 0.3987 0.6637 0.6329 -vn 0.3015 0.5073 0.8073 -vn 0.1494 0.3125 -0.9381 -vn 0.2458 0.3397 -0.9079 -vn -0.2806 -0.6256 -0.7279 -vn -0.2389 -0.4276 -0.8718 -vn -0.3334 -0.7587 -0.5597 -vn -0.1048 -0.3014 0.9477 -vn 0.2732 0.6154 0.7394 -vn 0.4776 0.7762 0.4116 -vn 0.3418 0.7887 0.5110 -vn 0.5265 0.8399 0.1318 -vn 0.4172 0.8971 0.1455 -vn 0.4741 0.8416 -0.2587 -vn 0.3560 0.6320 -0.6884 -vn 0.3731 0.8527 -0.3656 -vn 0.2433 0.5926 -0.7679 -vn -0.0047 0.0444 -0.9990 -vn 0.0171 0.1687 -0.9855 -vn -0.1680 -0.4460 -0.8791 -vn -0.1368 -0.2571 -0.9566 -vn -0.3051 -0.9108 -0.2781 -vn 0.1715 0.4520 0.8754 -vn -0.0376 -0.0185 -0.9991 -vn -0.1868 -0.6577 -0.7298 -vn 0.2965 0.9002 0.3188 -vn 0.2222 0.8288 0.5135 -vn 0.2084 0.7149 -0.6675 -vn 0.2287 0.9061 -0.3560 -vn 0.3284 0.8711 -0.3651 -vn -0.0654 -0.2298 -0.9710 -vn -0.0766 -0.1389 -0.9873 -vn -0.1743 -0.9428 -0.2842 -vn -0.2177 -0.8055 -0.5512 -vn 0.0308 0.0509 0.9982 -vn -0.0196 -0.2083 0.9779 -vn 0.0441 0.0405 0.9982 -vn 0.1846 0.6449 0.7417 -vn 0.1097 0.4703 0.8756 -vn 0.0203 0.2882 -0.9574 -vn 0.0860 0.3441 -0.9350 -vn -0.0559 0.1251 -0.9906 -vn -0.0949 -0.6776 -0.7292 -vn -0.1059 -0.4391 -0.8922 -vn 0.1738 0.9323 0.3172 -vn 0.1085 0.8481 0.5186 -vn -0.0560 0.3056 -0.9505 -vn -0.0328 -0.1168 -0.9926 -vn -0.0576 -0.9648 -0.2567 -vn -0.1065 -0.8252 -0.5547 -vn 0.0508 -0.0412 0.9979 -vn -0.0198 -0.3149 0.9489 -vn 0.0945 0.6594 0.7458 -vn 0.0417 0.4732 0.8800 -vn 0.0061 0.4797 -0.8774 -vn 0.0682 0.5279 -0.8466 -vn -0.0170 -0.6866 -0.7269 -vn -0.0397 -0.4807 -0.8760 -vn 0.0544 0.9471 0.3161 -vn -0.0181 0.9073 0.4201 -vn 0.0471 -0.9949 0.0896 -vn -0.0591 -0.9973 -0.0430 -vn -0.0539 -0.9701 0.2367 -vn 0.0537 -0.8196 0.5705 -vn -0.0447 -0.8815 0.4700 -vn -0.0274 -0.7257 0.6875 -vn 0.0507 -0.2925 0.9549 -vn -0.0159 -0.3532 0.9354 -vn 0.0234 0.6956 0.7180 -vn 0.0826 0.3366 0.9380 -vn 0.0500 0.9714 -0.2323 -vn -0.0611 0.9935 -0.0960 -vn 0.0563 0.9975 0.0422 -vn 0.0265 0.7198 -0.6937 -vn -0.0686 0.8180 -0.5712 -vn 0.0396 0.8832 -0.4673 -vn -0.2109 0.2076 -0.9552 -vn -0.0303 -0.6102 -0.7917 -vn 0.0282 0.2165 0.9759 -vn 0.1508 0.1253 0.9806 -vn 0.1148 0.3512 0.9292 -vn 0.3551 0.0355 0.9342 -vn 0.1882 -0.0830 0.9786 -vn 0.2615 0.2735 0.9257 -vn 0.1416 0.4804 0.8656 -vn -0.3124 -0.1499 -0.9380 -vn -0.4884 -0.0862 -0.8683 -vn -0.3835 0.0358 -0.9229 -vn -0.2064 -0.3756 -0.9035 -vn -0.3649 -0.3144 -0.8763 -vn -0.0960 -0.5990 -0.7950 -vn -0.2196 -0.5140 -0.8292 -vn 0.0794 -0.8221 -0.5638 -vn 0.0556 -0.7557 -0.6526 -vn 0.1754 -0.9365 -0.3038 -vn 0.2564 -0.8622 -0.4369 -vn -0.3139 0.8692 0.3820 -vn -0.1286 0.8163 0.5632 -vn -0.2254 0.9214 0.3167 -vn -0.6081 0.7619 -0.2230 -vn -0.3733 0.9258 -0.0601 -vn -0.4706 0.7330 -0.4911 -vn -0.6641 0.4628 -0.5872 -vn -0.4717 0.4561 -0.7546 -vn -0.6326 0.0384 -0.7735 -vn -0.5954 0.1567 -0.7880 -vn -0.4844 -0.2346 -0.8428 -vn -0.3151 -0.4326 -0.8447 -vn -0.0958 -0.6049 -0.7906 -vn 0.4442 -0.8759 -0.1886 -vn 0.3102 -0.8586 -0.4083 -vn 0.0509 0.6416 0.7653 -vn 0.2094 0.5289 0.8224 -vn 0.0324 0.7295 0.6832 -vn -0.6668 0.7260 -0.1685 -vn -0.4701 0.8720 0.1365 -vn -0.4696 -0.2215 -0.8546 -vn 0.1115 -0.7671 -0.6317 -vn 0.6507 -0.7021 0.2892 -vn 0.6672 -0.7241 0.1747 -vn 0.5785 -0.8122 0.0754 -vn 0.6608 -0.3375 0.6704 -vn 0.7191 -0.4290 0.5467 -vn 0.6745 -0.5419 0.5013 -vn -0.4768 0.8668 0.1459 -vn -0.6995 0.2910 -0.6527 -vn -0.7322 0.5191 -0.4409 -vn -0.7176 0.3079 -0.6247 -vn -0.2466 -0.3915 -0.8865 -vn 0.5316 0.1212 0.8383 -vn 0.5391 -0.1178 0.8340 -vn 0.6477 -0.1191 0.7525 -vn 0.3991 0.1680 0.9014 -vn 0.3908 0.3346 0.8575 -vn 0.2488 0.3996 0.8823 -vn 0.2230 0.5285 0.8191 -vn -0.1898 0.8416 0.5056 -vn -0.6353 0.4539 -0.6248 -vn -0.6511 0.6962 -0.3023 -vn -0.3250 -0.0101 -0.9457 -vn -0.5355 0.1027 -0.8383 -vn -0.3927 -0.1769 -0.9025 -vn -0.2214 -0.2520 -0.9421 -vn -0.1282 -0.4583 -0.8795 -vn 0.0334 -0.6653 -0.7458 -vn 0.2474 -0.8913 -0.3801 -vn 0.6002 -0.7668 0.2273 -vn 0.3947 -0.9175 0.0489 -vn 0.4708 -0.8696 -0.1488 -vn 0.6375 -0.4728 0.6083 -vn 0.4688 -0.7313 0.4954 -vn 0.3217 -0.0036 0.9468 -vn 0.2276 0.2190 0.9488 -vn 0.1173 0.4498 0.8854 -vn -0.3876 0.9213 0.0303 -vn -0.4484 0.8737 0.1885 -vn -0.5743 0.8156 -0.0711 -vn -0.4798 0.7513 -0.4532 -vn -0.0225 0.6249 0.7804 -vn 0.0239 0.4252 0.9048 -vn -0.0047 -0.5486 -0.8361 -vn -0.0213 -0.3989 -0.9167 -vn 0.0005 0.5492 0.8357 -vn -0.2612 0.8729 -0.4122 -vn -0.1291 0.9916 -0.0106 -vn -0.2528 0.9673 -0.0193 -vn -0.1274 -0.7477 -0.6517 -vn 0.0862 -0.8809 -0.4654 -vn 0.0056 -0.9697 -0.2444 -vn 0.1549 -0.9875 0.0298 -vn 0.1436 -0.9740 0.1752 -vn 0.1760 -0.8942 0.4116 -vn 0.2797 -0.3201 0.9051 -vn 0.2667 -0.5736 0.7745 -vn 0.1337 -0.3104 0.9412 -vn -0.2815 0.7027 -0.6534 -vn -0.2261 0.7915 -0.5678 -vn -0.4993 0.1504 -0.8533 -vn -0.2820 0.2806 -0.9175 -vn -0.2739 0.0414 -0.9609 -vn -0.2512 -0.2013 -0.9468 -vn -0.4808 -0.1597 -0.8622 -vn -0.0629 -0.9796 -0.1908 -vn 0.2221 -0.8019 0.5546 -vn 0.3419 -0.8241 0.4517 -vn 0.4042 -0.6261 0.6668 -vn -0.2762 0.8961 -0.3475 -vn -0.1686 0.9546 -0.2456 -vn -0.3771 0.7068 -0.5985 -vn -0.2699 0.5300 -0.8039 -vn -0.4707 0.4163 -0.7779 -vn -0.6317 0.0822 -0.7708 -vn 0.0157 -0.9991 0.0393 -vn 0.1284 0.9818 0.1400 -vn 0.2154 0.8927 0.3959 -vn -0.0461 0.9975 0.0534 -vn -0.3194 0.8587 -0.4007 -vn 0.2232 -0.9292 0.2945 -vn 0.6107 -0.3030 0.7316 -vn 0.5418 -0.5220 0.6588 -vn 0.5024 -0.6241 0.5984 -vn -0.1455 0.9624 -0.2294 -vn -0.4722 0.6696 -0.5733 -vn -0.5845 0.4095 -0.7005 -vn -0.5815 0.3972 -0.7100 -vn -0.6221 -0.2252 -0.7498 -vn -0.6569 -0.1015 -0.7471 -vn -0.5596 -0.4732 -0.6804 -vn -0.6183 -0.3756 -0.6904 -vn -0.4707 -0.6762 -0.5667 -vn -0.5102 -0.6694 -0.5401 -vn -0.3391 -0.8477 -0.4079 -vn -0.1861 -0.9577 -0.2193 -vn -0.2852 -0.9024 -0.3230 -vn -0.0424 -0.9986 -0.0323 -vn 0.6501 -0.0311 0.7592 -vn 0.6208 -0.2848 0.7304 -vn 0.5798 0.4904 0.6506 -vn 0.6226 0.2599 0.7381 -vn 0.5527 0.5092 0.6597 -vn 0.4385 0.7663 0.4695 -vn 0.4555 0.7256 0.5158 -vn 0.3124 0.8767 0.3659 -vn 0.2142 0.9483 0.2340 -vn -0.4835 0.6405 -0.5966 -vn -0.4204 -0.6884 -0.5911 -vn 0.4312 -0.7321 0.5274 -vn 0.3212 -0.7919 0.5193 -vn 0.0150 0.9998 -0.0117 -vn -0.3109 0.7914 -0.5263 -vn -0.4633 0.5244 -0.7144 -vn -0.5388 0.2113 -0.8155 -vn -0.6399 0.1621 -0.7512 -vn -0.5267 -0.3751 -0.7628 -vn -0.3989 -0.1811 -0.8989 -vn -0.5595 -0.0962 -0.8232 -vn -0.3421 -0.5930 -0.7289 -vn -0.1917 -0.8905 -0.4127 -vn 0.3300 0.5890 0.7376 -vn 0.5011 0.4877 0.7149 -vn 0.3842 0.7636 0.5189 -vn 0.1805 0.9055 0.3842 -vn -0.1708 0.8804 -0.4425 -vn -0.2987 0.6368 -0.7108 -vn -0.3625 0.3949 -0.8442 -vn -0.0178 0.0182 -0.9997 -vn -0.1928 0.1516 -0.9695 -vn -0.1989 -0.0556 -0.9784 -vn 0.0320 -0.7983 0.6015 -vn 0.0504 -0.9522 0.3014 -vn 0.1267 -0.7780 0.6154 -vn 0.1001 0.8948 0.4352 -vn -0.0192 0.7828 0.6220 -vn 0.1691 0.5777 0.7985 -vn 0.0035 0.9997 -0.0257 -vn -0.0322 0.9847 0.1710 -vn -0.0854 0.8887 -0.4504 -vn -0.0798 0.9899 -0.1170 -vn -0.1447 0.6452 -0.7502 -vn -0.0409 0.7813 -0.6228 -vn -0.1769 0.4021 -0.8984 -vn -0.0324 0.5281 -0.8485 -vn -0.0246 0.2741 -0.9614 -vn 0.0895 -0.9893 0.1148 -vn 0.0305 -0.9959 -0.0846 -vn -0.0476 -0.6056 0.7943 -vn 0.0320 -0.5206 0.8532 -vn -0.1654 0.5896 0.7906 -vn -0.0034 0.4928 0.8701 -vn -0.1492 0.9023 0.4045 -vn 0.0380 0.6095 -0.7919 -vn 0.2506 -0.0202 -0.9679 -vn 0.1275 0.0303 -0.9914 -vn 0.1559 -0.2442 -0.9571 -vn 0.2910 -0.3391 -0.8946 -vn 0.1715 -0.5903 -0.7887 -vn 0.2966 -0.6760 -0.6746 -vn 0.1515 -0.9071 -0.3926 -vn 0.2334 -0.9381 -0.2558 -vn -0.0969 -0.6225 0.7766 -vn 0.0169 -0.8505 0.5257 -vn -0.1511 0.2505 0.9563 -vn -0.2544 0.0231 0.9668 -vn -0.1262 -0.0238 0.9917 -vn -0.2949 0.3435 0.8916 -vn -0.2941 0.6733 0.6784 -vn -0.2309 0.9344 0.2712 -vn -0.0264 0.8781 -0.4778 -vn -0.1254 0.9774 -0.1703 -vn 0.2753 0.3349 -0.9011 -vn 0.1920 0.3092 -0.9314 -vn 0.4203 -0.4396 -0.7938 -vn 0.3680 -0.8117 -0.4535 -vn 0.2261 -0.9740 -0.0131 -vn 0.1277 -0.9742 0.1861 -vn 0.0079 -0.8909 0.4542 -vn -0.0958 -0.7349 0.6714 -vn -0.2400 -0.4475 0.8615 -vn -0.1909 -0.3008 0.9344 -vn -0.3235 -0.1900 0.9269 -vn -0.4213 0.4410 0.7925 -vn -0.3744 0.8042 0.4617 -vn -0.2347 0.9720 0.0122 -vn -0.1871 0.9697 -0.1572 -vn 0.1119 0.6140 -0.7813 -vn 0.1049 0.7090 -0.6974 -vn 0.4937 -0.0282 -0.8692 -vn 0.3854 -0.0609 -0.9207 -vn 0.5262 -0.5115 -0.6793 -vn 0.4236 -0.8484 -0.3175 -vn 0.0421 -0.9301 0.3650 -vn -0.2189 -0.6572 0.7212 -vn -0.3822 -0.3379 0.8601 -vn -0.3872 0.0847 0.9181 -vn -0.4993 0.0513 0.8649 -vn -0.5268 0.5064 0.6826 -vn -0.4189 0.8535 0.3101 -vn 0.0875 0.8265 -0.5561 -vn 0.3239 0.4594 -0.8271 -vn 0.5777 0.1247 -0.8067 -vn 0.6434 -0.3688 -0.6708 -vn 0.5417 -0.7547 -0.3702 -vn 0.3745 -0.9223 -0.0956 -vn 0.2777 -0.9604 -0.0215 -vn 0.1749 -0.9670 0.1852 -vn -0.2276 -0.7500 0.6211 -vn -0.4762 -0.3834 0.7913 -vn -0.6388 0.3816 0.6680 -vn -0.5350 0.7604 0.3683 -vn -0.2878 0.9565 -0.0488 -vn 0.0373 0.8914 -0.4517 -vn 0.3538 0.5892 -0.7264 -vn 0.7489 -0.0942 -0.6559 -vn 0.6996 -0.5585 -0.4458 -vn 0.4796 -0.8696 -0.1178 -vn -0.1071 -0.9039 0.4141 -vn -0.4040 -0.6784 0.6137 -vn -0.7510 0.0871 0.6546 -vn -0.6095 -0.0011 0.7928 -vn -0.7081 0.5430 0.4515 -vn -0.4921 0.8595 0.1382 -vn 0.6231 0.3452 -0.7018 -vn 0.8503 0.0751 -0.5208 -vn 0.8307 -0.4116 -0.3747 -vn 0.6130 -0.7722 -0.1673 -vn 0.0754 -0.9850 0.1554 -vn 0.0643 -0.9730 0.2217 -vn -0.2784 -0.8764 0.3930 -vn -0.6248 -0.3474 0.6992 -vn -0.6384 -0.5684 0.5190 -vn -0.8503 -0.0997 0.5168 -vn -0.8364 0.3930 0.3821 -vn -0.6133 0.7757 0.1485 -vn -0.2629 0.9596 -0.1002 -vn 0.9423 -0.2513 -0.2212 -vn 0.7681 -0.6315 -0.1060 -vn 0.4741 -0.8799 0.0319 -vn -0.9479 0.2297 0.2206 -vn -0.7831 0.6127 0.1066 -vn 0.1326 0.9366 -0.3243 -vn -0.1696 0.9777 -0.1236 -vn 0.4858 0.7321 -0.4775 -vn 0.3017 0.9198 -0.2509 -vn 0.7137 0.6272 -0.3118 -vn 0.7106 0.4580 -0.5341 -vn 0.9894 0.0382 -0.1402 -vn 0.9316 0.2076 -0.2984 -vn 0.6526 -0.7577 0.0005 -vn -0.7300 -0.6077 0.3127 -vn -0.5000 -0.8505 0.1636 -vn -0.3731 -0.8903 0.2611 -vn -0.9319 -0.2100 0.2958 -vn -0.9023 -0.3992 0.1626 -vn -0.8840 0.4641 0.0554 -vn 0.1547 0.9880 -0.0045 -vn 0.2880 0.9444 0.1585 -vn -0.0990 0.9834 0.1518 -vn 0.7716 0.6358 0.0172 -vn 0.8614 0.4684 0.1962 -vn 0.6139 0.7702 0.1730 -vn 0.2892 -0.9042 0.3144 -vn 0.0984 -0.9845 0.1451 -vn -0.0990 -0.9429 0.3181 -vn -0.4521 -0.8367 0.3090 -vn -0.6105 -0.7797 0.1393 -vn -0.7538 -0.5896 0.2901 -vn -0.9273 -0.2655 0.2638 -vn -0.9878 -0.1299 0.0862 -vn -0.9635 0.1326 0.2327 -vn -0.5579 -0.6411 0.5270 -vn -0.4508 -0.8280 0.3335 -vn -0.2540 -0.8026 0.5398 -vn 0.6252 -0.7183 0.3051 -vn 0.6799 -0.5164 0.5206 -vn 0.8951 -0.3474 0.2796 -vn -0.8053 0.5614 0.1905 -vn -0.8433 0.2970 0.4479 -vn -0.7763 -0.3759 0.5061 -vn -0.6759 0.6011 0.4264 -vn 0.4169 -0.7353 0.5344 -vn -0.8755 -0.0477 0.4809 -vn 0.0885 -0.8355 0.5424 -vn -0.7422 -0.5916 0.3149 -vn -0.9208 -0.2620 0.2890 -vn 0.8396 -0.2070 0.5023 -vn 0.8402 -0.4413 0.3151 -vn 0.9462 -0.1746 0.2723 -vn -0.9601 0.0975 0.2621 -vn 0.6134 -0.7204 0.3237 -vn 0.2824 -0.8975 0.3387 -vn -0.0909 -0.9353 0.3419 -vn 0.0000 -0.0787 -0.9969 -vn 0.3839 -0.5028 -0.7744 -vn 0.4653 -0.5729 -0.6747 -vn 0.9094 -0.3892 -0.1469 -vn -0.4661 0.2448 -0.8502 -vn -0.2790 0.4461 -0.8504 -vn -0.0512 -0.6480 -0.7599 -vn -0.0447 0.5210 -0.8524 -vn -0.4511 -0.4404 -0.7762 -vn -0.2655 -0.5868 -0.7650 -vn 0.1833 -0.6219 -0.7613 -vn 0.1768 0.4959 -0.8502 -vn 0.3835 0.3809 -0.8414 -vn -0.5590 -0.2462 -0.7917 -vn 0.5221 0.2070 -0.8274 -vn -0.5797 -0.0026 -0.8148 -vn -0.7051 -0.1143 -0.6999 -vn -0.5837 0.3320 -0.7410 -vn -0.2160 0.6162 -0.7574 -vn -0.4661 -0.5870 -0.6619 -vn 0.0376 -0.7589 -0.6502 -vn -0.2122 0.1538 0.9650 -vn -0.1952 0.1254 0.9727 -vn -0.2130 -0.0011 0.9770 -vn -0.2058 0.3243 0.9233 -vn -0.1866 0.5393 0.8212 -vn -0.1779 0.4465 0.8769 -vn -0.1337 0.8168 0.5612 -vn -0.1264 0.7592 0.6384 -vn -0.0277 0.9971 0.0702 -vn -0.0376 0.9776 0.2070 -vn 0.0868 0.8895 -0.4485 -vn 0.0588 0.9629 -0.2632 -vn 0.1508 0.6470 -0.7474 -vn 0.1340 0.7501 -0.6476 -vn 0.2090 0.1324 -0.9689 -vn 0.2000 0.1964 -0.9599 -vn 0.1996 0.2774 -0.9398 -vn 0.1963 -0.1301 -0.9719 -vn 0.2133 -0.0005 -0.9770 -vn 0.2145 -0.1350 -0.9673 -vn 0.2103 -0.2754 -0.9380 -vn 0.1240 -0.7597 -0.6383 -vn 0.1737 -0.6509 -0.7391 -vn 0.1173 -0.8957 -0.4289 -vn 0.0345 -0.9771 -0.2098 -vn 0.0102 -0.9976 0.0688 -vn -0.0616 -0.9641 0.2583 -vn -0.0785 -0.8902 0.4488 -vn -0.1365 -0.7519 0.6450 -vn -0.1295 -0.7336 0.6671 -vn -0.1656 -0.5575 0.8135 -vn -0.1799 -0.4804 0.8584 -vn -0.1882 -0.4005 0.8968 -vn -0.2025 -0.1628 0.9657 -vn -0.2009 -0.2658 0.9428 -vn -0.2090 -0.1339 0.9687 -vn 0.1824 0.4214 -0.8883 -vn 0.1657 0.7415 -0.6502 -vn 0.1725 0.0958 -0.9803 -vn 0.1324 -0.3262 -0.9360 -vn 0.1773 -0.4507 -0.8749 -vn -0.1143 -0.9751 0.1900 -vn -0.1766 -0.1874 0.9663 -vn -0.1120 0.4407 0.8906 -vn -0.0411 0.3138 0.9486 -vn -0.1495 0.1210 0.9813 -vn -0.0443 0.7535 0.6559 -vn 0.0529 0.6327 0.7726 -vn 0.0465 0.9739 0.2220 -vn 0.1661 0.9173 0.3619 -vn 0.1217 0.9592 -0.2551 -vn 0.2387 0.9551 -0.1756 -vn 0.1777 0.3153 -0.9322 -vn 0.0937 -0.0443 -0.9946 -vn -0.0543 -0.6474 -0.7602 -vn 0.0558 -0.7352 -0.6756 -vn -0.1589 -0.9197 -0.3591 -vn -0.0324 -0.9604 -0.2769 -vn -0.0272 0.0274 0.9993 -vn -0.1561 -0.2351 0.9593 -vn -0.1058 0.0661 0.9922 -vn 0.2994 0.7436 0.5978 -vn 0.3935 0.9119 0.1164 -vn 0.2452 0.7204 -0.6488 -vn 0.3791 0.8242 -0.4207 -vn 0.2634 0.5418 -0.7982 -vn -0.1317 -0.3914 -0.9108 -vn 0.0351 -0.3255 -0.9449 -vn -0.3053 -0.7362 -0.6039 -vn -0.4055 -0.9059 -0.1224 -vn -0.2240 -0.9615 0.1593 -vn -0.3955 -0.8291 0.3952 -vn -0.2294 -0.7559 0.6132 -vn -0.2813 -0.5404 0.7930 -vn -0.1957 -0.4664 0.8627 -vn 0.0757 0.2576 0.9633 -vn 0.1267 0.1741 0.9765 -vn 0.1425 0.1907 -0.9713 -vn 0.3745 0.4650 -0.8022 -vn -0.0924 -0.1246 -0.9879 -vn 0.0336 -0.0448 -0.9984 -vn -0.4367 -0.5755 -0.6914 -vn -0.5890 -0.7663 -0.2567 -vn -0.5895 -0.7619 0.2684 -vn -0.4347 -0.5580 0.7068 -vn -0.1998 -0.2599 0.9447 -vn 0.1040 0.0853 0.9909 -vn -0.0865 -0.0764 0.9933 -vn 0.3044 0.3959 0.8664 -vn 0.2976 0.2555 0.9198 -vn 0.4867 0.6312 0.6039 -vn 0.5308 0.4594 0.7122 -vn 0.6072 0.7842 0.1276 -vn 0.7289 0.6288 0.2709 -vn 0.5603 0.7205 -0.4086 -vn 0.7274 0.6277 -0.2772 -vn 0.5277 0.4564 -0.7164 -vn 0.2901 0.2407 -0.9262 -vn 0.0492 0.0531 -0.9974 -vn 0.3062 0.1637 -0.9378 -vn 0.1454 0.0392 0.9886 -vn -0.1023 -0.0532 0.9933 -vn 0.1257 0.0618 0.9901 -vn 0.8684 0.4156 0.2704 -vn 0.8334 0.2430 0.4964 -vn 0.6287 0.3048 0.7154 -vn 0.8665 0.4152 -0.2772 -vn 0.9593 0.2822 -0.0041 -vn 0.6209 0.3002 -0.7241 -vn 0.8170 0.2427 -0.5231 -vn 0.4698 0.1302 -0.8731 -vn -0.3174 -0.0896 0.9440 -vn -0.3555 -0.1787 0.9174 -vn -0.1096 -0.0138 0.9939 -vn -0.8643 -0.1250 -0.4873 -vn -0.6865 -0.2022 -0.6984 -vn -0.9255 -0.2738 -0.2618 -vn -0.9899 -0.1418 0.0038 -vn -0.9238 -0.2735 0.2680 -vn -0.8607 -0.1303 0.4921 -vn -0.6739 -0.1974 0.7120 -vn 0.6005 0.0735 -0.7962 -vn 0.8766 0.0636 -0.4769 -vn 0.8536 0.1311 -0.5042 -vn 0.6224 0.0114 0.7827 -vn 0.3917 -0.0048 0.9201 -vn 0.3879 0.0188 0.9215 -vn 0.6069 0.0117 -0.7947 -vn 0.8750 -0.0047 -0.4841 -vn 0.8756 0.0389 -0.4815 -vn -0.3996 0.0045 -0.9167 -vn -0.2079 -0.0025 -0.9781 -vn -0.3984 -0.0182 -0.9170 -vn -0.8694 0.0029 -0.4941 -vn -0.6229 -0.0115 -0.7822 -vn -0.8691 -0.0354 -0.4934 -vn -0.1625 0.0110 -0.9866 -vn -0.1491 -0.0088 -0.9888 -vn 0.4020 -0.0457 0.9145 -vn 0.1373 -0.0101 0.9905 -vn 0.4031 -0.0135 0.9150 -vn 0.6069 -0.0439 -0.7936 -vn 0.8547 -0.0834 -0.5124 -vn 0.8789 -0.0295 -0.4761 -vn 0.3845 -0.0641 0.9209 -vn 0.4889 -0.0864 -0.8681 -vn 0.3659 -0.0263 -0.9303 -vn -0.3829 0.0397 -0.9229 -vn -0.3254 0.0569 -0.9439 -vn -0.1197 0.0071 0.9928 -vn -0.3013 0.0530 0.9521 -vn -0.3865 0.0263 0.9219 -vn -0.4732 0.1595 0.8664 -vn -0.5995 0.1097 0.7928 -vn 0.6169 -0.3275 0.7157 -vn 0.5787 -0.1939 0.7921 -vn 0.8208 -0.2815 0.4971 -vn 0.8523 -0.4477 0.2705 -vn 0.9450 -0.3271 -0.0036 -vn 0.8508 -0.4472 -0.2760 -vn 0.8049 -0.2810 -0.5227 -vn 0.6296 -0.3219 -0.7070 -vn 0.4587 -0.1723 -0.8717 -vn -0.8321 0.2681 -0.4856 -vn -0.6241 0.3525 -0.6973 -vn -0.5933 0.1926 -0.7816 -vn -0.9523 0.3051 0.0038 -vn -0.8404 0.4751 -0.2609 -vn -0.8194 0.2640 0.5088 -vn -0.8388 0.4744 0.2671 -vn -0.6236 0.3455 0.7013 -vn -0.1138 0.1017 -0.9883 -vn 0.0654 -0.0580 -0.9962 -vn -0.1376 0.0729 -0.9878 -vn -0.3275 0.2285 0.9168 -vn -0.5321 0.4689 0.7050 -vn 0.2444 -0.3209 0.9150 -vn 0.0956 -0.0970 0.9907 -vn 0.3066 -0.2530 0.9176 -vn 0.0449 -0.0993 -0.9940 -vn 0.2330 -0.3275 -0.9157 -vn -0.2300 0.3225 0.9182 -vn -0.3600 0.6174 0.6995 -vn -0.4518 0.5451 0.7062 -vn 0.3430 -0.9062 0.2473 -vn 0.3424 -0.7329 0.5878 -vn 0.4817 -0.8393 0.2519 -vn -0.2014 0.3570 -0.9122 -vn -0.0749 0.2660 -0.9611 -vn -0.1191 0.1704 -0.9782 -vn -0.3651 0.6174 -0.6968 -vn -0.1521 0.5039 -0.8502 -vn -0.4924 0.8309 -0.2593 -vn -0.2920 0.7528 -0.5899 -vn -0.4905 0.8288 0.2693 -vn -0.3510 0.9280 -0.1250 -vn -0.3235 0.8592 0.3964 -vn -0.2166 0.5676 0.7943 -vn -0.0655 0.1089 0.9919 -vn -0.0748 0.2564 0.9637 -vn 0.0574 -0.1334 0.9894 -vn -0.0001 0.0062 1.0000 -vn 0.0753 -0.2681 0.9605 -vn 0.1601 -0.3434 0.9254 -vn 0.1029 -0.5805 0.8077 -vn 0.2560 -0.5460 0.7977 -vn 0.2179 -0.7798 0.5869 -vn 0.2127 -0.9691 0.1245 -vn 0.3489 -0.8952 -0.2771 -vn 0.1774 -0.9025 -0.3925 -vn 0.1879 -0.6686 -0.7195 -vn 0.0694 -0.4825 -0.8731 -vn 0.1323 -0.3664 -0.9210 -vn 0.0455 -0.2462 -0.9682 -vn -0.1330 0.5912 0.7955 -vn 0.0014 0.2483 0.9687 -vn -0.0032 -0.2639 0.9645 -vn 0.0631 -0.9188 0.3896 -vn 0.0775 -0.9861 -0.1468 -vn 0.0593 -0.7863 -0.6149 -vn -0.1791 0.7859 -0.5918 -vn -0.0397 0.6176 -0.7855 -vn -0.2283 0.9656 -0.1249 -vn -0.0478 0.9232 -0.3813 -vn -0.2121 0.8931 0.3967 -vn -0.0521 0.9884 0.1430 -vn -0.0480 0.7957 0.6038 -vn -0.1052 -0.7315 0.6737 -vn 0.0053 -0.4513 0.8923 -vn 0.0045 0.5117 0.8591 -vn 0.0715 0.7893 0.6098 -vn 0.0011 0.0061 1.0000 -vn 0.0521 0.2423 0.9688 -vn -0.0725 -0.2514 0.9652 -vn -0.2158 -0.9091 0.3563 -vn -0.0690 -0.9277 0.3668 -vn -0.2685 -0.7539 0.5996 -vn -0.1369 -0.4881 0.8620 -vn -0.3223 -0.9288 0.1830 -vn -0.2231 -0.9636 -0.1475 -vn -0.3084 -0.9047 -0.2941 -vn -0.1863 -0.7744 -0.6047 -vn -0.2148 -0.6719 -0.7088 -vn -0.0750 -0.4918 -0.8675 -vn -0.1475 -0.3473 -0.9261 -vn -0.0348 -0.2293 -0.9727 -vn -0.0432 -0.1096 -0.9930 -vn 0.0025 0.0387 -0.9992 -vn 0.1816 0.9076 -0.3786 -vn 0.2687 0.7551 -0.5981 -vn 0.1161 0.6088 -0.7848 -vn 0.1969 0.9697 0.1449 -vn 0.3357 0.9244 -0.1808 -vn 0.1572 0.7789 0.6071 -vn 0.3307 0.8997 0.2849 -vn 0.0977 0.4966 0.8625 -vn 0.2425 0.6551 0.7155 -vn 0.1167 0.2942 0.9486 -vn -0.1859 -0.3013 0.9352 -vn 0.0054 0.0046 1.0000 -vn -0.2984 -0.6460 0.7026 -vn -0.4273 -0.8674 0.2549 -vn 0.0724 0.2794 -0.9574 -vn 0.0689 0.1248 -0.9898 -vn 0.1764 0.4792 -0.8598 -vn 0.2135 0.3584 -0.9088 -vn 0.3299 0.6443 -0.6900 -vn 0.4409 0.8627 -0.2478 -vn 0.0934 0.1100 0.9895 -vn -0.4074 -0.5808 0.7047 -vn -0.0723 -0.0876 -0.9935 -vn 0.1982 0.3272 0.9240 -vn 0.4069 0.5763 0.7087 -vn 0.3122 0.6311 0.7101 -vn 0.2326 0.2623 -0.9365 -vn 0.2687 0.2842 0.9204 -vn -0.4147 -0.2982 0.8597 -vn -0.0871 -0.0855 0.9925 -vn -0.2631 -0.2931 0.9192 -vn -0.4958 -0.5115 0.7018 -vn -0.6861 -0.5204 0.5084 -vn -0.6738 -0.6928 0.2570 -vn -0.7984 -0.6019 -0.0170 -vn -0.6685 -0.6863 -0.2865 -vn -0.6868 -0.5136 -0.5143 -vn -0.4841 -0.4897 -0.7251 -vn 0.1276 0.0942 -0.9873 -vn 0.0829 0.0453 -0.9955 -vn 0.4859 0.5286 -0.6960 -vn 0.4238 0.2932 -0.8570 -vn 0.6576 0.7108 -0.2497 -vn 0.7098 0.5023 -0.4938 -vn 0.6523 0.7052 0.2780 -vn 0.8150 0.5792 0.0161 -vn 0.4767 0.5184 0.7100 -vn 0.7052 0.5031 0.4995 -vn -0.0535 -0.0123 0.9985 -vn 0.1029 0.0738 0.9919 -vn -0.3660 -0.1899 0.9111 -vn 0.2889 0.1321 0.9482 -vn 0.3004 0.2230 0.9274 -vn 0.1318 0.0342 0.9907 -vn -0.2555 -0.0581 0.9651 -vn -0.5912 -0.1896 0.7839 -vn -0.5649 -0.2596 0.7832 -vn -0.7904 -0.3641 0.4926 -vn -0.8831 -0.2718 0.3824 -vn -0.9073 -0.4202 -0.0169 -vn -0.9441 -0.2899 -0.1567 -vn -0.7790 -0.3620 -0.5120 -vn -0.7466 -0.2303 -0.6241 -vn -0.5448 -0.2530 -0.7995 -vn -0.4720 -0.1455 -0.8695 -vn -0.3655 -0.1603 -0.9169 -vn -0.2420 -0.0717 -0.9676 -vn -0.1592 -0.1108 -0.9810 -vn 0.4573 0.2301 -0.8590 -vn 0.3141 0.0933 -0.9448 -vn -0.6238 -0.0909 0.7763 -vn -0.0386 -0.0081 -0.9992 -vn -0.2340 -0.0267 -0.9719 -vn 0.2106 0.0312 -0.9771 -vn 0.5993 0.1788 -0.7803 -vn 0.6060 0.0987 -0.7893 -vn 0.8418 0.2559 -0.4752 -vn 0.9559 0.2931 0.0175 -vn 0.9154 0.1492 -0.3739 -vn 0.8273 0.2531 0.5015 -vn 0.9755 0.1615 0.1495 -vn 0.5829 0.1794 0.7924 -vn 0.7766 0.1310 0.6162 -vn 0.3582 0.1103 0.9271 -vn 0.4928 0.0831 0.8662 -vn 0.2451 0.0339 0.9689 -vn -0.3733 -0.0231 0.9274 -vn -0.7961 -0.0759 0.6004 -vn -0.9134 -0.1438 0.3807 -vn -0.9782 -0.0928 0.1859 -vn -0.9756 -0.1553 -0.1555 -vn -0.9572 -0.0890 -0.2755 -vn -0.7731 -0.1243 -0.6220 -vn -0.5922 -0.0592 -0.8036 -vn -0.4842 -0.0830 -0.8710 -vn -0.4060 -0.0364 -0.9131 -vn 0.8883 0.0797 0.4523 -vn -0.4994 -0.0504 -0.8649 -vn 0.2497 0.0186 -0.9681 -vn 0.1581 -0.0039 -0.9874 -vn 0.4929 0.0469 0.8688 -vn 0.7773 0.0616 0.6261 -vn 0.6943 0.0632 0.7169 -vn -0.5282 -0.0355 0.8484 -vn -0.5237 -0.0502 0.8504 -vn -0.9828 -0.0750 -0.1690 -vn -0.7836 -0.0698 -0.6174 -vn -0.7897 -0.0748 -0.6089 -vn -0.0637 -0.0129 -0.9979 -vn -0.2645 -0.0341 -0.9638 -vn 0.4901 0.0420 -0.8707 -vn 0.1384 -0.0659 -0.9882 -vn 0.7213 0.0605 -0.6900 -vn 0.6768 0.0546 -0.7341 -vn 0.9842 0.0697 0.1630 -vn 0.9939 0.0903 0.0637 -vn 0.2362 0.0681 0.9693 -vn 0.0560 0.0108 0.9984 -vn -0.1205 0.0063 0.9927 -vn -0.5049 -0.0659 0.8607 -vn -0.9769 -0.1205 -0.1767 -vn -0.9650 -0.0908 0.2461 -vn 0.0136 -0.0299 -0.9995 -vn 0.1557 0.0802 0.9845 -vn 0.2465 0.0575 0.9674 -vn -0.1657 0.0434 0.9852 -vn -0.7913 -0.0677 0.6076 -vn -0.8512 -0.1668 0.4976 -vn -0.9737 -0.2272 -0.0140 -vn -0.8384 -0.2253 -0.4963 -vn -0.7814 -0.1064 -0.6149 -vn -0.5905 -0.1843 -0.7857 -vn -0.4965 -0.0830 -0.8641 -vn -0.3531 -0.1381 -0.9253 -vn -0.2505 -0.0541 -0.9666 -vn -0.0648 -0.0707 -0.9954 -vn 0.2938 0.0402 -0.9550 -vn 0.9188 0.0913 -0.3840 -vn 0.8705 0.1497 -0.4688 -vn 0.9788 0.1346 0.1546 -vn 0.9782 0.2072 0.0111 -vn 0.7775 0.1176 0.6177 -vn 0.8389 0.2071 0.5034 -vn 0.4917 0.0883 0.8663 -vn 0.5855 0.1705 0.7925 -vn 0.3522 0.1299 0.9269 -vn -0.4199 -0.0841 0.9037 -vn -0.6141 -0.3335 0.7153 -vn -0.6068 -0.1533 0.7799 -vn -0.8249 -0.2919 0.4840 -vn -0.7525 -0.5835 0.3053 -vn -0.9159 -0.4014 -0.0009 -vn -0.6938 -0.6925 -0.1977 -vn -0.7791 -0.4078 -0.4762 -vn -0.4681 -0.6246 -0.6251 -vn -0.5413 -0.3441 -0.7672 -vn 0.8077 0.3205 -0.4948 -vn 0.6196 0.3043 -0.7235 -vn 0.5903 0.1255 -0.7974 -vn 0.9114 0.4114 -0.0139 -vn 0.7521 0.5629 -0.3428 -vn 0.7718 0.4103 0.4859 -vn 0.7005 0.6874 0.1918 -vn 0.5276 0.3312 0.7823 -vn 0.4382 0.6057 0.6642 -vn 0.0761 0.1748 0.9817 -vn 0.0884 0.3690 0.9252 -vn 0.3132 0.2557 0.9146 -vn -0.1898 0.3452 0.9191 -vn -0.1898 0.1586 0.9689 -vn -0.4043 -0.0642 0.9124 -vn -0.5337 -0.4947 0.6859 -vn -0.5294 -0.8036 0.2721 -vn -0.3817 -0.8997 -0.2119 -vn -0.1338 -0.7626 -0.6328 -vn -0.2137 -0.4675 -0.8578 -vn 0.1453 -0.4374 -0.8874 -vn -0.0105 -0.3066 -0.9518 -vn 0.2027 -0.1220 -0.9716 -vn 0.4299 0.1073 -0.8965 -vn 0.3276 -0.0650 -0.9426 -vn 0.5483 0.6518 -0.5239 -vn 0.4927 0.8631 -0.1111 -vn 0.3199 0.8836 0.3421 -vn 0.0567 0.6800 0.7310 -vn -0.3519 0.2701 0.8962 -vn 0.3956 -0.1122 -0.9115 -vn 0.4357 0.5277 -0.7292 -vn 0.5341 0.3910 -0.7496 -vn 0.3494 0.8567 -0.3794 -vn 0.1487 0.9835 0.1025 -vn -0.0779 0.8416 0.5344 -vn -0.2415 0.5747 0.7820 -vn -0.3473 -0.7167 0.6048 -vn -0.4458 -0.5272 0.7234 -vn -0.3461 -0.8677 0.3567 -vn -0.2188 -0.9260 0.3076 -vn -0.1564 -0.9817 -0.1090 -vn -0.0068 -0.9887 -0.1494 -vn 0.0719 -0.8355 -0.5448 -vn 0.2752 -0.6730 -0.6865 -vn 0.2760 -0.4885 -0.8277 -vn 0.1852 0.9672 -0.1738 -vn -0.0476 0.9510 0.3056 -vn -0.3967 0.1583 0.9042 -vn -0.3682 0.1273 0.9210 -vn -0.4017 -0.0019 0.9158 -vn -0.3819 0.3301 0.8633 -vn -0.3430 0.5424 0.7669 -vn -0.3083 0.5575 0.7708 -vn -0.2398 0.8159 0.5262 -vn -0.0506 0.9947 0.0893 -vn -0.1566 0.8994 0.4080 -vn 0.1163 0.9440 -0.3087 -vn 0.0347 0.9965 -0.0763 -vn 0.3817 0.2811 -0.8805 -vn 0.3640 0.3192 -0.8750 -vn 0.3516 0.4486 -0.8217 -vn 0.3937 0.1312 -0.9098 -vn 0.3728 -0.1326 -0.9184 -vn 0.3991 0.0002 -0.9169 -vn 0.3995 -0.1351 -0.9067 -vn 0.3882 -0.2794 -0.8782 -vn 0.3117 -0.5602 -0.7675 -vn 0.3661 -0.4458 -0.8168 -vn 0.3142 -0.6526 -0.6895 -vn -0.1490 -0.9237 0.3530 -vn -0.0053 -0.9977 0.0683 -vn -0.1677 -0.8876 0.4289 -vn -0.2589 -0.7338 0.6281 -vn -0.2571 -0.7641 0.5916 -vn -0.3215 -0.5600 0.7636 -vn -0.3699 -0.2785 0.8863 -vn -0.3587 -0.4055 0.8407 -vn -0.3806 -0.2646 0.8861 -vn -0.3964 -0.1327 0.9084 -vn -0.3224 -0.1220 0.9387 -vn -0.2759 0.3162 0.9077 -vn -0.1583 0.7328 0.6618 -vn -0.0078 0.9610 0.2764 -vn 0.2328 0.8201 -0.5227 -vn 0.1458 0.9786 -0.1455 -vn 0.3187 0.1286 -0.9391 -vn 0.2733 -0.3280 -0.9043 -vn 0.1564 -0.7345 -0.6603 -vn 0.1738 -0.8832 -0.4356 -vn 0.0119 -0.9593 -0.2820 -vn 0.0161 -0.9982 -0.0570 -vn -0.1380 -0.9764 0.1660 -vn -0.3147 -0.4569 0.8320 -vn -0.2229 -0.0952 0.9702 -vn -0.1062 0.3959 0.9121 -vn 0.0510 0.7920 0.6084 -vn 0.1978 0.9724 0.1240 -vn 0.2926 0.8704 -0.3960 -vn 0.2256 0.0653 -0.9720 -vn 0.1038 -0.4174 -0.9028 -vn -0.0513 -0.7941 -0.6057 -vn -0.1949 -0.9705 -0.1422 -vn -0.2857 -0.8856 0.3661 -vn -0.2998 -0.5635 0.7698 -vn -0.0372 0.0964 0.9946 -vn 0.2337 0.6387 0.7331 -vn 0.3775 0.8776 0.2956 -vn 0.4214 0.8718 -0.2497 -vn 0.3001 0.5299 -0.7932 -vn 0.3470 0.6566 -0.6697 -vn 0.2441 0.3235 -0.9142 -vn 0.0220 -0.1307 -0.9912 -vn -0.2452 -0.6493 -0.7199 -vn -0.3897 -0.8698 -0.3027 -vn -0.4379 -0.8722 0.2181 -vn -0.3651 -0.6404 0.6758 -vn -0.2077 -0.2506 0.9456 -vn 0.4570 0.5773 -0.6767 -vn -0.1083 -0.0618 0.9922 -vn -0.1292 -0.1168 0.9847 -vn 0.0513 0.0680 0.9964 -vn 0.0940 0.1000 -0.9905 -vn 0.3140 0.2714 -0.9098 -vn 0.7269 0.6071 0.3210 -vn 0.7296 0.4132 0.5450 -vn 0.5060 0.4241 0.7511 -vn 0.7473 0.6253 -0.2247 -vn 0.8676 0.4948 0.0501 -vn 0.5622 0.4750 -0.6770 -vn 0.7617 0.4371 -0.4783 -vn 0.4544 0.2617 -0.8515 -vn -0.0784 -0.0647 -0.9948 -vn 0.1534 0.0868 -0.9843 -vn -0.2570 -0.1358 -0.9568 -vn -0.2720 -0.2399 -0.9319 -vn -0.7469 -0.4031 -0.5287 -vn -0.5103 -0.4521 -0.7316 -vn -0.7124 -0.6301 -0.3091 -vn -0.8796 -0.4734 -0.0479 -vn -0.7314 -0.6465 0.2169 -vn -0.7808 -0.4186 0.4638 -vn -0.5622 -0.4972 0.6609 -vn -0.4743 -0.2530 0.8432 -vn -0.3306 -0.2847 0.8998 -vn -0.1083 -0.0270 -0.9937 -vn 0.3509 0.0581 0.9346 -vn 0.1446 0.0347 0.9889 -vn 0.3354 0.1181 0.9347 -vn 0.1607 0.0419 -0.9861 -vn 0.5411 0.0885 -0.8363 -vn 0.5055 0.1737 -0.8452 -vn -0.1667 -0.0131 0.9859 -vn -0.2523 -0.0680 0.9653 -vn -0.0377 -0.0104 0.9992 -vn 0.0937 0.0060 0.9956 -vn 0.3408 -0.0014 0.9401 -vn 0.3391 0.0350 0.9401 -vn 0.5779 0.0311 0.8155 -vn 0.6499 0.0371 -0.7591 -vn 0.9012 0.0007 -0.4335 -vn 0.8864 0.0763 -0.4566 -vn -0.6537 -0.0129 -0.7566 -vn -0.5756 -0.0531 -0.8160 -vn -0.8441 -0.0772 -0.5306 -vn 0.9691 -0.0574 -0.2399 -vn 0.9986 0.0087 0.0530 -vn 0.7381 -0.0426 -0.6733 -vn -0.1103 -0.0057 -0.9939 -vn 0.1671 -0.0112 -0.9859 -vn 0.1562 0.0107 -0.9877 -vn -0.9993 0.0281 -0.0254 -vn -0.9003 0.0016 -0.4353 -vn -0.9988 -0.0313 -0.0378 -vn -0.9465 0.0818 -0.3120 -vn -0.7656 0.0469 -0.6416 -vn -0.9755 0.0831 0.2038 -vn -0.8953 0.0179 0.4451 -vn 0.1770 -0.0398 0.9834 -vn -0.0099 0.0011 1.0000 -vn 0.2161 -0.0108 0.9763 -vn 0.5165 -0.0717 0.8533 -vn 0.3457 -0.0222 0.9381 -vn 0.5578 -0.0380 0.8291 -vn 0.7504 -0.1153 0.6509 -vn 0.7475 -0.0484 0.6625 -vn 0.8966 -0.0560 0.4392 -vn 0.9721 -0.1497 0.1804 -vn 0.9837 -0.0628 0.1685 -vn 0.9290 -0.1305 -0.3464 -vn 0.4321 -0.0271 -0.9014 -vn 0.4254 -0.0612 -0.9029 -vn -0.1535 0.0079 -0.9881 -vn -0.1112 0.0252 -0.9935 -vn -0.8320 0.1366 -0.5377 -vn -0.9849 0.1653 -0.0524 -vn -0.7328 0.0622 0.6776 -vn -0.8864 0.1405 0.4412 -vn 0.5531 -0.1399 0.8213 -vn 0.8169 -0.2018 0.5403 -vn 0.9700 -0.2380 0.0492 -vn 0.8690 -0.2342 -0.4359 -vn 0.6728 -0.1510 -0.7243 -vn -0.3393 0.0879 -0.9365 -vn -0.3389 0.0480 -0.9396 -vn -0.6612 0.1415 0.7367 -vn -0.8642 0.2369 0.4439 -vn -0.0295 0.0091 0.9995 -vn -0.1851 0.0445 0.9817 -vn -0.4236 0.0886 0.9015 -vn -0.4978 0.2146 0.8403 -vn -0.2813 0.2016 -0.9382 -vn -0.1381 0.0765 -0.9875 -vn -0.3213 0.1317 -0.9378 -vn -0.1756 0.1392 0.9746 -vn -0.4659 0.3263 0.8225 -vn 0.1320 -0.0726 0.9886 -vn 0.0460 -0.0621 0.9970 -vn 0.5844 -0.7933 0.1706 -vn 0.6783 -0.6106 0.4087 -vn 0.8118 -0.5831 0.0305 -vn 0.5730 -0.7865 -0.2305 -vn 0.6976 -0.5286 -0.4836 -vn -0.3656 0.4024 0.8393 -vn -0.0785 0.1551 0.9848 -vn 0.3456 -0.6782 0.6486 -vn 0.3308 -0.4452 0.8321 -vn 0.5619 -0.4908 0.6659 -vn 0.0698 -0.1521 -0.9859 -vn 0.2449 -0.3262 -0.9130 -vn 0.1322 -0.1093 -0.9852 -vn -0.5763 0.6231 -0.5288 -vn -0.3359 0.5893 -0.7347 -vn -0.3967 0.4279 -0.8121 -vn -0.6800 0.7317 -0.0473 -vn -0.4749 0.8238 -0.3094 -vn -0.6042 0.6481 0.4637 -vn -0.4874 0.8455 0.2180 -vn -0.3789 0.6481 0.6606 -vn 0.3102 -0.9404 0.1390 -vn 0.4042 -0.8019 0.4400 -vn 0.2259 -0.6981 -0.6794 -vn 0.4852 -0.6627 -0.5704 -vn 0.3797 -0.5169 -0.7672 -vn -0.1943 0.3944 0.8981 -vn -0.2685 0.6953 0.6667 -vn 0.0061 -0.0532 0.9986 -vn -0.0275 0.1730 0.9845 -vn 0.1537 -0.7735 0.6149 -vn 0.1788 -0.5342 0.8262 -vn 0.0387 -0.1762 -0.9836 -vn 0.1326 -0.4054 -0.9045 -vn 0.0146 -0.2430 0.9699 -vn 0.1149 -0.3196 0.9406 -vn 0.0722 -0.5446 0.8356 -vn 0.0782 -0.9897 -0.1200 -vn 0.2936 -0.9228 -0.2495 -vn 0.0506 -0.8037 -0.5928 -vn 0.0294 -0.4746 -0.8797 -vn -0.1185 0.3548 -0.9274 -vn -0.0155 0.2403 -0.9706 -vn -0.0500 0.1441 -0.9883 -vn -0.1711 0.6556 -0.7355 -vn -0.0229 0.4787 -0.8777 -vn -0.2434 0.9191 -0.3097 -vn -0.0909 0.7711 -0.6303 -vn -0.2488 0.9434 0.2191 -vn -0.1097 0.9783 -0.1759 -vn -0.1889 0.7156 0.6725 -vn -0.1147 0.9365 0.3314 -vn -0.0936 0.4201 0.9026 -vn -0.0193 0.5270 0.8497 -vn 0.0169 0.0709 0.9973 -vn -0.0352 -0.4514 0.8916 -vn -0.0887 -0.9815 0.1699 -vn 0.1901 -0.9121 0.3633 -vn -0.0262 -0.1937 -0.9807 -vn -0.0357 0.7473 0.6635 -vn 0.0022 0.9454 0.3258 -vn 0.0246 0.3141 0.9491 -vn -0.1384 -0.9247 -0.3547 -vn -0.0073 0.9843 -0.1763 -vn 0.1473 0.8993 -0.4119 -vn 0.0017 0.7666 -0.6422 -vn 0.1611 0.9827 0.0915 -vn 0.1217 0.8105 0.5729 -vn 0.1227 0.5335 0.8369 -vn 0.1150 0.2827 0.9523 -vn -0.1683 -0.4233 0.8902 -vn -0.0411 -0.1888 0.9812 -vn -0.3208 -0.8455 0.4269 -vn -0.0775 -0.8353 0.5443 -vn -0.3656 -0.9268 -0.0855 -vn -0.1054 -0.2818 -0.9537 -vn -0.1091 -0.4515 -0.8856 -vn -0.0082 -0.0147 -0.9999 -vn -0.0014 0.0469 -0.9989 -vn 0.0457 0.2137 -0.9758 -vn 0.2513 0.7862 0.5646 -vn -0.1545 -0.2523 0.9552 -vn 0.2404 0.6336 -0.7354 -vn 0.2388 0.4171 -0.8769 -vn 0.1002 0.4612 -0.8816 -vn 0.2336 0.4970 0.8357 -vn -0.3779 -0.4336 0.8180 -vn -0.2541 -0.6289 0.7347 -vn -0.4495 -0.7205 0.5280 -vn -0.5992 -0.7987 0.0549 -vn -0.5373 -0.6986 -0.4725 -vn -0.3370 -0.8028 -0.4918 -vn -0.2514 -0.5745 -0.7789 -vn -0.2544 -0.3331 -0.9079 -vn 0.3919 0.8266 -0.4040 -vn 0.4295 0.6284 -0.6486 -vn 0.4198 0.9029 0.0927 -vn 0.5496 0.8038 -0.2276 -vn 0.3490 0.7482 0.5643 -vn 0.5524 0.7998 0.2351 -vn 0.4229 0.6109 0.6693 -vn 0.2676 0.3464 0.8991 -vn 0.0553 0.0351 0.9978 -vn 0.1087 0.1353 0.9848 -vn -0.6163 -0.5879 0.5239 -vn 0.1942 0.2023 -0.9599 -vn 0.1059 0.1946 -0.9752 -vn 0.4008 0.3946 -0.8269 -vn 0.6307 0.7246 -0.2778 -vn 0.4429 0.4413 0.7804 -vn -0.7886 -0.6109 0.0700 -vn -0.1328 -0.1063 -0.9854 -vn -0.1763 -0.2124 -0.9612 -vn 0.0778 0.0529 -0.9956 -vn 0.5875 0.5888 0.5552 -vn 0.7390 0.6405 0.2087 -vn 0.6368 0.7413 0.2121 -vn 0.1900 0.1467 0.9708 -vn -0.3026 -0.1720 0.9375 -vn -0.1128 -0.0958 0.9890 -vn -0.2694 -0.2808 0.9212 -vn 0.6146 0.4124 0.6725 -vn 0.3549 0.2686 0.8955 -vn -0.9201 -0.3854 0.0697 -vn -0.7264 -0.4416 0.5266 -vn -0.0762 -0.0241 -0.9968 -vn 0.1844 0.0478 0.9817 -vn 0.2148 0.0865 0.9728 -vn -0.0670 -0.0215 0.9975 -vn 0.6682 0.4016 -0.6262 -vn 0.6360 0.2368 -0.7344 -vn 0.4861 0.2953 -0.8225 -vn 0.4101 0.1763 0.8948 -vn 0.6911 0.2484 0.6788 -vn 0.6563 0.3388 0.6742 -vn -0.8252 -0.1443 0.5462 -vn -0.5563 -0.1719 0.8130 -vn -0.7982 -0.2801 0.5333 -vn -0.9438 -0.2395 -0.2279 -vn -0.8668 -0.3657 -0.3390 -vn -0.2388 -0.0484 -0.9699 -vn -0.2484 -0.1235 -0.9608 -vn 0.8945 0.3279 -0.3038 -vn 0.8393 0.1506 -0.5223 -vn 0.9126 0.3377 0.2303 -vn 0.9767 0.2117 -0.0348 -vn 0.8664 0.1926 0.4607 -vn 0.0095 0.0202 0.9998 -vn -0.1945 -0.0210 0.9807 -vn -0.3255 -0.0706 0.9429 -vn -0.6333 -0.0471 0.7724 -vn -0.9658 -0.0887 -0.2436 -vn -0.9676 -0.1814 0.1758 -vn 0.3444 0.0874 -0.9347 -vn 0.2304 0.0066 -0.9731 -vn 0.0976 0.0351 -0.9946 -vn 0.6340 0.1132 0.7650 -vn 0.8545 0.1012 0.5095 -vn -0.5137 -0.0024 0.8580 -vn -0.8046 -0.0446 0.5921 -vn -0.9810 -0.0611 0.1839 -vn 0.5756 0.0885 -0.8129 -vn 0.5074 0.0035 -0.8617 -vn 0.9845 0.1101 -0.1366 -vn 0.9353 0.0527 -0.3499 -vn -0.9898 -0.1047 -0.0964 -vn -0.8809 -0.1183 -0.4583 -vn -0.8059 -0.1410 -0.5750 -vn -0.7151 -0.1116 -0.6900 -vn -0.6200 -0.1109 -0.7767 -vn -0.5291 -0.0767 -0.8451 -vn -0.4112 -0.0797 -0.9080 -vn -0.0486 -0.0293 -0.9984 -vn -0.2977 -0.0627 -0.9526 -vn 0.9756 0.1041 0.1931 -vn 0.9925 0.0955 0.0759 -vn 0.1870 0.0501 0.9811 -vn 0.3694 0.0721 0.9265 -vn 0.4077 0.0825 0.9094 -vn -0.9379 -0.0627 0.3412 -vn 0.2370 -0.0405 -0.9707 -vn 0.9171 0.1106 -0.3829 -vn 0.8870 0.1174 0.4467 -vn -0.5222 -0.1388 -0.8414 -vn 0.0217 -0.0991 -0.9948 -vn -0.0442 -0.0571 -0.9974 -vn 0.7454 0.0600 -0.6640 -vn 0.7603 0.0377 -0.6485 -vn 0.3112 0.1020 0.9449 -vn -0.9430 -0.1700 0.2860 -vn -0.7697 -0.0558 0.6359 -vn -0.9479 -0.2332 -0.2170 -vn -0.7083 -0.2405 -0.6637 -vn -0.3532 -0.1917 -0.9157 -vn -0.2823 -0.1135 -0.9526 -vn 0.4976 0.0083 -0.8674 -vn 0.2479 -0.0652 -0.9666 -vn 0.9780 0.2071 -0.0255 -vn 0.8585 0.2343 0.4562 -vn 0.7388 0.1105 0.6648 -vn 0.3390 0.1899 0.9214 -vn 0.5752 0.1744 0.7992 -vn 0.0537 0.1797 0.9823 -vn 0.1342 0.1252 0.9830 -vn -0.7458 -0.1702 0.6441 -vn -0.5365 -0.0253 0.8435 -vn 0.9218 0.2482 -0.2979 -vn 0.9147 0.3517 0.1992 -vn 0.6710 0.3440 0.6568 -vn -0.2651 0.0455 0.9631 -vn -0.3084 0.0237 0.9509 -vn -0.6127 -0.2192 0.7593 -vn -0.0240 -0.1977 -0.9800 -vn -0.2831 -0.3700 -0.8849 -vn -0.3369 -0.2704 -0.9019 -vn 0.5077 0.1019 -0.8555 -vn 0.7676 0.4194 -0.4846 -vn -0.4632 -0.2256 0.8571 -vn -0.6662 -0.5401 0.5142 -vn -0.8275 -0.4378 0.3516 -vn -0.6942 -0.7195 0.0193 -vn -0.8247 -0.5432 -0.1573 -vn -0.5350 -0.7033 -0.4681 -vn -0.6060 -0.5081 -0.6120 -vn -0.2516 -0.5100 -0.8225 -vn 0.4446 0.2052 -0.8719 -vn 0.7086 0.2253 -0.6687 -vn 0.8203 0.4579 -0.3426 -vn 0.6629 0.5212 -0.5375 -vn 0.8158 0.5547 0.1636 -vn 0.6955 0.7178 -0.0334 -vn 0.5933 0.5095 0.6232 -vn 0.5334 0.6959 0.4808 -vn 0.3225 0.4035 0.8563 -vn 0.2562 0.4882 0.8343 -vn 0.2075 0.3464 0.9148 -vn -0.1056 0.2842 0.9529 -vn 0.0422 0.5661 0.8233 -vn -0.4043 -0.1476 0.9027 -vn -0.4896 -0.5022 0.7128 -vn -0.5181 -0.8150 0.2595 -vn -0.3822 -0.8795 -0.2835 -vn -0.1595 -0.7022 -0.6939 -vn 0.0448 -0.4420 -0.8959 -vn 0.0182 -0.2620 -0.9649 -vn 0.2806 0.0274 -0.9594 -vn 0.1819 -0.1810 -0.9665 -vn 0.4799 0.5144 -0.7107 -vn 0.5409 0.7112 -0.4489 -vn 0.4677 0.8830 0.0381 -vn 0.2780 0.8042 0.5253 -vn -0.1868 0.5258 0.8298 -vn -0.3360 0.1180 0.9344 -vn -0.4220 -0.6885 0.5898 -vn 0.2250 -0.4477 -0.8654 -vn 0.3436 -0.1251 -0.9307 -vn 0.4200 0.2214 -0.8801 -vn 0.3641 0.8528 -0.3745 -vn 0.2277 0.9698 0.0877 -vn 0.0106 0.8402 0.5421 -vn -0.3864 0.3989 0.8316 -vn -0.4656 -0.3623 0.8075 -vn -0.2361 -0.9425 0.2366 -vn -0.3508 -0.9058 0.2375 -vn -0.1858 -0.9568 -0.2237 -vn 0.0218 -0.9659 -0.2579 -vn 0.0344 -0.7718 -0.6350 -vn 0.2494 -0.7271 -0.6396 -vn 0.4513 -0.0680 -0.8898 -vn 0.4685 0.3465 -0.8127 -vn 0.3978 0.6989 -0.5945 -vn 0.2405 0.9401 -0.2414 -vn -0.0135 0.9675 0.2526 -vn -0.2468 0.7234 0.6448 -vn -0.4807 0.3599 0.7997 -vn -0.4571 0.0469 0.8882 -vn -0.5229 -0.0977 0.8468 -vn -0.4832 -0.4487 0.7518 -vn -0.3724 -0.7382 0.5625 -vn 0.0476 -0.9920 -0.1172 -vn 0.2995 -0.8039 -0.5138 -vn 0.4746 -0.4093 -0.7793 -vn 0.3848 -0.4031 -0.8303 -vn 0.5267 0.0414 -0.8491 -vn 0.4786 0.4657 -0.7444 -vn -0.0408 0.9931 0.1099 -vn -0.3008 0.7993 0.5203 -vn -0.4052 -0.6676 0.6247 -vn -0.2936 -0.8404 0.4556 -vn -0.1895 -0.9442 0.2693 -vn 0.1620 0.9567 -0.2417 -vn 0.1846 0.9476 -0.2606 -vn -0.1282 0.9713 0.2003 -vn -0.4395 0.6052 0.6638 -vn -0.7191 0.2083 0.6629 -vn -0.6438 0.2753 0.7139 -vn -0.7427 -0.0043 0.6696 -vn -0.6630 0.4348 0.6094 -vn -0.4077 0.8239 0.3936 -vn -0.4274 0.7535 0.4996 -vn -0.5609 0.6419 0.5229 -vn 0.2729 0.9409 -0.2007 -vn -0.0595 0.9930 0.1024 -vn -0.1266 0.9800 0.1534 -vn 0.5132 0.7429 -0.4298 -vn 0.3196 0.8954 -0.3101 -vn 0.6380 0.5404 -0.5486 -vn 0.5676 0.5696 -0.5945 -vn 0.7090 0.3344 -0.6209 -vn 0.7387 0.1520 -0.6567 -vn 0.6673 0.1890 -0.7204 -vn 0.6429 -0.2661 -0.7182 -vn 0.7436 0.0023 -0.6686 -vn 0.7361 -0.1462 -0.6609 -vn 0.7000 -0.3315 -0.6325 -vn 0.6202 -0.5370 -0.5718 -vn 0.4532 -0.7241 -0.5199 -vn 0.4913 -0.7393 -0.4606 -vn 0.2403 -0.9410 -0.2383 -vn -0.4001 -0.8351 0.3776 -vn -0.1522 -0.9816 0.1155 -vn -0.4363 -0.8225 0.3649 -vn -0.5604 -0.5819 0.5893 -vn -0.5746 -0.6524 0.4942 -vn -0.6758 -0.4320 0.5973 -vn -0.3738 0.6549 0.6568 -vn -0.4453 0.5152 0.7323 -vn 0.1994 0.9408 -0.2741 -vn 0.4032 0.6642 -0.6295 -vn 0.5080 0.2634 -0.8201 -vn 0.3837 -0.6398 -0.6659 -vn 0.1677 -0.9310 -0.3243 -vn 0.1689 -0.9620 -0.2144 -vn -0.2897 -0.8387 0.4611 -vn -0.5056 -0.2486 0.8262 -vn -0.6707 -0.1914 0.7166 -vn -0.2852 0.4381 0.8525 -vn -0.4961 0.1940 0.8463 -vn -0.1879 0.7634 0.6179 -vn 0.1668 0.8979 -0.4073 -vn 0.2831 0.5742 -0.7682 -vn 0.2663 -0.5375 -0.8001 -vn 0.5028 -0.1880 -0.8437 -vn 0.1373 -0.8744 -0.4654 -vn -0.0167 -0.9991 -0.0402 -vn -0.0648 -0.9972 0.0363 -vn -0.1639 -0.9012 0.4013 -vn -0.2817 -0.5844 0.7610 -vn -0.4356 -0.5768 0.6910 -vn -0.1184 0.1705 0.9782 -vn -0.3213 0.1120 0.9403 -vn -0.0841 0.6391 0.7645 -vn -0.0227 0.9923 0.1218 -vn -0.0357 0.9277 0.3716 -vn 0.1093 -0.4241 -0.8990 -vn 0.3233 -0.1282 -0.9376 -vn 0.0628 -0.7934 -0.6055 -vn -0.0082 -0.9763 -0.2161 -vn -0.0623 -0.9809 0.1841 -vn -0.1063 -0.7637 0.6368 -vn -0.1336 -0.3328 0.9335 -vn -0.3273 -0.2162 0.9198 -vn 0.0662 -0.0896 0.9938 -vn 0.0702 0.4313 0.8995 -vn 0.0460 0.7276 0.6844 -vn 0.0934 0.8886 -0.4491 -vn -0.0038 0.9391 -0.3435 -vn 0.0321 0.9988 -0.0381 -vn 0.1279 0.5740 -0.8088 -vn -0.0356 0.6747 -0.7372 -vn 0.1383 0.2311 -0.9630 -vn -0.0556 0.3549 -0.9333 -vn 0.1298 -0.0588 -0.9898 -vn -0.0657 0.0888 -0.9939 -vn -0.0672 -0.1787 -0.9816 -vn -0.0639 -0.5295 -0.8459 -vn -0.0475 -0.8654 -0.4989 -vn -0.0003 -0.9510 0.3091 -vn 0.0314 -0.6870 0.7259 -vn 0.2353 0.2556 0.9377 -vn 0.0683 0.1638 0.9841 -vn 0.1785 0.6421 0.7455 -vn -0.0323 0.9991 -0.0277 -vn 0.0511 0.9195 0.3897 -vn -0.1563 0.8405 -0.5188 -vn -0.2476 0.2595 -0.9334 -vn -0.2504 -0.0405 -0.9673 -vn -0.2242 -0.3937 -0.8915 -vn -0.1398 -0.7778 -0.6128 -vn -0.0238 -0.9834 -0.1798 -vn 0.0146 0.9349 0.3547 -vn -0.2079 0.9698 -0.1272 -vn -0.3576 0.7568 -0.5472 -vn -0.2202 0.5594 -0.7991 -vn -0.4142 0.4633 -0.7835 -vn 0.0024 -0.9580 -0.2866 -vn -0.1284 0.9100 0.3942 -vn 0.1043 0.7498 0.6534 -vn 0.2427 -0.9446 0.2210 -vn 0.3456 -0.9376 -0.0381 -vn 0.3855 -0.6627 0.6420 -vn 0.5246 -0.7624 0.3788 -vn 0.4205 -0.3298 0.8452 -vn 0.5853 -0.3864 0.7128 -vn 0.4194 0.2681 0.8673 -vn 0.5108 0.1118 0.8524 -vn 0.5298 -0.0410 0.8471 -vn -0.1330 0.6982 0.7034 -vn -0.4784 0.8014 0.3589 -vn -0.3793 0.9249 -0.0277 -vn -0.7087 0.6992 -0.0940 -vn -0.5440 0.7130 -0.4425 -vn -0.7506 0.4260 -0.5051 -vn -0.5814 0.4241 -0.6944 -vn -0.2002 -0.6290 -0.7511 -vn -0.2905 -0.3761 -0.8799 -vn -0.3915 -0.3149 -0.8646 -vn 0.0876 -0.8859 -0.4556 -vn 0.0815 -0.6805 -0.7282 -vn 0.4649 -0.8099 -0.3577 -vn 0.7106 -0.6932 0.1206 -vn 0.7464 -0.3952 0.5355 -vn 0.6414 -0.1070 0.7597 -vn 0.5750 0.0279 0.8177 -vn 0.3992 0.1591 0.9029 -vn 0.3527 0.3151 0.8811 -vn 0.1637 0.2955 0.9412 -vn 0.1622 0.5026 0.8492 -vn -0.1797 0.4443 0.8777 -vn -0.5810 0.5417 0.6074 -vn -0.8502 0.5004 0.1636 -vn -0.8936 0.3347 -0.2992 -vn -0.7714 0.1471 -0.6192 -vn -0.6541 0.1369 -0.7439 -vn -0.6290 -0.0032 -0.7774 -vn -0.5192 -0.0961 -0.8493 -vn -0.4080 -0.1684 -0.8973 -vn 0.1365 -0.4287 -0.8931 -vn 0.5726 -0.5382 -0.6184 -vn 0.8582 -0.4924 -0.1452 -vn 0.8864 -0.3164 0.3380 -vn 0.7548 -0.1468 0.6393 -vn -0.3867 -0.0941 -0.9174 -vn 0.4647 -0.2777 -0.8408 -vn 0.8322 -0.2896 -0.4729 -vn 0.9746 -0.2193 0.0460 -vn 0.8593 -0.0857 0.5043 -vn -0.5028 0.0961 0.8591 -vn -0.2407 0.2528 0.9371 -vn -0.6669 0.3008 0.6818 -vn -0.8603 0.0732 0.5045 -vn -0.9349 0.2696 0.2308 -vn -0.9996 0.0291 0.0003 -vn -0.9512 0.1717 -0.2563 -vn -0.8756 -0.0201 -0.4827 -vn -0.7907 0.0560 -0.6097 -vn 0.0555 0.0256 0.9981 -vn 0.1342 0.0566 0.9893 -vn -0.1336 0.0276 0.9906 -vn -0.3479 -0.1142 0.9306 -vn -0.4991 -0.0211 0.8663 -vn -0.7409 -0.2457 0.6251 -vn -0.8548 -0.0785 0.5129 -vn -0.9380 -0.3158 0.1432 -vn -0.9932 -0.1165 0.0081 -vn -0.8871 -0.3030 -0.3482 -vn -0.8715 -0.1215 -0.4751 -vn -0.6841 -0.2370 -0.6899 -vn -0.6211 -0.1052 -0.7766 -vn -0.4264 -0.1487 -0.8922 -vn -0.3814 -0.0814 -0.9208 -vn 0.3499 0.1254 0.9284 -vn 0.4118 0.2598 0.8734 -vn 0.5858 0.2033 0.7845 -vn 0.2244 0.1316 0.9656 -vn -0.2330 -0.1522 -0.9605 -vn -0.1545 -0.0567 -0.9864 -vn 0.1031 0.0306 -0.9942 -vn -0.0168 -0.0156 -0.9997 -vn 0.4490 0.1457 -0.8816 -vn 0.2896 0.1806 -0.9399 -vn 0.8062 0.2663 -0.5284 -vn 0.6459 0.4025 -0.6487 -vn 0.9485 0.3167 -0.0079 -vn 0.8386 0.5236 -0.1506 -vn 0.8265 0.2814 0.4876 -vn 0.7899 0.4954 0.3615 -vn 0.6109 0.3875 0.6904 -vn 0.3747 0.4648 0.8022 -vn -0.4056 -0.4887 -0.7724 -vn -0.6177 -0.4091 -0.6716 -vn -0.4311 -0.2782 -0.8584 -vn -0.0173 -0.0191 0.9997 -vn 0.0792 0.2590 0.9626 -vn 0.1783 0.2105 0.9612 -vn -0.0828 -0.2361 0.9682 -vn -0.2463 -0.2864 0.9259 -vn -0.2132 -0.5690 0.7942 -vn -0.5034 -0.5988 0.6230 -vn -0.3233 -0.8592 0.3965 -vn -0.6339 -0.7598 0.1443 -vn -0.3504 -0.9297 -0.1136 -vn -0.5966 -0.7136 -0.3673 -vn -0.2865 -0.7643 -0.5778 -vn 0.4689 0.5967 -0.6512 -vn 0.2020 0.5370 -0.8190 -vn 0.2122 0.2661 -0.9403 -vn 0.6149 0.7738 -0.1519 -vn 0.3230 0.8511 -0.4139 -vn 0.5790 0.7201 0.3824 -vn 0.3526 0.9276 0.1236 -vn 0.2859 0.7410 0.6075 -vn 0.1780 0.4736 0.8626 -vn 0.0349 0.4954 0.8680 -vn -0.0132 0.1280 0.9917 -vn 0.0141 -0.1473 0.9890 -vn -0.0274 -0.4874 0.8728 -vn -0.0451 -0.8547 0.5172 -vn -0.0525 -0.9985 0.0183 -vn -0.0467 -0.8753 -0.4812 -vn -0.0264 -0.5247 -0.8509 -vn -0.1894 -0.4951 -0.8479 -vn -0.1024 -0.2553 -0.9614 -vn -0.0038 -0.1659 -0.9861 -vn -0.0218 -0.0320 -0.9993 -vn 0.0741 0.2478 -0.9660 -vn -0.0131 0.1056 -0.9943 -vn 0.0295 0.4603 -0.8873 -vn 0.0374 0.8383 -0.5440 -vn 0.0455 0.9987 -0.0225 -vn 0.0396 0.8646 0.5009 -vn -0.1274 0.3996 0.9078 -vn 0.1067 -0.3644 0.9251 -vn 0.1865 -0.6792 0.7099 -vn 0.2530 -0.9268 0.2777 -vn 0.2555 -0.9371 -0.2377 -vn 0.1979 -0.7064 -0.6795 -vn -0.1719 0.6551 -0.7358 -vn -0.2412 0.9269 -0.2876 -vn -0.2425 0.9366 0.2529 -vn -0.1726 0.6859 0.7069 -vn -0.1830 0.3363 0.9238 -vn 0.0037 -0.0093 0.9999 -vn 0.1641 -0.2749 0.9474 -vn 0.1499 -0.2579 -0.9545 -vn 0.1143 -0.3993 -0.9097 -vn 0.0222 -0.1174 -0.9928 -vn -0.1097 0.3516 -0.9297 -vn -0.1330 0.2098 -0.9687 -vn -0.2922 0.4829 -0.8255 -vn -0.4731 0.7729 -0.4229 -vn -0.5136 0.8511 0.1092 -vn -0.4052 0.6783 0.6129 -vn -0.3030 0.2578 0.9175 -vn 0.3456 -0.3272 0.8795 -vn 0.3558 -0.5978 0.7183 -vn 0.6730 -0.6301 0.3873 -vn 0.4925 -0.8240 0.2800 -vn 0.7299 -0.6752 -0.1066 -vn 0.4990 -0.8336 -0.2369 -vn 0.5951 -0.5443 -0.5913 -vn 0.3686 -0.6201 -0.6925 -vn 0.3085 -0.2796 -0.9092 -vn 0.0410 -0.0201 -0.9990 -vn -0.2102 0.1273 -0.9693 -vn -0.1664 0.1604 -0.9729 -vn -0.2260 0.0941 0.9696 -vn -0.4915 0.1379 0.8599 -vn -0.4737 0.2768 0.8361 -vn -0.8111 0.1685 0.5601 -vn -0.7632 0.2908 0.5771 -vn 0.2091 -0.0970 0.9731 -vn 0.1109 -0.0560 0.9923 -vn 0.0037 -0.0149 0.9999 -vn 0.3877 -0.1146 0.9146 -vn 0.4282 -0.1855 0.8844 -vn -0.0884 -0.0301 0.9956 -vn -0.2382 0.0391 0.9704 -vn -0.2249 0.0664 -0.9721 -vn -0.0008 0.0325 -0.9995 -vn -0.1698 0.0785 -0.9824 -vn -0.2706 0.1161 -0.9557 -vn -0.5270 0.0358 0.8491 -vn -0.8190 0.0559 0.5711 -vn -0.8177 0.1117 0.5646 -vn 0.9400 -0.1457 0.3085 -vn 0.8352 -0.1283 0.5348 -vn 0.9547 -0.1388 0.2631 -vn 0.4519 -0.0352 -0.8914 -vn 0.5230 -0.0379 -0.8515 -vn 0.2005 0.0115 -0.9796 -vn -0.0658 0.0543 -0.9964 -vn -0.6141 0.1056 -0.7821 -vn -0.4465 0.0804 -0.8912 -vn -0.4248 0.0921 -0.9006 -vn -0.7944 0.1332 -0.5926 -vn -0.6763 0.1282 -0.7254 -vn -0.9411 0.1324 -0.3111 -vn -0.9099 0.1038 -0.4015 -vn -0.2895 0.0029 0.9572 -vn -0.4784 0.0364 0.8774 -vn -0.1595 -0.0162 0.9871 -vn 0.0928 -0.0549 0.9942 -vn 0.7912 -0.1548 0.5916 -vn 0.6604 -0.1151 0.7420 -vn -0.2281 0.0709 -0.9710 -vn 0.9797 -0.1780 -0.0917 -vn 0.9925 -0.0871 -0.0858 -vn -0.4466 0.1231 -0.8862 -vn -0.9847 0.1460 0.0954 -vn -0.8998 0.1836 -0.3958 -vn -0.2683 0.0881 0.9593 -vn -0.0307 -0.0237 0.9992 -vn 0.2792 -0.0861 0.9564 -vn 0.2223 -0.1001 0.9698 -vn 0.8728 -0.2775 0.4016 -vn 0.7849 -0.2146 -0.5812 -vn 0.8839 -0.1110 -0.4542 -vn 0.7087 -0.0814 -0.7008 -vn 0.2563 -0.0659 -0.9643 -vn 0.1638 0.0134 -0.9864 -vn -0.2199 0.1113 -0.9691 -vn -0.4630 0.2549 0.8489 -vn -0.5167 0.1237 0.8472 -vn 0.2385 -0.2900 0.9268 -vn 0.1791 -0.1551 0.9715 -vn 0.3806 -0.2667 0.8854 -vn 0.3748 -0.4537 0.8085 -vn 0.5805 -0.3731 0.7237 -vn 0.5482 -0.6490 0.5275 -vn 0.7997 -0.4622 0.3832 -vn 0.6514 -0.7586 0.0159 -vn 0.8601 -0.4948 -0.1239 -vn 0.5179 -0.6895 -0.5064 -vn 0.6901 -0.3916 -0.6086 -vn 0.4009 -0.4660 -0.7888 -vn 0.3745 -0.2625 -0.8893 -vn 0.0826 -0.0447 -0.9956 -vn -0.0245 0.0607 -0.9979 -vn -0.1587 0.2260 -0.9611 -vn -0.2022 0.1537 -0.9672 -vn -0.5943 0.3974 -0.6993 -vn -0.4000 0.4666 -0.7889 -vn -0.3964 0.2681 -0.8781 -vn -0.3137 0.3443 0.8849 -vn -0.3424 0.6298 0.6972 -vn -0.5721 0.5689 0.5908 -vn 0.0899 -0.3917 0.9157 -vn 0.0669 -0.1710 0.9830 -vn 0.2594 -0.5587 -0.7877 -vn 0.0039 0.1766 -0.9843 -vn -0.1432 0.3743 -0.9162 -vn -0.6217 0.6778 -0.3925 -vn -0.2942 0.6573 -0.6939 -vn -0.6860 0.7175 0.1206 -vn -0.4273 0.8680 -0.2528 -vn -0.4441 0.8541 0.2705 -vn -0.1674 0.1488 0.9746 -vn -0.1652 0.3220 0.9322 -vn -0.0547 -0.1994 0.9784 -vn -0.0545 -0.0282 0.9981 -vn 0.2028 -0.8609 -0.4667 -vn 0.3397 -0.9395 -0.0435 -vn -0.0639 0.5204 -0.8516 -vn -0.1811 0.8540 -0.4877 -vn -0.2545 0.9671 -0.0035 -vn -0.1624 0.1164 0.9798 -vn -0.0456 -0.5262 0.8491 -vn 0.1759 -0.5828 0.7934 -vn 0.0550 -0.8807 0.4705 -vn 0.2489 -0.7367 0.6287 -vn 0.2947 -0.8966 0.3304 -vn 0.1851 -0.3300 -0.9256 -vn 0.1822 -0.2926 -0.9387 -vn 0.0952 0.3103 -0.9459 -vn 0.0237 0.7268 -0.6864 -vn -0.0681 0.9638 -0.2576 -vn -0.2182 0.9022 0.3721 -vn -0.1020 0.9845 0.1427 -vn -0.2148 0.4844 0.8480 -vn -0.2051 0.5465 0.8119 -vn -0.1939 -0.2131 0.9576 -vn -0.1288 -0.6441 0.7540 -vn -0.0693 -0.9403 0.3332 -vn 0.0905 -0.9935 -0.0692 -vn 0.1580 -0.8456 -0.5099 -vn 0.2162 -0.0473 -0.9752 -vn 0.1336 -0.0575 -0.9894 -vn 0.0120 0.9752 -0.2209 -vn -0.1897 0.7130 0.6750 -vn -0.2521 0.7688 0.5877 -vn -0.2538 0.1886 0.9487 -vn -0.2420 -0.4245 0.8725 -vn -0.1535 -0.8096 0.5665 -vn 0.2676 -0.1788 -0.9468 -vn 0.2140 -0.5505 -0.8069 -vn 0.1252 0.8620 -0.4912 -vn 0.1082 0.7325 -0.6721 -vn -0.0157 -0.9920 0.1255 -vn 0.2437 -0.6359 -0.7323 -vn 0.2068 0.6253 -0.7525 -vn 0.2813 0.2942 -0.9134 -vn 0.2412 0.2932 -0.9251 -vn -0.1464 0.8823 0.4473 -vn -0.0398 0.9968 0.0688 -vn -0.2493 0.6362 0.7301 -vn -0.2993 0.2274 0.9267 -vn -0.2873 0.4448 0.8483 -vn -0.2449 -0.5445 0.8022 -vn -0.1978 -0.6444 0.7387 -vn -0.1945 -0.7250 0.6607 -vn -0.1193 -0.8885 0.4431 -vn 0.1900 -0.8329 -0.5197 -vn 0.2899 -0.4428 -0.8485 -vn 0.2922 -0.1934 -0.9366 -vn 0.2507 0.5432 -0.8013 -vn 0.1971 0.7351 -0.6487 -vn 0.0941 0.9360 -0.3393 -vn -0.1616 0.8813 0.4441 -vn -0.4942 0.1359 0.8587 -vn -0.5582 0.1484 0.8163 -vn -0.4909 0.0010 0.8712 -vn -0.4935 0.2870 0.8210 -vn -0.4210 0.6423 0.6405 -vn -0.5397 0.5523 0.6354 -vn -0.4710 0.4470 0.7605 -vn -0.1641 0.9747 0.1518 -vn -0.3971 0.8724 0.2850 -vn -0.3405 0.8156 0.4678 -vn 0.0983 0.9466 -0.3070 -vn -0.1449 0.9781 -0.1495 -vn 0.2749 0.7409 -0.6128 -vn 0.1433 0.8305 -0.5382 -vn 0.4204 0.4038 -0.8126 -vn 0.3743 0.5117 -0.7733 -vn 0.3612 0.5663 -0.7408 -vn 0.4785 0.1388 -0.8671 -vn 0.5203 0.0751 -0.8507 -vn 0.4518 0.2692 -0.8505 -vn 0.4932 0.0021 -0.8699 -vn 0.4916 -0.1342 -0.8604 -vn 0.5526 -0.4127 -0.7241 -vn 0.4881 -0.2581 -0.8338 -vn 0.4813 -0.3950 -0.7825 -vn 0.4430 -0.5600 -0.7001 -vn 0.4341 -0.8064 -0.4015 -vn 0.3804 -0.7371 -0.5585 -vn 0.2380 -0.9410 -0.2406 -vn 0.1903 -0.9809 0.0410 -vn -0.0239 -0.9729 0.2300 -vn -0.0996 -0.8772 0.4698 -vn -0.2256 -0.8114 0.5392 -vn -0.3444 -0.5590 0.7543 -vn -0.3319 -0.6317 0.7006 -vn -0.4053 -0.4451 0.7985 -vn -0.4912 -0.2058 0.8464 -vn -0.4500 -0.2866 0.8458 -vn -0.4775 -0.1301 0.8690 -vn -0.7529 0.3661 0.5469 -vn -0.6998 0.6890 0.1886 -vn -0.4686 0.8530 -0.2299 -vn -0.1308 0.8050 -0.5786 -vn 0.1760 0.6178 -0.7664 -vn 0.4392 0.3515 -0.8268 -vn 0.0508 -0.7641 0.6431 -vn -0.4991 -0.2650 0.8250 -vn -0.6594 0.0263 0.7514 -vn -0.5601 -0.2352 0.7944 -vn -0.8517 0.0591 0.5207 -vn -0.9320 0.3530 0.0821 -vn -0.7613 0.5357 -0.3652 -vn -0.4123 0.5788 -0.7036 -vn -0.0538 0.5090 -0.8591 -vn 0.3009 0.3742 -0.8772 -vn 0.6875 0.1221 -0.7159 -vn 0.6465 0.0191 -0.7626 -vn 0.7526 -0.3178 -0.5768 -vn 0.9197 -0.1881 -0.3448 -vn 0.7082 -0.6755 -0.2051 -vn 0.8863 -0.4410 0.1415 -vn 0.4504 -0.8561 0.2536 -vn 0.5926 -0.5604 0.5786 -vn -0.2857 -0.5296 0.7987 -vn -0.1254 -0.4412 0.8886 -vn -0.9390 -0.0150 0.3437 -vn -0.9002 0.2040 -0.3848 -vn -0.6806 0.2700 -0.6811 -vn -0.2207 0.2957 -0.9294 -vn 0.0402 0.2805 -0.9590 -vn 0.4044 0.2252 -0.8864 -vn 0.9517 -0.0030 -0.3069 -vn 0.9820 -0.1289 0.1382 -vn 0.7680 -0.2338 0.5962 -vn 0.4618 -0.2801 0.8416 -vn 0.2540 -0.5545 0.7925 -vn 0.2131 -0.2841 0.9348 -vn -0.3886 -0.2096 0.8973 -vn -0.5358 -0.1162 0.8363 -vn -0.8633 -0.0593 0.5012 -vn -0.9951 0.0139 -0.0976 -vn -0.9958 0.0914 -0.0012 -vn -0.7688 0.0971 -0.6321 -vn -0.4545 0.2950 -0.8405 -vn -0.5664 0.1201 -0.8154 -vn -0.3403 0.1327 -0.9309 -vn -0.0881 0.1412 -0.9861 -vn 0.2816 0.1341 -0.9501 -vn 0.7105 0.0989 -0.6967 -vn 0.9605 -0.0374 0.2757 -vn 0.5693 -0.1184 0.8136 -vn 0.3427 -0.1307 0.9303 -vn -0.0532 -0.2731 0.9605 -vn 0.1061 -0.1400 0.9845 -vn -0.1652 -0.1377 0.9766 -vn -0.6473 -0.0540 0.7603 -vn -0.9214 -0.0059 0.3887 -vn -0.8480 0.0145 -0.5298 -vn -0.9203 0.0478 -0.3882 -vn -0.6656 0.0445 -0.7450 -vn -0.4463 0.0478 -0.8936 -vn -0.2243 0.0526 -0.9731 -vn 0.0101 0.0540 -0.9985 -vn 0.6070 0.0462 -0.7933 -vn 0.8734 0.0122 -0.4869 -vn 0.9471 0.0321 -0.3195 -vn 0.9948 -0.0044 -0.1021 -vn 0.0373 -0.0575 0.9977 -vn -0.2932 -0.0569 0.9544 -vn -0.9376 -0.0077 -0.3476 -vn -0.6840 -0.0012 -0.7295 -vn 0.6302 -0.0058 -0.7764 -vn 0.7820 -0.0170 0.6231 -vn 0.9592 0.0083 0.2827 -vn 0.9266 -0.0281 0.3751 -vn -0.8200 0.0351 0.5713 -vn -0.6277 0.0129 0.7783 -vn -0.9724 0.0161 0.2329 -vn -0.7610 -0.0340 -0.6479 -vn -0.5715 -0.0280 -0.8201 -vn -0.4549 0.0030 -0.8905 -vn -0.3406 -0.0335 -0.9396 -vn -0.2243 0.0050 -0.9745 -vn 0.0191 0.0021 -0.9998 -vn -0.0891 -0.0394 -0.9952 -vn 0.2937 0.0021 -0.9559 -vn 0.2671 -0.0373 -0.9630 -vn 0.8865 -0.0300 -0.4617 -vn 0.8119 0.0333 0.5828 -vn 0.5374 0.0065 0.8433 -vn 0.6452 0.0197 0.7637 -vn 0.3338 -0.0031 0.9427 -vn 0.3955 0.0308 0.9179 -vn -0.1641 0.0360 0.9858 -vn 0.0503 0.0007 0.9987 -vn -0.2796 0.0024 0.9601 -vn -0.5030 0.0284 0.8638 -vn -0.5928 -0.0584 -0.8033 -vn 0.6151 -0.0414 -0.7874 -vn 0.5002 -0.0777 -0.8624 -vn 0.6159 0.0559 0.7859 -vn -0.9540 0.0681 0.2921 -vn -0.9982 0.0474 -0.0355 -vn -0.9430 0.0111 -0.3326 -vn -0.7697 -0.0313 -0.6377 -vn -0.5238 -0.0714 -0.8488 -vn -0.3412 -0.0581 -0.9382 -vn -0.0833 -0.0628 -0.9945 -vn -0.2362 -0.0876 -0.9677 -vn 0.1855 -0.0761 -0.9797 -vn 0.0330 -0.0994 -0.9945 -vn 0.8027 -0.0697 -0.5923 -vn 0.9717 -0.0661 -0.2269 -vn 0.9996 -0.0085 -0.0272 -vn 0.8474 0.0188 0.5306 -vn 0.0563 0.0930 0.9941 -vn 0.1053 0.0630 0.9924 -vn -0.2028 0.0808 0.9759 -vn -0.7831 0.1101 0.6121 -vn -0.6786 0.0577 0.7322 -vn -0.4850 -0.0528 -0.8729 -vn -0.0282 -0.1156 -0.9929 -vn 0.4046 -0.1285 -0.9054 -vn 0.7936 -0.1199 -0.5966 -vn 0.5936 0.0666 0.8020 -vn 0.2372 0.1004 0.9662 -vn 0.3327 0.0832 0.9393 -vn -0.4052 0.1269 0.9054 -vn -0.4554 0.0743 0.8872 -vn -0.9839 0.1622 0.0750 -vn -0.9029 0.0791 -0.4225 -vn -0.6763 0.0086 -0.7366 -vn 0.9895 -0.0370 0.1400 -vn 0.9812 -0.1785 -0.0733 -vn 0.9048 -0.0978 0.4145 -vn 0.6819 -0.0126 0.7314 -vn -0.0067 0.1558 0.9878 -vn -0.0787 0.1173 0.9900 -vn -0.3648 -0.0036 -0.9311 -vn -0.3002 -0.0814 -0.9504 -vn -0.0303 -0.1469 -0.9887 -vn -0.3752 0.3022 0.8763 -vn -0.4067 0.1849 0.8947 -vn -0.1816 -0.0359 -0.9827 -vn 0.0291 0.1641 0.9860 -vn 0.1487 -0.0294 0.9884 -vn 0.1955 0.0232 0.9804 -vn -0.3750 0.7946 0.4774 -vn -0.1355 0.8133 0.5659 -vn -0.1648 0.5341 0.8292 -vn -0.4854 0.8727 -0.0523 -vn -0.2216 0.9737 0.0533 -vn -0.4565 0.7041 -0.5439 -vn -0.2443 0.8612 -0.4457 -vn -0.3523 0.4456 -0.8230 -vn -0.2193 0.6166 -0.7561 -vn -0.2431 0.2195 -0.9448 -vn -0.1790 0.3771 -0.9087 -vn -0.1315 0.0064 -0.9913 -vn -0.1344 0.1291 -0.9825 -vn -0.0619 -0.1746 -0.9827 -vn -0.0002 -0.2255 -0.9742 -vn 0.1521 -0.2176 0.9641 -vn 0.2593 -0.2426 0.9348 -vn 0.0794 0.0855 0.9932 -vn -0.0181 0.4398 0.8979 -vn -0.1157 -0.4796 -0.8698 -vn 0.0517 -0.5699 -0.8201 -vn -0.0766 -0.8464 -0.5270 -vn 0.1666 -0.8871 -0.4305 -vn -0.0152 -0.9994 -0.0296 -vn 0.2381 -0.9681 0.0785 -vn 0.0478 -0.8792 0.4741 -vn 0.2450 -0.8007 0.5467 -vn 0.0979 -0.5307 0.8419 -vn 0.2064 -0.5159 0.8314 -vn -0.1007 0.3633 -0.9262 -vn 0.0233 0.6659 -0.7456 -vn -0.0992 0.6335 -0.7673 -vn -0.0460 0.4704 -0.8812 -vn 0.7928 0.5810 0.1840 -vn 0.5497 0.7248 0.4152 -vn 0.4584 0.8829 -0.1017 -vn 0.6161 0.7222 -0.3146 -vn 0.2327 0.7815 -0.5789 -vn 0.2798 0.6573 -0.6998 -vn -0.2402 0.3307 -0.9127 -vn -0.2330 0.1762 -0.9564 -vn -0.3550 -0.0508 -0.9335 -vn -0.4173 0.1633 -0.8940 -vn -0.4781 -0.3668 -0.7980 -vn -0.5853 -0.0238 -0.8105 -vn -0.5448 -0.7101 -0.4461 -vn -0.7516 -0.2919 -0.5915 -vn -0.4696 -0.8820 0.0390 -vn -0.8007 -0.5705 -0.1826 -vn -0.2657 -0.8169 0.5119 -vn -0.6431 -0.7092 0.2890 -vn 0.0055 -0.5379 0.8430 -vn -0.3284 -0.6644 0.6714 -vn 0.2166 -0.2151 0.9523 -vn 0.2119 -0.3481 0.9132 -vn 0.3957 -0.1784 0.9009 -vn 0.3522 0.0382 0.9352 -vn 0.5651 0.0055 0.8250 -vn 0.4842 0.3666 0.7945 -vn 0.7376 0.2787 0.6150 -vn -0.1313 0.4169 -0.8994 -vn -0.0115 0.4993 -0.8663 -vn 0.9753 0.0479 0.2158 -vn 0.8720 0.0578 0.4860 -vn 0.9484 0.3144 0.0407 -vn 0.9249 0.2607 -0.2767 -vn 0.7552 0.4933 -0.4316 -vn 0.6094 0.4000 -0.6846 -vn 0.3478 0.5257 -0.7763 -vn 0.1463 0.4258 -0.8929 -vn -0.4768 0.2639 -0.8384 -vn -0.2282 0.3719 -0.8998 -vn -0.9375 -0.3443 -0.0509 -vn -0.9838 -0.0586 -0.1692 -vn -0.8729 -0.0904 -0.4795 -vn -0.7571 -0.5130 0.4044 -vn -0.9259 -0.2474 0.2853 -vn -0.3925 -0.5463 0.7400 -vn -0.6211 -0.3814 0.6847 -vn -0.0257 -0.4736 0.8804 -vn -0.1662 -0.4152 0.8944 -vn 0.7899 -0.1608 0.5917 -vn 0.4394 -0.2863 0.8515 -vn 0.6510 -0.1423 0.7457 -vn -0.6819 0.2137 -0.6996 -vn -0.5642 0.2620 -0.7830 -vn -0.8598 0.1149 -0.4976 -vn 0.5480 -0.2691 0.7920 -vn 0.9814 -0.0469 0.1862 -vn 0.9933 0.0091 -0.1148 -vn 0.9383 0.1319 -0.3195 -vn 0.8617 0.1292 -0.4906 -vn 0.6233 0.2684 -0.7345 -vn 0.5372 0.2081 -0.8174 -vn 0.1589 0.3289 -0.9309 -vn -0.1107 0.2535 -0.9610 -vn -0.2372 0.3116 -0.9201 -vn -0.3542 0.2382 -0.9043 -vn -0.8018 0.1547 -0.5772 -vn -0.9886 0.0231 -0.1486 -vn -0.9331 -0.1456 0.3288 -vn -0.9966 0.0050 0.0828 -vn -0.6234 -0.2808 0.7297 -vn -0.8667 -0.1129 0.4858 -vn -0.1677 -0.3372 0.9264 -vn -0.5333 -0.2025 0.8213 -vn 0.2295 -0.3194 0.9194 -vn 0.1150 -0.2495 0.9615 -vn 0.4205 -0.2270 0.8784 -vn 0.7686 -0.1688 0.6171 -vn 0.8554 0.0604 -0.5145 -vn -0.5922 0.2094 -0.7781 -vn -0.4092 0.1586 -0.8985 -vn -0.6799 0.1367 -0.7204 -vn -0.9071 0.0863 -0.4120 -vn -0.7356 0.0848 -0.6721 -vn -0.9579 0.0420 -0.2840 -vn 0.5884 -0.0760 0.8050 -vn 0.4199 -0.1577 0.8938 -vn 0.7550 -0.1295 0.6428 -vn 0.8387 -0.0673 0.5404 -vn 0.9654 -0.0682 0.2516 -vn 0.8592 -0.0691 -0.5069 -vn 0.9926 -0.0423 -0.1139 -vn 0.8440 0.0242 -0.5358 -vn -0.7394 0.0002 -0.6733 -vn -0.8736 -0.0197 0.4862 -vn -0.9153 0.0592 0.3983 -vn -0.9959 0.0295 0.0860 -vn -0.5285 -0.0520 0.8473 -vn -0.6201 0.0520 0.7828 -vn -0.1638 -0.0711 0.9839 -vn -0.2645 0.0408 0.9635 -vn 0.1179 -0.0788 0.9899 -vn 0.0643 0.0298 0.9975 -vn 0.9816 -0.0386 0.1870 -vn 0.9757 -0.0396 0.2153 -vn 0.9300 -0.1213 -0.3470 -vn 0.5442 -0.0527 -0.8373 -vn 0.6340 -0.1741 -0.7535 -vn 0.1727 -0.0382 -0.9842 -vn 0.2727 -0.1825 -0.9446 -vn -0.1236 -0.0265 -0.9920 -vn -0.0712 -0.1678 -0.9833 -vn -0.6035 -0.0921 -0.7920 -vn -0.4365 -0.0069 -0.8997 -vn -0.8627 -0.0251 -0.5051 -vn -0.9972 0.0717 -0.0221 -vn -0.8634 0.1482 0.4823 -vn -0.5156 0.1869 0.8362 -vn -0.1559 0.1825 0.9708 -vn 0.5795 -0.0037 0.8150 -vn 0.4290 0.1214 0.8951 -vn 0.3431 0.0114 0.9392 -vn 0.8320 -0.0206 0.5544 -vn 0.7657 0.0581 0.6406 -vn 0.1227 -0.3876 -0.9136 -vn -0.3625 -0.1312 -0.9227 -vn -0.2592 -0.3273 -0.9087 -vn -0.5163 -0.2600 -0.8160 -vn -0.8021 -0.1328 -0.5822 -vn -0.9863 0.0657 -0.1514 -vn -0.9083 0.2513 0.3344 -vn -0.5862 0.3672 0.7221 -vn -0.1319 0.3862 0.9129 -vn 0.1300 0.1622 0.9782 -vn 0.2475 0.3351 0.9091 -vn 0.9209 -0.2473 -0.3013 -vn 0.9717 -0.2360 -0.0118 -vn 0.9787 -0.0581 0.1967 -vn 0.5986 -0.3711 -0.7099 -vn 0.7576 -0.4958 -0.4246 -vn 0.3280 -0.6158 -0.7164 -vn -0.1855 -0.5602 -0.8073 -vn -0.6345 -0.3573 -0.6854 -vn -0.9271 -0.0589 -0.3700 -vn -0.9638 0.2636 0.0411 -vn -0.7425 0.5066 0.4382 -vn -0.3268 0.6161 0.7167 -vn 0.1773 0.5610 0.8086 -vn 0.7829 0.1391 0.6064 -vn 0.6199 0.3683 0.6929 -vn 0.5073 0.2671 0.8193 -vn 0.9119 0.0864 0.4012 -vn 0.9558 -0.2519 0.1515 -vn 0.7843 -0.5892 -0.1943 -vn 0.4104 -0.7713 -0.4865 -vn 0.0197 -0.7729 -0.6342 -vn -0.3513 -0.6491 -0.6748 -vn -0.8850 -0.1300 -0.4471 -vn -0.9549 0.2587 -0.1454 -vn -0.7767 0.5952 0.2061 -vn -0.3962 0.7707 0.4991 -vn 0.0725 0.7486 0.6590 -vn 0.4587 0.5895 0.6649 -vn 0.7342 0.3615 0.5747 -vn 0.8814 -0.4238 0.2084 -vn 0.6254 -0.7706 -0.1226 -vn 0.2186 -0.8874 -0.4058 -vn -0.1601 -0.8088 -0.5659 -vn -0.6622 -0.4329 -0.6116 -vn -0.4842 -0.6130 -0.6243 -vn -0.7304 -0.3430 -0.5907 -vn -0.8242 0.5560 -0.1074 -vn -0.5407 0.8177 0.1976 -vn -0.1026 0.8795 0.4647 -vn 0.3540 0.7094 0.6094 -vn 0.9135 0.0637 0.4018 -vn 0.8730 0.0542 0.4847 -vn 0.6522 -0.7335 0.1914 -vn 0.3357 -0.9278 -0.1628 -vn -0.0854 -0.8675 -0.4901 -vn -0.7044 -0.1618 -0.6911 -vn -0.8710 -0.0534 -0.4883 -vn -0.7880 0.1999 -0.5823 -vn -0.9107 0.2364 -0.3388 -vn -0.7491 0.5471 -0.3736 -vn -0.5347 0.8439 -0.0439 -vn -0.1700 0.9342 0.3136 -vn 0.2553 0.7717 0.5825 -vn 0.6014 0.3923 0.6960 -vn 0.6613 0.4389 0.6083 -vn 0.7825 -0.4230 0.4569 -vn 0.5431 -0.4251 0.7241 -vn 0.7781 -0.0612 0.6251 -vn 0.4868 -0.7457 0.4550 -vn 0.3010 -0.9534 0.0183 -vn 0.0296 -0.9009 -0.4331 -vn -0.4645 -0.5774 -0.6715 -vn -0.2328 -0.6098 -0.7576 -vn -0.4082 -0.2468 -0.8789 -vn -0.5303 0.4206 -0.7361 -vn -0.4535 0.7896 -0.4133 -vn -0.2601 0.9650 0.0336 -vn 0.0180 0.8813 0.4722 -vn 0.2867 0.5565 0.7798 -vn -0.0047 -0.9540 0.2999 -vn 0.1599 -0.8292 0.5356 -vn 0.1489 -0.9879 0.0439 -vn 0.1312 -0.9727 -0.1917 -vn 0.0938 -0.8782 -0.4690 -vn 0.2388 -0.7284 -0.6423 -vn 0.0122 -0.5397 -0.8417 -vn 0.2747 -0.3212 -0.9063 -vn -0.0581 -0.1531 -0.9865 -vn 0.2547 0.0547 -0.9655 -vn -0.1051 0.1512 -0.9829 -vn -0.1514 0.4911 -0.8578 -vn 0.2184 0.3725 -0.9020 -vn 0.0971 -0.2047 0.9740 -vn -0.2598 -0.0466 0.9645 -vn 0.0503 0.1284 0.9904 -vn -0.2087 -0.4749 0.8550 -vn 0.1374 -0.5295 0.8371 -vn -0.3213 -0.6743 0.6650 -vn -0.1207 -0.7583 0.6407 -vn 0.4461 -0.2493 -0.8596 -vn 0.4273 0.3072 -0.8503 -vn 0.0480 0.8951 -0.4434 -vn 0.2888 0.7480 -0.5976 -vn 0.1511 0.6558 -0.7397 -vn -0.0825 0.9963 0.0241 -vn 0.0816 0.9756 -0.2040 -vn -0.4297 -0.2890 0.8555 -vn 0.3749 -0.7402 -0.5581 -vn 0.1318 -0.9703 -0.2027 -vn 0.3247 -0.7291 -0.6024 -vn 0.5440 0.2010 -0.8146 -vn 0.4199 0.6613 -0.6216 -vn 0.1839 0.9477 -0.2607 -vn -0.2793 0.8227 0.4951 -vn -0.2839 0.8658 0.4120 -vn -0.0575 0.9952 0.0795 -vn -0.4225 0.4481 0.7879 -vn -0.4885 0.4809 0.7281 -vn -0.5491 -0.1292 0.8257 -vn -0.4601 0.0683 0.8852 -vn -0.2666 -0.8917 0.3657 -vn -0.4217 -0.6596 0.6221 -vn -0.1886 -0.9458 0.2643 -vn 0.0271 -0.9989 -0.0385 -vn 0.2647 -0.8913 -0.3681 -vn 0.4952 -0.5420 -0.6790 -vn 0.5300 -0.3052 -0.7912 -vn -0.1096 0.9793 0.1701 -vn -0.5298 -0.4541 0.7163 -vn 0.6976 0.1117 0.7077 -vn 0.9995 -0.0068 0.0296 -vn 0.9484 0.3169 0.0109 -vn 0.9656 -0.2587 0.0265 -vn 0.6444 -0.2664 0.7167 -vn 0.8138 -0.5806 0.0226 -vn -0.9995 0.0068 -0.0296 -vn -0.6932 0.0630 0.7180 -vn -0.9656 0.2587 -0.0265 -vn -0.6026 0.3396 0.7222 -vn -0.8138 0.5806 -0.0227 -vn 0.0852 0.6840 0.7245 -vn 0.0667 0.2287 0.9712 -vn 0.3406 0.5989 0.7248 -vn 0.6931 -0.0630 -0.7180 -vn 0.6026 -0.3396 -0.7222 -vn -0.5224 -0.4506 0.7239 -vn -0.2126 -0.1079 0.9712 -vn -0.6561 -0.2150 0.7234 -vn 0.2652 -0.0215 0.9640 -vn -0.0499 -0.6875 0.7245 -vn -0.0727 -0.2256 0.9715 -vn -0.3097 -0.6162 0.7242 -vn 0.2296 -0.1310 0.9644 -vn 0.5456 0.4201 0.7251 -vn 0.2099 0.1202 0.9703 -vn 0.3535 -0.0033 0.9354 -vn -0.3524 -0.0032 0.9358 -vn -0.2317 0.0703 0.9702 -vn 0.2377 -0.2555 0.9371 -vn 0.1064 -0.2179 0.9701 -vn -0.2665 0.2351 0.9347 -vn -0.0225 0.3562 0.9341 -vn 0.2394 0.2643 0.9343 -vn -0.0148 -0.3567 0.9341 -vn -0.1156 0.2088 0.9711 -vn -0.2617 -0.2422 0.9343 -vn 0.8138 -0.5806 0.0227 -vn -0.0012 0.2651 0.9642 -vn -0.1745 0.0901 0.9805 -vn 0.0028 -0.2004 0.9797 -vn 0.0010 0.6455 0.7637 -vn -0.1386 0.6263 0.7672 -vn -0.0009 0.9321 0.3621 -vn 0.0006 0.9945 -0.1048 -vn -0.0454 0.9686 0.2442 -vn -0.0011 0.9181 -0.3962 -vn 0.0377 0.9777 -0.2067 -vn -0.3413 -0.1782 0.9229 -vn -0.1690 -0.3299 0.9288 -vn -0.3108 0.4595 0.8320 -vn -0.1383 0.9178 0.3722 -vn 0.0373 0.9944 -0.0989 -vn 0.0774 0.8983 -0.4326 -vn 0.1376 0.9179 -0.3723 -vn 0.1545 0.4686 -0.8698 -vn 0.3037 0.4904 -0.8169 -vn 0.1524 0.5110 -0.8460 -vn 0.1647 0.3225 -0.9321 -vn 0.3294 0.3458 -0.8786 -vn 0.1645 0.3678 -0.9152 -vn 0.1582 0.2769 -0.9478 -vn 0.3446 0.2914 -0.8924 -vn 0.1625 0.3035 -0.9389 -vn 0.2635 0.2696 -0.9262 -vn -0.3256 0.7638 0.5573 -vn -0.0730 0.9897 0.1228 -vn 0.1063 0.9771 -0.1842 -vn 0.3831 0.2741 -0.8821 -vn 0.5520 0.2699 -0.7889 -vn -0.5727 0.4674 0.6735 -vn -0.4823 0.2954 0.8247 -vn -0.2531 0.9219 0.2934 -vn 0.0788 0.9923 -0.0951 -vn 0.6174 0.2931 -0.7300 -vn 0.4852 0.2932 -0.8238 -vn 0.8785 0.2823 -0.3853 -vn 0.7765 0.2693 -0.5697 -vn -0.5878 -0.7325 0.3436 -vn -0.4257 -0.8293 0.3619 -vn -0.5707 -0.6653 0.4813 -vn -0.7571 -0.4813 0.4418 -vn -0.7489 -0.1791 0.6380 -vn -0.6735 0.4700 0.5705 -vn -0.8598 0.0733 0.5054 -vn -0.2937 0.9226 0.2501 -vn -0.6635 0.6401 0.3873 -vn 0.0956 0.9925 -0.0764 -vn -0.1981 0.9735 0.1141 -vn 0.3140 0.9126 -0.2620 -vn 0.2220 0.9662 -0.1308 -vn 0.4131 0.8423 -0.3461 -vn 0.4287 0.8675 -0.2524 -vn 0.4598 0.7998 -0.3859 -vn 0.5010 0.8140 -0.2939 -vn 0.4954 0.7628 -0.4157 -vn 0.5426 0.7773 -0.3183 -vn 0.5335 0.7177 -0.4475 -vn 0.5838 0.7361 -0.3426 -vn 0.5729 0.6639 -0.4806 -vn 0.6281 0.6852 -0.3687 -vn 0.6082 0.6074 -0.5110 -vn 0.6703 0.6293 -0.3933 -vn 0.6387 0.5504 -0.5378 -vn 0.7068 0.5733 -0.4143 -vn 0.6663 0.4906 -0.5616 -vn 0.7388 0.5177 -0.4314 -vn 0.6944 0.4218 -0.5829 -vn 0.7652 0.4636 -0.4467 -vn 0.7154 0.3473 -0.6062 -vn 0.7902 0.4017 -0.4628 -vn 0.8237 0.2931 -0.4854 -vn 0.7306 0.2926 -0.6170 -vn -0.6914 -0.6762 0.2544 -vn -0.9140 -0.2231 0.3389 -vn -0.8952 0.2993 0.3302 -vn -0.5975 0.7711 0.2201 -vn -0.0926 0.9952 0.0304 -vn 0.2968 0.9481 -0.1136 -vn 0.4818 0.8576 -0.1801 -vn 0.5516 0.8084 -0.2057 -vn 0.5959 0.7717 -0.2222 -vn 0.6413 0.7290 -0.2394 -vn 0.6897 0.6768 -0.2576 -vn 0.7348 0.6203 -0.2744 -vn 0.7739 0.5635 -0.2890 -vn 0.8077 0.5065 -0.3019 -vn 0.8369 0.4487 -0.3134 -vn 0.8121 0.3333 -0.4790 -vn 0.8673 0.3795 -0.3221 -vn 0.9164 0.2661 -0.2990 -vn -0.7336 -0.6661 0.1350 -vn -0.6834 -0.7301 -0.0030 -vn -0.5487 -0.8300 0.1000 -vn -0.9613 -0.2124 0.1757 -vn -0.8945 -0.4470 -0.0018 -vn -0.9363 0.3076 0.1692 -vn -0.9650 0.2624 -0.0036 -vn -0.6179 0.7781 0.1130 -vn -0.5318 0.8468 0.0023 -vn -0.0862 0.9961 0.0178 -vn 0.0158 0.9999 0.0009 -vn 0.3143 0.9477 -0.0559 -vn 0.9496 0.3134 -0.0000 -vn 0.9257 0.3341 -0.1772 -vn 0.9376 0.2908 -0.1904 -vn 0.9616 0.2745 0.0009 -vn -0.7564 0.6390 -0.1397 -vn -0.2233 0.9739 -0.0417 -vn 0.2653 0.9630 0.0475 -vn 0.3573 0.9340 0.0023 -vn 0.9380 0.2893 0.1909 -vn 0.4006 0.9043 0.1474 -vn 0.5017 0.8601 0.0921 -vn 0.5067 0.8416 0.1870 -vn 0.5780 0.8092 0.1055 -vn 0.5630 0.7998 0.2081 -vn 0.6245 0.7726 0.1140 -vn 0.6065 0.7628 0.2242 -vn 0.6721 0.7302 0.1229 -vn 0.6532 0.7177 0.2414 -vn 0.7228 0.6783 0.1323 -vn 0.7014 0.6639 0.2593 -vn 0.7701 0.6222 0.1408 -vn 0.7450 0.6073 0.2759 -vn 0.8110 0.5660 0.1480 -vn 0.7828 0.5501 0.2907 -vn 0.8465 0.5100 0.1529 -vn 0.8168 0.4902 0.3041 -vn 0.8780 0.4517 0.1584 -vn 0.8500 0.4213 0.3162 -vn 0.9090 0.3842 0.1614 -vn 0.8778 0.3459 0.3313 -vn 0.9300 0.3267 0.1682 -vn -0.8238 -0.4797 -0.3022 -vn -0.6470 -0.6618 -0.3787 -vn -0.6429 -0.7291 -0.2346 -vn -0.9349 0.0729 -0.3474 -vn -0.8474 -0.1777 -0.5004 -vn -0.7643 0.4652 -0.4466 -vn -0.7236 0.6372 -0.2654 -vn -0.3371 0.9207 -0.1964 -vn -0.2324 0.9691 -0.0824 -vn 0.1058 0.9925 0.0617 -vn 0.1823 0.9809 0.0676 -vn 0.3523 0.9125 0.2080 -vn -0.7580 -0.0949 -0.6453 -vn -0.3854 0.8645 -0.3226 -vn 0.0089 0.9999 0.0076 -vn 0.2633 0.9385 0.2235 -vn 0.3856 0.8635 0.3251 -vn 0.4646 0.8422 0.2736 -vn 0.4442 0.8139 0.3745 -vn 0.5174 0.7998 0.3044 -vn 0.4810 0.7773 0.4055 -vn 0.5574 0.7628 0.3279 -vn 0.5175 0.7360 0.4363 -vn 0.6001 0.7177 0.3533 -vn 0.5569 0.6851 0.4696 -vn 0.6444 0.6639 0.3794 -vn 0.5943 0.6291 0.5011 -vn 0.6848 0.6073 0.4028 -vn 0.6265 0.5731 0.5283 -vn 0.7201 0.5503 0.4227 -vn 0.6535 0.5161 0.5536 -vn 0.7517 0.4904 0.4410 -vn 0.6762 0.4621 0.5738 -vn 0.7809 0.4216 0.4609 -vn 0.6994 0.3989 0.5930 -vn 0.8098 0.3469 0.4731 -vn -0.5045 0.6251 -0.5956 -vn -0.6752 0.4684 -0.5697 -vn -0.1657 0.9665 -0.1961 -vn 0.1170 0.9836 0.1373 -vn 0.2683 0.9099 0.3164 -vn 0.3488 0.8422 0.4112 -vn 0.3880 0.7997 0.4582 -vn 0.4180 0.7627 0.4935 -vn 0.4503 0.7176 0.5313 -vn 0.4836 0.6637 0.5706 -vn 0.5134 0.6072 0.6064 -vn 0.5374 0.5521 0.6375 -vn 0.5676 0.4737 0.6733 -vn 0.5852 0.4218 0.6925 -vn 0.5989 0.3638 0.7134 -vn 0.7201 0.3320 0.6092 -vn -0.6411 0.0939 -0.7617 -vn -0.4921 -0.1979 -0.8477 -vn -0.6099 -0.3295 -0.7207 -vn -0.4876 0.2678 -0.8310 -vn -0.1847 0.9320 -0.3119 -vn 0.0533 0.9945 0.0905 -vn 0.2001 0.9178 0.3428 -vn 0.4788 0.3197 0.8176 -vn 0.6103 0.3268 0.7216 -vn 0.6141 0.3025 0.7289 -vn 0.4825 0.2894 0.8267 -vn 0.6160 0.2770 0.7374 -vn 0.4828 0.2728 0.8321 -vn 0.6331 0.2695 0.7257 -vn 0.3473 0.2679 0.8987 -vn 0.6838 0.2615 0.6812 -vn -0.3459 0.0656 -0.9360 -vn -0.3100 0.4559 -0.8343 -vn -0.3856 0.6448 -0.6600 -vn -0.2183 0.7758 -0.5921 -vn -0.0772 0.9764 -0.2018 -vn 0.0679 0.9807 0.1833 -vn -0.1759 -0.1247 -0.9765 -vn -0.3307 -0.3205 -0.8877 -vn -0.1603 0.4431 -0.8820 -vn -0.0837 0.8832 -0.4615 -vn 0.0108 0.9980 0.0622 -vn 0.0682 0.9209 0.3838 -vn 0.1484 0.9038 0.4013 -vn 0.1698 0.2994 0.9389 -vn 0.3342 0.3194 0.8867 -vn 0.3406 0.2898 0.8945 -vn 0.1690 0.2785 0.9454 -vn 0.3486 0.2732 0.8966 -vn 0.1588 0.2684 0.9501 -vn -0.0002 0.0905 -0.9959 -vn 0.0028 0.6202 -0.7844 -vn 0.0048 0.9704 -0.2414 -vn 0.0029 0.9662 0.2578 -vn 0.0943 0.8439 0.5281 -vn 0.0023 0.8673 0.4978 -vn 0.1055 0.8005 0.5900 -vn 0.0013 0.8139 0.5810 -vn 0.1136 0.7636 0.6357 -vn 0.0013 0.7772 0.6293 -vn 0.1222 0.7187 0.6845 -vn 0.0016 0.7358 0.6771 -vn 0.1314 0.6650 0.7352 -vn 0.0018 0.6849 0.7287 -vn 0.1400 0.6084 0.7811 -vn 0.0016 0.6288 0.7775 -vn 0.1486 0.5534 0.8195 -vn 0.0012 0.5725 0.8199 -vn 0.1517 0.5094 0.8471 -vn 0.0008 0.5158 0.8567 -vn 0.1576 0.4754 0.8655 -vn 0.1616 0.4244 0.8909 -vn 0.0013 0.4630 0.8864 -vn 0.1670 0.3666 0.9153 -vn 0.0005 0.4017 0.9158 -vn 0.1692 0.3299 0.9287 -vn 0.0010 0.3477 0.9376 -vn 0.0016 0.3118 0.9501 -vn 0.0002 0.2866 0.9581 -vn -0.1693 0.3338 0.9273 -vn -0.1665 0.3028 0.9384 -vn 0.3413 -0.1766 -0.9232 -vn 0.1686 -0.3283 -0.9294 -vn 0.1732 0.0926 -0.9805 -vn 0.1364 0.6266 -0.7673 -vn 0.3123 0.4615 -0.8304 -vn 0.0408 0.9712 -0.2347 -vn 0.1403 0.9192 -0.3679 -vn -0.0490 0.9663 0.2527 -vn -0.0404 0.9926 0.1145 -vn -0.0905 0.8674 0.4893 -vn -0.1399 0.9124 0.3846 -vn -0.1047 0.8140 0.5714 -vn -0.1863 0.8423 0.5058 -vn -0.1133 0.7772 0.6190 -vn -0.2079 0.7998 0.5632 -vn -0.1222 0.7359 0.6660 -vn -0.2240 0.7628 0.6066 -vn -0.1316 0.6849 0.7166 -vn -0.2411 0.7177 0.6533 -vn -0.1402 0.6289 0.7647 -vn -0.2591 0.6639 0.7015 -vn -0.1473 0.5726 0.8065 -vn -0.2758 0.6073 0.7451 -vn -0.1518 0.5168 0.8426 -vn -0.2909 0.5503 0.7827 -vn -0.1571 0.4622 0.8727 -vn -0.3043 0.4904 0.8166 -vn -0.1624 0.4001 0.9020 -vn -0.3152 0.4215 0.8503 -vn -0.3304 0.3472 0.8776 -vn -0.1643 0.2754 0.9472 -vn -0.3443 0.2909 0.8926 -vn -0.3727 0.2744 0.8864 -vn -0.4336 0.2679 0.8603 -vn -0.1751 0.2720 0.9462 -vn -0.4865 0.2923 0.8233 -vn 0.4441 -0.7292 -0.5206 -vn 0.2812 -0.8279 -0.4853 -vn 0.3783 -0.6620 -0.6470 -vn 0.5698 -0.4790 -0.6677 -vn 0.4927 -0.1771 -0.8520 -vn 0.4452 0.4658 -0.7647 -vn 0.6427 0.0710 -0.7628 -vn 0.1952 0.9208 -0.3376 -vn 0.4962 0.6377 -0.5891 -vn -0.0643 0.9926 0.1032 -vn 0.1469 0.9738 -0.1734 -vn -0.2085 0.9124 0.3522 -vn -0.1743 0.9627 0.2071 -vn -0.2734 0.8423 0.4645 -vn -0.3279 0.8600 0.3911 -vn -0.3042 0.7997 0.5176 -vn -0.3780 0.8091 0.4500 -vn -0.3278 0.7627 0.5575 -vn -0.4084 0.7725 0.4862 -vn -0.3532 0.7176 0.6003 -vn -0.4395 0.7301 0.5234 -vn -0.3793 0.6638 0.6446 -vn -0.4726 0.6782 0.5628 -vn -0.4028 0.6072 0.6849 -vn -0.5037 0.6220 0.5995 -vn -0.4230 0.5500 0.7201 -vn -0.5308 0.5659 0.6309 -vn -0.4415 0.4901 0.7516 -vn -0.5552 0.5098 0.6572 -vn -0.4601 0.4211 0.7816 -vn -0.5762 0.4514 0.6813 -vn -0.4730 0.3458 0.8104 -vn -0.5984 0.3839 0.7032 -vn 0.7356 0.2610 -0.6252 -vn 0.4075 0.8467 -0.3421 -vn -0.0125 0.9999 0.0104 -vn -0.6732 0.2713 0.6879 -vn -0.7309 0.2915 0.6171 -vn -0.6173 0.2910 0.7309 -vn 0.6631 0.6402 -0.3878 -vn 0.2109 0.9699 -0.1220 -vn -0.2726 0.9341 0.2307 -vn -0.1707 0.9803 0.0998 -vn -0.3956 0.8566 0.3312 -vn -0.3744 0.9010 0.2193 -vn -0.4502 0.8091 0.3777 -vn -0.4737 0.8359 0.2773 -vn -0.4864 0.7726 0.4082 -vn -0.5232 0.7953 0.3062 -vn -0.5236 0.7301 0.4392 -vn -0.5631 0.7578 0.3296 -vn -0.5631 0.6781 0.4723 -vn -0.6065 0.7114 0.3551 -vn -0.5997 0.6220 0.5036 -vn -0.6507 0.6568 0.3810 -vn -0.6310 0.5657 0.5309 -vn -0.6902 0.6001 0.4043 -vn -0.6567 0.5092 0.5563 -vn -0.7247 0.5427 0.4247 -vn -0.6814 0.4494 0.5777 -vn -0.7569 0.4797 0.4439 -vn -0.7054 0.3824 0.5968 -vn -0.7878 0.4070 0.4624 -vn -0.7223 0.3241 0.6110 -vn -0.8116 0.3377 0.4767 -vn -0.8459 0.2825 0.4525 -vn -0.8521 0.2645 0.4517 -vn 0.9021 0.2699 -0.3368 -vn 0.8605 0.0709 -0.5045 -vn 0.4900 0.8530 -0.1796 -vn -0.0204 0.9998 0.0091 -vn -0.3332 0.9344 0.1258 -vn -0.4832 0.8567 0.1807 -vn -0.5505 0.8091 0.2054 -vn -0.5948 0.7726 0.2220 -vn -0.6401 0.7301 0.2391 -vn -0.6885 0.6781 0.2572 -vn -0.7337 0.6218 0.2739 -vn -0.7729 0.5653 0.2882 -vn -0.8077 0.5085 0.2983 -vn -0.8380 0.4493 0.3096 -vn -0.8663 0.3786 0.3257 -vn -0.8865 0.2888 0.3616 -vn -0.8243 0.2916 0.4854 -vn 0.8171 -0.5570 -0.1487 -vn 0.6401 -0.7297 -0.2403 -vn 0.8384 -0.4461 -0.3132 -vn 0.9747 0.1379 -0.1760 -vn 0.5924 0.7982 -0.1093 -vn 0.0374 0.9993 -0.0083 -vn -0.3247 0.9441 0.0567 -vn -0.4967 0.8634 0.0887 -vn -0.5727 0.8133 0.1028 -vn -0.6200 0.7766 0.1113 -vn -0.6671 0.7352 0.1197 -vn -0.7178 0.6842 0.1288 -vn -0.7657 0.6283 0.1377 -vn -0.8070 0.5722 0.1457 -vn -0.8423 0.5166 0.1538 -vn -0.8721 0.4624 0.1600 -vn -0.9024 0.3982 0.1650 -vn -0.8863 0.3286 0.3264 -vn -0.9323 0.3185 0.1712 -vn -0.9503 0.2734 0.1488 -vn 0.7460 -0.6660 -0.0025 -vn 0.6022 -0.7908 -0.1093 -vn 0.9837 -0.1800 0.0027 -vn 0.8820 0.4712 0.0001 -vn 0.3856 0.9227 -0.0004 -vn -0.1223 0.9925 0.0007 -vn -0.4086 0.9127 -0.0004 -vn -0.5389 0.8423 0.0002 -vn -0.6002 0.7998 0.0002 -vn -0.6465 0.7629 0.0002 -vn -0.6963 0.7178 0.0003 -vn -0.7478 0.6639 0.0003 -vn -0.7946 0.6072 0.0002 -vn -0.8352 0.5500 -0.0000 -vn -0.8718 0.4898 -0.0000 -vn -0.9065 0.4223 0.0008 -vn -0.9372 0.3488 -0.0048 -vn 0.6721 -0.7299 0.1242 -vn 0.5580 -0.8298 -0.0007 -vn 0.8797 -0.4475 0.1609 -vn 0.9486 0.2619 0.1777 -vn 0.5279 0.8436 0.0978 -vn 0.0034 1.0000 0.0025 -vn -0.3348 0.9404 -0.0596 -vn -0.5063 0.8576 -0.0901 -vn -0.5784 0.8092 -0.1035 -vn -0.6249 0.7726 -0.1119 -vn -0.6726 0.7301 -0.1203 -vn -0.7233 0.6783 -0.1293 -vn -0.7706 0.6221 -0.1380 -vn -0.8114 0.5660 -0.1458 -vn -0.8464 0.5099 -0.1537 -vn -0.8778 0.4516 -0.1599 -vn -0.9078 0.3844 -0.1677 -vn -0.9398 0.2892 -0.1818 -vn -0.9546 0.2977 -0.0088 -vn -0.9607 0.2772 0.0154 -vn -0.0133 0.9999 -0.0078 -vn 0.6668 0.6348 0.3904 -vn 0.9052 0.2587 0.3371 -vn 0.5030 0.8449 0.1819 -vn 0.2005 0.9727 0.1167 -vn -0.2230 0.9660 -0.1305 -vn -0.3338 0.9340 -0.1271 -vn -0.4293 0.8672 -0.2524 -vn -0.4831 0.8567 -0.1807 -vn -0.5010 0.8140 -0.2941 -vn -0.5506 0.8091 -0.2053 -vn -0.5426 0.7773 -0.3184 -vn -0.5950 0.7725 -0.2219 -vn -0.5838 0.7360 -0.3427 -vn -0.6404 0.7299 -0.2390 -vn -0.6282 0.6851 -0.3688 -vn -0.6888 0.6778 -0.2572 -vn -0.6703 0.6292 -0.3934 -vn -0.7339 0.6215 -0.2742 -vn -0.7069 0.5732 -0.4144 -vn -0.7731 0.5647 -0.2888 -vn -0.7390 0.5175 -0.4314 -vn -0.8068 0.5079 -0.3017 -vn -0.7653 0.4634 -0.4467 -vn -0.8361 0.4504 -0.3132 -vn -0.7905 0.4015 -0.4624 -vn -0.8660 0.3824 -0.3222 -vn -0.8122 0.3332 -0.4789 -vn -0.8860 0.3246 -0.3311 -vn 0.7607 -0.4781 0.4391 -vn 0.5747 -0.6593 0.4847 -vn 0.5936 -0.7283 0.3425 -vn 0.8609 0.0692 0.5040 -vn 0.7499 -0.1783 0.6370 -vn 0.6792 0.4604 0.5716 -vn 0.3039 0.9179 0.2553 -vn -0.0811 0.9945 -0.0670 -vn -0.3042 0.9178 -0.2552 -vn -0.4124 0.8430 -0.3454 -vn -0.4599 0.7997 -0.3860 -vn -0.4954 0.7627 -0.4157 -vn -0.5336 0.7176 -0.4476 -vn -0.5730 0.6638 -0.4807 -vn -0.6084 0.6072 -0.5111 -vn -0.6377 0.5511 -0.5381 -vn -0.6660 0.4898 -0.5627 -vn -0.6934 0.4227 -0.5835 -vn -0.7159 0.3467 -0.6060 -vn 0.6399 0.0901 0.7631 -vn 0.5024 0.6266 0.5958 -vn 0.1607 0.9688 0.1886 -vn -0.1359 0.9776 -0.1609 -vn 0.4916 -0.2004 0.8474 -vn 0.6093 -0.3300 0.7210 -vn 0.4867 0.2655 0.8322 -vn 0.1820 0.9327 0.3114 -vn -0.0554 0.9945 -0.0892 -vn -0.2001 0.9182 -0.3419 -vn -0.2828 0.8981 -0.3367 -vn -0.4802 0.3198 -0.8168 -vn -0.6074 0.3253 -0.7247 -vn -0.6123 0.3032 -0.7302 -vn -0.4822 0.2890 -0.8270 -vn -0.6160 0.2770 -0.7375 -vn -0.4828 0.2728 -0.8322 -vn -0.6331 0.2695 -0.7257 -vn -0.3457 0.2682 -0.8992 -vn -0.6919 0.2631 -0.6723 -vn -0.0384 0.9935 -0.1075 -vn -0.1772 0.8613 -0.4762 -vn -0.2708 0.8427 -0.4653 -vn -0.2036 0.8139 -0.5442 -vn -0.3021 0.7997 -0.5189 -vn -0.2205 0.7771 -0.5894 -vn -0.3255 0.7627 -0.5589 -vn -0.2375 0.7358 -0.6342 -vn -0.3504 0.7175 -0.6020 -vn -0.2557 0.6848 -0.6824 -vn -0.3765 0.6637 -0.6464 -vn -0.2727 0.6287 -0.7283 -vn -0.4004 0.6071 -0.6864 -vn -0.2860 0.5744 -0.7670 -vn -0.4204 0.5523 -0.7199 -vn -0.2965 0.5307 -0.7940 -vn -0.4342 0.5089 -0.7433 -vn -0.3034 0.4989 -0.8118 -vn -0.4449 0.4729 -0.7606 -vn -0.3098 0.4626 -0.8307 -vn -0.4585 0.4204 -0.7829 -vn -0.3236 0.3806 -0.8663 -vn -0.4705 0.3632 -0.8042 -vn -0.3277 0.3428 -0.8804 -vn 0.1766 0.0852 0.9806 -vn 0.3397 -0.1978 0.9195 -vn 0.3385 0.2615 0.9039 -vn 0.1416 0.6068 0.7821 -vn 0.2667 0.6430 0.7180 -vn 0.1268 0.9338 0.3345 -vn 0.0470 0.9643 0.2605 -vn -0.0377 0.9770 -0.2100 -vn -0.1308 0.9249 -0.3570 -vn -0.0827 0.8835 -0.4612 -vn -0.1635 0.4044 -0.8998 -vn -0.3188 0.4260 -0.8467 -vn -0.1741 0.2856 -0.9424 -vn -0.3369 0.2914 -0.8953 -vn -0.3523 0.2735 -0.8950 -vn -0.1582 0.2680 -0.9503 -vn 0.1694 -0.3098 0.9356 -vn -0.1017 0.8161 -0.5689 -vn -0.0014 0.8427 -0.5384 -vn -0.1106 0.7779 -0.6185 -vn -0.0013 0.7997 -0.6004 -vn -0.1188 0.7368 -0.6656 -vn -0.0015 0.7627 -0.6467 -vn -0.1279 0.6860 -0.7163 -vn -0.0018 0.7176 -0.6965 -vn -0.1368 0.6300 -0.7645 -vn -0.0018 0.6638 -0.7479 -vn -0.1452 0.5757 -0.8047 -vn -0.0015 0.6072 -0.7945 -vn -0.1503 0.5319 -0.8333 -vn -0.0014 0.5525 -0.8335 -vn -0.1540 0.5003 -0.8521 -vn -0.0013 0.5089 -0.8608 -vn -0.1585 0.4633 -0.8719 -vn -0.0010 0.4729 -0.8811 -vn -0.0007 0.4215 -0.9068 -vn -0.1681 0.3468 -0.9227 -vn 0.0006 0.3648 -0.9311 -vn -0.1715 0.3131 -0.9341 -vn 0.0007 0.3267 -0.9451 -vn 0.0042 0.2992 -0.9542 -vn -0.1042 0.3675 -0.9242 -vn 0.1708 0.0501 0.9840 -vn 0.1608 -0.0051 0.9870 -vn -0.5390 -0.4511 0.7113 -vn -0.3129 -0.0958 0.9449 -vn 0.5917 -0.7993 0.1053 -vn -0.7522 -0.0665 -0.6556 -vn -0.8275 -0.4120 -0.3814 -vn -0.1359 0.2062 -0.9690 -vn 0.2076 0.7890 -0.5783 -vn 0.3218 -0.2301 0.9184 -vn 0.2977 -0.8468 0.4407 -vn -0.3763 0.4153 -0.8282 -vn 0.4409 0.7015 -0.5600 -vn 0.1160 0.8642 -0.4896 -vn 0.3688 0.8308 0.4168 -vn -0.3914 -0.2751 -0.8781 -vn -0.3542 0.0530 -0.9337 -vn -0.3465 0.1013 -0.9326 -vn 0.3768 0.3062 0.8742 -vn 0.1010 -0.3271 0.9396 -vn 0.2685 -0.2521 0.9297 -vn -0.1840 0.3333 -0.9247 -vn 0.5073 0.8131 0.2854 -vn 0.6990 -0.6571 0.2822 -vn 0.7092 -0.6145 0.3456 -vn 0.4258 -0.7835 0.4525 -vn -0.1746 0.1842 -0.9673 -vn 0.2102 0.1452 -0.9668 -vn -0.9324 -0.3311 -0.1449 -vn 0.3742 -0.1618 0.9131 -vn -0.8178 -0.1193 0.5630 -vn -0.7921 -0.3155 0.5226 -vn 0.6029 -0.6592 -0.4494 -vn 0.1583 -0.8121 -0.5616 -vn 0.1702 -0.3049 0.9371 -vn 0.2992 0.3857 0.8728 -vn 0.2934 -0.7989 0.5251 -vn 0.3178 -0.1846 0.9300 -vn -0.2375 0.1952 -0.9516 -vn -0.1454 0.3520 -0.9246 -vn 0.5940 0.7518 0.2861 -vn -0.1581 0.1913 -0.9687 -vn -0.9633 -0.2265 -0.1437 -vn 0.3541 -0.2016 0.9132 -vn -0.8257 -0.0286 0.5634 -vn -0.8222 -0.2271 0.5219 -vn 0.4574 0.7443 -0.4866 -vn 0.3600 0.7495 -0.5555 -vn -0.3522 0.0708 -0.9333 -vn 0.3398 0.3500 0.8729 -vn 0.1359 -0.3208 0.9373 -vn 0.2953 -0.2189 0.9300 -vn -0.2146 0.2205 -0.9515 -vn -0.1448 0.3538 -0.9240 -vn 0.5941 0.7518 0.2860 -vn 0.3599 0.7496 -0.5555 -vn 0.2709 0.8694 0.4131 -vn 0.0138 0.8698 -0.4932 -vn 0.3395 0.3483 0.8738 -vn 0.1368 -0.3186 0.9380 -vn -0.1236 -0.8751 -0.4678 -vn -0.0527 -0.9032 -0.4259 -vn -0.0525 -0.8797 -0.4726 -vn 0.0057 -0.9122 -0.4097 -vn 0.0135 -0.8845 -0.4663 -vn 0.0602 -0.9251 -0.3749 -vn 0.0703 -0.9152 -0.3969 -vn 0.2457 -0.9130 0.3257 -vn 0.2272 -0.9225 0.3121 -vn 0.2263 -0.8824 0.4124 -vn 0.1942 -0.9097 0.3670 -vn 0.1661 -0.8777 0.4496 -vn 0.0973 -0.8934 0.4385 -vn 0.1109 -0.8689 0.4825 -vn 0.0370 -0.8846 0.4650 -vn -0.0271 -0.8755 0.4825 -vn -0.2856 -0.8551 0.4327 -vn -0.3628 -0.8360 0.4117 -vn -0.3063 -0.8411 0.4459 -vn -0.2211 -0.8610 0.4580 -vn -0.4200 -0.8307 0.3654 -vn -0.4696 -0.8270 0.3091 -vn -0.4417 -0.8472 0.2952 -vn -0.5103 -0.8245 0.2443 -vn -0.5412 -0.8229 0.1729 -vn -0.5099 -0.8446 0.1634 -vn -0.5592 -0.8235 0.0960 -vn -0.5315 -0.8469 0.0142 -vn -0.5646 -0.8252 0.0178 -vn -0.5268 -0.8479 -0.0598 -vn -0.5574 -0.8281 -0.0594 -vn -0.5381 -0.8323 -0.1329 -vn -0.4434 -0.8598 -0.2534 -vn -0.5112 -0.8370 -0.1953 -vn -0.1772 -0.8874 -0.4256 -vn 0.0774 -0.8650 -0.4958 -vn 0.1349 -0.8644 -0.4844 -vn 0.2087 -0.8613 -0.4632 -vn 0.2507 -0.8644 -0.4359 -vn 0.3148 -0.8615 -0.3983 -vn 0.3486 -0.8642 -0.3629 -vn 0.4016 -0.8615 -0.3106 -vn 0.4264 -0.8637 -0.2686 -vn 0.4659 -0.8613 -0.2029 -vn 0.4802 -0.8632 -0.1561 -vn 0.5080 -0.8608 -0.0311 -vn 0.4987 -0.8631 -0.0796 -vn 0.4997 -0.8608 0.0959 -vn 0.5026 -0.8633 0.0465 -vn 0.4617 -0.8607 0.2147 -vn 0.4758 -0.8634 0.1675 -vn 0.3965 -0.8603 0.3205 -vn 0.4215 -0.8634 0.2773 -vn 0.2955 -0.8638 0.4082 -vn 0.3409 -0.8633 0.3722 -vn -0.2347 -0.8379 0.4927 -vn -0.2954 -0.8416 0.4521 -vn -0.4701 -0.8398 -0.2715 -vn -0.4338 -0.8379 -0.3313 -vn -0.3999 -0.8662 -0.2997 -vn -0.3871 -0.8373 -0.3861 -vn -0.3538 -0.8689 -0.3462 -vn -0.3339 -0.8366 -0.4343 -vn -0.1460 -0.8162 -0.5590 -vn -0.0712 -0.8117 -0.5797 -vn 0.0077 -0.8064 -0.5913 -vn 0.0851 -0.7980 -0.5966 -vn 0.1628 -0.7931 -0.5870 -vn 0.2379 -0.7884 -0.5673 -vn 0.3091 -0.7841 -0.5381 -vn 0.3758 -0.7803 -0.4999 -vn 0.4370 -0.7771 -0.4530 -vn 0.4920 -0.7744 -0.3978 -vn 0.5398 -0.7723 -0.3349 -vn 0.5793 -0.7708 -0.2650 -vn 0.6094 -0.7700 -0.1893 -vn 0.6287 -0.7699 -0.1094 -vn 0.6366 -0.7707 -0.0278 -vn 0.6332 -0.7721 0.0535 -vn 0.6189 -0.7742 0.1324 -vn 0.5946 -0.7769 0.2073 -vn 0.5610 -0.7800 0.2772 -vn 0.5188 -0.7838 0.3414 -vn 0.4688 -0.7879 0.3992 -vn 0.4116 -0.7925 0.4500 -vn 0.3477 -0.7974 0.4932 -vn 0.2774 -0.8056 0.5235 -vn 0.2028 -0.8107 0.5491 -vn 0.1269 -0.8134 0.5677 -vn -0.3997 -0.8454 0.3544 -vn -0.4417 -0.8471 0.2956 -vn -0.5292 -0.8484 0.0143 -vn -0.5277 -0.8473 -0.0603 -vn -0.5168 -0.8457 -0.1329 -vn -0.1812 -0.7352 -0.6532 -vn -0.0948 -0.7281 -0.6789 -vn -0.0037 -0.7211 -0.6929 -vn 0.0885 -0.7143 -0.6942 -vn 0.1793 -0.7080 -0.6831 -vn 0.2669 -0.7020 -0.6602 -vn 0.3502 -0.6966 -0.6262 -vn 0.4281 -0.6918 -0.5815 -vn 0.4996 -0.6877 -0.5268 -vn 0.5639 -0.6843 -0.4624 -vn 0.6197 -0.6817 -0.3889 -vn 0.6659 -0.6798 -0.3072 -vn 0.7010 -0.6788 -0.2187 -vn 0.7236 -0.6787 -0.1254 -vn 0.7329 -0.6796 -0.0300 -vn 0.7289 -0.6815 0.0650 -vn 0.7123 -0.6841 0.1571 -vn 0.6838 -0.6875 0.2446 -vn 0.6445 -0.6915 0.3263 -vn 0.5952 -0.6962 0.4012 -vn 0.5367 -0.7016 0.4687 -vn 0.4698 -0.7074 0.5281 -vn 0.3951 -0.7138 0.5783 -vn 0.3137 -0.7204 0.6186 -vn 0.2282 -0.7273 0.6473 -vn 0.1370 -0.7344 0.6647 -vn 0.0470 -0.7418 0.6689 -vn -0.0428 -0.7507 0.6592 -vn -0.1287 -0.7546 0.6435 -vn -0.2100 -0.7609 0.6139 -vn -0.2856 -0.7657 0.5763 -vn -0.3550 -0.7701 0.5300 -vn -0.4178 -0.7741 0.4756 -vn -0.4738 -0.7774 0.4137 -vn -0.5221 -0.7801 0.3448 -vn -0.5620 -0.7820 0.2697 -vn -0.5925 -0.7831 0.1890 -vn -0.6132 -0.7829 0.1049 -vn -0.6230 -0.7820 0.0188 -vn -0.6218 -0.7803 -0.0675 -vn -0.6100 -0.7777 -0.1521 -vn -0.5879 -0.7744 -0.2338 -vn -0.5564 -0.7704 -0.3113 -vn -0.5154 -0.7660 -0.3841 -vn -0.4653 -0.7613 -0.4515 -vn -0.4064 -0.7562 -0.5128 -vn -0.3381 -0.7515 -0.5665 -vn -0.2640 -0.7426 -0.6155 -vn -0.2152 -0.6403 -0.7374 -vn -0.1164 -0.6340 -0.7646 -vn -0.0134 -0.6279 -0.7782 -vn 0.0902 -0.6221 -0.7777 -vn 0.1919 -0.6166 -0.7635 -vn 0.2896 -0.6115 -0.7363 -vn 0.3821 -0.6068 -0.6970 -vn 0.4683 -0.6027 -0.6461 -vn 0.5473 -0.5992 -0.5843 -vn 0.6182 -0.5963 -0.5120 -vn 0.6798 -0.5941 -0.4300 -vn 0.7307 -0.5925 -0.3391 -vn 0.7694 -0.5916 -0.2407 -vn 0.7945 -0.5915 -0.1372 -vn 0.8051 -0.5923 -0.0314 -vn 0.8012 -0.5938 0.0742 -vn 0.7833 -0.5960 0.1767 -vn 0.7525 -0.5988 0.2743 -vn 0.7097 -0.6022 0.3657 -vn 0.6558 -0.6062 0.4500 -vn 0.5915 -0.6107 0.5264 -vn 0.5176 -0.6157 0.5941 -vn 0.4348 -0.6212 0.6520 -vn 0.3441 -0.6269 0.6990 -vn 0.2479 -0.6330 0.7334 -vn 0.1456 -0.6392 0.7551 -vn 0.0429 -0.6454 0.7626 -vn -0.0590 -0.6513 0.7565 -vn -0.1574 -0.6568 0.7375 -vn -0.2508 -0.6619 0.7064 -vn -0.3382 -0.6665 0.6644 -vn -0.4189 -0.6706 0.6123 -vn -0.4922 -0.6742 0.5506 -vn -0.5576 -0.6773 0.4800 -vn -0.6142 -0.6797 0.4010 -vn -0.6610 -0.6813 0.3144 -vn -0.6969 -0.6822 0.2213 -vn -0.7206 -0.6822 0.1239 -vn -0.7314 -0.6815 0.0241 -vn -0.7293 -0.6800 -0.0757 -vn -0.7146 -0.6777 -0.1733 -vn -0.6881 -0.6747 -0.2670 -vn -0.6504 -0.6711 -0.3558 -vn -0.6021 -0.6670 -0.4389 -vn -0.5435 -0.6625 -0.5155 -vn -0.4751 -0.6575 -0.5848 -vn -0.3973 -0.6522 -0.6457 -vn -0.3106 -0.6465 -0.6969 -vn -0.2424 -0.5430 -0.8040 -vn -0.1337 -0.5384 -0.8320 -vn -0.0213 -0.5341 -0.8452 -vn 0.0914 -0.5299 -0.8431 -vn 0.2015 -0.5260 -0.8263 -vn 0.3071 -0.5223 -0.7956 -vn 0.4067 -0.5189 -0.7519 -vn 0.4993 -0.5159 -0.6961 -vn 0.5841 -0.5133 -0.6287 -vn 0.6601 -0.5113 -0.5504 -vn 0.7260 -0.5096 -0.4617 -vn 0.7805 -0.5084 -0.3637 -vn 0.8220 -0.5078 -0.2577 -vn 0.8491 -0.5076 -0.1463 -vn 0.8607 -0.5081 -0.0324 -vn 0.8568 -0.5092 0.0812 -vn 0.8381 -0.5107 0.1918 -vn 0.8055 -0.5126 0.2973 -vn 0.7601 -0.5150 0.3964 -vn 0.7026 -0.5178 0.4880 -vn 0.6340 -0.5210 0.5714 -vn 0.5548 -0.5246 0.6457 -vn 0.4657 -0.5285 0.7098 -vn 0.3678 -0.5326 0.7623 -vn 0.2631 -0.5370 0.8015 -vn 0.1523 -0.5416 0.8267 -vn 0.0399 -0.5460 0.8368 -vn -0.0718 -0.5503 0.8319 -vn -0.1803 -0.5543 0.8126 -vn -0.2836 -0.5580 0.7799 -vn -0.3806 -0.5613 0.7349 -vn -0.4704 -0.5643 0.6784 -vn -0.5523 -0.5670 0.6112 -vn -0.6254 -0.5693 0.5337 -vn -0.6888 -0.5711 0.4466 -vn -0.7413 -0.5723 0.3507 -vn -0.7813 -0.5729 0.2474 -vn -0.8076 -0.5730 0.1392 -vn -0.8194 -0.5726 0.0284 -vn -0.8164 -0.5715 -0.0824 -vn -0.7993 -0.5699 -0.1904 -vn -0.7689 -0.5678 -0.2939 -vn -0.7261 -0.5652 -0.3916 -vn -0.6715 -0.5623 -0.4826 -vn -0.6058 -0.5590 -0.5661 -vn -0.5294 -0.5555 -0.6412 -vn -0.4429 -0.5516 -0.7068 -vn -0.3470 -0.5475 -0.7614 -vn -0.2619 -0.4542 -0.8515 -vn -0.1461 -0.4512 -0.8804 -vn -0.0269 -0.4484 -0.8934 -vn 0.0923 -0.4456 -0.8904 -vn 0.2086 -0.4430 -0.8719 -vn 0.3198 -0.4406 -0.8388 -vn 0.4247 -0.4383 -0.7921 -vn 0.5221 -0.4363 -0.7328 -vn 0.6112 -0.4347 -0.6615 -vn 0.6910 -0.4333 -0.5787 -vn 0.7602 -0.4322 -0.4852 -vn 0.8174 -0.4313 -0.3818 -vn 0.8610 -0.4308 -0.2703 -vn 0.8894 -0.4307 -0.1531 -vn 0.9018 -0.4309 -0.0333 -vn 0.8979 -0.4316 0.0864 -vn 0.8785 -0.4325 0.2029 -vn 0.8445 -0.4337 0.3141 -vn 0.7970 -0.4352 0.4187 -vn 0.7370 -0.4370 0.5157 -vn 0.6650 -0.4390 0.6041 -vn 0.5819 -0.4413 0.6831 -vn 0.4881 -0.4439 0.7515 -vn 0.3849 -0.4465 0.8078 -vn 0.2740 -0.4494 0.8503 -vn 0.1570 -0.4523 0.8779 -vn 0.0379 -0.4552 0.8896 -vn -0.0809 -0.4580 0.8852 -vn -0.1964 -0.4607 0.8656 -vn -0.3067 -0.4630 0.8316 -vn -0.4104 -0.4652 0.7843 -vn -0.5065 -0.4672 0.7247 -vn -0.5943 -0.4690 0.6534 -vn -0.6727 -0.4705 0.5710 -vn -0.7408 -0.4718 0.4782 -vn -0.7971 -0.4726 0.3759 -vn -0.8400 -0.4731 0.2655 -vn -0.8681 -0.4732 0.1499 -vn -0.8805 -0.4730 0.0314 -vn -0.8771 -0.4724 -0.0870 -vn -0.8584 -0.4714 -0.2023 -vn -0.8254 -0.4700 -0.3126 -vn -0.7791 -0.4684 -0.4166 -vn -0.7203 -0.4665 -0.5133 -vn -0.6497 -0.4645 -0.6018 -vn -0.5678 -0.4622 -0.6811 -vn -0.4753 -0.4598 -0.7501 -vn -0.3729 -0.4571 -0.8074 -vn -0.2751 -0.3782 -0.8839 -vn -0.1544 -0.3765 -0.9135 -vn -0.0306 -0.3749 -0.9265 -vn 0.0931 -0.3734 -0.9230 -vn 0.2135 -0.3720 -0.9033 -vn 0.3288 -0.3706 -0.8687 -vn 0.4373 -0.3693 -0.8200 -vn 0.5377 -0.3661 -0.7596 -vn 0.6310 -0.3639 -0.6851 -vn 0.7135 -0.3631 -0.5992 -vn 0.7851 -0.3625 -0.5022 -vn 0.8443 -0.3621 -0.3950 -vn 0.8894 -0.3618 -0.2794 -vn 0.9189 -0.3615 -0.1581 -vn 0.9317 -0.3617 -0.0339 -vn 0.9278 -0.3620 0.0901 -vn 0.9078 -0.3625 0.2108 -vn 0.8720 -0.3643 0.3270 -vn 0.8226 -0.3672 0.4341 -vn 0.7607 -0.3681 0.5347 -vn 0.6864 -0.3692 0.6265 -vn 0.6004 -0.3705 0.7087 -vn 0.5035 -0.3719 0.7799 -vn 0.3966 -0.3733 0.8386 -vn 0.2815 -0.3750 0.8833 -vn 0.1603 -0.3766 0.9124 -vn 0.0366 -0.3783 0.9250 -vn -0.0869 -0.3798 0.9210 -vn -0.2072 -0.3813 0.9010 -vn -0.3220 -0.3826 0.8660 -vn -0.4301 -0.3838 0.8171 -vn -0.5304 -0.3849 0.7554 -vn -0.6220 -0.3859 0.6813 -vn -0.7040 -0.3845 0.5972 -vn -0.7764 -0.3839 0.4999 -vn -0.8353 -0.3844 0.3931 -vn -0.8803 -0.3846 0.2780 -vn -0.9096 -0.3847 0.1571 -vn -0.9224 -0.3847 0.0334 -vn -0.9187 -0.3844 -0.0902 -vn -0.8982 -0.3852 -0.2116 -vn -0.8629 -0.3868 -0.3251 -vn -0.8144 -0.3859 -0.4334 -vn -0.7529 -0.3849 -0.5338 -vn -0.6791 -0.3838 -0.6257 -vn -0.5936 -0.3826 -0.7080 -vn -0.4970 -0.3813 -0.7795 -vn -0.3904 -0.3799 -0.8386 -vn -0.2824 -0.3250 -0.9026 -vn -0.1588 -0.3241 -0.9326 -vn -0.0324 -0.3234 -0.9457 -vn 0.0939 -0.3227 -0.9418 -vn 0.2169 -0.3220 -0.9216 -vn 0.3344 -0.3213 -0.8860 -vn 0.6494 -0.2707 -0.7106 -vn 0.7350 -0.2702 -0.6219 -vn 0.8094 -0.2697 -0.5217 -vn 0.8710 -0.2693 -0.4110 -vn 0.9178 -0.2700 -0.2911 -vn 0.9501 -0.2657 -0.1632 -vn 0.9625 -0.2695 -0.0319 -vn 0.9584 -0.2685 0.0967 -vn 0.9374 -0.2686 0.2217 -vn 0.7747 -0.3195 0.5457 -vn 0.6991 -0.3200 0.6394 -vn 0.6115 -0.3206 0.7234 -vn 0.5127 -0.3212 0.7962 -vn 0.4037 -0.3219 0.8564 -vn 0.2861 -0.3227 0.9022 -vn 0.1626 -0.3235 0.9322 -vn 0.0362 -0.3243 0.9453 -vn -0.0900 -0.3251 0.9414 -vn -0.2128 -0.3258 0.9212 -vn -0.3303 -0.3264 0.8856 -vn -0.4409 -0.3270 0.8359 -vn -0.5435 -0.3275 0.7728 -vn -0.8052 -0.2821 0.5216 -vn -0.8662 -0.2817 0.4127 -vn -0.9148 -0.2803 0.2910 -vn -0.9453 -0.2838 0.1609 -vn -0.9586 -0.2830 0.0323 -vn -0.9543 -0.2830 -0.0960 -vn -0.8342 -0.3285 -0.4429 -vn -0.7711 -0.3281 -0.5456 -vn -0.6955 -0.3276 -0.6395 -vn -0.6080 -0.3271 -0.7235 -vn -0.5091 -0.3265 -0.7964 -vn -0.4001 -0.3258 -0.8566 -vn -0.2885 -0.2613 -0.9212 -vn -0.1623 -0.2605 -0.9517 -vn -0.0333 -0.2598 -0.9651 -vn 0.0956 -0.2591 -0.9611 -vn 0.2211 -0.2583 -0.9404 -vn 0.3411 -0.2575 -0.9041 -vn 0.7906 -0.2540 0.5571 -vn 0.7134 -0.2545 0.6529 -vn 0.6241 -0.2550 0.7386 -vn 0.5232 -0.2556 0.8130 -vn 0.4119 -0.2562 0.8744 -vn 0.2918 -0.2569 0.9213 -vn 0.1658 -0.2575 0.9519 -vn 0.0368 -0.2583 0.9654 -vn -0.0921 -0.2591 0.9615 -vn -0.2176 -0.2598 0.9408 -vn -0.3375 -0.2605 0.9045 -vn -0.4505 -0.2612 0.8537 -vn -0.5553 -0.2618 0.7894 -vn -0.8519 -0.2643 -0.4521 -vn -0.7875 -0.2639 -0.5569 -vn -0.7103 -0.2635 -0.6527 -vn -0.6209 -0.2631 -0.7384 -vn -0.5200 -0.2626 -0.8128 -vn -0.4086 -0.2619 -0.8743 -vn -0.2939 -0.1863 -0.9375 -vn -0.1656 -0.1854 -0.9686 -vn -0.0342 -0.1844 -0.9823 -vn 0.0969 -0.1834 -0.9782 -vn 0.2247 -0.1824 -0.9572 -vn 0.3468 -0.1815 -0.9202 -vn 0.4611 -0.1813 -0.8686 -vn 0.5682 -0.1815 -0.8026 -vn 0.6656 -0.1807 -0.7241 -vn 0.7528 -0.1800 -0.6331 -vn 0.8285 -0.1794 -0.5305 -vn 0.8910 -0.1788 -0.4172 -vn 0.9389 -0.1779 -0.2947 -vn 0.9700 -0.1770 -0.1667 -vn 0.9835 -0.1772 -0.0357 -vn 0.9795 -0.1774 0.0957 -vn 0.9584 -0.1774 0.2234 -vn 0.9215 -0.1775 0.3455 -vn 0.8696 -0.1766 0.4610 -vn 0.8045 -0.1762 0.5672 -vn 0.7259 -0.1767 0.6647 -vn 0.6350 -0.1773 0.7519 -vn 0.5323 -0.1780 0.8276 -vn 0.4190 -0.1788 0.8902 -vn 0.2967 -0.1796 0.9379 -vn 0.1684 -0.1805 0.9691 -vn 0.0370 -0.1815 0.9827 -vn -0.0941 -0.1825 0.9787 -vn -0.2219 -0.1835 0.9577 -vn -0.3440 -0.1844 0.9207 -vn -0.4589 -0.1853 0.8689 -vn -0.5656 -0.1862 0.8034 -vn -0.6623 -0.1880 0.7252 -vn -0.7499 -0.1903 0.6335 -vn -0.8257 -0.1910 0.5309 -vn -0.8882 -0.1914 0.4177 -vn -0.9669 -0.1923 0.1674 -vn -0.9359 -0.1918 0.2953 -vn -0.9806 -0.1929 0.0359 -vn -0.9766 -0.1930 -0.0953 -vn -0.9555 -0.1929 -0.2229 -vn -0.9186 -0.1913 -0.3458 -vn -0.8673 -0.1900 -0.4601 -vn -0.8018 -0.1896 -0.5668 -vn -0.7232 -0.1892 -0.6642 -vn -0.6322 -0.1886 -0.7515 -vn -0.5295 -0.1880 -0.8272 -vn -0.4162 -0.1872 -0.8898 -vn -0.2966 -0.1389 -0.9448 -vn -0.1673 -0.1380 -0.9762 -vn -0.0350 -0.1371 -0.9899 -vn 0.0973 -0.1361 -0.9859 -vn 0.2260 -0.1352 -0.9647 -vn 0.3490 -0.1342 -0.9275 -vn 0.4648 -0.1333 -0.8753 -vn 0.5723 -0.1325 -0.8092 -vn 0.6706 -0.1317 -0.7301 -vn 0.7585 -0.1310 -0.6384 -vn 0.8348 -0.1303 -0.5349 -vn 0.8979 -0.1297 -0.4207 -vn 0.9459 -0.1292 -0.2975 -vn 0.9773 -0.1288 -0.1681 -vn 0.9911 -0.1286 -0.0358 -vn 0.9870 -0.1285 0.0964 -vn 0.9658 -0.1285 0.2252 -vn 0.9286 -0.1286 0.3482 -vn 0.8764 -0.1288 0.4640 -vn 0.8103 -0.1291 0.5716 -vn 0.7312 -0.1296 0.6698 -vn 0.6395 -0.1301 0.7577 -vn 0.5360 -0.1308 0.8340 -vn 0.4218 -0.1316 0.8971 -vn 0.2986 -0.1324 0.9452 -vn 0.1693 -0.1333 0.9765 -vn 0.0369 -0.1342 0.9903 -vn -0.0953 -0.1352 0.9862 -vn -0.2240 -0.1361 0.9650 -vn -0.3471 -0.1370 0.9278 -vn -0.4629 -0.1379 0.8756 -vn -0.5704 -0.1387 0.8096 -vn -0.6686 -0.1396 0.7304 -vn -0.7566 -0.1403 0.6387 -vn -0.8329 -0.1410 0.5352 -vn -0.8959 -0.1416 0.4210 -vn -0.9440 -0.1421 0.2978 -vn -0.9754 -0.1424 0.1685 -vn -0.9891 -0.1427 0.0361 -vn -0.9851 -0.1428 -0.0961 -vn -0.9639 -0.1428 -0.2248 -vn -0.9266 -0.1427 -0.3479 -vn -0.8745 -0.1424 -0.4637 -vn -0.8084 -0.1421 -0.5712 -vn -0.7292 -0.1416 -0.6695 -vn -0.6375 -0.1411 -0.7574 -vn -0.5341 -0.1405 -0.8337 -vn -0.4199 -0.1397 -0.8968 -vn -0.2981 -0.1072 -0.9485 -vn -0.1683 -0.1064 -0.9800 -vn -0.0354 -0.1057 -0.9938 -vn 0.0973 -0.1049 -0.9897 -vn 0.2265 -0.1041 -0.9684 -vn 0.3500 -0.1033 -0.9310 -vn 0.4663 -0.1026 -0.8787 -vn 0.5742 -0.1018 -0.8124 -vn 0.6728 -0.1012 -0.7329 -vn 0.7611 -0.1006 -0.6408 -vn 0.9488 -0.0994 -0.2997 -vn 0.9808 -0.0975 -0.1689 -vn 0.9946 -0.0974 -0.0360 -vn 0.9905 -0.0978 0.0971 -vn 0.9691 -0.0985 0.2260 -vn 0.9317 -0.0986 0.3495 -vn 0.8794 -0.0987 0.4657 -vn 0.8131 -0.0990 0.5737 -vn 0.7336 -0.0994 0.6723 -vn 0.6416 -0.0999 0.7605 -vn 0.5377 -0.1004 0.8371 -vn 0.4231 -0.1011 0.9004 -vn 0.2994 -0.1017 0.9487 -vn 0.1696 -0.1025 0.9802 -vn 0.0367 -0.1033 0.9940 -vn -0.0960 -0.1041 0.9899 -vn -0.2252 -0.1049 0.9686 -vn -0.3487 -0.1056 0.9312 -vn -0.4650 -0.1064 0.8789 -vn -0.5729 -0.1071 0.8126 -vn -0.6715 -0.1077 0.7331 -vn -0.7598 -0.1084 0.6411 -vn -0.9480 -0.1098 0.2989 -vn -0.9794 -0.1101 0.1690 -vn -0.9932 -0.1104 0.0362 -vn -0.9892 -0.1105 -0.0965 -vn -0.9679 -0.1105 -0.2258 -vn -0.9305 -0.1104 -0.3493 -vn -0.8781 -0.1102 -0.4655 -vn -0.8118 -0.1099 -0.5735 -vn -0.7323 -0.1095 -0.6721 -vn -0.6403 -0.1091 -0.7603 -vn -0.5365 -0.1085 -0.8369 -vn -0.4218 -0.1079 -0.9002 -vn -0.2990 -0.0850 -0.9505 -vn -0.1689 -0.0845 -0.9820 -vn -0.0357 -0.0839 -0.9958 -vn 0.0973 -0.0833 -0.9918 -vn 0.2267 -0.0826 -0.9704 -vn 0.3505 -0.0820 -0.9330 -vn 0.4670 -0.0814 -0.8805 -vn 0.5751 -0.0809 -0.8141 -vn 0.9832 -0.0718 -0.1675 -vn 0.9967 -0.0732 -0.0353 -vn 0.9926 -0.0711 0.0984 -vn 0.8810 -0.0785 0.4667 -vn 0.8145 -0.0787 0.5748 -vn 0.7349 -0.0790 0.6736 -vn 0.6426 -0.0793 0.7620 -vn 0.5386 -0.0798 0.8388 -vn 0.4237 -0.0803 0.9022 -vn 0.2998 -0.0808 0.9506 -vn 0.1697 -0.0814 0.9821 -vn 0.0365 -0.0820 0.9960 -vn -0.0964 -0.0826 0.9919 -vn -0.2259 -0.0832 0.9706 -vn -0.3497 -0.0838 0.9331 -vn -0.4662 -0.0844 0.8807 -vn -0.5743 -0.0849 0.8142 -vn -0.6731 -0.0854 0.7346 -vn -0.9817 -0.0881 0.1688 -vn -0.8802 -0.0873 -0.4665 -vn -0.8137 -0.0871 -0.5747 -vn -0.7341 -0.0868 -0.6735 -vn -0.6419 -0.0865 -0.7619 -vn -0.5378 -0.0861 -0.8387 -vn -0.4229 -0.0856 -0.9021 -vn -0.2995 -0.0689 -0.9516 -vn -0.1692 -0.0685 -0.9832 -vn -0.0359 -0.0681 -0.9970 -vn 0.0972 -0.0676 -0.9930 -vn 0.2268 -0.0672 -0.9716 -vn 0.3507 -0.0668 -0.9341 -vn 0.8859 -0.0631 0.4597 -vn 0.8153 -0.0643 0.5754 -vn 0.7356 -0.0646 0.6744 -vn 0.6432 -0.0648 0.7629 -vn 0.5391 -0.0651 0.8397 -vn 0.4241 -0.0655 0.9033 -vn 0.3000 -0.0659 0.9517 -vn 0.1697 -0.0663 0.9833 -vn 0.0364 -0.0667 0.9971 -vn -0.0967 -0.0672 0.9930 -vn -0.2263 -0.0676 0.9717 -vn -0.3503 -0.0680 0.9342 -vn -0.4669 -0.0684 0.8817 -vn -0.5752 -0.0688 0.8151 -vn -0.8855 -0.0714 -0.4591 -vn -0.8149 -0.0704 -0.5754 -vn -0.7351 -0.0702 -0.6743 -vn -0.6428 -0.0700 -0.7629 -vn -0.5386 -0.0697 -0.8397 -vn -0.4236 -0.0693 -0.9032 -vn -0.2998 -0.0569 -0.9523 -vn -0.1695 -0.0566 -0.9839 -vn -0.0361 -0.0563 -0.9978 -vn 0.0972 -0.0560 -0.9937 -vn 0.2269 -0.0558 -0.9723 -vn 0.3585 -0.0561 -0.9319 -vn 0.7360 -0.0541 0.6748 -vn 0.6436 -0.0542 0.7634 -vn 0.5393 -0.0544 0.8403 -vn 0.4243 -0.0547 0.9039 -vn 0.3001 -0.0549 0.9523 -vn 0.1697 -0.0551 0.9839 -vn 0.0363 -0.0554 0.9978 -vn -0.0969 -0.0557 0.9937 -vn -0.2266 -0.0560 0.9724 -vn -0.3509 -0.0567 0.9347 -vn -0.9977 -0.0586 -0.0327 -vn -0.9577 -0.0584 -0.2819 -vn -0.7357 -0.0577 -0.6748 -vn -0.6433 -0.0575 -0.7634 -vn -0.5391 -0.0573 -0.8403 -vn -0.4240 -0.0571 -0.9039 -vn -0.3000 -0.0475 -0.9528 -vn -0.1696 -0.0474 -0.9844 -vn -0.0362 -0.0472 -0.9982 -vn 0.0972 -0.0471 -0.9941 -vn 0.2269 -0.0471 -0.9728 -vn 0.3590 -0.0472 -0.9322 -vn 0.5280 -0.0465 -0.8480 -vn 0.7363 -0.0464 0.6751 -vn 0.6438 -0.0465 0.7638 -vn 0.5395 -0.0465 0.8407 -vn 0.4244 -0.0466 0.9043 -vn 0.3001 -0.0467 0.9528 -vn 0.1698 -0.0468 0.9844 -vn 0.0363 -0.0469 0.9982 -vn -0.0970 -0.0470 0.9942 -vn -0.2268 -0.0471 0.9728 -vn -0.3588 -0.0466 0.9322 -vn -0.7188 -0.0474 0.6936 -vn -0.8743 -0.0478 0.4830 -vn -0.9709 -0.0478 0.2345 -vn -0.9983 -0.0479 -0.0323 -vn -0.9547 -0.0479 -0.2935 -vn -0.7415 -0.0484 -0.6692 -vn -0.6437 -0.0477 -0.7638 -vn -0.5394 -0.0477 -0.8407 -vn -0.4242 -0.0476 -0.9043 -vn -0.2998 -0.0537 -0.9525 -vn 0.0979 -0.0394 -0.9944 -vn 0.5275 -0.0287 -0.8491 -vn 0.7194 -0.0220 -0.6942 -vn 0.7444 -0.0214 0.6674 -vn 0.6443 -0.0246 0.7644 -vn 0.5399 -0.0277 0.8413 -vn 0.4246 -0.0311 0.9048 -vn 0.3003 -0.0349 0.9532 -vn -0.2265 -0.0511 0.9727 -vn -0.3610 -0.0547 0.9310 -vn -0.5301 -0.0623 0.8456 -vn -0.8728 -0.0729 0.4827 -vn -0.7180 -0.0671 0.6928 -vn -0.9691 -0.0762 0.2346 -vn -0.9965 -0.0772 -0.0316 -vn -0.9533 -0.0757 -0.2923 -vn -0.8528 -0.0721 -0.5172 -vn -0.7408 -0.0685 -0.6683 -vn -0.6428 -0.0651 -0.7632 -vn -0.5387 -0.0617 -0.8402 -vn -0.4238 -0.0579 -0.9039 -vn -0.2928 -0.0929 -0.9517 -vn 0.5159 0.0232 -0.8563 -vn 0.7170 0.0512 -0.6952 -vn 0.8718 0.0724 -0.4844 -vn 0.9681 0.0854 -0.2354 -vn 0.9955 0.0890 0.0321 -vn 0.9522 0.0829 0.2939 -vn 0.8516 0.0692 0.5195 -vn 0.7405 0.0541 0.6699 -vn 0.6428 0.0407 0.7650 -vn 0.5391 0.0262 0.8418 -vn 0.4247 0.0101 0.9053 -vn 0.3015 -0.0073 0.9535 -vn -0.5175 -0.1267 0.8462 -vn -0.7056 -0.1541 0.6916 -vn -0.8585 -0.1768 0.4814 -vn -0.9533 -0.1910 0.2339 -vn -0.9803 -0.1950 -0.0314 -vn -0.9378 -0.1886 -0.2915 -vn -0.8381 -0.1749 -0.5167 -vn -0.5286 -0.1278 -0.8392 -vn -0.4151 -0.1110 -0.9030 -vn -0.2681 -0.1540 -0.9510 -vn -0.1449 -0.1120 -0.9831 -vn 0.3062 0.0325 -0.9514 -vn 0.5069 0.1049 -0.8556 -vn 0.6996 0.1678 -0.6945 -vn 0.8482 0.2155 -0.4838 -vn 0.9406 0.2450 -0.2349 -vn 0.9669 0.2533 0.0322 -vn 0.9253 0.2400 0.2937 -vn 0.8288 0.2093 0.5189 -vn 0.5296 0.1111 0.8409 -vn 0.4190 0.0758 0.9048 -vn 0.3008 0.0368 0.9530 -vn 0.1774 -0.0039 0.9841 -vn 0.0497 -0.0459 0.9977 -vn -0.0829 -0.0901 0.9925 -vn -0.4731 -0.2244 0.8519 -vn -0.6630 -0.2882 0.6909 -vn -0.8090 -0.3381 0.4808 -vn -0.8995 -0.3691 0.2337 -vn -0.9253 -0.3779 -0.0312 -vn -0.8849 -0.3639 -0.2908 -vn -0.7841 -0.3293 -0.5261 -vn -0.4993 -0.2330 -0.8345 -vn -0.3852 -0.1934 -0.9023 -vn -0.1130 -0.1494 -0.9823 -vn -0.2236 -0.2160 -0.9504 -vn 0.2905 0.0977 -0.9519 -vn 0.4782 0.2013 -0.8549 -vn 0.6532 0.3029 -0.6939 -vn 0.7884 0.3805 -0.4833 -vn 0.8726 0.4284 -0.2347 -vn 0.8964 0.4420 0.0321 -vn 0.8585 0.4206 0.2934 -vn 0.7661 0.3657 0.5286 -vn 0.5035 0.2160 0.8366 -vn 0.3982 0.1544 0.9042 -vn 0.2910 0.0913 0.9524 -vn 0.1795 0.0255 0.9834 -vn 0.0639 -0.0432 0.9970 -vn -0.0564 -0.1147 0.9918 -vn -0.2160 -0.2130 0.9529 -vn -0.4084 -0.3281 0.8518 -vn -0.5801 -0.4321 0.6905 -vn -0.7118 -0.5122 0.4806 -vn -0.7935 -0.5619 0.2337 -vn -0.8168 -0.5760 -0.0309 -vn -0.7805 -0.5537 -0.2902 -vn -0.6899 -0.4984 -0.5251 -vn -0.5600 -0.4189 -0.7148 -vn -0.4332 -0.3429 -0.8335 -vn -0.3295 -0.2800 -0.9017 -vn -0.0692 -0.1771 -0.9817 -vn -0.1612 -0.2676 -0.9500 -vn 0.0273 -0.0826 -0.9962 -vn 0.1270 0.0144 -0.9918 -vn 0.2610 0.1452 -0.9544 -vn 0.4234 0.3011 -0.8545 -vn 0.5696 0.4406 -0.6939 -vn 0.6828 0.5477 -0.4835 -vn 0.7534 0.6141 -0.2350 -vn 0.7734 0.6331 0.0318 -vn 0.7414 0.6036 0.2931 -vn 0.6625 0.5298 0.5296 -vn 0.5504 0.4237 0.7194 -vn 0.4445 0.3220 0.8359 -vn 0.3565 0.2376 0.9036 -vn 0.2671 0.1512 0.9517 -vn 0.1744 0.0612 0.9828 -vn 0.0781 -0.0327 0.9964 -vn -0.0223 -0.1298 0.9913 -vn -0.2492 -0.3544 -0.9013 -vn -0.0213 -0.1849 -0.9825 -vn -0.0916 -0.2993 -0.9498 -vn 0.0564 -0.0777 -0.9954 -vn 0.2974 0.3111 0.9026 -vn 0.2292 0.2063 0.9513 -vn 0.1603 0.0965 0.9823 -vn 0.0887 -0.0160 0.9959 -vn 0.0167 -0.1284 0.9916 -vn -0.0548 -0.2436 0.9683 -vn -0.1603 -0.4104 -0.8977 -vn 0.0239 -0.1810 -0.9832 -vn 0.2319 0.3702 0.8995 -vn 0.1851 0.2478 0.9510 -vn 0.1383 0.1265 0.9823 -vn 0.0920 0.0016 0.9958 -vn 0.0453 -0.1225 0.9914 -vn -0.0044 -0.2503 0.9681 -vn -0.0661 -0.4177 0.9062 -vn -0.1404 -0.6119 0.7784 -vn -0.2039 -0.7795 0.5923 -vn -0.2490 -0.8984 0.3618 -vn -0.2713 -0.9570 0.1031 -vn -0.2687 -0.9496 -0.1615 -vn -0.2424 -0.8792 -0.4103 -vn -0.1950 -0.7532 -0.6283 -vn -0.1312 -0.5875 -0.7985 -vn 0.0808 -0.0574 -0.9951 -vn 0.1750 0.3881 -0.9048 -vn 0.2103 0.5883 -0.7809 -vn 0.2469 0.7647 -0.5952 -vn 0.2729 0.8905 -0.3641 -vn 0.2857 0.9527 -0.1037 -vn 0.2840 0.9449 0.1627 -vn 0.2684 0.8702 0.4131 -vn 0.2406 0.7365 0.6322 -vn 0.2046 0.5621 0.8014 -vn 0.0906 0.0210 0.9957 -vn 0.0027 -0.4222 0.9065 -vn -0.0399 -0.6257 0.7790 -vn -0.0763 -0.8016 0.5930 -vn -0.1021 -0.9264 0.3625 -vn -0.1149 -0.9880 0.1036 -vn -0.1135 -0.9804 -0.1613 -vn -0.0985 -0.9066 -0.4102 -vn -0.0716 -0.7746 -0.6283 -vn -0.0352 -0.5917 -0.8054 -vn 0.1251 0.4071 -0.9048 -vn 0.1455 0.6101 -0.7788 -vn 0.1622 0.7886 -0.5931 -vn 0.1743 0.9155 -0.3625 -vn 0.1802 0.9782 -0.1034 -vn 0.1795 0.9704 0.1617 -vn 0.1722 0.8952 0.4110 -vn 0.1594 0.7604 0.6296 -vn 0.1436 0.5727 0.8071 -vn 0.0542 -0.0379 -0.9978 -vn 0.0526 0.0355 0.9980 -vn 0.0601 -0.1056 0.9926 -vn 0.0310 -0.0440 -0.9985 -vn 0.0835 -0.2326 -0.9690 -vn -0.0098 0.0991 -0.9950 -vn 0.0059 0.0406 0.9992 -vn 0.0464 -0.1027 0.9936 -vn 0.1001 -0.2844 0.9535 -vn 0.1619 -0.4969 0.8526 -vn 0.2179 -0.6886 0.6917 -vn 0.2609 -0.8364 0.4821 -vn 0.2878 -0.9282 0.2357 -vn 0.2956 -0.9549 -0.0283 -vn 0.2839 -0.9149 -0.2871 -vn 0.2544 -0.8143 -0.5217 -vn 0.2093 -0.6603 -0.7213 -vn 0.0418 -0.0329 -0.9986 -vn 0.1435 -0.2004 -0.9691 -vn -0.0384 0.0957 -0.9947 -vn -0.1391 0.2513 -0.9579 -vn -0.2540 0.4394 -0.8616 -vn -0.3611 0.6121 -0.7036 -vn -0.4459 0.7474 -0.4925 -vn -0.4991 0.8322 -0.2417 -vn -0.5146 0.8570 0.0284 -vn -0.4915 0.8202 0.2927 -vn -0.4337 0.7280 0.5310 -vn -0.3454 0.5873 0.7320 -vn -0.2307 0.4037 0.8853 -vn -0.1033 0.2027 0.9738 -vn -0.0050 0.0412 0.9991 -vn 0.0697 -0.0776 0.9946 -vn 0.1387 -0.1863 0.9726 -vn 0.2956 -0.4417 0.8471 -vn 0.2070 -0.3009 0.9309 -vn 0.3965 -0.6026 0.6926 -vn 0.4780 -0.7338 0.4828 -vn 0.5286 -0.8153 0.2363 -vn 0.5433 -0.8391 -0.0280 -vn 0.5213 -0.8037 -0.2868 -vn 0.4659 -0.7148 -0.5216 -vn 0.3811 -0.5785 -0.7212 -vn 0.2698 -0.3997 -0.8760 -vn -0.1467 0.1677 -0.9748 -vn 0.0842 -0.0568 0.9948 -vn 0.1789 -0.1476 0.9727 -vn 0.2724 -0.2390 0.9320 -vn -0.1836 0.1397 -0.9730 -vn 0.1541 -0.0730 -0.9854 -vn 0.0928 -0.0370 0.9950 -vn 0.2037 -0.1090 0.9729 -vn 0.3081 -0.1760 0.9350 -vn 0.4117 -0.2413 0.8788 -vn 0.1677 -0.0563 -0.9842 -vn 0.3453 -0.1336 -0.9289 -vn -0.1987 0.1065 -0.9743 -vn -0.3111 0.1565 -0.9374 -vn -0.4251 0.2074 -0.8811 -vn -0.5660 0.2679 -0.7796 -vn -0.7234 0.3372 -0.6025 -vn -0.8435 0.3897 -0.3697 -vn -0.9031 0.4158 -0.1074 -vn -0.8964 0.4130 0.1610 -vn -0.8265 0.3824 0.4130 -vn -0.7012 0.3276 0.6333 -vn -0.5266 0.2511 0.8122 -vn -0.0203 0.0271 0.9994 -vn 0.0962 -0.0234 0.9951 -vn 0.2174 -0.0759 0.9731 -vn 0.3310 -0.1259 0.9352 -vn 0.4434 -0.1756 0.8789 -vn 0.5856 -0.2375 0.7750 -vn 0.7412 -0.3065 0.5973 -vn 0.8588 -0.3585 0.3659 -vn 0.9171 -0.3843 0.1061 -vn 0.9105 -0.3814 -0.1594 -vn 0.8421 -0.3513 -0.4092 -vn 0.7191 -0.2971 -0.6282 -vn 0.5473 -0.2215 -0.8071 -vn 0.3531 -0.0905 -0.9312 -vn -0.2101 0.0824 -0.9742 -vn -0.3293 0.1181 -0.9368 -vn -0.7366 0.2401 0.6323 -vn -0.5542 0.1849 0.8116 -vn 0.0981 -0.0093 0.9951 -vn 0.2248 -0.0480 0.9732 -vn 0.3439 -0.0834 0.9353 -vn 0.8790 -0.2444 -0.4095 -vn 0.7500 -0.2057 -0.6286 -vn 0.5699 -0.1517 -0.8075 -vn 0.3585 -0.0460 -0.9324 -vn -0.3450 0.0866 -0.9346 -vn -0.7562 0.1640 0.6334 -vn -0.5616 0.1271 0.8176 -vn -0.3558 0.0928 0.9299 -vn -0.1673 0.0478 0.9848 -vn 0.2285 -0.0231 0.9733 -vn 0.3547 -0.0470 0.9338 -vn 0.9041 -0.1524 -0.3993 -vn 0.7668 -0.1254 -0.6295 -vn 0.5830 -0.0901 -0.8074 -vn -0.3463 0.0548 -0.9365 -vn -0.4669 0.0665 -0.8818 -vn 0.2279 -0.0004 0.9737 -vn 0.3526 -0.0084 0.9357 -vn -0.3515 0.0254 -0.9358 -vn -0.2347 0.0250 -0.9717 -vn -0.4684 0.0276 -0.8831 -vn -0.5759 0.0307 -0.8169 -vn -0.7698 0.0340 -0.6374 -vn 0.3504 0.0111 0.9365 -vn 0.5774 0.0099 0.8164 -vn 0.6818 0.0122 0.7314 -vn -0.3506 0.0087 -0.9365 -vn -0.4687 0.0078 -0.8833 -vn -0.9737 0.0041 -0.2278 -vn -0.9999 0.0030 0.0143 -vn -0.3523 0.0053 -0.9359 -vn -0.2416 -0.0282 -0.9700 -vn 0.9902 0.0901 0.1063 -vn 0.9831 0.0896 -0.1594 -vn 0.9085 0.0841 -0.4093 -vn 0.7743 0.0744 -0.6285 -vn 0.3414 0.1127 -0.9331 -vn 0.3907 0.1218 0.9124 -vn 0.5990 0.1708 0.7823 -vn 0.7744 0.2119 0.5962 -vn 0.8991 0.2412 0.3652 -vn 0.9609 0.2557 0.1059 -vn 0.9540 0.2541 -0.1591 -vn 0.8814 0.2372 -0.4085 -vn 0.7509 0.2068 -0.6272 -vn 0.5686 0.1644 -0.8060 -vn -0.2346 -0.0628 -0.9701 -vn -0.5759 -0.2174 -0.7881 -vn -0.7423 -0.2928 -0.6027 -vn -0.8625 -0.3455 -0.3698 -vn -0.9221 -0.3717 -0.1074 -vn -0.9155 -0.3687 0.1611 -vn -0.8456 -0.3381 0.4132 -vn -0.7202 -0.2832 0.6334 -vn -0.5456 -0.2067 0.8122 -vn 0.3689 0.1941 0.9090 -vn 0.5579 0.2778 0.7820 -vn 0.7225 0.3504 0.5959 -vn 0.8397 0.4021 0.3650 -vn 0.8977 0.4277 0.1058 -vn 0.8911 0.4249 -0.1593 -vn 0.8228 0.3950 -0.4087 -vn 0.6999 0.3411 -0.6275 -vn 0.5284 0.2658 -0.8063 -vn -0.3559 -0.1969 -0.9135 -vn -0.5305 -0.3116 -0.7884 -vn -0.6842 -0.4118 -0.6019 -vn -0.7938 -0.4833 -0.3693 -vn -0.8482 -0.5187 -0.1071 -vn -0.8421 -0.5147 0.1612 -vn -0.7781 -0.4731 0.4131 -vn -0.6635 -0.3985 0.6332 -vn -0.5039 -0.2946 0.8120 -vn -0.3044 -0.1692 0.9374 -vn 0.0605 0.1021 0.9929 -vn 0.1626 0.2397 0.9571 -vn 0.3125 0.4147 0.8546 -vn 0.4411 0.5684 0.6945 -vn 0.5408 0.6876 0.4845 -vn 0.6028 0.7618 0.2372 -vn 0.6208 0.7835 -0.0281 -vn 0.5938 0.7513 -0.2881 -vn 0.5259 0.6703 -0.5235 -vn 0.4220 0.5463 -0.7235 -vn 0.2858 0.3839 -0.8780 -vn -0.2898 -0.4199 -0.8600 -vn -0.3967 -0.5936 -0.7002 -vn -0.4798 -0.7283 -0.4893 -vn -0.5317 -0.8123 -0.2398 -vn -0.5468 -0.8368 0.0284 -vn -0.5242 -0.8003 0.2910 -vn -0.4676 -0.7088 0.5282 -vn -0.3812 -0.5689 0.7288 -vn -0.2687 -0.3862 0.8824 -vn -0.1499 -0.1876 0.9707 -vn -0.0410 -0.0175 0.9990 -vn 0.0321 0.1196 0.9923 -vn 0.1335 0.2593 0.9565 -vn 0.2483 0.4566 0.8543 -vn 0.3528 0.6277 0.6939 -vn 0.4336 0.7601 0.4840 -vn 0.4839 0.8425 0.2368 -vn 0.4984 0.8665 -0.0284 -vn 0.4764 0.8306 -0.2882 -vn 0.4213 0.7405 -0.5236 -vn 0.3369 0.6025 -0.7235 -vn 0.2264 0.4218 -0.8780 -vn -0.2198 -0.4605 -0.8600 -vn -0.2981 -0.6489 -0.7000 -vn -0.3590 -0.7949 -0.4891 -vn -0.4014 -0.8850 -0.2357 -vn -0.3499 -0.7734 0.5286 -vn -0.2865 -0.6214 0.7292 -vn -0.2042 -0.4234 0.8826 -vn -0.1129 -0.2192 0.9691 -vn 0.0811 0.2746 0.9581 -vn 0.1733 0.4907 0.8539 -vn 0.2494 0.6761 0.6933 -vn 0.3081 0.8194 0.4834 -vn 0.3446 0.9085 0.2363 -vn 0.2963 0.8003 -0.5214 -vn 0.2375 0.6482 -0.7235 -vn 0.1570 0.4525 -0.8778 -vn -0.0064 0.1619 0.9868 -vn -0.0192 -0.0222 0.9996 -vn -0.0405 0.3501 -0.9358 -vn -0.0167 -0.1814 0.9833 -vn 0.0389 -0.3937 -0.9184 -vn 0.0630 -0.6152 -0.7859 -vn 0.0829 -0.7963 -0.5992 -vn 0.0980 -0.9332 -0.3458 -vn 0.0824 -0.7688 0.6341 -vn 0.0952 -0.9117 0.3996 -vn 0.0589 -0.5780 0.8139 -vn 0.0355 -0.3581 0.9330 -vn 0.0121 -0.1940 0.9809 -vn -0.0217 0.1673 0.9857 -vn -0.0045 -0.0044 1.0000 -vn -0.0499 0.4028 0.9139 -vn -0.0739 0.6215 0.7799 -vn -0.0937 0.7995 0.5933 -vn -0.1104 0.9331 0.3422 -vn -0.1097 0.9132 -0.3924 -vn -0.0908 0.7716 -0.6296 -vn -0.0701 0.5849 -0.8080 -vn -0.1039 0.3552 -0.9290 -vn -0.0498 0.1567 -0.9864 -vn 0.0987 -0.3821 -0.9189 -vn 0.1571 -0.5976 -0.7863 -vn 0.2049 -0.7737 -0.5995 -vn 0.2411 -0.9068 -0.3458 -vn 0.2571 -0.9659 0.0300 -vn 0.2359 -0.8872 0.3964 -vn 0.1975 -0.7455 0.6366 -vn 0.1472 -0.5607 0.8149 -vn 0.0899 -0.3471 0.9335 -vn 0.0492 -0.1828 0.9819 -vn -0.0494 0.1586 0.9861 -vn -0.1133 0.3907 0.9135 -vn -0.1714 0.6029 0.7792 -vn -0.2189 0.7752 0.5925 -vn -0.2547 0.9052 0.3403 -vn -0.2705 0.9623 -0.0294 -vn -0.2495 0.8862 -0.3904 -vn -0.2114 0.7477 -0.6295 -vn -0.1617 0.5669 -0.8078 -vn 0.1663 -0.3568 -0.9193 -vn 0.2636 -0.5583 -0.7866 -vn 0.3422 -0.7242 -0.5987 -vn -0.0748 0.1460 0.9864 -vn -0.1844 0.3636 0.9131 -vn -0.2807 0.5613 0.7785 -vn -0.3591 0.7217 0.5918 -vn -0.1926 0.1445 -0.9706 -vn 0.7624 -0.6471 0.0062 -vn 0.6504 -0.5431 0.5311 -vn 0.7305 -0.6122 0.3026 -vn 0.5179 -0.4381 0.7347 -vn 0.3529 -0.3020 0.8856 -vn 0.1718 -0.1467 0.9742 -vn -0.7739 0.6334 -0.0026 -vn -0.7436 0.6012 -0.2925 -vn -0.6605 0.5364 -0.5254 -vn -0.5354 0.4328 -0.7253 -vn -0.3736 0.2995 -0.8779 -vn -0.2202 0.1078 -0.9695 -vn -0.0682 0.0180 -0.9975 -vn 0.4492 -0.2423 -0.8600 -vn 0.6439 -0.3432 -0.6838 -vn 0.8214 -0.4357 -0.3679 -vn 0.8840 -0.4674 0.0090 -vn 0.8443 -0.4469 0.2957 -vn 0.7469 -0.3964 0.5338 -vn 0.5983 -0.3193 0.7349 -vn 0.4087 -0.2214 0.8854 -vn -0.1212 0.0424 0.9917 -vn -0.2585 0.1328 0.9568 -vn -0.4668 0.2346 0.8527 -vn -0.6585 0.3344 0.6742 -vn -0.8313 0.4248 0.3584 -vn -0.8903 0.4552 -0.0087 -vn -0.8521 0.4352 -0.2908 -vn -0.7578 0.3860 -0.5261 -vn -0.6133 0.3105 -0.7263 -vn -0.4273 0.2140 -0.8784 -vn 0.4886 -0.1509 -0.8594 -vn -0.2325 0.0568 -0.9709 -vn 0.6986 -0.2127 -0.6832 -vn 0.8901 -0.2723 -0.3654 -vn 0.8090 -0.2469 0.5335 -vn 0.6483 -0.1996 0.7347 -vn 0.4435 -0.1405 0.8852 -vn -0.2738 0.0711 0.9592 -vn -0.5020 0.1423 0.8531 -vn -0.7092 0.2042 0.6748 -vn -0.8962 0.2606 0.3590 -vn -0.8169 0.2353 -0.5265 -vn -0.6598 0.1890 -0.7272 -vn -0.4589 0.1293 -0.8790 -vn 0.9229 -0.1172 -0.3669 -vn 0.8537 -0.1135 0.5082 -vn 0.6807 -0.0912 0.7269 -vn 0.4708 -0.0699 0.8795 -vn -0.5274 0.0593 0.8476 -vn -0.7411 0.0866 0.6658 -vn -0.9402 0.1085 0.3228 -vn -0.8473 0.0986 -0.5219 -vn -0.6732 0.0776 -0.7354 -vn -0.4652 0.0512 -0.8837 -vn 0.9353 -0.0338 -0.3522 -vn -0.9894 0.0143 -0.1447 -vn -0.8590 0.0093 -0.5119 -vn -0.6840 0.0060 -0.7294 -vn -0.4730 0.0008 -0.8811 -vn -0.9998 -0.0075 -0.0166 -vn -0.8492 -0.0056 -0.5280 -vn -0.9555 -0.0070 -0.2949 -vn -0.6943 -0.0081 -0.7196 -vn -0.5477 -0.0097 -0.8366 -vn -0.4267 -0.0093 -0.9044 -vn -0.4240 -0.0020 -0.9057 -vn 0.6928 -0.0339 0.7203 -vn 0.6800 0.0261 0.7328 -vn 0.4657 0.2451 0.8503 -vn 0.2230 0.2406 0.9447 -vn -0.0901 0.4200 0.9031 -vn 0.1061 0.6025 0.7910 -vn -0.9290 -0.3686 -0.0334 -vn -0.6857 -0.5701 -0.4525 -vn -0.4823 -0.6298 -0.6089 -vn -0.2061 0.3963 0.8947 -vn 0.0602 0.4598 0.8860 -vn -0.1101 -0.2554 -0.9605 -vn 0.1581 -0.2176 -0.9632 -vn 0.1190 0.2707 0.9553 -vn 0.8457 0.2637 0.4639 -vn -0.1956 -0.0636 -0.9786 -vn -0.3233 -0.0630 -0.9442 -vn -0.0457 -0.0579 -0.9973 -vn 0.8564 0.0847 0.5093 -vn 0.6970 0.0861 0.7119 -vn 0.5056 0.0823 0.8588 -vn 0.3197 0.0759 0.9445 -vn 0.1405 0.0679 0.9877 -vn 0.3312 -0.0216 0.9433 -vn 0.1972 -0.0251 0.9800 -vn -0.1935 0.0065 -0.9811 -vn -0.3303 0.0098 -0.9438 -vn -0.0537 0.0084 -0.9985 -vn 0.2025 -0.0217 0.9790 -vn 0.1124 0.0602 0.9918 -vn -0.1149 -0.0710 0.9908 -vn 0.0974 0.0903 -0.9911 -vn -0.0806 -0.1023 -0.9915 -vn 0.2716 0.2460 0.9304 -vn 0.0891 0.0810 0.9927 -vn -0.1113 -0.1043 0.9883 -vn 0.0797 0.1142 -0.9903 -vn 0.2815 0.4009 -0.8718 -vn 0.4958 0.6994 -0.5149 -vn 0.5828 0.8126 0.0054 -vn 0.5057 0.7043 0.4982 -vn 0.3582 0.4979 0.7898 -vn -0.3031 -0.4162 0.8573 -vn -0.5060 -0.7003 0.5036 -vn -0.1701 -0.3414 -0.9244 -vn -0.1312 -0.3788 -0.9161 -vn -0.0034 0.7132 -0.7010 -vn -0.0069 0.9272 -0.3746 -vn -0.0086 0.4872 -0.8733 -vn -0.0070 0.9906 0.1368 -vn -0.0055 0.7962 0.6050 -vn 0.0039 -0.9288 0.3707 -vn 0.0017 -0.7229 0.6909 -vn 0.0093 -0.9910 -0.1333 -vn 0.0072 -0.8044 -0.5941 -vn 0.0088 -0.5139 -0.8578 -vn -0.0455 0.2562 -0.9655 -vn -0.0770 0.4813 -0.8732 -vn 0.0403 -0.2482 -0.9679 -vn 0.0805 -0.4925 0.8666 -vn 0.0033 -0.0081 -1.0000 -vn 0.0794 -0.2559 -0.9634 -vn -0.0746 0.2457 -0.9665 -vn -0.1490 0.4793 -0.8649 -vn 0.1542 -0.4901 0.8579 -vn -0.1215 0.2413 -0.9628 -vn -0.2156 0.4553 -0.8639 -vn 0.1160 -0.2414 -0.9635 -vn -0.3391 0.7154 -0.6109 -vn -0.4238 0.8950 -0.1388 -vn -0.3932 0.8305 0.3944 -vn -0.2600 0.5502 0.7935 -vn 0.2196 -0.4635 0.8585 -vn 0.3428 -0.7228 0.6001 -vn 0.4245 -0.8953 0.1352 -vn 0.3951 -0.8336 -0.3859 -vn 0.2660 -0.5622 -0.7831 -vn -0.0784 0.0977 -0.9921 -vn 0.0814 -0.0937 -0.9923 -vn -0.0561 0.0759 0.9955 -vn 0.0978 -0.1124 0.9888 -vn 0.0982 -0.0758 -0.9923 -vn 0.3214 -0.2406 -0.9159 -vn -0.5354 0.5876 -0.6067 -vn 0.5459 -0.5864 0.5984 -vn -0.1065 0.0793 -0.9911 -vn -0.2967 0.2231 -0.9285 -vn -0.4737 0.3559 -0.8056 -vn -0.6344 0.4768 -0.6085 -vn -0.7735 0.5820 -0.2510 -vn -0.7703 0.5797 0.2657 -vn -0.5664 0.4264 0.7053 -vn -0.3083 0.2313 0.9227 -vn -0.0838 0.0661 0.9943 -vn 0.1201 -0.0877 0.9889 -vn 0.4832 -0.3619 0.7972 -vn 0.3081 -0.2316 0.9227 -vn 0.6410 -0.4800 0.5989 -vn 0.7762 -0.5815 0.2435 -vn 0.7734 -0.5794 -0.2573 -vn 0.5788 -0.4341 -0.6903 -vn 0.1122 -0.0535 -0.9922 -vn 0.3643 -0.1688 -0.9159 -vn -0.1219 0.0552 -0.9910 -vn -0.3358 0.1566 -0.9288 -vn -0.5356 0.2501 -0.8066 -vn -0.7182 0.3351 -0.6099 -vn -0.8769 0.4088 -0.2528 -vn -0.8740 0.4070 0.2656 -vn -0.6420 0.2990 0.7060 -vn -0.3485 0.1611 0.9233 -vn -0.0938 0.0454 0.9946 -vn 0.1372 -0.0614 0.9886 -vn 0.3500 -0.1636 0.9224 -vn 0.5469 -0.2550 0.7974 -vn 0.7254 -0.3386 0.5993 -vn 0.8787 -0.4104 0.2438 -vn 0.8757 -0.4092 -0.2564 -vn 0.6560 -0.3070 -0.6895 -vn -0.1274 0.0254 -0.9915 -vn -0.3627 0.0763 -0.9288 -vn -0.5805 0.1250 -0.8046 -vn -0.7813 0.1634 -0.6024 -vn -0.9516 0.1957 -0.2370 -vn -0.9391 0.1919 0.2851 -vn -0.6793 0.1410 0.7202 -vn -0.3820 0.0883 0.9199 -vn 0.3775 -0.0809 0.9224 -vn 0.5840 -0.1267 0.8018 -vn 0.7743 -0.1669 0.6104 -vn 0.9425 -0.2062 0.2629 -vn 0.9483 -0.2094 -0.2386 -vn 0.7183 -0.1585 -0.6775 -vn -0.2204 0.0176 -0.9753 -vn -0.4011 0.0181 -0.9159 -vn -0.5972 0.0314 -0.8015 -vn -0.7796 0.0377 -0.6252 -vn 0.9232 -0.0486 0.3813 -vn 0.9918 -0.0546 0.1158 -vn 0.9880 -0.0537 -0.1448 -vn 0.9146 -0.0503 -0.4013 -vn 0.7870 -0.0426 -0.6155 -vn 0.6003 -0.0295 -0.7992 -vn -0.6048 0.0121 -0.7963 -vn -0.4517 0.0080 -0.8922 -vn 0.3870 -0.0072 0.9220 -vn 0.5970 -0.0112 0.8022 -vn 0.7838 -0.0144 0.6208 -vn 0.9199 -0.0166 0.3917 -vn 0.9910 -0.0180 0.1323 -vn 0.9907 -0.0179 -0.1349 -vn 0.9217 -0.0166 -0.3876 -vn 0.7918 -0.0141 -0.6106 -vn 0.6074 -0.0105 -0.7943 -vn -0.6148 0.0132 -0.7886 -vn 0.1954 -0.0032 0.9807 -vn 0.7851 -0.0125 -0.6193 -vn -0.5981 -0.0568 -0.7994 -vn 0.7972 0.0062 0.6036 -vn -0.3730 -0.0539 -0.9263 -vn 0.3794 0.0524 0.9237 -vn 0.5977 0.0693 0.7987 -vn 0.7947 0.0834 0.6012 -vn 0.9646 0.0926 0.2468 -vn 0.9630 0.0823 -0.2566 -vn -0.5728 -0.1875 -0.7980 -vn -0.7702 -0.2191 -0.5990 -vn -0.9420 -0.2377 -0.2372 -vn -0.9431 -0.2061 0.2609 -vn 0.5773 0.1904 0.7940 -vn 0.7723 0.2206 0.5957 -vn 0.9410 0.2366 0.2420 -vn 0.5948 0.1148 -0.7956 -vn -0.5365 -0.2968 -0.7900 -vn 0.8992 0.3709 0.2320 -vn -0.5081 -0.4039 -0.7607 -vn -0.3017 -0.3149 -0.8999 -vn 0.3185 0.3293 0.8889 -vn 0.8477 0.4763 0.2336 -vn -0.6345 -0.5681 -0.5241 -vn -0.8061 -0.5915 -0.0173 -vn -0.7463 -0.6118 -0.2623 -vn -0.8140 -0.5310 0.2354 -vn -0.5844 -0.6878 -0.4307 -vn -0.6676 -0.7231 -0.1776 -vn -0.7031 -0.7030 0.1068 -vn -0.6503 -0.5991 0.4672 -vn 0.7155 0.6979 0.0308 -vn 0.5511 0.7192 0.4230 -vn 0.6171 0.7687 0.1683 -vn 0.6340 0.6015 -0.4860 -vn -0.5749 -0.8015 -0.1646 -vn -0.5903 -0.8004 0.1046 -vn -0.5660 -0.7412 0.3610 -vn -0.4950 -0.6322 0.5961 -vn -0.4554 -0.8849 -0.0975 -vn -0.4469 -0.8776 0.1736 -vn -0.4061 -0.8072 0.4284 -vn -0.3372 -0.6814 0.6496 -vn -0.2481 -0.5051 0.8266 -vn 0.1085 0.2144 0.9707 -vn -0.3085 -0.9507 -0.0302 -vn -0.2923 -0.9267 0.2364 -vn -0.2557 -0.8382 0.4817 -vn -0.2021 -0.6941 0.6909 -vn -0.1322 -0.5035 0.8538 -vn -0.0632 -0.2844 0.9566 -vn 0.3046 0.9525 0.0030 -vn 0.1355 0.5181 -0.8445 -vn -0.1748 -0.9840 -0.0340 -vn -0.1623 -0.9589 0.2326 -vn -0.1388 -0.8673 0.4781 -vn -0.1061 -0.7180 0.6879 -vn -0.0816 -0.5427 0.8359 -vn 0.0719 0.2147 0.9740 -vn 0.1760 0.9838 0.0334 -vn 0.1582 0.9615 -0.2246 -vn 0.1320 0.8686 -0.4776 -vn 0.1069 0.7229 -0.6826 -vn -0.0273 -0.3098 -0.9504 -vn -0.0165 -0.5410 0.8408 -vn 0.0250 0.2212 0.9749 -vn 0.0049 -0.9039 -0.4276 -vn -0.0269 -0.3457 -0.9380 -vn -0.0415 -0.0819 -0.9958 -vn 0.0563 -0.5407 0.8393 -vn -0.0665 0.5479 -0.8339 -vn -0.1619 -0.0783 -0.9837 -vn -0.1037 -0.3397 -0.9348 -vn 0.1901 -0.9785 0.0799 -vn 0.2446 -0.8119 0.5301 -vn 0.2415 -0.5301 0.8128 -vn 0.2172 -0.2841 0.9339 -vn -0.0986 0.9161 0.3887 -vn -0.2034 0.9724 -0.1140 -vn -0.2521 0.7854 -0.5653 -vn -0.2456 0.5015 -0.8295 -vn -0.4358 0.2229 -0.8720 -vn 0.3893 -0.9206 0.0300 -vn 0.4919 -0.7285 0.4768 -vn 0.4850 -0.4463 0.7521 -vn 0.4340 -0.2008 0.8782 -vn 0.5975 -0.1208 0.7927 -vn 0.4967 0.1080 0.8612 -vn 0.3661 0.3268 0.8713 -vn -0.6198 0.0381 -0.7838 -vn -0.4046 0.2433 -0.8815 -vn 0.1501 -0.8819 -0.4469 -vn 0.4528 -0.4616 0.7628 -vn 0.3999 -0.2293 0.8874 -vn -0.4580 0.4740 -0.7520 -vn -0.2115 0.2901 -0.9333 -vn -0.1620 0.0445 -0.9858 -vn -0.0967 -0.2028 -0.9744 -vn 0.2547 -0.9656 0.0527 -vn 0.2870 -0.8012 0.5251 -vn 0.2602 -0.5404 0.8002 -vn 0.2169 -0.2972 0.9299 -vn 0.1661 -0.0552 0.9846 -vn 0.0991 0.1884 0.9771 -vn -0.1539 0.8654 0.4769 -vn -0.2534 0.5191 -0.8163 -vn -0.1397 0.3064 -0.9416 -vn -0.1134 0.0575 -0.9919 -vn -0.0699 -0.2069 -0.9759 -vn 0.1090 -0.0657 0.9919 -vn 0.0636 0.1992 0.9779 -vn -0.0786 0.8829 0.4630 -vn -0.1623 0.5501 -0.8192 -vn -0.2119 -0.4515 -0.8667 -vn 0.2717 -0.0465 0.9613 -vn 0.2417 0.2372 0.9409 -vn 0.1684 0.5955 0.7855 -vn 0.0399 0.9036 0.4266 -vn -0.4218 -0.4716 -0.7744 -vn -0.2879 -0.7722 -0.5664 -vn 0.4899 -0.3024 0.8177 -vn 0.5012 -0.0300 0.8648 -vn 0.4825 0.2394 0.8425 -vn 0.3921 0.5920 0.7041 -vn 0.6392 -0.0196 0.7688 -vn 0.6418 0.2290 0.7319 -vn 0.1128 -0.9598 0.2571 -vn 0.4583 -0.5293 0.7140 -vn 0.5288 -0.2762 0.8025 -vn 0.5551 -0.0204 0.8315 -vn 0.5477 0.2363 0.8026 -vn -0.3920 0.1375 -0.9096 -vn 0.2209 -0.7945 0.5656 -vn 0.3287 -0.5211 0.7876 -vn 0.3786 -0.2667 0.8863 -vn 0.3957 -0.0157 0.9182 -vn 0.3907 0.2525 0.8852 -vn -0.1932 -0.2802 -0.9403 -vn -0.1592 -0.6201 -0.7682 -vn -0.1122 -0.8885 -0.4450 -vn 0.1701 -0.5068 0.8451 -vn 0.1924 -0.2532 0.9481 -vn 0.1994 -0.0130 0.9798 -vn 0.1949 0.2430 0.9503 -vn -0.0038 -0.2537 -0.9673 -vn 0.0100 -0.6015 -0.7988 -vn 0.0291 -0.9096 -0.4146 -vn 0.0217 -0.2674 0.9633 -vn 0.0163 -0.0139 0.9998 -vn 0.0059 0.2411 0.9705 -vn 0.0952 0.3029 -0.9482 -vn -0.0983 -0.2935 0.9509 -vn 0.1061 0.9056 -0.4106 -vn 0.4001 0.6806 -0.6138 -vn -0.5294 0.8484 -0.0027 -vn 0.9449 0.2858 -0.1596 -vn 0.8441 0.5071 -0.1740 -vn 0.8184 -0.5737 -0.0326 -vn 0.9017 -0.4291 -0.0534 -vn 0.9824 -0.1436 -0.1194 -vn 0.9527 -0.2879 -0.0978 -vn 0.4291 -0.9016 0.0544 -vn 0.1903 -0.9774 0.0921 -vn -0.1223 -0.9842 0.1284 -vn -0.9779 -0.1500 0.1459 -vn -0.9901 0.0714 0.1207 -vn -0.7661 -0.6196 0.1710 -vn -0.5726 0.8193 -0.0284 -vn -0.7563 0.6541 0.0132 -vn -0.9566 0.2786 0.0860 -vn -0.3421 0.9364 -0.0776 -vn 0.3220 0.9343 -0.1529 -vn -0.0559 0.9913 -0.1193 -vn 0.6543 0.7339 -0.1824 -vn 0.9672 0.1027 0.2323 -vn 0.9939 0.0995 0.0474 -vn 0.9224 -0.3765 0.0860 -vn 0.6353 -0.7636 0.1155 -vn -0.8461 0.5325 0.0218 -vn -0.4738 0.8647 0.1666 -vn -0.4435 0.8962 -0.0087 -vn -0.0000 0.0787 0.9969 -vn 0.5598 0.7144 0.4199 -vn -0.0863 0.9106 0.4041 -vn 0.7772 0.4489 0.4409 -vn 0.2564 0.8767 0.4069 -vn -0.4174 0.8086 0.4147 -vn 0.8745 0.1246 0.4687 -vn -0.7865 -0.6161 0.0424 -vn -0.9630 -0.2690 0.0150 -vn 0.9528 0.1496 0.2643 -vn -0.7247 0.6510 0.2260 -vn -0.8920 0.3856 0.2359 -vn 0.2828 0.9377 0.2018 -vn -0.4492 0.8690 0.2077 -vn 0.6127 0.7604 0.2156 -vn -0.0901 0.9758 0.1990 -vn 0.8531 0.4664 0.2339 -vn 0.8813 -0.4721 0.0221 -vn 0.9994 0.0296 -0.0180 -vn -0.9928 0.1152 -0.0329 -vn 0.6206 -0.7821 0.0558 -vn -0.8310 0.5521 -0.0678 -vn 0.2711 -0.9600 0.0699 -vn 0.2701 0.9594 -0.0818 -vn -0.4952 0.8648 -0.0831 -vn -0.1183 -0.9903 0.0722 -vn 0.6185 0.7829 -0.0678 -vn -0.1195 0.9893 -0.0843 -vn -0.4899 -0.8696 0.0625 -vn 0.8792 0.4744 -0.0444 -vn 0.3148 0.5769 -0.7537 -vn 0.6619 0.1857 -0.7262 -vn 0.9828 0.0490 -0.1782 -vn 0.6642 -0.2458 -0.7060 -vn 0.1805 0.4439 -0.8777 -vn 0.1972 -0.4458 -0.8731 -vn -0.1702 -0.7573 0.6304 -vn 0.1764 0.4803 0.8592 -vn -0.3165 -0.2661 -0.9105 -vn -0.1247 -0.0997 -0.9872 -vn -0.2478 -0.3317 -0.9102 -vn -0.5546 -0.4652 -0.6899 -vn -0.7405 -0.6209 -0.2572 -vn -0.7383 -0.6191 0.2675 -vn -0.5491 -0.4608 0.6972 -vn -0.3043 -0.2516 0.9188 -vn 0.3523 0.1759 0.9192 -vn 0.0779 0.0366 -0.9963 -vn -0.1419 -0.0694 -0.9875 -vn -0.3684 -0.1853 -0.9110 -vn -0.6447 -0.3241 -0.6923 -vn -0.8628 -0.4341 -0.2591 -vn -0.8607 -0.4333 0.2672 -vn -0.6397 -0.3199 0.6989 -vn 0.3710 0.1013 0.9231 -vn 0.5892 0.1656 0.7908 -vn -0.3927 -0.1128 -0.9127 -vn -0.1514 -0.0400 -0.9877 -vn 0.3834 0.0610 0.9216 -vn 0.1392 0.0162 0.9901 -vn 0.6119 0.0931 0.7855 -vn 0.8609 0.1310 0.4916 -vn 0.9886 0.1504 -0.0035 -vn 0.3571 0.0465 -0.9329 -vn 0.1045 0.0138 -0.9944 -vn -0.1450 -0.0208 -0.9892 -vn -0.3850 -0.0594 -0.9210 -vn -0.6158 -0.0908 -0.7826 -vn -0.3678 -0.0468 0.9287 -vn -0.6177 -0.0760 0.7827 -vn 0.4014 0.0293 0.9154 -vn 0.6179 0.0499 0.7847 -vn 0.8686 0.0706 0.4905 -vn 0.9967 0.0809 -0.0034 -vn -0.3843 -0.0323 -0.9227 -vn -0.6209 -0.0502 -0.7822 -vn -0.8705 -0.0711 -0.4871 -vn -0.9967 -0.0814 0.0035 -vn -0.8581 -0.0643 0.5094 -vn -0.1119 -0.0042 0.9937 -vn 0.1425 0.0025 0.9898 -vn 0.8744 0.0356 0.4838 -vn 0.9993 0.0370 -0.0034 -vn 0.3656 0.0063 -0.9307 -vn 0.1051 0.0034 -0.9945 -vn -0.9993 -0.0371 0.0034 -vn -0.8635 -0.0372 0.5030 -vn -0.6236 -0.0115 0.7817 -vn -0.3693 -0.0066 0.9293 -vn 0.8752 -0.0030 0.4838 -vn 1.0000 0.0005 -0.0033 -vn -1.0000 -0.0004 0.0033 -vn -0.8630 0.0049 0.5052 -vn 0.6198 -0.0242 0.7844 -vn 0.8706 -0.0352 0.4907 -vn 0.9992 -0.0407 -0.0033 -vn 0.1034 -0.0062 -0.9946 -vn -0.3784 0.0141 -0.9255 -vn -0.6216 0.0238 -0.7830 -vn -0.9992 0.0387 0.0033 -vn -0.8724 0.0337 -0.4877 -vn -0.8609 0.0271 0.5080 -vn -0.6414 0.0455 0.7659 -vn 0.6256 -0.0606 0.7778 -vn 0.8753 -0.0848 0.4762 -vn 0.9951 -0.0964 -0.0226 -vn -0.6105 0.0600 -0.7897 -vn -0.8613 0.0837 -0.5012 -vn -0.9952 0.0967 -0.0154 -vn -0.8723 0.0914 0.4804 -vn 0.1276 -0.0321 0.9913 -vn -0.1222 0.0452 0.9915 -vn 0.5974 -0.1115 0.7942 -vn 0.8553 -0.1598 0.4928 -vn 0.9831 -0.1832 -0.0031 -vn 0.8395 -0.1564 -0.5204 -vn 0.1097 -0.0313 -0.9935 -vn -0.6050 0.1147 -0.7879 -vn -0.1395 0.0445 -0.9892 -vn -0.8588 0.1640 -0.4853 -vn -0.9822 0.1877 0.0034 -vn -0.8558 0.1627 0.4911 -vn 0.3645 -0.1222 0.9231 -vn -0.3717 0.1254 -0.9199 -vn 0.1282 -0.0687 0.9894 -vn -0.0970 0.0822 0.9919 -vn 0.3467 -0.1894 0.9187 -vn 0.3092 -0.2040 -0.9289 -vn -0.3582 0.1992 -0.9122 -vn 0.5351 -0.4582 0.7097 -vn 0.7307 -0.6271 0.2697 -vn 0.7290 -0.6259 -0.2770 -vn 0.5282 -0.4690 -0.7079 -vn -0.3153 0.2657 -0.9110 -vn -0.5489 0.4677 -0.6928 -vn -0.7351 0.6263 -0.2596 -vn -0.7334 0.6250 0.2673 -vn 0.4344 -0.5607 0.7049 -vn 0.6026 -0.7522 0.2665 -vn 0.6006 -0.7504 -0.2760 -vn 0.4475 -0.5458 -0.7084 -vn -0.2665 0.3210 -0.9088 -vn -0.4535 0.5605 -0.6929 -vn -0.6076 0.7508 -0.2591 -vn -0.6057 0.7489 0.2688 -vn 0.4777 -0.8346 -0.2742 -vn 0.3409 -0.6126 -0.7131 -vn -0.0445 0.0961 -0.9944 -vn -0.0026 0.0341 -0.9994 -vn -0.0005 0.2946 -0.9556 -vn -0.0784 -0.9860 -0.1474 -vn -0.0536 -0.7919 -0.6083 -vn 0.0534 0.6198 -0.7829 -vn 0.0720 0.9221 -0.3801 -vn 0.0772 0.9866 0.1438 -vn -0.4287 -0.8572 -0.2854 -vn -0.3258 -0.6242 -0.7101 -vn 0.4331 0.8579 0.2764 -vn -0.5469 -0.7968 0.2569 -vn -0.5428 -0.7904 -0.2840 -vn -0.3917 -0.5873 -0.7083 -vn -0.2376 -0.2898 -0.9271 -vn 0.4153 0.5901 -0.6923 -vn 0.5496 0.7969 -0.2508 -vn 0.5457 0.7908 0.2771 -vn -0.3065 -0.2294 -0.9238 -vn -0.4807 -0.3511 -0.8036 -vn 0.4977 0.3564 0.7908 -vn 0.7839 0.3802 -0.4909 -vn 0.9015 0.4325 0.0170 -vn 0.7816 0.3749 0.4986 -vn 0.5462 0.2607 0.7960 -vn 0.9264 0.0824 -0.3673 -vn 0.3220 0.0307 0.9463 -vn 0.2501 0.0304 0.9677 -vn 0.4916 0.0501 0.8694 -vn -0.3217 -0.2688 -0.9079 -vn -0.0985 -0.1795 -0.9788 -vn -0.4387 -0.1122 0.8916 -vn -0.3136 0.5825 0.7499 -vn -0.3635 0.4310 0.8259 -vn -0.4192 0.1574 0.8941 -vn -0.4326 0.0318 0.9010 -vn -0.3922 0.2897 0.8731 -vn -0.4306 -0.3619 0.8268 -vn -0.4409 -0.2299 0.8676 -vn -0.4362 -0.1009 0.8942 -vn 0.4115 -0.1690 -0.8956 -vn 0.3888 -0.3044 -0.8696 -vn -0.4001 -0.5246 0.7515 -vn 0.1686 -0.8510 -0.4974 -vn 0.3524 -0.4782 -0.8045 -vn 0.4376 0.0943 -0.8942 -vn 0.4312 -0.0330 -0.9017 -vn 0.4352 0.2251 -0.8717 -vn 0.4278 0.3400 -0.8375 -vn 0.3794 0.6263 -0.6810 -vn 0.3178 0.7924 -0.5207 -vn 0.4202 0.4686 -0.7771 -vn -0.2214 0.7610 0.6098 -vn 0.2151 0.8171 -0.5348 -vn 0.2983 0.6365 -0.7113 -vn 0.3083 0.5444 -0.7801 -vn 0.1953 -0.8936 -0.4041 -vn 0.0839 0.3534 0.9317 -vn 0.1469 0.1210 -0.9817 -vn -0.0935 -0.3706 -0.9241 -vn -0.2442 -0.3101 0.9188 -vn 0.2103 0.2786 0.9371 -vn 0.4088 0.5313 0.7420 -vn 0.5800 0.7503 0.3171 -vn 0.5963 0.7703 -0.2262 -vn -0.0548 -0.0830 -0.9950 -vn -0.2160 -0.2951 -0.9307 -vn -0.4137 -0.5479 -0.7271 -vn -0.5786 -0.7553 -0.3079 -vn -0.5978 -0.7717 0.2170 -vn -0.4576 -0.5866 0.6682 -vn 0.2703 0.2362 0.9334 -vn 0.1230 0.0951 0.9878 -vn 0.3304 0.1667 0.9290 -vn 0.4961 0.2735 0.8240 -vn -0.5067 -0.2738 -0.8175 -vn 0.5458 0.1807 0.8182 -vn 0.7987 0.2639 0.5408 -vn 0.9484 0.3131 0.0511 -vn 0.8351 0.2762 -0.4757 -vn -0.5532 -0.1862 -0.8120 -vn -0.3278 -0.1109 -0.9382 -vn -0.8049 -0.2692 -0.5288 -vn -0.9471 -0.3170 -0.0491 -vn -0.8398 -0.2812 0.4644 -vn -0.5267 -0.1840 0.8299 -vn 0.5663 0.1033 0.8177 -vn 0.8284 0.1514 0.5393 -vn 0.9825 0.1793 0.0513 -vn 0.8664 0.1585 -0.4735 -vn -0.5731 -0.1062 -0.8126 -vn -0.3309 -0.0559 -0.9420 -vn -0.8331 -0.1543 -0.5311 -vn -0.9820 -0.1821 -0.0499 -vn -0.8703 -0.1626 0.4649 -vn -0.5395 -0.0960 0.8365 -vn 0.8408 0.0777 0.5357 -vn 0.9949 0.0865 0.0512 -vn 0.4114 0.0278 -0.9110 -vn -0.3403 -0.0283 -0.9399 -vn -0.9949 -0.0877 -0.0504 -vn -0.8885 -0.0861 0.4508 -vn -0.6624 -0.0359 0.7483 -vn -0.4161 -0.0218 0.9091 -vn 0.8469 0.0063 0.5317 -vn -0.3577 -0.0043 -0.9338 -vn -0.8912 -0.0140 0.4534 -vn -0.1691 0.0071 0.9856 -vn -0.3432 0.0235 -0.9390 -vn -0.5521 0.0350 -0.8330 -vn -0.4370 0.0236 0.8991 -vn 0.3282 -0.0695 0.9420 -vn 0.1476 -0.0510 -0.9877 -vn -0.5748 0.0885 -0.8135 -vn -0.5642 0.1473 -0.8124 -vn 0.3496 -0.1384 -0.9266 -vn -0.8198 0.2145 -0.5310 -vn -0.9660 0.2535 -0.0506 -vn 0.3294 -0.1296 0.9352 -vn 0.7615 -0.3490 0.5462 -vn 0.5211 -0.2289 0.8222 -vn 0.9157 -0.3998 0.0408 -vn 0.8260 -0.3310 -0.4563 -vn 0.4660 -0.2729 -0.8416 -vn -0.5371 0.2325 -0.8108 -vn -0.7789 0.3376 -0.5285 -vn -0.9162 0.3976 -0.0494 -vn -0.8129 0.3548 0.4619 -vn 0.4589 -0.3169 0.8300 -vn 0.2791 -0.2024 0.9387 -vn -0.0683 0.0863 -0.9939 -vn -0.4805 0.3373 -0.8095 -vn -0.6956 0.4890 -0.5263 -vn -0.8170 0.5746 -0.0485 -vn -0.7252 0.5101 0.4624 -vn 0.1963 -0.2612 0.9451 -vn -0.2374 0.2567 -0.9369 -vn -0.1774 0.3132 -0.9330 -vn -0.2582 0.6319 -0.7308 -vn -0.3527 0.8833 -0.3087 -vn -0.3616 0.9063 0.2190 -vn -0.0606 -0.6732 0.7370 -vn -0.1023 -0.7242 -0.6820 -vn 0.0908 0.6613 -0.7446 -vn 0.2588 0.8745 -0.4102 -vn 0.2933 0.9516 0.0922 -vn -0.3898 -0.5149 -0.7635 -vn 0.5560 0.5431 -0.6292 -vn -0.7456 -0.5786 -0.3306 -vn -0.5033 -0.3833 -0.7745 -vn -0.6387 -0.4906 -0.5928 -vn -0.3248 -0.2481 -0.9127 -vn 0.7240 0.6280 -0.2855 -vn -0.4951 -0.2986 0.8159 -vn 0.2997 0.1714 -0.9385 -vn 0.7861 0.5486 -0.2849 -vn 0.8056 0.5472 0.2272 -vn -0.7451 -0.3165 -0.5871 -vn -0.5789 -0.2466 -0.7772 -vn -0.3868 -0.1625 -0.9077 -vn 0.8595 0.4231 -0.2868 -vn 0.8683 0.4401 0.2291 -vn -0.3184 -0.0490 0.9467 -vn -0.0994 0.0733 0.9923 -vn 0.5298 0.0686 0.8453 -vn -0.0453 -0.1426 -0.9887 -vn -0.9085 -0.2866 0.3041 -vn -0.9083 -0.3712 -0.1928 -vn -0.6761 -0.3613 -0.6421 -vn 0.3789 0.2829 0.8811 -vn -0.5519 0.0803 0.8300 -vn -0.5514 -0.0486 0.8328 -vn -0.5007 0.4069 0.7640 -vn -0.5356 0.2301 0.8125 -vn -0.5212 -0.3210 0.7908 -vn -0.5430 -0.1779 0.8206 -vn 0.0640 -0.9937 -0.0916 -vn -0.1498 -0.9602 0.2356 -vn -0.4779 -0.4837 0.7332 -vn 0.2680 -0.8790 -0.3944 -vn 0.4716 -0.5239 -0.7093 -vn 0.3930 -0.7061 -0.5890 -vn 0.5483 -0.0844 -0.8320 -vn 0.5385 -0.2091 -0.8163 -vn 0.5210 -0.3518 -0.7777 -vn 0.5410 0.1775 -0.8221 -vn 0.5246 0.2964 -0.7981 -vn 0.5527 0.0480 -0.8320 -vn 0.3500 0.7673 -0.5374 -vn 0.4454 0.5885 -0.6748 -vn 0.5001 0.4302 -0.7516 -vn -0.7294 -0.2158 0.6492 -vn -0.3325 0.7987 0.5014 -vn 0.3292 0.2182 -0.9187 -vn 0.2511 -0.0781 0.9648 -vn 0.0527 -0.3589 0.9319 -vn 0.1938 -0.6973 0.6901 -vn 0.0888 -0.9581 0.2722 -vn 0.2409 -0.3648 0.8994 -vn 0.3626 0.2433 0.8996 -vn 0.4041 -0.0635 0.9125 -vn 0.2870 0.4786 0.8298 -vn -0.3911 -0.0579 -0.9185 -vn -0.4193 0.2049 -0.8844 -vn -0.3151 -0.4043 -0.8587 -vn 0.2573 0.5394 0.8017 -vn -0.5571 0.1744 -0.8119 -vn -0.4882 -0.0685 -0.8701 -vn 0.1243 0.1722 0.9772 -vn 0.3740 0.0981 0.9222 -vn -0.1933 -0.2870 -0.9382 -vn -0.1545 -0.1571 -0.9754 -vn 0.1024 -0.2147 -0.9713 -vn 0.6094 0.0497 0.7913 -vn 0.3754 0.0765 0.9237 -vn -0.1360 0.0982 0.9858 -vn 0.1356 0.0894 0.9867 -vn -0.6225 -0.0526 -0.7808 -vn -0.3805 -0.0755 -0.9217 -vn -0.1403 -0.0877 -0.9862 -vn 0.1248 -0.0996 -0.9872 -vn 0.4900 -0.1011 -0.8659 -vn 0.8581 -0.0807 -0.5071 -vn 0.9992 -0.0369 0.0127 -vn 0.8667 0.0115 0.4987 -vn 0.6089 0.1066 0.7860 -vn 0.3701 0.0808 0.9255 -vn -0.1477 -0.0574 -0.9874 -vn 0.1198 -0.0247 -0.9925 -vn 0.4824 0.0259 -0.8756 -vn 0.8495 0.0846 -0.5208 -vn 0.9925 0.1220 -0.0012 -vn 0.8629 0.1252 0.4896 -vn -0.3198 -0.2060 0.9248 -vn -0.0095 -0.0038 0.9999 -vn -0.6559 -0.4261 0.6230 -vn -0.8294 -0.5399 0.1436 -vn -0.7827 -0.5157 -0.3485 -vn -0.1842 -0.2229 -0.9573 -vn -0.5812 0.5372 0.6112 -vn 0.1757 -0.1621 0.9710 -vn 0.5124 -0.4812 0.7113 -vn -0.3283 0.3101 -0.8922 -vn -0.5046 0.4751 -0.7209 -vn -0.6709 0.6249 -0.3994 -vn -0.7293 0.6754 0.1094 -vn -0.7027 0.3999 0.5885 -vn 0.2034 -0.1328 0.9700 -vn 0.4008 -0.2502 0.8813 -vn 0.5964 -0.3645 0.7152 -vn 0.7893 -0.4761 0.3877 -vn 0.8545 -0.5088 -0.1048 -vn 0.6953 -0.4075 -0.5921 -vn 0.3551 -0.1977 -0.9137 -vn -0.3899 0.2425 -0.8883 -vn -0.5886 0.3565 -0.7256 -vn -0.7873 0.4691 -0.4001 -vn -0.8573 0.5037 0.1062 -vn 0.6437 -0.2634 0.7185 -vn 0.8552 -0.3392 0.3919 -vn 0.9283 -0.3575 -0.1022 -vn 0.7561 -0.2850 -0.5891 -vn 0.3856 -0.1427 -0.9116 -vn 0.2030 -0.0294 -0.9787 -vn -0.4294 0.1863 -0.8837 -vn -0.6365 0.2591 -0.7265 -vn -0.8522 0.3353 -0.4017 -vn -0.9293 0.3546 0.1028 -vn 0.6581 -0.1812 0.7308 -vn 0.8882 -0.2317 0.3968 -vn 0.9667 -0.2348 -0.1015 -vn 0.8031 -0.1785 -0.5685 -vn 0.5128 -0.0952 -0.8532 -vn -0.4665 0.1346 -0.8742 -vn -0.6603 0.1771 -0.7298 -vn -0.8881 0.2191 -0.4041 -vn -0.9681 0.2289 0.1020 -vn 0.2830 -0.0717 0.9564 -vn 0.4627 -0.1003 0.8808 -vn 0.8174 -0.0886 -0.5692 -vn 0.9840 -0.1282 -0.1235 -vn -0.9818 0.1499 0.1164 -vn -0.9898 0.0925 0.1082 -vn 0.4262 -0.0987 0.8992 -vn 0.6017 -0.1142 0.7905 -vn -0.8104 0.0994 0.5775 -vn -0.7888 0.2302 0.5700 -vn 0.4278 -0.1698 0.8877 -vn 0.6465 -0.2265 0.7285 -vn 0.9535 -0.2834 -0.1025 -vn 0.5290 -0.1194 -0.8402 -vn -0.4379 0.1780 -0.8812 -vn -0.6549 0.2415 -0.7161 -vn -0.8714 0.2997 -0.3884 -vn -0.9475 0.3028 0.1025 -vn -0.7129 0.4038 0.5734 -vn -0.7811 0.4924 -0.3841 -vn -0.8541 0.5082 0.1108 -vn 0.1855 0.3158 -0.9305 -vn -0.2714 -0.1719 0.9470 -vn 0.0984 -0.9658 -0.2400 -vn -0.3076 -0.1233 0.9435 -vn -0.3147 -0.0025 0.9492 -vn -0.2459 0.6521 0.7171 -vn -0.3028 0.2773 0.9118 -vn -0.3086 0.1292 0.9424 -vn -0.2758 -0.3870 0.8799 -vn -0.2927 -0.2532 0.9221 -vn 0.3090 0.0018 -0.9511 -vn 0.3045 0.1240 -0.9444 -vn 0.2553 -0.6465 -0.7190 -vn 0.3076 -0.2734 -0.9114 -vn 0.3147 -0.1292 -0.9404 -vn 0.3005 0.2512 -0.9201 -vn 0.2828 0.3856 -0.8783 -vn 0.7816 -0.0889 0.6174 -vn 0.2809 0.0517 -0.9583 -vn 0.5616 -0.0518 0.8258 -vn 0.3347 -0.0530 0.9408 -vn 0.1138 0.0379 0.9928 -vn 0.3499 0.0567 0.9351 -vn 0.4531 0.0501 0.8900 -vn -0.7932 0.2073 0.5726 -vn 0.0589 -0.1498 -0.9869 -vn 0.4116 -0.1949 -0.8903 -vn 0.7986 -0.2131 -0.5629 -vn -0.7451 0.3752 0.5514 -vn -0.9315 0.3611 0.0433 -vn -0.8557 0.2518 -0.4521 -vn -0.6324 0.1176 -0.7657 -vn 0.3831 -0.2901 -0.8769 -vn 0.7562 -0.3825 -0.5309 -vn 0.9280 -0.3704 -0.0403 -vn 0.8595 -0.2653 0.4369 -vn 0.6528 -0.1334 0.7457 -vn 0.4435 -0.0260 0.8959 -vn 0.2204 0.0700 0.9729 -vn -0.5984 0.5952 0.5364 -vn -0.3067 0.3919 0.8674 -vn -0.7692 0.6388 0.0173 -vn -0.7163 0.5024 -0.4842 -vn -0.5408 0.3023 -0.7850 -vn -0.3662 0.1306 -0.9213 -vn 0.0352 -0.2121 -0.9766 -vn 0.3344 -0.4345 -0.8363 -vn 0.6316 -0.6153 -0.4717 -vn 0.7695 -0.6382 0.0256 -vn 0.7107 -0.5020 0.4928 -vn 0.5405 -0.3054 0.7839 -vn 0.3697 -0.1375 0.9189 -vn 0.1931 -0.5307 -0.8253 -vn 0.3962 -0.7996 -0.4513 -vn 0.4999 -0.8645 0.0520 -vn 0.4737 -0.7114 0.5192 -vn 0.3679 -0.4621 0.8069 -vn 0.0691 0.8390 0.5397 -vn 0.0080 0.9997 0.0247 -vn -0.0524 0.8824 -0.4675 -vn -0.1176 0.1322 -0.9842 -vn 0.1157 -0.1687 0.9789 -vn 0.1225 0.1079 0.9866 -vn -0.1246 -0.1162 -0.9854 -vn 0.1089 0.4597 0.8814 -vn 0.2830 0.8156 0.5047 -vn 0.2223 0.9749 -0.0119 -vn 0.1049 0.8624 -0.4952 -vn -0.1520 0.1576 -0.9757 -vn -0.2157 -0.1110 -0.9701 -vn -0.2705 -0.4606 -0.8454 -vn -0.2871 -0.8187 -0.4974 -vn -0.2266 -0.9740 -0.0015 -vn -0.1051 -0.8628 0.4945 -vn 0.0455 -0.5294 0.8471 -vn 0.1459 -0.1750 0.9737 -vn 0.2097 0.0925 0.9734 -vn 0.2637 0.4407 0.8580 -vn -0.0167 -0.5080 0.8612 -vn -0.6799 0.1154 -0.7242 -vn 0.2233 -0.3838 0.8960 -vn 0.2308 -0.3661 0.9015 -vn 0.5369 0.1276 -0.8339 -vn 0.1718 0.2466 -0.9538 -vn -0.1730 -0.2424 0.9546 -vn 0.1684 0.1582 -0.9729 -vn -0.1206 0.1676 -0.9785 -vn -0.8752 -0.0566 0.4804 -vn -0.5317 -0.1281 0.8372 -vn -0.1684 -0.1582 0.9729 -vn 0.1179 -0.1673 0.9788 -vn 0.5339 0.0480 -0.8442 -vn 0.1654 0.0694 -0.9838 -vn -0.1184 0.0778 -0.9899 -vn -0.4203 0.0806 -0.9038 -vn 0.3569 -0.0800 0.9307 -vn -0.4978 0.0709 -0.8644 -vn 0.4415 0.1921 0.8764 -vn 0.5112 -0.1076 0.8527 -vn -0.1776 0.8403 -0.5122 -vn -0.1583 0.9872 -0.0167 -vn -0.0985 0.8647 0.4926 -vn -0.0156 0.5072 0.8617 -vn -0.2039 0.8363 0.5089 -vn -0.2699 0.4427 0.8551 -vn -0.1188 -0.9475 0.2970 -vn 0.5896 0.0050 -0.8077 -vn 0.5851 -0.1177 -0.8024 -vn -0.4467 -0.6578 0.6064 -vn 0.4054 -0.7261 -0.5554 -vn 0.5467 -0.3809 -0.7457 -vn 0.5744 -0.2443 -0.7812 -vn 0.5431 0.4017 -0.7374 -vn 0.4942 0.5610 -0.6641 -vn 0.5887 0.1339 -0.7972 -vn 0.5740 0.2624 -0.7757 -vn 0.3949 0.7529 -0.5265 -vn 0.1940 0.9482 -0.2515 -vn -0.3398 0.8139 0.4713 -vn -0.4551 0.6319 0.6273 -vn -0.5725 0.2624 0.7768 -vn -0.5906 0.1237 0.7974 -vn -0.5267 0.4330 0.7315 -vn 0.4714 0.8819 0.0008 -vn -0.5897 -0.0052 0.8076 -vn -0.5830 -0.1347 0.8012 -vn -0.5671 -0.2881 0.7717 -vn 0.7729 0.6345 0.0016 -vn 0.4443 -0.5208 0.7289 -vn 0.2173 -0.6538 0.7248 -vn 0.6267 -0.7792 -0.0024 -vn 0.2903 -0.9569 0.0009 -vn -0.0980 -0.9952 0.0000 -vn -0.4714 -0.8819 -0.0008 -vn -0.7729 -0.6345 -0.0016 -vn -0.9484 -0.3169 -0.0109 -vn -0.6267 0.7792 0.0024 -vn -0.4245 0.5439 0.7239 -vn -0.2903 0.9569 -0.0009 -vn -0.1836 0.6647 0.7242 -vn 0.0980 0.9952 -0.0000 -vn 0.4244 -0.5439 -0.7239 -vn 0.6561 0.2150 -0.7234 -vn 0.0499 0.6875 -0.7245 -vn -0.2173 0.6538 -0.7248 -vn -0.4443 0.5208 -0.7289 -vn -0.6976 -0.1117 -0.7077 -vn -0.6444 0.2664 -0.7168 -vn -0.5456 -0.4200 -0.7252 -vn -0.3406 -0.5988 -0.7248 -vn -0.0852 -0.6840 -0.7245 -vn 0.1836 -0.6647 -0.7242 -vn 0.5224 0.4506 -0.7240 -vn 0.3097 0.6161 -0.7242 -vn 0.4444 -0.5208 0.7289 -vn 0.5223 0.4506 -0.7240 -vn 0.7729 0.6345 0.0015 -vn 0.5899 0.0004 -0.8075 -vn -0.1104 -0.0005 -0.9939 -vn -0.1095 0.0001 -0.9940 -vn 0.9937 0.0006 -0.1124 -vn 0.6247 -0.0006 -0.7809 -vn 0.7642 0.0005 0.6450 -vn 0.9983 -0.0006 -0.0575 -vn 0.1104 0.0005 0.9939 -vn 0.7355 -0.0004 0.6775 -vn -0.5899 -0.0004 0.8075 -vn -0.6247 0.0006 0.7809 -vn -0.9937 -0.0006 0.1124 -vn -0.9983 0.0006 0.0575 -vn -0.7642 -0.0005 -0.6450 -vn -0.7355 0.0004 -0.6775 -vn 0.0000 1.0000 -0.0000 -vn 0.0000 -1.0000 0.0000 -vn 0.0008 -0.8235 0.5673 -vn -0.1332 -0.6732 0.7274 -vn -0.0998 -0.8250 0.5562 -vn -0.0011 -0.6507 0.7593 -vn 0.0957 0.8410 -0.5325 -vn 0.1068 0.7998 -0.5907 -vn 0.1151 0.7627 -0.6365 -vn 0.1240 0.7176 -0.6853 -vn 0.1332 0.6639 -0.7358 -vn 0.1415 0.6075 -0.7816 -vn 0.1485 0.5533 -0.8196 -vn 0.1609 0.4219 -0.8923 -vn 0.0046 0.2785 -0.9604 -vn 0.0016 0.2695 -0.9630 -vn -0.0206 0.2629 -0.9646 -vn -0.2635 -0.6592 0.7043 -vn -0.1954 -0.8265 0.5279 -vn 0.1875 0.8431 -0.5041 -vn 0.2092 0.7998 -0.5627 -vn 0.2254 0.7627 -0.6062 -vn 0.2428 0.7176 -0.6528 -vn 0.2608 0.6639 -0.7008 -vn 0.2772 0.6075 -0.7444 -vn 0.2904 0.5515 -0.7820 -vn 0.3163 0.4219 -0.8497 -vn -0.3800 -0.6620 0.6460 -vn -0.2828 -0.8278 0.4846 -vn -0.4903 -0.2096 0.8460 -vn 0.2096 0.9089 -0.3604 -vn 0.2722 0.8424 -0.4651 -vn 0.3032 0.7998 -0.5181 -vn 0.3268 0.7627 -0.5582 -vn 0.3519 0.7176 -0.6010 -vn 0.3779 0.6640 -0.6452 -vn 0.4015 0.6076 -0.6853 -vn 0.4221 0.5506 -0.7202 -vn 0.4408 0.4911 -0.7513 -vn 0.4593 0.4208 -0.7823 -vn 0.4754 0.3462 -0.8088 -vn -0.4833 -0.6639 0.5706 -vn -0.3603 -0.8287 0.4284 -vn -0.6327 -0.1793 0.7533 -vn 0.2632 0.9126 -0.3128 -vn 0.3473 0.8424 -0.4120 -vn 0.3869 0.7998 -0.4590 -vn 0.4169 0.7627 -0.4945 -vn 0.4489 0.7176 -0.5324 -vn 0.4820 0.6640 -0.5716 -vn 0.5121 0.6076 -0.6071 -vn 0.5382 0.5507 -0.6380 -vn 0.5618 0.4911 -0.6657 -vn 0.5851 0.4209 -0.6932 -vn 0.6053 0.3463 -0.7167 -vn 0.8870 0.3225 -0.3306 -vn 0.8966 0.2861 -0.3380 -vn -0.5163 -0.8351 0.1900 -vn 0.5781 0.8092 -0.1046 -vn 0.5044 0.8587 -0.0909 -vn 0.6248 0.7725 -0.1131 -vn 0.6725 0.7300 -0.1218 -vn 0.7232 0.6781 -0.1310 -vn 0.7705 0.6220 -0.1397 -vn 0.8115 0.5655 -0.1472 -vn 0.8468 0.5092 -0.1537 -vn 0.8781 0.4510 -0.1597 -vn 0.9081 0.3843 -0.1663 -vn 0.5154 0.8569 0.0004 -vn 0.5875 0.8092 -0.0000 -vn 0.6350 0.7725 -0.0000 -vn 0.6835 0.7300 -0.0000 -vn 0.7350 0.6781 -0.0000 -vn 0.7831 0.6219 -0.0000 -vn 0.8249 0.5653 -0.0000 -vn 0.8608 0.5089 -0.0000 -vn 0.8927 0.4507 -0.0000 -vn 0.9237 0.3831 0.0008 -vn -0.6751 -0.7277 -0.1214 -vn -0.8637 -0.4802 -0.1533 -vn -0.9811 0.0718 -0.1799 -vn 0.9205 0.2699 0.2827 -vn 0.8954 0.2924 0.3357 -vn -0.4831 -0.8278 -0.2854 -vn 0.8242 0.2932 0.4846 -vn 0.7884 0.2682 0.5536 -vn -0.4298 -0.8266 -0.3632 -vn -0.5861 -0.6430 -0.4930 -vn 0.7361 0.2906 0.6113 -vn -0.3633 -0.8254 -0.4322 -vn -0.4787 -0.6733 -0.5634 -vn 0.5569 0.5077 0.6573 -vn -0.2854 -0.8241 -0.4892 -vn -0.3842 -0.6511 -0.6545 -vn 0.2718 0.8430 0.4642 -vn 0.3033 0.7997 0.5182 -vn 0.3268 0.7626 0.5583 -vn 0.3520 0.7175 0.6011 -vn 0.3780 0.6638 0.6454 -vn 0.4016 0.6074 0.6854 -vn 0.4213 0.5532 0.7187 -vn 0.4353 0.5092 0.7424 -vn 0.4460 0.4721 0.7604 -vn 0.4594 0.4204 0.7825 -vn 0.4718 0.3634 0.8033 -vn -0.1969 -0.8232 -0.5325 -vn -0.2612 -0.6685 -0.6963 -vn 0.1876 0.8416 0.5064 -vn 0.2091 0.7997 0.5628 -vn 0.2253 0.7626 0.6063 -vn 0.2427 0.7175 0.6529 -vn 0.2607 0.6638 0.7010 -vn 0.2770 0.6073 0.7446 -vn 0.2907 0.5531 0.7808 -vn 0.3005 0.5092 0.8065 -vn 0.3080 0.4722 0.8259 -vn 0.3175 0.4201 0.8501 -vn 0.3256 0.3638 0.8727 -vn -0.1023 -0.8177 -0.5665 -vn -0.1383 -0.6298 -0.7643 -vn 0.0012 -0.6710 -0.7415 -vn -0.0008 -0.8241 -0.5664 -vn 0.0009 -0.3234 -0.9463 -vn -0.0010 0.2676 0.9635 -vn 0.0235 0.2638 0.9643 -vn 0.1332 -0.6736 -0.7270 -vn 0.0997 -0.8254 -0.5556 -vn 0.2636 -0.6590 -0.7045 -vn 0.1953 -0.8267 -0.5276 -vn -0.6138 0.3231 0.7203 -vn 0.6837 -0.4478 -0.5762 -vn 0.5248 -0.7277 -0.4417 -vn 0.7583 -0.4808 -0.4403 -vn 0.5919 -0.7280 -0.3459 -vn -0.9341 0.2740 -0.2288 -vn -0.9302 0.3251 -0.1701 -vn 0.6437 -0.7272 0.2384 -vn 0.8387 -0.4475 0.3103 -vn -0.9156 0.2671 -0.3006 -vn -0.8955 0.2912 -0.3365 -vn -0.8247 0.2917 -0.4845 -vn -0.7881 0.2681 -0.5540 -vn -0.7364 0.2903 -0.6111 -vn 0.4290 -0.8264 0.3648 -vn 0.3637 -0.8250 0.4326 -vn 0.4778 -0.6734 0.5641 -vn -0.3484 0.8409 -0.4141 -vn -0.3870 0.7997 -0.4590 -vn -0.4170 0.7626 -0.4945 -vn -0.4490 0.7175 -0.5325 -vn -0.4822 0.6638 -0.5717 -vn -0.5122 0.6074 -0.6073 -vn -0.5371 0.5532 -0.6368 -vn -0.5535 0.5109 -0.6577 -vn -0.5839 0.4217 -0.6937 -vn -0.5671 0.4685 -0.6774 -vn -0.5989 0.3671 -0.7117 -vn 0.2858 -0.8235 0.4901 -vn 0.3841 -0.6511 0.6546 -vn 0.3841 0.6454 0.6602 -vn 0.1973 -0.8225 0.5335 -vn 0.2673 -0.6470 0.7141 -vn -0.3335 0.3090 -0.8907 -vn 0.1025 -0.8169 0.5676 -vn 0.1344 -0.6563 0.7424 -vn -0.1180 -0.8954 -0.4292 -vn 0.1450 -0.9023 0.4059 -vn 0.1095 -0.0001 0.9940 -vn 0.9185 0.2633 0.2951 -s 1 -f 3/1/1 7/2/2 14/3/3 -f 115/4/4 6/5/5 73/6/6 -f 94/7/7 69/8/8 79/9/9 -f 4/10/10 106/11/11 113/12/12 -f 101/13/13 1/14/14 17/15/15 -f 70/16/16 115/4/4 65/17/17 -f 65/17/17 9/18/18 70/16/16 -f 87/19/19 15/20/20 71/21/21 -f 87/19/19 71/21/21 83/22/22 -f 118/23/23 125/24/24 15/25/20 -f 116/26/25 124/27/26 82/28/27 -f 84/29/28 87/19/19 86/30/29 -f 81/31/30 88/32/31 80/33/32 -f 90/34/33 4/10/10 12/35/34 -f 98/36/35 75/37/36 101/13/13 -f 109/38/37 12/35/34 112/39/38 -f 112/39/38 4/10/10 113/12/12 -f 77/40/39 94/41/7 93/42/40 -f 64/43/41 62/44/42 81/31/30 -f 3/1/1 14/3/3 81/31/30 -f 92/45/43 99/46/44 102/47/45 -f 111/48/46 67/49/47 114/50/48 -f 5/51/49 111/48/46 10/52/50 -f 11/53/51 66/54/52 114/50/48 -f 111/48/46 114/50/48 66/54/52 -f 109/38/37 112/39/38 92/45/43 -f 92/45/43 112/39/38 99/46/44 -f 100/55/53 115/4/4 70/16/16 -f 113/12/12 102/47/45 99/46/44 -f 113/12/12 106/11/11 102/47/45 -f 78/56/54 122/57/55 13/58/56 -f 78/56/54 123/59/57 122/57/55 -f 79/9/9 121/60/58 120/61/59 -f 10/62/50 94/7/7 5/63/49 -f 65/17/17 116/26/25 9/18/18 -f 66/54/52 118/23/23 10/52/50 -f 121/60/58 118/64/23 126/65/60 -f 123/59/57 116/66/25 127/67/61 -f 184/68/62 21/69/63 22/70/64 -f 162/71/65 137/72/66 149/73/67 -f 172/74/68 128/75/69 131/76/70 -f 18/77/71 31/78/72 167/79/73 -f 136/80/74 130/81/75 171/82/76 -f 172/74/68 131/76/70 136/80/74 -f 138/83/77 184/68/62 134/84/78 -f 134/84/78 23/85/79 138/83/77 -f 155/86/80 29/87/81 139/88/82 -f 155/86/80 139/88/82 151/89/83 -f 187/90/84 194/91/85 29/92/81 -f 185/93/86 193/94/87 150/95/88 -f 195/96/89 152/97/90 153/98/91 -f 154/99/92 152/100/90 155/86/80 -f 196/101/93 151/102/83 158/103/94 -f 176/104/95 27/105/96 179/106/97 -f 21/107/63 145/108/98 161/109/99 -f 143/110/100 163/111/101 128/112/69 -f 128/112/69 163/111/101 131/113/70 -f 168/114/102 142/115/103 129/116/104 -f 166/117/105 181/118/106 183/119/107 -f 132/120/108 165/121/109 133/122/110 -f 129/123/104 170/124/111 30/125/112 -f 178/126/113 136/80/74 182/127/114 -f 182/127/114 136/80/74 171/82/76 -f 19/128/115 173/129/116 132/120/108 -f 20/130/117 178/126/113 24/131/118 -f 25/132/119 135/133/120 182/127/114 -f 178/126/113 182/127/114 135/133/120 -f 176/104/95 179/106/97 168/114/102 -f 170/124/111 184/68/62 138/83/77 -f 180/134/121 175/135/122 169/136/123 -f 149/73/67 190/137/124 189/138/125 -f 134/84/78 185/93/86 23/85/79 -f 135/133/120 187/90/84 24/131/118 -f 190/137/124 187/139/84 195/96/89 -f 192/140/126 185/141/86 196/101/93 -f 251/142/127 35/143/128 36/144/129 -f 229/145/130 206/146/131 216/147/132 -f 239/148/133 197/149/134 200/150/135 -f 250/151/136 33/152/137 242/153/138 -f 32/154/139 46/155/140 234/156/141 -f 205/157/142 199/158/143 238/159/144 -f 239/148/133 200/150/135 205/157/142 -f 207/160/145 251/142/127 203/161/146 -f 203/161/146 37/162/147 207/160/145 -f 222/163/148 44/164/149 208/165/150 -f 222/163/148 208/165/150 218/166/151 -f 254/167/152 261/168/153 44/169/149 -f 252/170/154 260/171/155 217/172/156 -f 262/173/157 219/174/158 220/175/159 -f 221/176/160 219/177/158 222/163/148 -f 263/178/161 218/179/151 225/180/162 -f 226/181/163 215/182/164 227/183/165 -f 212/184/166 233/185/167 230/186/168 -f 244/187/169 40/188/170 247/189/171 -f 212/190/166 201/191/172 197/192/134 -f 235/193/173 211/194/174 198/195/175 -f 233/185/167 243/196/176 249/197/177 -f 43/198/178 232/199/179 202/200/180 -f 198/201/175 237/202/181 45/203/182 -f 246/204/183 205/157/142 248/205/184 -f 248/205/184 205/157/142 238/159/144 -f 34/206/185 246/204/183 38/207/186 -f 39/208/187 204/209/188 248/205/184 -f 246/204/183 248/205/184 204/209/188 -f 235/193/173 247/189/171 236/210/189 -f 244/187/169 247/189/171 235/193/173 -f 237/202/181 251/142/127 207/160/145 -f 250/151/136 242/153/138 236/210/189 -f 215/182/164 258/211/190 41/212/191 -f 215/182/164 259/213/192 258/211/190 -f 216/147/132 257/214/193 256/215/194 -f 203/161/146 252/170/154 37/162/147 -f 204/209/188 254/167/152 38/207/186 -f 257/214/193 254/216/152 262/173/157 -f 259/213/192 252/217/154 263/178/161 -f 318/218/127 50/219/128 51/220/129 -f 297/221/195 273/222/131 283/223/132 -f 307/224/133 264/225/134 267/226/135 -f 317/227/196 48/228/137 310/229/138 -f 47/230/139 61/231/140 302/232/197 -f 272/233/142 266/234/198 306/235/144 -f 307/224/133 267/226/135 272/233/142 -f 274/236/145 318/218/127 270/237/146 -f 270/237/146 52/238/147 274/236/145 -f 289/239/148 59/240/149 275/241/199 -f 289/239/148 275/241/199 285/242/151 -f 321/243/152 328/244/153 59/245/149 -f 319/246/154 327/247/155 284/248/156 -f 329/249/157 286/250/158 287/251/159 -f 288/252/160 286/253/158 289/239/148 -f 330/254/200 285/255/151 292/256/162 -f 293/257/163 282/258/164 294/259/165 -f 279/260/166 301/261/201 298/262/168 -f 312/263/169 55/264/170 315/265/202 -f 279/266/166 268/267/172 264/268/134 -f 303/269/203 278/270/204 265/271/205 -f 58/272/178 300/273/179 269/274/180 -f 265/275/205 305/276/181 60/277/206 -f 314/278/183 272/233/142 316/279/184 -f 316/279/184 272/233/142 306/235/144 -f 49/280/185 314/278/183 53/281/186 -f 54/282/187 271/283/188 316/279/184 -f 314/278/183 316/279/184 271/283/188 -f 312/263/169 315/265/202 303/269/203 -f 305/276/181 318/218/127 274/236/145 -f 317/227/196 310/229/138 304/284/207 -f 282/258/164 325/285/190 56/286/191 -f 282/258/164 326/287/192 325/285/190 -f 283/223/132 324/288/193 323/289/194 -f 270/237/146 319/246/154 52/238/147 -f 271/283/188 321/243/152 53/281/186 -f 324/288/193 321/290/152 329/249/157 -f 326/287/192 319/291/154 330/254/200 -f 335/292/208 382/293/209 334/294/210 -f 337/295/211 383/296/212 336/297/213 -f 339/298/214 384/299/215 338/300/216 -f 341/301/217 385/302/218 340/303/219 -f 386/304/220 342/305/221 343/306/222 -f 387/307/223 344/308/224 345/309/225 -f 388/310/226 346/311/227 347/312/228 -f 389/313/229 348/314/230 349/315/231 -f 390/316/232 350/317/233 351/318/234 -f 359/319/235 397/320/236 358/321/237 -f 409/322/238 375/323/239 376/324/240 -f 400/325/241 435/326/242 399/327/243 -f 437/328/244 407/329/245 408/330/246 -f 396/331/247 467/332/248 395/333/249 -f 432/334/250 468/335/251 397/320/236 -f 436/336/252 471/337/253 402/338/254 -f 472/339/255 436/336/252 404/340/256 -f 476/341/257 409/322/238 410/342/258 -f 724/343/259 771/344/260 723/345/261 -f 772/346/262 734/347/263 735/348/264 -f 750/349/265 786/350/266 749/351/267 -f 787/352/268 757/353/269 758/354/270 -f 901/355/271 948/356/272 900/357/273 -f 971/358/274 924/359/275 925/360/276 -f 947/361/277 992/362/278 946/363/279 -f 949/364/280 993/365/281 948/356/272 -f 994/366/282 953/367/283 954/368/284 -f 1010/369/285 970/370/286 971/358/274 -f 1011/371/287 971/358/274 972/372/288 -f 1013/373/289 974/374/290 975/375/291 -f 1014/376/292 976/377/293 977/378/294 -f 991/379/295 1027/380/296 990/381/297 -f 1051/382/298 1009/383/299 1010/369/285 -f 1052/384/300 1010/369/285 1011/371/287 -f 1053/385/301 1011/371/287 1012/386/302 -f 1029/387/303 1069/388/304 1028/389/305 -f 1070/390/306 1037/391/307 1038/392/308 -f 1050/393/309 1081/394/310 1049/395/311 -f 1087/396/312 1056/397/313 1057/398/314 -f 1030/399/315 1100/400/316 1029/387/303 -f 1032/401/317 1101/402/318 1031/403/319 -f 1034/404/320 1102/405/321 1033/406/322 -f 1036/407/323 1103/408/324 1035/409/325 -f 1104/410/326 1037/391/307 1070/390/306 -f 1094/411/327 1127/412/328 1093/413/329 -f 1097/414/330 1128/415/331 1096/416/332 -f 1111/417/333 1141/418/334 1110/419/335 -f 1142/420/336 1111/421/333 1112/422/337 -f 1176/423/338 1143/424/339 1144/425/340 -f 1184/426/341 1152/427/342 1153/428/343 -f 1159/429/344 1189/430/345 1158/431/346 -f 1169/432/347 1198/433/348 1168/434/349 -f 1266/435/350 1235/436/351 1236/437/352 -f 1267/438/353 1236/437/352 1237/439/354 -f 1268/440/355 1237/441/354 1238/442/356 -f 1269/443/357 1238/442/356 1239/444/358 -f 1270/445/359 1239/444/358 1240/446/360 -f 1271/447/361 1240/448/360 1241/449/362 -f 1272/450/363 1241/449/362 1242/451/364 -f 1273/452/365 1242/451/364 1243/453/366 -f 1274/454/367 1243/453/366 1244/455/368 -f 1250/456/369 1278/457/370 1249/458/371 -f 1251/459/372 1279/460/373 1250/456/369 -f 1252/461/374 1280/462/375 1251/459/372 -f 1253/463/376 1281/464/377 1252/465/374 -f 1254/466/378 1282/467/379 1253/463/376 -f 1255/468/380 1283/469/381 1254/466/378 -f 1256/470/382 1284/471/383 1255/468/380 -f 1257/472/384 1285/473/385 1256/470/382 -f 1258/474/386 1286/475/387 1257/472/384 -f 1259/476/388 1287/477/389 1258/474/386 -f 1333/478/390 1303/479/391 1304/480/392 -f 1334/481/393 1333/482/390 1305/483/394 -f 1319/484/395 1344/485/396 1318/486/397 -f 1320/487/398 1344/485/396 1319/484/395 -f 1345/488/399 1322/489/400 1323/490/401 -f 1308/491/402 1358/492/403 1307/493/404 -f 1372/494/405 1345/495/399 1346/496/406 -f 1373/497/407 1346/496/406 1347/498/408 -f 1374/499/409 1347/498/408 1348/500/410 -f 1375/501/411 1348/500/410 1349/502/412 -f 1376/503/413 1349/502/412 1350/504/414 -f 1377/505/415 1350/506/414 1351/507/416 -f 1378/508/417 1351/507/416 1352/509/418 -f 1379/510/419 1352/509/418 1353/511/420 -f 1380/512/421 1353/511/420 1354/513/422 -f 1356/514/423 1355/515/424 1334/516/393 -f 1359/517/425 1385/518/426 1358/519/403 -f 1360/520/427 1386/521/428 1359/517/425 -f 1361/522/429 1387/523/430 1360/520/427 -f 1362/524/431 1388/525/432 1361/522/429 -f 1363/526/433 1389/527/434 1362/524/431 -f 1364/528/435 1390/529/436 1363/526/433 -f 1365/530/437 1391/531/438 1364/528/435 -f 1366/532/439 1392/533/440 1365/530/437 -f 1367/534/441 1393/535/442 1366/532/439 -f 1368/536/443 1394/537/444 1367/534/441 -f 1369/538/445 1395/539/446 1368/536/443 -f 1407/540/447 1381/541/448 1355/515/424 -f 1464/542/449 1436/543/450 1437/544/451 -f 1440/545/452 1466/546/453 1439/547/454 -f 1441/548/455 1467/549/456 1440/545/452 -f 1442/550/457 1468/551/458 1441/548/455 -f 1443/552/459 1469/553/460 1442/554/457 -f 1444/555/461 1470/556/462 1443/552/459 -f 1445/557/463 1471/558/464 1444/555/461 -f 1446/559/465 1472/560/466 1445/557/463 -f 1447/561/467 1473/562/468 1446/559/465 -f 1448/563/469 1474/564/470 1447/561/467 -f 1449/565/471 1475/566/472 1448/563/469 -f 1450/567/473 1476/568/474 1449/565/471 -f 1481/569/475 1454/570/476 1455/571/477 -f 1482/572/478 1455/571/477 1456/573/479 -f 1483/574/480 1456/575/479 1457/576/481 -f 1484/577/482 1457/576/481 1458/578/483 -f 1485/579/484 1458/580/483 1459/581/485 -f 1486/582/486 1459/581/485 1460/583/487 -f 1487/584/488 1460/583/487 1461/585/489 -f 1488/586/490 1461/585/489 1462/587/491 -f 1489/588/492 1462/587/491 1435/589/493 -f 1521/590/494 1491/591/495 1492/592/496 -f 1505/593/497 1533/594/498 1504/595/499 -f 1547/596/500 1520/597/501 1491/591/495 -f 1525/598/502 1551/599/503 1524/600/504 -f 1526/601/505 1552/602/506 1525/598/502 -f 1527/603/507 1553/604/508 1526/601/505 -f 1528/605/509 1554/606/510 1527/603/507 -f 1529/607/511 1555/608/512 1528/605/509 -f 1530/609/513 1556/610/514 1529/607/511 -f 1565/611/515 1537/612/516 1538/613/517 -f 1566/614/518 1538/613/517 1539/615/519 -f 1567/616/520 1539/615/519 1540/617/521 -f 1568/618/522 1540/617/521 1541/619/523 -f 1569/620/524 1541/621/523 1542/622/525 -f 1575/623/526 1548/624/527 1549/625/528 -f 1552/602/506 1578/626/529 1551/599/503 -f 1553/604/508 1579/627/530 1552/602/506 -f 1554/606/510 1580/628/531 1553/604/508 -f 1555/608/512 1581/629/532 1554/606/510 -f 1556/610/514 1582/630/533 1555/608/512 -f 1587/631/534 1561/632/535 1562/633/536 -f 1566/614/518 1591/634/537 1565/611/515 -f 1567/616/520 1592/635/538 1566/614/518 -f 1568/618/522 1593/636/539 1567/616/520 -f 1569/637/524 1594/638/540 1568/618/522 -f 1600/639/541 1574/640/542 1547/596/500 -f 1580/628/531 1608/641/543 1579/627/530 -f 1609/642/544 1580/628/531 1581/629/532 -f 1610/643/545 1581/629/532 1582/630/533 -f 1611/644/546 1582/630/533 1583/645/547 -f 1612/646/548 1583/645/547 1584/647/549 -f 1613/648/550 1584/647/549 1585/649/551 -f 1614/650/552 1585/649/551 1586/651/553 -f 1592/635/538 1619/652/554 1591/634/537 -f 1593/636/539 1620/653/555 1592/635/538 -f 1594/638/540 1621/654/556 1593/636/539 -f 1595/655/557 1622/656/558 1594/657/540 -f 1596/658/559 1623/659/560 1595/655/557 -f 1597/660/561 1624/661/562 1596/658/559 -f 1598/662/563 1625/663/564 1597/660/561 -f 1600/639/541 1626/664/565 1574/640/542 -f 1627/665/566 1616/666/567 1617/667/568 -f 1618/668/569 1631/669/570 1590/670/571 -f 1624/661/562 1635/671/572 1623/659/560 -f 1601/672/573 1637/673/574 1600/639/541 -f 1638/674/575 1615/675/576 1616/666/567 -f 1620/653/555 1641/676/577 1619/652/554 -f 1632/677/578 1641/676/577 1620/653/555 -f 1599/678/579 1644/679/580 1598/662/563 -f 1636/680/581 1644/679/580 1599/678/579 -f 1649/681/582 1610/643/545 1611/644/546 -f 1650/682/583 1611/644/546 1612/646/548 -f 1651/683/584 1612/646/548 1613/648/550 -f 1654/684/585 1653/685/586 1615/675/576 -f 1645/686/587 1655/687/588 1601/688/573 -f 1656/689/589 1648/690/590 1606/691/591 -f 1626/664/565 1674/692/592 1636/680/581 -f 1663/693/593 1638/674/575 1627/665/566 -f 1662/694/594 1668/695/595 1632/677/578 -f 1634/696/596 1669/697/597 1633/698/598 -f 1635/671/572 1670/699/599 1634/700/596 -f 1642/701/600 1671/702/601 1635/671/572 -f 1643/703/602 1672/704/603 1642/701/600 -f 1644/679/580 1673/705/604 1643/703/602 -f 1636/680/581 1674/692/592 1644/679/580 -f 1637/673/574 1675/706/605 1626/664/565 -f 1676/707/606 1628/708/607 1629/709/608 -f 1664/710/609 1678/711/610 1631/669/570 -f 1683/712/611 1652/713/612 1653/685/586 -f 1684/714/613 1663/693/593 1628/715/607 -f 1675/706/605 1695/716/614 1674/692/592 -f 1679/717/615 1686/718/616 1655/687/588 -f 1687/719/617 1682/720/618 1656/689/589 -f 1687/719/617 1656/689/589 1657/721/619 -f 1688/722/620 1658/723/621 1659/724/622 -f 1691/725/623 1649/681/582 1650/682/583 -f 1692/726/624 1650/682/583 1651/683/584 -f 1693/727/625 1651/683/584 1652/713/612 -f 1694/728/626 1654/684/585 1638/674/575 -f 1698/729/627 1681/730/628 1682/720/618 -f 1699/731/629 1693/727/625 1683/712/611 -f 1701/732/630 1694/728/626 1663/693/593 -f 1702/733/631 1684/734/613 1676/707/606 -f 1678/735/610 1703/736/632 1677/737/633 -f 1665/738/634 1704/739/635 1664/710/609 -f 1667/740/636 1705/741/637 1666/742/638 -f 1662/694/594 1706/743/639 1668/695/595 -f 1695/716/614 1711/744/640 1673/705/604 -f 1685/745/641 1712/746/642 1675/706/605 -f 1686/747/616 1712/746/642 1685/745/641 -f 1713/748/643 1697/749/644 1698/750/627 -f 1714/751/645 1698/729/627 1687/719/617 -f 1715/752/646 1687/719/617 1688/722/620 -f 1716/753/647 1688/722/620 1689/754/648 -f 1717/755/649 1689/754/648 1690/756/650 -f 1718/757/651 1690/756/650 1691/725/623 -f 1719/758/652 1691/725/623 1692/726/624 -f 1720/759/653 1692/726/624 1693/727/625 -f 1721/760/654 1693/727/625 1699/731/629 -f 1722/761/655 1701/732/630 1684/714/613 -f 1722/762/655 1684/734/613 1702/733/631 -f 1736/763/656 1697/749/644 1713/748/643 -f 1745/764/657 1699/731/629 1700/765/658 -f 1746/766/659 1694/728/626 1701/732/630 -f 1733/767/660 1782/768/661 1758/769/662 -f 1734/770/663 1759/771/664 1712/746/642 -f 1772/772/665 1747/773/666 1748/774/667 -f 1733/767/660 1783/775/668 1782/768/661 -f 1759/771/664 1783/775/668 1733/767/660 -f 1760/776/669 1784/777/670 1734/778/663 -f 1795/779/671 1771/780/672 1746/766/659 -f 1773/781/673 1797/782/674 1772/772/665 -f 1774/783/675 1798/784/676 1773/785/673 -f 1775/786/677 1799/787/678 1774/783/675 -f 1776/788/679 1800/789/680 1775/786/677 -f 1777/790/681 1801/791/682 1776/792/679 -f 1778/793/683 1802/794/684 1777/795/681 -f 1779/796/685 1803/797/686 1778/793/683 -f 1780/798/687 1804/799/688 1779/796/685 -f 1781/800/689 1805/801/690 1780/798/687 -f 1782/768/661 1806/802/691 1781/800/689 -f 1810/803/692 1784/777/670 1785/804/693 -f 1811/805/694 1785/806/693 1786/807/695 -f 1812/808/696 1786/807/695 1787/809/697 -f 1813/810/698 1787/809/697 1788/811/699 -f 1814/812/700 1788/811/699 1789/813/701 -f 1815/814/702 1789/815/701 1790/816/703 -f 1816/817/704 1790/816/703 1791/818/705 -f 1817/819/706 1791/818/705 1792/820/707 -f 1818/821/708 1792/820/707 1793/822/709 -f 1819/823/710 1793/822/709 1794/824/711 -f 1820/825/712 1794/824/711 1771/780/672 -f 1808/826/713 1831/827/714 1807/828/715 -f 1832/829/716 1809/830/717 1810/803/692 -f 1856/831/718 1879/832/719 1855/833/720 -f 1880/834/721 1857/835/722 1858/836/723 -f 1885/837/724 1863/838/725 1864/839/726 -f 1875/840/727 1897/841/728 1874/842/729 -f 1902/843/730 1857/835/722 1880/834/721 -f 1903/844/731 1880/845/721 1881/846/732 -f 1904/847/733 1881/846/732 1882/848/734 -f 1905/849/735 1882/848/734 1883/850/736 -f 1906/851/737 1883/850/736 1884/852/738 -f 1907/853/739 1884/852/738 1885/837/724 -f 1908/854/740 1885/837/724 1886/855/741 -f 1909/856/742 1886/855/741 1887/857/743 -f 1910/858/744 1887/857/743 1888/859/745 -f 1911/860/746 1888/859/745 1889/861/747 -f 1893/862/748 1913/863/749 1892/864/750 -f 1894/865/751 1914/866/752 1893/862/748 -f 1895/867/753 1915/868/754 1894/865/751 -f 1896/869/755 1916/870/756 1895/867/753 -f 1897/871/728 1917/872/757 1896/869/755 -f 1898/873/758 1918/874/759 1897/841/728 -f 1899/875/760 1919/876/761 1898/873/758 -f 1900/877/762 1920/878/763 1899/875/760 -f 1879/879/719 1921/880/764 1900/877/762 -f 1901/881/765 1922/882/766 1879/832/719 -f 1928/883/767 1907/853/739 1908/854/740 -f 1918/874/759 1940/884/768 1917/885/757 -f 1946/886/769 1922/882/766 1923/887/770 -f 1967/888/771 1991/889/772 1990/890/773 -f 1992/891/774 1968/892/775 1969/893/776 -f 1996/894/777 1973/895/778 1974/896/779 -f 1997/897/780 1974/896/779 1975/898/781 -f 1998/899/782 1975/898/781 1976/900/783 -f 1999/901/784 1976/900/783 1977/902/785 -f 2000/903/786 1977/902/785 1978/904/787 -f 2001/905/788 1978/906/787 1979/907/789 -f 1980/908/790 2001/909/788 1979/910/789 -f 1986/911/791 2006/912/792 1985/913/793 -f 1987/914/794 2007/915/795 1986/916/791 -f 1988/917/796 2008/918/797 1987/914/794 -f 1989/919/798 2009/920/799 1988/917/796 -f 1990/921/773 2010/922/800 1989/919/798 -f 2012/923/801 1968/892/775 1992/891/774 -f 2013/924/802 1992/891/774 1993/925/803 -f 2014/926/804 1993/925/803 1994/927/805 -f 2015/928/806 1994/927/805 1995/929/807 -f 2016/930/808 1995/929/807 1996/931/777 -f 2002/932/809 2023/933/810 1980/908/790 -f 2003/934/811 2024/935/812 2002/932/809 -f 2004/936/813 2025/937/814 2003/934/811 -f 2005/938/815 2026/939/816 2004/936/813 -f 2006/940/792 2027/941/817 2005/938/815 -f 2058/942/818 2035/943/819 2036/944/820 -f 2046/945/821 2067/946/822 2045/947/823 -f 2084/948/824 2062/949/825 2063/950/826 -f 2073/951/827 2096/952/828 2072/953/829 -f 2078/954/830 2100/955/831 2077/956/832 -f 2105/957/833 2091/958/834 2092/959/835 -f 2079/960/836 2114/961/837 2078/962/830 -f 2101/963/838 2118/964/839 2117/965/840 -f 2102/966/841 2119/967/842 2101/963/838 -f 2103/968/843 2120/969/844 2102/966/841 -f 2088/970/845 2121/971/846 2104/972/847 -f 2122/973/848 2089/974/849 2090/975/850 -f 2126/976/851 2106/977/852 2107/978/853 -f 2127/979/854 2107/978/853 2108/980/855 -f 2081/981/856 2136/982/857 2080/983/858 -f 2082/984/859 2137/985/860 2081/981/856 -f 2115/986/861 2137/985/860 2082/984/859 -f 2121/987/846 2139/988/862 2104/989/847 -f 2113/990/863 2156/991/864 2135/992/865 -f 2137/985/860 2136/982/857 2081/981/856 -f 2138/993/866 2146/994/867 2120/969/844 -f 2147/995/868 2126/976/851 2127/979/854 -f 2148/996/869 2127/979/854 2128/997/870 -f 2079/960/836 2157/998/871 2114/961/837 -f 2136/982/857 2158/999/872 2080/983/858 -f 2141/1000/873 2167/1001/874 2137/985/860 -f 2159/1002/875 2140/1003/876 2089/974/849 -f 2160/1004/877 2122/1005/848 2123/1006/878 -f 2158/999/872 2161/1007/879 2079/960/836 -f 2140/1003/876 2162/1008/880 2121/971/846 -f 2163/1009/881 2159/1002/875 2122/973/848 -f 2164/1010/882 2124/1011/883 2125/1012/884 -f 2170/1013/885 2125/1012/884 2147/995/868 -f 2157/1014/871 2165/1015/886 2113/990/863 -f 2162/1016/880 2168/1017/887 2139/988/862 -f 2170/1013/885 2164/1010/882 2125/1012/884 -f 2171/1018/888 2170/1013/885 2147/995/868 -f 2171/1018/888 2147/995/868 2148/996/869 -f 2172/1019/889 2148/996/869 2149/1020/890 -f 2173/1021/891 2149/1020/890 2150/1022/892 -f 2174/1023/893 2150/1024/892 2151/1025/894 -f 2175/1026/895 2151/1025/894 2152/1027/896 -f 2176/1028/897 2152/1027/896 2153/1029/898 -f 2177/1030/899 2153/1029/898 2154/1031/900 -f 2167/1001/874 2166/1032/901 2136/982/857 -f 2142/1033/902 2180/1034/903 2141/1000/873 -f 2143/1035/904 2181/1036/905 2142/1033/902 -f 2144/1037/906 2182/1038/907 2143/1035/904 -f 2145/1039/908 2183/1040/909 2144/1037/906 -f 2146/994/867 2184/1041/910 2145/1039/908 -f 2166/1032/901 2186/1042/911 2158/999/872 -f 2187/1043/912 2169/1044/913 2159/1002/875 -f 2161/1007/879 2191/1045/914 2157/998/871 -f 2186/1042/911 2191/1045/914 2161/1007/879 -f 2194/1046/915 2168/1017/887 2162/1016/880 -f 2187/1043/912 2195/1047/916 2169/1044/913 -f 2188/1048/917 2195/1047/916 2187/1043/912 -f 2196/1049/918 2160/1004/877 2164/1010/882 -f 2197/1050/919 2164/1010/882 2170/1013/885 -f 2198/1051/920 2170/1013/885 2171/1018/888 -f 2200/1052/921 2175/1026/895 2176/1028/897 -f 2202/1053/922 2178/1054/923 2179/1055/924 -f 2191/1056/914 2203/1057/925 2190/1058/926 -f 2186/1042/911 2204/1059/927 2191/1045/914 -f 2192/1060/928 2205/1061/929 2186/1042/911 -f 2183/1040/909 2208/1062/930 2182/1038/907 -f 2213/1063/931 2188/1064/917 2189/1065/932 -f 2216/1066/933 2173/1067/891 2174/1023/893 -f 2216/1066/933 2174/1023/893 2175/1026/895 -f 2217/1068/934 2175/1026/895 2200/1052/921 -f 2218/1069/935 2200/1052/921 2201/1070/936 -f 2219/1071/937 2201/1070/936 2202/1072/922 -f 2203/1073/925 2221/1074/938 2220/1075/939 -f 2204/1076/927 2222/1077/940 2203/1073/925 -f 2205/1061/929 2223/1078/941 2204/1059/927 -f 2206/1079/942 2223/1078/941 2205/1061/929 -f 2210/1080/943 2227/1081/944 2209/1082/945 -f 2231/1083/946 2214/1084/947 2215/1085/948 -f 2234/1086/949 2199/1087/950 2216/1088/933 -f 2235/1089/951 2217/1068/934 2218/1069/935 -f 2222/1077/940 2237/1090/952 2221/1074/938 -f 2223/1091/941 2238/1092/953 2222/1077/940 -f 2224/1093/954 2239/1094/955 2223/1078/941 -f 2207/1095/956 2225/1096/957 2180/1034/903 -f 2226/1097/958 2240/1098/959 2207/1095/956 -f 2227/1081/944 2254/1099/960 2226/1097/958 -f 2228/1100/961 2242/1101/962 2227/1102/944 -f 2229/1103/963 2243/1104/964 2228/1100/961 -f 2247/1105/965 2233/1106/966 2199/1087/950 -f 2247/1105/965 2199/1087/950 2234/1086/949 -f 2249/1107/967 2234/1108/949 2235/1089/951 -f 2251/1109/968 2235/1110/951 2236/1111/969 -f 2252/1112/970 2236/1111/969 2221/1074/938 -f 2225/1096/957 2253/1113/971 2224/1093/954 -f 2242/1101/962 2256/1114/972 2255/1115/973 -f 2243/1104/964 2257/1116/974 2242/1101/962 -f 2259/1117/975 2232/1118/976 2233/1106/966 -f 2260/1119/977 2252/1112/970 2237/1090/952 -f 2238/1092/953 2261/1120/978 2237/1090/952 -f 2239/1094/955 2262/1121/979 2238/1122/953 -f 2283/1123/980 2263/1124/981 2239/1094/955 -f 2240/1098/959 2264/1125/982 2225/1096/957 -f 2255/1126/973 2265/1127/983 2254/1099/960 -f 2267/1128/984 2244/1129/985 2245/1130/986 -f 2268/1131/987 2245/1132/986 2246/1133/988 -f 2269/1134/989 2246/1133/988 2232/1118/976 -f 2241/1135/990 2276/1136/991 2240/1098/959 -f 2265/1127/983 2276/1136/991 2241/1135/990 -f 2256/1114/972 2277/1137/992 2255/1115/973 -f 2266/1138/993 2278/1139/994 2257/1116/974 -f 2305/1140/995 2259/1117/975 2233/1106/966 -f 2261/1120/978 2280/1141/996 2260/1119/977 -f 2262/1142/979 2281/1143/997 2261/1120/978 -f 2263/1124/981 2282/1144/998 2262/1121/979 -f 2277/1145/992 2284/1146/999 2265/1127/983 -f 2278/1139/994 2285/1147/1000 2277/1137/992 -f 2266/1138/993 2286/1148/1001 2278/1139/994 -f 2289/1149/1002 2268/1131/987 2269/1134/989 -f 2290/1150/1003 2271/1151/1004 2272/1152/1005 -f 2290/1150/1003 2272/1152/1005 2273/1153/1006 -f 2264/1125/982 2297/1154/1007 2253/1113/971 -f 2276/1136/991 2298/1155/1008 2275/1156/1009 -f 2284/1146/999 2298/1155/1008 2276/1136/991 -f 2285/1157/1000 2300/1158/1010 2284/1146/999 -f 2286/1148/1001 2301/1159/1011 2285/1147/1000 -f 2306/1160/1012 2270/1161/1013 2271/1151/1004 -f 2307/1162/1014 2271/1151/1004 2290/1150/1003 -f 2308/1163/1015 2290/1150/1003 2291/1164/1016 -f 2309/1165/1017 2291/1166/1016 2292/1167/1018 -f 2296/1168/1019 2313/1169/1020 2295/1170/1021 -f 2323/1171/1022 2303/1172/1023 2304/1173/1024 -f 2325/1174/1025 2304/1173/1024 2305/1140/995 -f 2326/1175/1026 2305/1140/995 2279/1176/1027 -f 2327/1177/1028 2306/1160/1012 2307/1162/1014 -f 2313/1169/1020 2347/1178/1029 2312/1179/1030 -f 2314/1180/1031 2348/1181/1032 2297/1154/1007 -f 2316/1182/1033 2331/1183/1034 2315/1184/1035 -f 2338/1185/1036 2323/1171/1022 2324/1186/1037 -f 2339/1187/1038 2324/1186/1037 2325/1174/1025 -f 2340/1188/1039 2325/1174/1025 2305/1140/995 -f 2341/1189/1040 2326/1175/1026 2306/1190/1012 -f 2344/1191/1041 2328/1192/1042 2329/1193/1043 -f 2332/1194/1044 2350/1195/1045 2316/1182/1033 -f 2351/1196/1046 2322/1197/1047 2323/1171/1022 -f 2352/1198/1048 2340/1188/1039 2326/1175/1026 -f 2353/1199/1049 2343/1200/1050 2328/1192/1042 -f 2348/1181/1032 2356/1201/1051 2296/1168/1019 -f 2351/1196/1046 2357/1202/1052 2322/1197/1047 -f 2338/1185/1036 2351/1196/1046 2323/1171/1022 -f 2358/1203/1053 2342/1204/1054 2343/1200/1050 -f 2360/1205/1055 2333/1206/1056 2334/1207/1057 -f 2357/1202/1052 2364/1208/1058 2337/1209/1059 -f 2365/1210/1060 2339/1187/1038 2340/1188/1039 -f 2333/1206/1056 2370/1211/1061 2332/1194/1044 -f 2371/1212/1062 2351/1196/1046 2338/1185/1036 -f 2372/1213/1063 2371/1212/1062 2339/1187/1038 -f 2373/1214/1064 2358/1203/1053 2353/1199/1049 -f 2374/1215/1065 2353/1199/1049 2354/1216/1066 -f 2350/1195/1045 2378/1217/1067 2331/1183/1034 -f 2380/1218/1068 2357/1202/1052 2351/1196/1046 -f 2381/1219/1069 2352/1198/1048 2341/1189/1040 -f 2382/1220/1070 2342/1204/1054 2358/1203/1053 -f 2367/1221/1071 2383/1222/1072 2366/1223/1073 -f 2356/1201/1051 2384/1224/1074 2359/1225/1075 -f 2377/1226/1076 2391/1227/1077 2349/1228/1078 -f 2362/1229/1079 2386/1230/1080 2361/1231/1081 -f 2387/1232/1082 2364/1208/1058 2357/1202/1052 -f 2388/1233/1083 2365/1210/1060 2352/1198/1048 -f 2401/1234/1084 2375/1235/1085 2376/1236/1086 -f 2359/1225/1075 2389/1237/1087 2369/1238/1088 -f 2384/1224/1074 2389/1237/1087 2359/1225/1075 -f 2396/1239/1089 2387/1232/1082 2380/1218/1068 -f 2398/1240/1090 2372/1213/1063 2365/1210/1060 -f 2399/1241/1091 2382/1220/1070 2373/1214/1064 -f 2367/1221/1071 2402/1242/1092 2383/1222/1072 -f 2369/1238/1088 2403/1243/1093 2368/1244/1094 -f 2389/1237/1087 2403/1243/1093 2369/1238/1088 -f 2370/1211/1061 2392/1245/1095 2379/1246/1096 -f 2385/1247/1097 2404/1248/1098 2370/1211/1061 -f 2386/1249/1080 2405/1250/1099 2385/1247/1097 -f 2393/1251/1100 2405/1252/1099 2386/1230/1080 -f 2406/1253/1101 2395/1254/1102 2387/1232/1082 -f 2396/1239/1089 2380/1218/1068 2371/1212/1062 -f 2407/1255/1103 2381/1256/1069 2382/1220/1070 -f 2408/1257/1104 2400/1258/1105 2401/1259/1084 -f 2410/1260/1106 2409/1261/1107 2402/1242/1092 -f 2403/1243/1093 2411/1262/1108 2402/1263/1092 -f 2389/1237/1087 2412/1264/1109 2403/1243/1093 -f 2384/1224/1074 2413/1265/1110 2389/1237/1087 -f 2390/1266/1111 2413/1265/1110 2384/1224/1074 -f 2391/1227/1077 2414/1267/1112 2390/1266/1111 -f 2377/1226/1076 2414/1267/1112 2391/1227/1077 -f 2378/1217/1067 2415/1268/1113 2377/1226/1076 -f 2392/1245/1095 2415/1268/1113 2378/1217/1067 -f 2420/1269/1114 2394/1270/1115 2395/1271/1102 -f 2421/1272/1116 2396/1239/1089 2397/1273/1117 -f 2422/1274/1118 2398/1240/1090 2388/1233/1083 -f 2423/1275/1119 2381/1256/1069 2407/1255/1103 -f 2411/1276/1108 2427/1277/1120 2410/1260/1106 -f 2412/1264/1109 2428/1278/1121 2411/1262/1108 -f 2413/1265/1110 2429/1279/1122 2412/1264/1109 -f 2414/1267/1112 2430/1280/1123 2413/1265/1110 -f 2417/1281/1124 2433/1282/1125 2416/1283/1126 -f 2419/1284/1127 2434/1285/1128 2418/1286/1129 -f 2437/1287/1130 2406/1253/1101 2421/1272/1116 -f 2438/1288/1131 2421/1272/1116 2422/1274/1118 -f 2441/1289/1132 2424/1290/1133 2425/1291/1134 -f 2442/1292/1135 2426/1293/1136 2410/1260/1106 -f 2443/1294/1137 2442/1292/1135 2427/1277/1120 -f 2430/1280/1123 2445/1295/1138 2429/1279/1122 -f 2431/1296/1139 2446/1297/1140 2430/1280/1123 -f 2432/1298/1141 2447/1299/1142 2431/1296/1139 -f 2433/1282/1125 2448/1300/1143 2432/1298/1141 -f 2434/1301/1128 2449/1302/1144 2433/1303/1125 -f 2453/1304/1145 2437/1287/1130 2438/1288/1131 -f 2454/1305/1146 2438/1288/1131 2439/1306/1147 -f 2455/1307/1148 2439/1306/1147 2440/1308/1149 -f 2456/1309/1150 2440/1310/1149 2441/1289/1132 -f 2472/1311/1151 2455/1312/1148 2456/1313/1150 -f 2473/1314/1152 2456/1313/1150 2457/1315/1153 -f 2474/1316/1154 2457/1315/1153 2458/1317/1155 -f 2475/1318/1156 2458/1317/1155 2443/1319/1137 -f 2467/1320/1157 2484/1321/1158 2466/1322/1159 -f 2485/1323/1160 2468/1324/1161 2469/1325/1162 -f 2478/1326/1163 2494/1327/1164 2477/1328/1165 -f 2480/1329/1166 2495/1330/1167 2479/1331/1168 -f 2481/1332/1169 2496/1333/1170 2480/1329/1166 -f 2482/1334/1171 2497/1335/1172 2481/1332/1169 -f 2483/1336/1173 2498/1337/1174 2482/1334/1171 -f 2502/1338/1175 2485/1323/1160 2486/1339/1176 -f 2503/1340/1177 2487/1341/1178 2488/1342/1179 -f 2504/1343/1180 2488/1342/1179 2489/1344/1181 -f 2505/1345/1182 2489/1346/1181 2490/1347/1183 -f 2506/1348/1184 2490/1347/1183 2491/1349/1185 -f 2508/1350/1186 2493/1351/1187 2476/1352/1188 -f 2477/1328/1165 2508/1353/1186 2476/1354/1188 -f 2494/1327/1164 2508/1353/1186 2477/1328/1165 -f 2500/1355/1189 2514/1356/1190 2499/1357/1191 -f 2521/1358/1192 2520/1359/1193 2493/1360/1187 -f 2509/1361/1194 2522/1362/1195 2494/1327/1164 -f 2510/1363/1196 2523/1364/1197 2509/1365/1194 -f 2511/1366/1198 2524/1367/1199 2510/1363/1196 -f 2512/1368/1200 2525/1369/1201 2511/1366/1198 -f 2513/1370/1202 2526/1371/1203 2512/1368/1200 -f 2514/1356/1190 2527/1372/1204 2513/1370/1202 -f 2500/1355/1189 2528/1373/1205 2514/1356/1190 -f 2530/1374/1206 2501/1375/1207 2502/1338/1175 -f 2530/1374/1206 2502/1338/1175 2515/1376/1208 -f 2531/1377/1209 2515/1378/1208 2516/1379/1210 -f 2532/1380/1211 2516/1379/1210 2517/1381/1212 -f 2533/1382/1213 2517/1383/1212 2518/1384/1214 -f 2534/1385/1215 2518/1384/1214 2519/1386/1216 -f 2535/1387/1217 2519/1386/1216 2520/1359/1193 -f 2536/1388/1218 2521/1389/1192 2508/1353/1186 -f 2522/1362/1195 2536/1388/1218 2508/1353/1186 -f 2528/1373/1205 2542/1390/1219 2527/1372/1204 -f 2543/1391/1220 2530/1392/1206 2531/1377/1209 -f 2529/1393/1221 2557/1394/1222 2528/1395/1205 -f 2558/1396/1223 2529/1393/1221 2530/1374/1206 -f 2553/1397/1224 2569/1398/1225 2552/1399/1226 -f 2554/1400/1227 2570/1401/1228 2553/1397/1224 -f 2555/1402/1229 2571/1403/1230 2554/1400/1227 -f 2556/1404/1231 2572/1405/1232 2555/1402/1229 -f 2577/1406/1233 2560/1407/1234 2561/1408/1235 -f 2578/1409/1236 2561/1408/1235 2562/1410/1237 -f 2579/1411/1238 2562/1412/1237 2563/1413/1239 -f 2580/1414/1240 2563/1413/1239 2564/1415/1241 -f 2574/1416/1242 2588/1417/1243 2573/1418/1244 -f 2589/1419/1245 2574/1416/1242 2575/1420/1246 -f 2598/1421/1247 2612/1422/1248 2597/1423/1249 -f 2599/1424/1250 2613/1425/1251 2598/1421/1247 -f 2600/1426/1252 2615/1427/1253 2599/1424/1250 -f 2601/1428/1254 2616/1429/1255 2600/1426/1252 -f 2602/1430/1256 2617/1431/1257 2601/1428/1254 -f 2603/1432/1258 2618/1433/1259 2602/1434/1256 -f 2620/1435/1260 2605/1436/1261 2606/1437/1262 -f 2621/1438/1263 2606/1437/1262 2607/1439/1264 -f 2623/1440/1265 2607/1439/1264 2608/1441/1266 -f 2624/1442/1267 2608/1443/1266 2609/1444/1268 -f 2625/1445/1269 2609/1444/1268 2610/1446/1270 -f 2626/1447/1271 2610/1448/1270 2595/1449/1272 -f 2663/1450/1273 2703/1451/1274 2681/1452/1275 -f 2693/1453/1276 2673/1454/1277 2674/1455/1278 -f 2682/1456/1279 2704/1457/1280 2663/1450/1273 -f 2709/1458/1281 2690/1459/1282 2691/1460/1283 -f 2711/1461/1284 2673/1454/1277 2693/1453/1276 -f 2712/1462/1285 2693/1463/1276 2694/1464/1286 -f 2696/1465/1287 2713/1466/1288 2695/1467/1289 -f 2704/1457/1280 2728/1468/1290 2703/1451/1274 -f 2714/1469/1291 2708/1470/1292 2687/1471/1293 -f 2715/1472/1294 2687/1471/1293 2688/1473/1295 -f 2716/1474/1296 2688/1473/1295 2689/1475/1297 -f 2717/1476/1298 2689/1475/1297 2690/1459/1282 -f 2718/1477/1299 2711/1478/1284 2693/1463/1276 -f 2730/1479/1300 2717/1476/1298 2709/1458/1281 -f 2731/1480/1301 2709/1458/1281 2710/1481/1302 -f 2732/1482/1303 2710/1481/1302 2692/1483/1304 -f 2733/1484/1305 2692/1483/1304 2672/1485/1306 -f 2729/1486/1307 2774/1487/1308 2682/1488/1279 -f 2735/1489/1309 2729/1486/1307 2705/1490/1310 -f 2738/1491/1311 2708/1470/1292 2714/1469/1291 -f 2740/1492/1312 2716/1474/1296 2717/1476/1298 -f 2741/1493/1313 2733/1484/1305 2673/1454/1277 -f 2755/1494/1314 2738/1491/1311 2739/1495/1315 -f 2756/1496/1316 2739/1495/1315 2740/1492/1312 -f 2758/1497/1317 2740/1492/1312 2730/1479/1300 -f 2759/1498/1318 2730/1479/1300 2731/1480/1301 -f 2760/1499/1319 2731/1480/1301 2732/1482/1303 -f 2761/1500/1320 2732/1482/1303 2733/1484/1305 -f 2747/1501/1321 2762/1502/1322 2746/1503/1323 -f 2754/1504/1324 2772/1505/1325 2753/1506/1326 -f 2734/1507/1327 2773/1508/1328 2754/1504/1324 -f 2729/1486/1307 2763/1509/1329 2774/1487/1308 -f 2764/1510/1330 2729/1486/1307 2735/1489/1309 -f 2764/1510/1330 2735/1489/1309 2736/1511/1331 -f 2767/1512/1332 2741/1493/1313 2742/1513/1333 -f 2745/1514/1334 2768/1515/1335 2744/1516/1336 -f 2762/1502/1322 2768/1517/1335 2745/1518/1334 -f 2749/1519/1337 2770/1520/1338 2748/1521/1339 -f 2751/1522/1340 2771/1523/1341 2750/1524/1342 -f 2776/1525/1343 2766/1526/1344 2755/1494/1314 -f 2776/1525/1343 2755/1494/1314 2756/1496/1316 -f 2777/1527/1345 2757/1528/1346 2758/1497/1317 -f 2778/1529/1347 2759/1498/1318 2760/1499/1319 -f 2773/1508/1328 2786/1530/1348 2772/1505/1325 -f 2774/1531/1308 2804/1532/1349 2773/1508/1328 -f 2763/1533/1329 2787/1534/1350 2774/1531/1308 -f 2795/1535/1351 2778/1529/1347 2779/1536/1352 -f 2807/1537/1353 2788/1538/1354 2789/1539/1355 -f 2813/1540/1356 2779/1536/1352 2780/1541/1357 -f 2814/1542/1358 2780/1541/1357 2796/1543/1359 -f 2797/1544/1360 2815/1545/1361 2796/1546/1359 -f 2802/1547/1362 2819/1548/1363 2801/1549/1364 -f 2805/1550/1365 2836/1551/1366 2804/1532/1349 -f 2807/1537/1353 2820/1552/1367 2806/1553/1368 -f 2823/1554/1369 2809/1555/1370 2810/1556/1371 -f 2824/1557/1372 2810/1556/1371 2811/1558/1373 -f 2826/1559/1374 2812/1560/1375 2794/1561/1376 -f 2826/1559/1374 2794/1561/1376 2795/1535/1351 -f 2828/1562/1377 2813/1540/1356 2814/1542/1358 -f 2829/1563/1378 2814/1564/1358 2815/1565/1361 -f 2817/1566/1379 2831/1567/1380 2816/1568/1381 -f 2844/1569/1382 2825/1570/1383 2812/1560/1375 -f 2845/1571/1384 2828/1562/1377 2829/1572/1378 -f 2834/1573/1385 2849/1574/1386 2833/1575/1387 -f 2837/1576/1388 2850/1577/1389 2805/1550/1365 -f 2851/1578/1390 2837/1579/1388 2838/1580/1391 -f 2852/1581/1392 2838/1580/1391 2839/1582/1393 -f 2853/1583/1394 2825/1570/1383 2844/1569/1382 -f 2854/1584/1395 2826/1559/1374 2827/1585/1396 -f 2869/1586/1397 2827/1585/1396 2813/1540/1356 -f 2870/1587/1398 2828/1562/1377 2845/1571/1384 -f 2847/1588/1399 2855/1589/1400 2846/1590/1401 -f 2848/1591/1402 2857/1592/1403 2847/1588/1399 -f 2849/1574/1386 2858/1593/1404 2848/1594/1402 -f 2834/1573/1385 2859/1595/1405 2849/1574/1386 -f 2835/1596/1406 2860/1597/1407 2834/1573/1385 -f 2836/1551/1366 2861/1598/1408 2835/1596/1406 -f 2876/1599/1409 2837/1579/1388 2851/1578/1390 -f 2863/1600/1410 2852/1581/1392 2839/1582/1393 -f 2868/1601/1411 2853/1583/1394 2844/1569/1382 -f 2869/1586/1397 2854/1584/1395 2827/1585/1396 -f 2870/1587/1398 2869/1586/1397 2828/1562/1377 -f 2846/1590/1401 2871/1602/1412 2845/1603/1384 -f 2857/1592/1403 2872/1604/1413 2856/1605/1414 -f 2860/1597/1407 2874/1606/1415 2859/1595/1405 -f 2850/1577/1389 2875/1607/1416 2862/1608/1417 -f 2876/1599/1409 2850/1609/1389 2837/1579/1388 -f 2877/1610/1418 2868/1601/1411 2854/1584/1395 -f 2861/1598/1408 2884/1611/1419 2860/1597/1407 -f 2887/1612/1420 2850/1609/1389 2876/1599/1409 -f 2888/1613/1421 2876/1599/1409 2851/1578/1390 -f 2900/1614/1422 2852/1581/1392 2863/1600/1410 -f 2895/1615/1423 2878/1616/1424 2870/1587/1398 -f 2871/1602/1412 2896/1617/1425 2870/1618/1398 -f 2872/1604/1413 2879/1619/1426 2855/1589/1400 -f 2886/1620/1427 2899/1621/1428 2885/1622/1429 -f 2900/1614/1422 2888/1613/1421 2852/1581/1392 -f 2901/1623/1430 2894/1624/1431 2877/1610/1418 -f 2897/1625/1432 2908/1626/1433 2882/1627/1434 -f 2898/1628/1435 2909/1629/1436 2897/1630/1432 -f 2914/1631/1437 2900/1614/1422 2889/1632/1438 -f 2920/1633/1439 2901/1623/1430 2902/1634/1440 -f 2903/1635/1441 2923/1636/1442 2896/1617/1425 -f 2905/1637/1443 2904/1638/1444 2879/1619/1426 -f 2899/1621/1428 2926/1639/1445 2910/1640/1446 -f 2928/1641/1447 2913/1642/1448 2900/1614/1422 -f 2929/1643/1449 2900/1614/1422 2914/1631/1437 -f 2930/1644/1450 2901/1623/1430 2920/1633/1439 -f 2905/1637/1443 2931/1645/1451 2904/1638/1444 -f 2906/1646/1452 2932/1647/1453 2905/1637/1443 -f 2907/1648/1454 2933/1649/1455 2906/1646/1452 -f 2924/1650/1456 2933/1649/1455 2907/1648/1454 -f 2925/1651/1457 2934/1652/1458 2924/1653/1456 -f 2926/1639/1445 2935/1654/1459 2925/1655/1457 -f 2927/1656/1460 2953/1657/1461 2926/1639/1445 -f 2937/1658/1462 2912/1659/1463 2913/1642/1448 -f 2938/1660/1464 2929/1643/1449 2914/1631/1437 -f 2931/1645/1451 2946/1661/1465 2903/1635/1441 -f 2936/1662/1466 2953/1657/1461 2927/1656/1460 -f 2954/1663/1467 2937/1658/1462 2928/1641/1447 -f 2947/1664/1468 2957/1665/1469 2932/1647/1453 -f 2950/1666/1470 2958/1667/1471 2949/1668/1472 -f 2959/1669/1473 2936/1670/1466 2911/1671/1474 -f 2959/1672/1473 2911/1673/1474 2912/1659/1463 -f 2961/1674/1475 2955/1675/1476 2938/1660/1464 -f 2969/1676/1477 2945/1677/1478 2921/1678/1479 -f 2956/1679/1480 2971/1680/1481 2946/1661/1465 -f 2953/1657/1461 2978/1681/1482 2952/1682/1483 -f 2936/1662/1466 2979/1683/1484 2953/1657/1461 -f 2980/1684/1485 2960/1685/1486 2954/1663/1467 -f 2972/1686/1487 2983/1687/1488 2957/1665/1469 -f 2979/1683/1484 3017/1688/1489 2978/1681/1482 -f 2984/1689/1490 2936/1670/1466 2959/1669/1473 -f 2987/1690/1491 2981/1691/1492 2961/1674/1475 -f 2993/1692/1493 2968/1693/1494 2945/1677/1478 -f 2982/1694/1495 2995/1695/1496 2971/1680/1481 -f 2978/1696/1482 3001/1697/1497 2977/1698/1498 -f 3002/1699/1499 2986/1700/1500 2980/1684/1485 -f 2996/1701/1501 3005/1702/1502 2983/1687/1488 -f 3008/1703/1503 2988/1704/1504 2989/1705/1505 -f 3009/1706/1506 2990/1707/1507 2991/1708/1508 -f 3011/1709/1509 2967/1710/1510 2968/1711/1494 -f 3004/1712/1511 3013/1713/1512 2995/1695/1496 -f 2998/1714/1513 3014/1715/1514 2997/1716/1515 -f 3000/1717/1516 3015/1718/1517 2999/1719/1518 -f 2979/1720/1484 3018/1721/1519 3017/1722/1489 -f 3021/1723/1520 2986/1700/1500 3002/1699/1499 -f 3012/1724/1521 3029/1725/1522 2993/1726/1493 -f 3013/1713/1512 3030/1727/1523 3012/1728/1521 -f 3044/1729/1524 3028/1730/1525 3029/1725/1522 -f 3045/1731/1526 3029/1732/1522 3030/1727/1523 -f 3046/1733/1527 3030/1727/1523 3013/1713/1512 -f 3037/1734/1528 3053/1735/1529 3036/1736/1530 -f 3038/1737/1531 3054/1738/1532 3037/1739/1528 -f 3039/1740/1533 3055/1741/1534 3038/1737/1531 -f 3022/1742/1535 3056/1743/1536 3021/1723/1520 -f 3023/1744/1537 3057/1745/1538 3022/1742/1535 -f 3067/1746/1539 3031/1747/1540 3048/1748/1541 -f 3069/1749/1542 3049/1750/1543 3050/1751/1544 -f 3070/1752/1545 3050/1751/1544 3051/1753/1546 -f 3072/1754/1547 3071/1755/1548 3053/1756/1529 -f 3054/1738/1532 3073/1757/1549 3053/1758/1529 -f 3055/1741/1534 3074/1759/1550 3054/1738/1532 -f 3039/1740/1533 3075/1760/1551 3055/1741/1534 -f 3058/1761/1552 3076/1762/1553 3057/1745/1538 -f 3077/1763/1554 3066/1764/1555 3047/1765/1556 -f 3078/1766/1557 3068/1767/1558 3069/1749/1542 -f 3074/1759/1550 3082/1768/1559 3073/1757/1549 -f 3057/1745/1538 3083/1769/1560 3056/1743/1536 -f 3060/1770/1561 3085/1771/1562 3059/1772/1563 -f 3062/1773/1564 3086/1774/1565 3061/1775/1566 -f 3091/1776/1567 3031/1747/1540 3067/1746/1539 -f 3095/1777/1568 3079/1778/1569 3080/1779/1570 -f 3075/1760/1551 3098/1780/1571 3074/1759/1550 -f 3076/1762/1553 3083/1769/1560 3057/1745/1538 -f 3088/1781/1572 3101/1782/1573 3087/1783/1574 -f 3102/1784/1575 3088/1785/1572 3089/1786/1576 -f 3103/1787/1577 3089/1786/1576 3090/1788/1578 -f 3104/1789/1579 3077/1763/1554 3031/1747/1540 -f 3105/1790/1580 3094/1791/1581 3079/1792/1569 -f 3105/1793/1580 3079/1778/1569 3095/1777/1568 -f 3106/1794/1582 3096/1795/1583 3097/1796/1584 -f 3098/1780/1571 3107/1797/1585 3097/1798/1584 -f 3075/1760/1551 3108/1799/1586 3098/1780/1571 -f 3083/1769/1560 3109/1800/1587 3075/1760/1551 -f 3076/1762/1553 3111/1801/1588 3083/1769/1560 -f 3099/1802/1589 3112/1803/1590 3084/1804/1591 -f 3100/1805/1592 3113/1806/1593 3099/1802/1589 -f 3102/1807/1575 3116/1808/1594 3101/1809/1573 -f 3117/1810/1595 3102/1784/1575 3103/1787/1577 -f 3118/1811/1596 3103/1787/1577 3077/1763/1554 -f 3120/1812/1597 3092/1813/1598 3093/1814/1599 -f 3121/1815/1600 3094/1791/1581 3105/1790/1580 -f 3119/1816/1601 3133/1817/1602 3118/1811/1596 -f 3142/1818/1603 3126/1819/1604 3109/1800/1587 -f 3119/1816/1601 3150/1820/1605 3133/1817/1602 -f 3136/1821/1606 3152/1822/1607 3135/1823/1608 -f 3158/1824/1609 3142/1818/1603 3143/1825/1610 -f 3159/1826/1611 3143/1825/1610 3144/1827/1612 -f 3160/1828/1613 3144/1827/1612 3145/1829/1614 -f 3162/1830/1615 3161/1831/1616 3147/1832/1617 -f 3137/1833/1618 3169/1834/1619 3136/1821/1606 -f 3172/1835/1620 3171/1836/1621 3155/1837/1622 -f 3156/1838/1623 3173/1839/1624 3155/1837/1622 -f 3160/1828/1613 3176/1840/1625 3159/1826/1611 -f 3146/1841/1626 3177/1842/1627 3160/1828/1613 -f 3161/1831/1616 3178/1843/1628 3146/1841/1626 -f 3184/1844/1629 3168/1845/1630 3169/1834/1619 -f 3185/1846/1631 3169/1834/1619 3170/1847/1632 -f 3186/1848/1633 3170/1847/1632 3171/1849/1621 -f 3187/1850/1634 3172/1851/1620 3173/1852/1624 -f 3160/1828/1613 3193/1853/1635 3176/1840/1625 -f 3201/1854/1636 3183/1855/1637 3167/1856/1638 -f 3202/1857/1639 3168/1845/1630 3184/1844/1629 -f 3177/1842/1627 3216/1858/1640 3160/1828/1613 -f 3195/1859/1641 3206/1860/1642 3194/1861/1643 -f 3208/1862/1644 3201/1863/1636 3168/1845/1630 -f 3202/1857/1639 3184/1844/1629 3203/1864/1645 -f 3209/1865/1646 3203/1864/1645 3204/1866/1647 -f 3188/1867/1648 3211/1868/1649 3187/1850/1634 -f 3189/1869/1650 3212/1870/1651 3188/1867/1648 -f 3190/1871/1652 3213/1872/1653 3189/1873/1650 -f 3191/1874/1654 3213/1872/1653 3190/1871/1652 -f 3192/1875/1655 3214/1876/1656 3191/1874/1654 -f 3193/1853/1635 3215/1877/1657 3192/1875/1655 -f 3207/1878/1658 3217/1879/1659 3195/1859/1641 -f 3219/1880/1660 3197/1881/1661 3198/1882/1662 -f 3220/1883/1663 3198/1882/1662 3199/1884/1664 -f 3220/1883/1663 3199/1884/1664 3200/1885/1665 -f 3221/1886/1666 3200/1885/1665 3201/1854/1636 -f 3222/1887/1667 3202/1857/1639 3203/1864/1645 -f 3214/1876/1656 3226/1888/1668 3213/1872/1653 -f 3205/1889/1669 3228/1890/1670 3216/1858/1640 -f 3235/1891/1671 3221/1886/1666 3201/1854/1636 -f 3236/1892/1672 3208/1862/1644 3202/1857/1639 -f 3236/1892/1672 3202/1857/1639 3222/1887/1667 -f 3237/1893/1673 3222/1887/1667 3209/1865/1646 -f 3223/1894/1674 3209/1895/1646 3210/1896/1675 -f 3225/1897/1676 3239/1898/1677 3224/1899/1678 -f 3226/1888/1668 3240/1900/1679 3225/1897/1676 -f 3214/1876/1656 3241/1901/1680 3226/1888/1668 -f 3215/1877/1657 3241/1901/1680 3214/1876/1656 -f 3247/1902/1681 3233/1903/1682 3234/1904/1683 -f 3248/1905/1684 3234/1904/1683 3221/1886/1666 -f 3249/1906/1685 3208/1862/1644 3236/1892/1672 -f 3250/1907/1686 3236/1892/1672 3237/1893/1673 -f 3251/1908/1687 3237/1893/1673 3223/1909/1674 -f 3270/1910/1688 3252/1911/1689 3253/1912/1690 -f 3274/1913/1691 3258/1914/1692 3259/1915/1693 -f 3265/1916/1694 3280/1917/1695 3264/1918/1696 -f 3266/1919/1697 3281/1920/1698 3265/1916/1694 -f 3267/1921/1699 3297/1922/1700 3266/1923/1697 -f 3268/1924/1701 3282/1925/1702 3267/1921/1699 -f 3269/1926/1703 3283/1927/1704 3268/1924/1701 -f 3252/1928/1689 3284/1929/1705 3269/1926/1703 -f 3289/1930/1706 3257/1931/1707 3258/1914/1692 -f 3291/1932/1708 3274/1913/1691 3275/1933/1709 -f 3280/1917/1695 3295/1934/1710 3279/1935/1711 -f 3281/1920/1698 3296/1936/1712 3280/1917/1695 -f 3283/1927/1704 3299/1937/1713 3282/1925/1702 -f 3301/1938/1714 3285/1939/1715 3286/1940/1716 -f 3302/1941/1717 3286/1940/1716 3287/1942/1718 -f 3303/1943/1719 3287/1944/1718 3288/1945/1720 -f 3304/1946/1721 3288/1945/1720 3289/1930/1706 -f 3306/1947/1722 3290/1948/1723 3291/1932/1708 -f 3294/1949/1724 3308/1950/1725 3293/1951/1726 -f 3295/1952/1710 3309/1953/1727 3294/1949/1724 -f 3296/1936/1712 3310/1954/1728 3295/1934/1710 -f 3297/1955/1700 3311/1956/1729 3296/1936/1712 -f 3298/1957/1730 3312/1958/1731 3297/1922/1700 -f 3301/1938/1714 3328/1959/1732 3314/1960/1733 -f 3316/1961/1734 3302/1941/1717 3303/1962/1719 -f 3317/1963/1735 3303/1943/1719 3304/1946/1721 -f 3318/1964/1736 3304/1946/1721 3305/1965/1737 -f 3319/1966/1738 3305/1965/1737 3290/1948/1723 -f 3320/1967/1739 3290/1948/1723 3306/1947/1722 -f 3321/1968/1740 3306/1969/1722 3307/1970/1741 -f 3322/1971/1742 3307/1970/1741 3308/1950/1725 -f 3310/1972/1728 3324/1973/1743 3309/1953/1727 -f 3311/1956/1729 3325/1974/1744 3310/1954/1728 -f 3312/1975/1731 3326/1976/1745 3311/1956/1729 -f 3298/1957/1730 3339/1977/1746 3312/1958/1731 -f 3313/1978/1747 3327/1979/1748 3298/1957/1730 -f 3314/1960/1733 3328/1959/1732 3313/1980/1747 -f 3329/1981/1749 3328/1959/1732 3315/1982/1750 -f 3329/1981/1749 3315/1982/1750 3316/1961/1734 -f 3330/1983/1751 3316/1984/1734 3317/1963/1735 -f 3331/1985/1752 3317/1963/1735 3318/1964/1736 -f 3333/1986/1753 3318/1964/1736 3319/1966/1738 -f 3334/1987/1754 3320/1967/1739 3321/1988/1740 -f 3335/1989/1755 3321/1968/1740 3322/1971/1742 -f 3323/1990/1756 3336/1991/1757 3322/1971/1742 -f 3324/1973/1743 3336/1991/1757 3323/1990/1756 -f 3325/1974/1744 3337/1992/1758 3324/1993/1743 -f 3326/1976/1745 3338/1994/1759 3325/1974/1744 -f 3327/1979/1748 3340/1995/1760 3339/1977/1746 -f 3328/1996/1732 3341/1997/1761 3327/1979/1748 -f 3342/1998/1762 3341/1999/1761 3329/1981/1749 -f 3343/2000/1763 3329/1981/1749 3330/2001/1751 -f 3344/2002/1764 3330/1983/1751 3331/1985/1752 -f 3345/2003/1765 3331/1985/1752 3332/2004/1766 -f 3346/2005/1767 3332/2004/1766 3333/1986/1753 -f 3347/2006/1768 3319/1966/1738 3334/1987/1754 -f 3348/2007/1769 3334/2008/1754 3335/1989/1755 -f 3336/1991/1757 3348/2007/1769 3335/1989/1755 -f 3337/2009/1758 3350/2010/1770 3336/1991/1757 -f 3338/1994/1759 3351/2011/1771 3337/1992/1758 -f 3339/2012/1746 3352/2013/1772 3338/1994/1759 -f 3340/1995/1760 3353/2014/1773 3339/1977/1746 -f 3341/1997/1761 3354/2015/1774 3340/1995/1760 -f 3356/2016/1775 3342/1998/1762 3343/2000/1763 -f 3357/2017/1776 3343/2000/1763 3344/2018/1764 -f 3358/2019/1777 3344/2002/1764 3345/2003/1765 -f 3359/2020/1778 3346/2005/1767 3333/1986/1753 -f 3359/2020/1778 3333/1986/1753 3347/2006/1768 -f 3360/2021/1779 3347/2022/1768 3348/2007/1769 -f 3350/2010/1770 3362/2023/1780 3349/2024/1781 -f 3351/2025/1771 3363/2026/1782 3350/2010/1770 -f 3352/2013/1772 3364/2027/1783 3351/2011/1771 -f 3355/2028/1784 3354/2029/1774 3342/1998/1762 -f 3367/2030/1785 3355/2028/1784 3356/2016/1775 -f 3368/2031/1786 3356/2016/1775 3357/2017/1776 -f 3369/2032/1787 3357/2033/1776 3358/2019/1777 -f 3370/2034/1788 3358/2019/1777 3346/2005/1767 -f 3371/2035/1789 3346/2005/1767 3359/2020/1778 -f 3372/2036/1790 3359/2020/1778 3360/2037/1779 -f 3361/2038/1791 3373/2039/1792 3360/2021/1779 -f 3362/2023/1780 3374/2040/1793 3361/2038/1791 -f 3363/2026/1782 3375/2041/1794 3362/2023/1780 -f 3364/2027/1783 3376/2042/1795 3363/2043/1782 -f 3352/2013/1772 3377/2044/1796 3364/2027/1783 -f 3382/2045/1797 3367/2030/1785 3368/2031/1786 -f 3383/2046/1798 3368/2031/1786 3369/2047/1787 -f 3384/2048/1799 3369/2032/1787 3370/2034/1788 -f 3370/2034/1788 3371/2035/1789 3372/2036/1790 -f 3375/2041/1794 3388/2049/1800 3374/2040/1793 -f 3376/2050/1795 3389/2051/1801 3375/2041/1794 -f 3378/2052/1802 3391/2053/1803 3377/2054/1796 -f 3379/2055/1804 3392/2056/1805 3378/2052/1802 -f 3393/2057/1806 3379/2058/1804 3380/2059/1807 -f 3395/2060/1808 3381/2061/1809 3382/2045/1797 -f 3384/2048/1799 3400/2062/1810 3383/2063/1798 -f 3386/2064/1811 3404/2065/1812 3385/2066/1813 -f 3387/2067/1814 3406/2068/1815 3386/2069/1811 -f 3389/2051/1801 3410/2070/1816 3388/2049/1800 -f 3419/2071/1817 3418/2072/1818 3442/2073/1819 -f 3422/2074/1820 3421/2075/1821 3420/2076/1822 -f 3429/2077/1823 3431/2078/1824 3430/2079/1825 -f 3432/2080/1826 3434/2081/1827 3433/2082/1828 -f 3435/2083/1829 3437/2084/1830 3436/2085/1831 -f 3452/2086/1832 3468/2087/1833 3451/2088/1834 -f 3427/2089/1835 3448/2090/1836 3425/2091/1837 -f 3438/2092/1838 3455/2093/1839 3436/2085/1831 -f 3435/2083/1829 3453/2094/1840 3433/2095/1828 -f 3432/2080/1826 3451/2088/1834 3430/2079/1825 -f 3438/2092/1838 3456/2096/1841 3455/2093/1839 -f 3429/2077/1823 3449/2097/1842 3427/2098/1835 -f 3436/2085/1831 3454/2099/1843 3435/2083/1829 -f 3433/2095/1828 3452/2100/1832 3432/2101/1826 -f 3430/2079/1825 3450/2102/1844 3429/2077/1823 -f 3453/2094/1840 3469/2103/1845 3452/2100/1832 -f 3454/2099/1843 3470/2104/1846 3453/2094/1840 -f 3447/2105/1847 3464/2106/1848 3463/2107/1849 -f 3448/2090/1836 3464/2106/1848 3447/2105/1847 -f 3455/2093/1839 3471/2108/1850 3454/2099/1843 -f 3449/2097/1842 3465/2109/1851 3448/2110/1836 -f 3450/2102/1844 3466/2111/1852 3449/2097/1842 -f 3451/2088/1834 3467/2112/1853 3450/2102/1844 -f 3487/2113/1854 3503/2114/1854 3502/2115/1854 -f 3504/2116/1855 3513/2117/1856 3503/2114/1857 -f 3481/2118/1854 3496/2119/1858 3480/2120/1854 -f 3482/2121/1854 3497/2122/1859 3481/2118/1854 -f 3476/2123/1854 3491/2124/1860 3490/2125/1854 -f 3489/2126/1854 3504/2116/1855 3488/2127/1854 -f 3483/2128/1854 3498/2129/1861 3482/2121/1854 -f 3477/2130/1854 3493/2131/1862 3492/2132/1863 -f 3490/2125/1854 3505/2133/1864 3489/2126/1854 -f 3484/2134/1854 3499/2135/1865 3483/2128/1854 -f 3484/2134/1854 3500/2136/1866 3499/2135/1865 -f 3478/2137/1854 3494/2138/1867 3493/2131/1862 -f 3485/2139/1854 3501/2140/1868 3500/2136/1866 -f 3479/2141/1854 3495/2142/1869 3494/2138/1867 -f 3486/2143/1854 3502/2115/1854 3501/2140/1868 -f 3507/2144/1870 3508/2145/1871 3509/2146/1872 -f 3493/2147/1862 3506/2148/1873 3492/2149/1863 -f 3491/2150/1860 3514/2151/1874 3505/2152/1864 -f 3495/2153/1869 3507/2154/1870 3494/2155/1867 -f 3516/2156/1875 3539/2157/1876 3515/2158/1877 -f 3517/2159/1878 3539/2157/1876 3516/2156/1875 -f 3518/2160/1879 3540/2161/1880 3517/2162/1878 -f 3519/2163/1881 3541/2164/1882 3518/2160/1879 -f 3520/2165/1883 3542/2166/1884 3519/2163/1881 -f 3521/2167/1885 3543/2168/1886 3520/2169/1883 -f 3522/2170/1887 3544/2171/1888 3521/2167/1885 -f 3525/2172/1889 3546/2173/1890 3524/2174/1891 -f 3547/2175/1892 3526/2176/1893 3527/2177/1894 -f 3547/2175/1892 3527/2177/1894 3528/2178/1895 -f 3549/2179/1896 3530/2180/1897 3531/2181/1898 -f 3550/2182/1899 3531/2181/1898 3532/2183/1900 -f 3551/2184/1901 3532/2183/1900 3533/2185/1902 -f 3552/2186/1903 3533/2185/1902 3534/2187/1904 -f 3552/2186/1903 3534/2187/1904 3535/2188/1905 -f 3553/2189/1906 3535/2190/1905 3536/2191/1907 -f 3554/2192/1908 3537/2193/1909 3538/2194/1910 -f 3545/2195/1911 3560/2196/1912 3544/2171/1888 -f 3547/2175/1892 3561/2197/1913 3546/2173/1890 -f 3562/2198/1914 3547/2175/1892 3548/2199/1915 -f 3565/2200/1916 3550/2182/1899 3551/2184/1901 -f 3567/2201/1917 3553/2189/1906 3554/2192/1908 -f 3556/2202/1918 3569/2203/1919 3555/2204/1920 -f 3557/2205/1921 3570/2206/1922 3556/2202/1918 -f 3558/2207/1923 3571/2208/1924 3557/2205/1921 -f 3559/2209/1925 3572/2210/1926 3558/2211/1923 -f 3561/2197/1913 3574/2212/1927 3545/2213/1911 -f 3575/2214/1928 3561/2197/1913 3562/2198/1914 -f 3577/2215/1929 3562/2216/1914 3563/2217/1930 -f 3578/2218/1931 3563/2217/1930 3564/2219/1932 -f 3583/2220/1933 3582/2221/1934 3568/2222/1935 -f 3571/2208/1924 3586/2223/1936 3570/2206/1922 -f 3572/2210/1926 3587/2224/1937 3571/2225/1924 -f 3573/2226/1938 3588/2227/1939 3572/2210/1926 -f 3574/2228/1927 3589/2229/1940 3573/2226/1938 -f 3592/2230/1941 3576/2231/1942 3577/2215/1929 -f 3593/2232/1943 3577/2215/1929 3578/2218/1931 -f 3594/2233/1944 3578/2218/1931 3579/2234/1945 -f 3595/2235/1946 3579/2234/1945 3580/2236/1947 -f 3596/2237/1948 3580/2236/1947 3581/2238/1949 -f 3584/2239/1950 3598/2240/1951 3583/2241/1933 -f 3590/2242/1952 3603/2243/1953 3589/2244/1940 -f 3604/2245/1954 3591/2246/1955 3592/2247/1941 -f 3606/2248/1956 3592/2230/1941 3593/2232/1943 -f 3607/2249/1957 3593/2232/1943 3594/2233/1944 -f 3608/2250/1958 3594/2233/1944 3595/2235/1946 -f 3609/2251/1959 3595/2235/1946 3596/2237/1948 -f 3610/2252/1960 3596/2253/1948 3582/2221/1934 -f 3611/2254/1961 3597/2255/1962 3598/2256/1951 -f 3599/2257/1963 3612/2258/1964 3598/2259/1951 -f 3600/2260/1965 3613/2261/1966 3599/2257/1963 -f 3601/2262/1967 3614/2263/1968 3600/2260/1965 -f 3602/2264/1969 3615/2265/1970 3601/2266/1967 -f 3603/2243/1953 3616/2267/1971 3602/2268/1969 -f 3590/2242/1952 3617/2269/1972 3603/2243/1953 -f 3618/2270/1973 3633/2271/1974 3617/2269/1972 -f 3641/2272/1975 3626/2273/1976 3627/2274/1977 -f 3630/2275/1978 3644/2276/1979 3629/2277/1980 -f 3631/2278/1981 3645/2279/1982 3630/2280/1978 -f 3632/2281/1983 3646/2282/1984 3631/2278/1981 -f 3633/2271/1974 3647/2283/1985 3632/2281/1983 -f 3655/2284/1986 3640/2285/1987 3626/2273/1976 -f 3656/2286/1988 3655/2284/1986 3626/2273/1976 -f 3669/2287/1989 3651/2288/1990 3652/2289/1991 -f 3670/2290/1992 3652/2289/1991 3653/2291/1993 -f 3671/2292/1994 3653/2291/1993 3654/2293/1995 -f 3663/2294/1996 3678/2295/1997 3662/2296/1998 -f 3687/2297/1999 3703/2298/2000 3686/2299/2001 -f 3691/2300/2002 3706/2301/2003 3690/2302/2004 -f 3707/2303/2005 3695/2304/2006 3696/2305/2007 -f 3708/2306/2008 3697/2307/2009 3698/2308/2010 -f 3695/2304/2006 3721/2309/2011 3694/2310/2012 -f 3729/2311/2013 3712/2312/2014 3713/2313/2015 -f 3718/2314/2016 3733/2315/2017 3717/2316/2018 -f 3740/2317/2019 3712/2312/2014 3729/2311/2013 -f 3745/2318/2020 3718/2314/2016 3719/2319/2021 -f 3734/2320/2022 3748/2321/2023 3721/2309/2011 -f 3711/2322/2024 3754/2323/2025 3728/2324/2026 -f 3754/2323/2025 3766/2325/2027 3753/2326/2028 -f 3771/2327/2029 3756/2328/2030 3757/2329/2031 -f 3772/2330/2032 3757/2329/2031 3758/2331/2033 -f 3773/2332/2034 3758/2333/2033 3759/2334/2035 -f 3774/2335/2036 3759/2334/2035 3760/2336/2037 -f 3763/2337/2038 3779/2338/2039 3762/2339/2040 -f 3764/2340/2041 3780/2341/2042 3763/2337/2038 -f 3765/2342/2043 3781/2343/2044 3764/2340/2041 -f 3766/2325/2027 3782/2344/2045 3765/2342/2043 -f 3790/2345/2046 3776/2346/2047 3777/2347/2048 -f 3783/2348/2049 3795/2349/2050 3782/2344/2045 -f 3796/2350/2051 3784/2351/2052 3785/2352/2053 -f 3817/2353/2054 3801/2354/2055 3776/2346/2047 -f 3808/2355/2056 3823/2356/2057 3807/2357/2058 -f 3824/2358/2059 3813/2359/2060 3814/2360/2061 -f 3819/2361/2062 3828/2362/2063 3802/2363/2064 -f 3820/2364/2065 3829/2365/2066 3819/2361/2062 -f 3821/2366/2067 3830/2367/2068 3820/2364/2065 -f 3822/2368/2069 3831/2369/2070 3821/2366/2067 -f 3823/2370/2057 3832/2371/2071 3822/2368/2069 -f 3808/2372/2056 3833/2373/2072 3823/2370/2057 -f 3809/2374/2073 3834/2375/2074 3808/2355/2056 -f 3810/2376/2075 3835/2377/2076 3809/2374/2073 -f 3836/2378/2077 3810/2379/2075 3811/2380/2078 -f 3837/2381/2079 3811/2380/2078 3812/2382/2080 -f 3838/2383/2081 3812/2382/2080 3813/2359/2060 -f 3838/2383/2081 3813/2359/2060 3824/2358/2059 -f 3839/2384/2082 3824/2385/2059 3825/2386/2083 -f 3840/2387/2084 3825/2386/2083 3826/2388/2085 -f 3841/2389/2086 3826/2388/2085 3827/2390/2087 -f 3842/2391/2088 3827/2392/2087 3817/2353/2054 -f 3834/2393/2074 3847/2394/2089 3833/2373/2072 -f 3835/2377/2076 3859/2395/2090 3834/2375/2074 -f 3848/2396/2091 3835/2377/2076 3836/2397/2077 -f 3850/2398/2092 3838/2383/2081 3839/2399/2082 -f 3851/2400/2093 3839/2384/2082 3840/2387/2084 -f 3852/2401/2094 3840/2387/2084 3826/2388/2085 -f 3844/2402/2095 3854/2403/2096 3829/2365/2066 -f 3845/2404/2097 3855/2405/2098 3844/2402/2095 -f 3846/2406/2099 3856/2407/2100 3845/2404/2097 -f 3847/2394/2089 3857/2408/2101 3846/2406/2099 -f 3861/2409/2102 3849/2410/2103 3837/2381/2079 -f 3858/2411/2104 3871/2412/2105 3857/2408/2101 -f 3860/2413/2106 3883/2414/2107 3859/2395/2090 -f 3872/2415/2108 3860/2413/2106 3848/2396/2091 -f 3874/2416/2109 3861/2409/2102 3862/2417/2110 -f 3885/2418/2111 3873/2419/2112 3861/2409/2102 -f 3885/2418/2111 3861/2409/2102 3874/2416/2109 -f 3886/2420/2113 3874/2421/2109 3875/2422/2114 -f 3887/2423/2115 3875/2422/2114 3876/2424/2116 -f 3888/2425/2117 3876/2424/2116 3865/2426/2118 -f 3889/2427/2119 3865/2426/2118 3866/2428/2120 -f 3890/2429/2121 3866/2430/2120 3867/2431/2122 -f 3879/2432/2123 3892/2433/2124 3878/2434/2125 -f 3880/2435/2126 3893/2436/2127 3879/2432/2123 -f 3881/2437/2128 3894/2438/2129 3880/2435/2126 -f 3882/2439/2130 3895/2440/2131 3881/2437/2128 -f 3883/2441/2107 3896/2442/2132 3882/2439/2130 -f 3897/2443/2133 3884/2444/2134 3872/2415/2108 -f 3897/2443/2133 3872/2415/2108 3873/2445/2112 -f 3898/2446/2135 3873/2419/2112 3885/2418/2111 -f 3899/2447/2136 3885/2418/2111 3886/2448/2113 -f 3877/2449/2137 3902/2450/2138 3867/2431/2122 -f 3891/2451/2139 3903/2452/2140 3877/2449/2137 -f 3892/2433/2124 3904/2453/2141 3891/2454/2139 -f 3893/2436/2127 3905/2455/2142 3892/2433/2124 -f 3884/2456/2134 3909/2457/2143 3896/2458/2132 -f 3911/2459/2144 3897/2443/2133 3898/2460/2135 -f 3902/2450/2138 3916/2461/2145 3890/2429/2121 -f 3908/2462/2146 3920/2463/2147 3907/2464/2148 -f 3903/2452/2140 3927/2465/2149 3902/2450/2138 -f 3909/2457/2143 3932/2466/2150 3908/2467/2146 -f 3934/2468/2151 3910/2469/2152 3921/2470/2153 -f 3934/2468/2151 3921/2470/2153 3922/2471/2154 -f 3935/2472/2155 3922/2471/2154 3923/2473/2156 -f 3936/2474/2157 3923/2475/2156 3924/2476/2158 -f 3937/2477/2159 3924/2478/2158 3925/2479/2160 -f 3926/2480/2161 3941/2481/2162 3916/2461/2145 -f 3928/2482/2163 3942/2483/2164 3927/2465/2149 -f 3929/2484/2165 3943/2485/2166 3928/2482/2163 -f 3930/2486/2167 3944/2487/2168 3929/2484/2165 -f 3931/2488/2169 3945/2489/2170 3930/2490/2167 -f 3948/2491/2171 3933/2492/2172 3910/2469/2152 -f 3949/2493/2173 3910/2469/2152 3934/2468/2151 -f 3933/2494/2172 3961/2495/2174 3947/2496/2175 -f 3962/2497/2176 3961/2495/2174 3933/2494/2172 -f 3963/2498/2177 3948/2491/2171 3949/2493/2173 -f 3964/2499/2178 3949/2493/2173 3950/2500/2179 -f 3964/2499/2178 3950/2500/2179 3951/2501/2180 -f 3965/2502/2181 3951/2501/2180 3952/2503/2182 -f 3966/2504/2183 3952/2505/2182 3953/2506/2184 -f 3967/2507/2185 3953/2506/2184 3954/2508/2186 -f 3968/2509/2187 3954/2510/2186 3955/2511/2188 -f 3969/2512/2189 3955/2511/2188 3940/2513/2190 -f 3956/2514/2191 3971/2515/2192 3941/2481/2162 -f 3978/2516/2193 3963/2498/2177 3964/2499/2178 -f 3970/2517/2194 3983/2518/2195 3969/2512/2189 -f 3971/2515/2192 3984/2519/2196 3941/2481/2162 -f 3972/2520/2197 3985/2521/2198 3971/2515/2192 -f 3973/2522/2199 3985/2521/2198 3972/2520/2197 -f 3974/2523/2200 3986/2524/2201 3973/2522/2199 -f 3975/2525/2202 3987/2526/2203 3974/2523/2200 -f 3976/2527/2204 3988/2528/2205 3975/2525/2202 -f 3977/2529/2206 3989/2530/2207 3976/2527/2204 -f 3962/2497/2176 3990/2531/2208 3977/2532/2206 -f 3991/2533/2209 3948/2491/2171 3963/2498/2177 -f 3993/2534/2210 3978/2516/2193 3979/2535/2211 -f 3994/2536/2212 3979/2537/2211 3980/2538/2213 -f 3995/2539/2214 3980/2538/2213 3981/2540/2215 -f 3997/2541/2216 3981/2540/2215 3982/2542/2217 -f 3998/2543/2218 3982/2544/2217 3983/2518/2195 -f 3988/2528/2205 4004/2545/2219 3987/2526/2203 -f 4010/2546/2220 3997/2541/2216 3998/2547/2218 -f 3999/2548/2221 4013/2549/2222 3984/2519/2196 -f 4006/2550/2223 4021/2551/2224 4005/2552/2225 -f 4014/2553/2226 3991/2533/2209 3992/2554/2227 -f 4017/2555/2228 3994/2536/2212 3995/2539/2214 -f 4018/2556/2229 3995/2539/2214 3996/2557/2230 -f 4018/2556/2229 3996/2557/2230 3997/2541/2216 -f 4012/2558/2231 4011/2559/2232 3983/2518/2195 -f 4000/2560/2233 4060/2561/2234 3999/2548/2221 -f 4001/2562/2235 4031/2563/2236 4000/2560/2233 -f 4004/2545/2219 4020/2564/2237 4003/2565/2238 -f 4024/2566/2239 4008/2567/2240 4009/2568/2241 -f 4025/2569/2242 4009/2568/2241 3991/2533/2209 -f 4026/2570/2243 4016/2571/2244 4017/2555/2228 -f 4013/2549/2222 4030/2572/2245 4012/2573/2231 -f 4036/2574/2246 4035/2575/2247 4024/2576/2239 -f 4037/2577/2248 4024/2566/2239 4009/2568/2241 -f 4025/2569/2242 4014/2553/2226 4015/2578/2249 -f 4038/2579/2250 4015/2578/2249 4016/2580/2244 -f 4039/2581/2251 4016/2571/2244 4026/2570/2243 -f 4040/2582/2252 4026/2570/2243 4027/2583/2253 -f 4041/2584/2254 4027/2583/2253 4028/2585/2255 -f 4042/2586/2256 4028/2587/2255 4029/2588/2257 -f 4030/2589/2245 4043/2590/2258 4029/2588/2257 -f 4031/2563/2236 4044/2591/2259 4060/2561/2234 -f 4019/2592/2260 4046/2593/2261 4031/2563/2236 -f 4032/2594/2262 4047/2595/2263 4019/2592/2260 -f 4033/2596/2264 4048/2597/2265 4032/2594/2262 -f 4034/2598/2266 4049/2599/2267 4033/2596/2264 -f 4035/2575/2247 4050/2600/2268 4034/2601/2266 -f 4052/2602/2269 4037/2577/2248 4025/2569/2242 -f 4067/2603/2270 4052/2602/2269 4053/2604/2271 -f 4067/2603/2270 4053/2604/2271 4054/2605/2272 -f 4068/2606/2273 4054/2605/2272 4055/2607/2274 -f 4069/2608/2275 4055/2609/2274 4056/2610/2276 -f 4070/2611/2277 4056/2612/2276 4057/2613/2278 -f 4061/2614/2279 4074/2615/2280 4045/2616/2281 -f 4062/2617/2282 4075/2618/2283 4061/2614/2279 -f 4063/2619/2284 4076/2620/2285 4062/2617/2282 -f 4064/2621/2286 4077/2622/2287 4063/2623/2284 -f 4051/2624/2288 4078/2625/2289 4065/2626/2290 -f 4079/2627/2291 4078/2628/2289 4066/2629/2292 -f 4080/2630/2293 4066/2629/2292 4052/2602/2269 -f 4080/2630/2293 4052/2602/2269 4067/2603/2270 -f 4081/2631/2294 4067/2603/2270 4068/2606/2273 -f 4082/2632/2295 4068/2633/2273 4069/2634/2275 -f 4083/2635/2296 4069/2634/2275 4070/2611/2277 -f 4084/2636/2297 4070/2611/2277 4071/2637/2298 -f 4085/2638/2299 4071/2637/2298 4072/2639/2300 -f 4073/2640/2301 4085/2641/2299 4072/2642/2300 -f 4074/2615/2280 4087/2643/2302 4086/2644/2303 -f 4075/2618/2283 4089/2645/2304 4074/2615/2280 -f 4076/2646/2285 4090/2647/2305 4075/2648/2283 -f 4077/2622/2287 4091/2649/2306 4076/2646/2285 -f 4078/2625/2289 4092/2650/2307 4077/2622/2287 -f 4080/2630/2293 4093/2651/2308 4079/2627/2291 -f 4087/2643/2302 4100/2652/2309 4086/2644/2303 -f 4089/2645/2304 4101/2653/2310 4088/2654/2311 -f 4090/2647/2305 4102/2655/2312 4089/2656/2304 -f 4091/2649/2306 4103/2657/2313 4090/2647/2305 -f 4092/2650/2307 4104/2658/2314 4091/2649/2306 -f 4079/2659/2291 4105/2660/2315 4092/2650/2307 -f 4114/2661/2316 4095/2662/2317 4096/2663/2318 -f 4115/2664/2319 4096/2665/2318 4097/2666/2320 -f 4116/2667/2321 4097/2666/2320 4098/2668/2322 -f 4118/2669/2323 4098/2668/2322 4099/2670/2324 -f 4103/2657/2313 4129/2671/2325 4102/2655/2312 -f 4104/2658/2314 4130/2672/2326 4103/2657/2313 -f 4134/2673/2327 4158/2674/2328 4133/2675/2329 -f 4135/2676/2330 4158/2674/2328 4134/2673/2327 -f 4136/2677/2331 4159/2678/2332 4135/2676/2330 -f 4137/2679/2333 4159/2680/2332 4136/2681/2331 -f 4138/2682/2334 4160/2683/2335 4137/2679/2333 -f 4139/2684/2336 4161/2685/2337 4138/2682/2334 -f 4143/2686/2338 4164/2687/2339 4142/2688/2340 -f 4144/2689/2341 4164/2687/2339 4143/2686/2338 -f 4165/2690/2342 4145/2691/2343 4146/2692/2344 -f 4165/2690/2342 4146/2692/2344 4147/2693/2345 -f 4166/2694/2346 4148/2695/2347 4149/2696/2348 -f 4169/2697/2349 4151/2698/2350 4152/2699/2351 -f 4169/2697/2349 4152/2699/2351 4153/2700/2352 -f 4181/2701/2353 4153/2700/2352 4154/2702/2354 -f 4170/2703/2355 4154/2704/2354 4155/2705/2356 -f 4170/2703/2355 4155/2705/2356 4156/2706/2357 -f 4170/2703/2355 4156/2706/2357 4157/2707/2358 -f 4171/2708/2359 4170/2703/2355 4158/2674/2328 -f 4159/2678/2332 4172/2709/2360 4158/2674/2328 -f 4160/2683/2335 4173/2710/2361 4159/2680/2332 -f 4161/2685/2337 4174/2711/2362 4160/2683/2335 -f 4162/2712/2363 4175/2713/2364 4161/2714/2337 -f 4165/2690/2342 4176/2715/2365 4164/2687/2339 -f 4177/2716/2366 4165/2690/2342 4166/2717/2346 -f 4178/2718/2367 4166/2694/2346 4167/2719/2368 -f 4179/2720/2369 4167/2719/2368 4168/2721/2370 -f 4180/2722/2371 4168/2721/2370 4169/2697/2349 -f 4182/2723/2372 4181/2701/2353 4170/2724/2355 -f 4172/2709/2360 4183/2725/2373 4171/2708/2359 -f 4173/2710/2361 4184/2726/2374 4172/2727/2360 -f 4174/2711/2362 4185/2728/2375 4173/2710/2361 -f 4175/2729/2364 4186/2730/2376 4174/2711/2362 -f 4162/2712/2363 4187/2731/2377 4175/2713/2364 -f 4189/2732/2378 4176/2715/2365 4177/2716/2366 -f 4190/2733/2379 4177/2734/2366 4178/2718/2367 -f 4191/2735/2380 4178/2718/2367 4179/2720/2369 -f 4192/2736/2381 4179/2720/2369 4180/2722/2371 -f 4193/2737/2382 4180/2722/2371 4181/2701/2353 -f 4194/2738/2383 4181/2701/2353 4182/2723/2372 -f 4184/2739/2374 4195/2740/2384 4183/2725/2373 -f 4185/2728/2375 4197/2741/2385 4184/2726/2374 -f 4186/2730/2376 4198/2742/2386 4185/2728/2375 -f 4187/2743/2377 4199/2744/2387 4186/2730/2376 -f 4188/2745/2388 4200/2746/2389 4187/2731/2377 -f 4189/2732/2378 4201/2747/2390 4188/2748/2388 -f 4203/2749/2391 4189/2732/2378 4190/2750/2379 -f 4205/2751/2392 4190/2733/2379 4191/2735/2380 -f 4206/2752/2393 4191/2735/2380 4192/2736/2381 -f 4207/2753/2394 4192/2736/2381 4193/2737/2382 -f 4208/2754/2395 4193/2737/2382 4194/2738/2383 -f 4209/2755/2396 4194/2756/2383 4183/2725/2373 -f 4201/2747/2390 4216/2757/2397 4200/2758/2389 -f 4239/2759/2398 4210/2760/2399 4211/2761/2400 -f 4217/2762/2401 4231/2763/2402 4201/2747/2390 -f 4228/2764/2403 4242/2765/2404 4227/2766/2405 -f 4229/2767/2406 4243/2768/2407 4228/2764/2403 -f 4230/2769/2408 4244/2770/2409 4229/2771/2406 -f 4231/2763/2402 4245/2772/2410 4230/2769/2408 -f 4232/2773/2411 4246/2774/2412 4217/2762/2401 -f 4247/2775/2413 4232/2776/2411 4233/2777/2414 -f 4249/2778/2415 4234/2779/2416 4235/2780/2417 -f 4250/2781/2418 4235/2780/2417 4236/2782/2419 -f 4251/2783/2420 4236/2782/2419 4237/2784/2421 -f 4252/2785/2422 4237/2784/2421 4238/2786/2423 -f 4247/2775/2413 4262/2787/2424 4232/2776/2411 -f 4270/2788/2425 4254/2789/2426 4255/2790/2427 -f 4261/2791/2428 4275/2792/2429 4260/2793/2430 -f 4282/2794/2431 4269/2795/2432 4253/2796/2433 -f 4283/2797/2434 4253/2798/2433 4254/2789/2426 -f 4300/2799/2435 4283/2797/2434 4284/2800/2436 -f 4285/2801/2437 4300/2799/2435 4284/2800/2436 -f 4289/2802/2438 4303/2803/2439 4288/2804/2440 -f 4305/2805/2441 4294/2806/2442 4295/2807/2443 -f 4303/2803/2439 4317/2808/2444 4302/2809/2445 -f 4289/2802/2438 4318/2810/2446 4303/2803/2439 -f 4292/2811/2447 4320/2812/2448 4291/2813/2449 -f 4325/2814/2450 4306/2815/2451 4307/2816/2452 -f 4328/2817/2453 4324/2818/2454 4306/2815/2451 -f 4328/2817/2453 4306/2815/2451 4325/2814/2450 -f 4329/2819/2455 4325/2814/2450 4326/2820/2456 -f 4332/2821/2457 4310/2822/2458 4311/2823/2459 -f 4334/2824/2460 4312/2825/2461 4313/2826/2462 -f 4335/2827/2463 4313/2828/2462 4314/2829/2464 -f 4335/2827/2463 4314/2829/2464 4315/2830/2465 -f 4336/2831/2466 4315/2830/2465 4316/2832/2467 -f 4336/2831/2466 4316/2832/2467 4317/2833/2444 -f 4318/2810/2446 4337/2834/2468 4317/2808/2444 -f 4319/2835/2469 4339/2836/2470 4318/2810/2446 -f 4320/2812/2448 4339/2836/2470 4319/2835/2469 -f 4321/2837/2471 4340/2838/2472 4292/2839/2447 -f 4328/2817/2453 4343/2840/2473 4324/2818/2454 -f 4329/2819/2455 4344/2841/2474 4328/2817/2453 -f 4330/2842/2475 4345/2843/2476 4329/2819/2455 -f 4348/2844/2477 4334/2845/2460 4335/2827/2463 -f 4349/2846/2478 4335/2827/2463 4336/2831/2466 -f 4350/2847/2479 4336/2831/2466 4337/2848/2468 -f 4351/2849/2480 4337/2834/2468 4338/2850/2481 -f 4354/2851/2482 4340/2838/2472 4341/2852/2483 -f 4346/2853/2484 4358/2854/2485 4345/2843/2476 -f 4310/2855/2458 4359/2856/2486 4331/2857/2487 -f 4347/2858/2488 4373/2859/2489 4346/2853/2484 -f 4383/2860/2490 4367/2861/2491 4368/2862/2492 -f 4374/2863/2493 4388/2864/2494 4373/2859/2489 -f 4360/2865/2495 4389/2866/2496 4359/2867/2486 -f 4392/2868/2497 4378/2869/2498 4379/2870/2499 -f 4393/2871/2500 4379/2872/2499 4380/2873/2501 -f 4374/2863/2493 4402/2874/2502 4388/2864/2494 -f 4389/2875/2496 4403/2876/2503 4374/2863/2493 -f 4404/2877/2504 4391/2878/2505 4377/2879/2506 -f 4406/2880/2507 4396/2881/2508 4381/2882/2509 -f 4399/2883/2510 4408/2884/2511 4398/2885/2512 -f 4400/2886/2513 4409/2887/2514 4399/2883/2510 -f 4401/2888/2515 4410/2889/2516 4400/2886/2513 -f 4402/2890/2502 4411/2891/2517 4401/2888/2515 -f 4416/2892/2518 4405/2893/2519 4392/2868/2497 -f 4418/2894/2520 4394/2895/2521 4395/2896/2522 -f 4412/2897/2523 4425/2898/2524 4411/2891/2517 -f 4413/2899/2525 4426/2900/2526 4403/2876/2503 -f 4427/2901/2527 4415/2902/2528 4404/2877/2504 -f 4429/2903/2529 4419/2904/2530 4406/2880/2507 -f 4436/2905/2531 4413/2899/2525 4414/2906/2532 -f 4437/2907/2533 4414/2908/2532 4415/2902/2528 -f 4437/2907/2533 4415/2902/2528 4427/2901/2527 -f 4438/2909/2534 4416/2892/2518 4417/2910/2535 -f 4439/2911/2536 4417/2912/2535 4418/2894/2520 -f 4440/2913/2537 4418/2894/2520 4419/2914/2530 -f 4421/2915/2538 4441/2916/2539 4420/2917/2540 -f 4431/2918/2541 4442/2919/2542 4421/2920/2538 -f 4432/2921/2543 4443/2922/2544 4431/2918/2541 -f 4433/2923/2545 4444/2924/2546 4432/2921/2543 -f 4434/2925/2547 4445/2926/2548 4433/2923/2545 -f 4435/2927/2549 4447/2928/2550 4434/2925/2547 -f 4413/2899/2525 4448/2929/2551 4426/2900/2526 -f 4449/2930/2552 4436/2931/2531 4437/2907/2533 -f 4452/2932/2553 4428/2933/2554 4438/2909/2534 -f 4453/2934/2555 4440/2913/2537 4429/2935/2529 -f 4446/2936/2556 4456/2937/2557 4445/2926/2548 -f 4426/2938/2526 4457/2939/2558 4435/2927/2549 -f 4459/2940/2559 4452/2932/2553 4438/2909/2534 -f 4459/2941/2559 4438/2942/2534 4439/2911/2536 -f 4455/2943/2560 4465/2944/2561 4454/2945/2562 -f 4456/2937/2557 4466/2946/2563 4455/2943/2560 -f 4446/2936/2556 4467/2947/2564 4456/2937/2557 -f 4457/2939/2558 4468/2948/2565 4447/2928/2550 -f 4448/2949/2551 4480/2950/2566 4457/2951/2558 -f 4469/2952/2567 4458/2953/2568 4449/2954/2552 -f 4471/2955/2569 4451/2956/2570 4452/2932/2553 -f 4472/2957/2571 4452/2932/2553 4459/2940/2559 -f 4482/2958/2572 4461/2959/2573 4453/2934/2555 -f 4475/2960/2574 4453/2961/2555 4430/2962/2575 -f 4462/2963/2576 4475/2960/2574 4430/2962/2575 -f 4468/2948/2565 4479/2964/2577 4467/2947/2564 -f 4481/2965/2578 4448/2929/2551 4458/2953/2568 -f 4476/2966/2579 4484/2967/2580 4463/2968/2581 -f 4480/2969/2566 4488/2970/2582 4468/2948/2565 -f 4489/2971/2583 4469/2952/2567 4470/2972/2584 -f 4490/2973/2585 4470/2974/2584 4471/2955/2569 -f 4491/2975/2586 4471/2955/2569 4472/2957/2571 -f 4492/2976/2587 4472/2977/2571 4473/2978/2588 -f 4492/2976/2587 4473/2978/2588 4474/2979/2589 -f 4494/2980/2590 4474/2979/2589 4482/2958/2572 -f 4485/2981/2591 4496/2982/2592 4476/2966/2579 -f 4486/2983/2593 4497/2984/2594 4485/2981/2591 -f 4487/2985/2595 4498/2986/2596 4486/2983/2593 -f 4488/2970/2582 4499/2987/2597 4487/2985/2595 -f 4480/2969/2566 4500/2988/2598 4488/2970/2582 -f 4523/2989/2599 4501/2990/2600 4448/2929/2551 -f 4504/2991/2601 4489/2971/2583 4490/2992/2585 -f 4484/2993/2580 4506/2994/2602 4483/2995/2603 -f 4496/2996/2592 4507/2997/2604 4484/2993/2580 -f 4497/2984/2594 4509/2998/2605 4496/2982/2592 -f 4500/2988/2598 4512/2999/2606 4499/2987/2597 -f 4513/3000/2607 4504/2991/2601 4491/3001/2586 -f 4518/3002/2608 4495/3003/2609 4475/3004/2574 -f 4505/3005/2610 4518/3006/2608 4475/2960/2574 -f 4511/3007/2611 4520/3008/2612 4510/3009/2613 -f 4523/3010/2599 4522/3011/2614 4501/3012/2600 -f 4524/3013/2615 4502/3014/2616 4503/3015/2617 -f 4512/2999/2606 4532/3016/2618 4511/3007/2611 -f 4521/3017/2619 4532/3016/2618 4512/2999/2606 -f 4533/3018/2620 4526/3019/2621 4513/3000/2607 -f 4505/3005/2610 4539/3020/2622 4518/3006/2608 -f 4545/3021/2623 4544/3022/2624 4523/3010/2599 -f 4546/3023/2625 4523/2989/2599 4502/3014/2616 -f 4529/3024/2626 4552/3025/2627 4528/3026/2628 -f 4543/3027/2629 4555/3028/2630 4542/3029/2631 -f 4556/3030/2632 4548/3031/2633 4549/3032/2634 -f 4558/3033/2635 4533/3034/2620 4534/3035/2636 -f 4539/3036/2622 4562/3037/2637 4538/3038/2638 -f 4553/3039/2639 4564/3040/2640 4552/3025/2627 -f 4554/3041/2641 4565/3042/2642 4553/3039/2639 -f 4555/3028/2630 4566/3043/2643 4554/3041/2641 -f 4576/3044/2644 4545/3045/2623 4546/3023/2625 -f 4569/3046/2645 4546/3023/2625 4547/3047/2646 -f 4570/3048/2647 4547/3047/2646 4548/3031/2633 -f 4571/3049/2648 4557/3050/2649 4558/3051/2635 -f 4551/3052/2650 4572/3053/2651 4550/3054/2652 -f 4567/3055/2653 4575/3056/2654 4566/3043/2643 -f 4577/3057/2655 4569/3046/2645 4547/3047/2646 -f 4578/3058/2656 4570/3048/2647 4556/3030/2632 -f 4579/3059/2657 4556/3030/2632 4557/3050/2649 -f 4579/3059/2657 4557/3050/2649 4571/3049/2648 -f 4563/3060/2658 4582/3061/2659 4551/3052/2650 -f 4573/3062/2660 4584/3063/2661 4564/3040/2640 -f 4599/3064/2662 4579/3059/2657 4571/3049/2648 -f 4588/3065/2663 4571/3066/2648 4559/3067/2664 -f 4589/3068/2665 4559/3067/2664 4560/3069/2666 -f 4590/3070/2667 4560/3069/2666 4561/3071/2668 -f 4581/3072/2669 4580/3073/2670 4562/3037/2637 -f 4574/3074/2671 4593/3075/2672 4573/3062/2660 -f 4585/3076/2673 4597/3077/2674 4568/3078/2675 -f 4598/3079/2676 4578/3058/2656 4579/3059/2657 -f 4582/3061/2659 4591/3080/2677 4572/3053/2651 -f 4593/3075/2672 4592/3081/2678 4584/3063/2661 -f 4594/3082/2679 4593/3075/2672 4574/3074/2671 -f 4604/3083/2680 4589/3068/2665 4590/3070/2667 -f 4591/3080/2677 4606/3084/2681 4600/3085/2682 -f 4592/3081/2678 4601/3086/2683 4583/3087/2684 -f 4585/3076/2673 4608/3088/2685 4597/3077/2674 -f 4611/3089/2686 4603/3090/2687 4598/3079/2676 -f 4611/3089/2686 4598/3079/2676 4599/3064/2662 -f 4612/3091/2688 4599/3092/2662 4588/3065/2663 -f 4613/3093/2689 4588/3065/2663 4589/3068/2665 -f 4613/3093/2689 4589/3068/2665 4604/3083/2680 -f 4614/3094/2690 4604/3095/2680 4605/3096/2691 -f 4607/3097/2692 4627/3098/2693 4591/3080/2677 -f 4593/3075/2672 4616/3099/2694 4592/3081/2678 -f 4594/3082/2679 4617/3100/2695 4593/3075/2672 -f 4595/3101/2696 4617/3100/2695 4594/3082/2679 -f 4608/3088/2685 4619/3102/2697 4618/3103/2698 -f 4620/3104/2699 4619/3102/2697 4602/3105/2700 -f 4621/3106/2701 4610/3107/2702 4603/3090/2687 -f 4616/3099/2694 4630/3108/2703 4592/3081/2678 -f 4617/3100/2695 4631/3109/2704 4616/3099/2694 -f 4618/3110/2698 4632/3111/2705 4617/3100/2695 -f 4635/3112/2706 4620/3113/2699 4609/3114/2707 -f 4636/3115/2708 4610/3107/2702 4621/3106/2701 -f 4626/3116/2709 4640/3117/2710 4625/3118/2711 -f 4628/3119/2712 4627/3098/2693 4607/3097/2692 -f 4645/3120/2713 4634/3121/2713 4620/3113/2713 -f 4646/3122/2714 4609/3114/2707 4636/3115/2708 -f 4647/3123/2715 4636/3115/2708 4637/3124/2716 -f 4648/3125/2717 4637/3124/2716 4638/3126/2718 -f 4649/3127/2719 4638/3128/2718 4639/3129/2720 -f 4650/3130/2721 4639/3129/2720 4640/3117/2710 -f 4628/3119/2712 4652/3131/2722 4627/3098/2693 -f 4629/3132/2723 4652/3131/2722 4628/3119/2712 -f 4641/3133/2724 4653/3134/2725 4629/3132/2723 -f 4642/3135/2726 4654/3136/2727 4641/3133/2724 -f 4643/3137/2728 4655/3138/2729 4642/3139/2726 -f 4644/3140/2730 4656/3141/2731 4643/3137/2728 -f 4634/3142/2732 4656/3141/2731 4644/3140/2730 -f 4645/3143/2733 4669/3144/2734 4656/3141/2731 -f 4657/3145/2735 4635/3112/2706 4646/3122/2714 -f 4658/3146/2736 4646/3122/2714 4647/3123/2715 -f 4659/3147/2737 4647/3148/2715 4648/3149/2717 -f 4660/3150/2738 4648/3151/2717 4649/3127/2719 -f 4661/3152/2739 4649/3127/2719 4650/3130/2721 -f 4662/3153/2740 4650/3130/2721 4651/3154/2741 -f 4652/3131/2722 4664/3155/2742 4663/3156/2743 -f 4653/3134/2725 4665/3157/2744 4652/3131/2722 -f 4654/3158/2727 4666/3159/2745 4653/3160/2725 -f 4655/3138/2729 4667/3161/2746 4654/3158/2727 -f 4656/3141/2731 4668/3162/2747 4655/3138/2729 -f 4645/3143/2733 4670/3163/2748 4669/3144/2734 -f 4635/3112/2706 4671/3164/2749 4645/3120/2733 -f 4671/3164/2749 4635/3112/2706 4657/3145/2735 -f 4672/3165/2750 4658/3166/2736 4659/3147/2737 -f 4676/3167/2751 4662/3168/2740 4663/3156/2743 -f 4664/3155/2742 4677/3169/2752 4663/3156/2743 -f 4652/3131/2722 4678/3170/2753 4664/3155/2742 -f 4665/3157/2744 4678/3170/2753 4652/3131/2722 -f 4667/3161/2746 4679/3171/2754 4666/3159/2745 -f 4668/3162/2747 4680/3172/2755 4667/3161/2746 -f 4669/3144/2734 4681/3173/2756 4668/3162/2747 -f 4671/3164/2749 4682/3174/2757 4670/3175/2748 -f 4684/3176/2758 4657/3145/2735 4658/3146/2736 -f 4686/3177/2759 4673/3178/2760 4674/3179/2761 -f 4687/3180/2762 4674/3179/2761 4675/3181/2763 -f 4688/3182/2764 4675/3181/2763 4676/3183/2751 -f 4678/3170/2753 4690/3184/2765 4677/3169/2752 -f 4665/3157/2744 4691/3185/2766 4678/3170/2753 -f 4679/3171/2754 4692/3186/2767 4665/3187/2744 -f 4680/3172/2755 4693/3188/2768 4679/3171/2754 -f 4681/3173/2756 4694/3189/2769 4680/3172/2755 -f 4670/3163/2748 4695/3190/2770 4681/3173/2756 -f 4682/3174/2757 4696/3191/2771 4695/3192/2770 -f 4683/3193/2772 4696/3191/2771 4682/3174/2757 -f 4697/3194/2773 4683/3193/2772 4684/3176/2758 -f 4698/3195/2774 4684/3176/2758 4685/3196/2775 -f 4700/3197/2776 4686/3177/2759 4687/3180/2762 -f 4701/3198/2777 4687/3180/2762 4688/3182/2764 -f 4702/3199/2778 4688/3200/2764 4689/3201/2779 -f 4702/3199/2778 4689/3201/2779 4690/3184/2765 -f 4691/3185/2766 4703/3202/2780 4690/3184/2765 -f 4692/3203/2767 4704/3204/2781 4691/3185/2766 -f 4694/3189/2769 4706/3205/2782 4693/3188/2768 -f 4695/3190/2770 4707/3206/2783 4694/3189/2769 -f 4715/3207/2784 4698/3208/2774 4685/3209/2775 -f 4716/3210/2785 4685/3209/2775 4699/3211/2786 -f 4706/3205/2782 4731/3212/2787 4705/3213/2788 -f 4707/3206/2783 4732/3214/2789 4706/3205/2782 -f 4696/3215/2771 4734/3216/2790 4707/3206/2783 -f 4736/3217/2791 4757/3218/2792 4735/3219/2793 -f 4737/3220/2794 4757/3218/2792 4736/3217/2791 -f 4739/3221/2795 4758/3222/2796 4738/3223/2797 -f 4741/3224/2798 4759/3225/2799 4740/3226/2800 -f 4742/3227/2801 4760/3228/2802 4741/3224/2798 -f 4743/3229/2803 4761/3230/2804 4742/3231/2801 -f 4744/3232/2805 4761/3230/2804 4743/3229/2803 -f 4745/3233/2806 4762/3234/2807 4744/3232/2805 -f 4763/3235/2808 4746/3236/2809 4747/3237/2810 -f 4763/3235/2808 4747/3237/2810 4748/3238/2811 -f 4763/3235/2808 4748/3238/2811 4749/3239/2812 -f 4764/3240/2813 4749/3241/2812 4750/3242/2814 -f 4764/3240/2813 4750/3242/2814 4751/3243/2815 -f 4767/3244/2816 4752/3245/2817 4753/3246/2818 -f 4768/3247/2819 4754/3248/2820 4755/3249/2821 -f 4758/3250/2796 4771/3251/2822 4757/3218/2792 -f 4759/3225/2799 4772/3252/2823 4758/3222/2796 -f 4760/3228/2802 4773/3253/2824 4759/3225/2799 -f 4761/3254/2804 4774/3255/2825 4760/3256/2802 -f 4762/3234/2807 4775/3257/2826 4761/3230/2804 -f 4777/3258/2827 4763/3235/2808 4764/3259/2813 -f 4778/3260/2828 4764/3240/2813 4765/3261/2829 -f 4779/3262/2830 4767/3244/2816 4768/3263/2819 -f 4781/3264/2831 4768/3247/2819 4769/3265/2832 -f 4771/3251/2822 4783/3266/2833 4770/3267/2834 -f 4772/3252/2823 4784/3268/2835 4771/3269/2822 -f 4774/3270/2825 4786/3271/2836 4773/3253/2824 -f 4775/3257/2826 4787/3272/2837 4774/3273/2825 -f 4790/3274/2838 4776/3275/2839 4777/3258/2827 -f 4791/3276/2840 4777/3277/2827 4778/3260/2828 -f 4792/3278/2841 4778/3260/2828 4766/3279/2842 -f 4793/3280/2843 4766/3279/2842 4779/3262/2830 -f 4794/3281/2844 4779/3262/2830 4780/3282/2845 -f 4794/3283/2844 4780/3284/2845 4781/3264/2831 -f 4783/3266/2833 4796/3285/2846 4782/3286/2847 -f 4784/3268/2835 4797/3287/2848 4783/3288/2833 -f 4785/3289/2849 4798/3290/2850 4784/3268/2835 -f 4803/3291/2851 4789/3292/2852 4790/3274/2838 -f 4804/3293/2853 4790/3294/2838 4791/3276/2840 -f 4805/3295/2854 4791/3276/2840 4792/3278/2841 -f 4806/3296/2855 4792/3278/2841 4793/3280/2843 -f 4807/3297/2856 4793/3280/2843 4794/3281/2844 -f 4808/3298/2857 4794/3283/2844 4795/3299/2858 -f 4809/3300/2859 4808/3298/2857 4796/3285/2846 -f 4797/3287/2848 4811/3301/2860 4796/3302/2846 -f 4798/3290/2850 4812/3303/2861 4797/3287/2848 -f 4799/3304/2862 4814/3305/2863 4813/3306/2864 -f 4800/3307/2865 4815/3308/2866 4799/3309/2862 -f 4801/3310/2867 4816/3311/2868 4800/3307/2865 -f 4802/3312/2869 4817/3313/2870 4801/3314/2867 -f 4818/3315/2871 4802/3312/2869 4803/3291/2851 -f 4819/3316/2872 4803/3317/2851 4804/3293/2853 -f 4820/3318/2873 4804/3293/2853 4805/3295/2854 -f 4821/3319/2874 4806/3296/2855 4807/3297/2856 -f 4822/3320/2875 4807/3297/2856 4808/3321/2857 -f 4811/3301/2860 4825/3322/2876 4810/3323/2877 -f 4812/3303/2861 4826/3324/2878 4811/3301/2860 -f 4813/3306/2864 4828/3325/2879 4827/3326/2880 -f 4814/3305/2863 4828/3325/2879 4813/3306/2864 -f 4815/3327/2866 4829/3328/2881 4814/3305/2863 -f 4817/3329/2870 4831/3330/2882 4816/3331/2868 -f 4832/3332/2883 4817/3329/2870 4818/3333/2871 -f 4833/3334/2884 4818/3335/2871 4819/3316/2872 -f 4834/3336/2885 4819/3316/2872 4820/3318/2873 -f 4835/3337/2886 4820/3318/2873 4805/3295/2854 -f 4843/3338/2887 4827/3326/2880 4828/3325/2879 -f 4844/3339/2888 4828/3325/2879 4829/3328/2881 -f 4845/3340/2889 4829/3341/2881 4830/3342/2890 -f 4846/3343/2891 4830/3342/2890 4831/3344/2882 -f 4835/3337/2886 4850/3345/2892 4834/3336/2885 -f 4857/3346/2893 4842/3347/2894 4843/3338/2887 -f 4851/3348/2895 4866/3349/2896 4850/3345/2892 -f 4852/3350/2897 4867/3351/2898 4851/3348/2895 -f 4853/3352/2899 4868/3353/2900 4852/3350/2897 -f 4855/3354/2901 4869/3355/2902 4854/3356/2903 -f 4872/3357/2904 4842/3347/2894 4857/3346/2893 -f 4873/3358/2905 4857/3346/2893 4858/3359/2906 -f 4874/3360/2907 4858/3359/2906 4859/3361/2908 -f 4875/3362/2909 4859/3363/2908 4860/3364/2910 -f 4864/3365/2911 4878/3366/2912 4863/3367/2913 -f 4865/3368/2914 4879/3369/2915 4864/3365/2911 -f 4866/3349/2896 4880/3370/2916 4865/3368/2914 -f 4867/3351/2898 4881/3371/2917 4866/3349/2896 -f 4868/3353/2900 4882/3372/2918 4867/3351/2898 -f 4854/3356/2903 4883/3373/2919 4868/3374/2900 -f 4869/3355/2902 4884/3375/2920 4883/3373/2919 -f 4885/3376/2921 4869/3377/2902 4870/3378/2922 -f 4886/3379/2923 4870/3380/2922 4871/3381/2924 -f 4887/3382/2925 4871/3381/2924 4872/3357/2904 -f 4888/3383/2926 4872/3357/2904 4873/3358/2905 -f 4889/3384/2927 4873/3385/2905 4874/3386/2907 -f 4890/3387/2928 4874/3388/2907 4875/3362/2909 -f 4891/3389/2929 4875/3362/2909 4876/3390/2930 -f 4892/3391/2931 4876/3390/2930 4877/3392/2932 -f 4878/3393/2912 4893/3394/2933 4877/3395/2932 -f 4879/3369/2915 4895/3396/2934 4878/3366/2912 -f 4880/3370/2916 4896/3397/2935 4879/3369/2915 -f 4881/3398/2917 4897/3399/2936 4880/3400/2916 -f 4882/3401/2918 4898/3402/2937 4881/3398/2917 -f 4883/3373/2919 4899/3403/2938 4882/3401/2918 -f 4907/3404/2939 4892/3391/2931 4893/3405/2933 -f 4896/3406/2935 4910/3407/2940 4895/3408/2934 -f 4897/3399/2936 4911/3409/2941 4896/3406/2935 -f 4898/3402/2937 4912/3410/2942 4897/3399/2936 -f 4899/3403/2938 4913/3411/2943 4898/3402/2937 -f 4884/3375/2920 4913/3411/2943 4899/3403/2938 -f 4918/3412/2944 4902/3413/2945 4903/3414/2946 -f 4919/3415/2947 4903/3414/2946 4904/3416/2948 -f 4920/3417/2949 4904/3416/2948 4905/3418/2950 -f 4921/3419/2951 4905/3420/2950 4906/3421/2952 -f 4948/3422/2953 4932/3423/2954 4933/3424/2955 -f 4949/3425/2956 4933/3424/2955 4934/3426/2957 -f 4950/3427/2958 4934/3426/2957 4935/3428/2959 -f 4951/3429/2960 4935/3428/2959 4936/3430/2961 -f 4952/3431/2962 4936/3432/2961 4937/3433/2963 -f 4953/3434/2964 4937/3433/2963 4938/3435/2965 -f 4954/3436/2966 4938/3435/2965 4939/3437/2967 -f 4947/3438/2968 4961/3439/2969 4946/3440/2970 -f 4948/3441/2953 4962/3442/2971 4947/3438/2968 -f 4970/3443/2972 4954/3444/2966 4955/3445/2973 -f 4956/3446/2974 4971/3447/2975 4955/3448/2973 -f 4957/3449/2976 4972/3450/2977 4956/3446/2974 -f 4958/3451/2978 4973/3452/2979 4957/3449/2976 -f 4959/3453/2980 4974/3454/2981 4958/3451/2978 -f 4960/3455/2982 4975/3456/2983 4959/3453/2980 -f 4946/3457/2970 4976/3458/2984 4960/3455/2982 -f 4961/3459/2969 4977/3460/2985 4976/3461/2984 -f 4984/3462/2986 4968/3463/2987 4969/3464/2988 -f 4979/3465/2989 4992/3466/2990 4978/3467/2991 -f 4993/3468/2992 4979/3469/2989 4980/3470/2993 -f 4994/3471/2994 4980/3470/2993 4981/3472/2995 -f 4995/3473/2996 4981/3474/2995 4982/3475/2997 -f 4996/3476/2998 4982/3477/2997 4983/3478/2999 -f 4997/3479/3000 4983/3478/2999 4984/3462/2986 -f 4988/3480/3001 5001/3481/3002 4987/3482/3003 -f 4989/3483/3004 5002/3484/3005 4988/3485/3001 -f 4990/3486/3006 5003/3487/3007 4989/3483/3004 -f 4977/3460/2985 5004/3488/3008 4990/3486/3006 -f 4991/3489/3009 5005/3490/3010 5004/3488/3008 -f 4992/3491/2990 5005/3490/3010 4991/3489/3009 -f 4979/3469/2989 5006/3492/3011 4992/3493/2990 -f 5007/3494/3012 4979/3469/2989 4993/3468/2992 -f 5008/3495/3013 4993/3468/2992 4994/3471/2994 -f 5009/3496/3014 4994/3497/2994 4995/3473/2996 -f 5010/3498/3015 4995/3473/2996 4996/3499/2998 -f 5011/3500/3016 4996/3476/2998 4997/3479/3000 -f 5012/3501/3017 4997/3479/3000 4998/3502/3018 -f 5012/3501/3017 4998/3502/3018 4999/3503/3019 -f 5013/3504/3020 4999/3505/3019 4986/3506/3021 -f 5000/3507/3022 5014/3508/3023 4986/3506/3021 -f 5001/3481/3002 5015/3509/3024 5000/3507/3022 -f 5002/3484/3005 5016/3510/3025 5001/3511/3002 -f 5003/3487/3007 5017/3512/3026 5002/3484/3005 -f 5004/3488/3008 5018/3513/3027 5003/3487/3007 -f 5006/3514/3011 5019/3515/3028 5005/3490/3010 -f 5020/3516/3029 5007/3494/3012 5008/3495/3013 -f 5021/3517/3030 5008/3518/3013 5009/3496/3014 -f 5022/3519/3031 5009/3496/3014 5010/3498/3015 -f 5023/3520/3032 5010/3521/3015 5011/3500/3016 -f 5024/3522/3033 5011/3500/3016 5012/3501/3017 -f 5016/3510/3025 5028/3523/3034 5015/3524/3024 -f 5017/3512/3026 5029/3525/3035 5016/3510/3025 -f 5018/3513/3027 5030/3526/3036 5017/3512/3026 -f 5005/3490/3010 5031/3527/3037 5018/3513/3027 -f 5019/3515/3028 5032/3528/3038 5031/3527/3037 -f 5006/3492/3011 5032/3529/3038 5019/3530/3028 -f 5007/3494/3012 5033/3531/3039 5006/3492/3011 -f 5034/3532/3040 5007/3533/3012 5020/3534/3029 -f 5039/3535/3041 5025/3536/3042 5026/3537/3043 -f 5027/3538/3044 5040/3539/3045 5014/3540/3023 -f 5028/3523/3034 5041/3541/3046 5027/3542/3044 -f 5029/3525/3035 5042/3543/3047 5028/3523/3034 -f 5030/3526/3036 5043/3544/3048 5029/3525/3035 -f 5031/3527/3037 5044/3545/3049 5030/3526/3036 -f 5033/3531/3039 5045/3546/3050 5032/3529/3038 -f 5047/3547/3051 5034/3548/3040 5035/3549/3052 -f 5049/3550/3053 5035/3549/3052 5036/3551/3054 -f 5050/3552/3055 5036/3551/3054 5037/3553/3056 -f 5051/3554/3057 5037/3555/3056 5038/3556/3058 -f 5052/3557/3059 5038/3558/3058 5039/3535/3041 -f 5053/3559/3060 5039/3535/3041 5026/3537/3043 -f 5070/3560/3061 5053/3561/3060 5054/3562/3062 -f 5061/3563/3063 5076/3564/3064 5060/3565/3065 -f 5076/3564/3064 5105/3566/3066 5091/3567/3067 -f 5077/3568/3068 5093/3569/3069 5062/3570/3070 -f 5094/3571/3071 5077/3568/3068 5078/3572/3072 -f 5093/3573/3069 5108/3574/3073 5092/3575/3074 -f 5118/3576/3075 5084/3577/3076 5085/3578/3077 -f 5118/3576/3075 5085/3578/3077 5086/3579/3078 -f 5106/3580/3079 5127/3581/3080 5121/3582/3081 -f 5131/3583/3082 5112/3584/3083 5113/3585/3084 -f 5135/3586/3085 5116/3587/3086 5117/3588/3087 -f 5137/3589/3088 5117/3588/3087 5084/3590/3076 -f 5123/3591/3089 5139/3592/3090 5119/3593/3091 -f 5124/3594/3092 5140/3595/3093 5123/3596/3089 -f 5125/3597/3094 5141/3598/3095 5124/3594/3092 -f 5107/3599/3096 5142/3600/3097 5106/3580/3079 -f 5108/3574/3073 5143/3601/3098 5107/3599/3096 -f 5122/3602/3099 5143/3601/3098 5108/3574/3073 -f 5144/3603/3100 5111/3604/3101 5112/3584/3083 -f 5139/3592/3090 5138/3605/3102 5118/3576/3075 -f 5147/3606/3103 5131/3583/3082 5132/3607/3104 -f 5140/3595/3093 5148/3608/3105 5139/3609/3090 -f 5145/3610/3106 5149/3611/3107 5141/3598/3095 -f 5143/3601/3098 5151/3612/3108 5142/3600/3097 -f 5122/3602/3099 5152/3613/3109 5143/3601/3098 -f 5128/3614/3110 5153/3615/3111 5122/3616/3099 -f 5156/3617/3112 5144/3603/3100 5131/3583/3082 -f 5158/3618/3113 5133/3619/3114 5134/3620/3115 -f 5160/3621/3116 5135/3586/3085 5136/3622/3117 -f 5162/3623/3118 5137/3624/3088 5138/3605/3102 -f 5151/3612/3108 5168/3625/3119 5150/3626/3120 -f 5186/3627/3121 5169/3628/3122 5170/3629/3123 -f 5187/3630/3124 5170/3629/3123 5171/3631/3125 -f 5188/3632/3126 5171/3631/3125 5172/3633/3127 -f 5189/3634/3128 5172/3635/3127 5173/3636/3129 -f 5190/3637/3130 5173/3638/3129 5174/3639/3131 -f 5191/3640/3132 5174/3639/3131 5175/3641/3133 -f 5192/3642/3134 5160/3621/3116 5161/3643/3135 -f 5193/3644/3136 5161/3645/3135 5176/3646/3137 -f 5178/3647/3138 5194/3648/3139 5177/3649/3140 -f 5182/3650/3141 5210/3651/3142 5197/3652/3143 -f 5198/3653/3144 5185/3654/3145 5186/3627/3121 -f 5203/3655/3146 5190/3656/3130 5191/3657/3132 -f 5205/3658/3147 5192/3642/3134 5161/3643/3135 -f 5205/3658/3147 5161/3643/3135 5193/3659/3136 -f 5194/3648/3139 5206/3660/3148 5193/3661/3136 -f 5195/3662/3149 5207/3663/3150 5194/3648/3139 -f 5196/3664/3151 5208/3665/3152 5195/3662/3149 -f 5197/3666/3143 5209/3667/3153 5196/3664/3151 -f 5183/3668/3154 5212/3669/3155 5182/3650/3141 -f 5213/3670/3156 5184/3671/3157 5185/3672/3145 -f 5214/3673/3158 5202/3674/3159 5190/3656/3130 -f 5207/3663/3150 5215/3675/3160 5206/3660/3148 -f 5208/3665/3152 5216/3676/3161 5207/3663/3150 -f 5209/3667/3153 5217/3677/3162 5208/3665/3152 -f 5184/3671/3157 5230/3678/3163 5183/3668/3154 -f 5213/3670/3156 5230/3678/3163 5184/3671/3157 -f 5220/3679/3164 5213/3670/3156 5198/3680/3144 -f 5220/3681/3164 5198/3653/3144 5199/3682/3165 -f 5221/3683/3166 5200/3684/3167 5201/3685/3168 -f 5223/3686/3169 5203/3655/3146 5204/3687/3170 -f 5215/3688/3160 5225/3689/3171 5205/3658/3147 -f 5216/3676/3161 5226/3690/3172 5215/3675/3160 -f 5217/3677/3162 5227/3691/3173 5216/3676/3161 -f 5218/3692/3174 5228/3693/3175 5217/3677/3162 -f 5212/3694/3155 5229/3695/3176 5211/3696/3177 -f 5213/3670/3156 5231/3697/3178 5230/3678/3163 -f 5231/3697/3178 5213/3670/3156 5220/3679/3164 -f 5232/3698/3179 5220/3681/3164 5221/3683/3166 -f 5233/3699/3180 5221/3683/3166 5222/3700/3181 -f 5244/3701/3182 5214/3673/3158 5203/3655/3146 -f 5235/3702/3183 5223/3703/3169 5224/3704/3184 -f 5228/3693/3175 5238/3705/3185 5227/3691/3173 -f 5211/3696/3177 5250/3706/3186 5219/3707/3187 -f 5230/3678/3163 5239/3708/3188 5229/3709/3176 -f 5231/3697/3178 5239/3708/3188 5230/3678/3163 -f 5241/3710/3189 5231/3697/3178 5232/3711/3179 -f 5243/3712/3190 5232/3698/3179 5233/3699/3180 -f 5244/3701/3182 5222/3713/3181 5214/3673/3158 -f 5245/3714/3191 5234/3715/3192 5223/3686/3169 -f 5245/3716/3191 5223/3703/3169 5235/3702/3183 -f 5238/3705/3185 5248/3717/3193 5237/3718/3194 -f 5267/3719/3195 5233/3699/3180 5222/3700/3181 -f 5253/3720/3196 5244/3701/3182 5234/3715/3192 -f 5247/3721/3197 5255/3722/3198 5246/3723/3199 -f 5249/3724/3200 5280/3725/3201 5228/3693/3175 -f 5229/3695/3176 5256/3726/3202 5250/3706/3186 -f 5251/3727/3203 5257/3728/3204 5256/3729/3202 -f 5264/3730/3205 5241/3731/3189 5242/3732/3206 -f 5265/3733/3207 5242/3732/3206 5243/3712/3190 -f 5266/3734/3208 5243/3712/3190 5233/3699/3180 -f 5268/3735/3209 5244/3701/3182 5253/3720/3196 -f 5270/3736/3210 5253/3737/3196 5254/3738/3211 -f 5247/3721/3197 5277/3739/3212 5255/3722/3198 -f 5248/3717/3193 5278/3740/3213 5247/3721/3197 -f 5238/3705/3185 5279/3741/3214 5248/3717/3193 -f 5249/3724/3200 5281/3742/3215 5280/3725/3201 -f 5256/3726/3202 5281/3742/3215 5249/3724/3200 -f 5284/3743/3216 5309/3744/3217 5283/3745/3218 -f 5285/3746/3219 5309/3744/3217 5284/3743/3216 -f 5287/3747/3220 5310/3748/3221 5286/3749/3222 -f 5289/3750/3223 5311/3751/3224 5288/3752/3225 -f 5290/3753/3226 5312/3754/3227 5289/3750/3223 -f 5291/3755/3228 5313/3756/3229 5290/3753/3226 -f 5293/3757/3230 5314/3758/3231 5292/3759/3232 -f 5295/3760/3233 5315/3761/3234 5294/3762/3235 -f 5315/3761/3234 5295/3760/3233 5296/3763/3236 -f 5315/3761/3234 5296/3763/3236 5297/3764/3237 -f 5316/3765/3238 5298/3766/3239 5299/3767/3240 -f 5316/3765/3238 5299/3767/3240 5300/3768/3241 -f 5317/3769/3242 5301/3770/3243 5302/3771/3244 -f 5318/3772/3245 5302/3771/3244 5303/3773/3246 -f 5319/3774/3247 5303/3773/3246 5304/3775/3248 -f 5320/3776/3249 5305/3777/3250 5306/3778/3251 -f 5321/3779/3252 5307/3780/3253 5308/3781/3254 -f 5310/3748/3221 5323/3782/3255 5309/3744/3217 -f 5311/3783/3224 5324/3784/3256 5310/3785/3221 -f 5312/3754/3227 5325/3786/3257 5311/3751/3224 -f 5313/3756/3229 5326/3787/3258 5312/3754/3227 -f 5314/3788/3231 5327/3789/3259 5313/3756/3229 -f 5328/3790/3260 5314/3758/3231 5315/3761/3234 -f 5333/3791/3261 5319/3774/3247 5320/3792/3249 -f 5321/3779/3252 5335/3793/3262 5320/3776/3249 -f 5322/3794/3263 5347/3795/3264 5335/3793/3262 -f 5323/3782/3255 5348/3796/3265 5322/3794/3263 -f 5324/3797/3256 5336/3798/3266 5323/3782/3255 -f 5325/3786/3257 5337/3799/3267 5324/3800/3256 -f 5326/3787/3258 5338/3801/3268 5325/3786/3257 -f 5327/3789/3259 5339/3802/3269 5326/3787/3258 -f 5340/3803/3270 5327/3804/3259 5328/3790/3260 -f 5341/3805/3271 5328/3790/3260 5329/3806/3272 -f 5341/3805/3271 5329/3806/3272 5330/3807/3273 -f 5342/3808/3274 5330/3807/3273 5331/3809/3275 -f 5343/3810/3276 5331/3811/3275 5332/3812/3277 -f 5344/3813/3278 5332/3812/3277 5333/3791/3261 -f 5334/3814/3279 5346/3815/3280 5333/3791/3261 -f 5336/3798/3266 5349/3816/3281 5348/3796/3265 -f 5337/3817/3267 5350/3818/3282 5336/3819/3266 -f 5338/3820/3268 5351/3821/3283 5337/3817/3267 -f 5339/3802/3269 5353/3822/3284 5338/3801/3268 -f 5354/3823/3285 5339/3802/3269 5340/3824/3270 -f 5355/3825/3286 5340/3803/3270 5341/3805/3271 -f 5356/3826/3287 5341/3805/3271 5342/3808/3274 -f 5357/3827/3288 5342/3808/3274 5343/3828/3276 -f 5358/3829/3289 5343/3828/3276 5344/3830/3278 -f 5359/3831/3290 5344/3813/3278 5345/3832/3291 -f 5360/3833/3292 5345/3832/3291 5346/3815/3280 -f 5347/3795/3264 5362/3834/3293 5346/3835/3280 -f 5348/3796/3265 5378/3836/3294 5362/3834/3293 -f 5349/3816/3281 5363/3837/3295 5348/3796/3265 -f 5350/3818/3282 5381/3838/3296 5364/3839/3297 -f 5351/3821/3283 5365/3840/3298 5350/3818/3282 -f 5352/3841/3299 5366/3842/3300 5351/3821/3283 -f 5353/3843/3284 5367/3844/3301 5352/3845/3299 -f 5368/3846/3302 5353/3843/3284 5354/3847/3285 -f 5369/3848/3303 5354/3849/3285 5355/3825/3286 -f 5370/3850/3304 5355/3825/3286 5341/3805/3271 -f 5370/3850/3304 5341/3805/3271 5356/3826/3287 -f 5372/3851/3305 5357/3827/3288 5358/3829/3289 -f 5374/3852/3306 5358/3829/3289 5359/3853/3290 -f 5375/3854/3307 5359/3855/3290 5360/3856/3292 -f 5361/3857/3308 5376/3858/3309 5360/3856/3292 -f 5362/3834/3293 5377/3859/3310 5361/3860/3308 -f 5363/3837/3295 5379/3861/3311 5378/3836/3294 -f 5364/3862/3297 5380/3863/3312 5363/3837/3295 -f 5365/3840/3298 5383/3864/3313 5382/3865/3314 -f 5366/3842/3300 5384/3866/3315 5365/3840/3298 -f 5367/3867/3301 5385/3868/3316 5366/3842/3300 -f 5386/3869/3317 5367/3844/3301 5368/3846/3302 -f 5387/3870/3318 5368/3871/3302 5369/3848/3303 -f 5389/3872/3319 5369/3848/3303 5370/3850/3304 -f 5390/3873/3320 5370/3850/3304 5371/3874/3321 -f 5405/3875/3322 5356/3826/3287 5372/3851/3305 -f 5377/3859/3310 5395/3876/3323 5376/3877/3309 -f 5378/3836/3294 5396/3878/3324 5377/3859/3310 -f 5398/3879/3325 5381/3838/3296 5382/3865/3314 -f 5399/3880/3326 5383/3864/3313 5384/3866/3315 -f 5390/3873/3320 5404/3881/3327 5389/3872/3319 -f 5392/3882/3328 5421/3883/3329 5391/3884/3330 -f 5410/3885/3331 5397/3886/3332 5380/3863/3312 -f 5411/3887/3333 5380/3863/3312 5381/3888/3296 -f 5413/3889/3334 5383/3864/3313 5399/3880/3326 -f 5414/3890/3335 5399/3880/3326 5400/3891/3336 -f 5415/3892/3337 5400/3893/3336 5401/3894/3338 -f 5402/3895/3339 5416/3896/3340 5401/3894/3338 -f 5403/3897/3341 5417/3898/3342 5402/3899/3339 -f 5404/3881/3327 5417/3898/3342 5403/3897/3341 -f 5390/3873/3320 5419/3900/3343 5404/3881/3327 -f 5405/3875/3322 5419/3900/3343 5390/3873/3320 -f 5392/3882/3328 5422/3901/3344 5421/3883/3329 -f 5406/3902/3345 5423/3903/3346 5392/3882/3328 -f 5407/3904/3347 5424/3905/3348 5406/3906/3345 -f 5426/3907/3349 5408/3908/3350 5409/3909/3351 -f 5427/3910/3352 5409/3909/3351 5397/3886/3332 -f 5429/3911/3353 5413/3889/3334 5414/3890/3335 -f 5418/3912/3354 5433/3913/3355 5417/3898/3342 -f 5424/3914/3348 5434/3915/3356 5423/3903/3346 -f 5439/3916/3357 5410/3885/3331 5411/3887/3333 -f 5440/3917/3358 5411/3887/3333 5412/3918/3359 -f 5441/3919/3360 5398/3879/3325 5413/3889/3334 -f 5442/3920/3361 5429/3921/3353 5430/3922/3362 -f 5431/3923/3363 5443/3924/3364 5430/3922/3362 -f 5432/3925/3365 5444/3926/3366 5431/3927/3363 -f 5419/3900/3343 5445/3928/3367 5418/3912/3354 -f 5405/3875/3322 5446/3929/3368 5419/3900/3343 -f 5420/3930/3369 5446/3929/3368 5405/3875/3322 -f 5422/3901/3344 5447/3931/3370 5421/3883/3329 -f 5450/3932/3371 5436/3933/3372 5437/3934/3373 -f 5451/3935/3374 5428/3936/3375 5439/3916/3357 -f 5452/3937/3376 5441/3919/3360 5442/3938/3361 -f 5444/3939/3366 5454/3940/3377 5443/3924/3364 -f 5433/3913/3355 5455/3941/3378 5432/3925/3365 -f 5446/3929/3368 5456/3942/3379 5445/3928/3367 -f 5434/3915/3356 5447/3931/3370 5422/3901/3344 -f 5448/3943/3380 5447/3931/3370 5434/3915/3356 -f 5458/3944/3381 5449/3945/3382 5450/3946/3371 -f 5460/3947/3383 5437/3934/3373 5438/3948/3384 -f 5462/3949/3385 5439/3916/3357 5440/3917/3358 -f 5463/3950/3386 5412/3951/3359 5441/3919/3360 -f 5464/3952/3387 5441/3919/3360 5452/3937/3376 -f 5457/3953/3388 5469/3954/3389 5446/3929/3368 -f 5447/3931/3370 5470/3955/3390 5457/3953/3388 -f 5448/3943/3380 5471/3956/3391 5447/3931/3370 -f 5473/3957/3392 5458/3944/3381 5459/3958/3393 -f 5479/3959/3394 5452/3960/3376 5453/3961/3395 -f 5465/3962/3396 5479/3959/3394 5453/3961/3395 -f 5487/3963/3397 5473/3964/3392 5474/3965/3398 -f 5465/3962/3396 5493/3966/3399 5479/3959/3394 -f 5502/3967/3400 5517/3968/3401 5501/3969/3402 -f 5504/3970/3403 5519/3971/3404 5518/3972/3405 -f 5505/3973/3406 5520/3974/3407 5504/3970/3403 -f 5506/3975/3408 5521/3976/3409 5505/3977/3406 -f 5507/3978/3410 5522/3979/3411 5506/3975/3408 -f 5508/3980/3412 5523/3981/3413 5507/3978/3410 -f 5509/3982/3414 5524/3983/3415 5508/3984/3412 -f 5525/3985/3416 5509/3982/3414 5510/3986/3417 -f 5531/3987/3418 5516/3988/3419 5517/3968/3401 -f 5502/3967/3400 5532/3989/3420 5517/3968/3401 -f 5518/3972/3405 5533/3990/3421 5502/3991/3400 -f 5541/3992/3422 5525/3993/3416 5526/3994/3423 -f 5542/3995/3424 5526/3994/3423 5527/3996/3425 -f 5543/3997/3426 5527/3996/3425 5528/3998/3427 -f 5544/3999/3428 5528/3998/3427 5529/4000/3429 -f 5545/4001/3430 5529/4000/3429 5530/4002/3431 -f 5545/4001/3430 5530/4002/3431 5531/4003/3418 -f 5538/4004/3432 5552/4005/3433 5537/4006/3434 -f 5538/4004/3432 5566/4007/3435 5552/4005/3433 -f 5577/4008/3436 5563/4009/3437 5564/4010/3438 -f 5578/4011/3439 5564/4010/3438 5565/4012/3440 -f 5579/4013/3441 5565/4014/3440 5566/4007/3435 -f 5581/4015/3442 5566/4007/3435 5567/4016/3443 -f 5568/4017/3444 5582/4018/3445 5567/4019/3443 -f 5569/4020/3446 5583/4021/3447 5568/4022/3444 -f 5570/4023/3448 5584/4024/3449 5569/4020/3446 -f 5571/4025/3450 5585/4026/3451 5570/4027/3448 -f 5572/4028/3452 5586/4029/3453 5571/4025/3450 -f 5573/4030/3454 5587/4031/3455 5572/4028/3452 -f 5574/4032/3456 5589/4033/3457 5573/4030/3454 -f 5590/4034/3458 5574/4035/3456 5575/4036/3459 -f 5591/4037/3460 5575/4038/3459 5576/4039/3461 -f 5592/4040/3462 5576/4039/3461 5563/4041/3437 -f 5596/4042/3463 5580/4043/3464 5581/4015/3442 -f 5608/4044/3465 5607/4045/3466 5593/4046/3467 -f 5609/4047/3468 5593/4046/3467 5594/4048/3469 -f 5610/4049/3470 5594/4050/3469 5595/4051/3471 -f 5611/4052/3472 5595/4053/3471 5596/4042/3463 -f 5597/4054/3473 5612/4055/3474 5596/4042/3463 -f 5600/4056/3475 5615/4057/3476 5599/4058/3477 -f 5601/4059/3478 5616/4060/3479 5600/4056/3475 -f 5602/4061/3480 5617/4062/3481 5601/4059/3478 -f 5603/4063/3482 5618/4064/3483 5602/4065/3480 -f 5620/4066/3484 5605/4067/3485 5606/4068/3486 -f 5620/4066/3484 5606/4068/3486 5607/4045/3466 -f 5613/4069/3487 5626/4070/3488 5597/4071/3473 -f 5614/4072/3489 5626/4070/3488 5613/4069/3487 -f 5632/4073/3490 5605/4067/3485 5620/4066/3484 -f 5621/4074/3491 5620/4066/3484 5608/4044/3465 -f 5634/4075/3492 5621/4074/3491 5622/4076/3493 -f 5635/4077/3494 5622/4076/3493 5623/4078/3495 -f 5636/4079/3496 5623/4080/3495 5624/4081/3497 -f 5638/4082/3498 5624/4083/3497 5625/4084/3499 -f 5626/4085/3488 5639/4086/3500 5625/4084/3499 -f 5614/4072/3489 5641/4087/3501 5626/4070/3488 -f 5627/4088/3502 5641/4087/3501 5614/4072/3489 -f 5628/4089/3503 5655/4090/3504 5627/4088/3502 -f 5629/4091/3505 5642/4092/3506 5628/4089/3503 -f 5630/4093/3507 5643/4094/3508 5629/4091/3505 -f 5631/4095/3509 5645/4096/3510 5630/4097/3507 -f 5646/4098/3511 5631/4095/3509 5632/4099/3490 -f 5647/4100/3512 5632/4073/3490 5620/4066/3484 -f 5648/4101/3513 5634/4075/3492 5635/4077/3494 -f 5640/4102/3514 5652/4103/3515 5639/4104/3500 -f 5641/4087/3501 5653/4105/3516 5640/4102/3514 -f 5627/4088/3502 5654/4106/3517 5641/4087/3501 -f 5654/4106/3517 5669/4107/3518 5653/4105/3516 -f 5655/4090/3504 5670/4108/3519 5654/4106/3517 -f 5676/4109/3520 5660/4110/3521 5661/4111/3522 -f 5677/4112/3523 5661/4111/3522 5633/4113/3524 -f 5678/4114/3525 5663/4115/3526 5664/4116/3527 -f 5670/4108/3519 5683/4117/3528 5669/4107/3518 -f 5671/4118/3529 5685/4119/3530 5684/4120/3531 -f 5672/4121/3532 5686/4122/3533 5671/4118/3529 -f 5673/4123/3534 5687/4124/3535 5672/4121/3532 -f 5674/4125/3536 5688/4126/3537 5673/4123/3534 -f 5663/4115/3526 5692/4127/3538 5662/4128/3539 -f 5678/4114/3525 5693/4129/3540 5663/4115/3526 -f 5679/4130/3541 5694/4131/3542 5678/4114/3525 -f 5680/4132/3543 5695/4133/3544 5679/4134/3541 -f 5681/4135/3545 5696/4136/3546 5680/4132/3543 -f 5698/4137/3547 5682/4138/3548 5683/4117/3528 -f 5699/4139/3549 5683/4117/3528 5670/4108/3519 -f 5700/4140/3550 5670/4108/3519 5684/4120/3531 -f 5700/4140/3550 5684/4120/3531 5685/4119/3530 -f 5701/4141/3551 5685/4119/3530 5686/4122/3533 -f 5702/4142/3552 5686/4122/3533 5687/4124/3535 -f 5703/4143/3553 5687/4124/3535 5688/4126/3537 -f 5690/4144/3554 5705/4145/3555 5689/4146/3556 -f 5691/4147/3557 5706/4148/3558 5690/4144/3554 -f 5662/4128/3539 5692/4127/3538 5691/4147/3557 -f 5696/4136/3546 5710/4149/3559 5695/4133/3544 -f 5697/4150/3560 5711/4151/3561 5696/4152/3546 -f 5712/4153/3562 5697/4154/3560 5698/4137/3547 -f 5713/4155/3563 5698/4137/3547 5699/4139/3549 -f 5714/4156/3564 5699/4139/3549 5700/4140/3550 -f 5715/4157/3565 5700/4140/3550 5701/4141/3551 -f 5716/4158/3566 5701/4141/3551 5702/4142/3552 -f 5717/4159/3567 5702/4142/3552 5703/4143/3553 -f 5717/4160/3567 5703/4161/3553 5704/4162/3568 -f 5718/4163/3569 5704/4162/3568 5705/4164/3555 -f 5708/4165/3570 5721/4166/3571 5707/4167/3572 -f 5709/4168/3573 5722/4169/3574 5708/4165/3570 -f 5710/4149/3559 5723/4170/3575 5709/4171/3573 -f 5711/4151/3561 5724/4172/3576 5710/4173/3559 -f 5725/4174/3577 5712/4153/3562 5713/4155/3563 -f 5726/4175/3578 5713/4155/3563 5714/4156/3564 -f 5727/4176/3579 5714/4156/3564 5715/4157/3565 -f 5728/4177/3580 5715/4157/3565 5716/4158/3566 -f 5729/4178/3581 5716/4158/3566 5717/4159/3567 -f 5730/4179/3582 5717/4160/3567 5718/4163/3569 -f 5720/4180/3583 5731/4181/3584 5719/4182/3585 -f 5707/4167/3572 5732/4183/3586 5720/4180/3583 -f 5721/4166/3571 5733/4184/3587 5732/4183/3586 -f 5722/4169/3574 5734/4185/3588 5721/4166/3571 -f 5723/4186/3575 5735/4187/3589 5722/4188/3574 -f 5724/4172/3576 5736/4189/3590 5723/4186/3575 -f 5725/4190/3577 5737/4191/3591 5724/4172/3576 -f 5739/4192/3592 5725/4174/3577 5726/4175/3578 -f 5740/4193/3593 5726/4175/3578 5727/4176/3579 -f 5741/4194/3594 5727/4176/3579 5728/4177/3580 -f 5742/4195/3595 5728/4196/3580 5729/4197/3581 -f 5743/4198/3596 5729/4197/3581 5730/4179/3582 -f 5744/4199/3597 5730/4179/3582 5731/4200/3584 -f 5732/4183/3586 5745/4201/3598 5731/4181/3584 -f 5734/4185/3588 5747/4202/3599 5733/4184/3587 -f 5735/4203/3589 5748/4204/3600 5734/4205/3588 -f 5736/4189/3590 5749/4206/3601 5735/4187/3589 -f 5737/4191/3591 5750/4207/3602 5736/4189/3590 -f 5738/4208/3603 5751/4209/3604 5737/4191/3591 -f 5752/4210/3605 5738/4211/3603 5739/4192/3592 -f 5755/4212/3606 5740/4193/3593 5741/4194/3594 -f 5756/4213/3607 5741/4214/3594 5742/4195/3595 -f 5757/4215/3608 5742/4195/3595 5743/4198/3596 -f 5758/4216/3609 5743/4198/3596 5744/4199/3597 -f 5746/4217/3610 5760/4218/3611 5745/4201/3598 -f 5733/4184/3587 5760/4218/3611 5746/4217/3610 -f 5748/4204/3600 5762/4219/3612 5747/4220/3599 -f 5749/4206/3601 5763/4221/3613 5748/4222/3600 -f 5750/4207/3602 5764/4223/3614 5749/4206/3601 -f 5766/4224/3615 5752/4210/3605 5753/4225/3616 -f 5767/4226/3617 5753/4225/3616 5754/4227/3618 -f 5768/4228/3619 5754/4227/3618 5755/4212/3606 -f 5769/4229/3620 5755/4230/3606 5756/4213/3607 -f 5770/4231/3621 5756/4213/3607 5757/4215/3608 -f 5771/4232/3622 5757/4215/3608 5758/4216/3609 -f 5772/4233/3623 5758/4216/3609 5759/4234/3624 -f 5772/4235/3623 5759/4236/3624 5760/4218/3611 -f 5761/4237/3625 5774/4238/3626 5773/4239/3627 -f 5762/4219/3612 5775/4240/3628 5761/4241/3625 -f 5763/4242/3613 5776/4243/3629 5762/4219/3612 -f 5764/4223/3614 5777/4244/3630 5763/4221/3613 -f 5765/4245/3631 5778/4246/3632 5764/4223/3614 -f 5766/4247/3615 5779/4248/3633 5765/4245/3631 -f 5781/4249/3634 5767/4226/3617 5768/4228/3619 -f 5782/4250/3635 5768/4251/3619 5769/4229/3620 -f 5783/4252/3636 5769/4229/3620 5770/4231/3621 -f 5784/4253/3637 5770/4231/3621 5771/4232/3622 -f 5785/4254/3638 5771/4232/3622 5772/4233/3623 -f 5803/4255/3639 5789/4256/3640 5790/4257/3641 -f 5804/4258/3642 5790/4259/3641 5791/4260/3643 -f 5805/4261/3644 5791/4260/3643 5792/4262/3645 -f 5806/4263/3646 5792/4264/3645 5793/4265/3647 -f 5807/4266/3648 5793/4265/3647 5794/4267/3649 -f 5795/4268/3650 5808/4269/3651 5794/4267/3649 -f 5801/4270/3652 5814/4271/3653 5800/4272/3654 -f 5815/4273/3655 5801/4274/3652 5788/4275/3656 -f 5816/4276/3657 5815/4273/3655 5802/4277/3658 -f 5820/4278/3659 5806/4263/3646 5807/4266/3648 -f 5808/4269/3651 5821/4279/3660 5807/4266/3648 -f 5810/4280/3661 5822/4281/3662 5809/4282/3663 -f 5811/4283/3664 5823/4284/3665 5810/4280/3661 -f 5827/4285/3666 5814/4271/3653 5815/4286/3655 -f 5830/4287/3667 5818/4288/3668 5819/4289/3669 -f 5821/4279/3660 5832/4290/3670 5820/4278/3659 -f 5822/4291/3662 5833/4292/3671 5821/4279/3660 -f 5823/4284/3665 5834/4293/3672 5822/4281/3662 -f 5824/4294/3673 5836/4295/3674 5835/4296/3675 -f 5825/4297/3676 5837/4298/3677 5824/4294/3673 -f 5838/4299/3678 5826/4300/3679 5827/4285/3666 -f 5840/4301/3680 5828/4302/3681 5829/4303/3682 -f 5841/4304/3683 5829/4303/3682 5818/4305/3668 -f 5842/4306/3684 5818/4288/3668 5830/4287/3667 -f 5844/4307/3685 5830/4308/3667 5831/4309/3686 -f 5836/4295/3674 5855/4310/3687 5835/4296/3675 -f 5864/4311/3688 5838/4299/3678 5828/4312/3681 -f 5873/4313/3689 5899/4314/3690 5898/4315/3691 -f 5900/4316/3692 5875/4317/3693 5901/4318/3694 -f 5908/4319/3695 5889/4320/3696 5909/4321/3697 -f 5890/4322/3698 5910/4323/3699 5909/4321/3697 -f 5866/4324/3700 5913/4325/3701 5868/4326/3702 -f 5872/4327/3703 5900/4316/3692 5874/4328/3704 -f 5885/4329/3705 5919/4330/3706 5887/4331/3707 -f 5873/4332/3689 5915/4333/3708 5875/4334/3693 -f 5881/4335/3709 5918/4336/3710 5883/4337/3711 -f 5875/4334/3693 5915/4333/3708 5916/4338/3712 -f 5870/4339/3713 5914/4340/3714 5873/4332/3689 -f 5915/4341/3708 5924/4342/3715 5916/4343/3712 -f 5919/4330/3706 5928/4344/3716 5920/4345/3717 -f 5916/4346/3712 5925/4347/3718 5917/4348/3719 -f 5920/4345/3717 5928/4344/3716 5929/4349/3720 -f 5913/4350/3701 5922/4351/3721 5923/4352/3722 -f 5917/4348/3719 5926/4353/3723 5918/4354/3710 -f 5921/4355/3724 5929/4356/3720 5922/4351/3721 -f 5914/4357/3714 5923/4352/3722 5924/4358/3715 -f 5918/4336/3710 5927/4359/3725 5919/4330/3706 -f 5938/4360/3689 5964/4361/3690 5963/4362/3691 -f 5965/4363/3692 5940/4364/3693 5966/4365/3726 -f 5973/4366/3695 5954/4367/3696 5974/4368/3697 -f 5955/4369/3698 5975/4370/3699 5974/4368/3697 -f 5931/4371/3700 5978/4372/3701 5933/4373/3702 -f 5937/4374/3703 5965/4363/3692 5939/4375/3704 -f 5950/4376/3705 5984/4377/3706 5952/4378/3707 -f 5938/4379/3689 5980/4380/3708 5940/4381/3693 -f 5946/4382/3709 5983/4383/3710 5948/4384/3711 -f 5940/4381/3693 5980/4380/3708 5981/4385/3712 -f 5935/4386/3713 5979/4387/3714 5938/4379/3689 -f 5980/4388/3708 5989/4389/3715 5981/4390/3712 -f 5984/4377/3706 5993/4391/3716 5985/4392/3717 -f 5981/4393/3712 5990/4394/3718 5982/4395/3719 -f 5985/4392/3717 5993/4391/3716 5994/4396/3720 -f 5978/4397/3701 5987/4398/3721 5988/4399/3722 -f 5982/4395/3719 5991/4400/3723 5983/4401/3710 -f 5986/4402/3724 5994/4403/3720 5987/4398/3721 -f 5979/4404/3714 5988/4399/3722 5989/4405/3715 -f 5983/4383/3710 5992/4406/3725 5984/4377/3706 -f 6003/4407/3689 6029/4408/3690 6028/4409/3691 -f 6030/4410/3692 6005/4411/3693 6031/4412/3726 -f 6038/4413/3695 6019/4414/3696 6039/4415/3697 -f 6020/4416/3698 6040/4417/3699 6039/4415/3697 -f 5996/4418/3700 6043/4419/3701 5998/4420/3702 -f 6002/4421/3703 6030/4410/3692 6004/4422/3704 -f 6015/4423/3705 6049/4424/3706 6017/4425/3707 -f 6003/4426/3689 6045/4427/3708 6005/4428/3693 -f 6011/4429/3709 6048/4430/3710 6013/4431/3711 -f 6005/4428/3693 6045/4427/3708 6046/4432/3712 -f 6000/4433/3713 6044/4434/3714 6003/4426/3689 -f 6045/4435/3708 6054/4436/3715 6046/4437/3712 -f 6049/4424/3706 6058/4438/3716 6050/4439/3717 -f 6046/4440/3712 6055/4441/3718 6047/4442/3719 -f 6050/4439/3717 6058/4438/3716 6059/4443/3720 -f 6043/4444/3701 6052/4445/3721 6053/4446/3722 -f 6047/4442/3719 6056/4447/3723 6048/4448/3710 -f 6051/4449/3724 6059/4450/3720 6052/4445/3721 -f 6044/4451/3714 6053/4446/3722 6054/4452/3715 -f 6048/4430/3710 6057/4453/3725 6049/4424/3706 -f 6068/4454/3689 6094/4455/3690 6093/4456/3691 -f 6095/4457/3692 6070/4458/3693 6096/4459/3726 -f 6103/4460/3695 6084/4461/3696 6104/4462/3697 -f 6085/4463/3698 6105/4464/3699 6104/4462/3697 -f 6061/4465/3700 6108/4466/3701 6063/4467/3702 -f 6067/4468/3703 6095/4457/3692 6069/4469/3704 -f 6080/4470/3705 6114/4471/3706 6082/4472/3707 -f 6068/4473/3689 6110/4474/3708 6070/4475/3693 -f 6076/4476/3709 6113/4477/3710 6078/4478/3711 -f 6070/4475/3693 6110/4474/3708 6111/4479/3712 -f 6065/4480/3713 6109/4481/3714 6068/4473/3689 -f 6110/4482/3708 6119/4483/3715 6111/4484/3712 -f 6114/4471/3706 6123/4485/3716 6115/4486/3717 -f 6111/4487/3712 6120/4488/3718 6112/4489/3719 -f 6115/4486/3717 6123/4485/3716 6124/4490/3720 -f 6108/4491/3701 6117/4492/3721 6118/4493/3722 -f 6112/4489/3719 6121/4494/3723 6113/4495/3710 -f 6116/4496/3724 6124/4497/3720 6117/4492/3721 -f 6109/4498/3714 6118/4493/3722 6119/4499/3715 -f 6113/4477/3710 6122/4500/3725 6114/4471/3706 -f 6144/4501/3727 6168/4502/3728 6143/4503/3729 -f 6145/4504/3730 6169/4505/3731 6144/4506/3727 -f 6146/4507/3732 6169/4505/3731 6145/4504/3730 -f 6147/4508/3733 6170/4509/3734 6146/4507/3732 -f 6148/4510/3735 6171/4511/3736 6147/4508/3733 -f 6191/4512/3737 6167/4513/3738 6168/4502/3728 -f 6169/4514/3731 6192/4515/3739 6168/4502/3728 -f 6170/4509/3734 6193/4516/3740 6169/4505/3731 -f 6171/4511/3736 6194/4517/3741 6170/4509/3734 -f 6172/4518/3742 6195/4519/3743 6171/4511/3736 -f 6181/4520/3744 6203/4521/3745 6180/4522/3746 -f 6184/4523/3747 6205/4524/3748 6183/4525/3749 -f 6186/4526/3750 6206/4527/3751 6185/4528/3752 -f 6187/4529/3753 6206/4527/3751 6186/4526/3750 -f 6193/4516/3740 6211/4530/3754 6192/4531/3739 -f 6194/4517/3741 6212/4532/3755 6193/4516/3740 -f 6195/4519/3743 6213/4533/3756 6194/4517/3741 -f 6188/4534/3757 6226/4535/3758 6187/4529/3753 -f 6211/4536/3754 6230/4537/3759 6210/4538/3760 -f 6212/4532/3755 6231/4539/3761 6211/4530/3754 -f 6213/4533/3756 6232/4540/3762 6212/4532/3755 -f 6226/4535/3758 6244/4541/3763 6225/4542/3764 -f 6245/4543/3765 6264/4544/3766 6226/4535/3758 -f 6265/4545/3767 6246/4546/3768 6247/4547/3769 -f 6266/4548/3770 6247/4549/3769 6248/4550/3771 -f 6249/4551/3772 6267/4552/3773 6248/4550/3771 -f 6250/4553/3774 6268/4554/3775 6249/4555/3772 -f 6251/4556/3776 6269/4557/3777 6250/4553/3774 -f 6252/4558/3778 6270/4559/3779 6251/4556/3776 -f 6253/4560/3780 6271/4561/3781 6252/4558/3778 -f 6254/4562/3782 6272/4563/3783 6253/4560/3780 -f 6255/4564/3784 6273/4565/3785 6254/4562/3782 -f 6256/4566/3786 6274/4567/3787 6255/4564/3784 -f 6257/4568/3788 6275/4569/3789 6256/4570/3786 -f 6258/4571/3790 6276/4572/3791 6257/4568/3788 -f 6259/4573/3792 6277/4574/3793 6258/4571/3790 -f 6260/4575/3794 6278/4576/3795 6259/4573/3792 -f 6261/4577/3796 6279/4578/3797 6260/4575/3794 -f 6262/4579/3798 6280/4580/3799 6261/4577/3796 -f 6264/4544/3766 6282/4581/3800 6263/4582/3801 -f 6284/4583/3802 6265/4584/3767 6266/4548/3770 -f 6285/4585/3803 6266/4548/3770 6267/4552/3773 -f 6268/4586/3775 6286/4587/3804 6267/4552/3773 -f 6269/4557/3777 6287/4588/3805 6268/4554/3775 -f 6270/4559/3779 6288/4589/3806 6269/4557/3777 -f 6271/4561/3781 6289/4590/3807 6270/4559/3779 -f 6272/4563/3783 6290/4591/3808 6271/4561/3781 -f 6273/4565/3785 6291/4592/3809 6272/4563/3783 -f 6274/4567/3787 6292/4593/3810 6273/4565/3785 -f 6275/4569/3789 6293/4594/3811 6274/4595/3787 -f 6276/4572/3791 6294/4596/3812 6275/4569/3789 -f 6277/4574/3793 6295/4597/3813 6276/4572/3791 -f 6278/4576/3795 6296/4598/3814 6277/4574/3793 -f 6279/4578/3797 6297/4599/3815 6278/4576/3795 -f 6280/4580/3799 6298/4600/3816 6279/4578/3797 -f 6281/4601/3817 6299/4602/3818 6280/4580/3799 -f 6245/4543/3765 6302/4603/3819 6264/4544/3766 -f 6304/4604/3820 6322/4605/3821 6303/4606/3822 -f 6305/4607/3823 6323/4608/3824 6304/4609/3820 -f 6306/4610/3825 6324/4611/3826 6305/4607/3823 -f 6324/4611/3826 6306/4610/3825 6307/4612/3827 -f 6325/4613/3828 6307/4614/3827 6308/4615/3829 -f 6326/4616/3830 6308/4615/3829 6309/4617/3831 -f 6338/4618/3832 6320/4619/3833 6321/4620/3834 -f 6339/4621/3835 6321/4620/3834 6302/4603/3819 -f 6343/4622/3836 6324/4611/3826 6325/4623/3828 -f 6344/4624/3837 6325/4613/3828 6326/4616/3830 -f 6345/4625/3838 6326/4616/3830 6327/4626/3839 -f 6357/4627/3840 6338/4618/3832 6339/4621/3835 -f 6366/4628/3841 6345/4625/3838 6346/4629/3842 -f 6367/4630/3843 6346/4629/3842 6347/4631/3844 -f 6368/4632/3845 6347/4631/3844 6348/4633/3846 -f 6369/4634/3847 6348/4633/3846 6349/4635/3848 -f 6370/4636/3849 6349/4635/3848 6350/4637/3850 -f 6371/4638/3851 6350/4639/3850 6351/4640/3852 -f 6372/4641/3853 6351/4640/3852 6352/4642/3854 -f 6373/4643/3855 6352/4642/3854 6353/4644/3856 -f 6374/4645/3857 6353/4644/3856 6354/4646/3858 -f 6375/4647/3859 6354/4646/3858 6355/4648/3860 -f 6376/4649/3861 6355/4648/3860 6356/4650/3862 -f 6361/4651/3863 6379/4652/3864 6360/4653/3865 -f 6362/4654/3866 6380/4655/3867 6361/4656/3863 -f 6381/4657/3868 6362/4654/3866 6363/4658/3869 -f 6382/4659/3870 6363/4660/3869 6364/4661/3871 -f 6383/4662/3872 6364/4661/3871 6365/4663/3873 -f 6384/4664/3874 6365/4663/3873 6366/4628/3841 -f 6400/4665/3875 6380/4655/3867 6381/4657/3868 -f 6402/4666/3876 6381/4667/3868 6382/4659/3870 -f 6403/4668/3877 6382/4659/3870 6383/4662/3872 -f 6404/4669/3878 6383/4662/3872 6384/4664/3874 -f 6405/4670/3879 6384/4664/3874 6385/4671/3880 -f 6406/4672/3881 6385/4671/3880 6386/4673/3882 -f 6407/4674/3883 6386/4673/3882 6387/4675/3884 -f 6408/4676/3885 6387/4675/3884 6388/4677/3886 -f 6409/4678/3887 6388/4677/3886 6389/4679/3888 -f 6410/4680/3889 6389/4679/3888 6390/4681/3890 -f 6411/4682/3891 6390/4681/3890 6391/4683/3892 -f 6412/4684/3893 6391/4685/3892 6392/4686/3894 -f 6413/4687/3895 6392/4686/3894 6393/4688/3896 -f 6414/4689/3897 6393/4688/3896 6394/4690/3898 -f 6421/4691/3899 6401/4692/3900 6402/4666/3876 -f 6422/4693/3901 6402/4666/3876 6403/4668/3877 -f 6423/4694/3902 6403/4668/3877 6404/4669/3878 -f 6424/4695/3903 6404/4669/3878 6405/4670/3879 -f 6425/4696/3904 6405/4670/3879 6406/4672/3881 -f 6426/4697/3905 6406/4672/3881 6407/4674/3883 -f 6427/4698/3906 6407/4674/3883 6408/4676/3885 -f 6428/4699/3907 6408/4676/3885 6409/4678/3887 -f 6429/4700/3908 6409/4678/3887 6410/4680/3889 -f 6430/4701/3909 6410/4680/3889 6411/4682/3891 -f 6431/4702/3910 6411/4682/3891 6412/4703/3893 -f 6433/4704/3911 6412/4703/3893 6413/4705/3895 -f 6434/4706/3912 6413/4687/3895 6414/4689/3897 -f 6435/4707/3913 6414/4689/3897 6415/4708/3914 -f 6420/4709/3915 6442/4710/3916 6419/4711/3917 -f 6443/4712/3918 6420/4709/3915 6421/4713/3899 -f 6445/4714/3919 6421/4691/3899 6422/4693/3901 -f 6446/4715/3920 6422/4693/3901 6423/4694/3902 -f 6447/4716/3921 6423/4694/3902 6424/4695/3903 -f 6459/4717/3922 6436/4718/3923 6437/4719/3924 -f 6460/4720/3925 6437/4719/3924 6438/4721/3926 -f 6461/4722/3927 6438/4721/3926 6439/4723/3928 -f 6462/4724/3929 6439/4723/3928 6397/4725/3930 -f 6466/4726/3931 6442/4727/3916 6443/4728/3918 -f 6467/4729/3932 6443/4730/3918 6444/4731/3933 -f 6468/4732/3934 6444/4731/3933 6445/4714/3919 -f 6469/4733/3935 6445/4714/3919 6446/4715/3920 -f 6470/4734/3936 6446/4715/3920 6447/4716/3921 -f 6466/4726/3931 6488/4735/3937 6465/4736/3938 -f 6489/4737/3939 6466/4738/3931 6467/4729/3932 -f 6489/4737/3939 6467/4729/3932 6468/4732/3934 -f 6490/4739/3940 6468/4732/3934 6469/4733/3935 -f 6491/4740/3941 6469/4733/3935 6470/4734/3936 -f 6492/4741/3942 6470/4734/3936 6471/4742/3943 -f 6505/4743/3944 6483/4744/3945 6484/4745/3946 -f 6506/4746/3947 6484/4745/3946 6485/4747/3948 -f 6507/4748/3949 6485/4747/3948 6462/4749/3929 -f 6489/4750/3939 6511/4751/3950 6488/4735/3937 -f 6490/4739/3940 6512/4752/3951 6489/4737/3939 -f 6491/4740/3941 6513/4753/3952 6490/4739/3940 -f 6492/4741/3942 6514/4754/3953 6491/4740/3941 -f 6493/4755/3954 6515/4756/3955 6492/4741/3942 -f 6494/4757/3956 6516/4758/3957 6493/4755/3954 -f 6495/4759/3958 6517/4760/3959 6494/4757/3956 -f 6496/4761/3960 6518/4762/3961 6495/4759/3958 -f 6497/4763/3962 6519/4764/3963 6496/4761/3960 -f 6498/4765/3964 6520/4766/3965 6497/4763/3962 -f 6499/4767/3966 6521/4768/3967 6498/4765/3964 -f 6500/4769/3968 6522/4770/3969 6499/4767/3966 -f 6501/4771/3970 6522/4770/3969 6500/4769/3968 -f 6502/4772/3971 6523/4773/3972 6501/4771/3970 -f 6503/4774/3973 6524/4775/3974 6502/4772/3971 -f 6504/4776/3975 6525/4777/3976 6503/4774/3973 -f 6505/4743/3944 6526/4778/3977 6504/4776/3975 -f 6506/4746/3947 6527/4779/3978 6505/4743/3944 -f 6526/4778/3977 6547/4780/3979 6525/4777/3976 -f 6527/4779/3978 6548/4781/3980 6526/4778/3977 -f 6554/4782/3981 6532/4783/3982 6533/4784/3983 -f 6534/4785/3984 6555/4786/3985 6533/4784/3983 -f 6535/4787/3986 6556/4788/3987 6534/4789/3984 -f 6536/4790/3988 6557/4791/3989 6535/4787/3986 -f 6537/4792/3990 6558/4793/3991 6536/4790/3988 -f 6538/4794/3992 6559/4795/3993 6537/4792/3990 -f 6539/4796/3994 6560/4797/3995 6538/4794/3992 -f 6540/4798/3996 6561/4799/3997 6539/4796/3994 -f 6541/4800/3998 6562/4801/3999 6540/4798/3996 -f 6542/4802/4000 6563/4803/4001 6541/4800/3998 -f 6543/4804/4002 6564/4805/4003 6542/4802/4000 -f 6544/4806/4004 6565/4807/4005 6543/4804/4002 -f 6545/4808/4006 6566/4809/4007 6544/4806/4004 -f 6546/4810/4008 6567/4811/4009 6545/4808/4006 -f 6547/4812/3979 6568/4813/4010 6546/4814/4008 -f 6549/4815/4011 6569/4816/4012 6548/4817/3980 -f 6551/4818/4013 6570/4819/4014 6550/4820/4015 -f 6570/4819/4014 6588/4821/4016 6569/4816/4012 -f 6589/4822/4017 6571/4823/4018 6572/4824/4019 -f 6590/4825/4020 6572/4826/4019 6573/4827/4021 -f 6574/4828/4022 6591/4829/4023 6573/4827/4021 -f 6575/4830/4024 6592/4831/4025 6574/4832/4022 -f 6576/4833/4026 6593/4834/4027 6575/4830/4024 -f 6577/4835/4028 6594/4836/4029 6576/4833/4026 -f 6578/4837/4030 6595/4838/4031 6577/4835/4028 -f 6579/4839/4032 6596/4840/4033 6578/4837/4030 -f 6580/4841/4034 6597/4842/4035 6579/4839/4032 -f 6581/4843/4036 6598/4844/4037 6580/4841/4034 -f 6582/4845/4038 6599/4846/4039 6581/4843/4036 -f 6583/4847/4040 6600/4848/4041 6582/4845/4038 -f 6584/4849/4042 6601/4850/4043 6583/4851/4040 -f 6585/4852/4044 6602/4853/4045 6584/4849/4042 -f 6586/4854/4046 6603/4855/4047 6585/4852/4044 -f 6587/4856/4048 6604/4857/4049 6586/4854/4046 -f 6592/4858/4025 6611/4859/4050 6591/4829/4023 -f 6593/4834/4027 6612/4860/4051 6592/4831/4025 -f 6594/4836/4029 6613/4861/4052 6593/4834/4027 -f 6607/4862/4053 6626/4863/4054 6606/4864/4055 -f 6612/4865/4051 6630/4866/4056 6611/4859/4050 -f 6613/4861/4052 6631/4867/4057 6612/4860/4051 -f 6614/4868/4058 6632/4869/4059 6613/4861/4052 -f 6615/4870/4060 6633/4871/4061 6614/4868/4058 -f 6616/4872/4062 6634/4873/4063 6615/4870/4060 -f 6617/4874/4064 6635/4875/4065 6616/4872/4062 -f 6618/4876/4066 6636/4877/4067 6617/4874/4064 -f 6619/4878/4068 6637/4879/4069 6618/4876/4066 -f 6620/4880/4070 6638/4881/4071 6619/4882/4068 -f 6621/4883/4072 6639/4884/4073 6620/4880/4070 -f 6622/4885/4074 6640/4886/4075 6621/4883/4072 -f 6623/4887/4076 6641/4888/4077 6622/4885/4074 -f 6624/4889/4078 6642/4890/4079 6623/4887/4076 -f 6625/4891/4080 6643/4892/4081 6624/4889/4078 -f 6608/4893/4082 6645/4894/4083 6607/4862/4053 -f 6630/4866/4056 6648/4895/4084 6629/4896/4085 -f 6631/4867/4057 6649/4897/4086 6630/4898/4056 -f 6632/4869/4059 6650/4899/4087 6631/4867/4057 -f 6633/4871/4061 6651/4900/4088 6632/4869/4059 -f 6634/4873/4063 6652/4901/4089 6633/4871/4061 -f 6635/4875/4065 6653/4902/4090 6634/4873/4063 -f 6636/4877/4067 6654/4903/4091 6635/4875/4065 -f 6637/4879/4069 6655/4904/4092 6636/4877/4067 -f 6638/4881/4071 6656/4905/4093 6637/4906/4069 -f 6639/4884/4073 6657/4907/4094 6638/4881/4071 -f 6640/4886/4075 6658/4908/4095 6639/4884/4073 -f 6641/4888/4077 6659/4909/4096 6640/4886/4075 -f 6642/4890/4079 6660/4910/4097 6641/4888/4077 -f 6643/4892/4081 6661/4911/4098 6642/4890/4079 -f 6645/4894/4083 6663/4912/4099 6644/4913/4100 -f 6665/4914/4101 6646/4915/4102 6647/4916/4103 -f 6666/4917/4104 6647/4918/4103 6648/4895/4084 -f 6667/4919/4105 6648/4895/4084 6649/4920/4086 -f 6668/4921/4106 6649/4897/4086 6650/4899/4087 -f 6651/4900/4088 6669/4922/4107 6650/4899/4087 -f 6652/4901/4089 6670/4923/4108 6651/4900/4088 -f 6653/4902/4090 6671/4924/4109 6652/4901/4089 -f 6654/4903/4091 6672/4925/4110 6653/4902/4090 -f 6655/4904/4092 6673/4926/4111 6654/4903/4091 -f 6656/4927/4093 6674/4928/4112 6655/4904/4092 -f 6657/4907/4094 6675/4929/4113 6656/4905/4093 -f 6658/4908/4095 6676/4930/4114 6657/4907/4094 -f 6659/4909/4096 6677/4931/4115 6658/4908/4095 -f 6660/4910/4097 6678/4932/4116 6659/4909/4096 -f 6661/4911/4098 6679/4933/4117 6660/4910/4097 -f 6662/4934/4118 6680/4935/4119 6661/4911/4098 -f 6663/4912/4099 6680/4935/4119 6662/4934/4118 -f 6645/4894/4083 6681/4936/4120 6663/4912/4099 -f 6608/4893/4082 6681/4936/4120 6645/4894/4083 -f 6665/4914/4101 6683/4937/4121 6664/4938/4122 -f 6666/4917/4104 6684/4939/4123 6665/4940/4101 -f 6685/4941/4124 6666/4917/4104 6667/4919/4105 -f 6686/4942/4125 6667/4943/4105 6668/4921/4106 -f 6687/4944/4126 6668/4921/4106 6669/4922/4107 -f 6688/4945/4127 6669/4922/4107 6670/4923/4108 -f 6689/4946/4128 6670/4923/4108 6671/4924/4109 -f 6690/4947/4129 6671/4924/4109 6672/4925/4110 -f 6691/4948/4130 6672/4925/4110 6673/4926/4111 -f 6692/4949/4131 6673/4926/4111 6674/4928/4112 -f 6693/4950/4132 6674/4951/4112 6675/4929/4113 -f 6694/4952/4133 6675/4929/4113 6676/4930/4114 -f 6695/4953/4134 6676/4930/4114 6677/4931/4115 -f 6696/4954/4135 6677/4931/4115 6678/4932/4116 -f 6697/4955/4136 6678/4932/4116 6679/4933/4117 -f 6698/4956/4137 6679/4933/4117 6680/4935/4119 -f 6683/4937/4121 6702/4957/4138 6682/4958/4139 -f 6684/4939/4123 6703/4959/4140 6683/4960/4121 -f 6704/4961/4141 6684/4939/4123 6685/4941/4124 -f 6705/4962/4142 6685/4963/4124 6686/4942/4125 -f 6706/4964/4143 6686/4942/4125 6687/4944/4126 -f 6707/4965/4144 6687/4944/4126 6688/4945/4127 -f 6708/4966/4145 6688/4945/4127 6689/4946/4128 -f 6709/4967/4146 6689/4946/4128 6690/4947/4129 -f 6710/4968/4147 6690/4947/4129 6691/4948/4130 -f 6711/4969/4148 6691/4948/4130 6692/4949/4131 -f 6712/4970/4149 6692/4949/4131 6693/4971/4132 -f 6713/4972/4150 6693/4950/4132 6694/4952/4133 -f 6714/4973/4151 6694/4952/4133 6695/4953/4134 -f 6715/4974/4152 6695/4953/4134 6696/4954/4135 -f 6716/4975/4153 6696/4954/4135 6697/4955/4136 -f 6717/4976/4154 6697/4955/4136 6698/4956/4137 -f 6719/4977/4155 6699/4978/4156 6700/4979/4157 -f 6725/4980/4158 6706/4964/4143 6707/4965/4144 -f 6742/4981/4159 6723/4982/4160 6724/4983/4161 -f 6743/4984/4162 6724/4983/4161 6725/4980/4158 -f 6744/4985/4163 6725/4980/4158 6726/4986/4164 -f 6745/4987/4165 6726/4986/4164 6727/4988/4166 -f 6746/4989/4167 6727/4988/4166 6728/4990/4168 -f 6747/4991/4169 6728/4990/4168 6729/4992/4170 -f 6748/4993/4171 6729/4992/4170 6730/4994/4172 -f 6749/4995/4173 6730/4994/4172 6731/4996/4174 -f 6750/4997/4175 6731/4996/4174 6732/4998/4176 -f 6751/4999/4177 6732/5000/4176 6733/5001/4178 -f 6752/5002/4179 6733/5001/4178 6734/5003/4180 -f 6753/5004/4181 6734/5003/4180 6735/5005/4182 -f 6754/5006/4183 6735/5005/4182 6736/5007/4184 -f 6755/5008/4185 6736/5007/4184 6737/5009/4186 -f 6740/5010/4187 6760/5011/4188 6739/5012/4189 -f 6741/5013/4190 6761/5014/4191 6740/5015/4187 -f 6762/5016/4192 6741/5013/4190 6742/5017/4159 -f 6763/5018/4193 6742/4981/4159 6743/4984/4162 -f 6764/5019/4194 6743/4984/4162 6744/4985/4163 -f 6765/5020/4195 6744/4985/4163 6745/4987/4165 -f 6766/5021/4196 6745/4987/4165 6746/4989/4167 -f 6767/5022/4197 6746/4989/4167 6747/4991/4169 -f 6768/5023/4198 6747/4991/4169 6748/4993/4171 -f 6769/5024/4199 6748/4993/4171 6749/4995/4173 -f 6770/5025/4200 6749/4995/4173 6750/4997/4175 -f 6771/5026/4201 6750/4997/4175 6751/5027/4177 -f 6772/5028/4202 6751/5027/4177 6752/5029/4179 -f 6773/5030/4203 6752/5002/4179 6753/5004/4181 -f 6774/5031/4204 6753/5004/4181 6754/5006/4183 -f 6775/5032/4205 6754/5006/4183 6755/5008/4185 -f 6780/5033/4206 6761/5014/4191 6762/5016/4192 -f 6781/5034/4207 6762/5035/4192 6763/5018/4193 -f 6782/5036/4208 6763/5018/4193 6764/5019/4194 -f 6783/5037/4209 6764/5019/4194 6765/5020/4195 -f 6780/5033/4206 6802/5038/4210 6779/5039/4211 -f 6803/5040/4212 6780/5033/4206 6781/5041/4207 -f 6805/5042/4213 6781/5034/4207 6782/5036/4208 -f 6806/5043/4214 6782/5036/4208 6783/5037/4209 -f 6807/5044/4215 6783/5037/4209 6784/5045/4216 -f 6819/5046/4217 6796/5047/4218 6797/5048/4219 -f 6820/5049/4220 6797/5048/4219 6798/5050/4221 -f 6821/5051/4222 6798/5050/4221 6799/5052/4223 -f 6822/5053/4224 6799/5052/4223 6758/5054/4225 -f 6829/5055/4226 6806/5043/4214 6807/5044/4215 -f 6831/5056/4227 6807/5044/4215 6808/5057/4228 -f 6832/5058/4229 6808/5057/4228 6809/5059/4230 -f 6833/5060/4231 6809/5059/4230 6810/5061/4232 -f 6834/5062/4233 6810/5061/4232 6811/5063/4234 -f 6835/5064/4235 6811/5063/4234 6812/5065/4236 -f 6836/5066/4237 6812/5065/4236 6813/5067/4238 -f 6837/5068/4239 6813/5067/4238 6814/5069/4240 -f 6838/5070/4241 6814/5069/4240 6815/5071/4242 -f 6839/5072/4243 6815/5071/4242 6816/5073/4244 -f 6840/5074/4245 6816/5073/4244 6817/5075/4246 -f 6842/5076/4247 6817/5075/4246 6818/5077/4248 -f 6843/5078/4249 6818/5077/4248 6819/5079/4217 -f 6850/5080/4250 6825/5081/4251 6826/5082/4252 -f 6851/5083/4253 6826/5084/4252 6827/5085/4254 -f 6851/5083/4253 6827/5085/4254 6828/5086/4255 -f 6852/5087/4256 6828/5086/4255 6829/5055/4226 -f 6853/5088/4257 6829/5055/4226 6830/5089/4258 -f 6854/5090/4259 6830/5089/4258 6831/5056/4227 -f 6864/5091/4260 6841/5092/4261 6842/5076/4247 -f 6867/5093/4262 6845/5094/4263 6846/5095/4264 -f 6868/5096/4265 6846/5095/4264 6822/5097/4224 -f 6143/5098/3729 6849/5099/4266 6850/5080/4250 -f 6851/5083/4253 6144/4506/3727 6850/5100/4250 -f 6852/5087/4256 6146/4507/3732 6851/5083/4253 -f 6853/5088/4257 6147/4508/3733 6852/5087/4256 -f 6854/5090/4259 6148/4510/3735 6853/5088/4257 -f 6855/5101/4267 6149/5102/4268 6854/5090/4259 -f 6856/5103/4269 6150/5104/4270 6855/5101/4267 -f 6857/5105/4271 6151/5106/4272 6856/5103/4269 -f 6858/5107/4273 6152/5108/4274 6857/5105/4271 -f 6859/5109/4275 6153/5110/4276 6858/5107/4273 -f 6860/5111/4277 6154/5112/4278 6859/5109/4275 -f 6861/5113/4279 6155/5114/4280 6860/5111/4277 -f 6862/5115/4281 6156/5116/4282 6861/5113/4279 -f 6863/5117/4283 6157/5118/4284 6862/5115/4281 -f 6864/5091/4260 6158/5119/4285 6863/5117/4283 -f 6865/5120/4286 6159/5121/4287 6864/5091/4260 -f 6866/5122/4288 6160/5123/4289 6865/5120/4286 -f 6867/5093/4262 6161/5124/4290 6866/5122/4288 -f 115/4/4 105/5125/4291 6/5/5 -f 94/41/7 77/40/39 5/5126/49 -f 1/5127/14 103/5128/4292 17/5129/15 -f 62/5130/42 68/5131/4293 103/5128/4292 -f 74/5132/4294 67/5133/47 63/5134/4295 -f 63/5135/4295 68/5131/4293 64/5136/41 -f 67/49/47 103/5128/4292 68/5131/4293 -f 12/5137/34 70/16/16 9/18/18 -f 91/5138/4296 13/58/56 11/5139/51 -f 91/5138/4296 11/5139/51 4/10/10 -f 127/67/61 116/66/25 82/5140/27 -f 7/2/2 88/32/31 14/3/3 -f 65/17/17 115/4/4 73/6/6 -f 88/32/31 8/5141/4297 2/5142/4298 -f 84/5143/28 119/5144/4299 85/5145/4300 -f 126/65/60 118/64/23 15/5146/20 -f 125/24/24 117/5147/4301 71/5148/21 -f 125/24/24 71/5148/21 15/25/20 -f 124/27/26 119/5149/4299 86/5150/29 -f 124/27/26 86/5150/29 82/28/27 -f 84/5143/28 126/65/60 15/5146/20 -f 82/5151/27 87/19/19 83/22/22 -f 80/33/32 64/43/41 81/31/30 -f 84/29/28 15/20/20 87/19/19 -f 2/5142/4298 80/33/32 88/32/31 -f 81/31/30 14/3/3 88/32/31 -f 74/5132/4294 63/5134/4295 80/33/32 -f 83/5152/22 117/5153/4301 89/5154/4302 -f 83/5152/22 127/67/61 82/5140/27 -f 91/5138/4296 78/56/54 13/58/56 -f 12/35/34 9/5155/18 90/34/33 -f 9/5155/18 78/56/54 90/34/33 -f 95/5156/4303 75/37/36 98/36/35 -f 4/10/10 90/34/33 91/5138/4296 -f 93/5157/40 73/5158/6 6/5159/5 -f 75/37/36 1/14/14 101/13/13 -f 12/35/34 4/10/10 112/39/38 -f 75/5160/36 62/44/42 1/5161/14 -f 6/5162/5 76/5163/4304 93/42/40 -f 93/42/40 76/5163/4304 77/40/39 -f 93/5157/40 94/7/7 79/9/9 -f 62/44/42 3/1/1 81/31/30 -f 102/47/45 2/5164/4298 92/45/43 -f 107/5165/4305 101/13/13 108/5166/4306 -f 2/5164/4298 16/5167/4307 92/45/43 -f 107/5165/4305 95/5156/4303 98/36/35 -f 97/5168/4308 8/5169/4297 7/5170/2 -f 100/55/53 2/5171/4298 8/5169/4297 -f 97/5168/4308 104/5172/4309 100/55/53 -f 67/49/47 106/5173/11 114/50/48 -f 111/48/46 103/5128/4292 67/49/47 -f 17/15/15 108/5166/4306 101/13/13 -f 103/5128/4292 110/5174/4310 17/5129/15 -f 102/47/45 96/5175/4311 74/5176/4294 -f 7/5170/2 104/5172/4309 97/5168/4308 -f 110/5177/4310 77/5178/39 108/5166/4306 -f 3/5179/1 95/5180/4303 104/5172/4309 -f 5/51/49 110/5174/4310 111/48/46 -f 114/50/48 4/5181/10 11/53/51 -f 76/5182/4304 105/5183/4291 107/5165/4305 -f 10/52/50 111/48/46 66/54/52 -f 77/5178/39 107/5165/4305 108/5166/4306 -f 109/5184/37 100/55/53 70/16/16 -f 99/46/44 112/39/38 113/12/12 -f 16/5167/4307 109/38/37 92/45/43 -f 100/55/53 104/5172/4309 115/4/4 -f 105/5125/4291 104/5172/4309 95/5180/4303 -f 102/47/45 106/11/11 96/5175/4311 -f 127/67/61 122/57/55 123/59/57 -f 72/5185/4312 13/58/56 122/57/55 -f 117/5153/4301 122/57/55 89/5154/4302 -f 126/65/60 120/61/59 121/60/58 -f 119/5144/4299 120/61/59 85/5145/4300 -f 79/9/9 120/61/59 73/5158/6 -f 124/27/26 73/6/6 119/5149/4299 -f 125/24/24 72/5186/4312 117/5147/4301 -f 116/26/25 65/17/17 124/27/26 -f 66/54/52 11/53/51 72/5186/4312 -f 118/64/23 69/8/8 10/62/50 -f 118/23/23 66/54/52 125/24/24 -f 79/9/9 69/8/8 121/60/58 -f 116/66/25 78/56/54 9/5155/18 -f 132/5187/108 143/110/100 19/5188/115 -f 24/5189/118 162/71/65 20/5190/117 -f 184/68/62 174/5191/4313 21/69/63 -f 162/71/65 146/5192/4314 20/5190/117 -f 128/75/69 31/5193/72 18/5194/71 -f 142/5195/103 171/82/76 130/81/75 -f 180/134/121 144/5196/4315 175/135/122 -f 175/5197/122 160/5198/4316 25/5199/119 -f 136/80/74 131/76/70 130/81/75 -f 160/5200/4316 148/5201/4317 25/5202/119 -f 27/5203/96 138/83/77 23/85/79 -f 196/101/93 185/141/86 150/5204/88 -f 188/5205/4318 152/97/90 26/5206/4319 -f 134/84/78 184/68/62 22/70/64 -f 28/5207/4320 133/5208/110 129/5209/104 -f 163/111/101 132/5187/108 133/5208/110 -f 195/96/89 187/139/84 29/5210/81 -f 194/91/85 186/5211/4321 139/5212/82 -f 194/91/85 139/5212/82 29/92/81 -f 193/94/87 188/5213/4318 26/5214/4319 -f 193/94/87 26/5214/4319 150/95/88 -f 155/86/80 150/5215/88 154/99/92 -f 195/96/89 29/5210/81 152/97/90 -f 150/5215/88 26/5216/4319 154/99/92 -f 26/5216/4319 152/100/90 154/99/92 -f 130/5217/75 156/5218/4322 157/5219/4323 -f 155/86/80 152/100/90 29/87/81 -f 156/5218/4322 163/111/101 133/5208/110 -f 28/5207/4320 156/5218/4322 133/5208/110 -f 129/5209/104 157/5219/4323 28/5207/4320 -f 142/5220/103 130/5217/75 157/5219/4323 -f 186/5221/4321 151/102/83 139/5222/82 -f 144/5223/4315 159/5224/4324 160/5198/4316 -f 196/101/93 150/5204/88 151/102/83 -f 27/5225/96 23/5226/79 159/5224/4324 -f 23/5227/79 147/5228/4325 159/5229/4324 -f 148/5201/4317 159/5229/4324 147/5228/4325 -f 167/79/73 143/5230/100 18/77/71 -f 19/5231/115 143/5230/100 166/117/105 -f 144/5196/4315 179/106/97 27/105/96 -f 161/5232/99 22/5233/64 21/5234/63 -f 146/5192/4314 161/5232/99 145/5235/98 -f 149/73/67 161/5232/99 162/71/65 -f 183/119/107 19/5231/115 166/117/105 -f 18/5236/71 143/110/100 128/112/69 -f 168/114/102 169/136/123 142/115/103 -f 30/5237/112 168/114/102 129/116/104 -f 166/117/105 167/79/73 181/118/106 -f 164/5238/4326 182/127/114 171/82/76 -f 133/122/110 165/121/109 170/124/111 -f 165/121/109 173/129/116 170/124/111 -f 129/123/104 133/122/110 170/124/111 -f 178/126/113 172/74/68 136/80/74 -f 31/78/72 181/118/106 167/79/73 -f 172/74/68 177/5239/4327 31/5193/72 -f 169/136/123 164/5240/4326 142/115/103 -f 177/5241/4327 146/5192/4314 181/118/106 -f 132/120/108 173/129/116 165/121/109 -f 20/130/117 177/5239/4327 178/126/113 -f 145/5235/98 174/5242/4313 183/119/107 -f 175/5243/122 25/132/119 182/127/114 -f 24/131/118 178/126/113 135/133/120 -f 181/118/106 145/5235/98 183/119/107 -f 180/134/121 168/114/102 179/106/97 -f 176/5244/95 170/124/111 138/83/77 -f 30/5237/112 176/104/95 168/114/102 -f 170/124/111 173/129/116 184/68/62 -f 174/5191/4313 173/129/116 19/128/115 -f 169/136/123 175/135/122 164/5240/4326 -f 191/5245/4328 147/5228/4325 192/140/126 -f 158/103/94 192/140/126 196/101/93 -f 140/5246/4329 148/5201/4317 191/5245/4328 -f 186/5221/4321 191/5245/4328 158/103/94 -f 195/96/89 189/138/125 190/137/124 -f 141/5247/4330 149/73/67 189/138/125 -f 188/5205/4318 189/138/125 153/98/91 -f 193/94/87 141/5248/4330 188/5213/4318 -f 134/84/78 22/70/64 141/5248/4330 -f 194/91/85 140/5249/4329 186/5211/4321 -f 185/93/86 134/84/78 193/94/87 -f 135/133/120 25/132/119 140/5249/4329 -f 187/139/84 137/72/66 24/5189/118 -f 187/90/84 135/133/120 194/91/85 -f 149/73/67 137/72/66 190/137/124 -f 185/141/86 147/5228/4325 23/5227/79 -f 38/5250/186 229/145/130 34/5251/185 -f 251/142/127 241/5252/4331 35/143/128 -f 229/5253/130 214/5254/4332 34/5255/185 -f 197/149/134 46/5256/140 32/5257/139 -f 211/5258/174 238/159/144 199/158/143 -f 40/5259/170 207/160/145 37/162/147 -f 205/157/142 200/150/135 199/158/143 -f 227/183/165 41/212/191 39/5260/187 -f 227/5261/165 39/5262/187 33/5263/137 -f 263/178/161 252/217/154 217/5264/156 -f 219/174/158 255/5265/4333 220/175/159 -f 203/161/146 251/142/127 36/144/129 -f 42/5266/4334 202/5267/180 198/5268/175 -f 262/173/157 254/216/152 44/5269/149 -f 261/168/153 253/5270/4335 208/5271/150 -f 261/168/153 208/5271/150 44/169/149 -f 260/171/155 255/5272/4333 221/5273/160 -f 260/171/155 221/5273/160 217/172/156 -f 222/163/148 217/5274/156 221/176/160 -f 262/173/157 44/5269/149 219/174/158 -f 199/5275/143 223/5276/4336 224/5277/4337 -f 222/163/148 219/177/158 44/164/149 -f 223/5276/4336 43/5278/178 202/5267/180 -f 42/5266/4334 223/5276/4336 202/5267/180 -f 198/5268/175 224/5277/4337 42/5266/4334 -f 211/5279/174 199/5275/143 224/5277/4337 -f 253/5280/4335 218/179/151 208/5281/150 -f 227/183/165 40/188/170 226/181/163 -f 263/178/161 217/5264/156 218/179/151 -f 40/5282/170 37/5283/147 226/5284/163 -f 37/5285/147 215/182/164 226/181/163 -f 234/156/141 212/184/166 32/154/139 -f 227/183/165 215/182/164 41/212/191 -f 33/152/137 247/189/171 40/188/170 -f 228/5286/4338 36/5287/129 35/5288/128 -f 214/5254/4332 228/5289/4338 213/5290/4339 -f 35/5291/128 213/5290/4339 228/5289/4338 -f 216/147/132 228/5286/4338 229/145/130 -f 43/5278/178 197/192/134 201/191/172 -f 32/5292/139 212/190/166 197/192/134 -f 249/197/177 230/186/168 233/185/167 -f 235/193/173 236/210/189 211/194/174 -f 45/5293/182 235/193/173 198/195/175 -f 233/185/167 234/156/141 243/196/176 -f 238/159/144 242/5294/138 248/205/184 -f 202/200/180 232/199/179 237/202/181 -f 232/199/179 240/5295/4340 237/202/181 -f 198/201/175 202/200/180 237/202/181 -f 246/204/183 239/148/133 205/157/142 -f 46/155/140 243/196/176 234/156/141 -f 239/148/133 245/5296/4341 46/5256/140 -f 236/210/189 231/5297/4342 211/194/174 -f 43/198/178 240/5295/4340 232/199/179 -f 240/5295/4340 212/5298/166 230/5299/168 -f 245/5300/4341 214/5301/4332 243/196/176 -f 34/206/185 245/5296/4341 246/204/183 -f 248/205/184 33/5302/137 39/208/187 -f 213/5303/4339 241/5304/4331 249/197/177 -f 38/207/186 246/204/183 204/209/188 -f 243/196/176 213/5303/4339 249/197/177 -f 244/5305/169 237/202/181 207/160/145 -f 236/210/189 247/189/171 250/151/136 -f 45/5293/182 244/187/169 235/193/173 -f 237/202/181 240/5295/4340 251/142/127 -f 241/5252/4331 240/5295/4340 230/5299/168 -f 236/210/189 242/153/138 231/5297/4342 -f 225/180/162 259/213/192 263/178/161 -f 209/5306/4343 41/212/191 258/211/190 -f 253/5280/4335 258/211/190 225/180/162 -f 262/173/157 256/215/194 257/214/193 -f 210/5307/4344 216/147/132 256/215/194 -f 255/5265/4333 256/215/194 220/175/159 -f 260/171/155 210/5308/4344 255/5272/4333 -f 203/161/146 36/144/129 210/5308/4344 -f 261/168/153 209/5309/4343 253/5270/4335 -f 252/170/154 203/161/146 260/171/155 -f 204/209/188 39/208/187 209/5309/4343 -f 254/216/152 206/146/131 38/5250/186 -f 254/167/152 204/209/188 261/168/153 -f 216/147/132 206/146/131 257/214/193 -f 252/217/154 215/182/164 37/5285/147 -f 53/5310/186 297/221/195 49/5311/185 -f 318/218/127 309/5312/4345 50/219/128 -f 297/5313/195 281/5314/4346 49/5315/185 -f 264/225/134 61/5316/140 47/5317/139 -f 278/5318/204 306/235/144 266/234/198 -f 55/5319/170 274/236/145 52/238/147 -f 272/233/142 267/226/135 266/234/198 -f 294/259/165 56/286/191 54/5320/187 -f 294/5321/165 54/5322/187 48/5323/137 -f 330/254/200 319/291/154 284/5324/156 -f 286/250/158 322/5325/4333 287/251/159 -f 270/237/146 318/218/127 51/220/129 -f 57/5326/4334 269/5327/180 265/5328/205 -f 329/249/157 321/290/152 59/5329/149 -f 328/244/153 320/5330/4335 275/5331/199 -f 328/244/153 275/5331/199 59/245/149 -f 327/247/155 322/5332/4333 288/5333/160 -f 327/247/155 288/5333/160 284/248/156 -f 289/239/148 284/5334/156 288/252/160 -f 329/249/157 59/5329/149 286/250/158 -f 266/5335/198 290/5336/4336 291/5337/4337 -f 289/239/148 286/253/158 59/240/149 -f 290/5336/4336 58/5338/178 269/5327/180 -f 57/5326/4334 290/5336/4336 269/5327/180 -f 265/5328/205 291/5337/4337 57/5326/4334 -f 278/5339/204 266/5335/198 291/5337/4337 -f 320/5340/4335 285/255/151 275/5341/199 -f 294/259/165 55/264/170 293/257/163 -f 330/254/200 284/5324/156 285/255/151 -f 55/5342/170 52/5343/147 293/5344/163 -f 52/5345/147 282/258/164 293/257/163 -f 302/232/197 279/260/166 47/230/139 -f 294/259/165 282/258/164 56/286/191 -f 48/228/137 315/265/202 55/264/170 -f 296/5346/4338 51/5347/129 50/5348/128 -f 281/5314/4346 296/5349/4338 280/5350/4347 -f 50/5351/128 280/5350/4347 296/5349/4338 -f 283/223/132 296/5346/4338 297/221/195 -f 58/5338/178 264/268/134 268/267/172 -f 47/5352/139 279/266/166 264/268/134 -f 295/5353/4348 301/261/201 302/232/197 -f 303/269/203 304/284/207 278/270/204 -f 60/5354/206 303/269/203 265/271/205 -f 311/5355/4349 298/262/168 301/261/201 -f 306/235/144 310/5356/138 316/279/184 -f 269/274/180 300/273/179 305/276/181 -f 300/273/179 308/5357/4340 305/276/181 -f 265/275/205 269/274/180 305/276/181 -f 314/278/183 307/224/133 272/233/142 -f 61/231/140 295/5353/4348 302/232/197 -f 307/224/133 313/5358/4350 61/5316/140 -f 304/284/207 299/5359/4351 278/270/204 -f 58/272/178 308/5357/4340 300/273/179 -f 308/5357/4340 279/5360/166 298/5361/168 -f 313/5362/4350 281/5363/4346 295/5353/4348 -f 49/280/185 313/5358/4350 314/278/183 -f 316/279/184 48/5364/137 54/282/187 -f 280/5365/4347 309/5366/4345 311/5355/4349 -f 53/281/186 314/278/183 271/283/188 -f 295/5353/4348 280/5365/4347 311/5355/4349 -f 317/227/196 303/269/203 315/265/202 -f 312/5367/169 305/276/181 274/236/145 -f 60/5354/206 312/263/169 303/269/203 -f 305/276/181 308/5357/4340 318/218/127 -f 309/5312/4345 308/5357/4340 298/5361/168 -f 304/284/207 310/229/138 299/5359/4351 -f 292/256/162 326/287/192 330/254/200 -f 276/5368/4343 56/286/191 325/285/190 -f 320/5340/4335 325/285/190 292/256/162 -f 329/249/157 323/289/194 324/288/193 -f 277/5369/4344 283/223/132 323/289/194 -f 322/5325/4333 323/289/194 287/251/159 -f 327/247/155 277/5370/4344 322/5332/4333 -f 270/237/146 51/220/129 277/5370/4344 -f 328/244/153 276/5371/4343 320/5330/4335 -f 319/246/154 270/237/146 327/247/155 -f 271/283/188 54/282/187 276/5371/4343 -f 321/290/152 273/222/131 53/5310/186 -f 321/243/152 271/283/188 328/244/153 -f 283/223/132 273/222/131 324/288/193 -f 319/291/154 282/258/164 52/5345/147 -f 411/5372/4352 331/5373/4353 379/5374/4354 -f 379/5374/4354 332/5375/4355 380/5376/4356 -f 380/5376/4356 333/5377/4357 381/5378/4358 -f 381/5378/4358 334/294/210 382/293/209 -f 382/293/209 336/297/213 383/296/212 -f 383/296/212 338/300/216 384/299/215 -f 384/299/215 340/303/219 385/302/218 -f 385/302/218 342/305/221 386/304/220 -f 343/306/222 387/307/223 386/304/220 -f 345/309/225 388/310/226 387/307/223 -f 347/312/228 389/313/229 388/310/226 -f 349/315/231 390/316/232 389/313/229 -f 351/318/234 391/5379/4359 390/316/232 -f 352/5380/4360 392/5381/4361 391/5379/4359 -f 353/5382/4362 393/5383/4363 392/5381/4361 -f 393/5383/4363 355/5384/4364 394/5385/4365 -f 394/5385/4365 356/5386/4366 395/333/249 -f 395/333/249 357/5387/4367 396/331/247 -f 396/331/247 358/321/237 397/320/236 -f 398/5388/4368 362/5389/4369 399/327/243 -f 361/5390/4370 398/5388/4368 360/5391/4371 -f 399/327/243 363/5392/4372 400/325/241 -f 400/325/241 364/5393/4373 401/5394/4374 -f 401/5394/4374 365/5395/4375 402/338/254 -f 402/338/254 366/5396/4376 403/5397/4377 -f 366/5396/4376 404/340/256 403/5397/4377 -f 367/5398/4378 405/5399/4379 404/340/256 -f 368/5400/4380 406/5401/4381 405/5399/4379 -f 369/5402/4382 407/329/245 406/5401/4381 -f 370/5403/4383 408/330/246 407/329/245 -f 376/324/240 410/342/258 409/322/238 -f 372/5404/4384 408/330/246 371/5405/4385 -f 377/5406/4386 411/5372/4352 410/342/258 -f 412/5407/4387 382/293/209 413/5408/4388 -f 381/5378/4358 412/5407/4387 380/5376/4356 -f 414/5409/4389 383/296/212 415/5410/4390 -f 382/293/209 414/5409/4389 413/5408/4388 -f 416/5411/4391 384/299/215 417/5412/4392 -f 383/296/212 416/5411/4391 415/5410/4390 -f 418/5413/4393 385/302/218 419/5414/4394 -f 384/299/215 418/5413/4393 417/5412/4392 -f 420/5415/4395 386/304/220 421/5416/4396 -f 385/302/218 420/5415/4395 419/5414/4394 -f 386/304/220 423/5417/4397 422/5418/4398 -f 422/5418/4398 421/5416/4396 386/304/220 -f 387/307/223 425/5419/4399 424/5420/4400 -f 424/5420/4400 423/5417/4397 387/307/223 -f 388/310/226 427/5421/4401 426/5422/4402 -f 426/5422/4402 425/5419/4399 388/310/226 -f 389/313/229 429/5423/4403 428/5424/4404 -f 428/5424/4404 427/5421/4401 389/313/229 -f 390/316/232 431/5425/4405 430/5426/4406 -f 430/5426/4406 429/5423/4403 390/316/232 -f 432/334/250 360/5391/4371 433/5427/4407 -f 392/5381/4361 431/5425/4405 391/5379/4359 -f 359/319/235 432/334/250 397/320/236 -f 433/5427/4407 398/5388/4368 434/5428/4408 -f 434/5428/4408 399/327/243 435/326/242 -f 408/330/246 438/5429/4409 437/328/244 -f 403/5397/4377 436/336/252 402/338/254 -f 404/340/256 436/336/252 403/5397/4377 -f 372/5404/4384 439/5430/4410 438/5429/4409 -f 373/5431/4411 440/5432/4412 439/5430/4410 -f 374/5433/4413 441/5434/4414 440/5432/4412 -f 477/5435/4415 379/5374/4354 442/5436/4416 -f 409/322/238 441/5434/4414 375/323/239 -f 442/5436/4416 380/5376/4356 443/5437/4417 -f 443/5437/4417 412/5407/4387 444/5438/4418 -f 444/5438/4418 413/5408/4388 445/5439/4419 -f 445/5439/4419 414/5409/4389 446/5440/4420 -f 446/5440/4420 415/5410/4390 447/5441/4421 -f 447/5441/4421 416/5411/4391 448/5442/4422 -f 448/5442/4422 417/5412/4392 449/5443/4423 -f 449/5443/4423 418/5413/4393 450/5444/4424 -f 450/5444/4424 419/5414/4394 451/5445/4425 -f 451/5445/4425 420/5415/4395 452/5446/4426 -f 452/5446/4426 421/5416/4396 453/5447/4427 -f 421/5416/4396 454/5448/4428 453/5447/4427 -f 422/5418/4398 455/5449/4429 454/5448/4428 -f 423/5417/4397 456/5450/4430 455/5449/4429 -f 424/5420/4400 457/5451/4431 456/5450/4430 -f 425/5419/4399 458/5452/4432 457/5451/4431 -f 426/5422/4402 459/5453/4433 458/5452/4432 -f 427/5421/4401 460/5454/4434 459/5453/4433 -f 428/5424/4404 461/5455/4435 460/5454/4434 -f 429/5423/4403 462/5456/4436 461/5455/4435 -f 430/5426/4406 463/5457/4437 462/5456/4436 -f 431/5425/4405 464/5458/4438 463/5457/4437 -f 392/5381/4361 465/5459/4439 464/5458/4438 -f 465/5459/4439 394/5385/4365 466/5460/4440 -f 466/5460/4440 395/333/249 467/332/248 -f 469/5461/4441 401/5394/4374 470/5462/4442 -f 397/320/236 468/335/251 396/331/247 -f 400/325/241 469/5461/4441 435/326/242 -f 470/5462/4442 402/338/254 471/337/253 -f 404/340/256 473/5463/4443 472/339/255 -f 405/5399/4379 474/5464/4444 473/5463/4443 -f 406/5401/4381 475/5465/4445 474/5464/4444 -f 410/342/258 477/5435/4415 476/341/257 -f 437/328/244 475/5465/4445 407/329/245 -f 525/5466/4446 442/5436/4416 478/5467/4447 -f 478/5467/4447 443/5437/4417 479/5468/4448 -f 479/5468/4448 444/5438/4418 480/5469/4449 -f 480/5469/4449 445/5439/4419 481/5470/4450 -f 481/5470/4450 446/5440/4420 482/5471/4451 -f 482/5471/4451 447/5441/4421 483/5472/4452 -f 483/5472/4452 448/5442/4422 484/5473/4453 -f 484/5473/4453 449/5443/4423 485/5474/4454 -f 485/5474/4454 450/5444/4424 486/5475/4455 -f 486/5475/4455 451/5445/4425 487/5476/4456 -f 487/5476/4456 452/5446/4426 488/5477/4457 -f 488/5477/4457 453/5447/4427 489/5478/4458 -f 489/5478/4458 454/5448/4428 490/5479/4459 -f 454/5448/4428 491/5480/4460 490/5479/4459 -f 455/5449/4429 492/5481/4461 491/5480/4460 -f 456/5450/4430 493/5482/4462 492/5481/4461 -f 457/5451/4431 494/5483/4463 493/5482/4462 -f 458/5452/4432 495/5484/4464 494/5483/4463 -f 459/5453/4433 496/5485/4465 495/5484/4464 -f 460/5454/4434 497/5486/4466 496/5485/4465 -f 461/5455/4435 498/5487/4467 497/5486/4466 -f 462/5456/4436 499/5488/4468 498/5487/4467 -f 463/5457/4437 500/5489/4469 499/5488/4468 -f 464/5458/4438 501/5490/4470 500/5489/4469 -f 501/5490/4470 466/5460/4440 502/5491/4471 -f 502/5491/4471 467/332/248 503/5492/4472 -f 503/5492/4472 396/331/247 504/5493/4473 -f 504/5493/4473 468/335/251 505/5494/4474 -f 505/5494/4474 432/334/250 506/5495/4475 -f 506/5495/4475 433/5427/4407 507/5496/4476 -f 507/5496/4476 434/5428/4408 508/5497/4477 -f 508/5497/4477 435/326/242 509/5498/4478 -f 509/5498/4478 469/5461/4441 510/5499/4479 -f 510/5499/4479 470/5462/4442 511/5500/4480 -f 511/5500/4480 471/337/253 512/5501/4481 -f 512/5501/4481 436/336/252 513/5502/4482 -f 436/336/252 514/5503/4483 513/5502/4482 -f 472/339/255 515/5504/4484 514/5503/4483 -f 473/5463/4443 516/5505/4485 515/5504/4484 -f 474/5464/4444 517/5506/4486 516/5505/4485 -f 475/5465/4445 518/5507/4487 517/5506/4486 -f 437/328/244 519/5508/4488 518/5507/4487 -f 438/5429/4409 520/5509/4489 519/5508/4488 -f 439/5430/4410 521/5510/4490 520/5509/4489 -f 440/5432/4412 522/5511/4491 521/5510/4490 -f 441/5434/4414 523/5512/4492 522/5511/4491 -f 409/322/238 524/5513/4493 523/5512/4492 -f 476/341/257 525/5466/4446 524/5513/4493 -f 573/5514/4494 478/5467/4447 526/5515/4495 -f 526/5515/4495 479/5468/4448 527/5516/4496 -f 527/5516/4496 480/5469/4449 528/5517/4497 -f 528/5517/4497 481/5470/4450 529/5518/4498 -f 529/5518/4498 482/5471/4451 530/5519/4499 -f 530/5519/4499 483/5472/4452 531/5520/4500 -f 531/5520/4500 484/5473/4453 532/5521/4501 -f 532/5521/4501 485/5474/4454 533/5522/4502 -f 533/5522/4502 486/5475/4455 534/5523/4503 -f 534/5524/4503 487/5525/4456 535/5526/4504 -f 535/5526/4504 488/5527/4457 536/5528/4505 -f 536/5528/4505 489/5529/4458 537/5530/4506 -f 537/5530/4506 490/5531/4459 538/5532/4507 -f 490/5531/4459 539/5533/4508 538/5532/4507 -f 491/5534/4460 540/5535/4509 539/5533/4508 -f 492/5536/4461 541/5537/4510 540/5535/4509 -f 493/5482/4462 542/5538/4511 541/5539/4510 -f 494/5483/4463 543/5540/4512 542/5538/4511 -f 495/5484/4464 544/5541/4513 543/5540/4512 -f 496/5485/4465 545/5542/4514 544/5541/4513 -f 497/5486/4466 546/5543/4515 545/5542/4514 -f 498/5487/4467 547/5544/4516 546/5543/4515 -f 499/5488/4468 548/5545/4517 547/5544/4516 -f 500/5489/4469 549/5546/4518 548/5545/4517 -f 549/5546/4518 502/5491/4471 550/5547/4519 -f 550/5547/4519 503/5492/4472 551/5548/4520 -f 551/5548/4520 504/5493/4473 552/5549/4521 -f 552/5549/4521 505/5494/4474 553/5550/4522 -f 553/5550/4522 506/5495/4475 554/5551/4523 -f 554/5551/4523 507/5496/4476 555/5552/4524 -f 555/5552/4524 508/5497/4477 556/5553/4525 -f 556/5553/4525 509/5498/4478 557/5554/4526 -f 557/5554/4526 510/5499/4479 558/5555/4527 -f 558/5555/4527 511/5500/4480 559/5556/4528 -f 559/5556/4528 512/5501/4481 560/5557/4529 -f 560/5557/4529 513/5502/4482 561/5558/4530 -f 513/5502/4482 562/5559/4531 561/5558/4530 -f 514/5503/4483 563/5560/4532 562/5559/4531 -f 515/5504/4484 564/5561/4533 563/5560/4532 -f 516/5505/4485 565/5562/4534 564/5561/4533 -f 517/5506/4486 566/5563/4535 565/5562/4534 -f 518/5507/4487 567/5564/4536 566/5563/4535 -f 519/5508/4488 568/5565/4537 567/5564/4536 -f 520/5509/4489 569/5566/4538 568/5565/4537 -f 521/5510/4490 570/5567/4539 569/5566/4538 -f 522/5511/4491 571/5568/4540 570/5567/4539 -f 523/5512/4492 572/5569/4541 571/5568/4540 -f 524/5513/4493 573/5514/4494 572/5569/4541 -f 621/5570/4542 526/5515/4495 574/5571/4543 -f 574/5571/4543 527/5516/4496 575/5572/4544 -f 575/5572/4544 528/5517/4497 576/5573/4545 -f 576/5573/4545 529/5518/4498 577/5574/4546 -f 577/5574/4546 530/5519/4499 578/5575/4547 -f 578/5575/4547 531/5520/4500 579/5576/4548 -f 579/5577/4548 532/5578/4501 580/5579/4549 -f 580/5579/4549 533/5580/4502 581/5581/4550 -f 581/5581/4550 534/5524/4503 582/5582/4551 -f 582/5582/4551 535/5526/4504 583/5583/4552 -f 583/5583/4552 536/5528/4505 584/5584/4553 -f 584/5584/4553 537/5530/4506 585/5585/4554 -f 585/5585/4554 538/5532/4507 586/5586/4555 -f 538/5532/4507 587/5587/4556 586/5586/4555 -f 539/5533/4508 588/5588/4557 587/5587/4556 -f 540/5535/4509 589/5589/4558 588/5588/4557 -f 541/5537/4510 590/5590/4559 589/5589/4558 -f 542/5591/4511 591/5592/4560 590/5590/4559 -f 543/5593/4512 592/5594/4561 591/5592/4560 -f 544/5541/4513 593/5595/4562 592/5596/4561 -f 545/5542/4514 594/5597/4563 593/5595/4562 -f 546/5543/4515 595/5598/4564 594/5597/4563 -f 547/5544/4516 596/5599/4565 595/5598/4564 -f 548/5545/4517 597/5600/4566 596/5599/4565 -f 597/5600/4566 550/5547/4519 598/5601/4567 -f 598/5601/4567 551/5548/4520 599/5602/4568 -f 599/5602/4568 552/5549/4521 600/5603/4569 -f 600/5603/4569 553/5550/4522 601/5604/4570 -f 601/5604/4570 554/5551/4523 602/5605/4571 -f 602/5605/4571 555/5552/4524 603/5606/4572 -f 603/5607/4572 556/5608/4525 604/5609/4573 -f 604/5609/4573 557/5610/4526 605/5611/4574 -f 605/5611/4574 558/5612/4527 606/5613/4575 -f 606/5613/4575 559/5614/4528 607/5615/4576 -f 607/5615/4576 560/5616/4529 608/5617/4577 -f 608/5617/4577 561/5618/4530 609/5619/4578 -f 561/5618/4530 610/5620/4579 609/5619/4578 -f 562/5621/4531 611/5622/4580 610/5620/4579 -f 563/5623/4532 612/5624/4581 611/5625/4580 -f 564/5626/4533 613/5627/4582 612/5624/4581 -f 565/5628/4534 614/5629/4583 613/5627/4582 -f 566/5563/4535 615/5630/4584 614/5631/4583 -f 567/5564/4536 616/5632/4585 615/5630/4584 -f 568/5565/4537 617/5633/4586 616/5632/4585 -f 569/5566/4538 618/5634/4587 617/5633/4586 -f 570/5567/4539 619/5635/4588 618/5634/4587 -f 571/5568/4540 620/5636/4589 619/5635/4588 -f 572/5569/4541 621/5570/4542 620/5636/4589 -f 669/5637/4590 574/5571/4543 622/5638/4591 -f 622/5638/4591 575/5572/4544 623/5639/4592 -f 623/5639/4592 576/5573/4545 624/5640/4593 -f 624/5640/4593 577/5574/4546 625/5641/4594 -f 625/5641/4594 578/5575/4547 626/5642/4595 -f 626/5643/4595 579/5577/4548 627/5644/4596 -f 627/5644/4596 580/5579/4549 628/5645/4597 -f 628/5645/4597 581/5581/4550 629/5646/4598 -f 629/5646/4598 582/5582/4551 630/5647/4599 -f 630/5647/4599 583/5583/4552 631/5648/4600 -f 631/5648/4600 584/5584/4553 632/5649/4601 -f 632/5649/4601 585/5585/4554 633/5650/4602 -f 633/5650/4602 586/5586/4555 634/5651/4603 -f 586/5586/4555 635/5652/4604 634/5651/4603 -f 587/5587/4556 636/5653/4605 635/5652/4604 -f 588/5588/4557 637/5654/4606 636/5653/4605 -f 589/5589/4558 638/5655/4607 637/5654/4606 -f 590/5590/4559 639/5656/4608 638/5655/4607 -f 591/5592/4560 640/5657/4609 639/5656/4608 -f 592/5594/4561 641/5658/4610 640/5657/4609 -f 593/5595/4562 642/5659/4611 641/5660/4610 -f 594/5597/4563 643/5661/4612 642/5659/4611 -f 595/5598/4564 644/5662/4613 643/5661/4612 -f 596/5599/4565 645/5663/4614 644/5662/4613 -f 645/5663/4614 598/5601/4567 646/5664/4615 -f 646/5664/4615 599/5602/4568 647/5665/4616 -f 647/5665/4616 600/5603/4569 648/5666/4617 -f 648/5666/4617 601/5604/4570 649/5667/4618 -f 649/5667/4618 602/5605/4571 650/5668/4619 -f 650/5669/4619 603/5607/4572 651/5670/4620 -f 651/5670/4620 604/5609/4573 652/5671/4621 -f 652/5671/4621 605/5611/4574 653/5672/4622 -f 653/5672/4622 606/5613/4575 654/5673/4623 -f 654/5673/4623 607/5615/4576 655/5674/4624 -f 655/5674/4624 608/5617/4577 656/5675/4625 -f 656/5675/4625 609/5619/4578 657/5676/4626 -f 609/5619/4578 658/5677/4627 657/5676/4626 -f 610/5620/4579 659/5678/4628 658/5677/4627 -f 611/5625/4580 660/5679/4629 659/5680/4628 -f 612/5624/4581 661/5681/4630 660/5679/4629 -f 613/5627/4582 662/5682/4631 661/5681/4630 -f 614/5629/4583 663/5683/4632 662/5682/4631 -f 615/5684/4584 664/5685/4633 663/5683/4632 -f 616/5632/4585 665/5686/4634 664/5687/4633 -f 617/5633/4586 666/5688/4635 665/5686/4634 -f 618/5634/4587 667/5689/4636 666/5688/4635 -f 619/5635/4588 668/5690/4637 667/5689/4636 -f 620/5636/4589 669/5637/4590 668/5690/4637 -f 717/5691/4638 622/5638/4591 670/5692/4639 -f 670/5692/4639 623/5639/4592 671/5693/4640 -f 671/5693/4640 624/5640/4593 672/5694/4641 -f 672/5694/4641 625/5641/4594 673/5695/4642 -f 673/5696/4642 626/5643/4595 674/5697/4643 -f 674/5697/4643 627/5644/4596 675/5698/4644 -f 675/5698/4644 628/5645/4597 676/5699/4645 -f 676/5699/4645 629/5646/4598 677/5700/4646 -f 677/5700/4646 630/5647/4599 678/5701/4647 -f 678/5701/4647 631/5648/4600 679/5702/4648 -f 679/5702/4648 632/5649/4601 680/5703/4649 -f 680/5703/4649 633/5650/4602 681/5704/4650 -f 681/5704/4650 634/5651/4603 682/5705/4651 -f 634/5651/4603 683/5706/4652 682/5705/4651 -f 635/5652/4604 684/5707/4653 683/5706/4652 -f 636/5653/4605 685/5708/4654 684/5707/4653 -f 637/5654/4606 686/5709/4655 685/5708/4654 -f 638/5655/4607 687/5710/4656 686/5709/4655 -f 639/5656/4608 688/5711/4657 687/5710/4656 -f 640/5657/4609 689/5712/4658 688/5711/4657 -f 641/5658/4610 690/5713/4659 689/5712/4658 -f 642/5659/4611 691/5714/4660 690/5715/4659 -f 643/5661/4612 692/5716/4661 691/5714/4660 -f 644/5662/4613 693/5717/4662 692/5716/4661 -f 693/5717/4662 646/5664/4615 694/5718/4663 -f 694/5718/4663 647/5665/4616 695/5719/4664 -f 695/5719/4664 648/5666/4617 696/5720/4665 -f 696/5721/4665 649/5722/4618 697/5723/4666 -f 697/5723/4666 650/5669/4619 698/5724/4667 -f 698/5724/4667 651/5670/4620 699/5725/4668 -f 699/5725/4668 652/5671/4621 700/5726/4669 -f 700/5726/4669 653/5672/4622 701/5727/4670 -f 701/5727/4670 654/5673/4623 702/5728/4671 -f 702/5728/4671 655/5674/4624 703/5729/4672 -f 703/5729/4672 656/5675/4625 704/5730/4673 -f 704/5730/4673 657/5676/4626 705/5731/4674 -f 657/5676/4626 706/5732/4675 705/5731/4674 -f 658/5677/4627 707/5733/4676 706/5732/4675 -f 659/5680/4628 708/5734/4677 707/5735/4676 -f 660/5679/4629 709/5736/4678 708/5734/4677 -f 661/5681/4630 710/5737/4679 709/5736/4678 -f 662/5682/4631 711/5738/4680 710/5737/4679 -f 663/5683/4632 712/5739/4681 711/5738/4680 -f 664/5685/4633 713/5740/4682 712/5739/4681 -f 665/5741/4634 714/5742/4683 713/5740/4682 -f 666/5688/4635 715/5743/4684 714/5744/4683 -f 667/5689/4636 716/5745/4685 715/5743/4684 -f 668/5690/4637 717/5691/4638 716/5745/4685 -f 765/5746/4686 670/5692/4639 718/5747/4687 -f 718/5747/4687 671/5693/4640 719/5748/4688 -f 719/5748/4688 672/5694/4641 720/5749/4689 -f 720/5750/4689 673/5696/4642 721/5751/4690 -f 721/5751/4690 674/5697/4643 722/5752/4691 -f 722/5752/4691 675/5698/4644 723/345/261 -f 723/345/261 676/5699/4645 724/343/259 -f 724/343/259 677/5700/4646 725/5753/4692 -f 725/5753/4692 678/5701/4647 726/5754/4693 -f 726/5754/4693 679/5702/4648 727/5755/4694 -f 727/5755/4694 680/5703/4649 728/5756/4695 -f 728/5756/4695 681/5704/4650 729/5757/4696 -f 729/5757/4696 682/5705/4651 730/5758/4697 -f 682/5705/4651 731/5759/4698 730/5758/4697 -f 683/5706/4652 732/5760/4699 731/5759/4698 -f 684/5707/4653 733/5761/4700 732/5760/4699 -f 685/5708/4654 734/347/263 733/5761/4700 -f 686/5709/4655 735/348/264 734/347/263 -f 687/5710/4656 736/5762/4701 735/348/264 -f 688/5711/4657 737/5763/4702 736/5762/4701 -f 689/5712/4658 738/5764/4703 737/5763/4702 -f 690/5713/4659 739/5765/4704 738/5764/4703 -f 691/5714/4660 740/5766/4705 739/5767/4704 -f 692/5716/4661 741/5768/4706 740/5766/4705 -f 741/5768/4706 694/5718/4663 742/5769/4707 -f 742/5769/4707 695/5719/4664 743/5770/4708 -f 743/5770/4708 696/5720/4665 744/5771/4709 -f 744/5772/4709 697/5723/4666 745/5773/4710 -f 745/5773/4710 698/5724/4667 746/5774/4711 -f 746/5774/4711 699/5725/4668 747/5775/4712 -f 747/5775/4712 700/5726/4669 748/5776/4713 -f 748/5776/4713 701/5727/4670 749/351/267 -f 749/351/267 702/5728/4671 750/349/265 -f 750/349/265 703/5729/4672 751/5777/4714 -f 751/5777/4714 704/5730/4673 752/5778/4715 -f 752/5778/4715 705/5731/4674 753/5779/4716 -f 705/5731/4674 754/5780/4717 753/5779/4716 -f 706/5732/4675 755/5781/4718 754/5780/4717 -f 707/5735/4676 756/5782/4719 755/5783/4718 -f 708/5734/4677 757/353/269 756/5782/4719 -f 709/5736/4678 758/354/270 757/353/269 -f 710/5737/4679 759/5784/4720 758/354/270 -f 711/5738/4680 760/5785/4721 759/5784/4720 -f 712/5739/4681 761/5786/4722 760/5785/4721 -f 713/5740/4682 762/5787/4723 761/5786/4722 -f 714/5744/4683 763/5788/4724 762/5789/4723 -f 715/5743/4684 764/5790/4725 763/5788/4724 -f 716/5745/4685 765/5746/4686 764/5790/4725 -f 794/5791/4726 718/5747/4687 766/5792/4727 -f 766/5792/4727 719/5748/4688 767/5793/4728 -f 767/5793/4728 720/5749/4689 768/5794/4729 -f 768/5795/4729 721/5751/4690 769/5796/4730 -f 769/5796/4730 722/5752/4691 770/5797/4731 -f 770/5797/4731 723/345/261 771/344/260 -f 735/348/264 773/5798/4732 772/346/262 -f 736/5762/4701 774/5799/4733 773/5798/4732 -f 737/5763/4702 775/5800/4734 774/5799/4733 -f 738/5764/4703 776/5801/4735 775/5800/4734 -f 739/5767/4704 777/5802/4736 776/5803/4735 -f 740/5766/4705 778/5804/4737 777/5802/4736 -f 778/5804/4737 742/5769/4707 779/5805/4738 -f 779/5805/4738 743/5770/4708 780/5806/4739 -f 780/5807/4739 744/5772/4709 781/5808/4740 -f 781/5808/4740 745/5773/4710 782/5809/4741 -f 782/5809/4741 746/5774/4711 783/5810/4742 -f 783/5810/4742 747/5775/4712 784/5811/4743 -f 784/5811/4743 748/5776/4713 785/5812/4744 -f 785/5812/4744 749/351/267 786/350/266 -f 758/354/270 788/5813/4745 787/352/268 -f 759/5784/4720 789/5814/4746 788/5813/4745 -f 760/5785/4721 790/5815/4747 789/5814/4746 -f 761/5786/4722 791/5816/4748 790/5815/4747 -f 762/5787/4723 792/5817/4749 791/5816/4748 -f 763/5788/4724 793/5818/4750 792/5819/4749 -f 764/5790/4725 794/5791/4726 793/5818/4750 -f 842/5820/4751 766/5792/4727 795/5821/4752 -f 795/5821/4752 767/5793/4728 796/5822/4753 -f 796/5823/4753 768/5795/4729 797/5824/4754 -f 797/5824/4754 769/5796/4730 798/5825/4755 -f 798/5825/4755 770/5797/4731 799/5826/4756 -f 799/5826/4756 771/344/260 800/5827/4757 -f 800/5827/4757 724/343/259 801/5828/4758 -f 801/5828/4758 725/5753/4692 802/5829/4759 -f 802/5829/4759 726/5754/4693 803/5830/4760 -f 803/5830/4760 727/5755/4694 804/5831/4761 -f 804/5831/4761 728/5756/4695 805/5832/4762 -f 805/5832/4762 729/5757/4696 806/5833/4763 -f 806/5833/4763 730/5758/4697 807/5834/4764 -f 730/5758/4697 808/5835/4765 807/5834/4764 -f 731/5759/4698 809/5836/4766 808/5835/4765 -f 732/5760/4699 810/5837/4767 809/5836/4766 -f 733/5761/4700 811/5838/4768 810/5837/4767 -f 734/347/263 812/5839/4769 811/5838/4768 -f 772/346/262 813/5840/4770 812/5839/4769 -f 773/5798/4732 814/5841/4771 813/5840/4770 -f 774/5799/4733 815/5842/4772 814/5841/4771 -f 775/5800/4734 816/5843/4773 815/5842/4772 -f 776/5801/4735 817/5844/4774 816/5843/4773 -f 777/5845/4736 818/5846/4775 817/5847/4774 -f 818/5846/4775 779/5848/4738 819/5849/4776 -f 819/5849/4776 780/5850/4739 820/5851/4777 -f 820/5852/4777 781/5808/4740 821/5853/4778 -f 821/5853/4778 782/5809/4741 822/5854/4779 -f 822/5854/4779 783/5810/4742 823/5855/4780 -f 823/5855/4780 784/5811/4743 824/5856/4781 -f 824/5856/4781 785/5812/4744 825/5857/4782 -f 825/5857/4782 786/350/266 826/5858/4783 -f 826/5858/4783 750/349/265 827/5859/4784 -f 827/5859/4784 751/5777/4714 828/5860/4785 -f 828/5860/4785 752/5778/4715 829/5861/4786 -f 753/5779/4716 829/5861/4786 752/5778/4715 -f 753/5779/4716 831/5862/4787 830/5863/4788 -f 754/5780/4717 832/5864/4789 831/5862/4787 -f 755/5783/4718 833/5865/4790 832/5866/4789 -f 756/5782/4719 834/5867/4791 833/5865/4790 -f 757/353/269 835/5868/4792 834/5867/4791 -f 787/352/268 836/5869/4793 835/5868/4792 -f 788/5813/4745 837/5870/4794 836/5869/4793 -f 789/5814/4746 838/5871/4795 837/5870/4794 -f 790/5815/4747 839/5872/4796 838/5871/4795 -f 791/5816/4748 840/5873/4797 839/5872/4796 -f 792/5819/4749 841/5874/4798 840/5875/4797 -f 793/5818/4750 842/5820/4751 841/5874/4798 -f 890/5876/4799 795/5877/4752 843/5878/4800 -f 843/5878/4800 796/5879/4753 844/5880/4801 -f 844/5881/4801 797/5824/4754 845/5882/4802 -f 845/5882/4802 798/5825/4755 846/5883/4803 -f 846/5883/4803 799/5826/4756 847/5884/4804 -f 847/5884/4804 800/5827/4757 848/5885/4805 -f 848/5885/4805 801/5828/4758 849/5886/4806 -f 849/5886/4806 802/5829/4759 850/5887/4807 -f 850/5887/4807 803/5830/4760 851/5888/4808 -f 851/5888/4808 804/5831/4761 852/5889/4809 -f 852/5889/4809 805/5832/4762 853/5890/4810 -f 853/5890/4810 806/5833/4763 854/5891/4811 -f 854/5891/4811 807/5834/4764 855/5892/4812 -f 855/5892/4812 808/5835/4765 856/5893/4813 -f 808/5835/4765 857/5894/4814 856/5893/4813 -f 809/5836/4766 858/5895/4815 857/5894/4814 -f 810/5837/4767 859/5896/4816 858/5895/4815 -f 811/5838/4768 860/5897/4817 859/5896/4816 -f 812/5839/4769 861/5898/4818 860/5897/4817 -f 813/5840/4770 862/5899/4819 861/5898/4818 -f 814/5841/4771 863/5900/4820 862/5899/4819 -f 815/5842/4772 864/5901/4821 863/5900/4820 -f 816/5843/4773 865/5902/4822 864/5901/4821 -f 817/5847/4774 866/5903/4823 865/5904/4822 -f 866/5903/4823 819/5849/4776 867/5905/4824 -f 867/5905/4824 820/5851/4777 868/5906/4825 -f 868/5907/4825 821/5853/4778 869/5908/4826 -f 869/5908/4826 822/5854/4779 870/5909/4827 -f 870/5909/4827 823/5855/4780 871/5910/4828 -f 871/5910/4828 824/5856/4781 872/5911/4829 -f 872/5911/4829 825/5857/4782 873/5912/4830 -f 873/5912/4830 826/5858/4783 874/5913/4831 -f 874/5913/4831 827/5859/4784 875/5914/4832 -f 875/5914/4832 828/5860/4785 876/5915/4833 -f 876/5915/4833 829/5861/4786 877/5916/4834 -f 829/5861/4786 878/5917/4835 877/5916/4834 -f 830/5863/4788 879/5918/4836 878/5917/4835 -f 831/5862/4787 880/5919/4837 879/5918/4836 -f 832/5866/4789 881/5920/4838 880/5921/4837 -f 833/5865/4790 882/5922/4839 881/5920/4838 -f 834/5867/4791 883/5923/4840 882/5922/4839 -f 835/5868/4792 884/5924/4841 883/5923/4840 -f 836/5869/4793 885/5925/4842 884/5924/4841 -f 837/5870/4794 886/5926/4843 885/5925/4842 -f 838/5871/4795 887/5927/4844 886/5926/4843 -f 839/5872/4796 888/5928/4845 887/5927/4844 -f 840/5873/4797 889/5929/4846 888/5928/4845 -f 841/5930/4798 890/5876/4799 889/5931/4846 -f 938/5932/4847 843/5878/4800 891/5933/4848 -f 891/5933/4848 844/5880/4801 892/5934/4849 -f 892/5935/4849 845/5882/4802 893/5936/4850 -f 893/5936/4850 846/5883/4803 894/5937/4851 -f 894/5937/4851 847/5884/4804 895/5938/4852 -f 895/5938/4852 848/5885/4805 896/5939/4853 -f 896/5939/4853 849/5886/4806 897/5940/4854 -f 897/5940/4854 850/5887/4807 898/5941/4855 -f 898/5941/4855 851/5888/4808 899/5942/4856 -f 899/5942/4856 852/5889/4809 900/357/273 -f 900/357/273 853/5890/4810 901/355/271 -f 901/355/271 854/5891/4811 902/5943/4857 -f 902/5943/4857 855/5892/4812 903/5944/4858 -f 903/5944/4858 856/5893/4813 904/5945/4859 -f 856/5893/4813 905/5946/4860 904/5945/4859 -f 857/5894/4814 906/5947/4861 905/5946/4860 -f 858/5895/4815 907/5948/4862 906/5947/4861 -f 859/5896/4816 908/5949/4863 907/5948/4862 -f 860/5897/4817 909/5950/4864 908/5949/4863 -f 861/5898/4818 910/5951/4865 909/5950/4864 -f 862/5899/4819 911/5952/4866 910/5951/4865 -f 863/5900/4820 912/5953/4867 911/5952/4866 -f 864/5901/4821 913/5954/4868 912/5953/4867 -f 865/5904/4822 914/5955/4869 913/5956/4868 -f 914/5955/4869 867/5905/4824 915/5957/4870 -f 915/5957/4870 868/5906/4825 916/5958/4871 -f 916/5959/4871 869/5908/4826 917/5960/4872 -f 917/5960/4872 870/5909/4827 918/5961/4873 -f 918/5961/4873 871/5910/4828 919/5962/4874 -f 919/5962/4874 872/5911/4829 920/5963/4875 -f 920/5963/4875 873/5912/4830 921/5964/4876 -f 921/5964/4876 874/5913/4831 922/5965/4877 -f 922/5965/4877 875/5914/4832 923/5966/4878 -f 923/5966/4878 876/5915/4833 924/359/275 -f 876/5915/4833 925/360/276 924/359/275 -f 877/5916/4834 926/5967/4879 925/360/276 -f 878/5917/4835 927/5968/4880 926/5967/4879 -f 879/5918/4836 928/5969/4881 927/5968/4880 -f 880/5921/4837 929/5970/4882 928/5971/4881 -f 881/5920/4838 930/5972/4883 929/5970/4882 -f 882/5922/4839 931/5973/4884 930/5972/4883 -f 883/5923/4840 932/5974/4885 931/5973/4884 -f 884/5924/4841 933/5975/4886 932/5974/4885 -f 885/5925/4842 934/5976/4887 933/5975/4886 -f 886/5926/4843 935/5977/4888 934/5976/4887 -f 887/5927/4844 936/5978/4889 935/5977/4888 -f 888/5928/4845 937/5979/4890 936/5978/4889 -f 889/5931/4846 938/5932/4847 937/5980/4890 -f 984/5981/4891 891/5933/4848 939/5982/4892 -f 939/5982/4892 892/5934/4849 940/5983/4893 -f 940/5984/4893 893/5936/4850 941/5985/4894 -f 941/5985/4894 894/5937/4851 942/5986/4895 -f 942/5986/4895 895/5938/4852 943/5987/4896 -f 943/5987/4896 896/5939/4853 944/5988/4897 -f 944/5988/4897 897/5940/4854 945/5989/4898 -f 945/5989/4898 898/5941/4855 946/363/279 -f 946/363/279 899/5942/4856 947/361/277 -f 947/361/277 900/357/273 948/356/272 -f 948/356/272 902/5943/4857 949/364/280 -f 949/364/280 903/5944/4858 950/5990/4899 -f 950/5990/4899 904/5945/4859 951/5991/4900 -f 951/5991/4900 905/5946/4860 952/5992/4901 -f 905/5946/4860 953/367/283 952/5992/4901 -f 906/5947/4861 954/368/284 953/367/283 -f 907/5948/4862 955/5993/4902 954/368/284 -f 908/5949/4863 956/5994/4903 955/5993/4902 -f 909/5950/4864 957/5995/4904 956/5994/4903 -f 910/5951/4865 958/5996/4905 957/5995/4904 -f 911/5952/4866 959/5997/4906 958/5996/4905 -f 912/5953/4867 960/5998/4907 959/5997/4906 -f 913/5956/4868 961/5999/4908 960/6000/4907 -f 961/5999/4908 915/5957/4870 962/6001/4909 -f 962/6001/4909 916/5958/4871 963/6002/4910 -f 963/6003/4910 917/5960/4872 964/6004/4911 -f 964/6004/4911 918/5961/4873 965/6005/4912 -f 965/6005/4912 919/5962/4874 966/6006/4913 -f 966/6006/4913 920/5963/4875 967/6007/4914 -f 967/6007/4914 921/5964/4876 968/6008/4915 -f 968/6008/4915 922/5965/4877 969/6009/4916 -f 969/6009/4916 923/5966/4878 970/370/286 -f 970/370/286 924/359/275 971/358/274 -f 925/360/276 972/372/288 971/358/274 -f 926/5967/4879 973/6010/4917 972/372/288 -f 927/5968/4880 974/6011/290 973/6010/4917 -f 928/5971/4881 975/375/291 974/374/290 -f 929/5970/4882 976/377/293 975/375/291 -f 930/5972/4883 977/378/294 976/377/293 -f 931/5973/4884 978/6012/4918 977/378/294 -f 932/5974/4885 979/6013/4919 978/6012/4918 -f 933/5975/4886 980/6014/4920 979/6013/4919 -f 934/5976/4887 981/6015/4921 980/6014/4920 -f 935/5977/4888 982/6016/4922 981/6015/4921 -f 936/5978/4889 983/6017/4923 982/6016/4922 -f 937/5980/4890 984/5981/4891 983/6018/4923 -f 1021/6019/4924 939/5982/4892 985/6020/4925 -f 985/6020/4925 940/5983/4893 986/6021/4926 -f 986/6022/4926 941/5985/4894 987/6023/4927 -f 987/6023/4927 942/5986/4895 988/6024/4928 -f 988/6024/4928 943/5987/4896 989/6025/4929 -f 989/6025/4929 944/5988/4897 990/381/297 -f 990/381/297 945/5989/4898 991/379/295 -f 991/379/295 946/363/279 992/362/278 -f 992/362/278 948/356/272 993/365/281 -f 954/368/284 995/6026/4930 994/366/282 -f 953/367/283 994/366/282 952/5992/4901 -f 955/5993/4902 996/6027/4931 995/6026/4930 -f 956/5994/4903 997/6028/4932 996/6027/4931 -f 957/5995/4904 998/6029/4933 997/6028/4932 -f 958/5996/4905 999/6030/4934 998/6029/4933 -f 959/5997/4906 1000/6031/4935 999/6030/4934 -f 960/6000/4907 1001/6032/4936 1000/6033/4935 -f 1001/6032/4936 962/6001/4909 1002/6034/4937 -f 1002/6034/4937 963/6002/4910 1003/6035/4938 -f 1003/6036/4938 964/6004/4911 1004/6037/4939 -f 1004/6037/4939 965/6005/4912 1005/6038/4940 -f 1005/6038/4940 966/6006/4913 1006/6039/4941 -f 1006/6039/4941 967/6007/4914 1007/6040/4942 -f 1007/6040/4942 968/6008/4915 1008/6041/4943 -f 1008/6041/4943 969/6009/4916 1009/383/299 -f 1009/383/299 970/370/286 1010/369/285 -f 972/372/288 1012/386/302 1011/371/287 -f 1011/371/287 1010/369/285 971/358/274 -f 973/6010/4917 1013/6042/289 1012/386/302 -f 975/375/291 1014/376/292 1013/373/289 -f 977/378/294 1015/6043/4944 1014/376/292 -f 978/6012/4918 1016/6044/4945 1015/6043/4944 -f 979/6013/4919 1017/6045/4946 1016/6044/4945 -f 980/6014/4920 1018/6046/4947 1017/6045/4946 -f 981/6015/4921 1019/6047/4948 1018/6046/4947 -f 982/6016/4922 1020/6048/4949 1019/6047/4948 -f 983/6018/4923 1021/6019/4924 1020/6049/4949 -f 1062/6050/4950 985/6020/4925 1022/6051/4951 -f 1022/6051/4951 986/6021/4926 1023/6052/4952 -f 1023/6053/4952 987/6023/4927 1024/6054/4953 -f 1024/6054/4953 988/6024/4928 1025/6055/4954 -f 1025/6055/4954 989/6025/4929 1026/6056/4955 -f 1026/6056/4955 990/381/297 1027/380/296 -f 1027/380/296 992/362/278 1028/389/305 -f 1028/389/305 993/365/281 1029/387/303 -f 993/365/281 1031/403/319 1030/399/315 -f 993/365/281 1030/399/315 1029/387/303 -f 1031/403/319 950/5990/4899 1032/401/317 -f 1032/401/317 951/5991/4900 1033/406/322 -f 1033/406/322 952/5992/4901 1034/404/320 -f 1034/404/320 994/366/282 1035/409/325 -f 995/6026/4930 1036/407/323 994/366/282 -f 1036/407/323 1035/409/325 994/366/282 -f 995/6026/4930 1038/392/308 1037/391/307 -f 996/6027/4931 1039/6057/4956 1038/392/308 -f 997/6028/4932 1040/6058/4957 1039/6057/4956 -f 998/6029/4933 1041/6059/4958 1040/6058/4957 -f 999/6030/4934 1042/6060/4959 1041/6059/4958 -f 1000/6033/4935 1043/6061/4960 1042/6062/4959 -f 1043/6061/4960 1002/6034/4937 1044/6063/4961 -f 1044/6063/4961 1003/6035/4938 1045/6064/4962 -f 1045/6065/4962 1004/6037/4939 1046/6066/4963 -f 1046/6066/4963 1005/6038/4940 1047/6067/4964 -f 1047/6067/4964 1006/6039/4941 1048/6068/4965 -f 1048/6068/4965 1007/6040/4942 1049/395/311 -f 1049/395/311 1008/6041/4943 1050/393/309 -f 1050/393/309 1009/383/299 1051/382/298 -f 1012/386/302 1054/6069/4966 1053/385/301 -f 1052/384/300 1051/382/298 1010/369/285 -f 1053/385/301 1052/384/300 1011/371/287 -f 1013/373/289 1055/6070/4967 1054/6071/4966 -f 1014/376/292 1056/397/313 1055/6070/4967 -f 1015/6043/4944 1057/398/314 1056/397/313 -f 1016/6044/4945 1058/6072/4968 1057/398/314 -f 1017/6045/4946 1059/6073/4969 1058/6072/4968 -f 1018/6046/4947 1060/6074/4970 1059/6073/4969 -f 1019/6047/4948 1061/6075/4971 1060/6074/4970 -f 1020/6049/4949 1062/6050/4950 1061/6076/4971 -f 1092/6077/4972 1022/6051/4951 1063/6078/4973 -f 1063/6078/4973 1023/6052/4952 1064/6079/4974 -f 1064/6080/4974 1024/6054/4953 1065/6081/4975 -f 1065/6081/4975 1025/6055/4954 1066/6082/4976 -f 1066/6082/4976 1026/6056/4955 1067/6083/4977 -f 1067/6083/4977 1027/380/296 1068/6084/4978 -f 1068/6084/4978 1028/389/305 1069/388/304 -f 1038/392/308 1071/6085/4979 1070/390/306 -f 1039/6057/4956 1072/6086/4980 1071/6085/4979 -f 1040/6058/4957 1073/6087/4981 1072/6086/4980 -f 1041/6059/4958 1074/6088/4982 1073/6087/4981 -f 1042/6062/4959 1075/6089/4983 1074/6090/4982 -f 1075/6089/4983 1044/6063/4961 1076/6091/4984 -f 1076/6091/4984 1045/6064/4962 1077/6092/4985 -f 1077/6093/4985 1046/6066/4963 1078/6094/4986 -f 1078/6094/4986 1047/6067/4964 1079/6095/4987 -f 1079/6095/4987 1048/6068/4965 1080/6096/4988 -f 1048/6068/4965 1081/394/310 1080/6096/4988 -f 1050/393/309 1082/6097/4989 1081/394/310 -f 1051/382/298 1083/6098/4990 1082/6097/4989 -f 1052/384/300 1084/6099/4991 1083/6098/4990 -f 1053/385/301 1085/6100/4992 1084/6099/4991 -f 1054/6071/4966 1086/6101/4993 1085/6102/4992 -f 1055/6070/4967 1087/396/312 1086/6101/4993 -f 1057/398/314 1088/6103/4994 1087/396/312 -f 1058/6072/4968 1089/6104/4995 1088/6103/4994 -f 1059/6073/4969 1090/6105/4996 1089/6104/4995 -f 1060/6074/4970 1091/6106/4997 1090/6105/4996 -f 1061/6076/4971 1092/6077/4972 1091/6107/4997 -f 1126/6108/4998 1063/6078/4973 1093/413/329 -f 1093/413/329 1064/6079/4974 1094/411/327 -f 1064/6080/4974 1095/6109/4999 1094/6110/327 -f 1065/6081/4975 1096/416/332 1095/6109/4999 -f 1096/416/332 1067/6083/4977 1097/414/330 -f 1097/414/330 1068/6084/4978 1098/6111/5000 -f 1098/6111/5000 1069/388/304 1099/6112/5001 -f 1069/388/304 1100/400/316 1099/6112/5001 -f 1100/400/316 1031/403/319 1101/402/318 -f 1101/402/318 1033/406/322 1102/405/321 -f 1102/405/321 1035/409/325 1103/408/324 -f 1103/408/324 1037/391/307 1104/410/326 -f 1070/390/306 1105/6113/5002 1104/410/326 -f 1071/6085/4979 1106/6114/5003 1105/6113/5002 -f 1072/6086/4980 1107/6115/5004 1106/6114/5003 -f 1073/6087/4981 1108/6116/5005 1107/6115/5004 -f 1074/6090/4982 1109/6117/5006 1108/6118/5005 -f 1109/6117/5006 1076/6091/4984 1110/419/335 -f 1110/419/335 1077/6092/4985 1111/417/333 -f 1077/6093/4985 1112/422/337 1111/421/333 -f 1112/422/337 1079/6095/4987 1113/6119/5007 -f 1113/6119/5007 1080/6096/4988 1114/6120/5008 -f 1080/6096/4988 1115/6121/5009 1114/6120/5008 -f 1082/6097/4989 1115/6121/5009 1081/394/310 -f 1082/6097/4989 1117/6122/5010 1116/6123/5011 -f 1083/6098/4990 1118/6124/5012 1117/6122/5010 -f 1084/6099/4991 1119/6125/5013 1118/6124/5012 -f 1085/6102/4992 1120/6126/5014 1119/6127/5013 -f 1086/6101/4993 1121/6128/5015 1120/6126/5014 -f 1087/396/312 1122/6129/5016 1121/6128/5015 -f 1088/6103/4994 1123/6130/5017 1122/6129/5016 -f 1089/6104/4995 1124/6131/5018 1123/6130/5017 -f 1090/6105/4996 1125/6132/5019 1124/6131/5018 -f 1091/6107/4997 1126/6108/4998 1125/6133/5019 -f 1156/6134/5020 1093/413/329 1127/412/328 -f 1128/415/331 1098/6111/5000 1129/6135/5021 -f 1096/416/332 1128/415/331 1095/6109/4999 -f 1129/6135/5021 1099/6112/5001 1130/6136/5022 -f 1130/6136/5022 1100/400/316 1131/6137/5023 -f 1131/6137/5023 1101/402/318 1132/6138/5024 -f 1132/6138/5024 1102/405/321 1133/6139/5025 -f 1133/6139/5025 1103/408/324 1134/6140/5026 -f 1134/6140/5026 1104/410/326 1135/6141/5027 -f 1135/6141/5027 1105/6113/5002 1136/6142/5028 -f 1136/6142/5028 1106/6114/5003 1137/6143/5029 -f 1137/6143/5029 1107/6115/5004 1138/6144/5030 -f 1138/6144/5030 1108/6116/5005 1139/6145/5031 -f 1139/6146/5031 1109/6117/5006 1140/6147/5032 -f 1140/6147/5032 1110/419/335 1141/418/334 -f 1112/422/337 1143/424/339 1142/420/336 -f 1113/6119/5007 1144/425/340 1143/424/339 -f 1114/6120/5008 1145/6148/5033 1144/425/340 -f 1115/6121/5009 1146/6149/5034 1145/6148/5033 -f 1116/6123/5011 1147/6150/5035 1146/6149/5034 -f 1117/6122/5010 1148/6151/5036 1147/6150/5035 -f 1118/6124/5012 1149/6152/5037 1148/6151/5036 -f 1119/6127/5013 1150/6153/5038 1149/6154/5037 -f 1120/6126/5014 1151/6155/5039 1150/6153/5038 -f 1121/6128/5015 1152/427/342 1151/6155/5039 -f 1122/6129/5016 1153/428/343 1152/427/342 -f 1123/6130/5017 1154/6156/5040 1153/428/343 -f 1124/6131/5018 1155/6157/5041 1154/6156/5040 -f 1125/6133/5019 1156/6134/5020 1155/6158/5041 -f 1187/6159/5042 1127/412/328 1157/6160/5043 -f 1127/412/328 1158/6161/346 1157/6160/5043 -f 1158/431/346 1095/6109/4999 1159/429/344 -f 1159/429/344 1128/415/331 1160/6162/5044 -f 1160/6162/5044 1129/6135/5021 1161/6163/5045 -f 1161/6163/5045 1130/6136/5022 1162/6164/5046 -f 1162/6164/5046 1131/6137/5023 1163/6165/5047 -f 1163/6165/5047 1132/6138/5024 1164/6166/5048 -f 1164/6166/5048 1133/6139/5025 1165/6167/5049 -f 1165/6167/5049 1134/6140/5026 1166/6168/5050 -f 1166/6168/5050 1135/6141/5027 1167/6169/5051 -f 1167/6169/5051 1136/6142/5028 1168/434/349 -f 1168/434/349 1137/6143/5029 1169/432/347 -f 1169/432/347 1138/6144/5030 1170/6170/5052 -f 1170/6170/5052 1139/6145/5031 1171/6171/5053 -f 1171/6172/5053 1140/6147/5032 1172/6173/5054 -f 1172/6173/5054 1141/418/334 1173/6174/5055 -f 1141/418/334 1174/6175/5056 1173/6174/5055 -f 1142/420/336 1174/6176/5056 1111/421/333 -f 1142/420/336 1176/423/338 1175/6177/5057 -f 1144/425/340 1177/6178/5058 1176/423/338 -f 1145/6148/5033 1178/6179/5059 1177/6178/5058 -f 1146/6149/5034 1179/6180/5060 1178/6179/5059 -f 1147/6150/5035 1180/6181/5061 1179/6180/5060 -f 1148/6151/5036 1181/6182/5062 1180/6181/5061 -f 1149/6154/5037 1182/6183/5063 1181/6184/5062 -f 1150/6153/5038 1183/6185/5064 1182/6183/5063 -f 1151/6155/5039 1184/426/341 1183/6185/5064 -f 1153/428/343 1185/6186/5065 1184/426/341 -f 1154/6187/5040 1186/6188/5066 1185/6189/5065 -f 1155/6158/5041 1187/6159/5042 1186/6188/5066 -f 1187/6159/5042 1188/6190/5067 1216/6191/5068 -f 1188/6190/5067 1158/6161/346 1189/6192/345 -f 1190/6193/5069 1161/6163/5045 1191/6194/5070 -f 1160/6162/5044 1190/6193/5069 1159/429/344 -f 1191/6194/5070 1162/6164/5046 1192/6195/5071 -f 1192/6195/5071 1163/6165/5047 1193/6196/5072 -f 1193/6196/5072 1164/6166/5048 1194/6197/5073 -f 1194/6197/5073 1165/6167/5049 1195/6198/5074 -f 1195/6198/5074 1166/6168/5050 1196/6199/5075 -f 1196/6199/5075 1167/6169/5051 1197/6200/5076 -f 1197/6200/5076 1168/434/349 1198/433/348 -f 1198/433/348 1170/6170/5052 1199/6201/5077 -f 1199/6202/5077 1171/6172/5053 1200/6203/5078 -f 1200/6203/5078 1172/6173/5054 1201/6204/5079 -f 1201/6204/5079 1173/6174/5055 1202/6205/5080 -f 1173/6174/5055 1203/6206/5081 1202/6205/5080 -f 1174/6176/5056 1204/6207/5082 1203/6208/5081 -f 1175/6177/5057 1205/6209/5083 1204/6207/5082 -f 1176/423/338 1206/6210/5084 1205/6209/5083 -f 1177/6178/5058 1207/6211/5085 1206/6210/5084 -f 1178/6179/5059 1208/6212/5086 1207/6211/5085 -f 1179/6180/5060 1209/6213/5087 1208/6212/5086 -f 1180/6181/5061 1210/6214/5088 1209/6213/5087 -f 1181/6184/5062 1211/6215/5089 1210/6216/5088 -f 1182/6183/5063 1212/6217/5090 1211/6215/5089 -f 1183/6185/5064 1213/6218/5091 1212/6217/5090 -f 1184/6219/341 1214/6220/5092 1213/6221/5091 -f 1185/6222/5065 1215/6223/5093 1214/6220/5092 -f 1186/6224/5066 1216/6225/5068 1215/6223/5093 -f 1216/6225/5068 1217/6226/5094 1246/6227/5095 -f 1217/6228/5094 1189/6192/345 1218/6229/5096 -f 1189/430/345 1219/6230/5097 1218/6231/5096 -f 1219/6230/5097 1190/6193/5069 1220/6232/5098 -f 1220/6232/5098 1191/6194/5070 1221/6233/5099 -f 1221/6233/5099 1192/6195/5071 1222/6234/5100 -f 1222/6234/5100 1193/6196/5072 1223/6235/5101 -f 1223/6235/5101 1194/6197/5073 1224/6236/5102 -f 1224/6236/5102 1195/6198/5074 1225/6237/5103 -f 1225/6237/5103 1196/6199/5075 1226/6238/5104 -f 1226/6238/5104 1197/6200/5076 1227/6239/5105 -f 1227/6239/5105 1198/433/348 1228/6240/5106 -f 1228/6241/5106 1199/6242/5077 1229/6243/5107 -f 1229/6243/5107 1200/6244/5078 1230/6245/5108 -f 1230/6246/5108 1201/6204/5079 1231/6247/5109 -f 1231/6247/5109 1202/6205/5080 1232/6248/5110 -f 1202/6205/5080 1233/6249/5111 1232/6248/5110 -f 1203/6208/5081 1234/6250/5112 1233/6251/5111 -f 1204/6207/5082 1235/436/351 1234/6250/5112 -f 1205/6209/5083 1236/437/352 1235/436/351 -f 1206/6210/5084 1237/439/354 1236/437/352 -f 1207/6211/5085 1238/6252/356 1237/439/354 -f 1208/6212/5086 1239/6253/358 1238/6252/356 -f 1209/6213/5087 1240/6254/360 1239/6253/358 -f 1210/6216/5088 1241/6255/362 1240/6256/360 -f 1211/6257/5089 1242/451/364 1241/449/362 -f 1212/6258/5090 1243/453/366 1242/451/364 -f 1213/6221/5091 1244/455/368 1243/453/366 -f 1214/6220/5092 1245/6259/5113 1244/455/368 -f 1215/6223/5093 1246/6227/5095 1245/6259/5113 -f 1246/6227/5095 1247/6260/5114 1276/6261/5115 -f 1247/6262/5114 1218/6231/5096 1248/6263/5116 -f 1218/6231/5096 1249/458/371 1248/6263/5116 -f 1249/458/371 1220/6232/5098 1250/456/369 -f 1250/456/369 1221/6233/5099 1251/459/372 -f 1251/459/372 1222/6234/5100 1252/461/374 -f 1252/461/374 1223/6235/5101 1253/6264/376 -f 1253/6264/376 1224/6236/5102 1254/6265/378 -f 1254/6265/378 1225/6237/5103 1255/6266/380 -f 1255/468/380 1226/6267/5104 1256/470/382 -f 1256/470/382 1227/6268/5105 1257/472/384 -f 1257/472/384 1228/6241/5106 1258/474/386 -f 1258/474/386 1229/6243/5107 1259/476/388 -f 1259/476/388 1230/6245/5108 1260/6269/5117 -f 1260/6269/5117 1231/6270/5109 1261/6271/5118 -f 1261/6272/5118 1232/6248/5110 1262/6273/5119 -f 1232/6248/5110 1263/6274/5120 1262/6273/5119 -f 1233/6251/5111 1264/6275/5121 1263/6276/5120 -f 1234/6250/5112 1265/6277/5122 1264/6275/5121 -f 1244/455/368 1275/6278/5123 1274/454/367 -f 1266/435/350 1265/6277/5122 1235/436/351 -f 1267/438/353 1266/435/350 1236/437/352 -f 1268/440/355 1267/6279/353 1237/441/354 -f 1269/443/357 1268/440/355 1238/442/356 -f 1270/445/359 1269/443/357 1239/444/358 -f 1271/447/361 1270/6280/359 1240/448/360 -f 1272/450/363 1271/447/361 1241/449/362 -f 1273/452/365 1272/450/363 1242/451/364 -f 1274/454/367 1273/452/365 1243/453/366 -f 1245/6259/5113 1276/6261/5115 1275/6278/5123 -f 1276/6261/5115 1277/6281/5124 1304/480/392 -f 1287/477/389 1260/6269/5117 1288/6282/5125 -f 1248/6263/5116 1277/6283/5124 1247/6262/5114 -f 1249/458/371 1278/457/370 1248/6263/5116 -f 1250/456/369 1279/460/373 1278/457/370 -f 1251/459/372 1280/462/375 1279/460/373 -f 1252/465/374 1281/464/377 1280/6284/375 -f 1253/463/376 1282/467/379 1281/464/377 -f 1254/466/378 1283/469/381 1282/467/379 -f 1255/468/380 1284/471/383 1283/469/381 -f 1256/470/382 1285/473/385 1284/471/383 -f 1257/472/384 1286/475/387 1285/473/385 -f 1258/474/386 1287/477/389 1286/475/387 -f 1288/6282/5125 1261/6271/5118 1289/6285/5126 -f 1289/6286/5126 1262/6273/5119 1290/6287/5127 -f 1290/6287/5127 1263/6274/5120 1291/6288/5128 -f 1263/6274/5120 1292/6289/5129 1291/6288/5128 -f 1264/6275/5121 1293/6290/5130 1292/6291/5129 -f 1265/6277/5122 1294/6292/5131 1293/6290/5130 -f 1266/6293/350 1295/6294/5132 1294/6295/5131 -f 1267/6279/353 1296/6296/5133 1295/6294/5132 -f 1268/440/355 1297/6297/5134 1296/6296/5133 -f 1269/443/357 1298/6298/5135 1297/6297/5134 -f 1270/6280/359 1299/6299/5136 1298/6300/5135 -f 1271/447/361 1300/6301/5137 1299/6299/5136 -f 1272/450/363 1301/6302/5138 1300/6301/5137 -f 1273/452/365 1302/6303/5139 1301/6302/5138 -f 1274/454/367 1303/479/391 1302/6303/5139 -f 1275/6278/5123 1304/480/392 1303/479/391 -f 1304/480/392 1305/6304/394 1333/478/390 -f 1277/6283/5124 1306/6305/5140 1305/483/394 -f 1306/6305/5140 1278/457/370 1307/493/404 -f 1308/491/402 1279/460/373 1309/6306/5141 -f 1278/457/370 1308/491/402 1307/493/404 -f 1309/6306/5141 1280/462/375 1310/6307/5142 -f 1310/6308/5142 1281/464/377 1311/6309/5143 -f 1311/6309/5143 1282/467/379 1312/6310/5144 -f 1312/6310/5144 1283/469/381 1313/6311/5145 -f 1313/6311/5145 1284/471/383 1314/6312/5146 -f 1314/6312/5146 1285/473/385 1315/6313/5147 -f 1315/6313/5147 1286/475/387 1316/6314/5148 -f 1316/6314/5148 1287/477/389 1317/6315/5149 -f 1317/6315/5149 1288/6282/5125 1318/486/397 -f 1318/486/397 1289/6285/5126 1319/484/395 -f 1319/484/395 1290/6316/5127 1320/487/398 -f 1320/6317/398 1291/6288/5128 1321/6318/5150 -f 1292/6289/5129 1321/6318/5150 1291/6288/5128 -f 1292/6291/5129 1323/490/401 1322/489/400 -f 1293/6319/5130 1324/6320/5151 1323/6321/401 -f 1294/6295/5131 1325/6322/5152 1324/6320/5151 -f 1295/6294/5132 1326/6323/5153 1325/6322/5152 -f 1296/6296/5133 1327/6324/5154 1326/6323/5153 -f 1297/6297/5134 1328/6325/5155 1327/6324/5154 -f 1298/6300/5135 1329/6326/5156 1328/6327/5155 -f 1299/6299/5136 1330/6328/5157 1329/6326/5156 -f 1300/6301/5137 1331/6329/5158 1330/6328/5157 -f 1301/6302/5138 1332/6330/5159 1331/6329/5158 -f 1302/6303/5139 1333/478/390 1332/6330/5159 -f 1335/6331/5160 1310/6308/5142 1336/6332/5161 -f 1306/6305/5140 1334/481/393 1305/483/394 -f 1309/6306/5141 1335/6333/5160 1308/491/402 -f 1336/6332/5161 1311/6309/5143 1337/6334/5162 -f 1337/6334/5162 1312/6310/5144 1338/6335/5163 -f 1338/6335/5163 1313/6311/5145 1339/6336/5164 -f 1339/6336/5164 1314/6312/5146 1340/6337/5165 -f 1340/6337/5165 1315/6313/5147 1341/6338/5166 -f 1341/6338/5166 1316/6314/5148 1342/6339/5167 -f 1342/6339/5167 1317/6315/5149 1343/6340/5168 -f 1343/6340/5168 1318/486/397 1344/485/396 -f 1323/6321/401 1346/496/406 1345/495/399 -f 1324/6320/5151 1347/498/408 1346/496/406 -f 1325/6322/5152 1348/500/410 1347/498/408 -f 1326/6323/5153 1349/502/412 1348/500/410 -f 1327/6324/5154 1350/504/414 1349/502/412 -f 1328/6327/5155 1351/507/416 1350/506/414 -f 1329/6326/5156 1352/509/418 1351/507/416 -f 1330/6328/5157 1353/511/420 1352/509/418 -f 1331/6329/5158 1354/513/422 1353/511/420 -f 1332/6330/5159 1355/515/424 1354/513/422 -f 1334/481/393 1357/6341/5169 1356/6342/423 -f 1334/516/393 1355/515/424 1333/478/390 -f 1306/6305/5140 1358/492/403 1357/6341/5169 -f 1358/492/403 1335/6333/5160 1359/6343/425 -f 1359/517/425 1336/6332/5161 1360/520/427 -f 1360/520/427 1337/6334/5162 1361/522/429 -f 1361/522/429 1338/6335/5163 1362/524/431 -f 1362/524/431 1339/6336/5164 1363/526/433 -f 1363/526/433 1340/6337/5165 1364/528/435 -f 1364/528/435 1341/6338/5166 1365/530/437 -f 1365/530/437 1342/6339/5167 1366/532/439 -f 1366/532/439 1343/6340/5168 1367/534/441 -f 1367/534/441 1344/485/396 1368/536/443 -f 1344/485/396 1369/538/445 1368/536/443 -f 1369/6344/445 1321/6318/5150 1370/6345/5170 -f 1370/6345/5170 1322/6346/400 1371/6347/5171 -f 1345/6348/399 1371/6347/5171 1322/6346/400 -f 1356/6342/423 1383/6349/5172 1382/6350/5173 -f 1373/497/407 1372/494/405 1346/496/406 -f 1374/499/409 1373/497/407 1347/498/408 -f 1375/501/411 1374/499/409 1348/500/410 -f 1376/503/413 1375/501/411 1349/502/412 -f 1377/6351/415 1376/503/413 1350/504/414 -f 1378/508/417 1377/505/415 1351/507/416 -f 1379/510/419 1378/508/417 1352/509/418 -f 1380/512/421 1379/510/419 1353/511/420 -f 1381/541/448 1380/512/421 1354/513/422 -f 1355/515/424 1381/541/448 1354/513/422 -f 1357/6341/5169 1384/6352/5174 1383/6349/5172 -f 1395/6353/446 1370/6354/5170 1396/6355/5175 -f 1358/492/403 1385/6356/426 1384/6352/5174 -f 1359/517/425 1386/521/428 1385/518/426 -f 1360/520/427 1387/523/430 1386/521/428 -f 1361/522/429 1388/525/432 1387/523/430 -f 1362/524/431 1389/527/434 1388/525/432 -f 1363/526/433 1390/529/436 1389/527/434 -f 1364/528/435 1391/531/438 1390/529/436 -f 1365/530/437 1392/533/440 1391/531/438 -f 1366/532/439 1393/535/442 1392/533/440 -f 1367/534/441 1394/537/444 1393/535/442 -f 1368/536/443 1395/539/446 1394/537/444 -f 1396/6355/5175 1371/6357/5171 1397/6358/5176 -f 1371/6357/5171 1398/6359/5177 1397/6358/5176 -f 1372/494/405 1399/6360/5178 1398/6361/5177 -f 1373/497/407 1400/6362/5179 1399/6360/5178 -f 1374/499/409 1401/6363/5180 1400/6362/5179 -f 1375/501/411 1402/6364/5181 1401/6363/5180 -f 1376/503/413 1403/6365/5182 1402/6364/5181 -f 1377/505/415 1404/6366/5183 1403/6367/5182 -f 1378/508/417 1405/6368/5184 1404/6366/5183 -f 1379/510/419 1406/6369/5185 1405/6368/5184 -f 1380/512/421 1407/540/447 1406/6369/5185 -f 1356/514/423 1407/540/447 1355/515/424 -f 1382/6350/5173 1409/6370/5186 1408/6371/5187 -f 1383/6349/5172 1410/6372/5188 1409/6370/5186 -f 1410/6372/5188 1385/6356/426 1411/6373/5189 -f 1411/6374/5189 1386/521/428 1412/6375/5190 -f 1412/6375/5190 1387/523/430 1413/6376/5191 -f 1413/6376/5191 1388/525/432 1414/6377/5192 -f 1414/6377/5192 1389/527/434 1415/6378/5193 -f 1415/6378/5193 1390/529/436 1416/6379/5194 -f 1416/6379/5194 1391/531/438 1417/6380/5195 -f 1417/6380/5195 1392/533/440 1418/6381/5196 -f 1418/6381/5196 1393/535/442 1419/6382/5197 -f 1419/6382/5197 1394/537/444 1420/6383/5198 -f 1420/6383/5198 1395/539/446 1421/6384/5199 -f 1421/6385/5199 1396/6355/5175 1422/6386/5200 -f 1422/6386/5200 1397/6358/5176 1423/6387/5201 -f 1397/6388/5176 1424/6389/5202 1423/6390/5201 -f 1398/6361/5177 1426/6391/5203 1425/6392/5204 -f 1425/6392/5204 1424/6393/5202 1398/6361/5177 -f 1399/6360/5178 1427/6394/5205 1426/6391/5203 -f 1400/6362/5179 1428/6395/5206 1427/6394/5205 -f 1401/6363/5180 1429/6396/5207 1428/6395/5206 -f 1402/6364/5181 1430/6397/5208 1429/6396/5207 -f 1403/6367/5182 1431/6398/5209 1430/6399/5208 -f 1404/6366/5183 1432/6400/5210 1431/6398/5209 -f 1405/6368/5184 1433/6401/5211 1432/6400/5210 -f 1406/6369/5185 1434/6402/5212 1433/6401/5211 -f 1407/6403/447 1408/6371/5187 1434/6404/5212 -f 1409/6370/5186 1435/589/493 1408/6371/5187 -f 1409/6370/5186 1437/544/451 1436/543/450 -f 1410/6405/5188 1438/6406/5213 1437/6407/451 -f 1439/547/454 1412/6375/5190 1440/545/452 -f 1411/6374/5189 1439/547/454 1438/6408/5213 -f 1440/545/452 1413/6376/5191 1441/548/455 -f 1441/548/455 1414/6377/5192 1442/550/457 -f 1442/550/457 1415/6378/5193 1443/6409/459 -f 1443/6409/459 1416/6379/5194 1444/6410/461 -f 1444/6410/461 1417/6380/5195 1445/6411/463 -f 1445/6411/463 1418/6381/5196 1446/6412/465 -f 1446/6412/465 1419/6382/5197 1447/6413/467 -f 1447/561/467 1420/6414/5198 1448/563/469 -f 1448/563/469 1421/6385/5199 1449/565/471 -f 1449/565/471 1422/6386/5200 1450/567/473 -f 1422/6386/5200 1451/6415/5214 1450/567/473 -f 1451/6416/5214 1424/6389/5202 1452/6417/5215 -f 1424/6389/5202 1453/6418/5216 1452/6417/5215 -f 1425/6392/5204 1454/570/476 1453/6419/5216 -f 1426/6391/5203 1455/571/477 1454/570/476 -f 1427/6394/5205 1456/573/479 1455/571/477 -f 1428/6395/5206 1457/6420/481 1456/573/479 -f 1429/6396/5207 1458/6421/483 1457/6420/481 -f 1430/6399/5208 1459/6422/485 1458/6423/483 -f 1431/6398/5209 1460/6424/487 1459/6422/485 -f 1432/6425/5210 1461/585/489 1460/583/487 -f 1433/6426/5211 1462/587/491 1461/585/489 -f 1434/6404/5212 1435/589/493 1462/587/491 -f 1437/6407/451 1465/6427/5217 1464/6428/449 -f 1463/6429/5218 1489/588/492 1435/589/493 -f 1436/543/450 1463/6429/5218 1435/589/493 -f 1465/6427/5217 1439/6430/454 1466/6431/453 -f 1477/6432/5219 1452/6417/5215 1478/6433/5220 -f 1440/545/452 1467/549/456 1466/546/453 -f 1441/548/455 1468/551/458 1467/549/456 -f 1442/550/457 1469/6434/460 1468/551/458 -f 1443/552/459 1470/556/462 1469/553/460 -f 1444/555/461 1471/558/464 1470/556/462 -f 1445/557/463 1472/560/466 1471/558/464 -f 1446/559/465 1473/562/468 1472/560/466 -f 1447/561/467 1474/564/470 1473/562/468 -f 1448/563/469 1475/566/472 1474/564/470 -f 1449/565/471 1476/568/474 1475/566/472 -f 1451/6415/5214 1477/6435/5219 1450/567/473 -f 1452/6417/5215 1479/6436/5221 1478/6433/5220 -f 1453/6419/5216 1480/6437/5222 1479/6438/5221 -f 1489/588/492 1490/6439/5223 1519/6440/5224 -f 1481/569/475 1480/6437/5222 1454/570/476 -f 1482/572/478 1481/569/475 1455/571/477 -f 1483/574/480 1482/6441/478 1456/575/479 -f 1484/577/482 1483/574/480 1457/576/481 -f 1485/579/484 1484/6442/482 1458/580/483 -f 1486/582/486 1485/579/484 1459/581/485 -f 1487/584/488 1486/582/486 1460/583/487 -f 1488/586/490 1487/584/488 1461/585/489 -f 1489/588/492 1488/586/490 1462/587/491 -f 1463/6429/5218 1491/591/495 1490/6439/5223 -f 1464/542/449 1491/591/495 1436/543/450 -f 1464/6428/449 1493/6443/5225 1492/6444/496 -f 1493/6443/5225 1466/6431/453 1494/6445/5226 -f 1495/6446/5227 1467/549/456 1496/6447/5228 -f 1466/6431/453 1495/6448/5227 1494/6445/5226 -f 1496/6449/5228 1468/6450/458 1497/6451/5229 -f 1497/6451/5229 1469/553/460 1498/6452/5230 -f 1498/6452/5230 1470/556/462 1499/6453/5231 -f 1499/6453/5231 1471/558/464 1500/6454/5232 -f 1500/6454/5232 1472/560/466 1501/6455/5233 -f 1501/6455/5233 1473/562/468 1502/6456/5234 -f 1502/6456/5234 1474/564/470 1503/6457/5235 -f 1503/6457/5235 1475/566/472 1504/595/499 -f 1504/595/499 1476/568/474 1505/593/497 -f 1476/568/474 1506/6458/5236 1505/593/497 -f 1506/6458/5236 1477/6435/5219 1507/6459/5237 -f 1507/6460/5237 1478/6433/5220 1508/6461/5238 -f 1478/6433/5220 1509/6462/5239 1508/6461/5238 -f 1479/6436/5221 1510/6463/5240 1509/6462/5239 -f 1480/6464/5222 1511/6465/5241 1510/6466/5240 -f 1481/6467/475 1512/6468/5242 1511/6465/5241 -f 1482/6441/478 1513/6469/5243 1512/6468/5242 -f 1483/574/480 1514/6470/5244 1513/6469/5243 -f 1484/6442/482 1515/6471/5245 1514/6472/5244 -f 1485/579/484 1516/6473/5246 1515/6471/5245 -f 1486/582/486 1517/6474/5247 1516/6473/5246 -f 1487/584/488 1518/6475/5248 1517/6474/5247 -f 1488/586/490 1519/6440/5224 1518/6475/5248 -f 1490/6439/5223 1546/6476/5249 1519/6440/5224 -f 1492/6444/496 1522/6477/5250 1521/6478/494 -f 1491/591/495 1520/597/501 1490/6439/5223 -f 1522/6477/5250 1494/6445/5226 1523/6479/5251 -f 1523/6479/5251 1495/6448/5227 1524/6480/504 -f 1524/600/504 1496/6449/5228 1525/598/502 -f 1525/598/502 1497/6451/5229 1526/601/505 -f 1526/601/505 1498/6452/5230 1527/603/507 -f 1527/603/507 1499/6453/5231 1528/605/509 -f 1528/605/509 1500/6454/5232 1529/607/511 -f 1529/607/511 1501/6455/5233 1530/609/513 -f 1530/609/513 1502/6456/5234 1531/6481/5252 -f 1531/6481/5252 1503/6457/5235 1532/6482/5253 -f 1532/6482/5253 1504/595/499 1533/594/498 -f 1534/6483/5254 1508/6461/5238 1535/6484/5255 -f 1507/6459/5237 1534/6485/5254 1506/6458/5236 -f 1508/6461/5238 1536/6486/5256 1535/6484/5255 -f 1509/6462/5239 1537/6487/516 1536/6486/5256 -f 1510/6466/5240 1538/613/517 1537/612/516 -f 1511/6465/5241 1539/615/519 1538/613/517 -f 1512/6468/5242 1540/617/521 1539/615/519 -f 1513/6469/5243 1541/619/523 1540/617/521 -f 1514/6472/5244 1542/622/525 1541/621/523 -f 1515/6471/5245 1543/6488/5257 1542/622/525 -f 1516/6473/5246 1544/6489/5258 1543/6488/5257 -f 1517/6474/5247 1545/6490/5259 1544/6489/5258 -f 1518/6475/5248 1546/6476/5249 1545/6490/5259 -f 1521/590/494 1547/596/500 1491/591/495 -f 1520/597/501 1573/6491/5260 1546/6476/5249 -f 1521/6478/494 1549/625/528 1548/624/527 -f 1549/625/528 1523/6479/5251 1550/6492/5261 -f 1550/6492/5261 1524/6480/504 1551/6493/503 -f 1530/609/513 1557/6494/5262 1556/610/514 -f 1525/598/502 1552/602/506 1551/599/503 -f 1526/601/505 1553/604/508 1552/602/506 -f 1527/603/507 1554/606/510 1553/604/508 -f 1528/605/509 1555/608/512 1554/606/510 -f 1529/607/511 1556/610/514 1555/608/512 -f 1557/6494/5262 1532/6482/5253 1558/6495/5263 -f 1558/6495/5263 1533/594/498 1559/6496/5264 -f 1533/594/498 1560/6497/5265 1559/6496/5264 -f 1560/6497/5265 1506/6458/5236 1561/632/535 -f 1561/632/535 1534/6485/5254 1562/633/536 -f 1562/6498/536 1535/6484/5255 1563/6499/5266 -f 1535/6484/5255 1564/6500/5267 1563/6499/5266 -f 1536/6486/5256 1565/6501/515 1564/6500/5267 -f 1542/622/525 1570/6502/5268 1569/620/524 -f 1566/614/518 1565/611/515 1538/613/517 -f 1567/616/520 1566/614/518 1539/615/519 -f 1568/618/522 1567/616/520 1540/617/521 -f 1569/637/524 1568/618/522 1541/619/523 -f 1543/6488/5257 1571/6503/5269 1570/6502/5268 -f 1544/6489/5258 1572/6504/5270 1571/6503/5269 -f 1545/6490/5259 1573/6491/5260 1572/6504/5270 -f 1573/6491/5260 1574/640/542 1599/678/579 -f 1575/623/526 1550/6492/5261 1576/6505/5271 -f 1547/596/500 1574/640/542 1520/597/501 -f 1576/6505/5271 1551/6493/503 1577/6506/5272 -f 1557/6494/5262 1583/645/547 1556/610/514 -f 1551/599/503 1578/626/529 1577/6507/5272 -f 1552/602/506 1579/627/530 1578/626/529 -f 1553/604/508 1580/628/531 1579/627/530 -f 1554/606/510 1581/629/532 1580/628/531 -f 1555/608/512 1582/630/533 1581/629/532 -f 1556/610/514 1583/645/547 1582/630/533 -f 1557/6494/5262 1585/649/551 1584/647/549 -f 1558/6495/5263 1586/651/553 1585/649/551 -f 1562/6498/536 1588/6508/5273 1587/6509/534 -f 1560/6497/5265 1586/651/553 1559/6496/5264 -f 1588/6508/5273 1564/6500/5267 1589/6510/5274 -f 1589/6510/5274 1565/6501/515 1590/6511/571 -f 1570/6502/5268 1595/655/557 1569/620/524 -f 1565/611/515 1591/634/537 1590/670/571 -f 1566/614/518 1592/635/538 1591/634/537 -f 1567/616/520 1593/636/539 1592/635/538 -f 1568/618/522 1594/638/540 1593/636/539 -f 1595/655/557 1594/657/540 1569/620/524 -f 1570/6502/5268 1597/660/561 1596/658/559 -f 1571/6503/5269 1598/662/563 1597/660/561 -f 1572/6504/5270 1599/678/579 1598/662/563 -f 1548/6512/527 1600/639/541 1547/596/500 -f 1575/623/526 1601/688/573 1548/624/527 -f 1575/623/526 1603/6513/5275 1602/6514/5276 -f 1576/6505/5271 1604/6515/5277 1603/6513/5275 -f 1577/6507/5272 1605/6516/5278 1604/6517/5277 -f 1606/691/591 1579/627/530 1607/6518/5279 -f 1578/626/529 1606/691/591 1605/6516/5278 -f 1586/651/553 1616/666/567 1615/675/576 -f 1579/627/530 1608/641/543 1607/6518/5279 -f 1609/642/544 1608/641/543 1580/628/531 -f 1610/643/545 1609/642/544 1581/629/532 -f 1611/644/546 1610/643/545 1582/630/533 -f 1612/646/548 1611/644/546 1583/645/547 -f 1613/648/550 1612/646/548 1584/647/549 -f 1614/650/552 1613/648/550 1585/649/551 -f 1615/675/576 1614/650/552 1586/651/553 -f 1560/6497/5265 1617/667/568 1616/666/567 -f 1636/680/581 1574/640/542 1626/664/565 -f 1587/631/534 1617/667/568 1561/632/535 -f 1591/634/537 1618/668/569 1590/670/571 -f 1591/634/537 1619/652/554 1618/668/569 -f 1592/635/538 1620/653/555 1619/652/554 -f 1593/636/539 1621/654/556 1620/653/555 -f 1594/638/540 1622/6519/558 1621/654/556 -f 1595/655/557 1623/659/560 1622/656/558 -f 1596/658/559 1624/661/562 1623/659/560 -f 1597/660/561 1625/663/564 1624/661/562 -f 1617/667/568 1628/715/607 1627/665/566 -f 1587/6509/534 1629/709/608 1628/708/607 -f 1629/709/608 1589/6510/5274 1630/6520/5280 -f 1589/6510/5274 1631/6521/570 1630/6520/5280 -f 1632/677/578 1621/654/556 1633/698/598 -f 1633/698/598 1622/6519/558 1634/696/596 -f 1634/700/596 1623/659/560 1635/671/572 -f 1639/6522/5281 1619/652/554 1640/6523/5282 -f 1600/639/541 1637/673/574 1626/664/565 -f 1615/675/576 1653/685/586 1614/650/552 -f 1627/665/566 1638/674/575 1616/666/567 -f 1618/668/569 1639/6522/5281 1631/669/570 -f 1642/701/600 1625/663/564 1643/703/602 -f 1619/652/554 1641/676/577 1640/6523/5282 -f 1624/661/562 1642/701/600 1635/671/572 -f 1625/663/564 1644/679/580 1643/703/602 -f 1602/6514/5276 1646/6524/5283 1645/686/587 -f 1602/6514/5276 1645/686/587 1601/688/573 -f 1604/6515/5277 1646/6524/5283 1603/6513/5275 -f 1605/6516/5278 1647/6525/5284 1604/6517/5277 -f 1613/648/550 1653/685/586 1652/713/612 -f 1606/691/591 1648/690/590 1605/6516/5278 -f 1650/682/583 1649/681/582 1611/644/546 -f 1651/683/584 1650/682/583 1612/646/548 -f 1652/713/612 1651/683/584 1613/648/550 -f 1606/691/591 1657/721/619 1656/689/589 -f 1638/674/575 1654/684/585 1615/675/576 -f 1601/672/573 1655/6526/588 1637/673/574 -f 1607/6518/5279 1658/723/621 1657/721/619 -f 1608/641/543 1660/6527/5285 1659/724/622 -f 1659/724/622 1658/723/621 1608/641/543 -f 1609/642/544 1661/6528/5286 1660/6527/5285 -f 1639/6522/5281 1665/738/634 1664/710/609 -f 1649/681/582 1661/6528/5286 1610/643/545 -f 1633/698/598 1662/694/594 1632/677/578 -f 1628/715/607 1663/693/593 1627/665/566 -f 1639/6522/5281 1664/710/609 1631/669/570 -f 1665/738/634 1641/676/577 1666/742/638 -f 1641/676/577 1668/695/595 1667/740/636 -f 1641/676/577 1667/740/636 1666/742/638 -f 1676/707/606 1630/6520/5280 1677/737/633 -f 1633/698/598 1669/697/597 1662/694/594 -f 1634/700/596 1670/699/599 1669/6529/597 -f 1635/671/572 1671/702/601 1670/699/599 -f 1642/701/600 1672/704/603 1671/702/601 -f 1643/703/602 1673/705/604 1672/704/603 -f 1644/679/580 1674/692/592 1673/705/604 -f 1626/664/565 1675/706/605 1674/692/592 -f 1630/6520/5280 1678/735/610 1677/737/633 -f 1645/686/587 1680/6530/5287 1679/717/615 -f 1645/686/587 1679/717/615 1655/687/588 -f 1647/6531/5284 1680/6530/5287 1646/6524/5283 -f 1648/690/590 1681/730/628 1647/6525/5284 -f 1685/745/641 1655/6526/588 1686/747/616 -f 1656/689/589 1682/720/618 1648/690/590 -f 1654/684/585 1683/712/611 1653/685/586 -f 1676/707/606 1684/734/613 1628/708/607 -f 1637/673/574 1685/745/641 1675/706/605 -f 1657/721/619 1688/722/620 1687/719/617 -f 1659/724/622 1689/754/648 1688/722/620 -f 1660/6527/5285 1690/756/650 1689/754/648 -f 1661/6528/5286 1691/725/623 1690/756/650 -f 1679/717/615 1697/749/644 1696/6532/5288 -f 1692/726/624 1691/725/623 1650/682/583 -f 1693/727/625 1692/726/624 1651/683/584 -f 1683/712/611 1693/727/625 1652/713/612 -f 1663/693/593 1694/728/626 1638/674/575 -f 1674/692/592 1695/716/614 1673/705/604 -f 1679/717/615 1696/6532/5288 1686/718/616 -f 1680/6530/5287 1698/750/627 1697/749/644 -f 1683/712/611 1700/765/658 1699/731/629 -f 1687/719/617 1698/729/627 1682/720/618 -f 1702/733/631 1677/737/633 1703/736/632 -f 1694/728/626 1700/765/658 1654/684/585 -f 1684/714/613 1701/732/630 1663/693/593 -f 1678/711/610 1704/739/635 1703/6533/632 -f 1704/739/635 1666/742/638 1705/741/637 -f 1705/741/637 1668/695/595 1706/743/639 -f 1706/743/639 1669/697/597 1707/6534/5289 -f 1707/6535/5289 1670/699/599 1708/6536/5290 -f 1708/6536/5290 1671/702/601 1709/6537/5291 -f 1709/6537/5291 1672/704/603 1710/6538/5292 -f 1710/6538/5292 1673/705/604 1711/744/640 -f 1732/6539/5293 1675/706/605 1712/746/642 -f 1722/762/655 1703/736/632 1723/6540/5294 -f 1714/751/645 1713/6541/643 1698/729/627 -f 1715/752/646 1714/751/645 1687/719/617 -f 1716/753/647 1715/752/646 1688/722/620 -f 1717/755/649 1716/753/647 1689/754/648 -f 1718/757/651 1717/755/649 1690/756/650 -f 1719/758/652 1718/757/651 1691/725/623 -f 1720/759/653 1719/758/652 1692/726/624 -f 1721/760/654 1720/759/653 1693/727/625 -f 1723/6542/5294 1704/739/635 1724/6543/5295 -f 1724/6543/5295 1705/741/637 1725/6544/5296 -f 1725/6544/5296 1706/743/639 1726/6545/5297 -f 1726/6545/5297 1707/6534/5289 1727/6546/5298 -f 1727/6547/5298 1708/6536/5290 1728/6548/5299 -f 1728/6548/5299 1709/6537/5291 1729/6549/5300 -f 1729/6549/5300 1710/6538/5292 1730/6550/5301 -f 1730/6550/5301 1711/744/640 1731/6551/5302 -f 1731/6551/5302 1695/716/614 1732/6539/5293 -f 1758/769/662 1712/746/642 1733/767/660 -f 1734/778/663 1696/6532/5288 1735/6552/5303 -f 1686/747/616 1734/770/663 1712/746/642 -f 1696/6532/5288 1736/763/656 1735/6552/5303 -f 1713/6541/643 1737/6553/5304 1736/6554/656 -f 1714/751/645 1738/6555/5305 1737/6553/5304 -f 1715/752/646 1739/6556/5306 1738/6555/5305 -f 1716/753/647 1740/6557/5307 1739/6556/5306 -f 1717/755/649 1741/6558/5308 1740/6557/5307 -f 1718/757/651 1742/6559/5309 1741/6558/5308 -f 1719/758/652 1743/6560/5310 1742/6559/5309 -f 1720/759/653 1744/6561/5311 1743/6560/5310 -f 1721/760/654 1745/764/657 1744/6561/5311 -f 1700/765/658 1746/766/659 1745/764/657 -f 1722/761/655 1746/766/659 1701/732/630 -f 1748/774/667 1723/6540/5294 1749/6562/5312 -f 1748/774/667 1747/773/666 1722/762/655 -f 1749/6563/5312 1724/6543/5295 1750/6564/5313 -f 1750/6564/5313 1725/6544/5296 1751/6565/5314 -f 1751/6565/5314 1726/6545/5297 1752/6566/5315 -f 1752/6566/5315 1727/6546/5298 1753/6567/5316 -f 1753/6568/5316 1728/6548/5299 1754/6569/5317 -f 1754/6569/5317 1729/6549/5300 1755/6570/5318 -f 1755/6570/5318 1730/6550/5301 1756/6571/5319 -f 1756/6571/5319 1731/6551/5302 1757/6572/5320 -f 1757/6572/5320 1732/6539/5293 1758/769/662 -f 1735/6552/5303 1761/6573/5321 1760/776/669 -f 1712/746/642 1759/771/664 1733/767/660 -f 1735/6552/5303 1760/776/669 1734/778/663 -f 1736/6574/656 1762/6575/5322 1761/6576/5321 -f 1737/6553/5304 1763/6577/5323 1762/6578/5322 -f 1738/6555/5305 1764/6579/5324 1763/6577/5323 -f 1739/6556/5306 1765/6580/5325 1764/6579/5324 -f 1740/6557/5307 1766/6581/5326 1765/6580/5325 -f 1741/6558/5308 1767/6582/5327 1766/6581/5326 -f 1742/6559/5309 1768/6583/5328 1767/6582/5327 -f 1743/6560/5310 1769/6584/5329 1768/6583/5328 -f 1744/6561/5311 1770/6585/5330 1769/6584/5329 -f 1746/766/659 1770/6585/5330 1745/764/657 -f 1772/772/665 1749/6562/5312 1773/781/673 -f 1773/785/673 1750/6586/5313 1774/783/675 -f 1774/6587/675 1751/6565/5314 1775/6588/677 -f 1775/6588/677 1752/6566/5315 1776/792/679 -f 1776/792/679 1753/6567/5316 1777/790/681 -f 1777/795/681 1754/6569/5317 1778/793/683 -f 1778/793/683 1755/6570/5318 1779/796/685 -f 1779/796/685 1756/6571/5319 1780/798/687 -f 1780/798/687 1757/6572/5320 1781/800/689 -f 1781/800/689 1758/769/662 1782/768/661 -f 1760/776/669 1785/804/693 1784/777/670 -f 1734/770/663 1784/6589/670 1759/771/664 -f 1761/6576/5321 1786/807/695 1785/806/693 -f 1762/6575/5322 1787/809/697 1786/807/695 -f 1763/6577/5323 1788/6590/699 1787/6591/697 -f 1764/6579/5324 1789/815/701 1788/6590/699 -f 1765/6580/5325 1790/816/703 1789/815/701 -f 1766/6581/5326 1791/818/705 1790/816/703 -f 1767/6582/5327 1792/820/707 1791/818/705 -f 1768/6583/5328 1793/822/709 1792/820/707 -f 1769/6584/5329 1794/824/711 1793/822/709 -f 1747/6592/666 1795/779/671 1746/766/659 -f 1771/780/672 1794/824/711 1770/6585/5330 -f 1772/772/665 1796/6593/5331 1747/773/666 -f 1807/828/715 1759/771/664 1808/826/713 -f 1773/785/673 1798/784/676 1797/6594/674 -f 1774/783/675 1799/787/678 1798/784/676 -f 1775/786/677 1800/789/680 1799/787/678 -f 1776/792/679 1801/791/682 1800/6595/680 -f 1777/790/681 1802/6596/684 1801/791/682 -f 1778/793/683 1803/797/686 1802/794/684 -f 1779/796/685 1804/799/688 1803/797/686 -f 1780/798/687 1805/801/690 1804/799/688 -f 1781/800/689 1806/802/691 1805/801/690 -f 1782/768/661 1783/775/668 1806/802/691 -f 1808/826/713 1784/6589/670 1809/6597/717 -f 1821/6598/5332 1798/784/676 1822/6599/5333 -f 1810/803/692 1809/830/717 1784/777/670 -f 1811/805/694 1810/6600/692 1785/806/693 -f 1812/808/696 1811/805/694 1786/807/695 -f 1813/810/698 1812/808/696 1787/809/697 -f 1814/812/700 1813/810/698 1788/811/699 -f 1815/6601/702 1814/812/700 1789/813/701 -f 1816/817/704 1815/814/702 1790/816/703 -f 1817/819/706 1816/817/704 1791/818/705 -f 1818/821/708 1817/819/706 1792/820/707 -f 1819/823/710 1818/821/708 1793/822/709 -f 1820/825/712 1819/823/710 1794/824/711 -f 1795/779/671 1820/825/712 1771/780/672 -f 1797/782/674 1821/6602/5332 1796/6593/5331 -f 1822/6599/5333 1799/787/678 1823/6603/5334 -f 1823/6603/5334 1800/789/680 1824/6604/5335 -f 1824/6604/5335 1801/6605/682 1825/6606/5336 -f 1825/6606/5336 1802/6607/684 1826/6608/5337 -f 1826/6609/5337 1803/6610/686 1827/6611/5338 -f 1827/6612/5338 1804/799/688 1828/6613/5339 -f 1828/6613/5339 1805/801/690 1829/6614/5340 -f 1829/6614/5340 1806/802/691 1830/6615/5341 -f 1830/6615/5341 1783/775/668 1807/828/715 -f 1810/6600/692 1833/6616/5342 1832/6617/716 -f 1811/805/694 1834/6618/5343 1833/6616/5342 -f 1812/808/696 1835/6619/5344 1834/6618/5343 -f 1813/810/698 1836/6620/5345 1835/6619/5344 -f 1814/812/700 1837/6621/5346 1836/6620/5345 -f 1815/6601/702 1838/6622/5347 1837/6621/5346 -f 1816/6623/704 1839/6624/5348 1838/6622/5347 -f 1817/819/706 1840/6625/5349 1839/6626/5348 -f 1818/821/708 1841/6627/5350 1840/6625/5349 -f 1819/823/710 1842/6628/5351 1841/6627/5350 -f 1820/825/712 1843/6629/5352 1842/6628/5351 -f 1795/779/671 1844/6630/5353 1843/6629/5352 -f 1844/6631/5353 1821/6602/5332 1845/6632/5354 -f 1845/6633/5354 1822/6599/5333 1846/6634/5355 -f 1846/6634/5355 1823/6603/5334 1847/6635/5356 -f 1847/6635/5356 1824/6604/5335 1848/6636/5357 -f 1848/6636/5357 1825/6606/5336 1849/6637/5358 -f 1849/6637/5358 1826/6608/5337 1850/6638/5359 -f 1850/6639/5359 1827/6611/5338 1851/6640/5360 -f 1851/6640/5360 1828/6641/5339 1852/6642/5361 -f 1852/6642/5361 1829/6643/5340 1853/6644/5362 -f 1853/6645/5362 1830/6615/5341 1854/6646/5363 -f 1854/6646/5363 1807/828/715 1831/827/714 -f 1855/833/720 1808/826/713 1856/831/718 -f 1856/831/718 1809/6597/717 1857/6647/722 -f 1809/830/717 1858/836/723 1857/835/722 -f 1832/6617/716 1859/6648/5364 1858/6649/723 -f 1833/6616/5342 1860/6650/5365 1859/6648/5364 -f 1834/6618/5343 1861/6651/5366 1860/6650/5365 -f 1835/6619/5344 1862/6652/5367 1861/6651/5366 -f 1836/6620/5345 1863/838/725 1862/6652/5367 -f 1837/6621/5346 1864/839/726 1863/838/725 -f 1838/6622/5347 1865/6653/5368 1864/839/726 -f 1839/6624/5348 1866/6654/5369 1865/6653/5368 -f 1840/6625/5349 1867/6655/5370 1866/6656/5369 -f 1841/6627/5350 1868/6657/5371 1867/6655/5370 -f 1869/6658/5372 1846/6634/5355 1870/6659/5373 -f 1843/6629/5352 1868/6657/5371 1842/6628/5351 -f 1845/6632/5354 1869/6660/5372 1844/6631/5353 -f 1870/6659/5373 1847/6635/5356 1871/6661/5374 -f 1871/6661/5374 1848/6636/5357 1872/6662/5375 -f 1872/6662/5375 1849/6637/5358 1873/6663/5376 -f 1873/6663/5376 1850/6638/5359 1874/6664/729 -f 1874/842/729 1851/6640/5360 1875/840/727 -f 1875/840/727 1852/6642/5361 1876/6665/5377 -f 1876/6665/5377 1853/6644/5362 1877/6666/5378 -f 1877/6666/5378 1854/6667/5363 1878/6668/5379 -f 1878/6669/5379 1831/827/714 1855/833/720 -f 1858/6649/723 1881/846/732 1880/845/721 -f 1859/6648/5364 1882/848/734 1881/846/732 -f 1860/6650/5365 1883/850/736 1882/848/734 -f 1861/6651/5366 1884/852/738 1883/850/736 -f 1862/6652/5367 1885/837/724 1884/852/738 -f 1864/839/726 1886/855/741 1885/837/724 -f 1865/6653/5368 1887/857/743 1886/855/741 -f 1866/6654/5369 1888/859/745 1887/857/743 -f 1867/6655/5370 1889/6670/747 1888/6671/745 -f 1843/6629/5352 1889/6670/747 1868/6657/5371 -f 1843/6629/5352 1891/6672/5380 1890/6673/5381 -f 1891/6672/5380 1869/6674/5372 1892/6675/750 -f 1892/864/750 1870/6659/5373 1893/862/748 -f 1893/862/748 1871/6661/5374 1894/865/751 -f 1894/865/751 1872/6662/5375 1895/867/753 -f 1895/867/753 1873/6663/5376 1896/869/755 -f 1896/869/755 1874/6664/729 1897/871/728 -f 1897/841/728 1876/6665/5377 1898/873/758 -f 1898/873/758 1877/6666/5378 1899/875/760 -f 1899/875/760 1878/6668/5379 1900/877/762 -f 1900/6676/762 1855/833/720 1879/832/719 -f 1856/831/718 1902/6677/730 1901/881/765 -f 1856/831/718 1901/881/765 1879/832/719 -f 1921/6678/764 1922/882/766 1944/6679/5382 -f 1903/844/731 1902/6680/730 1880/845/721 -f 1904/847/733 1903/844/731 1881/846/732 -f 1905/849/735 1904/847/733 1882/848/734 -f 1906/851/737 1905/849/735 1883/850/736 -f 1907/853/739 1906/851/737 1884/852/738 -f 1908/854/740 1907/853/739 1885/837/724 -f 1909/856/742 1908/854/740 1886/855/741 -f 1910/858/744 1909/856/742 1887/857/743 -f 1911/860/746 1910/858/744 1888/859/745 -f 1912/6681/5383 1911/6682/746 1889/6670/747 -f 1890/6673/5381 1912/6681/5383 1889/6670/747 -f 1892/864/750 1913/863/749 1891/6683/5380 -f 1893/862/748 1914/866/752 1913/863/749 -f 1894/865/751 1915/868/754 1914/866/752 -f 1895/867/753 1916/870/756 1915/868/754 -f 1896/869/755 1917/872/757 1916/870/756 -f 1897/841/728 1918/874/759 1917/885/757 -f 1898/873/758 1919/876/761 1918/874/759 -f 1899/875/760 1920/878/763 1919/876/761 -f 1900/877/762 1921/880/764 1920/878/763 -f 1901/881/765 1923/887/770 1922/882/766 -f 1902/6680/730 1924/6684/5384 1923/6685/770 -f 1903/844/731 1925/6686/5385 1924/6684/5384 -f 1904/847/733 1926/6687/5386 1925/6686/5385 -f 1905/849/735 1927/6688/5387 1926/6687/5386 -f 1906/851/737 1928/883/767 1927/6688/5387 -f 1908/854/740 1930/6689/5388 1929/6690/5389 -f 1929/6690/5389 1928/883/767 1908/854/740 -f 1909/856/742 1931/6691/5390 1930/6689/5388 -f 1910/858/744 1932/6692/5391 1931/6691/5390 -f 1911/6682/746 1933/6693/5392 1932/6694/5391 -f 1890/6673/5381 1933/6693/5392 1912/6681/5383 -f 1890/6673/5381 1935/6695/5393 1934/6696/5394 -f 1935/6697/5393 1913/863/749 1936/6698/5395 -f 1936/6698/5395 1914/866/752 1937/6699/5396 -f 1937/6699/5396 1915/868/754 1938/6700/5397 -f 1938/6700/5397 1916/870/756 1939/6701/5398 -f 1939/6701/5398 1917/872/757 1940/6702/768 -f 1941/6703/5399 1919/876/761 1942/6704/5400 -f 1918/874/759 1941/6703/5399 1940/884/768 -f 1942/6704/5400 1920/878/763 1943/6705/5401 -f 1943/6705/5401 1921/880/764 1944/6706/5382 -f 1966/6707/5402 1922/882/766 1945/6708/5403 -f 1923/6685/770 1947/6709/5404 1946/6710/769 -f 1924/6684/5384 1948/6711/5405 1947/6709/5404 -f 1925/6686/5385 1949/6712/5406 1948/6711/5405 -f 1926/6687/5386 1950/6713/5407 1949/6712/5406 -f 1927/6688/5387 1951/6714/5408 1950/6713/5407 -f 1928/883/767 1952/6715/5409 1951/6714/5408 -f 1929/6690/5389 1953/6716/5410 1952/6715/5409 -f 1930/6689/5388 1954/6717/5411 1953/6716/5410 -f 1931/6691/5390 1955/6718/5412 1954/6717/5411 -f 1932/6694/5391 1956/6719/5413 1955/6720/5412 -f 1957/6721/5414 1936/6698/5395 1958/6722/5415 -f 1934/6696/5394 1956/6719/5413 1933/6693/5392 -f 1935/6695/5393 1957/6723/5414 1934/6696/5394 -f 1958/6722/5415 1937/6699/5396 1959/6724/5416 -f 1959/6724/5416 1938/6700/5397 1960/6725/5417 -f 1960/6725/5417 1939/6701/5398 1961/6726/5418 -f 1961/6726/5418 1940/6702/768 1962/6727/5419 -f 1962/6728/5419 1941/6703/5399 1963/6729/5420 -f 1963/6729/5420 1942/6704/5400 1964/6730/5421 -f 1964/6730/5421 1943/6705/5401 1965/6731/5422 -f 1965/6731/5422 1944/6706/5382 1966/6732/5402 -f 1990/890/773 1945/6733/5403 1967/888/771 -f 1945/6708/5403 1968/892/775 1967/6734/771 -f 1946/886/769 1968/892/775 1922/882/766 -f 1946/886/769 1970/6735/5423 1969/893/776 -f 1947/6709/5404 1971/6736/5424 1970/6737/5423 -f 1948/6711/5405 1972/6738/5425 1971/6736/5424 -f 1949/6712/5406 1973/895/778 1972/6738/5425 -f 1950/6713/5407 1974/896/779 1973/895/778 -f 1951/6714/5408 1975/898/781 1974/896/779 -f 1952/6715/5409 1976/900/783 1975/898/781 -f 1953/6716/5410 1977/902/785 1976/900/783 -f 1954/6717/5411 1978/904/787 1977/902/785 -f 1955/6739/5412 1979/907/789 1978/906/787 -f 1979/910/789 1934/6696/5394 1980/908/790 -f 1980/908/790 1957/6723/5414 1981/6740/5426 -f 1981/6740/5426 1958/6741/5415 1982/6742/5427 -f 1982/6743/5427 1959/6724/5416 1983/6744/5428 -f 1983/6744/5428 1960/6725/5417 1984/6745/5429 -f 1984/6745/5429 1961/6726/5418 1985/913/793 -f 1985/913/793 1962/6727/5419 1986/911/791 -f 1986/916/791 1963/6729/5420 1987/914/794 -f 1987/914/794 1964/6730/5421 1988/917/796 -f 1988/917/796 1965/6731/5422 1989/919/798 -f 1989/919/798 1966/6732/5402 1990/921/773 -f 1969/893/776 1993/925/803 1992/891/774 -f 1968/6746/775 1991/889/772 1967/888/771 -f 1970/6737/5423 1994/6747/805 1993/6748/803 -f 1971/6736/5424 1995/6749/807 1994/6747/805 -f 1972/6738/5425 1996/894/777 1995/6749/807 -f 2002/932/809 1982/6742/5427 2003/934/811 -f 1997/897/780 1996/894/777 1974/896/779 -f 1998/899/782 1997/897/780 1975/898/781 -f 1999/901/784 1998/899/782 1976/900/783 -f 2000/903/786 1999/901/784 1977/902/785 -f 2001/6750/788 2000/903/786 1978/904/787 -f 1981/6740/5426 2002/932/809 1980/908/790 -f 2003/934/811 1983/6751/5428 2004/936/813 -f 2004/6752/813 1984/6745/5429 2005/6753/815 -f 2005/6753/815 1985/913/793 2006/912/792 -f 1991/889/772 2012/6754/801 2011/6755/5430 -f 1986/911/791 2007/6756/795 2006/912/792 -f 1987/914/794 2008/918/797 2007/915/795 -f 1988/917/796 2009/920/799 2008/918/797 -f 1989/919/798 2010/922/800 2009/920/799 -f 1990/921/773 1991/6757/772 2010/922/800 -f 1996/894/777 2017/6758/5431 2016/6759/808 -f 2013/924/802 2012/923/801 1992/891/774 -f 2014/926/804 2013/924/802 1993/925/803 -f 2015/928/806 2014/926/804 1994/927/805 -f 2016/930/808 2015/928/806 1995/929/807 -f 1997/897/780 2019/6760/5432 2018/6761/5433 -f 2018/6761/5433 2017/6758/5431 1997/897/780 -f 1998/899/782 2020/6762/5434 2019/6760/5432 -f 1999/901/784 2021/6763/5435 2020/6762/5434 -f 2000/903/786 2022/6764/5436 2021/6763/5435 -f 2001/905/788 2023/6765/810 2022/6766/5436 -f 2027/6767/817 2007/6756/795 2028/6768/5437 -f 2002/932/809 2024/935/812 2023/933/810 -f 2003/934/811 2025/937/814 2024/935/812 -f 2004/936/813 2026/939/816 2025/937/814 -f 2005/938/815 2027/941/817 2026/939/816 -f 2029/6769/5438 2008/918/797 2030/6770/5439 -f 2007/915/795 2029/6769/5438 2028/6771/5437 -f 2030/6770/5439 2009/920/799 2031/6772/5440 -f 2031/6772/5440 2010/922/800 2032/6773/5441 -f 2032/6773/5441 1991/6757/772 2011/6774/5430 -f 2012/6754/801 2033/6775/5442 2011/6755/5430 -f 2012/923/801 2036/944/820 2035/943/819 -f 2035/943/819 2034/6776/5443 2012/923/801 -f 2013/924/802 2037/6777/5444 2036/944/820 -f 2014/926/804 2038/6778/5445 2037/6777/5444 -f 2015/928/806 2039/6779/5446 2038/6778/5445 -f 2016/930/808 2040/6780/5447 2039/6779/5446 -f 2017/6781/5431 2041/6782/5448 2040/6780/5447 -f 2018/6783/5433 2042/6784/5449 2041/6782/5448 -f 2019/6785/5432 2043/6786/5450 2042/6784/5449 -f 2020/6762/5434 2044/6787/5451 2043/6788/5450 -f 2021/6789/5435 2045/947/823 2044/6790/5451 -f 2045/947/823 2023/6765/810 2046/945/821 -f 2047/6791/5452 2024/935/812 2048/6792/5453 -f 2023/933/810 2047/6791/5452 2046/6793/821 -f 2048/6792/5453 2025/937/814 2049/6794/5454 -f 2049/6794/5454 2026/939/816 2050/6795/5455 -f 2050/6795/5455 2027/941/817 2051/6796/5456 -f 2051/6796/5456 2028/6797/5437 2052/6798/5457 -f 2052/6799/5457 2029/6800/5438 2053/6801/5458 -f 2053/6801/5458 2030/6802/5439 2054/6803/5459 -f 2054/6803/5459 2031/6804/5440 2055/6805/5460 -f 2055/6806/5460 2032/6773/5441 2056/6807/5461 -f 2056/6808/5461 2011/6755/5430 2033/6775/5442 -f 2036/944/820 2059/6809/5462 2058/942/818 -f 2034/6810/5443 2057/6811/5463 2033/6775/5442 -f 2037/6777/5444 2060/6812/5464 2059/6809/5462 -f 2038/6778/5445 2061/6813/5465 2060/6812/5464 -f 2039/6779/5446 2062/949/825 2061/6813/5465 -f 2040/6780/5447 2063/950/826 2062/949/825 -f 2041/6782/5448 2064/6814/5466 2063/950/826 -f 2042/6784/5449 2065/6815/5467 2064/6814/5466 -f 2043/6786/5450 2066/6816/5468 2065/6815/5467 -f 2044/6790/5451 2067/946/822 2066/6817/5468 -f 2068/6818/5469 2049/6794/5454 2069/6819/5470 -f 2048/6792/5453 2068/6818/5469 2047/6791/5452 -f 2069/6819/5470 2050/6795/5455 2070/6820/5471 -f 2070/6820/5471 2051/6796/5456 2071/6821/5472 -f 2071/6821/5472 2052/6798/5457 2072/6822/829 -f 2072/953/829 2053/6801/5458 2073/951/827 -f 2073/951/827 2054/6803/5459 2074/6823/5473 -f 2074/6823/5473 2055/6805/5460 2075/6824/5474 -f 2075/6824/5474 2056/6825/5461 2076/6826/5475 -f 2076/6827/5475 2033/6775/5442 2057/6811/5463 -f 2057/6811/5463 2078/954/830 2077/956/832 -f 2035/943/819 2078/962/830 2034/6776/5443 -f 2058/942/818 2079/960/836 2035/943/819 -f 2058/942/818 2081/981/856 2080/983/858 -f 2059/6809/5462 2082/984/859 2081/981/856 -f 2060/6812/5464 2083/6828/5476 2082/984/859 -f 2062/949/825 2083/6828/5476 2061/6813/5465 -f 2063/950/826 2085/6829/5477 2084/948/824 -f 2064/6814/5466 2086/6830/5478 2085/6829/5477 -f 2065/6815/5467 2087/6831/5479 2086/6830/5478 -f 2067/946/822 2087/6832/5479 2066/6817/5468 -f 2067/946/822 2090/975/850 2089/974/849 -f 2067/946/822 2089/974/849 2088/970/845 -f 2046/6793/821 2091/958/834 2090/6833/850 -f 2091/958/834 2068/6818/5469 2092/959/835 -f 2092/959/835 2069/6819/5470 2093/6834/5480 -f 2093/6834/5480 2070/6820/5471 2094/6835/5481 -f 2094/6835/5481 2071/6821/5472 2095/6836/5482 -f 2095/6836/5482 2072/6822/829 2096/6837/828 -f 2073/951/827 2097/6838/5483 2096/952/828 -f 2097/6838/5483 2075/6824/5474 2098/6839/5484 -f 2098/6839/5484 2076/6826/5475 2099/6840/5485 -f 2099/6841/5485 2057/6811/5463 2077/956/832 -f 2083/6828/5476 2117/965/840 2116/6842/5486 -f 2083/6828/5476 2115/986/861 2082/984/859 -f 2083/6828/5476 2116/6842/5486 2115/986/861 -f 2101/963/838 2085/6829/5477 2102/966/841 -f 2084/948/824 2101/963/838 2117/965/840 -f 2102/966/841 2086/6830/5478 2103/968/843 -f 2103/968/843 2087/6831/5479 2104/989/847 -f 2092/959/835 2106/977/852 2105/957/833 -f 2088/970/845 2104/972/847 2087/6832/5479 -f 2093/6834/5480 2107/978/853 2106/977/852 -f 2094/6835/5481 2108/980/855 2107/978/853 -f 2095/6836/5482 2109/6843/5487 2108/980/855 -f 2096/952/828 2110/6844/5488 2109/6845/5487 -f 2097/6838/5483 2111/6846/5489 2110/6844/5488 -f 2098/6839/5484 2112/6847/5490 2111/6846/5489 -f 2099/6841/5485 2100/955/831 2112/6848/5490 -f 2100/955/831 2114/6849/837 2113/990/863 -f 2100/955/831 2113/990/863 2135/992/865 -f 2090/6833/850 2123/1006/878 2122/1005/848 -f 2101/963/838 2119/967/842 2118/964/839 -f 2102/966/841 2120/969/844 2119/967/842 -f 2105/957/833 2123/1006/878 2091/958/834 -f 2106/977/852 2124/1011/883 2105/957/833 -f 2108/980/855 2129/6850/5491 2128/997/870 -f 2126/976/851 2125/1012/884 2106/977/852 -f 2127/979/854 2126/976/851 2107/978/853 -f 2128/997/870 2127/979/854 2108/980/855 -f 2109/6845/5487 2131/6851/5492 2130/6852/5493 -f 2130/6852/5493 2129/6853/5491 2109/6845/5487 -f 2110/6844/5488 2132/6854/5494 2131/6851/5492 -f 2111/6846/5489 2133/6855/5495 2132/6854/5494 -f 2112/6848/5490 2135/992/865 2134/6856/5496 -f 2134/6857/5496 2133/6855/5495 2112/6847/5490 -f 2138/993/866 2104/989/847 2139/988/862 -f 2103/968/843 2138/993/866 2120/969/844 -f 2141/1000/873 2116/6842/5486 2142/1033/902 -f 2088/970/845 2140/1003/876 2121/971/846 -f 2089/974/849 2140/1003/876 2088/970/845 -f 2115/986/861 2141/1000/873 2137/985/860 -f 2142/1033/902 2117/965/840 2143/1035/904 -f 2143/1035/904 2118/964/839 2144/1037/906 -f 2144/1037/906 2119/967/842 2145/1039/908 -f 2145/1039/908 2120/969/844 2146/994/867 -f 2128/997/870 2150/1022/892 2149/1020/890 -f 2147/995/868 2125/1012/884 2126/976/851 -f 2148/996/869 2147/995/868 2127/979/854 -f 2149/1020/890 2148/996/869 2128/997/870 -f 2129/6853/5491 2151/1025/894 2150/1024/892 -f 2130/6852/5493 2152/1027/896 2151/1025/894 -f 2131/6851/5492 2153/1029/898 2152/1027/896 -f 2132/6854/5494 2154/1031/900 2153/1029/898 -f 2133/6855/5495 2155/6858/5497 2154/1031/900 -f 2134/6856/5496 2156/991/864 2155/6859/5497 -f 2156/991/864 2165/1015/886 2179/1055/924 -f 2114/6849/837 2157/1014/871 2113/990/863 -f 2080/983/858 2158/999/872 2079/960/836 -f 2122/973/848 2159/1002/875 2089/974/849 -f 2124/1011/883 2160/1004/877 2123/1006/878 -f 2079/960/836 2161/1007/879 2157/998/871 -f 2121/987/846 2162/1016/880 2139/988/862 -f 2160/1004/877 2163/6860/881 2122/1005/848 -f 2185/6861/5498 2139/988/862 2168/1017/887 -f 2136/982/857 2166/1032/901 2158/999/872 -f 2137/985/860 2167/1001/874 2136/982/857 -f 2138/993/866 2185/6861/5498 2146/994/867 -f 2154/1031/900 2178/6862/923 2177/1030/899 -f 2140/1003/876 2169/1044/913 2162/1008/880 -f 2159/1002/875 2169/1044/913 2140/1003/876 -f 2164/1010/882 2160/1004/877 2124/1011/883 -f 2172/1019/889 2171/1018/888 2148/996/869 -f 2173/1021/891 2172/1019/889 2149/1020/890 -f 2174/1023/893 2173/1067/891 2150/1024/892 -f 2175/1026/895 2174/1023/893 2151/1025/894 -f 2176/1028/897 2175/1026/895 2152/1027/896 -f 2177/1030/899 2176/1028/897 2153/1029/898 -f 2155/6859/5497 2179/1055/924 2178/1054/923 -f 2163/1009/881 2187/1043/912 2159/1002/875 -f 2141/1000/873 2206/1079/942 2167/1001/874 -f 2141/1000/873 2180/1034/903 2206/1079/942 -f 2142/1033/902 2181/1036/905 2180/1034/903 -f 2143/1035/904 2182/1038/907 2181/1036/905 -f 2144/1037/906 2183/1040/909 2182/1038/907 -f 2145/1039/908 2184/1041/910 2183/1040/909 -f 2146/994/867 2185/6861/5498 2184/1041/910 -f 2158/999/872 2186/1042/911 2161/1007/879 -f 2167/1001/874 2192/1060/928 2166/1032/901 -f 2167/1001/874 2206/1079/942 2192/1060/928 -f 2160/1004/877 2188/1064/917 2163/6860/881 -f 2202/1053/922 2165/1015/886 2190/1058/926 -f 2165/1015/886 2191/1056/914 2190/1058/926 -f 2185/6861/5498 2194/1046/915 2193/6863/5499 -f 2166/1032/901 2192/1060/928 2186/1042/911 -f 2185/6861/5498 2193/6863/5499 2184/1041/910 -f 2194/6864/915 2169/1044/913 2195/1047/916 -f 2176/1028/897 2201/1070/936 2200/1052/921 -f 2160/1004/877 2196/1049/918 2189/1065/932 -f 2197/1050/919 2196/1049/918 2164/1010/882 -f 2198/1051/920 2197/1050/919 2170/1013/885 -f 2172/1019/889 2198/1051/920 2171/1018/888 -f 2173/1021/891 2199/1087/950 2172/1019/889 -f 2177/1030/899 2202/1072/922 2201/1070/936 -f 2220/6865/939 2190/1058/926 2203/1057/925 -f 2207/1095/956 2182/1038/907 2208/1062/930 -f 2191/1045/914 2204/1059/927 2203/6866/925 -f 2186/1042/911 2205/1061/929 2204/1059/927 -f 2192/1060/928 2206/1079/942 2205/1061/929 -f 2181/1036/905 2207/1095/956 2180/1034/903 -f 2208/1062/930 2184/1041/910 2209/1082/945 -f 2209/1082/945 2193/6863/5499 2210/1080/943 -f 2210/1080/943 2194/1046/915 2211/6867/5500 -f 2211/6868/5500 2195/1047/916 2212/6869/5501 -f 2195/1047/916 2213/6870/931 2212/6869/5501 -f 2189/1065/932 2214/1084/947 2213/1063/931 -f 2196/1049/918 2215/1085/948 2214/1084/947 -f 2224/1093/954 2180/1034/903 2225/1096/957 -f 2198/1051/920 2215/1085/948 2197/1050/919 -f 2216/1088/933 2199/1087/950 2173/1021/891 -f 2217/1068/934 2216/1066/933 2175/1026/895 -f 2218/1069/935 2217/1068/934 2200/1052/921 -f 2219/1071/937 2218/1069/935 2201/1070/936 -f 2220/6865/939 2219/6871/937 2202/1053/922 -f 2203/1073/925 2222/1077/940 2221/1074/938 -f 2204/1059/927 2223/1078/941 2222/6872/940 -f 2206/1079/942 2224/1093/954 2223/1078/941 -f 2226/1097/958 2209/1082/945 2227/1081/944 -f 2208/1062/930 2226/1097/958 2207/1095/956 -f 2227/1102/944 2211/6873/5500 2228/1100/961 -f 2228/1100/961 2212/6874/5501 2229/1103/963 -f 2212/6874/5501 2230/6875/5502 2229/1103/963 -f 2213/1063/931 2231/1083/946 2230/6876/5502 -f 2215/1085/948 2232/1118/976 2231/1083/946 -f 2198/1051/920 2233/1106/966 2232/1118/976 -f 2216/1066/933 2235/1089/951 2234/1108/949 -f 2199/1087/950 2233/1106/966 2172/1019/889 -f 2218/6877/935 2236/1111/969 2235/1110/951 -f 2219/6878/937 2221/1074/938 2236/1111/969 -f 2229/1103/963 2244/1129/985 2258/6879/5503 -f 2237/1090/952 2252/1112/970 2221/1074/938 -f 2222/1077/940 2238/1092/953 2237/1090/952 -f 2223/1078/941 2239/1094/955 2238/1122/953 -f 2207/1095/956 2240/1098/959 2225/1096/957 -f 2226/1097/958 2241/1135/990 2240/1098/959 -f 2226/1097/958 2254/1099/960 2241/1135/990 -f 2227/1081/944 2255/1126/973 2254/1099/960 -f 2227/1102/944 2242/1101/962 2255/1115/973 -f 2228/1100/961 2243/1104/964 2242/1101/962 -f 2229/1103/963 2258/6879/5503 2243/1104/964 -f 2230/6875/5502 2245/1130/986 2244/1129/985 -f 2247/6880/965 2271/1151/1004 2270/1161/1013 -f 2246/1133/988 2245/1132/986 2231/1083/946 -f 2232/1118/976 2246/1133/988 2231/1083/946 -f 2248/6881/5504 2247/6880/965 2234/1108/949 -f 2249/1107/967 2248/6881/5504 2234/1108/949 -f 2250/6882/5505 2249/1107/967 2235/1089/951 -f 2251/1109/968 2250/6883/5505 2235/1110/951 -f 2252/1112/970 2251/1109/968 2236/1111/969 -f 2224/1093/954 2283/1123/980 2239/1094/955 -f 2224/1093/954 2253/1113/971 2283/1123/980 -f 2242/1101/962 2257/1116/974 2256/1114/972 -f 2243/1104/964 2258/6879/5503 2257/1116/974 -f 2237/1090/952 2261/1120/978 2260/1119/977 -f 2238/1092/953 2262/1142/979 2261/1120/978 -f 2239/1094/955 2263/1124/981 2262/1121/979 -f 2225/1096/957 2264/1125/982 2253/1113/971 -f 2254/1099/960 2265/1127/983 2241/1135/990 -f 2258/6879/5503 2266/1138/993 2257/1116/974 -f 2244/1129/985 2266/1138/993 2258/6879/5503 -f 2268/1131/987 2267/6884/984 2245/1132/986 -f 2269/1134/989 2268/1131/987 2246/1133/988 -f 2259/1117/975 2269/1134/989 2232/1118/976 -f 2248/6881/5504 2272/1152/1005 2271/1151/1004 -f 2249/1107/967 2273/1153/1006 2272/1152/1005 -f 2250/6883/5505 2274/6885/5506 2273/6886/1006 -f 2251/1109/968 2260/1119/977 2274/6885/5506 -f 2277/1137/992 2257/1116/974 2278/1139/994 -f 2240/1098/959 2275/1156/1009 2264/1125/982 -f 2240/1098/959 2276/1136/991 2275/1156/1009 -f 2255/1126/973 2277/1145/992 2265/1127/983 -f 2233/1106/966 2279/1176/1027 2305/1140/995 -f 2267/1128/984 2266/1138/993 2244/1129/985 -f 2266/1138/993 2288/6887/5507 2287/6888/5508 -f 2270/6889/1013 2279/1176/1027 2247/1105/965 -f 2280/1141/996 2292/1167/1018 2260/1119/977 -f 2261/1120/978 2281/1143/997 2280/1141/996 -f 2262/1121/979 2282/1144/998 2281/6890/997 -f 2263/1124/981 2283/1123/980 2282/1144/998 -f 2265/1127/983 2284/1146/999 2276/1136/991 -f 2277/1137/992 2285/1147/1000 2284/6891/999 -f 2278/1139/994 2286/1148/1001 2285/1147/1000 -f 2266/1138/993 2287/6888/5508 2286/1148/1001 -f 2267/6884/984 2289/1149/1002 2288/6892/5507 -f 2273/6886/1006 2291/1166/1016 2290/6893/1003 -f 2259/1117/975 2289/1149/1002 2269/1134/989 -f 2274/6885/5506 2292/1167/1018 2291/1166/1016 -f 2292/1167/1018 2293/6894/5509 2309/1165/1017 -f 2293/6894/5509 2281/1143/997 2294/6895/5510 -f 2294/6896/5510 2282/1144/998 2295/1170/1021 -f 2295/1170/1021 2283/1123/980 2296/1168/1019 -f 2296/1168/1019 2253/1113/971 2297/1154/1007 -f 2301/1159/1011 2287/6888/5508 2302/6897/5511 -f 2275/1156/1009 2298/1155/1008 2264/1125/982 -f 2284/1146/999 2299/6898/5512 2298/1155/1008 -f 2284/1146/999 2300/1158/1010 2299/6898/5512 -f 2285/1147/1000 2301/1159/1011 2300/6899/1010 -f 2287/6888/5508 2303/6900/1023 2302/6897/5511 -f 2288/6892/5507 2304/1173/1024 2303/1172/1023 -f 2289/1149/1002 2305/1140/995 2304/1173/1024 -f 2309/6901/1017 2310/6902/5513 2330/6903/5514 -f 2306/1190/1012 2279/1176/1027 2270/6889/1013 -f 2307/1162/1014 2306/1160/1012 2271/1151/1004 -f 2308/1163/1015 2307/1162/1014 2290/1150/1003 -f 2309/1165/1017 2308/6904/1015 2291/1166/1016 -f 2311/6905/5515 2294/6896/5510 2312/1179/1030 -f 2293/6906/5509 2311/6907/5515 2310/6902/5513 -f 2312/1179/1030 2295/1170/1021 2313/1169/1020 -f 2314/1180/1031 2298/1155/1008 2315/1184/1035 -f 2264/1125/982 2314/1180/1031 2297/1154/1007 -f 2316/1182/1033 2299/6898/5512 2317/6908/5516 -f 2298/1155/1008 2316/1182/1033 2315/1184/1035 -f 2317/6908/5516 2300/1158/1010 2318/6909/5517 -f 2318/6909/5517 2301/6910/1011 2319/6911/5518 -f 2320/6912/5519 2302/6913/5511 2321/6914/5520 -f 2301/6915/1011 2320/6912/5519 2319/6916/5518 -f 2302/6917/5511 2322/1197/1047 2321/6918/5520 -f 2307/1162/1014 2328/1192/1042 2327/1177/1028 -f 2323/1171/1022 2322/1197/1047 2303/1172/1023 -f 2324/1186/1037 2323/1171/1022 2304/1173/1024 -f 2325/1174/1025 2324/1186/1037 2304/1173/1024 -f 2306/1190/1012 2326/1175/1026 2279/1176/1027 -f 2308/1163/1015 2329/1193/1043 2328/1192/1042 -f 2349/1228/1078 2315/1184/1035 2331/1183/1034 -f 2330/6903/5514 2329/6919/1043 2309/6901/1017 -f 2332/1194/1044 2318/6909/5517 2333/1206/1056 -f 2317/6908/5516 2332/1194/1044 2316/1182/1033 -f 2318/6909/5517 2334/1207/1057 2333/1206/1056 -f 2319/6916/5518 2335/6920/5521 2334/6921/1057 -f 2320/6912/5519 2336/6922/5522 2335/6920/5521 -f 2306/1160/1012 2342/1204/1054 2341/6923/1040 -f 2337/6924/1059 2336/6922/5522 2321/6914/5520 -f 2322/1197/1047 2337/1209/1059 2321/6918/5520 -f 2340/1188/1039 2339/1187/1038 2325/1174/1025 -f 2326/1175/1026 2340/1188/1039 2305/1140/995 -f 2330/6903/5514 2345/6925/5523 2355/6926/5524 -f 2343/1200/1050 2342/1204/1054 2327/1177/1028 -f 2328/1192/1042 2343/1200/1050 2327/1177/1028 -f 2330/6903/5514 2344/6927/1041 2329/6919/1043 -f 2345/6925/5523 2311/6907/5515 2346/6928/5525 -f 2311/6905/5515 2347/1178/1029 2346/6929/5525 -f 2344/1191/1041 2353/1199/1049 2328/1192/1042 -f 2297/1154/1007 2348/1181/1032 2296/1168/1019 -f 2314/1180/1031 2349/1228/1078 2348/1181/1032 -f 2316/1182/1033 2350/1195/1045 2331/1183/1034 -f 2339/1187/1038 2338/1185/1036 2324/1186/1037 -f 2341/1189/1040 2352/1198/1048 2326/1175/1026 -f 2344/6927/1041 2355/6926/5524 2354/6930/1066 -f 2335/6920/5521 2361/1231/1081 2334/6921/1057 -f 2296/1168/1019 2359/1225/1075 2313/1169/1020 -f 2296/1168/1019 2356/1201/1051 2359/1225/1075 -f 2322/1197/1047 2357/1202/1052 2337/1209/1059 -f 2352/1198/1048 2365/1210/1060 2340/1188/1039 -f 2353/1199/1049 2358/1203/1053 2343/1200/1050 -f 2313/1169/1020 2369/1238/1088 2347/1178/1029 -f 2313/1169/1020 2359/1225/1075 2369/1238/1088 -f 2332/1194/1044 2379/1246/1096 2350/1195/1045 -f 2361/6931/1081 2360/1205/1055 2334/1207/1057 -f 2362/1229/1079 2336/6922/5522 2363/6932/5526 -f 2336/6922/5522 2364/6933/1058 2363/6932/5526 -f 2355/6926/5524 2366/1223/1073 2376/1236/1086 -f 2339/1187/1038 2371/1212/1062 2338/1185/1036 -f 2366/1223/1073 2346/6928/5525 2367/1221/1071 -f 2346/6929/5525 2368/1244/1094 2367/6934/1071 -f 2354/6930/1066 2376/1236/1086 2375/1235/1085 -f 2347/1178/1029 2369/1238/1088 2368/1244/1094 -f 2331/1183/1034 2377/1226/1076 2349/1228/1078 -f 2332/1194/1044 2370/1211/1061 2379/1246/1096 -f 2360/1205/1055 2370/1211/1061 2333/1206/1056 -f 2365/1210/1060 2372/1213/1063 2339/1187/1038 -f 2374/1215/1065 2373/1214/1064 2353/1199/1049 -f 2375/6935/1085 2374/1215/1065 2354/1216/1066 -f 2341/6923/1040 2382/1220/1070 2381/1256/1069 -f 2331/1183/1034 2378/1217/1067 2377/1226/1076 -f 2350/1195/1045 2379/1246/1096 2378/1217/1067 -f 2376/1236/1086 2383/1222/1072 2401/1234/1084 -f 2373/1214/1064 2382/1220/1070 2358/1203/1053 -f 2360/1205/1055 2386/1249/1080 2385/1247/1097 -f 2348/1181/1032 2390/1266/1111 2356/1201/1051 -f 2349/1228/1078 2391/1227/1077 2348/1181/1032 -f 2360/1205/1055 2385/1247/1097 2370/1211/1061 -f 2393/1251/1100 2363/6932/5526 2394/1270/1115 -f 2380/1218/1068 2387/1232/1082 2357/1202/1052 -f 2371/1212/1062 2380/1218/1068 2351/1196/1046 -f 2381/1219/1069 2388/1233/1083 2352/1198/1048 -f 2356/1201/1051 2390/1266/1111 2384/1224/1074 -f 2348/1181/1032 2391/1227/1077 2390/1266/1111 -f 2379/1246/1096 2392/1245/1095 2378/1217/1067 -f 2362/1229/1079 2393/1251/1100 2386/1230/1080 -f 2363/6932/5526 2395/1271/1102 2394/1270/1115 -f 2373/1214/1064 2400/1258/1105 2399/1241/1091 -f 2387/1232/1082 2395/1254/1102 2364/1208/1058 -f 2372/1213/1063 2397/1273/1117 2371/1212/1062 -f 2388/1233/1083 2398/1240/1090 2365/1210/1060 -f 2374/1215/1065 2401/1259/1084 2400/1258/1105 -f 2401/1234/1084 2402/1242/1092 2409/1261/1107 -f 2402/1263/1092 2368/1244/1094 2403/1243/1093 -f 2416/1283/1126 2404/1248/1098 2417/1281/1124 -f 2370/1211/1061 2404/1248/1098 2392/1245/1095 -f 2385/1247/1097 2405/1250/1099 2404/1248/1098 -f 2396/1239/1089 2406/1253/1101 2387/1232/1082 -f 2397/1273/1117 2396/1239/1089 2371/1212/1062 -f 2398/1240/1090 2397/1273/1117 2372/1213/1063 -f 2399/1241/1091 2407/1255/1103 2382/1220/1070 -f 2409/1261/1107 2408/6936/1104 2401/1234/1084 -f 2402/1242/1092 2411/1276/1108 2410/1260/1106 -f 2403/1243/1093 2412/1264/1109 2411/1262/1108 -f 2389/1237/1087 2413/1265/1110 2412/1264/1109 -f 2390/1266/1111 2414/1267/1112 2413/1265/1110 -f 2377/1226/1076 2415/1268/1113 2414/1267/1112 -f 2392/1245/1095 2416/1283/1126 2415/1268/1113 -f 2417/1281/1124 2405/1250/1099 2418/6937/1129 -f 2405/1252/1099 2419/1284/1127 2418/1286/1129 -f 2393/1251/1100 2420/1269/1114 2419/1284/1127 -f 2397/1273/1117 2422/1274/1118 2421/1272/1116 -f 2406/1253/1101 2420/6938/1114 2395/1254/1102 -f 2421/1272/1116 2406/1253/1101 2396/1239/1089 -f 2388/1233/1083 2423/6939/1119 2422/1274/1118 -f 2407/1255/1103 2424/1290/1133 2423/1275/1119 -f 2399/1241/1091 2425/1291/1134 2424/1290/1133 -f 2400/1258/1105 2426/6940/1136 2425/1291/1134 -f 2408/6936/1104 2410/1260/1106 2426/1293/1136 -f 2431/1296/1139 2415/1268/1113 2432/1298/1141 -f 2427/1277/1120 2442/1292/1135 2410/1260/1106 -f 2411/1262/1108 2428/1278/1121 2427/6941/1120 -f 2412/1264/1109 2429/1279/1122 2428/1278/1121 -f 2413/1265/1110 2430/1280/1123 2429/1279/1122 -f 2414/1267/1112 2431/1296/1139 2430/1280/1123 -f 2432/1298/1141 2416/1283/1126 2433/1282/1125 -f 2433/1282/1125 2418/6937/1129 2434/6942/1128 -f 2419/1284/1127 2435/6943/5527 2434/1285/1128 -f 2420/6938/1114 2436/6944/5528 2435/6945/5527 -f 2422/1274/1118 2440/1308/1149 2439/1306/1147 -f 2437/1287/1130 2436/6944/5528 2406/1253/1101 -f 2438/1288/1131 2437/1287/1130 2421/1272/1116 -f 2439/1306/1147 2438/1288/1131 2422/1274/1118 -f 2423/1275/1119 2441/1289/1132 2440/1310/1149 -f 2425/1291/1134 2442/6946/1135 2441/1289/1132 -f 2427/6941/1120 2444/6947/5529 2459/6948/5530 -f 2427/1277/1120 2459/6949/5530 2443/1294/1137 -f 2444/6947/5529 2429/1279/1122 2445/1295/1138 -f 2450/6950/5531 2435/6943/5527 2451/6951/5532 -f 2430/1280/1123 2446/1297/1140 2445/1295/1138 -f 2431/1296/1139 2447/1299/1142 2446/1297/1140 -f 2432/1298/1141 2448/1300/1143 2447/1299/1142 -f 2433/1303/1125 2449/1302/1144 2448/6952/1143 -f 2434/1301/1128 2450/6953/5531 2449/1302/1144 -f 2435/6945/5527 2452/6954/5533 2451/6955/5532 -f 2436/6944/5528 2453/1304/1145 2452/6954/5533 -f 2441/6956/1132 2458/1317/1155 2457/1315/1153 -f 2454/1305/1146 2453/1304/1145 2438/1288/1131 -f 2455/1307/1148 2454/1305/1146 2439/1306/1147 -f 2456/1309/1150 2455/6957/1148 2440/1310/1149 -f 2457/1315/1153 2456/1313/1150 2441/6956/1132 -f 2460/6958/5534 2445/1295/1138 2461/6959/5535 -f 2443/1319/1137 2458/1317/1155 2442/6960/1135 -f 2459/6949/5530 2475/6961/1156 2443/1294/1137 -f 2444/6947/5529 2460/6958/5534 2459/6948/5530 -f 2461/6959/5535 2446/1297/1140 2462/6962/5536 -f 2462/6963/5536 2447/6964/1142 2463/6965/5537 -f 2463/6965/5537 2448/6952/1143 2464/6966/5538 -f 2464/6966/5538 2449/1302/1144 2465/6967/5539 -f 2465/6967/5539 2450/6953/5531 2466/1322/1159 -f 2466/6968/1159 2451/6951/5532 2467/6969/1157 -f 2451/6955/5532 2469/1325/1162 2468/1324/1161 -f 2468/6970/1161 2467/6969/1157 2451/6951/5532 -f 2452/6954/5533 2470/6971/5540 2469/1325/1162 -f 2453/1304/1145 2471/6972/5541 2470/6971/5540 -f 2454/6973/1146 2472/6974/1151 2471/6975/5541 -f 2475/1318/1156 2476/6976/1188 2492/6977/5542 -f 2473/1314/1152 2472/1311/1151 2456/1313/1150 -f 2474/1316/1154 2473/1314/1152 2457/1315/1153 -f 2475/1318/1156 2474/1316/1154 2458/1317/1155 -f 2476/1354/1188 2460/6958/5534 2477/1328/1165 -f 2477/1328/1165 2461/6959/5535 2478/1326/1163 -f 2479/1331/1168 2462/6963/5536 2480/1329/1166 -f 2461/6959/5535 2479/6978/1168 2478/1326/1163 -f 2480/1329/1166 2463/6965/5537 2481/1332/1169 -f 2481/1332/1169 2464/6966/5538 2482/1334/1171 -f 2482/1334/1171 2465/6967/5539 2483/1336/1173 -f 2483/1336/1173 2466/1322/1159 2484/1321/1158 -f 2469/1325/1162 2486/1339/1176 2485/1323/1160 -f 2470/6979/5540 2488/1342/1179 2487/1341/1178 -f 2487/6980/1178 2486/1339/1176 2470/6971/5540 -f 2471/6975/5541 2489/1344/1181 2488/1342/1179 -f 2472/1311/1151 2490/1347/1183 2489/1346/1181 -f 2473/1314/1152 2491/1349/1185 2490/1347/1183 -f 2474/1316/1154 2492/6977/5542 2491/1349/1185 -f 2476/6976/1188 2507/6981/5543 2492/6977/5542 -f 2494/6982/1164 2479/1331/1168 2495/1330/1167 -f 2498/1337/1174 2484/1321/1158 2499/1357/1191 -f 2480/1329/1166 2496/1333/1170 2495/1330/1167 -f 2481/1332/1169 2497/1335/1172 2496/1333/1170 -f 2482/1334/1171 2498/1337/1174 2497/1335/1172 -f 2499/1357/1191 2467/1320/1157 2500/1355/1189 -f 2500/6983/1189 2468/6970/1161 2501/6984/1207 -f 2486/6985/1176 2503/1340/1177 2502/6986/1175 -f 2485/1323/1160 2501/1375/1207 2468/1324/1161 -f 2491/1349/1185 2507/6981/5543 2506/1348/1184 -f 2504/1343/1180 2503/1340/1177 2488/1342/1179 -f 2505/1345/1182 2504/6987/1180 2489/1346/1181 -f 2506/1348/1184 2505/1345/1182 2490/1347/1183 -f 2509/1365/1194 2495/1330/1167 2510/1363/1196 -f 2493/1360/1187 2520/1359/1193 2507/6981/5543 -f 2510/1363/1196 2496/1333/1170 2511/1366/1198 -f 2511/1366/1198 2497/1335/1172 2512/1368/1200 -f 2512/1368/1200 2498/1337/1174 2513/1370/1202 -f 2513/1370/1202 2499/1357/1191 2514/1356/1190 -f 2502/6986/1175 2516/1379/1210 2515/1378/1208 -f 2502/1338/1175 2501/1375/1207 2485/1323/1160 -f 2503/1340/1177 2517/1381/1212 2516/1379/1210 -f 2504/6987/1180 2518/1384/1214 2517/1383/1212 -f 2505/1345/1182 2519/1386/1216 2518/1384/1214 -f 2506/1348/1184 2520/1359/1193 2519/1386/1216 -f 2528/1395/1205 2501/1375/1207 2529/1393/1221 -f 2508/1353/1186 2521/1389/1192 2493/6988/1187 -f 2494/1327/1164 2522/1362/1195 2508/1353/1186 -f 2509/1365/1194 2523/1364/1197 2522/6989/1195 -f 2510/1363/1196 2524/1367/1199 2523/1364/1197 -f 2511/1366/1198 2525/1369/1201 2524/1367/1199 -f 2512/1368/1200 2526/1371/1203 2525/1369/1201 -f 2513/1370/1202 2527/1372/1204 2526/1371/1203 -f 2514/1356/1190 2528/1373/1205 2527/1372/1204 -f 2538/6990/5544 2524/1367/1199 2539/6991/5545 -f 2531/1377/1209 2530/1392/1206 2515/1378/1208 -f 2532/1380/1211 2531/1377/1209 2516/1379/1210 -f 2533/6992/1213 2532/1380/1211 2517/1381/1212 -f 2534/1385/1215 2533/1382/1213 2518/1384/1214 -f 2535/1387/1217 2534/1385/1215 2519/1386/1216 -f 2521/1358/1192 2535/1387/1217 2520/1359/1193 -f 2523/1364/1197 2537/6993/5546 2522/6989/1195 -f 2523/1364/1197 2538/6990/5544 2537/6993/5546 -f 2539/6991/5545 2525/1369/1201 2540/6994/5547 -f 2540/6994/5547 2526/1371/1203 2541/6995/5548 -f 2541/6995/5548 2527/1372/1204 2542/1390/1219 -f 2531/1377/1209 2545/6996/5549 2544/6997/5550 -f 2530/1374/1206 2529/1393/1221 2501/1375/1207 -f 2544/6997/5550 2543/1391/1220 2531/1377/1209 -f 2532/1380/1211 2546/6998/5551 2545/6996/5549 -f 2533/1382/1213 2547/6999/5552 2546/7000/5551 -f 2534/1385/1215 2548/7001/5553 2547/6999/5552 -f 2550/7002/5554 2537/6993/5546 2551/7003/5555 -f 2521/1358/1192 2548/7001/5553 2535/1387/1217 -f 2536/1388/1218 2549/7004/5556 2521/1389/1192 -f 2522/1362/1195 2550/7005/5554 2536/1388/1218 -f 2551/7003/5555 2538/6990/5544 2552/1399/1226 -f 2552/1399/1226 2539/6991/5545 2553/1397/1224 -f 2553/1397/1224 2540/6994/5547 2554/1400/1227 -f 2554/1400/1227 2541/6995/5548 2555/1402/1229 -f 2555/1402/1229 2542/1390/1219 2556/1404/1231 -f 2556/1404/1231 2528/1373/1205 2557/7006/1222 -f 2530/1392/1206 2559/7007/5557 2558/7008/1223 -f 2543/1391/1220 2560/1407/1234 2559/7007/5557 -f 2544/6997/5550 2561/1408/1235 2560/1407/1234 -f 2545/6996/5549 2562/1410/1237 2561/1408/1235 -f 2546/7000/5551 2563/1413/1239 2562/1412/1237 -f 2547/6999/5552 2564/1415/1241 2563/1413/1239 -f 2548/7001/5553 2549/7009/5556 2564/1415/1241 -f 2549/7004/5556 2566/7010/5558 2565/7011/5559 -f 2566/7010/5558 2550/7005/5554 2567/7012/5560 -f 2567/7013/5560 2551/7003/5555 2568/7014/5561 -f 2568/7014/5561 2552/1399/1226 2569/1398/1225 -f 2572/1405/1232 2557/7006/1222 2573/7015/1244 -f 2553/1397/1224 2570/1401/1228 2569/1398/1225 -f 2554/1400/1227 2571/1403/1230 2570/1401/1228 -f 2555/1402/1229 2572/1405/1232 2571/1403/1230 -f 2573/1418/1244 2529/1393/1221 2574/1416/1242 -f 2529/1393/1221 2575/1420/1246 2574/1416/1242 -f 2558/7008/1223 2576/7016/5562 2575/7017/1246 -f 2559/7007/5557 2577/1406/1233 2576/7016/5562 -f 2564/1415/1241 2565/7018/5559 2580/1414/1240 -f 2578/1409/1236 2577/1406/1233 2561/1408/1235 -f 2579/1411/1238 2578/7019/1236 2562/1412/1237 -f 2580/1414/1240 2579/1411/1238 2563/1413/1239 -f 2582/7020/5563 2568/7014/5561 2583/7021/5564 -f 2566/7010/5558 2581/7022/5565 2565/7011/5559 -f 2567/7012/5560 2582/7023/5563 2566/7010/5558 -f 2583/7021/5564 2569/1398/1225 2584/7024/5566 -f 2584/7024/5566 2570/1401/1228 2585/7025/5567 -f 2585/7025/5567 2571/1403/1230 2586/7026/5568 -f 2586/7026/5568 2572/1405/1232 2587/7027/5569 -f 2587/7027/5569 2573/7015/1244 2588/7028/1243 -f 2575/7017/1246 2590/7029/5570 2589/7030/1245 -f 2576/7016/5562 2591/7031/5571 2590/7029/5570 -f 2577/1406/1233 2592/7032/5572 2591/7031/5571 -f 2578/7019/1236 2593/7033/5573 2592/7034/5572 -f 2579/1411/1238 2594/7035/5574 2593/7033/5573 -f 2580/1414/1240 2581/7036/5565 2594/7035/5574 -f 2566/7010/5558 2595/1449/1272 2581/7022/5565 -f 2566/7037/5558 2597/7038/1249 2596/7039/5575 -f 2596/7040/5575 2611/7041/5576 2566/7010/5558 -f 2597/1423/1249 2583/7021/5564 2598/1421/1247 -f 2598/1421/1247 2584/7024/5566 2599/1424/1250 -f 2599/1424/1250 2585/7025/5567 2600/1426/1252 -f 2600/1426/1252 2586/7026/5568 2601/1428/1254 -f 2601/1428/1254 2587/7027/5569 2602/1430/1256 -f 2602/1434/1256 2588/1417/1243 2603/1432/1258 -f 2603/1432/1258 2574/1416/1242 2619/7042/5577 -f 2574/7043/1242 2605/7044/1261 2604/7045/5578 -f 2574/1416/1242 2604/7046/5578 2619/7042/5577 -f 2589/7030/1245 2606/1437/1262 2605/1436/1261 -f 2590/7029/5570 2607/1439/1264 2606/1437/1262 -f 2591/7031/5571 2608/1441/1266 2607/1439/1264 -f 2592/7034/5572 2609/1444/1268 2608/1443/1266 -f 2593/7033/5573 2610/1446/1270 2609/1444/1268 -f 2594/7047/5574 2595/1449/1272 2610/1448/1270 -f 2626/1447/1271 2627/7048/5579 2644/7049/5580 -f 2611/7041/5576 2626/1447/1271 2595/1449/1272 -f 2597/7038/1249 2612/7050/1248 2596/7039/5575 -f 2598/1421/1247 2613/1425/1251 2612/1422/1248 -f 2599/1424/1250 2614/7051/5581 2613/1425/1251 -f 2599/1424/1250 2615/1427/1253 2614/7051/5581 -f 2600/1426/1252 2616/1429/1255 2615/1427/1253 -f 2601/1428/1254 2617/1431/1257 2616/1429/1255 -f 2602/1434/1256 2618/1433/1259 2617/7052/1257 -f 2603/1432/1258 2619/7042/5577 2618/1433/1259 -f 2620/7053/1260 2604/7045/5578 2605/7044/1261 -f 2621/1438/1263 2620/1435/1260 2606/1437/1262 -f 2622/7054/5582 2621/1438/1263 2607/1439/1264 -f 2623/1440/1265 2622/7054/5582 2607/1439/1264 -f 2624/1442/1267 2623/7055/1265 2608/1443/1266 -f 2625/1445/1269 2624/1442/1267 2609/1444/1268 -f 2626/1447/1271 2625/7056/1269 2610/1448/1270 -f 2611/7041/5576 2628/7057/5583 2627/7048/5579 -f 2628/7058/5583 2612/7050/1248 2629/7059/5584 -f 2629/7060/5584 2613/1425/1251 2630/7061/5585 -f 2630/7061/5585 2614/7051/5581 2631/7062/5586 -f 2631/7062/5586 2615/1427/1253 2632/7063/5587 -f 2632/7064/5587 2616/7065/1255 2633/7066/5588 -f 2633/7066/5588 2617/7052/1257 2634/7067/5589 -f 2634/7067/5589 2618/1433/1259 2635/7068/5590 -f 2635/7068/5590 2619/7042/5577 2636/7069/5591 -f 2636/7069/5591 2604/7046/5578 2637/7070/5592 -f 2620/7053/1260 2637/7071/5592 2604/7045/5578 -f 2620/1435/1260 2639/7072/5593 2638/7073/5594 -f 2621/1438/1263 2640/7074/5595 2639/7072/5593 -f 2622/7054/5582 2641/7075/5596 2640/7074/5595 -f 2623/7076/1265 2642/7077/5597 2641/7078/5596 -f 2624/7079/1267 2643/7080/5598 2642/7077/5597 -f 2625/7056/1269 2644/7049/5580 2643/7080/5598 -f 2644/7049/5580 2645/7081/5599 2662/7082/5600 -f 2627/7048/5579 2646/7083/5601 2645/7081/5599 -f 2628/7058/5583 2647/7084/5602 2646/7085/5601 -f 2647/7086/5602 2630/7061/5585 2648/7087/5603 -f 2648/7088/5603 2631/7089/5586 2649/7090/5604 -f 2649/7090/5604 2632/7064/5587 2650/7091/5605 -f 2650/7091/5605 2633/7066/5588 2651/7092/5606 -f 2651/7092/5606 2634/7067/5589 2652/7093/5607 -f 2652/7093/5607 2635/7068/5590 2653/7094/5608 -f 2653/7094/5608 2636/7069/5591 2654/7095/5609 -f 2654/7095/5609 2637/7070/5592 2655/7096/5610 -f 2637/7071/5592 2656/7097/5611 2655/7098/5610 -f 2638/7073/5594 2657/7099/5612 2656/7100/5611 -f 2639/7101/5593 2658/7102/5613 2657/7103/5612 -f 2640/7104/5595 2659/7105/5614 2658/7102/5613 -f 2641/7078/5596 2660/7106/5615 2659/7107/5614 -f 2642/7077/5597 2661/7108/5616 2660/7106/5615 -f 2643/7080/5598 2662/7082/5600 2661/7108/5616 -f 2662/7082/5600 2663/1450/1273 2681/1452/1275 -f 2645/7081/5599 2664/7109/5617 2663/1450/1273 -f 2646/7085/5601 2665/7110/5618 2664/7111/5617 -f 2665/7112/5618 2648/7088/5603 2666/7113/5619 -f 2666/7113/5619 2649/7090/5604 2667/7114/5620 -f 2667/7114/5620 2650/7091/5605 2668/7115/5621 -f 2668/7115/5621 2651/7092/5606 2669/7116/5622 -f 2669/7116/5622 2652/7093/5607 2670/7117/5623 -f 2670/7117/5623 2653/7094/5608 2671/7118/5624 -f 2671/7118/5624 2654/7095/5609 2672/1485/1306 -f 2654/7095/5609 2674/1455/1278 2673/1454/1277 -f 2654/7095/5609 2673/1454/1277 2672/1485/1306 -f 2655/7098/5610 2675/7119/5625 2674/7120/1278 -f 2656/7121/5611 2676/7122/5626 2675/7123/5625 -f 2657/7103/5612 2677/7124/5627 2676/7122/5626 -f 2658/7102/5613 2678/7125/5628 2677/7124/5627 -f 2659/7107/5614 2679/7126/5629 2678/7127/5628 -f 2660/7106/5615 2680/7128/5630 2679/7126/5629 -f 2661/7108/5616 2681/1452/1275 2680/7128/5630 -f 2683/7129/5631 2665/7110/5618 2684/7130/5632 -f 2664/7109/5617 2682/1456/1279 2663/1450/1273 -f 2664/7111/5617 2683/7129/5631 2682/1488/1279 -f 2665/7112/5618 2685/7131/5633 2684/7132/5632 -f 2666/7113/5619 2686/7133/5634 2685/7131/5633 -f 2667/7114/5620 2687/1471/1293 2686/7133/5634 -f 2668/7115/5621 2689/1475/1297 2688/1473/1295 -f 2688/1473/1295 2687/1471/1293 2668/7115/5621 -f 2670/7117/5623 2690/1459/1282 2669/7116/5622 -f 2690/1459/1282 2689/1475/1297 2669/7116/5622 -f 2670/7117/5623 2692/1483/1304 2691/1460/1283 -f 2674/7120/1278 2694/1464/1286 2693/1463/1276 -f 2672/1485/1306 2692/1483/1304 2671/7118/5624 -f 2675/7123/5625 2695/1467/1289 2694/7134/1286 -f 2695/1467/1289 2677/7124/5627 2696/1465/1287 -f 2677/7124/5627 2697/7135/5635 2696/1465/1287 -f 2698/7136/5636 2679/7126/5629 2699/7137/5637 -f 2678/7125/5628 2698/7138/5636 2697/7135/5635 -f 2700/7139/5638 2680/7128/5630 2701/7140/5639 -f 2679/7126/5629 2700/7139/5638 2699/7137/5637 -f 2702/7141/5640 2681/1452/1275 2703/1451/1274 -f 2680/7128/5630 2702/7141/5640 2701/7140/5639 -f 2684/7132/5632 2707/7142/5641 2706/7143/5642 -f 2663/1450/1273 2704/1457/1280 2703/1451/1274 -f 2684/7130/5632 2705/1490/1310 2683/7129/5631 -f 2706/7144/5642 2705/1490/1310 2684/7130/5632 -f 2685/7131/5633 2708/1470/1292 2707/7142/5641 -f 2694/7134/1286 2713/1466/1288 2712/7145/1285 -f 2687/1471/1293 2708/1470/1292 2686/7133/5634 -f 2710/1481/1302 2709/1458/1281 2691/1460/1283 -f 2692/1483/1304 2710/1481/1302 2691/1460/1283 -f 2718/1477/1299 2712/1462/1285 2719/7146/5643 -f 2715/1472/1294 2714/1469/1291 2687/1471/1293 -f 2716/1474/1296 2715/1472/1294 2688/1473/1295 -f 2717/1476/1298 2716/1474/1296 2689/1475/1297 -f 2709/1458/1281 2717/1476/1298 2690/1459/1282 -f 2712/7145/1285 2720/7147/5644 2719/7148/5643 -f 2713/1466/1288 2721/7149/5645 2720/7147/5644 -f 2721/7149/5645 2697/7135/5635 2722/7150/5646 -f 2722/7150/5646 2698/7138/5636 2723/7151/5647 -f 2723/7152/5647 2699/7137/5637 2724/7153/5648 -f 2724/7153/5648 2700/7139/5638 2725/7154/5649 -f 2725/7154/5649 2701/7140/5639 2726/7155/5650 -f 2726/7155/5650 2702/7141/5640 2727/7156/5651 -f 2727/7156/5651 2703/1451/1274 2728/1468/1290 -f 2754/1504/1324 2704/1457/1280 2734/1507/1327 -f 2683/7129/5631 2729/1486/1307 2682/1488/1279 -f 2705/1490/1310 2729/1486/1307 2683/7129/5631 -f 2731/1480/1301 2730/1479/1300 2709/1458/1281 -f 2732/1482/1303 2731/1480/1301 2710/1481/1302 -f 2733/1484/1305 2732/1482/1303 2692/1483/1304 -f 2673/1454/1277 2733/1484/1305 2672/1485/1306 -f 2704/1457/1280 2774/1531/1308 2734/1507/1327 -f 2705/1490/1310 2736/1511/1331 2735/1489/1309 -f 2706/7143/5642 2737/7157/5652 2736/7158/1331 -f 2707/7142/5641 2738/1491/1311 2737/7157/5652 -f 2714/1469/1291 2739/1495/1315 2738/1491/1311 -f 2715/1472/1294 2740/1492/1312 2739/1495/1315 -f 2711/1461/1284 2741/1493/1313 2673/1454/1277 -f 2730/1479/1300 2740/1492/1312 2717/1476/1298 -f 2711/1478/1284 2743/7159/5653 2742/7160/1333 -f 2743/7159/5653 2719/7146/5643 2744/1516/1336 -f 2719/7148/5643 2746/1503/1323 2745/1518/1334 -f 2719/7146/5643 2745/1514/1334 2744/1516/1336 -f 2746/1503/1323 2721/7149/5645 2747/1501/1321 -f 2747/1501/1321 2722/7150/5646 2748/1521/1339 -f 2748/1521/1339 2723/7151/5647 2749/1519/1337 -f 2749/7161/1337 2724/7153/5648 2750/1524/1342 -f 2750/1524/1342 2725/7154/5649 2751/1522/1340 -f 2751/1522/1340 2726/7155/5650 2752/7162/5654 -f 2726/7155/5650 2753/1506/1326 2752/7162/5654 -f 2753/1506/1326 2728/1468/1290 2754/1504/1324 -f 2737/7157/5652 2764/7163/1330 2736/7158/1331 -f 2756/1496/1316 2755/1494/1314 2739/1495/1315 -f 2757/1528/1346 2756/1496/1316 2740/1492/1312 -f 2758/1497/1317 2757/1528/1346 2740/1492/1312 -f 2759/1498/1318 2758/1497/1317 2730/1479/1300 -f 2760/1499/1319 2759/1498/1318 2731/1480/1301 -f 2761/1500/1320 2760/1499/1319 2732/1482/1303 -f 2746/1503/1323 2762/1502/1322 2745/1518/1334 -f 2753/1506/1326 2772/1505/1325 2752/7162/5654 -f 2738/1491/1311 2765/7164/5655 2737/7157/5652 -f 2742/7160/1333 2744/1516/1336 2768/1515/1335 -f 2755/1494/1314 2766/1526/1344 2738/1491/1311 -f 2769/7165/5656 2748/1521/1339 2770/1520/1338 -f 2747/1501/1321 2769/7165/5656 2762/1502/1322 -f 2770/7166/1338 2750/1524/1342 2771/1523/1341 -f 2771/1523/1341 2752/7162/5654 2772/1505/1325 -f 2764/1510/1330 2763/1509/1329 2729/1486/1307 -f 2754/1504/1324 2773/1508/1328 2772/1505/1325 -f 2734/1507/1327 2774/1531/1308 2773/1508/1328 -f 2756/1496/1316 2777/1527/1345 2776/1525/1343 -f 2765/7164/5655 2775/7167/5657 2764/7163/1330 -f 2758/1497/1317 2778/1529/1347 2777/1527/1345 -f 2761/1500/1320 2778/1529/1347 2760/1499/1319 -f 2761/1500/1320 2780/1541/1357 2779/1536/1352 -f 2767/7168/1332 2768/1515/1335 2781/7169/5658 -f 2741/1493/1313 2780/1541/1357 2733/1484/1305 -f 2768/1517/1335 2782/7170/5659 2781/7171/5658 -f 2782/7170/5659 2769/7165/5656 2783/7172/5660 -f 2783/7172/5660 2770/1520/1338 2784/7173/5661 -f 2784/7174/5661 2771/1523/1341 2785/7175/5662 -f 2785/7175/5662 2772/1505/1325 2786/1530/1348 -f 2775/7176/5657 2788/1538/1354 2763/1509/1329 -f 2763/1509/1329 2788/1538/1354 2787/7177/1350 -f 2775/7167/5657 2790/7178/5663 2789/7179/1355 -f 2765/7164/5655 2791/7180/5664 2790/7178/5663 -f 2766/1526/1344 2792/7181/5665 2791/7180/5664 -f 2776/1525/1343 2793/7182/5666 2792/7181/5665 -f 2777/1527/1345 2794/1561/1376 2793/7182/5666 -f 2741/1493/1313 2796/1543/1359 2780/1541/1357 -f 2795/1535/1351 2794/1561/1376 2778/1529/1347 -f 2796/1546/1359 2781/7169/5658 2797/1544/1360 -f 2797/7183/1360 2782/7170/5659 2798/7184/5667 -f 2798/7184/5667 2783/7172/5660 2799/7185/5668 -f 2799/7185/5668 2784/7173/5661 2800/7186/5669 -f 2800/7187/5669 2785/7175/5662 2801/1549/1364 -f 2801/1549/1364 2786/1530/1348 2802/1547/1362 -f 2803/7188/5670 2773/1508/1328 2804/1532/1349 -f 2786/1530/1348 2803/7188/5670 2802/1547/1362 -f 2774/1531/1308 2806/7189/1368 2805/1550/1365 -f 2774/1531/1308 2805/1550/1365 2804/1532/1349 -f 2806/1553/1368 2788/1538/1354 2807/1537/1353 -f 2789/7190/1355 2808/7191/5671 2807/7192/1353 -f 2790/7178/5663 2809/1555/1370 2808/7193/5671 -f 2791/7180/5664 2810/1556/1371 2809/1555/1370 -f 2792/7181/5665 2811/1558/1373 2810/1556/1371 -f 2793/7182/5666 2812/1560/1375 2811/1558/1373 -f 2815/1565/1361 2798/7194/5667 2816/7195/1381 -f 2814/1542/1358 2813/1540/1356 2780/1541/1357 -f 2815/1545/1361 2814/7196/1358 2796/1546/1359 -f 2816/1568/1381 2799/7185/5668 2817/1566/1379 -f 2817/1566/1379 2800/7186/5669 2818/7197/5672 -f 2818/7198/5672 2801/1549/1364 2819/1548/1363 -f 2807/7192/1353 2822/7199/5673 2821/7200/5674 -f 2806/7189/1368 2820/7201/1367 2805/1550/1365 -f 2821/7200/5674 2820/7202/1367 2807/7192/1353 -f 2808/7193/5671 2823/1554/1369 2822/7203/5673 -f 2830/7204/5675 2816/7195/1381 2831/7205/1380 -f 2824/1557/1372 2823/1554/1369 2810/1556/1371 -f 2825/1570/1383 2824/1557/1372 2811/1558/1373 -f 2812/1560/1375 2825/1570/1383 2811/1558/1373 -f 2827/1585/1396 2826/1559/1374 2795/1535/1351 -f 2779/1536/1352 2827/1585/1396 2795/1535/1351 -f 2829/1572/1378 2828/1562/1377 2814/1542/1358 -f 2815/1565/1361 2830/7204/5675 2829/1563/1378 -f 2831/1567/1380 2818/7197/5672 2832/7206/5676 -f 2832/7207/5676 2819/1548/1363 2833/1575/1387 -f 2833/1575/1387 2802/1547/1362 2834/1573/1385 -f 2834/1573/1385 2803/7188/5670 2835/1596/1406 -f 2835/1596/1406 2804/1532/1349 2836/1551/1366 -f 2820/7202/1367 2838/1580/1391 2837/1579/1388 -f 2805/1550/1365 2862/1608/1417 2836/1551/1366 -f 2820/7201/1367 2837/1576/1388 2805/1550/1365 -f 2821/7200/5674 2839/1582/1393 2838/1580/1391 -f 2822/7199/5673 2840/7208/5677 2839/1582/1393 -f 2823/1554/1369 2842/7209/5678 2841/7210/5679 -f 2841/7210/5679 2840/7211/5677 2823/1554/1369 -f 2845/1603/1384 2830/7204/5675 2846/1590/1401 -f 2843/7212/5680 2842/7209/5678 2824/1557/1372 -f 2825/1570/1383 2843/7212/5680 2824/1557/1372 -f 2826/1559/1374 2844/1569/1382 2812/1560/1375 -f 2813/1540/1356 2827/1585/1396 2779/1536/1352 -f 2846/1590/1401 2831/7205/1380 2847/1588/1399 -f 2847/7213/1399 2832/7206/5676 2848/7214/1402 -f 2848/1594/1402 2833/1575/1387 2849/1574/1386 -f 2839/1582/1393 2864/7215/5681 2863/1600/1410 -f 2805/1550/1365 2850/1577/1389 2862/1608/1417 -f 2852/1581/1392 2851/1578/1390 2838/1580/1391 -f 2853/1583/1394 2843/7212/5680 2825/1570/1383 -f 2854/1584/1395 2844/1569/1382 2826/1559/1374 -f 2828/1562/1377 2869/1586/1397 2813/1540/1356 -f 2847/1588/1399 2856/1605/1414 2855/1589/1400 -f 2847/1588/1399 2857/1592/1403 2856/1605/1414 -f 2848/7214/1402 2858/7216/1404 2857/7217/1403 -f 2849/1574/1386 2859/1595/1405 2858/1593/1404 -f 2834/1573/1385 2860/1597/1407 2859/1595/1405 -f 2835/1596/1406 2861/1598/1408 2860/1597/1407 -f 2836/1551/1366 2862/1608/1417 2861/1598/1408 -f 2840/7208/5677 2865/7218/5682 2864/7215/5681 -f 2841/7210/5679 2866/7219/5683 2865/7220/5682 -f 2842/7209/5678 2867/7221/5684 2866/7219/5683 -f 2873/7222/5685 2859/1595/1405 2874/1606/1415 -f 2853/1583/1394 2867/7221/5684 2843/7212/5680 -f 2854/1584/1395 2868/1601/1411 2844/1569/1382 -f 2845/1603/1384 2871/1602/1412 2870/1618/1398 -f 2856/1605/1414 2872/1604/1413 2855/1589/1400 -f 2858/7223/1404 2873/7224/5685 2857/1592/1403 -f 2854/1584/1395 2878/1616/1424 2877/1610/1418 -f 2868/1601/1411 2867/7221/5684 2853/1583/1394 -f 2871/1602/1412 2855/1589/1400 2879/1619/1426 -f 2870/1587/1398 2878/1616/1424 2869/1586/1397 -f 2880/7225/5686 2873/7224/5685 2881/7226/5687 -f 2857/1592/1403 2880/7225/5686 2872/1604/1413 -f 2882/7227/1434 2874/1606/1415 2883/7228/5688 -f 2873/7224/5685 2882/7229/1434 2881/7226/5687 -f 2883/7228/5688 2860/1597/1407 2884/1611/1419 -f 2884/1611/1419 2862/1608/1417 2875/1607/1416 -f 2885/1622/1429 2850/1577/1389 2886/1620/1427 -f 2863/1600/1410 2889/1632/1438 2900/1614/1422 -f 2850/1577/1389 2887/7230/1420 2886/1620/1427 -f 2888/1613/1421 2887/1612/1420 2876/1599/1409 -f 2852/1581/1392 2888/1613/1421 2851/1578/1390 -f 2864/7215/5681 2890/7231/5689 2889/1632/1438 -f 2865/7218/5682 2891/7232/5690 2890/7231/5689 -f 2866/7233/5683 2892/7234/5691 2891/7232/5690 -f 2867/7221/5684 2894/1624/1431 2893/7235/5692 -f 2893/7235/5692 2892/7236/5691 2867/7221/5684 -f 2897/1630/1432 2884/1611/1419 2898/1628/1435 -f 2877/1610/1418 2894/1624/1431 2868/1601/1411 -f 2896/7237/1425 2895/1615/1423 2870/1587/1398 -f 2883/7238/5688 2897/1625/1432 2882/1627/1434 -f 2898/1628/1435 2875/1607/1416 2885/1622/1429 -f 2877/1610/1418 2902/1634/1440 2901/1623/1430 -f 2903/1635/1441 2879/1619/1426 2904/1638/1444 -f 2895/1615/1423 2902/1634/1440 2878/1616/1424 -f 2871/1602/1412 2903/1635/1441 2896/1617/1425 -f 2905/1637/1443 2880/7225/5686 2906/1646/1452 -f 2872/1604/1413 2905/1637/1443 2879/1619/1426 -f 2906/1646/1452 2881/7226/5687 2907/1648/1454 -f 2907/1648/1454 2882/7229/1434 2908/7239/1433 -f 2910/1640/1446 2885/1622/1429 2899/1621/1428 -f 2897/1625/1432 2909/7240/1436 2908/1626/1433 -f 2898/1628/1435 2910/1640/1446 2909/1629/1436 -f 2927/7241/1460 2887/7242/1420 2911/1671/1474 -f 2886/1620/1427 2927/1656/1460 2899/1621/1428 -f 2887/1612/1420 2913/1642/1448 2912/1659/1463 -f 2912/1659/1463 2911/1673/1474 2887/1612/1420 -f 2889/1632/1438 2915/7243/5693 2914/1631/1437 -f 2900/1614/1422 2913/1642/1448 2888/1613/1421 -f 2890/7231/5689 2916/7244/5694 2915/7243/5693 -f 2891/7232/5690 2917/7245/5695 2916/7244/5694 -f 2892/7234/5691 2918/7246/5696 2917/7245/5695 -f 2893/7247/5692 2919/7248/5697 2918/7246/5696 -f 2895/1615/1423 2920/1633/1439 2902/1634/1440 -f 2901/1623/1430 2919/7249/5697 2894/1624/1431 -f 2896/7250/1425 2921/7251/1479 2895/7252/1423 -f 2924/1653/1456 2909/7240/1436 2925/1651/1457 -f 2896/1617/1425 2923/1636/1442 2922/7253/5698 -f 2908/7239/1433 2924/1650/1456 2907/1648/1454 -f 2925/1655/1457 2910/1640/1446 2926/1639/1445 -f 2914/1631/1437 2939/7254/5699 2938/1660/1464 -f 2899/1621/1428 2927/1656/1460 2926/1639/1445 -f 2929/1643/1449 2928/1641/1447 2900/1614/1422 -f 2930/1644/1450 2919/7249/5697 2901/1623/1430 -f 2904/1638/1444 2931/1645/1451 2903/1635/1441 -f 2905/1637/1443 2932/1647/1453 2931/1645/1451 -f 2906/1646/1452 2933/1649/1455 2932/1647/1453 -f 2924/1653/1456 2934/1652/1458 2933/7255/1455 -f 2925/1651/1457 2935/7256/1459 2934/1652/1458 -f 2911/1671/1474 2936/1670/1466 2927/7241/1460 -f 2928/1641/1447 2937/1658/1462 2913/1642/1448 -f 2915/7243/5693 2940/7257/5700 2939/7254/5699 -f 2916/7244/5694 2941/7258/5701 2940/7257/5700 -f 2917/7245/5695 2942/7259/5702 2941/7258/5701 -f 2918/7246/5696 2943/7260/5703 2942/7259/5702 -f 2919/7249/5697 2944/7261/5704 2943/7262/5703 -f 2930/1644/1450 2945/1677/1478 2944/7261/5704 -f 2922/7253/5698 2903/1635/1441 2946/1661/1465 -f 2921/1678/1479 2945/1677/1478 2920/1633/1439 -f 2948/7263/5705 2934/1652/1458 2949/1668/1472 -f 2933/1649/1455 2947/1664/1468 2932/1647/1453 -f 2933/1649/1455 2948/7264/5705 2947/1664/1468 -f 2949/1668/1472 2935/7256/1459 2950/1666/1470 -f 2951/7265/5706 2926/1639/1445 2952/1682/1483 -f 2935/7256/1459 2951/7266/5706 2950/1666/1470 -f 2928/1641/1447 2955/1675/1476 2954/1663/1467 -f 2926/1639/1445 2953/1657/1461 2952/1682/1483 -f 2956/1679/1480 2932/1647/1453 2957/1665/1469 -f 2938/1660/1464 2955/1675/1476 2929/1643/1449 -f 2931/1645/1451 2956/1679/1480 2946/1661/1465 -f 2937/1658/1462 2959/1672/1473 2912/1659/1463 -f 2949/1668/1472 2958/1667/1471 2948/7263/5705 -f 2938/1660/1464 2962/7267/5707 2961/1674/1475 -f 2954/1663/1467 2960/1685/1486 2937/1658/1462 -f 2939/7254/5699 2963/7268/5708 2962/7267/5707 -f 2940/7257/5700 2964/7269/5709 2963/7268/5708 -f 2941/7258/5701 2965/7270/5710 2964/7269/5709 -f 2942/7259/5702 2966/7271/5711 2965/7270/5710 -f 2943/7260/5703 2967/1710/1510 2966/7271/5711 -f 2921/7251/1479 2970/7272/5712 2969/7273/1477 -f 2968/1693/1494 2967/7274/1510 2944/7261/5704 -f 2945/1677/1478 2968/1693/1494 2944/7261/5704 -f 2970/7275/5712 2946/1661/1465 2971/1680/1481 -f 2972/1686/1487 2948/7264/5705 2973/7276/5713 -f 2947/1664/1468 2972/1686/1487 2957/1665/1469 -f 2973/7277/5713 2958/1667/1471 2974/7278/5714 -f 2975/7279/5715 2950/1666/1470 2976/7280/5716 -f 2958/1667/1471 2975/7279/5715 2974/7278/5714 -f 2976/7280/5716 2951/7266/5706 2977/1698/1498 -f 2977/1698/1498 2952/7281/1483 2978/1696/1482 -f 2954/1663/1467 2981/1691/1492 2980/1684/1485 -f 2953/1657/1461 2979/1683/1484 2978/1681/1482 -f 2982/1694/1495 2957/1665/1469 2983/1687/1488 -f 2961/1674/1475 2981/1691/1492 2955/1675/1476 -f 2956/1679/1480 2982/1694/1495 2971/1680/1481 -f 2959/1672/1473 2986/1700/1500 2985/7282/5717 -f 2936/1662/1466 2984/7283/1490 2979/1683/1484 -f 2985/7282/5717 2984/7284/1490 2959/1672/1473 -f 2961/1674/1475 2988/1704/1504 2987/1690/1491 -f 2980/1684/1485 2986/1700/1500 2960/1685/1486 -f 2962/7267/5707 2989/1705/1505 2988/1704/1504 -f 2963/7268/5708 2990/1707/1507 2989/1705/1505 -f 2964/7269/5709 2991/1708/1508 2990/1707/1507 -f 2965/7270/5710 2992/7285/5718 2991/1708/1508 -f 2994/7286/5719 2971/1680/1481 2995/1695/1496 -f 2967/1710/1510 2992/7285/5718 2966/7271/5711 -f 2969/1676/1477 2993/1692/1493 2945/1677/1478 -f 2970/7287/5712 2994/7288/5719 2969/1676/1477 -f 2996/1701/1501 2973/7276/5713 2997/7289/1515 -f 2972/1686/1487 2996/1701/1501 2983/1687/1488 -f 2997/1716/1515 2974/7278/5714 2998/1714/1513 -f 2998/1714/1513 2975/7279/5715 2999/1719/1518 -f 2999/1719/1518 2976/7280/5716 3000/1717/1516 -f 3000/1717/1516 2977/1698/1498 3001/1697/1497 -f 2980/1684/1485 3003/7290/5720 3002/1699/1499 -f 2978/1681/1482 3017/1688/1489 3001/7291/1497 -f 3004/1712/1511 2983/1687/1488 3005/1702/1502 -f 2987/1690/1491 3003/7290/5720 2981/1691/1492 -f 2982/1694/1495 3004/1712/1511 2995/1695/1496 -f 2984/7284/1490 3007/7292/5721 3006/7293/5722 -f 2984/7283/1490 3006/7294/5722 2979/1683/1484 -f 2987/1690/1491 3008/1703/1503 3003/7290/5720 -f 2986/1700/1500 3007/7292/5721 2985/7282/5717 -f 2989/1705/1505 3009/1706/1506 3008/1703/1503 -f 2991/1708/1508 3010/7295/5723 3009/1706/1506 -f 2992/7285/5718 3011/1709/1509 3010/7295/5723 -f 2993/1692/1493 2994/7288/5719 3012/7296/1521 -f 2993/1692/1493 3011/7297/1509 2968/1693/1494 -f 3012/1728/1521 2995/1695/1496 3013/1713/1512 -f 3005/1702/1502 2997/7289/1515 3014/7298/1514 -f 3014/1715/1514 2999/1719/1518 3015/1718/1517 -f 3015/1718/1517 3001/1697/1497 3016/7299/5724 -f 2979/1720/1484 3019/7300/5725 3018/1721/1519 -f 3001/1697/1497 3017/7301/1489 3016/7299/5724 -f 3006/7293/5722 3020/7302/5726 3019/7303/5725 -f 3007/7292/5721 3021/1723/1520 3020/7302/5726 -f 3002/1699/1499 3022/1742/1535 3021/1723/1520 -f 3003/7290/5720 3024/7304/5727 3023/1744/1537 -f 3023/1744/1537 3022/1742/1535 3003/7290/5720 -f 3008/1703/1503 3025/7305/5728 3024/7304/5727 -f 3009/1706/1506 3026/7306/5729 3025/7305/5728 -f 3010/7295/5723 3027/7307/5730 3026/7306/5729 -f 3011/7308/1509 3028/1730/1525 3027/7309/5730 -f 3031/1747/1540 3005/1702/1502 3032/7310/5731 -f 2993/1726/1493 3029/1725/1522 3028/1730/1525 -f 3012/1728/1521 3030/1727/1523 3029/1732/1522 -f 3004/1712/1511 3031/1747/1540 3013/1713/1512 -f 3032/7310/5731 3014/7298/1514 3033/7311/5732 -f 3033/7312/5732 3015/1718/1517 3034/7313/5733 -f 3034/7313/5733 3016/7299/5724 3035/7314/5734 -f 3035/7314/5734 3017/7301/1489 3018/7315/1519 -f 3036/1736/1530 3019/7300/5725 3037/1734/1528 -f 3036/1736/1530 3052/7316/5735 3018/1721/1519 -f 3037/1739/1528 3020/7302/5726 3038/1737/1531 -f 3038/1737/1531 3021/1723/1520 3039/1740/1533 -f 3040/7317/5736 3025/7305/5728 3041/7318/5737 -f 3024/7304/5727 3040/7317/5736 3023/1744/1537 -f 3041/7318/5737 3026/7306/5729 3042/7319/5738 -f 3042/7319/5738 3027/7307/5730 3043/7320/5739 -f 3043/7321/5739 3028/1730/1525 3044/1729/1524 -f 3033/7311/5732 3048/1748/1541 3032/7310/5731 -f 3045/7322/1526 3044/1729/1524 3029/1725/1522 -f 3046/1733/1527 3045/1731/1526 3030/1727/1523 -f 3047/1765/1556 3046/1733/1527 3013/1713/1512 -f 3031/1747/1540 3047/1765/1556 3013/1713/1512 -f 3048/1748/1541 3031/1747/1540 3032/7310/5731 -f 3033/7312/5732 3050/1751/1544 3049/1750/1543 -f 3034/7313/5733 3051/1753/1546 3050/1751/1544 -f 3035/7314/5734 3052/7323/5735 3051/1753/1546 -f 3052/7324/5735 3053/1756/1529 3071/1755/1548 -f 3057/1745/1538 3040/7317/5736 3058/1761/1552 -f 3037/1734/1528 3054/7325/1532 3053/1735/1529 -f 3038/1737/1531 3055/1741/1534 3054/1738/1532 -f 3021/1723/1520 3056/1743/1536 3039/1740/1533 -f 3022/1742/1535 3057/1745/1538 3056/1743/1536 -f 3059/1772/1563 3041/7318/5737 3060/1770/1561 -f 3040/7317/5736 3059/1772/1563 3058/1761/1552 -f 3061/7326/1566 3042/7319/5738 3062/7327/1564 -f 3041/7318/5737 3061/7326/1566 3060/1770/1561 -f 3062/1773/1564 3043/7328/5739 3063/7329/5740 -f 3063/7329/5740 3044/7330/1524 3064/7331/5741 -f 3044/1729/1524 3065/7332/5742 3064/7333/5741 -f 3045/1731/1526 3066/1764/1555 3065/7334/5742 -f 3048/1748/1541 3068/7335/1558 3067/1746/1539 -f 3047/1765/1556 3066/1764/1555 3046/1733/1527 -f 3051/7336/1546 3071/1755/1548 3070/7337/1545 -f 3069/1749/1542 3068/1767/1558 3049/1750/1543 -f 3070/1752/1545 3069/1749/1542 3050/1751/1544 -f 3069/1749/1542 3079/7338/1569 3078/1766/1557 -f 3073/7339/1549 3072/1754/1547 3053/1756/1529 -f 3054/1738/1532 3074/1759/1550 3073/1757/1549 -f 3055/1741/1534 3075/1760/1551 3074/1759/1550 -f 3070/7337/1545 3072/1754/1547 3080/1779/1570 -f 3080/1779/1570 3079/1778/1569 3070/7337/1545 -f 3072/1754/1547 3082/7340/1559 3081/7341/5743 -f 3075/1760/1551 3056/1743/1536 3083/1769/1560 -f 3084/1804/1591 3059/1772/1563 3085/1771/1562 -f 3058/1761/1552 3084/1804/1591 3076/1762/1553 -f 3085/7342/1562 3061/1775/1566 3086/1774/1565 -f 3086/1774/1565 3063/7329/5740 3087/1783/1574 -f 3087/1783/1574 3064/7331/5741 3088/1781/1572 -f 3064/7343/5741 3089/1786/1576 3088/1785/1572 -f 3065/7334/5742 3090/1788/1578 3089/1786/1576 -f 3067/1746/1539 3092/7344/1598 3091/1776/1567 -f 3077/1763/1554 3090/1788/1578 3066/1764/1555 -f 3031/1747/1540 3077/1763/1554 3047/1765/1556 -f 3068/1767/1558 3093/1814/1599 3092/1813/1598 -f 3080/1779/1570 3081/7341/5743 3095/1777/1568 -f 3094/1791/1581 3093/1814/1599 3078/1766/1557 -f 3079/1778/1569 3094/7345/1581 3078/7346/1557 -f 3081/7341/5743 3097/7347/1584 3096/7348/1583 -f 3097/1798/1584 3074/1759/1550 3098/1780/1571 -f 3099/1802/1589 3086/7349/1565 3100/1805/1592 -f 3085/1771/1562 3099/1802/1589 3084/1804/1591 -f 3100/7350/1592 3087/1783/1574 3101/1782/1573 -f 3095/1777/1568 3096/7348/1583 3105/1793/1580 -f 3088/1781/1572 3102/7351/1575 3101/1782/1573 -f 3103/1787/1577 3102/1784/1575 3089/1786/1576 -f 3077/1763/1554 3103/1787/1577 3090/1788/1578 -f 3091/1776/1567 3104/1789/1579 3031/1747/1540 -f 3111/1801/1588 3084/1804/1591 3112/1803/1590 -f 3106/1794/1582 3123/7352/5744 3096/1795/1583 -f 3097/1796/1584 3107/7353/1585 3106/1794/1582 -f 3098/1780/1571 3108/1799/1586 3107/1797/1585 -f 3075/1760/1551 3109/1800/1587 3108/1799/1586 -f 3083/1769/1560 3110/7354/5745 3109/1800/1587 -f 3083/1769/1560 3111/1801/1588 3110/7354/5745 -f 3114/7355/5746 3101/7356/1573 3115/7357/5747 -f 3099/1802/1589 3113/1806/1593 3112/1803/1590 -f 3100/1805/1592 3114/7355/5746 3113/1806/1593 -f 3091/1776/1567 3120/7358/1597 3104/1789/1579 -f 3101/1809/1573 3116/1808/1594 3115/7359/5747 -f 3117/7360/1595 3116/1808/1594 3102/1807/1575 -f 3118/1811/1596 3117/1810/1595 3103/1787/1577 -f 3119/1816/1601 3118/1811/1596 3077/1763/1554 -f 3104/1789/1579 3119/1816/1601 3077/1763/1554 -f 3093/1814/1599 3121/1815/1600 3120/1812/1597 -f 3105/1793/1580 3123/7361/5744 3122/7362/5748 -f 3122/7363/5748 3121/1815/1600 3105/1790/1580 -f 3139/7364/5749 3106/1794/1582 3124/7365/5750 -f 3106/1794/1582 3125/7366/5751 3124/7365/5750 -f 3107/1797/1585 3126/1819/1604 3125/7367/5751 -f 3111/1801/1588 3127/7368/5752 3110/7354/5745 -f 3109/1800/1587 3126/1819/1604 3108/1799/1586 -f 3112/1803/1590 3128/7369/5753 3127/7368/5752 -f 3113/1806/1593 3129/7370/5754 3128/7369/5753 -f 3114/7355/5746 3130/7371/5755 3129/7370/5754 -f 3115/7359/5747 3131/7372/5756 3130/7373/5755 -f 3131/7372/5756 3117/7360/1595 3132/7374/5757 -f 3132/7375/5757 3118/1811/1596 3133/1817/1602 -f 3134/7376/5758 3120/7358/1597 3135/7377/1608 -f 3104/1789/1579 3134/7376/5758 3119/1816/1601 -f 3120/1812/1597 3136/1821/1606 3135/1823/1608 -f 3137/1833/1618 3122/7363/5748 3138/7378/5759 -f 3121/1815/1600 3137/1833/1618 3136/1821/1606 -f 3138/7378/5759 3123/7379/5744 3139/7380/5749 -f 3154/7381/5760 3124/7365/5750 3140/7382/5761 -f 3124/7365/5750 3141/7383/5762 3140/7382/5761 -f 3125/7367/5751 3142/1818/1603 3141/7384/5762 -f 3109/1800/1587 3143/1825/1610 3142/1818/1603 -f 3110/7354/5745 3144/1827/1612 3143/1825/1610 -f 3127/7368/5752 3145/1829/1614 3144/1827/1612 -f 3128/7369/5753 3161/1831/1616 3146/1841/1626 -f 3146/1841/1626 3145/1829/1614 3128/7369/5753 -f 3129/7370/5754 3147/1832/1617 3161/1831/1616 -f 3130/7373/5755 3148/7385/5763 3147/7386/1617 -f 3148/7385/5763 3132/7374/5757 3149/7387/5764 -f 3149/7388/5764 3133/1817/1602 3150/1820/1605 -f 3150/1820/1605 3134/7376/5758 3151/7389/5765 -f 3151/7389/5765 3135/7377/1608 3152/7390/1607 -f 3153/7391/5766 3139/7380/5749 3154/7392/5760 -f 3138/7378/5759 3153/7391/5766 3137/1833/1618 -f 3171/1836/1621 3140/7382/5761 3155/1837/1622 -f 3140/7382/5761 3156/1838/1623 3155/1837/1622 -f 3141/7384/5762 3157/7393/5767 3156/7394/1623 -f 3147/7386/1617 3163/7395/5768 3162/7396/1615 -f 3158/1824/1609 3157/7393/5767 3142/1818/1603 -f 3159/1826/1611 3158/1824/1609 3143/1825/1610 -f 3160/1828/1613 3159/1826/1611 3144/1827/1612 -f 3146/1841/1626 3160/1828/1613 3145/1829/1614 -f 3163/7395/5768 3149/7387/5764 3164/7397/5769 -f 3164/7398/5769 3150/1820/1605 3165/7399/5770 -f 3165/7399/5770 3151/7389/5765 3166/7400/5771 -f 3166/7400/5771 3152/7390/1607 3167/1856/1638 -f 3168/1845/1630 3136/1821/1606 3169/1834/1619 -f 3152/1822/1607 3168/1845/1630 3167/7401/1638 -f 3169/1834/1619 3153/7391/5766 3170/1847/1632 -f 3170/1847/1632 3154/7392/5760 3171/1849/1621 -f 3173/7402/1624 3157/7393/5767 3174/7403/5772 -f 3173/1839/1624 3172/1835/1620 3155/1837/1622 -f 3174/7403/5772 3158/1824/1609 3175/7404/5773 -f 3175/7404/5773 3159/1826/1611 3176/1840/1625 -f 3178/1843/1628 3162/1830/1615 3179/7405/5774 -f 3146/1841/1626 3178/1843/1628 3177/1842/1627 -f 3179/7406/5774 3163/7395/5768 3180/7407/5775 -f 3163/7395/5768 3181/7408/5776 3180/7407/5775 -f 3164/7398/5769 3182/7409/5777 3181/7410/5776 -f 3165/7399/5770 3183/1855/1637 3182/7409/5777 -f 3188/7411/1648 3174/7403/5772 3189/1873/1650 -f 3167/1856/1638 3183/1855/1637 3166/7400/5771 -f 3185/1846/1631 3184/1844/1629 3169/1834/1619 -f 3186/1848/1633 3185/1846/1631 3170/1847/1632 -f 3172/7412/1620 3186/1848/1633 3171/1849/1621 -f 3173/1852/1624 3188/1867/1648 3187/1850/1634 -f 3189/1873/1650 3175/7404/5773 3190/1871/1652 -f 3191/1874/1654 3176/1840/1625 3192/1875/1655 -f 3175/7404/5773 3191/1874/1654 3190/1871/1652 -f 3194/7413/1643 3179/7405/5774 3195/7414/1641 -f 3176/1840/1625 3193/1853/1635 3192/1875/1655 -f 3178/1843/1628 3194/7413/1643 3177/1842/1627 -f 3195/1859/1641 3180/7415/5775 3196/7416/5778 -f 3180/7415/5775 3197/7417/1661 3196/7416/5778 -f 3181/7410/5776 3198/1882/1662 3197/7418/1661 -f 3182/7409/5777 3200/1885/1665 3199/1884/1664 -f 3199/1884/1664 3198/1882/1662 3182/7409/5777 -f 3185/1846/1631 3204/1866/1647 3203/1864/1645 -f 3201/1854/1636 3200/1885/1665 3183/1855/1637 -f 3168/1845/1630 3201/1863/1636 3167/7401/1638 -f 3203/1864/1645 3184/1844/1629 3185/1846/1631 -f 3186/7419/1633 3187/1850/1634 3204/7420/1647 -f 3216/1858/1640 3194/7413/1643 3205/1889/1669 -f 3160/1828/1613 3216/1858/1640 3193/1853/1635 -f 3204/7420/1647 3210/1896/1675 3209/1895/1646 -f 3194/7413/1643 3206/7421/1642 3205/1889/1669 -f 3196/7416/5778 3207/1878/1658 3195/1859/1641 -f 3202/1857/1639 3208/1862/1644 3168/1845/1630 -f 3196/7416/5778 3218/7422/5779 3207/1878/1658 -f 3187/1850/1634 3211/1868/1649 3210/1896/1675 -f 3188/1867/1648 3212/1870/1651 3211/1868/1649 -f 3189/1873/1650 3213/1872/1653 3212/7423/1651 -f 3191/1874/1654 3214/1876/1656 3213/1872/1653 -f 3192/1875/1655 3215/1877/1657 3214/1876/1656 -f 3195/1859/1641 3217/1879/1659 3206/1860/1642 -f 3210/1896/1675 3224/7424/1678 3223/1894/1674 -f 3219/7425/1660 3218/7422/5779 3197/7417/1661 -f 3220/1883/1663 3219/1880/1660 3198/1882/1662 -f 3221/1886/1666 3220/1883/1663 3200/1885/1665 -f 3208/1862/1644 3235/7426/1671 3201/1863/1636 -f 3209/1865/1646 3222/1887/1667 3203/1864/1645 -f 3224/7424/1678 3212/1870/1651 3225/7427/1676 -f 3225/1897/1676 3213/1872/1653 3226/1888/1668 -f 3215/1877/1657 3216/1858/1640 3227/7428/5780 -f 3228/1890/1670 3206/7421/1642 3229/7429/5781 -f 3216/1858/1640 3228/1890/1670 3227/7428/5780 -f 3229/7429/5781 3217/7430/1659 3230/7431/5782 -f 3230/7432/5782 3207/1878/1658 3231/7433/5783 -f 3207/1878/1658 3232/7434/5784 3231/7433/5783 -f 3218/7422/5779 3233/7435/1682 3232/7434/5784 -f 3219/1880/1660 3234/1904/1683 3233/1903/1682 -f 3238/7436/5785 3224/7424/1678 3239/7437/1677 -f 3221/1886/1666 3234/1904/1683 3220/1883/1663 -f 3237/1893/1673 3236/1892/1672 3222/1887/1667 -f 3223/1909/1674 3237/1893/1673 3209/1865/1646 -f 3242/7438/5786 3229/7429/5781 3243/7439/5787 -f 3225/1897/1676 3240/1900/1679 3239/1898/1677 -f 3226/1888/1668 3241/1901/1680 3240/1900/1679 -f 3228/1890/1670 3242/7438/5786 3227/7428/5780 -f 3243/7439/5787 3230/7431/5782 3244/7440/5788 -f 3244/7441/5788 3231/7433/5783 3245/7442/5789 -f 3231/7433/5783 3246/7443/5790 3245/7442/5789 -f 3232/7444/5784 3247/1902/1681 3246/7445/5790 -f 3238/7446/5785 3253/1912/1690 3252/1911/1689 -f 3248/1905/1684 3247/1902/1681 3234/1904/1683 -f 3235/1891/1671 3248/1905/1684 3221/1886/1666 -f 3249/1906/1685 3235/7426/1671 3208/1862/1644 -f 3250/1907/1686 3249/1906/1685 3236/1892/1672 -f 3251/1908/1687 3250/1907/1686 3237/1893/1673 -f 3238/7447/5785 3251/1908/1687 3223/1909/1674 -f 3254/7448/5791 3240/1900/1679 3255/7449/5792 -f 3239/7450/1677 3254/7451/5791 3253/1912/1690 -f 3255/7449/5792 3241/1901/1680 3256/7452/5793 -f 3256/7452/5793 3215/1877/1657 3257/1931/1707 -f 3257/1931/1707 3227/7428/5780 3258/1914/1692 -f 3258/1914/1692 3242/7438/5786 3259/1915/1693 -f 3259/1915/1693 3243/7439/5787 3260/7453/5794 -f 3260/7453/5794 3244/7440/5788 3261/7454/5795 -f 3261/7455/5795 3245/7456/5789 3262/7457/5796 -f 3245/7456/5789 3263/7458/5797 3262/7457/5796 -f 3246/7445/5790 3264/1918/1696 3263/7459/5797 -f 3247/1902/1681 3265/1916/1694 3264/1918/1696 -f 3248/1905/1684 3266/1919/1697 3265/1916/1694 -f 3235/7426/1671 3267/1921/1699 3266/1923/1697 -f 3249/1906/1685 3268/1924/1701 3267/1921/1699 -f 3250/1907/1686 3269/1926/1703 3268/1924/1701 -f 3251/1908/1687 3252/1928/1689 3269/1926/1703 -f 3253/1912/1690 3271/7460/5798 3270/1910/1688 -f 3252/1911/1689 3270/1910/1688 3284/7461/1705 -f 3254/7448/5791 3272/7462/5799 3271/7463/5798 -f 3255/7449/5792 3273/7464/5800 3272/7462/5799 -f 3259/1915/1693 3275/1933/1709 3274/1913/1691 -f 3257/1931/1707 3273/7464/5800 3256/7452/5793 -f 3260/7453/5794 3276/7465/5801 3275/1933/1709 -f 3261/7455/5795 3277/7466/5802 3276/7467/5801 -f 3277/7466/5802 3263/7458/5797 3278/7468/5803 -f 3278/7469/5803 3264/1918/1696 3279/1935/1711 -f 3300/7470/5804 3270/7471/1688 3285/1939/1715 -f 3264/1918/1696 3280/1917/1695 3279/1935/1711 -f 3265/1916/1694 3281/1920/1698 3280/1917/1695 -f 3266/1923/1697 3297/1922/1700 3281/7472/1698 -f 3267/1921/1699 3282/1925/1702 3297/1922/1700 -f 3268/1924/1701 3283/1927/1704 3282/1925/1702 -f 3269/1926/1703 3284/1929/1705 3283/1927/1704 -f 3270/7471/1688 3286/1940/1716 3285/1939/1715 -f 3271/7463/5798 3287/1944/1718 3286/7473/1716 -f 3272/7462/5799 3288/1945/1720 3287/1944/1718 -f 3273/7464/5800 3289/1930/1706 3288/1945/1720 -f 3274/1913/1691 3289/1930/1706 3258/1914/1692 -f 3275/1933/1709 3292/7474/5805 3291/1932/1708 -f 3291/1932/1708 3290/1948/1723 3274/1913/1691 -f 3276/7475/5801 3293/1951/1726 3292/7476/5805 -f 3293/1951/1726 3278/7477/5803 3294/1949/1724 -f 3294/7478/1724 3279/1935/1711 3295/1934/1710 -f 3299/1937/1713 3284/1929/1705 3300/7479/5804 -f 3280/1917/1695 3296/1936/1712 3295/1934/1710 -f 3281/1920/1698 3297/1955/1700 3296/1936/1712 -f 3282/1925/1702 3298/1957/1730 3297/1922/1700 -f 3282/1925/1702 3299/1937/1713 3298/1957/1730 -f 3314/1960/1733 3285/1939/1715 3301/1938/1714 -f 3291/1932/1708 3307/7480/1741 3306/1947/1722 -f 3302/1941/1717 3301/1938/1714 3286/1940/1716 -f 3303/1943/1719 3302/7481/1717 3287/1944/1718 -f 3304/1946/1721 3303/1943/1719 3288/1945/1720 -f 3305/1965/1737 3304/1946/1721 3289/1930/1706 -f 3290/1948/1723 3305/1965/1737 3289/1930/1706 -f 3292/7476/5805 3308/1950/1725 3307/1970/1741 -f 3313/1978/1747 3300/7479/5804 3314/7482/1733 -f 3294/1949/1724 3309/1953/1727 3308/1950/1725 -f 3295/1934/1710 3310/1954/1728 3309/7483/1727 -f 3296/1936/1712 3311/1956/1729 3310/1954/1728 -f 3297/1955/1700 3312/1975/1731 3311/1956/1729 -f 3299/1937/1713 3313/1978/1747 3298/1957/1730 -f 3301/1938/1714 3316/1961/1734 3315/1982/1750 -f 3315/1982/1750 3328/1959/1732 3301/1938/1714 -f 3323/1990/1756 3309/1953/1727 3324/1973/1743 -f 3317/1963/1735 3316/1984/1734 3303/1943/1719 -f 3318/1964/1736 3317/1963/1735 3304/1946/1721 -f 3319/1966/1738 3318/1964/1736 3305/1965/1737 -f 3320/1967/1739 3319/1966/1738 3290/1948/1723 -f 3321/1988/1740 3320/1967/1739 3306/1947/1722 -f 3322/1971/1742 3321/1968/1740 3307/1970/1741 -f 3308/1950/1725 3323/1990/1756 3322/1971/1742 -f 3361/2038/1791 3349/2024/1781 3362/2023/1780 -f 3310/1954/1728 3325/1974/1744 3324/1993/1743 -f 3311/1956/1729 3326/1976/1745 3325/1974/1744 -f 3312/1958/1731 3339/1977/1746 3326/7484/1745 -f 3298/1957/1730 3327/1979/1748 3339/1977/1746 -f 3313/1978/1747 3328/1996/1732 3327/1979/1748 -f 3329/1981/1749 3341/1999/1761 3328/1959/1732 -f 3330/2001/1751 3329/1981/1749 3316/1961/1734 -f 3331/1985/1752 3330/1983/1751 3317/1963/1735 -f 3332/2004/1766 3331/1985/1752 3318/1964/1736 -f 3333/1986/1753 3332/2004/1766 3318/1964/1736 -f 3334/1987/1754 3319/1966/1738 3320/1967/1739 -f 3335/1989/1755 3334/2008/1754 3321/1968/1740 -f 3322/1971/1742 3336/1991/1757 3335/1989/1755 -f 3324/1973/1743 3337/2009/1758 3336/1991/1757 -f 3325/1974/1744 3338/1994/1759 3337/1992/1758 -f 3326/1976/1745 3339/2012/1746 3338/1994/1759 -f 3327/1979/1748 3341/1997/1761 3340/1995/1760 -f 3343/2000/1763 3342/1998/1762 3329/1981/1749 -f 3344/2018/1764 3343/2000/1763 3330/2001/1751 -f 3345/2003/1765 3344/2002/1764 3331/1985/1752 -f 3346/2005/1767 3345/2003/1765 3332/2004/1766 -f 3347/2006/1768 3333/1986/1753 3319/1966/1738 -f 3348/7485/1769 3347/2006/1768 3334/1987/1754 -f 3336/1991/1757 3349/2024/1781 3348/2007/1769 -f 3336/1991/1757 3350/2010/1770 3349/2024/1781 -f 3337/2009/1758 3351/2025/1771 3350/2010/1770 -f 3338/1994/1759 3352/2013/1772 3351/2011/1771 -f 3339/1977/1746 3353/2014/1773 3352/7486/1772 -f 3340/1995/1760 3354/2015/1774 3353/2014/1773 -f 3341/1999/1761 3342/1998/1762 3354/2029/1774 -f 3356/2016/1775 3355/2028/1784 3342/1998/1762 -f 3357/2017/1776 3356/2016/1775 3343/2000/1763 -f 3358/2019/1777 3357/2033/1776 3344/2002/1764 -f 3346/2005/1767 3358/2019/1777 3345/2003/1765 -f 3360/2037/1779 3359/2020/1778 3347/2006/1768 -f 3361/2038/1791 3360/2021/1779 3348/2007/1769 -f 3365/7487/5806 3354/2015/1774 3366/7488/5807 -f 3350/2010/1770 3363/2026/1782 3362/2023/1780 -f 3351/2011/1771 3364/2027/1783 3363/2043/1782 -f 3353/2014/1773 3365/7487/5806 3352/7486/1772 -f 3377/2054/1796 3365/7487/5806 3378/2052/1802 -f 3354/2029/1774 3355/2028/1784 3366/7489/5807 -f 3367/2030/1785 3380/2059/1807 3355/2028/1784 -f 3368/2031/1786 3367/2030/1785 3356/2016/1775 -f 3369/2047/1787 3368/2031/1786 3357/2017/1776 -f 3370/2034/1788 3369/2032/1787 3358/2019/1777 -f 3371/2035/1789 3370/2034/1788 3346/2005/1767 -f 3372/2036/1790 3371/2035/1789 3359/2020/1778 -f 3373/7490/1792 3372/2036/1790 3360/2037/1779 -f 3361/2038/1791 3374/2040/1793 3373/2039/1792 -f 3362/2023/1780 3375/2041/1794 3374/2040/1793 -f 3363/2026/1782 3376/2050/1795 3375/2041/1794 -f 3364/2027/1783 3377/2044/1796 3376/2042/1795 -f 3378/2052/1802 3366/7488/5807 3379/2055/1804 -f 3355/2028/1784 3379/2058/1804 3366/7489/5807 -f 3380/2059/1807 3381/2061/1809 3393/2057/1806 -f 3385/2066/1813 3373/7490/1792 3386/2064/1811 -f 3382/2045/1797 3381/2061/1809 3367/2030/1785 -f 3383/2046/1798 3382/2045/1797 3368/2031/1786 -f 3384/2048/1799 3383/2063/1798 3369/2032/1787 -f 3385/2066/1813 3370/2034/1788 3372/2036/1790 -f 3386/2069/1811 3374/2040/1793 3387/2067/1814 -f 3390/7491/5808 3377/2044/1796 3391/7492/1803 -f 3374/2040/1793 3388/2049/1800 3387/2067/1814 -f 3375/2041/1794 3389/2051/1801 3388/2049/1800 -f 3376/2042/1795 3390/7491/5808 3389/7493/1801 -f 3393/2057/1806 3394/7494/5809 3417/7495/5810 -f 3378/2052/1802 3392/2056/1805 3391/2053/1803 -f 3393/7496/1806 3392/2056/1805 3379/2055/1804 -f 3382/2045/1797 3399/7497/5811 3398/7498/5812 -f 3395/2060/1808 3394/7494/5809 3381/2061/1809 -f 3396/7499/5813 3395/2060/1808 3382/2045/1797 -f 3397/7500/5814 3396/7499/5813 3382/2045/1797 -f 3398/7498/5812 3397/7500/5814 3382/2045/1797 -f 3401/7501/5815 3370/2034/1788 3402/7502/5816 -f 3400/7503/1810 3399/7497/5811 3383/2046/1798 -f 3384/2048/1799 3401/7501/5815 3400/2062/1810 -f 3403/7504/5817 3385/2066/1813 3404/2065/1812 -f 3370/2034/1788 3403/7504/5817 3402/7502/5816 -f 3407/7505/5818 3388/2049/1800 3408/7506/5819 -f 3386/2064/1811 3405/7507/5820 3404/2065/1812 -f 3386/2069/1811 3406/2068/1815 3405/7508/5820 -f 3387/2067/1814 3407/7505/5818 3406/2068/1815 -f 3389/7493/1801 3412/7509/5821 3411/7510/5822 -f 3388/2049/1800 3409/7511/5823 3408/7506/5819 -f 3388/2049/1800 3410/2070/1816 3409/7511/5823 -f 3389/2051/1801 3411/7512/5822 3410/2070/1816 -f 3390/7491/5808 3413/7513/5824 3412/7509/5821 -f 3391/2053/1803 3415/7514/5825 3414/7515/5826 -f 3414/7515/5826 3413/7516/5824 3391/2053/1803 -f 3392/2056/1805 3416/7517/5827 3415/7514/5825 -f 3420/7518/1822 3419/2071/1817 3422/7519/1820 -f 3417/7495/5810 3416/7520/5827 3393/2057/1806 -f 3423/7521/5828 3422/2074/1820 3424/7522/5829 -f 3425/2091/1837 3424/7522/5829 3426/7523/5830 -f 3428/7524/5831 3425/2091/1837 3426/7523/5830 -f 3431/2078/1824 3427/2098/1835 3428/7525/5831 -f 3434/2081/1827 3430/2079/1825 3431/2078/1824 -f 3437/2084/1830 3433/2095/1828 3434/7526/1827 -f 3439/7527/5832 3436/2085/1831 3437/2084/1830 -f 3440/7528/5833 3439/7529/5832 3441/7530/5834 -f 3441/7531/5835 3439/7527/5835 3437/2084/5835 -f 3442/2073/1819 3441/7530/5834 3419/2071/1817 -f 3444/7532/5836 3418/2072/1818 3420/7518/1822 -f 3458/7533/5837 3440/7528/5833 3442/2073/1819 -f 3433/2095/1828 3453/2094/1840 3452/2100/1832 -f 3430/2079/1825 3451/2088/1834 3450/2102/1844 -f 3427/2098/1835 3449/2097/1842 3448/2110/1836 -f 3447/2105/1847 3423/7521/5828 3425/2091/1837 -f 3445/7534/5838 3420/7518/1822 3421/7535/1821 -f 3443/7536/5839 3442/2073/1819 3418/2072/1818 -f 3435/2083/1829 3454/2099/1843 3453/2094/1840 -f 3457/7537/5840 3438/7538/1838 3440/7528/5833 -f 3432/2080/1826 3452/2086/1832 3451/2088/1834 -f 3429/2077/1823 3450/2102/1844 3449/2097/1842 -f 3425/2091/1837 3448/2090/1836 3447/2105/1847 -f 3446/7539/5841 3421/2075/1821 3423/7521/5828 -f 3485/7540/5842 3470/2104/1846 3486/7541/5843 -f 3436/2085/1831 3455/2093/1839 3454/2099/1843 -f 3462/7542/5844 3445/7543/5838 3446/7539/5841 -f 3452/2100/1832 3469/2103/1845 3468/7544/1833 -f 3463/2107/1849 3446/7539/5841 3447/2105/1847 -f 3453/2094/1840 3470/2104/1846 3469/2103/1845 -f 3473/7545/5845 3455/2093/1839 3456/2096/1841 -f 3454/2099/1843 3471/2108/1850 3470/2104/1846 -f 3448/2110/1836 3465/2109/1851 3464/7546/1848 -f 3455/2093/1839 3472/7547/5846 3471/2108/1850 -f 3459/7548/5847 3458/7533/5837 3443/7536/5839 -f 3449/2097/1842 3466/2111/1852 3465/2109/1851 -f 3474/7549/5848 3456/7550/1841 3457/7537/5840 -f 3460/7551/5849 3443/7536/5839 3444/7532/5836 -f 3450/2102/1844 3467/2112/1853 3466/2111/1852 -f 3475/7552/5850 3457/7537/5840 3458/7533/5837 -f 3461/7553/5851 3444/7532/5836 3445/7534/5838 -f 3451/2088/1834 3468/2087/1833 3467/2112/1853 -f 3480/2120/5852 3463/2107/1849 3464/2106/1848 -f 3462/7542/5844 3463/2107/1849 3479/2141/5853 -f 3486/7541/5843 3471/2108/1850 3487/7554/5854 -f 3480/2120/5852 3465/7555/1851 3481/2118/5855 -f 3487/7554/5854 3472/7547/5846 3488/7556/5856 -f 3481/7557/5855 3466/2111/1852 3482/7558/5857 -f 3472/7547/5846 3473/7545/5845 3488/7556/5856 -f 3476/7559/5858 3475/7552/5850 3459/7548/5847 -f 3489/7560/5859 3473/7561/5845 3474/7549/5848 -f 3482/7558/5857 3467/2112/1853 3483/7562/5860 -f 3477/7563/5861 3459/7548/5847 3460/7551/5849 -f 3490/7564/5862 3474/7549/5848 3475/7552/5850 -f 3483/7562/5860 3468/2087/1833 3484/7565/5863 -f 3478/2137/5864 3460/7566/5849 3461/7567/5851 -f 3484/7565/5863 3469/7568/1845 3485/7569/5842 -f 3479/2141/5853 3461/7567/5851 3462/7542/5844 -f 3495/2142/1869 3480/2120/1854 3496/2119/1858 -f 3487/2113/1854 3488/2127/1854 3503/2114/1854 -f 3481/2118/1854 3497/2122/1859 3496/2119/1858 -f 3488/2127/1854 3504/2116/1855 3503/2114/1854 -f 3482/2121/1854 3498/2129/1861 3497/2122/1859 -f 3476/2123/1854 3492/2132/1863 3491/2124/1860 -f 3476/2123/1854 3477/2130/1854 3492/2132/1863 -f 3489/2126/1854 3505/2133/1864 3504/2116/1855 -f 3483/2128/1854 3499/2135/1865 3498/2129/1861 -f 3477/2130/1854 3478/2137/1854 3493/2131/1862 -f 3490/2125/1854 3491/2124/1860 3505/2133/1864 -f 3484/2134/1854 3485/2139/1854 3500/2136/1866 -f 3478/2137/1854 3479/2141/1854 3494/2138/1867 -f 3485/2139/1854 3486/2143/1854 3501/2140/1868 -f 3486/2143/1854 3487/2113/1854 3502/2115/1854 -f 3514/2151/1874 3492/2149/1863 3506/2148/1873 -f 3497/7570/1859 3498/7571/1861 3509/7572/1872 -f 3513/7573/1856 3505/2152/1864 3514/2151/1874 -f 3510/7574/5865 3498/7571/1861 3499/7575/1865 -f 3506/7576/1873 3494/2155/1867 3507/2154/1870 -f 3499/7575/1865 3500/7577/1866 3510/7574/5865 -f 3511/7578/5866 3500/2136/1866 3501/2140/1868 -f 3507/2154/1870 3496/7579/1858 3508/7580/1871 -f 3501/2140/1868 3502/2115/5867 3511/7578/5866 -f 3512/7581/5868 3502/2115/5867 3503/2114/1857 -f 3509/7572/1872 3496/7582/1858 3497/7570/1859 -f 3515/2158/1877 3554/2192/1908 3538/2194/1910 -f 3503/2114/1857 3513/2117/1856 3512/7581/5868 -f 3544/2171/1888 3523/7583/5869 3545/2195/1911 -f 3517/2159/1878 3540/7584/1880 3539/2157/1876 -f 3518/2160/1879 3541/2164/1882 3540/2161/1880 -f 3519/2163/1881 3542/2166/1884 3541/2164/1882 -f 3520/2169/1883 3543/2168/1886 3542/7585/1884 -f 3521/2167/1885 3544/2171/1888 3543/2168/1886 -f 3523/7586/5869 3546/2173/1890 3545/2213/1911 -f 3546/2173/1890 3526/2176/1893 3547/2175/1892 -f 3528/2178/1895 3548/2199/1915 3547/2175/1892 -f 3529/7587/5870 3549/2179/1896 3548/7588/1915 -f 3536/2191/1907 3554/2192/1908 3553/2189/1906 -f 3550/2182/1899 3549/2179/1896 3531/2181/1898 -f 3551/2184/1901 3550/2182/1899 3532/2183/1900 -f 3552/2186/1903 3551/2184/1901 3533/2185/1902 -f 3553/7589/1906 3552/2186/1903 3535/2188/1905 -f 3539/2157/1876 3567/2201/1917 3554/2192/1908 -f 3555/7590/1920 3540/7584/1880 3556/7591/1918 -f 3556/2202/1918 3541/2164/1882 3557/2205/1921 -f 3557/2205/1921 3542/2166/1884 3558/2207/1923 -f 3558/2211/1923 3543/2168/1886 3559/2209/1925 -f 3559/2209/1925 3544/2171/1888 3560/2196/1912 -f 3548/7588/1915 3563/2217/1930 3562/2216/1914 -f 3546/2173/1890 3561/2197/1913 3545/2213/1911 -f 3562/2198/1914 3561/2197/1913 3547/2175/1892 -f 3549/2179/1896 3564/2219/1932 3563/2217/1930 -f 3551/2184/1901 3566/7592/5871 3565/2200/1916 -f 3565/2200/1916 3564/2219/1932 3550/2182/1899 -f 3567/2201/1917 3568/2222/1935 3582/2221/1934 -f 3553/7589/1906 3566/7592/5871 3552/2186/1903 -f 3572/2210/1926 3560/2196/1912 3573/2226/1938 -f 3555/7590/1920 3569/7593/1919 3568/2222/1935 -f 3556/2202/1918 3570/2206/1922 3569/2203/1919 -f 3557/2205/1921 3571/2208/1924 3570/2206/1922 -f 3558/2211/1923 3572/2210/1926 3571/2225/1924 -f 3560/2196/1912 3574/2228/1927 3573/2226/1938 -f 3564/2219/1932 3579/2234/1945 3578/2218/1931 -f 3561/2197/1913 3575/2214/1928 3574/2212/1927 -f 3576/7594/1942 3575/2214/1928 3562/2198/1914 -f 3577/2215/1929 3576/2231/1942 3562/2216/1914 -f 3578/2218/1931 3577/2215/1929 3563/2217/1930 -f 3565/2200/1916 3580/2236/1947 3579/2234/1945 -f 3566/7592/5871 3581/2238/1949 3580/2236/1947 -f 3567/2201/1917 3581/7595/1949 3553/2189/1906 -f 3583/2220/1933 3569/7593/1919 3584/7596/1950 -f 3584/7597/1950 3570/2206/1922 3585/7598/5872 -f 3574/2212/1927 3591/7599/1955 3590/2242/1952 -f 3570/2206/1922 3586/2223/1936 3585/7598/5872 -f 3571/2208/1924 3587/7600/1937 3586/2223/1936 -f 3572/2210/1926 3588/2227/1939 3587/2224/1937 -f 3573/2226/1938 3589/2229/1940 3588/2227/1939 -f 3574/2212/1927 3590/2242/1952 3589/2244/1940 -f 3575/2214/1928 3592/7601/1941 3591/7599/1955 -f 3583/2220/1933 3610/2252/1960 3582/2221/1934 -f 3593/2232/1943 3592/2230/1941 3577/2215/1929 -f 3594/2233/1944 3593/2232/1943 3578/2218/1931 -f 3595/2235/1946 3594/2233/1944 3579/2234/1945 -f 3596/2237/1948 3595/2235/1946 3580/2236/1947 -f 3582/2221/1934 3596/2253/1948 3581/7595/1949 -f 3598/2259/1951 3585/7598/5872 3599/2257/1963 -f 3598/2256/1951 3597/2255/1962 3583/2220/1933 -f 3599/2257/1963 3586/2223/1936 3600/2260/1965 -f 3600/2260/1965 3587/7600/1937 3601/2262/1967 -f 3601/2266/1967 3588/2227/1939 3602/2264/1969 -f 3602/2264/1969 3589/2229/1940 3603/7602/1953 -f 3604/7603/1954 3620/7604/5873 3619/7605/5874 -f 3591/7599/1955 3618/2270/1973 3590/2242/1952 -f 3591/7599/1955 3604/7606/1954 3618/2270/1973 -f 3605/7607/5875 3604/7603/1954 3592/2230/1941 -f 3606/2248/1956 3605/7607/5875 3592/2230/1941 -f 3607/2249/1957 3606/2248/1956 3593/2232/1943 -f 3608/2250/1958 3607/2249/1957 3594/2233/1944 -f 3609/2251/1959 3608/2250/1958 3595/2235/1946 -f 3610/2252/1960 3609/7608/1959 3596/2253/1948 -f 3598/2240/1951 3612/7609/1964 3611/7610/1961 -f 3599/2257/1963 3613/2261/1966 3612/2258/1964 -f 3600/2260/1965 3614/2263/1968 3613/2261/1966 -f 3601/2266/1967 3615/2265/1970 3614/7611/1968 -f 3602/2268/1969 3616/2267/1971 3615/7612/1970 -f 3603/2243/1953 3617/2269/1972 3616/2267/1971 -f 3590/2242/1952 3618/2270/1973 3617/2269/1972 -f 3604/7606/1954 3619/7613/5874 3618/2270/1973 -f 3605/7607/5875 3621/7614/5876 3620/7604/5873 -f 3606/2248/1956 3622/7615/5877 3621/7614/5876 -f 3607/2249/1957 3623/7616/5878 3622/7615/5877 -f 3608/7617/1958 3624/7618/5879 3623/7619/5878 -f 3609/7608/1959 3625/7620/5880 3624/7618/5879 -f 3597/2255/1962 3627/2274/1977 3626/2273/1976 -f 3597/2255/1962 3625/7620/5880 3610/2252/1960 -f 3627/7621/1977 3612/7609/1964 3628/7622/5881 -f 3628/7623/5881 3613/2261/1966 3629/7624/1980 -f 3629/2277/1980 3614/7625/1968 3630/2275/1978 -f 3630/2280/1978 3615/7612/1970 3631/2278/1981 -f 3631/2278/1981 3616/2267/1971 3632/2281/1983 -f 3632/2281/1983 3617/2269/1972 3633/2271/1974 -f 3648/7626/5882 3619/7613/5874 3634/7627/5883 -f 3618/2270/1973 3648/7626/5882 3633/2271/1974 -f 3619/7628/5874 3635/7629/5884 3634/7630/5883 -f 3620/7604/5873 3636/7631/5885 3635/7632/5884 -f 3621/7633/5876 3637/7634/5886 3636/7635/5885 -f 3622/7636/5877 3638/7637/5887 3637/7634/5886 -f 3623/7619/5878 3639/7638/5888 3638/7637/5887 -f 3624/7618/5879 3640/2285/1987 3639/7638/5888 -f 3625/7620/5880 3626/2273/1976 3640/2285/1987 -f 3627/7621/1977 3642/7639/5889 3641/7640/1975 -f 3642/7641/5889 3629/2277/1980 3643/7642/5890 -f 3634/7630/5883 3650/7643/5891 3649/7644/5892 -f 3629/2277/1980 3644/2276/1979 3643/7642/5890 -f 3630/2275/1978 3645/7645/1982 3644/2276/1979 -f 3631/2278/1981 3646/2282/1984 3645/2279/1982 -f 3632/2281/1983 3647/2283/1985 3646/2282/1984 -f 3633/2271/1974 3648/7626/5882 3647/2283/1985 -f 3634/7627/5883 3649/7646/5892 3648/7626/5882 -f 3635/7647/5884 3651/2288/1990 3650/7648/5891 -f 3636/7635/5885 3652/2289/1991 3651/2288/1990 -f 3637/7634/5886 3653/2291/1993 3652/2289/1991 -f 3638/7637/5887 3654/2293/1995 3653/2291/1993 -f 3639/7638/5888 3655/2284/1986 3654/2293/1995 -f 3641/2272/1975 3656/2286/1988 3626/2273/1976 -f 3641/7640/1975 3658/7649/5893 3657/7650/5894 -f 3658/7651/5893 3643/7642/5890 3659/7652/5895 -f 3659/7652/5895 3644/2276/1979 3660/7653/5896 -f 3660/7653/5896 3645/7645/1982 3661/7654/5897 -f 3661/7655/5897 3646/2282/1984 3662/2296/1998 -f 3662/2296/1998 3647/2283/1985 3663/2294/1996 -f 3664/7656/5898 3648/7626/5882 3665/7657/5899 -f 3647/2283/1985 3664/7656/5898 3663/2294/1996 -f 3665/7657/5899 3649/7646/5892 3666/7658/5900 -f 3649/7644/5892 3667/7659/5901 3666/7660/5900 -f 3650/7648/5891 3668/7661/5902 3667/7662/5901 -f 3654/2293/1995 3673/7663/5903 3672/7664/5904 -f 3669/2287/1989 3668/7661/5902 3651/2288/1990 -f 3670/2290/1992 3669/2287/1989 3652/2289/1991 -f 3671/2292/1994 3670/2290/1992 3653/2291/1993 -f 3672/7664/5904 3671/2292/1994 3654/2293/1995 -f 3674/7665/5905 3659/7652/5895 3675/7666/5906 -f 3656/2286/1988 3673/7663/5903 3655/2284/1986 -f 3658/7649/5893 3674/7667/5905 3657/7650/5894 -f 3675/7666/5906 3660/7653/5896 3676/7668/5907 -f 3676/7668/5907 3661/7654/5897 3677/7669/5908 -f 3677/7670/5908 3662/2296/1998 3678/2295/1997 -f 3666/7660/5900 3679/7671/5909 3694/2310/2012 -f 3667/7662/5901 3680/7672/5910 3679/7673/5909 -f 3668/7661/5902 3681/7674/5911 3680/7672/5910 -f 3669/2287/1989 3682/7675/5912 3681/7674/5911 -f 3670/2290/1992 3683/7676/5913 3682/7675/5912 -f 3657/7677/5894 3684/7678/5914 3656/2286/1988 -f 3672/7664/5904 3683/7676/5913 3671/2292/1994 -f 3685/7679/5915 3674/7667/5905 3686/7680/2001 -f 3686/2299/2001 3675/7666/5906 3687/2297/1999 -f 3675/7666/5906 3688/7681/5916 3687/2297/1999 -f 3688/7681/5916 3677/7669/5908 3689/7682/5917 -f 3689/7683/5917 3678/2295/1997 3690/2302/2004 -f 3690/2302/2004 3663/2294/1996 3691/2300/2002 -f 3691/2300/2002 3664/7656/5898 3692/7684/5918 -f 3692/7684/5918 3665/7657/5899 3693/7685/5919 -f 3665/7657/5899 3694/7686/2012 3693/7685/5919 -f 3694/2310/2012 3696/2305/2007 3695/2304/2006 -f 3680/7672/5910 3696/7687/2007 3679/7673/5909 -f 3680/7672/5910 3698/2308/2010 3697/2307/2009 -f 3681/7674/5911 3699/7688/5920 3698/2308/2010 -f 3682/7675/5912 3700/7689/5921 3699/7688/5920 -f 3683/7676/5913 3701/7690/5922 3700/7689/5921 -f 3672/7664/5904 3702/7691/5923 3701/7690/5922 -f 3673/7663/5903 3684/7678/5914 3702/7691/5923 -f 3704/7692/5924 3689/7682/5917 3705/7693/5925 -f 3686/7680/2001 3703/7694/2000 3685/7679/5915 -f 3688/7681/5916 3704/7692/5924 3687/2297/1999 -f 3705/7695/5925 3690/2302/2004 3706/2301/2003 -f 3698/2308/2010 3709/7696/5926 3708/2306/2008 -f 3697/2307/2009 3707/7697/2005 3696/7687/2007 -f 3699/7688/5920 3710/7698/5927 3709/7696/5926 -f 3685/7699/5915 3711/2322/2024 3684/7678/5914 -f 3701/7690/5922 3710/7698/5927 3700/7689/5921 -f 3712/2312/2014 3703/7694/2000 3713/2313/2015 -f 3703/2298/2000 3714/7700/5928 3713/7701/2015 -f 3714/7700/5928 3704/7692/5924 3715/7702/5929 -f 3715/7702/5929 3705/7693/5925 3716/7703/5930 -f 3716/7704/5930 3706/2301/2003 3717/2316/2018 -f 3717/2316/2018 3691/2300/2002 3718/2314/2016 -f 3718/2314/2016 3692/7684/5918 3719/2319/2021 -f 3719/2319/2021 3693/7685/5919 3720/7705/5931 -f 3693/7685/5919 3721/7706/2011 3720/7705/5931 -f 3695/2304/2006 3722/7707/5932 3721/2309/2011 -f 3707/7697/2005 3723/7708/5933 3722/7709/5932 -f 3708/2306/2008 3723/7708/5933 3697/2307/2009 -f 3708/2306/2008 3725/7710/5934 3724/7711/5935 -f 3709/7696/5926 3726/7712/5936 3725/7710/5934 -f 3710/7698/5927 3727/7713/5937 3726/7712/5936 -f 3701/7690/5922 3728/2324/2026 3727/7713/5937 -f 3702/7691/5923 3711/2322/2024 3728/2324/2026 -f 3713/7701/2015 3730/7714/5938 3729/7715/2013 -f 3730/7714/5938 3715/7702/5929 3731/7716/5939 -f 3731/7716/5939 3716/7703/5930 3732/7717/5940 -f 3732/7718/5940 3717/2316/2018 3733/2315/2017 -f 3722/7709/5932 3735/7719/5941 3734/7720/2022 -f 3722/7707/5932 3734/2320/2022 3721/2309/2011 -f 3723/7708/5933 3736/7721/5942 3735/7719/5941 -f 3724/7711/5935 3737/7722/5943 3736/7721/5942 -f 3725/7710/5934 3738/7723/5944 3737/7722/5943 -f 3711/2322/2024 3739/7724/5945 3767/7725/5946 -f 3727/7713/5937 3738/7723/5944 3726/7712/5936 -f 3711/2322/2024 3767/7725/5946 3754/2323/2025 -f 3729/7715/2013 3741/7726/5947 3740/7727/2019 -f 3740/2317/2019 3739/7728/5945 3712/2312/2014 -f 3730/7714/5938 3742/7729/5948 3741/7726/5947 -f 3731/7716/5939 3743/7730/5949 3742/7729/5948 -f 3732/7718/5940 3744/7731/5950 3743/7732/5949 -f 3733/2315/2017 3745/2318/2020 3744/7731/5950 -f 3720/7705/5931 3745/2318/2020 3719/2319/2021 -f 3721/7706/2011 3746/7733/5951 3720/7705/5931 -f 3748/7734/2023 3735/7719/5941 3749/7735/5952 -f 3748/2321/2023 3747/7736/5953 3721/2309/2011 -f 3749/7735/5952 3736/7721/5942 3750/7737/5954 -f 3750/7737/5954 3737/7722/5943 3751/7738/5955 -f 3751/7738/5955 3738/7723/5944 3752/7739/5956 -f 3738/7723/5944 3753/2326/2028 3752/7739/5956 -f 3753/2326/2028 3728/2324/2026 3754/2323/2025 -f 3740/7727/2019 3756/2328/2030 3755/7740/5957 -f 3755/7741/5957 3739/7728/5945 3740/2317/2019 -f 3741/7726/5947 3757/2329/2031 3756/2328/2030 -f 3742/7729/5948 3758/2331/2033 3757/2329/2031 -f 3743/7732/5949 3759/2334/2035 3758/2333/2033 -f 3744/7731/5950 3760/2336/2037 3759/2334/2035 -f 3761/7742/5958 3749/7735/5952 3762/2339/2040 -f 3746/7733/5951 3760/2336/2037 3745/2318/2020 -f 3748/2321/2023 3761/7743/5958 3747/7736/5953 -f 3762/2339/2040 3750/7737/5954 3763/2337/2038 -f 3763/2337/2038 3751/7738/5955 3764/2340/2041 -f 3764/2340/2041 3752/7739/5956 3765/2342/2043 -f 3765/2342/2043 3753/2326/2028 3766/2325/2027 -f 3767/7725/5946 3769/7744/5959 3768/7745/5960 -f 3754/2323/2025 3767/7725/5946 3766/2325/2027 -f 3755/7741/5957 3769/7746/5959 3739/7728/5945 -f 3755/7740/5957 3771/2327/2029 3770/7747/5961 -f 3760/2336/2037 3776/2346/2047 3775/7748/5962 -f 3772/2330/2032 3771/2327/2029 3757/2329/2031 -f 3773/2332/2034 3772/7749/2032 3758/2333/2033 -f 3774/2335/2036 3773/2332/2034 3759/2334/2035 -f 3775/7748/5962 3774/2335/2036 3760/2336/2037 -f 3746/7733/5951 3777/2347/2048 3776/2346/2047 -f 3777/7750/2048 3761/7743/5958 3778/7751/5963 -f 3778/7752/5963 3762/2339/2040 3779/2338/2039 -f 3783/2348/2049 3767/7725/5946 3768/7745/5960 -f 3763/2337/2038 3780/2341/2042 3779/2338/2039 -f 3764/2340/2041 3781/2343/2044 3780/2341/2042 -f 3765/2342/2043 3782/2344/2045 3781/2343/2044 -f 3766/2325/2027 3783/2348/2049 3782/2344/2045 -f 3769/7746/5959 3785/2352/2053 3784/2351/2052 -f 3769/7744/5959 3784/7753/2052 3768/7745/5960 -f 3770/7754/5961 3786/7755/5964 3785/7756/2053 -f 3771/2327/2029 3787/7757/5965 3786/7758/5964 -f 3772/7749/2032 3788/7759/5966 3787/7760/5965 -f 3773/2332/2034 3789/7761/5967 3788/7759/5966 -f 3777/7750/2048 3791/7762/5968 3790/7763/2046 -f 3775/7748/5962 3789/7761/5967 3774/2335/2036 -f 3791/7764/5968 3779/7765/2039 3792/7766/5969 -f 3792/7767/5969 3780/2341/2042 3793/7768/5970 -f 3793/7768/5970 3781/2343/2044 3794/7769/5971 -f 3794/7769/5971 3782/2344/2045 3795/2349/2050 -f 3785/7756/2053 3797/7770/5972 3796/7771/2051 -f 3786/7755/5964 3798/7772/5973 3797/7770/5972 -f 3787/7773/5965 3799/7774/5974 3798/7775/5973 -f 3788/7759/5966 3800/7776/5975 3799/7777/5974 -f 3789/7761/5967 3801/2354/2055 3800/7776/5975 -f 3790/7763/2046 3803/7778/5976 3802/7779/2064 -f 3776/2346/2047 3801/2354/2055 3775/7748/5962 -f 3803/7780/5976 3792/7766/5969 3804/7781/5977 -f 3804/7781/5977 3793/7782/5970 3805/7783/5978 -f 3805/7783/5978 3794/7784/5971 3806/7785/5979 -f 3806/7786/5979 3795/2349/2050 3807/2357/2058 -f 3807/2357/2058 3783/2348/2049 3808/2355/2056 -f 3808/2355/2056 3768/7745/5960 3809/2374/2073 -f 3809/2374/2073 3784/7753/2052 3810/2376/2075 -f 3796/2350/2051 3810/7787/2075 3784/2351/2052 -f 3796/7771/2051 3812/2382/2080 3811/2380/2078 -f 3797/7770/5972 3814/2360/2061 3813/2359/2060 -f 3813/2359/2060 3812/2382/2080 3797/7770/5972 -f 3798/7775/5973 3815/7788/5980 3814/7789/2061 -f 3799/7774/5974 3816/7790/5981 3815/7788/5980 -f 3790/2345/2046 3817/2353/2054 3776/2346/2047 -f 3801/2354/2055 3816/7791/5981 3800/7776/5975 -f 3819/2361/2062 3804/7781/5977 3820/2364/2065 -f 3802/7779/2064 3818/7792/5982 3790/7763/2046 -f 3803/7778/5976 3819/7793/2062 3802/7779/2064 -f 3820/2364/2065 3805/7783/5978 3821/2366/2067 -f 3821/2366/2067 3806/7785/5979 3822/2368/2069 -f 3822/2368/2069 3807/7794/2058 3823/2370/2057 -f 3814/7789/2061 3825/2386/2083 3824/2385/2059 -f 3815/7788/5980 3826/2388/2085 3825/2386/2083 -f 3816/7790/5981 3827/2390/2087 3826/2388/2085 -f 3844/2402/2095 3831/2369/2070 3845/2404/2097 -f 3817/2353/2054 3827/2392/2087 3801/2354/2055 -f 3802/7779/2064 3828/7795/2063 3818/7792/5982 -f 3819/2361/2062 3829/2365/2066 3828/2362/2063 -f 3820/2364/2065 3830/2367/2068 3829/2365/2066 -f 3821/2366/2067 3831/2369/2070 3830/2367/2068 -f 3822/2368/2069 3832/2371/2071 3831/2369/2070 -f 3823/2370/2057 3833/2373/2072 3832/2371/2071 -f 3808/2355/2056 3834/2375/2074 3833/7796/2072 -f 3809/2374/2073 3835/2377/2076 3834/2375/2074 -f 3836/2397/2077 3835/2377/2076 3810/2376/2075 -f 3837/2381/2079 3836/2378/2077 3811/2380/2078 -f 3838/2383/2081 3837/2381/2079 3812/2382/2080 -f 3839/2399/2082 3838/2383/2081 3824/2358/2059 -f 3840/2387/2084 3839/2384/2082 3825/2386/2083 -f 3842/7797/2088 3841/2389/2086 3827/2390/2087 -f 3843/7798/5983 3842/2391/2088 3817/2353/2054 -f 3818/7799/5982 3843/7798/5983 3817/2353/2054 -f 3830/2367/2068 3844/2402/2095 3829/2365/2066 -f 3845/2404/2097 3832/2371/2071 3846/2406/2099 -f 3846/2406/2099 3833/2373/2072 3847/2394/2089 -f 3836/2378/2077 3849/2410/2103 3848/7800/2091 -f 3843/7798/5983 3828/7801/2063 3853/7802/5984 -f 3850/2398/2092 3837/2381/2079 3838/2383/2081 -f 3851/2400/2093 3850/7803/2092 3839/2384/2082 -f 3852/2401/2094 3851/2400/2093 3840/2387/2084 -f 3841/2389/2086 3852/2401/2094 3826/2388/2085 -f 3853/7804/5984 3829/2365/2066 3854/2403/2096 -f 3858/2411/2104 3834/2393/2074 3859/7805/2090 -f 3844/2402/2095 3855/2405/2098 3854/2403/2096 -f 3845/2404/2097 3856/2407/2100 3855/2405/2098 -f 3846/2406/2099 3857/2408/2101 3856/2407/2100 -f 3847/2394/2089 3858/2411/2104 3857/2408/2101 -f 3837/2381/2079 3862/2417/2110 3861/2409/2102 -f 3835/2377/2076 3860/2413/2106 3859/2395/2090 -f 3848/2396/2091 3860/2413/2106 3835/2377/2076 -f 3850/7803/2092 3863/7806/5985 3862/7807/2110 -f 3851/2400/2093 3864/7808/5986 3863/7806/5985 -f 3852/2401/2094 3865/2426/2118 3864/7808/5986 -f 3841/2389/2086 3866/2428/2120 3865/2426/2118 -f 3843/7798/5983 3866/2430/2120 3842/2391/2088 -f 3868/7809/5987 3855/2405/2098 3869/7810/5988 -f 3854/2403/2096 3868/7809/5987 3853/7804/5984 -f 3869/7810/5988 3856/2407/2100 3870/7811/5989 -f 3870/7811/5989 3857/2408/2101 3871/2412/2105 -f 3848/7800/2091 3873/2419/2112 3872/7812/2108 -f 3862/7807/2110 3875/2422/2114 3874/2421/2109 -f 3861/2409/2102 3873/2419/2112 3849/2410/2103 -f 3863/7806/5985 3876/2424/2116 3875/2422/2114 -f 3867/2431/2122 3853/7802/5984 3877/2449/2137 -f 3865/2426/2118 3876/2424/2116 3864/7808/5986 -f 3877/7813/2137 3868/7809/5987 3878/2434/2125 -f 3878/2434/2125 3869/7810/5988 3879/2432/2123 -f 3879/2432/2123 3870/7811/5989 3880/2435/2126 -f 3880/2435/2126 3871/2412/2105 3881/2437/2128 -f 3881/2437/2128 3858/2411/2104 3882/2439/2130 -f 3882/2439/2130 3859/7805/2090 3883/2441/2107 -f 3883/2414/2107 3884/2444/2134 3896/7814/2132 -f 3886/2420/2113 3900/7815/5990 3899/7816/2136 -f 3872/2415/2108 3884/2444/2134 3860/2413/2106 -f 3886/2448/2113 3885/2418/2111 3874/2416/2109 -f 3887/2423/2115 3886/2420/2113 3875/2422/2114 -f 3888/2425/2117 3887/2423/2115 3876/2424/2116 -f 3889/2427/2119 3888/2425/2117 3865/2426/2118 -f 3890/7817/2121 3889/2427/2119 3866/2428/2120 -f 3878/7818/2125 3891/2451/2139 3877/2449/2137 -f 3878/2434/2125 3892/2433/2124 3891/2454/2139 -f 3879/2432/2123 3893/2436/2127 3892/2433/2124 -f 3880/2435/2126 3894/2438/2129 3893/2436/2127 -f 3881/2437/2128 3895/2440/2131 3894/2438/2129 -f 3882/2439/2130 3896/2442/2132 3895/2440/2131 -f 3898/2446/2135 3897/7819/2133 3873/2419/2112 -f 3899/2447/2136 3898/2446/2135 3885/2418/2111 -f 3887/2423/2115 3901/7820/5991 3900/7815/5990 -f 3905/2455/2142 3894/2438/2129 3906/7821/5992 -f 3889/2427/2119 3901/7820/5991 3888/2425/2117 -f 3867/2431/2122 3902/2450/2138 3890/2429/2121 -f 3877/2449/2137 3903/2452/2140 3902/2450/2138 -f 3891/2454/2139 3904/2453/2141 3903/7822/2140 -f 3892/2433/2124 3905/2455/2142 3904/2453/2141 -f 3906/7821/5992 3895/2440/2131 3907/2464/2148 -f 3907/2464/2148 3896/2442/2132 3908/2462/2146 -f 3898/2446/2135 3912/7823/5993 3911/7824/2144 -f 3910/2469/2152 3909/7825/2143 3884/2444/2134 -f 3897/2443/2133 3910/2469/2152 3884/2444/2134 -f 3899/7816/2136 3913/7826/5994 3912/7827/5993 -f 3900/7815/5990 3914/7828/5995 3913/7826/5994 -f 3901/7820/5991 3915/7829/5996 3914/7828/5995 -f 3889/2427/2119 3916/7830/2145 3915/7829/5996 -f 3917/7831/5997 3905/2455/2142 3918/7832/5998 -f 3904/7833/2141 3917/7834/5997 3903/2452/2140 -f 3918/7832/5998 3906/7821/5992 3919/7835/5999 -f 3919/7835/5999 3907/2464/2148 3920/2463/2147 -f 3897/2443/2133 3922/2471/2154 3921/2470/2153 -f 3896/2442/2132 3909/7836/2143 3908/2462/2146 -f 3921/2470/2153 3910/2469/2152 3897/2443/2133 -f 3911/2459/2144 3923/2473/2156 3922/2471/2154 -f 3912/7827/5993 3924/2478/2158 3923/7837/2156 -f 3913/7826/5994 3925/2479/2160 3924/2478/2158 -f 3927/2465/2149 3917/7834/5997 3928/2482/2163 -f 3915/7829/5996 3925/2479/2160 3914/7828/5995 -f 3902/2450/2138 3926/2480/2161 3916/2461/2145 -f 3902/2450/2138 3927/2465/2149 3926/2480/2161 -f 3928/2482/2163 3918/7838/5998 3929/2484/2165 -f 3929/7839/2165 3919/7835/5999 3930/2490/2167 -f 3930/2490/2167 3920/2463/2147 3931/2488/2169 -f 3931/2488/2169 3908/2462/2146 3932/7840/2150 -f 3925/2479/2160 3939/7841/6000 3938/7842/6001 -f 3910/2469/2152 3933/2492/2172 3909/7825/2143 -f 3935/2472/2155 3934/2468/2151 3922/2471/2154 -f 3936/7843/2157 3935/2472/2155 3923/2473/2156 -f 3937/2477/2159 3936/7844/2157 3924/2478/2158 -f 3938/7842/6001 3937/2477/2159 3925/2479/2160 -f 3916/7845/2145 3939/7846/6000 3915/7847/5996 -f 3946/7848/6002 3932/7840/2150 3947/7849/2175 -f 3916/7845/2145 3941/7850/2162 3940/2513/2190 -f 3927/2465/2149 3942/2483/2164 3926/2480/2161 -f 3928/2482/2163 3943/2485/2166 3942/2483/2164 -f 3929/2484/2165 3944/2487/2168 3943/2485/2166 -f 3930/2486/2167 3945/7851/2170 3944/2487/2168 -f 3931/2488/2169 3946/7848/6002 3945/2489/2170 -f 3932/2466/2150 3933/2494/2172 3947/2496/2175 -f 3934/2468/2151 3951/2501/2180 3950/2500/2179 -f 3949/2493/2173 3948/2491/2171 3910/2469/2152 -f 3950/2500/2179 3949/2493/2173 3934/2468/2151 -f 3935/2472/2155 3952/2503/2182 3951/2501/2180 -f 3936/2474/2157 3953/2506/2184 3952/2505/2182 -f 3937/7852/2159 3954/2508/2186 3953/2506/2184 -f 3938/7842/6001 3955/7853/2188 3954/7854/2186 -f 3941/2481/2162 3942/2483/2164 3956/2514/2191 -f 3940/2513/2190 3955/2511/2188 3939/7846/6000 -f 3956/2514/2191 3943/2485/2166 3957/7855/6003 -f 3957/7855/6003 3944/2487/2168 3958/7856/6004 -f 3958/7856/6004 3945/7851/2170 3959/7857/6005 -f 3959/7857/6005 3946/7858/6002 3960/7859/6006 -f 3960/7860/6006 3947/7849/2175 3961/7861/2174 -f 3969/2512/2189 3941/7850/2162 3970/2517/2194 -f 3948/2491/2171 3962/7862/2176 3933/2492/2172 -f 3964/2499/2178 3963/2498/2177 3949/2493/2173 -f 3965/2502/2181 3964/2499/2178 3951/2501/2180 -f 3966/2504/2183 3965/7863/2181 3952/2505/2182 -f 3967/2507/2185 3966/2504/2183 3953/2506/2184 -f 3968/7864/2187 3967/2507/2185 3954/2508/2186 -f 3969/2512/2189 3968/2509/2187 3955/2511/2188 -f 3972/2520/2197 3957/7855/6003 3973/2522/2199 -f 3956/2514/2191 3972/2520/2197 3971/2515/2192 -f 3973/2522/2199 3958/7856/6004 3974/2523/2200 -f 3974/2523/2200 3959/7857/6005 3975/2525/2202 -f 3975/2525/2202 3960/7859/6006 3976/2527/2204 -f 3976/7865/2204 3961/2495/2174 3977/2532/2206 -f 3964/2499/2178 3979/2535/2211 3978/2516/2193 -f 3961/2495/2174 3962/2497/2176 3977/2532/2206 -f 3965/7863/2181 3980/2538/2213 3979/2537/2211 -f 3966/2504/2183 3981/2540/2215 3980/2538/2213 -f 3967/2507/2185 3982/2542/2217 3981/2540/2215 -f 3969/2512/2189 3982/2544/2217 3968/2509/2187 -f 3963/2498/2177 3992/2554/2227 3991/2533/2209 -f 3941/2481/2162 3984/2519/2196 3970/7866/2194 -f 3971/2515/2192 3985/2521/2198 3984/2519/2196 -f 3973/2522/2199 3986/2524/2201 3985/2521/2198 -f 3974/2523/2200 3987/2526/2203 3986/2524/2201 -f 3975/2525/2202 3988/2528/2205 3987/2526/2203 -f 3976/2527/2204 3989/2530/2207 3988/2528/2205 -f 3977/2532/2206 3990/2531/2208 3989/7867/2207 -f 3999/2548/2221 3985/2521/2198 4000/2560/2233 -f 3993/2534/2210 3992/2554/2227 3978/2516/2193 -f 3994/7868/2212 3993/2534/2210 3979/2535/2211 -f 3995/2539/2214 3994/2536/2212 3980/2538/2213 -f 3996/2557/2230 3995/2539/2214 3981/2540/2215 -f 3997/2541/2216 3996/2557/2230 3981/2540/2215 -f 3998/2547/2218 3997/2541/2216 3982/2542/2217 -f 4001/2562/2235 3986/2524/2201 4002/7869/6007 -f 3985/2521/2198 4001/2562/2235 4000/2560/2233 -f 4002/7869/6007 3987/2526/2203 4003/2565/2238 -f 4005/2552/2225 3989/2530/2207 4006/2550/2223 -f 3987/2526/2203 4004/2545/2219 4003/2565/2238 -f 3988/2528/2205 4005/2552/2225 4004/2545/2219 -f 4006/7870/2223 3990/2531/2208 4007/7871/6008 -f 3990/2531/2208 4008/7872/2240 4023/7873/6009 -f 3948/2491/2171 4008/2567/2240 3962/7862/2176 -f 4012/2573/2231 3984/2519/2196 4013/2549/2222 -f 3991/2533/2209 4009/2568/2241 3948/2491/2171 -f 4011/2559/2232 4010/7874/2220 3998/2543/2218 -f 3983/2518/2195 4011/2559/2232 3998/2543/2218 -f 3970/2517/2194 4012/2558/2231 3983/2518/2195 -f 3992/2554/2227 4015/2578/2249 4014/2553/2226 -f 4005/2552/2225 4021/2551/2224 4004/2545/2219 -f 3990/2531/2208 4023/7873/6009 4007/7871/6008 -f 3993/2534/2210 4016/2580/2244 4015/2578/2249 -f 4031/2563/2236 4002/7869/6007 4019/2592/2260 -f 4017/2555/2228 4016/2571/2244 3994/2536/2212 -f 4018/2556/2229 4017/2555/2228 3995/2539/2214 -f 4010/2546/2220 4018/2556/2229 3997/2541/2216 -f 3999/2548/2221 4060/2561/2234 4013/2549/2222 -f 4002/7869/6007 4020/2564/2237 4019/2592/2260 -f 4006/7870/2223 4023/7873/6009 4022/7875/6010 -f 4004/2545/2219 4021/2551/2224 4020/2564/2237 -f 4006/2550/2223 4022/7876/6010 4021/2551/2224 -f 4023/7873/6009 4024/2576/2239 4035/2575/2247 -f 4017/2555/2228 4027/2583/2253 4026/2570/2243 -f 4014/2553/2226 4025/2569/2242 3991/2533/2209 -f 4018/2556/2229 4028/2585/2255 4027/2583/2253 -f 4010/7874/2220 4029/2588/2257 4028/2587/2255 -f 4011/2559/2232 4030/2589/2245 4029/2588/2257 -f 4032/2594/2262 4021/2551/2224 4033/2596/2264 -f 4000/2560/2233 4031/2563/2236 4060/2561/2234 -f 4020/2564/2237 4032/2594/2262 4019/2592/2260 -f 4033/2596/2264 4022/7876/6010 4034/2598/2266 -f 4022/7875/6010 4035/2575/2247 4034/2601/2266 -f 4030/2572/2245 4060/2561/2234 4043/7877/2258 -f 4037/2577/2248 4036/7878/2246 4024/2566/2239 -f 4025/2569/2242 4037/2577/2248 4009/2568/2241 -f 4038/2579/2250 4025/2569/2242 4015/2578/2249 -f 4039/7879/2251 4038/2579/2250 4016/2580/2244 -f 4040/2582/2252 4039/2581/2251 4026/2570/2243 -f 4041/2584/2254 4040/2582/2252 4027/2583/2253 -f 4042/7880/2256 4041/2584/2254 4028/2585/2255 -f 4043/2590/2258 4042/2586/2256 4029/2588/2257 -f 4025/2569/2242 4054/2605/2272 4053/2604/2271 -f 4031/2563/2236 4045/2616/2281 4044/2591/2259 -f 4031/2563/2236 4046/2593/2261 4045/2616/2281 -f 4019/2592/2260 4047/2595/2263 4046/2593/2261 -f 4032/2594/2262 4048/2597/2265 4047/2595/2263 -f 4033/2596/2264 4049/2599/2267 4048/2597/2265 -f 4034/2598/2266 4050/7881/2268 4049/2599/2267 -f 4035/2575/2247 4036/2574/2246 4050/2600/2268 -f 4037/2577/2248 4051/7882/2288 4036/7878/2246 -f 4053/2604/2271 4052/2602/2269 4025/2569/2242 -f 4038/2579/2250 4055/2607/2274 4054/2605/2272 -f 4039/2581/2251 4056/2610/2276 4055/2609/2274 -f 4040/2582/2252 4057/7883/2278 4056/2610/2276 -f 4041/7884/2254 4058/7885/6011 4057/2613/2278 -f 4042/2586/2256 4059/7886/6012 4058/7887/6011 -f 4061/2614/2279 4047/2595/2263 4062/2617/2282 -f 4043/7877/2258 4060/2561/2234 4059/7888/6012 -f 4046/2593/2261 4061/2614/2279 4045/2616/2281 -f 4062/2617/2282 4048/2597/2265 4063/2619/2284 -f 4063/2619/2284 4049/2599/2267 4064/7889/2286 -f 4064/7890/2286 4050/2600/2268 4065/7891/2290 -f 4065/7891/2290 4036/2574/2246 4051/7892/2288 -f 4051/7882/2288 4066/2629/2292 4078/2628/2289 -f 4057/2613/2278 4071/2637/2298 4070/2611/2277 -f 4052/2602/2269 4066/2629/2292 4037/2577/2248 -f 4068/2606/2273 4067/2603/2270 4054/2605/2272 -f 4069/2608/2275 4068/7893/2273 4055/2609/2274 -f 4070/7894/2277 4069/2608/2275 4056/2610/2276 -f 4058/7885/6011 4072/2639/2300 4071/2637/2298 -f 4072/2642/2300 4060/2561/2234 4073/2640/2301 -f 4073/2640/2301 4044/2591/2259 4086/2644/2303 -f 4044/2591/2259 4074/2615/2280 4086/2644/2303 -f 4077/2622/2287 4065/2626/2290 4078/2625/2289 -f 4061/2614/2279 4075/2618/2283 4074/2615/2280 -f 4062/2617/2282 4076/2620/2285 4075/2618/2283 -f 4063/2619/2284 4077/7895/2287 4076/2620/2285 -f 4080/2630/2293 4095/7896/2317 4094/7897/6013 -f 4080/2630/2293 4079/2627/2291 4066/2629/2292 -f 4081/2631/2294 4080/2630/2293 4067/2603/2270 -f 4082/7898/2295 4081/2631/2294 4068/2606/2273 -f 4083/2635/2296 4082/2632/2295 4069/2634/2275 -f 4084/2636/2297 4083/2635/2296 4070/2611/2277 -f 4085/2638/2299 4084/2636/2297 4071/2637/2298 -f 4073/2640/2301 4086/2644/2303 4085/2641/2299 -f 4074/2615/2280 4088/2654/2311 4087/2643/2302 -f 4074/2615/2280 4089/2645/2304 4088/2654/2311 -f 4075/2648/2283 4090/2647/2305 4089/2656/2304 -f 4076/2646/2285 4091/2649/2306 4090/2647/2305 -f 4077/2622/2287 4092/2650/2307 4091/2649/2306 -f 4078/2625/2289 4079/2659/2291 4092/2650/2307 -f 4079/2659/2291 4093/7899/2308 4105/2660/2315 -f 4094/7897/6013 4093/2651/2308 4080/2630/2293 -f 4081/7900/2294 4096/2663/2318 4095/2662/2317 -f 4082/2632/2295 4097/2666/2320 4096/2665/2318 -f 4083/2635/2296 4098/2668/2322 4097/2666/2320 -f 4084/2636/2297 4099/2670/2324 4098/2668/2322 -f 4085/2641/2299 4100/2652/2309 4099/7901/2324 -f 4132/7902/6014 4093/2651/2308 4106/7903/6015 -f 4088/2654/2311 4101/2653/2310 4087/2643/2302 -f 4089/2656/2304 4102/2655/2312 4101/7904/2310 -f 4090/2647/2305 4103/2657/2313 4102/2655/2312 -f 4091/2649/2306 4104/2658/2314 4103/2657/2313 -f 4092/2650/2307 4105/2660/2315 4104/2658/2314 -f 4108/7905/6016 4094/7897/6013 4109/7906/6017 -f 4093/2651/2308 4107/7907/6018 4106/7903/6015 -f 4093/2651/2308 4108/7905/6016 4107/7907/6018 -f 4094/7897/6013 4112/7908/6019 4111/7909/6020 -f 4110/7910/6021 4109/7906/6017 4094/7897/6013 -f 4111/7909/6020 4110/7910/6021 4094/7897/6013 -f 4099/7901/2324 4121/7911/6022 4120/7912/6023 -f 4113/7913/6024 4112/7908/6019 4095/7896/2317 -f 4114/2661/2316 4113/7914/6024 4095/2662/2317 -f 4115/7915/2319 4114/2661/2316 4096/2663/2318 -f 4116/2667/2321 4115/2664/2319 4097/2666/2320 -f 4117/7916/6025 4116/2667/2321 4098/2668/2322 -f 4118/2669/2323 4117/7916/6025 4098/2668/2322 -f 4119/7917/6026 4118/2669/2323 4099/2670/2324 -f 4120/7912/6023 4119/7918/6026 4099/7901/2324 -f 4100/2652/2309 4123/7919/6027 4122/7920/6028 -f 4122/7920/6028 4121/7911/6022 4100/2652/2309 -f 4124/7921/6029 4101/2653/2310 4125/7922/6030 -f 4087/2643/2302 4124/7921/6029 4123/7919/6027 -f 4127/7923/6031 4102/2655/2312 4128/7924/6032 -f 4101/2653/2310 4126/7925/6033 4125/7922/6030 -f 4101/2653/2310 4127/7926/6031 4126/7925/6033 -f 4131/7927/6034 4105/2660/2315 4132/7928/6014 -f 4102/2655/2312 4129/2671/2325 4128/7924/6032 -f 4103/2657/2313 4130/2672/2326 4129/2671/2325 -f 4104/2658/2314 4131/7927/6034 4130/2672/2326 -f 4133/2675/2329 4170/2703/2355 4157/2707/2358 -f 4161/2714/2337 4140/7929/6035 4162/2712/2363 -f 4135/2676/2330 4159/2678/2332 4158/2674/2328 -f 4137/2679/2333 4160/2683/2335 4159/2680/2332 -f 4138/2682/2334 4161/2685/2337 4160/2683/2335 -f 4162/2712/2363 4141/7930/6036 4163/7931/6037 -f 4141/7932/6036 4164/2687/2339 4163/7933/6037 -f 4164/2687/2339 4145/2691/2343 4165/2690/2342 -f 4147/2693/2345 4166/2717/2346 4165/2690/2342 -f 4149/2696/2348 4167/2719/2368 4166/2694/2346 -f 4150/7934/6038 4168/2721/2370 4167/2719/2368 -f 4187/2731/2377 4163/7931/6037 4188/2745/2388 -f 4169/2697/2349 4168/2721/2370 4151/2698/2350 -f 4181/2701/2353 4169/2697/2349 4153/2700/2352 -f 4170/2724/2355 4181/2701/2353 4154/2702/2354 -f 4158/2674/2328 4172/2709/2360 4171/2708/2359 -f 4159/2680/2332 4173/2710/2361 4172/2727/2360 -f 4160/2683/2335 4174/2711/2362 4173/2710/2361 -f 4161/2685/2337 4175/2729/2364 4174/2711/2362 -f 4164/2687/2339 4176/2715/2365 4163/7935/6037 -f 4177/2716/2366 4176/2715/2365 4165/2690/2342 -f 4178/2718/2367 4177/2734/2366 4166/2694/2346 -f 4179/2720/2369 4178/2718/2367 4167/2719/2368 -f 4180/2722/2371 4179/2720/2369 4168/2721/2370 -f 4181/2701/2353 4180/2722/2371 4169/2697/2349 -f 4171/2708/2359 4182/7936/2372 4170/2703/2355 -f 4172/2709/2360 4184/2739/2374 4183/2725/2373 -f 4173/2710/2361 4185/2728/2375 4184/2726/2374 -f 4174/2711/2362 4186/2730/2376 4185/2728/2375 -f 4175/2729/2364 4187/2743/2377 4186/2730/2376 -f 4188/2748/2388 4176/2715/2365 4189/2732/2378 -f 4182/7936/2372 4183/2725/2373 4194/2756/2383 -f 4190/2750/2379 4189/2732/2378 4177/2716/2366 -f 4191/2735/2380 4190/2733/2379 4178/2718/2367 -f 4192/2736/2381 4191/2735/2380 4179/2720/2369 -f 4193/2737/2382 4192/2736/2381 4180/2722/2371 -f 4194/2738/2383 4193/2737/2382 4181/2701/2353 -f 4209/2755/2396 4211/2761/2400 4210/2760/2399 -f 4195/2740/2384 4209/2755/2396 4183/2725/2373 -f 4184/2726/2374 4196/7937/6039 4195/7938/2384 -f 4184/2726/2374 4197/2741/2385 4196/7937/6039 -f 4185/2728/2375 4198/2742/2386 4197/2741/2385 -f 4186/2730/2376 4199/2744/2387 4198/2742/2386 -f 4187/2731/2377 4200/2746/2389 4199/7939/2387 -f 4188/2748/2388 4201/2747/2390 4200/2758/2389 -f 4189/2732/2378 4202/7940/6040 4201/2747/2390 -f 4189/2732/2378 4203/2749/2391 4202/7940/6040 -f 4204/7941/6041 4203/7942/2391 4190/2733/2379 -f 4205/2751/2392 4204/7941/6041 4190/2733/2379 -f 4206/2752/2393 4205/2751/2392 4191/2735/2380 -f 4207/2753/2394 4206/2752/2393 4192/2736/2381 -f 4208/2754/2395 4207/2753/2394 4193/2737/2382 -f 4209/2755/2396 4208/7943/2395 4194/2756/2383 -f 4210/2760/2399 4224/7944/6042 4209/2755/2396 -f 4211/7945/2400 4196/7937/6039 4212/7946/6043 -f 4212/7946/6043 4197/2741/2385 4213/7947/6044 -f 4213/7947/6044 4198/2742/2386 4214/7948/6045 -f 4214/7948/6045 4199/2744/2387 4215/7949/6046 -f 4215/7950/6046 4200/2746/2389 4216/7951/2397 -f 4202/7940/6040 4218/7952/6047 4217/2762/2401 -f 4202/7940/6040 4217/2762/2401 4201/2747/2390 -f 4203/7942/2391 4219/7953/6048 4218/7954/6047 -f 4204/7941/6041 4220/7955/6049 4219/7953/6048 -f 4205/2751/2392 4221/7956/6050 4220/7955/6049 -f 4206/2752/2393 4222/7957/6051 4221/7956/6050 -f 4207/2753/2394 4223/7958/6052 4222/7957/6051 -f 4208/7943/2395 4224/7944/6042 4223/7959/6052 -f 4211/7960/2400 4226/7961/6053 4225/7962/6054 -f 4225/7962/6054 4239/7963/2398 4211/7960/2400 -f 4226/7964/6053 4213/7947/6044 4227/7965/2405 -f 4227/7965/2405 4214/7948/6045 4228/7966/2403 -f 4228/7966/2403 4215/7949/6046 4229/7967/2406 -f 4229/2771/2406 4216/2757/2397 4230/2769/2408 -f 4230/2769/2408 4201/2747/2390 4231/2763/2402 -f 4218/7968/6047 4233/2777/2414 4232/2776/2411 -f 4218/7952/6047 4232/2773/2411 4217/2762/2401 -f 4219/7953/6048 4234/7969/2416 4233/7970/2414 -f 4220/7955/6049 4235/7971/2417 4234/7969/2416 -f 4221/7956/6050 4236/7972/2419 4235/7971/2417 -f 4222/7973/6051 4237/2784/2421 4236/2782/2419 -f 4223/7959/6052 4238/2786/2423 4237/2784/2421 -f 4240/7974/6055 4227/7965/2405 4241/7975/6056 -f 4210/2760/2399 4238/2786/2423 4224/7944/6042 -f 4226/7961/6053 4240/7976/6055 4225/7962/6054 -f 4245/2772/2410 4217/2762/2401 4246/2774/2412 -f 4227/7965/2405 4242/7977/2404 4241/7975/6056 -f 4228/2764/2403 4243/2768/2407 4242/2765/2404 -f 4229/2771/2406 4244/2770/2409 4243/7978/2407 -f 4230/2769/2408 4245/2772/2410 4244/2770/2409 -f 4233/7970/2414 4248/7979/6057 4247/7980/2413 -f 4238/2786/2423 4239/2759/2398 4252/2785/2422 -f 4249/7981/2415 4248/7979/6057 4234/7969/2416 -f 4250/2781/2418 4249/2778/2415 4235/2780/2417 -f 4251/2783/2420 4250/2781/2418 4236/2782/2419 -f 4252/2785/2422 4251/2783/2420 4237/2784/2421 -f 4239/7963/2398 4254/2789/2426 4253/2798/2433 -f 4253/2796/2433 4269/2795/2432 4239/2759/2398 -f 4254/2789/2426 4240/7976/6055 4255/2790/2427 -f 4255/2790/2427 4241/7982/6056 4256/7983/6058 -f 4256/7984/6058 4242/2765/2404 4257/7985/6059 -f 4257/7985/6059 4243/2768/2407 4258/7986/6060 -f 4258/7987/6060 4244/2770/2409 4259/7988/6061 -f 4259/7988/6061 4245/2772/2410 4260/2793/2430 -f 4260/2793/2430 4246/2774/2412 4261/2791/2428 -f 4246/2774/2412 4262/7989/2424 4261/2791/2428 -f 4247/2775/2413 4264/7990/6062 4263/7991/6063 -f 4263/7991/6063 4262/2787/2424 4247/2775/2413 -f 4248/7992/6057 4265/7993/6064 4264/7994/6062 -f 4249/2778/2415 4266/7995/6065 4265/7993/6064 -f 4250/2781/2418 4267/7996/6066 4266/7995/6065 -f 4251/2783/2420 4268/7997/6067 4267/7996/6066 -f 4252/2785/2422 4269/2795/2432 4268/7997/6067 -f 4270/2788/2425 4256/7983/6058 4271/7998/6068 -f 4271/7999/6068 4257/7985/6059 4272/8000/6069 -f 4272/8000/6069 4258/7986/6060 4273/8001/6070 -f 4273/8002/6070 4259/7988/6061 4274/8003/6071 -f 4274/8003/6071 4260/2793/2430 4275/2792/2429 -f 4263/7991/6063 4277/8004/6072 4276/8005/6073 -f 4263/7991/6063 4276/8005/6073 4262/2787/2424 -f 4264/7994/6062 4278/8006/6074 4277/8007/6072 -f 4265/7993/6064 4279/8008/6075 4278/8006/6074 -f 4266/7995/6065 4280/8009/6076 4279/8008/6075 -f 4267/7996/6066 4281/8010/6077 4280/8009/6076 -f 4270/2788/2425 4283/2797/2434 4254/2789/2426 -f 4269/2795/2432 4281/8010/6077 4268/7997/6067 -f 4283/8011/2434 4282/2794/2431 4253/2796/2433 -f 4284/2800/2436 4271/7998/6068 4285/2801/2437 -f 4271/7999/6068 4286/8012/6078 4285/8013/2437 -f 4286/8012/6078 4273/8001/6070 4287/8014/6079 -f 4287/8015/6079 4274/8003/6071 4288/2804/2440 -f 4288/2804/2440 4275/2792/2429 4289/2802/2438 -f 4290/8016/6080 4261/2791/2428 4291/2813/2449 -f 4275/2792/2429 4290/8016/6080 4289/2802/2438 -f 4291/2813/2449 4262/7989/2424 4292/2811/2447 -f 4292/2839/2447 4276/8005/6073 4293/8017/6081 -f 4276/8005/6073 4294/8018/2442 4293/8017/6081 -f 4277/8007/6072 4295/2807/2443 4294/2806/2442 -f 4278/8006/6074 4296/8019/6082 4295/2807/2443 -f 4279/8008/6075 4297/8020/6083 4296/8019/6082 -f 4280/8009/6076 4298/8021/6084 4297/8020/6083 -f 4281/8010/6077 4282/2794/2431 4299/8022/6085 -f 4299/8022/6085 4298/8021/6084 4281/8010/6077 -f 4301/8023/6086 4287/8014/6079 4302/8024/2445 -f 4286/8012/6078 4301/8023/6086 4285/8013/2437 -f 4302/2809/2445 4288/2804/2440 4303/2803/2439 -f 4293/8017/6081 4305/8025/2441 4304/8026/6087 -f 4293/8017/6081 4304/8026/6087 4292/2839/2447 -f 4295/2807/2443 4307/2816/2452 4306/2815/2451 -f 4306/2815/2451 4305/2805/2441 4295/2807/2443 -f 4296/8019/6082 4308/8027/6088 4307/2816/2452 -f 4283/8011/2434 4309/8028/6089 4282/2794/2431 -f 4298/8021/6084 4308/8027/6088 4297/8020/6083 -f 4300/2799/2435 4311/2823/2459 4283/2797/2434 -f 4311/2823/2459 4310/2822/2458 4283/2797/2434 -f 4300/2799/2435 4313/2826/2462 4312/2825/2461 -f 4313/2828/2462 4301/8023/6086 4314/2829/2464 -f 4315/2830/2465 4302/8024/2445 4316/2832/2467 -f 4301/8023/6086 4315/2830/2465 4314/2829/2464 -f 4318/2810/2446 4290/8016/6080 4319/2835/2469 -f 4302/8024/2445 4317/2833/2444 4316/2832/2467 -f 4303/2803/2439 4318/2810/2446 4317/2808/2444 -f 4319/2835/2469 4291/2813/2449 4320/2812/2448 -f 4321/2837/2471 4304/8026/6087 4322/8029/6090 -f 4304/8026/6087 4323/8030/6091 4322/8029/6090 -f 4307/2816/2452 4326/2820/2456 4325/2814/2450 -f 4324/2818/2454 4323/8031/6091 4305/2805/2441 -f 4306/2815/2451 4324/2818/2454 4305/2805/2441 -f 4308/8027/6088 4330/2842/2475 4326/2820/2456 -f 4298/8021/6084 4327/8032/6092 4330/2842/2475 -f 4299/8022/6085 4309/8028/6089 4327/8032/6092 -f 4311/2823/2459 4333/8033/6093 4332/2821/2457 -f 4329/2819/2455 4328/2817/2453 4325/2814/2450 -f 4330/2842/2475 4329/2819/2455 4326/2820/2456 -f 4310/2855/2458 4331/2857/2487 4309/8028/6089 -f 4320/2812/2448 4340/8034/2472 4353/8035/6094 -f 4334/2824/2460 4333/8033/6093 4312/2825/2461 -f 4335/2827/2463 4334/2845/2460 4313/2828/2462 -f 4336/2831/2466 4335/2827/2463 4315/2830/2465 -f 4317/2833/2444 4337/2848/2468 4336/2831/2466 -f 4318/2810/2446 4338/2850/2481 4337/2834/2468 -f 4318/2810/2446 4339/2836/2470 4338/2850/2481 -f 4320/2812/2448 4353/8035/6094 4339/2836/2470 -f 4321/2837/2471 4341/2852/2483 4340/2838/2472 -f 4341/2852/2483 4323/8030/6091 4342/8036/6095 -f 4342/8037/6095 4324/2818/2454 4343/2840/2473 -f 4330/2842/2475 4347/2858/2488 4346/2853/2484 -f 4328/2817/2453 4344/2841/2474 4343/2840/2473 -f 4329/2819/2455 4345/2843/2476 4344/2841/2474 -f 4346/2853/2484 4345/2843/2476 4330/2842/2475 -f 4327/8032/6092 4331/2857/2487 4347/2858/2488 -f 4354/2851/2482 4342/8036/6095 4355/8038/6096 -f 4348/8039/2477 4333/8033/6093 4334/2824/2460 -f 4349/2846/2478 4348/2844/2477 4335/2827/2463 -f 4350/2847/2479 4349/2846/2478 4336/2831/2466 -f 4351/2849/2480 4350/8040/2479 4337/2834/2468 -f 4352/8041/6097 4338/2850/2481 4339/2836/2470 -f 4353/8035/6094 4352/8041/6097 4339/2836/2470 -f 4355/8042/6096 4343/2840/2473 4356/8043/6098 -f 4356/8043/6098 4344/2841/2474 4357/8044/6099 -f 4357/8044/6099 4345/2843/2476 4358/2854/2485 -f 4359/2867/2486 4332/2821/2457 4360/2865/2495 -f 4331/2857/2487 4359/2856/2486 4374/2863/2493 -f 4332/2821/2457 4361/8045/6100 4360/2865/2495 -f 4348/8039/2477 4361/8045/6100 4333/8033/6093 -f 4348/2844/2477 4363/8046/6101 4362/8047/6102 -f 4349/2846/2478 4364/8048/6103 4363/8046/6101 -f 4350/8040/2479 4365/8049/6104 4364/8050/6103 -f 4338/2850/2481 4365/8049/6104 4351/2849/2480 -f 4340/2838/2472 4368/2862/2492 4367/2861/2491 -f 4352/8041/6097 4366/8051/6105 4338/2850/2481 -f 4368/2862/2492 4355/8038/6096 4369/8052/6106 -f 4369/8053/6106 4356/8043/6098 4370/8054/6107 -f 4370/8054/6107 4357/8044/6099 4371/8055/6108 -f 4371/8055/6108 4358/2854/2485 4372/8056/6109 -f 4372/8056/6109 4346/2853/2484 4373/2859/2489 -f 4373/2859/2489 4331/2857/2487 4374/2863/2493 -f 4361/8045/6100 4376/8057/6110 4375/8058/6111 -f 4361/8045/6100 4375/8058/6111 4360/2865/2495 -f 4362/8047/6102 4377/8059/2506 4376/8060/6110 -f 4364/8048/6103 4378/8061/2498 4363/8046/6101 -f 4378/8061/2498 4377/8059/2506 4363/8046/6101 -f 4364/8050/6103 4380/2873/2501 4379/2872/2499 -f 4352/8041/6097 4381/2882/2509 4366/8051/6105 -f 4366/8051/6105 4380/2873/2501 4365/8049/6104 -f 4353/8035/6094 4382/8062/6112 4381/2882/2509 -f 4383/8063/2490 4369/8064/6106 4384/8065/6113 -f 4367/2861/2491 4382/8066/6112 4340/2838/2472 -f 4384/8067/6113 4370/8054/6107 4385/8068/6114 -f 4385/8068/6114 4371/8055/6108 4386/8069/6115 -f 4386/8069/6115 4372/8056/6109 4387/8070/6116 -f 4387/8070/6116 4373/2859/2489 4388/2864/2494 -f 4375/8058/6111 4389/2866/2496 4360/2865/2495 -f 4359/2856/2486 4389/2875/2496 4374/2863/2493 -f 4375/8071/6111 4391/2878/2505 4390/8072/6117 -f 4380/2873/2501 4395/8073/2522 4394/8074/2521 -f 4377/2879/2506 4391/2878/2505 4376/8075/6110 -f 4393/8076/2500 4392/8077/2497 4379/8078/2499 -f 4394/8074/2521 4393/2871/2500 4380/2873/2501 -f 4382/8066/6112 4383/2860/2490 4397/8079/6118 -f 4396/2881/2508 4395/8073/2522 4366/8051/6105 -f 4381/2882/2509 4396/2881/2508 4366/8051/6105 -f 4397/8080/6118 4384/8065/6113 4398/2885/2512 -f 4398/2885/2512 4385/8081/6114 4399/2883/2510 -f 4399/2883/2510 4386/8082/6115 4400/2886/2513 -f 4400/8083/2513 4387/8070/6116 4401/8084/2515 -f 4401/8084/2515 4388/2864/2494 4402/2874/2502 -f 4377/2879/2506 4405/2893/2519 4404/2877/2504 -f 4407/8085/6119 4398/2885/2512 4408/2884/2511 -f 4392/2868/2497 4405/2893/2519 4378/2869/2498 -f 4382/8062/6112 4406/2880/2507 4381/2882/2509 -f 4397/8079/6118 4407/8086/6119 4382/8066/6112 -f 4412/8087/2523 4374/2863/2493 4403/2876/2503 -f 4399/2883/2510 4409/2887/2514 4408/2884/2511 -f 4400/2886/2513 4410/2889/2516 4409/2887/2514 -f 4401/2888/2515 4411/2891/2517 4410/2889/2516 -f 4402/2874/2502 4412/8087/2523 4411/8088/2517 -f 4389/2866/2496 4414/8089/2532 4413/8090/2525 -f 4389/2875/2496 4413/2899/2525 4403/2876/2503 -f 4390/8072/6117 4415/2902/2528 4414/2908/2532 -f 4392/2868/2497 4417/2910/2535 4416/2892/2518 -f 4404/2877/2504 4415/2902/2528 4391/2878/2505 -f 4393/8091/2500 4418/2894/2520 4417/2912/2535 -f 4395/2896/2522 4419/2914/2530 4418/2894/2520 -f 4420/8092/2540 4407/8086/6119 4421/8093/2538 -f 4406/2880/2507 4419/2904/2530 4396/2881/2508 -f 4421/2920/2538 4408/2884/2511 4422/8094/6120 -f 4422/8094/6120 4409/2887/2514 4423/8095/6121 -f 4423/8095/6121 4410/2889/2516 4424/8096/6122 -f 4424/8096/6122 4411/2891/2517 4425/2898/2524 -f 4404/2877/2504 4428/2933/2554 4427/2901/2527 -f 4406/2880/2507 4430/2962/2575 4429/2903/2529 -f 4416/2892/2518 4428/2933/2554 4405/2893/2519 -f 4431/2918/2541 4423/8095/6121 4432/2921/2543 -f 4420/2917/2540 4430/2962/2575 4382/8062/6112 -f 4422/8094/6120 4431/2918/2541 4421/2920/2538 -f 4432/2921/2543 4424/8096/6122 4433/2923/2545 -f 4433/2923/2545 4425/2898/2524 4434/2925/2547 -f 4434/2925/2547 4412/2897/2523 4435/2927/2549 -f 4435/8097/2549 4403/2876/2503 4426/2900/2526 -f 4437/2907/2533 4451/2956/2570 4450/8098/6123 -f 4437/2907/2533 4436/2931/2531 4414/2908/2532 -f 4438/2909/2534 4428/2933/2554 4416/2892/2518 -f 4439/2911/2536 4438/2942/2534 4417/2912/2535 -f 4440/2913/2537 4439/2911/2536 4418/2894/2520 -f 4429/2935/2529 4440/2913/2537 4419/2914/2530 -f 4420/2917/2540 4441/2916/2539 4430/2962/2575 -f 4421/2920/2538 4442/2919/2542 4441/8099/2539 -f 4431/2918/2541 4443/2922/2544 4442/2919/2542 -f 4432/2921/2543 4444/2924/2546 4443/2922/2544 -f 4433/2923/2545 4445/2926/2548 4444/2924/2546 -f 4434/2925/2547 4446/2936/2556 4445/2926/2548 -f 4434/2925/2547 4447/2928/2550 4446/2936/2556 -f 4426/2900/2526 4448/2929/2551 4457/8100/2558 -f 4450/8098/6123 4449/2930/2552 4437/2907/2533 -f 4427/2901/2527 4452/2932/2553 4451/2956/2570 -f 4454/2945/2562 4444/2924/2546 4455/2943/2560 -f 4430/2962/2575 4453/2961/2555 4429/2903/2529 -f 4443/2922/2544 4454/2945/2562 4442/2919/2542 -f 4455/2943/2560 4445/2926/2548 4456/2937/2557 -f 4436/2905/2531 4448/2929/2551 4413/2899/2525 -f 4435/2927/2549 4457/2939/2558 4447/2928/2550 -f 4439/2911/2536 4461/2959/2573 4460/8101/6124 -f 4449/2930/2552 4458/8102/2568 4436/2931/2531 -f 4460/8101/6124 4459/2941/2559 4439/2911/2536 -f 4462/8103/2576 4442/2919/2542 4463/2968/2581 -f 4453/2934/2555 4461/2959/2573 4440/2913/2537 -f 4441/2916/2539 4462/2963/2576 4430/2962/2575 -f 4463/2968/2581 4454/2945/2562 4464/8104/6125 -f 4467/2947/2564 4447/2928/2550 4468/2948/2565 -f 4454/2945/2562 4465/2944/2561 4464/8104/6125 -f 4455/2943/2560 4466/2946/2563 4465/2944/2561 -f 4456/2937/2557 4467/2947/2564 4466/2946/2563 -f 4449/2930/2552 4470/2974/2584 4469/8105/2567 -f 4450/8098/6123 4471/2955/2569 4470/2974/2584 -f 4459/2941/2559 4474/2979/2589 4473/2978/2588 -f 4472/2957/2571 4471/2955/2569 4452/2932/2553 -f 4473/2978/2588 4472/2977/2571 4459/2941/2559 -f 4476/2966/2579 4465/2944/2561 4477/8106/6126 -f 4461/2959/2573 4474/2979/2589 4460/8101/6124 -f 4475/3004/2574 4482/8107/2572 4453/8108/2555 -f 4464/8104/6125 4476/2966/2579 4463/2968/2581 -f 4477/8106/6126 4466/2946/2563 4478/8109/6127 -f 4478/8109/6127 4467/2947/2564 4479/2964/2577 -f 4483/2995/2603 4463/8110/2581 4484/2993/2580 -f 4457/2939/2558 4480/2969/2566 4468/2948/2565 -f 4448/2949/2551 4501/3012/2600 4480/2950/2566 -f 4469/2952/2567 4481/2965/2578 4458/2953/2568 -f 4482/2958/2572 4474/2979/2589 4461/2959/2573 -f 4462/2963/2576 4483/2995/2603 4475/2960/2574 -f 4485/2981/2591 4478/8109/6127 4486/2983/2593 -f 4477/8106/6126 4485/2981/2591 4476/2966/2579 -f 4486/2983/2593 4479/2964/2577 4487/2985/2595 -f 4487/2985/2595 4468/2948/2565 4488/2970/2582 -f 4481/2965/2578 4523/2989/2599 4448/2929/2551 -f 4489/2971/2583 4481/2965/2578 4469/2952/2567 -f 4490/2973/2585 4489/8111/2583 4470/2974/2584 -f 4491/2975/2586 4490/2973/2585 4471/2955/2569 -f 4492/2976/2587 4491/8112/2586 4472/2977/2571 -f 4493/8113/6128 4492/2976/2587 4474/2979/2589 -f 4494/2980/2590 4493/8113/6128 4474/2979/2589 -f 4495/8114/2609 4494/2980/2590 4482/2958/2572 -f 4475/3004/2574 4495/3003/2609 4482/8107/2572 -f 4476/8115/2579 4496/2996/2592 4484/2993/2580 -f 4485/2981/2591 4497/2984/2594 4496/2982/2592 -f 4486/2983/2593 4498/2986/2596 4497/2984/2594 -f 4487/2985/2595 4499/2987/2597 4498/2986/2596 -f 4488/2970/2582 4500/2988/2598 4499/2987/2597 -f 4480/2969/2566 4501/8116/2600 4500/2988/2598 -f 4475/2960/2574 4506/2994/2602 4505/3005/2610 -f 4503/3015/2617 4502/3014/2616 4481/2965/2578 -f 4489/2971/2583 4503/3015/2617 4481/2965/2578 -f 4491/2975/2586 4504/8117/2601 4490/2973/2585 -f 4509/2998/2605 4498/2986/2596 4510/3009/2613 -f 4484/2993/2580 4507/2997/2604 4506/2994/2602 -f 4496/2996/2592 4508/8118/6129 4507/2997/2604 -f 4496/2996/2592 4509/8119/2605 4508/8118/6129 -f 4510/3009/2613 4499/2987/2597 4511/3007/2611 -f 4491/8112/2586 4514/8120/6130 4513/8121/2607 -f 4499/2987/2597 4512/2999/2606 4511/3007/2611 -f 4492/2976/2587 4516/8122/6131 4515/8123/6132 -f 4515/8123/6132 4514/8120/6130 4492/2976/2587 -f 4493/8113/6128 4517/8124/6133 4516/8122/6131 -f 4494/2980/2590 4518/8125/2608 4517/8124/6133 -f 4519/8126/6134 4510/8127/2613 4520/8128/2612 -f 4509/8119/2605 4519/8126/6134 4508/8118/6129 -f 4521/3017/2619 4501/8116/2600 4522/8129/2614 -f 4500/2988/2598 4521/3017/2619 4512/2999/2606 -f 4503/3015/2617 4525/8130/6135 4524/3013/2615 -f 4489/2971/2583 4526/3019/2621 4525/8130/6135 -f 4527/8131/6136 4507/2997/2604 4528/3026/2628 -f 4513/3000/2607 4526/3019/2621 4504/2991/2601 -f 4506/2994/2602 4527/8131/6136 4505/3005/2610 -f 4528/3026/2628 4508/8118/6129 4529/3024/2626 -f 4529/3024/2626 4519/8126/6134 4530/8132/6137 -f 4530/8132/6137 4520/8128/2612 4531/8133/6138 -f 4531/8133/6138 4511/8134/2611 4532/8135/2618 -f 4513/8136/2607 4534/3035/2636 4533/3034/2620 -f 4523/3010/2599 4544/3022/2624 4522/3011/2614 -f 4514/8137/6130 4535/8138/6139 4534/3035/2636 -f 4515/8139/6132 4536/8140/6140 4535/8138/6139 -f 4516/8122/6131 4537/8141/6141 4536/8142/6140 -f 4517/8143/6133 4538/3038/2638 4537/8144/6141 -f 4540/8145/6142 4531/8133/6138 4541/8146/6143 -f 4518/3002/2608 4539/3036/2622 4538/3038/2638 -f 4530/8132/6137 4540/8145/6142 4529/3024/2626 -f 4541/8146/6143 4532/8135/2618 4542/3029/2631 -f 4542/8147/2631 4521/3017/2619 4543/8148/2629 -f 4543/8149/2629 4522/3011/2614 4544/3022/2624 -f 4524/3013/2615 4546/3023/2625 4502/3014/2616 -f 4546/3023/2625 4545/3045/2623 4523/2989/2599 -f 4524/3013/2615 4548/3031/2633 4547/3047/2646 -f 4525/8130/6135 4549/3032/2634 4548/3031/2633 -f 4550/3054/2652 4527/8131/6136 4551/3052/2650 -f 4533/3018/2620 4549/3032/2634 4526/3019/2621 -f 4505/3005/2610 4550/3054/2652 4539/3020/2622 -f 4551/3052/2650 4528/3026/2628 4552/3025/2627 -f 4552/3025/2627 4540/8145/6142 4553/3039/2639 -f 4553/3039/2639 4541/8146/6143 4554/3041/2641 -f 4554/3041/2641 4542/3029/2631 4555/3028/2630 -f 4549/3032/2634 4557/3050/2649 4556/3030/2632 -f 4534/3035/2636 4559/3067/2664 4558/3033/2635 -f 4558/3051/2635 4557/3050/2649 4533/3018/2620 -f 4535/8138/6139 4560/3069/2666 4559/3067/2664 -f 4536/8140/6140 4561/3071/2668 4560/3069/2666 -f 4537/8144/6141 4562/3037/2637 4561/8150/2668 -f 4567/3055/2653 4543/3027/2629 4568/8151/2675 -f 4552/3025/2627 4563/3060/2658 4551/3052/2650 -f 4552/3025/2627 4564/3040/2640 4563/3060/2658 -f 4553/3039/2639 4565/3042/2642 4564/3040/2640 -f 4554/3041/2641 4566/3043/2643 4565/3042/2642 -f 4555/3028/2630 4567/3055/2653 4566/3043/2643 -f 4543/8149/2629 4545/3021/2623 4568/3078/2675 -f 4574/3074/2671 4566/3043/2643 4575/3056/2654 -f 4569/3046/2645 4576/3044/2644 4546/3023/2625 -f 4556/3030/2632 4570/3048/2647 4548/3031/2633 -f 4559/3067/2664 4571/3066/2648 4558/3033/2635 -f 4550/3054/2652 4581/8152/2669 4539/3020/2622 -f 4550/3054/2652 4572/3053/2651 4581/8152/2669 -f 4565/3042/2642 4573/3062/2660 4564/3040/2640 -f 4565/3042/2642 4574/3074/2671 4573/3062/2660 -f 4563/3060/2658 4583/3087/2684 4582/3061/2659 -f 4576/8153/2644 4585/3076/2673 4545/3021/2623 -f 4570/3048/2647 4577/3057/2655 4547/3047/2646 -f 4579/3059/2657 4578/3058/2656 4556/3030/2632 -f 4562/3037/2637 4580/3073/2670 4561/8150/2668 -f 4539/3036/2622 4581/3072/2669 4562/3037/2637 -f 4551/3052/2650 4582/3061/2659 4572/3053/2651 -f 4576/3044/2644 4587/8154/6144 4586/8155/6145 -f 4564/3040/2640 4584/3063/2661 4583/3087/2684 -f 4567/3055/2653 4595/3101/2696 4575/3056/2654 -f 4568/8151/2675 4596/8156/6146 4567/3055/2653 -f 4545/3021/2623 4585/3076/2673 4568/3078/2675 -f 4600/3085/2682 4572/3053/2651 4591/3080/2677 -f 4577/3057/2655 4587/8154/6144 4569/3046/2645 -f 4578/3058/2656 4577/3057/2655 4570/3048/2647 -f 4588/3065/2663 4599/3092/2662 4571/3066/2648 -f 4589/3068/2665 4588/3065/2663 4559/3067/2664 -f 4590/3070/2667 4589/3068/2665 4560/3069/2666 -f 4580/3073/2670 4590/8157/2667 4561/8150/2668 -f 4607/3097/2692 4583/3087/2684 4601/3086/2683 -f 4584/3063/2661 4592/3081/2678 4583/3087/2684 -f 4573/3062/2660 4593/3075/2672 4584/3063/2661 -f 4575/3056/2654 4594/3082/2679 4574/3074/2671 -f 4575/3056/2654 4595/3101/2696 4594/3082/2679 -f 4567/3055/2653 4596/8156/6146 4595/3101/2696 -f 4568/3078/2675 4597/3077/2674 4596/8158/6146 -f 4586/8155/6145 4585/8159/2673 4576/3044/2644 -f 4598/3079/2676 4603/3090/2687 4578/3058/2656 -f 4599/3064/2662 4598/3079/2676 4579/3059/2657 -f 4581/3072/2669 4600/8160/2682 4580/3073/2670 -f 4577/3057/2655 4603/3090/2687 4610/3107/2702 -f 4602/3105/2700 4608/3088/2685 4585/3076/2673 -f 4586/8155/6145 4602/8161/2700 4585/8159/2673 -f 4577/3057/2655 4586/8155/6145 4587/8154/6144 -f 4590/8157/2667 4605/3096/2691 4604/3095/2680 -f 4605/3096/2691 4600/8160/2682 4606/8162/2681 -f 4596/8158/6146 4608/3088/2685 4618/3103/2698 -f 4582/3061/2659 4607/3097/2692 4591/3080/2677 -f 4596/8156/6146 4618/3110/2698 4595/3101/2696 -f 4614/3094/2690 4606/8162/2681 4615/8163/6147 -f 4602/3105/2700 4619/3102/2697 4608/3088/2685 -f 4609/3114/2707 4602/8161/2700 4586/8155/6145 -f 4577/3057/2655 4609/3114/2707 4586/8155/6145 -f 4612/8164/2688 4611/3089/2686 4599/3064/2662 -f 4613/3093/2689 4612/3091/2688 4588/3065/2663 -f 4614/8165/2690 4613/3093/2689 4604/3083/2680 -f 4603/3090/2687 4622/8166/6148 4621/3106/2701 -f 4591/3080/2677 4627/3098/2693 4606/3084/2681 -f 4593/3075/2672 4617/3100/2695 4616/3099/2694 -f 4595/3101/2696 4618/3110/2698 4617/3100/2695 -f 4609/3114/2707 4620/3113/2699 4602/8161/2700 -f 4610/3107/2702 4609/3114/2707 4577/3057/2655 -f 4611/3089/2686 4623/8167/6149 4622/8166/6148 -f 4612/3091/2688 4624/8168/6150 4623/8169/6149 -f 4613/8170/2689 4625/3118/2711 4624/8171/6150 -f 4625/8172/2711 4615/8163/6147 4626/8173/2709 -f 4615/8174/6147 4627/3098/2693 4626/8175/2709 -f 4628/3119/2712 4601/3086/2683 4629/3132/2723 -f 4629/3132/2723 4592/3081/2678 4630/3108/2703 -f 4632/3111/2705 4619/8176/2697 4633/8177/6151 -f 4616/3099/2694 4631/3109/2704 4630/3108/2703 -f 4617/3100/2695 4632/3111/2705 4631/3109/2704 -f 4621/3106/2701 4637/3124/2716 4636/3115/2708 -f 4619/8178/2697 4620/8179/2699 4633/8180/6151 -f 4636/3115/2708 4609/3114/2707 4610/3107/2702 -f 4622/8166/6148 4638/3126/2718 4637/3124/2716 -f 4623/8169/6149 4639/8181/2720 4638/8182/2718 -f 4624/8171/6150 4640/3117/2710 4639/3129/2720 -f 4641/3133/2724 4631/3109/2704 4642/3135/2726 -f 4630/3108/2703 4641/3133/2724 4629/3132/2723 -f 4642/3135/2726 4632/3111/2705 4643/8183/2728 -f 4643/3137/2728 4633/8180/6151 4644/3140/2730 -f 4644/3140/2730 4620/8179/2699 4634/3142/2732 -f 4640/3117/2710 4651/3154/2741 4650/3130/2721 -f 4634/3142/2732 4645/3143/2733 4656/3141/2731 -f 4635/3112/2706 4645/3120/2733 4620/3113/2699 -f 4646/3122/2714 4635/3112/2706 4609/3114/2707 -f 4647/3123/2715 4646/3122/2714 4636/3115/2708 -f 4648/3125/2717 4647/3123/2715 4637/3124/2716 -f 4649/3127/2719 4648/3151/2717 4638/3128/2718 -f 4650/3130/2721 4649/3127/2719 4639/3129/2720 -f 4651/8184/2741 4627/3098/2693 4663/3156/2743 -f 4659/8185/2737 4674/3179/2761 4673/3178/2760 -f 4627/3098/2693 4652/3131/2722 4663/3156/2743 -f 4629/3132/2723 4653/3134/2725 4652/3131/2722 -f 4641/3133/2724 4654/3136/2727 4653/3134/2725 -f 4642/3135/2726 4655/8186/2729 4654/3136/2727 -f 4643/3137/2728 4656/3141/2731 4655/3138/2729 -f 4658/3146/2736 4657/3145/2735 4646/3122/2714 -f 4659/8187/2737 4658/3146/2736 4647/3123/2715 -f 4660/3150/2738 4659/8185/2737 4648/3151/2717 -f 4661/3152/2739 4660/3150/2738 4649/3127/2719 -f 4662/3153/2740 4661/3152/2739 4650/3130/2721 -f 4663/8188/2743 4662/3153/2740 4651/3154/2741 -f 4653/3134/2725 4666/8189/2745 4665/3157/2744 -f 4654/3158/2727 4667/3161/2746 4666/3159/2745 -f 4655/3138/2729 4668/3162/2747 4667/3161/2746 -f 4656/3141/2731 4669/3144/2734 4668/3162/2747 -f 4645/3120/2733 4671/3164/2749 4670/3175/2748 -f 4673/8190/2760 4672/3165/2750 4659/3147/2737 -f 4660/3150/2738 4675/3181/2763 4674/3179/2761 -f 4661/3152/2739 4676/3183/2751 4675/3181/2763 -f 4671/3164/2749 4684/3176/2758 4683/3193/2772 -f 4677/3169/2752 4676/3167/2751 4663/3156/2743 -f 4664/3155/2742 4678/3170/2753 4677/3169/2752 -f 4666/3159/2745 4679/3171/2754 4665/3187/2744 -f 4667/3161/2746 4680/3172/2755 4679/3171/2754 -f 4668/3162/2747 4681/3173/2756 4680/3172/2755 -f 4669/3144/2734 4670/3163/2748 4681/3173/2756 -f 4670/3163/2748 4682/8191/2757 4695/3190/2770 -f 4671/3164/2749 4683/3193/2772 4682/3174/2757 -f 4672/8192/2750 4684/3176/2758 4658/3146/2736 -f 4672/3165/2750 4686/8193/2759 4685/3209/2775 -f 4676/3167/2751 4690/3184/2765 4689/3201/2779 -f 4687/3180/2762 4686/3177/2759 4674/3179/2761 -f 4688/3182/2764 4687/3180/2762 4675/3181/2763 -f 4689/8194/2779 4688/3182/2764 4676/3183/2751 -f 4710/8195/6152 4697/3194/2773 4711/8196/6153 -f 4678/3170/2753 4691/3185/2766 4690/3184/2765 -f 4665/3157/2744 4692/3203/2767 4691/3185/2766 -f 4679/3171/2754 4693/3188/2768 4692/3186/2767 -f 4680/3172/2755 4694/3189/2769 4693/3188/2768 -f 4681/3173/2756 4695/3190/2770 4694/3189/2769 -f 4697/3194/2773 4696/3191/2771 4683/3193/2772 -f 4698/3195/2774 4697/3194/2773 4684/3176/2758 -f 4699/3211/2786 4685/3209/2775 4686/8193/2759 -f 4700/3197/2776 4699/8197/2786 4686/3177/2759 -f 4701/3198/2777 4700/3197/2776 4687/3180/2762 -f 4702/8198/2778 4701/3198/2777 4688/3182/2764 -f 4703/3202/2780 4702/3199/2778 4690/3184/2765 -f 4691/3185/2766 4704/3204/2781 4703/3202/2780 -f 4693/3188/2768 4705/3213/2788 4692/3186/2767 -f 4693/3188/2768 4706/3205/2782 4705/3213/2788 -f 4694/3189/2769 4707/3206/2783 4706/3205/2782 -f 4695/3190/2770 4696/3215/2771 4707/3206/2783 -f 4696/3191/2771 4708/8199/6154 4734/8200/2790 -f 4696/3191/2771 4709/8201/6155 4708/8199/6154 -f 4696/3191/2771 4710/8195/6152 4709/8201/6155 -f 4697/3194/2773 4713/8202/6156 4712/8203/6157 -f 4712/8203/6157 4711/8196/6153 4697/3194/2773 -f 4699/8197/2786 4718/8204/6158 4717/8205/6159 -f 4714/8206/6160 4713/8202/6156 4698/3195/2774 -f 4715/8207/2784 4714/8206/6160 4698/3195/2774 -f 4716/3210/2785 4715/3207/2784 4685/3209/2775 -f 4717/8208/6159 4716/3210/2785 4699/3211/2786 -f 4700/3197/2776 4719/8209/6161 4718/8204/6158 -f 4701/3198/2777 4721/8210/6162 4720/8211/6163 -f 4720/8211/6163 4719/8209/6161 4701/3198/2777 -f 4702/3199/2778 4724/8212/6164 4723/8213/6165 -f 4722/8214/6166 4721/8215/6162 4702/3199/2778 -f 4723/8213/6165 4722/8214/6166 4702/3199/2778 -f 4726/8216/6167 4704/3204/2781 4727/8217/6168 -f 4725/8218/6169 4724/8212/6164 4703/3202/2780 -f 4703/3202/2780 4726/8216/6167 4725/8218/6169 -f 4704/8219/2781 4730/8220/6170 4729/8221/6171 -f 4704/3204/2781 4728/8222/6172 4727/8217/6168 -f 4704/3204/2781 4729/8223/6171 4728/8222/6172 -f 4730/8220/6170 4705/3213/2788 4731/3212/2787 -f 4735/3219/2793 4769/3265/2832 4756/8224/6173 -f 4706/3205/2782 4732/3214/2789 4731/3212/2787 -f 4707/3206/2783 4733/8225/6174 4732/3214/2789 -f 4707/3206/2783 4734/3216/2790 4733/8225/6174 -f 4757/3218/2792 4738/8226/2797 4758/3250/2796 -f 4758/3222/2796 4740/3226/2800 4759/3225/2799 -f 4762/3234/2807 4746/3236/2809 4763/3235/2808 -f 4741/3224/2798 4760/3228/2802 4759/3225/2799 -f 4742/8227/2801 4761/3254/2804 4760/3256/2802 -f 4744/3232/2805 4762/3234/2807 4761/3230/2804 -f 4752/3245/2817 4765/3261/2829 4751/3243/2815 -f 4764/3259/2813 4763/3235/2808 4749/3239/2812 -f 4765/3261/2829 4764/3240/2813 4751/3243/2815 -f 4753/3246/2818 4768/3263/2819 4767/3244/2816 -f 4767/3244/2816 4766/3279/2842 4752/3245/2817 -f 4755/3249/2821 4769/3265/2832 4768/3247/2819 -f 4757/3218/2792 4781/3264/2831 4769/3265/2832 -f 4775/3257/2826 4763/3235/2808 4776/3275/2839 -f 4757/3218/2792 4771/3251/2822 4770/3267/2834 -f 4758/3222/2796 4772/3252/2823 4771/3269/2822 -f 4759/3225/2799 4785/3289/2849 4772/3252/2823 -f 4759/3225/2799 4773/3253/2824 4785/3289/2849 -f 4760/3228/2802 4774/3270/2825 4773/3253/2824 -f 4761/3230/2804 4775/3257/2826 4774/3273/2825 -f 4781/3264/2831 4782/3286/2847 4795/3299/2858 -f 4777/3258/2827 4776/3275/2839 4763/3235/2808 -f 4778/3260/2828 4777/3277/2827 4764/3240/2813 -f 4766/3279/2842 4778/3260/2828 4765/3261/2829 -f 4779/3262/2830 4766/3279/2842 4767/3244/2816 -f 4780/3282/2845 4779/3262/2830 4768/3263/2819 -f 4781/3264/2831 4780/3284/2845 4768/3247/2819 -f 4788/8228/6175 4776/3275/2839 4789/3292/2852 -f 4770/3267/2834 4783/3266/2833 4782/3286/2847 -f 4771/3269/2822 4784/3268/2835 4783/3288/2833 -f 4772/3252/2823 4785/3289/2849 4784/3268/2835 -f 4773/3253/2824 4786/3271/2836 4785/3289/2849 -f 4774/3255/2825 4787/8229/2837 4786/8230/2836 -f 4775/3257/2826 4788/8228/6175 4787/3272/2837 -f 4795/3299/2858 4796/3285/2846 4808/3298/2857 -f 4790/3274/2838 4789/3292/2852 4776/3275/2839 -f 4791/3276/2840 4790/3294/2838 4777/3277/2827 -f 4792/3278/2841 4791/3276/2840 4778/3260/2828 -f 4793/3280/2843 4792/3278/2841 4766/3279/2842 -f 4794/3281/2844 4793/3280/2843 4779/3262/2830 -f 4795/3299/2858 4794/3283/2844 4781/3264/2831 -f 4813/3306/2864 4786/3271/2836 4799/3304/2862 -f 4783/3288/2833 4797/3287/2848 4796/3302/2846 -f 4784/3268/2835 4798/3290/2850 4797/3287/2848 -f 4785/3289/2849 4813/3306/2864 4798/3290/2850 -f 4799/3309/2862 4787/8229/2837 4800/3307/2865 -f 4800/8231/2865 4788/8228/6175 4801/3314/2867 -f 4801/3314/2867 4789/3292/2852 4802/3312/2869 -f 4824/8232/6176 4810/8233/2877 4825/8234/2876 -f 4803/3291/2851 4802/3312/2869 4789/3292/2852 -f 4804/3293/2853 4803/3317/2851 4790/3294/2838 -f 4805/3295/2854 4804/3293/2853 4791/3276/2840 -f 4806/3296/2855 4805/3295/2854 4792/3278/2841 -f 4807/3297/2856 4806/3296/2855 4793/3280/2843 -f 4808/3321/2857 4807/3297/2856 4794/3281/2844 -f 4796/3285/2846 4810/8235/2877 4809/3300/2859 -f 4796/3302/2846 4811/3301/2860 4810/3323/2877 -f 4797/3287/2848 4812/3303/2861 4811/3301/2860 -f 4798/3290/2850 4827/3326/2880 4812/3303/2861 -f 4798/3290/2850 4813/3306/2864 4827/3326/2880 -f 4799/3304/2862 4815/3327/2866 4814/3305/2863 -f 4800/3307/2865 4816/3311/2868 4815/3308/2866 -f 4801/3314/2867 4817/3313/2870 4816/8236/2868 -f 4818/3315/2871 4817/3313/2870 4802/3312/2869 -f 4819/3316/2872 4818/3335/2871 4803/3317/2851 -f 4820/3318/2873 4819/3316/2872 4804/3293/2853 -f 4821/3319/2874 4805/3295/2854 4806/3296/2855 -f 4822/3320/2875 4821/3319/2874 4807/3297/2856 -f 4823/8237/6177 4822/3320/2875 4808/3321/2857 -f 4809/3300/2859 4823/8238/6177 4808/3298/2857 -f 4830/3342/2890 4816/3311/2868 4831/3344/2882 -f 4811/3301/2860 4826/3324/2878 4825/3322/2876 -f 4812/3303/2861 4827/3326/2880 4826/3324/2878 -f 4814/3305/2863 4829/3328/2881 4828/3325/2879 -f 4815/3308/2866 4830/3342/2890 4829/3341/2881 -f 4821/3319/2874 4835/3337/2886 4805/3295/2854 -f 4817/3329/2870 4832/3332/2883 4831/3330/2882 -f 4833/8239/2884 4832/3332/2883 4818/3333/2871 -f 4834/3336/2885 4833/3334/2884 4819/3316/2872 -f 4835/3337/2886 4834/3336/2885 4820/3318/2873 -f 4821/3319/2874 4837/8240/6178 4836/8241/6179 -f 4822/3320/2875 4838/8242/6180 4837/8240/6178 -f 4823/8243/6177 4824/8232/6176 4838/8244/6180 -f 4824/8232/6176 4840/8245/6181 4839/8246/6182 -f 4825/3322/2876 4841/8247/6183 4840/8248/6181 -f 4826/3324/2878 4843/3338/2887 4842/3347/2894 -f 4842/3347/2894 4841/8247/6183 4826/3324/2878 -f 4831/3330/2882 4848/8249/6184 4847/8250/6185 -f 4844/3339/2888 4843/3338/2887 4828/3325/2879 -f 4845/8251/2889 4844/3339/2888 4829/3328/2881 -f 4846/3343/2891 4845/3340/2889 4830/3342/2890 -f 4847/8252/6185 4846/3343/2891 4831/3344/2882 -f 4848/8249/6184 4833/8239/2884 4849/8253/6186 -f 4849/8254/6186 4834/3336/2885 4864/3365/2911 -f 4850/3345/2892 4836/8241/6179 4851/3348/2895 -f 4834/3336/2885 4850/3345/2892 4864/3365/2911 -f 4851/3348/2895 4837/8240/6178 4852/3350/2897 -f 4852/3350/2897 4838/8242/6180 4853/3352/2899 -f 4853/8255/2899 4824/8232/6176 4839/8246/6182 -f 4840/8245/6181 4854/8256/2903 4839/8246/6182 -f 4840/8248/6181 4856/8257/6187 4855/8258/2901 -f 4843/3338/2887 4858/3359/2906 4857/3346/2893 -f 4842/3347/2894 4856/8257/6187 4841/8247/6183 -f 4844/3339/2888 4859/3361/2908 4858/3359/2906 -f 4845/3340/2889 4860/8259/2910 4859/8260/2908 -f 4846/3343/2891 4861/8261/6188 4860/8259/2910 -f 4847/8262/6185 4862/8263/6189 4861/8264/6188 -f 4862/8265/6189 4849/8253/6186 4863/8266/2913 -f 4868/3374/2900 4839/8267/6182 4854/3356/2903 -f 4849/8254/6186 4864/3365/2911 4863/3367/2913 -f 4850/3345/2892 4865/3368/2914 4864/3365/2911 -f 4850/3345/2892 4866/3349/2896 4865/3368/2914 -f 4851/3348/2895 4867/3351/2898 4866/3349/2896 -f 4852/3350/2897 4868/3353/2900 4867/3351/2898 -f 4855/8258/2901 4871/3381/2924 4870/3380/2922 -f 4854/3356/2903 4869/3355/2902 4883/3373/2919 -f 4870/3378/2922 4869/3377/2902 4855/8268/2901 -f 4856/8257/6187 4872/3357/2904 4871/3381/2924 -f 4860/3364/2910 4876/3390/2930 4875/3362/2909 -f 4873/3358/2905 4872/3357/2904 4857/3346/2893 -f 4874/3360/2907 4873/3358/2905 4858/3359/2906 -f 4875/8269/2909 4874/8270/2907 4859/8260/2908 -f 4861/8264/6188 4877/3392/2932 4876/3390/2930 -f 4877/8271/2932 4863/3367/2913 4878/3366/2912 -f 4885/3376/2921 4901/8272/6190 4900/8273/6191 -f 4864/3365/2911 4879/3369/2915 4878/3366/2912 -f 4865/3368/2914 4880/3370/2916 4879/3369/2915 -f 4866/3349/2896 4881/3371/2917 4880/3370/2916 -f 4867/3351/2898 4882/3372/2918 4881/3371/2917 -f 4868/3374/2900 4883/3373/2919 4882/3401/2918 -f 4869/3355/2902 4885/8274/2921 4884/3375/2920 -f 4886/3379/2923 4885/8275/2921 4870/3380/2922 -f 4887/3382/2925 4886/3379/2923 4871/3381/2924 -f 4888/3383/2926 4887/3382/2925 4872/3357/2904 -f 4889/3384/2927 4888/8276/2926 4873/3385/2905 -f 4890/8277/2928 4889/3384/2927 4874/3386/2907 -f 4891/3389/2929 4890/3387/2928 4875/3362/2909 -f 4892/3391/2931 4891/3389/2929 4876/3390/2930 -f 4893/3405/2933 4892/3391/2931 4877/3392/2932 -f 4878/3366/2912 4894/8278/6192 4893/8279/2933 -f 4878/3366/2912 4895/3396/2934 4894/8278/6192 -f 4879/3369/2915 4896/3397/2935 4895/3396/2934 -f 4880/3400/2916 4897/3399/2936 4896/3406/2935 -f 4881/3398/2917 4898/3402/2937 4897/3399/2936 -f 4882/3401/2918 4899/3403/2938 4898/3402/2937 -f 4883/3373/2919 4884/3375/2920 4899/3403/2938 -f 4885/8274/2921 4900/8280/6191 4884/3375/2920 -f 4886/3379/2923 4902/8281/2945 4901/8282/6190 -f 4887/8283/2925 4903/3414/2946 4902/3413/2945 -f 4888/8276/2926 4904/3416/2948 4903/3414/2946 -f 4889/3384/2927 4905/3418/2950 4904/3416/2948 -f 4890/3387/2928 4906/3421/2952 4905/3420/2950 -f 4907/8284/2939 4894/8285/6192 4908/8286/6193 -f 4892/3391/2931 4906/3421/2952 4891/3389/2929 -f 4908/8287/6193 4895/3396/2934 4909/8288/6194 -f 4914/8289/6195 4900/8280/6191 4915/8290/6196 -f 4895/3408/2934 4910/3407/2940 4909/8291/6194 -f 4896/3406/2935 4911/3409/2941 4910/3407/2940 -f 4897/3399/2936 4912/3410/2942 4911/3409/2941 -f 4898/3402/2937 4913/3411/2943 4912/3410/2942 -f 4901/8272/6190 4915/8292/6196 4900/8273/6191 -f 4901/8293/6190 4917/8294/6197 4916/8295/6198 -f 4906/3421/2952 4922/8296/6199 4921/3419/2951 -f 4918/3412/2944 4917/8294/6197 4902/3413/2945 -f 4919/3415/2947 4918/3412/2944 4903/3414/2946 -f 4920/3417/2949 4919/3415/2947 4904/3416/2948 -f 4921/3419/2951 4920/8297/2949 4905/3420/2950 -f 4907/3404/2939 4922/8296/6199 4892/3391/2931 -f 4923/8298/6200 4908/8286/6193 4924/8299/6201 -f 4924/8300/6201 4909/8291/6194 4925/8301/6202 -f 4925/8301/6202 4910/3407/2940 4926/8302/6203 -f 4926/8302/6203 4911/3409/2941 4927/8303/6204 -f 4927/8303/6204 4912/3410/2942 4928/8304/6205 -f 4928/8304/6205 4913/3411/2943 4929/8305/6206 -f 4929/8305/6206 4884/3375/2920 4914/8289/6195 -f 4930/8306/6207 4915/8290/6196 4931/8307/6208 -f 4931/8308/6208 4916/8309/6198 4932/8310/2954 -f 4916/8295/6198 4933/3424/2955 4932/3423/2954 -f 4917/8294/6197 4934/3426/2957 4933/3424/2955 -f 4918/3412/2944 4935/3428/2959 4934/3426/2957 -f 4919/3415/2947 4936/3430/2961 4935/3428/2959 -f 4920/8297/2949 4937/3433/2963 4936/3432/2961 -f 4921/3419/2951 4938/3435/2965 4937/3433/2963 -f 4922/8296/6199 4939/3437/2967 4938/3435/2965 -f 4924/8299/6201 4939/8311/2967 4923/8298/6200 -f 4940/8312/6209 4925/8301/6202 4941/8313/6210 -f 4941/8313/6210 4926/8302/6203 4942/8314/6211 -f 4942/8314/6211 4927/8303/6204 4943/8315/6212 -f 4943/8315/6212 4928/8304/6205 4944/8316/6213 -f 4944/8316/6213 4929/8305/6206 4945/8317/6214 -f 4945/8317/6214 4914/8289/6195 4930/8306/6207 -f 4946/3457/2970 4931/8307/6208 4947/8318/2968 -f 4947/3438/2968 4932/8310/2954 4948/3441/2953 -f 4940/8319/6209 4954/3444/2966 4939/8311/2967 -f 4949/3425/2956 4948/3422/2953 4933/3424/2955 -f 4950/3427/2958 4949/3425/2956 4934/3426/2957 -f 4951/3429/2960 4950/3427/2958 4935/3428/2959 -f 4952/3431/2962 4951/8320/2960 4936/3432/2961 -f 4953/3434/2964 4952/3431/2962 4937/3433/2963 -f 4954/3436/2966 4953/3434/2964 4938/3435/2965 -f 4955/3448/2973 4941/8313/6210 4956/3446/2974 -f 4956/3446/2974 4942/8314/6211 4957/3449/2976 -f 4957/3449/2976 4943/8315/6212 4958/3451/2978 -f 4958/3451/2978 4944/8316/6213 4959/3453/2980 -f 4959/3453/2980 4945/8317/6214 4960/3455/2982 -f 4960/3455/2982 4930/8306/6207 4946/3457/2970 -f 4948/3422/2953 4964/8321/6215 4963/8322/6216 -f 4946/3457/2970 4961/8323/2969 4976/3458/2984 -f 4947/3438/2968 4962/3442/2971 4961/3439/2969 -f 4963/8324/6216 4962/3442/2971 4948/3441/2953 -f 4949/3425/2956 4965/8325/6217 4964/8321/6215 -f 4950/3427/2958 4966/8326/6218 4965/8325/6217 -f 4951/8320/2960 4967/8327/6219 4966/8328/6218 -f 4952/3431/2962 4968/8329/2987 4967/8327/6219 -f 4953/3434/2964 4969/8330/2988 4968/8329/2987 -f 4977/3460/2985 4962/8331/2971 4978/8332/2991 -f 4970/3443/2972 4969/8333/2988 4954/3444/2966 -f 4955/3445/2973 4971/8334/2975 4970/3443/2972 -f 4956/3446/2974 4972/3450/2977 4971/3447/2975 -f 4957/3449/2976 4973/3452/2979 4972/3450/2977 -f 4958/3451/2978 4974/3454/2981 4973/3452/2979 -f 4959/3453/2980 4975/3456/2983 4974/3454/2981 -f 4960/3455/2982 4976/3458/2984 4975/3456/2983 -f 4963/8324/6216 4978/3467/2991 4962/3442/2971 -f 4963/8322/6216 4980/3470/2993 4979/3469/2989 -f 4964/8321/6215 4981/3472/2995 4980/3470/2993 -f 4965/8325/6217 4982/8335/2997 4981/3472/2995 -f 4966/8328/6218 4983/8336/2999 4982/8337/2997 -f 4967/8338/6219 4984/3462/2986 4983/3478/2999 -f 4969/3464/2988 4985/8339/6220 4984/3462/2986 -f 4985/8340/6220 4971/8334/2975 4986/8341/3021 -f 4986/3506/3021 4972/3450/2977 4987/3482/3003 -f 4987/3482/3003 4973/3452/2979 4988/3480/3001 -f 4988/3480/3001 4974/3454/2981 4989/8342/3004 -f 4989/8342/3004 4975/3456/2983 4990/8343/3006 -f 4990/3486/3006 4976/3461/2984 4977/3460/2985 -f 4991/3489/3009 4978/8332/2991 4992/3491/2990 -f 4977/3460/2985 4991/3489/3009 5004/3488/3008 -f 4984/3462/2986 4999/3503/3019 4998/3502/3018 -f 4994/3471/2994 4993/3468/2992 4980/3470/2993 -f 4995/3473/2996 4994/3497/2994 4981/3474/2995 -f 4996/3499/2998 4995/3473/2996 4982/3475/2997 -f 4997/3479/3000 4996/3476/2998 4983/3478/2999 -f 4998/3502/3018 4997/3479/3000 4984/3462/2986 -f 5012/3501/3017 5026/8344/3043 5025/8345/3042 -f 4986/8341/3021 4999/8346/3019 4985/8340/6220 -f 4987/3482/3003 5000/3507/3022 4986/3506/3021 -f 4987/3482/3003 5001/3481/3002 5000/3507/3022 -f 4988/3485/3001 5002/3484/3005 5001/3511/3002 -f 4989/3483/3004 5003/3487/3007 5002/3484/3005 -f 4990/3486/3006 5004/3488/3008 5003/3487/3007 -f 4992/3491/2990 5006/3514/3011 5005/3490/3010 -f 5007/3494/3012 5006/3492/3011 4979/3469/2989 -f 5008/3495/3013 5007/3494/3012 4993/3468/2992 -f 5009/3496/3014 5008/3518/3013 4994/3497/2994 -f 5010/3498/3015 5009/3496/3014 4995/3473/2996 -f 5011/3500/3016 5010/3521/3015 4996/3476/2998 -f 5012/3501/3017 5011/3500/3016 4997/3479/3000 -f 5013/8347/3020 5012/3501/3017 4999/3503/3019 -f 4986/3506/3021 5014/3508/3023 5013/3504/3020 -f 5000/3507/3022 5015/3509/3024 5014/3508/3023 -f 5001/3511/3002 5016/3510/3025 5015/3524/3024 -f 5002/3484/3005 5017/3512/3026 5016/3510/3025 -f 5003/3487/3007 5018/3513/3027 5017/3512/3026 -f 5004/3488/3008 5005/3490/3010 5018/3513/3027 -f 5005/3490/3010 5019/3515/3028 5031/3527/3037 -f 5021/3517/3030 5020/8348/3029 5008/3518/3013 -f 5022/3519/3031 5021/3517/3030 5009/3496/3014 -f 5023/8349/3032 5022/3519/3031 5010/3498/3015 -f 5024/3522/3033 5023/3520/3032 5011/3500/3016 -f 5025/8345/3042 5024/3522/3033 5012/3501/3017 -f 5020/8348/3029 5035/3549/3052 5034/3548/3040 -f 5014/3508/3023 5026/3537/3043 5013/3504/3020 -f 5015/3509/3024 5027/8350/3044 5014/3508/3023 -f 5015/3524/3024 5028/3523/3034 5027/3542/3044 -f 5016/3510/3025 5029/3525/3035 5028/3523/3034 -f 5017/3512/3026 5030/3526/3036 5029/3525/3035 -f 5018/3513/3027 5031/3527/3037 5030/3526/3036 -f 5031/3527/3037 5032/3528/3038 5044/3545/3049 -f 5006/3492/3011 5033/3531/3039 5032/3529/3038 -f 5007/3494/3012 5034/8351/3040 5033/3531/3039 -f 5021/3517/3030 5036/3551/3054 5035/3549/3052 -f 5022/3519/3031 5037/3553/3056 5036/3551/3054 -f 5023/3520/3032 5038/3556/3058 5037/3555/3056 -f 5024/3522/3033 5039/8352/3041 5038/3556/3058 -f 5059/8353/6221 5032/3529/3038 5045/3546/3050 -f 5040/8354/3045 5026/3537/3043 5014/3508/3023 -f 5027/3542/3044 5041/3541/3046 5040/8355/3045 -f 5028/3523/3034 5042/3543/3047 5041/3541/3046 -f 5029/3525/3035 5043/3544/3048 5042/3543/3047 -f 5030/3526/3036 5044/3545/3049 5043/3544/3048 -f 5040/3539/3045 5053/3561/3060 5026/8356/3043 -f 5034/3532/3040 5046/8357/6222 5033/8358/3039 -f 5047/3547/3051 5046/8359/6222 5034/3548/3040 -f 5048/8360/6223 5047/3547/3051 5035/3549/3052 -f 5049/3550/3053 5048/8360/6223 5035/3549/3052 -f 5050/3552/3055 5049/3550/3053 5036/3551/3054 -f 5051/3554/3057 5050/8361/3055 5037/3555/3056 -f 5052/3557/3059 5051/8362/3057 5038/3558/3058 -f 5053/3559/3060 5052/3557/3059 5039/3535/3041 -f 5054/8363/3062 5041/3541/3046 5055/8364/6224 -f 5056/8365/6225 5042/3543/3047 5057/8366/6226 -f 5041/3541/3046 5056/8365/6225 5055/8364/6224 -f 5057/8366/6226 5043/3544/3048 5058/8367/6227 -f 5058/8367/6227 5044/3545/3049 5059/8368/6221 -f 5075/8369/6228 5045/3546/3050 5060/3565/3065 -f 5061/3563/3063 5033/3531/3039 5062/8370/3070 -f 5045/3546/3050 5061/3563/3063 5060/3565/3065 -f 5062/3570/3070 5046/8357/6222 5063/8371/6229 -f 5046/8357/6222 5064/8372/6230 5063/8371/6229 -f 5047/3547/3051 5065/8373/6231 5064/8374/6230 -f 5048/8360/6223 5066/8375/6232 5065/8373/6231 -f 5049/8376/3053 5067/8377/6233 5066/8378/6232 -f 5050/8379/3055 5068/8380/6234 5067/8381/6233 -f 5051/8362/3057 5069/8382/6235 5068/8380/6234 -f 5070/3560/3061 5055/8383/6224 5071/8384/6236 -f 5053/3559/3060 5069/8382/6235 5052/3557/3059 -f 5071/8385/6236 5056/8365/6225 5072/8386/6237 -f 5072/8386/6237 5057/8366/6226 5073/8387/6238 -f 5073/8388/6238 5058/8389/6227 5074/8390/6239 -f 5074/8390/6239 5059/8353/6221 5075/8369/6228 -f 5091/3567/3067 5060/3565/3065 5076/3564/3064 -f 5063/8371/6229 5078/3572/3072 5077/3568/3068 -f 5062/8370/3070 5092/3575/3074 5061/3563/3063 -f 5063/8371/6229 5077/3568/3068 5062/3570/3070 -f 5064/8391/6230 5079/8392/6240 5078/8393/3072 -f 5065/8394/6231 5080/8395/6241 5079/8392/6240 -f 5066/8378/6232 5081/8396/6242 5080/8395/6241 -f 5067/8381/6233 5082/8397/6243 5081/8398/6242 -f 5068/8380/6234 5083/8399/6244 5082/8397/6243 -f 5069/8382/6235 5100/8400/6245 5083/8399/6244 -f 5053/3561/3060 5085/3578/3077 5084/3577/3076 -f 5084/3590/3076 5100/8400/6245 5053/3559/3060 -f 5070/3560/3061 5087/8401/6246 5086/3579/3078 -f 5086/3579/3078 5085/3578/3077 5070/3560/3061 -f 5087/8402/6246 5072/8403/6237 5088/8404/6247 -f 5088/8404/6247 5073/8388/6238 5089/8405/6248 -f 5089/8405/6248 5074/8390/6239 5090/8406/6249 -f 5090/8406/6249 5075/8369/6228 5091/3567/3067 -f 5078/8393/3072 5095/8407/6250 5094/8408/3071 -f 5061/3563/3063 5092/3575/3074 5076/3564/3064 -f 5062/8370/3070 5093/3573/3069 5092/3575/3074 -f 5079/8392/6240 5096/8409/6251 5095/8407/6250 -f 5080/8395/6241 5097/8410/6252 5096/8409/6251 -f 5081/8398/6242 5098/8411/6253 5097/8412/6252 -f 5082/8397/6243 5099/8413/6254 5098/8411/6253 -f 5101/8414/6255 5088/8404/6247 5102/8415/6256 -f 5100/8400/6245 5099/8413/6254 5083/8399/6244 -f 5087/8401/6246 5101/8416/6255 5086/3579/3078 -f 5102/8415/6256 5089/8405/6248 5103/8417/6257 -f 5103/8417/6257 5090/8406/6249 5104/8418/6258 -f 5104/8418/6258 5091/3567/3067 5105/3566/3066 -f 5121/3582/3081 5076/3564/3064 5106/3580/3079 -f 5106/3580/3079 5092/3575/3074 5107/3599/3096 -f 5093/3569/3069 5094/3571/3071 5109/8419/6259 -f 5092/3575/3074 5108/3574/3073 5107/3599/3096 -f 5094/8408/3071 5111/3604/3101 5110/8420/6260 -f 5110/8421/6260 5109/8419/6259 5094/3571/3071 -f 5095/8407/6250 5112/3584/3083 5111/3604/3101 -f 5097/8410/6252 5113/3585/3084 5096/8409/6251 -f 5113/3585/3084 5112/3584/3083 5096/8409/6251 -f 5097/8412/6252 5115/8422/6261 5114/8423/6262 -f 5098/8411/6253 5116/3587/3086 5115/8422/6261 -f 5099/8413/6254 5117/3588/3087 5116/3587/3086 -f 5118/3576/3075 5101/8416/6255 5119/3593/3091 -f 5084/3590/3076 5117/3588/3087 5100/8400/6245 -f 5119/8424/3091 5102/8415/6256 5123/3596/3089 -f 5102/8415/6256 5124/3594/3092 5123/3596/3089 -f 5125/3597/3094 5104/8418/6258 5120/8425/6263 -f 5103/8417/6257 5125/3597/3094 5124/3594/3092 -f 5120/8425/6263 5105/3566/3066 5121/3582/3081 -f 5126/8426/6264 5121/3582/3081 5127/3581/3080 -f 5093/3573/3069 5122/3602/3099 5108/3574/3073 -f 5120/8425/6263 5126/8426/6264 5125/3597/3094 -f 5122/3616/3099 5109/8419/6259 5128/3614/3110 -f 5109/8419/6259 5129/8427/6265 5128/3614/3110 -f 5113/3585/3084 5132/3607/3104 5131/3583/3082 -f 5130/8428/6266 5129/8427/6265 5110/8421/6260 -f 5111/3604/3101 5130/8429/6266 5110/8420/6260 -f 5114/8423/6262 5133/3619/3114 5132/8430/3104 -f 5115/8422/6261 5135/3586/3085 5134/3620/3115 -f 5134/3620/3115 5133/3619/3114 5115/8422/6261 -f 5118/3576/3075 5137/3624/3088 5084/3577/3076 -f 5136/3622/3117 5135/3586/3085 5117/3588/3087 -f 5127/3581/3080 5142/3600/3097 5146/8431/6267 -f 5119/3593/3091 5139/3592/3090 5118/3576/3075 -f 5123/3596/3089 5140/3595/3093 5139/3609/3090 -f 5124/3594/3092 5141/3598/3095 5140/3595/3093 -f 5141/3598/3095 5126/8426/6264 5145/3610/3106 -f 5107/3599/3096 5143/3601/3098 5142/3600/3097 -f 5144/3603/3100 5130/8429/6266 5111/3604/3101 -f 5131/3583/3082 5144/3603/3100 5112/3584/3083 -f 5137/3589/3088 5136/3622/3117 5117/3588/3087 -f 5145/3610/3106 5127/3581/3080 5146/8431/6267 -f 5167/8432/6268 5142/3600/3097 5150/3626/3120 -f 5133/3619/3114 5147/8433/3103 5132/8430/3104 -f 5139/3592/3090 5148/8434/3105 5138/3605/3102 -f 5141/3598/3095 5149/3611/3107 5140/3595/3093 -f 5128/3614/3110 5154/8435/6269 5153/3615/3111 -f 5142/3600/3097 5151/3612/3108 5150/3626/3120 -f 5143/3601/3098 5152/3613/3109 5151/3612/3108 -f 5122/3616/3099 5153/3615/3111 5152/8436/3109 -f 5129/8437/6265 5155/8438/6270 5154/8439/6269 -f 5144/3603/3100 5155/8438/6270 5130/8429/6266 -f 5131/3583/3082 5157/8440/6271 5156/3617/3112 -f 5133/3619/3114 5157/8441/6271 5147/8433/3103 -f 5134/3620/3115 5159/8442/6272 5158/3618/3113 -f 5162/3623/3118 5148/8434/3105 5163/8443/6273 -f 5160/3621/3116 5159/8442/6272 5135/3586/3085 -f 5161/3643/3135 5160/3621/3116 5136/3622/3117 -f 5137/3589/3088 5161/3643/3135 5136/3622/3117 -f 5148/3608/3105 5164/8444/6274 5163/8445/6273 -f 5164/8444/6274 5149/3611/3107 5165/8446/6275 -f 5165/8446/6275 5145/3610/3106 5166/8447/6276 -f 5166/8447/6276 5146/8431/6267 5167/8432/6268 -f 5181/8448/6277 5150/3626/3120 5168/3625/3119 -f 5153/3615/3111 5170/8449/3123 5169/8450/3122 -f 5153/3615/3111 5169/8450/3122 5152/8436/3109 -f 5154/8439/6269 5171/8451/3125 5170/8452/3123 -f 5155/8438/6270 5172/3635/3127 5171/8451/3125 -f 5156/3617/3112 5173/3636/3129 5172/3635/3127 -f 5157/8441/6271 5174/3639/3131 5173/3638/3129 -f 5158/3618/3113 5175/3641/3133 5174/3639/3131 -f 5162/3623/3118 5161/3645/3135 5137/3624/3088 -f 5160/3621/3116 5175/3641/3133 5159/8442/6272 -f 5176/3646/3137 5163/8443/6273 5177/8453/3140 -f 5177/8454/3140 5164/8444/6274 5178/8455/3138 -f 5178/8455/3138 5165/8446/6275 5179/8456/6278 -f 5179/8456/6278 5166/8447/6276 5180/8457/6279 -f 5180/8457/6279 5167/8432/6268 5181/8448/6277 -f 5197/3652/3143 5168/3625/3119 5182/3650/3141 -f 5182/3650/3141 5151/3612/3108 5183/3668/3154 -f 5183/3668/3154 5152/3613/3109 5184/3671/3157 -f 5184/8458/3157 5169/8450/3122 5185/8459/3145 -f 5193/3661/3136 5177/3649/3140 5194/3648/3139 -f 5186/8460/3121 5185/8459/3145 5169/8450/3122 -f 5187/3630/3124 5186/3627/3121 5170/3629/3123 -f 5188/3632/3126 5187/3630/3124 5171/3631/3125 -f 5189/3634/3128 5188/8461/3126 5172/3635/3127 -f 5190/3637/3130 5189/8462/3128 5173/3638/3129 -f 5191/3640/3132 5190/3637/3130 5174/3639/3131 -f 5192/3642/3134 5175/3641/3133 5160/3621/3116 -f 5194/3648/3139 5179/8463/6278 5195/3662/3149 -f 5195/8464/3149 5180/8457/6279 5196/8465/3151 -f 5196/8465/3151 5181/8448/6277 5197/3652/3143 -f 5186/3627/3121 5199/3682/3165 5198/3653/3144 -f 5187/3630/3124 5200/3684/3167 5199/3682/3165 -f 5189/8466/3128 5201/3685/3168 5188/3632/3126 -f 5201/3685/3168 5200/3684/3167 5188/3632/3126 -f 5191/3640/3132 5204/8467/3170 5203/8468/3146 -f 5190/3656/3130 5202/3674/3159 5189/8469/3128 -f 5211/3696/3177 5182/8470/3141 5212/3694/3155 -f 5192/3642/3134 5204/8467/3170 5175/3641/3133 -f 5193/3661/3136 5206/3660/3148 5205/8471/3147 -f 5194/3648/3139 5207/3663/3150 5206/3660/3148 -f 5195/3662/3149 5208/3665/3152 5207/3663/3150 -f 5196/3664/3151 5209/3667/3153 5208/3665/3152 -f 5197/3666/3143 5210/8472/3142 5209/3667/3153 -f 5218/3692/3174 5210/8472/3142 5219/3707/3187 -f 5198/3653/3144 5213/8473/3156 5185/3654/3145 -f 5203/3655/3146 5214/3673/3158 5190/3656/3130 -f 5206/3660/3148 5215/3675/3160 5205/8471/3147 -f 5207/3663/3150 5216/3676/3161 5215/3675/3160 -f 5208/3665/3152 5217/3677/3162 5216/3676/3161 -f 5209/3667/3153 5218/3692/3174 5217/3677/3162 -f 5199/3682/3165 5221/3683/3166 5220/3681/3164 -f 5210/8472/3142 5211/3696/3177 5219/3707/3187 -f 5183/3668/3154 5230/3678/3163 5212/3669/3155 -f 5201/3685/3168 5222/3700/3181 5221/3683/3166 -f 5204/8467/3170 5224/3704/3184 5223/3703/3169 -f 5214/3673/3158 5222/3713/3181 5202/3674/3159 -f 5205/3658/3147 5224/3704/3184 5192/3642/3134 -f 5224/3704/3184 5236/8474/6280 5235/3702/3183 -f 5215/3675/3160 5226/3690/3172 5225/8475/3171 -f 5216/3676/3161 5227/3691/3173 5226/3690/3172 -f 5217/3677/3162 5228/3693/3175 5227/3691/3173 -f 5211/3696/3177 5229/3695/3176 5250/3706/3186 -f 5212/3669/3155 5230/3678/3163 5229/3709/3176 -f 5232/3698/3179 5231/8476/3178 5220/3681/3164 -f 5233/3699/3180 5232/3698/3179 5221/3683/3166 -f 5234/3715/3192 5244/3701/3182 5203/3655/3146 -f 5223/3686/3169 5234/3715/3192 5203/3655/3146 -f 5236/8477/6280 5226/3690/3172 5237/3718/3194 -f 5237/3718/3194 5227/3691/3173 5238/3705/3185 -f 5245/3716/3191 5236/8474/6280 5246/8478/3199 -f 5240/8479/6281 5239/3708/3188 5231/3697/3178 -f 5241/3710/3189 5240/8479/6281 5231/3697/3178 -f 5242/3732/3206 5241/3731/3189 5232/3698/3179 -f 5243/3712/3190 5242/3732/3206 5232/3698/3179 -f 5246/3723/3199 5237/3718/3194 5247/3721/3197 -f 5218/3692/3174 5250/3706/3186 5249/3724/3200 -f 5237/3718/3194 5248/3717/3193 5247/3721/3197 -f 5218/3692/3174 5249/3724/3200 5228/3693/3175 -f 5256/3726/3202 5239/8480/3188 5251/8481/3203 -f 5234/3715/3192 5254/8482/3211 5253/3720/3196 -f 5240/8479/6281 5251/3727/3203 5239/3708/3188 -f 5252/8483/6282 5267/3719/3195 5222/3700/3181 -f 5244/3701/3182 5252/8484/6282 5222/3713/3181 -f 5254/3738/3211 5246/8478/3199 5255/8485/3198 -f 5251/3727/3203 5261/8486/6283 5260/8487/6284 -f 5228/3693/3175 5280/3725/3201 5238/3705/3185 -f 5250/3706/3186 5256/3726/3202 5249/3724/3200 -f 5256/3726/3202 5257/8488/3204 5282/8489/6285 -f 5251/3727/3203 5258/8490/6286 5257/3728/3204 -f 5251/3727/3203 5259/8491/6287 5258/8490/6286 -f 5251/3727/3203 5260/8487/6284 5259/8491/6287 -f 5240/8479/6281 5263/8492/6288 5262/8493/6289 -f 5262/8493/6289 5261/8486/6283 5240/8479/6281 -f 5273/8494/6290 5255/8485/3198 5274/8495/6291 -f 5264/8496/3205 5263/8492/6288 5241/3710/3189 -f 5265/3733/3207 5264/3730/3205 5242/3732/3206 -f 5266/3734/3208 5265/3733/3207 5243/3712/3190 -f 5267/3719/3195 5266/3734/3208 5233/3699/3180 -f 5268/3735/3209 5252/8484/6282 5244/3701/3182 -f 5269/8497/6292 5268/3735/3209 5253/3720/3196 -f 5270/8498/3210 5269/8497/6292 5253/3720/3196 -f 5271/8499/6293 5270/3736/3210 5254/3738/3211 -f 5272/8500/6294 5271/8499/6293 5254/3738/3211 -f 5273/8494/6290 5272/8500/6294 5254/3738/3211 -f 5321/3779/3252 5283/3745/3218 5309/3744/3217 -f 5255/8485/3198 5275/8501/6295 5274/8495/6291 -f 5255/8485/3198 5276/8502/6296 5275/8501/6295 -f 5255/8485/3198 5277/8503/3212 5276/8502/6296 -f 5247/3721/3197 5278/3740/3213 5277/3739/3212 -f 5248/3717/3193 5279/3741/3214 5278/3740/3213 -f 5238/3705/3185 5280/3725/3201 5279/3741/3214 -f 5256/3726/3202 5282/8489/6285 5281/3742/3215 -f 5309/3744/3217 5286/3749/3222 5310/3748/3221 -f 5310/3785/3221 5288/8504/3225 5311/3783/3224 -f 5313/3756/3229 5292/8505/3232 5314/3788/3231 -f 5289/3750/3223 5312/3754/3227 5311/3751/3224 -f 5290/3753/3226 5313/3756/3229 5312/3754/3227 -f 5314/3758/3231 5294/3762/3235 5315/3761/3234 -f 5297/3764/3237 5316/3765/3238 5315/3761/3234 -f 5300/8506/3241 5317/3769/3242 5316/8507/3238 -f 5304/3775/3248 5320/3792/3249 5319/3774/3247 -f 5318/3772/3245 5317/3769/3242 5302/3771/3244 -f 5319/3774/3247 5318/3772/3245 5303/3773/3246 -f 5306/3778/3251 5321/3779/3252 5320/3776/3249 -f 5335/3793/3262 5309/3744/3217 5322/3794/3263 -f 5315/3761/3234 5330/3807/3273 5329/3806/3272 -f 5309/3744/3217 5323/3782/3255 5322/3794/3263 -f 5310/3748/3221 5324/3797/3256 5323/3782/3255 -f 5311/3751/3224 5325/3786/3257 5324/3800/3256 -f 5312/3754/3227 5326/3787/3258 5325/3786/3257 -f 5313/3756/3229 5327/3789/3259 5326/3787/3258 -f 5314/3758/3231 5328/3790/3260 5327/3804/3259 -f 5329/3806/3272 5328/3790/3260 5315/3761/3234 -f 5316/3765/3238 5331/3809/3275 5330/3807/3273 -f 5317/3769/3242 5332/3812/3277 5331/3811/3275 -f 5318/3772/3245 5333/3791/3261 5332/3812/3277 -f 5346/3835/3280 5335/3793/3262 5347/3795/3264 -f 5334/3814/3279 5333/3791/3261 5320/3792/3249 -f 5335/3793/3262 5334/8508/3279 5320/3776/3249 -f 5322/3794/3263 5348/3796/3265 5347/3795/3264 -f 5323/3782/3255 5336/3798/3266 5348/3796/3265 -f 5324/8509/3256 5337/3817/3267 5336/3819/3266 -f 5325/3786/3257 5338/3801/3268 5337/3799/3267 -f 5326/3787/3258 5339/3802/3269 5338/3801/3268 -f 5340/3824/3270 5339/3802/3269 5327/3789/3259 -f 5341/3805/3271 5340/3803/3270 5328/3790/3260 -f 5342/3808/3274 5341/3805/3271 5330/3807/3273 -f 5343/3828/3276 5342/3808/3274 5331/3809/3275 -f 5344/3813/3278 5343/3810/3276 5332/3812/3277 -f 5345/3832/3291 5344/3813/3278 5333/3791/3261 -f 5346/3815/3280 5345/3832/3291 5333/3791/3261 -f 5373/8510/6297 5391/3884/3330 5372/3851/3305 -f 5347/3795/3264 5348/3796/3265 5362/3834/3293 -f 5336/3798/3266 5364/3862/3297 5349/3816/3281 -f 5336/3819/3266 5350/3818/3282 5364/3839/3297 -f 5337/3817/3267 5351/3821/3283 5350/3818/3282 -f 5338/3801/3268 5352/8511/3299 5351/8512/3283 -f 5338/3801/3268 5353/3822/3284 5352/8511/3299 -f 5354/3823/3285 5353/3822/3284 5339/3802/3269 -f 5355/3825/3286 5354/3849/3285 5340/3803/3270 -f 5357/3827/3288 5356/3826/3287 5342/3808/3274 -f 5358/3829/3289 5357/3827/3288 5343/3828/3276 -f 5359/3831/3290 5358/8513/3289 5344/3813/3278 -f 5360/3833/3292 5359/3831/3290 5345/3832/3291 -f 5346/3815/3280 5361/8514/3308 5360/3833/3292 -f 5346/3835/3280 5362/3834/3293 5361/3860/3308 -f 5348/3796/3265 5363/3837/3295 5378/3836/3294 -f 5349/3816/3281 5364/3862/3297 5363/3837/3295 -f 5350/3818/3282 5382/3865/3314 5381/3838/3296 -f 5350/3818/3282 5365/3840/3298 5382/3865/3314 -f 5351/3821/3283 5366/3842/3300 5365/3840/3298 -f 5352/3841/3299 5367/3867/3301 5366/3842/3300 -f 5368/3846/3302 5367/3844/3301 5353/3843/3284 -f 5369/3848/3303 5368/3871/3302 5354/3849/3285 -f 5370/3850/3304 5369/3848/3303 5355/3825/3286 -f 5371/3874/3321 5370/3850/3304 5356/3826/3287 -f 5372/3851/3305 5356/3826/3287 5357/3827/3288 -f 5373/8510/6297 5372/3851/3305 5358/3829/3289 -f 5374/3852/3306 5373/8510/6297 5358/3829/3289 -f 5375/3854/3307 5374/8515/3306 5359/3855/3290 -f 5360/3856/3292 5376/3858/3309 5375/3854/3307 -f 5361/3860/3308 5377/3859/3310 5376/3877/3309 -f 5362/3834/3293 5378/3836/3294 5377/3859/3310 -f 5363/3837/3295 5380/3863/3312 5379/3861/3311 -f 5364/3862/3297 5381/3888/3296 5380/3863/3312 -f 5365/3840/3298 5384/3866/3315 5383/3864/3313 -f 5366/3842/3300 5385/3868/3316 5384/3866/3315 -f 5367/3844/3301 5386/3869/3317 5385/8516/3316 -f 5387/8517/3318 5386/3869/3317 5368/3846/3302 -f 5388/8518/6298 5387/3870/3318 5369/3848/3303 -f 5389/3872/3319 5388/8518/6298 5369/3848/3303 -f 5390/3873/3320 5389/3872/3319 5370/3850/3304 -f 5405/3875/3322 5371/3874/3321 5356/3826/3287 -f 5391/3884/3330 5405/3875/3322 5372/3851/3305 -f 5373/8510/6297 5393/8519/6299 5392/3882/3328 -f 5374/8515/3306 5394/8520/6300 5393/8521/6299 -f 5394/8520/6300 5376/3858/3309 5395/8522/3323 -f 5384/3866/3315 5400/3891/3336 5399/3880/3326 -f 5377/3859/3310 5396/3878/3324 5395/3876/3323 -f 5378/3836/3294 5379/3861/3311 5396/3878/3324 -f 5380/3863/3312 5397/3886/3332 5379/3861/3311 -f 5383/3864/3313 5398/3879/3325 5382/3865/3314 -f 5385/8516/3316 5401/3894/3338 5400/3893/3336 -f 5401/3894/3338 5387/8517/3318 5402/3895/3339 -f 5402/3899/3339 5388/8518/6298 5403/3897/3341 -f 5403/3897/3341 5389/3872/3319 5404/3881/3327 -f 5393/8521/6299 5407/3904/3347 5406/3906/3345 -f 5371/3874/3321 5405/3875/3322 5390/3873/3320 -f 5393/8519/6299 5406/3902/3345 5392/3882/3328 -f 5395/8522/3323 5407/3904/3347 5394/8520/6300 -f 5395/3876/3323 5409/3909/3351 5408/3908/3350 -f 5396/3878/3324 5397/3886/3332 5409/3909/3351 -f 5405/3875/3322 5421/3883/3329 5420/3930/3369 -f 5410/3885/3331 5427/3910/3352 5397/3886/3332 -f 5411/3887/3333 5410/3885/3331 5380/3863/3312 -f 5412/3918/3359 5411/3887/3333 5381/3888/3296 -f 5398/3879/3325 5412/3951/3359 5381/3838/3296 -f 5413/3889/3334 5398/3879/3325 5383/3864/3313 -f 5414/3890/3335 5413/3889/3334 5399/3880/3326 -f 5415/3892/3337 5414/8523/3335 5400/3893/3336 -f 5401/3894/3338 5416/3896/3340 5415/3892/3337 -f 5402/3899/3339 5417/3898/3342 5416/8524/3340 -f 5404/3881/3327 5418/3912/3354 5417/3898/3342 -f 5404/3881/3327 5419/3900/3343 5418/3912/3354 -f 5408/8525/3350 5424/3905/3348 5407/3904/3347 -f 5392/3882/3328 5423/3903/3346 5422/3901/3344 -f 5406/3902/3345 5424/3914/3348 5423/3903/3346 -f 5414/8523/3335 5430/3922/3362 5429/3921/3353 -f 5426/3907/3349 5425/8526/6301 5408/3908/3350 -f 5427/3910/3352 5426/3907/3349 5409/3909/3351 -f 5410/3885/3331 5428/3936/3375 5427/3910/3352 -f 5430/3922/3362 5416/3896/3340 5431/3923/3363 -f 5431/3927/3363 5417/3898/3342 5432/3925/3365 -f 5425/8527/6301 5435/8528/6302 5424/3905/3348 -f 5417/3898/3342 5433/3913/3355 5432/3925/3365 -f 5423/3903/3346 5434/3915/3356 5422/3901/3344 -f 5424/3905/3348 5435/8528/6302 5434/8529/3356 -f 5426/3907/3349 5436/3933/3372 5425/8526/6301 -f 5426/3907/3349 5438/3948/3384 5437/3934/3373 -f 5413/3889/3334 5442/3938/3361 5441/3919/3360 -f 5428/3936/3375 5438/3948/3384 5427/3910/3352 -f 5439/3916/3357 5428/3936/3375 5410/3885/3331 -f 5440/3917/3358 5439/3916/3357 5411/3887/3333 -f 5434/8529/3356 5449/3945/3382 5448/8530/3380 -f 5443/3924/3364 5442/3920/3361 5430/3922/3362 -f 5431/3923/3363 5444/3939/3366 5443/3924/3364 -f 5418/3912/3354 5445/3928/3367 5433/3913/3355 -f 5419/3900/3343 5446/3929/3368 5445/3928/3367 -f 5421/3883/3329 5457/3953/3388 5420/3930/3369 -f 5421/3883/3329 5447/3931/3370 5457/3953/3388 -f 5436/8531/3372 5449/3945/3382 5435/8528/6302 -f 5438/3948/3384 5451/3935/3374 5460/3947/3383 -f 5444/3926/3366 5455/3941/3378 5454/8532/3377 -f 5441/3919/3360 5412/3951/3359 5398/3879/3325 -f 5453/3961/3395 5452/3960/3376 5442/3920/3361 -f 5443/3924/3364 5453/3961/3395 5442/3920/3361 -f 5455/3941/3378 5445/3928/3367 5456/3942/3379 -f 5450/3932/3371 5460/3947/3383 5459/8533/3393 -f 5420/3930/3369 5457/3953/3388 5446/3929/3368 -f 5449/3945/3382 5472/8534/6303 5448/8530/3380 -f 5449/3945/3382 5458/3944/3381 5472/8534/6303 -f 5459/3958/3393 5458/3944/3381 5450/3946/3371 -f 5460/3947/3383 5461/8535/6304 5474/3965/3398 -f 5451/3935/3374 5462/3949/3385 5461/8535/6304 -f 5440/8536/3358 5463/3950/3386 5462/8537/3385 -f 5453/3961/3395 5454/3940/3377 5465/3962/3396 -f 5464/3952/3387 5463/3950/3386 5441/3919/3360 -f 5466/8538/6305 5455/3941/3378 5467/8539/6306 -f 5454/3940/3377 5466/8540/6305 5465/3962/3396 -f 5467/8539/6306 5456/3942/3379 5468/8541/6307 -f 5468/8541/6307 5446/3929/3368 5469/3954/3389 -f 5459/8533/3393 5474/3965/3398 5473/3964/3392 -f 5457/3953/3388 5470/3955/3390 5469/3954/3389 -f 5447/3931/3370 5471/3956/3391 5470/3955/3390 -f 5448/3943/3380 5472/8542/6303 5471/3956/3391 -f 5474/3965/3398 5475/8543/6308 5487/3963/3397 -f 5461/8535/6304 5476/8544/6309 5475/8543/6308 -f 5462/8537/3385 5477/8545/6310 5476/8546/6309 -f 5463/3950/3386 5478/8547/6311 5477/8545/6310 -f 5464/3952/3387 5479/8548/3394 5478/8547/6311 -f 5465/8549/3396 5467/8539/6306 5480/8550/6312 -f 5480/8550/6312 5468/8541/6307 5481/8551/6313 -f 5481/8551/6313 5469/3954/3389 5482/8552/6314 -f 5482/8552/6314 5470/3955/3390 5483/8553/6315 -f 5483/8553/6315 5471/3956/3391 5484/8554/6316 -f 5484/8554/6316 5472/8542/6303 5485/8555/6317 -f 5472/8534/6303 5486/8556/6318 5485/8557/6317 -f 5487/3963/3397 5488/8558/6319 5503/8559/6320 -f 5473/3957/3392 5486/8556/6318 5458/3944/3381 -f 5475/8543/6308 5489/8560/6321 5488/8558/6319 -f 5476/8546/6309 5490/8561/6322 5489/8562/6321 -f 5477/8545/6310 5491/8563/6323 5490/8561/6322 -f 5478/8547/6311 5492/8564/6324 5491/8563/6323 -f 5494/8565/6325 5480/8550/6312 5495/8566/6326 -f 5493/3966/3399 5492/8567/6324 5479/3959/3394 -f 5465/3962/3396 5494/8568/6325 5493/3966/3399 -f 5495/8566/6326 5481/8551/6313 5496/8569/6327 -f 5496/8569/6327 5482/8552/6314 5497/8570/6328 -f 5497/8570/6328 5483/8553/6315 5498/8571/6329 -f 5498/8571/6329 5484/8554/6316 5499/8572/6330 -f 5499/8572/6330 5485/8555/6317 5500/8573/6331 -f 5500/8574/6331 5486/8556/6318 5501/3969/3402 -f 5486/8556/6318 5502/3967/3400 5501/3969/3402 -f 5473/3964/3392 5503/8559/6320 5502/8575/3400 -f 5518/3972/3405 5488/8576/6319 5504/3970/3403 -f 5504/3970/3403 5489/8577/6321 5505/3973/3406 -f 5505/3977/3406 5490/8578/6322 5506/3975/3408 -f 5506/3975/3408 5491/8579/6323 5507/3978/3410 -f 5507/3978/3410 5492/8580/6324 5508/3980/3412 -f 5508/3984/3412 5493/3966/3399 5509/3982/3414 -f 5493/3966/3399 5510/3986/3417 5509/3982/3414 -f 5494/8581/6325 5511/8582/6332 5510/8583/3417 -f 5495/8584/6326 5512/8585/6333 5511/8582/6332 -f 5496/8586/6327 5513/8587/6334 5512/8585/6333 -f 5497/8588/6328 5514/8589/6335 5513/8587/6334 -f 5498/8590/6329 5515/8591/6336 5514/8589/6335 -f 5499/8592/6330 5516/8593/3419 5515/8591/6336 -f 5500/8574/6331 5517/3968/3401 5516/3988/3419 -f 5510/8583/3417 5526/3994/3423 5525/3993/3416 -f 5503/8559/6320 5518/8594/3405 5502/8575/3400 -f 5504/3970/3403 5520/3974/3407 5519/3971/3404 -f 5505/3973/3406 5521/8595/3409 5520/3974/3407 -f 5506/3975/3408 5522/3979/3411 5521/3976/3409 -f 5507/3978/3410 5523/3981/3413 5522/3979/3411 -f 5508/3980/3412 5524/8596/3415 5523/3981/3413 -f 5525/3985/3416 5524/3983/3415 5509/3982/3414 -f 5511/8582/6332 5527/3996/3425 5526/3994/3423 -f 5512/8585/6333 5528/3998/3427 5527/3996/3425 -f 5513/8587/6334 5529/4000/3429 5528/3998/3427 -f 5514/8589/6335 5530/4002/3431 5529/4000/3429 -f 5515/8591/6336 5531/4003/3418 5530/4002/3431 -f 5534/8597/6337 5520/3974/3407 5535/8598/6338 -f 5517/3968/3401 5532/3989/3420 5531/3987/3418 -f 5502/3991/3400 5533/3990/3421 5532/8599/3420 -f 5518/3972/3405 5519/3971/3404 5533/3990/3421 -f 5535/8598/6338 5521/8595/3409 5536/8600/6339 -f 5536/8601/6339 5522/3979/3411 5537/4006/3434 -f 5537/4006/3434 5523/3981/3413 5538/4004/3432 -f 5523/3981/3413 5539/8602/6340 5538/4004/3432 -f 5525/3985/3416 5539/8603/6340 5524/3983/3415 -f 5546/8604/6341 5532/3989/3420 5547/8605/6342 -f 5541/3992/3422 5540/8606/6343 5525/3993/3416 -f 5542/3995/3424 5541/3992/3422 5526/3994/3423 -f 5543/3997/3426 5542/3995/3424 5527/3996/3425 -f 5544/3999/3428 5543/3997/3426 5528/3998/3427 -f 5545/4001/3430 5544/3999/3428 5529/4000/3429 -f 5546/8607/6341 5545/4001/3430 5531/4003/3418 -f 5547/8608/6342 5533/3990/3421 5548/8609/6344 -f 5548/8609/6344 5519/3971/3404 5534/8597/6337 -f 5549/8610/6345 5535/8598/6338 5550/8611/6346 -f 5550/8611/6346 5536/8600/6339 5551/8612/6347 -f 5551/8613/6347 5537/4006/3434 5552/4005/3433 -f 5553/8614/6348 5540/8615/6343 5554/8616/6349 -f 5539/8602/6340 5553/8617/6348 5538/4004/3432 -f 5540/8606/6343 5555/8618/6350 5554/8619/6349 -f 5541/3992/3422 5556/8620/6351 5555/8618/6350 -f 5542/3995/3424 5557/8621/6352 5556/8620/6351 -f 5543/3997/3426 5558/8622/6353 5557/8621/6352 -f 5544/3999/3428 5559/8623/6354 5558/8622/6353 -f 5545/4001/3430 5560/8624/6355 5559/8623/6354 -f 5547/8605/6342 5560/8625/6355 5546/8604/6341 -f 5561/8626/6356 5548/8609/6344 5562/8627/6357 -f 5562/8627/6357 5534/8597/6337 5549/8610/6345 -f 5563/4009/3437 5550/8611/6346 5564/4010/3438 -f 5564/4010/3438 5551/8612/6347 5565/4012/3440 -f 5565/4014/3440 5552/4005/3433 5566/4007/3435 -f 5566/4007/3435 5553/8617/6348 5567/4016/3443 -f 5567/4019/3443 5554/8616/6349 5568/4017/3444 -f 5554/8616/6349 5569/8628/3446 5568/4017/3444 -f 5555/8618/6350 5570/4027/3448 5569/8629/3446 -f 5556/8620/6351 5571/4025/3450 5570/4027/3448 -f 5557/8621/6352 5572/4028/3452 5571/4025/3450 -f 5558/8622/6353 5573/4030/3454 5572/4028/3452 -f 5559/8623/6354 5574/4032/3456 5573/4030/3454 -f 5561/8630/6356 5574/4035/3456 5560/8625/6355 -f 5575/4036/3459 5562/8631/6357 5576/8632/3461 -f 5576/8633/3461 5549/8610/6345 5563/4009/3437 -f 5592/4040/3462 5593/4046/3467 5607/4045/3466 -f 5577/4008/3436 5592/8634/3462 5563/4009/3437 -f 5578/4011/3439 5577/4008/3436 5564/4010/3438 -f 5579/4013/3441 5578/8635/3439 5565/4014/3440 -f 5580/4043/3464 5579/4013/3441 5566/4007/3435 -f 5581/4015/3442 5580/4043/3464 5566/4007/3435 -f 5582/4018/3445 5581/8636/3442 5567/4019/3443 -f 5568/4017/3444 5583/8637/3447 5582/4018/3445 -f 5569/4020/3446 5584/4024/3449 5583/4021/3447 -f 5570/4027/3448 5585/4026/3451 5584/8638/3449 -f 5571/4025/3450 5586/4029/3453 5585/4026/3451 -f 5572/4028/3452 5587/4031/3455 5586/4029/3453 -f 5573/4030/3454 5588/8639/6358 5587/4031/3455 -f 5573/4030/3454 5589/4033/3457 5588/8639/6358 -f 5574/4035/3456 5590/4034/3458 5589/8640/3457 -f 5591/8641/3460 5590/4034/3458 5575/4036/3459 -f 5592/4040/3462 5591/4037/3460 5576/4039/3461 -f 5577/8642/3436 5594/4048/3469 5593/4046/3467 -f 5578/8643/3439 5595/4051/3471 5594/4050/3469 -f 5579/4013/3441 5596/4042/3463 5595/4053/3471 -f 5581/4015/3442 5597/4054/3473 5596/4042/3463 -f 5597/4071/3473 5583/4021/3447 5598/8644/6359 -f 5598/8644/6359 5584/4024/3449 5599/4058/3477 -f 5599/4058/3477 5585/8645/3451 5600/4056/3475 -f 5600/4056/3475 5586/8646/3453 5601/4059/3478 -f 5601/4059/3478 5587/8647/3455 5602/4061/3480 -f 5602/4065/3480 5588/8639/6358 5603/4063/3482 -f 5603/4063/3482 5589/4033/3457 5604/8648/6360 -f 5604/8648/6360 5590/8649/3458 5605/8650/3485 -f 5590/8651/3458 5606/4068/3486 5605/4067/3485 -f 5591/4037/3460 5607/4045/3466 5606/4068/3486 -f 5613/4069/3487 5599/4058/3477 5614/4072/3489 -f 5609/4047/3468 5608/4044/3465 5593/4046/3467 -f 5610/8652/3470 5609/4047/3468 5594/4048/3469 -f 5611/8653/3472 5610/4049/3470 5595/4051/3471 -f 5612/4055/3474 5611/4052/3472 5596/4042/3463 -f 5598/8644/6359 5613/4069/3487 5597/4071/3473 -f 5618/4064/3483 5604/8648/6360 5619/8654/6361 -f 5599/4058/3477 5615/4057/3476 5614/4072/3489 -f 5600/4056/3475 5616/4060/3479 5615/4057/3476 -f 5601/4059/3478 5617/4062/3481 5616/4060/3479 -f 5602/4061/3480 5618/8655/3483 5617/4062/3481 -f 5608/4044/3465 5622/4076/3493 5621/4074/3491 -f 5605/8650/3485 5619/8654/6361 5604/8648/6360 -f 5608/4044/3465 5620/4066/3484 5607/4045/3466 -f 5609/4047/3468 5623/4078/3495 5622/4076/3493 -f 5610/4049/3470 5624/4081/3497 5623/4080/3495 -f 5611/4052/3472 5625/4084/3499 5624/4083/3497 -f 5612/4055/3474 5626/4085/3488 5625/4084/3499 -f 5627/4088/3502 5616/4060/3479 5628/4089/3503 -f 5615/4057/3476 5627/4088/3502 5614/4072/3489 -f 5628/4089/3503 5617/4062/3481 5629/4091/3505 -f 5629/4091/3505 5618/8655/3483 5630/4093/3507 -f 5630/4097/3507 5619/8654/6361 5631/4095/3509 -f 5619/8654/6361 5632/4099/3490 5631/4095/3509 -f 5635/4077/3494 5649/8656/6362 5648/4101/3513 -f 5634/4075/3492 5633/4113/3524 5621/4074/3491 -f 5635/4077/3494 5634/4075/3492 5622/4076/3493 -f 5636/8657/3496 5635/4077/3494 5623/4078/3495 -f 5637/8658/6363 5636/4079/3496 5624/4081/3497 -f 5638/8659/3498 5637/8658/6363 5624/4081/3497 -f 5639/4086/3500 5638/4082/3498 5625/4084/3499 -f 5626/4085/3488 5640/8660/3514 5639/4086/3500 -f 5626/4070/3488 5641/4087/3501 5640/4102/3514 -f 5628/4089/3503 5642/4092/3506 5655/4090/3504 -f 5629/4091/3505 5643/4094/3508 5642/4092/3506 -f 5630/4093/3507 5644/8661/6364 5643/4094/3508 -f 5630/4093/3507 5645/8662/3510 5644/8661/6364 -f 5631/4095/3509 5646/4098/3511 5645/4096/3510 -f 5647/4100/3512 5646/8663/3511 5632/4073/3490 -f 5621/4074/3491 5647/4100/3512 5620/4066/3484 -f 5636/4079/3496 5650/8664/6365 5649/8665/6362 -f 5637/8658/6363 5651/8666/6366 5650/8664/6365 -f 5639/8667/3500 5651/8668/6366 5638/8669/3498 -f 5656/8670/6367 5643/4094/3508 5657/8671/6368 -f 5640/4102/3514 5653/4105/3516 5652/4103/3515 -f 5641/4087/3501 5654/4106/3517 5653/4105/3516 -f 5627/4088/3502 5655/4090/3504 5654/4106/3517 -f 5642/4092/3506 5656/8670/6367 5655/4090/3504 -f 5657/8671/6368 5644/8661/6364 5658/8672/6369 -f 5658/8672/6369 5645/8662/3510 5659/8673/6370 -f 5659/8674/6370 5646/8675/3511 5660/8676/3521 -f 5646/8663/3511 5661/4111/3522 5660/4110/3521 -f 5647/4100/3512 5633/4113/3524 5661/4111/3522 -f 5633/4113/3524 5663/4115/3526 5662/4128/3539 -f 5634/4075/3492 5664/4116/3527 5663/4115/3526 -f 5648/4101/3513 5665/8677/6371 5664/4116/3527 -f 5649/8665/6362 5666/8678/6372 5665/8679/6371 -f 5650/8664/6365 5667/8680/6373 5666/8678/6372 -f 5652/8681/3515 5667/8682/6373 5651/8668/6366 -f 5668/8683/6374 5653/4105/3516 5669/4107/3518 -f 5684/4120/3531 5656/8670/6367 5671/4118/3529 -f 5654/4106/3517 5670/4108/3519 5669/4107/3518 -f 5671/4118/3529 5657/8671/6368 5672/4121/3532 -f 5672/4121/3532 5658/8672/6369 5673/4123/3534 -f 5673/4123/3534 5659/8673/6370 5674/4125/3536 -f 5674/8684/3536 5660/8676/3521 5675/8685/6375 -f 5664/4116/3527 5679/4130/3541 5678/4114/3525 -f 5676/4109/3520 5675/8686/6375 5660/4110/3521 -f 5677/4112/3523 5676/4109/3520 5661/4111/3522 -f 5662/4128/3539 5677/4112/3523 5633/4113/3524 -f 5665/8679/6371 5680/4132/3543 5679/4134/3541 -f 5666/8678/6372 5681/4135/3545 5680/4132/3543 -f 5667/8682/6373 5682/8687/3548 5681/8688/3545 -f 5682/4138/3548 5669/4107/3518 5683/4117/3528 -f 5674/8684/3536 5689/8689/3556 5688/8690/3537 -f 5655/4090/3504 5684/4120/3531 5670/4108/3519 -f 5671/4118/3529 5686/4122/3533 5685/4119/3530 -f 5672/4121/3532 5687/4124/3535 5686/4122/3533 -f 5673/4123/3534 5688/4126/3537 5687/4124/3535 -f 5675/8686/6375 5690/4144/3554 5689/4146/3556 -f 5676/4109/3520 5691/4147/3557 5690/4144/3554 -f 5681/8688/3545 5697/8691/3560 5696/8692/3546 -f 5662/4128/3539 5691/4147/3557 5677/4112/3523 -f 5663/4115/3526 5693/4129/3540 5692/4127/3538 -f 5678/4114/3525 5694/4131/3542 5693/4129/3540 -f 5679/4134/3541 5695/4133/3544 5694/8693/3542 -f 5680/4132/3543 5696/4136/3546 5695/4133/3544 -f 5688/8690/3537 5705/8694/3555 5704/8695/3568 -f 5698/4137/3547 5697/4154/3560 5682/4138/3548 -f 5699/4139/3549 5698/4137/3547 5683/4117/3528 -f 5700/4140/3550 5699/4139/3549 5670/4108/3519 -f 5701/4141/3551 5700/4140/3550 5685/4119/3530 -f 5702/4142/3552 5701/4141/3551 5686/4122/3533 -f 5703/4143/3553 5702/4142/3552 5687/4124/3535 -f 5704/8696/3568 5703/4143/3553 5688/4126/3537 -f 5707/4167/3572 5693/4129/3540 5708/4165/3570 -f 5690/4144/3554 5706/4148/3558 5705/4145/3555 -f 5691/4147/3557 5692/4127/3538 5706/4148/3558 -f 5708/4165/3570 5694/4131/3542 5709/4168/3573 -f 5709/4171/3573 5695/4133/3544 5710/4149/3559 -f 5719/4182/3585 5706/4148/3558 5720/4180/3583 -f 5696/4152/3546 5711/4151/3561 5710/4173/3559 -f 5712/8697/3562 5711/8698/3561 5697/8691/3560 -f 5713/4155/3563 5712/4153/3562 5698/4137/3547 -f 5714/4156/3564 5713/4155/3563 5699/4139/3549 -f 5715/4157/3565 5714/4156/3564 5700/4140/3550 -f 5716/4158/3566 5715/4157/3565 5701/4141/3551 -f 5717/4159/3567 5716/4158/3566 5702/4142/3552 -f 5718/4163/3569 5717/4160/3567 5704/4162/3568 -f 5705/8694/3555 5719/8699/3585 5718/8700/3569 -f 5720/4180/3583 5692/4127/3538 5707/4167/3572 -f 5711/4151/3561 5725/4190/3577 5724/4172/3576 -f 5707/4167/3572 5721/4166/3571 5732/4183/3586 -f 5708/4165/3570 5722/4169/3574 5721/4166/3571 -f 5709/4171/3573 5723/4170/3575 5722/8701/3574 -f 5710/4173/3559 5724/4172/3576 5723/4186/3575 -f 5730/4179/3582 5719/8702/3585 5731/4200/3584 -f 5726/4175/3578 5725/4174/3577 5713/4155/3563 -f 5727/4176/3579 5726/4175/3578 5714/4156/3564 -f 5728/4177/3580 5727/4176/3579 5715/4157/3565 -f 5729/4178/3581 5728/4177/3580 5716/4158/3566 -f 5730/4179/3582 5729/4197/3581 5717/4160/3567 -f 5739/4192/3592 5754/4227/3618 5753/4225/3616 -f 5720/4180/3583 5732/4183/3586 5731/4181/3584 -f 5721/4166/3571 5734/4185/3588 5733/4184/3587 -f 5722/4169/3574 5735/8703/3589 5734/4185/3588 -f 5723/4186/3575 5736/4189/3590 5735/4187/3589 -f 5724/4172/3576 5737/4191/3591 5736/4189/3590 -f 5738/4208/3603 5737/4191/3591 5725/4190/3577 -f 5739/4192/3592 5738/4211/3603 5725/4174/3577 -f 5740/4193/3593 5739/4192/3592 5726/4175/3578 -f 5741/4194/3594 5740/4193/3593 5727/4176/3579 -f 5742/8704/3595 5741/4194/3594 5728/4177/3580 -f 5743/4198/3596 5742/4195/3595 5729/4197/3581 -f 5744/4199/3597 5743/4198/3596 5730/4179/3582 -f 5731/4200/3584 5745/8705/3598 5744/4199/3597 -f 5732/4183/3586 5746/4217/3610 5745/4201/3598 -f 5732/4183/3586 5733/4184/3587 5746/4217/3610 -f 5733/4184/3587 5747/4202/3599 5760/4218/3611 -f 5734/4185/3588 5748/8706/3600 5747/4202/3599 -f 5735/4187/3589 5749/4206/3601 5748/4222/3600 -f 5736/4189/3590 5750/4207/3602 5749/4206/3601 -f 5737/4191/3591 5751/4209/3604 5750/4207/3602 -f 5752/4210/3605 5751/8707/3604 5738/4211/3603 -f 5753/4225/3616 5752/4210/3605 5739/4192/3592 -f 5745/8705/3598 5758/4216/3609 5744/4199/3597 -f 5755/4212/3606 5754/4227/3618 5740/4193/3593 -f 5756/8708/3607 5755/4212/3606 5741/4194/3594 -f 5757/4215/3608 5756/4213/3607 5742/4195/3595 -f 5758/4216/3609 5757/4215/3608 5743/4198/3596 -f 5773/4239/3627 5747/4202/3599 5761/4237/3625 -f 5745/4201/3598 5760/4218/3611 5759/4236/3624 -f 5764/4223/3614 5751/4209/3604 5765/4245/3631 -f 5747/4202/3599 5762/8709/3612 5761/4237/3625 -f 5748/4204/3600 5763/4242/3613 5762/4219/3612 -f 5749/4206/3601 5764/4223/3614 5763/4221/3613 -f 5765/8710/3631 5752/4210/3605 5766/4224/3615 -f 5766/4224/3615 5781/4249/3634 5780/8711/6376 -f 5767/4226/3617 5766/4224/3615 5753/4225/3616 -f 5768/4228/3619 5767/4226/3617 5754/4227/3618 -f 5769/4229/3620 5768/4251/3619 5755/4230/3606 -f 5770/4231/3621 5769/4229/3620 5756/4213/3607 -f 5771/4232/3622 5770/4231/3621 5757/4215/3608 -f 5772/4233/3623 5771/4232/3622 5758/4216/3609 -f 5760/4218/3611 5773/4239/3627 5772/4235/3623 -f 5761/4237/3625 5775/8712/3628 5774/4238/3626 -f 5762/4219/3612 5776/4243/3629 5775/4240/3628 -f 5763/4221/3613 5777/4244/3630 5776/8713/3629 -f 5764/4223/3614 5778/4246/3632 5777/4244/3630 -f 5765/4245/3631 5779/4248/3633 5778/4246/3632 -f 5766/4224/3615 5780/8711/6376 5779/8714/3633 -f 5773/4239/3627 5786/8715/6377 5772/4235/3623 -f 5782/4250/3635 5781/8716/3634 5768/4251/3619 -f 5783/4252/3636 5782/4250/3635 5769/4229/3620 -f 5784/4253/3637 5783/4252/3636 5770/4231/3621 -f 5785/4254/3638 5784/4253/3637 5771/4232/3622 -f 5786/8717/6377 5785/4254/3638 5772/4233/3623 -f 5788/4275/3656 5775/4240/3628 5789/4256/3640 -f 5773/4239/3627 5774/4238/3626 5787/8718/6378 -f 5789/4256/3640 5776/4243/3629 5790/4257/3641 -f 5790/4259/3641 5777/4244/3630 5791/4260/3643 -f 5791/4260/3643 5778/4246/3632 5792/4262/3645 -f 5792/4262/3645 5779/4248/3633 5793/8719/3647 -f 5793/8720/3647 5780/8721/6376 5794/8722/3649 -f 5780/8723/6376 5795/8724/3650 5794/8725/3649 -f 5781/8716/3634 5796/8726/6379 5795/8724/3650 -f 5782/4250/3635 5797/8727/6380 5796/8726/6379 -f 5783/4252/3636 5798/8728/6381 5797/8727/6380 -f 5784/4253/3637 5799/8729/6382 5798/8728/6381 -f 5785/4254/3638 5800/8730/3654 5799/8729/6382 -f 5786/8731/6377 5801/8732/3652 5800/8733/3654 -f 5801/4274/3652 5774/8734/3626 5788/4275/3656 -f 5788/4275/3656 5802/4277/3658 5815/4273/3655 -f 5809/4282/3663 5796/8726/6379 5810/4280/3661 -f 5803/4255/3639 5802/4277/3658 5789/4256/3640 -f 5804/8735/3642 5803/4255/3639 5790/4257/3641 -f 5805/4261/3644 5804/4258/3642 5791/4260/3643 -f 5806/8736/3646 5805/4261/3644 5792/4262/3645 -f 5807/4266/3648 5806/4263/3646 5793/4265/3647 -f 5794/4267/3649 5808/4269/3651 5807/4266/3648 -f 5795/8724/3650 5809/4282/3663 5808/8737/3651 -f 5810/4280/3661 5797/8727/6380 5811/4283/3664 -f 5811/4283/3664 5798/8728/6381 5812/8738/6383 -f 5812/8738/6383 5799/8729/6382 5813/8739/6384 -f 5813/8740/6384 5800/4272/3654 5814/4271/3653 -f 5802/4277/3658 5817/8741/6385 5816/4276/3657 -f 5815/4286/3655 5814/4271/3653 5801/4270/3652 -f 5803/4255/3639 5818/4305/3668 5817/8741/6385 -f 5804/4258/3642 5819/4289/3669 5818/4288/3668 -f 5805/4261/3644 5820/8742/3659 5819/4289/3669 -f 5821/8743/3660 5809/4282/3663 5822/4281/3662 -f 5821/4279/3660 5820/4278/3659 5807/4266/3648 -f 5811/4283/3664 5824/4294/3673 5835/4296/3675 -f 5810/4280/3661 5823/4284/3665 5822/4281/3662 -f 5811/4283/3664 5835/4296/3675 5823/4284/3665 -f 5824/4294/3673 5813/8739/6384 5825/4297/3676 -f 5825/8744/3676 5814/4271/3653 5826/4300/3679 -f 5816/4276/3657 5829/4303/3682 5828/4302/3681 -f 5814/4271/3653 5827/4285/3666 5826/4300/3679 -f 5816/8745/3657 5827/4285/3666 5815/4286/3655 -f 5819/8746/3669 5831/4309/3686 5830/4308/3667 -f 5818/4305/3668 5829/4303/3682 5817/8741/6385 -f 5837/8747/3677 5826/4300/3679 5838/4299/3678 -f 5832/4290/3670 5831/4309/3686 5820/4278/3659 -f 5821/4279/3660 5833/4292/3671 5832/4290/3670 -f 5822/4281/3662 5834/4293/3672 5833/8748/3671 -f 5823/4284/3665 5835/4296/3675 5834/4293/3672 -f 5824/4294/3673 5837/4298/3677 5836/4295/3674 -f 5827/4285/3666 5828/4312/3681 5838/4299/3678 -f 5831/4309/3686 5848/8749/6386 5847/8750/6387 -f 5839/8751/6388 5864/4311/3688 5828/4312/3681 -f 5840/4301/3680 5839/8752/6388 5828/4302/3681 -f 5841/4304/3683 5840/4301/3680 5829/4303/3682 -f 5842/4306/3684 5841/8753/3683 5818/4288/3668 -f 5843/8754/6389 5842/4306/3684 5830/4287/3667 -f 5844/8755/3685 5843/8754/6389 5830/4287/3667 -f 5845/8756/6390 5844/4307/3685 5831/4309/3686 -f 5846/8757/6391 5845/8756/6390 5831/4309/3686 -f 5847/8750/6387 5846/8757/6391 5831/4309/3686 -f 5851/8758/6392 5833/4292/3671 5852/8759/6393 -f 5832/4290/3670 5849/8760/6394 5848/8749/6386 -f 5832/4290/3670 5850/8761/6395 5849/8760/6394 -f 5832/4290/3670 5851/8758/6392 5850/8761/6395 -f 5853/8762/6396 5834/4293/3672 5854/8763/6397 -f 5833/8748/3671 5853/8762/6396 5852/8764/6393 -f 5834/4293/3672 5855/4310/3687 5854/8763/6397 -f 5856/8765/6398 5837/4298/3677 5857/8766/6399 -f 5836/4295/3674 5856/8765/6398 5855/4310/3687 -f 5859/8767/6400 5838/4299/3678 5860/8768/6401 -f 5837/8747/3677 5858/8769/6402 5857/8770/6399 -f 5837/8747/3677 5859/8767/6400 5858/8769/6402 -f 5896/8771/6403 5866/8772/3700 5868/8773/3702 -f 5838/4299/3678 5861/8774/6404 5860/8768/6401 -f 5862/8775/6405 5861/8774/6404 5838/4299/3678 -f 5863/8776/6406 5862/8775/6405 5838/4299/3678 -f 5864/4311/3688 5863/8776/6406 5838/4299/3678 -f 5897/8777/6407 5868/8773/3702 5870/8778/3713 -f 5898/4315/3691 5870/8779/3713 5873/4313/3689 -f 5900/4316/3692 5873/4313/3689 5875/4317/3693 -f 5877/8780/6408 5901/4318/3694 5875/4317/3693 -f 5879/8781/6409 5902/8782/6410 5877/8783/6408 -f 5881/8784/3709 5903/8785/6411 5879/8781/6409 -f 5883/8786/3711 5904/8787/6412 5881/8784/3709 -f 5885/8788/3705 5905/8789/6413 5883/8786/3711 -f 5887/4331/3707 5906/8790/6414 5885/4329/3705 -f 5889/4320/3696 5907/8791/6415 5887/4331/3707 -f 5911/8792/6416 5890/4322/3698 5892/8793/6417 -f 5889/4320/3696 5890/4322/3698 5909/4321/3697 -f 5912/8794/6418 5892/8795/6417 5894/8796/6419 -f 5895/8797/6420 5894/8796/6419 5866/8772/3700 -f 5876/8798/6421 5871/8799/6422 5874/4328/3704 -f 5865/8800/6423 5912/8794/6418 5895/8797/6420 -f 5893/8801/6424 5911/8802/6416 5912/8794/6418 -f 5891/8803/6425 5910/4323/3699 5911/8792/6416 -f 5909/4321/3697 5886/8804/6426 5908/4319/3695 -f 5909/4321/3697 5910/4323/3699 5888/8805/6427 -f 5907/8791/6415 5884/8806/6428 5906/8790/6414 -f 5886/8804/6426 5907/8791/6415 5908/4319/3695 -f 5906/8807/6414 5882/8808/6429 5905/8789/6413 -f 5905/8789/6413 5880/8809/6430 5904/8787/6412 -f 5904/8787/6412 5878/8810/6431 5903/8785/6411 -f 5903/8785/6411 5876/8811/6421 5902/8782/6410 -f 5902/8812/6410 5874/4328/3704 5901/4318/3694 -f 5872/4327/3703 5898/4315/3691 5899/4314/3690 -f 5874/4328/3704 5900/4316/3692 5901/4318/3694 -f 5899/4314/3690 5900/4316/3692 5872/4327/3703 -f 5871/8799/6422 5897/8813/6407 5898/4315/3691 -f 5869/8814/6432 5896/8771/6403 5897/8777/6407 -f 5867/8815/6433 5895/8797/6420 5896/8771/6403 -f 5919/4330/3706 5883/4337/3711 5918/4336/3710 -f 5919/4330/3706 5927/4359/3725 5928/4344/3716 -f 5917/8816/3719 5881/4335/3709 5879/8817/6409 -f 5916/4338/3712 5877/8818/6408 5875/4334/3693 -f 5914/4340/3714 5868/4326/3702 5913/4325/3701 -f 5894/8819/6419 5892/8793/6417 5921/8820/3724 -f 5873/4332/3689 5914/4340/3714 5915/4333/3708 -f 5890/4322/3698 5889/4320/3696 5920/4345/3717 -f 5921/8820/3724 5866/4324/3700 5894/8819/6419 -f 5879/8817/6409 5877/8818/6408 5917/8816/3719 -f 5920/4345/3717 5892/8793/6417 5890/4322/3698 -f 5920/4345/3717 5887/4331/3707 5919/4330/3706 -f 5926/8821/3723 5925/8822/3718 5923/8823/3722 -f 5961/8824/6403 5931/8825/3700 5933/8826/3702 -f 5916/4343/3712 5924/4342/3715 5925/8827/3718 -f 5921/8820/3724 5920/4345/3717 5929/4349/3720 -f 5917/4348/3719 5925/4347/3718 5926/4353/3723 -f 5914/4357/3714 5913/4350/3701 5923/4352/3722 -f 5913/4350/3701 5921/4355/3724 5922/4351/3721 -f 5918/4336/3710 5926/8821/3723 5927/4359/3725 -f 5915/4341/3708 5914/8828/3714 5924/4342/3715 -f 5962/8829/6407 5933/8826/3702 5935/8830/3713 -f 5963/4362/3691 5935/8831/3713 5938/4360/3689 -f 5965/4363/3692 5938/4360/3689 5940/4364/3693 -f 5942/8832/6434 5966/4365/3726 5940/4364/3693 -f 5944/8833/6409 5967/8834/6410 5942/8835/6434 -f 5946/8836/3709 5968/8837/6411 5944/8833/6409 -f 5948/8838/3711 5969/8839/6412 5946/8836/3709 -f 5950/8840/3705 5970/8841/6413 5948/8838/3711 -f 5952/4378/3707 5971/8842/6414 5950/4376/3705 -f 5954/4367/3696 5972/8843/6415 5952/4378/3707 -f 5976/8844/6416 5955/4369/3698 5957/8845/6417 -f 5954/4367/3696 5955/4369/3698 5974/4368/3697 -f 5977/8846/6418 5957/8847/6417 5959/8848/6419 -f 5960/8849/6420 5959/8848/6419 5931/8825/3700 -f 5941/8850/6421 5936/8851/6422 5939/4375/3704 -f 5930/8852/6423 5977/8846/6418 5960/8849/6420 -f 5958/8853/6424 5976/8854/6416 5977/8846/6418 -f 5956/8855/6425 5975/4370/3699 5976/8844/6416 -f 5974/4368/3697 5951/8856/6426 5973/4366/3695 -f 5974/4368/3697 5975/4370/3699 5953/8857/6427 -f 5972/8843/6415 5949/8858/6428 5971/8842/6414 -f 5951/8856/6426 5972/8843/6415 5973/4366/3695 -f 5971/8859/6414 5947/8860/6429 5970/8841/6413 -f 5970/8841/6413 5945/8861/6430 5969/8839/6412 -f 5969/8839/6412 5943/8862/6431 5968/8837/6411 -f 5968/8837/6411 5941/8863/6421 5967/8834/6410 -f 5967/8864/6410 5939/4375/3704 5966/4365/3726 -f 5937/4374/3703 5963/4362/3691 5964/4361/3690 -f 5939/4375/3704 5965/4363/3692 5966/4365/3726 -f 5964/4361/3690 5965/4363/3692 5937/4374/3703 -f 5936/8851/6422 5962/8865/6407 5963/4362/3691 -f 5934/8866/6432 5961/8824/6403 5962/8829/6407 -f 5932/8867/6433 5960/8849/6420 5961/8824/6403 -f 5984/4377/3706 5948/4384/3711 5983/4383/3710 -f 5984/4377/3706 5992/4406/3725 5993/4391/3716 -f 5982/8868/3719 5946/4382/3709 5944/8869/6409 -f 5981/4385/3712 5942/8870/6434 5940/4381/3693 -f 5979/4387/3714 5933/4373/3702 5978/4372/3701 -f 5959/8871/6419 5957/8845/6417 5986/8872/3724 -f 5938/4379/3689 5979/4387/3714 5980/4380/3708 -f 5955/4369/3698 5954/4367/3696 5985/4392/3717 -f 5986/8872/3724 5931/4371/3700 5959/8871/6419 -f 5944/8869/6409 5942/8870/6434 5982/8868/3719 -f 5985/4392/3717 5957/8845/6417 5955/4369/3698 -f 5985/4392/3717 5952/4378/3707 5984/4377/3706 -f 5991/8873/3723 5990/8874/3718 5988/8875/3722 -f 6026/8876/6403 5996/8877/3700 5998/8878/3702 -f 5981/4390/3712 5989/4389/3715 5990/8879/3718 -f 5986/8872/3724 5985/4392/3717 5994/4396/3720 -f 5982/4395/3719 5990/4394/3718 5991/4400/3723 -f 5979/4404/3714 5978/4397/3701 5988/4399/3722 -f 5978/4397/3701 5986/4402/3724 5987/4398/3721 -f 5983/4383/3710 5991/8873/3723 5992/4406/3725 -f 5980/4388/3708 5979/8880/3714 5989/4389/3715 -f 6027/8881/6407 5998/8878/3702 6000/8882/3713 -f 6028/4409/3691 6000/8883/3713 6003/4407/3689 -f 6030/4410/3692 6003/4407/3689 6005/4411/3693 -f 6007/8884/6434 6031/4412/3726 6005/4411/3693 -f 6009/8885/6409 6032/8886/6410 6007/8887/6434 -f 6011/8888/3709 6033/8889/6411 6009/8885/6409 -f 6013/8890/3711 6034/8891/6412 6011/8888/3709 -f 6015/8892/3705 6035/8893/6413 6013/8890/3711 -f 6017/4425/3707 6036/8894/6414 6015/4423/3705 -f 6019/4414/3696 6037/8895/6415 6017/4425/3707 -f 6041/8896/6416 6020/4416/3698 6022/8897/6417 -f 6019/4414/3696 6020/4416/3698 6039/4415/3697 -f 6042/8898/6418 6022/8899/6417 6024/8900/6419 -f 6025/8901/6420 6024/8900/6419 5996/8877/3700 -f 6006/8902/6421 6001/8903/6422 6004/4422/3704 -f 5995/8904/6423 6042/8898/6418 6025/8901/6420 -f 6023/8905/6424 6041/8906/6416 6042/8898/6418 -f 6021/8907/6425 6040/4417/3699 6041/8896/6416 -f 6039/4415/3697 6016/8908/6426 6038/4413/3695 -f 6039/4415/3697 6040/4417/3699 6018/8909/6427 -f 6037/8895/6415 6014/8910/6428 6036/8894/6414 -f 6016/8908/6426 6037/8895/6415 6038/4413/3695 -f 6036/8911/6414 6012/8912/6429 6035/8893/6413 -f 6035/8893/6413 6010/8913/6430 6034/8891/6412 -f 6034/8891/6412 6008/8914/6431 6033/8889/6411 -f 6033/8889/6411 6006/8915/6421 6032/8886/6410 -f 6032/8916/6410 6004/4422/3704 6031/4412/3726 -f 6002/4421/3703 6028/4409/3691 6029/4408/3690 -f 6004/4422/3704 6030/4410/3692 6031/4412/3726 -f 6029/4408/3690 6030/4410/3692 6002/4421/3703 -f 6001/8903/6422 6027/8917/6407 6028/4409/3691 -f 5999/8918/6435 6026/8876/6403 6027/8881/6407 -f 5997/8919/6433 6025/8901/6420 6026/8876/6403 -f 6049/4424/3706 6013/4431/3711 6048/4430/3710 -f 6049/4424/3706 6057/4453/3725 6058/4438/3716 -f 6047/8920/3719 6011/4429/3709 6009/8921/6409 -f 6046/4432/3712 6007/8922/6434 6005/4428/3693 -f 6044/4434/3714 5998/4420/3702 6043/4419/3701 -f 6024/8923/6419 6022/8897/6417 6051/8924/3724 -f 6003/4426/3689 6044/4434/3714 6045/4427/3708 -f 6020/4416/3698 6019/4414/3696 6050/4439/3717 -f 6051/8924/3724 5996/4418/3700 6024/8923/6419 -f 6009/8921/6409 6007/8922/6434 6047/8920/3719 -f 6050/4439/3717 6022/8897/6417 6020/4416/3698 -f 6050/4439/3717 6017/4425/3707 6049/4424/3706 -f 6056/8925/3723 6055/8926/3718 6053/8927/3722 -f 6091/8928/6403 6061/8929/3700 6063/8930/3702 -f 6046/4437/3712 6054/4436/3715 6055/8931/3718 -f 6051/8924/3724 6050/4439/3717 6059/4443/3720 -f 6047/4442/3719 6055/4441/3718 6056/4447/3723 -f 6044/4451/3714 6043/4444/3701 6053/4446/3722 -f 6043/4444/3701 6051/4449/3724 6052/4445/3721 -f 6048/4430/3710 6056/8925/3723 6057/4453/3725 -f 6045/4435/3708 6044/8932/3714 6054/4436/3715 -f 6092/8933/6436 6063/8930/3702 6065/8934/3713 -f 6093/4456/3691 6065/8935/3713 6068/4454/3689 -f 6095/4457/3692 6068/4454/3689 6070/4458/3693 -f 6072/8936/6434 6096/4459/3726 6070/4458/3693 -f 6074/8937/6409 6097/8938/6410 6072/8939/6434 -f 6076/8940/3709 6098/8941/6411 6074/8937/6409 -f 6078/8942/3711 6099/8943/6412 6076/8940/3709 -f 6080/8944/3705 6100/8945/6413 6078/8942/3711 -f 6082/4472/3707 6101/8946/6414 6080/4470/3705 -f 6084/4461/3696 6102/8947/6415 6082/4472/3707 -f 6106/8948/6416 6085/4463/3698 6087/8949/6417 -f 6084/4461/3696 6085/4463/3698 6104/4462/3697 -f 6107/8950/6418 6087/8951/6417 6089/8952/6419 -f 6090/8953/6420 6089/8952/6419 6061/8929/3700 -f 6071/8954/6421 6066/8955/6422 6069/4469/3704 -f 6060/8956/6423 6107/8950/6418 6090/8953/6420 -f 6088/8957/6424 6106/8958/6416 6107/8950/6418 -f 6086/8959/6425 6105/4464/3699 6106/8948/6416 -f 6104/4462/3697 6081/8960/6426 6103/4460/3695 -f 6104/4462/3697 6105/4464/3699 6083/8961/6427 -f 6102/8947/6415 6079/8962/6428 6101/8946/6414 -f 6081/8960/6426 6102/8947/6415 6103/4460/3695 -f 6101/8963/6414 6077/8964/6429 6100/8945/6413 -f 6100/8945/6413 6075/8965/6430 6099/8943/6412 -f 6099/8943/6412 6073/8966/6431 6098/8941/6411 -f 6098/8941/6411 6071/8967/6421 6097/8938/6410 -f 6097/8968/6410 6069/4469/3704 6096/4459/3726 -f 6067/4468/3703 6093/4456/3691 6094/4455/3690 -f 6069/4469/3704 6095/4457/3692 6096/4459/3726 -f 6094/4455/3690 6095/4457/3692 6067/4468/3703 -f 6066/8955/6422 6092/8969/6436 6093/4456/3691 -f 6064/8970/6435 6091/8928/6403 6092/8933/6436 -f 6062/8971/6433 6090/8953/6420 6091/8928/6403 -f 6114/4471/3706 6078/4478/3711 6113/4477/3710 -f 6114/4471/3706 6122/4500/3725 6123/4485/3716 -f 6112/8972/3719 6076/4476/3709 6074/8973/6409 -f 6111/4479/3712 6072/8974/6434 6070/4475/3693 -f 6109/4481/3714 6063/4467/3702 6108/4466/3701 -f 6089/8975/6419 6087/8949/6417 6116/8976/3724 -f 6068/4473/3689 6109/4481/3714 6110/4474/3708 -f 6085/4463/3698 6084/4461/3696 6115/4486/3717 -f 6116/8976/3724 6061/4465/3700 6089/8975/6419 -f 6074/8973/6409 6072/8974/6434 6112/8972/3719 -f 6115/4486/3717 6087/8949/6417 6085/4463/3698 -f 6115/4486/3717 6082/4472/3707 6114/4471/3706 -f 6121/8977/3723 6120/8978/3718 6118/8979/3722 -f 6128/8980/6437 6125/8981/6438 6126/8982/6439 -f 6111/4484/3712 6119/4483/3715 6120/8983/3718 -f 6116/8976/3724 6115/4486/3717 6124/4490/3720 -f 6112/4489/3719 6120/4488/3718 6121/4494/3723 -f 6109/4498/3714 6108/4491/3701 6118/4493/3722 -f 6108/4491/3701 6116/4496/3724 6117/4492/3721 -f 6113/4477/3710 6121/8977/3723 6122/4500/3725 -f 6110/4482/3708 6109/8984/3714 6119/4483/3715 -f 6130/8985/6440 6127/8986/6441 6128/8980/6437 -f 6132/8987/6442 6129/8988/6443 6130/8985/6440 -f 6134/8989/6444 6131/8990/6445 6132/8991/6442 -f 6135/8992/6446 6134/8993/6444 6136/8994/6447 -f 6137/8995/6448 6136/8994/6447 6138/8996/6449 -f 6139/8997/6450 6138/8996/6449 6140/8998/6451 -f 6125/8999/6438 6140/8998/6451 6126/9000/6439 -f 6130/9001/6452 6128/9002/6452 6138/9003/6452 -f 6137/9004/6453 6139/9005/6453 6127/9006/6453 -f 6141/9007/6454 6166/9008/6455 6165/9009/6456 -f 6142/9010/6457 6167/9011/3738 6166/9008/6455 -f 6172/4518/3742 6149/5102/4268 6173/9012/6458 -f 6168/4502/3728 6167/4513/3738 6143/4503/3729 -f 6144/4506/3727 6169/4505/3731 6168/9013/3728 -f 6146/4507/3732 6170/4509/3734 6169/4505/3731 -f 6147/4508/3733 6171/4511/3736 6170/4509/3734 -f 6148/4510/3735 6172/4518/3742 6171/4511/3736 -f 6173/9012/6458 6150/5104/4270 6174/9014/6459 -f 6174/9014/6459 6151/5106/4272 6175/9015/6460 -f 6175/9015/6460 6152/5108/4274 6176/9016/6461 -f 6176/9016/6461 6153/5110/4276 6177/9017/6462 -f 6177/9017/6462 6154/5112/4278 6178/9018/6463 -f 6178/9018/6463 6155/5114/4280 6179/9019/6464 -f 6179/9019/6464 6156/5116/4282 6180/9020/3746 -f 6180/9020/3746 6157/5118/4284 6181/9021/3744 -f 6181/9021/3744 6158/5119/4285 6182/9022/6465 -f 6182/9022/6465 6159/5121/4287 6183/9023/3749 -f 6183/4525/3749 6160/9024/4289 6184/4523/3747 -f 6184/4523/3747 6161/9025/4290 6185/4528/3752 -f 6185/4528/3752 6162/9026/6466 6186/4526/3750 -f 6186/4526/3750 6163/9027/6467 6187/4529/3753 -f 6187/4529/3753 6164/9028/6468 6188/4534/3757 -f 6165/9009/6456 6190/9029/6469 6189/9030/6470 -f 6166/9031/6455 6191/4512/3737 6190/9032/6469 -f 6195/4519/3743 6173/9012/6458 6196/9033/6471 -f 6168/4502/3728 6192/4515/3739 6191/4512/3737 -f 6169/4505/3731 6193/4516/3740 6192/4531/3739 -f 6170/4509/3734 6194/4517/3741 6193/4516/3740 -f 6171/4511/3736 6195/4519/3743 6194/4517/3741 -f 6196/9033/6471 6174/9014/6459 6197/9034/6472 -f 6197/9034/6472 6175/9015/6460 6198/9035/6473 -f 6198/9035/6473 6176/9016/6461 6199/9036/6474 -f 6199/9036/6474 6177/9017/6462 6200/9037/6475 -f 6200/9037/6475 6178/9018/6463 6201/9038/6476 -f 6201/9038/6476 6179/9019/6464 6202/9039/6477 -f 6202/9039/6477 6180/9020/3746 6203/9040/3745 -f 6203/4521/3745 6182/9041/6465 6204/9042/6478 -f 6204/9042/6478 6183/4525/3749 6205/4524/3748 -f 6205/4524/3748 6185/4528/3752 6206/4527/3751 -f 6189/9030/6470 6208/9043/6479 6207/9044/6480 -f 6190/9032/6469 6209/9045/6481 6208/9046/6479 -f 6209/9045/6481 6192/4515/3739 6210/4538/3760 -f 6214/9047/6482 6196/9033/6471 6215/9048/6483 -f 6192/4515/3739 6211/4536/3754 6210/4538/3760 -f 6193/4516/3740 6212/4532/3755 6211/4530/3754 -f 6194/4517/3741 6213/4533/3756 6212/4532/3755 -f 6195/4519/3743 6214/9047/6482 6213/4533/3756 -f 6215/9048/6483 6197/9034/6472 6216/9049/6484 -f 6216/9049/6484 6198/9035/6473 6217/9050/6485 -f 6217/9050/6485 6199/9036/6474 6218/9051/6486 -f 6218/9051/6486 6200/9037/6475 6219/9052/6487 -f 6219/9052/6487 6201/9038/6476 6220/9053/6488 -f 6220/9054/6488 6202/9055/6477 6221/9056/6489 -f 6221/9056/6489 6203/4521/3745 6222/9057/6490 -f 6222/9057/6490 6204/9042/6478 6223/9058/6491 -f 6223/9058/6491 6205/4524/3748 6224/9059/6492 -f 6224/9059/6492 6206/4527/3751 6225/4542/3764 -f 6225/4542/3764 6187/4529/3753 6226/4535/3758 -f 6207/9044/6480 6228/9060/6493 6227/9061/6494 -f 6208/9046/6479 6229/9062/6495 6228/9063/6493 -f 6229/9062/6495 6210/4538/3760 6230/4537/3759 -f 6232/4540/3762 6214/9047/6482 6233/9064/6496 -f 6211/4530/3754 6231/4539/3761 6230/9065/3759 -f 6212/4532/3755 6232/4540/3762 6231/4539/3761 -f 6233/9064/6496 6215/9048/6483 6234/9066/6497 -f 6234/9066/6497 6216/9049/6484 6235/9067/6498 -f 6235/9067/6498 6217/9050/6485 6236/9068/6499 -f 6236/9068/6499 6218/9051/6486 6237/9069/6500 -f 6237/9069/6500 6219/9052/6487 6238/9070/6501 -f 6238/9071/6501 6220/9054/6488 6239/9072/6502 -f 6239/9072/6502 6221/9056/6489 6240/9073/6503 -f 6240/9073/6503 6222/9057/6490 6241/9074/6504 -f 6241/9074/6504 6223/9058/6491 6242/9075/6505 -f 6242/9075/6505 6224/9059/6492 6243/9076/6506 -f 6243/9076/6506 6225/4542/3764 6244/4541/3763 -f 6227/9061/6494 6247/4547/3769 6246/4546/3768 -f 6188/4534/3757 6245/4543/3765 6226/4535/3758 -f 6228/9063/6493 6248/4550/3771 6247/4549/3769 -f 6248/4550/3771 6230/4537/3759 6249/4551/3772 -f 6249/4555/3772 6231/4539/3761 6250/4553/3774 -f 6250/4553/3774 6232/4540/3762 6251/4556/3776 -f 6251/4556/3776 6233/9064/6496 6252/4558/3778 -f 6252/4558/3778 6234/9066/6497 6253/4560/3780 -f 6253/4560/3780 6235/9067/6498 6254/4562/3782 -f 6254/4562/3782 6236/9068/6499 6255/4564/3784 -f 6255/4564/3784 6237/9069/6500 6256/4566/3786 -f 6256/4566/3786 6238/9070/6501 6257/9077/3788 -f 6257/4568/3788 6239/9072/6502 6258/4571/3790 -f 6258/4571/3790 6240/9073/6503 6259/4573/3792 -f 6259/4573/3792 6241/9074/6504 6260/4575/3794 -f 6260/4575/3794 6242/9075/6505 6261/4577/3796 -f 6261/4577/3796 6243/9076/6506 6262/4579/3798 -f 6262/4579/3798 6244/4541/3763 6263/4582/3801 -f 6263/4582/3801 6226/4535/3758 6264/4544/3766 -f 6281/4601/3817 6263/4582/3801 6282/4581/3800 -f 6266/4548/3770 6265/4584/3767 6247/4549/3769 -f 6267/4552/3773 6266/4548/3770 6248/4550/3771 -f 6249/4551/3772 6268/4586/3775 6267/4552/3773 -f 6250/4553/3774 6269/4557/3777 6268/4554/3775 -f 6251/4556/3776 6270/4559/3779 6269/4557/3777 -f 6252/4558/3778 6271/4561/3781 6270/4559/3779 -f 6253/4560/3780 6272/4563/3783 6271/4561/3781 -f 6254/4562/3782 6273/4565/3785 6272/4563/3783 -f 6255/4564/3784 6274/4567/3787 6273/4565/3785 -f 6256/4566/3786 6275/9078/3789 6274/4567/3787 -f 6257/4568/3788 6276/4572/3791 6275/4569/3789 -f 6258/4571/3790 6277/4574/3793 6276/4572/3791 -f 6259/4573/3792 6278/4576/3795 6277/4574/3793 -f 6260/4575/3794 6279/4578/3797 6278/4576/3795 -f 6261/4577/3796 6280/4580/3799 6279/4578/3797 -f 6262/4579/3798 6281/4601/3817 6280/4580/3799 -f 6300/9079/6507 6282/4581/3800 6301/9080/6508 -f 6284/9081/3802 6283/9082/6509 6265/4545/3767 -f 6285/4585/3803 6284/4583/3802 6266/4548/3770 -f 6267/4552/3773 6286/4587/3804 6285/4585/3803 -f 6268/4586/3775 6287/9083/3805 6286/4587/3804 -f 6269/4557/3777 6288/4589/3806 6287/4588/3805 -f 6270/4559/3779 6289/4590/3807 6288/4589/3806 -f 6271/4561/3781 6290/4591/3808 6289/4590/3807 -f 6272/4563/3783 6291/4592/3809 6290/4591/3808 -f 6273/4565/3785 6292/4593/3810 6291/4592/3809 -f 6274/4567/3787 6293/9084/3811 6292/4593/3810 -f 6275/4569/3789 6294/4596/3812 6293/4594/3811 -f 6276/4572/3791 6295/4597/3813 6294/4596/3812 -f 6277/4574/3793 6296/4598/3814 6295/4597/3813 -f 6278/4576/3795 6297/4599/3815 6296/4598/3814 -f 6279/4578/3797 6298/4600/3816 6297/4599/3815 -f 6280/4580/3799 6299/4602/3818 6298/4600/3816 -f 6281/4601/3817 6300/9079/6507 6299/4602/3818 -f 6301/9080/6508 6264/4544/3766 6302/4603/3819 -f 6303/4606/3822 6284/9081/3802 6304/4604/3820 -f 6304/4609/3820 6285/4585/3803 6305/4607/3823 -f 6305/4607/3823 6286/4587/3804 6306/4610/3825 -f 6306/4610/3825 6287/9083/3805 6307/4612/3827 -f 6307/4614/3827 6288/4589/3806 6308/4615/3829 -f 6288/4589/3806 6309/4617/3831 6308/4615/3829 -f 6290/4591/3808 6309/4617/3831 6289/4590/3807 -f 6290/4591/3808 6311/9085/6510 6310/9086/6511 -f 6291/4592/3809 6312/9087/6512 6311/9085/6510 -f 6292/4593/3810 6313/9088/6513 6312/9087/6512 -f 6293/4594/3811 6314/9089/6514 6313/9090/6513 -f 6294/4596/3812 6315/9091/6515 6314/9089/6514 -f 6295/4597/3813 6316/9092/6516 6315/9091/6515 -f 6296/4598/3814 6317/9093/6517 6316/9092/6516 -f 6297/4599/3815 6318/9094/6518 6317/9093/6517 -f 6298/4600/3816 6319/9095/6519 6318/9094/6518 -f 6299/4602/3818 6320/4619/3833 6319/9095/6519 -f 6300/9079/6507 6321/4620/3834 6320/4619/3833 -f 6309/4617/3831 6328/9096/6520 6327/4626/3839 -f 6302/4603/3819 6321/4620/3834 6301/9080/6508 -f 6304/4609/3820 6323/4608/3824 6322/9097/3821 -f 6305/4607/3823 6324/4611/3826 6323/4608/3824 -f 6325/4623/3828 6324/4611/3826 6307/4612/3827 -f 6326/4616/3830 6325/4613/3828 6308/4615/3829 -f 6327/4626/3839 6326/4616/3830 6309/4617/3831 -f 6310/9086/6511 6329/9098/6521 6328/9096/6520 -f 6311/9085/6510 6330/9099/6522 6329/9098/6521 -f 6312/9087/6512 6331/9100/6523 6330/9099/6522 -f 6313/9090/6513 6332/9101/6524 6331/9102/6523 -f 6314/9089/6514 6333/9103/6525 6332/9101/6524 -f 6315/9091/6515 6334/9104/6526 6333/9103/6525 -f 6316/9092/6516 6335/9105/6527 6334/9104/6526 -f 6317/9093/6517 6336/9106/6528 6335/9105/6527 -f 6318/9094/6518 6337/9107/6529 6336/9106/6528 -f 6319/9095/6519 6338/4618/3832 6337/9107/6529 -f 6340/9108/6530 6323/9109/3824 6341/9110/6531 -f 6339/4621/3835 6338/4618/3832 6321/4620/3834 -f 6245/4543/3765 6339/4621/3835 6302/4603/3819 -f 6341/9111/6531 6324/4611/3826 6342/9112/6532 -f 6327/4626/3839 6346/4629/3842 6345/4625/3838 -f 6343/4622/3836 6342/9112/6532 6324/4611/3826 -f 6344/4624/3837 6343/9113/3836 6325/4613/3828 -f 6345/4625/3838 6344/4624/3837 6326/4616/3830 -f 6328/9096/6520 6347/4631/3844 6346/4629/3842 -f 6329/9098/6521 6348/4633/3846 6347/4631/3844 -f 6330/9099/6522 6349/4635/3848 6348/4633/3846 -f 6331/9100/6523 6350/4637/3850 6349/4635/3848 -f 6332/9101/6524 6351/4640/3852 6350/4639/3850 -f 6333/9103/6525 6352/4642/3854 6351/4640/3852 -f 6334/9104/6526 6353/4644/3856 6352/4642/3854 -f 6335/9105/6527 6354/4646/3858 6353/4644/3856 -f 6336/9106/6528 6355/4648/3860 6354/4646/3858 -f 6337/9107/6529 6356/4650/3862 6355/4648/3860 -f 6245/4543/3765 6358/9114/6533 6339/4621/3835 -f 6357/4627/3840 6356/4650/3862 6338/4618/3832 -f 6358/9114/6533 6357/4627/3840 6339/4621/3835 -f 6360/4653/3865 6341/9110/6531 6361/4651/3863 -f 6361/4656/3863 6342/9112/6532 6362/4654/3866 -f 6342/9112/6532 6363/4658/3869 6362/4654/3866 -f 6343/9113/3836 6364/4661/3871 6363/4660/3869 -f 6344/4624/3837 6365/4663/3873 6364/4661/3871 -f 6356/4650/3862 6377/9115/6534 6376/4649/3861 -f 6366/4628/3841 6365/4663/3873 6345/4625/3838 -f 6367/4630/3843 6366/4628/3841 6346/4629/3842 -f 6368/4632/3845 6367/4630/3843 6347/4631/3844 -f 6369/4634/3847 6368/4632/3845 6348/4633/3846 -f 6370/4636/3849 6369/4634/3847 6349/4635/3848 -f 6371/9116/3851 6370/4636/3849 6350/4637/3850 -f 6372/4641/3853 6371/4638/3851 6351/4640/3852 -f 6373/4643/3855 6372/4641/3853 6352/4642/3854 -f 6374/4645/3857 6373/4643/3855 6353/4644/3856 -f 6375/4647/3859 6374/4645/3857 6354/4646/3858 -f 6376/4649/3861 6375/4647/3859 6355/4648/3860 -f 6366/4628/3841 6385/4671/3880 6384/4664/3874 -f 6358/9114/6533 6377/9115/6534 6357/4627/3840 -f 6360/4653/3865 6379/4652/3864 6378/9117/6535 -f 6361/4656/3863 6380/4655/3867 6379/9118/3864 -f 6381/4657/3868 6380/4655/3867 6362/4654/3866 -f 6382/4659/3870 6381/4667/3868 6363/4660/3869 -f 6383/4662/3872 6382/4659/3870 6364/4661/3871 -f 6384/4664/3874 6383/4662/3872 6365/4663/3873 -f 6367/4630/3843 6386/4673/3882 6385/4671/3880 -f 6368/4632/3845 6387/4675/3884 6386/4673/3882 -f 6369/4634/3847 6388/4677/3886 6387/4675/3884 -f 6370/4636/3849 6389/4679/3888 6388/4677/3886 -f 6371/9116/3851 6390/4681/3890 6389/4679/3888 -f 6372/4641/3853 6391/4685/3892 6390/9119/3890 -f 6373/4643/3855 6392/4686/3894 6391/4685/3892 -f 6374/4645/3857 6393/4688/3896 6392/4686/3894 -f 6375/4647/3859 6394/4690/3898 6393/4688/3896 -f 6376/4649/3861 6395/9120/6536 6394/4690/3898 -f 6377/9115/6534 6396/9121/6537 6395/9120/6536 -f 6358/9114/6533 6397/4725/3930 6396/9121/6537 -f 6398/9122/6538 6379/4652/3864 6399/9123/6539 -f 6399/9124/6539 6380/4655/3867 6400/4665/3875 -f 6394/4690/3898 6416/9125/6540 6415/4708/3914 -f 6401/9126/3900 6400/4665/3875 6381/4657/3868 -f 6402/4666/3876 6401/4692/3900 6381/4667/3868 -f 6403/4668/3877 6402/4666/3876 6382/4659/3870 -f 6404/4669/3878 6403/4668/3877 6383/4662/3872 -f 6405/4670/3879 6404/4669/3878 6384/4664/3874 -f 6406/4672/3881 6405/4670/3879 6385/4671/3880 -f 6407/4674/3883 6406/4672/3881 6386/4673/3882 -f 6408/4676/3885 6407/4674/3883 6387/4675/3884 -f 6409/4678/3887 6408/4676/3885 6388/4677/3886 -f 6410/4680/3889 6409/4678/3887 6389/4679/3888 -f 6411/4682/3891 6410/4680/3889 6390/4681/3890 -f 6412/4703/3893 6411/4682/3891 6391/4683/3892 -f 6413/4687/3895 6412/4684/3893 6392/4686/3894 -f 6414/4689/3897 6413/4687/3895 6393/4688/3896 -f 6415/4708/3914 6414/4689/3897 6394/4690/3898 -f 6417/9127/6541 6399/9123/6539 6418/9128/6542 -f 6396/9121/6537 6416/9125/6540 6395/9120/6536 -f 6418/9128/6542 6400/9129/3875 6419/9130/3917 -f 6400/4665/3875 6421/4713/3899 6420/4709/3915 -f 6400/4665/3875 6420/4709/3915 6419/4711/3917 -f 6415/4708/3914 6437/4719/3924 6436/4718/3923 -f 6422/4693/3901 6421/4691/3899 6402/4666/3876 -f 6423/4694/3902 6422/4693/3901 6403/4668/3877 -f 6424/4695/3903 6423/4694/3902 6404/4669/3878 -f 6425/4696/3904 6424/4695/3903 6405/4670/3879 -f 6426/4697/3905 6425/4696/3904 6406/4672/3881 -f 6427/4698/3906 6426/4697/3905 6407/4674/3883 -f 6428/4699/3907 6427/4698/3906 6408/4676/3885 -f 6429/4700/3908 6428/4699/3907 6409/4678/3887 -f 6430/4701/3909 6429/4700/3908 6410/4680/3889 -f 6431/4702/3910 6430/4701/3909 6411/4682/3891 -f 6432/9131/6543 6431/4702/3910 6412/4703/3893 -f 6433/4704/3911 6432/9131/6543 6412/4703/3893 -f 6434/9132/3912 6433/4704/3911 6413/4705/3895 -f 6435/4707/3913 6434/4706/3912 6414/4689/3897 -f 6436/4718/3923 6435/4707/3913 6415/4708/3914 -f 6416/9125/6540 6439/4723/3928 6438/4721/3926 -f 6438/4721/3926 6437/4719/3924 6416/9125/6540 -f 6440/9133/6544 6418/9128/6542 6441/9134/6545 -f 6397/4725/3930 6439/4723/3928 6396/9121/6537 -f 6441/9134/6545 6419/9130/3917 6442/9135/3916 -f 6424/4695/3903 6448/9136/6546 6447/4716/3921 -f 6443/4712/3918 6442/4710/3916 6420/4709/3915 -f 6444/4731/3933 6443/4730/3918 6421/4691/3899 -f 6445/4714/3919 6444/4731/3933 6421/4691/3899 -f 6446/4715/3920 6445/4714/3919 6422/4693/3901 -f 6447/4716/3921 6446/4715/3920 6423/4694/3902 -f 6425/4696/3904 6449/9137/6547 6448/9136/6546 -f 6426/4697/3905 6450/9138/6548 6449/9137/6547 -f 6427/4698/3906 6451/9139/6549 6450/9138/6548 -f 6428/4699/3907 6452/9140/6550 6451/9139/6549 -f 6429/4700/3908 6453/9141/6551 6452/9140/6550 -f 6430/4701/3909 6454/9142/6552 6453/9141/6551 -f 6431/4702/3910 6455/9143/6553 6454/9142/6552 -f 6432/9131/6543 6456/9144/6554 6455/9143/6553 -f 6433/4704/3911 6457/9145/6555 6456/9144/6554 -f 6434/9132/3912 6458/9146/6556 6457/9145/6555 -f 6435/9147/3913 6459/9148/3922 6458/9146/6556 -f 6463/9149/6557 6441/9134/6545 6464/9150/6558 -f 6460/4720/3925 6459/4717/3922 6437/4719/3924 -f 6461/4722/3927 6460/4720/3925 6438/4721/3926 -f 6462/4724/3929 6461/4722/3927 6439/4723/3928 -f 6464/9150/6558 6442/9135/3916 6465/9151/3938 -f 6447/4716/3921 6472/9152/6559 6471/4742/3943 -f 6442/4727/3916 6466/4726/3931 6465/4736/3938 -f 6467/4729/3932 6466/4738/3931 6443/4730/3918 -f 6468/4732/3934 6467/4729/3932 6444/4731/3933 -f 6469/4733/3935 6468/4732/3934 6445/4714/3919 -f 6470/4734/3936 6469/4733/3935 6446/4715/3920 -f 6471/4742/3943 6470/4734/3936 6447/4716/3921 -f 6448/9136/6546 6473/9153/6560 6472/9152/6559 -f 6449/9137/6547 6474/9154/6561 6473/9153/6560 -f 6450/9138/6548 6475/9155/6562 6474/9154/6561 -f 6451/9139/6549 6476/9156/6563 6475/9155/6562 -f 6452/9140/6550 6477/9157/6564 6476/9156/6563 -f 6453/9141/6551 6478/9158/6565 6477/9157/6564 -f 6454/9142/6552 6479/9159/6566 6478/9158/6565 -f 6455/9143/6553 6480/9160/6567 6479/9159/6566 -f 6456/9144/6554 6481/9161/6568 6480/9160/6567 -f 6457/9145/6555 6482/9162/6569 6481/9161/6568 -f 6458/9146/6556 6483/4744/3945 6482/9162/6569 -f 6459/9148/3922 6484/4745/3946 6483/4744/3945 -f 6460/9163/3925 6485/4747/3948 6484/4745/3946 -f 6486/9164/6570 6464/9150/6558 6487/9165/6571 -f 6462/4749/3929 6485/4747/3948 6461/9166/3927 -f 6487/9165/6571 6465/9151/3938 6488/9167/3937 -f 6471/4742/3943 6493/4755/3954 6492/4741/3942 -f 6489/4750/3939 6488/4735/3937 6466/4726/3931 -f 6490/4739/3940 6489/4737/3939 6468/4732/3934 -f 6491/4740/3941 6490/4739/3940 6469/4733/3935 -f 6492/4741/3942 6491/4740/3941 6470/4734/3936 -f 6472/9152/6559 6494/4757/3956 6493/4755/3954 -f 6473/9153/6560 6495/4759/3958 6494/4757/3956 -f 6474/9154/6561 6496/4761/3960 6495/4759/3958 -f 6475/9155/6562 6497/4763/3962 6496/4761/3960 -f 6476/9156/6563 6498/4765/3964 6497/4763/3962 -f 6477/9157/6564 6499/4767/3966 6498/4765/3964 -f 6478/9158/6565 6500/4769/3968 6499/4767/3966 -f 6479/9159/6566 6501/4771/3970 6500/4769/3968 -f 6480/9160/6567 6502/4772/3971 6501/4771/3970 -f 6481/9161/6568 6503/4774/3973 6502/4772/3971 -f 6482/9162/6569 6504/4776/3975 6503/4774/3973 -f 6486/9164/6570 6509/9168/6572 6508/9169/6573 -f 6505/4743/3944 6504/4776/3975 6483/4744/3945 -f 6506/4746/3947 6505/4743/3944 6484/4745/3946 -f 6507/4748/3949 6506/4746/3947 6485/4747/3948 -f 6487/9165/6571 6510/9170/6574 6509/9168/6572 -f 6527/4779/3978 6507/4748/3949 6528/9171/6575 -f 6511/4751/3950 6510/9172/6574 6488/4735/3937 -f 6489/4737/3939 6512/4752/3951 6511/9173/3950 -f 6490/4739/3940 6513/4753/3952 6512/4752/3951 -f 6491/4740/3941 6514/4754/3953 6513/4753/3952 -f 6492/4741/3942 6515/4756/3955 6514/4754/3953 -f 6493/4755/3954 6516/4758/3957 6515/4756/3955 -f 6494/4757/3956 6517/4760/3959 6516/4758/3957 -f 6495/4759/3958 6518/4762/3961 6517/4760/3959 -f 6496/4761/3960 6519/4764/3963 6518/4762/3961 -f 6497/4763/3962 6520/4766/3965 6519/4764/3963 -f 6498/4765/3964 6521/4768/3967 6520/4766/3965 -f 6499/4767/3966 6522/4770/3969 6521/4768/3967 -f 6501/4771/3970 6523/4773/3972 6522/4770/3969 -f 6502/4772/3971 6524/4775/3974 6523/4773/3972 -f 6503/4774/3973 6525/4777/3976 6524/4775/3974 -f 6504/4776/3975 6526/4778/3977 6525/4777/3976 -f 6505/4743/3944 6527/4779/3978 6526/4778/3977 -f 6528/9171/6575 6462/4749/3929 6529/9174/6576 -f 6508/9169/6573 6531/9175/6577 6530/9176/6578 -f 6509/9168/6572 6532/9177/3982 6531/9175/6577 -f 6510/9178/6574 6533/4784/3983 6532/4783/3982 -f 6533/4784/3983 6512/9179/3951 6534/4785/3984 -f 6534/4789/3984 6513/4753/3952 6535/4787/3986 -f 6535/4787/3986 6514/4754/3953 6536/4790/3988 -f 6536/4790/3988 6515/4756/3955 6537/4792/3990 -f 6537/4792/3990 6516/4758/3957 6538/4794/3992 -f 6538/4794/3992 6517/4760/3959 6539/4796/3994 -f 6539/4796/3994 6518/4762/3961 6540/4798/3996 -f 6540/4798/3996 6519/4764/3963 6541/4800/3998 -f 6541/4800/3998 6520/4766/3965 6542/4802/4000 -f 6542/4802/4000 6521/4768/3967 6543/4804/4002 -f 6543/4804/4002 6522/4770/3969 6544/4806/4004 -f 6544/4806/4004 6523/4773/3972 6545/4808/4006 -f 6545/4808/4006 6524/4775/3974 6546/4810/4008 -f 6546/4810/4008 6525/4777/3976 6547/4780/3979 -f 6549/4815/4011 6528/9180/6575 6550/4820/4015 -f 6526/4778/3977 6548/4781/3980 6547/4780/3979 -f 6527/9181/3978 6549/4815/4011 6548/4817/3980 -f 6550/4820/4015 6529/9182/6576 6551/4818/4013 -f 6530/9176/6578 6553/9183/6579 6552/9184/6580 -f 6531/9175/6577 6554/9185/3981 6553/9183/6579 -f 6568/4813/4010 6548/4817/3980 6569/4816/4012 -f 6533/4784/3983 6555/4786/3985 6554/4782/3981 -f 6534/4789/3984 6556/4788/3987 6555/9186/3985 -f 6535/4787/3986 6557/4791/3989 6556/4788/3987 -f 6536/4790/3988 6558/4793/3991 6557/4791/3989 -f 6537/4792/3990 6559/4795/3993 6558/4793/3991 -f 6538/4794/3992 6560/4797/3995 6559/4795/3993 -f 6539/4796/3994 6561/4799/3997 6560/4797/3995 -f 6540/4798/3996 6562/4801/3999 6561/4799/3997 -f 6541/4800/3998 6563/4803/4001 6562/4801/3999 -f 6542/4802/4000 6564/4805/4003 6563/4803/4001 -f 6543/4804/4002 6565/4807/4005 6564/4805/4003 -f 6544/4806/4004 6566/4809/4007 6565/4807/4005 -f 6545/4808/4006 6567/4811/4009 6566/4809/4007 -f 6546/4814/4008 6568/4813/4010 6567/9187/4009 -f 6569/4816/4012 6550/4820/4015 6570/4819/4014 -f 6552/9184/6580 6572/4824/4019 6571/4823/4018 -f 6553/9188/6579 6573/4827/4021 6572/4826/4019 -f 6573/4827/4021 6555/4786/3985 6574/4828/4022 -f 6574/4832/4022 6556/4788/3987 6575/4830/4024 -f 6575/4830/4024 6557/4791/3989 6576/4833/4026 -f 6576/4833/4026 6558/4793/3991 6577/4835/4028 -f 6577/4835/4028 6559/4795/3993 6578/4837/4030 -f 6578/4837/4030 6560/4797/3995 6579/4839/4032 -f 6579/4839/4032 6561/4799/3997 6580/4841/4034 -f 6580/4841/4034 6562/4801/3999 6581/4843/4036 -f 6581/4843/4036 6563/4803/4001 6582/4845/4038 -f 6582/4845/4038 6564/4805/4003 6583/4847/4040 -f 6583/4847/4040 6565/4807/4005 6584/9189/4042 -f 6584/9189/4042 6566/4809/4007 6585/9190/4044 -f 6585/4852/4044 6567/9187/4009 6586/4854/4046 -f 6586/4854/4046 6568/4813/4010 6587/4856/4048 -f 6587/4856/4048 6569/4816/4012 6588/4821/4016 -f 6605/9191/6581 6588/4821/4016 6606/4864/4055 -f 6590/9192/4020 6589/4822/4017 6572/4824/4019 -f 6591/4829/4023 6590/4825/4020 6573/4827/4021 -f 6574/4828/4022 6592/4858/4025 6591/4829/4023 -f 6575/4830/4024 6593/4834/4027 6592/4831/4025 -f 6576/4833/4026 6594/4836/4029 6593/4834/4027 -f 6577/4835/4028 6595/4838/4031 6594/4836/4029 -f 6578/4837/4030 6596/4840/4033 6595/4838/4031 -f 6579/4839/4032 6597/4842/4035 6596/4840/4033 -f 6580/4841/4034 6598/4844/4037 6597/4842/4035 -f 6581/4843/4036 6599/4846/4039 6598/4844/4037 -f 6582/4845/4038 6600/4848/4041 6599/4846/4039 -f 6583/4847/4040 6601/9193/4043 6600/4848/4041 -f 6584/4849/4042 6602/4853/4045 6601/4850/4043 -f 6585/4852/4044 6603/4855/4047 6602/4853/4045 -f 6586/4854/4046 6604/4857/4049 6603/4855/4047 -f 6587/4856/4048 6605/9191/6581 6604/4857/4049 -f 6606/4864/4055 6570/4819/4014 6607/4862/4053 -f 6607/4862/4053 6551/4818/4013 6608/4893/4082 -f 6589/4822/4017 6610/9194/6582 6609/9195/6583 -f 6590/4825/4020 6611/4859/4050 6610/9196/6582 -f 6614/4868/4058 6595/4838/4031 6615/4870/4060 -f 6592/4858/4025 6612/4865/4051 6611/4859/4050 -f 6593/4834/4027 6613/4861/4052 6612/4860/4051 -f 6594/4836/4029 6614/4868/4058 6613/4861/4052 -f 6615/4870/4060 6596/4840/4033 6616/4872/4062 -f 6616/4872/4062 6597/4842/4035 6617/4874/4064 -f 6617/4874/4064 6598/4844/4037 6618/4876/4066 -f 6618/4876/4066 6599/4846/4039 6619/4878/4068 -f 6619/4878/4068 6600/4848/4041 6620/9197/4070 -f 6620/4880/4070 6601/4850/4043 6621/4883/4072 -f 6621/4883/4072 6602/4853/4045 6622/4885/4074 -f 6622/4885/4074 6603/4855/4047 6623/4887/4076 -f 6623/4887/4076 6604/4857/4049 6624/4889/4078 -f 6624/4889/4078 6605/9191/6581 6625/4891/4080 -f 6625/4891/4080 6606/4864/4055 6626/4863/4054 -f 6609/9195/6583 6628/9198/6584 6627/9199/6585 -f 6610/9196/6582 6629/4896/4085 6628/9200/6584 -f 6643/4892/4081 6626/4863/4054 6644/4913/4100 -f 6611/4859/4050 6630/4866/4056 6629/4896/4085 -f 6612/4860/4051 6631/4867/4057 6630/4898/4056 -f 6613/4861/4052 6632/4869/4059 6631/4867/4057 -f 6614/4868/4058 6633/4871/4061 6632/4869/4059 -f 6615/4870/4060 6634/4873/4063 6633/4871/4061 -f 6616/4872/4062 6635/4875/4065 6634/4873/4063 -f 6617/4874/4064 6636/4877/4067 6635/4875/4065 -f 6618/4876/4066 6637/4879/4069 6636/4877/4067 -f 6619/4878/4068 6638/9201/4071 6637/4879/4069 -f 6620/4880/4070 6639/4884/4073 6638/4881/4071 -f 6621/4883/4072 6640/4886/4075 6639/4884/4073 -f 6622/4885/4074 6641/4888/4077 6640/4886/4075 -f 6623/4887/4076 6642/4890/4079 6641/4888/4077 -f 6624/4889/4078 6643/4892/4081 6642/4890/4079 -f 6644/4913/4100 6607/4862/4053 6645/4894/4083 -f 6627/9199/6585 6647/4916/4103 6646/4915/4102 -f 6628/9200/6584 6648/4895/4084 6647/4918/4103 -f 6662/4934/4118 6644/4913/4100 6663/4912/4099 -f 6630/4866/4056 6649/4920/4086 6648/4895/4084 -f 6631/4867/4057 6650/4899/4087 6649/4897/4086 -f 6632/4869/4059 6651/4900/4088 6650/4899/4087 -f 6633/4871/4061 6652/4901/4089 6651/4900/4088 -f 6634/4873/4063 6653/4902/4090 6652/4901/4089 -f 6635/4875/4065 6654/4903/4091 6653/4902/4090 -f 6636/4877/4067 6655/4904/4092 6654/4903/4091 -f 6637/4879/4069 6656/4927/4093 6655/4904/4092 -f 6638/4881/4071 6657/4907/4094 6656/4905/4093 -f 6639/4884/4073 6658/4908/4095 6657/4907/4094 -f 6640/4886/4075 6659/4909/4096 6658/4908/4095 -f 6641/4888/4077 6660/4910/4097 6659/4909/4096 -f 6642/4890/4079 6661/4911/4098 6660/4910/4097 -f 6643/4892/4081 6662/4934/4118 6661/4911/4098 -f 6681/4936/4120 6699/4978/4156 6680/4935/4119 -f 6665/4914/4101 6664/4938/4122 6646/4915/4102 -f 6666/4917/4104 6665/4940/4101 6647/4918/4103 -f 6667/4919/4105 6666/4917/4104 6648/4895/4084 -f 6668/4921/4106 6667/4943/4105 6649/4897/4086 -f 6650/4899/4087 6669/4922/4107 6668/4921/4106 -f 6651/4900/4088 6670/4923/4108 6669/4922/4107 -f 6652/4901/4089 6671/4924/4109 6670/4923/4108 -f 6653/4902/4090 6672/4925/4110 6671/4924/4109 -f 6654/4903/4091 6673/4926/4111 6672/4925/4110 -f 6655/4904/4092 6674/4928/4112 6673/4926/4111 -f 6656/4905/4093 6675/4929/4113 6674/4951/4112 -f 6657/4907/4094 6676/4930/4114 6675/4929/4113 -f 6658/4908/4095 6677/4931/4115 6676/4930/4114 -f 6659/4909/4096 6678/4932/4116 6677/4931/4115 -f 6660/4910/4097 6679/4933/4117 6678/4932/4116 -f 6661/4911/4098 6680/4935/4119 6679/4933/4117 -f 6663/4912/4099 6681/4936/4120 6680/4935/4119 -f 6664/4938/4122 6683/4937/4121 6682/4958/4139 -f 6665/4940/4101 6684/4939/4123 6683/4960/4121 -f 6685/4941/4124 6684/4939/4123 6666/4917/4104 -f 6686/4942/4125 6685/4963/4124 6667/4943/4105 -f 6687/4944/4126 6686/4942/4125 6668/4921/4106 -f 6688/4945/4127 6687/4944/4126 6669/4922/4107 -f 6689/4946/4128 6688/4945/4127 6670/4923/4108 -f 6690/4947/4129 6689/4946/4128 6671/4924/4109 -f 6691/4948/4130 6690/4947/4129 6672/4925/4110 -f 6692/4949/4131 6691/4948/4130 6673/4926/4111 -f 6693/4971/4132 6692/4949/4131 6674/4928/4112 -f 6694/4952/4133 6693/4950/4132 6675/4929/4113 -f 6695/4953/4134 6694/4952/4133 6676/4930/4114 -f 6696/4954/4135 6695/4953/4134 6677/4931/4115 -f 6697/4955/4136 6696/4954/4135 6678/4932/4116 -f 6698/4956/4137 6697/4955/4136 6679/4933/4117 -f 6699/4978/4156 6698/4956/4137 6680/4935/4119 -f 6681/4936/4120 6701/9202/6586 6700/4979/4157 -f 6699/4978/4156 6718/9203/6587 6698/4956/4137 -f 6683/4937/4121 6703/9204/4140 6702/4957/4138 -f 6684/4939/4123 6704/4961/4141 6703/4959/4140 -f 6705/9205/4142 6704/4961/4141 6685/4941/4124 -f 6706/4964/4143 6705/4962/4142 6686/4942/4125 -f 6707/4965/4144 6706/4964/4143 6687/4944/4126 -f 6708/4966/4145 6707/4965/4144 6688/4945/4127 -f 6709/4967/4146 6708/4966/4145 6689/4946/4128 -f 6710/4968/4147 6709/4967/4146 6690/4947/4129 -f 6711/4969/4148 6710/4968/4147 6691/4948/4130 -f 6712/4970/4149 6711/4969/4148 6692/4949/4131 -f 6713/4972/4150 6712/9206/4149 6693/4950/4132 -f 6714/4973/4151 6713/4972/4150 6694/4952/4133 -f 6715/4974/4152 6714/4973/4151 6695/4953/4134 -f 6716/4975/4153 6715/4974/4152 6696/4954/4135 -f 6717/4976/4154 6716/4975/4153 6697/4955/4136 -f 6718/9203/6587 6717/4976/4154 6698/4956/4137 -f 6721/9207/6588 6703/9204/4140 6722/9208/6589 -f 6720/9209/6590 6719/4977/4155 6700/4979/4157 -f 6701/9202/6586 6720/9209/6590 6700/4979/4157 -f 6722/9210/6589 6704/4961/4141 6723/9211/4160 -f 6704/4961/4141 6724/9212/4161 6723/9211/4160 -f 6705/4962/4142 6725/4980/4158 6724/4983/4161 -f 6707/4965/4144 6727/4988/4166 6726/4986/4164 -f 6726/4986/4164 6725/4980/4158 6707/4965/4144 -f 6708/4966/4145 6728/4990/4168 6727/4988/4166 -f 6709/4967/4146 6729/4992/4170 6728/4990/4168 -f 6710/4968/4147 6730/4994/4172 6729/4992/4170 -f 6711/4969/4148 6731/4996/4174 6730/4994/4172 -f 6712/4970/4149 6732/4998/4176 6731/4996/4174 -f 6713/4972/4150 6733/5001/4178 6732/5000/4176 -f 6714/4973/4151 6734/5003/4180 6733/5001/4178 -f 6715/4974/4152 6735/5005/4182 6734/5003/4180 -f 6716/4975/4153 6736/5007/4184 6735/5005/4182 -f 6717/4976/4154 6737/5009/4186 6736/5007/4184 -f 6718/9203/6587 6738/9213/6591 6737/5009/4186 -f 6739/5012/4189 6722/9208/6589 6740/5010/4187 -f 6720/9209/6590 6738/9213/6591 6719/4977/4155 -f 6740/5015/4187 6723/9211/4160 6741/5013/4190 -f 6737/5009/4186 6756/9214/6592 6755/5008/4185 -f 6742/5017/4159 6741/5013/4190 6723/9211/4160 -f 6743/4984/4162 6742/4981/4159 6724/4983/4161 -f 6744/4985/4163 6743/4984/4162 6725/4980/4158 -f 6745/4987/4165 6744/4985/4163 6726/4986/4164 -f 6746/4989/4167 6745/4987/4165 6727/4988/4166 -f 6747/4991/4169 6746/4989/4167 6728/4990/4168 -f 6748/4993/4171 6747/4991/4169 6729/4992/4170 -f 6749/4995/4173 6748/4993/4171 6730/4994/4172 -f 6750/4997/4175 6749/4995/4173 6731/4996/4174 -f 6751/4999/4177 6750/9215/4175 6732/5000/4176 -f 6752/5002/4179 6751/4999/4177 6733/5001/4178 -f 6753/5004/4181 6752/5002/4179 6734/5003/4180 -f 6754/5006/4183 6753/5004/4181 6735/5005/4182 -f 6755/5008/4185 6754/5006/4183 6736/5007/4184 -f 6738/9213/6591 6757/9216/6593 6756/9214/6592 -f 6720/9209/6590 6758/5054/4225 6757/9216/6593 -f 6755/5008/4185 6776/9217/6594 6775/5032/4205 -f 6739/5012/4189 6760/5011/4188 6759/9218/6595 -f 6740/5015/4187 6761/5014/4191 6760/9219/4188 -f 6762/5016/4192 6761/5014/4191 6741/5013/4190 -f 6763/5018/4193 6762/5035/4192 6742/4981/4159 -f 6764/5019/4194 6763/5018/4193 6743/4984/4162 -f 6765/5020/4195 6764/5019/4194 6744/4985/4163 -f 6766/5021/4196 6765/5020/4195 6745/4987/4165 -f 6767/5022/4197 6766/5021/4196 6746/4989/4167 -f 6768/5023/4198 6767/5022/4197 6747/4991/4169 -f 6769/5024/4199 6768/5023/4198 6748/4993/4171 -f 6770/5025/4200 6769/5024/4199 6749/4995/4173 -f 6771/5026/4201 6770/5025/4200 6750/4997/4175 -f 6772/5028/4202 6771/5026/4201 6751/5027/4177 -f 6773/5030/4203 6772/9220/4202 6752/5002/4179 -f 6774/5031/4204 6773/5030/4203 6753/5004/4181 -f 6775/5032/4205 6774/5031/4204 6754/5006/4183 -f 6777/9221/6596 6760/5011/4188 6778/9222/6597 -f 6757/9216/6593 6776/9217/6594 6756/9214/6592 -f 6778/9222/6597 6761/9223/4191 6779/9224/4211 -f 6765/5020/4195 6785/9225/6598 6784/5045/4216 -f 6761/5014/4191 6780/5033/4206 6779/5039/4211 -f 6781/5041/4207 6780/5033/4206 6762/5016/4192 -f 6782/5036/4208 6781/5034/4207 6763/5018/4193 -f 6783/5037/4209 6782/5036/4208 6764/5019/4194 -f 6784/5045/4216 6783/5037/4209 6765/5020/4195 -f 6766/5021/4196 6786/9226/6599 6785/9225/6598 -f 6767/5022/4197 6787/9227/6600 6786/9226/6599 -f 6768/5023/4198 6788/9228/6601 6787/9227/6600 -f 6769/5024/4199 6789/9229/6602 6788/9228/6601 -f 6770/5025/4200 6790/9230/6603 6789/9229/6602 -f 6771/5026/4201 6791/9231/6604 6790/9230/6603 -f 6772/5028/4202 6792/9232/6605 6791/9231/6604 -f 6773/9233/4203 6794/9234/6606 6793/9235/6607 -f 6793/9235/6607 6792/9232/6605 6773/9233/4203 -f 6774/5031/4204 6795/9236/6608 6794/9237/6606 -f 6775/5032/4205 6797/5048/4219 6796/5047/4218 -f 6796/5047/4218 6795/9236/6608 6775/5032/4205 -f 6776/9217/6594 6799/5052/4223 6798/5050/4221 -f 6798/5050/4221 6797/5048/4219 6776/9217/6594 -f 6800/9238/6609 6778/9222/6597 6801/9239/6610 -f 6758/5054/4225 6799/5052/4223 6757/9216/6593 -f 6801/9239/6610 6779/9224/4211 6802/9240/4210 -f 6784/5045/4216 6808/5057/4228 6807/5044/4215 -f 6803/5040/4212 6802/5038/4210 6780/5033/4206 -f 6804/9241/6611 6803/9242/4212 6781/5034/4207 -f 6805/5042/4213 6804/9241/6611 6781/5034/4207 -f 6806/5043/4214 6805/5042/4213 6782/5036/4208 -f 6807/5044/4215 6806/5043/4214 6783/5037/4209 -f 6785/9225/6598 6809/5059/4230 6808/5057/4228 -f 6786/9226/6599 6810/5061/4232 6809/5059/4230 -f 6787/9227/6600 6811/5063/4234 6810/5061/4232 -f 6788/9228/6601 6812/5065/4236 6811/5063/4234 -f 6789/9229/6602 6813/5067/4238 6812/5065/4236 -f 6790/9230/6603 6814/5069/4240 6813/5067/4238 -f 6791/9231/6604 6815/5071/4242 6814/5069/4240 -f 6792/9232/6605 6816/5073/4244 6815/5071/4242 -f 6793/9235/6607 6817/5075/4246 6816/5073/4244 -f 6794/9234/6606 6818/5077/4248 6817/5075/4246 -f 6795/9243/6608 6819/5079/4217 6818/5077/4248 -f 6823/9244/6612 6801/9239/6610 6824/9245/6613 -f 6820/5049/4220 6819/5046/4217 6797/5048/4219 -f 6821/5051/4222 6820/5049/4220 6798/5050/4221 -f 6822/5053/4224 6821/5051/4222 6799/5052/4223 -f 6824/9245/6613 6802/9240/4210 6825/9246/4251 -f 6802/9247/4210 6826/5082/4252 6825/5081/4251 -f 6803/9242/4212 6827/5085/4254 6826/5084/4252 -f 6804/9241/6611 6828/5086/4255 6827/5085/4254 -f 6805/5042/4213 6829/5055/4226 6828/5086/4255 -f 6820/9248/4220 6844/9249/6614 6819/5079/4217 -f 6830/5089/4258 6829/5055/4226 6807/5044/4215 -f 6831/5056/4227 6830/5089/4258 6807/5044/4215 -f 6832/5058/4229 6831/5056/4227 6808/5057/4228 -f 6833/5060/4231 6832/5058/4229 6809/5059/4230 -f 6834/5062/4233 6833/5060/4231 6810/5061/4232 -f 6835/5064/4235 6834/5062/4233 6811/5063/4234 -f 6836/5066/4237 6835/5064/4235 6812/5065/4236 -f 6837/5068/4239 6836/5066/4237 6813/5067/4238 -f 6838/5070/4241 6837/5068/4239 6814/5069/4240 -f 6839/5072/4243 6838/5070/4241 6815/5071/4242 -f 6840/5074/4245 6839/5072/4243 6816/5073/4244 -f 6841/5092/4261 6840/5074/4245 6817/5075/4246 -f 6842/5076/4247 6841/5092/4261 6817/5075/4246 -f 6843/5078/4249 6842/5076/4247 6818/5077/4248 -f 6844/9249/6614 6843/5078/4249 6819/5079/4217 -f 6820/9248/4220 6846/5095/4264 6845/5094/4263 -f 6847/9250/6615 6824/9245/6613 6848/9251/6616 -f 6822/5097/4224 6846/5095/4264 6821/9252/4222 -f 6848/9251/6616 6825/9246/4251 6849/9253/4266 -f 6831/5056/4227 6855/5101/4267 6854/5090/4259 -f 6825/5081/4251 6850/5080/4250 6849/5099/4266 -f 6851/5083/4253 6850/5100/4250 6826/5084/4252 -f 6852/5087/4256 6851/5083/4253 6828/5086/4255 -f 6853/5088/4257 6852/5087/4256 6829/5055/4226 -f 6854/5090/4259 6853/5088/4257 6830/5089/4258 -f 6832/5058/4229 6856/5103/4269 6855/5101/4267 -f 6833/5060/4231 6857/5105/4271 6856/5103/4269 -f 6834/5062/4233 6858/5107/4273 6857/5105/4271 -f 6835/5064/4235 6859/5109/4275 6858/5107/4273 -f 6836/5066/4237 6860/5111/4277 6859/5109/4275 -f 6837/5068/4239 6861/5113/4279 6860/5111/4277 -f 6838/5070/4241 6862/5115/4281 6861/5113/4279 -f 6839/5072/4243 6863/5117/4283 6862/5115/4281 -f 6841/5092/4261 6863/5117/4283 6840/5074/4245 -f 6842/5076/4247 6865/5120/4286 6864/5091/4260 -f 6843/5078/4249 6866/5122/4288 6865/5120/4286 -f 6845/5094/4263 6866/5122/4288 6844/9249/6614 -f 6847/9250/6615 6142/9010/6457 6141/9007/6454 -f 6868/5096/4265 6867/5093/4262 6846/5095/4264 -f 6848/9251/6616 6143/9254/3729 6142/9010/6457 -f 6162/9255/6466 6868/5096/4265 6163/9256/6467 -f 6850/5080/4250 6144/9257/3727 6143/5098/3729 -f 6851/5083/4253 6145/4504/3730 6144/4506/3727 -f 6851/5083/4253 6146/4507/3732 6145/4504/3730 -f 6852/5087/4256 6147/4508/3733 6146/4507/3732 -f 6853/5088/4257 6148/4510/3735 6147/4508/3733 -f 6854/5090/4259 6149/5102/4268 6148/4510/3735 -f 6855/5101/4267 6150/5104/4270 6149/5102/4268 -f 6856/5103/4269 6151/5106/4272 6150/5104/4270 -f 6857/5105/4271 6152/5108/4274 6151/5106/4272 -f 6858/5107/4273 6153/5110/4276 6152/5108/4274 -f 6859/5109/4275 6154/5112/4278 6153/5110/4276 -f 6860/5111/4277 6155/5114/4280 6154/5112/4278 -f 6861/5113/4279 6156/5116/4282 6155/5114/4280 -f 6862/5115/4281 6157/5118/4284 6156/5116/4282 -f 6863/5117/4283 6158/5119/4285 6157/5118/4284 -f 6864/5091/4260 6159/5121/4287 6158/5119/4285 -f 6865/5120/4286 6160/5123/4289 6159/5121/4287 -f 6866/5122/4288 6161/5124/4290 6160/5123/4289 -f 6867/5093/4262 6162/9255/6466 6161/5124/4290 -f 6163/9256/6467 6822/5097/4224 6164/9258/6468 -f 10/62/50 69/8/8 94/7/7 -f 3509/2146/1872 3510/9259/5865 3506/9260/1873 -f 3510/9259/5865 3511/7578/5866 3514/9261/1874 -f 3506/9260/1873 3510/9259/5865 3514/9261/1874 -f 3511/7578/5866 3512/7581/5868 3513/2117/1856 -f 3513/2117/1856 3514/9261/1874 3511/7578/5866 -f 3506/9260/1873 3507/2144/1870 3509/2146/1872 -f 1/5127/14 62/5130/42 103/5128/4292 -f 62/5130/42 64/5136/41 68/5131/4293 -f 74/5132/4294 96/9262/4311 67/5133/47 -f 63/5135/4295 67/49/47 68/5131/4293 -f 12/5137/34 109/5184/37 70/16/16 -f 7/2/2 8/5141/4297 88/32/31 -f 84/5143/28 86/9263/29 119/5144/4299 -f 84/5143/28 85/5145/4300 126/65/60 -f 82/5151/27 86/30/29 87/19/19 -f 80/33/32 63/5134/4295 64/43/41 -f 2/5142/4298 74/5132/4294 80/33/32 -f 83/5152/22 71/9264/21 117/5153/4301 -f 83/5152/22 89/5154/4302 127/67/61 -f 91/5138/4296 90/34/33 78/56/54 -f 95/5156/4303 3/9265/1 75/37/36 -f 93/5157/40 79/9/9 73/5158/6 -f 75/5160/36 3/1/1 62/44/42 -f 102/47/45 74/5176/4294 2/5164/4298 -f 107/5165/4305 98/36/35 101/13/13 -f 107/5165/4305 105/5183/4291 95/5156/4303 -f 97/5168/4308 100/55/53 8/5169/4297 -f 100/55/53 16/9266/4307 2/5171/4298 -f 67/49/47 96/9267/4311 106/5173/11 -f 17/15/15 110/5177/4310 108/5166/4306 -f 103/5128/4292 111/48/46 110/5174/4310 -f 7/5170/2 3/5179/1 104/5172/4309 -f 110/5177/4310 5/9268/49 77/5178/39 -f 114/50/48 106/5173/11 4/5181/10 -f 76/5182/4304 6/9269/5 105/5183/4291 -f 77/5178/39 76/5182/4304 107/5165/4305 -f 109/5184/37 16/9266/4307 100/55/53 -f 105/5125/4291 115/4/4 104/5172/4309 -f 127/67/61 89/5154/4302 122/57/55 -f 72/5185/4312 11/5139/51 13/58/56 -f 117/5153/4301 72/5185/4312 122/57/55 -f 126/65/60 85/5145/4300 120/61/59 -f 119/5144/4299 73/5158/6 120/61/59 -f 124/27/26 65/17/17 73/6/6 -f 125/24/24 66/54/52 72/5186/4312 -f 118/64/23 121/60/58 69/8/8 -f 116/66/25 123/59/57 78/56/54 -f 132/5187/108 163/111/101 143/110/100 -f 24/5189/118 137/72/66 162/71/65 -f 128/75/69 172/74/68 31/5193/72 -f 142/5195/103 164/5238/4326 171/82/76 -f 175/5197/122 144/5223/4315 160/5198/4316 -f 27/5203/96 176/5244/95 138/83/77 -f 188/5205/4318 153/98/91 152/97/90 -f 155/86/80 151/89/83 150/5215/88 -f 130/5217/75 131/113/70 156/5218/4322 -f 156/5218/4322 131/113/70 163/111/101 -f 28/5207/4320 157/5219/4323 156/5218/4322 -f 129/5209/104 142/5220/103 157/5219/4323 -f 186/5221/4321 158/103/94 151/102/83 -f 144/5223/4315 27/5225/96 159/5224/4324 -f 148/5201/4317 160/5200/4316 159/5229/4324 -f 167/79/73 166/117/105 143/5230/100 -f 144/5196/4315 180/134/121 179/106/97 -f 161/5232/99 149/73/67 22/5233/64 -f 146/5192/4314 162/71/65 161/5232/99 -f 183/119/107 174/5242/4313 19/5231/115 -f 164/5238/4326 175/5243/122 182/127/114 -f 31/78/72 177/5241/4327 181/118/106 -f 172/74/68 178/126/113 177/5239/4327 -f 177/5241/4327 20/5190/117 146/5192/4314 -f 145/5235/98 21/9270/63 174/5242/4313 -f 181/118/106 146/5192/4314 145/5235/98 -f 180/134/121 169/136/123 168/114/102 -f 176/5244/95 30/125/112 170/124/111 -f 174/5191/4313 184/68/62 173/129/116 -f 191/5245/4328 148/5201/4317 147/5228/4325 -f 158/103/94 191/5245/4328 192/140/126 -f 140/5246/4329 25/5202/119 148/5201/4317 -f 186/5221/4321 140/5246/4329 191/5245/4328 -f 195/96/89 153/98/91 189/138/125 -f 141/5247/4330 22/5233/64 149/73/67 -f 188/5205/4318 141/5247/4330 189/138/125 -f 193/94/87 134/84/78 141/5248/4330 -f 194/91/85 135/133/120 140/5249/4329 -f 187/139/84 190/137/124 137/72/66 -f 185/141/86 192/140/126 147/5228/4325 -f 38/5250/186 206/146/131 229/145/130 -f 197/149/134 239/148/133 46/5256/140 -f 211/5258/174 231/9271/4342 238/159/144 -f 40/5259/170 244/5305/169 207/160/145 -f 219/174/158 221/9272/160 255/5265/4333 -f 222/163/148 218/166/151 217/5274/156 -f 199/5275/143 200/9273/135 223/5276/4336 -f 223/5276/4336 200/9273/135 43/5278/178 -f 42/5266/4334 224/5277/4337 223/5276/4336 -f 198/5268/175 211/5279/174 224/5277/4337 -f 253/5280/4335 225/180/162 218/179/151 -f 227/183/165 33/152/137 40/188/170 -f 234/156/141 233/185/167 212/184/166 -f 33/152/137 250/151/136 247/189/171 -f 228/5286/4338 216/147/132 36/5287/129 -f 214/5254/4332 229/5253/130 228/5289/4338 -f 43/5278/178 200/9273/135 197/192/134 -f 249/197/177 241/5304/4331 230/186/168 -f 238/159/144 231/9271/4342 242/5294/138 -f 46/155/140 245/5300/4341 243/196/176 -f 239/148/133 246/204/183 245/5296/4341 -f 43/198/178 201/9274/172 240/5295/4340 -f 240/5295/4340 201/9274/172 212/5298/166 -f 245/5300/4341 34/9275/185 214/5301/4332 -f 248/205/184 242/5294/138 33/5302/137 -f 213/5303/4339 35/9276/128 241/5304/4331 -f 243/196/176 214/5301/4332 213/5303/4339 -f 244/5305/169 45/203/182 237/202/181 -f 241/5252/4331 251/142/127 240/5295/4340 -f 225/180/162 258/211/190 259/213/192 -f 209/5306/4343 39/5260/187 41/212/191 -f 253/5280/4335 209/5306/4343 258/211/190 -f 262/173/157 220/175/159 256/215/194 -f 210/5307/4344 36/5287/129 216/147/132 -f 255/5265/4333 210/5307/4344 256/215/194 -f 260/171/155 203/161/146 210/5308/4344 -f 261/168/153 204/209/188 209/5309/4343 -f 254/216/152 257/214/193 206/146/131 -f 252/217/154 259/213/192 215/182/164 -f 53/5310/186 273/222/131 297/221/195 -f 264/225/134 307/224/133 61/5316/140 -f 278/5318/204 299/9277/4351 306/235/144 -f 55/5319/170 312/5367/169 274/236/145 -f 286/250/158 288/9278/160 322/5325/4333 -f 289/239/148 285/242/151 284/5334/156 -f 266/5335/198 267/9279/135 290/5336/4336 -f 290/5336/4336 267/9279/135 58/5338/178 -f 57/5326/4334 291/5337/4337 290/5336/4336 -f 265/5328/205 278/5339/204 291/5337/4337 -f 320/5340/4335 292/256/162 285/255/151 -f 294/259/165 48/228/137 55/264/170 -f 302/232/197 301/261/201 279/260/166 -f 48/228/137 317/227/196 315/265/202 -f 296/5346/4338 283/223/132 51/5347/129 -f 281/5314/4346 297/5313/195 296/5349/4338 -f 58/5338/178 267/9279/135 264/268/134 -f 295/5353/4348 311/5355/4349 301/261/201 -f 311/5355/4349 309/5366/4345 298/262/168 -f 306/235/144 299/9277/4351 310/5356/138 -f 61/231/140 313/5362/4350 295/5353/4348 -f 307/224/133 314/278/183 313/5358/4350 -f 58/272/178 268/9280/172 308/5357/4340 -f 308/5357/4340 268/9280/172 279/5360/166 -f 313/5362/4350 49/9281/185 281/5363/4346 -f 316/279/184 310/5356/138 48/5364/137 -f 280/5365/4347 50/9282/128 309/5366/4345 -f 295/5353/4348 281/5363/4346 280/5365/4347 -f 317/227/196 304/284/207 303/269/203 -f 312/5367/169 60/277/206 305/276/181 -f 309/5312/4345 318/218/127 308/5357/4340 -f 292/256/162 325/285/190 326/287/192 -f 276/5368/4343 54/5320/187 56/286/191 -f 320/5340/4335 276/5368/4343 325/285/190 -f 329/249/157 287/251/159 323/289/194 -f 277/5369/4344 51/5347/129 283/223/132 -f 322/5325/4333 277/5369/4344 323/289/194 -f 327/247/155 270/237/146 277/5370/4344 -f 328/244/153 271/283/188 276/5371/4343 -f 321/290/152 324/288/193 273/222/131 -f 319/291/154 326/287/192 282/258/164 -f 411/5372/4352 378/9283/6617 331/5373/4353 -f 379/5374/4354 331/5373/4353 332/5375/4355 -f 380/5376/4356 332/5375/4355 333/5377/4357 -f 381/5378/4358 333/5377/4357 334/294/210 -f 382/293/209 335/292/208 336/297/213 -f 383/296/212 337/295/211 338/300/216 -f 384/299/215 339/298/214 340/303/219 -f 385/302/218 341/301/217 342/305/221 -f 343/306/222 344/308/224 387/307/223 -f 345/309/225 346/311/227 388/310/226 -f 347/312/228 348/314/230 389/313/229 -f 349/315/231 350/317/233 390/316/232 -f 351/318/234 352/5380/4360 391/5379/4359 -f 352/5380/4360 353/5382/4362 392/5381/4361 -f 353/5382/4362 354/9284/6618 393/5383/4363 -f 393/5383/4363 354/9284/6618 355/5384/4364 -f 394/5385/4365 355/5384/4364 356/5386/4366 -f 395/333/249 356/5386/4366 357/5387/4367 -f 396/331/247 357/5387/4367 358/321/237 -f 398/5388/4368 361/5390/4370 362/5389/4369 -f 399/327/243 362/5389/4369 363/5392/4372 -f 400/325/241 363/5392/4372 364/5393/4373 -f 401/5394/4374 364/5393/4373 365/5395/4375 -f 402/338/254 365/5395/4375 366/5396/4376 -f 366/5396/4376 367/5398/4378 404/340/256 -f 367/5398/4378 368/5400/4380 405/5399/4379 -f 368/5400/4380 369/5402/4382 406/5401/4381 -f 369/5402/4382 370/5403/4383 407/329/245 -f 370/5403/4383 371/5405/4385 408/330/246 -f 376/324/240 377/5406/4386 410/342/258 -f 377/5406/4386 378/9283/6617 411/5372/4352 -f 412/5407/4387 381/5378/4358 382/293/209 -f 414/5409/4389 382/293/209 383/296/212 -f 416/5411/4391 383/296/212 384/299/215 -f 418/5413/4393 384/299/215 385/302/218 -f 420/5415/4395 385/302/218 386/304/220 -f 386/304/220 387/307/223 423/5417/4397 -f 387/307/223 388/310/226 425/5419/4399 -f 388/310/226 389/313/229 427/5421/4401 -f 389/313/229 390/316/232 429/5423/4403 -f 390/316/232 391/5379/4359 431/5425/4405 -f 432/334/250 359/319/235 360/5391/4371 -f 433/5427/4407 360/5391/4371 398/5388/4368 -f 434/5428/4408 398/5388/4368 399/327/243 -f 408/330/246 372/5404/4384 438/5429/4409 -f 372/5404/4384 373/5431/4411 439/5430/4410 -f 373/5431/4411 374/5433/4413 440/5432/4412 -f 374/5433/4413 375/323/239 441/5434/4414 -f 477/5435/4415 411/5372/4352 379/5374/4354 -f 442/5436/4416 379/5374/4354 380/5376/4356 -f 443/5437/4417 380/5376/4356 412/5407/4387 -f 444/5438/4418 412/5407/4387 413/5408/4388 -f 445/5439/4419 413/5408/4388 414/5409/4389 -f 446/5440/4420 414/5409/4389 415/5410/4390 -f 447/5441/4421 415/5410/4390 416/5411/4391 -f 448/5442/4422 416/5411/4391 417/5412/4392 -f 449/5443/4423 417/5412/4392 418/5413/4393 -f 450/5444/4424 418/5413/4393 419/5414/4394 -f 451/5445/4425 419/5414/4394 420/5415/4395 -f 452/5446/4426 420/5415/4395 421/5416/4396 -f 421/5416/4396 422/5418/4398 454/5448/4428 -f 422/5418/4398 423/5417/4397 455/5449/4429 -f 423/5417/4397 424/5420/4400 456/5450/4430 -f 424/5420/4400 425/5419/4399 457/5451/4431 -f 425/5419/4399 426/5422/4402 458/5452/4432 -f 426/5422/4402 427/5421/4401 459/5453/4433 -f 427/5421/4401 428/5424/4404 460/5454/4434 -f 428/5424/4404 429/5423/4403 461/5455/4435 -f 429/5423/4403 430/5426/4406 462/5456/4436 -f 430/5426/4406 431/5425/4405 463/5457/4437 -f 431/5425/4405 392/5381/4361 464/5458/4438 -f 392/5381/4361 393/5383/4363 465/5459/4439 -f 465/5459/4439 393/5383/4363 394/5385/4365 -f 466/5460/4440 394/5385/4365 395/333/249 -f 469/5461/4441 400/325/241 401/5394/4374 -f 470/5462/4442 401/5394/4374 402/338/254 -f 404/340/256 405/5399/4379 473/5463/4443 -f 405/5399/4379 406/5401/4381 474/5464/4444 -f 406/5401/4381 407/329/245 475/5465/4445 -f 410/342/258 411/5372/4352 477/5435/4415 -f 525/5466/4446 477/5435/4415 442/5436/4416 -f 478/5467/4447 442/5436/4416 443/5437/4417 -f 479/5468/4448 443/5437/4417 444/5438/4418 -f 480/5469/4449 444/5438/4418 445/5439/4419 -f 481/5470/4450 445/5439/4419 446/5440/4420 -f 482/5471/4451 446/5440/4420 447/5441/4421 -f 483/5472/4452 447/5441/4421 448/5442/4422 -f 484/5473/4453 448/5442/4422 449/5443/4423 -f 485/5474/4454 449/5443/4423 450/5444/4424 -f 486/5475/4455 450/5444/4424 451/5445/4425 -f 487/5476/4456 451/5445/4425 452/5446/4426 -f 488/5477/4457 452/5446/4426 453/5447/4427 -f 489/5478/4458 453/5447/4427 454/5448/4428 -f 454/5448/4428 455/5449/4429 491/5480/4460 -f 455/5449/4429 456/5450/4430 492/5481/4461 -f 456/5450/4430 457/5451/4431 493/5482/4462 -f 457/5451/4431 458/5452/4432 494/5483/4463 -f 458/5452/4432 459/5453/4433 495/5484/4464 -f 459/5453/4433 460/5454/4434 496/5485/4465 -f 460/5454/4434 461/5455/4435 497/5486/4466 -f 461/5455/4435 462/5456/4436 498/5487/4467 -f 462/5456/4436 463/5457/4437 499/5488/4468 -f 463/5457/4437 464/5458/4438 500/5489/4469 -f 464/5458/4438 465/5459/4439 501/5490/4470 -f 501/5490/4470 465/5459/4439 466/5460/4440 -f 502/5491/4471 466/5460/4440 467/332/248 -f 503/5492/4472 467/332/248 396/331/247 -f 504/5493/4473 396/331/247 468/335/251 -f 505/5494/4474 468/335/251 432/334/250 -f 506/5495/4475 432/334/250 433/5427/4407 -f 507/5496/4476 433/5427/4407 434/5428/4408 -f 508/5497/4477 434/5428/4408 435/326/242 -f 509/5498/4478 435/326/242 469/5461/4441 -f 510/5499/4479 469/5461/4441 470/5462/4442 -f 511/5500/4480 470/5462/4442 471/337/253 -f 512/5501/4481 471/337/253 436/336/252 -f 436/336/252 472/339/255 514/5503/4483 -f 472/339/255 473/5463/4443 515/5504/4484 -f 473/5463/4443 474/5464/4444 516/5505/4485 -f 474/5464/4444 475/5465/4445 517/5506/4486 -f 475/5465/4445 437/328/244 518/5507/4487 -f 437/328/244 438/5429/4409 519/5508/4488 -f 438/5429/4409 439/5430/4410 520/5509/4489 -f 439/5430/4410 440/5432/4412 521/5510/4490 -f 440/5432/4412 441/5434/4414 522/5511/4491 -f 441/5434/4414 409/322/238 523/5512/4492 -f 409/322/238 476/341/257 524/5513/4493 -f 476/341/257 477/5435/4415 525/5466/4446 -f 573/5514/4494 525/5466/4446 478/5467/4447 -f 526/5515/4495 478/5467/4447 479/5468/4448 -f 527/5516/4496 479/5468/4448 480/5469/4449 -f 528/5517/4497 480/5469/4449 481/5470/4450 -f 529/5518/4498 481/5470/4450 482/5471/4451 -f 530/5519/4499 482/5471/4451 483/5472/4452 -f 531/5520/4500 483/5472/4452 484/5473/4453 -f 532/5521/4501 484/5473/4453 485/5474/4454 -f 533/5522/4502 485/5474/4454 486/5475/4455 -f 534/5524/4503 486/9285/4455 487/5525/4456 -f 535/5526/4504 487/5525/4456 488/5527/4457 -f 536/5528/4505 488/5527/4457 489/5529/4458 -f 537/5530/4506 489/5529/4458 490/5531/4459 -f 490/5531/4459 491/5534/4460 539/5533/4508 -f 491/5534/4460 492/5536/4461 540/5535/4509 -f 492/5536/4461 493/9286/4462 541/5537/4510 -f 493/5482/4462 494/5483/4463 542/5538/4511 -f 494/5483/4463 495/5484/4464 543/5540/4512 -f 495/5484/4464 496/5485/4465 544/5541/4513 -f 496/5485/4465 497/5486/4466 545/5542/4514 -f 497/5486/4466 498/5487/4467 546/5543/4515 -f 498/5487/4467 499/5488/4468 547/5544/4516 -f 499/5488/4468 500/5489/4469 548/5545/4517 -f 500/5489/4469 501/5490/4470 549/5546/4518 -f 549/5546/4518 501/5490/4470 502/5491/4471 -f 550/5547/4519 502/5491/4471 503/5492/4472 -f 551/5548/4520 503/5492/4472 504/5493/4473 -f 552/5549/4521 504/5493/4473 505/5494/4474 -f 553/5550/4522 505/5494/4474 506/5495/4475 -f 554/5551/4523 506/5495/4475 507/5496/4476 -f 555/5552/4524 507/5496/4476 508/5497/4477 -f 556/5553/4525 508/5497/4477 509/5498/4478 -f 557/5554/4526 509/5498/4478 510/5499/4479 -f 558/5555/4527 510/5499/4479 511/5500/4480 -f 559/5556/4528 511/5500/4480 512/5501/4481 -f 560/5557/4529 512/5501/4481 513/5502/4482 -f 513/5502/4482 514/5503/4483 562/5559/4531 -f 514/5503/4483 515/5504/4484 563/5560/4532 -f 515/5504/4484 516/5505/4485 564/5561/4533 -f 516/5505/4485 517/5506/4486 565/5562/4534 -f 517/5506/4486 518/5507/4487 566/5563/4535 -f 518/5507/4487 519/5508/4488 567/5564/4536 -f 519/5508/4488 520/5509/4489 568/5565/4537 -f 520/5509/4489 521/5510/4490 569/5566/4538 -f 521/5510/4490 522/5511/4491 570/5567/4539 -f 522/5511/4491 523/5512/4492 571/5568/4540 -f 523/5512/4492 524/5513/4493 572/5569/4541 -f 524/5513/4493 525/5466/4446 573/5514/4494 -f 621/5570/4542 573/5514/4494 526/5515/4495 -f 574/5571/4543 526/5515/4495 527/5516/4496 -f 575/5572/4544 527/5516/4496 528/5517/4497 -f 576/5573/4545 528/5517/4497 529/5518/4498 -f 577/5574/4546 529/5518/4498 530/5519/4499 -f 578/5575/4547 530/5519/4499 531/5520/4500 -f 579/5577/4548 531/9287/4500 532/5578/4501 -f 580/5579/4549 532/5578/4501 533/5580/4502 -f 581/5581/4550 533/5580/4502 534/5524/4503 -f 582/5582/4551 534/5524/4503 535/5526/4504 -f 583/5583/4552 535/5526/4504 536/5528/4505 -f 584/5584/4553 536/5528/4505 537/5530/4506 -f 585/5585/4554 537/5530/4506 538/5532/4507 -f 538/5532/4507 539/5533/4508 587/5587/4556 -f 539/5533/4508 540/5535/4509 588/5588/4557 -f 540/5535/4509 541/5537/4510 589/5589/4558 -f 541/5537/4510 542/5591/4511 590/5590/4559 -f 542/5591/4511 543/5593/4512 591/5592/4560 -f 543/5593/4512 544/9288/4513 592/5594/4561 -f 544/5541/4513 545/5542/4514 593/5595/4562 -f 545/5542/4514 546/5543/4515 594/5597/4563 -f 546/5543/4515 547/5544/4516 595/5598/4564 -f 547/5544/4516 548/5545/4517 596/5599/4565 -f 548/5545/4517 549/5546/4518 597/5600/4566 -f 597/5600/4566 549/5546/4518 550/5547/4519 -f 598/5601/4567 550/5547/4519 551/5548/4520 -f 599/5602/4568 551/5548/4520 552/5549/4521 -f 600/5603/4569 552/5549/4521 553/5550/4522 -f 601/5604/4570 553/5550/4522 554/5551/4523 -f 602/5605/4571 554/5551/4523 555/5552/4524 -f 603/5607/4572 555/9289/4524 556/5608/4525 -f 604/5609/4573 556/5608/4525 557/5610/4526 -f 605/5611/4574 557/5610/4526 558/5612/4527 -f 606/5613/4575 558/5612/4527 559/5614/4528 -f 607/5615/4576 559/5614/4528 560/5616/4529 -f 608/5617/4577 560/5616/4529 561/5618/4530 -f 561/5618/4530 562/5621/4531 610/5620/4579 -f 562/5621/4531 563/9290/4532 611/5622/4580 -f 563/5623/4532 564/5626/4533 612/5624/4581 -f 564/5626/4533 565/5628/4534 613/5627/4582 -f 565/5628/4534 566/9291/4535 614/5629/4583 -f 566/5563/4535 567/5564/4536 615/5630/4584 -f 567/5564/4536 568/5565/4537 616/5632/4585 -f 568/5565/4537 569/5566/4538 617/5633/4586 -f 569/5566/4538 570/5567/4539 618/5634/4587 -f 570/5567/4539 571/5568/4540 619/5635/4588 -f 571/5568/4540 572/5569/4541 620/5636/4589 -f 572/5569/4541 573/5514/4494 621/5570/4542 -f 669/5637/4590 621/5570/4542 574/5571/4543 -f 622/5638/4591 574/5571/4543 575/5572/4544 -f 623/5639/4592 575/5572/4544 576/5573/4545 -f 624/5640/4593 576/5573/4545 577/5574/4546 -f 625/5641/4594 577/5574/4546 578/5575/4547 -f 626/5643/4595 578/9292/4547 579/5577/4548 -f 627/5644/4596 579/5577/4548 580/5579/4549 -f 628/5645/4597 580/5579/4549 581/5581/4550 -f 629/5646/4598 581/5581/4550 582/5582/4551 -f 630/5647/4599 582/5582/4551 583/5583/4552 -f 631/5648/4600 583/5583/4552 584/5584/4553 -f 632/5649/4601 584/5584/4553 585/5585/4554 -f 633/5650/4602 585/5585/4554 586/5586/4555 -f 586/5586/4555 587/5587/4556 635/5652/4604 -f 587/5587/4556 588/5588/4557 636/5653/4605 -f 588/5588/4557 589/5589/4558 637/5654/4606 -f 589/5589/4558 590/5590/4559 638/5655/4607 -f 590/5590/4559 591/5592/4560 639/5656/4608 -f 591/5592/4560 592/5594/4561 640/5657/4609 -f 592/5594/4561 593/9293/4562 641/5658/4610 -f 593/5595/4562 594/5597/4563 642/5659/4611 -f 594/5597/4563 595/5598/4564 643/5661/4612 -f 595/5598/4564 596/5599/4565 644/5662/4613 -f 596/5599/4565 597/5600/4566 645/5663/4614 -f 645/5663/4614 597/5600/4566 598/5601/4567 -f 646/5664/4615 598/5601/4567 599/5602/4568 -f 647/5665/4616 599/5602/4568 600/5603/4569 -f 648/5666/4617 600/5603/4569 601/5604/4570 -f 649/5667/4618 601/5604/4570 602/5605/4571 -f 650/5669/4619 602/9294/4571 603/5607/4572 -f 651/5670/4620 603/5607/4572 604/5609/4573 -f 652/5671/4621 604/5609/4573 605/5611/4574 -f 653/5672/4622 605/5611/4574 606/5613/4575 -f 654/5673/4623 606/5613/4575 607/5615/4576 -f 655/5674/4624 607/5615/4576 608/5617/4577 -f 656/5675/4625 608/5617/4577 609/5619/4578 -f 609/5619/4578 610/5620/4579 658/5677/4627 -f 610/5620/4579 611/5622/4580 659/5678/4628 -f 611/5625/4580 612/5624/4581 660/5679/4629 -f 612/5624/4581 613/5627/4582 661/5681/4630 -f 613/5627/4582 614/5629/4583 662/5682/4631 -f 614/5629/4583 615/5684/4584 663/5683/4632 -f 615/5684/4584 616/9295/4585 664/5685/4633 -f 616/5632/4585 617/5633/4586 665/5686/4634 -f 617/5633/4586 618/5634/4587 666/5688/4635 -f 618/5634/4587 619/5635/4588 667/5689/4636 -f 619/5635/4588 620/5636/4589 668/5690/4637 -f 620/5636/4589 621/5570/4542 669/5637/4590 -f 717/5691/4638 669/5637/4590 622/5638/4591 -f 670/5692/4639 622/5638/4591 623/5639/4592 -f 671/5693/4640 623/5639/4592 624/5640/4593 -f 672/5694/4641 624/5640/4593 625/5641/4594 -f 673/5696/4642 625/9296/4594 626/5643/4595 -f 674/5697/4643 626/5643/4595 627/5644/4596 -f 675/5698/4644 627/5644/4596 628/5645/4597 -f 676/5699/4645 628/5645/4597 629/5646/4598 -f 677/5700/4646 629/5646/4598 630/5647/4599 -f 678/5701/4647 630/5647/4599 631/5648/4600 -f 679/5702/4648 631/5648/4600 632/5649/4601 -f 680/5703/4649 632/5649/4601 633/5650/4602 -f 681/5704/4650 633/5650/4602 634/5651/4603 -f 634/5651/4603 635/5652/4604 683/5706/4652 -f 635/5652/4604 636/5653/4605 684/5707/4653 -f 636/5653/4605 637/5654/4606 685/5708/4654 -f 637/5654/4606 638/5655/4607 686/5709/4655 -f 638/5655/4607 639/5656/4608 687/5710/4656 -f 639/5656/4608 640/5657/4609 688/5711/4657 -f 640/5657/4609 641/5658/4610 689/5712/4658 -f 641/5658/4610 642/9297/4611 690/5713/4659 -f 642/5659/4611 643/5661/4612 691/5714/4660 -f 643/5661/4612 644/5662/4613 692/5716/4661 -f 644/5662/4613 645/5663/4614 693/5717/4662 -f 693/5717/4662 645/5663/4614 646/5664/4615 -f 694/5718/4663 646/5664/4615 647/5665/4616 -f 695/5719/4664 647/5665/4616 648/5666/4617 -f 696/5721/4665 648/9298/4617 649/5722/4618 -f 697/5723/4666 649/5722/4618 650/5669/4619 -f 698/5724/4667 650/5669/4619 651/5670/4620 -f 699/5725/4668 651/5670/4620 652/5671/4621 -f 700/5726/4669 652/5671/4621 653/5672/4622 -f 701/5727/4670 653/5672/4622 654/5673/4623 -f 702/5728/4671 654/5673/4623 655/5674/4624 -f 703/5729/4672 655/5674/4624 656/5675/4625 -f 704/5730/4673 656/5675/4625 657/5676/4626 -f 657/5676/4626 658/5677/4627 706/5732/4675 -f 658/5677/4627 659/5678/4628 707/5733/4676 -f 659/5680/4628 660/5679/4629 708/5734/4677 -f 660/5679/4629 661/5681/4630 709/5736/4678 -f 661/5681/4630 662/5682/4631 710/5737/4679 -f 662/5682/4631 663/5683/4632 711/5738/4680 -f 663/5683/4632 664/5685/4633 712/5739/4681 -f 664/5685/4633 665/5741/4634 713/5740/4682 -f 665/5741/4634 666/9299/4635 714/5742/4683 -f 666/5688/4635 667/5689/4636 715/5743/4684 -f 667/5689/4636 668/5690/4637 716/5745/4685 -f 668/5690/4637 669/5637/4590 717/5691/4638 -f 765/5746/4686 717/5691/4638 670/5692/4639 -f 718/5747/4687 670/5692/4639 671/5693/4640 -f 719/5748/4688 671/5693/4640 672/5694/4641 -f 720/5750/4689 672/9300/4641 673/5696/4642 -f 721/5751/4690 673/5696/4642 674/5697/4643 -f 722/5752/4691 674/5697/4643 675/5698/4644 -f 723/345/261 675/5698/4644 676/5699/4645 -f 724/343/259 676/5699/4645 677/5700/4646 -f 725/5753/4692 677/5700/4646 678/5701/4647 -f 726/5754/4693 678/5701/4647 679/5702/4648 -f 727/5755/4694 679/5702/4648 680/5703/4649 -f 728/5756/4695 680/5703/4649 681/5704/4650 -f 729/5757/4696 681/5704/4650 682/5705/4651 -f 682/5705/4651 683/5706/4652 731/5759/4698 -f 683/5706/4652 684/5707/4653 732/5760/4699 -f 684/5707/4653 685/5708/4654 733/5761/4700 -f 685/5708/4654 686/5709/4655 734/347/263 -f 686/5709/4655 687/5710/4656 735/348/264 -f 687/5710/4656 688/5711/4657 736/5762/4701 -f 688/5711/4657 689/5712/4658 737/5763/4702 -f 689/5712/4658 690/5713/4659 738/5764/4703 -f 690/5713/4659 691/9301/4660 739/5765/4704 -f 691/5714/4660 692/5716/4661 740/5766/4705 -f 692/5716/4661 693/5717/4662 741/5768/4706 -f 741/5768/4706 693/5717/4662 694/5718/4663 -f 742/5769/4707 694/5718/4663 695/5719/4664 -f 743/5770/4708 695/5719/4664 696/5720/4665 -f 744/5772/4709 696/5721/4665 697/5723/4666 -f 745/5773/4710 697/5723/4666 698/5724/4667 -f 746/5774/4711 698/5724/4667 699/5725/4668 -f 747/5775/4712 699/5725/4668 700/5726/4669 -f 748/5776/4713 700/5726/4669 701/5727/4670 -f 749/351/267 701/5727/4670 702/5728/4671 -f 750/349/265 702/5728/4671 703/5729/4672 -f 751/5777/4714 703/5729/4672 704/5730/4673 -f 752/5778/4715 704/5730/4673 705/5731/4674 -f 705/5731/4674 706/5732/4675 754/5780/4717 -f 706/5732/4675 707/5733/4676 755/5781/4718 -f 707/5735/4676 708/5734/4677 756/5782/4719 -f 708/5734/4677 709/5736/4678 757/353/269 -f 709/5736/4678 710/5737/4679 758/354/270 -f 710/5737/4679 711/5738/4680 759/5784/4720 -f 711/5738/4680 712/5739/4681 760/5785/4721 -f 712/5739/4681 713/5740/4682 761/5786/4722 -f 713/5740/4682 714/5742/4683 762/5787/4723 -f 714/5744/4683 715/5743/4684 763/5788/4724 -f 715/5743/4684 716/5745/4685 764/5790/4725 -f 716/5745/4685 717/5691/4638 765/5746/4686 -f 794/5791/4726 765/5746/4686 718/5747/4687 -f 766/5792/4727 718/5747/4687 719/5748/4688 -f 767/5793/4728 719/5748/4688 720/5749/4689 -f 768/5795/4729 720/5750/4689 721/5751/4690 -f 769/5796/4730 721/5751/4690 722/5752/4691 -f 770/5797/4731 722/5752/4691 723/345/261 -f 735/348/264 736/5762/4701 773/5798/4732 -f 736/5762/4701 737/5763/4702 774/5799/4733 -f 737/5763/4702 738/5764/4703 775/5800/4734 -f 738/5764/4703 739/5765/4704 776/5801/4735 -f 739/5767/4704 740/5766/4705 777/5802/4736 -f 740/5766/4705 741/5768/4706 778/5804/4737 -f 778/5804/4737 741/5768/4706 742/5769/4707 -f 779/5805/4738 742/5769/4707 743/5770/4708 -f 780/5807/4739 743/9302/4708 744/5772/4709 -f 781/5808/4740 744/5772/4709 745/5773/4710 -f 782/5809/4741 745/5773/4710 746/5774/4711 -f 783/5810/4742 746/5774/4711 747/5775/4712 -f 784/5811/4743 747/5775/4712 748/5776/4713 -f 785/5812/4744 748/5776/4713 749/351/267 -f 758/354/270 759/5784/4720 788/5813/4745 -f 759/5784/4720 760/5785/4721 789/5814/4746 -f 760/5785/4721 761/5786/4722 790/5815/4747 -f 761/5786/4722 762/5787/4723 791/5816/4748 -f 762/5787/4723 763/9303/4724 792/5817/4749 -f 763/5788/4724 764/5790/4725 793/5818/4750 -f 764/5790/4725 765/5746/4686 794/5791/4726 -f 842/5820/4751 794/5791/4726 766/5792/4727 -f 795/5821/4752 766/5792/4727 767/5793/4728 -f 796/5823/4753 767/9304/4728 768/5795/4729 -f 797/5824/4754 768/5795/4729 769/5796/4730 -f 798/5825/4755 769/5796/4730 770/5797/4731 -f 799/5826/4756 770/5797/4731 771/344/260 -f 800/5827/4757 771/344/260 724/343/259 -f 801/5828/4758 724/343/259 725/5753/4692 -f 802/5829/4759 725/5753/4692 726/5754/4693 -f 803/5830/4760 726/5754/4693 727/5755/4694 -f 804/5831/4761 727/5755/4694 728/5756/4695 -f 805/5832/4762 728/5756/4695 729/5757/4696 -f 806/5833/4763 729/5757/4696 730/5758/4697 -f 730/5758/4697 731/5759/4698 808/5835/4765 -f 731/5759/4698 732/5760/4699 809/5836/4766 -f 732/5760/4699 733/5761/4700 810/5837/4767 -f 733/5761/4700 734/347/263 811/5838/4768 -f 734/347/263 772/346/262 812/5839/4769 -f 772/346/262 773/5798/4732 813/5840/4770 -f 773/5798/4732 774/5799/4733 814/5841/4771 -f 774/5799/4733 775/5800/4734 815/5842/4772 -f 775/5800/4734 776/5801/4735 816/5843/4773 -f 776/5801/4735 777/9305/4736 817/5844/4774 -f 777/5845/4736 778/9306/4737 818/5846/4775 -f 818/5846/4775 778/9306/4737 779/5848/4738 -f 819/5849/4776 779/5848/4738 780/5850/4739 -f 820/5852/4777 780/5807/4739 781/5808/4740 -f 821/5853/4778 781/5808/4740 782/5809/4741 -f 822/5854/4779 782/5809/4741 783/5810/4742 -f 823/5855/4780 783/5810/4742 784/5811/4743 -f 824/5856/4781 784/5811/4743 785/5812/4744 -f 825/5857/4782 785/5812/4744 786/350/266 -f 826/5858/4783 786/350/266 750/349/265 -f 827/5859/4784 750/349/265 751/5777/4714 -f 828/5860/4785 751/5777/4714 752/5778/4715 -f 753/5779/4716 830/5863/4788 829/5861/4786 -f 753/5779/4716 754/5780/4717 831/5862/4787 -f 754/5780/4717 755/5781/4718 832/5864/4789 -f 755/5783/4718 756/5782/4719 833/5865/4790 -f 756/5782/4719 757/353/269 834/5867/4791 -f 757/353/269 787/352/268 835/5868/4792 -f 787/352/268 788/5813/4745 836/5869/4793 -f 788/5813/4745 789/5814/4746 837/5870/4794 -f 789/5814/4746 790/5815/4747 838/5871/4795 -f 790/5815/4747 791/5816/4748 839/5872/4796 -f 791/5816/4748 792/5817/4749 840/5873/4797 -f 792/5819/4749 793/5818/4750 841/5874/4798 -f 793/5818/4750 794/5791/4726 842/5820/4751 -f 890/5876/4799 842/9307/4751 795/5877/4752 -f 843/5878/4800 795/5877/4752 796/5879/4753 -f 844/5881/4801 796/5823/4753 797/5824/4754 -f 845/5882/4802 797/5824/4754 798/5825/4755 -f 846/5883/4803 798/5825/4755 799/5826/4756 -f 847/5884/4804 799/5826/4756 800/5827/4757 -f 848/5885/4805 800/5827/4757 801/5828/4758 -f 849/5886/4806 801/5828/4758 802/5829/4759 -f 850/5887/4807 802/5829/4759 803/5830/4760 -f 851/5888/4808 803/5830/4760 804/5831/4761 -f 852/5889/4809 804/5831/4761 805/5832/4762 -f 853/5890/4810 805/5832/4762 806/5833/4763 -f 854/5891/4811 806/5833/4763 807/5834/4764 -f 855/5892/4812 807/5834/4764 808/5835/4765 -f 808/5835/4765 809/5836/4766 857/5894/4814 -f 809/5836/4766 810/5837/4767 858/5895/4815 -f 810/5837/4767 811/5838/4768 859/5896/4816 -f 811/5838/4768 812/5839/4769 860/5897/4817 -f 812/5839/4769 813/5840/4770 861/5898/4818 -f 813/5840/4770 814/5841/4771 862/5899/4819 -f 814/5841/4771 815/5842/4772 863/5900/4820 -f 815/5842/4772 816/5843/4773 864/5901/4821 -f 816/5843/4773 817/5844/4774 865/5902/4822 -f 817/5847/4774 818/5846/4775 866/5903/4823 -f 866/5903/4823 818/5846/4775 819/5849/4776 -f 867/5905/4824 819/5849/4776 820/5851/4777 -f 868/5907/4825 820/5852/4777 821/5853/4778 -f 869/5908/4826 821/5853/4778 822/5854/4779 -f 870/5909/4827 822/5854/4779 823/5855/4780 -f 871/5910/4828 823/5855/4780 824/5856/4781 -f 872/5911/4829 824/5856/4781 825/5857/4782 -f 873/5912/4830 825/5857/4782 826/5858/4783 -f 874/5913/4831 826/5858/4783 827/5859/4784 -f 875/5914/4832 827/5859/4784 828/5860/4785 -f 876/5915/4833 828/5860/4785 829/5861/4786 -f 829/5861/4786 830/5863/4788 878/5917/4835 -f 830/5863/4788 831/5862/4787 879/5918/4836 -f 831/5862/4787 832/5864/4789 880/5919/4837 -f 832/5866/4789 833/5865/4790 881/5920/4838 -f 833/5865/4790 834/5867/4791 882/5922/4839 -f 834/5867/4791 835/5868/4792 883/5923/4840 -f 835/5868/4792 836/5869/4793 884/5924/4841 -f 836/5869/4793 837/5870/4794 885/5925/4842 -f 837/5870/4794 838/5871/4795 886/5926/4843 -f 838/5871/4795 839/5872/4796 887/5927/4844 -f 839/5872/4796 840/5873/4797 888/5928/4845 -f 840/5873/4797 841/9308/4798 889/5929/4846 -f 841/5930/4798 842/9307/4751 890/5876/4799 -f 938/5932/4847 890/5876/4799 843/5878/4800 -f 891/5933/4848 843/5878/4800 844/5880/4801 -f 892/5935/4849 844/5881/4801 845/5882/4802 -f 893/5936/4850 845/5882/4802 846/5883/4803 -f 894/5937/4851 846/5883/4803 847/5884/4804 -f 895/5938/4852 847/5884/4804 848/5885/4805 -f 896/5939/4853 848/5885/4805 849/5886/4806 -f 897/5940/4854 849/5886/4806 850/5887/4807 -f 898/5941/4855 850/5887/4807 851/5888/4808 -f 899/5942/4856 851/5888/4808 852/5889/4809 -f 900/357/273 852/5889/4809 853/5890/4810 -f 901/355/271 853/5890/4810 854/5891/4811 -f 902/5943/4857 854/5891/4811 855/5892/4812 -f 903/5944/4858 855/5892/4812 856/5893/4813 -f 856/5893/4813 857/5894/4814 905/5946/4860 -f 857/5894/4814 858/5895/4815 906/5947/4861 -f 858/5895/4815 859/5896/4816 907/5948/4862 -f 859/5896/4816 860/5897/4817 908/5949/4863 -f 860/5897/4817 861/5898/4818 909/5950/4864 -f 861/5898/4818 862/5899/4819 910/5951/4865 -f 862/5899/4819 863/5900/4820 911/5952/4866 -f 863/5900/4820 864/5901/4821 912/5953/4867 -f 864/5901/4821 865/5902/4822 913/5954/4868 -f 865/5904/4822 866/5903/4823 914/5955/4869 -f 914/5955/4869 866/5903/4823 867/5905/4824 -f 915/5957/4870 867/5905/4824 868/5906/4825 -f 916/5959/4871 868/5907/4825 869/5908/4826 -f 917/5960/4872 869/5908/4826 870/5909/4827 -f 918/5961/4873 870/5909/4827 871/5910/4828 -f 919/5962/4874 871/5910/4828 872/5911/4829 -f 920/5963/4875 872/5911/4829 873/5912/4830 -f 921/5964/4876 873/5912/4830 874/5913/4831 -f 922/5965/4877 874/5913/4831 875/5914/4832 -f 923/5966/4878 875/5914/4832 876/5915/4833 -f 876/5915/4833 877/5916/4834 925/360/276 -f 877/5916/4834 878/5917/4835 926/5967/4879 -f 878/5917/4835 879/5918/4836 927/5968/4880 -f 879/5918/4836 880/5919/4837 928/5969/4881 -f 880/5921/4837 881/5920/4838 929/5970/4882 -f 881/5920/4838 882/5922/4839 930/5972/4883 -f 882/5922/4839 883/5923/4840 931/5973/4884 -f 883/5923/4840 884/5924/4841 932/5974/4885 -f 884/5924/4841 885/5925/4842 933/5975/4886 -f 885/5925/4842 886/5926/4843 934/5976/4887 -f 886/5926/4843 887/5927/4844 935/5977/4888 -f 887/5927/4844 888/5928/4845 936/5978/4889 -f 888/5928/4845 889/5929/4846 937/5979/4890 -f 889/5931/4846 890/5876/4799 938/5932/4847 -f 984/5981/4891 938/5932/4847 891/5933/4848 -f 939/5982/4892 891/5933/4848 892/5934/4849 -f 940/5984/4893 892/5935/4849 893/5936/4850 -f 941/5985/4894 893/5936/4850 894/5937/4851 -f 942/5986/4895 894/5937/4851 895/5938/4852 -f 943/5987/4896 895/5938/4852 896/5939/4853 -f 944/5988/4897 896/5939/4853 897/5940/4854 -f 945/5989/4898 897/5940/4854 898/5941/4855 -f 946/363/279 898/5941/4855 899/5942/4856 -f 947/361/277 899/5942/4856 900/357/273 -f 948/356/272 901/355/271 902/5943/4857 -f 949/364/280 902/5943/4857 903/5944/4858 -f 950/5990/4899 903/5944/4858 904/5945/4859 -f 951/5991/4900 904/5945/4859 905/5946/4860 -f 905/5946/4860 906/5947/4861 953/367/283 -f 906/5947/4861 907/5948/4862 954/368/284 -f 907/5948/4862 908/5949/4863 955/5993/4902 -f 908/5949/4863 909/5950/4864 956/5994/4903 -f 909/5950/4864 910/5951/4865 957/5995/4904 -f 910/5951/4865 911/5952/4866 958/5996/4905 -f 911/5952/4866 912/5953/4867 959/5997/4906 -f 912/5953/4867 913/5954/4868 960/5998/4907 -f 913/5956/4868 914/5955/4869 961/5999/4908 -f 961/5999/4908 914/5955/4869 915/5957/4870 -f 962/6001/4909 915/5957/4870 916/5958/4871 -f 963/6003/4910 916/5959/4871 917/5960/4872 -f 964/6004/4911 917/5960/4872 918/5961/4873 -f 965/6005/4912 918/5961/4873 919/5962/4874 -f 966/6006/4913 919/5962/4874 920/5963/4875 -f 967/6007/4914 920/5963/4875 921/5964/4876 -f 968/6008/4915 921/5964/4876 922/5965/4877 -f 969/6009/4916 922/5965/4877 923/5966/4878 -f 970/370/286 923/5966/4878 924/359/275 -f 925/360/276 926/5967/4879 972/372/288 -f 926/5967/4879 927/5968/4880 973/6010/4917 -f 927/5968/4880 928/5969/4881 974/6011/290 -f 928/5971/4881 929/5970/4882 975/375/291 -f 929/5970/4882 930/5972/4883 976/377/293 -f 930/5972/4883 931/5973/4884 977/378/294 -f 931/5973/4884 932/5974/4885 978/6012/4918 -f 932/5974/4885 933/5975/4886 979/6013/4919 -f 933/5975/4886 934/5976/4887 980/6014/4920 -f 934/5976/4887 935/5977/4888 981/6015/4921 -f 935/5977/4888 936/5978/4889 982/6016/4922 -f 936/5978/4889 937/5979/4890 983/6017/4923 -f 937/5980/4890 938/5932/4847 984/5981/4891 -f 1021/6019/4924 984/5981/4891 939/5982/4892 -f 985/6020/4925 939/5982/4892 940/5983/4893 -f 986/6022/4926 940/5984/4893 941/5985/4894 -f 987/6023/4927 941/5985/4894 942/5986/4895 -f 988/6024/4928 942/5986/4895 943/5987/4896 -f 989/6025/4929 943/5987/4896 944/5988/4897 -f 990/381/297 944/5988/4897 945/5989/4898 -f 991/379/295 945/5989/4898 946/363/279 -f 992/362/278 947/361/277 948/356/272 -f 954/368/284 955/5993/4902 995/6026/4930 -f 955/5993/4902 956/5994/4903 996/6027/4931 -f 956/5994/4903 957/5995/4904 997/6028/4932 -f 957/5995/4904 958/5996/4905 998/6029/4933 -f 958/5996/4905 959/5997/4906 999/6030/4934 -f 959/5997/4906 960/5998/4907 1000/6031/4935 -f 960/6000/4907 961/5999/4908 1001/6032/4936 -f 1001/6032/4936 961/5999/4908 962/6001/4909 -f 1002/6034/4937 962/6001/4909 963/6002/4910 -f 1003/6036/4938 963/6003/4910 964/6004/4911 -f 1004/6037/4939 964/6004/4911 965/6005/4912 -f 1005/6038/4940 965/6005/4912 966/6006/4913 -f 1006/6039/4941 966/6006/4913 967/6007/4914 -f 1007/6040/4942 967/6007/4914 968/6008/4915 -f 1008/6041/4943 968/6008/4915 969/6009/4916 -f 1009/383/299 969/6009/4916 970/370/286 -f 972/372/288 973/6010/4917 1012/386/302 -f 973/6010/4917 974/6011/290 1013/6042/289 -f 975/375/291 976/377/293 1014/376/292 -f 977/378/294 978/6012/4918 1015/6043/4944 -f 978/6012/4918 979/6013/4919 1016/6044/4945 -f 979/6013/4919 980/6014/4920 1017/6045/4946 -f 980/6014/4920 981/6015/4921 1018/6046/4947 -f 981/6015/4921 982/6016/4922 1019/6047/4948 -f 982/6016/4922 983/6017/4923 1020/6048/4949 -f 983/6018/4923 984/5981/4891 1021/6019/4924 -f 1062/6050/4950 1021/6019/4924 985/6020/4925 -f 1022/6051/4951 985/6020/4925 986/6021/4926 -f 1023/6053/4952 986/6022/4926 987/6023/4927 -f 1024/6054/4953 987/6023/4927 988/6024/4928 -f 1025/6055/4954 988/6024/4928 989/6025/4929 -f 1026/6056/4955 989/6025/4929 990/381/297 -f 1027/380/296 991/379/295 992/362/278 -f 1028/389/305 992/362/278 993/365/281 -f 993/365/281 949/364/280 1031/403/319 -f 1031/403/319 949/364/280 950/5990/4899 -f 1032/401/317 950/5990/4899 951/5991/4900 -f 1033/406/322 951/5991/4900 952/5992/4901 -f 1034/404/320 952/5992/4901 994/366/282 -f 995/6026/4930 1037/391/307 1036/407/323 -f 995/6026/4930 996/6027/4931 1038/392/308 -f 996/6027/4931 997/6028/4932 1039/6057/4956 -f 997/6028/4932 998/6029/4933 1040/6058/4957 -f 998/6029/4933 999/6030/4934 1041/6059/4958 -f 999/6030/4934 1000/6031/4935 1042/6060/4959 -f 1000/6033/4935 1001/6032/4936 1043/6061/4960 -f 1043/6061/4960 1001/6032/4936 1002/6034/4937 -f 1044/6063/4961 1002/6034/4937 1003/6035/4938 -f 1045/6065/4962 1003/6036/4938 1004/6037/4939 -f 1046/6066/4963 1004/6037/4939 1005/6038/4940 -f 1047/6067/4964 1005/6038/4940 1006/6039/4941 -f 1048/6068/4965 1006/6039/4941 1007/6040/4942 -f 1049/395/311 1007/6040/4942 1008/6041/4943 -f 1050/393/309 1008/6041/4943 1009/383/299 -f 1012/386/302 1013/6042/289 1054/6069/4966 -f 1013/373/289 1014/376/292 1055/6070/4967 -f 1014/376/292 1015/6043/4944 1056/397/313 -f 1015/6043/4944 1016/6044/4945 1057/398/314 -f 1016/6044/4945 1017/6045/4946 1058/6072/4968 -f 1017/6045/4946 1018/6046/4947 1059/6073/4969 -f 1018/6046/4947 1019/6047/4948 1060/6074/4970 -f 1019/6047/4948 1020/6048/4949 1061/6075/4971 -f 1020/6049/4949 1021/6019/4924 1062/6050/4950 -f 1092/6077/4972 1062/6050/4950 1022/6051/4951 -f 1063/6078/4973 1022/6051/4951 1023/6052/4952 -f 1064/6080/4974 1023/6053/4952 1024/6054/4953 -f 1065/6081/4975 1024/6054/4953 1025/6055/4954 -f 1066/6082/4976 1025/6055/4954 1026/6056/4955 -f 1067/6083/4977 1026/6056/4955 1027/380/296 -f 1068/6084/4978 1027/380/296 1028/389/305 -f 1038/392/308 1039/6057/4956 1071/6085/4979 -f 1039/6057/4956 1040/6058/4957 1072/6086/4980 -f 1040/6058/4957 1041/6059/4958 1073/6087/4981 -f 1041/6059/4958 1042/6060/4959 1074/6088/4982 -f 1042/6062/4959 1043/6061/4960 1075/6089/4983 -f 1075/6089/4983 1043/6061/4960 1044/6063/4961 -f 1076/6091/4984 1044/6063/4961 1045/6064/4962 -f 1077/6093/4985 1045/6065/4962 1046/6066/4963 -f 1078/6094/4986 1046/6066/4963 1047/6067/4964 -f 1079/6095/4987 1047/6067/4964 1048/6068/4965 -f 1048/6068/4965 1049/395/311 1081/394/310 -f 1050/393/309 1051/382/298 1082/6097/4989 -f 1051/382/298 1052/384/300 1083/6098/4990 -f 1052/384/300 1053/385/301 1084/6099/4991 -f 1053/385/301 1054/6069/4966 1085/6100/4992 -f 1054/6071/4966 1055/6070/4967 1086/6101/4993 -f 1055/6070/4967 1056/397/313 1087/396/312 -f 1057/398/314 1058/6072/4968 1088/6103/4994 -f 1058/6072/4968 1059/6073/4969 1089/6104/4995 -f 1059/6073/4969 1060/6074/4970 1090/6105/4996 -f 1060/6074/4970 1061/6075/4971 1091/6106/4997 -f 1061/6076/4971 1062/6050/4950 1092/6077/4972 -f 1126/6108/4998 1092/6077/4972 1063/6078/4973 -f 1093/413/329 1063/6078/4973 1064/6079/4974 -f 1064/6080/4974 1065/6081/4975 1095/6109/4999 -f 1065/6081/4975 1066/6082/4976 1096/416/332 -f 1096/416/332 1066/6082/4976 1067/6083/4977 -f 1097/414/330 1067/6083/4977 1068/6084/4978 -f 1098/6111/5000 1068/6084/4978 1069/388/304 -f 1069/388/304 1029/387/303 1100/400/316 -f 1100/400/316 1030/399/315 1031/403/319 -f 1101/402/318 1032/401/317 1033/406/322 -f 1102/405/321 1034/404/320 1035/409/325 -f 1103/408/324 1036/407/323 1037/391/307 -f 1070/390/306 1071/6085/4979 1105/6113/5002 -f 1071/6085/4979 1072/6086/4980 1106/6114/5003 -f 1072/6086/4980 1073/6087/4981 1107/6115/5004 -f 1073/6087/4981 1074/6088/4982 1108/6116/5005 -f 1074/6090/4982 1075/6089/4983 1109/6117/5006 -f 1109/6117/5006 1075/6089/4983 1076/6091/4984 -f 1110/419/335 1076/6091/4984 1077/6092/4985 -f 1077/6093/4985 1078/6094/4986 1112/422/337 -f 1112/422/337 1078/6094/4986 1079/6095/4987 -f 1113/6119/5007 1079/6095/4987 1080/6096/4988 -f 1080/6096/4988 1081/394/310 1115/6121/5009 -f 1082/6097/4989 1116/6123/5011 1115/6121/5009 -f 1082/6097/4989 1083/6098/4990 1117/6122/5010 -f 1083/6098/4990 1084/6099/4991 1118/6124/5012 -f 1084/6099/4991 1085/6100/4992 1119/6125/5013 -f 1085/6102/4992 1086/6101/4993 1120/6126/5014 -f 1086/6101/4993 1087/396/312 1121/6128/5015 -f 1087/396/312 1088/6103/4994 1122/6129/5016 -f 1088/6103/4994 1089/6104/4995 1123/6130/5017 -f 1089/6104/4995 1090/6105/4996 1124/6131/5018 -f 1090/6105/4996 1091/6106/4997 1125/6132/5019 -f 1091/6107/4997 1092/6077/4972 1126/6108/4998 -f 1156/6134/5020 1126/6108/4998 1093/413/329 -f 1128/415/331 1097/414/330 1098/6111/5000 -f 1129/6135/5021 1098/6111/5000 1099/6112/5001 -f 1130/6136/5022 1099/6112/5001 1100/400/316 -f 1131/6137/5023 1100/400/316 1101/402/318 -f 1132/6138/5024 1101/402/318 1102/405/321 -f 1133/6139/5025 1102/405/321 1103/408/324 -f 1134/6140/5026 1103/408/324 1104/410/326 -f 1135/6141/5027 1104/410/326 1105/6113/5002 -f 1136/6142/5028 1105/6113/5002 1106/6114/5003 -f 1137/6143/5029 1106/6114/5003 1107/6115/5004 -f 1138/6144/5030 1107/6115/5004 1108/6116/5005 -f 1139/6146/5031 1108/6118/5005 1109/6117/5006 -f 1140/6147/5032 1109/6117/5006 1110/419/335 -f 1112/422/337 1113/6119/5007 1143/424/339 -f 1113/6119/5007 1114/6120/5008 1144/425/340 -f 1114/6120/5008 1115/6121/5009 1145/6148/5033 -f 1115/6121/5009 1116/6123/5011 1146/6149/5034 -f 1116/6123/5011 1117/6122/5010 1147/6150/5035 -f 1117/6122/5010 1118/6124/5012 1148/6151/5036 -f 1118/6124/5012 1119/6125/5013 1149/6152/5037 -f 1119/6127/5013 1120/6126/5014 1150/6153/5038 -f 1120/6126/5014 1121/6128/5015 1151/6155/5039 -f 1121/6128/5015 1122/6129/5016 1152/427/342 -f 1122/6129/5016 1123/6130/5017 1153/428/343 -f 1123/6130/5017 1124/6131/5018 1154/6156/5040 -f 1124/6131/5018 1125/6132/5019 1155/6157/5041 -f 1125/6133/5019 1126/6108/4998 1156/6134/5020 -f 1187/6159/5042 1156/6134/5020 1127/412/328 -f 1127/412/328 1094/411/327 1158/6161/346 -f 1158/431/346 1094/6110/327 1095/6109/4999 -f 1159/429/344 1095/6109/4999 1128/415/331 -f 1160/6162/5044 1128/415/331 1129/6135/5021 -f 1161/6163/5045 1129/6135/5021 1130/6136/5022 -f 1162/6164/5046 1130/6136/5022 1131/6137/5023 -f 1163/6165/5047 1131/6137/5023 1132/6138/5024 -f 1164/6166/5048 1132/6138/5024 1133/6139/5025 -f 1165/6167/5049 1133/6139/5025 1134/6140/5026 -f 1166/6168/5050 1134/6140/5026 1135/6141/5027 -f 1167/6169/5051 1135/6141/5027 1136/6142/5028 -f 1168/434/349 1136/6142/5028 1137/6143/5029 -f 1169/432/347 1137/6143/5029 1138/6144/5030 -f 1170/6170/5052 1138/6144/5030 1139/6145/5031 -f 1171/6172/5053 1139/6146/5031 1140/6147/5032 -f 1172/6173/5054 1140/6147/5032 1141/418/334 -f 1141/418/334 1111/417/333 1174/6175/5056 -f 1142/420/336 1175/6177/5057 1174/6176/5056 -f 1142/420/336 1143/424/339 1176/423/338 -f 1144/425/340 1145/6148/5033 1177/6178/5058 -f 1145/6148/5033 1146/6149/5034 1178/6179/5059 -f 1146/6149/5034 1147/6150/5035 1179/6180/5060 -f 1147/6150/5035 1148/6151/5036 1180/6181/5061 -f 1148/6151/5036 1149/6152/5037 1181/6182/5062 -f 1149/6154/5037 1150/6153/5038 1182/6183/5063 -f 1150/6153/5038 1151/6155/5039 1183/6185/5064 -f 1151/6155/5039 1152/427/342 1184/426/341 -f 1153/428/343 1154/6156/5040 1185/6186/5065 -f 1154/6187/5040 1155/6158/5041 1186/6188/5066 -f 1155/6158/5041 1156/6134/5020 1187/6159/5042 -f 1187/6159/5042 1157/6160/5043 1188/6190/5067 -f 1188/6190/5067 1157/6160/5043 1158/6161/346 -f 1190/6193/5069 1160/6162/5044 1161/6163/5045 -f 1191/6194/5070 1161/6163/5045 1162/6164/5046 -f 1192/6195/5071 1162/6164/5046 1163/6165/5047 -f 1193/6196/5072 1163/6165/5047 1164/6166/5048 -f 1194/6197/5073 1164/6166/5048 1165/6167/5049 -f 1195/6198/5074 1165/6167/5049 1166/6168/5050 -f 1196/6199/5075 1166/6168/5050 1167/6169/5051 -f 1197/6200/5076 1167/6169/5051 1168/434/349 -f 1198/433/348 1169/432/347 1170/6170/5052 -f 1199/6202/5077 1170/9309/5052 1171/6172/5053 -f 1200/6203/5078 1171/6172/5053 1172/6173/5054 -f 1201/6204/5079 1172/6173/5054 1173/6174/5055 -f 1173/6174/5055 1174/6175/5056 1203/6206/5081 -f 1174/6176/5056 1175/6177/5057 1204/6207/5082 -f 1175/6177/5057 1176/423/338 1205/6209/5083 -f 1176/423/338 1177/6178/5058 1206/6210/5084 -f 1177/6178/5058 1178/6179/5059 1207/6211/5085 -f 1178/6179/5059 1179/6180/5060 1208/6212/5086 -f 1179/6180/5060 1180/6181/5061 1209/6213/5087 -f 1180/6181/5061 1181/6182/5062 1210/6214/5088 -f 1181/6184/5062 1182/6183/5063 1211/6215/5089 -f 1182/6183/5063 1183/6185/5064 1212/6217/5090 -f 1183/6185/5064 1184/426/341 1213/6218/5091 -f 1184/6219/341 1185/6222/5065 1214/6220/5092 -f 1185/6222/5065 1186/6224/5066 1215/6223/5093 -f 1186/6224/5066 1187/9310/5042 1216/6225/5068 -f 1216/6225/5068 1188/9311/5067 1217/6226/5094 -f 1217/6228/5094 1188/6190/5067 1189/6192/345 -f 1189/430/345 1159/429/344 1219/6230/5097 -f 1219/6230/5097 1159/429/344 1190/6193/5069 -f 1220/6232/5098 1190/6193/5069 1191/6194/5070 -f 1221/6233/5099 1191/6194/5070 1192/6195/5071 -f 1222/6234/5100 1192/6195/5071 1193/6196/5072 -f 1223/6235/5101 1193/6196/5072 1194/6197/5073 -f 1224/6236/5102 1194/6197/5073 1195/6198/5074 -f 1225/6237/5103 1195/6198/5074 1196/6199/5075 -f 1226/6238/5104 1196/6199/5075 1197/6200/5076 -f 1227/6239/5105 1197/6200/5076 1198/433/348 -f 1228/6241/5106 1198/9312/348 1199/6242/5077 -f 1229/6243/5107 1199/6242/5077 1200/6244/5078 -f 1230/6246/5108 1200/6203/5078 1201/6204/5079 -f 1231/6247/5109 1201/6204/5079 1202/6205/5080 -f 1202/6205/5080 1203/6206/5081 1233/6249/5111 -f 1203/6208/5081 1204/6207/5082 1234/6250/5112 -f 1204/6207/5082 1205/6209/5083 1235/436/351 -f 1205/6209/5083 1206/6210/5084 1236/437/352 -f 1206/6210/5084 1207/6211/5085 1237/439/354 -f 1207/6211/5085 1208/6212/5086 1238/6252/356 -f 1208/6212/5086 1209/6213/5087 1239/6253/358 -f 1209/6213/5087 1210/6214/5088 1240/6254/360 -f 1210/6216/5088 1211/6215/5089 1241/6255/362 -f 1211/6257/5089 1212/6258/5090 1242/451/364 -f 1212/6258/5090 1213/6221/5091 1243/453/366 -f 1213/6221/5091 1214/6220/5092 1244/455/368 -f 1214/6220/5092 1215/6223/5093 1245/6259/5113 -f 1215/6223/5093 1216/6225/5068 1246/6227/5095 -f 1246/6227/5095 1217/6226/5094 1247/6260/5114 -f 1247/6262/5114 1217/9313/5094 1218/6231/5096 -f 1218/6231/5096 1219/6230/5097 1249/458/371 -f 1249/458/371 1219/6230/5097 1220/6232/5098 -f 1250/456/369 1220/6232/5098 1221/6233/5099 -f 1251/459/372 1221/6233/5099 1222/6234/5100 -f 1252/461/374 1222/6234/5100 1223/6235/5101 -f 1253/6264/376 1223/6235/5101 1224/6236/5102 -f 1254/6265/378 1224/6236/5102 1225/6237/5103 -f 1255/468/380 1225/9314/5103 1226/6267/5104 -f 1256/470/382 1226/6267/5104 1227/6268/5105 -f 1257/472/384 1227/6268/5105 1228/6241/5106 -f 1258/474/386 1228/6241/5106 1229/6243/5107 -f 1259/476/388 1229/6243/5107 1230/6245/5108 -f 1260/6269/5117 1230/6245/5108 1231/6270/5109 -f 1261/6272/5118 1231/6247/5109 1232/6248/5110 -f 1232/6248/5110 1233/6249/5111 1263/6274/5120 -f 1233/6251/5111 1234/6250/5112 1264/6275/5121 -f 1234/6250/5112 1235/436/351 1265/6277/5122 -f 1244/455/368 1245/6259/5113 1275/6278/5123 -f 1245/6259/5113 1246/6227/5095 1276/6261/5115 -f 1276/6261/5115 1247/6260/5114 1277/6281/5124 -f 1287/477/389 1259/476/388 1260/6269/5117 -f 1288/6282/5125 1260/6269/5117 1261/6271/5118 -f 1289/6286/5126 1261/6272/5118 1262/6273/5119 -f 1290/6287/5127 1262/6273/5119 1263/6274/5120 -f 1263/6274/5120 1264/9315/5121 1292/6289/5129 -f 1264/6275/5121 1265/6277/5122 1293/6290/5130 -f 1265/6277/5122 1266/435/350 1294/6292/5131 -f 1266/6293/350 1267/6279/353 1295/6294/5132 -f 1267/6279/353 1268/440/355 1296/6296/5133 -f 1268/440/355 1269/443/357 1297/6297/5134 -f 1269/443/357 1270/445/359 1298/6298/5135 -f 1270/6280/359 1271/447/361 1299/6299/5136 -f 1271/447/361 1272/450/363 1300/6301/5137 -f 1272/450/363 1273/452/365 1301/6302/5138 -f 1273/452/365 1274/454/367 1302/6303/5139 -f 1274/454/367 1275/6278/5123 1303/479/391 -f 1275/6278/5123 1276/6261/5115 1304/480/392 -f 1304/480/392 1277/6281/5124 1305/6304/394 -f 1277/6283/5124 1248/6263/5116 1306/6305/5140 -f 1306/6305/5140 1248/6263/5116 1278/457/370 -f 1308/491/402 1278/457/370 1279/460/373 -f 1309/6306/5141 1279/460/373 1280/462/375 -f 1310/6308/5142 1280/6284/375 1281/464/377 -f 1311/6309/5143 1281/464/377 1282/467/379 -f 1312/6310/5144 1282/467/379 1283/469/381 -f 1313/6311/5145 1283/469/381 1284/471/383 -f 1314/6312/5146 1284/471/383 1285/473/385 -f 1315/6313/5147 1285/473/385 1286/475/387 -f 1316/6314/5148 1286/475/387 1287/477/389 -f 1317/6315/5149 1287/477/389 1288/6282/5125 -f 1318/486/397 1288/6282/5125 1289/6285/5126 -f 1319/484/395 1289/6285/5126 1290/6316/5127 -f 1320/6317/398 1290/6287/5127 1291/6288/5128 -f 1292/6289/5129 1322/6346/400 1321/6318/5150 -f 1292/6291/5129 1293/6290/5130 1323/490/401 -f 1293/6319/5130 1294/6295/5131 1324/6320/5151 -f 1294/6295/5131 1295/6294/5132 1325/6322/5152 -f 1295/6294/5132 1296/6296/5133 1326/6323/5153 -f 1296/6296/5133 1297/6297/5134 1327/6324/5154 -f 1297/6297/5134 1298/6298/5135 1328/6325/5155 -f 1298/6300/5135 1299/6299/5136 1329/6326/5156 -f 1299/6299/5136 1300/6301/5137 1330/6328/5157 -f 1300/6301/5137 1301/6302/5138 1331/6329/5158 -f 1301/6302/5138 1302/6303/5139 1332/6330/5159 -f 1302/6303/5139 1303/479/391 1333/478/390 -f 1335/6331/5160 1309/9316/5141 1310/6308/5142 -f 1336/6332/5161 1310/6308/5142 1311/6309/5143 -f 1337/6334/5162 1311/6309/5143 1312/6310/5144 -f 1338/6335/5163 1312/6310/5144 1313/6311/5145 -f 1339/6336/5164 1313/6311/5145 1314/6312/5146 -f 1340/6337/5165 1314/6312/5146 1315/6313/5147 -f 1341/6338/5166 1315/6313/5147 1316/6314/5148 -f 1342/6339/5167 1316/6314/5148 1317/6315/5149 -f 1343/6340/5168 1317/6315/5149 1318/486/397 -f 1323/6321/401 1324/6320/5151 1346/496/406 -f 1324/6320/5151 1325/6322/5152 1347/498/408 -f 1325/6322/5152 1326/6323/5153 1348/500/410 -f 1326/6323/5153 1327/6324/5154 1349/502/412 -f 1327/6324/5154 1328/6325/5155 1350/504/414 -f 1328/6327/5155 1329/6326/5156 1351/507/416 -f 1329/6326/5156 1330/6328/5157 1352/509/418 -f 1330/6328/5157 1331/6329/5158 1353/511/420 -f 1331/6329/5158 1332/6330/5159 1354/513/422 -f 1332/6330/5159 1333/478/390 1355/515/424 -f 1334/481/393 1306/6305/5140 1357/6341/5169 -f 1306/6305/5140 1307/493/404 1358/492/403 -f 1358/492/403 1308/491/402 1335/6333/5160 -f 1359/517/425 1335/6331/5160 1336/6332/5161 -f 1360/520/427 1336/6332/5161 1337/6334/5162 -f 1361/522/429 1337/6334/5162 1338/6335/5163 -f 1362/524/431 1338/6335/5163 1339/6336/5164 -f 1363/526/433 1339/6336/5164 1340/6337/5165 -f 1364/528/435 1340/6337/5165 1341/6338/5166 -f 1365/530/437 1341/6338/5166 1342/6339/5167 -f 1366/532/439 1342/6339/5167 1343/6340/5168 -f 1367/534/441 1343/6340/5168 1344/485/396 -f 1344/485/396 1320/487/398 1369/538/445 -f 1369/6344/445 1320/6317/398 1321/6318/5150 -f 1370/6345/5170 1321/6318/5150 1322/6346/400 -f 1345/6348/399 1372/9317/405 1371/6347/5171 -f 1356/6342/423 1357/6341/5169 1383/6349/5172 -f 1357/6341/5169 1358/492/403 1384/6352/5174 -f 1395/6353/446 1369/9318/445 1370/6354/5170 -f 1396/6355/5175 1370/6354/5170 1371/6357/5171 -f 1371/6357/5171 1372/9319/405 1398/6359/5177 -f 1372/494/405 1373/497/407 1399/6360/5178 -f 1373/497/407 1374/499/409 1400/6362/5179 -f 1374/499/409 1375/501/411 1401/6363/5180 -f 1375/501/411 1376/503/413 1402/6364/5181 -f 1376/503/413 1377/6351/415 1403/6365/5182 -f 1377/505/415 1378/508/417 1404/6366/5183 -f 1378/508/417 1379/510/419 1405/6368/5184 -f 1379/510/419 1380/512/421 1406/6369/5185 -f 1380/512/421 1381/541/448 1407/540/447 -f 1356/514/423 1382/9320/5173 1407/540/447 -f 1382/6350/5173 1383/6349/5172 1409/6370/5186 -f 1383/6349/5172 1384/6352/5174 1410/6372/5188 -f 1410/6372/5188 1384/6352/5174 1385/6356/426 -f 1411/6374/5189 1385/518/426 1386/521/428 -f 1412/6375/5190 1386/521/428 1387/523/430 -f 1413/6376/5191 1387/523/430 1388/525/432 -f 1414/6377/5192 1388/525/432 1389/527/434 -f 1415/6378/5193 1389/527/434 1390/529/436 -f 1416/6379/5194 1390/529/436 1391/531/438 -f 1417/6380/5195 1391/531/438 1392/533/440 -f 1418/6381/5196 1392/533/440 1393/535/442 -f 1419/6382/5197 1393/535/442 1394/537/444 -f 1420/6383/5198 1394/537/444 1395/539/446 -f 1421/6385/5199 1395/6353/446 1396/6355/5175 -f 1422/6386/5200 1396/6355/5175 1397/6358/5176 -f 1397/6388/5176 1398/9321/5177 1424/6389/5202 -f 1398/6361/5177 1399/6360/5178 1426/6391/5203 -f 1399/6360/5178 1400/6362/5179 1427/6394/5205 -f 1400/6362/5179 1401/6363/5180 1428/6395/5206 -f 1401/6363/5180 1402/6364/5181 1429/6396/5207 -f 1402/6364/5181 1403/6365/5182 1430/6397/5208 -f 1403/6367/5182 1404/6366/5183 1431/6398/5209 -f 1404/6366/5183 1405/6368/5184 1432/6400/5210 -f 1405/6368/5184 1406/6369/5185 1433/6401/5211 -f 1406/6369/5185 1407/540/447 1434/6402/5212 -f 1407/6403/447 1382/6350/5173 1408/6371/5187 -f 1409/6370/5186 1436/543/450 1435/589/493 -f 1409/6370/5186 1410/6372/5188 1437/544/451 -f 1410/6405/5188 1411/9322/5189 1438/6406/5213 -f 1439/547/454 1411/6374/5189 1412/6375/5190 -f 1440/545/452 1412/6375/5190 1413/6376/5191 -f 1441/548/455 1413/6376/5191 1414/6377/5192 -f 1442/550/457 1414/6377/5192 1415/6378/5193 -f 1443/6409/459 1415/6378/5193 1416/6379/5194 -f 1444/6410/461 1416/6379/5194 1417/6380/5195 -f 1445/6411/463 1417/6380/5195 1418/6381/5196 -f 1446/6412/465 1418/6381/5196 1419/6382/5197 -f 1447/561/467 1419/9323/5197 1420/6414/5198 -f 1448/563/469 1420/6414/5198 1421/6385/5199 -f 1449/565/471 1421/6385/5199 1422/6386/5200 -f 1422/6386/5200 1423/6387/5201 1451/6415/5214 -f 1451/6416/5214 1423/6390/5201 1424/6389/5202 -f 1424/6389/5202 1425/9324/5204 1453/6418/5216 -f 1425/6392/5204 1426/6391/5203 1454/570/476 -f 1426/6391/5203 1427/6394/5205 1455/571/477 -f 1427/6394/5205 1428/6395/5206 1456/573/479 -f 1428/6395/5206 1429/6396/5207 1457/6420/481 -f 1429/6396/5207 1430/6397/5208 1458/6421/483 -f 1430/6399/5208 1431/6398/5209 1459/6422/485 -f 1431/6398/5209 1432/6400/5210 1460/6424/487 -f 1432/6425/5210 1433/6426/5211 1461/585/489 -f 1433/6426/5211 1434/6404/5212 1462/587/491 -f 1434/6404/5212 1408/6371/5187 1435/589/493 -f 1437/6407/451 1438/6406/5213 1465/6427/5217 -f 1465/6427/5217 1438/6406/5213 1439/6430/454 -f 1477/6432/5219 1451/6416/5214 1452/6417/5215 -f 1452/6417/5215 1453/6418/5216 1479/6436/5221 -f 1453/6419/5216 1454/570/476 1480/6437/5222 -f 1489/588/492 1463/6429/5218 1490/6439/5223 -f 1463/6429/5218 1436/543/450 1491/591/495 -f 1464/542/449 1492/592/496 1491/591/495 -f 1464/6428/449 1465/6427/5217 1493/6443/5225 -f 1493/6443/5225 1465/6427/5217 1466/6431/453 -f 1495/6446/5227 1466/546/453 1467/549/456 -f 1496/6449/5228 1467/9325/456 1468/6450/458 -f 1497/6451/5229 1468/6450/458 1469/553/460 -f 1498/6452/5230 1469/553/460 1470/556/462 -f 1499/6453/5231 1470/556/462 1471/558/464 -f 1500/6454/5232 1471/558/464 1472/560/466 -f 1501/6455/5233 1472/560/466 1473/562/468 -f 1502/6456/5234 1473/562/468 1474/564/470 -f 1503/6457/5235 1474/564/470 1475/566/472 -f 1504/595/499 1475/566/472 1476/568/474 -f 1476/568/474 1450/567/473 1506/6458/5236 -f 1506/6458/5236 1450/567/473 1477/6435/5219 -f 1507/6460/5237 1477/6432/5219 1478/6433/5220 -f 1478/6433/5220 1479/6436/5221 1509/6462/5239 -f 1479/6436/5221 1480/9326/5222 1510/6463/5240 -f 1480/6464/5222 1481/6467/475 1511/6465/5241 -f 1481/6467/475 1482/6441/478 1512/6468/5242 -f 1482/6441/478 1483/574/480 1513/6469/5243 -f 1483/574/480 1484/577/482 1514/6470/5244 -f 1484/6442/482 1485/579/484 1515/6471/5245 -f 1485/579/484 1486/582/486 1516/6473/5246 -f 1486/582/486 1487/584/488 1517/6474/5247 -f 1487/584/488 1488/586/490 1518/6475/5248 -f 1488/586/490 1489/588/492 1519/6440/5224 -f 1490/6439/5223 1520/597/501 1546/6476/5249 -f 1492/6444/496 1493/6443/5225 1522/6477/5250 -f 1522/6477/5250 1493/6443/5225 1494/6445/5226 -f 1523/6479/5251 1494/6445/5226 1495/6448/5227 -f 1524/600/504 1495/9327/5227 1496/6449/5228 -f 1525/598/502 1496/6449/5228 1497/6451/5229 -f 1526/601/505 1497/6451/5229 1498/6452/5230 -f 1527/603/507 1498/6452/5230 1499/6453/5231 -f 1528/605/509 1499/6453/5231 1500/6454/5232 -f 1529/607/511 1500/6454/5232 1501/6455/5233 -f 1530/609/513 1501/6455/5233 1502/6456/5234 -f 1531/6481/5252 1502/6456/5234 1503/6457/5235 -f 1532/6482/5253 1503/6457/5235 1504/595/499 -f 1534/6483/5254 1507/6460/5237 1508/6461/5238 -f 1508/6461/5238 1509/6462/5239 1536/6486/5256 -f 1509/6462/5239 1510/6463/5240 1537/6487/516 -f 1510/6466/5240 1511/6465/5241 1538/613/517 -f 1511/6465/5241 1512/6468/5242 1539/615/519 -f 1512/6468/5242 1513/6469/5243 1540/617/521 -f 1513/6469/5243 1514/6470/5244 1541/619/523 -f 1514/6472/5244 1515/6471/5245 1542/622/525 -f 1515/6471/5245 1516/6473/5246 1543/6488/5257 -f 1516/6473/5246 1517/6474/5247 1544/6489/5258 -f 1517/6474/5247 1518/6475/5248 1545/6490/5259 -f 1518/6475/5248 1519/6440/5224 1546/6476/5249 -f 1521/590/494 1548/6512/527 1547/596/500 -f 1521/6478/494 1522/6477/5250 1549/625/528 -f 1549/625/528 1522/6477/5250 1523/6479/5251 -f 1550/6492/5261 1523/6479/5251 1524/6480/504 -f 1530/609/513 1531/6481/5252 1557/6494/5262 -f 1557/6494/5262 1531/6481/5252 1532/6482/5253 -f 1558/6495/5263 1532/6482/5253 1533/594/498 -f 1533/594/498 1505/593/497 1560/6497/5265 -f 1560/6497/5265 1505/593/497 1506/6458/5236 -f 1561/632/535 1506/6458/5236 1534/6485/5254 -f 1562/6498/536 1534/6483/5254 1535/6484/5255 -f 1535/6484/5255 1536/6486/5256 1564/6500/5267 -f 1536/6486/5256 1537/6487/516 1565/6501/515 -f 1542/622/525 1543/6488/5257 1570/6502/5268 -f 1543/6488/5257 1544/6489/5258 1571/6503/5269 -f 1544/6489/5258 1545/6490/5259 1572/6504/5270 -f 1545/6490/5259 1546/6476/5249 1573/6491/5260 -f 1573/6491/5260 1520/597/501 1574/640/542 -f 1575/623/526 1549/625/528 1550/6492/5261 -f 1576/6505/5271 1550/6492/5261 1551/6493/503 -f 1557/6494/5262 1584/647/549 1583/645/547 -f 1557/6494/5262 1558/6495/5263 1585/649/551 -f 1558/6495/5263 1559/6496/5264 1586/651/553 -f 1562/6498/536 1563/6499/5266 1588/6508/5273 -f 1588/6508/5273 1563/6499/5266 1564/6500/5267 -f 1589/6510/5274 1564/6500/5267 1565/6501/515 -f 1570/6502/5268 1596/658/559 1595/655/557 -f 1570/6502/5268 1571/6503/5269 1597/660/561 -f 1571/6503/5269 1572/6504/5270 1598/662/563 -f 1572/6504/5270 1573/6491/5260 1599/678/579 -f 1548/6512/527 1601/672/573 1600/639/541 -f 1575/623/526 1602/6514/5276 1601/688/573 -f 1575/623/526 1576/6505/5271 1603/6513/5275 -f 1576/6505/5271 1577/6506/5272 1604/6515/5277 -f 1577/6507/5272 1578/626/529 1605/6516/5278 -f 1606/691/591 1578/626/529 1579/627/530 -f 1586/651/553 1560/6497/5265 1616/666/567 -f 1560/6497/5265 1561/632/535 1617/667/568 -f 1636/680/581 1599/678/579 1574/640/542 -f 1617/667/568 1587/631/534 1628/715/607 -f 1587/6509/534 1588/6508/5273 1629/709/608 -f 1629/709/608 1588/6508/5273 1589/6510/5274 -f 1589/6510/5274 1590/6511/571 1631/6521/570 -f 1632/677/578 1620/653/555 1621/654/556 -f 1633/698/598 1621/654/556 1622/6519/558 -f 1634/700/596 1622/656/558 1623/659/560 -f 1639/6522/5281 1618/668/569 1619/652/554 -f 1642/701/600 1624/661/562 1625/663/564 -f 1625/663/564 1598/662/563 1644/679/580 -f 1602/6514/5276 1603/6513/5275 1646/6524/5283 -f 1604/6515/5277 1647/6531/5284 1646/6524/5283 -f 1605/6516/5278 1648/690/590 1647/6525/5284 -f 1613/648/550 1614/650/552 1653/685/586 -f 1606/691/591 1607/6518/5279 1657/721/619 -f 1607/6518/5279 1608/641/543 1658/723/621 -f 1608/641/543 1609/642/544 1660/6527/5285 -f 1609/642/544 1610/643/545 1661/6528/5286 -f 1639/6522/5281 1640/6523/5282 1665/738/634 -f 1665/738/634 1640/6523/5282 1641/676/577 -f 1641/676/577 1632/677/578 1668/695/595 -f 1676/707/606 1629/709/608 1630/6520/5280 -f 1630/6520/5280 1631/6521/570 1678/735/610 -f 1645/686/587 1646/6524/5283 1680/6530/5287 -f 1647/6531/5284 1681/9328/628 1680/6530/5287 -f 1648/690/590 1682/720/618 1681/730/628 -f 1685/745/641 1637/673/574 1655/6526/588 -f 1657/721/619 1658/723/621 1688/722/620 -f 1659/724/622 1660/6527/5285 1689/754/648 -f 1660/6527/5285 1661/6528/5286 1690/756/650 -f 1661/6528/5286 1649/681/582 1691/725/623 -f 1679/717/615 1680/6530/5287 1697/749/644 -f 1680/6530/5287 1681/9328/628 1698/750/627 -f 1683/712/611 1654/684/585 1700/765/658 -f 1702/733/631 1676/707/606 1677/737/633 -f 1678/711/610 1664/710/609 1704/739/635 -f 1704/739/635 1665/738/634 1666/742/638 -f 1705/741/637 1667/740/636 1668/695/595 -f 1706/743/639 1662/694/594 1669/697/597 -f 1707/6535/5289 1669/6529/597 1670/699/599 -f 1708/6536/5290 1670/699/599 1671/702/601 -f 1709/6537/5291 1671/702/601 1672/704/603 -f 1710/6538/5292 1672/704/603 1673/705/604 -f 1732/6539/5293 1695/716/614 1675/706/605 -f 1722/762/655 1702/733/631 1703/736/632 -f 1723/6542/5294 1703/6533/632 1704/739/635 -f 1724/6543/5295 1704/739/635 1705/741/637 -f 1725/6544/5296 1705/741/637 1706/743/639 -f 1726/6545/5297 1706/743/639 1707/6534/5289 -f 1727/6547/5298 1707/6535/5289 1708/6536/5290 -f 1728/6548/5299 1708/6536/5290 1709/6537/5291 -f 1729/6549/5300 1709/6537/5291 1710/6538/5292 -f 1730/6550/5301 1710/6538/5292 1711/744/640 -f 1731/6551/5302 1711/744/640 1695/716/614 -f 1758/769/662 1732/6539/5293 1712/746/642 -f 1734/778/663 1686/718/616 1696/6532/5288 -f 1696/6532/5288 1697/749/644 1736/763/656 -f 1713/6541/643 1714/751/645 1737/6553/5304 -f 1714/751/645 1715/752/646 1738/6555/5305 -f 1715/752/646 1716/753/647 1739/6556/5306 -f 1716/753/647 1717/755/649 1740/6557/5307 -f 1717/755/649 1718/757/651 1741/6558/5308 -f 1718/757/651 1719/758/652 1742/6559/5309 -f 1719/758/652 1720/759/653 1743/6560/5310 -f 1720/759/653 1721/760/654 1744/6561/5311 -f 1721/760/654 1699/731/629 1745/764/657 -f 1700/765/658 1694/728/626 1746/766/659 -f 1722/761/655 1747/6592/666 1746/766/659 -f 1748/774/667 1722/762/655 1723/6540/5294 -f 1749/6563/5312 1723/6542/5294 1724/6543/5295 -f 1750/6564/5313 1724/6543/5295 1725/6544/5296 -f 1751/6565/5314 1725/6544/5296 1726/6545/5297 -f 1752/6566/5315 1726/6545/5297 1727/6546/5298 -f 1753/6568/5316 1727/6547/5298 1728/6548/5299 -f 1754/6569/5317 1728/6548/5299 1729/6549/5300 -f 1755/6570/5318 1729/6549/5300 1730/6550/5301 -f 1756/6571/5319 1730/6550/5301 1731/6551/5302 -f 1757/6572/5320 1731/6551/5302 1732/6539/5293 -f 1735/6552/5303 1736/763/656 1761/6573/5321 -f 1736/6574/656 1737/9329/5304 1762/6575/5322 -f 1737/6553/5304 1738/6555/5305 1763/6577/5323 -f 1738/6555/5305 1739/6556/5306 1764/6579/5324 -f 1739/6556/5306 1740/6557/5307 1765/6580/5325 -f 1740/6557/5307 1741/6558/5308 1766/6581/5326 -f 1741/6558/5308 1742/6559/5309 1767/6582/5327 -f 1742/6559/5309 1743/6560/5310 1768/6583/5328 -f 1743/6560/5310 1744/6561/5311 1769/6584/5329 -f 1744/6561/5311 1745/764/657 1770/6585/5330 -f 1746/766/659 1771/780/672 1770/6585/5330 -f 1772/772/665 1748/774/667 1749/6562/5312 -f 1773/785/673 1749/9330/5312 1750/6586/5313 -f 1774/6587/675 1750/6564/5313 1751/6565/5314 -f 1775/6588/677 1751/6565/5314 1752/6566/5315 -f 1776/792/679 1752/6566/5315 1753/6567/5316 -f 1777/795/681 1753/6568/5316 1754/6569/5317 -f 1778/793/683 1754/6569/5317 1755/6570/5318 -f 1779/796/685 1755/6570/5318 1756/6571/5319 -f 1780/798/687 1756/6571/5319 1757/6572/5320 -f 1781/800/689 1757/6572/5320 1758/769/662 -f 1760/776/669 1761/6573/5321 1785/804/693 -f 1761/6576/5321 1762/6575/5322 1786/807/695 -f 1762/6575/5322 1763/9331/5323 1787/809/697 -f 1763/6577/5323 1764/6579/5324 1788/6590/699 -f 1764/6579/5324 1765/6580/5325 1789/815/701 -f 1765/6580/5325 1766/6581/5326 1790/816/703 -f 1766/6581/5326 1767/6582/5327 1791/818/705 -f 1767/6582/5327 1768/6583/5328 1792/820/707 -f 1768/6583/5328 1769/6584/5329 1793/822/709 -f 1769/6584/5329 1770/6585/5330 1794/824/711 -f 1747/6592/666 1796/9332/5331 1795/779/671 -f 1772/772/665 1797/782/674 1796/6593/5331 -f 1807/828/715 1783/775/668 1759/771/664 -f 1808/826/713 1759/771/664 1784/6589/670 -f 1821/6598/5332 1797/6594/674 1798/784/676 -f 1822/6599/5333 1798/784/676 1799/787/678 -f 1823/6603/5334 1799/787/678 1800/789/680 -f 1824/6604/5335 1800/789/680 1801/6605/682 -f 1825/6606/5336 1801/6605/682 1802/6607/684 -f 1826/6609/5337 1802/9333/684 1803/6610/686 -f 1827/6612/5338 1803/797/686 1804/799/688 -f 1828/6613/5339 1804/799/688 1805/801/690 -f 1829/6614/5340 1805/801/690 1806/802/691 -f 1830/6615/5341 1806/802/691 1783/775/668 -f 1810/6600/692 1811/805/694 1833/6616/5342 -f 1811/805/694 1812/808/696 1834/6618/5343 -f 1812/808/696 1813/810/698 1835/6619/5344 -f 1813/810/698 1814/812/700 1836/6620/5345 -f 1814/812/700 1815/6601/702 1837/6621/5346 -f 1815/6601/702 1816/6623/704 1838/6622/5347 -f 1816/6623/704 1817/9334/706 1839/6624/5348 -f 1817/819/706 1818/821/708 1840/6625/5349 -f 1818/821/708 1819/823/710 1841/6627/5350 -f 1819/823/710 1820/825/712 1842/6628/5351 -f 1820/825/712 1795/779/671 1843/6629/5352 -f 1795/779/671 1796/9332/5331 1844/6630/5353 -f 1844/6631/5353 1796/6593/5331 1821/6602/5332 -f 1845/6633/5354 1821/6598/5332 1822/6599/5333 -f 1846/6634/5355 1822/6599/5333 1823/6603/5334 -f 1847/6635/5356 1823/6603/5334 1824/6604/5335 -f 1848/6636/5357 1824/6604/5335 1825/6606/5336 -f 1849/6637/5358 1825/6606/5336 1826/6608/5337 -f 1850/6639/5359 1826/6609/5337 1827/6611/5338 -f 1851/6640/5360 1827/6611/5338 1828/6641/5339 -f 1852/6642/5361 1828/6641/5339 1829/6643/5340 -f 1853/6645/5362 1829/6614/5340 1830/6615/5341 -f 1854/6646/5363 1830/6615/5341 1807/828/715 -f 1855/833/720 1831/827/714 1808/826/713 -f 1856/831/718 1808/826/713 1809/6597/717 -f 1809/830/717 1832/829/716 1858/836/723 -f 1832/6617/716 1833/6616/5342 1859/6648/5364 -f 1833/6616/5342 1834/6618/5343 1860/6650/5365 -f 1834/6618/5343 1835/6619/5344 1861/6651/5366 -f 1835/6619/5344 1836/6620/5345 1862/6652/5367 -f 1836/6620/5345 1837/6621/5346 1863/838/725 -f 1837/6621/5346 1838/6622/5347 1864/839/726 -f 1838/6622/5347 1839/6624/5348 1865/6653/5368 -f 1839/6624/5348 1840/9335/5349 1866/6654/5369 -f 1840/6625/5349 1841/6627/5350 1867/6655/5370 -f 1841/6627/5350 1842/6628/5351 1868/6657/5371 -f 1869/6658/5372 1845/6633/5354 1846/6634/5355 -f 1870/6659/5373 1846/6634/5355 1847/6635/5356 -f 1871/6661/5374 1847/6635/5356 1848/6636/5357 -f 1872/6662/5375 1848/6636/5357 1849/6637/5358 -f 1873/6663/5376 1849/6637/5358 1850/6638/5359 -f 1874/842/729 1850/6639/5359 1851/6640/5360 -f 1875/840/727 1851/6640/5360 1852/6642/5361 -f 1876/6665/5377 1852/6642/5361 1853/6644/5362 -f 1877/6666/5378 1853/6644/5362 1854/6667/5363 -f 1878/6669/5379 1854/6646/5363 1831/827/714 -f 1858/6649/723 1859/6648/5364 1881/846/732 -f 1859/6648/5364 1860/6650/5365 1882/848/734 -f 1860/6650/5365 1861/6651/5366 1883/850/736 -f 1861/6651/5366 1862/6652/5367 1884/852/738 -f 1862/6652/5367 1863/838/725 1885/837/724 -f 1864/839/726 1865/6653/5368 1886/855/741 -f 1865/6653/5368 1866/6654/5369 1887/857/743 -f 1866/6654/5369 1867/9336/5370 1888/859/745 -f 1867/6655/5370 1868/6657/5371 1889/6670/747 -f 1843/6629/5352 1890/6673/5381 1889/6670/747 -f 1843/6629/5352 1844/6630/5353 1891/6672/5380 -f 1891/6672/5380 1844/6630/5353 1869/6674/5372 -f 1892/864/750 1869/6658/5372 1870/6659/5373 -f 1893/862/748 1870/6659/5373 1871/6661/5374 -f 1894/865/751 1871/6661/5374 1872/6662/5375 -f 1895/867/753 1872/6662/5375 1873/6663/5376 -f 1896/869/755 1873/6663/5376 1874/6664/729 -f 1897/841/728 1875/840/727 1876/6665/5377 -f 1898/873/758 1876/6665/5377 1877/6666/5378 -f 1899/875/760 1877/6666/5378 1878/6668/5379 -f 1900/6676/762 1878/6669/5379 1855/833/720 -f 1856/831/718 1857/6647/722 1902/6677/730 -f 1921/6678/764 1879/832/719 1922/882/766 -f 1901/881/765 1902/6677/730 1923/887/770 -f 1902/6680/730 1903/844/731 1924/6684/5384 -f 1903/844/731 1904/847/733 1925/6686/5385 -f 1904/847/733 1905/849/735 1926/6687/5386 -f 1905/849/735 1906/851/737 1927/6688/5387 -f 1906/851/737 1907/853/739 1928/883/767 -f 1908/854/740 1909/856/742 1930/6689/5388 -f 1909/856/742 1910/858/744 1931/6691/5390 -f 1910/858/744 1911/860/746 1932/6692/5391 -f 1911/6682/746 1912/6681/5383 1933/6693/5392 -f 1890/6673/5381 1934/6696/5394 1933/6693/5392 -f 1890/6673/5381 1891/6672/5380 1935/6695/5393 -f 1935/6697/5393 1891/6683/5380 1913/863/749 -f 1936/6698/5395 1913/863/749 1914/866/752 -f 1937/6699/5396 1914/866/752 1915/868/754 -f 1938/6700/5397 1915/868/754 1916/870/756 -f 1939/6701/5398 1916/870/756 1917/872/757 -f 1941/6703/5399 1918/874/759 1919/876/761 -f 1942/6704/5400 1919/876/761 1920/878/763 -f 1943/6705/5401 1920/878/763 1921/880/764 -f 1966/6707/5402 1944/6679/5382 1922/882/766 -f 1923/6685/770 1924/6684/5384 1947/6709/5404 -f 1924/6684/5384 1925/6686/5385 1948/6711/5405 -f 1925/6686/5385 1926/6687/5386 1949/6712/5406 -f 1926/6687/5386 1927/6688/5387 1950/6713/5407 -f 1927/6688/5387 1928/883/767 1951/6714/5408 -f 1928/883/767 1929/6690/5389 1952/6715/5409 -f 1929/6690/5389 1930/6689/5388 1953/6716/5410 -f 1930/6689/5388 1931/6691/5390 1954/6717/5411 -f 1931/6691/5390 1932/6692/5391 1955/6718/5412 -f 1932/6694/5391 1933/6693/5392 1956/6719/5413 -f 1957/6721/5414 1935/6697/5393 1936/6698/5395 -f 1958/6722/5415 1936/6698/5395 1937/6699/5396 -f 1959/6724/5416 1937/6699/5396 1938/6700/5397 -f 1960/6725/5417 1938/6700/5397 1939/6701/5398 -f 1961/6726/5418 1939/6701/5398 1940/6702/768 -f 1962/6728/5419 1940/884/768 1941/6703/5399 -f 1963/6729/5420 1941/6703/5399 1942/6704/5400 -f 1964/6730/5421 1942/6704/5400 1943/6705/5401 -f 1965/6731/5422 1943/6705/5401 1944/6706/5382 -f 1990/890/773 1966/9337/5402 1945/6733/5403 -f 1945/6708/5403 1922/882/766 1968/892/775 -f 1946/886/769 1969/893/776 1968/892/775 -f 1946/886/769 1947/9338/5404 1970/6735/5423 -f 1947/6709/5404 1948/6711/5405 1971/6736/5424 -f 1948/6711/5405 1949/6712/5406 1972/6738/5425 -f 1949/6712/5406 1950/6713/5407 1973/895/778 -f 1950/6713/5407 1951/6714/5408 1974/896/779 -f 1951/6714/5408 1952/6715/5409 1975/898/781 -f 1952/6715/5409 1953/6716/5410 1976/900/783 -f 1953/6716/5410 1954/6717/5411 1977/902/785 -f 1954/6717/5411 1955/6718/5412 1978/904/787 -f 1955/6739/5412 1956/9339/5413 1979/907/789 -f 1979/910/789 1956/6719/5413 1934/6696/5394 -f 1980/908/790 1934/6696/5394 1957/6723/5414 -f 1981/6740/5426 1957/6723/5414 1958/6741/5415 -f 1982/6743/5427 1958/6722/5415 1959/6724/5416 -f 1983/6744/5428 1959/6724/5416 1960/6725/5417 -f 1984/6745/5429 1960/6725/5417 1961/6726/5418 -f 1985/913/793 1961/6726/5418 1962/6727/5419 -f 1986/916/791 1962/6728/5419 1963/6729/5420 -f 1987/914/794 1963/6729/5420 1964/6730/5421 -f 1988/917/796 1964/6730/5421 1965/6731/5422 -f 1989/919/798 1965/6731/5422 1966/6732/5402 -f 1969/893/776 1970/6735/5423 1993/925/803 -f 1970/6737/5423 1971/6736/5424 1994/6747/805 -f 1971/6736/5424 1972/6738/5425 1995/6749/807 -f 1972/6738/5425 1973/895/778 1996/894/777 -f 2002/932/809 1981/6740/5426 1982/6742/5427 -f 2003/934/811 1982/6742/5427 1983/6751/5428 -f 2004/6752/813 1983/6744/5428 1984/6745/5429 -f 2005/6753/815 1984/6745/5429 1985/913/793 -f 1991/889/772 1968/6746/775 2012/6754/801 -f 1996/894/777 1997/897/780 2017/6758/5431 -f 1997/897/780 1998/899/782 2019/6760/5432 -f 1998/899/782 1999/901/784 2020/6762/5434 -f 1999/901/784 2000/903/786 2021/6763/5435 -f 2000/903/786 2001/6750/788 2022/6764/5436 -f 2001/905/788 1980/9340/790 2023/6765/810 -f 2027/6767/817 2006/912/792 2007/6756/795 -f 2029/6769/5438 2007/915/795 2008/918/797 -f 2030/6770/5439 2008/918/797 2009/920/799 -f 2031/6772/5440 2009/920/799 2010/922/800 -f 2032/6773/5441 2010/922/800 1991/6757/772 -f 2012/6754/801 2034/6810/5443 2033/6775/5442 -f 2012/923/801 2013/924/802 2036/944/820 -f 2013/924/802 2014/926/804 2037/6777/5444 -f 2014/926/804 2015/928/806 2038/6778/5445 -f 2015/928/806 2016/930/808 2039/6779/5446 -f 2016/930/808 2017/6781/5431 2040/6780/5447 -f 2017/6781/5431 2018/6783/5433 2041/6782/5448 -f 2018/6783/5433 2019/6785/5432 2042/6784/5449 -f 2019/6785/5432 2020/9341/5434 2043/6786/5450 -f 2020/6762/5434 2021/6763/5435 2044/6787/5451 -f 2021/6789/5435 2022/6766/5436 2045/947/823 -f 2045/947/823 2022/6766/5436 2023/6765/810 -f 2047/6791/5452 2023/933/810 2024/935/812 -f 2048/6792/5453 2024/935/812 2025/937/814 -f 2049/6794/5454 2025/937/814 2026/939/816 -f 2050/6795/5455 2026/939/816 2027/941/817 -f 2051/6796/5456 2027/941/817 2028/6797/5437 -f 2052/6799/5457 2028/9342/5437 2029/6800/5438 -f 2053/6801/5458 2029/6800/5438 2030/6802/5439 -f 2054/6803/5459 2030/6802/5439 2031/6804/5440 -f 2055/6806/5460 2031/6772/5440 2032/6773/5441 -f 2056/6808/5461 2032/9343/5441 2011/6755/5430 -f 2036/944/820 2037/6777/5444 2059/6809/5462 -f 2037/6777/5444 2038/6778/5445 2060/6812/5464 -f 2038/6778/5445 2039/6779/5446 2061/6813/5465 -f 2039/6779/5446 2040/6780/5447 2062/949/825 -f 2040/6780/5447 2041/6782/5448 2063/950/826 -f 2041/6782/5448 2042/6784/5449 2064/6814/5466 -f 2042/6784/5449 2043/6786/5450 2065/6815/5467 -f 2043/6786/5450 2044/9344/5451 2066/6816/5468 -f 2044/6790/5451 2045/947/823 2067/946/822 -f 2068/6818/5469 2048/6792/5453 2049/6794/5454 -f 2069/6819/5470 2049/6794/5454 2050/6795/5455 -f 2070/6820/5471 2050/6795/5455 2051/6796/5456 -f 2071/6821/5472 2051/6796/5456 2052/6798/5457 -f 2072/953/829 2052/6799/5457 2053/6801/5458 -f 2073/951/827 2053/6801/5458 2054/6803/5459 -f 2074/6823/5473 2054/6803/5459 2055/6805/5460 -f 2075/6824/5474 2055/6805/5460 2056/6825/5461 -f 2076/6827/5475 2056/6808/5461 2033/6775/5442 -f 2057/6811/5463 2034/6810/5443 2078/954/830 -f 2035/943/819 2079/960/836 2078/962/830 -f 2058/942/818 2080/983/858 2079/960/836 -f 2058/942/818 2059/6809/5462 2081/981/856 -f 2059/6809/5462 2060/6812/5464 2082/984/859 -f 2060/6812/5464 2061/6813/5465 2083/6828/5476 -f 2062/949/825 2084/948/824 2083/6828/5476 -f 2063/950/826 2064/6814/5466 2085/6829/5477 -f 2064/6814/5466 2065/6815/5467 2086/6830/5478 -f 2065/6815/5467 2066/6816/5468 2087/6831/5479 -f 2067/946/822 2088/970/845 2087/6832/5479 -f 2067/946/822 2046/945/821 2090/975/850 -f 2046/6793/821 2047/6791/5452 2091/958/834 -f 2091/958/834 2047/6791/5452 2068/6818/5469 -f 2092/959/835 2068/6818/5469 2069/6819/5470 -f 2093/6834/5480 2069/6819/5470 2070/6820/5471 -f 2094/6835/5481 2070/6820/5471 2071/6821/5472 -f 2095/6836/5482 2071/6821/5472 2072/6822/829 -f 2073/951/827 2074/6823/5473 2097/6838/5483 -f 2097/6838/5483 2074/6823/5473 2075/6824/5474 -f 2098/6839/5484 2075/6824/5474 2076/6826/5475 -f 2099/6841/5485 2076/6827/5475 2057/6811/5463 -f 2083/6828/5476 2084/948/824 2117/965/840 -f 2101/963/838 2084/948/824 2085/6829/5477 -f 2102/966/841 2085/6829/5477 2086/6830/5478 -f 2103/968/843 2086/6830/5478 2087/6831/5479 -f 2092/959/835 2093/6834/5480 2106/977/852 -f 2093/6834/5480 2094/6835/5481 2107/978/853 -f 2094/6835/5481 2095/6836/5482 2108/980/855 -f 2095/6836/5482 2096/6837/828 2109/6843/5487 -f 2096/952/828 2097/6838/5483 2110/6844/5488 -f 2097/6838/5483 2098/6839/5484 2111/6846/5489 -f 2098/6839/5484 2099/6840/5485 2112/6847/5490 -f 2099/6841/5485 2077/956/832 2100/955/831 -f 2100/955/831 2078/954/830 2114/6849/837 -f 2090/6833/850 2091/958/834 2123/1006/878 -f 2105/957/833 2124/1011/883 2123/1006/878 -f 2106/977/852 2125/1012/884 2124/1011/883 -f 2108/980/855 2109/6843/5487 2129/6850/5491 -f 2109/6845/5487 2110/6844/5488 2131/6851/5492 -f 2110/6844/5488 2111/6846/5489 2132/6854/5494 -f 2111/6846/5489 2112/6847/5490 2133/6855/5495 -f 2112/6848/5490 2100/955/831 2135/992/865 -f 2138/993/866 2103/968/843 2104/989/847 -f 2141/1000/873 2115/986/861 2116/6842/5486 -f 2142/1033/902 2116/6842/5486 2117/965/840 -f 2143/1035/904 2117/965/840 2118/964/839 -f 2144/1037/906 2118/964/839 2119/967/842 -f 2145/1039/908 2119/967/842 2120/969/844 -f 2128/997/870 2129/6850/5491 2150/1022/892 -f 2129/6853/5491 2130/6852/5493 2151/1025/894 -f 2130/6852/5493 2131/6851/5492 2152/1027/896 -f 2131/6851/5492 2132/6854/5494 2153/1029/898 -f 2132/6854/5494 2133/6855/5495 2154/1031/900 -f 2133/6855/5495 2134/6857/5496 2155/6858/5497 -f 2134/6856/5496 2135/992/865 2156/991/864 -f 2156/991/864 2113/990/863 2165/1015/886 -f 2185/6861/5498 2138/993/866 2139/988/862 -f 2154/1031/900 2155/6858/5497 2178/6862/923 -f 2155/6859/5497 2156/991/864 2179/1055/924 -f 2163/1009/881 2188/1048/917 2187/1043/912 -f 2160/1004/877 2189/1065/932 2188/1064/917 -f 2202/1053/922 2179/1055/924 2165/1015/886 -f 2165/1015/886 2157/1014/871 2191/1056/914 -f 2185/6861/5498 2168/1017/887 2194/1046/915 -f 2194/6864/915 2162/1008/880 2169/1044/913 -f 2176/1028/897 2177/1030/899 2201/1070/936 -f 2177/1030/899 2178/6862/923 2202/1072/922 -f 2220/6865/939 2202/1053/922 2190/1058/926 -f 2207/1095/956 2181/1036/905 2182/1038/907 -f 2208/1062/930 2183/1040/909 2184/1041/910 -f 2209/1082/945 2184/1041/910 2193/6863/5499 -f 2210/1080/943 2193/6863/5499 2194/1046/915 -f 2211/6868/5500 2194/6864/915 2195/1047/916 -f 2195/1047/916 2188/1048/917 2213/6870/931 -f 2189/1065/932 2196/1049/918 2214/1084/947 -f 2196/1049/918 2197/1050/919 2215/1085/948 -f 2224/1093/954 2206/1079/942 2180/1034/903 -f 2226/1097/958 2208/1062/930 2209/1082/945 -f 2227/1102/944 2210/9345/943 2211/6873/5500 -f 2228/1100/961 2211/6873/5500 2212/6874/5501 -f 2212/6874/5501 2213/9346/931 2230/6875/5502 -f 2213/1063/931 2214/1084/947 2231/1083/946 -f 2215/1085/948 2198/1051/920 2232/1118/976 -f 2198/1051/920 2172/1019/889 2233/1106/966 -f 2216/1066/933 2217/1068/934 2235/1089/951 -f 2218/6877/935 2219/6878/937 2236/1111/969 -f 2219/6878/937 2220/1075/939 2221/1074/938 -f 2229/1103/963 2230/6875/5502 2244/1129/985 -f 2230/6875/5502 2231/9347/946 2245/1130/986 -f 2247/6880/965 2248/6881/5504 2271/1151/1004 -f 2248/6881/5504 2249/1107/967 2272/1152/1005 -f 2249/1107/967 2250/6882/5505 2273/1153/1006 -f 2250/6883/5505 2251/1109/968 2274/6885/5506 -f 2251/1109/968 2252/1112/970 2260/1119/977 -f 2277/1137/992 2256/1114/972 2257/1116/974 -f 2233/1106/966 2247/1105/965 2279/1176/1027 -f 2266/1138/993 2267/1128/984 2288/6887/5507 -f 2267/6884/984 2268/1131/987 2289/1149/1002 -f 2273/6886/1006 2274/6885/5506 2291/1166/1016 -f 2274/6885/5506 2260/1119/977 2292/1167/1018 -f 2292/1167/1018 2280/1141/996 2293/6894/5509 -f 2293/6894/5509 2280/1141/996 2281/1143/997 -f 2294/6896/5510 2281/6890/997 2282/1144/998 -f 2295/1170/1021 2282/1144/998 2283/1123/980 -f 2296/1168/1019 2283/1123/980 2253/1113/971 -f 2301/1159/1011 2286/1148/1001 2287/6888/5508 -f 2287/6888/5508 2288/6887/5507 2303/6900/1023 -f 2288/6892/5507 2289/1149/1002 2304/1173/1024 -f 2289/1149/1002 2259/1117/975 2305/1140/995 -f 2309/6901/1017 2293/6906/5509 2310/6902/5513 -f 2311/6905/5515 2293/9348/5509 2294/6896/5510 -f 2312/1179/1030 2294/6896/5510 2295/1170/1021 -f 2314/1180/1031 2264/1125/982 2298/1155/1008 -f 2316/1182/1033 2298/1155/1008 2299/6898/5512 -f 2317/6908/5516 2299/6898/5512 2300/1158/1010 -f 2318/6909/5517 2300/1158/1010 2301/6910/1011 -f 2320/6912/5519 2301/6915/1011 2302/6913/5511 -f 2302/6917/5511 2303/1172/1023 2322/1197/1047 -f 2307/1162/1014 2308/1163/1015 2328/1192/1042 -f 2308/1163/1015 2309/9349/1017 2329/1193/1043 -f 2349/1228/1078 2314/1180/1031 2315/1184/1035 -f 2332/1194/1044 2317/6908/5516 2318/6909/5517 -f 2318/6909/5517 2319/6911/5518 2334/1207/1057 -f 2319/6916/5518 2320/6912/5519 2335/6920/5521 -f 2320/6912/5519 2321/6914/5520 2336/6922/5522 -f 2306/1160/1012 2327/1177/1028 2342/1204/1054 -f 2330/6903/5514 2310/6902/5513 2345/6925/5523 -f 2345/6925/5523 2310/6902/5513 2311/6907/5515 -f 2311/6905/5515 2312/1179/1030 2347/1178/1029 -f 2344/1191/1041 2354/1216/1066 2353/1199/1049 -f 2344/6927/1041 2330/6903/5514 2355/6926/5524 -f 2335/6920/5521 2362/1229/1079 2361/1231/1081 -f 2362/1229/1079 2335/6920/5521 2336/6922/5522 -f 2336/6922/5522 2337/6924/1059 2364/6933/1058 -f 2355/6926/5524 2345/6925/5523 2366/1223/1073 -f 2366/1223/1073 2345/6925/5523 2346/6928/5525 -f 2346/6929/5525 2347/1178/1029 2368/1244/1094 -f 2354/6930/1066 2355/6926/5524 2376/1236/1086 -f 2341/6923/1040 2342/1204/1054 2382/1220/1070 -f 2376/1236/1086 2366/1223/1073 2383/1222/1072 -f 2360/1205/1055 2361/6931/1081 2386/1249/1080 -f 2393/1251/1100 2362/1229/1079 2363/6932/5526 -f 2363/6932/5526 2364/6933/1058 2395/1271/1102 -f 2373/1214/1064 2374/1215/1065 2400/1258/1105 -f 2374/1215/1065 2375/6935/1085 2401/1259/1084 -f 2401/1234/1084 2383/1222/1072 2402/1242/1092 -f 2402/1263/1092 2367/6934/1071 2368/1244/1094 -f 2416/1283/1126 2392/1245/1095 2404/1248/1098 -f 2417/1281/1124 2404/1248/1098 2405/1250/1099 -f 2405/1252/1099 2393/1251/1100 2419/1284/1127 -f 2393/1251/1100 2394/1270/1115 2420/1269/1114 -f 2397/1273/1117 2398/1240/1090 2422/1274/1118 -f 2388/1233/1083 2381/1219/1069 2423/6939/1119 -f 2407/1255/1103 2399/1241/1091 2424/1290/1133 -f 2399/1241/1091 2400/1258/1105 2425/1291/1134 -f 2400/1258/1105 2408/1257/1104 2426/6940/1136 -f 2408/6936/1104 2409/1261/1107 2410/1260/1106 -f 2431/1296/1139 2414/1267/1112 2415/1268/1113 -f 2432/1298/1141 2415/1268/1113 2416/1283/1126 -f 2433/1282/1125 2417/1281/1124 2418/6937/1129 -f 2419/1284/1127 2420/1269/1114 2435/6943/5527 -f 2420/6938/1114 2406/1253/1101 2436/6944/5528 -f 2422/1274/1118 2423/6939/1119 2440/1308/1149 -f 2423/1275/1119 2424/1290/1133 2441/1289/1132 -f 2425/1291/1134 2426/6940/1136 2442/6946/1135 -f 2427/6941/1120 2428/1278/1121 2444/6947/5529 -f 2444/6947/5529 2428/1278/1121 2429/1279/1122 -f 2450/6950/5531 2434/1285/1128 2435/6943/5527 -f 2435/6945/5527 2436/6944/5528 2452/6954/5533 -f 2436/6944/5528 2437/1287/1130 2453/1304/1145 -f 2441/6956/1132 2442/6960/1135 2458/1317/1155 -f 2460/6958/5534 2444/6947/5529 2445/1295/1138 -f 2461/6959/5535 2445/1295/1138 2446/1297/1140 -f 2462/6963/5536 2446/9350/1140 2447/6964/1142 -f 2463/6965/5537 2447/6964/1142 2448/6952/1143 -f 2464/6966/5538 2448/6952/1143 2449/1302/1144 -f 2465/6967/5539 2449/1302/1144 2450/6953/5531 -f 2466/6968/1159 2450/6950/5531 2451/6951/5532 -f 2451/6955/5532 2452/6954/5533 2469/1325/1162 -f 2452/6954/5533 2453/1304/1145 2470/6971/5540 -f 2453/1304/1145 2454/1305/1146 2471/6972/5541 -f 2454/6973/1146 2455/9351/1148 2472/6974/1151 -f 2475/1318/1156 2459/9352/5530 2476/6976/1188 -f 2476/1354/1188 2459/6948/5530 2460/6958/5534 -f 2477/1328/1165 2460/6958/5534 2461/6959/5535 -f 2479/1331/1168 2461/9353/5535 2462/6963/5536 -f 2480/1329/1166 2462/6963/5536 2463/6965/5537 -f 2481/1332/1169 2463/6965/5537 2464/6966/5538 -f 2482/1334/1171 2464/6966/5538 2465/6967/5539 -f 2483/1336/1173 2465/6967/5539 2466/1322/1159 -f 2469/1325/1162 2470/6971/5540 2486/1339/1176 -f 2470/6979/5540 2471/6975/5541 2488/1342/1179 -f 2471/6975/5541 2472/6974/1151 2489/1344/1181 -f 2472/1311/1151 2473/1314/1152 2490/1347/1183 -f 2473/1314/1152 2474/1316/1154 2491/1349/1185 -f 2474/1316/1154 2475/1318/1156 2492/6977/5542 -f 2476/6976/1188 2493/1360/1187 2507/6981/5543 -f 2494/6982/1164 2478/9354/1163 2479/1331/1168 -f 2498/1337/1174 2483/1336/1173 2484/1321/1158 -f 2499/1357/1191 2484/1321/1158 2467/1320/1157 -f 2500/6983/1189 2467/6969/1157 2468/6970/1161 -f 2486/6985/1176 2487/1341/1178 2503/1340/1177 -f 2491/1349/1185 2492/6977/5542 2507/6981/5543 -f 2509/1365/1194 2494/6982/1164 2495/1330/1167 -f 2510/1363/1196 2495/1330/1167 2496/1333/1170 -f 2511/1366/1198 2496/1333/1170 2497/1335/1172 -f 2512/1368/1200 2497/1335/1172 2498/1337/1174 -f 2513/1370/1202 2498/1337/1174 2499/1357/1191 -f 2502/6986/1175 2503/1340/1177 2516/1379/1210 -f 2503/1340/1177 2504/1343/1180 2517/1381/1212 -f 2504/6987/1180 2505/1345/1182 2518/1384/1214 -f 2505/1345/1182 2506/1348/1184 2519/1386/1216 -f 2506/1348/1184 2507/6981/5543 2520/1359/1193 -f 2528/1395/1205 2500/9355/1189 2501/1375/1207 -f 2538/6990/5544 2523/1364/1197 2524/1367/1199 -f 2539/6991/5545 2524/1367/1199 2525/1369/1201 -f 2540/6994/5547 2525/1369/1201 2526/1371/1203 -f 2541/6995/5548 2526/1371/1203 2527/1372/1204 -f 2531/1377/1209 2532/1380/1211 2545/6996/5549 -f 2532/1380/1211 2533/6992/1213 2546/6998/5551 -f 2533/1382/1213 2534/1385/1215 2547/6999/5552 -f 2534/1385/1215 2535/1387/1217 2548/7001/5553 -f 2550/7002/5554 2522/6989/1195 2537/6993/5546 -f 2551/7003/5555 2537/6993/5546 2538/6990/5544 -f 2552/1399/1226 2538/6990/5544 2539/6991/5545 -f 2553/1397/1224 2539/6991/5545 2540/6994/5547 -f 2554/1400/1227 2540/6994/5547 2541/6995/5548 -f 2555/1402/1229 2541/6995/5548 2542/1390/1219 -f 2556/1404/1231 2542/1390/1219 2528/1373/1205 -f 2530/1392/1206 2543/1391/1220 2559/7007/5557 -f 2543/1391/1220 2544/6997/5550 2560/1407/1234 -f 2544/6997/5550 2545/6996/5549 2561/1408/1235 -f 2545/6996/5549 2546/6998/5551 2562/1410/1237 -f 2546/7000/5551 2547/6999/5552 2563/1413/1239 -f 2547/6999/5552 2548/7001/5553 2564/1415/1241 -f 2548/7001/5553 2521/1358/1192 2549/7009/5556 -f 2549/7004/5556 2536/1388/1218 2566/7010/5558 -f 2566/7010/5558 2536/1388/1218 2550/7005/5554 -f 2567/7013/5560 2550/7002/5554 2551/7003/5555 -f 2568/7014/5561 2551/7003/5555 2552/1399/1226 -f 2572/1405/1232 2556/1404/1231 2557/7006/1222 -f 2573/1418/1244 2557/1394/1222 2529/1393/1221 -f 2529/1393/1221 2558/1396/1223 2575/1420/1246 -f 2558/7008/1223 2559/7007/5557 2576/7016/5562 -f 2559/7007/5557 2560/1407/1234 2577/1406/1233 -f 2564/1415/1241 2549/7009/5556 2565/7018/5559 -f 2582/7020/5563 2567/7013/5560 2568/7014/5561 -f 2583/7021/5564 2568/7014/5561 2569/1398/1225 -f 2584/7024/5566 2569/1398/1225 2570/1401/1228 -f 2585/7025/5567 2570/1401/1228 2571/1403/1230 -f 2586/7026/5568 2571/1403/1230 2572/1405/1232 -f 2587/7027/5569 2572/1405/1232 2573/7015/1244 -f 2575/7017/1246 2576/7016/5562 2590/7029/5570 -f 2576/7016/5562 2577/1406/1233 2591/7031/5571 -f 2577/1406/1233 2578/1409/1236 2592/7032/5572 -f 2578/7019/1236 2579/1411/1238 2593/7033/5573 -f 2579/1411/1238 2580/1414/1240 2594/7035/5574 -f 2580/1414/1240 2565/7018/5559 2581/7036/5565 -f 2566/7010/5558 2611/7041/5576 2595/1449/1272 -f 2566/7037/5558 2582/9356/5563 2597/7038/1249 -f 2597/1423/1249 2582/7020/5563 2583/7021/5564 -f 2598/1421/1247 2583/7021/5564 2584/7024/5566 -f 2599/1424/1250 2584/7024/5566 2585/7025/5567 -f 2600/1426/1252 2585/7025/5567 2586/7026/5568 -f 2601/1428/1254 2586/7026/5568 2587/7027/5569 -f 2602/1434/1256 2587/9357/5569 2588/1417/1243 -f 2603/1432/1258 2588/1417/1243 2574/1416/1242 -f 2574/7043/1242 2589/9358/1245 2605/7044/1261 -f 2589/7030/1245 2590/7029/5570 2606/1437/1262 -f 2590/7029/5570 2591/7031/5571 2607/1439/1264 -f 2591/7031/5571 2592/7032/5572 2608/1441/1266 -f 2592/7034/5572 2593/7033/5573 2609/1444/1268 -f 2593/7033/5573 2594/7035/5574 2610/1446/1270 -f 2594/7047/5574 2581/7022/5565 2595/1449/1272 -f 2626/1447/1271 2611/7041/5576 2627/7048/5579 -f 2611/7041/5576 2596/7040/5575 2628/7057/5583 -f 2628/7058/5583 2596/7039/5575 2612/7050/1248 -f 2629/7060/5584 2612/1422/1248 2613/1425/1251 -f 2630/7061/5585 2613/1425/1251 2614/7051/5581 -f 2631/7062/5586 2614/7051/5581 2615/1427/1253 -f 2632/7064/5587 2615/9359/1253 2616/7065/1255 -f 2633/7066/5588 2616/7065/1255 2617/7052/1257 -f 2634/7067/5589 2617/7052/1257 2618/1433/1259 -f 2635/7068/5590 2618/1433/1259 2619/7042/5577 -f 2636/7069/5591 2619/7042/5577 2604/7046/5578 -f 2620/7053/1260 2638/9360/5594 2637/7071/5592 -f 2620/1435/1260 2621/1438/1263 2639/7072/5593 -f 2621/1438/1263 2622/7054/5582 2640/7074/5595 -f 2622/7054/5582 2623/1440/1265 2641/7075/5596 -f 2623/7076/1265 2624/7079/1267 2642/7077/5597 -f 2624/7079/1267 2625/7056/1269 2643/7080/5598 -f 2625/7056/1269 2626/1447/1271 2644/7049/5580 -f 2644/7049/5580 2627/7048/5579 2645/7081/5599 -f 2627/7048/5579 2628/7057/5583 2646/7083/5601 -f 2628/7058/5583 2629/7059/5584 2647/7084/5602 -f 2647/7086/5602 2629/7060/5584 2630/7061/5585 -f 2648/7088/5603 2630/9361/5585 2631/7089/5586 -f 2649/7090/5604 2631/7089/5586 2632/7064/5587 -f 2650/7091/5605 2632/7064/5587 2633/7066/5588 -f 2651/7092/5606 2633/7066/5588 2634/7067/5589 -f 2652/7093/5607 2634/7067/5589 2635/7068/5590 -f 2653/7094/5608 2635/7068/5590 2636/7069/5591 -f 2654/7095/5609 2636/7069/5591 2637/7070/5592 -f 2637/7071/5592 2638/9360/5594 2656/7097/5611 -f 2638/7073/5594 2639/7072/5593 2657/7099/5612 -f 2639/7101/5593 2640/7104/5595 2658/7102/5613 -f 2640/7104/5595 2641/9362/5596 2659/7105/5614 -f 2641/7078/5596 2642/7077/5597 2660/7106/5615 -f 2642/7077/5597 2643/7080/5598 2661/7108/5616 -f 2643/7080/5598 2644/7049/5580 2662/7082/5600 -f 2662/7082/5600 2645/7081/5599 2663/1450/1273 -f 2645/7081/5599 2646/7083/5601 2664/7109/5617 -f 2646/7085/5601 2647/7084/5602 2665/7110/5618 -f 2665/7112/5618 2647/9363/5602 2648/7088/5603 -f 2666/7113/5619 2648/7088/5603 2649/7090/5604 -f 2667/7114/5620 2649/7090/5604 2650/7091/5605 -f 2668/7115/5621 2650/7091/5605 2651/7092/5606 -f 2669/7116/5622 2651/7092/5606 2652/7093/5607 -f 2670/7117/5623 2652/7093/5607 2653/7094/5608 -f 2671/7118/5624 2653/7094/5608 2654/7095/5609 -f 2654/7095/5609 2655/7096/5610 2674/1455/1278 -f 2655/7098/5610 2656/7097/5611 2675/7119/5625 -f 2656/7121/5611 2657/7103/5612 2676/7122/5626 -f 2657/7103/5612 2658/7102/5613 2677/7124/5627 -f 2658/7102/5613 2659/7105/5614 2678/7125/5628 -f 2659/7107/5614 2660/7106/5615 2679/7126/5629 -f 2660/7106/5615 2661/7108/5616 2680/7128/5630 -f 2661/7108/5616 2662/7082/5600 2681/1452/1275 -f 2683/7129/5631 2664/7111/5617 2665/7110/5618 -f 2665/7112/5618 2666/7113/5619 2685/7131/5633 -f 2666/7113/5619 2667/7114/5620 2686/7133/5634 -f 2667/7114/5620 2668/7115/5621 2687/1471/1293 -f 2668/7115/5621 2669/7116/5622 2689/1475/1297 -f 2670/7117/5623 2691/1460/1283 2690/1459/1282 -f 2670/7117/5623 2671/7118/5624 2692/1483/1304 -f 2674/7120/1278 2675/7119/5625 2694/1464/1286 -f 2675/7123/5625 2676/7122/5626 2695/1467/1289 -f 2695/1467/1289 2676/7122/5626 2677/7124/5627 -f 2677/7124/5627 2678/7125/5628 2697/7135/5635 -f 2698/7136/5636 2678/7127/5628 2679/7126/5629 -f 2700/7139/5638 2679/7126/5629 2680/7128/5630 -f 2702/7141/5640 2680/7128/5630 2681/1452/1275 -f 2684/7132/5632 2685/7131/5633 2707/7142/5641 -f 2685/7131/5633 2686/7133/5634 2708/1470/1292 -f 2694/7134/1286 2695/1467/1289 2713/1466/1288 -f 2718/1477/1299 2693/1463/1276 2712/1462/1285 -f 2712/7145/1285 2713/1466/1288 2720/7147/5644 -f 2713/1466/1288 2696/1465/1287 2721/7149/5645 -f 2721/7149/5645 2696/1465/1287 2697/7135/5635 -f 2722/7150/5646 2697/7135/5635 2698/7138/5636 -f 2723/7152/5647 2698/7136/5636 2699/7137/5637 -f 2724/7153/5648 2699/7137/5637 2700/7139/5638 -f 2725/7154/5649 2700/7139/5638 2701/7140/5639 -f 2726/7155/5650 2701/7140/5639 2702/7141/5640 -f 2727/7156/5651 2702/7141/5640 2703/1451/1274 -f 2754/1504/1324 2728/1468/1290 2704/1457/1280 -f 2704/1457/1280 2682/1456/1279 2774/1531/1308 -f 2705/1490/1310 2706/7144/5642 2736/1511/1331 -f 2706/7143/5642 2707/7142/5641 2737/7157/5652 -f 2707/7142/5641 2708/1470/1292 2738/1491/1311 -f 2714/1469/1291 2715/1472/1294 2739/1495/1315 -f 2715/1472/1294 2716/1474/1296 2740/1492/1312 -f 2711/1461/1284 2742/1513/1333 2741/1493/1313 -f 2711/1478/1284 2718/1477/1299 2743/7159/5653 -f 2743/7159/5653 2718/1477/1299 2719/7146/5643 -f 2719/7148/5643 2720/7147/5644 2746/1503/1323 -f 2746/1503/1323 2720/7147/5644 2721/7149/5645 -f 2747/1501/1321 2721/7149/5645 2722/7150/5646 -f 2748/1521/1339 2722/7150/5646 2723/7151/5647 -f 2749/7161/1337 2723/7152/5647 2724/7153/5648 -f 2750/1524/1342 2724/7153/5648 2725/7154/5649 -f 2751/1522/1340 2725/7154/5649 2726/7155/5650 -f 2726/7155/5650 2727/7156/5651 2753/1506/1326 -f 2753/1506/1326 2727/7156/5651 2728/1468/1290 -f 2737/7157/5652 2765/7164/5655 2764/7163/1330 -f 2738/1491/1311 2766/1526/1344 2765/7164/5655 -f 2742/7160/1333 2743/7159/5653 2744/1516/1336 -f 2769/7165/5656 2747/1501/1321 2748/1521/1339 -f 2770/7166/1338 2749/7161/1337 2750/1524/1342 -f 2771/1523/1341 2751/1522/1340 2752/7162/5654 -f 2764/1510/1330 2775/7176/5657 2763/1509/1329 -f 2756/1496/1316 2757/1528/1346 2777/1527/1345 -f 2758/1497/1317 2759/1498/1318 2778/1529/1347 -f 2761/1500/1320 2779/1536/1352 2778/1529/1347 -f 2761/1500/1320 2733/1484/1305 2780/1541/1357 -f 2767/7168/1332 2742/7160/1333 2768/1515/1335 -f 2768/1517/1335 2762/1502/1322 2782/7170/5659 -f 2782/7170/5659 2762/1502/1322 2769/7165/5656 -f 2783/7172/5660 2769/7165/5656 2770/1520/1338 -f 2784/7174/5661 2770/7166/1338 2771/1523/1341 -f 2785/7175/5662 2771/1523/1341 2772/1505/1325 -f 2775/7176/5657 2789/1539/1355 2788/1538/1354 -f 2775/7167/5657 2765/7164/5655 2790/7178/5663 -f 2765/7164/5655 2766/1526/1344 2791/7180/5664 -f 2766/1526/1344 2776/1525/1343 2792/7181/5665 -f 2776/1525/1343 2777/1527/1345 2793/7182/5666 -f 2777/1527/1345 2778/1529/1347 2794/1561/1376 -f 2741/1493/1313 2767/1512/1332 2796/1543/1359 -f 2796/1546/1359 2767/7168/1332 2781/7169/5658 -f 2797/7183/1360 2781/7171/5658 2782/7170/5659 -f 2798/7184/5667 2782/7170/5659 2783/7172/5660 -f 2799/7185/5668 2783/7172/5660 2784/7173/5661 -f 2800/7187/5669 2784/7174/5661 2785/7175/5662 -f 2801/1549/1364 2785/7175/5662 2786/1530/1348 -f 2803/7188/5670 2786/1530/1348 2773/1508/1328 -f 2774/1531/1308 2787/1534/1350 2806/7189/1368 -f 2806/1553/1368 2787/7177/1350 2788/1538/1354 -f 2789/7190/1355 2790/9364/5663 2808/7191/5671 -f 2790/7178/5663 2791/7180/5664 2809/1555/1370 -f 2791/7180/5664 2792/7181/5665 2810/1556/1371 -f 2792/7181/5665 2793/7182/5666 2811/1558/1373 -f 2793/7182/5666 2794/1561/1376 2812/1560/1375 -f 2815/1565/1361 2797/9365/1360 2798/7194/5667 -f 2816/1568/1381 2798/7184/5667 2799/7185/5668 -f 2817/1566/1379 2799/7185/5668 2800/7186/5669 -f 2818/7198/5672 2800/7187/5669 2801/1549/1364 -f 2807/7192/1353 2808/7191/5671 2822/7199/5673 -f 2808/7193/5671 2809/1555/1370 2823/1554/1369 -f 2830/7204/5675 2815/1565/1361 2816/7195/1381 -f 2831/1567/1380 2817/1566/1379 2818/7197/5672 -f 2832/7207/5676 2818/7198/5672 2819/1548/1363 -f 2833/1575/1387 2819/1548/1363 2802/1547/1362 -f 2834/1573/1385 2802/1547/1362 2803/7188/5670 -f 2835/1596/1406 2803/7188/5670 2804/1532/1349 -f 2820/7202/1367 2821/7200/5674 2838/1580/1391 -f 2821/7200/5674 2822/7199/5673 2839/1582/1393 -f 2822/7199/5673 2823/9366/1369 2840/7208/5677 -f 2823/1554/1369 2824/1557/1372 2842/7209/5678 -f 2845/1603/1384 2829/1563/1378 2830/7204/5675 -f 2846/1590/1401 2830/7204/5675 2831/7205/1380 -f 2847/7213/1399 2831/1567/1380 2832/7206/5676 -f 2848/1594/1402 2832/7207/5676 2833/1575/1387 -f 2839/1582/1393 2840/7208/5677 2864/7215/5681 -f 2840/7208/5677 2841/9367/5679 2865/7218/5682 -f 2841/7210/5679 2842/7209/5678 2866/7219/5683 -f 2842/7209/5678 2843/7212/5680 2867/7221/5684 -f 2873/7222/5685 2858/1593/1404 2859/1595/1405 -f 2854/1584/1395 2869/1586/1397 2878/1616/1424 -f 2871/1602/1412 2846/1590/1401 2855/1589/1400 -f 2880/7225/5686 2857/1592/1403 2873/7224/5685 -f 2882/7227/1434 2873/7222/5685 2874/1606/1415 -f 2883/7228/5688 2874/1606/1415 2860/1597/1407 -f 2884/1611/1419 2861/1598/1408 2862/1608/1417 -f 2885/1622/1429 2875/1607/1416 2850/1577/1389 -f 2863/1600/1410 2864/7215/5681 2889/1632/1438 -f 2864/7215/5681 2865/7218/5682 2890/7231/5689 -f 2865/7218/5682 2866/7233/5683 2891/7232/5690 -f 2866/7233/5683 2867/9368/5684 2892/7234/5691 -f 2867/7221/5684 2868/1601/1411 2894/1624/1431 -f 2897/1630/1432 2883/7228/5688 2884/1611/1419 -f 2898/1628/1435 2884/1611/1419 2875/1607/1416 -f 2877/1610/1418 2878/1616/1424 2902/1634/1440 -f 2903/1635/1441 2871/1602/1412 2879/1619/1426 -f 2905/1637/1443 2872/1604/1413 2880/7225/5686 -f 2906/1646/1452 2880/7225/5686 2881/7226/5687 -f 2907/1648/1454 2881/7226/5687 2882/7229/1434 -f 2910/1640/1446 2898/1628/1435 2885/1622/1429 -f 2927/7241/1460 2886/9369/1427 2887/7242/1420 -f 2887/1612/1420 2888/1613/1421 2913/1642/1448 -f 2889/1632/1438 2890/7231/5689 2915/7243/5693 -f 2890/7231/5689 2891/7232/5690 2916/7244/5694 -f 2891/7232/5690 2892/7234/5691 2917/7245/5695 -f 2892/7234/5691 2893/7247/5692 2918/7246/5696 -f 2893/7247/5692 2894/9370/1431 2919/7248/5697 -f 2895/1615/1423 2921/1678/1479 2920/1633/1439 -f 2896/7250/1425 2922/9371/5698 2921/7251/1479 -f 2924/1653/1456 2908/1626/1433 2909/7240/1436 -f 2925/1655/1457 2909/1629/1436 2910/1640/1446 -f 2914/1631/1437 2915/7243/5693 2939/7254/5699 -f 2915/7243/5693 2916/7244/5694 2940/7257/5700 -f 2916/7244/5694 2917/7245/5695 2941/7258/5701 -f 2917/7245/5695 2918/7246/5696 2942/7259/5702 -f 2918/7246/5696 2919/7248/5697 2943/7260/5703 -f 2919/7249/5697 2930/1644/1450 2944/7261/5704 -f 2930/1644/1450 2920/1633/1439 2945/1677/1478 -f 2922/7253/5698 2923/1636/1442 2903/1635/1441 -f 2948/7263/5705 2933/7255/1455 2934/1652/1458 -f 2949/1668/1472 2934/1652/1458 2935/7256/1459 -f 2951/7265/5706 2935/1654/1459 2926/1639/1445 -f 2928/1641/1447 2929/1643/1449 2955/1675/1476 -f 2956/1679/1480 2931/1645/1451 2932/1647/1453 -f 2937/1658/1462 2960/1685/1486 2959/1672/1473 -f 2938/1660/1464 2939/7254/5699 2962/7267/5707 -f 2939/7254/5699 2940/7257/5700 2963/7268/5708 -f 2940/7257/5700 2941/7258/5701 2964/7269/5709 -f 2941/7258/5701 2942/7259/5702 2965/7270/5710 -f 2942/7259/5702 2943/7260/5703 2966/7271/5711 -f 2943/7260/5703 2944/9372/5704 2967/1710/1510 -f 2921/7251/1479 2922/9371/5698 2970/7272/5712 -f 2970/7275/5712 2922/7253/5698 2946/1661/1465 -f 2972/1686/1487 2947/1664/1468 2948/7264/5705 -f 2973/7277/5713 2948/7263/5705 2958/1667/1471 -f 2975/7279/5715 2958/1667/1471 2950/1666/1470 -f 2976/7280/5716 2950/1666/1470 2951/7266/5706 -f 2977/1698/1498 2951/7266/5706 2952/7281/1483 -f 2954/1663/1467 2955/1675/1476 2981/1691/1492 -f 2982/1694/1495 2956/1679/1480 2957/1665/1469 -f 2959/1672/1473 2960/1685/1486 2986/1700/1500 -f 2961/1674/1475 2962/7267/5707 2988/1704/1504 -f 2962/7267/5707 2963/7268/5708 2989/1705/1505 -f 2963/7268/5708 2964/7269/5709 2990/1707/1507 -f 2964/7269/5709 2965/7270/5710 2991/1708/1508 -f 2965/7270/5710 2966/7271/5711 2992/7285/5718 -f 2994/7286/5719 2970/7275/5712 2971/1680/1481 -f 2996/1701/1501 2972/1686/1487 2973/7276/5713 -f 2997/1716/1515 2973/7277/5713 2974/7278/5714 -f 2998/1714/1513 2974/7278/5714 2975/7279/5715 -f 2999/1719/1518 2975/7279/5715 2976/7280/5716 -f 3000/1717/1516 2976/7280/5716 2977/1698/1498 -f 2980/1684/1485 2981/1691/1492 3003/7290/5720 -f 3004/1712/1511 2982/1694/1495 2983/1687/1488 -f 2984/7284/1490 2985/7282/5717 3007/7292/5721 -f 2987/1690/1491 2988/1704/1504 3008/1703/1503 -f 2989/1705/1505 2990/1707/1507 3009/1706/1506 -f 2991/1708/1508 2992/7285/5718 3010/7295/5723 -f 2992/7285/5718 2967/1710/1510 3011/1709/1509 -f 2993/1692/1493 2969/1676/1477 2994/7288/5719 -f 3012/1728/1521 2994/7286/5719 2995/1695/1496 -f 3005/1702/1502 2996/1701/1501 2997/7289/1515 -f 3014/1715/1514 2998/1714/1513 2999/1719/1518 -f 3015/1718/1517 3000/1717/1516 3001/1697/1497 -f 2979/1720/1484 3006/9373/5722 3019/7300/5725 -f 3006/7293/5722 3007/7292/5721 3020/7302/5726 -f 3007/7292/5721 2986/1700/1500 3021/1723/1520 -f 3002/1699/1499 3003/7290/5720 3022/1742/1535 -f 3003/7290/5720 3008/1703/1503 3024/7304/5727 -f 3008/1703/1503 3009/1706/1506 3025/7305/5728 -f 3009/1706/1506 3010/7295/5723 3026/7306/5729 -f 3010/7295/5723 3011/1709/1509 3027/7307/5730 -f 3011/7308/1509 2993/1726/1493 3028/1730/1525 -f 3031/1747/1540 3004/1712/1511 3005/1702/1502 -f 3032/7310/5731 3005/1702/1502 3014/7298/1514 -f 3033/7312/5732 3014/1715/1514 3015/1718/1517 -f 3034/7313/5733 3015/1718/1517 3016/7299/5724 -f 3035/7314/5734 3016/7299/5724 3017/7301/1489 -f 3036/1736/1530 3018/1721/1519 3019/7300/5725 -f 3037/1739/1528 3019/7303/5725 3020/7302/5726 -f 3038/1737/1531 3020/7302/5726 3021/1723/1520 -f 3040/7317/5736 3024/7304/5727 3025/7305/5728 -f 3041/7318/5737 3025/7305/5728 3026/7306/5729 -f 3042/7319/5738 3026/7306/5729 3027/7307/5730 -f 3043/7321/5739 3027/7309/5730 3028/1730/1525 -f 3033/7311/5732 3049/9374/1543 3048/1748/1541 -f 3033/7312/5732 3034/7313/5733 3050/1751/1544 -f 3034/7313/5733 3035/7314/5734 3051/1753/1546 -f 3035/7314/5734 3018/7315/1519 3052/7323/5735 -f 3052/7324/5735 3036/9375/1530 3053/1756/1529 -f 3057/1745/1538 3023/1744/1537 3040/7317/5736 -f 3059/1772/1563 3040/7317/5736 3041/7318/5737 -f 3061/7326/1566 3041/7318/5737 3042/7319/5738 -f 3062/1773/1564 3042/9376/5738 3043/7328/5739 -f 3063/7329/5740 3043/7328/5739 3044/7330/1524 -f 3044/1729/1524 3045/7322/1526 3065/7332/5742 -f 3045/1731/1526 3046/1733/1527 3066/1764/1555 -f 3048/1748/1541 3049/9374/1543 3068/7335/1558 -f 3051/7336/1546 3052/7324/5735 3071/1755/1548 -f 3069/1749/1542 3070/1752/1545 3079/7338/1569 -f 3070/7337/1545 3071/1755/1548 3072/1754/1547 -f 3072/1754/1547 3073/7339/1549 3082/7340/1559 -f 3075/1760/1551 3039/1740/1533 3056/1743/1536 -f 3084/1804/1591 3058/1761/1552 3059/1772/1563 -f 3085/7342/1562 3060/9377/1561 3061/1775/1566 -f 3086/1774/1565 3062/1773/1564 3063/7329/5740 -f 3087/1783/1574 3063/7329/5740 3064/7331/5741 -f 3064/7343/5741 3065/7334/5742 3089/1786/1576 -f 3065/7334/5742 3066/1764/1555 3090/1788/1578 -f 3067/1746/1539 3068/7335/1558 3092/7344/1598 -f 3068/1767/1558 3078/1766/1557 3093/1814/1599 -f 3080/1779/1570 3072/1754/1547 3081/7341/5743 -f 3081/7341/5743 3082/7340/1559 3097/7347/1584 -f 3097/1798/1584 3082/1768/1559 3074/1759/1550 -f 3099/1802/1589 3085/1771/1562 3086/7349/1565 -f 3100/7350/1592 3086/1774/1565 3087/1783/1574 -f 3095/1777/1568 3081/7341/5743 3096/7348/1583 -f 3111/1801/1588 3076/1762/1553 3084/1804/1591 -f 3114/7355/5746 3100/1805/1592 3101/7356/1573 -f 3091/1776/1567 3092/7344/1598 3120/7358/1597 -f 3093/1814/1599 3094/1791/1581 3121/1815/1600 -f 3105/1793/1580 3096/7348/1583 3123/7361/5744 -f 3139/7364/5749 3123/7352/5744 3106/1794/1582 -f 3106/1794/1582 3107/7353/1585 3125/7366/5751 -f 3107/1797/1585 3108/1799/1586 3126/1819/1604 -f 3111/1801/1588 3112/1803/1590 3127/7368/5752 -f 3112/1803/1590 3113/1806/1593 3128/7369/5753 -f 3113/1806/1593 3114/7355/5746 3129/7370/5754 -f 3114/7355/5746 3115/7357/5747 3130/7371/5755 -f 3115/7359/5747 3116/1808/1594 3131/7372/5756 -f 3131/7372/5756 3116/1808/1594 3117/7360/1595 -f 3132/7375/5757 3117/1810/1595 3118/1811/1596 -f 3134/7376/5758 3104/1789/1579 3120/7358/1597 -f 3120/1812/1597 3121/1815/1600 3136/1821/1606 -f 3137/1833/1618 3121/1815/1600 3122/7363/5748 -f 3138/7378/5759 3122/7363/5748 3123/7379/5744 -f 3154/7381/5760 3139/7364/5749 3124/7365/5750 -f 3124/7365/5750 3125/7366/5751 3141/7383/5762 -f 3125/7367/5751 3126/1819/1604 3142/1818/1603 -f 3109/1800/1587 3110/7354/5745 3143/1825/1610 -f 3110/7354/5745 3127/7368/5752 3144/1827/1612 -f 3127/7368/5752 3128/7369/5753 3145/1829/1614 -f 3128/7369/5753 3129/7370/5754 3161/1831/1616 -f 3129/7370/5754 3130/7371/5755 3147/1832/1617 -f 3130/7373/5755 3131/7372/5756 3148/7385/5763 -f 3148/7385/5763 3131/7372/5756 3132/7374/5757 -f 3149/7388/5764 3132/7375/5757 3133/1817/1602 -f 3150/1820/1605 3119/1816/1601 3134/7376/5758 -f 3151/7389/5765 3134/7376/5758 3135/7377/1608 -f 3153/7391/5766 3138/7378/5759 3139/7380/5749 -f 3171/1836/1621 3154/7381/5760 3140/7382/5761 -f 3140/7382/5761 3141/7383/5762 3156/1838/1623 -f 3141/7384/5762 3142/1818/1603 3157/7393/5767 -f 3147/7386/1617 3148/7385/5763 3163/7395/5768 -f 3163/7395/5768 3148/7385/5763 3149/7387/5764 -f 3164/7398/5769 3149/7388/5764 3150/1820/1605 -f 3165/7399/5770 3150/1820/1605 3151/7389/5765 -f 3166/7400/5771 3151/7389/5765 3152/7390/1607 -f 3168/1845/1630 3152/1822/1607 3136/1821/1606 -f 3169/1834/1619 3137/1833/1618 3153/7391/5766 -f 3170/1847/1632 3153/7391/5766 3154/7392/5760 -f 3173/7402/1624 3156/7394/1623 3157/7393/5767 -f 3174/7403/5772 3157/7393/5767 3158/1824/1609 -f 3175/7404/5773 3158/1824/1609 3159/1826/1611 -f 3178/1843/1628 3161/1831/1616 3162/1830/1615 -f 3179/7406/5774 3162/7396/1615 3163/7395/5768 -f 3163/7395/5768 3164/7397/5769 3181/7408/5776 -f 3164/7398/5769 3165/7399/5770 3182/7409/5777 -f 3165/7399/5770 3166/7400/5771 3183/1855/1637 -f 3188/7411/1648 3173/7402/1624 3174/7403/5772 -f 3189/1873/1650 3174/7403/5772 3175/7404/5773 -f 3191/1874/1654 3175/7404/5773 3176/1840/1625 -f 3194/7413/1643 3178/1843/1628 3179/7405/5774 -f 3195/1859/1641 3179/9378/5774 3180/7415/5775 -f 3180/7415/5775 3181/9379/5776 3197/7417/1661 -f 3181/7410/5776 3182/7409/5777 3198/1882/1662 -f 3182/7409/5777 3183/1855/1637 3200/1885/1665 -f 3185/1846/1631 3186/1848/1633 3204/1866/1647 -f 3186/7419/1633 3172/1851/1620 3187/1850/1634 -f 3216/1858/1640 3177/1842/1627 3194/7413/1643 -f 3204/7420/1647 3187/1850/1634 3210/1896/1675 -f 3196/7416/5778 3197/7417/1661 3218/7422/5779 -f 3210/1896/1675 3211/1868/1649 3224/7424/1678 -f 3224/7424/1678 3211/1868/1649 3212/1870/1651 -f 3225/1897/1676 3212/7423/1651 3213/1872/1653 -f 3215/1877/1657 3193/1853/1635 3216/1858/1640 -f 3228/1890/1670 3205/1889/1669 3206/7421/1642 -f 3229/7429/5781 3206/7421/1642 3217/7430/1659 -f 3230/7432/5782 3217/1879/1659 3207/1878/1658 -f 3207/1878/1658 3218/7422/5779 3232/7434/5784 -f 3218/7422/5779 3219/7425/1660 3233/7435/1682 -f 3219/1880/1660 3220/1883/1663 3234/1904/1683 -f 3238/7436/5785 3223/1894/1674 3224/7424/1678 -f 3242/7438/5786 3228/1890/1670 3229/7429/5781 -f 3243/7439/5787 3229/7429/5781 3230/7431/5782 -f 3244/7441/5788 3230/7432/5782 3231/7433/5783 -f 3231/7433/5783 3232/7434/5784 3246/7443/5790 -f 3232/7444/5784 3233/1903/1682 3247/1902/1681 -f 3238/7446/5785 3239/7450/1677 3253/1912/1690 -f 3254/7448/5791 3239/1898/1677 3240/1900/1679 -f 3255/7449/5792 3240/1900/1679 3241/1901/1680 -f 3256/7452/5793 3241/1901/1680 3215/1877/1657 -f 3257/1931/1707 3215/1877/1657 3227/7428/5780 -f 3258/1914/1692 3227/7428/5780 3242/7438/5786 -f 3259/1915/1693 3242/7438/5786 3243/7439/5787 -f 3260/7453/5794 3243/7439/5787 3244/7440/5788 -f 3261/7455/5795 3244/9380/5788 3245/7456/5789 -f 3245/7456/5789 3246/9381/5790 3263/7458/5797 -f 3246/7445/5790 3247/1902/1681 3264/1918/1696 -f 3247/1902/1681 3248/1905/1684 3265/1916/1694 -f 3248/1905/1684 3235/1891/1671 3266/1919/1697 -f 3235/7426/1671 3249/1906/1685 3267/1921/1699 -f 3249/1906/1685 3250/1907/1686 3268/1924/1701 -f 3250/1907/1686 3251/1908/1687 3269/1926/1703 -f 3251/1908/1687 3238/7447/5785 3252/1928/1689 -f 3253/1912/1690 3254/7451/5791 3271/7460/5798 -f 3254/7448/5791 3255/7449/5792 3272/7462/5799 -f 3255/7449/5792 3256/7452/5793 3273/7464/5800 -f 3259/1915/1693 3260/7453/5794 3275/1933/1709 -f 3260/7453/5794 3261/7454/5795 3276/7465/5801 -f 3261/7455/5795 3262/7457/5796 3277/7466/5802 -f 3277/7466/5802 3262/7457/5796 3263/7458/5797 -f 3278/7469/5803 3263/7459/5797 3264/1918/1696 -f 3300/7470/5804 3284/9382/1705 3270/7471/1688 -f 3270/7471/1688 3271/9383/5798 3286/1940/1716 -f 3271/7463/5798 3272/7462/5799 3287/1944/1718 -f 3272/7462/5799 3273/7464/5800 3288/1945/1720 -f 3273/7464/5800 3257/1931/1707 3289/1930/1706 -f 3274/1913/1691 3290/1948/1723 3289/1930/1706 -f 3275/1933/1709 3276/7465/5801 3292/7474/5805 -f 3276/7475/5801 3277/9384/5802 3293/1951/1726 -f 3293/1951/1726 3277/9384/5802 3278/7477/5803 -f 3294/7478/1724 3278/7469/5803 3279/1935/1711 -f 3299/1937/1713 3283/1927/1704 3284/1929/1705 -f 3314/1960/1733 3300/7470/5804 3285/1939/1715 -f 3291/1932/1708 3292/7474/5805 3307/7480/1741 -f 3292/7476/5805 3293/1951/1726 3308/1950/1725 -f 3313/1978/1747 3299/1937/1713 3300/7479/5804 -f 3301/1938/1714 3302/1941/1717 3316/1961/1734 -f 3323/1990/1756 3308/1950/1725 3309/1953/1727 -f 3361/2038/1791 3348/2007/1769 3349/2024/1781 -f 3365/7487/5806 3353/2014/1773 3354/2015/1774 -f 3377/2054/1796 3352/7486/1772 3365/7487/5806 -f 3378/2052/1802 3365/7487/5806 3366/7488/5807 -f 3355/2028/1784 3380/2059/1807 3379/2058/1804 -f 3380/2059/1807 3367/2030/1785 3381/2061/1809 -f 3385/2066/1813 3372/2036/1790 3373/7490/1792 -f 3386/2069/1811 3373/2039/1792 3374/2040/1793 -f 3390/7491/5808 3376/2042/1795 3377/2044/1796 -f 3393/2057/1806 3381/2061/1809 3394/7494/5809 -f 3382/2045/1797 3383/2046/1798 3399/7497/5811 -f 3401/7501/5815 3384/2048/1799 3370/2034/1788 -f 3403/7504/5817 3370/2034/1788 3385/2066/1813 -f 3407/7505/5818 3387/2067/1814 3388/2049/1800 -f 3389/7493/1801 3390/7491/5808 3412/7509/5821 -f 3390/7491/5808 3391/7492/1803 3413/7513/5824 -f 3391/2053/1803 3392/2056/1805 3415/7514/5825 -f 3392/2056/1805 3393/7496/1806 3416/7517/5827 -f 3420/7518/1822 3418/2072/1818 3419/2071/1817 -f 3423/7521/5828 3421/2075/1821 3422/2074/1820 -f 3425/2091/1837 3423/7521/5828 3424/7522/5829 -f 3428/7524/5831 3427/2089/1835 3425/2091/1837 -f 3431/2078/1824 3429/2077/1823 3427/2098/1835 -f 3434/2081/1827 3432/2080/1826 3430/2079/1825 -f 3437/2084/1830 3435/2083/1829 3433/2095/1828 -f 3439/7527/5832 3438/2092/1838 3436/2085/1831 -f 3440/7528/5833 3438/7538/1838 3439/7529/5832 -f 3437/2084/5835 3434/7526/5835 3441/7531/5835 -f 3434/7526/5835 3431/9385/5835 3419/9386/5835 -f 3441/7531/5835 3434/7526/5835 3419/9386/5835 -f 3431/9385/5835 3428/9387/5835 3422/9388/5835 -f 3428/9387/5835 3426/9389/5835 3424/9390/5835 -f 3422/9388/5835 3428/9387/5835 3424/9390/5835 -f 3422/9388/5835 3419/9386/5835 3431/9385/5835 -f 3442/2073/1819 3440/7528/5833 3441/7530/5834 -f 3444/7532/5836 3443/7536/5839 3418/2072/1818 -f 3458/7533/5837 3457/7537/5840 3440/7528/5833 -f 3447/2105/1847 3446/7539/5841 3423/7521/5828 -f 3445/7534/5838 3444/7532/5836 3420/7518/1822 -f 3443/7536/5839 3458/7533/5837 3442/2073/1819 -f 3457/7537/5840 3456/7550/1841 3438/7538/1838 -f 3446/7539/5841 3445/7543/5838 3421/2075/1821 -f 3485/7540/5842 3469/2103/1845 3470/2104/1846 -f 3462/7542/5844 3461/7567/5851 3445/7543/5838 -f 3463/2107/1849 3462/7542/5844 3446/7539/5841 -f 3473/7545/5845 3472/7547/5846 3455/2093/1839 -f 3459/7548/5847 3475/7552/5850 3458/7533/5837 -f 3474/7549/5848 3473/7561/5845 3456/7550/1841 -f 3460/7551/5849 3459/7548/5847 3443/7536/5839 -f 3475/7552/5850 3474/7549/5848 3457/7537/5840 -f 3461/7553/5851 3460/7551/5849 3444/7532/5836 -f 3480/2120/5852 3479/2141/5853 3463/2107/1849 -f 3486/7541/5843 3470/2104/1846 3471/2108/1850 -f 3480/2120/5852 3464/2106/1848 3465/7555/1851 -f 3487/7554/5854 3471/2108/1850 3472/7547/5846 -f 3481/7557/5855 3465/2109/1851 3466/2111/1852 -f 3476/7559/5858 3490/7564/5862 3475/7552/5850 -f 3489/7560/5859 3488/9391/5856 3473/7561/5845 -f 3482/7558/5857 3466/2111/1852 3467/2112/1853 -f 3477/7563/5861 3476/7559/5858 3459/7548/5847 -f 3490/7564/5862 3489/7560/5859 3474/7549/5848 -f 3483/7562/5860 3467/2112/1853 3468/2087/1833 -f 3478/2137/5864 3477/2130/5861 3460/7566/5849 -f 3484/7565/5863 3468/2087/1833 3469/7568/1845 -f 3479/2141/5853 3478/2137/5864 3461/7567/5851 -f 3495/2142/1869 3479/2141/1854 3480/2120/1854 -f 3514/2151/1874 3491/2150/1860 3492/2149/1863 -f 3513/7573/1856 3504/9392/1855 3505/2152/1864 -f 3510/7574/5865 3509/7572/1872 3498/7571/1861 -f 3506/7576/1873 3493/9393/1862 3494/2155/1867 -f 3511/7578/5866 3510/9259/5865 3500/2136/1866 -f 3507/2154/1870 3495/2153/1869 3496/7579/1858 -f 3512/7581/5868 3511/7578/5866 3502/2115/5867 -f 3509/7572/1872 3508/9394/1871 3496/7582/1858 -f 3515/2158/1877 3539/2157/1876 3554/2192/1908 -f 3544/2171/1888 3522/2170/1887 3523/7583/5869 -f 3523/7586/5869 3524/2174/1891 3546/2173/1890 -f 3546/2173/1890 3525/2172/1889 3526/2176/1893 -f 3528/2178/1895 3529/9395/5870 3548/2199/1915 -f 3529/7587/5870 3530/2180/1897 3549/2179/1896 -f 3536/2191/1907 3537/2193/1909 3554/2192/1908 -f 3539/2157/1876 3555/7590/1920 3567/2201/1917 -f 3555/7590/1920 3539/2157/1876 3540/7584/1880 -f 3556/2202/1918 3540/2161/1880 3541/2164/1882 -f 3557/2205/1921 3541/2164/1882 3542/2166/1884 -f 3558/2211/1923 3542/7585/1884 3543/2168/1886 -f 3559/2209/1925 3543/2168/1886 3544/2171/1888 -f 3548/7588/1915 3549/2179/1896 3563/2217/1930 -f 3549/2179/1896 3550/2182/1899 3564/2219/1932 -f 3551/2184/1901 3552/2186/1903 3566/7592/5871 -f 3567/2201/1917 3555/7590/1920 3568/2222/1935 -f 3572/2210/1926 3559/2209/1925 3560/2196/1912 -f 3560/2196/1912 3545/2195/1911 3574/2228/1927 -f 3564/2219/1932 3565/2200/1916 3579/2234/1945 -f 3565/2200/1916 3566/7592/5871 3580/2236/1947 -f 3566/7592/5871 3553/7589/1906 3581/2238/1949 -f 3567/2201/1917 3582/2221/1934 3581/7595/1949 -f 3583/2220/1933 3568/2222/1935 3569/7593/1919 -f 3584/7597/1950 3569/2203/1919 3570/2206/1922 -f 3574/2212/1927 3575/2214/1928 3591/7599/1955 -f 3575/2214/1928 3576/7594/1942 3592/7601/1941 -f 3583/2220/1933 3597/2255/1962 3610/2252/1960 -f 3598/2259/1951 3584/7597/1950 3585/7598/5872 -f 3599/2257/1963 3585/7598/5872 3586/2223/1936 -f 3600/2260/1965 3586/2223/1936 3587/7600/1937 -f 3601/2266/1967 3587/2224/1937 3588/2227/1939 -f 3602/2264/1969 3588/2227/1939 3589/2229/1940 -f 3604/7603/1954 3605/7607/5875 3620/7604/5873 -f 3605/7607/5875 3606/2248/1956 3621/7614/5876 -f 3606/2248/1956 3607/2249/1957 3622/7615/5877 -f 3607/2249/1957 3608/2250/1958 3623/7616/5878 -f 3608/7617/1958 3609/7608/1959 3624/7618/5879 -f 3609/7608/1959 3610/2252/1960 3625/7620/5880 -f 3597/2255/1962 3611/2254/1961 3627/2274/1977 -f 3627/7621/1977 3611/7610/1961 3612/7609/1964 -f 3628/7623/5881 3612/2258/1964 3613/2261/1966 -f 3629/2277/1980 3613/9396/1966 3614/7625/1968 -f 3630/2280/1978 3614/9397/1968 3615/7612/1970 -f 3631/2278/1981 3615/7612/1970 3616/2267/1971 -f 3632/2281/1983 3616/2267/1971 3617/2269/1972 -f 3648/7626/5882 3618/2270/1973 3619/7613/5874 -f 3619/7628/5874 3620/9398/5873 3635/7629/5884 -f 3620/7604/5873 3621/7614/5876 3636/7631/5885 -f 3621/7633/5876 3622/7636/5877 3637/7634/5886 -f 3622/7636/5877 3623/7619/5878 3638/7637/5887 -f 3623/7619/5878 3624/7618/5879 3639/7638/5888 -f 3624/7618/5879 3625/7620/5880 3640/2285/1987 -f 3625/7620/5880 3597/2255/1962 3626/2273/1976 -f 3627/7621/1977 3628/7622/5881 3642/7639/5889 -f 3642/7641/5889 3628/9399/5881 3629/2277/1980 -f 3634/7630/5883 3635/7629/5884 3650/7643/5891 -f 3635/7647/5884 3636/7635/5885 3651/2288/1990 -f 3636/7635/5885 3637/7634/5886 3652/2289/1991 -f 3637/7634/5886 3638/7637/5887 3653/2291/1993 -f 3638/7637/5887 3639/7638/5888 3654/2293/1995 -f 3639/7638/5888 3640/2285/1987 3655/2284/1986 -f 3641/2272/1975 3657/7677/5894 3656/2286/1988 -f 3641/7640/1975 3642/7639/5889 3658/7649/5893 -f 3658/7651/5893 3642/7641/5889 3643/7642/5890 -f 3659/7652/5895 3643/7642/5890 3644/2276/1979 -f 3660/7653/5896 3644/2276/1979 3645/7645/1982 -f 3661/7655/5897 3645/2279/1982 3646/2282/1984 -f 3662/2296/1998 3646/2282/1984 3647/2283/1985 -f 3664/7656/5898 3647/2283/1985 3648/7626/5882 -f 3665/7657/5899 3648/7626/5882 3649/7646/5892 -f 3649/7644/5892 3650/7643/5891 3667/7659/5901 -f 3650/7648/5891 3651/2288/1990 3668/7661/5902 -f 3654/2293/1995 3655/2284/1986 3673/7663/5903 -f 3674/7665/5905 3658/7651/5893 3659/7652/5895 -f 3675/7666/5906 3659/7652/5895 3660/7653/5896 -f 3676/7668/5907 3660/7653/5896 3661/7654/5897 -f 3677/7670/5908 3661/7655/5897 3662/2296/1998 -f 3666/7660/5900 3667/7659/5901 3679/7671/5909 -f 3667/7662/5901 3668/7661/5902 3680/7672/5910 -f 3668/7661/5902 3669/2287/1989 3681/7674/5911 -f 3669/2287/1989 3670/2290/1992 3682/7675/5912 -f 3670/2290/1992 3671/2292/1994 3683/7676/5913 -f 3657/7677/5894 3685/7699/5915 3684/7678/5914 -f 3685/7679/5915 3657/7650/5894 3674/7667/5905 -f 3686/2299/2001 3674/7665/5905 3675/7666/5906 -f 3675/7666/5906 3676/7668/5907 3688/7681/5916 -f 3688/7681/5916 3676/7668/5907 3677/7669/5908 -f 3689/7683/5917 3677/7670/5908 3678/2295/1997 -f 3690/2302/2004 3678/2295/1997 3663/2294/1996 -f 3691/2300/2002 3663/2294/1996 3664/7656/5898 -f 3692/7684/5918 3664/7656/5898 3665/7657/5899 -f 3665/7657/5899 3666/7658/5900 3694/7686/2012 -f 3694/2310/2012 3679/7671/5909 3696/2305/2007 -f 3680/7672/5910 3697/2307/2009 3696/7687/2007 -f 3680/7672/5910 3681/7674/5911 3698/2308/2010 -f 3681/7674/5911 3682/7675/5912 3699/7688/5920 -f 3682/7675/5912 3683/7676/5913 3700/7689/5921 -f 3683/7676/5913 3672/7664/5904 3701/7690/5922 -f 3672/7664/5904 3673/7663/5903 3702/7691/5923 -f 3673/7663/5903 3656/2286/1988 3684/7678/5914 -f 3704/7692/5924 3688/7681/5916 3689/7682/5917 -f 3705/7695/5925 3689/7683/5917 3690/2302/2004 -f 3698/2308/2010 3699/7688/5920 3709/7696/5926 -f 3699/7688/5920 3700/7689/5921 3710/7698/5927 -f 3685/7699/5915 3712/9400/2014 3711/2322/2024 -f 3712/2312/2014 3685/7679/5915 3703/7694/2000 -f 3703/2298/2000 3687/2297/1999 3714/7700/5928 -f 3714/7700/5928 3687/2297/1999 3704/7692/5924 -f 3715/7702/5929 3704/7692/5924 3705/7693/5925 -f 3716/7704/5930 3705/7695/5925 3706/2301/2003 -f 3717/2316/2018 3706/2301/2003 3691/2300/2002 -f 3718/2314/2016 3691/2300/2002 3692/7684/5918 -f 3719/2319/2021 3692/7684/5918 3693/7685/5919 -f 3693/7685/5919 3694/7686/2012 3721/7706/2011 -f 3695/2304/2006 3707/2303/2005 3722/7707/5932 -f 3707/7697/2005 3697/2307/2009 3723/7708/5933 -f 3708/2306/2008 3724/7711/5935 3723/7708/5933 -f 3708/2306/2008 3709/7696/5926 3725/7710/5934 -f 3709/7696/5926 3710/7698/5927 3726/7712/5936 -f 3710/7698/5927 3701/7690/5922 3727/7713/5937 -f 3701/7690/5922 3702/7691/5923 3728/2324/2026 -f 3702/7691/5923 3684/7678/5914 3711/2322/2024 -f 3713/7701/2015 3714/7700/5928 3730/7714/5938 -f 3730/7714/5938 3714/7700/5928 3715/7702/5929 -f 3731/7716/5939 3715/7702/5929 3716/7703/5930 -f 3732/7718/5940 3716/7704/5930 3717/2316/2018 -f 3722/7709/5932 3723/7708/5933 3735/7719/5941 -f 3723/7708/5933 3724/7711/5935 3736/7721/5942 -f 3724/7711/5935 3725/7710/5934 3737/7722/5943 -f 3725/7710/5934 3726/7712/5936 3738/7723/5944 -f 3711/2322/2024 3712/9400/2014 3739/7724/5945 -f 3729/7715/2013 3730/7714/5938 3741/7726/5947 -f 3730/7714/5938 3731/7716/5939 3742/7729/5948 -f 3731/7716/5939 3732/7717/5940 3743/7730/5949 -f 3732/7718/5940 3733/2315/2017 3744/7731/5950 -f 3733/2315/2017 3718/2314/2016 3745/2318/2020 -f 3720/7705/5931 3746/7733/5951 3745/2318/2020 -f 3721/7706/2011 3747/9401/5953 3746/7733/5951 -f 3748/7734/2023 3734/7720/2022 3735/7719/5941 -f 3749/7735/5952 3735/7719/5941 3736/7721/5942 -f 3750/7737/5954 3736/7721/5942 3737/7722/5943 -f 3751/7738/5955 3737/7722/5943 3738/7723/5944 -f 3738/7723/5944 3727/7713/5937 3753/2326/2028 -f 3753/2326/2028 3727/7713/5937 3728/2324/2026 -f 3740/7727/2019 3741/7726/5947 3756/2328/2030 -f 3741/7726/5947 3742/7729/5948 3757/2329/2031 -f 3742/7729/5948 3743/7730/5949 3758/2331/2033 -f 3743/7732/5949 3744/7731/5950 3759/2334/2035 -f 3744/7731/5950 3745/2318/2020 3760/2336/2037 -f 3761/7742/5958 3748/7734/2023 3749/7735/5952 -f 3762/2339/2040 3749/7735/5952 3750/7737/5954 -f 3763/2337/2038 3750/7737/5954 3751/7738/5955 -f 3764/2340/2041 3751/7738/5955 3752/7739/5956 -f 3765/2342/2043 3752/7739/5956 3753/2326/2028 -f 3767/7725/5946 3739/7724/5945 3769/7744/5959 -f 3755/7741/5957 3770/9402/5961 3769/7746/5959 -f 3755/7740/5957 3756/2328/2030 3771/2327/2029 -f 3760/2336/2037 3746/7733/5951 3776/2346/2047 -f 3746/7733/5951 3747/9401/5953 3777/2347/2048 -f 3777/7750/2048 3747/7736/5953 3761/7743/5958 -f 3778/7752/5963 3761/7742/5958 3762/2339/2040 -f 3783/2348/2049 3766/2325/2027 3767/7725/5946 -f 3769/7746/5959 3770/9402/5961 3785/2352/2053 -f 3770/7754/5961 3771/9403/2029 3786/7755/5964 -f 3771/2327/2029 3772/2330/2032 3787/7757/5965 -f 3772/7749/2032 3773/2332/2034 3788/7759/5966 -f 3773/2332/2034 3774/2335/2036 3789/7761/5967 -f 3777/7750/2048 3778/7751/5963 3791/7762/5968 -f 3791/7764/5968 3778/9404/5963 3779/7765/2039 -f 3792/7767/5969 3779/2338/2039 3780/2341/2042 -f 3793/7768/5970 3780/2341/2042 3781/2343/2044 -f 3794/7769/5971 3781/2343/2044 3782/2344/2045 -f 3785/7756/2053 3786/7755/5964 3797/7770/5972 -f 3786/7755/5964 3787/9405/5965 3798/7772/5973 -f 3787/7773/5965 3788/9406/5966 3799/7774/5974 -f 3788/7759/5966 3789/7761/5967 3800/7776/5975 -f 3789/7761/5967 3775/7748/5962 3801/2354/2055 -f 3790/7763/2046 3791/7762/5968 3803/7778/5976 -f 3803/7780/5976 3791/7764/5968 3792/7766/5969 -f 3804/7781/5977 3792/7766/5969 3793/7782/5970 -f 3805/7783/5978 3793/7782/5970 3794/7784/5971 -f 3806/7786/5979 3794/7769/5971 3795/2349/2050 -f 3807/2357/2058 3795/2349/2050 3783/2348/2049 -f 3808/2355/2056 3783/2348/2049 3768/7745/5960 -f 3809/2374/2073 3768/7745/5960 3784/7753/2052 -f 3796/2350/2051 3811/9407/2078 3810/7787/2075 -f 3796/7771/2051 3797/7770/5972 3812/2382/2080 -f 3797/7770/5972 3798/7772/5973 3814/2360/2061 -f 3798/7775/5973 3799/7774/5974 3815/7788/5980 -f 3799/7774/5974 3800/9408/5975 3816/7790/5981 -f 3790/2345/2046 3818/7799/5982 3817/2353/2054 -f 3819/2361/2062 3803/7780/5976 3804/7781/5977 -f 3820/2364/2065 3804/7781/5977 3805/7783/5978 -f 3821/2366/2067 3805/7783/5978 3806/7785/5979 -f 3822/2368/2069 3806/7785/5979 3807/7794/2058 -f 3814/7789/2061 3815/7788/5980 3825/2386/2083 -f 3815/7788/5980 3816/7790/5981 3826/2388/2085 -f 3816/7790/5981 3801/9409/2055 3827/2390/2087 -f 3844/2402/2095 3830/2367/2068 3831/2369/2070 -f 3845/2404/2097 3831/2369/2070 3832/2371/2071 -f 3846/2406/2099 3832/2371/2071 3833/2373/2072 -f 3836/2378/2077 3837/2381/2079 3849/2410/2103 -f 3843/7798/5983 3818/7799/5982 3828/7801/2063 -f 3853/7804/5984 3828/2362/2063 3829/2365/2066 -f 3858/2411/2104 3847/2394/2089 3834/2393/2074 -f 3837/2381/2079 3850/2398/2092 3862/2417/2110 -f 3850/7803/2092 3851/2400/2093 3863/7806/5985 -f 3851/2400/2093 3852/2401/2094 3864/7808/5986 -f 3852/2401/2094 3841/2389/2086 3865/2426/2118 -f 3841/2389/2086 3842/7797/2088 3866/2428/2120 -f 3843/7798/5983 3867/2431/2122 3866/2430/2120 -f 3868/7809/5987 3854/2403/2096 3855/2405/2098 -f 3869/7810/5988 3855/2405/2098 3856/2407/2100 -f 3870/7811/5989 3856/2407/2100 3857/2408/2101 -f 3848/7800/2091 3849/2410/2103 3873/2419/2112 -f 3862/7807/2110 3863/7806/5985 3875/2422/2114 -f 3863/7806/5985 3864/7808/5986 3876/2424/2116 -f 3867/2431/2122 3843/7798/5983 3853/7802/5984 -f 3877/7813/2137 3853/7804/5984 3868/7809/5987 -f 3878/2434/2125 3868/7809/5987 3869/7810/5988 -f 3879/2432/2123 3869/7810/5988 3870/7811/5989 -f 3880/2435/2126 3870/7811/5989 3871/2412/2105 -f 3881/2437/2128 3871/2412/2105 3858/2411/2104 -f 3882/2439/2130 3858/2411/2104 3859/7805/2090 -f 3883/2414/2107 3860/2413/2106 3884/2444/2134 -f 3886/2420/2113 3887/2423/2115 3900/7815/5990 -f 3887/2423/2115 3888/2425/2117 3901/7820/5991 -f 3905/2455/2142 3893/2436/2127 3894/2438/2129 -f 3906/7821/5992 3894/2438/2129 3895/2440/2131 -f 3907/2464/2148 3895/2440/2131 3896/2442/2132 -f 3898/2446/2135 3899/2447/2136 3912/7823/5993 -f 3899/7816/2136 3900/7815/5990 3913/7826/5994 -f 3900/7815/5990 3901/7820/5991 3914/7828/5995 -f 3901/7820/5991 3889/2427/2119 3915/7829/5996 -f 3889/2427/2119 3890/7817/2121 3916/7830/2145 -f 3917/7831/5997 3904/2453/2141 3905/2455/2142 -f 3918/7832/5998 3905/2455/2142 3906/7821/5992 -f 3919/7835/5999 3906/7821/5992 3907/2464/2148 -f 3897/2443/2133 3911/2459/2144 3922/2471/2154 -f 3911/2459/2144 3912/9410/5993 3923/2473/2156 -f 3912/7827/5993 3913/7826/5994 3924/2478/2158 -f 3913/7826/5994 3914/7828/5995 3925/2479/2160 -f 3927/2465/2149 3903/2452/2140 3917/7834/5997 -f 3928/2482/2163 3917/7834/5997 3918/7838/5998 -f 3929/7839/2165 3918/7832/5998 3919/7835/5999 -f 3930/2490/2167 3919/7835/5999 3920/2463/2147 -f 3931/2488/2169 3920/2463/2147 3908/2462/2146 -f 3925/2479/2160 3915/7829/5996 3939/7841/6000 -f 3916/7845/2145 3940/2513/2190 3939/7846/6000 -f 3946/7848/6002 3931/2488/2169 3932/7840/2150 -f 3932/2466/2150 3909/2457/2143 3933/2494/2172 -f 3934/2468/2151 3935/2472/2155 3951/2501/2180 -f 3935/2472/2155 3936/7843/2157 3952/2503/2182 -f 3936/2474/2157 3937/7852/2159 3953/2506/2184 -f 3937/7852/2159 3938/9411/6001 3954/2508/2186 -f 3938/7842/6001 3939/7841/6000 3955/7853/2188 -f 3941/2481/2162 3926/2480/2161 3942/2483/2164 -f 3956/2514/2191 3942/2483/2164 3943/2485/2166 -f 3957/7855/6003 3943/2485/2166 3944/2487/2168 -f 3958/7856/6004 3944/2487/2168 3945/7851/2170 -f 3959/7857/6005 3945/7851/2170 3946/7858/6002 -f 3960/7860/6006 3946/7848/6002 3947/7849/2175 -f 3969/2512/2189 3940/2513/2190 3941/7850/2162 -f 3972/2520/2197 3956/2514/2191 3957/7855/6003 -f 3973/2522/2199 3957/7855/6003 3958/7856/6004 -f 3974/2523/2200 3958/7856/6004 3959/7857/6005 -f 3975/2525/2202 3959/7857/6005 3960/7859/6006 -f 3976/7865/2204 3960/9412/6006 3961/2495/2174 -f 3964/2499/2178 3965/2502/2181 3979/2535/2211 -f 3965/7863/2181 3966/2504/2183 3980/2538/2213 -f 3966/2504/2183 3967/2507/2185 3981/2540/2215 -f 3967/2507/2185 3968/7864/2187 3982/2542/2217 -f 3969/2512/2189 3983/2518/2195 3982/2544/2217 -f 3963/2498/2177 3978/2516/2193 3992/2554/2227 -f 3999/2548/2221 3984/2519/2196 3985/2521/2198 -f 4001/2562/2235 3985/2521/2198 3986/2524/2201 -f 4002/7869/6007 3986/2524/2201 3987/2526/2203 -f 4005/2552/2225 3988/2528/2205 3989/2530/2207 -f 4006/7870/2223 3989/7867/2207 3990/2531/2208 -f 3990/2531/2208 3962/2497/2176 4008/7872/2240 -f 3948/2491/2171 4009/2568/2241 4008/2567/2240 -f 4012/2573/2231 3970/7866/2194 3984/2519/2196 -f 3992/2554/2227 3993/2534/2210 4015/2578/2249 -f 3993/2534/2210 3994/7868/2212 4016/2580/2244 -f 4031/2563/2236 4001/2562/2235 4002/7869/6007 -f 4002/7869/6007 4003/2565/2238 4020/2564/2237 -f 4006/7870/2223 4007/7871/6008 4023/7873/6009 -f 4023/7873/6009 4008/7872/2240 4024/2576/2239 -f 4017/2555/2228 4018/2556/2229 4027/2583/2253 -f 4018/2556/2229 4010/2546/2220 4028/2585/2255 -f 4010/7874/2220 4011/2559/2232 4029/2588/2257 -f 4011/2559/2232 4012/2558/2231 4030/2589/2245 -f 4032/2594/2262 4020/2564/2237 4021/2551/2224 -f 4033/2596/2264 4021/2551/2224 4022/7876/6010 -f 4022/7875/6010 4023/7873/6009 4035/2575/2247 -f 4030/2572/2245 4013/2549/2222 4060/2561/2234 -f 4025/2569/2242 4038/2579/2250 4054/2605/2272 -f 4038/2579/2250 4039/7879/2251 4055/2607/2274 -f 4039/2581/2251 4040/2582/2252 4056/2610/2276 -f 4040/2582/2252 4041/2584/2254 4057/7883/2278 -f 4041/7884/2254 4042/9413/2256 4058/7885/6011 -f 4042/2586/2256 4043/2590/2258 4059/7886/6012 -f 4061/2614/2279 4046/2593/2261 4047/2595/2263 -f 4062/2617/2282 4047/2595/2263 4048/2597/2265 -f 4063/2619/2284 4048/2597/2265 4049/2599/2267 -f 4064/7890/2286 4049/9414/2267 4050/2600/2268 -f 4065/7891/2290 4050/2600/2268 4036/2574/2246 -f 4051/7882/2288 4037/2577/2248 4066/2629/2292 -f 4057/2613/2278 4058/7885/6011 4071/2637/2298 -f 4058/7885/6011 4059/9415/6012 4072/2639/2300 -f 4072/2642/2300 4059/7888/6012 4060/2561/2234 -f 4073/2640/2301 4060/2561/2234 4044/2591/2259 -f 4044/2591/2259 4045/2616/2281 4074/2615/2280 -f 4077/2622/2287 4064/2621/2286 4065/2626/2290 -f 4080/2630/2293 4081/2631/2294 4095/7896/2317 -f 4081/7900/2294 4082/9416/2295 4096/2663/2318 -f 4082/2632/2295 4083/2635/2296 4097/2666/2320 -f 4083/2635/2296 4084/2636/2297 4098/2668/2322 -f 4084/2636/2297 4085/2638/2299 4099/2670/2324 -f 4085/2641/2299 4086/2644/2303 4100/2652/2309 -f 4132/7902/6014 4105/9417/2315 4093/2651/2308 -f 4108/7905/6016 4093/2651/2308 4094/7897/6013 -f 4094/7897/6013 4095/7896/2317 4112/7908/6019 -f 4099/7901/2324 4100/2652/2309 4121/7911/6022 -f 4100/2652/2309 4087/2643/2302 4123/7919/6027 -f 4124/7921/6029 4087/2643/2302 4101/2653/2310 -f 4127/7923/6031 4101/7904/2310 4102/2655/2312 -f 4131/7927/6034 4104/2658/2314 4105/2660/2315 -f 4133/2675/2329 4158/2674/2328 4170/2703/2355 -f 4161/2714/2337 4139/9418/2336 4140/7929/6035 -f 4162/2712/2363 4140/7929/6035 4141/7930/6036 -f 4141/7932/6036 4142/2688/2340 4164/2687/2339 -f 4164/2687/2339 4144/2689/2341 4145/2691/2343 -f 4147/2693/2345 4148/9419/2347 4166/2717/2346 -f 4149/2696/2348 4150/7934/6038 4167/2719/2368 -f 4150/7934/6038 4151/2698/2350 4168/2721/2370 -f 4187/2731/2377 4162/2712/2363 4163/7931/6037 -f 4188/2748/2388 4163/7935/6037 4176/2715/2365 -f 4182/7936/2372 4171/2708/2359 4183/2725/2373 -f 4209/2755/2396 4195/2740/2384 4211/2761/2400 -f 4211/7945/2400 4195/7938/2384 4196/7937/6039 -f 4212/7946/6043 4196/7937/6039 4197/2741/2385 -f 4213/7947/6044 4197/2741/2385 4198/2742/2386 -f 4214/7948/6045 4198/2742/2386 4199/2744/2387 -f 4215/7950/6046 4199/7939/2387 4200/2746/2389 -f 4202/7940/6040 4203/2749/2391 4218/7952/6047 -f 4203/7942/2391 4204/7941/6041 4219/7953/6048 -f 4204/7941/6041 4205/2751/2392 4220/7955/6049 -f 4205/2751/2392 4206/2752/2393 4221/7956/6050 -f 4206/2752/2393 4207/2753/2394 4222/7957/6051 -f 4207/2753/2394 4208/2754/2395 4223/7958/6052 -f 4208/7943/2395 4209/2755/2396 4224/7944/6042 -f 4211/7960/2400 4212/9420/6043 4226/7961/6053 -f 4226/7964/6053 4212/7946/6043 4213/7947/6044 -f 4227/7965/2405 4213/7947/6044 4214/7948/6045 -f 4228/7966/2403 4214/7948/6045 4215/7949/6046 -f 4229/2771/2406 4215/9421/6046 4216/2757/2397 -f 4230/2769/2408 4216/2757/2397 4201/2747/2390 -f 4218/7968/6047 4219/9422/6048 4233/2777/2414 -f 4219/7953/6048 4220/7955/6049 4234/7969/2416 -f 4220/7955/6049 4221/7956/6050 4235/7971/2417 -f 4221/7956/6050 4222/7957/6051 4236/7972/2419 -f 4222/7973/6051 4223/7959/6052 4237/2784/2421 -f 4223/7959/6052 4224/7944/6042 4238/2786/2423 -f 4240/7974/6055 4226/7964/6053 4227/7965/2405 -f 4245/2772/2410 4231/2763/2402 4217/2762/2401 -f 4233/7970/2414 4234/7969/2416 4248/7979/6057 -f 4238/2786/2423 4210/2760/2399 4239/2759/2398 -f 4239/7963/2398 4225/7962/6054 4254/2789/2426 -f 4254/2789/2426 4225/7962/6054 4240/7976/6055 -f 4255/2790/2427 4240/7976/6055 4241/7982/6056 -f 4256/7984/6058 4241/9423/6056 4242/2765/2404 -f 4257/7985/6059 4242/2765/2404 4243/2768/2407 -f 4258/7987/6060 4243/7978/2407 4244/2770/2409 -f 4259/7988/6061 4244/2770/2409 4245/2772/2410 -f 4260/2793/2430 4245/2772/2410 4246/2774/2412 -f 4246/2774/2412 4232/2773/2411 4262/7989/2424 -f 4247/2775/2413 4248/9424/6057 4264/7990/6062 -f 4248/7992/6057 4249/2778/2415 4265/7993/6064 -f 4249/2778/2415 4250/2781/2418 4266/7995/6065 -f 4250/2781/2418 4251/2783/2420 4267/7996/6066 -f 4251/2783/2420 4252/2785/2422 4268/7997/6067 -f 4252/2785/2422 4239/2759/2398 4269/2795/2432 -f 4270/2788/2425 4255/2790/2427 4256/7983/6058 -f 4271/7999/6068 4256/7984/6058 4257/7985/6059 -f 4272/8000/6069 4257/7985/6059 4258/7986/6060 -f 4273/8002/6070 4258/7987/6060 4259/7988/6061 -f 4274/8003/6071 4259/7988/6061 4260/2793/2430 -f 4263/7991/6063 4264/7990/6062 4277/8004/6072 -f 4264/7994/6062 4265/7993/6064 4278/8006/6074 -f 4265/7993/6064 4266/7995/6065 4279/8008/6075 -f 4266/7995/6065 4267/7996/6066 4280/8009/6076 -f 4267/7996/6066 4268/7997/6067 4281/8010/6077 -f 4270/2788/2425 4284/2800/2436 4283/2797/2434 -f 4284/2800/2436 4270/2788/2425 4271/7998/6068 -f 4271/7999/6068 4272/8000/6069 4286/8012/6078 -f 4286/8012/6078 4272/8000/6069 4273/8001/6070 -f 4287/8015/6079 4273/8002/6070 4274/8003/6071 -f 4288/2804/2440 4274/8003/6071 4275/2792/2429 -f 4290/8016/6080 4275/2792/2429 4261/2791/2428 -f 4291/2813/2449 4261/2791/2428 4262/7989/2424 -f 4292/2839/2447 4262/2787/2424 4276/8005/6073 -f 4276/8005/6073 4277/8004/6072 4294/8018/2442 -f 4277/8007/6072 4278/8006/6074 4295/2807/2443 -f 4278/8006/6074 4279/8008/6075 4296/8019/6082 -f 4279/8008/6075 4280/8009/6076 4297/8020/6083 -f 4280/8009/6076 4281/8010/6077 4298/8021/6084 -f 4281/8010/6077 4269/2795/2432 4282/2794/2431 -f 4301/8023/6086 4286/8012/6078 4287/8014/6079 -f 4302/2809/2445 4287/8015/6079 4288/2804/2440 -f 4293/8017/6081 4294/8018/2442 4305/8025/2441 -f 4295/2807/2443 4296/8019/6082 4307/2816/2452 -f 4296/8019/6082 4297/8020/6083 4308/8027/6088 -f 4283/8011/2434 4310/2855/2458 4309/8028/6089 -f 4300/2799/2435 4312/2825/2461 4311/2823/2459 -f 4300/2799/2435 4285/2801/2437 4313/2826/2462 -f 4313/2828/2462 4285/8013/2437 4301/8023/6086 -f 4315/2830/2465 4301/8023/6086 4302/8024/2445 -f 4318/2810/2446 4289/2802/2438 4290/8016/6080 -f 4319/2835/2469 4290/8016/6080 4291/2813/2449 -f 4321/2837/2471 4292/2839/2447 4304/8026/6087 -f 4304/8026/6087 4305/8025/2441 4323/8030/6091 -f 4307/2816/2452 4308/8027/6088 4326/2820/2456 -f 4308/8027/6088 4298/8021/6084 4330/2842/2475 -f 4298/8021/6084 4299/8022/6085 4327/8032/6092 -f 4299/8022/6085 4282/2794/2431 4309/8028/6089 -f 4311/2823/2459 4312/2825/2461 4333/8033/6093 -f 4320/2812/2448 4292/2811/2447 4340/8034/2472 -f 4321/2837/2471 4322/8029/6090 4341/2852/2483 -f 4341/2852/2483 4322/8029/6090 4323/8030/6091 -f 4342/8037/6095 4323/8031/6091 4324/2818/2454 -f 4330/2842/2475 4327/8032/6092 4347/2858/2488 -f 4327/8032/6092 4309/8028/6089 4331/2857/2487 -f 4354/2851/2482 4341/2852/2483 4342/8036/6095 -f 4355/8042/6096 4342/8037/6095 4343/2840/2473 -f 4356/8043/6098 4343/2840/2473 4344/2841/2474 -f 4357/8044/6099 4344/2841/2474 4345/2843/2476 -f 4359/2867/2486 4310/2822/2458 4332/2821/2457 -f 4332/2821/2457 4333/8033/6093 4361/8045/6100 -f 4348/8039/2477 4362/9425/6102 4361/8045/6100 -f 4348/2844/2477 4349/2846/2478 4363/8046/6101 -f 4349/2846/2478 4350/2847/2479 4364/8048/6103 -f 4350/8040/2479 4351/2849/2480 4365/8049/6104 -f 4338/2850/2481 4366/8051/6105 4365/8049/6104 -f 4340/2838/2472 4354/2851/2482 4368/2862/2492 -f 4368/2862/2492 4354/2851/2482 4355/8038/6096 -f 4369/8053/6106 4355/8042/6096 4356/8043/6098 -f 4370/8054/6107 4356/8043/6098 4357/8044/6099 -f 4371/8055/6108 4357/8044/6099 4358/2854/2485 -f 4372/8056/6109 4358/2854/2485 4346/2853/2484 -f 4373/2859/2489 4347/2858/2488 4331/2857/2487 -f 4361/8045/6100 4362/9425/6102 4376/8057/6110 -f 4362/8047/6102 4363/8046/6101 4377/8059/2506 -f 4364/8048/6103 4379/8078/2499 4378/8061/2498 -f 4364/8050/6103 4365/8049/6104 4380/2873/2501 -f 4352/8041/6097 4353/8035/6094 4381/2882/2509 -f 4353/8035/6094 4340/8034/2472 4382/8062/6112 -f 4383/8063/2490 4368/9426/2492 4369/8064/6106 -f 4384/8067/6113 4369/8053/6106 4370/8054/6107 -f 4385/8068/6114 4370/8054/6107 4371/8055/6108 -f 4386/8069/6115 4371/8055/6108 4372/8056/6109 -f 4387/8070/6116 4372/8056/6109 4373/2859/2489 -f 4375/8058/6111 4390/9427/6117 4389/2866/2496 -f 4375/8071/6111 4376/8075/6110 4391/2878/2505 -f 4380/2873/2501 4366/8051/6105 4395/8073/2522 -f 4382/8066/6112 4367/2861/2491 4383/2860/2490 -f 4397/8080/6118 4383/8063/2490 4384/8065/6113 -f 4398/2885/2512 4384/8065/6113 4385/8081/6114 -f 4399/2883/2510 4385/8081/6114 4386/8082/6115 -f 4400/8083/2513 4386/8069/6115 4387/8070/6116 -f 4401/8084/2515 4387/8070/6116 4388/2864/2494 -f 4377/2879/2506 4378/2869/2498 4405/2893/2519 -f 4407/8085/6119 4397/8080/6118 4398/2885/2512 -f 4412/8087/2523 4402/2874/2502 4374/2863/2493 -f 4389/2866/2496 4390/9427/6117 4414/8089/2532 -f 4390/8072/6117 4391/2878/2505 4415/2902/2528 -f 4392/2868/2497 4393/9428/2500 4417/2910/2535 -f 4393/8091/2500 4394/2895/2521 4418/2894/2520 -f 4395/2896/2522 4396/9429/2508 4419/2914/2530 -f 4420/8092/2540 4382/8066/6112 4407/8086/6119 -f 4421/2920/2538 4407/8085/6119 4408/2884/2511 -f 4422/8094/6120 4408/2884/2511 4409/2887/2514 -f 4423/8095/6121 4409/2887/2514 4410/2889/2516 -f 4424/8096/6122 4410/2889/2516 4411/2891/2517 -f 4404/2877/2504 4405/2893/2519 4428/2933/2554 -f 4406/2880/2507 4382/8062/6112 4430/2962/2575 -f 4431/2918/2541 4422/8094/6120 4423/8095/6121 -f 4432/2921/2543 4423/8095/6121 4424/8096/6122 -f 4433/2923/2545 4424/8096/6122 4425/2898/2524 -f 4434/2925/2547 4425/2898/2524 4412/2897/2523 -f 4435/8097/2549 4412/8087/2523 4403/2876/2503 -f 4437/2907/2533 4427/2901/2527 4451/2956/2570 -f 4427/2901/2527 4428/2933/2554 4452/2932/2553 -f 4454/2945/2562 4443/2922/2544 4444/2924/2546 -f 4455/2943/2560 4444/2924/2546 4445/2926/2548 -f 4436/2905/2531 4458/2953/2568 4448/2929/2551 -f 4439/2911/2536 4440/2913/2537 4461/2959/2573 -f 4462/8103/2576 4441/8099/2539 4442/2919/2542 -f 4463/2968/2581 4442/2919/2542 4454/2945/2562 -f 4467/2947/2564 4446/2936/2556 4447/2928/2550 -f 4449/2930/2552 4450/8098/6123 4470/2974/2584 -f 4450/8098/6123 4451/2956/2570 4471/2955/2569 -f 4459/2941/2559 4460/8101/6124 4474/2979/2589 -f 4476/2966/2579 4464/8104/6125 4465/2944/2561 -f 4477/8106/6126 4465/2944/2561 4466/2946/2563 -f 4478/8109/6127 4466/2946/2563 4467/2947/2564 -f 4483/2995/2603 4462/2963/2576 4463/8110/2581 -f 4485/2981/2591 4477/8106/6126 4478/8109/6127 -f 4486/2983/2593 4478/8109/6127 4479/2964/2577 -f 4487/2985/2595 4479/2964/2577 4468/2948/2565 -f 4481/2965/2578 4502/3014/2616 4523/2989/2599 -f 4475/2960/2574 4483/2995/2603 4506/2994/2602 -f 4509/2998/2605 4497/2984/2594 4498/2986/2596 -f 4510/3009/2613 4498/2986/2596 4499/2987/2597 -f 4491/8112/2586 4492/2976/2587 4514/8120/6130 -f 4492/2976/2587 4493/8113/6128 4516/8122/6131 -f 4493/8113/6128 4494/2980/2590 4517/8124/6133 -f 4494/2980/2590 4495/8114/2609 4518/8125/2608 -f 4519/8126/6134 4509/8119/2605 4510/8127/2613 -f 4521/3017/2619 4500/2988/2598 4501/8116/2600 -f 4503/3015/2617 4489/2971/2583 4525/8130/6135 -f 4489/2971/2583 4504/2991/2601 4526/3019/2621 -f 4527/8131/6136 4506/2994/2602 4507/2997/2604 -f 4528/3026/2628 4507/2997/2604 4508/8118/6129 -f 4529/3024/2626 4508/8118/6129 4519/8126/6134 -f 4530/8132/6137 4519/8126/6134 4520/8128/2612 -f 4531/8133/6138 4520/8128/2612 4511/8134/2611 -f 4513/8136/2607 4514/8137/6130 4534/3035/2636 -f 4514/8137/6130 4515/8139/6132 4535/8138/6139 -f 4515/8139/6132 4516/9430/6131 4536/8140/6140 -f 4516/8122/6131 4517/8124/6133 4537/8141/6141 -f 4517/8143/6133 4518/3002/2608 4538/3038/2638 -f 4540/8145/6142 4530/8132/6137 4531/8133/6138 -f 4541/8146/6143 4531/8133/6138 4532/8135/2618 -f 4542/8147/2631 4532/3016/2618 4521/3017/2619 -f 4543/8149/2629 4521/9431/2619 4522/3011/2614 -f 4524/3013/2615 4547/3047/2646 4546/3023/2625 -f 4524/3013/2615 4525/8130/6135 4548/3031/2633 -f 4525/8130/6135 4526/3019/2621 4549/3032/2634 -f 4550/3054/2652 4505/3005/2610 4527/8131/6136 -f 4551/3052/2650 4527/8131/6136 4528/3026/2628 -f 4552/3025/2627 4529/3024/2626 4540/8145/6142 -f 4553/3039/2639 4540/8145/6142 4541/8146/6143 -f 4554/3041/2641 4541/8146/6143 4542/3029/2631 -f 4549/3032/2634 4533/3018/2620 4557/3050/2649 -f 4534/3035/2636 4535/8138/6139 4559/3067/2664 -f 4535/8138/6139 4536/8140/6140 4560/3069/2666 -f 4536/8140/6140 4537/9432/6141 4561/3071/2668 -f 4537/8144/6141 4538/3038/2638 4562/3037/2637 -f 4567/3055/2653 4555/3028/2630 4543/3027/2629 -f 4543/8149/2629 4544/3022/2624 4545/3021/2623 -f 4574/3074/2671 4565/3042/2642 4566/3043/2643 -f 4563/3060/2658 4564/3040/2640 4583/3087/2684 -f 4576/3044/2644 4569/3046/2645 4587/8154/6144 -f 4600/3085/2682 4581/8152/2669 4572/3053/2651 -f 4607/3097/2692 4582/3061/2659 4583/3087/2684 -f 4577/3057/2655 4578/3058/2656 4603/3090/2687 -f 4590/8157/2667 4580/3073/2670 4605/3096/2691 -f 4605/3096/2691 4580/3073/2670 4600/8160/2682 -f 4596/8158/6146 4597/3077/2674 4608/3088/2685 -f 4614/3094/2690 4605/3096/2691 4606/8162/2681 -f 4603/3090/2687 4611/3089/2686 4622/8166/6148 -f 4611/3089/2686 4612/8164/2688 4623/8167/6149 -f 4612/3091/2688 4613/3093/2689 4624/8168/6150 -f 4613/8170/2689 4614/9433/2690 4625/3118/2711 -f 4625/8172/2711 4614/3094/2690 4615/8163/6147 -f 4615/8174/6147 4606/3084/2681 4627/3098/2693 -f 4628/3119/2712 4607/3097/2692 4601/3086/2683 -f 4629/3132/2723 4601/3086/2683 4592/3081/2678 -f 4632/3111/2705 4618/3110/2698 4619/8176/2697 -f 4621/3106/2701 4622/8166/6148 4637/3124/2716 -f 4622/8166/6148 4623/8167/6149 4638/3126/2718 -f 4623/8169/6149 4624/8168/6150 4639/8181/2720 -f 4624/8171/6150 4625/3118/2711 4640/3117/2710 -f 4641/3133/2724 4630/3108/2703 4631/3109/2704 -f 4642/3135/2726 4631/3109/2704 4632/3111/2705 -f 4643/3137/2728 4632/9434/2705 4633/8180/6151 -f 4644/3140/2730 4633/8180/6151 4620/8179/2699 -f 4640/3117/2710 4626/3116/2709 4651/3154/2741 -f 4651/8184/2741 4626/8175/2709 4627/3098/2693 -f 4659/8185/2737 4660/3150/2738 4674/3179/2761 -f 4660/3150/2738 4661/3152/2739 4675/3181/2763 -f 4661/3152/2739 4662/3153/2740 4676/3183/2751 -f 4671/3164/2749 4657/3145/2735 4684/3176/2758 -f 4672/8192/2750 4685/3196/2775 4684/3176/2758 -f 4672/3165/2750 4673/8190/2760 4686/8193/2759 -f 4676/3167/2751 4677/3169/2752 4690/3184/2765 -f 4710/8195/6152 4696/3191/2771 4697/3194/2773 -f 4697/3194/2773 4698/3195/2774 4713/8202/6156 -f 4699/8197/2786 4700/3197/2776 4718/8204/6158 -f 4700/3197/2776 4701/3198/2777 4719/8209/6161 -f 4701/3198/2777 4702/8198/2778 4721/8210/6162 -f 4702/3199/2778 4703/3202/2780 4724/8212/6164 -f 4726/8216/6167 4703/3202/2780 4704/3204/2781 -f 4704/8219/2781 4692/3186/2767 4730/8220/6170 -f 4730/8220/6170 4692/3186/2767 4705/3213/2788 -f 4735/3219/2793 4757/3218/2792 4769/3265/2832 -f 4757/3218/2792 4737/3220/2794 4738/8226/2797 -f 4758/3222/2796 4739/3221/2795 4740/3226/2800 -f 4762/3234/2807 4745/3233/2806 4746/3236/2809 -f 4752/3245/2817 4766/3279/2842 4765/3261/2829 -f 4753/3246/2818 4754/9435/2820 4768/3263/2819 -f 4755/3249/2821 4756/8224/6173 4769/3265/2832 -f 4757/3218/2792 4770/3267/2834 4781/3264/2831 -f 4775/3257/2826 4762/3234/2807 4763/3235/2808 -f 4781/3264/2831 4770/3267/2834 4782/3286/2847 -f 4788/8228/6175 4775/3257/2826 4776/3275/2839 -f 4795/3299/2858 4782/3286/2847 4796/3285/2846 -f 4813/3306/2864 4785/3289/2849 4786/3271/2836 -f 4799/3309/2862 4786/8230/2836 4787/8229/2837 -f 4800/8231/2865 4787/3272/2837 4788/8228/6175 -f 4801/3314/2867 4788/8228/6175 4789/3292/2852 -f 4824/8232/6176 4809/9436/2859 4810/8233/2877 -f 4830/3342/2890 4815/3308/2866 4816/3311/2868 -f 4821/3319/2874 4836/8241/6179 4835/3337/2886 -f 4821/3319/2874 4822/3320/2875 4837/8240/6178 -f 4822/3320/2875 4823/8237/6177 4838/8242/6180 -f 4823/8243/6177 4809/9436/2859 4824/8232/6176 -f 4824/8232/6176 4825/8234/2876 4840/8245/6181 -f 4825/3322/2876 4826/3324/2878 4841/8247/6183 -f 4826/3324/2878 4827/3326/2880 4843/3338/2887 -f 4831/3330/2882 4832/3332/2883 4848/8249/6184 -f 4848/8249/6184 4832/3332/2883 4833/8239/2884 -f 4849/8254/6186 4833/3334/2884 4834/3336/2885 -f 4850/3345/2892 4835/3337/2886 4836/8241/6179 -f 4851/3348/2895 4836/8241/6179 4837/8240/6178 -f 4852/3350/2897 4837/8240/6178 4838/8242/6180 -f 4853/8255/2899 4838/8244/6180 4824/8232/6176 -f 4840/8245/6181 4855/8268/2901 4854/8256/2903 -f 4840/8248/6181 4841/8247/6183 4856/8257/6187 -f 4843/3338/2887 4844/3339/2888 4858/3359/2906 -f 4844/3339/2888 4845/8251/2889 4859/3361/2908 -f 4845/3340/2889 4846/3343/2891 4860/8259/2910 -f 4846/3343/2891 4847/8252/6185 4861/8261/6188 -f 4847/8262/6185 4848/9437/6184 4862/8263/6189 -f 4862/8265/6189 4848/8249/6184 4849/8253/6186 -f 4868/3374/2900 4853/9438/2899 4839/8267/6182 -f 4855/8258/2901 4856/8257/6187 4871/3381/2924 -f 4856/8257/6187 4842/3347/2894 4872/3357/2904 -f 4860/3364/2910 4861/8264/6188 4876/3390/2930 -f 4861/8264/6188 4862/8263/6189 4877/3392/2932 -f 4877/8271/2932 4862/9439/6189 4863/3367/2913 -f 4885/3376/2921 4886/9440/2923 4901/8272/6190 -f 4886/3379/2923 4887/3382/2925 4902/8281/2945 -f 4887/8283/2925 4888/8276/2926 4903/3414/2946 -f 4888/8276/2926 4889/3384/2927 4904/3416/2948 -f 4889/3384/2927 4890/8277/2928 4905/3418/2950 -f 4890/3387/2928 4891/3389/2929 4906/3421/2952 -f 4907/8284/2939 4893/3394/2933 4894/8285/6192 -f 4908/8287/6193 4894/8278/6192 4895/3396/2934 -f 4914/8289/6195 4884/3375/2920 4900/8280/6191 -f 4901/8272/6190 4916/8309/6198 4915/8292/6196 -f 4901/8293/6190 4902/3413/2945 4917/8294/6197 -f 4906/3421/2952 4892/3391/2931 4922/8296/6199 -f 4907/3404/2939 4923/9441/6200 4922/8296/6199 -f 4923/8298/6200 4907/8284/2939 4908/8286/6193 -f 4924/8300/6201 4908/9442/6193 4909/8291/6194 -f 4925/8301/6202 4909/8291/6194 4910/3407/2940 -f 4926/8302/6203 4910/3407/2940 4911/3409/2941 -f 4927/8303/6204 4911/3409/2941 4912/3410/2942 -f 4928/8304/6205 4912/3410/2942 4913/3411/2943 -f 4929/8305/6206 4913/3411/2943 4884/3375/2920 -f 4930/8306/6207 4914/8289/6195 4915/8290/6196 -f 4931/8308/6208 4915/8292/6196 4916/8309/6198 -f 4916/8295/6198 4917/8294/6197 4933/3424/2955 -f 4917/8294/6197 4918/3412/2944 4934/3426/2957 -f 4918/3412/2944 4919/3415/2947 4935/3428/2959 -f 4919/3415/2947 4920/3417/2949 4936/3430/2961 -f 4920/8297/2949 4921/3419/2951 4937/3433/2963 -f 4921/3419/2951 4922/8296/6199 4938/3435/2965 -f 4922/8296/6199 4923/9441/6200 4939/3437/2967 -f 4924/8299/6201 4940/8319/6209 4939/8311/2967 -f 4940/8312/6209 4924/8300/6201 4925/8301/6202 -f 4941/8313/6210 4925/8301/6202 4926/8302/6203 -f 4942/8314/6211 4926/8302/6203 4927/8303/6204 -f 4943/8315/6212 4927/8303/6204 4928/8304/6205 -f 4944/8316/6213 4928/8304/6205 4929/8305/6206 -f 4945/8317/6214 4929/8305/6206 4914/8289/6195 -f 4946/3457/2970 4930/8306/6207 4931/8307/6208 -f 4947/3438/2968 4931/8308/6208 4932/8310/2954 -f 4940/8319/6209 4955/3445/2973 4954/3444/2966 -f 4955/3448/2973 4940/8312/6209 4941/8313/6210 -f 4956/3446/2974 4941/8313/6210 4942/8314/6211 -f 4957/3449/2976 4942/8314/6211 4943/8315/6212 -f 4958/3451/2978 4943/8315/6212 4944/8316/6213 -f 4959/3453/2980 4944/8316/6213 4945/8317/6214 -f 4960/3455/2982 4945/8317/6214 4930/8306/6207 -f 4948/3422/2953 4949/3425/2956 4964/8321/6215 -f 4949/3425/2956 4950/3427/2958 4965/8325/6217 -f 4950/3427/2958 4951/3429/2960 4966/8326/6218 -f 4951/8320/2960 4952/3431/2962 4967/8327/6219 -f 4952/3431/2962 4953/3434/2964 4968/8329/2987 -f 4953/3434/2964 4954/3436/2966 4969/8330/2988 -f 4977/3460/2985 4961/3459/2969 4962/8331/2971 -f 4963/8324/6216 4979/3465/2989 4978/3467/2991 -f 4963/8322/6216 4964/8321/6215 4980/3470/2993 -f 4964/8321/6215 4965/8325/6217 4981/3472/2995 -f 4965/8325/6217 4966/8326/6218 4982/8335/2997 -f 4966/8328/6218 4967/8327/6219 4983/8336/2999 -f 4967/8338/6219 4968/3463/2987 4984/3462/2986 -f 4969/3464/2988 4970/9443/2972 4985/8339/6220 -f 4985/8340/6220 4970/3443/2972 4971/8334/2975 -f 4986/3506/3021 4971/3447/2975 4972/3450/2977 -f 4987/3482/3003 4972/3450/2977 4973/3452/2979 -f 4988/3480/3001 4973/3452/2979 4974/3454/2981 -f 4989/8342/3004 4974/3454/2981 4975/3456/2983 -f 4990/3486/3006 4975/9444/2983 4976/3461/2984 -f 4991/3489/3009 4977/3460/2985 4978/8332/2991 -f 4984/3462/2986 4985/8339/6220 4999/3503/3019 -f 5012/3501/3017 5013/8347/3020 5026/8344/3043 -f 5020/8348/3029 5021/3517/3030 5035/3549/3052 -f 5021/3517/3030 5022/3519/3031 5036/3551/3054 -f 5022/3519/3031 5023/8349/3032 5037/3553/3056 -f 5023/3520/3032 5024/3522/3033 5038/3556/3058 -f 5024/3522/3033 5025/8345/3042 5039/8352/3041 -f 5059/8353/6221 5044/9445/3049 5032/3529/3038 -f 5040/3539/3045 5054/3562/3062 5053/3561/3060 -f 5054/8363/3062 5040/8355/3045 5041/3541/3046 -f 5056/8365/6225 5041/3541/3046 5042/3543/3047 -f 5057/8366/6226 5042/3543/3047 5043/3544/3048 -f 5058/8367/6227 5043/3544/3048 5044/3545/3049 -f 5075/8369/6228 5059/8353/6221 5045/3546/3050 -f 5061/3563/3063 5045/3546/3050 5033/3531/3039 -f 5062/3570/3070 5033/8358/3039 5046/8357/6222 -f 5046/8357/6222 5047/9446/3051 5064/8372/6230 -f 5047/3547/3051 5048/8360/6223 5065/8373/6231 -f 5048/8360/6223 5049/3550/3053 5066/8375/6232 -f 5049/8376/3053 5050/9447/3055 5067/8377/6233 -f 5050/8379/3055 5051/8362/3057 5068/8380/6234 -f 5051/8362/3057 5052/3557/3059 5069/8382/6235 -f 5070/3560/3061 5054/3562/3062 5055/8383/6224 -f 5071/8385/6236 5055/8364/6224 5056/8365/6225 -f 5072/8386/6237 5056/8365/6225 5057/8366/6226 -f 5073/8388/6238 5057/9448/6226 5058/8389/6227 -f 5074/8390/6239 5058/8389/6227 5059/8353/6221 -f 5091/3567/3067 5075/8369/6228 5060/3565/3065 -f 5063/8371/6229 5064/8372/6230 5078/3572/3072 -f 5064/8391/6230 5065/8394/6231 5079/8392/6240 -f 5065/8394/6231 5066/8378/6232 5080/8395/6241 -f 5066/8378/6232 5067/8377/6233 5081/8396/6242 -f 5067/8381/6233 5068/8380/6234 5082/8397/6243 -f 5068/8380/6234 5069/8382/6235 5083/8399/6244 -f 5069/8382/6235 5053/3559/3060 5100/8400/6245 -f 5053/3561/3060 5070/3560/3061 5085/3578/3077 -f 5070/3560/3061 5071/8384/6236 5087/8401/6246 -f 5087/8402/6246 5071/9449/6236 5072/8403/6237 -f 5088/8404/6247 5072/8403/6237 5073/8388/6238 -f 5089/8405/6248 5073/8388/6238 5074/8390/6239 -f 5090/8406/6249 5074/8390/6239 5075/8369/6228 -f 5078/8393/3072 5079/8392/6240 5095/8407/6250 -f 5079/8392/6240 5080/8395/6241 5096/8409/6251 -f 5080/8395/6241 5081/8396/6242 5097/8410/6252 -f 5081/8398/6242 5082/8397/6243 5098/8411/6253 -f 5082/8397/6243 5083/8399/6244 5099/8413/6254 -f 5101/8414/6255 5087/8402/6246 5088/8404/6247 -f 5102/8415/6256 5088/8404/6247 5089/8405/6248 -f 5103/8417/6257 5089/8405/6248 5090/8406/6249 -f 5104/8418/6258 5090/8406/6249 5091/3567/3067 -f 5121/3582/3081 5105/3566/3066 5076/3564/3064 -f 5106/3580/3079 5076/3564/3064 5092/3575/3074 -f 5093/3569/3069 5077/3568/3068 5094/3571/3071 -f 5094/8408/3071 5095/8407/6250 5111/3604/3101 -f 5095/8407/6250 5096/8409/6251 5112/3584/3083 -f 5097/8410/6252 5114/9450/6262 5113/3585/3084 -f 5097/8412/6252 5098/8411/6253 5115/8422/6261 -f 5098/8411/6253 5099/8413/6254 5116/3587/3086 -f 5099/8413/6254 5100/8400/6245 5117/3588/3087 -f 5118/3576/3075 5086/3579/3078 5101/8416/6255 -f 5119/8424/3091 5101/8414/6255 5102/8415/6256 -f 5102/8415/6256 5103/8417/6257 5124/3594/3092 -f 5125/3597/3094 5103/8417/6257 5104/8418/6258 -f 5120/8425/6263 5104/8418/6258 5105/3566/3066 -f 5126/8426/6264 5120/8425/6263 5121/3582/3081 -f 5122/3616/3099 5093/3569/3069 5109/8419/6259 -f 5109/8419/6259 5110/8421/6260 5129/8427/6265 -f 5113/3585/3084 5114/9450/6262 5132/3607/3104 -f 5114/8423/6262 5115/8422/6261 5133/3619/3114 -f 5115/8422/6261 5116/3587/3086 5135/3586/3085 -f 5118/3576/3075 5138/3605/3102 5137/3624/3088 -f 5127/3581/3080 5106/3580/3079 5142/3600/3097 -f 5141/3598/3095 5125/3597/3094 5126/8426/6264 -f 5145/3610/3106 5126/8426/6264 5127/3581/3080 -f 5167/8432/6268 5146/8431/6267 5142/3600/3097 -f 5128/3614/3110 5129/8427/6265 5154/8435/6269 -f 5129/8437/6265 5130/8429/6266 5155/8438/6270 -f 5144/3603/3100 5156/3617/3112 5155/8438/6270 -f 5131/3583/3082 5147/3606/3103 5157/8440/6271 -f 5133/3619/3114 5158/3618/3113 5157/8441/6271 -f 5134/3620/3115 5135/3586/3085 5159/8442/6272 -f 5162/3623/3118 5138/3605/3102 5148/8434/3105 -f 5148/3608/3105 5140/3595/3093 5164/8444/6274 -f 5164/8444/6274 5140/3595/3093 5149/3611/3107 -f 5165/8446/6275 5149/3611/3107 5145/3610/3106 -f 5166/8447/6276 5145/3610/3106 5146/8431/6267 -f 5181/8448/6277 5167/8432/6268 5150/3626/3120 -f 5153/3615/3111 5154/8435/6269 5170/8449/3123 -f 5154/8439/6269 5155/8438/6270 5171/8451/3125 -f 5155/8438/6270 5156/3617/3112 5172/3635/3127 -f 5156/3617/3112 5157/8440/6271 5173/3636/3129 -f 5157/8441/6271 5158/3618/3113 5174/3639/3131 -f 5158/3618/3113 5159/8442/6272 5175/3641/3133 -f 5162/3623/3118 5176/3646/3137 5161/3645/3135 -f 5176/3646/3137 5162/3623/3118 5163/8443/6273 -f 5177/8454/3140 5163/8445/6273 5164/8444/6274 -f 5178/8455/3138 5164/8444/6274 5165/8446/6275 -f 5179/8456/6278 5165/8446/6275 5166/8447/6276 -f 5180/8457/6279 5166/8447/6276 5167/8432/6268 -f 5197/3652/3143 5181/8448/6277 5168/3625/3119 -f 5182/3650/3141 5168/3625/3119 5151/3612/3108 -f 5183/3668/3154 5151/3612/3108 5152/3613/3109 -f 5184/8458/3157 5152/8436/3109 5169/8450/3122 -f 5193/3661/3136 5176/9451/3137 5177/3649/3140 -f 5194/3648/3139 5178/3647/3138 5179/8463/6278 -f 5195/8464/3149 5179/8456/6278 5180/8457/6279 -f 5196/8465/3151 5180/8457/6279 5181/8448/6277 -f 5186/3627/3121 5187/3630/3124 5199/3682/3165 -f 5187/3630/3124 5188/3632/3126 5200/3684/3167 -f 5189/8466/3128 5202/9452/3159 5201/3685/3168 -f 5191/3640/3132 5175/3641/3133 5204/8467/3170 -f 5211/3696/3177 5210/8472/3142 5182/8470/3141 -f 5218/3692/3174 5209/3667/3153 5210/8472/3142 -f 5199/3682/3165 5200/3684/3167 5221/3683/3166 -f 5201/3685/3168 5202/9452/3159 5222/3700/3181 -f 5204/8467/3170 5192/3642/3134 5224/3704/3184 -f 5205/3658/3147 5225/3689/3171 5224/3704/3184 -f 5224/3704/3184 5225/3689/3171 5236/8474/6280 -f 5236/8477/6280 5225/8475/3171 5226/3690/3172 -f 5237/3718/3194 5226/3690/3172 5227/3691/3173 -f 5245/3716/3191 5235/3702/3183 5236/8474/6280 -f 5246/3723/3199 5236/8477/6280 5237/3718/3194 -f 5218/3692/3174 5219/3707/3187 5250/3706/3186 -f 5256/3726/3202 5229/3695/3176 5239/8480/3188 -f 5234/3715/3192 5245/3714/3191 5254/8482/3211 -f 5254/3738/3211 5245/3716/3191 5246/8478/3199 -f 5251/3727/3203 5240/8479/6281 5261/8486/6283 -f 5240/8479/6281 5241/3710/3189 5263/8492/6288 -f 5273/8494/6290 5254/3738/3211 5255/8485/3198 -f 5321/3779/3252 5308/3781/3254 5283/3745/3218 -f 5309/3744/3217 5285/3746/3219 5286/3749/3222 -f 5310/3785/3221 5287/9453/3220 5288/8504/3225 -f 5313/3756/3229 5291/3755/3228 5292/8505/3232 -f 5314/3758/3231 5293/3757/3230 5294/3762/3235 -f 5297/3764/3237 5298/3766/3239 5316/3765/3238 -f 5300/8506/3241 5301/3770/3243 5317/3769/3242 -f 5304/3775/3248 5305/9454/3250 5320/3792/3249 -f 5306/3778/3251 5307/3780/3253 5321/3779/3252 -f 5335/3793/3262 5321/3779/3252 5309/3744/3217 -f 5315/3761/3234 5316/3765/3238 5330/3807/3273 -f 5316/3765/3238 5317/9455/3242 5331/3809/3275 -f 5317/3769/3242 5318/3772/3245 5332/3812/3277 -f 5318/3772/3245 5319/3774/3247 5333/3791/3261 -f 5346/3835/3280 5334/8508/3279 5335/3793/3262 -f 5373/8510/6297 5392/3882/3328 5391/3884/3330 -f 5373/8510/6297 5374/3852/3306 5393/8519/6299 -f 5374/8515/3306 5375/3854/3307 5394/8520/6300 -f 5394/8520/6300 5375/3854/3307 5376/3858/3309 -f 5384/3866/3315 5385/3868/3316 5400/3891/3336 -f 5385/8516/3316 5386/3869/3317 5401/3894/3338 -f 5401/3894/3338 5386/3869/3317 5387/8517/3318 -f 5402/3899/3339 5387/3870/3318 5388/8518/6298 -f 5403/3897/3341 5388/8518/6298 5389/3872/3319 -f 5393/8521/6299 5394/8520/6300 5407/3904/3347 -f 5395/8522/3323 5408/8525/3350 5407/3904/3347 -f 5395/3876/3323 5396/3878/3324 5409/3909/3351 -f 5396/3878/3324 5379/3861/3311 5397/3886/3332 -f 5405/3875/3322 5391/3884/3330 5421/3883/3329 -f 5408/8525/3350 5425/8527/6301 5424/3905/3348 -f 5414/8523/3335 5415/3892/3337 5430/3922/3362 -f 5430/3922/3362 5415/3892/3337 5416/3896/3340 -f 5431/3927/3363 5416/8524/3340 5417/3898/3342 -f 5425/8527/6301 5436/8531/3372 5435/8528/6302 -f 5426/3907/3349 5437/3934/3373 5436/3933/3372 -f 5426/3907/3349 5427/3910/3352 5438/3948/3384 -f 5413/3889/3334 5429/3911/3353 5442/3938/3361 -f 5434/8529/3356 5435/8528/6302 5449/3945/3382 -f 5436/8531/3372 5450/3946/3371 5449/3945/3382 -f 5438/3948/3384 5428/3936/3375 5451/3935/3374 -f 5444/3926/3366 5432/3925/3365 5455/3941/3378 -f 5455/3941/3378 5433/3913/3355 5445/3928/3367 -f 5450/3932/3371 5437/3934/3373 5460/3947/3383 -f 5460/3947/3383 5451/3935/3374 5461/8535/6304 -f 5451/3935/3374 5439/3916/3357 5462/3949/3385 -f 5440/8536/3358 5412/3951/3359 5463/3950/3386 -f 5453/3961/3395 5443/3924/3364 5454/3940/3377 -f 5466/8538/6305 5454/8532/3377 5455/3941/3378 -f 5467/8539/6306 5455/3941/3378 5456/3942/3379 -f 5468/8541/6307 5456/3942/3379 5446/3929/3368 -f 5459/8533/3393 5460/3947/3383 5474/3965/3398 -f 5474/3965/3398 5461/8535/6304 5475/8543/6308 -f 5461/8535/6304 5462/3949/3385 5476/8544/6309 -f 5462/8537/3385 5463/3950/3386 5477/8545/6310 -f 5463/3950/3386 5464/3952/3387 5478/8547/6311 -f 5464/3952/3387 5452/3937/3376 5479/8548/3394 -f 5465/8549/3396 5466/8538/6305 5467/8539/6306 -f 5480/8550/6312 5467/8539/6306 5468/8541/6307 -f 5481/8551/6313 5468/8541/6307 5469/3954/3389 -f 5482/8552/6314 5469/3954/3389 5470/3955/3390 -f 5483/8553/6315 5470/3955/3390 5471/3956/3391 -f 5484/8554/6316 5471/3956/3391 5472/8542/6303 -f 5472/8534/6303 5458/3944/3381 5486/8556/6318 -f 5487/3963/3397 5475/8543/6308 5488/8558/6319 -f 5475/8543/6308 5476/8544/6309 5489/8560/6321 -f 5476/8546/6309 5477/8545/6310 5490/8561/6322 -f 5477/8545/6310 5478/8547/6311 5491/8563/6323 -f 5478/8547/6311 5479/8548/3394 5492/8564/6324 -f 5494/8565/6325 5465/8549/3396 5480/8550/6312 -f 5495/8566/6326 5480/8550/6312 5481/8551/6313 -f 5496/8569/6327 5481/8551/6313 5482/8552/6314 -f 5497/8570/6328 5482/8552/6314 5483/8553/6315 -f 5498/8571/6329 5483/8553/6315 5484/8554/6316 -f 5499/8572/6330 5484/8554/6316 5485/8555/6317 -f 5500/8574/6331 5485/8557/6317 5486/8556/6318 -f 5486/8556/6318 5473/3957/3392 5502/3967/3400 -f 5473/3964/3392 5487/3963/3397 5503/8559/6320 -f 5518/3972/3405 5503/9456/6320 5488/8576/6319 -f 5504/3970/3403 5488/8576/6319 5489/8577/6321 -f 5505/3977/3406 5489/9457/6321 5490/8578/6322 -f 5506/3975/3408 5490/8578/6322 5491/8579/6323 -f 5507/3978/3410 5491/8579/6323 5492/8580/6324 -f 5508/3984/3412 5492/8567/6324 5493/3966/3399 -f 5493/3966/3399 5494/8568/6325 5510/3986/3417 -f 5494/8581/6325 5495/8584/6326 5511/8582/6332 -f 5495/8584/6326 5496/8586/6327 5512/8585/6333 -f 5496/8586/6327 5497/8588/6328 5513/8587/6334 -f 5497/8588/6328 5498/8590/6329 5514/8589/6335 -f 5498/8590/6329 5499/8592/6330 5515/8591/6336 -f 5499/8592/6330 5500/9458/6331 5516/8593/3419 -f 5500/8574/6331 5501/3969/3402 5517/3968/3401 -f 5510/8583/3417 5511/8582/6332 5526/3994/3423 -f 5511/8582/6332 5512/8585/6333 5527/3996/3425 -f 5512/8585/6333 5513/8587/6334 5528/3998/3427 -f 5513/8587/6334 5514/8589/6335 5529/4000/3429 -f 5514/8589/6335 5515/8591/6336 5530/4002/3431 -f 5515/8591/6336 5516/8593/3419 5531/4003/3418 -f 5534/8597/6337 5519/3971/3404 5520/3974/3407 -f 5535/8598/6338 5520/3974/3407 5521/8595/3409 -f 5536/8601/6339 5521/3976/3409 5522/3979/3411 -f 5537/4006/3434 5522/3979/3411 5523/3981/3413 -f 5523/3981/3413 5524/8596/3415 5539/8602/6340 -f 5525/3985/3416 5540/8615/6343 5539/8603/6340 -f 5546/8604/6341 5531/3987/3418 5532/3989/3420 -f 5547/8608/6342 5532/8599/3420 5533/3990/3421 -f 5548/8609/6344 5533/3990/3421 5519/3971/3404 -f 5549/8610/6345 5534/8597/6337 5535/8598/6338 -f 5550/8611/6346 5535/8598/6338 5536/8600/6339 -f 5551/8613/6347 5536/8601/6339 5537/4006/3434 -f 5553/8614/6348 5539/8603/6340 5540/8615/6343 -f 5540/8606/6343 5541/3992/3422 5555/8618/6350 -f 5541/3992/3422 5542/3995/3424 5556/8620/6351 -f 5542/3995/3424 5543/3997/3426 5557/8621/6352 -f 5543/3997/3426 5544/3999/3428 5558/8622/6353 -f 5544/3999/3428 5545/4001/3430 5559/8623/6354 -f 5545/4001/3430 5546/8607/6341 5560/8624/6355 -f 5547/8605/6342 5561/8630/6356 5560/8625/6355 -f 5561/8626/6356 5547/8608/6342 5548/8609/6344 -f 5562/8627/6357 5548/8609/6344 5534/8597/6337 -f 5563/4009/3437 5549/8610/6345 5550/8611/6346 -f 5564/4010/3438 5550/8611/6346 5551/8612/6347 -f 5565/4014/3440 5551/8613/6347 5552/4005/3433 -f 5566/4007/3435 5538/4004/3432 5553/8617/6348 -f 5567/4019/3443 5553/8614/6348 5554/8616/6349 -f 5554/8616/6349 5555/9459/6350 5569/8628/3446 -f 5555/8618/6350 5556/8620/6351 5570/4027/3448 -f 5556/8620/6351 5557/8621/6352 5571/4025/3450 -f 5557/8621/6352 5558/8622/6353 5572/4028/3452 -f 5558/8622/6353 5559/8623/6354 5573/4030/3454 -f 5559/8623/6354 5560/8624/6355 5574/4032/3456 -f 5561/8630/6356 5575/4036/3459 5574/4035/3456 -f 5575/4036/3459 5561/8630/6356 5562/8631/6357 -f 5576/8633/3461 5562/8627/6357 5549/8610/6345 -f 5592/4040/3462 5577/8642/3436 5593/4046/3467 -f 5577/8642/3436 5578/9460/3439 5594/4048/3469 -f 5578/8643/3439 5579/9461/3441 5595/4051/3471 -f 5579/4013/3441 5580/4043/3464 5596/4042/3463 -f 5581/4015/3442 5582/9462/3445 5597/4054/3473 -f 5597/4071/3473 5582/9463/3445 5583/4021/3447 -f 5598/8644/6359 5583/4021/3447 5584/4024/3449 -f 5599/4058/3477 5584/4024/3449 5585/8645/3451 -f 5600/4056/3475 5585/8645/3451 5586/8646/3453 -f 5601/4059/3478 5586/8646/3453 5587/8647/3455 -f 5602/4065/3480 5587/4031/3455 5588/8639/6358 -f 5603/4063/3482 5588/8639/6358 5589/4033/3457 -f 5604/8648/6360 5589/4033/3457 5590/8649/3458 -f 5590/8651/3458 5591/4037/3460 5606/4068/3486 -f 5591/4037/3460 5592/4040/3462 5607/4045/3466 -f 5613/4069/3487 5598/8644/6359 5599/4058/3477 -f 5618/4064/3483 5603/4063/3482 5604/8648/6360 -f 5608/4044/3465 5609/4047/3468 5622/4076/3493 -f 5609/4047/3468 5610/8652/3470 5623/4078/3495 -f 5610/4049/3470 5611/8653/3472 5624/4081/3497 -f 5611/4052/3472 5612/4055/3474 5625/4084/3499 -f 5612/4055/3474 5597/4054/3473 5626/4085/3488 -f 5627/4088/3502 5615/4057/3476 5616/4060/3479 -f 5628/4089/3503 5616/4060/3479 5617/4062/3481 -f 5629/4091/3505 5617/4062/3481 5618/8655/3483 -f 5630/4097/3507 5618/4064/3483 5619/8654/6361 -f 5619/8654/6361 5605/8650/3485 5632/4099/3490 -f 5635/4077/3494 5636/8657/3496 5649/8656/6362 -f 5636/4079/3496 5637/8658/6363 5650/8664/6365 -f 5637/8658/6363 5638/8659/3498 5651/8666/6366 -f 5639/8667/3500 5652/8681/3515 5651/8668/6366 -f 5656/8670/6367 5642/4092/3506 5643/4094/3508 -f 5657/8671/6368 5643/4094/3508 5644/8661/6364 -f 5658/8672/6369 5644/8661/6364 5645/8662/3510 -f 5659/8674/6370 5645/9464/3510 5646/8675/3511 -f 5646/8663/3511 5647/4100/3512 5661/4111/3522 -f 5647/4100/3512 5621/4074/3491 5633/4113/3524 -f 5633/4113/3524 5634/4075/3492 5663/4115/3526 -f 5634/4075/3492 5648/4101/3513 5664/4116/3527 -f 5648/4101/3513 5649/8656/6362 5665/8677/6371 -f 5649/8665/6362 5650/8664/6365 5666/8678/6372 -f 5650/8664/6365 5651/8666/6366 5667/8680/6373 -f 5652/8681/3515 5668/9465/6374 5667/8682/6373 -f 5668/8683/6374 5652/4103/3515 5653/4105/3516 -f 5684/4120/3531 5655/4090/3504 5656/8670/6367 -f 5671/4118/3529 5656/8670/6367 5657/8671/6368 -f 5672/4121/3532 5657/8671/6368 5658/8672/6369 -f 5673/4123/3534 5658/8672/6369 5659/8673/6370 -f 5674/8684/3536 5659/8674/6370 5660/8676/3521 -f 5664/4116/3527 5665/8677/6371 5679/4130/3541 -f 5665/8679/6371 5666/8678/6372 5680/4132/3543 -f 5666/8678/6372 5667/8680/6373 5681/4135/3545 -f 5667/8682/6373 5668/9465/6374 5682/8687/3548 -f 5682/4138/3548 5668/8683/6374 5669/4107/3518 -f 5674/8684/3536 5675/8685/6375 5689/8689/3556 -f 5675/8686/6375 5676/4109/3520 5690/4144/3554 -f 5676/4109/3520 5677/4112/3523 5691/4147/3557 -f 5681/8688/3545 5682/8687/3548 5697/8691/3560 -f 5688/8690/3537 5689/8689/3556 5705/8694/3555 -f 5707/4167/3572 5692/4127/3538 5693/4129/3540 -f 5708/4165/3570 5693/4129/3540 5694/4131/3542 -f 5709/4171/3573 5694/8693/3542 5695/4133/3544 -f 5719/4182/3585 5705/4145/3555 5706/4148/3558 -f 5720/4180/3583 5706/4148/3558 5692/4127/3538 -f 5711/4151/3561 5712/9466/3562 5725/4190/3577 -f 5730/4179/3582 5718/4163/3569 5719/8702/3585 -f 5739/4192/3592 5740/4193/3593 5754/4227/3618 -f 5745/8705/3598 5759/4234/3624 5758/4216/3609 -f 5773/4239/3627 5760/4218/3611 5747/4202/3599 -f 5764/4223/3614 5750/4207/3602 5751/4209/3604 -f 5765/8710/3631 5751/8707/3604 5752/4210/3605 -f 5766/4224/3615 5767/4226/3617 5781/4249/3634 -f 5773/4239/3627 5787/8718/6378 5786/8715/6377 -f 5788/4275/3656 5774/8734/3626 5775/4240/3628 -f 5789/4256/3640 5775/4240/3628 5776/4243/3629 -f 5790/4259/3641 5776/8713/3629 5777/4244/3630 -f 5791/4260/3643 5777/4244/3630 5778/4246/3632 -f 5792/4262/3645 5778/4246/3632 5779/4248/3633 -f 5793/8720/3647 5779/9467/3633 5780/8721/6376 -f 5780/8723/6376 5781/8716/3634 5795/8724/3650 -f 5781/8716/3634 5782/4250/3635 5796/8726/6379 -f 5782/4250/3635 5783/4252/3636 5797/8727/6380 -f 5783/4252/3636 5784/4253/3637 5798/8728/6381 -f 5784/4253/3637 5785/4254/3638 5799/8729/6382 -f 5785/4254/3638 5786/8717/6377 5800/8730/3654 -f 5786/8731/6377 5787/9468/6378 5801/8732/3652 -f 5801/4274/3652 5787/9469/6378 5774/8734/3626 -f 5788/4275/3656 5789/4256/3640 5802/4277/3658 -f 5809/4282/3663 5795/8724/3650 5796/8726/6379 -f 5810/4280/3661 5796/8726/6379 5797/8727/6380 -f 5811/4283/3664 5797/8727/6380 5798/8728/6381 -f 5812/8738/6383 5798/8728/6381 5799/8729/6382 -f 5813/8740/6384 5799/9470/6382 5800/4272/3654 -f 5802/4277/3658 5803/4255/3639 5817/8741/6385 -f 5803/4255/3639 5804/8735/3642 5818/4305/3668 -f 5804/4258/3642 5805/4261/3644 5819/4289/3669 -f 5805/4261/3644 5806/8736/3646 5820/8742/3659 -f 5821/8743/3660 5808/8737/3651 5809/4282/3663 -f 5811/4283/3664 5812/8738/6383 5824/4294/3673 -f 5824/4294/3673 5812/8738/6383 5813/8739/6384 -f 5825/8744/3676 5813/8740/6384 5814/4271/3653 -f 5816/4276/3657 5817/8741/6385 5829/4303/3682 -f 5819/8746/3669 5820/4278/3659 5831/4309/3686 -f 5837/8747/3677 5825/8744/3676 5826/4300/3679 -f 5827/4285/3666 5816/8745/3657 5828/4312/3681 -f 5831/4309/3686 5832/4290/3670 5848/8749/6386 -f 5851/8758/6392 5832/4290/3670 5833/4292/3671 -f 5853/8762/6396 5833/8748/3671 5834/4293/3672 -f 5834/4293/3672 5835/4296/3675 5855/4310/3687 -f 5856/8765/6398 5836/4295/3674 5837/4298/3677 -f 5859/8767/6400 5837/8747/3677 5838/4299/3678 -f 5896/8771/6403 5895/8797/6420 5866/8772/3700 -f 5897/8777/6407 5896/8771/6403 5868/8773/3702 -f 5898/4315/3691 5897/8813/6407 5870/8779/3713 -f 5900/4316/3692 5899/4314/3690 5873/4313/3689 -f 5877/8780/6408 5902/8812/6410 5901/4318/3694 -f 5879/8781/6409 5903/8785/6411 5902/8782/6410 -f 5881/8784/3709 5904/8787/6412 5903/8785/6411 -f 5883/8786/3711 5905/8789/6413 5904/8787/6412 -f 5885/8788/3705 5906/8807/6414 5905/8789/6413 -f 5887/4331/3707 5907/8791/6415 5906/8790/6414 -f 5889/4320/3696 5908/4319/3695 5907/8791/6415 -f 5911/8792/6416 5910/4323/3699 5890/4322/3698 -f 5912/8794/6418 5911/8802/6416 5892/8795/6417 -f 5895/8797/6420 5912/8794/6418 5894/8796/6419 -f 5878/9471/6431 5880/9472/6430 5867/9473/6433 -f 5880/9472/6430 5882/9474/6429 5865/9475/6423 -f 5882/9474/6429 5884/9476/6428 5893/9477/6424 -f 5884/9476/6428 5886/9478/6426 5888/9479/6427 -f 5891/9480/6425 5884/9476/6428 5888/9479/6427 -f 5891/9480/6425 5893/9477/6424 5884/9476/6428 -f 5893/9477/6424 5865/9475/6423 5882/9474/6429 -f 5865/9475/6423 5867/9473/6433 5880/9472/6430 -f 5867/9473/6433 5869/9481/6432 5878/9471/6431 -f 5878/9471/6431 5869/9481/6432 5876/8798/6421 -f 5869/9481/6432 5871/8799/6422 5876/8798/6421 -f 5871/8799/6422 5872/4327/3703 5874/4328/3704 -f 5865/8800/6423 5893/8801/6424 5912/8794/6418 -f 5893/8801/6424 5891/9482/6425 5911/8802/6416 -f 5891/8803/6425 5888/8805/6427 5910/4323/3699 -f 5909/4321/3697 5888/8805/6427 5886/8804/6426 -f 5907/8791/6415 5886/8804/6426 5884/8806/6428 -f 5906/8807/6414 5884/9483/6428 5882/8808/6429 -f 5905/8789/6413 5882/8808/6429 5880/8809/6430 -f 5904/8787/6412 5880/8809/6430 5878/8810/6431 -f 5903/8785/6411 5878/8810/6431 5876/8811/6421 -f 5902/8812/6410 5876/8798/6421 5874/4328/3704 -f 5872/4327/3703 5871/8799/6422 5898/4315/3691 -f 5871/8799/6422 5869/9481/6432 5897/8813/6407 -f 5869/8814/6432 5867/8815/6433 5896/8771/6403 -f 5867/8815/6433 5865/8800/6423 5895/8797/6420 -f 5919/4330/3706 5885/4329/3705 5883/4337/3711 -f 5917/8816/3719 5918/4336/3710 5881/4335/3709 -f 5916/4338/3712 5917/8816/3719 5877/8818/6408 -f 5914/4340/3714 5870/4339/3713 5868/4326/3702 -f 5921/8820/3724 5913/4325/3701 5866/4324/3700 -f 5920/4345/3717 5921/8820/3724 5892/8793/6417 -f 5920/4345/3717 5889/4320/3696 5887/4331/3707 -f 5924/9484/3715 5923/8823/3722 5925/8822/3718 -f 5923/8823/3722 5922/9485/3721 5926/8821/3723 -f 5922/9485/3721 5929/4349/3720 5927/4359/3725 -f 5929/4349/3720 5928/4344/3716 5927/4359/3725 -f 5927/4359/3725 5926/8821/3723 5922/9485/3721 -f 5961/8824/6403 5960/8849/6420 5931/8825/3700 -f 5962/8829/6407 5961/8824/6403 5933/8826/3702 -f 5963/4362/3691 5962/8865/6407 5935/8831/3713 -f 5965/4363/3692 5964/4361/3690 5938/4360/3689 -f 5942/8832/6434 5967/8864/6410 5966/4365/3726 -f 5944/8833/6409 5968/8837/6411 5967/8834/6410 -f 5946/8836/3709 5969/8839/6412 5968/8837/6411 -f 5948/8838/3711 5970/8841/6413 5969/8839/6412 -f 5950/8840/3705 5971/8859/6414 5970/8841/6413 -f 5952/4378/3707 5972/8843/6415 5971/8842/6414 -f 5954/4367/3696 5973/4366/3695 5972/8843/6415 -f 5976/8844/6416 5975/4370/3699 5955/4369/3698 -f 5977/8846/6418 5976/8854/6416 5957/8847/6417 -f 5960/8849/6420 5977/8846/6418 5959/8848/6419 -f 5943/9486/6431 5945/9487/6430 5932/9488/6433 -f 5945/9487/6430 5947/9489/6429 5930/9490/6423 -f 5947/9489/6429 5949/9491/6428 5958/9492/6424 -f 5949/9491/6428 5951/9493/6426 5953/9494/6427 -f 5956/9495/6425 5949/9491/6428 5953/9494/6427 -f 5956/9495/6425 5958/9492/6424 5949/9491/6428 -f 5958/9492/6424 5930/9490/6423 5947/9489/6429 -f 5930/9490/6423 5932/9488/6433 5945/9487/6430 -f 5932/9488/6433 5934/9496/6432 5943/9486/6431 -f 5943/9486/6431 5934/9496/6432 5941/8850/6421 -f 5934/9496/6432 5936/8851/6422 5941/8850/6421 -f 5936/8851/6422 5937/4374/3703 5939/4375/3704 -f 5930/8852/6423 5958/8853/6424 5977/8846/6418 -f 5958/8853/6424 5956/9497/6425 5976/8854/6416 -f 5956/8855/6425 5953/8857/6427 5975/4370/3699 -f 5974/4368/3697 5953/8857/6427 5951/8856/6426 -f 5972/8843/6415 5951/8856/6426 5949/8858/6428 -f 5971/8859/6414 5949/9498/6428 5947/8860/6429 -f 5970/8841/6413 5947/8860/6429 5945/8861/6430 -f 5969/8839/6412 5945/8861/6430 5943/8862/6431 -f 5968/8837/6411 5943/8862/6431 5941/8863/6421 -f 5967/8864/6410 5941/8850/6421 5939/4375/3704 -f 5937/4374/3703 5936/8851/6422 5963/4362/3691 -f 5936/8851/6422 5934/9496/6432 5962/8865/6407 -f 5934/8866/6432 5932/8867/6433 5961/8824/6403 -f 5932/8867/6433 5930/8852/6423 5960/8849/6420 -f 5984/4377/3706 5950/4376/3705 5948/4384/3711 -f 5982/8868/3719 5983/4383/3710 5946/4382/3709 -f 5981/4385/3712 5982/8868/3719 5942/8870/6434 -f 5979/4387/3714 5935/4386/3713 5933/4373/3702 -f 5986/8872/3724 5978/4372/3701 5931/4371/3700 -f 5985/4392/3717 5986/8872/3724 5957/8845/6417 -f 5985/4392/3717 5954/4367/3696 5952/4378/3707 -f 5989/9499/3715 5988/8875/3722 5990/8874/3718 -f 5988/8875/3722 5987/9500/3721 5991/8873/3723 -f 5987/9500/3721 5994/4396/3720 5992/4406/3725 -f 5994/4396/3720 5993/4391/3716 5992/4406/3725 -f 5992/4406/3725 5991/8873/3723 5987/9500/3721 -f 6026/8876/6403 6025/8901/6420 5996/8877/3700 -f 6027/8881/6407 6026/8876/6403 5998/8878/3702 -f 6028/4409/3691 6027/8917/6407 6000/8883/3713 -f 6030/4410/3692 6029/4408/3690 6003/4407/3689 -f 6007/8884/6434 6032/8916/6410 6031/4412/3726 -f 6009/8885/6409 6033/8889/6411 6032/8886/6410 -f 6011/8888/3709 6034/8891/6412 6033/8889/6411 -f 6013/8890/3711 6035/8893/6413 6034/8891/6412 -f 6015/8892/3705 6036/8911/6414 6035/8893/6413 -f 6017/4425/3707 6037/8895/6415 6036/8894/6414 -f 6019/4414/3696 6038/4413/3695 6037/8895/6415 -f 6041/8896/6416 6040/4417/3699 6020/4416/3698 -f 6042/8898/6418 6041/8906/6416 6022/8899/6417 -f 6025/8901/6420 6042/8898/6418 6024/8900/6419 -f 6008/9501/6431 6010/9502/6430 5997/9503/6433 -f 6010/9502/6430 6012/9504/6429 5995/9505/6423 -f 6012/9504/6429 6014/9506/6428 6023/9507/6424 -f 6014/9506/6428 6016/9508/6426 6018/9509/6427 -f 6021/9510/6425 6014/9506/6428 6018/9509/6427 -f 6021/9510/6425 6023/9507/6424 6014/9506/6428 -f 6023/9507/6424 5995/9505/6423 6012/9504/6429 -f 5995/9505/6423 5997/9503/6433 6010/9502/6430 -f 5997/9503/6433 5999/9511/6435 6008/9501/6431 -f 6008/9501/6431 5999/9511/6435 6006/8902/6421 -f 5999/9511/6435 6001/8903/6422 6006/8902/6421 -f 6001/8903/6422 6002/4421/3703 6004/4422/3704 -f 5995/8904/6423 6023/8905/6424 6042/8898/6418 -f 6023/8905/6424 6021/9512/6425 6041/8906/6416 -f 6021/8907/6425 6018/8909/6427 6040/4417/3699 -f 6039/4415/3697 6018/8909/6427 6016/8908/6426 -f 6037/8895/6415 6016/8908/6426 6014/8910/6428 -f 6036/8911/6414 6014/9513/6428 6012/8912/6429 -f 6035/8893/6413 6012/8912/6429 6010/8913/6430 -f 6034/8891/6412 6010/8913/6430 6008/8914/6431 -f 6033/8889/6411 6008/8914/6431 6006/8915/6421 -f 6032/8916/6410 6006/8902/6421 6004/4422/3704 -f 6002/4421/3703 6001/8903/6422 6028/4409/3691 -f 6001/8903/6422 5999/9511/6435 6027/8917/6407 -f 5999/8918/6435 5997/8919/6433 6026/8876/6403 -f 5997/8919/6433 5995/8904/6423 6025/8901/6420 -f 6049/4424/3706 6015/4423/3705 6013/4431/3711 -f 6047/8920/3719 6048/4430/3710 6011/4429/3709 -f 6046/4432/3712 6047/8920/3719 6007/8922/6434 -f 6044/4434/3714 6000/4433/3713 5998/4420/3702 -f 6051/8924/3724 6043/4419/3701 5996/4418/3700 -f 6050/4439/3717 6051/8924/3724 6022/8897/6417 -f 6050/4439/3717 6019/4414/3696 6017/4425/3707 -f 6054/9514/3715 6053/8927/3722 6055/8926/3718 -f 6053/8927/3722 6052/9515/3721 6056/8925/3723 -f 6052/9515/3721 6059/4443/3720 6057/4453/3725 -f 6059/4443/3720 6058/4438/3716 6057/4453/3725 -f 6057/4453/3725 6056/8925/3723 6052/9515/3721 -f 6091/8928/6403 6090/8953/6420 6061/8929/3700 -f 6092/8933/6436 6091/8928/6403 6063/8930/3702 -f 6093/4456/3691 6092/8969/6436 6065/8935/3713 -f 6095/4457/3692 6094/4455/3690 6068/4454/3689 -f 6072/8936/6434 6097/8968/6410 6096/4459/3726 -f 6074/8937/6409 6098/8941/6411 6097/8938/6410 -f 6076/8940/3709 6099/8943/6412 6098/8941/6411 -f 6078/8942/3711 6100/8945/6413 6099/8943/6412 -f 6080/8944/3705 6101/8963/6414 6100/8945/6413 -f 6082/4472/3707 6102/8947/6415 6101/8946/6414 -f 6084/4461/3696 6103/4460/3695 6102/8947/6415 -f 6106/8948/6416 6105/4464/3699 6085/4463/3698 -f 6107/8950/6418 6106/8958/6416 6087/8951/6417 -f 6090/8953/6420 6107/8950/6418 6089/8952/6419 -f 6073/9516/6431 6075/9517/6430 6062/9518/6433 -f 6075/9517/6430 6077/9519/6429 6060/9520/6423 -f 6077/9519/6429 6079/9521/6428 6088/9522/6424 -f 6079/9521/6428 6081/9523/6426 6083/9524/6427 -f 6086/9525/6425 6079/9521/6428 6083/9524/6427 -f 6086/9525/6425 6088/9522/6424 6079/9521/6428 -f 6088/9522/6424 6060/9520/6423 6077/9519/6429 -f 6060/9520/6423 6062/9518/6433 6075/9517/6430 -f 6062/9518/6433 6064/9526/6435 6073/9516/6431 -f 6073/9516/6431 6064/9526/6435 6071/8954/6421 -f 6064/9526/6435 6066/8955/6422 6071/8954/6421 -f 6066/8955/6422 6067/4468/3703 6069/4469/3704 -f 6060/8956/6423 6088/8957/6424 6107/8950/6418 -f 6088/8957/6424 6086/9527/6425 6106/8958/6416 -f 6086/8959/6425 6083/8961/6427 6105/4464/3699 -f 6104/4462/3697 6083/8961/6427 6081/8960/6426 -f 6102/8947/6415 6081/8960/6426 6079/8962/6428 -f 6101/8963/6414 6079/9528/6428 6077/8964/6429 -f 6100/8945/6413 6077/8964/6429 6075/8965/6430 -f 6099/8943/6412 6075/8965/6430 6073/8966/6431 -f 6098/8941/6411 6073/8966/6431 6071/8967/6421 -f 6097/8968/6410 6071/8954/6421 6069/4469/3704 -f 6067/4468/3703 6066/8955/6422 6093/4456/3691 -f 6066/8955/6422 6064/9526/6435 6092/8969/6436 -f 6064/8970/6435 6062/8971/6433 6091/8928/6403 -f 6062/8971/6433 6060/8956/6423 6090/8953/6420 -f 6114/4471/3706 6080/4470/3705 6078/4478/3711 -f 6112/8972/3719 6113/4477/3710 6076/4476/3709 -f 6111/4479/3712 6112/8972/3719 6072/8974/6434 -f 6109/4481/3714 6065/4480/3713 6063/4467/3702 -f 6116/8976/3724 6108/4466/3701 6061/4465/3700 -f 6115/4486/3717 6116/8976/3724 6087/8949/6417 -f 6115/4486/3717 6084/4461/3696 6082/4472/3707 -f 6119/9529/3715 6118/8979/3722 6120/8978/3718 -f 6118/8979/3722 6117/9530/3721 6121/8977/3723 -f 6117/9530/3721 6124/4490/3720 6122/4500/3725 -f 6124/4490/3720 6123/4485/3716 6122/4500/3725 -f 6122/4500/3725 6121/8977/3723 6117/9530/3721 -f 6128/8980/6437 6127/8986/6441 6125/8981/6438 -f 6130/8985/6440 6129/8988/6443 6127/8986/6441 -f 6132/8987/6442 6131/9531/6445 6129/8988/6443 -f 6134/8989/6444 6133/9532/6619 6131/8990/6445 -f 6135/8992/6446 6133/9533/6619 6134/8993/6444 -f 6137/8995/6448 6135/8992/6446 6136/8994/6447 -f 6139/8997/6450 6137/8995/6448 6138/8996/6449 -f 6125/8999/6438 6139/8997/6450 6140/8998/6451 -f 6126/9534/6452 6140/9535/6452 6128/9002/6452 -f 6140/9535/6452 6138/9003/6452 6128/9002/6452 -f 6138/9003/6452 6136/9536/6452 6130/9001/6452 -f 6136/9536/6452 6134/9537/6452 6132/9538/6452 -f 6130/9001/6452 6136/9536/6452 6132/9538/6452 -f 6139/9005/6453 6125/9539/6453 6127/9006/6453 -f 6127/9006/6453 6129/9540/6453 6137/9004/6453 -f 6129/9540/6453 6131/9541/6453 6135/9542/6453 -f 6131/9541/6453 6133/9543/6453 6135/9542/6453 -f 6135/9542/6453 6137/9004/6453 6129/9540/6453 -f 6141/9007/6454 6142/9010/6457 6166/9008/6455 -f 6142/9010/6457 6143/9254/3729 6167/9011/3738 -f 6172/4518/3742 6148/4510/3735 6149/5102/4268 -f 6173/9012/6458 6149/5102/4268 6150/5104/4270 -f 6174/9014/6459 6150/5104/4270 6151/5106/4272 -f 6175/9015/6460 6151/5106/4272 6152/5108/4274 -f 6176/9016/6461 6152/5108/4274 6153/5110/4276 -f 6177/9017/6462 6153/5110/4276 6154/5112/4278 -f 6178/9018/6463 6154/5112/4278 6155/5114/4280 -f 6179/9019/6464 6155/5114/4280 6156/5116/4282 -f 6180/9020/3746 6156/5116/4282 6157/5118/4284 -f 6181/9021/3744 6157/5118/4284 6158/5119/4285 -f 6182/9022/6465 6158/5119/4285 6159/5121/4287 -f 6183/4525/3749 6159/9544/4287 6160/9024/4289 -f 6184/4523/3747 6160/9024/4289 6161/9025/4290 -f 6185/4528/3752 6161/9025/4290 6162/9026/6466 -f 6186/4526/3750 6162/9026/6466 6163/9027/6467 -f 6187/4529/3753 6163/9027/6467 6164/9028/6468 -f 6165/9009/6456 6166/9008/6455 6190/9029/6469 -f 6166/9031/6455 6167/4513/3738 6191/4512/3737 -f 6195/4519/3743 6172/4518/3742 6173/9012/6458 -f 6196/9033/6471 6173/9012/6458 6174/9014/6459 -f 6197/9034/6472 6174/9014/6459 6175/9015/6460 -f 6198/9035/6473 6175/9015/6460 6176/9016/6461 -f 6199/9036/6474 6176/9016/6461 6177/9017/6462 -f 6200/9037/6475 6177/9017/6462 6178/9018/6463 -f 6201/9038/6476 6178/9018/6463 6179/9019/6464 -f 6202/9039/6477 6179/9019/6464 6180/9020/3746 -f 6203/4521/3745 6181/4520/3744 6182/9041/6465 -f 6204/9042/6478 6182/9041/6465 6183/4525/3749 -f 6205/4524/3748 6184/4523/3747 6185/4528/3752 -f 6189/9030/6470 6190/9029/6469 6208/9043/6479 -f 6190/9032/6469 6191/4512/3737 6209/9045/6481 -f 6209/9045/6481 6191/4512/3737 6192/4515/3739 -f 6214/9047/6482 6195/4519/3743 6196/9033/6471 -f 6215/9048/6483 6196/9033/6471 6197/9034/6472 -f 6216/9049/6484 6197/9034/6472 6198/9035/6473 -f 6217/9050/6485 6198/9035/6473 6199/9036/6474 -f 6218/9051/6486 6199/9036/6474 6200/9037/6475 -f 6219/9052/6487 6200/9037/6475 6201/9038/6476 -f 6220/9054/6488 6201/9545/6476 6202/9055/6477 -f 6221/9056/6489 6202/9055/6477 6203/4521/3745 -f 6222/9057/6490 6203/4521/3745 6204/9042/6478 -f 6223/9058/6491 6204/9042/6478 6205/4524/3748 -f 6224/9059/6492 6205/4524/3748 6206/4527/3751 -f 6225/4542/3764 6206/4527/3751 6187/4529/3753 -f 6207/9044/6480 6208/9043/6479 6228/9060/6493 -f 6208/9046/6479 6209/9045/6481 6229/9062/6495 -f 6229/9062/6495 6209/9045/6481 6210/4538/3760 -f 6232/4540/3762 6213/4533/3756 6214/9047/6482 -f 6233/9064/6496 6214/9047/6482 6215/9048/6483 -f 6234/9066/6497 6215/9048/6483 6216/9049/6484 -f 6235/9067/6498 6216/9049/6484 6217/9050/6485 -f 6236/9068/6499 6217/9050/6485 6218/9051/6486 -f 6237/9069/6500 6218/9051/6486 6219/9052/6487 -f 6238/9071/6501 6219/9546/6487 6220/9054/6488 -f 6239/9072/6502 6220/9054/6488 6221/9056/6489 -f 6240/9073/6503 6221/9056/6489 6222/9057/6490 -f 6241/9074/6504 6222/9057/6490 6223/9058/6491 -f 6242/9075/6505 6223/9058/6491 6224/9059/6492 -f 6243/9076/6506 6224/9059/6492 6225/4542/3764 -f 6227/9061/6494 6228/9060/6493 6247/4547/3769 -f 6228/9063/6493 6229/9062/6495 6248/4550/3771 -f 6248/4550/3771 6229/9062/6495 6230/4537/3759 -f 6249/4555/3772 6230/9065/3759 6231/4539/3761 -f 6250/4553/3774 6231/4539/3761 6232/4540/3762 -f 6251/4556/3776 6232/4540/3762 6233/9064/6496 -f 6252/4558/3778 6233/9064/6496 6234/9066/6497 -f 6253/4560/3780 6234/9066/6497 6235/9067/6498 -f 6254/4562/3782 6235/9067/6498 6236/9068/6499 -f 6255/4564/3784 6236/9068/6499 6237/9069/6500 -f 6256/4566/3786 6237/9069/6500 6238/9070/6501 -f 6257/4568/3788 6238/9071/6501 6239/9072/6502 -f 6258/4571/3790 6239/9072/6502 6240/9073/6503 -f 6259/4573/3792 6240/9073/6503 6241/9074/6504 -f 6260/4575/3794 6241/9074/6504 6242/9075/6505 -f 6261/4577/3796 6242/9075/6505 6243/9076/6506 -f 6262/4579/3798 6243/9076/6506 6244/4541/3763 -f 6263/4582/3801 6244/4541/3763 6226/4535/3758 -f 6281/4601/3817 6262/4579/3798 6263/4582/3801 -f 6300/9079/6507 6281/4601/3817 6282/4581/3800 -f 6301/9080/6508 6282/4581/3800 6264/4544/3766 -f 6303/4606/3822 6283/9082/6509 6284/9081/3802 -f 6304/4609/3820 6284/4583/3802 6285/4585/3803 -f 6305/4607/3823 6285/4585/3803 6286/4587/3804 -f 6306/4610/3825 6286/4587/3804 6287/9083/3805 -f 6307/4614/3827 6287/4588/3805 6288/4589/3806 -f 6288/4589/3806 6289/4590/3807 6309/4617/3831 -f 6290/4591/3808 6310/9086/6511 6309/4617/3831 -f 6290/4591/3808 6291/4592/3809 6311/9085/6510 -f 6291/4592/3809 6292/4593/3810 6312/9087/6512 -f 6292/4593/3810 6293/9084/3811 6313/9088/6513 -f 6293/4594/3811 6294/4596/3812 6314/9089/6514 -f 6294/4596/3812 6295/4597/3813 6315/9091/6515 -f 6295/4597/3813 6296/4598/3814 6316/9092/6516 -f 6296/4598/3814 6297/4599/3815 6317/9093/6517 -f 6297/4599/3815 6298/4600/3816 6318/9094/6518 -f 6298/4600/3816 6299/4602/3818 6319/9095/6519 -f 6299/4602/3818 6300/9079/6507 6320/4619/3833 -f 6300/9079/6507 6301/9080/6508 6321/4620/3834 -f 6309/4617/3831 6310/9086/6511 6328/9096/6520 -f 6310/9086/6511 6311/9085/6510 6329/9098/6521 -f 6311/9085/6510 6312/9087/6512 6330/9099/6522 -f 6312/9087/6512 6313/9088/6513 6331/9100/6523 -f 6313/9090/6513 6314/9089/6514 6332/9101/6524 -f 6314/9089/6514 6315/9091/6515 6333/9103/6525 -f 6315/9091/6515 6316/9092/6516 6334/9104/6526 -f 6316/9092/6516 6317/9093/6517 6335/9105/6527 -f 6317/9093/6517 6318/9094/6518 6336/9106/6528 -f 6318/9094/6518 6319/9095/6519 6337/9107/6529 -f 6319/9095/6519 6320/4619/3833 6338/4618/3832 -f 6340/9108/6530 6322/4605/3821 6323/9109/3824 -f 6341/9111/6531 6323/4608/3824 6324/4611/3826 -f 6327/4626/3839 6328/9096/6520 6346/4629/3842 -f 6328/9096/6520 6329/9098/6521 6347/4631/3844 -f 6329/9098/6521 6330/9099/6522 6348/4633/3846 -f 6330/9099/6522 6331/9100/6523 6349/4635/3848 -f 6331/9100/6523 6332/9547/6524 6350/4637/3850 -f 6332/9101/6524 6333/9103/6525 6351/4640/3852 -f 6333/9103/6525 6334/9104/6526 6352/4642/3854 -f 6334/9104/6526 6335/9105/6527 6353/4644/3856 -f 6335/9105/6527 6336/9106/6528 6354/4646/3858 -f 6336/9106/6528 6337/9107/6529 6355/4648/3860 -f 6337/9107/6529 6338/4618/3832 6356/4650/3862 -f 6245/4543/3765 6359/9548/6620 6358/9114/6533 -f 6360/4653/3865 6340/9108/6530 6341/9110/6531 -f 6361/4656/3863 6341/9111/6531 6342/9112/6532 -f 6342/9112/6532 6343/4622/3836 6363/4658/3869 -f 6343/9113/3836 6344/4624/3837 6364/4661/3871 -f 6344/4624/3837 6345/4625/3838 6365/4663/3873 -f 6356/4650/3862 6357/4627/3840 6377/9115/6534 -f 6366/4628/3841 6367/4630/3843 6385/4671/3880 -f 6367/4630/3843 6368/4632/3845 6386/4673/3882 -f 6368/4632/3845 6369/4634/3847 6387/4675/3884 -f 6369/4634/3847 6370/4636/3849 6388/4677/3886 -f 6370/4636/3849 6371/9116/3851 6389/4679/3888 -f 6371/9116/3851 6372/9549/3853 6390/4681/3890 -f 6372/4641/3853 6373/4643/3855 6391/4685/3892 -f 6373/4643/3855 6374/4645/3857 6392/4686/3894 -f 6374/4645/3857 6375/4647/3859 6393/4688/3896 -f 6375/4647/3859 6376/4649/3861 6394/4690/3898 -f 6376/4649/3861 6377/9115/6534 6395/9120/6536 -f 6377/9115/6534 6358/9114/6533 6396/9121/6537 -f 6358/9114/6533 6359/9548/6620 6397/4725/3930 -f 6398/9122/6538 6378/9117/6535 6379/4652/3864 -f 6399/9124/6539 6379/9118/3864 6380/4655/3867 -f 6394/4690/3898 6395/9120/6536 6416/9125/6540 -f 6417/9127/6541 6398/9122/6538 6399/9123/6539 -f 6418/9128/6542 6399/9123/6539 6400/9129/3875 -f 6400/4665/3875 6401/9126/3900 6421/4713/3899 -f 6415/4708/3914 6416/9125/6540 6437/4719/3924 -f 6416/9125/6540 6396/9121/6537 6439/4723/3928 -f 6440/9133/6544 6417/9127/6541 6418/9128/6542 -f 6441/9134/6545 6418/9128/6542 6419/9130/3917 -f 6424/4695/3903 6425/4696/3904 6448/9136/6546 -f 6425/4696/3904 6426/4697/3905 6449/9137/6547 -f 6426/4697/3905 6427/4698/3906 6450/9138/6548 -f 6427/4698/3906 6428/4699/3907 6451/9139/6549 -f 6428/4699/3907 6429/4700/3908 6452/9140/6550 -f 6429/4700/3908 6430/4701/3909 6453/9141/6551 -f 6430/4701/3909 6431/4702/3910 6454/9142/6552 -f 6431/4702/3910 6432/9131/6543 6455/9143/6553 -f 6432/9131/6543 6433/4704/3911 6456/9144/6554 -f 6433/4704/3911 6434/9132/3912 6457/9145/6555 -f 6434/9132/3912 6435/9147/3913 6458/9146/6556 -f 6435/9147/3913 6436/9550/3923 6459/9148/3922 -f 6463/9149/6557 6440/9133/6544 6441/9134/6545 -f 6464/9150/6558 6441/9134/6545 6442/9135/3916 -f 6447/4716/3921 6448/9136/6546 6472/9152/6559 -f 6448/9136/6546 6449/9137/6547 6473/9153/6560 -f 6449/9137/6547 6450/9138/6548 6474/9154/6561 -f 6450/9138/6548 6451/9139/6549 6475/9155/6562 -f 6451/9139/6549 6452/9140/6550 6476/9156/6563 -f 6452/9140/6550 6453/9141/6551 6477/9157/6564 -f 6453/9141/6551 6454/9142/6552 6478/9158/6565 -f 6454/9142/6552 6455/9143/6553 6479/9159/6566 -f 6455/9143/6553 6456/9144/6554 6480/9160/6567 -f 6456/9144/6554 6457/9145/6555 6481/9161/6568 -f 6457/9145/6555 6458/9146/6556 6482/9162/6569 -f 6458/9146/6556 6459/9148/3922 6483/4744/3945 -f 6459/9148/3922 6460/9163/3925 6484/4745/3946 -f 6460/9163/3925 6461/9166/3927 6485/4747/3948 -f 6486/9164/6570 6463/9149/6557 6464/9150/6558 -f 6487/9165/6571 6464/9150/6558 6465/9151/3938 -f 6471/4742/3943 6472/9152/6559 6493/4755/3954 -f 6472/9152/6559 6473/9153/6560 6494/4757/3956 -f 6473/9153/6560 6474/9154/6561 6495/4759/3958 -f 6474/9154/6561 6475/9155/6562 6496/4761/3960 -f 6475/9155/6562 6476/9156/6563 6497/4763/3962 -f 6476/9156/6563 6477/9157/6564 6498/4765/3964 -f 6477/9157/6564 6478/9158/6565 6499/4767/3966 -f 6478/9158/6565 6479/9159/6566 6500/4769/3968 -f 6479/9159/6566 6480/9160/6567 6501/4771/3970 -f 6480/9160/6567 6481/9161/6568 6502/4772/3971 -f 6481/9161/6568 6482/9162/6569 6503/4774/3973 -f 6482/9162/6569 6483/4744/3945 6504/4776/3975 -f 6486/9164/6570 6487/9165/6571 6509/9168/6572 -f 6487/9165/6571 6488/9167/3937 6510/9170/6574 -f 6527/4779/3978 6506/4746/3947 6507/4748/3949 -f 6528/9171/6575 6507/4748/3949 6462/4749/3929 -f 6508/9169/6573 6509/9168/6572 6531/9175/6577 -f 6509/9168/6572 6510/9170/6574 6532/9177/3982 -f 6510/9178/6574 6511/9551/3950 6533/4784/3983 -f 6533/4784/3983 6511/9551/3950 6512/9179/3951 -f 6534/4789/3984 6512/4752/3951 6513/4753/3952 -f 6535/4787/3986 6513/4753/3952 6514/4754/3953 -f 6536/4790/3988 6514/4754/3953 6515/4756/3955 -f 6537/4792/3990 6515/4756/3955 6516/4758/3957 -f 6538/4794/3992 6516/4758/3957 6517/4760/3959 -f 6539/4796/3994 6517/4760/3959 6518/4762/3961 -f 6540/4798/3996 6518/4762/3961 6519/4764/3963 -f 6541/4800/3998 6519/4764/3963 6520/4766/3965 -f 6542/4802/4000 6520/4766/3965 6521/4768/3967 -f 6543/4804/4002 6521/4768/3967 6522/4770/3969 -f 6544/4806/4004 6522/4770/3969 6523/4773/3972 -f 6545/4808/4006 6523/4773/3972 6524/4775/3974 -f 6546/4810/4008 6524/4775/3974 6525/4777/3976 -f 6549/4815/4011 6527/9181/3978 6528/9180/6575 -f 6550/4820/4015 6528/9180/6575 6529/9182/6576 -f 6530/9176/6578 6531/9175/6577 6553/9183/6579 -f 6531/9175/6577 6532/9177/3982 6554/9185/3981 -f 6568/4813/4010 6547/4812/3979 6548/4817/3980 -f 6569/4816/4012 6549/4815/4011 6550/4820/4015 -f 6552/9184/6580 6553/9183/6579 6572/4824/4019 -f 6553/9188/6579 6554/4782/3981 6573/4827/4021 -f 6573/4827/4021 6554/4782/3981 6555/4786/3985 -f 6574/4832/4022 6555/9186/3985 6556/4788/3987 -f 6575/4830/4024 6556/4788/3987 6557/4791/3989 -f 6576/4833/4026 6557/4791/3989 6558/4793/3991 -f 6577/4835/4028 6558/4793/3991 6559/4795/3993 -f 6578/4837/4030 6559/4795/3993 6560/4797/3995 -f 6579/4839/4032 6560/4797/3995 6561/4799/3997 -f 6580/4841/4034 6561/4799/3997 6562/4801/3999 -f 6581/4843/4036 6562/4801/3999 6563/4803/4001 -f 6582/4845/4038 6563/4803/4001 6564/4805/4003 -f 6583/4847/4040 6564/4805/4003 6565/4807/4005 -f 6584/9189/4042 6565/4807/4005 6566/4809/4007 -f 6585/4852/4044 6566/9552/4007 6567/9187/4009 -f 6586/4854/4046 6567/9187/4009 6568/4813/4010 -f 6587/4856/4048 6568/4813/4010 6569/4816/4012 -f 6605/9191/6581 6587/4856/4048 6588/4821/4016 -f 6606/4864/4055 6588/4821/4016 6570/4819/4014 -f 6607/4862/4053 6570/4819/4014 6551/4818/4013 -f 6589/4822/4017 6590/9192/4020 6610/9194/6582 -f 6590/4825/4020 6591/4829/4023 6611/4859/4050 -f 6614/4868/4058 6594/4836/4029 6595/4838/4031 -f 6615/4870/4060 6595/4838/4031 6596/4840/4033 -f 6616/4872/4062 6596/4840/4033 6597/4842/4035 -f 6617/4874/4064 6597/4842/4035 6598/4844/4037 -f 6618/4876/4066 6598/4844/4037 6599/4846/4039 -f 6619/4878/4068 6599/4846/4039 6600/4848/4041 -f 6620/4880/4070 6600/9553/4041 6601/4850/4043 -f 6621/4883/4072 6601/4850/4043 6602/4853/4045 -f 6622/4885/4074 6602/4853/4045 6603/4855/4047 -f 6623/4887/4076 6603/4855/4047 6604/4857/4049 -f 6624/4889/4078 6604/4857/4049 6605/9191/6581 -f 6625/4891/4080 6605/9191/6581 6606/4864/4055 -f 6609/9195/6583 6610/9194/6582 6628/9198/6584 -f 6610/9196/6582 6611/4859/4050 6629/4896/4085 -f 6643/4892/4081 6625/4891/4080 6626/4863/4054 -f 6644/4913/4100 6626/4863/4054 6607/4862/4053 -f 6627/9199/6585 6628/9198/6584 6647/4916/4103 -f 6628/9200/6584 6629/4896/4085 6648/4895/4084 -f 6662/4934/4118 6643/4892/4081 6644/4913/4100 -f 6681/4936/4120 6700/4979/4157 6699/4978/4156 -f 6681/4936/4120 6608/4893/4082 6701/9202/6586 -f 6699/4978/4156 6719/4977/4155 6718/9203/6587 -f 6721/9207/6588 6702/4957/4138 6703/9204/4140 -f 6722/9210/6589 6703/4959/4140 6704/4961/4141 -f 6704/4961/4141 6705/9205/4142 6724/9212/4161 -f 6705/4962/4142 6706/4964/4143 6725/4980/4158 -f 6707/4965/4144 6708/4966/4145 6727/4988/4166 -f 6708/4966/4145 6709/4967/4146 6728/4990/4168 -f 6709/4967/4146 6710/4968/4147 6729/4992/4170 -f 6710/4968/4147 6711/4969/4148 6730/4994/4172 -f 6711/4969/4148 6712/4970/4149 6731/4996/4174 -f 6712/4970/4149 6713/9554/4150 6732/4998/4176 -f 6713/4972/4150 6714/4973/4151 6733/5001/4178 -f 6714/4973/4151 6715/4974/4152 6734/5003/4180 -f 6715/4974/4152 6716/4975/4153 6735/5005/4182 -f 6716/4975/4153 6717/4976/4154 6736/5007/4184 -f 6717/4976/4154 6718/9203/6587 6737/5009/4186 -f 6718/9203/6587 6719/4977/4155 6738/9213/6591 -f 6739/5012/4189 6721/9207/6588 6722/9208/6589 -f 6740/5015/4187 6722/9210/6589 6723/9211/4160 -f 6737/5009/4186 6738/9213/6591 6756/9214/6592 -f 6738/9213/6591 6720/9209/6590 6757/9216/6593 -f 6720/9209/6590 6701/9202/6586 6758/5054/4225 -f 6755/5008/4185 6756/9214/6592 6776/9217/6594 -f 6777/9221/6596 6759/9218/6595 6760/5011/4188 -f 6778/9222/6597 6760/5011/4188 6761/9223/4191 -f 6765/5020/4195 6766/5021/4196 6785/9225/6598 -f 6766/5021/4196 6767/5022/4197 6786/9226/6599 -f 6767/5022/4197 6768/5023/4198 6787/9227/6600 -f 6768/5023/4198 6769/5024/4199 6788/9228/6601 -f 6769/5024/4199 6770/5025/4200 6789/9229/6602 -f 6770/5025/4200 6771/5026/4201 6790/9230/6603 -f 6771/5026/4201 6772/5028/4202 6791/9231/6604 -f 6772/5028/4202 6773/9233/4203 6792/9232/6605 -f 6773/9233/4203 6774/9555/4204 6794/9234/6606 -f 6774/5031/4204 6775/5032/4205 6795/9236/6608 -f 6775/5032/4205 6776/9217/6594 6797/5048/4219 -f 6776/9217/6594 6757/9216/6593 6799/5052/4223 -f 6800/9238/6609 6777/9221/6596 6778/9222/6597 -f 6801/9239/6610 6778/9222/6597 6779/9224/4211 -f 6784/5045/4216 6785/9225/6598 6808/5057/4228 -f 6785/9225/6598 6786/9226/6599 6809/5059/4230 -f 6786/9226/6599 6787/9227/6600 6810/5061/4232 -f 6787/9227/6600 6788/9228/6601 6811/5063/4234 -f 6788/9228/6601 6789/9229/6602 6812/5065/4236 -f 6789/9229/6602 6790/9230/6603 6813/5067/4238 -f 6790/9230/6603 6791/9231/6604 6814/5069/4240 -f 6791/9231/6604 6792/9232/6605 6815/5071/4242 -f 6792/9232/6605 6793/9235/6607 6816/5073/4244 -f 6793/9235/6607 6794/9234/6606 6817/5075/4246 -f 6794/9234/6606 6795/9243/6608 6818/5077/4248 -f 6795/9243/6608 6796/9556/4218 6819/5079/4217 -f 6823/9244/6612 6800/9238/6609 6801/9239/6610 -f 6824/9245/6613 6801/9239/6610 6802/9240/4210 -f 6802/9247/4210 6803/9557/4212 6826/5082/4252 -f 6803/9242/4212 6804/9241/6611 6827/5085/4254 -f 6804/9241/6611 6805/5042/4213 6828/5086/4255 -f 6805/5042/4213 6806/5043/4214 6829/5055/4226 -f 6820/9248/4220 6845/5094/4263 6844/9249/6614 -f 6820/9248/4220 6821/9252/4222 6846/5095/4264 -f 6847/9250/6615 6823/9244/6612 6824/9245/6613 -f 6848/9251/6616 6824/9245/6613 6825/9246/4251 -f 6831/5056/4227 6832/5058/4229 6855/5101/4267 -f 6832/5058/4229 6833/5060/4231 6856/5103/4269 -f 6833/5060/4231 6834/5062/4233 6857/5105/4271 -f 6834/5062/4233 6835/5064/4235 6858/5107/4273 -f 6835/5064/4235 6836/5066/4237 6859/5109/4275 -f 6836/5066/4237 6837/5068/4239 6860/5111/4277 -f 6837/5068/4239 6838/5070/4241 6861/5113/4279 -f 6838/5070/4241 6839/5072/4243 6862/5115/4281 -f 6839/5072/4243 6840/5074/4245 6863/5117/4283 -f 6841/5092/4261 6864/5091/4260 6863/5117/4283 -f 6842/5076/4247 6843/5078/4249 6865/5120/4286 -f 6843/5078/4249 6844/9249/6614 6866/5122/4288 -f 6845/5094/4263 6867/5093/4262 6866/5122/4288 -f 6847/9250/6615 6848/9251/6616 6142/9010/6457 -f 6848/9251/6616 6849/9253/4266 6143/9254/3729 -f 6162/9255/6466 6867/5093/4262 6868/5096/4265 -f 6163/9256/6467 6868/5096/4265 6822/5097/4224 diff --git a/Base/home/anon/Documents/Example Presentation.presenter b/Base/home/anon/Documents/Example Presentation.presenter deleted file mode 100644 index 7fb54cf8c14..00000000000 --- a/Base/home/anon/Documents/Example Presentation.presenter +++ /dev/null @@ -1,72 +0,0 @@ -{ - "version": 1, - "metadata": { - "author": "CatDog", - "title": "Example presentation", - "last-modified": "2022-10-21T14:50:00", - "width": 320, - "aspect": "16:9" - }, - "templates": {}, - "slides": [ - { - "title": "Introduction", - "frame_count": 2, - "objects": [ - { - "frame": 0, - "type": "text", - - "text": "Welcome to Presenter!", - - "rect": [20, 20, 280, 30], - "color": "#000000", - "font": "Liberation Serif", - "font-size": 18, - "font-weight": "Bold", - "text-alignment": "Center" - }, - { - "frame": 1, - "type": "text", - - "text": "This program is very cool. It supports:\n - Scaling properly to the window\n - Text\n - Switching between slides\n - that's all for now lol", - - "rect": [20, 60, 280, 140], - "color": "#2f0000", - "font": "Liberation Serif", - "font-size": 8, - "font-weight": "Regular", - "text-alignment": "TopLeft" - } - ] - }, - { - "title": "The Second Slide", - "frame_count": 2, - "objects": [ - { - "frame": 0, - "type": "text", - "text": "CatDog likes this program!", - - "rect": [20, 10, 280, 40], - "color": "#2f0000", - "font": "Liberation Serif", - "font-size": 7, - "font-weight": "Regular", - "text-alignment": "Center" - }, - { - "frame": 1, - "type": "image", - - "rect": [50, 50, 200, 100], - "path": "/res/graphics/catdog/alert.png", - "scaling": "fit-smallest", - "scaling-mode": "nearest-neighbor" - } - ] - } - ] -} diff --git a/Base/home/anon/Documents/sql/insert_values.sql b/Base/home/anon/Documents/sql/insert_values.sql deleted file mode 100644 index a9125c1cfb6..00000000000 --- a/Base/home/anon/Documents/sql/insert_values.sql +++ /dev/null @@ -1,14 +0,0 @@ -CREATE SCHEMA TestSchema; -CREATE TABLE TestSchema.TestTable -( - TextColumn text, - IntColumn integer -); -INSERT INTO TestSchema.TestTable (TextColumn, IntColumn) -VALUES ('Test_1', 42), - ('Test_3', 44), - ('Test_2', 43), - ('Test_4', 45), - ('Test_5', 46); -SELECT * -FROM TestSchema.TestTable; diff --git a/Base/home/anon/Documents/sql/select_cross_join.sql b/Base/home/anon/Documents/sql/select_cross_join.sql deleted file mode 100644 index a45090a3851..00000000000 --- a/Base/home/anon/Documents/sql/select_cross_join.sql +++ /dev/null @@ -1,26 +0,0 @@ -CREATE SCHEMA TestSchema; -CREATE TABLE TestSchema.TestTable1 -( - TextColumn1 text, - IntColumn integer -); -CREATE TABLE TestSchema.TestTable2 -( - TextColumn2 text, - IntColumn integer -); -INSERT INTO TestSchema.TestTable1 (TextColumn1, IntColumn) -VALUES ('Test_1', 42), - ('Test_3', 44), - ('Test_2', 43), - ('Test_4', 45), - ('Test_5', 46); -INSERT INTO TestSchema.TestTable2 (TextColumn2, IntColumn) -VALUES ('Test_10', 40), - ('Test_11', 41), - ('Test_12', 42), - ('Test_13', 47), - ('Test_14', 48); -SELECT * -FROM TestSchema.TestTable1, - TestSchema.TestTable2; diff --git a/Base/home/anon/Documents/sql/select_from_table.sql b/Base/home/anon/Documents/sql/select_from_table.sql deleted file mode 100644 index a9125c1cfb6..00000000000 --- a/Base/home/anon/Documents/sql/select_from_table.sql +++ /dev/null @@ -1,14 +0,0 @@ -CREATE SCHEMA TestSchema; -CREATE TABLE TestSchema.TestTable -( - TextColumn text, - IntColumn integer -); -INSERT INTO TestSchema.TestTable (TextColumn, IntColumn) -VALUES ('Test_1', 42), - ('Test_3', 44), - ('Test_2', 43), - ('Test_4', 45), - ('Test_5', 46); -SELECT * -FROM TestSchema.TestTable; diff --git a/Base/home/anon/Documents/sql/select_inner_join.sql b/Base/home/anon/Documents/sql/select_inner_join.sql deleted file mode 100644 index 0c5a5d14048..00000000000 --- a/Base/home/anon/Documents/sql/select_inner_join.sql +++ /dev/null @@ -1,27 +0,0 @@ -CREATE SCHEMA TestSchema; -CREATE TABLE TestSchema.TestTable1 -( - TextColumn1 text, - IntColumn integer -); -CREATE TABLE TestSchema.TestTable2 -( - TextColumn2 text, - IntColumn integer -); -INSERT INTO TestSchema.TestTable1 (TextColumn1, IntColumn) -VALUES ('Test_1', 42), - ('Test_3', 44), - ('Test_2', 43), - ('Test_4', 45), - ('Test_5', 46); -INSERT INTO TestSchema.TestTable2 (TextColumn2, IntColumn) -VALUES ('Test_10', 40), - ('Test_11', 41), - ('Test_12', 42), - ('Test_13', 47), - ('Test_14', 48); -SELECT TestTable1.IntColumn, TextColumn1, TextColumn2 -FROM TestSchema.TestTable1, - TestSchema.TestTable2 -WHERE TestTable1.IntColumn = TestTable2.IntColumn; diff --git a/Base/home/anon/Documents/sql/select_with_column_names.sql b/Base/home/anon/Documents/sql/select_with_column_names.sql deleted file mode 100644 index 891fc8e611e..00000000000 --- a/Base/home/anon/Documents/sql/select_with_column_names.sql +++ /dev/null @@ -1,14 +0,0 @@ -CREATE SCHEMA TestSchema; -CREATE TABLE TestSchema.TestTable -( - TextColumn text, - IntColumn integer -); -INSERT INTO TestSchema.TestTable (TextColumn, IntColumn) -VALUES ('Test_1', 42), - ('Test_3', 44), - ('Test_2', 43), - ('Test_4', 45), - ('Test_5', 46); -SELECT TextColumn -FROM TestSchema.TestTable; diff --git a/Base/home/anon/Documents/sql/select_with_where.sql b/Base/home/anon/Documents/sql/select_with_where.sql deleted file mode 100644 index 0dfec5be3b4..00000000000 --- a/Base/home/anon/Documents/sql/select_with_where.sql +++ /dev/null @@ -1,15 +0,0 @@ -CREATE SCHEMA TestSchema; -CREATE TABLE TestSchema.TestTable -( - TextColumn text, - IntColumn integer -); -INSERT INTO TestSchema.TestTable (TextColumn, IntColumn) -VALUES ('Test_1', 42), - ('Test_3', 44), - ('Test_2', 43), - ('Test_4', 45), - ('Test_5', 46); -SELECT TextColumn, IntColumn -FROM TestSchema.TestTable -WHERE IntColumn > 44; diff --git a/Base/home/anon/Source/js/array.js b/Base/home/anon/Source/js/array.js deleted file mode 100644 index b62f262396b..00000000000 --- a/Base/home/anon/Source/js/array.js +++ /dev/null @@ -1,11 +0,0 @@ -var a = [1, 2, 3]; - -a[1] = 5; - -var push_result = a.push(7); - -for (var i = 0; i < a.length; ++i) { - console.log(a[i]); -} - -console.log("push result: " + push_result); diff --git a/Base/home/anon/Source/js/charAt.js b/Base/home/anon/Source/js/charAt.js deleted file mode 100644 index 9a452fb4f5f..00000000000 --- a/Base/home/anon/Source/js/charAt.js +++ /dev/null @@ -1,2 +0,0 @@ -var foo = "foobar"; -console.log(foo.charAt(3)); diff --git a/Base/home/anon/Source/js/console.js b/Base/home/anon/Source/js/console.js deleted file mode 100644 index 1ebe4441edc..00000000000 --- a/Base/home/anon/Source/js/console.js +++ /dev/null @@ -1,5 +0,0 @@ -console.log("I am a generic log message"); -console.debug("I am a debug log message"); -console.info("I am an info log message"); -console.warn("I am a warning log message"); -console.error("I am an error log message"); diff --git a/Base/home/anon/Source/js/date.js b/Base/home/anon/Source/js/date.js deleted file mode 100644 index 160786e0bcb..00000000000 --- a/Base/home/anon/Source/js/date.js +++ /dev/null @@ -1,14 +0,0 @@ -var now = Date.now(); -console.log("Unix timestamp: " + now / 1000); - -var d = new Date(); -var year = d.getFullYear(); -var month = (d.getMonth() + 1).toString().padStart(2, "0"); -var day = d.getDate().toString().padStart(2, "0"); -var hours = d.getHours().toString().padStart(2, "0"); -var minutes = d.getMinutes().toString().padStart(2, "0"); -var seconds = d.getSeconds().toString().padStart(2, "0"); -var milliseconds = d.getMilliseconds().toString().padStart(3, "0"); - -console.log("Date: " + year + "-" + month + "-" + day); -console.log("Time: " + hours + ":" + minutes + ":" + seconds + "." + milliseconds); diff --git a/Base/home/anon/Source/js/for-loop.js b/Base/home/anon/Source/js/for-loop.js deleted file mode 100644 index 9602468d596..00000000000 --- a/Base/home/anon/Source/js/for-loop.js +++ /dev/null @@ -1,5 +0,0 @@ -var x = 0; -for (var i = 0; i !== 3; i += 1) { - x += 2; -} -x; diff --git a/Base/home/anon/Source/js/forced-gc.js b/Base/home/anon/Source/js/forced-gc.js deleted file mode 100644 index 5ea20bcdddb..00000000000 --- a/Base/home/anon/Source/js/forced-gc.js +++ /dev/null @@ -1,5 +0,0 @@ -function foo() { - var x = {}; - gc(); -} -foo(); diff --git a/Base/home/anon/Source/js/function-with-arguments.js b/Base/home/anon/Source/js/function-with-arguments.js deleted file mode 100644 index f87b61162c8..00000000000 --- a/Base/home/anon/Source/js/function-with-arguments.js +++ /dev/null @@ -1,4 +0,0 @@ -function foo(a, b) { - return a + b; -} -foo(1, 2 + 3); diff --git a/Base/home/anon/Source/js/gc-strings.js b/Base/home/anon/Source/js/gc-strings.js deleted file mode 100644 index 39d54008174..00000000000 --- a/Base/home/anon/Source/js/gc-strings.js +++ /dev/null @@ -1,8 +0,0 @@ -function foo() { - var a = []; - for (var i = 0; i < 4000; ++i) { - a.push("string" + i); - } -} - -foo(); diff --git a/Base/home/anon/Source/js/hasOwnProperty.js b/Base/home/anon/Source/js/hasOwnProperty.js deleted file mode 100644 index d874105c003..00000000000 --- a/Base/home/anon/Source/js/hasOwnProperty.js +++ /dev/null @@ -1,2 +0,0 @@ -var x = "foobar"; -console.log(x.hasOwnProperty("length")); diff --git a/Base/home/anon/Source/js/native-function.js b/Base/home/anon/Source/js/native-function.js deleted file mode 100644 index 529c4df04fd..00000000000 --- a/Base/home/anon/Source/js/native-function.js +++ /dev/null @@ -1 +0,0 @@ -console.log("Hello friends!") diff --git a/Base/home/anon/Source/js/object-expression.js b/Base/home/anon/Source/js/object-expression.js deleted file mode 100644 index 143ece5967b..00000000000 --- a/Base/home/anon/Source/js/object-expression.js +++ /dev/null @@ -1,10 +0,0 @@ -const a = 1; -const computedKey = "d"; -const object = {a, b: 2, "c": 3, [computedKey]: 2 + 2}; -const emptyObject = {}; - -console.log(object.a); -console.log(object.b); -console.log(object.c); -console.log(object.d); -console.log(emptyObject.foo); diff --git a/Base/home/anon/Source/js/operator-precedence.js b/Base/home/anon/Source/js/operator-precedence.js deleted file mode 100644 index 9a1b79682ef..00000000000 --- a/Base/home/anon/Source/js/operator-precedence.js +++ /dev/null @@ -1,2 +0,0 @@ -var foo = 1 + 2 * 3 - 4 / 5; -foo.bar(); diff --git a/Base/home/anon/Source/js/simple-function.js b/Base/home/anon/Source/js/simple-function.js deleted file mode 100644 index 801e58841f5..00000000000 --- a/Base/home/anon/Source/js/simple-function.js +++ /dev/null @@ -1,2 +0,0 @@ -function foo() { return (1 + 2) + 3; } -foo(); diff --git a/Base/home/anon/Source/js/simple-parse.js b/Base/home/anon/Source/js/simple-parse.js deleted file mode 100644 index 0bac548acbc..00000000000 --- a/Base/home/anon/Source/js/simple-parse.js +++ /dev/null @@ -1,7 +0,0 @@ -var foo = 1; -function bar() { - return 38; -} -foo = {}; -foo = bar() + 4; -foo; diff --git a/Base/home/anon/Source/js/simple-scopes.js b/Base/home/anon/Source/js/simple-scopes.js deleted file mode 100644 index 246a7c44127..00000000000 --- a/Base/home/anon/Source/js/simple-scopes.js +++ /dev/null @@ -1,11 +0,0 @@ - //I should return `undefined` because y is bound to the inner-most enclosing function, i.e the nested one (bar()), therefore, it's undefined in the scope of foo() -function foo() { - function bar() { - var y = 6; - } - - bar(); - return y; -} - -console.log(foo()); diff --git a/Base/home/anon/Source/js/simple-variables.js b/Base/home/anon/Source/js/simple-variables.js deleted file mode 100644 index dfecdab09c3..00000000000 --- a/Base/home/anon/Source/js/simple-variables.js +++ /dev/null @@ -1,7 +0,0 @@ -c = 1; -function foo() { - var a = 5; - var b = 7; - return a + b + c; -} -foo(); diff --git a/Base/home/anon/Source/js/string-length.js b/Base/home/anon/Source/js/string-length.js deleted file mode 100644 index 38b1ed191b9..00000000000 --- a/Base/home/anon/Source/js/string-length.js +++ /dev/null @@ -1 +0,0 @@ -"hello friends".length diff --git a/Base/home/anon/Source/js/strings.js b/Base/home/anon/Source/js/strings.js deleted file mode 100644 index 1928d61d6f3..00000000000 --- a/Base/home/anon/Source/js/strings.js +++ /dev/null @@ -1,12 +0,0 @@ -var d = "Double quoted string\n"; -console.log(d); -var s = 'Single quoted string\n'; -console.log(s) -var e = "Escaped characters \b \f \n \r \t \v \' \" \\ \n"; -console.log(e) -var u = "Unterminated string - this is not possible in js\n"; -console.log(u); - -var u2 = 'This is neither\n -console.log(u2); diff --git a/Base/home/anon/Source/js/throw.js b/Base/home/anon/Source/js/throw.js deleted file mode 100644 index 67c891f571e..00000000000 --- a/Base/home/anon/Source/js/throw.js +++ /dev/null @@ -1,5 +0,0 @@ -try { - throw 123; -} catch (e) { - console.log(e); -} diff --git a/Base/home/anon/Source/js/try.js b/Base/home/anon/Source/js/try.js deleted file mode 100644 index 7b4f892ca4d..00000000000 --- a/Base/home/anon/Source/js/try.js +++ /dev/null @@ -1,10 +0,0 @@ -try { - console.log("you should see me"); - foo(); - console.log("not me"); -} catch (e) { - console.log("catch"); - console.log(e.name); -} finally { - console.log("finally"); -} diff --git a/Base/home/anon/Source/js/type-play.js b/Base/home/anon/Source/js/type-play.js deleted file mode 100644 index a9a8bb3d53f..00000000000 --- a/Base/home/anon/Source/js/type-play.js +++ /dev/null @@ -1,12 +0,0 @@ -const object = {}; - -console.log(true == 1); -console.log(null == undefined); -console.log("12" == 12); -console.log(1 + "12"); -console.log(12 / "12" == true); -console.log(2 * "12"); -console.log(~"24"); -console.log(~true); -console.log(2*2 + "4"); -console.log(object == "[object Object]"); diff --git a/Base/home/anon/Source/js/typeof.js b/Base/home/anon/Source/js/typeof.js deleted file mode 100644 index ead2189226c..00000000000 --- a/Base/home/anon/Source/js/typeof.js +++ /dev/null @@ -1 +0,0 @@ -console.log(typeof undefined, typeof true, typeof 'a', typeof 1, typeof {}); diff --git a/Base/home/anon/Source/little/Makefile b/Base/home/anon/Source/little/Makefile deleted file mode 100644 index 7cb69aab702..00000000000 --- a/Base/home/anon/Source/little/Makefile +++ /dev/null @@ -1,17 +0,0 @@ -PROGRAM = little -OBJS = main.o other.o -CXXFLAGS = -g -std=c++23 - -all: $(PROGRAM) - -$(PROGRAM): $(OBJS) - $(CXX) -o $@ $(OBJS) - -%.o: %.cpp - $(CXX) $(CXXFLAGS) -o $@ -c $< - -clean: - rm $(OBJS) $(PROGRAM) - -run: - ./$(PROGRAM) diff --git a/Base/home/anon/Source/little/main.cpp b/Base/home/anon/Source/little/main.cpp deleted file mode 100644 index 42816ee22c8..00000000000 --- a/Base/home/anon/Source/little/main.cpp +++ /dev/null @@ -1,36 +0,0 @@ -#include "other.h" -#include -#include - -enum TestEnum { - ValueOne, - ValueTwo -}; - -struct MyStruct { - int x { -1 }; - bool status { false }; - TestEnum test_value { ValueOne }; -}; - -struct Container { - MyStruct inner; - int index; -}; - -int main(int, char**) -{ - MyStruct my_struct; - my_struct.status = !my_struct.status; - printf("my_struct.x is %d\n", my_struct.x); - int arr[6] = { -1, 2, 20, 5, 5 }; - int other_arr[1][2] = { { 0, 2 } }; - Container container; - for (int i = 0; i < 3; ++i) { - // This is a comment :^) - MyNamespace::func(); - printf("Hello friends!\n"); - mkdir("/tmp/xyz", 0755); - } - return 0; -} diff --git a/Base/home/anon/Source/little/other.cpp b/Base/home/anon/Source/little/other.cpp deleted file mode 100644 index 5569d78eeb7..00000000000 --- a/Base/home/anon/Source/little/other.cpp +++ /dev/null @@ -1,18 +0,0 @@ -#include "other.h" -#include - -namespace MyNamespace { - -int func() -{ - int x = 1; - int y = 2; - INT_Z = 3; - StructInHeader mystruct; - printf("x: %d\n", x); - printf("y: %d\n", y); - printf("x+y: %d\n", x + y); - return x + y; -} - -} diff --git a/Base/home/anon/Source/little/other.h b/Base/home/anon/Source/little/other.h deleted file mode 100644 index 9ccb32196cb..00000000000 --- a/Base/home/anon/Source/little/other.h +++ /dev/null @@ -1,20 +0,0 @@ -#pragma once - -namespace MyNamespace { - -int func(); - -#define USE_VAR2 - -#define INT_Z int z - -struct StructInHeader { - int var1; -#ifdef USE_VAR2 - int var2; -#else - int var3; -#endif -}; - -} diff --git a/Base/home/anon/Tests/run-tests-and-shutdown.sh b/Base/home/anon/Tests/run-tests-and-shutdown.sh deleted file mode 100755 index 62d72ede4b1..00000000000 --- a/Base/home/anon/Tests/run-tests-and-shutdown.sh +++ /dev/null @@ -1,25 +0,0 @@ -#!/bin/Shell -# shellcheck disable=all - -echo -echo "==== Running Tests on SerenityOS ====" - -echo "architecture is: >>$(uname -m)<<" -if [ "$(uname -m)" = "AArch64" ] && [ "$1" != "--force" ] { - fail_count=0 -} -else { - export LLVM_PROFILE_FILE="$HOME/profiles/%p-profile.profraw" - run-tests --show-progress=false --unlink-coredumps - fail_count=$? - unset LLVM_PROFILE_FILE -} - -echo "Failed: $fail_count" > ./test-results.log - -if test $DO_SHUTDOWN_AFTER_TESTS { - sync - shutdown -n -} - -exit $fail_count diff --git a/Base/res/apps/2048.af b/Base/res/apps/2048.af deleted file mode 100644 index e4606b7804d..00000000000 --- a/Base/res/apps/2048.af +++ /dev/null @@ -1,4 +0,0 @@ -[App] -Name=&2048 -Executable=/bin/2048 -Category=&Games diff --git a/Base/res/apps/3DFileViewer.af b/Base/res/apps/3DFileViewer.af deleted file mode 100644 index f8de60d8ea2..00000000000 --- a/Base/res/apps/3DFileViewer.af +++ /dev/null @@ -1,7 +0,0 @@ -[App] -Name=&3D File Viewer -Executable=/bin/3DFileViewer -Category=Gra&phics - -[Launcher] -FileTypes=obj diff --git a/Base/res/apps/AnalogClock.af b/Base/res/apps/AnalogClock.af deleted file mode 100644 index d433d3fa258..00000000000 --- a/Base/res/apps/AnalogClock.af +++ /dev/null @@ -1,4 +0,0 @@ -[App] -Name=A&nalog Clock -Executable=/bin/AnalogClock -Category=&Utilities diff --git a/Base/res/apps/Assistant.af b/Base/res/apps/Assistant.af deleted file mode 100644 index 23428362726..00000000000 --- a/Base/res/apps/Assistant.af +++ /dev/null @@ -1,4 +0,0 @@ -[App] -Name=&Assistant -Executable=/bin/Assistant -Category=&Utilities diff --git a/Base/res/apps/BrickGame.af b/Base/res/apps/BrickGame.af deleted file mode 100644 index fde8d62baba..00000000000 --- a/Base/res/apps/BrickGame.af +++ /dev/null @@ -1,4 +0,0 @@ -[App] -Name=&Brick Game -Executable=/bin/BrickGame -Category=&Games diff --git a/Base/res/apps/Browser.af b/Base/res/apps/Browser.af deleted file mode 100644 index 64a3b5e9f65..00000000000 --- a/Base/res/apps/Browser.af +++ /dev/null @@ -1,9 +0,0 @@ -[App] -Name=&Ladybird -Executable=/bin/Browser -Arguments=--new-window -Category=&Internet - -[Launcher] -FileTypes=html,htm -Protocols=gemini,http,https diff --git a/Base/res/apps/BrowserSettings.af b/Base/res/apps/BrowserSettings.af deleted file mode 100644 index fcdbd2fc68b..00000000000 --- a/Base/res/apps/BrowserSettings.af +++ /dev/null @@ -1,6 +0,0 @@ -[App] -Name=Browser Settings -Executable=/bin/BrowserSettings -Category=Settings -Description=Configure Browser -ExcludeFromSystemMenu=true diff --git a/Base/res/apps/Calculator.af b/Base/res/apps/Calculator.af deleted file mode 100644 index 29ded374d4a..00000000000 --- a/Base/res/apps/Calculator.af +++ /dev/null @@ -1,4 +0,0 @@ -[App] -Name=&Calculator -Executable=/bin/Calculator -Category=&Utilities diff --git a/Base/res/apps/Calendar.af b/Base/res/apps/Calendar.af deleted file mode 100644 index 3208826a973..00000000000 --- a/Base/res/apps/Calendar.af +++ /dev/null @@ -1,7 +0,0 @@ -[App] -Name=&Calendar -Executable=/bin/Calendar -Category=&Office - -[Launcher] -FileTypes=cal diff --git a/Base/res/apps/CalendarSettings.af b/Base/res/apps/CalendarSettings.af deleted file mode 100644 index e164d001d4e..00000000000 --- a/Base/res/apps/CalendarSettings.af +++ /dev/null @@ -1,6 +0,0 @@ -[App] -Name=Calendar Settings -Executable=/bin/CalendarSettings -Category=Settings -Description=Configure the Calendar application and applet -ExcludeFromSystemMenu=true diff --git a/Base/res/apps/CatDog.af b/Base/res/apps/CatDog.af deleted file mode 100644 index 491d8a4f917..00000000000 --- a/Base/res/apps/CatDog.af +++ /dev/null @@ -1,4 +0,0 @@ -[App] -Name=&CatDog -Executable=/bin/CatDog -Category=&Demos diff --git a/Base/res/apps/CertificatesSettings.af b/Base/res/apps/CertificatesSettings.af deleted file mode 100644 index 1ba704c9665..00000000000 --- a/Base/res/apps/CertificatesSettings.af +++ /dev/null @@ -1,6 +0,0 @@ -[App] -Name=Certificate Settings -Executable=/bin/CertificateSettings -Category=Settings -Description=Access and change the system's certificates -ExcludeFromSystemMenu=true diff --git a/Base/res/apps/CharacterMap.af b/Base/res/apps/CharacterMap.af deleted file mode 100644 index db8e54ec470..00000000000 --- a/Base/res/apps/CharacterMap.af +++ /dev/null @@ -1,4 +0,0 @@ -[App] -Name=Cha&racter Map -Executable=/bin/CharacterMap -Category=&Utilities diff --git a/Base/res/apps/Chess.af b/Base/res/apps/Chess.af deleted file mode 100644 index 3364153a1ff..00000000000 --- a/Base/res/apps/Chess.af +++ /dev/null @@ -1,4 +0,0 @@ -[App] -Name=&Chess -Executable=/bin/Chess -Category=&Games diff --git a/Base/res/apps/ClockSettings.af b/Base/res/apps/ClockSettings.af deleted file mode 100644 index baa3d417113..00000000000 --- a/Base/res/apps/ClockSettings.af +++ /dev/null @@ -1,6 +0,0 @@ -[App] -Name=Clock Settings -Executable=/bin/ClockSettings -Category=Settings -Description=Configure the system clock -ExcludeFromSystemMenu=true diff --git a/Base/res/apps/ColorLines.af b/Base/res/apps/ColorLines.af deleted file mode 100644 index 6583aa0ebf7..00000000000 --- a/Base/res/apps/ColorLines.af +++ /dev/null @@ -1,4 +0,0 @@ -[App] -Name=Color &Lines -Executable=/bin/ColorLines -Category=&Games diff --git a/Base/res/apps/DisplaySettings.af b/Base/res/apps/DisplaySettings.af deleted file mode 100644 index 262797ab353..00000000000 --- a/Base/res/apps/DisplaySettings.af +++ /dev/null @@ -1,6 +0,0 @@ -[App] -Name=Display Settings -Executable=/bin/DisplaySettings -Category=Settings -Description=Configure your display hardware, desktop wallpaper, fonts, etc. -ExcludeFromSystemMenu=true diff --git a/Base/res/apps/Eyes.af b/Base/res/apps/Eyes.af deleted file mode 100644 index 3687c373e71..00000000000 --- a/Base/res/apps/Eyes.af +++ /dev/null @@ -1,4 +0,0 @@ -[App] -Name=&Eyes -Executable=/bin/Eyes -Category=&Demos diff --git a/Base/res/apps/FileManager.af b/Base/res/apps/FileManager.af deleted file mode 100644 index d3974987eac..00000000000 --- a/Base/res/apps/FileManager.af +++ /dev/null @@ -1,7 +0,0 @@ -[App] -Name=&File Manager -Executable=/bin/FileManager -Category=&Utilities - -[Launcher] -FileTypes=zip diff --git a/Base/res/apps/FlappyBug.af b/Base/res/apps/FlappyBug.af deleted file mode 100644 index e228a8796fd..00000000000 --- a/Base/res/apps/FlappyBug.af +++ /dev/null @@ -1,4 +0,0 @@ -[App] -Name=&Flappy Bug -Executable=/bin/FlappyBug -Category=&Games diff --git a/Base/res/apps/Flood.af b/Base/res/apps/Flood.af deleted file mode 100644 index c0d1403151f..00000000000 --- a/Base/res/apps/Flood.af +++ /dev/null @@ -1,4 +0,0 @@ -[App] -Name=Floo&d -Executable=/bin/Flood -Category=&Games diff --git a/Base/res/apps/FontEditor.af b/Base/res/apps/FontEditor.af deleted file mode 100644 index 2fe9060d313..00000000000 --- a/Base/res/apps/FontEditor.af +++ /dev/null @@ -1,7 +0,0 @@ -[App] -Name=&Font Editor -Executable=/bin/FontEditor -Category=D&evelopment - -[Launcher] -FileTypes=font diff --git a/Base/res/apps/GMLPlayground.af b/Base/res/apps/GMLPlayground.af deleted file mode 100644 index 0d62b769327..00000000000 --- a/Base/res/apps/GMLPlayground.af +++ /dev/null @@ -1,7 +0,0 @@ -[App] -Name=&GML Playground -Executable=/bin/GMLPlayground -Category=D&evelopment - -[Launcher] -FileTypes=gml diff --git a/Base/res/apps/GameOfLife.af b/Base/res/apps/GameOfLife.af deleted file mode 100644 index 21a504df3aa..00000000000 --- a/Base/res/apps/GameOfLife.af +++ /dev/null @@ -1,4 +0,0 @@ -[App] -Name=&Game of Life -Executable=/bin/GameOfLife -Category=&Games diff --git a/Base/res/apps/GamesSettings.af b/Base/res/apps/GamesSettings.af deleted file mode 100644 index c4260df343b..00000000000 --- a/Base/res/apps/GamesSettings.af +++ /dev/null @@ -1,6 +0,0 @@ -[App] -Name=Games Settings -Executable=/bin/GamesSettings -Category=Settings -Description=Configure games -ExcludeFromSystemMenu=true diff --git a/Base/res/apps/Gradient.af b/Base/res/apps/Gradient.af deleted file mode 100644 index a4485f86052..00000000000 --- a/Base/res/apps/Gradient.af +++ /dev/null @@ -1,5 +0,0 @@ -[App] -Name=Gradient -Executable=/bin/Gradient -Category=Demos/Screensaver -ExcludeFromSystemMenu=true diff --git a/Base/res/apps/HackStudio.af b/Base/res/apps/HackStudio.af deleted file mode 100644 index 9c9ba1a54fb..00000000000 --- a/Base/res/apps/HackStudio.af +++ /dev/null @@ -1,4 +0,0 @@ -[App] -Name=&Hack Studio -Executable=/bin/HackStudio -Category=D&evelopment diff --git a/Base/res/apps/Hearts.af b/Base/res/apps/Hearts.af deleted file mode 100644 index 6a328942036..00000000000 --- a/Base/res/apps/Hearts.af +++ /dev/null @@ -1,4 +0,0 @@ -[App] -Name=&Hearts -Executable=/bin/Hearts -Category=&Games diff --git a/Base/res/apps/Help.af b/Base/res/apps/Help.af deleted file mode 100644 index 651709a94ac..00000000000 --- a/Base/res/apps/Help.af +++ /dev/null @@ -1,4 +0,0 @@ -[App] -Name=&Help -Executable=/bin/Help -Category=&Utilities diff --git a/Base/res/apps/HexEditor.af b/Base/res/apps/HexEditor.af deleted file mode 100644 index 2e6743e83b0..00000000000 --- a/Base/res/apps/HexEditor.af +++ /dev/null @@ -1,7 +0,0 @@ -[App] -Name=He&x Editor -Executable=/bin/HexEditor -Category=D&evelopment - -[Launcher] -FileTypes=font diff --git a/Base/res/apps/ImageViewer.af b/Base/res/apps/ImageViewer.af deleted file mode 100644 index 8771a417635..00000000000 --- a/Base/res/apps/ImageViewer.af +++ /dev/null @@ -1,7 +0,0 @@ -[App] -Name=&Image Viewer -Executable=/bin/ImageViewer -Category=Gra&phics - -[Launcher] -FileTypes=bmp,dds,gif,ico,iff,jb2,jbig2,jp2,jpeg,jpg,jpx,jxl,lbm,pbm,pgm,png,ppm,qoi,tga,tiff,tif,tvg diff --git a/Base/res/apps/Keyboard.af b/Base/res/apps/Keyboard.af deleted file mode 100644 index a72256ce90e..00000000000 --- a/Base/res/apps/Keyboard.af +++ /dev/null @@ -1,6 +0,0 @@ -[App] -Name=Keyboard Settings -Executable=/bin/KeyboardSettings -Category=Settings -Description=Customize your keyboard layout and other settings -ExcludeFromSystemMenu=true diff --git a/Base/res/apps/KeyboardMapper.af b/Base/res/apps/KeyboardMapper.af deleted file mode 100644 index 91356e42362..00000000000 --- a/Base/res/apps/KeyboardMapper.af +++ /dev/null @@ -1,4 +0,0 @@ -[App] -Name=&Keyboard Mapper -Executable=/bin/KeyboardMapper -Category=D&evelopment diff --git a/Base/res/apps/LibGfxDemo.af b/Base/res/apps/LibGfxDemo.af deleted file mode 100644 index b4bb9652847..00000000000 --- a/Base/res/apps/LibGfxDemo.af +++ /dev/null @@ -1,4 +0,0 @@ -[App] -Name=Lib&Gfx Demo -Executable=/bin/LibGfxDemo -Category=&Demos diff --git a/Base/res/apps/LibGfxScaleDemo.af b/Base/res/apps/LibGfxScaleDemo.af deleted file mode 100644 index a24c61e4cd9..00000000000 --- a/Base/res/apps/LibGfxScaleDemo.af +++ /dev/null @@ -1,4 +0,0 @@ -[App] -Name=LibGfx Sc&ale Demo -Executable=/bin/LibGfxScaleDemo -Category=&Demos diff --git a/Base/res/apps/Magnifier.af b/Base/res/apps/Magnifier.af deleted file mode 100644 index f35df859246..00000000000 --- a/Base/res/apps/Magnifier.af +++ /dev/null @@ -1,4 +0,0 @@ -[App] -Name=&Magnifier -Executable=/bin/Magnifier -Category=&Utilities diff --git a/Base/res/apps/Mail.af b/Base/res/apps/Mail.af deleted file mode 100644 index afc8f661ec4..00000000000 --- a/Base/res/apps/Mail.af +++ /dev/null @@ -1,4 +0,0 @@ -[App] -Name=&Mail -Executable=/bin/Mail -Category=&Internet diff --git a/Base/res/apps/MailSettings.af b/Base/res/apps/MailSettings.af deleted file mode 100644 index eab1cb6316b..00000000000 --- a/Base/res/apps/MailSettings.af +++ /dev/null @@ -1,6 +0,0 @@ -[App] -Name=Mail Settings -Executable=/bin/MailSettings -Category=Settings -Description=Configure the Mail application -ExcludeFromSystemMenu=true diff --git a/Base/res/apps/Mandelbrot.af b/Base/res/apps/Mandelbrot.af deleted file mode 100644 index 686c489cc15..00000000000 --- a/Base/res/apps/Mandelbrot.af +++ /dev/null @@ -1,4 +0,0 @@ -[App] -Name=&Mandelbrot -Executable=/bin/Mandelbrot -Category=&Demos diff --git a/Base/res/apps/Maps.af b/Base/res/apps/Maps.af deleted file mode 100644 index 69f194624f1..00000000000 --- a/Base/res/apps/Maps.af +++ /dev/null @@ -1,5 +0,0 @@ -[App] -Name=M&aps -Executable=/bin/Maps -Category=&Internet -Description=Explore the world diff --git a/Base/res/apps/MapsSettings.af b/Base/res/apps/MapsSettings.af deleted file mode 100644 index f49f36049dc..00000000000 --- a/Base/res/apps/MapsSettings.af +++ /dev/null @@ -1,6 +0,0 @@ -[App] -Name=Maps Settings -Executable=/bin/MapsSettings -Category=Settings -Description=Configure the Maps application -ExcludeFromSystemMenu=true diff --git a/Base/res/apps/MasterWord.af b/Base/res/apps/MasterWord.af deleted file mode 100644 index a99933eccee..00000000000 --- a/Base/res/apps/MasterWord.af +++ /dev/null @@ -1,4 +0,0 @@ -[App] -Name=&MasterWord -Executable=/bin/MasterWord -Category=&Games diff --git a/Base/res/apps/Minesweeper.af b/Base/res/apps/Minesweeper.af deleted file mode 100644 index 75e4f9edfa4..00000000000 --- a/Base/res/apps/Minesweeper.af +++ /dev/null @@ -1,4 +0,0 @@ -[App] -Name=M&inesweeper -Executable=/bin/Minesweeper -Category=&Games diff --git a/Base/res/apps/ModelGallery.af b/Base/res/apps/ModelGallery.af deleted file mode 100644 index c5ac91a39b1..00000000000 --- a/Base/res/apps/ModelGallery.af +++ /dev/null @@ -1,4 +0,0 @@ -[App] -Name=M&odel Gallery -Executable=/bin/ModelGallery -Category=&Demos diff --git a/Base/res/apps/MouseSettings.af b/Base/res/apps/MouseSettings.af deleted file mode 100644 index 6dd13a9bb39..00000000000 --- a/Base/res/apps/MouseSettings.af +++ /dev/null @@ -1,6 +0,0 @@ -[App] -Name=Mouse Settings -Executable=/bin/MouseSettings -Category=Settings -Description=Customize your mouse and cursor settings -ExcludeFromSystemMenu=true diff --git a/Base/res/apps/NetworkSettings.af b/Base/res/apps/NetworkSettings.af deleted file mode 100644 index 883dc828a4f..00000000000 --- a/Base/res/apps/NetworkSettings.af +++ /dev/null @@ -1,6 +0,0 @@ -[App] -Name=Network Settings -Executable=/bin/NetworkSettings -Category=Settings -Description=Configure network connections -ExcludeFromSystemMenu=true diff --git a/Base/res/apps/PDFViewer.af b/Base/res/apps/PDFViewer.af deleted file mode 100644 index 457f8b29676..00000000000 --- a/Base/res/apps/PDFViewer.af +++ /dev/null @@ -1,7 +0,0 @@ -[App] -Name=&PDF Viewer -Executable=/bin/PDFViewer -Category=&Office - -[Launcher] -FileTypes=pdf diff --git a/Base/res/apps/PartitionEditor.af b/Base/res/apps/PartitionEditor.af deleted file mode 100644 index 1c5fb6f593d..00000000000 --- a/Base/res/apps/PartitionEditor.af +++ /dev/null @@ -1,5 +0,0 @@ -[App] -Name=&Partition Editor -Executable=/bin/PartitionEditor -RequiresRoot=true -Category=&Utilities diff --git a/Base/res/apps/Piano.af b/Base/res/apps/Piano.af deleted file mode 100644 index efea3f9b673..00000000000 --- a/Base/res/apps/Piano.af +++ /dev/null @@ -1,4 +0,0 @@ -[App] -Name=&Piano -Executable=/bin/Piano -Category=&Media diff --git a/Base/res/apps/PixelPaint.af b/Base/res/apps/PixelPaint.af deleted file mode 100644 index 83ca6cb729f..00000000000 --- a/Base/res/apps/PixelPaint.af +++ /dev/null @@ -1,7 +0,0 @@ -[App] -Name=&Pixel Paint -Executable=/bin/PixelPaint -Category=Gra&phics - -[Launcher] -FileTypes=pp,bmp,dds,gif,ico,jpeg,jpg,jxl,pbm,pgm,png,ppm,qoi,tga diff --git a/Base/res/apps/Presenter.af b/Base/res/apps/Presenter.af deleted file mode 100644 index b6cf03a0e1d..00000000000 --- a/Base/res/apps/Presenter.af +++ /dev/null @@ -1,7 +0,0 @@ -[App] -Name=P&resenter -Executable=/bin/Presenter -Category=&Office - -[Launcher] -FileTypes=presenter diff --git a/Base/res/apps/Profiler.af b/Base/res/apps/Profiler.af deleted file mode 100644 index 1cc0f82a66c..00000000000 --- a/Base/res/apps/Profiler.af +++ /dev/null @@ -1,7 +0,0 @@ -[App] -Name=&Profiler -Executable=/bin/Profiler -Category=D&evelopment - -[Launcher] -FileTypes=profile diff --git a/Base/res/apps/SQLStudio.af b/Base/res/apps/SQLStudio.af deleted file mode 100644 index 32a90e19409..00000000000 --- a/Base/res/apps/SQLStudio.af +++ /dev/null @@ -1,7 +0,0 @@ -[App] -Name=&SQL Studio -Executable=/bin/SQLStudio -Category=D&evelopment - -[Launcher] -FileTypes=db,sql diff --git a/Base/res/apps/Screensaver.af b/Base/res/apps/Screensaver.af deleted file mode 100644 index 12532c50fca..00000000000 --- a/Base/res/apps/Screensaver.af +++ /dev/null @@ -1,4 +0,0 @@ -[App] -Name=&Screensaver -Executable=/bin/Screensaver -Category=&Demos diff --git a/Base/res/apps/Screenshot.af b/Base/res/apps/Screenshot.af deleted file mode 100644 index 13143a6ff96..00000000000 --- a/Base/res/apps/Screenshot.af +++ /dev/null @@ -1,4 +0,0 @@ -[App] -Name=&Screenshot -Executable=/bin/Screenshot -Category=&Utilities diff --git a/Base/res/apps/Snake.af b/Base/res/apps/Snake.af deleted file mode 100644 index d323fa16be3..00000000000 --- a/Base/res/apps/Snake.af +++ /dev/null @@ -1,4 +0,0 @@ -[App] -Name=&Snake -Executable=/bin/Snake -Category=&Games diff --git a/Base/res/apps/Solitaire.af b/Base/res/apps/Solitaire.af deleted file mode 100644 index 2639760ae3d..00000000000 --- a/Base/res/apps/Solitaire.af +++ /dev/null @@ -1,4 +0,0 @@ -[App] -Name=S&olitaire -Executable=/bin/Solitaire -Category=&Games diff --git a/Base/res/apps/SoundPlayer.af b/Base/res/apps/SoundPlayer.af deleted file mode 100644 index a1b68f97a0c..00000000000 --- a/Base/res/apps/SoundPlayer.af +++ /dev/null @@ -1,8 +0,0 @@ -[App] -Name=&Sound Player -Executable=/bin/SoundPlayer -Category=&Media - -[Launcher] -FileTypes=mp3,flac,m3u,m3u8,qoa,wav -MimeTypes=audio/mpeg,audio/wav,audio/flac,audio/qoa diff --git a/Base/res/apps/SpaceAnalyzer.af b/Base/res/apps/SpaceAnalyzer.af deleted file mode 100644 index eadf2ca4d34..00000000000 --- a/Base/res/apps/SpaceAnalyzer.af +++ /dev/null @@ -1,4 +0,0 @@ -[App] -Name=Space Ana&lyzer -Executable=/bin/SpaceAnalyzer -Category=&Utilities diff --git a/Base/res/apps/Spider.af b/Base/res/apps/Spider.af deleted file mode 100644 index 9e9b731e5ee..00000000000 --- a/Base/res/apps/Spider.af +++ /dev/null @@ -1,4 +0,0 @@ -[App] -Name=S&pider -Executable=/bin/Spider -Category=&Games diff --git a/Base/res/apps/Spreadsheet.af b/Base/res/apps/Spreadsheet.af deleted file mode 100644 index 6e1aa605934..00000000000 --- a/Base/res/apps/Spreadsheet.af +++ /dev/null @@ -1,7 +0,0 @@ -[App] -Name=&Spreadsheet -Executable=/bin/Spreadsheet -Category=&Office - -[Launcher] -FileTypes=sheets diff --git a/Base/res/apps/Starfield.af b/Base/res/apps/Starfield.af deleted file mode 100644 index 5c0d8dade70..00000000000 --- a/Base/res/apps/Starfield.af +++ /dev/null @@ -1,5 +0,0 @@ -[App] -Name=Starfield -Executable=/bin/Starfield -Category=Demos/Screensaver -ExcludeFromSystemMenu=true diff --git a/Base/res/apps/SystemMonitor.af b/Base/res/apps/SystemMonitor.af deleted file mode 100644 index 8416a23525b..00000000000 --- a/Base/res/apps/SystemMonitor.af +++ /dev/null @@ -1,4 +0,0 @@ -[App] -Name=S&ystem Monitor -Executable=/bin/SystemMonitor -Category=&Utilities diff --git a/Base/res/apps/Terminal.af b/Base/res/apps/Terminal.af deleted file mode 100644 index e0b35e933b5..00000000000 --- a/Base/res/apps/Terminal.af +++ /dev/null @@ -1,4 +0,0 @@ -[App] -Name=&Terminal -Executable=/bin/Terminal -Category=&Utilities diff --git a/Base/res/apps/TerminalSettings.af b/Base/res/apps/TerminalSettings.af deleted file mode 100644 index b6080289993..00000000000 --- a/Base/res/apps/TerminalSettings.af +++ /dev/null @@ -1,6 +0,0 @@ -[App] -Name=Terminal Settings -Executable=/bin/TerminalSettings -Category=Settings -Description=Configure the Terminal appearance and behavior -ExcludeFromSystemMenu=true diff --git a/Base/res/apps/TextEditor.af b/Base/res/apps/TextEditor.af deleted file mode 100644 index b75d18fe16f..00000000000 --- a/Base/res/apps/TextEditor.af +++ /dev/null @@ -1,7 +0,0 @@ -[App] -Name=Text &Editor -Executable=/bin/TextEditor -Category=&Utilities - -[Launcher] -FileTypes=txt,md,html,htm,js,json,ini diff --git a/Base/res/apps/ThemeEditor.af b/Base/res/apps/ThemeEditor.af deleted file mode 100644 index c4e3ee6a9ff..00000000000 --- a/Base/res/apps/ThemeEditor.af +++ /dev/null @@ -1,4 +0,0 @@ -[App] -Name=&Theme Editor -Executable=/bin/ThemeEditor -Category=D&evelopment diff --git a/Base/res/apps/Tubes.af b/Base/res/apps/Tubes.af deleted file mode 100644 index 18414021ca5..00000000000 --- a/Base/res/apps/Tubes.af +++ /dev/null @@ -1,5 +0,0 @@ -[App] -Name=Tubes -Executable=/bin/Tubes -Category=Demos/Screensaver -ExcludeFromSystemMenu=true diff --git a/Base/res/apps/VideoPlayer.af b/Base/res/apps/VideoPlayer.af deleted file mode 100644 index 91fd034bf28..00000000000 --- a/Base/res/apps/VideoPlayer.af +++ /dev/null @@ -1,7 +0,0 @@ -[App] -Name=&Video Player -Executable=/bin/VideoPlayer -Category=&Media - -[Launcher] -FileTypes=webm,mkv diff --git a/Base/res/apps/WidgetGallery.af b/Base/res/apps/WidgetGallery.af deleted file mode 100644 index 1137f253b8d..00000000000 --- a/Base/res/apps/WidgetGallery.af +++ /dev/null @@ -1,4 +0,0 @@ -[App] -Name=&Widget Gallery -Executable=/bin/WidgetGallery -Category=&Demos diff --git a/Base/res/color-palettes/default.palette b/Base/res/color-palettes/default.palette deleted file mode 100644 index 21598d9763e..00000000000 --- a/Base/res/color-palettes/default.palette +++ /dev/null @@ -1,28 +0,0 @@ -#000000 -#808080 -#800000 -#808000 -#008000 -#008080 -#000080 -#800080 -#808040 -#004040 -#0080ff -#004080 -#8000ff -#804000 -#ffffff -#c0c0c0 -#ff0000 -#ffff00 -#00ff00 -#00ffff -#0000ff -#ff00ff -#ffff80 -#00ff80 -#80ffff -#8080ff -#ff0080 -#ff8040 diff --git a/Base/res/color-palettes/greyscale.palette b/Base/res/color-palettes/greyscale.palette deleted file mode 100644 index 13730ad3b3a..00000000000 --- a/Base/res/color-palettes/greyscale.palette +++ /dev/null @@ -1,16 +0,0 @@ -#000000 -#111111 -#222222 -#333333 -#444444 -#555555 -#666666 -#777777 -#888888 -#999999 -#aaaaaa -#bbbbbb -#cccccc -#dddddd -#eeeeee -#ffffff diff --git a/Base/res/color-palettes/pastel.palette b/Base/res/color-palettes/pastel.palette deleted file mode 100644 index dd91b50af5a..00000000000 --- a/Base/res/color-palettes/pastel.palette +++ /dev/null @@ -1,14 +0,0 @@ -#8f8f8f -#d6d6d6 -#ffc3c3 -#ffc3a5 -#ffe7a6 -#d4ffa6 -#adffa5 -#a5ffd4 -#a5fff1 -#a6d2ff -#a6aeff -#caa6ff -#f3a5ff -#ffa6e9 diff --git a/Base/res/color-schemes/Base16 Dark.ini b/Base/res/color-schemes/Base16 Dark.ini deleted file mode 100644 index cf1874df8bf..00000000000 --- a/Base/res/color-schemes/Base16 Dark.ini +++ /dev/null @@ -1,26 +0,0 @@ -[Options] -ShowBoldTextAsBright=false - -[Primary] -Background=#181818 -Foreground=#d8d8d8 - -[Normal] -Black=#181818 -Red=#ab4642 -Green=#a1b56c -Yellow=#f7ca88 -Blue=#7cafc2 -Magenta=#ba8baf -Cyan=#86c1b9 -White=#d8d8d8 - -[Bright] -Black=#585858 -Red=#ab4642 -Green=#a1b56c -Yellow=#f7ca88 -Blue=#7cafc2 -Magenta=#ba8baf -Cyan=#86c1b9 -White=#f8f8f8 diff --git a/Base/res/color-schemes/Base16 Light.ini b/Base/res/color-schemes/Base16 Light.ini deleted file mode 100644 index b15919d8636..00000000000 --- a/Base/res/color-schemes/Base16 Light.ini +++ /dev/null @@ -1,26 +0,0 @@ -[Options] -ShowBoldTextAsBright=false - -[Primary] -Background=#f8f8f8 -Foreground=#383838 - -[Normal] -Black=#f8f8f8 -Red=#ab4642 -Green=#a1b56c -Yellow=#f7ca88 -Blue=#7cafc2 -Magenta=#ba8baf -Cyan=#86c1b9 -White=#383838 - -[Bright] -Black=#b8b8b8 -Red=#dc9656 -Green=#e8e8e8 -Yellow=#d8d8d8 -Blue=#585858 -Magenta=#282828 -Cyan=#a16946 -White=#181818 diff --git a/Base/res/color-schemes/C64.ini b/Base/res/color-schemes/C64.ini deleted file mode 100644 index 48edc12dace..00000000000 --- a/Base/res/color-schemes/C64.ini +++ /dev/null @@ -1,32 +0,0 @@ -[Options] -; Specifies whether bold text is displayed using bright colors. -ShowBoldTextAsBright=false - -; Default text and background colors -[Primary] -Background=#53489d -Foreground=#7b7fcd - -; Normal named colors -; These correspond to ANSI colors 0-7. -[Normal] -Black=#080200 -Red=#994c50 -Green=#67ac66 -Yellow=#cad488 -Blue=#ffffff -Magenta=#9d59a5 -Cyan=#7cc2c8 -White=#ffffff - -; Bright named colors -; These correspond to ANSI colors 8-15. -[Bright] -Black=#080200 -Red=#994c50 -Green=#67ac66 -Yellow=#cad488 -Blue=#ffffff -Magenta=#9d59a5 -Cyan=#7cc2c8 -White=#ffffff diff --git a/Base/res/color-schemes/Default.ini b/Base/res/color-schemes/Default.ini deleted file mode 100644 index 4fbb37d9a30..00000000000 --- a/Base/res/color-schemes/Default.ini +++ /dev/null @@ -1,32 +0,0 @@ -[Options] -; Specifies whether bold text is displayed using bright colors. -ShowBoldTextAsBright=true - -; Default text and background colors -[Primary] -Background=#000000 -Foreground=#ffffff - -; Normal named colors -; These correspond to ANSI colors 0-7. -[Normal] -Black=#000000 -Red=#cc0000 -Green=#3e9a06 -Yellow=#c4a000 -Blue=#3465a4 -Magenta=#75507b -Cyan=#06989a -White=#eeeeee - -; Bright named colors -; These correspond to ANSI colors 8-15. -[Bright] -Black=#555753 -Red=#ef2929 -Green=#8ae234 -Yellow=#fce94f -Blue=#729fcf -Magenta=#ad7fa8 -Cyan=#34e2e2 -White=#ffffff diff --git a/Base/res/color-schemes/Dracula.ini b/Base/res/color-schemes/Dracula.ini deleted file mode 100644 index 6727cc82ddf..00000000000 --- a/Base/res/color-schemes/Dracula.ini +++ /dev/null @@ -1,26 +0,0 @@ -[Options] -ShowBoldTextAsBright=false - -[Primary] -Background=#282a36 -Foreground=#f8f8f2 - -[Normal] -Black=#21222c -Red=#ff5555 -Green=#50fa7b -Yellow=#f1fa8c -Blue=#bd93f9 -Magenta=#ff79c6 -Cyan=#8be9fd -White=#f8f8f2 - -[Bright] -Black=#6272a4 -Red=#ff6e6e -Green=#69ff94 -Yellow=#ffffa5 -Blue=#d6acff -Magenta=#ff92df -Cyan=#a4ffff -White=#ffffff diff --git a/Base/res/color-schemes/Gruvbox Dark.ini b/Base/res/color-schemes/Gruvbox Dark.ini deleted file mode 100644 index 45272733b1a..00000000000 --- a/Base/res/color-schemes/Gruvbox Dark.ini +++ /dev/null @@ -1,26 +0,0 @@ -[Options] -ShowBoldTextAsBright=false - -[Primary] -Background=#282828 -Foreground=#ebdbb2 - -[Normal] -Black=#282828 -Red=#cc241d -Green=#98971a -Yellow=#d79921 -Blue=#458588 -Magenta=#b16286 -Cyan=#689d6a -White=#a89984 - -[Bright] -Black=#928374 -Red=#fb4934 -Green=#b8bb26 -Yellow=#fabd2f -Blue=#83a598 -Magenta=#d3869b -Cyan=#8ec07c -White=#ebdbb2 diff --git a/Base/res/color-schemes/Gruvbox Light.ini b/Base/res/color-schemes/Gruvbox Light.ini deleted file mode 100644 index 80671052def..00000000000 --- a/Base/res/color-schemes/Gruvbox Light.ini +++ /dev/null @@ -1,26 +0,0 @@ -[Options] -ShowBoldTextAsBright=false - -[Primary] -Background=#fbf1c7 -Foreground=#3c3836 - -[Normal] -Black=#fbf1c7 -Red=#cc241d -Green=#98971a -Yellow=#d79921 -Blue=#458588 -Magenta=#b16286 -Cyan=#689d6a -White=#7c6f64 - -[Bright] -Black=#928374 -Red=#9d0006 -Green=#79740e -Yellow=#b57614 -Blue=#076678 -Magenta=#8f3f71 -Cyan=#427b58 -White=#3c3836 diff --git a/Base/res/color-schemes/Monokai.ini b/Base/res/color-schemes/Monokai.ini deleted file mode 100644 index 9a417034bc6..00000000000 --- a/Base/res/color-schemes/Monokai.ini +++ /dev/null @@ -1,33 +0,0 @@ -[Options] -ShowBoldTextAsBright=false - -[Primary] -Background=#272822 -Foreground=#f8f8f2 - -[Normal] -Black=#1e1f1c -; Monokai red (invalid) -Red=#F44747 -; Monokai green (functions) -Green=#A6E22E -; Monokai yellow (strings) -Yellow=#E6DB74 -; Monokai purple (constants) -Blue=#AE81FF -; Monokai magenta (keywords) -Magenta=#F92672 -; Monokai blue (types) -Cyan=#66D9EF -White=#F8F8F2 - -; Identical to normal colors -[Bright] -Black=#1e1f1c -Red=#F44747 -Green=#A6E22E -Yellow=#E6DB74 -Blue=#AE81FF -Magenta=#F92672 -Cyan=#66D9EF -White=#F8F8F2 diff --git a/Base/res/color-schemes/Nord.ini b/Base/res/color-schemes/Nord.ini deleted file mode 100644 index 6f262f55f8d..00000000000 --- a/Base/res/color-schemes/Nord.ini +++ /dev/null @@ -1,26 +0,0 @@ -[Options] -ShowBoldTextAsBright=false - -[Primary] -Background=#2e3440 -Foreground=#d8dee9 - -[Normal] -Black=#3b4252 -Red=#bf616a -Green=#a3be8c -Yellow=#ebcb8b -Blue=#81a1c1 -Magenta=#b48ead -Cyan=#88c0d0 -White=#e5e9f0 - -[Bright] -Black=#4c566a -Red=#bf616a -Green=#a3be8c -Yellow=#ebcb8b -Blue=#81a1c1 -Magenta=#b48ead -Cyan=#8fbcbb -White=#eceff4 diff --git a/Base/res/color-schemes/Octopus Cat.ini b/Base/res/color-schemes/Octopus Cat.ini deleted file mode 100644 index ef8cac7d8fa..00000000000 --- a/Base/res/color-schemes/Octopus Cat.ini +++ /dev/null @@ -1,32 +0,0 @@ -[Options] -; Specifies whether bold text is displayed using bright colors. -ShowBoldTextAsBright=false - -; Default text and background colors -[Primary] -Background=#ffffff -Foreground=#141414 - -; Normal named colors -; These correspond to ANSI colors 0-7. -[Normal] -Black=#141414 -Red=#a71c21 -Green=#01a23d -Yellow=#01a23d -Blue=#152f65 -Magenta=#a0e5f9 -Cyan=#a0e5f9 -White=#eeeeee - -; Bright named colors -; These correspond to ANSI colors 8-15. -[Bright] -Black=#4f4f4f -Red=#e32113 -Green=#99dab2 -Yellow=#f9f4ef -Blue=#214fa8 -Magenta=#a0e5f9 -Cyan=#30f9fd -White=#ffffff diff --git a/Base/res/color-schemes/Solarized Dark.ini b/Base/res/color-schemes/Solarized Dark.ini deleted file mode 100644 index d0df0de28f5..00000000000 --- a/Base/res/color-schemes/Solarized Dark.ini +++ /dev/null @@ -1,26 +0,0 @@ -[Options] -ShowBoldTextAsBright=false - -[Primary] -Background=#002b36 -Foreground=#839496 - -[Normal] -Black=#073642 -Red=#dc322f -Green=#859900 -Yellow=#b58900 -Blue=#268bd2 -Magenta=#d33682 -Cyan=#2aa198 -White=#eee8d5 - -[Bright] -Black=#002b36 -Red=#cb4b16 -Green=#586e75 -Yellow=#657b83 -Blue=#839496 -Magenta=#6c71c4 -Cyan=#93a1a1 -White=#fdf6e3 diff --git a/Base/res/color-schemes/Solarized Light.ini b/Base/res/color-schemes/Solarized Light.ini deleted file mode 100644 index 1b299519f58..00000000000 --- a/Base/res/color-schemes/Solarized Light.ini +++ /dev/null @@ -1,26 +0,0 @@ -[Options] -ShowBoldTextAsBright=false - -[Primary] -Background=#fdf6e3 -Foreground=#657b83 - -[Normal] -Black=#073642 -Red=#dc322f -Green=#859900 -Yellow=#b58900 -Blue=#268bd2 -Magenta=#d33682 -Cyan=#2aa198 -White=#eee8d5 - -[Bright] -Black=#002b36 -Red=#cb4b16 -Green=#586e75 -Yellow=#657b83 -Blue=#839496 -Magenta=#6c71c4 -Cyan=#93a1a1 -White=#fdf6e3 diff --git a/Base/res/color-schemes/XTerm.ini b/Base/res/color-schemes/XTerm.ini deleted file mode 100644 index acc8bbe6f79..00000000000 --- a/Base/res/color-schemes/XTerm.ini +++ /dev/null @@ -1,26 +0,0 @@ -[Options] -ShowBoldTextAsBright=true - -[Primary] -Background=#000000 -Foreground=#ffffff - -[Normal] -Black=#000000 -Red=#800000 -Green=#008000 -Yellow=#808000 -Blue=#000080 -Magenta=#800080 -Cyan=#008080 -White=#c0c0c0 - -[Bright] -Black=#808080 -Red=#ff0000 -Green=#00ff00 -Yellow=#ffff00 -Blue=#0000ff -Magenta=#ff00ff -Cyan=#00ffff -White=#ffffff diff --git a/Base/res/color-schemes/Zenburn.ini b/Base/res/color-schemes/Zenburn.ini deleted file mode 100644 index 0597998d56d..00000000000 --- a/Base/res/color-schemes/Zenburn.ini +++ /dev/null @@ -1,26 +0,0 @@ -[Options] -ShowBoldTextAsBright=false - -[Primary] -Background=#111111 -Foreground=#dcdccc - -[Normal] -Black=#222222 -Red=#cc9393 -Green=#7f9f7f -Yellow=#d0bf8f -Blue=#6ca0a3 -Magenta=#dc8cc3 -Cyan=#93e0e3 -White=#dcdccc - -[Bright] -Black=#666666 -Red=#dca3a3 -Green=#bfebbf -Yellow=#f0dfaf -Blue=#8cd0d3 -Magenta=#fcace3 -Cyan=#b3ffff -White=#ffffff diff --git a/Base/res/cursor-themes/Dark/Config.ini b/Base/res/cursor-themes/Dark/Config.ini deleted file mode 100644 index 703130b3aaa..00000000000 --- a/Base/res/cursor-themes/Dark/Config.ini +++ /dev/null @@ -1,20 +0,0 @@ -[Cursor] -Hidden=hidden.png -Arrow=arrow.x2y2.png -ResizeH=resize-horizontal.png -ResizeV=resize-vertical.png -ResizeDTLBR=resize-diagonal-tlbr.png -ResizeDBLTR=resize-diagonal-bltr.png -ResizeColumn=resize-column.png -ResizeRow=resize-row.png -IBeam=i-beam.png -Disallowed=disallowed.png -Move=move.png -Hand=hand.x8y4.png -Help=help.x1y1.png -Drag=drag.png -DragCopy=drag-copy.x7y7.png -Wait=wait.f14t100.png -Crosshair=crosshair.png -Eyedropper=eyedropper.x2y2.png -Zoom=zoom.x0y0.png \ No newline at end of file diff --git a/Base/res/cursor-themes/Dark/arrow.x2y2-2x.png b/Base/res/cursor-themes/Dark/arrow.x2y2-2x.png deleted file mode 100644 index 6a8d081302e..00000000000 Binary files a/Base/res/cursor-themes/Dark/arrow.x2y2-2x.png and /dev/null differ diff --git a/Base/res/cursor-themes/Dark/arrow.x2y2.png b/Base/res/cursor-themes/Dark/arrow.x2y2.png deleted file mode 100644 index 30b5e80f684..00000000000 Binary files a/Base/res/cursor-themes/Dark/arrow.x2y2.png and /dev/null differ diff --git a/Base/res/cursor-themes/Dark/crosshair-2x.png b/Base/res/cursor-themes/Dark/crosshair-2x.png deleted file mode 100644 index 83a0ccbdc09..00000000000 Binary files a/Base/res/cursor-themes/Dark/crosshair-2x.png and /dev/null differ diff --git a/Base/res/cursor-themes/Dark/crosshair.png b/Base/res/cursor-themes/Dark/crosshair.png deleted file mode 100644 index ad8673d1f64..00000000000 Binary files a/Base/res/cursor-themes/Dark/crosshair.png and /dev/null differ diff --git a/Base/res/cursor-themes/Dark/disallowed.png b/Base/res/cursor-themes/Dark/disallowed.png deleted file mode 100644 index c7357b9fbe3..00000000000 Binary files a/Base/res/cursor-themes/Dark/disallowed.png and /dev/null differ diff --git a/Base/res/cursor-themes/Dark/drag-2x.png b/Base/res/cursor-themes/Dark/drag-2x.png deleted file mode 100644 index 7fdff50f653..00000000000 Binary files a/Base/res/cursor-themes/Dark/drag-2x.png and /dev/null differ diff --git a/Base/res/cursor-themes/Dark/drag-copy.x7y7-2x.png b/Base/res/cursor-themes/Dark/drag-copy.x7y7-2x.png deleted file mode 100644 index a06e3126f26..00000000000 Binary files a/Base/res/cursor-themes/Dark/drag-copy.x7y7-2x.png and /dev/null differ diff --git a/Base/res/cursor-themes/Dark/drag-copy.x7y7.png b/Base/res/cursor-themes/Dark/drag-copy.x7y7.png deleted file mode 100644 index 559dcb85829..00000000000 Binary files a/Base/res/cursor-themes/Dark/drag-copy.x7y7.png and /dev/null differ diff --git a/Base/res/cursor-themes/Dark/drag.png b/Base/res/cursor-themes/Dark/drag.png deleted file mode 100644 index dc311a971fd..00000000000 Binary files a/Base/res/cursor-themes/Dark/drag.png and /dev/null differ diff --git a/Base/res/cursor-themes/Dark/eyedropper.x2y2.png b/Base/res/cursor-themes/Dark/eyedropper.x2y2.png deleted file mode 100644 index 2d3c67584da..00000000000 Binary files a/Base/res/cursor-themes/Dark/eyedropper.x2y2.png and /dev/null differ diff --git a/Base/res/cursor-themes/Dark/hand.x8y4.png b/Base/res/cursor-themes/Dark/hand.x8y4.png deleted file mode 100644 index 5597473eb8d..00000000000 Binary files a/Base/res/cursor-themes/Dark/hand.x8y4.png and /dev/null differ diff --git a/Base/res/cursor-themes/Dark/help.x1y1.png b/Base/res/cursor-themes/Dark/help.x1y1.png deleted file mode 100644 index c26b3fe2faa..00000000000 Binary files a/Base/res/cursor-themes/Dark/help.x1y1.png and /dev/null differ diff --git a/Base/res/cursor-themes/Dark/hidden.png b/Base/res/cursor-themes/Dark/hidden.png deleted file mode 100644 index 96382d8740a..00000000000 Binary files a/Base/res/cursor-themes/Dark/hidden.png and /dev/null differ diff --git a/Base/res/cursor-themes/Dark/i-beam.png b/Base/res/cursor-themes/Dark/i-beam.png deleted file mode 100644 index b7d92b22c16..00000000000 Binary files a/Base/res/cursor-themes/Dark/i-beam.png and /dev/null differ diff --git a/Base/res/cursor-themes/Dark/move-2x.png b/Base/res/cursor-themes/Dark/move-2x.png deleted file mode 100644 index 167fbb83e7b..00000000000 Binary files a/Base/res/cursor-themes/Dark/move-2x.png and /dev/null differ diff --git a/Base/res/cursor-themes/Dark/move.png b/Base/res/cursor-themes/Dark/move.png deleted file mode 100644 index 7cc7a64f968..00000000000 Binary files a/Base/res/cursor-themes/Dark/move.png and /dev/null differ diff --git a/Base/res/cursor-themes/Dark/resize-column-2x.png b/Base/res/cursor-themes/Dark/resize-column-2x.png deleted file mode 100644 index d498a513d27..00000000000 Binary files a/Base/res/cursor-themes/Dark/resize-column-2x.png and /dev/null differ diff --git a/Base/res/cursor-themes/Dark/resize-column.png b/Base/res/cursor-themes/Dark/resize-column.png deleted file mode 100644 index ab7897403dd..00000000000 Binary files a/Base/res/cursor-themes/Dark/resize-column.png and /dev/null differ diff --git a/Base/res/cursor-themes/Dark/resize-diagonal-bltr-2x.png b/Base/res/cursor-themes/Dark/resize-diagonal-bltr-2x.png deleted file mode 100644 index d020a4ffcd7..00000000000 Binary files a/Base/res/cursor-themes/Dark/resize-diagonal-bltr-2x.png and /dev/null differ diff --git a/Base/res/cursor-themes/Dark/resize-diagonal-bltr.png b/Base/res/cursor-themes/Dark/resize-diagonal-bltr.png deleted file mode 100644 index 0afe0ecb6b4..00000000000 Binary files a/Base/res/cursor-themes/Dark/resize-diagonal-bltr.png and /dev/null differ diff --git a/Base/res/cursor-themes/Dark/resize-diagonal-tlbr-2x.png b/Base/res/cursor-themes/Dark/resize-diagonal-tlbr-2x.png deleted file mode 100644 index fd42d42e67e..00000000000 Binary files a/Base/res/cursor-themes/Dark/resize-diagonal-tlbr-2x.png and /dev/null differ diff --git a/Base/res/cursor-themes/Dark/resize-diagonal-tlbr.png b/Base/res/cursor-themes/Dark/resize-diagonal-tlbr.png deleted file mode 100644 index dc4b0b82944..00000000000 Binary files a/Base/res/cursor-themes/Dark/resize-diagonal-tlbr.png and /dev/null differ diff --git a/Base/res/cursor-themes/Dark/resize-horizontal-2x.png b/Base/res/cursor-themes/Dark/resize-horizontal-2x.png deleted file mode 100644 index 5db9e9d7a96..00000000000 Binary files a/Base/res/cursor-themes/Dark/resize-horizontal-2x.png and /dev/null differ diff --git a/Base/res/cursor-themes/Dark/resize-horizontal.png b/Base/res/cursor-themes/Dark/resize-horizontal.png deleted file mode 100644 index 80b98691bb7..00000000000 Binary files a/Base/res/cursor-themes/Dark/resize-horizontal.png and /dev/null differ diff --git a/Base/res/cursor-themes/Dark/resize-row-2x.png b/Base/res/cursor-themes/Dark/resize-row-2x.png deleted file mode 100644 index 1b62829d7c0..00000000000 Binary files a/Base/res/cursor-themes/Dark/resize-row-2x.png and /dev/null differ diff --git a/Base/res/cursor-themes/Dark/resize-row.png b/Base/res/cursor-themes/Dark/resize-row.png deleted file mode 100644 index 7f1c226c773..00000000000 Binary files a/Base/res/cursor-themes/Dark/resize-row.png and /dev/null differ diff --git a/Base/res/cursor-themes/Dark/resize-vertical-2x.png b/Base/res/cursor-themes/Dark/resize-vertical-2x.png deleted file mode 100644 index 3be1baea303..00000000000 Binary files a/Base/res/cursor-themes/Dark/resize-vertical-2x.png and /dev/null differ diff --git a/Base/res/cursor-themes/Dark/resize-vertical.png b/Base/res/cursor-themes/Dark/resize-vertical.png deleted file mode 100644 index 1d3e1d2f3ce..00000000000 Binary files a/Base/res/cursor-themes/Dark/resize-vertical.png and /dev/null differ diff --git a/Base/res/cursor-themes/Dark/wait.f14t100.png b/Base/res/cursor-themes/Dark/wait.f14t100.png deleted file mode 100644 index d48441de981..00000000000 Binary files a/Base/res/cursor-themes/Dark/wait.f14t100.png and /dev/null differ diff --git a/Base/res/cursor-themes/Dark/zoom.x0y0.png b/Base/res/cursor-themes/Dark/zoom.x0y0.png deleted file mode 100644 index 0d5e7c481f1..00000000000 Binary files a/Base/res/cursor-themes/Dark/zoom.x0y0.png and /dev/null differ diff --git a/Base/res/cursor-themes/Default/Config.ini b/Base/res/cursor-themes/Default/Config.ini deleted file mode 100644 index 703130b3aaa..00000000000 --- a/Base/res/cursor-themes/Default/Config.ini +++ /dev/null @@ -1,20 +0,0 @@ -[Cursor] -Hidden=hidden.png -Arrow=arrow.x2y2.png -ResizeH=resize-horizontal.png -ResizeV=resize-vertical.png -ResizeDTLBR=resize-diagonal-tlbr.png -ResizeDBLTR=resize-diagonal-bltr.png -ResizeColumn=resize-column.png -ResizeRow=resize-row.png -IBeam=i-beam.png -Disallowed=disallowed.png -Move=move.png -Hand=hand.x8y4.png -Help=help.x1y1.png -Drag=drag.png -DragCopy=drag-copy.x7y7.png -Wait=wait.f14t100.png -Crosshair=crosshair.png -Eyedropper=eyedropper.x2y2.png -Zoom=zoom.x0y0.png \ No newline at end of file diff --git a/Base/res/cursor-themes/Default/arrow.x2y2-2x.png b/Base/res/cursor-themes/Default/arrow.x2y2-2x.png deleted file mode 100644 index 1874fee0c99..00000000000 Binary files a/Base/res/cursor-themes/Default/arrow.x2y2-2x.png and /dev/null differ diff --git a/Base/res/cursor-themes/Default/arrow.x2y2.png b/Base/res/cursor-themes/Default/arrow.x2y2.png deleted file mode 100644 index 8e124cc5839..00000000000 Binary files a/Base/res/cursor-themes/Default/arrow.x2y2.png and /dev/null differ diff --git a/Base/res/cursor-themes/Default/crosshair-2x.png b/Base/res/cursor-themes/Default/crosshair-2x.png deleted file mode 100644 index f1abec7a341..00000000000 Binary files a/Base/res/cursor-themes/Default/crosshair-2x.png and /dev/null differ diff --git a/Base/res/cursor-themes/Default/crosshair.png b/Base/res/cursor-themes/Default/crosshair.png deleted file mode 100644 index 7f1d12a4c98..00000000000 Binary files a/Base/res/cursor-themes/Default/crosshair.png and /dev/null differ diff --git a/Base/res/cursor-themes/Default/disallowed-2x.png b/Base/res/cursor-themes/Default/disallowed-2x.png deleted file mode 100644 index 4b188bafc74..00000000000 Binary files a/Base/res/cursor-themes/Default/disallowed-2x.png and /dev/null differ diff --git a/Base/res/cursor-themes/Default/disallowed.png b/Base/res/cursor-themes/Default/disallowed.png deleted file mode 100644 index 5d7d5f26cd4..00000000000 Binary files a/Base/res/cursor-themes/Default/disallowed.png and /dev/null differ diff --git a/Base/res/cursor-themes/Default/drag-2x.png b/Base/res/cursor-themes/Default/drag-2x.png deleted file mode 100644 index 334f4773dab..00000000000 Binary files a/Base/res/cursor-themes/Default/drag-2x.png and /dev/null differ diff --git a/Base/res/cursor-themes/Default/drag-copy.x7y7-2x.png b/Base/res/cursor-themes/Default/drag-copy.x7y7-2x.png deleted file mode 100644 index 2957b445e38..00000000000 Binary files a/Base/res/cursor-themes/Default/drag-copy.x7y7-2x.png and /dev/null differ diff --git a/Base/res/cursor-themes/Default/drag-copy.x7y7.png b/Base/res/cursor-themes/Default/drag-copy.x7y7.png deleted file mode 100644 index 4715b785736..00000000000 Binary files a/Base/res/cursor-themes/Default/drag-copy.x7y7.png and /dev/null differ diff --git a/Base/res/cursor-themes/Default/drag.png b/Base/res/cursor-themes/Default/drag.png deleted file mode 100644 index b89032b4732..00000000000 Binary files a/Base/res/cursor-themes/Default/drag.png and /dev/null differ diff --git a/Base/res/cursor-themes/Default/eyedropper.x2y2-2x.png b/Base/res/cursor-themes/Default/eyedropper.x2y2-2x.png deleted file mode 100644 index 625676904e4..00000000000 Binary files a/Base/res/cursor-themes/Default/eyedropper.x2y2-2x.png and /dev/null differ diff --git a/Base/res/cursor-themes/Default/eyedropper.x2y2.png b/Base/res/cursor-themes/Default/eyedropper.x2y2.png deleted file mode 100644 index cd6b4c6bb6c..00000000000 Binary files a/Base/res/cursor-themes/Default/eyedropper.x2y2.png and /dev/null differ diff --git a/Base/res/cursor-themes/Default/hand.x8y4.png b/Base/res/cursor-themes/Default/hand.x8y4.png deleted file mode 100644 index 924386ef67d..00000000000 Binary files a/Base/res/cursor-themes/Default/hand.x8y4.png and /dev/null differ diff --git a/Base/res/cursor-themes/Default/help.x1y1.png b/Base/res/cursor-themes/Default/help.x1y1.png deleted file mode 100644 index c14c29d4530..00000000000 Binary files a/Base/res/cursor-themes/Default/help.x1y1.png and /dev/null differ diff --git a/Base/res/cursor-themes/Default/hidden.png b/Base/res/cursor-themes/Default/hidden.png deleted file mode 100644 index 96382d8740a..00000000000 Binary files a/Base/res/cursor-themes/Default/hidden.png and /dev/null differ diff --git a/Base/res/cursor-themes/Default/i-beam-2x.png b/Base/res/cursor-themes/Default/i-beam-2x.png deleted file mode 100644 index 79d1e6be1e5..00000000000 Binary files a/Base/res/cursor-themes/Default/i-beam-2x.png and /dev/null differ diff --git a/Base/res/cursor-themes/Default/i-beam.png b/Base/res/cursor-themes/Default/i-beam.png deleted file mode 100644 index a0b420d97d4..00000000000 Binary files a/Base/res/cursor-themes/Default/i-beam.png and /dev/null differ diff --git a/Base/res/cursor-themes/Default/move-2x.png b/Base/res/cursor-themes/Default/move-2x.png deleted file mode 100644 index 867ad89ff12..00000000000 Binary files a/Base/res/cursor-themes/Default/move-2x.png and /dev/null differ diff --git a/Base/res/cursor-themes/Default/move.png b/Base/res/cursor-themes/Default/move.png deleted file mode 100644 index 68c8f7c694e..00000000000 Binary files a/Base/res/cursor-themes/Default/move.png and /dev/null differ diff --git a/Base/res/cursor-themes/Default/resize-column-2x.png b/Base/res/cursor-themes/Default/resize-column-2x.png deleted file mode 100644 index 96b2f3ccb35..00000000000 Binary files a/Base/res/cursor-themes/Default/resize-column-2x.png and /dev/null differ diff --git a/Base/res/cursor-themes/Default/resize-column.png b/Base/res/cursor-themes/Default/resize-column.png deleted file mode 100644 index 6316a078935..00000000000 Binary files a/Base/res/cursor-themes/Default/resize-column.png and /dev/null differ diff --git a/Base/res/cursor-themes/Default/resize-diagonal-bltr-2x.png b/Base/res/cursor-themes/Default/resize-diagonal-bltr-2x.png deleted file mode 100644 index 977a92bb7e0..00000000000 Binary files a/Base/res/cursor-themes/Default/resize-diagonal-bltr-2x.png and /dev/null differ diff --git a/Base/res/cursor-themes/Default/resize-diagonal-bltr.png b/Base/res/cursor-themes/Default/resize-diagonal-bltr.png deleted file mode 100644 index 9a6fc76681f..00000000000 Binary files a/Base/res/cursor-themes/Default/resize-diagonal-bltr.png and /dev/null differ diff --git a/Base/res/cursor-themes/Default/resize-diagonal-tlbr-2x.png b/Base/res/cursor-themes/Default/resize-diagonal-tlbr-2x.png deleted file mode 100644 index 06eebf8ca4d..00000000000 Binary files a/Base/res/cursor-themes/Default/resize-diagonal-tlbr-2x.png and /dev/null differ diff --git a/Base/res/cursor-themes/Default/resize-diagonal-tlbr.png b/Base/res/cursor-themes/Default/resize-diagonal-tlbr.png deleted file mode 100644 index 2c51df5bfb7..00000000000 Binary files a/Base/res/cursor-themes/Default/resize-diagonal-tlbr.png and /dev/null differ diff --git a/Base/res/cursor-themes/Default/resize-horizontal-2x.png b/Base/res/cursor-themes/Default/resize-horizontal-2x.png deleted file mode 100644 index 39df5907362..00000000000 Binary files a/Base/res/cursor-themes/Default/resize-horizontal-2x.png and /dev/null differ diff --git a/Base/res/cursor-themes/Default/resize-horizontal.png b/Base/res/cursor-themes/Default/resize-horizontal.png deleted file mode 100644 index e6b714f736c..00000000000 Binary files a/Base/res/cursor-themes/Default/resize-horizontal.png and /dev/null differ diff --git a/Base/res/cursor-themes/Default/resize-row-2x.png b/Base/res/cursor-themes/Default/resize-row-2x.png deleted file mode 100644 index 0fa4c2ece96..00000000000 Binary files a/Base/res/cursor-themes/Default/resize-row-2x.png and /dev/null differ diff --git a/Base/res/cursor-themes/Default/resize-row.png b/Base/res/cursor-themes/Default/resize-row.png deleted file mode 100644 index e73b9c62a0b..00000000000 Binary files a/Base/res/cursor-themes/Default/resize-row.png and /dev/null differ diff --git a/Base/res/cursor-themes/Default/resize-vertical-2x.png b/Base/res/cursor-themes/Default/resize-vertical-2x.png deleted file mode 100644 index 2867f304047..00000000000 Binary files a/Base/res/cursor-themes/Default/resize-vertical-2x.png and /dev/null differ diff --git a/Base/res/cursor-themes/Default/resize-vertical.png b/Base/res/cursor-themes/Default/resize-vertical.png deleted file mode 100644 index a0b493ce8ce..00000000000 Binary files a/Base/res/cursor-themes/Default/resize-vertical.png and /dev/null differ diff --git a/Base/res/cursor-themes/Default/wait.f14t100-2x.png b/Base/res/cursor-themes/Default/wait.f14t100-2x.png deleted file mode 100644 index 0282ba4e8bb..00000000000 Binary files a/Base/res/cursor-themes/Default/wait.f14t100-2x.png and /dev/null differ diff --git a/Base/res/cursor-themes/Default/wait.f14t100.png b/Base/res/cursor-themes/Default/wait.f14t100.png deleted file mode 100644 index 00e29b9959d..00000000000 Binary files a/Base/res/cursor-themes/Default/wait.f14t100.png and /dev/null differ diff --git a/Base/res/cursor-themes/Default/zoom.x0y0-2x.png b/Base/res/cursor-themes/Default/zoom.x0y0-2x.png deleted file mode 100644 index 315bb98a71a..00000000000 Binary files a/Base/res/cursor-themes/Default/zoom.x0y0-2x.png and /dev/null differ diff --git a/Base/res/cursor-themes/Default/zoom.x0y0.png b/Base/res/cursor-themes/Default/zoom.x0y0.png deleted file mode 100644 index 8a6fdd810bc..00000000000 Binary files a/Base/res/cursor-themes/Default/zoom.x0y0.png and /dev/null differ diff --git a/Base/res/devel/templates/cpp-basic.ini b/Base/res/devel/templates/cpp-basic.ini deleted file mode 100644 index da86eb8f055..00000000000 --- a/Base/res/devel/templates/cpp-basic.ini +++ /dev/null @@ -1,5 +0,0 @@ -[HackStudioTemplate] -Name=Command-line Application (C++) -Description=Template for creating a basic C++ command-line application. -Priority=95 -IconName32x=cpp-basic diff --git a/Base/res/devel/templates/cpp-basic.postcreate b/Base/res/devel/templates/cpp-basic.postcreate deleted file mode 100644 index 30ade75a286..00000000000 --- a/Base/res/devel/templates/cpp-basic.postcreate +++ /dev/null @@ -1,21 +0,0 @@ -#!/bin/Shell - -echo > $2/Makefile <<-EOF -PROGRAM = $1 -OBJS = main.o -CXXFLAGS = -g -std=c++23 - -all: \$(PROGRAM) - -\$(PROGRAM): \$(OBJS) - \$(CXX) -o \$@ \$(OBJS) -lmain - -%.o: %.cpp - \$(CXX) \$(CXXFLAGS) -o \$@ -c \$< - -clean: - rm \$(OBJS) \$(PROGRAM) - -run: - ./\$(PROGRAM) -EOF diff --git a/Base/res/devel/templates/cpp-basic/main.cpp b/Base/res/devel/templates/cpp-basic/main.cpp deleted file mode 100644 index 0d35edce287..00000000000 --- a/Base/res/devel/templates/cpp-basic/main.cpp +++ /dev/null @@ -1,24 +0,0 @@ -#include -#include -#include -#include -#include - -ErrorOr serenity_main(Main::Arguments arguments) -{ - int hello_count = 1; - StringView epilog; - - Core::ArgsParser parser; - parser.add_option(hello_count, "How often to print \"Hello friends!\"", "count", 'c', "hello-count"); - parser.add_positional_argument(epilog, "What to print at the end", "epilog", Core::ArgsParser::Required::No); - parser.parse(arguments); - - for (auto i = 0; i < hello_count; ++i) - outln("Hello friends!"); - - if (!epilog.is_empty()) - outln("And finally: {}", epilog); - - return 0; -} diff --git a/Base/res/devel/templates/cpp-library.ini b/Base/res/devel/templates/cpp-library.ini deleted file mode 100644 index fd657bb6d70..00000000000 --- a/Base/res/devel/templates/cpp-library.ini +++ /dev/null @@ -1,4 +0,0 @@ -[HackStudioTemplate] -Name=Shared Library (C++) -Description=Template for creating a C++ shared library. -IconName32x=cpp-library diff --git a/Base/res/devel/templates/cpp-library.postcreate b/Base/res/devel/templates/cpp-library.postcreate deleted file mode 100644 index e1ab5d51d19..00000000000 --- a/Base/res/devel/templates/cpp-library.postcreate +++ /dev/null @@ -1,28 +0,0 @@ -#!/bin/Shell - -# $1: Project name, filesystem safe -# $2: Project full path -# $3: Project name, namespace safe - -# FIXME: Use a single sed command once we support that. -sed -i "s/\\\$LibName/$3/g" $2/Class1.h -sed -i "s/\\\$LibName/$3/g" $2/Class1.cpp - -# Generate Makefile -echo > $2/Makefile <<-EOF -LIBRARY = $1.so -OBJS = Class1.o -CXXFLAGS = -g -std=c++23 - -all: \$(LIBRARY) - -\$(LIBRARY): \$(OBJS) - \$(CXX) -shared -o \$@ \$(OBJS) - -%.o: %.cpp - \$(CXX) \$(CXXFLAGS) -fPIC -o \$@ -c \$< - -clean: - rm \$(OBJS) \$(LIBRARY) - -EOF diff --git a/Base/res/devel/templates/cpp-library/Class1.cpp b/Base/res/devel/templates/cpp-library/Class1.cpp deleted file mode 100644 index b858db00829..00000000000 --- a/Base/res/devel/templates/cpp-library/Class1.cpp +++ /dev/null @@ -1,11 +0,0 @@ -#include "Class1.h" -#include - -namespace $LibName { - -void Class1::hello() -{ - out("Hello friends! :^)\n"); -} - -} diff --git a/Base/res/devel/templates/cpp-library/Class1.h b/Base/res/devel/templates/cpp-library/Class1.h deleted file mode 100644 index 587c9623e22..00000000000 --- a/Base/res/devel/templates/cpp-library/Class1.h +++ /dev/null @@ -1,10 +0,0 @@ -#pragma once - -namespace $LibName { - -class Class1 { -public: - void hello(); -}; - -} diff --git a/Base/res/devel/templates/empty.ini b/Base/res/devel/templates/empty.ini deleted file mode 100644 index 4c32529c0b8..00000000000 --- a/Base/res/devel/templates/empty.ini +++ /dev/null @@ -1,5 +0,0 @@ -[HackStudioTemplate] -Name=Empty Project -Description=Template for creating an empty project with no files. -Priority=100 -IconName32x=empty diff --git a/Base/res/devel/templates/serenity-application.ini b/Base/res/devel/templates/serenity-application.ini deleted file mode 100644 index fe3a281bc05..00000000000 --- a/Base/res/devel/templates/serenity-application.ini +++ /dev/null @@ -1,5 +0,0 @@ -[HackStudioTemplate] -Name=SerenityOS GUI Application (C++) -Description=Template for creating a GUI-based SerenityOS application with CMake. -Priority=90 -IconName32x=serenity-application diff --git a/Base/res/devel/templates/serenity-application.postcreate b/Base/res/devel/templates/serenity-application.postcreate deleted file mode 100644 index 9baf09a9410..00000000000 --- a/Base/res/devel/templates/serenity-application.postcreate +++ /dev/null @@ -1,29 +0,0 @@ -#!/bin/Shell - -echo > $2/CMakeLists.txt <<-EOF -# NOTE! Make sure to edit this file and remove the comments before submitting a -# PR for a new application. - -# Defines your application component. If the application is essential to the -# system's operation, add REQUIRED. If it would be beneficial for the user that -# this application is built by default, add RECOMMENDED. -serenity_component( - $1 - TARGETS $1 -) - -# Place source files here. You should also add auto-generated headers, if you -# have any. -set(SOURCES - main.cpp -) - -# Change this to something cool. :^) -serenity_app($1 ICON filetype-executable) - -# You should place all the libraries that your application uses here. You can -# identify each library by their include path. An exception is LibCore, which -# is linked to all components by default. -target_link_libraries($1 LibGUI LibGfx LibMain) - -EOF diff --git a/Base/res/devel/templates/serenity-application/main.cpp b/Base/res/devel/templates/serenity-application/main.cpp deleted file mode 100644 index a7a2efdf907..00000000000 --- a/Base/res/devel/templates/serenity-application/main.cpp +++ /dev/null @@ -1,34 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include - -ErrorOr serenity_main(Main::Arguments arguments) -{ - TRY(Core::System::pledge("stdio recvfd sendfd rpath wpath cpath unix")); - - auto app = TRY(GUI::Application::create(arguments)); - - TRY(Core::System::pledge("stdio recvfd sendfd rpath")); - - auto window = GUI::Window::construct(); - window->set_title("Example Application"); - window->resize(200, 200); - window->set_resizable(false); - - auto main_widget = window->set_main_widget(); - main_widget->set_fill_with_background_color(true); - - main_widget->set_layout(16); - - auto& button = main_widget->add("Click me!"_string); - button.on_click = [&](auto) { - GUI::MessageBox::show(window, "Hello friends!"sv, ":^)"sv); - }; - - window->show(); - return app->exec(); -} diff --git a/Base/res/fonts/AtaraxiaBold10.font b/Base/res/fonts/AtaraxiaBold10.font deleted file mode 100644 index 7d431a1e507..00000000000 Binary files a/Base/res/fonts/AtaraxiaBold10.font and /dev/null differ diff --git a/Base/res/fonts/AtaraxiaBold8.font b/Base/res/fonts/AtaraxiaBold8.font deleted file mode 100644 index 751df2d2a68..00000000000 Binary files a/Base/res/fonts/AtaraxiaBold8.font and /dev/null differ diff --git a/Base/res/fonts/AtaraxiaLight10.font b/Base/res/fonts/AtaraxiaLight10.font deleted file mode 100644 index ad9ac63d317..00000000000 Binary files a/Base/res/fonts/AtaraxiaLight10.font and /dev/null differ diff --git a/Base/res/fonts/AtaraxiaLight8.font b/Base/res/fonts/AtaraxiaLight8.font deleted file mode 100644 index dc858d85962..00000000000 Binary files a/Base/res/fonts/AtaraxiaLight8.font and /dev/null differ diff --git a/Base/res/fonts/CJKBiángRegular36.font b/Base/res/fonts/CJKBiángRegular36.font deleted file mode 100644 index 73911515390..00000000000 Binary files a/Base/res/fonts/CJKBiángRegular36.font and /dev/null differ diff --git a/Base/res/fonts/CathodeRegular10.font b/Base/res/fonts/CathodeRegular10.font deleted file mode 100644 index 6b8f5dea32c..00000000000 Binary files a/Base/res/fonts/CathodeRegular10.font and /dev/null differ diff --git a/Base/res/fonts/LizaBlack10.font b/Base/res/fonts/LizaBlack10.font deleted file mode 100644 index f438d7616f1..00000000000 Binary files a/Base/res/fonts/LizaBlack10.font and /dev/null differ diff --git a/Base/res/fonts/LizaBlack24.font b/Base/res/fonts/LizaBlack24.font deleted file mode 100644 index 986bddae933..00000000000 Binary files a/Base/res/fonts/LizaBlack24.font and /dev/null differ diff --git a/Base/res/fonts/LizaBlack36.font b/Base/res/fonts/LizaBlack36.font deleted file mode 100644 index 0d6dbbec59e..00000000000 Binary files a/Base/res/fonts/LizaBlack36.font and /dev/null differ diff --git a/Base/res/fonts/LizaBold10.font b/Base/res/fonts/LizaBold10.font deleted file mode 100644 index 3efd5807a54..00000000000 Binary files a/Base/res/fonts/LizaBold10.font and /dev/null differ diff --git a/Base/res/fonts/LizaBold24.font b/Base/res/fonts/LizaBold24.font deleted file mode 100644 index 55730599470..00000000000 Binary files a/Base/res/fonts/LizaBold24.font and /dev/null differ diff --git a/Base/res/fonts/LizaBold36.font b/Base/res/fonts/LizaBold36.font deleted file mode 100644 index 77b144c46e2..00000000000 Binary files a/Base/res/fonts/LizaBold36.font and /dev/null differ diff --git a/Base/res/fonts/LizaRegular10.font b/Base/res/fonts/LizaRegular10.font deleted file mode 100644 index c89a53ac560..00000000000 Binary files a/Base/res/fonts/LizaRegular10.font and /dev/null differ diff --git a/Base/res/fonts/LizaRegular24.font b/Base/res/fonts/LizaRegular24.font deleted file mode 100644 index 4760b48a950..00000000000 Binary files a/Base/res/fonts/LizaRegular24.font and /dev/null differ diff --git a/Base/res/fonts/LizaRegular36.font b/Base/res/fonts/LizaRegular36.font deleted file mode 100644 index a1aaf766859..00000000000 Binary files a/Base/res/fonts/LizaRegular36.font and /dev/null differ diff --git a/Base/res/fonts/LucidityBold12.font b/Base/res/fonts/LucidityBold12.font deleted file mode 100644 index ade8a9a2710..00000000000 Binary files a/Base/res/fonts/LucidityBold12.font and /dev/null differ diff --git a/Base/res/fonts/LucidityRegular12.font b/Base/res/fonts/LucidityRegular12.font deleted file mode 100644 index b83d10779b9..00000000000 Binary files a/Base/res/fonts/LucidityRegular12.font and /dev/null differ diff --git a/Base/res/fonts/MarietaBold24.font b/Base/res/fonts/MarietaBold24.font deleted file mode 100644 index 766128ab1c2..00000000000 Binary files a/Base/res/fonts/MarietaBold24.font and /dev/null differ diff --git a/Base/res/fonts/MarietaBold36.font b/Base/res/fonts/MarietaBold36.font deleted file mode 100644 index 164d57abf8c..00000000000 Binary files a/Base/res/fonts/MarietaBold36.font and /dev/null differ diff --git a/Base/res/fonts/MarietaRegular24.font b/Base/res/fonts/MarietaRegular24.font deleted file mode 100644 index 72c20f04641..00000000000 Binary files a/Base/res/fonts/MarietaRegular24.font and /dev/null differ diff --git a/Base/res/fonts/MarietaRegular36.font b/Base/res/fonts/MarietaRegular36.font deleted file mode 100644 index 92fd18e5a1c..00000000000 Binary files a/Base/res/fonts/MarietaRegular36.font and /dev/null differ diff --git a/Base/res/fonts/PebbletonBold14.font b/Base/res/fonts/PebbletonBold14.font deleted file mode 100644 index 8dcbe493112..00000000000 Binary files a/Base/res/fonts/PebbletonBold14.font and /dev/null differ diff --git a/Base/res/fonts/PebbletonRegular14.font b/Base/res/fonts/PebbletonRegular14.font deleted file mode 100644 index 9d899148f86..00000000000 Binary files a/Base/res/fonts/PebbletonRegular14.font and /dev/null differ diff --git a/Base/res/fonts/RomanItalic10.font b/Base/res/fonts/RomanItalic10.font deleted file mode 100644 index 83e9ff0a124..00000000000 Binary files a/Base/res/fonts/RomanItalic10.font and /dev/null differ diff --git a/Base/res/fonts/RomanItalic8.font b/Base/res/fonts/RomanItalic8.font deleted file mode 100644 index a9031f4ecdc..00000000000 Binary files a/Base/res/fonts/RomanItalic8.font and /dev/null differ diff --git a/Base/res/fonts/RomanRegular10.font b/Base/res/fonts/RomanRegular10.font deleted file mode 100644 index 3dc1fd9fc18..00000000000 Binary files a/Base/res/fonts/RomanRegular10.font and /dev/null differ diff --git a/Base/res/fonts/SatoriBold10.font b/Base/res/fonts/SatoriBold10.font deleted file mode 100644 index 99a24371c6d..00000000000 Binary files a/Base/res/fonts/SatoriBold10.font and /dev/null differ diff --git a/Base/res/fonts/SatoriMonoBold10.font b/Base/res/fonts/SatoriMonoBold10.font deleted file mode 100644 index cb55de82a57..00000000000 Binary files a/Base/res/fonts/SatoriMonoBold10.font and /dev/null differ diff --git a/Base/res/fonts/SatoriMonoRegular10.font b/Base/res/fonts/SatoriMonoRegular10.font deleted file mode 100644 index 46d2ffd3611..00000000000 Binary files a/Base/res/fonts/SatoriMonoRegular10.font and /dev/null differ diff --git a/Base/res/fonts/SatoriRegular10.font b/Base/res/fonts/SatoriRegular10.font deleted file mode 100644 index 3974f593f23..00000000000 Binary files a/Base/res/fonts/SatoriRegular10.font and /dev/null differ diff --git a/Base/res/fonts/SourceBold10.font b/Base/res/fonts/SourceBold10.font deleted file mode 100644 index f8bc63828a0..00000000000 Binary files a/Base/res/fonts/SourceBold10.font and /dev/null differ diff --git a/Base/res/fonts/SourceItalic10.font b/Base/res/fonts/SourceItalic10.font deleted file mode 100644 index b18e3d4e6bd..00000000000 Binary files a/Base/res/fonts/SourceItalic10.font and /dev/null differ diff --git a/Base/res/fonts/SourceRegular10.font b/Base/res/fonts/SourceRegular10.font deleted file mode 100644 index 223647809be..00000000000 Binary files a/Base/res/fonts/SourceRegular10.font and /dev/null differ diff --git a/Base/res/fonts/TengchahRegular12.font b/Base/res/fonts/TengchahRegular12.font deleted file mode 100644 index b28198e42ee..00000000000 Binary files a/Base/res/fonts/TengchahRegular12.font and /dev/null differ diff --git a/Base/res/fonts/TinierRegular5.font b/Base/res/fonts/TinierRegular5.font deleted file mode 100644 index fb2d56f3a51..00000000000 Binary files a/Base/res/fonts/TinierRegular5.font and /dev/null differ diff --git a/Base/res/fonts/TinyRegular6.font b/Base/res/fonts/TinyRegular6.font deleted file mode 100644 index 96b0115f61d..00000000000 Binary files a/Base/res/fonts/TinyRegular6.font and /dev/null differ diff --git a/Base/res/fortunes.json b/Base/res/fortunes.json deleted file mode 100644 index 53865a62b9c..00000000000 --- a/Base/res/fortunes.json +++ /dev/null @@ -1,329 +0,0 @@ -[ - { - "quote": "add some quotes, problem solved?", - "author": "linusg", - "utc_time": 1596660175, - "url": "https://benwiederhake.github.io/freenode-serenity-archive/quote-83277.html", - "context": "About a different kind of quote, but that's good enough for me! :)" - }, - { - "quote": "BenW: i'm sure it's fine lol", - "author": "kling", - "utc_time": 1605364559, - "url": "https://benwiederhake.github.io/freenode-serenity-archive/quote-100995.html" - }, - { - "quote": "dammit fuzzer, what madman would write '?~x/'?", - "author": "CxByte", - "utc_time": 1606653374, - "url": "https://benwiederhake.github.io/freenode-serenity-archive/quote-104142.html", - "context": "Fuzzers are even worse than users." - }, - { - "quote": "I'd totally implement that", - "author": "nooga", - "utc_time": 1611335335, - "url": "https://benwiederhake.github.io/freenode-serenity-archive/quote-119659.html" - }, - { - "quote": "No need to dereference the nullptr!", - "author": "linusg", - "utc_time": 1612014120, - "url": "https://github.com/SerenityOS/serenity/commit/5b43419a636699d71752de7cec91f6eb35ed50b5" - }, - { - "quote": "We can't not have the quotes under VC, that's a sin", - "author": "CxByte", - "utc_time": 1612035713, - "url": "https://benwiederhake.github.io/freenode-serenity-archive/quote-122663.html", - "context": "A quote about putting quotes in VC, in VC." - }, - { - "quote": "\"We really should start a \\\"Quotes\\\" page.\" - BenW", - "author": "kling", - "utc_time": 1612900961, - "url": "https://benwiederhake.github.io/freenode-serenity-archive/quote-125770.html", - "context": "Apparently I said that once too often." - }, - { - "quote": "if your port uses textrels, it likely uses other things that we don't want near serenity", - "author": "thakis", - "utc_time": 1612900918, - "url": "https://benwiederhake.github.io/freenode-serenity-archive/quote-125768.html", - "context": "\"jk but only a little bit jk\"" - }, - { - "quote": "I'm afraid of where pulling that string will take me.", - "author": "boricj", - "utc_time": 1612954860, - "url": "https://benwiederhake.github.io/freenode-serenity-archive/quote-125894.html", - "context": "C++ templates will lead you down the rabbithole." - }, - { - "quote": "$commitdescription ($user opened: The build failed.)", - "author": "SerenityBot", - "utc_time": 1613906220, - "url": "https://benwiederhake.github.io/freenode-serenity-archive/quote-128669.html", - "context": "The IRC notifications are a little bit harsh sometimes, especially if they all seem to spell failure." - }, - { - "quote": "There cannot be more unused bits than the entirety of the input.", - "author": "CxByte", - "utc_time": 1615188240, - "url": "https://github.com/SerenityOS/serenity/pull/5692#issue-586526857" - }, - { - "quote": "Does your code contain unexpected integer overflow? of course it does! contact BenW to find out why!", - "author": "CxByte", - "utc_time": 1615228585, - "url": "https://benwiederhake.github.io/freenode-serenity-archive/quote-132827.html", - "context": "Overflow-correct code is deviously hard. https://github.com/SerenityOS/serenity/commit/183b2e71ba8d85293db493cab27b8adb4af54981" - }, - { - "quote": "clang-format formatted your JSON file? Call Brian at bgianf@serenityos.org today! *Terms and conditions apply", - "author": "Andrew Kaster", - "utc_time": 1619133730, - "url": "https://discord.com/channels/830522505605283862/830739873119207426/834932114125488149" - }, - { - "quote": "we started with dialup and \"web 2.0\" and ended up with touchscreen smartphones sucking broadband out of thin air", - "author": "nooga", - "url": "https://discord.com/channels/830522505605283862/830525235803586570/864277610510549002", - "utc_time": 1626130562 - }, - { - "quote": "1.28 GiB worth of hash table is a lot of hash table", - "author": "awesomekling", - "url": "https://discord.com/channels/830522505605283862/830739873119207426/870071227929202738", - "utc_time": 1627511606 - }, - { - "quote": "Changelog: All boogs fixed, thar be no moar boogs", - "author": "CxByte", - "utc_time": 1627859603, - "url": "https://github.com/SerenityOS/serenity/pull/9127#issuecomment-890603297" - }, - { - "quote": "i'm here to influencer-marknadsföring and mjukvaruutveckling, and i'm all done utveckling the mjukvaru", - "author": "thakis", - "url": "https://discord.com/channels/830522505605283862/859531354219872266/877267999940292660", - "utc_time": 1629242612 - }, - { - "quote": "\"Is there an ISO of Serenity?\" – \"No, because they charge money, and because it's not a spec.\" – \"… what?!\"", - "author": "BenW", - "url": "https://discord.com/channels/830522505605283862/859531354219872266/889222244218327130", - "utc_time": 1632077460 - }, - { - "quote": "You reached the end of the internet, turn around", - "author": "Hendiadyoin", - "url": "https://discord.com/channels/830522505605283862/830525031720943627/876969458432884766", - "utc_time": 1629156212, - "context": "An interesting Browser bug. https://discord.com/channels/830522505605283862/830525031720943627/876966237077389352" - }, - { - "quote": "Widgets all inherit from Object, which has the methods for dealing with children", - "author": "AtkinsSJ", - "url": "https://discord.com/channels/830522505605283862/830529037251641374/882936925739171881", - "utc_time": 1630578967 - }, - { - "quote": "slaps roof of PR queue\nThis baby can hold so many e̴̾̓l̷͑͑d̸̉̚r̵̅͂ï̷̀t̸͗̉c̵̾͝h̶̋͗ ̸̲̚h̸͒́ȱ̸r̶̛̄ȓ̶͌o̶͐͘r̴̲͝s̴̐͆", - "author": "Andrew Kaster", - "url": "https://discord.com/channels/830522505605283862/830525235803586570/922976701032562738", - "utc_time": 1640125193 - }, - { - "quote": "Do you like bugs? yeah, me neither, so I fixed one:", - "author": "linusg", - "url": "https://discord.com/channels/830522505605283862/859531354219872266/922965066423341066", - "utc_time": 1640122419 - }, - { - "quote": "why is german", - "author": "Agni", - "url": "https://discord.com/channels/830522505605283862/830525235803586570/917781310766993408", - "utc_time": 1638886516 - }, - { - "quote": "> Lasciate ogni speranza, voi ch'entrate!\n> Abandon hope all ye who enter here\n~ Dante", - "author": "Hendiadyoin", - "url": "https://discord.com/channels/830522505605283862/830522505605283866/917781369084596284", - "utc_time": 1638886529, - "context": "Multithreaded IPC. https://discord.com/channels/830522505605283862/830522505605283866/917780454298484806" - }, - { - "quote": "yakcoin is powered by proof of commits\neverytime you shave a yak you earn one yakcoin", - "author": "Agni", - "url": "https://discord.com/channels/830522505605283862/830525235803586570/849632952089837599", - "utc_time": 1622638681 - }, - { - "quote": "tootlips", - "author": "awesomekling", - "url": "https://discord.com/channels/830522505605283862/830739873119207426/895421091856666685", - "utc_time": 1633555424, - "context": "https://discord.com/channels/830522505605283862/830739873119207426/895421023833436160" - }, - { - "quote": "and then the firebug nation attacked", - "author": "gggggg", - "url": "https://discord.com/channels/830522505605283862/830525031720943627/893862331312902166", - "utc_time": 1633183786 - }, - { - "quote": "those are some hefty meatballs", - "author": "awesomekling", - "url": "https://discord.com/channels/830522505605283862/830739873119207426/882756655018803311", - "utc_time": 1630535987 - }, - { - "quote": "not a shell boog this time", - "author": "Agni", - "url": "https://discord.com/channels/830522505605283862/859531354219872266/882660065474146304", - "utc_time": 1630512958 - }, - { - "quote": "ignore the spec", - "author": "linusg", - "url": "https://discord.com/channels/830522505605283862/851522357734408232/939601822941843476", - "utc_time": 1644088920, - "context": "Overly complicated, evidently abandoned proposals can just be ignored" - }, - { - "quote": "'y u capture by ref?' - 'reason: am idiot'", - "author": "CxByte, awesomekling", - "url": "https://discord.com/channels/830522505605283862/831544568767578162/935533306873774140", - "context": "'these frame_index/frame_count thingies are stack values'", - "utc_time": 1643118899 - }, - { - "quote": "I mean I'm not a rug specialist, as unbelievable as that might sound", - "author": "CxByte", - "url": "https://discord.com/channels/830522505605283862/831544568767578162/944744540793864202", - "utc_time": 1645315020 - }, - { - "quote": "omg, guys, we're making ground-breaking progress here.", - "author": "hik", - "url": "https://discord.com/channels/830522505605283862/945032683560517722/945040411318165516", - "utc_time": 1645385592 - }, - { - "quote": "FANTASTIC ISSUE, I R8 8/8 M8", - "author": "CxByte", - "url": "https://discord.com/channels/830522505605283862/831544568767578162/956270720407699456", - "utc_time": 1648063106, - "context": "Misunderstanding of the \"good first issue\" tag" - }, - { - "quote": "my problems are imaginary!! wohoo!!", - "author": "awesomekling", - "url": "https://discord.com/channels/830522505605283862/830525031720943627/959405975339696168", - "utc_time": 1648810617 - }, - { - "quote": "do you want sonar cloud's eyes to bleed", - "author": "Andrew Kaster", - "url": "https://discord.com/channels/830522505605283862/831544568767578162/959529719789928551", - "utc_time": 1648840112, - "context": "Nobody cares about the formatting of generated code, except SonarCloud." - }, - { - "quote": "science man make picture of bright light", - "author": "Andrew Kaster", - "url": "https://discord.com/channels/830522505605283862/831544568767578162/959570790393733160", - "utc_time": 1648849904, - "context": "Explaining state-of-the-art astronomy research" - }, - { - "quote": "I think a Monte Carlo simulation is where your French Jupityr notebook is falsely accused of treason, arrested without trial, is jailed on an island, escapes, and then returns to Paris to avenge its wrongful imprisonment with a bunch of RAM", - "author": "Andrew Kaster", - "url": "https://discord.com/channels/830522505605283862/831544568767578162/959571489076690994", - "utc_time": 1648850070 - }, - { - "quote": "The Border Radii Trilogy: Book Three: Box-Shadows", - "author": "MacDue", - "url": "https://github.com/SerenityOS/serenity/pull/14343", - "utc_time": 1655726258, - "context": "We're not quite sure where the first two books are, but the third one was a $NEWSPAPER best-seller" - }, - { - "quote": "If escalation is so bad, why do so many places have escalators? Take that, security community!", - "author": "AtkinsSJ", - "url": "https://discord.com/channels/830522505605283862/830522505605283866/1007765914722238534", - "utc_time": 1660340460, - "context": "Dynamic implementations of drivers and interfaces (Kernel-modules, OpenGL, ...) dlopen-ing and executing random files if you ask them \"nicely\", and StylePainter potentially having to do the same, leading to Filiph Sandström saying \"Potential privilege escalation, it's not a bug it's a feature™\"" - }, - { - "quote": "if you think about it, but not too hard, a programmer's brain can earn them money, colloquially known as bread. so their brain is bread. and when debugging in shared channel, you have more than one programmer putting their heads together", - "author": "Andrew Kaster", - "url": "https://discord.com/channels/830522505605283862/830525235803586570/1000895024277504020", - "utc_time": 1658702369, - "context": "\"Is debugging a sandwich?\"" - }, - { - "quote": "TFW you google for some error message, find someone else having the same error, and seeing a reply written by yourself explaining how to fix it", - "author": "thakis", - "url": "https://discord.com/channels/830522505605283862/831544568767578162/1030534498925949060", - "utc_time": 1665773194 - }, - { - "quote": "tfw your code has such high quality that the compiler explodes", - "author": "linusg", - "url": "https://discord.com/channels/830522505605283862/859531354219872266/1050792703849398282", - "utc_time": 1670598903, - "context": "\"Don't worry about if it's 'good enough'. AK isn't reserved for the highest quality code, it just tends to be better quality because it gets used a lot and bugs get fixed\"" - }, - { - "quote": "\"Windows is great\"\n~ CxBoog, 2022", - "author": "BertalanD", - "url":"https://discord.com/channels/830522505605283862/831544568767578162/1052206570274041987", - "utc_time": 1670935995, - "context": "windows is a great platform if you want to challenge every single portability issue in the universe, *including* differences in #include resolution algorithms..." - }, - { - "quote": "⚠️ WEN DETECTED ⚠️\nAccording to the Law of Kling, you are now obligated to implement it. :yakkie:", - "author": "AtkinsSJ", - "url": "https://discord.com/channels/830522505605283862/1012807179075600535/1055163396187766835", - "utc_time":1671640957, - "context": "hardware compositor wen :yakbait:" - }, - { - "quote": "how does anything even work ¯\\_(ツ)_/¯", - "author": "thakis", - "url": "https://discord.com/channels/830522505605283862/897113928168013855/1066058521747148840", - "utc_time": 1674238557, - "context": "ARM kernel and userland cache effects and jump distances and and and..." - }, - { - "quote": "i like your teeth though, will try incorporating those\nedit: done", - "author": "squeek502", - "url": "https://discord.com/channels/830522505605283862/927893781968191508/1074642864799240212", - "utc_time": 1676285224, - "context": "Mouth emoji 👄 improvements" - }, - { - "quote": "System: *slight OOM situation*\nCLion: \"I am now going to do what's called a pro move\" *crashes*", - "author": "CxByte", - "url": "https://discord.com/channels/830522505605283862/831544568767578162/1121820025351839824", - "utc_time": 1687533136 - }, - { - "quote": "`LibJS/Parser.cpp` is something that must be experienced first-hand", - "author": "Agni", - "url": "https://discord.com/channels/830522505605283862/830525235803586570/1135586864959598722", - "utc_time": 1690815406, - "context": "Let's just say it has grown out of control a tiny bit" - }, - { - "quote": "ISO C++ is a conspiracy by big diagnostic to sell you more compiler diagnostics", - "author": "CxByte", - "url": "https://discord.com/channels/830522505605283862/860165510263996427/1154472970903887892", - "utc_time": 1695318205, - "context": "me: I'll just make NavigationParams match the spec\nevery call site that was constructing one with C++ designated initializers: error: ISO C++ requires field designators to be specified in declaration order; field 'reserved_environment' will be initialized after field 'navigable' [-Werror,-Wreorder-init-list]" - - } -] diff --git a/Base/res/graphics/brand-banner-2x.png b/Base/res/graphics/brand-banner-2x.png deleted file mode 100644 index 3dca723e98e..00000000000 Binary files a/Base/res/graphics/brand-banner-2x.png and /dev/null differ diff --git a/Base/res/graphics/brand-banner.png b/Base/res/graphics/brand-banner.png deleted file mode 100644 index 6d5c6502185..00000000000 Binary files a/Base/res/graphics/brand-banner.png and /dev/null differ diff --git a/Base/res/graphics/buggie.png b/Base/res/graphics/buggie.png deleted file mode 100644 index 94558bd2d37..00000000000 Binary files a/Base/res/graphics/buggie.png and /dev/null differ diff --git a/Base/res/graphics/buggie.tvg b/Base/res/graphics/buggie.tvg deleted file mode 100644 index c82d5a4e36f..00000000000 Binary files a/Base/res/graphics/buggie.tvg and /dev/null differ diff --git a/Base/res/graphics/cards/backs/Blue.png b/Base/res/graphics/cards/backs/Blue.png deleted file mode 100644 index bb6cbacd009..00000000000 Binary files a/Base/res/graphics/cards/backs/Blue.png and /dev/null differ diff --git a/Base/res/graphics/cards/backs/Buggie.png b/Base/res/graphics/cards/backs/Buggie.png deleted file mode 100644 index d08bb9e5f05..00000000000 Binary files a/Base/res/graphics/cards/backs/Buggie.png and /dev/null differ diff --git a/Base/res/graphics/cards/backs/Ladyball.png b/Base/res/graphics/cards/backs/Ladyball.png deleted file mode 100644 index 8a74a3b1848..00000000000 Binary files a/Base/res/graphics/cards/backs/Ladyball.png and /dev/null differ diff --git a/Base/res/graphics/cards/backs/Ladybug-Classic.png b/Base/res/graphics/cards/backs/Ladybug-Classic.png deleted file mode 100644 index c5fdbf3991b..00000000000 Binary files a/Base/res/graphics/cards/backs/Ladybug-Classic.png and /dev/null differ diff --git a/Base/res/graphics/cards/backs/Ladybug.png b/Base/res/graphics/cards/backs/Ladybug.png deleted file mode 100644 index 3574a34938f..00000000000 Binary files a/Base/res/graphics/cards/backs/Ladybug.png and /dev/null differ diff --git a/Base/res/graphics/cards/backs/Red.png b/Base/res/graphics/cards/backs/Red.png deleted file mode 100644 index 2206d0d13a9..00000000000 Binary files a/Base/res/graphics/cards/backs/Red.png and /dev/null differ diff --git a/Base/res/graphics/cards/backs/Yak-Classic.png b/Base/res/graphics/cards/backs/Yak-Classic.png deleted file mode 100644 index 2e385866409..00000000000 Binary files a/Base/res/graphics/cards/backs/Yak-Classic.png and /dev/null differ diff --git a/Base/res/graphics/cards/backs/Yak.png b/Base/res/graphics/cards/backs/Yak.png deleted file mode 100644 index a819bca17ab..00000000000 Binary files a/Base/res/graphics/cards/backs/Yak.png and /dev/null differ diff --git a/Base/res/graphics/cards/fronts/Classic/club.png b/Base/res/graphics/cards/fronts/Classic/club.png deleted file mode 100644 index a2e9b8efd5e..00000000000 Binary files a/Base/res/graphics/cards/fronts/Classic/club.png and /dev/null differ diff --git a/Base/res/graphics/cards/fronts/Classic/clubs-jack.png b/Base/res/graphics/cards/fronts/Classic/clubs-jack.png deleted file mode 100644 index 5c5fe9692d6..00000000000 Binary files a/Base/res/graphics/cards/fronts/Classic/clubs-jack.png and /dev/null differ diff --git a/Base/res/graphics/cards/fronts/Classic/clubs-king.png b/Base/res/graphics/cards/fronts/Classic/clubs-king.png deleted file mode 100644 index 91f14b489cb..00000000000 Binary files a/Base/res/graphics/cards/fronts/Classic/clubs-king.png and /dev/null differ diff --git a/Base/res/graphics/cards/fronts/Classic/clubs-queen.png b/Base/res/graphics/cards/fronts/Classic/clubs-queen.png deleted file mode 100644 index 2e9c8db2361..00000000000 Binary files a/Base/res/graphics/cards/fronts/Classic/clubs-queen.png and /dev/null differ diff --git a/Base/res/graphics/cards/fronts/Classic/diamond.png b/Base/res/graphics/cards/fronts/Classic/diamond.png deleted file mode 100644 index 9707ac4d793..00000000000 Binary files a/Base/res/graphics/cards/fronts/Classic/diamond.png and /dev/null differ diff --git a/Base/res/graphics/cards/fronts/Classic/diamonds-jack.png b/Base/res/graphics/cards/fronts/Classic/diamonds-jack.png deleted file mode 100644 index 779e14cb116..00000000000 Binary files a/Base/res/graphics/cards/fronts/Classic/diamonds-jack.png and /dev/null differ diff --git a/Base/res/graphics/cards/fronts/Classic/diamonds-king.png b/Base/res/graphics/cards/fronts/Classic/diamonds-king.png deleted file mode 100644 index 9c901b81779..00000000000 Binary files a/Base/res/graphics/cards/fronts/Classic/diamonds-king.png and /dev/null differ diff --git a/Base/res/graphics/cards/fronts/Classic/diamonds-queen.png b/Base/res/graphics/cards/fronts/Classic/diamonds-queen.png deleted file mode 100644 index 0a8e6914305..00000000000 Binary files a/Base/res/graphics/cards/fronts/Classic/diamonds-queen.png and /dev/null differ diff --git a/Base/res/graphics/cards/fronts/Classic/heart.png b/Base/res/graphics/cards/fronts/Classic/heart.png deleted file mode 100644 index e28112d324d..00000000000 Binary files a/Base/res/graphics/cards/fronts/Classic/heart.png and /dev/null differ diff --git a/Base/res/graphics/cards/fronts/Classic/hearts-jack.png b/Base/res/graphics/cards/fronts/Classic/hearts-jack.png deleted file mode 100644 index c13dfcdc86a..00000000000 Binary files a/Base/res/graphics/cards/fronts/Classic/hearts-jack.png and /dev/null differ diff --git a/Base/res/graphics/cards/fronts/Classic/hearts-king.png b/Base/res/graphics/cards/fronts/Classic/hearts-king.png deleted file mode 100644 index 24d0748377f..00000000000 Binary files a/Base/res/graphics/cards/fronts/Classic/hearts-king.png and /dev/null differ diff --git a/Base/res/graphics/cards/fronts/Classic/hearts-queen.png b/Base/res/graphics/cards/fronts/Classic/hearts-queen.png deleted file mode 100644 index a6e9f13200f..00000000000 Binary files a/Base/res/graphics/cards/fronts/Classic/hearts-queen.png and /dev/null differ diff --git a/Base/res/graphics/cards/fronts/Classic/joker.png b/Base/res/graphics/cards/fronts/Classic/joker.png deleted file mode 100644 index 3482e686778..00000000000 Binary files a/Base/res/graphics/cards/fronts/Classic/joker.png and /dev/null differ diff --git a/Base/res/graphics/cards/fronts/Classic/spade.png b/Base/res/graphics/cards/fronts/Classic/spade.png deleted file mode 100644 index b6f3eac0070..00000000000 Binary files a/Base/res/graphics/cards/fronts/Classic/spade.png and /dev/null differ diff --git a/Base/res/graphics/cards/fronts/Classic/spades-ace.png b/Base/res/graphics/cards/fronts/Classic/spades-ace.png deleted file mode 100644 index 89a7bb80feb..00000000000 Binary files a/Base/res/graphics/cards/fronts/Classic/spades-ace.png and /dev/null differ diff --git a/Base/res/graphics/cards/fronts/Classic/spades-jack.png b/Base/res/graphics/cards/fronts/Classic/spades-jack.png deleted file mode 100644 index 2b5b30cfac3..00000000000 Binary files a/Base/res/graphics/cards/fronts/Classic/spades-jack.png and /dev/null differ diff --git a/Base/res/graphics/cards/fronts/Classic/spades-king.png b/Base/res/graphics/cards/fronts/Classic/spades-king.png deleted file mode 100644 index 23b5a673965..00000000000 Binary files a/Base/res/graphics/cards/fronts/Classic/spades-king.png and /dev/null differ diff --git a/Base/res/graphics/cards/fronts/Classic/spades-queen.png b/Base/res/graphics/cards/fronts/Classic/spades-queen.png deleted file mode 100644 index 225b0cb996b..00000000000 Binary files a/Base/res/graphics/cards/fronts/Classic/spades-queen.png and /dev/null differ diff --git a/Base/res/graphics/catdog/alert.png b/Base/res/graphics/catdog/alert.png deleted file mode 100644 index 2cb0a53ee3d..00000000000 Binary files a/Base/res/graphics/catdog/alert.png and /dev/null differ diff --git a/Base/res/graphics/catdog/artist.png b/Base/res/graphics/catdog/artist.png deleted file mode 100644 index b5ac5d5e8ce..00000000000 Binary files a/Base/res/graphics/catdog/artist.png and /dev/null differ diff --git a/Base/res/graphics/catdog/erun1.png b/Base/res/graphics/catdog/erun1.png deleted file mode 100644 index f70027a7fca..00000000000 Binary files a/Base/res/graphics/catdog/erun1.png and /dev/null differ diff --git a/Base/res/graphics/catdog/erun2.png b/Base/res/graphics/catdog/erun2.png deleted file mode 100644 index f8b24e45db9..00000000000 Binary files a/Base/res/graphics/catdog/erun2.png and /dev/null differ diff --git a/Base/res/graphics/catdog/inspector.png b/Base/res/graphics/catdog/inspector.png deleted file mode 100644 index ff5711bb597..00000000000 Binary files a/Base/res/graphics/catdog/inspector.png and /dev/null differ diff --git a/Base/res/graphics/catdog/nerun1.png b/Base/res/graphics/catdog/nerun1.png deleted file mode 100644 index 5b63c4e848b..00000000000 Binary files a/Base/res/graphics/catdog/nerun1.png and /dev/null differ diff --git a/Base/res/graphics/catdog/nerun2.png b/Base/res/graphics/catdog/nerun2.png deleted file mode 100644 index 6a5ef174325..00000000000 Binary files a/Base/res/graphics/catdog/nerun2.png and /dev/null differ diff --git a/Base/res/graphics/catdog/nrun1.png b/Base/res/graphics/catdog/nrun1.png deleted file mode 100644 index f0fb254172a..00000000000 Binary files a/Base/res/graphics/catdog/nrun1.png and /dev/null differ diff --git a/Base/res/graphics/catdog/nrun2.png b/Base/res/graphics/catdog/nrun2.png deleted file mode 100644 index a3e0cd884ec..00000000000 Binary files a/Base/res/graphics/catdog/nrun2.png and /dev/null differ diff --git a/Base/res/graphics/catdog/nwrun1.png b/Base/res/graphics/catdog/nwrun1.png deleted file mode 100644 index ee233dae11c..00000000000 Binary files a/Base/res/graphics/catdog/nwrun1.png and /dev/null differ diff --git a/Base/res/graphics/catdog/nwrun2.png b/Base/res/graphics/catdog/nwrun2.png deleted file mode 100644 index 1242326b1fb..00000000000 Binary files a/Base/res/graphics/catdog/nwrun2.png and /dev/null differ diff --git a/Base/res/graphics/catdog/serun1.png b/Base/res/graphics/catdog/serun1.png deleted file mode 100644 index 9acfeba9fef..00000000000 Binary files a/Base/res/graphics/catdog/serun1.png and /dev/null differ diff --git a/Base/res/graphics/catdog/serun2.png b/Base/res/graphics/catdog/serun2.png deleted file mode 100644 index 4ae8680ba7c..00000000000 Binary files a/Base/res/graphics/catdog/serun2.png and /dev/null differ diff --git a/Base/res/graphics/catdog/sleep1.png b/Base/res/graphics/catdog/sleep1.png deleted file mode 100644 index d1d8214fb98..00000000000 Binary files a/Base/res/graphics/catdog/sleep1.png and /dev/null differ diff --git a/Base/res/graphics/catdog/sleep2.png b/Base/res/graphics/catdog/sleep2.png deleted file mode 100644 index 88c9c0acef1..00000000000 Binary files a/Base/res/graphics/catdog/sleep2.png and /dev/null differ diff --git a/Base/res/graphics/catdog/srun1.png b/Base/res/graphics/catdog/srun1.png deleted file mode 100644 index 300bb4891d8..00000000000 Binary files a/Base/res/graphics/catdog/srun1.png and /dev/null differ diff --git a/Base/res/graphics/catdog/srun2.png b/Base/res/graphics/catdog/srun2.png deleted file mode 100644 index 49da2400b2b..00000000000 Binary files a/Base/res/graphics/catdog/srun2.png and /dev/null differ diff --git a/Base/res/graphics/catdog/still.png b/Base/res/graphics/catdog/still.png deleted file mode 100644 index 3b4df05ea4b..00000000000 Binary files a/Base/res/graphics/catdog/still.png and /dev/null differ diff --git a/Base/res/graphics/catdog/swrun1.png b/Base/res/graphics/catdog/swrun1.png deleted file mode 100644 index 0f29c8e2790..00000000000 Binary files a/Base/res/graphics/catdog/swrun1.png and /dev/null differ diff --git a/Base/res/graphics/catdog/swrun2.png b/Base/res/graphics/catdog/swrun2.png deleted file mode 100644 index 84df6f1b217..00000000000 Binary files a/Base/res/graphics/catdog/swrun2.png and /dev/null differ diff --git a/Base/res/graphics/catdog/wrun1.png b/Base/res/graphics/catdog/wrun1.png deleted file mode 100644 index 8544cde427c..00000000000 Binary files a/Base/res/graphics/catdog/wrun1.png and /dev/null differ diff --git a/Base/res/graphics/catdog/wrun2.png b/Base/res/graphics/catdog/wrun2.png deleted file mode 100644 index cf7d7b048ca..00000000000 Binary files a/Base/res/graphics/catdog/wrun2.png and /dev/null differ diff --git a/Base/res/graphics/chess/mini-board.png b/Base/res/graphics/chess/mini-board.png deleted file mode 100644 index c47bcdbab9d..00000000000 Binary files a/Base/res/graphics/chess/mini-board.png and /dev/null differ diff --git a/Base/res/graphics/chess/sets/Classic/black-bishop.png b/Base/res/graphics/chess/sets/Classic/black-bishop.png deleted file mode 100644 index a8456db7a13..00000000000 Binary files a/Base/res/graphics/chess/sets/Classic/black-bishop.png and /dev/null differ diff --git a/Base/res/graphics/chess/sets/Classic/black-king.png b/Base/res/graphics/chess/sets/Classic/black-king.png deleted file mode 100644 index c840e0b8539..00000000000 Binary files a/Base/res/graphics/chess/sets/Classic/black-king.png and /dev/null differ diff --git a/Base/res/graphics/chess/sets/Classic/black-knight.png b/Base/res/graphics/chess/sets/Classic/black-knight.png deleted file mode 100644 index d6199ca1544..00000000000 Binary files a/Base/res/graphics/chess/sets/Classic/black-knight.png and /dev/null differ diff --git a/Base/res/graphics/chess/sets/Classic/black-pawn.png b/Base/res/graphics/chess/sets/Classic/black-pawn.png deleted file mode 100644 index 9b9641b9fd9..00000000000 Binary files a/Base/res/graphics/chess/sets/Classic/black-pawn.png and /dev/null differ diff --git a/Base/res/graphics/chess/sets/Classic/black-queen.png b/Base/res/graphics/chess/sets/Classic/black-queen.png deleted file mode 100644 index da8585f4c1e..00000000000 Binary files a/Base/res/graphics/chess/sets/Classic/black-queen.png and /dev/null differ diff --git a/Base/res/graphics/chess/sets/Classic/black-rook.png b/Base/res/graphics/chess/sets/Classic/black-rook.png deleted file mode 100644 index c8c6ba5bd65..00000000000 Binary files a/Base/res/graphics/chess/sets/Classic/black-rook.png and /dev/null differ diff --git a/Base/res/graphics/chess/sets/Classic/white-bishop.png b/Base/res/graphics/chess/sets/Classic/white-bishop.png deleted file mode 100644 index 04161ff0a2d..00000000000 Binary files a/Base/res/graphics/chess/sets/Classic/white-bishop.png and /dev/null differ diff --git a/Base/res/graphics/chess/sets/Classic/white-king.png b/Base/res/graphics/chess/sets/Classic/white-king.png deleted file mode 100644 index ea9209e5c6f..00000000000 Binary files a/Base/res/graphics/chess/sets/Classic/white-king.png and /dev/null differ diff --git a/Base/res/graphics/chess/sets/Classic/white-knight.png b/Base/res/graphics/chess/sets/Classic/white-knight.png deleted file mode 100644 index c7c6224c153..00000000000 Binary files a/Base/res/graphics/chess/sets/Classic/white-knight.png and /dev/null differ diff --git a/Base/res/graphics/chess/sets/Classic/white-pawn.png b/Base/res/graphics/chess/sets/Classic/white-pawn.png deleted file mode 100644 index f96e8ddc8ef..00000000000 Binary files a/Base/res/graphics/chess/sets/Classic/white-pawn.png and /dev/null differ diff --git a/Base/res/graphics/chess/sets/Classic/white-queen.png b/Base/res/graphics/chess/sets/Classic/white-queen.png deleted file mode 100644 index cbaa9c067b0..00000000000 Binary files a/Base/res/graphics/chess/sets/Classic/white-queen.png and /dev/null differ diff --git a/Base/res/graphics/chess/sets/Classic/white-rook.png b/Base/res/graphics/chess/sets/Classic/white-rook.png deleted file mode 100644 index 43cb16fd3ae..00000000000 Binary files a/Base/res/graphics/chess/sets/Classic/white-rook.png and /dev/null differ diff --git a/Base/res/graphics/chess/sets/Moderna/black-bishop.png b/Base/res/graphics/chess/sets/Moderna/black-bishop.png deleted file mode 100644 index 5e21cf6234d..00000000000 Binary files a/Base/res/graphics/chess/sets/Moderna/black-bishop.png and /dev/null differ diff --git a/Base/res/graphics/chess/sets/Moderna/black-king.png b/Base/res/graphics/chess/sets/Moderna/black-king.png deleted file mode 100644 index fc21f2238ef..00000000000 Binary files a/Base/res/graphics/chess/sets/Moderna/black-king.png and /dev/null differ diff --git a/Base/res/graphics/chess/sets/Moderna/black-knight.png b/Base/res/graphics/chess/sets/Moderna/black-knight.png deleted file mode 100644 index fa40ac7edf1..00000000000 Binary files a/Base/res/graphics/chess/sets/Moderna/black-knight.png and /dev/null differ diff --git a/Base/res/graphics/chess/sets/Moderna/black-pawn.png b/Base/res/graphics/chess/sets/Moderna/black-pawn.png deleted file mode 100644 index 6dfb85534d3..00000000000 Binary files a/Base/res/graphics/chess/sets/Moderna/black-pawn.png and /dev/null differ diff --git a/Base/res/graphics/chess/sets/Moderna/black-queen.png b/Base/res/graphics/chess/sets/Moderna/black-queen.png deleted file mode 100644 index 30f3e172a7f..00000000000 Binary files a/Base/res/graphics/chess/sets/Moderna/black-queen.png and /dev/null differ diff --git a/Base/res/graphics/chess/sets/Moderna/black-rook.png b/Base/res/graphics/chess/sets/Moderna/black-rook.png deleted file mode 100644 index fd591697f77..00000000000 Binary files a/Base/res/graphics/chess/sets/Moderna/black-rook.png and /dev/null differ diff --git a/Base/res/graphics/chess/sets/Moderna/moderna_final_black_bishop.svg b/Base/res/graphics/chess/sets/Moderna/moderna_final_black_bishop.svg deleted file mode 100644 index bd5cdba9831..00000000000 --- a/Base/res/graphics/chess/sets/Moderna/moderna_final_black_bishop.svg +++ /dev/null @@ -1,75 +0,0 @@ - - - - - - - - - - image/svg+xml - - - - - - - - - diff --git a/Base/res/graphics/chess/sets/Moderna/moderna_final_black_king.svg b/Base/res/graphics/chess/sets/Moderna/moderna_final_black_king.svg deleted file mode 100644 index a5ae6302099..00000000000 --- a/Base/res/graphics/chess/sets/Moderna/moderna_final_black_king.svg +++ /dev/null @@ -1,76 +0,0 @@ - - - - - - - - - - image/svg+xml - - - - - - - - - diff --git a/Base/res/graphics/chess/sets/Moderna/moderna_final_black_knight.svg b/Base/res/graphics/chess/sets/Moderna/moderna_final_black_knight.svg deleted file mode 100644 index 669e030a49d..00000000000 --- a/Base/res/graphics/chess/sets/Moderna/moderna_final_black_knight.svg +++ /dev/null @@ -1,76 +0,0 @@ - - - - - - - - - - image/svg+xml - - - - - - - - - diff --git a/Base/res/graphics/chess/sets/Moderna/moderna_final_black_pawn.svg b/Base/res/graphics/chess/sets/Moderna/moderna_final_black_pawn.svg deleted file mode 100644 index 712b31ac0c4..00000000000 --- a/Base/res/graphics/chess/sets/Moderna/moderna_final_black_pawn.svg +++ /dev/null @@ -1,77 +0,0 @@ - - - - - - - - - - image/svg+xml - - - - - - - - - diff --git a/Base/res/graphics/chess/sets/Moderna/moderna_final_black_queen.svg b/Base/res/graphics/chess/sets/Moderna/moderna_final_black_queen.svg deleted file mode 100644 index 61e0ce80c81..00000000000 --- a/Base/res/graphics/chess/sets/Moderna/moderna_final_black_queen.svg +++ /dev/null @@ -1,76 +0,0 @@ - - - - - - - - - - image/svg+xml - - - - - - - - - diff --git a/Base/res/graphics/chess/sets/Moderna/moderna_final_black_rook.svg b/Base/res/graphics/chess/sets/Moderna/moderna_final_black_rook.svg deleted file mode 100644 index 574c443f482..00000000000 --- a/Base/res/graphics/chess/sets/Moderna/moderna_final_black_rook.svg +++ /dev/null @@ -1,76 +0,0 @@ - - - - - - - - - - image/svg+xml - - - - - - - - - diff --git a/Base/res/graphics/chess/sets/Moderna/moderna_final_white_outline_bishop.svg b/Base/res/graphics/chess/sets/Moderna/moderna_final_white_outline_bishop.svg deleted file mode 100644 index 63eca917920..00000000000 --- a/Base/res/graphics/chess/sets/Moderna/moderna_final_white_outline_bishop.svg +++ /dev/null @@ -1,75 +0,0 @@ - - - - - - - - - - image/svg+xml - - - - - - - - - diff --git a/Base/res/graphics/chess/sets/Moderna/moderna_final_white_outline_king.svg b/Base/res/graphics/chess/sets/Moderna/moderna_final_white_outline_king.svg deleted file mode 100644 index c34bf80bde2..00000000000 --- a/Base/res/graphics/chess/sets/Moderna/moderna_final_white_outline_king.svg +++ /dev/null @@ -1,76 +0,0 @@ - - - - - - - - - - image/svg+xml - - - - - - - - - diff --git a/Base/res/graphics/chess/sets/Moderna/moderna_final_white_outline_knight.svg b/Base/res/graphics/chess/sets/Moderna/moderna_final_white_outline_knight.svg deleted file mode 100644 index e4581e678bf..00000000000 --- a/Base/res/graphics/chess/sets/Moderna/moderna_final_white_outline_knight.svg +++ /dev/null @@ -1,76 +0,0 @@ - - - - - - - - - - image/svg+xml - - - - - - - - - diff --git a/Base/res/graphics/chess/sets/Moderna/moderna_final_white_outline_pawn.svg b/Base/res/graphics/chess/sets/Moderna/moderna_final_white_outline_pawn.svg deleted file mode 100644 index 336358980dc..00000000000 --- a/Base/res/graphics/chess/sets/Moderna/moderna_final_white_outline_pawn.svg +++ /dev/null @@ -1,77 +0,0 @@ - - - - - - - - - - image/svg+xml - - - - - - - - - diff --git a/Base/res/graphics/chess/sets/Moderna/moderna_final_white_outline_queen.svg b/Base/res/graphics/chess/sets/Moderna/moderna_final_white_outline_queen.svg deleted file mode 100644 index 8e77d8c64ec..00000000000 --- a/Base/res/graphics/chess/sets/Moderna/moderna_final_white_outline_queen.svg +++ /dev/null @@ -1,76 +0,0 @@ - - - - - - - - - - image/svg+xml - - - - - - - - - diff --git a/Base/res/graphics/chess/sets/Moderna/moderna_final_white_outline_rook.svg b/Base/res/graphics/chess/sets/Moderna/moderna_final_white_outline_rook.svg deleted file mode 100644 index 74b891aa5d2..00000000000 --- a/Base/res/graphics/chess/sets/Moderna/moderna_final_white_outline_rook.svg +++ /dev/null @@ -1,76 +0,0 @@ - - - - - - - - - - image/svg+xml - - - - - - - - - diff --git a/Base/res/graphics/chess/sets/Moderna/white-bishop.png b/Base/res/graphics/chess/sets/Moderna/white-bishop.png deleted file mode 100644 index 957537e92a8..00000000000 Binary files a/Base/res/graphics/chess/sets/Moderna/white-bishop.png and /dev/null differ diff --git a/Base/res/graphics/chess/sets/Moderna/white-king.png b/Base/res/graphics/chess/sets/Moderna/white-king.png deleted file mode 100644 index b203cb5d01f..00000000000 Binary files a/Base/res/graphics/chess/sets/Moderna/white-king.png and /dev/null differ diff --git a/Base/res/graphics/chess/sets/Moderna/white-knight.png b/Base/res/graphics/chess/sets/Moderna/white-knight.png deleted file mode 100644 index 4bba750b337..00000000000 Binary files a/Base/res/graphics/chess/sets/Moderna/white-knight.png and /dev/null differ diff --git a/Base/res/graphics/chess/sets/Moderna/white-pawn.png b/Base/res/graphics/chess/sets/Moderna/white-pawn.png deleted file mode 100644 index 0212229f6ec..00000000000 Binary files a/Base/res/graphics/chess/sets/Moderna/white-pawn.png and /dev/null differ diff --git a/Base/res/graphics/chess/sets/Moderna/white-queen.png b/Base/res/graphics/chess/sets/Moderna/white-queen.png deleted file mode 100644 index 238ba7eb1d6..00000000000 Binary files a/Base/res/graphics/chess/sets/Moderna/white-queen.png and /dev/null differ diff --git a/Base/res/graphics/chess/sets/Moderna/white-rook.png b/Base/res/graphics/chess/sets/Moderna/white-rook.png deleted file mode 100644 index 18931bee444..00000000000 Binary files a/Base/res/graphics/chess/sets/Moderna/white-rook.png and /dev/null differ diff --git a/Base/res/graphics/chess/sets/Retro/black-bishop.png b/Base/res/graphics/chess/sets/Retro/black-bishop.png deleted file mode 100644 index a73d6212c77..00000000000 Binary files a/Base/res/graphics/chess/sets/Retro/black-bishop.png and /dev/null differ diff --git a/Base/res/graphics/chess/sets/Retro/black-king.png b/Base/res/graphics/chess/sets/Retro/black-king.png deleted file mode 100644 index 102cd70764b..00000000000 Binary files a/Base/res/graphics/chess/sets/Retro/black-king.png and /dev/null differ diff --git a/Base/res/graphics/chess/sets/Retro/black-knight.png b/Base/res/graphics/chess/sets/Retro/black-knight.png deleted file mode 100644 index ddf75cd2a0b..00000000000 Binary files a/Base/res/graphics/chess/sets/Retro/black-knight.png and /dev/null differ diff --git a/Base/res/graphics/chess/sets/Retro/black-pawn.png b/Base/res/graphics/chess/sets/Retro/black-pawn.png deleted file mode 100644 index 73dd51ba746..00000000000 Binary files a/Base/res/graphics/chess/sets/Retro/black-pawn.png and /dev/null differ diff --git a/Base/res/graphics/chess/sets/Retro/black-queen.png b/Base/res/graphics/chess/sets/Retro/black-queen.png deleted file mode 100644 index 663590e0b0d..00000000000 Binary files a/Base/res/graphics/chess/sets/Retro/black-queen.png and /dev/null differ diff --git a/Base/res/graphics/chess/sets/Retro/black-rook.png b/Base/res/graphics/chess/sets/Retro/black-rook.png deleted file mode 100644 index 342ae19c5ff..00000000000 Binary files a/Base/res/graphics/chess/sets/Retro/black-rook.png and /dev/null differ diff --git a/Base/res/graphics/chess/sets/Retro/white-bishop.png b/Base/res/graphics/chess/sets/Retro/white-bishop.png deleted file mode 100644 index 9702b0ed02c..00000000000 Binary files a/Base/res/graphics/chess/sets/Retro/white-bishop.png and /dev/null differ diff --git a/Base/res/graphics/chess/sets/Retro/white-king.png b/Base/res/graphics/chess/sets/Retro/white-king.png deleted file mode 100644 index abed4f8c8af..00000000000 Binary files a/Base/res/graphics/chess/sets/Retro/white-king.png and /dev/null differ diff --git a/Base/res/graphics/chess/sets/Retro/white-knight.png b/Base/res/graphics/chess/sets/Retro/white-knight.png deleted file mode 100644 index 19be483f04d..00000000000 Binary files a/Base/res/graphics/chess/sets/Retro/white-knight.png and /dev/null differ diff --git a/Base/res/graphics/chess/sets/Retro/white-pawn.png b/Base/res/graphics/chess/sets/Retro/white-pawn.png deleted file mode 100644 index 85c45d86970..00000000000 Binary files a/Base/res/graphics/chess/sets/Retro/white-pawn.png and /dev/null differ diff --git a/Base/res/graphics/chess/sets/Retro/white-queen.png b/Base/res/graphics/chess/sets/Retro/white-queen.png deleted file mode 100644 index 18984e6b288..00000000000 Binary files a/Base/res/graphics/chess/sets/Retro/white-queen.png and /dev/null differ diff --git a/Base/res/graphics/chess/sets/Retro/white-rook.png b/Base/res/graphics/chess/sets/Retro/white-rook.png deleted file mode 100644 index d39e02cdfa8..00000000000 Binary files a/Base/res/graphics/chess/sets/Retro/white-rook.png and /dev/null differ diff --git a/Base/res/graphics/colorlines/colorlines.png b/Base/res/graphics/colorlines/colorlines.png deleted file mode 100644 index 7be363222a3..00000000000 Binary files a/Base/res/graphics/colorlines/colorlines.png and /dev/null differ diff --git a/Base/res/graphics/double-click-down-arrow.png b/Base/res/graphics/double-click-down-arrow.png deleted file mode 100644 index 3b8d6d83c75..00000000000 Binary files a/Base/res/graphics/double-click-down-arrow.png and /dev/null differ diff --git a/Base/res/graphics/download-animation.gif b/Base/res/graphics/download-animation.gif deleted file mode 100644 index f5e55e40f5d..00000000000 Binary files a/Base/res/graphics/download-animation.gif and /dev/null differ diff --git a/Base/res/graphics/download-finished.gif b/Base/res/graphics/download-finished.gif deleted file mode 100644 index 5bb54ea94fb..00000000000 Binary files a/Base/res/graphics/download-finished.gif and /dev/null differ diff --git a/Base/res/graphics/file-flying-animation.gif b/Base/res/graphics/file-flying-animation.gif deleted file mode 100644 index d4557d7f7b9..00000000000 Binary files a/Base/res/graphics/file-flying-animation.gif and /dev/null differ diff --git a/Base/res/graphics/flappybug/background.png b/Base/res/graphics/flappybug/background.png deleted file mode 100644 index 6541413f210..00000000000 Binary files a/Base/res/graphics/flappybug/background.png and /dev/null differ diff --git a/Base/res/graphics/flappybug/cloud_0.png b/Base/res/graphics/flappybug/cloud_0.png deleted file mode 100644 index 6a25f42cbf2..00000000000 Binary files a/Base/res/graphics/flappybug/cloud_0.png and /dev/null differ diff --git a/Base/res/graphics/flappybug/cloud_1.png b/Base/res/graphics/flappybug/cloud_1.png deleted file mode 100644 index 8e7ee022661..00000000000 Binary files a/Base/res/graphics/flappybug/cloud_1.png and /dev/null differ diff --git a/Base/res/graphics/flappybug/cloud_2.png b/Base/res/graphics/flappybug/cloud_2.png deleted file mode 100644 index 53ba02d148e..00000000000 Binary files a/Base/res/graphics/flappybug/cloud_2.png and /dev/null differ diff --git a/Base/res/graphics/flappybug/falling.png b/Base/res/graphics/flappybug/falling.png deleted file mode 100644 index 22363e6f234..00000000000 Binary files a/Base/res/graphics/flappybug/falling.png and /dev/null differ diff --git a/Base/res/graphics/flappybug/flapping.png b/Base/res/graphics/flappybug/flapping.png deleted file mode 100644 index 6e5af7afa56..00000000000 Binary files a/Base/res/graphics/flappybug/flapping.png and /dev/null differ diff --git a/Base/res/graphics/mail-server-settings.png b/Base/res/graphics/mail-server-settings.png deleted file mode 100644 index 18762435a28..00000000000 Binary files a/Base/res/graphics/mail-server-settings.png and /dev/null differ diff --git a/Base/res/graphics/mail-user-settings.png b/Base/res/graphics/mail-user-settings.png deleted file mode 100644 index ad7e375f5e9..00000000000 Binary files a/Base/res/graphics/mail-user-settings.png and /dev/null differ diff --git a/Base/res/graphics/map.png b/Base/res/graphics/map.png deleted file mode 100644 index d744a3b99d8..00000000000 Binary files a/Base/res/graphics/map.png and /dev/null differ diff --git a/Base/res/graphics/maps/marker-blue.png b/Base/res/graphics/maps/marker-blue.png deleted file mode 100644 index 822042fbd6d..00000000000 Binary files a/Base/res/graphics/maps/marker-blue.png and /dev/null differ diff --git a/Base/res/graphics/maps/marker-gray.png b/Base/res/graphics/maps/marker-gray.png deleted file mode 100644 index 5aee1bc8cba..00000000000 Binary files a/Base/res/graphics/maps/marker-gray.png and /dev/null differ diff --git a/Base/res/graphics/maps/marker-red.png b/Base/res/graphics/maps/marker-red.png deleted file mode 100644 index 7d2ec39b866..00000000000 Binary files a/Base/res/graphics/maps/marker-red.png and /dev/null differ diff --git a/Base/res/graphics/minesweeper/1.png b/Base/res/graphics/minesweeper/1.png deleted file mode 100644 index 2a48f3bd191..00000000000 Binary files a/Base/res/graphics/minesweeper/1.png and /dev/null differ diff --git a/Base/res/graphics/minesweeper/2.png b/Base/res/graphics/minesweeper/2.png deleted file mode 100644 index ebe44b77acd..00000000000 Binary files a/Base/res/graphics/minesweeper/2.png and /dev/null differ diff --git a/Base/res/graphics/minesweeper/3.png b/Base/res/graphics/minesweeper/3.png deleted file mode 100644 index e9f5c899aa4..00000000000 Binary files a/Base/res/graphics/minesweeper/3.png and /dev/null differ diff --git a/Base/res/graphics/minesweeper/4.png b/Base/res/graphics/minesweeper/4.png deleted file mode 100644 index cf44593e13e..00000000000 Binary files a/Base/res/graphics/minesweeper/4.png and /dev/null differ diff --git a/Base/res/graphics/minesweeper/5.png b/Base/res/graphics/minesweeper/5.png deleted file mode 100644 index d64259f64b2..00000000000 Binary files a/Base/res/graphics/minesweeper/5.png and /dev/null differ diff --git a/Base/res/graphics/minesweeper/6.png b/Base/res/graphics/minesweeper/6.png deleted file mode 100644 index f16154b8bb3..00000000000 Binary files a/Base/res/graphics/minesweeper/6.png and /dev/null differ diff --git a/Base/res/graphics/minesweeper/7.png b/Base/res/graphics/minesweeper/7.png deleted file mode 100644 index df149914e1c..00000000000 Binary files a/Base/res/graphics/minesweeper/7.png and /dev/null differ diff --git a/Base/res/graphics/minesweeper/8.png b/Base/res/graphics/minesweeper/8.png deleted file mode 100644 index d0c7427e3e8..00000000000 Binary files a/Base/res/graphics/minesweeper/8.png and /dev/null differ diff --git a/Base/res/graphics/minesweeper/badflag.png b/Base/res/graphics/minesweeper/badflag.png deleted file mode 100644 index d9ac0cf1552..00000000000 Binary files a/Base/res/graphics/minesweeper/badflag.png and /dev/null differ diff --git a/Base/res/graphics/minesweeper/consider.png b/Base/res/graphics/minesweeper/consider.png deleted file mode 100644 index 2e12c4e740f..00000000000 Binary files a/Base/res/graphics/minesweeper/consider.png and /dev/null differ diff --git a/Base/res/graphics/minesweeper/face-bad.png b/Base/res/graphics/minesweeper/face-bad.png deleted file mode 100644 index 4e4fc82a96f..00000000000 Binary files a/Base/res/graphics/minesweeper/face-bad.png and /dev/null differ diff --git a/Base/res/graphics/minesweeper/face-default.png b/Base/res/graphics/minesweeper/face-default.png deleted file mode 100644 index aff22387bb1..00000000000 Binary files a/Base/res/graphics/minesweeper/face-default.png and /dev/null differ diff --git a/Base/res/graphics/minesweeper/face-good.png b/Base/res/graphics/minesweeper/face-good.png deleted file mode 100644 index 672df99a04c..00000000000 Binary files a/Base/res/graphics/minesweeper/face-good.png and /dev/null differ diff --git a/Base/res/graphics/minesweeper/flag.png b/Base/res/graphics/minesweeper/flag.png deleted file mode 100644 index f53fe7fba60..00000000000 Binary files a/Base/res/graphics/minesweeper/flag.png and /dev/null differ diff --git a/Base/res/graphics/minesweeper/mine.png b/Base/res/graphics/minesweeper/mine.png deleted file mode 100644 index 97693fd463b..00000000000 Binary files a/Base/res/graphics/minesweeper/mine.png and /dev/null differ diff --git a/Base/res/graphics/minesweeper/timer.png b/Base/res/graphics/minesweeper/timer.png deleted file mode 100644 index d178078181d..00000000000 Binary files a/Base/res/graphics/minesweeper/timer.png and /dev/null differ diff --git a/Base/res/graphics/monitor.png b/Base/res/graphics/monitor.png deleted file mode 100644 index 0d8c3c6a406..00000000000 Binary files a/Base/res/graphics/monitor.png and /dev/null differ diff --git a/Base/res/graphics/mouse-button-left.png b/Base/res/graphics/mouse-button-left.png deleted file mode 100644 index 8e56dd40650..00000000000 Binary files a/Base/res/graphics/mouse-button-left.png and /dev/null differ diff --git a/Base/res/graphics/mouse-button-right.png b/Base/res/graphics/mouse-button-right.png deleted file mode 100644 index 5129c4654ca..00000000000 Binary files a/Base/res/graphics/mouse-button-right.png and /dev/null differ diff --git a/Base/res/graphics/mouse-cursor-speed.png b/Base/res/graphics/mouse-cursor-speed.png deleted file mode 100644 index 78583ab6108..00000000000 Binary files a/Base/res/graphics/mouse-cursor-speed.png and /dev/null differ diff --git a/Base/res/graphics/overlay-rect-shadow.png b/Base/res/graphics/overlay-rect-shadow.png deleted file mode 100644 index 72257746c99..00000000000 Binary files a/Base/res/graphics/overlay-rect-shadow.png and /dev/null differ diff --git a/Base/res/graphics/scroll-wheel-step-size.png b/Base/res/graphics/scroll-wheel-step-size.png deleted file mode 100644 index 180a8477a44..00000000000 Binary files a/Base/res/graphics/scroll-wheel-step-size.png and /dev/null differ diff --git a/Base/res/graphics/snake/skins/Blue Malaysian Coral Snake (Calliophis bivirgatus)/corner.png b/Base/res/graphics/snake/skins/Blue Malaysian Coral Snake (Calliophis bivirgatus)/corner.png deleted file mode 100644 index 62e0349dff1..00000000000 Binary files a/Base/res/graphics/snake/skins/Blue Malaysian Coral Snake (Calliophis bivirgatus)/corner.png and /dev/null differ diff --git a/Base/res/graphics/snake/skins/Blue Malaysian Coral Snake (Calliophis bivirgatus)/head.png b/Base/res/graphics/snake/skins/Blue Malaysian Coral Snake (Calliophis bivirgatus)/head.png deleted file mode 100644 index c06ae638680..00000000000 Binary files a/Base/res/graphics/snake/skins/Blue Malaysian Coral Snake (Calliophis bivirgatus)/head.png and /dev/null differ diff --git a/Base/res/graphics/snake/skins/Blue Malaysian Coral Snake (Calliophis bivirgatus)/horizontal.png b/Base/res/graphics/snake/skins/Blue Malaysian Coral Snake (Calliophis bivirgatus)/horizontal.png deleted file mode 100644 index ead7d59d7b8..00000000000 Binary files a/Base/res/graphics/snake/skins/Blue Malaysian Coral Snake (Calliophis bivirgatus)/horizontal.png and /dev/null differ diff --git a/Base/res/graphics/snake/skins/Blue Malaysian Coral Snake (Calliophis bivirgatus)/tail.png b/Base/res/graphics/snake/skins/Blue Malaysian Coral Snake (Calliophis bivirgatus)/tail.png deleted file mode 100644 index 91abb4634cb..00000000000 Binary files a/Base/res/graphics/snake/skins/Blue Malaysian Coral Snake (Calliophis bivirgatus)/tail.png and /dev/null differ diff --git a/Base/res/graphics/snake/skins/Blue Malaysian Coral Snake (Calliophis bivirgatus)/vertical.png b/Base/res/graphics/snake/skins/Blue Malaysian Coral Snake (Calliophis bivirgatus)/vertical.png deleted file mode 100644 index fb2968ed413..00000000000 Binary files a/Base/res/graphics/snake/skins/Blue Malaysian Coral Snake (Calliophis bivirgatus)/vertical.png and /dev/null differ diff --git a/Base/res/graphics/snake/skins/Eastern Coral Snake (Micrurus fulvius)/corner.png b/Base/res/graphics/snake/skins/Eastern Coral Snake (Micrurus fulvius)/corner.png deleted file mode 100644 index 83ace1202a6..00000000000 Binary files a/Base/res/graphics/snake/skins/Eastern Coral Snake (Micrurus fulvius)/corner.png and /dev/null differ diff --git a/Base/res/graphics/snake/skins/Eastern Coral Snake (Micrurus fulvius)/head.png b/Base/res/graphics/snake/skins/Eastern Coral Snake (Micrurus fulvius)/head.png deleted file mode 100644 index 8c84645812a..00000000000 Binary files a/Base/res/graphics/snake/skins/Eastern Coral Snake (Micrurus fulvius)/head.png and /dev/null differ diff --git a/Base/res/graphics/snake/skins/Eastern Coral Snake (Micrurus fulvius)/horizontal.png b/Base/res/graphics/snake/skins/Eastern Coral Snake (Micrurus fulvius)/horizontal.png deleted file mode 100644 index 1126e19b874..00000000000 Binary files a/Base/res/graphics/snake/skins/Eastern Coral Snake (Micrurus fulvius)/horizontal.png and /dev/null differ diff --git a/Base/res/graphics/snake/skins/Eastern Coral Snake (Micrurus fulvius)/tail.png b/Base/res/graphics/snake/skins/Eastern Coral Snake (Micrurus fulvius)/tail.png deleted file mode 100644 index 6073f2e64b0..00000000000 Binary files a/Base/res/graphics/snake/skins/Eastern Coral Snake (Micrurus fulvius)/tail.png and /dev/null differ diff --git a/Base/res/graphics/snake/skins/Eastern Coral Snake (Micrurus fulvius)/vertical.png b/Base/res/graphics/snake/skins/Eastern Coral Snake (Micrurus fulvius)/vertical.png deleted file mode 100644 index 988d7c416fe..00000000000 Binary files a/Base/res/graphics/snake/skins/Eastern Coral Snake (Micrurus fulvius)/vertical.png and /dev/null differ diff --git a/Base/res/graphics/snake/skins/Ladybird/corner.png b/Base/res/graphics/snake/skins/Ladybird/corner.png deleted file mode 100644 index 17ce6b5ead6..00000000000 Binary files a/Base/res/graphics/snake/skins/Ladybird/corner.png and /dev/null differ diff --git a/Base/res/graphics/snake/skins/Ladybird/head.png b/Base/res/graphics/snake/skins/Ladybird/head.png deleted file mode 100644 index 2325d03639b..00000000000 Binary files a/Base/res/graphics/snake/skins/Ladybird/head.png and /dev/null differ diff --git a/Base/res/graphics/snake/skins/Ladybird/horizontal.png b/Base/res/graphics/snake/skins/Ladybird/horizontal.png deleted file mode 100644 index 001490fb78a..00000000000 Binary files a/Base/res/graphics/snake/skins/Ladybird/horizontal.png and /dev/null differ diff --git a/Base/res/graphics/snake/skins/Ladybird/tail.png b/Base/res/graphics/snake/skins/Ladybird/tail.png deleted file mode 100644 index c97eda41321..00000000000 Binary files a/Base/res/graphics/snake/skins/Ladybird/tail.png and /dev/null differ diff --git a/Base/res/graphics/snake/skins/Ladybird/vertical.png b/Base/res/graphics/snake/skins/Ladybird/vertical.png deleted file mode 100644 index 1d501666dcc..00000000000 Binary files a/Base/res/graphics/snake/skins/Ladybird/vertical.png and /dev/null differ diff --git a/Base/res/graphics/snake/skins/San Francisco Garter Snake (Thamnophis sirtalis tetrataenia)/corner.png b/Base/res/graphics/snake/skins/San Francisco Garter Snake (Thamnophis sirtalis tetrataenia)/corner.png deleted file mode 100644 index 3f158c32455..00000000000 Binary files a/Base/res/graphics/snake/skins/San Francisco Garter Snake (Thamnophis sirtalis tetrataenia)/corner.png and /dev/null differ diff --git a/Base/res/graphics/snake/skins/San Francisco Garter Snake (Thamnophis sirtalis tetrataenia)/head.png b/Base/res/graphics/snake/skins/San Francisco Garter Snake (Thamnophis sirtalis tetrataenia)/head.png deleted file mode 100644 index e6939d35b99..00000000000 Binary files a/Base/res/graphics/snake/skins/San Francisco Garter Snake (Thamnophis sirtalis tetrataenia)/head.png and /dev/null differ diff --git a/Base/res/graphics/snake/skins/San Francisco Garter Snake (Thamnophis sirtalis tetrataenia)/horizontal.png b/Base/res/graphics/snake/skins/San Francisco Garter Snake (Thamnophis sirtalis tetrataenia)/horizontal.png deleted file mode 100644 index b306ff4d4d6..00000000000 Binary files a/Base/res/graphics/snake/skins/San Francisco Garter Snake (Thamnophis sirtalis tetrataenia)/horizontal.png and /dev/null differ diff --git a/Base/res/graphics/snake/skins/San Francisco Garter Snake (Thamnophis sirtalis tetrataenia)/tail.png b/Base/res/graphics/snake/skins/San Francisco Garter Snake (Thamnophis sirtalis tetrataenia)/tail.png deleted file mode 100644 index 83e151e0e66..00000000000 Binary files a/Base/res/graphics/snake/skins/San Francisco Garter Snake (Thamnophis sirtalis tetrataenia)/tail.png and /dev/null differ diff --git a/Base/res/graphics/snake/skins/San Francisco Garter Snake (Thamnophis sirtalis tetrataenia)/vertical.png b/Base/res/graphics/snake/skins/San Francisco Garter Snake (Thamnophis sirtalis tetrataenia)/vertical.png deleted file mode 100644 index 0de5bd7c4f4..00000000000 Binary files a/Base/res/graphics/snake/skins/San Francisco Garter Snake (Thamnophis sirtalis tetrataenia)/vertical.png and /dev/null differ diff --git a/Base/res/graphics/snake/skins/Snake/corner.png b/Base/res/graphics/snake/skins/Snake/corner.png deleted file mode 100644 index f0cf226032c..00000000000 Binary files a/Base/res/graphics/snake/skins/Snake/corner.png and /dev/null differ diff --git a/Base/res/graphics/snake/skins/Snake/head.png b/Base/res/graphics/snake/skins/Snake/head.png deleted file mode 100644 index 8958131ac09..00000000000 Binary files a/Base/res/graphics/snake/skins/Snake/head.png and /dev/null differ diff --git a/Base/res/graphics/snake/skins/Snake/horizontal.png b/Base/res/graphics/snake/skins/Snake/horizontal.png deleted file mode 100644 index 70b11a8388a..00000000000 Binary files a/Base/res/graphics/snake/skins/Snake/horizontal.png and /dev/null differ diff --git a/Base/res/graphics/snake/skins/Snake/tail.png b/Base/res/graphics/snake/skins/Snake/tail.png deleted file mode 100644 index ef1bc00668b..00000000000 Binary files a/Base/res/graphics/snake/skins/Snake/tail.png and /dev/null differ diff --git a/Base/res/graphics/snake/skins/Snake/vertical.png b/Base/res/graphics/snake/skins/Snake/vertical.png deleted file mode 100644 index c1790767643..00000000000 Binary files a/Base/res/graphics/snake/skins/Snake/vertical.png and /dev/null differ diff --git a/Base/res/graphics/snake/skins/Sri Lankan Green Vine Snake (Ahaetulla nasuta)/corner.png b/Base/res/graphics/snake/skins/Sri Lankan Green Vine Snake (Ahaetulla nasuta)/corner.png deleted file mode 100644 index bedce20b0b8..00000000000 Binary files a/Base/res/graphics/snake/skins/Sri Lankan Green Vine Snake (Ahaetulla nasuta)/corner.png and /dev/null differ diff --git a/Base/res/graphics/snake/skins/Sri Lankan Green Vine Snake (Ahaetulla nasuta)/head.png b/Base/res/graphics/snake/skins/Sri Lankan Green Vine Snake (Ahaetulla nasuta)/head.png deleted file mode 100644 index 56374c3964b..00000000000 Binary files a/Base/res/graphics/snake/skins/Sri Lankan Green Vine Snake (Ahaetulla nasuta)/head.png and /dev/null differ diff --git a/Base/res/graphics/snake/skins/Sri Lankan Green Vine Snake (Ahaetulla nasuta)/horizontal.png b/Base/res/graphics/snake/skins/Sri Lankan Green Vine Snake (Ahaetulla nasuta)/horizontal.png deleted file mode 100644 index 29eebc0e8f6..00000000000 Binary files a/Base/res/graphics/snake/skins/Sri Lankan Green Vine Snake (Ahaetulla nasuta)/horizontal.png and /dev/null differ diff --git a/Base/res/graphics/snake/skins/Sri Lankan Green Vine Snake (Ahaetulla nasuta)/tail.png b/Base/res/graphics/snake/skins/Sri Lankan Green Vine Snake (Ahaetulla nasuta)/tail.png deleted file mode 100644 index 259963386d7..00000000000 Binary files a/Base/res/graphics/snake/skins/Sri Lankan Green Vine Snake (Ahaetulla nasuta)/tail.png and /dev/null differ diff --git a/Base/res/graphics/snake/skins/Sri Lankan Green Vine Snake (Ahaetulla nasuta)/vertical.png b/Base/res/graphics/snake/skins/Sri Lankan Green Vine Snake (Ahaetulla nasuta)/vertical.png deleted file mode 100644 index 5a534aea339..00000000000 Binary files a/Base/res/graphics/snake/skins/Sri Lankan Green Vine Snake (Ahaetulla nasuta)/vertical.png and /dev/null differ diff --git a/Base/res/graphics/wizard-banner-simple.png b/Base/res/graphics/wizard-banner-simple.png deleted file mode 100644 index aa7fc846745..00000000000 Binary files a/Base/res/graphics/wizard-banner-simple.png and /dev/null differ diff --git a/Base/res/icons/16x16/add-event.png b/Base/res/icons/16x16/add-event.png deleted file mode 100644 index e6c44424cc3..00000000000 Binary files a/Base/res/icons/16x16/add-event.png and /dev/null differ diff --git a/Base/res/icons/16x16/annotation-add.png b/Base/res/icons/16x16/annotation-add.png deleted file mode 100644 index a57ac1b0324..00000000000 Binary files a/Base/res/icons/16x16/annotation-add.png and /dev/null differ diff --git a/Base/res/icons/16x16/annotation-remove.png b/Base/res/icons/16x16/annotation-remove.png deleted file mode 100644 index e997451e7a7..00000000000 Binary files a/Base/res/icons/16x16/annotation-remove.png and /dev/null differ diff --git a/Base/res/icons/16x16/annotation.png b/Base/res/icons/16x16/annotation.png deleted file mode 100644 index e7c3acb98ea..00000000000 Binary files a/Base/res/icons/16x16/annotation.png and /dev/null differ diff --git a/Base/res/icons/16x16/app-2048.png b/Base/res/icons/16x16/app-2048.png deleted file mode 100644 index 7d050e50f53..00000000000 Binary files a/Base/res/icons/16x16/app-2048.png and /dev/null differ diff --git a/Base/res/icons/16x16/app-3d-file-viewer.png b/Base/res/icons/16x16/app-3d-file-viewer.png deleted file mode 100644 index 413df6c15af..00000000000 Binary files a/Base/res/icons/16x16/app-3d-file-viewer.png and /dev/null differ diff --git a/Base/res/icons/16x16/app-analog-clock.png b/Base/res/icons/16x16/app-analog-clock.png deleted file mode 100644 index e57522add34..00000000000 Binary files a/Base/res/icons/16x16/app-analog-clock.png and /dev/null differ diff --git a/Base/res/icons/16x16/app-assistant.png b/Base/res/icons/16x16/app-assistant.png deleted file mode 100644 index dbde07afec6..00000000000 Binary files a/Base/res/icons/16x16/app-assistant.png and /dev/null differ diff --git a/Base/res/icons/16x16/app-brickgame.png b/Base/res/icons/16x16/app-brickgame.png deleted file mode 100644 index 6e1504b1ad8..00000000000 Binary files a/Base/res/icons/16x16/app-brickgame.png and /dev/null differ diff --git a/Base/res/icons/16x16/app-calculator.png b/Base/res/icons/16x16/app-calculator.png deleted file mode 100644 index 703d4b1464e..00000000000 Binary files a/Base/res/icons/16x16/app-calculator.png and /dev/null differ diff --git a/Base/res/icons/16x16/app-calendar.png b/Base/res/icons/16x16/app-calendar.png deleted file mode 100644 index 5a6ae2e51a6..00000000000 Binary files a/Base/res/icons/16x16/app-calendar.png and /dev/null differ diff --git a/Base/res/icons/16x16/app-catdog.png b/Base/res/icons/16x16/app-catdog.png deleted file mode 100644 index 838df17aa64..00000000000 Binary files a/Base/res/icons/16x16/app-catdog.png and /dev/null differ diff --git a/Base/res/icons/16x16/app-character-map.png b/Base/res/icons/16x16/app-character-map.png deleted file mode 100644 index e023a4c226b..00000000000 Binary files a/Base/res/icons/16x16/app-character-map.png and /dev/null differ diff --git a/Base/res/icons/16x16/app-chess.png b/Base/res/icons/16x16/app-chess.png deleted file mode 100644 index 822b8882348..00000000000 Binary files a/Base/res/icons/16x16/app-chess.png and /dev/null differ diff --git a/Base/res/icons/16x16/app-colorlines.png b/Base/res/icons/16x16/app-colorlines.png deleted file mode 100644 index 7f9c5d5bd87..00000000000 Binary files a/Base/res/icons/16x16/app-colorlines.png and /dev/null differ diff --git a/Base/res/icons/16x16/app-crash-reporter.png b/Base/res/icons/16x16/app-crash-reporter.png deleted file mode 100644 index 6fa096f509f..00000000000 Binary files a/Base/res/icons/16x16/app-crash-reporter.png and /dev/null differ diff --git a/Base/res/icons/16x16/app-display-settings.png b/Base/res/icons/16x16/app-display-settings.png deleted file mode 100644 index 8400a37ad43..00000000000 Binary files a/Base/res/icons/16x16/app-display-settings.png and /dev/null differ diff --git a/Base/res/icons/16x16/app-escalator.png b/Base/res/icons/16x16/app-escalator.png deleted file mode 100644 index d238ca0bcef..00000000000 Binary files a/Base/res/icons/16x16/app-escalator.png and /dev/null differ diff --git a/Base/res/icons/16x16/app-eyes.png b/Base/res/icons/16x16/app-eyes.png deleted file mode 100644 index ed3b272e09d..00000000000 Binary files a/Base/res/icons/16x16/app-eyes.png and /dev/null differ diff --git a/Base/res/icons/16x16/app-file-manager.png b/Base/res/icons/16x16/app-file-manager.png deleted file mode 100644 index eb31ec4f004..00000000000 Binary files a/Base/res/icons/16x16/app-file-manager.png and /dev/null differ diff --git a/Base/res/icons/16x16/app-flappybug.png b/Base/res/icons/16x16/app-flappybug.png deleted file mode 100644 index 89f78b99f02..00000000000 Binary files a/Base/res/icons/16x16/app-flappybug.png and /dev/null differ diff --git a/Base/res/icons/16x16/app-flood.png b/Base/res/icons/16x16/app-flood.png deleted file mode 100644 index abb746898da..00000000000 Binary files a/Base/res/icons/16x16/app-flood.png and /dev/null differ diff --git a/Base/res/icons/16x16/app-font-editor.png b/Base/res/icons/16x16/app-font-editor.png deleted file mode 100644 index 9f19ba0a31a..00000000000 Binary files a/Base/res/icons/16x16/app-font-editor.png and /dev/null differ diff --git a/Base/res/icons/16x16/app-gameoflife.png b/Base/res/icons/16x16/app-gameoflife.png deleted file mode 100644 index 158a774090d..00000000000 Binary files a/Base/res/icons/16x16/app-gameoflife.png and /dev/null differ diff --git a/Base/res/icons/16x16/app-gml-playground.png b/Base/res/icons/16x16/app-gml-playground.png deleted file mode 100644 index 3fc21c9466b..00000000000 Binary files a/Base/res/icons/16x16/app-gml-playground.png and /dev/null differ diff --git a/Base/res/icons/16x16/app-gradient.png b/Base/res/icons/16x16/app-gradient.png deleted file mode 100644 index 73c57803779..00000000000 Binary files a/Base/res/icons/16x16/app-gradient.png and /dev/null differ diff --git a/Base/res/icons/16x16/app-hack-studio.png b/Base/res/icons/16x16/app-hack-studio.png deleted file mode 100644 index 602f98ffb80..00000000000 Binary files a/Base/res/icons/16x16/app-hack-studio.png and /dev/null differ diff --git a/Base/res/icons/16x16/app-hearts.png b/Base/res/icons/16x16/app-hearts.png deleted file mode 100644 index 3c07e20a05c..00000000000 Binary files a/Base/res/icons/16x16/app-hearts.png and /dev/null differ diff --git a/Base/res/icons/16x16/app-hello-world.png b/Base/res/icons/16x16/app-hello-world.png deleted file mode 100644 index b12975d382d..00000000000 Binary files a/Base/res/icons/16x16/app-hello-world.png and /dev/null differ diff --git a/Base/res/icons/16x16/app-help.png b/Base/res/icons/16x16/app-help.png deleted file mode 100644 index 3d4abff7837..00000000000 Binary files a/Base/res/icons/16x16/app-help.png and /dev/null differ diff --git a/Base/res/icons/16x16/app-hex-editor.png b/Base/res/icons/16x16/app-hex-editor.png deleted file mode 100644 index 1c099075172..00000000000 Binary files a/Base/res/icons/16x16/app-hex-editor.png and /dev/null differ diff --git a/Base/res/icons/16x16/app-image-viewer.png b/Base/res/icons/16x16/app-image-viewer.png deleted file mode 100644 index 48f24fbbeec..00000000000 Binary files a/Base/res/icons/16x16/app-image-viewer.png and /dev/null differ diff --git a/Base/res/icons/16x16/app-inspector.png b/Base/res/icons/16x16/app-inspector.png deleted file mode 100644 index 330dfc5aa06..00000000000 Binary files a/Base/res/icons/16x16/app-inspector.png and /dev/null differ diff --git a/Base/res/icons/16x16/app-keyboard-mapper.png b/Base/res/icons/16x16/app-keyboard-mapper.png deleted file mode 100644 index 7b26d31812e..00000000000 Binary files a/Base/res/icons/16x16/app-keyboard-mapper.png and /dev/null differ diff --git a/Base/res/icons/16x16/app-keyboard-settings.png b/Base/res/icons/16x16/app-keyboard-settings.png deleted file mode 100644 index 7b26d31812e..00000000000 Binary files a/Base/res/icons/16x16/app-keyboard-settings.png and /dev/null differ diff --git a/Base/res/icons/16x16/app-libgfx-demo.png b/Base/res/icons/16x16/app-libgfx-demo.png deleted file mode 100644 index 5eca9b90315..00000000000 Binary files a/Base/res/icons/16x16/app-libgfx-demo.png and /dev/null differ diff --git a/Base/res/icons/16x16/app-magnifier.png b/Base/res/icons/16x16/app-magnifier.png deleted file mode 100644 index 75d6a82f1c1..00000000000 Binary files a/Base/res/icons/16x16/app-magnifier.png and /dev/null differ diff --git a/Base/res/icons/16x16/app-mail.png b/Base/res/icons/16x16/app-mail.png deleted file mode 100644 index 197a0c4f469..00000000000 Binary files a/Base/res/icons/16x16/app-mail.png and /dev/null differ diff --git a/Base/res/icons/16x16/app-mandelbrot.png b/Base/res/icons/16x16/app-mandelbrot.png deleted file mode 100644 index c7a2e7676d5..00000000000 Binary files a/Base/res/icons/16x16/app-mandelbrot.png and /dev/null differ diff --git a/Base/res/icons/16x16/app-maps.png b/Base/res/icons/16x16/app-maps.png deleted file mode 100644 index 9bfa0ea2ece..00000000000 Binary files a/Base/res/icons/16x16/app-maps.png and /dev/null differ diff --git a/Base/res/icons/16x16/app-masterword.png b/Base/res/icons/16x16/app-masterword.png deleted file mode 100644 index d2cff34ee8e..00000000000 Binary files a/Base/res/icons/16x16/app-masterword.png and /dev/null differ diff --git a/Base/res/icons/16x16/app-minesweeper.png b/Base/res/icons/16x16/app-minesweeper.png deleted file mode 100644 index a1ceac92ec6..00000000000 Binary files a/Base/res/icons/16x16/app-minesweeper.png and /dev/null differ diff --git a/Base/res/icons/16x16/app-model-gallery.png b/Base/res/icons/16x16/app-model-gallery.png deleted file mode 100644 index 59d865b131e..00000000000 Binary files a/Base/res/icons/16x16/app-model-gallery.png and /dev/null differ diff --git a/Base/res/icons/16x16/app-mouse.png b/Base/res/icons/16x16/app-mouse.png deleted file mode 100644 index 69ad6202d23..00000000000 Binary files a/Base/res/icons/16x16/app-mouse.png and /dev/null differ diff --git a/Base/res/icons/16x16/app-partition-editor.png b/Base/res/icons/16x16/app-partition-editor.png deleted file mode 100644 index b09a6abaff6..00000000000 Binary files a/Base/res/icons/16x16/app-partition-editor.png and /dev/null differ diff --git a/Base/res/icons/16x16/app-pdf-viewer.png b/Base/res/icons/16x16/app-pdf-viewer.png deleted file mode 100644 index 830412e82c3..00000000000 Binary files a/Base/res/icons/16x16/app-pdf-viewer.png and /dev/null differ diff --git a/Base/res/icons/16x16/app-piano.png b/Base/res/icons/16x16/app-piano.png deleted file mode 100644 index 7f66ce1ae6b..00000000000 Binary files a/Base/res/icons/16x16/app-piano.png and /dev/null differ diff --git a/Base/res/icons/16x16/app-pixel-paint.png b/Base/res/icons/16x16/app-pixel-paint.png deleted file mode 100644 index db65295934b..00000000000 Binary files a/Base/res/icons/16x16/app-pixel-paint.png and /dev/null differ diff --git a/Base/res/icons/16x16/app-presenter.png b/Base/res/icons/16x16/app-presenter.png deleted file mode 100644 index 478a364a31c..00000000000 Binary files a/Base/res/icons/16x16/app-presenter.png and /dev/null differ diff --git a/Base/res/icons/16x16/app-profiler.png b/Base/res/icons/16x16/app-profiler.png deleted file mode 100644 index 56bac62efe5..00000000000 Binary files a/Base/res/icons/16x16/app-profiler.png and /dev/null differ diff --git a/Base/res/icons/16x16/app-run.png b/Base/res/icons/16x16/app-run.png deleted file mode 100644 index 9cd6ea6ca6a..00000000000 Binary files a/Base/res/icons/16x16/app-run.png and /dev/null differ diff --git a/Base/res/icons/16x16/app-screensaver.png b/Base/res/icons/16x16/app-screensaver.png deleted file mode 100644 index 4f74770fcd3..00000000000 Binary files a/Base/res/icons/16x16/app-screensaver.png and /dev/null differ diff --git a/Base/res/icons/16x16/app-screenshot.png b/Base/res/icons/16x16/app-screenshot.png deleted file mode 100644 index 9f193e69a95..00000000000 Binary files a/Base/res/icons/16x16/app-screenshot.png and /dev/null differ diff --git a/Base/res/icons/16x16/app-settings.png b/Base/res/icons/16x16/app-settings.png deleted file mode 100644 index 69a9c99bf79..00000000000 Binary files a/Base/res/icons/16x16/app-settings.png and /dev/null differ diff --git a/Base/res/icons/16x16/app-snake.png b/Base/res/icons/16x16/app-snake.png deleted file mode 100644 index 24f52bac9d0..00000000000 Binary files a/Base/res/icons/16x16/app-snake.png and /dev/null differ diff --git a/Base/res/icons/16x16/app-solitaire.png b/Base/res/icons/16x16/app-solitaire.png deleted file mode 100644 index ebe649d3351..00000000000 Binary files a/Base/res/icons/16x16/app-solitaire.png and /dev/null differ diff --git a/Base/res/icons/16x16/app-sound-player.png b/Base/res/icons/16x16/app-sound-player.png deleted file mode 100644 index 8bf442a8380..00000000000 Binary files a/Base/res/icons/16x16/app-sound-player.png and /dev/null differ diff --git a/Base/res/icons/16x16/app-space-analyzer.png b/Base/res/icons/16x16/app-space-analyzer.png deleted file mode 100644 index c2052d2b0c9..00000000000 Binary files a/Base/res/icons/16x16/app-space-analyzer.png and /dev/null differ diff --git a/Base/res/icons/16x16/app-spider.png b/Base/res/icons/16x16/app-spider.png deleted file mode 100644 index f71537b63f4..00000000000 Binary files a/Base/res/icons/16x16/app-spider.png and /dev/null differ diff --git a/Base/res/icons/16x16/app-spreadsheet.png b/Base/res/icons/16x16/app-spreadsheet.png deleted file mode 100644 index 0a4db7df6d0..00000000000 Binary files a/Base/res/icons/16x16/app-spreadsheet.png and /dev/null differ diff --git a/Base/res/icons/16x16/app-sql-studio.png b/Base/res/icons/16x16/app-sql-studio.png deleted file mode 100644 index 5f9d8adc6cd..00000000000 Binary files a/Base/res/icons/16x16/app-sql-studio.png and /dev/null differ diff --git a/Base/res/icons/16x16/app-starfield.png b/Base/res/icons/16x16/app-starfield.png deleted file mode 100644 index 4af5063fc20..00000000000 Binary files a/Base/res/icons/16x16/app-starfield.png and /dev/null differ diff --git a/Base/res/icons/16x16/app-terminal.png b/Base/res/icons/16x16/app-terminal.png deleted file mode 100644 index 853fe8e7f9f..00000000000 Binary files a/Base/res/icons/16x16/app-terminal.png and /dev/null differ diff --git a/Base/res/icons/16x16/app-text-editor.png b/Base/res/icons/16x16/app-text-editor.png deleted file mode 100644 index 31b18651885..00000000000 Binary files a/Base/res/icons/16x16/app-text-editor.png and /dev/null differ diff --git a/Base/res/icons/16x16/app-theme-editor.png b/Base/res/icons/16x16/app-theme-editor.png deleted file mode 100644 index 92a640a1c5a..00000000000 Binary files a/Base/res/icons/16x16/app-theme-editor.png and /dev/null differ diff --git a/Base/res/icons/16x16/app-tubes.png b/Base/res/icons/16x16/app-tubes.png deleted file mode 100644 index e4854dfacc8..00000000000 Binary files a/Base/res/icons/16x16/app-tubes.png and /dev/null differ diff --git a/Base/res/icons/16x16/app-video-player.png b/Base/res/icons/16x16/app-video-player.png deleted file mode 100644 index c7ba87be6e8..00000000000 Binary files a/Base/res/icons/16x16/app-video-player.png and /dev/null differ diff --git a/Base/res/icons/16x16/app-welcome.png b/Base/res/icons/16x16/app-welcome.png deleted file mode 100644 index ad71e4cde65..00000000000 Binary files a/Base/res/icons/16x16/app-welcome.png and /dev/null differ diff --git a/Base/res/icons/16x16/app-widget-gallery.png b/Base/res/icons/16x16/app-widget-gallery.png deleted file mode 100644 index bc0453edb14..00000000000 Binary files a/Base/res/icons/16x16/app-widget-gallery.png and /dev/null differ diff --git a/Base/res/icons/16x16/audio-volume-low.png b/Base/res/icons/16x16/audio-volume-low.png deleted file mode 100644 index 41ce1e91691..00000000000 Binary files a/Base/res/icons/16x16/audio-volume-low.png and /dev/null differ diff --git a/Base/res/icons/16x16/audio-volume-medium.png b/Base/res/icons/16x16/audio-volume-medium.png deleted file mode 100644 index 5e38cb9683d..00000000000 Binary files a/Base/res/icons/16x16/audio-volume-medium.png and /dev/null differ diff --git a/Base/res/icons/16x16/audio-volume-zero.png b/Base/res/icons/16x16/audio-volume-zero.png deleted file mode 100644 index 49f75f10584..00000000000 Binary files a/Base/res/icons/16x16/audio-volume-zero.png and /dev/null differ diff --git a/Base/res/icons/16x16/book-open.png b/Base/res/icons/16x16/book-open.png deleted file mode 100644 index 6248e0ba293..00000000000 Binary files a/Base/res/icons/16x16/book-open.png and /dev/null differ diff --git a/Base/res/icons/16x16/book.png b/Base/res/icons/16x16/book.png deleted file mode 100644 index 3d4abff7837..00000000000 Binary files a/Base/res/icons/16x16/book.png and /dev/null differ diff --git a/Base/res/icons/16x16/bookmark-contour.png b/Base/res/icons/16x16/bookmark-contour.png deleted file mode 100644 index ce43036bb28..00000000000 Binary files a/Base/res/icons/16x16/bookmark-contour.png and /dev/null differ diff --git a/Base/res/icons/16x16/bookmark-filled.png b/Base/res/icons/16x16/bookmark-filled.png deleted file mode 100644 index c76976b44e3..00000000000 Binary files a/Base/res/icons/16x16/bookmark-filled.png and /dev/null differ diff --git a/Base/res/icons/16x16/bottom-layer.png b/Base/res/icons/16x16/bottom-layer.png deleted file mode 100644 index bcd05b12283..00000000000 Binary files a/Base/res/icons/16x16/bottom-layer.png and /dev/null differ diff --git a/Base/res/icons/16x16/breakpoint.png b/Base/res/icons/16x16/breakpoint.png deleted file mode 100644 index 743b9db5aaf..00000000000 Binary files a/Base/res/icons/16x16/breakpoint.png and /dev/null differ diff --git a/Base/res/icons/16x16/build.png b/Base/res/icons/16x16/build.png deleted file mode 100644 index 6d4d2b3178d..00000000000 Binary files a/Base/res/icons/16x16/build.png and /dev/null differ diff --git a/Base/res/icons/16x16/c.png b/Base/res/icons/16x16/c.png deleted file mode 100644 index 95d56b9b44b..00000000000 Binary files a/Base/res/icons/16x16/c.png and /dev/null differ diff --git a/Base/res/icons/16x16/calendar-date.png b/Base/res/icons/16x16/calendar-date.png deleted file mode 100644 index eba765a429d..00000000000 Binary files a/Base/res/icons/16x16/calendar-date.png and /dev/null differ diff --git a/Base/res/icons/16x16/calendar-month-view.png b/Base/res/icons/16x16/calendar-month-view.png deleted file mode 100644 index af19dc5fd37..00000000000 Binary files a/Base/res/icons/16x16/calendar-month-view.png and /dev/null differ diff --git a/Base/res/icons/16x16/catdog-sleeping.png b/Base/res/icons/16x16/catdog-sleeping.png deleted file mode 100644 index 60f845ea908..00000000000 Binary files a/Base/res/icons/16x16/catdog-sleeping.png and /dev/null differ diff --git a/Base/res/icons/16x16/catdog-wake-up.png b/Base/res/icons/16x16/catdog-wake-up.png deleted file mode 100644 index 3530fe6214f..00000000000 Binary files a/Base/res/icons/16x16/catdog-wake-up.png and /dev/null differ diff --git a/Base/res/icons/16x16/certificate.png b/Base/res/icons/16x16/certificate.png deleted file mode 100644 index 720f00d66c1..00000000000 Binary files a/Base/res/icons/16x16/certificate.png and /dev/null differ diff --git a/Base/res/icons/16x16/clear-selection.png b/Base/res/icons/16x16/clear-selection.png deleted file mode 100644 index 29b73dbbf12..00000000000 Binary files a/Base/res/icons/16x16/clear-selection.png and /dev/null differ diff --git a/Base/res/icons/16x16/close-other-tabs.png b/Base/res/icons/16x16/close-other-tabs.png deleted file mode 100644 index 1f259ef61e2..00000000000 Binary files a/Base/res/icons/16x16/close-other-tabs.png and /dev/null differ diff --git a/Base/res/icons/16x16/code.png b/Base/res/icons/16x16/code.png deleted file mode 100644 index 1918fbb6cdd..00000000000 Binary files a/Base/res/icons/16x16/code.png and /dev/null differ diff --git a/Base/res/icons/16x16/color-chooser.png b/Base/res/icons/16x16/color-chooser.png deleted file mode 100644 index 97d11b8820c..00000000000 Binary files a/Base/res/icons/16x16/color-chooser.png and /dev/null differ diff --git a/Base/res/icons/16x16/columns-view.png b/Base/res/icons/16x16/columns-view.png deleted file mode 100644 index ade2fd2626f..00000000000 Binary files a/Base/res/icons/16x16/columns-view.png and /dev/null differ diff --git a/Base/res/icons/16x16/commit.png b/Base/res/icons/16x16/commit.png deleted file mode 100644 index 9278ec060b8..00000000000 Binary files a/Base/res/icons/16x16/commit.png and /dev/null differ diff --git a/Base/res/icons/16x16/completion/cpp-identifier.png b/Base/res/icons/16x16/completion/cpp-identifier.png deleted file mode 100644 index 9805fb180e7..00000000000 Binary files a/Base/res/icons/16x16/completion/cpp-identifier.png and /dev/null differ diff --git a/Base/res/icons/16x16/completion/unspecified-identifier.png b/Base/res/icons/16x16/completion/unspecified-identifier.png deleted file mode 100644 index 9fc73d22e81..00000000000 Binary files a/Base/res/icons/16x16/completion/unspecified-identifier.png and /dev/null differ diff --git a/Base/res/icons/16x16/completion/unspecified-unspecified.png b/Base/res/icons/16x16/completion/unspecified-unspecified.png deleted file mode 100644 index 74345c444af..00000000000 Binary files a/Base/res/icons/16x16/completion/unspecified-unspecified.png and /dev/null differ diff --git a/Base/res/icons/16x16/continue.png b/Base/res/icons/16x16/continue.png deleted file mode 100644 index 3898fdf144f..00000000000 Binary files a/Base/res/icons/16x16/continue.png and /dev/null differ diff --git a/Base/res/icons/16x16/debug-continue.png b/Base/res/icons/16x16/debug-continue.png deleted file mode 100644 index 7e2166baf5e..00000000000 Binary files a/Base/res/icons/16x16/debug-continue.png and /dev/null differ diff --git a/Base/res/icons/16x16/debug-pause.png b/Base/res/icons/16x16/debug-pause.png deleted file mode 100644 index a93a815ea31..00000000000 Binary files a/Base/res/icons/16x16/debug-pause.png and /dev/null differ diff --git a/Base/res/icons/16x16/debug-run.png b/Base/res/icons/16x16/debug-run.png deleted file mode 100644 index b244bb64aa0..00000000000 Binary files a/Base/res/icons/16x16/debug-run.png and /dev/null differ diff --git a/Base/res/icons/16x16/debug-step-in.png b/Base/res/icons/16x16/debug-step-in.png deleted file mode 100644 index 00c86d98f28..00000000000 Binary files a/Base/res/icons/16x16/debug-step-in.png and /dev/null differ diff --git a/Base/res/icons/16x16/debug-step-out.png b/Base/res/icons/16x16/debug-step-out.png deleted file mode 100644 index 477a346067b..00000000000 Binary files a/Base/res/icons/16x16/debug-step-out.png and /dev/null differ diff --git a/Base/res/icons/16x16/debug-step-over.png b/Base/res/icons/16x16/debug-step-over.png deleted file mode 100644 index a61b4fb6aaf..00000000000 Binary files a/Base/res/icons/16x16/debug-step-over.png and /dev/null differ diff --git a/Base/res/icons/16x16/delete.png b/Base/res/icons/16x16/delete.png deleted file mode 100644 index f271da46e77..00000000000 Binary files a/Base/res/icons/16x16/delete.png and /dev/null differ diff --git a/Base/res/icons/16x16/demos.png b/Base/res/icons/16x16/demos.png deleted file mode 100644 index fb7e1a1ebb5..00000000000 Binary files a/Base/res/icons/16x16/demos.png and /dev/null differ diff --git a/Base/res/icons/16x16/desktop.png b/Base/res/icons/16x16/desktop.png deleted file mode 100644 index 08bff67889e..00000000000 Binary files a/Base/res/icons/16x16/desktop.png and /dev/null differ diff --git a/Base/res/icons/16x16/detach.png b/Base/res/icons/16x16/detach.png deleted file mode 100644 index 17a888f3d6d..00000000000 Binary files a/Base/res/icons/16x16/detach.png and /dev/null differ diff --git a/Base/res/icons/16x16/development.png b/Base/res/icons/16x16/development.png deleted file mode 100644 index 4d90be3b51a..00000000000 Binary files a/Base/res/icons/16x16/development.png and /dev/null differ diff --git a/Base/res/icons/16x16/download.png b/Base/res/icons/16x16/download.png deleted file mode 100644 index 7836d3c3411..00000000000 Binary files a/Base/res/icons/16x16/download.png and /dev/null differ diff --git a/Base/res/icons/16x16/downward-triangle-2x.png b/Base/res/icons/16x16/downward-triangle-2x.png deleted file mode 100644 index 4242e167f99..00000000000 Binary files a/Base/res/icons/16x16/downward-triangle-2x.png and /dev/null differ diff --git a/Base/res/icons/16x16/downward-triangle.png b/Base/res/icons/16x16/downward-triangle.png deleted file mode 100644 index 72ddab9fc5e..00000000000 Binary files a/Base/res/icons/16x16/downward-triangle.png and /dev/null differ diff --git a/Base/res/icons/16x16/duplicate-tab.png b/Base/res/icons/16x16/duplicate-tab.png deleted file mode 100644 index 4df56f7f90f..00000000000 Binary files a/Base/res/icons/16x16/duplicate-tab.png and /dev/null differ diff --git a/Base/res/icons/16x16/edit-cut.png b/Base/res/icons/16x16/edit-cut.png deleted file mode 100644 index f58822be584..00000000000 Binary files a/Base/res/icons/16x16/edit-cut.png and /dev/null differ diff --git a/Base/res/icons/16x16/edit-flip-horizontal.png b/Base/res/icons/16x16/edit-flip-horizontal.png deleted file mode 100644 index d7f0a3848ca..00000000000 Binary files a/Base/res/icons/16x16/edit-flip-horizontal.png and /dev/null differ diff --git a/Base/res/icons/16x16/edit-flip-vertical.png b/Base/res/icons/16x16/edit-flip-vertical.png deleted file mode 100644 index 1139a07e330..00000000000 Binary files a/Base/res/icons/16x16/edit-flip-vertical.png and /dev/null differ diff --git a/Base/res/icons/16x16/edit-rotate-ccw.png b/Base/res/icons/16x16/edit-rotate-ccw.png deleted file mode 100644 index 881556abf71..00000000000 Binary files a/Base/res/icons/16x16/edit-rotate-ccw.png and /dev/null differ diff --git a/Base/res/icons/16x16/edit-rotate-cw.png b/Base/res/icons/16x16/edit-rotate-cw.png deleted file mode 100644 index 384b388c7dc..00000000000 Binary files a/Base/res/icons/16x16/edit-rotate-cw.png and /dev/null differ diff --git a/Base/res/icons/16x16/emoji.png b/Base/res/icons/16x16/emoji.png deleted file mode 100644 index 741fc047bd0..00000000000 Binary files a/Base/res/icons/16x16/emoji.png and /dev/null differ diff --git a/Base/res/icons/16x16/file-export.png b/Base/res/icons/16x16/file-export.png deleted file mode 100644 index 771ffba83eb..00000000000 Binary files a/Base/res/icons/16x16/file-export.png and /dev/null differ diff --git a/Base/res/icons/16x16/filetype-archive.png b/Base/res/icons/16x16/filetype-archive.png deleted file mode 100644 index 09060f6b701..00000000000 Binary files a/Base/res/icons/16x16/filetype-archive.png and /dev/null differ diff --git a/Base/res/icons/16x16/filetype-asm.png b/Base/res/icons/16x16/filetype-asm.png deleted file mode 100644 index 97f91d59d51..00000000000 Binary files a/Base/res/icons/16x16/filetype-asm.png and /dev/null differ diff --git a/Base/res/icons/16x16/filetype-c.png b/Base/res/icons/16x16/filetype-c.png deleted file mode 100644 index a59ed7ca982..00000000000 Binary files a/Base/res/icons/16x16/filetype-c.png and /dev/null differ diff --git a/Base/res/icons/16x16/filetype-calendar.png b/Base/res/icons/16x16/filetype-calendar.png deleted file mode 100644 index 90296ab2e50..00000000000 Binary files a/Base/res/icons/16x16/filetype-calendar.png and /dev/null differ diff --git a/Base/res/icons/16x16/filetype-cmake.png b/Base/res/icons/16x16/filetype-cmake.png deleted file mode 100644 index fc518092ed6..00000000000 Binary files a/Base/res/icons/16x16/filetype-cmake.png and /dev/null differ diff --git a/Base/res/icons/16x16/filetype-cplusplus.png b/Base/res/icons/16x16/filetype-cplusplus.png deleted file mode 100644 index 12801cc7714..00000000000 Binary files a/Base/res/icons/16x16/filetype-cplusplus.png and /dev/null differ diff --git a/Base/res/icons/16x16/filetype-db.png b/Base/res/icons/16x16/filetype-db.png deleted file mode 100644 index 3dcb5689bce..00000000000 Binary files a/Base/res/icons/16x16/filetype-db.png and /dev/null differ diff --git a/Base/res/icons/16x16/filetype-executable.png b/Base/res/icons/16x16/filetype-executable.png deleted file mode 100644 index 283a293f58d..00000000000 Binary files a/Base/res/icons/16x16/filetype-executable.png and /dev/null differ diff --git a/Base/res/icons/16x16/filetype-folder-inaccessible.png b/Base/res/icons/16x16/filetype-folder-inaccessible.png deleted file mode 100644 index 04443ae8374..00000000000 Binary files a/Base/res/icons/16x16/filetype-folder-inaccessible.png and /dev/null differ diff --git a/Base/res/icons/16x16/filetype-folder.png b/Base/res/icons/16x16/filetype-folder.png deleted file mode 100644 index d2025eb729d..00000000000 Binary files a/Base/res/icons/16x16/filetype-folder.png and /dev/null differ diff --git a/Base/res/icons/16x16/filetype-font.png b/Base/res/icons/16x16/filetype-font.png deleted file mode 100644 index 3ab6bc98061..00000000000 Binary files a/Base/res/icons/16x16/filetype-font.png and /dev/null differ diff --git a/Base/res/icons/16x16/filetype-form.png b/Base/res/icons/16x16/filetype-form.png deleted file mode 100644 index 1301acee071..00000000000 Binary files a/Base/res/icons/16x16/filetype-form.png and /dev/null differ diff --git a/Base/res/icons/16x16/filetype-git.png b/Base/res/icons/16x16/filetype-git.png deleted file mode 100644 index a519989785d..00000000000 Binary files a/Base/res/icons/16x16/filetype-git.png and /dev/null differ diff --git a/Base/res/icons/16x16/filetype-gml.png b/Base/res/icons/16x16/filetype-gml.png deleted file mode 100644 index b7be944e8df..00000000000 Binary files a/Base/res/icons/16x16/filetype-gml.png and /dev/null differ diff --git a/Base/res/icons/16x16/filetype-header.png b/Base/res/icons/16x16/filetype-header.png deleted file mode 100644 index cd27752a03e..00000000000 Binary files a/Base/res/icons/16x16/filetype-header.png and /dev/null differ diff --git a/Base/res/icons/16x16/filetype-ini.png b/Base/res/icons/16x16/filetype-ini.png deleted file mode 100644 index bde6a686d80..00000000000 Binary files a/Base/res/icons/16x16/filetype-ini.png and /dev/null differ diff --git a/Base/res/icons/16x16/filetype-java.png b/Base/res/icons/16x16/filetype-java.png deleted file mode 100644 index d3f1a400d91..00000000000 Binary files a/Base/res/icons/16x16/filetype-java.png and /dev/null differ diff --git a/Base/res/icons/16x16/filetype-javascript.png b/Base/res/icons/16x16/filetype-javascript.png deleted file mode 100644 index 4ab652099ed..00000000000 Binary files a/Base/res/icons/16x16/filetype-javascript.png and /dev/null differ diff --git a/Base/res/icons/16x16/filetype-json.png b/Base/res/icons/16x16/filetype-json.png deleted file mode 100644 index 8ff68ff8dae..00000000000 Binary files a/Base/res/icons/16x16/filetype-json.png and /dev/null differ diff --git a/Base/res/icons/16x16/filetype-library.png b/Base/res/icons/16x16/filetype-library.png deleted file mode 100644 index 705f2282546..00000000000 Binary files a/Base/res/icons/16x16/filetype-library.png and /dev/null differ diff --git a/Base/res/icons/16x16/filetype-markdown.png b/Base/res/icons/16x16/filetype-markdown.png deleted file mode 100644 index e24f420fb1c..00000000000 Binary files a/Base/res/icons/16x16/filetype-markdown.png and /dev/null differ diff --git a/Base/res/icons/16x16/filetype-music.png b/Base/res/icons/16x16/filetype-music.png deleted file mode 100644 index 5fb71b2dc1e..00000000000 Binary files a/Base/res/icons/16x16/filetype-music.png and /dev/null differ diff --git a/Base/res/icons/16x16/filetype-object.png b/Base/res/icons/16x16/filetype-object.png deleted file mode 100644 index 3fe856eee4c..00000000000 Binary files a/Base/res/icons/16x16/filetype-object.png and /dev/null differ diff --git a/Base/res/icons/16x16/filetype-palette.png b/Base/res/icons/16x16/filetype-palette.png deleted file mode 100644 index c107f93b644..00000000000 Binary files a/Base/res/icons/16x16/filetype-palette.png and /dev/null differ diff --git a/Base/res/icons/16x16/filetype-pdf.png b/Base/res/icons/16x16/filetype-pdf.png deleted file mode 100644 index bd96098685b..00000000000 Binary files a/Base/res/icons/16x16/filetype-pdf.png and /dev/null differ diff --git a/Base/res/icons/16x16/filetype-php.png b/Base/res/icons/16x16/filetype-php.png deleted file mode 100644 index c097ff6621c..00000000000 Binary files a/Base/res/icons/16x16/filetype-php.png and /dev/null differ diff --git a/Base/res/icons/16x16/filetype-pixelpaint.png b/Base/res/icons/16x16/filetype-pixelpaint.png deleted file mode 100644 index ca6b2f8f992..00000000000 Binary files a/Base/res/icons/16x16/filetype-pixelpaint.png and /dev/null differ diff --git a/Base/res/icons/16x16/filetype-presenter.png b/Base/res/icons/16x16/filetype-presenter.png deleted file mode 100644 index 2acd307fd66..00000000000 Binary files a/Base/res/icons/16x16/filetype-presenter.png and /dev/null differ diff --git a/Base/res/icons/16x16/filetype-python.png b/Base/res/icons/16x16/filetype-python.png deleted file mode 100644 index 0dac7507cc2..00000000000 Binary files a/Base/res/icons/16x16/filetype-python.png and /dev/null differ diff --git a/Base/res/icons/16x16/filetype-ruby.png b/Base/res/icons/16x16/filetype-ruby.png deleted file mode 100644 index 3ae83052484..00000000000 Binary files a/Base/res/icons/16x16/filetype-ruby.png and /dev/null differ diff --git a/Base/res/icons/16x16/filetype-shell.png b/Base/res/icons/16x16/filetype-shell.png deleted file mode 100644 index 4ced6f074cf..00000000000 Binary files a/Base/res/icons/16x16/filetype-shell.png and /dev/null differ diff --git a/Base/res/icons/16x16/filetype-socket.png b/Base/res/icons/16x16/filetype-socket.png deleted file mode 100644 index 22d801efc59..00000000000 Binary files a/Base/res/icons/16x16/filetype-socket.png and /dev/null differ diff --git a/Base/res/icons/16x16/filetype-spreadsheet.png b/Base/res/icons/16x16/filetype-spreadsheet.png deleted file mode 100644 index 1f2b51dc5aa..00000000000 Binary files a/Base/res/icons/16x16/filetype-spreadsheet.png and /dev/null differ diff --git a/Base/res/icons/16x16/filetype-sql.png b/Base/res/icons/16x16/filetype-sql.png deleted file mode 100644 index c1c2af07f58..00000000000 Binary files a/Base/res/icons/16x16/filetype-sql.png and /dev/null differ diff --git a/Base/res/icons/16x16/filetype-symlink.png b/Base/res/icons/16x16/filetype-symlink.png deleted file mode 100644 index 5683145bf90..00000000000 Binary files a/Base/res/icons/16x16/filetype-symlink.png and /dev/null differ diff --git a/Base/res/icons/16x16/filetype-text.png b/Base/res/icons/16x16/filetype-text.png deleted file mode 100644 index 0e818e300e9..00000000000 Binary files a/Base/res/icons/16x16/filetype-text.png and /dev/null differ diff --git a/Base/res/icons/16x16/filetype-truetype.png b/Base/res/icons/16x16/filetype-truetype.png deleted file mode 100644 index 1d5633e5d1b..00000000000 Binary files a/Base/res/icons/16x16/filetype-truetype.png and /dev/null differ diff --git a/Base/res/icons/16x16/filetype-unknown.png b/Base/res/icons/16x16/filetype-unknown.png deleted file mode 100644 index dd6c0ceb2c1..00000000000 Binary files a/Base/res/icons/16x16/filetype-unknown.png and /dev/null differ diff --git a/Base/res/icons/16x16/filetype-wasm.png b/Base/res/icons/16x16/filetype-wasm.png deleted file mode 100644 index 4e986f4e165..00000000000 Binary files a/Base/res/icons/16x16/filetype-wasm.png and /dev/null differ diff --git a/Base/res/icons/16x16/find-next.png b/Base/res/icons/16x16/find-next.png deleted file mode 100644 index 85f312b8419..00000000000 Binary files a/Base/res/icons/16x16/find-next.png and /dev/null differ diff --git a/Base/res/icons/16x16/find-previous.png b/Base/res/icons/16x16/find-previous.png deleted file mode 100644 index 8ac6d941658..00000000000 Binary files a/Base/res/icons/16x16/find-previous.png and /dev/null differ diff --git a/Base/res/icons/16x16/fit-image-to-view.png b/Base/res/icons/16x16/fit-image-to-view.png deleted file mode 100644 index 8e0c765aaf5..00000000000 Binary files a/Base/res/icons/16x16/fit-image-to-view.png and /dev/null differ diff --git a/Base/res/icons/16x16/fullscreen.png b/Base/res/icons/16x16/fullscreen.png deleted file mode 100644 index 1a26e81fc91..00000000000 Binary files a/Base/res/icons/16x16/fullscreen.png and /dev/null differ diff --git a/Base/res/icons/16x16/games.png b/Base/res/icons/16x16/games.png deleted file mode 100644 index 87136fc79a3..00000000000 Binary files a/Base/res/icons/16x16/games.png and /dev/null differ diff --git a/Base/res/icons/16x16/gear.png b/Base/res/icons/16x16/gear.png deleted file mode 100644 index f015ba37c72..00000000000 Binary files a/Base/res/icons/16x16/gear.png and /dev/null differ diff --git a/Base/res/icons/16x16/git-directory-open.png b/Base/res/icons/16x16/git-directory-open.png deleted file mode 100644 index af9a76b0c93..00000000000 Binary files a/Base/res/icons/16x16/git-directory-open.png and /dev/null differ diff --git a/Base/res/icons/16x16/git-directory.png b/Base/res/icons/16x16/git-directory.png deleted file mode 100644 index 249fdef1a78..00000000000 Binary files a/Base/res/icons/16x16/git-directory.png and /dev/null differ diff --git a/Base/res/icons/16x16/go-back.png b/Base/res/icons/16x16/go-back.png deleted file mode 100644 index bc40b051f49..00000000000 Binary files a/Base/res/icons/16x16/go-back.png and /dev/null differ diff --git a/Base/res/icons/16x16/go-down.png b/Base/res/icons/16x16/go-down.png deleted file mode 100644 index 3fffb0f73fd..00000000000 Binary files a/Base/res/icons/16x16/go-down.png and /dev/null differ diff --git a/Base/res/icons/16x16/go-first.png b/Base/res/icons/16x16/go-first.png deleted file mode 100644 index 64fe008e39a..00000000000 Binary files a/Base/res/icons/16x16/go-first.png and /dev/null differ diff --git a/Base/res/icons/16x16/go-home.png b/Base/res/icons/16x16/go-home.png deleted file mode 100644 index 1ec4db5719d..00000000000 Binary files a/Base/res/icons/16x16/go-home.png and /dev/null differ diff --git a/Base/res/icons/16x16/go-last.png b/Base/res/icons/16x16/go-last.png deleted file mode 100644 index 5e0d37b6163..00000000000 Binary files a/Base/res/icons/16x16/go-last.png and /dev/null differ diff --git a/Base/res/icons/16x16/go-to.png b/Base/res/icons/16x16/go-to.png deleted file mode 100644 index 7da854c2065..00000000000 Binary files a/Base/res/icons/16x16/go-to.png and /dev/null differ diff --git a/Base/res/icons/16x16/go-up.png b/Base/res/icons/16x16/go-up.png deleted file mode 100644 index 0a7d83c2e49..00000000000 Binary files a/Base/res/icons/16x16/go-up.png and /dev/null differ diff --git a/Base/res/icons/16x16/graphics.png b/Base/res/icons/16x16/graphics.png deleted file mode 100644 index 753f26fe9f3..00000000000 Binary files a/Base/res/icons/16x16/graphics.png and /dev/null differ diff --git a/Base/res/icons/16x16/hackstudio-project.png b/Base/res/icons/16x16/hackstudio-project.png deleted file mode 100644 index 8afb7a6759e..00000000000 Binary files a/Base/res/icons/16x16/hackstudio-project.png and /dev/null differ diff --git a/Base/res/icons/16x16/hard-disk.png b/Base/res/icons/16x16/hard-disk.png deleted file mode 100644 index 50036de5b7f..00000000000 Binary files a/Base/res/icons/16x16/hard-disk.png and /dev/null differ diff --git a/Base/res/icons/16x16/hex.png b/Base/res/icons/16x16/hex.png deleted file mode 100644 index bc17323317c..00000000000 Binary files a/Base/res/icons/16x16/hex.png and /dev/null differ diff --git a/Base/res/icons/16x16/highpriority.png b/Base/res/icons/16x16/highpriority.png deleted file mode 100644 index 8146b503a12..00000000000 Binary files a/Base/res/icons/16x16/highpriority.png and /dev/null differ diff --git a/Base/res/icons/16x16/home-directory-open.png b/Base/res/icons/16x16/home-directory-open.png deleted file mode 100644 index 57044db6368..00000000000 Binary files a/Base/res/icons/16x16/home-directory-open.png and /dev/null differ diff --git a/Base/res/icons/16x16/home-directory.png b/Base/res/icons/16x16/home-directory.png deleted file mode 100644 index 1b4d2ce9109..00000000000 Binary files a/Base/res/icons/16x16/home-directory.png and /dev/null differ diff --git a/Base/res/icons/16x16/icon-view.png b/Base/res/icons/16x16/icon-view.png deleted file mode 100644 index babcad5bd99..00000000000 Binary files a/Base/res/icons/16x16/icon-view.png and /dev/null differ diff --git a/Base/res/icons/16x16/inspect.png b/Base/res/icons/16x16/inspect.png deleted file mode 100644 index 266a8a0921f..00000000000 Binary files a/Base/res/icons/16x16/inspect.png and /dev/null differ diff --git a/Base/res/icons/16x16/inspector-object-red.png b/Base/res/icons/16x16/inspector-object-red.png deleted file mode 100644 index c73803f4901..00000000000 Binary files a/Base/res/icons/16x16/inspector-object-red.png and /dev/null differ diff --git a/Base/res/icons/16x16/inspector-object.png b/Base/res/icons/16x16/inspector-object.png deleted file mode 100644 index 98cb80e1108..00000000000 Binary files a/Base/res/icons/16x16/inspector-object.png and /dev/null differ diff --git a/Base/res/icons/16x16/internet.png b/Base/res/icons/16x16/internet.png deleted file mode 100644 index 2f2bafc0428..00000000000 Binary files a/Base/res/icons/16x16/internet.png and /dev/null differ diff --git a/Base/res/icons/16x16/kill.png b/Base/res/icons/16x16/kill.png deleted file mode 100644 index a6c47804aa0..00000000000 Binary files a/Base/res/icons/16x16/kill.png and /dev/null differ diff --git a/Base/res/icons/16x16/ladyball.png b/Base/res/icons/16x16/ladyball.png deleted file mode 100644 index eca1d674ade..00000000000 Binary files a/Base/res/icons/16x16/ladyball.png and /dev/null differ diff --git a/Base/res/icons/16x16/ladybug.png b/Base/res/icons/16x16/ladybug.png deleted file mode 100644 index 0fa2215de49..00000000000 Binary files a/Base/res/icons/16x16/ladybug.png and /dev/null differ diff --git a/Base/res/icons/16x16/layout-horizontally.png b/Base/res/icons/16x16/layout-horizontally.png deleted file mode 100644 index 45caadb1317..00000000000 Binary files a/Base/res/icons/16x16/layout-horizontally.png and /dev/null differ diff --git a/Base/res/icons/16x16/layout-vertically.png b/Base/res/icons/16x16/layout-vertically.png deleted file mode 100644 index 21bd239d949..00000000000 Binary files a/Base/res/icons/16x16/layout-vertically.png and /dev/null differ diff --git a/Base/res/icons/16x16/lowpriority.png b/Base/res/icons/16x16/lowpriority.png deleted file mode 100644 index 85dc6ef2192..00000000000 Binary files a/Base/res/icons/16x16/lowpriority.png and /dev/null differ diff --git a/Base/res/icons/16x16/minus.png b/Base/res/icons/16x16/minus.png deleted file mode 100644 index 4288ca17d23..00000000000 Binary files a/Base/res/icons/16x16/minus.png and /dev/null differ diff --git a/Base/res/icons/16x16/mkdir.png b/Base/res/icons/16x16/mkdir.png deleted file mode 100644 index 9889e6d479d..00000000000 Binary files a/Base/res/icons/16x16/mkdir.png and /dev/null differ diff --git a/Base/res/icons/16x16/move-to-back.png b/Base/res/icons/16x16/move-to-back.png deleted file mode 100644 index d2a8998ba3d..00000000000 Binary files a/Base/res/icons/16x16/move-to-back.png and /dev/null differ diff --git a/Base/res/icons/16x16/move-to-front.png b/Base/res/icons/16x16/move-to-front.png deleted file mode 100644 index d49f071deb9..00000000000 Binary files a/Base/res/icons/16x16/move-to-front.png and /dev/null differ diff --git a/Base/res/icons/16x16/move.png b/Base/res/icons/16x16/move.png deleted file mode 100644 index 6a2f3cd5803..00000000000 Binary files a/Base/res/icons/16x16/move.png and /dev/null differ diff --git a/Base/res/icons/16x16/multimedia.png b/Base/res/icons/16x16/multimedia.png deleted file mode 100644 index a5d95e24ff9..00000000000 Binary files a/Base/res/icons/16x16/multimedia.png and /dev/null differ diff --git a/Base/res/icons/16x16/network-connected.png b/Base/res/icons/16x16/network-connected.png deleted file mode 100644 index 94bcb60aecc..00000000000 Binary files a/Base/res/icons/16x16/network-connected.png and /dev/null differ diff --git a/Base/res/icons/16x16/network-disconnected.png b/Base/res/icons/16x16/network-disconnected.png deleted file mode 100644 index 90ae65ae152..00000000000 Binary files a/Base/res/icons/16x16/network-disconnected.png and /dev/null differ diff --git a/Base/res/icons/16x16/network.png b/Base/res/icons/16x16/network.png deleted file mode 100644 index a784ac12062..00000000000 Binary files a/Base/res/icons/16x16/network.png and /dev/null differ diff --git a/Base/res/icons/16x16/new-layer.png b/Base/res/icons/16x16/new-layer.png deleted file mode 100644 index 9b3b758a1fa..00000000000 Binary files a/Base/res/icons/16x16/new-layer.png and /dev/null differ diff --git a/Base/res/icons/16x16/new-window.png b/Base/res/icons/16x16/new-window.png deleted file mode 100644 index 6b99ea06c7d..00000000000 Binary files a/Base/res/icons/16x16/new-window.png and /dev/null differ diff --git a/Base/res/icons/16x16/new.png b/Base/res/icons/16x16/new.png deleted file mode 100644 index f33effb223c..00000000000 Binary files a/Base/res/icons/16x16/new.png and /dev/null differ diff --git a/Base/res/icons/16x16/normalpriority.png b/Base/res/icons/16x16/normalpriority.png deleted file mode 100644 index ef89d0728a8..00000000000 Binary files a/Base/res/icons/16x16/normalpriority.png and /dev/null differ diff --git a/Base/res/icons/16x16/office.png b/Base/res/icons/16x16/office.png deleted file mode 100644 index 3c71e0b4d41..00000000000 Binary files a/Base/res/icons/16x16/office.png and /dev/null differ diff --git a/Base/res/icons/16x16/open-recent.png b/Base/res/icons/16x16/open-recent.png deleted file mode 100644 index f45ab445b4b..00000000000 Binary files a/Base/res/icons/16x16/open-recent.png and /dev/null differ diff --git a/Base/res/icons/16x16/open.png b/Base/res/icons/16x16/open.png deleted file mode 100644 index e37f620b6b6..00000000000 Binary files a/Base/res/icons/16x16/open.png and /dev/null differ diff --git a/Base/res/icons/16x16/overflow-menu.png b/Base/res/icons/16x16/overflow-menu.png deleted file mode 100644 index 39ec9bf03eb..00000000000 Binary files a/Base/res/icons/16x16/overflow-menu.png and /dev/null differ diff --git a/Base/res/icons/16x16/play-debug.png b/Base/res/icons/16x16/play-debug.png deleted file mode 100644 index 3de78d4c35d..00000000000 Binary files a/Base/res/icons/16x16/play-debug.png and /dev/null differ diff --git a/Base/res/icons/16x16/plus.png b/Base/res/icons/16x16/plus.png deleted file mode 100644 index c3583756fc4..00000000000 Binary files a/Base/res/icons/16x16/plus.png and /dev/null differ diff --git a/Base/res/icons/16x16/power.png b/Base/res/icons/16x16/power.png deleted file mode 100644 index 95ddabe0cb6..00000000000 Binary files a/Base/res/icons/16x16/power.png and /dev/null differ diff --git a/Base/res/icons/16x16/program-pause.png b/Base/res/icons/16x16/program-pause.png deleted file mode 100644 index 3e3870cd7ed..00000000000 Binary files a/Base/res/icons/16x16/program-pause.png and /dev/null differ diff --git a/Base/res/icons/16x16/program-run.png b/Base/res/icons/16x16/program-run.png deleted file mode 100644 index f291bba4d99..00000000000 Binary files a/Base/res/icons/16x16/program-run.png and /dev/null differ diff --git a/Base/res/icons/16x16/program-stop.png b/Base/res/icons/16x16/program-stop.png deleted file mode 100644 index 9dfe0420e2a..00000000000 Binary files a/Base/res/icons/16x16/program-stop.png and /dev/null differ diff --git a/Base/res/icons/16x16/properties.png b/Base/res/icons/16x16/properties.png deleted file mode 100644 index c76746c6b6a..00000000000 Binary files a/Base/res/icons/16x16/properties.png and /dev/null differ diff --git a/Base/res/icons/16x16/redo.png b/Base/res/icons/16x16/redo.png deleted file mode 100644 index f109d7fa665..00000000000 Binary files a/Base/res/icons/16x16/redo.png and /dev/null differ diff --git a/Base/res/icons/16x16/reformat.png b/Base/res/icons/16x16/reformat.png deleted file mode 100644 index 7621efe633f..00000000000 Binary files a/Base/res/icons/16x16/reformat.png and /dev/null differ diff --git a/Base/res/icons/16x16/reload.png b/Base/res/icons/16x16/reload.png deleted file mode 100644 index 6c4ed3a9a15..00000000000 Binary files a/Base/res/icons/16x16/reload.png and /dev/null differ diff --git a/Base/res/icons/16x16/rename.png b/Base/res/icons/16x16/rename.png deleted file mode 100644 index f9a6eae3708..00000000000 Binary files a/Base/res/icons/16x16/rename.png and /dev/null differ diff --git a/Base/res/icons/16x16/run.png b/Base/res/icons/16x16/run.png deleted file mode 100644 index cb0b67d5b46..00000000000 Binary files a/Base/res/icons/16x16/run.png and /dev/null differ diff --git a/Base/res/icons/16x16/save-as.png b/Base/res/icons/16x16/save-as.png deleted file mode 100644 index 76e63971bac..00000000000 Binary files a/Base/res/icons/16x16/save-as.png and /dev/null differ diff --git a/Base/res/icons/16x16/save.png b/Base/res/icons/16x16/save.png deleted file mode 100644 index 2ae0eec0397..00000000000 Binary files a/Base/res/icons/16x16/save.png and /dev/null differ diff --git a/Base/res/icons/16x16/scale.png b/Base/res/icons/16x16/scale.png deleted file mode 100644 index 0bd55262884..00000000000 Binary files a/Base/res/icons/16x16/scale.png and /dev/null differ diff --git a/Base/res/icons/16x16/selection-move.png b/Base/res/icons/16x16/selection-move.png deleted file mode 100644 index eee0e8dfca5..00000000000 Binary files a/Base/res/icons/16x16/selection-move.png and /dev/null differ diff --git a/Base/res/icons/16x16/serenity.ico b/Base/res/icons/16x16/serenity.ico deleted file mode 100644 index 89dd18839b9..00000000000 Binary files a/Base/res/icons/16x16/serenity.ico and /dev/null differ diff --git a/Base/res/icons/16x16/sidebar.png b/Base/res/icons/16x16/sidebar.png deleted file mode 100644 index 04e0941b6e3..00000000000 Binary files a/Base/res/icons/16x16/sidebar.png and /dev/null differ diff --git a/Base/res/icons/16x16/stop-hand.png b/Base/res/icons/16x16/stop-hand.png deleted file mode 100644 index a7b44fb6ed2..00000000000 Binary files a/Base/res/icons/16x16/stop-hand.png and /dev/null differ diff --git a/Base/res/icons/16x16/stop.png b/Base/res/icons/16x16/stop.png deleted file mode 100644 index 31cfb7ed99f..00000000000 Binary files a/Base/res/icons/16x16/stop.png and /dev/null differ diff --git a/Base/res/icons/16x16/table-view.png b/Base/res/icons/16x16/table-view.png deleted file mode 100644 index 72c01aaabd1..00000000000 Binary files a/Base/res/icons/16x16/table-view.png and /dev/null differ diff --git a/Base/res/icons/16x16/text-color.png b/Base/res/icons/16x16/text-color.png deleted file mode 100644 index e972bbce1b1..00000000000 Binary files a/Base/res/icons/16x16/text-color.png and /dev/null differ diff --git a/Base/res/icons/16x16/themes.png b/Base/res/icons/16x16/themes.png deleted file mode 100644 index f511578d60a..00000000000 Binary files a/Base/res/icons/16x16/themes.png and /dev/null differ diff --git a/Base/res/icons/16x16/timer.png b/Base/res/icons/16x16/timer.png deleted file mode 100644 index 151c5aab624..00000000000 Binary files a/Base/res/icons/16x16/timer.png and /dev/null differ diff --git a/Base/res/icons/16x16/top-layer.png b/Base/res/icons/16x16/top-layer.png deleted file mode 100644 index 84b8fd318d5..00000000000 Binary files a/Base/res/icons/16x16/top-layer.png and /dev/null differ diff --git a/Base/res/icons/16x16/undo.png b/Base/res/icons/16x16/undo.png deleted file mode 100644 index 88422a1feab..00000000000 Binary files a/Base/res/icons/16x16/undo.png and /dev/null differ diff --git a/Base/res/icons/16x16/upward-triangle-2x.png b/Base/res/icons/16x16/upward-triangle-2x.png deleted file mode 100644 index 80621f3ac35..00000000000 Binary files a/Base/res/icons/16x16/upward-triangle-2x.png and /dev/null differ diff --git a/Base/res/icons/16x16/upward-triangle.png b/Base/res/icons/16x16/upward-triangle.png deleted file mode 100644 index ef1fd575788..00000000000 Binary files a/Base/res/icons/16x16/upward-triangle.png and /dev/null differ diff --git a/Base/res/icons/16x16/utilities.png b/Base/res/icons/16x16/utilities.png deleted file mode 100644 index 48bf310299b..00000000000 Binary files a/Base/res/icons/16x16/utilities.png and /dev/null differ diff --git a/Base/res/icons/16x16/window-close-2x.png b/Base/res/icons/16x16/window-close-2x.png deleted file mode 100644 index 1a56ad7f9de..00000000000 Binary files a/Base/res/icons/16x16/window-close-2x.png and /dev/null differ diff --git a/Base/res/icons/16x16/window-close-modified-2x.png b/Base/res/icons/16x16/window-close-modified-2x.png deleted file mode 100644 index f714d1a2e63..00000000000 Binary files a/Base/res/icons/16x16/window-close-modified-2x.png and /dev/null differ diff --git a/Base/res/icons/16x16/window-close-modified.png b/Base/res/icons/16x16/window-close-modified.png deleted file mode 100644 index 95657d3e4c4..00000000000 Binary files a/Base/res/icons/16x16/window-close-modified.png and /dev/null differ diff --git a/Base/res/icons/16x16/window-close.png b/Base/res/icons/16x16/window-close.png deleted file mode 100644 index 761c87cd1e9..00000000000 Binary files a/Base/res/icons/16x16/window-close.png and /dev/null differ diff --git a/Base/res/icons/16x16/window-pin.png b/Base/res/icons/16x16/window-pin.png deleted file mode 100644 index 9fa1af4c7d2..00000000000 Binary files a/Base/res/icons/16x16/window-pin.png and /dev/null differ diff --git a/Base/res/icons/16x16/window-restore-2x.png b/Base/res/icons/16x16/window-restore-2x.png deleted file mode 100644 index b2ed5ef528a..00000000000 Binary files a/Base/res/icons/16x16/window-restore-2x.png and /dev/null differ diff --git a/Base/res/icons/16x16/window-restore.png b/Base/res/icons/16x16/window-restore.png deleted file mode 100644 index cd305dbd6c5..00000000000 Binary files a/Base/res/icons/16x16/window-restore.png and /dev/null differ diff --git a/Base/res/icons/16x16/window.png b/Base/res/icons/16x16/window.png deleted file mode 100644 index 6c554822ac2..00000000000 Binary files a/Base/res/icons/16x16/window.png and /dev/null differ diff --git a/Base/res/icons/16x16/x86.png b/Base/res/icons/16x16/x86.png deleted file mode 100644 index 5edc6380d6c..00000000000 Binary files a/Base/res/icons/16x16/x86.png and /dev/null differ diff --git a/Base/res/icons/32x32/animation-effects.png b/Base/res/icons/32x32/animation-effects.png deleted file mode 100644 index 72dda0f8c4b..00000000000 Binary files a/Base/res/icons/32x32/animation-effects.png and /dev/null differ diff --git a/Base/res/icons/32x32/app-2048.png b/Base/res/icons/32x32/app-2048.png deleted file mode 100644 index 75421033ae3..00000000000 Binary files a/Base/res/icons/32x32/app-2048.png and /dev/null differ diff --git a/Base/res/icons/32x32/app-3d-file-viewer.png b/Base/res/icons/32x32/app-3d-file-viewer.png deleted file mode 100644 index c3e5025ca0d..00000000000 Binary files a/Base/res/icons/32x32/app-3d-file-viewer.png and /dev/null differ diff --git a/Base/res/icons/32x32/app-analog-clock.png b/Base/res/icons/32x32/app-analog-clock.png deleted file mode 100644 index 5432bf4fd45..00000000000 Binary files a/Base/res/icons/32x32/app-analog-clock.png and /dev/null differ diff --git a/Base/res/icons/32x32/app-assistant.png b/Base/res/icons/32x32/app-assistant.png deleted file mode 100644 index 53dc2eba4f5..00000000000 Binary files a/Base/res/icons/32x32/app-assistant.png and /dev/null differ diff --git a/Base/res/icons/32x32/app-brickgame.png b/Base/res/icons/32x32/app-brickgame.png deleted file mode 100644 index 084dc1ce610..00000000000 Binary files a/Base/res/icons/32x32/app-brickgame.png and /dev/null differ diff --git a/Base/res/icons/32x32/app-calculator.png b/Base/res/icons/32x32/app-calculator.png deleted file mode 100644 index 6a5efb452ee..00000000000 Binary files a/Base/res/icons/32x32/app-calculator.png and /dev/null differ diff --git a/Base/res/icons/32x32/app-calendar.png b/Base/res/icons/32x32/app-calendar.png deleted file mode 100644 index 3855a0c264e..00000000000 Binary files a/Base/res/icons/32x32/app-calendar.png and /dev/null differ diff --git a/Base/res/icons/32x32/app-catdog.png b/Base/res/icons/32x32/app-catdog.png deleted file mode 100644 index 3b4df05ea4b..00000000000 Binary files a/Base/res/icons/32x32/app-catdog.png and /dev/null differ diff --git a/Base/res/icons/32x32/app-character-map.png b/Base/res/icons/32x32/app-character-map.png deleted file mode 100644 index 434d0fb2b11..00000000000 Binary files a/Base/res/icons/32x32/app-character-map.png and /dev/null differ diff --git a/Base/res/icons/32x32/app-chess.png b/Base/res/icons/32x32/app-chess.png deleted file mode 100644 index 817cf1999c4..00000000000 Binary files a/Base/res/icons/32x32/app-chess.png and /dev/null differ diff --git a/Base/res/icons/32x32/app-colorlines.png b/Base/res/icons/32x32/app-colorlines.png deleted file mode 100644 index ccece4b75ab..00000000000 Binary files a/Base/res/icons/32x32/app-colorlines.png and /dev/null differ diff --git a/Base/res/icons/32x32/app-crash-reporter.png b/Base/res/icons/32x32/app-crash-reporter.png deleted file mode 100644 index 189b3fa34ce..00000000000 Binary files a/Base/res/icons/32x32/app-crash-reporter.png and /dev/null differ diff --git a/Base/res/icons/32x32/app-display-settings.png b/Base/res/icons/32x32/app-display-settings.png deleted file mode 100644 index 7d6640bae03..00000000000 Binary files a/Base/res/icons/32x32/app-display-settings.png and /dev/null differ diff --git a/Base/res/icons/32x32/app-escalator.png b/Base/res/icons/32x32/app-escalator.png deleted file mode 100644 index e387ca93952..00000000000 Binary files a/Base/res/icons/32x32/app-escalator.png and /dev/null differ diff --git a/Base/res/icons/32x32/app-eyes.png b/Base/res/icons/32x32/app-eyes.png deleted file mode 100644 index 7b3e9da2644..00000000000 Binary files a/Base/res/icons/32x32/app-eyes.png and /dev/null differ diff --git a/Base/res/icons/32x32/app-file-manager.png b/Base/res/icons/32x32/app-file-manager.png deleted file mode 100644 index f55905a0857..00000000000 Binary files a/Base/res/icons/32x32/app-file-manager.png and /dev/null differ diff --git a/Base/res/icons/32x32/app-flappybug.png b/Base/res/icons/32x32/app-flappybug.png deleted file mode 100644 index 3572435c88a..00000000000 Binary files a/Base/res/icons/32x32/app-flappybug.png and /dev/null differ diff --git a/Base/res/icons/32x32/app-flood.png b/Base/res/icons/32x32/app-flood.png deleted file mode 100644 index 34455290d1f..00000000000 Binary files a/Base/res/icons/32x32/app-flood.png and /dev/null differ diff --git a/Base/res/icons/32x32/app-font-editor.png b/Base/res/icons/32x32/app-font-editor.png deleted file mode 100644 index 4b2d3b670e4..00000000000 Binary files a/Base/res/icons/32x32/app-font-editor.png and /dev/null differ diff --git a/Base/res/icons/32x32/app-gameoflife.png b/Base/res/icons/32x32/app-gameoflife.png deleted file mode 100644 index 9360efa7bba..00000000000 Binary files a/Base/res/icons/32x32/app-gameoflife.png and /dev/null differ diff --git a/Base/res/icons/32x32/app-gml-playground.png b/Base/res/icons/32x32/app-gml-playground.png deleted file mode 100644 index 1b8f3cba4c6..00000000000 Binary files a/Base/res/icons/32x32/app-gml-playground.png and /dev/null differ diff --git a/Base/res/icons/32x32/app-gradient.png b/Base/res/icons/32x32/app-gradient.png deleted file mode 100644 index 3a5c512dedb..00000000000 Binary files a/Base/res/icons/32x32/app-gradient.png and /dev/null differ diff --git a/Base/res/icons/32x32/app-hack-studio.png b/Base/res/icons/32x32/app-hack-studio.png deleted file mode 100644 index ac90d361c65..00000000000 Binary files a/Base/res/icons/32x32/app-hack-studio.png and /dev/null differ diff --git a/Base/res/icons/32x32/app-hearts.png b/Base/res/icons/32x32/app-hearts.png deleted file mode 100644 index 7cf21faa5b8..00000000000 Binary files a/Base/res/icons/32x32/app-hearts.png and /dev/null differ diff --git a/Base/res/icons/32x32/app-hello-world.png b/Base/res/icons/32x32/app-hello-world.png deleted file mode 100644 index 0f37d032dd7..00000000000 Binary files a/Base/res/icons/32x32/app-hello-world.png and /dev/null differ diff --git a/Base/res/icons/32x32/app-help.png b/Base/res/icons/32x32/app-help.png deleted file mode 100644 index 0d70f5f7d68..00000000000 Binary files a/Base/res/icons/32x32/app-help.png and /dev/null differ diff --git a/Base/res/icons/32x32/app-hex-editor.png b/Base/res/icons/32x32/app-hex-editor.png deleted file mode 100644 index 9a7465eccb6..00000000000 Binary files a/Base/res/icons/32x32/app-hex-editor.png and /dev/null differ diff --git a/Base/res/icons/32x32/app-image-viewer.png b/Base/res/icons/32x32/app-image-viewer.png deleted file mode 100644 index a19cfba061a..00000000000 Binary files a/Base/res/icons/32x32/app-image-viewer.png and /dev/null differ diff --git a/Base/res/icons/32x32/app-inspector.png b/Base/res/icons/32x32/app-inspector.png deleted file mode 100644 index 4b531309632..00000000000 Binary files a/Base/res/icons/32x32/app-inspector.png and /dev/null differ diff --git a/Base/res/icons/32x32/app-keyboard-mapper.png b/Base/res/icons/32x32/app-keyboard-mapper.png deleted file mode 100644 index a92cfe93029..00000000000 Binary files a/Base/res/icons/32x32/app-keyboard-mapper.png and /dev/null differ diff --git a/Base/res/icons/32x32/app-keyboard-settings.png b/Base/res/icons/32x32/app-keyboard-settings.png deleted file mode 100644 index 05a704b8d6c..00000000000 Binary files a/Base/res/icons/32x32/app-keyboard-settings.png and /dev/null differ diff --git a/Base/res/icons/32x32/app-libgfx-demo.png b/Base/res/icons/32x32/app-libgfx-demo.png deleted file mode 100644 index 0803f6d3fe0..00000000000 Binary files a/Base/res/icons/32x32/app-libgfx-demo.png and /dev/null differ diff --git a/Base/res/icons/32x32/app-magnifier.png b/Base/res/icons/32x32/app-magnifier.png deleted file mode 100644 index 44ac341af0d..00000000000 Binary files a/Base/res/icons/32x32/app-magnifier.png and /dev/null differ diff --git a/Base/res/icons/32x32/app-mail.png b/Base/res/icons/32x32/app-mail.png deleted file mode 100644 index 7160c51fa83..00000000000 Binary files a/Base/res/icons/32x32/app-mail.png and /dev/null differ diff --git a/Base/res/icons/32x32/app-mandelbrot.png b/Base/res/icons/32x32/app-mandelbrot.png deleted file mode 100644 index c06030d4b0c..00000000000 Binary files a/Base/res/icons/32x32/app-mandelbrot.png and /dev/null differ diff --git a/Base/res/icons/32x32/app-maps.png b/Base/res/icons/32x32/app-maps.png deleted file mode 100644 index 859627c3b44..00000000000 Binary files a/Base/res/icons/32x32/app-maps.png and /dev/null differ diff --git a/Base/res/icons/32x32/app-masterword.png b/Base/res/icons/32x32/app-masterword.png deleted file mode 100644 index 467888db8c7..00000000000 Binary files a/Base/res/icons/32x32/app-masterword.png and /dev/null differ diff --git a/Base/res/icons/32x32/app-minesweeper.png b/Base/res/icons/32x32/app-minesweeper.png deleted file mode 100644 index 81939b88710..00000000000 Binary files a/Base/res/icons/32x32/app-minesweeper.png and /dev/null differ diff --git a/Base/res/icons/32x32/app-model-gallery.png b/Base/res/icons/32x32/app-model-gallery.png deleted file mode 100644 index 5e5c850874e..00000000000 Binary files a/Base/res/icons/32x32/app-model-gallery.png and /dev/null differ diff --git a/Base/res/icons/32x32/app-mouse.png b/Base/res/icons/32x32/app-mouse.png deleted file mode 100644 index 89ec5107136..00000000000 Binary files a/Base/res/icons/32x32/app-mouse.png and /dev/null differ diff --git a/Base/res/icons/32x32/app-partition-editor.png b/Base/res/icons/32x32/app-partition-editor.png deleted file mode 100644 index 8b75b46111a..00000000000 Binary files a/Base/res/icons/32x32/app-partition-editor.png and /dev/null differ diff --git a/Base/res/icons/32x32/app-pdf-viewer.png b/Base/res/icons/32x32/app-pdf-viewer.png deleted file mode 100644 index adaca4f2f28..00000000000 Binary files a/Base/res/icons/32x32/app-pdf-viewer.png and /dev/null differ diff --git a/Base/res/icons/32x32/app-piano.png b/Base/res/icons/32x32/app-piano.png deleted file mode 100644 index a56b2b42860..00000000000 Binary files a/Base/res/icons/32x32/app-piano.png and /dev/null differ diff --git a/Base/res/icons/32x32/app-pixel-paint.png b/Base/res/icons/32x32/app-pixel-paint.png deleted file mode 100644 index 7095ea9902f..00000000000 Binary files a/Base/res/icons/32x32/app-pixel-paint.png and /dev/null differ diff --git a/Base/res/icons/32x32/app-presenter.png b/Base/res/icons/32x32/app-presenter.png deleted file mode 100644 index 74673e6d179..00000000000 Binary files a/Base/res/icons/32x32/app-presenter.png and /dev/null differ diff --git a/Base/res/icons/32x32/app-profiler.png b/Base/res/icons/32x32/app-profiler.png deleted file mode 100644 index 6a92e1ddffd..00000000000 Binary files a/Base/res/icons/32x32/app-profiler.png and /dev/null differ diff --git a/Base/res/icons/32x32/app-run.png b/Base/res/icons/32x32/app-run.png deleted file mode 100644 index a2384dd2e1d..00000000000 Binary files a/Base/res/icons/32x32/app-run.png and /dev/null differ diff --git a/Base/res/icons/32x32/app-screensaver.png b/Base/res/icons/32x32/app-screensaver.png deleted file mode 100644 index 5d1a977a4cb..00000000000 Binary files a/Base/res/icons/32x32/app-screensaver.png and /dev/null differ diff --git a/Base/res/icons/32x32/app-screenshot.png b/Base/res/icons/32x32/app-screenshot.png deleted file mode 100644 index aca82dd84a7..00000000000 Binary files a/Base/res/icons/32x32/app-screenshot.png and /dev/null differ diff --git a/Base/res/icons/32x32/app-settings.png b/Base/res/icons/32x32/app-settings.png deleted file mode 100644 index 833515e207f..00000000000 Binary files a/Base/res/icons/32x32/app-settings.png and /dev/null differ diff --git a/Base/res/icons/32x32/app-snake.png b/Base/res/icons/32x32/app-snake.png deleted file mode 100644 index b9f1ebf5a01..00000000000 Binary files a/Base/res/icons/32x32/app-snake.png and /dev/null differ diff --git a/Base/res/icons/32x32/app-solitaire.png b/Base/res/icons/32x32/app-solitaire.png deleted file mode 100644 index 04043fb6f0c..00000000000 Binary files a/Base/res/icons/32x32/app-solitaire.png and /dev/null differ diff --git a/Base/res/icons/32x32/app-sound-player.png b/Base/res/icons/32x32/app-sound-player.png deleted file mode 100644 index 442c34b7194..00000000000 Binary files a/Base/res/icons/32x32/app-sound-player.png and /dev/null differ diff --git a/Base/res/icons/32x32/app-space-analyzer.png b/Base/res/icons/32x32/app-space-analyzer.png deleted file mode 100644 index 1b5654c6d26..00000000000 Binary files a/Base/res/icons/32x32/app-space-analyzer.png and /dev/null differ diff --git a/Base/res/icons/32x32/app-spider.png b/Base/res/icons/32x32/app-spider.png deleted file mode 100644 index 4b892cb9ebd..00000000000 Binary files a/Base/res/icons/32x32/app-spider.png and /dev/null differ diff --git a/Base/res/icons/32x32/app-spreadsheet.png b/Base/res/icons/32x32/app-spreadsheet.png deleted file mode 100644 index 802e49c4c29..00000000000 Binary files a/Base/res/icons/32x32/app-spreadsheet.png and /dev/null differ diff --git a/Base/res/icons/32x32/app-sql-studio.png b/Base/res/icons/32x32/app-sql-studio.png deleted file mode 100644 index 7a5bf009640..00000000000 Binary files a/Base/res/icons/32x32/app-sql-studio.png and /dev/null differ diff --git a/Base/res/icons/32x32/app-starfield.png b/Base/res/icons/32x32/app-starfield.png deleted file mode 100644 index 19f215c8ec6..00000000000 Binary files a/Base/res/icons/32x32/app-starfield.png and /dev/null differ diff --git a/Base/res/icons/32x32/app-terminal.png b/Base/res/icons/32x32/app-terminal.png deleted file mode 100644 index 9ea6a06a4a6..00000000000 Binary files a/Base/res/icons/32x32/app-terminal.png and /dev/null differ diff --git a/Base/res/icons/32x32/app-text-editor.png b/Base/res/icons/32x32/app-text-editor.png deleted file mode 100644 index 83898de802e..00000000000 Binary files a/Base/res/icons/32x32/app-text-editor.png and /dev/null differ diff --git a/Base/res/icons/32x32/app-theme-editor.png b/Base/res/icons/32x32/app-theme-editor.png deleted file mode 100644 index ace10175cb9..00000000000 Binary files a/Base/res/icons/32x32/app-theme-editor.png and /dev/null differ diff --git a/Base/res/icons/32x32/app-tubes.png b/Base/res/icons/32x32/app-tubes.png deleted file mode 100644 index bccfdd143c1..00000000000 Binary files a/Base/res/icons/32x32/app-tubes.png and /dev/null differ diff --git a/Base/res/icons/32x32/app-video-player.png b/Base/res/icons/32x32/app-video-player.png deleted file mode 100644 index 7b623f41dd0..00000000000 Binary files a/Base/res/icons/32x32/app-video-player.png and /dev/null differ diff --git a/Base/res/icons/32x32/app-welcome.png b/Base/res/icons/32x32/app-welcome.png deleted file mode 100644 index 4edc2736c94..00000000000 Binary files a/Base/res/icons/32x32/app-welcome.png and /dev/null differ diff --git a/Base/res/icons/32x32/app-widget-gallery.png b/Base/res/icons/32x32/app-widget-gallery.png deleted file mode 100644 index 9e2963dbadb..00000000000 Binary files a/Base/res/icons/32x32/app-widget-gallery.png and /dev/null differ diff --git a/Base/res/icons/32x32/certificate.png b/Base/res/icons/32x32/certificate.png deleted file mode 100644 index 5ffd8ac66a8..00000000000 Binary files a/Base/res/icons/32x32/certificate.png and /dev/null differ diff --git a/Base/res/icons/32x32/color-chooser.png b/Base/res/icons/32x32/color-chooser.png deleted file mode 100644 index 3ce2308c62a..00000000000 Binary files a/Base/res/icons/32x32/color-chooser.png and /dev/null differ diff --git a/Base/res/icons/32x32/desktop.png b/Base/res/icons/32x32/desktop.png deleted file mode 100644 index 5b99add22db..00000000000 Binary files a/Base/res/icons/32x32/desktop.png and /dev/null differ diff --git a/Base/res/icons/32x32/downloads.png b/Base/res/icons/32x32/downloads.png deleted file mode 100644 index 547369d5bd4..00000000000 Binary files a/Base/res/icons/32x32/downloads.png and /dev/null differ diff --git a/Base/res/icons/32x32/filetype-archive.png b/Base/res/icons/32x32/filetype-archive.png deleted file mode 100644 index 28d30140047..00000000000 Binary files a/Base/res/icons/32x32/filetype-archive.png and /dev/null differ diff --git a/Base/res/icons/32x32/filetype-asm.png b/Base/res/icons/32x32/filetype-asm.png deleted file mode 100644 index 6176471dc3e..00000000000 Binary files a/Base/res/icons/32x32/filetype-asm.png and /dev/null differ diff --git a/Base/res/icons/32x32/filetype-c.png b/Base/res/icons/32x32/filetype-c.png deleted file mode 100644 index 3c145305b5f..00000000000 Binary files a/Base/res/icons/32x32/filetype-c.png and /dev/null differ diff --git a/Base/res/icons/32x32/filetype-calendar.png b/Base/res/icons/32x32/filetype-calendar.png deleted file mode 100644 index 54970b9c7f6..00000000000 Binary files a/Base/res/icons/32x32/filetype-calendar.png and /dev/null differ diff --git a/Base/res/icons/32x32/filetype-cmake.png b/Base/res/icons/32x32/filetype-cmake.png deleted file mode 100644 index 91816392d60..00000000000 Binary files a/Base/res/icons/32x32/filetype-cmake.png and /dev/null differ diff --git a/Base/res/icons/32x32/filetype-cplusplus.png b/Base/res/icons/32x32/filetype-cplusplus.png deleted file mode 100644 index e159195716c..00000000000 Binary files a/Base/res/icons/32x32/filetype-cplusplus.png and /dev/null differ diff --git a/Base/res/icons/32x32/filetype-css.png b/Base/res/icons/32x32/filetype-css.png deleted file mode 100644 index 22f90dee93b..00000000000 Binary files a/Base/res/icons/32x32/filetype-css.png and /dev/null differ diff --git a/Base/res/icons/32x32/filetype-db.png b/Base/res/icons/32x32/filetype-db.png deleted file mode 100644 index 262c5883e6d..00000000000 Binary files a/Base/res/icons/32x32/filetype-db.png and /dev/null differ diff --git a/Base/res/icons/32x32/filetype-executable.png b/Base/res/icons/32x32/filetype-executable.png deleted file mode 100644 index 1ca82691cbb..00000000000 Binary files a/Base/res/icons/32x32/filetype-executable.png and /dev/null differ diff --git a/Base/res/icons/32x32/filetype-folder-inaccessible.png b/Base/res/icons/32x32/filetype-folder-inaccessible.png deleted file mode 100644 index c32df226438..00000000000 Binary files a/Base/res/icons/32x32/filetype-folder-inaccessible.png and /dev/null differ diff --git a/Base/res/icons/32x32/filetype-folder-open.png b/Base/res/icons/32x32/filetype-folder-open.png deleted file mode 100644 index f68b101cd3f..00000000000 Binary files a/Base/res/icons/32x32/filetype-folder-open.png and /dev/null differ diff --git a/Base/res/icons/32x32/filetype-font.png b/Base/res/icons/32x32/filetype-font.png deleted file mode 100644 index 194ef96d1d4..00000000000 Binary files a/Base/res/icons/32x32/filetype-font.png and /dev/null differ diff --git a/Base/res/icons/32x32/filetype-form.png b/Base/res/icons/32x32/filetype-form.png deleted file mode 100644 index 3d405c36893..00000000000 Binary files a/Base/res/icons/32x32/filetype-form.png and /dev/null differ diff --git a/Base/res/icons/32x32/filetype-git.png b/Base/res/icons/32x32/filetype-git.png deleted file mode 100644 index 63574046952..00000000000 Binary files a/Base/res/icons/32x32/filetype-git.png and /dev/null differ diff --git a/Base/res/icons/32x32/filetype-gml.png b/Base/res/icons/32x32/filetype-gml.png deleted file mode 100644 index 258450cc80b..00000000000 Binary files a/Base/res/icons/32x32/filetype-gml.png and /dev/null differ diff --git a/Base/res/icons/32x32/filetype-header.png b/Base/res/icons/32x32/filetype-header.png deleted file mode 100644 index 39b0ffd1e0d..00000000000 Binary files a/Base/res/icons/32x32/filetype-header.png and /dev/null differ diff --git a/Base/res/icons/32x32/filetype-html.png b/Base/res/icons/32x32/filetype-html.png deleted file mode 100644 index 4e951011b30..00000000000 Binary files a/Base/res/icons/32x32/filetype-html.png and /dev/null differ diff --git a/Base/res/icons/32x32/filetype-image.png b/Base/res/icons/32x32/filetype-image.png deleted file mode 100644 index 0c51e7916a8..00000000000 Binary files a/Base/res/icons/32x32/filetype-image.png and /dev/null differ diff --git a/Base/res/icons/32x32/filetype-ini.png b/Base/res/icons/32x32/filetype-ini.png deleted file mode 100644 index e92337f77ee..00000000000 Binary files a/Base/res/icons/32x32/filetype-ini.png and /dev/null differ diff --git a/Base/res/icons/32x32/filetype-java.png b/Base/res/icons/32x32/filetype-java.png deleted file mode 100644 index 7a676699edf..00000000000 Binary files a/Base/res/icons/32x32/filetype-java.png and /dev/null differ diff --git a/Base/res/icons/32x32/filetype-javascript.png b/Base/res/icons/32x32/filetype-javascript.png deleted file mode 100644 index 3516189f943..00000000000 Binary files a/Base/res/icons/32x32/filetype-javascript.png and /dev/null differ diff --git a/Base/res/icons/32x32/filetype-json.png b/Base/res/icons/32x32/filetype-json.png deleted file mode 100644 index f227093af23..00000000000 Binary files a/Base/res/icons/32x32/filetype-json.png and /dev/null differ diff --git a/Base/res/icons/32x32/filetype-library.png b/Base/res/icons/32x32/filetype-library.png deleted file mode 100644 index 4785d3050e0..00000000000 Binary files a/Base/res/icons/32x32/filetype-library.png and /dev/null differ diff --git a/Base/res/icons/32x32/filetype-markdown.png b/Base/res/icons/32x32/filetype-markdown.png deleted file mode 100644 index 31a0491a2f0..00000000000 Binary files a/Base/res/icons/32x32/filetype-markdown.png and /dev/null differ diff --git a/Base/res/icons/32x32/filetype-music.png b/Base/res/icons/32x32/filetype-music.png deleted file mode 100644 index c3c095b92b8..00000000000 Binary files a/Base/res/icons/32x32/filetype-music.png and /dev/null differ diff --git a/Base/res/icons/32x32/filetype-object.png b/Base/res/icons/32x32/filetype-object.png deleted file mode 100644 index aa3ce0fe350..00000000000 Binary files a/Base/res/icons/32x32/filetype-object.png and /dev/null differ diff --git a/Base/res/icons/32x32/filetype-palette.png b/Base/res/icons/32x32/filetype-palette.png deleted file mode 100644 index 08e15231716..00000000000 Binary files a/Base/res/icons/32x32/filetype-palette.png and /dev/null differ diff --git a/Base/res/icons/32x32/filetype-pdf.png b/Base/res/icons/32x32/filetype-pdf.png deleted file mode 100644 index 2d1e0034434..00000000000 Binary files a/Base/res/icons/32x32/filetype-pdf.png and /dev/null differ diff --git a/Base/res/icons/32x32/filetype-php.png b/Base/res/icons/32x32/filetype-php.png deleted file mode 100644 index d68aa55f98d..00000000000 Binary files a/Base/res/icons/32x32/filetype-php.png and /dev/null differ diff --git a/Base/res/icons/32x32/filetype-pixelpaint.png b/Base/res/icons/32x32/filetype-pixelpaint.png deleted file mode 100644 index 6b2c8408989..00000000000 Binary files a/Base/res/icons/32x32/filetype-pixelpaint.png and /dev/null differ diff --git a/Base/res/icons/32x32/filetype-presenter.png b/Base/res/icons/32x32/filetype-presenter.png deleted file mode 100644 index 51622f0a1a8..00000000000 Binary files a/Base/res/icons/32x32/filetype-presenter.png and /dev/null differ diff --git a/Base/res/icons/32x32/filetype-python.png b/Base/res/icons/32x32/filetype-python.png deleted file mode 100644 index 218f0653a8a..00000000000 Binary files a/Base/res/icons/32x32/filetype-python.png and /dev/null differ diff --git a/Base/res/icons/32x32/filetype-ruby.png b/Base/res/icons/32x32/filetype-ruby.png deleted file mode 100644 index e456eb994c1..00000000000 Binary files a/Base/res/icons/32x32/filetype-ruby.png and /dev/null differ diff --git a/Base/res/icons/32x32/filetype-shell.png b/Base/res/icons/32x32/filetype-shell.png deleted file mode 100644 index be80c198bc0..00000000000 Binary files a/Base/res/icons/32x32/filetype-shell.png and /dev/null differ diff --git a/Base/res/icons/32x32/filetype-socket.png b/Base/res/icons/32x32/filetype-socket.png deleted file mode 100644 index ce6f9dd469e..00000000000 Binary files a/Base/res/icons/32x32/filetype-socket.png and /dev/null differ diff --git a/Base/res/icons/32x32/filetype-sound.png b/Base/res/icons/32x32/filetype-sound.png deleted file mode 100644 index 72a641976cf..00000000000 Binary files a/Base/res/icons/32x32/filetype-sound.png and /dev/null differ diff --git a/Base/res/icons/32x32/filetype-spreadsheet.png b/Base/res/icons/32x32/filetype-spreadsheet.png deleted file mode 100644 index 3232a45c627..00000000000 Binary files a/Base/res/icons/32x32/filetype-spreadsheet.png and /dev/null differ diff --git a/Base/res/icons/32x32/filetype-sql.png b/Base/res/icons/32x32/filetype-sql.png deleted file mode 100644 index 72991b27cca..00000000000 Binary files a/Base/res/icons/32x32/filetype-sql.png and /dev/null differ diff --git a/Base/res/icons/32x32/filetype-symlink.png b/Base/res/icons/32x32/filetype-symlink.png deleted file mode 100644 index c95b4009982..00000000000 Binary files a/Base/res/icons/32x32/filetype-symlink.png and /dev/null differ diff --git a/Base/res/icons/32x32/filetype-text.png b/Base/res/icons/32x32/filetype-text.png deleted file mode 100644 index ff09412c066..00000000000 Binary files a/Base/res/icons/32x32/filetype-text.png and /dev/null differ diff --git a/Base/res/icons/32x32/filetype-truetype.png b/Base/res/icons/32x32/filetype-truetype.png deleted file mode 100644 index e21441595fd..00000000000 Binary files a/Base/res/icons/32x32/filetype-truetype.png and /dev/null differ diff --git a/Base/res/icons/32x32/filetype-video.png b/Base/res/icons/32x32/filetype-video.png deleted file mode 100644 index c367cdf5d9c..00000000000 Binary files a/Base/res/icons/32x32/filetype-video.png and /dev/null differ diff --git a/Base/res/icons/32x32/filetype-wasm.png b/Base/res/icons/32x32/filetype-wasm.png deleted file mode 100644 index f162f0d124b..00000000000 Binary files a/Base/res/icons/32x32/filetype-wasm.png and /dev/null differ diff --git a/Base/res/icons/32x32/games.png b/Base/res/icons/32x32/games.png deleted file mode 100644 index 33eb8475ca6..00000000000 Binary files a/Base/res/icons/32x32/games.png and /dev/null differ diff --git a/Base/res/icons/32x32/git-directory-open.png b/Base/res/icons/32x32/git-directory-open.png deleted file mode 100644 index 8f33264901f..00000000000 Binary files a/Base/res/icons/32x32/git-directory-open.png and /dev/null differ diff --git a/Base/res/icons/32x32/git-directory.png b/Base/res/icons/32x32/git-directory.png deleted file mode 100644 index 9e4f3e6be62..00000000000 Binary files a/Base/res/icons/32x32/git-directory.png and /dev/null differ diff --git a/Base/res/icons/32x32/hard-disk.png b/Base/res/icons/32x32/hard-disk.png deleted file mode 100644 index 599ddc79b96..00000000000 Binary files a/Base/res/icons/32x32/hard-disk.png and /dev/null differ diff --git a/Base/res/icons/32x32/home-directory.png b/Base/res/icons/32x32/home-directory.png deleted file mode 100644 index ab9550ff5be..00000000000 Binary files a/Base/res/icons/32x32/home-directory.png and /dev/null differ diff --git a/Base/res/icons/32x32/home.png b/Base/res/icons/32x32/home.png deleted file mode 100644 index 2e526f84a50..00000000000 Binary files a/Base/res/icons/32x32/home.png and /dev/null differ diff --git a/Base/res/icons/32x32/key.png b/Base/res/icons/32x32/key.png deleted file mode 100644 index dbfa9261a04..00000000000 Binary files a/Base/res/icons/32x32/key.png and /dev/null differ diff --git a/Base/res/icons/32x32/ladyball.png b/Base/res/icons/32x32/ladyball.png deleted file mode 100644 index 9d7fbbce31a..00000000000 Binary files a/Base/res/icons/32x32/ladyball.png and /dev/null differ diff --git a/Base/res/icons/32x32/msgbox-error.png b/Base/res/icons/32x32/msgbox-error.png deleted file mode 100644 index dfb5be0e74e..00000000000 Binary files a/Base/res/icons/32x32/msgbox-error.png and /dev/null differ diff --git a/Base/res/icons/32x32/msgbox-information.png b/Base/res/icons/32x32/msgbox-information.png deleted file mode 100644 index 0601b910d45..00000000000 Binary files a/Base/res/icons/32x32/msgbox-information.png and /dev/null differ diff --git a/Base/res/icons/32x32/msgbox-question.png b/Base/res/icons/32x32/msgbox-question.png deleted file mode 100644 index b66632a8441..00000000000 Binary files a/Base/res/icons/32x32/msgbox-question.png and /dev/null differ diff --git a/Base/res/icons/32x32/network.png b/Base/res/icons/32x32/network.png deleted file mode 100644 index b47cb75121a..00000000000 Binary files a/Base/res/icons/32x32/network.png and /dev/null differ diff --git a/Base/res/icons/32x32/recycle-bin.png b/Base/res/icons/32x32/recycle-bin.png deleted file mode 100644 index 14106e6229e..00000000000 Binary files a/Base/res/icons/32x32/recycle-bin.png and /dev/null differ diff --git a/Base/res/icons/32x32/search-engine.png b/Base/res/icons/32x32/search-engine.png deleted file mode 100644 index 89d1d2e75f4..00000000000 Binary files a/Base/res/icons/32x32/search-engine.png and /dev/null differ diff --git a/Base/res/icons/32x32/shutdown.png b/Base/res/icons/32x32/shutdown.png deleted file mode 100644 index 7da2d618953..00000000000 Binary files a/Base/res/icons/32x32/shutdown.png and /dev/null differ diff --git a/Base/res/icons/32x32/theming-effects.png b/Base/res/icons/32x32/theming-effects.png deleted file mode 100644 index 2be5977dade..00000000000 Binary files a/Base/res/icons/32x32/theming-effects.png and /dev/null differ diff --git a/Base/res/icons/32x32/workspaces.png b/Base/res/icons/32x32/workspaces.png deleted file mode 100644 index 649de46bcf9..00000000000 Binary files a/Base/res/icons/32x32/workspaces.png and /dev/null differ diff --git a/Base/res/icons/SystemMenu.ini b/Base/res/icons/SystemMenu.ini deleted file mode 100644 index 4391f7ca0c0..00000000000 --- a/Base/res/icons/SystemMenu.ini +++ /dev/null @@ -1,11 +0,0 @@ -[16x16] -&Demos=/res/icons/16x16/demos.png -D&evelopment=/res/icons/16x16/development.png -&Games=/res/icons/16x16/games.png -&Games/Puzzles=/res/icons/16x16/games.png -Gra&phics=/res/icons/16x16/graphics.png -&Internet=/res/icons/16x16/internet.png -&Office=/res/icons/16x16/office.png -Settings=/res/icons/16x16/settings.png -&Media=/res/icons/16x16/multimedia.png -&Utilities=/res/icons/16x16/utilities.png diff --git a/Base/res/icons/calculator/eulers_number.png b/Base/res/icons/calculator/eulers_number.png deleted file mode 100644 index dad57bae811..00000000000 Binary files a/Base/res/icons/calculator/eulers_number.png and /dev/null differ diff --git a/Base/res/icons/calculator/phi.png b/Base/res/icons/calculator/phi.png deleted file mode 100644 index a443063bf50..00000000000 Binary files a/Base/res/icons/calculator/phi.png and /dev/null differ diff --git a/Base/res/icons/calculator/pi.png b/Base/res/icons/calculator/pi.png deleted file mode 100644 index ea62ade0911..00000000000 Binary files a/Base/res/icons/calculator/pi.png and /dev/null differ diff --git a/Base/res/icons/hackstudio/Class.png b/Base/res/icons/hackstudio/Class.png deleted file mode 100644 index 082bdd16f52..00000000000 Binary files a/Base/res/icons/hackstudio/Class.png and /dev/null differ diff --git a/Base/res/icons/hackstudio/Cursor.png b/Base/res/icons/hackstudio/Cursor.png deleted file mode 100644 index 135634ffea7..00000000000 Binary files a/Base/res/icons/hackstudio/Cursor.png and /dev/null differ diff --git a/Base/res/icons/hackstudio/Function.png b/Base/res/icons/hackstudio/Function.png deleted file mode 100644 index c776832b370..00000000000 Binary files a/Base/res/icons/hackstudio/Function.png and /dev/null differ diff --git a/Base/res/icons/hackstudio/GButton.png b/Base/res/icons/hackstudio/GButton.png deleted file mode 100644 index 7d735c8e732..00000000000 Binary files a/Base/res/icons/hackstudio/GButton.png and /dev/null differ diff --git a/Base/res/icons/hackstudio/GCheckBox.png b/Base/res/icons/hackstudio/GCheckBox.png deleted file mode 100644 index 192972a22ca..00000000000 Binary files a/Base/res/icons/hackstudio/GCheckBox.png and /dev/null differ diff --git a/Base/res/icons/hackstudio/GGroupBox.png b/Base/res/icons/hackstudio/GGroupBox.png deleted file mode 100644 index 3e8dd643bdf..00000000000 Binary files a/Base/res/icons/hackstudio/GGroupBox.png and /dev/null differ diff --git a/Base/res/icons/hackstudio/GLabel.png b/Base/res/icons/hackstudio/GLabel.png deleted file mode 100644 index b6eb3a49f68..00000000000 Binary files a/Base/res/icons/hackstudio/GLabel.png and /dev/null differ diff --git a/Base/res/icons/hackstudio/GProgressBar.png b/Base/res/icons/hackstudio/GProgressBar.png deleted file mode 100644 index a8c5e10a45a..00000000000 Binary files a/Base/res/icons/hackstudio/GProgressBar.png and /dev/null differ diff --git a/Base/res/icons/hackstudio/GRadioButton.png b/Base/res/icons/hackstudio/GRadioButton.png deleted file mode 100644 index c694b06afaf..00000000000 Binary files a/Base/res/icons/hackstudio/GRadioButton.png and /dev/null differ diff --git a/Base/res/icons/hackstudio/GScrollBar.png b/Base/res/icons/hackstudio/GScrollBar.png deleted file mode 100644 index 275378e3668..00000000000 Binary files a/Base/res/icons/hackstudio/GScrollBar.png and /dev/null differ diff --git a/Base/res/icons/hackstudio/GSlider.png b/Base/res/icons/hackstudio/GSlider.png deleted file mode 100644 index 4f5d54064db..00000000000 Binary files a/Base/res/icons/hackstudio/GSlider.png and /dev/null differ diff --git a/Base/res/icons/hackstudio/GSpinBox.png b/Base/res/icons/hackstudio/GSpinBox.png deleted file mode 100644 index a81389690a1..00000000000 Binary files a/Base/res/icons/hackstudio/GSpinBox.png and /dev/null differ diff --git a/Base/res/icons/hackstudio/GTextBox.png b/Base/res/icons/hackstudio/GTextBox.png deleted file mode 100644 index 48c970e7fcc..00000000000 Binary files a/Base/res/icons/hackstudio/GTextBox.png and /dev/null differ diff --git a/Base/res/icons/hackstudio/Member.png b/Base/res/icons/hackstudio/Member.png deleted file mode 100644 index e79338fd077..00000000000 Binary files a/Base/res/icons/hackstudio/Member.png and /dev/null differ diff --git a/Base/res/icons/hackstudio/Namespace.png b/Base/res/icons/hackstudio/Namespace.png deleted file mode 100644 index 13320896047..00000000000 Binary files a/Base/res/icons/hackstudio/Namespace.png and /dev/null differ diff --git a/Base/res/icons/hackstudio/Preprocessor.png b/Base/res/icons/hackstudio/Preprocessor.png deleted file mode 100644 index 45edf08aa1e..00000000000 Binary files a/Base/res/icons/hackstudio/Preprocessor.png and /dev/null differ diff --git a/Base/res/icons/hackstudio/Struct.png b/Base/res/icons/hackstudio/Struct.png deleted file mode 100644 index 763d984654a..00000000000 Binary files a/Base/res/icons/hackstudio/Struct.png and /dev/null differ diff --git a/Base/res/icons/hackstudio/Variable.png b/Base/res/icons/hackstudio/Variable.png deleted file mode 100644 index ecafe2e39ad..00000000000 Binary files a/Base/res/icons/hackstudio/Variable.png and /dev/null differ diff --git a/Base/res/icons/hackstudio/add-editor.png b/Base/res/icons/hackstudio/add-editor.png deleted file mode 100644 index ebffece26ff..00000000000 Binary files a/Base/res/icons/hackstudio/add-editor.png and /dev/null differ diff --git a/Base/res/icons/hackstudio/add-terminal.png b/Base/res/icons/hackstudio/add-terminal.png deleted file mode 100644 index e64e4f959dc..00000000000 Binary files a/Base/res/icons/hackstudio/add-terminal.png and /dev/null differ diff --git a/Base/res/icons/hackstudio/remove-editor.png b/Base/res/icons/hackstudio/remove-editor.png deleted file mode 100644 index 243f13dbeac..00000000000 Binary files a/Base/res/icons/hackstudio/remove-editor.png and /dev/null differ diff --git a/Base/res/icons/hackstudio/remove-terminal.png b/Base/res/icons/hackstudio/remove-terminal.png deleted file mode 100644 index 311ac8df7bb..00000000000 Binary files a/Base/res/icons/hackstudio/remove-terminal.png and /dev/null differ diff --git a/Base/res/icons/hackstudio/templates-32x32/cpp-basic.png b/Base/res/icons/hackstudio/templates-32x32/cpp-basic.png deleted file mode 100644 index 96995229f23..00000000000 Binary files a/Base/res/icons/hackstudio/templates-32x32/cpp-basic.png and /dev/null differ diff --git a/Base/res/icons/hackstudio/templates-32x32/cpp-gui.png b/Base/res/icons/hackstudio/templates-32x32/cpp-gui.png deleted file mode 100644 index bdbb0a0130d..00000000000 Binary files a/Base/res/icons/hackstudio/templates-32x32/cpp-gui.png and /dev/null differ diff --git a/Base/res/icons/hackstudio/templates-32x32/cpp-library.png b/Base/res/icons/hackstudio/templates-32x32/cpp-library.png deleted file mode 100644 index e00ce34f165..00000000000 Binary files a/Base/res/icons/hackstudio/templates-32x32/cpp-library.png and /dev/null differ diff --git a/Base/res/icons/hackstudio/templates-32x32/empty.png b/Base/res/icons/hackstudio/templates-32x32/empty.png deleted file mode 100644 index 1ca82691cbb..00000000000 Binary files a/Base/res/icons/hackstudio/templates-32x32/empty.png and /dev/null differ diff --git a/Base/res/icons/hackstudio/templates-32x32/serenity-application.png b/Base/res/icons/hackstudio/templates-32x32/serenity-application.png deleted file mode 100644 index 2d85ff27292..00000000000 Binary files a/Base/res/icons/hackstudio/templates-32x32/serenity-application.png and /dev/null differ diff --git a/Base/res/icons/pixelpaint/active-layer-down.png b/Base/res/icons/pixelpaint/active-layer-down.png deleted file mode 100644 index 9d37c2621f0..00000000000 Binary files a/Base/res/icons/pixelpaint/active-layer-down.png and /dev/null differ diff --git a/Base/res/icons/pixelpaint/active-layer-up.png b/Base/res/icons/pixelpaint/active-layer-up.png deleted file mode 100644 index 5caf3bfeb87..00000000000 Binary files a/Base/res/icons/pixelpaint/active-layer-up.png and /dev/null differ diff --git a/Base/res/icons/pixelpaint/add-guide.png b/Base/res/icons/pixelpaint/add-guide.png deleted file mode 100644 index c120416c489..00000000000 Binary files a/Base/res/icons/pixelpaint/add-guide.png and /dev/null differ diff --git a/Base/res/icons/pixelpaint/add-mask.png b/Base/res/icons/pixelpaint/add-mask.png deleted file mode 100644 index 4fcfaddddd7..00000000000 Binary files a/Base/res/icons/pixelpaint/add-mask.png and /dev/null differ diff --git a/Base/res/icons/pixelpaint/brush.png b/Base/res/icons/pixelpaint/brush.png deleted file mode 100644 index cf1207417ba..00000000000 Binary files a/Base/res/icons/pixelpaint/brush.png and /dev/null differ diff --git a/Base/res/icons/pixelpaint/bucket.png b/Base/res/icons/pixelpaint/bucket.png deleted file mode 100644 index fac17416824..00000000000 Binary files a/Base/res/icons/pixelpaint/bucket.png and /dev/null differ diff --git a/Base/res/icons/pixelpaint/circle.png b/Base/res/icons/pixelpaint/circle.png deleted file mode 100644 index 9680bb8fff6..00000000000 Binary files a/Base/res/icons/pixelpaint/circle.png and /dev/null differ diff --git a/Base/res/icons/pixelpaint/clear-guides.png b/Base/res/icons/pixelpaint/clear-guides.png deleted file mode 100644 index e4ac353fb5b..00000000000 Binary files a/Base/res/icons/pixelpaint/clear-guides.png and /dev/null differ diff --git a/Base/res/icons/pixelpaint/clone.png b/Base/res/icons/pixelpaint/clone.png deleted file mode 100644 index c2116aceb05..00000000000 Binary files a/Base/res/icons/pixelpaint/clone.png and /dev/null differ diff --git a/Base/res/icons/pixelpaint/close-image.png b/Base/res/icons/pixelpaint/close-image.png deleted file mode 100644 index ab65d0f6e63..00000000000 Binary files a/Base/res/icons/pixelpaint/close-image.png and /dev/null differ diff --git a/Base/res/icons/pixelpaint/crop.png b/Base/res/icons/pixelpaint/crop.png deleted file mode 100644 index 9e62d16e902..00000000000 Binary files a/Base/res/icons/pixelpaint/crop.png and /dev/null differ diff --git a/Base/res/icons/pixelpaint/default-colors.png b/Base/res/icons/pixelpaint/default-colors.png deleted file mode 100644 index c421dfa3f2d..00000000000 Binary files a/Base/res/icons/pixelpaint/default-colors.png and /dev/null differ diff --git a/Base/res/icons/pixelpaint/eraser.png b/Base/res/icons/pixelpaint/eraser.png deleted file mode 100644 index 70dc5f070cd..00000000000 Binary files a/Base/res/icons/pixelpaint/eraser.png and /dev/null differ diff --git a/Base/res/icons/pixelpaint/filter.png b/Base/res/icons/pixelpaint/filter.png deleted file mode 100644 index c4b7a15f3f1..00000000000 Binary files a/Base/res/icons/pixelpaint/filter.png and /dev/null differ diff --git a/Base/res/icons/pixelpaint/flatten-image.png b/Base/res/icons/pixelpaint/flatten-image.png deleted file mode 100644 index 43b9f41ed30..00000000000 Binary files a/Base/res/icons/pixelpaint/flatten-image.png and /dev/null differ diff --git a/Base/res/icons/pixelpaint/generic-5x5-convolution.png b/Base/res/icons/pixelpaint/generic-5x5-convolution.png deleted file mode 100644 index d1140c2fe68..00000000000 Binary files a/Base/res/icons/pixelpaint/generic-5x5-convolution.png and /dev/null differ diff --git a/Base/res/icons/pixelpaint/gradients.png b/Base/res/icons/pixelpaint/gradients.png deleted file mode 100644 index 85c58eb72a5..00000000000 Binary files a/Base/res/icons/pixelpaint/gradients.png and /dev/null differ diff --git a/Base/res/icons/pixelpaint/guides.png b/Base/res/icons/pixelpaint/guides.png deleted file mode 100644 index a0f817444e2..00000000000 Binary files a/Base/res/icons/pixelpaint/guides.png and /dev/null differ diff --git a/Base/res/icons/pixelpaint/invert-selection.png b/Base/res/icons/pixelpaint/invert-selection.png deleted file mode 100644 index d1bfe011411..00000000000 Binary files a/Base/res/icons/pixelpaint/invert-selection.png and /dev/null differ diff --git a/Base/res/icons/pixelpaint/lasso-select.png b/Base/res/icons/pixelpaint/lasso-select.png deleted file mode 100644 index f0eb62a1880..00000000000 Binary files a/Base/res/icons/pixelpaint/lasso-select.png and /dev/null differ diff --git a/Base/res/icons/pixelpaint/levels.png b/Base/res/icons/pixelpaint/levels.png deleted file mode 100644 index 698c5167f36..00000000000 Binary files a/Base/res/icons/pixelpaint/levels.png and /dev/null differ diff --git a/Base/res/icons/pixelpaint/line.png b/Base/res/icons/pixelpaint/line.png deleted file mode 100644 index c1f611ebc50..00000000000 Binary files a/Base/res/icons/pixelpaint/line.png and /dev/null differ diff --git a/Base/res/icons/pixelpaint/load-color-palette.png b/Base/res/icons/pixelpaint/load-color-palette.png deleted file mode 100644 index 39f940add70..00000000000 Binary files a/Base/res/icons/pixelpaint/load-color-palette.png and /dev/null differ diff --git a/Base/res/icons/pixelpaint/merge-active-layer-down.png b/Base/res/icons/pixelpaint/merge-active-layer-down.png deleted file mode 100644 index f1b51a965f4..00000000000 Binary files a/Base/res/icons/pixelpaint/merge-active-layer-down.png and /dev/null differ diff --git a/Base/res/icons/pixelpaint/merge-active-layer-up.png b/Base/res/icons/pixelpaint/merge-active-layer-up.png deleted file mode 100644 index 5072a44b3ad..00000000000 Binary files a/Base/res/icons/pixelpaint/merge-active-layer-up.png and /dev/null differ diff --git a/Base/res/icons/pixelpaint/merge-visible.png b/Base/res/icons/pixelpaint/merge-visible.png deleted file mode 100644 index f108bcb1392..00000000000 Binary files a/Base/res/icons/pixelpaint/merge-visible.png and /dev/null differ diff --git a/Base/res/icons/pixelpaint/move.png b/Base/res/icons/pixelpaint/move.png deleted file mode 100644 index 2108ef43153..00000000000 Binary files a/Base/res/icons/pixelpaint/move.png and /dev/null differ diff --git a/Base/res/icons/pixelpaint/new-clipboard.png b/Base/res/icons/pixelpaint/new-clipboard.png deleted file mode 100644 index 92a4d4c43ed..00000000000 Binary files a/Base/res/icons/pixelpaint/new-clipboard.png and /dev/null differ diff --git a/Base/res/icons/pixelpaint/next-layer.png b/Base/res/icons/pixelpaint/next-layer.png deleted file mode 100644 index 608b8c15dda..00000000000 Binary files a/Base/res/icons/pixelpaint/next-layer.png and /dev/null differ diff --git a/Base/res/icons/pixelpaint/pen.png b/Base/res/icons/pixelpaint/pen.png deleted file mode 100644 index 9bf8067ff33..00000000000 Binary files a/Base/res/icons/pixelpaint/pen.png and /dev/null differ diff --git a/Base/res/icons/pixelpaint/picker.png b/Base/res/icons/pixelpaint/picker.png deleted file mode 100644 index 34f987766ea..00000000000 Binary files a/Base/res/icons/pixelpaint/picker.png and /dev/null differ diff --git a/Base/res/icons/pixelpaint/polygonal-select.png b/Base/res/icons/pixelpaint/polygonal-select.png deleted file mode 100644 index 663a33578c9..00000000000 Binary files a/Base/res/icons/pixelpaint/polygonal-select.png and /dev/null differ diff --git a/Base/res/icons/pixelpaint/previous-layer.png b/Base/res/icons/pixelpaint/previous-layer.png deleted file mode 100644 index 050e88bde95..00000000000 Binary files a/Base/res/icons/pixelpaint/previous-layer.png and /dev/null differ diff --git a/Base/res/icons/pixelpaint/rectangle-select.png b/Base/res/icons/pixelpaint/rectangle-select.png deleted file mode 100644 index f67db412dcb..00000000000 Binary files a/Base/res/icons/pixelpaint/rectangle-select.png and /dev/null differ diff --git a/Base/res/icons/pixelpaint/rectangle.png b/Base/res/icons/pixelpaint/rectangle.png deleted file mode 100644 index 6c0c5c183a7..00000000000 Binary files a/Base/res/icons/pixelpaint/rectangle.png and /dev/null differ diff --git a/Base/res/icons/pixelpaint/save-color-palette.png b/Base/res/icons/pixelpaint/save-color-palette.png deleted file mode 100644 index ca658ad84f6..00000000000 Binary files a/Base/res/icons/pixelpaint/save-color-palette.png and /dev/null differ diff --git a/Base/res/icons/pixelpaint/spray.png b/Base/res/icons/pixelpaint/spray.png deleted file mode 100644 index 3d42b0d7cd5..00000000000 Binary files a/Base/res/icons/pixelpaint/spray.png and /dev/null differ diff --git a/Base/res/icons/pixelpaint/swap-colors.png b/Base/res/icons/pixelpaint/swap-colors.png deleted file mode 100644 index 8851eac356f..00000000000 Binary files a/Base/res/icons/pixelpaint/swap-colors.png and /dev/null differ diff --git a/Base/res/icons/pixelpaint/text.png b/Base/res/icons/pixelpaint/text.png deleted file mode 100644 index 406e0ba015e..00000000000 Binary files a/Base/res/icons/pixelpaint/text.png and /dev/null differ diff --git a/Base/res/icons/pixelpaint/wand-select.png b/Base/res/icons/pixelpaint/wand-select.png deleted file mode 100644 index 724448e55ab..00000000000 Binary files a/Base/res/icons/pixelpaint/wand-select.png and /dev/null differ diff --git a/Base/res/icons/pixelpaint/zoom.png b/Base/res/icons/pixelpaint/zoom.png deleted file mode 100644 index cbdaa117d9d..00000000000 Binary files a/Base/res/icons/pixelpaint/zoom.png and /dev/null differ diff --git a/Base/res/icons/serenity/treeview-collapse.png b/Base/res/icons/serenity/treeview-collapse.png deleted file mode 100644 index 451d870226e..00000000000 Binary files a/Base/res/icons/serenity/treeview-collapse.png and /dev/null differ diff --git a/Base/res/icons/serenity/treeview-expand.png b/Base/res/icons/serenity/treeview-expand.png deleted file mode 100644 index 7beedb79553..00000000000 Binary files a/Base/res/icons/serenity/treeview-expand.png and /dev/null differ diff --git a/Base/res/icons/symlink-emblem-small.png b/Base/res/icons/symlink-emblem-small.png deleted file mode 100644 index e35cda89328..00000000000 Binary files a/Base/res/icons/symlink-emblem-small.png and /dev/null differ diff --git a/Base/res/icons/symlink-emblem.png b/Base/res/icons/symlink-emblem.png deleted file mode 100644 index 956de4e7698..00000000000 Binary files a/Base/res/icons/symlink-emblem.png and /dev/null differ diff --git a/Base/res/icons/themes/Chillychilly/16x16/window-close-modified.png b/Base/res/icons/themes/Chillychilly/16x16/window-close-modified.png deleted file mode 100644 index ba3340d79de..00000000000 Binary files a/Base/res/icons/themes/Chillychilly/16x16/window-close-modified.png and /dev/null differ diff --git a/Base/res/icons/themes/Chillychilly/16x16/window-close.png b/Base/res/icons/themes/Chillychilly/16x16/window-close.png deleted file mode 100644 index 8c55299b702..00000000000 Binary files a/Base/res/icons/themes/Chillychilly/16x16/window-close.png and /dev/null differ diff --git a/Base/res/icons/themes/Chillychilly/16x16/window-maximize.png b/Base/res/icons/themes/Chillychilly/16x16/window-maximize.png deleted file mode 100644 index 0628695ce07..00000000000 Binary files a/Base/res/icons/themes/Chillychilly/16x16/window-maximize.png and /dev/null differ diff --git a/Base/res/icons/themes/Chillychilly/16x16/window-minimize.png b/Base/res/icons/themes/Chillychilly/16x16/window-minimize.png deleted file mode 100644 index aa77102f4f7..00000000000 Binary files a/Base/res/icons/themes/Chillychilly/16x16/window-minimize.png and /dev/null differ diff --git a/Base/res/icons/themes/Chillychilly/16x16/window-restore.png b/Base/res/icons/themes/Chillychilly/16x16/window-restore.png deleted file mode 100644 index 45ef428cf50..00000000000 Binary files a/Base/res/icons/themes/Chillychilly/16x16/window-restore.png and /dev/null differ diff --git a/Base/res/icons/themes/Chillychilly/frame-shadow-dark.png b/Base/res/icons/themes/Chillychilly/frame-shadow-dark.png deleted file mode 100644 index 87f71fb51df..00000000000 Binary files a/Base/res/icons/themes/Chillychilly/frame-shadow-dark.png and /dev/null differ diff --git a/Base/res/icons/themes/Chillychilly/frame-shadow-light.png b/Base/res/icons/themes/Chillychilly/frame-shadow-light.png deleted file mode 100644 index f46d1cabceb..00000000000 Binary files a/Base/res/icons/themes/Chillychilly/frame-shadow-light.png and /dev/null differ diff --git a/Base/res/icons/themes/Chillychilly/menu-shadow.png b/Base/res/icons/themes/Chillychilly/menu-shadow.png deleted file mode 100644 index 1a6d2c00204..00000000000 Binary files a/Base/res/icons/themes/Chillychilly/menu-shadow.png and /dev/null differ diff --git a/Base/res/icons/themes/Coffee/16x16/window-close-hover.png b/Base/res/icons/themes/Coffee/16x16/window-close-hover.png deleted file mode 100644 index eafd505aea6..00000000000 Binary files a/Base/res/icons/themes/Coffee/16x16/window-close-hover.png and /dev/null differ diff --git a/Base/res/icons/themes/Coffee/16x16/window-close-modified-hover.png b/Base/res/icons/themes/Coffee/16x16/window-close-modified-hover.png deleted file mode 100644 index be550bd1d6a..00000000000 Binary files a/Base/res/icons/themes/Coffee/16x16/window-close-modified-hover.png and /dev/null differ diff --git a/Base/res/icons/themes/Coffee/16x16/window-close-modified.png b/Base/res/icons/themes/Coffee/16x16/window-close-modified.png deleted file mode 100644 index b2c4d071efe..00000000000 Binary files a/Base/res/icons/themes/Coffee/16x16/window-close-modified.png and /dev/null differ diff --git a/Base/res/icons/themes/Coffee/16x16/window-close.png b/Base/res/icons/themes/Coffee/16x16/window-close.png deleted file mode 100644 index 8affed278b2..00000000000 Binary files a/Base/res/icons/themes/Coffee/16x16/window-close.png and /dev/null differ diff --git a/Base/res/icons/themes/Coffee/16x16/window-maximize-hover.png b/Base/res/icons/themes/Coffee/16x16/window-maximize-hover.png deleted file mode 100644 index a953d0095f6..00000000000 Binary files a/Base/res/icons/themes/Coffee/16x16/window-maximize-hover.png and /dev/null differ diff --git a/Base/res/icons/themes/Coffee/16x16/window-maximize.png b/Base/res/icons/themes/Coffee/16x16/window-maximize.png deleted file mode 100644 index 99569637495..00000000000 Binary files a/Base/res/icons/themes/Coffee/16x16/window-maximize.png and /dev/null differ diff --git a/Base/res/icons/themes/Coffee/16x16/window-minimize-hover.png b/Base/res/icons/themes/Coffee/16x16/window-minimize-hover.png deleted file mode 100644 index 7201b3478b5..00000000000 Binary files a/Base/res/icons/themes/Coffee/16x16/window-minimize-hover.png and /dev/null differ diff --git a/Base/res/icons/themes/Coffee/16x16/window-minimize.png b/Base/res/icons/themes/Coffee/16x16/window-minimize.png deleted file mode 100644 index 9709e21c67b..00000000000 Binary files a/Base/res/icons/themes/Coffee/16x16/window-minimize.png and /dev/null differ diff --git a/Base/res/icons/themes/Coffee/16x16/window-restore-hover.png b/Base/res/icons/themes/Coffee/16x16/window-restore-hover.png deleted file mode 100644 index 4f3c75ec52a..00000000000 Binary files a/Base/res/icons/themes/Coffee/16x16/window-restore-hover.png and /dev/null differ diff --git a/Base/res/icons/themes/Coffee/16x16/window-restore.png b/Base/res/icons/themes/Coffee/16x16/window-restore.png deleted file mode 100644 index 950ee5ac666..00000000000 Binary files a/Base/res/icons/themes/Coffee/16x16/window-restore.png and /dev/null differ diff --git a/Base/res/icons/themes/Contrast/16x16/window-close-modified.png b/Base/res/icons/themes/Contrast/16x16/window-close-modified.png deleted file mode 100644 index c0de75eb0d3..00000000000 Binary files a/Base/res/icons/themes/Contrast/16x16/window-close-modified.png and /dev/null differ diff --git a/Base/res/icons/themes/Contrast/16x16/window-close.png b/Base/res/icons/themes/Contrast/16x16/window-close.png deleted file mode 100644 index fc145212812..00000000000 Binary files a/Base/res/icons/themes/Contrast/16x16/window-close.png and /dev/null differ diff --git a/Base/res/icons/themes/Contrast/16x16/window-maximize.png b/Base/res/icons/themes/Contrast/16x16/window-maximize.png deleted file mode 100644 index ab4f13b41cc..00000000000 Binary files a/Base/res/icons/themes/Contrast/16x16/window-maximize.png and /dev/null differ diff --git a/Base/res/icons/themes/Contrast/16x16/window-minimize.png b/Base/res/icons/themes/Contrast/16x16/window-minimize.png deleted file mode 100644 index 0c153bff250..00000000000 Binary files a/Base/res/icons/themes/Contrast/16x16/window-minimize.png and /dev/null differ diff --git a/Base/res/icons/themes/Contrast/16x16/window-restore.png b/Base/res/icons/themes/Contrast/16x16/window-restore.png deleted file mode 100644 index 9fc2afaf830..00000000000 Binary files a/Base/res/icons/themes/Contrast/16x16/window-restore.png and /dev/null differ diff --git a/Base/res/icons/themes/Cupertino/16x16/window-close-hover.png b/Base/res/icons/themes/Cupertino/16x16/window-close-hover.png deleted file mode 100644 index ee848f2e19f..00000000000 Binary files a/Base/res/icons/themes/Cupertino/16x16/window-close-hover.png and /dev/null differ diff --git a/Base/res/icons/themes/Cupertino/16x16/window-close-modified-hover.png b/Base/res/icons/themes/Cupertino/16x16/window-close-modified-hover.png deleted file mode 100644 index 6710dd77399..00000000000 Binary files a/Base/res/icons/themes/Cupertino/16x16/window-close-modified-hover.png and /dev/null differ diff --git a/Base/res/icons/themes/Cupertino/16x16/window-close-modified.png b/Base/res/icons/themes/Cupertino/16x16/window-close-modified.png deleted file mode 100644 index 4b8d8925b4e..00000000000 Binary files a/Base/res/icons/themes/Cupertino/16x16/window-close-modified.png and /dev/null differ diff --git a/Base/res/icons/themes/Cupertino/16x16/window-close.png b/Base/res/icons/themes/Cupertino/16x16/window-close.png deleted file mode 100644 index 6bc6768e62f..00000000000 Binary files a/Base/res/icons/themes/Cupertino/16x16/window-close.png and /dev/null differ diff --git a/Base/res/icons/themes/Cupertino/16x16/window-maximize-hover.png b/Base/res/icons/themes/Cupertino/16x16/window-maximize-hover.png deleted file mode 100644 index 0c177b913c3..00000000000 Binary files a/Base/res/icons/themes/Cupertino/16x16/window-maximize-hover.png and /dev/null differ diff --git a/Base/res/icons/themes/Cupertino/16x16/window-maximize.png b/Base/res/icons/themes/Cupertino/16x16/window-maximize.png deleted file mode 100644 index af2c4fea3cd..00000000000 Binary files a/Base/res/icons/themes/Cupertino/16x16/window-maximize.png and /dev/null differ diff --git a/Base/res/icons/themes/Cupertino/16x16/window-minimize-hover.png b/Base/res/icons/themes/Cupertino/16x16/window-minimize-hover.png deleted file mode 100644 index fb7e9c2d462..00000000000 Binary files a/Base/res/icons/themes/Cupertino/16x16/window-minimize-hover.png and /dev/null differ diff --git a/Base/res/icons/themes/Cupertino/16x16/window-minimize.png b/Base/res/icons/themes/Cupertino/16x16/window-minimize.png deleted file mode 100644 index fe86246d5ca..00000000000 Binary files a/Base/res/icons/themes/Cupertino/16x16/window-minimize.png and /dev/null differ diff --git a/Base/res/icons/themes/Cupertino/16x16/window-restore-hover.png b/Base/res/icons/themes/Cupertino/16x16/window-restore-hover.png deleted file mode 100644 index bb0faf9a7c1..00000000000 Binary files a/Base/res/icons/themes/Cupertino/16x16/window-restore-hover.png and /dev/null differ diff --git a/Base/res/icons/themes/Cupertino/16x16/window-restore.png b/Base/res/icons/themes/Cupertino/16x16/window-restore.png deleted file mode 100644 index 636e6101407..00000000000 Binary files a/Base/res/icons/themes/Cupertino/16x16/window-restore.png and /dev/null differ diff --git a/Base/res/icons/themes/Default/frame-shadow-dark.png b/Base/res/icons/themes/Default/frame-shadow-dark.png deleted file mode 100644 index 87f71fb51df..00000000000 Binary files a/Base/res/icons/themes/Default/frame-shadow-dark.png and /dev/null differ diff --git a/Base/res/icons/themes/Default/frame-shadow-light.png b/Base/res/icons/themes/Default/frame-shadow-light.png deleted file mode 100644 index 29da33354db..00000000000 Binary files a/Base/res/icons/themes/Default/frame-shadow-light.png and /dev/null differ diff --git a/Base/res/icons/themes/Durrque/16x16/window-close-hover.png b/Base/res/icons/themes/Durrque/16x16/window-close-hover.png deleted file mode 100644 index 47add135a13..00000000000 Binary files a/Base/res/icons/themes/Durrque/16x16/window-close-hover.png and /dev/null differ diff --git a/Base/res/icons/themes/Durrque/16x16/window-close-modified-hover.png b/Base/res/icons/themes/Durrque/16x16/window-close-modified-hover.png deleted file mode 100644 index ebd8b786f42..00000000000 Binary files a/Base/res/icons/themes/Durrque/16x16/window-close-modified-hover.png and /dev/null differ diff --git a/Base/res/icons/themes/Durrque/16x16/window-close-modified.png b/Base/res/icons/themes/Durrque/16x16/window-close-modified.png deleted file mode 100644 index a528c094eab..00000000000 Binary files a/Base/res/icons/themes/Durrque/16x16/window-close-modified.png and /dev/null differ diff --git a/Base/res/icons/themes/Durrque/16x16/window-close.png b/Base/res/icons/themes/Durrque/16x16/window-close.png deleted file mode 100644 index dc77bf3e626..00000000000 Binary files a/Base/res/icons/themes/Durrque/16x16/window-close.png and /dev/null differ diff --git a/Base/res/icons/themes/Durrque/16x16/window-maximize-hover.png b/Base/res/icons/themes/Durrque/16x16/window-maximize-hover.png deleted file mode 100644 index a6b47f53555..00000000000 Binary files a/Base/res/icons/themes/Durrque/16x16/window-maximize-hover.png and /dev/null differ diff --git a/Base/res/icons/themes/Durrque/16x16/window-maximize.png b/Base/res/icons/themes/Durrque/16x16/window-maximize.png deleted file mode 100644 index 2f5b3dabb78..00000000000 Binary files a/Base/res/icons/themes/Durrque/16x16/window-maximize.png and /dev/null differ diff --git a/Base/res/icons/themes/Durrque/16x16/window-minimize-hover.png b/Base/res/icons/themes/Durrque/16x16/window-minimize-hover.png deleted file mode 100644 index e3d91392c7a..00000000000 Binary files a/Base/res/icons/themes/Durrque/16x16/window-minimize-hover.png and /dev/null differ diff --git a/Base/res/icons/themes/Durrque/16x16/window-minimize.png b/Base/res/icons/themes/Durrque/16x16/window-minimize.png deleted file mode 100644 index 0303c922004..00000000000 Binary files a/Base/res/icons/themes/Durrque/16x16/window-minimize.png and /dev/null differ diff --git a/Base/res/icons/themes/Durrque/16x16/window-restore-hover.png b/Base/res/icons/themes/Durrque/16x16/window-restore-hover.png deleted file mode 100644 index 6fc1a9d1c41..00000000000 Binary files a/Base/res/icons/themes/Durrque/16x16/window-restore-hover.png and /dev/null differ diff --git a/Base/res/icons/themes/Durrque/16x16/window-restore.png b/Base/res/icons/themes/Durrque/16x16/window-restore.png deleted file mode 100644 index da5aa93fcff..00000000000 Binary files a/Base/res/icons/themes/Durrque/16x16/window-restore.png and /dev/null differ diff --git a/Base/res/icons/themes/Durrque/frame-shadow-dark.png b/Base/res/icons/themes/Durrque/frame-shadow-dark.png deleted file mode 100644 index 8e7c2bcf525..00000000000 Binary files a/Base/res/icons/themes/Durrque/frame-shadow-dark.png and /dev/null differ diff --git a/Base/res/icons/themes/Durrque/frame-shadow-light.png b/Base/res/icons/themes/Durrque/frame-shadow-light.png deleted file mode 100644 index 68fb89a59c0..00000000000 Binary files a/Base/res/icons/themes/Durrque/frame-shadow-light.png and /dev/null differ diff --git a/Base/res/icons/themes/Gruvbox-Dark/16x16/window-close-modified.png b/Base/res/icons/themes/Gruvbox-Dark/16x16/window-close-modified.png deleted file mode 100644 index dbc9102167a..00000000000 Binary files a/Base/res/icons/themes/Gruvbox-Dark/16x16/window-close-modified.png and /dev/null differ diff --git a/Base/res/icons/themes/Gruvbox-Dark/16x16/window-close.png b/Base/res/icons/themes/Gruvbox-Dark/16x16/window-close.png deleted file mode 100644 index 475ce382cd7..00000000000 Binary files a/Base/res/icons/themes/Gruvbox-Dark/16x16/window-close.png and /dev/null differ diff --git a/Base/res/icons/themes/Gruvbox-Dark/16x16/window-maximize.png b/Base/res/icons/themes/Gruvbox-Dark/16x16/window-maximize.png deleted file mode 100644 index 2618d2f9f2f..00000000000 Binary files a/Base/res/icons/themes/Gruvbox-Dark/16x16/window-maximize.png and /dev/null differ diff --git a/Base/res/icons/themes/Gruvbox-Dark/16x16/window-minimize.png b/Base/res/icons/themes/Gruvbox-Dark/16x16/window-minimize.png deleted file mode 100644 index 9d964522be4..00000000000 Binary files a/Base/res/icons/themes/Gruvbox-Dark/16x16/window-minimize.png and /dev/null differ diff --git a/Base/res/icons/themes/Gruvbox-Dark/16x16/window-restore.png b/Base/res/icons/themes/Gruvbox-Dark/16x16/window-restore.png deleted file mode 100644 index 681103bb720..00000000000 Binary files a/Base/res/icons/themes/Gruvbox-Dark/16x16/window-restore.png and /dev/null differ diff --git a/Base/res/icons/themes/Light/16x16/window-close-hover.png b/Base/res/icons/themes/Light/16x16/window-close-hover.png deleted file mode 100644 index 8cabdfa0fce..00000000000 Binary files a/Base/res/icons/themes/Light/16x16/window-close-hover.png and /dev/null differ diff --git a/Base/res/icons/themes/Light/16x16/window-close-modified-hover.png b/Base/res/icons/themes/Light/16x16/window-close-modified-hover.png deleted file mode 100644 index d27e288da97..00000000000 Binary files a/Base/res/icons/themes/Light/16x16/window-close-modified-hover.png and /dev/null differ diff --git a/Base/res/icons/themes/Light/16x16/window-close-modified.png b/Base/res/icons/themes/Light/16x16/window-close-modified.png deleted file mode 100644 index 6b1a713a56b..00000000000 Binary files a/Base/res/icons/themes/Light/16x16/window-close-modified.png and /dev/null differ diff --git a/Base/res/icons/themes/Light/16x16/window-close.png b/Base/res/icons/themes/Light/16x16/window-close.png deleted file mode 100644 index 5bca3842ce8..00000000000 Binary files a/Base/res/icons/themes/Light/16x16/window-close.png and /dev/null differ diff --git a/Base/res/icons/themes/Light/16x16/window-maximize-hover.png b/Base/res/icons/themes/Light/16x16/window-maximize-hover.png deleted file mode 100644 index db7c0512d9e..00000000000 Binary files a/Base/res/icons/themes/Light/16x16/window-maximize-hover.png and /dev/null differ diff --git a/Base/res/icons/themes/Light/16x16/window-maximize.png b/Base/res/icons/themes/Light/16x16/window-maximize.png deleted file mode 100644 index ed23618da4c..00000000000 Binary files a/Base/res/icons/themes/Light/16x16/window-maximize.png and /dev/null differ diff --git a/Base/res/icons/themes/Light/16x16/window-minimize-hover.png b/Base/res/icons/themes/Light/16x16/window-minimize-hover.png deleted file mode 100644 index fbd099a5828..00000000000 Binary files a/Base/res/icons/themes/Light/16x16/window-minimize-hover.png and /dev/null differ diff --git a/Base/res/icons/themes/Light/16x16/window-minimize.png b/Base/res/icons/themes/Light/16x16/window-minimize.png deleted file mode 100644 index 95445440324..00000000000 Binary files a/Base/res/icons/themes/Light/16x16/window-minimize.png and /dev/null differ diff --git a/Base/res/icons/themes/Light/16x16/window-restore-hover.png b/Base/res/icons/themes/Light/16x16/window-restore-hover.png deleted file mode 100644 index 525e1cd099f..00000000000 Binary files a/Base/res/icons/themes/Light/16x16/window-restore-hover.png and /dev/null differ diff --git a/Base/res/icons/themes/Light/16x16/window-restore.png b/Base/res/icons/themes/Light/16x16/window-restore.png deleted file mode 100644 index 375b04f297a..00000000000 Binary files a/Base/res/icons/themes/Light/16x16/window-restore.png and /dev/null differ diff --git a/Base/res/icons/themes/Redmond/16x16/window-close-modified.png b/Base/res/icons/themes/Redmond/16x16/window-close-modified.png deleted file mode 100644 index 1be1f673657..00000000000 Binary files a/Base/res/icons/themes/Redmond/16x16/window-close-modified.png and /dev/null differ diff --git a/Base/res/icons/themes/Redmond/16x16/window-close.png b/Base/res/icons/themes/Redmond/16x16/window-close.png deleted file mode 100644 index b599d0bdfd3..00000000000 Binary files a/Base/res/icons/themes/Redmond/16x16/window-close.png and /dev/null differ diff --git a/Base/res/icons/themes/Redmond/16x16/window-maximize.png b/Base/res/icons/themes/Redmond/16x16/window-maximize.png deleted file mode 100644 index 9dc5a782567..00000000000 Binary files a/Base/res/icons/themes/Redmond/16x16/window-maximize.png and /dev/null differ diff --git a/Base/res/icons/themes/Redmond/16x16/window-minimize.png b/Base/res/icons/themes/Redmond/16x16/window-minimize.png deleted file mode 100644 index a1577544c31..00000000000 Binary files a/Base/res/icons/themes/Redmond/16x16/window-minimize.png and /dev/null differ diff --git a/Base/res/icons/themes/Redmond/16x16/window-restore.png b/Base/res/icons/themes/Redmond/16x16/window-restore.png deleted file mode 100644 index 0d17bcfaa1c..00000000000 Binary files a/Base/res/icons/themes/Redmond/16x16/window-restore.png and /dev/null differ diff --git a/Base/res/icons/themes/Redmond/menu-shadow.png b/Base/res/icons/themes/Redmond/menu-shadow.png deleted file mode 100644 index 09168ca1fe0..00000000000 Binary files a/Base/res/icons/themes/Redmond/menu-shadow.png and /dev/null differ diff --git a/Base/res/icons/themes/Silver/16x16/window-close-hover.png b/Base/res/icons/themes/Silver/16x16/window-close-hover.png deleted file mode 100644 index 8cd2e9a917a..00000000000 Binary files a/Base/res/icons/themes/Silver/16x16/window-close-hover.png and /dev/null differ diff --git a/Base/res/icons/themes/Silver/16x16/window-close-modified-hover.png b/Base/res/icons/themes/Silver/16x16/window-close-modified-hover.png deleted file mode 100644 index 15a6f283791..00000000000 Binary files a/Base/res/icons/themes/Silver/16x16/window-close-modified-hover.png and /dev/null differ diff --git a/Base/res/icons/themes/Silver/16x16/window-close-modified.png b/Base/res/icons/themes/Silver/16x16/window-close-modified.png deleted file mode 100644 index bedf1bb9fd6..00000000000 Binary files a/Base/res/icons/themes/Silver/16x16/window-close-modified.png and /dev/null differ diff --git a/Base/res/icons/themes/Silver/16x16/window-close.png b/Base/res/icons/themes/Silver/16x16/window-close.png deleted file mode 100644 index 60a5d2563ac..00000000000 Binary files a/Base/res/icons/themes/Silver/16x16/window-close.png and /dev/null differ diff --git a/Base/res/icons/themes/Silver/16x16/window-maximize-hover.png b/Base/res/icons/themes/Silver/16x16/window-maximize-hover.png deleted file mode 100644 index 3bfb9d34cf3..00000000000 Binary files a/Base/res/icons/themes/Silver/16x16/window-maximize-hover.png and /dev/null differ diff --git a/Base/res/icons/themes/Silver/16x16/window-maximize.png b/Base/res/icons/themes/Silver/16x16/window-maximize.png deleted file mode 100644 index f05bacbcb56..00000000000 Binary files a/Base/res/icons/themes/Silver/16x16/window-maximize.png and /dev/null differ diff --git a/Base/res/icons/themes/Silver/16x16/window-minimize-hover.png b/Base/res/icons/themes/Silver/16x16/window-minimize-hover.png deleted file mode 100644 index 7ee27995f92..00000000000 Binary files a/Base/res/icons/themes/Silver/16x16/window-minimize-hover.png and /dev/null differ diff --git a/Base/res/icons/themes/Silver/16x16/window-minimize.png b/Base/res/icons/themes/Silver/16x16/window-minimize.png deleted file mode 100644 index d4590744f08..00000000000 Binary files a/Base/res/icons/themes/Silver/16x16/window-minimize.png and /dev/null differ diff --git a/Base/res/icons/themes/Silver/16x16/window-restore-hover.png b/Base/res/icons/themes/Silver/16x16/window-restore-hover.png deleted file mode 100644 index 78227f1bcb2..00000000000 Binary files a/Base/res/icons/themes/Silver/16x16/window-restore-hover.png and /dev/null differ diff --git a/Base/res/icons/themes/Silver/16x16/window-restore.png b/Base/res/icons/themes/Silver/16x16/window-restore.png deleted file mode 100644 index f947a33151c..00000000000 Binary files a/Base/res/icons/themes/Silver/16x16/window-restore.png and /dev/null differ diff --git a/Base/res/icons/themes/Sunshine/16x16/window-close-modified.png b/Base/res/icons/themes/Sunshine/16x16/window-close-modified.png deleted file mode 100644 index cfff0bcbe68..00000000000 Binary files a/Base/res/icons/themes/Sunshine/16x16/window-close-modified.png and /dev/null differ diff --git a/Base/res/icons/themes/Sunshine/16x16/window-close.png b/Base/res/icons/themes/Sunshine/16x16/window-close.png deleted file mode 100644 index 4e79eba6d36..00000000000 Binary files a/Base/res/icons/themes/Sunshine/16x16/window-close.png and /dev/null differ diff --git a/Base/res/icons/themes/Sunshine/16x16/window-maximize.png b/Base/res/icons/themes/Sunshine/16x16/window-maximize.png deleted file mode 100644 index 8e74b3a26e5..00000000000 Binary files a/Base/res/icons/themes/Sunshine/16x16/window-maximize.png and /dev/null differ diff --git a/Base/res/icons/themes/Sunshine/16x16/window-minimize.png b/Base/res/icons/themes/Sunshine/16x16/window-minimize.png deleted file mode 100644 index 23f03ba3bd6..00000000000 Binary files a/Base/res/icons/themes/Sunshine/16x16/window-minimize.png and /dev/null differ diff --git a/Base/res/icons/themes/Sunshine/16x16/window-restore.png b/Base/res/icons/themes/Sunshine/16x16/window-restore.png deleted file mode 100644 index 703eed5d8be..00000000000 Binary files a/Base/res/icons/themes/Sunshine/16x16/window-restore.png and /dev/null differ diff --git a/Base/res/js/Spreadsheet/runtime.js b/Base/res/js/Spreadsheet/runtime.js deleted file mode 100644 index 527622732e7..00000000000 --- a/Base/res/js/Spreadsheet/runtime.js +++ /dev/null @@ -1,1385 +0,0 @@ -"use strict"; - -const Break = {}; - -// FIXME: Figure out a way to document non-function entities too. -class Position { - constructor(column, row, sheet) { - this.column = column; - this.row = row; - this.sheet = sheet ?? thisSheet; - this.name = `${column}${row}`; - } - - get contents() { - return this.sheet.get_real_cell_contents(this.name); - } - - set contents(value) { - value = `${value}`; - this.sheet.set_real_cell_contents(this.name, value); - return value; - } - - static from_name(name) { - let sheet = thisSheet; - let obj = sheet.parse_cell_name(name); - return new Position(obj.column, obj.row, sheet); - } - - up(how_many) { - how_many = how_many ?? 1; - const row = Math.max(0, this.row - how_many); - return new Position(this.column, row, this.sheet); - } - - down(how_many) { - how_many = how_many ?? 1; - const row = Math.max(0, this.row + how_many); - return new Position(this.column, row, this.sheet); - } - - left(how_many) { - how_many = how_many ?? 1; - return new Position( - this.sheet.column_arithmetic(this.column, -how_many), - this.row, - this.sheet - ); - } - - right(how_many) { - how_many = how_many ?? 1; - return new Position( - this.sheet.column_arithmetic(this.column, how_many), - this.row, - this.sheet - ); - } - - range_up() { - if (this.row === 0) throw new Error(`No cells above this cell`); - const up_one = this.up(1); - let current_point = up_one; - for ( - let point = current_point.up(1); - current_point.row !== 0 && point.value() !== ""; - point = current_point.up(1) - ) - current_point = point; - - const sheetName = Object.is(this.sheet, thisSheet) - ? "" - : `sheet(${JSON.stringify(this.sheet.name)}):`; - return R(sheetName + current_point.name + ":" + up_one.name); - } - - range_down() { - let down_one = this.down(1); - let current_point = down_one; - for (let point = current_point.down(1); point.value() !== ""; point = current_point.down(1)) - current_point = point; - - const sheetName = Object.is(this.sheet, thisSheet) - ? "" - : `sheet(${JSON.stringify(this.sheet.name)}):`; - return R(sheetName + current_point.name + ":" + down_one.name); - } - - range_left() { - if (this.column === "A") throw new Error(`No cells to the left of this cell`); - const left_one = this.left(1); - let current_point = left_one; - for ( - let point = current_point.left(1); - current_point.column !== "A" && point.value() !== ""; - point = current_point.left(1) - ) - current_point = point; - - const sheetName = Object.is(this.sheet, thisSheet) - ? "" - : `sheet(${JSON.stringify(this.sheet.name)}):`; - return R(sheetName + current_point.name + ":" + left_one.name); - } - - range_right() { - let right_one = this.right(1); - let current_point = right_one; - for ( - let point = current_point.right(1); - point.value() !== ""; - point = current_point.right(1) - ) - current_point = point; - - const sheetName = Object.is(this.sheet, thisSheet) - ? "" - : `sheet(${JSON.stringify(this.sheet.name)}):`; - return R(sheetName + current_point.name + ":" + right_one.name); - } - - with_column(value) { - return new Position(value, this.row, this.sheet); - } - - with_row(value) { - return new Position(this.column, value, this.sheet); - } - - in_sheet(the_sheet) { - return new Position(this.column, this.row, sheet(the_sheet)); - } - - value() { - return this.sheet[this.name]; - } - - valueOf() { - return value(); - } - - toString() { - return ``; - } -} - -class CommonRange { - at(wantedIx) { - let ix = 0; - let found = null; - this.forEach(cell => { - if (ix++ === wantedIx) { - found = cell; - return Break; - } - }); - return found; - } - - findIndex(matcher) { - let i = 0; - let found = false; - this.forEach(cell => { - if (matcher(cell, i)) { - found = true; - return Break; - } - ++i; - }); - return found ? i : -1; - } - - find(matcher) { - let value = null; - let i = 0; - this.forEach(cell => { - if (matcher(cell, i)) { - value = cell; - return Break; - } - ++i; - }); - return value; - } - - indexOf(name) { - let i = 0; - let found = false; - this.forEach(cell => { - if (cell.name === name) { - found = true; - return Break; - } - ++i; - }); - return found ? i : -1; - } - - has(name) { - return this.indexOf(name) !== -1; - } - - toArray() { - const cells = []; - this.forEach(val => cells.push(val)); - return cells; - } - - filter(matches) { - const cells = []; - this.forEach(cell => { - if (matches(cell)) cells.push(cell); - }); - return new SplitRange(cells); - } - - unique() { - const cells = []; - const values = new Set(); - this.forEach(cell => { - const value = cell.value(); - if (!values.has(value)) { - values.add(value); - cells.push(cell); - } - }); - return new SplitRange(cells); - } -} - -class SplitRange extends CommonRange { - constructor(cells) { - super(); - this.cells = cells; - } - - static fromNames(...cellNames) { - return new SplitRange(cellNames.map(Position.from_name)); - } - - first() { - return this.cellNames[0]; - } - - forEach(callback) { - for (const cell of this.cells) { - if (callback(cell) === Break) return; - } - } - - toString() { - const namesFormatted = this.cells.map(cell => '"' + cell.name + '"').join(", "); - return `SplitRange.fromNames(${namesFormatted})`; - } -} - -class Ranges extends CommonRange { - constructor(ranges) { - super(); - this.ranges = ranges; - } - - first() { - return this.ranges[0].first(); - } - - static from(...ranges) { - return new Ranges(ranges); - } - - forEach(callback) { - for (const range of this.ranges) { - if (range.forEach(callback) === Break) break; - } - } - - union(other, direction = "right") { - if (direction === "left") { - if (other instanceof Ranges) return Ranges.from(...other.ranges, ...this.ranges); - return Ranges.from(other, ...this.ranges); - } else if (direction === "right") { - if (other instanceof Ranges) return Ranges.from(...this.ranges, ...other.ranges); - return Ranges.from(...this.ranges, other); - } else { - throw new Error(`Invalid direction '${direction}'`); - } - } - - toString() { - return `Ranges.from(${this.ranges.map(r => r.toString()).join(", ")})`; - } -} - -class Range extends CommonRange { - constructor( - startingColumnName, - endingColumnName, - startingRow, - endingRow, - columnStep, - rowStep, - sheet - ) { - super(); - // using == to account for '0' since js will parse `+'0'` to 0 - if (columnStep == 0 || rowStep == 0) - throw new Error("rowStep or columnStep is 0, this will cause an infinite loop"); - if (typeof startingRow === "string" || typeof endingRow === "string") - throw new Error( - "startingRow or endingRow is a string, this will cause an infinite loop" - ); - this.startingColumnName = startingColumnName; - this.endingColumnName = endingColumnName; - this.startingRow = startingRow; - this.endingRow = endingRow; - this.columnStep = columnStep ?? 1; - this.rowStep = rowStep ?? 1; - this.spansEntireColumn = endingRow === undefined; - this.sheet = sheet; - if (!this.spansEntireColumn && startingRow === undefined) - throw new Error("A Range with a defined end row must also have a defined start row"); - - this.normalize(); - } - - first() { - return new Position(this.startingColumnName, this.startingRow, this.sheet); - } - - forEach(callback) { - const ranges = []; - let startingColumnIndex = this.sheet.column_index(this.startingColumnName); - let endingColumnIndex = this.sheet.column_index(this.endingColumnName); - let columnDistance = endingColumnIndex - startingColumnIndex; - for ( - let columnOffset = 0; - columnOffset <= columnDistance; - columnOffset += this.columnStep - ) { - const columnName = this.sheet.column_arithmetic(this.startingColumnName, columnOffset); - ranges.push({ - column: columnName, - rowStart: this.startingRow, - rowEnd: this.spansEntireColumn - ? this.sheet.get_column_bound(columnName) - : this.endingRow, - }); - } - - outer: for (const range of ranges) { - for (let row = range.rowStart; row <= range.rowEnd; row += this.rowStep) { - if (callback(new Position(range.column, row, this.sheet)) === Break) break outer; - } - } - } - - union(other) { - if (other instanceof Ranges) return other.union(this, "left"); - - if (other instanceof Range) return Ranges.from(this, other); - - throw new Error(`Cannot add ${other} to a Range`); - } - - normalize() { - const startColumnIndex = this.sheet.column_index(this.startingColumnName); - const endColumnIndex = this.sheet.column_index(this.endingColumnName); - if (startColumnIndex > endColumnIndex) { - const temp = this.startingColumnName; - this.startingColumnName = this.endingColumnName; - this.endingColumnName = temp; - } - - if (this.startingRow !== undefined && this.endingRow !== undefined) { - if (this.startingRow > this.endingRow) { - const temp = this.startingRow; - this.startingRow = this.endingRow; - this.endingRow = temp; - } - } - } - - toString() { - const endingRow = this.endingRow ?? ""; - const showSteps = this.rowStep !== 1 || this.columnStep !== 1; - const steps = showSteps ? `:${this.columnStep}:${this.rowStep}` : ""; - const sheetName = Object.is(thisSheet, this.sheet) - ? "" - : `sheet(${JSON.stringify(this.sheet.name)}):`; - return `R\`${sheetName}${this.startingColumnName}${this.startingRow}:${this.endingColumnName}${endingRow}${steps}\``; - } -} - -const R_FORMAT = - /^(?:sheet\(("(?:[^"]|\\")*")\):)?([a-zA-Z_]+)(?:(\d+):([a-zA-Z_]+)(\d+)?(?::(\d+):(\d+))?)?$/; -function R(fmt, ...args) { - if (args.length !== 0) throw new TypeError("R`` format must be a literal"); - // done because: - // const myFunc = xyz => JSON.stringify(xyz) - // myFunc("ABC") => ""ABC"" - // myFunc`ABC` => "["ABC"]" - if (Array.isArray(fmt)) fmt = fmt[0]; - if (!R_FORMAT.test(fmt)) - throw new Error( - 'Invalid Format. Expected Format: R`A` or R`A0:A1` or R`A0:A2:1:2` or R`sheet("sheetName"):...`' - ); - // Format: (sheet("sheetName"):)?Col(Row:Col(Row)?(:ColStep:RowStep)?)? - // Ignore the first element of the match array as that will be the whole match. - const [, ...matches] = fmt.match(R_FORMAT); - const [sheetExpression, startCol, startRow, endCol, endRow, colStep, rowStep] = matches; - const sheetFromName = name => { - if (name == null || name === "") return thisSheet; - return sheet(JSON.parse(name)); - }; - return new Range( - startCol, - endCol ?? startCol, - integer(startRow ?? 0), - // Don't make undefined an integer, because then it becomes 0. - !!endRow ? integer(endRow) : endRow, - integer(colStep ?? 1), - integer(rowStep ?? 1), - sheetFromName(sheetExpression) - ); -} - -function select(criteria, t, f) { - if (criteria) return t; - return f; -} - -function choose(index, ...args) { - if (index > args.length) return undefined; - if (index < 0) return undefined; - return args[index]; -} - -function now() { - return new Date(); -} - -function repeat(count, str) { - return Array(count + 1).join(str); -} - -function randRange(min, max) { - return Math.random() * (max - min) + min; -} - -function integer(value) { - const typeVal = typeof value; - if ((typeVal !== "number" && typeVal !== "string") || Number.isNaN(Number(value))) - throw new Error(`integer() called with unexpected type "${typeVal}"`); - return value | 0; -} - -function sheet(name) { - return workbook.sheet(name); -} - -function reduce(op, accumulator, cells) { - return resolve(cells).reduce(op, accumulator); -} - -function numericReduce(op, accumulator, cells) { - return numericResolve(cells).reduce(op, accumulator); -} - -function numericResolve(cells) { - return resolve(cells).map(val => parseFloat(val)); -} - -function resolve(cells) { - if (!(cells instanceof Array)) { - cells = [cells]; - } - return cells.map(resolveRange).flat(); -} - -function resolveRange(cells) { - const isRange = cells instanceof CommonRange; - return isRange ? cells.toArray().map(cell => cell.value()) : cells; -} - -// Statistics - -function sum(...cells) { - return numericReduce((acc, x) => acc + x, 0, cells); -} - -function sumIf(condition, ...cells) { - return numericReduce((acc, x) => (condition(x) ? acc + x : acc), 0, cells); -} - -function count(...cells) { - return reduce((acc, x) => acc + 1, 0, cells); -} - -function countIf(condition, ...cells) { - return reduce((acc, x) => (condition(x) ? acc + 1 : acc), 0, cells); -} - -function average(...cells) { - const sumAndCount = numericReduce((acc, x) => [acc[0] + x, acc[1] + 1], [0, 0], cells); - return sumAndCount[0] / sumAndCount[1]; -} - -function averageIf(condition, ...cells) { - const sumAndCount = numericReduce( - (acc, x) => (condition(x) ? [acc[0] + x, acc[1] + 1] : acc), - [0, 0], - cells - ); - return sumAndCount[0] / sumAndCount[1]; -} - -function maxIf(condition, ...cells) { - return Math.max(...numericResolve(cells).filter(condition)); -} - -function max(...cells) { - return maxIf(() => true, ...cells); -} - -function minIf(condition, ...cells) { - return Math.min(...numericResolve(cells).filter(condition)); -} - -function min(...cells) { - return minIf(() => true, ...cells); -} - -function sumProductIf(condition, rangeOne, rangeTwo) { - const rangeOneNums = numericResolve(rangeOne); - const rangeTwoNums = numericResolve(rangeTwo); - return rangeOneNums.reduce((accumulator, curr, i) => { - const prod = curr * rangeTwoNums[i]; - if (!condition(curr, rangeTwoNums[i], prod)) return accumulator; - return accumulator + prod; - }, 0); -} - -function sumProduct(rangeOne, rangeTwo) { - return sumProductIf(() => true, rangeOne, rangeTwo); -} - -function median(...cells) { - const values = numericResolve(cells); - - if (values.length === 0) return 0; - - function qselect(arr, idx) { - if (arr.length === 1) return arr[0]; - - const pivot = arr[0]; - const ls = arr.filter(x => x < pivot); - const hs = arr.filter(x => x > pivot); - const eqs = arr.filter(x => x === pivot); - - if (idx < ls.length) return qselect(ls, k); - - if (idx < ls.length + eqs.length) return pivot; - - return qselect(hs, idx - ls.length - eqs.length); - } - - if (values.length % 2) return qselect(values, values.length / 2); - - return (qselect(values, values.length / 2) + qselect(values, values.length / 2 - 1)) / 2; -} - -function variance(...cells) { - const sumsAndSquaresAndCount = numericReduce( - (acc, x) => [acc[0] + x, acc[1] + x * x, acc[2] + 1], - [0, 0, 0], - cells - ); - let sums = sumsAndSquaresAndCount[0]; - let squares = sumsAndSquaresAndCount[1]; - let count = sumsAndSquaresAndCount[2]; - - return (count * squares - sums * sums) / count; -} - -function mode(...cells) { - const counts = numericReduce( - (map, x) => { - if (!map.has(x)) map.set(x, 0); - map.set(x, map.get(x) + 1); - return map; - }, - new Map(), - cells - ); - - let mostCommonValue = undefined; - let mostCommonCount = -1; - counts.forEach((count, value) => { - if (count > mostCommonCount) { - mostCommonCount = count; - mostCommonValue = value; - } - }); - - return mostCommonValue; -} - -function stddev(...cells) { - return Math.sqrt(variance(...cells)); -} - -// Lookup - -function row() { - return thisSheet.current_cell_position().row; -} - -function column() { - return thisSheet.current_cell_position().column; -} - -function here() { - const position = thisSheet.current_cell_position(); - return new Position(position.column, position.row, thisSheet); -} - -function internal_lookup( - req_lookup_value, - lookup_inputs, - lookup_outputs, - if_missing, - mode, - reference -) { - if_missing = if_missing ?? undefined; - const missing = () => { - if (if_missing !== undefined) return if_missing; - - throw new Error(`Failed to find ${req_lookup_value} in ${lookup_inputs}`); - }; - - mode = mode ?? "exact"; - const lookup_value = req_lookup_value; - let matches = null; - - if (mode === "exact") { - matches = value => value === lookup_value; - } else if (mode === "nextlargest") { - matches = value => value >= lookup_value; - } else if (mode === "nextsmallest") { - matches = value => value <= lookup_value; - } else { - throw new Error(`Match mode '${mode}' not supported`); - } - - let i = 0; - let value = null; - let found_input = null; - lookup_inputs.forEach(cell => { - value = cell.value(); - if (matches(value)) { - found_input = cell; - return Break; - } - ++i; - }); - - if (found_input == null) return missing(); - - if (lookup_outputs === undefined) { - if (reference) return found_input; - - return value; - } - - const found_output = lookup_outputs.at(i); - - if (found_output == null) - throw new Error("Lookup target length must not be smaller than lookup source length"); - - if (reference) return found_output; - - return found_output.value(); -} - -function lookup(req_lookup_value, lookup_inputs, lookup_outputs, if_missing, mode) { - return internal_lookup( - req_lookup_value, - lookup_inputs, - lookup_outputs, - if_missing, - mode, - false - ); -} - -function reflookup(req_lookup_value, lookup_inputs, lookup_outputs, if_missing, mode) { - return internal_lookup(req_lookup_value, lookup_inputs, lookup_outputs, if_missing, mode, true); -} - -// Cheat the system and add documentation -R.__documentation = JSON.stringify({ - name: "R", - argc: 1, - argnames: ["range specifier"], - doc: - "Generates a Range object, from the given" + - "range specifier, which must conform to the syntax shown below", - examples: { - "R`A`": "Generate a Range representing all the cells in the column A", - "R`A:C`": "Generate a Range representing all the cells in the columns A through C", - "R`A:C:2:2`": - "Generate a Range representing every other cells in every other column in A through C", - "R`A1:C4`": - "Generate a Range representing all cells in a rectangle with the top-left cell A1, and the bottom-right cell C4", - "R`A0:B10:1:2`": - "Generate a Range representing all cells in a rectangle with the top-left cell A1, and the bottom-right cell C4, with every column, and skipping every other row", - }, -}); - -select.__documentation = JSON.stringify({ - name: "select", - argc: 3, - argnames: ["criteria", "true value", "false value"], - doc: "Selects between the two `true` and `false` values based on the value of `criteria`", - examples: { - "select(A1, A2, A3)": "Evaluates to A2 if A1 is true, A3 otherwise", - }, -}); - -choose.__documentation = JSON.stringify({ - name: "choose", - argc: 1, - argnames: ["index"], - doc: "Selects an argument by the given `index`, starting at zero", - examples: { - "choose(A3, 'Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat')": - "Get the day name by the number in A3", - "choose(randRange(0, 2), 'Well', 'Hello', 'Friends')": - "Randomly pick one of the three words 'well', 'hello' and 'friends'", - }, -}); - -now.__documentation = JSON.stringify({ - name: "now", - argc: 0, - argnames: [], - doc: "Returns a Date instance for the current moment", - examples: { - "now().toString()": - "Returns a string containing the current date. Ex: 'Tue Sep 21 2021 02:38:10 GMT+0000 (UTC)'", - "now().getFullYear()": "Returns the current year. Ex: 2021", - }, -}); - -repeat.__documentation = JSON.stringify({ - name: "repeat", - argc: 2, - argnames: ["string", "count"], - doc: "Returns a string equivalent to `string` repeated `count` times", - examples: { - 'repeat("a", 10)': 'Generates the string "aaaaaaaaaa"', - }, -}); - -randRange.__documentation = JSON.stringify({ - name: "randRange", - argc: 2, - argnames: ["start", "end"], - doc: "Returns a random number in the range (`start`, `end`)", - examples: { - "randRange(0, 10)": "Returns a number from 0 through 10. Ex: 5.185799582250052", - }, -}); - -integer.__documentation = JSON.stringify({ - name: "integer", - argc: 1, - argnames: ["value"], - doc: "Returns the integer value of `value`", - examples: { - "A1 = integer(A0)": "Sets the value of the cell A1 to the integer value of the cell A0", - }, -}); - -sheet.__documentation = JSON.stringify({ - name: "sheet", - argc: 1, - argnames: ["name or index"], - doc: "Returns a reference to another sheet, identified by _name_ or _index_", - examples: { - "sheet('Sheet 1').A4": "Read the value of the cell A4 in a sheet named 'Sheet 1'", - "sheet(0).A0 = 123": "Set the value of the cell A0 in the first sheet to 123", - }, -}); - -reduce.__documentation = JSON.stringify({ - name: "reduce", - argc: 3, - argnames: ["reduction function", "accumulator", "cells"], - doc: - "Reduces the entries in `cells` with repeated applications of the `reduction function` " + - "to the `accumulator`\n The `reduction function` should be a function of arity 2, taking " + - "first the accumulator, then the current value, and returning the new accumulator value\n\n" + - "Please keep in mind that this function respects the cell type, and can yield non-numeric " + - "values to the `current value`.", - examples: { - "reduce((acc, x) => acc * x, 1, R`A0:A5`)": - "Calculate the product of all values in the range A0:A5", - }, -}); - -numericReduce.__documentation = JSON.stringify({ - name: "numericReduce", - argc: 3, - argnames: ["reduction function", "accumulator", "cells"], - doc: - "Reduces the entries in `cells` with repeated applications of the `reduction function` to the " + - "`accumulator`\n The `reduction function` should be a function of arity 2, taking first the " + - "accumulator, then the current value, and returning the new accumulator value\n\nThis function, " + - "unlike [`reduce`](spreadsheet://doc/reduce), casts the values to a number before passing them to the `reduction function`.", - examples: { - "numericReduce((acc, x) => acc * x, 1, R`A0:A5`)": - "Calculate the numeric product of all values in the range A0:A5", - }, -}); - -sum.__documentation = JSON.stringify({ - name: "sum", - argc: 1, - argnames: ["numbers or cell names"], - doc: "Calculates the total of the numbers or cell values in `numbers or cell names`", - examples: { - "sum(R`A0:C3`)": - "Calculate the sum of the values in A0:C3, [Click to view](spreadsheet://example/variance#simple)", - "sum(1, 2, 3)": "Calculate the sum of 1, 2, and 3 (Sum = 6)", - }, -}); - -sumIf.__documentation = JSON.stringify({ - name: "sumIf", - argc: 2, - argnames: ["condition", "numbers or cell names"], - doc: "Calculates the sum of all numbers or cell values which evaluate to true when passed to `condition`", - examples: { - "sumIf(x => x instanceof Number, R`A1:C4`)": - "Calculates the sum of all numbers within A1:C4", - }, -}); - -count.__documentation = JSON.stringify({ - name: "count", - argc: 1, - argnames: ["numbers or cell names"], - doc: "Counts the number of inputs or cells in a given range", - examples: { - "count(R`A0:C3`)": - "Count the number of cells in A0:C3, [Click to view](spreadsheet://example/variance#simple)", - }, -}); - -countIf.__documentation = JSON.stringify({ - name: "countIf", - argc: 2, - argnames: ["condition", "numbers or cell names"], - doc: "Counts inputs or cell values which evaluate to true when passed to `condition`", - examples: { - "countIf(x => x instanceof Number, R`A1:C3`)": - "Count the number of cells which have numbers within A1:C3", - }, -}); - -average.__documentation = JSON.stringify({ - name: "average", - argc: 1, - argnames: ["numbers or cell names"], - doc: "Calculates the average of the numbers or cell values in `numbers or cell names`", - examples: { - "average(R`A0:C3`)": - "Calculate the average of the values in A0:C3, [Click to view](spreadsheet://example/variance#simple)", - "average(4, 6)": "Calculate the average of 4 and 6 (Average = 5)", - }, -}); - -averageIf.__documentation = JSON.stringify({ - name: "averageIf", - argc: 2, - argnames: ["condition", "numbers or cell names"], - doc: "Calculates the average of all numbers or cell values which evaluate to true when passed to `condition`", - examples: { - "averageIf(x => x > 4, R`A1:C4`)": - "Calculate the average of all numbers larger then 4 within A1:C4", - }, -}); - -max.__documentation = JSON.stringify({ - name: "max", - argc: 1, - argnames: ["numbers or cell names"], - doc: "Calculates the largest number or cell value in `numbers or cell names`", - examples: { - "max(R`A1:C4`)": "Finds the largest number within A1:C4", - "max(1, 2, 3)": "Returns the largest of 1, 2, and 3 (Max = 3)", - }, -}); - -maxIf.__documentation = JSON.stringify({ - name: "maxIf", - argc: 1, - argnames: ["condition", "numbers or cell names"], - doc: "Calculates the largest of all numbers or cell values which evaluate to true when passed to `condition`", - examples: { - "maxIf(x => x > 4, R`A1:C4`)": - "Finds the largest number within A1:C4 that is greater than 4", - }, -}); - -min.__documentation = JSON.stringify({ - name: "min", - argc: 1, - argnames: ["numbers or cell names"], - doc: "Calculates the smallest number or cell value in `numbers or cell names`", - examples: { - "min(R`A1:C4`)": "Finds the smallest number within A1:C4", - "min(1, 2, 3)": "Returns the smallest of 1, 2, and 3 (Min = 1)", - }, -}); - -minIf.__documentation = JSON.stringify({ - name: "minIf", - argc: 1, - argnames: ["condition", "numbers or cell names"], - doc: "Calculates the smallest of all numbers or cell values which evaluate to true when passed to `condition`", - examples: { - "minIf(x => x > 4, R`A1:C4`)": - "Finds the smallest number within A1:C4 that is greater than 4", - }, -}); - -sumProduct.__documentation = JSON.stringify({ - name: "sumProduct", - argc: 2, - argnames: ["range one", "range two"], - doc: "For each cell in the first range, multiply it by the cell at the same index in range two, then add the result to a sum", - example_data: { - "sumProductIf((a, b, prod) => a > 2, R`A0:A`, R`B0:B`)": - "Calculate the product of each cell in a times it's equivalent cell in b, then adds the products, [Click to view](spreadsheet://example/sumProductIf#sum_product)", - }, -}); - -sumProductIf.__documentation = JSON.stringify({ - name: "sumProductIf", - argc: 3, - argnames: ["condition", "range one", "range two"], - doc: "For each cell in the first range, multiply it by the cell at the same index in range two, then add the result to a sum, if the condition evaluated to true", - examples: { - "sumProductIf((a, b, prod) => a > 2, R`A0:A`, R`B0:B`)": - "Calculate the product of each cell in a times it's equivalent cell in b, then adds the products if a's value was greater than 2, [Click to view](spreadsheet://example/sumProductIf#sum_product)", - }, - example_data: { - sum_product: { - name: "Sum Product", - columns: ["A", "B", "C"], - rows: 3, - cells: { - C0: { - kind: "Formula", - source: "sumProduct(R`A0:A`, R`B0:B`)", - value: "300.0", - type: "Numeric", - type_metadata: { - format: "sumProduct: %f", - }, - }, - C1: { - kind: "Formula", - source: "sumProductIf((a, b, prod) => a > 2, R`A0:A`, R`B0:B`)", - value: "250.0", - type: "Numeric", - type_metadata: { - format: "sumProductIf: %f", - }, - }, - ...Array.apply(null, { length: 4 }) - .map((_, i) => i) - .reduce((acc, i) => { - return { - ...acc, - [`A${i}`]: { - kind: "LiteralString", - value: `${i + 1}`, - type: "Numeric", - }, - [`B${i}`]: { - kind: "LiteralString", - value: `${(i + 1) * 10}`, - type: "Numeric", - }, - }; - }, {}), - }, - }, - }, -}); - -median.__documentation = JSON.stringify({ - name: "median", - argc: 1, - argnames: ["numbers or cell names"], - doc: "Calculates the median number or cell value in `numbers or cell names`", - examples: { - "median(R`A0:C3`)": - "Calculate the median of the values in A0:C3, [Click to view](spreadsheet://example/variance#simple)", - "median(1, 2, 5)": "Calculate the median of 1, 2, and 5 (Median = 2)", - }, -}); - -variance.__documentation = JSON.stringify({ - name: "variance", - argc: 1, - argnames: ["numbers or cell names"], - doc: "Calculates the variance of the numbers or cell values in `numbers or cell names`", - examples: { - "variance(R`A0:C3`)": - "Calculate the variance of the values in A0:C3, [Click to view](spreadsheet://example/variance#simple)", - }, - example_data: { - simple: { - name: "Simple Statistics", - columns: ["A", "B", "C", "D", "E"], - rows: 6, - cells: { - E0: { - kind: "Formula", - source: "stddev(R`A0:C3`)", - value: "5.329165", - type: "Numeric", - type_metadata: { - format: "stddev: %f", - }, - }, - E1: { - kind: "Formula", - source: "variance(R`A0:C3`)", - value: "28.39999999", - type: "Numeric", - type_metadata: { - format: "variance: %f", - }, - }, - E2: { - kind: "Formula", - source: "median(R`A0:C3`)", - value: "1", - type: "Numeric", - type_metadata: { - format: "median: %f", - }, - }, - E3: { - kind: "Formula", - source: "average(R`A0:C3`)", - value: "1.1999999", - type: "Numeric", - type_metadata: { - format: "average: %f", - }, - }, - E4: { - kind: "Formula", - source: "mode(R`A0:C3`)", - value: "1", - type: "Numeric", - type_metadata: { - format: "mode: %d", - }, - }, - E5: { - kind: "Formula", - source: "count(R`A0:C3`)", - value: "12", - type: "Numeric", - type_metadata: { - format: "count: %d", - }, - }, - E6: { - kind: "Formula", - source: "sum(R`A0:C3`)", - value: "18", - type: "Numeric", - type_metadata: { - format: "sum: %d", - }, - }, - ...Array.apply(null, { length: 4 }) - .map((_, i) => i) - .reduce((acc, i) => { - return { - ...acc, - [`A${i}`]: { - kind: "LiteralString", - value: `${i}`, - type: "Numeric", - }, - [`B${i}`]: { - kind: "LiteralString", - value: `${i + 1}`, - type: "Numeric", - }, - [`C${i}`]: { - kind: "LiteralString", - value: `${i - 1}`, - type: "Numeric", - }, - }; - }, {}), - }, - }, - }, -}); - -mode.__documentation = JSON.stringify({ - name: "mode", - argc: 1, - argnames: ["numbers or cell names"], - doc: "Calculates the mode (most common value) of the numbers or cell values in `numbers or cell names`", - examples: { - "mode(R`A2:A14`)": - "Calculate the mode of the values in A2:A14, [Click to view](spreadsheet://example/variance#simple)", - "mode(1, 2, 2)": "Calculate the mode of 1, 2, and 2 (Mode = 2)", - }, -}); - -stddev.__documentation = JSON.stringify({ - name: "stddev", - argc: 1, - argnames: ["cell names"], - doc: "Calculates the standard deviation (square root of variance) of the numbers or cell values in `numbers or cell names`", - examples: { - "stddev(R`A0:C3`)": - "Calculate the standard deviation of the values in A0:C3, [Click to view](spreadsheet://example/variance#simple)", - }, -}); - -row.__documentation = JSON.stringify({ - name: "row", - argc: 0, - argnames: [], - doc: "Returns the row number of the current cell", - examples: { - "row()": "Evaluates to 6 if placed in A6", - }, -}); - -column.__documentation = JSON.stringify({ - name: "column", - argc: 0, - argnames: [], - doc: "Returns the column name of the current cell", - examples: { - "column()": "Evaluates to A if placed in A6", - }, -}); - -here.__documentation = JSON.stringify({ - name: "here", - argc: 0, - argnames: [], - doc: - "Returns an object representing the current cell's position, see `Position` below.\n\n" + - "## Position\na `Position` is an object representing a given cell position in a given sheet.\n" + - "### Methods:\n" + - "- `up(count = 1)`: goes up count cells, or returns the top position if at the top\n" + - "- `down(count = 1)`: goes down count cells\n" + - "- `left(count = 1)`: Goes left count cells, or returns the leftmost position if the edge\n" + - "- `right(count = 1)`: Goes right count cells.\n" + - "- `range_up()`: make a range from the cell above this cell, upward, until there is a cell with no number in it.\n" + - "- `range_down()`: make a range from the cell below this cell, downward, until there is a cell with no number in it.\n" + - "- `range_left()`: make a range from the cell to the left of this cell, going left, until there is a cell with no number in it.\n" + - "- `range_right()`: make a range from the cell to the right of this cell, going right, until there is a cell with no number in it.\n" + - "- `with_row(row)`: Returns a Position with its column being this object's, and its row being the provided the value.\n" + - "- `with_column(column)`: Similar to `with_row()`, but changes the column instead.\n" + - "- `in_sheet(the_sheet)`: Returns a Position with the same column and row as this one, but with its sheet being `the_sheet`.\n" + - "- `value()`: Returns the value at the position which it represents, in the object's sheet (current sheet by default).\n" + - "- `contents`: An accessor for the real contents of the cell (i.e. the text as typed in the cell editor)\n", - examples: { - "here().up().value()": "Get the value of the cell above this one", - "here().up().with_column('A')": - "Get a Position above this one in column A, for instance, evaluates to A2 if run in B3, [Click to view](spreadsheet://example/here#with_column)", - }, - example_data: { - with_column: { - name: "here() With Column", - columns: ["A", "B"], - rows: 4, - cells: { - B3: { - kind: "Formula", - source: "here().up().with_column('A').name", - value: '"A2"', - type: "Identity", - }, - }, - }, - }, -}); - -lookup.__documentation = JSON.stringify({ - name: "lookup", - argc: 2, - argnames: [ - "lookup value", - "lookup source", - "lookup target", - "value if no match", - "match method", - ], - doc: - "Allows for finding things in a table or tabular data, by looking for matches in one range, and " + - "grabbing the corresponding output value from another range.\n" + - "if `lookup target` is not specified or is nullish, it is assumed to be the same as the `lookup source`\n." + - "if nothing matches, either the value `value if no match` (if not `undefined`) is returned, or " + - "an error is thrown.\nBy setting the `match method`, the function can be altered to return " + - "the closest ordered value (above or below) instead of an exact match. The valid choices for `match method` are:\n" + - "- `'exact'`: The default method. Uses strict equality to match values.\n" + - "- `'nextlargest'`: Uses the greater-or-equal operator to match values.\n" + - "- `'nextsmallest'`: Uses the less-than-or-equal operator to match values.\n", - examples: { - "lookup(F3, R`B2:B11`, R`D2:D11`)": - "Look for the value of F3 in the range B2:B11, and return the corresponding value from the D column", - "lookup(E2, R`C2:C5`, R`B2:B5`, 0, 'nextlargest')": - "Find the closest (larger) value to E2 in range C2:C5, and evaluate to 0 if no value in that range is larger.", - }, -}); - -reflookup.__documentation = JSON.stringify({ - name: "reflookup", - argc: 2, - argnames: [ - "lookup value", - "lookup source", - "lookup target", - "value if no match", - "match method", - ], - doc: - "Allows for finding references to things in a table or tabular data, by looking for matches in one range, and " + - "grabbing the corresponding output value from another range.\n" + - "if `lookup target` is not specified or is nullish, it is assumed to be the same as the `lookup source`\n." + - "if nothing matches, either the value `value if no match` (if not `undefined`) is returned, or " + - "an error is thrown.\nBy setting the `match method`, the function can be altered to return " + - "the closest ordered value (above or below) instead of an exact match. The valid choices for `match method` are:\n" + - "- `'exact'`: The default method. Uses strict equality to match values.\n" + - "- `'nextlargest'`: Uses the greater-or-equal operator to match values.\n" + - "- `'nextsmallest'`: Uses the less-than-or-equal operator to match values.\n" + - "\nThis function return a `Position` (see [`here()`](spreadsheet://doc/here))", - examples: { - "reflookup(A0, R`B1:B5`, R`C1:C5`)": - "Look for the value of A0 in the range B1:B5, and return the corresponding cell name from the C column," + - "[Click to view](spreadsheet://example/reflookup#simple)", - "reflookup(A0, R`C2:C5`, R`B2:B5`, here(), 'nextlargest')": - "Find the cell with the closest (larger) value to A0 in range C2:C5, and give the corresponding cell in range C1:C5, " + - "evaluating to the current cell if no value in that range is larger, [Click to view](spreadsheet://example/reflookup#nextlargest)", - }, - example_data: { - simple: { - name: "Simple", - columns: ["A", "B", "C"], - rows: 6, - cells: { - B1: { - kind: "LiteralString", - value: "1", - }, - B0: { - kind: "Formula", - source: "reflookup(A0, R`B1:B5`, R`C1:C5`).value()", - value: '"C"', - type: "Identity", - }, - C3: { - kind: "LiteralString", - value: "C", - type: "Identity", - }, - C2: { - kind: "LiteralString", - value: "B", - type: "Identity", - }, - B2: { - kind: "LiteralString", - value: "2", - }, - C4: { - kind: "LiteralString", - value: "D", - type: "Identity", - }, - A0: { - kind: "LiteralString", - value: "3", - }, - C1: { - kind: "LiteralString", - value: "A", - type: "Identity", - }, - C5: { - kind: "LiteralString", - value: "E", - type: "Identity", - }, - B3: { - kind: "LiteralString", - value: "3", - }, - B5: { - kind: "LiteralString", - value: "5", - }, - B4: { - kind: "LiteralString", - value: "4", - }, - }, - }, - nextlargest: { - name: "Next Largest", - columns: ["A", "B", "C"], - rows: 6, - cells: { - B0: { - kind: "Formula", - source: "reflookup(A0, R`C2:C5`, R`B2:B5`, here(), 'nextlargest').name", - value: '"B2"', - type: "Identity", - }, - C3: { - kind: "LiteralString", - value: "3", - }, - C2: { - kind: "LiteralString", - value: "2", - }, - B2: { - kind: "LiteralString", - value: "B", - type: "Identity", - }, - C4: { - kind: "LiteralString", - value: "4", - }, - A0: { - kind: "LiteralString", - value: "1", - }, - C5: { - kind: "LiteralString", - value: "5", - }, - B3: { - kind: "LiteralString", - value: "C", - type: "Identity", - }, - B5: { - kind: "LiteralString", - value: "E", - type: "Identity", - }, - B4: { - kind: "LiteralString", - value: "D", - type: "Identity", - }, - }, - }, - }, -}); diff --git a/Base/res/keymaps/ar.json b/Base/res/keymaps/ar.json deleted file mode 100644 index 60263cb8576..00000000000 --- a/Base/res/keymaps/ar.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "map": [ "", "\u001B", "1", "2", "3", "4", "5", "6", "7", "8", "9", "0", "-", "=", "\b", "\t", "ض", "ص", "ث", "ق", "ف", "غ", "ع", "ه", "خ", "ح", "ج", "د", "\n", "", "ش", "س", "ي", "ب", "ل", "ا", "ت", "ن", "م", "ك", "ط", "ذ", "", "\\", "ئ", "ء", "ؤ", "ر", "\uFEFB", "ى", "ة", "و", "ز", "ظ", "", "*", "", " ", "", "", "", "", "", "", "", "", "", "", "", "", "", "7", "8", "9", "-", "4", "5", "6", "+", "1", "2", "3", "0", ".", "", "", "\\", "", "", ""], - "shift_map": [ "", "\u001B", "!", "@", "#", "$", "%", "^", "&", "*", ")", "(", "_", "+", "\b", "\t", "َ", "ً", "ُ", "ٌ", "\uFEF9", "إ", "‘", "÷", "×", "؛", "<", ">", "\n", "", "ِ", "ٍ", "]", "[", "\uFEF7", "أ", "ـ", "،", "/", ":", "\"", "ّ", "", "|", "~", "ْ", "}", "{", "\uFEF5", "آ", "’", ",", ".", "؟", "", "*", "", " ", "", "", "", "", "", "", "", "", "", "", "", "", "", "7", "8", "9", "-", "4", "5", "6", "+", "1", "2", "3", "0", ".", "", "", "|", "", "", ""], - "alt_map": [ "", "\u001B", "1", "2", "3", "4", "5", "6", "7", "8", "9", "0", "-", "=", "\b", "\t", "ض", "ص", "ث", "ق", "ف", "غ", "ع", "ه", "خ", "ح", "ج", "د", "\n", "", "ش", "س", "ي", "ب", "ل", "ا", "ت", "ن", "م", "ك", "ط", "ذ", "", "\\", "ئ", "ء", "ؤ", "ر", "\uFEFB", "ى", "ة", "و", "ز", "ظ", "", "*", "", " ", "", "", "", "", "", "", "", "", "", "", "", "", "", "7", "8", "9", "-", "4", "5", "6", "+", "1", "2", "3", "0", ".", "", "", "\\", "", "", ""] -} diff --git a/Base/res/keymaps/be.json b/Base/res/keymaps/be.json deleted file mode 100644 index 5f4e14ca7d9..00000000000 --- a/Base/res/keymaps/be.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "map": [ "", "\u001B", "&", "é", "\"", "'", "(", "§", "è", "!", "ç", "à", ")", "-", "\b", "\t", "a", "z", "e", "r", "t", "y", "u", "i", "o", "p", "^", "$", "\n", "", "q", "s", "d", "f", "g", "h", "j", "k", "l", "m", "ù", "²", "", "µ", "w", "x", "c", "v", "b", "n", ",", ";", ":", "=", "", "*", "", " ", "", "", "", "", "", "", "", "", "", "", "", "", "", "7", "8", "9", "-", "4", "5", "6", "+", "1", "2", "3", "0", ".", "", "", "<", "", "", ""], - "shift_map": [ "", "\u001B", "1", "2", "3", "4", "5", "6", "7", "8", "9", "0", "°", "_", "\b", "\t", "A", "Z", "E", "R", "T", "Y", "U", "I", "O", "P", "¨", "*", "\n", "", "Q", "S", "D", "F", "G", "H", "J", "K", "L", "M", "%", "³", "", "£", "W", "X", "C", "V", "B", "N", "?", ".", "/", "+", "", "*", "", " ", "", "", "", "", "", "", "", "", "", "", "", "", "", "7", "8", "9", "-", "4", "5", "6", "+", "1", "2", "3", "0", ".", "", "", ">", "", "", ""], - "alt_map": [ "", "\u001B", "|", "@", "#", "¼", "½", "^", "{", "[", "{", "}", "\\", "", "\b", "\t", "a", "z", "e", "r", "t", "y", "u", "i", "o", "p", "[", "]", "\n", "", "q", "s", "d", "f", "g", "h", "j", "k", "l", "m", "'", "²", "*", "`", "w", "x", "c", "v", "b", "n", "", "", "", "~", "", "", "", " ", "", "", "", "", "", "", "", "", "", "", "", "", "", "7", "8", "9", "-", "4", "5", "6", "+", "1", "2", "3", "0", ".", "", "", "\\", "", "", ""] -} diff --git a/Base/res/keymaps/bepo-dg.json b/Base/res/keymaps/bepo-dg.json deleted file mode 100644 index d6fbdef90ed..00000000000 --- a/Base/res/keymaps/bepo-dg.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "map": [ "", "$", "\"", "<", ">", "(", ")", "@", "+", "-", "/", "*", "=", "%", "\b", "\t", "b", "é", "p", "o", "w", "\u0300", "v", "d", "l", "j", "=", "", "\n", "", "a", "u", "i", "e", ",", "c", "t", "s", "r", "n", "m", "ç", "", "ê", "z", "y", "x", ".", "k", "'", "q", "g", "h", "f", "", "*", "", " ", "", "", "", "", "", "", "", "", "", "", "", "", "", "7", "8", "9", "-", "4", "5", "6", "+", "1", "2", "3", "0", ".", "", "", "<", "", "", ""], - "shift_map": [ "", "#", "1", "2", "3", "4", "5", "6", "7", "8", "9", "0", "°", "\u0300", "\b", "\t", "B", "É", "P", "O", "W", "!", "V", "D", "L", "J", "\u0302", "", "\n", "", "A", "U", "I", "E", ";", "C", "T", "S", "R", "N", "M", "Ç", "", "Ê", "Z", "Y", "X", ":", "K", "?", "Q", "G", "H", "F", "", "*", "", " ", "", "", "", "", "", "", "", "", "", "", "", "", "", "7", "8", "9", "-", "4", "5", "6", "+", "1", "2", "3", "0", ".", "", "", ">", "", "", ""], - "alt_map": [ "", "–", "—", "≤", "≥", "[", "]", "^", "±", "−", "÷", "×", "≠", "", "\b", "\t", "|", "(", ")", "&", "\u0300", "¡", "", "ð", "/", "ij", "≠", "", "\n", "", "", "[", "]", "€", "", "©", "", "ß", "®", "", "", "", "*", "/", "\\", "{", "}", "_", "⌨", "¿", "µ", "", "", "", "", "", "", " ", "", "", "", "", "", "", "", "", "", "", "", "", "", "7", "8", "9", "-", "4", "5", "6", "+", "1", "2", "3", "0", ".", "", "", "\\", "", "", ""] -} diff --git a/Base/res/keymaps/bepo.json b/Base/res/keymaps/bepo.json deleted file mode 100644 index 995e0aadd9b..00000000000 --- a/Base/res/keymaps/bepo.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "map": [ "", "$", "\"", "«", "»", "(", ")", "@", "+", "-", "/", "*", "=", "%", "\b", "\t", "b", "é", "p", "o", "è", "^", "v", "d", "l", "j", "z", "w", "\n", "", "a", "u", "i", "e", ",", "c", "t", "s", "r", "n", "m", "ç", "", "ê", "à", "y", "x", ".", "k", "'", "q", "g", "h", "f", "", "*", "", " ", "", "", "", "", "", "", "", "", "", "", "", "", "", "7", "8", "9", "-", "4", "5", "6", "+", "1", "2", "3", "0", ".", "", "", "<", "", "", ""], - "shift_map": [ "", "#", "1", "2", "3", "4", "5", "6", "7", "8", "9", "0", "°", "`", "\b", "\t", "B", "É", "P", "O", "È", "!", "V", "D", "L", "J", "Z", "W", "\n", "", "A", "U", "I", "E", ";", "C", "T", "S", "R", "N", "M", "Ç", "", "Ê", "À", "Y", "X", ":", "K", "?", "Q", "G", "H", "F", "", "*", "", " ", "", "", "", "", "", "", "", "", "", "", "", "", "", "7", "8", "9", "-", "4", "5", "6", "+", "1", "2", "3", "0", ".", "", "", ">", "", "", ""], - "alt_map": [ "", "–", "—", "<", ">", "[", "]", "^", "±", "−", "÷", "×", "≠", "‰", "\b", "\t", "|", "´", "&", "œ", "`", "¡", "", "ð", "/", "ij", "-", "", "\n", "", "æ", "ù", "̤", "€", "", "", "", "", "", "", "~", "", "", "/", "\\", "{", "}", "…", "~", "¿", "", "", "", "", "", "", "", " ", "", "", "", "", "", "", "", "", "", "", "", "", "", "7", "8", "9", "-", "4", "5", "6", "+", "1", "2", "3", "0", ".", "", "", "\\", "", "", ""] -} diff --git a/Base/res/keymaps/colemak-caps-backspace.json b/Base/res/keymaps/colemak-caps-backspace.json deleted file mode 100644 index 9895c06e18c..00000000000 --- a/Base/res/keymaps/colemak-caps-backspace.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "map": [ "", "\u001B", "1", "2", "3", "4", "5", "6", "7", "8", "9", "0", "-", "=", "\b", "\t", "q", "w", "f", "p", "g", "j", "l", "u", "y", ";", "[", "]", "\n", "", "a", "r", "s", "t", "d", "h", "n", "e", "i", "o", "'", "`", "", "\\", "z", "x", "c", "v", "b", "k", "m", ",", ".", "/", "", "*", "", " ", "\b", "", "", "", "", "", "", "", "", "", "", "", "", "7", "8", "9", "-", "4", "5", "6", "+", "1", "2", "3", "0", ".", "", "", "\\", "", "", ""], - "shift_map": [ "", "\u001B", "!", "@", "#", "$", "%", "^", "&", "*", "(", ")", "_", "+", "\b", "\t", "Q", "W", "F", "P", "G", "J", "L", "U", "Y", ":", "{", "}", "\n", "", "A", "R", "S", "T", "D", "H", "N", "E", "I", "O", "\"", "~", "", "|", "Z", "X", "C", "V", "B", "K", "M", "<", ">", "?", "", "*", "", " ", "\b", "", "", "", "", "", "", "", "", "", "", "", "", "7", "8", "9", "-", "4", "5", "6", "+", "1", "2", "3", "0", ".", "", "", "|", "", "", ""], - "alt_map": [ "", "\u001B", "1", "2", "3", "4", "5", "6", "7", "8", "9", "0", "-", "=", "\b", "\t", "q", "w", "f", "p", "g", "j", "l", "u", "y", ";", "[", "]", "\n", "", "a", "r", "s", "t", "d", "h", "n", "e", "i", "o", "'", "`", "", "\\", "z", "x", "c", "v", "b", "k", "m", ",", ".", "/", "", "*", "", " ", "\b", "", "", "", "", "", "", "", "", "", "", "", "", "7", "8", "9", "-", "4", "5", "6", "+", "1", "2", "3", "0", ".", "", "", "\\", "", "", ""], - "altgr_map": [ "", "\u001B", "¡", "º", "ª", "¢", "€", "ħ", "ð", "þ", "‘", "’", "–", "×", "\b", "\t", "ä", "å", "ã", "ø", "g", "đ", "ł", "ú", "ü", "ö", "«", "»", "\n", "", "á", "r", "ß", "t", "d", "h", "ñ", "é", "í", "ó", "õ", "`", "", "\\", "æ", "x", "ç", "œ", "b", "k", "m", ",", ".", "¿", "", "*", "", " ", "\b", "", "", "", "", "", "", "", "", "", "", "", "", "7", "8", "9", "-", "4", "5", "6", "+", "1", "2", "3", "0", ".", "", "", "\\", "", "", ""], - "shift_altgr_map": [ "", "\u001B", "¹", "²", "³", "£", "¥", "Ħ", "Ð", "Þ", "“", "”", "—", "÷", "\b", "\t", "Ä", "Å", "Ã", "Ø", "G", "Đ", "Ł", "Ú", "Ü", "Ö", "‹", "›", "\n", "", "Á", "R", "ẞ", "S", "T", "D", "Ñ", "É", "Í", "Ó", "Õ", "~", "", "|", "Æ", "X", "Ç", "Œ", "B", "K", "M", "<", ">", "?", "", "*", "", "\u00A0", "\b", "", "", "", "", "", "", "", "", "", "", "", "", "7", "8", "9", "-", "4", "5", "6", "+", "1", "2", "3", "0", ".", "", "", "|", "", "", ""] -} diff --git a/Base/res/keymaps/colemak.json b/Base/res/keymaps/colemak.json deleted file mode 100644 index 17bc67efe3d..00000000000 --- a/Base/res/keymaps/colemak.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "map": [ "", "\u001B", "1", "2", "3", "4", "5", "6", "7", "8", "9", "0", "-", "=", "\b", "\t", "q", "w", "f", "p", "g", "j", "l", "u", "y", ";", "[", "]", "\n", "", "a", "r", "s", "t", "d", "h", "n", "e", "i", "o", "'", "`", "", "\\", "z", "x", "c", "v", "b", "k", "m", ",", ".", "/", "", "*", "", " ", "", "", "", "", "", "", "", "", "", "", "", "", "", "7", "8", "9", "-", "4", "5", "6", "+", "1", "2", "3", "0", ".", "", "", "\\", "", "", ""], - "shift_map": [ "", "\u001B", "!", "@", "#", "$", "%", "^", "&", "*", "(", ")", "_", "+", "\b", "\t", "Q", "W", "F", "P", "G", "J", "L", "U", "Y", ":", "{", "}", "\n", "", "A", "R", "S", "T", "D", "H", "N", "E", "I", "O", "\"", "~", "", "|", "Z", "X", "C", "V", "B", "K", "M", "<", ">", "?", "", "*", "", " ", "", "", "", "", "", "", "", "", "", "", "", "", "", "7", "8", "9", "-", "4", "5", "6", "+", "1", "2", "3", "0", ".", "", "", "|", "", "", ""], - "alt_map": [ "", "\u001B", "1", "2", "3", "4", "5", "6", "7", "8", "9", "0", "-", "=", "\b", "\t", "q", "w", "f", "p", "g", "j", "l", "u", "y", ";", "[", "]", "\n", "", "a", "r", "s", "t", "d", "h", "n", "e", "i", "o", "'", "`", "", "\\", "z", "x", "c", "v", "b", "k", "m", ",", ".", "/", "", "*", "", " ", "", "", "", "", "", "", "", "", "", "", "", "", "", "7", "8", "9", "-", "4", "5", "6", "+", "1", "2", "3", "0", ".", "", "", "\\", "", "", ""], - "altgr_map": [ "", "\u001B", "¡", "º", "ª", "¢", "€", "ħ", "ð", "þ", "‘", "’", "–", "×", "\b", "\t", "ä", "å", "ã", "ø", "g", "đ", "ł", "ú", "ü", "ö", "«", "»", "\n", "", "á", "r", "ß", "t", "d", "h", "ñ", "é", "í", "ó", "õ", "`", "", "\\", "æ", "x", "ç", "œ", "b", "k", "m", ",", ".", "¿", "", "*", "", " ", "", "", "", "", "", "", "", "", "", "", "", "", "", "7", "8", "9", "-", "4", "5", "6", "+", "1", "2", "3", "0", ".", "", "", "\\", "", "", ""], - "shift_altgr_map": [ "", "\u001B", "¹", "²", "³", "£", "¥", "Ħ", "Ð", "Þ", "“", "”", "—", "÷", "\b", "\t", "Ä", "Å", "Ã", "Ø", "G", "Đ", "Ł", "Ú", "Ü", "Ö", "‹", "›", "\n", "", "Á", "R", "ẞ", "S", "T", "D", "Ñ", "É", "Í", "Ó", "Õ", "~", "", "|", "Æ", "X", "Ç", "Œ", "B", "K", "M", "<", ">", "?", "", "*", "", "\u00A0", ":", "", "", "", "", "", "", "", "", "", "", "", "", "7", "8", "9", "-", "4", "5", "6", "+", "1", "2", "3", "0", ".", "", "", "|", "", "", ""] -} diff --git a/Base/res/keymaps/cs-cz-programmers.json b/Base/res/keymaps/cs-cz-programmers.json deleted file mode 100644 index 79783175934..00000000000 --- a/Base/res/keymaps/cs-cz-programmers.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "map": [ "", "\u001B", "1", "2", "3", "4", "5", "6", "7", "8", "9", "0", "-", "=", "\b", "\t", "q", "w", "e", "r", "t", "y", "u", "i", "o", "p", "[", "]", "\n", "", "a", "s", "d", "f", "g", "h", "j", "k", "l", ";", "'", "`", "", "\\", "z", "x", "c", "v", "b", "n", "m", ",", ".", "/", "", "*", "", " ", "", "", "", "", "", "", "", "", "", "", "", "", "", "7", "8", "9", "-", "4", "5", "6", "+", "1", "2", "3", "0", ".", "", "", "\\", "", "", "" ], - "shift_map": [ "", "\u001B", "!", "@", "#", "$", "%", "^", "&", "*", "(", ")", "_", "+", "\b", "\t", "Q", "W", "E", "R", "T", "Y", "U", "I", "O", "P", "{", "}", "\n", "", "A", "S", "D", "F", "G", "H", "J", "K", "L", ":", "\"", "~", "", "|", "Z", "X", "C", "V", "B", "N", "M", "<", ">", "?", "", "*", "", " ", "", "", "", "", "", "", "", "", "", "", "", "", "", "7", "8", "9", "-", "4", "5", "6", "+", "1", "2", "3", "0", ".", "", "", "|", "", "", "" ], - "alt_map": [ "", "\u001B", "1", "2", "3", "4", "5", "6", "7", "8", "9", "0", "-", "=", "\b", "\t", "q", "w", "e", "r", "t", "y", "u", "i", "o", "p", "[", "]", "\n", "", "a", "s", "d", "f", "g", "h", "j", "k", "l", ";", "'", "`", "", "\\", "z", "x", "c", "v", "b", "n", "m", ",", ".", "/", "", "*", "", " ", "", "", "", "", "", "", "", "", "", "", "", "", "", "7", "8", "9", "-", "4", "5", "6", "+", "1", "2", "3", "0", ".", "", "", "\\", "", "", "" ], - "altgr_map": [ "", "\u001B", "+", "ě", "š", "č", "ř", "ž", "ý", "á", "í", "é", "=", "´", "\b", "\t", "q", "w", "e", "r", "t", "y", "u", "i", "o", "p", "ú", ")", "\n", "", "a", "s", "d", "f", "g", "h", "j", "k", "l", "ů", "§", "¨", "", "\\", "z", "x", "c", "v", "b", "n", "m", ",", ".", "-", "", "*", "", " ", "", "", "", "", "", "", "", "", "", "", "", "", "", "7", "8", "9", "-", "4", "5", "6", "+", "1", "2", "3", "0", ",", "", "", "<", "", "", "" ], - "shift_altgr_map": [ "", "\u001B", "1", "2", "3", "4", "5", "6", "7", "8", "9", "0", "%", "ˇ", "\b", "\t", "Q", "W", "E", "R", "T", "Y", "U", "I", "O", "P", "/", "(", "\n", "", "A", "S", "D", "F", "G", "H", "J", "K", "L", "\"", "!", "'", "", "|", "Z", "X", "C", "V", "B", "N", "M", "?", ":", "_", "", "*", "", " ", "", "", "", "", "", "", "", "", "", "", "", "", "", "7", "8", "9", "-", "4", "5", "6", "+", "1", "2", "3", "0", ",", "", "", ">", "", "", "" ] -} diff --git a/Base/res/keymaps/cs-cz-qwertz.json b/Base/res/keymaps/cs-cz-qwertz.json deleted file mode 100644 index 8eb49821dd4..00000000000 --- a/Base/res/keymaps/cs-cz-qwertz.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "map": [ "", "\u001B", "+", "ě", "š", "č", "ř", "ž", "ý", "á", "í", "é", "=", "´", "\b", "\t", "q", "w", "e", "r", "t", "z", "u", "i", "o", "p", "ú", ")", "\n", "", "a", "s", "d", "f", "g", "h", "j", "k", "l", "ů", "§", ";", "", "\\", "y", "x", "c", "v", "b", "n", "m", ",", ".", "-", "", "*", "", " ", "", "", "", "", "", "", "", "", "", "", "", "", "", "7", "8", "9", "-", "4", "5", "6", "+", "1", "2", "3", "0", ",", "", "", "<", "", "", "" ], - "shift_map": [ "", "\u001B", "1", "2", "3", "4", "5", "6", "7", "8", "9", "0", "%", "ˇ", "\b", "\t", "Q", "W", "E", "R", "T", "Z", "U", "I", "O", "P", "/", "(", "\n", "", "A", "S", "D", "F", "G", "H", "J", "K", "L", "\"", "!", "°", "", "|", "Y", "X", "C", "V", "B", "N", "M", "?", ":", "_", "", "*", "", " ", "", "", "", "", "", "", "", "", "", "", "", "", "", "7", "8", "9", "-", "4", "5", "6", "+", "1", "2", "3", "0", ",", "", "", ">", "", "", "" ], - "alt_map": [ "", "\u001B", "+", "ě", "š", "č", "ř", "ž", "ý", "á", "í", "é", "=", "´", "\b", "\t", "q", "w", "e", "r", "t", "z", "u", "i", "o", "p", "ú", ")", "\n", "", "a", "s", "d", "f", "g", "h", "j", "k", "l", "ů", "§", "", "", "\\", "y", "x", "c", "v", "b", "n", "m", ",", ".", "-", "", "*", "", " ", "", "", "", "", "", "", "", "", "", "", "", "", "", "7", "8", "9", "-", "4", "5", "6", "+", "1", "2", "3", "0", ",", "", "", "<", "", "", "" ], - "altgr_map": [ "", "\u001B", "!", "@", "#", "$", "%", "^", "&", "*", "(", ")", "\\", "¯", "\b", "\t", "\\", "|", "€", "¶", "ŧ", "←", "↓", "→", "ø", "þ", "[", "]", "\n", "", "~", "đ", "Đ", "[", "]", "`", "'", "ł", "Ł", "$", "'", "`", "", "/", "°", "#", "&", "@", "{", "}", "^", "<", ">", "*", "", "*", "", " ", "", "", "", "", "", "", "", "", "", "", "", "", "", "7", "8", "9", "-", "4", "5", "6", "+", "1", "2", "3", "0", ",", "", "", "<", "", "", "" ], - "shift_altgr_map": [ "", "\u001B", "~", "ˇ", "^", "˘", "°", "˛", "`", "˙", "´", "˝", "¨", "¸", "\b", "\t", "Ω", "Ł", "E", "®", "Ŧ", "¥", "↑", "ı", "Ø", "Þ", "÷", "×", "\n", "", "Æ", "§", "Ð", "ª", "Ŋ", "Ħ", "̛", "&", "Ł", "˝", "ß", "~", "", "", "<", ">", "©", "‘", "’", "N", "º", "×", "÷", "˙", "", "*", "", " ", "", "", "", "", "", "", "", "", "", "", "", "", "", "7", "8", "9", "-", "4", "5", "6", "+", "1", "2", "3", "0", ",", "", "", ">", "", "", "" ] -} diff --git a/Base/res/keymaps/cs-cz.json b/Base/res/keymaps/cs-cz.json deleted file mode 100644 index 59633b4079d..00000000000 --- a/Base/res/keymaps/cs-cz.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "map": [ "", "\u001B", "+", "ě", "š", "č", "ř", "ž", "ý", "á", "í", "é", "=", "´", "\b", "\t", "q", "w", "e", "r", "t", "y", "u", "i", "o", "p", "ú", ")", "\n", "", "a", "s", "d", "f", "g", "h", "j", "k", "l", "ů", "§", ";", "", "\\", "z", "x", "c", "v", "b", "n", "m", ",", ".", "-", "", "*", "", " ", "", "", "", "", "", "", "", "", "", "", "", "", "", "7", "8", "9", "-", "4", "5", "6", "+", "1", "2", "3", "0", ",", "", "", "<", "", "", "" ], - "shift_map": [ "", "\u001B", "1", "2", "3", "4", "5", "6", "7", "8", "9", "0", "%", "ˇ", "\b", "\t", "Q", "W", "E", "R", "T", "Y", "U", "I", "O", "P", "/", "(", "\n", "", "A", "S", "D", "F", "G", "H", "J", "K", "L", "\"", "!", "°", "", "|", "Z", "X", "C", "V", "B", "N", "M", "?", ":", "_", "", "*", "", " ", "", "", "", "", "", "", "", "", "", "", "", "", "", "7", "8", "9", "-", "4", "5", "6", "+", "1", "2", "3", "0", ",", "", "", ">", "", "", "" ], - "alt_map": [ "", "\u001B", "+", "ě", "š", "č", "ř", "ž", "ý", "á", "í", "é", "=", "´", "\b", "\t", "q", "w", "e", "r", "t", "y", "u", "i", "o", "p", "ú", ")", "\n", "", "a", "s", "d", "f", "g", "h", "j", "k", "l", "ů", "§", "", "", "\\", "z", "x", "c", "v", "b", "n", "m", ",", ".", "-", "", "*", "", " ", "", "", "", "", "", "", "", "", "", "", "", "", "", "7", "8", "9", "-", "4", "5", "6", "+", "1", "2", "3", "0", ",", "", "", "<", "", "", "" ], - "altgr_map": [ "", "\u001B", "!", "@", "#", "$", "%", "^", "&", "*", "(", ")", "\\", "¯", "\b", "\t", "\\", "|", "€", "¶", "ŧ", "←", "↓", "→", "ø", "þ", "[", "]", "\n", "", "~", "đ", "Đ", "[", "]", "`", "'", "ł", "Ł", "$", "'", "`", "", "/", "°", "#", "&", "@", "{", "}", "^", "<", ">", "*", "", "*", "", " ", "", "", "", "", "", "", "", "", "", "", "", "", "", "7", "8", "9", "-", "4", "5", "6", "+", "1", "2", "3", "0", ",", "", "", "<", "", "", "" ], - "shift_altgr_map": [ "", "\u001B", "~", "ˇ", "^", "˘", "°", "˛", "`", "˙", "´", "˝", "¨", "¸", "\b", "\t", "Ω", "Ł", "E", "®", "Ŧ", "¥", "↑", "ı", "Ø", "Þ", "÷", "×", "\n", "", "Æ", "§", "Ð", "ª", "Ŋ", "Ħ", "̛", "&", "Ł", "˝", "ß", "~", "", "", "<", ">", "©", "‘", "’", "N", "º", "×", "÷", "˙", "", "*", "", " ", "", "", "", "", "", "", "", "", "", "", "", "", "", "7", "8", "9", "-", "4", "5", "6", "+", "1", "2", "3", "0", ",", "", "", ">", "", "", "" ] -} diff --git a/Base/res/keymaps/da.json b/Base/res/keymaps/da.json deleted file mode 100644 index e26522be86e..00000000000 --- a/Base/res/keymaps/da.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "map": [ "", "\u001B", "1", "2", "3", "4", "5", "6", "7", "8", "9", "0", "+", "´", "\b", "\t", "q", "w", "e", "r", "t", "y", "u", "i", "o", "p", "å", "¨", "\n", "", "a", "s", "d", "f", "g", "h", "j", "k", "l", "æ", "ø", "½", "", "'", "z", "x", "c", "v", "b", "n", "m", ",", ".", "-", "", "*", "", " ", "", "", "", "", "", "", "", "", "", "", "", "", "", "7", "8", "9", "-", "4", "5", "6", "+", "1", "2", "3", "0", ",", "", "", "<", "", "", ""], - "shift_map": [ "", "\u001B", "!", "\"", "#", "¤", "%", "&", "/", "(", ")", "=", "?", "`", "\b", "", "Q", "W", "E", "R", "T", "Y", "U", "I", "O", "P", "Å", "^", "\n", "", "A", "S", "D", "F", "G", "H", "J", "K", "L", "Æ", "Ø", "§", "", "*", "Z", "X", "C", "V", "B", "N", "M", ";", ":", "_", "", "*", "", " ", "", "", "", "", "", "", "", "", "", "", "", "", "", "7", "8", "9", "-", "4", "5", "6", "+", "1", "2", "3", "0", ",", "", "", ">", "", "", ""], - "alt_map": [ "", "\u001B", "1", "2", "3", "4", "5", "6", "7", "8", "9", "0", "+", "´", "\b", "\t", "q", "w", "e", "r", "t", "y", "u", "i", "o", "p", "å", "¨", "\n", "", "a", "s", "d", "f", "g", "h", "j", "k", "l", "æ", "ø", "½", "", "'", "z", "x", "c", "v", "b", "n", "m", ",", ".", "-", "", "*", "", " ", "", "", "", "", "", "", "", "", "", "", "", "", "", "7", "8", "9", "-", "4", "5", "6", "+", "1", "2", "3", "0", ",", "", "", "<", "", "", ""], - "altgr_map": [ "", "\u001B", "", "@", "£", "$", "€", "", "{", "[", "]", "}", "", "|", "", "", "", "", "€", "", "", "", "", "", "", "", "", "~", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "µ", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "−", "", "", "", "", "", "", "", "", "", "", "", "\\", "", "", ""] -} diff --git a/Base/res/keymaps/de-ch.json b/Base/res/keymaps/de-ch.json deleted file mode 100644 index dc1fb0fb4bf..00000000000 --- a/Base/res/keymaps/de-ch.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "map": [ "", "\u001B", "1", "2", "3", "4", "5", "6", "7", "8", "9", "0", "'", "^", "\b", "\t", "q", "w", "e", "r", "t", "z", "u", "i", "o", "p", "ü", "¨", "\n", "", "a", "s", "d", "f", "g", "h", "j", "k", "l", "ö", "ä", "§", "", "$", "y", "x", "c", "v", "b", "n", "m", ",", ".", "-", "", "*", "", " ", "", "", "", "", "", "", "", "", "", "", "", "", "", "7", "8", "9", "-", "4", "5", "6", "+", "1", "2", "3", "0", ".", "", "", "<", "", "", ""], - "shift_map": [ "", "\u001B", "+", "\"", "*", "ç", "%", "&", "/", "(", ")", "=", "?", "`", "\b", "\t", "Q", "W", "E", "R", "T", "Z", "U", "I", "O", "P", "è", "!", "\n", "", "A", "S", "D", "F", "G", "H", "J", "K", "L", "é", "à", "°", "", "£", "Y", "X", "C", "V", "B", "N", "M", ";", ":", "_", "", "*", "", " ", "", "", "", "", "", "", "", "", "", "", "", "", "", "7", "8", "9", "-", "4", "5", "6", "+", "1", "2", "3", "0", ".", "", "", ">", "", "", ""], - "alt_map": [ "", "\u001B", "|", "@", "#", "", "", "¬", "|", "¢", "", "", "´", "~", "\b", "\t", "", "", "€", "", "", "", "", "", "", "", "[", "]", "\n", "", "", "", "", "", "", "", "", "", "", "", "{", "", "", "}", "«", "»", "", "", "", "", "", "", "", "", "", "*", "", " ", "", "", "", "", "", "", "", "", "", "", "", "", "", "7", "8", "9", "-", "4", "5", "6", "+", "1", "2", "3", "0", ".", "", "", "\\", "", "", ""], - "shift_altgr_map": [ "", "\u001B", "", "", "", "", "", "", "", "", "", "", "", "", "\b", "\t", "", "", "", "", "", "", "", "", "", "", "", "", "\n", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "*", "", " ", "", "", "", "", "", "", "", "", "", "", "", "", "", "7", "8", "9", "-", "4", "5", "6", "+", "1", "2", "3", "0", ".", "", "", "", "", "", ""] -} diff --git a/Base/res/keymaps/de-macintosh.json b/Base/res/keymaps/de-macintosh.json deleted file mode 100644 index 8341e004e90..00000000000 --- a/Base/res/keymaps/de-macintosh.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "map": [ "", "\u001B", "1", "2", "3", "4", "5", "6", "7", "8", "9", "0", "ß", "´", "\b", "\t", "q", "w", "e", "r", "t", "z", "u", "i", "o", "p", "ü", "+", "\n", "", "a", "s", "d", "f", "g", "h", "j", "k", "l", "ö", "ä", "^", "", "#", "y", "x", "c", "v", "b", "n", "m", ",", ".", "-", "", "*", "", " ", "", "", "", "", "", "", "", "", "", "", "", "", "", "7", "8", "9", "-", "4", "5", "6", "+", "1", "2", "3", "0", ".", "", "", "<", "", "", ""], - "shift_map": [ "", "\u001B", "!", "\"", "§", "$", "%", "&", "/", "(", ")", "=", "?", "`", "\b", "\t", "Q", "W", "E", "R", "T", "Z", "U", "I", "O", "P", "Ü", "*", "\n", "", "A", "S", "D", "F", "G", "H", "J", "K", "L", "Ö", "Ä", "°", "", "'", "Y", "X", "C", "V", "B", "N", "M", ";", ":", "_", "", "*", "", " ", "", "", "", "", "", "", "", "", "", "", "", "", "", "7", "8", "9", "-", "4", "5", "6", "+", "1", "2", "3", "0", ".", "", "", ">", "", "", ""], - "alt_map": [ "", "\u001B", "¡", "²", "³", "¼", "[", "]", "|", "{", "}", "}", "\\", "¸", "\b", "\t", "«", "ł", "€", "®", "ŧ", "←", "¨", "/", "ø", "þ", "¨", "~", "\n", "", "å", "ſ", "ð", "đ", "©", "ª", "", "ĸ", "@", "´", "^", "′", "", "~", "»", "«", "¢", "„", "“", "~", "µ", "·", "…", "–", "", "*", "", " ", "", "", "", "", "", "", "", "", "", "", "", "", "", "7", "8", "9", "-", "4", "5", "6", "+", "1", "2", "3", "0", ".", "", "", "|", "", "", ""], - "shift_altgr_map": [ "", "\u001B", "@", "⅛", "£", "¤", "⅜", "⅝", "\\", "~", "±", "°", "¿", "˛", "\b", "\t", "»", "Ł", "€", "®", "Ŧ", "¥", "Á", "Û", "Ø", "Þ", "°", "¯", "\n", "", "Å", "ẞ", "Ð", "ª", "Ŋ", "Ħ", "˙", "&", "Ł", "̣", "ˇ", "″", "", "˘", "›", "‹", "©", "‚", "‘", "’", "º", "×", "÷", "—", "", "*", "", " ", "", "", "", "", "", "", "", "", "", "", "", "", "", "7", "8", "9", "-", "4", "5", "6", "+", "1", "2", "3", "0", ".", "", "", "", "", "", ""] -} diff --git a/Base/res/keymaps/de.json b/Base/res/keymaps/de.json deleted file mode 100644 index a20838aa906..00000000000 --- a/Base/res/keymaps/de.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "map": [ "", "\u001B", "1", "2", "3", "4", "5", "6", "7", "8", "9", "0", "ß", "´", "\b", "\t", "q", "w", "e", "r", "t", "z", "u", "i", "o", "p", "ü", "+", "\n", "", "a", "s", "d", "f", "g", "h", "j", "k", "l", "ö", "ä", "^", "", "#", "y", "x", "c", "v", "b", "n", "m", ",", ".", "-", "", "*", "", " ", "", "", "", "", "", "", "", "", "", "", "", "", "", "7", "8", "9", "-", "4", "5", "6", "+", "1", "2", "3", "0", ".", "", "", "<", "", "", ""], - "shift_map": [ "", "\u001B", "!", "\"", "§", "$", "%", "&", "/", "(", ")", "=", "?", "`", "\b", "\t", "Q", "W", "E", "R", "T", "Z", "U", "I", "O", "P", "Ü", "*", "\n", "", "A", "S", "D", "F", "G", "H", "J", "K", "L", "Ö", "Ä", "°", "", "'", "Y", "X", "C", "V", "B", "N", "M", ";", ":", "_", "", "*", "", " ", "", "", "", "", "", "", "", "", "", "", "", "", "", "7", "8", "9", "-", "4", "5", "6", "+", "1", "2", "3", "0", ".", "", "", ">", "", "", ""], - "alt_map": [ "", "\u001B", "¹", "²", "³", "¼", "½", "¬", "{", "[", "]", "}", "\\", "¸", "\b", "\t", "@", "ł", "€", "¶", "ŧ", "←", "↓", "→", "ø", "þ", "¨", "~", "\n", "", "æ", "ſ", "ð", "đ", "ŋ", "ħ", "˙", "ĸ", "ł", "˝", "^", "′", "", "#", "»", "«", "¢", "„", "“", "”", "µ", "·", "…", "–", "", "*", "", " ", "", "", "", "", "", "", "", "", "", "", "", "", "", "7", "8", "9", "-", "4", "5", "6", "+", "1", "2", "3", "0", ".", "", "", "|", "", "", ""], - "shift_altgr_map": [ "", "\u001B", "¡", "⅛", "£", "¤", "⅜", "⅝", "⅞", "™", "±", "°", "¿", "˛", "\b", "\t", "Ω", "Ł", "€", "®", "Ŧ", "¥", "↑", "ı", "Ø", "Þ", "°", "¯", "\n", "", "Æ", "ẞ", "Ð", "ª", "Ŋ", "Ħ", "˙", "&", "Ł", "", "ˇ", "″", "", "#", "›", "‹", "©", "‚", "‘", "’", "º", "×", "÷", "—", "", "*", "", " ", "", "", "", "", "", "", "", "", "", "", "", "", "", "7", "8", "9", "-", "4", "5", "6", "+", "1", "2", "3", "0", ".", "", "", "¦", "", "", ""] -} diff --git a/Base/res/keymaps/dvorak-programmer.json b/Base/res/keymaps/dvorak-programmer.json deleted file mode 100644 index 30aa80f399d..00000000000 --- a/Base/res/keymaps/dvorak-programmer.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "map": [ "", "\u001B", "&", "[", "{", "}", "(", "=", "*", ")", "+", "]", "!", "#", "\b", "\t", ";", ",", ".", "p", "y", "f", "g", "c", "r", "l", "/", "@", "\n", "", "a", "o", "e", "u", "i", "d", "h", "t", "n", "s", "-", "$", "", "\\", "'", "q", "j", "k", "x", "b", "m", "w", "v", "z", "", "*", "", " ", "", "", "", "", "", "", "", "", "", "", "", "", "", "7", "8", "9", "-", "4", "5", "6", "+", "1", "2", "3", "0", ".", "", "", "\\", "", "", ""], - "shift_map": [ "", "\u001B", "%", "7", "5", "3", "1", "9", "0", "2", "4", "6", "8", "`", "\b", "\t",":", "<", ">", "P", "Y", "F", "G", "C", "R", "L", "?", "^", "\n", "", "A", "O", "E", "U", "I", "D", "H", "T", "N", "S", "_", "~", "", "|", "\"", "Q", "J", "K", "X", "B", "M", "W", "V", "Z", "", "*", "", " ", "", "", "", "", "", "", "", "", "", "", "", "", "", "7", "8", "9", "-", "4", "5", "6", "+", "1", "2", "3", "0", ".", "", "", "|", "", "", ""], - "alt_map": [ "", "\u001B", "&", "[", "{", "}", "(", "=", "*", ")", "+", "]", "!", "#", "\b", "\t", ";", ",", ".", "p", "y", "f", "g", "c", "r", "l", "/", "@", "\n", "", "a", "o", "e", "u", "i", "d", "h", "t", "n", "s", "-", "$", "", "\\", "'", "q", "j", "k", "x", "b", "m", "w", "v", "z", "", "*", "", " ", "", "", "", "", "", "", "", "", "", "", "", "", "", "7", "8", "9", "-", "4", "5", "6", "+", "1", "2", "3", "0", ".", "", "", "\\", "", "", ""] -} diff --git a/Base/res/keymaps/dvorak.json b/Base/res/keymaps/dvorak.json deleted file mode 100644 index 91fba7b7f62..00000000000 --- a/Base/res/keymaps/dvorak.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "map": [ "", "\u001B", "1", "2", "3", "4", "5", "6", "7", "8", "9", "0", "[", "]", "\b", "\t", "'", ",", ".", "p", "y", "f", "g", "c", "r", "l", "/", "=", "\n", "", "a", "o", "e", "u", "i", "d", "h", "t", "n", "s", "-", "`", "", "\\", ";", "q", "j", "k", "x", "b", "m", "w", "v", "z", "", "*", "", " ", "", "", "", "", "", "", "", "", "", "", "", "", "", "7", "8", "9", "-", "4", "5", "6", "+", "1", "2", "3", "0", ".", "", "", "\\", "", "", ""], - "shift_map": [ "", "\u001B", "!", "@", "#", "$", "%", "^", "&", "*", "(", ")", "{", "}", "\b", "\t","\"", "<", ">", "P", "Y", "F", "G", "C", "R", "L", "?", "+", "\n", "", "A", "O", "E", "U", "I", "D", "H", "T", "N", "S", "_", "~", "", "|", ":", "Q", "J", "K", "X", "B", "M", "W", "V", "Z", "", "*", "", " ", "", "", "", "", "", "", "", "", "", "", "", "", "", "7", "8", "9", "-", "4", "5", "6", "+", "1", "2", "3", "0", ".", "", "", "|", "", "", ""], - "alt_map": [ "", "\u001B", "1", "2", "3", "4", "5", "6", "7", "8", "9", "0", "[", "]", "\b", "\t", "'", ",", ".", "p", "y", "f", "g", "c", "r", "l", "/", "=", "\n", "", "a", "o", "e", "u", "i", "d", "h", "t", "n", "s", "-", "`", "", "\\", ";", "q", "j", "k", "x", "b", "m", "w", "v", "z", "", "*", "", " ", "", "", "", "", "", "", "", "", "", "", "", "", "", "7", "8", "9", "-", "4", "5", "6", "+", "1", "2", "3", "0", ".", "", "", "\\", "", "", ""] -} diff --git a/Base/res/keymaps/el-gr.json b/Base/res/keymaps/el-gr.json deleted file mode 100644 index 93acf6591ae..00000000000 --- a/Base/res/keymaps/el-gr.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "map": [ "", "\u001B", "1", "2", "3", "4", "5", "6", "7", "8", "9", "0", "-", "=", "\b", "\t", ";", "ς", "ε", "ρ", "τ", "υ", "θ", "ι", "ο", "π", "[", "]", "\n", "", "α", "σ", "δ", "φ", "γ", "η", "ξ", "κ", "λ", "´", "'", "`", "", "\\", "ζ", "χ", "ψ", "ω", "β", "ν", "μ", ",", ".", "/", "", "*", "", " ", "", "", "", "", "", "", "", "", "", "", "", "", "", "7", "8", "9", "-", "4", "5", "6", "+", "1", "2", "3", "0", ".", "", "", "\\", "", "", ""], - "shift_map": [ "", "\u001B", "!", "@", "#", "$", "%", "^", "&", "*", "(", ")", "_", "+", "\b", "\t", ":", "Σ", "Ε", "Ρ", "Τ", "Υ", "Θ", "Ι", "Ο", "Π", "{", "}", "\n", "", "Α", "Σ", "Δ", "Φ", "Γ", "Η", "Ξ", "Κ", "Λ", "¨", "\"", "~", "", "|", "Ζ", "Χ", "Ψ", "Ω", "Β", "Ν", "Μ", "<", ">", "?", "", "*", "", " ", "", "", "", "", "", "", "", "", "", "", "", "", "", "7", "8", "9", "-", "4", "5", "6", "+", "1", "2", "3", "0", ".", "", "", "|", "", "", ""], - "alt_map": [ "", "\u001B", "1", "2", "3", "4", "5", "6", "7", "8", "9", "0", "-", "=", "\b", "\t", ";", "ς", "ε", "ρ", "τ", "υ", "θ", "ι", "ο", "π", "[", "]", "\n", "", "α", "σ", "δ", "φ", "γ", "η", "ξ", "κ", "λ", "´", "'", "`", "", "\\", "ζ", "χ", "ψ", "ω", "β", "ν", "μ", ",", ".", "/", "", "*", "", " ", "", "", "", "", "", "", "", "", "", "", "", "", "", "7", "8", "9", "-", "4", "5", "6", "+", "1", "2", "3", "0", ".", "", "", "\\", "", "", ""] -} diff --git a/Base/res/keymaps/en-ca-cms.json b/Base/res/keymaps/en-ca-cms.json deleted file mode 100644 index 4fbf45e405b..00000000000 --- a/Base/res/keymaps/en-ca-cms.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "map":["","\u001b","1","2","3","4","5","6","7","8","9","0","-","=","\b","\t","q","w","e","r","t","y","u","i","o","p","^","ç","\n","","a","s","d","f","g","h","j","k","l",";","è","/","","à","z","x","c","v","b","n","m",",",".","é","","*",""," ","","","","","","","","","","","","","","7","8","9","-","4","5","6","+","1","2","3","0",".","","","à","","",""], - "shift_map":["","\u001b","!","@","#","$","%","?","&","*","(",")","_","+","\b","\t","Q","W","E","R","T","Y","U","I","O","P","¨","Ç","\n","","A","S","D","F","G","H","J","K","L",":","È","\\","","À","Z","X","C","V","B","N","M","'","\"","É","","*",""," ","","","","","","","","","","","","","","7","8","9","-","4","5","6","+","1","2","3","0",".","","","À","","",""], - "alt_map":["","\u001b","","","","¤","","","{","}","[","]","","¬","\b","\t","","","€","","","","","","","","`","~","\n","","","","","","","","","","","°","","|","","","«","»","","","","","","<",">","","","*",""," ","","","","","","","","","","","","","","{","}","[","-","¤","","","¬","","","","]",">","","","","","",""] -} diff --git a/Base/res/keymaps/en-ca.json b/Base/res/keymaps/en-ca.json deleted file mode 100644 index 7c40a5102e3..00000000000 --- a/Base/res/keymaps/en-ca.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "map":["","\u001b","1","2","3","4","5","6","7","8","9","0","-","=","\b","\t","q","w","e","r","t","y","u","i","o","p","[","]","\n","","a","s","d","f","g","h","j","k","l",";","'","`","","\\","z","x","c","v","b","n","m",",",".","/","","*",""," ","","","","","","","","","","","","","","7","8","9","-","4","5","6","+","1","2","3","0",".","","","\\","","",""], - "shift_map":["","\u001b","!","@","#","$","%","^","&","*","(",")","_","+","\b","\t","Q","W","E","R","T","Y","U","I","O","P","{","}","\n","","A","S","D","F","G","H","J","K","L",":","\"","~","","|","Z","X","C","V","B","N","M","<",">","?","","*",""," ","","","","","","","","","","","","","","7","8","9","-","4","5","6","+","1","2","3","0",".","","","|","","",""], - "alt_map":["","\u001b","1","2","3","4","5","6","7","8","9","0","-","=","\b","\t","q","w","e","r","t","y","u","i","o","p","[","]","\n","","a","s","d","f","g","h","j","k","l",";","'","`","","\\","z","x","c","v","b","n","m",",",".","/","","*",""," ","","","","","","","","","","","","","","7","8","9","-","4","5","6","+","1","2","3","0",".","","","\\","","",""] -} diff --git a/Base/res/keymaps/en-gb-macintosh.json b/Base/res/keymaps/en-gb-macintosh.json deleted file mode 100644 index 27a67340852..00000000000 --- a/Base/res/keymaps/en-gb-macintosh.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "map": [ "", "\u001B", "1", "2", "3", "4", "5", "6", "7", "8", "9", "0", "-", "=", "\b", "\t", "q", "w", "e", "r", "t", "y", "u", "i", "o", "p", "[", "]", "\n", "", "a", "s", "d", "f", "g", "h", "j", "k", "l", ";", "'", "`", "", "\\", "z", "x", "c", "v", "b", "n", "m", ",", ".", "/", "", "*", "", " ", "", "", "", "", "", "", "", "", "", "", "", "", "", "7", "8", "9", "-", "4", "5", "6", "+", "1", "2", "3", "0", ".", "", "", "\\", "", "", ""], - "shift_map": [ "", "\u001B", "!", "@", "£", "$", "%", "^", "&", "*", "(", ")", "_", "+", "\b", "\t", "Q", "W", "E", "R", "T", "Y", "U", "I", "O", "P", "{", "}", "\n", "", "A", "S", "D", "F", "G", "H", "J", "K", "L", ":", "\"", "~", "", "|", "Z", "X", "C", "V", "B", "N", "M", "<", ">", "?", "", "*", "", " ", "", "", "", "", "", "", "", "", "", "", "", "", "", "7", "8", "9", "-", "4", "5", "6", "+", "1", "2", "3", "0", ".", "", "", "|", "", "", ""], - "alt_map": [ "", "\u001B", "¡", "€", "#", "¢", "∞", "§", "¶", "•", "ª", "º", "–", "≠", "\b", "\t", "Œ", "∑", "´", "®", "†", "¥", "¨", "^", "ø", "π", "“", "‘", "\n", "", "å", "ß", "∂", "ƒ", "©", "˙", "∆", "˚", "¬", "…", "æ", "`", "", "«", "Ω", "≈", "ç", "√", "∫", "~", "µ", "≤", "≥", "÷", "", "*", "", " ", "", "", "", "", "", "", "", "", "", "", "", "", "", "7", "8", "9", "-", "4", "5", "6", "+", "1", "2", "3", "0", ".", "", "", "\\", "", "", ""], - "shift_altgr_map": [ "", "\u001B", "⁄", "™", "‹", "›", "fi", "fl", "‡", "°", "·", "‚", "—", "±", "\b", "\t", "Œ", "„", "‰", "Â", "Ê", "Á", "Ë", "È", "Ø", "∏", "”", "’", "\n", "", "Å", "Í", "Î", "Ï", "Ì", "Ó", "Ô", "", "Ò", "Ú", "Æ", "Ÿ", "", "»", "Û", "Ù", "Ç", "◊", "ı", "ˆ", "˜", "¯", "˘", "¿", "", "*", "", " ", "", "", "", "", "", "", "", "", "", "", "", "", "", "7", "8", "9", "-", "4", "5", "6", "+", "1", "2", "3", "0", ".", "", "", "\\", "", "", ""] -} diff --git a/Base/res/keymaps/en-gb.json b/Base/res/keymaps/en-gb.json deleted file mode 100644 index 980bce4ce17..00000000000 --- a/Base/res/keymaps/en-gb.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "map": [ "", "\u001B", "1", "2", "3", "4", "5", "6", "7", "8", "9", "0", "-", "=", "\b", "\t", "q", "w", "e", "r", "t", "y", "u", "i", "o", "p", "[", "]", "\n", "", "a", "s", "d", "f", "g", "h", "j", "k", "l", ";", "'","`", "", "#", "z", "x", "c", "v", "b", "n", "m", ",", ".", "/", "", "*", "", " ", "", "", "", "", "", "", "", "", "", "", "", "", "", "7", "8", "9", "-", "4", "5", "6", "+", "1", "2", "3", "0", ".", "", "", "\\", "", "", ""], - "shift_map": [ "", "\u001B", "!","\"", "£", "$", "%", "^", "&", "*", "(", ")", "_", "+", "\b", "\t", "Q", "W", "E", "R", "T", "Y", "U", "I", "O", "P", "{", "}", "\n", "", "A", "S", "D", "F", "G", "H", "J", "K", "L", ":", "@", "", "", "~", "Z", "X", "C", "V", "B", "N", "M", "<", ">", "?", "", "*", "", " ", "", "", "", "", "", "", "", "", "", "", "", "", "", "7", "8", "9", "-", "4", "5", "6", "+", "1", "2", "3", "0", ".", "", "", "|", "", "", ""], - "alt_map": [ "", "\u001B", "1", "2", "3", "4", "5", "6", "7", "8", "9", "0", "-", "=", "\b", "\t", "q", "w", "e", "r", "t", "y", "u", "i", "o", "p", "[", "]", "\n", "", "a", "s", "d", "f", "g", "h", "j", "k", "l", ";", "'", "", "", "#", "z", "x", "c", "v", "b", "n", "m", ",", ".", "/", "", "*", "", " ", "", "", "", "", "", "", "", "", "", "", "", "", "", "7", "8", "9", "-", "4", "5", "6", "+", "1", "2", "3", "0", ".", "", "", "\\", "", "", ""] -} diff --git a/Base/res/keymaps/en-us-macintosh.json b/Base/res/keymaps/en-us-macintosh.json deleted file mode 100644 index 5782e2289dd..00000000000 --- a/Base/res/keymaps/en-us-macintosh.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "map": [ "", "\u001B", "1", "2", "3", "4", "5", "6", "7", "8", "9", "0", "-", "=", "\b", "\t", "q", "w", "e", "r", "t", "y", "u", "i", "o", "p", "[", "]", "\n", "", "a", "s", "d", "f", "g", "h", "j", "k", "l", ";", "'", "`", "", "\\", "z", "x", "c", "v", "b", "n", "m", ",", ".", "/", "", "*", "", " ", "", "", "", "", "", "", "", "", "", "", "", "", "", "7", "8", "9", "-", "4", "5", "6", "+", "1", "2", "3", "0", ".", "", "", "\\", "", "", ""], - "shift_map": [ "", "\u001B", "!", "@", "#", "$", "%", "^", "&", "*", "(", ")", "_", "+", "\b", "\t", "Q", "W", "E", "R", "T", "Y", "U", "I", "O", "P", "{", "}", "\n", "", "A", "S", "D", "F", "G", "H", "J", "K", "L", ":", "\"", "~", "", "|", "Z", "X", "C", "V", "B", "N", "M", "<", ">", "?", "", "*", "", " ", "", "", "", "", "", "", "", "", "", "", "", "", "", "7", "8", "9", "-", "4", "5", "6", "+", "1", "2", "3", "0", ".", "", "", "|", "", "", ""], - "alt_map": [ "", "\u001B", "¡", "™", "£", "¢", "∞", "§", "¶", "•", "ª", "º", "–", "≠", "\b", "\t", "œ", "∑", "´", "®", "†", "¥", "¨", "ˆ", "ø", "π", "“", "‘", "\n", "", "å", "ß", "∂", "ƒ", "©", "˙", "∆", "˚", "¬", "…", "æ", "`", "", "«", "Ω", "≈", "ç", "√", "∫", "˜", "µ", "≤", "≥", "÷", "", "*", "", " ", "", "", "", "", "", "", "", "", "", "", "", "", "", "7", "8", "9", "-", "4", "5", "6", "+", "1", "2", "3", "0", ".", "", "", "\\", "", "", ""], - "shift_altgr_map": [ "", "\u001B", "⁄", "€", "‹", "›", "fi", "fl", "‡", "°", "·", "‚", "—", "±", "\b", "\t", "Œ", "„", "´", "‰", "ˇ", "Á", "¨", "ˆ", "Ø", "∏", "”", "’", "\n", "", "Å", "Í", "Î", "Ï", "˝", "Ó", "Ô", "", "Ò", "Ú", "Æ", "`", "", "»", "¸", "˛", "Ç", "◊", "ı", "˜", "Â", "¯", "˘", "¿", "", "*", "", " ", "", "", "", "", "", "", "", "", "", "", "", "", "", "7", "8", "9", "-", "4", "5", "6", "+", "1", "2", "3", "0", ".", "", "", "\\", "", "", ""] -} diff --git a/Base/res/keymaps/en-us.json b/Base/res/keymaps/en-us.json deleted file mode 100644 index 3f39bdb4475..00000000000 --- a/Base/res/keymaps/en-us.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "map": [ "", "\u001B", "1", "2", "3", "4", "5", "6", "7", "8", "9", "0", "-", "=", "\b", "\t", "q", "w", "e", "r", "t", "y", "u", "i", "o", "p", "[", "]", "\n", "", "a", "s", "d", "f", "g", "h", "j", "k", "l", ";", "'", "`", "", "\\", "z", "x", "c", "v", "b", "n", "m", ",", ".", "/", "", "*", "", " ", "", "", "", "", "", "", "", "", "", "", "", "", "", "7", "8", "9", "-", "4", "5", "6", "+", "1", "2", "3", "0", ".", "", "", "\\", "", "", ""], - "shift_map": [ "", "\u001B", "!", "@", "#", "$", "%", "^", "&", "*", "(", ")", "_", "+", "\b", "\t", "Q", "W", "E", "R", "T", "Y", "U", "I", "O", "P", "{", "}", "\n", "", "A", "S", "D", "F", "G", "H", "J", "K", "L", ":", "\"", "~", "", "|", "Z", "X", "C", "V", "B", "N", "M", "<", ">", "?", "", "*", "", " ", "", "", "", "", "", "", "", "", "", "", "", "", "", "7", "8", "9", "-", "4", "5", "6", "+", "1", "2", "3", "0", ".", "", "", "|", "", "", ""], - "alt_map": [ "", "\u001B", "1", "2", "3", "4", "5", "6", "7", "8", "9", "0", "-", "=", "\b", "\t", "q", "w", "e", "r", "t", "y", "u", "i", "o", "p", "[", "]", "\n", "", "a", "s", "d", "f", "g", "h", "j", "k", "l", ";", "'", "`", "", "\\", "z", "x", "c", "v", "b", "n", "m", ",", ".", "/", "", "*", "", " ", "", "", "", "", "", "", "", "", "", "", "", "", "", "7", "8", "9", "-", "4", "5", "6", "+", "1", "2", "3", "0", ".", "", "", "\\", "", "", ""] -} diff --git a/Base/res/keymaps/es.json b/Base/res/keymaps/es.json deleted file mode 100644 index b71209eb802..00000000000 --- a/Base/res/keymaps/es.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "map": [ "", "\u001B", "1", "2", "3", "4", "5", "6", "7", "8", "9", "0", "'", "¡", "\b", "\t", "q", "w", "e", "r", "t", "y", "u", "i", "o", "p", "`", "+", "\n", "", "a", "s", "d", "f", "g", "h", "j", "k", "l", "ñ", "´", "º", "", "ç", "z", "x", "c", "v", "b", "n", "m", ",", ".", "-", "", "", "", " ", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "<", "", "", ""], - "shift_map": [ "", "\u001B", "!", "\"", "·", "$", "%", "&", "/", "(", ")", "=", "?", "¿", "\b", "\t", "Q", "W", "E", "R", "T", "Y", "U", "I", "O", "P", "^", "*", "\n", "", "A", "S", "D", "F", "G", "H", "J", "K", "L", "Ñ", "¨", "ª", "", "Ç", "Z", "X", "C", "V", "B", "N", "M", ";", ":", "_", "", "", "", " ", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", ">", "", "", ""], - "alt_map": [ "", "\u001B", "|", "@", "#", "~", "", "¬", "", "", "", "", "´", "", "\b", "\t", "q", "w", "e", "r", "t", "y", "u", "i", "o", "p", "[", "]", "\n", "", "a", "s", "d", "f", "g", "h", "j", "k", "l", "ñ", "{", "\\", "", "}", "z", "x", "c", "v", "b", "n", "m", "", "", "", "", "", "", " ", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", ""] -} diff --git a/Base/res/keymaps/fi.json b/Base/res/keymaps/fi.json deleted file mode 100644 index d46ea353e91..00000000000 --- a/Base/res/keymaps/fi.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "map": [ "", "\u001B", "1", "2", "3", "4", "5", "6", "7", "8", "9", "0", "+", "´", "\b", "\t", "q", "w", "e", "r", "t", "y", "u", "i", "o", "p", "å", "¨", "\n", "", "a", "s", "d", "f", "g", "h", "j", "k", "l", "ö", "ä", "", "", "'", "z", "x", "c", "v", "b", "n", "m", ",", ".", "-", "", "*", "", " ", "", "", "", "", "", "", "", "", "", "", "", "", "", "7", "8", "9", "-", "4", "5", "6", "+", "1", "2", "3", "0", ",", "", "", "<", "", "", "" ], - "shift_map": [ "", "\u001B", "!", "\"", "#", "¤", "%", "&", "/", "(", ")", "=", "?", "`", "\b", "\t", "Q", "W", "E", "R", "T", "Y", "U", "I", "O", "P", "Å", "^", "\n", "", "A", "S", "D", "F", "G", "H", "J", "K", "L", "Ö", "Ä", "", "", "*", "Z", "X", "C", "V", "B", "N", "M", ";", ":", "_", "", "*", "", " ", "", "", "", "", "", "", "", "", "", "", "", "", "", "7", "8", "9", "-", "4", "5", "6", "+", "1", "2", "3", "0", ",", "", "", ">", "", "", "" ], - "alt_map": [ "", "\u001B", "1", "2", "3", "4", "5", "6", "7", "8", "9", "0", "+", "", "\b", "\t", "q", "w", "e", "r", "t", "y", "u", "i", "o", "p", "å", "\"", "\n", "", "a", "s", "d", "f", "g", "h", "j", "k", "l", "ö", "ä", "", "", "'", "z", "x", "c", "v", "b", "n", "m", ",", ".", "-", "", "*", "", " ", "", "", "", "", "", "", "", "", "", "", "", "", "", "7", "8", "9", "-", "4", "5", "6", "+", "1", "2", "3", "0", ",", "", "", "<", "", "", "" ], - "altgr_map": [ "", "\u001B", "", "@", "£", "$", "€", "‚", "{", "[", "]", "}", "\\", "", "\b", "\t", "q", "w", "€", "r", "þ", "y", "u", "ı", "œ", "p", "˝", "~", "\n", "", "ə", "ß", "ð", "f", "g", "h", "j", "ĸ", "l", "ø", "æ", "/", "", "", "ʒ", "×", "c", "v", "b", "ŋ", "µ", "’", "", "–", "", "", "", " ", "", "", "", "", "", "", "", "", "", "", "", "", "", "7", "8", "9", "-", "4", "5", "6", "+", "1", "2", "3", "0", ",", "", "", "|", "", "", "" ], - "shift_altgr_map": [ "", "\u001B", "¡", "”", "»", "«", "“", "„", "", "<", ">", "°", "¿", "", "\b", "\t", "Q", "W", "E", "R", "Þ", "Y", "U", "I", "Œ", "P", "", "", "\n", "", "Ə", "ẞ", "Ð", "F", "G", "H", "J", "", "", "Ø", "Æ", "", "", "", "Ʒ", "·", "C", "V", "B", "Ŋ", "—", "‘", "", "", "", "", "", " ", "", "", "", "", "", "", "", "", "", "", "", "", "", "7", "8", "9", "-", "4", "5", "6", "+", "1", "2", "3", "0", ",", "", "", "¦", "", "", "" ] -} diff --git a/Base/res/keymaps/fr-ca-cms.json b/Base/res/keymaps/fr-ca-cms.json deleted file mode 100644 index 4fbf45e405b..00000000000 --- a/Base/res/keymaps/fr-ca-cms.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "map":["","\u001b","1","2","3","4","5","6","7","8","9","0","-","=","\b","\t","q","w","e","r","t","y","u","i","o","p","^","ç","\n","","a","s","d","f","g","h","j","k","l",";","è","/","","à","z","x","c","v","b","n","m",",",".","é","","*",""," ","","","","","","","","","","","","","","7","8","9","-","4","5","6","+","1","2","3","0",".","","","à","","",""], - "shift_map":["","\u001b","!","@","#","$","%","?","&","*","(",")","_","+","\b","\t","Q","W","E","R","T","Y","U","I","O","P","¨","Ç","\n","","A","S","D","F","G","H","J","K","L",":","È","\\","","À","Z","X","C","V","B","N","M","'","\"","É","","*",""," ","","","","","","","","","","","","","","7","8","9","-","4","5","6","+","1","2","3","0",".","","","À","","",""], - "alt_map":["","\u001b","","","","¤","","","{","}","[","]","","¬","\b","\t","","","€","","","","","","","","`","~","\n","","","","","","","","","","","°","","|","","","«","»","","","","","","<",">","","","*",""," ","","","","","","","","","","","","","","{","}","[","-","¤","","","¬","","","","]",">","","","","","",""] -} diff --git a/Base/res/keymaps/fr-ca.json b/Base/res/keymaps/fr-ca.json deleted file mode 100644 index 7dfdfb958b8..00000000000 --- a/Base/res/keymaps/fr-ca.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "map":["","\u001b","1","2","3","4","5","6","7","8","9","0","-","=","\b","\t","q","w","e","r","t","y","u","i","o","p","^",".","\n","","a","s","d","f","g","h","j","k","l",";","`","#","","<","z","x","c","v","b","n","m",",",".","é","","*",""," ","","","","","","","","","","","","","","7","8","9","-","4","5","6","+","1","2","3","0",".","","","<","","",""], - "shift_map":["","\u001b","!","\"","/","$","%","?","&","*","(",")","_","+","\b","\t","Q","W","E","R","T","Y","U","I","O","P","^","¨","\n","","A","S","D","F","G","H","J","K","L",":","`","|","",">","Z","X","C","V","B","N","M","'",".","É","","*",""," ","","","","","","","","","","","","","","7","8","9","-","4","5","6","+","1","2","3","0",".","","",">","","",""], - "alt_map": [ "", "\u001B", "±", "@", "£", "¢", "¤", "¬", "¦", "²", "³", "¼", "½", "¾", "\b", "\t", "", "", "€", "", "", "", "", "", "§", "¶", "[", "]", "\n", "", "", "", "", "", "", "", "", "", "", "~", "{", "\\", "", "}", "", "", "", "", "", "", "", "¯", "", "´", "", "*", "", " ", "", "", "", "", "", "", "", "", "", "", "", "", "", "¦", "²", "³", "¼", "¢", "¤", "¬", "¾", "±", "@", "£", "¼", ".", "", "", "}", "", "", ""] -} diff --git a/Base/res/keymaps/fr-ch.json b/Base/res/keymaps/fr-ch.json deleted file mode 100644 index 63ad0ed049b..00000000000 --- a/Base/res/keymaps/fr-ch.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "map": ["", "\u001B", "1", "2", "3", "4", "5", "6", "7", "8", "9", "0", "'", "^", "\b", "\t", "q", "w", "e", "r", "t", "z", "u", "i", "o", "p", "è", "¨", "\n", "", "a", "s", "d", "f", "g", "h", "j", "k", "l", "é", "à", "§", "", "$", "y", "x", "c", "v", "b", "n", "m", ",", ".", "-", "", "*", "", " ", "", "", "", "", "", "", "", "", "", "", "", "", "", "7", "8", "9", "-", "4", "5", "6", "+", "1", "2", "3", "0", ".", "", "", "<", "", "", ""], - "shift_map": ["", "\u001B", "+", "\"", "*", "ç", "%", "&", "/", "(", ")", "=", "?", "`", "\b", "\t", "Q", "W", "E", "R", "T", "Z", "U", "I", "O", "P", "ü", "!", "\n", "", "A", "S", "D", "F", "G", "H", "J", "K", "L", "ö", "ä", "°", "", "£", "Y", "X", "C", "V", "B", "N", "M", ";", ":", "_", "", "*", "", " ", "", "", "", "", "", "", "", "", "", "", "", "", "", "7", "8", "9", "-", "4", "5", "6", "+", "1", "2", "3", "0", ".", "", "", ">", "", "", ""], - "alt_map": ["", "\u001B", "¦", "@", "#", "¼", "½", "¬", "|", "¢", "]", "}", "'", "~", "\b", "\t", "@", "ł", "€", "¶", "ŧ", "←", "↓", "→", "ø", "þ", "[", "]", "\n", "", "æ", "ß", "ð", "đ", "ŋ", "ħ", "˙", "ĸ", "ł", "'", "{", "|", "", "}", "«", "»", "¢", "„", "“", "n", "µ", "·", "…", "–", "", "*", "", " ", "", "", "", "", "", "", "", "", "", "", "", "", "", "7", "8", "9", "-", "4", "5", "6", "+", "1", "2", "3", "0", ".", "", "", "\\", "", "", ""], - "shift_altgr_map": ["", "\u001B", "¡", "⅛", "£", "$", "⅜", "⅝", "⅞", "™", "±", "°", "¿", "˛", "\b", "\t", "Ω", "Ł", "€", "®", "Ŧ", "¥", "↑", "ı", "Œ", "Þ", "°", "¯", "\n", "", "Æ", "§", "Ð", "ª", "Ŋ", "Ħ", "˙", "&", "Ł", "˝", "ˇ", "¬", "", "˘", "<", ">", "©", ",", "‘", "N", "º", "×", "÷", "—", "", "*", "", " ", "", "", "", "", "", "", "", "", "", "", "", "", "", "7", "8", "9", "-", "4", "5", "6", "+", "1", "2", "3", "0", ".", "", "", "¦", "", "", ""] -} diff --git a/Base/res/keymaps/fr.json b/Base/res/keymaps/fr.json deleted file mode 100644 index a939267ec1e..00000000000 --- a/Base/res/keymaps/fr.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "map": [ "", "\u001B", "&", "é", "\"", "'", "(", "-", "è", "_", "ç", "à", ")", "=", "\b", "\t", "a", "z", "e", "r", "t", "y", "u", "i", "o", "p", "^", "$", "\n", "", "q", "s", "d", "f", "g", "h", "j", "k", "l", "m", "ù", "²", "", "*", "w", "x", "c", "v", "b", "n", ",", ";", ":", "!", "", "*", "", " ", "", "", "", "", "", "", "", "", "", "", "", "", "", "7", "8", "9", "-", "4", "5", "6", "+", "1", "2", "3", "0", ".", "", "", "<", "", "", ""], - "shift_map": [ "", "\u001B", "1", "2", "3", "4", "5", "6", "7", "8", "9", "0", "°", "+", "\b", "\t", "A", "Z", "E", "R", "T", "Y", "U", "I", "O", "P", "¨", "£", "\n", "", "Q", "S", "D", "F", "G", "H", "J", "K", "L", "M", "%", "~", "", "µ", "W", "X", "C", "V", "B", "N", "?", ".", "/", "§", "", "*", "", " ", "", "", "", "", "", "", "", "", "", "", "", "", "", "7", "8", "9", "-", "4", "5", "6", "+", "1", "2", "3", "0", ".", "", "", ">", "", "", ""], - "alt_map": [ "", "\u001B", "¹", "~", "#", "{", "[", "|", "`", "\\", "^", "@", "]", "}", "\b", "\t", "a", "z", "e", "r", "t", "y", "u", "i", "o", "p", "^", "¤", "\n", "", "q", "s", "d", "f", "g", "h", "j", "k", "l", "m", "^", "²", "*", "|", "w", "x", "c", "v", "b", "n", "", "", "", "", "", "", "", " ", "", "", "", "", "", "", "", "", "", "", "", "", "", "7", "8", "9", "-", "4", "5", "6", "+", "1", "2", "3", "0", ".", "", "", "|", "", "", ""] -} diff --git a/Base/res/keymaps/he.json b/Base/res/keymaps/he.json deleted file mode 100644 index a9f33633f55..00000000000 --- a/Base/res/keymaps/he.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "map": [ "", "\u001B", "1", "2", "3", "4", "5", "6", "7", "8", "9", "0", "-", "=", "\b", "\t", "/", "'", "ק", "ר", "א", "ט", "ו", "ן", "ם", "פ", "]", "[", "\n", "", "ש", "ד", "ג", "כ", "ע", "י", "ח", "ל", "ך", "ף", ",", ";", "", "\\", "ז", "ס", "ב", "ה", "נ", "מ", "צ", "ת", "ץ", ".", "", "*", "", " ", "", "", "", "", "", "", "", "", "", "", "", "", "", "7", "8", "9", "-", "4", "5", "6", "+", "1", "2", "3", "0", ".", "", "", "\\", "", "", ""], - "shift_map": [ "", "\u001B", "!", "@", "#", "$", "%", "^", "&", "*", ")", "(", "_", "+", "\b", "\t", "Q", "W", "E", "R", "T", "Y", "U", "I", "O", "P", "}", "{", "\n", "", "A", "S", "D", "F", "G", "H", "J", "K", "L", ":", "\"", "~", "", "|", "Z", "X", "C", "V", "B", "N", "M", "<", ">", "?", "", "*", "", " ", "", "", "", "", "", "", "", "", "", "", "", "", "", "7", "8", "9", "-", "4", "5", "6", "+", "1", "2", "3", "0", ".", "", "", "|", "", "", ""], - "alt_map": [ "", "\u001B", "1", "2", "3", "4", "5", "6", "7", "8", "9", "0", "-", "=", "\b", "\t", "q", "w", "e", "r", "t", "y", "u", "i", "o", "p", "[", "]", "\n", "", "a", "s", "d", "f", "g", "h", "j", "k", "l", ";", "'", "`", "", "\\", "z", "x", "c", "v", "b", "n", "m", ",", ".", "/", "", "*", "", " ", "", "", "", "", "", "", "", "", "", "", "", "", "", "7", "8", "9", "-", "4", "5", "6", "+", "1", "2", "3", "0", ".", "", "", "\\", "", "", ""] -} diff --git a/Base/res/keymaps/hu.json b/Base/res/keymaps/hu.json deleted file mode 100644 index 1b2603f903d..00000000000 --- a/Base/res/keymaps/hu.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "map": [ "", "\u001B", "1", "2", "3", "4", "5", "6", "7", "8", "9", "ö", "ü", "ó", "\b", "\t", "q", "w", "e", "r", "t", "z", "u", "i", "o", "p", "ő", "ú", "\n", "", "a", "s", "d", "f", "g", "h", "j", "k", "l", "é", "á", "0", "", "ű", "y", "x", "c", "v", "b", "n", "m", ",", ".", "-", "", "*", "", " ", "", "", "", "", "", "", "", "", "", "", "", "", "", "7", "8", "9", "-", "4", "5", "6", "+", "1", "2", "3", "0", ",", "", "", "í", "", "", "" ], - "shift_map": [ "", "\u001B", "'", "\"", "+", "!", "%", "/", "=", "(", ")", "Ö", "Ü", "Ó", "\b", "\t", "Q", "W", "E", "R", "T", "Z", "U", "I", "O", "P", "Ő", "Ú", "\n", "", "A", "S", "D", "F", "G", "H", "J", "K", "L", "É", "Á", "§", "", "Ű", "Y", "X", "C", "V", "B", "N", "M", "?", ":", "_", "", "*", "", " ", "", "", "", "", "", "", "", "", "", "", "", "", "", "7", "8", "9", "-", "4", "5", "6", "+", "1", "2", "3", "0", ",", "", "", "Í", "", "", "" ], - "alt_map": [ "", "\u001B", "1", "2", "3", "4", "5", "6", "7", "8", "9", "ö", "ü", "ó", "\b", "\t", "q", "w", "e", "r", "t", "z", "u", "i", "o", "p", "ő", "ú", "\n", "", "a", "s", "d", "f", "g", "h", "j", "k", "l", "é", "á", "0", "", "ű", "y", "x", "c", "v", "b", "n", "m", ",", ".", "-", "", "*", "", " ", "", "", "", "", "", "", "", "", "", "", "", "", "", "7", "8", "9", "-", "4", "5", "6", "+", "1", "2", "3", "0", ",", "", "", "í", "", "", "" ], - "altgr_map": [ "", "\u001B", "~", "", "^", "", "", "", "`", "", "", "", "", "", "\b", "\t", "\\", "|", "Ä", "", "", "", "€", "Í", "", "", "÷", "×", "\n", "", "ä", "đ", "Đ", "[", "]", "", "", "ł", "Ł", "$", "ß", "", "", "¤", ">", "#", "&", "@", "{", "}", "<", ";", ">", "*", "", "", "", " ", "", "", "", "", "", "", "", "", "", "", "", "", "", "7", "8", "9", "-", "4", "5", "6", "+", "1", "2", "3", "0", ",", "", "", "<", "", "", "" ] -} diff --git a/Base/res/keymaps/it.json b/Base/res/keymaps/it.json deleted file mode 100644 index a3c6b6f05db..00000000000 --- a/Base/res/keymaps/it.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "map": [ "", "\u001B", "1", "2", "3", "4", "5", "6", "7", "8", "9", "0", "'", "ì", "\b", "\t", "q", "w", "e", "r", "t", "y", "u", "i", "o", "p", "è", "+", "\n", "", "a", "s", "d", "f", "g", "h", "j", "k", "l", "ò", "à", "\\", "", "ù", "z", "x", "c", "v", "b", "n", "m", ",", ".", "-", "", "*", "", " ", "", "", "", "", "", "", "", "", "", "", "", "", "", "7", "8", "9", "-", "4", "5", "6", "+", "1", "2", "3", "0", ",", "", "", "<", "", "", ""], - "shift_map": [ "", "\u001B", "!", "\"", "£", "$", "%", "&", "/", "(", ")", "=", "?", "^", "\b", "\t", "Q", "W", "E", "R", "T", "Y", "U", "I", "O", "P", "é", "*", "\n", "", "A", "S", "D", "F", "G", "H", "J", "K", "L", "ç", "°", "|", "", "§", "Z", "X", "C", "V", "B", "N", "M", ";", ":", "_", "", "*", "", " ", "", "", "", "", "", "", "", "", "", "", "", "", "", "7", "8", "9", "-", "4", "5", "6", "+", "1", "2", "3", "0", ",", "", "", ">", "", "", ""], - "alt_map": [ "", "\u001B", "1", "2", "3", "4", "5", "6", "7", "8", "9", "0", "-", "=", "\b", "\t", "q", "w", "e", "r", "t", "y", "u", "i", "o", "p", "[", "]", "\n", "", "a", "s", "d", "f", "g", "h", "j", "k", "l", ";", "'", "`", "", "\\", "z", "x", "c", "v", "b", "n", "m", ",", ".", "/", "", "*", "", " ", "", "", "", "", "", "", "", "", "", "", "", "", "", "7", "8", "9", "-", "4", "5", "6", "+", "1", "2", "3", "0", ".", "", "", "\\", "", "", ""], - "altgr_map": [ "", "\u001B", "1", "2", "3", "4", "5", "6", "7", "8", "9", "0", "-", "=", "\b", "\t", "q", "w", "e", "r", "t", "y", "u", "i", "o", "p", "[", "]", "\n", "", "a", "s", "d", "f", "g", "h", "j", "k", "l", "@", "#", "`", "", "\\", "z", "x", "c", "v", "b", "n", "m", ",", ".", "/", "", "*", "", " ", "", "", "", "", "", "", "", "", "", "", "", "", "", "7", "8", "9", "-", "4", "5", "6", "+", "1", "2", "3", "0", ".", "", "", "\\", "", "", ""], - "shift_altgr_map": [ "", "\u001B", "1", "2", "3", "4", "5", "6", "7", "8", "9", "0", "-", "=", "\b", "\t", "q", "w", "e", "r", "t", "y", "u", "i", "o", "p", "{", "}", "\n", "", "a", "s", "d", "f", "g", "h", "j", "k", "l", "@", "#", "`", "", "\\", "z", "x", "c", "v", "b", "n", "m", ",", ".", "/", "", "*", "", " ", "", "", "", "", "", "", "", "", "", "", "", "", "", "7", "8", "9", "-", "4", "5", "6", "+", "1", "2", "3", "0", ".", "", "", "\\", "", "", ""] -} diff --git a/Base/res/keymaps/jp.json b/Base/res/keymaps/jp.json deleted file mode 100644 index 93d943f6f7f..00000000000 --- a/Base/res/keymaps/jp.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "map": [ "", "\u001B", "1", "2", "3", "4", "5", "6", "7", "8", "9", "0", "-", "^", "\b", "\t", "q", "w", "e", "r", "t", "y", "u", "i", "o", "p", "@", "[", "\n", "", "a", "s", "d", "f", "g", "h", "j", "k", "l", ";", ":", "", "", "]", "z", "x", "c", "v", "b", "n", "m", ",", ".", "/", "", "*", "", " ", "", "", "", "", "", "", "", "", "", "", "", "", "", "7", "8", "9", "-", "4", "5", "6", "+", "1", "2", "3", "0", ",", "", "", "\\", "", "", "","","","","","","","","","","","","","","","","","","","","","","","","","","\\","","","","","","","","", "","\\", "", ""], - "shift_map": [ "", "\u001B", "!","\"", "#", "$", "%", "&", "'", "(", ")", "~", "=", "~", "\b", "\t", "Q", "W", "E", "R", "T", "Y", "U", "I", "O", "P", "`", "{", "\n", "", "A", "S", "D", "F", "G", "H", "J", "K", "L", "+", "*", "", "", "}", "Z", "X", "C", "V", "B", "N", "M", "<", ">", "?", "", "*", "", " ", "", "", "", "", "", "", "", "", "", "", "", "", "", "7", "8", "9", "-", "4", "5", "6", "+", "1", "2", "3", "0", ",", "", "", "|", "", "", "","","","","","","","","","","","","","","","","","","","","","","","","","", "_","","","","","","","","", "", "|", "", ""], - "alt_map": [ "", "\u001B", "1", "2", "3", "4", "5", "6", "7", "8", "9", "0", "-", "^", "\b", "\t", "q", "w", "e", "r", "t", "y", "u", "i", "o", "p", "@", "[", "\n", "", "a", "s", "d", "f", "g", "h", "j", "k", "l", ";", ":", "", "", "]", "z", "x", "c", "v", "b", "n", "m", ",", ".", "/", "", "*", "", " ", "", "", "", "", "", "", "", "", "", "", "", "", "", "7", "8", "9", "-", "4", "5", "6", "+", "1", "2", "3", "0", ",", "", "", "\\", "", "", "","","","","","","","","","","","","","","","","","","","","","","","","","","\\","","","","","","","","", "","\\", "", ""] -} diff --git a/Base/res/keymaps/la-latin1.json b/Base/res/keymaps/la-latin1.json deleted file mode 100644 index 45e7109d85f..00000000000 --- a/Base/res/keymaps/la-latin1.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "map": [ "", "\u001B", "1", "2", "3", "4", "5", "6", "7", "8", "9", "0", "'", "=", "\b", "\t", "q", "w", "e", "r", "t", "y", "u", "i", "o", "p", ",", "+", "\n", "", "a", "s", "d", "f", "g", "h", "j", "k", "l", "~", "{", "}", "", "\\", "z", "x", "c", "v", "b", "n", "m", ",", ".", "-", "", "*", "", " ", "", "", "", "", "", "", "", "", "", "", "", "", "", "7", "8", "9", "-", "4", "5", "6", "+", "1", "2", "3", "0", ",", "", "", "<", "", "", ""], - "shift_map": [ "", "\u001B", "!", "\"", "#", "$", "%", "&", "/", "(", ")", "=", "?", "+", "\b", "\t", "Q", "W", "E", "R", "T", "Y", "U", "I", "O", "P", ",", "+", "\n", "", "A", "S", "D", "F", "G", "H", "J", "K", "L", "~", "[", "]", "", "|", "Z", "X", "C", "V", "B", "N", "M", ";", ":", "_", "", "*", "", " ", "", "", "", "", "", "", "", "", "", "", "", "", "", "7", "8", "9", "-", "4", "5", "6", "+", "1", "2", "3", "0", ",", "", "", ">", "", "", ""], - "alt_map": [ "", "\u001B", "|", "@", "3", "~", "5", "6", "{", "[", "]", "}","\\", "=", "\b", "\t", "q", "w", "e", "r", "t", "y", "u", "i", "o", "p", ",", "+", "\n", "", "a", "s", "d", "f", "g", "h", "j", "k", "l", "~", "^", "`", "", "\\", "z", "x", "c", "v", "b", "n", "m", ",", ".", "-", "", "*", "", " ", "", "", "", "", "", "", "", "", "", "", "", "", "", "7", "8", "9", "-", "4", "5", "6", "+", "1", "2", "3", "0", ",", "", "", "|", "", "", ""] -} diff --git a/Base/res/keymaps/no-latin1.json b/Base/res/keymaps/no-latin1.json deleted file mode 100644 index 52d3ac1708a..00000000000 --- a/Base/res/keymaps/no-latin1.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "map":["", "\u001B", "1", "2", "3", "4", "5", "6", "7", "8", "9", "0", "+", "\\", "\b", "\t", "q", "w", "e", "r", "t", "y", "u", "i", "o", "p", "å", "", "\n", "", "a", "s", "d", "f", "g", "h", "j", "k", "l", "ø", "æ", "|", "", "'", "z", "x", "c", "v", "b", "n", "m", ",", ".", "-", "", "*", "", " ", "", "", "", "", "", "", "", "", "", "", "", "", "", "7", "8", "9", "-", "4", "5", "6", "+", "1", "2", "3", "0", ",", "", "", "<", "", "", ""], - "shift_map":["", "\u001B", "!", "\"", "#", "4", "%", "&", "/", "(", ")", "=", "?", "`", "\b", "\t", "Q", "W", "E", "R", "T", "Y", "U", "I", "O", "P", "Å", "^", "\n", "", "A", "S", "D", "F", "G", "H", "J", "K", "L", "Ø", "Æ", "§", "", "*", "Z", "X", "C", "V", "B", "N", "M", ";", ":", "_", "", "*", "", " ", "", "", "", "", "", "", "", "", "", "", "", "", "", "7", "8", "9", "-", "4", "5", "6", "+", "1", "2", "3", "0", ",", "", "", ">", "", "", ""], - "alt_map":["", "\u001B", "1", "@", "3", "$", "5", "6", "{", "[", "]", "}", "+", "\\", "\b", "\t", "q", "w", "e", "r", "t", "y", "u", "i", "o", "p", "", "", "\n", "", "a", "s", "d", "f", "g", "h", "j", "k", "l", "", "", "'", "", "<", "z", "x", "c", "v", "b", "n", "m", ",", ".", "-", "", "*", "", " ", "", "", "", "", "", "", "", "", "", "", "", "", "", "7", "8", "9", "-", "4", "5", "6", "+", "1", "2", "3", "0", ",", "", "", "\\", "", "", ""], - "altgr_map":["", "\u001B", "1", "@", "3", "$", "5", "6", "{", "[", "]", "}", "+", "\\", "\b", "\t", "q", "w", "e", "r", "t", "y", "u", "i", "o", "p", "", "~", "\n", "", "a", "s", "d", "f", "g", "h", "j", "k", "l", "", "", "'", "", "", "z", "x", "c", "v", "b", "n", "m", ",", ".", "-", "", "*", "", " ", "", "", "", "", "", "", "", "", "", "", "", "", "", "7", "8", "9", "-", "4", "5", "6", "+", "1", "2", "3", "0", ",", "", "", "\\", "", "", ""] -} diff --git a/Base/res/keymaps/no-macintosh.json b/Base/res/keymaps/no-macintosh.json deleted file mode 100644 index f37dd102d8c..00000000000 --- a/Base/res/keymaps/no-macintosh.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "map": ["", "\u001B", "1", "2", "3", "4", "5", "6", "7", "8", "9", "0", "+", "´", "\b", "\t", "q", "w", "e", "r", "t", "y", "u", "i", "o", "p", "å", "¨", "\n", "", "a", "s", "d", "f", "g", "h", "j", "k", "l", "ø", "æ", "<", "", "@", "z", "x", "c", "v", "b", "n", "m", ",", ".", "-", "", "*", "", " ", "", "", "", "", "", "", "", "", "", "", "", "", "", "7", "8", "9", "-", "4", "5", "6", "+", "1", "2", "3", "0", ",", "", "", "", "", "", "="], - "shift_map": ["", "\u001B", "!", "\"", "#", "$", "%", "&", "/", "(", ")", "=", "?", "`", "\b", "\t", "Q", "W", "E", "R", "T", "Y", "U", "I", "O", "P", "Å", "^", "\n", "", "A", "S", "D", "F", "G", "H", "J", "K", "L", "Ø", "Æ", ">", "", "*", "Z", "X", "C", "V", "B", "N", "M", ";", ":", "_", "", "*", "", " ", "", "", "", "", "", "", "", "", "", "", "", "", "", "7", "8", "9", "-", "4", "5", "6", "+", "1", "2", "3", "0", ",", "", "", "", "", "", "="], - "alt_map": ["", "\u001B", "©", "™", "£", "€", "∞", "§", "|", "[", "]", "≈", "±", "`", "\b", "\t", "•", "Ω", "é", "", "†", "µ", "ü", "ı", "œ", "π", "˙", "~", "\n", "", "", "ß", "∂", "ƒ", "¸", "˛", "√", "ª", "fi", "ö", "ä", "≤", "", "'", "÷", "≈", "ç", "‹", "›", "‘", "’", "‚", "…", "–", "", "*", "", " ", "", "", "", "", "", "", "", "", "", "", "", "", "", "7", "8", "9", "-", "4", "5", "6", "+", "1", "2", "3", "0", ",", "", "", "", "", "", "="], - "shift_altgr_map": ["", "\u001B", "¡", "®", "¥", "¢", "‰", "¶", "\\", "{", "}", "≠", "¿", "", "\b", "\t", "°", "˝", "É", "", "", "˜", "Ü", "", "Œ", "∏", "˚", "^", "\n", "", "◊", "∑", "∆", "∫", "¯", "˘", "¬", "º", "fl", "Ö", "Ä", "≥", "", "", "⁄", "", "Ç", "«", "»", "“", "”", "„", "·", "—", "", "*", "", " ", "", "", "", "", "", "", "", "", "", "", "", "", "", "7", "8", "9", "-", "4", "5", "6", "+", "1", "2", "3", "0", ",", "", "", "", "", "", "="] -} diff --git a/Base/res/keymaps/no.json b/Base/res/keymaps/no.json deleted file mode 100644 index e79e97c09d6..00000000000 --- a/Base/res/keymaps/no.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "map": [ "", "\u001B", "1", "2", "3", "4", "5", "6", "7", "8", "9", "0", "+", "\\", "\b", "\t", "q", "w", "e", "r", "t", "y", "u", "i", "o", "p", "", "", "\n", "", "a", "s", "d", "f", "g", "h", "j", "k", "l", "", "", "'", "", "<", "z", "x", "c", "v", "b", "n", "m", ",", ".", "-", "", "*", "", " ", "", "", "", "", "", "", "", "", "", "", "", "", "", "7", "8", "9", "-", "4", "5", "6", "+", "1", "2", "3", "0", ",", "", "", "\\", "", "", ""], - "shift_map": [ "", "\u001B", "!", "\"", "#", "4", "%", "&", "/", "(", ")", "=", "?", "`", "\b", "\t", "Q", "W", "E", "R", "T", "Y", "U", "I", "O", "P", "", "", "\n", "", "A", "S", "D", "F", "G", "H", "J", "K", "L", "", "", "*", "", ">", "Z", "X", "C", "V", "B", "N", "M", ";", ":", "_", "", "*", "", " ", "", "", "", "", "", "", "", "", "", "", "", "", "", "7", "8", "9", "-", "4", "5", "6", "+", "1", "2", "3", "0", ",", "", "", "|", "", "", ""], - "alt_map": [ "", "\u001B", "1", "@", "3", "$", "5", "6", "{", "[", "]", "}", "+", "\\", "\b", "\t", "q", "w", "e", "r", "t", "y", "u", "i", "o", "p", "", "", "\n", "", "a", "s", "d", "f", "g", "h", "j", "k", "l", "", "", "'", "", "<", "z", "x", "c", "v", "b", "n", "m", ",", ".", "-", "", "*", "", " ", "", "", "", "", "", "", "", "", "", "", "", "", "", "7", "8", "9", "-", "4", "5", "6", "+", "1", "2", "3", "0", ",", "", "", "\\", "", "", ""] -} diff --git a/Base/res/keymaps/pl.json b/Base/res/keymaps/pl.json deleted file mode 100644 index 1c3b593e3ce..00000000000 --- a/Base/res/keymaps/pl.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "map": [ "", "\u001B", "1", "2", "3", "4", "5", "6", "7", "8", "9", "0", "-", "=", "\b", "\t", "q", "w", "e", "r", "t", "y", "u", "i", "o", "p", "[", "]", "\n", "", "a", "s", "d", "f", "g", "h", "j", "k", "l", ";", "'", "`", "", "\\", "z", "x", "c", "v", "b", "n", "m", ",", ".", "/", "", "*", "", " ", "", "", "", "", "", "", "", "", "", "", "", "", "", "7", "8", "9", "-", "4", "5", "6", "+", "1", "2", "3", "0", ".", "", "", "\\", "", "", ""], - "shift_map": [ "", "\u001B", "!", "@", "#", "$", "%", "^", "&", "*", "(", ")", "_", "+", "\b", "\t", "Q", "W", "E", "R", "T", "Y", "U", "I", "O", "P", "{", "}", "\n", "", "A", "S", "D", "F", "G", "H", "J", "K", "L", ":", "\"", "~", "", "|", "Z", "X", "C", "V", "B", "N", "M", "<", ">", "?", "", "*", "", " ", "", "", "", "", "", "", "", "", "", "", "", "", "", "7", "8", "9", "-", "4", "5", "6", "+", "1", "2", "3", "0", ".", "", "", "|", "", "", ""], - "alt_map": [ "", "\u001B", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "ę", "", "", "", "€", "", "ó", "", "[", "]", "\n", "", "ą", "ś", "", "", "", "", "", "", "ł", "", "", "", "", "", "ż", "ź", "ć", "", "", "ń", "", "", "", "", "", "", "", " ", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", ""], - "shift_altgr_map": [ "", "\u001B", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "Ę", "", "", "", "", "", "Ó", "", "[", "]", "\n", "", "Ą", "Ś", "", "", "", "", "", "", "Ł", "", "", "", "", "", "Ż", "Ź", "Ć", "", "", "Ń", "", "", "", "", "", "", "", " ", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", ""] -} diff --git a/Base/res/keymaps/pt-br.json b/Base/res/keymaps/pt-br.json deleted file mode 100644 index df541440be7..00000000000 --- a/Base/res/keymaps/pt-br.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "map": [ "", "\u001B", "1", "2", "3", "4", "5", "6", "7", "8", "9", "0", "-", "=", "\b", "\t", "q", "w", "e", "r", "t", "y", "u", "i", "o", "p", "´", "[", "\n", "", "a", "s", "d", "f", "g", "h", "j", "k", "l", "ç", "~", "'", "", "]", "z", "x", "c", "v", "b", "n", "m", ",", ".", ";", "/", "*", "", " ", "", "", "", "", "", "", "", "", "", "", "", "", "", "7", "8", "9", "-", "4", "5", "6", "+", "1", "2", "3", "0", ",", "", "", "\\", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "/", "", "", "", "", "", "", "", "", "", "", "", ""], - "shift_map": [ "", "\u001B", "!", "@", "#", "$", "%", "¨", "&", "*", "(", ")", "_", "+", "\b", "\t", "Q", "W", "E", "R", "T", "Y", "U", "I", "O", "P", "`", "{", "\n", "", "A", "S", "D", "F", "G", "H", "J", "K", "L", "Ç", "^", "\"", "", "}", "Z", "X", "C", "V", "B", "N", "M", "<", ">", ":", "", "*", "", " ", "", "", "", "", "", "", "", "", "", "", "", "", "", "7", "8", "9", "-", "4", "5", "6", "+", "1", "2", "3", "0", ",", "", "", "|", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "?", "", "", "", "", "", "", "", "", "", "", "", ""], - "alt_map": [ "", "\u001B", "¹", "²", "³", "£", "¢", "¬", "7", "8", "9", "0", "-", "§", "\b", "\t", "/", "?", "°", "r", "t", "y", "u", "i", "o", "p", "'", "ª", "\n", "", "a", "s", "d", "f", "g", "h", "j", "k", "l", "ç", "~", "'", "", "º", "z", "x", "₢", "v", "b", "n", "m", ",", ".", ";", "°", "*", "", " ", "", "", "", "", "", "", "", "", "", "", "", "", "", "7", "8", "9", "-", "4", "5", "6", "+", "1", "2", "3", "0", ",", "", "", "\\", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "°", "", "", "", "", "", "", "", "", "", "", "", ""] -} diff --git a/Base/res/keymaps/pt-pt.json b/Base/res/keymaps/pt-pt.json deleted file mode 100644 index 05a52fe569c..00000000000 --- a/Base/res/keymaps/pt-pt.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "map": [ "", "\u001B", "1", "2", "3", "4", "5", "6", "7", "8", "9", "0", "'", "«", "\b", "\t", "q", "w", "e", "r", "t", "y", "u", "i", "o", "p", "+", "´", "\n", "", "a", "s", "d", "f", "g", "h", "j", "k", "l", "ç", "º", "\\", "", "~", "z", "x", "c", "v", "b", "n", "m", ",", ".", "-", "", "*", "", " ", "", "", "", "", "", "", "", "", "", "", "", "", "", "7", "8", "9", "-", "4", "5", "6", "+", "1", "2", "3", "0", ".", "", "", "<", "", "", ""], - "shift_map": [ "", "\u001B", "!", "\"", "#", "$", "%", "&", "/", "(", ")", "=", "?", "»", "\b", "\t", "Q", "W", "E", "R", "T", "Y", "U", "I", "O", "P", "*", "`", "\n", "", "A", "S", "D", "F", "G", "H", "J", "K", "L", "Ç", "ª", "|", "", "^", "Z", "X", "C", "V", "B", "N", "M", ";", ":", "_", "", "*", "", " ", "", "", "", "", "", "", "", "", "", "", "", "", "", "7", "8", "9", "-", "4", "5", "6", "+", "1", "2", "3", "0", ".", "", "", ">", "", "", ""], - "alt_map": [ "", "\u001B", "1", "2", "3", "4", "5", "6", "7", "8", "9", "0", "'", "«", "\b", "\t", "q", "w", "e", "r", "t", "y", "u", "i", "o", "p", "+", "´", "\n", "", "a", "s", "d", "f", "g", "h", "j", "k", "l", "ç", "º", "\\", "", "~", "z", "x", "c", "v", "b", "n", "m", ",", ".", "-", "", "*", "", " ", "", "", "", "", "", "", "", "", "", "", "", "", "", "7", "8", "9", "-", "4", "5", "6", "+", "1", "2", "3", "0", ".", "", "", "<", "", "", ""], - "altgr_map": [ "", "\u001B", "¹", "@", "£", "§", "½", "¬", "{", "[", "]", "}", "\\", "¸", "\b", "\t", "@", "ł", "€", "¶", "ŧ", "←", "↓", "→", "ø", "þ", "¨", "~", "\n", "", "æ", "ß", "ð", "đ", "ŋ", "ħ", "̉", "ĸ", "ł", "´", "^", "¬", "", "`", "«", "»", "¢", "“", "”", "n", "µ", ",", "·", "̣", "", "*", "", " ", "", "", "", "", "", "", "", "", "", "", "", "", "", "7", "8", "9", "-", "4", "5", "6", "+", "1", "2", "3", "0", ".", "", "", "\\", "", "", ""] -} diff --git a/Base/res/keymaps/ro.json b/Base/res/keymaps/ro.json deleted file mode 100644 index 123d2442fe0..00000000000 --- a/Base/res/keymaps/ro.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "map": [ "", "\u001B", "1", "2", "3", "4", "5", "6", "7", "8", "9", "0", "-", "=", "\b", "\t", "q", "w", "e", "r", "t", "y", "u", "i", "o", "p", "[", "]", "\n", "", "a", "s", "d", "f", "g", "h", "j", "k", "l", ";", "'", "`", "", "\\", "z", "x", "c", "v", "b", "n", "m", ",", ".", "/", "", "*", "", " ", "", "", "", "", "", "", "", "", "", "", "", "", "", "7", "8", "9", "-", "4", "5", "6", "+", "1", "2", "3", "0", ".", "", "", "\\", "", "", ""], - "shift_map": [ "", "\u001B", "!", "@", "#", "$", "%", "^", "&", "*", "(", ")", "_", "+", "\b", "\t", "Q", "W", "E", "R", "T", "Y", "U", "I", "O", "P", "{", "}", "\n", "", "A", "S", "D", "F", "G", "H", "J", "K", "L", ":", "\"", "~", "", "|", "Z", "X", "C", "V", "B", "N", "M", "<", ">", "?", "", "*", "", " ", "", "", "", "", "", "", "", "", "", "", "", "", "", "7", "8", "9", "-", "4", "5", "6", "+", "1", "2", "3", "0", ".", "", "", "|", "", "", ""], - "alt_map": [ "", "\u001B", "1", "2", "3", "4", "5", "6", "7", "8", "9", "0", "-", "=", "\b", "\t", "q", "w", "e", "r", "t", "y", "u", "i", "o", "p", "[", "]", "\n", "", "a", "s", "d", "f", "g", "h", "j", "k", "l", ";", "'", "`", "", "\\", "z", "x", "c", "v", "b", "n", "m", ",", ".", "/", "", "*", "", " ", "", "", "", "", "", "", "", "", "", "", "", "", "", "7", "8", "9", "-", "4", "5", "6", "+", "1", "2", "3", "0", ".", "", "", "\\", "", "", ""], - "altgr_map": [ "", "\u001B", "1", "2", "3", "4", "5", "6", "7", "8", "9", "0", "-", "=", "\b", "\t", "â", "ß", "€", "r", "ț", "y", "u", "î", "o", "§", "{", "]", "\n", "", "ă", "ș", "đ", "f", "g", "h", "j", "k", "l", ";", "'", "`", "", "\\", "z", "x", "©", "v", "b", "n", "m", "«", "»", "/", "", "*", "", " ", "", "", "", "", "", "", "", "", "", "", "", "", "", "7", "8", "9", "-", "4", "5", "6", "+", "1", "2", "3", "0", ".", "", "", "\\", "", "", ""], - "shift_altgr_map": [ "", "\u001B", "!", "@", "#", "$", "%", "^", "&", "*", "(", ")", "_", "+", "\b", "\t", "Â", "ẞ", "E", "R", "Ț", "Y", "U", "Î", "O", "P", "{", "}", "\n", "", "Ă", "Ș", "Đ", "F", "G", "H", "J", "K", "L", ":", "\"", "~", "", "|", "Z", "X", "C", "V", "B", "N", "M", "<", ">", "?", "", "*", "", " ", "", "", "", "", "", "", "", "", "", "", "", "", "", "7", "8", "9", "-", "4", "5", "6", "+", "1", "2", "3", "0", ".", "", "", "|", "", "", ""] -} diff --git a/Base/res/keymaps/ru.json b/Base/res/keymaps/ru.json deleted file mode 100644 index 89998cb9cfa..00000000000 --- a/Base/res/keymaps/ru.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "map": [ "", "\u001B", "1", "2", "3", "4", "5", "6", "7", "8", "9", "0", "-", "=", "\b", "\t", "й", "ц", "у", "к", "е", "н", "г", "ш", "щ", "з", "х", "ъ", "\n", "", "ф", "ы", "в", "а", "п", "р", "о", "л", "д", "ж", "э", "ё", "", "\\", "я", "ч", "с", "м", "и", "т", "ь", "б", "ю", ".", "", "*", "", " ", "", "", "", "", "", "", "", "", "", "", "", "", "", "7", "8", "9", "-", "4", "5", "6", "+", "1", "2", "3", "0", ".", "", "", "\\", "", "", ""], - "shift_map": [ "", "\u001B", "!", "\"", "№", ";", "%", ":", "?", "*", "(", ")", "_", "+", "\b", "\t", "Й", "Ц", "У", "К", "Е", "Н", "Г", "Ш", "Щ", "З", "Х", "Ъ", "\n", "", "Ф", "Ы", "В", "А", "П", "Р", "О", "Л", "Д", "Ж", "Э", "Ё", "", "/", "Я", "Ч", "С", "М", "И", "Т", "Ь", "Б", "Ю", ",", "", "*", "", " ", "", "", "", "", "", "", "", "", "", "", "", "", "", "7", "8", "9", "-", "4", "5", "6", "+", "1", "2", "3", "0", ".", "", "", "|", "", "", ""], - "alt_map": [ "", "\u001B", "!", "@", "#", "$", "©", "^", "&", "₽", "(", ")", "–", "≠", "\b", "\t", "ј", "џ", "ў", "ќ", "†", "њ", "ѓ", "ѕ", "'", "‘", "“", "«", "\n", "", "d", "z", "ћ", "÷", "…", "•", "∆", "љ", "l", "«", "є", "Ë", "", "\\", "ђ", "x", "c", "v", "і", "ƒ", "m", "≤", "≥", "ї", "", "*", "", " ", "", "", "", "", "", "", "", "", "", "", "", "", "", "7", "8", "9", "-", "4", "5", "6", "+", "1", "2", "3", "0", ".", "", "", "\\", "", "", ""], - "shift_altgr_map": [ "", "\u001B", "™", "§", "£", "€", "®", "", "¶", "°", "{", "}", "—", "≈", "\b", "\t", "Œ", "„", "´", "‰", "ˇ", "Á", "¨", "ˆ", "Ø", "∏", "”", "’", "\n", "d", "Z", "Ћ", "±", "", "∞", "µ", "Љ", "L", "»", "Є", "]", "″", "", "Ђ", "X", "C", "V", "І", "ƒ", "M", "<", ">", "Ї", "—", "", "*", "", " ", "", "", "", "", "", "", "", "", "", "", "", "", "", "7", "8", "9", "-", "4", "5", "6", "+", "1", "2", "3", "0", ".", "", "", "", "", "", ""] -} diff --git a/Base/res/keymaps/se.json b/Base/res/keymaps/se.json deleted file mode 100644 index e3ded16468a..00000000000 --- a/Base/res/keymaps/se.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "map": [ "", "\u001B", "1", "2", "3", "4", "5", "6", "7", "8", "9", "0", "+", "'", "\b", "\t", "q", "w", "e", "r", "t", "y", "u", "i", "o", "p", "å", "\"", "\n", "", "a", "s", "d", "f", "g", "h", "j", "k", "l", "ö", "ä", "§", "", "'", "z", "x", "c", "v", "b", "n", "m", ",", ".", "-", "", "*", "", " ", "", "", "", "", "", "", "", "", "", "", "", "", "", "7", "8", "9", "-", "4", "5", "6", "+", "1", "2", "3", "0", ",", "", "", "<", "", "", "" ], - "shift_map": [ "", "\u001B", "!", "\"", "#", "¤", "%", "&", "/", "(", ")", "=", "?", "`", "\b", "\t", "Q", "W", "E", "R", "T", "Y", "U", "I", "O", "P", "Å", "^", "\n", "", "A", "S", "D", "F", "G", "H", "J", "K", "L", "Ö", "Ä", "½", "", "*", "Z", "X", "C", "V", "B", "N", "M", ";", ":", "_", "", "*", "", " ", "", "", "", "", "", "", "", "", "", "", "", "", "", "7", "8", "9", "-", "4", "5", "6", "+", "1", "2", "3", "0", ",", "", "", ">", "", "", "" ], - "alt_map": [ "", "\u001B", "1", "2", "3", "4", "5", "6", "7", "8", "9", "0", "+", "'", "\b", "\t", "q", "w", "e", "r", "t", "y", "u", "i", "o", "p", "å", "\"", "\n", "", "a", "s", "d", "f", "g", "h", "j", "k", "l", "ö", "ä", "", "", "'", "z", "x", "c", "v", "b", "n", "m", ",", ".", "-", "", "*", "", " ", "", "", "", "", "", "", "", "", "", "", "", "", "", "7", "8", "9", "-", "4", "5", "6", "+", "1", "2", "3", "0", ",", "", "", "<", "", "", "" ], - "altgr_map": [ "", "\u001B", "¡", "@", "£", "$", "€", "¥", "{", "[", "]", "}", "\\", "", "\b", "\t", "q", "w", "e", "r", "t", "y", "u", "i", "o", "p", "å", "~", "\n", "", "a", "s", "d", "f", "g", "h", "j", "k", "l", "ö", "ä", "", "", "", "z", "x", "c", "v", "b", "n", "m", ",", ".", "-", "", "*", "", " ", "", "", "", "", "", "", "", "", "", "", "", "", "", "7", "8", "9", "-", "4", "5", "6", "+", "1", "2", "3", "0", ",", "", "", "|", "", "", "" ] -} diff --git a/Base/res/keymaps/sk.json b/Base/res/keymaps/sk.json deleted file mode 100644 index 1a749728d01..00000000000 --- a/Base/res/keymaps/sk.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "map": [ "", "\u001B", "+", "ľ", "š", "č", "ť", "ž", "ý", "á", "í", "é", "=", "'", "\b", "\t", "q", "w", "e", "r", "t", "y", "u", "i", "o", "p", "ú", "ä", "\n", "", "a", "s", "d", "f", "g", "h", "j", "k", "l", "ô", "§", "<", "", "ň", "z", "x", "c", "v", "b", "n", "m", ",", ".", "-", "", "*", "", " ", "", "", "", "", "", "", "", "", "", "", "", "", "", "7", "8", "9", "-", "4", "5", "6", "+", "1", "2", "3", "0", ".", "", "", "\\", "", "", ""], - "shift_map": [ "", "\u001B", "1", "2", "3", "4", "5", "6", "7", "8", "9", "0", "%", "ˇ", "\b", "\t", "Q", "W", "E", "R", "T", "Y", "U", "I", "O", "P", "/", "(", "\n", "", "A", "S", "D", "F", "G", "H", "J", "K", "L", "\"", "!", ">", "", ")", "Z", "X", "C", "V", "B", "N", "M", "?", ":", "_", "", "*", "", " ", "", "", "", "", "", "", "", "", "", "", "", "", "", "7", "8", "9", "-", "4", "5", "6", "+", "1", "2", "3", "0", ".", "", "", "|", "", "", ""], - "alt_map": [ "", "\u001B", "`", "@", "#", "$", "~", "^", "&", "*", "{", "}", "°", "^", "\b", "\t", "q", "w", "e", "r", "t", "y", "u", "i", "o", "p", "[", "]", "\n", "", "a", "s", "d", "f", "g", "h", "j", "k", "ł", ";", "'", "<", "", "¨", "ż", "x", "c", "v", "b", "n", "m", "<", ">", "–", "", "*", "", " ", "", "", "", "", "", "", "", "", "", "", "", "", "", "7", "8", "9", "-", "4", "5", "6", "+", "1", "2", "3", "0", ".", "", "", "\\", "", "", ""] -} diff --git a/Base/res/keymaps/sl.json b/Base/res/keymaps/sl.json deleted file mode 100644 index 367fe3c6419..00000000000 --- a/Base/res/keymaps/sl.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "map": [ "", "\u001B", "1", "2", "3", "4", "5", "6", "7", "8", "9", "0", "'", "+", "\b", "\t", "q", "w", "e", "r", "t", "z", "u", "i", "o", "p", "š", "đ", "\n", "", "a", "s", "d", "f", "g", "h", "j", "k", "l", "č", "ć", "¸", "", "ž", "y", "x", "c", "v", "b", "n", "m", ",", ".", "-", "", "*", "", " ", "", "", "", "", "", "", "", "", "", "", "", "", "", "7", "8", "9", "-", "4", "5", "6", "+", "1", "2", "3", "0", ",", "", "", "<", "", "", ""], - "shift_map": [ "", "\u001B", "!", "\"", "#", "$", "%", "&", "/", "(", ")", "=", "?", "*", "\b", "\t", "Q", "W", "E", "R", "T", "Z", "U", "I", "O", "P", "Š", "Đ", "\n", "", "A", "S", "D", "F", "G", "H", "J", "K", "L", "Č", "Ć", "¨", "", "Ž", "Y", "X", "C", "V", "B", "N", "M", ";", ":", "_", "", "*", "", " ", "", "", "", "", "", "", "", "", "", "", "", "", "", "7", "8", "9", "-", "4", "5", "6", "+", "1", "2", "3", "0", ",", "", "", ">", "", "", ""], - "alt_map": [ "", "\u001B", "1", "2", "3", "4", "5", "6", "7", "8", "9", "0", "'", "+", "\b", "\t", "q", "w", "e", "r", "t", "z", "u", "i", "o", "p", "š", "đ", "\n", "", "a", "s", "d", "f", "g", "h", "j", "k", "l", "č", "ć", "¸", "", "ž", "y", "x", "c", "v", "b", "n", "m", ",", ".", "-", "", "*", "", " ", "", "", "", "", "", "", "", "", "", "", "", "", "", "7", "8", "9", "-", "4", "5", "6", "+", "1", "2", "3", "0", ",", "", "", "<", "", "", ""], - "altgr_map": [ "", "\u001B", "~", "", "^", "", "", "", "`", "", "", "", "", "", "\b", "\t", "\\", "|", "€", "", "", "", "", "", "", "", "÷", "×", "\n", "", "", "", "", "[", "]", "", "", "ł", "Ł", "", "ß", "", "", "¤", "", "", "", "@", "{", "}", "§", "<", ">", "", "", "*", "", " ", "", "", "", "", "", "", "", "", "", "", "", "", "", "7", "8", "9", "-", "4", "5", "6", "+", "1", "2", "3", "0", ",", "", "", "", "", "", ""] -} diff --git a/Base/res/keymaps/sv-dvorak.json b/Base/res/keymaps/sv-dvorak.json deleted file mode 100644 index 703ae2a2e80..00000000000 --- a/Base/res/keymaps/sv-dvorak.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "map": [ "", "\u001B", "1", "2", "3", "4", "5", "6", "7", "8", "9", "0", "+", "'", "\b", "\t", "å", "ä", "ö", "p", "y", "f", "g", "c", "r", "l", ",", "\"", "\n", "", "a", "o", "e", "u", "i", "d", "h", "t", "n", "s", "-", "", "", "'", ".", "q", "j", "k", "x", "b", "m", "w", "v", "z", "", "*", "", " ", "", "", "", "", "", "", "", "", "", "", "", "", "", "7", "8", "9", "-", "4", "5", "6", "+", "1", "2", "3", "0", ",", "", "", "<", "", "", "" ], - "shift_map": [ "", "\u001B", "!", "\"", "#", "", "%", "&", "/", "(", ")", "=", "?", "`", "\b", "\t", "Å", "Ä", "Ö", "P", "Y", "F", "G", "C", "R", "L", ";", "^", "\n", "", "A", "O", "E", "U", "I", "D", "H", "T", "N", "S", "_", "", "", "*", ":", "Q", "J", "K", "X", "B", "M", "W", "V", "Z", "", "*", "", " ", "", "", "", "", "", "", "", "", "", "", "", "", "", "7", "8", "9", "-", "4", "5", "6", "+", "1", "2", "3", "0", ",", "", "", ">", "", "", "" ], - "alt_map": [ "", "\u001B", "1", "2", "3", "4", "5", "6", "7", "8", "9", "0", "+", "'", "\b", "\t", "å", "ä", "ö", "p", "y", "f", "g", "c", "r", "l", ",", "\"", "\n", "", "a", "o", "e", "u", "i", "d", "h", "t", "n", "s", "-", "", "", "'", ".", "q", "j", "k", "x", "b", "m", "w", "v", "z", "", "*", "", " ", "", "", "", "", "", "", "", "", "", "", "", "", "", "7", "8", "9", "-", "4", "5", "6", "+", "1", "2", "3", "0", ",", "", "", "<", "", "", "" ], - "altgr_map": [ "", "\u001B", "", "@", "", "$", "", "", "{", "[", "]", "}", "\\", "", "\b", "\t","\\", "{", "}", "þ", "←", "đ", "ŋ", "©", "®", "ł", "¸", "~", "\n", "", "æ", "œ", "€", "↓", "→", "ð", "ħ", "ŧ", "n", "§", "˙", "/", "", "×", "·", "@", "©", "ĸ", "»", "”", "µ", "ł", "“", "«", "", "*", "", " ", "", "", "", "", "", "", "", "", "", "", "", "", "", "7", "8", "9", "-", "4", "5", "6", "+", "1", "2", "3", "0", ",", "", "", "|", "", "", "" ] -} diff --git a/Base/res/keymaps/th.json b/Base/res/keymaps/th.json deleted file mode 100644 index e4d5bd6d898..00000000000 --- a/Base/res/keymaps/th.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "map": [ "", "\u001B", "ๅ", "/", "_", "ภ", "ถ", "ุ", "ึ", "ค", "ต", "จ", "ข", "ช", "\b", "\t", "ๆ", "ไ", "ำ", "พ", "ะ", "ั", "ี", "ร", "น", "ย", "บ", "ล", "\n", "", "ฟ", "ห", "ก", "ด", "เ", "้", "่", "า", "ส", "ว", "ง", "-", "", "ฃ", "ผ", "ป", "แ", "อ", "ิ", "ื", "ท", "ม", "ใ", "ฝ", "", "*", "", " ", "", "", "", "", "", "", "", "", "", "", "", "", "", "7", "8", "9", "-", "4", "5", "6", "+", "1", "2", "3", "0", ".", "", "", "\\", "", "", ""], - "shift_map": [ "", "\u001B", "+", "๑", "๒", "๓", "๔", "ู", "฿", "๕", "๖", "๗", "๘", "๙", "\b", "\t", "๐", "\"", "ฎ", "ฑ", "ธ", "ํ", "๊", "ณ", "ฯ", "ญ", "ฐ", ",", "\n", "", "ฤ", "ฆ", "ฏ", "โ", "ฌ", "็", "๋", "ษ", "ศ", "ซ", ".", "%", "", "ฅ", "(", ")", "ฉ", "ฮ", "ฺ", "์", "?", "ฒ", "ฬ", "ฦ", "", "*", "", " ", "", "", "", "", "", "", "", "", "", "", "", "", "", "7", "8", "9", "-", "4", "5", "6", "+", "1", "2", "3", "0", ".", "", "", "|", "", "", ""], - "alt_map": [ "", "\u001B", "ๅ", "/", "_", "ภ", "ถ", "ุ", "ึ", "ค", "ต", "จ", "ข", "ช", "\b", "\t", "ๆ", "ไ", "ำ", "พ", "ะ", "ั", "ี", "ร", "น", "ย", "บ", "ล", "\n", "", "ฟ", "ห", "ก", "ด", "เ", "้", "่", "า", "ส", "ว", "ง", "-", "", "ฃ", "ผ", "ป", "แ", "อ", "ิ", "ื", "ท", "ม", "ใ", "ฝ", "", "*", "", " ", "", "", "", "", "", "", "", "", "", "", "", "", "", "7", "8", "9", "-", "4", "5", "6", "+", "1", "2", "3", "0", ".", "", "", "\\", "", "", ""] -} diff --git a/Base/res/keymaps/trf.json b/Base/res/keymaps/trf.json deleted file mode 100644 index e71f5cc7f92..00000000000 --- a/Base/res/keymaps/trf.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "map": [ "", "\u001B", "1", "2", "3", "4", "5", "6", "7", "8", "9", "0", "/", "-", "\b", "\t", "f", "g", "ğ", "ı", "o", "d", "r", "n", "h", "p", "q", "w", "\n", "", "u", "i", "e", "a", "ü", "t", "k", "m", "l", "y", "ş", "<", "", "x", "j", "ö", "v", "c", "ç", "z", "s", "b", ".", ",", "", "*", "", " ", "", "", "", "", "", "", "", "", "", "", "", "", "", "7", "8", "9", "-", "4", "5", "6", "+", "1", "2", "3", "0", ",", "", "", "<", "", "", ""], - "shift_map": [ "", "\u001B", "!", "\"", "^", "$", "%", "&", "'", "(", ")", "=", "?", "_", "\b", "\t", "F", "G", "Ğ", "I", "O", "D", "R", "N", "H", "P", "Q", "W", "\n", "", "U", "İ", "E", "A", "Ü", "T", "K", "M", "L", "Y", "Ş", ">", "", "X", "J", "Ö", "V", "C", "Ç", "Z", "S", "B", ":", ";", "", "*", "", " ", "", "", "", "", "", "", "", "", "", "", "", "", "", "7", "8", "9", "-", "4", "5", "6", "+", "1", "2", "3", "0", ",", "", "", ">", "", "", ""], - "alt_map": [ "", "\u001B", "¡", "™", "#", "¢", "∞", "+", "{", "[", "]", "}", "/", "|", "\b", "\t", "@", "∑", "´", "", "", "", "", "", "", "", "", "~", "\n", "", "", "", "€", "", "", "₺", "", "", "", "¥", "", "`", "", "*", "", "", "", "", "", "", "", "", "", "", "", "*", "", " ", "", "", "", "", "", "", "", "", "", "", "", "", "", "7", "8", "9", "-", "4", "5", "6", "+", "1", "2", "3", "0", ",", "", "", "|", "", "", ""], - "shift_altgr_map": [ "", "\u001B", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "\n", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", " ", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", ""] -} diff --git a/Base/res/keymaps/trq.json b/Base/res/keymaps/trq.json deleted file mode 100644 index fab7ea5c9e6..00000000000 --- a/Base/res/keymaps/trq.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "map": [ "", "\u001B", "1", "2", "3", "4", "5", "6", "7", "8", "9", "0", "*", "-", "\b", "\t", "q", "w", "e", "r", "t", "y", "u", "ı", "o", "p", "ğ", "ü", "\n", "", "a", "s", "d", "f", "g", "h", "j", "k", "l", "ş", "i", "\"", "", ",", "z", "x", "c", "v", "b", "n", "m", "ö", "ç", ".", "", "*", "", " ", "", "", "", "", "", "", "", "", "", "", "", "", "", "7", "8", "9", "-", "4", "5", "6", "+", "1", "2", "3", "0", ".", "", "", "<", "", "", ""], - "shift_map": [ "", "\u001B", "!", "'", "^", "+", "%", "&", "/", "(", ")", "=", "?", "_", "\b", "\t", "Q", "W", "E", "R", "T", "Y", "U", "I", "O", "P", "Ğ", "Ü", "\n", "", "A", "S", "D", "F", "G", "H", "J", "K", "L", "Ş", "İ", "", "", ";", "Z", "X", "C", "V", "B", "N", "M", "Ö", "Ç", ":", "", "*", "", " ", "", "", "", "", "", "", "", "", "", "", "", "", "", "7", "8", "9", "-", "4", "5", "6", "+", "1", "2", "3", "0", ".", "", "", ">", "", "", ""], - "alt_map": [ "", "\u001B", "", "", "#", "$", "", "", "{", "[", "]", "}", "\\", "|", "\b", "\t", "@", "", "", "", "", "", "", "", "", "", "", "~", "\n", "", "a", "s", "d", "f", "g", "h", "j", "k", "l", "", "", "", "", "`", "z", "x", "c", "v", "b", "n", "m", "", "", "", "", "*", "", " ", "", "", "", "", "", "", "", "", "", "", "", "", "", "7", "8", "9", "-", "4", "5", "6", "+", "1", "2", "3", "0", ".", "", "", "|", "", "", ""] -} diff --git a/Base/res/keymaps/workman.json b/Base/res/keymaps/workman.json deleted file mode 100644 index 86953959074..00000000000 --- a/Base/res/keymaps/workman.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "map": [ "", "\u001B", "1", "2", "3", "4", "5", "6", "7", "8", "9", "0", "-", "=", "\b", "\t", "q", "d", "r", "w", "b", "j", "f", "u", "p", ";", "[", "]", "\n", "", "a", "s", "h", "t", "g", "y", "n", "e", "o", "i", "'", "`", "", "\\", "z", "x", "m", "c", "v", "k", "l", ",", ".", "/", "", "*", "", " ", "", "", "", "", "", "", "", "", "", "", "", "", "", "7", "8", "9", "-", "4", "5", "6", "+", "1", "2", "3", "0", ".", "", "", "\\", "", "", ""], - "shift_map": [ "", "\u001B", "!", "@", "#", "$", "%", "^", "&", "*", "(", ")", "_", "+", "\b", "\t", "Q", "D", "R", "W", "B", "J", "F", "U", "P", ":", "{", "}", "\n", "", "A", "S", "H", "T", "G", "Y", "N", "E", "O", "I", "\"", "~", "", "|", "Z", "X", "M", "C", "V", "K", "L", "<", ">", "?", "", "*", "", " ", "", "", "", "", "", "", "", "", "", "", "", "", "", "7", "8", "9", "-", "4", "5", "6", "+", "1", "2", "3", "0", ".", "", "", "|", "", "", ""], - "alt_map": [ "", "\u001B", "1", "2", "3", "4", "5", "6", "7", "8", "9", "0", "-", "=", "\b", "\t", "q", "d", "r", "w", "b", "j", "f", "u", "p", ";", "[", "]", "\n", "", "a", "s", "h", "t", "g", "y", "n", "e", "o", "i", "'", "`", "", "\\", "z", "x", "m", "c", "v", "k", "l", ",", ".", "/", "", "*", "", " ", "", "", "", "", "", "", "", "", "", "", "", "", "", "7", "8", "9", "-", "4", "5", "6", "+", "1", "2", "3", "0", ".", "", "", "\\", "", "", ""] -} diff --git a/Base/res/themes/Basalt.ini b/Base/res/themes/Basalt.ini deleted file mode 100644 index 29354b3d00c..00000000000 --- a/Base/res/themes/Basalt.ini +++ /dev/null @@ -1,91 +0,0 @@ -[Menu] -Name=&Basalt -[Colors] -Accent=#ff7f00 -DesktopBackground=#171717 -ActiveWindowBorder1=black -ActiveWindowBorder2=#1f1f1f -ActiveWindowTitle=white -ActiveWindowTitleShadow=#000000 -ActiveWindowTitleStripes=#00000000 -InactiveWindowBorder1=#171717 -InactiveWindowBorder2=#1f1f1f -InactiveWindowTitle=#aaaaaa -InactiveWindowTitleShadow=#000000 -InactiveWindowTitleStripes=#00000000 -MovingWindowBorder1=black -MovingWindowBorder2=#2f1f1f -MovingWindowTitle=white -MovingWindowTitleShadow=#000000 -MovingWindowTitleStripes=#00000000 -HighlightWindowBorder1=black -HighlightWindowBorder2=#3f2f0f -HighlightWindowTitle=white -HighlightWindowTitleShadow=#000000 -HighlightWindowTitleStripes=#00000000 -HighlightSearching=#ffff00 -HighlightSearchingText=black -MenuBase=#1f1f1f -MenuBaseText=white -MenuStripe=#171717 -MenuSelection=#ff7f00 -MenuSelectionText=black -Window=#1f1f1f -WindowText=white -Button=#1f1f1f -ButtonText=white -Base=#1f1f1f -BaseText=white -DisabledTextFront=#4f4f4f -DisabledTextBack=#000000 -ThreedHighlight=#2f2f2f -ThreedShadow1=#171717 -ThreedShadow2=#0f0f0f -HoverHighlight=#272727 -Selection=#ff7f00 -SelectionText=black -InactiveSelection=#7f0000 -InactiveSelectionText=black -RubberBandFill=#ff7f002f -RubberBandBorder=#ff7f00 -Link=#88c -ActiveLink=#c88 -VisitedLink=#c8c -PlaceholderText=#808080 -Gutter=#0f0f0f -GutterBorder=#2f2f2f -Ruler=#0f0f0f -RulerBorder=#2f2f2f -RulerActiveText=white -RulerInactiveText=#2f2f2f -TextCursor=#ff4f4f -FocusOutline=#4f4f4f -SyntaxComment=#4fbf4f -SyntaxNumber=white -SyntaxString=#ff8f4f -SyntaxType=#6f8fff -SyntaxPunctuation=white -SyntaxOperator=white -SyntaxKeyword=#cf7fff -SyntaxControlKeyword=orchid -SyntaxIdentifier=white -SyntaxPreprocessorStatement=#ffafff -SyntaxPreprocessorValue=orange -Tooltip=#1f1f1f -TooltipText=white -Tray=#171717 -TrayText=white - -[Alignments] -TitleAlignment=Left - -[Flags] -IsDark=true - -[Metrics] -TitleHeight=24 -TitleButtonWidth=32 -TitleButtonHeight=18 - -[Paths] -ColorScheme=/res/color-schemes/Zenburn.ini diff --git a/Base/res/themes/Chillychilly.ini b/Base/res/themes/Chillychilly.ini deleted file mode 100644 index 214d1bdd14c..00000000000 --- a/Base/res/themes/Chillychilly.ini +++ /dev/null @@ -1,105 +0,0 @@ -[Menu] -Name=Chill&ychilly -[Colors] -DesktopBackground=#3f8077ff -Accent=#509296ff -ActiveWindowBorder1=#3c6e71ff -ActiveWindowBorder2=#509296ff -ActiveWindowTitle=white -ActiveWindowTitleShadow=#3c6e71ff -ActiveWindowTitleStripes=#509296ff -InactiveWindowBorder1=#8c8c8cff -InactiveWindowBorder2=#3c6e71ff -InactiveWindowTitle=#f6f6f6ff -InactiveWindowTitleShadow=#8c8c8cff -InactiveWindowTitleStripes=#8c8c8cff -MovingWindowBorder1=#3c6e71ff -MovingWindowBorder2=#3c6e71ff -MovingWindowTitle=white -MovingWindowTitleShadow=#1a4231 -MovingWindowTitleStripes=#3c6e71ff -HighlightWindowBorder1=#284b63 -HighlightWindowBorder2=#284b63 -HighlightWindowTitle=white -HighlightWindowTitleShadow=#1a4231 -HighlightWindowTitleStripes=#284b63 -HighlightSearching=#ffff00 -HighlightSearchingText=black -MenuBase=#cfdbd5 -MenuBaseText=#0a0a0a -MenuStripe=#cad6d0ff -MenuSelection=#e2ecefff -MenuSelectionText=#0c0c0cff -Window=#cfdbd5 -WindowText=#353535 -Button=#cfdbd5 -ButtonText=#353535 -Base=#fdfdff -BaseText=#353535 -DisabledTextFront=#999999 -DisabledTextBack=white -ThreedHighlight=#e7f5ee -ThreedShadow1=#aeb8b3 -ThreedShadow2=#616663 -HoverHighlight=#e2ece7 -Selection=#3c6e71 -SelectionText=#fdfdff -InactiveSelection=#9e9e9e -InactiveSelectionText=#353535 -PlaceholderText=#999999 -RubberBandFill=#3c6e713c -RubberBandBorder=#284b63 -Link=blue -ActiveLink=red -VisitedLink=magenta -Gutter=#cfdbd5 -GutterBorder=cfdbd5 -Ruler=#d4e0da -RulerBorder=#d4e0da -RulerActiveText=#606060 -RulerInactiveText=#999999 -TextCursor=#153627 -FocusOutline=#909090 -SyntaxComment=#a0a1a7 -SyntaxNumber=#396a8c -SyntaxString=#402afc -SyntaxType=#414d8a -SyntaxPunctuation=black -SyntaxOperator=black -SyntaxKeyword=#515fab -SyntaxControlKeyword=#515fab -SyntaxIdentifier=#000000 -SyntaxPreprocessorStatement=#00a0a0 -SyntaxPreprocessorValue=#2e5b87 -SyntaxFunction=#620ecf -SyntaxVariable=black -SyntaxCustomType=orange -SyntaxNamespace=orange -SyntaxMember=red -SyntaxParameter=#2954b3 -Tooltip=#ffffe1 -TooltipText=#0c1f16 -Tray=#cfdbd5 -TrayText=#353535 - -[Alignments] -TitleAlignment=Left - -[Flags] -IsDark=false - -[Metrics] -BorderThickness=3 -BorderRadius=0 -TitleHeight=22 -TitleButtonWidth=20 -TitleButtonHeight=18 - -[Paths] -ActiveWindowShadow=/res/icons/themes/Chillychilly/frame-shadow-light.png -ColorScheme=/res/color-schemes/Nord.ini -InactiveWindowShadow=/res/icons/themes/Chillychilly/frame-shadow-light.png -MenuShadow=/res/icons/themes/Chillychilly/menu-shadow.png -TaskBarShadow=/res/icons/themes/Chillychilly/frame-shadow-light.png -TooltipShadow=/res/icons/themes/Chillychilly/frame-shadow-light.png -TitleButtonIcons=/res/icons/themes/Chillychilly/16x16/ diff --git a/Base/res/themes/Coffee.ini b/Base/res/themes/Coffee.ini deleted file mode 100644 index ca05e076d03..00000000000 --- a/Base/res/themes/Coffee.ini +++ /dev/null @@ -1,88 +0,0 @@ -[Menu] -Name=&Coffee -[Colors] -Accent=#574dbb -DesktopBackground=#567f9d -ActiveWindowBorder1=#574dbb -ActiveWindowBorder2=#574dbb -ActiveWindowTitle=white -ActiveWindowTitleStripes=#00000000 -InactiveWindowBorder1=#443c92 -InactiveWindowBorder2=#443c92 -InactiveWindowTitle=white -InactiveWindowTitleStripes=#00000000 -MovingWindowBorder1=#574dbb -MovingWindowBorder2=#574dbb -MovingWindowTitle=white -MovingWindowTitleStripes=#00000000 -HighlightWindowBorder1=#b24d7a -HighlightWindowBorder2=#b24d7a -HighlightWindowTitle=white -HighlightWindowTitleStripes=#00000000 -HighlightSearching=#ffff00 -HighlightSearchingText=black -MenuBase=#e6e6e6 -MenuBaseText=black -MenuStripe=#e8e8e8 -MenuSelection=#594fbf -MenuSelectionText=white -Window=#e6e6e6 -WindowText=black -Button=#e6e6e6 -ButtonText=black -Base=white -BaseText=black -DisabledTextFront=#808080 -DisabledTextBack=white -ThreedHighlight=white -ThreedShadow1=#9397a5 -ThreedShadow2=#5d6069 -HoverHighlight=#574dbb3c -Selection=#594fbf -SelectionText=white -InactiveSelection=#888888 -InactiveSelectionText=white -PlaceholderText=#9397a5 -RubberBandFill=#574dbb3c -RubberBandBorder=#594fbf -Link=#0000ff -ActiveLink=#ee0000 -VisitedLink=#551a8b -Gutter=#aeb2c3 -GutterBorder=#5d6069 -Ruler=#aeb2c3 -RulerBorder=#5d6069 -RulerActiveText=#5d6069 -RulerInactiveText=#5d6069 -TextCursor=red -FocusOutline=#594fbf -SyntaxComment=#008000 -SyntaxNumber=#800000 -SyntaxString=#800000 -SyntaxType=#800080 -SyntaxPunctuation=black -SyntaxOperator=black -SyntaxKeyword=black -SyntaxControlKeyword=black -SyntaxIdentifier=#092e64 -SyntaxPreprocessorStatement=#008080 -SyntaxPreprocessorValue=#800000 -Tooltip=#ffffe1 -TooltipText=black -Tray=#9397a5 -TrayText=white - -[Alignments] -TitleAlignment=Left - -[Flags] -IsDark=false -TitleButtonsIconOnly=true - -[Metrics] -TitleButtonWidth=16 -TitleHeight=20 - -[Paths] -ColorScheme=/res/color-schemes/Monokai.ini -TitleButtonIcons=/res/icons/themes/Coffee/16x16/ diff --git a/Base/res/themes/Contrast.ini b/Base/res/themes/Contrast.ini deleted file mode 100644 index 1ff804e3b4a..00000000000 --- a/Base/res/themes/Contrast.ini +++ /dev/null @@ -1,102 +0,0 @@ -[Menu] -Name=Con&trast -[Metrics] -TitleButtonWidth=15 -BorderRadius=0 -TitleButtonHeight=15 -TitleHeight=19 -BorderThickness=4 - -[Flags] -IsDark=true - -[Paths] -ColorScheme=/res/color-schemes/XTerm.ini -TooltipShadow=/res/icons/themes/Default/frame-shadow-light.png -TaskbarShadow= -ActiveWindowShadow=/res/icons/themes/Default/frame-shadow-dark.png -InactiveWindowShadow=/res/icons/themes/Default/frame-shadow-light.png -MenuShadow=/res/icons/themes/Default/frame-shadow-light.png -TitleButtonIcons=/res/icons/themes/Contrast/16x16/ - -[Colors] -SyntaxComment=#6b8e23ff -SyntaxOperator=#ffffffff -DisabledTextBack=#ffffffff -SyntaxControlKeyword=#da70d6ff -ActiveLink=#ffffffff -HoverHighlight=#00f9ffff -MovingWindowTitle=#ffffffff -SelectionText=#000000ff -MenuBaseText=#ffffffff -Tooltip=#ffffe1ff -Ruler=#000000ff -Selection=#00f9ffff -InactiveSelection=#00c7ccff -SyntaxNamespace=#ffa500ff -TooltipText=#000000ff -Button=#000000ff -MenuSelectionText=#000000ff -BaseText=#f3f3f3ff -SyntaxPreprocessorStatement=#00a0a0ff -Accent=#ab6e4aff -Base=#000000ff -InactiveWindowTitle=#d5d0c7ff -MovingWindowTitleShadow=#421405ff -PlaceholderText=#ffffffff -SyntaxParameter=#eb6d1eff -WindowText=#f3f3f3ff -Link=#f7ff00ff -InactiveWindowBorder1=#00002fff -DesktopBackground=#000000ff -TextCursor=#ffffffff -SyntaxMember=#ff0000ff -SyntaxCustomType=#ffa500ff -ThreedShadow2=#000000ff -ThreedShadow1=#ffffffff -InactiveSelectionText=#000000ff -MovingWindowTitleStripes=#4e2771ff -Window=#000000ff -SyntaxKeyword=#00bfffff -HighlightSearchingText=#ffffffff -SyntaxVariable=#000000ff -MovingWindowBorder2=#4e2771ff -GutterBorder=#ffffffff -SyntaxNumber=#adff2fff -Gutter=#000000ff -SyntaxPunctuation=#ffffffff -RulerActiveText=#000000ff -ButtonText=#f7ff00ff -RulerBorder=#f3f3f3ff -ThreedHighlight=#ffffffff -SyntaxFunction=#0000ffff -SyntaxPreprocessorValue=#a00000ff -VisitedLink=#f7ff00ff -SyntaxType=#ee82eeff -Tray=#808080ff -SyntaxString=#ffa500ff -TrayText=#ffffffff -InactiveWindowBorder2=#00002fff -InactiveWindowTitleShadow=#4c4c4cff -HighlightSearching=#00f9ffff -SyntaxIdentifier=#87cefaff -MovingWindowBorder1=#4e2771ff -DisabledTextFront=#808080ff -MenuSelection=#00f9ffff -MenuStripe=#f7ff00ff -RulerInactiveText=#03ff01ff -InactiveWindowTitleStripes=#00002fff -MenuBase=#000000ff -FocusOutline=#00f9ffff -ActiveWindowBorder1=#4e2771ff -ActiveWindowBorder2=#4e2771ff -ActiveWindowTitle=#ffffffff -ActiveWindowTitleShadow=#421405ff -ActiveWindowTitleStripes=#4e2771ff -HighlightWindowBorder1=#614a87ff -HighlightWindowBorder2=#614a87ff -HighlightWindowTitle=#ffffffff -HighlightWindowTitleShadow=#600707ff -HighlightWindowTitleStripes=#614a87ff -RubberBandFill=#12ced33c -RubberBandBorder=#09eaf0ff diff --git a/Base/res/themes/Cupertino.ini b/Base/res/themes/Cupertino.ini deleted file mode 100644 index dfac7c93dbb..00000000000 --- a/Base/res/themes/Cupertino.ini +++ /dev/null @@ -1,95 +0,0 @@ -[Menu] -Name=C&upertino -[Colors] -DesktopBackground=#55bff0 -Accent=#55bff0 -ActiveWindowBorder1=#212121 -ActiveWindowBorder2=#212121 -ActiveWindowTitle=#fcfcfc -ActiveWindowTitleStripes=#363636 -InactiveWindowBorder1=#212121 -InactiveWindowBorder2=#212121 -InactiveWindowTitle=#fcfcfc -InactiveWindowTitleStripes=#212121 -MovingWindowBorder1=#212121 -MovingWindowBorder2=#212121 -MovingWindowTitle=#fcfcfc -MovingWindowTitleStripes=#363636 -HighlightWindowBorder1=#363636 -HighlightWindowBorder2=#363636 -HighlightWindowTitle=white -HighlightWindowTitleStripes=#212121 -HighlightSearching=#ffff00 -HighlightSearchingText=white -MenuBase=#212121 -MenuBaseText=#fcfcfc -MenuStripe=#212121 -MenuSelection=#363636 -MenuSelectionText=#fcfcfc -Window=#292929 -WindowText=#fcfcfc -Button=#212121 -ButtonText=#fcfcfc -Base=#292929 -BaseText=#fcfcfc -DisabledTextFront=#606060 -DisabledTextBack=#000000 -ThreedHighlight=#363636 -ThreedShadow1=#363636 -ThreedShadow2=#363636 -HoverHighlight=#212121 -Selection=#55bff0 -SelectionText=white -InactiveSelection=#606060 -InactiveSelectionText=white -PlaceholderText=#808080 -RubberBandFill=#55bff032 -RubberBandBorder=#1fabeb -Link=#55bff0 -ActiveLink=red -VisitedLink=magenta -Gutter=#363636 -GutterBorder=#363636 -Ruler=#363636 -RulerBorder=#363636 -RulerActiveText=#363636 -RulerInactiveText=#808080 -TextCursor=#fcfcfc -FocusOutline=#909090 -SyntaxComment=#a0a1a7 -SyntaxNumber=#b8ee43 -SyntaxString=#ffad76 -SyntaxType=#ff7684 -SyntaxPunctuation=white -SyntaxOperator=white -SyntaxKeyword=#f379cc -SyntaxControlKeyword=#f379cc -SyntaxIdentifier=#9ddaf6 -SyntaxPreprocessorStatement=#00a093 -SyntaxPreprocessorValue=#ff5252 -Tooltip=#363636 -TooltipText=white -Tray=#212121 -TrayText=#fcfcfc - -[Alignments] -TitleAlignment=Center - -[Flags] -IsDark=true -TitleButtonsIconOnly=true - -[Metrics] -BorderRadius=8 -BorderThickness=4 -TitleButtonWidth=20 -TitleButtonHeight=16 - -[Paths] -ColorScheme=/res/color-schemes/Default.ini -TitleButtonIcons=/res/icons/themes/Cupertino/16x16/ -ActiveWindowShadow= -InactiveWindowShadow= -MenuShadow=/res/icons/themes/Default/frame-shadow-light.png -TaskBarShadow=/res/icons/themes/Default/frame-shadow-light.png -TooltipShadow=/res/icons/themes/Default/frame-shadow-light.png diff --git a/Base/res/themes/Desert.ini b/Base/res/themes/Desert.ini deleted file mode 100644 index db8e8c1bc3f..00000000000 --- a/Base/res/themes/Desert.ini +++ /dev/null @@ -1,91 +0,0 @@ -[Menu] -Name=D&esert -[Colors] -Accent=#84bdaa -DesktopBackground=#a28d68 -ActiveWindowBorder1=#008080 -ActiveWindowBorder2=#84bdaa -ActiveWindowTitle=white -ActiveWindowTitleShadow=#421405 -ActiveWindowTitleStripes=#6e2209 -InactiveWindowBorder1=#a28d68 -InactiveWindowBorder2=#e8d080 -InactiveWindowTitle=#d5d0c7 -InactiveWindowTitleShadow=#4c4c4c -InactiveWindowTitleStripes=#808080 -MovingWindowBorder1=#3aa1a0 -MovingWindowBorder2=#b4efdb -MovingWindowTitle=white -MovingWindowTitleShadow=#601e07 -MovingWindowTitleStripes=#a1320d -HighlightWindowBorder1=#6ed6d6 -HighlightWindowBorder2=#7ca9ce -HighlightWindowTitle=white -HighlightWindowTitleShadow=#600707 -HighlightWindowTitleStripes=#a10d0d -HighlightSearching=#ffff00 -HighlightSearchingText=black -MenuBase=white -MenuBaseText=black -MenuStripe=#bbb7b0 -MenuSelection=#ad714f -MenuSelectionText=white -Window=#d5ccbb -WindowText=black -Button=#d5ccbb -ButtonText=black -Base=white -BaseText=black -DisabledTextFront=#808080 -DisabledTextBack=#e1e8e0 -ThreedHighlight=#eae6dd -ThreedShadow1=#a28d68 -ThreedShadow2=#000000 -HoverHighlight=#e1e8e0 -Selection=#008080 -SelectionText=white -InactiveSelection=#606060 -InactiveSelectionText=white -PlaceholderText=#808080 -RubberBandFill=#f4ca9e3c -RubberBandBorder=#6e2209 -Link=blue -ActiveLink=red -VisitedLink=magenta -Gutter=#d5ccbb -GutterBorder=#404040 -Ruler=#d5ccbb -RulerBorder=#404040 -RulerActiveText=#404040 -RulerInactiveText=#808080 -TextCursor=#6e2209 -FocusOutline=#909090 -SyntaxComment=#008000 -SyntaxNumber=#800000 -SyntaxString=#800000 -SyntaxType=#800080 -SyntaxPunctuation=black -SyntaxOperator=black -SyntaxKeyword=black -SyntaxControlKeyword=black -SyntaxIdentifier=#092e64 -SyntaxPreprocessorStatement=#008080 -SyntaxPreprocessorValue=#800000 -Tooltip=white -TooltipText=black -Tray=#a28d68 -TrayText=white - -[Alignments] -TitleAlignment=Left - -[Flags] -IsDark=false - -[Metrics] -TitleHeight=19 -TitleButtonWidth=15 -TitleButtonHeight=15 - -[Paths] -ColorScheme=/res/color-schemes/Base16 Light.ini diff --git a/Base/res/themes/Durrque.ini b/Base/res/themes/Durrque.ini deleted file mode 100644 index c09562b8699..00000000000 --- a/Base/res/themes/Durrque.ini +++ /dev/null @@ -1,107 +0,0 @@ -[Menu] -Name=Durr&que -[Metrics] -TitleButtonWidth=15 -BorderRadius=0 -TitleButtonHeight=15 -TitleHeight=19 -BorderThickness=4 - -[Alignments] -TitleAlignment=CenterLeft - -[Flags] -IsDark=true -TitleButtonsIconOnly=false - -[Paths] -ColorScheme=/res/color-schemes/Solarized Dark.ini -TooltipShadow=/res/icons/themes/Durrque/frame-shadow-light.png -InactiveWindowShadow=/res/icons/themes/Default/frame-shadow-light.png -ActiveWindowShadow=/res/icons/themes/Durrque/frame-shadow-dark.png -MenuShadow=/res/icons/themes/Durrque/frame-shadow-light.png -TaskbarShadow= -TitleButtonIcons=/res/icons/themes/Durrque/16x16/ - -[Colors] -TrayText=#1f6febff -DisabledTextBack=#8b9493ff -SyntaxOperator=#ffffdfff -RubberBandFill=#21268b3c -MovingWindowTitleShadow=#0d1117ff -SyntaxParameter=#a9d1d9ff -ActiveWindowTitleShadow=#0d1117ff -InactiveWindowTitle=#1f6febff -SyntaxKeyword=#1f6febff -FocusOutline=#b3b3b3ff -Gutter=#21262dff -Link=#0000ffff -Window=#0d1117ff -SyntaxIdentifier=#a9d1d9ff -SyntaxPunctuation=#a9d1d9ff -MenuBase=#0d1117ff -RulerActiveText=#ccccccff -SyntaxVariable=#a9d1d9ff -VisitedLink=#ff00ffff -Tray=#161b22ff -HighlightWindowBorder1=#21268bff -Base=#0d1117ff -HighlightWindowTitle=#ffffdfff -RulerBorder=#404040ff -HighlightSearchingText=#0d1117ff -MovingWindowBorder2=#0d1117ff -ActiveWindowBorder2=#0d1117ff -Ruler=#21262dff -InactiveWindowBorder1=#222222ff -ButtonText=#8b949eff -SyntaxPreprocessorValue=#1f6febff -InactiveSelection=#606060ff -DesktopBackground=#161b22ff -SyntaxNumber=#2d34bfff -HighlightWindowBorder2=#1f6febff -MenuStripe=#161b22ff -MenuBaseText=#a9d1d9ff -ActiveWindowBorder1=#1e4273ff -SyntaxType=#1f6febff -MovingWindowBorder1=#1e4273ff -RulerInactiveText=#808080ff -ActiveWindowTitleStripes=#1e4273ff -ThreedHighlight=#333333ff -Tooltip=#ffffe1ff -MenuSelectionText=#ffffdfff -Accent=#1f6febff -SyntaxCustomType=#e5fa31ff -MenuSelection=#1f6febff -HighlightWindowTitleStripes=#1e4273ff -SyntaxPreprocessorStatement=#00a0a0ff -WindowText=#8b949eff -InactiveWindowBorder2=#0d1117ff -SyntaxControlKeyword=#1f6febff -TextCursor=#126bd6ff -MovingWindowTitleStripes=#0d1117ff -HighlightSearching=#ffff00ff -ThreedShadow2=#333333ff -InactiveWindowTitleShadow=#0d1117ff -PlaceholderText=#808080ff -HighlightWindowTitleShadow=#21268bff -SyntaxFunction=#1f6febff -GutterBorder=#404040ff -SyntaxComment=#8b949eff -InactiveWindowTitleStripes=#222222ff -SelectionText=#ffffdfff -BaseText=#8b949eff -ThreedShadow1=#161b22ff -TooltipText=#8b949eff -InactiveSelectionText=#8b949eff -Button=#0d1117ff -SyntaxMember=#1f6febff -SyntaxString=#5eb55eff -ActiveWindowTitle=#ffffdfff -DisabledTextFront=#808080ff -ActiveLink=#ff0000ff -SyntaxNamespace=#e5fa31ff -Selection=#1f6febff -RubberBandBorder=#1f6febff -MovingWindowTitle=#ffffdfff -HoverHighlight=#1f6febff - diff --git a/Base/res/themes/Faux Pas.ini b/Base/res/themes/Faux Pas.ini deleted file mode 100644 index 15e4ebf7a95..00000000000 --- a/Base/res/themes/Faux Pas.ini +++ /dev/null @@ -1,78 +0,0 @@ -[Menu] -Name=&Faux Pas -[Colors] -Accent=#000000 -DesktopBackground=#505170 -ActiveWindowBorder1=black -ActiveWindowBorder2=black -ActiveWindowTitle=white -InactiveWindowBorder1=#707070 -InactiveWindowBorder2=#707070 -InactiveWindowTitle=black -MovingWindowBorder1=black -MovingWindowBorder2=black -MovingWindowTitle=white -HighlightWindowBorder1=black -HighlightWindowBorder2=black -HighlightWindowTitle=white -HighlightSearching=#7cfc00 -HighlightSearchingText=black -MenuBase=#a0a0a0 -MenuBaseText=black -MenuStripe=#505050 -MenuSelection=white -MenuSelectionText=black -Window=#a0a0a0 -WindowText=black -Button=#a0a0a0 -ButtonText=black -Base=#a0a0a0 -BaseText=black -DisabledTextFront=#707070 -DisabledTextBack=white -ThreedHighlight=white -ThreedShadow1=#282828 -ThreedShadow2=#282828 -HoverHighlight=white -Selection=white -SelectionText=black -InactiveSelection=white -InactiveSelectionText=black -PlaceholderText=#505050 -RubberBandFill=#9f9f9f6c -RubberBandBorder=black -Link=#0000b0 -ActiveLink=#2020d0 -VisitedLink=#2000b0 -Gutter=#808080 -GutterBorder=black -Ruler=#808080 -RulerBorder=black -RulerActiveText=#404040 -RulerInactiveText=#606060 -TextCursor=black -FocusOutline=#909090 -SyntaxComment=#008000 -SyntaxNumber=#800000 -SyntaxString=#800000 -SyntaxType=#800080 -SyntaxPunctuation=black -SyntaxOperator=black -SyntaxKeyword=black -SyntaxControlKeyword=black -SyntaxIdentifier=#092e64 -SyntaxPreprocessorStatement=#008080 -SyntaxPreprocessorValue=#800000 -Tooltip=#ffffe1 -TooltipText=black -Tray=#282828 -TrayText=white - -[Alignments] -TitleAlignment=Left - -[Flags] -IsDark=false - -[Paths] -ColorScheme=/res/color-schemes/Nord.ini diff --git a/Base/res/themes/Gruvbox Dark.ini b/Base/res/themes/Gruvbox Dark.ini deleted file mode 100644 index fca1d641e7f..00000000000 --- a/Base/res/themes/Gruvbox Dark.ini +++ /dev/null @@ -1,91 +0,0 @@ -[Menu] -Name=&Gruvbox Dark -[Colors] -Accent=#79740e -DesktopBackground=#282828 -ActiveWindowBorder1=#79740e -ActiveWindowBorder2=#32302f -ActiveWindowTitle=#ebdbb2 -InactiveWindowBorder1=#665c54 -InactiveWindowBorder2=#282828 -InactiveWindowTitle=#bdae93 -MovingWindowBorder1=#98971a -MovingWindowBorder2=#32302f -MovingWindowTitle=#ebdbb2 -HighlightWindowBorder1=#823517 -HighlightWindowBorder2=#383635 -HighlightWindowTitle=#ebdbb2 -HighlightSearching=#ebdbb2 -HighlightSearchingText=#282828 -MenuBase=#32302f -MenuBaseText=#ebdbb2 -MenuStripe=#282828 -MenuSelection=#98971a -MenuSelectionText=#282828 -Window=#32302f -WindowText=#ebdbb2 -Button=#32302f -ButtonText=#ebdbb2 -Base=#3c3836 -BaseText=#ebdbb2 -DisabledTextFront=#665c54 -DisabledTextBack=#1d2021 -ThreedHighlight=#504945 -ThreedShadow1=#282828 -ThreedShadow2=#1d2021 -HoverHighlight=#3c3836 -Selection=#98971a -SelectionText=#282828 -InactiveSelection=#665c54 -InactiveSelectionText=#ebdbb2 -RubberBandFill=#98971a3c -RubberBandBorder=#282828 -Link=#83a598 -ActiveLink=#fabd2f -VisitedLink=#d3869b -PlaceholderText=#bdae93 -Gutter=#32302f -GutterBorder=#665c54 -Ruler=#32302f -RulerBorder=#665c54 -RulerActiveText=#ebdbb2 -RulerInactiveText=#bdae93 -TextCursor=#79740e -FocusOutline=#98971a -SyntaxComment=#79740e -SyntaxNumber=#b8bb26 -SyntaxString=#fabd2f -SyntaxType=#d3869b -SyntaxPunctuation=#ebdbb2 -SyntaxOperator=#ebdbb2 -SyntaxKeyword=#689d6a -SyntaxControlKeyword=#b16286 -SyntaxIdentifier=#83a598 -SyntaxPreprocessorStatement=#a89984 -SyntaxPreprocessorValue=#fabd2f -Tooltip=#504945 -TooltipText=#ebdbb2 -Tray=#32302f -TrayText=#ebdbb2 - -[Alignments] -TitleAlignment=Left - -[Flags] -IsDark=true - -[Metrics] -BorderThickness=4 -BorderRadius=0 -TitleHeight=19 -TitleButtonWidth=15 -TitleButtonHeight=15 - -[Paths] -TitleButtonIcons=/res/icons/themes/Gruvbox-Dark/16x16/ -ActiveWindowShadow=/res/icons/themes/Default/frame-shadow-dark.png -InactiveWindowShadow=/res/icons/themes/Default/frame-shadow-light.png -MenuShadow=/res/icons/themes/Default/frame-shadow-light.png -TaskBarShadow=/res/icons/themes/Default/frame-shadow-light.png -TooltipShadow=/res/icons/themes/Default/frame-shadow-light.png -ColorScheme=/res/color-schemes/Gruvbox Dark.ini diff --git a/Base/res/themes/Light.ini b/Base/res/themes/Light.ini deleted file mode 100644 index 45b9c6539d4..00000000000 --- a/Base/res/themes/Light.ini +++ /dev/null @@ -1,92 +0,0 @@ -[Menu] -Name=&Light -[Colors] -Accent=#ffffff -DesktopBackground=#0f0f0f -ActiveWindowBorder1=#e3e3e3 -ActiveWindowBorder2=#e3e3e3 -ActiveWindowTitle=#6b6b6b -ActiveWindowTitleShadow=#ffffff -ActiveWindowTitleStripes=#00000000 -InactiveWindowBorder1=#d9d9d9 -InactiveWindowBorder2=#d9d9d9 -InactiveWindowTitle=#ababab -InactiveWindowTitleShadow=#ffffff -InactiveWindowTitleStripes=#00000000 -MovingWindowBorder1=#e3e3e3 -MovingWindowBorder2=#e3e3e3 -MovingWindowTitle=#6b6b6b -MovingWindowTitleShadow=#ffffff -MovingWindowTitleStripes=#00000000 -HighlightWindowBorder1=#e3e3e3 -HighlightWindowBorder2=#e3e3e3 -HighlightWindowTitle=black -HighlightWindowTitleShadow=#ffffff -HighlightWindowTitleStripes=#00000000 -HighlightSearching=#598dc6 -HighlightSearchingText=white -MenuBase=#e3e3e3 -MenuBaseText=black -MenuStripe=#d9d9d9 -MenuSelection=white -MenuSelectionText=black -Window=#e3e3e3 -WindowText=#4b4b4b -Button=#e3e3e3 -ButtonText=#4b4b4b -Base=white -BaseText=#4b4b4b -DisabledTextFront=#ababab -DisabledTextBack=white -ThreedHighlight=#e3e3e3 -ThreedShadow1=#ababab -ThreedShadow2=#6b6b6b -HoverHighlight=white -Selection=#598dc6 -SelectionText=white -InactiveSelection=#606060ff -InactiveSelectionText=white -PlaceholderText=#808080 -RubberBandFill=#598dc66c -RubberBandBorder=#598dc6 -Link=blue -ActiveLink=red -VisitedLink=magenta -Gutter=#d4d0c8 -GutterBorder=#404040 -Ruler=#d4d0c8 -RulerBorder=#404040 -RulerActiveText=#404040 -RulerInactiveText=#808080 -TextCursor=black -FocusOutline=#909090 -SyntaxComment=#008000 -SyntaxNumber=#800000 -SyntaxString=#800000 -SyntaxType=#800080 -SyntaxPunctuation=black -SyntaxOperator=black -SyntaxKeyword=black -SyntaxControlKeyword=black -SyntaxIdentifier=#092e64 -SyntaxPreprocessorStatement=#008080 -SyntaxPreprocessorValue=#800000 -Tooltip=#fbeaa0 -TooltipText=#4b4b4b -Tray=#3b3b3b -TrayText=white - -[Alignments] -TitleAlignment=Left - -[Flags] -IsDark=false -TitleButtonsIconOnly=true - -[Paths] -ActiveWindowShadow=/res/icons/themes/Default/frame-shadow-dark.png -ColorScheme=/res/color-schemes/Octopus Cat.ini -InactiveWindowShadow=/res/icons/themes/Default/frame-shadow-light.png -TitleButtonIcons=/res/icons/themes/Light/16x16/ -MenuShadow=/res/icons/themes/Redmond/menu-shadow.png -TooltipShadow=/res/icons/themes/Redmond/menu-shadow.png diff --git a/Base/res/themes/Nord.ini b/Base/res/themes/Nord.ini deleted file mode 100644 index ae0bb37a287..00000000000 --- a/Base/res/themes/Nord.ini +++ /dev/null @@ -1,78 +0,0 @@ -[Menu] -Name=&Nord -[Colors] -Accent=#4c566a -DesktopBackground=#3b4252 -ActiveWindowBorder1=#4c566a -ActiveWindowBorder2=#434c5e -ActiveWindowTitle=white -InactiveWindowBorder1=#2e3440 -InactiveWindowBorder2=#3b4252 -InactiveWindowTitle=white -MovingWindowBorder1=#4c566a -MovingWindowBorder2=#4c566a -MovingWindowTitle=white -HighlightWindowBorder1=#4c566a -HighlightWindowBorder2=#4c566a -HighlightWindowTitle=white -HighlightSearching=#ffff00 -HighlightSearchingText=black -MenuBase=#434c5e -MenuBaseText=white -MenuStripe=#4c566a -MenuSelection=#4c566a -MenuSelectionText=white -Window=#2e3440 -WindowText=white -Button=#434c5e -ButtonText=white -Base=#4c566a -BaseText=white -DisabledTextFront=#95adc5 -DisabledTextBack=#2e3440 -ThreedHighlight=#4c566a -ThreedShadow1=#3b4252 -ThreedShadow2=#2e3440 -HoverHighlight=#7e9dbc -Selection=#7e9dbc -SelectionText=white -InactiveSelection=#3b4252 -InactiveSelectionText=white -PlaceholderText=#d8dee9 -RubberBandFill=#04434c5e -RubberBandBorder=#4c566a -Link=#7e9dbc -ActiveLink=#95adc5 -VisitedLink=#3b4e68 -Gutter=#434c5e -GutterBorder=#3b4252 -Ruler=#434c5e -RulerBorder=#3b4252 -RulerActiveText=#95adc5 -RulerInactiveText=#7e9dbc -TextCursor=#7e9dbc -FocusOutline=#7e9dbc -SyntaxComment=#4fbf4f -SyntaxNumber=white -SyntaxString=#ff8f4f -SyntaxType=#6f8fff -SyntaxPunctuation=white -SyntaxOperator=white -SyntaxKeyword=#cf7fff -SyntaxControlKeyword=orchid -SyntaxIdentifier=white -SyntaxPreprocessorStatement=#ffafff -SyntaxPreprocessorValue=orange -Tooltip=#4c566a -TooltipText=white -Tray=#3b4252 -TrayText=white - -[Alignments] -TitleAlignment=Left - -[Flags] -IsDark=true - -[Paths] -ColorScheme=/res/color-schemes/Nord.ini diff --git a/Base/res/themes/Olive.ini b/Base/res/themes/Olive.ini deleted file mode 100644 index e3951786025..00000000000 --- a/Base/res/themes/Olive.ini +++ /dev/null @@ -1,97 +0,0 @@ -[Menu] -Name=&Olive -[Metrics] -TitleButtonWidth=15 -BorderRadius=0 -TitleButtonHeight=15 -TitleHeight=19 -BorderThickness=4 - -[Flags] -IsDark=false - -[Paths] -ColorScheme=/res/color-schemes/Solarized Light.ini -TooltipShadow=/res/icons/themes/Default/frame-shadow-light.png -TaskbarShadow= -ActiveWindowShadow=/res/icons/themes/Default/frame-shadow-dark.png -InactiveWindowShadow=/res/icons/themes/Default/frame-shadow-light.png -MenuShadow=/res/icons/themes/Default/frame-shadow-light.png -TitleButtonIcons=/res/icons/16x16/ - -[Colors] -SyntaxComment=#a0a1a7ff -RubberBandBorder=#8bd121ff -SyntaxOperator=#000000ff -SyntaxControlKeyword=#a42ea2ff -ActiveLink=#ff0000ff -HoverHighlight=#bec9b1ff -MovingWindowTitle=#ffffffff -MenuBaseText=#000000ff -Tooltip=#e2ffe1ff -ActiveWindowTitleShadow=#421405ff -Ruler=#c6cbc0ff -Selection=#619910ff -InactiveSelection=#606060ff -ActiveWindowTitleStripes=#49750bff -TooltipText=#000000ff -Button=#c1cbb5ff -MenuSelectionText=#000000ff -SelectionText=#ffffffff -BaseText=#000000ff -DisabledTextFront=#747d74ff -DisabledTextBack=#e3ffb9ff -SyntaxPreprocessorStatement=#00a0a0ff -Accent=#619910ff -Base=#ffffffff -InactiveWindowTitle=#d5d0c7ff -HighlightWindowTitle=#ffffffff -MovingWindowTitleShadow=#601e07ff -PlaceholderText=#747d74ff -WindowText=#000000ff -Link=#8bd121ff -InactiveWindowBorder1=#79856eff -DesktopBackground=#85956eff -TextCursor=#619910ff -Window=#c1cbb5ff -ThreedShadow2=#404040ff -ThreedShadow1=#808080ff -InactiveSelectionText=#ffffffff -RubberBandFill=#6199103c -MovingWindowTitleStripes=#9fe13aff -HighlightWindowBorder2=#9fe13aff -SyntaxKeyword=#a42ea2ff -HighlightSearchingText=#000000ff -HighlightWindowTitleShadow=#600707ff -ActiveWindowBorder2=#e3ffb9ff -MovingWindowBorder2=#619910ff -GutterBorder=#404040ff -SyntaxNumber=#976715ff -Gutter=#c1cbb5ff -SyntaxPunctuation=#000000ff -RulerActiveText=#404040ff -ButtonText=#000000ff -RulerBorder=#404040ff -ThreedHighlight=#ffffffff -ActiveWindowTitle=#ffffffff -SyntaxPreprocessorValue=#a00000ff -SyntaxType=#a000a0ff -Tray=#808080ff -VisitedLink=#ff00ffff -TrayText=#ffffffff -InactiveWindowBorder2=#afccabff -InactiveWindowTitleShadow=#4c4c4cff -HighlightSearching=#ffff00ff -SyntaxIdentifier=#000000ff -HighlightWindowBorder1=#48730bff -MovingWindowBorder1=#619910ff -ActiveWindowBorder1=#619910ff -SyntaxString=#53a053ff -MenuSelection=#b1d09cff -MenuStripe=#c9d3bfff -RulerInactiveText=#808080ff -InactiveWindowTitleStripes=#808080ff -MenuBase=#ffffffff -FocusOutline=#909090ff -HighlightWindowTitleStripes=#4d7a0bff - diff --git a/Base/res/themes/Plum.ini b/Base/res/themes/Plum.ini deleted file mode 100644 index 2b9535d7269..00000000000 --- a/Base/res/themes/Plum.ini +++ /dev/null @@ -1,96 +0,0 @@ -[Menu] -Name=&Plum -[Colors] -Accent=#a084b8 -DesktopBackground=#402840 -ActiveWindowBorder1=#484060 -ActiveWindowBorder2=#a084b8 -ActiveWindowTitle=white -ActiveWindowTitleShadow=#black -ActiveWindowTitleStripes=#302b40 -InactiveWindowBorder1=#786058 -InactiveWindowBorder2=#a89878 -InactiveWindowTitle=#cbc6cf -InactiveWindowTitleShadow=#black -InactiveWindowTitleStripes=#5b4943 -MovingWindowBorder1=#5f547d -MovingWindowBorder2=#b9a5ca -MovingWindowTitle=white -MovingWindowTitleShadow=#330760 -MovingWindowTitleStripes=#473f5f -HighlightWindowBorder1=#7f6db5 -HighlightWindowBorder2=#bba9f1 -HighlightWindowTitle=#ffffff -HighlightWindowTitleShadow=#black -HighlightWindowTitleStripes=#5b4943 -HighlightSearching=#ffff00 -HighlightSearchingText=black -MenuBase=#a89890 -MenuBaseText=black -MenuStripe=#75645cFF -MenuSelection=#008080 -MenuSelectionText=black -Window=#a89890 -WindowText=black -Button=#a89890 -ButtonText=black -Base=#d8d0c8 -BaseText=black -DisabledTextFront=#808080 -DisabledTextBack=#d8d0c8 -ThreedHighlight=#d8d0c8 -ThreedShadow1=#786058 -ThreedShadow2=#201a18 -HoverHighlight=#b3a59e -Selection=#008080 -SelectionText=white -InactiveSelection=#606060 -InactiveSelectionText=white -PlaceholderText=#808080 -RubberBandFill=#e39ef43c -RubberBandBorder=#50096e -Link=blue -ActiveLink=red -VisitedLink=magenta -Gutter=#c2afa6 -GutterBorder=#404040 -Ruler=#c2afa6 -RulerBorder=#404040 -RulerActiveText=#404040 -RulerInactiveText=#808080 -TextCursor=#4c096e -FocusOutline=#909090 -SyntaxComment=#008000 -SyntaxNumber=#800000 -SyntaxString=#800000 -SyntaxType=#800080 -SyntaxPunctuation=black -SyntaxOperator=black -SyntaxKeyword=black -SyntaxControlKeyword=black -SyntaxIdentifier=#092e64 -SyntaxPreprocessorStatement=#008080 -SyntaxPreprocessorValue=#800000 -Tooltip=#ffffe1 -TooltipText=black -Tray=#808080 -TrayText=#ffffff - -[Alignments] -TitleAlignment=Left - -[Flags] -IsDark=false - -[Metrics] -TitleHeight=19 -TitleButtonWidth=15 -TitleButtonHeight=15 - -[Paths] -ActiveWindowShadow=/res/icons/themes/Default/frame-shadow-dark.png -ColorScheme=/res/color-schemes/C64.ini -InactiveWindowShadow=/res/icons/themes/Default/frame-shadow-light.png -MenuShadow=/res/icons/themes/Default/frame-shadow-light.png -TaskBarShadow=/res/icons/themes/Default/frame-shadow-light.png -TooltipShadow=/res/icons/themes/Default/frame-shadow-light.png diff --git a/Base/res/themes/Pumpkin.ini b/Base/res/themes/Pumpkin.ini deleted file mode 100644 index 0d5df026192..00000000000 --- a/Base/res/themes/Pumpkin.ini +++ /dev/null @@ -1,97 +0,0 @@ -[Menu] -Name=Pumpk&in -[Metrics] -TitleButtonWidth=15 -BorderRadius=0 -TitleButtonHeight=15 -TitleHeight=19 -BorderThickness=4 - -[Flags] -IsDark=false - -[Paths] -ColorScheme=/res/color-schemes/Base16 Light.ini -TooltipShadow=/res/icons/themes/Default/frame-shadow-light.png -TaskbarShadow= -ActiveWindowShadow=/res/icons/themes/Default/frame-shadow-dark.png -InactiveWindowShadow=/res/icons/themes/Default/frame-shadow-light.png -MenuShadow=/res/icons/themes/Default/frame-shadow-light.png -TitleButtonIcons=/res/icons/16x16/ - -[Colors] -SyntaxComment=#a0a1a7ff -RubberBandBorder=#cfaf20ff -SyntaxOperator=#000000ff -SyntaxControlKeyword=#a42ea2ff -ActiveLink=#ff0000ff -HoverHighlight=#e4d6beff -MovingWindowTitle=#e1ba70ff -MenuBaseText=#000000ff -Tooltip=#c8b0caff -ActiveWindowTitleShadow=#f0b143ff -Ruler=#c6cbc0ff -Selection=#6f517bff -InactiveSelection=#606060ff -ActiveWindowTitleStripes=#bf600cff -TooltipText=#000000ff -Button=#c7c2b0ff -MenuSelectionText=#f3f3f3ff -SelectionText=#ffffffff -BaseText=#000000ff -DisabledTextFront=#808080 -DisabledTextBack=#e4d6beff -SyntaxPreprocessorStatement=#00a0a0ff -Accent=#6f517bff -Base=#ffffffff -InactiveWindowTitle=#f0b143ff -HighlightWindowTitle=#ffffffff -MovingWindowTitleShadow=#601e07ff -PlaceholderText=#747d74ff -WindowText=#000000ff -Link=#6f517bff -InactiveWindowBorder1=#bf600cff -DesktopBackground=#bf600cff -TextCursor=#573666ff -Window=#c7c6b0ff -ThreedShadow2=#404040ff -ThreedShadow1=#808080ff -InactiveSelectionText=#ffffffff -RubberBandFill=#978c0f3c -MovingWindowTitleStripes=#e0ab39ff -HighlightWindowBorder2=#e9d51aff -SyntaxKeyword=#a42ea2ff -HighlightSearchingText=#000000ff -HighlightWindowTitleShadow=#bf600cff -ActiveWindowBorder2=#f0b143ff -MovingWindowBorder2=#b77f19ff -GutterBorder=#404040ff -SyntaxNumber=#976715ff -Gutter=#cac6b4ff -SyntaxPunctuation=#000000ff -RulerActiveText=#404040ff -ButtonText=#000000ff -RulerBorder=#404040ff -ThreedHighlight=#ffffffff -ActiveWindowTitle=#000000ff -SyntaxPreprocessorValue=#a00000ff -SyntaxType=#a000a0ff -Tray=#808080ff -VisitedLink=#ff00ffff -TrayText=#ffffffff -InactiveWindowBorder2=#875b09ff -InactiveWindowTitleShadow=#4c4c4cff -HighlightSearching=#ffff00ff -SyntaxIdentifier=#000000ff -HighlightWindowBorder1=#e9d51aff -MovingWindowBorder1=#875b09ff -ActiveWindowBorder1=#e9d51aff -SyntaxString=#53a053ff -MenuSelection=#573666ff -MenuStripe=#d4c7d5ff -RulerInactiveText=#808080ff -InactiveWindowTitleStripes=#ce7526ff -MenuBase=#ffffffff -FocusOutline=#909090ff -HighlightWindowTitleStripes=#bf600cff - diff --git a/Base/res/themes/Redmond 2000.ini b/Base/res/themes/Redmond 2000.ini deleted file mode 100644 index 8f6e3b70603..00000000000 --- a/Base/res/themes/Redmond 2000.ini +++ /dev/null @@ -1,90 +0,0 @@ -[Menu] -Name=Redmond &2000 -[Colors] -Accent=#4a6eab -DesktopBackground=#3a6ea5 -ActiveWindowBorder1=#09226e -ActiveWindowBorder2=#9ecaf4 -ActiveWindowTitle=white -ActiveWindowTitleStripes=#00000000 -InactiveWindowBorder1=#808080 -InactiveWindowBorder2=#c0c0c0 -InactiveWindowTitle=#d5d0c7 -InactiveWindowTitleStripes=#00000000 -MovingWindowBorder1=#0d32a1 -MovingWindowBorder2=#bbdcfa -MovingWindowTitle=white -MovingWindowTitleStripes=#00000000 -HighlightWindowBorder1=#0d0da1 -HighlightWindowBorder2=#bbbbfa -HighlightWindowTitle=white -HighlightWindowTitleStripes=#00000000 -HighlightSearching=#ffff00 -HighlightSearchingText=black -MenuBase=white -MenuBaseText=black -MenuStripe=#dbd8d1 -MenuSelection=#b6c0d2 -MenuSelectionText=black -Window=#d4d0c8 -WindowText=black -Button=#d4d0c8 -ButtonText=black -Base=white -BaseText=black -DisabledTextFront=#808080 -DisabledTextBack=white -ThreedHighlight=white -ThreedShadow1=#808080 -ThreedShadow2=#404040 -HoverHighlight=#e3dfdb -Selection=#1a3584 -SelectionText=white -InactiveSelection=#606060 -InactiveSelectionText=white -PlaceholderText=#808080 -RubberBandFill=#9ecaf43c -RubberBandBorder=#09226e -Link=blue -ActiveLink=red -VisitedLink=magenta -Gutter=#d4d0c8 -GutterBorder=#404040 -Ruler=#d4d0c8 -RulerBorder=#404040 -RulerActiveText=#404040 -RulerInactiveText=#808080 -TextCursor=black -FocusOutline=#909090 -SyntaxComment=#008000 -SyntaxNumber=#800000 -SyntaxString=#800000 -SyntaxType=#800080 -SyntaxPunctuation=black -SyntaxOperator=black -SyntaxKeyword=black -SyntaxControlKeyword=black -SyntaxIdentifier=#092e64 -SyntaxPreprocessorStatement=#008080 -SyntaxPreprocessorValue=#800000 -Tooltip=#ffffe1 -TooltipText=black -Tray=#808080 -TrayText=white - -[Alignments] -TitleAlignment=Left - -[Flags] -IsDark=false - -[Metrics] -TitleButtonWidth=16 -TitleButtonHeight=14 -TitleHeight=18 - -[Paths] -ColorScheme=/res/color-schemes/Default.ini -MenuShadow=/res/icons/themes/Redmond/menu-shadow.png -TitleButtonIcons=/res/icons/themes/Redmond/16x16/ -TooltipShadow=/res/icons/themes/Redmond/menu-shadow.png diff --git a/Base/res/themes/Redmond.ini b/Base/res/themes/Redmond.ini deleted file mode 100644 index c1cc82b9486..00000000000 --- a/Base/res/themes/Redmond.ini +++ /dev/null @@ -1,88 +0,0 @@ -[Menu] -Name=&Redmond -[Colors] -Accent=#0000ab -DesktopBackground=#008080 -ActiveWindowBorder1=#00007f -ActiveWindowBorder2=#00007f -ActiveWindowTitle=white -ActiveWindowTitleStripes=#00000000 -InactiveWindowBorder1=#7f787f -InactiveWindowBorder2=#7f787f -InactiveWindowTitle=#bfb8bf -InactiveWindowTitleStripes=#00000000 -MovingWindowBorder1=#0000aa -MovingWindowBorder2=#0000aa -MovingWindowTitle=white -MovingWindowTitleStripes=#00000000 -HighlightWindowBorder1=#a10d0d -HighlightWindowBorder2=#a10d0d -HighlightWindowTitle=white -HighlightWindowTitleStripes=#00000000 -HighlightSearching=#ffff00 -HighlightSearchingText=black -MenuBase=#bfb8bf -MenuBaseText=black -MenuStripe=#bfb8bf -MenuSelection=#0000aa -MenuSelectionText=white -Window=#bfb8bf -WindowText=black -Button=#bfb8bf -ButtonText=black -Base=white -BaseText=black -DisabledTextFront=#7f787f -DisabledTextBack=#fff8ff -ThreedHighlight=#fff8ff -ThreedShadow1=#7f787f -ThreedShadow2=#000000 -HoverHighlight=#dbdbdb -Selection=#0000aa -SelectionText=white -InactiveSelection=#606060 -InactiveSelectionText=white -PlaceholderText=#7f787f -RubberBandFill=#8080803c -RubberBandBorder=black -Link=blue -ActiveLink=red -VisitedLink=magenta -Gutter=#d4d0c8 -GutterBorder=#404040 -Ruler=#d4d0c8 -RulerBorder=#404040 -RulerActiveText=#404040 -RulerInactiveText=#808080 -TextCursor=black -FocusOutline=#909090 -SyntaxComment=#008000 -SyntaxNumber=#800000 -SyntaxString=#800000 -SyntaxType=#800080 -SyntaxPunctuation=black -SyntaxOperator=black -SyntaxKeyword=black -SyntaxControlKeyword=black -SyntaxIdentifier=#092e64 -SyntaxPreprocessorStatement=#008080 -SyntaxPreprocessorValue=#800000 -Tooltip=#ffffe1 -TooltipText=black -Tray=#808080 -TrayText=white - -[Alignments] -TitleAlignment=Left - -[Flags] -IsDark=false - -[Metrics] -TitleButtonWidth=16 -TitleButtonHeight=14 -TitleHeight=18 - -[Paths] -ColorScheme=/res/color-schemes/Default.ini -TitleButtonIcons=/res/icons/themes/Redmond/16x16/ diff --git a/Base/res/themes/Scarlett.ini b/Base/res/themes/Scarlett.ini deleted file mode 100644 index 9f3e6591af9..00000000000 --- a/Base/res/themes/Scarlett.ini +++ /dev/null @@ -1,97 +0,0 @@ -[Menu] -Name=&Scarlett -[Metrics] -TitleButtonWidth=15 -BorderRadius=0 -TitleButtonHeight=15 -TitleHeight=19 -BorderThickness=4 - -[Flags] -IsDark=false - -[Paths] -ColorScheme=/res/color-schemes/Monokai.ini -TooltipShadow=/res/icons/themes/Default/frame-shadow-light.png -TaskbarShadow= -ActiveWindowShadow=/res/icons/themes/Default/frame-shadow-dark.png -InactiveWindowShadow=/res/icons/themes/Default/frame-shadow-light.png -MenuShadow=/res/icons/themes/Default/frame-shadow-light.png -TitleButtonIcons=/res/icons/16x16/ - -[Colors] -SyntaxComment=#a0a1a7ff -RubberBandBorder=#ba1e09ff -SyntaxOperator=#000000ff -SyntaxControlKeyword=#a42ea2ff -ActiveLink=#ff0000ff -HoverHighlight=#c8b0b0ff -MovingWindowTitle=#ffffffff -MenuBaseText=#000000ff -Tooltip=#ffe1e1ff -ActiveWindowTitleShadow=#421405ff -Ruler=#c6cbc0ff -Selection=#970e0eff -InactiveSelection=#606060ff -ActiveWindowTitleStripes=#730808ff -TooltipText=#000000ff -Button=#c7b0b0ff -MenuSelectionText=#000000ff -SelectionText=#ffffffff -BaseText=#000000ff -SyntaxPreprocessorStatement=#00a0a0ff -Accent=#970e0eff -Base=#ffffffff -DisabledTextFront=#808080ff -DisabledTextBack=#ffe1e1ff -InactiveWindowTitle=#d5c7c7ff -HighlightWindowTitle=#ffffffff -MovingWindowTitleShadow=#601e07ff -PlaceholderText=#747d74ff -WindowText=#000000ff -Link=#d02521ff -InactiveWindowBorder1=#856e6eff -DesktopBackground=#956e6eff -TextCursor=#619910ff -Window=#c9b3b3ff -ThreedShadow2=#404040ff -ThreedShadow1=#808080ff -InactiveSelectionText=#ffffffff -RubberBandFill=#9616053c -MovingWindowTitleStripes=#e03d39ff -HighlightWindowBorder2=#df3737ff -SyntaxKeyword=#a42ea2ff -HighlightSearchingText=#000000ff -HighlightWindowTitleShadow=#600707ff -ActiveWindowBorder2=#ffb7b7ff -MovingWindowBorder2=#970e0eff -GutterBorder=#404040ff -SyntaxNumber=#976715ff -Gutter=#c7b0b0ff -SyntaxPunctuation=#000000ff -RulerActiveText=#404040ff -ButtonText=#000000ff -RulerBorder=#404040ff -ThreedHighlight=#ffffffff -ActiveWindowTitle=#ffffffff -SyntaxPreprocessorValue=#a00000ff -SyntaxType=#a000a0ff -Tray=#808080ff -VisitedLink=#ff00ffff -TrayText=#ffffffff -InactiveWindowBorder2=#ccababff -InactiveWindowTitleShadow=#4c4c4cff -HighlightSearching=#ffff00ff -SyntaxIdentifier=#000000ff -HighlightWindowBorder1=#730b0bff -MovingWindowBorder1=#970e0eff -ActiveWindowBorder1=#970e0eff -SyntaxString=#53a053ff -MenuSelection=#cf9b9bff -MenuStripe=#d2bebeff -RulerInactiveText=#808080ff -InactiveWindowTitleStripes=#808080ff -MenuBase=#ffffffff -FocusOutline=#909090ff -HighlightWindowTitleStripes=#7a0606ff - diff --git a/Base/res/themes/Silver.ini b/Base/res/themes/Silver.ini deleted file mode 100644 index 80a44d4f836..00000000000 --- a/Base/res/themes/Silver.ini +++ /dev/null @@ -1,99 +0,0 @@ -[Menu] -Name=Sil&ver -[Metrics] -TitleButtonWidth=15 -BorderRadius=0 -TitleButtonHeight=15 -TitleHeight=19 -BorderThickness=4 - -[Flags] -IsDark=false - -[Alignments] -TitleAlignment=Center - -[Paths] -ColorScheme=/res/color-schemes/Default.ini -TooltipShadow= -TaskbarShadow= -ActiveWindowShadow= -InactiveWindowShadow= -MenuShadow= -TitleButtonIcons=/res/icons/themes/Silver/16x16/ - -[Colors] -DesktopBackground=#63639cff -Accent=#63639cff -ActiveWindowBorder1=#cececeff -ActiveWindowBorder2=#cececeff -ActiveWindowTitle=#000000ff -ActiveWindowTitleShadow=#babdb6ff -ActiveWindowTitleStripes=#363636ff -InactiveWindowBorder1=#ddddddff -InactiveWindowBorder2=#ddddddff -InactiveWindowTitle=#696969ff -InactiveWindowTitleShadow=#d3d7cfff -InactiveWindowTitleStripes=#808080ff -MovingWindowBorder1=#cececeff -MovingWindowBorder2=#9a9a9aff -MovingWindowTitle=#000000ff -MovingWindowTitleShadow=#8d8d8dff -MovingWindowTitleStripes=#000000ff -HighlightWindowBorder1=#cacacaff -HighlightWindowBorder2=#9a9a9aff -HighlightWindowTitle=#232342ff -HighlightWindowTitleShadow=#f3f3f3ff -HighlightWindowTitleStripes=#4e4e7dff -HighlightSearching=#7cfc00ff -HighlightSearchingText=#000000ff -MenuBase=#dededeff -MenuBaseText=#000000ff -MenuStripe=#dededeff -MenuSelection=#31319cff -MenuSelectionText=#ffffffff -Window=#ddddddff -WindowText=#2f3436ff -Button=#ddddddff -ButtonText=#000000ff -Base=#f3f3f3ff -BaseText=#000000ff -DisabledTextFront=#808080ff -DisabledTextBack=white -ThreedHighlight=#7f7f7fff -ThreedShadow1=#3b3b3bff -ThreedShadow2=#3b3b3bff -HoverHighlight=#babdb6ff -Selection=#63639cff -SelectionText=#f3f3f3ff -InactiveSelection=#606060ff -InactiveSelectionText=#ffffffff -PlaceholderText=#808080 -RubberBandFill=#dfdfdf3c -RubberBandBorder=#cececeff -Link=#0000ffff -ActiveLink=#ff0000ff -VisitedLink=#ff00ffff -Gutter=#d4d0c8ff -GutterBorder=#404040ff -Ruler=#d4d0c8ff -RulerBorder=#404040ff -RulerActiveText=#404040ff -RulerInactiveText=#808080ff -TextCursor=#ff0000ff -FocusOutline=#909090ff -SyntaxComment=#008000ff -SyntaxNumber=#800000ff -SyntaxString=#800000ff -SyntaxType=#800080ff -SyntaxPunctuation=#000000ff -SyntaxOperator=#000000ff -SyntaxKeyword=#000000ff -SyntaxControlKeyword=#000000ff -SyntaxIdentifier=#092e64ff -SyntaxPreprocessorStatement=#008080ff -SyntaxPreprocessorValue=#800000ff -Tooltip=#ffffe1ff -TooltipText=#000000ff -Tray=#3b3b3bff -TrayText=#ffffffff diff --git a/Base/res/themes/Sunshine.ini b/Base/res/themes/Sunshine.ini deleted file mode 100644 index 1e1e58c2b6c..00000000000 --- a/Base/res/themes/Sunshine.ini +++ /dev/null @@ -1,87 +0,0 @@ -[Menu] -Name=Suns&hine -[Colors] -Accent=#b24d7a -DesktopBackground=#574c8f -ActiveWindowBorder1=#b24d7a -ActiveWindowBorder2=#b24d7a -ActiveWindowTitle=white -ActiveWindowTitleShadow=#000000ff -ActiveWindowTitleStripes=#00000000 -InactiveWindowBorder1=#aeb2c3 -InactiveWindowBorder2=#aeb2c3 -InactiveWindowTitle=black -InactiveWindowTitleShadow=#d5cfd7ff -InactiveWindowTitleStripes=#00000000 -MovingWindowBorder1=#b24d7a -MovingWindowBorder2=#b24d7a -MovingWindowTitle=white -MovingWindowTitleShadow=#000000ff -MovingWindowTitleStripes=#00000000 -HighlightWindowBorder1=#b24d7a -HighlightWindowBorder2=#b24d7a -HighlightWindowTitle=white -HighlightWindowTitleShadow=#000000ff -HighlightWindowTitleStripes=#00000000 -HighlightSearching=#ffff00 -HighlightSearchingText=black -MenuBase=#aeb2c3 -MenuBaseText=black -MenuStripe=#aeb2c3 -MenuSelection=#9397a5 -MenuSelectionText=black -Window=#aeb2c3 -WindowText=black -Button=#aeb2c3 -ButtonText=black -Base=white -BaseText=black -DisabledTextFront=#9397a5 -DisabledTextBack=white -ThreedHighlight=white -ThreedShadow1=#9397a5 -ThreedShadow2=#5d6069 -HoverHighlight=#9397a5 -Selection=black -SelectionText=white -InactiveSelection=#606060 -InactiveSelectionText=white -PlaceholderText=#9397a5 -RubberBandFill=#0000003c -RubberBandBorder=#007f7f -Link=#88c -ActiveLink=#c88 -VisitedLink=#c8c -Gutter=#aeb2c3 -GutterBorder=#5d6069 -Ruler=#aeb2c3 -RulerBorder=#5d6069 -RulerActiveText=#5d6069 -RulerInactiveText=#5d6069 -TextCursor=red -FocusOutline=#909090 -SyntaxComment=#008000 -SyntaxNumber=#800000 -SyntaxString=#800000 -SyntaxType=#800080 -SyntaxPunctuation=black -SyntaxOperator=black -SyntaxKeyword=black -SyntaxControlKeyword=black -SyntaxIdentifier=#092e64 -SyntaxPreprocessorStatement=#008080 -SyntaxPreprocessorValue=#800000 -Tooltip=#ffffe1 -TooltipText=black -Tray=#9397a5 -TrayText=white - -[Alignments] -TitleAlignment=Left - -[Flags] -IsDark=false - -[Paths] -ColorScheme=/res/color-schemes/Dracula.ini -TitleButtonIcons=/res/icons/themes/Sunshine/16x16/ diff --git a/Base/res/wallpapers/grid.png b/Base/res/wallpapers/grid.png deleted file mode 100644 index 287baf03619..00000000000 Binary files a/Base/res/wallpapers/grid.png and /dev/null differ diff --git a/Base/res/wallpapers/sunset-retro.png b/Base/res/wallpapers/sunset-retro.png deleted file mode 100644 index c50832f8be4..00000000000 Binary files a/Base/res/wallpapers/sunset-retro.png and /dev/null differ diff --git a/Base/res/wallpapers/tile.png b/Base/res/wallpapers/tile.png deleted file mode 100644 index 36061e537d6..00000000000 Binary files a/Base/res/wallpapers/tile.png and /dev/null differ diff --git a/Base/res/words.txt b/Base/res/words.txt deleted file mode 100644 index 3d04ed9e431..00000000000 --- a/Base/res/words.txt +++ /dev/null @@ -1,2091 +0,0 @@ -# grep -iroE '[a-zA-Z]{2,}' --include "*.md" . | sort -u -f > all_words.txt -# grep -iFxf all_words.txt /usr/share/dict/words > words.txt -abbreviation -ability -able -aborting -about -above -absolute -abstraction -AC -accelerated -acceleration -accept -acceptable -access -accessed -accesses -accessible -accessing -according -account -accountability -Acer -achieve -achieved -acronym -across -ACT -active -Acts -actual -actually -adapter -ADD -added -adding -addition -additional -additionally -address -addressed -adds -adjust -adjusting -administrator -adopt -adopted -adopting -advanced -advantage -advice -aesthetically -AF -after -afterwards -Ag -again -against -aggressively -ahead -AK -alias -align -alignment -alive -all -allocate -allocated -allocates -allocation -allow -allowing -allows -alongside -Alpine -already -also -alt -alternate -alternative -alternatively -although -always -AM -AMD -amending -amount -An -analysis -analyzer -AND -Android -announce -anon -anonymous -another -answer -any -anybody -anymore -anyone -anything -anywhere -apart -API -Apis -app -appear -appears -append -application -applied -applies -approach -appropriate -appropriately -apt -arbitrary -Arch -architecture -archive -archiving -are -area -argument -Arm -around -array -Art -article -artificial -ASCII -Ask -asked -asks -aspirational -assert -assertion -assigned -assignment -assist -assume -assumes -assuming -At -attempt -attempting -attribute -attribution -audio -author -auto -automatically -available -avoid -avoiding -away -back -background -backing -bad -Banner -BAR -bare -base -based -bash -BASIC -basically -Be -Beau -beautifier -because -become -becomes -been -before -beforehand -Begin -behalf -behavior -behind -being -belong -below -better -between -beyond -big -bin -binaries -binary -bind -BIOS -bit -bitmap -Black -blasting -blinking -blob -block -blocking -blog -Board -Boards -body -boiler -Book -Boolean -boot -bootable -booted -booting -both -bottom -boundaries -bouquet -box -BR -brace -Branch -brand -breakdown -brew -Bridge -broken -browse -browser -browsing -BSD -buffer -Bug -buggy -build -builder -building -built -bunch -but -by -CAB -cable -cache -call -called -calling -Can -cannot -canonical -capabilities -capable -capacity -capital -capitalize -capture -captured -Card -CARE -Case -cask -CAT -catch -cater -cause -caused -CD -centered -certain -Ch -change -changed -changing -channel -char -character -cheat -check -checked -checking -checkmark -checkout -child -children -chipset -chipsets -choice -choose -Chosen -Ci -circle -circumvent -clang -class -classes -clause -clean -cleanup -clear -click -clicking -client -clipboard -clone -cloning -close -closed -clunky -clutter -code -coded -coding -coexist -coffee -coherent -collection -Colon -column -COM -combination -combiner -come -coming -comma -command -comment -commented -commit -Common -commonly -communicate -communication -community -compatibility -compatible -compilation -compile -compiled -compiler -compiles -compiling -complain -completeness -complexity -component -composited -comprehension -compressed -compromise -computer -concept -conceptually -concrete -concurrent -condition -conf -configuration -configure -configured -configures -configuring -conflicting -conform -confusing -connect -connected -connecting -connection -consequently -consider -considered -consistent -consisting -consists -console -Constant -constraint -construct -constructed -constructing -construction -constructor -contact -contain -contains -content -context -contiguous -contrast -control -controller -convenience -convenient -convention -conversion -convert -converted -cool -copied -copies -copy -copying -copyright -CORE -correct -correctly -corresponding -corrupted -corruption -cost -costly -could -Count -counted -counting -couple -courage -course -coverage -CP -CPU -crawl -create -created -creates -creating -Creation -Creator -criteria -critical -crop -Cross -Cu -curious -curl -current -currently -cursor -custom -customization -customize -customized -customizing -CZ -dark -data -database -dataflow -DD -DE -dead -deal -Debian -debug -debugger -debugging -decide -decided -decimal -decision -declaration -declare -declared -declaring -decode -decoded -decoding -decompress -default -deferred -define -defined -defines -definitely -definition -delete -deleted -deleting -delimited -Dell -denote -depend -dependencies -dependency -dependent -depending -depends -deployment -derived -derives -describe -described -describes -describing -description -descriptive -design -desired -desktop -destructor -detail -detailed -detect -detectable -detecting -determine -determined -deterministic -dev -devel -developer -development -device -diagonal -dialog -did -difference -different -differs -difficulty -diffs -Dir -direct -directing -direction -directive -directly -directories -directory -disable -disabled -disabling -disclaimer -discord -discouraged -discovering -discrete -disk -diskless -display -displayed -distribution -divvy -do -doc -document -documentation -documented -doing -Dom -Don -done -doohickey -Down -download -downloaded -downloading -dpi -drag -dragging -draw -drive -driver -drop -dude -due -dump -durable -during -dynamic -dynamically -each -eagerly -earlier -Early -easier -easiest -easily -East -easy -Echo -edge -edit -editing -editor -effect -efficient -Eg -either -element -elevated -else -EM -email -embed -embedded -emerged -emit -emoji -empty -emulate -emulated -emulation -emulator -en -enable -enabled -enables -enabling -encoded -encoding -encountered -end -ending -endpoint -enforces -engine -English -enough -ensure -ensures -entering -entire -entities -entries -entry -environment -EQ -equal -equivalent -equivalently -ERA -err -error -especially -essential -essentially -established -establishes -etc -Eth -Ethernet -evaluation -even -event -eventually -ever -every -everything -exactly -example -except -exception -exclude -excluded -excluding -Exe -executable -executables -execute -executed -execution -exist -existing -exists -exit -expand -expansion -expect -expected -experienced -experimental -expert -explain -explains -explicit -explore -Explorer -export -expose -exposed -exposing -expression -ext -extend -extends -extension -extensively -extra -extract -extremely -face -facilities -fact -factor -fail -failed -failing -failure -fairly -fake -false -familiar -fast -faster -fatal -fault -feature -fedora -feel -few -fewer -ff -fiddling -Field -fight -figure -figured -file -filename -fill -filtering -final -finalize -finally -find -finding -fine -fini -finish -finishes -firewall -firmware -first -fix -fixed -flag -flashing -flexible -float -floating -focal -focus -folder -follow -followed -following -font -foo -for -Force -Forces -forget -fork -form -format -formatted -formatter -formatters -formatting -fortify -forward -forwarded -found -four -frame -framework -free -frequently -fresh -Friend -from -front -FTP -full -fully -fun -function -functionality -functioning -further -furthermore -fuse -future -fuzzing -gag -gateway -GB -Gemini -general -generally -generate -generated -generates -generation -German -get -getter -getters -getting -gigabyte -git -GitHub -give -giving -gl -global -glyph -GMP -GNU -Go -goal -goes -going -gone -Good -goofy -Google -gotten -GPU -grab -graph -graphical -Greater -group -grow -grub -guaranteed -guard -guest -GUI -guide -guided -guideline -hacking -half -halt -halted -halting -hand -handle -handling -Handy -happen -happening -happens -hard -harder -hardware -hash -hashes -have -haven -having -HE -header -heap -heavy -height -held -hello -Help -Helper -helpful -hence -Here -hex -hexadecimal -hidden -high -higher -highlight -highlighter -highlighting -historical -hit -hold -Home -homebrew -horizontal -Host -hosted -hostile -how -however -HP -HT -HTML -HTTP -human -ich -icon -IDE -Idea -ideal -identically -identified -identifier -identifiers -idiomatic -IF -ignore -ignoring -iii -image -immediately -implement -implementation -implemented -implementing -implicit -implies -import -important -improve -IN -include -included -includes -including -incorrect -increment -incrementally -indented -Index -indexing -indicate -Indices -individually -Inf -influenced -info -information -inherit -inheritance -init -initial -initialization -initialize -initialized -initializer -inline -input -insert -insertion -inside -insight -inspect -inspired -install -installation -installed -installing -instance -instantiated -instantiation -instead -instrumentation -int -integer -integrate -integrated -integration -Intel -intended -inter -interact -interface -internally -Internet -interpret -interprets -into -intrinsic -introduce -introduction -intrusive -investigate -Invocation -Invocations -invoke -invokes -involve -Io -IP -IPA -ISO -issue -IT -item -iterators -itself -jagged -Jam -JavaScript -Joe -join -JPEG -judgement -jury -just -keep -kept -kernel -Key -keyboard -keyword -killed -kind -Kit -know -known -label -lack -ladybug -Lang -language -large -largely -larger -last -later -latest -launch -launcher -layout -lazily -leading -leaf -least -leave -leaving -Left -legacy -Len -length -let -letter -level -Lib -libraries -library -license -light -like -Likely -likewise -limit -limitation -limited -Line -link -linked -linker -lint -linters -Linux -list -listed -literal -littering -Little -live -living -LL -load -loaded -loading -local -locate -located -location -lock -locker -locking -log -logged -logging -logical -Long -longer -look -lookup -loop -loosely -losing -Lot -Low -Lower -lowercase -lowest -Mac -machine -macro -made -magic -magically -Main -mainstream -make -making -malicious -managed -manager -manipulate -manipulated -manipulation -manual -manually -many -Map -mapping -Mark -marked -marketplace -markup -Master -match -matchers -matches -matching -matter -Max -maximize -maximum -May -maybe -MB -MD -ME -mean -meaning -meaningful -meaningless -Means -medium -meet -Member -Members -membership -memory -mentioned -menu -mess -message -Meta -metal -meth -Method -Methods -mib -mice -Microsoft -Middle -might -mime -mind -mini -minimizing -minimum -minute -mirror -Miss -missing -mode -model -modeling -modem -Modern -modifier -modify -module -Mold -moment -monitor -More -most -mostly -motherboard -Mount -mouse -move -moved -moving -MT -much -multi -multiple -must -my -Na -name -named -naming -Nan -Native -natively -navigate -NE -nearest -necessary -need -needed -negative -neighbor -nesting -Net -network -networking -never -New -newer -newline -newly -newt -next -NF -nicely -Ninja -nix -NM -No -node -non -None -nonsensical -NOR -normal -normally -NOT -note -noted -nothing -notice -noticed -noting -NOW -nowadays -null -number -Numbers -numeric -numerical -numerous -object -occurs -octal -OEM -OF -off -official -offline -often -OG -OK -Old -older -omit -ON -onboard -once -one -online -only -onto -oom -Open -opened -opening -operand -operate -operating -operation -operator -opt -optimally -optimization -optimized -option -optional -optionally -OR -order -org -original -originally -OSes -other -otherwise -our -ourselves -out -outdated -outgoing -outgrow -output -outside -over -overall -overflow -overhead -overload -overridden -override -overriding -overview -overwrite -own -owned -owner -ownership -owns -PA -package -packet -Page -paint -painted -Painter -painting -palette -panel -panic -parallel -parameter -parent -parentheses -parse -parser -parsing -part -parted -partition -partly -pass -passed -passes -passing -password -past -paste -patch -path -pattern -PC -Pentium -per -performance -performed -period -persist -persistent -person -phase -physical -pi -pick -picked -PID -piece -ping -pitch -pixel -pkg -Place -placed -placement -plain -plan -planning -plate -platform -playback -Player -playground -please -pleasing -pledge -plumb -pluralization -plus -point -pointed -pointer -pointing -poll -Port -portal -possible -possibly -Post -potentially -power -practice -pre -precede -preceding -precision -prediction -prefer -preferred -prefix -prefixed -prepared -preprocessing -preprocessor -present -Presentation -presented -presently -prettier -pretty -prevent -previous -primary -Prime -principle -printed -printing -Private -PRO -probably -problem -proc -procedure -proceed -proceeding -process -processes -processor -profile -profiling -program -programming -project -prominently -prompt -prop -propagate -propagated -propagating -propagation -Proper -properly -properties -property -Prot -protect -protocol -provide -provided -provides -proxy -public -pull -punctuation -push -put -putting -PX -Python -qt -qualified -qualifier -qualify -querier -question -quick -quickly -quite -RA -radio -RAM -random -range -rare -raspberry -raw -Re -reaches -read -readable -Reader -Reading -ready -real -realize -really -reason -reasonably -reasoning -reboot -rebuild -rebuilds -rebuilt -receive -recognize -recognized -recommend -recommendation -recommended -reconfigure -recoverable -redirects -reduce -ref -refer -reference -referencing -referred -referring -reflect -refresh -Reg -regard -regenerate -regexp -region -regression -regular -related -relating -relative -release -relevant -reliable -relies -reload -relying -remember -remote -remove -removed -removing -render -rendition -reoccurring -repeat -replace -replaced -replacement -repo -report -repos -repositories -repository -represent -representation -representing -represents -request -requested -requesting -require -required -requirement -requires -requiring -resampled -reserved -reset -reside -resize -resized -resizing -resolution -resorting -resource -respect -respectively -response -responsibility -responsible -rest -restart -restarting -result -retaining -return -returned -returning -reveal -revert -Revolution -RF -Right -RM -Root -rough -roughly -row -ruc -rule -Rules -run -runner -running -runtime -rust -safe -safely -said -sake -SALT -same -sample -samurai -sanitize -sanitizer -sanitizers -satisfaction -save -say -scale -scaled -scaling -Scan -schedule -scheduler -scope -scoped -screen -screenshot -Script -scrolling -SCSI -SD -seamlessly -search -second -Secret -section -security -sed -see -seeing -seems -segment -segmentation -segmented -segregated -select -selected -selecting -selection -selectively -selector -self -sending -sent -Separate -separated -separately -separating -separation -sequence -sequential -sequentially -serenity -serial -serialize -series -serve -served -server -service -serving -session -Set -setter -setting -setup -several -sh -share -shared -Shell -shift -ship -Short -shortcut -should -show -shown -shutdown -side -sign -signal -signed -significant -significantly -similar -similarly -simple -simpler -simplest -simplicity -simply -since -single -Singleton -site -size -sized -skip -slave -slightly -slot -slow -slowly -Small -smaller -smart -smooth -smoother -Snake -sniffing -snip -snippet -SO -socket -soft -software -sole -solely -solves -some -somebody -someday -someone -something -sometimes -somewhat -somewhere -Sound -source -sourcing -space -Span -spawn -spawned -spawning -Speaker -speaking -Speaks -spec -special -specific -specifically -specification -specified -specifier -specifiers -specifies -specify -speed -speeding -Split -spreadsheet -squint -SSA -stability -stable -stack -stage -staged -Standard -start -started -starting -startup -state -statement -static -stay -STD -stdio -step -still -stop -stopping -storage -store -stored -strategy -stretch -string -stroke -strongly -structure -stuck -student -studio -stuff -style -styling -sub -subclasses -subdirectory -subframes -subjectively -subset -succeed -successful -successfully -such -suffice -suffix -suffixes -suggests -suitable -summarizes -summary -sunrise -super -superclass -supplied -supply -support -supported -supporting -suppose -supposed -sure -surface -surprisingly -surrounded -suspect -swap -switch -symptom -sync -synchronous -syntax -system -tab -tabulation -tailor -take -taken -taking -tap -tar -tarballs -Target -task -taskbar -team -technical -technique -telling -template -temporarily -tends -term -terminal -terminate -terminated -terminates -terse -test -tested -text -than -that -the -their -them -themselves -then -there -therefore -thereof -these -they -thin -thing -thingy -third -this -those -though -thought -thread -three -through -throughout -throwing -thumb -thus -tick -tied -time -timer -to -together -toggle -token -too -tool -toolbar -tooling -top -total -towards -trace -track -tracked -tracking -trackpad -trailing -transfer -transferred -transferring -transform -transmitted -transparently -Tree -tricky -tries -trivially -troubleshooting -true -try -ttys -tui -turn -turned -turning -tweaking -two -type -typing -Ubuntu -undefined -under -understand -underway -undo -unfilled -unfortunately -unifies -uninitialized -unique -universal -UNIX -unknown -unless -unlike -unprivileged -unrecognized -unsigned -unsupported -untested -until -unused -unveil -unzip -up -update -updated -updating -uploading -upon -uppercase -uptime -Uri -URL -usage -USB -use -used -useful -user -username -using -usual -usually -utility -valid -validate -validity -value -VAR -variable -variant -variety -various -varying -vector -verify -Version -Versions -vertical -Very -VGA -via -vice -video -view -virtual -visible -visual -visualizer -void -volatile -VX -wait -Wall -want -wanting -warning -was -wasm -watch -way -we -weak -weakly -Web -Website -went -what -wheel -when -whenever -where -whether -which -while -whilst -whiptail -White -WHO -why -wide -widget -width -WiFi -wiki -Wikipedia -wildcat -Will -willing -win -window -Windows -wipe -wireless -Wise -wish -wishes -with -within -without -wizard -won -Word -work -workaround -workflow -working -workspace -workstation -World -worry -Worth -would -wrapped -wrapper -writable -writing -written -wrong -WWW -Xe -xterm -xx -xxx -year -yet -yield -yielding -YMMV -you -your -yourself -YouTube -zero -ZIP -zoom diff --git a/Base/root/generate_manpages.sh b/Base/root/generate_manpages.sh deleted file mode 100755 index 2d7ffdae146..00000000000 --- a/Base/root/generate_manpages.sh +++ /dev/null @@ -1,49 +0,0 @@ -#!/bin/Shell - -export ARGSPARSER_EMIT_MARKDOWN=1 - -# Qemu likes to start us in the middle of a line, so: -echo - -ERROR_FILE="generate_manpages_error.log" -rm -f "$ERROR_FILE" - -exit_for_error() -{ - if test $DO_SHUTDOWN_AFTER_GENERATE { - touch "$ERROR_FILE" # Ensure it exists, in case there wasn't any stderr output. - shutdown -n - } else { - exit 1 - } -} - -rm -rf generated_manpages 2> "$ERROR_FILE" || exit_for_error - -for i in ( \ - (config 1) \ - (fortune 1) \ - (grep 1) \ - (nc 1) \ - (nl 1) \ - (passwd 1) \ - (readelf 1) \ - (shot 1) \ - (sql 1) \ - (tr 1) \ - (traceroute 1) \ - (truncate 1) \ - ) { - filename="generated_manpages/man$i[1]/$i[0].md" - mkdir -p "generated_manpages/man$i[1]" - echo "Generating for $i[0] in $filename ..." - $i[0] --help > "$filename" 2> "$ERROR_FILE" || exit_for_error - echo -e "\n" >> "$filename" 2> "$ERROR_FILE" || exit_for_error -} - -rm -f "$ERROR_FILE" -echo "Successful." - -if test $DO_SHUTDOWN_AFTER_GENERATE { - shutdown -n -} diff --git a/Base/usr/share/Maps/TileProviders.json b/Base/usr/share/Maps/TileProviders.json deleted file mode 100644 index c702eb0b83a..00000000000 --- a/Base/usr/share/Maps/TileProviders.json +++ /dev/null @@ -1,32 +0,0 @@ -[ - { - "name": "OpenStreetMap (Local labels)", - "url_format": "https://tile.openstreetmap.org/{}/{}/{}.png", - "attribution_text": "© OpenStreetMap contributors", - "attribution_url": "https://www.openstreetmap.org/copyright" - }, - { - "name": "OpenStreetMap (German labels)", - "url_format": "https://tile.openstreetmap.de/{}/{}/{}.png", - "attribution_text": "© OpenStreetMap contributors", - "attribution_url": "https://www.openstreetmap.org/copyright" - }, - { - "name": "OpenStreetMap (French labels)", - "url_format": "https://a.tile.openstreetmap.fr/osmfr/{}/{}/{}.png", - "attribution_text": "© OpenStreetMap contributors", - "attribution_url": "https://www.openstreetmap.fr/copyright" - }, - { - "name": "OpenStreetMap (Humanitarian map style)", - "url_format": "https://a.tile.openstreetmap.fr/hot/{}/{}/{}.png", - "attribution_text": "© OpenStreetMap contributors", - "attribution_url": "https://www.openstreetmap.fr/copyright" - }, - { - "name": "OpenStreetMap (Topographical map style)", - "url_format": "https://a.tile.opentopomap.org/{}/{}/{}.png", - "attribution_text": "© OpenStreetMap contributors", - "attribution_url": "https://www.opentopomap.org/credits" - } -] diff --git a/Base/usr/share/Welcome/tips.txt b/Base/usr/share/Welcome/tips.txt deleted file mode 100644 index 96980f0ce95..00000000000 --- a/Base/usr/share/Welcome/tips.txt +++ /dev/null @@ -1,27 +0,0 @@ -# SerenityOS Tips -# Did you know... -Pressing Ctrl+Alt+Space at an insertion caret pops up the emoji picker. -Middle clicking a window's maximize button extends that window vertically. -Browser has built-in ad blocking. Filter content by adding new domains to ~/.config/BrowserContentFiltering.txt -Default file and protocol associations can be changed in ~/.config/LaunchServer.ini -Text Editor has multiple viewing modes; edit and preview HTML and Markdown in real time. -It can help to get a second pair of $ Eyes on a problem. Or fifty: $ Eyes -n 100 -Highlighted text in Terminal can be launched or right-clicked for more context. -Focus can be cycled between windows by pressing and holding Super+Tab. Shift reverses the order. -Super+Down is a quick way to minimize a window. -Bold text in context menus hints at the default behavior of a double-click. -Tree nodes can be fully expanded by pressing Ctrl+Right. Collapse them again with Ctrl+Left. -Double clicking a window's title bar maximizes it; double clicking its icon will close it. -Text files can be dragged directly from Terminal and dropped on Text Editor to open them. -Resizable windows can be snapped to all sides of the screen. Drag a window to an edge or press Super+Left, Right or Up while it has focus. -The Run dialog accepts all Shell command language. Truly the gentleman's terminal. -Windows can be dragged from any visible point by holding Super+Left-click. Super+Right-click begins resizing them. -Many Serenity applications already have convenient aliases. $ cat /etc/shellrc to view them. -Custom keymaps can be created and edited with $ KeyboardMapper -Supplying # profile with a PID of -1 as root enables systemwide profiling. -Holding Ctrl accelerates mouse wheel interaction with sliders and spin boxes. -Selected files can be renamed by pressing F2. -Assistant can help you quickly find files and applications by pressing Super+Space. -Holding Ctrl while activating a menu item prevents that menu from closing. -Pressing Ctrl+Shift+A on a focused widget or application activates the command palette, a searchable list of available actions. -Workspaces can be switched by pressing Ctrl+Alt+Arrows. Shift brings the active window along. diff --git a/Base/usr/share/man/man1/Applications.md b/Base/usr/share/man/man1/Applications.md deleted file mode 100644 index 00dfe93c1f9..00000000000 --- a/Base/usr/share/man/man1/Applications.md +++ /dev/null @@ -1,42 +0,0 @@ -## Name - -Applications - SerenityOS GUI applications - -## Description - -SerenityOS comes with a suite of GUI applications. The applications are divided into multiple categories, through which they are available in the System Menu. - -Note that many applications can be disabled at SerenityOS build time if desired. Therefore, depending on your SerenityOS build configuration, not all applications will be available. - -## See also - -- Documentation for SerenityOS Games in section 6 - -### List of Application manpages - -- [3D File Viewer](help://man/1/Applications/3DFileViewer) -- [About](help://man/1/Applications/About) -- [Analog Clock](help://man/1/Applications/AnalogClock) -- [Assistant](help://man/1/Applications/Assistant) -- [Browser](help://man/1/Applications/Browser) -- [Calculator](help://man/1/Applications/Calculator) -- [Calendar](help://man/1/Applications/Calendar) -- [CatDog](help://man/1/Applications/CatDog) -- [Certificate Settings](help://man/1/Applications/CertificateSettings) -- [Character Map](help://man/1/Applications/CharacterMap) -- [Eyes](help://man/1/Applications/Eyes) -- [FontEditor](help://man/1/Applications/FontEditor) -- [GML Playground](help://man/1/Applications/GMLPlayground) -- [Help](help://man/1/Applications/Help) -- [Hex Editor](help://man/1/Applications/HexEditor) -- [Image Viewer](help://man/1/Applications/ImageViewer) -- [Magnifier](help://man/1/Applications/Magnifier) -- [Mail](help://man/1/Applications/Mail) -- [Maps](help://man/1/Applications/Maps) -- [Mouse Settings](help://man/1/Applications/MouseSettings) -- [Pixel Paint](help://man/1/Applications/PixelPaint) -- [Presenter](help://man/1/Applications/Presenter) -- [Profiler](help://man/1/Applications/Profiler) -- [SQL Studio](help://man/1/Applications/SQLStudio) -- [Terminal](help://man/1/Applications/Terminal) -- [Text Editor](help://man/1/Applications/TextEditor) diff --git a/Base/usr/share/man/man1/Applications/3DFileViewer.md b/Base/usr/share/man/man1/Applications/3DFileViewer.md deleted file mode 100644 index 0051e68954e..00000000000 --- a/Base/usr/share/man/man1/Applications/3DFileViewer.md +++ /dev/null @@ -1,61 +0,0 @@ -## Name - -![Icon](/res/icons/16x16/app-3d-file-viewer.png) 3D File Viewer - -[Open](file:///bin/3DFileViewer) - -## Synopsis - -```**sh -$ 3DFileViewer [file] -``` - -## Description - -`3D File Viewer` is an application for viewing 3D models. - -It currently supports opening the [Wavefront OBJ file format](https://en.wikipedia.org/wiki/Wavefront_.obj_file) (`.obj`). - -## Features - -By default, 3D File Viewer opens with a rotating [Utah teapot](https://en.wikipedia.org/wiki/Utah_teapot), a standard 3D test model. Open files via *File → Open* (`Ctrl+O`) or dragging and dropping files into the application. Sample files can be found in `Documents/3D Models`. - -Orbit around the model by grabbing it with the cursor, and zoom in and out with the mouse wheel. - -### View Options - -* View in Fullscreen mode by pressing `F11` and press `Esc` to return to windowed mode. -* **Rotation Axis** - Enable or disable rotation: - * **X** rotates upwards. - * **Y** rotates right. - * **Z** rotates clockwise. -* **Rotation Speed** - Set to slow, normal, fast or disable. -* **Show Frame Rate** - Display FPS (frames per second) and frame time values (indicating how long it took to render a single frame) in the top-right corner. - -### Texture Options - -To load a texture, ensure there's a [Bitmap image file](https://en.wikipedia.org/wiki/BMP_file_format) (`.bmp`) with the same name as the OBJ file in the same folder and 3D File Viewer will attempt to load it automatically. - -* **Enable Texture** - On by default, disable for a blank (gray) model. -* **Wrap S or T** - Controls how a texture is applied horizontally (S) or vertically (T): - * **Repeat** - Tiles the texture along the axis. - * **Mirrored** - Tiles and mirrors the texture along the axis. - * **Clamp** - Stretches or clamps the texture edges instead of repeating it. -* **Scale** - Adjusts texture size (e.g. `0.5` reduces by half, `2x` doubles the size). -* **Mag Filter** - Determines the appearance of textures when enlarged beyond their original resolution. - * **Nearest** - *Nearest Neighbor* means no scaling is applied to the texture, resulting in a pixellated look. - * **Linear** - *Linear Interpolation* scales the image by estimating pixel values, resulting in a smoother image. - -Currently, the flashing color lights can't be disabled. - -Settings persist whilst the application is open with different files, but do not persist between sessions. - -## Arguments - -* `file`: The 3D file to be viewed. - -## Example - -```**sh -$ 3DFileViewer Documents/3D\ Models/ladyball.obj -``` diff --git a/Base/usr/share/man/man1/Applications/About.md b/Base/usr/share/man/man1/Applications/About.md deleted file mode 100644 index 766ba8df1b4..00000000000 --- a/Base/usr/share/man/man1/Applications/About.md +++ /dev/null @@ -1,23 +0,0 @@ -## Name - -![Icon](/res/icons/16x16/ladyball.png) About - About SerenityOS - -[Open](file:///bin/About) - -## Synopsis - -```**sh -$ About -``` - -## Description - -`About` is an application that displays information about SerenityOS. - -On the left is the official Ladyball logo. Designed by Katalin Kult, it combines the Yin-Yang symbol with the distinctive shell pattern of a [ladybird](https://en.wikipedia.org/wiki/Coccinellidae), the projects's insect motif. It symbolizes the SerenityOS philosophy, encompassing the *Serenity Prayer's* acceptance of the uncontrollable, the *Yin and yang* concept of balancing opposing forces, and *Lagom*, the Swedish concept of moderation or finding "just the right amount." - -Since there are no official releases of the project, the version number is denoted as `1.0-dev` and the revision number reflects the latest Git commit's hash. - -Copyright is attributed to the SerenityOS developers. Started by Andreas Kling in 2018, the project has since grown into a thriving global community of volunteers and full-time developers. Contributors with 100+ commits are acknowledged on [GitHub](https://github.com/SerenityOS/serenity#authors). The project's history is detailed on [Wikipedia](https://en.wikipedia.org/wiki/SerenityOS). - -To learn more about SerenityOS visit the [homepage](https://serenityos.org). diff --git a/Base/usr/share/man/man1/Applications/AnalogClock.md b/Base/usr/share/man/man1/Applications/AnalogClock.md deleted file mode 100644 index 91390407769..00000000000 --- a/Base/usr/share/man/man1/Applications/AnalogClock.md +++ /dev/null @@ -1,21 +0,0 @@ -## Name - -![Icon](/res/icons/16x16/app-analog-clock.png) Analog Clock - -[Open](file:///bin/AnalogClock) - -## Synopsis - -```**sh -$ AnalogClock -``` - -## Description - -`Analog Clock` is a desktop utility that provides a moveable analog clock, evoking the nostalgia of 90's desktop environments. - -## Features - -The clock adopts its colors from the system theme. - -To make the clock frameless, right-click and de-select `Show Window Frame` or press `Alt+F`. This locks the clock in place, hides the application in the taskbar and keeps it beneath other windows, making it appear integrated into the desktop. This setting won't persist between sessions, requiring you to reopen it with each login. To restore the frame and the ability to quit the application, simply reverse the process. diff --git a/Base/usr/share/man/man1/Applications/Assistant.md b/Base/usr/share/man/man1/Applications/Assistant.md deleted file mode 100644 index 72255d1b45e..00000000000 --- a/Base/usr/share/man/man1/Applications/Assistant.md +++ /dev/null @@ -1,22 +0,0 @@ -## Name - -![Icon](/res/icons/16x16/app-assistant.png) Assistant - Serenity Assistant - -[Open](file:///bin/Assistant) - -## Synopsis - -```**sh -$ Assistant -``` - -## Description - -`Assistant` is a helpful program that is used to search for and open applications, files, settings, and anything else a user may want to access. It can be opened by pressing `Super+Space` and closed by pressing `Esc`. - -### Features - -* Enter a URL to open it in [Ladybird](help://man/1/Applications/Browser), e.g. `serenityos.org`. -* Perform quick calculations by typing the equal sign (=) followed by a mathematical expression, e.g. `=22*101`. Press `Return` to copy the result. -* Run commands in [Terminal](help://man/1/Applications/Terminal) by prefixing them with a dollar sign ($), e.g. `$ uname -a`. -* Launch applications with arguments, e.g. launch [Pixel Paint](help://man/1/Applications/PixelPaint) with a file: `pp image.png`. diff --git a/Base/usr/share/man/man1/Applications/Browser.md b/Base/usr/share/man/man1/Applications/Browser.md deleted file mode 100644 index 5c17e39947a..00000000000 --- a/Base/usr/share/man/man1/Applications/Browser.md +++ /dev/null @@ -1,33 +0,0 @@ -## Name - -![Icon](/res/icons/16x16/app-browser.png) Browser - Serenity WWW Browser - -[Open](file:///bin/Browser) - -## Synopsis - -```**sh -$ Browser [options] [urls] -``` - -## Description - -Ladybird (`Browser`) is an application used to view the World Wide Web. It is built on top of Serenity's own `LibWeb` and `LibJS` engines, allowing it to render HTML, CSS, and JavaScript. - -## Options - -* `--help`: Display help message and exit -* `--version`: Display version number and exit - -## Arguments - -* `urls`: A list of urls to open, one per tab. If none are specified, then the homepage will be opened instead. - -## Examples - -```**sh -$ Browser -$ Browser --help -$ Browser https://serenityos.org/ -$ Browser https://serenityos.org/ /res/html/misc/welcome.html github.com/serenityos/serenity -``` diff --git a/Base/usr/share/man/man1/Applications/Calculator.md b/Base/usr/share/man/man1/Applications/Calculator.md deleted file mode 100644 index 0eb1bb37143..00000000000 --- a/Base/usr/share/man/man1/Applications/Calculator.md +++ /dev/null @@ -1,15 +0,0 @@ -## Name - -![Icon](/res/icons/16x16/app-calculator.png) Calculator - Serenity calculator - -[Open](file:///bin/Calculator) - -## Synopsis - -```**sh -$ Calculator -``` - -## Description - -`Calculator` is a graphical calculator application. It supports basic arithmetic calculations in addition to special operations. It also has convenient shortcuts for mathematical constants such as Pi. diff --git a/Base/usr/share/man/man1/Applications/Calendar.md b/Base/usr/share/man/man1/Applications/Calendar.md deleted file mode 100644 index 4180a36ccb7..00000000000 --- a/Base/usr/share/man/man1/Applications/Calendar.md +++ /dev/null @@ -1,15 +0,0 @@ -## Name - -![Icon](/res/icons/16x16/app-calendar.png) Calendar - Serenity calendar - -[Open](file:///bin/Calendar) - -## Synopsis - -```**sh -$ Calendar -``` - -## Description - -`Calendar` is a graphical calendar application for Serenity. It supports both Month View and Year View. diff --git a/Base/usr/share/man/man1/Applications/CatDog.md b/Base/usr/share/man/man1/Applications/CatDog.md deleted file mode 100644 index 1cbf05698a1..00000000000 --- a/Base/usr/share/man/man1/Applications/CatDog.md +++ /dev/null @@ -1,38 +0,0 @@ -## Name - -![Icon](/res/icons/16x16/app-catdog.png) CatDog - A helpful companion - -[Open](file:///bin/CatDog) - -## Synopsis - -```**sh -$ CatDog -``` - -## Description - -`CatDog` is an animated screenmate application in the form of a virtual pet. - -It was inspired by [Neko](https://en.wikipedia.org/wiki/Neko_(software)). The name derives from its ambiguous appearance: is it a cat, a dog, or a hybrid? - -Its helpful suggestions evoke the animated assistants popular at the turn of the millennium, such as [Clippy](https://en.wikipedia.org/wiki/Office_Assistant). - -Alongside [Buggie](https://en.wikipedia.org/wiki/SerenityOS#History) and the [Yaks](http://yaksplained.org/), CatDog is a much loved member of the SerenityOS family of mascots. As such it has a wider presence in the project’s online community, promotional materials and merchandise. - -### Features - -By default, CatDog will chase your mouse cursor around the screen. It will always stay on-top of other windows. - -Additionally, CatDog offers helpful suggestions depending on the currently active application. - -CatDog has multiple states: - -- *Sleeping* if the mouse cursor hasn’t moved in some time -- *Alert* if woken by the mouse cursor or speaking after being idle -- *Dressed as an Artist* if [Pixel Paint](help://man/1/Applications/PixelPaint) or [Font Editor](help://man/1/Applications/FontEditor) are open -- *Dressed as an Inspector* if [Profiler](help://man/1/Applications/Profiler) or System Monitor are open - -To exit, right-click on CatDog and select `Quit` or, if active, press `Alt+F4`. - -CatDog brings good luck to those who keep it open. diff --git a/Base/usr/share/man/man1/Applications/CertificateSettings.md b/Base/usr/share/man/man1/Applications/CertificateSettings.md deleted file mode 100644 index 38be3ab8c03..00000000000 --- a/Base/usr/share/man/man1/Applications/CertificateSettings.md +++ /dev/null @@ -1,22 +0,0 @@ -## Name - -![Icon](/res/icons/16x16/certificate.png) Certificate Settings - -[Open](file:///bin/CertificateSettings) - -## Synopsis - -```**sh -$ CertificateSettings -``` - -## Description -`Certificate Settings` is an application for managing digital certificates in SerenityOS. - -Certificates are used for establishing a user's credentials when trying to create secure connection between a client and server by matching a public and private key. - -The *Certificate Store* lists all of the certificates currently stored in the system, the Trusted Root Certification Authorities (CAs) they were issued by and their expiration dates. - -### Features - -Import and export CAs as Privacy-Enhanced Mail files (`.pem`). \ No newline at end of file diff --git a/Base/usr/share/man/man1/Applications/CharacterMap.md b/Base/usr/share/man/man1/Applications/CharacterMap.md deleted file mode 100644 index c5593c2884f..00000000000 --- a/Base/usr/share/man/man1/Applications/CharacterMap.md +++ /dev/null @@ -1,32 +0,0 @@ -## Name - -![Icon](/res/icons/16x16/app-character-map.png) Character Map - Unicode character viewer - -[Open](file:///bin/CharacterMap) - -## Synopsis - -```**sh -$ CharacterMap -$ CharacterMap --search "yak" -``` - -## Description - -Character Map is a GUI application for viewing, searching for, and copying Unicode characters. Alternatively, you can use it to search for characters by name, from the command line. - -## Examples - -To open Character Map: -```sh -$ CharacterMap -``` - -To view a list of all characters that have "yak" in their name: -```sh -$ CharacterMap --search "yak" -``` - -## See Also - -* [`FontEditor`(1)](help://man/1/Applications/FontEditor) To edit the fonts instead of just viewing them. diff --git a/Base/usr/share/man/man1/Applications/Eyes.md b/Base/usr/share/man/man1/Applications/Eyes.md deleted file mode 100644 index 014145c1265..00000000000 --- a/Base/usr/share/man/man1/Applications/Eyes.md +++ /dev/null @@ -1,21 +0,0 @@ -## Name - -![Icon](/res/icons/16x16/app-eyes.png) Eyes - follow the mouse LibGUI demo - -[Open](file:///bin/Eyes) - -## Synopsis - -```sh -$ Eyes [--num-eyes number] [--max-in-row number] [--grid-rows number] [--grid-cols number] -``` - -## Options - -* `--help`: Display help message and exit -* `--version`: Print version -* `-n number`, `--num-eyes number`: Number of eyes -* `-m number`, `--max-in-row number`: Maximum number of eyes in a row -* `-r number`, `--grid-rows number`: Number of rows in grid (incompatible with --number) -* `-c number`, `--grid-cols number`: Number of columns in grid (incompatible with --number) -* `-h`, `--hide-window`: Hide window frame diff --git a/Base/usr/share/man/man1/Applications/FontEditor.md b/Base/usr/share/man/man1/Applications/FontEditor.md deleted file mode 100644 index 2350d2c8ac9..00000000000 --- a/Base/usr/share/man/man1/Applications/FontEditor.md +++ /dev/null @@ -1,105 +0,0 @@ -## Name - -![Icon](/res/icons/16x16/app-font-editor.png) FontEditor - Serenity font editor - -[Open](file:///bin/FontEditor) - -## Synopsis - -```**sh -$ FontEditor [file] -``` - -## Description - -FontEditor is the font editing application for creating and editing bitmap font files in SerenityOS. - -![](FontEditor.png) - -### Basic Parts -FontEditor has the following basic parts: -1. The menu bar at the top -2. The toolbar -3. The main workspace -4. The status bar at the bottom - -Presently, you can resize the entire FontEditor form to your liking. You can turn the visibility of Font Metadata and Unicode Blocks on or off. You can change the glyph editor window zoom factor. All of these things are currently available in FontEditor automatically. - -### The Toolbar -The Toolbar contains the same functional entries as that of the Menubar and is represented as clickable icons. Hovering on each icon will display additional information listed in the status bar which further states what each icon does. - -### The Main Workspace -The main workspace has three partitions: - -- The ***left section*** contains the glyph editor window. This window has three different zoom factors for ease of use and for your viewing comfort. They are 500%, 1000% and 1500%. You can freely switch to any zoom level at anytime while creating your font. Don't let the size of the glyph editor window fool you. This is where you will spend about 95% of the time. And the task is that of shaping each glyph by left-clicking on the mouse. You can form a dot, a line, or a complete glyph just by connecting the dots. If you need to undo a certain location with a black dot, you can right-click on top of the target area and it will revert back to empty. Each grid area can hold any of the three states: 1) on or black 2) off or white 3) empty or gray. On state is displayed as black by default. Off state is displayed as white by default. Empty state is displayed as gray by default. Off state or white is relevant most specially if the font is fixed-width as it dictates the distance a glyph will have from left, center or right. Empty state or gray is important for variable-width font as it affects the overall width of the glyph. For variable width fonts, you move the glyph flushed left on the glyph editor window and remove any extra empty grids by decrementing the present column counter located right under the glyph editor window and thereby leaving only the entire glyph all by itself. Below the glyph editor is the glyph tool which contains the following: the pen icon for creating the glyph itself, the move icon to enable the entire glyph move to top, down, left or right from within the glyph editor window. Below the glyph tool are the transform icons which are flip horizontal, flip vertical, rotate counter-clockwise 90°and rotate clockwise 90°. Transform tools are most helpful when copying over existing glyphs and transforming them to form a new glyph. A becomes V, M becomes W, c becomes e, n becomes u, etc. - -- The ***middle section*** contains two parts: the upper part which holds the entire glyph content of the font, or lack thereof if you are making a new one. And the lower part which contains ***Metadata*** information such as name, family, weight, slope, presentation size, mean line, baseline, glyph spacing and if the font is either fixed-width or variable-width. You can still further tweak your font parameters via the Metadata section. The `Fixed width` toggle located at the right side next to Glyph spacing is of special note. It applies to the entire font file. It does not only apply to a single glyph or group of glyphs. It is the differentiating factor that informs the system if the font is or is not fixed-width. Fixed-width fonts are mostly used for Terminals and for displaying source code. - -- The ***right section*** displays a searchable list of Unicode Blocks. -Metadata and Unicode Blocks can be turned on or off in `Menu → View`. Selecting a Unicode Block will show only the glyphs contained within the range of that block. Basic Latin covers 000-007F, Latin-1 Supplement covers 0080-00FF, Latin Extended-A covers 0100-017F and so on and so forth. Global search for a glyph is affected when a certain block is currently selected. Only by selecting `Show All` will the global glyph search work as expected. So make it a habit of confirming that Show All is active before searching for a glyph. - -### The status bar -The status bar displays additional information describing what each menu entry and toolbar icon does. It identifies the unicode value of the glyph currently under the cursor. It shows the glyph's visual representation (if available), description and dimensions. The right-most segment displays the code point range of the currently selected Unicode Block. Clicking this segment will toggle the display of the Unicode Block list. Second only to the glyph editor window, the status bar is your next best friend on your path to becoming a font master. - -## Tutorial: Create a new font -![](FontEditor_New_Font.png) - -To create a new font, you can either click on New Font icon on the Toolbar or go to `File → New Font` in the Menubar. -A wizard will walk you through setting the needed parameters for your new font. - -### Typeface Properties -![](FontEditor_Typeface_properties.png) - -You can try out the default values just to get the feel of the program. - -### Glyph Properties -![](FontEditor_Glyph_properties.png) - -Just click **Finish** when you are so inclined. - -### Edit Glyph Properties -![](FontEditor_Edit_Glyph_properties.png) - -Adjust the values to suit your needs. The higher the value, the larger the font size. - -### Untitled font -![](FontEditor_Untitled.png) - -Congratulations on your successful initial font setup. Now you are ready to begin. And begin you shall. There is more to font creation than simply scribbling away. You need to always remind yourself this question: "Where is the fun in that?". Start with what you need to do while striving to achieve fun in the process. Let's be honest, font creation is one of the many thankless jobs, unless and until there is fun in it, why bother? - -### Launch another instance -![](FontEditor_Launching_second_instance.png) - -The figure above shows where you can find FontEditor from inside the SerenityOS desktop. - -### Side by side -![](FontEditor_Twins_sidebyside.png) - -Having another instance of FontEditor can help boost productivity. This is most noticeable when one is just starting out in using FontEditor and trying out how to best make use of the application. Don't be afraid to experiment, let your inner font master slowly shine through. It is best to maintain the feeling of having fun while silently grinning from ear to ear as you steadily create form and personality in your font. Don't be afraid to start again, if you must. As with the entire workflow, being aware when to start, when to stop and when to reset is crucial. With FontEditor to assist you, starting from scratch is no longer an arduous process. - -### Save font -![](FontEditor_Save_font_as.png) - -Save your font by following the recommended `FontName + FontStyle + FontPresentationSize + .font` naming convention. - -### Continue Editing -![](FontEditor_Continue_editing_current_font.png) - -The figure above shows some previously made glyphs. Those with sharp eyes can immediately see that the glyphs for M and W, O and Q came from the same base. W was copied over from M and was flipped horizontally. Q was copied over from O and a descender was added to achieve the final glyph. Continue editing your font by adding more glyphs into it. Take your time in creating the font, only you know the reason why the glyph is formed the way it is. You know why the height and the width is so, why the curvature is just so. Aside from achieving balance, maintain the spirit of fun in making your font. The more glyphs are added, the easier it is to get the general feel and character of the font. Remember to always save and to save often. - -### fonts.serenityos.net -![](Fonts_SerenityOS_dot_Net.png) - -It is highly recommended to use our own [fonts portal](https://fonts.serenityos.net) as one of your primary resource for glyph and font information. The others are [Unicode charts [unicode.org]](https://www.unicode.org/charts/) and our [wiki](https://wiki.serenityos.net). - -### Search glyph -![](Fonts_SerenityOS_dot_Net_search_result.png) - -Figure above depicts a search session on the [fonts.serenityos.net](https://fonts.serenityos.net) portal. - -### Search detail -![](Fonts_SerenityOS_dot_Net_result_detail.png) - -The same search session displaying result of the previous query. - -So there you have it, by now you have at least an idea of how to make ***SerenityOS*** fonts using **FontEditor**. For any font-related questions or inquiries, just drop by the official [SerenityOS Discord #fonts](https://discord.com/channels/830522505605283862/927893781968191508). diff --git a/Base/usr/share/man/man1/Applications/FontEditor.png b/Base/usr/share/man/man1/Applications/FontEditor.png deleted file mode 100644 index d9802489321..00000000000 Binary files a/Base/usr/share/man/man1/Applications/FontEditor.png and /dev/null differ diff --git a/Base/usr/share/man/man1/Applications/FontEditor_Continue_editing_current_font.png b/Base/usr/share/man/man1/Applications/FontEditor_Continue_editing_current_font.png deleted file mode 100644 index 33d3c6f1d80..00000000000 Binary files a/Base/usr/share/man/man1/Applications/FontEditor_Continue_editing_current_font.png and /dev/null differ diff --git a/Base/usr/share/man/man1/Applications/FontEditor_Edit_Glyph_properties.png b/Base/usr/share/man/man1/Applications/FontEditor_Edit_Glyph_properties.png deleted file mode 100644 index 4b7d11fbb2f..00000000000 Binary files a/Base/usr/share/man/man1/Applications/FontEditor_Edit_Glyph_properties.png and /dev/null differ diff --git a/Base/usr/share/man/man1/Applications/FontEditor_Glyph_properties.png b/Base/usr/share/man/man1/Applications/FontEditor_Glyph_properties.png deleted file mode 100644 index 73dd13e3a4f..00000000000 Binary files a/Base/usr/share/man/man1/Applications/FontEditor_Glyph_properties.png and /dev/null differ diff --git a/Base/usr/share/man/man1/Applications/FontEditor_Launching_second_instance.png b/Base/usr/share/man/man1/Applications/FontEditor_Launching_second_instance.png deleted file mode 100644 index 53280ab8b96..00000000000 Binary files a/Base/usr/share/man/man1/Applications/FontEditor_Launching_second_instance.png and /dev/null differ diff --git a/Base/usr/share/man/man1/Applications/FontEditor_New_Font.png b/Base/usr/share/man/man1/Applications/FontEditor_New_Font.png deleted file mode 100644 index ea3cfdb66bb..00000000000 Binary files a/Base/usr/share/man/man1/Applications/FontEditor_New_Font.png and /dev/null differ diff --git a/Base/usr/share/man/man1/Applications/FontEditor_Save_font_as.png b/Base/usr/share/man/man1/Applications/FontEditor_Save_font_as.png deleted file mode 100644 index 4672bca5bc7..00000000000 Binary files a/Base/usr/share/man/man1/Applications/FontEditor_Save_font_as.png and /dev/null differ diff --git a/Base/usr/share/man/man1/Applications/FontEditor_Twins_sidebyside.png b/Base/usr/share/man/man1/Applications/FontEditor_Twins_sidebyside.png deleted file mode 100644 index 496c5f52cbd..00000000000 Binary files a/Base/usr/share/man/man1/Applications/FontEditor_Twins_sidebyside.png and /dev/null differ diff --git a/Base/usr/share/man/man1/Applications/FontEditor_Typeface_properties.png b/Base/usr/share/man/man1/Applications/FontEditor_Typeface_properties.png deleted file mode 100644 index 8349bb40dc2..00000000000 Binary files a/Base/usr/share/man/man1/Applications/FontEditor_Typeface_properties.png and /dev/null differ diff --git a/Base/usr/share/man/man1/Applications/FontEditor_Untitled.png b/Base/usr/share/man/man1/Applications/FontEditor_Untitled.png deleted file mode 100644 index 23f12d6bafe..00000000000 Binary files a/Base/usr/share/man/man1/Applications/FontEditor_Untitled.png and /dev/null differ diff --git a/Base/usr/share/man/man1/Applications/Fonts_SerenityOS_dot_Net.png b/Base/usr/share/man/man1/Applications/Fonts_SerenityOS_dot_Net.png deleted file mode 100644 index e1b7576935a..00000000000 Binary files a/Base/usr/share/man/man1/Applications/Fonts_SerenityOS_dot_Net.png and /dev/null differ diff --git a/Base/usr/share/man/man1/Applications/Fonts_SerenityOS_dot_Net_result_detail.png b/Base/usr/share/man/man1/Applications/Fonts_SerenityOS_dot_Net_result_detail.png deleted file mode 100644 index eb7504ba15e..00000000000 Binary files a/Base/usr/share/man/man1/Applications/Fonts_SerenityOS_dot_Net_result_detail.png and /dev/null differ diff --git a/Base/usr/share/man/man1/Applications/Fonts_SerenityOS_dot_Net_search_result.png b/Base/usr/share/man/man1/Applications/Fonts_SerenityOS_dot_Net_search_result.png deleted file mode 100644 index 03a95618e40..00000000000 Binary files a/Base/usr/share/man/man1/Applications/Fonts_SerenityOS_dot_Net_search_result.png and /dev/null differ diff --git a/Base/usr/share/man/man1/Applications/GMLPlayground.md b/Base/usr/share/man/man1/Applications/GMLPlayground.md deleted file mode 100644 index ba8310e2a9c..00000000000 --- a/Base/usr/share/man/man1/Applications/GMLPlayground.md +++ /dev/null @@ -1,35 +0,0 @@ -## Name - -![Icon](/res/icons/16x16/app-gml-playground.png) GML Playground - GUI Markup Language (GML) editor - -[Open](file:///bin/GMLPlayground) - -## Synopsis - -```**sh -$ GMLPlayground [file] -``` - -## Arguments - -* `file`: Path of GML file to load - -## Description - -GML Playground facilitates development of graphical user interfaces (GUI) -for Serenity applications using GUI Markup Language (GML) to compose -a layout for GUI widgets and set widget attributes. - -The specified widgets are automatically rendered in a live preview -window, allowing rapid prototyping and development of application GUIs. - -## Examples - -```sh -$ GMLPlayground /home/anon/example.gml -``` - -## See also - -* [`gml-format`(1)](help://man/1/gml-format) For automated GML formatting - diff --git a/Base/usr/share/man/man1/Applications/Help.md b/Base/usr/share/man/man1/Applications/Help.md deleted file mode 100644 index ef51e22699c..00000000000 --- a/Base/usr/share/man/man1/Applications/Help.md +++ /dev/null @@ -1,50 +0,0 @@ -## Name - -![Icon](/res/icons/16x16/app-help.png) Help - digital manual - -[Open](file:///bin/Help) - -## Synopsis - -```**sh -$ Help -$ Help [section] page -$ Help search_query -$ Help file -``` - -## Description - -`Help` is Serenity's digital manual, the GUI counterpart to `man`. -It lets you search for and read manual pages (or "man pages"). - -## Examples - -To open Help: -```sh -$ Help -``` - -To open documentation for the `echo` command: -```sh -$ Help echo -``` - -To open the documentation for the `mkdir` command: -```sh -$ Help 1 mkdir -``` -Conversely, to open the documentation about the `mkdir()` syscall: -```sh -$ Help 2 mkdir -``` - -## Files - -`Help` looks for man pages under `/usr/share/man`. For example, -this man page should be located at `/usr/share/man/man1/Applications/Help.md`. - -## See Also - -* [`man`(1)](help://man/1/man) To read these same man pages from the terminal -* [`man`(7)](help://man/7/man) For an overview on how manpages are organized diff --git a/Base/usr/share/man/man1/Applications/HexEditor.md b/Base/usr/share/man/man1/Applications/HexEditor.md deleted file mode 100644 index fee593bb34e..00000000000 --- a/Base/usr/share/man/man1/Applications/HexEditor.md +++ /dev/null @@ -1,44 +0,0 @@ -## Name - -![Icon](/res/icons/16x16/hex.png) Hex Editor - Binary file editor - -[Open](file:///bin/HexEditor) - -## Synopsis - -```**sh -$ HexEditor [--annotations ] [file] -``` - -## Description - -`Hex Editor` is an application that displays and edits raw and exact file contents. - -Hex Editor abstracts data access into documents, one for memory based streaming (unsaved new files), and another for file based streaming. Hex Editor does not store the entire file in memory, thereby improving load times and efficiently maximizing memory usage. - -![](HexEditor.png) - -### Value Inspector - -The core feature of Hex Editor is value inspector functionality. The inspector operates on current cursor position (or selection start range if one is selected) and interprets bytes moving forward as various data types. The inspector can toggle between big and little endian modes. A value selected in the inspector has its associated bytes that makes up that value gets highlighted in the editor. - -### Find value -One can search using ASCII string or Hex value, the result will be displayed on the right side with corresponding ValueInspector information. - -![](HexEditor_Find_Value.png) - -### Copy value -An option to copy as hex value, as text, or as C-code is available and can extract the file in parts or as a whole. The figure below shows all three output formats transferred into Text Editor. - - -![](HexEditor_Copy_Hex_Text_C_Code.png) - -Hex Editor's simple and straight-forward interface offers search, export, byte pattern insertions and statistics. - -## Options - -* `-a`, `--annotations`: Path to an annotations file to load on startup - -## Arguments - -* `file`: File to open on startup diff --git a/Base/usr/share/man/man1/Applications/HexEditor.png b/Base/usr/share/man/man1/Applications/HexEditor.png deleted file mode 100644 index 943ba0bfce4..00000000000 Binary files a/Base/usr/share/man/man1/Applications/HexEditor.png and /dev/null differ diff --git a/Base/usr/share/man/man1/Applications/HexEditor_Copy_Hex_Text_C_Code.png b/Base/usr/share/man/man1/Applications/HexEditor_Copy_Hex_Text_C_Code.png deleted file mode 100644 index 935fbf511d8..00000000000 Binary files a/Base/usr/share/man/man1/Applications/HexEditor_Copy_Hex_Text_C_Code.png and /dev/null differ diff --git a/Base/usr/share/man/man1/Applications/HexEditor_Find_Value.png b/Base/usr/share/man/man1/Applications/HexEditor_Find_Value.png deleted file mode 100644 index a0c129dd61d..00000000000 Binary files a/Base/usr/share/man/man1/Applications/HexEditor_Find_Value.png and /dev/null differ diff --git a/Base/usr/share/man/man1/Applications/ImageViewer.md b/Base/usr/share/man/man1/Applications/ImageViewer.md deleted file mode 100644 index 1b9fe87af0f..00000000000 --- a/Base/usr/share/man/man1/Applications/ImageViewer.md +++ /dev/null @@ -1,31 +0,0 @@ -## Name - -![Icon](/res/icons/16x16/app-image-viewer.png) Image Viewer - SerenityOS image viewer - -[Open](file:///bin/ImageViewer) - -## Synopsis - -```**sh -$ ImageViewer [file] -``` - -## Description - -ImageViewer is an image viewing application for SerenityOS. - -For user convenience, basic image manipulation facilities exist like image rotation clockwise or counter-clockwise, image flip horizontal or vertical, zoom in, zoom out, zoom reset, fullscreen view and fit image to view. - -File manipulation has no effect on the image. Flip or rotate actions are not saved or committed, it is simply ignored when the application is closed. - -ImageViewer is even smart enough to detect other images and display them when clicking on the navigation buttons or when using direction arrows. ImageViewer can even set the currently loaded image as a Desktop Wallpaper. - -## Arguments - -* `file`: The image file to be displayed. - -## Examples - -```sh -$ ImageViewer /res/graphics/buggie.png -``` diff --git a/Base/usr/share/man/man1/Applications/Magnifier.md b/Base/usr/share/man/man1/Applications/Magnifier.md deleted file mode 100644 index 14b6fb26e51..00000000000 --- a/Base/usr/share/man/man1/Applications/Magnifier.md +++ /dev/null @@ -1,27 +0,0 @@ -## Name - -![Icon](/res/icons/16x16/app-magnifier.png) Magnifier - Magnifier application - -[Open](file:///bin/Magnifier) - -## Synopsis - -```**sh -$ Magnifier -``` - -## Description - -Magnifier is an application that magnifies or zooms-in on the area surrounding your mouse cursor. - -You can launch Magnifier by navigating to `System Menu → Utilities → Magnifier`. - -Magnifier can do 2x, 4x, or 8x zoom in realtime. Quickly switch zoom level by pressing `2`, `4`, or `8`. - -Once you feel that the area being captured is just right, you can pause capture by pressing `Spacebar`. - -To lock the location, making Magnifier stop following your mouse and stay in place, press `L`. - -To view a pixel grid overlay, press `G`. - -For users with slight visual impairment, Magnifier can apply several filters to the captured image. Click on the *Accessibility* menu and select one of the impairment categories available in the drop-down list. diff --git a/Base/usr/share/man/man1/Applications/Mail.md b/Base/usr/share/man/man1/Applications/Mail.md deleted file mode 100644 index 1e054b96dcf..00000000000 --- a/Base/usr/share/man/man1/Applications/Mail.md +++ /dev/null @@ -1,35 +0,0 @@ -## Name - -![Icon](/res/icons/16x16/app-mail.png) Mail - Serenity e-mail client - -[Open](file:///bin/Mail) - -## Synopsis - -```**sh -$ Mail -``` - -## Description - -Mail is an e-mail client for Serenity. It can connect to real e-mail servers. -Currently, a configuration file is required. This must be stored in `~/.config/Mail.ini` -See the Examples section for an example configuration file. - -## Examples - -`~/.config/Mail.ini`: -```ini -[Connection] -Server=email.example.com -Port=993 -TLS=true - -[User] -Username=test@example.com -Password=Example1 -``` - -```sh -$ Mail -``` diff --git a/Base/usr/share/man/man1/Applications/Maps.md b/Base/usr/share/man/man1/Applications/Maps.md deleted file mode 100644 index f181a99fd60..00000000000 --- a/Base/usr/share/man/man1/Applications/Maps.md +++ /dev/null @@ -1,39 +0,0 @@ -## Name - -![Icon](/res/icons/16x16/app-maps.png) Maps - -[Open](file:///bin/Maps) - -## Synopsis - -```**sh -$ Maps -``` - -## Description - -`Maps` is an application for browsing a map of the world. - -The default data provider is [OpenStreetMaps](https://www.openstreetmap.org/), an open-source community-maintained map similar in spirit to Wikipedia. - -## Features -Pan around the map by clicking and dragging. - -Zoom by using the toolbar icons, the mouse wheel or `Ctrl +` to zoom-in and `Ctrl -` to zoom-out. Reset the zoom to a global view with `Ctrl 0`. -Double-clicking anywhere on the map zooms-in to that location. Double-clicking whilst holding `Shift` zooms-out. - -Right-click on a location to: -* Copy its coordinates to the Clipboard -* Save it to your favorites -* Open it in various mapping services -* Center the map on it - -Show and hide the search panel by clicking on the leftmost magnifying glass in the toolbar. Type your query, press `Return` and then click on a result to focus on it in the map. Navigate the search results with the `Up` and `Down` arrow keys. - -Show and hide the favorites panel by clicking on the leftmost heart in the toolbar. You can add favorites with the right click contextmenu. You can edit and delete your favorites by right clicking on them. Navigate your favorites with the `Up` and `Down` arrow keys. - -The default map tile provider can be changed in `Maps Settings`, enabling maps with labels in other languages, different types of map (e.g. topographical) and even setting a custom map. Other tile providers can be found [here](https://wiki.openstreetmap.org/wiki/Raster_tile_providers). - -To see an overlay of where in the world SerenityOS users are, click on the Ladyball icon (the SerenityOS logo) or enable `View → Show SerenityOS Users`. -This shows those who have added their location to the [SerenityOS User Map](https://usermap.serenityos.org). Hover over a marker to reveal the name of a user. Project contributors are distinguished by a blue marker. The total number of users on the map is listed in the top-right. -Add yourself in the [user-map repository](https://github.com/SerenityOS/user-map)! diff --git a/Base/usr/share/man/man1/Applications/MouseSettings.md b/Base/usr/share/man/man1/Applications/MouseSettings.md deleted file mode 100644 index d602e61ee74..00000000000 --- a/Base/usr/share/man/man1/Applications/MouseSettings.md +++ /dev/null @@ -1,23 +0,0 @@ -## Name - -![Icon](/res/icons/16x16/app-mouse.png) Mouse Settings - Mouse settings application - -[Open](file:///bin/MouseSettings) - -## Synopsis - -```**sh -$ MouseSettings -``` - -## Description - -Mouse Settings is an application that displays advanced mouse properties for user configuration. - -Under the _Mouse_ tab, the user can set mouse cursor speed, scroll wheel step size increments, double click speed and button configuration to switch primary and secondary buttons (for left-handed users). - -The _Cursor Theme_ tab provides the user with drop-down list of available default cursor themes, Default and Dark. - -Supplemental cursors are available via the `serenity-theming` port. Some supplemental cursor themes are Durrque, Chillychilly, Jakande, Vanliga and Vanliga-Dark, among others. - -The _Cursor Highlight_ tab allows the user to change highlight color, opacity and size. The global keyboard shortcut to enable and disable cursor highlighting is Super+H. diff --git a/Base/usr/share/man/man1/Applications/PixelPaint.md b/Base/usr/share/man/man1/Applications/PixelPaint.md deleted file mode 100644 index 9fc0fafefb2..00000000000 --- a/Base/usr/share/man/man1/Applications/PixelPaint.md +++ /dev/null @@ -1,183 +0,0 @@ -## Name -![Icon](/res/icons/16x16/app-pixel-paint.png) Pixel Paint - Image Editor - -[Open](file:///bin/PixelPaint) - -## Synopsis -```sh -$ PixelPaint [file] -$ pp -``` - -## Description -`Pixel Paint` is a feature-rich image editing application inspired by the likes of classic [Microsoft Paint](https://en.wikipedia.org/wiki/Microsoft_Paint#Windows_9x), though includes many more advanced features such as tabs, layers and filters. - -Documents can be saved as Pixel Paint Files (`.pp`), preserving layers and images. - -The supported export formats are BMP, PNG and QOI ([Quite Okay Image Format](https://qoiformat.org)). - -## User Interface -![Pixel Paint Interface](PixelPaint.png) - -The interface follows familiar conventions, making it easy for users experienced with other image editors to pick up. Most options are accessible through the Menu bar and are searchable with `Ctrl+Shift+A`. - -### Toolbar -The toolbar contains common actions such as creating, opening and saving a file. It also provides quick access to useful zoom levels (*Fit to width*, *Fit to height* and *Fit entire image*). - -#### Levels -![Levels Window](PixelPaint_Levels.png) - -The last icon on the toolbar opens the levels adjustment window. Levels control the exposure (light) in an image. -* **Brightness** - Lighten or darken all colors. -* **Contrast** - Increase or decrease the difference between light and dark areas. -* **Gamma** - Redistribute light intensities, allowing adjustments that can bring a digital image closer to the way human eyes perceive details, particularly in darker areas. - -### Main Workspace -* The tab bar above the canvas allows multiple documents to be open simultaneously. -* Surrounding the canvas is a vertical and horizontal ruler. When the Guide Tool is active, dragging from these rulers creates a line guide. -* In the center is the Canvas in which you can paint, create shapes, write text and manipulate images. Use the left mouse-button to paint with the primary color and the right mouse-button to paint with the secondary color. Zoom in/out with `Ctrl +` / `Ctrl -` or use the mouse wheel. Pan with the middle-mouse button. Zooming-in far enough reveals a pixel grid which is useful for precise work, such as making pixel art. - -### Color Panel -![Color Panel](PixelPaint_Color_Panel.png) - -Below the canvas is the Color Panel. On the left is the Color Picker, showing the currently selected colors. The inner color is the primary color, the outer color is the secondary color. Click on either to change its color. -To the right of the Color Picker is a palette of default colors. Left-click on one to set it as the primary color and right-click to set as the secondary color. Switch primary and secondary colors by pressing `X`. Reset to default colors (black and white) by pressing `D`. -In the *Edit* menu, palettes can be saved and loaded as `.palette` files. These are text files containing [Hex Color Codes](https://en.wikipedia.org/wiki/Web_colors#Hex_triplet), arranged by line from the top-left color going across. - -### Status Bar -Below the Color Panel is the Status Bar. It displays the pixel coordinates of the cursor when it hovers over the Canvas. When the cursor hovers over the program's interface it describes what a button does. - -### Tool Panel -To the left of the main workspace is the Tool Panel which showcases tools available for interacting with the canvas. When a tool is selected, using `[` / `]` adjusts the primary option in the Tool properties panel, e.g. *Size* for the Brush Tool. Adding `Shift` will adjust the secondary option, e.g. *Hardness* for the Brush Tool. -*Hardness* refers to how sharp or abrupt the transition is between painted elements and the background, affecting edge clarity. *Feathered edges* have a softer transition with a gradual decrease in intensity, creating a smooth and blended appearance contrasting with the sharpness of higher hardness. - -#### Move Tool (`M`) -Moves layers. Select layers either in the Layers Panel or on the canvas. Layers below the top layer can be selected on the canvas if the top layer doesn't fill the entire canvas. - -#### Pen Tool (`N`) -Paints with a square brush, by default 1px with 100% hardness. Increasing the thickness results in a larger square with 100% hardness. Useful for making pixel art. To draw a straight line, make a mark, hold `Shift` and click elsewhere. - -#### Brush Tool (`P`) -Paints with a round brush, by default 20px with feathered edges. Size and Hardness can be adjusted. Useful for a more natural appearance. To draw a straight line, make a mark, hold `Shift` and click elsewhere. - -#### Bucket Tool (`Shift+B`) -Will fill a closed shape, otherwise it will fill the whole layer. - -#### Spray Tool (`Shift+S`) -Creates a textured effect by scattering pixels randomly within the round brush. The size of and density (how closely packed the pixels are painted) of the spray can be adjusted. Useful for creating fading effects. - -#### Pick Tool (`O`) -Will make any selected color the primary color. By default, it samples only the active layer unless the option *Sample all layers* is selected. Quickly use at any time by holding `Alt`. - -#### Erase Tool (`Shift+E`) -Erases pixels from the current layer. Has two modes: Pencil (square brush with 100% hardness) and Brush (circular with adjustable hardness). Enabling *Use secondary color* will apply the secondary color instead of making that part of the layer transparent. - -#### Line Tool (`Ctrl+Shift+L`) -Creates a vector line that is rasterized (converted into a pixel equivalent) upon releasing the mouse. To constrain the angle to 22.5° increments hold `Shift` whilst drawing the line. - -#### Rectangle Tool (`Ctrl+Shift+R`) -Draws a vector rectangle that is rasterized upon releasing the mouse. To draw a square hold `Shift` whilst drawing the shape. - -Options: -* **Outline** - Rectangle with no fill. The line thickness can be adjusted. -* **Fill** - Filled with the primary color. -* **Gradient** - Filled with a gradient between the primary and secondary colors, from left-to-right. -* **Rounded** - Draws a vector square with rounded corners that is rasterized upon releasing the mouse. Adjust the corner radius before drawing, as it cannot be changed afterward. -* **Anti-alias** - Smoothens jagged edges for a more polished appearance. -* **Aspect ratio** - Sets the ratio of width and height (e.g. `1` x `1` for a square). - -#### Ellipse Tool (`Ctrl+Shift+E`) -Draws a vector ellipse that is rasterized upon releasing the mouse. To draw a circle hold `Shift` whilst drawing the shape. Has many of the same options as the Rectangle Tool. - -#### Text Tool (`Ctrl+Shift+T`) -Click to place the text and start typing. Use `Shift+Enter` to create a new line. Press `Esc` to cancel. The text will use the primary color. Choosing a different color will change the color of the text. Modify the font and font size in the Tool properties. Adjust the text's position while editing. Once you press `Enter` or click away from the text, it will be rasterized and become non-editable. - -#### Zoom Tool (`Z`) -Left-click to zoom-in, right-click to zoom-out. Adjust the increment size in the Tool properties. - -#### Selection Tools -Select All with `Ctrl+A`. Press `Esc` to clear a selection. Invert a selection in the *Edit* menu. - -Modes: -* **Set** - Making an initial selection. -* **Add** - Expands the selection area with each new selection, including merging areas. -* **Subtract** - Removes areas from selection. -* **Intersect** - Keeps the overlap with other selections. - -##### Rectangle Select Tool (`R`) -Selects a rectangular-shaped area. Holding `Ctrl` will expand the selection equally on all sides. - -##### Wand Select Tool (`W`) -Selects an area based on its color. Adjust the threshold for a more or less precise selection. - -##### Polygonal Select Tool (`Shift+P`) -Click to draw the vertices of a polygon. Hold `Shift` to constrain to 22.5° increments. It will become a selected area once a whole shape. Quickly close a shape by double-clicking. *Feathering* can be adjusted, which in this context will give rounded corners. - -##### Lasso Select Tool (`L`) -Freehand draw a shape, which will become a selected area once whole. Letting go of the mouse will automatically close the shape. - -#### Guide Tool (`G`) -Create a guide line by dragging from either of the rulers surrounding the Canvas. Hold `Shift` whilst dragging to do this in 10px increments (adjustable in the Tool properties). Existing guides can be moved. Right-click on a guide to delete it or open the *Set Offset* menu to modify its orientation and offset (the distance in pixels from the top or left-hand ruler). Useful for maintaining alignment when editing. Guide lines are not visible on export. - -#### Clone Tool (`C`) -Hold the `Alt` key and click to specify the area to clone (indicated by a green circle). Then paint the clone. Currently it can only clone from the same layer. Size and Hardness of the brush can be adjusted. - -#### Gradient Tool (`Ctrl+G`) -Click and drag to create a gradient, transitioning from the primary color to transparent by default. Change it to go from the primary to the secondary color in the Tool properties. Adjust the colors in the Color Panel. Move the gradient by grabbing the middle handle and change the size and orientation with the outer handles. Hold `Shift` to constrain to a horizontal or vertical orientation. Press `Enter` to rasterize or `Esc` to cancel. - -### Editor Panels -To the right of the main workspace, editor panels facilitate layer configuration, tool adjustments and provide information about the colors in an image. Minimize a panel by double-clicking its name label or using the toggle. Rearrange panels by dragging the name label. Turn them into resizable windows by dragging them out or pressing the detach icon. Turn all of them into a separate window by dragging the *Editor Panels* label out or pressing its detach icon. Turn a detached window back into a panel by closing the window. - -The default panels are: -* **Layers** - Add, remove, reorder or manipulate layers. Adjust layers by right-clicking or in the *Layers* menu. - * **Masks** - An overlay that controls a layer's visibility: black hides, white shows and shades of gray provide varying levels of transparency. Masks are non-destructive, allowing reversible edits without permanently altering the original image. Return to the mask menu to delete (completely remove a mask), apply (merge the mask and the underlying layer), invert (reverse the black and white contents) and clear (retain an empty mask layer). -* **Layer properties** - Rename, show/hide or adjust the opacity of a layer. -* **Tool properties** - Configure the settings for the currently selected tool. - -#### Color Visualizations -![Color Visualizations Panels](PixelPaint_Color_Visualizations.png) - -Two additional editor panels that visualize color can be enabled in *View → Scopes*: -* **Histogram** - A graph showing the range of color tones in an image, from the darkest on the left to the lightest on the right. As the cursor hovers over the canvas, a yellow line indicates where the pixels under the cursor are represented on the graph. -* **Vectorscope** - Corresponding to the [color wheel](https://en.wikipedia.org/wiki/Color_wheel), the markings indicate the degree of Hue and Saturation in an image. The further the marking from the center, the greater the Saturation. The line going up between red and yellow is the Skin Tone Line indicating the optimal location for all skin tones. As the cursor hovers over the canvas, a circle highlights where the colors are represented on the Vectorscope. - -### Filters -![Filter Gallery Window](PixelPaint_Filter_Gallery.png) - -In the *Filter* menu is the Filter Gallery which enables adjustment of the whole image (by default), or a selection, by tweaking colors or applying effects. - -#### Artistic -* **Bloom** - Adds a glow around bright areas. Adjust the glow area brightness threshold and the radius of the glow. - -#### Edge Detection -Edge detection highlights transitions between visual elements, emphasizing object boundaries. There are two different methods of achieving this: -* **Laplacian Cardinal** - Emphasizes horizontal and vertical edges, accentuating areas with significant intensity changes along these cardinal directions. -* **Laplacian Diagonal** - Emphasizes and highlights diagonal edges in an image, focusing on areas with pronounced intensity changes along diagonal directions. - -#### Blur & Sharpen -Various methods exist for producing a blur, leading to varied results. They differ in how they allocate weight, which represents the value assigned to pixels determining their greater or lesser impact on the final image. The Gaussian and Box Blurs offer two choices: `3x3` and `5x5`, indicating the grid size for blur calculation. In simple terms, a larger grid results in a smoother blur. -* **Fast Box Blur** - Quickly blurs an image by averaging the colors of pixels within a defined square area, resulting in a smoother appearance. The blur's radius can be adjusted. Enable *Asymmetric Radii* to adjust the blur along the horizontal, vertical or both axes. Enable *Use Direction and Magnitude* to adjust the blur angle and intensity. The *Approximate Gaussian Blur* option achieves a smoother blur by applying the box blur multiple times with different weights. -* **Gaussian Blur** - Smoothens an image by gradually blending pixel colors based on a bell-shaped distribution, resulting in a softened appearance. -* **Box Blur** - Takes more time than a Fast Box Blur by considering a broader range of neighboring pixels, resulting in a more thorough and refined smoothing. -* **Sharpen** - Enhances the clarity and detail of an image by increasing the contrast at edges, making them more distinct. -* **Median Filter** - Replaces each pixel's value with the median value of its neighboring pixels, providing effective noise reduction while preserving edges and details. Useful for removing dust or defective pixels from an image. - -#### Color -* **Hue/Saturation** - Precisely adjust the colors in an image by modifying the type of color, its intensity and the amount of black/white mixed in with it. - * **Hue** - The type of color, based on degrees of the color wheel starting from 0% (red). - * **Saturation** - The purity of the color, from 0% (black) to 100% (pure color). - * **Lightness** - The amount of black/white mixed in with the color, from 0% (black) to 100% (white). -* **Grayscale** - Converts an image to black and white by removing the color information, resulting in shades of gray based on the original colors' intensity. -* **Invert** - Transforms an image by reversing the colors, turning dark areas light and vice versa. -* **Sepia** - Makes an image black and white while adding warm, brownish tones, creating a nostalgic or aged appearance reminiscent of old photographs. The intensity can be adjusted. - -#### Generic 5x5 Convolution -The last option in the *Filter* menu, a Generic 5x5 Convolution filter alters an image by using a 5x5 grid of numbers to recalculate each pixel's appearance based on its nearby pixels. The *Normalize* option keeps the brightness consistent, maintaining a balanced and normalized appearance. The *Wrap* options makes sure calculations consider pixels on the other side, especially at the image edges, for a smoother effect. Without wrapping, the convolution might produce artifacts or irregularities at the image borders. - -## Arguments -* `file`: The image file to be edited - -## Example -```sh -$ PixelPaint /home/anon/Documents/cat.jpg -``` diff --git a/Base/usr/share/man/man1/Applications/PixelPaint.png b/Base/usr/share/man/man1/Applications/PixelPaint.png deleted file mode 100644 index 38cbc836e37..00000000000 Binary files a/Base/usr/share/man/man1/Applications/PixelPaint.png and /dev/null differ diff --git a/Base/usr/share/man/man1/Applications/PixelPaint_Color_Panel.png b/Base/usr/share/man/man1/Applications/PixelPaint_Color_Panel.png deleted file mode 100644 index 416f60c709c..00000000000 Binary files a/Base/usr/share/man/man1/Applications/PixelPaint_Color_Panel.png and /dev/null differ diff --git a/Base/usr/share/man/man1/Applications/PixelPaint_Color_Visualizations.png b/Base/usr/share/man/man1/Applications/PixelPaint_Color_Visualizations.png deleted file mode 100644 index 83c95263bc4..00000000000 Binary files a/Base/usr/share/man/man1/Applications/PixelPaint_Color_Visualizations.png and /dev/null differ diff --git a/Base/usr/share/man/man1/Applications/PixelPaint_Filter_Gallery.png b/Base/usr/share/man/man1/Applications/PixelPaint_Filter_Gallery.png deleted file mode 100644 index e04c8fb3e2b..00000000000 Binary files a/Base/usr/share/man/man1/Applications/PixelPaint_Filter_Gallery.png and /dev/null differ diff --git a/Base/usr/share/man/man1/Applications/PixelPaint_Levels.png b/Base/usr/share/man/man1/Applications/PixelPaint_Levels.png deleted file mode 100644 index 4fd6d00272b..00000000000 Binary files a/Base/usr/share/man/man1/Applications/PixelPaint_Levels.png and /dev/null differ diff --git a/Base/usr/share/man/man1/Applications/Presenter.md b/Base/usr/share/man/man1/Applications/Presenter.md deleted file mode 100644 index 064a380678e..00000000000 --- a/Base/usr/share/man/man1/Applications/Presenter.md +++ /dev/null @@ -1,42 +0,0 @@ -## Name - -![Icon](/res/icons/16x16/app-presenter.png) Presenter - Present slides to an audience - -[Open](file:///bin/Presenter) - -## Synopsis - -```**sh -$ Presenter [file] -``` - -## Description - -Presenter is a simple slide presentation application, capable of displaying presentations stored in a simple format. It provides a simple user interface that is specifically aimed at "getting out of the way" while you are giving a presentation to a live audience. - -### Opening Files - -Use `File → Open…` to open a presentation in Presenter, or specify it in the command line (see [Synopsis](#synopsis)). The file format currently understood by Presenter is based on JSON and explained in [presenter(5)](help://man/5/presenter). - -### Terminology - -- The **display area** is the window of Presenter, or the entire screen in full screen mode, where the presentation is visible. -- A **slide** is a single page of the presentation and the most top-level structure. -- A **frame** is a possibly animated step within a slide. - -### Controlling the Presentation - -During the presentation, the following keybindings are always available, though there are also corresponding menu options. Some of these keybindings mirror the functionality of other presentation software, though they don't usually behave exactly the same. - -- `Right`, `Down`, `Space`, `Enter`: Next slide or frame -- `Left`, `Up`, `Backspace`: Previous slide or frame -- `B`: Black-out display. Going forward or back a slide or frame or pressing Escape will disable the black-out and resume the presentation. -- `W`: White-out display. The behavior is identical to black-out. -- `F11`, `Shift + F5`: Toggle full-screen mode. -- `Escape`: Exit full-screen mode. -- `F5`: Return to first slide and enter full-screen mode. -- Typing a number followed by Enter will go to the first frame of that slide. - -## See Also - -- [presenter(5)](help://man/5/presenter) for the file format diff --git a/Base/usr/share/man/man1/Applications/Profiler.md b/Base/usr/share/man/man1/Applications/Profiler.md deleted file mode 100644 index 7b60a30e108..00000000000 --- a/Base/usr/share/man/man1/Applications/Profiler.md +++ /dev/null @@ -1,53 +0,0 @@ -## Name - -![Icon](/res/icons/16x16/app-profiler.png) Profiler - Serenity process profiler - -[Open](file:///bin/Profiler) - -## Synopsis - -```**sh -$ Profiler [--pid PID] [perfcore-file] -``` - -## Description - -Profiler facilitates process performance profiling and provides a GUI offering -visual graph and tree representations to easily navigate generated profiling -information. - -If no arguments are provided, a window containing a list of running processes -is presented, allowing a process to be selected for profiling. - -Profiling information is written to `perfcore.` in the working directory -and opened immediately for browsing following termination of profiling. - -Profiler can also load performance information from previously created -`perfcore` files. - -## Options - -* `-p PID`, `--pid PID`: PID to profile - -## Arguments - -* `perfcore-file`: Path of perfcore file to load - -## Examples - -Profile running Shell process: - -```sh -$ Profiler -p $(pidof Shell) -``` - -Open a previously created perfcore file for browsing: - -```sh -$ Profiler perfcore.123 -``` - -## See also - -* [`perfcore`(5)](help://man/5/perfcore) -* [`profile`(1)](help://man/1/profile) diff --git a/Base/usr/share/man/man1/Applications/SQLStudio.md b/Base/usr/share/man/man1/Applications/SQLStudio.md deleted file mode 100644 index 626b05c2d62..00000000000 --- a/Base/usr/share/man/man1/Applications/SQLStudio.md +++ /dev/null @@ -1,28 +0,0 @@ -## Name - -![Icon](/res/icons/16x16/app-sql-studio.png) SQL Studio - SerenityOS SQL Manager - -[Open](file:///bin/SQLStudio) - -## Synopsis - -```**sh -$ SQLStudio [script.sql | database.db] -``` - -## Description - -SQL Studio is a graphical SQL manager program that allows the user to create and edit -SQL scripts. - -## Arguments - -* `script.sql`: SQL script to open, edit or run -* `database.db`: SQL database to open and run scripts against - -## Examples - -```sh -$ SQLStudio -$ SQLStudio /home/anon/Documents/sql/insert_values.sql -``` diff --git a/Base/usr/share/man/man1/Applications/Screenshot.md b/Base/usr/share/man/man1/Applications/Screenshot.md deleted file mode 100644 index 6a5f5ac741a..00000000000 --- a/Base/usr/share/man/man1/Applications/Screenshot.md +++ /dev/null @@ -1,28 +0,0 @@ -## Name - -![Icon](/res/icons/16x16/app-screenshot.png) Screenshot - -[Open](file:///bin/Screenshot) - -## Synopsis - -```**sh -$ Screenshot -``` - -## Description - -`Screenshot` is an application for taking screenshots. - -Its interface remains invisible in screenshots, ensuring a clean capture. - -## Options - -* **Whole desktop** - Capture the entire user interface, including the cursor if it's visible on the desktop. -* **Selected area** - Click and drag to select a specific area. Release the mouse button to take a screenshot. The cursor will not be included. -* **Edit in Pixel Paint** - Open the screenshot as a new image in [Pixel Paint](help://man/1/Applications/PixelPaint) for quick annotations. -* **Select Folder** - Customize the destination folder for saving screenshots. By default, they are saved in the *Pictures* folder. - -## See Also - -* [`shot`(1)](help://man/1/shot) Command line screenshot tool with more options diff --git a/Base/usr/share/man/man1/Applications/Terminal.md b/Base/usr/share/man/man1/Applications/Terminal.md deleted file mode 100644 index 81335ce943e..00000000000 --- a/Base/usr/share/man/man1/Applications/Terminal.md +++ /dev/null @@ -1,47 +0,0 @@ -## Name - -![Icon](/res/icons/16x16/app-terminal.png) Terminal - Serenity terminal emulator - -[Open](file:///bin/Terminal) - -## Synopsis - -```**sh -$ Terminal [options] -``` - -## Description - -Terminal is a terminal emulator application for Serenity. - -It can be launched from the System Menu or the quick access icon to its right, via the `Open in Terminal` action in File Manager and on the Desktop. You can also click on the `Open` link above to launch Terminal. - -Select `File → Terminal Settings` to launch the Terminal Settings dialog and display user configurable application properties. This dialog box contains two tabs: View and Terminal. - -The *View* tab provides the most frequently sought options: -* Adjust the Terminal font (turn off `Use system default` to select a custom font. -* Specify background opacity, i.e. the amount to which the Terminal's background is transparent, displaying what's underneath. -* Change the shape of the cursor from Block, to Underscore or to Vertical bar. You can also opt to enable or disable cursor's blink property. -* To enable or disable the display of terminal scrollbar. - -The *Terminal* tab gives less frequently used options: -* To either enable System beep, or use Visual bell or disable bell mode altogether. -* To change Terminal's exit behavior - -Clicking on the *Apply* button will cause the currently selected options to take effect immediately. - -You can toggle Fullscreen mode by pressing F11. - -## Options - -* `--help`: Display help message and exit -* `--version`: Print version -* `-e`: Execute this command inside the terminal -* `-k`: Keep the terminal open after the command has finished executing - -## Examples - -```sh -$ Terminal -e Shell -$ Terminal -k -e Browser -``` diff --git a/Base/usr/share/man/man1/Applications/TextEditor.md b/Base/usr/share/man/man1/Applications/TextEditor.md deleted file mode 100644 index c58cf310f87..00000000000 --- a/Base/usr/share/man/man1/Applications/TextEditor.md +++ /dev/null @@ -1,31 +0,0 @@ -## Name - -![Icon](/res/icons/16x16/app-text-editor.png) TextEditor - SerenityOS text editor - -[Open](file:///bin/TextEditor) - -## Synopsis - -```**sh -$ TextEditor [--preview-mode mode] [file[:line[:column]]] -``` - -## Description - -TextEditor is a text document editor for SerenityOS featuring a preview mode -which allows automatic live rendering of HTML and Markdown documents. - -## Options - -* `--preview-mode mode`: Preview mode, one of 'none', 'html', 'markdown', 'auto' - -## Arguments - -* `file[:line[:column]]`: File to edit, with optional starting line and column number - -## Examples - -```sh -$ TextEditor /home/anon/Documents/emoji.txt -$ TextEditor /home/anon/Documents/emoji.txt:5:12 -``` diff --git a/Base/usr/share/man/man1/Shell.md b/Base/usr/share/man/man1/Shell.md deleted file mode 100644 index 805b28eb66e..00000000000 --- a/Base/usr/share/man/man1/Shell.md +++ /dev/null @@ -1,49 +0,0 @@ -## Name - -Shell - command language interpreter - -## Synopsis - -```**sh -$ Shell [--skip-shellrc] [--live-formatting] -$ Shell [--skip-shellrc] command_file [arguments...] -$ Shell [--skip-shellrc] -c command_string [arguments...] -$ Shell [--skip-shellrc] --format command_file -``` - -## Description - -The `Shell` utility is a command language interpreter, which reads commands from either a command string, a specified file, or the standard input. -The command language shall be described in [`Shell`(5)](help://man/5/Shell), _The Shell Command Language_. - -Any extra arguments passed into `arguments` are placed in the local variable `$ARGV` and can also be accessed through the special variable `$*`. - -**NOTE**: - -The `Shell` utility does not promise POSIX `sh` interoperability. - -## Options - -* `-c`, `--command-string`: Executes the given string as a command and exits -* `--skip-shellrc`: Skips running the initialization file (at `~/.shellrc`) -* `--format`: Format shell code from the given file and print the result to standard output -* `-f`, `--live-formatting`: Enable live formatting of the line editor buffer (in REPL mode) -* `--keep-open`: Keep the shell open after running the specified command or file -* `--posix`: Behave like a POSIX-compatible shell - -## Examples - -```sh -# Start an interactive REPL, ignoring the shellrc -Shell --skip-shellrc - -# Execute a given string -Shell -c 'rm foo*' - -# Execute the contents of a file with some arguments -Shell foo a b c -``` - -## See also - -* [`Shell-vars`(7)](help://man/7/Shell-vars) For details on local and environment variables used by the shell diff --git a/Base/usr/share/man/man1/abench.md b/Base/usr/share/man/man1/abench.md deleted file mode 100644 index cb336d7f717..00000000000 --- a/Base/usr/share/man/man1/abench.md +++ /dev/null @@ -1,30 +0,0 @@ -## Name - -abench - benchmark audio decoders - -## Synopsis - -```**sh -$ abench [--sample-count samples] -``` - -## Description - -This program can be used to benchmark the performance of audio decoder plugins in LibAudio. It reports the raw decoding speed that is achieved on the given input file, without any overhead from resampling or actually playing the file. It is not only useful for benchmarking the decode speed of the file and/or profiling decoders, but also for checking conformance with (quirky) files. - -While `abench` is running, it doesn't report anything to make measurements more accurate. After running, abench reports sample count, loader runtime, µs/sample, realtime speed and (for reference) realtime µs/Sample. "Realtime speed" refers to how much faster the loader is compared to playing the file, and "realtime µs/sample" then refers to the amount of time each sample normally takes up when played back. When realtime speed is over 100%, it means that the loader can load the file while it is playing at the same time. - -## Options - -* `-s`, `--sample-count`: How many samples to load at maximum. This allows you to only benchmark some initial chunk of the file, which is useful when testing on quirky files that happen to be large. - -## Arguments - -* `path`: Path to audio file. As usual, the file type is determined automatically. - -## Examples - -```sh -$ abench ~/sound.flac -$ abench -s 20000 ~/music.flac -``` diff --git a/Base/usr/share/man/man1/aconv.md b/Base/usr/share/man/man1/aconv.md deleted file mode 100644 index 62ac45bc466..00000000000 --- a/Base/usr/share/man/man1/aconv.md +++ /dev/null @@ -1,67 +0,0 @@ -## Name - -aconv - convert between audio formats - -## Synopsis - -```**sh -$ aconv -i input [--input-audio-codec input-codec] [--audio-codec output-codec] [--audio-format sample-format] -o output -``` - -## Description - -`aconv` converts between different audio formats, or more precisely, between containers and codecs. `aconv` can both read from and write to the standard input and output streams. Depending on the audio format used, `aconv` may or may not act as a streaming converter; that is, `aconv` may require to read until EOF of the input file to start writing to the output. More details can be found with the encoders and decoders that `aconv` supports. - -The input file option is always required. `aconv` will try to guess which container and codec the input has, unless the `--input-audio-codec` option is provided. Note that guessing the codec is not possible for raw formats such as raw PCM streams, therefore such formats always require specifying the input codec option. - -The output file may be omitted, in which case `aconv` does not write any data. If an output codec is provided via `--audio-codec`, `aconv` will internally still perform the conversion without writing data. If `--audio-codec` is not provided, `aconv` will decode the input and not do anything else. It is recommended that [`abench`](help://man/1/abench) be used for audio input testing purposes. - -`aconv` will try to guess the output container and codec based on the file name specified. The codec can be overwritten with the aforementioned `--audio-codec` option; this is mandatory for the standard output stream where the container and codec cannot be guessed. - -By specifying `--audio-format`, `aconv` will use a different sample format for the output than what the input file provides. The sample format is the format of the PCM stream that is encoded with the codec, and it specifies multiple parameters such as bit depth, data type, and endiannness. The supported sample formats depend on the codec, but they have common names shared across codecs. - -### Supported Codecs and Containers - -Note that `aconv` currently only supports codecs which have their own bespoke container. Therefore, the distinction does not currently matter. The names given below are the only recognized names for this codec for the command line options `--audio-codec` and `--input-audio-codec`. Some codecs can only be decoded or both encoded and decoded. - -* `mp3` (decode): MPEG Layer III audio codec and container. -* `wav` (decode, encode): RIFF WAVE audio codec and container. Supports sample formats `u8` and `s16le` for writing. -* `flac` (decode, encode): Free Lossless Audio Codec and container. Supports all integer sample formats for writing. -* `qoa` (decode): Quite Okay Audio codec and container. - -### Supported Sample Formats - -* `u8`: Unsigned 8-bit integer -* `s16le`: Signed 16-bit integer, little endian -* `s24le`: Signed 24-bit integer, little endian -* `s32le`: Signed 32-bit integer, little endian -* `f32le`: 32-bit IEEE 754 floating-point number (normalized to the range [-1, 1]), little endian -* `f64le`: 64-bit IEEE 754 floating-point number (normalized to the range [-1, 1]), little endian - -## Options - -The option format follows this general pattern: `--input_or_output-stream-parameter`, where `input_or_output` is either `input` when concerning the input stream, or omitted for the output stream (since this is the more common use case), `stream` currently is always `audio`, and `parameter` is the parameter of the stream that is changed. - -* `-i`, `--input`: The input file to convert from. Use `-` to read from standard input. -* `-o`, `--output`: The output file to write to. Use `-` to write to standard output. -* `--input-audio-codec`: Overwrite the used codec and/or sample format of the input file. -* `--audio-codec`: The codec to use for the output file. -* `--audio-format`: The sample format to use for the output file. - -## Examples - -```sh -# Decode a FLAC file to WAV -$ aconv -i ~/sound.flac -o ~/sound.wav - -# Decode an MP3 file stored in a metadata block of a FLAC file to WAV -$ aconv -i ~/has-mp3-contents.flac --input-audio-codec mp3 -o ~/weird.wav - -# Recode WAV to 8-bit and output it to stdout -$ aconv -i ~/music.wav --audio-format u8 -o - -``` - -## See Also - -* [`abench`(1)](help://man/1/abench) to test audio decoders and measure their performance -* [`aplay`(1)](help://man/1/aplay) to play audio files from the command line diff --git a/Base/usr/share/man/man1/adjtime.md b/Base/usr/share/man/man1/adjtime.md deleted file mode 100644 index 463899ab778..00000000000 --- a/Base/usr/share/man/man1/adjtime.md +++ /dev/null @@ -1,36 +0,0 @@ -## Name - -adjtime - print remaining system clock adjustment, and optionally set it - -## Synopsis - -```**sh -$ adjtime [options...] -``` - -## Description - -`adjtime -s delta_seconds` will smoothly adjust the system time by slowing it -down (if `delta_seconds` is negative) or speeding it up (if `delta_seconds` is -positive) by a fraction of a second. The larger `delta_seconds` is, the longer -this takes. If `delta_seconds` is set and a previous time adjustment is in -progress, the remaining adjustment is canceled. That is, if `adjtime -s 1` is -called, and then `adjtime -s 1` is called again later when only 0.3s of the -first `adjtime` call have been applied yet, the clock is adjusted by 1.3 -seconds total, not by 2 seconds. - -`adjtime` also prints the remaining system clock adjustment. - -## Options - -* `-s delta_seconds`, `--set delta_seconds`: Adjust system time by - `delta_seconds`. Must be superuser. - -## Examples - -```sh -# adjtime -s 4.2 -4.2 -# sleep 1 && adjtime -4.1 -``` diff --git a/Base/usr/share/man/man1/allocate.md b/Base/usr/share/man/man1/allocate.md deleted file mode 100644 index 5f43506be78..00000000000 --- a/Base/usr/share/man/man1/allocate.md +++ /dev/null @@ -1,99 +0,0 @@ -## Name - -allocate - allocate memory - -## Synopsis - -```**sh -$ allocate [--unit B/KiB/MiB/GiB] [--sleep-time N] [number] -``` - -## Description - -`allocate` allocates a specific amount of virtual memory. If nothing is specified -then it will allocate 100 bytes of memory. -If `number` is specified without `unit`, it will default to `number` of bytes. -It also writes to each allocated page and then sleeps for N seconds (by default 10). -It is primarily used to test the kernel's memory management capabilities. - -## Options - -* `-u`, `--size-unit`: Allocation's Size Unit (Base 2 units - B, KiB, MiB or GiB) -* `-n`, `--sleep-time`: Number of seconds to sleep before freeing memory - -## Examples - -```sh -$ allocate 500 -allocating memory (500 bytes)... -done in 0ms -writing one byte to each page of allocated memory... -done in 0ms -sleeping for 10 seconds... -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -done. -freeing memory... -done in 0ms - -$ allocate 500 -u KiB -allocating memory (512000 bytes)... -done in 0ms -writing one byte to each page of allocated memory... -step took 1ms (46.875MiB/s) -step took 1ms (46.875MiB/s) -step took 1ms (46.875MiB/s) -step took 1ms (46.875MiB/s) -step took 1ms (46.875MiB/s) -step took 1ms (46.875MiB/s) -step took 1ms (46.875MiB/s) -step took 1ms (46.875MiB/s) -step took 1ms (46.875MiB/s) -step took 1ms (46.875MiB/s) -done in 4ms -sleeping for 10 seconds... -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -done. -freeing memory... -done in 0ms - -$ allocate -u KiB -n 2 500 -allocating memory (512000 bytes)... -done in 0ms -writing one byte to each page of allocated memory... -step took 1ms (46.875MiB/s) -step took 1ms (46.875MiB/s) -step took 1ms (46.875MiB/s) -step took 1ms (46.875MiB/s) -step took 1ms (46.875MiB/s) -step took 1ms (46.875MiB/s) -step took 1ms (46.875MiB/s) -step took 1ms (46.875MiB/s) -step took 1ms (46.875MiB/s) -step took 1ms (46.875MiB/s) -done in 0ms -sleeping for 2 seconds... -0 -1 -done. -freeing memory... -done in 0ms - -``` diff --git a/Base/usr/share/man/man1/aplay.md b/Base/usr/share/man/man1/aplay.md deleted file mode 100644 index 0c83bf5eabc..00000000000 --- a/Base/usr/share/man/man1/aplay.md +++ /dev/null @@ -1,29 +0,0 @@ -## Name - -aplay - play audio - -## Synopsis - -```**sh -$ aplay [--loop] [--sample-progress] -``` - -## Description - -This program plays an audio file specified in `path` through AudioServer. - -## Options - -* `-l`, `--loop`: Loop playback -* `-s`, `--sample-progress`: Switch to (old-style) sample playback progress. By default, playback is printed as played, remaining and total length, all in minutes and seconds. - -## Arguments - -* `path`: Path to audio file - -## Examples - -```sh -$ aplay ~/sound.wav -$ aplay -l ~/music.flac -``` diff --git a/Base/usr/share/man/man1/arp.md b/Base/usr/share/man/man1/arp.md deleted file mode 100644 index bd0edb6557f..00000000000 --- a/Base/usr/share/man/man1/arp.md +++ /dev/null @@ -1,34 +0,0 @@ -## Name - -arp - Display or modify the system ARP cache - -## Synopsis - -```**sh -# arp -``` - -## Description - -This program run as root displays IP and MAC addresses of devices in local network. - -ARP stands for Address Resolution Protocol, which is used to find devices in local network. - -## Options - -* `-s`, `--set`: Set an ARP table entry -* `-d`, `--delete`: Delete an ARP table entry -* `-n`, `--numeric`: Display numerical addresses. Don't resolve hostnames - -## Arguments - -* `address`: IPv4 protocol address -* `hwaddress`: Hardware address - -## Examples - -```sh -# arp -Address HWaddress -192.168.1.1 52:54:00:12:34:56 -``` diff --git a/Base/usr/share/man/man1/asctl.md b/Base/usr/share/man/man1/asctl.md deleted file mode 100644 index 43c1fe40fdc..00000000000 --- a/Base/usr/share/man/man1/asctl.md +++ /dev/null @@ -1,61 +0,0 @@ -## Name - -asctl - Send control signals to the audio server and hardware - -## Synopsis - -```**sh -$ asctl [--human-readable] [args...] -``` - -## Description - -This program is used to send control signals to the AudioServer and the sound hardware. This allows changing audio server variables like volume and mute state, as well as querying the state of these variables. - -## Options - -* `-h`, `--human-readable`: Print human-readable output. If this option is not given, the output of `get` will be machine-readable and only consist of one line. - -## Arguments - -* `command`: The command to execute, either `get` or `set`. -* `args`: The arguments to the command. - -There are two commands available: `get` reports the state of audio variables, and `set` changes these variables. - -`get` expects a list of variables to report back, and it will report them in the order given. The exact format of the report depends on the `--human-readable` flag. If no variables are given, `get` will report all available variables, in the order that they are listed below. - -`set` expects one or more variables followed by a value to set them to, and will set the variables to the given values. A variable can be given multiple times and the last specified value will remain with the audio server. - -The available variables are: -* `(v)olume`: Audio server volume, in percent. Integer value. -* `(m)ute`: Mute state. Boolean value, may be set with `0`, `false` or `1`, `true`. -* `sample(r)ate`: Sample rate of the sound card. Integer value. - -Both commands and arguments can be abbreviated: Commands by their first letter, arguments by the letter in parenthesis. - -## Examples - -```**sh -Get the current volume (machine format) -$ asctl get volume -100 - -Get all variables -$ asctl -h get -Volume: 100 -Muted: No -Sample rate: 48000 Hz - -Set the volume to 100% -$ asctl set volume 100 - -Mute all audio -$ asctl set mute true - -Unmute all audio, set volume to 80% -$ asctl s m 0 v 80 - -Set sample rate -$ asctl s samplerate 48000 -``` diff --git a/Base/usr/share/man/man1/base64.md b/Base/usr/share/man/man1/base64.md deleted file mode 100644 index c02df51df91..00000000000 --- a/Base/usr/share/man/man1/base64.md +++ /dev/null @@ -1,34 +0,0 @@ -## Name - -base64 - encode and decode to base64 - -## Synopsis - -```**sh -$ base64 [--decode] [--wrap column] [file] -``` - -## Description - -`base64` encodes or decodes to base64 the data in file `file` or from stdin if -file is not specified or file is `-`. - -## Options - -* `-d`, `--decode`: Decode data -* `-w column`, `--wrap column`: When encoding, wrap output after `column` characters - -## Examples - -```sh -# base64 encode the text 'A' -$ echo 'A' | base64 -# base64 encode the content of hi.txt -$ base64 hi.txt -# base64 encode the content of baz.txt and wrap after 4 columns -$ base64 -w 4 baz.txt -# base64 decode the text 'Zm9v' -$ echo 'Zm9v' | base64 -d -# base64 decode the content of foo.txt -$ base64 -d foo.txt -``` diff --git a/Base/usr/share/man/man1/basename.md b/Base/usr/share/man/man1/basename.md deleted file mode 100644 index 5064bf5c18e..00000000000 --- a/Base/usr/share/man/man1/basename.md +++ /dev/null @@ -1,29 +0,0 @@ -## Name - -basename - strip directory names from path - -## Synopsis - -```**sh -$ basename [suffix] -``` - -## Description - -`basename` prints basename (filename, stripped of preceding directory names) of specified `path` to standard output. The path does not have to exist. - -## Arguments - -* `path`: The path which we want to get basename of -* `suffix`: Suffix to strip from name - -## Examples - -```sh -$ basename / -/ -$ basename ~ -anon -$ basename Source/js/array.js -array.js -``` diff --git a/Base/usr/share/man/man1/beep.md b/Base/usr/share/man/man1/beep.md deleted file mode 100644 index dd08bb4f058..00000000000 --- a/Base/usr/share/man/man1/beep.md +++ /dev/null @@ -1,38 +0,0 @@ -## Name - -beep - beep the pc speaker - -## Synopsis - -```**sh -$ beep -``` - -## Description - -beep allows the user to beep the PC speaker. - -## Options - -* `-f frequency`, `--beep-tone frequency`: Beep tone (frequency in Hz) -* `-n N`, `--duration N`: Duration (N in milliseconds) - -## Notes - -If the user disabled the usage of PC speaker in the kernel commandline, the program -will fail to use the PC speaker. - -## Examples - -```sh -# Use beep with default tone -$ beep -# Use beep with tone of 1000Hz -$ beep -f 1000 -# Use beep with tone of 1000Hz for 1 second -$ beep -f 1000 -n 1000 -``` - -## See also - -* [`boot_parameters`(7)](help://man/7/boot_parameters) diff --git a/Base/usr/share/man/man1/bt.md b/Base/usr/share/man/man1/bt.md deleted file mode 100644 index 3974937a7fd..00000000000 --- a/Base/usr/share/man/man1/bt.md +++ /dev/null @@ -1,46 +0,0 @@ -## Name - -bt - view the backtrace of the specified process - -## Synopsis - -```**sh -$ bt -``` - -## Description - -This program is used to inspect the current executable state of a process. -It will read the stack of each thread in the process, and symbolicate the -addresses for each frame in the stack producing a backtrace. - -**NOTE**: - -* Kernel addresses will not be available unless you are super user. - -* If Kernel addresses are available, they will not be symbolicated unless - the current user has access to the `/boot/Kernel` file. - -## Arguments - -* `pid`: Process ID - -## Examples - -View all stacks of pid number 10: - -```sh -$ bt 10 -``` - -Use [`watch`(1)](help://man/1/watch) to emit a backtrace of pid 124, every second: - -```sh -$ watch -n 1 -- bt 124 -``` - -## See also - -* [`Profiler`(1)](help://man/1/Applications/Profiler) - -* [`watch`(1)](help://man/1/watch) diff --git a/Base/usr/share/man/man1/cal.md b/Base/usr/share/man/man1/cal.md deleted file mode 100644 index 409a4e6d541..00000000000 --- a/Base/usr/share/man/man1/cal.md +++ /dev/null @@ -1,105 +0,0 @@ -## Name - -cal - Display a calendar - -## Synopsis - -```**sh -$ cal [--starting-day weekday] [--three-month-view] [--year] [[month] year] -``` - -## Description - -This program displays a simple calendar. If no arguments are specified, the current month is displayed with the current day highlighted. -An overview of a whole year is displayed when a `year` is passed without a `month`. - -The current day is always highlighted. - -Months and years are specified with numbers. Weeks start at what's configured in the Calendar system settings, -unless the `--starting-day` option is passed. - -Days, months and years are specified with numbers. Week starts at Sunday. - -## Options - -* `-s`, `--starting-day`: Specify which day should start the week. Accepts either short or long weekday names or indexes (0 being Sunday). -* `-3`, `--three-month-view`: Display the previous, current, and next months side-by-side. -* `-y`, `--year`: Display an entire year by laying out months on a grid. If no year number is specified, the current year is used as a default. - -## Examples - -```sh -# Display the current month -$ cal - March - 2023 -Su Mo Tu We Th Fr Sa - 1 2 3 4 - 5 6 7 8 9 10 11 -12 13 14 15 16 17 18 -19 20 21 22 23 24 25 -26 27 28 29 30 31 - -# Display any month -$ cal 1 1999 - January - 1999 -Su Mo Tu We Th Fr Sa - 1 2 - 3 4 5 6 7 8 9 -10 11 12 13 14 15 16 -17 18 19 20 21 22 23 -24 25 26 27 28 29 30 -31 - -# Display three months side-by-side -$ cal -3 3 2023 - February - 2023 March - 2023 April - 2023 -Su Mo Tu We Th Fr Sa Su Mo Tu We Th Fr Sa Su Mo Tu We Th Fr Sa - 1 2 3 4 1 2 3 4 1 - 5 6 7 8 9 10 11 5 6 7 8 9 10 11 2 3 4 5 6 7 8 -12 13 14 15 16 17 18 12 13 14 15 16 17 18 9 10 11 12 13 14 15 -19 20 21 22 23 24 25 19 20 21 22 23 24 25 16 17 18 19 20 21 22 -26 27 28 26 27 28 29 30 31 23 24 25 26 27 28 29 - -# Display an entire year -$ cal 2023 - Year 2023 - - - January February March -Su Mo Tu We Th Fr Sa Su Mo Tu We Th Fr Sa Su Mo Tu We Th Fr Sa - 1 2 3 4 5 6 7 1 2 3 4 1 2 3 4 - 8 9 10 11 12 13 14 5 6 7 8 9 10 11 5 6 7 8 9 10 11 -15 16 17 18 19 20 21 12 13 14 15 16 17 18 12 13 14 15 16 17 18 -22 23 24 25 26 27 28 19 20 21 22 23 24 25 19 20 21 22 23 24 25 -29 30 31 26 27 28 26 27 28 29 30 31 - - - April May June -Su Mo Tu We Th Fr Sa Su Mo Tu We Th Fr Sa Su Mo Tu We Th Fr Sa - 1 1 2 3 4 5 6 1 2 3 - 2 3 4 5 6 7 8 7 8 9 10 11 12 13 4 5 6 7 8 9 10 - 9 10 11 12 13 14 15 14 15 16 17 18 19 20 11 12 13 14 15 16 17 -16 17 18 19 20 21 22 21 22 23 24 25 26 27 18 19 20 21 22 23 24 -23 24 25 26 27 28 29 28 29 30 31 25 26 27 28 29 30 -30 - - - July August September -Su Mo Tu We Th Fr Sa Su Mo Tu We Th Fr Sa Su Mo Tu We Th Fr Sa - 1 1 2 3 4 5 1 2 - 2 3 4 5 6 7 8 6 7 8 9 10 11 12 3 4 5 6 7 8 9 - 9 10 11 12 13 14 15 13 14 15 16 17 18 19 10 11 12 13 14 15 16 -16 17 18 19 20 21 22 20 21 22 23 24 25 26 17 18 19 20 21 22 23 -23 24 25 26 27 28 29 27 28 29 30 31 24 25 26 27 28 29 30 -30 31 - - - October November December -Su Mo Tu We Th Fr Sa Su Mo Tu We Th Fr Sa Su Mo Tu We Th Fr Sa - 1 2 3 4 5 6 7 1 2 3 4 1 2 - 8 9 10 11 12 13 14 5 6 7 8 9 10 11 3 4 5 6 7 8 9 -15 16 17 18 19 20 21 12 13 14 15 16 17 18 10 11 12 13 14 15 16 -22 23 24 25 26 27 28 19 20 21 22 23 24 25 17 18 19 20 21 22 23 -29 30 31 26 27 28 29 30 24 25 26 27 28 29 30 - -``` diff --git a/Base/usr/share/man/man1/cat.md b/Base/usr/share/man/man1/cat.md deleted file mode 100644 index e0e4bcd6a78..00000000000 --- a/Base/usr/share/man/man1/cat.md +++ /dev/null @@ -1,58 +0,0 @@ -## Name - -cat - concatenate files to stdout - -## Synopsis - -```**sh -$ cat [file...] -``` - -## Description - -This program passes contents of specified `files` to standard output, in the specified order. If no `file` is specified, or it is `-`, it defaults to standard input. - -## Arguments - -* `file`: Files to print - -## Examples - -```sh -# Display a single file -$ cat README.md -# SerenityOS - -Graphical Unix-like operating system for x86 computers. -... - -# Display standard input -$ cat -aaa -aaa -bbb -bbb^C - -# Display ls output where each file is in separate line -$ ls | cat -Desktop -Documents -Downloads -README.md -Source -js-tests -tests -web-tests - -# Display multiple files -$ echo 123 > test.txt -$ echo 456 > test2.txt -cat test.txt test2.txt -123 -456 -``` - -## See Also -* [`head`(1)](help://man/1/head) -* [`tail`(1)](help://man/1/tail) -* [`cut`(1)](help://man/1/cut) diff --git a/Base/usr/share/man/man1/checksum.md b/Base/usr/share/man/man1/checksum.md deleted file mode 100644 index 2bb7ca02e29..00000000000 --- a/Base/usr/share/man/man1/checksum.md +++ /dev/null @@ -1,24 +0,0 @@ -## Name - -checksum - helper program for calculating checksums - -## Synopsis - -```**sh -$ b2sum [options...] -$ md5sum [options...] -$ sha1sum [options...] -$ sha256sum [options...] -$ sha512sum [options...] -``` - -## Description - -This program calculates and print specified checksum of files. It cannot be run directly, only -as `b2sum`, `md5sum`, `sha1sum`, `sha256sum` or `sha512sum`. A non-zero exit code is returned if the -input cannot be read. If the '--check' option is used, a non-zero exit code is also returned -if the checksums cannot be verified. - -## Options - -* `-c`, `--check`: Verify checksums against `file` or stdin. diff --git a/Base/usr/share/man/man1/chgrp.md b/Base/usr/share/man/man1/chgrp.md deleted file mode 100644 index 0725e2f343c..00000000000 --- a/Base/usr/share/man/man1/chgrp.md +++ /dev/null @@ -1,33 +0,0 @@ -## Name - -chgrp - change group ownership of files - -## Synopsis - -```**sh -$ chgrp -$ chgrp -``` - -## Description - -`chgrp` called as root or as file owner changes owning group of specified `path` to `gid` or `name`. - -## Options - -* `-h`, `--no-dereference`: Don't follow symlinks - -## Examples - -```sh -# Change group of README.md to 111 -# chgrp 111 README.md - -# Change group of README.md to root -# chgrp root README.md -``` - -## See also - -* [`chmod`(1)](help://man/1/chmod) -* [`chown`(1)](help://man/1/chown) diff --git a/Base/usr/share/man/man1/chmod.md b/Base/usr/share/man/man1/chmod.md deleted file mode 100644 index b068f8234c2..00000000000 --- a/Base/usr/share/man/man1/chmod.md +++ /dev/null @@ -1,52 +0,0 @@ -## Name - -chmod - change file mode - -## Synopsis - -```**sh -$ chmod -$ chmod -``` - -## Description - -`chmod` changes mode of all files specified in `path` to `octal-mode` or using symbolic representation. - -The symbolic representation format is `[[ugoa][+-=][rwx...],...]`. Multiple symbolic can be given, separated by commas. - -The letters `[ugoa]` controls which users' access will be changes: `u` means file owner, `g` file owning group, `o` others, and `a` - all users. If no letter is given, `a` is assumed. - -The letters `[+-=]` controls which action will be taken: `+` sets the permission, `-` removes the permission, and `=` sets the mentioned permissions and unsets the other permissions. - -The letters `[rwx]` controls which permission will be changes: `r` is read, `w` is write and `x` is execute. - -A numeric mode is combination of 1 to 4 numbers. Omitted digits are assumed to be leading zeros. The first digit select the set user ID (4), set group ID (2) and restricted deletion / sticky (1) attributes. The second, third and fourth digit controls permissions of each user group: owner, owning group and others (not owner or owning group), respectively: read (4), write (2) and execute (1). - -## Options - -* `-R`, `--recursive`: Change file modes recursively - -## Examples - -```sh -# Allow full access for owner, read-execute for group, and no access for others, of 'README.md': -$ chmod 750 README.md - -# Change '/bin/su' to be set-uid, read-write-execute for owner, only execute for others: -# chmod 4711 /bin/su - -# Add read access for others to all files in 'Source' directory: -$ chmod o+r Source/* - -# Deny read and write access for non-owners, and allow full access for owners of 'script.sh': -$ chmod o-rw,g-rw,u+rwx script.sh - -# Set group access to only read of 'script.sh': -$ chmod g=r script.sh -``` - -## See also - -* [`chgrp`(1)](help://man/1/chgrp) -* [`chown`(1)](help://man/1/chown) diff --git a/Base/usr/share/man/man1/chown.md b/Base/usr/share/man/man1/chown.md deleted file mode 100644 index b2f6398713d..00000000000 --- a/Base/usr/share/man/man1/chown.md +++ /dev/null @@ -1,37 +0,0 @@ -## Name - -chown - change file owner / group - -## Synopsis - -```**sh -$ chown -``` - -## Description - -`chown` changes the owner of specified files to `user`, and owning group to `group`. If `group` is not specified, it is left unchanged. - -**NOTE**: The caller must be a superuser to change user ownership. Other users can use `chown` to change the group to one of their other -group. - -## Options - -* `-h`, `--no-dereference`: Don't follow symlinks -* `-R`, `--recursive`: Change file ownership recursively -* `-L`: Follow symlinks while recursing into directories - -## Examples - -```sh -# Change 'file' owner and group to 'anon': -$ chown anon:anon file - -# Change 'file' owner to 'root', leave group unchanged: -# chown root file -``` - -## See also - -* [`chgrp`(1)](help://man/1/chgrp) -* [`chmod`(1)](help://man/1/chmod) diff --git a/Base/usr/share/man/man1/chres.md b/Base/usr/share/man/man1/chres.md deleted file mode 100644 index 75acb48964d..00000000000 --- a/Base/usr/share/man/man1/chres.md +++ /dev/null @@ -1,28 +0,0 @@ -## Name - -chres - change display resolution - -## Synopsis - -```**sh -$ chres [scale factor] -``` - -## Description - -`chres` changes the display resolution to x@x. - -## Options - -* `-s`, `--screen`: Screen - -## Examples - -```sh -# Change resolution to 1920x1080, scale 2x: -$ chres 1920 1080 2 -``` - -## Files - -* `/tmp/portal/window` to communicate with WindowServer diff --git a/Base/usr/share/man/man1/clear.md b/Base/usr/share/man/man1/clear.md deleted file mode 100644 index 61e51d5e276..00000000000 --- a/Base/usr/share/man/man1/clear.md +++ /dev/null @@ -1,13 +0,0 @@ -## Name - -clear - clear the terminal - -## Synopsis - -```**sh -$ clear -``` - -## Description - -`clear` clears the current terminal contents by writing `\033[3J\033[H\033[2J` to the standard output. diff --git a/Base/usr/share/man/man1/cmp.md b/Base/usr/share/man/man1/cmp.md deleted file mode 100644 index a89d563da99..00000000000 --- a/Base/usr/share/man/man1/cmp.md +++ /dev/null @@ -1,32 +0,0 @@ -## Name - -cmp - compare files - -## Synopsis - -```**sh -$ cmp [options...] file1 file2 -``` - -## Description - -Compare two files and report the location of any differences. By default, execution stops after the first difference, but this can be overridden (see `--verbose` option). Byte and line numbers start at 1. - -## Options - -* `--help`: Display help message and exit -* `-l`, `--verbose`: Output the byte number, and the differing bytes, for every difference. -* `-s`, `--silent`: Silence output. - -## Arguments - -* `file1` and `file2`: Files to compare. Use `-` as the file name to read from standard input. - -## Exit status - -* 0 - Files are identical. -* 1 - Files are different. -* 2 - An error occurred. - -## See also -* [`comm`(1)](help://man/1/comm) diff --git a/Base/usr/share/man/man1/comm.md b/Base/usr/share/man/man1/comm.md deleted file mode 100644 index dfed693c800..00000000000 --- a/Base/usr/share/man/man1/comm.md +++ /dev/null @@ -1,53 +0,0 @@ -## Name - -comm - compare two sorted files line by line - -## Synopsis - -```**sh -$ comm [options...] -``` - -## Description - -`comm` compares two **sorted** files specified by `file1` and `file2` line by line alphabetically. One of file1 and file2, but not both, can be `-`, in which case `comm` will read from the standard input for that file. - -With no options, `comm` produces a three column output, indented by tabs, of lines unique to `file1`, lines unique to `file2`, and lines common to both files. `comm` provides options to suppress the output of a specific column, use case insensitive comparison or print a summary. - -## Options - -* `-1`: Suppress the output of column 1 (lines unique to `file1`) -* `-2`: Suppress the output of column 2 (lines unique to `file2`) -* `-3`: Suppress the output of column 3 (lines common to `file1` and `file2`) -* `-i`: Use case insensitive comparison of lines -* `-c`, `--color`: Always print colored output even if the standard output is not a tty -* `--no-color`: Do not print colored output -* `-t`, `--total`: Print a summary - -## Arguments - -* `file1`: First file to compare. (`-` for the standard input) -* `file2`: Second file to compare. (`-` for the standard input) - -## Examples - -```sh -# Files should be sorted first -$ sort < file1 > file1_sorted -$ sort < file2 > file2_sorted - -# Display the default three-column output -$ comm file1_sorted file2_sorted - -# Read one sorted file from the standard input -# and only display column 3 -$ sort < file1 | comm -12c - file2_sorted | less - -# Use case insensitive comparison, -# suppress output of all columns -# and print a summary -$ comm -123it file1_sorted file2_sorted -``` - -## See also -* [`cmp`(1)](help://man/1/cmp) diff --git a/Base/usr/share/man/man1/config.md b/Base/usr/share/man/man1/config.md deleted file mode 100644 index 5685d6797b5..00000000000 --- a/Base/usr/share/man/man1/config.md +++ /dev/null @@ -1,26 +0,0 @@ -## Name - -config - -## Synopsis - -```sh -$ config [--remove] [key] [value] -``` - -## Description - -Show or modify values in the configuration files through ConfigServer. - -## Options - -* `-r`, `--remove`: Remove group or key - -## Arguments - -* `domain`: Config domain -* `group`: Group name -* `key`: Key name -* `value`: Value to write - - diff --git a/Base/usr/share/man/man1/copy.md b/Base/usr/share/man/man1/copy.md deleted file mode 100644 index f957e861fd0..00000000000 --- a/Base/usr/share/man/man1/copy.md +++ /dev/null @@ -1,31 +0,0 @@ -## Name - -copy - copy text to the clipboard - -## Synopsis - -```**sh -$ copy [options...] [text...] -``` - -## Description - -`copy` copies text from stdin or command-line argument (`text`) to the system clipboard. The clipboard is managed by WindowServer. - -## Options - -* `-t type`, `--type type`: MIME type of data stored in clipboard. The default type is `text/plain`. -* `-c`, `--clear`: Clear the clipboard instead of copying. - -## Examples - -```sh -# Copy some image to clipboard -$ cat image.png | copy -t image/png - -# Place text 'foo' in clipboard -$ copy foo -``` - -## See also -* [`clipboard`(5)](help://man/5/clipboard) diff --git a/Base/usr/share/man/man1/cp.md b/Base/usr/share/man/man1/cp.md deleted file mode 100644 index 40bc35898bf..00000000000 --- a/Base/usr/share/man/man1/cp.md +++ /dev/null @@ -1,39 +0,0 @@ -## Name - -cp - copy files - -## Synopsis - -```**sh -$ cp [options...] -``` - -## Description - -`cp` copies files specified in `source` to `destination`. - -If there are several `sources`, the directory `destination` is created and all files specified in `sources` are copied into that directory. Otherwise, the `source` file is copied as `destination` file. If the file exists, it is overridden. If `destination` directory exists and there is only one `source`, the `source` file is copied into the `destination` directory. - -If a directory is specified in `source`, the `-R` (recursive) flag is required. Otherwise, an error occurs. - -## Options - -* `-l`, `--link`: Create hard links instead of copying -* `-R`, `-r`, `--recursive`: Copy directories recursively -* `-v`, `--verbose`: Display what files are copied - -## Examples - -```sh -# Copy test file and name it test-backup -$ cp test test-backup - -# Copy tests directory and name it tests-backup -$ cp -R tests tests-backup - -# Copy test file into existing root -$ cp test root -``` - -## See also -* [`mv`(1)](help://man/1/mv) diff --git a/Base/usr/share/man/man1/crash.md b/Base/usr/share/man/man1/crash.md deleted file mode 100644 index 59ea72a7021..00000000000 --- a/Base/usr/share/man/man1/crash.md +++ /dev/null @@ -1,45 +0,0 @@ -## Name - -crash - intentionally perform an illegal operation - -## Synopsis - -```**sh -$ /usr/Tests/Kernel/crash [options] -``` - -## Description - -This program is used to test how the Serenity kernel handles userspace crashes, -and can be used to simulate many different kinds of crashes. - -## Options - -* `-A`: Test that all of the following crash types crash as expected. -* `-s`: Perform a segmentation violation by dereferencing an invalid pointer. -* `-d`: Perform a division by zero. -* `-i`: Execute an illegal CPU instruction. -* `-a`: Call `abort()`. -* `-m`: Read a pointer from uninitialized malloc memory, then read from it. -* `-f`: Read a pointer from memory freed using `free()`, then read from it. -* `-M`: Read a pointer from uninitialized malloc memory, then write to it. -* `-F`: Read a pointer from memory freed using `free()`, then write to it. -* `-r`: Write to read-only memory. -* `-T`: Make a syscall while using an invalid stack pointer. -* `-t`: Trigger a page fault while using an invalid stack pointer. -* `-S`: Make a syscall from writeable memory. -* `-y`: Make a syscall from legitimate memory (but outside syscall-code mapped region). -* `-X`: Attempt to execute non-executable memory (Not mapped with PROT\_EXEC). -* `-U`: Attempt to trigger an x86 User Mode Instruction Prevention fault. -* `-I`: Use an x86 I/O instruction in userspace. -* `-p`: Violate `pledge()`'d promises. -* `-n`: Perform a failing assertion. -* `-R`: Dereference a null RefPtr. - -## Examples - -```sh -$ crash -F -Testing: "Write to freed memory" -Shell: Job 1 (crash -F) Segmentation violation -``` diff --git a/Base/usr/share/man/man1/cut.md b/Base/usr/share/man/man1/cut.md deleted file mode 100644 index 1444399aff1..00000000000 --- a/Base/usr/share/man/man1/cut.md +++ /dev/null @@ -1,53 +0,0 @@ -## Name - -cut - remove sections from each line of files - -## Synopsis - -```**sh -$ cut option... [file...] -``` - -## Description - -Print selected parts of lines from each FILE to standard output. - -With no FILE, or when FILE is -, read standard input. - -## Arguments - -* `file`: File(s) to cut - -## Options - -* `-b` `--bytes=list`: Select only these bytes -* `-f` `--fields=list`: select only these fields; also print any line that contains no delimiter character -* `-d` `--delimiter=delim`: use `delim` instead of `tab` for field delimiter -* `-s`, `only-delimited`: suppress lines which don't contain any field delimiter characters - -## Examples - -```sh -$ cat example.txt -245:789 4567 M:4540 Admin 01:10:1980 -535:763 4987 M:3476 Sales 11:04:1978 - -# Display first and third fields from file example.txt -$ cut example.txt -f 1,3 -245:789 M:4540 -535:763 M:3476 - -# Display first and third fields using `:` as a delimiter -$ cut example.txt -d ':' -f 1,3 -245:4540 Admin 01 -535:3476 Sales 11 - -# Display bytes at given position -$ echo "serenity is cool" | cut -b 5 -n - -``` - -## See Also -* [`head`(1)](help://man/1/head) -* [`cat`(1)](help://man/1/cat) diff --git a/Base/usr/share/man/man1/date.md b/Base/usr/share/man/man1/date.md deleted file mode 100644 index fb5c999ec1f..00000000000 --- a/Base/usr/share/man/man1/date.md +++ /dev/null @@ -1,42 +0,0 @@ -## Name - -date - print or set the system date and time - -## Synopsis - -```**sh -$ date [--set date] [--unix] [--iso-8601] [--rfc-3339] [--rfc-5322] [format-string] -``` - -## Description - -date is a utility to set the system date and time -or print the system date and time in various formats. - -## Options - -* `-s`, `--set`: Set system date and time -* `-u`, `--unix`: Print date as Unix timestamp -* `-i`, `--iso-8601`: Print date in ISO 8601 format -* `-r`, `--rfc-3339`: Print date in RFC 3339 format -* `-R`, `--rfc-5322`: Print date in RFC 5322 format - -## Arguments - -* `format-string`: Custom format to print the date in. Must start with a '+' character. - -## Examples - -```sh -# Print the current date and time in ISO 8601 format -$ date --iso-8601 - -# Print the current date in a custom format -$ date +%Y-%m-%d - -# Set date to 1610017485 (UNIX time) -$ date -s 1610017485 -``` - -## See Also -* [`ntpquery`(1)](help://man/1/ntpquery) diff --git a/Base/usr/share/man/man1/dd.md b/Base/usr/share/man/man1/dd.md deleted file mode 100644 index 03447072937..00000000000 --- a/Base/usr/share/man/man1/dd.md +++ /dev/null @@ -1,36 +0,0 @@ -## Name - -dd - copy blocks of data - -## Synopsis - -```**sh -$ dd if=[input_file] of=[output_file] [args...] -``` - -## Description - -`dd` is an application used to write blocks of data from one file to another. A common use case for `dd` is to make a bootable hard drive from a ISO file. - -## Options - -* `--help`: Display help message and exit - -## Arguments - -* `if`: input file (or device) to read from (default: stdin) -* `of`: output file (or device) to write to (default: stdout) -* `bs`: block size (of bytes) to use (default: 512) -* `count`: number of blocks to write -* `seek`: number of output blocks to skip (default: 0) -* `skip`: number of input blocks to skip (default: 0) -* `status`: level of output (default: default) - * `default`: error messages + final statistics - * `none`: just error messages - * `noxfer`: no final statistics - -## Examples - -```**sh -$ dd if=/dev/zero of=./zeros bs=1M count=1 -``` diff --git a/Base/usr/share/man/man1/diff.md b/Base/usr/share/man/man1/diff.md deleted file mode 100644 index 000c79348da..00000000000 --- a/Base/usr/share/man/man1/diff.md +++ /dev/null @@ -1,54 +0,0 @@ -## Name - -diff - compare files line by line - -## Synopsis - -```**sh -$ diff [options...] [files...] -``` - -## Description - -Compare `files` line by line. - -## Arguments - -* `files`: files to compare ex: `file1 file2` - -## Options - -* `-u`, `-U `, `--unified `: Write diff in unified format with `` number of surrounding context lines (default 3). -* `-c`, `-C `, `--context `: Write diff in context format with `` number of surrounding context lines (default 3). - -## Examples - -First we create two files to compare: - -```sh -$ printf '1\n2\n3\n' > file1 -$ printf '1\nb\n3\n' > file2 -``` - -Here's how to view differences between the two files in normal format: - -```sh -$ diff file1 file2 -2c2 -< 2 ---- -> b -``` - -Here's how to view differences between the two files in unified format: - -```sh -$ diff -u file1 file2 ---- file1 -+++ file2 -@@ -1,3 +1,3 @@ - 1 --2 -+b - 3 -``` diff --git a/Base/usr/share/man/man1/dirname.md b/Base/usr/share/man/man1/dirname.md deleted file mode 100644 index 35a2d98ce3c..00000000000 --- a/Base/usr/share/man/man1/dirname.md +++ /dev/null @@ -1,18 +0,0 @@ -## Name - -dirname - return the directory portion of a path - -## Synopsis - -```sh -$ dirname [--zero] -``` - -## Options - -* `-z`, `--zero`: End each output line with \0, rather than \n - -## Arguments - -* `path`: Path - diff --git a/Base/usr/share/man/man1/drain.md b/Base/usr/share/man/man1/drain.md deleted file mode 100644 index a23c8349be9..00000000000 --- a/Base/usr/share/man/man1/drain.md +++ /dev/null @@ -1,38 +0,0 @@ -## Name - -drain - Print file to stdout, while progressively deleting read segments - -## Synopsis - -```**sh -$ drain [file] -``` - -## Description - -drain is a utility for simultaneously reading and deleting a file. It is -useful, for example, when working with large files on systems with low disk space. -drain will read blocks from the provided file, and write each block to -the standard output stream before removing that block from the file. -The output may be redirected to another file or utility for further processing. - -## Options - -* `-b`, `--block-size`: Base block size [in KiB] to be used during the utility operation, default is 256 KiB - -## Arguments - -* `file`: File to be read - -## Warning - -In order to progressively delete the file, drain reverses the file in-place to truncate blocks after they are read. -Thus, it is implicitly unsafe to interrupt the utility. If the operation is interrupted, or otherwise fails, -the input file is unlikely to be recoverable. - -## Examples - -```sh -$ drain my-big-tar-file.tar | tar -x -C my-big-tar-file-extracted -$ drain -b 1 my-small-tar-file.tar | tar -x -C my-small-tar-file-extracted -``` diff --git a/Base/usr/share/man/man1/du.md b/Base/usr/share/man/man1/du.md deleted file mode 100644 index a1c0d510f0d..00000000000 --- a/Base/usr/share/man/man1/du.md +++ /dev/null @@ -1,57 +0,0 @@ -## Name - -du - print disk usage - -## Synopsis - -```**sh -$ du [files...] -``` - -## Description - -`du` prints disk usage data for every argument, in KiB (kibibytes). - -## Options - -* `-a`, `--all`: Write counts for all files, not just directories -* `--apparent-size`: Print apparent sizes, rather than disk usage -* `-h` , `--human-readable`: Print human-readable sizes -* `--si`: Print human-readable sizes in SI units -* `-d N`, `--max-depth N`: Print the total for a directory or file only if it is N or fewer levels below the command line argument -* `-s`, `--summarize`: Display only a total for each argument -* `-t size`, `--threshold size`: Exclude entries smaller than size if positive, or entries greater than size if negative -* `--time time-type`: Show time of time time-type of any file in the directory, or any of its subdirectories. Available choices: mtime, modification, ctime, status, use, atime, access -* `--exclude pattern`: Exclude files that match pattern -* `-x`, `--one-file-system`: Don't traverse directories on different file systems -* `-X file, --exclude-from`: Exclude files that match any pattern in file - -## Arguments - -* `files`: Files to print disk usage of - -## Examples - -```sh -~ $ du -s * -4 Desktop -4 Documents -4 Downloads -6 README.md -4 Source -4 js-tests -4 tests -4 web-tests -~ $ du -a Documents -2 Documents/emoji.txt -2 Documents/zip/archive.zip -4 Documents/zip -2 Documents/tips.txt -4 Documents -~ $ du --apparent-size -a Documents -4 Documents/emoji.txt -4 Documents/zip/archive.zip -4 Documents/zip -4 Documents/tips.txt -4 Documents -``` diff --git a/Base/usr/share/man/man1/echo.md b/Base/usr/share/man/man1/echo.md deleted file mode 100644 index a66411c0aee..00000000000 --- a/Base/usr/share/man/man1/echo.md +++ /dev/null @@ -1,55 +0,0 @@ -## Name - -echo - print the given text - -## Synopsis - -```**sh -$ echo [-ne] [text...] -``` - -## Description - -Print the given `text` to the standard output. If multiple `text`s are provided, they will be joined with a space character. If no `text` is provided, an empty line will be printed. - -Character escape sequences and their meanings are as follows: - -`\\a` - `` - -`\\b` - `` - -`\\c` - Suppress the output of all remaining characters, including the trailing newline. - -`\\e` - The escape character (`\\033`). - -`\\f` - `` - -`\\n` - `` - -`\\r` - `` - -`\\t` - `` - -`\\v` - `` - -`\\\\` - The backslash character (`\\`). - -`\\0ooo` - A byte whose value is a zero, one, two, or three-digit octal number. - -`\\xHH` - A byte whose value is a two-digit hexadecimal number. - -`\\uHHHH` - An unicode code point whose value is a four-digit hexadecimal number. - -## Options - -* `-n`: Do not output a trailing newline -* `-e`: Interpret backslash escapes - -## Examples - -```sh -$ echo hello friends! -hello friends! -$ echo -ne '\x68\x65\x6c\x6c\x6f' 'friends\041\n' -hello friends! -``` diff --git a/Base/usr/share/man/man1/elfdeps.md b/Base/usr/share/man/man1/elfdeps.md deleted file mode 100644 index f41d9afc746..00000000000 --- a/Base/usr/share/man/man1/elfdeps.md +++ /dev/null @@ -1,41 +0,0 @@ -## Name - -elfdeps - list ELF object dynamic dependencies - -## Synopsis - -```**sh -$ elfdeps [-r] [-f] -``` - -## Description - -`elfdeps` prints all dependency libraries of an ELF object. - -## Options - -* `-f`, `--force-without-valid-interpreter`: Force library resolving on ELF - object without a valid interpreter -* `-r`, `--max-recursion`: Max library resolving recursion - -## Arguments - -* `path`: Path to ELF object - -## Security - -The `elfdeps` binary is completely safe for usage on untrusted binaries - -we only use the `LibELF` code for doing library resolving, and the actual -binary interpreter (when specified in an ELF exectuable) is never called to -decode the dependency information. - -## Examples - -```sh -# List all dependency libraries for libc.so -$ elfdeps -f /usr/lib/libc.so -# List all dependency libraries for /bin/id -$ elfdeps /bin/id -# List all dependency libraries for /bin/WindowServer -$ elfdeps /bin/WindowServer -``` diff --git a/Base/usr/share/man/man1/env.md b/Base/usr/share/man/man1/env.md deleted file mode 100644 index e0191b945a1..00000000000 --- a/Base/usr/share/man/man1/env.md +++ /dev/null @@ -1,19 +0,0 @@ -## Name - -env - Execute a command with a modified environment - -## Synopsis - -```sh -$ env [--ignore-environment] [--split-string S] [--unset name] [env/command...] -``` - -## Options - -* `-i`, `--ignore-environment`: Start with an empty environment -* `-S S`, `--split-string S`: Process and split S into separate arguments; used to pass multiple arguments on shebang lines -* `-u name`, `--unset name`: Remove variable from the environment - -## Arguments - -* `env/command`: Environment and commands diff --git a/Base/usr/share/man/man1/expr.md b/Base/usr/share/man/man1/expr.md deleted file mode 100644 index be325fabbaf..00000000000 --- a/Base/usr/share/man/man1/expr.md +++ /dev/null @@ -1,74 +0,0 @@ -## Name - -expr - evaluate expressions - -## Synopsis - -```**sh -$ expr -$ expr [--help] -``` - -## Description -expr evaluates and prints the result of an expression as described below to standard output. - -An _expression_ may be any of the following: -- `expr1 | expr2` - `expr2` if `expr1` is falsy, `expr1` otherwise. -- `expr1 & expr2` - `expr1` if neither expression is falsy, `0` otherwise. -- `expr1 < expr2` - `1` if `expr1` is less than `expr2`, `0` otherwise. -- `expr1 <= expr2` - `1` if `expr1` is less than or equal to `expr2`, `0` otherwise. -- `expr1 = expr2` - `1` if `expr1` is equal to `expr2`, `0` otherwise. -- `expr1 = expr2` - `1` if `expr1` is not equal to `expr2`, `0` otherwise. -- `expr1 => expr2` - `1` if `expr1` is greater than or equal to `expr2`, `0` otherwise. -- `expr1 > expr2` - `1` if `expr1` is greater than `expr2`, `0` otherwise. -- `expr1 + expr2` - arithmetic integral sum of `expr1` and `expr2`. -- `expr1 - expr2` - arithmetic integral difference of `expr1` and `expr2`. -- `expr1 * expr2` - arithmetic integral product of `expr1` and `expr2`. -- `expr1 / expr2` - arithmetic integral quotient of `expr1` divided by `expr2`. -- `expr1 % expr2` - arithmetic integral quotient of `expr1` divided by `expr2`. -- `expr1 : expr2` - pattern match of `expr2` as a regular expression in `expr1` - currently not implemented. -- `match expr1 expr2` - same as `expr1 : expr2`. -- `substr expr1 expr2 expr3` - substring with length `expr3` of `expr1`, starting at `expr2`, indices starting at 1. -- `index expr1 expr2` - index of `expr2` in `expr1`, starting at 1. 0 if not found. -- `length expr1` - length of the string `expr1` -- `+ token` - interpret `token` as a string, regardless of whether it is a keyword or an operator. -- `( expr )` - value of `expr` - -Note that many operators will need to be escaped or quoted if used from within a shell. -"falsy" means either the number 0, or the empty string. - -## Options - -* `--help`: Prints usage information and exits. - -## Examples - -```sh -$ expr 1 + 2 * 3 # = 7 -$ expr \( 1 + 2 \) = 3 # = 1 -$ expr substr foobar 1 3 # foo -``` - -## See also -* [`test`(1)](help://man/1/test) -* [`js`(1)](help://man/1/js) for evaluating more complex expressions diff --git a/Base/usr/share/man/man1/file.md b/Base/usr/share/man/man1/file.md deleted file mode 100644 index db41f5cc0c8..00000000000 --- a/Base/usr/share/man/man1/file.md +++ /dev/null @@ -1,36 +0,0 @@ -## Name - -file - determine type of files - -## Synopsis - -```**sh -$ file [options...] [files...] -``` - -## Description - -`file` attempts to identify the type of files. - -First, an attempt is made to identify a given file based on predetermined binary patterns. If this fails, `file` will fall back to determining the type based on the filename. - -## Options - -* `--help`: Display this message -* `-b`, `--brief`: Do not prepend file names to output lines -* `-I`, `--mime-type`: Only show mime type. - -## Arguments - -* `files`: Files to identify - -## Examples - -```sh -# Identify a file -$ file Buggie.png -Buggie.png: PNG image data, 64 x 138 -# Identify all files in the current directory, and show only the mime type. -$ file -I * -``` - diff --git a/Base/usr/share/man/man1/find.md b/Base/usr/share/man/man1/find.md deleted file mode 100644 index 46a172c5085..00000000000 --- a/Base/usr/share/man/man1/find.md +++ /dev/null @@ -1,140 +0,0 @@ -## Name - -find - recursively search for files - -## Synopsis - -```**sh -$ find [-L] [root-paths...] [commands...] -``` - -## Description - -`find` recursively traverses the file hierarchy starting at the given root paths -(or at the current working directory if no root paths have been specified), and -evaluates the given commands for each found file. The commands can be used to -both filter the set of files and to perform actions on them. - -If no *action command* (`-print`, `-print0`, or `-exec`) is found among the -specified commands, a `-print` command is implicitly appended. - -## Options - -* `-L`: Follow symlinks - -## Commands - -* `-maxdepth n`: Do not descend more than `n` levels below each path given on - the command line. Specifying `-maxdepth 0` has the effect of only evaluating - each command line argument. -* `-mindepth n`: Descend `n` levels below each path given on the command line - before executing any commands. Specifying `-mindepth 1` has the effect of - processing all files except the command line arguments. -* `-type t`: Checks if the file is of the specified type, which must be one of - `b` (for block device), `c` (character device), `d` (directory), `l` (symbolic - link), `p` (FIFO), `f` (regular file), and `s` (socket). -* `-links [-|+]number`: Checks if the file has the given number of hard links. -* `-user name`: Checks if the file is owned by the given user. Instead of a user - name, a numerical UID may be specified. -* `-group name`: Checks if the file is owned by the given group. Instead of a - group name, a numerical GID may be specified. -* `-size [-|+]number[bcwkMG]`: Checks if the file uses the specified `n` units of - space rounded up to the nearest whole unit. - - The '+' and '-' prefixes denote greater than and less than, i.e an exact size - of `n` units doesn't match. Sizes are always rounded up to the nearest unit, - empty files, while the latter will match files from 0 to 1,048,575 bytes. - - The unit of space may be specified by any of these suffixes: - - * `b`: 512-byte blocks. This is the default unit if no suffix is used. - * `c`: bytes - * `w`: two-byte words - * `k`: kibibytes (1024 bytes) - * `M`: mebibytes (1024 kibibytes) - * `G`: gibibytes (1024 mebibytes) - -* `-path pattern`: Checks if the full file path matches the given global-style - pattern. This check matches against the full file name, starting from one of - the start points given on the command line. This means that using an absolute - path only makes sense in the case where the start point given on the command - line is an absolute path. For example, the following command will never match - anything: - - `find bar -ipath '/foo/bar/test_file' -print` - - The given path is compared against the current directory concatenated with the - basename of the current file. Because such a concatenation can never end in a - '/', specifying path argument that ends with a '/' will never match anything. -* `-ipath pattern`: Functions identically to `-path` but is case-insensitive. -* `-name pattern`: Checks if the file name matches the given global-style - pattern (case sensitive). -* `-empty`: File is either an empty regular file or a directory containing no - files. -* `-iname pattern`: Checks if the file name matches the given global-style - pattern (case insensitive). -* `-readable`: Checks if the file is readable by the current user. -* `-writable`: Checks if the file is writable by the current user. -* `-executable`: Checks if the file is executable, or directory is searchable, -by the current user. -* `-newer file`: Checks if the file last modification time is greater than that - of the specified reference file. If `file` is a symbolic link and the `-L` - option is in use, then the last modification time of the file pointed to by - the symbolic link is used. -* `-anewer file`: Checks if the file last access time is greater than that of - the specified reference file. If `file` is a symbolic link and the `-L` - option is in use, then the last access time of the file pointed to by the - symbolic link is used. -* `-cnewer file`: Checks if the file creation time is greater than that of - the specified reference file. If `file` is a symbolic link and the `-L` - option is in use, then the creation time of the file pointed to by the - symbolic link is used. -* `-gid [-|+]number`: Checks if the file is owned by a group with an ID less - than, greater than or exactly `number`. -* `-uid [-|+]number`: Checks if the file is owned by a user with an ID less - than, greater than or exactly `number`. -* `-print`: Outputs the file path, followed by a newline. Always evaluates to - true. -* `-print0`: Outputs the file path, followed by a zero byte. Always evaluates to - true. -* `-exec command... ;`: Executes the given command with any arguments provided, - substituting the file path for any arguments specified as `{}`. The list of - arguments must be terminated by a semicolon. Checks if the command exits - successfully. -* `-ok command... ;`: Behaves identically to the `-exec` command, but will - prompt the user for confirmation before executing the given command. An - affirmative response is any response that begins with the 'y' character. - Any non-affirmative response will cause the command to not be executed and - the value returned by `-ok` to be false. - -The commands can be combined to form complex expressions using the following -operators: - -* `! command`: Logical NOT. -* `command1 -o command2`: Logical OR. -* `command1 -a command2`, `command1 command2`: Logical AND. -* `( command )`: Groups commands together for operator priority purposes. - -Commands which take a numeric argument `n` (`-links` and `-size` for example), -may be prefixed by a plus sign ('+') or a minus sign ('-'). A plus sign means -"grater than `n`", while a minus sign means "less than `n`". A numeric argument -with no prefix means "exactly equal". - -## Examples - -```sh -# Output a tree of paths rooted at the current directory: -$ find -# Output only directories: -$ find -type d -# Remove all sockets and any files owned by anon in /tmp: -$ find /tmp "(" -type s -o -user anon ")" -exec rm "{}" ";" -# Concatenate files with weird characters in their names: -$ find -type f -print0 | xargs -0 cat -# Find files with the word "config" in their name: -$ find -name \*config\* -``` - -## See also - -* [`xargs`(1)](help://man/1/xargs) diff --git a/Base/usr/share/man/man1/fortune.md b/Base/usr/share/man/man1/fortune.md deleted file mode 100644 index bf50aa49633..00000000000 --- a/Base/usr/share/man/man1/fortune.md +++ /dev/null @@ -1,23 +0,0 @@ -## Name - -fortune - -## Synopsis - -```sh -$ fortune [--color when] [path] -``` - -## Description - -Open a fortune cookie, receive a free quote for the day! - -## Options - -* `--color when`: Chose when to color the output. Valid options are always, never and auto (default). When color is set to auto, color codes will be emitted when stdout is a terminal - -## Arguments - -* `path`: Path to JSON file with quotes (/res/fortunes.json by default) - - diff --git a/Base/usr/share/man/man1/gml-format.md b/Base/usr/share/man/man1/gml-format.md deleted file mode 100644 index 4eb084e4630..00000000000 --- a/Base/usr/share/man/man1/gml-format.md +++ /dev/null @@ -1,26 +0,0 @@ -## Name - -gml-format - automated GUI Markup Language (GML) formatter - -## Synopsis - -```**sh -$ gml-format [--inplace] [path...] -``` - -## Description - -`gml-format` formats GUI Markup Language (GML) files. - -## Options - -* `-i`, `--inplace`: Write formatted contents back to file rather than standard output - -## Examples - -```sh -$ gml-format -i /home/anon/example.gml -``` - -## See Also -* [`GML`(5)](help://man/5/GML) diff --git a/Base/usr/share/man/man1/grep.md b/Base/usr/share/man/man1/grep.md deleted file mode 100644 index 2cff0400ccb..00000000000 --- a/Base/usr/share/man/man1/grep.md +++ /dev/null @@ -1,34 +0,0 @@ -## Name - -grep - -## Synopsis - -```sh -$ grep [--recursive] [--extended-regexp] [--fixed-strings] [--regexp Pattern] [--file File] [-i] [--line-numbers] [--invert-match] [--quiet] [--no-messages] [--binary-mode ] [--text] [-I] [--color WHEN] [--no-hyperlinks] [--count] [file...] -``` - -## Options - -* `-r`, `--recursive`: Recursively scan files -* `-E`, `--extended-regexp`: Extended regular expressions -* `-F`, `--fixed-strings`: Treat pattern as a string, not a regexp -* `-e Pattern`, `--regexp Pattern`: Pattern -* `-f File`, `--file File`: Read patterns from a file -* `-i`: Make matches case-insensitive -* `-n`, `--line-numbers`: Output line-numbers -* `-v`, `--invert-match`: Select non-matching lines -* `-q`, `--quiet`: Do not write anything to standard output -* `-s`, `--no-messages`: Suppress error messages for nonexistent or unreadable files -* `--binary-mode`: Action to take for binary files ([binary], text, skip) -* `-a`, `--text`: Treat binary files as text (same as --binary-mode text) -* `-I`: Ignore binary files (same as --binary-mode skip) -* `--color WHEN`: When to use colored output for the matching text ([auto], never, always) -* `--no-hyperlinks`: Disable hyperlinks -* `-c`, `--count`: Output line count instead of line contents - -## Arguments - -* `file`: File(s) to process - - diff --git a/Base/usr/share/man/man1/groups.md b/Base/usr/share/man/man1/groups.md deleted file mode 100644 index ce093f77f29..00000000000 --- a/Base/usr/share/man/man1/groups.md +++ /dev/null @@ -1,34 +0,0 @@ -## Name - -groups - list group memberships - -## Synopsis - -```**sh -$ groups [username...] -``` - -## Description - -`groups` lists group memberships. - -If no username is provided group memberships are listed for current user. - -## Arguments - -* `username`: username to list group memberships for - -## Examples - -```sh -# List group memberships for current user -$ groups -# List group memberships for one user -$ groups nona -# List group memberships for multiple users -$ groups nona anon root -``` - -## See Also -* [`groupdel`(8)](help://man/8/groupdel) -* [`groupadd`(8)](help://man/8/groupadd) diff --git a/Base/usr/share/man/man1/gunzip.md b/Base/usr/share/man/man1/gunzip.md deleted file mode 120000 index 548787f228a..00000000000 --- a/Base/usr/share/man/man1/gunzip.md +++ /dev/null @@ -1 +0,0 @@ -gzip.md \ No newline at end of file diff --git a/Base/usr/share/man/man1/gzip.md b/Base/usr/share/man/man1/gzip.md deleted file mode 100644 index d4a3745925b..00000000000 --- a/Base/usr/share/man/man1/gzip.md +++ /dev/null @@ -1,24 +0,0 @@ -## Name - -gzip, gunzip, zcat - -## Synopsis - -```sh -$ gzip [--keep] [--stdout] [--decompress] -$ gunzip [--keep] [--stdout] -$ zcat -``` - -## Options - -* `-k`, `--keep`: Keep (don't delete) input files -* `-c`, `--stdout`: Write to stdout, keep original files unchanged -* `-d`, `--decompress`: Decompress - -## Arguments - -* `FILES`: Files - -## See also -* [`tar`(1)](help://man/1/tar) diff --git a/Base/usr/share/man/man1/head.md b/Base/usr/share/man/man1/head.md deleted file mode 100644 index 0daa38f7042..00000000000 --- a/Base/usr/share/man/man1/head.md +++ /dev/null @@ -1,44 +0,0 @@ -## Name - -head - output the first part of files - -## Synopsis - -```**sh -$ head [option...] [file...] -``` - -## Description - -Print the first 10 lines of each `file` to standard output. With more than one `file`, -precede each with a header giving the file name. - -With no `file`, or when `file` is `-`, read standard input. - -## Arguments - -* `file`: File to process - -## Options - -* `-n` `--number=NUM`: Number of lines to print (default 10) -* `-c` `--bytes=NUM`: Number of bytes to print -* `-q` `--quiet`: Never print filenames -* `-v` `--verbose`: Always print filenames - - -## Examples - -```sh -# Print the first four lines from README.md and precede it with a filename header -$ head -v -n 4 README.md -==> README.md <== -# SerenityOS - -Graphical Unix-like operating system for x86 computers. - -``` - -## See also -* [`tail`(1)](help://man/1/tail) -* [`cat`(1)](help://man/1/cat) diff --git a/Base/usr/share/man/man1/host.md b/Base/usr/share/man/man1/host.md deleted file mode 100644 index c76d15192eb..00000000000 --- a/Base/usr/share/man/man1/host.md +++ /dev/null @@ -1,25 +0,0 @@ -## Name - -host - DNS lookup utility - -## Synopsis - -```**sh -$ host -``` - -## Description - -`host` is a simple utility for performing DNS lookups. It is used to -convert names to IP addresses and vice versa. - -`name` is the domain name that is to be looked up. It can also be a -dotted-decimal IPv4 address, in which case `host` will perform a reverse -lookup for that address. - -## Examples - -```sh -$ host github.com -$ host 8.8.8.8 -``` diff --git a/Base/usr/share/man/man1/id.md b/Base/usr/share/man/man1/id.md deleted file mode 100644 index f15221f4cda..00000000000 --- a/Base/usr/share/man/man1/id.md +++ /dev/null @@ -1,41 +0,0 @@ -## Name - -id - print user/group ID - -## Synopsis - -```**sh -$ id [options...] -``` - -## Description - -`id` outputs user and group information for the current user. - -## Options - -* `-u`: Print only real UID. -* `-g`: Print only real GID. -* `-G`: Print only all GIDs. -* `-n`: If `-u`, `-g` or `-G` is specified, print full names instead of IDs. - -## Examples - -```sh -$ id -uid=100(anon) gid=100(users) groups=1(wheel),10(lookup),12(notify),4(audio),13(window),14(clipboard),3(phys) -$ id -u -100 -$ id -g -100 -$ id -un -anon -$ id -G -1 10 12 4 13 14 3 -``` - -## See also - -* [`whoami`(1)](help://man/1/whoami) -* [`groups`(1)](help://man/1/groups) -* [`usermod`(8)](help://man/8/usermod) diff --git a/Base/usr/share/man/man1/ifconfig.md b/Base/usr/share/man/man1/ifconfig.md deleted file mode 100644 index c8f9da6f5f5..00000000000 --- a/Base/usr/share/man/man1/ifconfig.md +++ /dev/null @@ -1,25 +0,0 @@ -## Name - -ifconfig - -## Synopsis - -```sh -$ ifconfig [--ipv4 ip] [--adapter adapter] [--mask mask] -``` - -## Description - -Display or modify the configuration of each network interface. - -## Options - -* `-i ip`, `--ipv4 ip`: Set the IP address of the selected network -* `-a adapter`, `--adapter adapter`: Select a specific network adapter to configure -* `-m mask`, `--mask mask`: Set the network mask of the selected network - - - -## See Also -* [`Network`(5)](help://man/5/Network) -* [`netstat`(1)](help://man/1/netstat) diff --git a/Base/usr/share/man/man1/image2bin.md b/Base/usr/share/man/man1/image2bin.md deleted file mode 100644 index 061224c7116..00000000000 --- a/Base/usr/share/man/man1/image2bin.md +++ /dev/null @@ -1,23 +0,0 @@ -## Name - -image2bin - convert an image to a binary bitmap - -## Synopsis - -```**sh -$ image2bin -``` - -## Description - -`image2bin` uses LibGfx to decode a specified image to a raw bitmap, so it could be stored -in a raw binary format for further examination. - -## Examples - -```sh -# Convert a PNG image to raw bitmap -$ image2bin example.png > example.bin -# Convert a JPEG image to raw bitmap -$ image2bin another_example.jpg > another_example.bin -``` diff --git a/Base/usr/share/man/man1/jail-attach.md b/Base/usr/share/man/man1/jail-attach.md deleted file mode 100644 index 34e09ad4bb0..00000000000 --- a/Base/usr/share/man/man1/jail-attach.md +++ /dev/null @@ -1,36 +0,0 @@ -## Name - -jail-attach - attach a new process to existing jail - -## Synopsis - -```**sh -$ jail-attach -``` - -## Description - -`jail-attach` attaches a new process by specifying a command, to an existing jail, with a -specified jail index. - -## Options - -* `-E`, `--preserve-env`: Preserve user environment when running command -* `-i`, `--jail-index`: Use an already existing jail with its index -* `-n`, `--jail-name`: Create a new jail with a provided name - -## Examples - -```sh -# Attach the command "ps -ef" to an already existing jail with the index 0 -$ jail-attach -i 0 ps -ef -``` - -```sh -# Attach the command "/bin/Shell" to a new jail with the name "test jail" -$ jail-attach -n "test jail" /bin/Shell -``` - -## See also -* [`jail-create`(1)](help://man/1/jail-create) -* [`lsjails`(1)](help://man/1/lsjails) diff --git a/Base/usr/share/man/man1/jail-create.md b/Base/usr/share/man/man1/jail-create.md deleted file mode 100644 index b60845782a4..00000000000 --- a/Base/usr/share/man/man1/jail-create.md +++ /dev/null @@ -1,31 +0,0 @@ -## Name - -jail-create - create a new jail - -## Synopsis - -```**sh -$ jail-create -``` - -## Description - -`jail-create` creates a new jail, with a specified name - -## Options - -* `-p`, `--pid-isolation`: Use PID-isolation (as a custom isolation option) - -## Examples - -```sh -# Create jail with the name "test-jail", with no PID isolation -$ jail-create test-jail - -# Create jail with the name "test-jail", with PID isolation -$ jail-create -p test-jail -``` - -## See also -* [`jail-attach`(1)](help://man/1/jail-attach) -* [`lsjails`(1)](help://man/1/lsjails) diff --git a/Base/usr/share/man/man1/js.md b/Base/usr/share/man/man1/js.md deleted file mode 100644 index 5b8754cd33b..00000000000 --- a/Base/usr/share/man/man1/js.md +++ /dev/null @@ -1,66 +0,0 @@ -## Name - -js - evaluate JavaScript - -## Synopsis - -```**sh -$ js [options...] [script.js] -``` - -## Description - -`js` evaluates JavaScript programs using the LibJS engine. If you pass it a path -to a script file, it will execute that script. Otherwise, it enters the -Read-Eval-Print-Loop (REPL) mode, where it interactively reads pieces (usually, -single lines) of code from standard input, evaluates them in one shared -interpreter context, and prints back their results. This mode is useful for -quickly experimenting with LibJS. - -Run `help()` in REPL mode to see its available built-in functions. - -## Options - -* `-A`, `--dump-ast`: Dump the Abstract Syntax Tree after parsing the program. -* `-d`, `--dump-bytecode`: Dump the bytecode -* `-b`, `--run-bytecode`: Run the bytecode -* `-p`, `--optimize-bytecode`: Optimize the bytecode -* `-m`, `--as-module`: Treat as module -* `-l`, `--print-last-result`: Print the result of the last statement executed. -* `-g`, `--gc-on-every-allocation`: Run garbage collection on every allocation. -* `-i`, `--disable-ansi-colors`: Disable ANSI colors -* `-h`, `--disable-source-location-hints`: Disable source location hints -* `-s`, `--no-syntax-highlight`: Disable live syntax highlighting in the REPL -* `-c`, `--evaluate`: Evaluate the argument as a script - -## Examples - -Here's how you execute a script from a file: - -```sh -$ js ~/Source/js/type-play.js -``` - -Here's how you execute a script as a command line argument: - -```sh -$ js -c "console.log(42)" -42 -``` - -And here's an example of an interactive REPL session: - -```js -$ js -> function log_sum(a, b) { -> console.log(a + b) -> } -undefined -> log_sum(35, 42) -77 -undefined -``` - -## See also - -* [`test-js`(1)](help://man/1/test-js) diff --git a/Base/usr/share/man/man1/json.md b/Base/usr/share/man/man1/json.md deleted file mode 100644 index 2f38291c201..00000000000 --- a/Base/usr/share/man/man1/json.md +++ /dev/null @@ -1,39 +0,0 @@ -## Name - -json - pretty-print a JSON file with syntax-coloring and indentation - -## Synopsis - -```**sh -$ json [options...] [path...] -``` - -## Description - -`json` pretty-prints a JSON file with syntax-coloring and indentation. - -If no *path* argument is provided stdin is used. - -## Options - -* `--help`: Display this message -* `-i`, `--indent-size`: Size of indentations in spaces -* `-q`, `--query`: Dotted query key - -## Arguments - -* `path`: file to pretty-print - -## Examples - -```sh -# Pretty-print stdin -$ cat /sys/kernel/processes | json -# Pretty-print a file -$ json json-data.json -# Pretty-print a file with two spaces per indent -$ json -i 2 json-data.json -# Query data from JSON -$ json -q 1 .config/CommonLocations.json -$ cat /sys/kernel/processes | json -q processes -``` diff --git a/Base/usr/share/man/man1/keymap.md b/Base/usr/share/man/man1/keymap.md deleted file mode 100644 index 838f5abc8d7..00000000000 --- a/Base/usr/share/man/man1/keymap.md +++ /dev/null @@ -1,38 +0,0 @@ -## Name - -keymap - load a keyboard layout - -## Synopsis - -```**sh -$ keymap [--set-keymap keymap] [--set-keymaps keymaps] -``` - -## Description - -The `keymap` utility can be used to configure the list of selected keyboard layout and switch between them. - -Layouts loaded from `/res/keymaps/*.json`. - -## Options - -* `-m keymap`, `--set-keymap keymap`: The mapping to be used -* `-s keymaps`, `--set-keymaps keymaps`: Comma separated list of enabled mappings - -## Examples - -Get name of the currently set keymap: -```sh -$ keymap -en-us -``` - -Select a new list of keymaps: -```sh -$ keymap --set-keymaps en-us,ru -``` - -Set a keymap: -```sh -$ keymap --set-keymap ru -``` diff --git a/Base/usr/share/man/man1/less.md b/Base/usr/share/man/man1/less.md deleted file mode 100644 index fa2afcd8c7c..00000000000 --- a/Base/usr/share/man/man1/less.md +++ /dev/null @@ -1,81 +0,0 @@ -## Name - -`less` - A full-featured terminal pager. - -## Synopsis - -```**sh -$ cat file | less [-PXNem] -$ less [-PXNem] [file] -$ cat file | more -$ more [file] -``` - -## Description - -`less` is a terminal pager that allows backwards movement. It is inspired by, -but largely incompatible with -[GNU less](https://www.greenwoodsoftware.com/less/index.html). - -## Options - -* `-P`, `--prompt`: Set the prompt format string. See [Prompts](#prompts) for more details. -* `-X`, `--no-init`: Don't switch to the xterm alternate buffer on startup. -* `-N`, `--line-numbers`: Show line numbers before printed lines. -* `-e`, `--quit-at-eof`: Immediately exit less when the last line of the document is reached. -* `-F`, `--quit-if-one-screen`: Exit immediately if the entire file can be displayed on one screen. -* `-m`, `--emulate-more`: Apply `-Xe`, set the prompt to `--More--`, and disable - scrollback. This option is automatically applied when `less` is executed as `more` - -## Commands - -Commands may be preceded by a decimal number `N`. Currently, this feature -does not exist, and no command use `N`. - -| Command | Description | -|---------|-------------| -| `q` | Exit less. | -| `j` or `DOWNARROW` or `ENTER` | Go to the next line. | -| `k` or `UPARROW` | Go to the previous line. | -| `f` or `SPACE` | Go to the next page. | -| `b` | Go to the previous page. | - -## Prompts - -`less` accepts a special prompt string with the `-P` option. prompts accept a -variety of format specifiers so that they adapt to `less`'s state. - -A `%` followed by a letter will be replaced with the associated variable. If -such a variable does not exist, or currently has no value, it will be replaced -with a `?`. - -| Variable | Description | -|----------|-------------| -| `%f` | Replaced with the name of the current file | -| `%l` | Replaced with the current line number | - -A `?` followed by a letter acts as an if expression on the associated -condition. `:` then acts as the else, and `.` acts as the end token. for example -if you wanted to print 'true' if you are at the end of the file and 'false' -otherwise, your prompt would be `?etrue:false.`. These expressions are -arbitrarily nestable. If a condition does not exist, then it will always -evaluate it's false branch. - -| Condition | Description | -|----------|-------------| -| `?f` | True when reading from a file other than stdin. | -| `?e` | True when at the end of a file. | - -A `\\` followed by any character will be replaced with the literal value of the -character. For instance, `\\%l` would render as `%l`. - -All other characters are treated normally. - -#### Examples - -`less`'s current default prompt: `'?f%f :.(line %l)?e (END):.'` - -## See Also - -* [`more`(1)](help://man/1/more) For a simpler pager that less implements. -* [`man`(1)](help://man/1/man) For serenity's manual pager, that uses less. diff --git a/Base/usr/share/man/man1/listdir.md b/Base/usr/share/man/man1/listdir.md deleted file mode 100644 index bb63324537a..00000000000 --- a/Base/usr/share/man/man1/listdir.md +++ /dev/null @@ -1,43 +0,0 @@ -## Name - -lsdir - list directory entries - -## Synopsis - -```**sh -# lsdir [options...] [path...] -``` - -## Description - -This utility will list all directory entries of a given path (or list of paths) -and print their inode number and file type (in either POSIX DT_* format or human readable). - -The utility uses `LibCore` `DirIterator` object and restrict its functionality -to the `get_dir_entries` syscall only, to get the raw values of each directory -entry. - -## Options - -* `-P`, `--posix-names`: Show POSIX names for file types -* `-t`, `--total-entries-count`: Print count of listed entries when traversing a directory - -## Arguments - -* `path`: Directory to list - -## Examples - -```sh -# List directory entries of working directory -$ lsdir -# List directory entries of /proc directory -$ lsdir /proc -# List directory entries of /proc directory with POSIX names for file types -$ lsdir -P /proc -# List directory entries of /proc directory and print in the end the count of traversed entries -$ lsdir -t /proc -``` - -## See also -* [`ls`(1)](help://man/1/ls) diff --git a/Base/usr/share/man/man1/ls.md b/Base/usr/share/man/man1/ls.md deleted file mode 100644 index e2873d2d283..00000000000 --- a/Base/usr/share/man/man1/ls.md +++ /dev/null @@ -1,72 +0,0 @@ -## Name - -ls - list directory contents - -## Synopsis - -```**sh -$ ls [options...] [path...] -``` - -## Description - -`ls` lists directory contents and attributes. - -If no *path* argument is provided the current working directory is used. - -## Options - -* `--help`: Display this message -* `-a`, `--all`: Show dotfiles -* `-A`: Do not list implied . and .. directories -* `-B`, `--ignore-backups`: Do not list implied entries ending with ~ -* `-F`, `--classify`: Append a file type indicator to entries -* `-p`: Append a '/' indicator to directories -* `-d`, `--directory`: List directories themselves, not their contents -* `-l`, `--long`: Display long info -* `-t`: Sort files by timestamp (newest first) -* `-S`: Sort files by size (largest first) -* `-r`, `--reverse`: Reverse sort order -* `-G`: Use pretty colors -* `-i`, `--inode`: Show inode ids -* `-I`, `--raw-inode`: Show raw inode ids if possible (see Notes to understand when this will not work) -* `-n`, `--numeric-uid-gid`: In long format, display numeric UID/GID. Implies `-l` -* `-o`: In long format, do not show group information. Implies `-l` -* `-g`: In long format, do not show owner information. Implies `-l` -* `-h`, `--human-readable`: Print human-readable sizes -* `--si`: Print human-readable sizes in SI units -* `-K`, `--no-hyperlinks`: Disable hyperlinks -* `-R`, `--recursive`: List subdirectories recursively -* `-1`: List one file per line - -## Arguments - -* `path`: Directory to list - -## Examples - -```sh -# List contents of working directory -$ ls -# List contents of working directory including hidden dot files -$ ls -la -# List contents of working directory and its subdirectories -$ ls -R -# List contents of /etc/ directory -$ ls /etc -# List contents of /etc/ directory including hidden dot files -$ ls -la /etc -``` - -## Notes - -Printing raw inode numbers is only possible when listing an entire directory. -This happens because the program uses the LibC `readdir` function, which -will provide the raw inode numbers as they're appearing "on disk". -In other cases, when strictly using the LibC `lstat` function the kernel -will resolve the inode number with respect to the mount table, so if there -is a mounted filesystem on a directory entry, `lstat` will give the root -inode number for that filesystem. - -## See also -* [`tree`(1)](help://man/1/tree) to show the contents of the directory and subdirectories in a tree visualization diff --git a/Base/usr/share/man/man1/lsjails.md b/Base/usr/share/man/man1/lsjails.md deleted file mode 100644 index 1fd04b1b667..00000000000 --- a/Base/usr/share/man/man1/lsjails.md +++ /dev/null @@ -1,26 +0,0 @@ -## Name - -lsjails - list existing jails - -## Synopsis - -```**sh -# lsjails -``` - -## Description - -This utility will list all existing jails at the moment of invoking this program. -Please note that if the current process is in jail, it will not see any jail. - -## Examples - -```sh -# lsjails -Index Name -2 test-jail -``` - -## See also -* [`jail-create`(1)](help://man/1/jail-create) -* [`jail-attach`(1)](help://man/1/jail-attach) diff --git a/Base/usr/share/man/man1/lsof.md b/Base/usr/share/man/man1/lsof.md deleted file mode 100644 index 6d6691679db..00000000000 --- a/Base/usr/share/man/man1/lsof.md +++ /dev/null @@ -1,28 +0,0 @@ -## Name - -lsof - -## Synopsis - -```sh -$ lsof [-p pid] [-d fd] [-u login/UID] [-g PGID] [filename] -``` - -## Description - -List open files of a processes. This can mean actual files in the file system, sockets, pipes, etc. - -## Options - -* `-p pid`: Select by PID -* `-d fd`: Select by file descriptor -* `-u login/UID`: Select by login/UID -* `-g PGID`: Select by process group ID - -## Arguments - -* `filename`: Filename - -## See Also -* [`pmap`(1)](help://man/1/pmap) -* [`ps`(1)](help://man/1/ps) diff --git a/Base/usr/share/man/man1/man.md b/Base/usr/share/man/man1/man.md deleted file mode 100644 index fd6d54bdc7b..00000000000 --- a/Base/usr/share/man/man1/man.md +++ /dev/null @@ -1,47 +0,0 @@ -## Name - -man - read manual pages - -## Synopsis - -```**sh -$ man page -$ man section page -``` - -## Description - -`man` finds, loads and displays the so-called manual pages, -or man pages for short, from the SerenityOS manual. You're reading -the manual page for `man` program itself right now. - -## Options - -* `-P pager`, `--pager pager`: Pager to pipe the man page to - -## Examples - -To open documentation for the `echo` command: -```sh -$ man echo -``` - -To open the documentation for the `mkdir` command: -```sh -$ man 1 mkdir -``` -Conversely, to open the documentation about the `mkdir()` syscall: -```sh -$ man 2 mkdir -``` - -## Files - -`man` looks for man pages under `/usr/share/man`. For example, -this man page should be located at `/usr/share/man/man1/man.md`. - -## See Also - -* [`less`(1)](help://man/1/less) For the terminal pager that `man` uses by default -* [`Help`(1)](help://man/1/Applications/Help) To read these same man pages in a GUI -* [`man`(7)](help://man/7/man) For an overview on how manpages are organized diff --git a/Base/usr/share/man/man1/md.md b/Base/usr/share/man/man1/md.md deleted file mode 100644 index b06509bf667..00000000000 --- a/Base/usr/share/man/man1/md.md +++ /dev/null @@ -1,31 +0,0 @@ -## Name - -md - render markdown documents - -## Synopsis - -```**sh -$ md [--html] [input-file.md] -``` - -## Description - -Read a Markdown document and render it using either terminal -escape sequences (the default) or HTML. If a file name is given, -`md` reads the document from that file; by default it reads its -standard input. - -## Options - -* `-H`, `--html`: Render the document into HTML. - -## Examples - -Here's how you can render this man page into HTML: - -```sh -$ md --html /usr/share/man/man1/md.md -``` - -## See Also -* [`man`(1)](help://man/1/man) to more easily read manpages diff --git a/Base/usr/share/man/man1/md5sum.md b/Base/usr/share/man/man1/md5sum.md deleted file mode 120000 index 220a5d40cfe..00000000000 --- a/Base/usr/share/man/man1/md5sum.md +++ /dev/null @@ -1 +0,0 @@ -checksum.md \ No newline at end of file diff --git a/Base/usr/share/man/man1/memstat.md b/Base/usr/share/man/man1/memstat.md deleted file mode 100644 index 1e930195a50..00000000000 --- a/Base/usr/share/man/man1/memstat.md +++ /dev/null @@ -1,36 +0,0 @@ -## Name - -memstat - show memory statistics - -## Synopsis - -```**sh -$ memstat -``` - -## Options - -* `-h` , `--human-readable`: Print human-readable sizes - -## Examples - -```sh -$ memstat -Kmalloc allocated: 7894528/10979648 -Physical pages (in use) count: 172249088/1016643584 -Physical pages (committed) count: 125521920 -Physical pages (uncommitted) count: 718872576 -Physical pages (total) count: 248204 -Kmalloc call count: 77475 -Kfree call count: 59575 -Kmalloc/Kfree delta: +17900 -$ memstat -h -Kmalloc allocated: 7.5 MiB (7,908,928 bytes) / 10.4 MiB (10,978,624 bytes) -Physical pages (in use) count: 164.8 MiB (172,838,912 bytes) / 969.5 MiB (1,016,643,584 bytes) -Physical pages (committed) count: 119.6 MiB (125,485,056 bytes) -Physical pages (uncommitted) count: 685.0 MiB (718,319,616 bytes) -Physical pages (total) count: 248204 -Kmalloc call count: 78714 -Kfree call count: 60777 -Kmalloc/Kfree delta: +17937 -``` diff --git a/Base/usr/share/man/man1/mkdir.md b/Base/usr/share/man/man1/mkdir.md deleted file mode 100644 index fbb31e16fcb..00000000000 --- a/Base/usr/share/man/man1/mkdir.md +++ /dev/null @@ -1,32 +0,0 @@ -## Name - -mkdir - create directories - -## Synopsis - -```**sh -$ mkdir [options...] directories... -``` - -## Description - -Create a new empty directory for each of the given *directories*. - -## Options - -* `-p`, `--parents`: Create parent directories if they don't exist -* `-m`, `--mode`: Sets the permissions for the final directory (possibly altered by the process umask). The mode argument can be given in any of the formats -accepted by the chmod(1) command. Addition and removal of permissions is relative to a default permission of 0777. -* `-v`, `--verbose`: Print a message for each created directory - -## Examples - -```sh -$ mkdir -p /tmp/foo/bar -$ mkdir -m 0700 /tmp/owner-only -$ mkdir -m a=rx /tmp/foo/bar -``` - -## See also - -* [`mkdir`(2)](help://man/2/mkdir) diff --git a/Base/usr/share/man/man1/mktemp.md b/Base/usr/share/man/man1/mktemp.md deleted file mode 100644 index 016ad7e46a9..00000000000 --- a/Base/usr/share/man/man1/mktemp.md +++ /dev/null @@ -1,38 +0,0 @@ -## Name - -mktemp - create a temporary file or directory - -## Synopsis - -```**sh -$ mktemp [--directory] [--dry-run] [--quiet] [--tmpdir DIR] [template] -``` - -## Description - -`mktemp` creates temporary a file or directory safely, and then prints its name. - -A template may be specified and will be used instead of the default tmp.XXXXXXXXXX -as long as it contains at least 3 consecutive 'X's. - -## Options - -* `-d`, `--directory`: Create a temporary directory instead of a file -* `-u`, `--dry-run`: Do not create anything, just print a unique name -* `-q`, `--quiet`: Do not print diagnostics about file/directory creation failure -* `-p`, `--tmpdir`: Create temporary files relative to this directory - -## Examples - -```sh -# Create a temporary file -$ mktemp -# Find an available temporary file name -$ mktemp -u -# Create a temporary directory with a custom template -$ mktemp -d serenity.XXXXX -``` - -## See also -* [`mkdir`(1)](help://man/1/mkdir) to create a regular directory -* [`touch`(1)](help://man/1/touch) to create a regular file diff --git a/Base/usr/share/man/man1/more.md b/Base/usr/share/man/man1/more.md deleted file mode 100644 index d8267345f96..00000000000 --- a/Base/usr/share/man/man1/more.md +++ /dev/null @@ -1,26 +0,0 @@ -## Name - -more - page through text - -## Synopsis - -```**sh -$ more -``` - -## Description - -`more` reads data from standard input and prints it to standard output, screen by screen. -`more` is a symlink for [`less`(1)](help://man/1/less), which has a more emulation mode. - -## Examples - -```sh -# dmesg | more -... ---More-- -``` - -## See Also - -* [`less`(1)](help://man/1/less) For the more advanced terminal pager that implements more. diff --git a/Base/usr/share/man/man1/mv.md b/Base/usr/share/man/man1/mv.md deleted file mode 100644 index d753a477676..00000000000 --- a/Base/usr/share/man/man1/mv.md +++ /dev/null @@ -1,31 +0,0 @@ -## Name - -mv - move (rename) files - -## Synopsis - -```**sh -$ mv [options...] -$ mv [options...] -``` - -## Description - -`mv` renames file `old name` to `new name` or moves all `source` files into `destination`. - -## Options - -* `-f`, `--force`: Do not prompt before overwriting (not implemented for now) -* `-n`, `--no-clobber`: Do not overwrite existing files -* `-v`, `--verbose`: Display all moved files - -## Examples - -```sh -$ mv bin /usr -$ mv *.cpp /usr/src -$ mv old.txt new.txt -``` - -## See also -* [`cp`(1)](help://man/1/cp) diff --git a/Base/usr/share/man/man1/nc.md b/Base/usr/share/man/man1/nc.md deleted file mode 100644 index 3d94cc40de2..00000000000 --- a/Base/usr/share/man/man1/nc.md +++ /dev/null @@ -1,30 +0,0 @@ -## Name - -nc - -## Synopsis - -```sh -$ nc [--length ] [--listen] [-N] [-n] [--udp] [-p port] [--verbose] [target] [port] -``` - -## Description - -Network cat: Connect to network sockets as if it were a file. - -## Options - -* `-I`, `--length`: Set maximum tcp receive buffer size -* `-l`, `--listen`: Listen instead of connecting -* `-N`: Close connection after reading stdin to the end -* `-n`: Suppress name resolution -* `-u`, `--udp`: UDP mode -* `-p port`: Local port for remote connections -* `-v`, `--verbose`: Log everything that's happening - -## Arguments - -* `target`: Address to listen on, or the address or hostname to connect to -* `port`: Port to connect to or listen on - - diff --git a/Base/usr/share/man/man1/netstat.md b/Base/usr/share/man/man1/netstat.md deleted file mode 100644 index 730edeb79c2..00000000000 --- a/Base/usr/share/man/man1/netstat.md +++ /dev/null @@ -1,27 +0,0 @@ -## Name - -netstat - -## Synopsis - -```sh -$ netstat [--all] [--list] [--tcp] [--udp] [--numeric] [--program] [--wide] [--extend] -``` - -## Description - -Display network connections - -## Options - -* `-a`, `--all`: Display both listening and non-listening sockets -* `-l`, `--list`: Display only listening sockets -* `-t`, `--tcp`: Display only TCP network connections -* `-u`, `--udp`: Display only UDP network connections -* `-n`, `--numeric`: Display numerical addresses -* `-p`, `--program`: Show the PID and name of the program to which each socket belongs -* `-W`, `--wide`: Do not truncate IP addresses by printing out the whole symbolic host -* `-e`, `--extend`: Display more information - -## See Also -* [`ifconfig`(1)](help://man/1/ifconfig) diff --git a/Base/usr/share/man/man1/nl.md b/Base/usr/share/man/man1/nl.md deleted file mode 100644 index 7e2d4476256..00000000000 --- a/Base/usr/share/man/man1/nl.md +++ /dev/null @@ -1,23 +0,0 @@ -## Name - -nl - -## Synopsis - -```sh -$ nl [--body-numbering style] [--increment number] [--separator string] [--startnum number] [--width number] [file...] -``` - -## Options - -* `-b style`, `--body-numbering style`: Line numbering style: 't' for non-empty lines, 'a' for all lines, 'n' for no lines -* `-i number`, `--increment number`: Line count increment -* `-s string`, `--separator string`: Separator between line numbers and lines -* `-v number`, `--startnum number`: Initial line number -* `-w number`, `--width number`: Number width - -## Arguments - -* `file`: Files to process - - diff --git a/Base/usr/share/man/man1/nohup.md b/Base/usr/share/man/man1/nohup.md deleted file mode 100644 index 5a7d85bafd3..00000000000 --- a/Base/usr/share/man/man1/nohup.md +++ /dev/null @@ -1,30 +0,0 @@ -## Name - -nohup - Invoke a utility that will ignore SIGHUPs - -## Synopsis - -```sh -$ nohup [args...] -``` - -## Description -`nohup` will invoke `utility` given an optional list of `args`, where at the time of the invocation it will ignore the SIGHUP signal. - -If the standard output is a tty, `utility` will append its standard output to the end of the file **nohup.out** in the current directory. If **nohup.out** fails to be created or opened for -appending, `utility`'s standard output will instead be appended to the end of the file **$HOME/nohup.out**. If this also fails, `utility` won't be invoked. **nohup.out**'s -permission bits are set to S_IRUSR | S_IWUSR when it is created. - -If standard error is a tty, one out of the following will occur: -1. If the standard output is open but not a tty, `utility` will redirect its standard error to its standard output. -2. If the standard output is a tty or is closed, standard error shall be appended to the end of **nohup.out** as described above. - -## Arguments - -* `utility`: Utility to be invoked -* `args`: Arguments to pass to `utility` - -## Exit Status - -* 126 - `utility` was found but could not be invoked. -* 127 - Either `utility` could not be found or an error occurred in `nohup`. diff --git a/Base/usr/share/man/man1/notify.md b/Base/usr/share/man/man1/notify.md deleted file mode 100644 index 772d875a2b0..00000000000 --- a/Base/usr/share/man/man1/notify.md +++ /dev/null @@ -1,25 +0,0 @@ -## Name - -notify - create a notification - -## Synopsis - -```**sh -$ notify <message> [icon-path] -``` - -## Arguments - -* `title`: The title of notification -* `message`: The message of notification -* `icon-path`: Path to icon image file - -## Description - -`notify` creates a WindowServer notification with title `title` and message `message`. You can also provide an icon path; by default, no icon will be used. - -## Examples - -```sh -$ <command> && notify "Information" "Command succeeded" /res/icons/32x32/msgbox-information.png -``` diff --git a/Base/usr/share/man/man1/ntpquery.md b/Base/usr/share/man/man1/ntpquery.md deleted file mode 100644 index 51f07a3019c..00000000000 --- a/Base/usr/share/man/man1/ntpquery.md +++ /dev/null @@ -1,22 +0,0 @@ -## Name - -ntpquery - -## Synopsis - -```sh -$ ntpquery [--adjust] [--set] [--verbose] [host] -``` - -## Options - -* `-a`, `--adjust`: Gradually adjust system time (requires root) -* `-s`, `--set`: Immediately set system time (requires root) -* `-v`, `--verbose`: Verbose output - -## Arguments - -* `host`: NTP server - -## See Also -* [`date`(1)](help://man/1/date) diff --git a/Base/usr/share/man/man1/open.md b/Base/usr/share/man/man1/open.md deleted file mode 100644 index 7d671959f6a..00000000000 --- a/Base/usr/share/man/man1/open.md +++ /dev/null @@ -1,22 +0,0 @@ -## Name - -open - open file or URL - -## Synopsis - -```**sh -$ open <file-or-url...> -``` - -## Description - -Open the given file(s), or the given URL(s), in the standard viewer for that -kind of file. For non-file URLs, open the URL in the Browser. - -## Examples - -```sh -$ open https://serenityos.org https://example.org -$ open /proc -$ open /etc/fstab -``` diff --git a/Base/usr/share/man/man1/passwd.md b/Base/usr/share/man/man1/passwd.md deleted file mode 100644 index 7932cdc3287..00000000000 --- a/Base/usr/share/man/man1/passwd.md +++ /dev/null @@ -1,25 +0,0 @@ -## Name - -passwd - -## Synopsis - -```sh -$ passwd [--delete] [--lock] [--unlock] [username] -``` - -## Description - -Modify an account password. - -## Options - -* `-d`, `--delete`: Delete password -* `-l`, `--lock`: Lock password -* `-u`, `--unlock`: Unlock password - -## Arguments - -* `username`: Username - -<!-- Auto-generated through ArgsParser --> diff --git a/Base/usr/share/man/man1/pgrep.md b/Base/usr/share/man/man1/pgrep.md deleted file mode 100644 index ba0df8c1d3c..00000000000 --- a/Base/usr/share/man/man1/pgrep.md +++ /dev/null @@ -1,30 +0,0 @@ -## Name - -pgrep - look up processes based on name - -## Synopsis - -```sh -$ pgrep [--count] [-d delimiter] [--ignore-case] [--list-name] [--newest] [--oldest] [--older seconds] [--uid uid-list] [--invert-match] [--exact] <process-name> -``` - -## Options - -* `-c`, `--count`: Suppress normal output and print the number of matching processes -* `-d`, `--delimiter`: Set the string used to delimit multiple pids -* `-i`, `--ignore-case`: Make matches case-insensitive -* `-l`, `--list-name`: List the process name in addition to its pid -* `-n`, `--newest`: Select the most recently created process only -* `-o`, `--oldest`: Select the least recently created process only -* `-O`, `--older`: Select only processes older than the specified number of seconds -* `-U uid-list`, `--uid uid-list`: Select only processes whose UID is in the given comma-separated list. Login name or numerical user ID may be used -* `-x`, `--exact`: Select only processes whose names match the given pattern exactly -* `-v`, `--invert-match`: Select non-matching lines - -## Arguments - -* `process-name`: Process name to search for - -## See also -* [`pidof`(1)](help://man/1/pidof) -* [`ps`(1)](help://man/1/ps) diff --git a/Base/usr/share/man/man1/pidof.md b/Base/usr/share/man/man1/pidof.md deleted file mode 100644 index 42da018a635..00000000000 --- a/Base/usr/share/man/man1/pidof.md +++ /dev/null @@ -1,23 +0,0 @@ -## Name - -pidof - list pids for a given process name - -## Synopsis - -```sh -$ pidof [-s] [-o pid] [-S separator] <process-name> -``` - -## Options - -* `-o pid`: Omit the given pid, or the parent process if the special value %PPID is passed -* `-s`: Only return one pid -* `-S separator`: Use `separator` to separate multiple pids - -## Arguments - -* `process-name`: Process name to search for - -## See also -* [`pgrep`(1)](help://man/1/pgrep) -* [`ps`(1)](help://man/1/ps) diff --git a/Base/usr/share/man/man1/pkg.md b/Base/usr/share/man/man1/pkg.md deleted file mode 100644 index 7a01fe7a70f..00000000000 --- a/Base/usr/share/man/man1/pkg.md +++ /dev/null @@ -1,33 +0,0 @@ -## Name - -pkg - Package Manager - -## Synopsis - -```**sh -$ pkg [-l] [-u] [-v] [-q package] -``` - -## Description - -This program can list installed packages and query for [available packages](https://github.com/SerenityOS/serenity/blob/master/Ports/AvailablePorts.md). The [Ports for SerenityOS website](https://ports.serenityos.net) has more detailed information about available packages. - -It does not currently support installing and uninstalling packages. To install third-party software use the [Ports system](https://github.com/SerenityOS/serenity/blob/master/Ports/README.md). - -## Options - -* `-l`, `--list-manual-ports`: Show all manually-installed ports -* `-u`, `--update-ports-database`: Sync/Update ports database -* `-v`, `--verbose`: Verbose output -* `-q`, `--query-package`: Query the ports database for package name - -## Arguments - -* `package`: The name of the package you want to query - -## Example - -```sh -# Query the ports database for the serenity-theming package -$ pkg -q serenity-theming -``` diff --git a/Base/usr/share/man/man1/pkill.md b/Base/usr/share/man/man1/pkill.md deleted file mode 100644 index cddb9f79b4e..00000000000 --- a/Base/usr/share/man/man1/pkill.md +++ /dev/null @@ -1,28 +0,0 @@ -## Name - -pkill - Signal processes based on name - -## Synopsis - -```sh -$ pkill [--count] [--ignore-case] [--echo] [--newest] [--oldest] [--older seconds] [--signal signame] [--uid uid-list] [--exact] <process-name> -``` - -## Options - -* `-c`, `--count`: Display the number of matching processes -* `-i`, `--ignore-case`: Make matches case-insensitive -* `-e`, `--echo`: Display what is killed -* `-n`, `--newest`: Kill the most recently created process only -* `-o`, `--oldest`: Select the least recently created process only -* `-O`, `--older`: Select only processes older than the specified number of seconds -* `-s signame`, `--signal signame`: Signal to send. The signal name or number may be used -* `-U uid-list`, `--uid uid-list`: Select only processes whose UID is in the given comma-separated list. Login name or numerical user ID may be used -* `-x`, `--exact`: Select only processes whose names match the given pattern exactly - -## Arguments - -* `process-name`: Process name to search for - -## See Also -* [`ps`(1)](help://man/1/ps) diff --git a/Base/usr/share/man/man1/pmap.md b/Base/usr/share/man/man1/pmap.md deleted file mode 100644 index 2c6751db022..00000000000 --- a/Base/usr/share/man/man1/pmap.md +++ /dev/null @@ -1,27 +0,0 @@ -## Name - -pmap - print memory map of a process - -## Synopsis - -```**sh -$ pmap [-x] PID -``` - -## Description - -Print the memory map of a specified process. - -## Options - -* `-x`: Extended output - -## Examples - -```sh -$ pmap $$ -``` - -## See also -* [`lsof`(1)](help://man/1/lsof) -* [`ps`(1)](help://man/1/ps) diff --git a/Base/usr/share/man/man1/pmemdump.md b/Base/usr/share/man/man1/pmemdump.md deleted file mode 100644 index 670b991a4d6..00000000000 --- a/Base/usr/share/man/man1/pmemdump.md +++ /dev/null @@ -1,38 +0,0 @@ -## Name - -pmemdump - dump physical memory - -## Synopsis - -```**sh -$ pmemdump [-r] <offset> <length> -``` - -## Description - -Dump a portion of the physical memory space. - - -## Options - -* `-r`: Dump from /dev/mem with `read(2)` instead of doing `mmap(2)` on it. - -## Examples - -```sh -$ pmemdump -r 983040 65536 -$ pmemdump 983040 65536 -``` - -## Notes - -The pmemdump utility opens the `/dev/mem` file, and gets a mapping by doing `mmap(2)` -on it. - -Using the `-r` flag might be useful sometimes, especially when reading from an unaligned -reserved physical memory region when trying to `mmap(2)` `/dev/mem` on the specified -offset fails. - -## See also - -* [`mem`(4)](help://man/4/mem) diff --git a/Base/usr/share/man/man1/printf.md b/Base/usr/share/man/man1/printf.md deleted file mode 100644 index 995c99c2539..00000000000 --- a/Base/usr/share/man/man1/printf.md +++ /dev/null @@ -1,53 +0,0 @@ -## Name - -printf - format and print data - -## Synopsis - -```**sh -$ printf <format> [arguments...] -``` - -## Description - -`printf` formats _argument_(s) according to _format_ and prints the result to standard output. - -_format_ is similar to the C printf format string, with the following differences: -- The format specifier `b` (`%b`) is not supported. -- The format specifiers that require a writable pointer (e.g. `n`) are not supported. -- The format specifier `q` (`%q`) has a different behavior, where it shall print a given string as a quoted string, which is safe to use in shell inputs. -- Common escape sequences are interpreted, namely the following: - -| escape | description | -| :-: | :--- | -| `\\\\`| literal backslash | -| `\\"` | literal double quote | -| `\\a` | alert (BEL) | -| `\\b` | backspace | -| `\\c` | Ends the format string | -| `\\e` | escape (`\\x1b`) | -| `\\f` | form feed | -| `\\n` | newline | -| `\\r` | carriage return | -| `\\t` | tab | -| `\\v` | vertical tab | - - -The _format_ string is reapplied until all arguments are consumed, and a missing argument is treated as zero for numeric format specifiers, and an empty string for string format specifiers. - -## Examples - -```sh -# print 64 as a hexadecimal number, with the starting '0x' -$ printf '%#x' 64 - -# prints "a0\n", ignoring everything after '\c' -$ printf '%s%d\n\caaaa' a - -# prints "123400", as 'x' is an invalid number, and the missing argument for the last '%d' is treated as zero. -$ printf '%d%d%d' 1 2 3 4 x -``` - -## See also - -* [`echo`(1)](help://man/1/echo) diff --git a/Base/usr/share/man/man1/profile.md b/Base/usr/share/man/man1/profile.md deleted file mode 100644 index 166b57d18ce..00000000000 --- a/Base/usr/share/man/man1/profile.md +++ /dev/null @@ -1,45 +0,0 @@ -## Name - -profile - Process or system profiler - -## Synopsis - -```**sh -$ profile [-p PID] [-a] [-e] [-d] [-f] [-w] [-t event_type] [COMMAND_TO_PROFILE] -``` - -## Description - -`profile` records profiling information that can then be read with `ProfileViewer`. - -## Options - -* `-p PID`: Target PID -* `-a`: Profile all processes (super-user only), result at /sys/kernel/profile -* `-e`: Enable -* `-d`: Disable -* `-f`: Free the profiling buffer for the associated process(es). -* `-w`: Enable profiling and wait for user input to disable. -* `-t event_type`: Enable tracking specific event type - -Event type can be one of: sample, context_switch, page_fault, syscall, read, kmalloc and kfree. - -## Examples - -```sh -# Enable whole-system profiling -$ profile -ae -# ...then, to stop -$ profile -ad - -# Profile a running process, with PID 42 -$ profile -p 42 - -# Profile syscalls made by echo -$ profile -t syscall -- echo "Hello friends!" -``` - -## See also - -* [`Profiler`(1)](help://man/1/Applications/Profiler) GUI for viewing profiling data produced by `profile`. -* [`strace`(1)](help://man/1/strace) diff --git a/Base/usr/share/man/man1/ps.md b/Base/usr/share/man/man1/ps.md deleted file mode 100644 index 742b37e8b3f..00000000000 --- a/Base/usr/share/man/man1/ps.md +++ /dev/null @@ -1,63 +0,0 @@ -## Name - -ps - list currently running processes - -## Synopsis - -```**sh -$ ps [--version] [-a] [-A] [-e] [-f] [-o column-format] [-p pid-list] [--ppid pid-list] [-q pid-list] [-t tty-list] [-u user-list] -``` - -## Description - -Print a list of currently running processes in the current TTY. -For each process, print its PID (process ID), to which TTY it belongs, and invoking commandline (CMD). - -## Options - -* `-a`: Consider all processes that are associated with a TTY. -* `-A` or `-e`: Consider all processes, not just those in the current TTY. -* `-f`: Also print for each process: UID (as resolved username), PPID (parent PID), and STATE (Runnable, Sleeping, Selecting, Reading, etc.) -* `-o column-format`: Specify a user-defined format, as a list of column format specifiers separated by commas or spaces. - - A column format specifier is of the form: `COLUMN_NAME[=COLUMN_TITLE]`. - Where `COLUMN_NAME` is any of the following: `uid`, `pid`, `ppid`, `pgid`, `sid`, `state`, `tty`, or `cmd`. - - Specifying a `COLUMN_TITLE` will change the name shown in the column header. `COLUMN_TITLE` may be blank. - If all given column titles are blank, the column header is omitted. - -* `-p pid-list`: Select processes matching any of the given PIDs. `pid-list` is a list of PIDs, separated by commas or spaces. -* `--ppid pid-list`: Select processes whose PPID matches any of the given PIDs. `pid-list` is a list of PIDs, separated by commas or spaces. -* `-q pid-list`: Only consider the given PIDs, if they exist. Output the processes in the order provided by `pid-list`. `pid-list` is a list of PIDs, separated by commas or spaces. -* `-t tty-list`: Select processes associated with any of the given terminals. `tty-list` is a list of short TTY names (e.g: `pts:0`) or the full TTY device paths, separated by commas or spaces. -* `-u user-list`: Select processes matching any of the given UIDs. `user-list` is a list of UIDs or login names, separated by commas or spaces. - -## Examples - -Show all processes (full format): - -```sh -$ ps -ef -``` - -Show the PID, state and name of all processes - -```sh -$ ps -eo pid,state,cmd -``` - -Show the name and state of PID 42 and rename the first column from CMD to Command: - -```sh -$ ps -q 42 -o cmd=Command,state -``` - -Show name of PID 42 and omit the header entirely - -```sh -$ ps -q 42 -o cmd= -``` - -## See Also -* [`pmap`(1)](help://man/1/pmap) -* [`lsof`(1)](help://man/1/lsof) diff --git a/Base/usr/share/man/man1/pwd.md b/Base/usr/share/man/man1/pwd.md deleted file mode 100644 index be99452442d..00000000000 --- a/Base/usr/share/man/man1/pwd.md +++ /dev/null @@ -1,13 +0,0 @@ -## Name - -pwd - print name of working directory - -## Synopsis - -```**sh -$ pwd -``` - -## Description - -`pwd` prints the absolute pathname of the current working directory. \ No newline at end of file diff --git a/Base/usr/share/man/man1/readelf.md b/Base/usr/share/man/man1/readelf.md deleted file mode 100644 index ac814385006..00000000000 --- a/Base/usr/share/man/man1/readelf.md +++ /dev/null @@ -1,31 +0,0 @@ -## Name - -readelf - -## Synopsis - -```sh -$ readelf [--all] [--file-header] [--program-headers] [--section-headers] [--headers] [--syms] [--dyn-syms] [--dynamic] [--notes] [--relocs] [--unwind] [--checksec] [--string-dump section-name] <path> -``` - -## Options - -* `-a`, `--all`: Display all -* `-h`, `--file-header`: Display ELF header -* `-l`, `--program-headers`: Display program headers -* `-S`, `--section-headers`: Display section headers -* `-e`, `--headers`: Equivalent to: -h -l -S -s -r -d -n -u -c -* `-s`, `--syms`: Display the symbol table -* `--dyn-syms`: Display the dynamic symbol table -* `-d`, `--dynamic`: Display the dynamic section -* `-n`, `--notes`: Display core notes -* `-r`, `--relocs`: Display relocations -* `-u`, `--unwind`: Display unwind info -* `-c`, `--checksec`: Display security hardening info -* `-p section-name`, `--string-dump section-name`: Display the contents of a section as strings - -## Arguments - -* `path`: ELF path - -<!-- Auto-generated through ArgsParser --> diff --git a/Base/usr/share/man/man1/readlink.md b/Base/usr/share/man/man1/readlink.md deleted file mode 100644 index 0f70c1a1a4d..00000000000 --- a/Base/usr/share/man/man1/readlink.md +++ /dev/null @@ -1,27 +0,0 @@ -## Name - -readlink - get symlink target - -## Synopsis - -```**sh -$ readlink [--no-newline] <path...> -``` - -## Description - -Print targets of the specified symbolic links. - -## Options - -* `-n`, `--no-newline`: Do not output a newline after each target - -## Examples - -```sh -$ readlink /proc/self/cwd -``` - -## See also - -* [`readlink`(2)](help://man/2/readlink) diff --git a/Base/usr/share/man/man1/realpath.md b/Base/usr/share/man/man1/realpath.md deleted file mode 100644 index 0e09eba2561..00000000000 --- a/Base/usr/share/man/man1/realpath.md +++ /dev/null @@ -1,21 +0,0 @@ -## Name - -realpath - Print resolved file path - -## Synopsis - -```sh -$ realpath [options] <paths> -``` - -## Options - -* `-q`, `--quiet`: Suppress error messages. - -## Description - -Show the 'real' path of a file, by resolving all symbolic links along the way. - -## Arguments - -* `paths`: Paths to resolve diff --git a/Base/usr/share/man/man1/reboot.md b/Base/usr/share/man/man1/reboot.md deleted file mode 100644 index 9e105a10060..00000000000 --- a/Base/usr/share/man/man1/reboot.md +++ /dev/null @@ -1,18 +0,0 @@ -## Name - -reboot - Reboot the machine - -## Synopsis - -```**sh -$ reboot -``` - -## Description - -`reboot` instructs the kernel to reboot the machine immediately. - -## Notes - -The `reboot` utility opens the `/sys/kernel/power_state` node and writes the magic value "1" -to instruct the kernel to reboot the machine. diff --git a/Base/usr/share/man/man1/rev.md b/Base/usr/share/man/man1/rev.md deleted file mode 100644 index a89cc040d53..00000000000 --- a/Base/usr/share/man/man1/rev.md +++ /dev/null @@ -1,61 +0,0 @@ -## Name - -rev - reverse lines - -## Synopsis - -```*sh -$ rev [file...] -``` - -## Description - -`rev` reads the specified files line by line, and prints them to standard -output with each line being reversed characterwise. If no files are specified, -then `rev` will read from standard input. If the file `-` is specified then -`rev` also reads from standard input. - -## Arguments - -* `file`: Files to print - -## Examples - -To print two files 'foo' and 'bar' in reverse: -```sh -$ cat foo bar -foo 1 -foo 2 -bar 1 -bar 2 -$ rev foo bar -1 oof -2 oof -1 rab -2 rab -``` - -To list files with their names in reverse: -```sh -$ ls -foo -bar -$ ls | rev -oof -rab -``` - -To print a file 'foo' in reverse followed by the output of `ls` in reverse: -```sh -$ cat foo -foo 1 -foo 2 -$ ls -foo -bar -$ ls | rev foo - -1 oof -2 oof -oof -rab -``` diff --git a/Base/usr/share/man/man1/rm.md b/Base/usr/share/man/man1/rm.md deleted file mode 100644 index bf544b5a240..00000000000 --- a/Base/usr/share/man/man1/rm.md +++ /dev/null @@ -1,35 +0,0 @@ -## Name - -rm - Remove files - -## Synopsis - -```**sh -$ rm [options...] <path...> -``` - -## Description - -`rm` removes files specified in `path`. - -If a directory is specified in `path`, the `-r` (recursive) flag is required. Otherwise, an error occurs. - -## Options - -* `-r`, `--recursive`: Remove files and directories recursively -* `-f`, `--force`: Do not prompt before removing -* `-v`, `--verbose`: Display what files are removed -* `--no-preserve-root`: Do not treat '/' specially - -## Examples - -```sh -# Remove README.md file -$ rm README.md - -# Remove Tests directory -$ rm -r Tests -``` - -## See also -* [`rmdir`(1)](help://man/1/rmdir) to just delete empty directories diff --git a/Base/usr/share/man/man1/rmdir.md b/Base/usr/share/man/man1/rmdir.md deleted file mode 100644 index 8865770a0ec..00000000000 --- a/Base/usr/share/man/man1/rmdir.md +++ /dev/null @@ -1,46 +0,0 @@ -## Name - -rmdir - remove empty directories - -## Synopsis - -```**sh -$ rmdir `[directory...]` -``` - -## Description - -Remove given `directory(ies)`, if they are empty - -## Options - -* `-p`, `--parents`: Remove all directories in each given path -* `-v`, `--verbose`: List each directory as it is removed - -## Arguments - -* `directory`: directory(ies) to remove - -## Examples - -```sh -# Try to remove non-empty directory -$ mkdir serenity ; echo cool > serenity/cool.txt -$ rmdir serenity -rmdir: Directory not empty - -# Remove empty directory -$ mkdir example -$ ls -a example -. .. -$ rmdir example -$ ls -a example -example: No such file or directory - -# Removes foo/bar/baz/, foo/bar/ and foo/ -$ rmdir -p foo/bar/baz/ -``` - -## See also -* [`mkdir`(1)](help://man/1/mkdir) -* [`rm`(1)](help://man/1/rm) diff --git a/Base/usr/share/man/man1/sha1sum.md b/Base/usr/share/man/man1/sha1sum.md deleted file mode 120000 index 220a5d40cfe..00000000000 --- a/Base/usr/share/man/man1/sha1sum.md +++ /dev/null @@ -1 +0,0 @@ -checksum.md \ No newline at end of file diff --git a/Base/usr/share/man/man1/sha256sum.md b/Base/usr/share/man/man1/sha256sum.md deleted file mode 120000 index 220a5d40cfe..00000000000 --- a/Base/usr/share/man/man1/sha256sum.md +++ /dev/null @@ -1 +0,0 @@ -checksum.md \ No newline at end of file diff --git a/Base/usr/share/man/man1/sha512sum.md b/Base/usr/share/man/man1/sha512sum.md deleted file mode 120000 index 220a5d40cfe..00000000000 --- a/Base/usr/share/man/man1/sha512sum.md +++ /dev/null @@ -1 +0,0 @@ -checksum.md \ No newline at end of file diff --git a/Base/usr/share/man/man1/shot.md b/Base/usr/share/man/man1/shot.md deleted file mode 100644 index 6701c8cbdce..00000000000 --- a/Base/usr/share/man/man1/shot.md +++ /dev/null @@ -1,23 +0,0 @@ -## Name - -shot - -## Synopsis - -```sh -$ shot [--clipboard] [--delay seconds] [--screen index] [--region] [--edit] [output] -``` - -## Options - -* `-c`, `--clipboard`: Output to clipboard -* `-d seconds`, `--delay seconds`: Seconds to wait before taking a screenshot -* `-s index`, `--screen index`: The index of the screen (default: -1 for all screens) -* `-r`, `--region`: Select a region to capture -* `-e`, `--edit`: Open in PixelPaint - -## Arguments - -* `output`: Output filename - -<!-- Auto-generated through ArgsParser --> diff --git a/Base/usr/share/man/man1/shuf.md b/Base/usr/share/man/man1/shuf.md deleted file mode 100644 index f1aadaaab6f..00000000000 --- a/Base/usr/share/man/man1/shuf.md +++ /dev/null @@ -1,19 +0,0 @@ -## Name - -shuf - shuffle input lines randomly - -## Synopsis - -```sh -$ shuf [--head-count count] [--repeat] [--zero-terminated] [file] -``` - -## Options - -* `-n count`, `--head-count count`: Output at most "count" lines -* `-r`, `--repeat`: Pick lines at random rather than shuffling. The program will continue indefinitely if no `-n` option is specified -* `-z`, `--zero-terminated`: Split input on \0, not newline - -## Arguments - -* `file`: File diff --git a/Base/usr/share/man/man1/shutdown.md b/Base/usr/share/man/man1/shutdown.md deleted file mode 100644 index 42c0af7cf48..00000000000 --- a/Base/usr/share/man/man1/shutdown.md +++ /dev/null @@ -1,18 +0,0 @@ -## Name - -shutdown - Power off the machine - -## Synopsis - -```**sh -$ shutdown -``` - -## Description - -`shutdown` instructs the kernel to power off the machine immediately. - -## Notes - -The `shutdown` utility opens the `/sys/kernel/power_state` node and writes the magic value "2" -to instruct the kernel to power off the machine. diff --git a/Base/usr/share/man/man1/sleep.md b/Base/usr/share/man/man1/sleep.md deleted file mode 100644 index 2c993d91c4f..00000000000 --- a/Base/usr/share/man/man1/sleep.md +++ /dev/null @@ -1,23 +0,0 @@ -## Name - -sleep - delay for a specified number of seconds - -## Synopsis - -```**sh -$ sleep <seconds> -``` - -## Arguments - -* `seconds`: Number of seconds to sleep - -## Description - -`sleep` pauses execution for specified number of seconds. - -## Examples - -```sh -$ sleep 5 -``` diff --git a/Base/usr/share/man/man1/slugify.md b/Base/usr/share/man/man1/slugify.md deleted file mode 100644 index 651ad921daa..00000000000 --- a/Base/usr/share/man/man1/slugify.md +++ /dev/null @@ -1,25 +0,0 @@ -## Name - -slugify - text to slug transform utility - -## Synopsis - -```**sh -$ slugify [--format FORMAT] [--glue GLUE] [--single-page] [INPUTS...] -``` - -## Description - -Slugify is a simple text to slug transform utility and prints the result. - -## Options - -* `-f`, `--format`: Output format to choose from 'md', 'html', 'plain'. (default: md) -* `-g`, `--glue`: Specify delimiter (_single character only_) to join the parts. (default: -) -* `-s`, `--single-page`: Prepends hash/pound (#) to the slugified string when set, otherwise slash (/). Useful for markdowns like in GitHub (default: false) - -## Examples - -```sh -$ slugify 'Serenity is a cool ### PROject123.' -``` diff --git a/Base/usr/share/man/man1/sort.md b/Base/usr/share/man/man1/sort.md deleted file mode 100644 index cf298bb423c..00000000000 --- a/Base/usr/share/man/man1/sort.md +++ /dev/null @@ -1,32 +0,0 @@ -## Name - -sort - sort lines - -## Synopsis - -```**sh -$ sort [INPUT] -``` - -## Description - -Sort each lines of INPUT (or standard input). A quick sort algorithm is used. - -## Options - -* `-k keydef`, `--key-field keydef`: The field to sort by -* `-u`, `--unique`: Don't emit duplicate lines -* `-n`, `--numeric`: Treat the key field as a number -* `-t char`, `--sep char`: The separator to split fields by -* `-r`, `--reverse`: Sort in reverse order -* `-z`, `--zero-terminated`: Use `\0` as the line delimiter instead of a newline - -## Examples - -```sh -$ echo "Well\nHello\nFriends!" | sort -Friends! -Hello -Well -``` - diff --git a/Base/usr/share/man/man1/sql.md b/Base/usr/share/man/man1/sql.md deleted file mode 100644 index c3d046c602b..00000000000 --- a/Base/usr/share/man/man1/sql.md +++ /dev/null @@ -1,22 +0,0 @@ -## Name - -sql - -## Synopsis - -```sh -$ sql [--database database] [--read file] [--source file] [--no-sqlrc] -``` - -## Description - -This is a client for the SerenitySQL database server. - -## Options - -* `-d database`, `--database database`: Database to connect to -* `-r file`, `--read file`: File to read -* `-s file`, `--source file`: File to source -* `-n`, `--no-sqlrc`: Don't read ~/.sqlrc - -<!-- Auto-generated through ArgsParser --> diff --git a/Base/usr/share/man/man1/stat.md b/Base/usr/share/man/man1/stat.md deleted file mode 100644 index 8b12a7bfc5b..00000000000 --- a/Base/usr/share/man/man1/stat.md +++ /dev/null @@ -1,36 +0,0 @@ -## Name - -stat - Display file status - -## Synopsis - -```**sh** -$ stat [-L] file -``` - -## Description - -Display file status and provide more information about that file. - -## Options - -* `-L`: Follow links to files -* `--help`: Display help message and exit -* `--version`: Print version - -## Examples - -```sh -$ stat -L /bin/ls - File: /bin/ls - Inode: 8288 - Size: 184896 - Links: 1 - Blocks: 376 - UID: 0 (root) - GID: 0 (root) - Mode: (100755/-rwxr-xr-x) -Accessed: 2022-10-20 03:56:56 -Modified: 2022-10-20 03:55:34 - Changed: 2022-10-20 03:56:57 -``` diff --git a/Base/usr/share/man/man1/strace.md b/Base/usr/share/man/man1/strace.md deleted file mode 100644 index 7dbe9d2161e..00000000000 --- a/Base/usr/share/man/man1/strace.md +++ /dev/null @@ -1,28 +0,0 @@ -## Name - -strace - -## Synopsis - -```sh -$ strace [--pid pid] [--output output] [--exclude exclude] [--include include] [argument...] -``` - -## Description - -Trace all syscalls and their result. - -## Options - -* `-p pid`, `--pid pid`: Trace the given PID -* `-o output`, `--output output`: Filename to write output to -* `-e exclude`, `--exclude exclude`: Comma-delimited syscalls to exclude -* `-i include`, `--include include`: Comma-delimited syscalls to include - -## Arguments - -* `argument`: Arguments to exec - -## See Also -* [`profile`(1)](help://man/1/profile) -* [`Profiler`(1)](help://man/1/Applications/Profiler) diff --git a/Base/usr/share/man/man1/strings.md b/Base/usr/share/man/man1/strings.md deleted file mode 100644 index 22cce4b9e59..00000000000 --- a/Base/usr/share/man/man1/strings.md +++ /dev/null @@ -1,40 +0,0 @@ -## Name - -strings - find printable strings in files - -## Synopsis - -```**sh -$ strings [--bytes NUMBER] [--print-file-name] [-o] [--radix FORMAT] [PATHS...] -``` - -## Description - -`strings` looks for printable strings in each file specified in `PATHS` and writes them to standard output. If `PATHS` is not specified, input is read from standard input. - -## Options - -* `-n NUMBER`, `--bytes NUMBER`: Specify the minimum string length (4 is default). -* `-f`, `--print-file-name`: Print the name of the file before each string. -* `-o`: Equivalent to specifying `-t o`. -* `-t FORMAT`, `--radix FORMAT`: Write each string preceded by its byte offset from the start of the file in the specified `FORMAT`, where `FORMAT` matches one of the following: `d` (decimal), `o` (octal), or `x` (hexidecimal). - -## Examples - -Display the printable strings in /bin/strings with a minimum length of 8 characters: - -```sh -$ strings -n 8 /bin/strings -``` - -Display the printable strings in a binary file, preceded by their byte offset in hexadecimal format: - -```sh -$ strings -t x ~/Videos/test.webm -``` - -Display the printable strings in all .txt files in the current directory, preceded by their pathname: - -```sh -$ strings -f *.txt -``` diff --git a/Base/usr/share/man/man1/su.md b/Base/usr/share/man/man1/su.md deleted file mode 100644 index f9eab53327f..00000000000 --- a/Base/usr/share/man/man1/su.md +++ /dev/null @@ -1,42 +0,0 @@ -## Name - -su - switch to another user - -## Synopsis - -```sh -$ su [-] [user] -$ su [-] [user] [-c command] -``` - -## Description - -`su`: Switch to another user. - -When called with no user-specified, `su` defaults to switch to the *root* user. Need to enter the password if the user switch to has one. - -## Options - -* `-`, `-l`, `--login`: Start the shell as it was a real login -* `-c`, `--command`: Execute a command using `/bin/sh` instead of starting an interactive shell - -## Arguments - -* `user`: User to switch to (defaults to the user with UID 0) - -## Examples - -Switch to root user - -```sh -$ su -``` - -Switch to another user - -```sh -$ su nona -``` - -## See also -* [`pls`(8)](help://man/8/pls) diff --git a/Base/usr/share/man/man1/syscall.md b/Base/usr/share/man/man1/syscall.md deleted file mode 100644 index d3bc9e56700..00000000000 --- a/Base/usr/share/man/man1/syscall.md +++ /dev/null @@ -1,62 +0,0 @@ -## Name - -syscall - test a system call - -## Synopsis - -```**sh -$ syscall [-o] [-l] [-h] <syscall-name> <args...> [buf==BUFSIZ buffer] -``` - -## Description - -The `syscall` utility can be used to invoke a system call with the given arguments. - -## Options - -* `-o`: Output the contents of the buffer argument specified as buf to stdout. -* `-l`: Print a space separated list of all the Serenity system calls and exit. Note that not all the system calls can be invoked using this tool. -* `-h`: Print a help message and exit. - -## Examples - -Write a string to standard output: - -```sh -$ syscall write 1 hello 5 -``` - -Read a string from the standard input into a buffer and output the buffer contents to stdout: - -```sh -$ syscall -o read 0 buf 3 -``` - -Get the pid of the current running process: - -```sh -$ syscall getpid -``` - -Sleep for 3 seconds: - -```sh -$ syscall sleep 3 -``` - -Create a directory: - -```sh -$ syscall mkdir my-dir 0755 -``` - -Exit the program with status 2: - -```sh -$ syscall exit 2 -``` - -## History - -This is a direct port of a utility with the same name originated from the Plan 9 operating system. - diff --git a/Base/usr/share/man/man1/tail.md b/Base/usr/share/man/man1/tail.md deleted file mode 100644 index c16fa842fb9..00000000000 --- a/Base/usr/share/man/man1/tail.md +++ /dev/null @@ -1,55 +0,0 @@ -## Name - -tail - Print the end of a file - -## Synopsis - -```**sh -$ tail [-f] [-n number] [file] -``` - -## Description - -`tail` prints the specified `number` (10 by default) of lines at the end of `file`. - -## Options - -* `-f`, `--follow`: Output data as it is written to the file -* `-n [+]NUM`, `--lines [+]NUM`: output the last NUM lines, instead of the last 10; or use -n +NUM to output starting with line NUM -* `-c [+]NUM`, `--bytes [+]NUM`: output the last NUM bytes; or use -n +NUM to output starting with byte NUM - -## Arguments - -* `file`: Target file. If unspecified or `-`, defaults to the standard input. - -## Examples - -Print the last 10 lines of README.md: -```sh -$ tail README.md -``` - -Print the last 42 lines of todo.txt: -```sh -$ tail -n 42 todo.txt -``` - -Print the last lines as they are written to logs.log: -```sh -$ tail -f logs.log -``` - -Print everything but the first line of foobar.csv -```sh -$ tail -n +2 foobar.csv -``` - -Print the last 1337 bytes of leet.txt -```sh -$ tail -c 1337 leet.txt -``` - -## See also - -* [`head`(1)](help://man/1/head) -* [`cat`(1)](help://man/1/cat) diff --git a/Base/usr/share/man/man1/tar.md b/Base/usr/share/man/man1/tar.md deleted file mode 100644 index f1c1008ffe2..00000000000 --- a/Base/usr/share/man/man1/tar.md +++ /dev/null @@ -1,46 +0,0 @@ -## Name - -tar - file archiving utility - -## Synopsis - -```**sh -$ tar [--create] [--extract] [--list] [--verbose] [--gzip] [--no-auto-compress] [--directory DIRECTORY] [--file FILE] [PATHS...] -``` - -## Description - -tar is an archiving utility designed to store multiple files in an archive file -(tarball). - -Files may also be compressed and decompressed using GNU Zip (GZIP) compression. - -## Options - -* `-c`, `--create`: Create archive -* `-x`, `--extract`: Extract archive -* `-t`, `--list`: List contents -* `-v`, `--verbose`: Print paths -* `-z`, `--gzip`: Compress or decompress file using gzip -* `--lzma`: Compress or decompress file using lzma -* `-J`, `--xz`: Compress or decompress file using xz -* `--no-auto-compress`: Do not use the archive suffix to select the compression algorithm -* `-C DIRECTORY`, `--directory DIRECTORY`: Directory to extract to/create from -* `-f FILE`, `--file FILE`: Archive file - -## Examples - -```sh -# List the contents of archive.tar -$ tar -t -f archive.tar - -# Extract the contents from archive.tar.gz -$ tar -x -z -f archive.tar.gz - -# Extract the contents from archive.tar -$ tar -x -f archive.tar -``` - -## See also - -* [`unzip`(1)](help://man/1/unzip) diff --git a/Base/usr/share/man/man1/test-js.md b/Base/usr/share/man/man1/test-js.md deleted file mode 100644 index 4639eb9d36c..00000000000 --- a/Base/usr/share/man/man1/test-js.md +++ /dev/null @@ -1,52 +0,0 @@ -## Name - -test-js - run the LibJS test suite - -## Synopsis - -```**sh -$ test-js [options...] [path] -``` - -## Description - -`test-js` runs the LibJS test suite located in `/home/anon/Test/js-tests`. These -tests are using a custom JavaScript testing framework inspired by -[Jest](https://jestjs.io) (see [`test-common.js`](/home/anon/Tests/js-tests/test-common.js)). - -It also supports the [test262 parser tests](https://github.com/tc39/test262-parser-tests). - -The test root directory is assumed to be `/home/anon/Tests/js-tests`, or `$SERENITY_SOURCE_DIR/Userland/Libraries/LibJS/Tests` -when using the Lagom build. Optionally you can pass a custom path to `test-js` to override these defaults. - -You can disable output from `dbgln()` calls by setting the `DISABLE_DBG_OUTPUT` environment variable. - -## Options - -* `-t`, `--show-time`: Show duration of each test -* `-p`, `--show-progress`: Show progress with OSC 9 (true, false) -* `-j`, `--json`: Show results as JSON -* `--per-file`: Show detailed per-file results as JSON (implies -j) -* `-g`, `--collect-often`: Collect garbage after every allocation -* `-b`, `--run-bytecode`: Use the bytecode interpreter -* `-d`, `--dump-bytecode`: Dump the bytecode -* `-f glob`, `--filter glob`: Only run tests matching the given glob -* `--test262-parser-tests`: Run test262 parser tests - -## Examples - -A very simple test looks like this: - -```js -describe("Examples from Gary Bernhardt's 'Wat' talk", () => { - test("Na na na na na na na na na na na na na na na na Batman!", () => { - expect(Array(16).join("wat" - 1) + " Batman!").toBe( - "NaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaN Batman!" - ); - }); -}); -``` - -## See also - -* [`js`(1)](help://man/1/js) diff --git a/Base/usr/share/man/man1/test.md b/Base/usr/share/man/man1/test.md deleted file mode 100644 index 50402a96cb7..00000000000 --- a/Base/usr/share/man/man1/test.md +++ /dev/null @@ -1,101 +0,0 @@ -## Name - -test - check files and compare values - -## Synopsis - -```**sh -$ test expression -$ test -$ [ expression ] -$ [ ] -``` - -## Description - -`test` takes a given expression and sets the exit code according to its truthiness, 0 if true, 1 if false. -An omitted expression defaults to false, and an unexpected error causes an exit code of 126. - -If `test` is invoked as `[`, a trailing `]` is _required_ after the expression. - -## Expressions - -The expression can take any of the following forms: - -### Grouping - -* `( <expression> )` value of expression - -### Boolean operations - -* `! <expression>` negation of expression -* `<expression> -a <expression>` boolean conjunction of the values -* `<expression> -o <expression>` boolean disjunction of the values - -### String comparison - -* `<string>` whether the string is non-empty -* `-n <string>` whether the string is non-empty -* `-z <string>` whether the string is empty -* `<string> = <string>` whether the two strings are equal -* `<string> != <string>` whether the two strings not equal - -### Integer comparison - -* `<integer> -eq <integer>` whether the two integers are equal -* `<integer> -ne <integer>` whether the two integers are not equal -* `<integer> -lt <integer>` whether the integer on the left is less than the integer on the right -* `<integer> -gt <integer>` whether the integer on the left is greater than the integer on the right -* `<integer> -le <integer>` whether the integer on the left is less than or equal to the integer on the right -* `<integer> -ge <integer>` whether the integer on the left is greater than or equal to the integer on the right - -### File comparison - -* `<file> -ef <file>` whether the two files are the same (have the same inode and device numbers) -* `<file> -nt <file>` whether the file on the left is newer than the file on the right (modification date is used) -* `<file> -ot <file>` whether the file on the left is older than the file on the right (modification date is used) - -### File type checks - -* `-b <file>` whether the file is a block device -* `-c <file>` whether the file is a character device -* `-f <file>` whether the file is a regular file -* `-d <file>` whether the file is a directory -* `-p <file>` whether the file is a pipe -* `-S <file>` whether the file is a socket -* `-h <file>`, `-L <file>` whether the file is a symbolic link - -### File permission checks - -* `-r <file>` whether the current user has read access to the file -* `-w <file>` whether the current user has write access to the file -* `-x <file>` whether the current user has execute access to the file -* `-e <file>` whether the file exists -* `-g <file>` whether the file exists and has the set-group-ID bit set -* `-G <file>` whether the file exists and is owned by the effective group ID -* `-k <file>` whether the file exists and has the sticky bit set -* `-O <file>` whether the file exists and is owned by the effective user ID -* `-u <file>` whether the file exists and has the set-user-ID bit set - - -Except for `-h/-L`, all file checks dereference symbolic links. - -NOTE: Your shell might have a builtin named 'test' and/or '[', please refer to your shell's documentation for further details. - - -## Options - -None. - -## Examples - -```sh -# Conditionally do something based on the value of a variable -$ /bin/test "$foo" = bar && echo foo is bar -# Check some numbers -$ /bin/test \( 10 -gt 20 \) -o \( ! 10 -ne 10 \) && echo "magic numbers!" -``` - -## See Also -* [`expr`(1)](help://man/1/expr) -* [`find`(1)](help://man/1/find) diff --git a/Base/usr/share/man/man1/timezone.md b/Base/usr/share/man/man1/timezone.md deleted file mode 100644 index 9ad45046b39..00000000000 --- a/Base/usr/share/man/man1/timezone.md +++ /dev/null @@ -1,47 +0,0 @@ -## Name - -timezone - View or set the system time zone - -## Synopsis - -```**sh -$ timezone [--list-time-zones] [time-zone] -``` - -## Description - -The `timezone` utility can be used to view or change the current system time zone, or view all time zones available on the system. - -## Options - -* `-l`, `--list-time-zones`: View all available time zones and exit. - -## Arguments - -* `time-zone`: Time zone to change the system to use. - -## Examples - -Get the current system time zone: -```sh -$ timezone -UTC -``` - -Set the system time zone: -```sh -$ timezone America/New_York -``` - -View all available time zones: -```sh -$ timezone --list-time-zones -Africa/Abidjan -Africa/Algiers -Africa/Bissau -... -Pacific/Wake -Pacific/Wallis -PST8PDT -WET -``` diff --git a/Base/usr/share/man/man1/top.md b/Base/usr/share/man/man1/top.md deleted file mode 100644 index f5076d0ad58..00000000000 --- a/Base/usr/share/man/man1/top.md +++ /dev/null @@ -1,15 +0,0 @@ -## Name - -top - display information about processes - -## Synopsis - -```sh -$ top [--delay-time secs] [--pids pid-list] [--sort-by field] -``` - -## Options - -* `-d`, `--delay-time`: Delay time interval in seconds -* `-p`, `--pids`: A comma-separated list of pids to filter by. This option may be used multiple times. -* `-s`, `--sort-by`: Sort by field [pid, tid, pri, user, state, virt, phys, cpu, name] diff --git a/Base/usr/share/man/man1/touch.md b/Base/usr/share/man/man1/touch.md deleted file mode 100644 index 864b1296a69..00000000000 --- a/Base/usr/share/man/man1/touch.md +++ /dev/null @@ -1,56 +0,0 @@ -## Name - -touch - create a file or change its timestamps - -## Synopsis - -```**sh -$ touch [-acm] [-r ref_file|-t time|-d date_time] <path...> -``` - -## Description - -`touch` updates the last access and last modification times of all files -specified in `path` to the current time. - -Unless `-c` is specified, `touch` creates a regular empty file for a `path` -that does not exist. - -## Options - -* `-a`: Change access time of file -* `-c`: Do not create a file if it does not exist -* `-m`: Change modification time of file -* `-r`: Use time of file specified by reference path instead of current time -* `-t`: Use specified time in format [[CC]YY]MMDDhhmm[.SS] instead of current -time -* `-d`: Use specified datetime in formats YYYY-MM-DDThh:mm:SS[.frac][tz] or -YYYY-MM-DDThh:mm:SS[,frac][tz] instead of current time - -## Examples - -```sh -# Create or update a file named 'file' with its last access and last -# modification attributes set to the current time: -$ touch file - -# Update a file called 'somefile' with last access and last modification -# timestamps set to 14:49:30 on May 13, 2009: -$ touch -c -d '2009-05-13 14:49:30' somefile - -# Create or update a file called 'anotherfile', where the resulting file has -# both last modification and last access timestamps set to April 4, 1971 at -# 09:17:00 local time: -$ touch -t 197104180917 anotherfile - -# Create or update a file called 'thatfile'. It's last access time is set to -# the last access time of the file named 'anotherfile' instead of the current -# time, and the last modification time remains unchanged as long as the file -# exists: -$ touch -r anotherfile thatfile -``` - -## See also -* [`mktemp`(1)](help://man/1/mktemp) to create a temporary file -* [`futimens`(2)](help://man/3/futimens) -* [`utimensat`(2)](help://man/3/utimensat) diff --git a/Base/usr/share/man/man1/tr.md b/Base/usr/share/man/man1/tr.md deleted file mode 100644 index 6d030f06558..00000000000 --- a/Base/usr/share/man/man1/tr.md +++ /dev/null @@ -1,22 +0,0 @@ -## Name - -tr - -## Synopsis - -```sh -$ tr [--complement] [--delete] [--squeeze-repeats] <from> [to] -``` - -## Options - -* `-c`, `--complement`: Take the complement of the first set -* `-d`, `--delete`: Delete characters instead of replacing -* `-s`, `--squeeze-repeats`: Omit repeated characters listed in the last given set from the output - -## Arguments - -* `from`: Set of characters to translate from -* `to`: Set of characters to translate to - -<!-- Auto-generated through ArgsParser --> diff --git a/Base/usr/share/man/man1/traceroute.md b/Base/usr/share/man/man1/traceroute.md deleted file mode 100644 index bf79588eb12..00000000000 --- a/Base/usr/share/man/man1/traceroute.md +++ /dev/null @@ -1,21 +0,0 @@ -## Name - -traceroute - -## Synopsis - -```sh -$ traceroute [--max-hops hops] [--max-retries tries] [--timeout seconds] <destination> -``` - -## Options - -* `-h hops`, `--max-hops hops`: use at most <hops> to the destination -* `-r tries`, `--max-retries tries`: retry TTL at most <tries> times -* `-t seconds`, `--timeout seconds`: wait at most <seconds> for a response - -## Arguments - -* `destination`: destination - -<!-- Auto-generated through ArgsParser --> diff --git a/Base/usr/share/man/man1/tree.md b/Base/usr/share/man/man1/tree.md deleted file mode 100644 index 14c3599f060..00000000000 --- a/Base/usr/share/man/man1/tree.md +++ /dev/null @@ -1,22 +0,0 @@ -## Name - -tree - -## Synopsis - -```sh -$ tree [--all] [--only-directories] [--maximum-depth level] [directories...] -``` - -## Options - -* `-a`, `--all`: Show hidden files -* `-d`, `--only-directories`: Show only directories -* `-L level`, `--maximum-depth level`: Maximum depth of the tree - -## Arguments - -* `directories`: Directories to print - -## See also -* [`ls`(1)](help://man/1/ls) to show just the contents of one directory diff --git a/Base/usr/share/man/man1/truncate.md b/Base/usr/share/man/man1/truncate.md deleted file mode 100644 index 158555d22a0..00000000000 --- a/Base/usr/share/man/man1/truncate.md +++ /dev/null @@ -1,20 +0,0 @@ -## Name - -truncate - -## Synopsis - -```sh -$ truncate [--size size] [--reference file] <file> -``` - -## Options - -* `-s size`, `--size size`: Resize the target file to (or by) this size. Prefix with + or - to expand or shrink the file, or a bare number to set the size exactly -* `-r file`, `--reference file`: Resize the target file to match the size of this one - -## Arguments - -* `file`: File path - -<!-- Auto-generated through ArgsParser --> diff --git a/Base/usr/share/man/man1/uname.md b/Base/usr/share/man/man1/uname.md deleted file mode 100644 index c824d676158..00000000000 --- a/Base/usr/share/man/man1/uname.md +++ /dev/null @@ -1,34 +0,0 @@ -## Name - -uname - print kernel information - -## Synopsis - -```**sh -$ uname [-s] [-n] [-r] [-m] [-a]` -``` - -## Description - -Print information about the operating system, as reported by the `uname()` -system call. - -## Options - -* `-s`: Print the system name -* `-n`: Print the node name (hostname) -* `-r`: Print the system release version -* `-v`: Print the version of the release -* `-m`: Print the machine type -* `-a`: Print all of the above - -## Examples - -```sh -$ uname -sm -Serenity x86_64 -``` - -## See also - -* [`uname`(2)](help://man/2/uname) diff --git a/Base/usr/share/man/man1/uniq.md b/Base/usr/share/man/man1/uniq.md deleted file mode 100644 index 401f8dc39d4..00000000000 --- a/Base/usr/share/man/man1/uniq.md +++ /dev/null @@ -1,50 +0,0 @@ -## Name - -uniq - filter out repeated adjacent lines - -## Synopsis - -```**sh -$ uniq [-c] [-d|-u] [-f skip-fields] [-s skip-chars] [--version] [INPUT] [OUTPUT] -``` - -## Description - -Filter out repeated adjacent lines from INPUT (or standard input) and write to OUTPUT (or standard output). It is recommended to sort out the input using [`sort(1)`](help://man/1/sort) beforehand. - -## Options - -* `-c`, `--count`: Precede each line with its number of occurrences. -* `-d`, `--repeated`: Only print repeated lines. -* `-u`, `--unique`: Only print unique lines (default). -* `-i`, `--ignore-case`: Ignore case when comparing lines. -* `-f N`, `--skip-fields N`: Skip first N fields of each line before comparing. -* `-s N`, `--skip-chars N`: Skip first N chars of each line before comparing. -* `--help`: Display help message and exit. -* `--version`: Print version. - -## Examples - -Filter out repeated lines from README.md and write to standard output: -```sh -$ uniq README.md -``` - -Filter out repeated lines from README.md and write to UNIQUE.md: -```sh -$ uniq README.md UNIQUE.md -``` - -Filter out repeated lines from standard input with their occurrence count: -```sh -$ echo "Well\nWell\nWell\nHello Friends!" | uniq -c -3 Well -1 Hello Friends! -``` - -Filter out repeated lines, ignoring the first field ("XXXX" and "ZZZZ") and the four chars after it (" ABC" and " BCA", thus comparing only the "D"s — which are equal indeed): -```sh -$ echo "XXXX ABCD\nZZZZ BCAD" | uniq -f1 -s4 -ZZZZ BCAD -``` - diff --git a/Base/usr/share/man/man1/unveil.md b/Base/usr/share/man/man1/unveil.md deleted file mode 100644 index 183dac118e2..00000000000 --- a/Base/usr/share/man/man1/unveil.md +++ /dev/null @@ -1,34 +0,0 @@ -## Name - -unveil - unveil certain paths when running a command - -## Synopsis - -```**sh -$ unveil [--path] [command...] -``` - -## Description - -Run a command under certain path restrictions by using [`unveil`(2)](help://man/2/unveil). - -## Options - -* `-u`, `--path`: Unveil a path, with the format of `permissions,path` - -## Examples - -Run `ls -la /sys/kernel` with restricted access to certain paths: -```sh -$ unveil --path=r,/etc/timezone --path=r,/usr/lib --path=r,/sys/ --path=r,/etc/passwd --path=r,/etc/group ls -la /sys/kernel -``` - -Run `ps -ef` with restricted access to certain paths: -```sh -$ unveil --path=r,/etc/timezone --path=r,/usr/lib --path=r,/sys/ --path=r,/etc/passwd --path=r,/etc/group ps -ef -``` - -## See also -* [`pledge`(2)](help://man/2/pledge) -* [`unveil`(2)](help://man/2/unveil) -* [`Mitigations`(7)](help://man/7/Mitigations) diff --git a/Base/usr/share/man/man1/unzip.md b/Base/usr/share/man/man1/unzip.md deleted file mode 100644 index c15880e9d92..00000000000 --- a/Base/usr/share/man/man1/unzip.md +++ /dev/null @@ -1,43 +0,0 @@ -## Name - -unzip - extract files from a ZIP archive - -## Synopsis - -```**sh -$ unzip file.zip [files...] -``` - -## Description - -unzip will extract files from a zip archive to the current directory. - -The program is compatible with the PKZIP file format specification. - -The optional [files] argument can be used to only extract specific files within the archive (using wildcards) during the unzip process. A `_` can be used as a single-character wildcard, and `*` can be used as a variable-length wildcard. - -## Options - -* `-d path`, `--output-directory path`: Directory to receive the archive output -* `-q`, `--quiet`: Be less verbose - -## Examples - -```sh -# Unzip the contents from archive.zip -$ unzip archive.zip -Archive: archive.zip - extracting: file1.txt - extracting: file2.png -``` - -```sh -# Unzip select files from archive.zip, according to a filter -$ unzip archive.zip "*.tx_" -Archive: archive.unzip - extracting: file1.txt -``` - -## See also -* [`zip`(1)](help://man/1/zip) -* [`tar`(1)](help://man/1/tar) diff --git a/Base/usr/share/man/man1/uptime.md b/Base/usr/share/man/man1/uptime.md deleted file mode 100644 index 9c038e04ca2..00000000000 --- a/Base/usr/share/man/man1/uptime.md +++ /dev/null @@ -1,36 +0,0 @@ -## Name - -uptime - Tell how long the system has been running - -## Synopsis - -```sh -$ uptime -``` - -## Description - -`uptime` outputs information about the system, in a single line, to STDOUT. -This information includes when the system came online and how long it has been up. - -## Options - -* `-p`, `--pretty`: Output only the uptime, in human-readable format. -* `-s`, `--since`: Output only the datetime when the system came online. - -## Examples - -```sh -$ uptime -2024-01-24 06:23:27 up 4:20:00 -``` - -```sh -$ uptime -p -Up 2 minutes, 20 seconds -``` - -```sh -$ uptime -s -2024-01-24 06:23:27 -``` diff --git a/Base/usr/share/man/man1/utmpupdate.md b/Base/usr/share/man/man1/utmpupdate.md deleted file mode 100644 index 9c64fc0f5bc..00000000000 --- a/Base/usr/share/man/man1/utmpupdate.md +++ /dev/null @@ -1,23 +0,0 @@ -## Name - -utmpupdate - -## Synopsis - -```sh -$ utmpupdate [--create] [--delete] [--PID PID] [--from From] <tty> -``` - -## Options - -* `-c`, `--create`: Create entry -* `-d`, `--delete`: Delete entry -* `-p PID`, `--PID PID`: PID -* `-f From`, `--from From`: From - -## Arguments - -* `tty`: TTY name - -## See also -* [`w`(1)](help://man/1/w) diff --git a/Base/usr/share/man/man1/w.md b/Base/usr/share/man/man1/w.md deleted file mode 100644 index d863cd74686..00000000000 --- a/Base/usr/share/man/man1/w.md +++ /dev/null @@ -1,22 +0,0 @@ -## Name - -w - Show information about currently logged-in users - -## Synopsis - -```sh -$ w [--no-header] [user] -``` - -## Options - -* `-h`, `--no-header`: Don't show the header - -## Arguments - -* `user`: Only show information about the specified user - -## See also -* [`whoami`(1)](help://man/1/whoami) -* [`utmpupdate`(1)](help://man/1/utmpupdate) -* [`usermod`(8)](help://man/8/usermod) diff --git a/Base/usr/share/man/man1/wallpaper.md b/Base/usr/share/man/man1/wallpaper.md deleted file mode 100644 index 0bcdbb2b4e4..00000000000 --- a/Base/usr/share/man/man1/wallpaper.md +++ /dev/null @@ -1,34 +0,0 @@ -## Name - -wallpaper - manage the system wallpaper. - -## Synopsis - -```**sh -$ wallpaper [--show-all] [--show-current] [name] -``` - -## Description - -The `wallpaper` utility can be used to set the system wallpaper and -list available wallpapers in the `/res/wallpapers/` directory. - -## Options - -* `-a`, `--show-all`: Show all wallpapers -* `-c`, `--show-current`: Show current wallpaper -* `-r`, `--set-random`: Set random wallpaper - -## Examples - -Set wallpaper to `/res/wallpapers/grid.png`: - -```**sh -$ wallpaper grid.png -``` - -List available wallpapers: - -```**sh -$ wallpaper -a -``` diff --git a/Base/usr/share/man/man1/watch.md b/Base/usr/share/man/man1/watch.md deleted file mode 100644 index c0e8a4aa2f2..00000000000 --- a/Base/usr/share/man/man1/watch.md +++ /dev/null @@ -1,33 +0,0 @@ -## Name - -watch - execute a program periodically - -## Synopsis - -```**sh -$ watch [-n interval] [-t] [-b] command -``` - -## Description - -Run a command full-screen periodically until interrupted, then return the -aggregated error code. - -## Options - -* `-n seconds`: Interval between executions, in seconds. By default, the program is run every 2 seconds. -* `-t`, `--no-title`: Don't print the title bar. -* `-b`, `--beep`: Beep each time the command exits with a non-zero status. -* `-f file`, `--file file`: Run command whenever this file changes. Can be used multiple times. - -## Exit Values - -* 0 - Success -* 1 - At least one invocation of the command failed or exited with a non-zero status - -## Examples - -```sh -$ watch -n1 ls -$ watch -t -- uname -a -``` diff --git a/Base/usr/share/man/man1/wc.md b/Base/usr/share/man/man1/wc.md deleted file mode 100644 index 058a8a1a4e9..00000000000 --- a/Base/usr/share/man/man1/wc.md +++ /dev/null @@ -1,24 +0,0 @@ -## Name - -wc - word, line, character, and byte count - -## Synopsis - -```sh -$ wc [--lines] [--bytes] [--words] [--max-line-length] [file...] -``` - -## Options - -* `--help`: Display help message and exit -* `--version`: Print version -* `-l`, `--lines`: Output line count -* `-c`, `--bytes`: Output byte count -* `-w`, `--words`: Output word count -* `-L`, `--max-line-length`: Output byte count of the longest line - -## Arguments - -* `file`: File to process - -<!-- Auto-generated through ArgsParser --> diff --git a/Base/usr/share/man/man1/which.md b/Base/usr/share/man/man1/which.md deleted file mode 100644 index 43792b7a489..00000000000 --- a/Base/usr/share/man/man1/which.md +++ /dev/null @@ -1,25 +0,0 @@ -## Name - -which - show the full path of commands - -## Synopsis - -```**sh** -$ which [options] executable -``` - -## Description - -`which` provides the full path location of where an executable is installed. - -## Options - -* `--help`: Display help message and exit -* `--version`: Print version - -## Examples - -```sh -$ which ls -/bin/ls -``` diff --git a/Base/usr/share/man/man1/whoami.md b/Base/usr/share/man/man1/whoami.md deleted file mode 100644 index b423d45abfb..00000000000 --- a/Base/usr/share/man/man1/whoami.md +++ /dev/null @@ -1,26 +0,0 @@ -## Name - -whoami - print effective user name - -## Synopsis - -```**sh -$ whoami -``` - -## Description - -`whoami` outputs the user name associated with current effective UID. - -## Examples - -```sh -$ whoami -anon -# whoami -root -``` - -## See also - -* [`id`(1)](help://man/1/id) diff --git a/Base/usr/share/man/man1/xargs.md b/Base/usr/share/man/man1/xargs.md deleted file mode 100644 index e8a39f7a501..00000000000 --- a/Base/usr/share/man/man1/xargs.md +++ /dev/null @@ -1,51 +0,0 @@ -## Name - -xargs - build and execute commandlines from input - -## Synopsis - -```**sh -$ xargs [options...] [command [initial-arguments...]] -``` - -## Description - -`xargs` reads items from a stream, delimited by some blank character (`delimiter`), and executes the `command` as many times as there are items, with any processed `initial-arguments`, possibly followed by a number of items read from the input. - -If a `placeholder` is explicitly specified, the `max-lines` limit is set to 1, and each argument in `initial-arguments` is processed by replacing any occurrence of the `placeholder` with the input item, and treating the entire resulting value as _one_ argument. - -It is to be noted that `command` is also subject to substitution in this mode. - -If no argument in `command` or `initial-arguments` contains the `placeholder`, an argument is added at the end of the list containing only the `placeholder`. - - -If a `placeholder` is not explicitly specified, no substitution will be performed, rather, the item(s) will be appended to the end of the command line, until either of the following conditions are met: -- Adding another argument would overflow the system maximum command length (or the provided `max-chars` limit) -- The number of lines used per command (`max-lines`) would be exceeded - - -`xargs` will read the items from standard input by default, and when data is read from standard input, the standard input of `command` is redirected to read from `/dev/null`. -The standard input is left as-is if data is read from a file. - -## Options - -* `-I`, `--replace`: Set the `placeholder`, and force `max-lines` to 1 -* `-0`, `--null`: Split the items by zero bytes (null characters) instead of `delimiter` -* `-d`, `--delimiter`: Set the `delimiter`, which is a newline (`\n`) by default -* `-v`, `--verbose`: Display each expanded command on standard error before executing it -* `-a`, `--arg-file`: Read the items from the specified file, `-` refers to standard input and is the default -* `-L`, `--line-limit`: Set `max-lines`, `0` means unlimited (which is the default) -* `-s`, `--char-limit`: Set `max-chars`, which is `ARG_MAX` (the maximum command size supported by the system) by default - -## Examples - -```sh -$ pro http://list-of-example-urls.com/plain | xargs -I 'URL' pro URL > concatenated-outputs -$ xargs -a list-of-files-to-delete --verbose rm -$ xargs -a list-of-moves -L 2 mv -$ xargs -a stuff --null -s 1024 -``` - -## See also - -* [`find`(1)](help://man/1/find) diff --git a/Base/usr/share/man/man1/yes.md b/Base/usr/share/man/man1/yes.md deleted file mode 100644 index 8d16ec4baba..00000000000 --- a/Base/usr/share/man/man1/yes.md +++ /dev/null @@ -1,44 +0,0 @@ -## Name - -yes - output a string repeatedly until killed - -## Synopsis - -```**sh -$ yes [string] -``` - -## Description - -`yes` outputs an endless stream of specified `string` (with trailing newline) in a loop. - -## Options - -* `string`: String to print; defaults to "yes". - -## Examples - -```sh -$ yes -yes -yes -yes -yes -yes -yes -yes^C -$ yes t -t -t -t -t -t -t -t -t -t -t^C -``` - -## See also -* [`cat`(1)](help://man/1/cat) diff --git a/Base/usr/share/man/man1/zcat.md b/Base/usr/share/man/man1/zcat.md deleted file mode 120000 index 548787f228a..00000000000 --- a/Base/usr/share/man/man1/zcat.md +++ /dev/null @@ -1 +0,0 @@ -gzip.md \ No newline at end of file diff --git a/Base/usr/share/man/man1/zip.md b/Base/usr/share/man/man1/zip.md deleted file mode 100644 index 410d0d0bd08..00000000000 --- a/Base/usr/share/man/man1/zip.md +++ /dev/null @@ -1,35 +0,0 @@ -## Name - -zip - create ZIP archives - -## Synopsis - -```**sh -$ zip [--recurse-paths] [zip file] [files...] -``` - -## Description - -zip will pack the specified files into a zip archive, compressing them when possible. - -The program is compatible with the PKZIP file format specification. - -## Options - -* `-r`, `--recurse-paths`: Travel the directory structure recursively -* `-f`, `--force`: Overwrite existing zip file - -## Examples - -```sh -# Zip -$ zip archive.zip file1.txt file2.png -Archive: archive.zip - adding: file1.txt - adding: file2.png -``` - -## See also -* [`unzip`(1)](help://man/1/unzip) -* [`gzip`(1)](help://man/1/gzip) -* [`tar`(1)](help://man/1/tar) diff --git a/Base/usr/share/man/man2/accept.md b/Base/usr/share/man/man2/accept.md deleted file mode 100644 index acc5e3c3d86..00000000000 --- a/Base/usr/share/man/man2/accept.md +++ /dev/null @@ -1,26 +0,0 @@ -## Name - -accept - accept a new connection on a server socket - -## Synopsis - -```**c++ -#include <sys/socket.h> - -int accept(int sockfd, sockaddr* addr, socklen_t* addrlen); -``` - -## Description - -Accept a new connection from a client for the server specified by `sockfd`. This function will block until there is at least one client trying to connect to the server, with other clients being queued up for accepting. - -When `accept(2)` is successful, a new socket with a unique file descriptor is created and that file descriptor returned. If not null, the `addr` argument will contain the address of the newly-connected client. If not null, the `addrlen` argument will contain the maximum length of the client address that should be written, and it will in turn be overwritten with the actual length of the client address written back to `addr`. - -## Return value - -If the return value is positive, it represents the file descriptor of the new socket connected to the client that was accepted. If the return value is -1, the error can be found in `errno`, where the most important errors are: -- `EBADFD`: The file descriptor `sockfd` is invalid. -- `ENOTSOCK`: The given file descriptor `sockfd` is valid, but does not point to a socket. -- `EMFILE`: No more file descriptors are available for the new socket. -- `EAGAIN`: The socket was specified to be non-blocking, and there is no client in the queue. The user should try to `accept(2)` again at a later point. -- `EINTR`: A signal interrupted the blocking. diff --git a/Base/usr/share/man/man2/access.md b/Base/usr/share/man/man2/access.md deleted file mode 100644 index 13d7b77027b..00000000000 --- a/Base/usr/share/man/man2/access.md +++ /dev/null @@ -1,24 +0,0 @@ -## Name - -access - check if a file is accessible - -## Synopsis - -```**c++ -#include <unistd.h> - -int access(const char* path, int mode); -``` - -## Description - -Check if a file at the given *path* exists and is accessible to the current user for the given *mode*. -Valid flags for *mode* are: -* `F_OK` to check if the file is accessible at all, -* `R_OK` to check if the file can be read, -* `W_OK` to check if the file can be written to, -* `X_OK` to check if the file can be executed as a program. - -## Return value - -If the file is indeed accessible for the specified *mode*, `access()` returns 0. Otherwise, it returns -1 and sets `errno` to describe the error. diff --git a/Base/usr/share/man/man2/adjtime.md b/Base/usr/share/man/man2/adjtime.md deleted file mode 100644 index 29b315426a8..00000000000 --- a/Base/usr/share/man/man2/adjtime.md +++ /dev/null @@ -1,33 +0,0 @@ -## Name - -adjtime - gradually adjust system clock - -## Synopsis - -```**c++ -#include <sys/time.h> - -int adjtime(const struct timeval* delta, struct timeval* old_delta); -``` - -## Description - -`adjtime()` gradually increments the system time by `delta`, if it is non-null. - -Serenity OS slows down or speeds up the system clock by at most 1%, so adjusting the time by N seconds takes 100 * n seconds to complete. - -Calling `settimeofday()` or `clock_settime()` cancels in-progress time adjustments done by `adjtime`. - -If `delta` is not null, `adjtime` can only called by the superuser. - -If `old_delta` is not null, it returns the currently remaining time adjustment. Querying the remaining time adjustment does not need special permissions. - -## Pledge - -In pledged programs, the `settime` promise is required when `delta` is not null. - -## Errors - -* `EFAULT`: `delta` and/or `old_delta` are not null and not in readable memory. -* `EINVAL`: `delta` is not null and has a `tv_nsec` field that's less than 0 or larger or equal to `10^6`. Negative deltas should have a negative `tv_sec` field but a `tv_nsec` that's larger or equal zero. For example, a delta of -0.5 s is represented by `{-1, 500'000}`. -* `EPERM`: `delta` is not null but geteuid() is not 0. diff --git a/Base/usr/share/man/man2/bindmount.md b/Base/usr/share/man/man2/bindmount.md deleted file mode 100644 index 17e9aa1a4f3..00000000000 --- a/Base/usr/share/man/man2/bindmount.md +++ /dev/null @@ -1,39 +0,0 @@ -## Name - -bindmount - create a bindmount from `source_fd` to a target path. - -## Synopsis - -```**c++ -#include <LibCore/System.h> - -ErrorOr<void> bindmount(int source_fd, StringView target, int flags); -``` - -## Description - -`bindmount()` create a bindmount from `source_fd` to a target path `target`, with mount flags of `flags`. - -The following `flags` are supported: - -* `MS_NODEV`: Disallow opening any devices from this file system. -* `MS_NOEXEC`: Disallow executing any executables from this file system. -* `MS_NOSUID`: Ignore set-user-id bits on executables from this file system. -* `MS_RDONLY`: Mount the filesystem read-only. -* `MS_WXALLOWED`: Allow W^X protection circumvention for executables on this file system. -* `MS_AXALLOWED`: Allow anonymous executable mappings for executables on this file system. -* `MS_NOREGULAR`: Disallow opening any regular files from this file system. - -These flags can be used as a security measure to limit the possible abuses of the mounted file system. - -## Errors - -* `EINVAL`: The `flags` value contains deprecated flags such as `MS_REMOUNT` or `MS_BIND`. -* `EPERM`: The current process does not have superuser privileges. -* `ENODEV`: The `source_fd` is not an open file descriptor to a valid filesystem inode. - -All of the usual path resolution errors may also occur. - -## See also - -* [`mount`(2)](help://man/2/mount) diff --git a/Base/usr/share/man/man2/disown.md b/Base/usr/share/man/man2/disown.md deleted file mode 100644 index 84160298653..00000000000 --- a/Base/usr/share/man/man2/disown.md +++ /dev/null @@ -1,25 +0,0 @@ -## Name - -disown - disown a child process - -## Synopsis - -```**c++ -#include <serenity.h> - -int disown(pid_t pid); -``` - -## Description - -`disown()` unparents a child process of the calling process. The child's parent PID is set to zero, which allows the kernel to automatically reap it upon death. - -## Pledge - -In pledged programs, the `proc` promise is required for this system call. - -## Errors - -* `ESRCH`: No process with PID `pid` was found. -* `ECHILD`: The target process is not a child of the calling process. - diff --git a/Base/usr/share/man/man2/futex.md b/Base/usr/share/man/man2/futex.md deleted file mode 100644 index bd07eb9e4f5..00000000000 --- a/Base/usr/share/man/man2/futex.md +++ /dev/null @@ -1,193 +0,0 @@ -## Name - -futex - low-level synchronization primitive - -## Synopsis - -```c++ -#include <serenity.h> - -// Raw syscall. -int futex(uint32_t* userspace_address, int futex_op, uint32_t value, const struct timespec* timeout, uint32_t* userspace_address2, uint32_t value3); - -// More convenient wrappers. -int futex_wait(uint32_t* userspace_address, uint32_t value, const struct timespec* abstime, int clockid, int process_shared); -int futex_wake(uint32_t* userspace_address, uint32_t count, int process_shared); -``` - -## Description - -The `futex()` system call provides a low-level synchronization primitive, -essentially exposing the kernel's internal thread synchronization primitives -to userspace. - -While the `futex()` API is powerful and generic, it is complex and cumbersome -to use, and notoriously tricky to use *correctly*. For this reason, it is not -intended to be used by application code directly, but rather to serve as -a building block for more specialized and easier to use synchronization -primitives implemented in user space, such as mutexes and semaphores. -Specifically, the `futex()` API is designed to enable userspace synchronization -primitives to have a *fast path* that does not involve calling into the kernel -at all in the common uncontended case, avoiding the cost of making a syscall -completely. - -*A futex* is a single 32-bit integer cell located anywhere in the address space -of a process (identified by its address), as well as an associated kernel-side -queue of waiting threads. The kernel-side resources associated with a futex are -created and destroyed implicitly when a futex is used; in other words, any -32-bit integer can be used as a futex without any specific setup, and a futex -on which no threads are waiting is no different to any other integer. The -kernel does not assign any meaning to the value of the futex integer; it is up -to userspace to make use of the value for its own logic. - -The `futex()` API provides a number of *operations*, the most basic ones being -_waiting_ and _waking_: - -* `FUTEX_WAKE` / `futex_wake()`: wake up to `count` threads waiting on the - futex (in the raw `futex()` syscall, `count` is passed as the `value` - argument). The two most common values for `count` are 1 (wake a single - thread) and `UINT32_MAX` (wake all threads). -* `FUTEX_WAIT` / `futex_wait()`: wait on the futex, but only if the current - value of the futex integer matches the specified `value`. The value - comparison and blocking is done atomically: if another thread changes the - value before the calling thread starts waiting, the calling thread will not - begin waiting at all, and the `futex_wait()` call will return `EAGAIN` - immediately. A waiting thread may wake up spuriously, without a matching call - to `futex_wake()`. -* `FUTEX_WAKE_BITSET`: like `FUTEX_WAKE`, but only consider waiting threads - that have specified a matching bitset (passed in `value3`). Two bitsets match - if their *bitwise and* is non-zero. A thread that has not specified a bitset - is treated as having a bitset with all bits set (`FUTEX_BITSET_MATCH_ANY`, - equal to `0xffffffff`). -* `FUTEX_WAIT_BITSET`: like `FUTEX_WAIT`, but the thread will only get woken by - wake operations specifying a matching bitset. -* `FUTEX_REQUEUE`: wake up to `value` threads waiting on the futex, and requeue - up to `value2` (passed instead of the `timeout` argument) of the remaining - waiting threads to wait on another futex specified by `userspace_address2`, - without waking them up. Waking and requeueing threads is done atomically. - - Requeueing threads without waking them up is useful to avoid "thundering - herd" issues with synchronization primitives like condition variables, where - multiple threads may wait for an event, but an event can only be handled by a - single thread at a time. -* `FUTEX_CMP_REQUEUE`: like `FUTEX_REQUEUE`, but only if the current value of - the futex integer matches the specified `value3`. The value comparison, - waking and requeueing threads are all done atomically. -* `FUTEX_WAKE_OP`: modify the value of the futex specified by - `userspace_address2`, wake up to `value` threads waiting on the futex, and - optionally up to `value2` (passed instead of the `timeout` argument) threads - waiting on the futex specified by `userspace_address2`. - - The details of this operation are not currently documented here, see the - implementation for details. - -Additionally, the `FUTEX_PRIVATE_FLAG` flag can be *or*'ed in with one of the -*operation* values listed above. This flag restricts the call to only work on -other threads of the same process (as opposed to any threads in the system that -may have the same memory page mapped into their address space, possibly at a -different address), which enables additional optimizations in the syscall -implementation. The inverse of this flag is exposed as the `process_shared` -argument in `futex_wait()` and `futex_wake()` wrapper functions. - -## Return value - -* `FUTEX_WAKE`, `FUTEX_WAKE_BITSET`, `FUTEX_WAKE_OP`: the number of the waiting - threads that have been woken up, which may be 0 or a positive number. -* `FUTEX_WAIT`, `FUTEX_WAIT_BITSET`: 0 if blocked and got woken up by an - explicit wake call or woke up spuriously, an error otherwise. -* `FUTEX_REQUEUE`, `FUTEX_CMP_REQUEUE`: the total number of threads woken up - and requeued. - -## Errors - -* `EAGAIN`: for wait operations, did not begin waiting, because the futex value - has already been changed. -* `ETIMEDOUT`: for wait operations with a timeout, timed out. -* `EFAULT`: the specified futex address is invalid. -* `ENOSYS`: `FUTEX_CLOCK_REALTIME` was specified, but the operation is not - `FUTEX_WAIT` or `FUTEX_WAIT_BITSET`. -* `EINVAL`: The arithmetic-logical operation for `FUTEX_WAKE_OP` is invalid. - -## Examples - -The following program demonstrates how futexes can be used to implement a -simple "event" synchronization primitive. An event has a boolean state: it can -be *set* or *unset*; the initial state being unset. The two operations on an -event are *waiting* until it is set, and *setting* it (which wakes up any -threads that were waiting for the event to get set). - -Such a synchronization primitive could be used, for example, to notify threads -that are waiting for another thread to perform some sort of complex -initialization. - -The implementation features two fast paths: both setting an event that no -thread is waiting on, and trying to wait on an event that has already been set, -are performed entirely in userspace without calling into the kernel. For this -to work, the value of the futex integer is used to track both the state of the -event (whether it has been set) and whether any threads are waiting on it. - -```c++ -#include <AK/Atomic.h> -#include <serenity.h> - -class Event { -private: - enum State : u32 { - UnsetNoWaiters, - UnsetWithWaiters, - Set, - }; - - AK::Atomic<State> m_state { UnsetNoWaiters }; - - u32* state_futex_ptr() { return reinterpret_cast<u32*>(const_cast<State*>(m_state.ptr())); } - -public: - void set() - { - State previous_state = m_state.exchange(Set, AK::memory_order_release); - // If there was anyone waiting, wake them all up. - // Fast path: no one was waiting, so we're done. - if (previous_state == UnsetWithWaiters) - futex_wake(state_futex_ptr(), UINT32_MAX, false); - } - - void wait() - { - // If the state is UnsetNoWaiters, set it to UnsetWithWaiters. - State expected_state = UnsetNoWaiters; - bool have_exchanged = m_state.compare_exchange_strong( - expected_state, UnsetWithWaiters, - AK::memory_order_acquire); - if (have_exchanged) - expected_state = UnsetWithWaiters; - - // We need to check the state in a loop and not just once - // because of the possibility of spurious wakeups. - // Fast path: if the state was already Set, we're done. - while (expected_state != Set) { - futex_wait(state_futex_ptr(), expected_state, nullptr, 0, false); - expected_state = m_state.load(AK::memory_order_acquire); - } - } -}; -``` - -## History - -The name "futex" stands for "fast userspace mutex". - -The `futex()` system call originally appeared in Linux. Since then, many other -kernels implemented support for futex-like operations, under various names, in -particular: -* Darwin (XNU) has private `ulock_wait()` and `ulock_wake()` API; -* Windows (NT) apparently has `WaitOnAddress()`, `WakeByAddressSingle()` and - `WakeByAddressAll()`; -* FreeBSD and DargonFly BSD have `umtx`; -* OpenBSD has Linux-like `futex()`; -* GNU Hurd has `gsync_wait()`, `gsync_wake()`, and `gsync_requeue()`. - -## Further reading - -* [Futexes Are Tricky](https://akkadia.org/drepper/futex.pdf) by Ulrich Drepper -* [Locking in WebKit](https://webkit.org/blog/6161/locking-in-webkit/) by Filip Pizlo diff --git a/Base/usr/share/man/man2/get_process_name.md b/Base/usr/share/man/man2/get_process_name.md deleted file mode 100644 index 4d7ae0e4fa7..00000000000 --- a/Base/usr/share/man/man2/get_process_name.md +++ /dev/null @@ -1,28 +0,0 @@ -## Name - -get\_process\_name - get the process name - -## Synopsis - -```**c++ -#include <unistd.h> - -int get_process_name(char* buffer, int buffer_length); -``` - -## Description - -`get_process_name()` places the current process name into the provided `buffer`. - -## Pledge - -In pledged programs, the `stdio` promise is required for this system call. - -## Errors - -* `EFAULT`: the process name could not be copied into the buffer. -* `ENAMETOOLONG`: `buffer_length` is too short. - -## See also - -* [`set_process_name`(2)](help://man/2/set_process_name) diff --git a/Base/usr/share/man/man2/getegid.md b/Base/usr/share/man/man2/getegid.md deleted file mode 120000 index 96e56e9ccdd..00000000000 --- a/Base/usr/share/man/man2/getegid.md +++ /dev/null @@ -1 +0,0 @@ -geteuid.md \ No newline at end of file diff --git a/Base/usr/share/man/man2/geteuid.md b/Base/usr/share/man/man2/geteuid.md deleted file mode 100644 index e8d117695e5..00000000000 --- a/Base/usr/share/man/man2/geteuid.md +++ /dev/null @@ -1,25 +0,0 @@ -## Name - -geteuid, getegid - get effective user / group id - -## Synopsis - -```**c++ -#include <unistd.h> - -uid_t geteuid(void); -gid_t getegid(void); -``` - -## Description - -Returns the effective user or group id. - -## See also - -* [`setuid_overview`(7)](help://man/7/setuid_overview) -* [`getuid`(2) / `getgid`(2)](help://man/2/getuid) -* [`getresuid`(2) / `getresgid`(2)](help://man/2/getresuid) -* [`seteuid`(2) / `setegid`(2)](help://man/2/seteuid) -* [`setuid`(2) / `setgid`(2)](help://man/2/setuid) -* [`setresuid`(2) / `setresgid`(2)](help://man/2/setresuid) diff --git a/Base/usr/share/man/man2/getgid.md b/Base/usr/share/man/man2/getgid.md deleted file mode 120000 index 1908cb4700e..00000000000 --- a/Base/usr/share/man/man2/getgid.md +++ /dev/null @@ -1 +0,0 @@ -getuid.md \ No newline at end of file diff --git a/Base/usr/share/man/man2/getpid.md b/Base/usr/share/man/man2/getpid.md deleted file mode 100644 index b266ab8f05e..00000000000 --- a/Base/usr/share/man/man2/getpid.md +++ /dev/null @@ -1,28 +0,0 @@ -## Name - -getpid - get current process ID - -## Synopsis - -```**c++ -#include <unistd.h> - -pid_t getpid(); -``` - -## Description - -Returns the PID (process ID) of the calling process. - -## Return value - -The process ID of the calling process. - -## Errors - -None. - -## See also - -* [`getppid`(2)](help://man/2/getppid) -* [`gettid`(2)](help://man/2/gettid) diff --git a/Base/usr/share/man/man2/getppid.md b/Base/usr/share/man/man2/getppid.md deleted file mode 100644 index 181364192d1..00000000000 --- a/Base/usr/share/man/man2/getppid.md +++ /dev/null @@ -1,28 +0,0 @@ -## Name - -getppid - get current process's parent process ID - -## Synopsis - -```**c++ -#include <unistd.h> - -pid_t getppid(); -``` - -## Description - -Returns the PID (process ID) of the parent of the calling process. - -## Return value - -The process ID of the parent of the calling process. - -## Errors - -None. - -## See also - -* [`getpid`(2)](help://man/2/getpid) -* [`gettid`(2)](help://man/2/gettid) diff --git a/Base/usr/share/man/man2/getresgid.md b/Base/usr/share/man/man2/getresgid.md deleted file mode 120000 index ec0c7eb2094..00000000000 --- a/Base/usr/share/man/man2/getresgid.md +++ /dev/null @@ -1 +0,0 @@ -getresuid.md \ No newline at end of file diff --git a/Base/usr/share/man/man2/getresuid.md b/Base/usr/share/man/man2/getresuid.md deleted file mode 100644 index 698d187aac4..00000000000 --- a/Base/usr/share/man/man2/getresuid.md +++ /dev/null @@ -1,34 +0,0 @@ -## Name - -getresuid, getresgid - get real, effective, and saved user / group ID - -## Synopsis - -```**c++ -#include <unistd.h> - -int getresuid(uid_t*, uid_t*, uid_t*); -int getresgid(gid_t*, gid_t*, gid_t*); -``` - -## Description - -Returns the real, effective, and saved user or group ID. - -## Return value - -If the call was set successful, returns 0. -Otherwise, returns -1 and sets `errno` to describe the error. - -## Errors - -* `EFAULT`: One of the passed pointers is not valid, writeable pointer in the current process. - -## See also - -* [`setuid_overview`(7)](help://man/7/setuid_overview) -* [`geteuid`(2) / `getegid`(2)](help://man/2/geteuid) -* [`getuid`(2) / `getgid`(2)](help://man/2/getuid) -* [`seteuid`(2) / `setegid`(2)](help://man/2/seteuid) -* [`setuid`(2) / `setgid`(2)](help://man/2/setuid) -* [`setresuid`(2) / `setresgid`(2)](help://man/2/setresuid) diff --git a/Base/usr/share/man/man2/gettid.md b/Base/usr/share/man/man2/gettid.md deleted file mode 100644 index 0f9ab49eddf..00000000000 --- a/Base/usr/share/man/man2/gettid.md +++ /dev/null @@ -1,28 +0,0 @@ -## Name - -gettid - get current thread ID - -## Synopsis - -```**c++ -#include <unistd.h> - -int gettid(); -``` - -## Description - -Returns the TID (thread ID) of the calling thread. The first thread in a process has the same TID and PID. Subsequently spawned threads will have unique thread ID's, but all share the same PID. - -## Return value - -The thread ID of the calling thread. - -## Error - -None. - -## See also - -* [`getpid`(2)](help://man/2/getpid) -* [`getppid`(2)](help://man/2/getppid) diff --git a/Base/usr/share/man/man2/getuid.md b/Base/usr/share/man/man2/getuid.md deleted file mode 100644 index fa7ca8f7833..00000000000 --- a/Base/usr/share/man/man2/getuid.md +++ /dev/null @@ -1,25 +0,0 @@ -## Name - -getuid, getgid - get real user / group id - -## Synopsis - -```**c++ -#include <unistd.h> - -uid_t getuid(void); -gid_t getgid(void); -``` - -## Description - -Returns the real user or group id. - -## See also - -* [`setuid_overview`(7)](help://man/7/setuid_overview) -* [`geteuid`(2) / `getegid`(2)](help://man/2/geteuid) -* [`getresuid`(2) / `getresgid`(2)](help://man/2/getresuid) -* [`seteuid`(2) / `setegid`(2)](help://man/2/seteuid) -* [`setuid`(2) / `setgid`(2)](help://man/2/setuid) -* [`setresuid`(2) / `setresgid`(2)](help://man/2/setresuid) diff --git a/Base/usr/share/man/man2/mkdir.md b/Base/usr/share/man/man2/mkdir.md deleted file mode 100644 index 293b13d3fec..00000000000 --- a/Base/usr/share/man/man2/mkdir.md +++ /dev/null @@ -1,24 +0,0 @@ -## Name - -mkdir - create a directory - -## Synopsis - -```**c++ -#include <sys/stat.h> - -int mkdir(const char* path, mode_t mode); -``` - -## Description - -Create a new empty directory at the given *path* using the given *mode*. - -## Return value - -If the directory was created successfully, `mkdir()` returns 0. Otherwise, -it returns -1 and sets `errno` to describe the error. - -## See also - -* [`mkdir`(1)](help://man/1/mkdir) diff --git a/Base/usr/share/man/man2/mount.md b/Base/usr/share/man/man2/mount.md deleted file mode 100644 index b15b4fc8701..00000000000 --- a/Base/usr/share/man/man2/mount.md +++ /dev/null @@ -1,91 +0,0 @@ -## Name - -mount - mount a filesystem - -## Synopsis - -```**c++ -#include <unistd.h> - -int mount(int source_fd, const char* target, const char* fs_type, int flags); -``` - -## Description - -`mount()` mounts a filesystem stored at `source_fd` by overlaying its contents -over `target`. - -`fs_type` must be one of the following supported filesystems: - -* `Ext2FS` (or `ext2`): The ext2 filesystem. -* `ProcFS` (or `proc`): The process pseudo-filesystem (normally mounted at `/proc`). -* `DevPtsFS` (or `devpts`): The pseudoterminal pseudo-filesystem (normally mounted at `/dev/pts`). -* `RAMFS` (or `ram`): A non-persistent filesystem that stores all its data in RAM. An instance of this filesystem is normally mounted at `/tmp`. -* `Plan9FS` (or `9p`): A remote filesystem served over the 9P protocol. - -For Ext2FS, `source_fd` must refer to an open file descriptor to a file -containing the filesystem image. This may be a device file or any other seekable -file. For Plan9FS, `source_fd` must refer to a socket or a device connected to a -9P server. All the other filesystems ignore the `source_fd` - you can even pass -an invalid file descriptor such as -1. - -The following `flags` are supported: - -* `MS_NODEV`: Disallow opening any devices from this file system. -* `MS_NOEXEC`: Disallow executing any executables from this file system. -* `MS_NOSUID`: Ignore set-user-id bits on executables from this file system. -* `MS_RDONLY`: Mount the filesystem read-only. -* `MS_WXALLOWED`: Allow W^X protection circumvention for executables on this file system. -* `MS_AXALLOWED`: Allow anonymous executable mappings for executables on this file system. -* `MS_NOREGULAR`: Disallow opening any regular files from this file system. - -These flags can be used as a security measure to limit the possible abuses of the newly -mounted file system. - -### Bind mounts - -If `MS_BIND` is specified in `flags`, `fs_type` is ignored and a bind mount is -performed instead. In this case, the file or directory specified by `source_fd` -is overlaid over `target` — the target appears to be replaced by a copy of the -source. This can be used as an alternative to symlinks or hardlinks. - -Each bind mount has its own set of flags, independent of the others or the -original file system. It is possible to bind-mount a file or directory over -itself, which may be useful for changing mount flags for a part of a filesystem. - -### Remounting - -Note that remounting a file system will only affect future operations with the -file system, not any already opened files. For example, if you open a directory -on a filesystem that's mounted with `MS_NODEV`, then remount the filesystem to -allow opening devices, attempts to open a devices relative to the directory file -descriptor (such as by using `openat()`) will still fail. - -In particular, current working directory and root directory of any already -running processes behave the same way, and don't automatically "pick up" changes -in mount flags of the underlying file system. To "refresh" the working directory -to use the new mount flags after remounting a filesystem, a process can call -`chdir()` with the path to the same directory. - -## Errors - -* `EINVAL`: The `flags` value contains deprecated flags such as `MS_REMOUNT` or `MS_BIND`. -* `EFAULT`: The `fs_type` or `target` are invalid strings. -* `EPERM`: The current process does not have superuser privileges. -* `ENODEV`: The `fs_type` is unrecognized, or the file descriptor to source is - not found, or the source doesn't contain a valid filesystem image. Also, this - error occurs if `fs_type` is valid and required to be seekable, but the file - descriptor from `source_fd` is not seekable. -* `EBADF`: If the `source_fd` is not valid, and either `fs_type` specifies a - file-backed filesystem (and not a pseudo filesystem), or `MS_BIND` is - specified in flags. -* `ENOTBLK`: If the `source_fd` is not a block device, but one is required (i.e. - when `fs_type` is `Ext2FS`) - -All of the usual path resolution errors may also occur. - -## See also - -* [`mount`(8)](help://man/8/mount) -* [`remount`(2)](help://man/2/remount) -* [`bindmount`(2)](help://man/2/bindmount) diff --git a/Base/usr/share/man/man2/pipe.md b/Base/usr/share/man/man2/pipe.md deleted file mode 100644 index c569f4e81ee..00000000000 --- a/Base/usr/share/man/man2/pipe.md +++ /dev/null @@ -1,66 +0,0 @@ -## Name - -pipe, pipe2 - create a pipe - -## Synopsis - -```**c++ -#include <unistd.h> - -int pipe(int pipefd[2]); -int pipe2(int pipefd[2], int flags); -``` - -## Description - -`pipe()` creates a new pipe, an anonymous FIFO channel. It returns two new file descriptors in `pipefd`. -Any data written to the `pipefd[1]` can then be read from `pipefd[0]`. When `pipefd[1]` is closed, reads -from `pipefd[0]` will return EOF. - -`pipe2()` behaves the same as `pipe()`, but it additionally accepts the following *flags*: - -* `O_CLOEXEC`: Automatically close the file descriptors created by this call, as if by `close()` call, when performing an `exec()`. - -## Examples - -The following program creates a pipe, then forks, the child then -writes some data to the pipe which the parent reads: - -```c++ -#include <AK/Assertions.h> -#include <stdio.h> -#include <unistd.h> - -int main() -{ - // Create the pipe. - int pipefd[2]; - int rc = pipe(pipefd); - VERIFY(rc == 0); - - pid_t pid = fork(); - VERIFY(pid >= 0); - - if (pid == 0) { - // Close the reading end of the pipe. - close(pipefd[0]); - // Write a message to the writing end of the pipe. - static const char greeting[] = "Hello friends!"; - int nwritten = write(pipefd[1], greeting, sizeof(greeting)); - VERIFY(nwritten == sizeof(greeting)); - exit(0); - } else { - // Close the writing end of the pipe. - // If we don't do this, we'll never - // get an EOF. - close(pipefd[1]); - // Read the message from the reading end of the pipe. - char buffer[100]; - int nread = read(pipefd[0], buffer, sizeof(buffer)); - VERIFY(nread > 0); - // Try to read again. We should get an EOF this time. - nread = read(pipefd[0], buffer + nread, sizeof(buffer) - nread); - VERIFY(nread == 0); - } -} -``` diff --git a/Base/usr/share/man/man2/pledge.md b/Base/usr/share/man/man2/pledge.md deleted file mode 100644 index c399c7b1f2f..00000000000 --- a/Base/usr/share/man/man2/pledge.md +++ /dev/null @@ -1,77 +0,0 @@ -## Name - -pledge - reduce process capabilities - -## Synopsis - -```**c++ -#include <unistd.h> - -int pledge(const char* promises, const char* execpromises); -``` - -## Description - -`pledge()` makes a promise to the kernel that from this moment on, the calling process will only use a subset of system functionality. - -Functionality is divided into a curated set of promises (described below), which can be combined to cover the program's needs. Both arguments are space-separated lists of promises. - -Note that `pledge()` can be called repeatedly to remove previously-pledged promises, but it can never regain capabilities once lost. - -`promises` are applied to the current process, and will also be inherited by children created by [`fork`(2)](help://man/2/fork). - -`execpromises` are applied if/when a new process image is created with [`exec`(2)](help://man/2/exec). - -If `promises` or `execpromises` is null, the corresponding value is unchanged. - -If the process later attempts to use any system functionality it has previously promised *not* to use, the process is instantly terminated. Note that a process that has not ever called `pledge()` is considered to not have made any promises, and is allowed use any system functionality (subject to regular permission checks). - -`pledge()` is intended to be used in programs that want to sandbox themselves, either to limit the impact of a possible vulnerability exploitation, or before intentionally executing untrusted code. - -## Promises - -* `stdio`: Basic I/O, memory allocation, information about self, various non-destructive syscalls -* `thread`: The POSIX threading API (\*) -* `id`: Ability to change UID/GID -* `tty`: TTY related functionality -* `proc`: Process and scheduling related functionality -* `exec`: The [`exec`(2)](help://man/2/exec) syscall -* `unix`: UNIX local domain sockets -* `inet`: IPv4 domain sockets -* `accept`: May use [`accept`(2)](help://man/2/accept) to accept incoming socket connections on already listening sockets (\*) -* `rpath`: "Read" filesystem access -* `wpath`: "Write" filesystem access -* `cpath`: "Create" filesystem access -* `dpath`: Creating new device files -* `chown`: Changing file owner/group -* `fattr`: Changing file attributes/permissions -* `video`: May use [`ioctl`(2)](help://man/2/ioctl) and [`mmap`(2)](help://man/2/mmap) on framebuffer video devices -* `settime`: Changing the system time and date -* `setkeymap`: Changing the system keyboard layout (\*) -* `sigaction`: Change signal handlers and dispositions (\*) -* `sendfd`: Send file descriptors over a local socket -* `recvfd`: Receive file descriptors over a local socket -* `ptrace`: The [`ptrace`(2)](help://man/2/ptrace) syscall (\*) -* `prot_exec`: [`mmap`(2)](help://man/2/mmap) and [`mprotect`(2)](help://man/2/mprotect) with `PROT_EXEC` -* `map_fixed`: [`mmap`(2)](help://man/2/mmap) with `MAP_FIXED` or `MAP_FIXED_NOREPLACE` (\*) -* `mount`: [`mount`(2)](help://man/2/mount) Various filesystem mount related syscalls (\*) -* `no_error`: Ignore requests of pledge elevation going forwards, this is useful for enforcing _execpromises_ while the child process wants to ask for more upfront (Note that the elevation requests are _not_ granted, merely ignored), this is similar to the `error` pledge in OpenBSD. -* `jail`: Various jail-specific syscalls (\*) - -Promises marked with an asterisk (\*) are SerenityOS specific extensions not supported by the original OpenBSD `pledge()`. - -## Errors - -* `EFAULT`: `promises` and/or `execpromises` are not null and not in readable memory. -* `EINVAL`: One or more invalid promises were specified. -* `EPERM`: An attempt to increase capabilities was rejected. -* `E2BIG`: `promises` string or `execpromises `string are longer than all known promises strings together. - -## History - -The `pledge()` system call was first introduced by OpenBSD. The implementation in SerenityOS differs in many ways and is by no means final. - -## See also - -* [`unveil`(2)](help://man/2/unveil) -* [`Mitigations`(7)](help://man/7/Mitigations) diff --git a/Base/usr/share/man/man2/readlink.md b/Base/usr/share/man/man2/readlink.md deleted file mode 100644 index 6f4b9b6c94b..00000000000 --- a/Base/usr/share/man/man2/readlink.md +++ /dev/null @@ -1,67 +0,0 @@ -## Name - -readlink - get symlink target - -## Synopsis - -```**c++ -#include <unistd.h> - -ssize_t readlink(const char* path, char* buffer, size_t size) -``` - -## Description - -`readlink()` writes up to `size` bytes of the target path of a symbolic link at -the specified `path` to the given `buffer`. `readlink()` does not -null-terminate the buffer. If the target of the link is longer than `size` -bytes, it will get truncated. - -## Return value - -On success, `readlink()` returns the number of bytes written to the buffer, -which is always less or equal to the specified `size`. Otherwise, it returns -1 -and sets `errno` to describe the error. - -## Notes - -The underlying system call always returns the full size of the target path on -success, not the number of bytes written. `FileSystem::read_link()` makes use -of this to provide an alternative way to read links that doesn't require the -caller to pick a buffer size and allocate a buffer straight up. - -Since it's essentially impossible to guess the right buffer size for reading -links, it's strongly recommended that everything uses `FileSystem::read_link()` -instead. - -## Examples - -The following example demonstrates how one could implement an alternative -version of `getpid(2)` which reads the calling process ID from ProcFS: - -```c++ -#include <LibFileSystem/FileSystem.h> -#include <unistd.h> - -pid_t read_pid_using_readlink() -{ - char buffer[64]; - int rc = readlink("/proc/self", buffer, sizeof(buffer) - 1); - if (rc < 0) - return rc; - buffer[rc] = 0; - return atoi(buffer); -} - -ErrorOr<pid_t> read_pid_using_core_file() -{ - auto target = TRY(FileSystem::read_link("/proc/self"sv)); - auto pid = target.to_number<pid_t>(); - VERIFY(pid.has_value()); - return pid.value(); -} -``` - -## See also - -* [`readlink`(1)](help://man/1/readlink) diff --git a/Base/usr/share/man/man2/recvfd.md b/Base/usr/share/man/man2/recvfd.md deleted file mode 100644 index bd13777d69f..00000000000 --- a/Base/usr/share/man/man2/recvfd.md +++ /dev/null @@ -1,41 +0,0 @@ -## Name - -recvfd - receive a file descriptor from a local socket peer - -## Synopsis - -```**c++ -#include <sys/socket.h> - -int recvfd(int sockfd, int options); -``` - -## Description - -Receive an open file descriptor from a local socket peer connected via `sockfd`. This is a non-blocking call that will fail if there is no file descriptor waiting in the socket's queue. - -File descriptors are sent out-of-band and do not affect the regular data streams. - -The *options* argument accepts a bitmask of the following flags: - -* `O_CLOEXEC`: The opened fd shall be closed on [`exec`(2)](help://man/2/exec). - -## Return value - -If a file descriptor is successfully received, it is returned as a non-negative integer. Otherwise, -1 is returned and `errno` is set to indicate the error. - -## Errors - -* `EBADF`: `sockfd` is not an open file descriptor. -* `ENOTSOCK`: `sockfd` does not refer to a socket. -* `EAFNOSUPPORT`: `sockfd` does not refer to a local domain socket. -* `EINVAL`: `sockfd` does not refer to a connected or accepted socket. -* `EAGAIN`: There is no file descriptor queued on this socket. - -## History - -`recvfd()` was first introduced in Plan 9 from User Space. - -## See also - -* [`sendfd`(2)](help://man/2/sendfd) diff --git a/Base/usr/share/man/man2/remount.md b/Base/usr/share/man/man2/remount.md deleted file mode 100644 index d4384caaf3b..00000000000 --- a/Base/usr/share/man/man2/remount.md +++ /dev/null @@ -1,39 +0,0 @@ -## Name - -remount - remount a filesystem with new mount flags - -## Synopsis - -```**c++ -#include <LibCore/System.h> - -ErrorOr<void> remount(StringView target, int flags); -``` - -## Description - -`remount()` mounts a filesystem that is mounted at `target` with new mount flags of `flags`. - -The following `flags` are supported: - -* `MS_NODEV`: Disallow opening any devices from this file system. -* `MS_NOEXEC`: Disallow executing any executables from this file system. -* `MS_NOSUID`: Ignore set-user-id bits on executables from this file system. -* `MS_RDONLY`: Mount the filesystem read-only. -* `MS_WXALLOWED`: Allow W^X protection circumvention for executables on this file system. -* `MS_AXALLOWED`: Allow anonymous executable mappings for executables on this file system. -* `MS_NOREGULAR`: Disallow opening any regular files from this file system. - -These flags can be used as a security measure to limit the possible abuses of the mounted file system. - -## Errors - -* `EINVAL`: The `flags` value contains deprecated flags such as `MS_REMOUNT` or `MS_BIND`. -* `EPERM`: The current process does not have superuser privileges. -* `ENODEV`: No mount point was found for `target` path target. - -All of the usual path resolution errors may also occur. - -## See also - -* [`mount`(2)](help://man/2/mount) diff --git a/Base/usr/share/man/man2/scheduler_get_parameters.md b/Base/usr/share/man/man2/scheduler_get_parameters.md deleted file mode 120000 index 927be50c6f1..00000000000 --- a/Base/usr/share/man/man2/scheduler_get_parameters.md +++ /dev/null @@ -1 +0,0 @@ -scheduler_set_parameters.md \ No newline at end of file diff --git a/Base/usr/share/man/man2/scheduler_set_parameters.md b/Base/usr/share/man/man2/scheduler_set_parameters.md deleted file mode 100644 index 22d44365995..00000000000 --- a/Base/usr/share/man/man2/scheduler_set_parameters.md +++ /dev/null @@ -1,48 +0,0 @@ -## Name - -scheduler_set_parameters, scheduler_get_parameters - Set and get scheduler parameters for processes and threads - -## Description - -Modify or retrieve the scheduler parameters for processes or threads. `scheduler_set_parameters` will affect how the process or thread specified is scheduled the next time, so it might not have an immediately observable effect. - -The parameter argument given to both system calls is defined as: -```**c++ -struct SC_scheduler_parameters_params { - pid_t pid_or_tid; - SchedulerParametersMode mode; - struct sched_param parameters; -}; -``` - -- `mode` is an enum taking the values `Process` and `Thread`. It specifies whether the syscalls handle whole-process scheduler parameters or thread-level scheduler parameters. -- `pid_or_tid` specifies the process or thread to operate on, depending on `mode`. -- `sched_param` is the parameters that are to be read or written for the process or thread specified with the other parameters. This struct is the POSIX-compliant data structure used in `sched_setparam` and others. - -The only currently available scheduling parameter is the `int sched_priority`, the scheduling priority. - -### Security - -Both system calls require the `proc` promise. - -There are the following limitations as to which process can modify which process' or thread's parameters: -- The superuser can modify any process or thread scheduling parameters. -- Any thread can modify the scheduling parameters of all of its process' threads and of the process itself. -- Any process can modify the scheduling parameters of all processes that are owned by the same user (effective user ID and user ID must match). It cannot, however, modify the scheduling parameters of individual threads within the process. - -## Return value - -For `scheduler_get_parameters`, the retrieved parameters are written into the `sched_param` substructure. In either system call, a return value of 0 indicates success and a non-zero return value indicates an error. - -## Errors - -* `EINVAL`: The scheduling parameters are invalid. -* `EPERM`: The thread is not allowed to access the scheduling parameters for the given process or thread. -* `ESRC`: The given process ID or thread ID does not refer to an existing process or thread. -* `EFAULT`: The parameter structure pointer is invalid. - -## History - -The `scheduler_set_parameters` and `scheduler_get_parameters` syscalls replace the less generic `sched_setparam` and `sched_getparam` syscalls which precisely mirrored POSIX library functions. - -<!-- FIXME: Add See also: Scheduler(7) once that manpage exists --> diff --git a/Base/usr/share/man/man2/sendfd.md b/Base/usr/share/man/man2/sendfd.md deleted file mode 100644 index 99cac793e6f..00000000000 --- a/Base/usr/share/man/man2/sendfd.md +++ /dev/null @@ -1,38 +0,0 @@ -## Name - -sendfd - send a file descriptor to a local socket peer - -## Synopsis - -```**c++ -#include <sys/socket.h> - -int sendfd(int sockfd, int fd); -``` - -## Description - -Send an open file descriptor to a local socket peer connected via `sockfd`. This is a non-blocking call that will fail if there are too many file descriptors already waiting to be received by the peer. - -File descriptors are sent out-of-band and do not affect the regular data streams. - -## Return value - -If a file descriptor is successfully sent, `sendfd()` returns 0. Otherwise, -1 is returned and `errno` is set to indicate the error. - -## Errors - -* `EBADF`: `sockfd` or `fd` is not an open file descriptor. -* `ENOTSOCK`: `sockfd` does not refer to a socket. -* `EAFNOSUPPORT`: `sockfd` does not refer to a local domain socket. -* `ENOTCONN`: `sockfd` refers to a socket which is not connected. -* `EINVAL`: `sockfd` does not refer to a connected or accepted socket. -* `EBUSY`: There are too many file descriptors already waiting to be received by the peer. - -## History - -`sendfd()` was first introduced in Plan 9 from User Space. - -## See also - -* [`recvfd`(2)](help://man/2/recvfd) diff --git a/Base/usr/share/man/man2/set_process_name.md b/Base/usr/share/man/man2/set_process_name.md deleted file mode 100644 index e6589c09397..00000000000 --- a/Base/usr/share/man/man2/set_process_name.md +++ /dev/null @@ -1,28 +0,0 @@ -## Name - -set\_process\_name - change the process name - -## Synopsis - -```**c++ -#include <unistd.h> - -int set_process_name(const char* name, size_t name_length); -``` - -## Description - -`set_process_name()` changes the name of the calling process to the string `name` with length `name_length`. - -## Pledge - -In pledged programs, the `proc` promise is required for this system call. - -## Errors - -* `EFAULT`: `name` is not in readable memory. -* `ENAMETOOLONG`: `name_length` is too long. - -## See also - -* [`get_process_name`(2)](help://man/2/get_process_name) diff --git a/Base/usr/share/man/man2/setegid.md b/Base/usr/share/man/man2/setegid.md deleted file mode 120000 index 5485645e2ba..00000000000 --- a/Base/usr/share/man/man2/setegid.md +++ /dev/null @@ -1 +0,0 @@ -seteuid.md \ No newline at end of file diff --git a/Base/usr/share/man/man2/seteuid.md b/Base/usr/share/man/man2/seteuid.md deleted file mode 100644 index 912652af6b1..00000000000 --- a/Base/usr/share/man/man2/seteuid.md +++ /dev/null @@ -1,39 +0,0 @@ -## Name - -seteuid, setegid - set effective user / group ID - -## Synopsis - -```**c++ -#include <unistd.h> - -int seteuid(uid_t); -int setegid(gid_t); -``` - -## Description - -Sets the effective user or group ID. - -For non-superusers, the effective ID can only be set to the current real or saved ID. - -In particular, `seteuid(geteuid())` will fail if the current effective user ID is not equal to the current real or saved ID. - -## Return value - -If the call was set successful, returns 0. -Otherwise, returns -1 and sets `errno` to describe the error. - -## Errors - -* `EPERM`: The new ID is not equal to the real ID or saved ID, and the user is not superuser. -* `EINVAL`: The new ID is set to invalid value (-1). - -## See also - -* [`setuid_overview`(7)](help://man/7/setuid_overview) -* [`geteuid`(2) / `getegid`(2)](help://man/2/geteuid) -* [`getuid`(2) / `getgid`(2)](help://man/2/getuid) -* [`getresuid`(2) / `getresgid`(2)](help://man/2/getresuid) -* [`setuid`(2) / `setgid`(2)](help://man/2/setuid) -* [`setresuid`(2) / `setresgid`(2)](help://man/2/setresuid) diff --git a/Base/usr/share/man/man2/setgid.md b/Base/usr/share/man/man2/setgid.md deleted file mode 120000 index f6860c7a248..00000000000 --- a/Base/usr/share/man/man2/setgid.md +++ /dev/null @@ -1 +0,0 @@ -setuid.md \ No newline at end of file diff --git a/Base/usr/share/man/man2/setresgid.md b/Base/usr/share/man/man2/setresgid.md deleted file mode 120000 index 1351d867830..00000000000 --- a/Base/usr/share/man/man2/setresgid.md +++ /dev/null @@ -1 +0,0 @@ -setresuid.md \ No newline at end of file diff --git a/Base/usr/share/man/man2/setresuid.md b/Base/usr/share/man/man2/setresuid.md deleted file mode 100644 index d83b2b807e4..00000000000 --- a/Base/usr/share/man/man2/setresuid.md +++ /dev/null @@ -1,38 +0,0 @@ -## Name - -setresuid, setresgid - set real, effective, and saved user / group ID - -## Synopsis - -```**c++ -#include <unistd.h> - -int setresuid(uid_t, uid_t, uid_t); -int setresgid(gid_t, gid_t, gid_t); -``` - -## Description - -Sets all of real, effective, and saved user or group ID to the given values. - -An argument of `-1` keeps the corresponding ID unchanged. - -For non-superusers, each of the given values has to be equal to any of the current real, effective, or saved IDs for the call to succeed. - -## Return value - -If the call was set successful, returns 0. -Otherwise, returns -1 and sets `errno` to describe the error. - -## Errors - -* `EPERM`: At least one of the passed IDs was not equal to the current real, effective, or saved ID, and the user is not superuser. - -## See also - -* [`setuid_overview`(7)](help://man/7/setuid_overview) -* [`geteuid`(2) / `getegid`(2)](help://man/2/geteuid) -* [`getuid`(2) / `getgid`(2)](help://man/2/getuid) -* [`getresuid`(2) / `getresgid`(2)](help://man/2/getresuid) -* [`seteuid`(2) / `setegid`(2)](help://man/2/seteuid) -* [`setuid`(2) / `setgid`(2)](help://man/2/setuid) diff --git a/Base/usr/share/man/man2/setuid.md b/Base/usr/share/man/man2/setuid.md deleted file mode 100644 index 44c27b52f4e..00000000000 --- a/Base/usr/share/man/man2/setuid.md +++ /dev/null @@ -1,37 +0,0 @@ -## Name - -setuid, setgid - set user / group ID - -## Synopsis - -```**c++ -#include <unistd.h> - -int setuid(uid_t); -int setgid(gid_t); -``` - -## Description - -Sets all of real, effective, and saved user or group ID to the given ID. - -For non-superusers, the given ID has to be equal to the current real or effective ID for the call to succeed. - -## Return value - -If the call was set successful, returns 0. -Otherwise, returns -1 and sets `errno` to describe the error. - -## Errors - -* `EPERM`: The new ID is not equal to the real ID or effective ID, and the user is not superuser. -* `EINVAL`: The new ID is set to invalid value (-1). - -## See also - -* [`setuid_overview`(7)](help://man/7/setuid_overview) -* [`geteuid`(2) / `getegid`(2)](help://man/2/geteuid) -* [`getuid`(2) / `getgid`(2)](help://man/2/getuid) -* [`getresuid`(2) / `getresgid`(2)](help://man/2/getresuid) -* [`seteuid`(2) / `setegid`(2)](help://man/2/seteuid) -* [`setresuid`(2) / `setresgid`(2)](help://man/2/setresuid) diff --git a/Base/usr/share/man/man2/uname.md b/Base/usr/share/man/man2/uname.md deleted file mode 100644 index 61da6b3bbd5..00000000000 --- a/Base/usr/share/man/man2/uname.md +++ /dev/null @@ -1,38 +0,0 @@ -## Name - -uname - retrieve information about the current kernel - -## Synopsis - -```**c++ -#include <sys/utsname.h> - -int uname(struct utsname* buf); -``` - -## Description - -Retrieves information about the current kernel and writes it into the `utsname` -structure pointed to by `buf`. - -```**c++ -struct utsname { - char sysname[]; - char nodename[]; - char release[]; - char version[]; - char machine[]; -}; -``` - -## Return value - -If successful, returns 0. Otherwise, returns -1 and sets `errno` to describe the error. - -## Errors - -* `EFAULT`: `buf` is not a writable address. - -## See also - -* [`uname`(1)](help://man/1/uname) diff --git a/Base/usr/share/man/man2/unveil.md b/Base/usr/share/man/man2/unveil.md deleted file mode 100644 index 2afbd9b3899..00000000000 --- a/Base/usr/share/man/man2/unveil.md +++ /dev/null @@ -1,102 +0,0 @@ -## Name - -unveil - restrict filesystem access - -## Synopsis - -```**c++ -#include <unistd.h> - -int unveil(const char* path, const char* permissions); -``` - -## Description - -`unveil()` manipulates the process veil. The veil is a allowlist of paths on -the file system the calling process is allowed to access. - -A process that has not made any `unveil()` calls is allowed to access the whole -filesystem (subject to the regular permission checks). A process that has made -one or more `unveil()` calls cannot access any paths except those that were -explicitly unveiled. - -Calling `unveil()` allows the process to access the given `path`, which must be -an absolute path, according to the given `permissions` string, which may -include the following characters: - -* `r`: May read a file at this path -* `w`: May write to a file at this path -* `x`: May execute a program image at this path -* `c`: May create or remove a file at this path -* `b`: May browse directories at this path - -A single `unveil()` call may specify multiple permission characters at once. -Subsequent `unveil()` calls may take away permissions from the ones allowed -earlier for the same file or directory. Note that it remains possible to unveil -subdirectories with any permissions. - -Note that unveiling a path with any set of permissions does not turn off the -regular permission checks: access to a file which the process has unveiled for -itself, but has otherwise no appropriate permissions for, will still be rejected. -Unveiling a directory allows the process to access any files inside the -directory. - -Calling `unveil()` with both `path` and `permissions` set to null locks the -veil; no further `unveil()` calls are allowed after that. Although `unveil()` -calls start to take effect the moment they are made, until the veil is locked, -it remains possible to sometimes circumvent the restrictions set by unveiling -files and directories contained inside a restricted directory with different -permissions. - -When a process calls `fork()`, the unveil state is copied to the new process. -The veil state is reset after the program successfully performs an `execve()` -call. - -`unveil()` is intended to be used in programs that want to sandbox themselves, -either to limit the impact of a possible vulnerability exploitation, or before -intentionally executing untrusted code. - -## Return value - -If successful, returns 0. Otherwise, returns -1 and sets `errno` to describe -the error. - -## Errors - -* `EFAULT`: `path` and/or `permissions` are not null and not in readable - memory. -* `EPERM`: The veil is locked, or an attempt to add more permissions for an - already unveiled path was rejected. -* `EINVAL`: `path` is not an absolute path, or `permissions` are malformed. -* `E2BIG`: `permissions` string is longer than 5 characters. - -All of the usual path resolution errors may also occur. - -## History - -The `unveil()` system call was first introduced by OpenBSD. - -## Examples - -```c++ -// Allow the process to read from /res: -unveil("/res", "r"); - -// Allow the process to read, write, and create the config file: -unveil("/etc/WindowServer.ini", "rwc"); - -// Allow the process to execute Calendar: -unveil("/bin/Calendar", "x"); - -// Allow the process to browse files from /usr/share: -unveil("/usr/share", "b"); - -// Disallow any further veil manipulation: -unveil(nullptr, nullptr); -``` - -## See also - -* [`unveil`(1)](help://man/1/unveil) -* [`pledge`(2)](help://man/2/pledge) -* [`Mitigations`(7)](help://man/7/Mitigations) diff --git a/Base/usr/share/man/man3/basename.md b/Base/usr/share/man/man3/basename.md deleted file mode 100644 index 6ee64ffe865..00000000000 --- a/Base/usr/share/man/man3/basename.md +++ /dev/null @@ -1,53 +0,0 @@ -## Name - -basename - extract file name from a path - -## Synopsis - -```**c++ -#include <libgen.h> - -char* basename(char* path); -``` - -## Description - -Given a file path, `basename()` returns that file's name. `basename()` works -purely lexically, meaning it only manipulates the path as a string, and does -not check if such a file actually exists. - -A call to `basename()` may reuse and modify the passed in `path` buffer. Do not -expect it to have the same value after calling `basename()`. - -## Return value - -`basename()` returns the file name as a string. This string may be allocated -in static memory, or it may point to some part of the original `path` buffer. -Do not `free()` the returned string, and do not `free()` the original `path` -buffer while using the returned string. - -## Examples - -```c++ -#include <AK/LogStream.h> -#include <libgen.h> - -int main() -{ - char path1[] = "/home/anon/README.md"; - dbgln("{}", basename(path1)); // should be "README.md" - - char path2[] = "foo/bar/"; - dbgln("{}", basename(path2)); // should be "bar" - - char path3[] = "foo"; - dbgln("{}", basename(path3)); // should be "foo" - - char path4[] = "/"; - dbgln("{}", basename(path4)); // should be "/" -} -``` - -## See also - -* [`dirname`(3)](help://man/3/dirname) diff --git a/Base/usr/share/man/man3/clearenv.md b/Base/usr/share/man/man3/clearenv.md deleted file mode 100644 index 42185419b9a..00000000000 --- a/Base/usr/share/man/man3/clearenv.md +++ /dev/null @@ -1,34 +0,0 @@ -## Name - -clearenv - clear the environment - -## Synopsis - -```**c++ -#include <stdlib.h> - -clearenv(); -``` - -## Description - -Clears all environment variables and sets the external -variable `environ` to NULL. - -## Return value - -The `clearenv()` function returns zero. - -## Examples - -```c++ -#include <stdlib.h> - -int main() -{ - clearenv(); - putenv("PATH=/bin"); - - return 0; -} -``` diff --git a/Base/usr/share/man/man3/dirname.md b/Base/usr/share/man/man3/dirname.md deleted file mode 100644 index 2001e165f22..00000000000 --- a/Base/usr/share/man/man3/dirname.md +++ /dev/null @@ -1,54 +0,0 @@ -## Name - -dirname - get a file's containing directory path - -## Synopsis - -```**c++ -#include <libgen.h> - -char* dirname(char* path); -``` - -## Description - -Given a file path, `dirname()` returns a path to the directory that contains the -file. `dirname()` works purely lexically, meaning it only manipulates the path -as a string, and does not check if such a file or its containing directory -actually exist. - -A call to `dirname()` may reuse and modify the passed in `path` buffer. Do not -expect it to have the same value after calling `dirname()`. - -## Return value - -`dirname()` returns the directory path as a string. This string may be allocated -in static memory, or it may point to some part of the original `path` buffer. -Do not `free()` the returned string, and do not `free()` the original `path` -buffer while using the returned string. - -## Examples - -```c++ -#include <AK/LogStream.h> -#include <libgen.h> - -int main() -{ - char path1[] = "/home/anon/README.md"; - dbgln("{}", dirname(path1)); // should be "/home/anon" - - char path2[] = "foo/bar/"; - dbgln("{}", dirname(path2)); // should be "foo" - - char path3[] = "foo"; - dbgln("{}", dirname(path3)); // should be "." - - char path4[] = "/"; - dbgln("{}", dirname(path4)); // should be "/" -} -``` - -## See also - -* [`basename`(3)](help://man/3/basename) diff --git a/Base/usr/share/man/man3/futimens.md b/Base/usr/share/man/man3/futimens.md deleted file mode 120000 index 71f7162cffc..00000000000 --- a/Base/usr/share/man/man3/futimens.md +++ /dev/null @@ -1 +0,0 @@ -utimensat.md \ No newline at end of file diff --git a/Base/usr/share/man/man3/getopt.md b/Base/usr/share/man/man3/getopt.md deleted file mode 100644 index 88625e7084e..00000000000 --- a/Base/usr/share/man/man3/getopt.md +++ /dev/null @@ -1,153 +0,0 @@ -## Name - -getopt - parse command-line options - -## Synopsis - -```**c++ -#include <getopt.h> - -extern int opterr; -extern int optopt; -extern int optind; -extern int optreset; -extern char* optarg; - -struct option { - const char* name; - int has_arg; - int* flag; - int val; -}; - -int getopt(int argc, char** argv, const char* short_options); -int getopt_long(int argc, char** argv, const char* short_options, const struct option* long_options, int* out_long_option_index); -``` - -## Description - -`getopt()` and `getopt_long()` parse options according to the syntax specified -in [`getopt`(5)](help://man/5/getopt). `getopt()` only supports short options; -`getopt_long()` supports both short and long options. - -One invocation of either function extracts at most one option from command line -arguments, which are passed to it as the `argc`/`argv` pair, starting from -argument at index `optind`, which is initially set to 1 at program startup. - -The `short_options` string should specify the short options to be recognized, as -single characters. If a short option requires a value, it is to be followed by a -colon character (`:`); if a short option optionally accepts a value, it is to be -followed by a double colon (`::`). If the first character in the `short_options` -is `+`, `getopt()` and `getopt_long()` won't look for further options once first -non-option argument is encountered. - -`getopt_long()` additionally accepts an array of values describing long options -to be recognized. To specify whether a long option has a value, the `has_arg` -member of `struct option` must be set to one of the following predefined macro -values: - -* `no_argument`, if no value is accepted; -* `required_argument`, if a value is optionally accepted; -* `optional_argument`, if a value is optionally accepted. - -If an option is parsed successfully, `getopt()` and `getopt_long()` -automatically increase the `optind` variable to point to the next command-line -argument to be parsed. This makes it possible to invoke `getopt()` or -`getopt_long()` in a loop unless they indicate either an error or the end of -options, and then treat the remaining command-line arguments, starting from the -one pointed to be `optind`, as non-option argument. - -Unless `+` is specified as the first character of `short_options`, `getopt()` -and `getopt_long()` automatically reorder elements of `argv` to put options and -their values before any non-option arguments. - -If, after having used `getopt()` or `getopt_long()` to parse a set of -command-line arguments, the program intends to use the `getopt()` or -`getopt_long()` to parse a different set of command-line arguments, it must ask -`getopt()` and `getopt_long()` to reset the internal state that they keep across -calls to handle some tricky cases. To do so, the program must either set the -`optreset` variable to a non-zero value, or set `optind` variable to 0. Doing -either of these things will reset the internal state, and option parsing will -start from command-line argument at index 1 in either case. - -## Return value - -If no option has been found, `getopt()` and `getopt_long()` return -1. - -In case some invalid configuration of options and their values are passed in -`argv`, `getopt()` and `getopt_long()` return the `'?'` character. If the error -is related to a short option, the variable `optopt` is set to the option -character. If the variable `opterr` has a non-zero value (as it does by -default), an appropriate error message is printed to the standard error stream. - -If a short option has been successfully parsed, `getopt()` and `getopt_long()` -return its character. Its value, if any, is assigned to the `optarg` variable. -If the option has been given no value, `optarg` is set to `nullptr`. - -If a long option has been successfully parsed, `getopt_long()` return value -depends on the value of the `flag` pointer for that option. If `flag` is -`nullptr`, `getopt_long()` returns the value of `val` for that option. -Otherwise, the pointee of `flag` is set to `val`, and `getopt_long()` returns 0. -In either case, the index of the long option in the `long_options` array is -stored to the pointee of `out_long_option_index`, if it's not a `nullptr`. Same -as for short options, the `optarg` variable is set to point to the value of the -option, or to `nullptr` is none has been given. - -## Examples - -```c++ -#include <getopt.h> - -int verbose = 0; -const char* pad = nullptr; -const char* source = nullptr; - -while (true) { - // Accept short options: -h, -l, -s value, -p [value], -N. - const char* short_options = "hls:p::N"; - // Accept long options: --pad [value], --verbose. - const option long_options[] { - { "pad", optional_argument, nullptr, 'p' }, - { "verbose", no_argument, &verbose, 1 }, - }; - int opt = getopt_long(argc, argv, short_options, long_options, nullptr); - switch (opt) { - case -1: - // No more options. - return true; - case '?': - // Some error; getopt() has already printed an error message. - exit(1); - case 'h': - // Handle the -h option... - break; - case 'l': - // Handle the -l option... - break; - case 's': - // Handle the -s option. - source = optarg; - break; - case 'p': - // Handle the -p short option or the --pad long option. - if (optarg) - pad = optarg; - else - pad = ' '; - break; - case 'N': - // Handle the -N option. - break; - case 0: - // A long option (--verbose) has been parsed, but its - // effect was setting the verbose variable to 1. - break; - } -} - -const char* file_name = argv[optind]; -``` - -## See also - -* [`getopt`(5)](help://man/5/getopt) diff --git a/Base/usr/share/man/man3/isatty.md b/Base/usr/share/man/man3/isatty.md deleted file mode 100644 index f615d721b92..00000000000 --- a/Base/usr/share/man/man3/isatty.md +++ /dev/null @@ -1,25 +0,0 @@ -## Name - -isatty - check if a file descriptor is a TTY - -## Synopsis - -```**c++ -#include <unistd.h> - -int isatty(int fd); -``` - -## Description - -Checks if the device inside a given file descriptor is a TTY device. - -## Return value - -If `fd` refers to a TTY device, returns 1. Otherwise, returns 0 and `errno` is set to describe the error. - -## Errors - -* `EBADF`: `fd` is not an open file descriptor. -* `ENOTTY`: `fd` refers to something that's not a TTY device. -* `EINVAL`: `fd` refers to something that supports `ioctl()`, but is still not a TTY device. diff --git a/Base/usr/share/man/man3/posix_openpt.md b/Base/usr/share/man/man3/posix_openpt.md deleted file mode 100644 index 7734b7146e4..00000000000 --- a/Base/usr/share/man/man3/posix_openpt.md +++ /dev/null @@ -1,34 +0,0 @@ -## Name - -posix\_openpt - open a pseudo-terminal device - -## Synopsis - -```**c++ -#include <stdlib.h> -#include <fcntl.h> - -int posix_openpt(int flags); -``` - -## Description - -Open a pseudo-terminal master using the given *flags*. - -The *flags* argument accepts a bitmask of the following flags: - -* `O_RDWR`: Open for both reading and writing. -* `O_NOCTTY`: The opened pseudo-terminal will not be made the controlling TTY for the process. -* `O_CLOEXEC`: The opened fd shall be closed on [`exec`(2)](help://man/2/exec). - -## Return value - -On success, a pseudo-terminal device is allocated and `posix_openpt()` returns a file descriptor for it. Otherwise, it returns -1 and sets `errno` to describe the error. - -## Errors - -Returns the same errors as [`open`(2)](help://man/2/open). - -## See also - -* [`open`(2)](help://man/2/open) diff --git a/Base/usr/share/man/man3/posix_spawn.md b/Base/usr/share/man/man3/posix_spawn.md deleted file mode 100644 index a0bafe7873a..00000000000 --- a/Base/usr/share/man/man3/posix_spawn.md +++ /dev/null @@ -1,66 +0,0 @@ -## Name - -posix\_spawn - launch a new process - -## Synopsis - -```**c++ -#include <spawn.h> - -int posix_spawn(pid_t* pid, const char* executable_path, const posix_spawn_file_actions_t*, const posix_spawnattr_t*, char* const argv[], char* const envp[]); -int posix_spawnp(pid_t* pid, const char* executable_path, const posix_spawn_file_actions_t*, const posix_spawnattr_t*, char* const argv[], char* const envp[]); -``` - -## Description - -Spawn a new process reading the binary `executable_path`, passing `argv` as arguments to `main()` and setting `envp` as argument. - -Places the process ID of the new process in `pid`. - -If `executable_path` passed to `posix_spawn` is a relative path, it is resolved relative to the current working directory. - -If `executable_path` passed to `posix_spawnp` is a relative path, it is resolved by searching through directories specified in the `PATH` environment variable. - -The `posix_spawn_file_actions_t` and `posix_spawnattr_t` arguments may be `nullptr`. If they aren't, see [`posix_spawn_file_actions`(2)](help://man/3/posix_spawn_file_actions_init) and [`posix_spawnattr`(2)](help://man/3/posix_spawnattr_init) for what they do. - -The last entry in `argv` and `envp` has to be `nullptr`. - -The new process is started as if the following steps are executed in this order: - -1. A new process is started as if `fork()` was called. -2. If the `posix_spawnattr_t` parameter is non-nullptr, it [takes effect](help://man/3/posix_spawnattr_init). -3. If the `posix_spawn_file_actions_t` parameter is non-nullptr, it [takes effect](help://man/3/posix_spawn_file_actions_init). -4. `executable_path` is loaded and starts running, as if `execve` or `execvpe` was called. - -## Return value - -If the process is successfully forked, returns 0. -Otherwise, returns an error number. This function does *not* return -1 on error and does *not* set `errno` like most other functions, it instead returns what other functions set `errno` to as result. - -If the process forks successfully but spawnattr or file action processing or exec fail, `posix_spawn` returns 0 and the child exits with exit code `127`. - -## Example - -This simple example launches `/bin/Calculator`. - -To make the child process use the parent's environment, it passes `environ` from `unistd.h`. - -```**c++ -#include <errno.h> -#include <unistd.h> -#include <stdio.h> -#include <spawn.h> - -int main() -{ - const char* argv[] = { "Calculator", nullptr }; - pid_t child_pid; - if ((errno = posix_spawn(&child_pid, "/bin/Calculator", nullptr, nullptr, const_cast<char**>(argv), environ))) - perror("posix_spawn"); -} -``` - -## See also - -* [`posix_spawnattr`(2)](help://man/3/posix_spawnattr_init) -* [`posix_spawn_file_actions`(2)](help://man/3/posix_spawn_file_actions_init) diff --git a/Base/usr/share/man/man3/posix_spawn_file_actions_addchdir.md b/Base/usr/share/man/man3/posix_spawn_file_actions_addchdir.md deleted file mode 120000 index b6cbb8842e2..00000000000 --- a/Base/usr/share/man/man3/posix_spawn_file_actions_addchdir.md +++ /dev/null @@ -1 +0,0 @@ -posix_spawn_file_actions_init.md \ No newline at end of file diff --git a/Base/usr/share/man/man3/posix_spawn_file_actions_addclose.md b/Base/usr/share/man/man3/posix_spawn_file_actions_addclose.md deleted file mode 120000 index b6cbb8842e2..00000000000 --- a/Base/usr/share/man/man3/posix_spawn_file_actions_addclose.md +++ /dev/null @@ -1 +0,0 @@ -posix_spawn_file_actions_init.md \ No newline at end of file diff --git a/Base/usr/share/man/man3/posix_spawn_file_actions_adddestroy.md b/Base/usr/share/man/man3/posix_spawn_file_actions_adddestroy.md deleted file mode 120000 index b6cbb8842e2..00000000000 --- a/Base/usr/share/man/man3/posix_spawn_file_actions_adddestroy.md +++ /dev/null @@ -1 +0,0 @@ -posix_spawn_file_actions_init.md \ No newline at end of file diff --git a/Base/usr/share/man/man3/posix_spawn_file_actions_adddup2.md b/Base/usr/share/man/man3/posix_spawn_file_actions_adddup2.md deleted file mode 120000 index b6cbb8842e2..00000000000 --- a/Base/usr/share/man/man3/posix_spawn_file_actions_adddup2.md +++ /dev/null @@ -1 +0,0 @@ -posix_spawn_file_actions_init.md \ No newline at end of file diff --git a/Base/usr/share/man/man3/posix_spawn_file_actions_addfchdir.md b/Base/usr/share/man/man3/posix_spawn_file_actions_addfchdir.md deleted file mode 120000 index b6cbb8842e2..00000000000 --- a/Base/usr/share/man/man3/posix_spawn_file_actions_addfchdir.md +++ /dev/null @@ -1 +0,0 @@ -posix_spawn_file_actions_init.md \ No newline at end of file diff --git a/Base/usr/share/man/man3/posix_spawn_file_actions_addopen.md b/Base/usr/share/man/man3/posix_spawn_file_actions_addopen.md deleted file mode 120000 index b6cbb8842e2..00000000000 --- a/Base/usr/share/man/man3/posix_spawn_file_actions_addopen.md +++ /dev/null @@ -1 +0,0 @@ -posix_spawn_file_actions_init.md \ No newline at end of file diff --git a/Base/usr/share/man/man3/posix_spawn_file_actions_init.md b/Base/usr/share/man/man3/posix_spawn_file_actions_init.md deleted file mode 100644 index 4f44ba4f9ce..00000000000 --- a/Base/usr/share/man/man3/posix_spawn_file_actions_init.md +++ /dev/null @@ -1,50 +0,0 @@ -## Name - -`posix_spawn_file_actions` - configure file actions for `posix_spawn` - -## Synopsis - -```**c++ -#include <spawn.h> - -typedef struct posix_spawn_file_actions_t; - -int posix_spawn_file_actions_init(posix_spawn_file_actions_t*); -int posix_spawn_file_actions_destroy(posix_spawn_file_actions_t*); - -int posix_spawn_file_actions_addchdir(posix_spawn_file_actions_t*, const char*); -int posix_spawn_file_actions_addfchdir(posix_spawn_file_actions_t*, int); -int posix_spawn_file_actions_addclose(posix_spawn_file_actions_t*, int); -int posix_spawn_file_actions_adddup2(posix_spawn_file_actions_t*, int old_fd, int new_fd); -int posix_spawn_file_actions_addopen(posix_spawn_file_actions_t*, int fd, const char*, int flags, mode_t); -``` - -## Description - -Configure a `posix_spawn_file_actions_t` object for use with [`posix_spawn`(2)](help://man/3/posix_spawn). This object can be used to let `posix_spawn()` set up file-related state for the spawned child process. The file actions are executed after creating the new process but before loading its binary in the order they were added to the `posix_spawn_file_actions_t` object. - -A `posix_spawn_file_actions_t` object is allocated on the stack but starts in an undefined state. - -`posix_spawn_file_actions_init()` initializes a `posix_spawn_file_actions_t` object that is in an undefined state and puts it in a valid state. It has to be called before the object can be passed to any other function. - -`posix_spawn_file_actions_destroy()` frees up resources used by a valid `posix_spawn_file_actions_t` object and puts it into an undefined state. It has to be called after a `posix_spawn_file_actions_t` object is no longer needed. - -It is valid to alternatingly call `posix_spawn_file_actions_init()` and `posix_spawn_file_actions_destroy()` on the same object, - -`posix_spawn_file_actions_addchdir()` and `posix_spawn_file_actions_addfchdir()` make `posix_spawn()` change the current working directory before spawning a process, like `chdir` and `fchdir` would.. The current working directory affects the spawned child process, but also relative paths passed to later `posix_spawn_file_actions_add(f)chdir()` and `posix_spawn_file_actions_addopen()`, and relative paths passed to `posix_spawn()` for the executable path. - -`posix_spawn_file_actions_addclose()` makes `posix_spawn()` close a file descriptor before spawning a process, like `close` would. - -`posix_spawn_file_actions_addclose()` makes `posix_spawn()` dup a file descriptor before spawning a process, like `dup2` would. - -`posix_spawn_file_actions_addopen()` makes `posix_spawn()` open a file with given flags and mode, like `open` would, and then makes it available under fd `fd` to the spawned process. - -## Return value - -In SerenityOS, these functions always succeed and return 0. - -If the effect of a file action fails, the child will exit with exit code 127 before even executing the child binary. - -## See also - -* [`posix_spawn`(2)](help://man/3/posix_spawn) diff --git a/Base/usr/share/man/man3/posix_spawnattr_destroy.md b/Base/usr/share/man/man3/posix_spawnattr_destroy.md deleted file mode 120000 index c76ef05948e..00000000000 --- a/Base/usr/share/man/man3/posix_spawnattr_destroy.md +++ /dev/null @@ -1 +0,0 @@ -posix_spawnattr_init.md \ No newline at end of file diff --git a/Base/usr/share/man/man3/posix_spawnattr_getflags.md b/Base/usr/share/man/man3/posix_spawnattr_getflags.md deleted file mode 120000 index c76ef05948e..00000000000 --- a/Base/usr/share/man/man3/posix_spawnattr_getflags.md +++ /dev/null @@ -1 +0,0 @@ -posix_spawnattr_init.md \ No newline at end of file diff --git a/Base/usr/share/man/man3/posix_spawnattr_getpgroup.md b/Base/usr/share/man/man3/posix_spawnattr_getpgroup.md deleted file mode 120000 index c76ef05948e..00000000000 --- a/Base/usr/share/man/man3/posix_spawnattr_getpgroup.md +++ /dev/null @@ -1 +0,0 @@ -posix_spawnattr_init.md \ No newline at end of file diff --git a/Base/usr/share/man/man3/posix_spawnattr_getschedparam.md b/Base/usr/share/man/man3/posix_spawnattr_getschedparam.md deleted file mode 120000 index c76ef05948e..00000000000 --- a/Base/usr/share/man/man3/posix_spawnattr_getschedparam.md +++ /dev/null @@ -1 +0,0 @@ -posix_spawnattr_init.md \ No newline at end of file diff --git a/Base/usr/share/man/man3/posix_spawnattr_getschedpolicy.md b/Base/usr/share/man/man3/posix_spawnattr_getschedpolicy.md deleted file mode 120000 index c76ef05948e..00000000000 --- a/Base/usr/share/man/man3/posix_spawnattr_getschedpolicy.md +++ /dev/null @@ -1 +0,0 @@ -posix_spawnattr_init.md \ No newline at end of file diff --git a/Base/usr/share/man/man3/posix_spawnattr_getsigdefault.md b/Base/usr/share/man/man3/posix_spawnattr_getsigdefault.md deleted file mode 120000 index c76ef05948e..00000000000 --- a/Base/usr/share/man/man3/posix_spawnattr_getsigdefault.md +++ /dev/null @@ -1 +0,0 @@ -posix_spawnattr_init.md \ No newline at end of file diff --git a/Base/usr/share/man/man3/posix_spawnattr_getsigmask.md b/Base/usr/share/man/man3/posix_spawnattr_getsigmask.md deleted file mode 120000 index c76ef05948e..00000000000 --- a/Base/usr/share/man/man3/posix_spawnattr_getsigmask.md +++ /dev/null @@ -1 +0,0 @@ -posix_spawnattr_init.md \ No newline at end of file diff --git a/Base/usr/share/man/man3/posix_spawnattr_init.md b/Base/usr/share/man/man3/posix_spawnattr_init.md deleted file mode 100644 index 1ba01e3adb1..00000000000 --- a/Base/usr/share/man/man3/posix_spawnattr_init.md +++ /dev/null @@ -1,78 +0,0 @@ -## Name - -posix\_spawnattr - configure attributes for posix\_spawn - -## Synopsis - -```**c++ -#include <spawn.h> - -POSIX_SPAWN_RESETIDS -POSIX_SPAWN_SETPGROUP -POSIX_SPAWN_SETSCHEDPARAM -POSIX_SPAWN_SETSCHEDULER -POSIX_SPAWN_SETSIGDEF -POSIX_SPAWN_SETSIGMASK -POSIX_SPAWN_SETSID - -struct posix_spawnattr_t; - -int posix_spawnattr_init(posix_spawnattr_t*); -int posix_spawnattr_destroy(posix_spawnattr_t*); - -int posix_spawnattr_getflags(const posix_spawnattr_t*, short*); -int posix_spawnattr_getpgroup(const posix_spawnattr_t*, pid_t*); -int posix_spawnattr_getschedparam(const posix_spawnattr_t*, struct sched_param*); -int posix_spawnattr_getschedpolicy(const posix_spawnattr_t*, int*); -int posix_spawnattr_getsigdefault(const posix_spawnattr_t*, sigset_t*); -int posix_spawnattr_getsigmask(const posix_spawnattr_t*, sigset_t*); -int posix_spawnattr_setflags(posix_spawnattr_t*, short); -int posix_spawnattr_setpgroup(posix_spawnattr_t*, pid_t); -int posix_spawnattr_setschedparam(posix_spawnattr_t*, const struct sched_param*); -int posix_spawnattr_setschedpolicy(posix_spawnattr_t*, int); -int posix_spawnattr_setsigdefault(posix_spawnattr_t*, const sigset_t*); -int posix_spawnattr_setsigmask(posix_spawnattr_t*, const sigset_t*); -``` - -## Description - -Configures a `posix_spawnattr_t` object for use with [`posix_spawn`(2)](help://man/3/posix_spawn). This object can be used to let `posix_spawn()` set up process attributes for the spawned child process. The file actions are executed after creating the new process but before loading its binary. - -A `posix_spawnattr_t` object is allocated on the stack but starts in an undefined state. - -`posix_spawnattr_init()` initializes a `posix_spawnattr_t` object that is in an undefined state and puts it in a valid state. It has to be called before the object can be passed to any other function. - -`posix_spawnattr_destroy()` frees up resources used by a valid `posix_spawn_file_actions_t` object and puts it into an undefined state. It has to be called after a `posix_spawnattr_t` object is no longer needed. - -It is valid to alternatingly call `posix_spawnattr_init()` and `posix_spawnattr_destroy()` on the same object, - -`posix_spawnattr_setflags()` configures which attributes of the new child process `posix_spawn()` will set. It receives a bitmask that can contain: - -* `POSIX_SPAWN_RESETIDS`: If set, `posix_spawn()` will reset the effective uid and gid of the child process to the real uid and gid of the parent process. See also [`setuid_overview`(7)](help://man/7/setuid_overview). - -* `POSIX_SPAWN_SETPGROUP`: If set, `posix_spawn()` will set the process group ID of the child process to the process group ID configured with `posix_spawnattr_setpgroup()`, as if `setpgid(0, pgroup)` was called in the child process. The behavior if both this and `POSIX_SPAWN_SETSID` is set is undefined. - -* `POSIX_SPAWN_SETSCHEDPARAM`: If set, `posix_spawn()` will set the scheduler parameter of the child process to the process group ID configured with `posix_spawnattr_setschedparam()`, as if `sched_setparam(0, schedparam)` was called in the child process. - -* `POSIX_SPAWN_SETSCHEDULER`: This is not yet implemented in SerenityOS. - -* `POSIX_SPAWN_SETSIGDEF`: If set, `posix_spawn()` will reset the signal handlers of the child process configured with `posix_spawnattr_setsigdefault()` to each signal's default handler. - -* `POSIX_SPAWN_SETSIGMASK`: If set, `posix_spawn()` will set the signal mask of the child process to the signal mask configured with `posix_spawnattr_setsigmask()`, as if `sigprocmask()` was called in the child process. - -* `POSIX_SPAWN_SETSID`: If set, `posix_spawn()` will run the child process in a new session, as if `setsid()` was called in the child process. The behavior if both this and `POSIX_SPAWN_SETPGROUP` is set is undefined. - -The `posix_spawnattr_get*` functions return what's been set with the corresponding setters. The default `flags` and `pgroup` are 0, the default `sigdefault` set is `sigemptyset()`, all other fields have an unspecified default value. - - -## Return value - -In SerenityOS, these functions always succeed and return 0. - -The one exception is `posix_spawnattr_setflags()`, which can return -1 and set `errno` to `EINVAL` if an unknown bit is set in the passed bitmask. - -If the effect of an attr fails, the child will exit with exit code 127 before even executing the child binary. - -## See also - -* [`posix_spawn`(2)](help://man/3/posix_spawn) diff --git a/Base/usr/share/man/man3/posix_spawnattr_setflags.md b/Base/usr/share/man/man3/posix_spawnattr_setflags.md deleted file mode 120000 index c76ef05948e..00000000000 --- a/Base/usr/share/man/man3/posix_spawnattr_setflags.md +++ /dev/null @@ -1 +0,0 @@ -posix_spawnattr_init.md \ No newline at end of file diff --git a/Base/usr/share/man/man3/posix_spawnattr_setpgroup.md b/Base/usr/share/man/man3/posix_spawnattr_setpgroup.md deleted file mode 120000 index c76ef05948e..00000000000 --- a/Base/usr/share/man/man3/posix_spawnattr_setpgroup.md +++ /dev/null @@ -1 +0,0 @@ -posix_spawnattr_init.md \ No newline at end of file diff --git a/Base/usr/share/man/man3/posix_spawnattr_setschedparam.md b/Base/usr/share/man/man3/posix_spawnattr_setschedparam.md deleted file mode 120000 index c76ef05948e..00000000000 --- a/Base/usr/share/man/man3/posix_spawnattr_setschedparam.md +++ /dev/null @@ -1 +0,0 @@ -posix_spawnattr_init.md \ No newline at end of file diff --git a/Base/usr/share/man/man3/posix_spawnattr_setschedpolicy.md b/Base/usr/share/man/man3/posix_spawnattr_setschedpolicy.md deleted file mode 120000 index c76ef05948e..00000000000 --- a/Base/usr/share/man/man3/posix_spawnattr_setschedpolicy.md +++ /dev/null @@ -1 +0,0 @@ -posix_spawnattr_init.md \ No newline at end of file diff --git a/Base/usr/share/man/man3/posix_spawnattr_setsigdefault.md b/Base/usr/share/man/man3/posix_spawnattr_setsigdefault.md deleted file mode 120000 index c76ef05948e..00000000000 --- a/Base/usr/share/man/man3/posix_spawnattr_setsigdefault.md +++ /dev/null @@ -1 +0,0 @@ -posix_spawnattr_init.md \ No newline at end of file diff --git a/Base/usr/share/man/man3/posix_spawnattr_setsigmask.md b/Base/usr/share/man/man3/posix_spawnattr_setsigmask.md deleted file mode 120000 index c76ef05948e..00000000000 --- a/Base/usr/share/man/man3/posix_spawnattr_setsigmask.md +++ /dev/null @@ -1 +0,0 @@ -posix_spawnattr_init.md \ No newline at end of file diff --git a/Base/usr/share/man/man3/posix_spawnp.md b/Base/usr/share/man/man3/posix_spawnp.md deleted file mode 120000 index 66c09b5e090..00000000000 --- a/Base/usr/share/man/man3/posix_spawnp.md +++ /dev/null @@ -1 +0,0 @@ -posix_spawn.md \ No newline at end of file diff --git a/Base/usr/share/man/man3/utimensat.md b/Base/usr/share/man/man3/utimensat.md deleted file mode 100644 index 560398dfdbb..00000000000 --- a/Base/usr/share/man/man3/utimensat.md +++ /dev/null @@ -1,105 +0,0 @@ -## Name - -futimens, utimensat - update file access and modification times - -## Synopsis - -```**c++ -#include <sys/stat.h> - -int futimens(int fd, struct timespec const times[2]); - -#include <fcntl.h> - -int utimensat(int dirfd, char const* path, struct timespec const times[2], - int flag); -``` - -## Description - -`futimens()` and `utimensat()` set the access and modification times of a file -to the values specified in `times`. - -`futimens()` updates the times of the file associated with the file descriptor. - -`utimensat()` functions in two ways. - -1. Given a valid file descriptor for a directory and a non-empty path, -`utimensat()` updates the value of the file specified by the path relative to -the directory specified by the file descriptor. This is standard POSIX -behavior. -2. Given a valid file descriptor to a regular file and an empty path, -`utimensat()` updates the value of the file associated with the file -descriptor. This is not standard POSIX behavior, but it allows `futimens()` to -be implemented in terms of `utimensat()`. - -If the `tv_nsec` field of `times` is set to UTIME_NOW, then the corresponding -timestamp of the file is set to the current time. If the `tv_nsec` field of -`times` is set to UTIME_OMIT, then the corresponding timestamp is not modified. -In both of these special cases, `tv_sec`, the other field in `times`, is -ignored. - -If `times` is a null pointer, then both the last access time and the last -modification time are set to the current time. This configuration is equivalent -to setting the `tv_nsec` field to UTIME_NOW for both timespec structures in the -array. - -Parameter `flag` of `utimensat()` may be set to 0 or `AT_SYMLINK_NOFOLLOW`. If -set to `AT_SYMLINK_NOFOLLOW`, instead of following a symbolic link, -`utimensat()` updates the timestamps of the link itself. `futimens()` always -follows symbolic links. - -Parameter `dirfd` of `utimensat()` may be set to `AT_FDCWD` to use the current -working directory as the relative directory. - -## Return Value - -`futimens()` and `utimensat()` return 0 upon success and -1 otherwise. Upon -failure, these functions also set `errno` to indicate the error and leave the -access and modification times of the specified file unmodified. - -## Errors - -`futimens()` and `utimensat()` may return the following error codes. - -* `EFAULT`: `path` of `utimensat()` is a null pointer. -* `EINVAL`: Length of `path` is too long. -* `EINVAL`: `flag` is not 0 or `AT_SYMLINK_NOFOLLOW` -* `EINVAL`: The timestamp is not supported by the file system. -* `EINVAL`: Fields of `times` are less than 0 or greater than or equal to 1000 -million and not `UTIME_NOW` or `UTIME_OMIT`. -* `EACCES`: The current user does not have write access to the file. -* `EROFS`: The file system that contains the file is read-only. -* `ENOTDIR`: `path` is not absolute and `dirfd` is not a file descriptor -associated with a directory. - -## Examples - -```c++ -#include <fcntl.h> -#include <sys/stat.h> - -int main() -{ - timespec times[2]; - auto& atime = times[0]; - auto& mtime = times[1]; - - atime.tv_sec = 0; - atime.tv_nsec = UTIME_NOW; - mtime.tv_sec = 0; - mtime.tv_nsec = UTIME_OMIT; - - // Update only last access time of file "README.md" in current working - // directory to current time. Leave last modification time unchanged. - if (utimensat(AT_FDCWD, "README.md", times, 0) == -1) { - return 1; - } - - return 0; -} -``` - -## See also - -* [`touch`(1)](help://man/1/touch) diff --git a/Base/usr/share/man/man4/audio.md b/Base/usr/share/man/man4/audio.md deleted file mode 100644 index 38e8652239a..00000000000 --- a/Base/usr/share/man/man4/audio.md +++ /dev/null @@ -1,25 +0,0 @@ -## Name - -audio - system audio devices - -## Description - -`/dev/audio` is the directory for the system audio devices. Currently, there are only output devices, so every device file in the directory is an output channel. These channels are numbered, with `/dev/audio/0` being the first channel of the first device. To get the audio device to play audio, PCM samples need to be written to it as a series of "frames" (in MPEG terminology) or multi-channel samples with the following format: - -| Byte | 0-1 | 2-3 | -|--|:--:|:--:| -| Format | 16-bit signed | 16-bit signed | -| Data | Left sample | Right sample | - -The sample rate of the samples is determined by the audio device's current sample rate, which may be accessed by an [ioctl](help://man/2/ioctl). - -Note that for convenience, the audio device may not block the call to `write` and return before all the samples were actually transferred to the hardware and/or played by the hardware. For this reason, users need to be aware that the audio device driver's internal buffer may become full and calls to `write` may return `ENOSPC`. - -## Available `ioctl`s - -* `SOUNDCARD_IOCTL_GET_SAMPLE_RATE`: Passes the current device sample rate (in samples per second) into a provided `u16*` (16-bit unsigned integer pointer). -* `SOUNDCARD_IOCTL_SET_SAMPLE_RATE`: Sets the sample rate of the underlying hardware from a provided 16-bit unsigned integer. Note that not all sound cards support all sample rate and the actually achieved sample rate should be checked with the GET_SAMPLE_RATE ioctl. - -## See also - -* [Audio-subsystem](help://man/7/Audio-subsystem) diff --git a/Base/usr/share/man/man4/full.md b/Base/usr/share/man/man4/full.md deleted file mode 100644 index fac94af4dbc..00000000000 --- a/Base/usr/share/man/man4/full.md +++ /dev/null @@ -1,35 +0,0 @@ -## Name - -full - always full device - -## Description - -`/dev/full` is a character device which is always full. - -Reading from `/dev/full` returns '\0' bytes and exits with status 0. - -Writing to `/dev/full` fails with ENOSPC error. - -To create it manually: - -```sh -mknod /dev/full c 1 7 -chmod 666 /dev/full -``` - -## Files - -* /dev/full - -## Examples - -```sh -$ head -c 8 /dev/full | hexdump -00 00 00 00 00 00 00 00 -``` - -## See also - -* [`null`(4)](help://man/4/null) -* [`zero`(4)](help://man/4/zero) - diff --git a/Base/usr/share/man/man4/ipc.md b/Base/usr/share/man/man4/ipc.md deleted file mode 100644 index f98d013b22e..00000000000 --- a/Base/usr/share/man/man4/ipc.md +++ /dev/null @@ -1,23 +0,0 @@ -## Name - -ipc - Unix socket Inter Process Communication protocols - -## Synopsis - -IPC endpoints are provided as Unix sockets in `/tmp/portal`. All services have their own formats, automatically implemented through LibIPC. - -## Description - -The specifics of each service's format depend on the corresponding source `.ipc` file. - -The format can be identified by the format magic, which is derived in [`Meta/Lagom/Tools/CodeGenerators/IPCCompiler/main.cpp`](../../../../../Meta/Lagom/Tools/CodeGenerators/IPCCompiler/main.cpp) from the service-endpoint name, e.g. "ClipboardClient" (which hashes to 4008793515) or "ClipboardServer" (which hashes to 1329211611). - -In general, communication works by packets, which might have been sent in response to other packets. Everything is host endianness. Each packet consists of: -- a 32-bit message size (see `Connection::try_parse_messages` in [`Userland/Libraries/LibIPC/Connection.h`](../../../../../Userland/Libraries/LibIPC/Connection.h)) -- the 32-bit endpoint magic (note that responses use the endpoint of the requesting packet, so the Clipboard server might use the endpoint magic 4008793515 to signal that this packet is a response) -- the 32-bit message ID within that endpoint (sequentially assigned, starting at 1) -- the data of that message itself (e.g. see `Messages::ClipboardServer::SetClipboardData::{en,de}code` in `Build/*/Userland/Services/Clipboard/ClipboardServerEndpoint.h`). - -## See Also - -- [ipc(5)](help://man/5/ipc) (IPC file format documentation) diff --git a/Base/usr/share/man/man4/mem.md b/Base/usr/share/man/man4/mem.md deleted file mode 100644 index 89288fe5fb6..00000000000 --- a/Base/usr/share/man/man4/mem.md +++ /dev/null @@ -1,31 +0,0 @@ -## Name - -mem - physical system memory - -## Description - -`/dev/mem` is a character device file that is used by other programs to examine -the physical memory. - -Trying to [`mmap`(2)](help://man/2/mmap) a physical range results either with success, -or with an error. When invoking [`mmap`(2)](help://man/2/mmap) on bad memory range, -the kernel will write a message about it to the kernel log. - -By default, the kernel limits the areas which can be accessed. The allowed areas -are the reserved ranges in physical memory, essentially limiting the access to -ROMs and memory-mapped PCI regions on x86. - -To create it manually: -```sh -mknod /dev/mem c 1 1 -chmod 660 /dev/mem -``` - -## Returned error values after [`mmap`(2)](help://man/2/mmap) - -* `EINVAL`: An access violation was detected. -* `ENOMEM`: The requested range would wrap around, creating an access violation. - -## See also - -* [`mmap`(2)](help://man/2/mmap) diff --git a/Base/usr/share/man/man4/null.md b/Base/usr/share/man/man4/null.md deleted file mode 100644 index 6263c358dee..00000000000 --- a/Base/usr/share/man/man4/null.md +++ /dev/null @@ -1,19 +0,0 @@ -## Name - -null - data sink - -## Description - -`/dev/null` is a character device file which discards input. - -Reading from `/dev/null` returns end of file and exits with status 0. - -## Files - -* /dev/null - -## See also - -* [`full`(4)](help://man/4/full) -* [`zero`(4)](help://man/4/zero) - diff --git a/Base/usr/share/man/man4/zero.md b/Base/usr/share/man/man4/zero.md deleted file mode 100644 index e1ea844cbc2..00000000000 --- a/Base/usr/share/man/man4/zero.md +++ /dev/null @@ -1,26 +0,0 @@ -## Name - -zero - data sink - -## Description - -`/dev/zero` is a character device file which discards input. - -Reading from `/dev/zero` returns '\0' bytes and exits with status 0. - -## Files - -* /dev/zero - -## Examples - -```sh -$ head -c 8 /dev/zero | hexdump -00 00 00 00 00 00 00 00 -``` - -## See also - -* [`null`(4)](help://man/4/null) -* [`full`(4)](help://man/4/full) - diff --git a/Base/usr/share/man/man5/GML.md b/Base/usr/share/man/man5/GML.md deleted file mode 100644 index dba8cc58e48..00000000000 --- a/Base/usr/share/man/man5/GML.md +++ /dev/null @@ -1,81 +0,0 @@ -## Name - -GUI Markup Language (GML) - -## Description - -GML is Serenity's graphic user interface (GUI) markup language. GML files are human-readable text files and have no easily detectable filemagic. The format is strongly influenced by QML, the Qt Modeling Language. - -It allows you to easily define GUI interfaces for your applications. It is easy to learn and use in C++. - -You can easily add GML files to your project in Hack Studio either using - -`Project > New > GML File` - -Or right clicking on a folder in the TreeView and using - -`New > GML File` - -## See also - -- [gml-format(1)](help://man/1/gml-format) formats your GML files. -- [Applications/GML Playground(1)](help://man/1/Applications/GMLPlayground) is an interactive GML creation tool. - -### List of GML manpages - -- [Using GML](help://man/5/GML/Usage) -- [GML syntax](help://man/5/GML/Syntax) -- Extending GML - - [Define properties](help://man/5/GML/Define-property) - - [Define widgets](help://man/5/GML/Define-widget) -- GML object and property reference - - [Core::Object](help://man/5/GML/CoreObject) - - [UI Dimensions](help://man/5/GML/UI-Dimensions) - - Layouts - - [HorizontalBoxLayout](help://man/5/GML/Layout-HorizontalBoxLayout) - - [VerticalBoxLayout](help://man/5/GML/Layout-VerticalBoxLayout) - - Widgets - - [Breadcrumbbar](help://man/5/GML/Widget/Breadcrumbbar) - - [Button](help://man/5/GML/Widget/Button) - - [Calendar](help://man/5/GML/Widget/Calendar) - - [CheckBox](help://man/5/GML/Widget/CheckBox) - - [ColorInput](help://man/5/GML/Widget/ColorInput) - - [ComboBox](help://man/5/GML/Widget/ComboBox) - - [DynamicWidgetContainer](help://man/5/GML/Widget/DynamicWidgetContainer) - - [Frame](help://man/5/GML/Widget/Frame) - - [GroupBox](help://man/5/GML/Widget/GroupBox) - - [HorizontalProgressbar](help://man/5/GML/Widget/HorizontalProgressbar) - - [HorizontalSeparator](help://man/5/GML/Widget/HorizontalSeparator) - - [HorizontalSlider](help://man/5/GML/Widget/HorizontalSlider) - - [HorizontalSplitter](help://man/5/GML/Widget/HorizontalSplitter) - - [IconView](help://man/5/GML/Widget/IconView) - - [ImageWidget](help://man/5/GML/Widget/ImageWidget) - - [Label](help://man/5/GML/Widget/Label) - - [LinkLabel](help://man/5/GML/Widget/LinkLabel) - - [ListView](help://man/5/GML/Widget/ListView) - - [MultiView](help://man/5/GML/Widget/MultiView) - - [OpacitySlider](help://man/5/GML/Widget/OpacitySlider) - - [PasswordBox](help://man/5/GML/Widget/PasswordBox) - - [Progressbar](help://man/5/GML/Widget/Progressbar) - - [RadioButton](help://man/5/GML/Widget/RadioButton) - - [ScrollableContainerWidget](help://man/5/GML/Widget/ScrollableContainerWidget) - - [Scrollbar](help://man/5/GML/Widget/Scrollbar) - - [Slider](help://man/5/GML/Widget/Slider) - - [SpinBox](help://man/5/GML/Widget/SpinBox) - - [StackWidget](help://man/5/GML/Widget/StackWidget) - - [Statusbar](help://man/5/GML/Widget/Statusbar) - - [TableView](help://man/5/GML/Widget/TableView) - - [TabWidget](help://man/5/GML/Widget/TabWidget) - - [TextBox](help://man/5/GML/Widget/TextBox) - - [TextEditor](help://man/5/GML/Widget/TextEditor) - - [Toolbar](help://man/5/GML/Widget/Toolbar) - - [ToolbarContainer](help://man/5/GML/Widget/ToolbarContainer) - - [Tray](help://man/5/GML/Widget/Tray) - - [TreeView](help://man/5/GML/Widget/TreeView) - - [UrlBox](help://man/5/GML/Widget/UrlBox) - - [ValueSlider](help://man/5/GML/Widget/ValueSlider) - - [VerticalProgressbar](help://man/5/GML/Widget/VerticalProgressbar) - - [VerticalSeparator](help://man/5/GML/Widget/VerticalSeparator) - - [VerticalSlider](help://man/5/GML/Widget/VerticalSlider) - - [VerticalSplitter](help://man/5/GML/Widget/VerticalSplitter) - - [Widget](help://man/5/GML/Widget) diff --git a/Base/usr/share/man/man5/GML/CoreObject.md b/Base/usr/share/man/man5/GML/CoreObject.md deleted file mode 100644 index 53e4ada7313..00000000000 --- a/Base/usr/share/man/man5/GML/CoreObject.md +++ /dev/null @@ -1,14 +0,0 @@ -## Name - -A brief description of Core::Object and how it relates to GML. - -## Description - -All GML widgets inherit properties from `Core::Object`. - -## Registered Properties - -| Property | Type | Possible values | Description | -|-------------|-----------------|-----------------|------------------------------------------------| -| name | string | Any string | Name of widget, to be referenced later in C++. | -| class_name | readonly_string | Class name | Object class | diff --git a/Base/usr/share/man/man5/GML/Define-property.md b/Base/usr/share/man/man5/GML/Define-property.md deleted file mode 100644 index 2174c691b2d..00000000000 --- a/Base/usr/share/man/man5/GML/Define-property.md +++ /dev/null @@ -1,40 +0,0 @@ -## Name - -GML Property Definition - -## Description - -How to register property to a widget. - -**LibGUI** widget definitions contain macros that define the properties that can be used for a given widget. This is a useful feature to expose widget-type-specific configuration to GML. - -Widgets understand all properties defined by their parents. Such as `x`, `y`, `name`, etc. - -## `REGISTER_*` macros - -There is one REGISTER macro for every generic property type, a list of which can be found in [GML Syntax(5)](help://man/5/GML/Syntax#Properties). If you need special behavior for one single property, it is usually enough to re-use one of the property types and do extra handling in the setter and getter. - -The general syntax of the macros is as follows: - -```cpp -REGISTER_TYPENAME_PROPERTY(property_name, getter, setter [, additional parameters...]); -``` - -The macros are to be called in the constructor. `property_name` is a string that GML can use to set this property. `getter` is the name of a member function on this class that acts as a getter for the property. The getter receives value of the property's type, which is a C++ value for `int`, `string`, `readonly_string`, `enum`, and `bool`, or a JSON value for anything else. `setter` is the name of a member function on this class that acts as a setter for the property, taking no arguments and returning the exact type that the getter expects. It is not possible to omit either of the functions, but they may be no-ops if needed. For non-settable strings `REGISTER_READONLY_STRING_PROPERTY` is used. - -For some macros, additional parameters are required. Most of the macros and their parameters can be found in the `Core::Object` header; however some layout properties are situated in the `GUI::Layout` header. - -## Examples - -```cpp -REGISTER_STRING_PROPERTY("alt_text", alt_text, set_alt_text); -// The enum property requires that you specify all available enum fields plus their GML string representation. -REGISTER_ENUM_PROPERTY( - "button_style", button_style, set_button_style, Gfx::ButtonStyle, - { Gfx::ButtonStyle::Normal, "Normal" }, - { Gfx::ButtonStyle::Coolbar, "Coolbar" }); -``` - -## See also - -- [GML Define widget(5)](help://man/5/GML/Define-widget) diff --git a/Base/usr/share/man/man5/GML/Define-widget.md b/Base/usr/share/man/man5/GML/Define-widget.md deleted file mode 100644 index 692c226e75f..00000000000 --- a/Base/usr/share/man/man5/GML/Define-widget.md +++ /dev/null @@ -1,50 +0,0 @@ -## Name - -Library or Application Defined Widgets - -## Description - -Some applications and libraries find it useful to define their own widgets. - -This is done using `REGISTER_WIDGET()`, just as in **LibGUI**. The syntax of the macro is as follows: - -```cpp -REGISTER_WIDGET(namespace, class_name) -``` - -This means that every registered widget has to be placed in a namespace. For applications that usually do not need their own namespace, a common approach is to use the application name as the namespace for these registered widgets. - -Note that registered widgets need to be constructible without any arguments. - -## Examples - -```gml -@Web::OutOfProcessWebView { - name: "web_view" - min_width: 340 - min_height: 160 - visible: false -} -``` - -```cpp -// OutOfProcessWebView.cpp - -REGISTER_WIDGET(Web, OutOfProcessWebView) - -... - -OutOfProcessWebView::OutOfProcessWebView() -{ - set_should_hide_unnecessary_scrollbars(true); - set_focus_policy(GUI::FocusPolicy::StrongFocus); - - create_client(); -} - -... -``` - -## See also - -- [GML Define property(5)](help://man/5/GML/Define-property) diff --git a/Base/usr/share/man/man5/GML/Layout-HorizontalBoxLayout.md b/Base/usr/share/man/man5/GML/Layout-HorizontalBoxLayout.md deleted file mode 100644 index 80e4d2deec9..00000000000 --- a/Base/usr/share/man/man5/GML/Layout-HorizontalBoxLayout.md +++ /dev/null @@ -1,21 +0,0 @@ -## Name - -GML Horizontal Box Layout - -## Description - -Defines a horizontal GUI box layout. This is a layout object that lays out widgets side-by-side horizontally. - -## Examples - -```gml -@GUI::Widget { - layout: @GUI::HorizontalBoxLayout { - spacing: 2 - } -} -``` - -## See also - -- [Layout](help://man/5/GML/Layout) diff --git a/Base/usr/share/man/man5/GML/Layout-VerticalBoxLayout.md b/Base/usr/share/man/man5/GML/Layout-VerticalBoxLayout.md deleted file mode 100644 index 122e7d6d5a6..00000000000 --- a/Base/usr/share/man/man5/GML/Layout-VerticalBoxLayout.md +++ /dev/null @@ -1,21 +0,0 @@ -## Name - -GML Vertical Box Layout - -## Description - -Defines a vertical GUI box layout. This is a layout object that lays out widgets side-by-side vertically. - -## Examples - -```gml -@GUI::Widget { - layout: @GUI::VerticalBoxLayout { - spacing: 2 - } -} -``` - -## See also - -- [Layout](help://man/5/GML/Layout) diff --git a/Base/usr/share/man/man5/GML/Layout.md b/Base/usr/share/man/man5/GML/Layout.md deleted file mode 100644 index aa635d557bd..00000000000 --- a/Base/usr/share/man/man5/GML/Layout.md +++ /dev/null @@ -1,19 +0,0 @@ -## Name - -GML Layout object - -## Description - -Abstract base class for layout objects, not used directly. Common properties of all layout objects are defined here. - -## Registered properties - -| Property | Type | Possible values | Description | -| -------- | ------- | --------------- | --------------------------------------------------- | -| margins | margins | | Margins inside the container widget to the children | -| spacing | int | | Spacing around each widget in the layout | - -## See also - -- [HorizontalBoxLayout](help://man/5/GML/Layout-HorizontalBoxLayout) -- [VerticalBoxLayout](help://man/5/GML/Layout-VerticalBoxLayout) diff --git a/Base/usr/share/man/man5/GML/Syntax.md b/Base/usr/share/man/man5/GML/Syntax.md deleted file mode 100644 index bf24e7f7bad..00000000000 --- a/Base/usr/share/man/man5/GML/Syntax.md +++ /dev/null @@ -1,232 +0,0 @@ -## Name - -GML Syntax - -# Description - -Overview of the GML syntax. - -## Basic Syntax - -For a start, GML can be thought of as a QML (Qt Markup Language) derivative. JSON is used as a sub-language in some places. - -Each widget (or `Core::Object`, more generally) begins with `@`, with the name of the widget following. As this name refers to an actual C++ class, we need to include namespaces and separate them with `::` as usual. To define the properties of this widget, we follow the object name with curly brackets and a list of properties. - -```gml -// The base widget class, straight from LibGUI. -@GUI::Widget { - -} -// Some other common classes: -@GUI::HorizontalBoxLayout { - -} -// (for compactness) -@GUI::Button {} -@GUI::Label {} -@GUI::TabWidget {} - -// If your application-specific widget is registered, you can do this: -@MyApplication::CoolWidget { - -} -``` - -As seen above, C-style single line comments with `//` are possible. - -Inside an object, we declare the properties of the object as well as all of its children. The children are other widgets, specified plainly, while the properties take the form `key: value`. For almost all properties, the value is a JSON value, and each property expects a different kind of value. The documentation for each widget object contains information about the supported properties, their possible values, and what the properties do. Quite some properties are common to all widgets, see [GML/Widget(5)](help://man/5/GML/Widget). - -```gml -// A 20x200 coolbar button with text. -@GUI::Button { - width: 200 - height: 20 - text: "Operation failed successfully." - button_style: "Coolbar" -} - -// Two tabs, named "Tab 1" and "Tab 2", each containing a label. -@GUI::TabWidget { - min_width: 150 - min_height: 200 - - @GUI::Label { - title: "Tab 1" - text: "This is the first tab" - } - - @GUI::Label { - title: "Tab 2" - text: "This is the second tab. What did you expect?" - } -} -``` - -For layouting purposes, we use the special property `layout` which takes a layout object instead of a JSON value. The exact positioning of the children not only depends on the layout, but also on the container widget type. A widget's documentation contains information about such special cases if they exist. - -```gml -// Make a frame that has two buttons horizontally laid out. -@GUI::Frame { - min_height: 16 - min_width: 128 - - layout: @GUI::HorizontalBoxLayout { - // margin and spacing are frequent layouting properties. - spacing: 5 - } - @GUI::Button { - text: "I'm the left button..." - } - @GUI::Button { - text: "...and I'm the right button!" - } -} -``` - -## Properties - -A property's `value` is required to be either a JSON value or another object. Objects are only used for a few special properties which are documented with the widgets that need them. - -Among the supported JSON values, these types can be further distinguished: - -- `int`: Regular JSON integer, note that JSON floats are not currently used. -- `ui_dimension`: either positive integers, that work just like `int`, or special meaning values as JSON strings; see [UI Dimensions](help://man/5/GML/UI-Dimensions) -- `bool`: Regular JSON boolean, may be enclosed in quotes but this is discouraged. -- `string`: JSON string, also used as a basis for other types. -- `readonly_string`: String-valued property that cannot be changed from C++ later. -- `enum`: A JSON string with special semantics. This is always the value of some C++ enum, but it's enclosed in quotes. - - `font_weight`: Special case of `enum` for `Gfx::FontWeight`. One of `Thin`, `ExtraLight`, `Light`, `Regular`, `Medium`, `SemiBold`, `Bold`, `ExtraBold`, `Black`, `ExtraBlack`. - - `text_alignment`: Special case of `enum` for `Gfx::TextAlignment`. Supports values of the form `VerticalHorizontal`, where `Vertical` is the vertical alignment, one of `Center`, `Top`, `Bottom`, and `Horizontal` is the horizontal alignment, one of `Left`, `Right`, `Center`. The exception is the value `Center` (because `CenterCenter` is silly). - - `text_wrapping`: Special case of `enum` for `Gfx::TextWrapping`. One of `Wrap` or `DontWrap`. -- `rect`: A JSON object of four `int`s specifying a rectangle. The keys are `x`, `y`, `width`, `height`. -- `size`: A JSON array of two `int`s specifying two sizes in the format `[width, height]`. -- `ui_size`: A JSON array of two `ui_dimension`s specifying two sizes in the format `[width, height]` -- `margins`: A JSON array or object specifying four-directional margins as `int`s. If this is a JSON object, the four keys `top`, `right`, `bottom`, `left` are used. If this is a JSON array, there can be one to four integers. These have the following meaning (see also `GUI::Margins`): - - `[ all_four_margins ]` - - `[ top_and_bottom, right_and_left ]` - - `[ top, right_and_left, bottom ]` - - `[ top, right, bottom, left ]` - -Properties are never ended with `;` or `,`, and the property key is never enclosed in quotes or double quotes. - -## See also - -- The SerenityOS source code is the best source of further information on GML. The GML parser and AST builder can be found in the `GML` subdirectory in the `LibGUI` library. The `AK` JSON parsers and data structures are used for all JSON values. - -## Examples - -GML files can be found in the SerenityOS source tree with the `*.gml` extension. - -```gml -@GUI::Widget { - name: "my_first_widget" -} -``` - -```gml -// A Browser window GML -@GUI::Widget { - name: "browser" - fill_with_background_color: true - layout: @GUI::VerticalBoxLayout {} - - @GUI::HorizontalSeparator { - name: "top_line" - fixed_height: 2 - visible: false - } - - @GUI::TabWidget { - name: "tab_widget" - container_margins: [0] - uniform_tabs: true - text_alignment: "CenterLeft" - } -} -``` - -```gml -// A SystemMonitor GML (abbreviated) -// This makes use of quite some custom objects and properties. -@GUI::Widget { - fill_with_background_color: true - layout: @GUI::VerticalBoxLayout {} - - @GUI::Widget { - layout: @GUI::VerticalBoxLayout { - margins: [0, 4, 4] - } - - @GUI::TabWidget { - name: "main_tabs" - - @GUI::Widget { - title: "Processes" - name: "processes" - - @GUI::TableView { - name: "process_table" - column_headers_visible: true - } - } - - @GUI::Widget { - title: "Performance" - name: "performance" - background_role: "Button" - fill_with_background_color: true - layout: @GUI::VerticalBoxLayout { - margins: [4] - } - - @GUI::GroupBox { - title: "CPU usage" - name: "cpu_graph" - layout: @GUI::VerticalBoxLayout {} - } - - @GUI::GroupBox { - title: "Memory usage" - fixed_height: 120 - layout: @GUI::VerticalBoxLayout { - margins: [6] - } - - @SystemMonitor::GraphWidget { - stack_values: true - name: "memory_graph" - } - } - - @SystemMonitor::MemoryStatsWidget { - name: "memory_stats" - // A custom property that refers back up to the GraphWidget for the memory graph. - memory_graph: "memory_graph" - } - } - - @SystemMonitor::StorageTabWidget { - title: "Storage" - name: "storage" - layout: @GUI::VerticalBoxLayout { - margins: [4] - } - - @GUI::TableView { - name: "storage_table" - } - } - - @SystemMonitor::NetworkStatisticsWidget { - title: "Network" - name: "network" - } - } - } - - @GUI::Statusbar { - segment_count: 3 - name: "statusbar" - } -} -``` diff --git a/Base/usr/share/man/man5/GML/UI-Dimensions.md b/Base/usr/share/man/man5/GML/UI-Dimensions.md deleted file mode 100644 index b89921f55e3..00000000000 --- a/Base/usr/share/man/man5/GML/UI-Dimensions.md +++ /dev/null @@ -1,29 +0,0 @@ -## Name - -UI Dimensions - -# Description - -UI Dimension (or [GUI::UIDimension](file:///usr/src/serenity/Userland/Libraries/LibGUI/UIDimensions.h) in c++) is a special, union — of positive integer and enum — like, type that is used to represent dimensions in a user interface context. - -It can either store positive integers ≥0 or special meaning values from a pre determined set. - -## Basic Syntax - -In GML UI Dimensions that are "regular" values (integer ≥0) are represented by JSON's int type, -while "special" values are represented by their name as a JSON string type. - -# Special Values - -Special Values carry size information that would otherwise not be intuitively possible to be transported by an integer (positive or negative) alone. - -Importantly, while any "regular" (i.e. int ≥0) UI Dimension values might (by convention) be assigned to any UI Dimension property, many properties only allow a subset of the "special" values to be assigned to them. - -| Name | c++ name | GML/JSON representation | General meaning | -|-------------------|--------------------------------------------|-------------------------|-------------------------------------------------| -| Regular | `GUI::SpecialDimension::Regular` (mock) | int ≥0 | This is a regular integer value specifying a specific size | -| Grow | `GUI::SpecialDimension::Grow` | `"grow"` | Grow to the maximum size the surrounding allows | -| OpportunisticGrow | `GUI::SpecialDimension::OpportunisticGrow` | `"opportunistic_grow"` | Grow when the opportunity arises, meaning — only when all other widgets have already grown to their maximum size, and only opportunistically growing widgets are left | -| Fit | `GUI::SpecialDimension::Fit` | `"fit"` | Grow exactly to the size of the surrounding as determined by other factors, but do not call for e.g. expansion of the parent container itself | -| Shrink | `GUI::SpecialDimension::Shrink` | `"shrink"` | Shrink to the smallest size possible | - diff --git a/Base/usr/share/man/man5/GML/Usage.md b/Base/usr/share/man/man5/GML/Usage.md deleted file mode 100644 index 9d23f0e8e95..00000000000 --- a/Base/usr/share/man/man5/GML/Usage.md +++ /dev/null @@ -1,65 +0,0 @@ -## Name - -GML Usage - -## Description - -How to use GML in SerenityOS C++ applications - -## Note - -This manpage describes the new method of loading GML files via generated C++ code. There also is the runtime `load_from_gml` function which has the same behavior. The runtime method should not be used except for specific use cases such as [GMLPlayground](help://man/1/Applications/GMLPlayground). - -## CMake - -Include `compile_gml()` your application's CMake file. The generated source file name is not fixed, but should follow this convention. - -```cmake -compile_gml(MyApp.gml MyAppGML.cpp) -``` - -Include the name of the source file that will be compiled from your GML file in your `SOURCES`. - -```cmake -set(SOURCES - MyAppGML.cpp -) -``` - -## C++ - -The C++ source file that is generated will provide an implementation for the `ErrorOr<NonnullRefPtr<MyApp::Widget>> MyApp::Widget::try_create()` function, given that the root widget of the GML file is `MyApp::Widget`. For this to work, you have to add a declaration for this function in the header of the `MyApp::Widget` class. (The function will not collide with functions provided by `Core::Object`, do not worry.) Additionally, there must be a no-argument constructor in `MyApp::Widget`, which is used by the `try_create` implementation. - -Calling the `try_create` function directly or indirectly (e.g. `Window::try_set_main_widget`) will now automatically load the structure defined in GML. - -From there, you can use `find_descendant_of_type_named` to select widgets from your GML by their `name` property. - -```gml -@MyApp::Widget { - @GUI::Button { - name: "mem_add_button" - text: "M+" - fixed_width: 35 - fixed_height: 28 - foreground_color: "red" - } -} -``` - -```cpp -// MyApp::Widget::foo_bar -m_mem_add_button = *find_descendant_of_type_named<GUI::Button>("mem_add_button"); -``` - -### `initialize` Pattern - -Initialization, like adding models, attaching callbacks, etc., should be done in a member function with the signature: - -```cpp -// MyApp::Widget -ErrorOr<void> initialize(); -``` - -This initializer function, if it exists, will automatically be called after the structure of your widget was set up by the auto-generated GML code. - -The only case where this function cannot be used is when your initialization requires additional parameters, like a GUI window. You may still consider moving as much initialization to the canonical function as possible. diff --git a/Base/usr/share/man/man5/GML/Widget.md b/Base/usr/share/man/man5/GML/Widget.md deleted file mode 100644 index da3c2ae208c..00000000000 --- a/Base/usr/share/man/man5/GML/Widget.md +++ /dev/null @@ -1,54 +0,0 @@ -# Widget - -Defines a GUI widget. - -```gml -@GUI::Widget { - min_width: 200 - preferred_width: "opportunistic_grow" - fixed_height: 215 - fill_with_background_color: true - layout: @GUI::VerticalBoxLayout { - - } -} -``` - -## Registered Properties - -| Property | Type | Possible values | Description | -| --------------------------- | ------------- | ----------------------------------------------------- | -------------------------------------------------------------------------------------------------- | -| x | int | | x offset relative to parent | -| y | int | | y offset relative to parent | -| visible | bool | | Whether widget and children are drawn | -| focused | bool | | Whether widget should be tab-focused on start | -| focus_policy | enum | ClickFocus, NoFocus, TabFocus, StrongFocus | How the widget can receive focus | -| enabled | bool | | Whether this widget is enabled for interactive purposes, e.g. can be clicked | -| tooltip | string | | Mouse tooltip to show when hovering over this widget | -| min_size | ui_size | {Regular, Shrink}² | Minimum size this widget wants to occupy (Shrink is equivalent to 0) | -| max_size | ui_size | {Regular, Grow}² | Maximum size this widget wants to occupy | -| preferred_size | ui_size | {Regular, Shrink, Fit, OpportunisticGrow, Grow}² | Preferred size this widget wants to occupy, if not otherwise constrained (Shrink means min_size) | -| width | int | | Width of the widget, independent of its layout size calculation | -| height | int | | Height of the widget, independent of its layout size calculation | -| min_width | ui_dimension | Regular, Shrink | Minimum width this widget wants to occupy (Shrink is equivalent to 0) | -| min_height | ui_dimension | Regular, Shrink | Minimum height this widget wants to occupy (Shrink is equivalent to 0 | -| max_width | ui_dimension | Regular, Grow | Maximum width this widget wants to occupy | -| max_height | ui_dimension | Regular, Grow | Maximum height this widget wants to occupy | -| preferred_width | ui_dimension | Regular, Shrink, Fit, OpportunisticGrow, Grow | Preferred width this widget wants to occupy, if not otherwise constrained (Shrink means min_size) | -| preferred_height | ui_dimension | Regular, Shrink, Fit, OpportunisticGrow, Grow | Preferred height this widget wants to occupy, if not otherwise constrained (Shrink means min_size) | -| fixed_width | ui_dimension | Regular (currently only integer values ≥0 allowed) | Both maximum and minimum width; widget is fixed-width | -| fixed_height | ui_dimension | Regular (currently only integer values ≥0 allowed) | Both maximum and minimum height; widget is fixed-height | -| fixed_size | ui_size | {Regular}² | Both maximum and minimum size; widget is fixed-size | -| shrink_to_fit | bool | | Whether the widget shrinks as much as possible while still respecting its children's minimum sizes | -| font | string | Any system-known font | Font family | -| font_size | int | Font size that is available on this family | Font size | -| font_weight | font_weight | Font weight that is available on this family and size | Font weight | -| font_type | enum | FixedWidth, Normal | Font type | -| foreground_color | color | | Color of foreground elements such as text | -| foreground_role | string | Any theme palette color role name | Palette color role (depends on system theme) for the foreground elements | -| background_color | color | | Color of the widget background | -| background_role | string | Any theme palette color role name | Palette color role (depends on system theme) for the widget background | -| fill_width_background_color | bool | | Whether to fill the widget's background with the background color | -| layout | layout object | | Layout object for layouting this widget's children | -| relative_rect | rect | | Rectangle for relatively positioning the widget to the parent | - diff --git a/Base/usr/share/man/man5/GML/Widget/Breadcrumbbar.md b/Base/usr/share/man/man5/GML/Widget/Breadcrumbbar.md deleted file mode 100644 index 8ef8b8d2041..00000000000 --- a/Base/usr/share/man/man5/GML/Widget/Breadcrumbbar.md +++ /dev/null @@ -1,19 +0,0 @@ -## Name - -GML Breadcrumb Bar Widget - -## Description - -Defines a GUI bread crumb toolbar widget. - -## Synopsis - -`@GUI::Breadcrumbbar` - -## Examples - -```gml -@GUI::Breadcrumbbar { - name: "breadcrumbbar" -} -``` diff --git a/Base/usr/share/man/man5/GML/Widget/Button.md b/Base/usr/share/man/man5/GML/Widget/Button.md deleted file mode 100644 index 05964f95756..00000000000 --- a/Base/usr/share/man/man5/GML/Widget/Button.md +++ /dev/null @@ -1,34 +0,0 @@ -## Name - -## Description - -Defines a GUI Button widget. - -## Synopsis - -`@GUI::Button` - -## Examples - -```gml -@GUI::Button { - name: "normal_button" - text: "Button" -} - -@GUI::Button { - name: "disabled_normal_button" - text: "Disabled" - enabled: false -} -``` - -## Registered Properties - -| Property | Type | Possible values | Description | -| ------------ | ------ | --------------- | ----------------------------------------------------------------------------------------------------- | -| button_style | enum | Normal, Coolbar | Sets the style of the button | -| text | string | Any string | Sets the label text | -| checked | bool | true or false | Whether the button is checked; this only applies to checkable subclasses | -| checkable | bool | true or false | Whether the button can be checked; this only applies to checkable subclasses | -| exclusive | bool | true or false | Whether the button's check state is exclusive to its group; this only applies to checkable subclasses | diff --git a/Base/usr/share/man/man5/GML/Widget/Calendar.md b/Base/usr/share/man/man5/GML/Widget/Calendar.md deleted file mode 100644 index 301af8bff04..00000000000 --- a/Base/usr/share/man/man5/GML/Widget/Calendar.md +++ /dev/null @@ -1,19 +0,0 @@ -## Name - -GML Calendar Widget - -## Description - -Defines a GUI calendar widget. - -## Synopsis - -`@GUI::Calendar` - -## Examples - -```gml -@GUI::Calendar { - name: "calendar" -} -``` diff --git a/Base/usr/share/man/man5/GML/Widget/CheckBox.md b/Base/usr/share/man/man5/GML/Widget/CheckBox.md deleted file mode 100644 index abe4b201f25..00000000000 --- a/Base/usr/share/man/man5/GML/Widget/CheckBox.md +++ /dev/null @@ -1,36 +0,0 @@ -## Name - -GML Checkbox Widget - -## Description - -Defines a GUI checkbox widget. - -## Synopsis - -`@GUI::CheckBox` - -## Examples - -```gml -@GUI::CheckBox { - name: "top_checkbox" - text: "Checkbox" -} - -@GUI::CheckBox { - name: "bottom_checkbox" - text: "Disabled" - enabled: false -} -``` - -## Registered Properties - -| Property | Type | Possible values | Description | -|-------------------|--------|-------------------|--------------------------------------------------------------------| -| autosize | bool | true or false | Determines if auto-sized | -| checkbox_position | String | "Left" or "Right" | Place the checkbox itself on either the left or right of its label | - -## See also -- [GML Button](help://man/5/GML/Widget/Button) diff --git a/Base/usr/share/man/man5/GML/Widget/ColorInput.md b/Base/usr/share/man/man5/GML/Widget/ColorInput.md deleted file mode 100644 index 4bed8ca95dc..00000000000 --- a/Base/usr/share/man/man5/GML/Widget/ColorInput.md +++ /dev/null @@ -1,32 +0,0 @@ -## Name - -GML Color Input Widget - -## Description - -Defines a GUI color input widget. - -## Synopsis - -`@GUI::ColorInput` - -## Examples - -```gml -@GUI::ColorInput { - name: "font_colorinput" - placeholder: "Color dialog" -} - -@GUI::ColorInput { - placeholder: "Disabled" - enabled: false -} -``` - -## Registered Properties - -| Property | Type | Possible values | Description | -|--------------------|--------|-----------------|---------------------------------------------------------| -| color_picker_title | string | Any string | Sets the title of the input | -| has_alpha_channel | bool | true or false | Whether to include the alpha channel in color selection | diff --git a/Base/usr/share/man/man5/GML/Widget/ComboBox.md b/Base/usr/share/man/man5/GML/Widget/ComboBox.md deleted file mode 100644 index 513cdd8dd8c..00000000000 --- a/Base/usr/share/man/man5/GML/Widget/ComboBox.md +++ /dev/null @@ -1,32 +0,0 @@ -## Name - -GML Combo Box Widget - -## Description - -Defines a GUI combo box widget. Another name for this would be a dropdown or select. - -## Synopsis - -`@GUI::ComboBox` - -## Examples - -```gml -@GUI::ComboBox { - name: "msgbox_icon_combobox" - model_only: true -} - -@GUI::ComboBox { - name: "msgbox_buttons_combobox" - model_only: true -} -``` - -## Registered Properties - -| Property | Type | Possible values | Description | -|-------------|--------|-----------------|------------------------------| -| placeholder | string | Any string | Editor placeholder | -| model_only | bool | true or false | Only allow values from model | diff --git a/Base/usr/share/man/man5/GML/Widget/DynamicWidgetContainer.md b/Base/usr/share/man/man5/GML/Widget/DynamicWidgetContainer.md deleted file mode 100644 index 43d11f15da1..00000000000 --- a/Base/usr/share/man/man5/GML/Widget/DynamicWidgetContainer.md +++ /dev/null @@ -1,67 +0,0 @@ -## Name - -GML DynamicWidgetContainer - -## Description - -Defines a container widget that will group its child widgets together so that they can be collapsed, expanded or detached to a new window as one unit. If DynamicWidgetContainers are nested within one DynamicWidgetContainer it is possible to move the positions of the child containers dynamically. - -| Property | Description | -| --------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------- | -| config_domain | Defines if the changes to the widget's view state should be persisted. It is required that the domain has been already pleged by the application. | -| detached_size | Defines a size that the detached widget window should initially have. If not defined, the window will have the current size of the widget. | -| section_label | The label that will be used for the section. | -| show_controls | Defines if the buttons and label should be visible or not. This allows e.g. a parent container to hide its controls but provide rearrenage functionality. | -| with_individual_order | Configured on a parent container to enable the persistence of rearranged child containers. | - -## Synopsis - -`@GUI::DynamicWidgetContainer` - -## Examples - -Simple container: -```gml -@GUI::DynamicWidgetContainer { - section_label: "Section 1" - - @GUI::Widget { - } - - @GUI::Widget { - } -} -``` - -Nested containers with persistence: - -```gml -@GUI::DynamicWidgetContainer { - section_label: "Parent Section" - config_domain: "abc" - with_individual_order: true - detached_size: [200, 640] - - @GUI::DynamicWidgetContainer { - section_label: "Section 1" - config_domain: "abc" - - @GUI::Widget { - } - - @GUI::Widget { - } - } - - @GUI::DynamicWidgetContainer { - section_label: "Section 2" - config_domain: "abc" - - @GUI::Widget { - } - - @GUI::Widget { - } - } -} -``` diff --git a/Base/usr/share/man/man5/GML/Widget/Frame.md b/Base/usr/share/man/man5/GML/Widget/Frame.md deleted file mode 100644 index da66e93a271..00000000000 --- a/Base/usr/share/man/man5/GML/Widget/Frame.md +++ /dev/null @@ -1,32 +0,0 @@ -## Name - -GML Frame Widget - -## Description - -Defines a GUI frame widget. Frames can contain other GUI widgets. - -## Synopsis - -`@GUI::Frame` - -## Examples - -```gml -@GUI::Frame { - name: "tip_frame" - min_width: 340 - min_height: 160 - layout: @GUI::HorizontalBoxLayout { - margins: [0, 16, 0, 0] - } -} -``` - -## Registered Properties - -| Property | Type | Possible values | Description | -|-----------|---------|--------------------------------------------------------------|--------------------| -| thickness | integer | 64-bit integer | Sets the thickness | -| shadow | enum | Plain, Raised, Sunken | Sets the shadow | -| shape | enum | NoFrame, Box, Container, Panel, VerticalLine, HorizontalLine | Sets the shape | diff --git a/Base/usr/share/man/man5/GML/Widget/GroupBox.md b/Base/usr/share/man/man5/GML/Widget/GroupBox.md deleted file mode 100644 index b1db09260c3..00000000000 --- a/Base/usr/share/man/man5/GML/Widget/GroupBox.md +++ /dev/null @@ -1,25 +0,0 @@ -## Name - -GML Group Box Widget - -## Description - -Defines a GUI group box widget. Used to contain widgets in a border with a title. - -## Synopsis - -`@GUI::GroupBox` - -## Examples - -```gml -@GUI::GroupBox { - title: "Appearance" -} -``` - -## Registered Properties - -| Property | Type | Possible values | Description | -|----------|--------|-----------------|----------------| -| title | string | Any string | Sets the title | diff --git a/Base/usr/share/man/man5/GML/Widget/HorizontalProgressbar.md b/Base/usr/share/man/man5/GML/Widget/HorizontalProgressbar.md deleted file mode 100644 index 41132aac198..00000000000 --- a/Base/usr/share/man/man5/GML/Widget/HorizontalProgressbar.md +++ /dev/null @@ -1,20 +0,0 @@ -## Name - -GML Horizontal Progress Bar Widget - -## Description - -Defines a GUI horizontal progress bar widget. - -## Synopsis - -`@GUI::HorizontalProgressbar` - -## Examples - -```gml -@GUI::HorizontalProgressbar { - name: "horizontal_progressbar" - fixed_height: 20 -} -``` diff --git a/Base/usr/share/man/man5/GML/Widget/HorizontalSeparator.md b/Base/usr/share/man/man5/GML/Widget/HorizontalSeparator.md deleted file mode 100644 index 30f42f59962..00000000000 --- a/Base/usr/share/man/man5/GML/Widget/HorizontalSeparator.md +++ /dev/null @@ -1,19 +0,0 @@ -## Name - -GML Horizontal Separator Widget - -## Description - -Defines a horizontal separator widget. Creates a horizontal line separating elements. - -## Synopsis - -`@GUI::HorizontalSeparator` - -## Examples - -```gml -@GUI::HorizontalSeparator { - fixed_height: 2 -} -``` diff --git a/Base/usr/share/man/man5/GML/Widget/HorizontalSlider.md b/Base/usr/share/man/man5/GML/Widget/HorizontalSlider.md deleted file mode 100644 index d2a3f98a8cd..00000000000 --- a/Base/usr/share/man/man5/GML/Widget/HorizontalSlider.md +++ /dev/null @@ -1,19 +0,0 @@ -## Name - -GML Horizontal Slider Widget - -## Description - -Defines a GUI horizontal slider widget. - -## Synopsis - -`@GUI::HorizontalSlider` - -## Examples - -```gml -@GUI::HorizontalSlider { - -} -``` diff --git a/Base/usr/share/man/man5/GML/Widget/HorizontalSplitter.md b/Base/usr/share/man/man5/GML/Widget/HorizontalSplitter.md deleted file mode 100644 index 2a2b51687da..00000000000 --- a/Base/usr/share/man/man5/GML/Widget/HorizontalSplitter.md +++ /dev/null @@ -1,19 +0,0 @@ -## Name - -GUI Horizontal Splitter Widget - -## Description - -Defines a GUI horizontal splitter widget. - -## Synopsis - -`@GUI::HorizontalSplitter` - -## Examples - -```gml -@GUI::HorizontalSplitter { - -} -``` diff --git a/Base/usr/share/man/man5/GML/Widget/IconView.md b/Base/usr/share/man/man5/GML/Widget/IconView.md deleted file mode 100644 index 5977f429413..00000000000 --- a/Base/usr/share/man/man5/GML/Widget/IconView.md +++ /dev/null @@ -1,19 +0,0 @@ -## Name - -GML Icon View Widget - -## Description - -Defines a GUI icon view widget. - -## Synopsis - -`@GUI::IconView` - -## Examples - -```gml -@GUI::IconView { - -} -``` diff --git a/Base/usr/share/man/man5/GML/Widget/ImageWidget.md b/Base/usr/share/man/man5/GML/Widget/ImageWidget.md deleted file mode 100644 index 6dd79731613..00000000000 --- a/Base/usr/share/man/man5/GML/Widget/ImageWidget.md +++ /dev/null @@ -1,26 +0,0 @@ -## Name - -GML Image Widget - -## Description - -Defines a GUI image widget. - -## Synopsis - -`@GUI::ImageWidget` - -## Examples - -```gml -@GUI::ImageWidget { - -} -``` - -## Registered Properties - -| Property | Type | Possible values | Description | -|----------------|------|-----------------|-------------------------------------| -| auto_resize | bool | true or false | Set if the image should auto-resize | -| should_stretch | bool | true or false | Set if the image should stretch | diff --git a/Base/usr/share/man/man5/GML/Widget/Label.md b/Base/usr/share/man/man5/GML/Widget/Label.md deleted file mode 100644 index f53b5273c34..00000000000 --- a/Base/usr/share/man/man5/GML/Widget/Label.md +++ /dev/null @@ -1,30 +0,0 @@ -## Name - -GML Label Widget - -## Description - -Defines a GUI label widget. - -## Synopsis - -`@GUI::Label` - -## Examples - -``` -@GUI::Label { - text: "Copying files..." - text_alignment: "CenterLeft" - font_weight: "Bold" - fixed_height: 32 - name: "files_copied_label" -} -``` - -## Registered Properties - -| Property | Type | Possible values | Description | -|-----------------|----------------|-----------------------------------------------------------------------------|----------------------------| -| text_alignment | text_alignment | Center, CenterLeft, CenterRight, TopLeft, TopRight, BottomLeft, BottomRight | Sets alignment of the text | -| text_wrapping | text_wrapping | | | | | diff --git a/Base/usr/share/man/man5/GML/Widget/LinkLabel.md b/Base/usr/share/man/man5/GML/Widget/LinkLabel.md deleted file mode 100644 index 05d32feee21..00000000000 --- a/Base/usr/share/man/man5/GML/Widget/LinkLabel.md +++ /dev/null @@ -1,19 +0,0 @@ -## Name - -GML Link Label Widget - -## Description - -Defines a GUI link label widget. - -## Synopsis - -`@GUI::LinkLabel` - -## Examples - -```gml -@GUI::LinkLabel { - -} -``` diff --git a/Base/usr/share/man/man5/GML/Widget/ListView.md b/Base/usr/share/man/man5/GML/Widget/ListView.md deleted file mode 100644 index 7bb7b76858c..00000000000 --- a/Base/usr/share/man/man5/GML/Widget/ListView.md +++ /dev/null @@ -1,19 +0,0 @@ -## Name - -GML List-view Widget - -## Synopsis - -`@GUI::ListView` - -## Description - -Defines a GUI list view widget. - -## Examples - -```gml -@GUI::ListView { - name: "list_view" -} -``` diff --git a/Base/usr/share/man/man5/GML/Widget/MultiView.md b/Base/usr/share/man/man5/GML/Widget/MultiView.md deleted file mode 100644 index ffa72f2f058..00000000000 --- a/Base/usr/share/man/man5/GML/Widget/MultiView.md +++ /dev/null @@ -1,19 +0,0 @@ -## Name - -GML Multi-view Widget - -## Description - -Defines a GUI multi view widget. - -## Synopsis - -`@GUI::MultiView` - -## Examples - -```gml -@GUI::MultiView { - -} -``` diff --git a/Base/usr/share/man/man5/GML/Widget/NumericInput.md b/Base/usr/share/man/man5/GML/Widget/NumericInput.md deleted file mode 100644 index 89b1b23e4f3..00000000000 --- a/Base/usr/share/man/man5/GML/Widget/NumericInput.md +++ /dev/null @@ -1,29 +0,0 @@ -## Name - -GML Numeric Input Widget - -## Description - -Defines a GUI text box that only allows integers within a specified range. - -## Synopsis - -`@GUI::NumericInput` - -## Examples - -```gml -@GUI::NumericInput { - min: 0 - value: 42 - max: 9000 -} -``` - -## Registered Properties - -| Property | Type | Possible values | Description | -|----------|-------|--------------------|----------------------------------------| -| min | int | Any 64 bit integer | Minimum number the input can be set to | -| max | int | Any 64 bit integer | Maximum number the input can be set to | -| value | int | Any 64 bit integer | Initial value | diff --git a/Base/usr/share/man/man5/GML/Widget/OpacitySlider.md b/Base/usr/share/man/man5/GML/Widget/OpacitySlider.md deleted file mode 100644 index cabb0edf2dc..00000000000 --- a/Base/usr/share/man/man5/GML/Widget/OpacitySlider.md +++ /dev/null @@ -1,21 +0,0 @@ -## Name - -GML Opacity Slider Widget - -## Description - -Defines a GUI opacity slider widget. - -## Synopsis - -`@GUI::HorizontalOpacitySlider` -`@GUI::VerticalOpacitySlider` - -## Examples - -```gml -@GUI::HorizontalOpacitySlider { - name: "opacity_slider" - tooltip: "Opacity Slider" -} -``` diff --git a/Base/usr/share/man/man5/GML/Widget/PasswordBox.md b/Base/usr/share/man/man5/GML/Widget/PasswordBox.md deleted file mode 100644 index 7d91887a62a..00000000000 --- a/Base/usr/share/man/man5/GML/Widget/PasswordBox.md +++ /dev/null @@ -1,21 +0,0 @@ -## Name - -GML Password Box Widget - -## Description - -Defines a GUI password box widget. - -## Synopsis - -```gml -@GUI::PasswordBox -``` - -## Examples - -```gml -@GUI::PasswordBox { - -} -``` diff --git a/Base/usr/share/man/man5/GML/Widget/Progressbar.md b/Base/usr/share/man/man5/GML/Widget/Progressbar.md deleted file mode 100644 index 5f10831ee31..00000000000 --- a/Base/usr/share/man/man5/GML/Widget/Progressbar.md +++ /dev/null @@ -1,32 +0,0 @@ -## Name - -GML Progress Bar Widget - -## Description - -Defines a GUI progress bar widget. - -## Synopsis - -```gml -@GUI::Progressbar -``` - -## Examples - -```gml -@GUI::Progressbar { - fixed_height: 22 - name: "progressbar" - min: 0 -} -``` - -## Registered Properties - -| Property | Type | Possible values | Description | -|----------|--------|-----------------------------------|--------------------------------------------| -| text | string | Any string | Sets progress text | -| format | enum | NoText, Percentage, ValueSlashMax | Sets the format of the progress indication | -| min | int | Any 64 bit integer | Sets the minimum progress value | -| max | int | Any 64 bit integer | Set the maximum progress value | diff --git a/Base/usr/share/man/man5/GML/Widget/RadioButton.md b/Base/usr/share/man/man5/GML/Widget/RadioButton.md deleted file mode 100644 index c761e9a221a..00000000000 --- a/Base/usr/share/man/man5/GML/Widget/RadioButton.md +++ /dev/null @@ -1,26 +0,0 @@ -## Name - -GML Radio Button Widget - -## Description - -Defines a GUI radio button widget. - -## Synopsis - -`@GUI::RadioButton` - -## Examples - -```gml -@GUI::RadioButton { - name: "top_radiobutton" - text: "Radio 1" - checked: true -} - -@GUI::RadioButton { - name: "bottom_radiobutton" - text: "Radio 2" -} -``` diff --git a/Base/usr/share/man/man5/GML/Widget/ScrollableContainerWidget.md b/Base/usr/share/man/man5/GML/Widget/ScrollableContainerWidget.md deleted file mode 100644 index 38bcd53c7b9..00000000000 --- a/Base/usr/share/man/man5/GML/Widget/ScrollableContainerWidget.md +++ /dev/null @@ -1,34 +0,0 @@ -## Name - -GML Scrollable Container Widget - -## Description - -Defines a GUI scrollable container widget. - -Unlike other container widgets, this one does not allow multiple child widgets to be added, and thus also does not support setting a layout. - -## Synopsis - -`@GUI::ScrollableContainerWidget` - -Content declared as `content_widget` property. - -## Examples - -```gml -@GUI::ScrollableContainerWidget { - content_widget: @GUI::Widget { - [...] - } -} -``` - -## Registered Properties - -| Property | Type | Possible values | Description | -|------------------------------------|---------------|------------------------|---------------------------------------------| -| ~~layout~~ | | none | Does not take layout | -| scrollbars_enabled | bool | true or false | Status of scrollbar | -| should_hide_unnecessary_scrollbars | bool | true or false | Whether to hide scrollbars when unnecessary | -| content_widget | widget object | Any Subclass of Widget | The Widget to be displayed in the Container | diff --git a/Base/usr/share/man/man5/GML/Widget/Scrollbar.md b/Base/usr/share/man/man5/GML/Widget/Scrollbar.md deleted file mode 100644 index aea511b962d..00000000000 --- a/Base/usr/share/man/man5/GML/Widget/Scrollbar.md +++ /dev/null @@ -1,24 +0,0 @@ -## Name - -GML Scrollbar Widget - -## Description - -Defines a GUI scrollbar widget. - -## Synopsis - -`@GUI::Scrollbar` - -## Examples - -```gml -@GUI::Scrollbar { - name: "enabled_scrollbar" - fixed_height: 16 - fixed_width: -1 - min: 0 - max: 100 - value: 50 -} -``` diff --git a/Base/usr/share/man/man5/GML/Widget/Slider.md b/Base/usr/share/man/man5/GML/Widget/Slider.md deleted file mode 100644 index d69183bc218..00000000000 --- a/Base/usr/share/man/man5/GML/Widget/Slider.md +++ /dev/null @@ -1,25 +0,0 @@ -## Name - -GML Slider Widget - -## Description - -Defines a GUI slider widget. - -## Synopsis - -`@GUI::Slider` - -## Examples - -```gml -@GUI::Slider { - -} -``` - -## Registered Properties - -| Property | Type | Possible values | Description | -|----------------|------|---------------------|---------------------------| -| knob_size_mode | enum | Fixed, Proportional | Sets the slider knob size | diff --git a/Base/usr/share/man/man5/GML/Widget/SpinBox.md b/Base/usr/share/man/man5/GML/Widget/SpinBox.md deleted file mode 100644 index 7612510c6d0..00000000000 --- a/Base/usr/share/man/man5/GML/Widget/SpinBox.md +++ /dev/null @@ -1,28 +0,0 @@ -## Name - -GML Spin Box Widget - -## Description - -Defines a GUI spin box widget. This is a number input with buttons to increment and decrement the value. - -## Synopsis - -`@GUI::SpinBox` - -## Examples - -```gml -@GUI::SpinBox { - name: "thickness_spinbox" - min: 0 - max: 2 -} -``` - -## Registered Properties - -| Property | Type | Possible values | Description | -|----------|-------|--------------------|----------------------------------------------| -| min | int | Any 64 bit integer | Minimum number the spin box can increment to | -| max | int | Any 64 bit integer | Maximum number the spin box can increment to | diff --git a/Base/usr/share/man/man5/GML/Widget/StackWidget.md b/Base/usr/share/man/man5/GML/Widget/StackWidget.md deleted file mode 100644 index f282738fd58..00000000000 --- a/Base/usr/share/man/man5/GML/Widget/StackWidget.md +++ /dev/null @@ -1,19 +0,0 @@ -## Name - -GML Stack Widget - -## Description - -Defines a GUI stack widget. - -## Synopsis - -`@GUI::StackWidget` - -## Examples - -```gml -@GUI::StackWidget { - -} -``` diff --git a/Base/usr/share/man/man5/GML/Widget/Statusbar.md b/Base/usr/share/man/man5/GML/Widget/Statusbar.md deleted file mode 100644 index 0dd3989b414..00000000000 --- a/Base/usr/share/man/man5/GML/Widget/Statusbar.md +++ /dev/null @@ -1,26 +0,0 @@ -## Name - -GML Status Bar Widget - -## Description - -Defines a GUI status bar widget. - -## Synopsis - -`@GUI::Statusbar` - -## Examples - -```gml -@GUI::Statusbar { - -} -``` - -## Registered Properties - -| Property | Type | Possible values | Description | -|-------------|--------|--------------------|---------------------------------| -| text | string | Any string | Sets the text of the status bar | -| label_count | int | Any 64 bit integer | Sets the label count | diff --git a/Base/usr/share/man/man5/GML/Widget/TabWidget.md b/Base/usr/share/man/man5/GML/Widget/TabWidget.md deleted file mode 100644 index 2c8cb548a4c..00000000000 --- a/Base/usr/share/man/man5/GML/Widget/TabWidget.md +++ /dev/null @@ -1,38 +0,0 @@ -## Name - -GML Tab Widget - -## Description - -Defines a GUI tab widget. - -## Synopsis - -`@GUI::TabWidget` - -## Examples - -```gml -@GUI::TabWidget { - uniform_tabs: true - - @GUI::Widget { - title: "First tab" - } - @GUI::Widget { - title: "Second tab" - } -} -``` - -## Registered Properties - -| Property | Type | Possible values | Description | -|--------------------|----------------|-----------------------------------------------------------------------------|--------------------------------------| -| container_margins | margins | | Margins for the tab content | -| reorder_allowed | bool | true or false | Allow changing the order of the tabs | -| show_close_buttons | bool | true or false | Show a close button on each tab | -| show_tab_bar | bool | true or false | Whether to display the tabs | -| text_alignment | text_alignment | Center, CenterLeft, CenterRight, TopLeft, TopRight, BottomLeft, BottomRight | Set the alignment of tab text | -| tab_position | tab_position | Top, Bottom, Left, Right | Set the tab position | -| uniform_tabs | bool | true or false | Give all tabs the same width | diff --git a/Base/usr/share/man/man5/GML/Widget/TableView.md b/Base/usr/share/man/man5/GML/Widget/TableView.md deleted file mode 100644 index f36aec53c7b..00000000000 --- a/Base/usr/share/man/man5/GML/Widget/TableView.md +++ /dev/null @@ -1,20 +0,0 @@ -## Name - -GML Table View Widget - -## Description - -Defines a GUI table view widget. - -## Synopsis - -`@GUI::TableView` - -## Examples - -```gml -@GUI::TableView { - name: "cursors_tableview" - font_size: 12 -} -``` diff --git a/Base/usr/share/man/man5/GML/Widget/TextBox.md b/Base/usr/share/man/man5/GML/Widget/TextBox.md deleted file mode 100644 index aa53070e390..00000000000 --- a/Base/usr/share/man/man5/GML/Widget/TextBox.md +++ /dev/null @@ -1,25 +0,0 @@ -## Name - -GML Text Box Widget - -## Description - -Defines a GUI text box widget. - -## Synopsis - -`@GUI::TextBox` - -## Examples - -```gml -@GUI::TextBox { - placeholder: "Text box" - mode: "Editable" -} - -@GUI::TextBox { - text: "Disabled" - enabled: false -} -``` diff --git a/Base/usr/share/man/man5/GML/Widget/TextEditor.md b/Base/usr/share/man/man5/GML/Widget/TextEditor.md deleted file mode 100644 index 6bc5174e3ad..00000000000 --- a/Base/usr/share/man/man5/GML/Widget/TextEditor.md +++ /dev/null @@ -1,28 +0,0 @@ -## Name - -GML Text Editor Widget - -## Description - -Defines a GUI text editor widget. - -## Synopsis - -`@GUI::TextEditor` - -## Examples - -```gml -@GUI::TextEditor { - name: "text_editor" - placeholder: "Text editor" -} -``` - -## Registered Properties - -| Property | Type | Possible values | Description | -|-------------|--------|---------------------------------|-----------------| -| text | string | Any string | Set text | -| placeholder | string | Any string | Set placeholder | -| mode | enum | Editable, ReadOnly, DisplayOnly | Set editor mode | diff --git a/Base/usr/share/man/man5/GML/Widget/Toolbar.md b/Base/usr/share/man/man5/GML/Widget/Toolbar.md deleted file mode 100644 index 0490271c6da..00000000000 --- a/Base/usr/share/man/man5/GML/Widget/Toolbar.md +++ /dev/null @@ -1,32 +0,0 @@ -## Name - -GML Toolbar Widget - -## Description - -Defines a GUI toolbar widget. - -When `collapsible` is set to `true`, the toolbar can be resized below the size of its items. -Any items that do not fit the current size, will be placed in an overflow menu. -To keep groups (i.e. Buttons/items separated by Separators) together, and move them to the overflow menu as one, set the `grouped` property. - -## Synopsis - -`@GUI::Toolbar` - -## Examples - -```gml -@GUI::Toolbar { - name: "toolbar" - collapsible: true - grouped: true -} -``` - -## Registered Properties - -| Property | Type | Possible values | Description | -|-------------|------|-----------------|---------------------------------------------------------------------------------------| -| collapsible | bool | true or false | If items that do not fit should be placed in an overflow menu | -| grouped | bool | true or false | If items should be moved to the overflow menu in groups, separated by Separator items | diff --git a/Base/usr/share/man/man5/GML/Widget/ToolbarContainer.md b/Base/usr/share/man/man5/GML/Widget/ToolbarContainer.md deleted file mode 100644 index 27d5e770142..00000000000 --- a/Base/usr/share/man/man5/GML/Widget/ToolbarContainer.md +++ /dev/null @@ -1,23 +0,0 @@ -## Name - -GML Toolbar Container Widget - -## Description - -Defines a GUI toolbar container widget. - -## Synopsis - -`@GUI::ToolbarContainer` - -## Examples - -```gml -@GUI::ToolbarContainer { - name: "toolbar_container" - - @GUI::Toolbar { - name: "toolbar" - } -} -``` diff --git a/Base/usr/share/man/man5/GML/Widget/Tray.md b/Base/usr/share/man/man5/GML/Widget/Tray.md deleted file mode 100644 index a7a2e9b3be5..00000000000 --- a/Base/usr/share/man/man5/GML/Widget/Tray.md +++ /dev/null @@ -1,19 +0,0 @@ -## Name - -GML Tray Widget - -## Description - -Defines a GUI tray widget. - -## Synopsis - -`@GUI::Tray` - -## Examples - -```gml -@GUI::Tray { - -} -``` diff --git a/Base/usr/share/man/man5/GML/Widget/TreeView.md b/Base/usr/share/man/man5/GML/Widget/TreeView.md deleted file mode 100644 index 578bdb56bed..00000000000 --- a/Base/usr/share/man/man5/GML/Widget/TreeView.md +++ /dev/null @@ -1,20 +0,0 @@ -## Name - -GML Tree View Widget - -## Description - -Defines a GUI tree view widget. - -## Synopsis - -`@GUI::TreeView` - -## Examples - -```gml -@GUI::TreeView { - name: "tree_view" - fixed_width: 200 -} -``` diff --git a/Base/usr/share/man/man5/GML/Widget/UrlBox.md b/Base/usr/share/man/man5/GML/Widget/UrlBox.md deleted file mode 100644 index 59d2280f933..00000000000 --- a/Base/usr/share/man/man5/GML/Widget/UrlBox.md +++ /dev/null @@ -1,19 +0,0 @@ -## Name - -GML URL Box Widget - -## Description - -Defines a GUI url box widget. - -## Synopsis - -`@GUI::UrlBox` - -## Examples - -```gml -@GUI::UrlBox { - -} -``` diff --git a/Base/usr/share/man/man5/GML/Widget/ValueSlider.md b/Base/usr/share/man/man5/GML/Widget/ValueSlider.md deleted file mode 100644 index 1dd2eded434..00000000000 --- a/Base/usr/share/man/man5/GML/Widget/ValueSlider.md +++ /dev/null @@ -1,23 +0,0 @@ -## Name - -GML Value Slider Widget - -## Description - -Defines a GUI value slider widget. - -## Synopsis - -`@GUI::ValueSlider` - -## Examples - -```gml -@GUI::ValueSlider { - name: "value_slider" - min: 0 - max: 100 - value: 100 - tooltip: "Value Slider" -} -``` diff --git a/Base/usr/share/man/man5/GML/Widget/VerticalProgressbar.md b/Base/usr/share/man/man5/GML/Widget/VerticalProgressbar.md deleted file mode 100644 index ec9d1f64ca7..00000000000 --- a/Base/usr/share/man/man5/GML/Widget/VerticalProgressbar.md +++ /dev/null @@ -1,20 +0,0 @@ -## Name - -GML Vertical Progress Bar Widget - -## Description - -Defines a GUI vertical progress bar widget. - -## Synopsis - -`@GUI::VerticalProgressbar` - -## Examples - -```gml -@GUI::VerticalProgressbar { - name: "vertical_progressbar_left" - fixed_width: 36 -} -``` diff --git a/Base/usr/share/man/man5/GML/Widget/VerticalSeparator.md b/Base/usr/share/man/man5/GML/Widget/VerticalSeparator.md deleted file mode 100644 index 4a9ba27c7be..00000000000 --- a/Base/usr/share/man/man5/GML/Widget/VerticalSeparator.md +++ /dev/null @@ -1,19 +0,0 @@ -## Name - -GML Vertical Separator Widget - -## Description - -Defines a GUI vertical separator. - -## Synopsis - -`@GUI::VerticalSeparator` - -## Examples - -```gml -@GUI::VerticalSeparator { - -} -``` diff --git a/Base/usr/share/man/man5/GML/Widget/VerticalSlider.md b/Base/usr/share/man/man5/GML/Widget/VerticalSlider.md deleted file mode 100644 index 963fc1d1632..00000000000 --- a/Base/usr/share/man/man5/GML/Widget/VerticalSlider.md +++ /dev/null @@ -1,19 +0,0 @@ -## Name - -GML Vertical Slider Widget - -## Description - -Defines a GUI vertical slider widget. - -## Synopsis - -`@GUI::VerticalSlider` - -## Examples - -```gml -@GUI::VerticalSlider { - -} -``` diff --git a/Base/usr/share/man/man5/GML/Widget/VerticalSplitter.md b/Base/usr/share/man/man5/GML/Widget/VerticalSplitter.md deleted file mode 100644 index 57d5ac9e2bb..00000000000 --- a/Base/usr/share/man/man5/GML/Widget/VerticalSplitter.md +++ /dev/null @@ -1,19 +0,0 @@ -## Name - -GML Vertical Splitter Widget - -## Description - -Defines a GUI vertical splitter widget. - -## Synopsis - -`@GUI::VerticalSplitter` - -## Examples - -```gml -@GUI::VerticalSplitter { - -} -``` diff --git a/Base/usr/share/man/man5/Network.md b/Base/usr/share/man/man5/Network.md deleted file mode 100644 index a8106ed9331..00000000000 --- a/Base/usr/share/man/man5/Network.md +++ /dev/null @@ -1,44 +0,0 @@ -## Name - -NetworkServer - network configuration - -## Synopsis - -```** -/etc/Network.ini -``` - -## Description - -The Network configuration is loaded by NetworkServer service on startup. It consists of a list of interfaces, with adapter names as groups. - -The interface that is not listed in this config file has DHCP enabled by default. - -## Options - -* `Enabled` (default: `true`) - Whether the interface is enabled. -* `DHCP` (default: `false`) - Whether the DHCP client should be run on this interface. This overrides static IP settings. -* `IPv4Address` (default: `0.0.0.0`) - The static IPv4 address for the interface. Used only when `DHCP` is `false`. -* `IPv4Netmask` (default: `0.0.0.0`) - The static IPv4 netmask for the interface. Used only when `DHCP` is `false`. -* `IPv4Gateway` (default: `0.0.0.0`) - The static IPv4 default gateway for the interface. Used only when `DHCP` is `false`. - -## Example - -```ini -# Set static IP address to 10.0.0.5/8 and default gateway to 10.0.0.1 -[ep1s0] -IPv4Address=10.0.0.5 -IPv4Netmask=255.0.0.0 -IPv4Gateway=10.0.0.1 - -# Try to run DHCP discovery on ep0s8. It is equivalent to not adding this entry at all. -[ep0s8] -DHCP=true - -# Disable interface ep1s1 entirely. -[ep1s1] -Enabled=false -``` - -## See Also -* [`ifconfig`(1)](help://man/1/ifconfig) diff --git a/Base/usr/share/man/man5/Shell.md b/Base/usr/share/man/man5/Shell.md deleted file mode 100644 index f3348212497..00000000000 --- a/Base/usr/share/man/man5/Shell.md +++ /dev/null @@ -1,534 +0,0 @@ -## Name - -The Shell Command Language - -## Introduction - -The shell operates according to the following general steps: - -* Some string is read from a source, be it a file, the standard input, or a command string (see [`Shell`(1)](help://man/1/Shell)) -* The shell parses the input to an abstract syntax tree -* The shell performs various expansions and/or resolutions on the nodes -* The shell performs various type checks and syntactic checks -* The shell interprets the AST, evaluating commands as needed -* For each given command, the shell flattens all the string/list arguments -* For each given command, the shell records the applicable redirections -* Should a command be executed, the shell applies the redirections, and executes the command with the flattened argument list -* Should a command need waiting, the shell shall wait for the command to finish, and continue execution - -Any text below is superseded by the formal grammar defined in the _formal grammar_ section. - -## General Token Recognition - -This section describes the general tokens the language accepts, it should be noted that due to nature of the language, some tokens are valid only in a specific context. - -##### Bareword -String of characters that are not _Special_ or _Syntactic Elements_ - -##### Glob -String of characters containing at least one of `*?` in _bareword_ position - -##### History Events -A _designator_ starting with `!` in _bareword_ position that describes a word or a range of words from a previously entered command. -Please look at the section named 'History Event Designators' for a more thorough explanation. Only allowed in interactive mode. - -##### Single Quoted String -Any sequence of characters between two single quotes (`'`) - -##### Double Quoted String -Any sequence of _Double Quoted String Part_ tokens: -* Barewords -* Single Quotes -* Variable References -* History Events -* Evaluate expressions -* Escaped sequences - -##### Heredocs -Heredocs are made in two parts, the _initiator_ and the _contents_, the _initiator_ may be used in place of a string (i.e. wherever a string is allowed to be used), with the constraint that the _contents_ must follow the _sequence_ that the _initiator_ is used in. - -There are four different _initiators_: -- `<<-token`: The _contents_ may contain interpolations, and are terminated with a line containing only whitespace and then _token_ -- `<<-'token'`: The _contents_ may _not_ contain interpolations, but otherwise is the same as `<<-token` -- `<<~token`: Similar to `<<-token`, but the starting whitespace of the lines in the _contents_ is stripped, note that this happens after any and all expansions/interpolations are done. -- `<<~'token'`: Dedents (i.e. strips the initial whitespace) like `<<~token`, and disallows interpolations like `<<-'token'`. - -Note that heredocs _must_ be listed in the same order as they are used after a sequence that has been terminated with a newline. - -##### Variable Reference -Any sequence of _Identifier_ characters, or a _Special Variable_ following a `$`. -Variables may be followed by a _Slice_ (see [Slice](#slice)) - -##### Slice -Variables may be sliced into, which will allow the user to select a subset of entries in the contents of the variable. -An expression of the form $_identifier_[_slice-contents_] can be used to slice into a variable, where _slice-contents_ has semantics similar to _Brace Expansions_, but it may only evaluate to numeric values, that are used to index into the variable being sliced. -Negative indices are allowed, and will index the contents from the end. It should be noted that the shell will always perform bounds-checking on the indices, and raise an error on out-of-bound accesses. Slices can slice into both lists and strings. - -For example, `$lst[1..-2]` can be used to select a permutation of a 4-element list referred to by the variable `lst`, as the slice will evaluate to the list `(1 0 -1 -2)`, which will select the indices 1, 0, 3, 2 (in that order). - - -##### Immediate Expressions -An expression of the form '${identifier expression...}', such expressions are expanded to other kinds of nodes before resolution, and are internal functions provided by the shell. -Currently, the following functions are exposed: -- ${length (string|list)? _expression_} -Finds the length of the given _expression_. if either `string` or `list` is given, the shell will attempt to treat _expression_ as that type, otherwise the type of _expression_ will be inferred. - -- ${length\_across (string|list) _expression_} -Finds the lengths of the entries in _expression_, this requires _expression_ to be a list. -If either `string` or `list` is given, the shell attempts to treat the elements of _expression_ as that type, otherwise the types are individually inferred. - -- ${split _delimiter_ _string_} -Splits the _string_ with _delimiter_, and evaluates to a list. -Both _string_ and _delimiter_ must be strings. - -- ${remove\_suffix _suffix_ _string_} -Removes the suffix _suffix_ (if present) from the given _string_. - -- ${remove\_prefix _prefix_ _string_} -Removes the prefix _prefix_ (if present) from the given _string_. - -- ${concat\_lists _list_...} -Concatenates all the given expressions as lists, and evaluates to a list. - -- ${regex\_replace _pattern_ _replacement-template_ _string_} -Replaces all occurrences of the regular expression _pattern_ in the given _string_, using the given _replacement-template_. -Capture groups in _pattern_ can be referred to as `\<group_number>` in the _replacement template_, for example, to reference capture group 1, use `\1`. - -##### Evaluate expression -Any expression following a `$` that is not a variable reference: -* Inline execution: A _syntactic list_ following a `$`: -* Dynamic evaluation: Any other expression following a `$` - -##### Lists -Any two expressions joined by the Join operator (` ` [whitespace]), or a _variable reference_ referring to a list value -* Syntactic Lists: Any _list_ enclosed in parentheses (`(` and `)`) - -##### Comments -Any text following and including that in a word starting with `#`, up to but not including a newline - -##### Keywords -The following tokens: -* `for` in command name position -* `in` as a syntactic element of a `for` expression -* `if` in command name position, or after the `else` keyword -* `else` after a partial `if` expression -* `match` in command name position -* `as` as part of a `match` expression - -##### Special characters -Any of the following: -* `;` in bareword position -* `\\n` (a newline) in _bareword_ position -* Any of `(){}` -* Any of `*?` not in _glob_ position - -##### Tilde -Any initial path segment starting with the character `~` in _bareword_ position, Optionally followed by a _bareword_ for the username - -## Redirections -The shell can create various redirections to file descriptors of a command before executing it, the general syntax for redirections is an optional file descriptor, followed by a redirection operator, followed by a destination. - -There are four redirection operators corresponding to various file descriptor open modes: `Read`, `Write`, `WriteAppend` and `ReadWrite`, respectively `<`, `>`, `>>` and `<>`. - -A special syntactic element `&fd` can reference a file descriptor as a destination. - -Redirections take two main forms, Read/Write redirections, and fd closure redirections. -##### Read/Write -* Allowed operators: all -* Allowed destinations: file paths (any shell _expression_) and _file descriptor references_ - -##### Close -* Allowed operators: `Write` (`>`) -* Allowed destinations: the special "close" reference `&-` - -#### Examples -```sh -# Redirect the standard error to a file, and close the standard input -$ 2> foo 1>&- - -# Redirect a file as read-write into the standard input -$ 1<>foo - -# Redirect the standard output to /dev/null -$ >/dev/null -``` - -## Expansions -The shell performs various expansions, in different stages. - -* Glob Expansion: Globs shall be expanded to a list. - -* Variable Expansion: Variables shall be expanded preserving their types. - -* Brace Expansions: Brace expansions shall be expanded to a list. - -* Juxtaposition Expansion: Juxtapositions shall be expanded as list products. - -* Other expansions: Tildes, Evaluate expressions, etc. shall be expanded as needed. - -### Brace Expansions -Brace expansions are of two kinds, _normal brace expansions_ and _range brace expansions_. -_Normal brace expansions_ are sequences of optional expressions inside braces (`{}`), delimited by a comma (`','`); a missing expression is treated as an empty string literal. Such expressions are simply expanded to the expressions they enclose. -_Range brace expansions_ are of the form `{start_expression..end_expression}`, where `start_expression` and `end_expression` denote the bounds of an inclusive _range_, and can be one of two types: -- Single unicode code points: The range expands to all code points between the start and end, e.g. `{a..c}` shall expand to the list `(a b c)`. -- Numbers: The range expands to all numbers between the start and end, e.g. `{8..11}` shall expand to the list `(8 9 10 11)`. - -### Juxtapositions -Any two expressions joined without any operator are considered to be in a Juxtaposition, with the resulting value being the list product of two expressions. -For instance, `(1 2)(3 4)` shall be evaluated to `(13 14 23 24)` by calculating the list product of the two expressions `(1 2)` and `(3 4)`. - -### Tildes -Any bareword starting with a tilde (`~`) and spanning up to the first path separator (`/`) - or EOL - is considered to be a tilde expansion with the text between the tilde and the separator being the _username_, which shall be expanded to a single string containing the home directory of the given _username_ (or the current user if no username is provided). - -### Evaluate -Evaluate expressions take the general form of a dollar sign (`$`) followed by some _expression_, which is evaluated by the rules below. -- Should the _expression_ be a string, it shall be evaluated as a dynamic variable lookup by first evaluating the string, and then looking up the given variable. -- Should the _expression_ be a list or a command, it shall be converted to a command, whose output (from the standard output) shall be captured, and split to a list with the shell local variable `IFS` (or the default splitter `\n` (newline, 0x0a)). It should be noted that the shell option `inline_exec_keep_empty_segments` will determine whether empty segments in the split list shall be preserved when this expression is evaluated, this behavior is disabled by default. - -## Commands - -A `Command` is a single simple command, containing arguments and redirections for a single program, or a compound command containing a shell control structure. The shell can evaluate a sequence of commands, a conditional relation between commands, or various semantic elements composed of commands and intrinsics. - -Commands can be either calls to Shell builtins, or external programs. - -## Shell Semantic Elements -The commands can be composed into semantic elements, producing composite commands: - -### Sequences -A sequence of commands, executed serially independent of each other: `Command ; Command ; Command ...` - -It should be noted that a newline (`\\n`) can be substituted for the semicolon (`;`). - -#### Example -```sh -# Do one thing, then do another -echo foo; echo bar -``` - -### Logical Relations -A sequence of commands whose execution depends somehow on the result of another - -#### `Command && Command && Command ...` (AND) -Short-circuiting command evaluations, will cancel the entire chain should any command fails (have a non-zero exit code) - -#### `Command || Command || Command ...` (OR) -Short-circuiting command evaluation, will continue down the chain if any command fails. - -It should be noted that `And` chains bind more tightly than `Or` chains, so an expression of the form `C1 && C2 || C3` is understood as "evaluate `C1`, if successful, evaluate `C2`, if not successful, evaluate `C3`". - -##### Examples -```sh -# Create file if not found -test -f foo.txt || touch foo.txt - -# Announce execution status of a command -rm test && echo "deleted!" || echo "failed with $?" -``` - -#### Control Structures - -##### Conditionals -Conditionals can either be expressed with the _Logical Relations_, or via explicit `if` expressions. -An `if` expression contains at least a _condition_ command and a _then clause_, and optionally the `else` keyword followed by an _else clause_. -An _else clause_ may contain another `if` expression instead of a normal block. - -The _then clause_ **must** be surrounded by braces, but the _else clause_ may also be another `if` expression. - -An `if` expression evaluates either the _then clause_ or (if available) the _else clause_, based on the exit code of the _condition_ command; should the exit code be zero, the _then clause_ will be executed, and if not, the _else clause_ will. - -###### Examples -```sh -# Remove a file if it exists, create it otherwise -if test -e the_file { - rm the_file -} else { - touch the_file -} - -# Cond chain (if-elseif-else) -if A { - echo A -} else if B { - echo B -} else { - echo C -} -``` - -##### For Loops -For Loops evaluate a sequence of commands once per element in a given list. -The shell has two forms of _for loops_, one with an explicitly named iteration variable, and one with an implicitly named one. -The general syntax follows the form `for index index_name name in expr { sequence }`, and allows omitting the `index index_name name in` part to implicitly name the variable `it`. - -It should be noted that the `index index_name` section is optional, but if supplied, will require an explicit iteration variable as well. -In other words, `for index i in foo` is not valid syntax. - -A for-loop evaluates the _sequence_ once per every element in the _expr_, setting the local variable _name_ to the element being processed, and the local variable _enum name_ to the enumeration index (if set). - -The Shell shall cancel the for loop if two consecutive commands are interrupted via SIGINT (\^C), and any other terminating signal aborts the loop entirely. - -###### Examples -```sh -# Iterate over every non-hidden file in the current directory, and prepend '1-' to its name. -$ for * { mv $it 1-$it } - -# Iterate over a sequence and write each element to a file -$ for i in $(seq 1 100) { echo $i >> foo } - -# Iterate over some files and get their index -$ for index i x in * { echo file at index $i is named $x } -``` - -##### Infinite Loops -Infinite loops (as denoted by the keyword `loop`) can be used to repeat a block until the block runs `break`, or the loop terminates by external sources (interrupts, program exit, and terminating signals). - -The behavior regarding SIGINT and other signals is the same as for loops (mentioned above). - -###### Examples -```sh -# Keep deleting a file -loop { - rm -f foo -} -``` - -##### Subshells -Subshells evaluate a given block in a new instance (fork) of the current shell process. to create a subshell, any valid shell code can be enclosed in braces. - -###### Examples -```sh -# Run a block of code in the background, in a subshell, then detach it from the current shell -$ { for * { te $it } }& -$ disown -``` - -##### Functions -A function is a user-defined entity that can be used as a simple command to execute a compound command, optionally with some parameters. -Such a function is defined via the syntax below: - -```sh -function_name(explicitly_named_arguments...) { compound_command } -``` - -The function is named `function_name`, and has some explicitly named arguments `explicitly_named_arguments...`, which *must* be supplied by the caller, failure to do so will cause the command to exit with status 1. - -The compound command shall be executed whenever the simple command `function_name` is executed. -This execution shall be performed in a new local frame. - -Additionally, should the simple command containing the function name be in a pipeline, or requested to be run in the background, this execution shall be moved to a subshell; naturally, in such a case any changes to the shell state (such as variables, aliases, etc) shall not be leaked to the parent shell process. - -The passed arguments shall be stored in the special variables `*` and `ARGV`, and the explicitly named arguments shall be set, in order, from the first passed argument onwards. - - -The exit status of a function simple command shall be the exit status of the last command executed within the command, or 0 if the function has no commands. -The declaration is *not* a command, and will not alter the exit status. - -###### Examples -```sh -fn(a b c) { - echo $a $b $c \( $* \) -} - -$ fn 1 2 3 4 -# 1 2 3 ( 1 2 3 4 ) -``` - -##### Match Expressions -The pattern matching construct `match` shall choose from a sequence of patterns, and execute the corresponding action in a new frame. -The choice is done by matching the result of the _matched expression_ (after expansion) against the _patterns_ (expanded down to either globs or literals). -Multiple _patterns_ can be attributed to a single given action by delimiting them with a pipe ('|') symbol. -A _pattern_ (or the series of) may be annotated with an extra `as (...)` clause, which allows globbed parts of the matching pattern to be named and used in the matching block. - -The expanded _matched expression_ can optionally be given a name using the `as name` clause after the _matched expression_, with which it may be accessible in the action clauses. - -###### Examples -```sh -# Match the result of running 'make_some_value' (which is a list when captured by $(...)) -match "$(make_some_value)" as value { - (hello*) { echo "Hi!" } - (say\ *) { echo "No, I will not $value" } -} - -# Match the result of running 'make_some_value', cast to a string -# Note the `as (expr)` in the second pattern, which assigns whatever the `*` matches -# to the name `expr` inside the block. -match "$(make_some_value)" { - hello* { echo "Hi!" } - say\ * as (expr) { echo "No, I will not say $expr!" } -} -``` - -### History Event Designators -History expansion may be utilized to reuse previously typed words or commands. -Such expressions are of the general form `!<event_designator>(:<word_designator>)`, where `event_designator` would select an entry in the shell history, and `word_designator` would select a word (or a range of words) from that entry. - -| Event designator | Effect | -| :- | :----- | -| `!` | The immediately preceding command | -| _n_ | The _n_'th entry in the history, starting with 1 as the first entry | -| -_n_ | The last _n_'th entry in the history, starting with -1 as the previous entry | -| _str_ | The most recent entry starting with _str_ | -| `?`_str_ | The most recent entry containing _str_ | - -| Word designator | Effect | -| :-- | :----- | -| _n_ | The word at index _n_, starting with 0 as the first word (usually the command) | -| `^` | The first argument (index 1) | -| `$` | The last argument | -| _x_-_y_ | The range of words starting at _x_ and ending at _y_ (inclusive). _x_ defaults to 0 if omitted | -| `*` | All the arguments. Equivalent to `^`-`$` | -| _x_`*` | The range of words starting at _x_ and ending at the last word (`$`) (inclusive) | -| _x_- | The range of words starting at _x_ and ending at the second to last word (inclusive). _x_ defaults to 0 if omitted | - -Note: The event designator and the word designator should usually be separated by a colon (`:`). This colon can be omitted only if the word designator starts with `^`, `$` or `*` (such as `!1^` for the first argument of the first entry in the history). - -## Formal Grammar - -### Shell Grammar -``` -toplevel :: sequence? - -sequence :: variable_decls? or_logical_sequence terminator sequence - | variable_decls? or_logical_sequence '&' sequence - | variable_decls? or_logical_sequence - | variable_decls? function_decl (terminator sequence)? - | variable_decls? terminator sequence - -function_decl :: identifier '(' (ws* identifier)* ')' ws* '{' [!c] toplevel '}' - -or_logical_sequence :: and_logical_sequence '|' '|' and_logical_sequence - | and_logical_sequence - -and_logical_sequence :: pipe_sequence '&' '&' and_logical_sequence - | pipe_sequence - -terminator :: ';' - | '\n' [?!heredoc_stack.is_empty] heredoc_entries - -heredoc_entries :: { .*? (heredoc_entry) '\n' } [each heredoc_entries] - -variable_decls :: identifier '=' expression (' '+ variable_decls)? ' '* - | identifier '=' '(' pipe_sequence ')' (' '+ variable_decls)? ' '* - -pipe_sequence :: command '|' pipe_sequence - | command - | control_structure '|' pipe_sequence - | control_structure - -control_structure[c] :: for_expr - | loop_expr - | if_expr - | subshell - | match_expr - | ?c: continuation_control - -continuation_control :: 'break' - | 'continue' - -for_expr :: 'for' ws+ (('index' ' '+ identifier ' '+)? identifier ' '+ 'in' ws*)? expression ws+ '{' [c] toplevel '}' - -loop_expr :: 'loop' ws* '{' [c] toplevel '}' - -if_expr :: 'if' ws+ or_logical_sequence ws+ '{' toplevel '}' else_clause? - -else_clause :: else '{' toplevel '}' - | else if_expr - -subshell :: '{' toplevel '}' - -match_expr :: 'match' ws+ expression ws* ('as' ws+ identifier)? '{' match_entry* '}' - -match_entry :: match_pattern ws* (as identifier_list)? '{' toplevel '}' - -identifier_list :: '(' (identifier ws*)* ')' - -match_pattern :: expression (ws* '|' ws* expression)* - -command :: redirection command - | list_expression command? - -redirection :: number? '>'{1,2} ' '* string_composite - | number? '<' ' '* string_composite - | number? '>' '&' number - | number? '>' '&' '-' - -list_expression :: ' '* expression (' '+ list_expression)? - -expression :: evaluate expression? - | string_composite expression? - | comment expression? - | immediate_expression expression? - | history_designator expression? - | '(' list_expression ')' expression? - -evaluate :: '$' '(' pipe_sequence ')' - | '$' expression {eval / dynamic resolve} - -string_composite :: string string_composite? - | variable string_composite? - | bareword string_composite? - | glob string_composite? - | brace_expansion string_composite? - | heredoc_initiator string_composite? {append to heredoc_entries} - -heredoc_initiator :: '<' '<' '-' bareword {*bareword, interpolate, no deindent} - | '<' '<' '-' "'" [^']* "'" {*string, no interpolate, no deindent} - | '<' '<' '~' bareword {*bareword, interpolate, deindent} - | '<' '<' '~' "'" [^']* "'" {*bareword, no interpolate, deindent} - -string :: '"' dquoted_string_inner '"' - | "'" [^']* "'" - -dquoted_string_inner :: '\' . dquoted_string_inner? {concat} - | variable dquoted_string_inner? {compose} - | . dquoted_string_inner? - | '\' 'x' xdigit*2 dquoted_string_inner? - | '\' 'u' xdigit*8 dquoted_string_inner? - | '\' [abefrnt] dquoted_string_inner? - -variable :: variable_ref slice? - -variable_ref :: '$' identifier - | '$' '$' - | '$' '?' - | '$' '*' - | '$' '#' - | ... - -slice :: '[' brace_expansion_spec ']' - -comment :: (?<!\w) '#' .* - -immediate_expression :: '$' '{' immediate_function expression* '}' - -immediate_function :: identifier { predetermined list of names, see Shell.h:ENUMERATE_SHELL_IMMEDIATE_FUNCTIONS } - -history_designator :: '!' event_selector (':' word_selector_composite)? - -event_selector :: '!' {== '-0'} - | '?' bareword '?' - | bareword {number: index, otherwise: lookup} - -word_selector_composite :: word_selector ('-' word_selector)? - -word_selector :: number - | '^' {== 0} - | '$' {== end} - -bareword :: [^"'*$&|()[\]{} ?;<>] bareword? - | '\' [^"'*$&|()[\]{} ?;<>] bareword? - -bareword_with_tilde_expansion :: '~' bareword? - -glob :: [*?] bareword? - | bareword [*?] - -brace_expansion :: '{' brace_expansion_spec '}' - -brace_expansion_spec :: expression? (',' expression?)* - | expression '..' expression - -digit :: <native hex digit> -number :: <number in base 10> -identifier :: <string of word characters> -``` diff --git a/Base/usr/share/man/man5/SystemServer.md b/Base/usr/share/man/man5/SystemServer.md deleted file mode 100644 index 31cab7b8099..00000000000 --- a/Base/usr/share/man/man5/SystemServer.md +++ /dev/null @@ -1,93 +0,0 @@ -## Name - -SystemServer - services configuration - -## Synopsis - -```** -/etc/SystemServer.ini -``` - -## Description - -SystemServer configuration consists of a list of *services* for it to spawn. - -Each service is configured as a section in the configuration file, where the -section name is the service name and the keys inside the section are the options -describing how to launch and manage this service. - -## Options - -* `Executable` - an executable to spawn. If no explicit executable is specified, SystemServer assumes `/bin/{service name}` (for example, `/bin/WindowServer` for a service named `WindowServer`). -* `Arguments` - a space-separated list of arguments to pass to the service as `argv` (excluding `argv[0]`). By default, SystemServer does not pass any arguments other than `argv[0]`. -* `StdIO` - a path to a file to be passed as standard I/O streams to the service. By default, services run with `/dev/null` for standard I/O. -* `Priority` - the scheduling priority to set for the service, either "low", "normal", or "high". The default is "normal". -* `KeepAlive` - whether the service should be restarted if it exits or crashes. For lazy services, this means the service will get respawned once a new connection is attempted on their socket after they exit or crash. -* `Lazy` - whether the service should only get spawned once a client attempts to connect to their socket. -* `Socket` - a comma-separated list of paths to sockets to create on behalf of the service. For lazy services, SystemServer will actually watch the socket for new connection attempts. See [socket takeover mechanism](#socket-takeover-mechanism) for details on how sockets are passed to services by SystemServer. -* `SocketPermissions` - comma-separated list of (octal) file system permissions for the socket file. The default permissions are 0600. If the number of socket permissions defined is less than the number of sockets defined, then the last defined permission will be used for the remainder of the items in `Socket`. -* `User` - a name of the user to run the service as. This impacts what UID, GID (and extra GIDs) the service processes have. By default, services are run as root. -* `WorkingDirectory` - the working directory in which the service is spawned. By default, services are spawned in the root (`"/"`) directory. -* `SystemModes` - a comma-separated list of system modes in which the service should be enabled. By default, services are only enabled in the "graphical" mode. The current system mode is read from the [kernel command line](help://man/7/boot_parameters#options), and is assumed to be "graphical" if not specified there. -* `Environment` - a space-separated list of "variable=value" pairs to set in the environment for the service. -* `MultiInstance` - whether multiple instances of the service can be running simultaneously. -* `AcceptSocketConnections` - whether SystemServer should accept connections on the socket, and spawn an instance of the service for each client connection. - -Note that: -* `Lazy` requires `Socket`, but only one socket must be defined. -* `SocketPermissions` require a `Socket`. -* `MultiInstance` conflicts with `KeepAlive`. -* `AcceptSocketConnections` requires `Socket` (only one), `Lazy`, and `MultiInstance`. - -## Environment - -* `SOCKET_TAKEOVER` - set by SystemServer to describe the sockets being passed. - -## Socket takeover mechanism - -When SystemServer runs a service which has `Socket` defined, it will create the sockets and then pass an environment variable named `SOCKET_TAKEOVER` to the launched service. This environment variable is structured as follows: - -```console -SOCKET_TAKEOVER=/tmp/socket1:3;/tmp/socket2:4 -``` - -Items in the variable are separated by semicolons, and each item has two components separated by a colon. The first part is the path of the socket requested, and the second part is the file descriptor number that was passed to the newly created service. The service can then parse this information and obtain file descriptors for each socket. - -## Examples - -```ini -# Spawn the terminal as user anon once on startup. -[Terminal] -User=anon - -# Set up a socket at /tmp/portal/lookup; once a connection attempt -# is made spawn the LookupServer as user anon with a low priority. -# If it exits or crashes, repeat. -[LookupServer] -Socket=/tmp/portal/lookup -Lazy=1 -Priority=low -KeepAlive=1 -User=anon - -# Launch the Shell on /dev/tty0 on startup when booting in text mode. -[Shell@tty0] -Executable=/bin/Shell -StdIO=/dev/tty0 -Environment=TERM=xterm -KeepAlive=1 -SystemModes=text - -# Launch WindowManager with two sockets: one for main windowing operations, and -# one for window management operations. Both sockets get file permissions as 660. -[WindowServer] -Socket=/tmp/portal/window,/tmp/portal/wm -SocketPermissions=660 -Priority=high -KeepAlive=1 -User=window -``` - -## See also - -* [`SystemServer`(7)](help://man/7/SystemServer) diff --git a/Base/usr/share/man/man5/af.md b/Base/usr/share/man/man5/af.md deleted file mode 100644 index e7ba9f803d1..00000000000 --- a/Base/usr/share/man/man5/af.md +++ /dev/null @@ -1,47 +0,0 @@ -## Name - -af - Application File format (.af) - -## Synopsis - -The Application Files define System Menu entries and launcher file types / protocols. - -## Description - -Application files are a subset of the INI format. -They have no easily detectable filemagic and contain application information (App group): - -| Key | Description | -|---------------|----------------------------------| -| Name | name | -| Executable | executable path | -| Category | category (optional) | -| Description | description (optional) | -| IconPath | application icon path (optional) | -| RunInTerminal | run in terminal flag (optional) | - -and launcher information (Launcher group, optional): - -| Key | Description | -|-----------|---------------------------------------| -| FileTypes | supported file types separated by ',' | -| Protocols | protocols separated by ',' | - -All application files are stored in [`/res/apps`](../../../../res/apps). - -## Examples - -[`/res/apps/Calendar.af`](../../../../res/apps/Calendar.af) - -```ini -[App] -Name=Calendar -Executable=/bin/Calendar -Category=Utilities -``` - -## See also - -- [ini(5)](help://man/5/ini) -- [`Userland/Services/Taskbar/main.cpp`](../../../../../Userland/Services/Taskbar/main.cpp) -- `Launcher::load_handlers` in [`Userland/Services/LaunchServer/Launcher.cpp`](../../../../../Userland/Services/LaunchServer/Launcher.cpp) diff --git a/Base/usr/share/man/man5/clipboard.md b/Base/usr/share/man/man5/clipboard.md deleted file mode 100644 index d062274f472..00000000000 --- a/Base/usr/share/man/man5/clipboard.md +++ /dev/null @@ -1,51 +0,0 @@ -## Name - -clipboard - Data formats specific to Clipboard and drag & drop - -## Clipboard - -The clipboard feature works through the Clipboard server, which generally acts as a global storage or three things: -- a `ByteString` mime type, -- a (potentially large) block of data, shared as an anonymous file, -- a `HashMap<ByteString, ByteString>` of arbitrary metadata, depending on the mime type. - -See also [`Userland/Libraries/LibGUI/Clipboard.h`](../../../../../Userland/Libraries/LibGUI/Clipboard.h). - -## Drag & drop - -In contrast to the clipboard, the drag & drop feature works through WindowServer, and a bouquet of data is transmitted: -- a `[UTF8] ByteString` to be displayed while dragging, -- a `HashMap<ByteString, ByteBuffer>` map that contains arbitrary data for a variety of possible mime types, -- a `Gfx::ShareableBitmap` to be displayed while dragging - -Drag & drop is most prominently supported by File Manager, Spreadsheet, and Terminal. -Various applications accept drag & drop to open files. - -## glyph/x-fonteditor (Clipboard-only) - -Requires the metadata-fields `count` (count of glyphs copied) and `first_glyph` (lowest codepoint that is copied), encoded as decimal strings. - -The data contains code point (encoded as host-endian `u32`), width and height (as `u8`'s) and glyph bitmap data. It is encoded in width times height many bytes, either 0 (clear) or 1 (set). - -Implemented in `FontEditor::MainWidget::copy_selected_glyphs` and `FontEditor::MainWidget::paste_glyphs`, in [`Userland/Applications/FontEditor/MainWidget.cpp`](../../../../../Userland/Applications/FontEditor/MainWidget.cpp). - -## image/x-serenityos (Clipboard-only) - -Requires the metadata-fields `width`, `height`, `scale`, `format` (see `Gfx::BitmapFormat`), and `pitch`, encoded as decimal strings. - -The data is encoded according to `Gfx::determine_storage_format(BitmapFormat)`, so either as -`BGRx8888`, `BGRA8888`, `RGBA8888`, or 8-bit palette index. Note that the palette is not transferred. - -Implemented in [`Clipboard::set_bitmap` and `Clipboard::DataAndType::as_bitmap()`](../../../../../Userland/Libraries/LibGUI/Clipboard.cpp). - -## text/uri-list (Clipboard and drag & drop) - -Newline-delimited set of URIs. Used by File Manager, `FileSystemModel`, and Terminal. - -Example: - -``` -file:///home/anon/Desktop/Browser -file:///home/anon/Desktop/Help -file:///home/anon/Desktop/Home -``` diff --git a/Base/usr/share/man/man5/drag-and-drop.md b/Base/usr/share/man/man5/drag-and-drop.md deleted file mode 120000 index c45156163fc..00000000000 --- a/Base/usr/share/man/man5/drag-and-drop.md +++ /dev/null @@ -1 +0,0 @@ -clipboard.md \ No newline at end of file diff --git a/Base/usr/share/man/man5/font.md b/Base/usr/share/man/man5/font.md deleted file mode 100644 index ed1a2fe008d..00000000000 --- a/Base/usr/share/man/man5/font.md +++ /dev/null @@ -1,39 +0,0 @@ -## Name - -font - Bitmap Font File format (.font) - -## Synopsis - -Font files contain bitmap definitions of fonts (`Gfx::BitmapFont`). - -## Description - -Bitmap fonts can be either varying-width or fixed-width. -The first four bytes of font files contain the filemagic: `!Fnt` (0x21466e74). - -In addition, `Gfx::BitmapFont` supports reading from and writing to font files (as well as reading directly from memory) -and the question mark '?' used as a fallback for unknown glyphs or emojis. - -## Structure - -The order is big-endian. - -| Size | Member name | -|------------|---------------------| -| 4 bytes | Filemagic | -| 1 byte | Glyph width | -| 1 byte | Glyph height | -| 2 bytes | Range mask size | -| 1 byte | Variable width flag | -| 1 byte | Glyph spacing | -| 1 byte | Baseline | -| 1 byte | Mean line | -| 1 byte | Presentation size | -| 2 bytes | Weight | -| 1 byte | Slope | -| 32 bytes | Name | -| 32 bytes | Family | - -## See also - -- Format header definition in `Gfx::FontFileHeader` in [`Userland/Libraries/LibGfx/Font/BitmapFont.cpp`](../../../../../Userland/Libraries/LibGfx/Font/BitmapFont.cpp) diff --git a/Base/usr/share/man/man5/getopt.md b/Base/usr/share/man/man5/getopt.md deleted file mode 100644 index 331efe49e6c..00000000000 --- a/Base/usr/share/man/man5/getopt.md +++ /dev/null @@ -1,91 +0,0 @@ -## Name - -getopt - command-line options - -## Synopsis - -```**sh -$ command -o --long-option -``` - -## Description - -Most programs accept various *options* that configure their behavior to be -passed alongside other command-line arguments. Each program accepts its own set -of options, though many programs share options in common. - -Options come in two kinds, **short** and **long**. Short options have a single -letter as their name, are preceded by a single dash, and can be grouped together -in one argument. Long options have whole strings as their names, are preceded by -a double dash, and cannot be grouped. - -Each option can require (or optionally accept) a **value** (also often -confusingly called an *argument*). Generally, a value for an option, if any, -should be written after the option itself, although the exact syntax for values -of short and long options differs. In both cases, the value can be specified as -the next command-line argument after the option. For short options, the value -can also immediately follow the option as a part of the same command-line -argument. For long options, the value can follow the option as a part of the -same command-line argument, separated form it by the `=` character. - -If several short options are combined into one command line argument, only the -one specified last can be provided with a value. All the characters following -the first short option to accept (optionally or not) a value are treated as a -value for that option, and not as further options. - -Options can be freely mixed with non-option command-line arguments (with the -exception of the very first argument to be specified, which must be the command -itself). A special command-line argument value `--` can be specified to indicate -that all further command-line arguments are to be treated like non-option -arguments, even if they otherwise look like options. The `--` argument itself is -not considered to be either an option or a non-option argument, and is otherwise -ignored. - -A special argument `-` (a single dash) is always treated as a non-option -argument. - -## Examples - -Short and long options, without values or non-option arguments: - -```sh -$ command -o -$ command -vf -l -$ command --long-option -$ command --verbose --force --long -``` - -Short and long options with values: - -```sh -$ command -o rw -$ command --type text/plain -``` - -Alternative syntaxes for values: - -```sh -$ command -fttext/plain -$ command --force --type=text/plain -``` - -These two invocation are equivalent, provided the `-f` option has same effect as -`--force`, and the `-t` option has the same effect as `--type`. - -Mixing options and non-option arguments: - -```sh -$ command --force argument -$ command argument -o value another-argument -``` - -Using `--` to prevent arguments from being accidentally misinterpreted as -options: - -```sh -$ command --force -- -argument --another-argument -``` - -## See also - -* [`getopt`(3)](help://man/3/getopt) diff --git a/Base/usr/share/man/man5/ini.md b/Base/usr/share/man/man5/ini.md deleted file mode 100644 index a64bf36b13c..00000000000 --- a/Base/usr/share/man/man5/ini.md +++ /dev/null @@ -1,21 +0,0 @@ -## Name - -INI - generic config file format (.ini) - -## Description - -INI files serve as human-readable configuration files. - -They consist of key-value pairs separated by '=', optionally located under a unique group in square brackets. - -Additionally, [`Userland/Libraries/LibCore/ConfigFile.cpp`](../../../../../Userland/Libraries/LibCore/ConfigFile.cpp) -supports comments: the characters '#' and ';' skip the entire line only if they appear at the beginning of the line. - -## Examples - -[`/etc/Keyboard.ini`](../../../../etc/Keyboard.ini) - -```ini -[Mapping] -Keymaps=en-us -``` diff --git a/Base/usr/share/man/man5/installed.md b/Base/usr/share/man/man5/installed.md deleted file mode 100644 index 42c61d0dac0..00000000000 --- a/Base/usr/share/man/man5/installed.md +++ /dev/null @@ -1,41 +0,0 @@ -## Name - -installed.db - Package database format - -## Description - -`/usr/Ports/installed.db` is a file keeping track of installed packages (or ports, respectively). It is a simple text-based format suitable for editing by the port system shell scripts. - -Each line of installed.db consists of several space-separated fields. A line may also be empty and therefore ignored. - -The first field specifies what kind of entry the line contains: -- `auto` specifies a port that is installed automatically, i.e. as a dependency of another port. -- `manual` specifies a port that was installed manually. -- `dependency` does not specify a new port, but the dependencies of a port. The port also has a `manual` or `auto` line somewhere else in the file. - -The second field always specifies the port's name. - -Lines that specify an installed port (`manual`/`auto`) have a third field specifying the version. Both name and version may be found exactly like this in the package.sh script of the port, and in the list of available ports. - -Lines that specify a port's dependencies (`dependency`) have any number of additional fields. Each field contains the name of another port that this port depends on. Note that while the port scripts never create an empty dependency list, this is still valid and simply means that the port depends on nothing else. - -## Examples - -``` -manual libfftw3 3.3.10 -auto libopus 1.3.1 -auto libsamplerate 0.2.2 -auto libogg 1.3.5 -auto flac 1.4.2 -dependency flac libogg -auto lame 3.100 -auto libmpg123 1.29.3 -auto libvorbis 1.3.7 -dependency libvorbis libogg -auto sqlite 3410200 -auto libsndfile 1.2.2 -dependency libsndfile flac lame libmpg123 libogg libopus libvorbis sqlite -manual rubberband 3.3.0 -dependency rubberband libfftw3 libopus libsamplerate libsndfile -auto x264 baee400fa9ced6f5481a728138fed6e867b0ff7f -``` diff --git a/Base/usr/share/man/man5/ipc.md b/Base/usr/share/man/man5/ipc.md deleted file mode 100644 index eb61349becc..00000000000 --- a/Base/usr/share/man/man5/ipc.md +++ /dev/null @@ -1,144 +0,0 @@ -## Name - -IPC - Inter-Process Communication endpoint definition format (.ipc) - -## Synopsis - -The IPC format of SerenityOS is a domain-specific language (DSL) used to define communication endpoints for IPC. - -## Description - -Informally, IPC files - with the help of the IPC compiler - are used to generate message classes that will wrap messages -for interprocess communication in the system. IPC syntax is loosely inspired by C++ headers. Generated IPC message -classes support encode and decode functions to pass messages between the processes. - -Every IPC pair in the system has a client endpoint and a server endpoint that are described in the IPC files. -Each IPC endpoint should have a unique hashable name that will uniquely identify endpoints in the system. - -There are 2 types of APIs that are supported by the IPC files: synchronous and asynchronous. -Synchronous function calls always wait for a response from the other side, while the asynchronous counterparts do not. -In other words, in case of the synchronous calls, the IPC library will not return until it has a response for a caller. - -Ideally, all APIs for the server endpoint should be asynchronous. - -### Syntax Overview - -Each IPC endpoint definition has the form: - -```ipc -endpoint MyServer -{ - // messages... -} -``` - -You can use C++ `#include` directives before the `endpoint` keyword, which are copied to the resulting endpoint stub file. This is important if your messages use specific types of the library or application. - -Each message must appear on its own line. Synchronous messages are defined with an arrow `=>` like - -```ipc -message_name(arguments...) => (return values...) -``` - -and asynchronous messages are defined with a "stopped arrow" `=|` like - -```ipc -message_name(arguments...) =| -``` - -The argument and return value lists define what data the message passes to the other side, and what data will be retrieved back. There is no limitation to a single return value, since it will be converted to a `Messages` struct that can hold as much data as needed. The lists are defined like normal C++ formal parameter lists: - -```ipc -message_name(int first_param, bool second_param, My::Library::Type third_param) => (Optional<int> first_return, float second_return) -``` - -Currently, parameter types cannot contain spaces, so be careful with templates. - -Parameters can contain attributes, which are a comma-separated list within a `[]` block preceding the type. The only currently implemented attribute is the `UTF8` attribute for string types, which will add a run-time validation that the string is valid UTF-8. For example: - -```ipc -set_my_name([UTF8] String name) =| -``` - -For the String type in particular, this is not necessary. - -### Formal Syntax - -In Extended Backus-Naur form (and disregarding unwanted leniencies in the code generator's parser), IPC file syntax looks like the following: - -```ebnf -IPCFile = { Endpoint } ; -Endpoint = Includes, "endpoint", Identifier, "{", { Message }, "}" ; -Identifier = (* C++ identifier *) ; -Includes = { (* C++ preprocessor #include directive *) } ; - -Message = Identifier, "(", [ ParameterList ], ")", (SynchronousTrailer | AsynchronousTrailer) ; -SynchronousTrailer = "=>", "(", [ ParameterList ], ")"; -AsynchronousTrailer = "=|" ; -ParameterList = Parameter, { ",", Parameter } ; -Parameter = [ "[", AttributeList, "]" ], TypeName, Identifier ; -AttributeList = Identifier, { ",", Identifier } ; -TypeName = (* C++ type name, without spaces *) ; -``` - -## Examples - -To create a new connection, you first need to generate client and server endpoints. -These endpoints should implement the communication logic using the IPC compiler-generated API messages. - -Start from defining an endpoint in the IPC file in `MyServer.ipc`. - -```ipc -endpoint MyServer -{ - SyncAPI(ByteString text) => (i32 status) - AsyncAPI(i32 mode) =| -} -``` - -Part of the generated C++ messages: - -```cpp -class SyncAPI final : public IPC::Message { -public: - using ResponseType = SyncAPIResponse; - SyncAPI(const ByteString& text) : m_text(text) {} - virtual ~SyncAPI() override {} - static OwnPtr<SyncAPI> decode(...); - virtual IPC::MessageBuffer encode(...) const override; -}; -``` - -Then, you need to inherit your connection class from `IPC::ConnectionFromClient` with created server and client -endpoints as template parameters if it is a server connection. Otherwise, your class need to be inherited -from `IPC::ConnectionToServer` with created server and client endpoints as template parameters and from the client -endpoint class. - -Part of the connection implementations: - -```cpp -// Server side. -namespace MyServer { - -class ConnectionFromClient final - : public IPC::ConnectionFromClient<MyClientEndpoint, MyServerEndpoint> {}; - -} - -// Client side. -namespace MyClient { - - class Client final - : public IPC::ConnectionToServer<MyClientEndpoint, MyServerEndpoint> - , public MyClientEndpoint {}; - -} -``` - -Note, there are two types of functions for sending the messages: synchronous and asynchronous. The generated -asynchronous functions are prefixed with `async_` and the names of the synchronous functions are not changed. - -## See also - -- [`Meta/Lagom/Tools/CodeGenerators/IPCCompiler/main.cpp`](../../../../../Meta/Lagom/Tools/CodeGenerators/IPCCompiler/main.cpp) -- [ipc(4)](help://man/4/ipc) (IPC Unix socket documentation) diff --git a/Base/usr/share/man/man5/postcreate.md b/Base/usr/share/man/man5/postcreate.md deleted file mode 100644 index a688adef75b..00000000000 --- a/Base/usr/share/man/man5/postcreate.md +++ /dev/null @@ -1,22 +0,0 @@ -## Name - -postcreate - HackStudio postcreate scripts - -## Synopsis - -.postcreate shell scripts are executed by HackStudio after creating a new project. - -## Description - -It is possible to define project templates that set up HackStudio projects. These templates can contain custom setup logic in the form of a `*.postcreate` script in the template directory. The script name must match the template's (directory) name. Postcreate scripts are regular shell scripts. They are executed from an indeterminate directory with the following arguments: - -- The path to the postcreate script -- The name of the new project -- The path of the new project, i.e. not the parent directory it was created in -- The project name in a form that is safe for C++ namespaces - -The script may error out with a non-zero exit code, but the project is still created. The user is informed of the error. - -## See Also - -- `ProjectTemplate::create_project` in [`Userland/DevTools/HackStudio/ProjectTemplate.cpp`](../../../../../Userland/DevTools/HackStudio/ProjectTemplate.cpp). diff --git a/Base/usr/share/man/man5/pp.md b/Base/usr/share/man/man5/pp.md deleted file mode 100644 index b890a0efd40..00000000000 --- a/Base/usr/share/man/man5/pp.md +++ /dev/null @@ -1,29 +0,0 @@ -## Name - -PP - Pixel Paint application file format (.pp) - -## Description - -Pixel Paint files store the drawing data produced by the Pixel Paint application. - -This is a rough overview of the contents of the files: - -- width -- height -- layers (optional) - - width - - height - - name - - locationx - - locationy - - opacity_percent - - visible - - selected - - bitmap -- guides (optional) - - offset - - orientation - -## See also - -- [`Userland/Applications/PixelPaint/Image.cpp`](../../../../../Userland/Applications/PixelPaint/Image.cpp) diff --git a/Base/usr/share/man/man5/presenter.md b/Base/usr/share/man/man5/presenter.md deleted file mode 100644 index e2eea7a5f03..00000000000 --- a/Base/usr/share/man/man5/presenter.md +++ /dev/null @@ -1,75 +0,0 @@ -## Name - -presenter - Presenter slide presentation format (.presenter) - -## Description - -The presenter file format is a format for specifying slides and presentations in a JSON-based format. It is intentionally simple, allowing it to be written by hand. It is the native format of [Presenter](help://man/1/Applications/Presenter). - -This manpage specifies version 1 of the presenter file format. - -### Global structure - -A presenter file contains a global JSON object with the following three properties: - -- `version`: A number that is incremented every time there is a new version of this format. There are no guarantees that different formats are compatible, and Presenter will only read one version (the mainline repo version will read only the newest one). Please have a look at the history of this specification to see what has changed and how you need to modify the file. -- `metadata`: An object containing metadata about the presentation. See Metadata below. -- `templates`: An object containing named layout templates that can be re-used by slide objects. See Templates below. -- `slides`: An array containing the slides. See Slides below. - -### Metadata - -Metadata consists of simple key-value pairs of properties, of which any may or may not be present. Metadata by default only contains non-complex JSON types, however this is not required. It is allowed to store other data in the metadata object, as it is not necessary for correct Presenter behavior to read most or any of the properties in this object. - -- `author`: (string) Name of the author of this presentation. -- `last-modified`: (string containing ISO 8601 date time in UTC) Time when the presentation was last modified. This is useful as file system modification times may often be incorrect, especially when moving files around. -- `title`: (string) Title of the presentation. -- `color-scheme`: (string, currently `light` and `dark` are recognized) The color scheme of the presentation. Presenter uses this to determine which color(s) to use for black-out and white-out. Black-out always uses the background color of this color scheme and white-out uses the foreground color. The default is white. -- `width`: (float) One of the two required metadata properties. Determines the normative width of a slide in abstract units. Together with the aspect ratio, the height obtains a normative size and these two can then be used by slide objects for positioning. Note that because the displayed size of the slide will most likely not match these dimensions, the width is just important for positioning objects, not for determining how large the slide will appear. Dimensions are always translated to the actual displayed size. -- `aspect-ratio`: (string in the format `width:height`, e.g. `16:9`, width and height are integers) One of the two required metadata properties. Together with the width, determines the normative height of the slide for positioning. This is not the physical size in pixels; see the explanation on `width`. - -### Templates - -Templates provide a simple way of re-using layout data for multiple slide objects. Templates are very simple and just specify the value of some slide object properties. It is therefore possible to apply multiple templates; the later templates will override values from the previous templates and the slide object properties themselves override any template properties. - -Templates are given an ID as their key in the global templates object; this automatically ensures that template IDs are unique. The body of a template object is just the properties this template wishes to set for the slide objects that use it. - -### Slides - -The slides array contains a list of slide objects, their JSON order determines their order in the presentation. Each slide object contains these properties: - -- `title`: (string, optional) Name of this slide. It may instead be given by a title text, see below. -- `frames`: (integer, optional) Number of frames on this slide, 1 is the default and indicates no animations. -- `objects`: (array) The slide objects on this slide, see Slide Objects below. - -#### Slide Objects - -Most slide objects are graphical objects of one of the pre-defined types. All graphical objects, like GUI widgets, have a bounding box rectangle which determines their position and size. Objects choose which frames they appear on. - -In the file format, slide objects are JSON objects with the following basic properties: - -- `type`: (string enum) Specifies the type of the slide object and what other properties the object may have, see below. -- `rect`: (4-element array of integers: `[left, top, width, height]`, optional) Specifies the bounding box of the object. Is mandatory for most types. -- `frames`: (array of integers) Specifies on which frames this object is visible. The first frame is frame 0. A frame's number might exceed the number of frames in the slide, that just means that that frame specification is disregarded. -- `role`: (string, optional) Specifies a semantic role of this object in the slide. The only currently recognized role is `title`. If an object with this role has extractable text (for example, if it is a text object itself), that text will be used as the slide title if there is no slide title provided with the slide itself. -- `templates`: (array of strings, optional) IDs of templates to apply to this object. -- `color`: (string, a LibGUI-recognized color specification such as a hex color, optional) Foreground color for this object. This property applies to many graphical objects who have one foreground color, such as text or geometric primitives. - -The following types with their own special properties exist: - -- `text`: A label-like text object. The additional properties are: - - `text`: (string) The text being drawn. - - `font-size`: (integer) Font size of the text, in points relative to the relative pixel units. - - `font-weight`: (string enum, see GML) Font weight of the text. - - `font`: (string) Font of the text, must be system-wide accessible. - - `text-alignment`: (string enum, see GML) Alignment of the text within the bounding box. -- `image`: An external image. The additional properties are: - - `path`: (string) Path to the image file. This path may be relative, then it is relative to the presenter file. The image format may be any of the image formats supported by SerenityOS. - - `scaling`: (string enum, optional) Controls how the image is scaled: - - `fit-smallest`: (default) Fit the image into the bounding box, preserving its aspect ratio. - - `stretch`: Match the bounding box in width and height exactly; this will change the image's aspect ratio if the aspect ratio of the bounding box is not exactly the same. - - `fit-largest`: Make the image fill the bounding box, preserving its aspect ratio. This means that the image will be cut off on the top and bottom or left and right, depending on which dimension is "too large". This is a useful setting for background images. - -## See also - -- [Presenter(1)](help://man/1/Applications/Presenter) diff --git a/Base/usr/share/man/man6/2048.md b/Base/usr/share/man/man6/2048.md deleted file mode 100644 index 6c67a908aba..00000000000 --- a/Base/usr/share/man/man6/2048.md +++ /dev/null @@ -1,33 +0,0 @@ -## Name - -![Icon](/res/icons/16x16/app-2048.png) 2048 - -[Open](file:///bin/2048) - -## Synopsis - -```**sh -$ 2048 -``` - -## Description - -`2048` is the Serenity implementation of the 2014 game by [Gabriele Cirulli](https://github.com/gabrielecirulli/2048). - -The game begins with a 4x4 grid containing two tiles, each with a value of 2 or 4. -Use the arrow keys (or WASD) to move all tiles up, down, left, or right. When two tiles with the same number collide, they merge into one with the sum of their values. A new tile appears in a random empty space after each move. The goal is to combine tiles until they reach 2048, though gameplay can extend beyond this point. The game ends when the grid is full, and no more moves are possible. - -This implementation supports Undo (`Ctrl+Z`) and Redo (`Shift+Ctrl+Z`). - -Press `F2` to start a new game. - -## Settings - -Modify game size, goal and difficulty in the settings. The options are: - -* **Board Size** - Set the grid to the chosen number squared. -* **Target Tile** - The winning number, a power of two (e.g. 11 for 2048, 12 for 4096). -* **Evil AI** - Generates new tiles with the worst possible place and value. -* **Temporarily apply changes** - Changes to settings won't persist between sessions. - -Start a new game for changes to apply. diff --git a/Base/usr/share/man/man6/BrickGame.md b/Base/usr/share/man/man6/BrickGame.md deleted file mode 100644 index 9b567590d94..00000000000 --- a/Base/usr/share/man/man6/BrickGame.md +++ /dev/null @@ -1,52 +0,0 @@ -## Name - -![Icon](/res/icons/16x16/app-brickgame.png) BrickGame - -[Open](file:///bin/BrickGame) - -## Synopsis - -```**sh -$ BrickGame -``` - -## Description - -BrickGame is a classic game. Pieces consisting of four small squares each fall from the top of the screen, being fixed in place once they land at the very bottom or on top of other pieces. Once a piece has landed, the next piece will start falling; you can preview this piece on the right side of the screen. By filling an entire row (or line) of squares, that row will be removed and the rows above will shift down. It is also possible to clear multiple lines at once. - -You can control where and how a piece falls, by moving it left or right, making it move down faster, dropping it down instantly, or rotating it left and right. There are multiple control schemes available for these six basic actions; and the `space bar` always serves as the instant drop key. -- `Left`: Move left, `Right`: Move right, `Up`: Rotate right, `Down`: Move down -- `A`: Move left, `D`: Move right, `W`: Rotate right, `E`: Rotate left, `S`: Move down -- `H`: Move left, `L`: Move right, `K`: Rotate right - -The `Escape` and `P` keys pause and unpause the game. - -The seven pieces are commonly named "T", "J", "L", "O", "S", "Z", and "I". Note that while "J" and "L" as well as "S" and "Z" are mirrors of each other, they cannot be used interchangeably since you can only rotate pieces. - -``` -T: x x x - x - -J: x x x - x - -L: x x x - x - -O: x x - x x - -S: x x - x x - -Z: x x - x x - -I: x x x x -``` - -The game will award you points for clearing lines, and the number depends both on the number of lines cleared at once, as well as your current level. The base points are 40, 100, 300, and 1200 for one, two, three, and four lines respectively, each multiplied by your current level number. - -The game will transition to a higher level once you reach a certain score: 1000 points distance during the first ten levels, then 10,000 points distance. For example, you reach level 9 at 9000 points, level 10 at 10,000 points, but level 11 at 20,000 points. Levels determine how fast pieces drop down the screen, making it harder to play on higher levels. While the drop time is over half a second during level 1, it reduces to a tenth of a second at level 14. - -A game ends once the entire screen is filled up and you cannot drop pieces anymore. BrickGame will permanently keep track of your high score. diff --git a/Base/usr/share/man/man6/Chess.md b/Base/usr/share/man/man6/Chess.md deleted file mode 100644 index 9ef3353e6d6..00000000000 --- a/Base/usr/share/man/man6/Chess.md +++ /dev/null @@ -1,17 +0,0 @@ -## Name - -![Icon](/res/icons/16x16/app-chess.png) Chess - -[Open](file:///bin/Chess) - -## Synopsis - -```**sh -$ Chess -``` - -## Description - -Chess is an implementation of the 15th century board game. - -The game can either be played against another human or against the ChessEngine service. diff --git a/Base/usr/share/man/man6/ColorLines.md b/Base/usr/share/man/man6/ColorLines.md deleted file mode 100644 index 98e3363ff49..00000000000 --- a/Base/usr/share/man/man6/ColorLines.md +++ /dev/null @@ -1,25 +0,0 @@ -## Name - -![Icon](/res/icons/16x16/app-colorlines.png) Color Lines - -[Open](file:///bin/ColorLines) - -## Synopsis - -```**sh -$ ColorLines -``` - -## Description - -`Color Lines` is a classic puzzle game played on a 9x9 board. - -Click on a marble, then select an empty square for it to move to. Marbles can only move along unblocked paths. - -The goal is to arrange marbles of the same color in vertical, horizontal or diagonal lines. Once a line has five or more marbles it's removed from the board, scoring 2 points for each marble. - -The current score is displayed in the top-left and the high score in the top-right. - -After each move, three new marbles appear on the board. The game becomes more difficult as the board fills up and the game ends when the board is full. - -Press `F2` to start a new game. diff --git a/Base/usr/share/man/man6/FlappyBug.md b/Base/usr/share/man/man6/FlappyBug.md deleted file mode 100644 index 143defe1bc6..00000000000 --- a/Base/usr/share/man/man6/FlappyBug.md +++ /dev/null @@ -1,19 +0,0 @@ -## Name - -![Icon](/res/icons/16x16/app-flappybug.png) Flappy Bug - -[Open](file:///bin/FlappyBug) - -## Synopsis - -```**sh -$ FlappyBug -``` - -## Description - -`Flappy Bug` is a SerenityOS themed version of the 2013 game [Flappy Bird](https://en.wikipedia.org/wiki/Flappy_Bird). - -The goal is to keep Flappy Bug alive as long as possible by avoiding obstacles. Flappy Bug will automatically descend due to gravity and will ascend when you press any key. - -Press `Esc` to quit. diff --git a/Base/usr/share/man/man6/Flood.md b/Base/usr/share/man/man6/Flood.md deleted file mode 100644 index b14cd622fab..00000000000 --- a/Base/usr/share/man/man6/Flood.md +++ /dev/null @@ -1,23 +0,0 @@ -## Name - -![Icon](/res/icons/16x16/app-flood.png) Flood - -[Open](file:///bin/Flood) - -## Synopsis - -```**sh -$ Flood -``` - -## Description - -Flood is a game where the goal is to fill the entire board with cells of the same color in the most efficient way possible. - -The flooding begins from the top-left corner and continues in whichever direction chosen by selecting any cell of the wanted color. - -## Settings - -The size of the board can be changed in the settings. - -The color scheme of the board dynamically adapts to the system color scheme. diff --git a/Base/usr/share/man/man6/GameOfLife.md b/Base/usr/share/man/man6/GameOfLife.md deleted file mode 100644 index 351f3fd9d85..00000000000 --- a/Base/usr/share/man/man6/GameOfLife.md +++ /dev/null @@ -1,23 +0,0 @@ -## Name - -![Icon](/res/icons/16x16/app-gameoflife.png) GameOfLife - -[Open](file:///bin/GameOfLife) - -## Synopsis - -```**sh -$ GameOfLife -``` - -## Description - -GameOfLife is an implementation of John Conway's Game of Life. - -The game is a cellular automaton where each cell is either dead (grey) or alive (yellow) and will change state in the next tick if any of the following rules are fulfilled: - -* An alive cell will die by underpopulation if it has fewer than two neighbors. -* An alive cell will die by overpopulation if it has more than three neighbors. -* A dead cell will come alive by reproduction if it has exactly three neighbors. - -Otherwise, it will keep its old state. diff --git a/Base/usr/share/man/man6/Hearts.md b/Base/usr/share/man/man6/Hearts.md deleted file mode 100644 index 162887869b5..00000000000 --- a/Base/usr/share/man/man6/Hearts.md +++ /dev/null @@ -1,21 +0,0 @@ -## Name - -![Icon](/res/icons/16x16/app-hearts.png) Hearts - The Hearts card game - -[Open](file:///bin/Hearts) - -## Synopsis - -```**sh -$ Hearts -``` - -## Description - -Hearts is an implementation of the 19th century card game. - -The round starts by each player passing three of their cards to a neighbor. Afterwards, that a number of tricks is played. The player with the two of clubs starts the first trick. - -In each trick, the first player freely chooses a card to play (except the first trick where two of clubs is required), after which the other players each need to play a card in the same suit. If a player does not have a card in the same suit, they can throw any card. The player who played the highest ranked card in the correct suit wins the trick and gets to start the next trick. - -The winner is the player who wins the last trick. diff --git a/Base/usr/share/man/man6/MasterWord.md b/Base/usr/share/man/man6/MasterWord.md deleted file mode 100644 index b5d518be79a..00000000000 --- a/Base/usr/share/man/man6/MasterWord.md +++ /dev/null @@ -1,15 +0,0 @@ -## Name - -![Icon](/res/icons/16x16/app-masterword.png) MasterWord - -[Open](file:///bin/MasterWord) - -## Synopsis - -```**sh -$ MasterWord -``` - -## Description - -Try to guess a secret word. As you guess, letters that occur but are in the wrong spot will turn yellow and letters that are in the correct spot will turn green. diff --git a/Base/usr/share/man/man6/Minesweeper.md b/Base/usr/share/man/man6/Minesweeper.md deleted file mode 100644 index 4f97bab183e..00000000000 --- a/Base/usr/share/man/man6/Minesweeper.md +++ /dev/null @@ -1,19 +0,0 @@ -## Name - -![Icon](/res/icons/16x16/app-minesweeper.png) Minesweeper - -[Open](file:///bin/Minesweeper) - -## Synopsis - -```**sh -$ Minesweeper -``` - -## Description - -The goal is to find all the mines without detonating them. - -The player reveals what is underneath a tile by clicking on it. If it is a mine underneath the player loses. Otherwise the tile will show how many neighboring mines there are to the tile. If there are no neighboring mines, all the neighboring tiles are revealed recursively. - -The player can mark a tile as being a mine by right-clicking the tile. This is then shown with a flag. diff --git a/Base/usr/share/man/man6/Snake.md b/Base/usr/share/man/man6/Snake.md deleted file mode 100644 index a60fde084b3..00000000000 --- a/Base/usr/share/man/man6/Snake.md +++ /dev/null @@ -1,23 +0,0 @@ -## Name - -![Icon](/res/icons/16x16/app-snake.png) Snake - -[Open](file:///bin/Snake) - -## Synopsis - -```**sh -$ Snake -``` - -## Description -`Snake` is an arcade game in which you navigate Snake to eat food. With each item of food you eat Snake grows longer by one block. The challenge is to keep growing without colliding with yourself. Once this happens, the game is over. - -Use the arrow keys to move Up, Down, Left and Right or alternatively use `W`, `A`, `S`, `D`. -Moving into any edge will teleport Snake to the opposite edge. - -Press `Spacebar` to pause and unpause the game. - -Customize Snake's default color or change its appearance with one of the included skins in the `Game` menu. Bonus skins are available in the [Serenity Theming Port](https://github.com/SerenityOS/theming). - -Trivia: the food is what snakes actually eat. The glyphs are from the [SerenityOS Emoji font](https://emoji.serenityos.org). diff --git a/Base/usr/share/man/man6/Solitaire.md b/Base/usr/share/man/man6/Solitaire.md deleted file mode 100644 index 254373f8a4f..00000000000 --- a/Base/usr/share/man/man6/Solitaire.md +++ /dev/null @@ -1,58 +0,0 @@ -## Name - -![Icon](/res/icons/16x16/app-solitaire.png) Solitaire - The Solitaire card game - -[Open](file:///bin/Solitaire) - -## Synopsis - -```**sh -$ Solitaire -``` - -## Description - -Solitaire is an implementation of the classic solitaire card game Klondike, which became very popular in digital form during the 90s after being included in Microsoft Windows OS. - -The game is played with a standard 52-card deck. Objective of the game is completing four distinct ordered sequences of cards (*foundation*), one per suit, from Ace to King. - -After shuffling the deck, the game starts, with a setup (*tableau*) of seven vertical piles of cards, increasingly longer: the first pile is one card long, the seventh is seven cards long. Only the top card of each pile is upturned and accessible. The unused cards (*stock*) are kept aside, covered. - -Cards can be moved from one pile of the tableau to another only by building contiguous descending sequences of alternate color: for example, King of Diamonds / Queen of Clubs, or 5 of Spades / 4 of Diamonds / 3 of Clubs. Within that rule, multiple cards can be moved at the same time. If one of the seven piles is empty, a new pile can be built there, starting with a King. - -The top card from any pile can be detached and added to the foundation, if it is the next card in the sequence for that suit. The foundation sequence goes from the Ace up. - -When a new card reaches the top of the pile, it is turned face up. A card which is upturned is never turned face down, even if it is not at the top of the pile anymore. - -When no more moves are available, one (or, in a different variant, three) card(s) is taken from the stock and turned up. It can be added either to the tableau piles or to the foundation; if neither is possible, it goes to the waste. - -The game can conclude in one of two different ways: (a) the game is won if all 52 cards are moved to the foundation (b) the game is lost if no more progress can be achieved towards the foundation. - - -## Examples - - foundation - ♠ A ♥ 2 ♦ 3 ♣ A - - - pile 1 pile 2 ... - ♥ 3 ♦ K - ♠ Q - -Basic move: the ♠ Q can be shifted from pile 1 to pile 2, since (♦ K - ♠ Q) is a valid descending sequence. Now the ♥ 3 can be taken from pile 1 and moved to the foundation, because it is the next card in the Hearts sequence after the Two. - - - pile 1 pile 2 ... - ♥ 3 ♦ K - ♠ Q - ♥ J - -Multiple-card move: same as before, except the Q and J on pile 1 must be moved together to pile 2 to produce the sequence (♦ K - ♠ Q - ♥ J). - - - pile 1 pile 2 ... - ♥ 3 ♦ K - ♠ Q - ♠ J - -No move: no alternating-color sequence can be produced here. diff --git a/Base/usr/share/man/man6/Spider.md b/Base/usr/share/man/man6/Spider.md deleted file mode 100644 index eb819b64ac2..00000000000 --- a/Base/usr/share/man/man6/Spider.md +++ /dev/null @@ -1,60 +0,0 @@ -## Name - -![Icon](/res/icons/16x16/app-spider.png) Spider - The Spider card game - -[Open](file:///bin/Spider) - -## Synopsis - -```**sh -$ Spider -``` - -## Description - -Spider is an implementation of the classic solitaire card game with the same name, which became popular in digital form after its inclusion in Microsoft Windows XP. - -In this version, the game is played with 104 cards, forming 8 complete (A to K) instances of either 2 different suits or a single suit, as chosen from the in-game menu. - -After shuffling together the cards, the game starts, with a setup (*tableau*) of 54 cards over 10 vertical piles. Only the top card of each pile is upturned and accessible. The unused 50 cards (*stock*) are kept aside, covered, and dealt 10 at a time (1 per pile), when no more moves are possible on the current tableau. - -Objective of the game is removing all the cards from the tableau and stock. - -A single card can be moved from the top of one pile of the tableau to another, regardless of the suit, but only if its value is one less than the current top card on the destination pile: for example, a Queen of Diamonds can be moved onto a King of Spades; multiple cards can be moved at the same time only if they constitute a contiguous descending sequence of a same suit: for example, King of Diamonds / Queen of Diamonds / Jack of Diamonds. - -A complete King-to-Ace sequence of the same suit can be removed from the tableau. Any card(s) can be moved on an empty pile, respecting the aforementioned rule for multiple cards. - -When a new card reaches the top of the pile, it is turned face up. A card which is upturned is never turned face down, even if it is not at the top of the pile anymore. - -When no more moves are available, 10 more cards are moved from the stock to the piles, 1 per pile, and turned face up. - -The game can conclude in one of two different ways: (a) the game is won if all cards are removed from the tableau, and the stock is empty (b) the game is lost if no more progress can be achieved. - - -## Examples - - - - pile 1 pile 2 ... - ♥ 3 ♦ K - ♠ Q - -Basic move: the ♠ Q can be shifted from pile 1 to pile 2, since (K - Q) is a valid sequence. - - - pile 1 pile 2 ... - ♥ 3 ♦ K - ♠ Q - ♠ J - -Multiple-card move: same as before, except the Q and J on pile 1, constituting a valid in-suit sequence, can be moved together to pile 2 to produce (K - Q - J) sequence. - - - - pile 1 pile 2 ... - ♥ 3 ♥ K - ♠ K - ♥ J - -No move: no contiguous descending sequence can be produced here. - diff --git a/Base/usr/share/man/man7/Audio-subsystem.md b/Base/usr/share/man/man7/Audio-subsystem.md deleted file mode 100644 index bca4572861e..00000000000 --- a/Base/usr/share/man/man7/Audio-subsystem.md +++ /dev/null @@ -1,90 +0,0 @@ -## Name - -Overview of the SerenityOS audio subsystem, including a brief description of [`/dev/audio`](help://man/4/audio), the AudioServer and their interfaces. - -## Description - -(Note that familiarity with the basics of digitized audio, pulse code modulation (PCM), sample rate and bit depth is assumed.) - -SerenityOS structures audio into three groups of responsibilities: The Kernel audio subsystem, including drivers that talk to hardware and expose (among others) the `/dev/audio` devices; the AudioServer that is responsible for talking to userland audio clients, mixing and processing audio, and controlling the hardware via the Kernel interfaces; the audio libraries LibAudio and LibDSP that facilitate easier handling of audio data for userland applications. - -### Sample formats - -There are two primary sample formats used in SerenityOS. The `Sample` class in LibAudio provides the userland sample format. It contains 32-bit floating-point samples in multiple channels (currently 2; Stereo), which accurately represent mathematical audio signals between -1 and 1. The kernel audio interfaces use other audio formats described in [audio(4)](help://man/4/audio) which userland need not worry about. - -### AudioServer - -AudioServer is responsible for handling userland audio clients and talking to the hardware. For this reason, no userland -application should ever need to write to a device in `/dev/audio` directly, except for special cases in which -AudioServer is not present. - -As with all system servers, AudioServer provides an IPC interface on `/tmp/session/%sid/portal/audio`, with `%sid` being -the current login session id. For specifics on how to talk to AudioServer, the IPC interface specifications are the best source -of information. For controlling mixer functionality, clients have the ability to obtain and change their own volume. - -Userland audio transmission happens via the AudioQueue. This is a shared memory circular queue which supports concurrent -lock-free writing and reading. The queue is created by the audio client and its shared memory file descriptor sent to -the audio server. In order to use this queue, an audio application needs to split up its audio data into atomic chunks -that can then be provided to the queue. The application might need to wait around until the queue is empty in order to -write to it. For these reasons, there's a utility API in LibAudio which allows audio -applications to send off a large chunk of samples which get progressively sent in the background. - -On the server → client side, AudioServer has "event" calls that the client receives. These are various state changes relating to the client itself. Note that there are no "periodic" event calls relating to regular audio playback, such as a "buffer played" callback. - -#### AudioManagerServer - -AudioServer has a second IPC interface, the management interface. While the regular interface is intended for clients to be able to play audio and control the parameters of that playback, the management interface provides functionality to control AudioServer's internal behavior, such as output setup, global mixing control, as well as accessing other client's mixing properties like mute and volume. In most cases, a client needs to either access the client interface for playing audio, or the management interface for managing AudioServer itself; but not both at the same time. - -### Libraries - -There are two complementary audio libraries. - -#### LibAudio - -LibAudio is the baseline audio library that provides common audio abstractions, such as audio buffers and samples. Additionally, an important feature of LibAudio are the Loaders and Writers. The Loader class provides a multitude of audio formats (for example: WAV, FLAC, and MP3), can auto-detect the format of a file or stream and abstracts away the low-level complications of parsing and reading these formats. The Encoder class provides an abstraction over exporting audio in specific formats (for example: WAV and FLAC) to disk. - -#### LibDSP - -LibDSP is the digital signal processing library. It provides structures for audio editing programs, such as tracks and clips, while both dealing with MIDI data and sample data. More important is the Processor system, which allows synthesizers, samplers, sequencers, effects, etc. to be written with a common interface and be combined into chains for unlimited DSP (and musical) potential. The ProcessorParameters provide an interface for changing processor parameters programmatically or through a UI. - -The following class diagram outlines the structure of LibDSP pertaining to DAW-like applications: - -![LibDSP class diagram](LibDSP_classes.svg) - -LibDSP was started to support development efforts in Piano, but it is intended as a general-purpose audio processing library, building on the groundwork from LibAudio. Therefore, users of LibDSP must be familiar with LibAudio classes and concepts, as they are used extensively in LibDSP. - -LibDSP also contains a collection of general signal processing primitives, such as windowing functions and resamplers. - -### Applications - -This is a non-exhaustive list of applications that use audio. Most of these follow the good practices laid out in this manual page and may serve as a template for new audio applications. - -* **Piano** is a sequencer/tracker and synthesizer. -* [**aplay**](help://man/1/aplay) is a command line audio file playback utility. -* **SoundPlayer** is a UI audio file player with extra features such as playlist support and audio visualizations. -* [**asctl**](help://man/1/asctl) is a command line audio server control utility. -* **Applets/Audio** (AudioApplet) is a taskbar applet for setting audio parameters through a UI. -* The [Browser](help://man/1/Applications/Browser) can play audio on websites. - -### Volume - -Audio volume is more complicated than just multiplying a (digital or analog) audio signal with a percentage volume value. As the human hearing is logarithmic, volume changes also need to be logarithmic. An excellent article on the topic can be found [here](https://www.dr-lex.be/info-stuff/volumecontrols.html). - -For the SerenityOS audio system, the following applies: Userland applications and libraries that do their own volume changes need to be aware of the nature of volume. LibAudio provides utility functions for correctly handling volume, so these are to be used whenever applicable. For AudioServer, main and per-client volume is already handled correctly; to the outside, volume is linear between 0 and 1. - -For example: A program may set its client volume to 0.5 and the audio will be perceived as half as loud by a human. However, if the program wishes to change the volume beforehand, it needs to use logarithmic scaling, for example with LibAudio's built-in functionality. - -### Sample rate - -SerenityOS's audio system uses a variety of sample rates in different layers of the audio stack. For a client, one sample rate is relevant: The client's own sample rate. Audio samples passed to AudioServer are interpreted at the client sample rate, which may be changed at any time via a dedicated IPC API. The default sample rate is the current hardware sample rate, but clients are recommended to change the sample rate to whatever's most convenient for them, since this reduces the amount of resampling to be performed and therefore increases the audio quality. AudioServer uses independent hardware sample rates for audio devices, which may be configured via the management interface. - -## Files - -* [/dev/audio](help://man/4/audio) -* AudioApplet and AudioServer have settings which are managed by ConfigServer. -* `/tmp/session/%sid/portal/audio`: AudioServer's client IPC socket - -## See also - -* [asctl](help://man/1/asctl) -* [aplay](help://man/1/aplay) diff --git a/Base/usr/share/man/man7/Help-index.md b/Base/usr/share/man/man7/Help-index.md deleted file mode 100644 index 8883c6c74d1..00000000000 --- a/Base/usr/share/man/man7/Help-index.md +++ /dev/null @@ -1,21 +0,0 @@ -![Help Icon](/res/icons/32x32/app-help.png) - -## Welcome to the SerenityOS on-line help system! - -This is **Help**, the built-in documentation viewer for the SerenityOS desktop environment. If you prefer a command-line interface, the [`man`(1)](help://man/1/man) command offers a text-only view of the same library. - ---- - -Documentation is divided into traditional Unix-style manual categories. Use the **Browse** tab on the left-hand side to navigate by category. - -There's also a full-text search option under the **Search** tab. - -If you want more information on the structure of the manual system, take a look at [`man(7)`](help://man/7/man). - -### Getting Started -* [Keyboard Shortcuts](help://man/7/KeyboardShortcuts) -* [Tips and Tricks](help://man/7/Tips-and-Tricks) - ---- - -Thank you for using ***SerenityOS Help!*** \ No newline at end of file diff --git a/Base/usr/share/man/man7/KeyboardShortcuts.md b/Base/usr/share/man/man7/KeyboardShortcuts.md deleted file mode 100644 index 5006a46a56d..00000000000 --- a/Base/usr/share/man/man7/KeyboardShortcuts.md +++ /dev/null @@ -1,69 +0,0 @@ -![Keyboard Icon](/res/icons/32x32/app-keyboard-settings.png) - -## Name -Keyboard Shortcuts - -## Description -This is a collection of common keyboard shortcuts in SerenityOS. - -### Keywords -* `Super` means the ⊞ Windows or ⌘ Command key (and is sometimes called 'Meta' on Linux) -* `Alt` is equivalent to ⌥ Option on macOS - -## General -| Shortcut | Action | -|---------------|------------------------------------------------------------------------------------------------| -| `Super` | Open the System Menu (navigate via arrow keys) | -| `Super+Space` | Open [Assistant](help://man/1/Applications/Assistant) (system search and application launcher) | - -### Window Management -| Shortcut | Action | -|--------------------------|-----------------------------------------------------------------------| -| `Super+Up` | Maximize the window | -| `Super+Down` | Minimize the window (or un-maximize) | -| `Super+Left` | Snap window to left half of the screen | -| `Super+Right` | Snap window to right half of the screen | -| `Super+LeftMouseButton` | Move window (grabbing anywhere within) | -| `Super+RightMouseButton` | Resize window (in the direction of mouse drag) | -| `F11` | Fullscreen mode (only available in some applications) | -| `Alt+Spacebar` | Open the window's context menu | -| `Super+D` | Show and hide the Desktop | -| `Alt+Tab` | Application Switcher (`Shift` reverses direction) | -| `Super+Number` | Switch to application in taskbar position 1-9 (or minimize if active) | -| `Ctrl+Alt+Arrows` | Move Workspace | -| `Shift+Ctrl+Alt+Arrows` | Move Workspace with the active window | - -### Common within Applications -| Shortcut | Action | -|----------------|---------------------------------------------------------------| -| `Ctrl+Z` | Undo | -| `Ctrl+Shift+Z` | Redo | -| `Ctrl+A` | Select All | -| `Ctrl+X` | Cut | -| `Ctrl+C` | Copy | -| `Ctrl+V` | Paste | -| `Ctrl+O` | Open File | -| `Ctrl+S` | Save | -| `Ctrl+Shift+S` | Save As | -| `Ctrl+Shift+A` | Open Command Palette (a searchable list of available actions) | -| `Alt+Left` | Go Back | -| `Alt+Right` | Go Forward | -| `Ctrl+Right` | Expand Tree Node | -| `Ctrl+Left` | Collapse Tree Node | -| `F1` | Open the application's Manual | -| `Tab` | Cycle focus within an application | -| `Alt+F4` | Quit | - -### Text Input -| Shortcut | Action | -|------------------|-------------------------------------------------------| -| `Ctrl+Alt+Space` | Emoji Picker | -| `Shift+Alt` | Hold `Shift` and press `Alt` to cycle through keymaps | - -### Accessibility -| Shortcut | Action | -|-----------|------------------| -| `Super+H` | Highlight Cursor | - -## See also -* [Tips and Tricks](help://man/7/Tips-and-Tricks) \ No newline at end of file diff --git a/Base/usr/share/man/man7/LibDSP_classes.svg b/Base/usr/share/man/man7/LibDSP_classes.svg deleted file mode 100644 index 191477ef409..00000000000 --- a/Base/usr/share/man/man7/LibDSP_classes.svg +++ /dev/null @@ -1,4 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Do not edit this file with editors other than diagrams.net --> -<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> -<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="1051px" height="853px" viewBox="-0.5 -0.5 1051 853" content="<mxfile host="app.diagrams.net" modified="2022-05-14T10:09:11.623Z" agent="5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/101.0.4951.54 Safari/537.36" etag="0ssGZdW2Dw8UpAhAyTj3" version="17.5.0" type="device"><diagram id="C5RBs43oDa-KdzZeNtuy" name="Page-1">7V1bd5u4Gv0t58FrdR7ixR38GOfS6Zmk02na03XmTTay0QQjD8hJPL9+JBDYSMbGDjiUKKtr1cggLtra39bWhzwwrxYvH2OwDO6xD8OBofkvA/N6YBi6q3v0P1ayzkpGupUVzGPk8502BQ/oH8gLNV66Qj5MSjsSjEOCluXCKY4iOCWlMhDH+Lm82wyH5bMuwRxKBQ9TEMqlP5BPgqzUM9xN+a8QzYP8zLozyr6ZgOnjPMariJ9vYJiz9C/7egHyuviNJgHw8fNWkXkzMK9ijEn2afFyBUP2bPPH9uPT+kd49+h8/O8fyd/g+/i3b5//d5FVdnvMIcUdxjAiJ1f99/Wf0e+//gW9j3dfPDe49T89LS/ytn4C4Yo/z+8J5PdL1vkzhpF/yZqKbuEljAbmmJZwKOj0QYx9kASQnUinGwFZhPxj+njTLzS6ldULfalBD9wY3y/Bq3gK990Nhx+I55Ds288omo92C4gXkMRreiDvE9pQszTeK543kNIdDoQYhoCgp/JNAA7jeVFdcYYvGNHbK6rX857DO5yZAyyvIrtPftR2iwoVmc6BirIHIVVEmxKst3Zbsh2SPRdslc/jaXsvS9zdtEr70w/ZBQhH51eDZ7MEEuGIrWbaFKUAPwbsEtZ/g+sJBrEvAT55RosQRHRrPMMReeDfMEiDEM1pD7ieUszCmBY8wZggykeX/AuCl7R0GqDQvwNrvGJITAglm3xrHOAY/UOrBXk3oV/HhPcnwynt8cCO5P0nhgnd50veXXSh6B68lHa8AwnhBVMchmCZoElxGwuKDRSNMSF4Ueqe7Hbgy+D4DlrZn3StDIgc/7v6VrBF1a4A5x1d6yuNKCCa07vanM0UzjaSTzfacTb63EtnAyFt3ggQOGYclrQBSEMC5P2n608D1o0dsFimTaI9oZisAKvrsUCrhqLlikiope1GUjTF+BFe4RBTeF5HOIMxCkOhKEdyCGekEsfJEkxRNL9L97m2NiVf+aNjRZgeOwvT+BAg308jRIwJICADHEMXZxl6ofaY/qP3eKUN7YFNL/yKbuubbfqP7R6TKxzRewEoxR6kaH6GDNHj5wAR+EAvg9X9TGVNXfBW04IM3nUZFofAalnVYC3h5liQOBJIvsUgStjzUbTVJm1ZhqKtnYh0JUR+DPEkpSiCFpQaWI82tBBTRKW6n/IR/ZJeqKmzBvsAaWOtGQcZ2iqhYwd6XICSXxSfNcJnTrf5zNvFZ9PHexDRkVqsKI3tFIIJDMfF+DTHuQ9nYBWSFjnPEIYUuqtIL4PtSILtJ4a6ixS8DLIUcikF0hYjKQcqMivIrBk8V9NJd9kuFw1buNElaMj2ypaFIjgtKUHle/sILHDkfwtQJHCXbuUFt4hdcdrKzHnJCZS2YIDnmGL2ZlN6CHUTTlJn9nV4Ix70dSqwwBF1oQ09a6eie6WhU1BUXq13oqFj2QcqasjQscUBubnf0TGtvfu/oaMjj6A5HysVcT6RYGojJRIyPJoSHsEjGNKSqwBEEZt5UaqgCcumwsDvTNS3JRx0OOifeZLGeF00bzhYG2ZDwVqqqCJYN8Y1sjGoDYeRjDPakndMf+dxZKuDbyGuFrPU1PF8qpefaFDMoJ7U1e39ok5vRMIJU3IXYlu2KGFkP2QfVUxCzOTDNkE42eZWl3+7CdhRzb5dReBtTLcK/dSyR6d1eFM/UFFD6lxU25Y1EoC3pbYbg6Hsb3zGBCo1ffbZUUtXappPj8reSYrHJL1aerGGls2X3jKo0i2fqlcJrEphnxR2R7tB2xWFnd/IFjYuVz7CirDOPi+qCKsAZYUdtUVYgIGUVp6AxTJUnNUoZxkdz+QwZHdIavmfRuoXub4dkvoFw+TM5OhDu1xJbbEvjBpMr09iXw6ealKqrWTjt5qUskbD0daf65bPcmrSsbe32uLqD3STOpNER6cpe4JQaHJWqzGJICfcX4VoKfU+pVgbVKzihJVt1VSs4vxr/xSrPFHxe5qXx7L20HyFV0lKRXOEI/oBzxjj8YyXTLlqVMNNmbIlAc/ni9lJGTTBlGyUr1K4TShcazfOj1S4dmsKV56TYNaMYrhzm4hOXYbr/5hczla+pJuTVZSyVspo/K2LiEI1UUzVDFN1PAvZkKfdUv9QUdW57UNFVQUm5Tk4RlVLBKcwpyruHyqOaoKjOp47nCO3H35h3STeKo3bhl8ojAud0el+oWAmOPp5/ELHPYNfaMp+4U8MRK+DQHSbA6JgXDtmn4BoHAXEd5VXWZtgq6b2X4thYQhqizMmtRMrhXpqMmljGJNn6fqYV5l1pf7mVZqy0a6o4ugQWDGUeyUqbFF3WSdShViRVXOWtjGQyfZ5L7miQgn1hitk2/orDsMsq1B5QeezrV1PeUEckrtt6y2nWkKmMoFO4raOG9WmbFQ/8NzB7jCTqfWNmUSXWjFTDkhLDpYfZjFtEEO7Q5N0EkWt5NMQN+03qE0ZI7u5aQ8kX8VN1nG+4LsaY5m1x1jtvOcqvp46OnWMJVbknnmMZcmeXx/HWNb+ePTTj7Gs45Lf3xdX1J4bqxCrr7VuBbHjaSdyhViRW3PyoTGQyaZfL7nC7DlXyIPfLzGewiShj7ZDw57+GTKeYMiopX0KTMrj8A8+miOSLvqXrf73C2uADKjpCoCllB0NRP5AJRk2OT7KeKK73k0uuZXmqeb4k98ffGV0k17wE6eZT024MGyBClvWPPnp+q55KrIWmtU8F+abiR5bNlNuZjP6+JT0OYv0ccQ3cdWyBjkyZQfmkmsaGnvStd5zjYPVr1I0pG3sji9mYMt+zsM6IkHap2PFWWfhrJGY/6I4K4en7AR9zkZeirPa46wKX6oznCX7SrJM/lny2HMCPjiAqholt5DHPnK1obb1Z5TowjaNU7PavdG+ii2n3pDr2Bx3T1hRIp8KazXH3e7TgqD5oKZjIBXQY5wMylJFba0R9DYwlL2ra/rc11nsvPkj+/9yscw+DIdDJfXYTqVfQm1L94ljVbtuKkjvdV9+01uw/T/tzozkUjMlvcIY/kcpvmYUX8eXCc2pvR/BtO7q2lXeQQvB1BMXxe5OMG2cWmTH4/aeh8EIhJj/7OEP8ASzvqsC49kDo2iIqMBYoFc2RHhgTJhtp+Jis7/B2XEnxDnuB2q6HReduk5IlT/Vxq9OiNRwcmDUNXFiqnuRUX4loJgAoMED0PrVL7ueN3Gr9g+39n6JS0c2fb9HaIYoPdHrmDKUMu5gMwHLzawVa48Mt0mm4yJM6EFTygnpSphgkk4baNOAPpl0WgGSqaz0VPQ8KXo2805ea8tcOsdZtO8qr8upm8te5fi+Pq9rz0zBa7K89lR77pwvRzZn+5jz5RxYO72VPHdxBfP2Mr5c2assZNNXFlaUdjpP5pfw1oqtpBMHqJySOEMv0L9Y8h603AA0/4GbBYrQYrXIRNMCvGw2svz3JU5YM7PHH+I5iOlRCzRl6KYQTbPmlYRqQEJl3NJdCeUaR0mobhsQOZEf1DxVwrYNY15wDbyTVxQciW8HihW9uf/gys58EUhvotVCxdE38SBUHM3xKXvvOyInjFik5DupKNhIFGzGhm8vCso2fEFcY4xDCCLFXefJpPYOc5f7LrlL9vZnIZhfhOgRlkcAirGaYKyKn7vfw1i7YNkeY/UphdqtO3F4Rt3uCtmpurh6U+2EGpFjRKp6e9nep0znPJQrLDWMJboZY0y2d6ckG9xjn9naN/8C</diagram></mxfile>" style="background-color: rgb(255, 255, 255);"><defs/><g><path d="M 40 70 L 40 330 L 157.76 332.36" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" stroke-dasharray="3 3" pointer-events="stroke"/><path d="M 145.75 338.62 L 158.88 332.38 L 146.01 325.62" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 268px; margin-left: 40px;"><div data-drawio-colors="color: rgb(0, 0, 0); background-color: rgb(255, 255, 255); " style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 11px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; background-color: rgb(255, 255, 255); white-space: nowrap;">Use</div></div></div></foreignObject><text x="40" y="271" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="11px" text-anchor="middle">Use</text></switch></g><path d="M 0 26 L 0 0 L 160 0 L 160 26" fill="rgb(255, 255, 255)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="all"/><path d="M 0 26 L 0 70 L 160 70 L 160 26" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="none"/><path d="M 0 26 L 160 26" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="none"/><g fill="rgb(0, 0, 0)" font-family="Helvetica" font-weight="bold" pointer-events="none" text-anchor="middle" font-size="12px"><text x="79.5" y="17.5">Keyboard</text></g><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe flex-start; justify-content: unsafe flex-start; width: 150px; height: 1px; padding-top: 33px; margin-left: 6px;"><div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: left; max-height: 40px; overflow: hidden;"><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: none; white-space: normal; overflow-wrap: normal;">MIDI & virtual keyboard input</div></div></div></foreignObject><text x="6" y="45" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px">MIDI & virtual keyboard i...</text></switch></g><path d="M 320 26 L 320 0 L 480 0 L 480 26" fill="rgb(255, 255, 255)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="none"/><path d="M 320 26 L 320 70 L 480 70 L 480 26" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="none"/><path d="M 320 26 L 480 26" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="none"/><g fill="rgb(0, 0, 0)" font-family="Helvetica" font-weight="bold" pointer-events="none" text-anchor="middle" font-size="12px"><text x="399.5" y="17.5">Transport</text></g><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe flex-start; justify-content: unsafe flex-start; width: 150px; height: 1px; padding-top: 33px; margin-left: 326px;"><div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: left; max-height: 40px; overflow: hidden;"><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: none; white-space: normal; overflow-wrap: normal;">Global timing, loop control<br />(everyone uses this)</div></div></div></foreignObject><text x="326" y="45" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px">Global timing, loop contr...</text></switch></g><path d="M 160 186 L 160 160 L 320 160 L 320 186" fill="rgb(255, 255, 255)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="none"/><path d="M 160 186 L 160 230 L 320 230 L 320 186" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="none"/><path d="M 160 186 L 320 186" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="none"/><g fill="rgb(0, 0, 0)" font-family="Helvetica" font-weight="bold" pointer-events="none" text-anchor="middle" font-size="12px"><rect fill="rgb(255, 255, 255)" stroke="none" x="199" y="168" width="83" height="15" stroke-width="0"/><text x="239.5" y="177.5">TrackManager</text></g><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe flex-start; justify-content: unsafe flex-start; width: 150px; height: 1px; padding-top: 193px; margin-left: 166px;"><div data-drawio-colors="color: rgb(0, 0, 0); background-color: rgb(255, 255, 255); " style="box-sizing: border-box; font-size: 0px; text-align: left; max-height: 40px; overflow: hidden;"><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: none; background-color: rgb(255, 255, 255); white-space: normal; overflow-wrap: normal;">Inter-Track signal routing</div></div></div></foreignObject><text x="166" y="205" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px">Inter-Track signal routing</text></switch></g><path d="M 400 85.99 L 400 120 L 240 120 L 240 157.76" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="none"/><path d="M 400 70.99 L 404.41 78.49 L 400 85.99 L 395.59 78.49 Z" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="none"/><path d="M 233.5 145.88 L 240 158.88 L 246.5 145.88" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="none"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe flex-end; justify-content: unsafe flex-start; width: 1px; height: 1px; padding-top: 87px; margin-left: 412px;"><div data-drawio-colors="color: rgb(0, 0, 0); background-color: rgb(255, 255, 255); " style="box-sizing: border-box; font-size: 0px; text-align: left;"><div style="display: inline-block; font-size: 11px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: none; background-color: rgb(255, 255, 255); white-space: nowrap;">1</div></div></div></foreignObject><text x="412" y="87" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="11px">1</text></switch></g><path d="M 160 325 L 160 299 L 320 299 L 320 325" fill="rgb(255, 255, 255)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="none"/><path d="M 160 325 L 160 369 L 320 369 L 320 325" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="none"/><path d="M 160 325 L 320 325" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="none"/><g fill="rgb(0, 0, 0)" font-family="Helvetica" font-weight="bold" pointer-events="none" text-anchor="middle" font-size="12px"><text x="239.5" y="316.5">Track</text></g><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe flex-start; justify-content: unsafe flex-start; width: 150px; height: 1px; padding-top: 332px; margin-left: 166px;"><div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: left; max-height: 40px; overflow: hidden;"><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: none; white-space: normal; overflow-wrap: normal;">aka. Channel</div></div></div></foreignObject><text x="166" y="344" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px">aka. Channel</text></switch></g><path d="M 240 283.01 L 240 232.24" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="none"/><path d="M 240 298.01 L 235.59 290.51 L 240 283.01 L 244.41 290.51 Z" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="none"/><path d="M 246.5 244.12 L 240 231.12 L 233.5 244.12" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="none"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe flex-start; justify-content: unsafe flex-start; width: 1px; height: 1px; padding-top: 276px; margin-left: 252px;"><div data-drawio-colors="color: rgb(0, 0, 0); background-color: rgb(255, 255, 255); " style="box-sizing: border-box; font-size: 0px; text-align: left;"><div style="display: inline-block; font-size: 11px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: none; background-color: rgb(255, 255, 255); white-space: nowrap;">0..n</div></div></div></foreignObject><text x="252" y="287" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="11px">0..n</text></switch></g><path d="M 160 441.5 L 240 439 L 240 387.12" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="none"/><path d="M 240 370.12 L 248.5 387.12 L 231.5 387.12 Z" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="none"/><path d="M 0 435 L 0 409 L 160 409 L 160 435" fill="rgb(255, 255, 255)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="none"/><path d="M 0 435 L 0 479 L 160 479 L 160 435" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="none"/><path d="M 0 435 L 160 435" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="none"/><g fill="rgb(0, 0, 0)" font-family="Helvetica" font-weight="bold" pointer-events="none" text-anchor="middle" font-size="12px"><text x="79.5" y="426.5">NoteTrack</text></g><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe flex-start; justify-content: unsafe flex-start; width: 150px; height: 1px; padding-top: 442px; margin-left: 6px;"><div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: left; max-height: 40px; overflow: hidden;"><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: none; white-space: normal; overflow-wrap: normal;">Tracks with MIDI/Note data</div></div></div></foreignObject><text x="6" y="454" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px">Tracks with MIDI/Note data</text></switch></g><path d="M 320 435 L 320 409 L 480 409 L 480 435" fill="rgb(255, 255, 255)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="none"/><path d="M 320 435 L 320 479 L 480 479 L 480 435" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="none"/><path d="M 320 435 L 480 435" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="none"/><g fill="rgb(0, 0, 0)" font-family="Helvetica" font-weight="bold" pointer-events="none" text-anchor="middle" font-size="12px"><text x="399.5" y="426.5">AudioTrack</text></g><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe flex-start; justify-content: unsafe flex-start; width: 150px; height: 1px; padding-top: 442px; margin-left: 326px;"><div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: left; max-height: 40px; overflow: hidden;"><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: none; white-space: normal; overflow-wrap: normal;">Tracks with audio/sample data</div></div></div></foreignObject><text x="326" y="454" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px">Tracks with audio/sample...</text></switch></g><path d="M 320 441.5 L 240 439 L 240 387.12" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="none"/><path d="M 240 370.12 L 248.5 387.12 L 231.5 387.12 Z" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="none"/><path d="M 80 85.99 L 80 120 L 240 120 L 240 157.76" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="none"/><path d="M 80 70.99 L 84.41 78.49 L 80 85.99 L 75.59 78.49 Z" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="none"/><path d="M 233.5 145.88 L 240 158.88 L 246.5 145.88" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="none"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe flex-end; justify-content: unsafe flex-start; width: 1px; height: 1px; padding-top: 87px; margin-left: 92px;"><div data-drawio-colors="color: rgb(0, 0, 0); background-color: rgb(255, 255, 255); " style="box-sizing: border-box; font-size: 0px; text-align: left;"><div style="display: inline-block; font-size: 11px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: none; background-color: rgb(255, 255, 255); white-space: nowrap;">1</div></div></div></foreignObject><text x="92" y="87" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="11px">1</text></switch></g><path d="M 160 565 L 160 539 L 320 539 L 320 565" fill="rgb(255, 255, 255)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="none"/><path d="M 160 565 L 160 619 L 320 619 L 320 565" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="none"/><path d="M 160 565 L 320 565" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="none"/><g fill="rgb(0, 0, 0)" font-family="Helvetica" font-weight="bold" pointer-events="none" text-anchor="middle" font-size="12px"><text x="239.5" y="556.5">Clip</text></g><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe flex-start; justify-content: unsafe flex-start; width: 150px; height: 1px; padding-top: 572px; margin-left: 166px;"><div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: left; max-height: 50px; overflow: hidden;"><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: none; white-space: normal; overflow-wrap: normal;">One contiguous region of track data which the user interacts with</div></div></div></foreignObject><text x="166" y="584" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px">One contiguous region of...</text></switch></g><path d="M 0 665 L 0 639 L 160 639 L 160 665" fill="rgb(255, 255, 255)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="none"/><path d="M 0 665 L 0 709 L 160 709 L 160 665" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="none"/><path d="M 0 665 L 160 665" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="none"/><g fill="rgb(0, 0, 0)" font-family="Helvetica" font-weight="bold" pointer-events="none" text-anchor="middle" font-size="12px"><text x="79.5" y="656.5">NoteClip</text></g><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe flex-start; justify-content: unsafe flex-start; width: 150px; height: 1px; padding-top: 672px; margin-left: 6px;"><div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: left; max-height: 40px; overflow: hidden;"><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: none; white-space: normal; overflow-wrap: normal;">A bunch of MIDI notes</div></div></div></foreignObject><text x="6" y="684" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px">A bunch of MIDI notes</text></switch></g><path d="M 320 665 L 320 639 L 480 639 L 480 665" fill="rgb(255, 255, 255)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="none"/><path d="M 320 665 L 320 709 L 480 709 L 480 665" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="none"/><path d="M 320 665 L 480 665" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="none"/><g fill="rgb(0, 0, 0)" font-family="Helvetica" font-weight="bold" pointer-events="none" text-anchor="middle" font-size="12px"><text x="399.5" y="656.5">AudioClip</text></g><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe flex-start; justify-content: unsafe flex-start; width: 150px; height: 1px; padding-top: 672px; margin-left: 326px;"><div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: left; max-height: 40px; overflow: hidden;"><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: none; white-space: normal; overflow-wrap: normal;">A piece of audio</div></div></div></foreignObject><text x="326" y="684" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px">A piece of audio</text></switch></g><path d="M 160 671.5 L 240 669 L 240 637.12" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="none"/><path d="M 240 620.12 L 248.5 637.12 L 231.5 637.12 Z" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="none"/><path d="M 320 671.5 L 240 669 L 240 637.12" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="none"/><path d="M 240 620.12 L 248.5 637.12 L 231.5 637.12 Z" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="none"/><path d="M 80 623.01 L 80 481.24" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="none"/><path d="M 80 638.01 L 75.59 630.51 L 80 623.01 L 84.41 630.51 Z" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="none"/><path d="M 86.5 493.12 L 80 480.12 L 73.5 493.12" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="none"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe flex-start; justify-content: unsafe flex-start; width: 1px; height: 1px; padding-top: 616px; margin-left: 92px;"><div data-drawio-colors="color: rgb(0, 0, 0); background-color: rgb(255, 255, 255); " style="box-sizing: border-box; font-size: 0px; text-align: left;"><div style="display: inline-block; font-size: 11px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: none; background-color: rgb(255, 255, 255); white-space: nowrap;">0..n</div></div></div></foreignObject><text x="92" y="627" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="11px">0..n</text></switch></g><path d="M 400 623.01 L 400 481.24" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="none"/><path d="M 400 638.01 L 395.59 630.51 L 400 623.01 L 404.41 630.51 Z" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="none"/><path d="M 406.5 493.12 L 400 480.12 L 393.5 493.12" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="none"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe flex-start; justify-content: unsafe flex-start; width: 1px; height: 1px; padding-top: 616px; margin-left: 412px;"><div data-drawio-colors="color: rgb(0, 0, 0); background-color: rgb(255, 255, 255); " style="box-sizing: border-box; font-size: 0px; text-align: left;"><div style="display: inline-block; font-size: 11px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: none; background-color: rgb(255, 255, 255); white-space: nowrap;">0..n</div></div></div></foreignObject><text x="412" y="627" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="11px">0..n</text></switch></g><path d="M 0 805 L 0 779 L 160 779 L 160 805" fill="rgb(255, 255, 255)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="none"/><path d="M 0 805 L 0 849 L 160 849 L 160 805" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="none"/><path d="M 0 805 L 160 805" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="none"/><g fill="rgb(0, 0, 0)" font-family="Helvetica" font-weight="bold" pointer-events="none" text-anchor="middle" font-size="12px"><text x="79.5" y="796.5">RollNote</text></g><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe flex-start; justify-content: unsafe flex-start; width: 150px; height: 1px; padding-top: 812px; margin-left: 6px;"><div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: left; max-height: 40px; overflow: hidden;"><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: none; white-space: normal; overflow-wrap: normal;">A MIDI note</div></div></div></foreignObject><text x="6" y="824" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px">A MIDI note</text></switch></g><path d="M 320 809 L 320 779 L 480 779 L 480 809" fill="rgb(255, 255, 255)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="none"/><path d="M 320 809 L 320 849 L 480 849 L 480 809" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="none"/><path d="M 320 809 L 480 809" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="none"/><g fill="rgb(0, 0, 0)" font-family="Helvetica" font-weight="bold" pointer-events="none" text-anchor="middle" font-size="12px"><text x="399.5" y="796.5">Sample</text></g><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe flex-start; justify-content: unsafe flex-start; width: 150px; height: 1px; padding-top: 816px; margin-left: 326px;"><div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: left; max-height: 36px; overflow: hidden;"><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: none; white-space: normal; overflow-wrap: normal;">(from LibAudio)</div></div></div></foreignObject><text x="326" y="828" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px">(from LibAudio)</text></switch></g><path d="M 400 763.01 L 400 711.24" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="none"/><path d="M 400 778.01 L 395.59 770.51 L 400 763.01 L 404.41 770.51 Z" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="none"/><path d="M 406.5 723.12 L 400 710.12 L 393.5 723.12" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="none"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe flex-start; justify-content: unsafe flex-start; width: 1px; height: 1px; padding-top: 756px; margin-left: 412px;"><div data-drawio-colors="color: rgb(0, 0, 0); background-color: rgb(255, 255, 255); " style="box-sizing: border-box; font-size: 0px; text-align: left;"><div style="display: inline-block; font-size: 11px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: none; background-color: rgb(255, 255, 255); white-space: nowrap;">0..n</div></div></div></foreignObject><text x="412" y="767" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="11px">0..n</text></switch></g><path d="M 80 763.01 L 80 711.24" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="none"/><path d="M 80 778.01 L 75.59 770.51 L 80 763.01 L 84.41 770.51 Z" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="none"/><path d="M 86.5 723.12 L 80 710.12 L 73.5 723.12" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="none"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe flex-start; justify-content: unsafe flex-start; width: 1px; height: 1px; padding-top: 756px; margin-left: 92px;"><div data-drawio-colors="color: rgb(0, 0, 0); background-color: rgb(255, 255, 255); " style="box-sizing: border-box; font-size: 0px; text-align: left;"><div style="display: inline-block; font-size: 11px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: none; background-color: rgb(255, 255, 255); white-space: nowrap;">0..n</div></div></div></foreignObject><text x="92" y="767" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="11px">0..n</text></switch></g><path d="M 700 325 L 700 299 L 860 299 L 860 325" fill="rgb(255, 255, 255)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="none"/><path d="M 700 325 L 700 369 L 860 369 L 860 325" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="none"/><path d="M 700 325 L 860 325" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="none"/><g fill="rgb(0, 0, 0)" font-family="Helvetica" font-weight="bold" pointer-events="none" text-anchor="middle" font-size="12px"><text x="779.5" y="316.5">Processor</text></g><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe flex-start; justify-content: unsafe flex-start; width: 150px; height: 1px; padding-top: 332px; margin-left: 706px;"><div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: left; max-height: 40px; overflow: hidden;"><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: none; white-space: normal; overflow-wrap: normal;">(digital signal) processing of audio and notes</div></div></div></foreignObject><text x="706" y="344" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px">(digital signal) processi...</text></switch></g><path d="M 684.01 334 L 322.24 334" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="none"/><path d="M 699.01 334 L 691.51 338.41 L 684.01 334 L 691.51 329.59 Z" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="none"/><path d="M 334.12 327.5 L 321.12 334 L 334.12 340.5" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="none"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe flex-start; justify-content: unsafe flex-start; width: 1px; height: 1px; padding-top: 311px; margin-left: 672px;"><div data-drawio-colors="color: rgb(0, 0, 0); background-color: rgb(255, 255, 255); " style="box-sizing: border-box; font-size: 0px; text-align: left;"><div style="display: inline-block; font-size: 11px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: none; background-color: rgb(255, 255, 255); white-space: nowrap;">0..n</div></div></div></foreignObject><text x="672" y="322" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="11px">0..n</text></switch></g><path d="M 540 435 L 540 409 L 700 409 L 700 435" fill="rgb(255, 255, 255)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="none"/><path d="M 540 435 L 540 479 L 700 479 L 700 435" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="none"/><path d="M 540 435 L 700 435" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="none"/><g fill="rgb(0, 0, 0)" font-family="Helvetica" font-weight="bold" pointer-events="none" text-anchor="middle" font-size="12px"><text x="619.5" y="426.5">EffectProcessor</text></g><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe flex-start; justify-content: unsafe flex-start; width: 150px; height: 1px; padding-top: 442px; margin-left: 546px;"><div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: left; max-height: 40px; overflow: hidden;"><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: none; white-space: normal; overflow-wrap: normal;">Audio in, audio out</div></div></div></foreignObject><text x="546" y="454" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px">Audio in, audio out</text></switch></g><path d="M 860 435 L 860 409 L 1020 409 L 1020 435" fill="rgb(255, 255, 255)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="none"/><path d="M 860 435 L 860 479 L 1020 479 L 1020 435" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="none"/><path d="M 860 435 L 1020 435" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="none"/><g fill="rgb(0, 0, 0)" font-family="Helvetica" font-weight="bold" pointer-events="none" text-anchor="middle" font-size="12px"><text x="939.5" y="426.5">SynthesizerProcessor</text></g><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe flex-start; justify-content: unsafe flex-start; width: 150px; height: 1px; padding-top: 442px; margin-left: 866px;"><div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: left; max-height: 40px; overflow: hidden;"><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: none; white-space: normal; overflow-wrap: normal;">Notes in, audio out</div></div></div></foreignObject><text x="866" y="454" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px">Notes in, audio out</text></switch></g><path d="M 860 442 L 780 440 L 780 387.12" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="none"/><path d="M 780 370.12 L 788.5 387.12 L 771.5 387.12 Z" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="none"/><path d="M 700 442 L 780 440 L 780 387.12" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="none"/><path d="M 780 370.12 L 788.5 387.12 L 771.5 387.12 Z" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="none"/><path d="M 540 546 L 540 520 L 700 520 L 700 546" fill="rgb(255, 255, 255)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" stroke-dasharray="3 3" pointer-events="none"/><path d="M 540 546 L 540 590 L 700 590 L 700 546" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" stroke-dasharray="3 3" pointer-events="none"/><path d="M 540 546 L 700 546" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" stroke-dasharray="3 3" pointer-events="none"/><g fill="rgb(0, 0, 0)" font-family="Helvetica" font-weight="bold" pointer-events="none" text-anchor="middle" font-size="12px"><text x="619.5" y="537.5">Delay, EQ, Amp, ...</text></g><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe flex-start; justify-content: unsafe flex-start; width: 150px; height: 1px; padding-top: 553px; margin-left: 546px;"><div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: left; max-height: 40px; overflow: hidden;"><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: none; white-space: normal; overflow-wrap: normal;">Your effect here!</div></div></div></foreignObject><text x="546" y="565" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px">Your effect here!</text></switch></g><path d="M 620 520 L 620 497.12" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="none"/><path d="M 620 480.12 L 628.5 497.12 L 611.5 497.12 Z" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="none"/><path d="M 860 546 L 860 520 L 1020 520 L 1020 546" fill="rgb(255, 255, 255)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" stroke-dasharray="3 3" pointer-events="none"/><path d="M 860 546 L 860 590 L 1020 590 L 1020 546" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" stroke-dasharray="3 3" pointer-events="none"/><path d="M 860 546 L 1020 546" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" stroke-dasharray="3 3" pointer-events="none"/><g fill="rgb(0, 0, 0)" font-family="Helvetica" font-weight="bold" pointer-events="none" text-anchor="middle" font-size="12px"><text x="939.5" y="537.5">FM, Analog, Wavetable, ...</text></g><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe flex-start; justify-content: unsafe flex-start; width: 150px; height: 1px; padding-top: 553px; margin-left: 866px;"><div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: left; max-height: 40px; overflow: hidden;"><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: none; white-space: normal; overflow-wrap: normal;">Your synth here!</div></div></div></foreignObject><text x="866" y="565" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px">Your synth here!</text></switch></g><path d="M 940 520 L 940 497.12" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="none"/><path d="M 940 480.12 L 948.5 497.12 L 931.5 497.12 Z" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="none"/><path d="M 700 186 L 700 160 L 860 160 L 860 186" fill="rgb(255, 255, 255)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="none"/><path d="M 700 186 L 700 240 L 860 240 L 860 186" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="none"/><path d="M 700 186 L 860 186" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="none"/><g fill="rgb(0, 0, 0)" font-family="Helvetica" font-weight="bold" pointer-events="none" text-anchor="middle" font-size="12px"><text x="779.5" y="177.5">ProcessorParameter</text></g><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe flex-start; justify-content: unsafe flex-start; width: 150px; height: 1px; padding-top: 193px; margin-left: 706px;"><div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: left; max-height: 50px; overflow: hidden;"><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: none; white-space: normal; overflow-wrap: normal;">Unified access to processor parameters, notification about changes etc.</div></div></div></foreignObject><text x="706" y="205" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px">Unified access to process...</text></switch></g><path d="M 780 255.99 L 780 296.76" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="none"/><path d="M 780 240.99 L 784.41 248.49 L 780 255.99 L 775.59 248.49 Z" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="none"/><path d="M 773.5 284.88 L 780 297.88 L 786.5 284.88" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="none"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe flex-start; justify-content: unsafe flex-start; width: 1px; height: 1px; padding-top: 257px; margin-left: 792px;"><div data-drawio-colors="color: rgb(0, 0, 0); background-color: rgb(255, 255, 255); " style="box-sizing: border-box; font-size: 0px; text-align: left;"><div style="display: inline-block; font-size: 11px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: none; background-color: rgb(255, 255, 255); white-space: nowrap;">0..n</div></div></div></foreignObject><text x="792" y="268" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="11px">0..n</text></switch></g><path d="M 520 66 L 520 40 L 680 40 L 680 66" fill="rgb(255, 255, 255)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="none"/><path d="M 520 66 L 520 120 L 680 120 L 680 66" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="none"/><path d="M 520 66 L 680 66" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="none"/><g fill="rgb(0, 0, 0)" font-family="Helvetica" font-weight="bold" pointer-events="none" text-anchor="middle" font-size="12px"><text x="599.5" y="57.5">ProcessorRangeParameter</text></g><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe flex-start; justify-content: unsafe flex-start; width: 150px; height: 1px; padding-top: 73px; margin-left: 526px;"><div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: left; max-height: 50px; overflow: hidden;"><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: none; white-space: normal; overflow-wrap: normal;">fixed-point parameter with minimum, maximum, and possibly logarithmic scaling</div></div></div></foreignObject><text x="526" y="85" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px">fixed-point parameter wit...</text></switch></g><path d="M 660 120 L 704.92 149.95" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="none"/><path d="M 719.07 159.38 L 700.21 157.02 L 709.64 142.88 Z" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="none"/><path d="M 700 66 L 700 40 L 860 40 L 860 66" fill="rgb(255, 255, 255)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="none"/><path d="M 700 66 L 700 120 L 860 120 L 860 66" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="none"/><path d="M 700 66 L 860 66" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="none"/><g fill="rgb(0, 0, 0)" font-family="Helvetica" font-weight="bold" pointer-events="none" text-anchor="middle" font-size="12px"><text x="779.5" y="57.5">ProcessorEnumParameter</text></g><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe flex-start; justify-content: unsafe flex-start; width: 150px; height: 1px; padding-top: 73px; margin-left: 706px;"><div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: left; max-height: 50px; overflow: hidden;"><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: none; white-space: normal; overflow-wrap: normal;">parameter with enum value</div></div></div></foreignObject><text x="706" y="85" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px">parameter with enum value</text></switch></g><path d="M 880 66 L 880 40 L 1050 40 L 1050 66" fill="rgb(255, 255, 255)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="none"/><path d="M 880 66 L 880 120 L 1050 120 L 1050 66" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="none"/><path d="M 880 66 L 1050 66" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="none"/><g fill="rgb(0, 0, 0)" font-family="Helvetica" font-weight="bold" pointer-events="none" text-anchor="middle" font-size="12px"><text x="964.5" y="57.5">ProcessorBooleanParameter</text></g><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe flex-start; justify-content: unsafe flex-start; width: 160px; height: 1px; padding-top: 73px; margin-left: 886px;"><div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: left; max-height: 50px; overflow: hidden;"><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: none; white-space: normal; overflow-wrap: normal;">flag-like parameter</div></div></div></foreignObject><text x="886" y="85" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px">flag-like parameter</text></switch></g><path d="M 780 120 L 780 141.88" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="none"/><path d="M 780 158.88 L 771.5 141.88 L 788.5 141.88 Z" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="none"/><path d="M 903.33 120 L 856.87 150.14" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="none"/><path d="M 842.6 159.39 L 852.24 143.01 L 861.49 157.27 Z" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="none"/></g><switch><g requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"/><a transform="translate(0,-5)" xlink:href="https://www.diagrams.net/doc/faq/svg-export-text-problems" target="_blank"><text text-anchor="middle" font-size="10px" x="50%" y="100%">Text is not SVG - cannot display</text></a></switch></svg> \ No newline at end of file diff --git a/Base/usr/share/man/man7/Mitigations.md b/Base/usr/share/man/man7/Mitigations.md deleted file mode 100644 index cd53e37f1c6..00000000000 --- a/Base/usr/share/man/man7/Mitigations.md +++ /dev/null @@ -1,427 +0,0 @@ -## Name - -Mitigations - Security mitigations implemented by SerenityOS - -## Description - -The SerenityOS developers have put substantial effort into -integrating various mitigation technologies into the system -in order to enhance its security. The goal of this document is -to collect and describe the mitigations in one centralized place. - -## List of Mitigations - -### SMEP (Supervisor Mode Execution Protection) - -[Supervisor Mode Execution Protection](https://www.intel.com/content/www/us/en/developer/articles/technical/software-security-guidance/best-practices/related-intel-security-features-technologies.html) is an Intel CPU feature which prevents execution -of userspace code with kernel privileges. - -It was enabled in the following [commit](https://github.com/SerenityOS/serenity/commit/8602fa5b49aa4e2b039764a14698f0baa3ad0532): -``` -commit 8602fa5b49aa4e2b039764a14698f0baa3ad0532 -Author: Andreas Kling <awesomekling@gmail.com> -Date: Wed Jan 1 01:56:58 2020 +0100 - -Kernel: Enable x86 SMEP (Supervisor Mode Execution Protection) -``` - -### SMAP (Supervisor Mode Access Prevention) - -[Supervisor Mode Access Prevention](https://en.wikipedia.org/wiki/Supervisor_Mode_Access_Prevention) -complements SMEP by also guarding read/write access to -userspace memory while executing in kernel mode. - -It was enabled in the following [commit](https://github.com/SerenityOS/serenity/commit/9eef39d68a99c5e29099ae4eb4a56934b35eecde): - -``` -commit 9eef39d68a99c5e29099ae4eb4a56934b35eecde -Author: Andreas Kling <awesomekling@gmail.com> -Date: Sun Jan 5 18:00:15 2020 +0100 - -Kernel: Start implementing x86 SMAP support -``` - -### UMIP (User Mode Instruction Prevention) - -User Mode Instruction Prevention is an x86 CPU security feature which prevents execution of specific privileged -instructions in user mode (SGDT, SIDT, SLDT, SMSW, STR). -These instructions let user mode code query the addresses of various kernel structures (the GDT, LDT, IDT, etc), -meaning that they leak kernel addresses that can be exploited to defeat ASLR. - -It was enabled in the following [commit](https://github.com/SerenityOS/serenity/commit/9c0836ce97ae36165abd8eb5241bb5239af3a756): -``` -commit 9c0836ce97ae36165abd8eb5241bb5239af3a756 -Author: Andreas Kling <awesomekling@gmail.com> -Date: Wed Jan 1 13:02:32 2020 +0100 - -Kernel: Enable x86 UMIP (User Mode Instruction Prevention) if supported -``` - -### Pledge - -[pledge](https://marc.info/?l=openbsd-tech&m=143725996614627&w=2) is a mitigation which originated from OpenBSD. -It allows a program to voluntarily restrict its access to system calls -and kernel facilities. - -It was first added in the following [commit](https://github.com/SerenityOS/serenity/commit/41c504a33becea8aa9b437cd3c0dc312b2bf1fe9), -and the majority of programs were enlightened later: - -``` -commit 41c504a33becea8aa9b437cd3c0dc312b2bf1fe9 -Author: Andreas Kling <awesomekling@gmail.com> -Date: Sat Jan 11 20:45:51 2020 +0100 - -Kernel: Add pledge() syscall :^) -``` - -### Unveil - -[unveil](https://lwn.net/Articles/767137/) is a mitigation originating from OpenBSD. -It allows a program to voluntarily restrict its access to the filesystem. - -It was first added in the following [commit](https://github.com/SerenityOS/serenity/commit/0569123ad7cb9c54df724c2bb85933ea3cf97134), -and the majority of programs were enlightened later: - -``` -commit 0569123ad7cb9c54df724c2bb85933ea3cf97134 -Author: Andreas Kling <kling@serenityos.org> -Date: Mon Jan 20 22:12:04 2020 +0100 - -Kernel: Add a basic implementation of unveil() -``` - -### Jails - -`jails` are mitigation originating from FreeBSD. -It allows a program to be placed inside a lightweight OS-level virtualization environment. - -Current restrictions on jailed processes (configurable when creating a Jail): -- Process ID view isolation, being limited (both in `/proc` and `/sys/kernel/processes`) to only processes that share the same jail. - -Special restrictions on filesystem also apply: -- Write access is forbidden to the `/sys/kernel/power_state` node. -- Read accesses is forbidden by default to all nodes in `/sys/kernel` directory, except for: - `df`, `interrupts`, `keymap`, `memstat`, `processes`, `stats` and `uptime`. -- Write access is forbidden to kernel configuration variables (which are located in `/sys/kernel/conf`). -- Open access is forbidden to all device nodes except for `/dev/full`, `/dev/null`, `/dev/zero`, `/dev/random` and various - other TTY/PTY devices (not including Kernel virtual consoles). -- Executing SUID binaries is forbidden. - -It was first added in the following [commit](https://github.com/SerenityOS/serenity/commit/5e062414c11df31ed595c363990005eef00fa263), -for kernel support, and the following commits added basic userspace utilities: - -``` -commit 5e062414c11df31ed595c363990005eef00fa263 -Author: Liav A <liavalb@gmail.com> -Date: Wed Nov 2 22:26:02 2022 +0200 - -Kernel: Add support for jails - -... -``` - -### Readonly atexit - -[Readonly atexit](https://isopenbsdsecu.re/mitigations/atexit_hardening/) is a mitigation originating from OpenBSD. -Thanks to it, an attacker can no longer use the atexit region to escalate from arbitrary-write to code-execution. - -It was first added in the following [commit](https://github.com/SerenityOS/serenity/commit/553361d83f7bc6499dc4821eff9b23a6549bd99c), -and was later [improved](https://github.com/SerenityOS/serenity/commit/fb003d71c2becf0b3ea148aad08642e5a7ea35bc) -to incur no additional cost during program initialization and finalization: - -``` -commit 553361d83f7bc6499dc4821eff9b23a6549bd99c -Author: Andreas Kling <kling@serenityos.org> -Date: Sat Jan 30 10:34:41 2021 +0100 - -LibC: Protect the atexit() handler list when not writing to it - -Remap the list of atexit handlers as read-only while we're not actively -writing to it. This prevents an attacker from using a memory write -primitive to gain code execution via the atexit list. - -This is based on a technique used in OpenBSD. :^) -``` - -### Syscall call-from verification - -[Syscall call-from verification](https://marc.info/?l=openbsd-tech&m=157488907117170&w=2) is -a mitigation which originated from OpenBSD. -In short the kernel checks that all syscalls originate -from the address of the system's libc. This makes attacks -on OpenBSD more difficult as they random-relink their libc -on boot, which makes finding syscall stubs in libc difficult -for attackers. On serenity it is mostly just an inconvenience, -as there currently is no libc random-relinking. - -It was first enabled in the following [commit](https://github.com/SerenityOS/serenity/commit/823186031d9250217f9a51829d34a96b74113334): - -``` -commit 823186031d9250217f9a51829d34a96b74113334 -Author Andreas Kling <kling@serenityos.org> -Date: Tue Feb 2 19:56:11 2021 +0100 - -Kernel: Add a way to specify which memory regions can make syscalls -``` - -### Immutable memory mappings - -[Immutable memory mappings](https://lwn.net/SubscriberLink/915640/53bc300d11179c62/) is -a mitigation which originated from OpenBSD. -In short the annotation of a particular Kernel Region as immutable implies that -that these virtual memory mappings are locked to their last state (in regard to protection bits, etc), -and they cannot be unmapped by a process until that process gets finalized. - -It was first enabled in the following [commit](https://github.com/SerenityOS/serenity/commit/8585b2dc23ec206777a4cfbd558766d90fc577e7): - -``` -commit 8585b2dc23ec206777a4cfbd558766d90fc577e7 -Author: Liav A <liavalb@gmail.com> -Date: Thu Dec 15 21:08:57 2022 +0200 - -Kernel/Memory: Add option to annotate region mapping as immutable - -We add this basic functionality to the Kernel so Userspace can request a -particular virtual memory mapping to be immutable. This will be useful -later on in the DynamicLoader code. - -The annotation of a particular Kernel Region as immutable implies that -the following restrictions apply, so these features are prohibited: -- Changing the region's protection bits -- Unmapping the region -- Annotating the region with other virtual memory flags -- Applying further memory advises on the region -- Changing the region name -- Re-mapping the region -``` - -### Post-init read-only memory - -[Post-init read-only memory](https://lwn.net/Articles/666550/) is -a mitigation which originated from the Linux Kernel. -It tracks data that is initialized during kernel boot and never -changed again. Post kernel initialization, the memory is marked -read-only to protect it from potentially being modified by exploits. - -It was first enabled in the following [commit](https://github.com/SerenityOS/serenity/commit/d8013c60bb52756788e747183572067d6e3f204a) -and other kernel data structures were enlightened later: - -``` -commit d8013c60bb52756788e747183572067d6e3f204a -Author: Andreas Kling <kling@serenityos.org> -Date: Sun Feb 14 17:35:07 2021 +0100 - -Kernel: Add mechanism to make some memory read-only after init finishes -``` - -### KUBSAN (Kernel Undefined Behavior Sanitizer) - -UndefinedBehaviorSANitizer is a dynamic analysis tool, implemented in GCC, -which instruments generated code to flag undefined behavior at runtime. -It can find various issues, including integer overflows, out-of-bounds array -accesses, type corruption, and more. - -It was first enabled in the following [commit](https://github.com/SerenityOS/serenity/commit/d44be968938ecf95023351a358c43c4957638d87): -``` -commit d44be968938ecf95023351a358c43c4957638d87 -Author: Andreas Kling <kling@serenityos.org> -Date: Fri Feb 5 19:44:26 2021 +0100 - -Kernel: KUBSAN! (Kernel Undefined Behavior SANitizer) :^) -``` - -### Kernel unmap-after-init - -Unmap-after-init allows the kernel to remove functions which contain potentially -dangerous [ROP gadgets](https://en.wikipedia.org/wiki/Return-oriented_programming) -from kernel memory after we've booted up and they are no longer needed. Notably the -`write_cr4(..)` function used to control processor features like the SMEP/SMAP bits -in the CR4 register, and the `write_cr0(..)` function used to control processor features -like write protection, etc. - -With this mitigation it is now more difficult to craft a kernel exploit to do something -like disabling SMEP / SMAP. - -It was first enabled in the following [commit](https://github.com/SerenityOS/serenity/commit/6136faa4ebf6a878606f33bc03c5e62de9d5e662): -``` -commit 6136faa4ebf6a878606f33bc03c5e62de9d5e662 -Author: Andreas Kling <kling@serenityos.org> -Date: Fri Feb 19 18:21:54 2021 +0100 - -Kernel: Add .unmap_after_init section for code we don't need after init -``` - -### Relocation Read-Only (RELRO) - -[RELRO](https://hockeyinjune.medium.com/relro-relocation-read-only-c8d0933faef3) is a mitigation -in the linker and loader that hardens the data sections of an ELF binary. - -When enabled, it segregates function pointers resolved by the dynamic loader -into a separate section of the runtime executable memory, and allows the loader -to make that memory read-only before passing control to the main executable. - -This prevents attackers from overwriting the [Global Offset Table (GOT)](https://en.wikipedia.org/wiki/Global_Offset_Table). - -It was first enabled for executables in the following [commit](https://github.com/SerenityOS/serenity/commit/fa4c249425a65076ca04a3cb0c173d49472796fb): -``` -commit fa4c249425a65076ca04a3cb0c173d49472796fb -Author: Andreas Kling <kling@serenityos.org> -Date: Thu Feb 18 18:43:20 2021 +0100 - -LibELF+Userland: Enable RELRO for all userland executables :^) -``` - -Shared libraries were enabled in a follow-up [commit](https://github.com/SerenityOS/serenity/commit/713b3b36be4f659e58e253b6c830509898dbd2fa): -``` -commit 713b3b36be4f659e58e253b6c830509898dbd2fa -Author: Andreas Kling <kling@serenityos.org> -Date: Thu Feb 18 22:49:58 2021 +0100 - -DynamicLoader+Userland: Enable RELRO for shared libraries as well :^) -``` - -### -fstack-clash-protection - -The GCC compiler option [`-fstack-clash-protection`](https://gcc.gnu.org/onlinedocs/gcc/Instrumentation-Options.html) -is a mitigation which helps prevent [stack clash](https://blog.qualys.com/vulnerabilities-research/2017/06/19/the-stack-clash) -style attacks by generating code that probes the stack in page-sized increments to ensure a fault is provoked. -This prevents attackers from using a large stack allocation to "jump over" the stack guard page into adjacent memory. - -It was first enabled in the following [commit](https://github.com/SerenityOS/serenity/commit/7142562310e631156d1f64aff22f068ae2c48a5e): -``` -commit 7142562310e631156d1f64aff22f068ae2c48a5e -Author: Andreas Kling <kling@serenityos.org> -Date: Fri Feb 19 09:11:02 2021 +0100 - -Everywhere: Build with -fstack-clash-protection -``` - -### -fstack-protector / -fstack-protector-strong - -The GCC compiler provides a few variants of the `-fstack-protector` option mitigation. -This family of flags enables [buffer overflow protection](https://en.wikipedia.org/wiki/Buffer_overflow_protection) -to mitigate [stack-smashing attacks](https://en.wikipedia.org/wiki/Stack_buffer_overflow). - -The compiler implements the mitigation by storing a canary value randomized on program startup into the preamble of all -functions. Code is then generated to validate that stack canary on function return and crash if the value has been changed -(and hence a stack corruption has been detected.) - -`-fstack-protector` was first enabled in the following [commit](https://github.com/SerenityOS/serenity/commit/842716a0b5eceb8db31416cd643720c1037032b2): - -``` -commit 842716a0b5eceb8db31416cd643720c1037032b2 -Author: Andreas Kling <awesomekling@gmail.com> -Date: Fri Dec 20 20:51:50 2019 +0100 - -Kernel+LibC: Build with basic -fstack-protector support -``` - -It was later re-enabled and refined to `-fstack-protector-strong` in the following commits: - -``` -commit fd08c93ef57f71360d74b035214c71d7f7bfc5b8 -Author: Brian Gianforcaro <b.gianfo@gmail.com> -Date: Sat Jan 2 04:27:35 2021 -0800 - -LibC: Randomize the stack check cookie value on initialization - -commit 79328b2aba6192caf28f560881e56ff23fcb7294 -Author: Brian Gianforcaro <b.gianfo@gmail.com> -Date: Sat Jan 2 03:02:42 2021 -0800 - -Kernel: Enable -fstack-protector-strong (again) - -commit 06da50afc71a5ab2bc63de54c66930a2dbe379cd -Author: Brian Gianforcaro <b.gianfo@gmail.com> -Date: Fri Jan 1 15:27:42 2021 -0800 - -Build + LibC: Enable -fstack-protector-strong in user space -``` -### Protected Kernel Process Data - -The kernel applies a exploit mitigation technique where vulnerable data -related to the state of a process is separated out into it's own region -in memory which is always remapped as read-only after it's initialized -or updated. This means that an attacker needs more than an arbitrary -kernel write primitive to be able to elevate a process to root for example. - -It was first enabled in the following [commit](https://github.com/SerenityOS/serenity/commit/cbcf891040e9921ff628fdda668c9738f358a178): -``` -commit cbcf891040e9921ff628fdda668c9738f358a178 -Author: Andreas Kling <kling@serenityos.org> -Date: Wed Mar 10 19:59:46 2021 +0100 - -Kernel: Move select Process members into protected memory -``` - -### -fzero-call-used-regs - -GCC-11 added a new option `-fzero-call-used-regs` which causes the -compiler to zero function arguments before return of a function. The -goal being to reduce the possible attack surface by disarming ROP -gadgets that might be potentially useful to attackers, and reducing -the risk of information leaks via stale register data. - -It was first enabled when compiling the Kernel in the following [commit](https://github.com/SerenityOS/serenity/commit/204d5ff8f86547a8b100cf26a958aaabf49211f2): - -``` -commit 204d5ff8f86547a8b100cf26a958aaabf49211f2 -Author: Brian Gianforcaro <bgianf@serenityos.org> -Date: Fri Jul 23 00:42:54 2021 -0700 - -Kernel: Reduce useful ROP gadgets by zeroing used function registers -``` - -### Linking with "separate-code" - -The linker is passed the `separate-code` option, so it won't combine read-only data -and executable code. This reduces the total amount of executable pages in the system. - -It was first enabled in the following [commit](https://github.com/SerenityOS/serenity/commit/fac0bbe739154abb416526bdc983487c05ba0c81): - -``` -commit fac0bbe739154abb416526bdc983487c05ba0c81 -Author: Andreas Kling <kling@serenityos.org> -Date: Tue Aug 31 16:08:11 2021 +0200 - -Build: Pass "-z separate-code" to linker -``` - -### KASLR (Kernel Address Space Layout Randomization) - -The location of the kernel code is randomized at boot time, this ensures that attackers -can not use a hardcoded kernel addresses when attempting ROP, instead they must first find -an additional information leak to expose the KASLR offset. - -It was first enabled in the following [commit](https://github.com/SerenityOS/serenity/commit/1ad0e05ea1d3491e4724669d6f00f5668d8e0aa1): - -``` -commit 1ad0e05ea1d3491e4724669d6f00f5668d8e0aa1 -Author: Idan Horowitz <idan.horowitz@gmail.com> -Date: Mon Mar 21 22:59:48 2022 +0200 - -Kernel: Add an extremely primitive version of KASLR -``` - -### Kernel -ftrivial-auto-var-init - -As of GCC 12, both Clang and GCC now support the `-ftrivial-auto-var-init` -compiler flag. The flag will cause the compiler to automatically initialize -all variables to a pattern based on it's type. The goal being here is to -eradicate an entire bug class of issues that can originate from uninitialized -variables. - -It was first enabled for the SerenityOS Kernel in the following [commit](https://github.com/SerenityOS/serenity/commit/458244c0c1c8f077030fa0d8964fad8d75c60d4a): - -``` -From 458244c0c1c8f077030fa0d8964fad8d75c60d4a Mon Sep 17 00:00:00 2001 -From: Brian Gianforcaro <bgianf@serenityos.org> -Date: Fri, 24 Jun 2022 00:34:38 -0700 - -Kernel: Enable -ftrivial-auto-var-init as a security mitigation -``` - -## See also - -* [`unveil`(2)](help://man/2/unveil) -* [`pledge`(2)](help://man/2/pledge) diff --git a/Base/usr/share/man/man7/Shell-vars.md b/Base/usr/share/man/man7/Shell-vars.md deleted file mode 100644 index 811098ea018..00000000000 --- a/Base/usr/share/man/man7/Shell-vars.md +++ /dev/null @@ -1,78 +0,0 @@ -## Name - -Shell Variables - Special local and environment variables used by the Shell - -## Description - -The Shell uses various variables to allow for customizations of certain behavioral or visual things. -Such variables can be changed or set by the user to tweak how the shell presents things. - -## Behavioral - -1. Output interpretations - -`IFS` (local) - -The value of this variable is used to join lists or split strings into lists, its default value is a newline (`\\n`). - -2. History - -`HISTCONTROL` (environment) - -The value of this variable is used to determine which entries are kept in the Shell's history, both regarding the current active session and when writing the history to disk on exit. - -- `ignorespace`: Entries starting with one or more space characters are ignored -- `ignoredups`: Consecutive duplicate entries are ignored -- `ignoreboth`: The behavior of `ignorespace` and `ignoredups` is combined -- If the variable is unset (this is the default) or has any other value than the above, no entries will be excluded from history. - -Note: This variable is respected by every program using `Line::Editor`, e.g. [`js`(1)](help://man/1/js). - -`HISTFILE` (environment) - -The value of this variable is used as the Shell's history file path, both for reading history at startup and writing history on exit. -Its default value is `~/.history`. - -`HISTORY_AUTOSAVE_TIME_MS` (environment) - -Setting this variable to a value `t` other than zero (0) will make the shell save the history to the history file every `t` milliseconds. -If `t` is not a non-negative integer, zero will be assumed. -Note that multiple shell instances will not interfere with each other if they are to save to the same history file, instead, the entries will all be merged together chronologically. -The default value for this option is set in `/etc/shellrc`. - -3. Terminal settings - -`PROGRAMS_ALLOWED_TO_MODIFY_DEFAULT_TERMIOS` (local) - -The list value stored in this variable is used as an allow-list to filter programs that may modify the current terminal settings (i.e. default termios configuration) of the current shell session. -By default, this list includes `stty`, making it possible for the user to modify the terminal settings from within the shell. -Note that the shell will revert any termios changes if the running program is not in this list, or if it failed to run successfully (exited with an exit code other than zero). -Also note that the line editor will re-evaluate the keybindings and sync them when such a change occurs. - -## Visual - -1. Prompting - -`PROMPT` (environment) - -The value of this variable is used to generate a prompt, the following escape sequences can be used literally inside the value, and they would expand to their respective values: -- `\\a`: bell character (behavior depends on terminal) -- `\\e`: escape character (`0x1b`) -- `\\h`: the current hostname -- `\\p`: the string '$' (or '#' if the user is 'root') -- `\\u`: the current username -- `\\w`, `\\W`: a collapsed path (relative to home) to the current directory. If an integer follows the `\\`, it specifies the number of trailing components of the path to show; if 'w' is used instead of 'W', removed components are shown with ellipsis ("...") -- `\\X`: reset style (foreground and background color, etc) -- `\\t`: current time in the 24-hour format HH:MM:SS -- `\\T`: current time in the 12-hour format HH:MM -- `\\@`: current time in the 12-hour format HH:MM AM/PM -- `\\D{format}`: current time, where the string _format_ is passed on to `Core::DateTime::to_string`. If _format_ is empty, a default format string is chosen. -- `\\j`: the number of jobs currently managed by the shell -- `\\!`: the history number of the next command to be run -- `\\\\`: a backslash - -Any other escaped character shall be ignored. - -`PROMPT_EOL_MARK` (environment) - -The value of this variable is used to denote the ends of partial lines (lines with no newline), its default value is '%'. diff --git a/Base/usr/share/man/man7/SystemServer.md b/Base/usr/share/man/man7/SystemServer.md deleted file mode 100644 index bd47a0064c6..00000000000 --- a/Base/usr/share/man/man7/SystemServer.md +++ /dev/null @@ -1,51 +0,0 @@ -## Name - -SystemServer - one server to rule them all - -## Description - -SystemServer is the first userspace process to be started by the kernel on boot. -Its main responsibility is spawning all the other servers and other programs -that need to be autostarted (referred to as **services**). - -A service can be configured to be *kept alive*, in which case SystemServer will -respawn it if it exits or crashes. A service may also be configured as *lazy*, -in which case SystemServer won't spawn it immediately, but only once a client -connects to its socket (see **Socket takeover** below). - -## Socket takeover - -SystemServer can be configured to set up a socket on behalf of a service -(typically, an *IPC portal* socket inside `/tmp/portal/`). SystemServer sets up -the configured sockets before spawning any services, preventing any races -between socket creation and the client trying to connect to those sockets. - -When a service is spawned, SystemServer passes it an open file descriptor to the -configured socket as fd 3, and sets `SOCKET_TAKEOVER=1` in the environment to -inform the service that socket takeover is happening. SystemServer calls -[`listen`(2)](help://man/2/listen) on the file descriptor, so the service doesn't -need to do it again. The file descriptor does not have the `FD_CLOEXEC` flag set -on it. - -The service is advised to set this flag using [`fcntl`(2)](help://man/2/fcntl) and -unset `SOCKET_TAKEOVER` from the environment in order not to confuse its -children. - -LibCore provides `Core::LocalServer::take_over_from_system_server()` method that -performs the service side of the socket takeover automatically. - -If a service is configured as *lazy*, SystemServer will actually listen on the -socket it sets up for the service, and only spawn the service once a client -tries to connect to the socket. The service should then start up and accept the -connection. This all happens transparently to the client. If a lazy service is -configured to be *kept alive*, it can even exit after some period of inactivity; -in this case SystemServer will respawn it again once there is a new connection -to its socket. - -SystemServer can also be configured to accept connections on the socket and -spawn separate instances of the service for each accepted connection, passing -the accepted socket to the service process. - -## See also - -* [`SystemServer`(5)](help://man/5/SystemServer) diff --git a/Base/usr/share/man/man7/Tips-and-Tricks.md b/Base/usr/share/man/man7/Tips-and-Tricks.md deleted file mode 100644 index 564ee16bd7b..00000000000 --- a/Base/usr/share/man/man7/Tips-and-Tricks.md +++ /dev/null @@ -1,59 +0,0 @@ -![Welcome Icon](/res/icons/32x32/app-welcome.png) - -## Name -Tips and Tricks - -## Description -This is a list of useful tips and tricks to help you make the most out of SerenityOS. - -## General -* When on the Desktop or in File Manager, start typing the name of an item to select it. -* Bold text in context menus hints at the default behavior of a double-click. -* Hold `Ctrl` to accelerate mouse wheel interaction with sliders and spin boxes. -* Hold `Ctrl` while activating a menu item to prevent that menu from closing. -* Many applications can open a compatible file if you drag-and-drop it into their window. -* Change default file and protocol associations in `~/.config/LaunchServer.ini`. - -### Window Management -* Double-click a window's title bar to maximize it. -* Click on a window's icon to open the window's context menu. -* Double-click on the edge of an application's window to maximize it in that direction. -* Middle-click on a window's maximize button to extend the window vertically (this can be undone in the same way). -* Drag resizable windows to any side or corner of the screen to automatically resize them to fill half or one-quarter of the screen. -* Right-click on the Workspace Picker applet and select 'Workspace Settings' to easily customize the number and layout of Workspaces (virtual desktops). - -### Fun -* It can help to get a second pair of `$ Eyes` on a problem… or fifty: `$ Eyes -n 100`. - -## Applications - -### [Assistant](help://man/1/Applications/Assistant) -* Assistant can help you to quickly find files and launch applications. Open it with `Super+Space`. -* Enter a URL to open it in the web browser, e.g. `serenityos.org`. -* Perform quick calculations by typing the equal sign (=) followed by a mathematical expression, e.g. `=22*101`. Press Return to copy the result. -* Run commands in the terminal by prefixing them with a dollar sign ($), e.g. `$ uname -a`. - -### [Browser](help://man/1/Applications/Browser) -* Browser has built-in content filtering, which can be used for ad blocking. Update the filters in `~/.config/BrowserContentFiltering.txt`. - -### Keyboard Mapper -* Create and edit custom keymaps with `$ KeyboardMapper`. - -### Run -* The Run dialog accepts all [Shell](help://man/5/Shell) commands. - -### [Terminal](help://man/1/Applications/Terminal) -* Some of the bold or underlined text in Terminal can be double-clicked to open or right-clicked for additional actions. - * For example, right-click on a file or folder and select 'Copy URL' to copy the path. -* Many Serenity applications already have convenient aliases. Use `$ cat /etc/shellrc` to view them. - -### [Text Editor](help://man/1/Applications/TextEditor) -* Text files can be dragged directly from Terminal and dropped on Text Editor to open them. -* Text Editor has multiple viewing modes. You can edit HTML or Markdown and live preview it at the same time. - -## Development -* Supplying `# profile` with a process identifier (PID) of `-1` as root enables systemwide profiling. -* Easily transfer files from QEMU to your host machine by using the built-in web server. In the terminal enter `ws .` to start a WebServer instance for the current working directory. Then open `localhost:8000` on your host machine. - -## See also -* [Keyboard Shortcuts](help://man/7/KeyboardShortcuts) diff --git a/Base/usr/share/man/man7/boot_device_addressing.md b/Base/usr/share/man/man7/boot_device_addressing.md deleted file mode 100644 index 1f189ba0e4c..00000000000 --- a/Base/usr/share/man/man7/boot_device_addressing.md +++ /dev/null @@ -1,61 +0,0 @@ -## Name - -Boot Device Addressing - addressing the correct boot device to use. - -## Synopsis - -Serenity's kernel can select the boot device at boot time, based on the `root` boot parameter. -This functionality is used to control which boot device is selected to be used for all further boot process operations. - -## Description - -The kernel `root` boot parameter takes the form of **`root={value}`**, where the **`={value}`** -trailer can be set to specific prefixes to indicate the boot device preference. - -### Addressing options - -The user can choose to use addressing based on synthetic unix concepts: - -``` -block0:0 -``` - -This is especially useful in static hardware setups, so the user can choose to use -either a raw `StorageDevice` or partition block device. The `0,0` selection is the `MAJOR,MINOR` -numbers of the device. - -However, when there's knowledge of the hardware arrangement of raw `StorageDevice`s, -it could be valuable to use addressing based on hardware-relative interface-specific "location" -to address raw `StorageDevice`s: - -``` -ata0:0:0 [First ATA controller, ATA first primary channel, master device] -nvme0:1:0 [First NVMe Controller, First NVMe Namespace, Not Applicable] -``` - -When the logical arrangement is known, using (absolute) LUNs is the easiest option as it doesn't rely on -using unix device numbers or hardware-relative location: - -``` -lun0:0:0 - first device on the first channel of the first controller to be enumerated -``` - -### Note on selecting partitions from raw `StorageDevice`s - -All the addressing options above support selecting a partition device, given that -the selected device is a `StorageDevice` and not a `DiskPartition` device: - -``` -nvme0;part0 -lun0:0:0;part0 -``` - -The only exception to this is when choosing a `BlockDevice`. As such, -trying to specify `block0:0;part0`, for example, will lead to a kernel panic, -as an invalid boot device parameter. - -### Selecting a specific partition based on known GUID - -For GPT partitions, passing `PARTUUID:` and the GUID of the partition can be used -to select a GPT partition. Although it could be slower to find the corresponding -partition, it is the safest option available for persistent storage. diff --git a/Base/usr/share/man/man7/boot_parameters.md b/Base/usr/share/man/man7/boot_parameters.md deleted file mode 100644 index 6eed6be21bf..00000000000 --- a/Base/usr/share/man/man7/boot_parameters.md +++ /dev/null @@ -1,102 +0,0 @@ -## Name - -Boot Parameters - optional parameters accepted by the kernel - -## Description - -Serenity's kernel can process parameters at boot time. -This functionality is used to control or augment the state of features during the initial -startup of the system. - -### Options - -The kernel boot parameters take the form of **`{option_name}={value}`**, where the **`={value}`** -trailer can be omitted for specific parameters. - -List of options: - -* **`acpi`** - This parameter expects one of the following values. **`on`** - Boot with full ACPI support, using ACPI - Machine Language interpretation (default). **`limited`** - Boot with limited ACPI support. **`off`** - Don't initialize ACPI at all. - -* **`ahci_reset_mode`** - This parameter expects one of the following values. **`controllers`** - Reset just the AHCI controller on boot (default). - **`aggressive`** - Reset the AHCI controller, and all AHCI ports on boot. - -* **`boot_prof`** - If present on the command line, global system profiling will be enabled - as soon as possible during the boot sequence. Allowing you to profile startup of all applications. - -* **`disable_physical_storage`** - If present on the command line, neither AHCI, or IDE controllers will be initialized on boot. - -* **`disable_ps2_mouse`** - If present on the command line, no PS2 mouse will be attached. - -* **`disable_uhci_controller`** - If present on the command line, the UHCI controller will not be initialized on boot. - -* **`disable_virtio`** - If present on the command line, virtio devices will not be detected, and initialized on boot. - -* **`early_boot_console`** - This parameter expects **`on`** or **`off`** and is by default set to **`on`**. - When set to **`off`**, the kernel will not initialize any early console to show kernel dmesg output. - When set to **`on`**, the kernel will try to initialize either a text mode console (if VGA text mode was detected) - or framebuffer console, based on what the Multiboot bootloader specified on the physical address, width, height - and bit color depth. - -* **`enable_ioapic`** - This parameter expects **`on`** or **`off`** and is by default set to **`on`**. - When set to **`off`**, the kernel will initialize the two i8259 PICs. - When set to **`on`**, the kernel will try to initialize the IOAPIC (or IOAPICs if there's more than one), - but only if **`acpi`** is set to **`limited`** or **`on`**, and a `MADT` (APIC) table is available. - Otherwise, the kernel will fallback to use the i8259 PICs. - -* **`graphics_subsystem_mode`** - This parameter expects one of the following values. **`on`**- Boot into the graphical environment if possible (default). **`off`** - Boot into text mode, don't initialize any driver. **`limited`** - Boot into the pre-defined framebuffer that the bootloader -has set up before booting the Kernel, don't initialize any driver. - -* **`hpet`** - This parameter expects one of the following values. **`periodic`** - The High Precision Event Timer should - be configured in a periodic mode. **`nonperiodic`** - The High Precision Event Timer should eb configure din non-periodic mode. - -* **`init`** - This parameter expects the fully qualified path to the init program the Kernel should launch after boot. - This defaults to [`SystemServer`(7)](help://man/7/SystemServer). - -* **`init_args`** - This parameter expects a set of arguments to pass to the **`init`** program. - The value should be a set of strings separated by `,` characters. - -* **`i8042_presence_mode`** - This parameter expects one of the following values: - **`aggressive-test`** - The i8042 initialization sequence should only try an aggressive presence test. - **`auto`** - The i8042 initialization sequence should try to check if ACPI says i8042 exists, and if not an aggressive presence test should take place to determine presence. - **`none`** - Assume there's no i8042 controller in the system. - **`force`** - Assume there's i8042 controller in the system. - -* **`i8042_first_port_translation`** - This parameter expects **`on`** or **`off`** and is by default set to **`off`**. - When set to **`off`**, the kernel will not enable first PS2 port translation. - When set to **`on`**, the kernel will enable first PS2 port translation. - -* **`panic`** - This parameter expects **`halt`** or **`shutdown`**. This is particularly useful in CI contexts. - -* **`pci`** - This parameter expects **`ecam`**, **`io`** or **`none`**. When selecting **`none`** - the kernel will not use PCI resources/devices. - -* **`root`** - This parameter configures the device to use as the root file system. It defaults to **`/dev/hda`** if unspecified. - -* **`pcspeaker`** - This parameter controls whether the kernel can use the PC speaker or not. It defaults to **`off`** and can be set to **`on`** to enable the PC speaker. - -* **`smp`** - This parameter expects a binary value of **`on`** or **`off`**. If enabled kernel will - enable available APs (application processors) and use them with the BSP (Bootstrap processor) to - schedule and run threads. - This parameter defaults to **`off`**. This parameter requires **`enable_ioapic`** to be enabled - and a `MADT` (APIC) table to be available. - -* **`nvme_poll`** - This parameter configures the NVMe drive to use polling instead of interrupt driven completion. - -* **`system_mode`** - This parameter is not interpreted by the Kernel, and is made available at `/sys/kernel/system_mode`. SystemServer uses it to select the set of services that should be started. Common values are: - - **`graphical`** (default) - Boots the system in the normal graphical mode. - - **`self-test`** - Boots the system in self-test, validation mode. - - **`text`** - Boots the system in text only mode. (You may need to also set **`graphics_subsystem_mode=off`**.) - -* **`time`** - This parameter expects one of the following values. **`modern`** - This configures the system to attempt - to use High Precision Event Timer (HPET) on boot. **`legacy`** - Configures the system to use the legacy programmable interrupt - time for managing system team. - -* **`vmmouse`** - This parameter expects a binary value of **`on`** or **`off`**. If enabled and - running on a VMWare Hypervisor, the kernel will enable absolute mouse mode. - -* **`disable_kaslr`** - If present on the command line, the KASLR security mitigation will be disabled. - -## See also - -* [`SystemServer`(7)](help://man/7/SystemServer). diff --git a/Base/usr/share/man/man7/man.md b/Base/usr/share/man/man7/man.md deleted file mode 100644 index 91527839d40..00000000000 --- a/Base/usr/share/man/man7/man.md +++ /dev/null @@ -1,57 +0,0 @@ -## Name - -man - SerenityOS manual system - -## Description - -The SerenityOS manual pages, or "man pages", document various parts of the operating system for users and developers. They are one of the two parts of the SerenityOS documentation. - -The other part of the SerenityOS documentation is the developer documentation, which can be found in the `Documentation` folder in the repository ([online link](https://github.com/SerenityOS/serenity/tree/master/Documentation)). The developer documentation is focused on setting up a SerenityOS installation and workflow, as well as contributing to its development. - -Note that documentation might cover standardized topics (such as standard POSIX C library functions) or SerenityOS-specific extensions (such as custom file formats). SerenityOS intends to be spec-complaint with industry standard specifications. Non-compliance with a particular specification should be documented in the relevant sections. - -### Programs - -There are three ways of accessing the manual pages: - -- [`Help`(1)](help://man/1/Applications/Help) provides a graphical user interface for the man pages. -- [`man`(1)](help://man/1/man) implements the standard POSIX utility by accessing the man pages in the terminal. -- You can also choose to open the underlying Markdown files (see [Organization](#organization)) directly. - -### Organization - -Each SerenityOS manual page is a Markdown (.md) file under [`/usr/share/man`](/usr/share/man). The [main sections](#sections) live in the subdirectories `man1` through `man8`. Subsections are found within these directories. - -#### Sections - -The SerenityOS manual is split into the following _sections_, or _chapters_: - -1. User Programs - manuals for regular user applications and utilities -2. System Calls - SerenityOS system call interface documentation -3. Library Functions - SerenityOS C library function documentation -4. Special Files - documentation for pseudo-files of the SerenityOS virtual file system -5. File Formats - documentation for SerenityOS-specific file formats -6. Games - manuals for SerenityOS games -7. Miscellanea - various documentation that fits in no other category -8. Sysadmin Tools - manuals for services and utilities intended for system administration - -Sections are subject to change in the future. - -#### Subsections - -Subsections exist to organize various large collections of topics within one main section. Subsections have their own pages (often a table of contents or general overview), so they are a category and a page at the same time. Subsections can be arbitrarily nested. - -The currently existing subsections are not listed here, as they are subject to frequent change. - -#### Naming - -Manpages are named via standard POSIX convention, where the section number follows the page name in brackets. For example, this page is called `man(7)`, but there's also a page called `man(1)` (the _program_ named `man`) and a page named [`Mitigations(7)`](help://man/7/Mitigations). This naming convention also applies to subsections. - -For pages in subsections, conventional directory notation with slashes is used. For example, the page `Widget/Button` in the subsection `GML(5)` has the full name `GML/Widget/Button(5)`. - -When you open a page via command-line arguments, the section is specified separately before the page name, for example `7 man`, `1 man`, or `7 Mitigations`. - -## See Also - -- [`man`(1)](help://man/1/man) To read manpages in the terminal -- [`Help`(1)](help://man/1/Applications/Help) To read manpages in a GUI diff --git a/Base/usr/share/man/man7/proc.md b/Base/usr/share/man/man7/proc.md deleted file mode 100644 index 4a100714f9a..00000000000 --- a/Base/usr/share/man/man7/proc.md +++ /dev/null @@ -1,39 +0,0 @@ -## Name - -proc - SerenityOS ProcFS - -## Description - -The kernel can expose process related information in /proc. -This functionality is used by various userland programs. -All of the output layout (besides symbolic links) in the ProcFS nodes is JSON. - -### Per process entries - -* **`cwd`** - a symbolic link to current work directory of a process. -* **`exe`** - a symbolic link to the executable binary of the process. -* **`fds`** - this node exports information on all currently open file descriptors. -* **`fd`** - this directory lists all currently open file descriptors. -* **`perf_events`** - this node exports information being gathered during a profile on a process. -* **`pledge`** - this node exports information on all the pledge requests and promises of a process. -* **`stacks`** - this directory lists all stack traces of process threads. -* **`unveil`** - this node exports information on all the unveil requests of a process. -* **`vm`** - this node exports information on virtual memory mappings of a process. -* **`children`** - this directory lists all the child processes of a process. - -### Consistency and stability of data across multiple read operations - -When opening a data node, the kernel generates the required data so it's prepared -for read operation when requested to. However, in order to ensure that multiple reads -will not create a corrupted data from that data node, a read operation alone will -not inquire the kernel to refresh the data. -To keep data output being refreshed, the userland has to re-open the data node with a -new file descriptor, or to perform the `lseek` syscall on the open file descriptor to -reset the offset to 0. - -## See also - -* [`mount`(2))](help://man/2/mount). -* [`boot_parameters`(7))](help://man/7/boot_parameters). -* [`pledge`(2))](help://man/2/pledge). -* [`unveil`(2))](help://man/2/unveil). diff --git a/Base/usr/share/man/man7/setuid_overview.md b/Base/usr/share/man/man7/setuid_overview.md deleted file mode 100644 index 413c5b4217c..00000000000 --- a/Base/usr/share/man/man7/setuid_overview.md +++ /dev/null @@ -1,71 +0,0 @@ -## Name - -Overview of real, effective, and saved user and group IDs - -## Description - -Each process runs has a user ID and a group ID. By default, a new process inherits the user ID and group ID of its parent process. - -Each file has permissions that control if it can be read, written, and executed by processes belonging to its owner, by processes belonging to other users in the owner's group, and by processes belonging to others. - -In addition to the access permissions bits, executables may have the "Set User ID" and the "Set Group ID" bit set. When executables with these bits set are executed, they run with the user or group ID of the executable, instead of with the user and group ID of their parent process. These binaries are also called "SUID binaries" or "setuid binaries" (or "SGID binaries" or "setgid binaries" for the "Set Group ID" bit). - -The motivation behind SUID binaries is that they allow users to do tasks that would normally require elevated permissions, without having to give users these permissions fully. - -For example, `/bin/ping` has the Set User ID bit set and is owned by user root in group root. So if any process executes `/bin/ping`, it will run as root, which means it will be able to send network packets, even if the current process doesn't normally have network access. - -For another example, many other Unix systems contain a utility `passwd` that changes the password of the current user. To store the password, it has to write to `/etc/shadow` -- but the current user is not supposed to have write access to `/etc/shadow` so that they cannot change passwords of other users. The solution is to make `passwd` a SUID binary. (SerenityOS currently doesn't have support for user passwords.) - -SUID binaries mean that the effective user ID of a process and the real user ID of a process can be different. When a SUID binary is executed, it assumes the owner of the binary as effective user ID, and the user ID of the parent process that executed it as real user ID. The effective user ID is used for permission checks. - -Since SUID binaries are able to bypass access checks, only carefully selected binaries should be made SUID. If, for example, `cp` was SUID root, everyone could overwrite every file using this `cp` binary. - -In some instances, it is useful for a SUID binary to either temporarily or permanently drop its permissions and set the effective user ID to the real user ID. - -To make this possible, each process has *three* user (and group) IDs: The (real) user ID, the *effective* user ID, and the *saved* user ID. When a process executes a normal binary, all three IDs are set to the parent process's user ID. However, when a process executes a SUID binary, the process runs with the parent process's ID as its real ID, but it takes its effective ID and saved ID from the binary. (Analogously for the group ID for SGID binaries.) - -The function [`setresuid`(2)](help://man/2/getresuid) can change the real, effective, and saved user ID of a process -- but for non-root processes it is only valid to set each new ID to the current value of real, effective, or saved user ID. Since SUID binaries start with the binary's owner as effective and saved user ID and with the current user's ID as real user ID, this allows switching the effective user ID between the SUID owner's ID and the current user's ID. - -Hence, to temporarily drop SUID privileges, set the effective ID to a less privileged user ID, and store the current effective user ID in the saved user ID so that it can be restored in a later call: - -```c++ -if (setresuid(-1, new_uid, geteuid()) < 0) - return OH_NO; -``` - -(Since the saved ID starts out as the file owner's ID for SUID binaries, this should in practice be the same as `seteuid(getuid())`, but it's easier to reason about.) - -The process then has fewer permissions, but since the former effective ID is still stored in the saved ID, the process can restore its former permissions with: - -```c++ -uid_t ruid, euid, suid; -if (getresuid(&ruid, &euid, &suid) < 0) - return OH_NO; -if (setresuid(-1, suid, -1) < 0) - return OH_NO; -``` - -(Since SUID binaries are often owned by root who has user ID 0, this is often identical to `seteuid(0)` -- in particular, if a SUID root binary accepts user input in an unsafe way with temporarily dropped privileges, then if a user manages to take control of the binary with malicious input they can restore privileges with `seteuid(0)`.) - -A process can permanently drop its SUID privileges by copying the real user ID into both effective and saved user ID. Then, it's impossible to set the effective ID to anything else (assuming the current user isn't the superuser). To permanently drop privileges: - -```c++ -if (setresuid(new_uid, new_uid, new_uid) < 0) - return OH_NO; -``` - -(On SerenityOS, this is usually the same as calling `setuid(new_uid)`, but easier to reason about.) - -Changing group IDs is analogous. Since changing the user ID changes the permissions of a process, group privileges should be dropped before user privileges are dropped, and if they're dropped temporarily, user privileges should be restored before group privileges are restored. - -For historical reasons, there are many functions for setting and getting these IDs. `setresuid()`, `setresgid()`, `getresuid()`, and `getresgid()` are the most flexible of these functions and they have the easiest to understand semantics. - -## See also - -* "Setuid Demystified", Proceedings of the 11th USENIX Security Symposium, August 2002, Pages 171–190 -* [`getresuid`(2) / `getresgid`(2)](help://man/2/getresuid) -* [`geteuid`(2) / `getegid`(2)](help://man/2/geteuid) -* [`getuid`(2) / `getgid`(2)](help://man/2/getuid) -* [`seteuid`(2) / `setegid`(2)](help://man/2/seteuid) -* [`setuid`(2) / `setgid`(2)](help://man/2/setuid) -* [`setresuid`(2) / `setresgid`(2)](help://man/2/setresuid) diff --git a/Base/usr/share/man/man7/sys.md b/Base/usr/share/man/man7/sys.md deleted file mode 100644 index a202597e913..00000000000 --- a/Base/usr/share/man/man7/sys.md +++ /dev/null @@ -1,92 +0,0 @@ -## Name - -sys - SerenityOS SysFS - -## Description - -The kernel can expose system (kernel, firmware and hardware) related information in /sys. - -### `bus` directory - -This directory include a subdirectory for each discovered and registered bus in the system. - -Possible busses to be exposed in this directory are: -1. The `pci` subdirectory that includes all discovered PCI devices as subdirectories. -The subdirectories of the PCI devices include files with basic information on the devices. -2. The `usb` subdirectory that includes all discovered USB devices as files. -The files of the USB devices export basic information on the devices. - -### `dev` directory - -This directory include two subdirectories - `block` and `char`, each for block -and character devices respectively. The files in these subdirectories are not -device files, but merely a file with filename layout of "major:minor", to aid -userspace in generating the appropriate device files. - -### `firmware` directory - -This directory include two subdirectories - `acpi` and `bios`. -The `bios` subdirectory maintains files of the exposed SMBIOS blobs, if present -by the firmware. -The `acpi` subdirectory maintains files of the exposed ACPI tables, if present -by the firmware. -A file called `power_state` is responsible for power state switching. - -### `kernel` directory - -This directory includes two subdirectories - `net` and `conf`. -All other files in the directory are global data nodes which provide statistics -and other kernel-related data to userspace. - -#### `kernel` directory entries - -* **`processes`** - This node exports a list of all processes that currently exist. -* **`cpuinfo`** - This node exports information on the CPU. -* **`df`** - This node exports information on mounted filesystems and basic statistics on -them. -* **`dmesg`** - This node exports information from the kernel log. -* **`interrupts`** - This node exports information on all IRQ handlers and basic statistics on -them. -* **`keymap`** - This node exports information on the currently used keymap. -* **`memstat`** - This node exports statistics on memory allocation in the kernel. -* **`profile`** - This node exports statistics on profiling data. -* **`stats`** - This node exports statistics on scheduler timing data. -* **`uptime`** - This node exports the uptime data. -* **`jails`** - This node exports information about existing jails (only if the current process is not in jail). -* **`power_state`** - This node only responds to write requests on it. A written value of `1` results -in system reboot. A written value of `2` results in system shutdown. -* **`load_base`** - This node reveals the loading address of the kernel. -* **`system_mode`** - This node exports the chosen system mode as it was decided based on the kernel commandline or a default value. -* **`cmdline`** - This node exports the kernel boot commandline that was passed from the bootloader. -* **`request_panic`** - This node allows userspace to trigger (an artificial) kernel panic by writing/truncating it. - -#### `net` directory - -* **`adapters`** - This node exports information on all currently-discovered network adapters. -* **`arp`** - This node exports information on the kernel ARP table. -* **`local`** - This node exports information on local (Unix) sockets. -* **`tcp`** - This node exports information on TCP sockets. -* **`udp`** - This node exports information on UDP sockets. - -#### `conf` directory - -This subdirectory includes global settings of the kernel. - -* **`caps_lock_to_ctrl`** - This node controls remapping of of caps lock to the Ctrl key. -* **`kmalloc_stacks`** - This node controls whether to send information about kmalloc to debug log. -* **`ubsan_is_deadly`** - This node controls the deadliness of the kernel undefined behavior -sanitizer errors. - -### Consistency and stability of data across multiple read operations - -When opening a data node, the kernel generates the required data so it's prepared -for read operation when requested to. However, in order to ensure that multiple reads -will not create a corrupted data from that data node, a read operation alone will -not inquire the kernel to refresh the data. -To keep data output being refreshed, the userland has to re-open the data node with a -new file descriptor, or to perform the `lseek` syscall on the open file descriptor to -reset the offset to 0. - -## See also - -* [`mount`(2))](help://man/2/mount). diff --git a/Base/usr/share/man/man8/EchoServer.md b/Base/usr/share/man/man8/EchoServer.md deleted file mode 100644 index 7b945be7afe..00000000000 --- a/Base/usr/share/man/man8/EchoServer.md +++ /dev/null @@ -1,23 +0,0 @@ -## Name - -EchoServer - Serenity echo server - -## Synopsis - -```**sh -$ EchoServer [options] -``` - -## Description - -EchoServer is a basic echo server for Serenity. By default, it runs on port 7. - -## Options - -* `-p`: Choose different port for EchoServer to attach to. - -## Examples - -```sh -$ EchoServer -p 1234 -``` diff --git a/Base/usr/share/man/man8/TelnetServer.md b/Base/usr/share/man/man8/TelnetServer.md deleted file mode 100644 index 10c1901236b..00000000000 --- a/Base/usr/share/man/man8/TelnetServer.md +++ /dev/null @@ -1,27 +0,0 @@ -## Name - -TelnetServer - Serenity telnet server - -## Synopsis - -```sh -$ TelnetServer [-p port] [-c command] -``` -## Description - -TelnetServer is a basic telnet server for Serenity. By default, it -runs on port 23 and provides a shell upon connection. This program -must be run as root. - -## Options - -* `--help`: Display help message and exit -* `--version`: Print version -* `-p port`: Port to listen on -* `-c command`: Program to run on connection - -## Examples - -```sh -$ TelnetServer -p 24 -c "/usr/bin/nyancat -f 60" -``` diff --git a/Base/usr/share/man/man8/WebServer.md b/Base/usr/share/man/man8/WebServer.md deleted file mode 100644 index 571e3a3fd58..00000000000 --- a/Base/usr/share/man/man8/WebServer.md +++ /dev/null @@ -1,24 +0,0 @@ -## Name - -WebServer - Serenity web server - -## Synopsis - -```sh -$ WebServer [--listen-address listen_address] [--port port] [--user username] [--pass password] [path] -``` - -## Options - -* `--help`: Display help message and exit -* `--version`: Print version -* `-l listen_address`, `--listen-address listen_address`: IP address to listen on -* `-p port`, `--port port`: Port to listen on -* `-U username`, `--user username`: HTTP basic authentication username -* `-P password`, `--pass password`: HTTP basic authentication password - -## Arguments - -* `path`: Path to serve the contents of - -<!-- Auto-generated through ArgsParser --> diff --git a/Base/usr/share/man/man8/blockdev.md b/Base/usr/share/man/man8/blockdev.md deleted file mode 100644 index 66134774cb9..00000000000 --- a/Base/usr/share/man/man8/blockdev.md +++ /dev/null @@ -1,30 +0,0 @@ -## Name - -blockdev - query block devices - -## Synopsis - -```**sh -$ blockdev [options] <device> -``` - -## Description - -The `blockdev` command call ioctls on the given block device. - -## Options - -* `-s`, `--size`: Get disk size in bytes -* `-b`, `--block-size`: Get block size in bytes - -## Examples - -```sh -# Get disk size -# blockdev -s /dev/hda -863718912 - -# Get block size -# blockdev -b /dev/hda -512 -``` diff --git a/Base/usr/share/man/man8/dmesg.md b/Base/usr/share/man/man8/dmesg.md deleted file mode 100644 index e406ed6e439..00000000000 --- a/Base/usr/share/man/man8/dmesg.md +++ /dev/null @@ -1,19 +0,0 @@ -## Name - -dmesg - display the kernel log - -## Synopsis - -```**sh -# dmesg -``` - -## Description - -`dmesg` displays the kernel log (as seen on serial debug output). Only messages printed by kernel processes are displayed. - -## Examples - -```sh -$ dmesg -``` diff --git a/Base/usr/share/man/man8/groupadd.md b/Base/usr/share/man/man8/groupadd.md deleted file mode 100644 index 1dffa435957..00000000000 --- a/Base/usr/share/man/man8/groupadd.md +++ /dev/null @@ -1,36 +0,0 @@ -## Name - -groupadd - add a new group to the system group file - -## Synopsis - -```**sh -# groupadd [options] <group> -``` - -## Description - -This program adds a new group to the system. - -This program must be run as root. - -## Options - -* `-g`, `--gid` _gid_: The group identifier for the new group. If not specified, an unused GID above `100` will be auto-generated. -* `-U`, `--users` user-list: A comma-separated list of usernames to add as members of the new group - -## Files - -* `/etc/group` - new group information (such GID) is appended to this file. - -## Examples - -```sh -# groupadd -g 110 contributors -# groupadd maintainers -``` - -## See Also -* [`useradd`(8)](help://man/8/useradd) -* [`groupdel`(8)](help://man/8/groupdel) -* [`groups`(1)](help://man/1/groups) diff --git a/Base/usr/share/man/man8/groupdel.md b/Base/usr/share/man/man8/groupdel.md deleted file mode 100644 index 5ae3f074e59..00000000000 --- a/Base/usr/share/man/man8/groupdel.md +++ /dev/null @@ -1,45 +0,0 @@ -## Name - -groupdel - delete a group - -## Synopsis - -```**sh -# groupdel <group> -``` - -## Description - -This program deletes a group in the system. - -This program must be run as root. - -## Caveats - -You may not remove the primary group of any existing user. You must remove the user before you remove the group. - -You should manually check all file systems to ensure that no files remain owned by this group. - -You should manually check all users to ensure that no user remain in this group. - -## Exit Values - -* 0 - Success -* 1 - Couldn't update the group file -* 6 - Specified group doesn't exist -* 8 - Can't remove user's primary group - -## Files - -* `/etc/group` - group information (such as GID) in this file is deleted. - -## Examples - -```sh -# groupdel alice -``` - -## See Also -* [`userdel`(8)](help://man/8/userdel) -* [`groupadd`(8)](help://man/8/groupadd) -* [`groups`(1)](help://man/1/groups) diff --git a/Base/usr/share/man/man8/hostname.md b/Base/usr/share/man/man8/hostname.md deleted file mode 100644 index 52a71b6f170..00000000000 --- a/Base/usr/share/man/man8/hostname.md +++ /dev/null @@ -1,28 +0,0 @@ -## Name - -hostname - print or set hostname - -## Synopsis - -```**sh -$ hostname [hostname] -``` - -## Description - -`hostname` prints current host name. If the `hostname` argument is give and the user is a superuser, the utility sets the current host name to its value. The maximum hostname length is 64 characters. The hostname is not persisted after reboot. - -## Examples - -```sh -# Print current hostname -$ hostname -courage - -# Set the new hostname -# hostname foo - -# Print new hostname -# hostname -foo -``` diff --git a/Base/usr/share/man/man8/lsblk.md b/Base/usr/share/man/man8/lsblk.md deleted file mode 100644 index 9acf2eda43e..00000000000 --- a/Base/usr/share/man/man8/lsblk.md +++ /dev/null @@ -1,24 +0,0 @@ -## Name - -lsblk - list connected storage devices - -## Synopsis - -```**sh -$ lsblk -``` - -## Description - -lsblk is a utility for displaying information about storage devices in the system. -It shows a brief list of these devices. - -## Files - -* `/sys/devices/storage` - source of the storage devices list. - -## Examples - -```sh -$ lsblk -``` diff --git a/Base/usr/share/man/man8/lspci.md b/Base/usr/share/man/man8/lspci.md deleted file mode 100644 index 8816b7902b8..00000000000 --- a/Base/usr/share/man/man8/lspci.md +++ /dev/null @@ -1,30 +0,0 @@ -## Name - -lspci - list connected PCI devices - -## Synopsis - -```**sh -$ lspci -``` - -## Description - -lspci is a utility for displaying information about PCI buses in the system -and devices connected to them. It shows a brief list of devices. - -## Options - -* `-n`, `--numerical`: Don't try to resolve numerical PCI IDs. This is useful when -there is a need to see the actual PCI IDs, or if `/res/pci.ids` file is not available. - -## Files - -* `/sys/bus/pci` - source of the PCI devices list. -* `/res/pci.ids` - a database of PCI identifiers used to match available devices to their vendor, device and class names. - -## Examples - -```sh -$ lspci -``` diff --git a/Base/usr/share/man/man8/mount.md b/Base/usr/share/man/man8/mount.md deleted file mode 100644 index b69e1fb385e..00000000000 --- a/Base/usr/share/man/man8/mount.md +++ /dev/null @@ -1,55 +0,0 @@ -## Name - -mount - mount a filesystem - -## Synopsis - -```**sh -$ mount -# mount -a -# mount <source> <target> [-t fstype] [-o options] -``` - -## Description - -If invoked without any arguments, `mount` prints a list of all currently mounted -filesystems. - -If invoked as `mount -a`, `mount` mounts all the filesystems configured in -`/etc/fstab` and `/etc/fstab.d/*`. This is normally done on system startup by -[`SystemServer`(7)](help://man/7/SystemServer). - -Otherwise, `mount` performs a single filesystem mount. Source should be a path -to a file containing the filesystem image. Target and fstype have the same -meaning as in the [`mount`(2)](help://man/2/mount) syscall (if not specified, -fstype defaults to `ext2`). - -A special source value "none" is recognized, in which case -[`mount`(8)](help://man/8/mount) will not attempt to open the source as a file, and will -pass an invalid file descriptor to [`mount`(2)](help://man/2/mount). This is -useful for mounting pseudo filesystems. - -Options correspond to the mount flags, and should be specified as a -comma-separated list of flag names (lowercase and without the `MS_` prefix). -Additionally, the name `defaults` is accepted and ignored. - -## Files - -* `/etc/fstab` - read by `mount -a` on startup to find out which filesystems to mount. -* `/etc/fstab.d` - directory with drop-in additions to the normal `fstab` file, also read by `mount -a`. -* `/sys/kernel/df` - read by `mount` to get information about mounted filesystems. - -## Examples - -```sh -# mount devpts /dev/pts -t devpts -o noexec,nosuid -# mount /home/anon/myfile.txt /tmp/foo -o bind - -# mount a regular file using a temporary loop device -$ mount /home/anon/myfilesystem.bin /mnt -``` - -## See also - -* [`mount`(2)](help://man/2/mount) -* [`umount`(8)](help://man/8/umount) diff --git a/Base/usr/share/man/man8/ping.md b/Base/usr/share/man/man8/ping.md deleted file mode 100644 index 34ee2687083..00000000000 --- a/Base/usr/share/man/man8/ping.md +++ /dev/null @@ -1,26 +0,0 @@ -## Name - -ping - send ICMP ECHO_REQUEST packets to network hosts - -## Synopsis - -```sh -$ ping [--count count] [-i interval] [-W interval] [--size size] [--quiet] [-t ttl] [--adaptive] [--flood] <host> -``` - -## Options - -* `--help`: Display help message and exit. -* `--version`: Print version. -* `-c count`, `--count count`: Stop after sending specified number of ECHO_REQUEST packets. -* `-i interval`: Wait `interval` seconds between sending each ECHO_REQUEST packet. Fractional seconds are allowed. Only super-user can set the interval value less than 0.2 seconds. -* `-W interval`: Time in seconds to wait for a reply for each packet sent. Fractional seconds are allowed. 0 means infinite timeout. -* `-s size`, `--size size`: Amount of bytes to send as payload in the ECHO_REQUEST packets. -* `-q`, `--quiet`: Quiet mode. Only display the summary when finished. -* `-t`, `--ttl`: Set the TTL(time-to-live) value of the ICMP packets. -* `-A`, `--adaptive`: Adaptive ping. The interval between each ECHO_REQUEST adapts to the RTT(round-trip-time). -* `-f`, `--flood`: Flood ping. For every ECHO_REQUEST sent a period '.' is printed, while for every ECHO_REPLY received a backspace is printed. This provides a rapid display of how many packets are being dropped. If interval is not given, it sets interval to 0 and outputs packets as fast as they come back. Only super-user may use this option with 0 interval. - -## Arguments - -* `host`: Host to ping diff --git a/Base/usr/share/man/man8/pls.md b/Base/usr/share/man/man8/pls.md deleted file mode 100644 index 8e8de47dd4a..00000000000 --- a/Base/usr/share/man/man8/pls.md +++ /dev/null @@ -1,40 +0,0 @@ -## Name - -pls - Execute a command as root - -## Synopsis - -```**sh -$ pls [command] -``` - -## Description - -Executes a command as superuser (UID and GID 0). This command is only available for users in the `wheel` group. - -It is possible to execute commands that contain hyphenated options via the use of `--`, which signifies the -end of command options. For example: - -```sh -$ pls -- ls -la -``` - -## Examples - -```sh -$ pls whoami -Password: -root -$ -``` - -```sh -$ pls sh -Password: -# whoami -root -# -``` - -## See also -* [`su`(1)](help://man/1/su) diff --git a/Base/usr/share/man/man8/purge.md b/Base/usr/share/man/man8/purge.md deleted file mode 100644 index b49bb61f239..00000000000 --- a/Base/usr/share/man/man8/purge.md +++ /dev/null @@ -1,28 +0,0 @@ -## Name - -purge - release memory - -## Synopsis - -```**sh -$ purge [options] -``` - -## Description - -This program instructs the kernel to release memory that can -be released without interacting with its userspace owner(s). - -## Options - -* `-c`: Release all clean inode-backed memory. -* `-v`: Release all purgeable memory currently marked volatile. - -If no options are specified, all possible memory is released. - -## Examples - -```sh -# purge -Purged page count: 744 -``` diff --git a/Base/usr/share/man/man8/sysctl.md b/Base/usr/share/man/man8/sysctl.md deleted file mode 100644 index ebd93019397..00000000000 --- a/Base/usr/share/man/man8/sysctl.md +++ /dev/null @@ -1,53 +0,0 @@ -## Name - -sysctl - configure kernel parameters at runtime - -## Synopsis - -```**sh -# sysctl [-a] [-w] [variable[=value]...] -``` - -## Description - -sysctl is a utility for managing kernel configuration parameters at runtime. -This requires root privileges, and can crash your system. -Available parameters are listed under /sys/kernel/conf/. - -## Options - -* `-a`: Display all kernel parameters and associated values. -* `-w`: Set kernel parameters to the specified values. - -## Arguments - -* `variable`: Retrieve the specified parameter. -* `variable=value`: Set the specified parameter to the specified value. The option `-w` has to be specified. - -## Files - -* `/sys/kernel/conf` - source of kernel parameters - -## Examples - -View all parameters: - -```sh -# sysctl -a -``` - -View `ubsan_is_deadly` parameter: - -```sh -# sysctl ubsan_is_deadly -ubsan_is_deadly = 1 -``` - -Set `ubsan_is_deadly` parameter to zero (disabled): -(Note: This requires root privileges) - -```sh -# su -# sysctl -w ubsan_is_deadly=0 -ubsan_is_deadly: 1 -> 0 -``` diff --git a/Base/usr/share/man/man8/umount.md b/Base/usr/share/man/man8/umount.md deleted file mode 100644 index d1c784a6bac..00000000000 --- a/Base/usr/share/man/man8/umount.md +++ /dev/null @@ -1,26 +0,0 @@ -## Name - -umount - unmount file system - -## Synopsis - -```**sh -$ umount <mountpoint> -``` - -## Arguments -* `mountpoint`: File system path to unmount - -## Description - -`umount` run as root unmounts a file system mounted at specified `mountpoint`. - -## Examples - -```sh -# umount / -``` - -## See also - -* [`mount`(8)](help://man/8/mount) diff --git a/Base/usr/share/man/man8/useradd.md b/Base/usr/share/man/man8/useradd.md deleted file mode 100644 index c24784f260a..00000000000 --- a/Base/usr/share/man/man8/useradd.md +++ /dev/null @@ -1,54 +0,0 @@ -## Name - -useradd - add a new user to the system password file - -## Synopsis - -```**sh -# useradd [options] <login> -``` - -## Description - -This program adds a new user to the system. - -By default, the user will be added to the **users** group (which has a GID of 100). - -This program must be run as root. - -## Options - -* `-u`, `--uid` _uid_: The user identifier for the new user. If not specified, an unused UID above `1000` will be auto-generated. -* `-g`, `--gid` _group_: The group name or identifier for the new user. If not specified, it will default to 100 (the **users** group). -* `-p`, `--password` _password_: The encrypted password for the new user. If not specified, it will default to blank. -* `-s`, `--shell` _path-to-shell_: The shell binary for this login. The default is `/bin/Shell`. -* `-m`, `--create-home`: Create the specified home directory for this new user. -* `-d`, `--home-dir` _path_: Set the home directory for this user to path. By default, this is `/home/username`, where `username` is the value of login. -* `-n`, `--gecos` _general-info_: GECOS information about this login. See [Wikipedia](https://en.wikipedia.org/wiki/Gecos_field) for more information. - -## Exit Values - -* 0 - Success -* 1 - Couldn't update the password file -* 3 - Invalid argument to option -* 4 - UID already in use -* 12 - Couldn't create home directory - -## Files - -* `/etc/passwd` - new user information (such as UID and GID) is appended to this file. -* `/home/` - user home directory is created here if the `-m` flag is specified. - -## Examples - -```sh -# useradd -u 300 -m kling -# useradd -m -u 400 --gid 200 --gecos "Sergey Bugaev" bugaevc -# useradd quaker -# useradd --gid 1000 -d /tmp/somedir -n "Dan MacDonald" danboid -# useradd --create-home supercomputer7 -``` - -## See also -* [`userdel`(8)](help://man/8/userdel) -* [`usermod`(8)](help://man/8/usermod) diff --git a/Base/usr/share/man/man8/userdel.md b/Base/usr/share/man/man8/userdel.md deleted file mode 100644 index 204e1e6720c..00000000000 --- a/Base/usr/share/man/man8/userdel.md +++ /dev/null @@ -1,43 +0,0 @@ -## Name - -userdel - delete a user account - -## Synopsis - -```**sh -# userdel [-r] <login> -``` - -## Description - -This program deletes a user account in the system. - -This program must be run as root. - -## Options - -* `-r`, `--remove`: Remove the home directory for this user if the directory exists. - -## Exit Values - -* 0 - Success -* 1 - Couldn't update the password file -* 6 - Specified user doesn't exist -* 12 - Couldn't remove home directory - -## Files - -* `/etc/passwd` - user information (such as UID and GID) in this file is deleted. -* `/home/` - user home directory is deleted if the `-r` flag is specified. - -## Examples - -```sh -# userdel alice -# userdel -r alice -# userdel --remove alice -``` - -## See Also -* [`usermod`(8)](help://man/8/usermod) -* [`useradd`(8)](help://man/8/useradd) diff --git a/Base/usr/share/man/man8/usermod.md b/Base/usr/share/man/man8/usermod.md deleted file mode 100644 index 2c98a826268..00000000000 --- a/Base/usr/share/man/man8/usermod.md +++ /dev/null @@ -1,38 +0,0 @@ -## Name - -usermod - modify a user account - -## Synopsis - -```sh -$ usermod [--append] [--uid uid] [--gid group] [--groups groups] [--lock] [--remove] [--unlock] [--home new-home] [--move] [--shell path-to-shell] [--gecos general-info] <username> -``` - -## Description - -This program modifies an existing user account. -This program must be run as root. - -## Options - -* `--help`: Display help message and exit -* `--version`: Print version -* `-a`, `--append`: Append the supplementary groups specified with the -G option to the user -* `-u uid`, `--uid uid`: The new numerical value of the user's ID -* `-g group`, `--gid group`: The group name or number of the user's new initial login group -* `-G groups`, `--groups groups`: Set the user's supplementary groups. Groups are specified with a comma-separated list. Group names or numbers may be used -* `-L`, `--lock`: Lock password -* `-r`, `--remove`: Remove the supplementary groups specified with the -G option from the user -* `-U`, `--unlock`: Unlock password -* `-d new-home`, `--home new-home`: The user's new login directory -* `-m`, `--move`: Move the content of the user's home directory to the new location -* `-s path-to-shell`, `--shell path-to-shell`: The name of the user's new login shell -* `-n general-info`, `--gecos general-info`: Change the GECOS field of the user - -## Arguments - -* `username`: Username of the account to modify - -## See also -* [`userdel`(8)](help://man/8/userdel) -* [`useradd`(8)](help://man/8/useradd) diff --git a/Base/usr/share/shell/completion/builtin.sh b/Base/usr/share/shell/completion/builtin.sh deleted file mode 100644 index 8850961b228..00000000000 --- a/Base/usr/share/shell/completion/builtin.sh +++ /dev/null @@ -1,90 +0,0 @@ -#!/bin/Shell - -_complete_unalias() { - shift 2 - argsparser_parse \ - --add-positional-argument names --help-string _ --value-name _ --short-name '' --min 0 --max 9999 \ - -- $* - name='' - if test ${length $names} -ne 0 { - name="$names[-1]" - } - invariant="${length $name}" - for $(alias | grep "^$name") { - n=${regex_replace '"' '\"' ${regex_replace '\\([^\\])' '\1' ${regex_replace '=.*' '' "$it"}}} - v=${regex_replace '"' '\"' ${regex_replace '\\([^\\])' '\1' ${regex_replace '[^=]*=' '' "$it"}}} - echo '{"kind":"plain","completion":"'"$n"'", "trailing_trivia":" ", "display_trivia":"'"$v"'", "invariant_offset": '$invariant'}' - } -} - -__complete_job_spec() { - match $1 as hint { - %?* as (name) { - for $(jobs | grep "$name") { - id='' - match $it { - [*]\ * as (i _) { id=$i } - * { continue } - } - echo '{"kind":"plain","static_offset":'"${length "?$name"}"',"invariant_offset":0,"completion":"'"$id"'"}' - } - } - %* as (id) { - invariant=${length $id} - for $(jobs | grep "^\\[$id\\d+\\]") { - id='' - match $it { - [*]\ * as (i _) { id=$i } - * { continue } - } - echo '{"kind":"plain","static_offset":0,"invariant_offset":'"$invariant"',"completion":"'"$id"'"}' - } - } - (?<pid>^\d+$) { - invariant=${length $pid} - for $(ps -e | grep "^ *$pid") { - id='' - description='' - match $it { - "*$pid* *" as (_ i rest) { id="$pid$i" description="$rest" } - * { continue } - } - echo '{"kind":"plain","static_offset":0,"invariant_offset":'"$invariant"',"completion":"'"$id"'","display_trivia":"'"$description"'"}' - } - } - * as (name) { - static="${length $name}" - for $(ps -e | grep "$name") { - id='' - description='' - match $it { - (?: *(?<pid>\d+) (?<rest>.*)) { id="$pid" description="$rest" } - * { continue } - } - echo '{"kind":"plain","static_offset":'"$static"',"invariant_offset":0,"completion":"'"$id"'","display_trivia":"'"$description"'","allow_commit_without_listing":false}' - } - } - } -} - -_complete_kill() { - if test $*[-1] = '--' { - __complete_job_spec '' - } else { - __complete_job_spec $*[-1] - } -} - -_complete_cd() { - if test $*[-1] = '--' { - invariant_offset=0 - results=${concat_lists .*/ */} - } else { - invariant_offset=${length "$*[-1]"} - results=$(glob "$*[-1]*/") - } - - for $results { - echo '{"kind":"plain","static_offset":0,"invariant_offset":'"$invariant_offset"',"completion":"'"${remove_suffix / $it}"'","trailing_trivia":"/"}' - } -} diff --git a/Base/usr/share/shell/completion/proxy.sh b/Base/usr/share/shell/completion/proxy.sh deleted file mode 100644 index 2179cd42ba7..00000000000 --- a/Base/usr/share/shell/completion/proxy.sh +++ /dev/null @@ -1,30 +0,0 @@ -#!/bin/Shell - -__proxy() { - echo '{"kind":"proxy","argv":"'"${regex_replace '"' '\"' "$*"}"'"}' -} - -# Builtins -_complete_time() { - shift 2 - argsparser_parse \ - --add-option _ --help-string "Number of iterations" \ - --long-name iterations --short-name n --value-name iterations --type u32 \ - --add-positional-argument argv --help-string _ \ - --value-name _ --min 0 --max 9999999 \ - --stop-on-first-non-option \ - -- $* - __proxy $argv -} - -# Utilities -_complete_pls() { - shift 2 - argsparser_parse \ - --add-option _ --help-string "User to execute as" --short-name u --value-name UID \ - --add-positional-argument argv --help-string "Command to run at elevated privilege level" \ - --value-name command --min 0 --max 999999 \ - --stop-on-first-non-option \ - -- $* - __proxy $argv -} diff --git a/Base/www/index.html b/Base/www/index.html deleted file mode 100644 index 093ce7f5d94..00000000000 --- a/Base/www/index.html +++ /dev/null @@ -1,62 +0,0 @@ -<!DOCTYPE html> -<html lang="en"> - <head> - <meta charset="UTF-8" /> - <title>Serenity WebServer Start Page! - - - - -
-
- -

Serenity WebServer Start Page!

-
- -
-
- -

- If you are reading this in a browser, I think we may have succeeded! - Unless of course you just loaded the local file in your browser - (cheater!) :^) -

-

Try this link to another page!

-
-
- - diff --git a/Base/www/ladyball.png b/Base/www/ladyball.png deleted file mode 100644 index 9d7fbbce31a..00000000000 Binary files a/Base/www/ladyball.png and /dev/null differ diff --git a/Base/www/other.html b/Base/www/other.html deleted file mode 100644 index cd9dbbda1bc..00000000000 --- a/Base/www/other.html +++ /dev/null @@ -1,58 +0,0 @@ - - - - - Serenity WebServer Other Page! - - - - -
-
- -

Serenity WebServer Other Page!

-
- -
-
- -

This is not even index.html, how neat is that! :^)

-

Maybe you want to go back to index.html?

-
-
- - diff --git a/Documentation/BareMetalInstallation.md b/Documentation/BareMetalInstallation.md deleted file mode 100644 index 4086e1cffa4..00000000000 --- a/Documentation/BareMetalInstallation.md +++ /dev/null @@ -1,61 +0,0 @@ -# Serenity installation guide - -## DISCLAIMER - -Whilst it is possible to run Serenity on physical x86-compatible hardware, it is not yet ready to be used by non-technical users who aren't prepared to report bugs or assist with its development. For this reason, there are currently no pre-built install images so a bare-metal installation requires that you build an installation image from source. Current hardware support is extremely limited. Most successful hard disk installations have been on Pentium 4 era hardware but by network booting Serenity (which is no longer supported) users have been able to get it running on more modern hardware such as Core i5 machines. - - -## Hardware support and requirements - -Storage-wise Serenity requires a >= 2 GB parallel ATA or SATA disk for IDE/AHCI. Some older SATA chipsets already operate in IDE mode whilst some newer ones will depend upon adjusting a BIOS option to run your SATA controller in IDE (sometimes referred to as Legacy or PATA) mode. SATA AHCI is supported, but may not work on every controller due to bugs in the implementation. -NVMe drives are supported but it is recommended to use `nvme_poll` boot parameter in newer hardwares at the moment. SCSI, SAS and eMMC HBAs are all presently unsupported. - -You must be willing to wipe your disk's contents to allow for writing the Serenity image so be sure to back up any important data on your disk first! Serenity uses the GRUB2 bootloader so it should be possible to multiboot it with any other OS that can be booted from GRUB2 post-installation. - -Serenity currently has no support for USB but some machines will emulate PS/2 keyboards and mice in the BIOS via USB. BIOS USB PS/2 emulation can be buggy so having real PS/2 input devices is recommended. A minimum of 256 MB RAM and a x86-64 CPU are required. - -At present there is no real GPU support so don't expect OpenGL, Vulkan nor accelerated video playback and encoding support. Serenity currently relies upon VESA BIOS extensions to provide its display output and so it only runs on BIOS-based PCs. There is no WiFi support and the network card chipsets that are currently supported: Intel e1000, Intel e1000e and Realtek 8168. The e1000 driver has only been tested with qemu and VirtualBox although it may work with NICs such as those using the Intel 82545XX, 82540XX, 82546XX or similar chipsets. Supported sound cards are Intel AC'97 and Intel HDA PCI devices. - -For more details on known working hardware see the [SerenityOS Hardware Compatibility List](HardwareCompatibility.md). - -## Creating a Serenity GRUB disk image - -Before creating a Serenity disk image, you need to build the OS as described in the [SerenityOS build instructions](BuildInstructions.md). Follow those instructions up to and including running **ninja install** (`Meta/serenity.sh image `). After the OS has built, run **ninja grub-image** to create a new file called **grub_disk_image** with GRUB2 installed that can be booted on a real PC. This command requires `parted` and `grub2` (Arch: `grub`) to be installed. - -The final step is copying **grub_disk_image** onto the disk you wish to use to boot Serenity using a command such as: - -``` -$ sudo dd if=grub_disk_image of=/dev/sdx bs=64M && sync -``` - -Replace **/dev/sdx** with the target device. The **bs=64M** argument is optional but will speed up the data transfer. You can also use any other image flashing application. Flashing under Windows is possible; you can find the WSL files under `\\wsl$\\`. - -## Troubleshooting Serenity boot issues with Linux using a null modem (serial) cable - -Many guides on the internet recommend using `screen` to monitor or interact with a serial console under Linux. Using `screen` is an option but it is quite tricky to copy and paste the output from a `screen` console when there is more than one screens worth of text. So, unless you are already experienced with `screen` it is recommended you use `cu`. - -After installing `cu`, you will not be able to connect to your serial console device until you have added your user to the **dialout** group. You must log out and log back in again after running a command such as: - -``` -$ sudo usermod -aG dialout YourLinuxUserName -``` - -Once you are logged in with a user who is a member of the **dialout** group, you can connect to a USB serial console using a command like: - -``` -$ cu -s 57600 -l /dev/ttyUSB0 -``` - -## Troubleshooting boot issues without a serial port - -During the boot process, you should be able to see logging of important messages on the screen, printed solely by the kernel. -If it happens to you that the system hangs, you should be able to see the last message on the screen. It can be either -an assertion or kernel panic. Depending on your hardware setup, the framebuffer could be 80x25 VGA text mode, or high resolution -framebuffer with 8x8 font glyphs. - -You can force capable multiboot bootloaders to boot Serenity into high resolution mode by editing **Kernel/Arch/i386/Boot/boot.S** and -adding **| MULTIBOOT_VIDEO_MODE** to the end of the **multiboot_flags** before building Serenity. - -Setting a boot argument of `graphics_subsystem_mode=limited` will force the kernel to not initialize any framebuffer devices, hence allowing the system to boot into console-only mode as `SystemServer` will detect this condition and will not initialize `WindowServer`. - -If you do not see any output, it's possible that the Kernel panics before any video is initialized. In that case, you might try debugging the init sequence with the PC speaker to see where it gets stuck. diff --git a/Documentation/BuildInstructionsMacOS.md b/Documentation/BuildInstructionsMacOS.md deleted file mode 100644 index 74d50dc07a3..00000000000 --- a/Documentation/BuildInstructionsMacOS.md +++ /dev/null @@ -1,59 +0,0 @@ -# Setting up a development environment on macOS - -# Prerequisites - -This installation guide assumes that you have [Homebrew](https://brew.sh) and Xcode installed. You need to open Xcode at least once for it to install the required tools. - -Before you build, you must set your command line tools to Xcode's tools instead of the ones installed via Homebrew: -```console -sudo xcode-select --switch /Applications/Xcode.app -``` - -Make sure you also have all the following dependencies installed: - -```console -# core -brew install coreutils e2fsprogs qemu bash imagemagick ninja cmake ccache rsync zstd - -# (option 1) fuse + ext2 -brew install m4 autoconf automake libtool -brew install --cask macfuse -Toolchain/BuildFuseExt2.sh - -# (option 2) genext2fs -brew install genext2fs - -# for kernel debugging, on Apple Silicon -brew install x86_64-elf-gdb -``` - -If you have Xcode version 14.2 or older, also install a newer host compiler from homebrew. Xcode 14.3 is known to work. - -```console -brew install llvm@18 -# OR -brew install gcc@13 -``` - -# Notes - -You can use both Intel and Apple Silicon Macs to run the x86-64 version of SerenityOS. You do not -need to install Rosetta for this. An emulator is used when running on an Apple Silicon, so Serenity -will be slower compared to running natively with hardware-assisted virtualization on an Intel machine. - -If you're building on M1 Mac and have Homebrew installed in both Rosetta and native environments, -you have to make sure that required packages are installed only in one of the environments. Otherwise, -these installations can conflict during the build process, which is manifested in hard to diagnose issues. -Building on M1 natively without Rosetta is recommended, as the build process should be faster without Rosetta -overhead. - -Installing macfuse for the first time requires enabling its system extension in System Preferences and then restarting your machine. The output from installing macfuse with brew says this, but it's easy to miss. - -It's important to make sure that Xcode is not only installed but also accordingly updated, otherwise CMake will run into incompatibilities with GCC. - -Homebrew is known to ship bleeding edge CMake versions, but building CMake from source with homebrew -gcc or llvm may not work. If homebrew does not offer cmake 3.25.x+ on your platform, it may be necessary -to manually run Toolchain/BuildCMake.sh with Apple clang from Xcode as the first compiler in your $PATH. - -If you want to debug the x86-64 kernel on an Apple Silicon machine, you can install the `x86_64-elf-gdb` -package to get a native build of GDB that can cross-debug x86-64 code. diff --git a/Documentation/BuildInstructionsOther.md b/Documentation/BuildInstructionsOther.md deleted file mode 100644 index fa57f7624d9..00000000000 --- a/Documentation/BuildInstructionsOther.md +++ /dev/null @@ -1,77 +0,0 @@ -# Installing build requisites on other systems - -### Fedora - -```console -sudo dnf install texinfo binutils-devel curl cmake mpfr-devel libmpc-devel gmp-devel e2fsprogs ninja-build patch ccache rsync @"C Development Tools and Libraries" @Virtualization -``` -Optional: `fuse2fs` for [building images without root](https://github.com/SerenityOS/serenity/pull/11224). - -## openSUSE - -```console -sudo zypper install curl cmake mpfr-devel mpc-devel ninja gmp-devel e2fsprogs patch qemu-x86 qemu-audio-pa gcc gcc-c++ ccache rsync patterns-devel-C-C++-devel_C_C++ -``` - -## Void Linux - -```console -sudo xbps-install -S base-devel cmake curl mpfr-devel libmpc-devel gmp-devel e2fsprogs ninja qemu ccache rsync -``` - -## ALT Linux - -```console -apt-get install curl cmake libmpc-devel gmp-devel e2fsprogs libmpfr-devel ninja-build patch gcc ccache rsync -``` - -## NixOS - -You can use the flake in the root directory to enter a devShell that has all the required packages and tools to build SerenityOS: - -```console -nix develop -``` - -Or you can use the legacy `nix-shell` tool to enter the devShell: - -```console -nix-shell Toolchain -``` - -This will use the `Toolchain/default.nix` file and your host `nixpkgs`. - -## Alpine Linux - -First, make sure you have enabled the `community` repository in `/etc/apk/repositories` and run `apk update`. It has been tested on `edge`, YMMV on `stable`. - -```console -# the basics, if you have not already done so -apk add bash curl git util-linux sudo - -# GNU coreutils for GNU's version of `du` -apk add coreutils - -# rough equivalent of build-essential -apk add build-base - -# qemu -apk add qemu qemu-system-x86_64 qemu-img qemu-ui-gtk qemu-audio-pa - -# build tools (samurai is a drop-in replacement for ninja) -apk add cmake e2fsprogs grub-bios samurai mpc1-dev mpfr-dev gmp-dev ccache rsync texinfo -``` -Optional: `fuse2fs` for [building images without root](https://github.com/SerenityOS/serenity/pull/11224). - -## OpenBSD prerequisites - -```console -doas pkg_add bash cmake g++ gcc git gmake gmp ninja ccache rsync coreutils qemu sudo e2fsprogs -``` - -## FreeBSD prerequisites - -```console -pkg install qemu bash cmake coreutils e2fsprogs fusefs-ext2 gcc11 git gmake ninja sudo gmp mpc mpfr ccache rsync -``` -Optional: `fusefs-ext2` for [building images without root](https://github.com/SerenityOS/serenity/pull/11224). diff --git a/Documentation/BuildInstructionsWindows.md b/Documentation/BuildInstructionsWindows.md deleted file mode 100644 index 9bd1abecf5a..00000000000 --- a/Documentation/BuildInstructionsWindows.md +++ /dev/null @@ -1,54 +0,0 @@ -# Setting up a development environment on Windows - -SerenityOS can be built and run under WSL Version 2. -WSL Version 1 is not supported since Version 1 does not support ext2, which is needed for the setup. - -WSL Version 2 requires Windows 10 version 2004 or higher, with OS Build 19041 or greater. Here is a guide on how to -[get WSL2](https://docs.microsoft.com/en-us/windows/wsl/install-win10). - -Once installed, you will need to make sure the distribution you want to use (and the new default) is using Version 2: -- `wsl -l -v` lists distros and versions,
-- `wsl --set-version ` is used to convert a distro to another version, and
-- `wsl --set-default-version 2` will set the default version for all new distros (if desired.)
- -Next, go to [BuildInstructions.md](https://github.com/SerenityOS/serenity/blob/master/Documentation/BuildInstructions.md#prerequisites) -and follow the instructions for your chosen Linux environment, to get the needed build tools. - -## Note on filesystems - -WSL2 filesystem performance for IO heavy tasks (such as compiling a large C++ project) on the host Windows filesystem is -pretty bad. See [this issue on the WSL GitHub project](https://github.com/microsoft/WSL/issues/4197#issuecomment-604592340) -for details. - -The recommendation from the Microsoft team on that issue is: - -> If it's at all possible, store your projects in the Linux file system in WSL2. - -In practice, this means cloning and building the project to somewhere such as `/home/username/serenity`. You can then -access the linux filesystem at `\\wsl$`, so for example, the project would be at `\\wsl$\home\username\serenity`. - -## Setting up QEMU - -Grab the latest QEMU binaries from [here](https://www.qemu.org/download/#windows) and install them. At a minimum you -will need to install the tools, the system emulators for i386 and x86_64, and -the DLL libraries. - -![QEMU Components](QEMU_Components.png) - -Run `Meta/serenity.sh run` to build and run SerenityOS as usual. - -### Hardware acceleration - -The steps above will run QEMU in software virtualization mode, which is very slow. -QEMU supports hardware acceleration on Windows via the [Windows Hypervisor Platform](https://docs.microsoft.com/en-us/virtualization/api/) -(WHPX). - -Enable the Windows Hypervisor Platform feature, either using "Turn Windows features on or off", or by running the -following command in an elevated PowerShell session: \ -`dism /Online /Enable-Feature /All /FeatureName:HypervisorPlatform` - -![WHPX Windows Feature](WHPX_Feature.png) - -You may have to reboot after enabling the WHPX feature. - -Afterwards you can start the VM with `Meta/serenity.sh run` as usual. diff --git a/Documentation/HardwareCompatibility.md b/Documentation/HardwareCompatibility.md deleted file mode 100644 index 0bcd4fb85eb..00000000000 --- a/Documentation/HardwareCompatibility.md +++ /dev/null @@ -1,38 +0,0 @@ -## SerenityOS Hardware Compatibility List - -A list of hardware known to be at least partly working with SerenityOS. - -Serenity boots to a graphical desktop on all machines unless otherwise noted. - -### Network Adapters - -| Model | Notes | -| ---------------------------------------- | ----------------------------- | -| Intel 82545XX | Also known as e1000 | -| Intel 82574L | Also known as e1000e | -| RTL8168/8111 (Variants B, E, E-VL & H) | Other variants are WIP | - -### Desktop machines - -| Make and model | Notes | -| ---------------------------------------- | ----------------------------- | -| Viglen VM3B | Has onboard RTL8139 NIC | - -### Laptops, notebooks and netbooks - -| Make and model | Notes | -| ---------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| Dell Inspiron mini 10 | ICH7-M SATA controller works in IDE mode, RTL810xE NIC and Intel NM10 audio unsupported | -| HP 15-ac108na | Pentium 3825U, Wildcat Point Chipset. AHCI works. Keyboard and trackpad work (both are PS2), trackpad scrolling/gestures don't work. RTL810xE NIC, RTL8723BE Wireless NIC, xHCI and Intel HDA audio unsupported. | -| Lenovo Ideapad 510s | Sunrise Point Chipset. AHCI works. Keyboard and trackpad work (both are PS2), RTL810xE NIC, Intel 3165 Wireless NIC, USB and Intel HDA audio unsupported. | - -### Motherboards - -| Make and model | Notes | -| ---------------------------------------- | ----------------------------------------------------------------| -| Intel Desktop Board D915GAG / D915PSY | Pentium 4 HT CPU | -| Intel Desktop Board D875PBZ | Pentium 4 HT CPU | -| Gigabyte G31M-ES2L | ICH7 2009 machine with IDE controller only | -| Asus PRIME B360-PLUS | Has only one PS2 port, AHCI works | -| Asus H81M-K | w/ Intel Core i3-4170, AHCI works | -| Acer OEM Motherboard, H57H-AM2 | w/ Intel Core i5-760, AHCI work (graphics not tested) | diff --git a/Documentation/HighDPI.md b/Documentation/HighDPI.md deleted file mode 100644 index 0b31b27335e..00000000000 --- a/Documentation/HighDPI.md +++ /dev/null @@ -1,193 +0,0 @@ -HighDPI design -============== - -Background ----------- - -- macOS: Only integer scale factors at app level. Can stretch final composited framebuffer at very end for non-integer display scales. - - advantages: simple programming model, still fairly flexible; scaling at end produces coherent final image - - disadvantages: needs 4x memory even for 1.5x scale; scaling at very makes final image less detailed than it could be - -- Android: Many (but discrete) scale levels (ldpi (0.75x), mdpi, hdpi (1.5x), xhdpi (2x), xxhdpi (3x), xxhdpi (4x)) - -- Windows: has "not recommended" free form text entry for scale factor between 100% and 500%. - -Integer scale factors are needed in any case so let's get that working first. Actually, let's focus on just 2x for now. - - -Desired end state ------------------ - -- All rects (Window and Widget rects, mouse cursor, even bitmap sizes) are in "logical" coordinates, which is the same as pixels at 1x scale, as much as possible. -- If something needs to be in pixels, its name starts with `physical_`. Physical coordinates should as much as possible not cross API boundaries. -- Jury's still out if logical coordinates should stay ints. Probably, but it means mouse cursor etc only have point resolution, not pixel resolution -- We should have something that can store a collection of (lazily-loaded?) bitmaps and fonts that each represent a single image / font at different scale levels, and at paint time the right representation is picked for the current scale - -Resource loading ----------------- - -Resources such as icons, cursors, bitmap fonts are scale-dependent: In HighDPI modes, different resources need to be loaded. - -### Art direction - -A 2x resource should look like a 1x resource, just with less jagged edges. A horizontal or vertical line that's 1 pixel wide in 1x should be 2 pixels wide in 2x. - -A good guideline for black-and-white images: start with a 1x bitmap, resize it to 200% using nearest-neighbor filtering, and then move black pixels around to smooth diagonal edges -- but the number of black pixels shouldn't change relative to the 200% nearest-neighbor-resampled image. If that's not possible, err towards making the icon smaller instead of larger. A good technique is to use the Ctrl-Shift-Super-I shortcut in HighDPI mode to toggle between low-res and high-res icons in HighDPI mode. - -While a 1x 32x32 bitmap and a 2x 16x16 bitmap both have 32x32 pixels, they don't have to look the same: The 2x 16x16 should look exactly like the corresponding 1x 16x16, just with smoother edges. The 1x 32x32 pixel resource could instead pick a different crop. As a concrete example, the 1x 7x10 ladybug emoji image currently just has the ladybug's shell (for space reasons), and so should the 2x version of that emoji. On the other hand, if we add a higher-res 1x 14x20 ladybug emoji at some point, we might want show the ladybug's legs on it, instead of just a smoother rendition of just the shell. (The 2x version of that 14x20 emoji would then have legs and shell in less jagged.) - -### Directory structure - -currently: - - res/ - cursors/ - arrowx2y2.png - ... - emoji/ (currently all 7x10 px) - U+1F346.png - ... - fonts/ - CsillaRegular10.font - ... - graphics/ - brand-banner.png - ... - icons/ - 16x16/ - small app icons, small filetype icons, toolbar icons, window buttons, ... - 32x32/ - large app icons, large filetype icons, message box icons - various per-app folders with in-app UI images (XXX: maybe move into "apps" subdir?) - themes/ - Coffee/ - 16x16/ - custom window buttons - (more themes) - ... - wallpapers/ - desktop wallpapers - -Every one of these could grow a 2x variant (and if we do more scale factors later, even more variants). - -Possible new structures: - -1. Have "1x", "2x" folders right inside res and then mirror folder structures inside them: - - res/ - 1x/ - cursors/ - 16x16/ - emoji/ - ... - 2x/ - cursors/ - 16x16/ - emoji/ - ... - -2. Instead of having the 1x/2x fork at the root, have it at each leaf: - - res/ - cursors/ - 1x/ - arrowx2y2.png - ... - 2x/ - arrowx2y2.png - ... - emoji/ - 1/ - U+1F346.png - ... - 2/ - U+1F346.png - ... - ... - -3. Use filename suffixes instead of directories (similar to macOS): - - res/ - cursors/ - arrowx2y2.png - arrowx2y2@2x.png - ... - emoji/ - U+1F346.png - U+1F346@2x.png - ... - -4. Use suffixes on directory instead of subdirectory: - - res/ - cursors/ - arrowx2y2.png - ... - cursors-2x/ - arrowx2y2.png - ... - -Root-level split makes it easy to see which scale factors exist and is subjectively aesthetically pleasing. - -Filename suffixes make it easy to see which icons don't have high-res versions (but in return clutter up an icon directory), and it makes it easy to get the intrinsic scale factor of a bitmap (just need to look at the image's basename, not at any directory). - -Android has additional modifiers in addition to scale factors in its resource system (UI language, light/dark mode, screen size in addition to resolution, etc). If we ever add more factors to the resource system, a suffix-based system would probably extend more nicely than a nesting-based one. - -In the end probably doesn't matter all that much which version to pick. - -For now, we're going with a "-2x" suffix on the file name. - -### Resource loading strategy tradeoffs - -- eagerly load one scale, reload at new scale on scale factor change events - - needs explicit code - - random code in LibGfx currently loads icons, and scale factor change events would be more a LibGUI level concept, not clear how to plumb the event to there - + memory efficient -- only have one copy of each resource in memory - + easy to understand: Bitmap stays Bitmap, Font stays Font, no need for collections - -- have BitmapCollection that stores high-res and low-res path and load lazily when needed - - need to do synchronous disk access at first paint on UI thread - - or load compressed data at each scale eagerly and decompress lazily. still a blocking decode on UI thread then, and needs more memory -- 2x compressed resources in memory even if they might never be needed - + puts complexity in framework, app doesn't have to care - + can transparently paint UI at both 1x and 2x into different backbuffers (eg for multiple screens that have different scale factors) - -- eagerly load both and use the right one at paint time - - similar to GUI::Icon - - 400% memory overhead in 1x mode (but most icons are small) - + conceptually easy to understand, but still need some collection class - + puts (less) complexity in framework, app doesn't have to care - + can transparently paint UI at both 1x and 2x into different backbuffers (eg for multiple screens that have different scale factors) - -This isn't figured out yet, for now we're doing the first approach in select places in the window server. - -### Resource loading API - -Currently: - - auto app_icon = GUI::Icon::default_icon("app-gml-playground"); - -or - - s_unfilled_circle_bitmap = Bitmap::load_from_file("/res/icons/serenity/unfilled-radio-circle.png"); - -or - - header.set_font(Gfx::Font::load_from_file("/res/fonts/PebbletonBold14.font")); - -Going forward: - - FIXME (depends on loading strategy decision a bit?) - -Implementation plan -------------------- - -The plan is to have all applications use highdpi backbuffers eventually. It'll take some time to get there though, so here's a plan for getting there incrementally. - -0. Add some scaling support to Painter. Make it do 2x nearest neighbor scaling of everything at paint time for now. -1. Add scale factor concept to WindowServer. WindowServer has a scaled framebuffer/backbuffer. All other bitmaps (both other bitmaps in WindowServer, as well as everything WindowServer-client-side) are always stored at 1x and scaled up when they're painted to the framebuffer. Things will look fine at 2x, but pixely (but window gradients will be smooth already). -2. Let DisplaySettings toggle it WindowServer scale. Now it's possible to switch to and from HighDPI dynamically, using UI. -3. Come up with a system to have scale-dependent bitmap and font resources. Use that to use a high-res cursor bitmaps and high-res menu bar text painting in window server. Menu text and cursor will look less pixely. (And window frames too, I suppose.) -4. Let apps opt in to high-res window framebuffers, and convert all apps one-by-one -5. Remove high-res window framebuffer opt-in since all apps have it now. - -We're currently in the middle of point 3. Some window server icons are high-resolution, but fonts aren't yet, and in-window-server things with their own backing store (eg menus) aren't yet either. diff --git a/Documentation/Kernel/AHCILocking.md b/Documentation/Kernel/AHCILocking.md deleted file mode 100644 index 53f9d68f2fc..00000000000 --- a/Documentation/Kernel/AHCILocking.md +++ /dev/null @@ -1,55 +0,0 @@ -# AHCI Locking - -## Introduction to hard locks, soft locks and what they do - -### Soft lock - `Lock` - -A soft lock is basically a regular lock in the kernel. We use it -with a `Locker` class, to create a scoped locking of that lock: - -```c++ -Locker locker(m_lock); - -... -... - -return true; -``` - -This lock doesn't disable interrupts at all, and if it is already in use, the scheduler will simply yield away from that section until it tries to lock it again. - -### Hard lock - `Spinlock` - -A hard lock is essentially a lock that is used in critical sections in the kernel. We use it with a `ScopedSpinLock` class, to create a scoped locking of that lock: - -```c++ -ScopedSpinLock lock(m_lock); - -... -... - -return true; -``` - -### Why do we need soft and hard locking in the AHCI code? - -First of all, the proper way of taking a `SpinLock` and `Lock` is to: -```c++ -Locker locker(m_soft_lock); -ScopedSpinLock lock(m_spinlock); - -... -... - -return true; -``` - -This sequence is relevant for any pattern of taking a soft and hard lock together in the kernel. -The reason for this order is that `SpinLock` will disable interrupts, while `Lock` will still allow the system to yield execution -to another thread if we can't lock the soft lock, because interrupts are not disabled. Taking a `SpinLock` and then a `Lock` is considered a bug, because we already disabled interrupts so yielding from this section is not possible anymore. - -We need both types of locking to implement hardware access safely. -When we use the `SpinLock` object, we ensure that only one CPU can run the scoped code section without any interruptions at all. This is important, because interrupts can be fatal in essentially what is a critical section. - -We use the `Lock` object for basically anything else, most of the time together with `SpinLock` as described earlier. This object becomes important when we schedule IO work to happen in the IO `WorkQueue`. -When we run in `WorkQueue`, it is guaranteed that we will have interrupts enabled - therefore we will not use the `SpinLock` to allow the kernel to handle page fault interrupts, but we still want to ensure no other concurrent operation can happen, so we still hold the `Lock`. diff --git a/Documentation/Kernel/DevelopmentGuidelines.md b/Documentation/Kernel/DevelopmentGuidelines.md deleted file mode 100644 index e9ca2e492b6..00000000000 --- a/Documentation/Kernel/DevelopmentGuidelines.md +++ /dev/null @@ -1,174 +0,0 @@ -# Kernel Development Patterns & Guidelines - -This document intends to guide the immediate and newcomer kernel developer when creating, -modifying and removing Kernel code. -Please read all of this document if you intend to send pull requests, as well as the [general contributing guidelines](../../CONTRIBUTING.md) -and [patterns](../Patterns.md) for the entire codebase. - -This document was composed as a result of ideas, experience and a general vision of what -the Kernel could become in the prosperous future of the project. - -## Out of memory handling - -Maybe one of the most important issues we have to solve in kernel code is when OOM (Out of memory) -condition occurs - simply put, a new allocation request has failed due to various reasons - -the allocation request was too much "greedy" and couldn't be satisfied, or simply we can't allocate more physical RAM pages -to whoever that requested them. - -**The proper solution to this is to always use the `TRY()` semantics together with -appropriate `adopt_*` function (either for `OwnPtr` or `RefPtr`).** - -```cpp -#include -#include - -... - -auto new_object = TRY(adopt_nonnull_own_or_enomem(new (nothrow) Object(...))); -``` - -In case of failure, the above code will simply propagate `ENOMEM` code to the caller, up to the syscall entry code, -so the userland program could know about the situation and act accordingly. - -An exception to this is when there's simply no way to propagate the error code to the userland program. -Maybe it's a `ATAPort` (in the IDE ATA code) that asynchronously tries to handle reading data from the harddrive, -but because of the async operation, we can't send the `errno` code back to userland, so what we do is -to ensure that internal functions still use the `ErrorOr<>` return type, and in main calling function, we use -other meaningful infrastructure utilities in the Kernel to indicate that the operation failed. - -## KStrings vs FixedStringBuffers - -As you might understand, we put a respectable amount of effort into making the kernel code OOM-safe. -One approach to achieve this is to allow error propagation where possible. -The other approach is to eliminate heap allocations altogether where possible. - -To do so, the `FixedStringBuffer` class was introduced into the AK library, and is used -extensively in kernel syscall handlers' code. -The idea is very simple - if we know the maximum length of an inspected string during -a syscall and it's relatively short (so it doesn't exceed the stack size), something like -1024 bytes is the total max length (but in theory we could just make the stack size bigger), -it could be copied from userspace to that stack storage instead of doing an heap allocation -to create a KString. This is especially useful when inspecting a string only during the -syscall handler scope, because doing an heap allocation is wasteful on memory resources -and puts a strain on the kernel memory manager for no good reason. - -The `Process` and `Thread` classes use a `FixedStringBuffer` to store their names, -to completely circumvent OOM conditions due to needing to allocate heap storage -for their names in the past. - -The `FixedStringBuffer` puts some safety guards - like zeroing the memory when storing new -StringView, as well as truncating it if its length exceeds the allocated stack storage size. - -There are many helpers (in the `Process` class and also in the `Kernel/Library/StdLib.h` file) that will do a check on whether the input size is exceeding the allocated `FixedStringBuffer` storage size. -An appropriate error will be released instead of just truncating the string and continue execution in these helpers. - -## We don't break userspace - the SerenityOS version - -We don't break userspace. However, in contrast to the Linux vision on this statement, -we don't care about ABI/API breakage between the userland and the kernel. **What we do care -about is a possible incident when a Kernel change does introduce a misbehave in userland, and Userland was not -appropriately considered to ensure this does not happen.** - -Many internal changes in the Kernel don't affect userland - for example, a new shiny driver -for super-fast storage devices, is not something that will likely break userland, because the -proper abstractions have already put in place, so the userland simply does not care about -the specifics about each `StorageDevice` in the kernel, as long as it properly implements the -known interfaces. - -However, some kernel changes, mainly ABI/API changes between userland and the kernel, in the -syscall handling layer, will break userland unless it's properly handled beforehand. -The proper solution in git terms is to ensure that both "offending" kernel changes and the appropriate -userland changes to accommodate the kernel changes are in the same commit, so we still keep the rule that -each git commit is bisectable by itself. - -**It's expected that changes to the Kernel will be tested with userland utilities to ensure the changes -are not creating any misbehaves in the userland functionality.** - -Even more stricter than what has been said above - we don't remove functionality unless it's absolutely -clear that nobody uses that functionality. Even when it's absolutely clear that nobody uses some kind -of kernel functionality, it could still be useful to think about how to make it more available and usable -to the SerenityOS project community. -Again, such removal should happen according to what has been mentioned in terms of git handling. - -## Each kernel feature should be backed by a userland usecase - -In contrast to the previous guideline, this guideline is clearly about the healthy growth of Kernel - -we don't bloat the Kernel for things we don't need. For example, in the early days -of the project, there was a floppy driver in the Kernel and it got removed because -nobody used it. Similarly, when an Intel AC97 soundcard driver was introduced, the SB16 -soundcard driver was removed shortly afterwards. **We simply don't have interest in supporting -hardware that nobody will use, or a kernel feature that doesn't make sense to most people.** - -## Proper locking - -The [AHCI locking document](AHCILocking.md) describes our locking patterns thoroughly. -Still, it's very important to understand we do care about SMP (Symmetric Multiprocessing), -so proper locking is one of the top priorities in the kernel development mindset. - -**The general rule is that we should not acquire a `Mutex` after taking a `Spinlock`. -Taking a `Spinlock` after another is generally considered fine, as long as they are always -taken in the same order, to prevent deadlocks.** - -To ensure we do this properly, the `MutexProtected<>` and `SpinlockProtected<>` C++ containers -have been introduced in the kernel to ensure that locking is done on particular shared data objects, -so it's preferable to use these containers instead of a "random" spinlock as class member. - -## Proper, clean and meaningful syscall userland interfaces - -As at the time of writing this document, the syscall table is generally quite stable. -This happens to be that way because the syscalls are well-defined, backed by good-known POSIX interfaces. -**Suggestions/patches to add syscalls should be examined strictly, because generally-speaking it's the "last resort" -we should choose from other Unix interfaces that are available to us.** - -Because there's no definitive "yes" or "no" for all cases, expect that a discussion will be taking -place in your pull request, in case that you do introduce a new syscall in the Kernel. - -For example, say that one wants to add a new driver for the Storage subsystem, then -we already have the proper abstractions in place, so the new specific `StorageDevice` will be registered -as like any other `StorageDevice`, therefore it will be exposed in the `/dev` directory and regular -`write`, `open`, `read`, `ioctl` syscalls will be usable immediately. -Therefore, there's no need for a special syscall to handle the new hardware, because -the already-existing syscalls are sufficient. - -**We should also refrain from architecture-specific syscalls as much as possible. Linux had them -in the past and many of them were removed eventually.** - -## Security measures - -We, as the SerenityOS project, take seriously the concept of security information. -Many security mitigations have been implemented in the Kernel, and are documented in a -[different file](../../Base/usr/share/man/man7/Mitigations.md). -As kernel developers, we should be even more stricter on the security measures being -taken than the rest of system. -One of the core guidelines in that aspect **is to never undermine any security measure -that was implemented, at the very least.** - -It's also very nice and generous if one decides to improve on a security measure, -as long as it doesn't hurt other security measures. - -We also consider performance metrics, so a tradeoff between two mostly-contradictive metrics -is to be discussed when an issue arises. - -## No hardcoded userspace paths - -To ensure the kernel stays flexible to future changes, we should not put hardcoded -paths or assume where filesystem items (nor where filesystems are mounted) reside on - we should -always let userspace to inform the kernel about paths and assume nothing else. -Even when it's obvious some file will always be located in a certain path, it is considered -a violation of an abstraction layer to hardcode it in the kernel code, because we put an hard effort -to keep the abstractions we have intact and clean. - -There's one exception to this rule - the kernel will use a `dbgln` statement to -warn the user in case that the dynamic loader is not the usual binary we use. -To generalize the exception a bit more - debug messages (being used sparingly) with -assumption of paths could be OK, as long as they never have any functional implication -on the user. - -## Documentation - -As with any documentation, it's always good to see more of it, either with a new manual page, -or a kernel concept being described in the `Documentation/Kernel` repository directory so other -developers can understand it. -There's no well-defined template to use when writing a documentation, but it is expected -at the very least to have an opening paragraph about the topic so others can understand -what the document is about. diff --git a/Documentation/Kernel/GraphicsSubsystem.md b/Documentation/Kernel/GraphicsSubsystem.md deleted file mode 100644 index 60b5eec038d..00000000000 --- a/Documentation/Kernel/GraphicsSubsystem.md +++ /dev/null @@ -1,231 +0,0 @@ -# Introduction to the Kernel Graphics Subsystem - -## What is the Kernel Graphics Subsystem? - -The Kernel Graphics Subsystem is the kernel subsystem that is responsible to -manage all graphics devices, framebuffers, hardware 3D acceleration, memory mappings, etc. - -## Responsibilities - -* Provide a convenient interface to all supported video hardware in the Kernel. -* Manage 3D rendering on supported hardware. - -## Current Limitations and Future features? - -* No locking on who can do `mmap` on DisplayConnector devices currently, which can -lead to malicious applications "fighting" with WindowServer on what is shown to the user -from the framebuffer. - -# DisplayConnector Devices - -The Display Connector devices are an abstraction layer to what is essentially the -management layer of hardware display (commonly known as scanouts) output connectors. -The idea of using such type of device was inspired by Linux, which has a struct called -`drm_connector` as a base structure for other derived structures in the various Linux DRM drivers. - -A Display connector device is typically connected to a group of other connectors as well, -as it's generally common to have video hardware that utilizes multiple hardware connectors -to VGA, DisplayPort, HDMI, DVI, etc. However, it can be a stand-alone device too, which -is the case for the `GenericDisplayConnector` class, that can be initialized without being -attached to a parent PCI device object at all. - -Each display connector is programmatically accessible via a device file, in the -`/dev/gpu/` directory with a name `connectorX` (X is replaced with the minor number). - -Each display connector could be `mmap`-ed to gain control to video RAM directly. -This works nicely with the kernel TTY subsystem thanks to the role of virtual memory -in the subsystem. - -# Hardware framebuffers - -## History lesson on ISA, PCI, VGA (and SVGA) - -Since the beginning of video hardware with old-school ISA VGA display adapters, -there was a window being mapped in the physical address space, being translated -by the motherboard chipset as read/write to video RAM. When SuperVGA came along -in the 90s, it expanded the usage of that small VGA window (where it was in very low memory) -to high resolution framebuffers in very high memory regions. This tradition continues today -to some extent (excluding hardware which requires DMA from main memory to video memory), -because it's relatively cheap and easy way to let operating systems to access video RAM -directly without too much trouble. - -Since the main PC x86 computer bus was the IBM ISA bus, there was no easy way to tell where -the resources of each card were actually located at the IO space nor in the physical memory space. -There were a couple of attempts to fix this - the most notable was the Plug-and-Play standard. - -The real change came from a new computer bus in the mid 90s - the PCI bus. This new bus -was PnP friendly - no more hardcoded resource allocations which means also that OS drivers -can find where the firmware (BIOS) mapped the BAR (Base address registers) for the actual resources. -This was also the era where SuperVGA video adapters started to appear, taking advantage of this -new bus. - -Since VGA was introduced, countless amount of vendors brought their own implementations -and video adapters for usage in the PC market. By now, most of them are gone, leaving the major -vendors (Intel, AMD and Nvidia) to still be able to manufacture video adapters which are today -commonly known as Graphics Processing Unit (abbreviated as GPU) - due to the fact that today -video adapters are not only outputting pixels to the computer screen, but have a whole set of processors -to take care of heavy computational tasks of graphics assets, and even general processing tasks nowadays. - -SuperVGA was only the first step into this direction, yet SuperVGA is not a standard, but -a marketing name for is essentially each video adapters' vendor tried to do in the 90s - -building an extension upon VGA. All of these vendors did that without creating a unified standard, -like with VGA, which ensured everyone are conforming to well-known and expected video hardware behavior. -To try to cope with the dire situation, the VBE (Video BIOS extensions) standard was created to -help BIOS and operating system vendors to be able to get high resolution framebuffer from -any hardware that complied to the standard. When UEFI came along, the vendors agreed -to create the Graphics output protocol (known as UEFI GOP), to provide the same set of features -that VBE had, but now is usable from 64-bit kernel code as long as the kernel didn't shutdown -the UEFI services (which it really should do) after completing the boot process. - -## Then how does it all apply to the subsystem? - -Glad you asked! Since hardware framebuffers are still relevant today, we use them -to put pixels so the video encoder of a GPU can convert these bits into light, so -you could actually see a picture from a computer screen. Each GPU implements its own -internal functionality so it might vary from very simple devices (like the QEMU bochs-display -device, which is nothing more than framebuffer region and a couple of registers to manage it) -to very complex devices, such as bare metal devices (like Intel integrated GPUs, etc). - -The Kernel graphics subsystem strives to manage all of these devices in a unified fashion -as much as possible. Of course, actual implementations should vary internally in the -amount of code to handle the actual device, but all basic API being exposed to userspace is the same. - -## The role of MMUs and virtual memory - -One of the primary goals of the subsystem to is to allow userspace applications, -like the WindowServer, to utilize the hardware framebuffers, so we can see the SerenityOS -desktop, and to ensure the internal TTY subsystem in the Kernel can use the same framebuffers -to put output from kernel virtual consoles when desired to (i.e. the user switched to the -Virtual console from another console that is in graphics mode). - -The SerenityOS kernel utilizes the MMU and virtual memory in a very neat trick to -give the "feel of control" to whoever did the `mmap` syscall on a DisplayConnector -device, while keeping the control to the Kernel to decide who accesses the actual VRAM -at a given time. This works by working with the following assumptions: - -1. Current usage of `mmap` is only for direct framebuffer manipulation. This means -that if we add support for batch buffers or other objects that should reside in VRAM, this trick -can lead to catastrophic incidents with the underlying hardware. This happens to be this way, due to -the fact that we essentially can take VRAM access from the WindowServer at anytime we want, -without the WindowServer being aware of this so it can still function in the background. -2. We need to know the maximum dimensions of the framebuffers when initializing the device -and creating the DisplayConnector device at runtime. This happens because we map all the possible -pages of VRAM framebuffer at that time, and also reserve the same amount of pages in usable -physical memory space, so we could reserve the contents of VRAM between the switch -from graphics mode to console mode and vice-versa. - -The actual implementation is quite simple, yet powerful enough to let everyone -live comfortably - each DisplayConnector device is backed by a special VMObject (VMObject is -the base class for managing virtual memory scenarios easily) that is created when the -DisplayConnector device is initialized - we need to find the physical address of the -start of the framebuffer and the maximum resource size (this is where PCI BARs play their role, -as we can determine with them the physical address by reading their values and also -the maximum resource size, by doing a very simple write 1s-and-read trick that was introduced -with the PCI bus when it was created). Then when the object is created, the code ensures -we reserve for later usage the same amount of pages somewhere else to ensure we preserve -the contents of VRAM between the switch from console and graphics mode and vice-versa. -The special VMObject is tied to each `Memory::Region` object, so it can instruct each -virtual-to-physical memory mapping to be actually re-mapped to wherever we want in physical -address space, therefore, we do not interrupt any userspace application from drawing its pixels -to the framebuffer in the background. - -## Do you plan supporting old VGA adapters? - -Given the nature of the user experience SerenityOS strives to deliver to the users, -a core requirement from the first day of this project was to only support 32 bit-per-pixel -(also known as True-color framebuffer) hardware framebuffers. We do support hardware -framebuffers that neglect the alpha-channel (essentially it's a 24 bit-per-pixel), -as long as each pixel is aligned to 4 bytes. The QEMU std-vga (bochs-display with -VGA capabilities) device was chosen as the first device to be supported in the project, -and that was an excellent choice for that time to put up with the said requirement. - -This hard requirement is due to the fact that supporting anything besides True-color -framebuffers is a *waste of time* for a new modern kernel. Not only that, but relying -on VGA with modern monitors is essentially settling for blurry, badly-shaped graphics -on a computer monitor, due to unoptimized resolution scaling with modern screen ratios. - -Old VGA adapters are certainly not capable of using high resolution framebuffers -when operating in pure native VGA mode (i.e. not operating in an extension mode -of the video adapter), therefore, if the Kernel cannot find a suitable framebuffer -to work with or a video adapter it has a driver for, then the last resort is to use the old VGA text mode -console. Therefore, the SerenityOS kernel will probably never support pure VGA functionality. -That technology was good for operating systems in the 90s, but is not usable anymore. - -By doing so, we ensure that legacy cruft is not introduced in the Kernel space. This indeed -helps keeping the Graphics subsystem lean and flexible to future changes. - -## What about the Video BIOS Extensions? It can gives high resolution framebuffers without writing native drivers! - -As for using Video BIOS extensions - this requires us to be able to call to BIOS 16-bit real mode -code. The solutions for these are: -1. Drop to real mode, invoke the BIOS interrupt and return to our kernel. -2. Writing a Real-Mode 16-bit emulator, either in Kernel space or userspace. -3. Use Intel VT-x extensions to simulate a processor running in Real mode. -4. Use the old v8086 mode in x86 processors to get an hardware monitor of 16-bit tasks. - -Neither of these options is suitable for us. Dropping to real mode is quite dangerous task, and breaks -the concept of memory protection entirely. Writing a real mode emulator is the safest solution, yet can -take a not negligible amount of effort to get something usable and correct. Using the hardware options -such as Intel VT-x or the v8086 mode are almost equally equivalent to writing an emulator. - -We will probably never support using the Video BIOS extensions because of these reasons: -1. Major part of this project is to maximize usability and fun on what we do, and turning into legacy-cruft to -temporarily solve a solution is not the right thing to do. -2. VBE is not usable on machines that lack support of BIOS. As of 2022, this increasingly becomes a problem -because many PC vendors dropped support for BIOS (known as CSM [Compatibility Support Module] in UEFI terms). -3. VBE is limited to whatever the vendor decided to hardcode in the OptionROM of the video adapter, which means -it can limit us to a small set of resolutions and bits-per-pixel settings, -some of these settings are not convenient for us, nor suitable for our needs. -4. VBE lacks the support of detecting if the screen actually supports the resolution settings, -which means that the operating system has to use other methods to determine if screen output is -working properly (e.g. waiting for a couple of seconds for user confirmation on the selected settings). -This is because VBE lacks support of getting the screen EDID because most of the time, -the EDID resides in a ROM in the computer screen, which is inaccessible without using specific -methods to extract it (via the Display Data Channel), which are not encoded or implemented in -the PCI OptionROM of the device. -This is in contrast to native drivers which are able to do this, and VGA, that never relied on -such methods and instead relied on all video adapters and computer screen to use an well-known -specification-defined display modes. - -## What are the native drivers that are included in the kernel? what type of configurations are supported? - -The kernel can be configured to operate in the following conditions: -1. Fully-enable the graphics subsystem, initialize every device being supported. -2. Only use the pre-initialized framebuffer from the bootloader, don't initialize anything else. -3. Don't use any framebuffer, don't initialize any device. - -By default, we try to fully-initialize the graphics subsystem, which means we iterate -over all PCI devices, searching for VGA compatible devices or Display Controller devices. - -We currently natively support QEMU std-vga (and bochs-display) device, VirtIO GPU, VMWare SVGA II adapter, -and Intel Graphics (Gen 4 only). We try our best to avoid using a pre-initialized framebuffer, so -if we detect any of the said devices, we simply ignore the pre-initialized framebuffer from the bootloader. - -The user can choose to use a different condition of the Graphics subsystem, but hardware limitations -such as lack of supported hardware can either lead the Kernel to use a pre-initialized framebuffer -or completely "abandon" graphics usage (as was mentioned in third condition), making the system usable -only through a VGA 80x25 text mode console. - -# Userspace APIs - -## Unified Graphics IOCTLs - -All graphics ioctls are currently unified and being implemented in one final method -of the `DisplayDevice` class, to keep implementation consistent as much as possible. - -## Syscalls - -The `read` and `write` syscalls are not supported and probably will never be. In the transition -period from the old framebuffer code in the Kernel to the current design, the `mmap` syscall was -quite dangerous and did not handle multiple userspace programs trying to use it on one device. -Since that was resolved and `mmap` can be used safely, `read` and `write` syscalls are no longer -needed and are considered obsolete for this device because no userspace program in Serenity will -ever need to use them, or test them at the very least. - -The `ioctl` syscall is used to control the DisplayConnector device - to invoke -changing of the current mode-set of a framebuffer, flush the framebuffer, etc. - -## Major and minor numbering - -The major number is fixed at 226. Minor number is allocated incrementally as instances -are initialized. diff --git a/Documentation/Kernel/IOWindow.md b/Documentation/Kernel/IOWindow.md deleted file mode 100644 index 8be29b702cd..00000000000 --- a/Documentation/Kernel/IOWindow.md +++ /dev/null @@ -1,62 +0,0 @@ -# The IOWindow class - -## Introduction to port-mapped IO and memory-mapped IO - -### Port-mapped IO - -Port mapped IO is a x86-specific method to access hardware registers. It uses a -set of specific instructions in the x86 architecture to invoke Input and Output operations -on hardware that is present in the platform board. - -```c++ -IOAddress io(0x3f0) -u8 ide_status = io.offset(0).in() -``` - - -### Memory-mapped IO - -Memory mapped IO is a platform-agnostic method to access hardware registers. It uses a -set of memory access instructions in many computer architectures to invoke Input and Output operations -on hardware that is present in the platform board. - -```c++ -auto mapping = Memory::TypedMapping::map_typed_writable(0xb8000); -*mapping = 0x001b; -``` - -## The `IOWindow` class to rule them all (almost)! - -The entire idea behind the `IOWindow` class is to make it much more easier to compile -the Kernel for non-x86 builds, so the class abstracts platform-specific methods to access -hardware such as the port-mapped IO method. In compile-time, when generating a Kernel for -non-x86 target, the entire port-mapped IO code is omitted as it's not relevant for non-x86 -targets. - -In many cases, devices (such as PCI devices) can either use the IO space or memory space -to expose their registers for the CPU to utilize as the host software invokes IO operations -for various reasons. Some devices expose equivalent registers in both the IO space and memory space -to help legacy host software to interact with the hardware. One example to this is old AHCI controllers -which could be used in legacy mode - i.e. exposing SFF IDE registers in the IO space, or to enable memory -mapped registers as being defined in the SATA AHCI HBA specification. - -The general rule in kernel driver programming is to know that there are only two valid -cases on whether to use the `IOWindow` structure or not: -1. The device is known to either use the IO space, memory space or both, taking into -consideration that variants of the device can disable either of the options. In this case, -we need to use the `IOWindow` structure as it will help us to correctly use the IO window -in either case. -2. The device is known to use only the memory space, therefore we can ignore the `IOWindow` -structure and instead use the `Memory::TypedMapping` structure to help navigating in -the memory-mapped registers of the device. - -# A note about 64 bit access for memory mapped IO - -As far as we can tell, writing to 64 bit register can actually be done for the most part -with two 32 bit IO access operations. When genuine 64 bit access is needed, `IOWindow` is -probably not the appropriate solution anyway, because the device is only supporting memory-mapped IO -and there's no way it can provide register access via port mapped IO - that method simply doesn't -support generating 64 bit IO access, so you should use the `Memory::TypedMapping` mapping method instead. - -Therefore, to ensure we keep everything simple, there's simply no API to generate pure 64 bit IO access with -the `IOWindow` class. diff --git a/Documentation/Kernel/ProcFSIndexing.md b/Documentation/Kernel/ProcFSIndexing.md deleted file mode 100644 index 9723a70910b..00000000000 --- a/Documentation/Kernel/ProcFSIndexing.md +++ /dev/null @@ -1,71 +0,0 @@ -# ProcFS Indexing - -## Is a ProcFS index deterministic value? - -Short answer - yes. Long answer - because of the design pattern that was chosen, -each `InodeIndex` actually represent a known object, so it is guaranteed to be -the same always for global ProcFS objects. For process ID directories, once that -process has been killed, its primary segment value is no longer valid and hence -all sub-segments of it are not relevant anymore, but if the process is still alive, -it is guaranteed that accessing the same `InodeIndex` in regard to an object tied to -a process directory will provide the expected object. - -## The goal - zero allocations when creating new process - -The main goal is to have zero allocations happening in ProcFS when a new process is created. -The old ProcFS design followed that principle, but was quite hard to edit and to extend with new -functionality. -The current ProcFS design doesn't follow that principle, but is easier to edit and to extend. -A compromise is needed to ensure we get the advantages from both designs while minimizing the -effects of the disadvantages of each design. - -## The segmented index - -### The layout of the segmented index - -Since it was decided that heap allocations for ProcFS are *mostly* bad, the new -design layout tries to achieve most of the principle of "Don't allocate anything -until actually needed". For that to happen, `InodeIndex` (u64 value) is split -into 3 Segments: -- The primary segment: value 0 is reserved for all non-PID inodes in the procfs. -All values from 1 to 0xFFFFFFF are valid PID indices, which represents all PIDs from 0 to 0xFFFFFFE - -- The Sub-directory segment: value 0 is reserved for parent PID directory. All other values are -available for usage of sub-directories in the PID directory. - -- The property segment: value 0 is reserved for parent PID directory. All other values are -available for usage of components in the PID directory or in sub-directories of the PID directory. - -So, the final layout of the 64 bit index is: - -``` -| Primary Segment (28 bits) | Sub-directory (16 bits) | Component (20 bits) | -``` - -Example: To find a Thread 0 stack, for PID 1, the following encoding is applied: - -``` -hex(2 << 16 | 2 << (16 + 28)) == 0x200000020000 -``` - -### Two rules for indexing - -We don't want to allocate anything when a process is created, but we still want -to allocate global objects, so it's somewhat a compromise between two conflicting targets. -To do that we need to ensure that: - -1. If the primary segment value equals to 0, then the sub-directory and property segmentation -is not applied, but a sequential indexing is determined instead. This is needed so ProcFS can still -use global components that were pre-allocated beforehand. This means that there might be up to -68719476735 global components (including global sub-directories objects) in the ProcFS. -Otherwise, for every primary segment value > 0, then the sub-directory and property segmentation -is applied. This means that there might be up to 65534 sub-directories in a PID directory, and -up to 1048575 (1048574 for PID directory) properties (objects) in each sub-directory. - -2. If the primary segment value equals to 0, then value 0 in both artificial sub-directory -and property segments represents the root ProcFS folder. -Otherwise, for every primary segment value > 0, value 0 in both sub-directory and -property segments are reserved to represent the root PID directory. -Please note that if the sub-directory segment > 0, and property segment = 0 is a valid -index, and represents a valid property object in that sub-directory. - diff --git a/Documentation/Kernel/RAMFS.md b/Documentation/Kernel/RAMFS.md deleted file mode 100644 index fe96b157cbb..00000000000 --- a/Documentation/Kernel/RAMFS.md +++ /dev/null @@ -1,70 +0,0 @@ -# `RAMFS` filesystem and its purposes - -`RAMFS` is a RAM-backed filesystem. It is used to hold files and directories in the `/tmp` directory and -device nodes in the `/dev` directory. - -## What are the `RAMFS` filesystem characteristics? - -`RAMFS` is a pure RAM-backed filesystem, which means all files and directories -actually live in memory, each in its own `RAMFS` instance in the kernel. - -The `RAMFS` in its current design is very conservative about allocating virtual memory ranges -for itself, and instead it uses the `AnonymousVMObject` object to hold physical pages containing -data for its inodes. When doing actual IO, the `RAMFS` code temporarily allocates a small virtual memory -`Memory::Region` to perform the task, which works quite well although it puts a strain on the virtual memory -mapping code. The current design also ensures that fabricated huge files can be easily created in the filesystem -with very small overhead until actual IO is performed. - -### The `/tmp` directory and its purposes - -Currently, the `/tmp` directory is the **place** for facilitating the inter-process -communication layer, with many Unix sockets nodes being present in the directory. - -Many test suites in the project leverage `/tmp` for placing their test files -when trying to check the correctness of many system-related functionality. -Other programs rely on `/tmp` for placing their temporary files to properly function. - -### Why does the `RAMFS` work well for the `/dev` directory? - -To understand why `RAMFS` works reliably when mounted on `/dev`, we must understand -first what we did in the past and how `RAMFS` solves many of the issues with the previous design. - -At first, we didn't have any special filesystem mounted in `/dev` as the image build -script generated all the required device nodes in `/dev`. This was quite sufficient in -the early days of the project, where hardware support was extremely limited and of course -hotplugging any kind of hardware was not even a consideration. - -As the project grew larger and more hardware support was introduced, it became obvious -that this "solution" was not future-proof. For example, if one user has two SATA drives -connected to his computer, and another user has just one old IDE drive being used, -then how should we support both cases? The answer was that each user could simply invoke -the `mknod` utility to create device nodes. This solution meant that user interaction as well -as a deep understanding of kernel internals was required to achieve a proper setup. - -When it became apparent that another solution was needed, the `DevFS` filesystem was -invented. The idea was plain simple - the `DevFS` is a read-only filesystem that only -lists all present char and block devices. Permissions were hardcoded at known value, -and modifying the filesystem (including adding subdirectories) was strictly prohibited. -This solution was efficient in the sense of ensuring minimal user interaction for using -device nodes in `/dev`. The shortcomings were strictly immutable filesystem layout and hardcoded -permissions. Also, the filesystem implementation was specific to `/dev`, because no other -mount in the system used this special filesystem, which meant it needed special test cases, etc. - -The `DevFS` solution was short-lived, and was quickly replaced by the `DevTmpFS` solution. -That new shiny filesystem was again specific to `/dev`, but it solved many of the issues -`DevFS` suffered from - no more hardcoded permissions and now the design has flexible filesystem -layout in its mindset. -This was achieved by implementing from scratch a filesystem that resembles the `RAMFS` -filesystem, but was different in one major aspect - only device nodes and directories are allowed -to be in `/dev`. This strict requirement has been mandated to ensure the user doesn't -accidentally put unrelated files in `/dev`. When the `DevTmpFS` was invented, it clearly -needed userspace cooperation to create device nodes in `/dev`, so `SystemServer` was modified -to create those during boot. The process of how `SystemServer` does that is not discussed -in this document, but ultimately evolved to be flexible enough to work quite well. - -Everything worked quite well, but there was still a prominent problem with `DevTmpFS` - -it was an entire filesystem solution just for `/dev` and nobody else used it. -Testing the filesystem was quite clunky and truthfully lacking from the beginning until its removal. -To solve this problem, it was decided to stop using it, and instead just use `RAMFS`. -To ensure the current behavior of disallowing regular files in `/dev`, a new mount flag called -`MS_NOREGULAR` was invented, so it could be mounted with it. diff --git a/Documentation/QEMU_Components.png b/Documentation/QEMU_Components.png deleted file mode 100644 index 7ccf69e6205..00000000000 Binary files a/Documentation/QEMU_Components.png and /dev/null differ diff --git a/Documentation/RunningOnRaspberryPi.md b/Documentation/RunningOnRaspberryPi.md deleted file mode 100644 index 6d9243b9e0e..00000000000 --- a/Documentation/RunningOnRaspberryPi.md +++ /dev/null @@ -1,132 +0,0 @@ -# Running Serenity on Raspberry Pi - -## NOTE - -This is for development purposes only - Serenity doesn't currently boot on Raspberry Pi! Use this guide if you want to set up a development environment. - -Currently only UART output is supported, no display. - -64-bit only, so you need a Raspberry Pi 3 or newer. - -## Running in QEMU - -### Step 1: Set Up Serenity - -Please follow [build instructions](BuildInstructions.md) to download and build Serenity. Make sure everything builds successfully for x86. - -### Step 2: Build and run in emulator - -Use the following command to build and run the AArch64 version of the system: - -```console -Meta/serenity.sh run aarch64 -``` - -It should build Serenity and open a QEMU window, similar to the x86 version. You should see some messages in the terminal. - -You can also run it under gdb with: - -```console -Meta/serenity.sh gdb aarch64 -``` - -## Running on real hardware using an SD Card - -### Step 0: Download and run Raspberry Pi OS from an SD Card - -This step is needed because the original firmware files need to be present on the SD Card when booting Serenity. It will also help with the UART setup. - -### Step 1: Connect your Raspberry Pi to your PC using a UART cable - -Please follow one of the existing guides (for example [here](https://scribles.net/setting-up-serial-communication-between-raspberry-pi-and-pc)) and make sure UART is working on Raspberry Pi OS before proceeding. - -If you're using a Raspberry Pi 4B and want to test if the UART is working correctly, you need to do a few extra steps. -UART0 (the one that SerenityOS uses) is used for bluetooth on these models, so for the OS to use it instead, ensure that you disable Bluetooth inside the `config.txt`: - -``` -dtoverlay=disable-bt -``` - -### Step 2: Mount SD Card - -If you use a Raspberry Pi 4, and your serenity kernel is called `kernel8.img` -(the default), and you don't have any other `kernel*.img` files on your SD -card, make sure `config.txt` is empty. - -If you want to use filename that isn't `kernel8.img` or if you want to keep -other `kernel*.img` files on your SD card, put this in config.txt: - -``` -arm_64bit=1 -kernel=myfilename.img -``` - -If you use a Raspberry Pi 3, put this in config.txt: - -``` -enable_uart=1 -``` - -### Step 3: Copy Serenity kernel to SD Card - -`kernel8.img` can be found in `Build/aarch64/Kernel/`. Copy it to the main directory on the `Boot/` partition, next to `config.txt`. You can either replace the original file or use another name (see above). - -### Step 4: Put the SD Card in the Raspberry Pi and power on - -You should start seeing some messages in your UART terminal window. - -## Running on real hardware using network (Raspberry Pi 3) - -### Prerequisites - -There are multiple ways to set up your network. The easiest way is a direct connection between the Raspberry Pi and your PC. To achieve this your PC has to have an Ethernet port. - -Here's the [Raspberry Pi Documentation](https://www.raspberrypi.com/documentation/computers/raspberry-pi.html#debugging-network-boot-mode) on booting from the network. - -### Step 1: Make sure OTP mode is enabled on the board - -This is enabled by default on Raspberry Pi 3+. For the previous boards please see the section [Debugging Network Boot Mode](https://www.raspberrypi.com/documentation/computers/raspberry-pi.html#debugging-network-boot-mode) of the Raspberry Pi documentation. - -### Step 2: Copy all files from the original SD Card to your PC - -This directory will serve as a TFTP server, sending files to the Raspberry Pi when requested. - -### Step 2: Set up the network interface - -Switch the network interface to static mode (static IP) and disable the firewall. - -### Step 3: Set up network services - -Booting Raspberry Pi requires DHCP and TFTP servers. - -On Windows, you can use the [Tftpd32](https://bitbucket.org/phjounin/tftpd64/src/master/) program. - -Example configuration for DHCP: - -![](Tftpd32_Dhcp.png) - -Make sure you **disable** the `Ping address before assignment` option. - -Example configuration for TFTP: - -![](Tftpd32_Tftp.png) - -The only option worth noting is `Base Directory` which should contain the files from the SD Card. - -### Step 4: Power up the Raspberry Pi - -Remove the SD Card, connect an Ethernet cable between the Raspberry Pi and your PC and power on the board. - -After 5-10 seconds you should see files being served by the TFTP server: - -![](Tftpd32_Serving.png) - -The system should boot normally as it would from the SD Card. - -### Step 5: Modify config.txt and copy Serenity kernel - -Similarly to booting from SD Card (see above), modify `config.txt` and copy the Serenity kernel to the TFTP directory. - -### Step 6: Reset Raspberry Pi - -You should start seeing some Serenity messages in your UART terminal window. diff --git a/Documentation/SpiceIntegration.md b/Documentation/SpiceIntegration.md deleted file mode 100644 index a5c6a4997d2..00000000000 --- a/Documentation/SpiceIntegration.md +++ /dev/null @@ -1,38 +0,0 @@ -# Setting up SPICE integrations - -# Ubuntu - -1. Build QEMU via `Toolchain/BuildQemu.sh` -2. Install virt-viewer 8.0 - * On 23.04+ just do `sudo apt-get install virt-viewer` - * For earlier versions you need to build it from source (see below). -3. export SERENITY_SPICE=1 -4. Meta/serenity.sh run - -## Building + installing `virt-viewer` - -**Note:** If you installed an old version from `apt` uninstall that first (`sudo apt-get purge virt-viewer`). - - -Install the build dependencies: -``` -sudo apt-get install libvirt-glib-1.0 libvirt-dev spice-client-gtk-3.0 spice-client-glib-2.0 intltool -``` - -Fetch and extract the sources: -``` -wget https://releases.pagure.org/virt-viewer/virt-viewer-8.0.tar.gz -tar -xvf virt-viewer-8.0.tar.gz -``` - -Configure and build: -``` -cd ./virt-viewer-8.0 -./configure --with-spice-gtk -make -``` - -Install: -``` -sudo make install -``` diff --git a/Documentation/Tftpd32_Dhcp.png b/Documentation/Tftpd32_Dhcp.png deleted file mode 100644 index 6dcb4ddd8e5..00000000000 Binary files a/Documentation/Tftpd32_Dhcp.png and /dev/null differ diff --git a/Documentation/Tftpd32_Serving.png b/Documentation/Tftpd32_Serving.png deleted file mode 100644 index 7b09bda8fc9..00000000000 Binary files a/Documentation/Tftpd32_Serving.png and /dev/null differ diff --git a/Documentation/Tftpd32_Tftp.png b/Documentation/Tftpd32_Tftp.png deleted file mode 100644 index 440e5fde108..00000000000 Binary files a/Documentation/Tftpd32_Tftp.png and /dev/null differ diff --git a/Documentation/TransferringFiles.md b/Documentation/TransferringFiles.md deleted file mode 100644 index f3f1d6a750e..00000000000 --- a/Documentation/TransferringFiles.md +++ /dev/null @@ -1,91 +0,0 @@ -# Transferring files from QEMU to your host machine - -## Method 1: WebServer -Serenity has a built-in web server which extends to your host machine. - -Open a new terminal and use the following command to start a WebServer instance for the current working directory: - -```console -ws . -``` - -Then we just open `localhost:8000` on our host machine :^) - -![](WebServer_localhost.jpg) - -**NOTE:** Due to the fact that some browsers download unrecognized files as plain text, you may want to use something like `wget` to download the file **as is** instead. Otherwise the file may appear corrupted when the system tries to load it. - -## Method 2: Mount the disk image - -Another way is to mount Serenity's `_disk_image` to your host machine by using the following command on *nix systems (or inside WSL): - -```console -cd "Build/${SERENITY_ARCH}" -mkdir mnt -sudo mount -t ext2 _disk_image mnt -``` - -## Method 3: Archiving tool with ext2 support - -Some archiving tools, like [7-Zip](https://www.7-zip.org/), are capable of directly opening ext2 images like Serenity's `_disk_image`. With these, you can open the disk image like any other archive and extract the files you need. - -For WSL users: If you have the image on your native WSL drive (recommended), this drive can be opened in Explorer by manually opening `\\wsl$` (not visible in the Network tab!) and then the "network share" corresponding to your distro. - -## Method 4: Enable OpenSSH on host and use sftp client on SerenityOS - -- Setup OpenSSH server on your host. -For windows: Google is your friend (https://docs.microsoft.com/en-us/windows-server/administration/openssh/openssh_install_firstuse) -For linux: Google is your friend. -- Ensure that you already have a working SerenityOS working build. -```console -$ Meta/serenity.sh rebuild-world -``` -- To enable OpenSSH package from SerenityOS: (initial directory is SerenityOS ROOT_DIR) - -```console -cd Ports -cd openssh -./package.sh -cd ../.. -Meta/serenity.sh run -``` -- From within SerenityOS, check that you have a working sftp app: - -```console -courage:~ $ sftp -``` -The expected response will be: - -```console -courage:~ $ sftp -usage: sftp [-46aCfNpqrv] [-B buffer_size] [-b batchfile] [-c cipher] - [-D sftp_server_path] [-F ssh_config] [-i identity_file] - [-J destination] [-l limit] [-o ssh_option] [-P port] - [-R num_requests] [-S program] [-s subsystem | sftp_server] - destination -``` -- Assume that you have a working OpenSSH server as mentioned earlier, with an IP address of 192.168.0.11. -- Assume that you have a valid user on that host with account name user1. -- Assume that you are currently inside the folder from which you want to transfer the file(s) from. -- Local - User: anon -- Remote - User: user1 - IP Address: 192.168.0.11 -- Connect to remote server via sftp - -```console -courage:~ $ sftp user1@192.168.0.11 -The authenticity of host '(192.168.0.11)' can't be established. -ECDSA key finger print is SHA256:Iav!m/E0cHJBnzSk0hJEZnBZ2F3LBL4wxhyv4nrfPtU. -Are you sure you want to continue connecting (yes/no/[fingerprint])? yes - -user1@'s password: -Connected to 192.168.0.11 - -``` -- By this time, you have successfully connected and logged on to the remote host. -- You can get more information by typing ` help `. -- The most often used (simplified) sftp commands are ` ls `, ` cd `, ` put [filename] `, ` get [filename] `, and ` quit `. I said simplified since the actual commands have many more options. -- Be aware that there will be a time you would think that nothing is happening since the cursor just stares back at you. It is always waiting for your next instruction. Typing ` quit ` or ` bye ` will close the program. -- Congratulations. Pat yourself at the back. diff --git a/Documentation/VMware.md b/Documentation/VMware.md deleted file mode 100644 index ad84fd7a080..00000000000 --- a/Documentation/VMware.md +++ /dev/null @@ -1,35 +0,0 @@ -# Serenity installation guide for VMware - -## NOTICE -There are currently issues with running Serenity in VMware. Please refer to the [open issue](https://github.com/SerenityOS/serenity/issues/5716) for a list of currently known issues. Anything that doesn't currently work will be noted in this document. - -## Creating the disk image -Before creating a disk image that will work in VMware, you will need to create a GRUB image as described in the [Serenity installation guide](BareMetalInstallation.md). Please skip the final step of that section, as that is only relevant for putting the image onto a real drive. You **cannot** use the same disk image created for QEMU. Using that image will halt immediately with the message ``FATAL: No bootable medium found! System halted.`` - -The easiest way to convert the disk image is with QEMU: - -`` -qemu-img convert -O vmdk /path/to/grub_disk_image /path/to/output/serenityos.vmdk -`` - -## Creating the virtual machine -Creating a SerenityOS virtual machine is similar to any other virtual machine. The main difference is using the already created VMDK disk image. - -**Please note that these instructions were written with VMware Player 15 in mind. Therefore, these instructions may not match exactly for past and future versions or VMware Workstation.** - -1. Open the **Create a New Virtual Machine** dialog. Select **I will install the operating system later**. -2. Choose **Other** as the guest operating system. -3. Feel free to give it any name and store it anywhere. -4. Choose any size for the hard disk. This disk will later be removed and replaced with the converted GRUB image from the previous stage. -5. Select **Finish** to finalize creation of the virtual machine. -6. Select the newly created virtual machine and click **Edit virtual machine settings**. -7. Serenity requires at minimum 512 MiB of memory. Set **Memory for this virtual machine** equal to or above 512 MiB. The currently recommended size is 1 GiB. -8. Select the existing **Hard Disk** and click **Remove**. -9. Select **Add**, select **Hard Disk**, select **IDE (Recommended)**, select **Use an existing virtual disk**. -10. Click **Browse** and browse to where you stored the converted VMDK disk image from the previous stage and add it. Click **Finish**. -11. Finally click **Save**. You can now **Power On** the virtual machine. - -Please note that at the time of writing, audio and networking do not work in VMware. - -That is all you need to boot Serenity in VMware! - diff --git a/Documentation/VirtualBox.md b/Documentation/VirtualBox.md deleted file mode 100644 index a31402805e8..00000000000 --- a/Documentation/VirtualBox.md +++ /dev/null @@ -1,65 +0,0 @@ -# Serenity installation guide for VirtualBox - -## NOTICE -There are currently issues with running Serenity in VirtualBox. Please refer to the [open issue](https://github.com/SerenityOS/serenity/issues/2927) for a list of currently known issues. Anything that doesn't currently work will be noted in this document. - -## Creating the disk image -Before creating a disk image that will work in VirtualBox, you will need to create a GRUB image as described in the [Serenity installation guide](BareMetalInstallation.md). Please skip the final step of that section, as that is only relevant for putting the image onto a real drive. You **cannot** use the same disk image created for QEMU. Using that image will halt immediately with the message ``FATAL: No bootable medium found! System halted.`` - -There are a couple of ways to convert the disk image: - -If you have QEMU installed: -`` -qemu-img convert -O vdi /path/to/grub_disk_image /path/to/output/serenityos.vdi -`` - -If you only have VirtualBox installed: -`` -VBoxManage convertfromraw --format VDI /path/to/grub_disk_image /path/to/output/serenityos.vdi -`` - -Set an identifier to the disk image, otherwise updating the disk image makes the identifiers no longer match up. -`` -VBoxManage internalcommands sethduuid serenityos.vdi 19850209-0000-0000-0000-000000000000 -`` - -Note that if you are on Windows and you do not have QEMU or VirtualBox in your PATH environment variable, you must be in the installation folder for the tool you're using. You will also need to put ``./`` in front of the command. - -## Creating the virtual machine -**Please note that these instructions were written with VirtualBox v6.1.12 in mind. Therefore, these instructions may not match exactly for past and future versions.** - -1. Open the **Create Virtual Machine** dialog. Switch to **Expert Mode**. -2. Feel free to give it any name and store it anywhere. -3. Switch the **Type** to **Other** and the **Version** to **Other/Unknown (64-bit)**. -4. Serenity requires at minimum 512 MiB of memory. Set **Memory size** equal to or above 512 MiB. The currently recommended size is 1 GiB. -5. For **Hard disk**, select **Use an existing virtual hard disk file**. Click the folder icon next to the dropdown to open the **Hard Disk Selector**. -6. Click **Add**. Browse to where you stored the converted disk image from the previous stage and add it. Click **Choose**. -7. Finally click **Create**. - -Reference image: - -![](VirtualBox_Creation_Reference.png) - -## Configuring the virtual machine to boot Serenity -Serenity will not be able to boot with the default configuration. There are a couple settings to adjust. Open **Settings** and: -1. Go to **System**, open the **Processor** tab and tick **Enable PAE/NX**. -2. Go to **Audio** and set **Audio Controller** to **SoundBlaster 16**. - -There are a couple of settings to check: -- In **Storage**, click on the **Controller**. Make sure the controller type is PIIX4. PIIX3 and ICH6 are untested. Anything else is guaranteed not to work, as Serenity does not currently support them. -- In **Network** and in the **Advanced** drop down, make sure the **Adapter Type** is anything but **Intel PRO/1000 MT Desktop (82540EM)**. While it is the only adapter type Serenity currently supports, it does not currently work in VirtualBox. - -Please note that at the time of writing, audio and networking do not work in VirtualBox. - -That is all you need to boot Serenity in VirtualBox! Read on for additional configuration you may want to use. - -## Blinking cursor after GRUB menu -If you only see a blinking cursor after selecting an option in the GRUB menu, it is very likely you have encountered one of the errors listed in the [troubleshooting document.](Troubleshooting.md) - -- Check that you have enabled PAE/NX in the **Settings** > **System** > **Processor** tab. -- If you are using a 64-bit disk image, check that **Version** is set to **Other/Unknown (64-bit)** instead of **Other/Unknown** in **Settings** > **General**. - -## Additional configuration (optional) -For serial debugging, go to **Serial Ports** and enable port 1. Feel free to set the **Port Mode** to anything if you know what you're doing. The recommended mode is **Raw File**. Set **Path/Address** to where you want to store the file. This must also include the file name. - -While the default 16 MB of video memory is more than enough to use the default resolution, it is not enough to use all the supported resolutions. If you want to use 2560x1080, you will need to supply at minimum 22 MB of video memory. diff --git a/Documentation/VirtualBox_Creation_Reference.png b/Documentation/VirtualBox_Creation_Reference.png deleted file mode 100644 index ce036bb7420..00000000000 Binary files a/Documentation/VirtualBox_Creation_Reference.png and /dev/null differ diff --git a/Documentation/WHPX_Feature.png b/Documentation/WHPX_Feature.png deleted file mode 100644 index 0449386be37..00000000000 Binary files a/Documentation/WHPX_Feature.png and /dev/null differ diff --git a/Documentation/WebServer_localhost.jpg b/Documentation/WebServer_localhost.jpg deleted file mode 100644 index a7541da00fe..00000000000 Binary files a/Documentation/WebServer_localhost.jpg and /dev/null differ diff --git a/Documentation/WritingManPages.md b/Documentation/WritingManPages.md deleted file mode 100644 index 6c169e2364d..00000000000 --- a/Documentation/WritingManPages.md +++ /dev/null @@ -1,91 +0,0 @@ -# Guidelines for writing SerenityOS manual pages (manpages) - -Additional useful information on authoring manpages can be found with the [Linux man-pages project](https://www.man7.org/linux/man-pages/man7/man-pages.7.html) and the [FreeBSD Manual Pages documentation](https://docs.freebsd.org/en/books/fdp-primer/manual-pages/). - -## What does and does not belong in manual pages - -The SerenityOS manpages are the primary documentation for SerenityOS itself. Different from the documentation pages, it is not primarily intended for SerenityOS developers. If the question of "Would this information be useful to view within the OS?" can be answered with "Yes", that probably means the information belongs in a manual page. - -## Format - -Primary information on the manpage system and structure lives in [man(7)](../Base/usr/share/man/man7/man.md). - -Manpages are written in CommonMark Markdown. It is allowed, but not ideal, to use features not supported by Serenity's markdown tooling. - -### Sections - -Consult the primary structure information to choose the correct main section for a new page. If a section contains many closely related pages, or contains many pages in general, it may be worth moving them to a new subsection. Each subsection must have its own page which at minimum explains the purpose of the subsection and lists all pages contained within it. - -### Links - -It is allowed to link online resources in manpages. - -For resources specific to the manpage (for example, application screenshots), it is recommended to include these resources in the manpage folder next to the manpage. Other resources should be part of the SerenityOS [base files](../Base) and referenced via an absolute path (for example `/res/graphics/map.png`). Both kinds of paths will be converted to work correctly on [man.serenityos.org](https://man.serenityos.org). - -Links to other man pages must be provided via the `help` pseudo-scheme: - -``` -help://man/section/page -``` - -`section` is a main section number and `page` is a page name, possibly including sub-sections. For example: `help://man/1/Applications/FontEditor` or `help://man/7/boot_parameters`. Using this format is enforced by the manpage linter. - -### Headings - -The highest heading level of manpages is 2 (`##`). - -## Style and language - -The general project guidelines on human language and text style apply to manpages as well. Titles must be written in sentence case instead of book title case. - -Examples for described concepts, especially when complex, are always appreciated to help users understand a new concept. - -### Name - -A manpage always begins with a `## Name` heading that contains a maximum of one sentence naming the page. For most pages, the format of this should be - -```md -Short title - full name or single-sentence descriptor -``` - -For example: `INI - generic config file format (.ini)` (full name for an abbreviation) or `w - Show information about currently logged-in users` (describes an application as briefly as possible). A period should only be included if a proper sentence is used instead of the dash format. - -If the page describes an application with an icon, the 16x16 icon must be linked with alt text "Icon" inline before the textual name. For example: `![Icon](/res/icons/16x16/certificate.png) Certificate Settings`. - -### Structure - -All manpages should contain at least the following three sections: - -```md -## Name -The manpage name, in the style described above. -## Description -Main text of the manpage. -## See also -List of related pages. -``` - -The `See also` section should contain related manpages, and may also link to relevant external pages. Even if pages are linked within the main prose, they should still be listed here if they are relevant to the entire page topic. Manpage links in `See also` should go both ways (page A links to page B, and B also links to A) to ease navigation within the manpage system. The `See also` section may be omitted if there's no related pages. - -#### Applications - -Applications in sections 1 and 8, especially command-line ones, must adhere to the following section structure. - -```md -## Name -program name - single-sentence descriptor of the program -## Synopsis -Usage synopsis for invoking the program, within a `sh` code block. -## Description -Prose description of the program. -## Options -A list of optional arguments (flags) the program takes, with a brief description of each. More complex description, especially when options interact, should be part of the Description section. May be omitted if the program has no options. -## Arguments -A list of (required) arguments the program takes, in the same format as the Options section. May be omitted if the program has no arguments. -## Examples -Program invocation examples with resulting output. -## See also -List of related pages. -``` - -Other descriptive sections may be included as a subsection of `Description`, or directly after this section. diff --git a/Kernel/API/DeviceEvent.h b/Kernel/API/DeviceEvent.h deleted file mode 100644 index e209f8d088c..00000000000 --- a/Kernel/API/DeviceEvent.h +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright (c) 2021, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include - -struct DeviceEvent { - int state; - int is_block_device; - unsigned major_number; - unsigned minor_number; - - enum State { - Removed = 0x01, - Inserted = 0x02, - Recovered = 0x03, - FatalError = 0x04, - }; -}; diff --git a/Kernel/API/DeviceFileTypes.h b/Kernel/API/DeviceFileTypes.h deleted file mode 100644 index 60d76f47f37..00000000000 --- a/Kernel/API/DeviceFileTypes.h +++ /dev/null @@ -1,12 +0,0 @@ -/* - * Copyright (c) 2021, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include - -AK_TYPEDEF_DISTINCT_ORDERED_ID(unsigned, MajorNumber); -AK_TYPEDEF_DISTINCT_ORDERED_ID(unsigned, MinorNumber); diff --git a/Kernel/API/FileSystem/FATStructures.h b/Kernel/API/FileSystem/FATStructures.h deleted file mode 100644 index 9121e0c8abe..00000000000 --- a/Kernel/API/FileSystem/FATStructures.h +++ /dev/null @@ -1,87 +0,0 @@ -/* - * Copyright (c) 2022, Undefine - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include - -namespace Kernel { - -// This structure represents the DOS 3.31 BIOS Partition Block. -// While DOS 3.31 predates FAT verions 12/16/32 (the versions supported by this driver), -// the fields in this block are common with the DOS 4 and DOS 7 BIOS Parameter blocks. -// This structure will be followed by an "Extended BIOS Partition Block" (EBPB). -// -// The DOS 4 EBPB is *typically* used by FAT 12/16 file systems, while the DOS 7 EBPB -// is *typically* used by FAT 32. _However_, any combination is possible, as the FAT -// version is only determined by the number of clusters. -// -// Note that the DOS 4 and DOS 7 EBPB extensions are incompatible with each other -// (contain fields in different orders and of different lenghts) and do not contain -// an explicit indication to differentiate them. -// This driver uses heuristics to identify the EBPB version (based on the signature bytes -// and sector counts). -// FIXME: Consider also using the MBR parition type field in the future. -struct [[gnu::packed]] DOS3BIOSParameterBlock { - u8 boot_jump[3]; - char oem_identifier[8]; - u16 bytes_per_sector; // Offset 0x0B -- beginning of DOS 3.31 BPB. - u8 sectors_per_cluster; - u16 reserved_sector_count; - u8 fat_count; - u16 root_directory_entry_count; - u16 sector_count_16bit; - u8 media_descriptor_type; - u16 sectors_per_fat_16bit; - u16 sectors_per_track; - u16 head_count; - u32 hidden_sector_count; - u32 sector_count_32bit; // 0x020 -- end of DOS 3.31 BPB. -}; -// 11 is the boot jump/OEM identifier prefix prior to the official BPB. -static_assert(AssertSize()); - -struct [[gnu::packed]] DOS4BIOSParameterBlock { - // Begins at sector offset 0x024. - u8 drive_number; // 0x024 - u8 flags; - u8 signature; - u32 volume_id; - char volume_label_string[11]; - char file_system_type[8]; -}; -static_assert(AssertSize()); - -struct [[gnu::packed]] DOS7BIOSParameterBlock { - // Begins at sector offset 0x024. - u32 sectors_per_fat_32bit; // 0x024 - u16 flags; - u16 fat_version; // Expected value 0x2b2a. - u32 root_directory_cluster; - u16 fs_info_sector; - u16 backup_boot_sector; - u8 unused3[12]; - u8 drive_number; - u8 unused4; - u8 signature; - u32 volume_id; - char volume_label_string[11]; - char file_system_type[8]; -}; -static_assert(AssertSize()); - -struct [[gnu::packed]] FAT32FSInfo { - u32 lead_signature; - u8 unused1[480]; - u32 struct_signature; - u32 last_known_free_cluster_count; - u32 next_free_cluster_hint; - u8 unused2[12]; - u32 trailing_signature; -}; -static_assert(AssertSize()); - -} diff --git a/Kernel/API/FileSystem/MountSpecificFlags.h b/Kernel/API/FileSystem/MountSpecificFlags.h deleted file mode 100644 index b08de48da67..00000000000 --- a/Kernel/API/FileSystem/MountSpecificFlags.h +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright (c) 2022-2023, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include - -#define MOUNT_SPECIFIC_FLAG_KEY_STRING_MAX_LENGTH 64 - -#define MOUNT_SPECIFIC_FLAG_NON_ASCII_STRING_TYPE_MAX_LENGTH 64 -#define MOUNT_SPECIFIC_FLAG_ASCII_STRING_TYPE_MAX_LENGTH 1024 - -struct MountSpecificFlag { - u32 key_string_length; - u32 value_length; - - enum class ValueType : u32 { - Boolean = 0, - UnsignedInteger, - SignedInteger, - ASCIIString, - }; - - ValueType value_type; - unsigned char const* key_string_addr; - void const* value_addr; -}; diff --git a/Kernel/API/InodeWatcherFlags.h b/Kernel/API/InodeWatcherFlags.h deleted file mode 100644 index c9cdf42b1f9..00000000000 --- a/Kernel/API/InodeWatcherFlags.h +++ /dev/null @@ -1,18 +0,0 @@ -/* - * Copyright (c) 2021, sin-ack - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include - -enum class InodeWatcherFlags : u32 { - None = 0, - Nonblock = 1 << 0, - CloseOnExec = 1 << 1, -}; - -AK_ENUM_BITWISE_OPERATORS(InodeWatcherFlags); diff --git a/Kernel/API/Ioctl.h b/Kernel/API/Ioctl.h deleted file mode 100644 index 608e9c84c7f..00000000000 --- a/Kernel/API/Ioctl.h +++ /dev/null @@ -1,208 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * Copyright (c) 2021, Edwin Hoksberg - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#ifdef __cplusplus -extern "C" { -#endif - -struct winsize { - unsigned short ws_row; - unsigned short ws_col; - unsigned short ws_xpixel; - unsigned short ws_ypixel; -}; - -struct GraphicsConnectorProperties { - unsigned char multihead_support; - unsigned char doublebuffer_support; - unsigned char flushing_support; - unsigned char partial_flushing_support; - unsigned char refresh_rate_support; - unsigned max_buffer_bytes; -}; - -struct GraphicsHeadModeSetting { - int horizontal_stride; - int pixel_clock_in_khz; - int horizontal_active; - int horizontal_front_porch_pixels; - int horizontal_sync_time_pixels; - int horizontal_blank_pixels; - int vertical_active; - int vertical_front_porch_lines; - int vertical_sync_time_lines; - int vertical_blank_lines; - int horizontal_offset; - int vertical_offset; -}; - -struct GraphicsHeadEDID { - unsigned char* bytes; - unsigned bytes_size; -}; - -struct GraphicsHeadVerticalOffset { - int head_index; - int offsetted; -}; - -struct FBRect { - int head_index; - unsigned x; - unsigned y; - unsigned width; - unsigned height; -}; - -struct FBBufferOffset { - int buffer_index; - unsigned offset; -}; - -struct FBFlushRects { - int buffer_index; - unsigned count; - struct FBRect const* rects; -}; - -enum ConsoleModes { - KD_TEXT = 0x00, - KD_GRAPHICS = 0x01, -}; - -#ifdef __cplusplus -} -#endif - -enum IOCtlNumber { - TIOCGPGRP, - TIOCSPGRP, - TCGETS, - TCSETS, - TCSETSW, - TCSETSF, - TCFLSH, - TIOCGWINSZ, - TIOCSCTTY, - TIOCSTI, - TIOCNOTTY, - TIOCSWINSZ, - TIOCGPTN, - GRAPHICS_IOCTL_GET_PROPERTIES, - GRAPHICS_IOCTL_SET_HEAD_VERTICAL_OFFSET_BUFFER, - GRAPHICS_IOCTL_GET_HEAD_VERTICAL_OFFSET_BUFFER, - GRAPHICS_IOCTL_FLUSH_HEAD_BUFFERS, - GRAPHICS_IOCTL_FLUSH_HEAD, - GRAPHICS_IOCTL_SET_HEAD_MODE_SETTING, - GRAPHICS_IOCTL_GET_HEAD_MODE_SETTING, - GRAPHICS_IOCTL_SET_SAFE_HEAD_MODE_SETTING, - GRAPHICS_IOCTL_SET_RESPONSIBLE, - GRAPHICS_IOCTL_UNSET_RESPONSIBLE, - KEYBOARD_IOCTL_GET_NUM_LOCK, - KEYBOARD_IOCTL_SET_NUM_LOCK, - KEYBOARD_IOCTL_GET_CAPS_LOCK, - KEYBOARD_IOCTL_SET_CAPS_LOCK, - MOUNT_IOCTL_SET_MOUNT_SPECIFIC_FLAG, - SIOCATMARK, - SIOCSIFADDR, - SIOCGIFADDR, - SIOCGIFHWADDR, - SIOCGIFNAME, - SIOCGIFINDEX, - SIOCGIFNETMASK, - SIOCSIFNETMASK, - SIOCGIFBRDADDR, - SIOCGIFMTU, - SIOCGIFFLAGS, - SIOCGIFCONF, - SIOCADDRT, - SIOCDELRT, - SIOCSARP, - SIOCDARP, - FIBMAP, - FIONBIO, - FIONREAD, - FIOCLEX, - FIONCLEX, - KCOV_SETBUFSIZE, - KCOV_ENABLE, - KCOV_DISABLE, - SOUNDCARD_IOCTL_SET_SAMPLE_RATE, - SOUNDCARD_IOCTL_GET_SAMPLE_RATE, - STORAGE_DEVICE_GET_SIZE, - STORAGE_DEVICE_GET_BLOCK_SIZE, - VIRGL_IOCTL_CREATE_CONTEXT, - VIRGL_IOCTL_CREATE_RESOURCE, - VIRGL_IOCTL_SUBMIT_CMD, - VIRGL_IOCTL_TRANSFER_DATA, - KDSETMODE, - KDGETMODE, - DEVCTL_CREATE_LOOP_DEVICE, - DEVCTL_DESTROY_LOOP_DEVICE, -}; - -#define TIOCGPGRP TIOCGPGRP -#define TIOCSPGRP TIOCSPGRP -#define TCGETS TCGETS -#define TCSETS TCSETS -#define TCSETSW TCSETSW -#define TCSETSF TCSETSF -#define TCFLSH TCFLSH -#define TIOCGWINSZ TIOCGWINSZ -#define TIOCSCTTY TIOCSCTTY -#define TIOCSTI TIOCSTI -#define TIOCNOTTY TIOCNOTTY -#define TIOCSWINSZ TIOCSWINSZ -#define TIOCGPTN TIOCGPTN -#define GRAPHICS_IOCTL_GET_PROPERTIES GRAPHICS_IOCTL_GET_PROPERTIES -#define GRAPHICS_IOCTL_SET_HEAD_VERTICAL_OFFSET_BUFFER GRAPHICS_IOCTL_SET_HEAD_VERTICAL_OFFSET_BUFFER -#define GRAPHICS_IOCTL_GET_HEAD_VERTICAL_OFFSET_BUFFER GRAPHICS_IOCTL_GET_HEAD_VERTICAL_OFFSET_BUFFER -#define GRAPHICS_IOCTL_FLUSH_HEAD_BUFFERS GRAPHICS_IOCTL_FLUSH_HEAD_BUFFERS -#define GRAPHICS_IOCTL_FLUSH_HEAD GRAPHICS_IOCTL_FLUSH_HEAD -#define GRAPHICS_IOCTL_SET_HEAD_MODE_SETTING GRAPHICS_IOCTL_SET_HEAD_MODE_SETTING -#define GRAPHICS_IOCTL_GET_HEAD_MODE_SETTING GRAPHICS_IOCTL_GET_HEAD_MODE_SETTING -#define GRAPHICS_IOCTL_SET_SAFE_HEAD_MODE_SETTING GRAPHICS_IOCTL_SET_SAFE_HEAD_MODE_SETTING -#define GRAPHICS_IOCTL_SET_RESPONSIBLE GRAPHICS_IOCTL_SET_RESPONSIBLE -#define GRAPHICS_IOCTL_UNSET_RESPONSIBLE GRAPHICS_IOCTL_UNSET_RESPONSIBLE -#define KEYBOARD_IOCTL_GET_NUM_LOCK KEYBOARD_IOCTL_GET_NUM_LOCK -#define KEYBOARD_IOCTL_SET_NUM_LOCK KEYBOARD_IOCTL_SET_NUM_LOCK -#define KEYBOARD_IOCTL_GET_CAPS_LOCK KEYBOARD_IOCTL_GET_CAPS_LOCK -#define KEYBOARD_IOCTL_SET_CAPS_LOCK KEYBOARD_IOCTL_SET_CAPS_LOCK -#define SIOCATMARK SIOCATMARK -#define SIOCSIFADDR SIOCSIFADDR -#define SIOCGIFADDR SIOCGIFADDR -#define SIOCGIFHWADDR SIOCGIFHWADDR -#define SIOCGIFNETMASK SIOCGIFNETMASK -#define SIOCGIFNAME SIOCGIFNAME -#define SIOCGIFINDEX SIOCGIFINDEX -#define SIOCSIFNETMASK SIOCSIFNETMASK -#define SIOCGIFBRDADDR SIOCGIFBRDADDR -#define SIOCGIFMTU SIOCGIFMTU -#define SIOCGIFFLAGS SIOCGIFFLAGS -#define SIOCGIFCONF SIOCGIFCONF -#define SIOCADDRT SIOCADDRT -#define SIOCDELRT SIOCDELRT -#define SIOCSARP SIOCSARP -#define SIOCDARP SIOCDARP -#define FIBMAP FIBMAP -#define FIONBIO FIONBIO -#define FIONREAD FIONREAD -#define MOUNT_IOCTL_SET_MOUNT_SPECIFIC_FLAG MOUNT_IOCTL_SET_MOUNT_SPECIFIC_FLAG -#define SOUNDCARD_IOCTL_SET_SAMPLE_RATE SOUNDCARD_IOCTL_SET_SAMPLE_RATE -#define SOUNDCARD_IOCTL_GET_SAMPLE_RATE SOUNDCARD_IOCTL_GET_SAMPLE_RATE -#define STORAGE_DEVICE_GET_SIZE STORAGE_DEVICE_GET_SIZE -#define STORAGE_DEVICE_GET_BLOCK_SIZE STORAGE_DEVICE_GET_BLOCK_SIZE -#define VIRGL_IOCTL_CREATE_CONTEXT VIRGL_IOCTL_CREATE_CONTEXT -#define VIRGL_IOCTL_CREATE_RESOURCE VIRGL_IOCTL_CREATE_RESOURCE -#define VIRGL_IOCTL_SUBMIT_CMD VIRGL_IOCTL_SUBMIT_CMD -#define VIRGL_IOCTL_TRANSFER_DATA VIRGL_IOCTL_TRANSFER_DATA -#define KDSETMODE KDSETMODE -#define KDGETMODE KDGETMODE -#define DEVCTL_CREATE_LOOP_DEVICE DEVCTL_CREATE_LOOP_DEVICE -#define DEVCTL_DESTROY_LOOP_DEVICE DEVCTL_DESTROY_LOOP_DEVICE diff --git a/Kernel/API/Jail.h b/Kernel/API/Jail.h deleted file mode 100644 index cb2885fa187..00000000000 --- a/Kernel/API/Jail.h +++ /dev/null @@ -1,17 +0,0 @@ -/* - * Copyright (c) 2022, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include - -enum class JailIsolationFlags : u32 { - None = 0, - PIDIsolation = 1 << 0, -}; - -AK_ENUM_BITWISE_OPERATORS(JailIsolationFlags); diff --git a/Kernel/API/MemoryLayout.h b/Kernel/API/MemoryLayout.h deleted file mode 100644 index 97db070c10c..00000000000 --- a/Kernel/API/MemoryLayout.h +++ /dev/null @@ -1,12 +0,0 @@ -/* - * Copyright (c) 2021, Idan Horowitz - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include - -constexpr FlatPtr userspace_range_base = USER_RANGE_BASE; diff --git a/Kernel/API/MousePacket.h b/Kernel/API/MousePacket.h deleted file mode 100644 index 0ab8a5b4ecf..00000000000 --- a/Kernel/API/MousePacket.h +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include - -struct MousePacket { - int x { 0 }; - int y { 0 }; - int z { 0 }; - int w { 0 }; - - enum Button { - LeftButton = 0x01, - RightButton = 0x02, - MiddleButton = 0x04, - BackwardButton = 0x08, - ForwardButton = 0x10, - }; - - unsigned char buttons { 0 }; - bool is_relative { true }; -}; diff --git a/Kernel/API/POSIX/dirent.h b/Kernel/API/POSIX/dirent.h deleted file mode 100644 index 20b9db0bb43..00000000000 --- a/Kernel/API/POSIX/dirent.h +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright (c) 2018-2021, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include - -#ifdef __cplusplus -extern "C" { -#endif - -enum { - DT_UNKNOWN = 0, -#define DT_UNKNOWN DT_UNKNOWN - DT_FIFO = 1, -#define DT_FIFO DT_FIFO - DT_CHR = 2, -#define DT_CHR DT_CHR - DT_DIR = 4, -#define DT_DIR DT_DIR - DT_BLK = 6, -#define DT_BLK DT_BLK - DT_REG = 8, -#define DT_REG DT_REG - DT_LNK = 10, -#define DT_LNK DT_LNK - DT_SOCK = 12, -#define DT_SOCK DT_SOCK - DT_WHT = 14 -#define DT_WHT DT_WHT -}; - -#define MAXNAMLEN NAME_MAX - -#ifdef __cplusplus -} -#endif diff --git a/Kernel/API/POSIX/errno.h b/Kernel/API/POSIX/errno.h deleted file mode 100644 index 6b4f454e909..00000000000 --- a/Kernel/API/POSIX/errno.h +++ /dev/null @@ -1,99 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * Copyright (c) 2021, sin-ack - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#define ENUMERATE_ERRNO_CODES(E) \ - E(ESUCCESS, "Success (not an error)") \ - E(EPERM, "Operation not permitted") \ - E(ENOENT, "No such file or directory") \ - E(ESRCH, "No such process") \ - E(EINTR, "Interrupted syscall") \ - E(EIO, "I/O error") \ - E(ENXIO, "No such device or address") \ - E(E2BIG, "Argument list too long") \ - E(ENOEXEC, "Exec format error") \ - E(EBADF, "Bad fd number") \ - E(ECHILD, "No child processes") \ - E(EAGAIN, "Try again") \ - E(ENOMEM, "Out of memory") \ - E(EACCES, "Permission denied") \ - E(EFAULT, "Bad address") \ - E(ENOTBLK, "Block device required") \ - E(EBUSY, "Device or resource busy") \ - E(EEXIST, "File already exists") \ - E(EXDEV, "Cross-device link") \ - E(ENODEV, "No such device") \ - E(ENOTDIR, "Not a directory") \ - E(EISDIR, "Is a directory") \ - E(EINVAL, "Invalid argument") \ - E(ENFILE, "File table overflow") \ - E(EMFILE, "Too many open files") \ - E(ENOTTY, "Not a TTY") \ - E(ETXTBSY, "Text file busy") \ - E(EFBIG, "File too large") \ - E(ENOSPC, "No space left on device") \ - E(ESPIPE, "Illegal seek") \ - E(EROFS, "Read-only filesystem") \ - E(EMLINK, "Too many links") \ - E(EPIPE, "Broken pipe") \ - E(ERANGE, "Range error") \ - E(ENAMETOOLONG, "Name too long") \ - E(ELOOP, "Too many symlinks") \ - E(EOVERFLOW, "Overflow") \ - E(EOPNOTSUPP, "Operation not supported") \ - E(ENOSYS, "No such syscall") \ - E(ENOTIMPL, "Not implemented") \ - E(EAFNOSUPPORT, "Address family not supported") \ - E(ENOTSOCK, "Not a socket") \ - E(EADDRINUSE, "Address in use") \ - E(ENOTEMPTY, "Directory not empty") \ - E(EDOM, "Math argument out of domain") \ - E(ECONNREFUSED, "Connection refused") \ - E(EHOSTDOWN, "Host is down") \ - E(EADDRNOTAVAIL, "Address not available") \ - E(EISCONN, "Already connected") \ - E(ECONNABORTED, "Connection aborted") \ - E(EALREADY, "Connection already in progress") \ - E(ECONNRESET, "Connection reset") \ - E(EDESTADDRREQ, "Destination address required") \ - E(EHOSTUNREACH, "Host unreachable") \ - E(EILSEQ, "Illegal byte sequence") \ - E(EMSGSIZE, "Message size") \ - E(ENETDOWN, "Network down") \ - E(ENETUNREACH, "Network unreachable") \ - E(ENETRESET, "Network reset") \ - E(ENOBUFS, "No buffer space") \ - E(ENOLCK, "No lock available") \ - E(ENOMSG, "No message") \ - E(ENOPROTOOPT, "No protocol option") \ - E(ENOTCONN, "Not connected") \ - E(ESHUTDOWN, "Transport endpoint has shutdown") \ - E(ETOOMANYREFS, "Too many references") \ - E(ESOCKTNOSUPPORT, "Socket type not supported") \ - E(EPROTONOSUPPORT, "Protocol not supported") \ - E(EDEADLK, "Resource deadlock would occur") \ - E(ETIMEDOUT, "Timed out") \ - E(EPROTOTYPE, "Wrong protocol type") \ - E(EINPROGRESS, "Operation in progress") \ - E(ENOTHREAD, "No such thread") \ - E(EPROTO, "Protocol error") \ - E(ENOTSUP, "Not supported") \ - E(EPFNOSUPPORT, "Protocol family not supported") \ - E(EDIRINTOSELF, "Cannot make directory a subdirectory of itself") \ - E(EDQUOT, "Quota exceeded") \ - E(ENOTRECOVERABLE, "State not recoverable") \ - E(ECANCELED, "Operation cancelled") \ - E(EPROMISEVIOLATION, "The process has a promise violation") \ - E(ESTALE, "Stale network file handle") \ - E(EMAXERRNO, "The highest errno +1 :^)") - -enum ErrnoCode { -#define __ENUMERATE_ERRNO_CODE(c, s) c, - ENUMERATE_ERRNO_CODES(__ENUMERATE_ERRNO_CODE) -#undef __ENUMERATE_ERRNO_CODE -}; diff --git a/Kernel/API/POSIX/fcntl.h b/Kernel/API/POSIX/fcntl.h deleted file mode 100644 index cea7461ebc2..00000000000 --- a/Kernel/API/POSIX/fcntl.h +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright (c) 2018-2021, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include - -#ifdef __cplusplus -extern "C" { -#endif - -#define F_DUPFD 0 -#define F_GETFD 1 -#define F_SETFD 2 -#define F_GETFL 3 -#define F_SETFL 4 -#define F_ISTTY 5 -#define F_GETLK 6 -#define F_SETLK 7 -#define F_SETLKW 8 -#define F_DUPFD_CLOEXEC 9 - -#define FD_CLOEXEC 1 - -#define O_RDONLY (1 << 0) -#define O_WRONLY (1 << 1) -#define O_RDWR (O_RDONLY | O_WRONLY) -#define O_ACCMODE (O_RDONLY | O_WRONLY) -#define O_EXEC (1 << 2) -#define O_CREAT (1 << 3) -#define O_EXCL (1 << 4) -#define O_NOCTTY (1 << 5) -#define O_TRUNC (1 << 6) -#define O_APPEND (1 << 7) -#define O_NONBLOCK (1 << 8) -#define O_DIRECTORY (1 << 9) -#define O_NOFOLLOW (1 << 10) -#define O_CLOEXEC (1 << 11) -#define O_DIRECT (1 << 12) -#define O_SYNC (1 << 13) - -#define F_RDLCK ((short)0) -#define F_WRLCK ((short)1) -#define F_UNLCK ((short)2) - -#define AT_FDCWD -100 -#define AT_SYMLINK_NOFOLLOW 0x100 -#define AT_REMOVEDIR 0x200 -#define AT_EACCESS 0x400 - -struct flock { - short l_type; - short l_whence; - off_t l_start; - off_t l_len; - pid_t l_pid; -}; - -#ifdef __cplusplus -} -#endif diff --git a/Kernel/API/POSIX/futex.h b/Kernel/API/POSIX/futex.h deleted file mode 100644 index 0b42e4584c3..00000000000 --- a/Kernel/API/POSIX/futex.h +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright (c) 2018-2021, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include - -#ifdef __cplusplus -extern "C" { -#endif - -#define _FUTEX_OP_SHIFT_OP 28 -#define _FUTEX_OP_MASK_OP 0xf -#define _FUTEX_OP_SHIFT_CMP 24 -#define _FUTEX_OP_MASK_CMP 0xf -#define _FUTEX_OP_SHIFT_OP_ARG 12 -#define _FUTEX_OP_MASK_OP_ARG 0xfff -#define _FUTEX_OP_SHIFT_CMP_ARG 0 -#define _FUTEX_OP_MASK_CMP_ARG 0xfff - -#define FUTEX_OP(op, op_arg, cmp, cmp_arg) \ - ((((op) & _FUTEX_OP_MASK_OP) << _FUTEX_OP_SHIFT_OP) | (((cmp) & _FUTEX_OP_MASK_CMP) << _FUTEX_OP_SHIFT_CMP) | (((op_arg) & _FUTEX_OP_MASK_OP_ARG) << _FUTEX_OP_SHIFT_OP_ARG) | (((cmp_arg) & _FUTEX_OP_MASK_CMP_ARG) << _FUTEX_OP_SHIFT_CMP_ARG)) - -#define _FUTEX_OP(val3) (((val3) >> _FUTEX_OP_SHIFT_OP) & _FUTEX_OP_MASK_OP) -#define _FUTEX_CMP(val3) (((val3) >> _FUTEX_OP_SHIFT_CMP) & _FUTEX_OP_MASK_CMP) -#define _FUTEX_OP_ARG(val3) (((val3) >> _FUTEX_OP_SHIFT_OP_ARG) & _FUTEX_OP_MASK_OP_ARG) -#define _FUTEX_CMP_ARG(val3) (((val3) >> _FUTEX_OP_SHIFT_CMP_ARG) & _FUTEX_OP_MASK_CMP_ARG) - -#define FUTEX_OP_SET 0 -#define FUTEX_OP_ADD 1 -#define FUTEX_OP_OR 2 -#define FUTEX_OP_ANDN 3 -#define FUTEX_OP_XOR 4 -#define FUTEX_OP_ARG_SHIFT 8 - -#define FUTEX_OP_CMP_EQ 0 -#define FUTEX_OP_CMP_NE 1 -#define FUTEX_OP_CMP_LT 2 -#define FUTEX_OP_CMP_LE 3 -#define FUTEX_OP_CMP_GT 4 -#define FUTEX_OP_CMP_GE 5 - -#define FUTEX_WAIT 1 -#define FUTEX_WAKE 2 - -#define FUTEX_REQUEUE 3 -#define FUTEX_CMP_REQUEUE 4 -#define FUTEX_WAKE_OP 5 -#define FUTEX_WAIT_BITSET 9 -#define FUTEX_WAKE_BITSET 10 - -#define FUTEX_CLOCK_REALTIME (1 << 8) -#define FUTEX_PRIVATE_FLAG (1 << 9) -#define FUTEX_CMD_MASK ~(FUTEX_CLOCK_REALTIME | FUTEX_PRIVATE_FLAG) - -#define FUTEX_BITSET_MATCH_ANY 0xffffffff - -#ifdef __cplusplus -} -#endif diff --git a/Kernel/API/POSIX/ifaddrs.h b/Kernel/API/POSIX/ifaddrs.h deleted file mode 100644 index f17fee81200..00000000000 --- a/Kernel/API/POSIX/ifaddrs.h +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright (c) 2021, Idan Horowitz - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#ifdef __cplusplus -extern "C" { -#endif - -struct ifaddrs { - struct ifaddrs* ifa_next; - char* ifa_name; - unsigned int ifa_flags; - struct sockaddr* ifa_addr; - struct sockaddr* ifa_netmask; - union { - struct sockaddr* ifu_broadaddr; - struct sockaddr* ifu_dstaddr; - } ifa_ifu; -#define ifa_broadaddr ifa_ifu.ifu_broadaddr -#define ifa_dstaddr ifa_ifu.ifu_dstaddr - void* ifa_data; -}; - -#ifdef __cplusplus -} -#endif diff --git a/Kernel/API/POSIX/net/if.h b/Kernel/API/POSIX/net/if.h deleted file mode 100644 index 31eab3fad90..00000000000 --- a/Kernel/API/POSIX/net/if.h +++ /dev/null @@ -1,91 +0,0 @@ -/* - * Copyright (c) 2018-2021, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#ifdef __cplusplus -extern "C" { -#endif - -#include -#include - -#define IF_NAMESIZE 16 - -enum { - IFF_UP = 1 << 0, - IFF_BROADCAST = 1 << 1, - IFF_DEBUG = 1 << 2, - IFF_LOOPBACK = 1 << 3, - IFF_POINTOPOINT = 1 << 4, - IFF_RUNNING = 1 << 5, - IFF_NOARP = 1 << 6, - IFF_PROMISC = 1 << 7, - IFF_ALLMULTI = 1 << 8, - IFF_MULTICAST = 1 << 9, -}; -#define IFF_UP IFF_UP -#define IFF_BROADCAST IFF_BROADCAST -#define IFF_DEBUG IFF_DEBUG -#define IFF_LOOPBACK IFF_LOOPBACK -#define IFF_POINTOPOINT IFF_POINTOPOINT -#define IFF_RUNNING IFF_RUNNING -#define IFF_NOARP IFF_NOARP -#define IFF_PROMISC IFF_PROMISC -#define IFF_ALLMULTI IFF_ALLMULTI -#define IFF_MULTICAST IFF_MULTICAST - -struct ifconf { - int ifc_len; - union { - void* ifc_buf; - struct ifreq* ifc_req; - }; -}; - -struct ifreq { -#define IFNAMSIZ 16 - char ifr_name[IFNAMSIZ]; - union { - struct sockaddr ifru_addr; - struct sockaddr ifru_dstaddr; - struct sockaddr ifru_broadaddr; - struct sockaddr ifru_netmask; - struct sockaddr ifru_hwaddr; - short ifru_flags; - int ifru_metric; - int64_t ifru_vnetid; - uint64_t ifru_media; - void* ifru_data; - unsigned int ifru_index; - } ifr_ifru; - -#define ifr_addr ifr_ifru.ifru_addr // address -#define ifr_dstaddr ifr_ifru.ifru_dstaddr // other end of p-to-p link -#define ifr_broadaddr ifr_ifru.ifru_broadaddr // broadcast address -#define ifr_netmask ifr_ifru.ifru_netmask // network mask -#define ifr_flags ifr_ifru.ifru_flags // flags -#define ifr_metric ifr_ifru.ifru_metric // metric -#define ifr_mtu ifr_ifru.ifru_metric // mtu (overload) -#define ifr_hardmtu ifr_ifru.ifru_metric // hardmtu (overload) -#define ifr_media ifr_ifru.ifru_media // media options -#define ifr_rdomainid ifr_ifru.ifru_metric // VRF instance (overload) -#define ifr_vnetid ifr_ifru.ifru_vnetid // Virtual Net Id -#define ifr_ttl ifr_ifru.ifru_metric // tunnel TTL (overload) -#define ifr_data ifr_ifru.ifru_data // for use by interface -#define ifr_index ifr_ifru.ifru_index // interface index -#define ifr_llprio ifr_ifru.ifru_metric // link layer priority -#define ifr_hwaddr ifr_ifru.ifru_hwaddr // MAC address -}; - -struct if_nameindex { - unsigned int if_index; - char* if_name; -}; - -#ifdef __cplusplus -} -#endif diff --git a/Kernel/API/POSIX/net/if_arp.h b/Kernel/API/POSIX/net/if_arp.h deleted file mode 100644 index 5ac1555b17b..00000000000 --- a/Kernel/API/POSIX/net/if_arp.h +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright (c) 2018-2021, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#ifdef __cplusplus -extern "C" { -#endif - -#include -#include - -struct arpreq { - struct sockaddr arp_pa; /* protocol address */ - struct sockaddr arp_ha; /* hardware address */ - struct sockaddr arp_netmask; /* netmask of protocol address */ - int arp_flags; /* flags */ - char arp_dev[16]; -}; - -#define ARPHRD_ETHER 1 -#define ARPHRD_IEEE802 6 -#define ARPHRD_SLIP 256 -#define ARPHRD_PPP 512 -#define ARPHRD_LOOPBACK 772 -#define ARPHRD_FDDI 774 -#define ARPHRD_IEEE802_TR 800 - -#ifdef __cplusplus -} -#endif diff --git a/Kernel/API/POSIX/net/route.h b/Kernel/API/POSIX/net/route.h deleted file mode 100644 index 92c9bfb7f0a..00000000000 --- a/Kernel/API/POSIX/net/route.h +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright (c) 2020, Marios Prokopakis - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include - -#ifdef __cplusplus -extern "C" { -#endif - -struct rtentry { - struct sockaddr rt_dst; /* the target address */ - struct sockaddr rt_gateway; /* the gateway address */ - struct sockaddr rt_genmask; /* the target network mask */ - unsigned short int rt_flags; - char* rt_dev; - /* FIXME: complete the struct */ -}; - -#define RTF_UP 0x1 /* do not delete the route */ -#define RTF_GATEWAY 0x2 /* the route is a gateway and not an end host */ -#define RTF_HOST 0x4 /* host entry (net otherwise) */ - -#ifdef __cplusplus -} -#endif diff --git a/Kernel/API/POSIX/netinet/in.h b/Kernel/API/POSIX/netinet/in.h deleted file mode 100644 index fc96d5bba25..00000000000 --- a/Kernel/API/POSIX/netinet/in.h +++ /dev/null @@ -1,132 +0,0 @@ -/* - * Copyright (c) 2018-2021, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include - -#ifdef __cplusplus -extern "C" { -#endif - -typedef uint32_t in_addr_t; - -#define INADDR_ANY ((in_addr_t)0) -#define INADDR_NONE ((in_addr_t)(-1)) -#define INADDR_LOOPBACK 0x7f000001 -#define INADDR_BROADCAST 0xffffffff - -#define IN_CLASSA_NET 0xff000000 -#define IN_CLASSB_NET 0xffff0000 -#define IN_CLASSC_NET 0xffffff00 - -#define IN_LOOPBACKNET 127 - -#define IP_TOS 1 -#define IP_TTL 2 -#define IP_MULTICAST_LOOP 3 -#define IP_ADD_MEMBERSHIP 4 -#define IP_DROP_MEMBERSHIP 5 -#define IP_MULTICAST_IF 6 -#define IP_MULTICAST_TTL 7 -#define IP_BLOCK_SOURCE 8 -#define IP_UNBLOCK_SOURCE 9 -#define IP_OPTIONS 10 - -#define IPTOS_LOWDELAY 16 -#define IPTOS_THROUGHPUT 8 -#define IPTOS_RELIABILITY 4 - -/* Make sure these don't overlap with any other IPv4 and IPv6 options */ -#define MCAST_JOIN_SOURCE_GROUP 100 -#define MCAST_LEAVE_SOURCE_GROUP 101 - -#define IPPORT_RESERVED 1024 -#define IPPORT_USERRESERVED 5000 - -typedef uint16_t in_port_t; - -struct in_addr { - uint32_t s_addr; -}; - -struct sockaddr_in { - sa_family_t sin_family; - in_port_t sin_port; - struct in_addr sin_addr; - char sin_zero[8]; -}; - -struct ip_mreq { - struct in_addr imr_multiaddr; - struct in_addr imr_interface; -}; - -struct group_source_req { - uint32_t gsr_interface; - struct sockaddr_storage gsr_group; - struct sockaddr_storage gsr_source; -}; - -struct ip_mreq_source { - struct in_addr imr_multiaddr; - struct in_addr imr_sourceaddr; - struct in_addr imr_interface; -}; - -#define IPV6_UNICAST_HOPS 1 -#define IPV6_MULTICAST_HOPS 2 -#define IPV6_MULTICAST_LOOP 3 -#define IPV6_MULTICAST_IF 4 -#define IPV6_ADD_MEMBERSHIP 5 -#define IPV6_DROP_MEMBERSHIP 6 -#define IP_ADD_SOURCE_MEMBERSHIP 7 -#define IP_DROP_SOURCE_MEMBERSHIP 8 -#define IPV6_V6ONLY 9 -#define IPV6_JOIN_GROUP 5 -#define IPV6_LEAVE_GROUP 6 -#define IPV6_RECVPKTINFO 10 -#define IPV6_PKTINFO 11 -#define IPV6_RECVHOPLIMIT 12 -#define IPV6_HOPLIMIT 13 - -struct in6_addr { - union { - uint8_t s6_addr[16]; - uint32_t s6_addr32[4]; - }; -}; - -struct in6_pktinfo { - struct in6_addr ipi6_addr; - uint32_t ipi6_ifindex; -}; - -/* clang-format off */ -#define IN6ADDR_ANY_INIT { { { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } } } -#define IN6ADDR_LOOPBACK_INIT { { { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 } } } -/* clang-format on */ - -extern const struct in6_addr in6addr_any; -extern const struct in6_addr in6addr_loopback; - -struct sockaddr_in6 { - sa_family_t sin6_family; // AF_INET6. - in_port_t sin6_port; // Port number. - uint32_t sin6_flowinfo; // IPv6 traffic class and flow information. - struct in6_addr sin6_addr; // IPv6 address. - uint32_t sin6_scope_id; // Set of interfaces for a scop -}; - -struct ipv6_mreq { - struct in6_addr ipv6mr_multiaddr; - uint32_t ipv6mr_interface; -}; - -#ifdef __cplusplus -} -#endif diff --git a/Kernel/API/POSIX/netinet/tcp.h b/Kernel/API/POSIX/netinet/tcp.h deleted file mode 100644 index 5bea9ee99fc..00000000000 --- a/Kernel/API/POSIX/netinet/tcp.h +++ /dev/null @@ -1,18 +0,0 @@ -/* - * Copyright (c) 2023, Romain Chardiny - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#ifdef __cplusplus -extern "C" { -#endif - -#define TCP_NODELAY 10 -#define TCP_MAXSEG 11 - -#ifdef __cplusplus -} -#endif diff --git a/Kernel/API/POSIX/poll.h b/Kernel/API/POSIX/poll.h deleted file mode 100644 index c481ab61c75..00000000000 --- a/Kernel/API/POSIX/poll.h +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright (c) 2018-2021, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include - -#ifdef __cplusplus -extern "C" { -#endif - -#define POLLIN (1u << 0) -#define POLLRDNORM POLLIN -#define POLLPRI (1u << 1) -#define POLLOUT (1u << 2) -#define POLLWRNORM POLLOUT -#define POLLERR (1u << 3) -#define POLLHUP (1u << 4) -#define POLLNVAL (1u << 5) -#define POLLWRBAND (1u << 12) -#define POLLRDHUP (1u << 13) - -struct pollfd { - int fd; - short events; - short revents; -}; - -typedef unsigned nfds_t; - -#ifdef __cplusplus -} -#endif diff --git a/Kernel/API/POSIX/sched.h b/Kernel/API/POSIX/sched.h deleted file mode 100644 index e2e6b978b90..00000000000 --- a/Kernel/API/POSIX/sched.h +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright (c) 2018-2021, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include - -#ifdef __cplusplus -extern "C" { -#endif - -struct sched_param { - int sched_priority; -}; - -#define THREAD_PRIORITY_MIN 1 -#define THREAD_PRIORITY_LOW 10 -#define THREAD_PRIORITY_NORMAL 30 -#define THREAD_PRIORITY_HIGH 50 -#define THREAD_PRIORITY_MAX 99 - -#ifdef __cplusplus -} -#endif diff --git a/Kernel/API/POSIX/select.h b/Kernel/API/POSIX/select.h deleted file mode 100644 index fc80e62a76b..00000000000 --- a/Kernel/API/POSIX/select.h +++ /dev/null @@ -1,9 +0,0 @@ -/* - * Copyright (c) 2023, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#define FD_SETSIZE 1024 diff --git a/Kernel/API/POSIX/serenity.h b/Kernel/API/POSIX/serenity.h deleted file mode 100644 index 4bc29398463..00000000000 --- a/Kernel/API/POSIX/serenity.h +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright (c) 2018-2021, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include - -#ifdef __cplusplus -extern "C" { -#endif - -#define PURGE_ALL_VOLATILE 0x1 -#define PURGE_ALL_CLEAN_INODE 0x2 - -enum { - PERF_EVENT_SAMPLE = 1, - PERF_EVENT_MALLOC = 2, - PERF_EVENT_FREE = 4, - PERF_EVENT_MMAP = 8, - PERF_EVENT_MUNMAP = 16, - PERF_EVENT_PROCESS_CREATE = 32, - PERF_EVENT_PROCESS_EXEC = 64, - PERF_EVENT_PROCESS_EXIT = 128, - PERF_EVENT_THREAD_CREATE = 256, - PERF_EVENT_THREAD_EXIT = 512, - PERF_EVENT_CONTEXT_SWITCH = 1024, - PERF_EVENT_KMALLOC = 2048, - PERF_EVENT_KFREE = 4096, - PERF_EVENT_PAGE_FAULT = 8192, - PERF_EVENT_SYSCALL = 16384, - PERF_EVENT_SIGNPOST = 32768, - PERF_EVENT_FILESYSTEM = 65536, -}; - -#define PERF_EVENT_MASK_ALL (~0ull) - -#define THREAD_PRIORITY_MIN 1 -#define THREAD_PRIORITY_LOW 10 -#define THREAD_PRIORITY_NORMAL 30 -#define THREAD_PRIORITY_HIGH 50 -#define THREAD_PRIORITY_MAX 99 - -#ifdef __cplusplus -} -#endif diff --git a/Kernel/API/POSIX/signal.h b/Kernel/API/POSIX/signal.h deleted file mode 100644 index 10c6b4a20ef..00000000000 --- a/Kernel/API/POSIX/signal.h +++ /dev/null @@ -1,98 +0,0 @@ -/* - * Copyright (c) 2018-2021, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include - -#ifdef __cplusplus -extern "C" { -#endif - -typedef void (*__sighandler_t)(int); -typedef __sighandler_t sighandler_t; - -typedef uint32_t sigset_t; -typedef uint32_t sig_atomic_t; - -union sigval { - int sival_int; - void* sival_ptr; -}; - -typedef struct siginfo { - int si_signo; - int si_code; - int si_errno; - pid_t si_pid; - uid_t si_uid; - void* si_addr; - int si_status; - int si_band; - union sigval si_value; -} siginfo_t; - -struct sigaction { - union { - void (*sa_handler)(int); - void (*sa_sigaction)(int, siginfo_t*, void*); - }; - sigset_t sa_mask; - int sa_flags; -}; - -typedef struct { - void* ss_sp; - int ss_flags; - size_t ss_size; -} stack_t; - -#define SS_ONSTACK 1 -#define SS_DISABLE 2 - -// FIXME: These values are arbitrary, and might be platform dependent -#define MINSIGSTKSZ 4096 // Minimum allowed -#define SIGSTKSZ 32768 // Recommended size - -#define SIG_DFL ((__sighandler_t)0) -#define SIG_ERR ((__sighandler_t)(-1)) -#define SIG_IGN ((__sighandler_t)1) - -#define SA_NOCLDSTOP 1 -#define SA_NOCLDWAIT 2 -#define SA_SIGINFO 4 -#define SA_ONSTACK 0x08000000 -#define SA_RESTART 0x10000000 -#define SA_NODEFER 0x40000000 -#define SA_RESETHAND 0x80000000 - -#define SA_NOMASK SA_NODEFER -#define SA_ONESHOT SA_RESETHAND - -#define SIG_BLOCK 0 -#define SIG_UNBLOCK 1 -#define SIG_SETMASK 2 - -#define CLD_EXITED 0 -#define CLD_KILLED 1 -#define CLD_DUMPED 2 -#define CLD_TRAPPED 3 -#define CLD_STOPPED 4 -#define CLD_CONTINUED 5 - -#define FPE_INTDIV 0 -#define FPE_INTOVF 1 -#define FPE_FLTDIV 2 -#define FPE_FLTOVF 3 -#define FPE_FLTUND 4 -#define FPE_FLTRES 5 -#define FPE_FLTINV 6 -#define FPE_FLTSUB 7 - -#ifdef __cplusplus -} -#endif diff --git a/Kernel/API/POSIX/signal_numbers.h b/Kernel/API/POSIX/signal_numbers.h deleted file mode 100644 index 17ee5bd9d44..00000000000 --- a/Kernel/API/POSIX/signal_numbers.h +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#define SIGINVAL 0 -#define SIGHUP 1 -#define SIGINT 2 -#define SIGQUIT 3 -#define SIGILL 4 -#define SIGTRAP 5 -#define SIGABRT 6 -#define SIGBUS 7 -#define SIGFPE 8 -#define SIGKILL 9 -#define SIGUSR1 10 -#define SIGSEGV 11 -#define SIGUSR2 12 -#define SIGPIPE 13 -#define SIGALRM 14 -#define SIGTERM 15 -#define SIGSTKFLT 16 -#define SIGCHLD 17 -#define SIGCONT 18 -#define SIGSTOP 19 -#define SIGTSTP 20 -#define SIGTTIN 21 -#define SIGTTOU 22 -#define SIGURG 23 -#define SIGXCPU 24 -#define SIGXFSZ 25 -#define SIGVTALRM 26 -#define SIGPROF 27 -#define SIGWINCH 28 -#define SIGIO 29 -#define SIGINFO 30 -#define SIGSYS 31 -#define SIGCANCEL 32 -#define NSIG 33 diff --git a/Kernel/API/POSIX/stdio.h b/Kernel/API/POSIX/stdio.h deleted file mode 100644 index 9151b849b46..00000000000 --- a/Kernel/API/POSIX/stdio.h +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright (c) 2018-2021, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include - -#ifdef __cplusplus -extern "C" { -#endif - -#define SEEK_SET 0 -#define SEEK_CUR 1 -#define SEEK_END 2 - -#ifdef __cplusplus -} -#endif diff --git a/Kernel/API/POSIX/sys/auxv.h b/Kernel/API/POSIX/sys/auxv.h deleted file mode 100644 index 5506a1ccc5f..00000000000 --- a/Kernel/API/POSIX/sys/auxv.h +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright (c) 2021, Idan Horowitz - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#define AT_NULL 0 /* No length, last entry's a_type has this value */ -#define AT_IGNORE 1 /* Entry has no meaning, a_un undefined */ -#define AT_EXECFD 2 /* a_val contains a file descriptor of the main program image */ -#define AT_PHDR 3 /* a_ptr contains pointer to program header table of main program image */ -#define AT_PHENT 4 /* a_val holds size of program header table entries */ -#define AT_PHNUM 5 /* a_val holds number of program header table entries */ -#define AT_PAGESZ 6 /* a_val gives system page size in bytes */ -#define AT_BASE 7 /* a_ptr holds base address that Loader was loaded into memory */ -#define AT_FLAGS 8 /* a_val holds 1 bit flags. Undefined flags are 0 */ -#define AT_ENTRY 9 /* a_ptr holds entry point of the main program */ -#define AT_NOTELF 10 /* a_val non-zero if the program is not ELF */ -#define AT_UID 11 /* a_val holds real user id of process */ -#define AT_EUID 12 /* a_val holds effective user id of process */ -#define AT_GID 13 /* a_val holds real group id of process */ -#define AT_EGID 14 /* a_val holds effective group id of process */ -#define AT_PLATFORM 15 /* a_val points to a string containing platform name */ -#define AT_HWCAP 16 /* a_val contains bitmask of CPU features. Equivalent to CPUID 1.EDX*/ -#define AT_CLKTCK 17 /* a_val contains frequency at which times() increments. (Re: Spec. What is times()?) */ -#define AT_SECURE 23 /* a_val holds 1 if program in secure mode (e.g. suid). Otherwise 0 */ -#define AT_BASE_PLATFORM 24 /* a_ptr points to a string identifying base platform name, which might be different from platform (e.g x86_64 when in i386 compat) */ -#define AT_RANDOM 25 /* a_ptr points to 16 securely generated random bytes */ -#define AT_HWCAP2 26 /* a_val holds extended hw feature mask. Currently 0 */ -#define AT_EXECFN 31 /* a_ptr points to filename of executed program */ -#define AT_EXE_BASE 32 /* a_ptr holds base address where main program was loaded into memory */ -#define AT_EXE_SIZE 33 /* a_val holds the size of the main program in memory */ - -/* Auxiliary Vector types, from Intel386 ABI ver 1.0 section 2.3.3 */ -typedef struct -{ - long a_type; /* Note: Extended to long from int, for ease of compatibility w/64 bit */ - union { - long a_val; - void* a_ptr; - void (*a_fnc)(void); /* In spec, not used */ - } a_un; -} auxv_t; diff --git a/Kernel/API/POSIX/sys/limits.h b/Kernel/API/POSIX/sys/limits.h deleted file mode 100644 index 6de2f99b5a9..00000000000 --- a/Kernel/API/POSIX/sys/limits.h +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright (c) 2022, Andreas Kling - * Copyright (c) 2022, sin-ack - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -// FIXME: This file can be included from Lagom, but it should only be included on Serenity. When that is the case, we can remove the ifdef -#ifndef PAGE_SIZE -# define PAGE_SIZE 4096 -#endif - -#define PATH_MAX 4096 -#if !defined MAXPATHLEN && defined PATH_MAX -# define MAXPATHLEN PATH_MAX -#endif - -#define NAME_MAX 255 - -#define HOST_NAME_MAX 64 - -#define TTY_NAME_MAX 32 - -#define NGROUPS_MAX 32 - -#define ARG_MAX 65536 - -#define PTHREAD_STACK_MIN (64 * 1024) // 64KiB diff --git a/Kernel/API/POSIX/sys/mman.h b/Kernel/API/POSIX/sys/mman.h deleted file mode 100644 index 3e540decb3a..00000000000 --- a/Kernel/API/POSIX/sys/mman.h +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright (c) 2018-2021, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include - -#ifdef __cplusplus -extern "C" { -#endif - -#define MAP_FILE 0x00 -#define MAP_SHARED 0x01 -#define MAP_PRIVATE 0x02 -#define MAP_FIXED 0x10 -#define MAP_ANONYMOUS 0x20 -#define MAP_ANON MAP_ANONYMOUS -#define MAP_STACK 0x40 -#define MAP_NORESERVE 0x80 -#define MAP_RANDOMIZED 0x100 -#define MAP_PURGEABLE 0x200 -#define MAP_FIXED_NOREPLACE 0x400 - -#define PROT_READ 0x1 -#define PROT_WRITE 0x2 -#define PROT_EXEC 0x4 -#define PROT_NONE 0x0 - -#define MAP_FAILED ((void*)-1) - -#define MADV_NORMAL 0x0 -#define MADV_SET_VOLATILE 0x1 -#define MADV_SET_NONVOLATILE 0x2 -#define MADV_DONTNEED 0x3 -#define MADV_WILLNEED 0x4 -#define MADV_SEQUENTIAL 0x5 -#define MADV_RANDOM 0x6 - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/posix_madvise.html -#define POSIX_MADV_NORMAL MADV_NORMAL -#define POSIX_MADV_DONTNEED MADV_DONTNEED -#define POSIX_MADV_WILLNEED MADV_WILLNEED -#define POSIX_MADV_SEQUENTIAL MADV_SEQUENTIAL -#define POSIX_MADV_RANDOM MADV_RANDOM - -#define MS_SYNC 1 -#define MS_ASYNC 2 -#define MS_INVALIDATE 4 - -#ifdef __cplusplus -} -#endif diff --git a/Kernel/API/POSIX/sys/ptrace.h b/Kernel/API/POSIX/sys/ptrace.h deleted file mode 100644 index c27604bf0ad..00000000000 --- a/Kernel/API/POSIX/sys/ptrace.h +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright (c) 2018-2021, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include - -#ifdef __cplusplus -extern "C" { -#endif - -#define PT_TRACE_ME 1 -#define PT_ATTACH 2 -#define PT_CONTINUE 3 -#define PT_SYSCALL 4 -#define PT_GETREGS 5 -#define PT_DETACH 6 -#define PT_PEEK 7 -#define PT_POKE 8 -#define PT_SETREGS 9 - -// Serenity extensions: -#define PT_POKEDEBUG 10 -#define PT_PEEKDEBUG 11 -#define PT_PEEKBUF 12 - -#define PT_READ_I PT_PEEK -#define PT_READ_D PT_PEEK -#define PT_WRITE_I PT_POKE -#define PT_WRITE_D PT_POKE - -#define DEBUG_STATUS_REGISTER 6 -#define DEBUG_CONTROL_REGISTER 7 - -#ifdef __cplusplus -} -#endif diff --git a/Kernel/API/POSIX/sys/resource.h b/Kernel/API/POSIX/sys/resource.h deleted file mode 100644 index 09da0085019..00000000000 --- a/Kernel/API/POSIX/sys/resource.h +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright (c) 2022, Lucas Chollet - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include - -#ifdef __cplusplus -extern "C" { -#endif - -struct rusage { - struct timeval ru_utime; - struct timeval ru_stime; - long ru_maxrss; - long ru_ixrss; - long ru_idrss; - long ru_isrss; - long ru_minflt; - long ru_majflt; - long ru_nswap; - long ru_inblock; - long ru_oublock; - long ru_msgsnd; - long ru_msgrcv; - long ru_nsignals; - long ru_nvcsw; - long ru_nivcsw; -}; - -#define RUSAGE_SELF 1 -#define RUSAGE_CHILDREN 2 - -#ifdef __cplusplus -} -#endif diff --git a/Kernel/API/POSIX/sys/socket.h b/Kernel/API/POSIX/sys/socket.h deleted file mode 100644 index d8fc765b88a..00000000000 --- a/Kernel/API/POSIX/sys/socket.h +++ /dev/null @@ -1,187 +0,0 @@ -/* - * Copyright (c) 2018-2021, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include - -#ifdef __cplusplus -extern "C" { -#endif - -#define AF_MASK 0xff -#define AF_UNSPEC 0 -#define AF_LOCAL 1 -#define AF_UNIX AF_LOCAL -#define AF_INET 2 -#define AF_INET6 3 -#define AF_MAX 4 -#define PF_LOCAL AF_LOCAL -#define PF_UNIX PF_LOCAL -#define PF_INET AF_INET -#define PF_INET6 AF_INET6 -#define PF_UNSPEC AF_UNSPEC -#define PF_MAX AF_MAX - -#define SOCK_TYPE_MASK 0xff -#define SOCK_STREAM 1 -#define SOCK_DGRAM 2 -#define SOCK_RAW 3 -#define SOCK_RDM 4 -#define SOCK_SEQPACKET 5 -#define SOCK_NONBLOCK 04000 -#define SOCK_CLOEXEC 02000000 - -#define SHUT_RD 0 -#define SHUT_WR 1 -#define SHUT_RDWR 2 - -#define IPPROTO_IP 0 -#define IPPROTO_ICMP 1 -#define IPPROTO_IGMP 2 -#define IPPROTO_IPIP 4 -#define IPPROTO_TCP 6 -#define IPPROTO_UDP 17 -#define IPPROTO_IPV6 41 -#define IPPROTO_ESP 50 -#define IPPROTO_AH 51 -#define IPPROTO_ICMPV6 58 -#define IPPROTO_RAW 255 - -#define MSG_TRUNC 0x1 -#define MSG_CTRUNC 0x2 -#define MSG_PEEK 0x4 -#define MSG_OOB 0x8 -#define MSG_DONTROUTE 0x10 -#define MSG_WAITALL 0x20 -#define MSG_DONTWAIT 0x40 -#define MSG_NOSIGNAL 0x80 -#define MSG_EOR 0x100 - -typedef uint16_t sa_family_t; - -struct cmsghdr { - socklen_t cmsg_len; - int cmsg_level; - int cmsg_type; -}; - -struct msghdr { - void* msg_name; - socklen_t msg_namelen; - struct iovec* msg_iov; - int msg_iovlen; - void* msg_control; - socklen_t msg_controllen; - int msg_flags; -}; - -// These three are non-POSIX, but common: -#define CMSG_ALIGN(x) (((x) + sizeof(void*) - 1) & ~(sizeof(void*) - 1)) -#define CMSG_SPACE(x) (CMSG_ALIGN(sizeof(struct cmsghdr)) + CMSG_ALIGN(x)) -#define CMSG_LEN(x) (CMSG_ALIGN(sizeof(struct cmsghdr)) + (x)) - -static inline struct cmsghdr* CMSG_FIRSTHDR(struct msghdr* msg) -{ - if (msg->msg_controllen < sizeof(struct cmsghdr)) - return (struct cmsghdr*)0; - return (struct cmsghdr*)msg->msg_control; -} - -static inline struct cmsghdr* CMSG_NXTHDR(struct msghdr* msg, struct cmsghdr* cmsg) -{ - struct cmsghdr* next = (struct cmsghdr*)((char*)cmsg + CMSG_ALIGN(cmsg->cmsg_len)); - unsigned offset = (char*)next - (char*)msg->msg_control; - if (msg->msg_controllen < offset + sizeof(struct cmsghdr)) - return (struct cmsghdr*)0; - return next; -} - -static inline void* CMSG_DATA(struct cmsghdr* cmsg) -{ - return (void*)(cmsg + 1); -} - -struct sockaddr { - sa_family_t sa_family; - char sa_data[14]; -}; - -struct ucred { - pid_t pid; - uid_t uid; - gid_t gid; -}; - -struct linger { - int l_onoff; - int l_linger; -}; - -#define SOL_SOCKET 1 -#define SOMAXCONN 128 - -enum { - SO_RCVTIMEO, - SO_SNDTIMEO, - SO_TYPE, - SO_ERROR, - SO_PEERCRED, - SO_RCVBUF, - SO_SNDBUF, - SO_DEBUG, - SO_REUSEADDR, - SO_BINDTODEVICE, - SO_KEEPALIVE, - SO_TIMESTAMP, - SO_BROADCAST, - SO_LINGER, - SO_ACCEPTCONN, - SO_DONTROUTE, - SO_OOBINLINE, - SO_SNDLOWAT, - SO_RCVLOWAT, -}; -#define SO_RCVTIMEO SO_RCVTIMEO -#define SO_SNDTIMEO SO_SNDTIMEO -#define SO_TYPE SO_TYPE -#define SO_ERROR SO_ERROR -#define SO_PEERCRED SO_PEERCRED -#define SO_DEBUG SO_DEBUG -#define SO_REUSEADDR SO_REUSEADDR -#define SO_BINDTODEVICE SO_BINDTODEVICE -#define SO_KEEPALIVE SO_KEEPALIVE -#define SO_TIMESTAMP SO_TIMESTAMP -#define SO_BROADCAST SO_BROADCAST -#define SO_SNDBUF SO_SNDBUF -#define SO_RCVBUF SO_RCVBUF -#define SO_LINGER SO_LINGER -#define SO_ACCEPTCONN SO_ACCEPTCONN -#define SO_DONTROUTE SO_DONTROUTE -#define SO_OOBINLINE SO_OOBINLINE -#define SO_SNDLOWAT SO_SNDLOWAT -#define SO_RCVLOWAT SO_RCVLOWAT - -enum { - SCM_TIMESTAMP, - SCM_RIGHTS, -}; -#define SCM_TIMESTAMP SCM_TIMESTAMP -#define SCM_RIGHTS SCM_RIGHTS - -struct sockaddr_storage { - sa_family_t ss_family; - union { - char data[sizeof(struct sockaddr_un)]; - void* alignment; - }; -}; - -#ifdef __cplusplus -} -#endif diff --git a/Kernel/API/POSIX/sys/stat.h b/Kernel/API/POSIX/sys/stat.h deleted file mode 100644 index 93919991653..00000000000 --- a/Kernel/API/POSIX/sys/stat.h +++ /dev/null @@ -1,78 +0,0 @@ -/* - * Copyright (c) 2018-2021, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include - -#ifdef __cplusplus -extern "C" { -#endif - -#define S_IFMT 0170000 -#define S_IFDIR 0040000 -#define S_IFCHR 0020000 -#define S_IFBLK 0060000 -#define S_IFREG 0100000 -#define S_IFIFO 0010000 -#define S_IFLNK 0120000 -#define S_IFSOCK 0140000 - -#define S_ISUID 04000 -#define S_ISGID 02000 -#define S_ISVTX 01000 -#define S_IRUSR 0400 -#define S_IWUSR 0200 -#define S_IXUSR 0100 -#define S_IREAD S_IRUSR -#define S_IWRITE S_IWUSR -#define S_IEXEC S_IXUSR -#define S_IRGRP 0040 -#define S_IWGRP 0020 -#define S_IXGRP 0010 -#define S_IROTH 0004 -#define S_IWOTH 0002 -#define S_IXOTH 0001 - -#define S_IRWXU (S_IRUSR | S_IWUSR | S_IXUSR) - -#define S_IRWXG (S_IRWXU >> 3) -#define S_IRWXO (S_IRWXG >> 3) -#define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR) -#define S_ISCHR(m) (((m) & S_IFMT) == S_IFCHR) -#define S_ISBLK(m) (((m) & S_IFMT) == S_IFBLK) -#define S_ISREG(m) (((m) & S_IFMT) == S_IFREG) -#define S_ISFIFO(m) (((m) & S_IFMT) == S_IFIFO) -#define S_ISLNK(m) (((m) & S_IFMT) == S_IFLNK) -#define S_ISSOCK(m) (((m) & S_IFMT) == S_IFSOCK) - -struct stat { - dev_t st_dev; /* ID of device containing file */ - ino_t st_ino; /* inode number */ - mode_t st_mode; /* protection */ - nlink_t st_nlink; /* number of hard links */ - uid_t st_uid; /* user ID of owner */ - gid_t st_gid; /* group ID of owner */ - dev_t st_rdev; /* device ID (if special file) */ - off_t st_size; /* total size, in bytes */ - blksize_t st_blksize; /* block size for file system I/O */ - blkcnt_t st_blocks; /* number of 512-byte blocks allocated */ - struct timespec st_atim; /* time of last access */ - struct timespec st_mtim; /* time of last modification */ - struct timespec st_ctim; /* time of last status change */ -}; - -#define st_atime st_atim.tv_sec -#define st_mtime st_mtim.tv_sec -#define st_ctime st_ctim.tv_sec - -#define UTIME_OMIT -1 -#define UTIME_NOW -2 - -#ifdef __cplusplus -} -#endif diff --git a/Kernel/API/POSIX/sys/statvfs.h b/Kernel/API/POSIX/sys/statvfs.h deleted file mode 100644 index cb3ce6ddd87..00000000000 --- a/Kernel/API/POSIX/sys/statvfs.h +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright (c) 2018-2021, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include - -#ifdef __cplusplus -extern "C" { -#endif - -#define ST_RDONLY 0x1 -#define ST_NOSUID 0x2 - -#define FSTYPSZ 16 - -struct statvfs { - unsigned long f_bsize; - unsigned long f_frsize; - fsblkcnt_t f_blocks; - fsblkcnt_t f_bfree; - fsblkcnt_t f_bavail; - - fsfilcnt_t f_files; - fsfilcnt_t f_ffree; - fsfilcnt_t f_favail; - - unsigned long f_fsid; - unsigned long f_flag; - unsigned long f_namemax; - - char f_basetype[FSTYPSZ]; -}; - -#ifdef __cplusplus -} -#endif diff --git a/Kernel/API/POSIX/sys/time.h b/Kernel/API/POSIX/sys/time.h deleted file mode 100644 index bcb0366f7bc..00000000000 --- a/Kernel/API/POSIX/sys/time.h +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Copyright (c) 2018-2021, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include - -#ifdef __cplusplus -extern "C" { -#endif - -struct timeval { - time_t tv_sec; - suseconds_t tv_usec; -}; - -#ifdef __cplusplus -} -#endif diff --git a/Kernel/API/POSIX/sys/times.h b/Kernel/API/POSIX/sys/times.h deleted file mode 100644 index c208e9ed3b7..00000000000 --- a/Kernel/API/POSIX/sys/times.h +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright (c) 2018-2021, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include - -#ifdef __cplusplus -extern "C" { -#endif - -struct tms { - clock_t tms_utime; - clock_t tms_stime; - clock_t tms_cutime; - clock_t tms_cstime; -}; - -#ifdef __cplusplus -} -#endif diff --git a/Kernel/API/POSIX/sys/types.h b/Kernel/API/POSIX/sys/types.h deleted file mode 100644 index 1d2a43b0f61..00000000000 --- a/Kernel/API/POSIX/sys/types.h +++ /dev/null @@ -1,97 +0,0 @@ -/* - * Copyright (c) 2018-2021, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#ifndef KERNEL -# include -# include -#endif - -#ifdef __cplusplus -extern "C" { -#endif - -/* There is no __SSIZE_TYPE__ but we can trick the preprocessor into defining it for us anyway! */ -#define unsigned signed -typedef __SIZE_TYPE__ ssize_t; -#undef unsigned - -typedef unsigned char u_char; -typedef unsigned short u_short; -typedef unsigned int u_int; -typedef unsigned long u_long; - -typedef uint32_t uid_t; -typedef uint32_t gid_t; - -typedef int __pid_t; -#define pid_t __pid_t - -typedef char* caddr_t; - -typedef int id_t; - -typedef uint64_t ino_t; -typedef int64_t off_t; - -typedef uint64_t blkcnt_t; -typedef uint64_t blksize_t; -typedef uint64_t dev_t; -typedef uint16_t mode_t; -typedef uint64_t nlink_t; - -typedef int64_t time_t; -typedef uint32_t useconds_t; -typedef int64_t suseconds_t; -typedef uint64_t clock_t; - -typedef uint64_t fsblkcnt_t; -typedef uint64_t fsfilcnt_t; - -#define __socklen_t_defined -#define __socklen_t uint32_t -typedef __socklen_t socklen_t; - -struct utimbuf { - time_t actime; - time_t modtime; -}; - -typedef int pthread_t; -typedef int pthread_key_t; -typedef uint32_t pthread_once_t; - -typedef struct __pthread_mutex_t { - uint32_t lock; - pthread_t owner; - int level; - int type; -} pthread_mutex_t; - -typedef void* pthread_attr_t; -typedef struct __pthread_mutexattr_t { - int type; -} pthread_mutexattr_t; - -typedef struct __pthread_cond_t { - pthread_mutex_t* mutex; - uint32_t value; - int clockid; // clockid_t -} pthread_cond_t; - -typedef uint64_t pthread_rwlock_t; -typedef void* pthread_rwlockattr_t; -typedef struct __pthread_spinlock_t { - int m_lock; -} pthread_spinlock_t; -typedef struct __pthread_condattr_t { - int clockid; // clockid_t -} pthread_condattr_t; - -#ifdef __cplusplus -} -#endif diff --git a/Kernel/API/POSIX/sys/uio.h b/Kernel/API/POSIX/sys/uio.h deleted file mode 100644 index be8922ef4f7..00000000000 --- a/Kernel/API/POSIX/sys/uio.h +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright (c) 2018-2021, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include - -#ifdef __cplusplus -extern "C" { -#endif - -// Arbitrary pain threshold. -#define IOV_MAX 1024 - -struct iovec { - void* iov_base; - size_t iov_len; -}; - -#ifdef __cplusplus -} -#endif diff --git a/Kernel/API/POSIX/sys/un.h b/Kernel/API/POSIX/sys/un.h deleted file mode 100644 index a8aec6c2508..00000000000 --- a/Kernel/API/POSIX/sys/un.h +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright (c) 2018-2021, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include - -#ifdef __cplusplus -extern "C" { -#endif - -#define UNIX_PATH_MAX 108 - -struct sockaddr_un { - uint16_t sun_family; - char sun_path[UNIX_PATH_MAX]; -}; - -#ifdef __cplusplus -} -#endif diff --git a/Kernel/API/POSIX/sys/utsname.h b/Kernel/API/POSIX/sys/utsname.h deleted file mode 100644 index e270665be54..00000000000 --- a/Kernel/API/POSIX/sys/utsname.h +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright (c) 2018-2021, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include - -#ifdef __cplusplus -extern "C" { -#endif - -#define UTSNAME_ENTRY_LEN 65 - -struct utsname { - char sysname[UTSNAME_ENTRY_LEN]; - char nodename[UTSNAME_ENTRY_LEN]; - char release[UTSNAME_ENTRY_LEN]; - char version[UTSNAME_ENTRY_LEN]; - char machine[UTSNAME_ENTRY_LEN]; -}; - -#ifdef __cplusplus -} -#endif diff --git a/Kernel/API/POSIX/sys/wait.h b/Kernel/API/POSIX/sys/wait.h deleted file mode 100644 index f6b3eb2f7e4..00000000000 --- a/Kernel/API/POSIX/sys/wait.h +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright (c) 2018-2021, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include - -#ifdef __cplusplus -extern "C" { -#endif - -#define WEXITSTATUS(status) (((status) & 0xff00) >> 8) -#define WSTOPSIG(status) WEXITSTATUS(status) -#define WTERMSIG(status) ((status) & 0x7f) -#define WIFEXITED(status) (WTERMSIG(status) == 0) -#define WIFSTOPPED(status) (((status) & 0xff) == 0x7f) -#define WIFSIGNALED(status) (((char)(((status) & 0x7f) + 1) >> 1) > 0) -#define WIFCONTINUED(status) ((status) == 0xffff) - -#define WNOHANG 1 -#define WUNTRACED 2 -#define WSTOPPED WUNTRACED -#define WEXITED 4 -#define WCONTINUED 8 -#define WNOWAIT 0x1000000 - -typedef enum { - P_ALL = 1, - P_PID, - P_PGID -} idtype_t; - -#ifdef __cplusplus -} -#endif diff --git a/Kernel/API/POSIX/termios.h b/Kernel/API/POSIX/termios.h deleted file mode 100644 index 901f62e1d9b..00000000000 --- a/Kernel/API/POSIX/termios.h +++ /dev/null @@ -1,209 +0,0 @@ -/* - * Copyright (c) 2018-2021, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include - -#ifdef __cplusplus -extern "C" { -#endif - -#define NCCS 32 - -typedef uint32_t tcflag_t; -typedef uint8_t cc_t; -typedef uint32_t speed_t; - -struct termios { - tcflag_t c_iflag; - tcflag_t c_oflag; - tcflag_t c_cflag; - tcflag_t c_lflag; - cc_t c_cc[NCCS]; - speed_t c_ispeed; - speed_t c_ospeed; -}; - -/* c_cc characters */ -#define VINTR 0 -#define VQUIT 1 -#define VERASE 2 -#define VKILL 3 -#define VEOF 4 -#define VTIME 5 -#define VMIN 6 -#define VSWTC 7 -#define VSTART 8 -#define VSTOP 9 -#define VSUSP 10 -#define VEOL 11 -#define VREPRINT 12 -#define VDISCARD 13 -#define VWERASE 14 -#define VLNEXT 15 -#define VEOL2 16 -#define VINFO 17 - -/* c_iflag bits */ -#define IGNBRK 0000001 -#define BRKINT 0000002 -#define IGNPAR 0000004 -#define PARMRK 0000010 -#define INPCK 0000020 -#define ISTRIP 0000040 -#define INLCR 0000100 -#define IGNCR 0000200 -#define ICRNL 0000400 -#define IUCLC 0001000 -#define IXON 0002000 -#define IXANY 0004000 -#define IXOFF 0010000 -#define IMAXBEL 0020000 -#define IUTF8 0040000 - -/* c_oflag bits */ -#define OPOST 0000001 -#define OLCUC 0000002 -#define ONLCR 0000004 -#define OCRNL 0000010 -#define ONOCR 0000020 -#define ONLRET 0000040 -#define OFILL 0000100 -#define OFDEL 0000200 -#if defined __USE_MISC || defined __USE_XOPEN -# define NLDLY 0000400 -# define NL0 0000000 -# define NL1 0000400 -# define CRDLY 0003000 -# define CR0 0000000 -# define CR1 0001000 -# define CR2 0002000 -# define CR3 0003000 -# define TABDLY 0014000 -# define TAB0 0000000 -# define TAB1 0004000 -# define TAB2 0010000 -# define TAB3 0014000 -# define BSDLY 0020000 -# define BS0 0000000 -# define BS1 0020000 -# define FFDLY 0100000 -# define FF0 0000000 -# define FF1 0100000 -#endif - -#define VTDLY 0040000 -#define VT0 0000000 -#define VT1 0040000 - -#ifdef __USE_MISC -# define XTABS 0014000 -#endif - -/* c_cflag bit meaning */ -#ifdef __USE_MISC -# define CBAUD 0010017 -#endif -#define B0 0000000 /* hang up */ -#define B50 0000001 -#define B75 0000002 -#define B110 0000003 -#define B134 0000004 -#define B150 0000005 -#define B200 0000006 -#define B300 0000007 -#define B600 0000010 -#define B1200 0000011 -#define B1800 0000012 -#define B2400 0000013 -#define B4800 0000014 -#define B9600 0000015 -#define B19200 0000016 -#define B38400 0000017 -#ifdef __USE_MISC -# define EXTA B19200 -# define EXTB B38400 -#endif -#define CSIZE 0000060 -#define CS5 0000000 -#define CS6 0000020 -#define CS7 0000040 -#define CS8 0000060 -#define CSTOPB 0000100 -#define CREAD 0000200 -#define PARENB 0000400 -#define PARODD 0001000 -#define HUPCL 0002000 -#define CLOCAL 0004000 -#ifdef __USE_MISC -# define CBAUDEX 0010000 -#endif -#define B57600 0010001 -#define B115200 0010002 -#define B230400 0010003 -#define B460800 0010004 -#define B500000 0010005 -#define B576000 0010006 -#define B921600 0010007 -#define B1000000 0010010 -#define B1152000 0010011 -#define B1500000 0010012 -#define B2000000 0010013 -#define B2500000 0010014 -#define B3000000 0010015 -#define B3500000 0010016 -#define B4000000 0010017 -#define __MAX_BAUD B4000000 -#ifdef __USE_MISC -# define CIBAUD 002003600000 /* input baud rate (not used) */ -# define CMSPAR 010000000000 /* mark or space (stick) parity */ -# define CRTSCTS 020000000000 /* flow control */ -#endif - -/* c_lflag bits */ -#define ISIG 0000001 -#define ICANON 0000002 -#if defined __USE_MISC || (defined __USE_XOPEN && !defined __USE_XOPEN2K) -# define XCASE 0000004 -#endif -#define ECHO 0000010 -#define ECHOE 0000020 -#define ECHOK 0000040 -#define ECHONL 0000100 -#define NOFLSH 0000200 -#define TOSTOP 0000400 -#ifdef __USE_MISC -# define ECHOCTL 0001000 -# define ECHOPRT 0002000 -# define ECHOKE 0004000 -# define FLUSHO 0010000 -# define PENDIN 0040000 -#endif -#define IEXTEN 0100000 -#ifdef __USE_MISC -# define EXTPROC 0200000 -#endif - -/* tcflow() and TCXONC use these */ -#define TCOOFF 0 -#define TCOON 1 -#define TCIOFF 2 -#define TCION 3 - -/* tcflush() and TCFLSH use these */ -#define TCIFLUSH 0 -#define TCOFLUSH 1 -#define TCIOFLUSH 2 - -/* tcsetattr uses these */ -#define TCSANOW 0 -#define TCSADRAIN 1 -#define TCSAFLUSH 2 - -#ifdef __cplusplus -} -#endif diff --git a/Kernel/API/POSIX/time.h b/Kernel/API/POSIX/time.h deleted file mode 100644 index 19717a4a982..00000000000 --- a/Kernel/API/POSIX/time.h +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright (c) 2018-2021, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include - -#ifdef __cplusplus -extern "C" { -#endif - -#define CLOCKS_PER_SEC 1000 - -struct timespec { - time_t tv_sec; - long tv_nsec; -}; - -typedef int clockid_t; - -enum { - CLOCK_REALTIME, -#define CLOCK_REALTIME CLOCK_REALTIME - CLOCK_MONOTONIC, -#define CLOCK_MONOTONIC CLOCK_MONOTONIC - CLOCK_MONOTONIC_RAW, -#define CLOCK_MONOTONIC_RAW CLOCK_MONOTONIC_RAW - CLOCK_REALTIME_COARSE, -#define CLOCK_REALTIME_COARSE CLOCK_REALTIME_COARSE - CLOCK_MONOTONIC_COARSE, -#define CLOCK_MONOTONIC_COARSE CLOCK_MONOTONIC_COARSE - CLOCK_ID_COUNT, -}; - -#define TIMER_ABSTIME 99 - -#ifdef __cplusplus -} -#endif diff --git a/Kernel/API/POSIX/ucontext.h b/Kernel/API/POSIX/ucontext.h deleted file mode 100644 index e7c4cffd96e..00000000000 --- a/Kernel/API/POSIX/ucontext.h +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright (c) 2022, Ali Mohammad Pur - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include - -#ifdef __cplusplus -extern "C" { -#endif - -typedef struct __mcontext mcontext_t; - -typedef struct __ucontext { - struct __ucontext* uc_link; - sigset_t uc_sigmask; - stack_t uc_stack; - mcontext_t uc_mcontext; -} ucontext_t; - -#define ILL_ILLOPC 0 -#define ILL_ILLOPN 1 -#define ILL_ILLADR 2 -#define ILL_ILLTRP 3 -#define ILL_PRVOPC 4 -#define ILL_PRVREG 5 -#define ILL_COPROC 6 -#define ILL_BADSTK 7 - -#define FPE_INTDIV 0 -#define FPE_INTOVF 1 -#define FPE_FLTDIV 2 -#define FPE_FLTOVF 3 -#define FPE_FLTUND 4 -#define FPE_FLTRES 5 -#define FPE_FLTINV 6 - -#define SEGV_MAPERR 0 -#define SEGV_ACCERR 1 - -#define BUS_ADRALN 0 -#define BUS_ADRERR 1 -#define BUS_OBJERR 2 - -#define TRAP_BRKPT 0 -#define TRAP_TRACE 1 - -#define SI_USER 0x40000000 -#define SI_QUEUE 0x40000001 -#define SI_TIMER 0x40000002 -#define SI_ASYNCIO 0x40000003 -#define SI_MESGQ 0x40000004 -#define SI_NOINFO 0x40000042 - -#ifdef __cplusplus -} -#endif diff --git a/Kernel/API/POSIX/unistd.h b/Kernel/API/POSIX/unistd.h deleted file mode 100644 index a7d841522c8..00000000000 --- a/Kernel/API/POSIX/unistd.h +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright (c) 2018-2021, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include - -#ifdef __cplusplus -extern "C" { -#endif - -#define STDIN_FILENO 0 -#define STDOUT_FILENO 1 -#define STDERR_FILENO 2 - -#define R_OK 4 -#define W_OK 2 -#define X_OK 1 -#define F_OK 0 - -#define MS_NODEV (1 << 0) -#define MS_NOEXEC (1 << 1) -#define MS_NOSUID (1 << 2) -#define MS_BIND (1 << 3) -#define MS_RDONLY (1 << 4) -#define MS_REMOUNT (1 << 5) -#define MS_WXALLOWED (1 << 6) -#define MS_AXALLOWED (1 << 7) -#define MS_NOREGULAR (1 << 8) -#define MS_SRCHIDDEN (1 << 9) - -enum { - _SC_MONOTONIC_CLOCK, - _SC_NPROCESSORS_CONF, - _SC_NPROCESSORS_ONLN, - _SC_OPEN_MAX, - _SC_HOST_NAME_MAX, - _SC_TTY_NAME_MAX, - _SC_PAGESIZE, - _SC_GETPW_R_SIZE_MAX, - _SC_GETGR_R_SIZE_MAX, - _SC_CLK_TCK, - _SC_SYMLOOP_MAX, - _SC_MAPPED_FILES, - _SC_ARG_MAX, - _SC_IOV_MAX, - _SC_PHYS_PAGES, -}; - -#define _SC_MONOTONIC_CLOCK _SC_MONOTONIC_CLOCK -#define _SC_NPROCESSORS_CONF _SC_NPROCESSORS_CONF -#define _SC_NPROCESSORS_ONLN _SC_NPROCESSORS_ONLN -#define _SC_OPEN_MAX _SC_OPEN_MAX -#define _SC_PAGESIZE _SC_PAGESIZE -#define _SC_PAGE_SIZE _SC_PAGESIZE -#define _SC_HOST_NAME_MAX _SC_HOST_NAME_MAX -#define _SC_TTY_NAME_MAX _SC_TTY_NAME_MAX -#define _SC_GETPW_R_SIZE_MAX _SC_GETPW_R_SIZE_MAX -#define _SC_GETGR_R_SIZE_MAX _SC_GETGR_R_SIZE_MAX -#define _SC_CLK_TCK _SC_CLK_TCK -#define _SC_SYMLOOP_MAX _SC_SYMLOOP_MAX -#define _SC_MAPPED_FILES _SC_MAPPED_FILES -#define _SC_ARG_MAX _SC_ARG_MAX -#define _SC_IOV_MAX _SC_IOV_MAX -#define _SC_PHYS_PAGES _SC_PHYS_PAGES - -#ifdef __cplusplus -} -#endif diff --git a/Kernel/API/Syscall.h b/Kernel/API/Syscall.h deleted file mode 100644 index 171c7815302..00000000000 --- a/Kernel/API/Syscall.h +++ /dev/null @@ -1,726 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include - -#ifdef KERNEL -# include -# include -#endif - -constexpr int syscall_vector = 0x82; - -extern "C" { -struct pollfd; -struct timeval; -struct timespec; -struct sockaddr; -struct siginfo; -struct stat; -struct statvfs; -typedef u32 socklen_t; -} - -namespace Kernel { - -enum class NeedsBigProcessLock { - Yes, - No -}; - -// Declare all syscalls and associated metadata. -// -// NOTE: When declaring a new syscall or modifying an existing, please -// ensure that the proper assert is present at the top of the syscall -// implementation to both verify and document to any readers if the -// syscall acquires the big process lock or not. The asserts are: -// - VERIFY_PROCESS_BIG_LOCK_ACQUIRED(this) -// - VERIFY_NO_PROCESS_BIG_LOCK(this) -// -#define ENUMERATE_SYSCALLS(S) \ - S(accept4, NeedsBigProcessLock::No) \ - S(adjtime, NeedsBigProcessLock::No) \ - S(alarm, NeedsBigProcessLock::No) \ - S(archctl, NeedsBigProcessLock::No) \ - S(anon_create, NeedsBigProcessLock::No) \ - S(annotate_mapping, NeedsBigProcessLock::No) \ - S(bind, NeedsBigProcessLock::No) \ - S(bindmount, NeedsBigProcessLock::No) \ - S(chdir, NeedsBigProcessLock::No) \ - S(chmod, NeedsBigProcessLock::No) \ - S(chown, NeedsBigProcessLock::No) \ - S(clock_gettime, NeedsBigProcessLock::No) \ - S(clock_nanosleep, NeedsBigProcessLock::No) \ - S(clock_getres, NeedsBigProcessLock::No) \ - S(clock_settime, NeedsBigProcessLock::No) \ - S(close, NeedsBigProcessLock::No) \ - S(connect, NeedsBigProcessLock::No) \ - S(create_inode_watcher, NeedsBigProcessLock::No) \ - S(create_thread, NeedsBigProcessLock::No) \ - S(dbgputstr, NeedsBigProcessLock::No) \ - S(detach_thread, NeedsBigProcessLock::No) \ - S(disown, NeedsBigProcessLock::No) \ - S(dump_backtrace, NeedsBigProcessLock::No) \ - S(dup2, NeedsBigProcessLock::No) \ - S(emuctl, NeedsBigProcessLock::No) \ - S(execve, NeedsBigProcessLock::Yes) \ - S(exit, NeedsBigProcessLock::Yes) \ - S(exit_thread, NeedsBigProcessLock::Yes) \ - S(faccessat, NeedsBigProcessLock::No) \ - S(fchdir, NeedsBigProcessLock::No) \ - S(fchmod, NeedsBigProcessLock::No) \ - S(fchown, NeedsBigProcessLock::No) \ - S(fcntl, NeedsBigProcessLock::No) \ - S(fork, NeedsBigProcessLock::No) \ - S(fstat, NeedsBigProcessLock::No) \ - S(fstatvfs, NeedsBigProcessLock::No) \ - S(fsopen, NeedsBigProcessLock::No) \ - S(fsmount, NeedsBigProcessLock::No) \ - S(fsync, NeedsBigProcessLock::No) \ - S(ftruncate, NeedsBigProcessLock::No) \ - S(futex, NeedsBigProcessLock::Yes) \ - S(futimens, NeedsBigProcessLock::No) \ - S(get_dir_entries, NeedsBigProcessLock::No) \ - S(get_root_session_id, NeedsBigProcessLock::No) \ - S(get_stack_bounds, NeedsBigProcessLock::No) \ - S(getcwd, NeedsBigProcessLock::No) \ - S(getegid, NeedsBigProcessLock::No) \ - S(geteuid, NeedsBigProcessLock::No) \ - S(getgid, NeedsBigProcessLock::No) \ - S(getgroups, NeedsBigProcessLock::No) \ - S(gethostname, NeedsBigProcessLock::No) \ - S(getkeymap, NeedsBigProcessLock::No) \ - S(getpeername, NeedsBigProcessLock::No) \ - S(getpgid, NeedsBigProcessLock::No) \ - S(getpgrp, NeedsBigProcessLock::No) \ - S(getpid, NeedsBigProcessLock::No) \ - S(getppid, NeedsBigProcessLock::No) \ - S(getrandom, NeedsBigProcessLock::No) \ - S(getresgid, NeedsBigProcessLock::No) \ - S(getresuid, NeedsBigProcessLock::No) \ - S(getrusage, NeedsBigProcessLock::No) \ - S(getsid, NeedsBigProcessLock::No) \ - S(getsockname, NeedsBigProcessLock::No) \ - S(getsockopt, NeedsBigProcessLock::No) \ - S(gettid, NeedsBigProcessLock::No) \ - S(getuid, NeedsBigProcessLock::No) \ - S(inode_watcher_add_watch, NeedsBigProcessLock::No) \ - S(inode_watcher_remove_watch, NeedsBigProcessLock::No) \ - S(ioctl, NeedsBigProcessLock::No) \ - S(join_thread, NeedsBigProcessLock::No) \ - S(jail_create, NeedsBigProcessLock::No) \ - S(jail_attach, NeedsBigProcessLock::No) \ - S(kill, NeedsBigProcessLock::No) \ - S(kill_thread, NeedsBigProcessLock::No) \ - S(killpg, NeedsBigProcessLock::No) \ - S(link, NeedsBigProcessLock::No) \ - S(listen, NeedsBigProcessLock::No) \ - S(lseek, NeedsBigProcessLock::No) \ - S(madvise, NeedsBigProcessLock::No) \ - S(map_time_page, NeedsBigProcessLock::No) \ - S(mkdir, NeedsBigProcessLock::No) \ - S(mknod, NeedsBigProcessLock::No) \ - S(mmap, NeedsBigProcessLock::No) \ - S(mprotect, NeedsBigProcessLock::No) \ - S(mremap, NeedsBigProcessLock::No) \ - S(msync, NeedsBigProcessLock::No) \ - S(munmap, NeedsBigProcessLock::No) \ - S(open, NeedsBigProcessLock::No) \ - S(perf_event, NeedsBigProcessLock::Yes) \ - S(perf_register_string, NeedsBigProcessLock::Yes) \ - S(pipe, NeedsBigProcessLock::No) \ - S(pledge, NeedsBigProcessLock::No) \ - S(poll, NeedsBigProcessLock::No) \ - S(posix_fallocate, NeedsBigProcessLock::No) \ - S(prctl, NeedsBigProcessLock::No) \ - S(profiling_disable, NeedsBigProcessLock::Yes) \ - S(profiling_enable, NeedsBigProcessLock::Yes) \ - S(profiling_free_buffer, NeedsBigProcessLock::Yes) \ - S(ptrace, NeedsBigProcessLock::Yes) \ - S(purge, NeedsBigProcessLock::Yes) \ - S(read, NeedsBigProcessLock::Yes) \ - S(pread, NeedsBigProcessLock::Yes) \ - S(readlink, NeedsBigProcessLock::No) \ - S(readv, NeedsBigProcessLock::Yes) \ - S(realpath, NeedsBigProcessLock::No) \ - S(recvfd, NeedsBigProcessLock::No) \ - S(recvmsg, NeedsBigProcessLock::Yes) \ - S(rename, NeedsBigProcessLock::No) \ - S(remount, NeedsBigProcessLock::No) \ - S(rmdir, NeedsBigProcessLock::No) \ - S(scheduler_get_parameters, NeedsBigProcessLock::No) \ - S(scheduler_set_parameters, NeedsBigProcessLock::No) \ - S(sendfd, NeedsBigProcessLock::No) \ - S(sendmsg, NeedsBigProcessLock::Yes) \ - S(set_mmap_name, NeedsBigProcessLock::No) \ - S(setegid, NeedsBigProcessLock::No) \ - S(seteuid, NeedsBigProcessLock::No) \ - S(setgid, NeedsBigProcessLock::No) \ - S(setgroups, NeedsBigProcessLock::No) \ - S(sethostname, NeedsBigProcessLock::No) \ - S(setkeymap, NeedsBigProcessLock::No) \ - S(setpgid, NeedsBigProcessLock::No) \ - S(setregid, NeedsBigProcessLock::No) \ - S(setresgid, NeedsBigProcessLock::No) \ - S(setresuid, NeedsBigProcessLock::No) \ - S(setreuid, NeedsBigProcessLock::No) \ - S(setsid, NeedsBigProcessLock::No) \ - S(setsockopt, NeedsBigProcessLock::No) \ - S(setuid, NeedsBigProcessLock::No) \ - S(shutdown, NeedsBigProcessLock::No) \ - S(sigaction, NeedsBigProcessLock::Yes) \ - S(sigaltstack, NeedsBigProcessLock::Yes) \ - S(sigpending, NeedsBigProcessLock::No) \ - S(sigprocmask, NeedsBigProcessLock::No) \ - S(sigreturn, NeedsBigProcessLock::No) \ - S(sigsuspend, NeedsBigProcessLock::No) \ - S(sigtimedwait, NeedsBigProcessLock::No) \ - S(socket, NeedsBigProcessLock::No) \ - S(socketpair, NeedsBigProcessLock::No) \ - S(stat, NeedsBigProcessLock::No) \ - S(statvfs, NeedsBigProcessLock::No) \ - S(symlink, NeedsBigProcessLock::No) \ - S(sync, NeedsBigProcessLock::No) \ - S(sysconf, NeedsBigProcessLock::No) \ - S(times, NeedsBigProcessLock::No) \ - S(umask, NeedsBigProcessLock::No) \ - S(umount, NeedsBigProcessLock::No) \ - S(uname, NeedsBigProcessLock::No) \ - S(unlink, NeedsBigProcessLock::No) \ - S(unveil, NeedsBigProcessLock::No) \ - S(utime, NeedsBigProcessLock::No) \ - S(utimensat, NeedsBigProcessLock::No) \ - S(waitid, NeedsBigProcessLock::Yes) \ - S(write, NeedsBigProcessLock::Yes) \ - S(pwritev, NeedsBigProcessLock::Yes) \ - S(yield, NeedsBigProcessLock::No) - -namespace Syscall { - -#ifdef KERNEL -ErrorOr handle(RegisterState&, FlatPtr function, FlatPtr arg1, FlatPtr arg2, FlatPtr arg3, FlatPtr arg4); -#endif - -enum Function { -#undef __ENUMERATE_SYSCALL -#define __ENUMERATE_SYSCALL(sys_call, needs_lock) SC_##sys_call, - ENUMERATE_SYSCALLS(__ENUMERATE_SYSCALL) -#undef __ENUMERATE_SYSCALL - __Count -}; - -#ifdef AK_OS_SERENITY -struct StringArgument { - char const* characters; - size_t length { 0 }; -}; - -template -struct MutableBufferArgument { - DataType* data { nullptr }; - SizeType size { 0 }; -}; - -struct StringListArgument { - StringArgument* strings {}; - size_t length { 0 }; -}; - -struct SC_mmap_params { - void* addr; - size_t size; - size_t alignment; - int32_t prot; - int32_t flags; - int32_t fd; - int64_t offset; - StringArgument name; -}; - -struct SC_mremap_params { - void* old_address; - size_t old_size; - size_t new_size; - int32_t flags; -}; - -struct SC_open_params { - int dirfd; - StringArgument path; - int options; - u16 mode; -}; - -struct SC_poll_params { - struct pollfd* fds; - unsigned nfds; - const struct timespec* timeout; - u32 const* sigmask; -}; - -struct SC_clock_nanosleep_params { - int clock_id; - int flags; - const struct timespec* requested_sleep; - struct timespec* remaining_sleep; -}; - -struct SC_clock_getres_params { - int clock_id; - struct timespec* result; -}; - -struct SC_accept4_params { - sockaddr* addr; - socklen_t* addrlen; - int sockfd; - int flags; -}; - -struct SC_getsockopt_params { - int sockfd; - int level; - int option; - void* value; - socklen_t* value_size; -}; - -struct SC_setsockopt_params { - void const* value; - int sockfd; - int level; - int option; - socklen_t value_size; -}; - -struct SC_getsockname_params { - int sockfd; - sockaddr* addr; - socklen_t* addrlen; -}; - -struct SC_getpeername_params { - int sockfd; - sockaddr* addr; - socklen_t* addrlen; -}; - -struct SC_socketpair_params { - int domain; - int type; - int protocol; - int* sv; -}; - -struct SC_futex_params { - u32* userspace_address; - int futex_op; - u32 val; - union { - timespec const* timeout; - uintptr_t val2; - }; - u32* userspace_address2; - u32 val3; -}; - -struct SC_setkeymap_params { - u32 const* map; - u32 const* shift_map; - u32 const* alt_map; - u32 const* altgr_map; - u32 const* shift_altgr_map; - StringArgument map_name; -}; - -struct SC_jail_create_params { - u64 index; - StringArgument name; - int flags; -}; - -struct SC_jail_attach_params { - u64 index; -}; - -struct SC_getkeymap_params { - u32* map; - u32* shift_map; - u32* alt_map; - u32* altgr_map; - u32* shift_altgr_map; - MutableBufferArgument map_name; -}; - -struct SC_create_thread_params { - unsigned int detach_state = 0; // JOINABLE or DETACHED - int schedule_priority = 30; // THREAD_PRIORITY_NORMAL - // FIXME: Implement guard pages in create_thread (unreadable pages at "overflow" end of stack) - // "If an implementation rounds up the value of guardsize to a multiple of {PAGESIZE}, - // a call to pthread_attr_getguardsize() specifying attr shall store in the guardsize - // parameter the guard size specified by the previous pthread_attr_setguardsize() function call" - // ... ok, if you say so posix. Guess we get to lie to people about guard page size - unsigned int guard_page_size = 0; // Rounded up to PAGE_SIZE - unsigned int reported_guard_page_size = 0; // The lie we tell callers - unsigned int stack_size = 1 * MiB; // Equal to Thread::default_userspace_stack_size - void* stack_location; // nullptr means any, o.w. process virtual address - void* (*entry)(void*); - void* entry_argument; - void* tls_pointer; -}; - -struct SC_realpath_params { - StringArgument path; - MutableBufferArgument buffer; -}; - -struct SC_set_mmap_name_params { - void* addr; - size_t size; - StringArgument name; -}; - -struct SC_execve_params { - StringArgument path; - StringListArgument arguments; - StringListArgument environment; -}; - -struct SC_readlink_params { - StringArgument path; - MutableBufferArgument buffer; - int dirfd; -}; - -struct SC_link_params { - StringArgument old_path; - StringArgument new_path; -}; - -struct SC_chown_params { - StringArgument path; - u32 uid; - u32 gid; - int dirfd; - int follow_symlinks; -}; - -struct SC_mknod_params { - StringArgument path; - u16 mode; - dev_t dev; - int dirfd; -}; - -struct SC_symlink_params { - StringArgument target; - StringArgument linkpath; - int dirfd; -}; - -struct SC_rename_params { - int olddirfd; - StringArgument old_path; - int newdirfd; - StringArgument new_path; -}; - -struct SC_fsopen_params { - StringArgument fs_type; - int flags; -}; - -struct SC_fsmount_params { - int mount_fd; - StringArgument target; - int source_fd; -}; - -struct SC_bindmount_params { - StringArgument target; - int source_fd; - int flags; -}; - -struct SC_remount_params { - StringArgument target; - int flags; -}; - -struct SC_pledge_params { - StringArgument promises; - StringArgument execpromises; -}; - -struct SC_unveil_params { - int flags; - StringArgument path; - StringArgument permissions; -}; - -struct SC_utimensat_params { - int dirfd; - StringArgument path; - struct timespec const* times; - int flag; -}; - -struct SC_futimens_params { - int fd; - struct timespec const* times; -}; - -struct SC_waitid_params { - int idtype; - int id; - struct siginfo* infop; - int options; -}; - -struct SC_stat_params { - StringArgument path; - struct stat* statbuf; - int dirfd; - int follow_symlinks; -}; - -struct SC_ptrace_buf_params { - MutableBufferArgument buf; -}; - -struct SC_ptrace_params { - int request; - pid_t tid; - void* addr; - FlatPtr data; -}; - -struct SC_set_coredump_metadata_params { - StringArgument key; - StringArgument value; -}; - -struct SC_inode_watcher_add_watch_params { - StringArgument user_path; - int fd; - u32 event_mask; -}; - -struct SC_statvfs_params { - StringArgument path; - struct statvfs* buf; -}; - -struct SC_chmod_params { - int dirfd; - StringArgument path; - u16 mode; - int follow_symlinks; -}; - -enum class SchedulerParametersMode : bool { - Process, - Thread, -}; - -struct SC_scheduler_parameters_params { - pid_t pid_or_tid; - SchedulerParametersMode mode; - struct sched_param parameters; -}; - -struct SC_faccessat_params { - int dirfd; - StringArgument pathname; - int mode; - int flags; -}; - -void initialize(); -int sync(); - -# if ARCH(X86_64) || ARCH(AARCH64) || ARCH(RISCV64) -inline uintptr_t invoke(Function function) -{ -# if ARCH(X86_64) - uintptr_t result; - asm volatile("syscall" - : "=a"(result) - : "a"(function) - : "rcx", "r11", "memory"); -# elif ARCH(AARCH64) - uintptr_t result; - register uintptr_t x0 asm("x0"); - register uintptr_t x8 asm("x8") = function; - asm volatile("svc #0" - : "=r"(x0) - : "r"(x8) - : "memory"); - result = x0; -# elif ARCH(RISCV64) - register uintptr_t a7 asm("a7") = function; - register uintptr_t result asm("a0"); - asm volatile("ecall" - : "=r"(result) - : "r"(a7) - : "memory"); -# endif - return result; -} - -template -inline uintptr_t invoke(Function function, T1 arg1) -{ -# if ARCH(X86_64) - uintptr_t result; - asm volatile("syscall" - : "=a"(result) - : "a"(function), "d"((uintptr_t)arg1) - : "rcx", "r11", "memory"); -# elif ARCH(AARCH64) - uintptr_t result; - register uintptr_t x0 asm("x0"); - register uintptr_t x1 asm("x1") = arg1; - register uintptr_t x8 asm("x8") = function; - asm volatile("svc #0" - : "=r"(x0) - : "r"(x1), "r"(x8) - : "memory"); - result = x0; -# elif ARCH(RISCV64) - register uintptr_t a0 asm("a0") = arg1; - register uintptr_t a7 asm("a7") = function; - register uintptr_t result asm("a0"); - asm volatile("ecall" - : "=r"(result) - : "0"(a0), "r"(a7) - : "memory"); -# endif - return result; -} - -template -inline uintptr_t invoke(Function function, T1 arg1, T2 arg2) -{ -# if ARCH(X86_64) - uintptr_t result; - asm volatile("syscall" - : "=a"(result) - : "a"(function), "d"((uintptr_t)arg1), "D"((uintptr_t)arg2) - : "rcx", "r11", "memory"); -# elif ARCH(AARCH64) - uintptr_t result; - register uintptr_t x0 asm("x0"); - register uintptr_t x1 asm("x1") = arg1; - register uintptr_t x2 asm("x2") = arg2; - register uintptr_t x8 asm("x8") = function; - asm volatile("svc #0" - : "=r"(x0) - : "r"(x1), "r"(x2), "r"(x8) - : "memory"); - result = x0; -# elif ARCH(RISCV64) - register uintptr_t a0 asm("a0") = arg1; - register uintptr_t a1 asm("a1") = arg2; - register uintptr_t a7 asm("a7") = function; - register uintptr_t result asm("a0"); - asm volatile("ecall" - : "=r"(result) - : "0"(a0), "r"(a1), "r"(a7) - : "memory"); -# endif - return result; -} - -template -inline uintptr_t invoke(Function function, T1 arg1, T2 arg2, T3 arg3) -{ -# if ARCH(X86_64) - uintptr_t result; - asm volatile("syscall" - : "=a"(result) - : "a"(function), "d"((uintptr_t)arg1), "D"((uintptr_t)arg2), "b"((uintptr_t)arg3) - : "rcx", "r11", "memory"); -# elif ARCH(AARCH64) - uintptr_t result; - register uintptr_t x0 asm("x0"); - register uintptr_t x1 asm("x1") = arg1; - register uintptr_t x2 asm("x2") = arg2; - register uintptr_t x3 asm("x3") = arg3; - register uintptr_t x8 asm("x8") = function; - asm volatile("svc #0" - : "=r"(x0) - : "r"(x1), "r"(x2), "r"(x3), "r"(x8) - : "memory"); - result = x0; -# elif ARCH(RISCV64) - register uintptr_t a0 asm("a0") = arg1; - register uintptr_t a1 asm("a1") = arg2; - register uintptr_t a2 asm("a2") = arg3; - register uintptr_t a7 asm("a7") = function; - register uintptr_t result asm("a0"); - asm volatile("ecall" - : "=r"(result) - : "0"(a0), "r"(a1), "r"(a2), "r"(a7) - : "memory"); -# endif - return result; -} - -template -inline uintptr_t invoke(Function function, T1 arg1, T2 arg2, T3 arg3, T4 arg4) -{ -# if ARCH(X86_64) - uintptr_t result; - asm volatile("syscall" - : "=a"(result) - : "a"(function), "d"((uintptr_t)arg1), "D"((uintptr_t)arg2), "b"((uintptr_t)arg3), "S"((uintptr_t)arg4) - : "memory"); -# elif ARCH(AARCH64) - uintptr_t result; - register uintptr_t x0 asm("x0"); - register uintptr_t x1 asm("x1") = arg1; - register uintptr_t x2 asm("x2") = arg2; - register uintptr_t x3 asm("x3") = arg3; - register uintptr_t x4 asm("x4") = arg4; - register uintptr_t x8 asm("x8") = function; - asm volatile("svc #0" - : "=r"(x0) - : "r"(x1), "r"(x2), "r"(x3), "r"(x4), "r"(x8) - : "memory"); - result = x0; -# elif ARCH(RISCV64) - register uintptr_t a0 asm("a0") = arg1; - register uintptr_t a1 asm("a1") = arg2; - register uintptr_t a2 asm("a2") = arg3; - register uintptr_t a3 asm("a3") = arg4; - register uintptr_t a7 asm("a7") = function; - register uintptr_t result asm("a0"); - asm volatile("ecall" - : "=r"(result) - : "0"(a0), "r"(a1), "r"(a2), "r"(a3), "r"(a7) - : "memory"); -# endif - return result; -} -# endif -#endif - -} - -#undef __ENUMERATE_SYSCALL -#define __ENUMERATE_SYSCALL(sys_call, needs_lock) using Syscall::SC_##sys_call; -ENUMERATE_SYSCALLS(__ENUMERATE_SYSCALL) -#undef __ENUMERATE_SYSCALL - -} - -using namespace Kernel; diff --git a/Kernel/API/SyscallString.h b/Kernel/API/SyscallString.h deleted file mode 100644 index 6b9df75e78d..00000000000 --- a/Kernel/API/SyscallString.h +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright (c) 2018-2022, Andreas Kling - * Copyright (c) 2022, Patrick Meyer - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include - -namespace Kernel::Syscall { - -// Separate header so syscall.h doesn't depend on malloc. -// https://github.com/SerenityOS/serenity/issues/13869 -constexpr StringView to_string(Function function) -{ - switch (function) { -#undef __ENUMERATE_SYSCALL -#define __ENUMERATE_SYSCALL(sys_call, needs_lock) \ - case SC_##sys_call: \ - return #sys_call##sv; - ENUMERATE_SYSCALLS(__ENUMERATE_SYSCALL) -#undef __ENUMERATE_SYSCALL - default: - break; - } - return "Unknown"sv; -} - -} diff --git a/Kernel/API/TimePage.h b/Kernel/API/TimePage.h deleted file mode 100644 index 4092b347bfb..00000000000 --- a/Kernel/API/TimePage.h +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright (c) 2021, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include - -#ifdef KERNEL -# include -#else -# include -#endif - -namespace Kernel { - -inline bool time_page_supports(clockid_t clock_id) -{ - return clock_id == CLOCK_REALTIME_COARSE || clock_id == CLOCK_MONOTONIC_COARSE; -} - -struct TimePage { - u32 volatile update1; - struct timespec clocks[CLOCK_ID_COUNT]; - u32 volatile update2; -}; - -} diff --git a/Kernel/API/Unveil.h b/Kernel/API/Unveil.h deleted file mode 100644 index 7d116f8e416..00000000000 --- a/Kernel/API/Unveil.h +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Copyright (c) 2022, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include - -namespace Kernel { - -enum class UnveilFlags : u32 { - None = 0, - CurrentProgram = 1 << 0, - AfterExec = 1 << 1, -}; - -AK_ENUM_BITWISE_OPERATORS(UnveilFlags); - -} diff --git a/Kernel/API/VirGL.h b/Kernel/API/VirGL.h deleted file mode 100644 index 2d7a848459d..00000000000 --- a/Kernel/API/VirGL.h +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright (c) 2022, Sahan Fernando - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include - -struct VirGL3DResourceSpec { - u32 target; - u32 format; - u32 bind; - u32 width; - u32 height; - u32 depth; - u32 array_size; - u32 last_level; - u32 nr_samples; - u32 flags; - u32 created_resource_id; -}; - -struct VirGLCommandBuffer { - u32 const* data; - u32 num_elems; -}; - -#define VIRGL_DATA_DIR_GUEST_TO_HOST 1 -#define VIRGL_DATA_DIR_HOST_TO_GUEST 2 - -struct VirGLTransferDescriptor { - void* data; - size_t offset_in_region; - size_t num_bytes; - int direction; -}; diff --git a/Kernel/API/VirtualMemoryAnnotations.h b/Kernel/API/VirtualMemoryAnnotations.h deleted file mode 100644 index bed3ffe113d..00000000000 --- a/Kernel/API/VirtualMemoryAnnotations.h +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Copyright (c) 2022, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include - -namespace Kernel { - -enum class VirtualMemoryRangeFlags : u32 { - None = 0, - SyscallCode = 1 << 0, - Immutable = 1 << 1, -}; - -AK_ENUM_BITWISE_OPERATORS(VirtualMemoryRangeFlags); - -} diff --git a/Kernel/API/archctl_numbers.h b/Kernel/API/archctl_numbers.h deleted file mode 100644 index cb341b45de6..00000000000 --- a/Kernel/API/archctl_numbers.h +++ /dev/null @@ -1,9 +0,0 @@ -/* - * Copyright (c) 2024, Sönke Holz - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#define ARCHCTL_X86_64_SET_FS_BASE_FOR_CURRENT_THREAD 1 diff --git a/Kernel/API/kcov.h b/Kernel/API/kcov.h deleted file mode 100644 index 2957a44569e..00000000000 --- a/Kernel/API/kcov.h +++ /dev/null @@ -1,12 +0,0 @@ -/* - * Copyright (c) 2021, Patrick Meyer - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include - -typedef u64 volatile kcov_pc_t; -#define KCOV_ENTRY_SIZE sizeof(kcov_pc_t) diff --git a/Kernel/API/prctl_numbers.h b/Kernel/API/prctl_numbers.h deleted file mode 100644 index b9641671198..00000000000 --- a/Kernel/API/prctl_numbers.h +++ /dev/null @@ -1,18 +0,0 @@ -/* - * Copyright (c) 2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#define PR_SET_DUMPABLE 1 -#define PR_GET_DUMPABLE 2 -#define PR_SET_NO_NEW_SYSCALL_REGION_ANNOTATIONS 3 -#define PR_GET_NO_NEW_SYSCALL_REGION_ANNOTATIONS 4 -#define PR_SET_COREDUMP_METADATA_VALUE 5 -#define PR_SET_PROCESS_NAME 6 -#define PR_GET_PROCESS_NAME 7 -#define PR_SET_THREAD_NAME 8 -#define PR_GET_THREAD_NAME 9 -#define PR_SET_NO_TRANSITION_TO_EXECUTABLE_FROM_WRITABLE_PROT 10 diff --git a/Kernel/API/ttydefaults.h b/Kernel/API/ttydefaults.h deleted file mode 100644 index cf02a574801..00000000000 --- a/Kernel/API/ttydefaults.h +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright (c) 2021, Daniel Bertalan - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#define TTYDEF_IFLAG (ICRNL) -#define TTYDEF_OFLAG (OPOST | ONLCR) -#define TTYDEF_LFLAG_NOECHO (ISIG | ICANON) -#define TTYDEF_LFLAG_ECHO (TTYDEF_LFLAG_NOECHO | ECHO | ECHOE | ECHOK | ECHONL) -#define TTYDEF_LFLAG TTYDEF_LFLAG_ECHO -#define TTYDEF_CFLAG (CS8) -#define TTYDEF_SPEED (B9600) - -#define CTRL(c) (c & 0x1F) -#define CINTR CTRL('c') -#define CQUIT 034 -#define CERASE 010 -#define CKILL CTRL('u') -#define CEOF CTRL('d') -#define CTIME 0 -#define CMIN 1 -#define CSWTC 0 -#define CSTART CTRL('q') -#define CSTOP CTRL('s') -#define CSUSP CTRL('z') -#define CEOL 0 -#define CREPRINT CTRL('r') -#define CDISCARD CTRL('o') -#define CWERASE CTRL('w') -#define CLNEXT CTRL('v') -#define CEOL2 CEOL - -#define CEOT CEOF -#define CBRK CEOL -#define CRPRNT CREPRINT -#define CFLUSH CDISCARD diff --git a/Kernel/API/ttydefaultschars.h b/Kernel/API/ttydefaultschars.h deleted file mode 100644 index 3b48f9a8a1f..00000000000 --- a/Kernel/API/ttydefaultschars.h +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright (c) 2021, Daniel Bertalan - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include - -#ifdef __clang__ -# pragma clang diagnostic push -# pragma clang diagnostic ignored "-Wc99-designator" -#endif - -#ifdef __cplusplus -extern "C" { -#endif - -static cc_t const ttydefchars[NCCS] = { - [VINTR] = CINTR, - [VQUIT] = CQUIT, - [VERASE] = CERASE, - [VKILL] = CKILL, - [VEOF] = CEOF, - [VTIME] = CTIME, - [VMIN] = CMIN, - [VSWTC] = CSWTC, - [VSTART] = CSTART, - [VSTOP] = CSTOP, - [VSUSP] = CSUSP, - [VEOL] = CEOL, - [VREPRINT] = CREPRINT, - [VDISCARD] = CDISCARD, - [VWERASE] = CWERASE, - [VLNEXT] = CLNEXT, - [VEOL2] = CEOL2 -}; - -#ifdef __clang__ -# pragma clang diagnostic pop -#endif - -#ifdef __cplusplus -} -#endif diff --git a/Kernel/Arch/ArchSpecificThreadData.h b/Kernel/Arch/ArchSpecificThreadData.h deleted file mode 100644 index 3a03a17b00c..00000000000 --- a/Kernel/Arch/ArchSpecificThreadData.h +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Copyright (c) 2024, Sönke Holz - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include - -#if ARCH(X86_64) -# include -#elif ARCH(AARCH64) -# include -#elif ARCH(RISCV64) -# include -#else -# error Unknown architecture -#endif diff --git a/Kernel/Arch/CPU.h b/Kernel/Arch/CPU.h deleted file mode 100644 index f8fce98148a..00000000000 --- a/Kernel/Arch/CPU.h +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright (c) 2018-2022, James Mintram - * Copyright (c) 2018-2021, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include - -#define PAGE_MASK (~(FlatPtr)0xfffu) - -#define LSW(x) ((u32)(x) & 0xFFFF) -#define MSW(x) (((u32)(x) >> 16) & 0xFFFF) -#define LSB(x) ((x) & 0xFF) -#define MSB(x) (((x) >> 8) & 0xFF) - -#if ARCH(X86_64) -# include -#elif ARCH(AARCH64) -# include -#elif ARCH(RISCV64) -# include -#else -# error "Unknown architecture" -#endif - -namespace Kernel { - -struct RegisterState; - -void dump_registers(RegisterState const& regs); -void handle_crash(RegisterState const&, char const* description, int signal, bool out_of_memory = false); - -} diff --git a/Kernel/Arch/CPUID.h b/Kernel/Arch/CPUID.h deleted file mode 100644 index d8d6fad5d60..00000000000 --- a/Kernel/Arch/CPUID.h +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Copyright (c) 2023, kleines Filmröllchen - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include - -#if ARCH(X86_64) -# include -#elif ARCH(AARCH64) -# include -#elif ARCH(RISCV64) -# include -#else -# error "Unknown architecture" -#endif diff --git a/Kernel/Arch/CurrentTime.h b/Kernel/Arch/CurrentTime.h deleted file mode 100644 index b5ca112ebea..00000000000 --- a/Kernel/Arch/CurrentTime.h +++ /dev/null @@ -1,17 +0,0 @@ -/* - * Copyright (c) 2022, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include - -namespace Kernel { - -typedef u64 (*fptr)(); - -fptr optional_current_time(); - -} diff --git a/Kernel/Arch/DebugOutput.h b/Kernel/Arch/DebugOutput.h deleted file mode 100644 index 6f774463438..00000000000 --- a/Kernel/Arch/DebugOutput.h +++ /dev/null @@ -1,13 +0,0 @@ -/* - * Copyright (c) 2022, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -namespace Kernel { - -void debug_output(char ch); - -} diff --git a/Kernel/Arch/DeferredCallEntry.h b/Kernel/Arch/DeferredCallEntry.h deleted file mode 100644 index 7c14bf655e1..00000000000 --- a/Kernel/Arch/DeferredCallEntry.h +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright (c) 2020, Tom - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include - -namespace Kernel { - -struct DeferredCallEntry { - using HandlerFunction = Function; - - DeferredCallEntry* next; - alignas(HandlerFunction) u8 handler_storage[sizeof(HandlerFunction)]; - bool was_allocated; - - HandlerFunction& handler_value() - { - return *bit_cast(&handler_storage); - } - - void invoke_handler() - { - handler_value()(); - } -}; - -} diff --git a/Kernel/Arch/DeferredCallPool.cpp b/Kernel/Arch/DeferredCallPool.cpp deleted file mode 100644 index 7e2b8efd683..00000000000 --- a/Kernel/Arch/DeferredCallPool.cpp +++ /dev/null @@ -1,93 +0,0 @@ -/* - * Copyright (c) 2020, Tom - * Copyright (c) 2023, Timon Kruiper - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include - -namespace Kernel { - -void DeferredCallPool::init() -{ - size_t pool_count = sizeof(m_deferred_call_pool) / sizeof(m_deferred_call_pool[0]); - for (size_t i = 0; i < pool_count; i++) { - auto& entry = m_deferred_call_pool[i]; - entry.next = i < pool_count - 1 ? &m_deferred_call_pool[i + 1] : nullptr; - new (entry.handler_storage) DeferredCallEntry::HandlerFunction; - entry.was_allocated = false; - } - m_pending_deferred_calls = nullptr; - m_free_deferred_call_pool_entry = &m_deferred_call_pool[0]; -} - -void DeferredCallPool::return_to_pool(DeferredCallEntry* entry) -{ - VERIFY(!entry->was_allocated); - - entry->handler_value() = {}; - - entry->next = m_free_deferred_call_pool_entry; - m_free_deferred_call_pool_entry = entry; -} - -DeferredCallEntry* DeferredCallPool::get_free() -{ - if (m_free_deferred_call_pool_entry) { - // Fast path, we have an entry in our pool - auto* entry = m_free_deferred_call_pool_entry; - m_free_deferred_call_pool_entry = entry->next; - VERIFY(!entry->was_allocated); - return entry; - } - - auto* entry = new DeferredCallEntry; - new (entry->handler_storage) DeferredCallEntry::HandlerFunction; - entry->was_allocated = true; - return entry; -} - -void DeferredCallPool::execute_pending() -{ - if (!m_pending_deferred_calls) - return; - auto* pending_list = m_pending_deferred_calls; - m_pending_deferred_calls = nullptr; - - // We pulled the stack of pending deferred calls in LIFO order, so we need to reverse the list first - auto reverse_list = [](DeferredCallEntry* list) -> DeferredCallEntry* { - DeferredCallEntry* rev_list = nullptr; - while (list) { - auto next = list->next; - list->next = rev_list; - rev_list = list; - list = next; - } - return rev_list; - }; - pending_list = reverse_list(pending_list); - - do { - pending_list->invoke_handler(); - - // Return the entry back to the pool, or free it - auto* next = pending_list->next; - if (pending_list->was_allocated) { - pending_list->handler_value().~Function(); - delete pending_list; - } else - return_to_pool(pending_list); - pending_list = next; - } while (pending_list); -} - -void DeferredCallPool::queue_entry(DeferredCallEntry* entry) -{ - entry->next = m_pending_deferred_calls; - m_pending_deferred_calls = entry; -} - -} diff --git a/Kernel/Arch/DeferredCallPool.h b/Kernel/Arch/DeferredCallPool.h deleted file mode 100644 index d7d6886c702..00000000000 --- a/Kernel/Arch/DeferredCallPool.h +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright (c) 2020, Tom - * Copyright (c) 2023, Timon Kruiper - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include - -namespace Kernel { - -class DeferredCallPool { -public: - void init(); - void execute_pending(); - DeferredCallEntry* get_free(); - void return_to_pool(DeferredCallEntry*); - void queue_entry(DeferredCallEntry*); - -private: - DeferredCallEntry* m_pending_deferred_calls; // in reverse order - DeferredCallEntry* m_free_deferred_call_pool_entry; - DeferredCallEntry m_deferred_call_pool[5]; -}; - -} diff --git a/Kernel/Arch/Delay.h b/Kernel/Arch/Delay.h deleted file mode 100644 index 5b9f0be5513..00000000000 --- a/Kernel/Arch/Delay.h +++ /dev/null @@ -1,15 +0,0 @@ -/* - * Copyright (c) 2022, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include - -namespace Kernel { - -void microseconds_delay(u32 microseconds); - -} diff --git a/Kernel/Arch/FPUState.h b/Kernel/Arch/FPUState.h deleted file mode 100644 index 8ec757e80ca..00000000000 --- a/Kernel/Arch/FPUState.h +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright (c) 2023, kleines Filmröllchen - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include - -namespace Kernel { -struct FPUState; -} - -#if ARCH(X86_64) -# include -#elif ARCH(AARCH64) -# include -#elif ARCH(RISCV64) -# include -#else -# error "Unknown architecture" -#endif diff --git a/Kernel/Arch/IRQController.h b/Kernel/Arch/IRQController.h deleted file mode 100644 index a93414b5287..00000000000 --- a/Kernel/Arch/IRQController.h +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Copyright (c) 2022, Timon Kruiper - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include - -#if ARCH(X86_64) -# include -#elif ARCH(AARCH64) -# include -#elif ARCH(RISCV64) -# include -#else -# error "Unknown architecture" -#endif diff --git a/Kernel/Arch/InterruptManagement.h b/Kernel/Arch/InterruptManagement.h deleted file mode 100644 index 2cb188a4ebe..00000000000 --- a/Kernel/Arch/InterruptManagement.h +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Copyright (c) 2022, Timon Kruiper - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include - -#if ARCH(X86_64) -# include -#elif ARCH(AARCH64) -# include -#elif ARCH(RISCV64) -# include -#else -# error "Unknown architecture" -#endif diff --git a/Kernel/Arch/Interrupts.h b/Kernel/Arch/Interrupts.h deleted file mode 100644 index 6e0d2808237..00000000000 --- a/Kernel/Arch/Interrupts.h +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright (c) 2022, Timon Kruiper - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include - -#if ARCH(X86_64) -# include -#endif - -namespace Kernel { - -class GenericInterruptHandler; - -GenericInterruptHandler& get_interrupt_handler(u8 interrupt_number); -void register_generic_interrupt_handler(u8 number, GenericInterruptHandler&); -void unregister_generic_interrupt_handler(u8 number, GenericInterruptHandler&); -ErrorOr reserve_interrupt_handlers(u8 number_of_irqs); - -void initialize_interrupts(); - -} diff --git a/Kernel/Arch/PCIMSI.h b/Kernel/Arch/PCIMSI.h deleted file mode 100644 index 0e8738a5945..00000000000 --- a/Kernel/Arch/PCIMSI.h +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright (c) 2023, Pankaj R - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include - -namespace Kernel { -#if ARCH(X86_64) -u64 msi_address_register(u8 destination_id, bool redirection_hint, bool destination_mode); -u32 msi_data_register(u8 vector, bool level_trigger, bool assert); -u32 msix_vector_control_register(u32 vector_control, bool mask); -void msi_signal_eoi(); -#elif ARCH(AARCH64) || ARCH(RISCV64) -[[maybe_unused]] static u64 msi_address_register([[maybe_unused]] u8 destination_id, [[maybe_unused]] bool redirection_hint, [[maybe_unused]] bool destination_mode) -{ - TODO_AARCH64(); - return 0; -} - -[[maybe_unused]] static u32 msi_data_register([[maybe_unused]] u8 vector, [[maybe_unused]] bool level_trigger, [[maybe_unused]] bool assert) -{ - TODO_AARCH64(); - return 0; -} - -[[maybe_unused]] static u32 msix_vector_control_register([[maybe_unused]] u32 vector_control, [[maybe_unused]] bool mask) -{ - TODO_AARCH64(); - return 0; -} - -[[maybe_unused]] static void msi_signal_eoi() -{ - TODO_AARCH64(); - return; -} -#endif -} diff --git a/Kernel/Arch/PageDirectory.h b/Kernel/Arch/PageDirectory.h deleted file mode 100644 index c42c023e1bf..00000000000 --- a/Kernel/Arch/PageDirectory.h +++ /dev/null @@ -1,20 +0,0 @@ -/* - * Copyright (c) 2018-2021, Andreas Kling - * Copyright (c) 2022, Timon Kruiper - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include - -#if ARCH(X86_64) -# include -#elif ARCH(AARCH64) -# include -#elif ARCH(RISCV64) -# include -#else -# error "Unknown architecture" -#endif diff --git a/Kernel/Arch/PageFault.cpp b/Kernel/Arch/PageFault.cpp deleted file mode 100644 index 50ea1a56509..00000000000 --- a/Kernel/Arch/PageFault.cpp +++ /dev/null @@ -1,144 +0,0 @@ -/* - * Copyright (c) 2018-2021, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include -#include -#include - -namespace Kernel { - -void PageFault::handle(RegisterState& regs) -{ - auto fault_address = m_vaddr.get(); - bool faulted_in_kernel = regs.previous_mode() == ExecutionMode::Kernel; - - if (faulted_in_kernel && Processor::current_in_irq()) { - // If we're faulting in an IRQ handler, first check if we failed - // due to safe_memcpy, safe_strnlen, or safe_memset. If we did, - // gracefully continue immediately. Because we're in an IRQ handler - // we can't really try to resolve the page fault in a meaningful - // way, so we need to do this before calling into - // MemoryManager::handle_page_fault, which would just bail and - // request a crash - if (handle_safe_access_fault(regs, fault_address)) - return; - } - - auto current_thread = Thread::current(); - - if (current_thread) { - current_thread->set_handling_page_fault(true); - PerformanceManager::add_page_fault_event(*current_thread, regs); - } - - ScopeGuard guard = [current_thread] { - if (current_thread) - current_thread->set_handling_page_fault(false); - }; - - if (!faulted_in_kernel) { - VirtualAddress userspace_sp = VirtualAddress { regs.userspace_sp() }; - bool has_valid_stack_pointer = current_thread->process().address_space().with([&](auto& space) { - return MM.validate_user_stack(*space, userspace_sp); - }); - if (!has_valid_stack_pointer) { - dbgln("Invalid stack pointer: {}", userspace_sp); - return handle_crash(regs, "Bad stack on page fault", SIGSEGV); - } - } - - auto response = MM.handle_page_fault(*this); - - if (response == PageFaultResponse::ShouldCrash || response == PageFaultResponse::OutOfMemory || response == PageFaultResponse::BusError) { - if (faulted_in_kernel && handle_safe_access_fault(regs, fault_address)) { - // If this would be a ring0 (kernel) fault and the fault was triggered by - // safe_memcpy, safe_strnlen, or safe_memset then we resume execution at - // the appropriate _fault label rather than crashing - return; - } - - if (response == PageFaultResponse::BusError && current_thread->has_signal_handler(SIGBUS)) { - current_thread->send_urgent_signal_to_self(SIGBUS); - return; - } - - if (response != PageFaultResponse::OutOfMemory && current_thread) { - if (current_thread->has_signal_handler(SIGSEGV)) { - current_thread->send_urgent_signal_to_self(SIGSEGV); - return; - } - } - - dbgln("Unrecoverable page fault, {}{}{} address {}", - is_reserved_bit_violation() ? "reserved bit violation / " : "", - is_instruction_fetch() ? "instruction fetch / " : "", - is_write() ? "write to" : "read from", - VirtualAddress(fault_address)); - constexpr FlatPtr kmalloc_scrub_pattern = explode_byte(KMALLOC_SCRUB_BYTE); - constexpr FlatPtr kfree_scrub_pattern = explode_byte(KFREE_SCRUB_BYTE); - if (response == PageFaultResponse::BusError) { - dbgln("Note: Address {} is an access to an undefined memory range of an Inode-backed VMObject", VirtualAddress(fault_address)); - } else if ((fault_address & 0xffff0000) == (kmalloc_scrub_pattern & 0xffff0000)) { - dbgln("Note: Address {} looks like it may be uninitialized kmalloc() memory", VirtualAddress(fault_address)); - } else if ((fault_address & 0xffff0000) == (kfree_scrub_pattern & 0xffff0000)) { - dbgln("Note: Address {} looks like it may be recently kfree()'d memory", VirtualAddress(fault_address)); - } else if (fault_address < 4096) { - dbgln("Note: Address {} looks like a possible nullptr dereference", VirtualAddress(fault_address)); - } else if constexpr (SANITIZE_PTRS) { - constexpr FlatPtr refptr_scrub_pattern = explode_byte(REFPTR_SCRUB_BYTE); - constexpr FlatPtr nonnullrefptr_scrub_pattern = explode_byte(NONNULLREFPTR_SCRUB_BYTE); - constexpr FlatPtr ownptr_scrub_pattern = explode_byte(OWNPTR_SCRUB_BYTE); - constexpr FlatPtr nonnullownptr_scrub_pattern = explode_byte(NONNULLOWNPTR_SCRUB_BYTE); - constexpr FlatPtr lockrefptr_scrub_pattern = explode_byte(LOCKREFPTR_SCRUB_BYTE); - constexpr FlatPtr nonnulllockrefptr_scrub_pattern = explode_byte(NONNULLLOCKREFPTR_SCRUB_BYTE); - - if ((fault_address & 0xffff0000) == (refptr_scrub_pattern & 0xffff0000)) { - dbgln("Note: Address {} looks like it may be a recently destroyed LockRefPtr", VirtualAddress(fault_address)); - } else if ((fault_address & 0xffff0000) == (nonnullrefptr_scrub_pattern & 0xffff0000)) { - dbgln("Note: Address {} looks like it may be a recently destroyed NonnullLockRefPtr", VirtualAddress(fault_address)); - } else if ((fault_address & 0xffff0000) == (ownptr_scrub_pattern & 0xffff0000)) { - dbgln("Note: Address {} looks like it may be a recently destroyed OwnPtr", VirtualAddress(fault_address)); - } else if ((fault_address & 0xffff0000) == (nonnullownptr_scrub_pattern & 0xffff0000)) { - dbgln("Note: Address {} looks like it may be a recently destroyed NonnullOwnPtr", VirtualAddress(fault_address)); - } else if ((fault_address & 0xffff0000) == (lockrefptr_scrub_pattern & 0xffff0000)) { - dbgln("Note: Address {} looks like it may be a recently destroyed LockRefPtr", VirtualAddress(fault_address)); - } else if ((fault_address & 0xffff0000) == (nonnulllockrefptr_scrub_pattern & 0xffff0000)) { - dbgln("Note: Address {} looks like it may be a recently destroyed NonnullLockRefPtr", VirtualAddress(fault_address)); - } - } - - if (current_thread) { - auto& current_process = current_thread->process(); - if (current_process.is_user_process()) { - auto fault_address_string = KString::formatted("{:p}", fault_address); - auto fault_address_view = fault_address_string.is_error() ? ""sv : fault_address_string.value()->view(); - (void)current_process.try_set_coredump_property("fault_address"sv, fault_address_view); - if (type() != PageFault::Type::Unknown) - (void)current_process.try_set_coredump_property("fault_type"sv, type() == PageFault::Type::PageNotPresent ? "NotPresent"sv : "ProtectionViolation"sv); - StringView fault_access; - if (is_instruction_fetch()) - fault_access = "Execute"sv; - else - fault_access = access() == PageFault::Access::Read ? "Read"sv : "Write"sv; - (void)current_process.try_set_coredump_property("fault_access"sv, fault_access); - } - } - - if (response == PageFaultResponse::BusError) - return handle_crash(regs, "Page Fault (Bus Error)", SIGBUS, false); - return handle_crash(regs, "Page Fault", SIGSEGV, response == PageFaultResponse::OutOfMemory); - } else if (response == PageFaultResponse::Continue) { - dbgln_if(PAGE_FAULT_DEBUG, "Continuing after resolved page fault"); - } else { - VERIFY_NOT_REACHED(); - } -} - -} diff --git a/Kernel/Arch/PageFault.h b/Kernel/Arch/PageFault.h deleted file mode 100644 index c0e14f88eed..00000000000 --- a/Kernel/Arch/PageFault.h +++ /dev/null @@ -1,103 +0,0 @@ -/* - * Copyright (c) 2018-2021, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include - -namespace Kernel { - -// NOTE: These flags are x86_64 specific. -struct PageFaultFlags { - enum Flags { - NotPresent = 0x00, - ProtectionViolation = 0x01, - Read = 0x00, - Write = 0x02, - UserMode = 0x04, - SupervisorMode = 0x00, - ReservedBitViolation = 0x08, - InstructionFetch = 0x10, - }; -}; - -class PageFault { -public: - PageFault(u16 code, VirtualAddress vaddr) - : m_vaddr(vaddr) - { - m_type = (Type)(code & PageFaultFlags::ProtectionViolation); - m_access = (Access)(code & PageFaultFlags::Write); - m_execution_mode = (code & PageFaultFlags::UserMode) != 0 ? ExecutionMode::User : ExecutionMode::Kernel; - m_is_reserved_bit_violation = (code & PageFaultFlags::ReservedBitViolation) != 0; - m_is_instruction_fetch = (code & PageFaultFlags::InstructionFetch) != 0; - } - - explicit PageFault(VirtualAddress vaddr) - : m_vaddr(vaddr) - { - } - - void handle(RegisterState& regs); - - enum class Type { - PageNotPresent = PageFaultFlags::NotPresent, - ProtectionViolation = PageFaultFlags::ProtectionViolation, - Unknown, - }; - - enum class Access { - Read = PageFaultFlags::Read, - Write = PageFaultFlags::Write, - }; - - VirtualAddress vaddr() const { return m_vaddr; } - u16 code() const - { - u16 code = 0; - code |= (u16)m_type; - code |= (u16)m_access; - code |= m_execution_mode == ExecutionMode::User ? PageFaultFlags::UserMode : 0; - code |= m_is_reserved_bit_violation ? PageFaultFlags::ReservedBitViolation : 0; - code |= m_is_instruction_fetch ? PageFaultFlags::InstructionFetch : 0; - return code; - } - - void set_type(Type type) { m_type = type; } - Type type() const { return m_type; } - - void set_access(Access access) { m_access = access; } - Access access() const { return m_access; } - - void set_mode(ExecutionMode execution_mode) { m_execution_mode = execution_mode; } - ExecutionMode mode() const { return m_execution_mode; } - - void set_instruction_fetch(bool b) { m_is_instruction_fetch = b; } - - bool is_not_present() const { return m_type == Type::PageNotPresent; } - bool is_protection_violation() const { return m_type == Type::ProtectionViolation; } - bool is_read() const { return m_access == Access::Read; } - bool is_write() const { return m_access == Access::Write; } - bool is_user() const { return m_execution_mode == ExecutionMode::User; } - bool is_kernel() const { return m_execution_mode == ExecutionMode::Kernel; } - bool is_reserved_bit_violation() const { return m_is_reserved_bit_violation; } - bool is_instruction_fetch() const { return m_is_instruction_fetch; } - -private: - Type m_type = Type::Unknown; - Access m_access; - ExecutionMode m_execution_mode; - bool m_is_reserved_bit_violation { false }; - bool m_is_instruction_fetch { false }; - - VirtualAddress m_vaddr; -}; - -} diff --git a/Kernel/Arch/PowerState.h b/Kernel/Arch/PowerState.h deleted file mode 100644 index a8539313402..00000000000 --- a/Kernel/Arch/PowerState.h +++ /dev/null @@ -1,16 +0,0 @@ -/* - * Copyright (c) 2023, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include - -namespace Kernel { - -void arch_specific_reboot(); -void arch_specific_poweroff(); - -} diff --git a/Kernel/Arch/Processor.cpp b/Kernel/Arch/Processor.cpp deleted file mode 100644 index 83958d72bc4..00000000000 --- a/Kernel/Arch/Processor.cpp +++ /dev/null @@ -1,136 +0,0 @@ -/* - * Copyright (c) 2022, Timon Kruiper - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include -#include - -namespace Kernel { - -READONLY_AFTER_INIT FPUState s_clean_fpu_state; -READONLY_AFTER_INIT Atomic g_total_processors; - -template -void ProcessorBase::check_invoke_scheduler() -{ - VERIFY_INTERRUPTS_DISABLED(); - VERIFY(!m_in_irq); - VERIFY(!m_in_critical); - VERIFY(&Processor::current() == this); - if (m_invoke_scheduler_async && m_scheduler_initialized.was_set()) { - m_invoke_scheduler_async = false; - Scheduler::invoke_async(); - } -} -template void ProcessorBase::check_invoke_scheduler(); - -template -void ProcessorBase::deferred_call_queue(Function callback) -{ - // NOTE: If we are called outside of a critical section and outside - // of an irq handler, the function will be executed before we return! - ScopedCritical critical; - auto& cur_proc = Processor::current(); - - auto* entry = cur_proc.m_deferred_call_pool.get_free(); - entry->handler_value() = move(callback); - - cur_proc.m_deferred_call_pool.queue_entry(entry); -} -template void ProcessorBase::deferred_call_queue(Function); - -template -void ProcessorBase::enter_trap(TrapFrame& trap, bool raise_irq) -{ - VERIFY_INTERRUPTS_DISABLED(); - VERIFY(&Processor::current() == this); -#if ARCH(X86_64) - // FIXME: Figure out if we need prev_irq_level - trap.prev_irq_level = m_in_irq; -#endif - if (raise_irq) - m_in_irq++; - auto* current_thread = Processor::current_thread(); - if (current_thread) { - auto& current_trap = current_thread->current_trap(); - trap.next_trap = current_trap; - current_trap = &trap; - auto new_previous_mode = trap.regs->previous_mode(); - if (current_thread->set_previous_mode(new_previous_mode)) { - current_thread->update_time_scheduled(TimeManagement::scheduler_current_time(), new_previous_mode == ExecutionMode::Kernel, false); - } - } else { - trap.next_trap = nullptr; - } -} -template void ProcessorBase::enter_trap(TrapFrame&, bool); - -template -u64 ProcessorBase::time_spent_idle() const -{ - return m_idle_thread->time_in_user() + m_idle_thread->time_in_kernel(); -} -template u64 ProcessorBase::time_spent_idle() const; - -template -void ProcessorBase::leave_critical() -{ - InterruptDisabler disabler; - current().do_leave_critical(); -} -template void ProcessorBase::leave_critical(); - -template -void ProcessorBase::do_leave_critical() -{ - VERIFY(m_in_critical > 0); - if (m_in_critical == 1) { - if (m_in_irq == 0) { - m_deferred_call_pool.execute_pending(); - VERIFY(m_in_critical == 1); - } - m_in_critical = 0; - if (m_in_irq == 0) - check_invoke_scheduler(); - } else { - m_in_critical = m_in_critical - 1; - } -} -template void ProcessorBase::do_leave_critical(); - -void exit_kernel_thread(void) -{ - Thread::current()->exit(); -} - -void do_context_first_init(Thread* from_thread, Thread* to_thread) -{ - VERIFY(!Processor::are_interrupts_enabled()); - VERIFY(Processor::is_kernel_mode()); - - dbgln_if(CONTEXT_SWITCH_DEBUG, "switch_context <-- from {} {} to {} {} (context_first_init)", VirtualAddress(from_thread), *from_thread, VirtualAddress(to_thread), *to_thread); - - VERIFY(to_thread == Thread::current()); - - Scheduler::enter_current(*from_thread); - - auto in_critical = to_thread->saved_critical(); - VERIFY(in_critical > 0); - Processor::restore_critical(in_critical); - - // Since we got here and don't have Scheduler::context_switch in the - // call stack (because this is the first time we switched into this - // context), we need to notify the scheduler so that it can release - // the scheduler lock. We don't want to enable interrupts at this point - // as we're still in the middle of a context switch. Doing so could - // trigger a context switch within a context switch, leading to a crash. - Scheduler::leave_on_first_switch(InterruptsState::Disabled); -} - -} diff --git a/Kernel/Arch/Processor.h b/Kernel/Arch/Processor.h deleted file mode 100644 index 6d7fd37e0f1..00000000000 --- a/Kernel/Arch/Processor.h +++ /dev/null @@ -1,295 +0,0 @@ -/* - * Copyright (c) 2018-2021, James Mintram - * Copyright (c) 2018-2021, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include -#include -#include -#include - -#if ARCH(X86_64) -# include -#endif - -namespace Kernel { - -enum class InterruptsState { - Enabled, - Disabled -}; - -namespace Memory { -class PageDirectory; -} - -struct TrapFrame; -class Thread; - -class Processor; - -extern Atomic g_total_processors; - -extern FPUState s_clean_fpu_state; - -// context_first_init is an architecture-specific detail with various properties. -// All variants eventually call into the common code here. -void do_context_first_init(Thread* from_thread, Thread* to_thread); -extern "C" void exit_kernel_thread(void); -extern "C" void thread_context_first_enter(void); -extern "C" void do_assume_context(Thread* thread, u32 flags); -extern "C" FlatPtr do_init_context(Thread* thread, u32) __attribute__((used)); - -template -class ProcessorBase { -public: - template - T* get_specific() - { - return static_cast(m_processor_specific_data[static_cast(T::processor_specific_data_id())]); - } - - void set_specific(ProcessorSpecificDataID specific_id, void* ptr) - { - m_processor_specific_data[static_cast(specific_id)] = ptr; - } - - static bool is_smp_enabled(); - static void smp_enable(); - static u32 smp_wake_n_idle_processors(u32 wake_count); - - static void flush_tlb_local(VirtualAddress vaddr, size_t page_count); - static void flush_tlb(Memory::PageDirectory const*, VirtualAddress, size_t); - - void early_initialize(u32 cpu); - void initialize(u32 cpu); - ALWAYS_INLINE static bool is_initialized(); - - [[noreturn]] static void halt(); - void wait_for_interrupt() const; - ALWAYS_INLINE static void pause(); - ALWAYS_INLINE static void wait_check(); - - ALWAYS_INLINE static ProcessorT& current(); - static Processor& by_id(u32); - - ALWAYS_INLINE u32 id() const - { - // NOTE: This variant should only be used when iterating over all - // Processor instances, or when it's guaranteed that the thread - // cannot move to another processor in between calling Processor::current - // and Processor::id, or if this fact is not important. - // All other cases should use Processor::current_id instead! - return m_cpu; - } - - ALWAYS_INLINE static u32 current_id(); - ALWAYS_INLINE static bool is_bootstrap_processor(); - ALWAYS_INLINE bool has_nx() const; - ALWAYS_INLINE bool has_pat() const; - ALWAYS_INLINE bool has_feature(CPUFeature::Type const& feature) const - { - return m_features.has_flag(feature); - } - static StringView platform_string(); - - static u32 count() - { - // NOTE: because this value never changes once all APs are booted, - // we can safely bypass loading it atomically. - // NOTE: This does not work on aarch64, since the variable is never written. - return *g_total_processors.ptr(); - } - - void enter_trap(TrapFrame& trap, bool raise_irq); - void exit_trap(TrapFrame& trap); - - static void flush_entire_tlb_local(); - - ALWAYS_INLINE static Thread* current_thread(); - ALWAYS_INLINE static void set_current_thread(Thread& current_thread); - ALWAYS_INLINE static Thread* idle_thread(); - - ALWAYS_INLINE static u32 in_critical(); - ALWAYS_INLINE static void enter_critical(); - static void leave_critical(); - void do_leave_critical(); - static u32 clear_critical(); - ALWAYS_INLINE static void restore_critical(u32 prev_critical); - ALWAYS_INLINE static void verify_no_spinlocks_held() - { - VERIFY(!ProcessorBase::in_critical()); - } - - static InterruptsState interrupts_state(); - static void restore_interrupts_state(InterruptsState); - static bool are_interrupts_enabled(); - ALWAYS_INLINE static void enable_interrupts(); - ALWAYS_INLINE static void disable_interrupts(); - ALWAYS_INLINE static FlatPtr current_in_irq(); - - ALWAYS_INLINE static bool is_kernel_mode(); - - ALWAYS_INLINE void set_idle_thread(Thread& idle_thread) - { - m_idle_thread = &idle_thread; - } - void idle_begin() const; - void idle_end() const; - u64 time_spent_idle() const; - ALWAYS_INLINE static u64 read_cpu_counter(); - - void check_invoke_scheduler(); - void invoke_scheduler_async() { m_invoke_scheduler_async = true; } - ALWAYS_INLINE static bool current_in_scheduler(); - ALWAYS_INLINE static void set_current_in_scheduler(bool value); - ALWAYS_INLINE bool is_in_scheduler() const { return m_in_scheduler; } - - ALWAYS_INLINE u8 physical_address_bit_width() const - { - return m_physical_address_bit_width; - } - ALWAYS_INLINE u8 virtual_address_bit_width() const - { - return m_virtual_address_bit_width; - } - - ALWAYS_INLINE static FPUState const& clean_fpu_state() { return s_clean_fpu_state; } - - static void deferred_call_queue(Function callback); - - [[noreturn]] void initialize_context_switching(Thread& initial_thread); - NEVER_INLINE void switch_context(Thread*& from_thread, Thread*& to_thread); - [[noreturn]] static void assume_context(Thread& thread, InterruptsState new_interrupts_state); - FlatPtr init_context(Thread& thread, bool leave_crit); - static ErrorOr> capture_stack_trace(Thread& thread, size_t max_frames = 0); - -protected: - ProcessorT* m_self; - CPUFeature::Type m_features; - - Atomic m_halt_requested; - - u8 m_physical_address_bit_width; - u8 m_virtual_address_bit_width; - -private: - void* m_processor_specific_data[static_cast(ProcessorSpecificDataID::__Count)]; - Thread* m_idle_thread; - Thread* m_current_thread; - - u32 m_cpu { 0 }; - - // FIXME: On aarch64, once there is code in place to differentiate IRQs from synchronous exceptions (syscalls), - // this member should be incremented. Also this member shouldn't be a FlatPtr. - FlatPtr m_in_irq { 0 }; - u32 volatile m_in_critical; - // NOTE: Since these variables are accessed with atomic magic on x86 (through GP with a single load instruction), - // they need to be FlatPtrs or everything becomes highly unsound and breaks. They are actually just booleans. - FlatPtr m_in_scheduler; - FlatPtr m_invoke_scheduler_async; - - SetOnce m_scheduler_initialized; - - DeferredCallPool m_deferred_call_pool {}; -}; - -template class ProcessorBase; - -} - -#if ARCH(X86_64) -# include -#elif ARCH(AARCH64) -# include -#elif ARCH(RISCV64) -# include -#else -# error "Unknown architecture" -#endif - -namespace Kernel { - -template -ALWAYS_INLINE bool ProcessorBase::is_bootstrap_processor() -{ - return current_id() == 0; -} - -template -InterruptsState ProcessorBase::interrupts_state() -{ - return Processor::are_interrupts_enabled() ? InterruptsState::Enabled : InterruptsState::Disabled; -} - -template -void ProcessorBase::restore_interrupts_state(InterruptsState interrupts_state) -{ - if (interrupts_state == InterruptsState::Enabled) - Processor::enable_interrupts(); - else - Processor::disable_interrupts(); -} - -struct ProcessorMessageEntry; -struct ProcessorMessage { - using CallbackFunction = Function; - - enum Type { - FlushTlb, - Callback, - }; - Type type; - Atomic refs; - union { - ProcessorMessage* next; // only valid while in the pool - alignas(CallbackFunction) u8 callback_storage[sizeof(CallbackFunction)]; - struct { - Memory::PageDirectory const* page_directory; - u8* ptr; - size_t page_count; - } flush_tlb; - }; - - bool volatile async; - - ProcessorMessageEntry* per_proc_entries; - - CallbackFunction& callback_value() - { - return *bit_cast(&callback_storage); - } - - void invoke_callback() - { - VERIFY(type == Type::Callback); - callback_value()(); - } -}; - -struct ProcessorMessageEntry { - ProcessorMessageEntry* next; - ProcessorMessage* msg; -}; - -template -class ProcessorSpecific { -public: - static void initialize() - { - Processor::current().set_specific(T::processor_specific_data_id(), new T); - } - static T& get() - { - return *Processor::current().get_specific(); - } -}; -} diff --git a/Kernel/Arch/ProcessorFunctions.include b/Kernel/Arch/ProcessorFunctions.include deleted file mode 100644 index c0eb89237ee..00000000000 --- a/Kernel/Arch/ProcessorFunctions.include +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright (c) 2023, kleines Filmröllchen - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include - -// This header instantiates all functions of ProcessorBase that are architecture-specific. -namespace Kernel { -template bool ProcessorBase::is_smp_enabled(); -template void ProcessorBase::idle_begin() const; -template void ProcessorBase::idle_end() const; -template void ProcessorBase::smp_enable(); -template void ProcessorBase::flush_tlb_local(VirtualAddress vaddr, size_t page_count); -template void ProcessorBase::flush_entire_tlb_local(); -template void ProcessorBase::flush_tlb(Memory::PageDirectory const*, VirtualAddress, size_t); -template void ProcessorBase::early_initialize(u32 cpu); -template void ProcessorBase::initialize(u32 cpu); -template void ProcessorBase::halt(); -template void ProcessorBase::exit_trap(TrapFrame& trap); -template u32 ProcessorBase::clear_critical(); -template bool ProcessorBase::are_interrupts_enabled(); -template void ProcessorBase::wait_for_interrupt() const; -template Processor& ProcessorBase::by_id(u32 id); -template StringView ProcessorBase::platform_string(); -template void ProcessorBase::initialize_context_switching(Thread& initial_thread); -template void ProcessorBase::switch_context(Thread*& from_thread, Thread*& to_thread); -template void ProcessorBase::assume_context(Thread& thread, InterruptsState new_interrupts_state); -template FlatPtr ProcessorBase::init_context(Thread& thread, bool leave_crit); -template ErrorOr> ProcessorBase::capture_stack_trace(Thread& thread, size_t max_frames); -template u32 ProcessorBase::smp_wake_n_idle_processors(u32 wake_count); -} diff --git a/Kernel/Arch/ProcessorSpecificDataID.h b/Kernel/Arch/ProcessorSpecificDataID.h deleted file mode 100644 index b9587900574..00000000000 --- a/Kernel/Arch/ProcessorSpecificDataID.h +++ /dev/null @@ -1,16 +0,0 @@ -/* - * Copyright (c) 2018-2021, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -namespace Kernel { - -enum class ProcessorSpecificDataID { - MemoryManager, - __Count, -}; - -} diff --git a/Kernel/Arch/RegisterState.h b/Kernel/Arch/RegisterState.h deleted file mode 100644 index 3d7021cf8ab..00000000000 --- a/Kernel/Arch/RegisterState.h +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Copyright (c) 2018-2021, James Mintram - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include - -#if ARCH(X86_64) -# include -#elif ARCH(AARCH64) -# include -#elif ARCH(RISCV64) -# include -#else -# error "Unknown architecture" -#endif diff --git a/Kernel/Arch/SafeMem.h b/Kernel/Arch/SafeMem.h deleted file mode 100644 index 63efc585c3d..00000000000 --- a/Kernel/Arch/SafeMem.h +++ /dev/null @@ -1,100 +0,0 @@ -/* - * Copyright (c) 2020, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include - -namespace Kernel { - -struct RegisterState; - -[[nodiscard]] bool safe_memcpy(void* dest_ptr, void const* src_ptr, size_t n, void*& fault_at) __attribute__((used)); -[[nodiscard]] ssize_t safe_strnlen(char const* str, size_t max_n, void*& fault_at) __attribute__((used)); -[[nodiscard]] bool safe_memset(void* dest_ptr, int c, size_t n, void*& fault_at) __attribute__((used)); -[[nodiscard]] Optional safe_atomic_fetch_add_relaxed(u32 volatile* var, u32 val) __attribute__((used)); -[[nodiscard]] Optional safe_atomic_exchange_relaxed(u32 volatile* var, u32 val) __attribute__((used)); -[[nodiscard]] Optional safe_atomic_load_relaxed(u32 volatile* var) __attribute__((used)); -[[nodiscard]] bool safe_atomic_store_relaxed(u32 volatile* var, u32 val) __attribute__((used)); -[[nodiscard]] Optional safe_atomic_compare_exchange_relaxed(u32 volatile* var, u32& expected, u32 val) __attribute__((used)); - -[[nodiscard]] ALWAYS_INLINE Optional safe_atomic_fetch_and_relaxed(u32 volatile* var, u32 val) -{ - auto expected_value = safe_atomic_load_relaxed(var); - if (!expected_value.has_value()) - return {}; // fault - u32& expected = expected_value.value(); - for (;;) { - auto result = safe_atomic_compare_exchange_relaxed(var, expected, expected & val); - if (!result.has_value()) - return {}; // fault - if (result.value()) - return expected; // exchanged - - // This is only so that we don't saturate the bus... - AK::atomic_thread_fence(AK::MemoryOrder::memory_order_acquire); - } -} - -[[nodiscard]] ALWAYS_INLINE Optional safe_atomic_fetch_and_not_relaxed(u32 volatile* var, u32 val) -{ - auto expected_value = safe_atomic_load_relaxed(var); - if (!expected_value.has_value()) - return {}; // fault - u32& expected = expected_value.value(); - for (;;) { - auto result = safe_atomic_compare_exchange_relaxed(var, expected, expected & ~val); - if (!result.has_value()) - return {}; // fault - if (result.value()) - return expected; // exchanged - - // This is only so that we don't saturate the bus... - AK::atomic_thread_fence(AK::MemoryOrder::memory_order_acquire); - } -} - -[[nodiscard]] ALWAYS_INLINE Optional safe_atomic_fetch_or_relaxed(u32 volatile* var, u32 val) -{ - auto expected_value = safe_atomic_load_relaxed(var); - if (!expected_value.has_value()) - return {}; // fault - u32& expected = expected_value.value(); - for (;;) { - auto result = safe_atomic_compare_exchange_relaxed(var, expected, expected | val); - if (!result.has_value()) - return {}; // fault - if (result.value()) - return expected; // exchanged - - // This is only so that we don't saturate the bus... - AK::atomic_thread_fence(AK::MemoryOrder::memory_order_acquire); - } -} - -[[nodiscard]] ALWAYS_INLINE Optional safe_atomic_fetch_xor_relaxed(u32 volatile* var, u32 val) -{ - auto expected_value = safe_atomic_load_relaxed(var); - if (!expected_value.has_value()) - return {}; // fault - u32& expected = expected_value.value(); - for (;;) { - auto result = safe_atomic_compare_exchange_relaxed(var, expected, expected ^ val); - if (!result.has_value()) - return {}; // fault - if (result.value()) - return expected; // exchanged - - // This is only so that we don't saturate the bus... - AK::atomic_thread_fence(AK::MemoryOrder::memory_order_acquire); - } -} - -bool handle_safe_access_fault(RegisterState& regs, FlatPtr fault_address); - -} diff --git a/Kernel/Arch/SmapDisabler.h b/Kernel/Arch/SmapDisabler.h deleted file mode 100644 index 89973189b1d..00000000000 --- a/Kernel/Arch/SmapDisabler.h +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Copyright (c) 2018-2021, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include - -namespace Kernel { - -class SmapDisabler { -public: - SmapDisabler(); - ~SmapDisabler(); - -private: - FlatPtr const m_flags; -}; - -} diff --git a/Kernel/Arch/ThreadRegisters.h b/Kernel/Arch/ThreadRegisters.h deleted file mode 100644 index b18c431920d..00000000000 --- a/Kernel/Arch/ThreadRegisters.h +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Copyright (c) 2022, Timon Kruiper - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include - -#if ARCH(X86_64) -# include -#elif ARCH(AARCH64) -# include -#elif ARCH(RISCV64) -# include -#else -# error "Unknown architecture" -#endif diff --git a/Kernel/Arch/TrapFrame.cpp b/Kernel/Arch/TrapFrame.cpp deleted file mode 100644 index af2ccb6f148..00000000000 --- a/Kernel/Arch/TrapFrame.cpp +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright (c) 2018-2021, Andreas Kling - * Copyright (c) 2023, Idan Horowitz - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include - -namespace Kernel { - -extern "C" void enter_trap_no_irq(TrapFrame* trap) -{ - InterruptDisabler disable; - Processor::current().enter_trap(*trap, false); -} - -extern "C" void enter_trap(TrapFrame* trap) -{ - InterruptDisabler disable; - Processor::current().enter_trap(*trap, true); -} - -extern "C" void exit_trap(TrapFrame* trap) -{ - InterruptDisabler disable; - return Processor::current().exit_trap(*trap); -} - -} diff --git a/Kernel/Arch/TrapFrame.h b/Kernel/Arch/TrapFrame.h deleted file mode 100644 index f60b66715c0..00000000000 --- a/Kernel/Arch/TrapFrame.h +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright (c) 2022, Gunnar Beutner - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include - -// FIXME: There's only a minor difference between x86 and Aarch64/RISC-V trap frames; the prev_irq member. -// This seems to be unnecessary (see FIXME in Processor::enter_trap), -// so investigate whether we need it and either: -// (1) Remove the member and corresponding code from x86 -// (2) Implement prev_irq in the assembly stubs of Aarch64 and RISC-V -// and then use the same TrapFrame on all architectures. - -#if ARCH(X86_64) -# include -#elif ARCH(AARCH64) -# include -#elif ARCH(RISCV64) -# include -#else -# error "Unknown architecture" -#endif - -namespace Kernel { - -extern "C" void enter_trap_no_irq(TrapFrame* trap) __attribute__((used)); -extern "C" void enter_trap(TrapFrame*) __attribute__((used)); -extern "C" void exit_trap(TrapFrame*) __attribute__((used)); - -} diff --git a/Kernel/Arch/aarch64/ASM_wrapper.h b/Kernel/Arch/aarch64/ASM_wrapper.h deleted file mode 100644 index 1e0b922ee83..00000000000 --- a/Kernel/Arch/aarch64/ASM_wrapper.h +++ /dev/null @@ -1,147 +0,0 @@ -/* - * Copyright (c) 2021, James Mintram - * Copyright (c) 2021, Nico Weber - * Copyright (c) 2021, Marcin Undak - * Copyright (c) 2021, Jesse Buhagiar - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include - -namespace Kernel::Aarch64::Asm { - -inline void set_ttbr1_el1(FlatPtr ttbr1_el1) -{ - asm volatile("msr ttbr1_el1, %[value]" ::[value] "r"(ttbr1_el1)); -} - -inline void set_ttbr0_el1(FlatPtr ttbr0_el1) -{ - asm volatile("msr ttbr0_el1, %[value]" ::[value] "r"(ttbr0_el1)); -} - -inline FlatPtr get_ttbr0_el1() -{ - FlatPtr ttbr0_el1; - asm volatile("mrs %[value], ttbr0_el1\n" - : [value] "=r"(ttbr0_el1)); - return ttbr0_el1; -} - -inline void set_sp_el1(FlatPtr sp_el1) -{ - asm volatile("msr sp_el1, %[value]" ::[value] "r"(sp_el1)); -} - -inline void set_tpidr_el0(FlatPtr tpidr_el0) -{ - asm volatile("msr tpidr_el0, %[value]" ::[value] "r"(tpidr_el0)); -} - -inline void flush() -{ - asm volatile("dsb ish"); - asm volatile("isb"); -} - -[[noreturn]] inline void halt() -{ - for (;;) { - asm volatile("wfi"); - } -} - -enum class ExceptionLevel : u8 { - EL0 = 0, - EL1 = 1, - EL2 = 2, - EL3 = 3, -}; - -inline ExceptionLevel get_current_exception_level() -{ - u64 current_exception_level; - - asm volatile("mrs %[value], CurrentEL" - : [value] "=r"(current_exception_level)); - - current_exception_level = (current_exception_level >> 2) & 0x3; - return static_cast(current_exception_level); -} - -inline void wait_cycles(int n) -{ - // FIXME: Make timer-based. - for (int i = 0; i < n; i = i + 1) { - Processor::pause(); - } -} - -inline void load_el1_vector_table(void* vector_table) -{ - asm volatile("msr VBAR_EL1, %[value]" ::[value] "r"(vector_table)); -} - -inline void enter_el2_from_el3() -{ - // NOTE: This also copies the current stack pointer into SP_EL2, as - // the processor is set up to use SP_EL2 when jumping into EL2. - asm volatile(" mov x0, sp\n" - " msr sp_el2, x0\n" - " adr x0, entered_el2\n" - " msr elr_el3, x0\n" - " eret\n" - "entered_el2:" :: - : "x0"); -} - -inline void enter_el1_from_el2() -{ - // NOTE: This also copies the current stack pointer into SP_EL1, as - // the processor is set up to use SP_EL1 when jumping into EL1. - asm volatile(" mov x0, sp\n" - " msr sp_el1, x0\n" - " adr x0, entered_el1\n" - " msr elr_el2, x0\n" - " eret\n" - "entered_el1:" :: - : "x0"); -} - -inline u64 read_rndrrs() -{ - u64 value = 0; - - asm volatile( - "retry:\n" - "mrs %[value], s3_3_c2_c4_1 \n" // encoded RNDRRS register - "b.eq retry\n" - : [value] "=r"(value)); - - return value; -} - -inline FlatPtr get_cache_line_size() -{ - FlatPtr ctr_el0; - asm volatile("mrs %[value], ctr_el0" - : [value] "=r"(ctr_el0)); - auto log2_size = (ctr_el0 >> 16) & 0xF; - return 1 << log2_size; -} - -inline void flush_data_cache(FlatPtr start, size_t size) -{ - auto const cache_size = get_cache_line_size(); - for (FlatPtr addr = align_down_to(start, cache_size); addr < start + size; addr += cache_size) - asm volatile("dc civac, %[addr]" ::[addr] "r"(addr) - : "memory"); - asm volatile("dsb sy" :: - : "memory"); -} - -} diff --git a/Kernel/Arch/aarch64/ArchSpecificThreadData.h b/Kernel/Arch/aarch64/ArchSpecificThreadData.h deleted file mode 100644 index 5dbe07b8d27..00000000000 --- a/Kernel/Arch/aarch64/ArchSpecificThreadData.h +++ /dev/null @@ -1,16 +0,0 @@ -/* - * Copyright (c) 2024, Sönke Holz - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include - -namespace Kernel { - -struct ArchSpecificThreadData { -}; - -} diff --git a/Kernel/Arch/aarch64/BootPPMParser.cpp b/Kernel/Arch/aarch64/BootPPMParser.cpp deleted file mode 100644 index b8fe4f25407..00000000000 --- a/Kernel/Arch/aarch64/BootPPMParser.cpp +++ /dev/null @@ -1,111 +0,0 @@ -/* - * Copyright (c) 2021, Marcin Undak - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "BootPPMParser.h" - -namespace Kernel { - -BootPPMParser::BootPPMParser(u8 const* buffer, u32 buffer_size) -{ - m_cursor = reinterpret_cast(buffer); - m_buffer_end = m_cursor + buffer_size; -} - -bool BootPPMParser::parse() -{ - if (!check_position()) { - return false; - } - if (!parse_magic()) { - return false; - } - if (!parse_new_line()) { - return false; - } - if (!parse_comment()) { - return false; - } - if (!parse_integer(image.width)) { - return false; - } - if (!parse_integer(image.height)) { - return false; - } - u32 max_color_value; - if (!parse_integer(max_color_value) || max_color_value != 255) { - return false; - } - - image.pixel_data = reinterpret_cast(m_cursor); - - return true; -} - -bool BootPPMParser::check_position() const -{ - if (m_cursor >= m_buffer_end) { - return false; - } - return true; -} - -bool BootPPMParser::parse_magic() -{ - if (m_cursor[0] != 'P' || m_cursor[1] != '6') { - return false; - } - m_cursor += 2; - - return check_position(); -} - -bool BootPPMParser::parse_new_line() -{ - if (*m_cursor != '\n') { - return false; - } - ++m_cursor; - - return check_position(); -} - -bool BootPPMParser::parse_comment() -{ - if (*m_cursor == '#') { - // Skip to the next new line character - while (check_position() && *m_cursor != '\n') { - ++m_cursor; - } - ++m_cursor; - } - - return check_position(); -} - -bool BootPPMParser::parse_integer(u32& value) -{ - auto begin = m_cursor; - while (check_position() && *m_cursor != ' ' && *m_cursor != '\n') { - ++m_cursor; - } - auto end = m_cursor; - ++m_cursor; - - if (!check_position()) { - return false; - } - - value = 0; - u32 multiplier = 1; - while (--end >= begin) { - value += multiplier * (*end - '0'); - multiplier *= 10; - } - - return true; -} - -} diff --git a/Kernel/Arch/aarch64/BootPPMParser.h b/Kernel/Arch/aarch64/BootPPMParser.h deleted file mode 100644 index 4c007964bcd..00000000000 --- a/Kernel/Arch/aarch64/BootPPMParser.h +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright (c) 2021, Marcin Undak - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include - -namespace Kernel { - -// Quick parser for .ppm image format (raw PortablePixMap) -// This is much simpler version than userland implementation in PPMLoader.cpp -class BootPPMParser { -public: - struct { - u32 width = 0; - u32 height = 0; - u8 const* pixel_data = nullptr; - } image; - - BootPPMParser(u8 const* buffer, u32 buffer_size); - - bool parse(); - -private: - char const* m_cursor; - char const* m_buffer_end; - - bool check_position() const; - bool parse_magic(); - bool parse_new_line(); - bool parse_comment(); - bool parse_integer(u32& value); -}; - -} diff --git a/Kernel/Arch/aarch64/CPU.h b/Kernel/Arch/aarch64/CPU.h deleted file mode 100644 index b4d52f4f51e..00000000000 --- a/Kernel/Arch/aarch64/CPU.h +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright (c) 2018-2022, James Mintram - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include - -namespace Kernel { - -void initialize_exceptions(); -void panic_without_mmu(StringView); -void dbgln_without_mmu(StringView); - -namespace Memory { - -void init_page_tables(); -void unmap_identity_map(); - -} - -} diff --git a/Kernel/Arch/aarch64/CPUID.cpp b/Kernel/Arch/aarch64/CPUID.cpp deleted file mode 100644 index 18f3d6ec2f9..00000000000 --- a/Kernel/Arch/aarch64/CPUID.cpp +++ /dev/null @@ -1,1523 +0,0 @@ -/* - * Copyright (c) 2023, Konrad - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include - -namespace Kernel { - -CPUFeature::Type detect_cpu_features() -{ - auto features = CPUFeature::Type(0u); - - auto instruction_set_attribute_register_0 = Aarch64::ID_AA64ISAR0_EL1::read(); - auto instruction_set_attribute_register_1 = Aarch64::ID_AA64ISAR1_EL1::read(); - auto instruction_set_attribute_register_2 = Aarch64::ID_AA64ISAR2_EL1::read(); - auto processor_feature_register_0 = Aarch64::ID_AA64PFR0_EL1::read(); - auto processor_feature_register_1 = Aarch64::ID_AA64PFR1_EL1::read(); - auto memory_model_feature_register_0 = Aarch64::ID_AA64MMFR0_EL1::read(); - auto memory_model_feature_register_1 = Aarch64::ID_AA64MMFR1_EL1::read(); - auto memory_model_feature_register_2 = Aarch64::ID_AA64MMFR2_EL1::read(); - auto memory_model_feature_register_3 = Aarch64::ID_AA64MMFR3_EL1::read(); - auto sme_feature_register_0 = Aarch64::ID_AA64SMFR0_EL1::read(); - auto sve_feature_register_0 = Aarch64::ID_AA64ZFR0_EL1::read(); - auto debug_feature_register_0 = Aarch64::ID_AA64DFR0_EL1::read(); - auto debug_feature_register_1 = Aarch64::ID_AA64DFR1_EL1::read(); - auto translation_control_register = Aarch64::TCR_EL1::read(); - - // positives - if (instruction_set_attribute_register_0.AES == 0b0001) - features |= CPUFeature::AES; - if (instruction_set_attribute_register_0.AES == 0b0010) - features |= CPUFeature::PMULL; - if (instruction_set_attribute_register_0.SHA1 == 0b0001) - features |= CPUFeature::SHA1; - if (instruction_set_attribute_register_0.SHA2 == 0b0001) - features |= CPUFeature::SHA256; - if (instruction_set_attribute_register_0.SHA2 == 0b0010) - features |= CPUFeature::SHA512; - if (instruction_set_attribute_register_0.CRC32 == 0b0001) - features |= CPUFeature::CRC32; - if (instruction_set_attribute_register_0.Atomic == 0b0010) - features |= CPUFeature::LSE; - if (instruction_set_attribute_register_0.Atomic == 0b0011) - features |= CPUFeature::LSE128; - if (instruction_set_attribute_register_0.TME == 0b0001) - // TODO: confirm that—missing in the spec - features |= CPUFeature::TME; - if (instruction_set_attribute_register_0.RDM == 0b0001) - features |= CPUFeature::RDM; - if (instruction_set_attribute_register_0.SHA3 == 0b0001) - features |= CPUFeature::SHA3; - if (instruction_set_attribute_register_0.SM3 == 0b0001) - features |= CPUFeature::SM3; - if (instruction_set_attribute_register_0.SM4 == 0b0001) - // TODO: confirm that—unclear spec - features |= CPUFeature::SM4; - if (instruction_set_attribute_register_0.DP == 0b0001) - features |= CPUFeature::DotProd; - if (instruction_set_attribute_register_0.FHM == 0b0001) - features |= CPUFeature::FHM; - if (instruction_set_attribute_register_0.TS == 0b0001) - features |= CPUFeature::FlagM; - if (instruction_set_attribute_register_0.TS == 0b0010) - features |= CPUFeature::FlagM2; - if (instruction_set_attribute_register_0.TLB == 0b0001 || instruction_set_attribute_register_0.TLB == 0b0010) - features |= CPUFeature::TLBIOS; - if (instruction_set_attribute_register_0.TLB == 0b0010) - features |= CPUFeature::TLBIRANGE; - if (instruction_set_attribute_register_0.RNDR == 0b0001) - features |= CPUFeature::RNG; - if (instruction_set_attribute_register_1.DPB == 0b0001) - features |= CPUFeature::DPB; - if (instruction_set_attribute_register_1.DPB == 0b0010) - features |= CPUFeature::DPB2; - if (instruction_set_attribute_register_1.API == 0b0100 && instruction_set_attribute_register_1.APA == 0b0100 && instruction_set_attribute_register_2.APA3 == 0b0100) - features |= CPUFeature::FPAC; - if (instruction_set_attribute_register_1.API == 0b0101 && instruction_set_attribute_register_1.APA == 0b0101 && instruction_set_attribute_register_2.APA3 == 0b0101) - features |= CPUFeature::FPACCOMBINE; - if (instruction_set_attribute_register_1.API == 0b0001 && instruction_set_attribute_register_1.APA == 0b0001 && instruction_set_attribute_register_2.APA3 == 0b0001) - features |= CPUFeature::PAuth; - if (instruction_set_attribute_register_1.API == 0b0011 && instruction_set_attribute_register_1.APA == 0b0011 && instruction_set_attribute_register_2.APA3 == 0b0011) - features |= CPUFeature::PAuth2; - if (instruction_set_attribute_register_1.JSCVT == 0b0001) - features |= CPUFeature::JSCVT; - if (instruction_set_attribute_register_1.FCMA == 0b0001) - features |= CPUFeature::FCMA; - if (instruction_set_attribute_register_1.LRCPC == 0b0001) - features |= CPUFeature::LRCPC; - if (instruction_set_attribute_register_1.LRCPC == 0b0010) - features |= CPUFeature::LRCPC2; - if (instruction_set_attribute_register_1.LRCPC == 0b0011) - features |= CPUFeature::LRCPC3; - if (instruction_set_attribute_register_1.GPA == 0b0001 && instruction_set_attribute_register_1.APA != 0b0000) - features |= CPUFeature::PACQARMA5; - if (instruction_set_attribute_register_1.GPI == 0b0001 && instruction_set_attribute_register_1.API != 0b0000) - features |= CPUFeature::PACIMP; - if (instruction_set_attribute_register_1.FRINTTS == 0b0001) - features |= CPUFeature::FRINTTS; - if (instruction_set_attribute_register_1.SB == 0b0001) - features |= CPUFeature::SB; - if (instruction_set_attribute_register_1.SPECRES == 0b0001) - features |= CPUFeature::SPECRES; - if (instruction_set_attribute_register_1.SPECRES == 0b0010) - features |= CPUFeature::SPECRES2; - if (instruction_set_attribute_register_1.BF16 == 0b0001) - features |= CPUFeature::BF16; - if (instruction_set_attribute_register_1.BF16 == 0b0010) - features |= CPUFeature::EBF16; - if (instruction_set_attribute_register_1.DGH == 0b0001) - features |= CPUFeature::DGH; - if (instruction_set_attribute_register_1.I8MM == 0b0001) - features |= CPUFeature::I8MM; - if (instruction_set_attribute_register_1.XS == 0b0001) - features |= CPUFeature::XS; - if (instruction_set_attribute_register_1.LS64 == 0b0001) - features |= CPUFeature::LS64; - if (instruction_set_attribute_register_1.LS64 == 0b0010) - features |= CPUFeature::LS64_V; - if (instruction_set_attribute_register_1.LS64 == 0b0011) - features |= CPUFeature::LS64_ACCDATA; - if (instruction_set_attribute_register_2.WFxT == 0b0010) - features |= CPUFeature::WFxT; - if (instruction_set_attribute_register_2.RPRES == 0b0001) - features |= CPUFeature::RPRES; - if (instruction_set_attribute_register_2.GPA3 == 0b0001 && instruction_set_attribute_register_2.APA3 == 0b0000) - features |= CPUFeature::PACQARMA3; - if (instruction_set_attribute_register_2.MOPS == 0b0001) - features |= CPUFeature::MOPS; - if (instruction_set_attribute_register_2.BC == 0b0001) - features |= CPUFeature::HBC; - if (instruction_set_attribute_register_2.PAC_frac == 0b0001) - features |= CPUFeature::CONSTPACFIELD; - if (instruction_set_attribute_register_2.CLRBHB == 0b0001) - features |= CPUFeature::CLRBHB; - if (instruction_set_attribute_register_2.SYSREG_128 == 0b0001) - features |= CPUFeature::SYSREG128; - if (instruction_set_attribute_register_2.SYSINSTR_128 == 0b0001) - features |= CPUFeature::SYSINSTR128; - if (instruction_set_attribute_register_2.PRFMSLC == 0b0001) - features |= CPUFeature::PRFMSLC; - if (instruction_set_attribute_register_2.RPRFM == 0b0001) - features |= CPUFeature::RPRFM; - if (instruction_set_attribute_register_2.CSSC == 0b0001) - features |= CPUFeature::CSSC; - if (processor_feature_register_0.FP == 0b0001) - features |= CPUFeature::FP16; - if (processor_feature_register_0.AdvSIMD != 0b0000) - features |= CPUFeature::AdvSIMD; // TODO/FIXME: not explicit? - if (processor_feature_register_0.AdvSIMD == 0b0001) - features |= CPUFeature::FP16; - // TODO: GIC - if (processor_feature_register_0.RAS == 0b0001) - features |= CPUFeature::RAS; - if (processor_feature_register_0.RAS == 0b0010) - features |= CPUFeature::DoubleFault; - if (processor_feature_register_0.RAS == 0b0010) - features |= CPUFeature::RASv1p1; - if (processor_feature_register_0.RAS == 0b0001 && processor_feature_register_1.RAS_frac == 0b0001) - features |= CPUFeature::RASv1p1; - if (processor_feature_register_0.RAS == 0b0011) - features |= CPUFeature::RASv2; - if (processor_feature_register_0.SVE == 0b0001) - features |= CPUFeature::SVE; - if (processor_feature_register_0.SEL2 == 0b0001) - features |= CPUFeature::SEL2; - // TODO: MPAM - if (processor_feature_register_0.AMU == 0b0001) - features |= CPUFeature::AMUv1; - if (processor_feature_register_0.AMU == 0b0010) - features |= CPUFeature::AMUv1p1; - if (processor_feature_register_0.DIT == 0b0001) - features |= CPUFeature::DIT; - if (processor_feature_register_0.RME == 0b0001) - features |= CPUFeature::RME; - if (processor_feature_register_0.CSV2 == 0b0001) - features |= CPUFeature::CSV2; - if (processor_feature_register_0.CSV2 == 0b0010) - features |= CPUFeature::CSV2_2; - if (processor_feature_register_0.CSV2 == 0b0011) - features |= CPUFeature::CSV2_3; - if (processor_feature_register_0.CSV3 == 0b0001) - features |= CPUFeature::CSV3; - if (processor_feature_register_1.BT == 0b0001) - features |= CPUFeature::BTI; - if (processor_feature_register_1.SSBS == 0b0001) - features |= CPUFeature::SSBS; - if (processor_feature_register_1.SSBS == 0b0010) - features |= CPUFeature::SSBS2; - if (processor_feature_register_1.MTE == 0b0001) - features |= CPUFeature::MTE; - if (processor_feature_register_1.MTE == 0b0010) - features |= CPUFeature::MTE2; - if (processor_feature_register_1.MTE == 0b0011) - features |= CPUFeature::MTE3; - if (processor_feature_register_1.MTE >= 0b0010 && processor_feature_register_1.MTEX == 0b0001) { - features |= CPUFeature::MTE4; - features |= CPUFeature::MTE_CANONICAL_TAGS; // FIXME: not really explicit in the spec - features |= CPUFeature::MTE_NO_ADDRESS_TAGS; // FIXME: not really explicit in the spec - } - if (processor_feature_register_1.MTE >= 0b0011 && processor_feature_register_1.MTE_frac == 0b0000) - features |= CPUFeature::MTE_ASYM_FAULT; // FIXME: not really explicit in the spec - if (processor_feature_register_1.SME == 0b0010) - features |= CPUFeature::SME2; - if (processor_feature_register_1.RNDR_trap == 0b0001) - features |= CPUFeature::RNG_TRAP; - if (processor_feature_register_1.CSV2_frac == 0b0001) - features |= CPUFeature::CSV2_1p1; - if (processor_feature_register_1.CSV2_frac == 0b0010) - features |= CPUFeature::CSV2_1p2; - if (processor_feature_register_1.NMI == 0b0001) - features |= CPUFeature::NMI; - if (processor_feature_register_1.GCS == 0b0001) - features |= CPUFeature::GCS; - if (processor_feature_register_1.THE == 0b0001) - features |= CPUFeature::THE; - if (processor_feature_register_1.DF2 == 0b0001) - features |= CPUFeature::DoubleFault2; - if (processor_feature_register_1.PFAR == 0b0001) - features |= CPUFeature::PFAR; - if (memory_model_feature_register_0.PARange == 0b0110) { - features |= translation_control_register.DS == 0b1 ? CPUFeature::LPA2 : CPUFeature::LPA; - } - if (memory_model_feature_register_0.PARange == 0b0111) - features |= CPUFeature::D128; - if (memory_model_feature_register_0.ExS == 0b0001) - features |= CPUFeature::ExS; - if (memory_model_feature_register_0.FGT == 0b0001) - features |= CPUFeature::FGT; - if (memory_model_feature_register_0.FGT == 0b0010) - features |= CPUFeature::FGT2; - if (memory_model_feature_register_0.ECV == 0b0001 || memory_model_feature_register_0.ECV == 0b0010) - features |= CPUFeature::ECV; - if (memory_model_feature_register_1.HAFDBS == 0b0001 || memory_model_feature_register_1.HAFDBS == 0b0010) - features |= CPUFeature::HAFDBS; - if (memory_model_feature_register_1.VMIDBits == 0b0010) - features |= CPUFeature::VMID16; - if (memory_model_feature_register_1.VH == 0b0011) - features |= CPUFeature::HAFT; - if (memory_model_feature_register_1.HPDS == 0b0010) - features |= CPUFeature::HPDS2; - if (memory_model_feature_register_1.LO == 0b0001) - features |= CPUFeature::LOR; - if (memory_model_feature_register_1.PAN == 0b0001) - features |= CPUFeature::PAN; - if (memory_model_feature_register_1.PAN == 0b0010) - features |= CPUFeature::PAN2; - if (memory_model_feature_register_1.PAN == 0b0011) - features |= CPUFeature::PAN3; - if (memory_model_feature_register_1.XNX == 0b0001) - features |= CPUFeature::XNX; - if (memory_model_feature_register_1.TWED == 0b0001) - features |= CPUFeature::TWED; - if (memory_model_feature_register_1.ETS == 0b0001) - features |= CPUFeature::ETS; - if (memory_model_feature_register_1.HCX == 0b0001) - features |= CPUFeature::HCX; - if (memory_model_feature_register_1.AFP == 0b0001) - features |= CPUFeature::AFP; - if (memory_model_feature_register_1.nTLBPA == 0b0001) - features |= CPUFeature::nTLBPA; - if (memory_model_feature_register_1.TIDCP1 == 0b0001) - features |= CPUFeature::TIDCP1; - if (memory_model_feature_register_1.CMOW == 0b0001) - features |= CPUFeature::CMOW; - if (memory_model_feature_register_1.ECBHB == 0b0001) - features |= CPUFeature::ECBHB; - if (memory_model_feature_register_2.CnP == 0b0001) - features |= CPUFeature::TTCNP; - if (memory_model_feature_register_2.UAO == 0b0001) - features |= CPUFeature::UAO; - if (memory_model_feature_register_2.LSM == 0b0001) - features |= CPUFeature::LSMAOC; - if (memory_model_feature_register_2.IESB == 0b0001) - features |= CPUFeature::IESB; - if (memory_model_feature_register_2.VARange == 0b0001) - features |= CPUFeature::LVA; - if (memory_model_feature_register_2.CCIDX == 0b0001) - features |= CPUFeature::CCIDX; - if (memory_model_feature_register_2.NV == 0b0001) - features |= CPUFeature::NV; - if (memory_model_feature_register_2.NV == 0b0010) - features |= CPUFeature::NV2; - if (memory_model_feature_register_2.ST == 0b0001) - features |= CPUFeature::TTST; - if (memory_model_feature_register_2.AT == 0b0001) - features |= CPUFeature::LSE2; - if (memory_model_feature_register_2.IDS == 0b0001) - features |= CPUFeature::IDST; - if (memory_model_feature_register_2.FWB == 0b0001) - features |= CPUFeature::S2FWB; - if (memory_model_feature_register_2.TTL == 0b0001) - features |= CPUFeature::TTL; - if (memory_model_feature_register_2.BBM == 0b0000 || memory_model_feature_register_2.BBM == 0b0001 || memory_model_feature_register_2.BBM == 0b0010) - features |= CPUFeature::BBM; - if (memory_model_feature_register_2.EVT == 0b0001 || memory_model_feature_register_2.EVT == 0b0010) - features |= CPUFeature::EVT; - if (memory_model_feature_register_2.E0PD == 0b0001) { - features |= CPUFeature::E0PD; - features |= CPUFeature::CSV3; - } - if (memory_model_feature_register_3.ADERR == 0b0010 && memory_model_feature_register_3.SDERR == 0b0010) - features |= CPUFeature::ADERR; - if (memory_model_feature_register_3.ANERR == 0b0010 && memory_model_feature_register_3.SNERR == 0b0010) - features |= CPUFeature::ANERR; - if (memory_model_feature_register_3.AIE == 0b0001) - features |= CPUFeature::AIE; - if (memory_model_feature_register_3.MEC == 0b0001) - features |= CPUFeature::MEC; - if (memory_model_feature_register_3.S1PIE == 0b0001) - features |= CPUFeature::S1PIE; - if (memory_model_feature_register_3.S2PIE == 0b0001) - features |= CPUFeature::S2PIE; - if (memory_model_feature_register_3.S1POE == 0b0001) - features |= CPUFeature::S1POE; - if (memory_model_feature_register_3.S2POE == 0b0001) - features |= CPUFeature::S2POE; - if (memory_model_feature_register_3.AIE == 0b0001) - features |= CPUFeature::AIE; - if (memory_model_feature_register_3.MEC == 0b0001) - features |= CPUFeature::MEC; - if (memory_model_feature_register_3.ANERR == 0b0010 && memory_model_feature_register_3.SNERR == 0b0010) - features |= CPUFeature::ANERR; - if (memory_model_feature_register_3.ADERR == 0b0001 && memory_model_feature_register_3.SDERR == 0b0000 && memory_model_feature_register_3.ANERR == 0b0010 && memory_model_feature_register_3.SNERR == 0b0010 && processor_feature_register_0.RAS == 0b0011) - features |= CPUFeature::RASv2; - if (memory_model_feature_register_3.ADERR == 0b0010 && memory_model_feature_register_3.SDERR == 0b0010) - features |= CPUFeature::ADERR; - if (memory_model_feature_register_3.ADERR == 0b0010 && memory_model_feature_register_3.SDERR == 0b0010) - features |= CPUFeature::ADERR; - if (translation_control_register.DS == 0b1) { - features |= CPUFeature::LVA; - } - if (sme_feature_register_0.F16F16 == 0b1) - features |= CPUFeature::SME_F16F16; - if (sme_feature_register_0.F64F64 == 0b1) - features |= CPUFeature::SME_F64F64; - if (sme_feature_register_0.I16I64 == 0b1111) - features |= CPUFeature::SME_I16I64; - if (processor_feature_register_1.SME != 0b0000) { - if (sme_feature_register_0.SMEver == 0b0000) - features |= CPUFeature::SME; - if (sme_feature_register_0.SMEver == 0b0001) - features |= CPUFeature::SME2; - if (sme_feature_register_0.SMEver == 0b0010) - features |= CPUFeature::SME2p1; - if (sme_feature_register_0.FA64 == 0b1) - features |= CPUFeature::SME_FA64; // sve_feature_register_0.I8MM/SM4/SHA3/BitPerm/AES - } - if (sve_feature_register_0.SVEver == 0b0001 && processor_feature_register_1.SME == 0b0001) - features |= CPUFeature::SME; // streaming sve mode only! - if (sve_feature_register_0.SVEver == 0b0001) - features |= CPUFeature::SVE2; // non-streaming sve mode only! - if (sve_feature_register_0.SVEver == 0b0010) - features |= CPUFeature::SVE2p1; // non-streaming sve mode only! - if (sve_feature_register_0.AES == 0b0001) - features |= CPUFeature::SVE_AES; - if (sve_feature_register_0.AES == 0b0010) - features |= CPUFeature::SVE_PMULL128; - if (sve_feature_register_0.BitPerm == 0b0001) - features |= CPUFeature::SVE_BitPerm; - if (sve_feature_register_0.BF16 == 0b0001) - features |= CPUFeature::BF16; - if (sve_feature_register_0.BF16 == 0b0010) - features |= CPUFeature::EBF16; - if (sve_feature_register_0.B16B16 == 0b0001 && sme_feature_register_0.B16B16 == 0b1) - features |= CPUFeature::B16B16; - if (sve_feature_register_0.SHA3 == 0b0001) - features |= CPUFeature::SVE_SHA3; - if (sve_feature_register_0.SM4 == 0b0001) - features |= CPUFeature::SVE_SM4; - if (sve_feature_register_0.I8MM == 0b0001) - features |= CPUFeature::I8MM; - if (sve_feature_register_0.F32MM == 0b0001) - features |= CPUFeature::F32MM; - if (sve_feature_register_0.F64MM == 0b0001) - features |= CPUFeature::F64MM; - if (debug_feature_register_0.DebugVer == 0b1000) - features |= CPUFeature::Debugv8p2; - if (debug_feature_register_0.DebugVer == 0b1001) - features |= CPUFeature::Debugv8p4; - if (debug_feature_register_0.DebugVer == 0b1010) - features |= CPUFeature::Debugv8p8; - if (debug_feature_register_0.DebugVer == 0b0111 && memory_model_feature_register_1.VH == 0b0001) - features |= CPUFeature::VHE; - if (debug_feature_register_0.DebugVer == 0b1101) - features |= CPUFeature::Debugv8p9; - if (debug_feature_register_0.PMUVer == 0b0001) - features |= CPUFeature::PMUv3; - if (debug_feature_register_0.PMUVer == 0b0100) - features |= CPUFeature::PMUv3p1; - if (debug_feature_register_0.PMUVer == 0b0101) - features |= CPUFeature::PMUv3p4; - if (debug_feature_register_0.PMUVer == 0b0110) - features |= CPUFeature::PMUv3p5; - if (debug_feature_register_0.PMUVer == 0b0111) - features |= CPUFeature::PMUv3p7; - if (debug_feature_register_0.PMUVer == 0b1000) - features |= CPUFeature::PMUv3p8; - if (debug_feature_register_0.PMUVer == 0b1001) - features |= CPUFeature::PMUv3p9; - if (debug_feature_register_0.PMSS == 0b0001) - features |= CPUFeature::PMUv3_SS; - if (debug_feature_register_0.SEBEP == 0b0001) - features |= CPUFeature::SEBEP; - if (debug_feature_register_0.PMSVer == 0b0001) - features |= CPUFeature::SPE; - if (debug_feature_register_0.PMSVer == 0b0010) - features |= CPUFeature::SPEv1p1; - if (debug_feature_register_0.PMSVer == 0b0011) - features |= CPUFeature::SPEv1p2; - if (debug_feature_register_0.PMSVer == 0b0100) - features |= CPUFeature::SPEv1p3; - if (debug_feature_register_0.PMSVer == 0b0101) - features |= CPUFeature::SPEv1p4; - if (debug_feature_register_0.PMSVer == 0b0011) - features |= CPUFeature::SPEv1p2; - if (debug_feature_register_0.DoubleLock == 0b0000) - features |= CPUFeature::DoubleLock; - if (debug_feature_register_0.TraceFilt == 0b0001) - features |= CPUFeature::TRF; - if (debug_feature_register_0.TraceBuffer == 0b0001) - features |= CPUFeature::TRBE; - if (debug_feature_register_0.MTPMU == 0b0001) - features |= CPUFeature::MTPMU; // TODO: has additional notes - if (debug_feature_register_0.BRBE == 0b0001) - features |= CPUFeature::BRBE; - if (debug_feature_register_0.BRBE == 0b0010) - features |= CPUFeature::BRBEv1p1; - if (debug_feature_register_0.ExtTrcBuff == 0b0001 && features.has_flag(CPUFeature::TRBE)) // FIXME: order-dependent! - features |= CPUFeature::TRBE_EXT; - if (debug_feature_register_0.HPMN0 == 0b0001) - features |= CPUFeature::HPMN0; - if (debug_feature_register_1.ABLE == 0b0001) - features |= CPUFeature::ABLE; - if (debug_feature_register_1.EBEP == 0b0001) - features |= CPUFeature::EBEP; - if (debug_feature_register_1.ITE == 0b0001) - features |= CPUFeature::ITE; - if (debug_feature_register_1.PMICNTR == 0b0001) - features |= CPUFeature::PMUv3_ICNTR; - if (debug_feature_register_1.SPMU == 0b0001) - features |= CPUFeature::SPMU; - if (debug_feature_register_1.ABLE == 0b0001) - features |= CPUFeature::ABLE; - if (debug_feature_register_1.EBEP == 0b0001) - features |= CPUFeature::EBEP; - if (debug_feature_register_1.ITE == 0b0001) - features |= CPUFeature::ITE; - if (debug_feature_register_1.PMICNTR == 0b0001) - features |= CPUFeature::PMUv3_ICNTR; - if (debug_feature_register_1.SPMU == 0b0001) - features |= CPUFeature::SPMU; - - // negatives - if (sme_feature_register_0.B16B16 == 0b0000) - features &= ~(CPUFeature::SVE2p1 | CPUFeature::SME2p1); - if (sme_feature_register_0.F16F16 == 0b0) - features &= ~CPUFeature::SME2p1; - if (sve_feature_register_0.B16B16 == 0b0000) - features &= ~(CPUFeature::SVE2p1 | CPUFeature::SME2p1); - if (sve_feature_register_0.B16B16 == 0b0001 && sme_feature_register_0.B16B16 == 0b1) - features |= CPUFeature::B16B16; - - return features; -} - -// https://developer.arm.com/downloads/-/exploration-tools/feature-names-for-a-profile -StringView cpu_feature_to_name(CPUFeature::Type const& feature) -{ - // 2022 Architecture Extensions - if (feature == CPUFeature::ABLE) - return "ABLE"sv; - if (feature == CPUFeature::ADERR) - return "ADERR"sv; - if (feature == CPUFeature::ANERR) - return "ANERR"sv; - if (feature == CPUFeature::AIE) - return "AIE"sv; - if (feature == CPUFeature::B16B16) - return "B16B16"sv; - if (feature == CPUFeature::CLRBHB) - return "CLRBHB"sv; - if (feature == CPUFeature::CHK) - return "CHK"sv; - if (feature == CPUFeature::CSSC) - return "CSSC"sv; - if (feature == CPUFeature::CSV2_2) - return "CSV2_2"sv; - if (feature == CPUFeature::CSV2_3) - return "CSV2_3"sv; - if (feature == CPUFeature::D128) - return "D128"sv; - if (feature == CPUFeature::Debugv8p9) - return "Debugv8p9"sv; - if (feature == CPUFeature::DoubleFault2) - return "DoubleFault2"sv; - if (feature == CPUFeature::EBEP) - return "EBEP"sv; - if (feature == CPUFeature::ECBHB) - return "ECBHB"sv; - if (feature == CPUFeature::ETEv1p3) - return "ETEv1p3"sv; - if (feature == CPUFeature::FGT2) - return "FGT2"sv; - if (feature == CPUFeature::GCS) - return "GCS"sv; - if (feature == CPUFeature::HAFT) - return "HAFT"sv; - if (feature == CPUFeature::ITE) - return "ITE"sv; - if (feature == CPUFeature::LRCPC3) - return "LRCPC3"sv; - if (feature == CPUFeature::LSE128) - return "LSE128"sv; - if (feature == CPUFeature::LVA3) - return "LVA3"sv; - if (feature == CPUFeature::MEC) - return "MEC"sv; - if (feature == CPUFeature::MTE4) - return "MTE4"sv; - if (feature == CPUFeature::MTE_CANONICAL_TAGS) - return "MTE_CANONICAL_TAGS"sv; - if (feature == CPUFeature::MTE_TAGGED_FAR) - return "MTE_TAGGED_FAR"sv; - if (feature == CPUFeature::MTE_STORE_ONLY) - return "MTE_STORE_ONLY"sv; - if (feature == CPUFeature::MTE_NO_ADDRESS_TAGS) - return "MTE_NO_ADDRESS_TAGS"sv; - if (feature == CPUFeature::MTE_ASYM_FAULT) - return "MTE_ASYM_FAULT"sv; - if (feature == CPUFeature::MTE_ASYNC) - return "MTE_ASYNC"sv; - if (feature == CPUFeature::MTE_PERM) - return "MTE_PERM"sv; - if (feature == CPUFeature::PCSRv8p9) - return "PCSRv8p9"sv; - if (feature == CPUFeature::PIE) - return "PIE"sv; - if (feature == CPUFeature::POE) - return "POE"sv; - if (feature == CPUFeature::S1PIE) - return "S1PIE"sv; - if (feature == CPUFeature::S2PIE) - return "S2PIE"sv; - if (feature == CPUFeature::S1POE) - return "S1POE"sv; - if (feature == CPUFeature::S2POE) - return "S2POE"sv; - if (feature == CPUFeature::PMUv3p9) - return "PMUv3p9"sv; - if (feature == CPUFeature::PMUv3_EDGE) - return "PMUv3_EDGE"sv; - if (feature == CPUFeature::PMUv3_ICNTR) - return "PMUv3_ICNTR"sv; - if (feature == CPUFeature::PMUv3_SS) - return "PMUv3_SS"sv; - if (feature == CPUFeature::PRFMSLC) - return "PRFMSLC"sv; - if (feature == CPUFeature::PFAR) - return "PFAR"sv; - if (feature == CPUFeature::RASv2) - return "RASv2"sv; - if (feature == CPUFeature::RPZ) - return "RPZ"sv; - if (feature == CPUFeature::RPRFM) - return "RPRFM"sv; - if (feature == CPUFeature::SCTLR2) - return "SCTLR2"sv; - if (feature == CPUFeature::SEBEP) - return "SEBEP"sv; - if (feature == CPUFeature::SME_F16F16) - return "SME_F16F16"sv; - if (feature == CPUFeature::SME2) - return "SME2"sv; - if (feature == CPUFeature::SME2p1) - return "SME2p1"sv; - if (feature == CPUFeature::SPECRES2) - return "SPECRES2"sv; - if (feature == CPUFeature::SPMU) - return "SPMU"sv; - if (feature == CPUFeature::SPEv1p4) - return "SPEv1p4"sv; - if (feature == CPUFeature::SPE_FDS) - return "SPE_FDS"sv; - if (feature == CPUFeature::SVE2p1) - return "SVE2p1"sv; - if (feature == CPUFeature::SYSINSTR128) - return "SYSINSTR128"sv; - if (feature == CPUFeature::SYSREG128) - return "SYSREG128"sv; - if (feature == CPUFeature::TCR2) - return "TCR2"sv; - if (feature == CPUFeature::THE) - return "THE"sv; - if (feature == CPUFeature::TRBE_EXT) - return "TRBE_EXT"sv; - if (feature == CPUFeature::TRBE_MPAM) - return "TRBE_MPAM"sv; - - // 2021 Architecture Extensions - if (feature == CPUFeature::CMOW) - return "CMOW"sv; - if (feature == CPUFeature::CONSTPACFIELD) - return "CONSTPACFIELD"sv; - if (feature == CPUFeature::Debugv8p8) - return "Debugv8p8"sv; - if (feature == CPUFeature::HBC) - return "HBC"sv; - if (feature == CPUFeature::HPMN0) - return "HPMN0"sv; - if (feature == CPUFeature::NMI) - return "NMI"sv; - if (feature == CPUFeature::GICv3_NMI) - return "GICv3_NMI"sv; - if (feature == CPUFeature::MOPS) - return "MOPS"sv; - if (feature == CPUFeature::PACQARMA3) - return "PACQARMA3"sv; - if (feature == CPUFeature::PMUv3_TH) - return "PMUv3_TH"sv; - if (feature == CPUFeature::PMUv3p8) - return "PMUv3p8"sv; - if (feature == CPUFeature::PMUv3_EXT64) - return "PMUv3_EXT64"sv; - if (feature == CPUFeature::PMUv3_EXT32) - return "PMUv3_EXT32"sv; - if (feature == CPUFeature::RNG_TRAP) - return "RNG_TRAP"sv; - if (feature == CPUFeature::SPEv1p3) - return "SPEv1p3"sv; - if (feature == CPUFeature::TIDCP1) - return "TIDCP1"sv; - if (feature == CPUFeature::BRBEv1p1) - return "BRBEv1p1"sv; - - // 2020 Architecture Extensions - if (feature == CPUFeature::AFP) - return "AFP"sv; - if (feature == CPUFeature::HCX) - return "HCX"sv; - if (feature == CPUFeature::LPA2) - return "LPA2"sv; - if (feature == CPUFeature::LS64) - return "LS64"sv; - if (feature == CPUFeature::LS64_V) - return "LS64_V"sv; - if (feature == CPUFeature::LS64_ACCDATA) - return "LS64_ACCDATA"sv; - if (feature == CPUFeature::MTE3) - return "MTE3"sv; - if (feature == CPUFeature::PAN3) - return "PAN3"sv; - if (feature == CPUFeature::PMUv3p7) - return "PMUv3p7"sv; - if (feature == CPUFeature::RPRES) - return "RPRES"sv; - if (feature == CPUFeature::RME) - return "RME"sv; - if (feature == CPUFeature::SME_FA64) - return "SME_FA64"sv; - if (feature == CPUFeature::SME_F64F64) - return "SME_F64F64"sv; - if (feature == CPUFeature::SME_I16I64) - return "SME_I16I64"sv; - if (feature == CPUFeature::EBF16) - return "EBF16"sv; - if (feature == CPUFeature::SPEv1p2) - return "SPEv1p2"sv; - if (feature == CPUFeature::WFxT) - return "WFxT"sv; - if (feature == CPUFeature::XS) - return "XS"sv; - if (feature == CPUFeature::BRBE) - return "BRBE"sv; - - // Features introduced prior to 2020 - if (feature == CPUFeature::AdvSIMD) - return "AdvSIMD"sv; - if (feature == CPUFeature::AES) - return "AES"sv; - if (feature == CPUFeature::PMULL) - return "PMULL"sv; - if (feature == CPUFeature::CP15SDISABLE2) - return "CP15SDISABLE2"sv; - if (feature == CPUFeature::CSV2) - return "CSV2"sv; - if (feature == CPUFeature::CSV2_1p1) - return "CSV2_1p1"sv; - if (feature == CPUFeature::CSV2_1p2) - return "CSV2_1p2"sv; - if (feature == CPUFeature::CSV2) - return "CSV2"sv; - if (feature == CPUFeature::CSV3) - return "CSV3"sv; - if (feature == CPUFeature::DGH) - return "DGH"sv; - if (feature == CPUFeature::DoubleLock) - return "DoubleLock"sv; - if (feature == CPUFeature::ETS) - return "ETS"sv; - if (feature == CPUFeature::FP) - return "FP"sv; - if (feature == CPUFeature::IVIPT) - return "IVIPT"sv; - if (feature == CPUFeature::PCSRv8) - return "PCSRv8"sv; - if (feature == CPUFeature::SPECRES) - return "SPECRES"sv; - if (feature == CPUFeature::RAS) - return "RAS"sv; - if (feature == CPUFeature::SB) - return "SB"sv; - if (feature == CPUFeature::SHA1) - return "SHA1"sv; - if (feature == CPUFeature::SHA256) - return "SHA256"sv; - if (feature == CPUFeature::SSBS) - return "SSBS2"sv; - if (feature == CPUFeature::SSBS2) - return "SSBS2"sv; - if (feature == CPUFeature::CRC32) - return "CRC32"sv; - if (feature == CPUFeature::nTLBPA) - return "nTLBPA"sv; - if (feature == CPUFeature::Debugv8p1) - return "Debugv8p1"sv; - if (feature == CPUFeature::HPDS) - return "HPDS"sv; - if (feature == CPUFeature::LOR) - return "LOR"sv; - if (feature == CPUFeature::LSE) - return "LSE"sv; - if (feature == CPUFeature::PAN) - return "PAN"sv; - if (feature == CPUFeature::PMUv3p1) - return "PMUv3p1"sv; - if (feature == CPUFeature::RDM) - return "RDM"sv; - if (feature == CPUFeature::HAFDBS) - return "HAFDBS"sv; - if (feature == CPUFeature::VHE) - return "VHE"sv; - if (feature == CPUFeature::VMID16) - return "VMID16"sv; - if (feature == CPUFeature::AA32BF16) - return "AA32BF16"sv; - if (feature == CPUFeature::AA32HPD) - return "AA32HPD"sv; - if (feature == CPUFeature::AA32I8MM) - return "AA32I8MM"sv; - if (feature == CPUFeature::PAN2) - return "PAN2"sv; - if (feature == CPUFeature::BF16) - return "BF16"sv; - if (feature == CPUFeature::DPB2) - return "DPB2"sv; - if (feature == CPUFeature::DPB) - return "DPB"sv; - if (feature == CPUFeature::Debugv8p2) - return "Debugv8p2"sv; - if (feature == CPUFeature::DotProd) - return "DotProd"sv; - if (feature == CPUFeature::EVT) - return "EVT"sv; - if (feature == CPUFeature::F32MM) - return "F32MM"sv; - if (feature == CPUFeature::F64MM) - return "F64MM"sv; - if (feature == CPUFeature::FHM) - return "FHM"sv; - if (feature == CPUFeature::FP16) - return "FP16"sv; - if (feature == CPUFeature::I8MM) - return "I8MM"sv; - if (feature == CPUFeature::IESB) - return "IESB"sv; - if (feature == CPUFeature::LPA) - return "LPA"sv; - if (feature == CPUFeature::LSMAOC) - return "LSMAOC"sv; - if (feature == CPUFeature::LVA) - return "LVA"sv; - if (feature == CPUFeature::MPAM) - return "MPAM"sv; - if (feature == CPUFeature::PCSRv8p2) - return "PCSRv8p2"sv; - if (feature == CPUFeature::SHA3) - return "SHA3"sv; - if (feature == CPUFeature::SHA512) - return "SHA512"sv; - if (feature == CPUFeature::SM3) - return "SM3"sv; - if (feature == CPUFeature::SM4) - return "SM4"sv; - if (feature == CPUFeature::SPE) - return "SPE"sv; - if (feature == CPUFeature::SVE) - return "SVE"sv; - if (feature == CPUFeature::TTCNP) - return "TTCNP"sv; - if (feature == CPUFeature::HPDS2) - return "HPDS2"sv; - if (feature == CPUFeature::XNX) - return "XNX"sv; - if (feature == CPUFeature::UAO) - return "UAO"sv; - if (feature == CPUFeature::VPIPT) - return "VPIPT"sv; - if (feature == CPUFeature::CCIDX) - return "CCIDX"sv; - if (feature == CPUFeature::FCMA) - return "FCMA"sv; - if (feature == CPUFeature::DoPD) - return "DoPD"sv; - if (feature == CPUFeature::EPAC) - return "EPAC"sv; - if (feature == CPUFeature::FPAC) - return "FPAC"sv; - if (feature == CPUFeature::FPACCOMBINE) - return "FPACCOMBINE"sv; - if (feature == CPUFeature::JSCVT) - return "JSCVT"sv; - if (feature == CPUFeature::LRCPC) - return "LRCPC"sv; - if (feature == CPUFeature::NV) - return "NV"sv; - if (feature == CPUFeature::PACQARMA5) - return "PACQARMA5"sv; - if (feature == CPUFeature::PACIMP) - return "PACIMP"sv; - if (feature == CPUFeature::PAuth) - return "PAuth"sv; - if (feature == CPUFeature::PAuth2) - return "PAuth2"sv; - if (feature == CPUFeature::SPEv1p1) - return "SPEv1p1"sv; - if (feature == CPUFeature::AMUv1) - return "AMUv1"sv; - if (feature == CPUFeature::CNTSC) - return "CNTSC"sv; - if (feature == CPUFeature::Debugv8p4) - return "Debugv8p4"sv; - if (feature == CPUFeature::DoubleFault) - return "DoubleFault"sv; - if (feature == CPUFeature::DIT) - return "DIT"sv; - if (feature == CPUFeature::FlagM) - return "FlagM"sv; - if (feature == CPUFeature::IDST) - return "IDST"sv; - if (feature == CPUFeature::LRCPC2) - return "LRCPC2"sv; - if (feature == CPUFeature::LSE2) - return "LSE2"sv; - if (feature == CPUFeature::NV2) - return "NV2"sv; - if (feature == CPUFeature::PMUv3p4) - return "PMUv3p4"sv; - if (feature == CPUFeature::RASv1p1) - return "RASv1p1"sv; - if (feature == CPUFeature::S2FWB) - return "S2FWB"sv; - if (feature == CPUFeature::SEL2) - return "SEL2"sv; - if (feature == CPUFeature::TLBIOS) - return "TLBIOS"sv; - if (feature == CPUFeature::TLBIRANGE) - return "TLBIRANGE"sv; - if (feature == CPUFeature::TRF) - return "TRF"sv; - if (feature == CPUFeature::TTL) - return "TTL"sv; - if (feature == CPUFeature::BBM) - return "BBM"sv; - if (feature == CPUFeature::TTST) - return "TTST"sv; - if (feature == CPUFeature::BTI) - return "BTI"sv; - if (feature == CPUFeature::FlagM2) - return "FlagM2"sv; - if (feature == CPUFeature::ExS) - return "ExS"sv; - if (feature == CPUFeature::E0PD) - return "E0PD"sv; - if (feature == CPUFeature::FRINTTS) - return "FRINTTS"sv; - if (feature == CPUFeature::GTG) - return "GTG"sv; - if (feature == CPUFeature::MTE) - return "MTE"sv; - if (feature == CPUFeature::MTE2) - return "MTE2"sv; - if (feature == CPUFeature::PMUv3p5) - return "PMUv3p5"sv; - if (feature == CPUFeature::RNG) - return "RNG"sv; - if (feature == CPUFeature::AMUv1p1) - return "AMUv1p1"sv; - if (feature == CPUFeature::ECV) - return "ECV"sv; - if (feature == CPUFeature::FGT) - return "FGT"sv; - if (feature == CPUFeature::MPAMv0p1) - return "MPAMv0p1"sv; - if (feature == CPUFeature::MPAMv1p1) - return "MPAMv1p1"sv; - if (feature == CPUFeature::MTPMU) - return "MTPMU"sv; - if (feature == CPUFeature::TWED) - return "TWED"sv; - if (feature == CPUFeature::ETMv4) - return "ETMv4"sv; - if (feature == CPUFeature::ETMv4p1) - return "ETMv4p1"sv; - if (feature == CPUFeature::ETMv4p2) - return "ETMv4p2"sv; - if (feature == CPUFeature::ETMv4p3) - return "ETMv4p3"sv; - if (feature == CPUFeature::ETMv4p4) - return "ETMv4p4"sv; - if (feature == CPUFeature::ETMv4p5) - return "ETMv4p5"sv; - if (feature == CPUFeature::ETMv4p6) - return "ETMv4p6"sv; - if (feature == CPUFeature::GICv3) - return "GICv3"sv; - if (feature == CPUFeature::GICv3p1) - return "GICv3p1"sv; - if (feature == CPUFeature::GICv3_LEGACY) - return "GICv3_LEGACY"sv; - if (feature == CPUFeature::GICv3_TDIR) - return "GICv3_TDIR"sv; - if (feature == CPUFeature::GICv4) - return "GICv4"sv; - if (feature == CPUFeature::GICv4p1) - return "GICv4p1"sv; - if (feature == CPUFeature::PMUv3) - return "PMUv3"sv; - if (feature == CPUFeature::ETE) - return "ETE"sv; - if (feature == CPUFeature::ETEv1p1) - return "ETEv1p1"sv; - if (feature == CPUFeature::SVE2) - return "SVE2"sv; - if (feature == CPUFeature::SVE_AES) - return "SVE_AES"sv; - if (feature == CPUFeature::SVE_PMULL128) - return "SVE_PMULL128"sv; - if (feature == CPUFeature::SVE_BitPerm) - return "SVE_BitPerm"sv; - if (feature == CPUFeature::SVE_SHA3) - return "SVE_SHA3"sv; - if (feature == CPUFeature::SVE_SM4) - return "SVE_SM4"sv; - if (feature == CPUFeature::TME) - return "TME"sv; - if (feature == CPUFeature::TRBE) - return "TRBE"sv; - if (feature == CPUFeature::SME) - return "SME"sv; - - VERIFY_NOT_REACHED(); -} - -// https://developer.arm.com/downloads/-/exploration-tools/feature-names-for-a-profile -StringView cpu_feature_to_description(CPUFeature::Type const& feature) -{ - // 2022 Architecture Extensions - if (feature == CPUFeature::ABLE) - return "Address Breakpoint Linking extension"sv; - if (feature == CPUFeature::ADERR) - return "RASv2 Additional Error syndrome reporting, for Device memory"sv; - if (feature == CPUFeature::ANERR) - return "RASv2 Additional Error syndrome reporting, for Normal memory"sv; - if (feature == CPUFeature::AIE) - return "Memory Attribute Index Enhancement"sv; - if (feature == CPUFeature::B16B16) - return "Non-widening BFloat16 to BFloat16 arithmetic for SVE2.1 and SME2.1"sv; - if (feature == CPUFeature::CLRBHB) - return "A new instruction CLRBHB is added in HINT space"sv; - if (feature == CPUFeature::CHK) - return "Detect when Guarded Control Stacks are implemented"sv; - if (feature == CPUFeature::CSSC) - return "Common Short Sequence Compression scalar integer instructions"sv; - if (feature == CPUFeature::CSV2_3) - return "New identification mechanism for Branch History information"sv; - if (feature == CPUFeature::D128) - return "128-bit Translation Tables, 56 bit PA"sv; - if (feature == CPUFeature::Debugv8p9) - return "Debug 2022"sv; - if (feature == CPUFeature::DoubleFault2) - return "Error exception routing extensions"sv; // NOTE: removed trailing dot compared to source! - if (feature == CPUFeature::EBEP) - return "Exception-based event profiling"sv; - if (feature == CPUFeature::ECBHB) - return "Imposes restrictions on branch history speculation around exceptions"sv; - if (feature == CPUFeature::ETEv1p3) - return "ETE support for v9.3"sv; - if (feature == CPUFeature::FGT2) - return "Fine-grained traps 2"sv; - if (feature == CPUFeature::GCS) - return "Guarded Control Stack Extension"sv; - if (feature == CPUFeature::HAFT) - return "Hardware managed Access Flag for Table descriptors"sv; - if (feature == CPUFeature::ITE) - return "Instrumentation trace extension"sv; - if (feature == CPUFeature::LRCPC3) - return "Load-Acquire RCpc instructions version 3"sv; - if (feature == CPUFeature::LSE128) - return "128-bit Atomics"sv; - if (feature == CPUFeature::LVA3) - return "56-bit VA"sv; - if (feature == CPUFeature::MEC) - return "Memory Encryption Contexts"sv; - if (feature == CPUFeature::MTE4) - return "Support for Canonical tag checking, reporting of all non-address bits on a fault, Store-only Tag checking, Memory tagging with Address tagging disabled"sv; - if (feature == CPUFeature::MTE_CANONICAL_TAGS) - return "Support for Canonical tag checking"sv; - if (feature == CPUFeature::MTE_TAGGED_FAR) - return "Support for reporting of all non-address bits on a fault"sv; - if (feature == CPUFeature::MTE_STORE_ONLY) - return "Support for Store-only Tag checking"sv; - if (feature == CPUFeature::MTE_NO_ADDRESS_TAGS) - return "Support for Memory tagging with Address tagging disabled"sv; - if (feature == CPUFeature::MTE_ASYM_FAULT) - return "Asymmetric Tag Check Fault handling"sv; - if (feature == CPUFeature::MTE_ASYNC) - return "Asynchronous Tag Check Fault handling"sv; - if (feature == CPUFeature::MTE_PERM) - return "Allocation tag access permission"sv; - if (feature == CPUFeature::PCSRv8p9) - return "PCSR disable control"sv; - if (feature == CPUFeature::PIE) - return "Permission model enhancements"sv; - if (feature == CPUFeature::POE) - return "Permission model enhancements"sv; - if (feature == CPUFeature::S1PIE) - return "Permission model enhancements"sv; - if (feature == CPUFeature::S2PIE) - return "Permission model enhancements"sv; - if (feature == CPUFeature::S1POE) - return "Permission model enhancements"sv; - if (feature == CPUFeature::S2POE) - return "Permission model enhancements"sv; - if (feature == CPUFeature::PMUv3p9) - return "EL0 access controls for PMU event counters"sv; - if (feature == CPUFeature::PMUv3_EDGE) - return "PMU event edge detection"sv; - if (feature == CPUFeature::PMUv3_ICNTR) - return "PMU instruction counter"sv; - if (feature == CPUFeature::PMUv3_SS) - return "PMU snapshot"sv; - if (feature == CPUFeature::PRFMSLC) - return "Prefetching enhancements"sv; - if (feature == CPUFeature::PFAR) - // https://developer.arm.com/documentation/ddi0602/2022-12/Shared-Pseudocode/Shared-Functions?lang=en - // Library pseudocode for shared/functions/extension/HavePFAR - return "Physical Fault Address Extension (RASv2)"sv; - if (feature == CPUFeature::RASv2) - return "Reliability, Availability, and Serviceability (RAS) Extension version 2"sv; - if (feature == CPUFeature::RPZ) - return "RPZ (RASv2)"sv; // Note: not really known - if (feature == CPUFeature::RPRFM) - return "RPRFM range prefetch hint instruction"sv; - if (feature == CPUFeature::SCTLR2) - return "Extension to SCTLR_ELx"sv; - if (feature == CPUFeature::SEBEP) - return "Synchronous Exception-based event profiling"sv; - if (feature == CPUFeature::SME_F16F16) - return "Non-widening half-precision FP16 to FP16 arithmetic for SME2.1"sv; - if (feature == CPUFeature::SME2) - return "Scalable Matrix Extension version 2"sv; - if (feature == CPUFeature::SME2p1) - return "Scalable Matrix Extension version 2.1"sv; - if (feature == CPUFeature::SPECRES2) - return "Adds new Clear Other Speculative Predictions instruction"sv; - if (feature == CPUFeature::SPMU) - return "System PMU"sv; - if (feature == CPUFeature::SPEv1p4) - return "Additional SPE events"sv; - if (feature == CPUFeature::SPE_FDS) - return "SPE filtering by data source"sv; - if (feature == CPUFeature::SVE2p1) - return "Scalable Vector Extension version SVE2.1"sv; - if (feature == CPUFeature::SYSINSTR128) - return "128-bit System instructions"sv; - if (feature == CPUFeature::SYSREG128) - return "128-bit System registers"sv; - if (feature == CPUFeature::TCR2) - return "Extension to TCR_ELx"sv; - if (feature == CPUFeature::THE) - return "Translation Hardening Extension"sv; - if (feature == CPUFeature::TRBE_EXT) - return "Represents TRBE external mode"sv; - if (feature == CPUFeature::TRBE_MPAM) - return "Trace Buffer MPAM extensions"sv; - - // 2021 Architecture Extensions - if (feature == CPUFeature::CMOW) - return "Control for cache maintenance permission"sv; - if (feature == CPUFeature::CONSTPACFIELD) - return "PAC Algorithm enhancement"sv; - if (feature == CPUFeature::Debugv8p8) - return "Debug v8.8"sv; - if (feature == CPUFeature::HBC) - return "Hinted conditional branches"sv; - if (feature == CPUFeature::HPMN0) - return "Setting of MDCR_EL2.HPMN to zero"sv; - if (feature == CPUFeature::NMI) - return "Non-maskable Interrupts"sv; - if (feature == CPUFeature::GICv3_NMI) - return "Non-maskable Interrupts"sv; - if (feature == CPUFeature::MOPS) - return "Standardization of memory operations"sv; - if (feature == CPUFeature::PACQARMA3) - return "Pointer authentication - QARMA3 algorithm"sv; - if (feature == CPUFeature::PMUv3_TH) - return "Event counting threshold"sv; - if (feature == CPUFeature::PMUv3p8) - return "Armv8.8 PMU Extensions"sv; - if (feature == CPUFeature::PMUv3_EXT64) - return "Optional 64-bit external interface to the Performance Monitors"sv; - if (feature == CPUFeature::PMUv3_EXT32) - return "Represents the original mostly 32-bit external interface to the Performance Monitors"sv; - if (feature == CPUFeature::RNG_TRAP) - return "Trapping support for RNDR and RNDRRS"sv; - if (feature == CPUFeature::SPEv1p3) - return "Armv8.8 Statistical Profiling Extensions"sv; - if (feature == CPUFeature::TIDCP1) - return "EL0 use of IMPLEMENTATION DEFINED functionality"sv; - if (feature == CPUFeature::BRBEv1p1) - return "Branch Record Buffer Extensions version 1.1"sv; - - // 2020 Architecture Extensions - if (feature == CPUFeature::AFP) - return "Alternate floating-point behavior"sv; - if (feature == CPUFeature::HCX) - return "Support for the HCRX_EL2 register"sv; - if (feature == CPUFeature::LPA2) - return "Larger physical address for 4KB and 16KB translation granules"sv; - if (feature == CPUFeature::LS64) - return "Support for 64 byte loads/stores without return"sv; - if (feature == CPUFeature::LS64_V) - return "Support for 64-byte stores with return"sv; - if (feature == CPUFeature::LS64_ACCDATA) - return "Support for 64-byte EL0 stores with return"sv; - if (feature == CPUFeature::MTE3) - return "MTE Asymmetric Fault Handling"sv; - if (feature == CPUFeature::PAN3) - return "Support for SCTLR_ELx.EPAN"sv; - if (feature == CPUFeature::PMUv3p7) - return "Armv8.7 PMU Extensions"sv; - if (feature == CPUFeature::RPRES) - return "Increased precision of Reciprocal Estimate and Reciprocal Square Root Estimate"sv; - if (feature == CPUFeature::RME) - return "Realm Management Extension"sv; - if (feature == CPUFeature::SME_FA64) - return "Additional instructions for the SME Extension"sv; - if (feature == CPUFeature::SME_F64F64) - return "Additional instructions for the SME Extension"sv; - if (feature == CPUFeature::SME_I16I64) - return "Additional instructions for the SME Extension"sv; - if (feature == CPUFeature::EBF16) - return "Additional instructions for the SME Extension"sv; - if (feature == CPUFeature::SPEv1p2) - return "Armv8.7 SPE"sv; - if (feature == CPUFeature::WFxT) - return "WFE and WFI instructions with timeout"sv; - if (feature == CPUFeature::XS) - return "XS attribute"sv; - if (feature == CPUFeature::BRBE) - return "Branch Record Buffer Extensions"sv; - - // Features introduced prior to 2020 - if (feature == CPUFeature::AdvSIMD) - return "Advanced SIMD Extension"sv; - if (feature == CPUFeature::AES) - return "Advanced SIMD AES instructions"sv; - if (feature == CPUFeature::PMULL) - return "Advanced SIMD PMULL instructions"sv; // ARMv8.0-AES is split into AES and PMULL - if (feature == CPUFeature::CP15SDISABLE2) - return "CP15DISABLE2"sv; - if (feature == CPUFeature::CSV2) - return "Cache Speculation Variant 2"sv; - if (feature == CPUFeature::CSV2_1p1) - return "Cache Speculation Variant 2 version 1.1"sv; - if (feature == CPUFeature::CSV2_1p2) - return "Cache Speculation Variant 2 version 1.2"sv; - if (feature == CPUFeature::CSV2_2) - return "Cache Speculation Variant 2 version 2"sv; // NOTE: name mistake in source! - if (feature == CPUFeature::CSV3) - return "Cache Speculation Variant 3"sv; - if (feature == CPUFeature::DGH) - return "Data Gathering Hint"sv; - if (feature == CPUFeature::DoubleLock) - return "Double Lock"sv; - if (feature == CPUFeature::ETS) - return "Enhanced Translation Synchronization"sv; - if (feature == CPUFeature::FP) - return "Floating point extension"sv; - if (feature == CPUFeature::IVIPT) - return "The IVIPT Extension"sv; - if (feature == CPUFeature::PCSRv8) - return "PC Sample-base Profiling extension (not EL3 and EL2)"sv; - if (feature == CPUFeature::SPECRES) - return "Speculation restriction instructions"sv; - if (feature == CPUFeature::RAS) - return "Reliability, Availability, and Serviceability (RAS) Extension"sv; - if (feature == CPUFeature::SB) - return "Speculation barrier"sv; - if (feature == CPUFeature::SHA1) - return "Advanced SIMD SHA1 instructions"sv; - if (feature == CPUFeature::SHA256) - return "Advanced SIMD SHA256 instructions"sv; // ARMv8.2-SHA is split into SHA-256, SHA-512 and SHA-3 - if (feature == CPUFeature::SSBS) - return "Speculative Store Bypass Safe Instruction"sv; // ARMv8.0-SSBS is split into SSBS and SSBS2 - if (feature == CPUFeature::SSBS2) - return "MRS and MSR instructions for SSBS"sv; // ARMv8.0-SSBS is split into SSBS and SSBS2 - if (feature == CPUFeature::CRC32) - return "CRC32 instructions"sv; - if (feature == CPUFeature::nTLBPA) - return "No intermediate caching by output address in TLB"sv; - if (feature == CPUFeature::Debugv8p1) - return "Debug with VHE"sv; - if (feature == CPUFeature::HPDS) - return "Hierarchical permission disables in translation tables"sv; - if (feature == CPUFeature::LOR) - return "Limited ordering regions"sv; - if (feature == CPUFeature::LSE) - return "Large System Extensions"sv; - if (feature == CPUFeature::PAN) - return "Privileged access-never"sv; - if (feature == CPUFeature::PMUv3p1) - return "PMU extensions version 3.1"sv; - if (feature == CPUFeature::RDM) - return "Rounding double multiply accumulate"sv; - if (feature == CPUFeature::HAFDBS) - return "Hardware updates to access flag and dirty state in translation tables"sv; - if (feature == CPUFeature::VHE) - return "Virtualization Host Extensions"sv; - if (feature == CPUFeature::VMID16) - return "16-bit VMID"sv; - if (feature == CPUFeature::AA32BF16) - return "AArch32 BFloat16 instructions"sv; - if (feature == CPUFeature::AA32HPD) - return "AArch32 Hierarchical permission disables"sv; - if (feature == CPUFeature::AA32I8MM) - return "AArch32 Int8 Matrix Multiplication"sv; - if (feature == CPUFeature::PAN2) - return "AT S1E1R and AT S1E1W instruction variants for PAN"sv; - if (feature == CPUFeature::BF16) - return "AArch64 BFloat16 instructions"sv; // NOTE: typo in source! - if (feature == CPUFeature::DPB2) - return "DC CVADP instruction"sv; - if (feature == CPUFeature::DPB) - return "DC CVAP instruction"sv; - if (feature == CPUFeature::Debugv8p2) - return "ARMv8.2 Debug"sv; - if (feature == CPUFeature::DotProd) - return "Advanced SIMD Int8 dot product instructions"sv; - if (feature == CPUFeature::EVT) - return "Enhanced Virtualization Traps"sv; - if (feature == CPUFeature::F32MM) - return "SVE single-precision floating-point matrix multiply instruction"sv; - if (feature == CPUFeature::F64MM) - return "SVE double-precision floating-point matrix multiply instruction"sv; - if (feature == CPUFeature::FHM) - return "Half-precision floating-point FMLAL instructions"sv; - if (feature == CPUFeature::FP16) - return "Half-precision floating-point data processing"sv; - if (feature == CPUFeature::I8MM) - return "Int8 Matrix Multiplication"sv; - if (feature == CPUFeature::IESB) - return "Implicit Error synchronization event"sv; - if (feature == CPUFeature::LPA) - return "Large PA and IPA support"sv; - if (feature == CPUFeature::LSMAOC) - return "Load/Store instruction multiple atomicity and ordering controls"sv; - if (feature == CPUFeature::LVA) - return "Large VA support"sv; - if (feature == CPUFeature::MPAM) - return "Memory Partitioning and Monitoring"sv; - if (feature == CPUFeature::PCSRv8p2) - return "PC Sample-based profiling version 8.2"sv; - if (feature == CPUFeature::SHA3) - return "Advanced SIMD EOR3, RAX1, XAR, and BCAX instructions"sv; // ARMv8.2-SHA is split into SHA-256, SHA-512 and SHA-3 - if (feature == CPUFeature::SHA512) - return "Advanced SIMD SHA512 instructions"sv; // ARMv8.2-SHA is split into SHA-256, SHA-512 and SHA-3 - if (feature == CPUFeature::SM3) - return "Advanced SIMD SM3 instructions"sv; // Split into SM3 and SM4 - if (feature == CPUFeature::SM4) - return "Advanced SIMD SM4 instructions"sv; // Split into SM3 and SM4 - if (feature == CPUFeature::SPE) - return "Statistical Profiling Extension"sv; - if (feature == CPUFeature::SVE) - return "Scalable Vector Extension"sv; - if (feature == CPUFeature::TTCNP) - return "Common not private translations"sv; - if (feature == CPUFeature::HPDS2) - return "Heirarchical permission disables in translation tables 2"sv; - if (feature == CPUFeature::XNX) - return "Execute-never control distinction by Exception level at stage 2"sv; - if (feature == CPUFeature::UAO) - return "Unprivileged Access Override control"sv; - if (feature == CPUFeature::VPIPT) - return "VMID-aware PIPT instruction cache"sv; - if (feature == CPUFeature::CCIDX) - return "Extended cache index"sv; - if (feature == CPUFeature::FCMA) - return "Floating-point FCMLA and FCADD instructions"sv; - if (feature == CPUFeature::DoPD) - return "Debug over Powerdown"sv; - if (feature == CPUFeature::EPAC) - return "Enhanced Pointer authentication"sv; - if (feature == CPUFeature::FPAC) - return "Faulting on pointer authentication instructions"sv; - if (feature == CPUFeature::FPACCOMBINE) - return "Faulting on combined pointer authentication instructions"sv; - if (feature == CPUFeature::JSCVT) - return "JavaScript FJCVTS conversion instruction"sv; - if (feature == CPUFeature::LRCPC) - return "Load-acquire RCpc instructions"sv; - if (feature == CPUFeature::NV) - return "Nested virtualization"sv; - if (feature == CPUFeature::PACQARMA5) - return "Pointer authentication - QARMA5 algorithm"sv; - if (feature == CPUFeature::PACIMP) - return "Pointer authentication - IMPLEMENTATION DEFINED algorithm"sv; - if (feature == CPUFeature::PAuth) - return "Pointer authentication"sv; - if (feature == CPUFeature::PAuth2) - return "Enhancements to pointer authentication"sv; - if (feature == CPUFeature::SPEv1p1) - return "Statistical Profiling Extensions version 1.1"sv; - if (feature == CPUFeature::AMUv1) - return "Activity Monitors Extension"sv; - if (feature == CPUFeature::CNTSC) - return "Generic Counter Scaling"sv; - if (feature == CPUFeature::Debugv8p4) - return "Debug relaxations and extensions version 8.4"sv; - if (feature == CPUFeature::DoubleFault) - return "Double Fault Extension"sv; - if (feature == CPUFeature::DIT) - return "Data Independent Timing instructions"sv; - if (feature == CPUFeature::FlagM) - return "Condition flag manipulation"sv; - if (feature == CPUFeature::IDST) - return "ID space trap handling"sv; - if (feature == CPUFeature::LRCPC2) - return "Load-acquire RCpc instructions version 2"sv; - if (feature == CPUFeature::LSE2) - return "Large System Extensions version 2"sv; - if (feature == CPUFeature::NV2) - return "Enhanced support for nested virtualization"sv; - if (feature == CPUFeature::PMUv3p4) - return "PMU extension version 3.4"sv; - if (feature == CPUFeature::RASv1p1) - return "Reliability, Availability, and Serviceability (RAS) Extension version 1.1"sv; - if (feature == CPUFeature::S2FWB) - return "Stage 2 forced write-back"sv; - if (feature == CPUFeature::SEL2) - return "Secure EL2"sv; - if (feature == CPUFeature::TLBIOS) - return "TLB invalidate outer-shared instructions"sv; // Split into TLBIOS and TLBIRANGE - if (feature == CPUFeature::TLBIRANGE) - return "TLB range invalidate range instructions"sv; // Split into TLBIOS and TLBIRANGE - if (feature == CPUFeature::TRF) - return "Self hosted Trace Extensions"sv; - if (feature == CPUFeature::TTL) - return "Translation Table Level"sv; - if (feature == CPUFeature::BBM) - return "Translation table break before make levels"sv; - if (feature == CPUFeature::TTST) - return "Small translation tables"sv; - if (feature == CPUFeature::BTI) - return "Branch target identification"sv; - if (feature == CPUFeature::FlagM2) - return "Condition flag manipulation version 2"sv; - if (feature == CPUFeature::ExS) - return "Disabling context synchronizing exception entry and exit"sv; - if (feature == CPUFeature::E0PD) - return "Preventing EL0 access to halves of address maps"sv; - if (feature == CPUFeature::FRINTTS) - return "FRINT32Z, FRINT32X, FRINT64Z, and FRINT64X instructions"sv; - if (feature == CPUFeature::GTG) - return "Guest translation granule size"sv; - if (feature == CPUFeature::MTE) - return "Instruction-only Memory Tagging Extension"sv; - if (feature == CPUFeature::MTE2) - return "Full Memory Tagging Extension"sv; - if (feature == CPUFeature::PMUv3p5) - return "PMU Extension version 3.5"sv; - if (feature == CPUFeature::RNG) - return "Random number generator"sv; - if (feature == CPUFeature::AMUv1p1) - return "Activity Monitors Extension version 1.1"sv; - if (feature == CPUFeature::ECV) - return "Enhanced counter virtualization"sv; - if (feature == CPUFeature::FGT) - return "Fine Grain Traps"sv; - if (feature == CPUFeature::MPAMv0p1) - return "Memory Partitioning and Monitoring version 0.1"sv; - if (feature == CPUFeature::MPAMv1p1) - return "Memory Partitioning and Monitoring version 1.1"sv; - if (feature == CPUFeature::MTPMU) - return "Multi-threaded PMU Extensions"sv; - if (feature == CPUFeature::TWED) - return "Delayed trapping of WFE"sv; - if (feature == CPUFeature::ETMv4) - return "Embedded Trace Macrocell version4"sv; - if (feature == CPUFeature::ETMv4p1) - return "Embedded Trace Macrocell version 4.1"sv; - if (feature == CPUFeature::ETMv4p2) - return "Embedded Trace Macrocell version 4.2"sv; - if (feature == CPUFeature::ETMv4p3) - return "Embedded Trace Macrocell version 4.3"sv; - if (feature == CPUFeature::ETMv4p4) - return "Embedded Trace Macrocell version 4.3"sv; - if (feature == CPUFeature::ETMv4p5) - return "Embedded Trace Macrocell version 4.4"sv; - if (feature == CPUFeature::ETMv4p6) - return "Embedded Trace Macrocell version 4.5"sv; - if (feature == CPUFeature::GICv3) - return "Generic Interrupt Controller version 3"sv; - if (feature == CPUFeature::GICv3p1) - return "Generic Interrupt Controller version 3.1"sv; - if (feature == CPUFeature::GICv3_LEGACY) - return "Support for GICv2 legacy operation"sv; // Note: missing in source - if (feature == CPUFeature::GICv3_TDIR) - return "Trapping Non-secure EL1 writes to ICV_DIR"sv; - if (feature == CPUFeature::GICv4) - return "Generic Interrupt Controller version 4"sv; - if (feature == CPUFeature::GICv4p1) - return "Generic Interrupt Controller version 4.1"sv; - if (feature == CPUFeature::PMUv3) - return "PMU extension version 3"sv; - if (feature == CPUFeature::ETE) - return "Embedded Trace Extension"sv; - if (feature == CPUFeature::ETEv1p1) - return "Embedded Trace Extension, version 1.1"sv; - if (feature == CPUFeature::SVE2) - return "SVE version 2"sv; - if (feature == CPUFeature::SVE_AES) - return "SVE AES instructions"sv; - if (feature == CPUFeature::SVE_PMULL128) - return "SVE PMULL instructions"sv; // SVE2-AES is split into AES and PMULL support - if (feature == CPUFeature::SVE_BitPerm) - return "SVE Bit Permute"sv; - if (feature == CPUFeature::SVE_SHA3) - return "SVE SHA-3 instructions"sv; - if (feature == CPUFeature::SVE_SM4) - return "SVE SM4 instructions"sv; - if (feature == CPUFeature::TME) - return "Transactional Memory Extension"sv; - if (feature == CPUFeature::TRBE) - return "Trace Buffer Extension"sv; - if (feature == CPUFeature::SME) - return "Scalable Matrix Extension"sv; - - VERIFY_NOT_REACHED(); -} - -NonnullOwnPtr build_cpu_feature_names(CPUFeature::Type const& features) -{ - StringBuilder builder; - bool first = true; - for (auto feature = CPUFeature::Type(1u); feature != CPUFeature::__End; feature <<= 1u) { - if (features.has_flag(feature)) { - if (first) - first = false; - else - MUST(builder.try_append(' ')); - auto name = cpu_feature_to_name(feature); - MUST(builder.try_append(name)); - } - } - return KString::must_create(builder.string_view()); -} - -u8 detect_physical_address_bit_width() -{ - auto memory_model_feature_register_0 = Aarch64::ID_AA64MMFR0_EL1::read(); - - switch (memory_model_feature_register_0.PARange) { - case 0b0000: - return 32; // 4GB - case 0b0001: - return 36; // 64GB - case 0b0010: - return 40; // 1TB - case 0b0011: - return 42; // 4TB - case 0b0100: - return 44; // 16TB - case 0b0101: - return 48; // 256TB - case 0b0110: - return 52; // 4PB (applies for FEAT_LPA or FEAT_LPA2) - case 0b0111: - return 56; // 64PB (applies for FEAT_D128) - default: - VERIFY_NOT_REACHED(); - } -} - -u8 detect_virtual_address_bit_width() -{ - auto memory_model_feature_register_2 = Aarch64::ID_AA64MMFR2_EL1::read(); - - switch (memory_model_feature_register_2.VARange) { - case 0b0000: - return 48; // 256TB - case 0b0001: - return 52; // 4PB (only for 64KB translation granule) - case 0b0010: - return 56; // 64PB (applies for FEAT_D128) - default: - VERIFY_NOT_REACHED(); - } -} - -} diff --git a/Kernel/Arch/aarch64/CPUID.h b/Kernel/Arch/aarch64/CPUID.h deleted file mode 100644 index edbb53047a8..00000000000 --- a/Kernel/Arch/aarch64/CPUID.h +++ /dev/null @@ -1,283 +0,0 @@ -/* - * Copyright (c) 2023, Konrad - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include -#include - -#include -VALIDATE_IS_AARCH64() - -namespace Kernel { - -// https://developer.arm.com/downloads/-/exploration-tools/feature-names-for-a-profile -AK_MAKE_ARBITRARY_SIZED_ENUM(CPUFeature, u256, - // 2022 Architecture Extensions - ABLE = CPUFeature(1u) << 0u, // Address Breakpoint Linking extension - ADERR = CPUFeature(1u) << 1u, // RASv2 Additional Error syndrome reporting, for Device memory - ANERR = CPUFeature(1u) << 2u, // RASv2 Additional Error syndrome reporting, for Normal memory - AIE = CPUFeature(1u) << 3u, // Memory Attribute Index Enhancement - B16B16 = CPUFeature(1u) << 4u, // Non-widening BFloat16 to BFloat16 arithmetic for SVE2.1 and SME2.1 - CLRBHB = CPUFeature(1u) << 5u, // A new instruction CLRBHB is added in HINT space - CHK = CPUFeature(1u) << 6u, // Detect when Guarded Control Stacks are implemented - CSSC = CPUFeature(1u) << 7u, // Common Short Sequence Compression scalar integer instructions - CSV2_3 = CPUFeature(1u) << 8u, // New identification mechanism for Branch History information - D128 = CPUFeature(1u) << 9u, // 128-bit Translation Tables, 56 bit PA - Debugv8p9 = CPUFeature(1u) << 10u, // Debug 2022 - DoubleFault2 = CPUFeature(1u) << 11u, // Error exception routing extensions. - EBEP = CPUFeature(1u) << 12u, // Exception-based event profiling - ECBHB = CPUFeature(1u) << 13u, // Imposes restrictions on branch history speculation around exceptions - ETEv1p3 = CPUFeature(1u) << 14u, // ETE support for v9.3 - FGT2 = CPUFeature(1u) << 15u, // Fine-grained traps 2 - GCS = CPUFeature(1u) << 16u, // Guarded Control Stack Extension - HAFT = CPUFeature(1u) << 17u, // Hardware managed Access Flag for Table descriptors - ITE = CPUFeature(1u) << 18u, // Instrumentation trace extension - LRCPC3 = CPUFeature(1u) << 19u, // Load-Acquire RCpc instructions version 3 - LSE128 = CPUFeature(1u) << 20u, // 128-bit Atomics - LVA3 = CPUFeature(1u) << 21u, // 56-bit VA - MEC = CPUFeature(1u) << 22u, // Memory Encryption Contexts - MTE4 = CPUFeature(1u) << 23u, // Support for Canonical tag checking, reporting of all non-address bits on a fault, Store-only Tag checking, Memory tagging with Address tagging disabled - MTE_CANONICAL_TAGS = CPUFeature(1u) << 24u, // Support for Canonical tag checking - MTE_TAGGED_FAR = CPUFeature(1u) << 25u, // Support for reporting of all non-address bits on a fault - MTE_STORE_ONLY = CPUFeature(1u) << 26u, // Support for Store-only Tag checking - MTE_NO_ADDRESS_TAGS = CPUFeature(1u) << 27u, // Support for Memory tagging with Address tagging disabled - MTE_ASYM_FAULT = CPUFeature(1u) << 28u, // Asymmetric Tag Check Fault handling - MTE_ASYNC = CPUFeature(1u) << 29u, // Asynchronous Tag Check Fault handling - MTE_PERM = CPUFeature(1u) << 30u, // Allocation tag access permission - PCSRv8p9 = CPUFeature(1u) << 31u, // PCSR disable control - PIE = CPUFeature(1u) << 32u, // Permission model enhancements - POE = CPUFeature(1u) << 33u, // Permission model enhancements - S1PIE = CPUFeature(1u) << 34u, // Permission model enhancements - S2PIE = CPUFeature(1u) << 35u, // Permission model enhancements - S1POE = CPUFeature(1u) << 36u, // Permission model enhancements - S2POE = CPUFeature(1u) << 37u, // Permission model enhancements - PMUv3p9 = CPUFeature(1u) << 38u, // EL0 access controls for PMU event counters - PMUv3_EDGE = CPUFeature(1u) << 39u, // PMU event edge detection - PMUv3_ICNTR = CPUFeature(1u) << 40u, // PMU instruction counter - PMUv3_SS = CPUFeature(1u) << 41u, // PMU snapshot - PRFMSLC = CPUFeature(1u) << 42u, // Prefetching enhancements - PFAR = CPUFeature(1u) << 43u, // Physical Fault Address Extension [NOTE: not yet listed] - RASv2 = CPUFeature(1u) << 44u, // Reliability, Availability, and Serviceability (RAS) Extension version 2 - RPZ = CPUFeature(1u) << 45u, // ? [NOTE: not yet listed] - RPRFM = CPUFeature(1u) << 46u, // RPRFM range prefetch hint instruction - SCTLR2 = CPUFeature(1u) << 47u, // Extension to SCTLR_ELx - SEBEP = CPUFeature(1u) << 48u, // Synchronous Exception-based event profiling - SME_F16F16 = CPUFeature(1u) << 49u, // Non-widening half-precision FP16 to FP16 arithmetic for SME2.1 - SME2 = CPUFeature(1u) << 50u, // Scalable Matrix Extension version 2 - SME2p1 = CPUFeature(1u) << 51u, // Scalable Matrix Extension version 2.1 - SPECRES2 = CPUFeature(1u) << 52u, // Adds new Clear Other Speculative Predictions instruction - SPMU = CPUFeature(1u) << 53u, // System PMU - SPEv1p4 = CPUFeature(1u) << 54u, // Additional SPE events - SPE_FDS = CPUFeature(1u) << 55u, // SPE filtering by data source - SVE2p1 = CPUFeature(1u) << 56u, // Scalable Vector Extension version SVE2.1 - SYSINSTR128 = CPUFeature(1u) << 57u, // 128-bit System instructions - SYSREG128 = CPUFeature(1u) << 58u, // 128-bit System registers - TCR2 = CPUFeature(1u) << 59u, // Extension to TCR_ELx - THE = CPUFeature(1u) << 60u, // Translation Hardening Extension - TRBE_EXT = CPUFeature(1u) << 61u, // Represents TRBE external mode - TRBE_MPAM = CPUFeature(1u) << 62u, // Trace Buffer MPAM extensions - - // 2021 Architecture Extensions - CMOW = CPUFeature(1u) << 63u, // Control for cache maintenance permission - CONSTPACFIELD = CPUFeature(1u) << 64u, // PAC Algorithm enhancement - Debugv8p8 = CPUFeature(1u) << 65u, // Debug v8.8 - HBC = CPUFeature(1u) << 66u, // Hinted conditional branches - HPMN0 = CPUFeature(1u) << 67u, // Setting of MDCR_EL2.HPMN to zero - NMI = CPUFeature(1u) << 68u, // Non-maskable Interrupts - GICv3_NMI = CPUFeature(1u) << 69u, // Non-maskable Interrupts - MOPS = CPUFeature(1u) << 70u, // Standardization of memory operations - PACQARMA3 = CPUFeature(1u) << 71u, // Pointer authentication - QARMA3 algorithm - PMUv3_TH = CPUFeature(1u) << 72u, // Event counting threshold - PMUv3p8 = CPUFeature(1u) << 73u, // Armv8.8 PMU Extensions - PMUv3_EXT64 = CPUFeature(1u) << 74u, // Optional 64-bit external interface to the Performance Monitors - PMUv3_EXT32 = CPUFeature(1u) << 75u, // Represents the original mostly 32-bit external interface to the Performance Monitors - RNG_TRAP = CPUFeature(1u) << 76u, // Trapping support for RNDR and RNDRRS - SPEv1p3 = CPUFeature(1u) << 77u, // Armv8.8 Statistical Profiling Extensions - TIDCP1 = CPUFeature(1u) << 78u, // EL0 use of IMPLEMENTATION DEFINED functionality - BRBEv1p1 = CPUFeature(1u) << 79u, // Branch Record Buffer Extensions version 1.1 - - // 2020 Architecture Extensions - AFP = CPUFeature(1u) << 80u, // Alternate floating-point behavior - HCX = CPUFeature(1u) << 81u, // Support for the HCRX_EL2 register - LPA2 = CPUFeature(1u) << 82u, // Larger physical address for 4KB and 16KB translation granules - LS64 = CPUFeature(1u) << 83u, // Support for 64 byte loads/stores without return - LS64_V = CPUFeature(1u) << 84u, // Support for 64-byte stores with return - LS64_ACCDATA = CPUFeature(1u) << 85u, // Support for 64-byte EL0 stores with return - MTE3 = CPUFeature(1u) << 86u, // MTE Asymmetric Fault Handling - PAN3 = CPUFeature(1u) << 87u, // Support for SCTLR_ELx.EPAN - PMUv3p7 = CPUFeature(1u) << 88u, // Armv8.7 PMU Extensions - RPRES = CPUFeature(1u) << 89u, // Increased precision of Reciprocal Estimate and Reciprocal Square Root Estimate - RME = CPUFeature(1u) << 90u, // Realm Management Extension - SME_FA64 = CPUFeature(1u) << 91u, // Additional instructions for the SME Extension - SME_F64F64 = CPUFeature(1u) << 92u, // Additional instructions for the SME Extension - SME_I16I64 = CPUFeature(1u) << 93u, // Additional instructions for the SME Extension - EBF16 = CPUFeature(1u) << 94u, // Additional instructions for the SME Extension - SPEv1p2 = CPUFeature(1u) << 95u, // Armv8.7 SPE - WFxT = CPUFeature(1u) << 96u, // WFE and WFI instructions with timeout - XS = CPUFeature(1u) << 97u, // XS attribute - BRBE = CPUFeature(1u) << 98u, // Branch Record Buffer Extensions - - // Features introduced prior to 2020 - AdvSIMD = CPUFeature(1u) << 99u, // Advanced SIMD Extension - AES = CPUFeature(1u) << 100u, // Advanced SIMD AES instructions - PMULL = CPUFeature(1u) << 101u, // Advanced SIMD PMULL instructions; ARMv8.0-AES is split into AES and PMULL - CP15SDISABLE2 = CPUFeature(1u) << 102u, // CP15DISABLE2 - CSV2 = CPUFeature(1u) << 103u, // Cache Speculation Variant 2 - CSV2_1p1 = CPUFeature(1u) << 104u, // Cache Speculation Variant 2 version 1.1 - CSV2_1p2 = CPUFeature(1u) << 105u, // Cache Speculation Variant 2 version 1.2 - CSV2_2 = CPUFeature(1u) << 106u, // Cache Speculation Variant 2 version 2 [NOTE: name mistake in source!] - CSV3 = CPUFeature(1u) << 107u, // Cache Speculation Variant 3 - DGH = CPUFeature(1u) << 108u, // Data Gathering Hint - DoubleLock = CPUFeature(1u) << 109u, // Double Lock - ETS = CPUFeature(1u) << 110u, // Enhanced Translation Synchronization - FP = CPUFeature(1u) << 111u, // Floating point extension - IVIPT = CPUFeature(1u) << 112u, // The IVIPT Extension - PCSRv8 = CPUFeature(1u) << 113u, // PC Sample-base Profiling extension (not EL3 and EL2) - SPECRES = CPUFeature(1u) << 114u, // Speculation restriction instructions - RAS = CPUFeature(1u) << 115u, // Reliability, Availability, and Serviceability (RAS) Extension - SB = CPUFeature(1u) << 116u, // Speculation barrier - SHA1 = CPUFeature(1u) << 117u, // Advanced SIMD SHA1 instructions - SHA256 = CPUFeature(1u) << 118u, // Advanced SIMD SHA256 instructions; Split ARMv8.2-SHA into SHA-256, SHA-512 and SHA-3 - SSBS = CPUFeature(1u) << 119u, // Speculative Store Bypass Safe Instruction; ARMv8.0-SSBS is split into SSBS and SSBS2 - SSBS2 = CPUFeature(1u) << 120u, // MRS and MSR instructions for SSBS; ARMv8.0-SSBS is split into SSBS and SSBS2 - CRC32 = CPUFeature(1u) << 121u, // CRC32 instructions - nTLBPA = CPUFeature(1u) << 122u, // No intermediate caching by output address in TLB - Debugv8p1 = CPUFeature(1u) << 123u, // Debug with VHE - HPDS = CPUFeature(1u) << 124u, // Hierarchical permission disables in translation tables - LOR = CPUFeature(1u) << 125u, // Limited ordering regions - LSE = CPUFeature(1u) << 126u, // Large System Extensions - PAN = CPUFeature(1u) << 127u, // Privileged access-never - PMUv3p1 = CPUFeature(1u) << 128u, // PMU extensions version 3.1 - RDM = CPUFeature(1u) << 129u, // Rounding double multiply accumulate - HAFDBS = CPUFeature(1u) << 130u, // Hardware updates to access flag and dirty state in translation tables - VHE = CPUFeature(1u) << 131u, // Virtualization Host Extensions - VMID16 = CPUFeature(1u) << 132u, // 16-bit VMID - AA32BF16 = CPUFeature(1u) << 133u, // AArch32 BFloat16 instructions - AA32HPD = CPUFeature(1u) << 134u, // AArch32 Hierarchical permission disables - AA32I8MM = CPUFeature(1u) << 135u, // AArch32 Int8 Matrix Multiplication - PAN2 = CPUFeature(1u) << 136u, // AT S1E1R and AT S1E1W instruction variants for PAN - BF16 = CPUFeature(1u) << 137u, // AARch64 BFloat16 instructions - DPB2 = CPUFeature(1u) << 138u, // DC CVADP instruction - DPB = CPUFeature(1u) << 139u, // DC CVAP instruction - Debugv8p2 = CPUFeature(1u) << 140u, // ARMv8.2 Debug - DotProd = CPUFeature(1u) << 141u, // Advanced SIMD Int8 dot product instructions - EVT = CPUFeature(1u) << 142u, // Enhanced Virtualization Traps - F32MM = CPUFeature(1u) << 143u, // SVE single-precision floating-point matrix multiply instruction - F64MM = CPUFeature(1u) << 144u, // SVE double-precision floating-point matrix multiply instruction - FHM = CPUFeature(1u) << 145u, // Half-precision floating-point FMLAL instructions - FP16 = CPUFeature(1u) << 146u, // Half-precision floating-point data processing - I8MM = CPUFeature(1u) << 147u, // Int8 Matrix Multiplication - IESB = CPUFeature(1u) << 148u, // Implicit Error synchronization event - LPA = CPUFeature(1u) << 149u, // Large PA and IPA support - LSMAOC = CPUFeature(1u) << 150u, // Load/Store instruction multiple atomicity and ordering controls - LVA = CPUFeature(1u) << 151u, // Large VA support - MPAM = CPUFeature(1u) << 152u, // Memory Partitioning and Monitoring - PCSRv8p2 = CPUFeature(1u) << 153u, // PC Sample-based profiling version 8.2 - SHA3 = CPUFeature(1u) << 154u, // Advanced SIMD EOR3, RAX1, XAR, and BCAX instructions; Split ARMv8.2-SHA into SHA-256, SHA-512 and SHA-3 - SHA512 = CPUFeature(1u) << 155u, // Advanced SIMD SHA512 instructions; Split ARMv8.2-SHA into SHA-256, SHA-512 and SHA-3 - SM3 = CPUFeature(1u) << 156u, // Advanced SIMD SM3 instructions; Split into SM3 and SM4 - SM4 = CPUFeature(1u) << 157u, // Advanced SIMD SM4 instructions; Split into SM3 and SM4 - SPE = CPUFeature(1u) << 158u, // Statistical Profiling Extension - SVE = CPUFeature(1u) << 159u, // Scalable Vector Extension - TTCNP = CPUFeature(1u) << 160u, // Common not private translations - HPDS2 = CPUFeature(1u) << 161u, // Heirarchical permission disables in translation tables 2 - XNX = CPUFeature(1u) << 162u, // Execute-never control distinction by Exception level at stage 2 - UAO = CPUFeature(1u) << 163u, // Unprivileged Access Override control - VPIPT = CPUFeature(1u) << 164u, // VMID-aware PIPT instruction cache - CCIDX = CPUFeature(1u) << 165u, // Extended cache index - FCMA = CPUFeature(1u) << 166u, // Floating-point FCMLA and FCADD instructions - DoPD = CPUFeature(1u) << 167u, // Debug over Powerdown - EPAC = CPUFeature(1u) << 168u, // Enhanced Pointer authentication - FPAC = CPUFeature(1u) << 169u, // Faulting on pointer authentication instructions - FPACCOMBINE = CPUFeature(1u) << 170u, // Faulting on combined pointer authentication instructions - JSCVT = CPUFeature(1u) << 171u, // JavaScript FJCVTS conversion instruction - LRCPC = CPUFeature(1u) << 172u, // Load-acquire RCpc instructions - NV = CPUFeature(1u) << 173u, // Nested virtualization - PACQARMA5 = CPUFeature(1u) << 174u, // Pointer authentication - QARMA5 algorithm - PACIMP = CPUFeature(1u) << 175u, // Pointer authentication - IMPLEMENTATION DEFINED algorithm - PAuth = CPUFeature(1u) << 176u, // Pointer authentication - PAuth2 = CPUFeature(1u) << 177u, // Enhancements to pointer authentication - SPEv1p1 = CPUFeature(1u) << 178u, // Statistical Profiling Extensions version 1.1 - AMUv1 = CPUFeature(1u) << 179u, // Activity Monitors Extension - CNTSC = CPUFeature(1u) << 180u, // Generic Counter Scaling - Debugv8p4 = CPUFeature(1u) << 181u, // Debug relaxations and extensions version 8.4 - DoubleFault = CPUFeature(1u) << 182u, // Double Fault Extension - DIT = CPUFeature(1u) << 183u, // Data Independent Timing instructions - FlagM = CPUFeature(1u) << 184u, // Condition flag manipulation - IDST = CPUFeature(1u) << 185u, // ID space trap handling - LRCPC2 = CPUFeature(1u) << 186u, // Load-acquire RCpc instructions version 2 - LSE2 = CPUFeature(1u) << 187u, // Large System Extensions version 2 - NV2 = CPUFeature(1u) << 188u, // Enhanced support for nested virtualization - PMUv3p4 = CPUFeature(1u) << 189u, // PMU extension version 3.4 - RASv1p1 = CPUFeature(1u) << 190u, // Reliability, Availability, and Serviceability (RAS) Extension version 1.1 - S2FWB = CPUFeature(1u) << 191u, // Stage 2 forced write-back - SEL2 = CPUFeature(1u) << 192u, // Secure EL2 - TLBIOS = CPUFeature(1u) << 193u, // TLB invalidate outer-shared instructions; Split into TLBIOS and TLBIRANGE - TLBIRANGE = CPUFeature(1u) << 194u, // TLB range invalidate range instructions; Split into TLBIOS and TLBIRANGE - TRF = CPUFeature(1u) << 195u, // Self hosted Trace Extensions - TTL = CPUFeature(1u) << 196u, // Translation Table Level - BBM = CPUFeature(1u) << 197u, // Translation table break before make levels - TTST = CPUFeature(1u) << 198u, // Small translation tables - BTI = CPUFeature(1u) << 199u, // Branch target identification - FlagM2 = CPUFeature(1u) << 200u, // Condition flag manipulation version 2 - ExS = CPUFeature(1u) << 201u, // Disabling context synchronizing exception entry and exit - E0PD = CPUFeature(1u) << 202u, // Preventing EL0 access to halves of address maps - FRINTTS = CPUFeature(1u) << 203u, // FRINT32Z, FRINT32X, FRINT64Z, and FRINT64X instructions - GTG = CPUFeature(1u) << 204u, // Guest translation granule size - MTE = CPUFeature(1u) << 205u, // Instruction-only Memory Tagging Extension - MTE2 = CPUFeature(1u) << 206u, // Full Memory Tagging Extension - PMUv3p5 = CPUFeature(1u) << 207u, // PMU Extension version 3.5 - RNG = CPUFeature(1u) << 208u, // Random number generator - AMUv1p1 = CPUFeature(1u) << 209u, // Activity Monitors Extension version 1.1 - ECV = CPUFeature(1u) << 210u, // Enhanced counter virtualization - FGT = CPUFeature(1u) << 211u, // Fine Grain Traps - MPAMv0p1 = CPUFeature(1u) << 212u, // Memory Partitioning and Monitoring version 0.1 - MPAMv1p1 = CPUFeature(1u) << 213u, // Memory Partitioning and Monitoring version 1.1 - MTPMU = CPUFeature(1u) << 214u, // Multi-threaded PMU Extensions - TWED = CPUFeature(1u) << 215u, // Delayed trapping of WFE - ETMv4 = CPUFeature(1u) << 216u, // Embedded Trace Macrocell version4 - ETMv4p1 = CPUFeature(1u) << 217u, // Embedded Trace Macrocell version 4.1 - ETMv4p2 = CPUFeature(1u) << 218u, // Embedded Trace Macrocell version 4.2 - ETMv4p3 = CPUFeature(1u) << 219u, // Embedded Trace Macrocell version 4.3 - ETMv4p4 = CPUFeature(1u) << 220u, // Embedded Trace Macrocell version 4.3 - ETMv4p5 = CPUFeature(1u) << 221u, // Embedded Trace Macrocell version 4.4 - ETMv4p6 = CPUFeature(1u) << 222u, // Embedded Trace Macrocell version 4.5 - GICv3 = CPUFeature(1u) << 223u, // Generic Interrupt Controller version 3 - GICv3p1 = CPUFeature(1u) << 224u, // Generic Interrupt Controller version 3.1 - // Note: cf. https://developer.arm.com/documentation/ihi0069/h/?lang=en - GICv3_LEGACY = CPUFeature(1u) << 225u, // Support for GICv2 legacy operation - GICv3_TDIR = CPUFeature(1u) << 226u, // Trapping Non-secure EL1 writes to ICV_DIR - GICv4 = CPUFeature(1u) << 227u, // Generic Interrupt Controller version 4 - GICv4p1 = CPUFeature(1u) << 228u, // Generic Interrupt Controller version 4.1 - PMUv3 = CPUFeature(1u) << 229u, // PMU extension version 3 - ETE = CPUFeature(1u) << 230u, // Embedded Trace Extension - ETEv1p1 = CPUFeature(1u) << 231u, // Embedded Trace Extension, version 1.1 - SVE2 = CPUFeature(1u) << 232u, // SVE version 2 - SVE_AES = CPUFeature(1u) << 233u, // SVE AES instructions - SVE_PMULL128 = CPUFeature(1u) << 234u, // SVE PMULL instructions; SVE2-AES is split into AES and PMULL support - SVE_BitPerm = CPUFeature(1u) << 235u, // SVE Bit Permute - SVE_SHA3 = CPUFeature(1u) << 236u, // SVE SHA-3 instructions - SVE_SM4 = CPUFeature(1u) << 237u, // SVE SM4 instructions - TME = CPUFeature(1u) << 238u, // Transactional Memory Extension - TRBE = CPUFeature(1u) << 239u, // Trace Buffer Extension - SME = CPUFeature(1u) << 240u, // Scalable Matrix Extension - - __End = CPUFeature(1u) << 255u); // SENTINEL VALUE - -CPUFeature::Type detect_cpu_features(); -StringView cpu_feature_to_name(CPUFeature::Type const&); -StringView cpu_feature_to_description(CPUFeature::Type const&); -NonnullOwnPtr build_cpu_feature_names(CPUFeature::Type const&); - -u8 detect_physical_address_bit_width(); -u8 detect_virtual_address_bit_width(); - -} diff --git a/Kernel/Arch/aarch64/CurrentTime.cpp b/Kernel/Arch/aarch64/CurrentTime.cpp deleted file mode 100644 index ef859819b33..00000000000 --- a/Kernel/Arch/aarch64/CurrentTime.cpp +++ /dev/null @@ -1,16 +0,0 @@ -/* - * Copyright (c) 2022, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include - -namespace Kernel { - -fptr optional_current_time() -{ - return nullptr; -} - -} diff --git a/Kernel/Arch/aarch64/Dummy.cpp b/Kernel/Arch/aarch64/Dummy.cpp deleted file mode 100644 index 9491d422d3a..00000000000 --- a/Kernel/Arch/aarch64/Dummy.cpp +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright (c) 2021, Nico Weber - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include - -#include -#include -#include -#include -#include -#include - -// Delay.cpp -namespace Kernel { - -void microseconds_delay(u32) -{ - TODO_AARCH64(); -} - -} - -// Initializer.cpp -namespace Kernel::PCI { - -SetOnce g_pci_access_io_probe_failed; -SetOnce g_pci_access_is_disabled_from_commandline; - -void initialize() -{ - dbgln("PCI: FIXME: Enable PCI for aarch64 platforms"); - g_pci_access_io_probe_failed.set(); -} - -} - -// kprintf.cpp -void set_serial_debug_enabled(bool) -{ - dbgln("FIXME: Add support for changing state of serial debugging"); -} diff --git a/Kernel/Arch/aarch64/Exceptions.cpp b/Kernel/Arch/aarch64/Exceptions.cpp deleted file mode 100644 index fb7df581f6c..00000000000 --- a/Kernel/Arch/aarch64/Exceptions.cpp +++ /dev/null @@ -1,140 +0,0 @@ -/* - * Copyright (c) 2021, James Mintram - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include - -extern "C" uintptr_t vector_table_el1; - -namespace Kernel { - -static void drop_el3_to_el2() -{ - Aarch64::SCR_EL3 secure_configuration_register_el3 = {}; - - secure_configuration_register_el3.ST = 1; // Don't trap access to Counter-timer Physical Secure registers - secure_configuration_register_el3.RW = 1; // Lower level to use Aarch64 - secure_configuration_register_el3.NS = 1; // Non-secure state - secure_configuration_register_el3.HCE = 1; // Enable Hypervisor instructions at all levels - - Aarch64::SCR_EL3::write(secure_configuration_register_el3); - - Aarch64::SPSR_EL3 saved_program_status_register_el3 = {}; - - // Mask (disable) all interrupts - saved_program_status_register_el3.A = 1; - saved_program_status_register_el3.I = 1; - saved_program_status_register_el3.F = 1; - saved_program_status_register_el3.D = 1; - - // Indicate EL1 as exception origin mode (so we go back there) - saved_program_status_register_el3.M = Aarch64::SPSR_EL3::Mode::EL2h; - - // Set the register - Aarch64::SPSR_EL3::write(saved_program_status_register_el3); - - // This will jump into os_start() below - Aarch64::Asm::enter_el2_from_el3(); -} - -static void drop_el2_to_el1() -{ - Aarch64::HCR_EL2 hypervisor_configuration_register_el2 = {}; - hypervisor_configuration_register_el2.RW = 1; // EL1 to use 64-bit mode - Aarch64::HCR_EL2::write(hypervisor_configuration_register_el2); - - Aarch64::SPSR_EL2 saved_program_status_register_el2 = {}; - - // Mask (disable) all interrupts - saved_program_status_register_el2.A = 1; - saved_program_status_register_el2.I = 1; - saved_program_status_register_el2.F = 1; - - // Indicate EL1 as exception origin mode (so we go back there) - saved_program_status_register_el2.M = Aarch64::SPSR_EL2::Mode::EL1h; - - Aarch64::SPSR_EL2::write(saved_program_status_register_el2); - Aarch64::Asm::enter_el1_from_el2(); -} - -static void setup_el1() -{ - Aarch64::SCTLR_EL1 system_control_register_el1 = Aarch64::SCTLR_EL1::reset_value(); - - system_control_register_el1.UCT = 1; // Don't trap access to CTR_EL0 - system_control_register_el1.nTWE = 1; // Don't trap WFE instructions - system_control_register_el1.nTWI = 1; // Don't trap WFI instructions - system_control_register_el1.DZE = 1; // Don't trap DC ZVA instructions - system_control_register_el1.UMA = 1; // Don't trap access to DAIF (debugging) flags of EFLAGS register - system_control_register_el1.SA0 = 1; // Enable stack access alignment check for EL0 - system_control_register_el1.SA = 1; // Enable stack access alignment check for EL1 - - // FIXME: Enable memory access alignment check when userspace will not execute unaligned memory accesses anymore. - // See: https://github.com/SerenityOS/serenity/issues/17516 - system_control_register_el1.A = 0; // Disable memory access alignment check - - Aarch64::SCTLR_EL1::write(system_control_register_el1); - - Aarch64::CPACR_EL1 cpacr_el1 = {}; - cpacr_el1.ZEN = 0; // Trap SVE instructions at EL1 and EL0 - cpacr_el1.FPEN = 0b11; // Don't trap Advanced SIMD and floating-point instructions - cpacr_el1.SMEN = 0; // Trap SME instructions at EL1 and EL0 - cpacr_el1.TTA = 0; // Don't trap access to trace registers - Aarch64::CPACR_EL1::write(cpacr_el1); - - Aarch64::Asm::load_el1_vector_table(&vector_table_el1); -} - -void initialize_exceptions() -{ - auto base_exception_level = Aarch64::Asm::get_current_exception_level(); - - if (base_exception_level > Aarch64::Asm::ExceptionLevel::EL3) { - panic_without_mmu("Started in unknown EL (Greater than EL3)"sv); - } else if (base_exception_level < Aarch64::Asm::ExceptionLevel::EL1) { - panic_without_mmu("Started in unsupported EL (Less than EL1)"sv); - } else { - if (base_exception_level == Aarch64::Asm::ExceptionLevel::EL1) - dbgln_without_mmu("Started in EL1"sv); - else if (base_exception_level == Aarch64::Asm::ExceptionLevel::EL2) - dbgln_without_mmu("Started in EL2"sv); - else if (base_exception_level == Aarch64::Asm::ExceptionLevel::EL3) - dbgln_without_mmu("Started in EL3"sv); - } - - if (base_exception_level > Aarch64::Asm::ExceptionLevel::EL2) { - drop_el3_to_el2(); - dbgln_without_mmu("Dropped to EL2"sv); - } - - if (base_exception_level > Aarch64::Asm::ExceptionLevel::EL1) { - drop_el2_to_el1(); - dbgln_without_mmu("Dropped to EL1"sv); - } - - setup_el1(); - dbgln_without_mmu("Set up EL1"sv); -} - -// NOTE: The normal PANIC macro cannot be used early in the boot process when the MMU is disabled, -// as it will access global variables, which will cause a crash since they aren't mapped yet. -void panic_without_mmu(StringView message) -{ - (void)message; - // FIXME: Print out message to early boot console. - Processor::halt(); -} - -void dbgln_without_mmu(StringView message) -{ - (void)message; - // FIXME: Print out message to early boot console. -} - -} diff --git a/Kernel/Arch/aarch64/FPUState.S b/Kernel/Arch/aarch64/FPUState.S deleted file mode 100644 index 66ae139e582..00000000000 --- a/Kernel/Arch/aarch64/FPUState.S +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright (c) 2022, Timon Kruiper - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -// The kernel is compiled with -mgeneral-regs-only on AArch64, -// so we have to explicitly allow the use of floating-point instructions here. -.arch_extension fp - -.global store_fpu_state -.type store_fpu_state, @function -store_fpu_state: - stp q0, q1, [x0, #(0 * 16)] - stp q2, q3, [x0, #(2 * 16)] - stp q4, q5, [x0, #(4 * 16)] - stp q6, q7, [x0, #(6 * 16)] - stp q8, q9, [x0, #(8 * 16)] - stp q10, q11, [x0, #(10 * 16)] - stp q12, q13, [x0, #(12 * 16)] - stp q14, q15, [x0, #(14 * 16)] - stp q16, q17, [x0, #(16 * 16)] - stp q18, q19, [x0, #(18 * 16)] - stp q20, q21, [x0, #(20 * 16)] - stp q22, q23, [x0, #(22 * 16)] - stp q24, q25, [x0, #(24 * 16)] - stp q26, q27, [x0, #(26 * 16)] - stp q28, q29, [x0, #(28 * 16)] - stp q30, q31, [x0, #(30 * 16)] - ret - -.global load_fpu_state -.type load_fpu_state, @function -load_fpu_state: - ldp q0, q1, [x0, #(0 * 16)] - ldp q2, q3, [x0, #(2 * 16)] - ldp q4, q5, [x0, #(4 * 16)] - ldp q6, q7, [x0, #(6 * 16)] - ldp q8, q9, [x0, #(8 * 16)] - ldp q10, q11, [x0, #(10 * 16)] - ldp q12, q13, [x0, #(12 * 16)] - ldp q14, q15, [x0, #(14 * 16)] - ldp q16, q17, [x0, #(16 * 16)] - ldp q18, q19, [x0, #(18 * 16)] - ldp q20, q21, [x0, #(20 * 16)] - ldp q22, q23, [x0, #(22 * 16)] - ldp q24, q25, [x0, #(24 * 16)] - ldp q26, q27, [x0, #(26 * 16)] - ldp q28, q29, [x0, #(28 * 16)] - ldp q30, q31, [x0, #(30 * 16)] - ret diff --git a/Kernel/Arch/aarch64/FPUState.h b/Kernel/Arch/aarch64/FPUState.h deleted file mode 100644 index ef83bd55351..00000000000 --- a/Kernel/Arch/aarch64/FPUState.h +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Copyright (c) 2023, kleines Filmröllchen - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include - -VALIDATE_IS_AARCH64() - -namespace Kernel { - -struct [[gnu::aligned(16)]] FPUState { - u8 buffer[512]; -}; - -extern "C" void store_fpu_state(FPUState* fpu_state); -extern "C" void load_fpu_state(FPUState* fpu_state); - -} diff --git a/Kernel/Arch/aarch64/Firmware/ACPI/StaticParsing.cpp b/Kernel/Arch/aarch64/Firmware/ACPI/StaticParsing.cpp deleted file mode 100644 index ba0d9bc13e8..00000000000 --- a/Kernel/Arch/aarch64/Firmware/ACPI/StaticParsing.cpp +++ /dev/null @@ -1,17 +0,0 @@ -/* - * Copyright (c) 2023, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include - -namespace Kernel::ACPI::StaticParsing { - -ErrorOr> find_rsdp_in_platform_specific_memory_locations() -{ - // FIXME: Implement finding RSDP for aarch64. - return Optional {}; -} - -} diff --git a/Kernel/Arch/aarch64/IRQController.h b/Kernel/Arch/aarch64/IRQController.h deleted file mode 100644 index e0e7b1de2cd..00000000000 --- a/Kernel/Arch/aarch64/IRQController.h +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright (c) 2020, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include - -namespace Kernel { - -class GenericInterruptHandler; - -class IRQController : public AtomicRefCounted { -public: - virtual ~IRQController() = default; - - virtual void enable(GenericInterruptHandler const&) = 0; - virtual void disable(GenericInterruptHandler const&) = 0; - - virtual void eoi(GenericInterruptHandler const&) const = 0; - - virtual u64 pending_interrupts() const = 0; - - virtual StringView model() const = 0; - -protected: - IRQController() = default; -}; - -} diff --git a/Kernel/Arch/aarch64/InterruptManagement.cpp b/Kernel/Arch/aarch64/InterruptManagement.cpp deleted file mode 100644 index 7f93c63422d..00000000000 --- a/Kernel/Arch/aarch64/InterruptManagement.cpp +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright (c) 2020, Liav A. - * Copyright (c) 2022, Timon Kruiper - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include - -namespace Kernel { - -static InterruptManagement* s_interrupt_management; - -bool InterruptManagement::initialized() -{ - return s_interrupt_management != nullptr; -} - -InterruptManagement& InterruptManagement::the() -{ - VERIFY(InterruptManagement::initialized()); - return *s_interrupt_management; -} - -void InterruptManagement::initialize() -{ - VERIFY(!InterruptManagement::initialized()); - s_interrupt_management = new InterruptManagement; - - the().find_controllers(); -} - -void InterruptManagement::find_controllers() -{ - // TODO: Once device tree support is in place, find interrupt controllers using that. - m_interrupt_controllers.append(adopt_lock_ref(*new (nothrow) RPi::InterruptController)); -} - -u8 InterruptManagement::acquire_mapped_interrupt_number(u8 interrupt_number) -{ - return interrupt_number; -} - -Vector> const& InterruptManagement::controllers() -{ - return m_interrupt_controllers; -} - -NonnullLockRefPtr InterruptManagement::get_responsible_irq_controller(u8) -{ - // TODO: Support more interrupt controllers - VERIFY(m_interrupt_controllers.size() == 1); - return m_interrupt_controllers[0]; -} - -void InterruptManagement::enumerate_interrupt_handlers(Function) -{ - TODO_AARCH64(); -} - -} diff --git a/Kernel/Arch/aarch64/InterruptManagement.h b/Kernel/Arch/aarch64/InterruptManagement.h deleted file mode 100644 index 697ce70bde6..00000000000 --- a/Kernel/Arch/aarch64/InterruptManagement.h +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright (c) 2020, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include - -namespace Kernel { - -class InterruptManagement { -public: - static InterruptManagement& the(); - static void initialize(); - static bool initialized(); - - static u8 acquire_mapped_interrupt_number(u8 original_irq); - - Vector> const& controllers(); - NonnullLockRefPtr get_responsible_irq_controller(u8 interrupt_vector); - - void enumerate_interrupt_handlers(Function); - -private: - InterruptManagement() = default; - void find_controllers(); - - Vector> m_interrupt_controllers; -}; - -} diff --git a/Kernel/Arch/aarch64/Interrupts.cpp b/Kernel/Arch/aarch64/Interrupts.cpp deleted file mode 100644 index a7d054d2c0f..00000000000 --- a/Kernel/Arch/aarch64/Interrupts.cpp +++ /dev/null @@ -1,234 +0,0 @@ -/* - * Copyright (c) 2022, Timon Kruiper - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace Kernel { - -extern "C" void syscall_handler(TrapFrame const*); - -static void dump_exception_syndrome_register(Aarch64::ESR_EL1 const& esr_el1) -{ - dbgln("Exception Syndrome: EC({:#b}) IL({:#b}) ISS({:#b}) ISS2({:#b})", esr_el1.EC, esr_el1.IL, esr_el1.ISS, esr_el1.ISS2); - dbgln(" Class: {}", Aarch64::exception_class_to_string(esr_el1.EC)); - - if (Aarch64::exception_class_is_data_abort(esr_el1.EC)) - dbgln(" Data Fault Status Code: {}", Aarch64::data_fault_status_code_to_string(esr_el1.ISS)); - if (Aarch64::exception_class_has_set_far(esr_el1.EC)) - dbgln(" Faulting Virtual Address: {:#x}", Aarch64::FAR_EL1::read().virtual_address); -} - -void dump_registers(RegisterState const& regs) -{ - auto esr_el1 = Kernel::Aarch64::ESR_EL1::read(); - dump_exception_syndrome_register(esr_el1); - - // Special registers - Aarch64::SPSR_EL1 spsr_el1 = {}; - memcpy(&spsr_el1, (u8 const*)®s.spsr_el1, sizeof(u64)); - - dbgln("Saved Program Status: (NZCV({:#b}) DAIF({:#b}) M({:#b})) / {:#x}", ((regs.spsr_el1 >> 28) & 0b1111), ((regs.spsr_el1 >> 6) & 0b1111), regs.spsr_el1 & 0b1111, regs.spsr_el1); - dbgln("Exception Link Register: {:#x}", regs.elr_el1); - dbgln("Stack Pointer (EL0): {:#x}", regs.sp_el0); - dbgln("Software Thread ID Register (EL0): {:#x}", regs.tpidr_el0); - - dbgln(" x0={:p} x1={:p} x2={:p} x3={:p} x4={:p}", regs.x[0], regs.x[1], regs.x[2], regs.x[3], regs.x[4]); - dbgln(" x5={:p} x6={:p} x7={:p} x8={:p} x9={:p}", regs.x[5], regs.x[6], regs.x[7], regs.x[8], regs.x[9]); - dbgln("x10={:p} x11={:p} x12={:p} x13={:p} x14={:p}", regs.x[10], regs.x[11], regs.x[12], regs.x[13], regs.x[14]); - dbgln("x15={:p} x16={:p} x17={:p} x18={:p} x19={:p}", regs.x[15], regs.x[16], regs.x[17], regs.x[18], regs.x[19]); - dbgln("x20={:p} x21={:p} x22={:p} x23={:p} x24={:p}", regs.x[20], regs.x[21], regs.x[22], regs.x[23], regs.x[24]); - dbgln("x25={:p} x26={:p} x27={:p} x28={:p} x29={:p}", regs.x[25], regs.x[26], regs.x[27], regs.x[28], regs.x[29]); - dbgln("x30={:p}", regs.x[30]); -} - -static ErrorOr page_fault_from_exception_syndrome_register(VirtualAddress fault_address, Aarch64::ESR_EL1 esr_el1) -{ - PageFault fault { fault_address }; - - u8 data_fault_status_code = esr_el1.ISS & 0x3f; - if (data_fault_status_code >= 0b001100 && data_fault_status_code <= 0b001111) { - fault.set_type(PageFault::Type::ProtectionViolation); - } else if (data_fault_status_code >= 0b000100 && data_fault_status_code <= 0b000111) { - fault.set_type(PageFault::Type::PageNotPresent); - } else { - dbgln("Unknown DFSC: {}", Aarch64::data_fault_status_code_to_string(esr_el1.ISS)); - return Error::from_errno(EFAULT); - } - - fault.set_access((esr_el1.ISS & (1 << 6)) == (1 << 6) ? PageFault::Access::Write : PageFault::Access::Read); - - fault.set_mode(Aarch64::exception_class_is_data_or_instruction_abort_from_lower_exception_level(esr_el1.EC) ? ExecutionMode::User : ExecutionMode::Kernel); - - if (Aarch64::exception_class_is_instruction_abort(esr_el1.EC)) - fault.set_instruction_fetch(true); - - return fault; -} - -extern "C" void exception_common(Kernel::TrapFrame* trap_frame); -extern "C" void exception_common(Kernel::TrapFrame* trap_frame) -{ - Processor::current().enter_trap(*trap_frame, false); - - auto esr_el1 = Kernel::Aarch64::ESR_EL1::read(); - auto fault_address = Aarch64::FAR_EL1::read().virtual_address; - Processor::enable_interrupts(); - - if (Aarch64::exception_class_is_data_abort(esr_el1.EC) || Aarch64::exception_class_is_instruction_abort(esr_el1.EC)) { - auto page_fault_or_error = page_fault_from_exception_syndrome_register(VirtualAddress(fault_address), esr_el1); - if (page_fault_or_error.is_error()) { - dump_registers(*trap_frame->regs); - handle_crash(*trap_frame->regs, "Unknown page fault", SIGSEGV, false); - } else { - auto page_fault = page_fault_or_error.release_value(); - page_fault.handle(*trap_frame->regs); - } - } else if (Aarch64::exception_class_is_svc_instruction_execution(esr_el1.EC)) { - syscall_handler(trap_frame); - } else { - dump_registers(*trap_frame->regs); - handle_crash(*trap_frame->regs, "Unexpected exception", SIGSEGV, false); - } - - Processor::disable_interrupts(); - Processor::current().exit_trap(*trap_frame); -} - -static Array s_interrupt_handlers; - -extern "C" void handle_interrupt(TrapFrame&); -extern "C" void handle_interrupt(TrapFrame& trap_frame) -{ - Processor::current().enter_trap(trap_frame, true); - - for (auto& interrupt_controller : InterruptManagement::the().controllers()) { - auto pending_interrupts = interrupt_controller->pending_interrupts(); - - // TODO: Add these interrupts as a source of entropy for randomness. - u8 irq = 0; - while (pending_interrupts) { - if ((pending_interrupts & 0b1) != 0b1) { - irq += 1; - pending_interrupts >>= 1; - continue; - } - - auto* handler = s_interrupt_handlers[irq]; - VERIFY(handler); - handler->increment_call_count(); - handler->handle_interrupt(*trap_frame.regs); - handler->eoi(); - - irq += 1; - pending_interrupts >>= 1; - } - } - - Processor::current().exit_trap(trap_frame); -} - -// FIXME: Share the code below with Arch/x86_64/Interrupts.cpp -// While refactoring, the interrupt handlers can also be moved into the InterruptManagement class. -GenericInterruptHandler& get_interrupt_handler(u8 interrupt_number) -{ - auto*& handler_slot = s_interrupt_handlers[interrupt_number]; - VERIFY(handler_slot != nullptr); - return *handler_slot; -} - -static void revert_to_unused_handler(u8 interrupt_number) -{ - auto handler = new UnhandledInterruptHandler(interrupt_number); - handler->register_interrupt_handler(); -} - -void register_generic_interrupt_handler(u8 interrupt_number, GenericInterruptHandler& handler) -{ - auto*& handler_slot = s_interrupt_handlers[interrupt_number]; - if (handler_slot == nullptr) { - handler_slot = &handler; - return; - } - if (handler_slot->type() == HandlerType::UnhandledInterruptHandler) { - auto* unhandled_handler = static_cast(handler_slot); - unhandled_handler->unregister_interrupt_handler(); - delete unhandled_handler; - handler_slot = &handler; - return; - } - if (handler_slot->is_shared_handler()) { - VERIFY(handler_slot->type() == HandlerType::SharedIRQHandler); - static_cast(handler_slot)->register_handler(handler); - return; - } - if (!handler_slot->is_shared_handler()) { - if (handler_slot->type() == HandlerType::SpuriousInterruptHandler) { - // FIXME: Add support for spurious interrupts on aarch64 - TODO_AARCH64(); - } - VERIFY(handler_slot->type() == HandlerType::IRQHandler); - auto& previous_handler = *handler_slot; - handler_slot = nullptr; - SharedIRQHandler::initialize(interrupt_number); - VERIFY(handler_slot); - static_cast(handler_slot)->register_handler(previous_handler); - static_cast(handler_slot)->register_handler(handler); - return; - } - VERIFY_NOT_REACHED(); -} - -void unregister_generic_interrupt_handler(u8 interrupt_number, GenericInterruptHandler& handler) -{ - auto*& handler_slot = s_interrupt_handlers[interrupt_number]; - VERIFY(handler_slot != nullptr); - if (handler_slot->type() == HandlerType::UnhandledInterruptHandler) - return; - if (handler_slot->is_shared_handler()) { - VERIFY(handler_slot->type() == HandlerType::SharedIRQHandler); - auto* shared_handler = static_cast(handler_slot); - shared_handler->unregister_handler(handler); - if (!shared_handler->sharing_devices_count()) { - handler_slot = nullptr; - revert_to_unused_handler(interrupt_number); - } - return; - } - if (!handler_slot->is_shared_handler()) { - VERIFY(handler_slot->type() == HandlerType::IRQHandler); - handler_slot = nullptr; - revert_to_unused_handler(interrupt_number); - return; - } -} - -void initialize_interrupts() -{ - for (u8 i = 0; i < s_interrupt_handlers.size(); ++i) { - auto* handler = new UnhandledInterruptHandler(i); - handler->register_interrupt_handler(); - } -} - -// Sets the reserved flag on `number_of_irqs` if it finds unused interrupt handler on -// a contiguous range. -ErrorOr reserve_interrupt_handlers([[maybe_unused]] u8 number_of_irqs) -{ - TODO(); - return Error::from_errno(EINVAL); -} - -} diff --git a/Kernel/Arch/aarch64/MMU.cpp b/Kernel/Arch/aarch64/MMU.cpp deleted file mode 100644 index 23dc9154bd1..00000000000 --- a/Kernel/Arch/aarch64/MMU.cpp +++ /dev/null @@ -1,307 +0,0 @@ -/* - * Copyright (c) 2021, James Mintram - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include - -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -// Documentation here for Aarch64 Address Translations -// https://documentation-service.arm.com/static/5efa1d23dbdee951c1ccdec5?token= - -// These come from the linker script -extern u8 page_tables_phys_start[]; -extern u8 page_tables_phys_end[]; -extern u8 start_of_kernel_image[]; -extern u8 end_of_kernel_image[]; - -namespace Kernel::Memory { - -// physical memory -constexpr u32 START_OF_NORMAL_MEMORY = 0x00000000; -constexpr u32 END_OF_NORMAL_MEMORY = 0x3EFFFFFF; - -ALWAYS_INLINE static u64* descriptor_to_pointer(FlatPtr descriptor) -{ - return (u64*)(descriptor & DESCRIPTOR_MASK); -} - -namespace { -class PageBumpAllocator { -public: - PageBumpAllocator(u64* start, u64* end) - : m_start(start) - , m_end(end) - , m_current(start) - { - if (m_start >= m_end) { - panic_without_mmu("Invalid memory range passed to PageBumpAllocator"sv); - } - if ((FlatPtr)m_start % PAGE_TABLE_SIZE != 0 || (FlatPtr)m_end % PAGE_TABLE_SIZE != 0) { - panic_without_mmu("Memory range passed into PageBumpAllocator not aligned to PAGE_TABLE_SIZE"sv); - } - } - - u64* take_page() - { - if (m_current == m_end) { - panic_without_mmu("Prekernel pagetable memory exhausted"sv); - } - - u64* page = m_current; - m_current += (PAGE_TABLE_SIZE / sizeof(FlatPtr)); - - zero_page(page); - return page; - } - -private: - void zero_page(u64* page) - { - // Memset all page table memory to zero - for (u64* p = page; p < page + (PAGE_TABLE_SIZE / sizeof(u64)); p++) { - *p = 0; - } - } - - u64 const* m_start; - u64 const* m_end; - u64* m_current; -}; -} - -// NOTE: To access global variables while the MMU is not yet enabled, we need -// to convert the address of a global variable to a physical address by -// subtracting KERNEL_MAPPING_BASE. This is because the kernel is linked -// for virtual memory at KERNEL_MAPPING_BASE, so a regular access to global variables -// will use the high virtual memory address. This does not work when the MMU is not yet -// enabled, so this function must be used for accessing global variables. -template -inline T* adjust_by_mapping_base(T* ptr) -{ - return (T*)((FlatPtr)ptr - KERNEL_MAPPING_BASE); -} - -static u64* insert_page_table(PageBumpAllocator& allocator, u64* page_table, VirtualAddress virtual_addr) -{ - // Each level has 9 bits (512 entries) - u64 level0_idx = (virtual_addr.get() >> 39) & 0x1FF; - u64 level1_idx = (virtual_addr.get() >> 30) & 0x1FF; - u64 level2_idx = (virtual_addr.get() >> 21) & 0x1FF; - - u64* level1_table = page_table; - - if (level1_table[level0_idx] == 0) { - level1_table[level0_idx] = (FlatPtr)allocator.take_page(); - level1_table[level0_idx] |= TABLE_DESCRIPTOR; - } - - u64* level2_table = descriptor_to_pointer(level1_table[level0_idx]); - - if (level2_table[level1_idx] == 0) { - level2_table[level1_idx] = (FlatPtr)allocator.take_page(); - level2_table[level1_idx] |= TABLE_DESCRIPTOR; - } - - u64* level3_table = descriptor_to_pointer(level2_table[level1_idx]); - - if (level3_table[level2_idx] == 0) { - level3_table[level2_idx] = (FlatPtr)allocator.take_page(); - level3_table[level2_idx] |= TABLE_DESCRIPTOR; - } - - return descriptor_to_pointer(level3_table[level2_idx]); -} - -static void insert_entries_for_memory_range(PageBumpAllocator& allocator, u64* page_table, VirtualAddress start, VirtualAddress end, PhysicalAddress paddr, u64 flags) -{ - // Not very efficient, but simple and it works. - for (VirtualAddress addr = start; addr < end;) { - u64* level4_table = insert_page_table(allocator, page_table, addr); - - u64 level3_idx = (addr.get() >> 12) & 0x1FF; - u64* l4_entry = &level4_table[level3_idx]; - *l4_entry = paddr.get(); - *l4_entry |= flags; - - addr = addr.offset(GRANULE_SIZE); - paddr = paddr.offset(GRANULE_SIZE); - } -} - -static void setup_quickmap_page_table(PageBumpAllocator& allocator, u64* root_table) -{ - // FIXME: Rename boot_pd_kernel_pt1023 to quickmap_page_table - // FIXME: Rename KERNEL_PT1024_BASE to quickmap_page_table_address - auto kernel_pt1024_base = VirtualAddress(*adjust_by_mapping_base(&kernel_mapping_base) + KERNEL_PT1024_OFFSET); - - auto quickmap_page_table = PhysicalAddress((PhysicalPtr)insert_page_table(allocator, root_table, kernel_pt1024_base)); - *adjust_by_mapping_base(&boot_pd_kernel_pt1023) = (PageTableEntry*)quickmap_page_table.offset(KERNEL_MAPPING_BASE).get(); -} - -static void build_mappings(PageBumpAllocator& allocator, u64* root_table) -{ - u64 normal_memory_flags = ACCESS_FLAG | PAGE_DESCRIPTOR | INNER_SHAREABLE | NORMAL_MEMORY; - u64 device_memory_flags = ACCESS_FLAG | PAGE_DESCRIPTOR | OUTER_SHAREABLE | DEVICE_MEMORY; - - // TODO: We should change the RPi drivers to use the MemoryManager to map physical memory, - // instead of mapping the complete MMIO region beforehand. - auto mmio_base = RPi::MMIO::the().peripheral_base_address().get(); - auto mmio_end = RPi::MMIO::the().peripheral_end_address().get(); - - // Align the identity mapping of the kernel image to 2 MiB, the rest of the memory is initially not mapped. - auto start_of_kernel_range = VirtualAddress((FlatPtr)start_of_kernel_image & ~(FlatPtr)0x1fffff); - auto end_of_kernel_range = VirtualAddress(((FlatPtr)end_of_kernel_image & ~(FlatPtr)0x1fffff) + 0x200000 - 1); - auto start_of_mmio_range = VirtualAddress(mmio_base + KERNEL_MAPPING_BASE); - auto end_of_mmio_range = VirtualAddress(mmio_end + KERNEL_MAPPING_BASE); - - auto start_of_physical_kernel_range = PhysicalAddress(start_of_kernel_range.get()).offset(-KERNEL_MAPPING_BASE); - auto start_of_physical_mmio_range = PhysicalAddress(start_of_mmio_range.get()).offset(-KERNEL_MAPPING_BASE); - - // Insert identity mappings - insert_entries_for_memory_range(allocator, root_table, start_of_kernel_range.offset(-KERNEL_MAPPING_BASE), end_of_kernel_range.offset(-KERNEL_MAPPING_BASE), start_of_physical_kernel_range, normal_memory_flags); - insert_entries_for_memory_range(allocator, root_table, start_of_mmio_range.offset(-KERNEL_MAPPING_BASE), end_of_mmio_range.offset(-KERNEL_MAPPING_BASE), start_of_physical_mmio_range, device_memory_flags); - - // Map kernel and MMIO into high virtual memory - insert_entries_for_memory_range(allocator, root_table, start_of_kernel_range, end_of_kernel_range, start_of_physical_kernel_range, normal_memory_flags); - insert_entries_for_memory_range(allocator, root_table, start_of_mmio_range, end_of_mmio_range, start_of_physical_mmio_range, device_memory_flags); -} - -static void switch_to_page_table(u8* page_table) -{ - Aarch64::Asm::set_ttbr0_el1((FlatPtr)page_table); - Aarch64::Asm::set_ttbr1_el1((FlatPtr)page_table); -} - -static void activate_mmu() -{ - Aarch64::MAIR_EL1 mair_el1 = {}; - mair_el1.Attr[0] = 0xFF; // Normal memory - mair_el1.Attr[1] = 0b00000100; // Device-nGnRE memory (non-cacheble) - Aarch64::MAIR_EL1::write(mair_el1); - - // Configure cacheability attributes for memory associated with translation table walks - Aarch64::TCR_EL1 tcr_el1 = {}; - - tcr_el1.SH1 = Aarch64::TCR_EL1::InnerShareable; - tcr_el1.ORGN1 = Aarch64::TCR_EL1::NormalMemory_Outer_WriteBack_ReadAllocate_WriteAllocateCacheable; - tcr_el1.IRGN1 = Aarch64::TCR_EL1::NormalMemory_Inner_WriteBack_ReadAllocate_WriteAllocateCacheable; - tcr_el1.T1SZ = 16; - - tcr_el1.SH0 = Aarch64::TCR_EL1::InnerShareable; - tcr_el1.ORGN0 = Aarch64::TCR_EL1::NormalMemory_Outer_WriteBack_ReadAllocate_WriteAllocateCacheable; - tcr_el1.IRGN0 = Aarch64::TCR_EL1::NormalMemory_Inner_WriteBack_ReadAllocate_WriteAllocateCacheable; - tcr_el1.T0SZ = 16; - - tcr_el1.TG1 = Aarch64::TCR_EL1::TG1GranuleSize::Size_4KB; - tcr_el1.TG0 = Aarch64::TCR_EL1::TG0GranuleSize::Size_4KB; - - // Auto detect the Intermediate Physical Address Size - Aarch64::ID_AA64MMFR0_EL1 feature_register = Aarch64::ID_AA64MMFR0_EL1::read(); - tcr_el1.IPS = feature_register.PARange; - - Aarch64::TCR_EL1::write(tcr_el1); - - // Enable MMU in the system control register - Aarch64::SCTLR_EL1 sctlr_el1 = Aarch64::SCTLR_EL1::reset_value(); - sctlr_el1.M = 1; // Enable MMU - sctlr_el1.C = 1; // Enable data cache - sctlr_el1.I = 1; // Enable instruction cache - Aarch64::SCTLR_EL1::write(sctlr_el1); - - Aarch64::Asm::flush(); -} - -static u64* get_page_directory(u64* root_table, VirtualAddress virtual_addr) -{ - u64 level0_idx = (virtual_addr.get() >> 39) & 0x1FF; - u64 level1_idx = (virtual_addr.get() >> 30) & 0x1FF; - - u64* level1_table = root_table; - - if (level1_table[level0_idx] == 0) - return nullptr; - - u64* level2_table = descriptor_to_pointer(level1_table[level0_idx]); - - if (level2_table[level1_idx] == 0) - return nullptr; - - return descriptor_to_pointer(level2_table[level1_idx]); -} - -static u64* get_page_directory_table(u64* root_table, VirtualAddress virtual_addr) -{ - u64 level0_idx = (virtual_addr.get() >> 39) & 0x1FF; - u64* level1_table = root_table; - - if (level1_table[level0_idx] == 0) - return nullptr; - - return descriptor_to_pointer(level1_table[level0_idx]); -} - -static void setup_kernel_page_directory(u64* root_table) -{ - auto kernel_page_directory = (PhysicalPtr)get_page_directory(root_table, VirtualAddress { *adjust_by_mapping_base(&kernel_mapping_base) }); - if (!kernel_page_directory) - panic_without_mmu("Could not find kernel page directory!"sv); - - *adjust_by_mapping_base(&boot_pd_kernel) = PhysicalAddress(kernel_page_directory); - - // FIXME: Rename boot_pml4t to something architecture agnostic. - *adjust_by_mapping_base(&boot_pml4t) = PhysicalAddress((PhysicalPtr)root_table); - - // FIXME: Rename to directory_table or similar - *adjust_by_mapping_base(&boot_pdpt) = PhysicalAddress((PhysicalPtr)get_page_directory_table(root_table, VirtualAddress { *adjust_by_mapping_base(&kernel_mapping_base) })); -} - -void init_page_tables() -{ - *adjust_by_mapping_base(&physical_to_virtual_offset) = KERNEL_MAPPING_BASE; - *adjust_by_mapping_base(&kernel_mapping_base) = KERNEL_MAPPING_BASE; - *adjust_by_mapping_base(&kernel_load_base) = KERNEL_MAPPING_BASE; - - PageBumpAllocator allocator(adjust_by_mapping_base((u64*)page_tables_phys_start), adjust_by_mapping_base((u64*)page_tables_phys_end)); - auto root_table = allocator.take_page(); - build_mappings(allocator, root_table); - setup_quickmap_page_table(allocator, root_table); - setup_kernel_page_directory(root_table); - - switch_to_page_table(adjust_by_mapping_base(page_tables_phys_start)); - activate_mmu(); -} - -void unmap_identity_map() -{ - auto start_of_physical_memory = FlatPtr(START_OF_NORMAL_MEMORY); - - u64 level0_idx = (start_of_physical_memory >> 39) & 0x1FF; - u64 level1_idx = (start_of_physical_memory >> 30) & 0x1FF; - - u64* level1_table = (u64*)page_tables_phys_start; - - auto level2_table = FlatPtr(descriptor_to_pointer(level1_table[level0_idx])); - if (!level2_table) - panic_without_mmu("Could not find table!"sv); - - // NOTE: The function descriptor_to_pointer returns a physical address, but we want to unmap that range - // so, the pointer must be converted to a virtual address by adding KERNEL_MAPPING_BASE. - level2_table += KERNEL_MAPPING_BASE; - - // Unmap the complete identity map - ((u64*)level2_table)[level1_idx] = 0; -} - -} diff --git a/Kernel/Arch/aarch64/MainIdRegister.cpp b/Kernel/Arch/aarch64/MainIdRegister.cpp deleted file mode 100644 index 0ffec21116e..00000000000 --- a/Kernel/Arch/aarch64/MainIdRegister.cpp +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Copyright (c) 2021, Nico Weber - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include - -namespace Kernel { - -MainIdRegister::MainIdRegister() -{ - unsigned int mrs; - asm volatile("mrs %x0, MIDR_EL1" - : "=r"(mrs)); - m_value = mrs; -} - -} diff --git a/Kernel/Arch/aarch64/MainIdRegister.h b/Kernel/Arch/aarch64/MainIdRegister.h deleted file mode 100644 index 1b53c269d06..00000000000 --- a/Kernel/Arch/aarch64/MainIdRegister.h +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright (c) 2021, Nico Weber - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -namespace Kernel { - -class MainIdRegister { -public: - MainIdRegister(); - - enum Implementer { - ArmLimited = 0x41, - }; - unsigned implementer() const { return (m_value >> 24) & 0xFF; } - unsigned variant() const { return (m_value >> 20) & 0xF; } - unsigned architecture() const { return (m_value >> 16) & 0xF; } - - enum PartNum { - RaspberryPi1 = 0xB76, - RaspberryPi2 = 0xC07, - RaspberryPi3 = 0xD03, - RaspberryPi4 = 0xD08, - }; - unsigned part_num() const { return (m_value >> 4) & 0xFFF; } - - unsigned revision() const { return m_value & 0xF; } - -private: - unsigned int m_value; -}; - -} diff --git a/Kernel/Arch/aarch64/PageDirectory.cpp b/Kernel/Arch/aarch64/PageDirectory.cpp deleted file mode 100644 index bb99f3abcf6..00000000000 --- a/Kernel/Arch/aarch64/PageDirectory.cpp +++ /dev/null @@ -1,128 +0,0 @@ -/* - * Copyright (c) 2018-2021, Andreas Kling - * Copyright (c) 2018-2022, James Mintram - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace Kernel::Memory { - -struct TTBR0Map { - SpinlockProtected, LockRank::None> map {}; -}; - -static Singleton s_ttbr0_map; - -void PageDirectory::register_page_directory(PageDirectory* directory) -{ - s_ttbr0_map->map.with([&](auto& map) { - map.insert(directory->ttbr0(), *directory); - }); -} - -void PageDirectory::deregister_page_directory(PageDirectory* directory) -{ - s_ttbr0_map->map.with([&](auto& map) { - map.remove(directory->ttbr0()); - }); -} - -LockRefPtr PageDirectory::find_current() -{ - return s_ttbr0_map->map.with([&](auto& map) { - return map.find(Aarch64::Asm::get_ttbr0_el1()); - }); -} - -void activate_kernel_page_directory(PageDirectory const& page_directory) -{ - Aarch64::Asm::set_ttbr0_el1(page_directory.ttbr0()); - Processor::flush_entire_tlb_local(); -} - -void activate_page_directory(PageDirectory const& page_directory, Thread* current_thread) -{ - current_thread->regs().ttbr0_el1 = page_directory.ttbr0(); - Aarch64::Asm::set_ttbr0_el1(page_directory.ttbr0()); - Processor::flush_entire_tlb_local(); -} - -UNMAP_AFTER_INIT NonnullLockRefPtr PageDirectory::must_create_kernel_page_directory() -{ - return adopt_lock_ref_if_nonnull(new (nothrow) PageDirectory).release_nonnull(); -} - -ErrorOr> PageDirectory::try_create_for_userspace(Process& process) -{ - auto directory = TRY(adopt_nonnull_lock_ref_or_enomem(new (nothrow) PageDirectory)); - - directory->m_process = &process; - - directory->m_root_table = TRY(MM.allocate_physical_page()); - - directory->m_directory_table = TRY(MM.allocate_physical_page()); - auto kernel_pd_index = (kernel_mapping_base >> 30) & 0x1ffu; - for (size_t i = 0; i < kernel_pd_index; i++) { - directory->m_directory_pages[i] = TRY(MM.allocate_physical_page()); - } - - // Share the top 1 GiB of kernel-only mappings (>=kernel_mapping_base) - directory->m_directory_pages[kernel_pd_index] = MM.kernel_page_directory().m_directory_pages[kernel_pd_index]; - - { - InterruptDisabler disabler; - auto& table = *(PageDirectoryPointerTable*)MM.quickmap_page(*directory->m_root_table); - table.raw[0] = (FlatPtr)directory->m_directory_table->paddr().as_ptr() | TABLE_DESCRIPTOR; - MM.unquickmap_page(); - } - - { - InterruptDisabler disabler; - auto& table = *(PageDirectoryPointerTable*)MM.quickmap_page(*directory->m_directory_table); - for (size_t i = 0; i < sizeof(m_directory_pages) / sizeof(m_directory_pages[0]); i++) { - if (directory->m_directory_pages[i]) { - table.raw[i] = (FlatPtr)directory->m_directory_pages[i]->paddr().as_ptr() | PAGE_DESCRIPTOR; - } - } - MM.unquickmap_page(); - } - - register_page_directory(directory); - return directory; -} - -PageDirectory::PageDirectory() = default; - -UNMAP_AFTER_INIT void PageDirectory::allocate_kernel_directory() -{ - // Adopt the page tables already set up by boot.S - dmesgln("MM: boot_pml4t @ {}", boot_pml4t); - m_root_table = PhysicalRAMPage::create(boot_pml4t, MayReturnToFreeList::No); - dmesgln("MM: boot_pdpt @ {}", boot_pdpt); - dmesgln("MM: boot_pd0 @ {}", boot_pd0); - dmesgln("MM: boot_pd_kernel @ {}", boot_pd_kernel); - m_directory_table = PhysicalRAMPage::create(boot_pdpt, MayReturnToFreeList::No); - m_directory_pages[0] = PhysicalRAMPage::create(boot_pd0, MayReturnToFreeList::No); - m_directory_pages[(kernel_mapping_base >> 30) & 0x1ff] = PhysicalRAMPage::create(boot_pd_kernel, MayReturnToFreeList::No); -} - -PageDirectory::~PageDirectory() -{ - if (is_root_table_initialized()) { - deregister_page_directory(this); - } -} - -} diff --git a/Kernel/Arch/aarch64/PageDirectory.h b/Kernel/Arch/aarch64/PageDirectory.h deleted file mode 100644 index 249db8fe3b1..00000000000 --- a/Kernel/Arch/aarch64/PageDirectory.h +++ /dev/null @@ -1,223 +0,0 @@ -/* - * Copyright (c) 2018-2021, Andreas Kling - * Copyright (c) 2022, Timon Kruiper - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace Kernel::Memory { - -// 4KiB page size was chosen to make this code slightly simpler -constexpr u32 GRANULE_SIZE = 0x1000; -constexpr u32 PAGE_TABLE_SIZE = 0x1000; - -// Documentation for translation table format -// https://developer.arm.com/documentation/101811/0101/Controlling-address-translation -constexpr u32 PAGE_DESCRIPTOR = 0b11; -constexpr u32 TABLE_DESCRIPTOR = 0b11; -constexpr u32 DESCRIPTOR_MASK = ~0b11; - -constexpr u32 ACCESS_FLAG = 1 << 10; - -// shareability -constexpr u32 OUTER_SHAREABLE = (2 << 8); -constexpr u32 INNER_SHAREABLE = (3 << 8); - -// these index into the MAIR attribute table -constexpr u32 NORMAL_MEMORY = (0 << 2); -constexpr u32 DEVICE_MEMORY = (1 << 2); - -constexpr u32 ACCESS_PERMISSION_EL0 = (1 << 6); -constexpr u32 ACCESS_PERMISSION_READONLY = (1 << 7); - -// Figure D5-15 of Arm Architecture Reference Manual Armv8 - page D5-2588 -class PageDirectoryEntry { -public: - PhysicalPtr page_table_base() const { return PhysicalAddress::physical_page_base(m_raw); } - void set_page_table_base(PhysicalPtr value) - { - m_raw &= 0xffff000000000fffULL; - m_raw |= PhysicalAddress::physical_page_base(value); - - // FIXME: Do not hardcode this. - m_raw |= TABLE_DESCRIPTOR; - } - - bool is_null() const { return m_raw == 0; } - void clear() { m_raw = 0; } - - u64 raw() const { return m_raw; } - void copy_from(Badge, PageDirectoryEntry const& other) { m_raw = other.m_raw; } - - enum Flags { - Present = 1 << 0, - }; - - bool is_present() const { return (raw() & Present) == Present; } - void set_present(bool) { } - - bool is_user_allowed() const { TODO_AARCH64(); } - void set_user_allowed(bool) { } - - bool is_huge() const { TODO_AARCH64(); } - void set_huge(bool) { } - - bool is_writable() const { TODO_AARCH64(); } - void set_writable(bool) { } - - bool is_write_through() const { TODO_AARCH64(); } - void set_write_through(bool) { } - - bool is_cache_disabled() const { TODO_AARCH64(); } - void set_cache_disabled(bool) { } - - bool is_global() const { TODO_AARCH64(); } - void set_global(bool) { } - - bool is_execute_disabled() const { TODO_AARCH64(); } - void set_execute_disabled(bool) { } - -private: - void set_bit(u64 bit, bool value) - { - if (value) - m_raw |= bit; - else - m_raw &= ~bit; - } - - u64 m_raw; -}; - -// Figure D5-17 VMSAv8-64 level 3 descriptor format of Arm Architecture Reference Manual Armv8 - page D5-2592 -class PageTableEntry { -public: - PhysicalPtr physical_page_base() const { return PhysicalAddress::physical_page_base(m_raw); } - void set_physical_page_base(PhysicalPtr value) - { - m_raw &= 0xffff000000000fffULL; - m_raw |= PhysicalAddress::physical_page_base(value); - - // FIXME: For now we map everything with the same permissions. - u64 normal_memory_flags = ACCESS_FLAG | PAGE_DESCRIPTOR | INNER_SHAREABLE | NORMAL_MEMORY; - m_raw |= normal_memory_flags; - } - - u64 raw() const { return m_raw; } - - enum Flags { - Present = 1 << 0, - }; - - bool is_present() const { return (raw() & Present) == Present; } - void set_present(bool b) { set_bit(Present, b); } - - bool is_user_allowed() const { return (raw() & ACCESS_PERMISSION_EL0) == ACCESS_PERMISSION_EL0; } - void set_user_allowed(bool b) { set_bit(ACCESS_PERMISSION_EL0, b); } - - bool is_writable() const { return !((raw() & ACCESS_PERMISSION_READONLY) == ACCESS_PERMISSION_READONLY); } - void set_writable(bool b) { set_bit(ACCESS_PERMISSION_READONLY, !b); } - - bool is_write_through() const { TODO_AARCH64(); } - void set_write_through(bool) { } - - bool is_cache_disabled() const { TODO_AARCH64(); } - void set_cache_disabled(bool) { } - - bool is_global() const { TODO_AARCH64(); } - void set_global(bool) { } - - bool is_execute_disabled() const { TODO_AARCH64(); } - void set_execute_disabled(bool) { } - - bool is_pat() const { TODO_AARCH64(); } - void set_pat(bool) { } - - bool is_null() const { return m_raw == 0; } - void clear() { m_raw = 0; } - -private: - void set_bit(u64 bit, bool value) - { - if (value) - m_raw |= bit; - else - m_raw &= ~bit; - } - - u64 m_raw; -}; - -static_assert(AssertSize()); -static_assert(AssertSize()); - -class PageDirectoryPointerTable { -public: - PageDirectoryEntry* directory(size_t index) - { - VERIFY(index <= (NumericLimits::max() << 30)); - return (PageDirectoryEntry*)(PhysicalAddress::physical_page_base(raw[index])); - } - - u64 raw[512]; -}; - -class PageDirectory final : public AtomicRefCounted { - friend class MemoryManager; - -public: - static ErrorOr> try_create_for_userspace(Process&); - static NonnullLockRefPtr must_create_kernel_page_directory(); - static LockRefPtr find_current(); - - ~PageDirectory(); - - void allocate_kernel_directory(); - - FlatPtr ttbr0() const - { - return m_root_table->paddr().get(); - } - - bool is_root_table_initialized() const - { - return m_root_table; - } - - Process* process() { return m_process; } - - RecursiveSpinlock& get_lock() { return m_lock; } - - // This has to be public to let the global singleton access the member pointer - IntrusiveRedBlackTreeNode> m_tree_node; - -private: - PageDirectory(); - static void register_page_directory(PageDirectory* directory); - static void deregister_page_directory(PageDirectory* directory); - - Process* m_process { nullptr }; - RefPtr m_root_table; - RefPtr m_directory_table; - RefPtr m_directory_pages[512]; - RecursiveSpinlock m_lock {}; -}; - -void activate_kernel_page_directory(PageDirectory const& pgd); -void activate_page_directory(PageDirectory const& pgd, Thread* current_thread); - -} diff --git a/Kernel/Arch/aarch64/Panic.cpp b/Kernel/Arch/aarch64/Panic.cpp deleted file mode 100644 index 5b0ddc99c4f..00000000000 --- a/Kernel/Arch/aarch64/Panic.cpp +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright (c) 2022, Timon Kruiper - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include - -// FIXME: Merge the code in this file with Kernel/Library/Panic.cpp once the proper abstractions are in place. - -// Note: This is required here, since __assertion_failed should be out of the Kernel namespace, -// but the PANIC macro uses functions that require the Kernel namespace. -using namespace Kernel; - -[[noreturn]] void __assertion_failed(char const* msg, char const* file, unsigned line, char const* func) -{ - critical_dmesgln("ASSERTION FAILED: {}", msg); - critical_dmesgln("{}:{} in {}", file, line, func); - - // Used for printing a nice backtrace! - PANIC("Aborted"); -} diff --git a/Kernel/Arch/aarch64/PowerState.cpp b/Kernel/Arch/aarch64/PowerState.cpp deleted file mode 100644 index 2cc333d4132..00000000000 --- a/Kernel/Arch/aarch64/PowerState.cpp +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright (c) 2023, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include - -namespace Kernel { - -void arch_specific_reboot() -{ -} - -void arch_specific_poweroff() -{ - RPi::Watchdog::the().system_shutdown(); -} - -} diff --git a/Kernel/Arch/aarch64/Processor.cpp b/Kernel/Arch/aarch64/Processor.cpp deleted file mode 100644 index 65860a2ff2a..00000000000 --- a/Kernel/Arch/aarch64/Processor.cpp +++ /dev/null @@ -1,456 +0,0 @@ -/* - * Copyright (c) 2022, Timon Kruiper - * Copyright (c) 2023, Idan Horowitz - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace Kernel { - -extern "C" void context_first_init(Thread* from_thread, Thread* to_thread) __attribute__((used)); -extern "C" void enter_thread_context(Thread* from_thread, Thread* to_thread) __attribute__((used)); - -Processor* g_current_processor; - -template -void ProcessorBase::early_initialize(u32 cpu) -{ - VERIFY(g_current_processor == nullptr); - m_cpu = cpu; - m_features = detect_cpu_features(); - m_physical_address_bit_width = detect_physical_address_bit_width(); - m_virtual_address_bit_width = detect_virtual_address_bit_width(); - - g_current_processor = static_cast(this); -} - -template -void ProcessorBase::initialize(u32) -{ - m_deferred_call_pool.init(); - - dmesgln("CPU[{}]: Supports {}", m_cpu, build_cpu_feature_names(m_features)); - dmesgln("CPU[{}]: Physical address bit width: {}", m_cpu, m_physical_address_bit_width); - dmesgln("CPU[{}]: Virtual address bit width: {}", m_cpu, m_virtual_address_bit_width); - if (!has_feature(CPUFeature::RNG)) - dmesgln("CPU[{}]: {} not detected, randomness will be poor", m_cpu, cpu_feature_to_description(CPUFeature::RNG)); - - store_fpu_state(&s_clean_fpu_state); -} - -template -[[noreturn]] void ProcessorBase::halt() -{ - disable_interrupts(); - for (;;) - asm volatile("wfi"); -} - -template -void ProcessorBase::flush_tlb_local(VirtualAddress, size_t) -{ - // FIXME: Figure out how to flush a single page - asm volatile("dsb ishst"); - asm volatile("tlbi vmalle1"); - asm volatile("dsb ish"); - asm volatile("isb"); -} - -template -void ProcessorBase::flush_entire_tlb_local() -{ - asm volatile("dsb ishst"); - asm volatile("tlbi vmalle1"); - asm volatile("dsb ish"); - asm volatile("isb"); -} - -template -void ProcessorBase::flush_tlb(Memory::PageDirectory const*, VirtualAddress vaddr, size_t page_count) -{ - flush_tlb_local(vaddr, page_count); -} - -template -u32 ProcessorBase::clear_critical() -{ - InterruptDisabler disabler; - auto prev_critical = in_critical(); - auto& proc = current(); - proc.m_in_critical = 0; - if (proc.m_in_irq == 0) - proc.check_invoke_scheduler(); - return prev_critical; -} - -template -u32 ProcessorBase::smp_wake_n_idle_processors(u32 wake_count) -{ - (void)wake_count; - // FIXME: Actually wake up other cores when SMP is supported for aarch64. - return 0; -} - -template -void ProcessorBase::initialize_context_switching(Thread& initial_thread) -{ - VERIFY(initial_thread.process().is_kernel_process()); - - m_scheduler_initialized.set(); - - // FIXME: Figure out if we need to call {pre_,post_,}init_finished once aarch64 supports SMP - Processor::set_current_in_scheduler(true); - - auto& regs = initial_thread.regs(); - // clang-format off - asm volatile( - "mov sp, %[new_sp] \n" - - "sub sp, sp, 32 \n" - "str %[from_to_thread], [sp, #0] \n" - "str %[from_to_thread], [sp, #8] \n" - "br %[new_ip] \n" - :: [new_sp] "r" (regs.sp_el0), - [new_ip] "r" (regs.elr_el1), - [from_to_thread] "r" (&initial_thread) - ); - // clang-format on - - VERIFY_NOT_REACHED(); -} - -template -void ProcessorBase::switch_context(Thread*& from_thread, Thread*& to_thread) -{ - VERIFY(!m_in_irq); - VERIFY(m_in_critical == 1); - - dbgln_if(CONTEXT_SWITCH_DEBUG, "switch_context --> switching out of: {} {}", VirtualAddress(from_thread), *from_thread); - - // m_in_critical is restored in enter_thread_context - from_thread->save_critical(m_in_critical); - - asm volatile( - "sub sp, sp, #256 \n" - "stp x0, x1, [sp, #(0 * 0)] \n" - "stp x2, x3, [sp, #(2 * 8)] \n" - "stp x4, x5, [sp, #(4 * 8)] \n" - "stp x6, x7, [sp, #(6 * 8)] \n" - "stp x8, x9, [sp, #(8 * 8)] \n" - "stp x10, x11, [sp, #(10 * 8)] \n" - "stp x12, x13, [sp, #(12 * 8)] \n" - "stp x14, x15, [sp, #(14 * 8)] \n" - "stp x16, x17, [sp, #(16 * 8)] \n" - "stp x18, x19, [sp, #(18 * 8)] \n" - "stp x20, x21, [sp, #(20 * 8)] \n" - "stp x22, x23, [sp, #(22 * 8)] \n" - "stp x24, x25, [sp, #(24 * 8)] \n" - "stp x26, x27, [sp, #(26 * 8)] \n" - "stp x28, x29, [sp, #(28 * 8)] \n" - "str x30, [sp, #(30 * 8)] \n" - "mov x0, sp \n" - "str x0, %[from_sp] \n" - "ldr x0, =1f \n" - "str x0, %[from_ip] \n" - - "ldr x0, %[to_sp] \n" - "mov sp, x0 \n" - - "sub sp, sp, 32 \n" - "ldr x0, %[from_thread] \n" - "ldr x1, %[to_thread] \n" - "ldr x2, %[to_ip] \n" - "str x0, [sp, #0] \n" - "str x1, [sp, #8] \n" - "str x2, [sp, #16] \n" - - "bl enter_thread_context \n" - "ldr x0, [sp, #16]\n" - "br x0 \n" - - "1: \n" - "add sp, sp, 32 \n" - - "ldp x0, x1, [sp, #(0 * 0)] \n" - "ldp x2, x3, [sp, #(2 * 8)] \n" - "ldp x4, x5, [sp, #(4 * 8)] \n" - "ldp x6, x7, [sp, #(6 * 8)] \n" - "ldp x8, x9, [sp, #(8 * 8)] \n" - "ldp x10, x11, [sp, #(10 * 8)] \n" - "ldp x12, x13, [sp, #(12 * 8)] \n" - "ldp x14, x15, [sp, #(14 * 8)] \n" - "ldp x16, x17, [sp, #(16 * 8)] \n" - "ldp x18, x19, [sp, #(18 * 8)] \n" - "ldp x20, x21, [sp, #(20 * 8)] \n" - "ldp x22, x23, [sp, #(22 * 8)] \n" - "ldp x24, x25, [sp, #(24 * 8)] \n" - "ldp x26, x27, [sp, #(26 * 8)] \n" - "ldp x28, x29, [sp, #(28 * 8)] \n" - "ldr x30, [sp, #(30 * 8)] \n" - - "sub sp, sp, 32 \n" - "ldr x0, [sp, #0] \n" - "ldr x1, [sp, #8] \n" - "str x0, %[from_thread] \n" - "str x1, %[to_thread] \n" - - "add sp, sp, #288 \n" - : - [from_ip] "=m"(from_thread->regs().elr_el1), - [from_sp] "=m"(from_thread->regs().sp_el0), - "=m"(from_thread), - "=m"(to_thread) - - : [to_ip] "m"(to_thread->regs().elr_el1), - [to_sp] "m"(to_thread->regs().sp_el0), - [from_thread] "m"(from_thread), - [to_thread] "m"(to_thread) - : "memory", "x0", "x1", "x2"); - - dbgln_if(CONTEXT_SWITCH_DEBUG, "switch_context <-- from {} {} to {} {}", VirtualAddress(from_thread), *from_thread, VirtualAddress(to_thread), *to_thread); -} - -extern "C" FlatPtr do_init_context(Thread* thread, u32 new_interrupts_state) -{ - VERIFY_INTERRUPTS_DISABLED(); - - Aarch64::SPSR_EL1 spsr_el1 = {}; - memcpy(&spsr_el1, (u8 const*)&thread->regs().spsr_el1, sizeof(u64)); - spsr_el1.I = (new_interrupts_state == to_underlying(InterruptsState::Disabled)); - memcpy((void*)&thread->regs().spsr_el1, &spsr_el1, sizeof(u64)); - - return Processor::current().init_context(*thread, true); -} - -// FIXME: Share this code with other architectures. -template -void ProcessorBase::assume_context(Thread& thread, InterruptsState new_interrupts_state) -{ - dbgln_if(CONTEXT_SWITCH_DEBUG, "Assume context for thread {} {}", VirtualAddress(&thread), thread); - - VERIFY_INTERRUPTS_DISABLED(); - Scheduler::prepare_after_exec(); - // in_critical() should be 2 here. The critical section in Process::exec - // and then the scheduler lock - VERIFY(Processor::in_critical() == 2); - - do_assume_context(&thread, to_underlying(new_interrupts_state)); - - VERIFY_NOT_REACHED(); -} - -template -FlatPtr ProcessorBase::init_context(Thread& thread, bool leave_crit) -{ - VERIFY(g_scheduler_lock.is_locked()); - if (leave_crit) { - // Leave the critical section we set up in Process::exec, - // but because we still have the scheduler lock we should end up with 1 - VERIFY(in_critical() == 2); - m_in_critical = 1; // leave it without triggering anything or restoring flags - } - - u64 kernel_stack_top = thread.kernel_stack_top(); - - // Add a random offset between 0-256 (16-byte aligned) - kernel_stack_top -= round_up_to_power_of_two(get_fast_random(), 16); - - u64 stack_top = kernel_stack_top; - - auto& thread_regs = thread.regs(); - - // Push a RegisterState and TrapFrame onto the stack, which will be popped of the stack and restored into the - // state of the processor by restore_previous_context. - stack_top -= sizeof(RegisterState); - RegisterState& eretframe = *reinterpret_cast(stack_top); - memcpy(eretframe.x, thread_regs.x, sizeof(thread_regs.x)); - - // We don't overwrite the link register if it's not 0, since that means this thread's register state was already initialized with - // an existing link register value (e.g. it was fork()'ed), so we assume exit_kernel_thread is already saved as previous LR on the - // stack somewhere. - if (eretframe.x[30] == 0x0) { - // x30 is the Link Register for the aarch64 ABI, so this will return to exit_kernel_thread when main thread function returns. - eretframe.x[30] = FlatPtr(&exit_kernel_thread); - } - eretframe.elr_el1 = thread_regs.elr_el1; - eretframe.sp_el0 = thread_regs.sp_el0; - eretframe.tpidr_el0 = thread_regs.tpidr_el0; - eretframe.spsr_el1 = thread_regs.spsr_el1; - - // Push a TrapFrame onto the stack - stack_top -= sizeof(TrapFrame); - TrapFrame& trap = *reinterpret_cast(stack_top); - trap.regs = &eretframe; - trap.next_trap = nullptr; - - if constexpr (CONTEXT_SWITCH_DEBUG) { - dbgln("init_context {} ({}) set up to execute at ip={}, sp={}, stack_top={}", - thread, - VirtualAddress(&thread), - VirtualAddress(thread_regs.elr_el1), - VirtualAddress(thread_regs.sp_el0), - VirtualAddress(stack_top)); - } - - // This make sure the thread first executes thread_context_first_enter, which will actually call restore_previous_context - // which restores the context set up above. - thread_regs.set_sp(stack_top); - thread_regs.set_ip(FlatPtr(&thread_context_first_enter)); - - return stack_top; -} - -// FIXME: Figure out if we can fully share this code with x86. -template -void ProcessorBase::exit_trap(TrapFrame& trap) -{ - VERIFY_INTERRUPTS_DISABLED(); - VERIFY(&Processor::current() == this); - - // Temporarily enter a critical section. This is to prevent critical - // sections entered and left within e.g. smp_process_pending_messages - // to trigger a context switch while we're executing this function - // See the comment at the end of the function why we don't use - // ScopedCritical here. - m_in_critical = m_in_critical + 1; - - // FIXME: Figure out if we need prev_irq_level, see duplicated code in Kernel/Arch/x86/common/Processor.cpp - m_in_irq = 0; - - // Process the deferred call queue. Among other things, this ensures - // that any pending thread unblocks happen before we enter the scheduler. - m_deferred_call_pool.execute_pending(); - - auto* current_thread = Processor::current_thread(); - if (current_thread) { - auto& current_trap = current_thread->current_trap(); - current_trap = trap.next_trap; - ExecutionMode new_previous_mode; - if (current_trap) { - VERIFY(current_trap->regs); - new_previous_mode = current_trap->regs->previous_mode(); - } else { - // If we don't have a higher level trap then we're back in user mode. - // Which means that the previous mode prior to being back in user mode was kernel mode - new_previous_mode = ExecutionMode::Kernel; - } - - if (current_thread->set_previous_mode(new_previous_mode)) - current_thread->update_time_scheduled(TimeManagement::scheduler_current_time(), true, false); - } - - VERIFY_INTERRUPTS_DISABLED(); - - // Leave the critical section without actually enabling interrupts. - // We don't want context switches to happen until we're explicitly - // triggering a switch in check_invoke_scheduler. - m_in_critical = m_in_critical - 1; - if (!m_in_irq && !m_in_critical) - check_invoke_scheduler(); -} - -template -ErrorOr> ProcessorBase::capture_stack_trace(Thread& thread, size_t max_frames) -{ - (void)thread; - (void)max_frames; - dbgln("FIXME: Implement Processor::capture_stack_trace() for AArch64"); - return Vector {}; -} - -NAKED void thread_context_first_enter(void) -{ - asm( - "ldr x0, [sp, #0] \n" - "ldr x1, [sp, #8] \n" - "add sp, sp, 32 \n" - "bl context_first_init \n" - "b restore_context_and_eret \n"); -} - -NAKED void do_assume_context(Thread*, u32) -{ - // clang-format off - asm( - "mov x19, x0 \n" // save thread ptr - // We're going to call Processor::init_context, so just make sure - // we have enough stack space so we don't stomp over it - "sub sp, sp, #" __STRINGIFY(8 + REGISTER_STATE_SIZE + TRAP_FRAME_SIZE + 8) " \n" - "bl do_init_context \n" - "mov sp, x0 \n" // move stack pointer to what Processor::init_context set up for us - "mov x0, x19 \n" // to_thread - "mov x1, x19 \n" // from_thread - "sub sp, sp, 32 \n" - "stp x19, x19, [sp] \n" // to_thread, from_thread (for thread_context_first_enter) - "ldr lr, =thread_context_first_enter \n" // should be same as regs.elr_el1 - "b enter_thread_context \n"); - // clang-format on -} - -extern "C" void context_first_init(Thread* from_thread, Thread* to_thread) -{ - do_context_first_init(from_thread, to_thread); -} - -extern "C" void enter_thread_context(Thread* from_thread, Thread* to_thread) -{ - VERIFY(from_thread == to_thread || from_thread->state() != Thread::State::Running); - VERIFY(to_thread->state() == Thread::State::Running); - - Processor::set_current_thread(*to_thread); - - store_fpu_state(&from_thread->fpu_state()); - - auto& from_regs = from_thread->regs(); - auto& to_regs = to_thread->regs(); - if (from_regs.ttbr0_el1 != to_regs.ttbr0_el1) { - Aarch64::Asm::set_ttbr0_el1(to_regs.ttbr0_el1); - Processor::flush_entire_tlb_local(); - } - - to_thread->set_cpu(Processor::current().id()); - - auto in_critical = to_thread->saved_critical(); - VERIFY(in_critical > 0); - Processor::restore_critical(in_critical); - - load_fpu_state(&to_thread->fpu_state()); -} - -template -StringView ProcessorBase::platform_string() -{ - return "aarch64"sv; -} - -template -void ProcessorBase::wait_for_interrupt() const -{ - asm("wfi"); -} - -template -Processor& ProcessorBase::by_id(u32 id) -{ - (void)id; - TODO_AARCH64(); -} - -} - -#include diff --git a/Kernel/Arch/aarch64/Processor.h b/Kernel/Arch/aarch64/Processor.h deleted file mode 100644 index 3f07739f073..00000000000 --- a/Kernel/Arch/aarch64/Processor.h +++ /dev/null @@ -1,219 +0,0 @@ -/* - * Copyright (c) 2018-2021, James Mintram - * Copyright (c) 2022, Timon Kruiper - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -#include -VALIDATE_IS_AARCH64() - -namespace Kernel { - -namespace Memory { -class PageDirectory; -}; - -class Thread; -class Processor; -struct TrapFrame; -enum class InterruptsState; - -template -class ProcessorBase; - -// FIXME: Remove this once we support SMP in aarch64 -extern Processor* g_current_processor; - -constexpr size_t MAX_CPU_COUNT = 1; - -class Processor final : public ProcessorBase { -public: - template Callback> - static inline IterationDecision for_each(Callback callback) - { - // FIXME: Once we support SMP for aarch64, make sure to call the callback for every processor. - if (callback(*g_current_processor) == IterationDecision::Break) - return IterationDecision::Break; - return IterationDecision::Continue; - } - - template Callback> - static inline IterationDecision for_each(Callback callback) - { - // FIXME: Once we support SMP for aarch64, make sure to call the callback for every processor. - callback(*g_current_processor); - return IterationDecision::Continue; - } -}; - -template -ALWAYS_INLINE bool ProcessorBase::is_initialized() -{ - return g_current_processor != nullptr; -} - -template -ALWAYS_INLINE Thread* ProcessorBase::idle_thread() -{ - return current().m_idle_thread; -} - -template -ALWAYS_INLINE void ProcessorBase::set_current_thread(Thread& current_thread) -{ - current().m_current_thread = ¤t_thread; -} - -// FIXME: When aarch64 supports multiple cores, return the correct core id here. -template -ALWAYS_INLINE u32 ProcessorBase::current_id() -{ - return 0; -} - -template -ALWAYS_INLINE u32 ProcessorBase::in_critical() -{ - return current().m_in_critical; -} - -template -ALWAYS_INLINE void ProcessorBase::enter_critical() -{ - auto& current_processor = current(); - current_processor.m_in_critical = current_processor.m_in_critical + 1; -} - -template -ALWAYS_INLINE void ProcessorBase::restore_critical(u32 prev_critical) -{ - current().m_in_critical = prev_critical; -} - -template -ALWAYS_INLINE T& ProcessorBase::current() -{ - return *g_current_processor; -} - -template -void ProcessorBase::idle_begin() const -{ - // FIXME: Implement this when SMP for aarch64 is supported. -} - -template -void ProcessorBase::idle_end() const -{ - // FIXME: Implement this when SMP for aarch64 is supported. -} - -template -void ProcessorBase::smp_enable() -{ - // FIXME: Implement this when SMP for aarch64 is supported. -} - -template -bool ProcessorBase::is_smp_enabled() -{ - return false; -} - -template -ALWAYS_INLINE bool ProcessorBase::are_interrupts_enabled() -{ - auto daif = Aarch64::DAIF::read(); - return !daif.I; -} - -template -ALWAYS_INLINE void ProcessorBase::enable_interrupts() -{ - Aarch64::DAIF::clear_I(); -} - -template -ALWAYS_INLINE void ProcessorBase::disable_interrupts() -{ - Aarch64::DAIF::set_I(); -} - -template -ALWAYS_INLINE bool ProcessorBase::is_kernel_mode() -{ - // FIXME: Implement this correctly. - return true; -} - -template -ALWAYS_INLINE bool ProcessorBase::current_in_scheduler() -{ - return current().m_in_scheduler; -} - -template -ALWAYS_INLINE void ProcessorBase::set_current_in_scheduler(bool value) -{ - current().m_in_scheduler = value; -} - -template -ALWAYS_INLINE bool ProcessorBase::has_nx() const -{ - return true; -} - -template -ALWAYS_INLINE bool ProcessorBase::has_pat() const -{ - return false; -} - -template -ALWAYS_INLINE FlatPtr ProcessorBase::current_in_irq() -{ - return current().m_in_irq; -} - -template -ALWAYS_INLINE Thread* ProcessorBase::current_thread() -{ - return current().m_current_thread; -} - -template -ALWAYS_INLINE void ProcessorBase::pause() -{ - asm volatile("isb sy"); -} - -template -ALWAYS_INLINE void ProcessorBase::wait_check() -{ - asm volatile("yield"); - // FIXME: Process SMP messages once we support SMP on aarch64; cf. x86_64 -} - -template -ALWAYS_INLINE u64 ProcessorBase::read_cpu_counter() -{ - TODO_AARCH64(); - return 0; -} - -} diff --git a/Kernel/Arch/aarch64/RPi/DebugOutput.cpp b/Kernel/Arch/aarch64/RPi/DebugOutput.cpp deleted file mode 100644 index 87a3b35a6fd..00000000000 --- a/Kernel/Arch/aarch64/RPi/DebugOutput.cpp +++ /dev/null @@ -1,17 +0,0 @@ -/* - * Copyright (c) 2022, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include - -namespace Kernel { - -void debug_output(char ch) -{ - RPi::UART::the().send(ch); -} - -} diff --git a/Kernel/Arch/aarch64/RPi/Framebuffer.cpp b/Kernel/Arch/aarch64/RPi/Framebuffer.cpp deleted file mode 100644 index 7fb4c5a96c1..00000000000 --- a/Kernel/Arch/aarch64/RPi/Framebuffer.cpp +++ /dev/null @@ -1,135 +0,0 @@ -/* - * Copyright (c) 2021, Marcin Undak - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include -#include - -namespace Kernel::RPi { - -Framebuffer::Framebuffer() -{ - // FIXME: query HDMI for best mode - // https://github.com/raspberrypi/userland/blob/master/host_applications/linux/apps/tvservice/tvservice.c - m_width = 1280; - m_height = 720; - m_depth = 32; - m_initialized = false; - - struct __attribute__((aligned(16))) { - Mailbox::MessageHeader header; - FramebufferSetPhysicalSizeMboxMessage set_physical_size; - FramebufferSetVirtualSizeMboxMessage set_virtual_size; - FramebufferSetVirtualOffsetMboxMessage set_virtual_offset; - FramebufferSetDepthMboxMessage set_depth; - FramebufferSetPixelOrderMboxMessage set_pixel_order; - FramebufferAllocateBufferMboxMessage allocate_buffer; - FramebufferGetPitchMboxMessage get_pitch; - Mailbox::MessageTail tail; - } message_queue; - - message_queue.header.set_queue_size(sizeof(message_queue)); - message_queue.set_physical_size.width = m_width; - message_queue.set_physical_size.height = m_height; - message_queue.set_virtual_size.width = m_width; - message_queue.set_virtual_size.height = m_height; - - // FIXME! those next 2 lines crash... - // message_queue.set_virtual_offset.x = 0; - // message_queue.set_virtual_offset.y = 0; - - message_queue.set_depth.depth_bits = 32; - message_queue.set_pixel_order.pixel_order = FramebufferSetPixelOrderMboxMessage::PixelOrder::BGR; - message_queue.allocate_buffer.alignment = 4096; - - if (!Mailbox::the().send_queue(&message_queue, sizeof(message_queue))) { - dbgln("Framebuffer(): Mailbox send failed."); - return; - } - - // Now message queue contains responses. Process them. - - if (message_queue.set_physical_size.width != m_width || message_queue.set_physical_size.height != m_height) { - dbgln("Framebuffer(): Setting physical dimension failed."); - return; - } - - if (message_queue.set_virtual_size.width != m_width || message_queue.set_virtual_size.height != m_height) { - dbgln("Framebuffer(): Setting virtual dimension failed."); - return; - } - - if (message_queue.set_virtual_offset.x != 0 || message_queue.set_virtual_offset.y != 0) { - dbgln("Framebuffer(): Setting virtual offset failed."); - return; - } - - if (message_queue.set_depth.depth_bits != m_depth) { - dbgln("Framebuffer(): Setting depth failed."); - return; - } - - if (message_queue.allocate_buffer.size == 0 || message_queue.allocate_buffer.address == 0) { - dbgln("Framebuffer(): Allocating buffer failed."); - return; - } - - if (message_queue.get_pitch.pitch == 0) { - dbgln("Framebuffer(): Retrieving pitch failed."); - return; - } - - // Convert GPU address space to RAM - // GPU maps memory from 0x80000000 instead of 0x00000000 - m_gpu_buffer = reinterpret_cast(message_queue.allocate_buffer.address & 0x3FFFFFFF); - - m_buffer_size = message_queue.allocate_buffer.size; - m_pitch = message_queue.get_pitch.pitch; - - switch (message_queue.set_pixel_order.pixel_order) { - case FramebufferSetPixelOrderMboxMessage::PixelOrder::RGB: - m_pixel_order = PixelOrder::RGB; - break; - case FramebufferSetPixelOrderMboxMessage::PixelOrder::BGR: - m_pixel_order = PixelOrder::BGR; - break; - default: - dbgln("Framebuffer(): Unsupported pixel order reported by GPU."); - m_pixel_order = PixelOrder::RGB; - break; - } - - dbgln("Initialized framebuffer: {} x {} @ {} bits", m_width, m_height, m_depth); - m_initialized = true; -} - -Framebuffer& Framebuffer::the() -{ - static Framebuffer instance; - return instance; -} - -void Framebuffer::initialize() -{ - auto& framebuffer = the(); - if (framebuffer.initialized()) { - multiboot_framebuffer_addr = PhysicalAddress((PhysicalPtr)framebuffer.gpu_buffer()); - multiboot_framebuffer_width = framebuffer.width(); - multiboot_framebuffer_height = framebuffer.height(); - multiboot_framebuffer_pitch = framebuffer.pitch(); - - // NOTE: The required pixel format for MULTIBOOT_FRAMEBUFFER_TYPE_RGB is actually BGRx8888. - VERIFY(framebuffer.pixel_order() == PixelOrder::BGR); - multiboot_framebuffer_type = MULTIBOOT_FRAMEBUFFER_TYPE_RGB; - - multiboot_flags |= MULTIBOOT_INFO_FRAMEBUFFER_INFO; - } -} - -} diff --git a/Kernel/Arch/aarch64/RPi/Framebuffer.h b/Kernel/Arch/aarch64/RPi/Framebuffer.h deleted file mode 100644 index eb1da572c1e..00000000000 --- a/Kernel/Arch/aarch64/RPi/Framebuffer.h +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright (c) 2021, Marcin Undak - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include - -namespace Kernel::RPi { - -class Framebuffer { -public: - enum class PixelOrder { - RGB, - BGR, - }; - - static Framebuffer& the(); - static void initialize(); - - bool initialized() const { return m_initialized; } - u16 width() const { return m_width; } - u16 height() const { return m_height; } - u8 depth() const { return m_depth; } - u8* gpu_buffer() const { return m_gpu_buffer; } - u32 buffer_size() const { return m_buffer_size; } - u32 pitch() const { return m_pitch; } - PixelOrder pixel_order() { return m_pixel_order; } - -private: - u16 m_width; - u16 m_height; - u8 m_depth; - u8* m_gpu_buffer; - u32 m_buffer_size; - u32 m_pitch; - bool m_initialized; - PixelOrder m_pixel_order; - - Framebuffer(); -}; -} diff --git a/Kernel/Arch/aarch64/RPi/FramebufferMailboxMessages.h b/Kernel/Arch/aarch64/RPi/FramebufferMailboxMessages.h deleted file mode 100644 index 1415f449326..00000000000 --- a/Kernel/Arch/aarch64/RPi/FramebufferMailboxMessages.h +++ /dev/null @@ -1,113 +0,0 @@ -/* - * Copyright (c) 2021, Marcin Undak - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include - -namespace Kernel::RPi { - -class FramebufferSetPhysicalSizeMboxMessage : public Mailbox::Message { -public: - u32 width; - u32 height; - - FramebufferSetPhysicalSizeMboxMessage() - : Mailbox::Message(0x48003, 8) - { - width = 0; - height = 0; - } -}; -static_assert(sizeof(FramebufferSetPhysicalSizeMboxMessage) == 20); - -class FramebufferSetVirtualSizeMboxMessage : public Mailbox::Message { -public: - u32 width; - u32 height; - - FramebufferSetVirtualSizeMboxMessage() - : Mailbox::Message(0x48004, 8) - { - width = 0; - height = 0; - } -}; -static_assert(sizeof(FramebufferSetVirtualSizeMboxMessage) == 20); - -class FramebufferSetVirtualOffsetMboxMessage : public Mailbox::Message { -public: - u32 x; - u32 y; - - FramebufferSetVirtualOffsetMboxMessage() - : Mailbox::Message(0x48009, 8) - { - x = 0; - y = 0; - } -}; -static_assert(sizeof(FramebufferSetVirtualOffsetMboxMessage) == 20); - -class FramebufferSetDepthMboxMessage : public Mailbox::Message { -public: - u32 depth_bits; - - FramebufferSetDepthMboxMessage() - : Mailbox::Message(0x48005, 4) - { - depth_bits = 0; - } -}; -static_assert(sizeof(FramebufferSetDepthMboxMessage) == 16); - -class FramebufferSetPixelOrderMboxMessage : public Mailbox::Message { -public: - enum PixelOrder : u32 { - BGR = 0, - RGB = 1 - }; - - PixelOrder pixel_order; - - FramebufferSetPixelOrderMboxMessage() - : Mailbox::Message(0x48006, 4) - { - pixel_order = PixelOrder::BGR; - } -}; -static_assert(sizeof(FramebufferSetPixelOrderMboxMessage) == 16); - -class FramebufferAllocateBufferMboxMessage : public Mailbox::Message { -public: - union { - u32 alignment; - u32 address; - }; - u32 size = 0; - - FramebufferAllocateBufferMboxMessage() - : Mailbox::Message(0x40001, 8) - { - alignment = 0; - size = 0; - } -}; -static_assert(sizeof(FramebufferAllocateBufferMboxMessage) == 20); - -class FramebufferGetPitchMboxMessage : public Mailbox::Message { -public: - u32 pitch; - - FramebufferGetPitchMboxMessage() - : Mailbox::Message(0x40008, 4) - { - pitch = 0; - } -}; -static_assert(sizeof(FramebufferGetPitchMboxMessage) == 16); - -} diff --git a/Kernel/Arch/aarch64/RPi/GPIO.cpp b/Kernel/Arch/aarch64/RPi/GPIO.cpp deleted file mode 100644 index aef8d3e5c48..00000000000 --- a/Kernel/Arch/aarch64/RPi/GPIO.cpp +++ /dev/null @@ -1,112 +0,0 @@ -/* - * Copyright (c) 2021, Nico Weber - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include - -namespace Kernel::RPi { - -// See BCM2835-ARM-Peripherals.pdf section "6 General Purpose I/O" or bcm2711-peripherals.pdf "Chapter 5. General Purpose I/O". - -// "6.1 Register View" / "5.2 Register View" - -struct PinData { - u32 bits[2]; - u32 reserved; -}; - -struct GPIOControlRegisters { - u32 function_select[6]; // Every u32 stores a 3-bit function code for 10 pins. - u32 reserved; - PinData output_set; - PinData output_clear; - PinData level; - PinData event_detect_status; - PinData rising_edge_detect_enable; - PinData falling_edge_detect_enable; - PinData high_detect_enable; - PinData low_detect_enable; - PinData async_rising_edge_detect_enable; - PinData async_falling_edge_detect_enable; - u32 pull_up_down_enable; - PinData pull_up_down_enable_clock; - u32 test; -}; - -GPIO::GPIO() - : m_registers(MMIO::the().peripheral(0x20'0000)) -{ -} - -GPIO& GPIO::the() -{ - static GPIO instance; - return instance; -} - -void GPIO::set_pin_function(unsigned pin_number, PinFunction function) -{ - // pin_number must be <= 53. We can't VERIFY() that since this function runs too early to print assertion failures. - - unsigned function_select_index = pin_number / 10; - unsigned function_select_bits_start = (pin_number % 10) * 3; - - u32 function_bits = m_registers->function_select[function_select_index]; - function_bits = (function_bits & ~(0b111 << function_select_bits_start)) | (static_cast(function) << function_select_bits_start); - m_registers->function_select[function_select_index] = function_bits; -} - -void GPIO::internal_enable_pins(u32 enable[2], PullUpDownState state) -{ - // Section "GPIO Pull-up/down Clock Registers (GPPUDCLKn)": - // The GPIO Pull-up/down Clock Registers control the actuation of internal pull-downs on - // the respective GPIO pins. These registers must be used in conjunction with the GPPUD - // register to effect GPIO Pull-up/down changes. The following sequence of events is - // required: - // 1. Write to GPPUD to set the required control signal (i.e. Pull-up or Pull-Down or neither - // to remove the current Pull-up/down) - m_registers->pull_up_down_enable = static_cast(state); - - // 2. Wait 150 cycles – this provides the required set-up time for the control signal - Aarch64::Asm::wait_cycles(150); - - // 3. Write to GPPUDCLK0/1 to clock the control signal into the GPIO pads you wish to - // modify – NOTE only the pads which receive a clock will be modified, all others will - // retain their previous state. - m_registers->pull_up_down_enable_clock.bits[0] = enable[0]; - m_registers->pull_up_down_enable_clock.bits[1] = enable[1]; - - // 4. Wait 150 cycles – this provides the required hold time for the control signal - Aarch64::Asm::wait_cycles(150); - - // 5. Write to GPPUD to remove the control signal - m_registers->pull_up_down_enable = 0; - - // 6. Write to GPPUDCLK0/1 to remove the clock - m_registers->pull_up_down_enable_clock.bits[0] = 0; - m_registers->pull_up_down_enable_clock.bits[1] = 0; - - // bcm2711-peripherals.pdf documents GPIO_PUP_PDN_CNTRL_REG[4] registers that store 2 bits state per register, similar to function_select. - // I don't know if the RPi3 has that already, so this uses the old BCM2835 approach for now. -} - -void GPIO::set_pin_high_detect_enable(unsigned pin_number, bool enable) -{ - if (enable) { - if (pin_number < 32) - m_registers->high_detect_enable.bits[0] = m_registers->high_detect_enable.bits[0] | (1 << pin_number); - else - m_registers->high_detect_enable.bits[1] = m_registers->high_detect_enable.bits[1] | (1 << (pin_number - 32)); - } else { - if (pin_number < 32) - m_registers->high_detect_enable.bits[0] = m_registers->high_detect_enable.bits[0] & ~(1 << pin_number); - else - m_registers->high_detect_enable.bits[1] = m_registers->high_detect_enable.bits[1] & ~(1 << (pin_number - 32)); - } -} - -} diff --git a/Kernel/Arch/aarch64/RPi/GPIO.h b/Kernel/Arch/aarch64/RPi/GPIO.h deleted file mode 100644 index a01e2762510..00000000000 --- a/Kernel/Arch/aarch64/RPi/GPIO.h +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright (c) 2021, Nico Weber - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include - -namespace Kernel::RPi { - -struct GPIOControlRegisters; - -// Can configure the general-purpose I/O registers on a Raspberry Pi. -class GPIO { -public: - enum class PinFunction { - Input = 0, - Output = 1, - Alternate0 = 0b100, - Alternate1 = 0b101, - Alternate2 = 0b110, - Alternate3 = 0b111, - Alternate4 = 0b011, - Alternate5 = 0b010, - }; - - static GPIO& the(); - - void set_pin_function(unsigned pin_number, PinFunction); - - enum class PullUpDownState { - Disable = 0, - PullDown = 1, - PullUp = 2, - }; - - template - void set_pin_pull_up_down_state(Array pins, PullUpDownState state) - { - u32 enable[2] = {}; - for (int pin : pins) { - if (pin < 32) - enable[0] |= (1 << pin); - else - enable[1] |= (1 << (pin - 32)); - } - internal_enable_pins(enable, state); - } - - void set_pin_high_detect_enable(unsigned pin_number, bool enable); - -private: - GPIO(); - void internal_enable_pins(u32 enable[2], PullUpDownState state); - - GPIOControlRegisters volatile* m_registers; -}; - -} diff --git a/Kernel/Arch/aarch64/RPi/InterruptController.cpp b/Kernel/Arch/aarch64/RPi/InterruptController.cpp deleted file mode 100644 index fb603401a98..00000000000 --- a/Kernel/Arch/aarch64/RPi/InterruptController.cpp +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright (c) 2022, Timon Kruiper - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include - -namespace Kernel::RPi { - -// "7.5 Interrupts Registers" -// https://github.com/raspberrypi/documentation/files/1888662/BCM2837-ARM-Peripherals.-.Revised.-.V2-1.pdf -struct InterruptControllerRegisters { - u32 irq_basic_pending; - u32 irq_pending_1; - u32 irq_pending_2; - u32 fiq_control; - - u32 enable_irqs_1; - u32 enable_irqs_2; - u32 enable_basic_irqs; - - u32 disable_irqs_1; - u32 disable_irqs_2; - u32 disable_basic_irqs; -}; - -InterruptController::InterruptController() - : m_registers(MMIO::the().peripheral(0xB200)) -{ -} - -void InterruptController::enable(GenericInterruptHandler const& handler) -{ - u8 interrupt_number = handler.interrupt_number(); - VERIFY(interrupt_number < 64); - - if (interrupt_number < 32) - m_registers->enable_irqs_1 = m_registers->enable_irqs_1 | (1 << interrupt_number); - else - m_registers->enable_irqs_2 = m_registers->enable_irqs_2 | (1 << (interrupt_number - 32)); -} - -void InterruptController::disable(GenericInterruptHandler const& handler) -{ - u8 interrupt_number = handler.interrupt_number(); - VERIFY(interrupt_number < 64); - - if (interrupt_number < 32) - m_registers->disable_irqs_1 = m_registers->disable_irqs_1 | (1 << interrupt_number); - else - m_registers->disable_irqs_2 = m_registers->disable_irqs_2 | (1 << (interrupt_number - 32)); -} - -void InterruptController::eoi(GenericInterruptHandler const&) const -{ - // NOTE: The interrupt controller cannot clear the interrupt, since it is basically just a big multiplexer. - // The interrupt should be cleared by the corresponding device driver, such as a timer or uart. -} - -u64 InterruptController::pending_interrupts() const -{ - return ((u64)m_registers->irq_pending_2 << 32) | (u64)m_registers->irq_pending_1; -} - -} diff --git a/Kernel/Arch/aarch64/RPi/InterruptController.h b/Kernel/Arch/aarch64/RPi/InterruptController.h deleted file mode 100644 index 0730a9e8863..00000000000 --- a/Kernel/Arch/aarch64/RPi/InterruptController.h +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright (c) 2022, Timon Kruiper - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include - -namespace Kernel::RPi { - -struct InterruptControllerRegisters; - -// This class implements the simple Interrupt Controller found in the BCM2837. (RPi3) -// A description of this device can be found at chapter 7 (Interrupts) of the manual: -// https://github.com/raspberrypi/documentation/files/1888662/BCM2837-ARM-Peripherals.-.Revised.-.V2-1.pdf (RPi3) -class InterruptController : public IRQController { -public: - InterruptController(); - -private: - virtual void enable(GenericInterruptHandler const&) override; - virtual void disable(GenericInterruptHandler const&) override; - - virtual void eoi(GenericInterruptHandler const&) const override; - - virtual u64 pending_interrupts() const override; - - virtual StringView model() const override - { - return "Raspberry Pi Interrupt Controller"sv; - } - - InterruptControllerRegisters volatile* m_registers; -}; - -} diff --git a/Kernel/Arch/aarch64/RPi/MMIO.cpp b/Kernel/Arch/aarch64/RPi/MMIO.cpp deleted file mode 100644 index 031ed3bdfd9..00000000000 --- a/Kernel/Arch/aarch64/RPi/MMIO.cpp +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright (c) 2021, Nico Weber - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include - -namespace Kernel::RPi { - -MMIO::MMIO() - : m_base_address(0xFE00'0000) -{ - MainIdRegister id; - if (id.part_num() <= MainIdRegister::RaspberryPi3) - m_base_address = PhysicalAddress(0x3F00'0000); -} - -MMIO& MMIO::the() -{ - static MMIO instance; - return instance; -} - -} diff --git a/Kernel/Arch/aarch64/RPi/MMIO.h b/Kernel/Arch/aarch64/RPi/MMIO.h deleted file mode 100644 index edcefcd5553..00000000000 --- a/Kernel/Arch/aarch64/RPi/MMIO.h +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright (c) 2021, Nico Weber - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include - -namespace Kernel::RPi { - -// Knows about memory-mapped IO addresses on the Broadcom family of SOCs used in Raspberry Pis. -// RPi3 is the first Raspberry Pi that supports aarch64. -// https://github.com/raspberrypi/documentation/files/1888662/BCM2837-ARM-Peripherals.-.Revised.-.V2-1.pdf (RPi3) -// https://datasheets.raspberrypi.org/bcm2711/bcm2711-peripherals.pdf (RPi4 Model B) -class MMIO { -public: - static MMIO& the(); - - u32 read(FlatPtr offset) { return *peripheral_address(offset); } - void write(FlatPtr offset, u32 value) { *peripheral_address(offset) = value; } - - // FIXME: The MMIO region is currently mapped at kernel_mapping_base + peripheral_base_address(), - // but the code should be changed to use the MemoryManager to map the physical memory instead - // of pre-mapping the whole MMIO region. - u32 volatile* peripheral_address(FlatPtr offset) { return (u32 volatile*)(kernel_mapping_base + m_base_address.get() + offset); } - template - T volatile* peripheral(FlatPtr offset) { return (T volatile*)peripheral_address(offset); } - - PhysicalAddress peripheral_base_address() const { return m_base_address; } - PhysicalAddress peripheral_end_address() const { return m_base_address.offset(0x00FFFFFF); } - -private: - MMIO(); - - PhysicalAddress m_base_address; -}; - -} diff --git a/Kernel/Arch/aarch64/RPi/Mailbox.cpp b/Kernel/Arch/aarch64/RPi/Mailbox.cpp deleted file mode 100644 index 5379114f3fa..00000000000 --- a/Kernel/Arch/aarch64/RPi/Mailbox.cpp +++ /dev/null @@ -1,231 +0,0 @@ -/* - * Copyright (c) 2021, Nico Weber - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include - -namespace Kernel::RPi { - -// There's one mailbox at MBOX_BASE_OFFSET for reading responses from VideoCore, and one at MBOX_BASE_OFFSET + 0x20 for sending requests. -// Each has its own status word. - -constexpr u32 MBOX_BASE_OFFSET = 0xB880; -constexpr u32 MBOX_0 = MBOX_BASE_OFFSET; -constexpr u32 MBOX_1 = MBOX_BASE_OFFSET + 0x20; - -constexpr u32 MBOX_READ_DATA = MBOX_0; -constexpr u32 MBOX_READ_POLL = MBOX_0 + 0x10; -constexpr u32 MBOX_READ_SENDER = MBOX_0 + 0x14; -constexpr u32 MBOX_READ_STATUS = MBOX_0 + 0x18; -constexpr u32 MBOX_READ_CONFIG = MBOX_0 + 0x1C; - -constexpr u32 MBOX_WRITE_DATA = MBOX_1; -constexpr u32 MBOX_WRITE_STATUS = MBOX_1 + 0x18; - -constexpr u32 MBOX_RESPONSE_SUCCESS = 0x8000'0000; -constexpr u32 MBOX_RESPONSE_PARTIAL = 0x8000'0001; -constexpr u32 MBOX_REQUEST = 0; -constexpr u32 MBOX_FULL = 0x8000'0000; -constexpr u32 MBOX_EMPTY = 0x4000'0000; - -constexpr int ARM_TO_VIDEOCORE_CHANNEL = 8; - -Mailbox::Message::Message(u32 tag, u32 arguments_size) -{ - m_tag = tag; - m_arguments_size = arguments_size; - m_command_tag = MBOX_REQUEST; -} - -Mailbox::MessageHeader::MessageHeader() -{ - m_message_queue_size = 0; - m_command_tag = MBOX_REQUEST; -} - -bool Mailbox::MessageHeader::success() const -{ - return m_command_tag == MBOX_RESPONSE_SUCCESS; -} - -Mailbox& Mailbox::the() -{ - static Mailbox instance; - return instance; -} - -static void wait_until_we_can_write(MMIO& mmio) -{ - // Since nothing else writes to the mailbox, this wait is mostly cargo-culted. - // Most baremetal tutorials on the internet query MBOX_READ_STATUS here, which I think is incorrect and only works because this wait really isn't needed. - while (mmio.read(MBOX_WRITE_STATUS) & MBOX_FULL) - Processor::wait_check(); -} - -static void wait_for_reply(MMIO& mmio) -{ - while (mmio.read(MBOX_READ_STATUS) & MBOX_EMPTY) - Processor::wait_check(); -} - -bool Mailbox::send_queue(void* queue, u32 queue_size) const -{ - // According to Raspberry Pi specs this is the only channel implemented. - u32 const channel = ARM_TO_VIDEOCORE_CHANNEL; - - auto message_header = reinterpret_cast(queue); - message_header->set_queue_size(queue_size); - - auto& mmio = MMIO::the(); - - // The mailbox interface has a FIFO for message delivery in both directions. - // Responses can be delivered out of order to requests, but we currently ever only send on request at once. - // It'd be nice to have an async interface here where we send a message, then return immediately, and read the response when an interrupt arrives. - // But for now, this is synchronous. - - wait_until_we_can_write(mmio); - - // The mailbox message is 32-bit based, so this assumes that message is in the first 4 GiB. - u32 request = static_cast(reinterpret_cast(queue) & ~0xF) | (channel & 0xF); - - // The queue buffer might point to normal cached memory, so flush any writes that are in cache and not visible to VideoCore. - Aarch64::Asm::flush_data_cache((FlatPtr)queue, queue_size); - - mmio.write(MBOX_WRITE_DATA, request); - - for (;;) { - wait_for_reply(mmio); - - u32 response = mmio.read(MBOX_READ_DATA); - // We keep at most one message in flight and do synchronous communication, so response will always be == request for us. - if (response == request) - return message_header->success(); - } - - return true; -} - -class QueryFirmwareVersionMboxMessage : RPi::Mailbox::Message { -public: - u32 version; - - QueryFirmwareVersionMboxMessage() - : RPi::Mailbox::Message(0x0000'0001, 4) - { - version = 0; - } -}; - -u32 Mailbox::query_firmware_version() -{ - struct __attribute__((aligned(16))) { - MessageHeader header; - QueryFirmwareVersionMboxMessage query_firmware_version; - MessageTail tail; - } message_queue; - - if (!the().send_queue(&message_queue, sizeof(message_queue))) { - return 0xffff'ffff; - } - - return message_queue.query_firmware_version.version; -} - -// https://github.com/raspberrypi/firmware/wiki/Mailbox-property-interface#get-command-line -// -// Note: This function is called very early in the boot process, before the heap or the console -// is initialized. Please ensure that it does the minimum amount of work possible. -StringView Mailbox::query_kernel_command_line(Bytes buffer) -{ - // We want to use the user-provided buffer rather than a fixed-size one on the stack, - // so we need to construct the message manually. - auto aligned_buffer = buffer.align_to(16); - if (aligned_buffer.size() < 24) - return ""sv; - - auto max_response_length = aligned_buffer.size() - 24; - - auto* message = reinterpret_cast(aligned_buffer.data()); - message[0] = aligned_buffer.size(); - message[1] = MBOX_REQUEST; - - message[2] = 0x0005'0001; // Query command line - message[3] = max_response_length; - message[4] = max_response_length; - - message[aligned_buffer.size() / sizeof(u32) - 1] = 0; - - if (!the().send_queue(message, aligned_buffer.size())) - return ""sv; - - // Bit 31 indicates that this is a response, the rest denote the length. - auto response_length = message[4] & 0x7fff'ffff; - - if (response_length > max_response_length) - return ""sv; // The buffer was too small to hold the response. - - return StringView { (char const*)&message[5], response_length }; -} - -class QueryARMMemoryMailboxMessage : RPi::Mailbox::Message { -public: - u32 base; - u32 size; - - QueryARMMemoryMailboxMessage() - : RPi::Mailbox::Message(0x0001'0005, 8) - { - base = 0; - size = 0; - } -}; - -Mailbox::MemoryRange Mailbox::query_lower_arm_memory_range() -{ - struct __attribute__((aligned(16))) { - MessageHeader header; - QueryARMMemoryMailboxMessage query_arm_memory; - MessageTail tail; - } message_queue; - - if (!the().send_queue(&message_queue, sizeof(message_queue))) { - PANIC("Failed to determine the available RAM range"); - } - - return { message_queue.query_arm_memory.base, message_queue.query_arm_memory.size }; -} - -class QueryVCMemoryMailboxMessage : RPi::Mailbox::Message { -public: - u32 base; - u32 size; - - QueryVCMemoryMailboxMessage() - : RPi::Mailbox::Message(0x0001'0006, 8) - { - base = 0; - size = 0; - } -}; - -Mailbox::MemoryRange Mailbox::query_videocore_memory_range() -{ - struct __attribute__((aligned(16))) { - MessageHeader header; - QueryVCMemoryMailboxMessage query_vc_memory; - MessageTail tail; - } message_queue; - - if (!the().send_queue(&message_queue, sizeof(message_queue))) { - PANIC("Failed to determine the VideoCore memory range"); - } - - return { message_queue.query_vc_memory.base, message_queue.query_vc_memory.size }; -} - -} diff --git a/Kernel/Arch/aarch64/RPi/Mailbox.h b/Kernel/Arch/aarch64/RPi/Mailbox.h deleted file mode 100644 index ec20d4ac00f..00000000000 --- a/Kernel/Arch/aarch64/RPi/Mailbox.h +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Copyright (c) 2021, Nico Weber - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include - -namespace Kernel::RPi { - -// Can exchange mailbox messages with the Raspberry Pi's VideoCore chip. -// https://github.com/raspberrypi/firmware/wiki/Mailbox-property-interface -class Mailbox { -public: - // Base class for Mailbox messages. Implemented in subsystems that use Mailbox. - class Message { - protected: - Message(u32 tag, u32 arguments_size); - - private: - u32 m_tag; - u32 m_arguments_size; - u32 m_command_tag; - }; - - // Must be at the beginning of every command message queue - class MessageHeader { - public: - MessageHeader(); - - u32 queue_size() { return m_message_queue_size; } - void set_queue_size(u32 size) { m_message_queue_size = size; } - bool success() const; - - private: - u32 m_message_queue_size; - u32 m_command_tag; - }; - - // Must be at the end of every command message queue - class MessageTail { - private: - u32 m_empty_tag = 0; - }; - - static Mailbox& the(); - - // Sends message queue to VideoCore - bool send_queue(void* queue, u32 queue_size) const; - - u32 query_firmware_version(); - - // Returns the kernel command line as a StringView into the given buffer. - StringView query_kernel_command_line(Bytes buffer); - - struct MemoryRange { - u32 base; - u32 size; - }; - // Returns the RAM available to the CPU in the first 1GB of the physical address space. - // FIXME: Remove in favor of parsing the device tree. - MemoryRange query_lower_arm_memory_range(); - MemoryRange query_videocore_memory_range(); -}; - -} diff --git a/Kernel/Arch/aarch64/RPi/MiniUART.cpp b/Kernel/Arch/aarch64/RPi/MiniUART.cpp deleted file mode 100644 index 98d305ab4a3..00000000000 --- a/Kernel/Arch/aarch64/RPi/MiniUART.cpp +++ /dev/null @@ -1,135 +0,0 @@ -/* - * Copyright (c) 2023, Daniel Bertalan - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include - -namespace Kernel::RPi { - -// bcm2711-peripherals.pdf "Table 2. Auxiliary peripherals Address Map" -struct MiniUARTRegisters { - u32 io_data; - u32 interrupt_enable; - u32 interrupt_identify; - u32 line_control; - u32 modem_control; - u32 line_status; - u32 modem_status; - u32 extra_control; - u32 extra_status; - u32 baud_rate; -}; - -// "Table 4. AUX_ENABLES Register" -enum AuxControlBits { - MiniUARTEnable = 1, - SPI1Enable = 1 << 1, - SPI2Enable = 1 << 2, -}; - -// "Table 8. AUX_MU_LCR_REG Register" -enum LineControl { - DataSize8Bits = 1, - Break = 1 << 6, - DLABAccess = 1 << 7, -}; - -// "Table 13. AUX_MU_CNTL_REG Register" -enum ExtraControl { - ReceiverEnable = 1, - TransmitterEnable = 2, -}; - -// "Table 10. AUX_MU_LSR_REG Register" -enum LineStatus { - DataReady = 0, - ReceiverOverrun = 1, - TransmitterEmpty = 1 << 5, - TransmitterIdle = 1 << 6, -}; - -constexpr FlatPtr AUX_ENABLES = 0x21'5000; - -UNMAP_AFTER_INIT ErrorOr> MiniUART::create() -{ - return DeviceManagement::try_create_device(); -} - -UNMAP_AFTER_INIT MiniUART::MiniUART() - : CharacterDevice(4, 64) - , m_registers(MMIO::the().peripheral(0x21'5040)) -{ - auto& gpio = GPIO::the(); - gpio.set_pin_function(40, GPIO::PinFunction::Alternate5); // TXD1 - gpio.set_pin_function(41, GPIO::PinFunction::Alternate5); // RXD1 - gpio.set_pin_pull_up_down_state(Array { 40, 41 }, GPIO::PullUpDownState::Disable); - - // The mini UART peripheral needs to be enabled before we can configure it. - MMIO::the().write(AUX_ENABLES, MMIO::the().read(AUX_ENABLES) | MiniUARTEnable); - - set_baud_rate(115'200); - m_registers->line_control = DataSize8Bits; - m_registers->extra_control = ReceiverEnable | TransmitterEnable; -} - -UNMAP_AFTER_INIT MiniUART::~MiniUART() = default; - -bool MiniUART::can_read(OpenFileDescription const&, u64) const -{ - return false; -} - -ErrorOr MiniUART::read(OpenFileDescription&, u64, UserOrKernelBuffer&, size_t) -{ - // FIXME: Implement reading from the MiniUART. - return ENOTIMPL; -} - -bool MiniUART::can_write(OpenFileDescription const&, u64) const -{ - return (m_registers->line_status & TransmitterEmpty) != 0; -} - -ErrorOr MiniUART::write(Kernel::OpenFileDescription& description, u64, Kernel::UserOrKernelBuffer const& buffer, size_t size) -{ - if (!size) - return 0; - - SpinlockLocker lock(m_serial_lock); - if (!can_write(description, size)) - return EAGAIN; - - return buffer.read_buffered<128>(size, [&](ReadonlyBytes bytes) { - for (auto const& byte : bytes) - put_char(byte); - return bytes.size(); - }); -} - -void MiniUART::put_char(u8 ch) -{ - while ((m_registers->line_status & TransmitterEmpty) == 0) - Processor::wait_check(); - - if (ch == '\n' && !m_last_put_char_was_carriage_return) - m_registers->io_data = '\r'; - - m_registers->io_data = ch; - - m_last_put_char_was_carriage_return = (ch == '\r'); -} - -// The mini UAT's clock is generated from the system (VideoCore) clock. -// See section "2.2.1. Mini UART implementation details" -void MiniUART::set_baud_rate(u32 baud_rate) -{ - auto system_clock = Timer::get_clock_rate(Timer::ClockID::V3D); - m_registers->baud_rate = system_clock / (8 * baud_rate) - 1; -} - -} diff --git a/Kernel/Arch/aarch64/RPi/MiniUART.h b/Kernel/Arch/aarch64/RPi/MiniUART.h deleted file mode 100644 index b143a477e02..00000000000 --- a/Kernel/Arch/aarch64/RPi/MiniUART.h +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright (c) 2023, Daniel Bertalan - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include - -namespace Kernel::RPi { - -struct MiniUARTRegisters; - -// Makes the secondary "mini UART" (UART1) available to the userspace. -// See bcm2711-peripherals.pdf chapter "2.2. Mini UART". -class MiniUART final : public CharacterDevice { - friend class Kernel::DeviceManagement; - -public: - static ErrorOr> create(); - - virtual ~MiniUART() override; - - // ^CharacterDevice - virtual bool can_read(OpenFileDescription const&, u64) const override; - virtual ErrorOr read(OpenFileDescription&, u64, UserOrKernelBuffer&, size_t) override; - virtual bool can_write(OpenFileDescription const&, u64) const override; - virtual ErrorOr write(OpenFileDescription&, u64, UserOrKernelBuffer const&, size_t) override; - - void put_char(u8); - -private: - MiniUART(); - - // ^CharacterDevice - virtual StringView class_name() const override { return "MiniUART"sv; } - - void set_baud_rate(u32); - - bool m_last_put_char_was_carriage_return { false }; - Spinlock m_serial_lock {}; - MiniUARTRegisters volatile* m_registers; -}; -} diff --git a/Kernel/Arch/aarch64/RPi/SDHostController.cpp b/Kernel/Arch/aarch64/RPi/SDHostController.cpp deleted file mode 100644 index cba0e9db52e..00000000000 --- a/Kernel/Arch/aarch64/RPi/SDHostController.cpp +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright (c) 2023, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include - -namespace Kernel::RPi { - -Singleton s_sdhc; - -SDHostController& SDHostController::the() -{ - return *s_sdhc; -} - -SDHostController::SDHostController() - : ::SDHostController() -{ - auto& gpio = GPIO::the(); - gpio.set_pin_function(21, GPIO::PinFunction::Alternate3); // CD - gpio.set_pin_high_detect_enable(21, true); - - gpio.set_pin_function(22, GPIO::PinFunction::Alternate3); // SD1_CLK - gpio.set_pin_function(23, GPIO::PinFunction::Alternate3); // SD1_CMD - - gpio.set_pin_function(24, GPIO::PinFunction::Alternate3); // SD1_DAT0 - gpio.set_pin_function(25, GPIO::PinFunction::Alternate3); // SD1_DAT1 - gpio.set_pin_function(26, GPIO::PinFunction::Alternate3); // SD1_DAT2 - gpio.set_pin_function(27, GPIO::PinFunction::Alternate3); // SD1_DAT3 - - m_registers = MMIO::the().peripheral(0x30'0000); -} - -} diff --git a/Kernel/Arch/aarch64/RPi/SDHostController.h b/Kernel/Arch/aarch64/RPi/SDHostController.h deleted file mode 100644 index 74f5e835b0b..00000000000 --- a/Kernel/Arch/aarch64/RPi/SDHostController.h +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright (c) 2023, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include - -namespace Kernel::RPi { - -class SDHostController : public ::SDHostController { -public: - static SDHostController& the(); - SDHostController(); - virtual ~SDHostController() override = default; - -protected: - // ^SDHostController - virtual SD::HostControlRegisterMap volatile* get_register_map_base_address() override { return m_registers; } - -private: - SD::HostControlRegisterMap volatile* m_registers; -}; - -} diff --git a/Kernel/Arch/aarch64/RPi/Timer.cpp b/Kernel/Arch/aarch64/RPi/Timer.cpp deleted file mode 100644 index 8e9ff7da393..00000000000 --- a/Kernel/Arch/aarch64/RPi/Timer.cpp +++ /dev/null @@ -1,185 +0,0 @@ -/* - * Copyright (c) 2021, Nico Weber - * Copyright (c) 2022, Timon Kruiper - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include - -namespace Kernel::RPi { - -// "12.1 System Timer Registers" / "10.2 System Timer Registers" -struct TimerRegisters { - u32 control_and_status; - u32 counter_low; - u32 counter_high; - u32 compare[4]; -}; - -// Bits of the `control_and_status` register. -// See "CS register" in Broadcom doc for details. -enum FlagBits { - SystemTimerMatch0 = 1 << 0, - SystemTimerMatch1 = 1 << 1, - SystemTimerMatch2 = 1 << 2, - SystemTimerMatch3 = 1 << 3, -}; - -Timer::Timer() - : HardwareTimer(1) - , m_registers(MMIO::the().peripheral(0x3000)) -{ - // FIXME: Actually query the frequency of the timer. By default it is 100MHz. - m_frequency = 1e6; - - set_interrupt_interval_usec(m_frequency / OPTIMAL_TICKS_PER_SECOND_RATE); - enable_interrupt_mode(); -} - -Timer::~Timer() = default; - -NonnullLockRefPtr Timer::initialize() -{ - return adopt_lock_ref(*new Timer); -} - -u64 Timer::microseconds_since_boot() -{ - u32 high = m_registers->counter_high; - u32 low = m_registers->counter_low; - if (high != m_registers->counter_high) { - high = m_registers->counter_high; - low = m_registers->counter_low; - } - return (static_cast(high) << 32) | low; -} - -bool Timer::handle_irq(RegisterState const& regs) -{ - auto result = HardwareTimer::handle_irq(regs); - - set_compare(TimerID::Timer1, microseconds_since_boot() + m_interrupt_interval); - clear_interrupt(TimerID::Timer1); - - return result; -} - -u64 Timer::update_time(u64& seconds_since_boot, u32& ticks_this_second, bool query_only) -{ - // Should only be called by the time keeper interrupt handler! - u64 current_value = microseconds_since_boot(); - u64 delta_ticks = m_main_counter_drift; - if (current_value >= m_main_counter_last_read) { - delta_ticks += current_value - m_main_counter_last_read; - } else { - // the counter wrapped around - delta_ticks += (NumericLimits::max() - m_main_counter_last_read + 1) + current_value; - } - - u64 ticks_since_last_second = (u64)ticks_this_second + delta_ticks; - auto frequency = ticks_per_second(); - seconds_since_boot += ticks_since_last_second / frequency; - ticks_this_second = ticks_since_last_second % frequency; - - if (!query_only) { - m_main_counter_drift = 0; - m_main_counter_last_read = current_value; - } - - // Return the time passed (in ns) since last time update_time was called - return (delta_ticks * 1000000000ull) / frequency; -} - -void Timer::enable_interrupt_mode() -{ - set_compare(TimerID::Timer1, microseconds_since_boot() + m_interrupt_interval); - enable_irq(); -} - -void Timer::set_interrupt_interval_usec(u32 interrupt_interval) -{ - m_interrupt_interval = interrupt_interval; -} - -void Timer::clear_interrupt(TimerID id) -{ - m_registers->control_and_status = 1 << to_underlying(id); -} - -void Timer::set_compare(TimerID id, u32 compare) -{ - m_registers->compare[to_underlying(id)] = compare; -} - -class SetClockRateMboxMessage : Mailbox::Message { -public: - u32 clock_id; - u32 rate_hz; - u32 skip_setting_turbo; - - SetClockRateMboxMessage() - : Mailbox::Message(0x0003'8002, 12) - { - clock_id = 0; - rate_hz = 0; - skip_setting_turbo = 0; - } -}; - -u32 Timer::set_clock_rate(ClockID clock_id, u32 rate_hz, bool skip_setting_turbo) -{ - struct __attribute__((aligned(16))) { - Mailbox::MessageHeader header; - SetClockRateMboxMessage set_clock_rate; - Mailbox::MessageTail tail; - } message_queue; - - message_queue.set_clock_rate.clock_id = static_cast(clock_id); - message_queue.set_clock_rate.rate_hz = rate_hz; - message_queue.set_clock_rate.skip_setting_turbo = skip_setting_turbo ? 1 : 0; - - if (!Mailbox::the().send_queue(&message_queue, sizeof(message_queue))) { - dbgln("Timer::set_clock_rate() failed!"); - return 0; - } - - return message_queue.set_clock_rate.rate_hz; -} - -class GetClockRateMboxMessage : Mailbox::Message { -public: - u32 clock_id; - u32 rate_hz; - - GetClockRateMboxMessage() - : Mailbox::Message(0x0003'0002, 8) - { - clock_id = 0; - rate_hz = 0; - } -}; - -u32 Timer::get_clock_rate(ClockID clock_id) -{ - struct __attribute__((aligned(16))) { - Mailbox::MessageHeader header; - GetClockRateMboxMessage get_clock_rate; - Mailbox::MessageTail tail; - } message_queue; - - message_queue.get_clock_rate.clock_id = static_cast(clock_id); - - if (!Mailbox::the().send_queue(&message_queue, sizeof(message_queue))) { - dbgln("Timer::get_clock_rate() failed!"); - return 0; - } - - return message_queue.get_clock_rate.rate_hz; -} - -} diff --git a/Kernel/Arch/aarch64/RPi/Timer.h b/Kernel/Arch/aarch64/RPi/Timer.h deleted file mode 100644 index 99b30cb6f7e..00000000000 --- a/Kernel/Arch/aarch64/RPi/Timer.h +++ /dev/null @@ -1,89 +0,0 @@ -/* - * Copyright (c) 2021, Nico Weber - * Copyright (c) 2022, Timon Kruiper - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include - -namespace Kernel::RPi { - -struct TimerRegisters; - -class Timer final : public HardwareTimer { -public: - virtual ~Timer(); - - static NonnullLockRefPtr initialize(); - - virtual HardwareTimerType timer_type() const override { return HardwareTimerType::RPiTimer; } - virtual StringView model() const override { return "RPi Timer"sv; } - - virtual bool is_periodic() const override { TODO_AARCH64(); } - virtual bool is_periodic_capable() const override { TODO_AARCH64(); } - virtual void set_periodic() override { TODO_AARCH64(); } - virtual void set_non_periodic() override { TODO_AARCH64(); } - virtual void disable() override { TODO_AARCH64(); } - - virtual void reset_to_default_ticks_per_second() override { TODO_AARCH64(); } - virtual bool try_to_set_frequency(size_t) override { TODO_AARCH64(); } - virtual bool is_capable_of_frequency(size_t) const override { TODO_AARCH64(); } - virtual size_t calculate_nearest_possible_frequency(size_t) const override { TODO_AARCH64(); } - - // FIXME: Share code with HPET::update_time - u64 update_time(u64& seconds_since_boot, u32& ticks_this_second, bool query_only); - - u64 microseconds_since_boot(); - - void set_interrupt_interval_usec(u32); - void enable_interrupt_mode(); - - enum class ClockID { - Reserved = 0, - EMMC = 1, - UART = 2, - ARM = 3, - CORE = 4, - V3D = 5, - H264 = 6, - ISP = 7, - SDRAM = 8, - PIXEL = 9, - PWM = 10, - HEVC = 11, - EMMC2 = 12, - M2MC = 13, - PIXEL_BVB = 14, - }; - static u32 set_clock_rate(ClockID, u32 rate_hz, bool skip_setting_turbo = true); - static u32 get_clock_rate(ClockID); - -private: - Timer(); - - enum class TimerID : u32 { - Timer0 = 0, - Timer1 = 1, - Timer2 = 2, - Timer3 = 3, - }; - void set_compare(TimerID, u32 compare); - void clear_interrupt(TimerID); - - //^ IRQHandler - virtual bool handle_irq(RegisterState const&) override; - - TimerRegisters volatile* m_registers; - u32 m_interrupt_interval { 0 }; - - u64 m_main_counter_last_read { 0 }; - u64 m_main_counter_drift { 0 }; -}; - -} diff --git a/Kernel/Arch/aarch64/RPi/UART.cpp b/Kernel/Arch/aarch64/RPi/UART.cpp deleted file mode 100644 index 9d8698d9613..00000000000 --- a/Kernel/Arch/aarch64/RPi/UART.cpp +++ /dev/null @@ -1,172 +0,0 @@ -/* - * Copyright (c) 2021, Nico Weber - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include - -namespace Kernel::RPi { - -// "13.4 Register View" / "11.5 Register View" -struct UARTRegisters { - u32 data; - u32 receive_status_or_error_clear; - u32 unused[4]; - u32 flag; - u32 unused2; - - u32 unused_ilpr; - u32 integer_baud_rate_divisor; // Only the lowest 16 bits are used. - u32 fractional_baud_rate_divisor; // Only the lowest 6 bits are used. - u32 line_control; - - u32 control; - u32 interrupt_fifo_level_select; - u32 interrupt_mask_set_clear; - u32 raw_interrupt_status; - - u32 masked_interrupt_status; - u32 interrupt_clear; - u32 dma_control; - u32 test_control; -}; - -// Bits of the `flag` register. -// See "FR register" in Broadcom doc for details. -enum FlagBits { - ClearToSend = 1 << 0, - UnsupportedDSR = 1 << 1, - UnsupportedDCD = 1 << 2, - UARTBusy = 1 << 3, - ReceiveFifoEmpty = 1 << 4, - TransmitFifoFull = 1 << 5, - ReceiveFifoFull = 1 << 6, - TransmitFifoEmpty = 1 << 7, -}; - -// Bits for the `line_control` register. -// See "LCRH register" in Broadcom doc for details. -enum LineControlBits { - SendBreak = 1 << 0, - EnableParityCheckingAndGeneration = 1 << 1, - EvenParity = 1 << 2, - TransmitTwoStopBits = 1 << 3, - EnableFIFOs = 1 << 4, - - WordLength5Bits = 0b00 << 5, - WordLength6Bits = 0b01 << 5, - WordLength7Bits = 0b10 << 5, - WordLength8Bits = 0b11 << 5, - - StickParity = 1 << 7, -}; - -// Bits for the `control` register. -// See "CR register" in Broadcom doc for details. From there: -// NOTE: Program the control registers as follows: -// 1. Disable the UART. -// 2. Wait for the end of transmission or reception of the current character. -// 3. Flush the transmit FIFO by setting the FEN bit to 0 in the Line Control Register, UART_LCRH. -// 4. Reprogram the Control Register, UART_CR. -// 5. Enable the UART -enum ControlBits { - UARTEnable = 1 << 0, - UnsupportedSIREN = 1 << 1, - UnsupportedSIRLP = 1 << 2, - // Bits 3-6 are reserved. - LoopbackEnable = 1 << 7, - TransmitEnable = 1 << 8, - ReceiveEnable = 1 << 9, - UnsupportedDTR = 1 << 10, - RequestToSend = 1 << 11, - UnsupportedOut1 = 1 << 12, - UnsupportedOut2 = 1 << 13, - RTSHardwareFlowControlEnable = 1 << 14, - CTSHardwareFlowControlEnable = 1 << 15, -}; - -UART::UART() - : m_registers(MMIO::the().peripheral(0x20'1000)) -{ - // Disable UART while changing configuration. - m_registers->control = 0; - - // FIXME: Should wait for current transmission to end and should flush FIFO. - - constexpr int baud_rate = 115'200; - - // Set UART clock so that the baud rate divisor ends up as 1.0. - // FIXME: Not sure if this is a good UART clock rate. - u32 rate_in_hz = Timer::set_clock_rate(Timer::ClockID::UART, 16 * baud_rate); - - // The BCM's PL011 UART is alternate function 0 on pins 14 and 15. - auto& gpio = GPIO::the(); - gpio.set_pin_function(14, GPIO::PinFunction::Alternate0); - gpio.set_pin_function(15, GPIO::PinFunction::Alternate0); - gpio.set_pin_pull_up_down_state(Array { 14, 15 }, GPIO::PullUpDownState::Disable); - - // Clock and pins are configured. Turn UART on. - set_baud_rate(baud_rate, rate_in_hz); - m_registers->line_control = EnableFIFOs | WordLength8Bits; - - m_registers->control = UARTEnable | TransmitEnable | ReceiveEnable; -} - -UART& UART::the() -{ - static UART instance; - return instance; -} - -void UART::send(u32 c) -{ - wait_until_we_can_send(); - m_registers->data = c; -} - -void UART::print_str(char const* s, size_t length) -{ - for (size_t i = 0; i < length; ++i) { - char character = *s++; - if (character == '\n') - send('\r'); - send(character); - } -} - -u32 UART::receive() -{ - wait_until_we_can_receive(); - - // Mask out error bits. - return m_registers->data & 0xFF; -} - -void UART::set_baud_rate(int baud_rate, int uart_frequency_in_hz) -{ - // Broadcom doc: """Baud rate divisor BAUDDIV = (FUARTCLK/(16 * Baud rate))""". - // BAUDDIV is stored as a 16.6 fixed point value, so do computation scaled by (1 << 6) == 64. - // 64*(FUARTCLK/(16 * Baud rate)) == 4*FUARTCLK/(Baud rate). For rounding, add 0.5 == (Baud rate/2)/(Baud rate). - u32 baud_rate_divisor_fixed_point = (4 * uart_frequency_in_hz + baud_rate / 2) / baud_rate; - - m_registers->integer_baud_rate_divisor = baud_rate_divisor_fixed_point / 64; - m_registers->fractional_baud_rate_divisor = baud_rate_divisor_fixed_point % 64; -} - -void UART::wait_until_we_can_send() -{ - while (m_registers->flag & TransmitFifoFull) - Processor::wait_check(); -} - -void UART::wait_until_we_can_receive() -{ - while (m_registers->flag & ReceiveFifoEmpty) - Processor::wait_check(); -} - -} diff --git a/Kernel/Arch/aarch64/RPi/UART.h b/Kernel/Arch/aarch64/RPi/UART.h deleted file mode 100644 index d20137f1a4f..00000000000 --- a/Kernel/Arch/aarch64/RPi/UART.h +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright (c) 2021, Nico Weber - * Copyright (c) 2021, Jesse Buhagiar - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include - -namespace Kernel::RPi { - -struct UARTRegisters; - -// Abstracts the PL011 UART on a Raspberry Pi. -// (The BCM2711 on a Raspberry Pi 4 has five PL011 UARTs; this is always the first of those.) -class UART { -public: - static UART& the(); - - void send(u32 c); - u32 receive(); - - void print_str(char const*, size_t); - -private: - UART(); - - void set_baud_rate(int baud_rate, int uart_frequency_in_hz); - void wait_until_we_can_send(); - void wait_until_we_can_receive(); - - UARTRegisters volatile* m_registers; -}; - -} diff --git a/Kernel/Arch/aarch64/RPi/Watchdog.cpp b/Kernel/Arch/aarch64/RPi/Watchdog.cpp deleted file mode 100644 index 50555543f92..00000000000 --- a/Kernel/Arch/aarch64/RPi/Watchdog.cpp +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright (c) 2023, Daniel Bertalan - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include - -namespace Kernel::RPi { -struct WatchdogRegisters { - u32 rstc; - u32 rsts; - u32 wdog; -}; - -constexpr u32 PASSWORD = 0x5a000000; -constexpr u32 RSTS_PARTITION_MASK = 0xfffffaaa; -constexpr u32 RSTS_PARTITION_SHUTDOWN = 0x00000555; -constexpr u32 RSTC_WRCFG_MASK = 0xffffffcf; -constexpr u32 RSTC_WRCFG_FULL_RESET = 0x00000020; - -Watchdog::Watchdog() - : m_registers(MMIO::the().peripheral(0x10'001c)) -{ -} - -Watchdog& Watchdog::the() -{ - static Watchdog watchdog; - return watchdog; -} - -// This is the same mechanism used by Linux, the ARM Trusted Firmware and U-Boot to trigger a system shutdown. -// See e.g. https://github.com/ARM-software/arm-trusted-firmware/blob/dcf430656ca8ef964fa55ad9eb81cf838c7837f2/plat/rpi/common/rpi3_pm.c#L231-L249 -void Watchdog::system_shutdown() -{ - // The Raspberry Pi hardware doesn't support powering off. Setting the reboot target partition to this - // special value will cause the firmware to halt the CPU and put it in a low power state when the watchdog - // timer expires. When running under Qemu, this will cause the emulator to exit. - m_registers->rsts = PASSWORD | (m_registers->rsts & RSTS_PARTITION_MASK) | RSTS_PARTITION_SHUTDOWN; - // Set the timeout to 10 ticks (~150us). - m_registers->wdog = PASSWORD | 10; - // Start the watchdog. - m_registers->rstc = PASSWORD | (m_registers->rstc & RSTC_WRCFG_MASK) | RSTC_WRCFG_FULL_RESET; -} -} diff --git a/Kernel/Arch/aarch64/RPi/Watchdog.h b/Kernel/Arch/aarch64/RPi/Watchdog.h deleted file mode 100644 index 7901fd1ef5d..00000000000 --- a/Kernel/Arch/aarch64/RPi/Watchdog.h +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright (c) 2023, Daniel Bertalan - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -namespace Kernel::RPi { - -struct WatchdogRegisters; - -class Watchdog { -public: - static Watchdog& the(); - - void system_shutdown(); - -private: - Watchdog(); - - WatchdogRegisters volatile* m_registers; -}; -} diff --git a/Kernel/Arch/aarch64/RegisterState.h b/Kernel/Arch/aarch64/RegisterState.h deleted file mode 100644 index b794ef8b74b..00000000000 --- a/Kernel/Arch/aarch64/RegisterState.h +++ /dev/null @@ -1,79 +0,0 @@ -/* - * Copyright (c) 2018-2021, James Mintram - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include - -#include - -#include -#include - -VALIDATE_IS_AARCH64() - -namespace Kernel { - -struct alignas(16) RegisterState { - u64 x[31]; // Saved general purpose registers - u64 spsr_el1; // Save Processor Status Register, EL1 - u64 elr_el1; // Exception Link Register, EL1 - u64 sp_el0; // EL0 stack pointer - u64 tpidr_el0; // EL0 Software Thread ID Register - - FlatPtr userspace_sp() const { return sp_el0; } - void set_userspace_sp(FlatPtr value) - { - sp_el0 = value; - } - FlatPtr ip() const { return elr_el1; } - void set_ip(FlatPtr value) - { - elr_el1 = value; - } - FlatPtr bp() const { return x[29]; } - - ExecutionMode previous_mode() const - { - return ((spsr_el1 & 0b1111) == 0) ? ExecutionMode::User : ExecutionMode::Kernel; - } - - void set_return_reg(FlatPtr value) { x[0] = value; } - void capture_syscall_params(FlatPtr& function, FlatPtr& arg1, FlatPtr& arg2, FlatPtr& arg3, FlatPtr& arg4) const - { - function = x[8]; - arg1 = x[1]; - arg2 = x[2]; - arg3 = x[3]; - arg4 = x[4]; - } -}; - -#define REGISTER_STATE_SIZE (36 * 8) -static_assert(AssertSize()); - -inline void copy_kernel_registers_into_ptrace_registers(PtraceRegisters& ptrace_regs, RegisterState const& kernel_regs) -{ - for (auto i = 0; i < 31; i++) - ptrace_regs.x[i] = kernel_regs.x[i]; - - ptrace_regs.sp = kernel_regs.userspace_sp(); - ptrace_regs.pc = kernel_regs.ip(); -} - -inline void copy_ptrace_registers_into_kernel_registers(RegisterState& kernel_regs, PtraceRegisters const& ptrace_regs) -{ - for (auto i = 0; i < 31; i++) - kernel_regs.x[i] = ptrace_regs.x[i]; - - kernel_regs.set_userspace_sp(ptrace_regs.sp); - kernel_regs.set_ip(ptrace_regs.pc); -} - -struct DebugRegisterState { -}; - -} diff --git a/Kernel/Arch/aarch64/Registers.h b/Kernel/Arch/aarch64/Registers.h deleted file mode 100644 index e8b3d985ee9..00000000000 --- a/Kernel/Arch/aarch64/Registers.h +++ /dev/null @@ -1,1374 +0,0 @@ -/* - * Copyright (c) 2021, James Mintram - * Copyright (c) 2021, Marcin Undak - * Copyright (c) 2022, Konrad - * Copyright (c) 2022, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include - -namespace Kernel::Aarch64 { - -// https://developer.arm.com/documentation/ddi0601/2022-09/AArch64-Registers/ID-AA64ISAR0-EL1--AArch64-Instruction-Set-Attribute-Register-0?lang=en -// ID_AA64ISAR0_EL1, AArch64 Instruction Set Attribute Register 0 -struct alignas(u64) ID_AA64ISAR0_EL1 { - u64 : 4; - u64 AES : 4; - u64 SHA1 : 4; - u64 SHA2 : 4; - u64 CRC32 : 4; - u64 Atomic : 4; - u64 TME : 4; - u64 RDM : 4; - u64 SHA3 : 4; - u64 SM3 : 4; - u64 SM4 : 4; - u64 DP : 4; - u64 FHM : 4; - u64 TS : 4; - u64 TLB : 4; - u64 RNDR : 4; - - static inline ID_AA64ISAR0_EL1 read() - { - ID_AA64ISAR0_EL1 feature_register; - - asm volatile("mrs %[value], ID_AA64ISAR0_EL1" - : [value] "=r"(feature_register)); - - return feature_register; - } -}; -static_assert(sizeof(ID_AA64ISAR0_EL1) == 8); - -// https://developer.arm.com/documentation/ddi0601/2022-12/AArch64-Registers/ID-AA64ISAR1-EL1--AArch64-Instruction-Set-Attribute-Register-1 -// ID_AA64ISAR1_EL1, AArch64 Instruction Set Attribute Register 1 -struct alignas(u64) ID_AA64ISAR1_EL1 { - u64 DPB : 4; - u64 APA : 4; - u64 API : 4; - u64 JSCVT : 4; - u64 FCMA : 4; - u64 LRCPC : 4; - u64 GPA : 4; - u64 GPI : 4; - u64 FRINTTS : 4; - u64 SB : 4; - u64 SPECRES : 4; - u64 BF16 : 4; - u64 DGH : 4; - u64 I8MM : 4; - u64 XS : 4; - u64 LS64 : 4; - - static inline ID_AA64ISAR1_EL1 read() - { - ID_AA64ISAR1_EL1 feature_register; - - asm volatile("mrs %[value], ID_AA64ISAR1_EL1" - : [value] "=r"(feature_register)); - - return feature_register; - } -}; -static_assert(sizeof(ID_AA64ISAR1_EL1) == 8); - -// https://developer.arm.com/documentation/ddi0601/2022-12/AArch64-Registers/ID-AA64ISAR2-EL1--AArch64-Instruction-Set-Attribute-Register-2 -// ID_AA64ISAR2_EL1, AArch64 Instruction Set Attribute Register 2 -struct alignas(u64) ID_AA64ISAR2_EL1 { - u64 WFxT : 4; - u64 RPRES : 4; - u64 GPA3 : 4; - u64 APA3 : 4; - u64 MOPS : 4; - u64 BC : 4; - u64 PAC_frac : 4; - u64 CLRBHB : 4; - u64 SYSREG_128 : 4; - u64 SYSINSTR_128 : 4; - u64 PRFMSLC : 4; - u64 : 4; - u64 RPRFM : 4; - u64 CSSC : 4; - u64 : 8; - - static inline ID_AA64ISAR2_EL1 read() - { - ID_AA64ISAR2_EL1 feature_register; - - asm volatile("mrs %[value], ID_AA64ISAR2_EL1" - : [value] "=r"(feature_register)); - - return feature_register; - } -}; -static_assert(sizeof(ID_AA64ISAR2_EL1) == 8); - -// https://developer.arm.com/documentation/ddi0601/2022-12/AArch64-Registers/ID-AA64PFR0-EL1--AArch64-Processor-Feature-Register-0 -// ID_AA64PFR0_EL1, AArch64 Processor Feature Register 0 -struct alignas(u64) ID_AA64PFR0_EL1 { - u64 EL0 : 4; - u64 EL1 : 4; - u64 EL2 : 4; - u64 EL3 : 4; - u64 FP : 4; - u64 AdvSIMD : 4; - u64 GIC : 4; - u64 RAS : 4; - u64 SVE : 4; - u64 SEL2 : 4; - u64 MPAM : 4; - u64 AMU : 4; - u64 DIT : 4; - u64 RME : 4; - u64 CSV2 : 4; - u64 CSV3 : 4; - - static inline ID_AA64PFR0_EL1 read() - { - ID_AA64PFR0_EL1 feature_register; - - asm volatile("mrs %[value], ID_AA64PFR0_EL1" - : [value] "=r"(feature_register)); - - return feature_register; - } -}; -static_assert(sizeof(ID_AA64PFR0_EL1) == 8); - -// https://developer.arm.com/documentation/ddi0601/2022-12/AArch64-Registers/ID-AA64PFR1-EL1--AArch64-Processor-Feature-Register-1 -// ID_AA64PFR1_EL1, AArch64 Processor Feature Register 1 -struct alignas(u64) ID_AA64PFR1_EL1 { - u64 BT : 4; - u64 SSBS : 4; - u64 MTE : 4; - u64 RAS_frac : 4; - u64 MPAM_frac : 4; - u64 : 4; - u64 SME : 4; - u64 RNDR_trap : 4; - u64 CSV2_frac : 4; - u64 NMI : 4; - u64 MTE_frac : 4; - u64 GCS : 4; - u64 THE : 4; - u64 MTEX : 4; - u64 DF2 : 4; - u64 PFAR : 4; - - static inline ID_AA64PFR1_EL1 read() - { - ID_AA64PFR1_EL1 feature_register; - - asm volatile("mrs %[value], ID_AA64PFR1_EL1" - : [value] "=r"(feature_register)); - - return feature_register; - } -}; -static_assert(sizeof(ID_AA64PFR1_EL1) == 8); - -// https://developer.arm.com/documentation/ddi0601/2022-12/AArch64-Registers/ID-AA64PFR2-EL1--AArch64-Processor-Feature-Register-2 -// ID_AA64PFR2_EL1, AArch64 Processor Feature Register 2 -struct alignas(u64) ID_AA64PFR2_EL1 { - u64 MTEPERM : 4; - u64 MTESTOREONLY : 4; - u64 MTEFAR : 4; - u64 : 20; - u64 : 32; - - static inline ID_AA64PFR2_EL1 read() - { - ID_AA64PFR2_EL1 feature_register; - - asm volatile("mrs %[value], s3_0_c0_c4_2" // encoded ID_AA64PFR2_EL1 register - : [value] "=r"(feature_register)); - - return feature_register; - } -}; -static_assert(sizeof(ID_AA64PFR2_EL1) == 8); - -// https://developer.arm.com/documentation/ddi0595/2021-12/AArch64-Registers/MPIDR-EL1--Multiprocessor-Affinity-Register?lang=en -// MPIDR_EL1, Multiprocessor Affinity Register -struct alignas(u64) MPIDR_EL1 { - u64 Aff0 : 8; - u64 Aff1 : 8; - u64 Aff2 : 8; - u64 MT : 1; - u64 : 5; - u64 U : 1; - u64 : 1; - u64 Aff3 : 8; - u64 : 24; - - static inline MPIDR_EL1 read() - { - MPIDR_EL1 affinity_register; - - asm volatile("mrs %[value], MPIDR_EL1" - : [value] "=r"(affinity_register)); - - return affinity_register; - } -}; -static_assert(sizeof(MPIDR_EL1) == 8); - -// https://developer.arm.com/documentation/ddi0601/2022-12/AArch64-Registers/ID-AA64MMFR0-EL1--AArch64-Memory-Model-Feature-Register-0 -// ID_AA64MMFR0_EL1, AArch64 Memory Model Feature Register 0 -struct alignas(u64) ID_AA64MMFR0_EL1 { - u64 PARange : 4; - u64 ASIDBits : 4; - u64 BigEnd : 4; - u64 SNSMem : 4; - u64 BigEndEL0 : 4; - u64 TGran16 : 4; - u64 TGran64 : 4; - u64 TGran4 : 4; - u64 TGran16_2 : 4; - u64 TGran64_2 : 4; - u64 TGran4_2 : 4; - u64 ExS : 4; - u64 : 8; - u64 FGT : 4; - u64 ECV : 4; - - static inline ID_AA64MMFR0_EL1 read() - { - ID_AA64MMFR0_EL1 feature_register; - - asm volatile("mrs %[value], ID_AA64MMFR0_EL1" - : [value] "=r"(feature_register)); - - return feature_register; - } -}; -static_assert(sizeof(ID_AA64MMFR0_EL1) == 8); - -// https://developer.arm.com/documentation/ddi0601/2022-12/AArch64-Registers/ID-AA64MMFR1-EL1--AArch64-Memory-Model-Feature-Register-1 -// ID_AA64MMFR1_EL1, AArch64 Memory Model Feature Register 1 -struct alignas(u64) ID_AA64MMFR1_EL1 { - u64 HAFDBS : 4; - u64 VMIDBits : 4; - u64 VH : 4; - u64 HPDS : 4; - u64 LO : 4; - u64 PAN : 4; - u64 SpecSEI : 4; - u64 XNX : 4; - u64 TWED : 4; - u64 ETS : 4; - u64 HCX : 4; - u64 AFP : 4; - u64 nTLBPA : 4; - u64 TIDCP1 : 4; - u64 CMOW : 4; - u64 ECBHB : 4; - - static inline ID_AA64MMFR1_EL1 read() - { - ID_AA64MMFR1_EL1 feature_register; - - asm volatile("mrs %[value], ID_AA64MMFR1_EL1" - : [value] "=r"(feature_register)); - - return feature_register; - } -}; -static_assert(sizeof(ID_AA64MMFR1_EL1) == 8); - -// https://developer.arm.com/documentation/ddi0601/2022-12/AArch64-Registers/ID-AA64MMFR2-EL1--AArch64-Memory-Model-Feature-Register-2 -// ID_AA64MMFR2_EL1, AArch64 Memory Model Feature Register 2 -struct alignas(u64) ID_AA64MMFR2_EL1 { - u64 CnP : 4; - u64 UAO : 4; - u64 LSM : 4; - u64 IESB : 4; - u64 VARange : 4; - u64 CCIDX : 4; - u64 NV : 4; - u64 ST : 4; - u64 AT : 4; - u64 IDS : 4; - u64 FWB : 4; - u64 : 4; - u64 TTL : 4; - u64 BBM : 4; - u64 EVT : 4; - u64 E0PD : 4; - - static inline ID_AA64MMFR2_EL1 read() - { - ID_AA64MMFR2_EL1 feature_register; - - asm volatile("mrs %[value], ID_AA64MMFR2_EL1" - : [value] "=r"(feature_register)); - - return feature_register; - } -}; -static_assert(sizeof(ID_AA64MMFR2_EL1) == 8); - -// https://developer.arm.com/documentation/ddi0601/2022-12/AArch64-Registers/ID-AA64MMFR3-EL1--AArch64-Memory-Model-Feature-Register-3 -// ID_AA64MMFR3_EL1, AArch64 Memory Model Feature Register 3 -struct alignas(u64) ID_AA64MMFR3_EL1 { - u64 TCRX : 4; - u64 SCTLRX : 4; - u64 S1PIE : 4; - u64 S2PIE : 4; - u64 S1POE : 4; - u64 S2POE : 4; - u64 AIE : 4; - u64 MEC : 4; - u64 D128 : 4; - u64 D128_2 : 4; - u64 SNERR : 4; - u64 ANERR : 4; - u64 : 4; - u64 SDERR : 4; - u64 ADERR : 4; - u64 Spec_FPACC : 4; - - static inline ID_AA64MMFR3_EL1 read() - { - ID_AA64MMFR3_EL1 feature_register; - - asm volatile("mrs %[value], s3_0_c0_c7_3" // encoded ID_AA64MMFR3_EL1 register - : [value] "=r"(feature_register)); - - return feature_register; - } -}; -static_assert(sizeof(ID_AA64MMFR3_EL1) == 8); - -// https://developer.arm.com/documentation/ddi0601/2022-12/AArch64-Registers/ID-AA64MMFR4-EL1--AArch64-Memory-Model-Feature-Register-4 -// ID_AA64MMFR4_EL1, AArch64 Memory Model Feature Register 4 -struct alignas(u64) ID_AA64MMFR4_EL1 { - u64 : 4; - u64 EIESB : 4; - u64 : 24; - u64 : 32; - - static inline ID_AA64MMFR4_EL1 read() - { - ID_AA64MMFR4_EL1 feature_register; - - asm volatile("mrs %[value], s3_0_c0_c7_4" // encoded ID_AA64MMFR4_EL1 register - : [value] "=r"(feature_register)); - - return feature_register; - } -}; -static_assert(sizeof(ID_AA64MMFR4_EL1) == 8); - -// https://developer.arm.com/documentation/ddi0601/2022-12/AArch64-Registers/ID-AA64SMFR0-EL1--SME-Feature-ID-register-0 -// ID_AA64SMFR0_EL1, AArch64 SME Feature ID register 0 -struct alignas(u64) ID_AA64SMFR0_EL1 { - u64 : 32; - u64 F32F32 : 1; - u64 BI32I32 : 1; - u64 B16F32 : 1; - u64 F16F32 : 1; - u64 I8I32 : 4; - u64 : 2; - u64 F16F16 : 1; - u64 B16B16 : 1; - u64 I16I32 : 4; - u64 F64F64 : 1; - u64 : 3; - u64 I16I64 : 4; - u64 SMEver : 4; - u64 : 3; - u64 FA64 : 1; - - static inline ID_AA64SMFR0_EL1 read() - { - ID_AA64SMFR0_EL1 feature_register; - - asm volatile("mrs %[value], s3_0_c0_c4_5" // encoded ID_AA64SMFR0_EL1 register - : [value] "=r"(feature_register)); - - return feature_register; - } -}; -static_assert(sizeof(ID_AA64SMFR0_EL1) == 8); - -// https://developer.arm.com/documentation/ddi0601/2022-12/AArch64-Registers/ID-AA64ZFR0-EL1--SVE-Feature-ID-register-0 -// ID_AA64ZFR0_EL1, AArch64 SVE Feature ID register 0 -struct alignas(u64) ID_AA64ZFR0_EL1 { - u64 SVEver : 4; - u64 AES : 4; - u64 : 8; - u64 BitPerm : 4; - u64 BF16 : 4; - u64 B16B16 : 4; - u64 : 4; - u64 SHA3 : 4; - u64 : 4; - u64 SM4 : 4; - u64 I8MM : 4; - u64 : 4; - u64 F32MM : 4; - u64 F64MM : 4; - u64 : 4; - - static inline ID_AA64ZFR0_EL1 read() - { - ID_AA64ZFR0_EL1 feature_register; - - asm volatile("mrs %[value], s3_0_c0_c4_4" // encoded ID_AA64ZFR0_EL1 register - : [value] "=r"(feature_register)); - - return feature_register; - } -}; -static_assert(sizeof(ID_AA64ZFR0_EL1) == 8); - -// https://developer.arm.com/documentation/ddi0601/2022-12/AArch64-Registers/ID-AA64DFR0-EL1--AArch64-Debug-Feature-Register-0 -// ID_AA64DFR0_EL1, AArch64 Debug Feature Register 0 -struct alignas(u64) ID_AA64DFR0_EL1 { - u64 DebugVer : 4; - u64 TraceVer : 4; - u64 PMUVer : 4; - u64 BRPs : 4; - u64 PMSS : 4; - u64 WRPs : 4; - u64 SEBEP : 4; - u64 CTX_CMPs : 4; - u64 PMSVer : 4; - u64 DoubleLock : 4; - u64 TraceFilt : 4; - u64 TraceBuffer : 4; - u64 MTPMU : 4; - u64 BRBE : 4; - u64 ExtTrcBuff : 4; - u64 HPMN0 : 4; - - static inline ID_AA64DFR0_EL1 read() - { - ID_AA64DFR0_EL1 feature_register; - - asm volatile("mrs %[value], ID_AA64DFR0_EL1" - : [value] "=r"(feature_register)); - - return feature_register; - } -}; -static_assert(sizeof(ID_AA64DFR0_EL1) == 8); - -// https://developer.arm.com/documentation/ddi0601/2022-12/AArch64-Registers/ID-AA64DFR1-EL1--AArch64-Debug-Feature-Register-1 -// ID_AA64DFR1_EL1, AArch64 Debug Feature Register 1 -struct alignas(u64) ID_AA64DFR1_EL1 { - u64 SYSPMUID : 8; - u64 BRPs : 8; - u64 WRPs : 8; - u64 CTX_CMPs : 8; - u64 SPMU : 4; - u64 PMICNTR : 4; - u64 ABLE : 4; - u64 ITE : 4; - u64 EBEP : 4; - u64 : 4; - u64 ABL_CMPs : 8; - - static inline ID_AA64DFR1_EL1 read() - { - ID_AA64DFR1_EL1 feature_register; - - asm volatile("mrs %[value], ID_AA64DFR1_EL1" - : [value] "=r"(feature_register)); - - return feature_register; - } -}; -static_assert(sizeof(ID_AA64DFR1_EL1) == 8); - -// https://developer.arm.com/documentation/ddi0595/2020-12/AArch64-Registers/CNTFRQ-EL0--Counter-timer-Frequency-register -// CNTFRQ_EL0, Counter-timer Frequency register -struct alignas(u64) CNTFRQ_EL0 { - u64 : 32; - u64 ClockFrequency : 32; - - static inline CNTFRQ_EL0 read() - { - CNTFRQ_EL0 frequency; - - asm volatile("mrs %[value], CNTFRQ_EL0" - : [value] "=r"(frequency)); - - return frequency; - } -}; -static_assert(sizeof(CNTFRQ_EL0) == 8); - -// https://developer.arm.com/documentation/ddi0595/2021-06/AArch64-Registers/TCR-EL1--Translation-Control-Register--EL1- -// Translation Control Register -struct alignas(u64) TCR_EL1 { - - enum Shareability { - NonSharable = 0b00, - OuterShareable = 0b10, - InnerShareable = 0b11, - }; - enum OuterCacheability { - NormalMemory_Outer_NonCacheable = 0b00, - NormalMemory_Outer_WriteBack_ReadAllocate_WriteAllocateCacheable = 0b01, - NormalMemory_Outer_WriteThrough_ReadAllocate_NoWriteAllocateCacheable = 0b10, - NormalMemory_Outer_WriteBack_ReadAllocate_NoWriteAllocateCacheable = 0b11, - }; - enum InnerCacheability { - NormalMemory_Inner_NonCacheable = 0b00, - NormalMemory_Inner_WriteBack_ReadAllocate_WriteAllocateCacheable = 0b01, - NormalMemory_Inner_WriteThrough_ReadAllocate_NoWriteAllocateCacheable = 0b10, - NormalMemory_Inner_WriteBack_ReadAllocate_NoWriteAllocateCacheable = 0b11, - }; - - // In AArch64, you have 3 possible translation granules to choose from, - // each of which results in a different set of page sizes: - // - 4KB granule: 4KB, 2MB, and 1GB pages. - // - 16KB granule: 16KB and 32MB pages. - // - 64KB granule: 64KB and 512MB pages. - // - // (https://stackoverflow.com/a/34269498) - - enum class TG1GranuleSize : u8 { - Size_16KB = 0b01, - Size_4KB = 0b10, - Size_64KB = 0b11, - }; - - enum class TG0GranuleSize : u8 { - Size_4KB = 0b00, - Size_64KB = 0b01, - Size_16KB = 0b10, - }; - - u64 T0SZ : 6; - u64 RES0_0 : 1; - u64 EPD0 : 1; - InnerCacheability IRGN0 : 2; - OuterCacheability ORGN0 : 2; - Shareability SH0 : 2; - TG0GranuleSize TG0 : 2; - - u64 T1SZ : 6; - u64 A1 : 1; - u64 EPD1 : 1; - InnerCacheability IRGN1 : 2; - OuterCacheability ORGN1 : 2; - Shareability SH1 : 2; - TG1GranuleSize TG1 : 2; - - u64 IPS : 3; - u64 RES0_1 : 1; - u64 AS : 1; - u64 TBI0 : 1; - u64 TBI1 : 1; - u64 HA : 1; - u64 HD : 1; - u64 HPD0 : 1; - u64 HPD1 : 1; - u64 HWU059 : 1; - u64 HWU060 : 1; - u64 HWU061 : 1; - u64 HWU062 : 1; - - u64 HWU159 : 1; - u64 HWU160 : 1; - u64 HWU161 : 1; - u64 HWU162 : 1; - - u64 TBID0 : 1; - u64 TBID1 : 1; - u64 NFD0 : 1; - u64 NFD1 : 1; - - u64 E0PD0 : 1; - u64 E0PD1 : 1; - u64 TCMA0 : 1; - u64 TCMA1 : 1; - u64 DS : 1; - u64 RES0_2 : 4; - - static inline void write(TCR_EL1 tcr_el1) - { - asm volatile("msr tcr_el1, %[value]" ::[value] "r"(tcr_el1)); - } - - static inline TCR_EL1 read() - { - TCR_EL1 tcr_el1; - - asm volatile("mrs %[value], tcr_el1" - : [value] "=r"(tcr_el1)); - - return tcr_el1; - } - - static constexpr TCR_EL1 reset_value() - { - return {}; - } -}; -static_assert(sizeof(TCR_EL1) == 8); - -// https://developer.arm.com/documentation/ddi0595/2021-03/AArch64-Registers/SCTLR-EL1--System-Control-Register--EL1- -// System Control Register -struct alignas(u64) SCTLR_EL1 { - u64 M : 1; - u64 A : 1; - u64 C : 1; - u64 SA : 1; - u64 SA0 : 1; - u64 CP15BEN : 1; - u64 nAA : 1; - u64 ITD : 1; - u64 SED : 1; - u64 UMA : 1; - u64 EnRCTX : 1; - u64 EOS : 1; - u64 I : 1; - u64 EnDB : 1; - u64 DZE : 1; - u64 UCT : 1; - u64 nTWI : 1; - u64 _reserved17 : 1 = 0; - u64 nTWE : 1; - u64 WXN : 1; - u64 TSCXT : 1; - u64 IESB : 1; - u64 EIS : 1; - u64 SPAN : 1; - u64 E0E : 1; - u64 EE : 1; - u64 UCI : 1; - u64 EnDA : 1; - u64 nTLSMD : 1; - u64 LSMAOE : 1; - u64 EnIB : 1; - u64 EnIA : 1; - u64 _reserved32 : 3 = 0; - u64 BT0 : 1; - u64 BT1 : 1; - u64 ITFSB : 1; - u64 TCF0 : 2; - u64 TCF : 2; - u64 ATA0 : 1; - u64 ATA : 1; - u64 DSSBS : 1; - u64 TWEDEn : 1; - u64 TWEDEL : 4; - u64 _reserved50 : 4 = 0; - u64 EnASR : 1; - u64 EnAS0 : 1; - u64 EnALS : 1; - u64 EPAN : 1; - u64 _reserved58 : 6 = 0; - - static inline void write(SCTLR_EL1 sctlr_el1) - { - asm volatile("msr sctlr_el1, %[value]" ::[value] "r"(sctlr_el1)); - } - - static inline SCTLR_EL1 read() - { - SCTLR_EL1 sctlr; - - asm volatile("mrs %[value], sctlr_el1" - : [value] "=r"(sctlr)); - - return sctlr; - } - - static constexpr SCTLR_EL1 reset_value() - { - SCTLR_EL1 system_control_register_el1 = {}; - system_control_register_el1.SA = 1; - system_control_register_el1.SA0 = 1; - system_control_register_el1.ITD = 1; - system_control_register_el1.SED = 1; - system_control_register_el1.EOS = 1; - system_control_register_el1.TSCXT = 1; - system_control_register_el1.IESB = 1; - system_control_register_el1.EIS = 1; - system_control_register_el1.SPAN = 1; - system_control_register_el1.LSMAOE = 1; - system_control_register_el1.nTLSMD = 1; - return system_control_register_el1; - } -}; -static_assert(sizeof(SCTLR_EL1) == 8); - -// https://developer.arm.com/documentation/ddi0601/2022-09/AArch64-Registers/MIDR-EL1--Main-ID-Register?lang=en -// MIDR_EL1, Main ID Register -struct alignas(u64) MIDR_EL1 { - u8 Revision : 4; - u16 PartNum : 12; - u8 Architecture : 4; - u8 Variant : 4; - u8 Implementer : 8; - u64 : 32; - - static inline MIDR_EL1 read() - { - MIDR_EL1 main_id_register; - - asm volatile("mrs %[value], MIDR_EL1" - : [value] "=r"(main_id_register)); - - return main_id_register; - } -}; -static_assert(sizeof(MIDR_EL1) == 8); - -// https://developer.arm.com/documentation/ddi0601/2022-09/AArch64-Registers/AIDR-EL1--Auxiliary-ID-Register?lang=en -// AIDR_EL1, Auxiliary ID Register -struct alignas(u64) AIDR_EL1 { - u64 AIDR : 64; - - static inline AIDR_EL1 read() - { - AIDR_EL1 auxiliary_id_register; - - asm volatile("mrs %[value], AIDR_EL1" - : [value] "=r"(auxiliary_id_register)); - - return auxiliary_id_register; - } -}; -static_assert(sizeof(AIDR_EL1) == 8); - -// https://developer.arm.com/documentation/ddi0595/2021-06/AArch64-Registers/HCR-EL2--Hypervisor-Configuration-Register -// Hypervisor Configuration Register -struct alignas(u64) HCR_EL2 { - u64 VM : 1; - u64 SWIO : 1; - u64 PTW : 1; - u64 FMO : 1; - u64 IMO : 1; - u64 AMO : 1; - u64 VF : 1; - u64 VI : 1; - u64 VSE : 1; - u64 FB : 1; - u64 BSU : 2; - u64 DC : 1; - u64 TWI : 1; - u64 TWE : 1; - u64 TID0 : 1; - u64 TID1 : 1; - u64 TID2 : 1; - u64 TID3 : 1; - u64 TSC : 1; - u64 TIPDCP : 1; - u64 TACR : 1; - u64 TSW : 1; - u64 TPCF : 1; - u64 TPU : 1; - u64 TTLB : 1; - u64 TVM : 1; - u64 TGE : 1; - u64 TDZ : 1; - u64 HCD : 1; - u64 TRVM : 1; - u64 RW : 1; - u64 CD : 1; - u64 ID : 1; - u64 E2H : 1; - u64 TLOR : 1; - u64 TERR : 1; - u64 MIOCNCE : 1; - u64 _reserved39 : 1 = 0; - u64 APK : 1 = 0; - u64 API : 1 = 0; - u64 NV : 1 = 0; - u64 NV1 : 1 = 0; - u64 AT : 1 = 0; - u64 _reserved45 : 18 = 0; - - static inline void write(HCR_EL2 hcr_el2) - { - asm volatile("msr hcr_el2, %[value]" ::[value] "r"(hcr_el2)); - } - - static inline HCR_EL2 read() - { - HCR_EL2 spsr; - - asm volatile("mrs %[value], hcr_el2" - : [value] "=r"(spsr)); - - return spsr; - } -}; -static_assert(sizeof(HCR_EL2) == 8); - -// https://developer.arm.com/documentation/ddi0595/2021-06/AArch64-Registers/SCR-EL3--Secure-Configuration-Register -// Secure Configuration Register -struct alignas(u64) SCR_EL3 { - u64 NS : 1; - u64 IRQ : 1; - u64 FIQ : 1; - u64 EA : 1; - u64 _reserved4 : 1 = 1; - u64 _reserved5 : 1 = 1; - u64 _reserved6 : 1 = 0; - u64 SMD : 1; - u64 HCE : 1; - u64 SIF : 1; - u64 RW : 1; - u64 ST : 1; - u64 TWI : 1; - u64 TWE : 1; - u64 TLOR : 1; - u64 TERR : 1; - u64 APK : 1; - u64 API : 1; - u64 EEL2 : 1; - u64 EASE : 1; - u64 NMEA : 1; - u64 FIEN : 1; - u64 _reserved22 : 3 = 0; - u64 EnSCXT : 1; - u64 ATA : 1; - u64 FGTEn : 1; - u64 ECVEn : 1; - u64 TWEDEn : 1; - u64 TWEDEL : 4; - u64 _reserved34 : 1 = 0; - u64 AMVOFFEN : 1; - u64 EnAS0 : 1; - u64 ADEn : 1; - u64 HXEn : 1; - u64 _reserved39 : 14 = 0; - - static inline void write(SCR_EL3 scr_el3) - { - asm volatile("msr scr_el3, %[value]" ::[value] "r"(scr_el3)); - } - - static inline SCR_EL3 read() - { - SCR_EL3 scr; - - asm volatile("mrs %[value], scr_el3" - : [value] "=r"(scr)); - - return scr; - } -}; -static_assert(sizeof(SCR_EL3) == 8); - -// C5.2.18 SPSR_EL1, Saved Program Status Register (EL1) -struct alignas(u64) SPSR_EL1 { - enum Mode : u8 { - EL0t = 0b0000, - EL1t = 0b0100, - EL1h = 0b0101 - }; - - Mode M : 4; - u64 M_4 : 1 = 0; - u64 _reserved5 : 1 = 0; - u64 F : 1; - u64 I : 1; - u64 A : 1; - u64 D : 1; - u64 BTYPE : 2; - u64 SSBS : 1; - u64 _reserved13 : 7 = 0; - u64 IL : 1; - u64 SS : 1; - u64 PAN : 1; - u64 UA0 : 1; - u64 DIT : 1; - u64 TCO : 1; - u64 _reserved26 : 2 = 0; - u64 V : 1; - u64 C : 1; - u64 Z : 1; - u64 N : 1; - u64 _reserved32 : 32 = 0; - - static inline void write(SPSR_EL1 spsr_el1) - { - asm volatile("msr spsr_el1, %[value]" ::[value] "r"(spsr_el1)); - } - - static inline SPSR_EL1 read() - { - SPSR_EL1 spsr; - - asm volatile("mrs %[value], spsr_el1" - : [value] "=r"(spsr)); - - return spsr; - } -}; -static_assert(sizeof(SPSR_EL1) == 8); - -struct alignas(u64) SPSR_EL2 { - enum Mode : u16 { - EL0t = 0b0000, - EL1t = 0b0100, - EL1h = 0b0101, - EL2t = 0b1000, - EL2h = 0b1001 - }; - - Mode M : 4; - u64 M_4 : 1 = 0; - u64 _reserved5 : 1 = 0; - u64 F : 1; - u64 I : 1; - u64 A : 1; - u64 D : 1; - u64 BTYPE : 2; - u64 SSBS : 1; - u64 _reserved13 : 7 = 0; - u64 IL : 1; - u64 SS : 1; - u64 PAN : 1; - u64 UA0 : 1; - u64 DIT : 1; - u64 TCO : 1; - u64 _reserved26 : 2 = 0; - u64 V : 1; - u64 C : 1; - u64 Z : 1; - u64 N : 1; - u64 _reserved32 : 32 = 0; - - static inline void write(SPSR_EL2 spsr_el2) - { - asm volatile("msr spsr_el2, %[value]" ::[value] "r"(spsr_el2)); - } - - static inline SPSR_EL2 read() - { - SPSR_EL2 spsr; - - asm volatile("mrs %[value], spsr_el2" - : [value] "=r"(spsr)); - - return spsr; - } -}; -static_assert(sizeof(SPSR_EL2) == 8); - -// https://developer.arm.com/documentation/ddi0595/2021-06/AArch64-Registers/SPSR-EL3--Saved-Program-Status-Register--EL3- -// Saved Program Status Register -struct alignas(u64) SPSR_EL3 { - enum Mode : uint16_t { - EL0t = 0b0000, - EL1t = 0b0100, - EL1h = 0b0101, - EL2t = 0b1000, - EL2h = 0b1001, - EL3t = 0b1100, - EL3h = 0b1101 - }; - - Mode M : 4; - u64 M_4 : 1 = 0; - u64 _reserved5 : 1 = 0; - u64 F : 1; - u64 I : 1; - u64 A : 1; - u64 D : 1; - u64 _reserved10 : 10 = 0; - u64 IL : 1; - u64 SS : 1; - u64 PAN : 1; - u64 UA0 : 1; - u64 _reserved24 : 4 = 0; - u64 V : 1; - u64 C : 1; - u64 Z : 1; - u64 N : 1; - u64 _reserved32 : 32 = 0; - - static inline void write(SPSR_EL3 spsr_el3) - { - asm volatile("msr spsr_el3, %[value]" ::[value] "r"(spsr_el3)); - } - - static inline SPSR_EL3 read() - { - SPSR_EL3 spsr; - - asm volatile("mrs %[value], spsr_el3" - : [value] "=r"(spsr)); - - return spsr; - } -}; -static_assert(sizeof(SPSR_EL3) == 8); - -// https://developer.arm.com/documentation/ddi0595/2020-12/AArch64-Registers/MAIR-EL1--Memory-Attribute-Indirection-Register--EL1-?lang=en#fieldset_0-63_0 -// Memory Attribute Indirection Register -struct alignas(u64) MAIR_EL1 { - using AttributeEncoding = uint8_t; - AttributeEncoding Attr[8]; - - static inline void write(MAIR_EL1 mair_el1) - { - asm volatile("msr mair_el1, %[value]" ::[value] "r"(mair_el1)); - } -}; -static_assert(sizeof(MAIR_EL1) == 8); - -// https://developer.arm.com/documentation/ddi0595/2021-06/AArch64-Registers/ESR-EL1--Exception-Syndrome-Register--EL1- -// Exception Syndrome Register (EL1) -struct ESR_EL1 { - u64 ISS : 25; - u64 IL : 1; - u64 EC : 6; - u64 ISS2 : 5; - u64 : 27; - - static inline ESR_EL1 read() - { - ESR_EL1 esr_el1; - - asm volatile("mrs %[value], esr_el1" - : [value] "=r"(esr_el1)); - - return esr_el1; - } -}; -static_assert(sizeof(ESR_EL1) == 8); - -// D17.2.40 FAR_EL1, Fault Address Register (EL1) -struct FAR_EL1 { - u64 virtual_address; - - static inline FAR_EL1 read() - { - FAR_EL1 far_el1; - - asm volatile("mrs %[value], far_el1" - : [value] "=r"(far_el1)); - - return far_el1; - } -}; -static_assert(sizeof(FAR_EL1) == 8); - -// D17.2.37 ESR_EL1, Exception Syndrome Register (EL1) -static inline StringView exception_class_to_string(u8 exception_class) -{ - switch (exception_class) { - case 0b000000: - return "Unknown reason"sv; - case 0b000001: - return "Trapped WF* instruction execution"sv; - case 0b000011: - return "Trapped MCR or MRC access with (coproc==0b1111) that is not reported using EC 0b000000"sv; - case 0b000100: - return "Trapped MCRR or MRRC access with (coproc==0b1111) that is not reported using EC 0b000000"sv; - case 0b000101: - return "Trapped MCR or MRC access with (coproc==0b1110)"sv; - case 0b000110: - return "Trapped LDC or STC access"sv; - case 0b000111: - return "Access to SME, SVE, Advanced SIMD or floating-point functionality trapped by CPACR_EL1.FPEN, CPTR_EL2.FPEN, CPTR_EL2.TFP, or CPTR_EL3.TFP control"sv; - case 0b001010: - return "Trapped execution of an LD64B or ST64B* instruction"sv; - case 0b001100: - return "Trapped MRRC access with (coproc==0b1110)"sv; - case 0b001101: - return "Branch Target Exception"sv; - case 0b001110: - return "Illegal Execution state"sv; - case 0b010001: - return "SVC instruction execution in AArch32 state"sv; - case 0b010101: - return "SVC instruction execution in AArch64 state"sv; - case 0b011000: - return "Trapped MSR, MRS or System instruction execution in AArch64 state, that is not reported using EC 0b000000, 0b000001, or 0b000111"sv; - case 0b011001: - return "Access to SVE functionality trapped as a result of CPACR_EL1.ZEN, CPTR_EL2.ZEN, CPTR_EL2.TZ, or CPTR_EL3.EZ, that is not reported using EC 0b000000"sv; - case 0b011011: - return "Exception from an access to a TSTART instruction at EL0 when SCTLR_EL1.TME0 == 0, EL0 when SCTLR_EL2.TME0 == 0, at EL1 when SCTLR_EL1.TME == 0, at EL2 when SCTLR_EL2.TME == 0 or at EL3 when SCTLR_EL3.TME == 0"sv; - case 0b011100: - return "Exception from a Pointer Authentication instruction authentication failure"sv; - case 0b011101: - return "Access to SME functionality trapped as a result of CPACR_EL1.SMEN, CPTR_EL2.SMEN, CPTR_EL2.TSM, CPTR_EL3.ESM, or an attempted execution of an instruction that is illegal because of the value of PSTATE.SM or PSTATE.ZA, that is not reported using EC 0b000000"sv; - case 0b011110: - return "Exception from a Granule Protection Check"sv; - case 0b100000: - return "Instruction Abort from a lower Exception level"sv; - case 0b100001: - return "Instruction Abort taken without a change in Exception level"sv; - case 0b100010: - return "PC alignment fault exception"sv; - case 0b100100: - return "Data Abort exception from a lower Exception level"sv; - case 0b100101: - return "Data Abort exception taken without a change in Exception level"sv; - case 0b100110: - return "SP alignment fault exception"sv; - case 0b100111: - return "Memory Operation Exception"sv; - case 0b101000: - return "Trapped floating-point exception taken from AArch32 state"sv; - case 0b101100: - return "Trapped floating-point exception taken from AArch64 state"sv; - case 0b101111: - return "SError interrupt"sv; - case 0b110000: - return "Breakpoint exception from a lower Exception level"sv; - case 0b110001: - return "Breakpoint exception taken without a change in Exception level"sv; - case 0b110010: - return "Software Step exception from a lower Exception level"sv; - case 0b110011: - return "Software Step exception taken without a change in Exception level"sv; - case 0b110100: - return "Watchpoint exception from a lower Exception level"sv; - case 0b110101: - return "Watchpoint exception taken without a change in Exception level"sv; - case 0b111000: - return "BKPT instruction execution in AArch32 state"sv; - case 0b111100: - return "BRK instruction execution in AArch64 state"sv; - default: - VERIFY_NOT_REACHED(); - } -} - -// D17.2.40 FAR_EL1, Fault Address Register (EL1) -static inline bool exception_class_has_set_far(u8 exception_class) -{ - // Faulting Virtual Address for synchronous exceptions taken to EL1. Exceptions that set the - // FAR_EL1 are Instruction Aborts (EC 0x20 or 0x21), Data Aborts (EC 0x24 or 0x25), PC alignment - // faults (EC 0x22), and Watchpoints (EC 0x34 or 0x35). ESR_EL1.EC holds the EC value for the - // exception. - switch (exception_class) { - case 0x20: - case 0x21: - case 0x22: - case 0x24: - case 0x25: - case 0x34: - case 0x35: - return true; - default: - return false; - } -} - -static inline bool exception_class_is_data_abort(u8 exception_class) -{ - return exception_class == 0x24 || exception_class == 0x25; -} - -static inline bool exception_class_is_instruction_abort(u8 exception_class) -{ - return exception_class == 0x20 || exception_class == 0x21; -} - -static inline bool exception_class_is_data_or_instruction_abort_from_lower_exception_level(u8 exception_class) -{ - return exception_class == 0x20 || exception_class == 0x24; -} - -static inline bool exception_class_is_svc_instruction_execution(u8 exception_class) -{ - return exception_class == 0x11 || exception_class == 0x15; -} - -// D17.2.37 ESR_EL1, Exception Syndrome Register (EL1) -// ISS encoding for an exception from a Data Abort -// DFSC, bits [5:0] -static inline StringView data_fault_status_code_to_string(u32 instruction_specific_syndrome) -{ - u8 data_fault_status_code = instruction_specific_syndrome & 0x3f; - switch (data_fault_status_code) { - case 0b000000: - return "Address size fault, level 0 of translation or translation table base register"sv; - case 0b000001: - return "Address size fault, level 1"sv; - case 0b000010: - return "Address size fault, level 2"sv; - case 0b000011: - return "Address size fault, level 3"sv; - case 0b000100: - return "Translation fault, level 0"sv; - case 0b000101: - return "Translation fault, level 1"sv; - case 0b000110: - return "Translation fault, level 2"sv; - case 0b000111: - return "Translation fault, level 3"sv; - case 0b001001: - return "Access flag fault, level 1"sv; - case 0b001010: - return "Access flag fault, level 2"sv; - case 0b001011: - return "Access flag fault, level 3"sv; - case 0b001000: - return "Access flag fault, level 0"sv; - case 0b001100: - return "Permission fault, level 0"sv; - case 0b001101: - return "Permission fault, level 1"sv; - case 0b001110: - return "Permission fault, level 2"sv; - case 0b001111: - return "Permission fault, level 3"sv; - case 0b010000: - return "Synchronous External abort, not on translation table walk or hardware update of translation table"sv; - case 0b010001: - return "Synchronous Tag Check Fault"sv; - case 0b010011: - return "Synchronous External abort on translation table walk or hardware update of translation table, level -1"sv; - case 0b010100: - return "Synchronous External abort on translation table walk or hardware update of translation table, level 0"sv; - case 0b010101: - return "Synchronous External abort on translation table walk or hardware update of translation table, level 1"sv; - case 0b010110: - return "Synchronous External abort on translation table walk or hardware update of translation table, level 2"sv; - case 0b010111: - return "Synchronous External abort on translation table walk or hardware update of translation table, level 3"sv; - case 0b011000: - return "Synchronous parity or ECC error on memory access, not on translation table walk"sv; - case 0b011011: - return "Synchronous parity or ECC error on memory access on translation table walk or hardware update of translation table, level -1"sv; - case 0b011100: - return "Synchronous parity or ECC error on memory access on translation table walk or hardware update of translation table, level 0"sv; - case 0b011101: - return "Synchronous parity or ECC error on memory access on translation table walk or hardware update of translation table, level 1"sv; - case 0b011110: - return "Synchronous parity or ECC error on memory access on translation table walk or hardware update of translation table, level 2"sv; - case 0b011111: - return "Synchronous parity or ECC error on memory access on translation table walk or hardware update of translation table, level 3"sv; - case 0b100001: - return "Alignment fault"sv; - case 0b100011: - return "Granule Protection Fault on translation table walk or hardware update of translation table, level -1"sv; - case 0b100100: - return "Granule Protection Fault on translation table walk or hardware update of translation table, level 0"sv; - case 0b100101: - return "Granule Protection Fault on translation table walk or hardware update of translation table, level 1"sv; - case 0b100110: - return "Granule Protection Fault on translation table walk or hardware update of translation table, level 2"sv; - case 0b100111: - return "Granule Protection Fault on translation table walk or hardware update of translation table, level 3"sv; - case 0b101000: - return "Granule Protection Fault, not on translation table walk or hardware update of translation table"sv; - case 0b101001: - return "Address size fault, level -1"sv; - case 0b101011: - return "Translation fault, level -1"sv; - case 0b110000: - return "TLB conflict abort"sv; - case 0b110001: - return "Unsupported atomic hardware update fault"sv; - case 0b110100: - return "IMPLEMENTATION DEFINED fault (Lockdown)"sv; - case 0b110101: - return "IMPLEMENTATION DEFINED fault (Unsupported Exclusive or Atomic access)"sv; - default: - VERIFY_NOT_REACHED(); - } -} - -// https://developer.arm.com/documentation/ddi0601/2020-12/AArch64-Registers/DAIF--Interrupt-Mask-Bits?lang=en -// DAIF, Interrupt Mask Bits -struct DAIF { - u64 : 6; - u64 F : 1; - u64 I : 1; - u64 A : 1; - u64 D : 1; - u64 : 54; - - static inline DAIF read() - { - DAIF daif; - - asm volatile("mrs %[value], daif" - : [value] "=r"(daif)); - - return daif; - } - - // Clearing the I bit, causes interrupts to be enabled. - static inline void clear_I() - { - asm volatile("msr daifclr, #2" :: - :); - } - - // Setting the I bit, causes interrupts to be disabled. - static inline void set_I() - { - asm volatile("msr daifset, #2" :: - :); - } -}; -static_assert(sizeof(DAIF) == 8); - -// https://developer.arm.com/documentation/ddi0595/2021-03/AArch64-Registers/NZCV--Condition-Flags -// NZCV, Condition Flags -struct alignas(u64) NZCV { - u64 : 28; - u64 V : 1; - u64 C : 1; - u64 Z : 1; - u64 N : 1; - u64 : 32; - - static inline NZCV read() - { - NZCV nzcv; - asm volatile("mrs %[value], nzcv" - : [value] "=r"(nzcv)); - return nzcv; - } -}; -static_assert(sizeof(NZCV) == 8); - -// https://developer.arm.com/documentation/ddi0595/2021-03/AArch64-Registers/PMCCNTR-EL0--Performance-Monitors-Cycle-Count-Register -// PMCCNTR_EL0, Performance Monitors Cycle Count Register -struct alignas(u64) PMCCNTR_EL0 { - u64 CCNT : 64; - - static inline PMCCNTR_EL0 read() - { - PMCCNTR_EL0 pmccntr_el0; - asm volatile("mrs %[value], pmccntr_el0" - : [value] "=r"(pmccntr_el0)); - return pmccntr_el0; - } -}; -static_assert(sizeof(PMCCNTR_EL0) == 8); - -// D17.2.30 CPACR_EL1, Architectural Feature Access Control Register -struct alignas(u64) CPACR_EL1 { - u64 _reserved0 : 16 = 0; - u64 ZEN : 2; - u64 _reserved18 : 2 = 0; - u64 FPEN : 2; - u64 _reserved22 : 2 = 0; - u64 SMEN : 2; - u64 _reserved26 : 2 = 0; - u64 TTA : 1; - u64 _reserved29 : 3 = 0; - u64 _reserved32 : 32 = 0; - - static inline void write(CPACR_EL1 cpacr_el1) - { - asm volatile("msr cpacr_el1, %[value]" ::[value] "r"(cpacr_el1)); - } -}; -static_assert(sizeof(CPACR_EL1) == 8); - -} diff --git a/Kernel/Arch/aarch64/SafeMem.cpp b/Kernel/Arch/aarch64/SafeMem.cpp deleted file mode 100644 index 721a1ee00d6..00000000000 --- a/Kernel/Arch/aarch64/SafeMem.cpp +++ /dev/null @@ -1,109 +0,0 @@ -/* - * Copyright (c) 2022, Timon Kruiper - * Copyright (c) 2023, Daniel Bertalan - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include - -#define CODE_SECTION(section_name) __attribute__((section(section_name))) - -extern "C" u8 start_of_safemem_text[]; -extern "C" u8 end_of_safemem_text[]; - -extern "C" u8 start_of_safemem_atomic_text[]; -extern "C" u8 end_of_safemem_atomic_text[]; - -namespace Kernel { - -CODE_SECTION(".text.safemem") -NEVER_INLINE FLATTEN bool safe_memset(void* dest_ptr, int c, size_t n, void*& fault_at) -{ - // FIXME: Actually implement a safe memset. - auto* dest = static_cast(dest_ptr); - for (; n--;) - *dest++ = c; - fault_at = nullptr; - return true; -} - -CODE_SECTION(".text.safemem") -NEVER_INLINE FLATTEN ssize_t safe_strnlen(char const* str, unsigned long max_n, void*& fault_at) -{ - // FIXME: Actually implement a safe strnlen. - size_t len = 0; - for (; len < max_n && *str; str++) - len++; - fault_at = nullptr; - return len; -} - -CODE_SECTION(".text.safemem") -NEVER_INLINE FLATTEN bool safe_memcpy(void* dest_ptr, void const* src_ptr, unsigned long n, void*& fault_at) -{ - // FIXME: Actually implement a safe memcpy. - auto* pd = static_cast(dest_ptr); - auto const* ps = static_cast(src_ptr); - for (; n--;) - *pd++ = *ps++; - fault_at = nullptr; - return true; -} - -CODE_SECTION(".text.safemem.atomic") -NEVER_INLINE FLATTEN Optional safe_atomic_compare_exchange_relaxed(u32 volatile* var, u32& expected, u32 val) -{ - // FIXME: Handle access faults. - return AK::atomic_compare_exchange_strong(var, expected, val, AK::memory_order_relaxed); -} - -CODE_SECTION(".text.safemem.atomic") -NEVER_INLINE FLATTEN Optional safe_atomic_load_relaxed(u32 volatile* var) -{ - // FIXME: Handle access faults. - return AK::atomic_load(var, AK::memory_order_relaxed); -} - -CODE_SECTION(".text.safemem.atomic") -NEVER_INLINE FLATTEN Optional safe_atomic_fetch_add_relaxed(u32 volatile* var, u32 val) -{ - // FIXME: Handle access faults. - return AK::atomic_fetch_add(var, val, AK::memory_order_relaxed); -} - -CODE_SECTION(".text.safemem.atomic") -NEVER_INLINE FLATTEN Optional safe_atomic_exchange_relaxed(u32 volatile* var, u32 val) -{ - // FIXME: Handle access faults. - return AK::atomic_exchange(var, val, AK::memory_order_relaxed); -} - -CODE_SECTION(".text.safemem.atomic") -NEVER_INLINE FLATTEN bool safe_atomic_store_relaxed(u32 volatile* var, u32 val) -{ - // FIXME: Handle access faults. - AK::atomic_store(var, val); - return true; -} - -bool handle_safe_access_fault(RegisterState& regs, FlatPtr fault_address) -{ - FlatPtr ip = regs.ip(); - - if (ip >= (FlatPtr)&start_of_safemem_text && ip < (FlatPtr)&end_of_safemem_text) { - dbgln("FIXME: Faulted while accessing userspace address {:p}.", fault_address); - dbgln(" We need to jump back into the appropriate SafeMem function, set fault_at and return failure."); - TODO_AARCH64(); - } else if (ip >= (FlatPtr)&start_of_safemem_atomic_text && ip < (FlatPtr)&end_of_safemem_atomic_text) { - dbgln("FIXME: Faulted while accessing userspace address {:p}.", fault_address); - dbgln(" We need to jump back into the appropriate atomic SafeMem function and return failure."); - TODO_AARCH64(); - } - - return false; -} - -} diff --git a/Kernel/Arch/aarch64/SmapDisabler.cpp b/Kernel/Arch/aarch64/SmapDisabler.cpp deleted file mode 100644 index cb2be2bae5d..00000000000 --- a/Kernel/Arch/aarch64/SmapDisabler.cpp +++ /dev/null @@ -1,18 +0,0 @@ -/* - * Copyright (c) 2018-2021, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include - -namespace Kernel { - -SmapDisabler::SmapDisabler() - : m_flags(0) -{ -} - -SmapDisabler::~SmapDisabler() = default; - -} diff --git a/Kernel/Arch/aarch64/ThreadRegisters.h b/Kernel/Arch/aarch64/ThreadRegisters.h deleted file mode 100644 index f9f217a929b..00000000000 --- a/Kernel/Arch/aarch64/ThreadRegisters.h +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright (c) 2022, Timon Kruiper - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include - -namespace Kernel { - -struct ThreadRegisters { - u64 x[31]; - u64 spsr_el1; - u64 elr_el1; - u64 sp_el0; - u64 tpidr_el0; - u64 ttbr0_el1; - - FlatPtr ip() const { return elr_el1; } - void set_ip(FlatPtr value) { elr_el1 = value; } - - FlatPtr sp() const { return sp_el0; } - void set_sp(FlatPtr value) { sp_el0 = value; } - - void set_initial_state(bool is_kernel_process, Memory::AddressSpace& space, FlatPtr kernel_stack_top) - { - set_sp(kernel_stack_top); - ttbr0_el1 = space.page_directory().ttbr0(); - set_spsr_el1(is_kernel_process); - } - - void set_entry_function(FlatPtr entry_ip, FlatPtr entry_data) - { - set_ip(entry_ip); - x[0] = entry_data; - } - - void set_exec_state(FlatPtr entry_ip, FlatPtr userspace_sp, Memory::AddressSpace& space) - { - set_ip(entry_ip); - set_sp(userspace_sp); - ttbr0_el1 = space.page_directory().ttbr0(); - set_spsr_el1(false); - } - - void set_spsr_el1(bool is_kernel_process) - { - Aarch64::SPSR_EL1 saved_program_status_register_el1 = {}; - - // Don't mask any interrupts, so all interrupts are enabled when transfering into the new context - saved_program_status_register_el1.D = 0; - saved_program_status_register_el1.A = 0; - saved_program_status_register_el1.I = 0; - saved_program_status_register_el1.F = 0; - - saved_program_status_register_el1.M = is_kernel_process ? Aarch64::SPSR_EL1::Mode::EL1h : Aarch64::SPSR_EL1::Mode::EL0t; - memcpy(&spsr_el1, &saved_program_status_register_el1, sizeof(u64)); - } -}; - -} diff --git a/Kernel/Arch/aarch64/TrapFrame.h b/Kernel/Arch/aarch64/TrapFrame.h deleted file mode 100644 index a72986e0e1f..00000000000 --- a/Kernel/Arch/aarch64/TrapFrame.h +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright (c) 2018-2021, Andreas Kling - * Copyright (c) 2022, Gunnar Beutner - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include - -namespace Kernel { - -struct TrapFrame { - TrapFrame* next_trap; - RegisterState* regs; - - TrapFrame() = delete; - TrapFrame(TrapFrame const&) = delete; - TrapFrame(TrapFrame&&) = delete; - TrapFrame& operator=(TrapFrame const&) = delete; - TrapFrame& operator=(TrapFrame&&) = delete; -}; - -#define TRAP_FRAME_SIZE (2 * 8) -static_assert(AssertSize()); - -} diff --git a/Kernel/Arch/aarch64/archctl.cpp b/Kernel/Arch/aarch64/archctl.cpp deleted file mode 100644 index 98b0eab4d19..00000000000 --- a/Kernel/Arch/aarch64/archctl.cpp +++ /dev/null @@ -1,20 +0,0 @@ -/* - * Copyright (c) 2024, Sönke Holz - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include - -namespace Kernel { - -ErrorOr Process::sys$archctl(int option, FlatPtr arg1) -{ - (void)option; - (void)arg1; - - VERIFY_NO_PROCESS_BIG_LOCK(this); - return ENOSYS; -} - -} diff --git a/Kernel/Arch/aarch64/boot.S b/Kernel/Arch/aarch64/boot.S deleted file mode 100644 index da8c8baf2a9..00000000000 --- a/Kernel/Arch/aarch64/boot.S +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright (c) 2021, Nico Weber - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -// In a specially-named text section so that the linker script can put it first in .text. -.section ".text.first" - -.global start -.type start, @function -start: - // Let only core 0 continue, put other cores to sleep. - mrs x13, MPIDR_EL1 - and x13, x13, 0xff - cbnz x13, halt - - // Let stack start before .text for now. - // 512 kiB (0x80000) of stack are probably not sufficient, especially once we give the other cores some stack too, - // but for now it's ok. - adrp x14, start - add x14, x14, :lo12:start - mov sp, x14 - - // Clear BSS. - adrp x14, start_of_bss - add x14, x14, :lo12:start_of_bss - ldr x15, =size_of_bss_divided_by_8 -Lbss_clear_loop: - str xzr, [x14], #8 - subs x15, x15, #1 - bne Lbss_clear_loop - - b pre_init - -halt: - msr daifset, #2 - wfi - b halt diff --git a/Kernel/Arch/aarch64/kprintf.cpp b/Kernel/Arch/aarch64/kprintf.cpp deleted file mode 100644 index 1c907245456..00000000000 --- a/Kernel/Arch/aarch64/kprintf.cpp +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Copyright (c) 2022, Timon Kruiper - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include - -// FIXME: Merge the code in this file with Kernel/kprintf.cpp once the proper abstractions are in place. - -namespace Kernel { -extern Atomic g_boot_console; -} - -static void console_out(char ch) -{ - if (auto* boot_console = g_boot_console.load()) { - boot_console->write(ch, true); - } -} - -static void critical_console_out(char ch) -{ - if (auto* boot_console = g_boot_console.load()) { - boot_console->write(ch, true); - } -} - -void kernelputstr(char const* characters, size_t length) -{ - if (!characters) - return; - - auto& uart = Kernel::RPi::UART::the(); - uart.print_str(characters, length); - - for (size_t i = 0; i < length; ++i) - console_out(characters[i]); -} - -void kernelcriticalputstr(char const* characters, size_t length) -{ - if (!characters) - return; - - auto& uart = Kernel::RPi::UART::the(); - uart.print_str(characters, length); - - for (size_t i = 0; i < length; ++i) - critical_console_out(characters[i]); -} - -void kernelearlyputstr(char const* characters, size_t length) -{ - kernelputstr(characters, length); -} - -void dbgputstr(char const* characters, size_t length) -{ - kernelputstr(characters, length); -} - -void dbgputstr(StringView view) -{ - dbgputstr(view.characters_without_null_termination(), view.length()); -} - -void dbgputchar(char ch) -{ - kernelputstr(&ch, 1); -} diff --git a/Kernel/Arch/aarch64/linker.ld b/Kernel/Arch/aarch64/linker.ld deleted file mode 100644 index dba38d052db..00000000000 --- a/Kernel/Arch/aarch64/linker.ld +++ /dev/null @@ -1,100 +0,0 @@ -ENTRY(start) - -KERNEL_MAPPING_BASE = 0x2000000000; - -/* TODO: Add FLAGS to the program headers */ -PHDRS -{ - text PT_LOAD ; - data PT_LOAD ; - ksyms PT_LOAD ; - bss PT_LOAD ; -} - -SECTIONS -{ - . = KERNEL_MAPPING_BASE + 0x80000; - - start_of_kernel_image = .; - - .text ALIGN(4K) : AT (ADDR(.text) - KERNEL_MAPPING_BASE) - { - *(.text.first) - - start_of_safemem_text = .; - KEEP(*(.text.safemem)) - end_of_safemem_text = .; - start_of_safemem_atomic_text = .; - KEEP(*(.text.safemem.atomic)) - end_of_safemem_atomic_text = .; - - *(.text*) - } :text - - .driver_init ALIGN(4K) : AT (ADDR(.driver_init) - KERNEL_MAPPING_BASE) - { - driver_init_table_start = .; - *(.driver_init) - driver_init_table_end = .; - } :text - - .rodata ALIGN(4K) : AT (ADDR(.rodata) - KERNEL_MAPPING_BASE) - { - start_heap_ctors = .; - *libkernel_heap.a:*(.init_array) - end_heap_ctors = .; - - start_ctors = .; - *(.init_array) - end_ctors = .; - - *(.rodata*) - } :data - - .data ALIGN(4K) : AT (ADDR(.data) - KERNEL_MAPPING_BASE) - { - *(.data*) - } :data - - .ksyms ALIGN(4K) : AT (ADDR(.ksyms) - KERNEL_MAPPING_BASE) - { - start_of_kernel_ksyms = .; - *(.kernel_symbols) - end_of_kernel_ksyms = .; - } :ksyms - - .bss ALIGN(4K) (NOLOAD) : AT (ADDR(.bss) - KERNEL_MAPPING_BASE) - { - start_of_bss = .; - *(.bss) - end_of_bss = .; - - . = ALIGN(4K); - *(.heap) - } :bss - - /* - FIXME: 8MB is enough space for all of the tables required to identity map - physical memory. 8M is wasteful, so this should be properly calculated. - */ - - /* FIXME: Placeholder to satisfy linker */ - start_of_kernel_text = .; - end_of_kernel_text = .; - start_of_unmap_after_init = .; - end_of_unmap_after_init = .; - start_of_ro_after_init = .; - end_of_ro_after_init = .; - start_of_kernel_data = .; - end_of_kernel_data = .; - - . = ALIGN(4K); - page_tables_phys_start = .; - - . += 8M; - page_tables_phys_end = .; - - end_of_kernel_image = .; -} - -size_of_bss_divided_by_8 = (end_of_bss - start_of_bss + 7) / 8; diff --git a/Kernel/Arch/aarch64/mcontext.h b/Kernel/Arch/aarch64/mcontext.h deleted file mode 100644 index 316631f5045..00000000000 --- a/Kernel/Arch/aarch64/mcontext.h +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright (c) 2022, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include - -#ifdef __cplusplus -extern "C" { -#endif - -struct __attribute__((packed)) __mcontext { - uint64_t x[31]; - uint64_t sp; - uint64_t pc; -}; - -#ifdef __cplusplus -} -#endif diff --git a/Kernel/Arch/aarch64/pre_init.cpp b/Kernel/Arch/aarch64/pre_init.cpp deleted file mode 100644 index a2b3f3dc01b..00000000000 --- a/Kernel/Arch/aarch64/pre_init.cpp +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright (c) 2023, Timon Kruiper - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include - -// We arrive here from boot.S with the MMU disabled and in an unknown exception level (EL). -// The kernel is linked at the virtual address, so we have to be really carefull when accessing -// global variables, as the MMU is not yet enabled. - -// FIXME: This should probably be shared with the Prekernel. - -namespace Kernel { - -extern "C" [[noreturn]] void init(); - -extern "C" [[noreturn]] void pre_init(); -extern "C" [[noreturn]] void pre_init() -{ - // We want to drop to EL1 as soon as possible, because that is the - // exception level the kernel should run at. - initialize_exceptions(); - - // Next step is to set up page tables and enable the MMU. - Memory::init_page_tables(); - - // At this point the MMU is enabled, physical memory is identity mapped, - // and the kernel is also mapped into higher virtual memory. However we are still executing - // from the physical memory address, so we have to jump to the kernel in high memory. We also need to - // switch the stack pointer to high memory, such that we can unmap the identity mapping. - - // Continue execution at high virtual address, by using an absolute jump. - asm volatile( - "ldr x0, =1f \n" - "br x0 \n" - "1: \n" :: - : "x0"); - - // Add kernel_mapping_base to the stack pointer, such that it is also using the mapping - // in high virtual memory. - asm volatile( - "mov x0, %[base] \n" - "add sp, sp, x0 \n" ::[base] "r"(kernel_mapping_base) - : "x0"); - - // We can now unmap the identity map as everything is running in high virtual memory at this point. - Memory::unmap_identity_map(); - - // Clear the frame pointer (x29) and link register (x30) to make sure the kernel cannot backtrace - // into this code, and jump to actual init function in the kernel. - asm volatile( - "mov x29, xzr \n" - "mov x30, xzr \n" - "b init \n"); - - VERIFY_NOT_REACHED(); -} - -} diff --git a/Kernel/Arch/aarch64/vector_table.S b/Kernel/Arch/aarch64/vector_table.S deleted file mode 100644 index 6f3e2be6736..00000000000 --- a/Kernel/Arch/aarch64/vector_table.S +++ /dev/null @@ -1,225 +0,0 @@ -/* - * Copyright (c) 2021, Jesse Buhagiar - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -.section .text.vector_table - -// NOTE: This size must be a multiple of 16 bytes, to ensure that the stack pointer -// stays 16 byte aligned. -#define REGISTER_STATE_SIZE (36 * 8) -#if REGISTER_STATE_SIZE % 16 != 0 -# error "REGISTER_STATE_SIZE is not a multiple of 16 bytes!" -#endif - -#define SPSR_EL1_SLOT (31 * 8) -#define ELR_EL1_SLOT (32 * 8) -#define SP_EL0_SLOT (33 * 8) -#define TPIDR_EL0_SLOT (34 * 8) - -// Vector Table Entry macro. Each entry is aligned at 128 bytes, meaning we have -// at most that many instructions. -.macro table_entry label - .align 7 - b \label -.endm - -.macro unimplemented_entry - .align 7 - wfe - b . -.endm - -.extern exception_common -.extern handle_interrupt - -// -// Save all register states to the current stack -// and enter the C++ exception handler -// -.macro save_current_context - // Allocate stack space for Trap Frame - sub sp, sp, #REGISTER_STATE_SIZE - - stp x0, x1, [sp, #(0 * 0)] - stp x2, x3, [sp, #(2 * 8)] - stp x4, x5, [sp, #(4 * 8)] - stp x6, x7, [sp, #(6 * 8)] - stp x8, x9, [sp, #(8 * 8)] - stp x10, x11, [sp, #(10 * 8)] - stp x12, x13, [sp, #(12 * 8)] - stp x14, x15, [sp, #(14 * 8)] - stp x16, x17, [sp, #(16 * 8)] - stp x18, x19, [sp, #(18 * 8)] - stp x20, x21, [sp, #(20 * 8)] - stp x22, x23, [sp, #(22 * 8)] - stp x24, x25, [sp, #(24 * 8)] - stp x26, x27, [sp, #(26 * 8)] - stp x28, x29, [sp, #(28 * 8)] - str x30, [sp, #(30 * 8)] - - // Let's save some special registers - mrs x0, spsr_el1 - str x0, [sp, #SPSR_EL1_SLOT] - mrs x0, elr_el1 - str x0, [sp, #ELR_EL1_SLOT] - mrs x0, sp_el0 - str x0, [sp, #SP_EL0_SLOT] - mrs x0, tpidr_el0 - str x0, [sp, #TPIDR_EL0_SLOT] - - // Set up TrapFrame struct on the stack - mov x0, sp - sub sp, sp, #16 - str x0, [sp, #(1 * 8)] - str xzr, [sp, #(0 * 0)] - - // Move stack pointer into first argument register - // and jump to the C++ exception handler - mov x0, sp -.endm - -.macro restore_previous_context - // Remove TrapFrame from the stack - add sp, sp, #16 - - // Restore special registers first - ldr x0, [sp, #SPSR_EL1_SLOT] - msr spsr_el1, x0 - ldr x0, [sp, #ELR_EL1_SLOT] - msr elr_el1, x0 - ldr x0, [sp, #SP_EL0_SLOT] - msr sp_el0, x0 - ldr x0, [sp, #TPIDR_EL0_SLOT] - msr tpidr_el0, x0 - - ldp x0, x1, [sp, #(0 * 0)] - ldp x2, x3, [sp, #(2 * 8)] - ldp x4, x5, [sp, #(4 * 8)] - ldp x6, x7, [sp, #(6 * 8)] - ldp x8, x9, [sp, #(8 * 8)] - ldp x10, x11, [sp, #(10 * 8)] - ldp x12, x13, [sp, #(12 * 8)] - ldp x14, x15, [sp, #(14 * 8)] - ldp x16, x17, [sp, #(16 * 8)] - ldp x18, x19, [sp, #(18 * 8)] - ldp x20, x21, [sp, #(20 * 8)] - ldp x22, x23, [sp, #(22 * 8)] - ldp x24, x25, [sp, #(24 * 8)] - ldp x26, x27, [sp, #(26 * 8)] - ldp x28, x29, [sp, #(28 * 8)] - ldr x30, [sp, #(30 * 8)] - - add sp, sp, #REGISTER_STATE_SIZE -.endm - -.global vector_table_el1 -.weak vector_table_el1 // Vector table is weak in case someone wants to hook us in C++ land :^) -.type vector_table_el1, @object - -// Vector table is 2KiB aligned (2^11) -.align 11 -vector_table_el1: - // Exceptions taken from Current EL, with SP_EL0 - table_entry synchronous_current_elsp_el0 - table_entry irq_current_elsp_el0 - table_entry fiq_current_elsp_el0 - table_entry system_error_current_elsp_el0 - - // Exceptions taken from Current EL, with SP_ELx, x>0 - table_entry synchronous_current_elsp_elx - table_entry irq_current_elsp_elx - table_entry fiq_current_elsp_elx - table_entry system_error_current_elsp_elx - - // Exceptions from Lower EL, where causing application is in AArch64 mode - table_entry synchronous_lower_el - table_entry irq_lower_el - table_entry fiq_lower_el - table_entry system_error_lower_el - - // Exceptions from Lower EL, where causing application is in AArch32 mode - unimplemented_entry - unimplemented_entry - unimplemented_entry - unimplemented_entry - -synchronous_current_elsp_elx: - save_current_context - bl exception_common - restore_previous_context - eret - -irq_current_elsp_elx: - save_current_context - bl handle_interrupt - restore_previous_context - eret - -fiq_current_elsp_elx: - save_current_context - bl exception_common - restore_previous_context - eret - -system_error_current_elsp_elx: - save_current_context - bl exception_common - restore_previous_context - eret - -synchronous_current_elsp_el0: - save_current_context - bl exception_common - restore_previous_context - eret - -irq_current_elsp_el0: - save_current_context - bl handle_interrupt - restore_previous_context - eret - -fiq_current_elsp_el0: - save_current_context - bl exception_common - restore_previous_context - eret - -system_error_current_elsp_el0: - save_current_context - bl exception_common - restore_previous_context - eret - -synchronous_lower_el: - save_current_context - bl exception_common - restore_previous_context - eret - -irq_lower_el: - save_current_context - bl handle_interrupt - restore_previous_context - eret - -fiq_lower_el: - save_current_context - bl exception_common - restore_previous_context - eret - -system_error_lower_el: - save_current_context - bl exception_common - restore_previous_context - eret - -.global restore_context_and_eret -restore_context_and_eret: - mov x0, sp - bl exit_trap - restore_previous_context - eret diff --git a/Kernel/Arch/init.cpp b/Kernel/Arch/init.cpp deleted file mode 100644 index e6a11e316df..00000000000 --- a/Kernel/Arch/init.cpp +++ /dev/null @@ -1,515 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#ifdef ENABLE_KERNEL_COVERAGE_COLLECTION -# include -#endif -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#if ARCH(X86_64) -# include -# include -# include -# include -#elif ARCH(AARCH64) -# include -# include -# include -#elif ARCH(RISCV64) -# include -#endif - -// Defined in the linker script -typedef void (*ctor_func_t)(); -extern ctor_func_t start_heap_ctors[]; -extern ctor_func_t end_heap_ctors[]; -extern ctor_func_t start_ctors[]; -extern ctor_func_t end_ctors[]; - -extern uintptr_t __stack_chk_guard; -READONLY_AFTER_INIT uintptr_t __stack_chk_guard __attribute__((used)); - -#if ARCH(X86_64) -extern "C" u8 start_of_safemem_text[]; -extern "C" u8 end_of_safemem_text[]; -extern "C" u8 start_of_safemem_atomic_text[]; -extern "C" u8 end_of_safemem_atomic_text[]; -#endif - -extern "C" USB::DriverInitFunction driver_init_table_start[]; -extern "C" USB::DriverInitFunction driver_init_table_end[]; - -extern "C" u8 end_of_kernel_image[]; - -READONLY_AFTER_INIT SetOnce g_not_in_early_boot; - -namespace Kernel { - -[[noreturn]] static void init_stage2(void*); -static void setup_serial_debug(); - -// boot.S expects these functions to exactly have the following signatures. -// We declare them here to ensure their signatures don't accidentally change. -extern "C" void init_finished(u32 cpu) __attribute__((used)); -extern "C" [[noreturn]] void init_ap(FlatPtr cpu, Processor* processor_info); -extern "C" [[noreturn]] void init(BootInfo const&); - -READONLY_AFTER_INIT VirtualConsole* tty0; - -ProcessID g_init_pid { 0 }; - -ALWAYS_INLINE static Processor& bsp_processor() -{ - // This solves a problem where the bsp Processor instance - // gets "re"-initialized in init() when we run all global constructors. - alignas(Processor) static u8 bsp_processor_storage[sizeof(Processor)]; - return (Processor&)bsp_processor_storage; -} - -// SerenityOS Kernel C++ entry point :^) -// -// This is where C++ execution begins, after boot.S transfers control here. -// -// The purpose of init() is to start multi-tasking. It does the bare minimum -// amount of work needed to start the scheduler. -// -// Once multi-tasking is ready, we spawn a new thread that starts in the -// init_stage2() function. Initialization continues there. - -extern "C" { -READONLY_AFTER_INIT PhysicalAddress start_of_prekernel_image; -READONLY_AFTER_INIT PhysicalAddress end_of_prekernel_image; -READONLY_AFTER_INIT size_t physical_to_virtual_offset; -READONLY_AFTER_INIT FlatPtr kernel_mapping_base; -READONLY_AFTER_INIT FlatPtr kernel_load_base; -READONLY_AFTER_INIT PhysicalAddress boot_pml4t; -READONLY_AFTER_INIT PhysicalAddress boot_pdpt; -READONLY_AFTER_INIT PhysicalAddress boot_pd0; -READONLY_AFTER_INIT PhysicalAddress boot_pd_kernel; -READONLY_AFTER_INIT Memory::PageTableEntry* boot_pd_kernel_pt1023; -READONLY_AFTER_INIT StringView kernel_cmdline; -READONLY_AFTER_INIT u32 multiboot_flags; -READONLY_AFTER_INIT multiboot_memory_map_t* multiboot_memory_map; -READONLY_AFTER_INIT size_t multiboot_memory_map_count; -READONLY_AFTER_INIT PhysicalAddress multiboot_framebuffer_addr; -READONLY_AFTER_INIT u32 multiboot_framebuffer_pitch; -READONLY_AFTER_INIT u32 multiboot_framebuffer_width; -READONLY_AFTER_INIT u32 multiboot_framebuffer_height; -READONLY_AFTER_INIT u8 multiboot_framebuffer_bpp; -READONLY_AFTER_INIT u8 multiboot_framebuffer_type; -READONLY_AFTER_INIT PhysicalAddress multiboot_module_physical_ptr; -READONLY_AFTER_INIT size_t multiboot_module_length; -} - -Atomic g_boot_console; - -#if ARCH(AARCH64) -READONLY_AFTER_INIT static u8 s_command_line_buffer[512]; -#endif - -extern "C" [[noreturn]] UNMAP_AFTER_INIT NO_SANITIZE_COVERAGE void init([[maybe_unused]] BootInfo const& boot_info) -{ -#if ARCH(X86_64) - start_of_prekernel_image = PhysicalAddress { boot_info.start_of_prekernel_image }; - end_of_prekernel_image = PhysicalAddress { boot_info.end_of_prekernel_image }; - physical_to_virtual_offset = boot_info.physical_to_virtual_offset; - kernel_mapping_base = boot_info.kernel_mapping_base; - kernel_load_base = boot_info.kernel_load_base; - gdt64ptr = boot_info.gdt64ptr; - code64_sel = boot_info.code64_sel; - boot_pml4t = PhysicalAddress { boot_info.boot_pml4t }; - boot_pdpt = PhysicalAddress { boot_info.boot_pdpt }; - boot_pd0 = PhysicalAddress { boot_info.boot_pd0 }; - boot_pd_kernel = PhysicalAddress { boot_info.boot_pd_kernel }; - boot_pd_kernel_pt1023 = (Memory::PageTableEntry*)boot_info.boot_pd_kernel_pt1023; - char const* cmdline = (char const*)boot_info.kernel_cmdline; - kernel_cmdline = StringView { cmdline, strlen(cmdline) }; - multiboot_flags = boot_info.multiboot_flags; - multiboot_memory_map = (multiboot_memory_map_t*)boot_info.multiboot_memory_map; - multiboot_memory_map_count = boot_info.multiboot_memory_map_count; - multiboot_module_physical_ptr = PhysicalAddress { boot_info.multiboot_module_physical_ptr }; - multiboot_module_length = boot_info.multiboot_module_length; - multiboot_framebuffer_addr = PhysicalAddress { boot_info.multiboot_framebuffer_addr }; - multiboot_framebuffer_pitch = boot_info.multiboot_framebuffer_pitch; - multiboot_framebuffer_width = boot_info.multiboot_framebuffer_width; - multiboot_framebuffer_height = boot_info.multiboot_framebuffer_height; - multiboot_framebuffer_bpp = boot_info.multiboot_framebuffer_bpp; - multiboot_framebuffer_type = boot_info.multiboot_framebuffer_type; -#elif ARCH(AARCH64) - // FIXME: For the aarch64 platforms, we should get the information by parsing a device tree instead of using multiboot. - auto [ram_base, ram_size] = RPi::Mailbox::the().query_lower_arm_memory_range(); - auto [vcmem_base, vcmem_size] = RPi::Mailbox::the().query_videocore_memory_range(); - multiboot_memory_map_t mmap[] = { - { - sizeof(struct multiboot_mmap_entry) - sizeof(u32), - (u64)ram_base, - (u64)ram_size, - MULTIBOOT_MEMORY_AVAILABLE, - }, - { - sizeof(struct multiboot_mmap_entry) - sizeof(u32), - (u64)vcmem_base, - (u64)vcmem_size, - MULTIBOOT_MEMORY_RESERVED, - }, - // FIXME: VideoCore only reports the first 1GB of RAM, the rest only shows up in the device tree. - }; - multiboot_memory_map = mmap; - multiboot_memory_map_count = 2; - - multiboot_module_length = 0; - // FIXME: Read the /chosen/bootargs property. - kernel_cmdline = RPi::Mailbox::the().query_kernel_command_line(s_command_line_buffer); -#elif ARCH(RISCV64) - auto maybe_command_line = get_command_line_from_fdt(); - if (maybe_command_line.is_error()) - kernel_cmdline = "serial_debug"sv; - else - kernel_cmdline = maybe_command_line.value(); -#endif - - setup_serial_debug(); - - // We need to copy the command line before kmalloc is initialized, - // as it may overwrite parts of multiboot! - CommandLine::early_initialize(kernel_cmdline); - - new (&bsp_processor()) Processor(); - bsp_processor().early_initialize(0); - -#if ARCH(RISCV64) - // We implicitly assume the boot hart is hart 0 above and below - VERIFY(boot_info.mhartid == 0); -#endif - - // Invoke the constructors needed for the kernel heap - for (ctor_func_t* ctor = start_heap_ctors; ctor < end_heap_ctors; ctor++) - (*ctor)(); - kmalloc_init(); - - load_kernel_symbol_table(); - - bsp_processor().initialize(0); - - CommandLine::initialize(); - Memory::MemoryManager::initialize(0); - -#if ARCH(AARCH64) - auto firmware_version = RPi::Mailbox::the().query_firmware_version(); - dmesgln("RPi: Firmware version: {}", firmware_version); - - RPi::Framebuffer::initialize(); -#endif - - // NOTE: If the bootloader provided a framebuffer, then set up an initial console. - // If the bootloader didn't provide a framebuffer, then set up an initial text console. - // We do so we can see the output on the screen as soon as possible. - if (!kernel_command_line().is_early_boot_console_disabled()) { - if ((multiboot_flags & MULTIBOOT_INFO_FRAMEBUFFER_INFO) && !multiboot_framebuffer_addr.is_null() && multiboot_framebuffer_type == MULTIBOOT_FRAMEBUFFER_TYPE_RGB) { - g_boot_console = &try_make_lock_ref_counted(multiboot_framebuffer_addr, multiboot_framebuffer_width, multiboot_framebuffer_height, multiboot_framebuffer_pitch).value().leak_ref(); - } else { -#if ARCH(X86_64) - g_boot_console = &Graphics::VGATextModeConsole::initialize().leak_ref(); -#else - dbgln("No early framebuffer console available"); -#endif - } - } - dmesgln("Starting SerenityOS..."); - -#if ARCH(X86_64) - MM.unmap_prekernel(); - - // Ensure that the safemem sections are not empty. This could happen if the linker accidentally discards the sections. - VERIFY(+start_of_safemem_text != +end_of_safemem_text); - VERIFY(+start_of_safemem_atomic_text != +end_of_safemem_atomic_text); -#endif - - // Invoke all static global constructors in the kernel. - // Note that we want to do this as early as possible. - for (ctor_func_t* ctor = start_ctors; ctor < end_ctors; ctor++) - (*ctor)(); - -#if ARCH(RISCV64) - MUST(unflatten_fdt()); - - if (kernel_command_line().contains("dump_fdt"sv)) - dump_fdt(); - - init_delay_loop(); -#endif - - InterruptManagement::initialize(); - ACPI::initialize(); - - // Initialize TimeManagement before using randomness! - TimeManagement::initialize(0); - - DeviceManagement::initialize(); - SysFSComponentRegistry::initialize(); - DeviceManagement::the().attach_null_device(*NullDevice::must_initialize()); - DeviceManagement::the().attach_console_device(*ConsoleDevice::must_create()); - DeviceManagement::the().attach_device_control_device(*DeviceControlDevice::must_create()); - - __stack_chk_guard = get_fast_random(); - - Process::initialize(); - - Scheduler::initialize(); - -#if ARCH(X86_64) - // FIXME: Add an abstraction for the smp related functions, instead of using ifdefs in this file. - if (APIC::initialized() && APIC::the().enabled_processor_count() > 1) { - // We must set up the AP boot environment before switching to a kernel process, - // as pages below address USER_RANGE_BASE are only accessible through the kernel - // page directory. - APIC::the().setup_ap_boot_environment(); - } -#endif - - MUST(Process::create_kernel_process("init_stage2"sv, init_stage2, nullptr, THREAD_AFFINITY_DEFAULT, Process::RegisterProcess::No)); - - Scheduler::start(); - VERIFY_NOT_REACHED(); -} - -#if ARCH(X86_64) -// -// This is where C++ execution begins for APs, after boot.S transfers control here. -// -// The purpose of init_ap() is to initialize APs for multi-tasking. -// -extern "C" [[noreturn]] UNMAP_AFTER_INIT void init_ap(FlatPtr cpu, Processor* processor_info) -{ - processor_info->early_initialize(cpu); - - processor_info->initialize(cpu); - Memory::MemoryManager::initialize(cpu); - - Scheduler::set_idle_thread(APIC::the().get_idle_thread(cpu)); - - Scheduler::start(); - VERIFY_NOT_REACHED(); -} - -// -// This method is called once a CPU enters the scheduler and its idle thread -// At this point the initial boot stack can be freed -// -extern "C" UNMAP_AFTER_INIT void init_finished(u32 cpu) -{ - if (cpu == 0) { - // TODO: we can reuse the boot stack, maybe for kmalloc()? - } else { - APIC::the().init_finished(cpu); - TimeManagement::initialize(cpu); - } -} -#endif - -void init_stage2(void*) -{ - // This is a little bit of a hack. We can't register our process at the time we're - // creating it, but we need to be registered otherwise finalization won't be happy. - // The colonel process gets away without having to do this because it never exits. - Process::register_new(Process::current()); - - WorkQueue::initialize(); - -#if ARCH(X86_64) - if (kernel_command_line().is_smp_enabled() && APIC::initialized() && APIC::the().enabled_processor_count() > 1) { - // We can't start the APs until we have a scheduler up and running. - // We need to be able to process ICI messages, otherwise another - // core may send too many and end up deadlocking once the pool is - // exhausted - APIC::the().boot_aps(); - } -#endif - - // Initialize the PCI Bus as early as possible, for early boot (PCI based) serial logging - PCI::initialize(); - if (!PCI::Access::is_disabled()) { - PCISerialDevice::detect(); - } - - VirtualFileSystem::initialize(); - -#if ARCH(X86_64) - if (!is_serial_debug_enabled()) - (void)SerialDevice::must_create(0).leak_ref(); - (void)SerialDevice::must_create(1).leak_ref(); - (void)SerialDevice::must_create(2).leak_ref(); - (void)SerialDevice::must_create(3).leak_ref(); -#elif ARCH(AARCH64) - (void)MUST(RPi::MiniUART::create()).leak_ref(); -#endif - - (void)PCSpeakerDevice::must_create().leak_ref(); - -#if ARCH(X86_64) - VMWareBackdoor::the(); // don't wait until first mouse packet -#endif - MUST(HIDManagement::initialize()); - - GraphicsManagement::the().initialize(); - ConsoleManagement::the().initialize(); - - SyncTask::spawn(); - FinalizerTask::spawn(); - - auto boot_profiling = kernel_command_line().is_boot_profiling_enabled(); - - if (!PCI::Access::is_disabled()) { - USB::USBManagement::initialize(); - } - SysFSFirmwareDirectory::initialize(); - - if (!PCI::Access::is_disabled()) { - VirtIO::detect_pci_instances(); - } - - NetworkingManagement::the().initialize(); - -#ifdef ENABLE_KERNEL_COVERAGE_COLLECTION - (void)KCOVDevice::must_create().leak_ref(); -#endif - (void)MemoryDevice::must_create().leak_ref(); - (void)ZeroDevice::must_create().leak_ref(); - (void)FullDevice::must_create().leak_ref(); - (void)FUSEDevice::must_create().leak_ref(); - (void)RandomDevice::must_create().leak_ref(); - (void)SelfTTYDevice::must_create().leak_ref(); - PTYMultiplexer::initialize(); - - AudioManagement::the().initialize(); - - // Initialize all USB Drivers - for (auto* init_function = driver_init_table_start; init_function != driver_init_table_end; init_function++) - (*init_function)(); - - StorageManagement::the().initialize(kernel_command_line().is_nvme_polling_enabled()); - for (int i = 0; i < 5; ++i) { - if (StorageManagement::the().determine_boot_device(kernel_command_line().root_device())) - break; - dbgln_if(STORAGE_DEVICE_DEBUG, "Boot device {} not found, sleeping 2 seconds", kernel_command_line().root_device()); - (void)Thread::current()->sleep(Duration::from_seconds(2)); - } - if (VirtualFileSystem::the().mount_root(StorageManagement::the().root_filesystem()).is_error()) { - PANIC("VirtualFileSystem::mount_root failed"); - } - - // Switch out of early boot mode. - g_not_in_early_boot.set(); - - // NOTE: Everything marked READONLY_AFTER_INIT becomes non-writable after this point. - MM.protect_readonly_after_init_memory(); - - // NOTE: Everything in the .ksyms section becomes read-only after this point. - MM.protect_ksyms_after_init(); - - // NOTE: Everything marked UNMAP_AFTER_INIT becomes inaccessible after this point. - MM.unmap_text_after_init(); - - auto userspace_init = kernel_command_line().userspace_init(); - auto init_args = kernel_command_line().userspace_init_args(); - - dmesgln("Running first user process: {}", userspace_init); - dmesgln("Init (first) process args: {}", init_args); - auto init_or_error = Process::create_user_process(userspace_init, UserID(0), GroupID(0), move(init_args), {}, tty0); - if (init_or_error.is_error()) - PANIC("init_stage2: Error spawning init process: {}", init_or_error.error()); - - auto [init_process, init_thread] = init_or_error.release_value(); - - g_init_pid = init_process->pid(); - init_thread->set_priority(THREAD_PRIORITY_HIGH); - - NetworkTask::spawn(); - - // NOTE: All kernel processes must be created before enabling boot profiling. - // This is so profiling_enable() can emit process created performance events for them. - if (boot_profiling) { - dbgln("Starting full system boot profiling"); - MutexLocker mutex_locker(Process::current().big_lock()); - auto const enable_all = ~(u64)0; - auto result = Process::current().profiling_enable(-1, enable_all); - VERIFY(!result.is_error()); - } - - Process::current().sys$exit(0); - VERIFY_NOT_REACHED(); -} - -UNMAP_AFTER_INIT void setup_serial_debug() -{ - // serial_debug will output all the dbgln() data to COM1 at - // 8-N-1 57600 baud. this is particularly useful for debugging the boot - // process on live hardware. - if (kernel_cmdline.contains("serial_debug"sv)) { - set_serial_debug_enabled(true); - } -} - -// Define some Itanium C++ ABI methods to stop the linker from complaining. -// If we actually call these something has gone horribly wrong -void* __dso_handle __attribute__((visibility("hidden"))); - -} diff --git a/Kernel/Arch/mcontext.h b/Kernel/Arch/mcontext.h deleted file mode 100644 index e87802d9ffd..00000000000 --- a/Kernel/Arch/mcontext.h +++ /dev/null @@ -1,15 +0,0 @@ -/* - * Copyright (c) 2022, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#if defined(__x86_64__) -# include -#elif defined(__aarch64__) -# include -#elif defined(__riscv) && __riscv_xlen == 64 -# include -#endif diff --git a/Kernel/Arch/riscv64/ArchSpecificThreadData.h b/Kernel/Arch/riscv64/ArchSpecificThreadData.h deleted file mode 100644 index 5dbe07b8d27..00000000000 --- a/Kernel/Arch/riscv64/ArchSpecificThreadData.h +++ /dev/null @@ -1,16 +0,0 @@ -/* - * Copyright (c) 2024, Sönke Holz - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include - -namespace Kernel { - -struct ArchSpecificThreadData { -}; - -} diff --git a/Kernel/Arch/riscv64/CPU.cpp b/Kernel/Arch/riscv64/CPU.cpp deleted file mode 100644 index d2baed0f0c6..00000000000 --- a/Kernel/Arch/riscv64/CPU.cpp +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright (c) 2024, Leon Albrecht - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include -#include - -static Singleton> s_device_tree; - -namespace Kernel { - -BootInfo s_boot_info; - -alignas(PAGE_SIZE) __attribute__((section(".bss.fdt"))) u8 s_fdt_storage[fdt_storage_size]; - -ErrorOr unflatten_fdt() -{ - *s_device_tree = TRY(DeviceTree::DeviceTree::parse({ s_fdt_storage, fdt_storage_size })); - return {}; -} - -void dump_fdt() -{ - auto& header = *bit_cast(&s_fdt_storage[0]); - auto fdt = ReadonlyBytes(s_fdt_storage, header.totalsize); - MUST(DeviceTree::dump(header, fdt)); -} - -ErrorOr get_command_line_from_fdt() -{ - auto& header = *bit_cast(&s_fdt_storage[0]); - auto fdt = ReadonlyBytes(s_fdt_storage, header.totalsize); - return TRY(DeviceTree::slow_get_property("/chosen/bootargs"sv, header, fdt)).as_string(); -} - -} - -DeviceTree::DeviceTree const& DeviceTree::get() -{ - VERIFY(*s_device_tree); - return **s_device_tree; -} diff --git a/Kernel/Arch/riscv64/CPU.h b/Kernel/Arch/riscv64/CPU.h deleted file mode 100644 index 3a8831169b2..00000000000 --- a/Kernel/Arch/riscv64/CPU.h +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright (c) 2023, Sönke Holz - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include - -#include -VALIDATE_IS_RISCV64() - -namespace Kernel { - -constexpr size_t fdt_storage_size = 2 * MiB; -extern u8 s_fdt_storage[fdt_storage_size]; - -// FIXME: These should move to an architecture independent location, -// once we need device tree parsing in other architectures, like aarch64 -extern BootInfo s_boot_info; - -ErrorOr unflatten_fdt(); -void dump_fdt(); -ErrorOr get_command_line_from_fdt(); -} - -namespace DeviceTree { -DeviceTree const& get(); -} diff --git a/Kernel/Arch/riscv64/CPUID.h b/Kernel/Arch/riscv64/CPUID.h deleted file mode 100644 index a3eb3c5db6e..00000000000 --- a/Kernel/Arch/riscv64/CPUID.h +++ /dev/null @@ -1,17 +0,0 @@ -/* - * Copyright (c) 2023, Sönke Holz - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include - -#include -VALIDATE_IS_RISCV64() - -AK_MAKE_ARBITRARY_SIZED_ENUM(CPUFeature, u256, - __End = CPUFeature(1u) << 255u) // SENTINEL VALUE diff --git a/Kernel/Arch/riscv64/CSR.h b/Kernel/Arch/riscv64/CSR.h deleted file mode 100644 index 7b05bdff790..00000000000 --- a/Kernel/Arch/riscv64/CSR.h +++ /dev/null @@ -1,393 +0,0 @@ -/* - * Copyright (c) 2023, Sönke Holz - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include - -#include -VALIDATE_IS_RISCV64() - -// Documentation for the CSRs: -// RISC-V ISA Manual, Volume II (https://github.com/riscv/riscv-isa-manual/releases/download/Priv-v1.12/riscv-privileged-20211203.pdf) - -namespace Kernel::RISCV64::CSR { - -// 2.2 CSR Listing -enum class Address : u16 { - // Supervisor Trap Setup - SSTATUS = 0x100, - SIE = 0x104, - STVEC = 0x105, - - // Supervisor Protection and Translation - SATP = 0x180, - - // Unprivileged Counters/Timers - CYCLE = 0xc00, - TIME = 0xc01, -}; - -ALWAYS_INLINE FlatPtr read(Address address) -{ - FlatPtr ret; - asm volatile("csrr %0, %1" - : "=r"(ret) - : "i"(address)); - return ret; -} - -ALWAYS_INLINE void write(Address address, FlatPtr value) -{ - asm volatile("csrw %0, %1" ::"i"(address), "Kr"(value)); -} - -ALWAYS_INLINE FlatPtr read_and_set_bits(Address address, FlatPtr bit_mask) -{ - FlatPtr ret; - asm volatile("csrrs %0, %1, %2" - : "=r"(ret) - : "i"(address), "Kr"(bit_mask)); - return ret; -} - -ALWAYS_INLINE void set_bits(Address address, FlatPtr bit_mask) -{ - asm volatile("csrs %0, %1" ::"i"(address), "Kr"(bit_mask)); -} - -ALWAYS_INLINE void clear_bits(Address address, FlatPtr bit_mask) -{ - asm volatile("csrc %0, %1" ::"i"(address), "Kr"(bit_mask)); -} - -// 4.1.11 Supervisor Address Translation and Protection (satp) Register -struct [[gnu::packed]] alignas(u64) SATP { - enum class Mode : u64 { - Bare = 0, - Sv39 = 8, - Sv48 = 9, - Sv57 = 10, - }; - - // Physical page number of root page table - u64 PPN : 44; - - // Address space identifier - u64 ASID : 16; - - // Current address-translation scheme - Mode MODE : 4; - - static ALWAYS_INLINE void write(SATP satp) - { - CSR::write(CSR::Address::SATP, bit_cast(satp)); - } - - static ALWAYS_INLINE SATP read() - { - return bit_cast(CSR::read(CSR::Address::SATP)); - } - - bool operator==(SATP const& other) const - { - return bit_cast(*this) == bit_cast(other); - } -}; -static_assert(AssertSize()); - -// 4.1.1 Supervisor Status Register (sstatus) -struct [[gnu::packed]] alignas(u64) SSTATUS { - // Useful for CSR::{set,clear}_bits - enum class Offset { - SIE = 1, - SPIE = 5, - UBE = 6, - SPP = 8, - VS = 9, - FS = 13, - XS = 15, - SUM = 18, - MXR = 19, - UXL = 32, - SD = 63, - }; - - enum class PrivilegeMode : u64 { - User = 0, - Supervisor = 1, - }; - - enum class FloatingPointStatus : u64 { - Off = 0, - Initial = 1, - Clean = 2, - Dirty = 3, - }; - - enum class VectorStatus : u64 { - Off = 0, - Initial = 1, - Clean = 2, - Dirty = 3, - }; - - enum class UserModeExtensionsStatus : u64 { - AllOff = 0, - NoneDirtyOrClean_SomeOn = 1, - NoneDirty_SomeOn = 2, - SomeDirty = 3, - }; - - enum class XLEN : u64 { - Bits32 = 1, - Bits64 = 2, - Bits128 = 3, - }; - - u64 _reserved0 : 1; - - // Enables or disables all interrupts in supervisor mode - u64 SIE : 1; - - u64 _reserved2 : 3; - - // Indicates whether supervisor interrupts were enabled prior to trapping into supervisor mode - // When a trap is taken into supervisor mode, SPIE is set to SIE, and SIE is set to 0. When - // an SRET instruction is executed, SIE is set to SPIE, then SPIE is set to 1. - u64 SPIE : 1; - - // Controls the endianness of explicit memory accesses made from - // U-mode, which may differ from the endianness of memory accesses in S-mode - u64 UBE : 1; - - u64 _reserved7 : 1; - - // Indicates the privilege level at which a hart was executing before entering supervisor mode - PrivilegeMode SPP : 1; - - // Encodes the status of the vector extension state, including the vector registers v0–v31 and - // the CSRs vcsr, vxrm, vxsat, vstart, vl, vtype, and vlenb. - VectorStatus VS : 2; - - u64 _reserved11 : 2; - - // Encodes the status of the floating-point unit state, - // including the floating-point registers f0–f31 and the CSRs fcsr, frm, and fflags. - FloatingPointStatus FS : 2; - - // The XS field encodes the status of additional user-mode extensions and associated state. - UserModeExtensionsStatus XS : 2; - - u64 _reserved17 : 1; - - // The SUM (permit Supervisor User Memory access) bit modifies the privilege with which S-mode - // loads and stores access virtual memory. When SUM=0, S-mode memory accesses to pages that are - // accessible by U-mode (U=1 in Figure 5.18) will fault. When SUM=1, these accesses are permitted. - // SUM has no effect when page-based virtual memory is not in effect, nor when executing in U-mode. - // Note that S-mode can never execute instructions from user pages, regardless of the state of SUM. - u64 SUM : 1; - - // The MXR (Make eXecutable Readable) bit modifies the privilege with which loads access virtual - // memory. When MXR=0, only loads from pages marked readable (R=1 in Figure 5.18) will succeed. - // When MXR=1, loads from pages marked either readable or executable (R=1 or X=1) will succeed. - // MXR has no effect when page-based virtual memory is not in effect. - u64 MXR : 1; - - u64 _reserved20 : 12; - - // Controls the value of XLEN for U-mode - XLEN UXL : 2; - - u64 _reserved34 : 29; - - // The SD bit is a read-only bit that summarizes whether either the FS, VS, or XS fields signal the - // presence of some dirty state that will require saving extended user context to memory. - u64 SD : 1; - - static ALWAYS_INLINE void write(SSTATUS sstatus) - { - CSR::write(CSR::Address::SSTATUS, bit_cast(sstatus)); - } - - static ALWAYS_INLINE SSTATUS read() - { - return bit_cast(CSR::read(CSR::Address::SSTATUS)); - } -}; -static_assert(AssertSize()); - -// 4.1.8 Supervisor Cause Register (scause) -constexpr u64 SCAUSE_INTERRUPT_MASK = 1LU << 63; - -enum class SCAUSE : u64 { - // Interrupts - SupervisorSoftwareInterrupt = SCAUSE_INTERRUPT_MASK | 1, - SupervisorTimerInterrupt = SCAUSE_INTERRUPT_MASK | 5, - SupervisorExternalInterrupt = SCAUSE_INTERRUPT_MASK | 9, - - // Exceptions - InstructionAddressMisaligned = 0, - InstructionAccessFault = 1, - IllegalInstrction = 2, - Breakpoint = 3, - LoadAddressMisaligned = 4, - LoadAccessFault = 5, - StoreOrAMOAddressMisaligned = 6, - StoreOrAMOAccessFault = 7, - EnvironmentCallFromUMode = 8, - EnvironmentCallFromSMode = 9, - - InstructionPageFault = 12, - LoadPageFault = 13, - - StoreOrAMOPageFault = 15, -}; - -} - -template<> -struct AK::Formatter : AK::Formatter { - ErrorOr format(FormatBuilder& builder, Kernel::RISCV64::CSR::SSTATUS value) - { - if (value.SD) - TRY(builder.put_literal("SD "sv)); - - switch (value.UXL) { - case Kernel::RISCV64::CSR::SSTATUS::XLEN::Bits32: - TRY(builder.put_literal("UXL=32 "sv)); - break; - case Kernel::RISCV64::CSR::SSTATUS::XLEN::Bits64: - TRY(builder.put_literal("UXL=64 "sv)); - break; - case Kernel::RISCV64::CSR::SSTATUS::XLEN::Bits128: - TRY(builder.put_literal("UXL=128 "sv)); - break; - } - - if (value.MXR) - TRY(builder.put_literal("MXR "sv)); - - if (value.SUM) - TRY(builder.put_literal("SUM "sv)); - - switch (value.XS) { - case Kernel::RISCV64::CSR::SSTATUS::UserModeExtensionsStatus::AllOff: - TRY(builder.put_literal("XS=AllOff "sv)); - break; - case Kernel::RISCV64::CSR::SSTATUS::UserModeExtensionsStatus::NoneDirtyOrClean_SomeOn: - TRY(builder.put_literal("XS=NoneDirtyOrClean_SomeOn "sv)); - break; - case Kernel::RISCV64::CSR::SSTATUS::UserModeExtensionsStatus::NoneDirty_SomeOn: - TRY(builder.put_literal("XS=NoneDirty_SomeOn "sv)); - break; - case Kernel::RISCV64::CSR::SSTATUS::UserModeExtensionsStatus::SomeDirty: - TRY(builder.put_literal("XS=SomeDirty "sv)); - break; - } - - switch (value.FS) { - case Kernel::RISCV64::CSR::SSTATUS::FloatingPointStatus::Off: - TRY(builder.put_literal("FS=Off "sv)); - break; - case Kernel::RISCV64::CSR::SSTATUS::FloatingPointStatus::Initial: - TRY(builder.put_literal("FS=Initial "sv)); - break; - case Kernel::RISCV64::CSR::SSTATUS::FloatingPointStatus::Clean: - TRY(builder.put_literal("FS=Clean "sv)); - break; - case Kernel::RISCV64::CSR::SSTATUS::FloatingPointStatus::Dirty: - TRY(builder.put_literal("FS=Dirty "sv)); - break; - } - - switch (value.VS) { - case Kernel::RISCV64::CSR::SSTATUS::VectorStatus::Off: - TRY(builder.put_literal("VS=Off "sv)); - break; - case Kernel::RISCV64::CSR::SSTATUS::VectorStatus::Initial: - TRY(builder.put_literal("VS=Initial "sv)); - break; - case Kernel::RISCV64::CSR::SSTATUS::VectorStatus::Clean: - TRY(builder.put_literal("VS=Clean "sv)); - break; - case Kernel::RISCV64::CSR::SSTATUS::VectorStatus::Dirty: - TRY(builder.put_literal("VS=Dirty "sv)); - break; - } - - switch (value.SPP) { - case Kernel::RISCV64::CSR::SSTATUS::PrivilegeMode::User: - TRY(builder.put_literal("SPP=User "sv)); - break; - case Kernel::RISCV64::CSR::SSTATUS::PrivilegeMode::Supervisor: - TRY(builder.put_literal("SPP=Supervisor "sv)); - break; - } - - if (value.UBE) - TRY(builder.put_literal("UBE "sv)); - - if (value.SPIE) - TRY(builder.put_literal("SPIE "sv)); - - if (value.SIE) - TRY(builder.put_literal("SIE "sv)); - - TRY(builder.put_literal("("sv)); - TRY(builder.put_u64(bit_cast(value), 16, true, false, true, false, FormatBuilder::Align::Right, 16)); - TRY(builder.put_literal(")"sv)); - - return {}; - } -}; - -template<> -struct AK::Formatter : AK::Formatter { - ErrorOr format(FormatBuilder& builder, Kernel::RISCV64::CSR::SCAUSE value) - { - switch (value) { - case Kernel::RISCV64::CSR::SCAUSE::SupervisorSoftwareInterrupt: - return builder.put_string("Supervisor software interrupt"sv); - case Kernel::RISCV64::CSR::SCAUSE::SupervisorTimerInterrupt: - return builder.put_string("Supervisor timer interrupt"sv); - case Kernel::RISCV64::CSR::SCAUSE::SupervisorExternalInterrupt: - return builder.put_string("Supervisor external interrupt"sv); - - case Kernel::RISCV64::CSR::SCAUSE::InstructionAddressMisaligned: - return builder.put_string("Instruction address misaligned"sv); - case Kernel::RISCV64::CSR::SCAUSE::InstructionAccessFault: - return builder.put_string("Instruction access fault"sv); - case Kernel::RISCV64::CSR::SCAUSE::IllegalInstrction: - return builder.put_string("Illegal instruction"sv); - case Kernel::RISCV64::CSR::SCAUSE::Breakpoint: - return builder.put_string("Breakpoint"sv); - case Kernel::RISCV64::CSR::SCAUSE::LoadAddressMisaligned: - return builder.put_string("Load address misaligned"sv); - case Kernel::RISCV64::CSR::SCAUSE::LoadAccessFault: - return builder.put_string("Load access fault"sv); - case Kernel::RISCV64::CSR::SCAUSE::StoreOrAMOAddressMisaligned: - return builder.put_string("Store/AMO address misaligned"sv); - case Kernel::RISCV64::CSR::SCAUSE::StoreOrAMOAccessFault: - return builder.put_string("Store/AMO access fault"sv); - case Kernel::RISCV64::CSR::SCAUSE::EnvironmentCallFromUMode: - return builder.put_string("Environment call from U-mode"sv); - case Kernel::RISCV64::CSR::SCAUSE::EnvironmentCallFromSMode: - return builder.put_string("Environment call from S-mode"sv); - case Kernel::RISCV64::CSR::SCAUSE::InstructionPageFault: - return builder.put_string("Instruction page fault"sv); - case Kernel::RISCV64::CSR::SCAUSE::LoadPageFault: - return builder.put_string("Load page fault"sv); - case Kernel::RISCV64::CSR::SCAUSE::StoreOrAMOPageFault: - return builder.put_string("Store/AMO page fault"sv); - default: - VERIFY_NOT_REACHED(); - } - } -}; diff --git a/Kernel/Arch/riscv64/CurrentTime.cpp b/Kernel/Arch/riscv64/CurrentTime.cpp deleted file mode 100644 index ec3ebf32c97..00000000000 --- a/Kernel/Arch/riscv64/CurrentTime.cpp +++ /dev/null @@ -1,16 +0,0 @@ -/* - * Copyright (c) 2023, Sönke Holz - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include - -namespace Kernel { - -fptr optional_current_time() -{ - return nullptr; -} - -} diff --git a/Kernel/Arch/riscv64/DebugOutput.cpp b/Kernel/Arch/riscv64/DebugOutput.cpp deleted file mode 100644 index b6f61f608dc..00000000000 --- a/Kernel/Arch/riscv64/DebugOutput.cpp +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Copyright (c) 2023, Sönke Holz - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include - -namespace Kernel { - -void debug_output(char c) -{ - // FIXME: add extension probing support to SBI.cpp to check which debug console extensions are available - (void)SBI::Legacy::console_putchar(c); -} - -} diff --git a/Kernel/Arch/riscv64/Delay.cpp b/Kernel/Arch/riscv64/Delay.cpp deleted file mode 100644 index b40d94fe187..00000000000 --- a/Kernel/Arch/riscv64/Delay.cpp +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright (c) 2023, Sönke Holz - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include - -namespace Kernel { - -static u32 s_timebase_frequency = 0; - -void microseconds_delay(u32 microseconds) -{ - VERIFY(s_timebase_frequency != 0); - - u64 const start = RISCV64::CSR::read(RISCV64::CSR::Address::TIME); - u64 const delta = (static_cast(microseconds) * s_timebase_frequency) / 1'000'000ull; - - while ((RISCV64::CSR::read(RISCV64::CSR::Address::TIME) - start) < delta) - Processor::pause(); -} - -void init_delay_loop() -{ - s_timebase_frequency = DeviceTree::get().resolve_property("/cpus/timebase-frequency"sv).value().as(); -} - -} diff --git a/Kernel/Arch/riscv64/Delay.h b/Kernel/Arch/riscv64/Delay.h deleted file mode 100644 index e96dcbce7a6..00000000000 --- a/Kernel/Arch/riscv64/Delay.h +++ /dev/null @@ -1,15 +0,0 @@ -/* - * Copyright (c) 2024, Sönke Holz - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include - -namespace Kernel { - -void init_delay_loop(); - -} diff --git a/Kernel/Arch/riscv64/FPUState.h b/Kernel/Arch/riscv64/FPUState.h deleted file mode 100644 index a8c67dac8f6..00000000000 --- a/Kernel/Arch/riscv64/FPUState.h +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright (c) 2023, Sönke Holz - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include - -#include -VALIDATE_IS_RISCV64() - -namespace Kernel { - -// This struct will get pushed on the stack by the signal handling code. -// Therefore, it has to be aligned to a 16-byte boundary. -struct [[gnu::aligned(16)]] FPUState { - // FIXME: Add support for the Q extension. - u64 f[32]; - u64 fcsr; -}; - -} diff --git a/Kernel/Arch/riscv64/Firmware/ACPI/StaticParsing.cpp b/Kernel/Arch/riscv64/Firmware/ACPI/StaticParsing.cpp deleted file mode 100644 index 44685cf34b6..00000000000 --- a/Kernel/Arch/riscv64/Firmware/ACPI/StaticParsing.cpp +++ /dev/null @@ -1,17 +0,0 @@ -/* - * Copyright (c) 2023, Sönke Holz - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include - -namespace Kernel::ACPI::StaticParsing { - -ErrorOr> find_rsdp_in_platform_specific_memory_locations() -{ - // FIXME: Implement finding RSDP for riscv64. - return Optional {}; -} - -} diff --git a/Kernel/Arch/riscv64/IRQController.h b/Kernel/Arch/riscv64/IRQController.h deleted file mode 100644 index 09e3ec7ed2e..00000000000 --- a/Kernel/Arch/riscv64/IRQController.h +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright (c) 2023, Sönke Holz - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include - -#include - -#include -VALIDATE_IS_RISCV64() - -namespace Kernel { - -class IRQController : public AtomicRefCounted { -public: - virtual ~IRQController() = default; - - virtual void enable(GenericInterruptHandler const&) = 0; - virtual void disable(GenericInterruptHandler const&) = 0; - - virtual void eoi(GenericInterruptHandler const&) = 0; - - virtual u8 pending_interrupt() const = 0; - - virtual StringView model() const = 0; - -protected: - IRQController() = default; -}; - -} diff --git a/Kernel/Arch/riscv64/InterruptManagement.cpp b/Kernel/Arch/riscv64/InterruptManagement.cpp deleted file mode 100644 index 9766263d34e..00000000000 --- a/Kernel/Arch/riscv64/InterruptManagement.cpp +++ /dev/null @@ -1,121 +0,0 @@ -/* - * Copyright (c) 2023, Sönke Holz - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include -#include - -namespace Kernel { - -static InterruptManagement* s_interrupt_management; - -bool InterruptManagement::initialized() -{ - return s_interrupt_management != nullptr; -} - -InterruptManagement& InterruptManagement::the() -{ - VERIFY(InterruptManagement::initialized()); - return *s_interrupt_management; -} - -void InterruptManagement::initialize() -{ - VERIFY(!InterruptManagement::initialized()); - s_interrupt_management = new InterruptManagement; - - the().find_controllers(); -} - -void InterruptManagement::find_controllers() -{ - auto const& device_tree = DeviceTree::get(); - auto maybe_soc = device_tree.get_child("soc"sv); - if (!maybe_soc.has_value()) { - dmesgln("Interrupts: No `soc` node found in the device tree, Interrupts initialization will be skipped"); - return; - } - auto const& soc = maybe_soc.value(); - auto soc_address_cells = soc.get_property("#address-cells"sv).value().as(); - auto soc_size_cells = soc.get_property("#size-cells"sv).value().as(); - auto interrupt_controllers_seen = 0; - for (auto const& [node_name, node] : soc.children()) { - if (!node.has_property("interrupt-controller"sv)) - continue; - - interrupt_controllers_seen++; - - auto maybe_compatible = node.get_property("compatible"sv); - if (!maybe_compatible.has_value()) { - dmesgln("Interrupts: Devicetree node for {} does not have a 'compatible' string, rejecting", node_name); - continue; - } - - // Reject non sifive-compatible interrupt controllers - auto sifive_plic = false; - maybe_compatible->for_each_string([&sifive_plic](StringView compatible_string) -> IterationDecision { - if (compatible_string == "sifive,plic-1.0.0"sv) { - sifive_plic = true; - return IterationDecision::Break; - } - return IterationDecision::Continue; - }); - if (!sifive_plic) - continue; - - auto maybe_reg = node.get_property("reg"sv); - if (!maybe_reg.has_value()) { - dmesgln("Interrupts: Devicetree node for {} does not have a physical address assigned to it, rejecting", node_name); - continue; - } - auto reg = maybe_reg.value(); - auto stream = reg.as_stream(); - FlatPtr paddr; - if (soc_address_cells == 1) - paddr = MUST(stream.read_value>()); - else - paddr = MUST(stream.read_value>()); - size_t size; - if (soc_size_cells == 1) - size = MUST(stream.read_value>()); - else - size = MUST(stream.read_value>()); - auto max_interrupt_id = node.get_property("riscv,ndev"sv).value().as(); - m_interrupt_controllers.append(adopt_lock_ref(*new (nothrow) PLIC(PhysicalAddress(paddr), size, max_interrupt_id + 1))); - } - - if (interrupt_controllers_seen > 0 && m_interrupt_controllers.is_empty()) { - dmesgln("Interrupts: {} interrupt controllers seen, but none are compatible", interrupt_controllers_seen); - } -} - -u8 InterruptManagement::acquire_mapped_interrupt_number(u8 original_irq) -{ - return original_irq; -} - -Vector> const& InterruptManagement::controllers() -{ - return m_interrupt_controllers; -} - -NonnullLockRefPtr InterruptManagement::get_responsible_irq_controller(size_t) -{ - // TODO: Support more interrupt controllers - VERIFY(m_interrupt_controllers.size() == 1); - return m_interrupt_controllers[0]; -} - -void InterruptManagement::enumerate_interrupt_handlers(Function) -{ - TODO_RISCV64(); -} - -} diff --git a/Kernel/Arch/riscv64/InterruptManagement.h b/Kernel/Arch/riscv64/InterruptManagement.h deleted file mode 100644 index 3e0822df357..00000000000 --- a/Kernel/Arch/riscv64/InterruptManagement.h +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright (c) 2023, Sönke Holz - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include - -#include -VALIDATE_IS_RISCV64() - -namespace Kernel { - -class InterruptManagement { -public: - static InterruptManagement& the(); - static void initialize(); - static bool initialized(); - - static u8 acquire_mapped_interrupt_number(u8 original_irq); - - Vector> const& controllers(); - NonnullLockRefPtr get_responsible_irq_controller(size_t irq_number); - - void enumerate_interrupt_handlers(Function); - -private: - InterruptManagement() = default; - void find_controllers(); - - Vector> m_interrupt_controllers; -}; - -} diff --git a/Kernel/Arch/riscv64/Interrupts.cpp b/Kernel/Arch/riscv64/Interrupts.cpp deleted file mode 100644 index fb1ff7de624..00000000000 --- a/Kernel/Arch/riscv64/Interrupts.cpp +++ /dev/null @@ -1,253 +0,0 @@ -/* - * Copyright (c) 2023, Sönke Holz - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace Kernel { - -extern "C" void syscall_handler(TrapFrame const*); - -// FIXME: Share this array with x86_64/aarch64 somehow and consider if this really needs to use raw pointers and not OwnPtrs -static Array s_interrupt_handlers; - -void dump_registers(RegisterState const& regs) -{ - dbgln("scause: {} ({:p})", regs.scause, to_underlying(regs.scause)); - dbgln("sepc: {:p}", regs.sepc); - dbgln("stval: {:p}", regs.stval); - dbgln("sstatus: {}", regs.sstatus); - - dbgln("ra={:p} sp={:p} gp={:p} tp={:p} fp={:p}", regs.x[0], regs.x[1], regs.x[2], regs.x[3], regs.x[7]); - dbgln("a0={:p} a1={:p} a2={:p} a3={:p} a4={:p} a5={:p} a6={:p} a7={:p}", regs.x[9], regs.x[10], regs.x[11], regs.x[12], regs.x[13], regs.x[14], regs.x[15], regs.x[16]); - dbgln("t0={:p} t1={:p} t2={:p} t3={:p} t4={:p} t5={:p} t6={:p}", regs.x[4], regs.x[5], regs.x[6], regs.x[27], regs.x[28], regs.x[29], regs.x[30]); - dbgln("s1={:p} s2={:p} s3={:p} s4={:p} s5={:p} s6={:p} s7={:p} s8={:p} s9={:p} s10={:p} s11={:p}", regs.x[8], regs.x[17], regs.x[18], regs.x[19], regs.x[20], regs.x[21], regs.x[22], regs.x[23], regs.x[24], regs.x[25], regs.x[26]); -} - -extern "C" void trap_handler(TrapFrame& trap_frame); -extern "C" void trap_handler(TrapFrame& trap_frame) -{ - auto scause = trap_frame.regs->scause; - - // We have to increment sepc for these exceptions, as we otherwise would return to the instruction causing the trap. - // sepc has to be incremented before interrupts are re-enabled, as code triggered by interrupts also can cause sepc to be updated. - if (scause == RISCV64::CSR::SCAUSE::EnvironmentCallFromUMode) { - trap_frame.regs->sepc += 4; - } else if (scause == RISCV64::CSR::SCAUSE::Breakpoint) { - u32 break_instruction; - if (!copy_from_user(&break_instruction, bit_cast(trap_frame.regs->sepc), sizeof(break_instruction)).is_error()) { - // Increment sepc based on the instruction length of the breakpoint instruction. - if ((break_instruction & 0b11) == 0b11) - trap_frame.regs->sepc += 4; - else - trap_frame.regs->sepc += 2; - } - } - - if ((to_underlying(scause) & RISCV64::CSR::SCAUSE_INTERRUPT_MASK) != 0) { - // Interrupt - - Processor::current().enter_trap(trap_frame, true); - - if (scause == RISCV64::CSR::SCAUSE::SupervisorTimerInterrupt) { - RISCV64::Timer::the().handle_interrupt(*trap_frame.regs); - } else if (scause == RISCV64::CSR::SCAUSE::SupervisorExternalInterrupt) { - for (auto& interrupt_controller : InterruptManagement::the().controllers()) { - u8 pending_interrupt = 0; - while ((pending_interrupt = interrupt_controller->pending_interrupt())) { - auto* handler = s_interrupt_handlers[pending_interrupt]; - VERIFY(handler); - handler->increment_call_count(); - handler->handle_interrupt(*trap_frame.regs); - handler->eoi(); - } - } - } else { - TODO_RISCV64(); - } - - Processor::current().exit_trap(trap_frame); - } else { - // Exception - - Processor::current().enter_trap(trap_frame, false); - if (trap_frame.regs->sstatus.SPIE == 1) - Processor::enable_interrupts(); - - using enum RISCV64::CSR::SCAUSE; - switch (scause) { - case InstructionAddressMisaligned: - case LoadAddressMisaligned: - case StoreOrAMOAddressMisaligned: - handle_crash(*trap_frame.regs, "Unaligned memory access", SIGBUS, false); - break; - - case InstructionAccessFault: - case LoadAccessFault: - case StoreOrAMOAccessFault: - handle_crash(*trap_frame.regs, "Memory access fault", SIGBUS, false); - break; - - case IllegalInstrction: - handle_crash(*trap_frame.regs, "Illegal instruction", SIGILL, false); - break; - - case InstructionPageFault: - case LoadPageFault: - case StoreOrAMOPageFault: { - // The privileged ISA theoretically allows stval to always be zero (in which case we would report a page fault in the zero page). - // But all implementations capable of running general purpose operating systems should probably set this CSR, - // as otherwise you can't handle page faults. - // We simply require that Sstvala (see RISC-V Profiles) is supported, which means stval is always set to the faulting address on a page fault. - PageFault fault { VirtualAddress(trap_frame.regs->stval) }; - - if (scause == InstructionPageFault) - fault.set_instruction_fetch(true); - else if (scause == LoadPageFault) - fault.set_access(PageFault::Access::Read); - else if (scause == StoreOrAMOPageFault) - fault.set_access(PageFault::Access::Write); - - // RISC-V doesn't tell you the reason why a page fault occurred, so we don't use PageFault::set_type() here. - // The RISC-V implementation of Region::handle_fault() works without a correct PageFault::type(). - - fault.handle(*trap_frame.regs); - break; - } - - case EnvironmentCallFromUMode: - syscall_handler(&trap_frame); - break; - - case Breakpoint: - if (trap_frame.regs->previous_mode() == ExecutionMode::User) { - auto* current_thread = Thread::current(); - auto& current_process = current_thread->process(); - - if (auto* tracer = current_process.tracer()) - tracer->set_regs(*trap_frame.regs); - - current_thread->send_urgent_signal_to_self(SIGTRAP); - } else { - handle_crash(*trap_frame.regs, "Unexpected breakpoint trap", SIGTRAP, false); - } - break; - - default: - VERIFY_NOT_REACHED(); - }; - - Processor::disable_interrupts(); - Processor::current().exit_trap(trap_frame); - } -} - -// FIXME: Share the code below with Arch/x86_64/Interrupts.cpp -// While refactoring, the interrupt handlers can also be moved into the InterruptManagement class. -GenericInterruptHandler& get_interrupt_handler(u8) -{ - TODO_RISCV64(); -} - -// Sets the reserved flag on `number_of_irqs` if it finds unused interrupt handler on -// a contiguous range. -ErrorOr reserve_interrupt_handlers(u8) -{ - TODO_RISCV64(); -} - -static void revert_to_unused_handler(u8 interrupt_number) -{ - auto* handler = new UnhandledInterruptHandler(interrupt_number); - handler->register_interrupt_handler(); -} - -// FIXME: Share the code below with Arch/{x86_64,aarch64}/Interrupts.cpp -void register_generic_interrupt_handler(u8 interrupt_number, GenericInterruptHandler& handler) -{ - auto*& handler_slot = s_interrupt_handlers[interrupt_number]; - if (handler_slot == nullptr) { - handler_slot = &handler; - return; - } - if (handler_slot->type() == HandlerType::UnhandledInterruptHandler) { - auto* unhandled_handler = static_cast(handler_slot); - unhandled_handler->unregister_interrupt_handler(); - delete unhandled_handler; - handler_slot = &handler; - return; - } - if (handler_slot->is_shared_handler()) { - VERIFY(handler_slot->type() == HandlerType::SharedIRQHandler); - static_cast(handler_slot)->register_handler(handler); - return; - } - if (!handler_slot->is_shared_handler()) { - if (handler_slot->type() == HandlerType::SpuriousInterruptHandler) { - // FIXME: Add support for spurious interrupts on riscv64 - TODO_RISCV64(); - } - VERIFY(handler_slot->type() == HandlerType::IRQHandler); - auto& previous_handler = *handler_slot; - handler_slot = nullptr; - SharedIRQHandler::initialize(interrupt_number); - VERIFY(handler_slot); - static_cast(handler_slot)->register_handler(previous_handler); - static_cast(handler_slot)->register_handler(handler); - return; - } - VERIFY_NOT_REACHED(); -} - -// FIXME: Share the code below with Arch/{x86_64,aarch64}/Interrupts.cpp -void unregister_generic_interrupt_handler(u8 interrupt_number, GenericInterruptHandler& handler) -{ - auto*& handler_slot = s_interrupt_handlers[interrupt_number]; - VERIFY(handler_slot != nullptr); - if (handler_slot->type() == HandlerType::UnhandledInterruptHandler) - return; - if (handler_slot->is_shared_handler()) { - VERIFY(handler_slot->type() == HandlerType::SharedIRQHandler); - auto* shared_handler = static_cast(handler_slot); - shared_handler->unregister_handler(handler); - if (shared_handler->sharing_devices_count() == 0) { - handler_slot = nullptr; - revert_to_unused_handler(interrupt_number); - } - return; - } - if (!handler_slot->is_shared_handler()) { - VERIFY(handler_slot->type() == HandlerType::IRQHandler); - handler_slot = nullptr; - revert_to_unused_handler(interrupt_number); - return; - } -} - -void initialize_interrupts() -{ - for (size_t i = 0; i < s_interrupt_handlers.size(); ++i) { - auto* handler = new UnhandledInterruptHandler(i); - handler->register_interrupt_handler(); - } -} - -} diff --git a/Kernel/Arch/riscv64/Interrupts/PLIC.cpp b/Kernel/Arch/riscv64/Interrupts/PLIC.cpp deleted file mode 100644 index 6ca6abb1e60..00000000000 --- a/Kernel/Arch/riscv64/Interrupts/PLIC.cpp +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright (c) 2024, Idan Horowitz - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include - -namespace Kernel { - -UNMAP_AFTER_INIT PLIC::PLIC(PhysicalAddress address, size_t size, u32 interrupt_count) - : m_registers(Memory::map_typed(address, size, Memory::Region::Access::ReadWrite).release_value_but_fixme_should_propagate_errors()) - , m_interrupt_count(interrupt_count) -{ - VERIFY(m_interrupt_count < 256); // TODO: Serenity currently only supports up to 256 unique interrupts, but the PLIC supports up to 1024 - initialize(); -} - -UNMAP_AFTER_INIT void PLIC::initialize() -{ - for (auto i = 1u; i < m_interrupt_count; ++i) { - // Initialize all interrupt priorities to 1 (0 means never-interrupt) - m_registers->interrupt_priority[i] = 1; - } - for (auto i = 0u; i <= ((m_interrupt_count - 1) >> 5); ++i) { - // Initialize all interrupt sources to disabled - m_registers->interrupt_enable_bitmap[interrupt_context][i] = 0; - } - // Initialize priority-threshold to 0 (accept any interrupt of priority 1 or above) - m_registers->contexts[interrupt_context].priority_threshold = 0; - // Enable external interrupts in the current hart - RISCV64::CSR::set_bits(RISCV64::CSR::Address::SIE, 1 << (to_underlying(RISCV64::CSR::SCAUSE::SupervisorExternalInterrupt) & ~RISCV64::CSR::SCAUSE_INTERRUPT_MASK)); -} - -void PLIC::enable(GenericInterruptHandler const& handler) -{ - auto interrupt_number = handler.interrupt_number(); - VERIFY(interrupt_number > 0); // Interrupt number 0 is reserved to mean no-interrupt - m_registers->interrupt_enable_bitmap[interrupt_context][interrupt_number >> 5] |= 1u << (interrupt_number & 0x1F); -} - -void PLIC::disable(GenericInterruptHandler const& handler) -{ - auto interrupt_number = handler.interrupt_number(); - VERIFY(interrupt_number > 0); // Interrupt number 0 is reserved to mean no-interrupt - m_registers->interrupt_enable_bitmap[interrupt_context][interrupt_number >> 5] &= ~(1u << (interrupt_number & 0x1F)); -} - -void PLIC::eoi(GenericInterruptHandler const& handler) -{ - m_registers->contexts[interrupt_context].claim_complete = handler.interrupt_number(); -} - -u8 PLIC::pending_interrupt() const -{ - return m_registers->contexts[interrupt_context].claim_complete; -} - -} diff --git a/Kernel/Arch/riscv64/Interrupts/PLIC.h b/Kernel/Arch/riscv64/Interrupts/PLIC.h deleted file mode 100644 index 11f254a140f..00000000000 --- a/Kernel/Arch/riscv64/Interrupts/PLIC.h +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright (c) 2024, Idan Horowitz - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include - -namespace Kernel { - -class PLIC final : public IRQController { -public: - PLIC(PhysicalAddress, size_t size, u32 interrupt_count); - - virtual void enable(GenericInterruptHandler const&) override; - virtual void disable(GenericInterruptHandler const&) override; - - virtual void eoi(GenericInterruptHandler const&) override; - - virtual u8 pending_interrupt() const override; - - virtual StringView model() const override { return "PLIC"sv; } - -private: - enum { - M_MODE_CONTEXT = 0, - S_MODE_CONTEXT, - CONTEXTS_PER_HART, - }; - static constexpr size_t interrupt_context = (0 * CONTEXTS_PER_HART) + S_MODE_CONTEXT; // We assume we only have hart 0, change this once we support SMP - - struct RegisterMap { - u32 interrupt_priority[1024]; - u32 interrupt_pending_bitmap[32]; - u32 reserved1[992]; - u32 interrupt_enable_bitmap[15872][32]; - u32 reserved2[14336]; - struct { - u32 priority_threshold; - u32 claim_complete; - u32 reserved3[1022]; - } contexts[15872]; - }; - static_assert(AssertSize()); - - void initialize(); - - Memory::TypedMapping m_registers; - u32 m_interrupt_count { 0 }; -}; - -} diff --git a/Kernel/Arch/riscv64/MMU.cpp b/Kernel/Arch/riscv64/MMU.cpp deleted file mode 100644 index 131fb3c3576..00000000000 --- a/Kernel/Arch/riscv64/MMU.cpp +++ /dev/null @@ -1,269 +0,0 @@ -/* - * Copyright (c) 2023, Sönke Holz - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -// These come from the linker script -extern u8 page_tables_phys_start[]; -extern u8 page_tables_phys_end[]; -extern u8 start_of_kernel_image[]; -extern u8 end_of_kernel_image[]; - -namespace Kernel::Memory { - -class PageBumpAllocator { -public: - PageBumpAllocator(u64* start, u64 const* end) - : m_start(start) - , m_end(end) - , m_current(start) - { - if (m_start >= m_end) { - panic_without_mmu("Invalid memory range passed to PageBumpAllocator"sv); - } - if (bit_cast(m_start) % PAGE_TABLE_SIZE != 0 || bit_cast(m_end) % PAGE_TABLE_SIZE != 0) { - panic_without_mmu("Memory range passed into PageBumpAllocator not aligned to PAGE_TABLE_SIZE"sv); - } - } - - u64* take_page() - { - if (m_current >= m_end) { - panic_without_mmu("pre_init page table memory exhausted"sv); - } - - u64* page = m_current; - m_current += (PAGE_TABLE_SIZE / sizeof(FlatPtr)); - - // We can't use [__builtin_]memset here, as that would call into code which has stack protectors enabled, - // resulting in an access to an absolute address. - for (u64* p = page; p < page + (PAGE_TABLE_SIZE / sizeof(u64)); p++) - *p = 0; - - return page; - } - -private: - u64 const* m_start; - u64 const* m_end; - u64* m_current; -}; - -static UNMAP_AFTER_INIT FlatPtr calculate_physical_to_link_time_address_offset() -{ - FlatPtr physical_address; - FlatPtr link_time_address; - - asm volatile( - " lla %[physical_address], 1f\n" - "1: la %[link_time_address], 1b\n" - : [physical_address] "=r"(physical_address), - [link_time_address] "=r"(link_time_address)); - - return link_time_address - physical_address; -} - -template -inline T* adjust_by_mapping_base(T* ptr) -{ - return bit_cast(bit_cast(ptr) - calculate_physical_to_link_time_address_offset()); -} - -static UNMAP_AFTER_INIT bool page_table_entry_valid(u64 entry) -{ - return (entry & to_underlying(PageTableEntryBits::Valid)) != 0; -} - -static UNMAP_AFTER_INIT u64* insert_page_table(PageBumpAllocator& allocator, u64* root_table, VirtualAddress virtual_addr) -{ - size_t vpn_1 = (virtual_addr.get() >> VPN_1_OFFSET) & PAGE_TABLE_INDEX_MASK; - size_t vpn_2 = (virtual_addr.get() >> VPN_2_OFFSET) & PAGE_TABLE_INDEX_MASK; - - u64* level2_table = root_table; - - if (!page_table_entry_valid(level2_table[vpn_2])) { - level2_table[vpn_2] = (bit_cast(allocator.take_page()) >> PADDR_PPN_OFFSET) << PTE_PPN_OFFSET; - level2_table[vpn_2] |= to_underlying(PageTableEntryBits::Valid); - } - - u64* level1_table = bit_cast((level2_table[vpn_2] >> PTE_PPN_OFFSET) << PADDR_PPN_OFFSET); - - if (!page_table_entry_valid(level1_table[vpn_1])) { - level1_table[vpn_1] = (bit_cast(allocator.take_page()) >> PADDR_PPN_OFFSET) << PTE_PPN_OFFSET; - level1_table[vpn_1] |= to_underlying(PageTableEntryBits::Valid); - } - - return bit_cast((level1_table[vpn_1] >> PTE_PPN_OFFSET) << PADDR_PPN_OFFSET); -} - -static UNMAP_AFTER_INIT u64* get_page_directory(u64* root_table, VirtualAddress virtual_addr) -{ - size_t vpn_2 = (virtual_addr.get() >> VPN_2_OFFSET) & PAGE_TABLE_INDEX_MASK; - - u64* level2_table = root_table; - - if (!page_table_entry_valid(level2_table[vpn_2])) - return nullptr; - - return bit_cast((level2_table[vpn_2] >> PTE_PPN_OFFSET) << PADDR_PPN_OFFSET); -} - -static UNMAP_AFTER_INIT void insert_entry(PageBumpAllocator& allocator, u64* root_table, VirtualAddress vaddr, PhysicalAddress paddr, PageTableEntryBits flags) -{ - u64* level0_table = insert_page_table(allocator, root_table, vaddr); - - size_t vpn_0 = (vaddr.get() >> VPN_0_OFFSET) & PAGE_TABLE_INDEX_MASK; - level0_table[vpn_0] = (paddr.get() >> PADDR_PPN_OFFSET) << PTE_PPN_OFFSET; - level0_table[vpn_0] |= to_underlying(PageTableEntryBits::Valid | PageTableEntryBits::Accessed | PageTableEntryBits::Dirty | flags); -} - -static UNMAP_AFTER_INIT void insert_entries_for_memory_range(PageBumpAllocator& allocator, u64* root_table, VirtualAddress start, VirtualAddress end, PhysicalAddress paddr, PageTableEntryBits flags) -{ - // Not very efficient, but simple and it works. - for (VirtualAddress vaddr = start; vaddr < end;) { - insert_entry(allocator, root_table, vaddr, paddr, flags); - vaddr = vaddr.offset(PAGE_SIZE); - paddr = paddr.offset(PAGE_SIZE); - } -} - -static UNMAP_AFTER_INIT void setup_quickmap_page_table(PageBumpAllocator& allocator, u64* root_table) -{ - auto kernel_pt1024_base = VirtualAddress { *adjust_by_mapping_base(&kernel_mapping_base) + KERNEL_PT1024_OFFSET }; - - auto quickmap_page_table = PhysicalAddress { bit_cast(insert_page_table(allocator, root_table, kernel_pt1024_base)) }; - *adjust_by_mapping_base(&boot_pd_kernel_pt1023) = bit_cast(quickmap_page_table.offset(calculate_physical_to_link_time_address_offset()).get()); -} - -static UNMAP_AFTER_INIT void build_mappings(PageBumpAllocator& allocator, u64* root_table) -{ - auto start_of_kernel_range = VirtualAddress { bit_cast(+start_of_kernel_image) }; - auto end_of_kernel_range = VirtualAddress { bit_cast(+end_of_kernel_image) }; - - auto start_of_physical_kernel_range = PhysicalAddress { start_of_kernel_range.get() }.offset(-calculate_physical_to_link_time_address_offset()); - - // FIXME: dont map everything RWX - - // Map kernel into high virtual memory - insert_entries_for_memory_range(allocator, root_table, start_of_kernel_range, end_of_kernel_range, start_of_physical_kernel_range, PageTableEntryBits::Readable | PageTableEntryBits::Writeable | PageTableEntryBits::Executable); -} - -static UNMAP_AFTER_INIT void setup_kernel_page_directory(u64* root_table) -{ - auto kernel_page_directory = bit_cast(get_page_directory(root_table, VirtualAddress { *adjust_by_mapping_base(&kernel_mapping_base) })); - if (kernel_page_directory == 0) - panic_without_mmu("Could not find kernel page directory!"sv); - - *adjust_by_mapping_base(&boot_pd_kernel) = PhysicalAddress { kernel_page_directory }; - - // There is no level 4 table in Sv39 - *adjust_by_mapping_base(&boot_pml4t) = PhysicalAddress { 0 }; - - *adjust_by_mapping_base(&boot_pdpt) = PhysicalAddress { bit_cast(root_table) }; -} - -// This function has to fit into one page as it will be identity mapped. -[[gnu::aligned(PAGE_SIZE)]] [[noreturn]] UNMAP_AFTER_INIT static void enable_paging(BootInfo const& info, FlatPtr satp, u64* enable_paging_pte) -{ - // Switch current root page table to argument 1. This will immediately take effect, but we won't not crash as this function is identity mapped. - // Also, set up a temporary trap handler to catch any traps while switching page tables. - auto offset = calculate_physical_to_link_time_address_offset(); - register FlatPtr a0 asm("a0") = bit_cast(&info); - asm volatile( - " lla t0, 1f \n" - " csrw stvec, t0 \n" - - " csrw satp, %[satp] \n" - " sfence.vma \n" - - // Continue execution at high virtual address, by using an absolute jump. - " ld t0, 3f \n" - " jr t0 \n" - "3: .dword 4f \n" - "4: \n" - - // Add kernel_mapping_base to the stack pointer, such that it is also using the mapping in high virtual memory. - " add sp, sp, %[offset] \n" - - // Zero the PTE which identity maps this function - " add t0, %[offset], %[enable_paging_pte] \n" - " sd zero, (t0) \n" - " sfence.vma \n" - - " la t0, asm_trap_handler \n" - " csrw stvec, t0 \n" - - " li ra, 0 \n" - " li fp, 0 \n" - " tail init \n" - - ".p2align 2 \n" - "1: csrw sie, zero \n" - " wfi \n" - " j 1b \n" - : - : "r"(a0), [satp] "r"(satp), [offset] "r"(offset), [enable_paging_pte] "r"(enable_paging_pte) - : "t0"); - - VERIFY_NOT_REACHED(); -} - -[[noreturn]] UNMAP_AFTER_INIT void init_page_tables_and_jump_to_init(FlatPtr mhartid, PhysicalPtr fdt_phys_addr) -{ - if (RISCV64::CSR::SATP::read().MODE != RISCV64::CSR::SATP::Mode::Bare) - panic_without_mmu("Kernel booted with MMU enabled"sv); - - // Copy the FDT to a known location - DeviceTree::FlattenedDeviceTreeHeader* fdt_header = bit_cast(fdt_phys_addr); - u8* fdt_storage = bit_cast(fdt_phys_addr); - if (fdt_header->totalsize > fdt_storage_size) - panic_without_mmu("Passed FDT is bigger than the internal storage"sv); - for (size_t o = 0; o < fdt_header->totalsize; o += 1) { - // FIXME: Maybe increase the IO size here - adjust_by_mapping_base(s_fdt_storage)[o] = fdt_storage[o]; - } - - *adjust_by_mapping_base(&physical_to_virtual_offset) = calculate_physical_to_link_time_address_offset(); - *adjust_by_mapping_base(&kernel_mapping_base) = KERNEL_MAPPING_BASE; - *adjust_by_mapping_base(&kernel_load_base) = KERNEL_MAPPING_BASE; - - *adjust_by_mapping_base(&s_boot_info) = { .mhartid = mhartid, .fdt_phys_addr = fdt_phys_addr }; - - PageBumpAllocator allocator(adjust_by_mapping_base(reinterpret_cast(page_tables_phys_start)), adjust_by_mapping_base(reinterpret_cast(page_tables_phys_end))); - auto* root_table = allocator.take_page(); - build_mappings(allocator, root_table); - setup_quickmap_page_table(allocator, root_table); - setup_kernel_page_directory(root_table); - - // Identity map the `enable_paging` function and save the level 0 table address in order to remove the identity mapping in `enable_paging` again - auto const enable_paging_vaddr = VirtualAddress { bit_cast(&enable_paging) }; - auto const enable_paging_paddr = PhysicalAddress { bit_cast(&enable_paging) }; - - u64* enable_paging_level0_table = insert_page_table(allocator, root_table, enable_paging_vaddr); - - size_t enable_paging_vpn_0 = (enable_paging_vaddr.get() >> VPN_0_OFFSET) & PAGE_TABLE_INDEX_MASK; - enable_paging_level0_table[enable_paging_vpn_0] = (enable_paging_paddr.get() >> PADDR_PPN_OFFSET) << PTE_PPN_OFFSET; - enable_paging_level0_table[enable_paging_vpn_0] |= to_underlying(PageTableEntryBits::Valid | PageTableEntryBits::Accessed | PageTableEntryBits::Dirty | PageTableEntryBits::Readable | PageTableEntryBits::Executable); - - RISCV64::CSR::SATP satp = { - .PPN = bit_cast(root_table) >> PADDR_PPN_OFFSET, - .ASID = 0, - .MODE = RISCV64::CSR::SATP::Mode::Sv39, - }; - - enable_paging(s_boot_info, bit_cast(satp), &enable_paging_level0_table[enable_paging_vpn_0]); -} - -} diff --git a/Kernel/Arch/riscv64/MMU.h b/Kernel/Arch/riscv64/MMU.h deleted file mode 100644 index e5ac63eb863..00000000000 --- a/Kernel/Arch/riscv64/MMU.h +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Copyright (c) 2023, Sönke Holz - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include - -#include -VALIDATE_IS_RISCV64() - -namespace Kernel::Memory { - -[[noreturn]] void init_page_tables_and_jump_to_init(FlatPtr mhartid, PhysicalPtr fdt_phys_addr); - -} diff --git a/Kernel/Arch/riscv64/PCI/Initializer.cpp b/Kernel/Arch/riscv64/PCI/Initializer.cpp deleted file mode 100644 index 1de549c91c5..00000000000 --- a/Kernel/Arch/riscv64/PCI/Initializer.cpp +++ /dev/null @@ -1,288 +0,0 @@ -/* - * Copyright (c) 2023, Sönke Holz - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace Kernel::PCI { - -SetOnce g_pci_access_io_probe_failed; -SetOnce g_pci_access_is_disabled_from_commandline; - -void initialize() -{ - if (kernel_command_line().is_pci_disabled()) { - g_pci_access_is_disabled_from_commandline.set(); - return; - } - - new Access(); - - // [1]: https://github.com/devicetree-org/devicetree-specification/releases/download/v0.4/devicetree-specification-v0.4.pdf - // [2]: https://github.com/devicetree-org/dt-schema/blob/main/dtschema/schemas/pci/pci-bus-common.yaml - // [3]: https://github.com/devicetree-org/dt-schema/blob/main/dtschema/schemas/pci/pci-host-bridge.yaml - - // The pci controllers are usually in /soc/pcie?@XXXXXXXX - // FIXME: They can also appear in the root node, or any simple-bus other than soc - auto const& device_tree = DeviceTree::get(); - - auto maybe_soc = device_tree.get_child("soc"sv); - if (!maybe_soc.has_value()) { - dmesgln("PCI: No `soc` node found in the device tree, PCI initialization will be skipped"); - return; - } - - auto const& soc = maybe_soc.value(); - - enum class ControllerCompatible { - Unknown, - ECAM, - }; - - // These properties must be present - auto soc_address_cells = soc.get_property("#address-cells"sv).value().as(); - [[maybe_unused]] auto soc_size_cells = soc.get_property("#size-cells"sv).value().as(); - - Optional domain_counter; - FlatPtr pci_32bit_mmio_base = 0; - u32 pci_32bit_mmio_size = 0; - FlatPtr pci_64bit_mmio_base = 0; - u64 pci_64bit_mmio_size = 0; - HashMap masked_interrupt_mapping; - PCIInterruptSpecifier interrupt_mask; - for (auto const& [name, node] : soc.children()) { - if (!name.starts_with("pci"sv)) - continue; - - if (auto device_type = node.get_property("device_type"sv); !device_type.has_value() || device_type.value().as_string() != "pci"sv) { - // Technically, the device_type property is deprecated, but if it is present, - // no harm's done in checking it anyway - dmesgln("PCI: PCI named devicetree entry {} not a PCI type device, got device type '{}' instead", name, device_type.has_value() ? device_type.value().as_string() : ""sv); - continue; - } - - auto maybe_compatible = node.get_property("compatible"sv); - if (!maybe_compatible.has_value()) { - dmesgln("PCI: Devicetree node for {} does not have a 'compatible' string, rejecting", name); - continue; - } - auto compatible = maybe_compatible.value(); - auto controller_compatibility = ControllerCompatible::Unknown; - // Compatible strings are a list of strings; - // They should be sorted from most specific to least specific, - // so it's best to take the first one we recognize - compatible.for_each_string([&controller_compatibility](StringView compatible_string) -> IterationDecision { - if (compatible_string == "pci-host-ecam-generic"sv) { - controller_compatibility = ControllerCompatible::ECAM; - return IterationDecision::Break; - } - // FIXME: Implement CAM (pci-host-cam-generic), but maybe it's to old to be relevant - - return IterationDecision::Continue; - }); - if (controller_compatibility == ControllerCompatible::Unknown) { - dmesgln("PCI: Devicetree node for {} does not have a known 'compatible' string, rejecting", name); - dmesgln("PCI: Compatible strings provided: {}", compatible.as_strings()); - continue; - } - - auto maybe_reg = node.get_property("reg"sv); - if (!maybe_reg.has_value()) { - dmesgln("PCI: Devicetree node for {} does not have a physical address assigned to it, rejecting", name); - continue; - } - auto reg = maybe_reg.value(); - - Array bus_range { 0, 255 }; - auto maybe_bus_range = node.get_property("bus-range"sv); - if (maybe_bus_range.has_value()) { - auto provided_bus_range = maybe_bus_range.value().as, 2>>(); - // FIXME: Range check these - bus_range[0] = provided_bus_range[0]; - bus_range[1] = provided_bus_range[1]; - } - - u32 domain; - auto maybe_domain = node.get_property("linux,pci-domain"sv); - if (!maybe_domain.has_value()) { - // FIXME: Make a check similar to the domain counter check below - if (!domain_counter.has_value()) - domain_counter = 0; - domain = domain_counter.value(); - domain_counter = domain_counter.value() + 1; - } else { - if (domain_counter.has_value()) { - dmesgln("PCI: Devicetree node for {} has a PCI-domain assigned, but a previous controller did not have one assigned", name); - dmesgln("PCI: This could lead to domain collisions if handled improperly"); - dmesgln("PCI: We will for now reject this device for now, further investigation is advised"); - continue; - } - domain = maybe_domain.value().as(); - } - - switch (controller_compatibility) { - case ControllerCompatible::ECAM: { - // FIXME: Make this use a nice helper function - // FIXME: Use the provided size field - auto stream = reg.as_stream(); - FlatPtr paddr = MUST(stream.read_cells(soc_address_cells)); - - Access::the().add_host_controller( - MemoryBackedHostBridge::must_create( - Domain { - domain, - bus_range[0], - bus_range[1], - }, - PhysicalAddress { paddr })); - break; - } - case ControllerCompatible::Unknown: - VERIFY_NOT_REACHED(); // This should have been rejected earlier - } - - auto maybe_ranges = node.get_property("ranges"sv); - if (maybe_ranges.has_value()) { - auto address_cells = node.get_property("#address-cells"sv).value().as(); - VERIFY(address_cells == 3); // Additional cell for OpenFirmware PCI address metadata - auto size_cells = node.get_property("#size-cells"sv).value().as(); - - auto stream = maybe_ranges.value().as_stream(); - while (!stream.is_eof()) { - auto pci_address_metadata = bit_cast(MUST(stream.read_cell())); - FlatPtr pci_address = MUST(stream.read_cells(2)); - - FlatPtr mmio_address = MUST(stream.read_cells(soc_address_cells)); - u64 mmio_size = MUST(stream.read_cells(size_cells)); - - if (pci_address_metadata.space_type != OpenFirmwareAddress::SpaceType::Memory32BitSpace - && pci_address_metadata.space_type != OpenFirmwareAddress::SpaceType::Memory64BitSpace) - continue; // We currently only support memory-mapped PCI on RISC-V - - // TODO: Support mapped PCI addresses - VERIFY(pci_address == mmio_address); - if (pci_address_metadata.space_type == OpenFirmwareAddress::SpaceType::Memory32BitSpace) { - if (pci_address_metadata.prefetchable) - continue; // We currently only use non-prefetchable 32-bit regions, since 64-bit regions are always prefetchable - TODO: Use 32-bit prefetchable regions if only they are available - if (pci_32bit_mmio_size >= mmio_size) - continue; // We currently only use the single largest region - TODO: Use all available regions if needed - - pci_32bit_mmio_base = mmio_address; - pci_32bit_mmio_size = mmio_size; - } else { - if (pci_64bit_mmio_size >= mmio_size) - continue; // We currently only use the single largest region - TODO: Use all available regions if needed - - pci_64bit_mmio_base = mmio_address; - pci_64bit_mmio_size = mmio_size; - } - } - } - - // 2.4.3 Interrupt Nexus Properties - // #interrupt-cells: [2] `1` for pci busses - // interrupt-map: - // [{ - // child-unit-address(bus-node/#address-cells|3), - // child-interrupt-specifier(#interrupt-cells|1), - // interrupt-parent(phandle), - // parent-unit-address(interrupt-parent/#address-cells), - // parent-interrupt-specifier(interrupt-parent/#interrupt-cells) - // }] - // Note: The bus-node may be any other bus the child is connected to - // FIXME?: Let's just hope this is always this/a PCI bus - // interrupt-map-mask: - // > This property specifies a mask that is ANDed with the incoming - // > unit interrupt specifier being looked up in the table specified in the - // > interrupt-map property. - // Hence this should be of size: - // pci/#address-cells(3) + #interrupt-cells(1) = 4 - auto maybe_interrupt_map = node.get_property("interrupt-map"sv); - auto maybe_interrupt_map_mask = node.get_property("interrupt-map-mask"sv); - if (maybe_interrupt_map.has_value() && maybe_interrupt_map_mask.has_value()) { - VERIFY(node.get_property("#interrupt-cells"sv)->as() == 1); - VERIFY(maybe_interrupt_map_mask.value().size() == 4 * sizeof(u32)); - - auto mask_stream = maybe_interrupt_map_mask.value().as_stream(); - auto metadata_mask = bit_cast(MUST(mask_stream.read_cell())); - u64 phyical_address_mask = MUST(mask_stream.read_cells(2)); - // [2]: phys.mid and phys.lo mask should be 0 -> physical-address-mask = 0 - // 0 < metadata_mask < 0xff00 - VERIFY(phyical_address_mask == 0); - VERIFY(metadata_mask.raw <= 0xff00); - // Additionally it would be ludicrous/impossible to differentiate interrupts on registers - VERIFY(metadata_mask.register_ == 0); - - u32 pin_mask = MUST(mask_stream.read_cell()); - // [2]: The interrupt specifier mask should be between 0 and 7 - VERIFY(pin_mask <= 7); - - interrupt_mask = PCIInterruptSpecifier { - .interrupt_pin = static_cast(pin_mask), - .function = metadata_mask.function, - .device = metadata_mask.device, - .bus = metadata_mask.bus, - }; - auto map_stream = maybe_interrupt_map.value().as_stream(); - while (!map_stream.is_eof()) { - auto pci_address_metadata = bit_cast(MUST(map_stream.read_cell())); - MUST(map_stream.discard(sizeof(u32) * 2)); // Physical Address, the mask for those is guaranteed to be 0 - u32 pin = MUST(map_stream.read_cell()); - - u32 interrupt_controller_phandle = MUST(map_stream.read_cell()); - auto const* interrupt_controller = device_tree.phandle(interrupt_controller_phandle); - VERIFY(interrupt_controller); - - auto interrupt_cells = interrupt_controller->get_property("#interrupt-cells"sv)->as(); - VERIFY(interrupt_cells == 1 || interrupt_cells == 2); - u64 interrupt = MUST(map_stream.read_cells(interrupt_cells)); - - pin &= pin_mask; - pci_address_metadata.raw &= metadata_mask.raw; - masked_interrupt_mapping.set( - PCIInterruptSpecifier { - .interrupt_pin = static_cast(pin), - .function = pci_address_metadata.function, - .device = pci_address_metadata.device, - .bus = pci_address_metadata.bus, - }, - interrupt); - } - } - } - - if (pci_32bit_mmio_size != 0 || pci_64bit_mmio_size != 0) { - PCIConfiguration config { - pci_32bit_mmio_base, - pci_32bit_mmio_base + pci_32bit_mmio_size, - pci_64bit_mmio_base, - pci_64bit_mmio_base + pci_64bit_mmio_size, - move(masked_interrupt_mapping), - interrupt_mask, - }; - Access::the().configure_pci_space(config); - } else { - dmesgln("PCI: No MMIO ranges found - assuming pre-configured by bootloader"); - } - Access::the().rescan_hardware(); - - PCIBusSysFSDirectory::initialize(); - - // FIXME: X86_64 Reserves Interrupts here, maybe we need to do something like this here as well - - MUST(PCI::enumerate([&](DeviceIdentifier const& device_identifier) { - dmesgln("{} {}", device_identifier.address(), device_identifier.hardware_id()); - })); -} - -} diff --git a/Kernel/Arch/riscv64/PageDirectory.cpp b/Kernel/Arch/riscv64/PageDirectory.cpp deleted file mode 100644 index 4cdba33f602..00000000000 --- a/Kernel/Arch/riscv64/PageDirectory.cpp +++ /dev/null @@ -1,113 +0,0 @@ -/* - * Copyright (c) 2023, Sönke Holz - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include - -#include -#include -#include -#include -#include -#include - -namespace Kernel::Memory { - -struct SATPMap { - SpinlockProtected, LockRank::None> map {}; -}; - -static Singleton s_satp_map; - -void PageDirectory::register_page_directory(PageDirectory* page_directory) -{ - s_satp_map->map.with([&](auto& map) { - map.insert(bit_cast(page_directory->satp()), *page_directory); - }); -} - -void PageDirectory::deregister_page_directory(PageDirectory* page_directory) -{ - s_satp_map->map.with([&](auto& map) { - map.remove(bit_cast(page_directory->satp())); - }); -} - -ErrorOr> PageDirectory::try_create_for_userspace(Process& process) -{ - auto directory = TRY(adopt_nonnull_lock_ref_or_enomem(new (nothrow) PageDirectory)); - - directory->m_process = &process; - - directory->m_directory_table = TRY(MM.allocate_physical_page()); - auto kernel_pd_index = (kernel_mapping_base >> VPN_2_OFFSET) & PAGE_TABLE_INDEX_MASK; - for (size_t i = 0; i < kernel_pd_index; i++) { - directory->m_directory_pages[i] = TRY(MM.allocate_physical_page()); - } - - // Share the top 1 GiB of kernel-only mappings (>=kernel_mapping_base) - directory->m_directory_pages[kernel_pd_index] = MM.kernel_page_directory().m_directory_pages[kernel_pd_index]; - - { - InterruptDisabler disabler; - auto& table = *reinterpret_cast(MM.quickmap_page(*directory->m_directory_table)); - for (size_t i = 0; i < array_size(directory->m_directory_pages); i++) { - if (directory->m_directory_pages[i] != nullptr) { - table.raw[i] = ((directory->m_directory_pages[i]->paddr().get()) >> PADDR_PPN_OFFSET) << PTE_PPN_OFFSET; - table.raw[i] |= to_underlying(PageTableEntryBits::Valid); - } - } - MM.unquickmap_page(); - } - - register_page_directory(directory); - return directory; -} - -LockRefPtr PageDirectory::find_current() -{ - return s_satp_map->map.with([&](auto& map) { - return map.find(bit_cast(RISCV64::CSR::SATP::read())); - }); -} - -void activate_kernel_page_directory(PageDirectory const& page_directory) -{ - RISCV64::CSR::SATP::write(page_directory.satp()); - Processor::flush_entire_tlb_local(); -} - -void activate_page_directory(PageDirectory const& page_directory, Thread* thread) -{ - auto const satp = page_directory.satp(); - thread->regs().satp = satp; - RISCV64::CSR::SATP::write(satp); - Processor::flush_entire_tlb_local(); -} - -UNMAP_AFTER_INIT NonnullLockRefPtr PageDirectory::must_create_kernel_page_directory() -{ - return adopt_lock_ref_if_nonnull(new (nothrow) PageDirectory).release_nonnull(); -} - -PageDirectory::PageDirectory() = default; - -UNMAP_AFTER_INIT void PageDirectory::allocate_kernel_directory() -{ - dmesgln("MM: boot_pdpt @ {}", boot_pdpt); - dmesgln("MM: boot_pd_kernel @ {}", boot_pd_kernel); - m_directory_table = PhysicalRAMPage::create(boot_pdpt, MayReturnToFreeList::No); - m_directory_pages[(kernel_mapping_base >> VPN_2_OFFSET) & PAGE_TABLE_INDEX_MASK] = PhysicalRAMPage::create(boot_pd_kernel, MayReturnToFreeList::No); -} - -PageDirectory::~PageDirectory() -{ - if (is_root_table_initialized()) { - deregister_page_directory(this); - } -} - -} diff --git a/Kernel/Arch/riscv64/PageDirectory.h b/Kernel/Arch/riscv64/PageDirectory.h deleted file mode 100644 index 9970ce51bc8..00000000000 --- a/Kernel/Arch/riscv64/PageDirectory.h +++ /dev/null @@ -1,208 +0,0 @@ -/* - * Copyright (c) 2023, Sönke Holz - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include - -#include -#include -#include -#include - -#include -VALIDATE_IS_RISCV64() - -namespace Kernel::Memory { - -// Documentation for RISC-V Virtual Memory: -// The RISC-V Instruction Set Manual, Volume II: Privileged Architecture -// https://github.com/riscv/riscv-isa-manual/releases/download/Priv-v1.12/riscv-privileged-20211203.pdf - -// Currently, only the Sv39 (3 level paging) virtual memory system is implemented - -// Figure 4.19-4.21 -constexpr size_t PAGE_TABLE_SHIFT = 12; -constexpr size_t PAGE_TABLE_SIZE = 1LU << PAGE_TABLE_SHIFT; - -constexpr size_t PADDR_PPN_OFFSET = PAGE_TABLE_SHIFT; -constexpr size_t VADDR_VPN_OFFSET = PAGE_TABLE_SHIFT; -constexpr size_t PTE_PPN_OFFSET = 10; - -constexpr size_t PPN_SIZE = 26 + 9 + 9; -constexpr size_t VPN_SIZE = 9 + 9 + 9; - -constexpr size_t VPN_2_OFFSET = 30; -constexpr size_t VPN_1_OFFSET = 21; -constexpr size_t VPN_0_OFFSET = 12; - -constexpr size_t PPN_MASK = (1LU << PPN_SIZE) - 1; -constexpr size_t PTE_PPN_MASK = PPN_MASK << PTE_PPN_OFFSET; - -constexpr size_t PAGE_TABLE_INDEX_MASK = 0x1ff; - -enum class PageTableEntryBits { - Valid = 1 << 0, - Readable = 1 << 1, - Writeable = 1 << 2, - Executable = 1 << 3, - UserAllowed = 1 << 4, - Global = 1 << 5, - Accessed = 1 << 6, - Dirty = 1 << 7, -}; -AK_ENUM_BITWISE_OPERATORS(PageTableEntryBits); - -class PageDirectoryEntry { -public: - PhysicalPtr page_table_base() const { return (m_raw >> PTE_PPN_OFFSET) << PADDR_PPN_OFFSET; } - void set_page_table_base(PhysicalPtr value) - { - m_raw &= ~PTE_PPN_MASK; - m_raw |= (value >> PADDR_PPN_OFFSET) << PTE_PPN_OFFSET; - } - - void clear() { m_raw = 0; } - - bool is_present() const { return (m_raw & to_underlying(PageTableEntryBits::Valid)) != 0; } - void set_present(bool b) { set_bit(PageTableEntryBits::Valid, b); } - - bool is_user_allowed() const { TODO_RISCV64(); } - void set_user_allowed(bool) { } - - bool is_writable() const { TODO_RISCV64(); } - void set_writable(bool) { } - - bool is_global() const { TODO_RISCV64(); } - void set_global(bool) { } - -private: - void set_bit(PageTableEntryBits bit, bool value) - { - if (value) - m_raw |= to_underlying(bit); - else - m_raw &= ~to_underlying(bit); - } - - u64 m_raw; -}; - -class PageTableEntry { -public: - PhysicalPtr physical_page_base() const { return PhysicalAddress::physical_page_base(m_raw); } - void set_physical_page_base(PhysicalPtr value) - { - m_raw &= ~PTE_PPN_MASK; - m_raw |= (value >> PADDR_PPN_OFFSET) << PTE_PPN_OFFSET; - } - - bool is_present() const { return (m_raw & to_underlying(PageTableEntryBits::Valid)) != 0; } - void set_present(bool b) - { - set_bit(PageTableEntryBits::Valid, b); - set_bit(PageTableEntryBits::Readable, b); - - // Always set the A/D bits as we don't know if the hardware updates them automatically. - // If the hardware doesn't update them automatically they act like additional permission bits. - set_bit(PageTableEntryBits::Accessed, b); - set_bit(PageTableEntryBits::Dirty, b); - } - - bool is_user_allowed() const { TODO_RISCV64(); } - void set_user_allowed(bool b) { set_bit(PageTableEntryBits::UserAllowed, b); } - - bool is_writable() const { return (m_raw & to_underlying(PageTableEntryBits::Writeable)) != 0; } - void set_writable(bool b) { set_bit(PageTableEntryBits::Writeable, b); } - - bool is_cache_disabled() const { TODO_RISCV64(); } - void set_cache_disabled(bool) { } - - bool is_global() const { TODO_RISCV64(); } - void set_global(bool b) { set_bit(PageTableEntryBits::Global, b); } - - bool is_execute_disabled() const { TODO_RISCV64(); } - void set_execute_disabled(bool b) { set_bit(PageTableEntryBits::Executable, !b); } - - bool is_pat() const { TODO_RISCV64(); } - void set_pat(bool) { } - - bool is_null() const { return m_raw == 0; } - void clear() { m_raw = 0; } - -private: - void set_bit(PageTableEntryBits bit, bool value) - { - if (value) - m_raw |= to_underlying(bit); - else - m_raw &= ~to_underlying(bit); - } - - u64 m_raw; -}; - -class PageDirectoryPointerTable { -public: - PageDirectoryEntry* directory(size_t index) - { - VERIFY(index <= (NumericLimits::max() << 30)); - return (PageDirectoryEntry*)(PhysicalAddress::physical_page_base(raw[index])); - } - - u64 raw[512]; -}; - -class PageDirectory final : public AtomicRefCounted { - friend class MemoryManager; - -public: - static ErrorOr> try_create_for_userspace(Process&); - static NonnullLockRefPtr must_create_kernel_page_directory(); - static LockRefPtr find_current(); - - ~PageDirectory(); - - void allocate_kernel_directory(); - - RISCV64::CSR::SATP satp() const - { - return RISCV64::CSR::SATP { - .PPN = m_directory_table->paddr().get() >> PADDR_PPN_OFFSET, - .ASID = 0, - .MODE = RISCV64::CSR::SATP::Mode::Sv39, - }; - } - - bool is_root_table_initialized() const - { - return m_directory_table != nullptr; - } - - Process* process() { return m_process; } - - RecursiveSpinlock& get_lock() { return m_lock; } - - // This has to be public to let the global singleton access the member pointer - IntrusiveRedBlackTreeNode> m_tree_node; - -private: - PageDirectory(); - static void register_page_directory(PageDirectory* directory); - static void deregister_page_directory(PageDirectory* directory); - - Process* m_process { nullptr }; - RefPtr m_directory_table; - RefPtr m_directory_pages[512]; - RecursiveSpinlock m_lock {}; -}; - -void activate_kernel_page_directory(PageDirectory const& page_directory); -void activate_page_directory(PageDirectory const& page_directory, Thread* current_thread); - -} diff --git a/Kernel/Arch/riscv64/Panic.cpp b/Kernel/Arch/riscv64/Panic.cpp deleted file mode 100644 index 5b0ddc99c4f..00000000000 --- a/Kernel/Arch/riscv64/Panic.cpp +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright (c) 2022, Timon Kruiper - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include - -// FIXME: Merge the code in this file with Kernel/Library/Panic.cpp once the proper abstractions are in place. - -// Note: This is required here, since __assertion_failed should be out of the Kernel namespace, -// but the PANIC macro uses functions that require the Kernel namespace. -using namespace Kernel; - -[[noreturn]] void __assertion_failed(char const* msg, char const* file, unsigned line, char const* func) -{ - critical_dmesgln("ASSERTION FAILED: {}", msg); - critical_dmesgln("{}:{} in {}", file, line, func); - - // Used for printing a nice backtrace! - PANIC("Aborted"); -} diff --git a/Kernel/Arch/riscv64/PowerState.cpp b/Kernel/Arch/riscv64/PowerState.cpp deleted file mode 100644 index e7c339cc1f7..00000000000 --- a/Kernel/Arch/riscv64/PowerState.cpp +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright (c) 2023, Sönke Holz - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include - -namespace Kernel { - -void arch_specific_reboot() -{ - auto ret = SBI::SystemReset::system_reset(SBI::SystemReset::ResetType::ColdReboot, SBI::SystemReset::ResetReason::NoReason); - dbgln("SBI: Failed to reboot: {}", ret); - dbgln("SBI: Attempting to shut down using the legacy extension..."); - SBI::Legacy::shutdown(); -} - -void arch_specific_poweroff() -{ - auto ret = SBI::SystemReset::system_reset(SBI::SystemReset::ResetType::Shutdown, SBI::SystemReset::ResetReason::NoReason); - dbgln("SBI: Failed to shut down: {}", ret); - dbgln("SBI: Attempting to shut down using the legacy extension..."); - SBI::Legacy::shutdown(); -} - -} diff --git a/Kernel/Arch/riscv64/Processor.cpp b/Kernel/Arch/riscv64/Processor.cpp deleted file mode 100644 index d9043aae69d..00000000000 --- a/Kernel/Arch/riscv64/Processor.cpp +++ /dev/null @@ -1,567 +0,0 @@ -/* - * Copyright (c) 2023, Sönke Holz - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -namespace Kernel { - -Processor* g_current_processor; - -static void store_fpu_state(FPUState* fpu_state) -{ - asm volatile( - "fsd f0, 0*8(%0) \n" - "fsd f1, 1*8(%0) \n" - "fsd f2, 2*8(%0) \n" - "fsd f3, 3*8(%0) \n" - "fsd f4, 4*8(%0) \n" - "fsd f5, 5*8(%0) \n" - "fsd f6, 6*8(%0) \n" - "fsd f7, 7*8(%0) \n" - "fsd f8, 8*8(%0) \n" - "fsd f9, 9*8(%0) \n" - "fsd f10, 10*8(%0) \n" - "fsd f11, 11*8(%0) \n" - "fsd f12, 12*8(%0) \n" - "fsd f13, 13*8(%0) \n" - "fsd f14, 14*8(%0) \n" - "fsd f15, 15*8(%0) \n" - "fsd f16, 16*8(%0) \n" - "fsd f17, 17*8(%0) \n" - "fsd f18, 18*8(%0) \n" - "fsd f19, 19*8(%0) \n" - "fsd f20, 20*8(%0) \n" - "fsd f21, 21*8(%0) \n" - "fsd f22, 22*8(%0) \n" - "fsd f23, 23*8(%0) \n" - "fsd f24, 24*8(%0) \n" - "fsd f25, 25*8(%0) \n" - "fsd f26, 26*8(%0) \n" - "fsd f27, 27*8(%0) \n" - "fsd f28, 28*8(%0) \n" - "fsd f29, 29*8(%0) \n" - "fsd f30, 30*8(%0) \n" - "fsd f31, 31*8(%0) \n" - - "csrr t0, fcsr \n" - "sd t0, 32*8(%0) \n" ::"r"(fpu_state) - : "t0", "memory"); -} - -static void load_fpu_state(FPUState* fpu_state) -{ - asm volatile( - "fld f0, 0*8(%0) \n" - "fld f1, 1*8(%0) \n" - "fld f2, 2*8(%0) \n" - "fld f3, 3*8(%0) \n" - "fld f4, 4*8(%0) \n" - "fld f5, 5*8(%0) \n" - "fld f6, 6*8(%0) \n" - "fld f7, 7*8(%0) \n" - "fld f8, 8*8(%0) \n" - "fld f9, 9*8(%0) \n" - "fld f10, 10*8(%0) \n" - "fld f11, 11*8(%0) \n" - "fld f12, 12*8(%0) \n" - "fld f13, 13*8(%0) \n" - "fld f14, 14*8(%0) \n" - "fld f15, 15*8(%0) \n" - "fld f16, 16*8(%0) \n" - "fld f17, 17*8(%0) \n" - "fld f18, 18*8(%0) \n" - "fld f19, 19*8(%0) \n" - "fld f20, 20*8(%0) \n" - "fld f21, 21*8(%0) \n" - "fld f22, 22*8(%0) \n" - "fld f23, 23*8(%0) \n" - "fld f24, 24*8(%0) \n" - "fld f25, 25*8(%0) \n" - "fld f26, 26*8(%0) \n" - "fld f27, 27*8(%0) \n" - "fld f28, 28*8(%0) \n" - "fld f29, 29*8(%0) \n" - "fld f30, 30*8(%0) \n" - "fld f31, 31*8(%0) \n" - - "ld t0, 32*8(%0) \n" - "csrw fcsr, t0 \n" ::"r"(fpu_state) - : "t0", "memory"); -} - -template -void ProcessorBase::early_initialize(u32 cpu) -{ - VERIFY(g_current_processor == nullptr); - m_cpu = cpu; - - g_current_processor = static_cast(this); -} - -template -void ProcessorBase::initialize(u32) -{ - m_deferred_call_pool.init(); - - // FIXME: Actually set the correct count when we support SMP on riscv64. - g_total_processors.store(1, AK::MemoryOrder::memory_order_release); - - // Enable the FPU - auto sstatus = RISCV64::CSR::SSTATUS::read(); - sstatus.FS = RISCV64::CSR::SSTATUS::FloatingPointStatus::Initial; - RISCV64::CSR::SSTATUS::write(sstatus); - - store_fpu_state(&s_clean_fpu_state); - - initialize_interrupts(); -} - -template -[[noreturn]] void ProcessorBase::halt() -{ - // WFI ignores the value of sstatus.SIE, so we can't use disable_interrupts(). - // Instead, disable all interrupts sources by setting sie to zero. - RISCV64::CSR::write(RISCV64::CSR::Address::SIE, 0); - for (;;) - asm volatile("wfi"); -} - -template -void ProcessorBase::flush_tlb_local(VirtualAddress vaddr, size_t page_count) -{ - auto addr = vaddr.get(); - while (page_count > 0) { - asm volatile("sfence.vma %0" - : - : "r"(addr) - : "memory"); - addr += PAGE_SIZE; - page_count--; - } -} - -template -void ProcessorBase::flush_entire_tlb_local() -{ - asm volatile("sfence.vma"); -} - -template -void ProcessorBase::flush_tlb(Memory::PageDirectory const*, VirtualAddress vaddr, size_t page_count) -{ - // FIXME: Use the SBI RFENCE extension to flush the TLB of other harts when we support SMP on riscv64. - flush_tlb_local(vaddr, page_count); -} - -template -u32 ProcessorBase::clear_critical() -{ - InterruptDisabler disabler; - auto prev_critical = in_critical(); - auto& proc = current(); - proc.m_in_critical = 0; - if (proc.m_in_irq == 0) - proc.check_invoke_scheduler(); - return prev_critical; -} - -template -u32 ProcessorBase::smp_wake_n_idle_processors(u32) -{ - // FIXME: Actually wake up other cores when SMP is supported for riscv64. - return 0; -} - -template -void ProcessorBase::initialize_context_switching(Thread& initial_thread) -{ - VERIFY(initial_thread.process().is_kernel_process()); - - m_scheduler_initialized.set(); - - // FIXME: Figure out if we need to call {pre_,post_,}init_finished once riscv64 supports SMP - Processor::set_current_in_scheduler(true); - - auto& regs = initial_thread.regs(); - asm volatile( - "mv sp, %[new_sp] \n" - - "addi sp, sp, -32 \n" - "sd %[from_to_thread], 0(sp) \n" - "sd %[from_to_thread], 8(sp) \n" - - "jr %[new_ip] \n" ::[new_sp] "r"(regs.sp()), - [new_ip] "r"(regs.ip()), - [from_to_thread] "r"(&initial_thread) - : "t0"); - - VERIFY_NOT_REACHED(); -} - -template -void ProcessorBase::switch_context(Thread*& from_thread, Thread*& to_thread) -{ - VERIFY(!m_in_irq); - VERIFY(m_in_critical == 1); - - dbgln_if(CONTEXT_SWITCH_DEBUG, "switch_context --> switching out of: {} {}", VirtualAddress(from_thread), *from_thread); - - // m_in_critical is restored in enter_thread_context - from_thread->save_critical(m_in_critical); - - asm volatile( - // Store a RegisterState of from_thread on from_thread's stack - "addi sp, sp, -(34 * 8) \n" - - "sd x1, 0*8(sp) \n" - // sp - "sd x3, 2*8(sp) \n" - "sd x4, 3*8(sp) \n" - "sd x5, 4*8(sp) \n" - "sd x6, 5*8(sp) \n" - "sd x7, 6*8(sp) \n" - "sd x8, 7*8(sp) \n" - "sd x9, 8*8(sp) \n" - "sd x10, 9*8(sp) \n" - "sd x11, 10*8(sp) \n" - "sd x12, 11*8(sp) \n" - "sd x13, 12*8(sp) \n" - "sd x14, 13*8(sp) \n" - "sd x15, 14*8(sp) \n" - "sd x16, 15*8(sp) \n" - "sd x17, 16*8(sp) \n" - "sd x18, 17*8(sp) \n" - "sd x19, 18*8(sp) \n" - "sd x20, 19*8(sp) \n" - "sd x21, 20*8(sp) \n" - "sd x22, 21*8(sp) \n" - "sd x23, 22*8(sp) \n" - "sd x24, 23*8(sp) \n" - "sd x25, 24*8(sp) \n" - "sd x26, 25*8(sp) \n" - "sd x27, 26*8(sp) \n" - "sd x28, 27*8(sp) \n" - "sd x29, 28*8(sp) \n" - "sd x30, 29*8(sp) \n" - "sd x31, 30*8(sp) \n" - - // Store current sp as from_thread's sp. - "sd sp, %[from_sp] \n" - - // Set from_thread's pc to label "1" - "la t0, 1f \n" - "sd t0, %[from_ip] \n" - - // Switch to to_thread's stack - "ld sp, %[to_sp] \n" - - // Store from_thread, to_thread, to_ip on to_thread's stack - "addi sp, sp, -(4 * 8) \n" - "ld a0, %[from_thread] \n" - "sd a0, 0*8(sp) \n" - "ld a1, %[to_thread] \n" - "sd a1, 1*8(sp) \n" - "ld s1, %[to_ip] \n" - "sd s1, 2*8(sp) \n" - - // enter_thread_context(from_thread, to_thread) - "call enter_thread_context \n" - - // Jump to to_ip - "jr s1 \n" - - // A thread enters here when they were already scheduled at least once - "1: \n" - "addi sp, sp, (4 * 8) \n" - - // Restore the RegisterState of to_thread - "ld x1, 0*8(sp) \n" - // sp - "ld x3, 2*8(sp) \n" - "ld x4, 3*8(sp) \n" - "ld x5, 4*8(sp) \n" - "ld x6, 5*8(sp) \n" - "ld x7, 6*8(sp) \n" - "ld x8, 7*8(sp) \n" - "ld x9, 8*8(sp) \n" - "ld x10, 9*8(sp) \n" - "ld x11, 10*8(sp) \n" - "ld x12, 11*8(sp) \n" - "ld x13, 12*8(sp) \n" - "ld x14, 13*8(sp) \n" - "ld x15, 14*8(sp) \n" - "ld x16, 15*8(sp) \n" - "ld x17, 16*8(sp) \n" - "ld x18, 17*8(sp) \n" - "ld x19, 18*8(sp) \n" - "ld x20, 19*8(sp) \n" - "ld x21, 20*8(sp) \n" - "ld x22, 21*8(sp) \n" - "ld x23, 22*8(sp) \n" - "ld x24, 23*8(sp) \n" - "ld x25, 24*8(sp) \n" - "ld x26, 25*8(sp) \n" - "ld x27, 26*8(sp) \n" - "ld x28, 27*8(sp) \n" - "ld x29, 28*8(sp) \n" - "ld x30, 29*8(sp) \n" - "ld x31, 30*8(sp) \n" - - "addi sp, sp, -(4 * 8) \n" - "ld t0, 0*8(sp) \n" - "sd t0, %[from_thread] \n" - "ld t0, 1*8(sp) \n" - "sd t0, %[to_thread] \n" - - "addi sp, sp, (34 * 8) + (4 * 8) \n" - : - [from_ip] "=m"(from_thread->regs().pc), - [from_sp] "=m"(from_thread->regs().x[1]), - "=m"(from_thread), - "=m"(to_thread) - - : [to_ip] "m"(to_thread->regs().pc), - [to_sp] "m"(to_thread->regs().x[1]), - [from_thread] "m"(from_thread), - [to_thread] "m"(to_thread) - : "memory", "t0", "s1", "a0", "a1"); - - dbgln_if(CONTEXT_SWITCH_DEBUG, "switch_context <-- from {} {} to {} {}", VirtualAddress(from_thread), *from_thread, VirtualAddress(to_thread), *to_thread); -} - -extern "C" FlatPtr do_init_context(Thread* thread, u32 new_interrupts_state) -{ - VERIFY_INTERRUPTS_DISABLED(); - - thread->regs().sstatus.SPIE = (new_interrupts_state == to_underlying(InterruptsState::Enabled)); - - return Processor::current().init_context(*thread, true); -} - -template -void ProcessorBase::assume_context(Thread& thread, InterruptsState new_interrupts_state) -{ - dbgln_if(CONTEXT_SWITCH_DEBUG, "Assume context for thread {} {}", VirtualAddress(&thread), thread); - - VERIFY_INTERRUPTS_DISABLED(); - Scheduler::prepare_after_exec(); - // in_critical() should be 2 here. The critical section in Process::exec - // and then the scheduler lock - VERIFY(Processor::in_critical() == 2); - - do_assume_context(&thread, to_underlying(new_interrupts_state)); - - VERIFY_NOT_REACHED(); -} - -template -FlatPtr ProcessorBase::init_context(Thread& thread, bool leave_crit) -{ - VERIFY(g_scheduler_lock.is_locked()); - if (leave_crit) { - // Leave the critical section we set up in Process::exec, - // but because we still have the scheduler lock we should end up with 1 - VERIFY(in_critical() == 2); - m_in_critical = 1; // leave it without triggering anything or restoring flags - } - - u64 kernel_stack_top = thread.kernel_stack_top(); - - // Add a random offset between 0-256 (16-byte aligned) - kernel_stack_top -= round_up_to_power_of_two(get_fast_random(), 16); - - u64 stack_top = kernel_stack_top; - - auto& thread_regs = thread.regs(); - - // Push a RegisterState and TrapFrame onto the stack, which will be popped of the stack and restored into the - // state of the processor by restore_previous_context. - stack_top -= sizeof(RegisterState); - RegisterState& frame = *reinterpret_cast(stack_top); - memcpy(frame.x, thread_regs.x, sizeof(thread_regs.x)); - - // We don't overwrite the return address register if it's not 0, since that means this thread's register state was already initialized with - // an existing return address register value (e.g. it was fork()'ed), so we assume exit_kernel_thread is already saved as previous RA on the - // stack somewhere. - if (frame.x[0] == 0x0) { - // x1 is the return address register for the riscv64 ABI, so this will return to exit_kernel_thread when main thread function returns. - frame.x[0] = FlatPtr(&exit_kernel_thread); - } - frame.sepc = thread_regs.pc; - frame.set_userspace_sp(thread_regs.sp()); - frame.sstatus = thread_regs.sstatus; - - // Push a TrapFrame onto the stack - stack_top -= sizeof(TrapFrame); - TrapFrame& trap = *reinterpret_cast(stack_top); - trap.regs = &frame; - trap.next_trap = nullptr; - - if constexpr (CONTEXT_SWITCH_DEBUG) { - dbgln("init_context {} ({}) set up to execute at ip={}, sp={}, stack_top={}", - thread, - VirtualAddress(&thread), - VirtualAddress(thread_regs.pc), - VirtualAddress(thread_regs.sp()), - VirtualAddress(stack_top)); - } - - // This make sure the thread first executes thread_context_first_enter, which will actually call restore_previous_context - // which restores the context set up above. - thread_regs.set_sp(stack_top); - thread_regs.set_ip(FlatPtr(&thread_context_first_enter)); - - return stack_top; -} - -// FIXME: Figure out if we can fully share this code with x86. -template -void ProcessorBase::exit_trap(TrapFrame& trap) -{ - VERIFY_INTERRUPTS_DISABLED(); - VERIFY(&Processor::current() == this); - - // Temporarily enter a critical section. This is to prevent critical - // sections entered and left within e.g. smp_process_pending_messages - // to trigger a context switch while we're executing this function - // See the comment at the end of the function why we don't use - // ScopedCritical here. - m_in_critical = m_in_critical + 1; - - // FIXME: Figure out if we need prev_irq_level, see duplicated code in Kernel/Arch/x86/common/Processor.cpp - m_in_irq = 0; - - // Process the deferred call queue. Among other things, this ensures - // that any pending thread unblocks happen before we enter the scheduler. - m_deferred_call_pool.execute_pending(); - - auto* current_thread = Processor::current_thread(); - if (current_thread) { - auto& current_trap = current_thread->current_trap(); - current_trap = trap.next_trap; - ExecutionMode new_previous_mode; - if (current_trap) { - VERIFY(current_trap->regs); - new_previous_mode = current_trap->regs->previous_mode(); - } else { - // If we don't have a higher level trap then we're back in user mode. - // Which means that the previous mode prior to being back in user mode was kernel mode - new_previous_mode = ExecutionMode::Kernel; - } - - if (current_thread->set_previous_mode(new_previous_mode)) - current_thread->update_time_scheduled(TimeManagement::scheduler_current_time(), true, false); - } - - VERIFY_INTERRUPTS_DISABLED(); - - // Leave the critical section without actually enabling interrupts. - // We don't want context switches to happen until we're explicitly - // triggering a switch in check_invoke_scheduler. - m_in_critical = m_in_critical - 1; - if (!m_in_irq && !m_in_critical) - check_invoke_scheduler(); -} - -template -ErrorOr> ProcessorBase::capture_stack_trace(Thread&, size_t) -{ - dbgln("FIXME: Implement Processor::capture_stack_trace() for riscv64"); - return Vector {}; -} - -extern "C" void context_first_init(Thread* from_thread, Thread* to_thread); -extern "C" void context_first_init(Thread* from_thread, Thread* to_thread) -{ - do_context_first_init(from_thread, to_thread); -} - -extern "C" void enter_thread_context(Thread* from_thread, Thread* to_thread); -extern "C" void enter_thread_context(Thread* from_thread, Thread* to_thread) -{ - VERIFY(from_thread == to_thread || from_thread->state() != Thread::State::Running); - VERIFY(to_thread->state() == Thread::State::Running); - - Processor::set_current_thread(*to_thread); - - store_fpu_state(&from_thread->fpu_state()); - - auto& from_regs = from_thread->regs(); - auto& to_regs = to_thread->regs(); - if (from_regs.satp != to_regs.satp) { - RISCV64::CSR::SATP::write(to_regs.satp); - Processor::flush_entire_tlb_local(); - } - - to_thread->set_cpu(Processor::current().id()); - - auto in_critical = to_thread->saved_critical(); - VERIFY(in_critical > 0); - Processor::restore_critical(in_critical); - - load_fpu_state(&to_thread->fpu_state()); -} - -NAKED void thread_context_first_enter() -{ - asm( - "ld a0, 0(sp) \n" - "ld a1, 8(sp) \n" - "addi sp, sp, 32 \n" - "call context_first_init \n" - "mv a0, sp \n" - "call exit_trap \n" - "tail restore_context_and_sret \n"); -} - -NAKED void do_assume_context(Thread*, u32) -{ - // clang-format off - asm( - "mv s1, a0 \n" // save thread ptr - // We're going to call Processor::init_context, so just make sure - // we have enough stack space so we don't stomp over it - "addi sp, sp, -" __STRINGIFY(8 + REGISTER_STATE_SIZE + TRAP_FRAME_SIZE + 8) " \n" - "call do_init_context \n" - "mv sp, a0 \n" // move stack pointer to what Processor::init_context set up for us - "mv a0, s1 \n" // to_thread - "mv a1, s1 \n" // from_thread - "addi sp, sp, -32 \n" - "sd s1, 0(sp) \n" - "sd s1, 8(sp) \n" - "la ra, thread_context_first_enter \n" // should be same as regs.sepc - "tail enter_thread_context \n"); - // clang-format on -} - -template -StringView ProcessorBase::platform_string() -{ - return "riscv64"sv; -} - -template -void ProcessorBase::wait_for_interrupt() const -{ - asm("wfi"); -} - -template -Processor& ProcessorBase::by_id(u32) -{ - TODO_RISCV64(); -} - -} - -#include diff --git a/Kernel/Arch/riscv64/Processor.h b/Kernel/Arch/riscv64/Processor.h deleted file mode 100644 index 644847a6fa3..00000000000 --- a/Kernel/Arch/riscv64/Processor.h +++ /dev/null @@ -1,217 +0,0 @@ -/* - * Copyright (c) 2023, Sönke Holz - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include - -#include -#include -#include -#include -#include - -#include -VALIDATE_IS_RISCV64() - -namespace Kernel { - -namespace Memory { -class PageDirectory; -}; - -class Thread; -class Processor; -struct TrapFrame; -enum class InterruptsState; - -template -class ProcessorBase; - -// FIXME: Remove this once we support SMP in riscv64 -extern Processor* g_current_processor; - -constexpr size_t MAX_CPU_COUNT = 1; - -class Processor final : public ProcessorBase { -public: - template Callback> - static inline IterationDecision for_each(Callback callback) - { - // FIXME: Once we support SMP for riscv64, make sure to call the callback for every processor. - if (callback(*g_current_processor) == IterationDecision::Break) - return IterationDecision::Break; - return IterationDecision::Continue; - } - - template Callback> - static inline IterationDecision for_each(Callback callback) - { - // FIXME: Once we support SMP for riscv64, make sure to call the callback for every processor. - callback(*g_current_processor); - return IterationDecision::Continue; - } -}; - -template -ALWAYS_INLINE bool ProcessorBase::is_initialized() -{ - return g_current_processor != nullptr; -} - -template -ALWAYS_INLINE Thread* ProcessorBase::idle_thread() -{ - return current().m_idle_thread; -} - -template -ALWAYS_INLINE void ProcessorBase::set_current_thread(Thread& current_thread) -{ - current().m_current_thread = ¤t_thread; -} - -// FIXME: When riscv64 supports multiple cores, return the correct core id here. -template -ALWAYS_INLINE u32 ProcessorBase::current_id() -{ - return 0; -} - -template -ALWAYS_INLINE u32 ProcessorBase::in_critical() -{ - return current().m_in_critical; -} - -template -ALWAYS_INLINE void ProcessorBase::enter_critical() -{ - auto& current_processor = current(); - current_processor.m_in_critical += 1; -} - -template -ALWAYS_INLINE void ProcessorBase::restore_critical(u32 prev_critical) -{ - current().m_in_critical = prev_critical; -} - -template -ALWAYS_INLINE T& ProcessorBase::current() -{ - return *g_current_processor; -} - -template -void ProcessorBase::idle_begin() const -{ - // FIXME: Implement this when SMP for riscv64 is supported. -} - -template -void ProcessorBase::idle_end() const -{ - // FIXME: Implement this when SMP for riscv64 is supported. -} - -template -void ProcessorBase::smp_enable() -{ - // FIXME: Implement this when SMP for riscv64 is supported. -} - -template -bool ProcessorBase::is_smp_enabled() -{ - return false; -} - -template -ALWAYS_INLINE bool ProcessorBase::are_interrupts_enabled() -{ - return RISCV64::CSR::SSTATUS::read().SIE == 1; -} - -template -ALWAYS_INLINE void ProcessorBase::enable_interrupts() -{ - RISCV64::CSR::set_bits(RISCV64::CSR::Address::SSTATUS, 1 << to_underlying(RISCV64::CSR::SSTATUS::Offset::SIE)); -} - -template -ALWAYS_INLINE void ProcessorBase::disable_interrupts() -{ - RISCV64::CSR::clear_bits(RISCV64::CSR::Address::SSTATUS, 1 << to_underlying(RISCV64::CSR::SSTATUS::Offset::SIE)); -} - -template -ALWAYS_INLINE bool ProcessorBase::is_kernel_mode() -{ - // FIXME: Implement this correctly. - return true; -} - -template -ALWAYS_INLINE bool ProcessorBase::current_in_scheduler() -{ - return current().m_in_scheduler; -} - -template -ALWAYS_INLINE void ProcessorBase::set_current_in_scheduler(bool value) -{ - current().m_in_scheduler = value; -} - -template -ALWAYS_INLINE bool ProcessorBase::has_nx() const -{ - return true; -} - -template -ALWAYS_INLINE bool ProcessorBase::has_pat() const -{ - return false; -} - -template -ALWAYS_INLINE FlatPtr ProcessorBase::current_in_irq() -{ - return current().m_in_irq; -} - -template -ALWAYS_INLINE Thread* ProcessorBase::current_thread() -{ - return current().m_current_thread; -} - -template -ALWAYS_INLINE void ProcessorBase::pause() -{ - // FIXME: Use the pause instruction directly (via .option arch, +zihintpause) - // when we upgrade our toolchain to clang 17 - asm volatile(".word 0x0100000f"); -} - -template -ALWAYS_INLINE void ProcessorBase::wait_check() -{ - Processor::pause(); - // FIXME: Process SMP messages once we support SMP on riscv64; cf. x86_64 -} - -template -ALWAYS_INLINE u64 ProcessorBase::read_cpu_counter() -{ - return RISCV64::CSR::read(RISCV64::CSR::Address::CYCLE); -} - -} diff --git a/Kernel/Arch/riscv64/RegisterState.h b/Kernel/Arch/riscv64/RegisterState.h deleted file mode 100644 index 7398c377b91..00000000000 --- a/Kernel/Arch/riscv64/RegisterState.h +++ /dev/null @@ -1,85 +0,0 @@ -/* - * Copyright (c) 2023, Sönke Holz - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include - -#include -#include - -#include -#include - -VALIDATE_IS_RISCV64() - -namespace Kernel { - -struct alignas(16) RegisterState { - u64 x[31]; - - RISCV64::CSR::SSTATUS sstatus; - u64 sepc; - RISCV64::CSR::SCAUSE scause; - u64 stval; - - // x86_64 uses its additional RegisterState member "userspace_rsp" here, which is also invalid if no privilege mode change happened. - // On RISC-V, we only have one sp member, and regardless of the previous privilege mode, we always use this member here. - FlatPtr userspace_sp() const { return x[1]; } - void set_userspace_sp(FlatPtr value) { x[1] = value; } - - FlatPtr ip() const { return sepc; } - void set_ip(FlatPtr value) { sepc = value; } - - FlatPtr bp() const { return x[7]; } - void set_bp(FlatPtr value) { x[7] = value; } - - ExecutionMode previous_mode() const - { - switch (sstatus.SPP) { - case RISCV64::CSR::SSTATUS::PrivilegeMode::User: - return ExecutionMode::User; - case RISCV64::CSR::SSTATUS::PrivilegeMode::Supervisor: - return ExecutionMode::Kernel; - default: - VERIFY_NOT_REACHED(); - } - } - - void set_return_reg(FlatPtr value) { x[9] = value; } - void capture_syscall_params(FlatPtr& function, FlatPtr& arg1, FlatPtr& arg2, FlatPtr& arg3, FlatPtr& arg4) const - { - function = x[16]; - arg1 = x[9]; - arg2 = x[10]; - arg3 = x[11]; - arg4 = x[12]; - } -}; - -#define REGISTER_STATE_SIZE (36 * 8) -static_assert(AssertSize()); - -inline void copy_kernel_registers_into_ptrace_registers(PtraceRegisters& ptrace_regs, RegisterState const& kernel_regs) -{ - for (auto i = 0; i < 31; i++) - ptrace_regs.x[i] = kernel_regs.x[i]; - - ptrace_regs.pc = kernel_regs.ip(); -} - -inline void copy_ptrace_registers_into_kernel_registers(RegisterState& kernel_regs, PtraceRegisters const& ptrace_regs) -{ - for (auto i = 0; i < 31; i++) - kernel_regs.x[i] = ptrace_regs.x[i]; - - kernel_regs.set_ip(ptrace_regs.pc); -} - -struct DebugRegisterState { -}; - -} diff --git a/Kernel/Arch/riscv64/SBI.cpp b/Kernel/Arch/riscv64/SBI.cpp deleted file mode 100644 index d321647bf5e..00000000000 --- a/Kernel/Arch/riscv64/SBI.cpp +++ /dev/null @@ -1,204 +0,0 @@ -/* - * Copyright (c) 2023, Sönke Holz - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include - -#include - -namespace Kernel::SBI { - -static bool s_sbi_is_legacy = false; - -static SBIErrorOr sbi_ecall0(EID extension_id, u32 function_id) -{ - register unsigned long a0 asm("a0"); - register unsigned long a1 asm("a1"); - register unsigned long a6 asm("a6") = function_id; - register unsigned long a7 asm("a7") = to_underlying(extension_id); - asm volatile("ecall" - : "=r"(a0), "=r"(a1) - : "r"(a6), "r"(a7) - : "memory"); - if (a0 == to_underlying(SBIError::Success)) - return static_cast(a1); - - return static_cast(a0); -} - -static SBIErrorOr sbi_ecall1(EID extension_id, u32 function_id, unsigned long arg0) -{ - register unsigned long a0 asm("a0") = arg0; - register unsigned long a1 asm("a1"); - register unsigned long a6 asm("a6") = function_id; - register unsigned long a7 asm("a7") = to_underlying(extension_id); - asm volatile("ecall" - : "+r"(a0), "=r"(a1) - : "r"(a0), "r"(a6), "r"(a7) - : "memory"); - if (a0 == to_underlying(SBIError::Success)) - return static_cast(a1); - - return static_cast(a0); -} - -static SBIErrorOr sbi_ecall2(EID extension_id, u32 function_id, unsigned long arg0, unsigned long arg1) -{ - register unsigned long a0 asm("a0") = arg0; - register unsigned long a1 asm("a1") = arg1; - register unsigned long a6 asm("a6") = function_id; - register unsigned long a7 asm("a7") = to_underlying(extension_id); - asm volatile("ecall" - : "+r"(a0), "+r"(a1) - : "r"(a0), "r"(a1), "r"(a6), "r"(a7) - : "memory"); - if (a0 == to_underlying(SBIError::Success)) - return static_cast(a1); - - return static_cast(a0); -} - -namespace Base { - -SBIErrorOr get_spec_version() -{ - auto version = TRY(SBI::sbi_ecall0(EID::Base, to_underlying(FID::GetSpecVersion))); - return bit_cast(static_cast(version)); -} - -SBIErrorOr get_impl_id() -{ - return SBI::sbi_ecall0(EID::Base, to_underlying(FID::GetImplID)); -} - -SBIErrorOr get_impl_version() -{ - return SBI::sbi_ecall0(EID::Base, to_underlying(FID::GetImplVersion)); -} - -SBIErrorOr probe_extension(EID extension_id) -{ - return SBI::sbi_ecall1(EID::Base, to_underlying(FID::ProbeExtension), to_underlying(extension_id)); -} - -SBIErrorOr get_mvendorid() -{ - return SBI::sbi_ecall0(EID::Base, to_underlying(FID::GetMVENDORID)); -} - -SBIErrorOr get_marchid() -{ - return SBI::sbi_ecall0(EID::Base, to_underlying(FID::GetMARCHID)); -} - -SBIErrorOr get_mimpid() -{ - return SBI::sbi_ecall0(EID::Base, to_underlying(FID::GetMIMPID)); -} - -} - -namespace Legacy { - -static long sbi_legacy_ecall0(LegacyEID extension_id) -{ - register unsigned long a0 asm("a0"); - register unsigned long a7 asm("a7") = to_underlying(extension_id); - asm volatile("ecall" - : "=r"(a0) - : "r"(a7) - : "memory"); - return static_cast(a0); -} - -static long sbi_legacy_ecall1(LegacyEID extension_id, unsigned long arg0) -{ - register unsigned long a0 asm("a0") = arg0; - register unsigned long a7 asm("a7") = to_underlying(extension_id); - asm volatile("ecall" - : "+r"(a0) - : "r"(a0), "r"(a7) - : "memory"); - return static_cast(a0); -} - -LegacySBIErrorOr set_timer(u64 stime_value) -{ - auto err = sbi_legacy_ecall1(LegacyEID::SetTimer, stime_value); - if (err == 0) - return {}; - - return err; -} - -LegacySBIErrorOr console_putchar(int ch) -{ - auto err = sbi_legacy_ecall1(LegacyEID::ConsolePutchar, ch); - if (err == 0) - return {}; - - return err; -} - -void shutdown() -{ - sbi_legacy_ecall0(LegacyEID::SystemShutdown); - - VERIFY_NOT_REACHED(); -} - -} - -namespace Timer { - -SBIErrorOr set_timer(u64 stime_value) -{ - TRY(SBI::sbi_ecall1(EID::Timer, to_underlying(FID::SetTimer), stime_value)); - return {}; -} - -} - -namespace SystemReset { - -SBIError system_reset(ResetType reset_type, ResetReason reset_reason) -{ - auto const res = SBI::sbi_ecall2(EID::SystemReset, to_underlying(FID::SystemReset), to_underlying(reset_type), to_underlying(reset_reason)); - - // This SBI call shold only return if it didn't succeed - VERIFY(res.is_error()); - - return res.error(); -} - -} - -namespace DBCN { - -SBIErrorOr debug_console_write_byte(u8 byte) -{ - TRY(SBI::sbi_ecall1(EID::DebugConsole, to_underlying(FID::DebugConsoleWriteByte), byte)); - return {}; -} - -} - -void initialize() -{ - auto spec_version = Base::get_spec_version(); - if (spec_version.is_error()) { - s_sbi_is_legacy = true; - dbgln("SBI: Specification version: 0.1"); - } else { - dbgln("SBI: Specification version: {}", spec_version.value()); - dbgln("SBI: Implementation ID: {}", MUST(Base::get_impl_id())); - dbgln("SBI: Implementation version: {:#x}", MUST(Base::get_impl_version())); - dbgln("SBI: mvendorid: {:#x}", MUST(Base::get_mvendorid())); - dbgln("SBI: marchid: {:#x}", MUST(Base::get_marchid())); - dbgln("SBI: mimpid: {:#x}", MUST(Base::get_mimpid())); - } -} - -} diff --git a/Kernel/Arch/riscv64/SBI.h b/Kernel/Arch/riscv64/SBI.h deleted file mode 100644 index 827dea472fc..00000000000 --- a/Kernel/Arch/riscv64/SBI.h +++ /dev/null @@ -1,258 +0,0 @@ -/* - * Copyright (c) 2023, Sönke Holz - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include - -// Documentation about the SBI: -// RISC-V Supervisor Binary Interface Specification (https://github.com/riscv-non-isa/riscv-sbi-doc) - -namespace Kernel::SBI { - -// Chapter 3. Binary Encoding -enum class SBIError : long { - // SBI_SUCCESS: Completed successfully - Success = 0, - // SBI_ERR_FAILED: Failed - Failed = -1, - // SBI_ERR_NOT_SUPPORTED: Not supported - NotSupported = -2, - // SBI_ERR_INVALID_PARAM: Invalid parameter(s) - InvalidParam = -3, - // SBI_ERR_DENIED: Denied or not allowed - Denied = -4, - // SBI_ERR_INVALID_ADDRESS: Invalid address(s) - InvalidAddress = -5, - // SBI_ERR_ALREADY_AVAILABLE: Already available - AlreadyAvailable = -6, - // SBI_ERR_ALREADY_STARTED: Already started - AlreadyStarted = -7, - // SBI_ERR_ALREADY_STOPPED: Already stopped - AlreadyStopped = -8, - // SBI_ERR_NO_SHMEM: Shared memory not available - NoSHMEM = -9, -}; - -template -using SBIErrorOr = ErrorOr; - -enum class EID : i32 { - // Base Extension - Base = 0x10, - // Debug Console Extension ("DBCN") - DebugConsole = 0x4442434E, - // System Reset Extension ("SRST") - SystemReset = 0x53525354, - // Timer Extension ("TIME") - Timer = 0x54494D45, -}; - -// Chapter 4. Base Extension (EID #0x10) -// Required extension since SBI v0.2 -namespace Base { - -enum class FID : i32 { - GetSpecVersion = 0, - GetImplID = 1, - GetImplVersion = 2, - ProbeExtension = 3, - GetMVENDORID = 4, - GetMARCHID = 5, - GetMIMPID = 6, -}; - -struct SpecificationVersion { - u32 minor : 24; - u32 major : 7; - u32 reserved : 1; -}; -static_assert(AssertSize()); - -// Get SBI specification version (FID #0) -// Returns the current SBI specification version. This function must always succeed. -// The minor number of the SBI specification is encoded in the low 24 bits, -// with the major number encoded in the next 7 bits. Bit 31 must be 0 and is reserved for future expansion. -SBIErrorOr get_spec_version(); - -// Get SBI implementation ID (FID #1) -// Returns the current SBI implementation ID, which is different for every SBI implementation. It is -// intended that this implementation ID allows software to probe for SBI implementation quirks. -SBIErrorOr get_impl_id(); - -// Get SBI implementation version (FID #2) -// Returns the current SBI implementation version. The encoding of this version number is specific to -// the SBI implementation. -SBIErrorOr get_impl_version(); - -// Probe SBI extension (FID #3) -// Returns 0 if the given SBI extension ID (EID) is not available, or 1 if it is available unless defined as -// any other non-zero value by the implementation. -SBIErrorOr probe_extension(EID extension_id); - -// Get machine vendor ID (FID #4) -// Return a value that is legal for the mvendorid CSR and 0 is always a legal value for this CSR. -SBIErrorOr get_mvendorid(); - -// Get machine architecture ID (FID #5) -// Return a value that is legal for the marchid CSR and 0 is always a legal value for this CSR. -SBIErrorOr get_marchid(); - -// Get machine implementation ID (FID #6) -// Return a value that is legal for the mimpid CSR and 0 is always a legal value for this CSR. -SBIErrorOr get_mimpid(); - -} - -// Chapter 5. Legacy Extensions (EIDs #0x00 - #0x0F) -namespace Legacy { - -enum class LegacyEID : i32 { - SetTimer = 0, - ConsolePutchar = 1, - ConsoleGetchar = 2, - ClearIPI = 3, - SendIPI = 4, - RemoteFENCEI = 5, - RemoteSFENCEVMA = 6, - RemoteSFENCEVMAWithASID = 7, - SystemShutdown = 8, -}; - -template -using LegacySBIErrorOr = ErrorOr; - -// Set Timer (EID #0x00) -// Programs the clock for next event after stime_value time. This function also clears the pending -// timer interrupt bit. -LegacySBIErrorOr set_timer(u64 stime_value); - -// Console Putchar (EID #0x01) -// Write data present in ch to debug console. -LegacySBIErrorOr console_putchar(int ch); - -// System Shutdown (EID #0x08) -// Puts all the harts to shutdown state from supervisor point of view. -// This SBI call doesn’t return irrespective whether it succeeds or fails. -[[noreturn]] void shutdown(); - -} - -// Chapter 6. Timer Extension (EID #0x54494D45 "TIME") -// Since SBI v0.2 -namespace Timer { - -enum class FID : i32 { - SetTimer = 0, -}; - -// Set Timer (FID #0) -// Programs the clock for next event after stime_value time. stime_value is in absolute time. This -// function must clear the pending timer interrupt bit as well. -SBIErrorOr set_timer(u64 stime_value); - -} - -// Chapter 10. System Reset Extension (EID #0x53525354 "SRST") -// Since SBI v0.2 -namespace SystemReset { - -enum class FID : i32 { - SystemReset = 0, -}; - -enum class ResetType : u32 { - Shutdown = 0x0, - ColdReboot = 0x1, - WarmReboot = 0x2, -}; - -enum class ResetReason : u32 { - NoReason = 0x0, - SystemFailure = 0x1, -}; - -// System reset (FID #0) -// Reset the system based on provided reset_type and reset_reason. This is a synchronous call and -// does not return if it succeeds. -SBIError system_reset(ResetType reset_type, ResetReason reset_reason); - -} - -// Chapter 12. Debug Console Extension (EID #0x4442434E "DBCN") -// Since SBI v2.0 -namespace DBCN { - -enum class FID : i32 { - DebugConsoleWrite = 0, - DebugConsoleRead = 1, - DebugConsoleWriteByte = 2, -}; - -// Console Write Byte (FID #2) -// Write a single byte to the debug console. -SBIErrorOr debug_console_write_byte(u8 byte); - -} - -void initialize(); - -} - -template<> -struct AK::Formatter : Formatter { - ErrorOr format(FormatBuilder& builder, Kernel::SBI::SBIError error) - { - auto string = "Unknown error"sv; - - using enum Kernel::SBI::SBIError; - switch (error) { - case Success: - string = "Completed successfully"sv; - break; - case Failed: - string = "Failed"sv; - break; - case NotSupported: - string = "Not supported"sv; - break; - case InvalidParam: - string = "Invalid parameter(s)"sv; - break; - case Denied: - string = "Denied or not allowed"sv; - break; - case InvalidAddress: - string = "Invalid address(s)"sv; - break; - case AlreadyAvailable: - string = "Already available"sv; - break; - case AlreadyStarted: - string = "Already started"sv; - break; - case AlreadyStopped: - string = "Already stopped"sv; - break; - case NoSHMEM: - string = "Shared memory not available"sv; - break; - } - - return builder.put_string(string); - } -}; - -template<> -struct AK::Formatter : Formatter { - ErrorOr format(FormatBuilder& builder, Kernel::SBI::Base::SpecificationVersion const& version) - { - VERIFY(version.reserved == 0); - return Formatter::format(builder, "{}.{}"sv, version.major, version.minor); - } -}; diff --git a/Kernel/Arch/riscv64/SafeMem.cpp b/Kernel/Arch/riscv64/SafeMem.cpp deleted file mode 100644 index aea63c29b78..00000000000 --- a/Kernel/Arch/riscv64/SafeMem.cpp +++ /dev/null @@ -1,109 +0,0 @@ -/* - * Copyright (c) 2022, Timon Kruiper - * Copyright (c) 2023, Daniel Bertalan - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include - -#define CODE_SECTION(section_name) __attribute__((section(section_name))) - -extern "C" u8 start_of_safemem_text[]; -extern "C" u8 end_of_safemem_text[]; - -extern "C" u8 start_of_safemem_atomic_text[]; -extern "C" u8 end_of_safemem_atomic_text[]; - -namespace Kernel { - -CODE_SECTION(".text.safemem") -NEVER_INLINE FLATTEN bool safe_memset(void* dest_ptr, int c, size_t n, void*& fault_at) -{ - // FIXME: Actually implement a safe memset. - auto* dest = static_cast(dest_ptr); - for (; n--;) - *dest++ = c; - fault_at = nullptr; - return true; -} - -CODE_SECTION(".text.safemem") -NEVER_INLINE FLATTEN ssize_t safe_strnlen(char const* str, unsigned long max_n, void*& fault_at) -{ - // FIXME: Actually implement a safe strnlen. - size_t len = 0; - for (; len < max_n && *str; str++) - len++; - fault_at = nullptr; - return len; -} - -CODE_SECTION(".text.safemem") -NEVER_INLINE FLATTEN bool safe_memcpy(void* dest_ptr, void const* src_ptr, unsigned long n, void*& fault_at) -{ - // FIXME: Actually implement a safe memcpy. - auto* pd = static_cast(dest_ptr); - auto const* ps = static_cast(src_ptr); - for (; n--;) - *pd++ = *ps++; - fault_at = nullptr; - return true; -} - -CODE_SECTION(".text.safemem.atomic") -NEVER_INLINE FLATTEN Optional safe_atomic_compare_exchange_relaxed(u32 volatile* var, u32& expected, u32 val) -{ - // FIXME: Handle access faults. - return AK::atomic_compare_exchange_strong(var, expected, val, AK::memory_order_relaxed); -} - -CODE_SECTION(".text.safemem.atomic") -NEVER_INLINE FLATTEN Optional safe_atomic_load_relaxed(u32 volatile* var) -{ - // FIXME: Handle access faults. - return AK::atomic_load(var, AK::memory_order_relaxed); -} - -CODE_SECTION(".text.safemem.atomic") -NEVER_INLINE FLATTEN Optional safe_atomic_fetch_add_relaxed(u32 volatile* var, u32 val) -{ - // FIXME: Handle access faults. - return AK::atomic_fetch_add(var, val, AK::memory_order_relaxed); -} - -CODE_SECTION(".text.safemem.atomic") -NEVER_INLINE FLATTEN Optional safe_atomic_exchange_relaxed(u32 volatile* var, u32 val) -{ - // FIXME: Handle access faults. - return AK::atomic_exchange(var, val, AK::memory_order_relaxed); -} - -CODE_SECTION(".text.safemem.atomic") -NEVER_INLINE FLATTEN bool safe_atomic_store_relaxed(u32 volatile* var, u32 val) -{ - // FIXME: Handle access faults. - AK::atomic_store(var, val); - return true; -} - -bool handle_safe_access_fault(RegisterState& regs, FlatPtr fault_address) -{ - FlatPtr ip = regs.ip(); - - if (ip >= (FlatPtr)&start_of_safemem_text && ip < (FlatPtr)&end_of_safemem_text) { - dbgln("FIXME: Faulted while accessing userspace address {:p}.", fault_address); - dbgln(" We need to jump back into the appropriate SafeMem function, set fault_at and return failure."); - TODO_RISCV64(); - } else if (ip >= (FlatPtr)&start_of_safemem_atomic_text && ip < (FlatPtr)&end_of_safemem_atomic_text) { - dbgln("FIXME: Faulted while accessing userspace address {:p}.", fault_address); - dbgln(" We need to jump back into the appropriate atomic SafeMem function and return failure."); - TODO_RISCV64(); - } - - return false; -} - -} diff --git a/Kernel/Arch/riscv64/SmapDisabler.cpp b/Kernel/Arch/riscv64/SmapDisabler.cpp deleted file mode 100644 index 324ca3ed955..00000000000 --- a/Kernel/Arch/riscv64/SmapDisabler.cpp +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright (c) 2023, Sönke Holz - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include - -namespace Kernel { - -SmapDisabler::SmapDisabler() - : m_flags(RISCV64::CSR::read_and_set_bits(RISCV64::CSR::Address::SSTATUS, 1 << to_underlying(RISCV64::CSR::SSTATUS::Offset::SUM))) -{ -} - -SmapDisabler::~SmapDisabler() -{ - if ((m_flags & (1 << to_underlying(RISCV64::CSR::SSTATUS::Offset::SUM))) == 0) - RISCV64::CSR::clear_bits(RISCV64::CSR::Address::SSTATUS, 1 << to_underlying(RISCV64::CSR::SSTATUS::Offset::SUM)); -} - -} diff --git a/Kernel/Arch/riscv64/ThreadRegisters.h b/Kernel/Arch/riscv64/ThreadRegisters.h deleted file mode 100644 index 73fefc9a84e..00000000000 --- a/Kernel/Arch/riscv64/ThreadRegisters.h +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright (c) 2023, Sönke Holz - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include - -#include -VALIDATE_IS_RISCV64() - -namespace Kernel { - -struct ThreadRegisters { - u64 x[31]; - RISCV64::CSR::SSTATUS sstatus; - RISCV64::CSR::SATP satp; - u64 pc; - - u64 kernel_sp; - - FlatPtr ip() const { return pc; } - void set_ip(FlatPtr value) { pc = value; } - - FlatPtr sp() const { return x[1]; } - void set_sp(FlatPtr value) { x[1] = value; } - - void set_initial_state(bool is_kernel_process, Memory::AddressSpace& space, FlatPtr kernel_stack_top) - { - set_sp(kernel_stack_top); - satp = space.page_directory().satp(); - set_sstatus(is_kernel_process); - } - - void set_entry_function(FlatPtr entry_ip, FlatPtr entry_data) - { - set_ip(entry_ip); - x[9] = entry_data; // a0 - } - - void set_exec_state(FlatPtr entry_ip, FlatPtr userspace_sp, Memory::AddressSpace& space) - { - set_ip(entry_ip); - set_sp(userspace_sp); - satp = space.page_directory().satp(); - set_sstatus(false); - } - - void set_sstatus(bool is_kernel_process) - { - // Enable interrupts - sstatus.SPIE = 1; - - sstatus.FS = RISCV64::CSR::SSTATUS::FloatingPointStatus::Initial; - - sstatus.SPP = is_kernel_process ? RISCV64::CSR::SSTATUS::PrivilegeMode::Supervisor : RISCV64::CSR::SSTATUS::PrivilegeMode::User; - sstatus.UXL = RISCV64::CSR::SSTATUS::XLEN::Bits64; - } -}; - -} diff --git a/Kernel/Arch/riscv64/Timer.cpp b/Kernel/Arch/riscv64/Timer.cpp deleted file mode 100644 index 812d68d70d5..00000000000 --- a/Kernel/Arch/riscv64/Timer.cpp +++ /dev/null @@ -1,84 +0,0 @@ -/* - * Copyright (c) 2023, Sönke Holz - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include - -namespace Kernel::RISCV64 { - -static Timer* s_the; - -Timer::Timer() -{ - m_frequency = DeviceTree::get().resolve_property("/cpus/timebase-frequency"sv).value().as(); - - m_interrupt_interval = m_frequency / OPTIMAL_TICKS_PER_SECOND_RATE; - - set_compare(current_ticks() + m_interrupt_interval); - RISCV64::CSR::set_bits(RISCV64::CSR::Address::SIE, 1 << (to_underlying(CSR::SCAUSE::SupervisorTimerInterrupt) & ~CSR::SCAUSE_INTERRUPT_MASK)); -} - -NonnullLockRefPtr Timer::initialize() -{ - VERIFY(!s_the); - auto timer = adopt_lock_ref(*new Timer); - s_the = timer.ptr(); - return timer; -} - -Timer& Timer::the() -{ - VERIFY(s_the); - return *s_the; -} - -u64 Timer::current_ticks() -{ - return RISCV64::CSR::read(RISCV64::CSR::Address::TIME); -} - -void Timer::handle_interrupt(RegisterState const& regs) -{ - if (m_callback) - m_callback(regs); - set_compare(current_ticks() + m_interrupt_interval); -} - -u64 Timer::update_time(u64& seconds_since_boot, u32& ticks_this_second, bool query_only) -{ - // Should only be called by the time keeper interrupt handler! - u64 current_value = current_ticks(); - u64 delta_ticks = m_main_counter_drift; - if (current_value >= m_main_counter_last_read) { - delta_ticks += current_value - m_main_counter_last_read; - } else { - // the counter wrapped around - delta_ticks += (NumericLimits::max() - m_main_counter_last_read + 1) + current_value; - } - - u64 ticks_since_last_second = (u64)ticks_this_second + delta_ticks; - auto frequency = ticks_per_second(); - seconds_since_boot += ticks_since_last_second / frequency; - ticks_this_second = ticks_since_last_second % frequency; - - if (!query_only) { - m_main_counter_drift = 0; - m_main_counter_last_read = current_value; - } - - // Return the time passed (in ns) since last time update_time was called - return (delta_ticks * 1000000000ull) / frequency; -} - -void Timer::set_compare(u64 compare) -{ - if (SBI::Timer::set_timer(compare).is_error()) - MUST(SBI::Legacy::set_timer(compare)); -} - -} diff --git a/Kernel/Arch/riscv64/Timer.h b/Kernel/Arch/riscv64/Timer.h deleted file mode 100644 index 6fe1ccf6596..00000000000 --- a/Kernel/Arch/riscv64/Timer.h +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright (c) 2023, Sönke Holz - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include - -namespace Kernel::RISCV64 { - -class Timer final : public HardwareTimerBase { -public: - static NonnullLockRefPtr initialize(); - static Timer& the(); - - virtual HardwareTimerType timer_type() const override { return HardwareTimerType::RISCVTimer; } - virtual StringView model() const override { return "RISC-V Timer"sv; } - virtual size_t ticks_per_second() const override { return m_frequency; } - - virtual bool is_periodic() const override { TODO_RISCV64(); } - virtual bool is_periodic_capable() const override { TODO_RISCV64(); } - virtual void set_periodic() override { TODO_RISCV64(); } - virtual void set_non_periodic() override { TODO_RISCV64(); } - virtual void disable() override { TODO_RISCV64(); } - - virtual void reset_to_default_ticks_per_second() override { TODO_RISCV64(); } - virtual bool try_to_set_frequency(size_t) override { TODO_RISCV64(); } - virtual bool is_capable_of_frequency(size_t) const override { TODO_RISCV64(); } - virtual size_t calculate_nearest_possible_frequency(size_t) const override { TODO_RISCV64(); } - - virtual void will_be_destroyed() override { } - virtual Function set_callback(Function callback) override - { - auto previous_callback = move(m_callback); - m_callback = move(callback); - return previous_callback; - } - - // FIXME: Share code with HPET::update_time - u64 update_time(u64& seconds_since_boot, u32& ticks_this_second, bool query_only); - void handle_interrupt(RegisterState const&); - -private: - Timer(); - - static u64 current_ticks(); - static void set_compare(u64 compare); - - Function m_callback; - u64 m_frequency { 0 }; - u32 m_interrupt_interval { 0 }; - - u64 m_main_counter_last_read { 0 }; - u64 m_main_counter_drift { 0 }; -}; - -} diff --git a/Kernel/Arch/riscv64/TrapFrame.h b/Kernel/Arch/riscv64/TrapFrame.h deleted file mode 100644 index 6e01b3fe4c1..00000000000 --- a/Kernel/Arch/riscv64/TrapFrame.h +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright (c) 2023, Sönke Holz - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include - -#include -#include - -VALIDATE_IS_RISCV64() - -namespace Kernel { - -struct TrapFrame { - TrapFrame* next_trap; - RegisterState* regs; - - TrapFrame() = delete; - TrapFrame(TrapFrame const&) = delete; - TrapFrame(TrapFrame&&) = delete; - TrapFrame& operator=(TrapFrame const&) = delete; - TrapFrame& operator=(TrapFrame&&) = delete; -}; - -#define TRAP_FRAME_SIZE (2 * 8) -static_assert(AssertSize()); - -extern "C" void exit_trap(TrapFrame*) __attribute__((used)); - -} diff --git a/Kernel/Arch/riscv64/archctl.cpp b/Kernel/Arch/riscv64/archctl.cpp deleted file mode 100644 index 98b0eab4d19..00000000000 --- a/Kernel/Arch/riscv64/archctl.cpp +++ /dev/null @@ -1,20 +0,0 @@ -/* - * Copyright (c) 2024, Sönke Holz - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include - -namespace Kernel { - -ErrorOr Process::sys$archctl(int option, FlatPtr arg1) -{ - (void)option; - (void)arg1; - - VERIFY_NO_PROCESS_BIG_LOCK(this); - return ENOSYS; -} - -} diff --git a/Kernel/Arch/riscv64/boot.S b/Kernel/Arch/riscv64/boot.S deleted file mode 100644 index f3473b5a1bb..00000000000 --- a/Kernel/Arch/riscv64/boot.S +++ /dev/null @@ -1,100 +0,0 @@ -/* - * Copyright (c) 2023, Sönke Holz - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -// In a specially-named text section so that the linker script can put it first in .text. -.section ".text.first" - -// Make the kernel bootable using U-Boot's booti command by adding a linux header (https://www.kernel.org/doc/html/latest/arch/riscv/boot-image-header.html). -// Booting via booti causes U-Boot to pass us the boot hart ID and a flattened devicetree (https://www.kernel.org/doc/html/latest/arch/riscv/boot.html). -linux_header: -.option push -.option norvc - j start // u32 code0 - j start // u32 code1 -.option pop - - // This offset is needed, as otherwise U-Boot will try to load us at the same address where OpenSBI is loaded. - // The value is the same that Linux uses. - .dword 0x400000 // u64 text_offset - - .dword end_of_kernel_image - linux_header // u64 image_size - .dword 0 // u64 flags - .word 2 // u32 version - .word 0 // u32 res1 - .dword 0 // u64 res2 - .ascii "RISCV\0\0\0" // u64 magic (deprecated) - .ascii "RSC\x5" // u32 magic2 - .word 0 // u32 res3 - -.global start -.type start, @function -start: - // We expect that only one hart jumps here and that we are running in supervisor mode. - // We also expect that an implementation of the RISC-V Supervisor Binary Interface is available. - - // Don't touch a0/a1 as we expect those registers to contain the hart ID - // and a pointer to the Flattened Fevice Tree. - - // Clear sstatus.SIE, which disables all interrupts in supervisor mode. - csrci sstatus, 1 << 1 - - // Also, disable all interrupts sources and mark them as non-pending. - csrw sie, zero - csrw sip, zero - - // TODO: maybe load the gp register here? - - // Clear the BSS. - lla t0, start_of_bss - lla t1, end_of_bss - bgeu t0, t1, Lclear_bss_done -Lclear_bss_loop: - sd zero, (t0) - addi t0, t0, 8 - bltu t0, t1, Lclear_bss_loop -Lclear_bss_done: - - // Set the stack pointer register to the location defined in the linker script. - lla sp, end_of_initial_stack - - // Zero all registers except sp, a0 and a1. - li ra, 0 - // sp - li gp, 0 - li tp, 0 - li t0, 0 - li t1, 0 - li t2, 0 - li fp, 0 - li s1, 0 - // a0 - // a1 - li a2, 0 - li a3, 0 - li a4, 0 - li a5, 0 - li a6, 0 - li a7, 0 - li s2, 0 - li s3, 0 - li s4, 0 - li s5, 0 - li s6, 0 - li s7, 0 - li s8, 0 - li s9, 0 - li s10, 0 - li s11, 0 - li t3, 0 - li t4, 0 - li t5, 0 - li t6, 0 - - // The trap handler expects sscratch to be zero if we are in supervisor mode. - // sscratch contains the kernel stack pointer if we are in user mode. - csrw sscratch, zero - - tail pre_init diff --git a/Kernel/Arch/riscv64/linker.ld b/Kernel/Arch/riscv64/linker.ld deleted file mode 100644 index 6a3bc958341..00000000000 --- a/Kernel/Arch/riscv64/linker.ld +++ /dev/null @@ -1,119 +0,0 @@ -/* - * Copyright (c) 2023, Sönke Holz - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -ENTRY(start) - -KERNEL_MAPPING_BASE = 0x2000000000; - -/* TODO: Add FLAGS to the program headers */ -PHDRS -{ - text PT_LOAD ; - data PT_LOAD ; - ksyms PT_LOAD ; - bss PT_LOAD ; -} - -SECTIONS -{ - . = KERNEL_MAPPING_BASE; - - start_of_kernel_image = .; - - .text ALIGN(4K) : - { - start_of_kernel_text = .; - *(.text.first) - - start_of_safemem_text = .; - KEEP(*(.text.safemem)) - end_of_safemem_text = .; - start_of_safemem_atomic_text = .; - KEEP(*(.text.safemem.atomic)) - end_of_safemem_atomic_text = .; - - *(.text*) - } :text - - .driver_init ALIGN(4K) : AT (ADDR(.driver_init)) - { - driver_init_table_start = .; - *(.driver_init) - driver_init_table_end = .; - } :text - - .unmap_after_init ALIGN(4K) : - { - start_of_unmap_after_init = .; - *(.unmap_after_init*); - end_of_unmap_after_init = .; - - end_of_kernel_text = .; - } :text - - .rodata ALIGN(4K) : - { - start_heap_ctors = .; - *libkernel_heap.a:*(.init_array) - end_heap_ctors = .; - - start_ctors = .; - *(.init_array) - end_ctors = .; - - *(.srodata* .rodata*) - } :data - - .data ALIGN(4K) : - { - start_of_kernel_data = .; - *(.sdata* .data*) - end_of_kernel_data = .; - } :data - - .ro_after_init ALIGN(4K) : - { - start_of_ro_after_init = .; - *(.ro_after_init); - end_of_ro_after_init = .; - } :data - - .ksyms ALIGN(4K) : - { - start_of_kernel_ksyms = .; - *(.kernel_symbols) - end_of_kernel_ksyms = .; - } :ksyms - - .bss ALIGN(4K) (NOLOAD) : - { - start_of_bss = .; - *(.sbss* .bss*) - end_of_bss = .; - - . = ALIGN(4K); - *(.heap) - } :bss - - . = ALIGN(4K); - start_of_initial_stack = .; - - . += 32K; - end_of_initial_stack = .; - - /* - FIXME: 8MB is enough space for all of the tables required to identity map - physical memory. 8M is wasteful, so this should be properly calculated. - */ - - . = ALIGN(4K); - page_tables_phys_start = .; - - . += 8M; - page_tables_phys_end = .; - - end_of_kernel_image = .; -} diff --git a/Kernel/Arch/riscv64/mcontext.h b/Kernel/Arch/riscv64/mcontext.h deleted file mode 100644 index 19ee3c8840a..00000000000 --- a/Kernel/Arch/riscv64/mcontext.h +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Copyright (c) 2023, Sönke Holz - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include - -#ifdef __cplusplus -extern "C" { -#endif - -struct __attribute__((packed)) __mcontext { - uint64_t x[31]; - uint64_t pc; -}; - -#ifdef __cplusplus -} -#endif diff --git a/Kernel/Arch/riscv64/pre_init.cpp b/Kernel/Arch/riscv64/pre_init.cpp deleted file mode 100644 index 50255552b45..00000000000 --- a/Kernel/Arch/riscv64/pre_init.cpp +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright (c) 2023, Sönke Holz - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include - -#include -#include -#include -#include - -namespace Kernel { - -UNMAP_AFTER_INIT void dbgln_without_mmu(StringView message) -{ - auto probe_result = SBI::Base::probe_extension(SBI::EID::DebugConsole); - if (probe_result.is_error() || probe_result.value() == 0) { - for (auto const ch : message.bytes()) - (void)SBI::Legacy::console_putchar(ch); - (void)SBI::Legacy::console_putchar('\n'); - } else { - for (auto const ch : message.bytes()) - (void)SBI::DBCN::debug_console_write_byte(ch); - (void)SBI::DBCN::debug_console_write_byte('\n'); - } -} - -[[noreturn]] UNMAP_AFTER_INIT void panic_without_mmu(StringView message) -{ - dbgln_without_mmu("KERNEL PANIC in pre_init :^("sv); - dbgln_without_mmu(message); - - // We can't use Processor::halt() here, as that would result in an absolute jump. - RISCV64::CSR::write(RISCV64::CSR::Address::SIE, 0); - for (;;) - asm volatile("wfi"); -} - -[[gnu::aligned(4)]] [[noreturn]] UNMAP_AFTER_INIT static void early_trap_handler() -{ - panic_without_mmu("Unexpected trap"sv); -} - -extern "C" [[noreturn]] UNMAP_AFTER_INIT void pre_init(FlatPtr mhartid, PhysicalPtr fdt_phys_addr) -{ - - // Catch traps in pre_init - RISCV64::CSR::write(RISCV64::CSR::Address::STVEC, bit_cast(&early_trap_handler)); - - Memory::init_page_tables_and_jump_to_init(mhartid, fdt_phys_addr); -} - -} diff --git a/Kernel/Arch/riscv64/pre_init.h b/Kernel/Arch/riscv64/pre_init.h deleted file mode 100644 index d304c85a3dd..00000000000 --- a/Kernel/Arch/riscv64/pre_init.h +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Copyright (c) 2023, Sönke Holz - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include - -#include -VALIDATE_IS_RISCV64() - -namespace Kernel { - -void dbgln_without_mmu(StringView); -[[noreturn]] void panic_without_mmu(StringView); - -extern "C" [[noreturn]] void pre_init(FlatPtr mhartid, PhysicalPtr fdt_phys_addr); - -} diff --git a/Kernel/Arch/riscv64/trap_handler.S b/Kernel/Arch/riscv64/trap_handler.S deleted file mode 100644 index 42d24a31335..00000000000 --- a/Kernel/Arch/riscv64/trap_handler.S +++ /dev/null @@ -1,194 +0,0 @@ -/* - * Copyright (c) 2024, Sönke Holz - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -.section .text.asm_trap_handler - -#define REGISTER_STATE_SIZE (36 * 8) -#if REGISTER_STATE_SIZE % 16 != 0 -# error "REGISTER_STATE_SIZE is not a multiple of 16 bytes!" -#endif - -#define SSTATUS_SLOT (31 * 8) -#define SEPC_SLOT (32 * 8) -#define SCAUSE_SLOT (33 * 8) -#define STVAL_SLOT (34 * 8) - -.extern trap_handler - -.macro save_gpr_state_except_sp_on_stack - sd x1, 0*8(sp) - // sp - sd x3, 2*8(sp) - sd x4, 3*8(sp) - sd x5, 4*8(sp) - sd x6, 5*8(sp) - sd x7, 6*8(sp) - sd x8, 7*8(sp) - sd x9, 8*8(sp) - sd x10, 9*8(sp) - sd x11, 10*8(sp) - sd x12, 11*8(sp) - sd x13, 12*8(sp) - sd x14, 13*8(sp) - sd x15, 14*8(sp) - sd x16, 15*8(sp) - sd x17, 16*8(sp) - sd x18, 17*8(sp) - sd x19, 18*8(sp) - sd x20, 19*8(sp) - sd x21, 20*8(sp) - sd x22, 21*8(sp) - sd x23, 22*8(sp) - sd x24, 23*8(sp) - sd x25, 24*8(sp) - sd x26, 25*8(sp) - sd x27, 26*8(sp) - sd x28, 27*8(sp) - sd x29, 28*8(sp) - sd x30, 29*8(sp) - sd x31, 30*8(sp) -.endm - -.macro load_gpr_state_except_sp_from_stack - ld x1, 0*8(sp) - // sp - ld x3, 2*8(sp) - ld x4, 3*8(sp) - ld x5, 4*8(sp) - ld x6, 5*8(sp) - ld x7, 6*8(sp) - ld x8, 7*8(sp) - ld x9, 8*8(sp) - ld x10, 9*8(sp) - ld x11, 10*8(sp) - ld x12, 11*8(sp) - ld x13, 12*8(sp) - ld x14, 13*8(sp) - ld x15, 14*8(sp) - ld x16, 15*8(sp) - ld x17, 16*8(sp) - ld x18, 17*8(sp) - ld x19, 18*8(sp) - ld x20, 19*8(sp) - ld x21, 20*8(sp) - ld x22, 21*8(sp) - ld x23, 22*8(sp) - ld x24, 23*8(sp) - ld x25, 24*8(sp) - ld x26, 25*8(sp) - ld x27, 26*8(sp) - ld x28, 27*8(sp) - ld x29, 28*8(sp) - ld x30, 29*8(sp) - ld x31, 30*8(sp) -.endm - -.p2align 2 -.global asm_trap_handler -asm_trap_handler: - // We entered here from either the kernel or userland, - // so we have to find out if we came here from userland and if so, switch to the kernel stack. - - // Swap the contents of sscratch and sp. - csrrw sp, sscratch, sp - - // sp now contains the value of sscratch when we entered the trap handler. - // When this value is 0, we were already in supervisor (kernel) mode. - // Otherwise, the value in sp is now the kernel stack and sscratch contains the user stack pointer. - beqz sp, .Ltrap_is_from_kernel - - j .Ltrap_is_from_userland - -.Ltrap_is_from_kernel: - // Store 0 in sscratch and write the value inside sscratch (the kernel stack pointer) to sp. - csrrw sp, sscratch, zero - -.Ltrap_is_from_userland: - // sscratch now contains the user stack pointer, or 0 if the trap was from supervisor mode. - // sp points to the kernel stack. - - // Save the current register state on the kernel stack. - - // Allocate stack space for a RegisterState struct. - addi sp, sp, -REGISTER_STATE_SIZE - - save_gpr_state_except_sp_on_stack - - // Save some CSRs to correctly handle the trap. - csrr t0, sepc - sd t0, SEPC_SLOT(sp) - csrr t0, sstatus - sd t0, SSTATUS_SLOT(sp) - - // Also store these CSRs to be able to display the state of them before trap entry. - // We also might get an interrupt while handling page faults, so scause and stval would be changed by the interrupt. - csrr t0, scause - sd t0, SCAUSE_SLOT(sp) - csrr t0, stval - sd t0, STVAL_SLOT(sp) - - // Read the saved stack pointer from sscratch (which is 0 if the trap is from supervisor mode) - // and set sscratch to 0, as we are currently in the kernel. - csrrw t0, sscratch, zero - - // Save the user or kernel stack pointer in the RegisterState struct. - bnez t0, 1f - mv t0, sp -1: - sd t0, 1*8(sp) - - // Set up a TrapFrame struct on the stack. - mv t0, sp - addi sp, sp, -16 - sd zero, 0*8(sp) - sd t0, 1*8(sp) - - // Move the stack pointer into the first argument register - // and jump to the C++ trap handler. - mv a0, sp - - call trap_handler - -.global restore_context_and_sret -restore_context_and_sret: - - // Remove the TrapFrame from the stack. - addi sp, sp, 16 - - // Restore some CSRs first. - ld t0, SSTATUS_SLOT(sp) - csrw sstatus, t0 - ld t0, SEPC_SLOT(sp) - csrw sepc, t0 - - // Find out to which privilege mode we have to return to. - csrr t0, sstatus - srl t0, t0, 8 // SPP (previous privilege mode) - andi t0, t0, 1 - beqz t0, .Lreturn_to_user - - // Return to supervisor mode. - - csrw sscratch, zero - - load_gpr_state_except_sp_from_stack - - // Remove the RegisterState struct from the kernel stack. - addi sp, sp, REGISTER_STATE_SIZE - - sret - -.Lreturn_to_user: - // Store sp with the RegisterState struct removed to sscratch. - addi t0, sp, REGISTER_STATE_SIZE - csrw sscratch, t0 - - load_gpr_state_except_sp_from_stack - - // Load the user stack pointer from the RegisterState struct on the kernel stack. - ld sp, 1*8(sp) - - sret diff --git a/Kernel/Arch/x86_64/ASM_wrapper.cpp b/Kernel/Arch/x86_64/ASM_wrapper.cpp deleted file mode 100644 index e406f9910f6..00000000000 --- a/Kernel/Arch/x86_64/ASM_wrapper.cpp +++ /dev/null @@ -1,117 +0,0 @@ -/* - * Copyright (c) 2018-2021, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include - -#include -#include -#include - -namespace Kernel { - -#define XCR_XFEATURE_ENABLED_MASK 0 - -UNMAP_AFTER_INIT u64 read_xcr0() -{ - u32 eax, edx; - asm volatile("xgetbv" - : "=a"(eax), "=d"(edx) - : "c"(XCR_XFEATURE_ENABLED_MASK)); - return eax + ((u64)edx << 32); -} - -UNMAP_AFTER_INIT void write_xcr0(u64 value) -{ - u32 eax = value; - u32 edx = value >> 32; - asm volatile("xsetbv" ::"a"(eax), "d"(edx), "c"(XCR_XFEATURE_ENABLED_MASK)); -} - -void stac() -{ - if (!Processor::current().has_feature(CPUFeature::SMAP)) - return; - asm volatile("stac" :: - : "cc"); -} - -void clac() -{ - if (!Processor::current().has_feature(CPUFeature::SMAP)) - return; - asm volatile("clac" :: - : "cc"); -} - -UNMAP_AFTER_INIT void write_cr0(FlatPtr value) -{ - asm volatile("mov %%rax, %%cr0" ::"a"(value)); -} - -UNMAP_AFTER_INIT void write_cr4(FlatPtr value) -{ - asm volatile("mov %%rax, %%cr4" ::"a"(value)); -} -FlatPtr read_cr0() -{ - FlatPtr cr0; - asm("mov %%cr0, %%rax" - : "=a"(cr0)); - return cr0; -} - -FlatPtr read_cr2() -{ - FlatPtr cr2; - asm("mov %%cr2, %%rax" - : "=a"(cr2)); - return cr2; -} - -FlatPtr read_cr3() -{ - FlatPtr cr3; - asm("mov %%cr3, %%rax" - : "=a"(cr3)); - return cr3; -} - -void write_cr3(FlatPtr cr3) -{ - // NOTE: If you're here from a GPF crash, it's very likely that a PDPT entry is incorrect, not this! - asm volatile("mov %%rax, %%cr3" ::"a"(cr3) - : "memory"); -} - -FlatPtr read_cr4() -{ - FlatPtr cr4; - asm("mov %%cr4, %%rax" - : "=a"(cr4)); - return cr4; -} - -#define DEFINE_DEBUG_REGISTER(index) \ - FlatPtr read_dr##index() \ - { \ - FlatPtr value; \ - asm("mov %%dr" #index ", %%rax" \ - : "=a"(value)); \ - return value; \ - } \ - void write_dr##index(FlatPtr value) \ - { \ - asm volatile("mov %%rax, %%dr" #index ::"a"(value)); \ - } - -DEFINE_DEBUG_REGISTER(0); -DEFINE_DEBUG_REGISTER(1); -DEFINE_DEBUG_REGISTER(2); -DEFINE_DEBUG_REGISTER(3); -DEFINE_DEBUG_REGISTER(6); -DEFINE_DEBUG_REGISTER(7); - -} diff --git a/Kernel/Arch/x86_64/ASM_wrapper.h b/Kernel/Arch/x86_64/ASM_wrapper.h deleted file mode 100644 index 9fb013fcf67..00000000000 --- a/Kernel/Arch/x86_64/ASM_wrapper.h +++ /dev/null @@ -1,154 +0,0 @@ -/* - * Copyright (c) 2018-2021, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include - -#include -VALIDATE_IS_X86() - -namespace Kernel { - -ALWAYS_INLINE void cli() -{ - asm volatile("cli" :: - : "memory"); -} -ALWAYS_INLINE void sti() -{ - asm volatile("sti" :: - : "memory"); -} -ALWAYS_INLINE NO_SANITIZE_COVERAGE FlatPtr cpu_flags() -{ - FlatPtr flags; - asm volatile( - "pushf\n" - "pop %0\n" - : "=rm"(flags)::"memory"); - return flags; -} - -template -ALWAYS_INLINE T read_gs_value(FlatPtr offset) -{ - T val; - asm volatile( - "mov %%gs:%a[off], %[val]" - : [val] "=r"(val) - : [off] "ir"(offset)); - return val; -} - -template -ALWAYS_INLINE void write_gs_value(FlatPtr offset, T val) -{ - asm volatile( - "mov %[val], %%gs:%a[off]" ::[off] "ir"(offset), [val] "r"(val) - : "memory"); -} - -ALWAYS_INLINE NO_SANITIZE_COVERAGE FlatPtr read_gs_ptr(FlatPtr offset) -{ - FlatPtr val; - asm volatile( - "mov %%gs:%a[off], %[val]" - : [val] "=r"(val) - : [off] "ir"(offset)); - return val; -} - -ALWAYS_INLINE void write_gs_ptr(u32 offset, FlatPtr val) -{ - asm volatile( - "mov %[val], %%gs:%a[off]" ::[off] "ir"(offset), [val] "r"(val) - : "memory"); -} - -ALWAYS_INLINE NO_SANITIZE_COVERAGE bool are_interrupts_enabled() -{ - return (cpu_flags() & 0x200) != 0; -} - -FlatPtr read_cr0(); -FlatPtr read_cr2(); -FlatPtr read_cr3(); -FlatPtr read_cr4(); -u64 read_xcr0(); - -void write_cr0(FlatPtr); -void write_cr3(FlatPtr); -void write_cr4(FlatPtr); -void write_xcr0(u64); - -void flush_idt(); - -ALWAYS_INLINE void load_task_register(u16 selector) -{ - asm("ltr %0" ::"r"(selector)); -} - -FlatPtr read_dr0(); -void write_dr0(FlatPtr); -FlatPtr read_dr1(); -void write_dr1(FlatPtr); -FlatPtr read_dr2(); -void write_dr2(FlatPtr); -FlatPtr read_dr3(); -void write_dr3(FlatPtr); -FlatPtr read_dr6(); -void write_dr6(FlatPtr); -FlatPtr read_dr7(); -void write_dr7(FlatPtr); - -ALWAYS_INLINE void read_tsc(u32& lsw, u32& msw) -{ - asm volatile("rdtsc" - : "=d"(msw), "=a"(lsw)); -} - -ALWAYS_INLINE u64 read_tsc() -{ - u32 lsw; - u32 msw; - read_tsc(lsw, msw); - return ((u64)msw << 32) | lsw; -} - -ALWAYS_INLINE u32 read_rdrand() -{ - u32 value; - asm volatile( - "1:\n" - "rdrand %0\n" - "jnc 1b\n" - : "=r"(value)::"cc"); - return value; -} - -ALWAYS_INLINE u32 read_rdseed() -{ - u32 value; - asm volatile( - "1:\n" - "rdseed %0\n" - "jnc 1b\n" - : "=r"(value)::"cc"); - return value; -} - -void stac(); -void clac(); - -[[noreturn]] ALWAYS_INLINE void halt_this() -{ - for (;;) { - asm volatile("cli; hlt"); - } -} - -} diff --git a/Kernel/Arch/x86_64/ArchSpecificThreadData.h b/Kernel/Arch/x86_64/ArchSpecificThreadData.h deleted file mode 100644 index ffee9d5c8fd..00000000000 --- a/Kernel/Arch/x86_64/ArchSpecificThreadData.h +++ /dev/null @@ -1,17 +0,0 @@ -/* - * Copyright (c) 2024, Sönke Holz - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include - -namespace Kernel { - -struct ArchSpecificThreadData { - FlatPtr fs_base { 0 }; -}; - -} diff --git a/Kernel/Arch/x86_64/BochsDebugOutput.h b/Kernel/Arch/x86_64/BochsDebugOutput.h deleted file mode 100644 index 624b26dcb46..00000000000 --- a/Kernel/Arch/x86_64/BochsDebugOutput.h +++ /dev/null @@ -1,13 +0,0 @@ -/* - * Copyright (c) 2022, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -namespace Kernel { - -void bochs_debug_output(char ch); - -} diff --git a/Kernel/Arch/x86_64/Boot/ap_setup.S b/Kernel/Arch/x86_64/Boot/ap_setup.S deleted file mode 100644 index 3cb63554d44..00000000000 --- a/Kernel/Arch/x86_64/Boot/ap_setup.S +++ /dev/null @@ -1,230 +0,0 @@ -#include - -.section .text - -.global gdt64ptr -gdt64ptr: -.quad 0 - -.global code64_sel -code64_sel: -.short 0 - -/* - The apic_ap_start function will be loaded to P0x00008000 where the APIC - will boot the AP from in real mode. This code also contains space for - special variables that *must* remain here. When initializing the APIC, - the code here gets copied to P0x00008000, the variables in here get - populated and then the boot of the APs will be triggered. - Having the variables here allows us to access them from real mode. Also, the - code here avoids the need for relocation entries. - - Basically, the variables between apic_ap_start and end_apic_ap_start - *MUST* remain here and cannot be moved into a .bss or any other location. -*/ -.global apic_ap_start -.type apic_ap_start, @function -.align 8 -apic_ap_start: -.code16 - cli - jmp $0x800, $(1f - apic_ap_start) /* avoid relocation entries */ -1: - mov %cs, %ax - mov %ax, %ds - - xor %ax, %ax - mov %ax, %sp - - /* load the first temporary gdt */ - lgdt (ap_cpu_gdtr_initial - apic_ap_start) - - /* enable PM */ - movl %cr0, %eax - orl $1, %eax - movl %eax, %cr0 - - ljmpl $8, $(apic_ap_start32 - apic_ap_start + 0x8000) -apic_ap_start32: -.code32 - mov $0x10, %ax - mov %ax, %ss - mov %ax, %ds - mov %ax, %es - mov %ax, %fs - mov %ax, %gs - - movl $0x8000, %ebp - - /* generate a unique ap cpu id (0 means 1st ap, not bsp!) */ - xorl %eax, %eax - incl %eax - lock; xaddl %eax, (ap_cpu_id - apic_ap_start)(%ebp) /* avoid relocation entries */ - movl %eax, %esi - - /* check if we support NX and enable it if we do */ - movl $0x80000001, %eax - cpuid - testl $0x100000, %edx - je (1f - apic_ap_start + 0x8000) - /* turn on IA32_EFER.NXE */ - movl $0xc0000080, %ecx - rdmsr - orl $0x800, %eax - wrmsr -1: - - /* load the bsp's cr3 value */ - movl (ap_cpu_init_cr3 - apic_ap_start)(%ebp), %eax - movl %eax, %cr3 - - /* Enter Long-mode! ref(https://wiki.osdev.org/Setting_Up_Long_Mode)*/ - mov $0xC0000080, %ecx /* Set the C-register to 0xC0000080, which is the EFER MSR.*/ - rdmsr /* Read from the model-specific register.*/ - or $(1 << 8), %eax /* Set the LM-bit which is the 9th bit (bit 8).*/ - wrmsr /* Write to the model-specific register.*/ - - /* enable PAE + PSE */ - movl %cr4, %eax - orl $0x60, %eax - movl %eax, %cr4 - - /* enable PG */ - movl %cr0, %eax - orl $0x80000000, %eax - movl %eax, %cr0 - - /* load the temporary 64-bit gdt from boot that points above 3GB */ - lgdt (ap_cpu_gdt64ptr - apic_ap_start + 0x8000) - - /* Jump into our identity mapped area, stay in low memory for now. - We need to fully enable 64 bit mode before we can adjust rsp and rip - to values higher than 4GB */ - ljmpl $(ap_cpu_gdt64code - ap_cpu_gdt64), $(apic_ap_start64 - apic_ap_start + 0x8000) -.code64 -apic_ap_start64: - movq (ap_cpu_kernel_map_base - apic_ap_start)(%rbp), %rbp - addq $0x8000, %rbp - - /* find our allocated stack based on the generated id */ - movq (ap_cpu_init_stacks - apic_ap_start)(%rbp, %rsi, 8), %rsp - - /* Now jump from our identity mapped area into high memory */ - movq $(1f - apic_ap_start), %rax - addq %rbp, %rax - jmp *%rax -1: - mov $0, %ax - mov %ax, %ss - mov %ax, %ds - mov %ax, %es - mov %ax, %fs - mov %ax, %gs - - /* flush the TLB */ - movq %cr3, %rax - movq %rax, %cr3 - - /* now load the final gdt and idt from the identity mapped area */ - movq (ap_cpu_gdtr - apic_ap_start)(%rbp), %rax - lgdt (%rax) - movq (ap_cpu_idtr - apic_ap_start)(%rbp), %rax - lidt (%rax) - - /* set same cr0 and cr4 values as the BSP */ - movq (ap_cpu_init_cr0 - apic_ap_start)(%rbp), %rax - movq %rax, %cr0 - movq (ap_cpu_init_cr4 - apic_ap_start)(%rbp), %rax - movq %rax, %cr4 - - - /* Save the cpu id into rdi (first argument), 0 representing the bsp */ - movq %rsi, %rdi - incq %rdi - - /* Save the Processor pointer this CPU is going to use into rsi (second argument) */ - movq (ap_cpu_init_processor_info_array - apic_ap_start)(%rbp), %rax - movq 0(%rax, %rsi, 8), %rsi - - /* Get the entry function */ - movq (ap_cpu_kernel_entry_function - apic_ap_start)(%rbp), %r10 - - movq %rsp, %rbp - cld - - /* We are in identity mapped P0x8000 and the BSP will unload this code - once all APs are initialized, so call the entry function and return to our - infinite loop if it ever were to return. */ - call *%r10 - -loop: - hlt - jmp loop - -.align 4 -.global apic_ap_start_size -apic_ap_start_size: - .2byte end_apic_ap_start - apic_ap_start -.align 4 -ap_cpu_id: - .4byte 0x0 -ap_cpu_gdt: - /* null */ - .8byte 0x0 - /* code */ - .4byte 0x0000FFFF - .4byte 0x00cf9a00 - /* data */ - .4byte 0x0000FFFF - .4byte 0x00cf9200 -ap_cpu_gdt_end: -ap_cpu_gdtr_initial: - .2byte ap_cpu_gdt_end - ap_cpu_gdt - 1 - .4byte (ap_cpu_gdt - apic_ap_start) + 0x8000 -.align 8 -.global ap_cpu_gdtr -ap_cpu_gdtr: - .8byte 0x0 /* will be set at runtime */ -.global ap_cpu_idtr -ap_cpu_idtr: - .8byte 0x0 /* will be set at runtime */ -.global ap_cpu_init_cr0 -ap_cpu_init_cr0: - .8byte 0x0 /* will be set at runtime */ -.global ap_cpu_init_cr3 -ap_cpu_init_cr3: - .8byte 0x0 /* will be set at runtime */ -.global ap_cpu_init_cr4 -ap_cpu_init_cr4: - .8byte 0x0 /* will be set at runtime */ -.global ap_cpu_gdt64 -ap_cpu_gdt64: - .8byte 0x0 -.global ap_cpu_gdt64code -ap_cpu_gdt64code: - .4byte 0xffff - .4byte 0xaf9a00 -.global ap_cpu_gdt64data -ap_cpu_gdt64data: - .4byte 0xffff - .4byte 0xaf9200 -.global ap_cpu_gdt64ptr -ap_cpu_gdt64ptr: - .2byte ap_cpu_gdt64ptr - ap_cpu_gdt64 - 1 - .8byte (ap_cpu_gdt64 - apic_ap_start) + 0x8000 -.align 8 -.global ap_cpu_kernel_entry_function -ap_cpu_kernel_entry_function: - .8byte 0x0 /* will be set at runtime */ -.global ap_cpu_kernel_map_base -ap_cpu_kernel_map_base: - .8byte 0x0 /* will be set at runtime */ -.global ap_cpu_init_processor_info_array -ap_cpu_init_processor_info_array: - .8byte 0x0 /* will be set at runtime */ -.global ap_cpu_init_stacks -ap_cpu_init_stacks: - /* array of allocated stack pointers */ - /* NOTE: ap_cpu_init_stacks must be the last variable before - end_apic_ap_start! */ -.set end_apic_ap_start, . diff --git a/Kernel/Arch/x86_64/CMOS.cpp b/Kernel/Arch/x86_64/CMOS.cpp deleted file mode 100644 index 310fd87e295..00000000000 --- a/Kernel/Arch/x86_64/CMOS.cpp +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include - -namespace Kernel::CMOS { - -u8 read(u8 index) -{ - IO::out8(0x70, index); - return IO::in8(0x71); -} - -void write(u8 index, u8 data) -{ - IO::out8(0x70, index); - IO::out8(0x71, data); -} - -} diff --git a/Kernel/Arch/x86_64/CMOS.h b/Kernel/Arch/x86_64/CMOS.h deleted file mode 100644 index fc3616da247..00000000000 --- a/Kernel/Arch/x86_64/CMOS.h +++ /dev/null @@ -1,16 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include - -namespace Kernel::CMOS { - -u8 read(u8 index); -void write(u8 index, u8 data); - -} diff --git a/Kernel/Arch/x86_64/CPU.cpp b/Kernel/Arch/x86_64/CPU.cpp deleted file mode 100644 index 81490464bd6..00000000000 --- a/Kernel/Arch/x86_64/CPU.cpp +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include - -using namespace Kernel; - -NO_SANITIZE_COVERAGE void __assertion_failed(char const* msg, char const* file, unsigned line, char const* func) -{ - asm volatile("cli"); - critical_dmesgln("ASSERTION FAILED: {}", msg); - critical_dmesgln("{}:{} in {}", file, line, func); - - abort(); -} - -[[noreturn]] void abort() -{ - // Avoid lock ranking checks on crashing paths, just try to get some debugging messages out. - auto thread = Thread::current(); - if (thread) - thread->set_crashing(); - - // Switch back to the current process's page tables if there are any. - // Otherwise stack walking will be a disaster. - if (Process::has_current()) - Memory::MemoryManager::enter_process_address_space(Process::current()); - - PANIC("Aborted"); -} - -[[noreturn]] void _abort() -{ - asm volatile("ud2"); - __builtin_unreachable(); -} diff --git a/Kernel/Arch/x86_64/CPU.h b/Kernel/Arch/x86_64/CPU.h deleted file mode 100644 index 8360fff976a..00000000000 --- a/Kernel/Arch/x86_64/CPU.h +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright (c) 2018-2021, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include - -#include - -#include -VALIDATE_IS_X86() - -/* Map IRQ0-15 @ ISR 0x50-0x5F */ -#define IRQ_VECTOR_BASE 0x50 -#define GENERIC_INTERRUPT_HANDLERS_COUNT (256 - IRQ_VECTOR_BASE) - -namespace Kernel { - -struct RegisterState; -class GenericInterruptHandler; - -static constexpr u32 safe_eflags_mask = 0xdff; -static constexpr u32 iopl_mask = 3u << 12; - -inline u32 get_iopl_from_eflags(u32 eflags) -{ - return (eflags & iopl_mask) >> 12; -} - -DescriptorTablePointer const& get_gdtr(); -DescriptorTablePointer const& get_idtr(); - -constexpr FlatPtr page_base_of(FlatPtr address) -{ - return address & PAGE_MASK; -} - -inline FlatPtr page_base_of(void const* address) -{ - return page_base_of((FlatPtr)address); -} - -constexpr FlatPtr offset_in_page(FlatPtr address) -{ - return address & (~PAGE_MASK); -} - -inline FlatPtr offset_in_page(void const* address) -{ - return offset_in_page((FlatPtr)address); -} - -} diff --git a/Kernel/Arch/x86_64/CPUID.cpp b/Kernel/Arch/x86_64/CPUID.cpp deleted file mode 100644 index 6485f0c3a37..00000000000 --- a/Kernel/Arch/x86_64/CPUID.cpp +++ /dev/null @@ -1,376 +0,0 @@ -/* - * Copyright (c) 2022, Linus Groh - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include - -namespace Kernel { - -StringView cpu_feature_to_name(CPUFeature::Type const& feature) -{ - if (feature == CPUFeature::SSE3) - return "sse3"sv; - if (feature == CPUFeature::PCLMULQDQ) - return "pclmulqdq"sv; - if (feature == CPUFeature::DTES64) - return "dtes64"sv; - if (feature == CPUFeature::MONITOR) - return "monitor"sv; - if (feature == CPUFeature::DS_CPL) - return "ds_cpl"sv; - if (feature == CPUFeature::VMX) - return "vmx"sv; - if (feature == CPUFeature::SMX) - return "smx"sv; - if (feature == CPUFeature::EST) - return "est"sv; - if (feature == CPUFeature::TM2) - return "tm2"sv; - if (feature == CPUFeature::SSSE3) - return "ssse3"sv; - // NOTE: This is called cid on Linux, but CNXT_ID in the Intel manual. - if (feature == CPUFeature::CNXT_ID) - return "cnxt_id"sv; - if (feature == CPUFeature::SDBG) - return "sdbg"sv; - if (feature == CPUFeature::FMA) - return "fma"sv; - if (feature == CPUFeature::CX16) - return "cx16"sv; - if (feature == CPUFeature::XTPR) - return "xtpr"sv; - if (feature == CPUFeature::PDCM) - return "pdcm"sv; - if (feature == CPUFeature::PCID) - return "pcid"sv; - if (feature == CPUFeature::DCA) - return "dca"sv; - if (feature == CPUFeature::SSE4_1) - return "sse4_1"sv; - if (feature == CPUFeature::SSE4_2) - return "sse4_2"sv; - if (feature == CPUFeature::X2APIC) - return "x2apic"sv; - if (feature == CPUFeature::MOVBE) - return "movbe"sv; - if (feature == CPUFeature::POPCNT) - return "popcnt"sv; - // NOTE: This is called tsc_deadline_timer on Linux, but TSC_DEADLINE in the Intel manual. - if (feature == CPUFeature::TSC_DEADLINE) - return "tsc_deadline"sv; - if (feature == CPUFeature::AES) - return "aes"sv; - if (feature == CPUFeature::XSAVE) - return "xsave"sv; - if (feature == CPUFeature::OSXSAVE) - return "osxsave"sv; - if (feature == CPUFeature::AVX) - return "avx"sv; - if (feature == CPUFeature::F16C) - return "f16c"sv; - if (feature == CPUFeature::RDRAND) - return "rdrand"sv; - if (feature == CPUFeature::HYPERVISOR) - return "hypervisor"sv; - if (feature == CPUFeature::FPU) - return "fpu"sv; - if (feature == CPUFeature::VME) - return "vme"sv; - if (feature == CPUFeature::DE) - return "de"sv; - if (feature == CPUFeature::PSE) - return "pse"sv; - if (feature == CPUFeature::TSC) - return "tsc"sv; - if (feature == CPUFeature::MSR) - return "msr"sv; - if (feature == CPUFeature::PAE) - return "pae"sv; - if (feature == CPUFeature::MCE) - return "mce"sv; - if (feature == CPUFeature::CX8) - return "cx8"sv; - if (feature == CPUFeature::APIC) - return "apic"sv; - if (feature == CPUFeature::SEP) - return "sep"sv; - if (feature == CPUFeature::MTRR) - return "mtrr"sv; - if (feature == CPUFeature::PGE) - return "pge"sv; - if (feature == CPUFeature::MCA) - return "mca"sv; - if (feature == CPUFeature::CMOV) - return "cmov"sv; - if (feature == CPUFeature::PAT) - return "pat"sv; - if (feature == CPUFeature::PSE36) - return "pse36"sv; - if (feature == CPUFeature::PSN) - return "psn"sv; - if (feature == CPUFeature::CLFLUSH) - return "clflush"sv; - if (feature == CPUFeature::DS) - return "ds"sv; - if (feature == CPUFeature::ACPI) - return "acpi"sv; - if (feature == CPUFeature::MMX) - return "mmx"sv; - if (feature == CPUFeature::FXSR) - return "fxsr"sv; - if (feature == CPUFeature::SSE) - return "sse"sv; - if (feature == CPUFeature::SSE2) - return "sse2"sv; - if (feature == CPUFeature::SS) - return "ss"sv; - if (feature == CPUFeature::HTT) - return "htt"sv; - if (feature == CPUFeature::TM) - return "tm"sv; - if (feature == CPUFeature::IA64) - return "ia64"sv; - if (feature == CPUFeature::PBE) - return "pbe"sv; - if (feature == CPUFeature::FSGSBASE) - return "fsgsbase"sv; - if (feature == CPUFeature::TSC_ADJUST) - return "tsc_adjust"sv; - if (feature == CPUFeature::SGX) - return "sgx"sv; - if (feature == CPUFeature::BMI1) - return "bmi1"sv; - if (feature == CPUFeature::HLE) - return "hle"sv; - if (feature == CPUFeature::AVX2) - return "avx2"sv; - if (feature == CPUFeature::FDP_EXCPTN_ONLY) - return "fdp_excptn_only"sv; - if (feature == CPUFeature::SMEP) - return "smep"sv; - if (feature == CPUFeature::BMI2) - return "bmi2"sv; - if (feature == CPUFeature::ERMS) - return "erms"sv; - if (feature == CPUFeature::INVPCID) - return "invpcid"sv; - if (feature == CPUFeature::RTM) - return "rtm"sv; - if (feature == CPUFeature::PQM) - return "pqm"sv; - if (feature == CPUFeature::ZERO_FCS_FDS) - return "zero_fcs_fds"sv; - if (feature == CPUFeature::MPX) - return "mpx"sv; - if (feature == CPUFeature::PQE) - return "pqe"sv; - if (feature == CPUFeature::AVX512_F) - return "avx512_f"sv; - if (feature == CPUFeature::AVX512_DQ) - return "avx512_dq"sv; - if (feature == CPUFeature::RDSEED) - return "rdseed"sv; - if (feature == CPUFeature::ADX) - return "adx"sv; - if (feature == CPUFeature::SMAP) - return "smap"sv; - if (feature == CPUFeature::AVX512_IFMA) - return "avx512_ifma"sv; - if (feature == CPUFeature::PCOMMIT) - return "pcommit"sv; - if (feature == CPUFeature::CLFLUSHOPT) - return "clflushopt"sv; - if (feature == CPUFeature::CLWB) - return "clwb"sv; - if (feature == CPUFeature::INTEL_PT) - return "intel_pt"sv; - if (feature == CPUFeature::AVX512_PF) - return "avx512_pf"sv; - if (feature == CPUFeature::AVX512_ER) - return "avx512_er"sv; - if (feature == CPUFeature::AVX512_CD) - return "avx512_cd"sv; - if (feature == CPUFeature::SHA) - return "sha"sv; - if (feature == CPUFeature::AVX512_BW) - return "avx512_bw"sv; - if (feature == CPUFeature::AVX512_VL) - return "avx512_vl"sv; - if (feature == CPUFeature::PREFETCHWT1) - return "prefetchwt1"sv; - if (feature == CPUFeature::AVX512_VBMI) - return "avx512_vbmi"sv; - if (feature == CPUFeature::UMIP) - return "umip"sv; - if (feature == CPUFeature::PKU) - return "pku"sv; - if (feature == CPUFeature::OSPKE) - return "ospke"sv; - if (feature == CPUFeature::WAITPKG) - return "waitpkg"sv; - if (feature == CPUFeature::AVX512_VBMI2) - return "avx512_vbmi2"sv; - if (feature == CPUFeature::CET_SS) - return "cet_ss"sv; - if (feature == CPUFeature::GFNI) - return "gfni"sv; - if (feature == CPUFeature::VAES) - return "vaes"sv; - if (feature == CPUFeature::VPCLMULQDQ) - return "vpclmulqdq"sv; - if (feature == CPUFeature::AVX512_VNNI) - return "avx512_vnni"sv; - if (feature == CPUFeature::AVX512_BITALG) - return "avx512_bitalg"sv; - if (feature == CPUFeature::TME_EN) - return "tme_en"sv; - if (feature == CPUFeature::AVX512_VPOPCNTDQ) - return "avx512_vpopcntdq"sv; - if (feature == CPUFeature::INTEL_5_LEVEL_PAGING) - return "intel_5_level_paging"sv; - if (feature == CPUFeature::RDPID) - return "rdpid"sv; - if (feature == CPUFeature::KL) - return "kl"sv; - if (feature == CPUFeature::CLDEMOTE) - return "cldemote"sv; - if (feature == CPUFeature::MOVDIRI) - return "movdiri"sv; - if (feature == CPUFeature::MOVDIR64B) - return "movdir64b"sv; - if (feature == CPUFeature::ENQCMD) - return "enqcmd"sv; - if (feature == CPUFeature::SGX_LC) - return "sgx_lc"sv; - if (feature == CPUFeature::PKS) - return "pks"sv; - if (feature == CPUFeature::AVX512_4VNNIW) - return "avx512_4vnniw"sv; - if (feature == CPUFeature::AVX512_4FMAPS) - return "avx512_4fmaps"sv; - if (feature == CPUFeature::FSRM) - return "fsrm"sv; - if (feature == CPUFeature::AVX512_VP2INTERSECT) - return "avx512_vp2intersect"sv; - if (feature == CPUFeature::SRBDS_CTRL) - return "srbds_ctrl"sv; - if (feature == CPUFeature::MD_CLEAR) - return "md_clear"sv; - if (feature == CPUFeature::RTM_ALWAYS_ABORT) - return "rtm_always_abort"sv; - if (feature == CPUFeature::TSX_FORCE_ABORT) - return "tsx_force_abort"sv; - if (feature == CPUFeature::SERIALIZE) - return "serialize"sv; - if (feature == CPUFeature::HYBRID) - return "hybrid"sv; - if (feature == CPUFeature::TSXLDTRK) - return "tsxldtrk"sv; - if (feature == CPUFeature::PCONFIG) - return "pconfig"sv; - if (feature == CPUFeature::LBR) - return "lbr"sv; - if (feature == CPUFeature::CET_IBT) - return "cet_ibt"sv; - if (feature == CPUFeature::AMX_BF16) - return "amx_bf16"sv; - if (feature == CPUFeature::AVX512_FP16) - return "avx512_fp16"sv; - if (feature == CPUFeature::AMX_TILE) - return "amx_tile"sv; - if (feature == CPUFeature::AMX_INT8) - return "amx_int8"sv; - if (feature == CPUFeature::SPEC_CTRL) - return "spec_ctrl"sv; - if (feature == CPUFeature::STIBP) - return "stibp"sv; - // NOTE: This is called flush_l1d on Linux, but L1D_FLUSH in the Intel manual. - if (feature == CPUFeature::L1D_FLUSH) - return "l1d_flush"sv; - if (feature == CPUFeature::IA32_ARCH_CAPABILITIES) - return "ia32_arch_capabilities"sv; - if (feature == CPUFeature::IA32_CORE_CAPABILITIES) - return "ia32_code_capabilities"sv; - if (feature == CPUFeature::SSBD) - return "ssbd"sv; - if (feature == CPUFeature::LAHF_LM) - return "lahf_lm"sv; - if (feature == CPUFeature::CMP_LEGACY) - return "cmp_legacy"sv; - if (feature == CPUFeature::SVM) - return "svm"sv; - if (feature == CPUFeature::EXTAPIC) - return "extapic"sv; - if (feature == CPUFeature::CR8_LEGACY) - return "cr8_legacy"sv; - if (feature == CPUFeature::ABM) - return "abm"sv; - if (feature == CPUFeature::SSE4A) - return "sse4a"sv; - if (feature == CPUFeature::MISALIGNSSE) - return "misalignsse"sv; - if (feature == CPUFeature::_3DNOWPREFETCH) - return "3dnowprefetch"sv; - if (feature == CPUFeature::OSVW) - return "osvw"sv; - if (feature == CPUFeature::IBS) - return "ibs"sv; - if (feature == CPUFeature::XOP) - return "xop"sv; - if (feature == CPUFeature::SKINIT) - return "skinit"sv; - if (feature == CPUFeature::WDT) - return "wdt"sv; - if (feature == CPUFeature::LWP) - return "lwp"sv; - if (feature == CPUFeature::FMA4) - return "fma4"sv; - if (feature == CPUFeature::TCE) - return "tce"sv; - if (feature == CPUFeature::NODEID_MSR) - return "nodeid_msr"sv; - if (feature == CPUFeature::TBM) - return "tbm"sv; - if (feature == CPUFeature::TOPOEXT) - return "topoext"sv; - if (feature == CPUFeature::PERFCTR_CORE) - return "perfctr_core"sv; - if (feature == CPUFeature::PERFCTR_NB) - return "perfctr_nb"sv; - if (feature == CPUFeature::DBX) - return "dbx"sv; - if (feature == CPUFeature::PERFTSC) - return "perftsc"sv; - // NOTE: This is called perfctr_l2 on Linux, but PCX_L2I in the AMD manual & other references. - if (feature == CPUFeature::PCX_L2I) - return "pcx_l2i"sv; - if (feature == CPUFeature::SYSCALL) - return "syscall"sv; - if (feature == CPUFeature::MP) - return "mp"sv; - if (feature == CPUFeature::NX) - return "nx"sv; - if (feature == CPUFeature::MMXEXT) - return "mmxext"sv; - if (feature == CPUFeature::FXSR_OPT) - return "fxsr_opt"sv; - if (feature == CPUFeature::PDPE1GB) - return "pdpe1gb"sv; - if (feature == CPUFeature::RDTSCP) - return "rdtscp"sv; - if (feature == CPUFeature::LM) - return "lm"sv; - if (feature == CPUFeature::_3DNOWEXT) - return "3dnowext"sv; - if (feature == CPUFeature::_3DNOW) - return "3dnow"sv; - if (feature == CPUFeature::CONSTANT_TSC) - return "constant_tsc"sv; - if (feature == CPUFeature::NONSTOP_TSC) - return "nonstop_tsc"sv; - VERIFY_NOT_REACHED(); -} - -} diff --git a/Kernel/Arch/x86_64/CPUID.h b/Kernel/Arch/x86_64/CPUID.h deleted file mode 100644 index fc3038be43a..00000000000 --- a/Kernel/Arch/x86_64/CPUID.h +++ /dev/null @@ -1,242 +0,0 @@ -/* - * Copyright (c) 2018-2021, Andreas Kling - * Copyright (c) 2022, Linus Groh - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include - -#include -VALIDATE_IS_X86() - -namespace Kernel { - -class CPUID { -public: - explicit CPUID(u32 function, u32 ecx = 0) - { - asm volatile("cpuid" - : "=a"(m_eax), "=b"(m_ebx), "=c"(m_ecx), "=d"(m_edx) - : "a"(function), "c"(ecx)); - } - - u32 eax() const { return m_eax; } - u32 ebx() const { return m_ebx; } - u32 ecx() const { return m_ecx; } - u32 edx() const { return m_edx; } - -private: - u32 m_eax { 0xffffffff }; - u32 m_ebx { 0xffffffff }; - u32 m_ecx { 0xffffffff }; - u32 m_edx { 0xffffffff }; -}; - -AK_MAKE_ARBITRARY_SIZED_ENUM(CPUFeature, u256, - /* EAX=1, ECX */ // - SSE3 = CPUFeature(1u) << 0u, // Streaming SIMD Extensions 3 - PCLMULQDQ = CPUFeature(1u) << 1u, // PCLMULDQ Instruction - DTES64 = CPUFeature(1u) << 2u, // 64-Bit Debug Store - MONITOR = CPUFeature(1u) << 3u, // MONITOR/MWAIT Instructions - DS_CPL = CPUFeature(1u) << 4u, // CPL Qualified Debug Store - VMX = CPUFeature(1u) << 5u, // Virtual Machine Extensions - SMX = CPUFeature(1u) << 6u, // Safer Mode Extensions - EST = CPUFeature(1u) << 7u, // Enhanced Intel SpeedStep® Technology - TM2 = CPUFeature(1u) << 8u, // Thermal Monitor 2 - SSSE3 = CPUFeature(1u) << 9u, // Supplemental Streaming SIMD Extensions 3 - CNXT_ID = CPUFeature(1u) << 10u, // L1 Context ID - SDBG = CPUFeature(1u) << 11u, // Silicon Debug (IA32_DEBUG_INTERFACE MSR) - FMA = CPUFeature(1u) << 12u, // Fused Multiply Add - CX16 = CPUFeature(1u) << 13u, // CMPXCHG16B Instruction - XTPR = CPUFeature(1u) << 14u, // xTPR Update Control - PDCM = CPUFeature(1u) << 15u, // Perfmon and Debug Capability (IA32_PERF_CAPABILITIES MSR) - /* ECX Bit 16 */ // Reserved - PCID = CPUFeature(1u) << 17u, // Process Context Identifiers - DCA = CPUFeature(1u) << 18u, // Direct Cache Access - SSE4_1 = CPUFeature(1u) << 19u, // Streaming SIMD Extensions 4.1 - SSE4_2 = CPUFeature(1u) << 20u, // Streaming SIMD Extensions 4.2 - X2APIC = CPUFeature(1u) << 21u, // Extended xAPIC Support - MOVBE = CPUFeature(1u) << 22u, // MOVBE Instruction - POPCNT = CPUFeature(1u) << 23u, // POPCNT Instruction - TSC_DEADLINE = CPUFeature(1u) << 24u, // Time Stamp Counter Deadline - AES = CPUFeature(1u) << 25u, // AES Instruction Extensions - XSAVE = CPUFeature(1u) << 26u, // XSAVE/XSTOR States - OSXSAVE = CPUFeature(1u) << 27u, // OS-Enabled Extended State Management - AVX = CPUFeature(1u) << 28u, // Advanced Vector Extensions - F16C = CPUFeature(1u) << 29u, // 16-bit floating-point conversion instructions - RDRAND = CPUFeature(1u) << 30u, // RDRAND Instruction - HYPERVISOR = CPUFeature(1u) << 31u, // Hypervisor present (always zero on physical CPUs) - /* EAX=1, EDX */ // - FPU = CPUFeature(1u) << 32u, // Floating-point Unit On-Chip - VME = CPUFeature(1u) << 33u, // Virtual Mode Extension - DE = CPUFeature(1u) << 34u, // Debugging Extension - PSE = CPUFeature(1u) << 35u, // Page Size Extension - TSC = CPUFeature(1u) << 36u, // Time Stamp Counter - MSR = CPUFeature(1u) << 37u, // Model Specific Registers - PAE = CPUFeature(1u) << 38u, // Physical Address Extension - MCE = CPUFeature(1u) << 39u, // Machine-Check Exception - CX8 = CPUFeature(1u) << 40u, // CMPXCHG8 Instruction - APIC = CPUFeature(1u) << 41u, // On-chip APIC Hardware - /* EDX Bit 10 */ // Reserved - SEP = CPUFeature(1u) << 43u, // Fast System Call - MTRR = CPUFeature(1u) << 44u, // Memory Type Range Registers - PGE = CPUFeature(1u) << 45u, // Page Global Enable - MCA = CPUFeature(1u) << 46u, // Machine-Check Architecture - CMOV = CPUFeature(1u) << 47u, // Conditional Move Instruction - PAT = CPUFeature(1u) << 48u, // Page Attribute Table - PSE36 = CPUFeature(1u) << 49u, // 36-bit Page Size Extension - PSN = CPUFeature(1u) << 50u, // Processor serial number is present and enabled - CLFLUSH = CPUFeature(1u) << 51u, // CLFLUSH Instruction - /* EDX Bit 20 */ // Reserved - DS = CPUFeature(1u) << 53u, // CLFLUSH Instruction - ACPI = CPUFeature(1u) << 54u, // CLFLUSH Instruction - MMX = CPUFeature(1u) << 55u, // CLFLUSH Instruction - FXSR = CPUFeature(1u) << 56u, // CLFLUSH Instruction - SSE = CPUFeature(1u) << 57u, // Streaming SIMD Extensions - SSE2 = CPUFeature(1u) << 58u, // Streaming SIMD Extensions 2 - SS = CPUFeature(1u) << 59u, // Self-Snoop - HTT = CPUFeature(1u) << 60u, // Multi-Threading - TM = CPUFeature(1u) << 61u, // Thermal Monitor - IA64 = CPUFeature(1u) << 62u, // IA64 processor emulating x86 - PBE = CPUFeature(1u) << 63u, // Pending Break Enable - /* EAX=7, EBX */ // - FSGSBASE = CPUFeature(1u) << 64u, // Access to base of %fs and %gs - TSC_ADJUST = CPUFeature(1u) << 65u, // IA32_TSC_ADJUST MSR - SGX = CPUFeature(1u) << 66u, // Software Guard Extensions - BMI1 = CPUFeature(1u) << 67u, // Bit Manipulation Instruction Set 1 - HLE = CPUFeature(1u) << 68u, // TSX Hardware Lock Elision - AVX2 = CPUFeature(1u) << 69u, // Advanced Vector Extensions 2 - FDP_EXCPTN_ONLY = CPUFeature(1u) << 70u, // FDP_EXCPTN_ONLY - SMEP = CPUFeature(1u) << 71u, // Supervisor Mode Execution Protection - BMI2 = CPUFeature(1u) << 72u, // Bit Manipulation Instruction Set 2 - ERMS = CPUFeature(1u) << 73u, // Enhanced REP MOVSB/STOSB - INVPCID = CPUFeature(1u) << 74u, // INVPCID Instruction - RTM = CPUFeature(1u) << 75u, // TSX Restricted Transactional Memory - PQM = CPUFeature(1u) << 76u, // Platform Quality of Service Monitoring - ZERO_FCS_FDS = CPUFeature(1u) << 77u, // FPU CS and FPU DS deprecated - MPX = CPUFeature(1u) << 78u, // Intel MPX (Memory Protection Extensions) - PQE = CPUFeature(1u) << 79u, // Platform Quality of Service Enforcement - AVX512_F = CPUFeature(1u) << 80u, // AVX-512 Foundation - AVX512_DQ = CPUFeature(1u) << 81u, // AVX-512 Doubleword and Quadword Instructions - RDSEED = CPUFeature(1u) << 82u, // RDSEED Instruction - ADX = CPUFeature(1u) << 83u, // Intel ADX (Multi-Precision Add-Carry Instruction Extensions) - SMAP = CPUFeature(1u) << 84u, // Supervisor Mode Access Prevention - AVX512_IFMA = CPUFeature(1u) << 85u, // AVX-512 Integer Fused Multiply-Add Instructions - PCOMMIT = CPUFeature(1u) << 86u, // PCOMMIT Instruction - CLFLUSHOPT = CPUFeature(1u) << 87u, // CLFLUSHOPT Instruction - CLWB = CPUFeature(1u) << 88u, // CLWB Instruction - INTEL_PT = CPUFeature(1u) << 89u, // Intel Processor Tracing - AVX512_PF = CPUFeature(1u) << 90u, // AVX-512 Prefetch Instructions - AVX512_ER = CPUFeature(1u) << 91u, // AVX-512 Exponential and Reciprocal Instructions - AVX512_CD = CPUFeature(1u) << 92u, // AVX-512 Conflict Detection Instructions - SHA = CPUFeature(1u) << 93u, // Intel SHA Extensions - AVX512_BW = CPUFeature(1u) << 94u, // AVX-512 Byte and Word Instructions - AVX512_VL = CPUFeature(1u) << 95u, // AVX-512 Vector Length Extensions - /* EAX=7, ECX */ // - PREFETCHWT1 = CPUFeature(1u) << 96u, // PREFETCHWT1 Instruction - AVX512_VBMI = CPUFeature(1u) << 97u, // AVX-512 Vector Bit Manipulation Instructions - UMIP = CPUFeature(1u) << 98u, // UMIP - PKU = CPUFeature(1u) << 99u, // Memory Protection Keys for User-mode pages - OSPKE = CPUFeature(1u) << 100u, // PKU enabled by OS - WAITPKG = CPUFeature(1u) << 101u, // Timed pause and user-level monitor/wait - AVX512_VBMI2 = CPUFeature(1u) << 102u, // AVX-512 Vector Bit Manipulation Instructions 2 - CET_SS = CPUFeature(1u) << 103u, // Control Flow Enforcement (CET) Shadow Stack - GFNI = CPUFeature(1u) << 104u, // Galois Field Instructions - VAES = CPUFeature(1u) << 105u, // Vector AES instruction set (VEX-256/EVEX) - VPCLMULQDQ = CPUFeature(1u) << 106u, // CLMUL instruction set (VEX-256/EVEX) - AVX512_VNNI = CPUFeature(1u) << 107u, // AVX-512 Vector Neural Network Instructions - AVX512_BITALG = CPUFeature(1u) << 108u, // AVX-512 BITALG Instructions - TME_EN = CPUFeature(1u) << 109u, // IA32_TME related MSRs are supported - AVX512_VPOPCNTDQ = CPUFeature(1u) << 110u, // AVX-512 Vector Population Count Double and Quad-word - /* ECX Bit 15 */ // Reserved - INTEL_5_LEVEL_PAGING = CPUFeature(1u) << 112u, // Intel 5-Level Paging - RDPID = CPUFeature(1u) << 113u, // RDPID Instruction - KL = CPUFeature(1u) << 114u, // Key Locker - /* ECX Bit 24 */ // Reserved - CLDEMOTE = CPUFeature(1u) << 116u, // Cache Line Demote - /* ECX Bit 26 */ // Reserved - MOVDIRI = CPUFeature(1u) << 118u, // MOVDIRI Instruction - MOVDIR64B = CPUFeature(1u) << 119u, // MOVDIR64B Instruction - ENQCMD = CPUFeature(1u) << 120u, // ENQCMD Instruction - SGX_LC = CPUFeature(1u) << 121u, // SGX Launch Configuration - PKS = CPUFeature(1u) << 122u, // Protection Keys for Supervisor-Mode Pages - /* EAX=7, EDX */ // - /* ECX Bit 0-1 */ // Reserved - AVX512_4VNNIW = CPUFeature(1u) << 125u, // AVX-512 4-register Neural Network Instructions - AVX512_4FMAPS = CPUFeature(1u) << 126u, // AVX-512 4-register Multiply Accumulation Single precision - FSRM = CPUFeature(1u) << 127u, // Fast Short REP MOVSB - /* ECX Bit 5-7 */ // Reserved - AVX512_VP2INTERSECT = CPUFeature(1u) << 131u, // AVX-512 VP2INTERSECT Doubleword and Quadword Instructions - SRBDS_CTRL = CPUFeature(1u) << 132u, // Special Register Buffer Data Sampling Mitigations - MD_CLEAR = CPUFeature(1u) << 133u, // VERW instruction clears CPU buffers - RTM_ALWAYS_ABORT = CPUFeature(1u) << 134u, // All TSX transactions are aborted - /* ECX Bit 12 */ // Reserved - TSX_FORCE_ABORT = CPUFeature(1u) << 136u, // TSX_FORCE_ABORT MSR - SERIALIZE = CPUFeature(1u) << 137u, // Serialize instruction execution - HYBRID = CPUFeature(1u) << 138u, // Mixture of CPU types in processor topology - TSXLDTRK = CPUFeature(1u) << 139u, // TSX suspend load address tracking - /* ECX Bit 17 */ // Reserved - PCONFIG = CPUFeature(1u) << 141u, // Platform configuration (Memory Encryption Technologies Instructions) - LBR = CPUFeature(1u) << 142u, // Architectural Last Branch Records - CET_IBT = CPUFeature(1u) << 143u, // Control flow enforcement (CET) indirect branch tracking - /* ECX Bit 21 */ // Reserved - AMX_BF16 = CPUFeature(1u) << 145u, // Tile computation on bfloat16 numbers - AVX512_FP16 = CPUFeature(1u) << 146u, // AVX512-FP16 half-precision floating-point instructions - AMX_TILE = CPUFeature(1u) << 147u, // Tile architecture - AMX_INT8 = CPUFeature(1u) << 148u, // Tile computation on 8-bit integers - SPEC_CTRL = CPUFeature(1u) << 149u, // Speculation Control - STIBP = CPUFeature(1u) << 150u, // Single Thread Indirect Branch Predictor - L1D_FLUSH = CPUFeature(1u) << 151u, // IA32_FLUSH_CMD MSR - IA32_ARCH_CAPABILITIES = CPUFeature(1u) << 152u, // IA32_ARCH_CAPABILITIES MSR - IA32_CORE_CAPABILITIES = CPUFeature(1u) << 153u, // IA32_CORE_CAPABILITIES MSR - SSBD = CPUFeature(1u) << 154u, // Speculative Store Bypass Disable - /* EAX=80000001h, ECX */ // - LAHF_LM = CPUFeature(1u) << 155u, // LAHF/SAHF in long mode - CMP_LEGACY = CPUFeature(1u) << 156u, // Hyperthreading not valid - SVM = CPUFeature(1u) << 157u, // Secure Virtual Machine - EXTAPIC = CPUFeature(1u) << 158u, // Extended APIC Space - CR8_LEGACY = CPUFeature(1u) << 159u, // CR8 in 32-bit mode - ABM = CPUFeature(1u) << 160u, // Advanced Bit Manipulation - SSE4A = CPUFeature(1u) << 161u, // SSE4a - MISALIGNSSE = CPUFeature(1u) << 162u, // Misaligned SSE Mode - _3DNOWPREFETCH = CPUFeature(1u) << 163u, // PREFETCH and PREFETCHW Instructions - OSVW = CPUFeature(1u) << 164u, // OS Visible Workaround - IBS = CPUFeature(1u) << 165u, // Instruction Based Sampling - XOP = CPUFeature(1u) << 166u, // XOP instruction set - SKINIT = CPUFeature(1u) << 167u, // SKINIT/STGI Instructions - WDT = CPUFeature(1u) << 168u, // Watchdog timer - LWP = CPUFeature(1u) << 169u, // Light Weight Profiling - FMA4 = CPUFeature(1u) << 170u, // FMA4 instruction set - TCE = CPUFeature(1u) << 171u, // Translation Cache Extension - NODEID_MSR = CPUFeature(1u) << 172u, // NodeID MSR - TBM = CPUFeature(1u) << 173u, // Trailing Bit Manipulation - TOPOEXT = CPUFeature(1u) << 174u, // Topology Extensions - PERFCTR_CORE = CPUFeature(1u) << 175u, // Core Performance Counter Extensions - PERFCTR_NB = CPUFeature(1u) << 176u, // NB Performance Counter Extensions - DBX = CPUFeature(1u) << 177u, // Data Breakpoint Extensions - PERFTSC = CPUFeature(1u) << 178u, // Performance TSC - PCX_L2I = CPUFeature(1u) << 179u, // L2I Performance Counter Extensions - /* EAX=80000001h, EDX */ // - SYSCALL = CPUFeature(1u) << 180u, // SYSCALL/SYSRET Instructions - MP = CPUFeature(1u) << 181u, // Multiprocessor Capable - NX = CPUFeature(1u) << 182u, // NX bit - MMXEXT = CPUFeature(1u) << 183u, // Extended MMX - FXSR_OPT = CPUFeature(1u) << 184u, // FXSAVE/FXRSTOR Optimizations - PDPE1GB = CPUFeature(1u) << 185u, // Gigabyte Pages - RDTSCP = CPUFeature(1u) << 186u, // RDTSCP Instruction - LM = CPUFeature(1u) << 187u, // Long Mode - _3DNOWEXT = CPUFeature(1u) << 188u, // Extended 3DNow! - _3DNOW = CPUFeature(1u) << 189u, // 3DNow! - /* EAX=80000007h, EDX */ // - CONSTANT_TSC = CPUFeature(1u) << 190u, // Invariant TSC - NONSTOP_TSC = CPUFeature(1u) << 191u, // Invariant TSC - __End = CPUFeature(1u) << 255u); - -StringView cpu_feature_to_name(CPUFeature::Type const&); - -} diff --git a/Kernel/Arch/x86_64/CurrentTime.cpp b/Kernel/Arch/x86_64/CurrentTime.cpp deleted file mode 100644 index 54e1db9dd7b..00000000000 --- a/Kernel/Arch/x86_64/CurrentTime.cpp +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright (c) 2022, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include - -namespace Kernel { - -static u64 current_time_tsc() -{ - return read_tsc(); -} - -fptr optional_current_time() -{ - VERIFY(Processor::is_initialized()); // sanity check - // Figure out a good scheduling time source - if (Processor::current().has_feature(CPUFeature::TSC) && Processor::current().has_feature(CPUFeature::CONSTANT_TSC)) { - return current_time_tsc; - } - return nullptr; -} - -} diff --git a/Kernel/Arch/x86_64/DebugOutput.cpp b/Kernel/Arch/x86_64/DebugOutput.cpp deleted file mode 100644 index 097469cc1af..00000000000 --- a/Kernel/Arch/x86_64/DebugOutput.cpp +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright (c) 2022, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include - -namespace Kernel { - -static constexpr u16 serial_com1_io_port = 0x3F8; - -void bochs_debug_output(char ch) -{ - IO::out8(IO::BOCHS_DEBUG_PORT, ch); -} - -void debug_output(char ch) -{ - static bool serial_ready = false; - static bool was_cr = false; - - if (!serial_ready) { - IO::out8(serial_com1_io_port + 1, 0x00); - IO::out8(serial_com1_io_port + 3, 0x80); - IO::out8(serial_com1_io_port + 0, 0x02); - IO::out8(serial_com1_io_port + 1, 0x00); - IO::out8(serial_com1_io_port + 3, 0x03); - IO::out8(serial_com1_io_port + 2, 0xC7); - IO::out8(serial_com1_io_port + 4, 0x0B); - - serial_ready = true; - } - - while ((IO::in8(serial_com1_io_port + 5) & 0x20) == 0) - Processor::wait_check(); - - if (ch == '\n' && !was_cr) - IO::out8(serial_com1_io_port, '\r'); - - IO::out8(serial_com1_io_port, ch); - - was_cr = ch == '\r'; -} - -} diff --git a/Kernel/Arch/x86_64/Delay.cpp b/Kernel/Arch/x86_64/Delay.cpp deleted file mode 100644 index 9896abfd4f0..00000000000 --- a/Kernel/Arch/x86_64/Delay.cpp +++ /dev/null @@ -1,17 +0,0 @@ -/* - * Copyright (c) 2022, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include - -namespace Kernel { - -void microseconds_delay(u32 microseconds) -{ - IO::delay(microseconds); -} - -} diff --git a/Kernel/Arch/x86_64/DescriptorTable.h b/Kernel/Arch/x86_64/DescriptorTable.h deleted file mode 100644 index 8c22bf299a0..00000000000 --- a/Kernel/Arch/x86_64/DescriptorTable.h +++ /dev/null @@ -1,150 +0,0 @@ -/* - * Copyright (c) 2018-2021, Andreas Kling - * Copyright (c) 2021, Leon Albrecht - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include - -#include -VALIDATE_IS_X86() - -// Note: These values are x86-64. -#define GDT_SELECTOR_CODE0 0x08 -#define GDT_SELECTOR_DATA0 0x10 -#define GDT_SELECTOR_DATA3 0x18 -#define GDT_SELECTOR_CODE3 0x20 -#define GDT_SELECTOR_TSS 0x28 -#define GDT_SELECTOR_TSS_PART2 0x30 - -namespace Kernel { - -struct [[gnu::packed]] DescriptorTablePointer { - u16 limit; - void* address; -}; - -union [[gnu::packed]] Descriptor { - struct { - u16 limit_lo; - u16 base_lo; - u8 base_hi; - u8 type : 4; - u8 descriptor_type : 1; - u8 dpl : 2; - u8 segment_present : 1; - u8 limit_hi : 4; - u8 : 1; - u8 operation_size64 : 1; - u8 operation_size32 : 1; - u8 granularity : 1; - u8 base_hi2; - }; - struct { - u32 low; - u32 high; - }; - - enum SystemType { - Invalid = 0, - AvailableTSS_16bit = 0x1, - LDT = 0x2, - BusyTSS_16bit = 0x3, - CallGate_16bit = 0x4, - TaskGate = 0x5, - InterruptGate_16bit = 0x6, - TrapGate_16bit = 0x7, - AvailableTSS = 0x9, - BusyTSS = 0xb, - CallGate = 0xc, - InterruptGate = 0xe, - TrapGate = 0xf, - }; - - VirtualAddress base() const - { - FlatPtr base = base_lo; - base |= base_hi << 16u; - base |= base_hi2 << 24u; - return VirtualAddress { base }; - } - - void set_base(VirtualAddress base) - { - base_lo = base.get() & 0xffffu; - base_hi = (base.get() >> 16u) & 0xffu; - base_hi2 = (base.get() >> 24u) & 0xffu; - VERIFY(base.get() <= 0xffffffff); - } - - void set_limit(u32 length) - { - limit_lo = length & 0xffff; - limit_hi = (length >> 16) & 0xf; - } -}; - -static_assert(AssertSize()); - -enum class IDTEntryType { - TaskGate32 = 0b0101, - InterruptGate16 = 0b110, - TrapGate16 = 0b111, - InterruptGate32 = 0b1110, - TrapGate32 = 0b1111, -}; - -struct [[gnu::packed]] IDTEntry { - u16 offset_1; // offset bits 0..15 - u16 selector; // a code segment selector in GDT or LDT - - struct { - u8 interrupt_stack_table : 3; - u8 zero : 5; // unused, set to 0 - }; - - struct { - u8 gate_type : 4; - u8 storage_segment : 1; - u8 descriptor_privilege_level : 2; - u8 present : 1; - } type_attr; // type and attributes - u16 offset_2; // offset bits 16..31 - u32 offset_3; - u32 zeros; - - IDTEntry() = default; - IDTEntry(FlatPtr callback, u16 selector_, IDTEntryType type, u8 privilege_level) - : offset_1 { (u16)((FlatPtr)callback & 0xFFFF) } - , selector { selector_ } - , interrupt_stack_table { 0 } - , zero { 0 } - , type_attr { - .gate_type = (u8)type, - .storage_segment = 0, - .descriptor_privilege_level = (u8)(privilege_level & 0b11), - .present = 1, - } - , offset_2 { (u16)((FlatPtr)callback >> 16) } - , offset_3 { (u32)(((FlatPtr)callback) >> 32) } - , zeros { 0 } - { - } - - FlatPtr off() const - { - return (u64)offset_3 << 32 & (u64)offset_2 << 16 & (u64)offset_1; - } - IDTEntryType type() const - { - return IDTEntryType(type_attr.gate_type); - } -}; - -static_assert(AssertSize()); - -} diff --git a/Kernel/Arch/x86_64/FPUState.h b/Kernel/Arch/x86_64/FPUState.h deleted file mode 100644 index a2214cef5ca..00000000000 --- a/Kernel/Arch/x86_64/FPUState.h +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright (c) 2023, kleines Filmröllchen - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include - -VALIDATE_IS_X86() - -namespace Kernel { - -struct [[gnu::aligned(64), gnu::packed]] FPUState { - SIMD::LegacyRegion legacy_region; - SIMD::Header xsave_header; - - // FIXME: This should be dynamically allocated! For now, we only save the `YMM` registers here, - // so this will do for now. The size of the area is queried via CPUID(EAX=0dh, ECX=2):EAX. - // https://www.intel.com/content/dam/develop/external/us/en/documents/36945 - u8 ext_save_area[256]; -}; - -} diff --git a/Kernel/Arch/x86_64/Firmware/ACPI/StaticParsing.cpp b/Kernel/Arch/x86_64/Firmware/ACPI/StaticParsing.cpp deleted file mode 100644 index 18e12b53c88..00000000000 --- a/Kernel/Arch/x86_64/Firmware/ACPI/StaticParsing.cpp +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright (c) 2023, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include - -namespace Kernel::ACPI::StaticParsing { - -// https://uefi.org/specs/ACPI/6.4/05_ACPI_Software_Programming_Model/ACPI_Software_Programming_Model.html#finding-the-rsdp-on-ia-pc-systems -ErrorOr> find_rsdp_in_platform_specific_memory_locations() -{ - constexpr auto signature = "RSD PTR "sv; - auto ebda_or_error = map_ebda(); - if (!ebda_or_error.is_error()) { - auto rsdp = ebda_or_error.value().find_chunk_starting_with(signature, 16); - if (rsdp.has_value()) - return rsdp; - } - auto bios_or_error = map_bios(); - if (!bios_or_error.is_error()) { - auto rsdp = bios_or_error.value().find_chunk_starting_with(signature, 16); - if (rsdp.has_value()) - return rsdp; - } - - // On some systems the RSDP may be located in ACPI NVS or reclaimable memory regions - Optional rsdp; - MM.for_each_physical_memory_range([&](auto& memory_range) { - if (!(memory_range.type == Memory::PhysicalMemoryRangeType::ACPI_NVS || memory_range.type == Memory::PhysicalMemoryRangeType::ACPI_Reclaimable)) - return IterationDecision::Continue; - - Memory::MappedROM mapping; - auto region_size_or_error = Memory::page_round_up(memory_range.length); - if (region_size_or_error.is_error()) - return IterationDecision::Continue; - auto region_or_error = MM.allocate_mmio_kernel_region(memory_range.start, region_size_or_error.value(), {}, Memory::Region::Access::Read); - if (region_or_error.is_error()) - return IterationDecision::Continue; - mapping.region = region_or_error.release_value(); - mapping.offset = memory_range.start.offset_in_page(); - mapping.size = memory_range.length; - mapping.paddr = memory_range.start; - - rsdp = mapping.find_chunk_starting_with(signature, 16); - if (rsdp.has_value()) - return IterationDecision::Break; - - return IterationDecision::Continue; - }); - return rsdp; -} - -} diff --git a/Kernel/Arch/x86_64/Firmware/MultiProcessor/Parser.cpp b/Kernel/Arch/x86_64/Firmware/MultiProcessor/Parser.cpp deleted file mode 100644 index 0e992bee092..00000000000 --- a/Kernel/Arch/x86_64/Firmware/MultiProcessor/Parser.cpp +++ /dev/null @@ -1,164 +0,0 @@ -/* - * Copyright (c) 2020, Liav A. - * Copyright (c) 2020, Andreas Kling - * Copyright (c) 2022, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -namespace Kernel { - -UNMAP_AFTER_INIT OwnPtr MultiProcessorParser::autodetect() -{ - auto floating_pointer = find_floating_pointer(); - if (!floating_pointer.has_value()) - return {}; - auto parser = adopt_own_if_nonnull(new (nothrow) MultiProcessorParser(floating_pointer.value())); - VERIFY(parser != nullptr); - return parser; -} - -UNMAP_AFTER_INIT MultiProcessorParser::MultiProcessorParser(PhysicalAddress floating_pointer) - : m_floating_pointer(floating_pointer) -{ - dbgln("MultiProcessor: Floating Pointer Structure @ {}", m_floating_pointer); - parse_floating_pointer_data(); - parse_configuration_table(); -} - -UNMAP_AFTER_INIT void MultiProcessorParser::parse_floating_pointer_data() -{ - auto floating_pointer = Memory::map_typed(m_floating_pointer).release_value_but_fixme_should_propagate_errors(); - m_configuration_table = PhysicalAddress(floating_pointer->physical_address_ptr); - dbgln("Features {}, IMCR? {}", floating_pointer->feature_info[0], (floating_pointer->feature_info[0] & (1 << 7))); -} - -UNMAP_AFTER_INIT void MultiProcessorParser::parse_configuration_table() -{ - auto configuration_table_length = Memory::map_typed(m_configuration_table).release_value_but_fixme_should_propagate_errors()->length; - auto config_table = Memory::map_typed(m_configuration_table, configuration_table_length).release_value_but_fixme_should_propagate_errors(); - - size_t entry_count = config_table->entry_count; - auto* entry = config_table->entries; - while (entry_count > 0) { - dbgln_if(MULTIPROCESSOR_DEBUG, "MultiProcessor: Entry Type {} detected.", entry->entry_type); - switch (entry->entry_type) { - case ((u8)MultiProcessor::ConfigurationTableEntryType::Processor): - entry = (MultiProcessor::EntryHeader*)(FlatPtr)entry + sizeof(MultiProcessor::ProcessorEntry); - break; - case ((u8)MultiProcessor::ConfigurationTableEntryType::Bus): - MUST(m_bus_entries.try_append(*(MultiProcessor::BusEntry const*)entry)); - entry = (MultiProcessor::EntryHeader*)(FlatPtr)entry + sizeof(MultiProcessor::BusEntry); - break; - case ((u8)MultiProcessor::ConfigurationTableEntryType::IOAPIC): - entry = (MultiProcessor::EntryHeader*)(FlatPtr)entry + sizeof(MultiProcessor::IOAPICEntry); - break; - case ((u8)MultiProcessor::ConfigurationTableEntryType::IO_Interrupt_Assignment): - MUST(m_io_interrupt_assignment_entries.try_append(*(MultiProcessor::IOInterruptAssignmentEntry const*)entry)); - entry = (MultiProcessor::EntryHeader*)(FlatPtr)entry + sizeof(MultiProcessor::IOInterruptAssignmentEntry); - break; - case ((u8)MultiProcessor::ConfigurationTableEntryType::Local_Interrupt_Assignment): - entry = (MultiProcessor::EntryHeader*)(FlatPtr)entry + sizeof(MultiProcessor::LocalInterruptAssignmentEntry); - break; - case ((u8)MultiProcessor::ConfigurationTableEntryType::SystemAddressSpaceMapping): - entry = (MultiProcessor::EntryHeader*)(FlatPtr)entry + sizeof(MultiProcessor::SystemAddressSpaceMappingEntry); - break; - case ((u8)MultiProcessor::ConfigurationTableEntryType::BusHierarchyDescriptor): - entry = (MultiProcessor::EntryHeader*)(FlatPtr)entry + sizeof(MultiProcessor::BusHierarchyDescriptorEntry); - break; - case ((u8)MultiProcessor::ConfigurationTableEntryType::CompatibilityBusAddressSpaceModifier): - entry = (MultiProcessor::EntryHeader*)(FlatPtr)entry + sizeof(MultiProcessor::CompatibilityBusAddressSpaceModifierEntry); - break; - default: - VERIFY_NOT_REACHED(); - } - --entry_count; - } -} - -UNMAP_AFTER_INIT Optional MultiProcessorParser::find_floating_pointer() -{ - constexpr auto signature = "_MP_"sv; - auto ebda_or_error = map_ebda(); - if (!ebda_or_error.is_error()) { - auto mp_floating_pointer = ebda_or_error.value().find_chunk_starting_with(signature, 16); - if (mp_floating_pointer.has_value()) - return mp_floating_pointer; - } - auto bios_or_error = map_bios(); - if (bios_or_error.is_error()) - return {}; - return bios_or_error.value().find_chunk_starting_with(signature, 16); -} - -UNMAP_AFTER_INIT Vector MultiProcessorParser::get_pci_bus_ids() const -{ - Vector pci_bus_ids; - for (auto& entry : m_bus_entries) { - if (!strncmp("PCI ", entry.bus_type, strlen("PCI "))) - pci_bus_ids.append(entry.bus_id); - } - return pci_bus_ids; -} - -UNMAP_AFTER_INIT Vector MultiProcessorParser::get_pci_interrupt_redirections() -{ - dbgln("MultiProcessor: Get PCI IOAPIC redirections"); - Vector overrides; - auto pci_bus_ids = get_pci_bus_ids(); - for (auto& entry : m_io_interrupt_assignment_entries) { - for (auto id : pci_bus_ids) { - if (id == entry.source_bus_id) { - - dbgln("Interrupts: Bus {}, polarity {}, trigger mode {}, INT {}, IOAPIC {}, IOAPIC INTIN {}", - entry.source_bus_id, - entry.polarity, - entry.trigger_mode, - entry.source_bus_irq, - entry.destination_ioapic_id, - entry.destination_ioapic_intin_pin); - MUST(overrides.try_empend( - entry.source_bus_id, - entry.polarity, - entry.trigger_mode, - entry.source_bus_irq, - entry.destination_ioapic_id, - entry.destination_ioapic_intin_pin)); - } - } - } - - for (auto& override_metadata : overrides) { - dbgln("Interrupts: Bus {}, polarity {}, PCI device {}, trigger mode {}, INT {}, IOAPIC {}, IOAPIC INTIN {}", - override_metadata.bus(), - override_metadata.polarity(), - override_metadata.pci_device_number(), - override_metadata.trigger_mode(), - override_metadata.pci_interrupt_pin(), - override_metadata.ioapic_id(), - override_metadata.ioapic_interrupt_pin()); - } - return overrides; -} - -UNMAP_AFTER_INIT PCIInterruptOverrideMetadata::PCIInterruptOverrideMetadata(u8 bus_id, u8 polarity, u8 trigger_mode, u8 source_irq, u32 ioapic_id, u16 ioapic_int_pin) - : m_bus_id(bus_id) - , m_polarity(polarity) - , m_trigger_mode(trigger_mode) - , m_pci_interrupt_pin(source_irq & 0b11) - , m_pci_device_number((source_irq >> 2) & 0b11111) - , m_ioapic_id(ioapic_id) - , m_ioapic_interrupt_pin(ioapic_int_pin) -{ -} - -} diff --git a/Kernel/Arch/x86_64/Firmware/MultiProcessor/Parser.h b/Kernel/Arch/x86_64/Firmware/MultiProcessor/Parser.h deleted file mode 100644 index bd3644135a9..00000000000 --- a/Kernel/Arch/x86_64/Firmware/MultiProcessor/Parser.h +++ /dev/null @@ -1,171 +0,0 @@ -/* - * Copyright (c) 2020, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include - -namespace Kernel { -namespace MultiProcessor { - -struct [[gnu::packed]] FloatingPointer { - char sig[4]; - u32 physical_address_ptr; - u8 length; - u8 specification_revision; - u8 checksum; - u8 feature_info[5]; -}; - -struct [[gnu::packed]] EntryHeader { - u8 entry_type; -}; - -struct [[gnu::packed]] ConfigurationTableHeader { - char sig[4]; - u16 length; - u8 specification_revision; - u8 checksum; - char oem_id[8]; - char product_id[12]; - u32 oem_table_ptr; - u16 oem_table_size; - u16 entry_count; - u32 local_apic_address; - u16 ext_table_length; - u8 ext_table_checksum; - u8 reserved; - EntryHeader entries[]; -}; - -enum class ConfigurationTableEntryType { - Processor = 0, - Bus = 1, - IOAPIC = 2, - IO_Interrupt_Assignment = 3, - Local_Interrupt_Assignment = 4, - SystemAddressSpaceMapping = 128, - BusHierarchyDescriptor = 129, - CompatibilityBusAddressSpaceModifier = 130 -}; - -struct [[gnu::packed]] ExtEntryHeader { - u8 entry_type; - u8 entry_length; -}; - -struct [[gnu::packed]] ProcessorEntry { - EntryHeader h; - u8 local_apic_id; - u8 local_apic_version; - u8 cpu_flags; - u32 cpu_signature; - u32 feature_flags; - u8 reserved[8]; -}; - -struct [[gnu::packed]] BusEntry { - EntryHeader h; - u8 bus_id; - char bus_type[6]; -}; - -struct [[gnu::packed]] IOAPICEntry { - EntryHeader h; - u8 ioapic_id; - u8 ioapic_version; - u8 ioapic_flags; - u32 ioapic_address; -}; - -enum class InterruptType { - INT = 0, - NMI = 1, - SMI = 2, - ExtINT = 3, -}; - -struct [[gnu::packed]] IOInterruptAssignmentEntry { - EntryHeader h; - u8 interrupt_type; - u8 polarity; - u8 trigger_mode; - u8 source_bus_id; - u8 source_bus_irq; - u8 destination_ioapic_id; - u8 destination_ioapic_intin_pin; -}; - -struct [[gnu::packed]] LocalInterruptAssignmentEntry { - EntryHeader h; - u8 interrupt_type; - u8 polarity; - u8 trigger_mode; - u8 source_bus_id; - u8 source_bus_irq; - u8 destination_lapic_id; - u8 destination_lapic_lintin_pin; -}; - -enum class SystemAddressType { - IO = 0, - Memory = 1, - Prefetch = 2, -}; - -struct [[gnu::packed]] SystemAddressSpaceMappingEntry { - ExtEntryHeader h; - u8 bus_id; - u8 address_type; - u64 address_base; - u64 length; -}; - -struct [[gnu::packed]] BusHierarchyDescriptorEntry { - ExtEntryHeader h; - u8 bus_id; - u8 bus_info; - u8 parent_bus; - u8 reserved[3]; -}; - -struct [[gnu::packed]] CompatibilityBusAddressSpaceModifierEntry { - ExtEntryHeader h; - u8 bus_id; - u8 address_modifier; - u32 predefined_range_list; -}; - -} - -class PCIInterruptOverrideMetadata; - -class MultiProcessorParser final { -public: - static OwnPtr autodetect(); - - Vector get_pci_interrupt_redirections(); - -private: - explicit MultiProcessorParser(PhysicalAddress floating_pointer); - - void parse_configuration_table(); - void parse_floating_pointer_data(); - - Vector get_pci_bus_ids() const; - - static Optional find_floating_pointer(); - - PhysicalAddress m_floating_pointer; - PhysicalAddress m_configuration_table; - Vector m_io_interrupt_assignment_entries; - Vector m_bus_entries; -}; -} diff --git a/Kernel/Arch/x86_64/Firmware/PCBIOS/DMI/Definitions.h b/Kernel/Arch/x86_64/Firmware/PCBIOS/DMI/Definitions.h deleted file mode 100644 index 5f575aa2ffc..00000000000 --- a/Kernel/Arch/x86_64/Firmware/PCBIOS/DMI/Definitions.h +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright (c) 2022, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include - -namespace Kernel::SMBIOS { - -struct [[gnu::packed]] LegacyEntryPoint32bit { - char legacy_sig[5]; - u8 checksum2; - u16 smbios_table_length; - u32 smbios_table_ptr; - u16 smbios_tables_count; - u8 smbios_bcd_revision; -}; - -struct [[gnu::packed]] EntryPoint32bit { - char sig[4]; - u8 checksum; - u8 length; - u8 major_version; - u8 minor_version; - u16 maximum_structure_size; - u8 implementation_revision; - char formatted_area[5]; - LegacyEntryPoint32bit legacy_structure; -}; - -struct [[gnu::packed]] EntryPoint64bit { - char sig[5]; - u8 checksum; - u8 length; - u8 major_version; - u8 minor_version; - u8 document_revision; - u8 revision; - u8 reserved; - u32 table_maximum_size; - u64 table_ptr; -}; -} diff --git a/Kernel/Arch/x86_64/Firmware/PCBIOS/Mapper.cpp b/Kernel/Arch/x86_64/Firmware/PCBIOS/Mapper.cpp deleted file mode 100644 index acf2593425a..00000000000 --- a/Kernel/Arch/x86_64/Firmware/PCBIOS/Mapper.cpp +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright (c) 2020, Andreas Kling - * Copyright (c) 2022, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include - -namespace Kernel { - -ErrorOr map_bios() -{ - Memory::MappedROM mapping; - mapping.size = 128 * KiB; - mapping.paddr = PhysicalAddress(0xe0000); - auto region_size = TRY(Memory::page_round_up(mapping.size)); - mapping.region = TRY(MM.allocate_mmio_kernel_region(mapping.paddr, region_size, {}, Memory::Region::Access::Read)); - return mapping; -} - -ErrorOr map_ebda() -{ - auto ebda_segment_ptr = TRY(Memory::map_typed(PhysicalAddress(0x40e))); - PhysicalAddress ebda_paddr(PhysicalAddress(*ebda_segment_ptr).get() << 4); - // The EBDA size is stored in the first byte of the EBDA in 1K units - size_t ebda_size = *TRY(Memory::map_typed(ebda_paddr)); - ebda_size *= 1024; - - Memory::MappedROM mapping; - auto region_size = TRY(Memory::page_round_up(ebda_size)); - mapping.region = TRY(MM.allocate_mmio_kernel_region(ebda_paddr.page_base(), region_size, {}, Memory::Region::Access::Read)); - mapping.offset = ebda_paddr.offset_in_page(); - mapping.size = ebda_size; - mapping.paddr = ebda_paddr; - return mapping; -} - -} diff --git a/Kernel/Arch/x86_64/Firmware/PCBIOS/Mapper.h b/Kernel/Arch/x86_64/Firmware/PCBIOS/Mapper.h deleted file mode 100644 index 5571c9274b5..00000000000 --- a/Kernel/Arch/x86_64/Firmware/PCBIOS/Mapper.h +++ /dev/null @@ -1,20 +0,0 @@ -/* - * Copyright (c) 2020, Andreas Kling - * Copyright (c) 2022, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include - -namespace Kernel { - -ErrorOr map_bios(); -ErrorOr map_ebda(); - -} diff --git a/Kernel/Arch/x86_64/Firmware/PCBIOS/SysFSComponent.cpp b/Kernel/Arch/x86_64/Firmware/PCBIOS/SysFSComponent.cpp deleted file mode 100644 index 66f4ed131e6..00000000000 --- a/Kernel/Arch/x86_64/Firmware/PCBIOS/SysFSComponent.cpp +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright (c) 2022, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -namespace Kernel { - -NonnullRefPtr SysFSPCBIOSComponent::must_create(Type type, PhysicalAddress blob_paddr, size_t blob_size) -{ - return adopt_ref_if_nonnull(new (nothrow) SysFSPCBIOSComponent(type, blob_paddr, blob_size)).release_nonnull(); -} - -UNMAP_AFTER_INIT SysFSPCBIOSComponent::SysFSPCBIOSComponent(Type type, PhysicalAddress blob_paddr, size_t blob_size) - : SysFSComponent() - , m_blob_paddr(blob_paddr) - , m_blob_length(blob_size) - , m_type(type) -{ -} - -ErrorOr SysFSPCBIOSComponent::read_bytes(off_t offset, size_t count, UserOrKernelBuffer& buffer, OpenFileDescription*) const -{ - auto blob = TRY(try_to_generate_buffer()); - - if ((size_t)offset >= blob->size()) - return 0; - - ssize_t nread = min(static_cast(blob->size() - offset), static_cast(count)); - TRY(buffer.write(blob->data() + offset, nread)); - return nread; -} - -StringView SysFSPCBIOSComponent::name() const -{ - switch (m_type) { - case Type::DMIEntryPoint: - return "smbios_entry_point"sv; - case Type::SMBIOSTable: - return "DMI"sv; - default: - break; - } - VERIFY_NOT_REACHED(); -} - -ErrorOr> SysFSPCBIOSComponent::try_to_generate_buffer() const -{ - auto blob = TRY(Memory::map_typed((m_blob_paddr), m_blob_length)); - return KBuffer::try_create_with_bytes("SysFSPCBIOSComponent: Blob"sv, Span { blob.ptr(), m_blob_length }); -} -} diff --git a/Kernel/Arch/x86_64/Firmware/PCBIOS/SysFSComponent.h b/Kernel/Arch/x86_64/Firmware/PCBIOS/SysFSComponent.h deleted file mode 100644 index 2f287824c21..00000000000 --- a/Kernel/Arch/x86_64/Firmware/PCBIOS/SysFSComponent.h +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright (c) 2022, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include -#include -#include - -namespace Kernel { - -class SysFSPCBIOSComponent final : public SysFSComponent { -public: - enum class Type { - DMIEntryPoint, - SMBIOSTable, - }; - -public: - static NonnullRefPtr must_create(Type, PhysicalAddress, size_t blob_size); - virtual StringView name() const override; - virtual ErrorOr read_bytes(off_t, size_t, UserOrKernelBuffer&, OpenFileDescription*) const override; - -private: - ErrorOr> try_to_generate_buffer() const; - SysFSPCBIOSComponent(Type, PhysicalAddress, size_t blob_size); - - virtual size_t size() const override { return m_blob_length; } - - PhysicalAddress const m_blob_paddr; - size_t const m_blob_length { 0 }; - Type const m_type { Type::DMIEntryPoint }; -}; -} diff --git a/Kernel/Arch/x86_64/Firmware/PCBIOS/SysFSDirectory.cpp b/Kernel/Arch/x86_64/Firmware/PCBIOS/SysFSDirectory.cpp deleted file mode 100644 index 40987edb710..00000000000 --- a/Kernel/Arch/x86_64/Firmware/PCBIOS/SysFSDirectory.cpp +++ /dev/null @@ -1,113 +0,0 @@ -/* - * Copyright (c) 2022, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace Kernel { - -#define SMBIOS_BASE_SEARCH_ADDR 0xf0000 -#define SMBIOS_END_SEARCH_ADDR 0xfffff -#define SMBIOS_SEARCH_AREA_SIZE (SMBIOS_END_SEARCH_ADDR - SMBIOS_BASE_SEARCH_ADDR) - -UNMAP_AFTER_INIT void SysFSBIOSDirectory::set_dmi_64_bit_entry_initialization_values() -{ - dbgln("SysFSBIOSDirectory: SMBIOS 64bit Entry point @ {}", m_dmi_entry_point); - auto smbios_entry = Memory::map_typed(m_dmi_entry_point, SMBIOS_SEARCH_AREA_SIZE).release_value_but_fixme_should_propagate_errors(); - m_smbios_structure_table = PhysicalAddress(smbios_entry.ptr()->table_ptr); - m_dmi_entry_point_length = smbios_entry.ptr()->length; - m_smbios_structure_table_length = smbios_entry.ptr()->table_maximum_size; -} - -UNMAP_AFTER_INIT void SysFSBIOSDirectory::set_dmi_32_bit_entry_initialization_values() -{ - dbgln("SysFSBIOSDirectory: SMBIOS 32bit Entry point @ {}", m_dmi_entry_point); - auto smbios_entry = Memory::map_typed(m_dmi_entry_point, SMBIOS_SEARCH_AREA_SIZE).release_value_but_fixme_should_propagate_errors(); - m_smbios_structure_table = PhysicalAddress(smbios_entry.ptr()->legacy_structure.smbios_table_ptr); - m_dmi_entry_point_length = smbios_entry.ptr()->length; - m_smbios_structure_table_length = smbios_entry.ptr()->legacy_structure.smbios_table_length; -} - -UNMAP_AFTER_INIT NonnullRefPtr SysFSBIOSDirectory::must_create(SysFSFirmwareDirectory& firmware_directory) -{ - auto bios_directory = MUST(adopt_nonnull_ref_or_enomem(new (nothrow) SysFSBIOSDirectory(firmware_directory))); - bios_directory->create_components(); - return bios_directory; -} - -void SysFSBIOSDirectory::create_components() -{ - if (m_dmi_entry_point.is_null() || m_smbios_structure_table.is_null()) - return; - if (m_dmi_entry_point_length == 0) { - dbgln("SysFSBIOSDirectory: invalid dmi entry length"); - return; - } - if (m_smbios_structure_table_length == 0) { - dbgln("SysFSBIOSDirectory: invalid smbios structure table length"); - return; - } - MUST(m_child_components.with([&](auto& list) -> ErrorOr { - list.append(SysFSPCBIOSComponent::must_create(SysFSPCBIOSComponent::Type::DMIEntryPoint, m_dmi_entry_point, m_dmi_entry_point_length)); - list.append(SysFSPCBIOSComponent::must_create(SysFSPCBIOSComponent::Type::SMBIOSTable, m_smbios_structure_table, m_smbios_structure_table_length)); - return {}; - })); -} - -UNMAP_AFTER_INIT void SysFSBIOSDirectory::initialize_dmi_exposer() -{ - VERIFY(!(m_dmi_entry_point.is_null())); - if (m_using_64bit_dmi_entry_point.was_set()) { - set_dmi_64_bit_entry_initialization_values(); - } else { - set_dmi_32_bit_entry_initialization_values(); - } - dbgln("SysFSBIOSDirectory: Data table @ {}", m_smbios_structure_table); -} - -UNMAP_AFTER_INIT SysFSBIOSDirectory::SysFSBIOSDirectory(SysFSFirmwareDirectory& firmware_directory) - : SysFSDirectory(firmware_directory) -{ - auto entry_32bit = find_dmi_entry32bit_point(); - if (entry_32bit.has_value()) { - m_dmi_entry_point = entry_32bit.value(); - } - - auto entry_64bit = find_dmi_entry64bit_point(); - if (entry_64bit.has_value()) { - m_dmi_entry_point = entry_64bit.value(); - m_using_64bit_dmi_entry_point.set(); - } - if (m_dmi_entry_point.is_null()) - return; - initialize_dmi_exposer(); -} - -UNMAP_AFTER_INIT Optional SysFSBIOSDirectory::find_dmi_entry64bit_point() -{ - auto bios_or_error = map_bios(); - if (bios_or_error.is_error()) - return {}; - return bios_or_error.value().find_chunk_starting_with("_SM3_"sv, 16); -} - -UNMAP_AFTER_INIT Optional SysFSBIOSDirectory::find_dmi_entry32bit_point() -{ - auto bios_or_error = map_bios(); - if (bios_or_error.is_error()) - return {}; - return bios_or_error.value().find_chunk_starting_with("_SM_"sv, 16); -} - -} diff --git a/Kernel/Arch/x86_64/Firmware/PCBIOS/SysFSDirectory.h b/Kernel/Arch/x86_64/Firmware/PCBIOS/SysFSDirectory.h deleted file mode 100644 index 63b112f6d86..00000000000 --- a/Kernel/Arch/x86_64/Firmware/PCBIOS/SysFSDirectory.h +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright (c) 2022, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include -#include -#include - -namespace Kernel { - -class SysFSBIOSDirectory : public SysFSDirectory { -public: - virtual StringView name() const override { return "bios"sv; } - static NonnullRefPtr must_create(SysFSFirmwareDirectory&); - - void create_components(); - -private: - explicit SysFSBIOSDirectory(SysFSFirmwareDirectory&); - - void set_dmi_64_bit_entry_initialization_values(); - void set_dmi_32_bit_entry_initialization_values(); - void initialize_dmi_exposer(); - - Optional find_dmi_entry64bit_point(); - Optional find_dmi_entry32bit_point(); - - PhysicalAddress m_dmi_entry_point; - PhysicalAddress m_smbios_structure_table; - SetOnce m_using_64bit_dmi_entry_point; - size_t m_smbios_structure_table_length { 0 }; - size_t m_dmi_entry_point_length { 0 }; -}; - -} diff --git a/Kernel/Arch/x86_64/Hypervisor/BochsDisplayConnector.cpp b/Kernel/Arch/x86_64/Hypervisor/BochsDisplayConnector.cpp deleted file mode 100644 index fd40de1c441..00000000000 --- a/Kernel/Arch/x86_64/Hypervisor/BochsDisplayConnector.cpp +++ /dev/null @@ -1,179 +0,0 @@ -/* - * Copyright (c) 2022, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace Kernel { - -static void set_register_with_io(u16 index, u16 data) -{ - IO::out16(VBE_DISPI_IOPORT_INDEX, index); - IO::out16(VBE_DISPI_IOPORT_DATA, data); -} - -static u16 get_register_with_io(u16 index) -{ - IO::out16(VBE_DISPI_IOPORT_INDEX, index); - return IO::in16(VBE_DISPI_IOPORT_DATA); -} - -LockRefPtr BochsDisplayConnector::try_create_for_vga_isa_connector() -{ - VERIFY(PCI::Access::is_hardware_disabled()); - BochsDisplayConnector::IndexID index_id = get_register_with_io(0); - if (index_id != VBE_DISPI_ID5) - return {}; - - auto video_ram_64k_chunks_count = get_register_with_io(to_underlying(BochsDISPIRegisters::VIDEO_RAM_64K_CHUNKS_COUNT)); - if (video_ram_64k_chunks_count == 0 || video_ram_64k_chunks_count == 0xffff) { - dmesgln("Graphics: Bochs ISA VGA compatible adapter does not indicate amount of VRAM, default to 8 MiB"); - video_ram_64k_chunks_count = (8 * MiB) / (64 * KiB); - } else { - dmesgln("Graphics: Bochs ISA VGA compatible adapter indicates {} bytes of VRAM", video_ram_64k_chunks_count * (64 * KiB)); - } - - // Note: The default physical address for isa-vga framebuffer in QEMU is 0xE0000000. - // Since this is probably hardcoded at other OSes in their guest drivers, - // we can assume this is going to stay the same framebuffer physical address for - // this device and will not be changed in the future. - auto device_or_error = DeviceManagement::try_create_device(PhysicalAddress(0xE0000000), video_ram_64k_chunks_count * (64 * KiB)); - VERIFY(!device_or_error.is_error()); - auto connector = device_or_error.release_value(); - MUST(connector->create_attached_framebuffer_console()); - MUST(connector->initialize_edid_for_generic_monitor({})); - return connector; -} - -NonnullLockRefPtr BochsDisplayConnector::must_create(PhysicalAddress framebuffer_address, size_t framebuffer_resource_size, bool virtual_box_hardware) -{ - auto device_or_error = DeviceManagement::try_create_device(framebuffer_address, framebuffer_resource_size); - VERIFY(!device_or_error.is_error()); - auto connector = device_or_error.release_value(); - MUST(connector->create_attached_framebuffer_console()); - if (virtual_box_hardware) - MUST(connector->initialize_edid_for_generic_monitor(Array { 'V', 'B', 'X' })); - else - MUST(connector->initialize_edid_for_generic_monitor({})); - return connector; -} - -BochsDisplayConnector::BochsDisplayConnector(PhysicalAddress framebuffer_address, size_t framebuffer_resource_size) - : DisplayConnector(framebuffer_address, framebuffer_resource_size, false) -{ -} - -ErrorOr BochsDisplayConnector::create_attached_framebuffer_console() -{ - // We assume safe resolution is 1024x768x32 - m_framebuffer_console = Graphics::ContiguousFramebufferConsole::initialize(m_framebuffer_address.value(), 1024, 768, 1024 * sizeof(u32)); - GraphicsManagement::the().set_console(*m_framebuffer_console); - return {}; -} - -BochsDisplayConnector::IndexID BochsDisplayConnector::index_id() const -{ - return get_register_with_io(0); -} - -void BochsDisplayConnector::enable_console() -{ - VERIFY(m_control_lock.is_locked()); - VERIFY(m_framebuffer_console); - m_framebuffer_console->enable(); -} - -void BochsDisplayConnector::disable_console() -{ - VERIFY(m_control_lock.is_locked()); - VERIFY(m_framebuffer_console); - m_framebuffer_console->disable(); -} - -ErrorOr BochsDisplayConnector::flush_first_surface() -{ - return Error::from_errno(ENOTSUP); -} - -ErrorOr BochsDisplayConnector::set_safe_mode_setting() -{ - DisplayConnector::ModeSetting safe_mode_set { - .horizontal_stride = 1024 * sizeof(u32), - .pixel_clock_in_khz = 0, // Note: There's no pixel clock in paravirtualized hardware - .horizontal_active = 1024, - .horizontal_front_porch_pixels = 0, // Note: There's no horizontal_front_porch_pixels in paravirtualized hardware - .horizontal_sync_time_pixels = 0, // Note: There's no horizontal_sync_time_pixels in paravirtualized hardware - .horizontal_blank_pixels = 0, // Note: There's no horizontal_blank_pixels in paravirtualized hardware - .vertical_active = 768, - .vertical_front_porch_lines = 0, // Note: There's no vertical_front_porch_lines in paravirtualized hardware - .vertical_sync_time_lines = 0, // Note: There's no vertical_sync_time_lines in paravirtualized hardware - .vertical_blank_lines = 0, // Note: There's no vertical_blank_lines in paravirtualized hardware - .horizontal_offset = 0, - .vertical_offset = 0, - }; - return set_mode_setting(safe_mode_set); -} - -ErrorOr BochsDisplayConnector::set_mode_setting(ModeSetting const& mode_setting) -{ - SpinlockLocker locker(m_modeset_lock); - size_t width = mode_setting.horizontal_active; - size_t height = mode_setting.vertical_active; - - dbgln_if(BXVGA_DEBUG, "BochsDisplayConnector resolution registers set to - {}x{}", width, height); - - set_register_with_io(to_underlying(BochsDISPIRegisters::ENABLE), 0); - set_register_with_io(to_underlying(BochsDISPIRegisters::XRES), (u16)width); - set_register_with_io(to_underlying(BochsDISPIRegisters::YRES), (u16)height); - set_register_with_io(to_underlying(BochsDISPIRegisters::VIRT_WIDTH), (u16)width); - set_register_with_io(to_underlying(BochsDISPIRegisters::VIRT_HEIGHT), (u16)height * 2); - set_register_with_io(to_underlying(BochsDISPIRegisters::BPP), 32); - set_register_with_io(to_underlying(BochsDISPIRegisters::ENABLE), to_underlying(BochsFramebufferSettings::Enabled) | to_underlying(BochsFramebufferSettings::LinearFramebuffer)); - set_register_with_io(to_underlying(BochsDISPIRegisters::BANK), 0); - if ((u16)width != get_register_with_io(to_underlying(BochsDISPIRegisters::XRES)) || (u16)height != get_register_with_io(to_underlying(BochsDISPIRegisters::YRES))) { - return Error::from_errno(ENOTIMPL); - } - auto current_horizontal_active = get_register_with_io(to_underlying(BochsDISPIRegisters::XRES)); - auto current_vertical_active = get_register_with_io(to_underlying(BochsDISPIRegisters::YRES)); - DisplayConnector::ModeSetting mode_set { - .horizontal_stride = current_horizontal_active * sizeof(u32), - .pixel_clock_in_khz = 0, // Note: There's no pixel clock in paravirtualized hardware - .horizontal_active = current_horizontal_active, - .horizontal_front_porch_pixels = 0, // Note: There's no horizontal_front_porch_pixels in paravirtualized hardware - .horizontal_sync_time_pixels = 0, // Note: There's no horizontal_sync_time_pixels in paravirtualized hardware - .horizontal_blank_pixels = 0, // Note: There's no horizontal_blank_pixels in paravirtualized hardware - .vertical_active = current_vertical_active, - .vertical_front_porch_lines = 0, // Note: There's no vertical_front_porch_lines in paravirtualized hardware - .vertical_sync_time_lines = 0, // Note: There's no vertical_sync_time_lines in paravirtualized hardware - .vertical_blank_lines = 0, // Note: There's no vertical_blank_lines in paravirtualized hardware - .horizontal_offset = 0, - .vertical_offset = 0, - }; - m_current_mode_setting = mode_set; - return {}; -} - -ErrorOr BochsDisplayConnector::set_y_offset(size_t) -{ - // Note: Although when using this device on QEMU we can actually set the horizontal and vertical offsets - // with IO ports, this class is meant to be used for plain old Bochs graphics which might not support - // this feature at all. - return Error::from_errno(ENOTIMPL); -} - -ErrorOr BochsDisplayConnector::unblank() -{ - return Error::from_errno(ENOTIMPL); -} - -} diff --git a/Kernel/Arch/x86_64/Hypervisor/BochsDisplayConnector.h b/Kernel/Arch/x86_64/Hypervisor/BochsDisplayConnector.h deleted file mode 100644 index 43817000d98..00000000000 --- a/Kernel/Arch/x86_64/Hypervisor/BochsDisplayConnector.h +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright (c) 2022, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include -#include -#include - -namespace Kernel { - -class BochsDisplayConnector - : public DisplayConnector { - friend class BochsGraphicsAdapter; - friend class DeviceManagement; - friend class GraphicsManagement; - -public: - AK_TYPEDEF_DISTINCT_ORDERED_ID(u16, IndexID); - - static LockRefPtr try_create_for_vga_isa_connector(); - - static NonnullLockRefPtr must_create(PhysicalAddress framebuffer_address, size_t framebuffer_resource_size, bool virtual_box_hardware); - -private: - IndexID index_id() const; - - ErrorOr create_attached_framebuffer_console(); - - BochsDisplayConnector(PhysicalAddress framebuffer_address, size_t framebuffer_resource_size); - - virtual bool mutable_mode_setting_capable() const override final { return true; } - virtual bool double_framebuffering_capable() const override { return false; } - virtual ErrorOr set_mode_setting(ModeSetting const&) override; - virtual ErrorOr set_safe_mode_setting() override final; - virtual ErrorOr set_y_offset(size_t y) override; - virtual ErrorOr unblank() override; - - virtual bool partial_flush_support() const override final { return false; } - virtual bool flush_support() const override final { return false; } - // Note: Paravirtualized hardware doesn't require a defined refresh rate for modesetting. - virtual bool refresh_rate_support() const override final { return false; } - - virtual ErrorOr flush_first_surface() override final; - - virtual void enable_console() override final; - virtual void disable_console() override final; - - LockRefPtr m_framebuffer_console; -}; -} diff --git a/Kernel/Arch/x86_64/Hypervisor/VMWareBackdoor.cpp b/Kernel/Arch/x86_64/Hypervisor/VMWareBackdoor.cpp deleted file mode 100644 index 34810ff8368..00000000000 --- a/Kernel/Arch/x86_64/Hypervisor/VMWareBackdoor.cpp +++ /dev/null @@ -1,247 +0,0 @@ -/* - * Copyright (c) 2020, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -namespace Kernel { - -#define VMWARE_CMD_GETVERSION 0x0a - -#define VMMOUSE_READ_ID 0x45414552 -#define VMMOUSE_DISABLE 0x000000f5 -#define VMMOUSE_REQUEST_RELATIVE 0x4c455252 -#define VMMOUSE_REQUEST_ABSOLUTE 0x53424152 - -#define VMMOUSE_QEMU_VERSION 0x3442554a -#define VMMOUSE_LEFT_CLICK 0x20 -#define VMMOUSE_RIGHT_CLICK 0x10 -#define VMMOUSE_MIDDLE_CLICK 0x08 - -#define VMWARE_MAGIC 0x564D5868 -#define VMWARE_PORT 0x5658 -#define VMWARE_PORT_HIGHBANDWIDTH 0x5659 - -inline void vmware_out(VMWareCommand& command) -{ - command.magic = VMWARE_MAGIC; - command.port = VMWARE_PORT; - command.si = 0; - command.di = 0; - asm volatile("in %%dx, %0" - : "+a"(command.ax), "+b"(command.bx), "+c"(command.cx), "+d"(command.dx), "+S"(command.si), "+D"(command.di)); -} - -inline void vmware_high_bandwidth_send(VMWareCommand& command) -{ - command.magic = VMWARE_MAGIC; - command.port = VMWARE_PORT_HIGHBANDWIDTH; - - asm volatile("cld; rep; outsb" - : "+a"(command.ax), "+b"(command.bx), "+c"(command.cx), "+d"(command.dx), "+S"(command.si), "+D"(command.di)); -} - -inline void vmware_high_bandwidth_get(VMWareCommand& command) -{ - command.magic = VMWARE_MAGIC; - command.port = VMWARE_PORT_HIGHBANDWIDTH; - asm volatile("cld; rep; insb" - : "+a"(command.ax), "+b"(command.bx), "+c"(command.cx), "+d"(command.dx), "+S"(command.si), "+D"(command.di)); -} - -class VMWareBackdoorDetector { -public: - VMWareBackdoorDetector() - { - if (detect_presence()) - m_backdoor = adopt_nonnull_own_or_enomem(new (nothrow) VMWareBackdoor()).release_value_but_fixme_should_propagate_errors(); - } - - VMWareBackdoor* get_instance() - { - return m_backdoor.ptr(); - } - -private: - static bool detect_presence() - { - VMWareCommand command; - command.bx = ~VMWARE_MAGIC; - command.command = VMWARE_CMD_GETVERSION; - vmware_out(command); - if (command.bx != VMWARE_MAGIC || command.ax == 0xFFFFFFFF) - return false; - return true; - } - - OwnPtr m_backdoor; -}; - -static Singleton s_vmware_backdoor; - -VMWareBackdoor* VMWareBackdoor::the() -{ - return s_vmware_backdoor->get_instance(); -} - -UNMAP_AFTER_INIT VMWareBackdoor::VMWareBackdoor() -{ - if (kernel_command_line().is_vmmouse_enabled()) - enable_absolute_vmmouse(); -} - -bool VMWareBackdoor::detect_vmmouse() -{ - VMWareCommand command; - command.bx = VMMOUSE_READ_ID; - command.command = VMMOUSE_COMMAND; - send(command); - command.bx = 1; - command.command = VMMOUSE_DATA; - send(command); - if (command.ax != VMMOUSE_QEMU_VERSION) - return false; - return true; -} -bool VMWareBackdoor::vmmouse_is_absolute() const -{ - return m_vmmouse_absolute; -} - -void VMWareBackdoor::enable_absolute_vmmouse() -{ - InterruptDisabler disabler; - if (!detect_vmmouse()) - return; - dmesgln("VMWareBackdoor: Enabling absolute mouse mode"); - - VMWareCommand command; - - command.bx = 0; - command.command = VMMOUSE_STATUS; - send(command); - if (command.ax == 0xFFFF0000) { - dmesgln("VMWareBackdoor: VMMOUSE_STATUS got bad status"); - return; - } - - // Enable absolute vmmouse - command.bx = VMMOUSE_REQUEST_ABSOLUTE; - command.command = VMMOUSE_COMMAND; - send(command); - m_vmmouse_absolute = true; -} -void VMWareBackdoor::disable_absolute_vmmouse() -{ - InterruptDisabler disabler; - VMWareCommand command; - command.bx = VMMOUSE_REQUEST_RELATIVE; - command.command = VMMOUSE_COMMAND; - send(command); - m_vmmouse_absolute = false; -} - -void VMWareBackdoor::send_high_bandwidth(VMWareCommand& command) -{ - vmware_high_bandwidth_send(command); - - dbgln_if(VMWARE_BACKDOOR_DEBUG, "VMWareBackdoor Command High bandwidth Send Results: EAX {:#x} EBX {:#x} ECX {:#x} EDX {:#x}", - command.ax, - command.bx, - command.cx, - command.dx); -} - -void VMWareBackdoor::get_high_bandwidth(VMWareCommand& command) -{ - vmware_high_bandwidth_get(command); - - dbgln_if(VMWARE_BACKDOOR_DEBUG, "VMWareBackdoor Command High bandwidth Get Results: EAX {:#x} EBX {:#x} ECX {:#x} EDX {:#x}", - command.ax, - command.bx, - command.cx, - command.dx); -} - -void VMWareBackdoor::send(VMWareCommand& command) -{ - vmware_out(command); - - dbgln_if(VMWARE_BACKDOOR_DEBUG, "VMWareBackdoor Command Send Results: EAX {:#x} EBX {:#x} ECX {:#x} EDX {:#x}", - command.ax, - command.bx, - command.cx, - command.dx); -} - -u16 VMWareBackdoor::read_mouse_status_queue_size() -{ - VMWareCommand command; - command.bx = 0; - command.command = VMMOUSE_STATUS; - send(command); - - if (command.ax == 0xFFFF0000) { - dbgln_if(PS2MOUSE_DEBUG, "PS2MouseDevice: Resetting VMWare mouse"); - disable_absolute_vmmouse(); - enable_absolute_vmmouse(); - return 0; - } - - return command.ax & 0xFFFF; -} - -MousePacket VMWareBackdoor::receive_mouse_packet() -{ - VMWareCommand command; - command.size = 4; - command.command = VMMOUSE_DATA; - send(command); - - int buttons = (command.ax & 0xFFFF); - int x = command.bx; - int y = command.cx; - int z = static_cast(command.dx); // signed 8 bit value only! - int w = 0; - - // horizontal scroll is reported as +-2 by qemu - // FIXME: Scroll only functions correctly when the sign is flipped there - if (z == 2) { - w = -1; - z = 0; - } else if (z == -2) { - w = 1; - z = 0; - } - - if constexpr (PS2MOUSE_DEBUG) { - dbgln("Absolute Mouse: Buttons {:x}", buttons); - dbgln("Mouse: x={}, y={}, z={}, w={}", x, y, z, w); - } - - MousePacket packet; - packet.x = x; - packet.y = y; - packet.z = z; - packet.w = w; - if (buttons & VMMOUSE_LEFT_CLICK) - packet.buttons |= MousePacket::LeftButton; - if (buttons & VMMOUSE_RIGHT_CLICK) - packet.buttons |= MousePacket::RightButton; - if (buttons & VMMOUSE_MIDDLE_CLICK) - packet.buttons |= MousePacket::MiddleButton; - - packet.is_relative = false; - return packet; -} - -} diff --git a/Kernel/Arch/x86_64/Hypervisor/VMWareBackdoor.h b/Kernel/Arch/x86_64/Hypervisor/VMWareBackdoor.h deleted file mode 100644 index a76aa8d8a2f..00000000000 --- a/Kernel/Arch/x86_64/Hypervisor/VMWareBackdoor.h +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright (c) 2020, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include - -namespace Kernel { - -#define VMMOUSE_GETVERSION 10 -#define VMMOUSE_DATA 39 -#define VMMOUSE_STATUS 40 -#define VMMOUSE_COMMAND 41 - -struct VMWareCommand { - union { - u32 ax; - u32 magic; - }; - union { - u32 bx; - u32 size; - }; - union { - u32 cx; - u32 command; - }; - union { - u32 dx; - u32 port; - }; - u32 si; - u32 di; -}; - -class VMWareBackdoor { - -public: - VMWareBackdoor(); - static VMWareBackdoor* the(); - - bool vmmouse_is_absolute() const; - void enable_absolute_vmmouse(); - void disable_absolute_vmmouse(); - void send(VMWareCommand& command); - - u16 read_mouse_status_queue_size(); - MousePacket receive_mouse_packet(); - -private: - void send_high_bandwidth(VMWareCommand& command); - void get_high_bandwidth(VMWareCommand& command); - bool detect_vmmouse(); - bool m_vmmouse_absolute { false }; -}; - -} diff --git a/Kernel/Arch/x86_64/I8042Reboot.cpp b/Kernel/Arch/x86_64/I8042Reboot.cpp deleted file mode 100644 index 6f987d280f5..00000000000 --- a/Kernel/Arch/x86_64/I8042Reboot.cpp +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Copyright (c) 2022, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include - -namespace Kernel { - -void i8042_reboot() -{ - dbgln("attempting reboot via KB Controller..."); - IO::out8(0x64, 0xFE); -} - -} diff --git a/Kernel/Arch/x86_64/I8042Reboot.h b/Kernel/Arch/x86_64/I8042Reboot.h deleted file mode 100644 index 9c457a0f1ad..00000000000 --- a/Kernel/Arch/x86_64/I8042Reboot.h +++ /dev/null @@ -1,13 +0,0 @@ -/* - * Copyright (c) 2022, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -namespace Kernel { - -void i8042_reboot(); - -} diff --git a/Kernel/Arch/x86_64/IO.h b/Kernel/Arch/x86_64/IO.h deleted file mode 100644 index 79a7b92a4c8..00000000000 --- a/Kernel/Arch/x86_64/IO.h +++ /dev/null @@ -1,153 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include - -#include -VALIDATE_IS_X86() - -namespace IO { - -// Every character written to this IO port is written to the Bochs console -// (e.g. the console where Qemu is running). -static constexpr u16 BOCHS_DEBUG_PORT = 0xE9; - -inline u8 in8(u16 port) -{ - u8 value; - asm volatile("inb %1, %0" - : "=a"(value) - : "Nd"(port)); - return value; -} - -inline u16 in16(u16 port) -{ - u16 value; - asm volatile("inw %1, %0" - : "=a"(value) - : "Nd"(port)); - return value; -} - -inline u32 in32(u16 port) -{ - u32 value; - asm volatile("inl %1, %0" - : "=a"(value) - : "Nd"(port)); - return value; -} - -inline void out8(u16 port, u8 value) -{ - asm volatile("outb %0, %1" ::"a"(value), "Nd"(port)); -} - -inline void out16(u16 port, u16 value) -{ - asm volatile("outw %0, %1" ::"a"(value), "Nd"(port)); -} - -inline void out32(u16 port, u32 value) -{ - asm volatile("outl %0, %1" ::"a"(value), "Nd"(port)); -} - -inline void delay(size_t microseconds) -{ - for (size_t i = 0; i < microseconds; ++i) - IO::in8(0x80); -} - -} - -class IOAddress { -public: - IOAddress() = default; - explicit IOAddress(u16 address) - : m_address(address) - { - } - - IOAddress offset(u16 o) const { return IOAddress(m_address + o); } - u16 get() const { return m_address; } - void set(u16 address) { m_address = address; } - void mask(u16 m) { m_address &= m; } - - template - ALWAYS_INLINE T in() - { - static_assert(sizeof(T) <= 4); - if constexpr (sizeof(T) == 4) - return IO::in32(get()); - if constexpr (sizeof(T) == 2) - return IO::in16(get()); - if constexpr (sizeof(T) == 1) - return IO::in8(get()); - VERIFY_NOT_REACHED(); - } - - template - ALWAYS_INLINE void out(T value) const - { - static_assert(sizeof(T) <= 4); - if constexpr (sizeof(T) == 4) { - IO::out32(get(), value); - return; - } - if constexpr (sizeof(T) == 2) { - IO::out16(get(), value); - return; - } - if constexpr (sizeof(T) == 1) { - IO::out8(get(), value); - return; - } - VERIFY_NOT_REACHED(); - } - - inline void out(u32 value, u8 bit_width) const - { - if (bit_width == 32) { - IO::out32(get(), value); - return; - } - if (bit_width == 16) { - IO::out16(get(), value); - return; - } - if (bit_width == 8) { - IO::out8(get(), value); - return; - } - VERIFY_NOT_REACHED(); - } - - bool is_null() const { return m_address == 0; } - - bool operator==(IOAddress const& other) const { return m_address == other.m_address; } - bool operator!=(IOAddress const& other) const { return m_address != other.m_address; } - bool operator>(IOAddress const& other) const { return m_address > other.m_address; } - bool operator>=(IOAddress const& other) const { return m_address >= other.m_address; } - bool operator<(IOAddress const& other) const { return m_address < other.m_address; } - bool operator<=(IOAddress const& other) const { return m_address <= other.m_address; } - -private: - u16 m_address { 0 }; -}; - -template<> -struct AK::Formatter : AK::Formatter { - ErrorOr format(FormatBuilder& builder, IOAddress value) - { - return Formatter::format(builder, "IO {:x}"sv, value.get()); - } -}; diff --git a/Kernel/Arch/x86_64/IRQController.h b/Kernel/Arch/x86_64/IRQController.h deleted file mode 100644 index 64e969033c3..00000000000 --- a/Kernel/Arch/x86_64/IRQController.h +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright (c) 2020, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include - -namespace Kernel { - -class GenericInterruptHandler; - -enum class IRQControllerType { - i8259 = 1, /* Intel 8259 Dual PIC */ - i82093AA = 2 /* Intel 82093AA I/O ADVANCED PROGRAMMABLE INTERRUPT CONTROLLER (IOAPIC) */ -}; - -class IRQController : public AtomicRefCounted { -public: - virtual ~IRQController() = default; - - virtual void enable(GenericInterruptHandler const&) = 0; - virtual void disable(GenericInterruptHandler const&) = 0; - virtual void hard_disable() { m_hard_disabled = true; } - virtual bool is_vector_enabled(u8 number) const = 0; - virtual bool is_enabled() const = 0; - bool is_hard_disabled() const { return m_hard_disabled; } - virtual void eoi(GenericInterruptHandler const&) const = 0; - virtual void spurious_eoi(GenericInterruptHandler const&) const = 0; - virtual size_t interrupt_vectors_count() const = 0; - virtual u32 gsi_base() const = 0; - virtual u16 get_isr() const = 0; - virtual u16 get_irr() const = 0; - virtual StringView model() const = 0; - virtual IRQControllerType type() const = 0; - -protected: - IRQController() = default; - virtual void initialize() = 0; - bool m_hard_disabled { false }; -}; -} diff --git a/Kernel/Arch/x86_64/ISABus/HID/VMWareMouseDevice.cpp b/Kernel/Arch/x86_64/ISABus/HID/VMWareMouseDevice.cpp deleted file mode 100644 index 3915a69b354..00000000000 --- a/Kernel/Arch/x86_64/ISABus/HID/VMWareMouseDevice.cpp +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright (c) 2021, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include - -namespace Kernel { - -UNMAP_AFTER_INIT ErrorOr> VMWareMouseDevice::try_to_initialize(SerialIOController const& serial_io_controller, SerialIOController::PortIndex port_index, MouseDevice const& mouse_device) -{ - // FIXME: return the correct error - if (!VMWareBackdoor::the()) - return Error::from_errno(EIO); - if (!VMWareBackdoor::the()->vmmouse_is_absolute()) - return Error::from_errno(EIO); - auto device = TRY(adopt_nonnull_own_or_enomem(new (nothrow) VMWareMouseDevice(serial_io_controller, port_index, mouse_device))); - TRY(device->initialize()); - return device; -} - -void VMWareMouseDevice::handle_byte_read_from_serial_input(u8) -{ - auto backdoor = VMWareBackdoor::the(); - VERIFY(backdoor); - VERIFY(backdoor->vmmouse_is_absolute()); - - // We will receive 4 bytes from the I8042 controller that we are going to - // ignore. Instead, we will check with VMWareBackdoor to see how many bytes - // of mouse event data are waiting for us. For each multiple of 4, we - // produce a mouse packet. - constexpr u8 max_iterations = 128; - u8 current_iteration = 0; - while (++current_iteration < max_iterations) { - auto number_of_mouse_event_bytes = backdoor->read_mouse_status_queue_size(); - if (number_of_mouse_event_bytes == 0) - break; - VERIFY(number_of_mouse_event_bytes % 4 == 0); - - auto mouse_packet = backdoor->receive_mouse_packet(); - m_mouse_device->handle_mouse_packet_input_event(mouse_packet); - } -} - -VMWareMouseDevice::VMWareMouseDevice(SerialIOController const& serial_io_controller, SerialIOController::PortIndex port_index, MouseDevice const& mouse_device) - : PS2MouseDevice(serial_io_controller, port_index, mouse_device) -{ -} -VMWareMouseDevice::~VMWareMouseDevice() = default; - -} diff --git a/Kernel/Arch/x86_64/ISABus/HID/VMWareMouseDevice.h b/Kernel/Arch/x86_64/ISABus/HID/VMWareMouseDevice.h deleted file mode 100644 index 6c9d8b19d54..00000000000 --- a/Kernel/Arch/x86_64/ISABus/HID/VMWareMouseDevice.h +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright (c) 2021, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include - -namespace Kernel { - -class VMWareMouseDevice final : public PS2MouseDevice { -public: - friend class DeviceManagement; - static ErrorOr> try_to_initialize(SerialIOController const&, SerialIOController::PortIndex, MouseDevice const&); - virtual ~VMWareMouseDevice() override; - - // ^PS2Device - virtual void handle_byte_read_from_serial_input(u8 byte) override; - -private: - VMWareMouseDevice(SerialIOController const&, SerialIOController::PortIndex, MouseDevice const&); -}; - -} diff --git a/Kernel/Arch/x86_64/ISABus/I8042Controller.cpp b/Kernel/Arch/x86_64/ISABus/I8042Controller.cpp deleted file mode 100644 index 1155d783ac8..00000000000 --- a/Kernel/Arch/x86_64/ISABus/I8042Controller.cpp +++ /dev/null @@ -1,497 +0,0 @@ -/* - * Copyright (c) 2020, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace Kernel { - -#define IRQ_FIRST_PORT 1 -#define IRQ_SECOND_PORT 12 - -UNMAP_AFTER_INIT ErrorOr> I8042ControllerIRQHandler::try_create(I8042Controller const& controller, u8 irq_number) -{ - return adopt_nonnull_own_or_enomem(new I8042ControllerIRQHandler(controller, irq_number)); -} - -bool I8042ControllerIRQHandler::handle_irq(RegisterState const&) -{ - return m_controller->handle_irq({}, interrupt_number()); -} - -UNMAP_AFTER_INIT I8042ControllerIRQHandler::I8042ControllerIRQHandler(I8042Controller const& controller, u8 irq_number) - : IRQHandler(irq_number) - , m_controller(controller) -{ -} - -UNMAP_AFTER_INIT ErrorOr> I8042Controller::create() -{ - return TRY(adopt_nonnull_ref_or_enomem(new (nothrow) I8042Controller())); -} - -UNMAP_AFTER_INIT I8042Controller::I8042Controller() -{ -} - -bool I8042Controller::handle_irq(Badge, u8 irq_number) -{ - // NOTE: The controller will read the data and call handle_byte_read_from_serial_input - // for the appropriate device. - VERIFY(irq_number == IRQ_FIRST_PORT || irq_number == IRQ_SECOND_PORT); - PortIndex port_index = I8042PortIndex::FirstPort; - if (irq_number == IRQ_SECOND_PORT) { - port_index = I8042PortIndex::SecondPort; - } - return irq_process_input_buffer(port_index); -} - -UNMAP_AFTER_INIT bool I8042Controller::check_existence_via_probing(Badge) -{ - { - u8 configuration = 0; - SpinlockLocker lock(m_lock); - - // This drains the output buffer and serves as an existence test. - if (auto result = drain_output_buffer(); result.is_error()) { - dbgln("I8042: Trying to flush output buffer as an existence test failed, error {}", result.error()); - return false; - } - - // Note: Perform controller self-test before touching the controller - // Try to probe the controller for 10 times and give up if nothing - // responded. - // Some controllers will reset and behave abnormally on this, so let's ensure - // we keep the configuration before initiating this command. - - if (auto result = do_wait_then_write(I8042Port::Command, I8042Command::ReadConfiguration); result.is_error()) { - dbgln("I8042: Trying to read configuration failed during the existence test, error {}", result.error()); - return false; - } - - { - auto result = do_wait_then_read_any_input(I8042Port::Buffer); - if (result.is_error()) { - dbgln("I8042: Trying to read configuration failed during the existence test, error {}", result.error()); - return false; - } - configuration = result.release_value(); - } - - bool successful_self_test = false; - for (int attempt = 0; attempt < 20; attempt++) { - do_write(I8042Port::Command, I8042Command::TestPS2Controller); - if (do_read(I8042Port::Buffer) == I8042Response::ControllerTestPassed) { - successful_self_test = true; - break; - } - // Note: Wait 500 microseconds in case the controller couldn't respond - microseconds_delay(500); - } - if (!successful_self_test) { - dbgln("I8042: Trying to probe for existence of controller failed"); - return false; - } - - if (auto result = do_wait_then_write(I8042Port::Command, I8042Command::WriteConfiguration); result.is_error()) { - dbgln("I8042: Trying to restore configuration after self-test failed with error {}", result.error()); - return false; - } - - if (auto result = do_wait_then_write(I8042Port::Buffer, configuration); result.is_error()) { - dbgln("I8042: Trying to write restored configuration after self-test failed with error {}", result.error()); - return false; - } - - return true; - } -} - -UNMAP_AFTER_INIT ErrorOr I8042Controller::detect_devices(EnableKeyboardFirstPortTranslation enable_first_port_translation) -{ - u8 configuration; - { - SpinlockLocker lock(m_lock); - // Note: This flushes all the garbage left in the controller registers - TRY(drain_output_buffer()); - - TRY(do_wait_then_write(I8042Port::Command, I8042Command::DisableFirstPS2Port)); - TRY(do_wait_then_write(I8042Port::Command, I8042Command::DisableSecondPS2Port)); // ignored if it doesn't exist - - TRY(do_wait_then_write(I8042Port::Command, I8042Command::ReadConfiguration)); - configuration = TRY(do_wait_then_read_any_input(I8042Port::Buffer)); - configuration &= ~I8042ConfigurationFlag::FirstPS2PortInterrupt; - configuration &= ~I8042ConfigurationFlag::SecondPS2PortInterrupt; - - // FIXME: Don't enable translation for the first i8042 port if nothing is connected - // or even worse - a mouse device, because we will get garbage data. - if (enable_first_port_translation == EnableKeyboardFirstPortTranslation::Yes) - configuration |= I8042ConfigurationFlag::FirstPS2PortTranslation; - else - configuration &= ~I8042ConfigurationFlag::FirstPS2PortTranslation; - - TRY(do_wait_then_write(I8042Port::Command, I8042Command::WriteConfiguration)); - TRY(do_wait_then_write(I8042Port::Buffer, configuration)); - - // Perform controller self-test - TRY(do_wait_then_write(I8042Port::Command, I8042Command::TestPS2Controller)); - auto self_test_result = TRY(do_wait_then_read_any_input(I8042Port::Buffer)); - if (self_test_result == I8042Response::ControllerTestPassed) { - // Restore configuration in case the controller reset - TRY(do_wait_then_write(I8042Port::Command, I8042Command::WriteConfiguration)); - TRY(do_wait_then_write(I8042Port::Buffer, configuration)); - } else { - dbgln("I8042: Controller self test failed"); - return Error::from_errno(EIO); - } - - m_is_dual_channel = (configuration & I8042ConfigurationFlag::SecondPS2PortClock) != 0; - dbgln("I8042: {} channel controller", m_is_dual_channel ? "Dual" : "Single"); - - // Test ports and enable them if available - TRY(do_wait_then_write(I8042Port::Command, I8042Command::TestFirstPS2Port)); - auto first_port_test_result = TRY(do_wait_then_read_any_input(I8042Port::Buffer)); - m_first_port_available = (first_port_test_result == 0); - - if (m_first_port_available) { - TRY(do_wait_then_write(I8042Port::Command, I8042Command::EnableFirstPS2Port)); - configuration |= I8042ConfigurationFlag::FirstPS2PortInterrupt; - configuration &= ~I8042ConfigurationFlag::FirstPS2PortClock; - } else { - dbgln("I8042: Keyboard port not available"); - } - - TRY(drain_output_buffer()); - - if (m_is_dual_channel) { - TRY(do_wait_then_write(I8042Port::Command, I8042Command::TestSecondPS2Port)); - auto test_second_port_result = TRY(do_wait_then_read_any_input(I8042Port::Buffer)); - m_second_port_available = (test_second_port_result == 0); - if (m_second_port_available) { - TRY(do_wait_then_write(I8042Port::Command, I8042Command::EnableSecondPS2Port)); - configuration |= I8042ConfigurationFlag::SecondPS2PortInterrupt; - configuration &= ~I8042ConfigurationFlag::SecondPS2PortClock; - } else { - dbgln("I8042: Mouse port not available"); - } - } - - // Enable IRQs for the ports that are usable - if (m_first_port_available || m_second_port_available) { - configuration &= ~I8042ConfigurationFlag::FirstPS2PortClock; - configuration &= ~I8042ConfigurationFlag::SecondPS2PortClock; - TRY(do_wait_then_write(I8042Port::Command, I8042Command::WriteConfiguration)); - TRY(do_wait_then_write(I8042Port::Buffer, configuration)); - } - } - - // Try to detect and initialize the devices - if (m_first_port_available) { - // FIXME: Actually figure out the connected PS2 device type - m_first_ps2_port.device_type = PS2DeviceType::StandardKeyboard; - auto keyboard_device = TRY(KeyboardDevice::try_to_initialize()); - // FIXME: Determine if the user wants to operate in scan code set 3. - auto keyboard_device_scan_code_set = enable_first_port_translation == EnableKeyboardFirstPortTranslation::Yes ? ScanCodeSet::Set1 : ScanCodeSet::Set2; - auto error_or_device = PS2KeyboardDevice::try_to_initialize(*this, I8042PortIndex::FirstPort, keyboard_device_scan_code_set, *keyboard_device); - if (error_or_device.is_error()) { - dbgln("I8042: Keyboard device failed to initialize, disable"); - m_first_port_available = false; - configuration &= ~I8042ConfigurationFlag::FirstPS2PortInterrupt; - configuration |= I8042ConfigurationFlag::FirstPS2PortClock; - m_first_ps2_port.device = nullptr; - m_first_ps2_port.device_type = {}; - SpinlockLocker lock(m_lock); - // NOTE: Before setting the actual scan code set, stop packet streaming entirely. - TRY(do_send_command(I8042PortIndex::FirstPort, I8042Command::DisablePacketStreaming)); - TRY(do_wait_then_write(I8042Port::Buffer, I8042Command::SetScanCodeSet)); - TRY(do_wait_then_write(I8042Port::Buffer, 0x2)); - - TRY(do_wait_then_write(I8042Port::Command, I8042Command::WriteConfiguration)); - TRY(do_wait_then_write(I8042Port::Buffer, configuration)); - } else { - m_first_ps2_port.device = error_or_device.release_value(); - } - } - if (m_second_port_available && !kernel_command_line().disable_ps2_mouse()) { - // FIXME: Actually figure out the connected PS2 device type - m_second_ps2_port.device_type = PS2DeviceType::StandardMouse; - auto mouse_device = TRY(MouseDevice::try_to_initialize()); - auto vmmouse_device_or_error = VMWareMouseDevice::try_to_initialize(*this, I8042PortIndex::SecondPort, *mouse_device); - if (vmmouse_device_or_error.is_error()) { - // FIXME: is there something to do with the VMWare errors? - auto mouse_device_or_error = PS2MouseDevice::try_to_initialize(*this, I8042PortIndex::SecondPort, *mouse_device); - if (mouse_device_or_error.is_error()) { - dbgln("I8042: Mouse device failed to initialize, disable"); - m_second_port_available = false; - configuration |= I8042ConfigurationFlag::SecondPS2PortClock; - m_second_ps2_port.device = nullptr; - m_second_ps2_port.device_type = {}; - SpinlockLocker lock(m_lock); - TRY(do_wait_then_write(I8042Port::Command, I8042Command::WriteConfiguration)); - TRY(do_wait_then_write(I8042Port::Buffer, configuration)); - } else { - m_second_ps2_port.device = mouse_device_or_error.release_value(); - } - } else { - m_second_ps2_port.device = vmmouse_device_or_error.release_value(); - } - } - - m_irq_handlers[0] = TRY(I8042ControllerIRQHandler::try_create(*this, IRQ_FIRST_PORT)); - m_irq_handlers[1] = TRY(I8042ControllerIRQHandler::try_create(*this, IRQ_SECOND_PORT)); - - // Enable IRQs after both are detected and initialized - if (m_first_ps2_port.device) - m_irq_handlers[0]->enable_irq(); - if (m_second_ps2_port.device) - m_irq_handlers[1]->enable_irq(); - return {}; -} - -ErrorOr I8042Controller::send_command(PortIndex port_index, DeviceCommand command) -{ - switch (command) { - case DeviceCommand::GetDeviceID: { - SpinlockLocker lock(m_lock); - return do_send_command(port_index, I8042Command::GetDeviceID); - } - case DeviceCommand::EnablePacketStreaming: { - SpinlockLocker lock(m_lock); - return do_send_command(port_index, I8042Command::EnablePacketStreaming); - } - case DeviceCommand::DisablePacketStreaming: { - SpinlockLocker lock(m_lock); - return do_send_command(port_index, I8042Command::DisablePacketStreaming); - } - case DeviceCommand::SetDefaults: { - SpinlockLocker lock(m_lock); - return do_send_command(port_index, I8042Command::SetDefaults); - } - - // NOTE: The sample rate command is supported only with sending data byte with it! - case DeviceCommand::SetSampleRate: - return EOPNOTSUPP; - - default: - return EINVAL; - } -} - -ErrorOr I8042Controller::send_command(PortIndex port_index, DeviceCommand command, u8 data) -{ - switch (command) { - // NOTE: Only the sample rate command supports sending data byte with it! - case DeviceCommand::SetSampleRate: { - SpinlockLocker lock(m_lock); - return do_send_command(port_index, I8042Command::SetSampleRate, data); - } - - case DeviceCommand::GetDeviceID: - return EOPNOTSUPP; - case DeviceCommand::EnablePacketStreaming: - return EOPNOTSUPP; - case DeviceCommand::DisablePacketStreaming: - return EOPNOTSUPP; - case DeviceCommand::SetDefaults: - return EOPNOTSUPP; - - default: - return EINVAL; - } -} - -bool I8042Controller::irq_process_input_buffer(PortIndex port_index) -{ - VERIFY(Processor::current_in_irq()); - - u8 status = IO::in8(I8042Port::Status); - if (!(status & I8042StatusFlag::OutputBuffer)) - return false; - u8 byte = IO::in8(I8042Port::Buffer); - - PS2Port* selected_port = nullptr; - if (port_index == I8042PortIndex::FirstPort) - selected_port = &m_first_ps2_port; - else if (port_index == I8042PortIndex::SecondPort) - selected_port = &m_second_ps2_port; - else - return false; - - if (!selected_port->device) - return false; - selected_port->device->handle_byte_read_from_serial_input(byte); - return true; -} - -ErrorOr I8042Controller::drain_output_buffer() -{ - for (int attempt = 0; attempt < 50; attempt++) { - u8 status = IO::in8(I8042Port::Status); - if (!(status & I8042StatusFlag::OutputBuffer)) - return {}; - IO::in8(I8042Port::Buffer); - - microseconds_delay(100); - } - return Error::from_errno(EBUSY); -} - -ErrorOr I8042Controller::do_reset_device(PortIndex port_index) -{ - VERIFY(m_lock.is_locked()); - - VERIFY(!Processor::current_in_irq()); - TRY(do_send_command(port_index, I8042Command::Reset)); - // Wait until we get the self-test result - auto self_test_result = TRY(do_wait_then_read_any_input(I8042Port::Buffer)); - - // FIXME: Is this the correct errno value for this? - if (self_test_result != I8042Response::Success) - return Error::from_errno(EIO); - return {}; -} - -ErrorOr I8042Controller::do_send_command(PortIndex port_index, u8 command) -{ - VERIFY(m_lock.is_locked()); - - VERIFY(!Processor::current_in_irq()); - TRY(do_write_to_device(port_index, command)); - return {}; -} - -ErrorOr I8042Controller::do_send_command(PortIndex port_index, u8 command, u8 data) -{ - VERIFY(m_lock.is_locked()); - - VERIFY(!Processor::current_in_irq()); - - TRY(do_write_to_device(port_index, command)); - TRY(do_write_to_device(port_index, data)); - return {}; -} - -ErrorOr I8042Controller::do_write_to_device(PortIndex port_index, u8 data) -{ - VERIFY(m_lock.is_locked()); - - VERIFY(!Processor::current_in_irq()); - - int attempts = 0; - u8 response; - do { - if (port_index != I8042PortIndex::FirstPort) { - VERIFY(port_index == I8042PortIndex::SecondPort); - TRY(prepare_for_any_output()); - IO::out8(I8042Port::Command, I8042Command::WriteSecondPS2PortInputBuffer); - } - TRY(prepare_for_any_output()); - IO::out8(I8042Port::Buffer, data); - - response = TRY(do_wait_then_read_any_input(I8042Port::Buffer)); - } while (response == I8042Response::Resend && ++attempts < 250); - if (attempts >= 250 || response == I8042Response::Resend) { - dbgln("I8042: Failed to write byte to device, gave up"); - return Error::from_errno(EBUSY); - } - return {}; -} - -ErrorOr I8042Controller::do_read_from_device(PortIndex port_index) -{ - TRY(prepare_for_input(port_index)); - return IO::in8(I8042Port::Buffer); -} - -ErrorOr I8042Controller::prepare_for_any_input() -{ - VERIFY(m_lock.is_locked()); - for (int attempt = 0; attempt < 1000; attempt++) { - u8 status = IO::in8(I8042Port::Status); - if (!(status & I8042StatusFlag::OutputBuffer)) { - microseconds_delay(1000); - continue; - } - return {}; - } - return Error::from_errno(EBUSY); -} - -ErrorOr I8042Controller::prepare_for_input(PortIndex port_index) -{ - VERIFY(m_lock.is_locked()); - u8 const second_port_flag = port_index == I8042PortIndex::FirstPort ? 0 : I8042StatusFlag::SecondPS2PortOutputBuffer; - PS2Port* port = nullptr; - if (port_index == I8042PortIndex::FirstPort) - port = &m_first_ps2_port; - else if (port_index == I8042PortIndex::SecondPort) - port = &m_second_ps2_port; - else - return Error::from_errno(ENODEV); - for (int attempt = 0; attempt < 1000; attempt++) { - u8 status = IO::in8(I8042Port::Status); - if (!(status & I8042StatusFlag::OutputBuffer)) { - microseconds_delay(1000); - continue; - } - if (!port->device_type.has_value() || port->device_type.value() == PS2DeviceType::Unknown) - return {}; - if ((status & I8042StatusFlag::SecondPS2PortOutputBuffer) == second_port_flag) - return {}; - microseconds_delay(1000); - } - return Error::from_errno(EBUSY); -} - -ErrorOr I8042Controller::prepare_for_any_output() -{ - VERIFY(m_lock.is_locked()); - for (int attempt = 0; attempt < 250; attempt++) { - u8 status = IO::in8(I8042Port::Status); - if (!(status & I8042StatusFlag::InputBuffer)) - return {}; - microseconds_delay(1000); - } - return Error::from_errno(EBUSY); -} - -UNMAP_AFTER_INIT void I8042Controller::do_write(u8 port, u8 data) -{ - VERIFY(m_lock.is_locked()); - IO::out8(port, data); -} - -UNMAP_AFTER_INIT u8 I8042Controller::do_read(u8 port) -{ - VERIFY(m_lock.is_locked()); - return IO::in8(port); -} - -ErrorOr I8042Controller::do_wait_then_write(u8 port, u8 data) -{ - VERIFY(m_lock.is_locked()); - TRY(prepare_for_any_output()); - IO::out8(port, data); - return {}; -} - -ErrorOr I8042Controller::do_wait_then_read_any_input(u8 port) -{ - VERIFY(m_lock.is_locked()); - TRY(prepare_for_any_input()); - return IO::in8(port); -} -} diff --git a/Kernel/Arch/x86_64/ISABus/I8042Controller.h b/Kernel/Arch/x86_64/ISABus/I8042Controller.h deleted file mode 100644 index 69be77416b6..00000000000 --- a/Kernel/Arch/x86_64/ISABus/I8042Controller.h +++ /dev/null @@ -1,187 +0,0 @@ -/* - * Copyright (c) 2020, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include -#include - -namespace Kernel { - -enum I8042Port : u8 { - Buffer = 0x60, - Command = 0x64, - Status = 0x64, -}; - -enum I8042Command : u8 { - ReadConfiguration = 0x20, - WriteConfiguration = 0x60, - DisableSecondPS2Port = 0xA7, - EnableSecondPS2Port = 0xA8, - TestSecondPS2Port = 0xA9, - TestPS2Controller = 0xAA, - TestFirstPS2Port = 0xAB, - DisableFirstPS2Port = 0xAD, - EnableFirstPS2Port = 0xAE, - WriteSecondPS2PortInputBuffer = 0xD4, - SetScanCodeSet = 0xF0, - GetDeviceID = 0xF2, - SetSampleRate = 0xF3, - EnablePacketStreaming = 0xF4, - DisablePacketStreaming = 0xF5, - SetDefaults = 0xF6, - Reset = 0xFF, -}; - -enum I8042ConfigurationFlag : u8 { - FirstPS2PortInterrupt = 1 << 0, - SecondPS2PortInterrupt = 1 << 1, - SystemFlag = 1 << 2, - FirstPS2PortClock = 1 << 4, - SecondPS2PortClock = 1 << 5, - FirstPS2PortTranslation = 1 << 6, -}; - -enum I8042StatusFlag : u8 { - OutputBuffer = 1 << 0, - InputBuffer = 1 << 1, - System = 1 << 2, - InputType = 1 << 3, - SecondPS2PortOutputBuffer = 1 << 5, - TimeoutError = 1 << 6, - ParityError = 1 << 7, -}; - -enum I8042Response : u8 { - ControllerTestPassed = 0x55, - Success = 0xAA, - Acknowledge = 0xFA, - Resend = 0xFE, -}; - -class PS2KeyboardDevice; -class PS2MouseDevice; - -class I8042Controller; -class I8042ControllerIRQHandler final - : public IRQHandler { -public: - static ErrorOr> try_create(I8042Controller const&, u8 irq_number); - -private: - I8042ControllerIRQHandler(I8042Controller const& controller, u8 irq_number); - - // ^IRQHandler - virtual bool handle_irq(RegisterState const&) override; - virtual StringView purpose() const override { return "I8042ControllerIRQHandler"sv; } - - NonnullRefPtr const m_controller; -}; - -class HIDManagement; -class I8042Controller final : public SerialIOController { - friend class PS2KeyboardDevice; - friend class PS2MouseDevice; - -public: - static ErrorOr> create(); - - enum class EnableKeyboardFirstPortTranslation { - Yes, - No - }; - ErrorOr detect_devices(EnableKeyboardFirstPortTranslation); - - virtual ErrorOr send_command(PortIndex, DeviceCommand command) override; - virtual ErrorOr send_command(PortIndex, DeviceCommand command, u8 data) override; - - virtual ErrorOr reset_device(PortIndex port_index) override - { - SpinlockLocker lock(m_lock); - return do_reset_device(port_index); - } - virtual ErrorOr read_from_device(PortIndex port_index) override - { - SpinlockLocker lock(m_lock); - return do_read_from_device(port_index); - } - virtual ErrorOr prepare_for_input(PortIndex) override; - - // Note: This function exists only for the initialization process of the controller - bool check_existence_via_probing(Badge); - - bool handle_irq(Badge, u8 irq_number); - -private: - I8042Controller(); - - bool irq_process_input_buffer(PortIndex); - - ErrorOr prepare_for_any_output(); - - ErrorOr prepare_for_any_input(); - - ErrorOr do_reset_device(PortIndex); - ErrorOr do_send_command(PortIndex port_index, u8 data); - ErrorOr do_send_command(PortIndex port_index, u8 command, u8 data); - ErrorOr do_write_to_device(PortIndex port_index, u8 data); - ErrorOr do_read_from_device(PortIndex port_index); - ErrorOr do_wait_then_write(u8 port, u8 data); - - // NOTE: The meaning of "any input" here is that this is not attached - // to any PS2 port, but rather we accept any serial input, which is vital - // when reading values before initializing any actual PS2 device! - ErrorOr do_wait_then_read_any_input(u8 port); - - ErrorOr drain_output_buffer(); - - // Note: These functions exist only for the initialization process of the controller - void do_write(u8 port, u8 data); - u8 do_read(u8 port); - - Spinlock m_lock {}; - bool m_first_port_available { false }; - bool m_second_port_available { false }; - bool m_is_dual_channel { false }; - - enum I8042PortIndex { - FirstPort = 0, - SecondPort = 1, - }; - - // NOTE: Each i8042 controller can have at most 2 ports - a regular (traditional - // ATKBD) port and AUX port (for mouse devices mostly). - // However, the specification for i8042 controller, as well as decent hardware - // implementations and software drivers actually allow a user to still operate - // a keyboard and mouse even if they were connected in reverse (i.e. keyboard - // was connected to AUX port, and mouse was connected to the traditional ATKBD port). - // - // Please note, that if the keyboard and mouse devices are connected in reverse, then ATKBD translation mode - // cannot be sanely enabled due to obvious peripheral devices' protocol differences, and will result - // in misproper data being sent back. - struct PS2Port { - OwnPtr device; - // NOTE: This value is being used as 1:1 map between the I8042 port being handled, to - // the either the MouseDevice or KeyboardDevice being attached. - Optional device_type; - }; - - // NOTE: Each i8042 controller can have at most 2 devices - a mouse and keyboard, - // mouse and a mouse, or keyboard and a keyboard. - // NOTE: This is usually used as the ATKBD port. - PS2Port m_first_ps2_port; - // NOTE: This is usually used as the AUX port. - PS2Port m_second_ps2_port; - - Array, 2> m_irq_handlers; -}; - -} diff --git a/Kernel/Arch/x86_64/ISABus/SerialDevice.cpp b/Kernel/Arch/x86_64/ISABus/SerialDevice.cpp deleted file mode 100644 index b46cf931d63..00000000000 --- a/Kernel/Arch/x86_64/ISABus/SerialDevice.cpp +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright (c) 2022, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include - -namespace Kernel { - -#define SERIAL_COM1_ADDR 0x3F8 -#define SERIAL_COM2_ADDR 0x2F8 -#define SERIAL_COM3_ADDR 0x3E8 -#define SERIAL_COM4_ADDR 0x2E8 - -UNMAP_AFTER_INIT NonnullLockRefPtr SerialDevice::must_create(size_t com_number) -{ - // FIXME: This way of blindly doing release_value is really not a good thing, find - // a way to propagate errors back. - LockRefPtr serial_device; - switch (com_number) { - case 0: { - auto io_window = IOWindow::create_for_io_space(IOAddress(SERIAL_COM1_ADDR), 16).release_value_but_fixme_should_propagate_errors(); - serial_device = DeviceManagement::try_create_device(move(io_window), 64).release_value(); - break; - } - case 1: { - auto io_window = IOWindow::create_for_io_space(IOAddress(SERIAL_COM2_ADDR), 16).release_value_but_fixme_should_propagate_errors(); - serial_device = DeviceManagement::try_create_device(move(io_window), 65).release_value(); - break; - } - case 2: { - auto io_window = IOWindow::create_for_io_space(IOAddress(SERIAL_COM3_ADDR), 16).release_value_but_fixme_should_propagate_errors(); - serial_device = DeviceManagement::try_create_device(move(io_window), 66).release_value(); - break; - } - case 3: { - auto io_window = IOWindow::create_for_io_space(IOAddress(SERIAL_COM4_ADDR), 16).release_value_but_fixme_should_propagate_errors(); - serial_device = DeviceManagement::try_create_device(move(io_window), 67).release_value(); - break; - } - default: - break; - } - return serial_device.release_nonnull(); -} - -} diff --git a/Kernel/Arch/x86_64/ISRStubs.h b/Kernel/Arch/x86_64/ISRStubs.h deleted file mode 100644 index 6ffc1e24d7e..00000000000 --- a/Kernel/Arch/x86_64/ISRStubs.h +++ /dev/null @@ -1,237 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include - -#include -VALIDATE_IS_X86() - -GENERATE_GENERIC_INTERRUPT_HANDLER_ASM_ENTRY(32) -GENERATE_GENERIC_INTERRUPT_HANDLER_ASM_ENTRY(33) -GENERATE_GENERIC_INTERRUPT_HANDLER_ASM_ENTRY(34) -GENERATE_GENERIC_INTERRUPT_HANDLER_ASM_ENTRY(35) -GENERATE_GENERIC_INTERRUPT_HANDLER_ASM_ENTRY(36) -GENERATE_GENERIC_INTERRUPT_HANDLER_ASM_ENTRY(37) -GENERATE_GENERIC_INTERRUPT_HANDLER_ASM_ENTRY(38) -GENERATE_GENERIC_INTERRUPT_HANDLER_ASM_ENTRY(39) -GENERATE_GENERIC_INTERRUPT_HANDLER_ASM_ENTRY(40) -GENERATE_GENERIC_INTERRUPT_HANDLER_ASM_ENTRY(41) -GENERATE_GENERIC_INTERRUPT_HANDLER_ASM_ENTRY(42) -GENERATE_GENERIC_INTERRUPT_HANDLER_ASM_ENTRY(43) -GENERATE_GENERIC_INTERRUPT_HANDLER_ASM_ENTRY(44) -GENERATE_GENERIC_INTERRUPT_HANDLER_ASM_ENTRY(45) -GENERATE_GENERIC_INTERRUPT_HANDLER_ASM_ENTRY(46) -GENERATE_GENERIC_INTERRUPT_HANDLER_ASM_ENTRY(47) -GENERATE_GENERIC_INTERRUPT_HANDLER_ASM_ENTRY(48) -GENERATE_GENERIC_INTERRUPT_HANDLER_ASM_ENTRY(49) -GENERATE_GENERIC_INTERRUPT_HANDLER_ASM_ENTRY(50) -GENERATE_GENERIC_INTERRUPT_HANDLER_ASM_ENTRY(51) -GENERATE_GENERIC_INTERRUPT_HANDLER_ASM_ENTRY(52) -GENERATE_GENERIC_INTERRUPT_HANDLER_ASM_ENTRY(53) -GENERATE_GENERIC_INTERRUPT_HANDLER_ASM_ENTRY(54) -GENERATE_GENERIC_INTERRUPT_HANDLER_ASM_ENTRY(55) -GENERATE_GENERIC_INTERRUPT_HANDLER_ASM_ENTRY(56) -GENERATE_GENERIC_INTERRUPT_HANDLER_ASM_ENTRY(57) -GENERATE_GENERIC_INTERRUPT_HANDLER_ASM_ENTRY(58) -GENERATE_GENERIC_INTERRUPT_HANDLER_ASM_ENTRY(59) -GENERATE_GENERIC_INTERRUPT_HANDLER_ASM_ENTRY(60) -GENERATE_GENERIC_INTERRUPT_HANDLER_ASM_ENTRY(61) -GENERATE_GENERIC_INTERRUPT_HANDLER_ASM_ENTRY(62) -GENERATE_GENERIC_INTERRUPT_HANDLER_ASM_ENTRY(63) -GENERATE_GENERIC_INTERRUPT_HANDLER_ASM_ENTRY(64) -GENERATE_GENERIC_INTERRUPT_HANDLER_ASM_ENTRY(65) -GENERATE_GENERIC_INTERRUPT_HANDLER_ASM_ENTRY(66) -GENERATE_GENERIC_INTERRUPT_HANDLER_ASM_ENTRY(67) -GENERATE_GENERIC_INTERRUPT_HANDLER_ASM_ENTRY(68) -GENERATE_GENERIC_INTERRUPT_HANDLER_ASM_ENTRY(69) -GENERATE_GENERIC_INTERRUPT_HANDLER_ASM_ENTRY(70) -GENERATE_GENERIC_INTERRUPT_HANDLER_ASM_ENTRY(71) -GENERATE_GENERIC_INTERRUPT_HANDLER_ASM_ENTRY(72) -GENERATE_GENERIC_INTERRUPT_HANDLER_ASM_ENTRY(73) -GENERATE_GENERIC_INTERRUPT_HANDLER_ASM_ENTRY(74) -GENERATE_GENERIC_INTERRUPT_HANDLER_ASM_ENTRY(75) -GENERATE_GENERIC_INTERRUPT_HANDLER_ASM_ENTRY(76) -GENERATE_GENERIC_INTERRUPT_HANDLER_ASM_ENTRY(77) -GENERATE_GENERIC_INTERRUPT_HANDLER_ASM_ENTRY(78) -GENERATE_GENERIC_INTERRUPT_HANDLER_ASM_ENTRY(79) -GENERATE_GENERIC_INTERRUPT_HANDLER_ASM_ENTRY(80) -GENERATE_GENERIC_INTERRUPT_HANDLER_ASM_ENTRY(81) -GENERATE_GENERIC_INTERRUPT_HANDLER_ASM_ENTRY(82) -GENERATE_GENERIC_INTERRUPT_HANDLER_ASM_ENTRY(83) -GENERATE_GENERIC_INTERRUPT_HANDLER_ASM_ENTRY(84) -GENERATE_GENERIC_INTERRUPT_HANDLER_ASM_ENTRY(85) -GENERATE_GENERIC_INTERRUPT_HANDLER_ASM_ENTRY(86) -GENERATE_GENERIC_INTERRUPT_HANDLER_ASM_ENTRY(87) -GENERATE_GENERIC_INTERRUPT_HANDLER_ASM_ENTRY(88) -GENERATE_GENERIC_INTERRUPT_HANDLER_ASM_ENTRY(89) -GENERATE_GENERIC_INTERRUPT_HANDLER_ASM_ENTRY(90) -GENERATE_GENERIC_INTERRUPT_HANDLER_ASM_ENTRY(91) -GENERATE_GENERIC_INTERRUPT_HANDLER_ASM_ENTRY(92) -GENERATE_GENERIC_INTERRUPT_HANDLER_ASM_ENTRY(93) -GENERATE_GENERIC_INTERRUPT_HANDLER_ASM_ENTRY(94) -GENERATE_GENERIC_INTERRUPT_HANDLER_ASM_ENTRY(95) -GENERATE_GENERIC_INTERRUPT_HANDLER_ASM_ENTRY(96) -GENERATE_GENERIC_INTERRUPT_HANDLER_ASM_ENTRY(97) -GENERATE_GENERIC_INTERRUPT_HANDLER_ASM_ENTRY(98) -GENERATE_GENERIC_INTERRUPT_HANDLER_ASM_ENTRY(99) -GENERATE_GENERIC_INTERRUPT_HANDLER_ASM_ENTRY(100) -GENERATE_GENERIC_INTERRUPT_HANDLER_ASM_ENTRY(101) -GENERATE_GENERIC_INTERRUPT_HANDLER_ASM_ENTRY(102) -GENERATE_GENERIC_INTERRUPT_HANDLER_ASM_ENTRY(103) -GENERATE_GENERIC_INTERRUPT_HANDLER_ASM_ENTRY(104) -GENERATE_GENERIC_INTERRUPT_HANDLER_ASM_ENTRY(105) -GENERATE_GENERIC_INTERRUPT_HANDLER_ASM_ENTRY(106) -GENERATE_GENERIC_INTERRUPT_HANDLER_ASM_ENTRY(107) -GENERATE_GENERIC_INTERRUPT_HANDLER_ASM_ENTRY(108) -GENERATE_GENERIC_INTERRUPT_HANDLER_ASM_ENTRY(109) -GENERATE_GENERIC_INTERRUPT_HANDLER_ASM_ENTRY(110) -GENERATE_GENERIC_INTERRUPT_HANDLER_ASM_ENTRY(111) -GENERATE_GENERIC_INTERRUPT_HANDLER_ASM_ENTRY(112) -GENERATE_GENERIC_INTERRUPT_HANDLER_ASM_ENTRY(113) -GENERATE_GENERIC_INTERRUPT_HANDLER_ASM_ENTRY(114) -GENERATE_GENERIC_INTERRUPT_HANDLER_ASM_ENTRY(115) -GENERATE_GENERIC_INTERRUPT_HANDLER_ASM_ENTRY(116) -GENERATE_GENERIC_INTERRUPT_HANDLER_ASM_ENTRY(117) -GENERATE_GENERIC_INTERRUPT_HANDLER_ASM_ENTRY(118) -GENERATE_GENERIC_INTERRUPT_HANDLER_ASM_ENTRY(119) -GENERATE_GENERIC_INTERRUPT_HANDLER_ASM_ENTRY(120) -GENERATE_GENERIC_INTERRUPT_HANDLER_ASM_ENTRY(121) -GENERATE_GENERIC_INTERRUPT_HANDLER_ASM_ENTRY(122) -GENERATE_GENERIC_INTERRUPT_HANDLER_ASM_ENTRY(123) -GENERATE_GENERIC_INTERRUPT_HANDLER_ASM_ENTRY(124) -GENERATE_GENERIC_INTERRUPT_HANDLER_ASM_ENTRY(125) -GENERATE_GENERIC_INTERRUPT_HANDLER_ASM_ENTRY(126) -GENERATE_GENERIC_INTERRUPT_HANDLER_ASM_ENTRY(127) -GENERATE_GENERIC_INTERRUPT_HANDLER_ASM_ENTRY(128) -GENERATE_GENERIC_INTERRUPT_HANDLER_ASM_ENTRY(129) -GENERATE_GENERIC_INTERRUPT_HANDLER_ASM_ENTRY(130) -GENERATE_GENERIC_INTERRUPT_HANDLER_ASM_ENTRY(131) -GENERATE_GENERIC_INTERRUPT_HANDLER_ASM_ENTRY(132) -GENERATE_GENERIC_INTERRUPT_HANDLER_ASM_ENTRY(133) -GENERATE_GENERIC_INTERRUPT_HANDLER_ASM_ENTRY(134) -GENERATE_GENERIC_INTERRUPT_HANDLER_ASM_ENTRY(135) -GENERATE_GENERIC_INTERRUPT_HANDLER_ASM_ENTRY(136) -GENERATE_GENERIC_INTERRUPT_HANDLER_ASM_ENTRY(137) -GENERATE_GENERIC_INTERRUPT_HANDLER_ASM_ENTRY(138) -GENERATE_GENERIC_INTERRUPT_HANDLER_ASM_ENTRY(139) -GENERATE_GENERIC_INTERRUPT_HANDLER_ASM_ENTRY(140) -GENERATE_GENERIC_INTERRUPT_HANDLER_ASM_ENTRY(141) -GENERATE_GENERIC_INTERRUPT_HANDLER_ASM_ENTRY(142) -GENERATE_GENERIC_INTERRUPT_HANDLER_ASM_ENTRY(143) -GENERATE_GENERIC_INTERRUPT_HANDLER_ASM_ENTRY(144) -GENERATE_GENERIC_INTERRUPT_HANDLER_ASM_ENTRY(145) -GENERATE_GENERIC_INTERRUPT_HANDLER_ASM_ENTRY(146) -GENERATE_GENERIC_INTERRUPT_HANDLER_ASM_ENTRY(147) -GENERATE_GENERIC_INTERRUPT_HANDLER_ASM_ENTRY(148) -GENERATE_GENERIC_INTERRUPT_HANDLER_ASM_ENTRY(149) -GENERATE_GENERIC_INTERRUPT_HANDLER_ASM_ENTRY(150) -GENERATE_GENERIC_INTERRUPT_HANDLER_ASM_ENTRY(151) -GENERATE_GENERIC_INTERRUPT_HANDLER_ASM_ENTRY(152) -GENERATE_GENERIC_INTERRUPT_HANDLER_ASM_ENTRY(153) -GENERATE_GENERIC_INTERRUPT_HANDLER_ASM_ENTRY(154) -GENERATE_GENERIC_INTERRUPT_HANDLER_ASM_ENTRY(155) -GENERATE_GENERIC_INTERRUPT_HANDLER_ASM_ENTRY(156) -GENERATE_GENERIC_INTERRUPT_HANDLER_ASM_ENTRY(157) -GENERATE_GENERIC_INTERRUPT_HANDLER_ASM_ENTRY(158) -GENERATE_GENERIC_INTERRUPT_HANDLER_ASM_ENTRY(159) -GENERATE_GENERIC_INTERRUPT_HANDLER_ASM_ENTRY(160) -GENERATE_GENERIC_INTERRUPT_HANDLER_ASM_ENTRY(161) -GENERATE_GENERIC_INTERRUPT_HANDLER_ASM_ENTRY(162) -GENERATE_GENERIC_INTERRUPT_HANDLER_ASM_ENTRY(163) -GENERATE_GENERIC_INTERRUPT_HANDLER_ASM_ENTRY(164) -GENERATE_GENERIC_INTERRUPT_HANDLER_ASM_ENTRY(165) -GENERATE_GENERIC_INTERRUPT_HANDLER_ASM_ENTRY(166) -GENERATE_GENERIC_INTERRUPT_HANDLER_ASM_ENTRY(167) -GENERATE_GENERIC_INTERRUPT_HANDLER_ASM_ENTRY(168) -GENERATE_GENERIC_INTERRUPT_HANDLER_ASM_ENTRY(169) -GENERATE_GENERIC_INTERRUPT_HANDLER_ASM_ENTRY(170) -GENERATE_GENERIC_INTERRUPT_HANDLER_ASM_ENTRY(171) -GENERATE_GENERIC_INTERRUPT_HANDLER_ASM_ENTRY(172) -GENERATE_GENERIC_INTERRUPT_HANDLER_ASM_ENTRY(173) -GENERATE_GENERIC_INTERRUPT_HANDLER_ASM_ENTRY(174) -GENERATE_GENERIC_INTERRUPT_HANDLER_ASM_ENTRY(175) -GENERATE_GENERIC_INTERRUPT_HANDLER_ASM_ENTRY(176) -GENERATE_GENERIC_INTERRUPT_HANDLER_ASM_ENTRY(177) -GENERATE_GENERIC_INTERRUPT_HANDLER_ASM_ENTRY(178) -GENERATE_GENERIC_INTERRUPT_HANDLER_ASM_ENTRY(179) -GENERATE_GENERIC_INTERRUPT_HANDLER_ASM_ENTRY(180) -GENERATE_GENERIC_INTERRUPT_HANDLER_ASM_ENTRY(181) -GENERATE_GENERIC_INTERRUPT_HANDLER_ASM_ENTRY(182) -GENERATE_GENERIC_INTERRUPT_HANDLER_ASM_ENTRY(183) -GENERATE_GENERIC_INTERRUPT_HANDLER_ASM_ENTRY(184) -GENERATE_GENERIC_INTERRUPT_HANDLER_ASM_ENTRY(185) -GENERATE_GENERIC_INTERRUPT_HANDLER_ASM_ENTRY(186) -GENERATE_GENERIC_INTERRUPT_HANDLER_ASM_ENTRY(187) -GENERATE_GENERIC_INTERRUPT_HANDLER_ASM_ENTRY(188) -GENERATE_GENERIC_INTERRUPT_HANDLER_ASM_ENTRY(189) -GENERATE_GENERIC_INTERRUPT_HANDLER_ASM_ENTRY(190) -GENERATE_GENERIC_INTERRUPT_HANDLER_ASM_ENTRY(191) -GENERATE_GENERIC_INTERRUPT_HANDLER_ASM_ENTRY(192) -GENERATE_GENERIC_INTERRUPT_HANDLER_ASM_ENTRY(193) -GENERATE_GENERIC_INTERRUPT_HANDLER_ASM_ENTRY(194) -GENERATE_GENERIC_INTERRUPT_HANDLER_ASM_ENTRY(195) -GENERATE_GENERIC_INTERRUPT_HANDLER_ASM_ENTRY(196) -GENERATE_GENERIC_INTERRUPT_HANDLER_ASM_ENTRY(197) -GENERATE_GENERIC_INTERRUPT_HANDLER_ASM_ENTRY(198) -GENERATE_GENERIC_INTERRUPT_HANDLER_ASM_ENTRY(199) -GENERATE_GENERIC_INTERRUPT_HANDLER_ASM_ENTRY(200) -GENERATE_GENERIC_INTERRUPT_HANDLER_ASM_ENTRY(201) -GENERATE_GENERIC_INTERRUPT_HANDLER_ASM_ENTRY(202) -GENERATE_GENERIC_INTERRUPT_HANDLER_ASM_ENTRY(203) -GENERATE_GENERIC_INTERRUPT_HANDLER_ASM_ENTRY(204) -GENERATE_GENERIC_INTERRUPT_HANDLER_ASM_ENTRY(205) -GENERATE_GENERIC_INTERRUPT_HANDLER_ASM_ENTRY(206) -GENERATE_GENERIC_INTERRUPT_HANDLER_ASM_ENTRY(207) -GENERATE_GENERIC_INTERRUPT_HANDLER_ASM_ENTRY(208) -GENERATE_GENERIC_INTERRUPT_HANDLER_ASM_ENTRY(209) -GENERATE_GENERIC_INTERRUPT_HANDLER_ASM_ENTRY(210) -GENERATE_GENERIC_INTERRUPT_HANDLER_ASM_ENTRY(211) -GENERATE_GENERIC_INTERRUPT_HANDLER_ASM_ENTRY(212) -GENERATE_GENERIC_INTERRUPT_HANDLER_ASM_ENTRY(213) -GENERATE_GENERIC_INTERRUPT_HANDLER_ASM_ENTRY(214) -GENERATE_GENERIC_INTERRUPT_HANDLER_ASM_ENTRY(215) -GENERATE_GENERIC_INTERRUPT_HANDLER_ASM_ENTRY(216) -GENERATE_GENERIC_INTERRUPT_HANDLER_ASM_ENTRY(217) -GENERATE_GENERIC_INTERRUPT_HANDLER_ASM_ENTRY(218) -GENERATE_GENERIC_INTERRUPT_HANDLER_ASM_ENTRY(219) -GENERATE_GENERIC_INTERRUPT_HANDLER_ASM_ENTRY(220) -GENERATE_GENERIC_INTERRUPT_HANDLER_ASM_ENTRY(221) -GENERATE_GENERIC_INTERRUPT_HANDLER_ASM_ENTRY(222) -GENERATE_GENERIC_INTERRUPT_HANDLER_ASM_ENTRY(223) -GENERATE_GENERIC_INTERRUPT_HANDLER_ASM_ENTRY(224) -GENERATE_GENERIC_INTERRUPT_HANDLER_ASM_ENTRY(225) -GENERATE_GENERIC_INTERRUPT_HANDLER_ASM_ENTRY(226) -GENERATE_GENERIC_INTERRUPT_HANDLER_ASM_ENTRY(227) -GENERATE_GENERIC_INTERRUPT_HANDLER_ASM_ENTRY(228) -GENERATE_GENERIC_INTERRUPT_HANDLER_ASM_ENTRY(229) -GENERATE_GENERIC_INTERRUPT_HANDLER_ASM_ENTRY(230) -GENERATE_GENERIC_INTERRUPT_HANDLER_ASM_ENTRY(231) -GENERATE_GENERIC_INTERRUPT_HANDLER_ASM_ENTRY(232) -GENERATE_GENERIC_INTERRUPT_HANDLER_ASM_ENTRY(233) -GENERATE_GENERIC_INTERRUPT_HANDLER_ASM_ENTRY(234) -GENERATE_GENERIC_INTERRUPT_HANDLER_ASM_ENTRY(235) -GENERATE_GENERIC_INTERRUPT_HANDLER_ASM_ENTRY(236) -GENERATE_GENERIC_INTERRUPT_HANDLER_ASM_ENTRY(237) -GENERATE_GENERIC_INTERRUPT_HANDLER_ASM_ENTRY(238) -GENERATE_GENERIC_INTERRUPT_HANDLER_ASM_ENTRY(239) -GENERATE_GENERIC_INTERRUPT_HANDLER_ASM_ENTRY(240) -GENERATE_GENERIC_INTERRUPT_HANDLER_ASM_ENTRY(241) -GENERATE_GENERIC_INTERRUPT_HANDLER_ASM_ENTRY(242) -GENERATE_GENERIC_INTERRUPT_HANDLER_ASM_ENTRY(243) -GENERATE_GENERIC_INTERRUPT_HANDLER_ASM_ENTRY(244) -GENERATE_GENERIC_INTERRUPT_HANDLER_ASM_ENTRY(245) -GENERATE_GENERIC_INTERRUPT_HANDLER_ASM_ENTRY(246) -GENERATE_GENERIC_INTERRUPT_HANDLER_ASM_ENTRY(247) -GENERATE_GENERIC_INTERRUPT_HANDLER_ASM_ENTRY(248) -GENERATE_GENERIC_INTERRUPT_HANDLER_ASM_ENTRY(249) -GENERATE_GENERIC_INTERRUPT_HANDLER_ASM_ENTRY(250) -GENERATE_GENERIC_INTERRUPT_HANDLER_ASM_ENTRY(251) -GENERATE_GENERIC_INTERRUPT_HANDLER_ASM_ENTRY(252) -GENERATE_GENERIC_INTERRUPT_HANDLER_ASM_ENTRY(253) -GENERATE_GENERIC_INTERRUPT_HANDLER_ASM_ENTRY(254) -GENERATE_GENERIC_INTERRUPT_HANDLER_ASM_ENTRY(255) diff --git a/Kernel/Arch/x86_64/InterruptEntry.cpp b/Kernel/Arch/x86_64/InterruptEntry.cpp deleted file mode 100644 index 7c91ce38c5a..00000000000 --- a/Kernel/Arch/x86_64/InterruptEntry.cpp +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * Copyright (c) 2021, Gunnar Beutner - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include - -// clang-format off -asm( - ".globl interrupt_common_asm_entry\n" - "interrupt_common_asm_entry: \n" - // save all the other registers - " pushq %r15\n" - " pushq %r14\n" - " pushq %r13\n" - " pushq %r12\n" - " pushq %r11\n" - " pushq %r10\n" - " pushq %r9\n" - " pushq %r8\n" - " pushq %rax\n" - " pushq %rcx\n" - " pushq %rdx\n" - " pushq %rbx\n" - " pushq %rsp\n" - " pushq %rbp\n" - " pushq %rsi\n" - " pushq %rdi\n" - " pushq %rsp \n" /* set TrapFrame::regs */ - " subq $" __STRINGIFY(TRAP_FRAME_SIZE - 8) ", %rsp \n" - " movq %rsp, %rdi \n" - " cld\n" - " call enter_trap \n" - " movq %rsp, %rdi \n" - " call handle_interrupt \n" - ".globl common_trap_exit \n" - "common_trap_exit: \n" - // another thread may have handled this trap at this point, so don't - // make assumptions about the stack other than there's a TrapFrame. - " movq %rsp, %rdi \n" - " call exit_trap \n" - " addq $" __STRINGIFY(TRAP_FRAME_SIZE) ", %rsp\n" // pop TrapFrame - ".globl interrupt_common_asm_exit \n" - "interrupt_common_asm_exit: \n" - " popq %rdi\n" - " popq %rsi\n" - " popq %rbp\n" - " addq $8, %rsp\n" // skip restoring rsp - " popq %rbx\n" - " popq %rdx\n" - " popq %rcx\n" - " popq %rax\n" - " popq %r8\n" - " popq %r9\n" - " popq %r10\n" - " popq %r11\n" - " popq %r12\n" - " popq %r13\n" - " popq %r14\n" - " popq %r15\n" - " addq $0x8, %rsp\n" // skip exception_code, isr_number - " iretq\n" -); -// clang-format on diff --git a/Kernel/Arch/x86_64/InterruptManagement.cpp b/Kernel/Arch/x86_64/InterruptManagement.cpp deleted file mode 100644 index 77bf26fc70e..00000000000 --- a/Kernel/Arch/x86_64/InterruptManagement.cpp +++ /dev/null @@ -1,230 +0,0 @@ -/* - * Copyright (c) 2020, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define PCAT_COMPAT_FLAG 0x1 - -namespace Kernel { - -static InterruptManagement* s_interrupt_management; - -bool InterruptManagement::initialized() -{ - return (s_interrupt_management != nullptr); -} - -InterruptManagement& InterruptManagement::the() -{ - VERIFY(InterruptManagement::initialized()); - return *s_interrupt_management; -} - -UNMAP_AFTER_INIT void InterruptManagement::initialize() -{ - VERIFY(!InterruptManagement::initialized()); - s_interrupt_management = new InterruptManagement(); - if (!kernel_command_line().is_smp_enabled_without_ioapic_enabled()) { - dbgln("Can't enable SMP mode without IOAPIC mode being enabled"); - } - if (!kernel_command_line().is_ioapic_enabled() && !kernel_command_line().is_smp_enabled()) - InterruptManagement::the().switch_to_pic_mode(); - else - InterruptManagement::the().switch_to_ioapic_mode(); -} - -void InterruptManagement::enumerate_interrupt_handlers(Function callback) -{ - for (size_t i = 0; i < GENERIC_INTERRUPT_HANDLERS_COUNT; i++) { - auto& handler = get_interrupt_handler(i); - if (handler.type() == HandlerType::SharedIRQHandler) { - static_cast(handler).enumerate_handlers(callback); - continue; - } - if (handler.type() != HandlerType::UnhandledInterruptHandler) - callback(handler); - } -} - -IRQController& InterruptManagement::get_interrupt_controller(size_t index) -{ - return *m_interrupt_controllers[index]; -} - -u8 InterruptManagement::acquire_mapped_interrupt_number(u8 original_irq) -{ - if (!InterruptManagement::initialized()) { - // This is necessary, because we install UnhandledInterruptHandlers before we actually initialize the Interrupt Management object... - return original_irq; - } - return InterruptManagement::the().get_mapped_interrupt_vector(original_irq); -} - -u8 InterruptManagement::acquire_irq_number(u8 mapped_interrupt_vector) -{ - VERIFY(InterruptManagement::initialized()); - return InterruptManagement::the().get_irq_vector(mapped_interrupt_vector); -} - -u8 InterruptManagement::get_mapped_interrupt_vector(u8 original_irq) -{ - // FIXME: For SMP configuration (with IOAPICs) use a better routing scheme to make redirections more efficient. - // FIXME: Find a better way to handle conflict with Syscall interrupt gate. - VERIFY((original_irq + IRQ_VECTOR_BASE) != syscall_vector); - return original_irq; -} - -u8 InterruptManagement::get_irq_vector(u8 mapped_interrupt_vector) -{ - // FIXME: For SMP configuration (with IOAPICs) use a better routing scheme to make redirections more efficient. - return mapped_interrupt_vector; -} - -NonnullLockRefPtr InterruptManagement::get_responsible_irq_controller(IRQControllerType controller_type, u8 interrupt_vector) -{ - for (auto& irq_controller : m_interrupt_controllers) { - if (irq_controller->gsi_base() <= interrupt_vector && irq_controller->type() == controller_type) - return irq_controller; - } - VERIFY_NOT_REACHED(); -} - -NonnullLockRefPtr InterruptManagement::get_responsible_irq_controller(u8 interrupt_vector) -{ - if (m_interrupt_controllers.size() == 1 && m_interrupt_controllers[0]->type() == IRQControllerType::i8259) { - return m_interrupt_controllers[0]; - } - for (auto& irq_controller : m_interrupt_controllers) { - if (irq_controller->gsi_base() <= interrupt_vector) - if (!irq_controller->is_hard_disabled()) - return irq_controller; - } - VERIFY_NOT_REACHED(); -} - -UNMAP_AFTER_INIT ErrorOr> InterruptManagement::find_madt_physical_address() -{ - dbgln("Early access to ACPI tables for interrupt setup"); - auto possible_rsdp_physical_address = TRY(ACPI::StaticParsing::find_rsdp_in_platform_specific_memory_locations()); - if (!possible_rsdp_physical_address.has_value()) - return Optional {}; - auto possible_apic_physical_address = TRY(ACPI::StaticParsing::find_table(possible_rsdp_physical_address.value(), "APIC"sv)); - if (!possible_apic_physical_address.has_value()) - return Optional {}; - return possible_apic_physical_address.value(); -} - -UNMAP_AFTER_INIT InterruptManagement::InterruptManagement() -{ -} - -UNMAP_AFTER_INIT void InterruptManagement::switch_to_pic_mode() -{ - VERIFY(m_interrupt_controllers.is_empty()); - dmesgln("Interrupts: Switch to Legacy PIC mode"); - InterruptDisabler disabler; - m_interrupt_controllers.append(adopt_lock_ref(*new PIC())); - SpuriousInterruptHandler::initialize(7); - SpuriousInterruptHandler::initialize(15); - dbgln("Interrupts: Detected {}", m_interrupt_controllers[0]->model()); -} - -UNMAP_AFTER_INIT void InterruptManagement::switch_to_ioapic_mode() -{ - dmesgln("Interrupts: Switch to IOAPIC mode"); - InterruptDisabler disabler; - - m_madt_physical_address = MUST(find_madt_physical_address()); - - if (!m_madt_physical_address.has_value()) { - dbgln("Interrupts: ACPI MADT is not available, reverting to PIC mode"); - switch_to_pic_mode(); - return; - } - - dbgln("Interrupts: MADT @ P {}", m_madt_physical_address.value().as_ptr()); - locate_apic_data(); - if (m_interrupt_controllers.size() == 1) { - if (get_interrupt_controller(0).type() == IRQControllerType::i8259) { - dmesgln("Interrupts: NO IOAPIC detected, Reverting to PIC mode."); - return; - } - } - for (auto& irq_controller : m_interrupt_controllers) { - VERIFY(irq_controller); - if (irq_controller->type() == IRQControllerType::i8259) { - irq_controller->hard_disable(); - dbgln("Interrupts: Detected {} - Disabled", irq_controller->model()); - SpuriousInterruptHandler::initialize_for_disabled_master_pic(); - SpuriousInterruptHandler::initialize_for_disabled_slave_pic(); - } else { - dbgln("Interrupts: Detected {}", irq_controller->model()); - } - } - - if (auto mp_parser = MultiProcessorParser::autodetect()) { - m_pci_interrupt_overrides = mp_parser->get_pci_interrupt_redirections(); - } - - APIC::initialize(); - APIC::the().init_bsp(); -} - -UNMAP_AFTER_INIT void InterruptManagement::locate_apic_data() -{ - VERIFY(m_madt_physical_address.has_value()); - auto madt = Memory::map_typed(m_madt_physical_address.value()).release_value_but_fixme_should_propagate_errors(); - - if (madt->flags & PCAT_COMPAT_FLAG) - m_interrupt_controllers.append(adopt_lock_ref(*new PIC())); - size_t entry_index = 0; - size_t entries_length = madt->h.length - sizeof(ACPI::Structures::MADT); - auto* madt_entry = madt->entries; - while (entries_length > 0) { - size_t entry_length = madt_entry->length; - if (madt_entry->type == (u8)ACPI::Structures::MADTEntryType::IOAPIC) { - auto* ioapic_entry = (const ACPI::Structures::MADTEntries::IOAPIC*)madt_entry; - dbgln("IOAPIC found @ MADT entry {}, MMIO Registers @ {}", entry_index, PhysicalAddress(ioapic_entry->ioapic_address)); - m_interrupt_controllers.append(adopt_lock_ref(*new IOAPIC(PhysicalAddress(ioapic_entry->ioapic_address), ioapic_entry->gsi_base))); - } - if (madt_entry->type == (u8)ACPI::Structures::MADTEntryType::InterruptSourceOverride) { - auto* interrupt_override_entry = (const ACPI::Structures::MADTEntries::InterruptSourceOverride*)madt_entry; - u32 global_system_interrupt = 0; - ByteReader::load(reinterpret_cast(&interrupt_override_entry->global_system_interrupt), global_system_interrupt); - u16 flags = 0; - ByteReader::load(reinterpret_cast(&interrupt_override_entry->flags), flags); - MUST(m_isa_interrupt_overrides.try_empend( - interrupt_override_entry->bus, - interrupt_override_entry->source, - global_system_interrupt, - flags)); - - dbgln("Interrupts: Overriding INT {:#x} with GSI {}, for bus {:#x}", - interrupt_override_entry->source, - global_system_interrupt, - interrupt_override_entry->bus); - } - madt_entry = (ACPI::Structures::MADTEntryHeader*)(VirtualAddress(madt_entry).offset(entry_length).get()); - entries_length -= entry_length; - entry_index++; - } -} - -} diff --git a/Kernel/Arch/x86_64/InterruptManagement.h b/Kernel/Arch/x86_64/InterruptManagement.h deleted file mode 100644 index e004d1ece88..00000000000 --- a/Kernel/Arch/x86_64/InterruptManagement.h +++ /dev/null @@ -1,78 +0,0 @@ -/* - * Copyright (c) 2020, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace Kernel { - -class ISAInterruptOverrideMetadata { -public: - ISAInterruptOverrideMetadata(u8 bus, u8 source, u32 global_system_interrupt, u16 flags) - : m_bus(bus) - , m_source(source) - , m_global_system_interrupt(global_system_interrupt) - , m_flags(flags) - { - } - - u8 bus() const { return m_bus; } - u8 source() const { return m_source; } - u32 gsi() const { return m_global_system_interrupt; } - u16 flags() const { return m_flags; } - -private: - u8 const m_bus; - u8 const m_source; - u32 const m_global_system_interrupt; - u16 const m_flags; -}; - -class InterruptManagement { -public: - static InterruptManagement& the(); - static void initialize(); - static bool initialized(); - static u8 acquire_mapped_interrupt_number(u8 original_irq); - static u8 acquire_irq_number(u8 mapped_interrupt_vector); - - void switch_to_pic_mode(); - void switch_to_ioapic_mode(); - - NonnullLockRefPtr get_responsible_irq_controller(u8 interrupt_vector); - NonnullLockRefPtr get_responsible_irq_controller(IRQControllerType controller_type, u8 interrupt_vector); - - Vector const& isa_overrides() const { return m_isa_interrupt_overrides; } - - u8 get_mapped_interrupt_vector(u8 original_irq); - u8 get_irq_vector(u8 mapped_interrupt_vector); - - void enumerate_interrupt_handlers(Function); - IRQController& get_interrupt_controller(size_t index); - -protected: - virtual ~InterruptManagement() = default; - -private: - InterruptManagement(); - ErrorOr> find_madt_physical_address(); - void locate_apic_data(); - Vector> m_interrupt_controllers; - Vector m_isa_interrupt_overrides; - Vector m_pci_interrupt_overrides; - Optional m_madt_physical_address; -}; - -} diff --git a/Kernel/Arch/x86_64/Interrupts.cpp b/Kernel/Arch/x86_64/Interrupts.cpp deleted file mode 100644 index 213e134c8c0..00000000000 --- a/Kernel/Arch/x86_64/Interrupts.cpp +++ /dev/null @@ -1,739 +0,0 @@ -/* - * Copyright (c) 2018-2021, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -extern FlatPtr start_of_unmap_after_init; -extern FlatPtr end_of_unmap_after_init; -extern FlatPtr start_of_ro_after_init; -extern FlatPtr end_of_ro_after_init; -extern FlatPtr start_of_kernel_ksyms; -extern FlatPtr end_of_kernel_ksyms; - -namespace Kernel { - -READONLY_AFTER_INIT static DescriptorTablePointer s_idtr; -READONLY_AFTER_INIT static IDTEntry s_idt[256]; - -// This spinlock is used to reserve IRQs that can be later used by interrupt mechanism such as MSIx -static Spinlock s_interrupt_handler_lock {}; -static GenericInterruptHandler* s_interrupt_handler[GENERIC_INTERRUPT_HANDLERS_COUNT]; -static GenericInterruptHandler* s_disabled_interrupt_handler[2]; - -static EntropySource s_entropy_source_interrupts { EntropySource::Static::Interrupts }; - -// clang-format off - -#define EH_ENTRY(ec, title) \ - extern "C" void title##_asm_entry(); \ - extern "C" void title##_handler(TrapFrame*) __attribute__((used)); \ - NAKED void title##_asm_entry() { \ - asm( \ - " pushq %r15\n" \ - " pushq %r14\n" \ - " pushq %r13\n" \ - " pushq %r12\n" \ - " pushq %r11\n" \ - " pushq %r10\n" \ - " pushq %r9\n" \ - " pushq %r8\n" \ - " pushq %rax\n" \ - " pushq %rcx\n" \ - " pushq %rdx\n" \ - " pushq %rbx\n" \ - " pushq %rsp\n" \ - " pushq %rbp\n" \ - " pushq %rsi\n" \ - " pushq %rdi\n" \ - " pushq %rsp \n" /* set TrapFrame::regs */ \ - " subq $" __STRINGIFY(TRAP_FRAME_SIZE - 8) ", %rsp \n" \ - " subq $0x8, %rsp\n" /* align stack */ \ - " lea 0x8(%rsp), %rdi \n" \ - " cld\n" \ - " call enter_trap_no_irq \n" \ - " lea 0x8(%rsp), %rdi \n" \ - " call " #title "_handler\n" \ - " addq $0x8, %rsp\n" /* undo alignment */ \ - " jmp common_trap_exit \n" \ - ); \ - } - -#define EH_ENTRY_NO_CODE(ec, title) \ - extern "C" void title##_handler(TrapFrame*) __attribute__((used)); \ - extern "C" void title##_asm_entry(); \ - NAKED void title##_asm_entry() { \ - asm( \ - " pushq $0x0\n" \ - " pushq %r15\n" \ - " pushq %r14\n" \ - " pushq %r13\n" \ - " pushq %r12\n" \ - " pushq %r11\n" \ - " pushq %r10\n" \ - " pushq %r9\n" \ - " pushq %r8\n" \ - " pushq %rax\n" \ - " pushq %rcx\n" \ - " pushq %rdx\n" \ - " pushq %rbx\n" \ - " pushq %rsp\n" \ - " pushq %rbp\n" \ - " pushq %rsi\n" \ - " pushq %rdi\n" \ - " pushq %rsp \n" /* set TrapFrame::regs */ \ - " subq $" __STRINGIFY(TRAP_FRAME_SIZE - 8) ", %rsp \n" \ - " movq %rsp, %rdi \n" \ - " cld\n" \ - " call enter_trap_no_irq \n" \ - " movq %rsp, %rdi \n" \ - " call " #title "_handler\n" \ - " jmp common_trap_exit \n" \ - ); \ - } - -// clang-format on - -void dump_registers(RegisterState const& regs) -{ - u64 rsp; - - if (!(regs.cs & 3)) - rsp = regs.rsp; - else - rsp = regs.userspace_rsp; - - dbgln("Exception code: {:04x} (isr: {:04x})", regs.exception_code, regs.isr_number); - dbgln(" pc={:#04x}:{:p} rflags={:p}", (u16)regs.cs, regs.rip, regs.rflags); - MSR fs_base_msr(MSR_FS_BASE); - MSR gs_base_msr(MSR_GS_BASE); - dbgln(" stack={:p} fs={:p} gs={:p}", rsp, fs_base_msr.get(), gs_base_msr.get()); - dbgln(" rax={:p} rbx={:p} rcx={:p} rdx={:p}", regs.rax, regs.rbx, regs.rcx, regs.rdx); - dbgln(" rbp={:p} rsp={:p} rsi={:p} rdi={:p}", regs.rbp, regs.rsp, regs.rsi, regs.rdi); - dbgln(" r8={:p} r9={:p} r10={:p} r11={:p}", regs.r8, regs.r9, regs.r10, regs.r11); - dbgln(" r12={:p} r13={:p} r14={:p} r15={:p}", regs.r12, regs.r13, regs.r14, regs.r15); - dbgln(" cr0={:p} cr2={:p} cr3={:p} cr4={:p}", read_cr0(), read_cr2(), read_cr3(), read_cr4()); -} - -EH_ENTRY_NO_CODE(6, illegal_instruction); -void illegal_instruction_handler(TrapFrame* trap) -{ - clac(); - handle_crash(*trap->regs, "Illegal instruction", SIGILL); -} - -EH_ENTRY_NO_CODE(0, divide_error); -void divide_error_handler(TrapFrame* trap) -{ - clac(); - handle_crash(*trap->regs, "Divide error", SIGFPE); -} - -EH_ENTRY(13, general_protection_fault); -void general_protection_fault_handler(TrapFrame* trap) -{ - clac(); - handle_crash(*trap->regs, "General protection fault", SIGSEGV); -} - -// 7: FPU not available exception -EH_ENTRY_NO_CODE(7, fpu_exception); -void fpu_exception_handler(TrapFrame*) -{ - // Just clear the TS flag. We've already restored the FPU state eagerly. - // FIXME: It would be nice if we didn't have to do this at all. - asm volatile("clts"); -} - -// 14: Page Fault -EH_ENTRY(14, page_fault); -void page_fault_handler(TrapFrame* trap) -{ - clac(); - - auto fault_address = read_cr2(); - - auto& regs = *trap->regs; - - // NOTE: Once we've extracted the faulting address from CR2, we can re-enable interrupts. - // However, we only do this *if* they were enabled when the page fault occurred. - if (regs.flags() & 0x200) { - sti(); - } - - if constexpr (PAGE_FAULT_DEBUG) { - u32 fault_page_directory = read_cr3(); - dbgln("CPU #{} ring {} {} page fault in PD={:#x}, {}{} {}", - Processor::is_initialized() ? Processor::current_id() : 0, - regs.cs & 3, - regs.exception_code & 1 ? "PV" : "NP", - fault_page_directory, - regs.exception_code & 8 ? "reserved-bit " : "", - regs.exception_code & 2 ? "write" : "read", - VirtualAddress(fault_address)); - - dump_registers(regs); - } - - PageFault fault { regs.exception_code, VirtualAddress { fault_address } }; - fault.handle(regs); -} - -EH_ENTRY_NO_CODE(1, debug); -void debug_handler(TrapFrame* trap) -{ - clac(); - auto& regs = *trap->regs; - auto current_thread = Thread::current(); - auto& process = current_thread->process(); - if ((regs.cs & 3) == 0) { - PANIC("Debug exception in ring 0"); - } - constexpr u8 REASON_SINGLESTEP = 14; - auto debug_status = read_dr6(); - auto should_trap_mask = (1 << REASON_SINGLESTEP) | 0b1111; - if ((debug_status & should_trap_mask) == 0) - return; - if (auto tracer = process.tracer()) { - tracer->set_regs(regs); - } - current_thread->send_urgent_signal_to_self(SIGTRAP); - write_dr6(debug_status & ~(should_trap_mask)); -} - -EH_ENTRY_NO_CODE(3, breakpoint); -void breakpoint_handler(TrapFrame* trap) -{ - clac(); - auto& regs = *trap->regs; - auto current_thread = Thread::current(); - auto& process = current_thread->process(); - if ((regs.cs & 3) == 0) { - PANIC("Breakpoint trap in ring 0"); - } - if (auto tracer = process.tracer()) { - tracer->set_regs(regs); - } - current_thread->send_urgent_signal_to_self(SIGTRAP); -} - -#define EH(i, msg) \ - static void _exception##i() \ - { \ - dbgln("{}", msg); \ - PANIC("cr0={:08x} cr2={:08x} cr3={:08x} cr4={:08x}", read_cr0(), read_cr2(), read_cr3(), read_cr4()); \ - } - -EH(2, "Unknown error") -EH(4, "Overflow") -EH(5, "Bounds check") -EH(8, "Double fault") -EH(9, "Coprocessor segment overrun") -EH(10, "Invalid TSS") -EH(11, "Segment not present") -EH(12, "Stack exception") -EH(15, "Unknown error") -EH(16, "Coprocessor error") - -extern "C" void pre_init_finished(void) __attribute__((used)); -extern "C" void post_init_finished(void) __attribute__((used)); -extern "C" void handle_interrupt(TrapFrame*) __attribute__((used)); - -extern "C" UNMAP_AFTER_INIT void pre_init_finished(void) -{ - VERIFY(g_scheduler_lock.is_locked_by_current_processor()); - - // Because init_finished() will wait on the other APs, we need - // to release the scheduler lock so that the other APs can also get - // to this point - - // The target flags will get restored upon leaving the trap - Scheduler::leave_on_first_switch(Processor::interrupts_state()); -} - -extern "C" UNMAP_AFTER_INIT void post_init_finished(void) -{ - // We need to re-acquire the scheduler lock before a context switch - // transfers control into the idle loop, which needs the lock held - Scheduler::prepare_for_idle_loop(); -} - -void handle_interrupt(TrapFrame* trap) -{ - clac(); - auto& regs = *trap->regs; - - GenericInterruptHandler* handler = nullptr; - // Note: we declare interrupt service routine offset 0x20 to 0x2f as - // reserved for when the PIC is disabled, so we can still route spurious - // IRQs to a different interrupt handlers at different location. - if (regs.isr_number >= pic_disabled_vector_base && regs.isr_number <= pic_disabled_vector_end) { - u8 irq = (u8)(regs.isr_number - pic_disabled_vector_base); - if (irq == 7) { - handler = s_disabled_interrupt_handler[0]; - } else if (irq == 15) { - handler = s_disabled_interrupt_handler[1]; - } - } else { - VERIFY(regs.isr_number >= IRQ_VECTOR_BASE && regs.isr_number <= (IRQ_VECTOR_BASE + GENERIC_INTERRUPT_HANDLERS_COUNT)); - u8 irq = (u8)(regs.isr_number - IRQ_VECTOR_BASE); - s_entropy_source_interrupts.add_random_event(irq); - handler = s_interrupt_handler[irq]; - } - VERIFY(handler); - handler->increment_call_count(); - handler->handle_interrupt(regs); - handler->eoi(); -} - -DescriptorTablePointer const& get_idtr() -{ - return s_idtr; -} - -static void unimp_trap() -{ - PANIC("Unhandled IRQ"); -} - -GenericInterruptHandler& get_interrupt_handler(u8 interrupt_number) -{ - auto*& handler_slot = s_interrupt_handler[interrupt_number]; - VERIFY(handler_slot != nullptr); - return *handler_slot; -} - -static void revert_to_unused_handler(u8 interrupt_number) -{ - auto handler = new UnhandledInterruptHandler(interrupt_number); - handler->register_interrupt_handler(); -} - -static bool is_unused_handler(GenericInterruptHandler* handler_slot) -{ - return (handler_slot->type() == HandlerType::UnhandledInterruptHandler) && !handler_slot->reserved(); -} - -// Sets the reserved flag on `number_of_irqs` if it finds unused interrupt handler on -// a contiguous range. -ErrorOr reserve_interrupt_handlers(u8 number_of_irqs) -{ - bool found_range = false; - u8 first_irq = 0; - SpinlockLocker locker(s_interrupt_handler_lock); - for (int start_irq = 0; start_irq < GENERIC_INTERRUPT_HANDLERS_COUNT; start_irq++) { - auto*& handler_slot = s_interrupt_handler[start_irq]; - VERIFY(handler_slot != nullptr); - - if (!is_unused_handler(handler_slot)) - continue; - - found_range = true; - for (auto off = 1; off < number_of_irqs; off++) { - auto*& handler = s_interrupt_handler[start_irq + off]; - VERIFY(handler_slot != nullptr); - - if (!is_unused_handler(handler)) { - found_range = false; - break; - } - } - - if (found_range == true) { - first_irq = start_irq; - break; - } - } - - if (!found_range) - return Error::from_errno(EAGAIN); - - for (auto irq = first_irq; irq < number_of_irqs; irq++) { - auto*& handler_slot = s_interrupt_handler[irq]; - handler_slot->set_reserved(); - } - - return first_irq; -} - -void register_disabled_interrupt_handler(u8 number, GenericInterruptHandler& handler) -{ - if (number == 15) { - s_disabled_interrupt_handler[0] = &handler; - return; - } else if (number == 7) { - s_disabled_interrupt_handler[1] = &handler; - return; - } - VERIFY_NOT_REACHED(); -} - -void register_generic_interrupt_handler(u8 interrupt_number, GenericInterruptHandler& handler) -{ - VERIFY(interrupt_number < GENERIC_INTERRUPT_HANDLERS_COUNT); - auto*& handler_slot = s_interrupt_handler[interrupt_number]; - if (handler_slot == nullptr) { - handler_slot = &handler; - return; - } - if (handler_slot->type() == HandlerType::UnhandledInterruptHandler) { - auto* unhandled_handler = static_cast(handler_slot); - unhandled_handler->unregister_interrupt_handler(); - delete unhandled_handler; - handler_slot = &handler; - return; - } - if (handler_slot->is_shared_handler()) { - VERIFY(handler_slot->type() == HandlerType::SharedIRQHandler); - static_cast(handler_slot)->register_handler(handler); - return; - } - if (!handler_slot->is_shared_handler()) { - if (handler_slot->type() == HandlerType::SpuriousInterruptHandler) { - static_cast(handler_slot)->register_handler(handler); - return; - } - VERIFY(handler_slot->type() == HandlerType::IRQHandler); - static_cast(handler_slot)->set_shared_with_others(true); - auto& previous_handler = *handler_slot; - handler_slot = nullptr; - SharedIRQHandler::initialize(interrupt_number); - VERIFY(handler_slot); - static_cast(handler_slot)->register_handler(previous_handler); - static_cast(handler_slot)->register_handler(handler); - return; - } - VERIFY_NOT_REACHED(); -} - -void unregister_generic_interrupt_handler(u8 interrupt_number, GenericInterruptHandler& handler) -{ - auto*& handler_slot = s_interrupt_handler[interrupt_number]; - VERIFY(handler_slot != nullptr); - if (handler_slot->type() == HandlerType::UnhandledInterruptHandler) - return; - if (handler_slot->is_shared_handler()) { - VERIFY(handler_slot->type() == HandlerType::SharedIRQHandler); - auto* shared_handler = static_cast(handler_slot); - shared_handler->unregister_handler(handler); - if (!shared_handler->sharing_devices_count()) { - handler_slot = nullptr; - revert_to_unused_handler(interrupt_number); - } - return; - } - if (!handler_slot->is_shared_handler()) { - VERIFY(handler_slot->type() == HandlerType::IRQHandler); - static_cast(handler_slot)->set_shared_with_others(false); - handler_slot = nullptr; - revert_to_unused_handler(interrupt_number); - return; - } - VERIFY_NOT_REACHED(); -} - -UNMAP_AFTER_INIT void register_interrupt_handler(u8 index, void (*handler)()) -{ - s_idt[index] = IDTEntry((FlatPtr)handler, GDT_SELECTOR_CODE0, IDTEntryType::InterruptGate32, 0); -} - -UNMAP_AFTER_INIT void register_user_callable_interrupt_handler(u8 index, void (*handler)()) -{ - s_idt[index] = IDTEntry((FlatPtr)handler, GDT_SELECTOR_CODE0, IDTEntryType::TrapGate32, 3); -} - -UNMAP_AFTER_INIT void flush_idt() -{ - asm("lidt %0" ::"m"(s_idtr)); -} - -UNMAP_AFTER_INIT void initialize_interrupts() -{ - s_idtr.address = s_idt; - s_idtr.limit = 256 * sizeof(IDTEntry) - 1; - - register_interrupt_handler(0x00, divide_error_asm_entry); - register_user_callable_interrupt_handler(0x01, debug_asm_entry); - register_interrupt_handler(0x02, _exception2); - register_user_callable_interrupt_handler(0x03, breakpoint_asm_entry); - register_interrupt_handler(0x04, _exception4); - register_interrupt_handler(0x05, _exception5); - register_interrupt_handler(0x06, illegal_instruction_asm_entry); - register_interrupt_handler(0x07, fpu_exception_asm_entry); - register_interrupt_handler(0x08, _exception8); - register_interrupt_handler(0x09, _exception9); - register_interrupt_handler(0x0a, _exception10); - register_interrupt_handler(0x0b, _exception11); - register_interrupt_handler(0x0c, _exception12); - register_interrupt_handler(0x0d, general_protection_fault_asm_entry); - register_interrupt_handler(0x0e, page_fault_asm_entry); - register_interrupt_handler(0x0f, _exception15); - register_interrupt_handler(0x10, _exception16); - - for (u8 i = 0x11; i < 0x20; i++) - register_interrupt_handler(i, unimp_trap); - - dbgln("Initializing unhandled interrupt handlers"); - register_interrupt_handler(0x20, interrupt_32_asm_entry); - register_interrupt_handler(0x21, interrupt_33_asm_entry); - register_interrupt_handler(0x22, interrupt_34_asm_entry); - register_interrupt_handler(0x23, interrupt_35_asm_entry); - register_interrupt_handler(0x24, interrupt_36_asm_entry); - register_interrupt_handler(0x25, interrupt_37_asm_entry); - register_interrupt_handler(0x26, interrupt_38_asm_entry); - register_interrupt_handler(0x27, interrupt_39_asm_entry); - register_interrupt_handler(0x28, interrupt_40_asm_entry); - register_interrupt_handler(0x29, interrupt_41_asm_entry); - register_interrupt_handler(0x2a, interrupt_42_asm_entry); - register_interrupt_handler(0x2b, interrupt_43_asm_entry); - register_interrupt_handler(0x2c, interrupt_44_asm_entry); - register_interrupt_handler(0x2d, interrupt_45_asm_entry); - register_interrupt_handler(0x2e, interrupt_46_asm_entry); - register_interrupt_handler(0x2f, interrupt_47_asm_entry); - register_interrupt_handler(0x30, interrupt_48_asm_entry); - register_interrupt_handler(0x31, interrupt_49_asm_entry); - register_interrupt_handler(0x32, interrupt_50_asm_entry); - register_interrupt_handler(0x33, interrupt_51_asm_entry); - register_interrupt_handler(0x34, interrupt_52_asm_entry); - register_interrupt_handler(0x35, interrupt_53_asm_entry); - register_interrupt_handler(0x36, interrupt_54_asm_entry); - register_interrupt_handler(0x37, interrupt_55_asm_entry); - register_interrupt_handler(0x38, interrupt_56_asm_entry); - register_interrupt_handler(0x39, interrupt_57_asm_entry); - register_interrupt_handler(0x3a, interrupt_58_asm_entry); - register_interrupt_handler(0x3b, interrupt_59_asm_entry); - register_interrupt_handler(0x3c, interrupt_60_asm_entry); - register_interrupt_handler(0x3d, interrupt_61_asm_entry); - register_interrupt_handler(0x3e, interrupt_62_asm_entry); - register_interrupt_handler(0x3f, interrupt_63_asm_entry); - register_interrupt_handler(0x40, interrupt_64_asm_entry); - register_interrupt_handler(0x41, interrupt_65_asm_entry); - register_interrupt_handler(0x42, interrupt_66_asm_entry); - register_interrupt_handler(0x43, interrupt_67_asm_entry); - register_interrupt_handler(0x44, interrupt_68_asm_entry); - register_interrupt_handler(0x45, interrupt_69_asm_entry); - register_interrupt_handler(0x46, interrupt_70_asm_entry); - register_interrupt_handler(0x47, interrupt_71_asm_entry); - register_interrupt_handler(0x48, interrupt_72_asm_entry); - register_interrupt_handler(0x49, interrupt_73_asm_entry); - register_interrupt_handler(0x4a, interrupt_74_asm_entry); - register_interrupt_handler(0x4b, interrupt_75_asm_entry); - register_interrupt_handler(0x4c, interrupt_76_asm_entry); - register_interrupt_handler(0x4d, interrupt_77_asm_entry); - register_interrupt_handler(0x4e, interrupt_78_asm_entry); - register_interrupt_handler(0x4f, interrupt_79_asm_entry); - register_interrupt_handler(0x50, interrupt_80_asm_entry); - register_interrupt_handler(0x51, interrupt_81_asm_entry); - register_interrupt_handler(0x52, interrupt_82_asm_entry); - register_interrupt_handler(0x53, interrupt_83_asm_entry); - register_interrupt_handler(0x54, interrupt_84_asm_entry); - register_interrupt_handler(0x55, interrupt_85_asm_entry); - register_interrupt_handler(0x56, interrupt_86_asm_entry); - register_interrupt_handler(0x57, interrupt_87_asm_entry); - register_interrupt_handler(0x58, interrupt_88_asm_entry); - register_interrupt_handler(0x59, interrupt_89_asm_entry); - register_interrupt_handler(0x5a, interrupt_90_asm_entry); - register_interrupt_handler(0x5b, interrupt_91_asm_entry); - register_interrupt_handler(0x5c, interrupt_92_asm_entry); - register_interrupt_handler(0x5d, interrupt_93_asm_entry); - register_interrupt_handler(0x5e, interrupt_94_asm_entry); - register_interrupt_handler(0x5f, interrupt_95_asm_entry); - register_interrupt_handler(0x60, interrupt_96_asm_entry); - register_interrupt_handler(0x61, interrupt_97_asm_entry); - register_interrupt_handler(0x62, interrupt_98_asm_entry); - register_interrupt_handler(0x63, interrupt_99_asm_entry); - register_interrupt_handler(0x64, interrupt_100_asm_entry); - register_interrupt_handler(0x65, interrupt_101_asm_entry); - register_interrupt_handler(0x66, interrupt_102_asm_entry); - register_interrupt_handler(0x67, interrupt_103_asm_entry); - register_interrupt_handler(0x68, interrupt_104_asm_entry); - register_interrupt_handler(0x69, interrupt_105_asm_entry); - register_interrupt_handler(0x6a, interrupt_106_asm_entry); - register_interrupt_handler(0x6b, interrupt_107_asm_entry); - register_interrupt_handler(0x6c, interrupt_108_asm_entry); - register_interrupt_handler(0x6d, interrupt_109_asm_entry); - register_interrupt_handler(0x6e, interrupt_110_asm_entry); - register_interrupt_handler(0x6f, interrupt_111_asm_entry); - register_interrupt_handler(0x70, interrupt_112_asm_entry); - register_interrupt_handler(0x71, interrupt_113_asm_entry); - register_interrupt_handler(0x72, interrupt_114_asm_entry); - register_interrupt_handler(0x73, interrupt_115_asm_entry); - register_interrupt_handler(0x74, interrupt_116_asm_entry); - register_interrupt_handler(0x75, interrupt_117_asm_entry); - register_interrupt_handler(0x76, interrupt_118_asm_entry); - register_interrupt_handler(0x77, interrupt_119_asm_entry); - register_interrupt_handler(0x78, interrupt_120_asm_entry); - register_interrupt_handler(0x79, interrupt_121_asm_entry); - register_interrupt_handler(0x7a, interrupt_122_asm_entry); - register_interrupt_handler(0x7b, interrupt_123_asm_entry); - register_interrupt_handler(0x7c, interrupt_124_asm_entry); - register_interrupt_handler(0x7d, interrupt_125_asm_entry); - register_interrupt_handler(0x7e, interrupt_126_asm_entry); - register_interrupt_handler(0x7f, interrupt_127_asm_entry); - register_interrupt_handler(0x80, interrupt_128_asm_entry); - register_interrupt_handler(0x81, interrupt_129_asm_entry); - register_interrupt_handler(0x82, interrupt_130_asm_entry); - register_interrupt_handler(0x83, interrupt_131_asm_entry); - register_interrupt_handler(0x84, interrupt_132_asm_entry); - register_interrupt_handler(0x85, interrupt_133_asm_entry); - register_interrupt_handler(0x86, interrupt_134_asm_entry); - register_interrupt_handler(0x87, interrupt_135_asm_entry); - register_interrupt_handler(0x88, interrupt_136_asm_entry); - register_interrupt_handler(0x89, interrupt_137_asm_entry); - register_interrupt_handler(0x8a, interrupt_138_asm_entry); - register_interrupt_handler(0x8b, interrupt_139_asm_entry); - register_interrupt_handler(0x8c, interrupt_140_asm_entry); - register_interrupt_handler(0x8d, interrupt_141_asm_entry); - register_interrupt_handler(0x8e, interrupt_142_asm_entry); - register_interrupt_handler(0x8f, interrupt_143_asm_entry); - register_interrupt_handler(0x90, interrupt_144_asm_entry); - register_interrupt_handler(0x91, interrupt_145_asm_entry); - register_interrupt_handler(0x92, interrupt_146_asm_entry); - register_interrupt_handler(0x93, interrupt_147_asm_entry); - register_interrupt_handler(0x94, interrupt_148_asm_entry); - register_interrupt_handler(0x95, interrupt_149_asm_entry); - register_interrupt_handler(0x96, interrupt_150_asm_entry); - register_interrupt_handler(0x97, interrupt_151_asm_entry); - register_interrupt_handler(0x98, interrupt_152_asm_entry); - register_interrupt_handler(0x99, interrupt_153_asm_entry); - register_interrupt_handler(0x9a, interrupt_154_asm_entry); - register_interrupt_handler(0x9b, interrupt_155_asm_entry); - register_interrupt_handler(0x9c, interrupt_156_asm_entry); - register_interrupt_handler(0x9d, interrupt_157_asm_entry); - register_interrupt_handler(0x9e, interrupt_158_asm_entry); - register_interrupt_handler(0x9f, interrupt_159_asm_entry); - register_interrupt_handler(0xa0, interrupt_160_asm_entry); - register_interrupt_handler(0xa1, interrupt_161_asm_entry); - register_interrupt_handler(0xa2, interrupt_162_asm_entry); - register_interrupt_handler(0xa3, interrupt_163_asm_entry); - register_interrupt_handler(0xa4, interrupt_164_asm_entry); - register_interrupt_handler(0xa5, interrupt_165_asm_entry); - register_interrupt_handler(0xa6, interrupt_166_asm_entry); - register_interrupt_handler(0xa7, interrupt_167_asm_entry); - register_interrupt_handler(0xa8, interrupt_168_asm_entry); - register_interrupt_handler(0xa9, interrupt_169_asm_entry); - register_interrupt_handler(0xaa, interrupt_170_asm_entry); - register_interrupt_handler(0xab, interrupt_171_asm_entry); - register_interrupt_handler(0xac, interrupt_172_asm_entry); - register_interrupt_handler(0xad, interrupt_173_asm_entry); - register_interrupt_handler(0xae, interrupt_174_asm_entry); - register_interrupt_handler(0xaf, interrupt_175_asm_entry); - register_interrupt_handler(0xb0, interrupt_176_asm_entry); - register_interrupt_handler(0xb1, interrupt_177_asm_entry); - register_interrupt_handler(0xb2, interrupt_178_asm_entry); - register_interrupt_handler(0xb3, interrupt_179_asm_entry); - register_interrupt_handler(0xb4, interrupt_180_asm_entry); - register_interrupt_handler(0xb5, interrupt_181_asm_entry); - register_interrupt_handler(0xb6, interrupt_182_asm_entry); - register_interrupt_handler(0xb7, interrupt_183_asm_entry); - register_interrupt_handler(0xb8, interrupt_184_asm_entry); - register_interrupt_handler(0xb9, interrupt_185_asm_entry); - register_interrupt_handler(0xba, interrupt_186_asm_entry); - register_interrupt_handler(0xbb, interrupt_187_asm_entry); - register_interrupt_handler(0xbc, interrupt_188_asm_entry); - register_interrupt_handler(0xbd, interrupt_189_asm_entry); - register_interrupt_handler(0xbe, interrupt_190_asm_entry); - register_interrupt_handler(0xbf, interrupt_191_asm_entry); - register_interrupt_handler(0xc0, interrupt_192_asm_entry); - register_interrupt_handler(0xc1, interrupt_193_asm_entry); - register_interrupt_handler(0xc2, interrupt_194_asm_entry); - register_interrupt_handler(0xc3, interrupt_195_asm_entry); - register_interrupt_handler(0xc4, interrupt_196_asm_entry); - register_interrupt_handler(0xc5, interrupt_197_asm_entry); - register_interrupt_handler(0xc6, interrupt_198_asm_entry); - register_interrupt_handler(0xc7, interrupt_199_asm_entry); - register_interrupt_handler(0xc8, interrupt_200_asm_entry); - register_interrupt_handler(0xc9, interrupt_201_asm_entry); - register_interrupt_handler(0xca, interrupt_202_asm_entry); - register_interrupt_handler(0xcb, interrupt_203_asm_entry); - register_interrupt_handler(0xcc, interrupt_204_asm_entry); - register_interrupt_handler(0xcd, interrupt_205_asm_entry); - register_interrupt_handler(0xce, interrupt_206_asm_entry); - register_interrupt_handler(0xcf, interrupt_207_asm_entry); - register_interrupt_handler(0xd0, interrupt_208_asm_entry); - register_interrupt_handler(0xd1, interrupt_209_asm_entry); - register_interrupt_handler(0xd2, interrupt_210_asm_entry); - register_interrupt_handler(0xd3, interrupt_211_asm_entry); - register_interrupt_handler(0xd4, interrupt_212_asm_entry); - register_interrupt_handler(0xd5, interrupt_213_asm_entry); - register_interrupt_handler(0xd6, interrupt_214_asm_entry); - register_interrupt_handler(0xd7, interrupt_215_asm_entry); - register_interrupt_handler(0xd8, interrupt_216_asm_entry); - register_interrupt_handler(0xd9, interrupt_217_asm_entry); - register_interrupt_handler(0xda, interrupt_218_asm_entry); - register_interrupt_handler(0xdb, interrupt_219_asm_entry); - register_interrupt_handler(0xdc, interrupt_220_asm_entry); - register_interrupt_handler(0xdd, interrupt_221_asm_entry); - register_interrupt_handler(0xde, interrupt_222_asm_entry); - register_interrupt_handler(0xdf, interrupt_223_asm_entry); - register_interrupt_handler(0xe0, interrupt_224_asm_entry); - register_interrupt_handler(0xe1, interrupt_225_asm_entry); - register_interrupt_handler(0xe2, interrupt_226_asm_entry); - register_interrupt_handler(0xe3, interrupt_227_asm_entry); - register_interrupt_handler(0xe4, interrupt_228_asm_entry); - register_interrupt_handler(0xe5, interrupt_229_asm_entry); - register_interrupt_handler(0xe6, interrupt_230_asm_entry); - register_interrupt_handler(0xe7, interrupt_231_asm_entry); - register_interrupt_handler(0xe8, interrupt_232_asm_entry); - register_interrupt_handler(0xe9, interrupt_233_asm_entry); - register_interrupt_handler(0xea, interrupt_234_asm_entry); - register_interrupt_handler(0xeb, interrupt_235_asm_entry); - register_interrupt_handler(0xec, interrupt_236_asm_entry); - register_interrupt_handler(0xed, interrupt_237_asm_entry); - register_interrupt_handler(0xee, interrupt_238_asm_entry); - register_interrupt_handler(0xef, interrupt_239_asm_entry); - register_interrupt_handler(0xf0, interrupt_240_asm_entry); - register_interrupt_handler(0xf1, interrupt_241_asm_entry); - register_interrupt_handler(0xf2, interrupt_242_asm_entry); - register_interrupt_handler(0xf3, interrupt_243_asm_entry); - register_interrupt_handler(0xf4, interrupt_244_asm_entry); - register_interrupt_handler(0xf5, interrupt_245_asm_entry); - register_interrupt_handler(0xf6, interrupt_246_asm_entry); - register_interrupt_handler(0xf7, interrupt_247_asm_entry); - register_interrupt_handler(0xf8, interrupt_248_asm_entry); - register_interrupt_handler(0xf9, interrupt_249_asm_entry); - register_interrupt_handler(0xfa, interrupt_250_asm_entry); - register_interrupt_handler(0xfb, interrupt_251_asm_entry); - register_interrupt_handler(0xfc, interrupt_252_asm_entry); - register_interrupt_handler(0xfd, interrupt_253_asm_entry); - register_interrupt_handler(0xfe, interrupt_254_asm_entry); - register_interrupt_handler(0xff, interrupt_255_asm_entry); - - for (u8 i = 0; i < GENERIC_INTERRUPT_HANDLERS_COUNT; ++i) { - auto* handler = new UnhandledInterruptHandler(i); - handler->register_interrupt_handler(); - } - - flush_idt(); -} - -} diff --git a/Kernel/Arch/x86_64/Interrupts.h b/Kernel/Arch/x86_64/Interrupts.h deleted file mode 100644 index 79d08614911..00000000000 --- a/Kernel/Arch/x86_64/Interrupts.h +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright (c) 2018-2021, Andreas Kling - * Copyright (c) 2021, Leon Albrecht - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include - -#include -VALIDATE_IS_X86() - -namespace Kernel { - -class GenericInterruptHandler; - -extern "C" void interrupt_common_asm_entry(); - -#define INTERRUPT_HANDLER_PUSH_PADDING "pushw $0\npushw $0\n" - -// clang-format off -#define GENERATE_GENERIC_INTERRUPT_HANDLER_ASM_ENTRY(isr_number) \ - extern "C" void interrupt_##isr_number##_asm_entry(); \ - static void interrupt_##isr_number##_asm_entry_dummy() __attribute__((used)); \ - NEVER_INLINE void interrupt_##isr_number##_asm_entry_dummy() \ - { \ - asm(".globl interrupt_" #isr_number "_asm_entry\n" \ - "interrupt_" #isr_number "_asm_entry:\n" \ - INTERRUPT_HANDLER_PUSH_PADDING \ - " pushw $" #isr_number "\n" \ - " pushw $0\n" \ - " jmp interrupt_common_asm_entry\n"); \ - } -// clang-format on - -void register_interrupt_handler(u8 number, void (*handler)()); -void register_user_callable_interrupt_handler(u8 number, void (*handler)()); -void register_disabled_interrupt_handler(u8 number, GenericInterruptHandler& handler); - -} diff --git a/Kernel/Arch/x86_64/Interrupts/APIC.cpp b/Kernel/Arch/x86_64/Interrupts/APIC.cpp deleted file mode 100644 index 7edb653516f..00000000000 --- a/Kernel/Arch/x86_64/Interrupts/APIC.cpp +++ /dev/null @@ -1,679 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define IRQ_APIC_TIMER (0xfc - IRQ_VECTOR_BASE) -#define IRQ_APIC_IPI (0xfd - IRQ_VECTOR_BASE) -#define IRQ_APIC_ERR (0xfe - IRQ_VECTOR_BASE) -#define IRQ_APIC_SPURIOUS (0xff - IRQ_VECTOR_BASE) - -#define APIC_ICR_DELIVERY_PENDING (1 << 12) - -#define APIC_ENABLED (1 << 8) - -#define APIC_BASE_MSR 0x1b -#define APIC_REGS_MSR_BASE 0x800 - -#define APIC_REG_ID 0x20 -#define APIC_REG_EOI 0xb0 -#define APIC_REG_LD 0xd0 -#define APIC_REG_DF 0xe0 -#define APIC_REG_SIV 0xf0 -#define APIC_REG_TPR 0x80 -#define APIC_REG_ICR_LOW 0x300 -#define APIC_REG_ICR_HIGH 0x310 -#define APIC_REG_LVT_TIMER 0x320 -#define APIC_REG_LVT_THERMAL 0x330 -#define APIC_REG_LVT_PERFORMANCE_COUNTER 0x340 -#define APIC_REG_LVT_LINT0 0x350 -#define APIC_REG_LVT_LINT1 0x360 -#define APIC_REG_LVT_ERR 0x370 -#define APIC_REG_TIMER_INITIAL_COUNT 0x380 -#define APIC_REG_TIMER_CURRENT_COUNT 0x390 -#define APIC_REG_TIMER_CONFIGURATION 0x3e0 - -namespace Kernel { - -static Singleton s_apic; - -class APICIPIInterruptHandler final : public GenericInterruptHandler { -public: - explicit APICIPIInterruptHandler(u8 interrupt_vector) - : GenericInterruptHandler(interrupt_vector, true) - { - } - virtual ~APICIPIInterruptHandler() - { - } - - static void initialize(u8 interrupt_number) - { - auto* handler = new APICIPIInterruptHandler(interrupt_number); - handler->register_interrupt_handler(); - } - - virtual bool handle_interrupt(RegisterState const&) override; - - virtual bool eoi() override; - - virtual HandlerType type() const override { return HandlerType::IRQHandler; } - virtual StringView purpose() const override { return "IPI Handler"sv; } - virtual StringView controller() const override { return {}; } - - virtual size_t sharing_devices_count() const override { return 0; } - virtual bool is_shared_handler() const override { return false; } - -private: -}; - -class APICErrInterruptHandler final : public GenericInterruptHandler { -public: - explicit APICErrInterruptHandler(u8 interrupt_vector) - : GenericInterruptHandler(interrupt_vector, true) - { - } - virtual ~APICErrInterruptHandler() - { - } - - static void initialize(u8 interrupt_number) - { - auto* handler = new APICErrInterruptHandler(interrupt_number); - handler->register_interrupt_handler(); - } - - virtual bool handle_interrupt(RegisterState const&) override; - - virtual bool eoi() override; - - virtual HandlerType type() const override { return HandlerType::IRQHandler; } - virtual StringView purpose() const override { return "SMP Error Handler"sv; } - virtual StringView controller() const override { return {}; } - - virtual size_t sharing_devices_count() const override { return 0; } - virtual bool is_shared_handler() const override { return false; } - -private: -}; - -bool APIC::initialized() -{ - return s_apic.is_initialized(); -} - -APIC& APIC::the() -{ - VERIFY(APIC::initialized()); - return *s_apic; -} - -UNMAP_AFTER_INIT void APIC::initialize() -{ - VERIFY(!APIC::initialized()); - s_apic.ensure_instance(); -} - -PhysicalAddress APIC::get_base() -{ - MSR msr(APIC_BASE_MSR); - auto base = msr.get(); - return PhysicalAddress(base & 0xfffff000); -} - -void APIC::set_base(PhysicalAddress const& base) -{ - MSR msr(APIC_BASE_MSR); - u64 flags = 1 << 11; - if (m_is_x2.was_set()) - flags |= 1 << 10; - msr.set(base.get() | flags); -} - -void APIC::write_register(u32 offset, u32 value) -{ - if (m_is_x2.was_set()) { - MSR msr(APIC_REGS_MSR_BASE + (offset >> 4)); - msr.set(value); - } else { - *reinterpret_cast(m_apic_base->vaddr().offset(offset).as_ptr()) = value; - } -} - -u32 APIC::read_register(u32 offset) -{ - if (m_is_x2.was_set()) { - MSR msr(APIC_REGS_MSR_BASE + (offset >> 4)); - return (u32)msr.get(); - } - return *reinterpret_cast(m_apic_base->vaddr().offset(offset).as_ptr()); -} - -void APIC::set_lvt(u32 offset, u8 interrupt) -{ - write_register(offset, read_register(offset) | interrupt); -} - -void APIC::set_siv(u32 offset, u8 interrupt) -{ - write_register(offset, read_register(offset) | interrupt | APIC_ENABLED); -} - -void APIC::wait_for_pending_icr() -{ - while ((read_register(APIC_REG_ICR_LOW) & APIC_ICR_DELIVERY_PENDING) != 0) { - microseconds_delay(200); - } -} - -void APIC::write_icr(ICRReg const& icr) -{ - if (m_is_x2.was_set()) { - MSR msr(APIC_REGS_MSR_BASE + (APIC_REG_ICR_LOW >> 4)); - msr.set(icr.x2_value()); - } else { - write_register(APIC_REG_ICR_HIGH, icr.x_high()); - write_register(APIC_REG_ICR_LOW, icr.x_low()); - } -} - -#define APIC_LVT_TIMER_ONESHOT 0 -#define APIC_LVT_TIMER_PERIODIC (1 << 17) -#define APIC_LVT_TIMER_TSCDEADLINE (1 << 18) - -#define APIC_LVT_MASKED (1 << 16) -#define APIC_LVT_TRIGGER_LEVEL (1 << 14) -#define APIC_LVT(iv, dm) (((iv) & 0xff) | (((dm) & 0x7) << 8)) - -extern "C" void apic_ap_start(void); -extern "C" u16 apic_ap_start_size; -extern "C" FlatPtr ap_cpu_init_stacks; -extern "C" FlatPtr ap_cpu_init_processor_info_array; -extern "C" u32 ap_cpu_init_cr0; -extern "C" FlatPtr ap_cpu_init_cr3; -extern "C" u32 ap_cpu_init_cr4; -extern "C" FlatPtr ap_cpu_gdtr; -extern "C" FlatPtr ap_cpu_idtr; -extern "C" FlatPtr ap_cpu_kernel_map_base; -extern "C" FlatPtr ap_cpu_kernel_entry_function; - -extern "C" [[noreturn]] void init_ap(FlatPtr, Processor*); - -void APIC::eoi() -{ - write_register(APIC_REG_EOI, 0x0); -} - -u8 APIC::spurious_interrupt_vector() -{ - return IRQ_APIC_SPURIOUS; -} - -#define APIC_INIT_VAR_PTR(tpe, vaddr, varname) \ - reinterpret_cast(reinterpret_cast(vaddr) \ - + reinterpret_cast(&varname) \ - - reinterpret_cast(&apic_ap_start)) - -UNMAP_AFTER_INIT bool APIC::init_bsp() -{ - // FIXME: Use the ACPI MADT table - if (!MSR::have()) - return false; - - // check if we support local apic - CPUID id(1); - if ((id.edx() & (1 << 9)) == 0) - return false; - if (id.ecx() & (1 << 21)) - m_is_x2.set(); - - PhysicalAddress apic_base = get_base(); - dbgln_if(APIC_DEBUG, "Initializing {}APIC, base: {}", m_is_x2.was_set() ? "x2" : "x", apic_base); - set_base(apic_base); - - if (!m_is_x2.was_set()) { - auto region_or_error = MM.allocate_mmio_kernel_region(apic_base.page_base(), PAGE_SIZE, {}, Memory::Region::Access::ReadWrite); - if (region_or_error.is_error()) { - dbgln("APIC: Failed to allocate memory for APIC base"); - return false; - } - m_apic_base = region_or_error.release_value(); - } - - auto possible_rsdp_physical_address_or_error = ACPI::StaticParsing::find_rsdp_in_platform_specific_memory_locations(); - if (possible_rsdp_physical_address_or_error.is_error()) { - dbgln("APIC: Failed to map RSDP"); - return false; - } - auto possible_rsdp_physical_address = possible_rsdp_physical_address_or_error.release_value(); - if (!possible_rsdp_physical_address.has_value()) { - dbgln("APIC: RSDP not found"); - return false; - } - - auto possible_apic_physical_address_or_error = ACPI::StaticParsing::find_table(possible_rsdp_physical_address.value(), "APIC"sv); - if (possible_apic_physical_address_or_error.is_error()) { - dbgln("APIC: Failed to map RSDT/XSDT"); - return false; - } - auto possible_apic_physical_address = possible_apic_physical_address_or_error.release_value(); - if (!possible_apic_physical_address.has_value()) { - dbgln("APIC: MADT table not found"); - return false; - } - - if (kernel_command_line().is_smp_enabled()) { - auto madt_or_error = Memory::map_typed(possible_apic_physical_address.value()); - if (madt_or_error.is_error()) { - dbgln("APIC: Failed to map MADT table"); - return false; - } - auto madt = madt_or_error.release_value(); - size_t entry_index = 0; - size_t entries_length = madt->h.length - sizeof(ACPI::Structures::MADT); - auto* madt_entry = madt->entries; - while (entries_length > 0) { - size_t entry_length = madt_entry->length; - if (madt_entry->type == (u8)ACPI::Structures::MADTEntryType::LocalAPIC) { - auto* plapic_entry = (const ACPI::Structures::MADTEntries::ProcessorLocalAPIC*)madt_entry; - dbgln_if(APIC_DEBUG, "APIC: AP found @ MADT entry {}, processor ID: {}, xAPIC ID: {}, flags: {:#08x}", entry_index, plapic_entry->acpi_processor_id, plapic_entry->apic_id, plapic_entry->flags); - m_processor_cnt++; - if ((plapic_entry->flags & 0x1) != 0) - m_processor_enabled_cnt++; - } else if (madt_entry->type == (u8)ACPI::Structures::MADTEntryType::Local_x2APIC) { - // Only used for APID IDs >= 255 - auto* plx2apic_entry = (const ACPI::Structures::MADTEntries::ProcessorLocalX2APIC*)madt_entry; - dbgln_if(APIC_DEBUG, "APIC: AP found @ MADT entry {}, processor ID: {}, x2APIC ID: {}, flags: {:#08x}", entry_index, plx2apic_entry->acpi_processor_id, plx2apic_entry->apic_id, plx2apic_entry->flags); - m_processor_cnt++; - if ((plx2apic_entry->flags & 0x1) != 0) - m_processor_enabled_cnt++; - } - madt_entry = (ACPI::Structures::MADTEntryHeader*)(VirtualAddress(madt_entry).offset(entry_length).get()); - entries_length -= entry_length; - entry_index++; - } - dbgln("APIC processors found: {}, enabled: {}", m_processor_cnt, m_processor_enabled_cnt); - } - - if (m_processor_enabled_cnt < 1) - m_processor_enabled_cnt = 1; - if (m_processor_cnt < 1) - m_processor_cnt = 1; - - enable(0); - return true; -} - -UNMAP_AFTER_INIT void APIC::setup_ap_boot_environment() -{ - VERIFY(!m_ap_boot_environment); - VERIFY(m_processor_enabled_cnt > 1); - u32 aps_to_enable = m_processor_enabled_cnt - 1; - - // Copy the APIC startup code and variables to P0x00008000 - // Also account for the data appended to: - // * aps_to_enable u32 values for ap_cpu_init_stacks - // * aps_to_enable u32 values for ap_cpu_init_processor_info_array - constexpr u64 apic_startup_region_base = 0x8000; - auto apic_startup_region_size = Memory::page_round_up(apic_ap_start_size + (2 * aps_to_enable * sizeof(FlatPtr))).release_value_but_fixme_should_propagate_errors(); - VERIFY(apic_startup_region_size < USER_RANGE_BASE); - auto apic_startup_region = MUST(MM.create_identity_mapped_region(PhysicalAddress(apic_startup_region_base), apic_startup_region_size)); - u8* apic_startup_region_ptr = apic_startup_region->vaddr().as_ptr(); - memcpy(apic_startup_region_ptr, reinterpret_cast(apic_ap_start), apic_ap_start_size); - - // Allocate enough stacks for all APs - m_ap_temporary_boot_stacks.ensure_capacity(aps_to_enable); - for (u32 i = 0; i < aps_to_enable; i++) { - auto stack_region_or_error = MM.allocate_kernel_region(Thread::default_kernel_stack_size, {}, Memory::Region::Access::ReadWrite, AllocationStrategy::AllocateNow); - if (stack_region_or_error.is_error()) { - dbgln("APIC: Failed to allocate stack for AP #{}", i); - return; - } - auto stack_region = stack_region_or_error.release_value(); - stack_region->set_stack(true); - m_ap_temporary_boot_stacks.unchecked_append(move(stack_region)); - } - - // Store pointers to all stacks for the APs to use - auto* ap_stack_array = APIC_INIT_VAR_PTR(FlatPtr, apic_startup_region_ptr, ap_cpu_init_stacks); - VERIFY(aps_to_enable == m_ap_temporary_boot_stacks.size()); - for (size_t i = 0; i < aps_to_enable; i++) { - ap_stack_array[i] = m_ap_temporary_boot_stacks[i]->vaddr().get() + Thread::default_kernel_stack_size; - dbgln_if(APIC_DEBUG, "APIC: CPU[{}] stack at {}", i + 1, VirtualAddress { ap_stack_array[i] }); - } - - // Allocate Processor structures for all APs and store the pointer to the data - m_ap_processor_info.resize(aps_to_enable); - for (size_t i = 0; i < aps_to_enable; i++) - m_ap_processor_info[i] = adopt_nonnull_own_or_enomem(new (nothrow) Processor()).release_value_but_fixme_should_propagate_errors(); - auto* ap_processor_info_array = &ap_stack_array[aps_to_enable]; - for (size_t i = 0; i < aps_to_enable; i++) { - ap_processor_info_array[i] = FlatPtr(m_ap_processor_info[i].ptr()); - dbgln_if(APIC_DEBUG, "APIC: CPU[{}] processor at {}", i + 1, VirtualAddress { ap_processor_info_array[i] }); - } - *APIC_INIT_VAR_PTR(FlatPtr, apic_startup_region_ptr, ap_cpu_init_processor_info_array) = FlatPtr(&ap_processor_info_array[0]); - - // Store the BSP's CR3 value for the APs to use - *APIC_INIT_VAR_PTR(FlatPtr, apic_startup_region_ptr, ap_cpu_init_cr3) = MM.kernel_page_directory().cr3(); - - // Store the BSP's GDT and IDT for the APs to use - auto const& gdtr = Processor::current().get_gdtr(); - *APIC_INIT_VAR_PTR(FlatPtr, apic_startup_region_ptr, ap_cpu_gdtr) = FlatPtr(&gdtr); - auto const& idtr = get_idtr(); - *APIC_INIT_VAR_PTR(FlatPtr, apic_startup_region_ptr, ap_cpu_idtr) = FlatPtr(&idtr); - - *APIC_INIT_VAR_PTR(FlatPtr, apic_startup_region_ptr, ap_cpu_kernel_map_base) = FlatPtr(kernel_mapping_base); - *APIC_INIT_VAR_PTR(FlatPtr, apic_startup_region_ptr, ap_cpu_kernel_entry_function) = FlatPtr(&init_ap); - - // Store the BSP's CR0 and CR4 values for the APs to use - *APIC_INIT_VAR_PTR(FlatPtr, apic_startup_region_ptr, ap_cpu_init_cr0) = read_cr0(); - *APIC_INIT_VAR_PTR(FlatPtr, apic_startup_region_ptr, ap_cpu_init_cr4) = read_cr4(); - - m_ap_boot_environment = move(apic_startup_region); -} - -UNMAP_AFTER_INIT void APIC::do_boot_aps() -{ - VERIFY(m_ap_boot_environment); - VERIFY(m_processor_enabled_cnt > 1); - u32 aps_to_enable = m_processor_enabled_cnt - 1; - - // Create an idle thread for each processor. We have to do this here - // because we won't be able to send FlushTLB messages, so we have to - // have all memory set up for the threads so that when the APs are - // starting up, they can access all the memory properly - m_ap_idle_threads.resize(aps_to_enable); - for (u32 i = 0; i < aps_to_enable; i++) - m_ap_idle_threads[i] = Scheduler::create_ap_idle_thread(i + 1); - - dbgln_if(APIC_DEBUG, "APIC: Starting {} AP(s)", aps_to_enable); - - // INIT - write_icr({ 0, 0, ICRReg::INIT, ICRReg::Physical, ICRReg::Assert, ICRReg::TriggerMode::Edge, ICRReg::AllExcludingSelf }); - - microseconds_delay(10 * 1000); - - for (int i = 0; i < 2; i++) { - // SIPI - write_icr({ 0x08, 0, ICRReg::StartUp, ICRReg::Physical, ICRReg::Assert, ICRReg::TriggerMode::Edge, ICRReg::AllExcludingSelf }); // start execution at P8000 - - microseconds_delay(200); - } - - // Now wait until the ap_cpu_init_pending variable dropped to 0, which means all APs are initialized and no longer need these special mappings - if (m_apic_ap_count.load(AK::MemoryOrder::memory_order_consume) != aps_to_enable) { - dbgln_if(APIC_DEBUG, "APIC: Waiting for {} AP(s) to finish initialization...", aps_to_enable); - do { - // Wait a little bit - microseconds_delay(200); - } while (m_apic_ap_count.load(AK::MemoryOrder::memory_order_consume) != aps_to_enable); - } - - dbgln_if(APIC_DEBUG, "APIC: {} processors are initialized and running", m_processor_enabled_cnt); - - // NOTE: Since this region is identity-mapped, we have to unmap it manually to prevent the virtual - // address range from leaking into the general virtual range allocator. - m_ap_boot_environment->unmap(); - m_ap_boot_environment = nullptr; - // When the APs signal that they finished their initialization they have already switched over to their - // idle thread's stack, so the temporary boot stack can be deallocated - m_ap_temporary_boot_stacks.clear(); -} - -UNMAP_AFTER_INIT void APIC::boot_aps() -{ - if (m_processor_enabled_cnt <= 1) - return; - - // We split this into another call because do_boot_aps() will cause - // MM calls upon exit, and we don't want to call smp_enable before that - do_boot_aps(); - - // Enable SMP, which means IPIs may now be sent - Processor::smp_enable(); - - dbgln_if(APIC_DEBUG, "All processors initialized and waiting, trigger all to continue"); - - // Now trigger all APs to continue execution (need to do this after - // the regions have been freed so that we don't trigger IPIs - m_apic_ap_continue.store(1, AK::MemoryOrder::memory_order_release); -} - -UNMAP_AFTER_INIT void APIC::enable(u32 cpu) -{ - VERIFY(m_is_x2.was_set() || cpu < 8); - - u32 apic_id; - if (m_is_x2.was_set()) { - dbgln_if(APIC_DEBUG, "Enable x2APIC on CPU #{}", cpu); - - // We need to enable x2 mode on each core independently - set_base(get_base()); - - apic_id = read_register(APIC_REG_ID); - } else { - dbgln_if(APIC_DEBUG, "Setting logical xAPIC ID for CPU #{}", cpu); - - // Use the CPU# as logical apic id - VERIFY(cpu <= 8); - write_register(APIC_REG_LD, (read_register(APIC_REG_LD) & 0x00ffffff) | (cpu << 24)); - - // read it back to make sure it's actually set - apic_id = read_register(APIC_REG_LD) >> 24; - } - - dbgln_if(APIC_DEBUG, "CPU #{} apic id: {}", cpu, apic_id); - Processor::current().info().set_apic_id(apic_id); - - dbgln_if(APIC_DEBUG, "Enabling local APIC for CPU #{}, logical APIC ID: {}", cpu, apic_id); - - if (cpu == 0) { - SpuriousInterruptHandler::initialize(IRQ_APIC_SPURIOUS); - - APICErrInterruptHandler::initialize(IRQ_APIC_ERR); - - // register IPI interrupt vector - APICIPIInterruptHandler::initialize(IRQ_APIC_IPI); - } - - if (!m_is_x2.was_set()) { - // local destination mode (flat mode), not supported in x2 mode - write_register(APIC_REG_DF, 0xf0000000); - } - - // set error interrupt vector - set_lvt(APIC_REG_LVT_ERR, IRQ_APIC_ERR); - - // set spurious interrupt vector - set_siv(APIC_REG_SIV, IRQ_APIC_SPURIOUS); - - write_register(APIC_REG_LVT_TIMER, APIC_LVT(0, 0) | APIC_LVT_MASKED); - write_register(APIC_REG_LVT_THERMAL, APIC_LVT(0, 0) | APIC_LVT_MASKED); - write_register(APIC_REG_LVT_PERFORMANCE_COUNTER, APIC_LVT(0, 0) | APIC_LVT_MASKED); - write_register(APIC_REG_LVT_LINT0, APIC_LVT(0, 7) | APIC_LVT_MASKED); - write_register(APIC_REG_LVT_LINT1, APIC_LVT(0, 0) | APIC_LVT_TRIGGER_LEVEL); - - write_register(APIC_REG_TPR, 0); -} - -Thread* APIC::get_idle_thread(u32 cpu) const -{ - VERIFY(cpu > 0); - return m_ap_idle_threads[cpu - 1]; -} - -UNMAP_AFTER_INIT void APIC::init_finished(u32 cpu) -{ - // This method is called once the boot stack is no longer needed - VERIFY(cpu > 0); - VERIFY(cpu < m_processor_enabled_cnt); - // Since we're waiting on other APs here, we shouldn't have the - // scheduler lock - VERIFY(!g_scheduler_lock.is_locked_by_current_processor()); - - // Notify the BSP that we are done initializing. It will unmap the startup data at P8000 - m_apic_ap_count.fetch_add(1, AK::MemoryOrder::memory_order_acq_rel); - dbgln_if(APIC_DEBUG, "APIC: CPU #{} initialized, waiting for all others", cpu); - - // The reason we're making all APs wait until the BSP signals them is that - // we don't want APs to trigger IPIs (e.g. through MM) while the BSP - // is unable to process them - while (!m_apic_ap_continue.load(AK::MemoryOrder::memory_order_consume)) { - microseconds_delay(200); - } - - dbgln_if(APIC_DEBUG, "APIC: CPU #{} continues, all others are initialized", cpu); - - // do_boot_aps() freed memory, so we need to update our tlb - Processor::flush_entire_tlb_local(); - - // Now enable all the interrupts - APIC::the().enable(cpu); -} - -void APIC::broadcast_ipi() -{ - dbgln_if(APIC_SMP_DEBUG, "SMP: Broadcast IPI from CPU #{}", Processor::current_id()); - wait_for_pending_icr(); - write_icr({ IRQ_APIC_IPI + IRQ_VECTOR_BASE, 0xffffffff, ICRReg::Fixed, ICRReg::Logical, ICRReg::Assert, ICRReg::TriggerMode::Edge, ICRReg::AllExcludingSelf }); -} - -void APIC::send_ipi(u32 cpu) -{ - dbgln_if(APIC_SMP_DEBUG, "SMP: Send IPI from CPU #{} to CPU #{}", Processor::current_id(), cpu); - VERIFY(cpu != Processor::current_id()); - VERIFY(cpu < Processor::count()); - wait_for_pending_icr(); - write_icr({ IRQ_APIC_IPI + IRQ_VECTOR_BASE, m_is_x2.was_set() ? Processor::by_id(cpu).info().apic_id() : cpu, ICRReg::Fixed, m_is_x2.was_set() ? ICRReg::Physical : ICRReg::Logical, ICRReg::Assert, ICRReg::TriggerMode::Edge, ICRReg::NoShorthand }); -} - -UNMAP_AFTER_INIT APICTimer* APIC::initialize_timers(HardwareTimerBase& calibration_timer) -{ - if (!m_apic_base && !m_is_x2.was_set()) - return nullptr; - - // We should only initialize and calibrate the APIC timer once on the BSP! - VERIFY(Processor::is_bootstrap_processor()); - VERIFY(!m_apic_timer); - - m_apic_timer = APICTimer::initialize(IRQ_APIC_TIMER, calibration_timer); - return m_apic_timer; -} - -void APIC::setup_local_timer(u32 ticks, TimerMode timer_mode, bool enable) -{ - u32 flags = 0; - switch (timer_mode) { - case TimerMode::OneShot: - flags |= APIC_LVT_TIMER_ONESHOT; - break; - case TimerMode::Periodic: - flags |= APIC_LVT_TIMER_PERIODIC; - break; - case TimerMode::TSCDeadline: - flags |= APIC_LVT_TIMER_TSCDEADLINE; - break; - } - if (!enable) - flags |= APIC_LVT_MASKED; - write_register(APIC_REG_LVT_TIMER, APIC_LVT(IRQ_APIC_TIMER + IRQ_VECTOR_BASE, 0) | flags); - - u32 config = read_register(APIC_REG_TIMER_CONFIGURATION); - config &= ~0xf; // clear divisor (bits 0-3) - switch (get_timer_divisor()) { - case 1: - config |= (1 << 3) | 3; - break; - case 2: - break; - case 4: - config |= 1; - break; - case 8: - config |= 2; - break; - case 16: - config |= 3; - break; - case 32: - config |= (1 << 3); - break; - case 64: - config |= (1 << 3) | 1; - break; - case 128: - config |= (1 << 3) | 2; - break; - default: - VERIFY_NOT_REACHED(); - } - write_register(APIC_REG_TIMER_CONFIGURATION, config); - - if (timer_mode == TimerMode::Periodic) - write_register(APIC_REG_TIMER_INITIAL_COUNT, ticks / get_timer_divisor()); -} - -u32 APIC::get_timer_current_count() -{ - return read_register(APIC_REG_TIMER_CURRENT_COUNT); -} - -u32 APIC::get_timer_divisor() -{ - return 16; -} - -bool APICIPIInterruptHandler::handle_interrupt(RegisterState const&) -{ - dbgln_if(APIC_SMP_DEBUG, "APIC IPI on CPU #{}", Processor::current_id()); - return true; -} - -bool APICIPIInterruptHandler::eoi() -{ - dbgln_if(APIC_SMP_DEBUG, "SMP: IPI EOI"); - APIC::the().eoi(); - return true; -} - -bool APICErrInterruptHandler::handle_interrupt(RegisterState const&) -{ - dbgln("APIC: SMP error on CPU #{}", Processor::current_id()); - return true; -} - -bool APICErrInterruptHandler::eoi() -{ - APIC::the().eoi(); - return true; -} - -bool HardwareTimer::eoi() -{ - APIC::the().eoi(); - return true; -} - -} diff --git a/Kernel/Arch/x86_64/Interrupts/APIC.h b/Kernel/Arch/x86_64/Interrupts/APIC.h deleted file mode 100644 index fbfe0953064..00000000000 --- a/Kernel/Arch/x86_64/Interrupts/APIC.h +++ /dev/null @@ -1,116 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include - -namespace Kernel { - -class APICTimer; - -struct LocalAPIC { - u32 apic_id; -}; - -class APIC { -public: - static APIC& the(); - static void initialize(); - static bool initialized(); - - bool init_bsp(); - void eoi(); - void setup_ap_boot_environment(); - void boot_aps(); - void enable(u32 cpu); - void init_finished(u32 cpu); - void broadcast_ipi(); - void send_ipi(u32 cpu); - static u8 spurious_interrupt_vector(); - Thread* get_idle_thread(u32 cpu) const; - u32 enabled_processor_count() const { return m_processor_enabled_cnt; } - - APICTimer* initialize_timers(HardwareTimerBase&); - APICTimer* get_timer() const { return m_apic_timer; } - enum class TimerMode { - OneShot, - Periodic, - TSCDeadline - }; - void setup_local_timer(u32, TimerMode, bool); - u32 get_timer_current_count(); - u32 get_timer_divisor(); - -private: - struct ICRReg { - enum DeliveryMode { - Fixed = 0x0, - LowPriority = 0x1, - SMI = 0x2, - NMI = 0x4, - INIT = 0x5, - StartUp = 0x6, - }; - enum DestinationMode { - Physical = 0x0, - Logical = 0x1, - }; - enum Level { - DeAssert = 0x0, - Assert = 0x1 - }; - enum class TriggerMode { - Edge = 0x0, - Level = 0x1, - }; - enum DestinationShorthand { - NoShorthand = 0x0, - Self = 0x1, - AllIncludingSelf = 0x2, - AllExcludingSelf = 0x3, - }; - - u8 vector { 0 }; - u32 destination { 0 }; - DeliveryMode delivery_mode { DeliveryMode::Fixed }; - DestinationMode destination_mode { DestinationMode::Physical }; - Level level { Level::DeAssert }; - TriggerMode trigger_mode { TriggerMode::Edge }; - DestinationShorthand destination_short { DestinationShorthand::NoShorthand }; - - u32 x_low() const { return (u32)vector | (delivery_mode << 8) | (destination_mode << 11) | (level << 14) | (static_cast(trigger_mode) << 15) | (destination_short << 18); } - u32 x_high() const { return destination << 24; } - u64 x2_value() const { return ((u64)destination << 32) | x_low(); } - }; - - OwnPtr m_apic_base; - Vector> m_ap_processor_info; - Vector> m_ap_temporary_boot_stacks; - Vector m_ap_idle_threads; - OwnPtr m_ap_boot_environment; - Atomic m_apic_ap_count { 0 }; - Atomic m_apic_ap_continue { 0 }; - u32 m_processor_cnt { 0 }; - u32 m_processor_enabled_cnt { 0 }; - APICTimer* m_apic_timer { nullptr }; - SetOnce m_is_x2; - - static PhysicalAddress get_base(); - void set_base(PhysicalAddress const& base); - void write_register(u32 offset, u32 value); - u32 read_register(u32 offset); - void set_lvt(u32 offset, u8 interrupt); - void set_siv(u32 offset, u8 interrupt); - void wait_for_pending_icr(); - void write_icr(ICRReg const& icr); - void do_boot_aps(); -}; - -} diff --git a/Kernel/Arch/x86_64/Interrupts/IOAPIC.cpp b/Kernel/Arch/x86_64/Interrupts/IOAPIC.cpp deleted file mode 100644 index 92eda676839..00000000000 --- a/Kernel/Arch/x86_64/Interrupts/IOAPIC.cpp +++ /dev/null @@ -1,311 +0,0 @@ -/* - * Copyright (c) 2020, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include -#include -#include - -#define IOAPIC_REDIRECTION_ENTRY_OFFSET 0x10 -namespace Kernel { -enum DeliveryMode { - Normal = 0, - LowPriority = 1, - SMI = 2, - NMI = 3, - INIT = 4, - External = 7 -}; - -UNMAP_AFTER_INIT IOAPIC::IOAPIC(PhysicalAddress address, u32 gsi_base) - : m_address(address) - , m_regs(Memory::map_typed_writable(m_address).release_value_but_fixme_should_propagate_errors()) - , m_gsi_base(gsi_base) - , m_id((read_register(0x0) >> 24) & 0xFF) - , m_version(read_register(0x1) & 0xFF) - , m_redirection_entries_count((read_register(0x1) >> 16) + 1) -{ - InterruptDisabler disabler; - dmesgln("IOAPIC ID: {:#x}", m_id); - dmesgln("IOAPIC Version: {:#x}, redirection entries: {}", m_version, m_redirection_entries_count); - dmesgln("IOAPIC Arbitration ID {:#x}", read_register(0x2)); - mask_all_redirection_entries(); -} - -UNMAP_AFTER_INIT void IOAPIC::initialize() -{ -} - -void IOAPIC::map_interrupt_redirection(u8 interrupt_vector) -{ - InterruptDisabler disabler; - for (auto redirection_override : InterruptManagement::the().isa_overrides()) { - if (redirection_override.source() != interrupt_vector) - continue; - bool active_low = false; - // See ACPI spec Version 6.2, page 205 to learn more about Interrupt Overriding Flags. - switch ((redirection_override.flags() & 0b11)) { - case 0: - active_low = false; - break; - case 1: - active_low = false; - break; - case 2: - VERIFY_NOT_REACHED(); // Reserved value - case 3: - active_low = true; - break; - } - - bool trigger_level_mode = false; - // See ACPI spec Version 6.2, page 205 to learn more about Interrupt Overriding Flags. - switch (((redirection_override.flags() >> 2) & 0b11)) { - case 0: - trigger_level_mode = false; - break; - case 1: - trigger_level_mode = false; - break; - case 2: - VERIFY_NOT_REACHED(); // Reserved value - case 3: - trigger_level_mode = true; - break; - } - configure_redirection_entry(redirection_override.gsi() - gsi_base(), InterruptManagement::acquire_mapped_interrupt_number(redirection_override.source()) + IRQ_VECTOR_BASE, DeliveryMode::Normal, false, active_low, trigger_level_mode, true, 0); - return; - } - isa_identity_map(interrupt_vector); -} - -void IOAPIC::isa_identity_map(size_t index) -{ - InterruptDisabler disabler; - configure_redirection_entry(index, InterruptManagement::acquire_mapped_interrupt_number(index) + IRQ_VECTOR_BASE, DeliveryMode::Normal, false, false, false, true, 0); -} - -void IOAPIC::map_pci_interrupts() -{ - InterruptDisabler disabler; - configure_redirection_entry(11, 11 + IRQ_VECTOR_BASE, DeliveryMode::Normal, false, false, true, true, 0); -} - -bool IOAPIC::is_enabled() const -{ - return !is_hard_disabled(); -} - -void IOAPIC::spurious_eoi(GenericInterruptHandler const& handler) const -{ - InterruptDisabler disabler; - VERIFY(handler.type() == HandlerType::SpuriousInterruptHandler); - VERIFY(handler.interrupt_number() == APIC::spurious_interrupt_vector()); - dbgln("IOAPIC: Spurious interrupt"); -} - -void IOAPIC::map_isa_interrupts() -{ - InterruptDisabler disabler; - for (auto redirection_override : InterruptManagement::the().isa_overrides()) { - if ((redirection_override.gsi() < gsi_base()) || (redirection_override.gsi() >= (gsi_base() + m_redirection_entries_count))) - continue; - bool active_low = false; - // See ACPI spec Version 6.2, page 205 to learn more about Interrupt Overriding Flags. - switch ((redirection_override.flags() & 0b11)) { - case 0: - active_low = false; - break; - case 1: - active_low = false; - break; - case 2: - VERIFY_NOT_REACHED(); - case 3: - active_low = true; - break; - } - - bool trigger_level_mode = false; - // See ACPI spec Version 6.2, page 205 to learn more about Interrupt Overriding Flags. - switch (((redirection_override.flags() >> 2) & 0b11)) { - case 0: - trigger_level_mode = false; - break; - case 1: - trigger_level_mode = false; - break; - case 2: - VERIFY_NOT_REACHED(); - case 3: - trigger_level_mode = true; - break; - } - configure_redirection_entry(redirection_override.gsi() - gsi_base(), InterruptManagement::acquire_mapped_interrupt_number(redirection_override.source()) + IRQ_VECTOR_BASE, 0, false, active_low, trigger_level_mode, true, 0); - } -} - -void IOAPIC::reset_all_redirection_entries() const -{ - InterruptDisabler disabler; - for (size_t index = 0; index < m_redirection_entries_count; index++) - reset_redirection_entry(index); -} - -void IOAPIC::hard_disable() -{ - InterruptDisabler disabler; - reset_all_redirection_entries(); - IRQController::hard_disable(); -} - -void IOAPIC::reset_redirection_entry(size_t index) const -{ - InterruptDisabler disabler; - configure_redirection_entry(index, 0, 0, false, false, false, true, 0); -} - -void IOAPIC::configure_redirection_entry(size_t index, u8 interrupt_vector, u8 delivery_mode, bool logical_destination, bool active_low, bool trigger_level_mode, bool masked, u8 destination) const -{ - InterruptDisabler disabler; - VERIFY(index < m_redirection_entries_count); - u32 redirection_entry1 = interrupt_vector | (delivery_mode & 0b111) << 8 | logical_destination << 11 | active_low << 13 | trigger_level_mode << 15 | masked << 16; - u32 redirection_entry2 = destination << 24; - write_register((index << 1) + IOAPIC_REDIRECTION_ENTRY_OFFSET, redirection_entry1); - - if constexpr (IOAPIC_DEBUG) - dbgln("IOAPIC Value: {:#x}", read_register((index << 1) + IOAPIC_REDIRECTION_ENTRY_OFFSET)); - - write_register((index << 1) + IOAPIC_REDIRECTION_ENTRY_OFFSET + 1, redirection_entry2); - - if constexpr (IOAPIC_DEBUG) - dbgln("IOAPIC Value: {:#x}", read_register((index << 1) + 0x11)); -} - -void IOAPIC::mask_all_redirection_entries() const -{ - InterruptDisabler disabler; - for (size_t index = 0; index < m_redirection_entries_count; index++) - mask_redirection_entry(index); -} - -void IOAPIC::mask_redirection_entry(u8 index) const -{ - VERIFY(index < m_redirection_entries_count); - u32 redirection_entry = read_register((index << 1) + IOAPIC_REDIRECTION_ENTRY_OFFSET); - if (redirection_entry & (1 << 16)) - return; - write_register((index << 1) + IOAPIC_REDIRECTION_ENTRY_OFFSET, redirection_entry | (1 << 16)); -} - -bool IOAPIC::is_redirection_entry_masked(u8 index) const -{ - VERIFY(index < m_redirection_entries_count); - return (read_register((index << 1) + IOAPIC_REDIRECTION_ENTRY_OFFSET) & (1 << 16)) != 0; -} - -void IOAPIC::unmask_redirection_entry(u8 index) const -{ - VERIFY(index < m_redirection_entries_count); - u32 redirection_entry = read_register((index << 1) + IOAPIC_REDIRECTION_ENTRY_OFFSET); - if (!(redirection_entry & (1 << 16))) - return; - write_register((index << 1) + IOAPIC_REDIRECTION_ENTRY_OFFSET, redirection_entry & ~(1 << 16)); -} - -bool IOAPIC::is_vector_enabled(u8 interrupt_vector) const -{ - InterruptDisabler disabler; - return is_redirection_entry_masked(interrupt_vector); -} - -u8 IOAPIC::read_redirection_entry_vector(u8 index) const -{ - VERIFY(index < m_redirection_entries_count); - return (read_register((index << 1) + IOAPIC_REDIRECTION_ENTRY_OFFSET) & 0xFF); -} - -Optional IOAPIC::find_redirection_entry_by_vector(u8 vector) const -{ - InterruptDisabler disabler; - for (size_t index = 0; index < m_redirection_entries_count; index++) { - if (read_redirection_entry_vector(index) == (InterruptManagement::acquire_mapped_interrupt_number(vector) + IRQ_VECTOR_BASE)) - return index; - } - return {}; -} - -void IOAPIC::disable(GenericInterruptHandler const& handler) -{ - InterruptDisabler disabler; - VERIFY(!is_hard_disabled()); - u8 interrupt_vector = handler.interrupt_number(); - VERIFY(interrupt_vector >= gsi_base() && interrupt_vector < interrupt_vectors_count()); - auto found_index = find_redirection_entry_by_vector(interrupt_vector); - if (!found_index.has_value()) { - map_interrupt_redirection(interrupt_vector); - found_index = find_redirection_entry_by_vector(interrupt_vector); - } - VERIFY(found_index.has_value()); - mask_redirection_entry(found_index.value()); -} - -void IOAPIC::enable(GenericInterruptHandler const& handler) -{ - InterruptDisabler disabler; - VERIFY(!is_hard_disabled()); - u8 interrupt_vector = handler.interrupt_number(); - VERIFY(interrupt_vector >= gsi_base() && interrupt_vector < interrupt_vectors_count()); - auto found_index = find_redirection_entry_by_vector(interrupt_vector); - if (!found_index.has_value()) { - map_interrupt_redirection(interrupt_vector); - found_index = find_redirection_entry_by_vector(interrupt_vector); - } - VERIFY(found_index.has_value()); - unmask_redirection_entry(found_index.value()); -} - -void IOAPIC::eoi(GenericInterruptHandler const& handler) const -{ - InterruptDisabler disabler; - VERIFY(!is_hard_disabled()); - VERIFY(handler.interrupt_number() >= gsi_base() && handler.interrupt_number() < interrupt_vectors_count()); - VERIFY(handler.type() != HandlerType::SpuriousInterruptHandler); - APIC::the().eoi(); -} - -u16 IOAPIC::get_isr() const -{ - InterruptDisabler disabler; - VERIFY_NOT_REACHED(); -} - -u16 IOAPIC::get_irr() const -{ - InterruptDisabler disabler; - VERIFY_NOT_REACHED(); -} - -void IOAPIC::write_register(u32 index, u32 value) const -{ - InterruptDisabler disabler; - m_regs->select = index; - m_regs->window = value; - - dbgln_if(IOAPIC_DEBUG, "IOAPIC Writing, Value {:#x} @ offset {:#x}", (u32)m_regs->window, (u32)m_regs->select); -} -u32 IOAPIC::read_register(u32 index) const -{ - InterruptDisabler disabler; - m_regs->select = index; - dbgln_if(IOAPIC_DEBUG, "IOAPIC Reading, Value {:#x} @ offset {:#x}", (u32)m_regs->window, (u32)m_regs->select); - return m_regs->window; -} - -} diff --git a/Kernel/Arch/x86_64/Interrupts/IOAPIC.h b/Kernel/Arch/x86_64/Interrupts/IOAPIC.h deleted file mode 100644 index 7079897af2c..00000000000 --- a/Kernel/Arch/x86_64/Interrupts/IOAPIC.h +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Copyright (c) 2020, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include - -namespace Kernel { -struct [[gnu::packed]] ioapic_mmio_regs { - u32 volatile select; - u32 reserved[3]; - u32 volatile window; -}; - -class PCIInterruptOverrideMetadata { -public: - PCIInterruptOverrideMetadata(u8 bus_id, u8 polarity, u8 trigger_mode, u8 source_irq, u32 ioapic_id, u16 ioapic_int_pin); - u8 bus() const { return m_bus_id; } - u8 polarity() const { return m_polarity; } - u8 trigger_mode() const { return m_trigger_mode; } - u8 pci_interrupt_pin() const { return m_pci_interrupt_pin; } - u8 pci_device_number() const { return m_pci_device_number; } - u32 ioapic_id() const { return m_ioapic_id; } - u16 ioapic_interrupt_pin() const { return m_ioapic_interrupt_pin; } - -private: - u8 const m_bus_id; - u8 const m_polarity; - u8 const m_trigger_mode; - u8 const m_pci_interrupt_pin; - u8 const m_pci_device_number; - u32 const m_ioapic_id; - u16 const m_ioapic_interrupt_pin; -}; - -class IOAPIC final : public IRQController { -public: - IOAPIC(PhysicalAddress, u32 gsi_base); - virtual void enable(GenericInterruptHandler const&) override; - virtual void disable(GenericInterruptHandler const&) override; - virtual void hard_disable() override; - virtual void eoi(GenericInterruptHandler const&) const override; - virtual void spurious_eoi(GenericInterruptHandler const&) const override; - virtual bool is_vector_enabled(u8 number) const override; - virtual bool is_enabled() const override; - virtual u16 get_isr() const override; - virtual u16 get_irr() const override; - virtual u32 gsi_base() const override { return m_gsi_base; } - virtual size_t interrupt_vectors_count() const override { return m_redirection_entries_count; } - virtual StringView model() const override { return "IOAPIC"sv; } - virtual IRQControllerType type() const override { return IRQControllerType::i82093AA; } - -private: - void configure_redirection_entry(size_t index, u8 interrupt_vector, u8 delivery_mode, bool logical_destination, bool active_low, bool trigger_level_mode, bool masked, u8 destination) const; - void reset_redirection_entry(size_t index) const; - void map_interrupt_redirection(u8 interrupt_vector); - void reset_all_redirection_entries() const; - - void mask_all_redirection_entries() const; - void mask_redirection_entry(u8 index) const; - void unmask_redirection_entry(u8 index) const; - bool is_redirection_entry_masked(u8 index) const; - - u8 read_redirection_entry_vector(u8 index) const; - Optional find_redirection_entry_by_vector(u8 vector) const; - - void write_register(u32 index, u32 value) const; - u32 read_register(u32 index) const; - - virtual void initialize() override; - void map_isa_interrupts(); - void map_pci_interrupts(); - void isa_identity_map(size_t index); - - PhysicalAddress m_address; - mutable Memory::TypedMapping m_regs; - u32 m_gsi_base; - u8 m_id; - u8 m_version; - size_t m_redirection_entries_count; -}; -} diff --git a/Kernel/Arch/x86_64/Interrupts/PIC.cpp b/Kernel/Arch/x86_64/Interrupts/PIC.cpp deleted file mode 100644 index 48c282c9322..00000000000 --- a/Kernel/Arch/x86_64/Interrupts/PIC.cpp +++ /dev/null @@ -1,230 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include -#include -#include - -namespace Kernel { - -// The slave 8259 is connected to the master's IRQ2 line. -// This is really only to enhance clarity. -#define SLAVE_INDEX 2 - -#define PIC0_CTL 0x20 -#define PIC0_CMD 0x21 -#define PIC1_CTL 0xA0 -#define PIC1_CMD 0xA1 - -#define ICW1_ICW4 0x01 // ICW4 (not) needed -#define ICW1_SINGLE 0x02 // Single (cascade) mode -#define ICW1_INTERVAL4 0x04 // Call address interval 4 (8) -#define ICW1_LEVEL 0x08 // Level triggered (edge) mode -#define ICW1_INIT 0x10 // Initialization - required - -#define ICW4_8086 0x01 // 8086/88 (MCS-80/85) mode -#define ICW4_AUTO 0x02 // Auto (normal) EOI -#define ICW4_BUF_SLAVE 0x08 // Buffered mode/slave -#define ICW4_BUF_MASTER 0x0C // Buffered mode/master -#define ICW4_SFNM 0x10 // Special fully nested (not) - -bool inline static is_all_masked(u16 reg) -{ - return reg == 0xFFFF; -} - -bool PIC::is_enabled() const -{ - return !is_all_masked(m_cached_irq_mask) && !is_hard_disabled(); -} - -void PIC::disable(GenericInterruptHandler const& handler) -{ - InterruptDisabler disabler; - VERIFY(!is_hard_disabled()); - VERIFY(handler.interrupt_number() >= gsi_base() && handler.interrupt_number() < interrupt_vectors_count()); - u8 irq = handler.interrupt_number(); - if (m_cached_irq_mask & (1 << irq)) - return; - u8 imr; - if (irq & 8) { - imr = IO::in8(PIC1_CMD); - imr |= 1 << (irq & 7); - IO::out8(PIC1_CMD, imr); - } else { - imr = IO::in8(PIC0_CMD); - imr |= 1 << irq; - IO::out8(PIC0_CMD, imr); - } - m_cached_irq_mask |= 1 << irq; -} - -UNMAP_AFTER_INIT PIC::PIC() -{ - initialize(); -} - -void PIC::spurious_eoi(GenericInterruptHandler const& handler) const -{ - VERIFY(handler.type() == HandlerType::SpuriousInterruptHandler); - if (handler.interrupt_number() == 7) - return; - if (handler.interrupt_number() == 15) { - IO::in8(PIC1_CMD); /* dummy read */ - IO::out8(PIC0_CTL, 0x60 | (2)); - } -} - -bool PIC::is_vector_enabled(u8 irq) const -{ - return m_cached_irq_mask & (1 << irq); -} - -void PIC::enable(GenericInterruptHandler const& handler) -{ - InterruptDisabler disabler; - VERIFY(!is_hard_disabled()); - VERIFY(handler.interrupt_number() >= gsi_base() && handler.interrupt_number() < interrupt_vectors_count()); - enable_vector(handler.interrupt_number()); -} - -void PIC::enable_vector(u8 irq) -{ - InterruptDisabler disabler; - VERIFY(!is_hard_disabled()); - if (!(m_cached_irq_mask & (1 << irq))) - return; - u8 imr; - if (irq & 8) { - imr = IO::in8(PIC1_CMD); - imr &= ~(1 << (irq & 7)); - IO::out8(PIC1_CMD, imr); - } else { - imr = IO::in8(PIC0_CMD); - imr &= ~(1 << irq); - IO::out8(PIC0_CMD, imr); - } - m_cached_irq_mask &= ~(1 << irq); -} - -void PIC::eoi(GenericInterruptHandler const& handler) const -{ - InterruptDisabler disabler; - VERIFY(!is_hard_disabled()); - u8 irq = handler.interrupt_number(); - VERIFY(irq >= gsi_base() && irq < interrupt_vectors_count()); - if ((1 << irq) & m_cached_irq_mask) { - spurious_eoi(handler); - return; - } - eoi_interrupt(irq); -} - -void PIC::eoi_interrupt(u8 irq) const -{ - if (irq & 8) { - IO::in8(PIC1_CMD); /* dummy read */ - IO::out8(PIC1_CTL, 0x60 | (irq & 7)); - IO::out8(PIC0_CTL, 0x60 | (2)); - return; - } - IO::in8(PIC0_CMD); /* dummy read */ - IO::out8(PIC0_CTL, 0x60 | irq); -} - -void PIC::complete_eoi() const -{ - IO::out8(PIC1_CTL, 0x20); - IO::out8(PIC0_CTL, 0x20); -} - -void PIC::hard_disable() -{ - InterruptDisabler disabler; - remap(pic_disabled_vector_base); - IO::out8(PIC0_CMD, 0xff); - IO::out8(PIC1_CMD, 0xff); - m_cached_irq_mask = 0xffff; - IRQController::hard_disable(); -} - -void PIC::remap(u8 offset) -{ - /* ICW1 (edge triggered mode, cascading controllers, expect ICW4) */ - IO::out8(PIC0_CTL, ICW1_INIT | ICW1_ICW4); - IO::out8(PIC1_CTL, ICW1_INIT | ICW1_ICW4); - - /* ICW2 (upper 5 bits specify ISR indices, lower 3 don't specify anything) */ - IO::out8(PIC0_CMD, offset); - IO::out8(PIC1_CMD, offset + 0x08); - - /* ICW3 (configure master/slave relationship) */ - IO::out8(PIC0_CMD, 1 << SLAVE_INDEX); - IO::out8(PIC1_CMD, SLAVE_INDEX); - - /* ICW4 (set x86 mode) */ - IO::out8(PIC0_CMD, ICW4_8086); - IO::out8(PIC1_CMD, ICW4_8086); - - // Mask -- start out with all IRQs disabled. - IO::out8(PIC0_CMD, 0xff); - IO::out8(PIC1_CMD, 0xff); - m_cached_irq_mask = 0xffff; - - // ...except IRQ2, since that's needed for the master to let through slave interrupts. - enable_vector(2); -} - -UNMAP_AFTER_INIT void PIC::initialize() -{ - /* ICW1 (edge triggered mode, cascading controllers, expect ICW4) */ - IO::out8(PIC0_CTL, ICW1_INIT | ICW1_ICW4); - IO::out8(PIC1_CTL, ICW1_INIT | ICW1_ICW4); - - /* ICW2 (upper 5 bits specify ISR indices, lower 3 don't specify anything) */ - IO::out8(PIC0_CMD, IRQ_VECTOR_BASE); - IO::out8(PIC1_CMD, IRQ_VECTOR_BASE + 0x08); - - /* ICW3 (configure master/slave relationship) */ - IO::out8(PIC0_CMD, 1 << SLAVE_INDEX); - IO::out8(PIC1_CMD, SLAVE_INDEX); - - /* ICW4 (set x86 mode) */ - IO::out8(PIC0_CMD, ICW4_8086); - IO::out8(PIC1_CMD, ICW4_8086); - - // Mask -- start out with all IRQs disabled. - IO::out8(PIC0_CMD, 0xff); - IO::out8(PIC1_CMD, 0xff); - - // ...except IRQ2, since that's needed for the master to let through slave interrupts. - enable_vector(2); - - dmesgln("PIC: Cascading mode, vectors {:#02x}-{:#02x}", IRQ_VECTOR_BASE, IRQ_VECTOR_BASE + 0xf); -} - -u16 PIC::get_isr() const -{ - IO::out8(PIC0_CTL, 0x0b); - IO::out8(PIC1_CTL, 0x0b); - u8 isr0 = IO::in8(PIC0_CTL); - u8 isr1 = IO::in8(PIC1_CTL); - return (isr1 << 8) | isr0; -} - -u16 PIC::get_irr() const -{ - IO::out8(PIC0_CTL, 0x0a); - IO::out8(PIC1_CTL, 0x0a); - u8 irr0 = IO::in8(PIC0_CTL); - u8 irr1 = IO::in8(PIC1_CTL); - return (irr1 << 8) | irr0; -} -} diff --git a/Kernel/Arch/x86_64/Interrupts/PIC.h b/Kernel/Arch/x86_64/Interrupts/PIC.h deleted file mode 100644 index e88e72326fd..00000000000 --- a/Kernel/Arch/x86_64/Interrupts/PIC.h +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include - -namespace Kernel { - -static constexpr size_t pic_disabled_vector_base = 0x20; -static constexpr size_t pic_disabled_vector_end = 0x2f; - -class PIC final : public IRQController { -public: - PIC(); - virtual void enable(GenericInterruptHandler const&) override; - virtual void disable(GenericInterruptHandler const&) override; - virtual void hard_disable() override; - virtual void eoi(GenericInterruptHandler const&) const override; - virtual bool is_vector_enabled(u8 number) const override; - virtual bool is_enabled() const override; - virtual void spurious_eoi(GenericInterruptHandler const&) const override; - virtual u16 get_isr() const override; - virtual u16 get_irr() const override; - virtual u32 gsi_base() const override { return 0; } - virtual size_t interrupt_vectors_count() const override { return 16; } - virtual StringView model() const override { return "Dual i8259"sv; } - virtual IRQControllerType type() const override { return IRQControllerType::i8259; } - -private: - u16 m_cached_irq_mask { 0xffff }; - void eoi_interrupt(u8 irq) const; - void enable_vector(u8 number); - void remap(u8 offset); - void complete_eoi() const; - virtual void initialize() override; -}; - -} diff --git a/Kernel/Arch/x86_64/MSR.h b/Kernel/Arch/x86_64/MSR.h deleted file mode 100644 index ff2810d62e7..00000000000 --- a/Kernel/Arch/x86_64/MSR.h +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright (c) 2018-2021, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include - -#include - -#include -VALIDATE_IS_X86() - -namespace Kernel { - -class MSR { - uint32_t m_msr; - -public: - static bool have() - { - CPUID id(1); - return (id.edx() & (1 << 5)) != 0; - } - - MSR(const MSR&) = delete; - MSR& operator=(const MSR&) = delete; - - MSR(uint32_t msr) - : m_msr(msr) - { - } - - [[nodiscard]] u64 get() - { - u32 low, high; - asm volatile("rdmsr" - : "=a"(low), "=d"(high) - : "c"(m_msr)); - return ((u64)high << 32) | low; - } - - void set(u64 value) - { - u32 low = value & 0xffffffff; - u32 high = value >> 32; - asm volatile("wrmsr" ::"a"(low), "d"(high), "c"(m_msr)); - } -}; - -} diff --git a/Kernel/Arch/x86_64/NonMaskableInterruptDisabler.h b/Kernel/Arch/x86_64/NonMaskableInterruptDisabler.h deleted file mode 100644 index f08ada98fb2..00000000000 --- a/Kernel/Arch/x86_64/NonMaskableInterruptDisabler.h +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright (c) 2018-2021, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include - -#include - -namespace Kernel { - -class NonMaskableInterruptDisabler { -public: - NonMaskableInterruptDisabler() - { - IO::out8(0x70, IO::in8(0x70) | 0x80); - } - - ~NonMaskableInterruptDisabler() - { - IO::out8(0x70, IO::in8(0x70) & 0x7F); - } -}; - -} diff --git a/Kernel/Arch/x86_64/PCI/Controller/PIIX4HostBridge.cpp b/Kernel/Arch/x86_64/PCI/Controller/PIIX4HostBridge.cpp deleted file mode 100644 index da84509d4ed..00000000000 --- a/Kernel/Arch/x86_64/PCI/Controller/PIIX4HostBridge.cpp +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright (c) 2022, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include - -namespace Kernel::PCI { - -NonnullOwnPtr PIIX4HostBridge::must_create_with_io_access() -{ - PCI::Domain domain { 0, 0, 0xff }; - return adopt_own_if_nonnull(new (nothrow) PIIX4HostBridge(domain)).release_nonnull(); -} - -PIIX4HostBridge::PIIX4HostBridge(PCI::Domain const& domain) - : HostController(domain) -{ -} - -static u32 io_address_for_pci_field(BusNumber bus, DeviceNumber device, FunctionNumber function, u8 field) -{ - return 0x80000000u | (bus.value() << 16u) | (device.value() << 11u) | (function.value() << 8u) | (field & 0xfc); -} - -void PIIX4HostBridge::write8_field_locked(BusNumber bus, DeviceNumber device, FunctionNumber function, u32 field, u8 value) -{ - VERIFY(m_access_lock.is_locked()); - IO::out32(PCI::address_port, io_address_for_pci_field(bus, device, function, field)); - IO::out8(PCI::value_port + (field & 3), value); -} - -void PIIX4HostBridge::write16_field_locked(BusNumber bus, DeviceNumber device, FunctionNumber function, u32 field, u16 value) -{ - VERIFY(m_access_lock.is_locked()); - IO::out32(PCI::address_port, io_address_for_pci_field(bus, device, function, field)); - IO::out16(PCI::value_port + (field & 2), value); -} - -void PIIX4HostBridge::write32_field_locked(BusNumber bus, DeviceNumber device, FunctionNumber function, u32 field, u32 value) -{ - VERIFY(m_access_lock.is_locked()); - IO::out32(PCI::address_port, io_address_for_pci_field(bus, device, function, field)); - IO::out32(PCI::value_port, value); -} - -u8 PIIX4HostBridge::read8_field_locked(BusNumber bus, DeviceNumber device, FunctionNumber function, u32 field) -{ - VERIFY(m_access_lock.is_locked()); - IO::out32(PCI::address_port, io_address_for_pci_field(bus, device, function, field)); - return IO::in8(PCI::value_port + (field & 3)); -} - -u16 PIIX4HostBridge::read16_field_locked(BusNumber bus, DeviceNumber device, FunctionNumber function, u32 field) -{ - VERIFY(m_access_lock.is_locked()); - IO::out32(PCI::address_port, io_address_for_pci_field(bus, device, function, field)); - return IO::in16(PCI::value_port + (field & 2)); -} - -u32 PIIX4HostBridge::read32_field_locked(BusNumber bus, DeviceNumber device, FunctionNumber function, u32 field) -{ - VERIFY(m_access_lock.is_locked()); - IO::out32(PCI::address_port, io_address_for_pci_field(bus, device, function, field)); - return IO::in32(PCI::value_port); -} - -} diff --git a/Kernel/Arch/x86_64/PCI/Controller/PIIX4HostBridge.h b/Kernel/Arch/x86_64/PCI/Controller/PIIX4HostBridge.h deleted file mode 100644 index 9f1986ce82f..00000000000 --- a/Kernel/Arch/x86_64/PCI/Controller/PIIX4HostBridge.h +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright (c) 2022, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include -#include - -namespace Kernel::PCI { - -class PIIX4HostBridge : public HostController { -public: - static NonnullOwnPtr must_create_with_io_access(); - -private: - virtual void write8_field_locked(BusNumber, DeviceNumber, FunctionNumber, u32 field, u8 value) override; - virtual void write16_field_locked(BusNumber, DeviceNumber, FunctionNumber, u32 field, u16 value) override; - virtual void write32_field_locked(BusNumber, DeviceNumber, FunctionNumber, u32 field, u32 value) override; - - virtual u8 read8_field_locked(BusNumber, DeviceNumber, FunctionNumber, u32 field) override; - virtual u16 read16_field_locked(BusNumber, DeviceNumber, FunctionNumber, u32 field) override; - virtual u32 read32_field_locked(BusNumber, DeviceNumber, FunctionNumber, u32 field) override; - - explicit PIIX4HostBridge(PCI::Domain const&); -}; - -} diff --git a/Kernel/Arch/x86_64/PCI/Initializer.cpp b/Kernel/Arch/x86_64/PCI/Initializer.cpp deleted file mode 100644 index 08ea2a6a88e..00000000000 --- a/Kernel/Arch/x86_64/PCI/Initializer.cpp +++ /dev/null @@ -1,109 +0,0 @@ -/* - * Copyright (c) 2020, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace Kernel::PCI { - -READONLY_AFTER_INIT SetOnce g_pci_access_io_probe_failed; -READONLY_AFTER_INIT SetOnce g_pci_access_is_disabled_from_commandline; - -static bool test_pci_io(); - -UNMAP_AFTER_INIT static PCIAccessLevel detect_optimal_access_type() -{ - auto boot_determined = kernel_command_line().pci_access_level(); - if (!ACPI::is_enabled() || !ACPI::Parser::the()->find_table("MCFG"sv).has_value()) - return PCIAccessLevel::IOAddressing; - - if (boot_determined != PCIAccessLevel::IOAddressing) - return boot_determined; - - if (!g_pci_access_io_probe_failed.was_set()) - return PCIAccessLevel::IOAddressing; - - PANIC("No PCI bus access method detected!"); -} - -UNMAP_AFTER_INIT void initialize() -{ - if (kernel_command_line().is_pci_disabled()) - g_pci_access_is_disabled_from_commandline.set(); - - Optional possible_mcfg; - // FIXME: There are other arch-specific methods to find the memory range - // for accessing the PCI configuration space. - // For example, the QEMU microvm machine type might expose an FDT so we could - // parse it to find a PCI host bridge. - if (ACPI::is_enabled()) { - possible_mcfg = ACPI::Parser::the()->find_table("MCFG"sv); - if ((!test_pci_io()) && (!possible_mcfg.has_value())) - g_pci_access_io_probe_failed.set(); - } else { - if (!test_pci_io()) - g_pci_access_io_probe_failed.set(); - } - if (g_pci_access_is_disabled_from_commandline.was_set() || g_pci_access_io_probe_failed.was_set()) - return; - switch (detect_optimal_access_type()) { - case PCIAccessLevel::MemoryAddressing: { - VERIFY(possible_mcfg.has_value()); - auto success = Access::initialize_for_multiple_pci_domains(possible_mcfg.value()); - VERIFY(success); - break; - } - case PCIAccessLevel::IOAddressing: { - auto success = Access::initialize_for_one_pci_domain(); - VERIFY(success); - break; - } - default: - VERIFY_NOT_REACHED(); - } - - PCIBusSysFSDirectory::initialize(); - - // IRQ from pin-based interrupt should be set as reserved as soon as possible so that the PCI device - // that chooses to use MSI(x) based interrupt can avoid sharing the IRQ with other devices. - MUST(PCI::enumerate([&](DeviceIdentifier const& device_identifier) { - // A simple sanity check to avoid getting a panic in get_interrupt_handler() before setting the IRQ as reserved. - if (auto irq = device_identifier.interrupt_line().value(); irq < GENERIC_INTERRUPT_HANDLERS_COUNT) { - auto& handler = get_interrupt_handler(irq); - handler.set_reserved(); - } - })); - - MUST(PCI::enumerate([&](DeviceIdentifier const& device_identifier) { - dmesgln("{} {}", device_identifier.address(), device_identifier.hardware_id()); - })); -} - -UNMAP_AFTER_INIT bool test_pci_io() -{ - dmesgln("Testing PCI via manual probing..."); - u32 tmp = 0x80000000; - IO::out32(PCI::address_port, tmp); - tmp = IO::in32(PCI::address_port); - if (tmp == 0x80000000) { - dmesgln("PCI IO supported"); - return true; - } - - dmesgln("PCI IO not supported"); - return false; -} - -} diff --git a/Kernel/Arch/x86_64/PCI/MSI.cpp b/Kernel/Arch/x86_64/PCI/MSI.cpp deleted file mode 100644 index e6a991c620f..00000000000 --- a/Kernel/Arch/x86_64/PCI/MSI.cpp +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright (c) 2023, Pankaj R - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include - -namespace Kernel { -u64 msi_address_register(u8 destination_id, bool redirection_hint, bool destination_mode) -{ - u64 flags = 0; - if (redirection_hint) { - flags |= msi_redirection_hint; - if (destination_mode) - flags |= msi_destination_mode_logical; - } - return (msi_address_base | (destination_id << msi_destination_shift) | flags); -} - -u32 msi_data_register(u8 vector, bool level_trigger, bool assert) -{ - u32 flags = 0; - - if (level_trigger) { - flags |= msi_trigger_mode_level; - if (assert) - flags |= msi_level_assert; - } - return ((vector + IRQ_VECTOR_BASE) & msi_data_vector_mask) | flags; -} - -u32 msix_vector_control_register(u32 vector_control, bool mask) -{ - if (!mask) - return (vector_control & msi_vector_control_unmask); - return (vector_control | msi_vector_control_mask); -} - -void msi_signal_eoi() -{ - InterruptDisabler disabler; - APIC::the().eoi(); -} - -} diff --git a/Kernel/Arch/x86_64/PCI/MSI.h b/Kernel/Arch/x86_64/PCI/MSI.h deleted file mode 100644 index efe38c28889..00000000000 --- a/Kernel/Arch/x86_64/PCI/MSI.h +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Copyright (c) 2023, Pankaj R - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -// Address register -static constexpr u32 msi_address_base = 0xfee00000; -static constexpr u8 msi_destination_shift = 12; -static constexpr u32 msi_redirection_hint = 0x00000008; -static constexpr u32 msi_destination_mode_logical = 0x00000004; - -// Data register -static constexpr u8 msi_data_vector_mask = 0xff; -static constexpr u32 msi_trigger_mode_level = 0x00008000; -static constexpr u32 msi_level_assert = 0x00004000; - -// Vector control -static constexpr u32 msi_vector_control_mask = 0x1; -static constexpr u32 msi_vector_control_unmask = ~(0x1); diff --git a/Kernel/Arch/x86_64/PCSpeaker.cpp b/Kernel/Arch/x86_64/PCSpeaker.cpp deleted file mode 100644 index 176a87073f5..00000000000 --- a/Kernel/Arch/x86_64/PCSpeaker.cpp +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include - -void PCSpeaker::tone_on(int frequency) -{ - IO::out8(PIT_CTL, TIMER2_SELECT | WRITE_WORD | MODE_SQUARE_WAVE); - u16 timer_reload = BASE_FREQUENCY / frequency; - - IO::out8(TIMER2_CTL, LSB(timer_reload)); - IO::out8(TIMER2_CTL, MSB(timer_reload)); - - IO::out8(0x61, IO::in8(0x61) | 3); -} - -void PCSpeaker::tone_off() -{ - IO::out8(0x61, IO::in8(0x61) & ~3); -} diff --git a/Kernel/Arch/x86_64/PCSpeaker.h b/Kernel/Arch/x86_64/PCSpeaker.h deleted file mode 100644 index ba08bb8f097..00000000000 --- a/Kernel/Arch/x86_64/PCSpeaker.h +++ /dev/null @@ -1,13 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -class PCSpeaker { -public: - static void tone_on(int frequency); - static void tone_off(); -}; diff --git a/Kernel/Arch/x86_64/PageDirectory.cpp b/Kernel/Arch/x86_64/PageDirectory.cpp deleted file mode 100644 index 398c968873d..00000000000 --- a/Kernel/Arch/x86_64/PageDirectory.cpp +++ /dev/null @@ -1,149 +0,0 @@ -/* - * Copyright (c) 2018-2022, Andreas Kling - * Copyright (c) 2018-2022, James Mintram - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace Kernel::Memory { - -struct CR3Map { - SpinlockProtected, LockRank::None> map {}; -}; - -static Singleton s_cr3_map; - -void PageDirectory::register_page_directory(PageDirectory* directory) -{ - s_cr3_map->map.with([&](auto& map) { - map.insert(directory->cr3(), *directory); - }); -} - -void PageDirectory::deregister_page_directory(PageDirectory* directory) -{ - s_cr3_map->map.with([&](auto& map) { - map.remove(directory->cr3()); - }); -} - -LockRefPtr PageDirectory::find_current() -{ - return s_cr3_map->map.with([&](auto& map) { - return map.find(read_cr3()); - }); -} - -void activate_kernel_page_directory(PageDirectory const& pgd) -{ - write_cr3(pgd.cr3()); -} - -void activate_page_directory(PageDirectory const& pgd, Thread* current_thread) -{ - current_thread->regs().cr3 = pgd.cr3(); - write_cr3(pgd.cr3()); -} - -UNMAP_AFTER_INIT NonnullLockRefPtr PageDirectory::must_create_kernel_page_directory() -{ - return adopt_lock_ref_if_nonnull(new (nothrow) PageDirectory).release_nonnull(); -} - -ErrorOr> PageDirectory::try_create_for_userspace(Process& process) -{ - auto directory = TRY(adopt_nonnull_lock_ref_or_enomem(new (nothrow) PageDirectory)); - - directory->m_process = &process; - - directory->m_pml4t = TRY(MM.allocate_physical_page()); - - directory->m_directory_table = TRY(MM.allocate_physical_page()); - auto kernel_pd_index = (kernel_mapping_base >> 30) & 0x1ffu; - for (size_t i = 0; i < kernel_pd_index; i++) { - directory->m_directory_pages[i] = TRY(MM.allocate_physical_page()); - } - - // Share the top 1 GiB of kernel-only mappings (>=kernel_mapping_base) - directory->m_directory_pages[kernel_pd_index] = MM.kernel_page_directory().m_directory_pages[kernel_pd_index]; - - { - InterruptDisabler disabler; - auto& table = *(PageDirectoryPointerTable*)MM.quickmap_page(*directory->m_pml4t); - table.raw[0] = (FlatPtr)directory->m_directory_table->paddr().as_ptr() | 7; - MM.unquickmap_page(); - } - - { - InterruptDisabler disabler; - auto& table = *(PageDirectoryPointerTable*)MM.quickmap_page(*directory->m_directory_table); - for (size_t i = 0; i < sizeof(m_directory_pages) / sizeof(m_directory_pages[0]); i++) { - if (directory->m_directory_pages[i]) { - table.raw[i] = (FlatPtr)directory->m_directory_pages[i]->paddr().as_ptr() | 7; - } - } - - // 2 ** MAXPHYADDR - 1 - // Where MAXPHYADDR = physical_address_bit_width - u64 max_physical_address = (1ULL << Processor::current().physical_address_bit_width()) - 1; - - // bit 63 = no execute - // bit 7 = page size - // bit 5 = accessed - // bit 4 = cache disable - // bit 3 = write through - // bit 2 = user/supervisor - // bit 1 = read/write - // bit 0 = present - constexpr u64 pdpte_bit_flags = 0x80000000000000BF; - - // This is to notify us of bugs where we're: - // 1. Going over what the processor is capable of. - // 2. Writing into the reserved bits (51:MAXPHYADDR), where doing so throws a GPF - // when writing out the PDPT pointer to CR3. - // The reason we're not checking the page directory's physical address directly is because - // we're checking for sign extension when putting it into a PDPTE. See issue #4584. - for (auto table_entry : table.raw) - VERIFY((table_entry & ~pdpte_bit_flags) <= max_physical_address); - - MM.unquickmap_page(); - } - - register_page_directory(directory); - return directory; -} - -PageDirectory::PageDirectory() = default; - -UNMAP_AFTER_INIT void PageDirectory::allocate_kernel_directory() -{ - // Adopt the page tables already set up by boot.S - dmesgln("MM: boot_pml4t @ {}", boot_pml4t); - m_pml4t = PhysicalRAMPage::create(boot_pml4t, MayReturnToFreeList::No); - dmesgln("MM: boot_pdpt @ {}", boot_pdpt); - dmesgln("MM: boot_pd0 @ {}", boot_pd0); - dmesgln("MM: boot_pd_kernel @ {}", boot_pd_kernel); - m_directory_table = PhysicalRAMPage::create(boot_pdpt, MayReturnToFreeList::No); - m_directory_pages[0] = PhysicalRAMPage::create(boot_pd0, MayReturnToFreeList::No); - m_directory_pages[(kernel_mapping_base >> 30) & 0x1ff] = PhysicalRAMPage::create(boot_pd_kernel, MayReturnToFreeList::No); -} - -PageDirectory::~PageDirectory() -{ - if (is_cr3_initialized()) { - deregister_page_directory(this); - } -} - -} diff --git a/Kernel/Arch/x86_64/PageDirectory.h b/Kernel/Arch/x86_64/PageDirectory.h deleted file mode 100644 index 992108d7330..00000000000 --- a/Kernel/Arch/x86_64/PageDirectory.h +++ /dev/null @@ -1,205 +0,0 @@ -/* - * Copyright (c) 2018-2021, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace Kernel::Memory { - -class PageDirectoryEntry { -public: - PhysicalPtr page_table_base() const { return PhysicalAddress::physical_page_base(m_raw); } - void set_page_table_base(PhysicalPtr value) - { - m_raw &= 0x8000000000000fffULL; - m_raw |= PhysicalAddress::physical_page_base(value); - } - - bool is_null() const { return m_raw == 0; } - void clear() { m_raw = 0; } - - u64 raw() const { return m_raw; } - void copy_from(Badge, PageDirectoryEntry const& other) { m_raw = other.m_raw; } - - enum Flags { - Present = 1 << 0, - ReadWrite = 1 << 1, - UserSupervisor = 1 << 2, - WriteThrough = 1 << 3, - CacheDisabled = 1 << 4, - Huge = 1 << 7, - Global = 1 << 8, - NoExecute = 0x8000000000000000ULL, - }; - - bool is_present() const { return (raw() & Present) == Present; } - void set_present(bool b) { set_bit(Present, b); } - - bool is_user_allowed() const { return (raw() & UserSupervisor) == UserSupervisor; } - void set_user_allowed(bool b) { set_bit(UserSupervisor, b); } - - bool is_huge() const { return (raw() & Huge) == Huge; } - void set_huge(bool b) { set_bit(Huge, b); } - - bool is_writable() const { return (raw() & ReadWrite) == ReadWrite; } - void set_writable(bool b) { set_bit(ReadWrite, b); } - - bool is_write_through() const { return (raw() & WriteThrough) == WriteThrough; } - void set_write_through(bool b) { set_bit(WriteThrough, b); } - - bool is_cache_disabled() const { return (raw() & CacheDisabled) == CacheDisabled; } - void set_cache_disabled(bool b) { set_bit(CacheDisabled, b); } - - bool is_global() const { return (raw() & Global) == Global; } - void set_global(bool b) { set_bit(Global, b); } - - bool is_execute_disabled() const { return (raw() & NoExecute) == NoExecute; } - void set_execute_disabled(bool b) { set_bit(NoExecute, b); } - -private: - void set_bit(u64 bit, bool value) - { - if (value) - m_raw |= bit; - else - m_raw &= ~bit; - } - - u64 m_raw; -}; - -class PageTableEntry { -public: - PhysicalPtr physical_page_base() const { return PhysicalAddress::physical_page_base(m_raw); } - void set_physical_page_base(PhysicalPtr value) - { - // FIXME: IS THIS PLATFORM SPECIFIC? - m_raw &= 0x8000000000000fffULL; - m_raw |= PhysicalAddress::physical_page_base(value); - } - - u64 raw() const { return m_raw; } - - enum Flags { - Present = 1 << 0, - ReadWrite = 1 << 1, - UserSupervisor = 1 << 2, - WriteThrough = 1 << 3, - CacheDisabled = 1 << 4, - PAT = 1 << 7, - Global = 1 << 8, - NoExecute = 0x8000000000000000ULL, - }; - - bool is_present() const { return (raw() & Present) == Present; } - void set_present(bool b) { set_bit(Present, b); } - - bool is_user_allowed() const { return (raw() & UserSupervisor) == UserSupervisor; } - void set_user_allowed(bool b) { set_bit(UserSupervisor, b); } - - bool is_writable() const { return (raw() & ReadWrite) == ReadWrite; } - void set_writable(bool b) { set_bit(ReadWrite, b); } - - bool is_write_through() const { return (raw() & WriteThrough) == WriteThrough; } - void set_write_through(bool b) { set_bit(WriteThrough, b); } - - bool is_cache_disabled() const { return (raw() & CacheDisabled) == CacheDisabled; } - void set_cache_disabled(bool b) { set_bit(CacheDisabled, b); } - - bool is_global() const { return (raw() & Global) == Global; } - void set_global(bool b) { set_bit(Global, b); } - - bool is_execute_disabled() const { return (raw() & NoExecute) == NoExecute; } - void set_execute_disabled(bool b) { set_bit(NoExecute, b); } - - bool is_pat() const { return (raw() & PAT) == PAT; } - void set_pat(bool b) { set_bit(PAT, b); } - - bool is_null() const { return m_raw == 0; } - void clear() { m_raw = 0; } - -private: - void set_bit(u64 bit, bool value) - { - if (value) - m_raw |= bit; - else - m_raw &= ~bit; - } - - u64 m_raw; -}; - -static_assert(AssertSize()); -static_assert(AssertSize()); - -class PageDirectoryPointerTable { -public: - PageDirectoryEntry* directory(size_t index) - { - VERIFY(index <= (NumericLimits::max() << 30)); - return (PageDirectoryEntry*)(PhysicalAddress::physical_page_base(raw[index])); - } - - u64 raw[512]; -}; - -class PageDirectory final : public AtomicRefCounted { - friend class MemoryManager; - -public: - static ErrorOr> try_create_for_userspace(Process& process); - static NonnullLockRefPtr must_create_kernel_page_directory(); - static LockRefPtr find_current(); - - ~PageDirectory(); - - void allocate_kernel_directory(); - - FlatPtr cr3() const - { - return m_pml4t->paddr().get(); - } - - bool is_cr3_initialized() const - { - return m_pml4t; - } - - Process* process() { return m_process; } - - RecursiveSpinlock& get_lock() { return m_lock; } - - // This has to be public to let the global singleton access the member pointer - IntrusiveRedBlackTreeNode> m_tree_node; - -private: - PageDirectory(); - static void register_page_directory(PageDirectory* directory); - static void deregister_page_directory(PageDirectory* directory); - - Process* m_process { nullptr }; - RefPtr m_pml4t; - RefPtr m_directory_table; - RefPtr m_directory_pages[512]; - RecursiveSpinlock m_lock {}; -}; - -void activate_kernel_page_directory(PageDirectory const& pgd); -void activate_page_directory(PageDirectory const& pgd, Thread* current_thread); - -} diff --git a/Kernel/Arch/x86_64/PowerState.cpp b/Kernel/Arch/x86_64/PowerState.cpp deleted file mode 100644 index dc08aa2ebf2..00000000000 --- a/Kernel/Arch/x86_64/PowerState.cpp +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright (c) 2023, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include - -namespace Kernel { - -void arch_specific_reboot() -{ - i8042_reboot(); -} - -void arch_specific_poweroff() -{ - qemu_shutdown(); - virtualbox_shutdown(); -} - -} diff --git a/Kernel/Arch/x86_64/Processor.cpp b/Kernel/Arch/x86_64/Processor.cpp deleted file mode 100644 index 7f5805f91a8..00000000000 --- a/Kernel/Arch/x86_64/Processor.cpp +++ /dev/null @@ -1,1745 +0,0 @@ -/* - * Copyright (c) 2018-2021, Andreas Kling - * Copyright (c) 2022, Linus Groh - * Copyright (c) 2022, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -namespace Kernel { - -READONLY_AFTER_INIT static ProcessorContainer s_processors {}; -READONLY_AFTER_INIT static bool volatile s_smp_enabled; - -static Atomic s_message_pool; -Atomic Processor::s_idle_cpu_mask { 0 }; - -extern "C" void enter_thread_context(Thread* from_thread, Thread* to_thread) __attribute__((used)); -extern "C" FlatPtr do_init_context(Thread* thread, u32 flags) __attribute__((used)); -extern "C" void syscall_entry(); - -template -bool ProcessorBase::is_smp_enabled() -{ - return s_smp_enabled; -} - -UNMAP_AFTER_INIT static void sse_init() -{ - write_cr0((read_cr0() & 0xfffffffbu) | 0x2); - write_cr4(read_cr4() | 0x600); -} - -UNMAP_AFTER_INIT void Processor::cpu_detect() -{ - // NOTE: This is called during Processor::early_initialize, we cannot - // safely log at this point because we don't have kmalloc - // initialized yet! - m_features = CPUFeature::Type(0u); - - CPUID processor_info(0x1); - - auto handle_edx_bit_11_feature = [&] { - u32 stepping = processor_info.eax() & 0xf; - u32 model = (processor_info.eax() >> 4) & 0xf; - u32 family = (processor_info.eax() >> 8) & 0xf; - // FIXME: I have no clue what these mean or where it's from (the Intel manual I've seen just says EDX[11] is SEP). - // If you do, please convert them to constants or add comments! - if (!(family == 6 && model < 3 && stepping < 3)) - m_features |= CPUFeature::SEP; - if ((family == 6 && model >= 3) || (family == 0xf && model >= 0xe)) - m_features |= CPUFeature::CONSTANT_TSC; - }; - - if (processor_info.ecx() & (1 << 0)) - m_features |= CPUFeature::SSE3; - if (processor_info.ecx() & (1 << 1)) - m_features |= CPUFeature::PCLMULQDQ; - if (processor_info.ecx() & (1 << 2)) - m_features |= CPUFeature::DTES64; - if (processor_info.ecx() & (1 << 3)) - m_features |= CPUFeature::MONITOR; - if (processor_info.ecx() & (1 << 4)) - m_features |= CPUFeature::DS_CPL; - if (processor_info.ecx() & (1 << 5)) - m_features |= CPUFeature::VMX; - if (processor_info.ecx() & (1 << 6)) - m_features |= CPUFeature::SMX; - if (processor_info.ecx() & (1 << 7)) - m_features |= CPUFeature::EST; - if (processor_info.ecx() & (1 << 8)) - m_features |= CPUFeature::TM2; - if (processor_info.ecx() & (1 << 9)) - m_features |= CPUFeature::SSSE3; - if (processor_info.ecx() & (1 << 10)) - m_features |= CPUFeature::CNXT_ID; - if (processor_info.ecx() & (1 << 11)) - m_features |= CPUFeature::SDBG; - if (processor_info.ecx() & (1 << 12)) - m_features |= CPUFeature::FMA; - if (processor_info.ecx() & (1 << 13)) - m_features |= CPUFeature::CX16; - if (processor_info.ecx() & (1 << 14)) - m_features |= CPUFeature::XTPR; - if (processor_info.ecx() & (1 << 15)) - m_features |= CPUFeature::PDCM; - if (processor_info.ecx() & (1 << 17)) - m_features |= CPUFeature::PCID; - if (processor_info.ecx() & (1 << 18)) - m_features |= CPUFeature::DCA; - if (processor_info.ecx() & (1 << 19)) - m_features |= CPUFeature::SSE4_1; - if (processor_info.ecx() & (1 << 20)) - m_features |= CPUFeature::SSE4_2; - if (processor_info.ecx() & (1 << 21)) - m_features |= CPUFeature::X2APIC; - if (processor_info.ecx() & (1 << 22)) - m_features |= CPUFeature::MOVBE; - if (processor_info.ecx() & (1 << 23)) - m_features |= CPUFeature::POPCNT; - if (processor_info.ecx() & (1 << 24)) - m_features |= CPUFeature::TSC_DEADLINE; - if (processor_info.ecx() & (1 << 25)) - m_features |= CPUFeature::AES; - if (processor_info.ecx() & (1 << 26)) - m_features |= CPUFeature::XSAVE; - if (processor_info.ecx() & (1 << 27)) - m_features |= CPUFeature::OSXSAVE; - if (processor_info.ecx() & (1 << 28)) - m_features |= CPUFeature::AVX; - if (processor_info.ecx() & (1 << 29)) - m_features |= CPUFeature::F16C; - if (processor_info.ecx() & (1 << 30)) - m_features |= CPUFeature::RDRAND; - if (processor_info.ecx() & (1 << 31)) - m_features |= CPUFeature::HYPERVISOR; - - if (processor_info.edx() & (1 << 0)) - m_features |= CPUFeature::FPU; - if (processor_info.edx() & (1 << 1)) - m_features |= CPUFeature::VME; - if (processor_info.edx() & (1 << 2)) - m_features |= CPUFeature::DE; - if (processor_info.edx() & (1 << 3)) - m_features |= CPUFeature::PSE; - if (processor_info.edx() & (1 << 4)) - m_features |= CPUFeature::TSC; - if (processor_info.edx() & (1 << 5)) - m_features |= CPUFeature::MSR; - if (processor_info.edx() & (1 << 6)) - m_features |= CPUFeature::PAE; - if (processor_info.edx() & (1 << 7)) - m_features |= CPUFeature::MCE; - if (processor_info.edx() & (1 << 8)) - m_features |= CPUFeature::CX8; - if (processor_info.edx() & (1 << 9)) - m_features |= CPUFeature::APIC; - if (processor_info.edx() & (1 << 11)) - handle_edx_bit_11_feature(); - if (processor_info.edx() & (1 << 12)) - m_features |= CPUFeature::MTRR; - if (processor_info.edx() & (1 << 13)) - m_features |= CPUFeature::PGE; - if (processor_info.edx() & (1 << 14)) - m_features |= CPUFeature::MCA; - if (processor_info.edx() & (1 << 15)) - m_features |= CPUFeature::CMOV; - if (processor_info.edx() & (1 << 16)) - m_features |= CPUFeature::PAT; - if (processor_info.edx() & (1 << 17)) - m_features |= CPUFeature::PSE36; - if (processor_info.edx() & (1 << 18)) - m_features |= CPUFeature::PSN; - if (processor_info.edx() & (1 << 19)) - m_features |= CPUFeature::CLFLUSH; - if (processor_info.edx() & (1 << 21)) - m_features |= CPUFeature::DS; - if (processor_info.edx() & (1 << 22)) - m_features |= CPUFeature::ACPI; - if (processor_info.edx() & (1 << 23)) - m_features |= CPUFeature::MMX; - if (processor_info.edx() & (1 << 24)) - m_features |= CPUFeature::FXSR; - if (processor_info.edx() & (1 << 25)) - m_features |= CPUFeature::SSE; - if (processor_info.edx() & (1 << 26)) - m_features |= CPUFeature::SSE2; - if (processor_info.edx() & (1 << 27)) - m_features |= CPUFeature::SS; - if (processor_info.edx() & (1 << 28)) - m_features |= CPUFeature::HTT; - if (processor_info.edx() & (1 << 29)) - m_features |= CPUFeature::TM; - if (processor_info.edx() & (1 << 30)) - m_features |= CPUFeature::IA64; - if (processor_info.edx() & (1 << 31)) - m_features |= CPUFeature::PBE; - - CPUID extended_features(0x7); - - if (extended_features.ebx() & (1 << 0)) - m_features |= CPUFeature::FSGSBASE; - if (extended_features.ebx() & (1 << 1)) - m_features |= CPUFeature::TSC_ADJUST; - if (extended_features.ebx() & (1 << 2)) - m_features |= CPUFeature::SGX; - if (extended_features.ebx() & (1 << 3)) - m_features |= CPUFeature::BMI1; - if (extended_features.ebx() & (1 << 4)) - m_features |= CPUFeature::HLE; - if (extended_features.ebx() & (1 << 5)) - m_features |= CPUFeature::AVX2; - if (extended_features.ebx() & (1 << 6)) - m_features |= CPUFeature::FDP_EXCPTN_ONLY; - if (extended_features.ebx() & (1 << 7)) - m_features |= CPUFeature::SMEP; - if (extended_features.ebx() & (1 << 8)) - m_features |= CPUFeature::BMI2; - if (extended_features.ebx() & (1 << 9)) - m_features |= CPUFeature::ERMS; - if (extended_features.ebx() & (1 << 10)) - m_features |= CPUFeature::INVPCID; - if (extended_features.ebx() & (1 << 11)) - m_features |= CPUFeature::RTM; - if (extended_features.ebx() & (1 << 12)) - m_features |= CPUFeature::PQM; - if (extended_features.ebx() & (1 << 13)) - m_features |= CPUFeature::ZERO_FCS_FDS; - if (extended_features.ebx() & (1 << 14)) - m_features |= CPUFeature::MPX; - if (extended_features.ebx() & (1 << 15)) - m_features |= CPUFeature::PQE; - if (extended_features.ebx() & (1 << 16)) - m_features |= CPUFeature::AVX512_F; - if (extended_features.ebx() & (1 << 17)) - m_features |= CPUFeature::AVX512_DQ; - if (extended_features.ebx() & (1 << 18)) - m_features |= CPUFeature::RDSEED; - if (extended_features.ebx() & (1 << 19)) - m_features |= CPUFeature::ADX; - if (extended_features.ebx() & (1 << 20)) - m_features |= CPUFeature::SMAP; - if (extended_features.ebx() & (1 << 21)) - m_features |= CPUFeature::AVX512_IFMA; - if (extended_features.ebx() & (1 << 22)) - m_features |= CPUFeature::PCOMMIT; - if (extended_features.ebx() & (1 << 23)) - m_features |= CPUFeature::CLFLUSHOPT; - if (extended_features.ebx() & (1 << 24)) - m_features |= CPUFeature::CLWB; - if (extended_features.ebx() & (1 << 25)) - m_features |= CPUFeature::INTEL_PT; - if (extended_features.ebx() & (1 << 26)) - m_features |= CPUFeature::AVX512_PF; - if (extended_features.ebx() & (1 << 27)) - m_features |= CPUFeature::AVX512_ER; - if (extended_features.ebx() & (1 << 28)) - m_features |= CPUFeature::AVX512_CD; - if (extended_features.ebx() & (1 << 29)) - m_features |= CPUFeature::SHA; - if (extended_features.ebx() & (1 << 30)) - m_features |= CPUFeature::AVX512_BW; - if (extended_features.ebx() & (1 << 31)) - m_features |= CPUFeature::AVX512_VL; - - if (extended_features.ecx() & (1 << 0)) - m_features |= CPUFeature::PREFETCHWT1; - if (extended_features.ecx() & (1 << 1)) - m_features |= CPUFeature::AVX512_VBMI; - if (extended_features.ecx() & (1 << 2)) - m_features |= CPUFeature::UMIP; - if (extended_features.ecx() & (1 << 3)) - m_features |= CPUFeature::PKU; - if (extended_features.ecx() & (1 << 4)) - m_features |= CPUFeature::OSPKE; - if (extended_features.ecx() & (1 << 5)) - m_features |= CPUFeature::WAITPKG; - if (extended_features.ecx() & (1 << 6)) - m_features |= CPUFeature::AVX512_VBMI2; - if (extended_features.ecx() & (1 << 7)) - m_features |= CPUFeature::CET_SS; - if (extended_features.ecx() & (1 << 8)) - m_features |= CPUFeature::GFNI; - if (extended_features.ecx() & (1 << 9)) - m_features |= CPUFeature::VAES; - if (extended_features.ecx() & (1 << 10)) - m_features |= CPUFeature::VPCLMULQDQ; - if (extended_features.ecx() & (1 << 11)) - m_features |= CPUFeature::AVX512_VNNI; - if (extended_features.ecx() & (1 << 12)) - m_features |= CPUFeature::AVX512_BITALG; - if (extended_features.ecx() & (1 << 13)) - m_features |= CPUFeature::TME_EN; - if (extended_features.ecx() & (1 << 14)) - m_features |= CPUFeature::AVX512_VPOPCNTDQ; - if (extended_features.ecx() & (1 << 16)) - m_features |= CPUFeature::INTEL_5_LEVEL_PAGING; - if (extended_features.ecx() & (1 << 22)) - m_features |= CPUFeature::RDPID; - if (extended_features.ecx() & (1 << 23)) - m_features |= CPUFeature::KL; - if (extended_features.ecx() & (1 << 25)) - m_features |= CPUFeature::CLDEMOTE; - if (extended_features.ecx() & (1 << 27)) - m_features |= CPUFeature::MOVDIRI; - if (extended_features.ecx() & (1 << 28)) - m_features |= CPUFeature::MOVDIR64B; - if (extended_features.ecx() & (1 << 29)) - m_features |= CPUFeature::ENQCMD; - if (extended_features.ecx() & (1 << 30)) - m_features |= CPUFeature::SGX_LC; - if (extended_features.ecx() & (1 << 31)) - m_features |= CPUFeature::PKS; - - if (extended_features.edx() & (1 << 2)) - m_features |= CPUFeature::AVX512_4VNNIW; - if (extended_features.edx() & (1 << 3)) - m_features |= CPUFeature::AVX512_4FMAPS; - if (extended_features.edx() & (1 << 4)) - m_features |= CPUFeature::FSRM; - if (extended_features.edx() & (1 << 8)) - m_features |= CPUFeature::AVX512_VP2INTERSECT; - if (extended_features.edx() & (1 << 9)) - m_features |= CPUFeature::SRBDS_CTRL; - if (extended_features.edx() & (1 << 10)) - m_features |= CPUFeature::MD_CLEAR; - if (extended_features.edx() & (1 << 11)) - m_features |= CPUFeature::RTM_ALWAYS_ABORT; - if (extended_features.edx() & (1 << 13)) - m_features |= CPUFeature::TSX_FORCE_ABORT; - if (extended_features.edx() & (1 << 14)) - m_features |= CPUFeature::SERIALIZE; - if (extended_features.edx() & (1 << 15)) - m_features |= CPUFeature::HYBRID; - if (extended_features.edx() & (1 << 16)) - m_features |= CPUFeature::TSXLDTRK; - if (extended_features.edx() & (1 << 18)) - m_features |= CPUFeature::PCONFIG; - if (extended_features.edx() & (1 << 19)) - m_features |= CPUFeature::LBR; - if (extended_features.edx() & (1 << 20)) - m_features |= CPUFeature::CET_IBT; - if (extended_features.edx() & (1 << 22)) - m_features |= CPUFeature::AMX_BF16; - if (extended_features.edx() & (1 << 23)) - m_features |= CPUFeature::AVX512_FP16; - if (extended_features.edx() & (1 << 24)) - m_features |= CPUFeature::AMX_TILE; - if (extended_features.edx() & (1 << 25)) - m_features |= CPUFeature::AMX_INT8; - if (extended_features.edx() & (1 << 26)) - m_features |= CPUFeature::SPEC_CTRL; - if (extended_features.edx() & (1 << 27)) - m_features |= CPUFeature::STIBP; - if (extended_features.edx() & (1 << 28)) - m_features |= CPUFeature::L1D_FLUSH; - if (extended_features.edx() & (1 << 29)) - m_features |= CPUFeature::IA32_ARCH_CAPABILITIES; - if (extended_features.edx() & (1 << 30)) - m_features |= CPUFeature::IA32_CORE_CAPABILITIES; - if (extended_features.edx() & (1 << 31)) - m_features |= CPUFeature::SSBD; - - u32 max_extended_leaf = CPUID(0x80000000).eax(); - - if (max_extended_leaf >= 0x80000001) { - CPUID extended_processor_info(0x80000001); - - if (extended_processor_info.ecx() & (1 << 0)) - m_features |= CPUFeature::LAHF_LM; - if (extended_processor_info.ecx() & (1 << 1)) - m_features |= CPUFeature::CMP_LEGACY; - if (extended_processor_info.ecx() & (1 << 2)) - m_features |= CPUFeature::SVM; - if (extended_processor_info.ecx() & (1 << 3)) - m_features |= CPUFeature::EXTAPIC; - if (extended_processor_info.ecx() & (1 << 4)) - m_features |= CPUFeature::CR8_LEGACY; - if (extended_processor_info.ecx() & (1 << 5)) - m_features |= CPUFeature::ABM; - if (extended_processor_info.ecx() & (1 << 6)) - m_features |= CPUFeature::SSE4A; - if (extended_processor_info.ecx() & (1 << 7)) - m_features |= CPUFeature::MISALIGNSSE; - if (extended_processor_info.ecx() & (1 << 8)) - m_features |= CPUFeature::_3DNOWPREFETCH; - if (extended_processor_info.ecx() & (1 << 9)) - m_features |= CPUFeature::OSVW; - if (extended_processor_info.ecx() & (1 << 10)) - m_features |= CPUFeature::IBS; - if (extended_processor_info.ecx() & (1 << 11)) - m_features |= CPUFeature::XOP; - if (extended_processor_info.ecx() & (1 << 12)) - m_features |= CPUFeature::SKINIT; - if (extended_processor_info.ecx() & (1 << 13)) - m_features |= CPUFeature::WDT; - if (extended_processor_info.ecx() & (1 << 15)) - m_features |= CPUFeature::LWP; - if (extended_processor_info.ecx() & (1 << 16)) - m_features |= CPUFeature::FMA4; - if (extended_processor_info.ecx() & (1 << 17)) - m_features |= CPUFeature::TCE; - if (extended_processor_info.ecx() & (1 << 19)) - m_features |= CPUFeature::NODEID_MSR; - if (extended_processor_info.ecx() & (1 << 21)) - m_features |= CPUFeature::TBM; - if (extended_processor_info.ecx() & (1 << 22)) - m_features |= CPUFeature::TOPOEXT; - if (extended_processor_info.ecx() & (1 << 23)) - m_features |= CPUFeature::PERFCTR_CORE; - if (extended_processor_info.ecx() & (1 << 24)) - m_features |= CPUFeature::PERFCTR_NB; - if (extended_processor_info.ecx() & (1 << 26)) - m_features |= CPUFeature::DBX; - if (extended_processor_info.ecx() & (1 << 27)) - m_features |= CPUFeature::PERFTSC; - if (extended_processor_info.ecx() & (1 << 28)) - m_features |= CPUFeature::PCX_L2I; - - if (extended_processor_info.edx() & (1 << 11)) - m_features |= CPUFeature::SYSCALL; // Only available in 64 bit mode - if (extended_processor_info.edx() & (1 << 19)) - m_features |= CPUFeature::MP; - if (extended_processor_info.edx() & (1 << 20)) - m_features |= CPUFeature::NX; - if (extended_processor_info.edx() & (1 << 22)) - m_features |= CPUFeature::MMXEXT; - if (extended_processor_info.edx() & (1 << 23)) - m_features |= CPUFeature::RDTSCP; - if (extended_processor_info.edx() & (1 << 25)) - m_features |= CPUFeature::FXSR_OPT; - if (extended_processor_info.edx() & (1 << 26)) - m_features |= CPUFeature::PDPE1GB; - if (extended_processor_info.edx() & (1 << 27)) - m_features |= CPUFeature::RDTSCP; - if (extended_processor_info.edx() & (1 << 29)) - m_features |= CPUFeature::LM; - if (extended_processor_info.edx() & (1 << 30)) - m_features |= CPUFeature::_3DNOWEXT; - if (extended_processor_info.edx() & (1 << 31)) - m_features |= CPUFeature::_3DNOW; - } - - if (max_extended_leaf >= 0x80000007) { - CPUID cpuid(0x80000007); - if (cpuid.edx() & (1 << 8)) { - m_features |= CPUFeature::CONSTANT_TSC; - m_features |= CPUFeature::NONSTOP_TSC; - } - } - - if (max_extended_leaf >= 0x80000008) { - // CPUID.80000008H:EAX[7:0] reports the physical-address width supported by the processor. - CPUID cpuid(0x80000008); - m_physical_address_bit_width = cpuid.eax() & 0xff; - // CPUID.80000008H:EAX[15:8] reports the linear-address width supported by the processor. - m_virtual_address_bit_width = (cpuid.eax() >> 8) & 0xff; - } else { - // For processors that do not support CPUID function 80000008H, the width is generally 36 if CPUID.01H:EDX.PAE [bit 6] = 1 and 32 otherwise. - m_physical_address_bit_width = has_feature(CPUFeature::PAE) ? 36 : 32; - // Processors that do not support CPUID function 80000008H, support a linear-address width of 32. - m_virtual_address_bit_width = 32; - // Workaround QEMU hypervisor.framework bug - // https://gitlab.com/qemu-project/qemu/-/issues/664 - // - // We detect this as follows: - // * We're in a hypervisor - // * hypervisor_leaf_range is null under Hypervisor.framework - // * m_physical_address_bit_width is 36 bits - if (has_feature(CPUFeature::HYPERVISOR)) { - CPUID hypervisor_leaf_range(0x40000000); - if (!hypervisor_leaf_range.ebx() && m_physical_address_bit_width == 36) { - m_has_qemu_hvf_quirk.set(); - m_virtual_address_bit_width = 48; - } - } - } -} - -UNMAP_AFTER_INIT void Processor::cpu_setup() -{ - // NOTE: This is called during Processor::early_initialize, we cannot - // safely log at this point because we don't have kmalloc - // initialized yet! - cpu_detect(); - - if (has_feature(CPUFeature::SSE)) { - // enter_thread_context() assumes that if a x86 CPU supports SSE then it also supports FXSR. - // SSE support without FXSR is an extremely unlikely scenario, so let's be pragmatic about it. - VERIFY(has_feature(CPUFeature::FXSR)); - sse_init(); - } - - write_cr0(read_cr0() | 0x00010000); - - if (has_feature(CPUFeature::PGE)) { - // Turn on CR4.PGE so the CPU will respect the G bit in page tables. - write_cr4(read_cr4() | 0x80); - } - - if (has_feature(CPUFeature::NX)) { - // Turn on IA32_EFER.NXE - MSR ia32_efer(MSR_IA32_EFER); - ia32_efer.set(ia32_efer.get() | 0x800); - } - - if (has_feature(CPUFeature::PAT)) { - MSR ia32_pat(MSR_IA32_PAT); - // Set PA4 to Write Comine. This allows us to - // use this mode by only setting the bit in the PTE - // and leaving all other bits in the upper levels unset, - // which maps to setting bit 3 of the index, resulting - // in the index value 0 or 4. - u64 pat = ia32_pat.get() & ~(0x7ull << 32); - pat |= 0x1ull << 32; // set WC mode for PA4 - ia32_pat.set(pat); - } - - if (has_feature(CPUFeature::SMEP)) { - // Turn on CR4.SMEP - write_cr4(read_cr4() | 0x100000); - } - - if (has_feature(CPUFeature::SMAP)) { - // Turn on CR4.SMAP - write_cr4(read_cr4() | 0x200000); - } - - if (has_feature(CPUFeature::UMIP)) { - write_cr4(read_cr4() | 0x800); - } - - if (has_feature(CPUFeature::XSAVE)) { - // Turn on CR4.OSXSAVE - write_cr4(read_cr4() | 0x40000); - - // According to the Intel manual: "After reset, all bits (except bit 0) in XCR0 are cleared to zero; XCR0[0] is set to 1." - // Sadly we can't trust this, for example VirtualBox starts with bits 0-4 set, so let's do it ourselves. - write_xcr0(0x1); - - if (has_feature(CPUFeature::AVX)) { - // Turn on SSE, AVX and x87 flags - write_xcr0(read_xcr0() | SIMD::StateComponent::AVX | SIMD::StateComponent::SSE | SIMD::StateComponent::X87); - } - } - - // x86_64 processors must support the syscall feature. - VERIFY(has_feature(CPUFeature::SYSCALL)); - MSR efer_msr(MSR_EFER); - efer_msr.set(efer_msr.get() | 1u); - - // Write code and stack selectors to the STAR MSR. The first value stored in bits 63:48 controls the sysret CS (value + 0x10) and SS (value + 0x8), - // and the value stored in bits 47:32 controls the syscall CS (value) and SS (value + 0x8). - u64 star = 0; - star |= 0x13ul << 48u; - star |= 0x08ul << 32u; - MSR star_msr(MSR_STAR); - star_msr.set(star); - - // Write the syscall entry point to the LSTAR MSR. - MSR lstar_msr(MSR_LSTAR); - lstar_msr.set(reinterpret_cast(&syscall_entry)); - - // Write the SFMASK MSR. This MSR controls which bits of rflags are masked when a syscall instruction is executed - - // if a bit is set in sfmask, the corresponding bit in rflags is cleared. The value set here clears most of rflags, - // but keeps the reserved and virtualization bits intact. The userspace rflags value is saved in r11 by syscall. - constexpr u64 rflags_mask = 0x257fd5u; - MSR sfmask_msr(MSR_SFMASK); - sfmask_msr.set(rflags_mask); - - if (has_feature(CPUFeature::FSGSBASE)) { - // Turn off CR4.FSGSBASE to ensure the current Processor base kernel address is not leaked via - // the RDGSBASE instruction until we implement proper GS swapping at the userspace/kernel boundaries - write_cr4(read_cr4() & ~0x10000); - } - - // Query OS-enabled CPUID features again, and set the flags if needed. - CPUID processor_info(0x1); - if (processor_info.ecx() & (1 << 27)) - m_features |= CPUFeature::OSXSAVE; - CPUID extended_features(0x7); - if (extended_features.ecx() & (1 << 4)) - m_features |= CPUFeature::OSPKE; -} - -template -UNMAP_AFTER_INIT void ProcessorBase::early_initialize(u32 cpu) -{ - m_self = static_cast(this); - auto self = static_cast(this); - - m_cpu = cpu; - m_in_irq = 0; - m_in_critical = 0; - - m_invoke_scheduler_async = false; - m_in_scheduler = true; - - self->m_message_queue = nullptr; - m_idle_thread = nullptr; - m_current_thread = nullptr; - self->m_info = nullptr; - - m_halt_requested = false; - if (cpu == 0) { - s_smp_enabled = false; - g_total_processors.store(1u, AK::MemoryOrder::memory_order_release); - } else { - g_total_processors.fetch_add(1u, AK::MemoryOrder::memory_order_acq_rel); - } - - m_deferred_call_pool.init(); - - self->cpu_setup(); - self->gdt_init(); - - VERIFY(is_initialized()); - VERIFY(¤t() == this); // sanity check -} - -template -UNMAP_AFTER_INIT void ProcessorBase::initialize(u32 cpu) -{ - VERIFY(m_self == this); - VERIFY(¤t() == this); // sanity check - - auto self = static_cast(this); - - self->m_info = new ProcessorInfo(*self); - - dmesgln("CPU[{}]: Supported features: {}", current_id(), self->m_info->features_string()); - if (!has_feature(CPUFeature::RDRAND)) - dmesgln("CPU[{}]: No RDRAND support detected, randomness will be poor", current_id()); - dmesgln("CPU[{}]: Physical address bit width: {}", current_id(), m_physical_address_bit_width); - dmesgln("CPU[{}]: Virtual address bit width: {}", current_id(), m_virtual_address_bit_width); - if (self->m_has_qemu_hvf_quirk.was_set()) - dmesgln("CPU[{}]: Applied correction for QEMU Hypervisor.framework quirk", current_id()); - - if (cpu == 0) - initialize_interrupts(); - else - flush_idt(); - - if (cpu == 0) { - VERIFY((FlatPtr(&s_clean_fpu_state) & 0xF) == 0); - asm volatile("fninit"); - // Initialize AVX state - if (has_feature(CPUFeature::XSAVE | CPUFeature::AVX)) { - asm volatile("xsave %0\n" - : "=m"(s_clean_fpu_state) - : "a"(static_cast(SIMD::StateComponent::AVX | SIMD::StateComponent::SSE | SIMD::StateComponent::X87)), "d"(0u)); - } else if (has_feature(CPUFeature::FXSR)) { - asm volatile("fxsave %0" - : "=m"(s_clean_fpu_state)); - } else { - asm volatile("fnsave %0" - : "=m"(s_clean_fpu_state)); - } - - if (has_feature(CPUFeature::HYPERVISOR)) - self->detect_hypervisor(); - } - - { - // We need to prevent races between APs starting up at the same time - VERIFY(cpu < s_processors.size()); - s_processors[cpu] = static_cast(this); - } -} - -UNMAP_AFTER_INIT void Processor::detect_hypervisor() -{ - CPUID hypervisor_leaf_range(0x40000000); - auto hypervisor_vendor_id_string = m_info->hypervisor_vendor_id_string(); - dmesgln("CPU[{}]: CPUID hypervisor signature '{}', max leaf {:#x}", current_id(), hypervisor_vendor_id_string, hypervisor_leaf_range.eax()); - - if (hypervisor_vendor_id_string == "Microsoft Hv"sv) - detect_hypervisor_hyperv(hypervisor_leaf_range); -} - -UNMAP_AFTER_INIT void Processor::detect_hypervisor_hyperv(CPUID const& hypervisor_leaf_range) -{ - if (hypervisor_leaf_range.eax() < 0x40000001) - return; - - CPUID hypervisor_interface(0x40000001); - - // Get signature of hypervisor interface. - alignas(sizeof(u32)) char interface_signature_buffer[5]; - *reinterpret_cast(interface_signature_buffer) = hypervisor_interface.eax(); - interface_signature_buffer[4] = '\0'; - StringView hyperv_interface_signature { interface_signature_buffer, strlen(interface_signature_buffer) }; - - dmesgln("CPU[{}]: Hyper-V interface signature '{}' ({:#x})", current_id(), hyperv_interface_signature, hypervisor_interface.eax()); - - if (hypervisor_leaf_range.eax() < 0x40000001) - return; - - CPUID hypervisor_sysid(0x40000002); - dmesgln("CPU[{}]: Hyper-V system identity {}.{}, build number {}", current_id(), hypervisor_sysid.ebx() >> 16, hypervisor_sysid.ebx() & 0xFFFF, hypervisor_sysid.eax()); - - if (hypervisor_leaf_range.eax() < 0x40000005 || hyperv_interface_signature != "Hv#1"sv) - return; - - dmesgln("CPU[{}]: Hyper-V hypervisor detected", current_id()); - - // TODO: Actually do something with Hyper-V. -} - -void Processor::write_raw_gdt_entry(u16 selector, u32 low, u32 high) -{ - u16 i = (selector & 0xfffc) >> 3; - u32 prev_gdt_length = m_gdt_length; - - if (i >= m_gdt_length) { - m_gdt_length = i + 1; - VERIFY(m_gdt_length <= sizeof(m_gdt) / sizeof(m_gdt[0])); - m_gdtr.limit = (m_gdt_length + 1) * 8 - 1; - } - m_gdt[i].low = low; - m_gdt[i].high = high; - - // clear selectors we may have skipped - for (auto j = prev_gdt_length; j < i; ++j) { - m_gdt[j].low = 0; - m_gdt[j].high = 0; - } -} - -void Processor::write_gdt_entry(u16 selector, Descriptor& descriptor) -{ - write_raw_gdt_entry(selector, descriptor.low, descriptor.high); -} - -Descriptor& Processor::get_gdt_entry(u16 selector) -{ - u16 i = (selector & 0xfffc) >> 3; - return *(Descriptor*)(&m_gdt[i]); -} - -void Processor::flush_gdt() -{ - m_gdtr.address = m_gdt; - m_gdtr.limit = (m_gdt_length * 8) - 1; - asm volatile("lgdt %0" ::"m"(m_gdtr) - : "memory"); -} - -DescriptorTablePointer const& Processor::get_gdtr() -{ - return m_gdtr; -} - -template -ErrorOr> ProcessorBase::capture_stack_trace(Thread& thread, size_t max_frames) -{ - FlatPtr frame_ptr = 0, ip = 0; - Vector stack_trace; - - auto walk_stack = [&](FlatPtr frame_ptr) -> ErrorOr { - constexpr size_t max_stack_frames = 4096; - bool is_walking_userspace_stack = false; - TRY(stack_trace.try_append(ip)); - - TRY(AK::unwind_stack_from_frame_pointer( - frame_ptr, - [&is_walking_userspace_stack](FlatPtr address) -> ErrorOr { - if (!Memory::is_user_address(VirtualAddress { address })) { - if (is_walking_userspace_stack) { - dbgln("SHENANIGANS! Userspace stack points back into kernel memory"); - return EFAULT; - } - } else { - is_walking_userspace_stack = true; - } - - FlatPtr value; - - if (Memory::is_user_range(VirtualAddress { address }, sizeof(FlatPtr))) { - TRY(copy_from_user(&value, bit_cast(address))); - } else { - void* fault_at; - if (!safe_memcpy(&value, bit_cast(address), sizeof(FlatPtr), fault_at)) - return EFAULT; - } - - return value; - }, - [&stack_trace, max_frames](AK::StackFrame stack_frame) -> ErrorOr { - if (stack_trace.size() >= max_stack_frames || (max_frames != 0 && stack_trace.size() >= max_frames)) - return IterationDecision::Break; - - TRY(stack_trace.try_append(stack_frame.return_address)); - - return IterationDecision::Continue; - })); - - return {}; - }; - - auto capture_current_thread = [&]() { - frame_ptr = (FlatPtr)__builtin_frame_address(0); - ip = (FlatPtr)__builtin_return_address(0); - - return walk_stack(frame_ptr); - }; - - // Since the thread may be running on another processor, there - // is a chance a context switch may happen while we're trying - // to get it. It also won't be entirely accurate and merely - // reflect the status at the last context switch. - SpinlockLocker lock(g_scheduler_lock); - if (&thread == Processor::current_thread()) { - VERIFY(thread.state() == Thread::State::Running); - // Leave the scheduler lock. If we trigger page faults we may - // need to be preempted. Since this is our own thread it won't - // cause any problems as the stack won't change below this frame. - lock.unlock(); - TRY(capture_current_thread()); - } else if (thread.is_active()) { - VERIFY(thread.cpu() != Processor::current_id()); - // If this is the case, the thread is currently running - // on another processor. We can't trust the kernel stack as - // it may be changing at any time. We need to probably send - // an IPI to that processor, have it walk the stack and wait - // until it returns the data back to us - auto& proc = Processor::current(); - ErrorOr result; - Processor::smp_unicast( - thread.cpu(), - [&]() { - dbgln("CPU[{}] getting stack for cpu #{}", Processor::current_id(), proc.id()); - ScopedAddressSpaceSwitcher switcher(thread.process()); - VERIFY(&Processor::current() != &proc); - VERIFY(&thread == Processor::current_thread()); - // NOTE: Because the other processor is still holding the - // scheduler lock while waiting for this callback to finish, - // the current thread on the target processor cannot change - - // TODO: What to do about page faults here? We might deadlock - // because the other processor is still holding the - // scheduler lock... - result = capture_current_thread(); - }, - false); - TRY(result); - } else { - switch (thread.state()) { - case Thread::State::Running: - VERIFY_NOT_REACHED(); // should have been handled above - case Thread::State::Runnable: - case Thread::State::Stopped: - case Thread::State::Blocked: - case Thread::State::Dying: - case Thread::State::Dead: { - ScopedAddressSpaceSwitcher switcher(thread.process()); - auto& regs = thread.regs(); - - ip = regs.ip(); - frame_ptr = regs.rbp; - - // TODO: We need to leave the scheduler lock here, but we also - // need to prevent the target thread from being run while - // we walk the stack - lock.unlock(); - TRY(walk_stack(frame_ptr)); - break; - } - default: - dbgln("Cannot capture stack trace for thread {} in state {}", thread, thread.state_string()); - break; - } - } - return stack_trace; -} - -ProcessorContainer& Processor::processors() -{ - return s_processors; -} - -template -Processor& ProcessorBase::by_id(u32 id) -{ - return *s_processors[id]; -} - -template -void ProcessorBase::exit_trap(TrapFrame& trap) -{ - VERIFY_INTERRUPTS_DISABLED(); - VERIFY(&Processor::current() == this); - - auto* self = static_cast(this); - - // Temporarily enter a critical section. This is to prevent critical - // sections entered and left within e.g. smp_process_pending_messages - // to trigger a context switch while we're executing this function - // See the comment at the end of the function why we don't use - // ScopedCritical here. - m_in_critical = m_in_critical + 1; - - VERIFY(m_in_irq >= trap.prev_irq_level); - m_in_irq = trap.prev_irq_level; - - if (s_smp_enabled) - self->smp_process_pending_messages(); - - // Process the deferred call queue. Among other things, this ensures - // that any pending thread unblocks happen before we enter the scheduler. - m_deferred_call_pool.execute_pending(); - - auto* current_thread = Processor::current_thread(); - if (current_thread) { - auto& current_trap = current_thread->current_trap(); - current_trap = trap.next_trap; - ExecutionMode new_previous_mode; - if (current_trap) { - VERIFY(current_trap->regs); - // If we have another higher level trap then we probably returned - // from an interrupt or irq handler. - new_previous_mode = current_trap->regs->previous_mode(); - } else { - // If we don't have a higher level trap then we're back in user mode. - // Which means that the previous mode prior to being back in user mode was kernel mode - new_previous_mode = ExecutionMode::Kernel; - } - - if (current_thread->set_previous_mode(new_previous_mode)) - current_thread->update_time_scheduled(TimeManagement::scheduler_current_time(), true, false); - } - - VERIFY_INTERRUPTS_DISABLED(); - - // Leave the critical section without actually enabling interrupts. - // We don't want context switches to happen until we're explicitly - // triggering a switch in check_invoke_scheduler. - m_in_critical = m_in_critical - 1; - if (!m_in_irq && !m_in_critical) - check_invoke_scheduler(); -} - -template -void ProcessorBase::flush_tlb_local(VirtualAddress vaddr, size_t page_count) -{ - auto ptr = vaddr.as_ptr(); - while (page_count > 0) { - asm volatile("invlpg %0" - : - : "m"(*ptr) - : "memory"); - ptr += PAGE_SIZE; - page_count--; - } -} - -template -void ProcessorBase::flush_entire_tlb_local() -{ - write_cr3(read_cr3()); -} - -template -void ProcessorBase::flush_tlb(Memory::PageDirectory const* page_directory, VirtualAddress vaddr, size_t page_count) -{ - if (s_smp_enabled && (!Memory::is_user_address(vaddr) || Process::current().thread_count() > 1)) - Processor::smp_broadcast_flush_tlb(page_directory, vaddr, page_count); - else - flush_tlb_local(vaddr, page_count); -} - -void Processor::smp_return_to_pool(ProcessorMessage& msg) -{ - ProcessorMessage* next = nullptr; - for (;;) { - msg.next = next; - if (s_message_pool.compare_exchange_strong(next, &msg, AK::MemoryOrder::memory_order_acq_rel)) - break; - Processor::pause(); - } -} - -ProcessorMessage& Processor::smp_get_from_pool() -{ - ProcessorMessage* msg; - - // The assumption is that messages are never removed from the pool! - for (;;) { - msg = s_message_pool.load(AK::MemoryOrder::memory_order_consume); - if (!msg) { - if (!Processor::current().smp_process_pending_messages()) { - Processor::pause(); - } - continue; - } - // If another processor were to use this message in the meanwhile, - // "msg" is still valid (because it never gets freed). We'd detect - // this because the expected value "msg" and pool would - // no longer match, and the compare_exchange will fail. But accessing - // "msg->next" is always safe here. - if (s_message_pool.compare_exchange_strong(msg, msg->next, AK::MemoryOrder::memory_order_acq_rel)) { - // We successfully "popped" this available message - break; - } - } - - VERIFY(msg != nullptr); - return *msg; -} - -template -u32 ProcessorBase::smp_wake_n_idle_processors(u32 wake_count) -{ - VERIFY_INTERRUPTS_DISABLED(); - VERIFY(wake_count > 0); - if (!s_smp_enabled) - return 0; - - // Wake at most N - 1 processors - if (wake_count >= Processor::count()) { - wake_count = Processor::count() - 1; - VERIFY(wake_count > 0); - } - - u32 current_id = Processor::current_id(); - - u32 did_wake_count = 0; - auto& apic = APIC::the(); - while (did_wake_count < wake_count) { - // Try to get a set of idle CPUs and flip them to busy - u32 idle_mask = Processor::s_idle_cpu_mask.load(AK::MemoryOrder::memory_order_relaxed) & ~(1u << current_id); - u32 idle_count = popcount(idle_mask); - if (idle_count == 0) - break; // No (more) idle processor available - - u32 found_mask = 0; - for (u32 i = 0; i < idle_count; i++) { - u32 cpu = bit_scan_forward(idle_mask) - 1; - idle_mask &= ~(1u << cpu); - found_mask |= 1u << cpu; - } - - idle_mask = Processor::s_idle_cpu_mask.fetch_and(~found_mask, AK::MemoryOrder::memory_order_acq_rel) & found_mask; - if (idle_mask == 0) - continue; // All of them were flipped to busy, try again - idle_count = popcount(idle_mask); - for (u32 i = 0; i < idle_count; i++) { - u32 cpu = bit_scan_forward(idle_mask) - 1; - idle_mask &= ~(1u << cpu); - - // Send an IPI to that CPU to wake it up. There is a possibility - // someone else woke it up as well, or that it woke up due to - // a timer interrupt. But we tried hard to avoid this... - apic.send_ipi(cpu); - did_wake_count++; - } - } - return did_wake_count; -} - -template -UNMAP_AFTER_INIT void ProcessorBase::smp_enable() -{ - size_t msg_pool_size = Processor::count() * 100u; - size_t msg_entries_cnt = Processor::count(); - - auto msgs = new ProcessorMessage[msg_pool_size]; - auto msg_entries = new ProcessorMessageEntry[msg_pool_size * msg_entries_cnt]; - size_t msg_entry_i = 0; - for (size_t i = 0; i < msg_pool_size; i++, msg_entry_i += msg_entries_cnt) { - auto& msg = msgs[i]; - msg.next = i < msg_pool_size - 1 ? &msgs[i + 1] : nullptr; - msg.per_proc_entries = &msg_entries[msg_entry_i]; - for (size_t k = 0; k < msg_entries_cnt; k++) - msg_entries[msg_entry_i + k].msg = &msg; - } - - s_message_pool.store(&msgs[0], AK::MemoryOrder::memory_order_release); - - // Start sending IPI messages - s_smp_enabled = true; -} - -void Processor::smp_cleanup_message(ProcessorMessage& msg) -{ - switch (msg.type) { - case ProcessorMessage::Callback: - msg.callback_value().~Function(); - break; - default: - break; - } -} - -bool Processor::smp_process_pending_messages() -{ - VERIFY(s_smp_enabled); - - bool did_process = false; - enter_critical(); - - if (auto pending_msgs = m_message_queue.exchange(nullptr, AK::MemoryOrder::memory_order_acq_rel)) { - // We pulled the stack of pending messages in LIFO order, so we need to reverse the list first - auto reverse_list = - [](ProcessorMessageEntry* list) -> ProcessorMessageEntry* { - ProcessorMessageEntry* rev_list = nullptr; - while (list) { - auto next = list->next; - list->next = rev_list; - rev_list = list; - list = next; - } - return rev_list; - }; - - pending_msgs = reverse_list(pending_msgs); - - // now process in the right order - ProcessorMessageEntry* next_msg; - for (auto cur_msg = pending_msgs; cur_msg; cur_msg = next_msg) { - next_msg = cur_msg->next; - auto msg = cur_msg->msg; - - dbgln_if(SMP_DEBUG, "SMP[{}]: Processing message {}", current_id(), VirtualAddress(msg)); - - switch (msg->type) { - case ProcessorMessage::Callback: - msg->invoke_callback(); - break; - case ProcessorMessage::FlushTlb: - if (Memory::is_user_address(VirtualAddress(msg->flush_tlb.ptr))) { - // We assume that we don't cross into kernel land! - VERIFY(Memory::is_user_range(VirtualAddress(msg->flush_tlb.ptr), msg->flush_tlb.page_count * PAGE_SIZE)); - if (read_cr3() != msg->flush_tlb.page_directory->cr3()) { - // This processor isn't using this page directory right now, we can ignore this request - dbgln_if(SMP_DEBUG, "SMP[{}]: No need to flush {} pages at {}", current_id(), msg->flush_tlb.page_count, VirtualAddress(msg->flush_tlb.ptr)); - break; - } - } - flush_tlb_local(VirtualAddress(msg->flush_tlb.ptr), msg->flush_tlb.page_count); - break; - } - - bool is_async = msg->async; // Need to cache this value *before* dropping the ref count! - auto prev_refs = msg->refs.fetch_sub(1u, AK::MemoryOrder::memory_order_acq_rel); - VERIFY(prev_refs != 0); - if (prev_refs == 1) { - // All processors handled this. If this is an async message, - // we need to clean it up and return it to the pool - if (is_async) { - smp_cleanup_message(*msg); - smp_return_to_pool(*msg); - } - } - - if (m_halt_requested.load(AK::MemoryOrder::memory_order_relaxed)) - halt_this(); - } - did_process = true; - } else if (m_halt_requested.load(AK::MemoryOrder::memory_order_relaxed)) { - halt_this(); - } - - leave_critical(); - return did_process; -} - -bool Processor::smp_enqueue_message(ProcessorMessage& msg) -{ - // Note that it's quite possible that the other processor may pop - // the queue at any given time. We rely on the fact that the messages - // are pooled and never get freed! - auto& msg_entry = msg.per_proc_entries[id()]; - VERIFY(msg_entry.msg == &msg); - ProcessorMessageEntry* next = nullptr; - for (;;) { - msg_entry.next = next; - if (m_message_queue.compare_exchange_strong(next, &msg_entry, AK::MemoryOrder::memory_order_acq_rel)) - break; - Processor::pause(); - } - - // If the enqueued message was the only message in the queue when posted, - // we return true. This is used by callers when deciding whether to generate an IPI. - return next == nullptr; -} - -void Processor::smp_broadcast_message(ProcessorMessage& msg) -{ - auto& current_processor = Processor::current(); - - dbgln_if(SMP_DEBUG, "SMP[{}]: Broadcast message {} to cpus: {} processor: {}", current_processor.id(), VirtualAddress(&msg), count(), VirtualAddress(¤t_processor)); - - msg.refs.store(count() - 1, AK::MemoryOrder::memory_order_release); - VERIFY(msg.refs > 0); - bool need_broadcast = false; - for_each( - [&](Processor& proc) { - if (&proc != ¤t_processor) { - if (proc.smp_enqueue_message(msg)) - need_broadcast = true; - } - }); - - // Now trigger an IPI on all other APs (unless all targets already had messages queued) - if (need_broadcast) - APIC::the().broadcast_ipi(); -} - -void Processor::smp_broadcast_wait_sync(ProcessorMessage& msg) -{ - auto& cur_proc = Processor::current(); - VERIFY(!msg.async); - // If synchronous then we must cleanup and return the message back - // to the pool. Otherwise, the last processor to complete it will return it - while (msg.refs.load(AK::MemoryOrder::memory_order_consume) != 0) { - Processor::pause(); - - // We need to process any messages that may have been sent to - // us while we're waiting. This also checks if another processor - // may have requested us to halt. - cur_proc.smp_process_pending_messages(); - } - - smp_cleanup_message(msg); - smp_return_to_pool(msg); -} - -void Processor::smp_unicast_message(u32 cpu, ProcessorMessage& msg, bool async) -{ - auto& current_processor = Processor::current(); - VERIFY(cpu != current_processor.id()); - auto& target_processor = processors()[cpu]; - msg.async = async; - - dbgln_if(SMP_DEBUG, "SMP[{}]: Send message {} to cpu #{} processor: {}", current_processor.id(), VirtualAddress(&msg), cpu, VirtualAddress(&target_processor)); - - msg.refs.store(1u, AK::MemoryOrder::memory_order_release); - if (target_processor->smp_enqueue_message(msg)) { - APIC::the().send_ipi(cpu); - } - - if (!async) { - // If synchronous then we must cleanup and return the message back - // to the pool. Otherwise, the last processor to complete it will return it - while (msg.refs.load(AK::MemoryOrder::memory_order_consume) != 0) { - Processor::pause(); - - // We need to process any messages that may have been sent to - // us while we're waiting. This also checks if another processor - // may have requested us to halt. - current_processor.smp_process_pending_messages(); - } - - smp_cleanup_message(msg); - smp_return_to_pool(msg); - } -} - -void Processor::smp_unicast(u32 cpu, Function callback, bool async) -{ - auto& msg = smp_get_from_pool(); - msg.type = ProcessorMessage::Callback; - new (msg.callback_storage) ProcessorMessage::CallbackFunction(move(callback)); - smp_unicast_message(cpu, msg, async); -} - -void Processor::smp_broadcast_flush_tlb(Memory::PageDirectory const* page_directory, VirtualAddress vaddr, size_t page_count) -{ - auto& msg = smp_get_from_pool(); - msg.async = false; - msg.type = ProcessorMessage::FlushTlb; - msg.flush_tlb.page_directory = page_directory; - msg.flush_tlb.ptr = vaddr.as_ptr(); - msg.flush_tlb.page_count = page_count; - smp_broadcast_message(msg); - // While the other processors handle this request, we'll flush ours - flush_tlb_local(vaddr, page_count); - // Now wait until everybody is done as well - smp_broadcast_wait_sync(msg); -} - -void Processor::smp_broadcast_halt() -{ - // We don't want to use a message, because this could have been triggered - // by being out of memory and we might not be able to get a message - for_each( - [&](Processor& proc) { - proc.m_halt_requested.store(true, AK::MemoryOrder::memory_order_release); - }); - - // Now trigger an IPI on all other APs - APIC::the().broadcast_ipi(); -} - -template -void ProcessorBase::halt() -{ - if (s_smp_enabled) - Processor::smp_broadcast_halt(); - - halt_this(); -} - -UNMAP_AFTER_INIT void Processor::gdt_init() -{ - m_gdt_length = 0; - m_gdtr.address = nullptr; - m_gdtr.limit = 0; - - write_raw_gdt_entry(0x0000, 0x00000000, 0x00000000); - write_raw_gdt_entry(GDT_SELECTOR_CODE0, 0x0000ffff, 0x00af9a00); // code0 - write_raw_gdt_entry(GDT_SELECTOR_DATA0, 0x0000ffff, 0x00af9200); // data0 - write_raw_gdt_entry(GDT_SELECTOR_DATA3, 0x0000ffff, 0x008ff200); // data3 - write_raw_gdt_entry(GDT_SELECTOR_CODE3, 0x0000ffff, 0x00affa00); // code3 - - Descriptor tss_descriptor {}; - tss_descriptor.set_base(VirtualAddress { (size_t)&m_tss & 0xffffffff }); - tss_descriptor.set_limit(sizeof(TSS) - 1); - tss_descriptor.dpl = 0; - tss_descriptor.segment_present = 1; - tss_descriptor.granularity = 0; - tss_descriptor.operation_size64 = 0; - tss_descriptor.operation_size32 = 1; - tss_descriptor.descriptor_type = 0; - tss_descriptor.type = Descriptor::SystemType::AvailableTSS; - write_gdt_entry(GDT_SELECTOR_TSS, tss_descriptor); // tss - - Descriptor tss_descriptor_part2 {}; - tss_descriptor_part2.low = (size_t)&m_tss >> 32; - write_gdt_entry(GDT_SELECTOR_TSS_PART2, tss_descriptor_part2); - - flush_gdt(); - load_task_register(GDT_SELECTOR_TSS); - - MSR gs_base(MSR_GS_BASE); - gs_base.set((u64)this); -} - -extern "C" void context_first_init(Thread* from_thread, Thread* to_thread, [[maybe_unused]] TrapFrame* trap) -{ - do_context_first_init(from_thread, to_thread); -} - -extern "C" void enter_thread_context(Thread* from_thread, Thread* to_thread) -{ - VERIFY(from_thread == to_thread || from_thread->state() != Thread::State::Running); - VERIFY(to_thread->state() == Thread::State::Running); - - bool has_fxsr = Processor::current().has_feature(CPUFeature::FXSR); - bool has_xsave_avx_support = Processor::current().has_feature(CPUFeature::XSAVE) && Processor::current().has_feature(CPUFeature::AVX); - Processor::set_current_thread(*to_thread); - - auto& from_regs = from_thread->regs(); - auto& to_regs = to_thread->regs(); - - // NOTE: IOPL should never be non-zero in any situation, so let's panic immediately - // instead of carrying on with elevated I/O privileges. - VERIFY(get_iopl_from_eflags(to_regs.flags()) == 0); - - if (has_xsave_avx_support) { - // The specific state components saved correspond to the bits set in the requested-feature bitmap (RFBM), which is the logical-AND of EDX:EAX and XCR0. - // https://www.moritz.systems/blog/how-debuggers-work-getting-and-setting-x86-registers-part-2/ - asm volatile("xsave %0\n" - : "=m"(from_thread->fpu_state()) - : "a"(static_cast(SIMD::StateComponent::AVX | SIMD::StateComponent::SSE | SIMD::StateComponent::X87)), "d"(0u)); - } else if (has_fxsr) { - asm volatile("fxsave %0" - : "=m"(from_thread->fpu_state())); - } else { - asm volatile("fnsave %0" - : "=m"(from_thread->fpu_state())); - } - - if (from_thread->process().is_traced()) - read_debug_registers_into(from_thread->debug_register_state()); - - if (to_thread->process().is_traced()) { - write_debug_registers_from(to_thread->debug_register_state()); - } else { - clear_debug_registers(); - } - - auto& processor = Processor::current(); - Processor::set_fs_base(to_thread->arch_specific_data().fs_base); - - if (from_regs.cr3 != to_regs.cr3) - write_cr3(to_regs.cr3); - - to_thread->set_cpu(processor.id()); - - auto in_critical = to_thread->saved_critical(); - VERIFY(in_critical > 0); - Processor::restore_critical(in_critical); - - if (has_xsave_avx_support) - asm volatile("xrstor %0" ::"m"(to_thread->fpu_state()), "a"(static_cast(SIMD::StateComponent::AVX | SIMD::StateComponent::SSE | SIMD::StateComponent::X87)), "d"(0u)); - else if (has_fxsr) - asm volatile("fxrstor %0" ::"m"(to_thread->fpu_state())); - else - asm volatile("frstor %0" ::"m"(to_thread->fpu_state())); -} - -extern "C" NO_SANITIZE_COVERAGE FlatPtr do_init_context(Thread* thread, u32 flags) -{ - VERIFY_INTERRUPTS_DISABLED(); - thread->regs().set_flags(flags); - return Processor::current().init_context(*thread, true); -} - -// FIXME: Share this code with other architectures. -template -void ProcessorBase::assume_context(Thread& thread, InterruptsState new_interrupts_state) -{ - dbgln_if(CONTEXT_SWITCH_DEBUG, "Assume context for thread {} {}", VirtualAddress(&thread), thread); - - VERIFY_INTERRUPTS_DISABLED(); - Scheduler::prepare_after_exec(); - // in_critical() should be 2 here. The critical section in Process::exec - // and then the scheduler lock - VERIFY(Processor::in_critical() == 2); - - u32 flags = 2 | (new_interrupts_state == InterruptsState::Enabled ? 0x200 : 0); - do_assume_context(&thread, flags); - - VERIFY_NOT_REACHED(); -} - -template -u32 ProcessorBase::clear_critical() -{ - InterruptDisabler disabler; - auto prev_critical = in_critical(); - write_gs_ptr(__builtin_offsetof(Processor, m_in_critical), 0); - auto& proc = current(); - if (proc.m_in_irq == 0) - proc.check_invoke_scheduler(); - return prev_critical; -} - -NAKED void thread_context_first_enter(void) -{ - // enter_thread_context returns to here first time a thread is executing - asm( - // switch_context will have pushed from_thread and to_thread to our news - // stack prior to thread_context_first_enter() being called, and the - // pointer to TrapFrame was the top of the stack before that - " popq %rdi \n" // from_thread (argument 0) - " popq %rsi \n" // to_thread (argument 1) - " popq %rdx \n" // pointer to TrapFrame (argument 2) - " cld \n" - " call context_first_init \n" - " jmp common_trap_exit \n"); -} - -NAKED NO_SANITIZE_COVERAGE void do_assume_context(Thread*, u32) -{ - // clang-format off - // FIXME: I hope (Thread* thread, u32 flags) aren't compiled away - asm( - " movq %rdi, %r12 \n" // save thread ptr - " movq %rsi, %r13 \n" // save flags - // We're going to call Processor::init_context, so just make sure - // we have enough stack space so we don't stomp over it - " subq $(" __STRINGIFY(16 + REGISTER_STATE_SIZE + TRAP_FRAME_SIZE + 8) "), %rsp \n" - " cld \n" - " call do_init_context \n" - " movq %rax, %rsp \n" // move stack pointer to what Processor::init_context set up for us - " movq %r12, %rdi \n" // to_thread - " movq %r12, %rsi \n" // from_thread - " pushq %r12 \n" // to_thread (for thread_context_first_enter) - " pushq %r12 \n" // from_thread (for thread_context_first_enter) - " leaq thread_context_first_enter(%rip), %r12 \n" // should be same as regs.rip - " pushq %r12 \n" - " jmp enter_thread_context \n"); - // clang-format on -} - -template -StringView ProcessorBase::platform_string() -{ - return "x86_64"sv; -} - -template -FlatPtr ProcessorBase::init_context(Thread& thread, bool leave_crit) -{ - VERIFY(is_kernel_mode()); - VERIFY(g_scheduler_lock.is_locked()); - if (leave_crit) { - // Leave the critical section we set up in in Process::exec, - // but because we still have the scheduler lock we should end up with 1 - VERIFY(in_critical() == 2); - m_in_critical = 1; // leave it without triggering anything or restoring flags - } - - u64 kernel_stack_top = thread.kernel_stack_top(); - - // Add a random offset between 0-256 (16-byte aligned) - kernel_stack_top -= round_up_to_power_of_two(get_fast_random(), 16); - - u64 stack_top = kernel_stack_top; - - // TODO: handle NT? - VERIFY((cpu_flags() & 0x24000) == 0); // Assume !(NT | VM) - - auto& regs = thread.regs(); - bool return_to_user = (regs.cs & 3) != 0; - - stack_top -= 1 * sizeof(u64); - *reinterpret_cast(kernel_stack_top - 2 * sizeof(u64)) = FlatPtr(&exit_kernel_thread); - - stack_top -= sizeof(RegisterState); - - // we want to end up 16-byte aligned, %rsp + 8 should be aligned - stack_top -= sizeof(u64); - *reinterpret_cast(kernel_stack_top - sizeof(u64)) = 0; - - // set up the stack so that after returning from thread_context_first_enter() - // we will end up either in kernel mode or user mode, depending on how the thread is set up - // However, the first step is to always start in kernel mode with thread_context_first_enter - RegisterState& iretframe = *reinterpret_cast(stack_top); - iretframe.rdi = regs.rdi; - iretframe.rsi = regs.rsi; - iretframe.rbp = regs.rbp; - iretframe.rsp = 0; - iretframe.rbx = regs.rbx; - iretframe.rdx = regs.rdx; - iretframe.rcx = regs.rcx; - iretframe.rax = regs.rax; - iretframe.r8 = regs.r8; - iretframe.r9 = regs.r9; - iretframe.r10 = regs.r10; - iretframe.r11 = regs.r11; - iretframe.r12 = regs.r12; - iretframe.r13 = regs.r13; - iretframe.r14 = regs.r14; - iretframe.r15 = regs.r15; - iretframe.rflags = regs.rflags; - iretframe.rip = regs.rip; - iretframe.cs = regs.cs; - if (return_to_user) { - iretframe.userspace_rsp = regs.rsp; - iretframe.userspace_ss = GDT_SELECTOR_DATA3 | 3; - } else { - iretframe.userspace_rsp = kernel_stack_top; - iretframe.userspace_ss = 0; - } - - // make space for a trap frame - stack_top -= sizeof(TrapFrame); - TrapFrame& trap = *reinterpret_cast(stack_top); - trap.regs = &iretframe; - trap.prev_irq_level = 0; - trap.next_trap = nullptr; - - stack_top -= sizeof(u64); // pointer to TrapFrame - *reinterpret_cast(stack_top) = stack_top + 8; - - if constexpr (CONTEXT_SWITCH_DEBUG) { - if (return_to_user) { - dbgln("init_context {} ({}) set up to execute at rip={}:{}, rsp={}, stack_top={}, user_top={}", - thread, - VirtualAddress(&thread), - iretframe.cs, regs.rip, - VirtualAddress(regs.rsp), - VirtualAddress(stack_top), - iretframe.userspace_rsp); - } else { - dbgln("init_context {} ({}) set up to execute at rip={}:{}, rsp={}, stack_top={}", - thread, - VirtualAddress(&thread), - iretframe.cs, regs.rip, - VirtualAddress(regs.rsp), - VirtualAddress(stack_top)); - } - } - - // make switch_context() always first return to thread_context_first_enter() - // in kernel mode, so set up these values so that we end up popping iretframe - // off the stack right after the context switch completed, at which point - // control is transferred to what iretframe is pointing to. - regs.rip = FlatPtr(&thread_context_first_enter); - regs.rsp0 = kernel_stack_top; - regs.rsp = stack_top; - regs.cs = GDT_SELECTOR_CODE0; - return stack_top; -} - -template -void ProcessorBase::switch_context(Thread*& from_thread, Thread*& to_thread) -{ - VERIFY(!m_in_irq); - VERIFY(m_in_critical == 1); - VERIFY(is_kernel_mode()); - auto* self = static_cast(this); - - dbgln_if(CONTEXT_SWITCH_DEBUG, "switch_context --> switching out of: {} {}", VirtualAddress(from_thread), *from_thread); - - // m_in_critical is restored in enter_thread_context - from_thread->save_critical(m_in_critical); - - // clang-format off - // Switch to new thread context, passing from_thread and to_thread - // through to the new context using registers rdx and rax - asm volatile( - // NOTE: changing how much we push to the stack affects thread_context_first_enter()! - "pushfq \n" - "pushq %%rbx \n" - "pushq %%rcx \n" - "pushq %%rbp \n" - "pushq %%rsi \n" - "pushq %%rdi \n" - "pushq %%r8 \n" - "pushq %%r9 \n" - "pushq %%r10 \n" - "pushq %%r11 \n" - "pushq %%r12 \n" - "pushq %%r13 \n" - "pushq %%r14 \n" - "pushq %%r15 \n" - "movq %%rsp, %[from_rsp] \n" - "leaq 1f(%%rip), %%rbx \n" - "movq %%rbx, %[from_rip] \n" - "movq %[to_rsp0], %%rbx \n" - "movl %%ebx, %[tss_rsp0l] \n" - "shrq $32, %%rbx \n" - "movl %%ebx, %[tss_rsp0h] \n" - "movq %[to_rsp], %%rsp \n" - "movq %%rbp, %[from_rbp] \n" - "pushq %[to_thread] \n" - "pushq %[from_thread] \n" - "pushq %[to_rip] \n" - "cld \n" - "movq 16(%%rsp), %%rsi \n" - "movq 8(%%rsp), %%rdi \n" - "jmp enter_thread_context \n" - "1: \n" - "popq %%rdx \n" - "popq %%rax \n" - "popq %%r15 \n" - "popq %%r14 \n" - "popq %%r13 \n" - "popq %%r12 \n" - "popq %%r11 \n" - "popq %%r10 \n" - "popq %%r9 \n" - "popq %%r8 \n" - "popq %%rdi \n" - "popq %%rsi \n" - "popq %%rbp \n" - "popq %%rcx \n" - "popq %%rbx \n" - "popfq \n" - : [from_rsp] "=m" (from_thread->regs().rsp), - [from_rbp] "=m" (from_thread->regs().rbp), - [from_rip] "=m" (from_thread->regs().rip), - [tss_rsp0l] "=m" (self->m_tss.rsp0l), - [tss_rsp0h] "=m" (self->m_tss.rsp0h), - "=d" (from_thread), // needed so that from_thread retains the correct value - "=a" (to_thread) // needed so that to_thread retains the correct value - : [to_rsp] "g" (to_thread->regs().rsp), - [to_rsp0] "g" (to_thread->regs().rsp0), - [to_rip] "c" (to_thread->regs().rip), - [from_thread] "d" (from_thread), - [to_thread] "a" (to_thread) - : "memory", "rbx" - ); - // clang-format on - - dbgln_if(CONTEXT_SWITCH_DEBUG, "switch_context <-- from {} {} to {} {}", VirtualAddress(from_thread), *from_thread, VirtualAddress(to_thread), *to_thread); -} - -template -UNMAP_AFTER_INIT void ProcessorBase::initialize_context_switching(Thread& initial_thread) -{ - VERIFY(initial_thread.process().is_kernel_process()); - auto* self = static_cast(this); - - auto& regs = initial_thread.regs(); - self->m_tss.iomapbase = sizeof(self->m_tss); - self->m_tss.rsp0l = regs.rsp0 & 0xffffffff; - self->m_tss.rsp0h = regs.rsp0 >> 32; - - m_scheduler_initialized.set(); - - // clang-format off - asm volatile( - "movq %[new_rsp], %%rsp \n" // switch to new stack - "pushq %[from_to_thread] \n" // to_thread - "pushq %[from_to_thread] \n" // from_thread - "pushq %[new_rip] \n" // save the entry rip to the stack - "cld \n" - "pushq %[cpu] \n" // push argument for init_finished before register is clobbered - "call pre_init_finished \n" - "pop %%rdi \n" // move argument for init_finished into place - "call init_finished \n" - "call post_init_finished \n" - "movq 24(%%rsp), %%rdi \n" // move pointer to TrapFrame into place - "call enter_trap_no_irq \n" - "retq \n" - :: [new_rsp] "g" (regs.rsp), - [new_rip] "a" (regs.rip), - [from_to_thread] "b" (&initial_thread), - [cpu] "c" ((u64)id()) - ); - // clang-format on - - VERIFY_NOT_REACHED(); -} - -void Processor::set_fs_base(FlatPtr fs_base) -{ - MSR fs_base_msr(MSR_FS_BASE); - fs_base_msr.set(fs_base); -} - -template -void ProcessorBase::idle_begin() const -{ - Processor::s_idle_cpu_mask.fetch_or(1u << m_cpu, AK::MemoryOrder::memory_order_relaxed); -} - -template -void ProcessorBase::idle_end() const -{ - Processor::s_idle_cpu_mask.fetch_and(~(1u << m_cpu), AK::MemoryOrder::memory_order_relaxed); -} - -template -void ProcessorBase::wait_for_interrupt() const -{ - asm("hlt"); -} - -} - -#include diff --git a/Kernel/Arch/x86_64/Processor.h b/Kernel/Arch/x86_64/Processor.h deleted file mode 100644 index 66c15e05084..00000000000 --- a/Kernel/Arch/x86_64/Processor.h +++ /dev/null @@ -1,308 +0,0 @@ -/* - * Copyright (c) 2018-2021, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -VALIDATE_IS_X86() - -namespace Kernel { - -class ProcessorInfo; -struct ProcessorMessage; -struct ProcessorMessageEntry; - -#define MSR_EFER 0xc0000080 -#define MSR_STAR 0xc0000081 -#define MSR_LSTAR 0xc0000082 -#define MSR_SFMASK 0xc0000084 -#define MSR_FS_BASE 0xc0000100 -#define MSR_GS_BASE 0xc0000101 -#define MSR_IA32_EFER 0xc0000080 -#define MSR_IA32_PAT 0x277 - -enum class InterruptsState; -class Processor; - -template -class ProcessorBase; - -// Note: We only support 64 processors at most at the moment, -// so allocate 64 slots of inline capacity in the container. - -constexpr size_t MAX_CPU_COUNT = 64; -using ProcessorContainer = Array; - -extern "C" void context_first_init(Thread* from_thread, Thread* to_thread, [[maybe_unused]] TrapFrame* trap); - -// If this fails to compile because ProcessorBase was not found, you are including this header directly. -// Include Arch/Processor.h instead. -class Processor final : public ProcessorBase { - friend class ProcessorInfo; - // Allow some implementations to access the idle CPU mask and various x86 implementation details. - friend class ProcessorBase; - -private: - // Saved user stack for the syscall instruction. - void* m_user_stack; - - DescriptorTablePointer m_gdtr; - alignas(Descriptor) Descriptor m_gdt[256]; - u32 m_gdt_length; - - static Atomic s_idle_cpu_mask; - - TSS m_tss; - SetOnce m_has_qemu_hvf_quirk; - - ProcessorInfo* m_info; - - Atomic m_message_queue; - - void gdt_init(); - void write_raw_gdt_entry(u16 selector, u32 low, u32 high); - void write_gdt_entry(u16 selector, Descriptor& descriptor); - static ProcessorContainer& processors(); - - static void smp_return_to_pool(ProcessorMessage& msg); - static ProcessorMessage& smp_get_from_pool(); - static void smp_cleanup_message(ProcessorMessage& msg); - bool smp_enqueue_message(ProcessorMessage&); - static void smp_unicast_message(u32 cpu, ProcessorMessage& msg, bool async); - static void smp_broadcast_message(ProcessorMessage& msg); - static void smp_broadcast_wait_sync(ProcessorMessage& msg); - static void smp_broadcast_halt(); - - void cpu_detect(); - void cpu_setup(); - - void detect_hypervisor(); - void detect_hypervisor_hyperv(CPUID const& hypervisor_leaf_range); - -public: - Descriptor& get_gdt_entry(u16 selector); - void flush_gdt(); - DescriptorTablePointer const& get_gdtr(); - - template Callback> - static inline IterationDecision for_each(Callback callback) - { - auto& procs = processors(); - size_t count = procs.size(); - for (size_t i = 0; i < count; i++) { - if (callback(*procs[i]) == IterationDecision::Break) - return IterationDecision::Break; - } - return IterationDecision::Continue; - } - - template Callback> - static inline IterationDecision for_each(Callback callback) - { - auto& procs = processors(); - size_t count = procs.size(); - for (size_t i = 0; i < count; i++) { - if (procs[i] != nullptr) - callback(*procs[i]); - } - return IterationDecision::Continue; - } - - static inline ErrorOr try_for_each(Function(Processor&)> callback) - { - auto& procs = processors(); - size_t count = procs.size(); - for (size_t i = 0; i < count; i++) { - if (procs[i] != nullptr) - TRY(callback(*procs[i])); - } - return {}; - } - - ALWAYS_INLINE ProcessorInfo& info() { return *m_info; } - - static constexpr u64 user_stack_offset() - { - return __builtin_offsetof(Processor, m_user_stack); - } - static constexpr u64 kernel_stack_offset() - { - return __builtin_offsetof(Processor, m_tss) + __builtin_offsetof(TSS, rsp0l); - } - - bool smp_process_pending_messages(); - - static void smp_unicast(u32 cpu, Function, bool async); - static void smp_broadcast_flush_tlb(Memory::PageDirectory const*, VirtualAddress, size_t); - - static void set_fs_base(FlatPtr); -}; - -template -ALWAYS_INLINE NO_SANITIZE_COVERAGE Thread* ProcessorBase::current_thread() -{ - // If we were to use ProcessorBase::current here, we'd have to - // disable interrupts to prevent a race where we may get pre-empted - // right after getting the Processor structure and then get moved - // to another processor, which would lead us to get the wrong thread. - // To avoid having to disable interrupts, we can just read the field - // directly in an atomic fashion, similar to Processor::current. - return (Thread*)read_gs_ptr(__builtin_offsetof(ProcessorBase, m_current_thread)); -} - -template -ALWAYS_INLINE u32 ProcessorBase::current_id() -{ - // See comment in ProcessorBase::current_thread - return read_gs_ptr(__builtin_offsetof(ProcessorBase, m_cpu)); -} - -template -ALWAYS_INLINE void ProcessorBase::restore_critical(u32 prev_critical) -{ - // NOTE: This doesn't have to be atomic, and it's also fine if we - // get preempted in between these steps. If we move to another - // processors m_in_critical will move along with us. And if we - // are preempted, we would resume with the same flags. - write_gs_ptr(__builtin_offsetof(ProcessorBase, m_in_critical), prev_critical); -} - -template -ALWAYS_INLINE u32 ProcessorBase::in_critical() -{ - // See comment in ProcessorBase::current_thread - return read_gs_ptr(__builtin_offsetof(ProcessorBase, m_in_critical)); -} - -template -ALWAYS_INLINE void ProcessorBase::set_current_thread(Thread& current_thread) -{ - // See comment in ProcessorBase::current_thread - write_gs_ptr(__builtin_offsetof(ProcessorBase, m_current_thread), FlatPtr(¤t_thread)); -} - -template -ALWAYS_INLINE Thread* ProcessorBase::idle_thread() -{ - // See comment in ProcessorBase::current_thread - return (Thread*)read_gs_ptr(__builtin_offsetof(ProcessorBase, m_idle_thread)); -} - -template -T& ProcessorBase::current() -{ - return *(Processor*)(read_gs_ptr(__builtin_offsetof(ProcessorBase, m_self))); -} - -template -ALWAYS_INLINE bool ProcessorBase::is_initialized() -{ - return read_gs_ptr(__builtin_offsetof(ProcessorBase, m_self)) != 0; -} - -template -ALWAYS_INLINE void ProcessorBase::enter_critical() -{ - write_gs_ptr(__builtin_offsetof(ProcessorBase, m_in_critical), in_critical() + 1); -} - -template -ALWAYS_INLINE NO_SANITIZE_COVERAGE bool ProcessorBase::are_interrupts_enabled() -{ - return Kernel::are_interrupts_enabled(); -} - -template -ALWAYS_INLINE bool ProcessorBase::is_kernel_mode() -{ - u16 cs; - asm volatile( - "mov %%cs, %[cs] \n" - : [cs] "=g"(cs)); - return (cs & 3) == 0; -} - -template -ALWAYS_INLINE bool ProcessorBase::current_in_scheduler() -{ - auto value = read_gs_ptr(__builtin_offsetof(ProcessorBase, m_in_scheduler)); - return value; -} - -template -ALWAYS_INLINE void ProcessorBase::set_current_in_scheduler(bool value) -{ - write_gs_ptr(__builtin_offsetof(ProcessorBase, m_in_scheduler), value); -} - -template -ALWAYS_INLINE void ProcessorBase::enable_interrupts() -{ - sti(); -} - -template -ALWAYS_INLINE void ProcessorBase::disable_interrupts() -{ - cli(); -} - -template -ALWAYS_INLINE bool ProcessorBase::has_nx() const -{ - return has_feature(CPUFeature::NX); -} - -template -ALWAYS_INLINE bool ProcessorBase::has_pat() const -{ - return has_feature(CPUFeature::PAT); -} - -template -ALWAYS_INLINE u64 ProcessorBase::read_cpu_counter() -{ - return read_tsc(); -} - -template -ALWAYS_INLINE void ProcessorBase::pause() -{ - asm volatile("pause"); -} - -template -ALWAYS_INLINE void ProcessorBase::wait_check() -{ - Processor::pause(); - if (Processor::is_smp_enabled()) - Processor::current().smp_process_pending_messages(); -} - -template -ALWAYS_INLINE NO_SANITIZE_COVERAGE FlatPtr ProcessorBase::current_in_irq() -{ - return read_gs_ptr(__builtin_offsetof(Processor, m_in_irq)); -} - -} diff --git a/Kernel/Arch/x86_64/ProcessorInfo.cpp b/Kernel/Arch/x86_64/ProcessorInfo.cpp deleted file mode 100644 index 7075710c2fa..00000000000 --- a/Kernel/Arch/x86_64/ProcessorInfo.cpp +++ /dev/null @@ -1,186 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * Copyright (c) 2022, Linus Groh - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include - -namespace Kernel { - -ProcessorInfo::ProcessorInfo(Processor const& processor) - : m_vendor_id_string(build_vendor_id_string()) - , m_hypervisor_vendor_id_string(build_hypervisor_vendor_id_string(processor)) - , m_brand_string(build_brand_string()) - , m_features_string(build_features_string(processor)) -{ - CPUID cpuid(1); - m_stepping = cpuid.eax() & 0xf; - u32 model = (cpuid.eax() >> 4) & 0xf; - u32 family = (cpuid.eax() >> 8) & 0xf; - m_type = (cpuid.eax() >> 12) & 0x3; - u32 extended_model = (cpuid.eax() >> 16) & 0xf; - u32 extended_family = (cpuid.eax() >> 20) & 0xff; - if (family == 15) { - m_display_family = family + extended_family; - m_display_model = model + (extended_model << 4); - } else if (family == 6) { - m_display_family = family; - m_display_model = model + (extended_model << 4); - } else { - m_display_family = family; - m_display_model = model; - } - - // NOTE: Intel exposes detailed CPU's cache information in CPUID 04. On the - // other hand, AMD uses CPUID's extended function set. - if (m_vendor_id_string->view() == s_amd_vendor_id) - populate_cache_sizes_amd(); - else if (m_vendor_id_string->view() == s_intel_vendor_id) - populate_cache_sizes_intel(); -} - -static void emit_u32(StringBuilder& builder, u32 value) -{ - builder.appendff("{:c}{:c}{:c}{:c}", - value & 0xff, - (value >> 8) & 0xff, - (value >> 16) & 0xff, - (value >> 24) & 0xff); -} - -NonnullOwnPtr ProcessorInfo::build_vendor_id_string() -{ - CPUID cpuid(0); - StringBuilder builder; - emit_u32(builder, cpuid.ebx()); - emit_u32(builder, cpuid.edx()); - emit_u32(builder, cpuid.ecx()); - // NOTE: This isn't necessarily fixed length and might have null terminators at the end. - return KString::must_create(builder.string_view().trim("\0"sv, TrimMode::Right)); -} - -NonnullOwnPtr ProcessorInfo::build_hypervisor_vendor_id_string(Processor const& processor) -{ - if (!processor.has_feature(CPUFeature::HYPERVISOR)) - return KString::must_create({}); - - CPUID cpuid(0x40000000); - StringBuilder builder; - emit_u32(builder, cpuid.ebx()); - emit_u32(builder, cpuid.ecx()); - emit_u32(builder, cpuid.edx()); - // NOTE: This isn't necessarily fixed length and might have null terminators at the end. - return KString::must_create(builder.string_view().trim("\0"sv, TrimMode::Right)); -} - -NonnullOwnPtr ProcessorInfo::build_brand_string() -{ - u32 max_extended_leaf = CPUID(0x80000000).eax(); - - if (max_extended_leaf < 0x80000004) - return KString::must_create({}); - - StringBuilder builder; - auto append_brand_string_part_to_builder = [&](u32 i) { - CPUID cpuid(0x80000002 + i); - emit_u32(builder, cpuid.eax()); - emit_u32(builder, cpuid.ebx()); - emit_u32(builder, cpuid.ecx()); - emit_u32(builder, cpuid.edx()); - }; - append_brand_string_part_to_builder(0); - append_brand_string_part_to_builder(1); - append_brand_string_part_to_builder(2); - // NOTE: This isn't necessarily fixed length and might have null terminators at the end. - return KString::must_create(builder.string_view().trim("\0"sv, TrimMode::Right)); -} - -NonnullOwnPtr ProcessorInfo::build_features_string(Processor const& processor) -{ - StringBuilder builder; - bool first = true; - for (auto feature = CPUFeature::Type(1u); feature != CPUFeature::__End; feature <<= 1u) { - if (processor.has_feature(feature)) { - if (first) - first = false; - else - MUST(builder.try_append(' ')); - MUST(builder.try_append(cpu_feature_to_name(feature))); - } - } - return KString::must_create(builder.string_view()); -} - -void ProcessorInfo::populate_cache_sizes_amd() -{ - auto const max_extended_leaf = CPUID(0x80000000).eax(); - - if (max_extended_leaf < 0x80000005) - return; - - auto const l1_cache_info = CPUID(0x80000005); - - if (l1_cache_info.ecx() != 0) { - m_l1_data_cache = Cache { - .size = ((l1_cache_info.ecx() >> 24) & 0xff) * KiB, - .line_size = l1_cache_info.ecx() & 0xff, - }; - } - - if (l1_cache_info.edx() != 0) { - m_l1_instruction_cache = Cache { - .size = ((l1_cache_info.edx() >> 24) & 0xff) * KiB, - .line_size = l1_cache_info.edx() & 0xff, - }; - } - - if (max_extended_leaf < 0x80000006) - return; - - auto const l2_l3_cache_info = CPUID(0x80000006); - - if (l2_l3_cache_info.ecx() != 0) { - m_l2_cache = Cache { - .size = ((l2_l3_cache_info.ecx() >> 16) & 0xffff) * KiB, - .line_size = l2_l3_cache_info.ecx() & 0xff, - }; - } - - if (l2_l3_cache_info.edx() != 0) { - m_l3_cache = Cache { - .size = (static_cast((l2_l3_cache_info.edx() >> 18)) & 0x3fff) * 512 * KiB, - .line_size = l2_l3_cache_info.edx() & 0xff, - }; - } -} - -void ProcessorInfo::populate_cache_sizes_intel() -{ - auto const collect_cache_info = [](u32 ecx) { - auto const cache_info = CPUID(0x04, ecx); - auto const ways = ((cache_info.ebx() >> 22) & 0x3ff) + 1; - auto const partitions = ((cache_info.ebx() >> 12) & 0x3ff) + 1; - auto const line_size = (cache_info.ebx() & 0xfff) + 1; - auto const sets = cache_info.ecx() + 1; - - return Cache { - .size = ways * partitions * line_size * sets, - .line_size = line_size - }; - }; - - // NOTE: Those ECX numbers are the one used on recent Intel CPUs, an algorithm - // also exists to retrieve them. - m_l1_instruction_cache = collect_cache_info(0); - m_l1_data_cache = collect_cache_info(1); - m_l2_cache = collect_cache_info(2); - m_l3_cache = collect_cache_info(3); -} - -} diff --git a/Kernel/Arch/x86_64/ProcessorInfo.h b/Kernel/Arch/x86_64/ProcessorInfo.h deleted file mode 100644 index dfb4629c25a..00000000000 --- a/Kernel/Arch/x86_64/ProcessorInfo.h +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * Copyright (c) 2022, Linus Groh - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include - -#include -VALIDATE_IS_X86() - -namespace Kernel { - -class Processor; - -class ProcessorInfo { -public: - ProcessorInfo(Processor const& processor); - - struct Cache { - u64 size; - u64 line_size; - }; - - StringView vendor_id_string() const { return m_vendor_id_string->view(); } - StringView hypervisor_vendor_id_string() const { return m_hypervisor_vendor_id_string->view(); } - StringView brand_string() const { return m_brand_string->view(); } - StringView features_string() const { return m_features_string->view(); } - u32 display_model() const { return m_display_model; } - u32 display_family() const { return m_display_family; } - u32 stepping() const { return m_stepping; } - u32 type() const { return m_type; } - u32 apic_id() const { return m_apic_id; } - Optional const& l1_data_cache() const { return m_l1_data_cache; } - Optional const& l1_instruction_cache() const { return m_l1_instruction_cache; } - Optional const& l2_cache() const { return m_l2_cache; } - Optional const& l3_cache() const { return m_l3_cache; } - - void set_apic_id(u32 apic_id) { m_apic_id = apic_id; } - - static constexpr StringView s_amd_vendor_id = "AuthenticAMD"sv; - static constexpr StringView s_intel_vendor_id = "GenuineIntel"sv; - -private: - static NonnullOwnPtr build_vendor_id_string(); - static NonnullOwnPtr build_hypervisor_vendor_id_string(Processor const&); - static NonnullOwnPtr build_brand_string(); - static NonnullOwnPtr build_features_string(Processor const&); - - void populate_cache_sizes_amd(); - void populate_cache_sizes_intel(); - - NonnullOwnPtr m_vendor_id_string; - NonnullOwnPtr m_hypervisor_vendor_id_string; - NonnullOwnPtr m_brand_string; - NonnullOwnPtr m_features_string; - u32 m_display_model { 0 }; - u32 m_display_family { 0 }; - u32 m_stepping { 0 }; - u32 m_type { 0 }; - u32 m_apic_id { 0 }; - - Optional m_l1_data_cache; - Optional m_l1_instruction_cache; - Optional m_l2_cache; - Optional m_l3_cache; -}; - -} diff --git a/Kernel/Arch/x86_64/RTC.cpp b/Kernel/Arch/x86_64/RTC.cpp deleted file mode 100644 index 788868f158a..00000000000 --- a/Kernel/Arch/x86_64/RTC.cpp +++ /dev/null @@ -1,119 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include - -namespace Kernel::RTC { - -static time_t s_boot_time; - -void initialize() -{ - s_boot_time = now(); -} - -UnixDateTime boot_time() -{ - return UnixDateTime::from_seconds_since_epoch(s_boot_time); -} - -static bool update_in_progress() -{ - return CMOS::read(0x0a) & 0x80; -} - -static u8 bcd_to_binary(u8 bcd) -{ - return (bcd & 0x0F) + ((bcd >> 4) * 10); -} - -static bool try_to_read_registers(unsigned& year, unsigned& month, unsigned& day, unsigned& hour, unsigned& minute, unsigned& second) -{ - // Note: Let's wait 0.01 seconds until we stop trying to query the RTC CMOS - size_t time_passed_in_milliseconds = 0; - bool update_in_progress_ended_successfully = false; - while (time_passed_in_milliseconds < 100) { - if (!update_in_progress()) { - update_in_progress_ended_successfully = true; - break; - } - microseconds_delay(1000); - time_passed_in_milliseconds++; - } - - if (!update_in_progress_ended_successfully) { - year = 1970; - month = 1; - day = 1; - hour = 0; - minute = 0; - second = 0; - return false; - } - - u8 status_b = CMOS::read(0x0b); - - second = CMOS::read(0x00); - minute = CMOS::read(0x02); - hour = CMOS::read(0x04); - day = CMOS::read(0x07); - month = CMOS::read(0x08); - year = CMOS::read(0x09); - - bool is_pm = hour & 0x80; - - if (!(status_b & 0x04)) { - second = bcd_to_binary(second); - minute = bcd_to_binary(minute); - hour = bcd_to_binary(hour & 0x7F); - day = bcd_to_binary(day); - month = bcd_to_binary(month); - year = bcd_to_binary(year); - } - - if (!(status_b & 0x02)) { - // In the 12 hour clock, midnight and noon are 12, not 0. Map it to 0. - hour %= 12; - if (is_pm) - hour += 12; - } - - year += 2000; - return true; -} - -time_t now() -{ - - auto check_registers_against_preloaded_values = [](unsigned year, unsigned month, unsigned day, unsigned hour, unsigned minute, unsigned second) { - unsigned checked_year, checked_month, checked_day, checked_hour, checked_minute, checked_second; - if (!try_to_read_registers(checked_year, checked_month, checked_day, checked_hour, checked_minute, checked_second)) - return false; - return checked_year == year && checked_month == month && checked_day == day && checked_hour == hour && checked_minute == minute && checked_second == second; - }; - - unsigned year, month, day, hour, minute, second; - bool did_read_rtc_successfully = false; - for (size_t attempt = 0; attempt < 5; attempt++) { - if (!try_to_read_registers(year, month, day, hour, minute, second)) - break; - if (check_registers_against_preloaded_values(year, month, day, hour, minute, second)) { - did_read_rtc_successfully = true; - break; - } - } - - dmesgln("RTC: {} Year: {}, month: {}, day: {}, hour: {}, minute: {}, second: {}", (did_read_rtc_successfully ? "" : "(failed to read)"), year, month, day, hour, minute, second); - - time_t days = days_since_epoch(year, month, day); - return ((days * 24 + hour) * 60 + minute) * 60 + second; -} - -} diff --git a/Kernel/Arch/x86_64/RTC.h b/Kernel/Arch/x86_64/RTC.h deleted file mode 100644 index 32e81d5f093..00000000000 --- a/Kernel/Arch/x86_64/RTC.h +++ /dev/null @@ -1,18 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include - -namespace Kernel::RTC { - -void initialize(); -time_t now(); -UnixDateTime boot_time(); - -} diff --git a/Kernel/Arch/x86_64/RegisterState.h b/Kernel/Arch/x86_64/RegisterState.h deleted file mode 100644 index 4947d9b816f..00000000000 --- a/Kernel/Arch/x86_64/RegisterState.h +++ /dev/null @@ -1,171 +0,0 @@ -/* - * Copyright (c) 2018-2021, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include - -#include -#include -#include - -#include -VALIDATE_IS_X86() - -namespace Kernel { - -struct [[gnu::packed]] RegisterState { - FlatPtr rdi; - FlatPtr rsi; - FlatPtr rbp; - FlatPtr rsp; - FlatPtr rbx; - FlatPtr rdx; - FlatPtr rcx; - FlatPtr rax; - FlatPtr r8; - FlatPtr r9; - FlatPtr r10; - FlatPtr r11; - FlatPtr r12; - FlatPtr r13; - FlatPtr r14; - FlatPtr r15; - - u16 exception_code; - u16 isr_number; - u32 padding; - - FlatPtr rip; - FlatPtr cs; - FlatPtr rflags; - FlatPtr userspace_rsp; - FlatPtr userspace_ss; - - FlatPtr userspace_sp() const - { - return userspace_rsp; - } - void set_userspace_sp(FlatPtr value) { userspace_rsp = value; } - FlatPtr ip() const { return rip; } - void set_ip(FlatPtr value) { rip = value; } - void set_dx(FlatPtr value) { rdx = value; } - FlatPtr bp() const { return rbp; } - void set_bp(FlatPtr value) { rbp = value; } - FlatPtr flags() const { return rflags; } - void set_flags(FlatPtr value) { rflags = value; } - void set_return_reg(FlatPtr value) { rax = value; } - - void capture_syscall_params(FlatPtr& function, FlatPtr& arg1, FlatPtr& arg2, FlatPtr& arg3, FlatPtr& arg4) const - { - // The syscall instruction clobbers rcx, so we must use a different calling convention to 32-bit. - function = rax; - arg1 = rdx; - arg2 = rdi; - arg3 = rbx; - arg4 = rsi; - } - - ExecutionMode previous_mode() const - { - return ((cs & 3) != 0) ? ExecutionMode::User : ExecutionMode::Kernel; - } -}; - -#define REGISTER_STATE_SIZE (22 * 8) -static_assert(AssertSize()); - -inline void copy_kernel_registers_into_ptrace_registers(PtraceRegisters& ptrace_regs, RegisterState const& kernel_regs) -{ - ptrace_regs.rax = kernel_regs.rax; - ptrace_regs.rcx = kernel_regs.rcx; - ptrace_regs.rdx = kernel_regs.rdx; - ptrace_regs.rbx = kernel_regs.rbx; - ptrace_regs.rsp = kernel_regs.userspace_rsp; - ptrace_regs.rbp = kernel_regs.rbp; - ptrace_regs.rsi = kernel_regs.rsi; - ptrace_regs.rdi = kernel_regs.rdi; - ptrace_regs.rip = kernel_regs.rip; - ptrace_regs.r8 = kernel_regs.r8; - ptrace_regs.r9 = kernel_regs.r9; - ptrace_regs.r10 = kernel_regs.r10; - ptrace_regs.r11 = kernel_regs.r11; - ptrace_regs.r12 = kernel_regs.r12; - ptrace_regs.r13 = kernel_regs.r13; - ptrace_regs.r14 = kernel_regs.r14; - ptrace_regs.r15 = kernel_regs.r15; - ptrace_regs.rflags = kernel_regs.rflags, - ptrace_regs.cs = 0; - ptrace_regs.ss = 0; - ptrace_regs.ds = 0; - ptrace_regs.es = 0; - ptrace_regs.fs = 0; - ptrace_regs.gs = 0; -} - -inline void copy_ptrace_registers_into_kernel_registers(RegisterState& kernel_regs, PtraceRegisters const& ptrace_regs) -{ - kernel_regs.rax = ptrace_regs.rax; - kernel_regs.rcx = ptrace_regs.rcx; - kernel_regs.rdx = ptrace_regs.rdx; - kernel_regs.rbx = ptrace_regs.rbx; - kernel_regs.rsp = ptrace_regs.rsp; - kernel_regs.rbp = ptrace_regs.rbp; - kernel_regs.rsi = ptrace_regs.rsi; - kernel_regs.rdi = ptrace_regs.rdi; - kernel_regs.rip = ptrace_regs.rip; - kernel_regs.r8 = ptrace_regs.r8; - kernel_regs.r9 = ptrace_regs.r9; - kernel_regs.r10 = ptrace_regs.r10; - kernel_regs.r11 = ptrace_regs.r11; - kernel_regs.r12 = ptrace_regs.r12; - kernel_regs.r13 = ptrace_regs.r13; - kernel_regs.r14 = ptrace_regs.r14; - kernel_regs.r15 = ptrace_regs.r15; - // FIXME: do we need a separate safe_rflags_mask here? - kernel_regs.rflags = (kernel_regs.rflags & ~safe_eflags_mask) | (ptrace_regs.rflags & safe_eflags_mask); -} - -struct [[gnu::packed]] DebugRegisterState { - FlatPtr dr0; - FlatPtr dr1; - FlatPtr dr2; - FlatPtr dr3; - FlatPtr dr6; - FlatPtr dr7; -}; - -inline void read_debug_registers_into(DebugRegisterState& state) -{ - state.dr0 = read_dr0(); - state.dr1 = read_dr1(); - state.dr2 = read_dr2(); - state.dr3 = read_dr3(); - state.dr6 = read_dr6(); - state.dr7 = read_dr7(); -} - -inline void write_debug_registers_from(DebugRegisterState const& state) -{ - write_dr0(state.dr0); - write_dr1(state.dr1); - write_dr2(state.dr2); - write_dr3(state.dr3); - write_dr6(state.dr6); - write_dr7(state.dr7); -} - -inline void clear_debug_registers() -{ - write_dr0(0); - write_dr1(0); - write_dr2(0); - write_dr3(0); - write_dr7(1 << 10); // Bit 10 is reserved and must be set to 1. -} - -} diff --git a/Kernel/Arch/x86_64/SIMDState.h b/Kernel/Arch/x86_64/SIMDState.h deleted file mode 100644 index f2a6466a7d7..00000000000 --- a/Kernel/Arch/x86_64/SIMDState.h +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright (c) 2022, Leon Albrecht - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include - -namespace Kernel::SIMD { - -// Intel-Manual Vol 1 Chp 13.4 -enum StateComponent : u64 { - X87 = 1ull << 0ull, - SSE = 1ull << 1ull, // xmm0-xmm7(15) - AVX = 1ull << 2ull, // ymm0-ymm7(15) hi - MPX_BNDREGS = 1ull << 3ull, - MPX_BNDCSR = 1ull << 4ull, - AVX512_opmask = 1ull << 5ull, // k0 - k9 - AVX512_ZMM_hi = 1ull << 6ull, // 0 - 15 - AVX512_ZMM = 1ull << 7ull, // 16 - 31 full - PT = 1ull << 8ull, - PKRU = 1ull << 9ull, - - CET_U = 1ull << 11ull, - CET_S = 1ull << 12ull, - HDC = 1ull << 13ull, - - LBR = 1ull << 15ull, - HWP = 1ull << 16ull, - - XCOMP_ENABLE = 1ull << 63ull -}; -AK_ENUM_BITWISE_OPERATORS(StateComponent); - -struct [[gnu::packed]] LegacyRegion { - AK::X87ControlWord FCW; - u16 FSW; - u8 FTW; - u8 : 8; - u16 FOP; - - // 64-bit version - u64 FIP_64; - u64 FDP_64; - - AK::MXCSR MXCSR; - u32 MXCSR_mask; - u8 st_mmx[128]; - u8 xmm[256]; - u8 available[96]; // Extra available space -}; - -static_assert(sizeof(LegacyRegion) == 512); - -struct [[gnu::packed]] Header { - StateComponent xstate_bv; - StateComponent xcomp_bv; - u8 reserved[48]; -}; -static_assert(sizeof(Header) == 64); - -} diff --git a/Kernel/Arch/x86_64/SafeMem.cpp b/Kernel/Arch/x86_64/SafeMem.cpp deleted file mode 100644 index fe54941f57c..00000000000 --- a/Kernel/Arch/x86_64/SafeMem.cpp +++ /dev/null @@ -1,346 +0,0 @@ -/* - * Copyright (c) 2020, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include - -#define CODE_SECTION(section_name) __attribute__((section(section_name))) - -extern "C" u8 start_of_safemem_text[]; -extern "C" u8 end_of_safemem_text[]; - -extern "C" u8 safe_memcpy_ins_1[]; -extern "C" u8 safe_memcpy_1_faulted[]; -extern "C" u8 safe_memcpy_ins_2[]; -extern "C" u8 safe_memcpy_2_faulted[]; -extern "C" u8 safe_strnlen_ins[]; -extern "C" u8 safe_strnlen_faulted[]; -extern "C" u8 safe_memset_ins_1[]; -extern "C" u8 safe_memset_1_faulted[]; -extern "C" u8 safe_memset_ins_2[]; -extern "C" u8 safe_memset_2_faulted[]; - -extern "C" u8 start_of_safemem_atomic_text[]; -extern "C" u8 end_of_safemem_atomic_text[]; - -extern "C" u8 safe_atomic_fetch_add_relaxed_ins[]; -extern "C" u8 safe_atomic_fetch_add_relaxed_faulted[]; -extern "C" u8 safe_atomic_exchange_relaxed_ins[]; -extern "C" u8 safe_atomic_exchange_relaxed_faulted[]; -extern "C" u8 safe_atomic_load_relaxed_ins[]; -extern "C" u8 safe_atomic_load_relaxed_faulted[]; -extern "C" u8 safe_atomic_store_relaxed_ins[]; -extern "C" u8 safe_atomic_store_relaxed_faulted[]; -extern "C" u8 safe_atomic_compare_exchange_relaxed_ins[]; -extern "C" u8 safe_atomic_compare_exchange_relaxed_faulted[]; - -namespace Kernel { - -ALWAYS_INLINE bool validate_canonical_address(size_t address) -{ - auto most_significant_bits = Processor::current().virtual_address_bit_width() - 1; - auto insignificant_bits = address >> most_significant_bits; - return insignificant_bits == 0 || insignificant_bits == (0xffffffffffffffffull >> most_significant_bits); -} - -CODE_SECTION(".text.safemem") -NEVER_INLINE bool safe_memcpy(void* dest_ptr, void const* src_ptr, size_t n, void*& fault_at) -{ - fault_at = nullptr; - size_t dest = (size_t)dest_ptr; - if (!validate_canonical_address(dest)) { - fault_at = dest_ptr; - return false; - } - size_t src = (size_t)src_ptr; - if (!validate_canonical_address(src)) { - fault_at = const_cast(src_ptr); - return false; - } - size_t remainder; - // FIXME: Support starting at an unaligned address. - if (!(dest & 0x3) && !(src & 0x3) && n >= 12) { - size_t size_ts = n / sizeof(size_t); - asm volatile( - ".globl safe_memcpy_ins_1 \n" - "safe_memcpy_ins_1: \n" - "rep movsq \n" - ".globl safe_memcpy_1_faulted \n" - "safe_memcpy_1_faulted: \n" // handle_safe_access_fault() set edx/rdx to the fault address! - : "=S"(src), - "=D"(dest), - "=c"(remainder), - [fault_at] "=d"(fault_at) - : "S"(src), - "D"(dest), - "c"(size_ts) - : "memory"); - if (remainder != 0) - return false; // fault_at is already set! - n -= size_ts * sizeof(size_t); - if (n == 0) { - fault_at = nullptr; - return true; - } - } - asm volatile( - ".globl safe_memcpy_ins_2 \n" - "safe_memcpy_ins_2: \n" - "rep movsb \n" - ".globl safe_memcpy_2_faulted \n" - "safe_memcpy_2_faulted: \n" // handle_safe_access_fault() set edx/rdx to the fault address! - : "=c"(remainder), - [fault_at] "=d"(fault_at) - : "S"(src), - "D"(dest), - "c"(n) - : "memory"); - if (remainder != 0) - return false; // fault_at is already set! - fault_at = nullptr; - return true; -} - -CODE_SECTION(".text.safemem") -NEVER_INLINE ssize_t safe_strnlen(char const* str, size_t max_n, void*& fault_at) -{ - if (!validate_canonical_address((size_t)str)) { - fault_at = const_cast(str); - return false; - } - ssize_t count = 0; - fault_at = nullptr; - asm volatile( - "1: \n" - "test %[max_n], %[max_n] \n" - "je 2f \n" - "dec %[max_n] \n" - ".globl safe_strnlen_ins \n" - "safe_strnlen_ins: \n" - "cmpb $0,(%[str], %[count], 1) \n" - "je 2f \n" - "inc %[count] \n" - "jmp 1b \n" - ".globl safe_strnlen_faulted \n" - "safe_strnlen_faulted: \n" // handle_safe_access_fault() set edx/rdx to the fault address! - "xor %[count_on_error], %[count_on_error] \n" - "dec %[count_on_error] \n" // return -1 on fault - "2:" - : [count_on_error] "=c"(count), - [fault_at] "=d"(fault_at) - : [str] "b"(str), - [count] "c"(count), - [max_n] "d"(max_n)); - if (count >= 0) - fault_at = nullptr; - return count; -} - -CODE_SECTION(".text.safemem") -NEVER_INLINE bool safe_memset(void* dest_ptr, int c, size_t n, void*& fault_at) -{ - fault_at = nullptr; - size_t dest = (size_t)dest_ptr; - if (!validate_canonical_address(dest)) { - fault_at = dest_ptr; - return false; - } - size_t remainder; - // FIXME: Support starting at an unaligned address. - if (!(dest & 0x3) && n >= 12) { - size_t size_ts = n / sizeof(size_t); - size_t expanded_c = (u8)c; - expanded_c |= expanded_c << 8; - expanded_c |= expanded_c << 16; - asm volatile( - ".globl safe_memset_ins_1 \n" - "safe_memset_ins_1: \n" - "rep stosq \n" - ".globl safe_memset_1_faulted \n" - "safe_memset_1_faulted: \n" // handle_safe_access_fault() set edx/rdx to the fault address! - : "=D"(dest), - "=c"(remainder), - [fault_at] "=d"(fault_at) - : "D"(dest), - "a"(expanded_c), - "c"(size_ts) - : "memory"); - if (remainder != 0) - return false; // fault_at is already set! - n -= size_ts * sizeof(size_t); - if (n == 0) { - fault_at = nullptr; - return true; - } - } - asm volatile( - ".globl safe_memset_ins_2 \n" - "safe_memset_ins_2: \n" - "rep stosb \n" - ".globl safe_memset_2_faulted \n" - "safe_memset_2_faulted: \n" // handle_safe_access_fault() set edx/rdx to the fault address! - : "=D"(dest), - "=c"(remainder), - [fault_at] "=d"(fault_at) - : "D"(dest), - "c"(n), - "a"(c) - : "memory"); - if (remainder != 0) - return false; // fault_at is already set! - fault_at = nullptr; - return true; -} - -CODE_SECTION(".text.safemem.atomic") -NEVER_INLINE Optional safe_atomic_fetch_add_relaxed(u32 volatile* var, u32 val) -{ - u32 result; - bool error; - asm volatile( - "xor %[error], %[error] \n" - ".globl safe_atomic_fetch_add_relaxed_ins \n" - "safe_atomic_fetch_add_relaxed_ins: \n" - "lock xadd %[result], %[var] \n" - ".globl safe_atomic_fetch_add_relaxed_faulted \n" - "safe_atomic_fetch_add_relaxed_faulted: \n" - : [error] "=d"(error), [result] "=a"(result), [var] "=m"(*var) - : [val] "a"(val) - : "memory"); - if (error) - return {}; - return result; -} - -CODE_SECTION(".text.safemem.atomic") -NEVER_INLINE Optional safe_atomic_exchange_relaxed(u32 volatile* var, u32 val) -{ - u32 result; - bool error; - asm volatile( - "xor %[error], %[error] \n" - ".globl safe_atomic_exchange_relaxed_ins \n" - "safe_atomic_exchange_relaxed_ins: \n" - "xchg %[val], %[var] \n" - ".globl safe_atomic_exchange_relaxed_faulted \n" - "safe_atomic_exchange_relaxed_faulted: \n" - : [error] "=d"(error), "=a"(result), [var] "=m"(*var) - : [val] "a"(val) - : "memory"); - if (error) - return {}; - return result; -} - -CODE_SECTION(".text.safemem.atomic") -NEVER_INLINE Optional safe_atomic_load_relaxed(u32 volatile* var) -{ - u32 result; - bool error; - asm volatile( - "xor %[error], %[error] \n" - ".globl safe_atomic_load_relaxed_ins \n" - "safe_atomic_load_relaxed_ins: \n" - "mov (%[var]), %[result] \n" - ".globl safe_atomic_load_relaxed_faulted \n" - "safe_atomic_load_relaxed_faulted: \n" - : [error] "=d"(error), [result] "=c"(result) - : [var] "b"(var) - : "memory"); - if (error) - return {}; - return result; -} - -CODE_SECTION(".text.safemem.atomic") -NEVER_INLINE bool safe_atomic_store_relaxed(u32 volatile* var, u32 val) -{ - bool error; - asm volatile( - "xor %[error], %[error] \n" - ".globl safe_atomic_store_relaxed_ins \n" - "safe_atomic_store_relaxed_ins: \n" - "xchg %[val], %[var] \n" - ".globl safe_atomic_store_relaxed_faulted \n" - "safe_atomic_store_relaxed_faulted: \n" - : [error] "=d"(error), [var] "=m"(*var) - : [val] "r"(val) - : "memory"); - return !error; -} - -CODE_SECTION(".text.safemem.atomic") -NEVER_INLINE Optional safe_atomic_compare_exchange_relaxed(u32 volatile* var, u32& expected, u32 val) -{ - // NOTE: accessing expected is NOT protected as it should always point - // to a valid location in kernel memory! - bool error; - bool did_exchange; - asm volatile( - "xor %[error], %[error] \n" - ".globl safe_atomic_compare_exchange_relaxed_ins \n" - "safe_atomic_compare_exchange_relaxed_ins: \n" - "lock cmpxchg %[val], %[var] \n" - ".globl safe_atomic_compare_exchange_relaxed_faulted \n" - "safe_atomic_compare_exchange_relaxed_faulted: \n" - : [error] "=d"(error), "=a"(expected), [var] "=m"(*var), "=@ccz"(did_exchange) - : "a"(expected), [val] "b"(val) - : "memory"); - if (error) - return {}; - return did_exchange; -} - -bool handle_safe_access_fault(RegisterState& regs, FlatPtr fault_address) -{ - FlatPtr ip = regs.ip(); - ; - if (ip >= (FlatPtr)&start_of_safemem_text && ip < (FlatPtr)&end_of_safemem_text) { - // If we detect that the fault happened in safe_memcpy() safe_strnlen(), - // or safe_memset() then resume at the appropriate _faulted label - if (ip == (FlatPtr)safe_memcpy_ins_1) - ip = (FlatPtr)safe_memcpy_1_faulted; - else if (ip == (FlatPtr)safe_memcpy_ins_2) - ip = (FlatPtr)safe_memcpy_2_faulted; - else if (ip == (FlatPtr)safe_strnlen_ins) - ip = (FlatPtr)safe_strnlen_faulted; - else if (ip == (FlatPtr)safe_memset_ins_1) - ip = (FlatPtr)safe_memset_1_faulted; - else if (ip == (FlatPtr)safe_memset_ins_2) - ip = (FlatPtr)safe_memset_2_faulted; - else - return false; - - regs.set_ip(ip); - regs.set_dx(fault_address); - return true; - } - if (ip >= (FlatPtr)&start_of_safemem_atomic_text && ip < (FlatPtr)&end_of_safemem_atomic_text) { - // If we detect that a fault happened in one of the atomic safe_ - // functions, resume at the appropriate _faulted label and set - // the edx/rdx register to 1 to indicate an error - if (ip == (FlatPtr)safe_atomic_fetch_add_relaxed_ins) - ip = (FlatPtr)safe_atomic_fetch_add_relaxed_faulted; - else if (ip == (FlatPtr)safe_atomic_exchange_relaxed_ins) - ip = (FlatPtr)safe_atomic_exchange_relaxed_faulted; - else if (ip == (FlatPtr)safe_atomic_load_relaxed_ins) - ip = (FlatPtr)safe_atomic_load_relaxed_faulted; - else if (ip == (FlatPtr)safe_atomic_store_relaxed_ins) - ip = (FlatPtr)safe_atomic_store_relaxed_faulted; - else if (ip == (FlatPtr)safe_atomic_compare_exchange_relaxed_ins) - ip = (FlatPtr)safe_atomic_compare_exchange_relaxed_faulted; - else - return false; - - regs.set_ip(ip); - regs.set_dx(1); - return true; - } - return false; -} - -} diff --git a/Kernel/Arch/x86_64/Shutdown.cpp b/Kernel/Arch/x86_64/Shutdown.cpp deleted file mode 100644 index bfc296ede1c..00000000000 --- a/Kernel/Arch/x86_64/Shutdown.cpp +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright (c) 2022, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include - -namespace Kernel { - -void qemu_shutdown() -{ - // Note: This will invoke QEMU Shutdown, but for other platforms (or emulators), - // this has no effect on the system. - // We also try the Bochs/Old QEMU shutdown method, if the first didn't work. - IO::out16(0x604, 0x2000); - IO::out16(0xb004, 0x2000); -} - -void virtualbox_shutdown() -{ - IO::out16(0x4004, 0x3400); -} - -} diff --git a/Kernel/Arch/x86_64/Shutdown.h b/Kernel/Arch/x86_64/Shutdown.h deleted file mode 100644 index 90a742176e2..00000000000 --- a/Kernel/Arch/x86_64/Shutdown.h +++ /dev/null @@ -1,14 +0,0 @@ -/* - * Copyright (c) 2022, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -namespace Kernel { - -void qemu_shutdown(); -void virtualbox_shutdown(); - -} diff --git a/Kernel/Arch/x86_64/SmapDisabler.cpp b/Kernel/Arch/x86_64/SmapDisabler.cpp deleted file mode 100644 index 530254b0c86..00000000000 --- a/Kernel/Arch/x86_64/SmapDisabler.cpp +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright (c) 2018-2021, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include - -#include - -namespace Kernel { - -SmapDisabler::SmapDisabler() - : m_flags(cpu_flags()) -{ - stac(); -} - -SmapDisabler::~SmapDisabler() -{ - if (!(m_flags & 0x40000)) - clac(); -} - -} diff --git a/Kernel/Arch/x86_64/SyscallEntry.cpp b/Kernel/Arch/x86_64/SyscallEntry.cpp deleted file mode 100644 index dfc6fe09e90..00000000000 --- a/Kernel/Arch/x86_64/SyscallEntry.cpp +++ /dev/null @@ -1,92 +0,0 @@ -/* - * Copyright (c) 2021, Owen Smith - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -using namespace Kernel; - -extern "C" void syscall_entry(); -extern "C" NO_SANITIZE_COVERAGE [[gnu::naked]] void syscall_entry() -{ - // clang-format off - asm( - // Store the user stack, then switch to the kernel stack. - " movq %%rsp, %%gs:%c[user_stack] \n" - " movq %%gs:%c[kernel_stack], %%rsp \n" - - // Build RegisterState. - " pushq $0x1b \n" // User ss - " pushq %%gs:%c[user_stack] \n" // User rsp - " sti \n" // It's now safe to enable interrupts, but we can't index into gs after this point - " pushq %%r11 \n" // The CPU preserves the user rflags in r11 - " pushq $0x23 \n" // User cs - " pushq %%rcx \n" // The CPU preserves the user IP in rcx - " pushq $0 \n" - " pushq %%r15 \n" - " pushq %%r14 \n" - " pushq %%r13 \n" - " pushq %%r12 \n" - " pushq %%r11 \n" - " pushq %%r10 \n" - " pushq %%r9 \n" - " pushq %%r8 \n" - " pushq %%rax \n" - " pushq %%rcx \n" - " pushq %%rdx \n" - " pushq %%rbx \n" - " pushq %%rsp \n" - " pushq %%rbp \n" - " pushq %%rsi \n" - " pushq %%rdi \n" - - " pushq %%rsp \n" // TrapFrame::regs - " subq $" __STRINGIFY(TRAP_FRAME_SIZE - 8) ", %%rsp \n" - " movq %%rsp, %%rdi \n" - " call enter_trap_no_irq \n" - " movq %%rsp, %%rdi \n" - " call syscall_handler \n" - " movq %%rsp, %%rdi \n" - " call exit_trap \n" - " addq $" __STRINGIFY(TRAP_FRAME_SIZE) ", %%rsp \n" // Pop TrapFrame - - " popq %%rdi \n" - " popq %%rsi \n" - " popq %%rbp \n" - " addq $8, %%rsp \n" // Skip restoring kernel rsp - " popq %%rbx \n" - " popq %%rdx \n" - " popq %%rcx \n" - " popq %%rax \n" - " popq %%r8 \n" - " popq %%r9 \n" - " popq %%r10 \n" - " popq %%r11 \n" - " popq %%r12 \n" - " popq %%r13 \n" - " popq %%r14 \n" - " popq %%r15 \n" - " addq $8, %%rsp \n" - " popq %%rcx \n" - " addq $16, %%rsp \n" - - // Disable interrupts before we restore the user stack pointer. sysret will re-enable interrupts when it restores - // rflags. - " cli \n" - " popq %%rsp \n" - " sysretq \n" - :: [user_stack] "i"(Kernel::Processor::user_stack_offset()), [kernel_stack] "i"(Kernel::Processor::kernel_stack_offset())); - // clang-format on -} diff --git a/Kernel/Arch/x86_64/TSS.h b/Kernel/Arch/x86_64/TSS.h deleted file mode 100644 index b84cf2d618d..00000000000 --- a/Kernel/Arch/x86_64/TSS.h +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright (c) 2018-2021, Andreas Kling - * Copyright (c) 2021, Leon Albrecht - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include - -#include -VALIDATE_IS_X86() - -namespace Kernel { - -struct [[gnu::packed]] TSS32 { - u16 backlink, __blh; - u32 esp0; - u16 ss0, __ss0h; - u32 esp1; - u16 ss1, __ss1h; - u32 esp2; - u16 ss2, __ss2h; - u32 cr3, eip, eflags; - u32 eax, ecx, edx, ebx, esp, ebp, esi, edi; - u16 es, __esh; - u16 cs, __csh; - u16 ss, __ssh; - u16 ds, __dsh; - u16 fs, __fsh; - u16 gs, __gsh; - u16 ldt, __ldth; - u16 trace, iomapbase; -}; - -struct [[gnu::packed]] TSS64 { - u32 __1; // Link? - u32 rsp0l; - u32 rsp0h; - u32 rsp1l; - u32 rsp1h; - u32 rsp2l; - u32 rsp2h; - u64 __2; // probably CR3 and EIP? - u32 ist1l; - u32 ist1h; - u32 ist2l; - u32 ist2h; - u32 ist3l; - u32 ist3h; - u32 ist4l; - u32 ist4h; - u32 ist5l; - u32 ist5h; - u32 ist6l; - u32 ist6h; - u32 ist7l; - u32 ist7h; - u64 __3; // GS and LDTR? - u16 __4; - u16 iomapbase; -}; - -using TSS = TSS64; - -} diff --git a/Kernel/Arch/x86_64/ThreadRegisters.h b/Kernel/Arch/x86_64/ThreadRegisters.h deleted file mode 100644 index 5ba62fdc84f..00000000000 --- a/Kernel/Arch/x86_64/ThreadRegisters.h +++ /dev/null @@ -1,92 +0,0 @@ -/* - * Copyright (c) 2021, Gunnar Beutner - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include - -namespace Kernel { - -struct ThreadRegisters { - FlatPtr rdi; - FlatPtr rsi; - FlatPtr rbp; - FlatPtr rsp; - FlatPtr rbx; - FlatPtr rdx; - FlatPtr rcx; - FlatPtr rax; - FlatPtr r8; - FlatPtr r9; - FlatPtr r10; - FlatPtr r11; - FlatPtr r12; - FlatPtr r13; - FlatPtr r14; - FlatPtr r15; - FlatPtr rip; - FlatPtr rsp0; - FlatPtr cs; - - FlatPtr rflags; - FlatPtr flags() const { return rflags; } - void set_flags(FlatPtr value) { rflags = value; } - void set_sp(FlatPtr value) { rsp = value; } - void set_sp0(FlatPtr value) { rsp0 = value; } - void set_ip(FlatPtr value) { rip = value; } - - FlatPtr cr3; - - FlatPtr ip() const - { - return rip; - } - - FlatPtr sp() const - { - return rsp; - } - - void set_initial_state(bool is_kernel_process, Memory::AddressSpace& space, FlatPtr kernel_stack_top) - { - // Only IF is set when a process boots. - set_flags(0x0202); - - if (is_kernel_process) - cs = GDT_SELECTOR_CODE0; - else - cs = GDT_SELECTOR_CODE3 | 3; - - cr3 = space.page_directory().cr3(); - - if (is_kernel_process) { - set_sp(kernel_stack_top); - set_sp0(kernel_stack_top); - } else { - // Ring 3 processes get a separate stack for ring 0. - // The ring 3 stack will be assigned by exec(). - set_sp0(kernel_stack_top); - } - } - - void set_entry_function(FlatPtr entry_ip, FlatPtr entry_data) - { - set_ip(entry_ip); - rdi = entry_data; // entry function argument is expected to be in regs.rdi - } - - void set_exec_state(FlatPtr entry_ip, FlatPtr userspace_sp, Memory::AddressSpace& space) - { - cs = GDT_SELECTOR_CODE3 | 3; - rip = entry_ip; - rsp = userspace_sp; - cr3 = space.page_directory().cr3(); - } -}; - -} diff --git a/Kernel/Arch/x86_64/Time/APICTimer.cpp b/Kernel/Arch/x86_64/Time/APICTimer.cpp deleted file mode 100644 index e9b261f87ee..00000000000 --- a/Kernel/Arch/x86_64/Time/APICTimer.cpp +++ /dev/null @@ -1,174 +0,0 @@ -/* - * Copyright (c) 2020, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include - -namespace Kernel { - -#define APIC_TIMER_MEASURE_CPU_CLOCK - -UNMAP_AFTER_INIT APICTimer* APICTimer::initialize(u8 interrupt_number, HardwareTimerBase& calibration_source) -{ - auto timer = adopt_lock_ref(*new APICTimer(interrupt_number, nullptr)); - timer->register_interrupt_handler(); - if (!timer->calibrate(calibration_source)) { - return nullptr; - } - return &timer.leak_ref(); -} - -UNMAP_AFTER_INIT APICTimer::APICTimer(u8 interrupt_number, Function callback) - : HardwareTimer(interrupt_number, move(callback)) -{ - disable_remap(); -} - -UNMAP_AFTER_INIT bool APICTimer::calibrate(HardwareTimerBase& calibration_source) -{ - VERIFY_INTERRUPTS_DISABLED(); - - dmesgln("APICTimer: Using {} as calibration source", calibration_source.model()); - - struct { -#ifdef APIC_TIMER_MEASURE_CPU_CLOCK - bool supports_tsc { Processor::current().has_feature(CPUFeature::TSC) }; -#endif - APIC& apic { APIC::the() }; - size_t ticks_in_100ms { 0 }; - Atomic calibration_ticks { 0 }; -#ifdef APIC_TIMER_MEASURE_CPU_CLOCK - u64 volatile start_tsc { 0 }, end_tsc { 0 }; -#endif - u64 volatile start_reference { 0 }, end_reference { 0 }; - u32 volatile start_apic_count { 0 }, end_apic_count { 0 }; - bool query_reference { false }; - } state; - - state.ticks_in_100ms = calibration_source.ticks_per_second() / 10; - state.query_reference = calibration_source.can_query_raw(); - - // temporarily replace the timer callbacks - auto original_source_callback = calibration_source.set_callback([&state, &calibration_source](RegisterState const&) { - u32 current_timer_count = state.apic.get_timer_current_count(); -#ifdef APIC_TIMER_MEASURE_CPU_CLOCK - u64 current_tsc = state.supports_tsc ? read_tsc() : 0; -#endif - u64 current_reference = state.query_reference ? calibration_source.current_raw() : 0; - - auto prev_tick = state.calibration_ticks.fetch_add(1); - if (prev_tick == 0) { -#ifdef APIC_TIMER_MEASURE_CPU_CLOCK - state.start_tsc = current_tsc; -#endif - state.start_apic_count = current_timer_count; - state.start_reference = current_reference; - } else if (prev_tick + 1 == state.ticks_in_100ms + 1) { -#ifdef APIC_TIMER_MEASURE_CPU_CLOCK - state.end_tsc = current_tsc; -#endif - state.end_apic_count = current_timer_count; - state.end_reference = current_reference; - } - }); - - // Setup a counter that should be much longer than our calibration time. - // We don't want the APIC timer to actually fire. We do however want the - // calbibration_source timer to fire so that we can read the current - // tick count from the APIC timer - auto original_callback = set_callback([&](RegisterState const&) { - // TODO: How should we handle this? - PANIC("APICTimer: Timer fired during calibration!"); - }); - state.apic.setup_local_timer(0xffffffff, APIC::TimerMode::Periodic, true); - - sti(); - // Loop for about 100 ms - while (state.calibration_ticks.load() <= state.ticks_in_100ms) - Processor::wait_check(); - cli(); - - // Restore timer callbacks - calibration_source.set_callback(move(original_source_callback)); - set_callback(move(original_callback)); - - disable_local_timer(); - - if (state.query_reference) { - u64 one_tick_ns = calibration_source.raw_to_ns((state.end_reference - state.start_reference) / state.ticks_in_100ms); - m_frequency = (u32)(1000000000ull / one_tick_ns); - dmesgln("APICTimer: Ticks per second: {} ({}.{}ms)", m_frequency, one_tick_ns / 1000000, one_tick_ns % 1000000); - } else { - // For now, assume the frequency is exactly the same - m_frequency = calibration_source.ticks_per_second(); - dmesgln("APICTimer: Ticks per second: {} (assume same frequency as reference clock)", m_frequency); - } - - auto delta_apic_count = state.start_apic_count - state.end_apic_count; // The APIC current count register decrements! - m_timer_period = (delta_apic_count * state.apic.get_timer_divisor()) / state.ticks_in_100ms; - - u64 apic_freq = delta_apic_count * state.apic.get_timer_divisor() * 10; - dmesgln("APICTimer: Bus clock speed: {}.{} MHz", apic_freq / 1000000, apic_freq % 1000000); - if (apic_freq < 1000000) { - dmesgln("APICTimer: Frequency too slow!"); - return false; - } - -#ifdef APIC_TIMER_MEASURE_CPU_CLOCK - if (state.supports_tsc) { - auto delta_tsc = (state.end_tsc - state.start_tsc) * 10; - dmesgln("APICTimer: CPU clock speed: {}.{} MHz", delta_tsc / 1000000, delta_tsc % 1000000); - } -#endif - - enable_local_timer(); - return true; -} - -void APICTimer::enable_local_timer() -{ - APIC::the().setup_local_timer(m_timer_period, m_timer_mode, true); -} - -void APICTimer::disable_local_timer() -{ - APIC::the().setup_local_timer(0, APIC::TimerMode::OneShot, false); -} - -void APICTimer::set_periodic() -{ - // FIXME: Implement it... - VERIFY_NOT_REACHED(); -} -void APICTimer::set_non_periodic() -{ - // FIXME: Implement it... - VERIFY_NOT_REACHED(); -} - -void APICTimer::reset_to_default_ticks_per_second() -{ -} - -bool APICTimer::try_to_set_frequency([[maybe_unused]] size_t frequency) -{ - return true; -} - -bool APICTimer::is_capable_of_frequency([[maybe_unused]] size_t frequency) const -{ - return false; -} - -size_t APICTimer::calculate_nearest_possible_frequency([[maybe_unused]] size_t frequency) const -{ - return 0; -} - -} diff --git a/Kernel/Arch/x86_64/Time/APICTimer.h b/Kernel/Arch/x86_64/Time/APICTimer.h deleted file mode 100644 index 842c910ad77..00000000000 --- a/Kernel/Arch/x86_64/Time/APICTimer.h +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright (c) 2020, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include - -namespace Kernel { - -class APICTimer final : public HardwareTimer { -public: - static APICTimer* initialize(u8, HardwareTimerBase&); - virtual HardwareTimerType timer_type() const override { return HardwareTimerType::LocalAPICTimer; } - virtual StringView model() const override { return "LocalAPIC"sv; } - - virtual bool is_periodic() const override { return m_timer_mode == APIC::TimerMode::Periodic; } - virtual bool is_periodic_capable() const override { return true; } - virtual void set_periodic() override; - virtual void set_non_periodic() override; - virtual void disable() override { } - - virtual void reset_to_default_ticks_per_second() override; - virtual bool try_to_set_frequency(size_t frequency) override; - virtual bool is_capable_of_frequency(size_t frequency) const override; - virtual size_t calculate_nearest_possible_frequency(size_t frequency) const override; - - void will_be_destroyed() override { HardwareTimer::will_be_destroyed(); } - void enable_local_timer(); - void disable_local_timer(); - -private: - explicit APICTimer(u8, Function); - - bool calibrate(HardwareTimerBase&); - - u32 m_timer_period { 0 }; - APIC::TimerMode m_timer_mode { APIC::TimerMode::Periodic }; -}; - -} diff --git a/Kernel/Arch/x86_64/Time/HPET.cpp b/Kernel/Arch/x86_64/Time/HPET.cpp deleted file mode 100644 index 43353eee1d7..00000000000 --- a/Kernel/Arch/x86_64/Time/HPET.cpp +++ /dev/null @@ -1,462 +0,0 @@ -/* - * Copyright (c) 2020, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace Kernel { - -#define ABSOLUTE_MAXIMUM_COUNTER_TICK_PERIOD 0x05F5E100 -#define NANOSECOND_PERIOD_TO_HERTZ(x) 1000000000 / x -#define HERTZ_TO_MEGAHERTZ(x) (x / 1000000) - -namespace HPETFlags { -enum class Attributes { - Counter64BitCapable = 1 << 13, - LegacyReplacementRouteCapable = 1 << 15 -}; - -enum class Configuration { - Enable = 1 << 0, - LegacyReplacementRoute = 1 << 1 -}; - -enum class TimerConfiguration : u32 { - LevelTriggered = 1 << 1, - InterruptEnable = 1 << 2, - GeneratePeriodicInterrupt = 1 << 3, - PeriodicInterruptCapable = 1 << 4, - Timer64BitsCapable = 1 << 5, - ValueSet = 1 << 6, - Force32BitMode = 1 << 8, - FSBInterruptEnable = 1 << 14, - FSBInterruptDelivery = 1 << 15 -}; -}; - -struct [[gnu::packed]] HPETRegister { - union { - u64 volatile full; - struct { - u32 volatile low; - u32 volatile high; - }; - }; -}; - -struct [[gnu::packed]] TimerStructure { - u32 volatile capabilities; - u32 volatile interrupt_routing; - HPETRegister comparator_value; - u64 volatile fsb_interrupt_route; - u64 reserved; -}; - -struct [[gnu::packed]] HPETCapabilityRegister { - // Note: We must do a 32 bit access to offsets 0x0, or 0x4 only, according to HPET spec. - u32 volatile attributes; - u32 volatile main_counter_tick_period; - u64 reserved; -}; - -struct [[gnu::packed]] HPETRegistersBlock { - HPETCapabilityRegister capabilities; - HPETRegister configuration; - u64 reserved1; - HPETRegister interrupt_status; - u8 reserved2[0xF0 - 0x28]; - HPETRegister main_counter_value; - u64 reserved3; - TimerStructure timers[32]; -}; - -static_assert(__builtin_offsetof(HPETRegistersBlock, main_counter_value) == 0xf0); -static_assert(__builtin_offsetof(HPETRegistersBlock, timers[0]) == 0x100); -static_assert(__builtin_offsetof(HPETRegistersBlock, timers[1]) == 0x120); - -// Note: The HPET specification says it reserves the range of byte 0x160 to -// 0x400 for comparators 3-31, but for implementing all 32 comparators the HPET -// MMIO space has to be 1280 bytes and not 1024 bytes. -static_assert(AssertSize()); - -static u64 read_register_safe64(HPETRegister const& reg) -{ - return reg.full; -} - -static HPET* s_hpet; -static bool hpet_initialized { false }; - -bool HPET::initialized() -{ - return hpet_initialized; -} - -HPET& HPET::the() -{ - VERIFY(HPET::initialized()); - VERIFY(s_hpet != nullptr); - return *s_hpet; -} - -UNMAP_AFTER_INIT bool HPET::test_and_initialize() -{ - VERIFY(!HPET::initialized()); - hpet_initialized = true; - auto hpet_table = ACPI::Parser::the()->find_table("HPET"sv); - if (!hpet_table.has_value()) - return false; - dmesgln("HPET @ {}", hpet_table.value()); - - auto sdt_or_error = Memory::map_typed(hpet_table.value()); - if (sdt_or_error.is_error()) { - dbgln("Failed mapping HPET table"); - return false; - } - - // Note: HPET is only usable from System Memory - VERIFY(sdt_or_error.value()->event_timer_block.address_space == (u8)ACPI::GenericAddressStructure::AddressSpace::SystemMemory); - - if (TimeManagement::is_hpet_periodic_mode_allowed()) { - if (!check_for_exisiting_periodic_timers()) { - dbgln("HPET: No periodic capable timers"); - return false; - } - } - new HPET(PhysicalAddress(hpet_table.value())); - return true; -} - -UNMAP_AFTER_INIT bool HPET::check_for_exisiting_periodic_timers() -{ - auto hpet_table = ACPI::Parser::the()->find_table("HPET"sv); - if (!hpet_table.has_value()) - return false; - - auto sdt_or_error = Memory::map_typed(hpet_table.value()); - if (sdt_or_error.is_error()) - return false; - auto sdt = sdt_or_error.release_value(); - VERIFY(sdt->event_timer_block.address_space == 0); - auto registers_or_error = Memory::map_typed(PhysicalAddress(sdt->event_timer_block.address)); - if (registers_or_error.is_error()) - return false; - auto registers = registers_or_error.release_value(); - - size_t timers_count = ((registers->capabilities.attributes >> 8) & 0x1f) + 1; - for (size_t index = 0; index < timers_count; index++) { - if (registers->timers[index].capabilities & (u32)HPETFlags::TimerConfiguration::PeriodicInterruptCapable) - return true; - } - return false; -} - -void HPET::global_disable() -{ - auto& regs = registers(); - regs.configuration.low = regs.configuration.low & ~(u32)HPETFlags::Configuration::Enable; -} -void HPET::global_enable() -{ - auto& regs = registers(); - regs.configuration.low = regs.configuration.low | (u32)HPETFlags::Configuration::Enable; -} - -void HPET::update_periodic_comparator_value() -{ - // According to 2.3.9.2.2 the only safe way to change the periodic timer frequency - // is to disable all periodic timers, reset the main counter and each timer's comparator value. - // This introduces time drift, so it should be avoided unless absolutely necessary. - global_disable(); - auto& regs = registers(); - - u64 previous_main_value = (u64)regs.main_counter_value.low | ((u64)regs.main_counter_value.high << 32); - m_main_counter_drift += previous_main_value - m_main_counter_last_read; - m_main_counter_last_read = 0; - regs.main_counter_value.low = 0; - if (m_main_counter_64bits) - regs.main_counter_value.high = 0; - for (auto& comparator : m_comparators) { - auto& timer = regs.timers[comparator->comparator_number()]; - if (!comparator->is_enabled()) - continue; - if (comparator->is_periodic()) { - // Note that this means we're restarting all periodic timers. There is no - // way to resume periodic timers properly because we reset the main counter - // and we can only write the period into the comparator value... - timer.capabilities = timer.capabilities | (u32)HPETFlags::TimerConfiguration::ValueSet; - u64 value = ns_to_raw_counter_ticks(1000000000ull / comparator->ticks_per_second()); - dbgln_if(HPET_DEBUG, "HPET: Update periodic comparator {} comparator value to {} main value was: {}", - comparator->comparator_number(), - value, - previous_main_value); - timer.comparator_value.low = (u32)value; - if (comparator->is_64bit_capable()) { - timer.capabilities = timer.capabilities | (u32)HPETFlags::TimerConfiguration::ValueSet; - timer.comparator_value.high = (u32)(value >> 32); - } - } else { - // Set the new target comparator value to the delta to the remaining ticks - u64 current_value = (u64)timer.comparator_value.low | ((u64)timer.comparator_value.high << 32); - u64 value = current_value - previous_main_value; - dbgln_if(HPET_DEBUG, "HPET: Update non-periodic comparator {} comparator value from {} to {} main value was: {}", - comparator->comparator_number(), - current_value, - value, - previous_main_value); - timer.comparator_value.low = (u32)value; - if (comparator->is_64bit_capable()) - timer.comparator_value.high = (u32)(value >> 32); - } - } - - global_enable(); -} - -void HPET::update_non_periodic_comparator_value(HPETComparator const& comparator) -{ - VERIFY_INTERRUPTS_DISABLED(); - VERIFY(!comparator.is_periodic()); - VERIFY(comparator.comparator_number() <= m_comparators.size()); - auto& regs = registers(); - auto& timer = regs.timers[comparator.comparator_number()]; - u64 value = frequency() / comparator.ticks_per_second(); - // NOTE: If the main counter passes this new value before we finish writing it, we will never receive an interrupt! - u64 new_counter_value = read_main_counter() + value; - timer.comparator_value.high = (u32)(new_counter_value >> 32); - timer.comparator_value.low = (u32)new_counter_value; -} - -u64 HPET::update_time(u64& seconds_since_boot, u32& ticks_this_second, bool query_only) -{ - // Should only be called by the time keeper interrupt handler! - u64 current_value = read_main_counter(); - u64 delta_ticks = m_main_counter_drift; - if (current_value >= m_main_counter_last_read) { - delta_ticks += current_value - m_main_counter_last_read; - } else { - // the counter wrapped around - if (m_main_counter_64bits) { - delta_ticks += (NumericLimits::max() - m_main_counter_last_read + 1) + current_value; - } else { - delta_ticks += (NumericLimits::max() - m_main_counter_last_read + 1) + current_value; - m_32bit_main_counter_wraps++; - } - } - - u64 ticks_since_last_second = (u64)ticks_this_second + delta_ticks; - auto ticks_per_second = frequency(); - seconds_since_boot += ticks_since_last_second / ticks_per_second; - ticks_this_second = ticks_since_last_second % ticks_per_second; - - if (!query_only) { - m_main_counter_drift = 0; - m_main_counter_last_read = current_value; - } - - // Return the time passed (in ns) since last time update_time was called - return (delta_ticks * 1000000000ull) / ticks_per_second; -} - -u64 HPET::read_main_counter_unsafe() const -{ - auto& main_counter = registers().main_counter_value; - if (m_main_counter_64bits) - return ((u64)main_counter.high << 32) | (u64)main_counter.low; - - return ((u64)m_32bit_main_counter_wraps << 32) | (u64)main_counter.low; -} - -u64 HPET::read_main_counter() const -{ - if (m_main_counter_64bits) - return read_register_safe64(registers().main_counter_value); - - auto& main_counter = registers().main_counter_value; - u32 wraps = m_32bit_main_counter_wraps; - u32 last_read_value = m_main_counter_last_read & 0xffffffff; - u32 current_value = main_counter.low; - if (current_value < last_read_value) - wraps++; - return ((u64)wraps << 32) | (u64)current_value; -} - -void HPET::enable_periodic_interrupt(HPETComparator const& comparator) -{ - dbgln_if(HPET_DEBUG, "HPET: Set comparator {} to be periodic.", comparator.comparator_number()); - disable(comparator); - VERIFY(comparator.comparator_number() <= m_comparators.size()); - auto& timer = registers().timers[comparator.comparator_number()]; - auto capabilities = timer.capabilities; - VERIFY(capabilities & (u32)HPETFlags::TimerConfiguration::PeriodicInterruptCapable); - timer.capabilities = capabilities | (u32)HPETFlags::TimerConfiguration::GeneratePeriodicInterrupt; - if (comparator.is_enabled()) - enable(comparator); -} -void HPET::disable_periodic_interrupt(HPETComparator const& comparator) -{ - dbgln_if(HPET_DEBUG, "HPET: Disable periodic interrupt in comparator {}", comparator.comparator_number()); - disable(comparator); - VERIFY(comparator.comparator_number() <= m_comparators.size()); - auto& timer = registers().timers[comparator.comparator_number()]; - auto capabilities = timer.capabilities; - VERIFY(capabilities & (u32)HPETFlags::TimerConfiguration::PeriodicInterruptCapable); - timer.capabilities = capabilities & ~(u32)HPETFlags::TimerConfiguration::GeneratePeriodicInterrupt; - if (comparator.is_enabled()) - enable(comparator); -} - -void HPET::disable(HPETComparator const& comparator) -{ - dbgln_if(HPET_DEBUG, "HPET: Disable comparator {}", comparator.comparator_number()); - VERIFY(comparator.comparator_number() <= m_comparators.size()); - auto& timer = registers().timers[comparator.comparator_number()]; - timer.capabilities = timer.capabilities & ~(u32)HPETFlags::TimerConfiguration::InterruptEnable; -} -void HPET::enable(HPETComparator const& comparator) -{ - dbgln_if(HPET_DEBUG, "HPET: Enable comparator {}", comparator.comparator_number()); - VERIFY(comparator.comparator_number() <= m_comparators.size()); - auto& timer = registers().timers[comparator.comparator_number()]; - timer.capabilities = timer.capabilities | (u32)HPETFlags::TimerConfiguration::InterruptEnable; -} - -Vector HPET::capable_interrupt_numbers(HPETComparator const& comparator) -{ - VERIFY(comparator.comparator_number() <= m_comparators.size()); - Vector capable_interrupts; - auto& comparator_registers = registers().timers[comparator.comparator_number()]; - u32 interrupt_bitfield = comparator_registers.interrupt_routing; - for (size_t index = 0; index < 32; index++) { - if (interrupt_bitfield & 1) - capable_interrupts.append(index); - interrupt_bitfield >>= 1; - } - return capable_interrupts; -} - -Vector HPET::capable_interrupt_numbers(u8 comparator_number) -{ - VERIFY(comparator_number <= m_comparators.size()); - Vector capable_interrupts; - auto& comparator_registers = registers().timers[comparator_number]; - u32 interrupt_bitfield = comparator_registers.interrupt_routing; - for (size_t index = 0; index < 32; index++) { - if (interrupt_bitfield & 1) - capable_interrupts.append(index); - interrupt_bitfield >>= 1; - } - return capable_interrupts; -} - -void HPET::set_comparator_irq_vector(u8 comparator_number, u8 irq_vector) -{ - VERIFY(comparator_number <= m_comparators.size()); - auto& comparator_registers = registers().timers[comparator_number]; - comparator_registers.capabilities = comparator_registers.capabilities | (irq_vector << 9); -} - -bool HPET::is_periodic_capable(u8 comparator_number) const -{ - VERIFY(comparator_number <= m_comparators.size()); - auto& comparator_registers = registers().timers[comparator_number]; - return comparator_registers.capabilities & (u32)HPETFlags::TimerConfiguration::PeriodicInterruptCapable; -} - -bool HPET::is_64bit_capable(u8 comparator_number) const -{ - VERIFY(comparator_number <= m_comparators.size()); - auto& comparator_registers = registers().timers[comparator_number]; - return comparator_registers.capabilities & (u32)HPETFlags::TimerConfiguration::Timer64BitsCapable; -} - -void HPET::set_comparators_to_optimal_interrupt_state(size_t) -{ - // FIXME: Implement this method for allowing to use HPET timers 2-31... - VERIFY_NOT_REACHED(); -} - -PhysicalAddress HPET::find_acpi_hpet_registers_block() -{ - auto sdt = Memory::map_typed(m_physical_acpi_hpet_table).release_value_but_fixme_should_propagate_errors(); - VERIFY(sdt->event_timer_block.address_space == (u8)ACPI::GenericAddressStructure::AddressSpace::SystemMemory); - return PhysicalAddress(sdt->event_timer_block.address); -} - -HPETRegistersBlock const& HPET::registers() const -{ - return *(HPETRegistersBlock const*)m_hpet_mmio_region->vaddr().offset(m_physical_acpi_hpet_registers.offset_in_page()).as_ptr(); -} - -HPETRegistersBlock& HPET::registers() -{ - return *(HPETRegistersBlock*)m_hpet_mmio_region->vaddr().offset(m_physical_acpi_hpet_registers.offset_in_page()).as_ptr(); -} - -u64 HPET::raw_counter_ticks_to_ns(u64 raw_ticks) const -{ - // ABSOLUTE_MAXIMUM_COUNTER_TICK_PERIOD == 100 nanoseconds - return (raw_ticks * (u64)registers().capabilities.main_counter_tick_period * 100ull) / ABSOLUTE_MAXIMUM_COUNTER_TICK_PERIOD; -} - -u64 HPET::ns_to_raw_counter_ticks(u64 ns) const -{ - return (ns * 1000000ull) / (u64)registers().capabilities.main_counter_tick_period; -} - -UNMAP_AFTER_INIT HPET::HPET(PhysicalAddress acpi_hpet) - : m_physical_acpi_hpet_table(acpi_hpet) - , m_physical_acpi_hpet_registers(find_acpi_hpet_registers_block()) - , m_hpet_mmio_region(MM.allocate_mmio_kernel_region(m_physical_acpi_hpet_registers.page_base(), PAGE_SIZE, "HPET MMIO"sv, Memory::Region::Access::ReadWrite).release_value()) -{ - s_hpet = this; // Make available as soon as possible so that IRQs can use it - - auto sdt = Memory::map_typed(m_physical_acpi_hpet_table).release_value_but_fixme_should_propagate_errors(); - m_vendor_id = sdt->pci_vendor_id; - m_minimum_tick = sdt->mininum_clock_tick; - dmesgln("HPET: Minimum clock tick - {}", m_minimum_tick); - - auto& regs = registers(); - - // Note: We must do a 32 bit access to offsets 0x0, or 0x4 only. - size_t timers_count = ((regs.capabilities.attributes >> 8) & 0x1f) + 1; - m_main_counter_64bits = (regs.capabilities.attributes & (u32)HPETFlags::Attributes::Counter64BitCapable) != 0; - dmesgln("HPET: Timers count - {}", timers_count); - dmesgln("HPET: Main counter size: {}", (m_main_counter_64bits ? "64-bit" : "32-bit")); - for (size_t i = 0; i < timers_count; i++) { - bool capable_64_bit = regs.timers[i].capabilities & (u32)HPETFlags::TimerConfiguration::Timer64BitsCapable; - dmesgln("HPET: Timer[{}] comparator size: {}, mode: {}", i, - (capable_64_bit ? "64-bit" : "32-bit"), - ((!capable_64_bit || (regs.timers[i].capabilities & (u32)HPETFlags::TimerConfiguration::Force32BitMode)) ? "32-bit" : "64-bit")); - } - VERIFY(timers_count >= 2); - - global_disable(); - - m_frequency = NANOSECOND_PERIOD_TO_HERTZ(raw_counter_ticks_to_ns(1)); - dmesgln("HPET: frequency {} Hz ({} MHz) resolution: {} ns", m_frequency, HERTZ_TO_MEGAHERTZ(m_frequency), raw_counter_ticks_to_ns(1)); - - VERIFY(regs.capabilities.main_counter_tick_period <= ABSOLUTE_MAXIMUM_COUNTER_TICK_PERIOD); - - // Reset the counter, just in case... (needs to match m_main_counter_last_read) - regs.main_counter_value.high = 0; - regs.main_counter_value.low = 0; - if (regs.capabilities.attributes & (u32)HPETFlags::Attributes::LegacyReplacementRouteCapable) - regs.configuration.low = regs.configuration.low | (u32)HPETFlags::Configuration::LegacyReplacementRoute; - - m_comparators.append(HPETComparator::create(0, 0, is_periodic_capable(0), is_64bit_capable(0))); - m_comparators.append(HPETComparator::create(1, 8, is_periodic_capable(1), is_64bit_capable(1))); - - global_enable(); -} -} diff --git a/Kernel/Arch/x86_64/Time/HPET.h b/Kernel/Arch/x86_64/Time/HPET.h deleted file mode 100644 index 5d2d3479640..00000000000 --- a/Kernel/Arch/x86_64/Time/HPET.h +++ /dev/null @@ -1,80 +0,0 @@ -/* - * Copyright (c) 2020, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include - -namespace Kernel { - -class HPETComparator; -struct HPETRegistersBlock; - -class HPET { -public: - static bool initialized(); - static bool test_and_initialize(); - static bool check_for_exisiting_periodic_timers(); - static HPET& the(); - - u64 frequency() const { return m_frequency; } - u64 raw_counter_ticks_to_ns(u64) const; - u64 ns_to_raw_counter_ticks(u64) const; - - Vector> const& comparators() const { return m_comparators; } - void disable(HPETComparator const&); - void enable(HPETComparator const&); - - void update_periodic_comparator_value(); - void update_non_periodic_comparator_value(HPETComparator const& comparator); - - void set_comparator_irq_vector(u8 comparator_number, u8 irq_vector); - - void enable_periodic_interrupt(HPETComparator const& comparator); - void disable_periodic_interrupt(HPETComparator const& comparator); - - u64 update_time(u64& seconds_since_boot, u32& ticks_this_second, bool query_only); - u64 read_main_counter_unsafe() const; - u64 read_main_counter() const; - - Vector capable_interrupt_numbers(u8 comparator_number); - Vector capable_interrupt_numbers(HPETComparator const&); - -private: - HPETRegistersBlock const& registers() const; - HPETRegistersBlock& registers(); - - void global_disable(); - void global_enable(); - - bool is_periodic_capable(u8 comparator_number) const; - bool is_64bit_capable(u8 comparator_number) const; - void set_comparators_to_optimal_interrupt_state(size_t timers_count); - - PhysicalAddress find_acpi_hpet_registers_block(); - explicit HPET(PhysicalAddress acpi_hpet); - PhysicalAddress m_physical_acpi_hpet_table; - PhysicalAddress m_physical_acpi_hpet_registers; - OwnPtr m_hpet_mmio_region; - - u64 m_main_counter_last_read { 0 }; - u64 m_main_counter_drift { 0 }; - u32 m_32bit_main_counter_wraps { 0 }; - - u16 m_vendor_id; - u16 m_minimum_tick; - u64 m_frequency; - u8 m_revision_id; - bool m_main_counter_64bits : 1; - bool legacy_replacement_route_capable : 1; - - Vector> m_comparators; -}; -} diff --git a/Kernel/Arch/x86_64/Time/HPETComparator.cpp b/Kernel/Arch/x86_64/Time/HPETComparator.cpp deleted file mode 100644 index 0c898ce322e..00000000000 --- a/Kernel/Arch/x86_64/Time/HPETComparator.cpp +++ /dev/null @@ -1,131 +0,0 @@ -/* - * Copyright (c) 2020, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include -#include - -namespace Kernel { - -UNMAP_AFTER_INIT NonnullLockRefPtr HPETComparator::create(u8 number, u8 irq, bool periodic_capable, bool is_64bit_capable) -{ - auto timer = adopt_lock_ref(*new HPETComparator(number, irq, periodic_capable, is_64bit_capable)); - timer->register_interrupt_handler(); - return timer; -} - -UNMAP_AFTER_INIT HPETComparator::HPETComparator(u8 number, u8 irq, bool periodic_capable, bool is_64bit_capable) - : HardwareTimer(irq) - , m_periodic(false) - , m_periodic_capable(periodic_capable) - , m_enabled(false) - , m_is_64bit_capable(is_64bit_capable) - , m_comparator_number(number) -{ -} - -void HPETComparator::disable() -{ - if (!m_enabled) - return; - m_enabled = false; - HPET::the().disable(*this); -} - -void HPETComparator::set_periodic() -{ - VERIFY(m_periodic_capable); - m_periodic = true; - m_enabled = true; - HPET::the().enable_periodic_interrupt(*this); -} -void HPETComparator::set_non_periodic() -{ - VERIFY(m_periodic_capable); - m_periodic = false; - m_enabled = true; - HPET::the().disable_periodic_interrupt(*this); -} - -bool HPETComparator::handle_irq(RegisterState const& regs) -{ - auto result = HardwareTimer::handle_irq(regs); - if (!is_periodic()) - set_new_countdown(); - return result; -} - -void HPETComparator::set_new_countdown() -{ - VERIFY_INTERRUPTS_DISABLED(); - VERIFY(m_frequency <= HPET::the().frequency()); - HPET::the().update_non_periodic_comparator_value(*this); -} - -void HPETComparator::reset_to_default_ticks_per_second() -{ - dbgln("reset_to_default_ticks_per_second"); - m_frequency = OPTIMAL_TICKS_PER_SECOND_RATE; - if (!is_periodic()) - set_new_countdown(); - else - try_to_set_frequency(m_frequency); -} -bool HPETComparator::try_to_set_frequency(size_t frequency) -{ - InterruptDisabler disabler; - if (!is_capable_of_frequency(frequency)) { - dbgln("HPETComparator: not capable of frequency: {}", frequency); - return false; - } - - auto hpet_frequency = HPET::the().frequency(); - VERIFY(frequency <= hpet_frequency); - m_frequency = frequency; - m_enabled = true; - - dbgln_if(HPET_COMPARATOR_DEBUG, "HPET Comparator: Max frequency {} Hz, want to set {} Hz, periodic: {}", hpet_frequency, frequency, is_periodic()); - - if (is_periodic()) { - HPET::the().update_periodic_comparator_value(); - } else { - HPET::the().update_non_periodic_comparator_value(*this); - } - HPET::the().enable(*this); - enable_irq(); // Enable if we haven't already - return true; -} -bool HPETComparator::is_capable_of_frequency(size_t frequency) const -{ - if (frequency > HPET::the().frequency()) - return false; - // HPET::update_periodic_comparator_value and HPET::update_non_periodic_comparator_value - // calculate the best counter based on the desired frequency. - return true; -} -size_t HPETComparator::calculate_nearest_possible_frequency(size_t frequency) const -{ - if (frequency > HPET::the().frequency()) - return HPET::the().frequency(); - // HPET::update_periodic_comparator_value and HPET::update_non_periodic_comparator_value - // calculate the best counter based on the desired frequency. - return frequency; -} - -u64 HPETComparator::current_raw() const -{ - return HPET::the().read_main_counter(); -} - -u64 HPETComparator::raw_to_ns(u64 raw_delta) const -{ - return HPET::the().raw_counter_ticks_to_ns(raw_delta); -} - -} diff --git a/Kernel/Arch/x86_64/Time/HPETComparator.h b/Kernel/Arch/x86_64/Time/HPETComparator.h deleted file mode 100644 index 9e3fc48cb76..00000000000 --- a/Kernel/Arch/x86_64/Time/HPETComparator.h +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright (c) 2020, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include - -namespace Kernel { -class HPETComparator final : public HardwareTimer { - friend class HPET; - -public: - static NonnullLockRefPtr create(u8 number, u8 irq, bool periodic_capable, bool is_64bit_capable); - - virtual HardwareTimerType timer_type() const override { return HardwareTimerType::HighPrecisionEventTimer; } - virtual StringView model() const override { return "HPET"sv; } - - u8 comparator_number() const { return m_comparator_number; } - bool is_enabled() const { return m_enabled; } - bool is_64bit_capable() const { return m_is_64bit_capable; } - - virtual bool is_periodic() const override { return m_periodic; } - virtual bool is_periodic_capable() const override { return m_periodic_capable; } - virtual void set_periodic() override; - virtual void set_non_periodic() override; - virtual void disable() override; - virtual bool can_query_raw() const override { return true; } - virtual u64 current_raw() const override; - virtual u64 raw_to_ns(u64) const override; - - virtual void reset_to_default_ticks_per_second() override; - virtual bool try_to_set_frequency(size_t frequency) override; - virtual bool is_capable_of_frequency(size_t frequency) const override; - virtual size_t calculate_nearest_possible_frequency(size_t frequency) const override; - -private: - void set_new_countdown(); - virtual bool handle_irq(RegisterState const&) override; - HPETComparator(u8 number, u8 irq, bool periodic_capable, bool is_64bit_capable); - bool m_periodic : 1; - bool m_periodic_capable : 1; - bool m_enabled : 1; - bool m_is_64bit_capable : 1; - u8 m_comparator_number { 0 }; -}; -} diff --git a/Kernel/Arch/x86_64/Time/PIT.cpp b/Kernel/Arch/x86_64/Time/PIT.cpp deleted file mode 100644 index 340af103bdb..00000000000 --- a/Kernel/Arch/x86_64/Time/PIT.cpp +++ /dev/null @@ -1,84 +0,0 @@ -/* - * Copyright (c) 2020, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -#define IRQ_TIMER 0 -namespace Kernel { - -UNMAP_AFTER_INIT NonnullLockRefPtr PIT::initialize(Function callback) -{ - return adopt_lock_ref(*new PIT(move(callback))); -} - -[[maybe_unused]] inline static void reset_countdown(u16 timer_reload) -{ - IO::out8(PIT_CTL, TIMER0_SELECT | WRITE_WORD | MODE_COUNTDOWN); - IO::out8(TIMER0_CTL, LSB(timer_reload)); - IO::out8(TIMER0_CTL, MSB(timer_reload)); -} - -PIT::PIT(Function callback) - : HardwareTimer(IRQ_TIMER, move(callback)) - , m_periodic(true) -{ - IO::out8(PIT_CTL, TIMER0_SELECT | WRITE_WORD | MODE_SQUARE_WAVE); - - dmesgln("PIT: {} Hz, square wave ({:#08x})", OPTIMAL_TICKS_PER_SECOND_RATE, BASE_FREQUENCY / OPTIMAL_TICKS_PER_SECOND_RATE); - reset_to_default_ticks_per_second(); - enable_irq(); -} - -void PIT::set_periodic() -{ - IO::out8(PIT_CTL, TIMER0_CTL | WRITE_WORD | MODE_SQUARE_WAVE); - m_periodic = true; -} -void PIT::set_non_periodic() -{ - IO::out8(PIT_CTL, TIMER0_CTL | WRITE_WORD | MODE_ONESHOT); - m_periodic = false; -} - -void PIT::reset_to_default_ticks_per_second() -{ - InterruptDisabler disabler; - bool success = try_to_set_frequency(OPTIMAL_TICKS_PER_SECOND_RATE); - VERIFY(success); -} - -bool PIT::try_to_set_frequency(size_t frequency) -{ - InterruptDisabler disabler; - if (!is_capable_of_frequency(frequency)) - return false; - disable_irq(); - size_t reload_value = BASE_FREQUENCY / frequency; - IO::out8(TIMER0_CTL, LSB(reload_value)); - IO::out8(TIMER0_CTL, MSB(reload_value)); - m_frequency = frequency; - enable_irq(); - return true; -} -bool PIT::is_capable_of_frequency(size_t frequency) const -{ - VERIFY(frequency != 0); - return frequency <= BASE_FREQUENCY; -} -size_t PIT::calculate_nearest_possible_frequency(size_t frequency) const -{ - VERIFY(frequency != 0); - return frequency; -} - -} diff --git a/Kernel/Arch/x86_64/Time/PIT.h b/Kernel/Arch/x86_64/Time/PIT.h deleted file mode 100644 index 5b1feade0d7..00000000000 --- a/Kernel/Arch/x86_64/Time/PIT.h +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright (c) 2020, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include - -namespace Kernel { - -/* Timer related ports */ -#define TIMER0_CTL 0x40 -#define TIMER1_CTL 0x41 -#define TIMER2_CTL 0x42 -#define PIT_CTL 0x43 - -/* Building blocks for PIT_CTL */ -#define TIMER0_SELECT 0x00 -#define TIMER1_SELECT 0x40 -#define TIMER2_SELECT 0x80 - -#define MODE_COUNTDOWN 0x00 -#define MODE_ONESHOT 0x02 -#define MODE_RATE 0x04 -#define MODE_SQUARE_WAVE 0x06 - -#define WRITE_WORD 0x30 - -#define BASE_FREQUENCY 1193182 - -class PIT final : public HardwareTimer { -public: - static NonnullLockRefPtr initialize(Function); - virtual HardwareTimerType timer_type() const override { return HardwareTimerType::i8253; } - virtual StringView model() const override { return "i8254"sv; } - - virtual bool is_periodic() const override { return m_periodic; } - virtual bool is_periodic_capable() const override { return true; } - virtual void set_periodic() override; - virtual void set_non_periodic() override; - virtual void disable() override { } - - virtual void reset_to_default_ticks_per_second() override; - virtual bool try_to_set_frequency(size_t frequency) override; - virtual bool is_capable_of_frequency(size_t frequency) const override; - virtual size_t calculate_nearest_possible_frequency(size_t frequency) const override; - -private: - explicit PIT(Function); - bool m_periodic { true }; -}; -} diff --git a/Kernel/Arch/x86_64/Time/RTC.cpp b/Kernel/Arch/x86_64/Time/RTC.cpp deleted file mode 100644 index 97e47e26fe4..00000000000 --- a/Kernel/Arch/x86_64/Time/RTC.cpp +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Copyright (c) 2020, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include -#include - -namespace Kernel { -#define IRQ_TIMER 8 -#define MAX_FREQUENCY 8000 - -NonnullLockRefPtr RealTimeClock::create(Function callback) -{ - return adopt_lock_ref(*new RealTimeClock(move(callback))); -} -RealTimeClock::RealTimeClock(Function callback) - : HardwareTimer(IRQ_TIMER, move(callback)) -{ - InterruptDisabler disabler; - NonMaskableInterruptDisabler nmi_disabler; - enable_irq(); - CMOS::write(0x8B, CMOS::read(0xB) | 0x40); - reset_to_default_ticks_per_second(); -} -bool RealTimeClock::handle_irq(RegisterState const& regs) -{ - auto result = HardwareTimer::handle_irq(regs); - CMOS::read(0x8C); - return result; -} - -void RealTimeClock::reset_to_default_ticks_per_second() -{ - InterruptDisabler disabler; - bool success = try_to_set_frequency(1024); - VERIFY(success); -} - -// FIXME: This is a quick & dirty log base 2 with a parameter. Please provide something better in the future. -static int quick_log2(size_t number) -{ - int count = 0; - while (number >>= 1) - count++; - return count; -} - -bool RealTimeClock::try_to_set_frequency(size_t frequency) -{ - InterruptDisabler disabler; - if (!is_capable_of_frequency(frequency)) - return false; - disable_irq(); - u8 previous_rate = CMOS::read(0x8A); - u8 rate = quick_log2(32768 / frequency) + 1; - dbgln("RTC: Set rate to {}", rate); - CMOS::write(0x8A, (previous_rate & 0xF0) | rate); - m_frequency = frequency; - dbgln("RTC: Set frequency to {} Hz", frequency); - enable_irq(); - return true; -} -bool RealTimeClock::is_capable_of_frequency(size_t frequency) const -{ - VERIFY(frequency != 0); - if (frequency > MAX_FREQUENCY) - return false; - if (32768 % frequency) - return false; - - u16 divider = 32768 / frequency; - return (divider <= 16384 && divider >= 4); // Frequency can be in range of 2 Hz to 8 KHz -} -size_t RealTimeClock::calculate_nearest_possible_frequency(size_t frequency) const -{ - VERIFY(frequency != 0); - return frequency; -} - -} diff --git a/Kernel/Arch/x86_64/Time/RTC.h b/Kernel/Arch/x86_64/Time/RTC.h deleted file mode 100644 index bdf3174e5e8..00000000000 --- a/Kernel/Arch/x86_64/Time/RTC.h +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright (c) 2020, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include - -namespace Kernel { -class RealTimeClock final : public HardwareTimer { -public: - static NonnullLockRefPtr create(Function callback); - virtual HardwareTimerType timer_type() const override { return HardwareTimerType::RTC; } - virtual StringView model() const override { return "Real Time Clock"sv; } - - virtual bool is_periodic() const override { return true; } - virtual bool is_periodic_capable() const override { return true; } - virtual void set_periodic() override { } - virtual void set_non_periodic() override { } - virtual void disable() override { } - - virtual void reset_to_default_ticks_per_second() override; - virtual bool try_to_set_frequency(size_t frequency) override; - virtual bool is_capable_of_frequency(size_t frequency) const override; - virtual size_t calculate_nearest_possible_frequency(size_t frequency) const override; - -private: - explicit RealTimeClock(Function callback); - virtual bool handle_irq(RegisterState const&) override; -}; -} diff --git a/Kernel/Arch/x86_64/TrapFrame.h b/Kernel/Arch/x86_64/TrapFrame.h deleted file mode 100644 index 719f2e35fbe..00000000000 --- a/Kernel/Arch/x86_64/TrapFrame.h +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright (c) 2018-2021, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include - -#include -VALIDATE_IS_X86() - -namespace Kernel { - -struct RegisterState; - -struct TrapFrame { - FlatPtr prev_irq_level; - TrapFrame* next_trap; - RegisterState* regs; // must be last - - TrapFrame() = delete; - TrapFrame(TrapFrame const&) = delete; - TrapFrame(TrapFrame&&) = delete; - TrapFrame& operator=(TrapFrame const&) = delete; - TrapFrame& operator=(TrapFrame&&) = delete; -}; - -#define TRAP_FRAME_SIZE (3 * 8) - -static_assert(AssertSize()); - -} diff --git a/Kernel/Arch/x86_64/VGA/IOArbiter.cpp b/Kernel/Arch/x86_64/VGA/IOArbiter.cpp deleted file mode 100644 index 9561c5fdf14..00000000000 --- a/Kernel/Arch/x86_64/VGA/IOArbiter.cpp +++ /dev/null @@ -1,83 +0,0 @@ -/* - * Copyright (c) 2022, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include - -namespace Kernel { - -NonnullOwnPtr VGAIOArbiter::must_create(Badge) -{ - return MUST(adopt_nonnull_own_or_enomem(new (nothrow) VGAIOArbiter())); -} - -VGAIOArbiter::~VGAIOArbiter() = default; -VGAIOArbiter::VGAIOArbiter() = default; - -void VGAIOArbiter::disable_vga_emulation_access_permanently(Badge) -{ - SpinlockLocker locker(m_main_vga_lock); - disable_vga_text_mode_console_cursor(); - IO::out8(0x3c4, 1); - u8 sr1 = IO::in8(0x3c5); - IO::out8(0x3c5, sr1 | 1 << 5); - microseconds_delay(1000); - m_vga_access_is_disabled.set(); -} - -void VGAIOArbiter::enable_vga_text_mode_console_cursor(Badge) -{ - enable_vga_text_mode_console_cursor(); -} - -void VGAIOArbiter::enable_vga_text_mode_console_cursor() -{ - SpinlockLocker locker(m_main_vga_lock); - if (m_vga_access_is_disabled.was_set()) - return; - IO::out8(0x3D4, 0xA); - IO::out8(0x3D5, 0); -} - -void VGAIOArbiter::disable_vga_text_mode_console_cursor(Badge) -{ - disable_vga_text_mode_console_cursor(); -} - -void VGAIOArbiter::disable_vga_text_mode_console_cursor() -{ - SpinlockLocker locker(m_main_vga_lock); - if (m_vga_access_is_disabled.was_set()) - return; - IO::out8(0x3D4, 0xA); - IO::out8(0x3D5, 0x20); -} - -void VGAIOArbiter::unblank_screen(Badge) -{ - SpinlockLocker locker(m_main_vga_lock); - if (m_vga_access_is_disabled.was_set()) - return; - IO::out8(0x3c0, 0x20); -} - -void VGAIOArbiter::set_vga_text_mode_cursor(Badge, size_t console_width, size_t x, size_t y) -{ - SpinlockLocker locker(m_main_vga_lock); - if (m_vga_access_is_disabled.was_set()) - return; - enable_vga_text_mode_console_cursor(); - u16 value = y * console_width + x; - IO::out8(0x3d4, 0x0e); - IO::out8(0x3d5, MSB(value)); - IO::out8(0x3d4, 0x0f); - IO::out8(0x3d5, LSB(value)); -} - -} diff --git a/Kernel/Arch/x86_64/VGA/IOArbiter.h b/Kernel/Arch/x86_64/VGA/IOArbiter.h deleted file mode 100644 index 1f1d9a7c15e..00000000000 --- a/Kernel/Arch/x86_64/VGA/IOArbiter.h +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright (c) 2022, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include - -namespace Kernel { - -class GraphicsManagement; -class VGAIOArbiter { -public: - static NonnullOwnPtr must_create(Badge); - - void disable_vga_emulation_access_permanently(Badge); - void enable_vga_text_mode_console_cursor(Badge); - void disable_vga_text_mode_console_cursor(Badge); - void set_vga_text_mode_cursor(Badge, size_t console_width, size_t x, size_t y); - - void unblank_screen(Badge); - - ~VGAIOArbiter(); - -private: - VGAIOArbiter(); - - void disable_vga_text_mode_console_cursor(); - void enable_vga_text_mode_console_cursor(); - - RecursiveSpinlock m_main_vga_lock {}; - SetOnce m_vga_access_is_disabled; -}; - -} diff --git a/Kernel/Arch/x86_64/VGA/TextModeConsole.cpp b/Kernel/Arch/x86_64/VGA/TextModeConsole.cpp deleted file mode 100644 index 615b30b301c..00000000000 --- a/Kernel/Arch/x86_64/VGA/TextModeConsole.cpp +++ /dev/null @@ -1,180 +0,0 @@ -/* - * Copyright (c) 2021, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include - -namespace Kernel::Graphics { - -NonnullLockRefPtr VGATextModeConsole::initialize() -{ - auto vga_window_size = MUST(Memory::page_round_up(0xc0000 - 0xa0000)); - auto vga_window_region = MUST(MM.allocate_mmio_kernel_region(PhysicalAddress(0xa0000), vga_window_size, "VGA Display"sv, Memory::Region::Access::ReadWrite)); - return adopt_lock_ref(*new (nothrow) VGATextModeConsole(move(vga_window_region))); -} - -VGATextModeConsole::VGATextModeConsole(NonnullOwnPtr vga_window_region) - : Console(80, 25) - , m_vga_window_region(move(vga_window_region)) - , m_current_vga_window(m_vga_window_region->vaddr().offset(0x18000).as_ptr()) -{ - for (size_t index = 0; index < height(); index++) { - clear_vga_row(index); - } - dbgln("VGA Text mode console initialized!"); -} - -enum VGAColor : u8 { - Black = 0, - Blue, - Green, - Cyan, - Red, - Magenta, - Brown, - LightGray, - DarkGray, - BrightBlue, - BrightGreen, - BrightCyan, - BrightRed, - BrightMagenta, - Yellow, - White, -}; - -[[maybe_unused]] static inline VGAColor convert_standard_color_to_vga_color(Console::Color color) -{ - switch (color) { - case Console::Color::Black: - return VGAColor::Black; - case Console::Color::Red: - return VGAColor::Red; - case Console::Color::Brown: - return VGAColor::Brown; - case Console::Color::Blue: - return VGAColor::Blue; - case Console::Color::Magenta: - return VGAColor::Magenta; - case Console::Color::Green: - return VGAColor::Green; - case Console::Color::Cyan: - return VGAColor::Cyan; - case Console::Color::LightGray: - return VGAColor::LightGray; - case Console::Color::DarkGray: - return VGAColor::DarkGray; - case Console::Color::BrightRed: - return VGAColor::BrightRed; - case Console::Color::BrightGreen: - return VGAColor::BrightGreen; - case Console::Color::Yellow: - return VGAColor::Yellow; - case Console::Color::BrightBlue: - return VGAColor::BrightBlue; - case Console::Color::BrightMagenta: - return VGAColor::BrightMagenta; - case Console::Color::BrightCyan: - return VGAColor::BrightCyan; - case Console::Color::White: - return VGAColor::White; - default: - VERIFY_NOT_REACHED(); - } -} - -void VGATextModeConsole::set_cursor(size_t x, size_t y) -{ - SpinlockLocker lock(m_vga_lock); - GraphicsManagement::the().set_vga_text_mode_cursor(width(), x, y); - m_x = x; - m_y = y; -} -void VGATextModeConsole::hide_cursor() -{ - SpinlockLocker lock(m_vga_lock); - GraphicsManagement::the().disable_vga_text_mode_console_cursor(); -} -void VGATextModeConsole::show_cursor() -{ - set_cursor(m_x, m_y); -} - -void VGATextModeConsole::clear(size_t x, size_t y, size_t length) -{ - SpinlockLocker lock(m_vga_lock); - auto* buf = (u16*)m_current_vga_window.offset((x * 2) + (y * width() * 2)).as_ptr(); - for (size_t index = 0; index < length; index++) { - buf[index] = 0x0720; - } -} - -void VGATextModeConsole::scroll_up() -{ - auto* start_of_buffer = m_current_vga_window.as_ptr(); - auto* start_of_second_line = m_current_vga_window.offset(max_column() * 2).as_ptr(); - auto size_to_move = (max_row() - 1) * max_column() * 2; - memmove(start_of_buffer, start_of_second_line, size_to_move); - - // Clear the last row with a memset, we're called from `write` here which already - // grabs `m_vga_lock` so we will get a deadlock trying to call `clear_vga_row` - auto* last_line = m_current_vga_window.offset((max_row() - 1) * width() * 2).as_ptr(); - memset(last_line, 0, width() * 2); -} - -void VGATextModeConsole::write(size_t x, size_t y, char ch, bool critical) -{ - write(x, y, ch, m_default_background_color, m_default_foreground_color, critical); -} - -void VGATextModeConsole::write(size_t x, size_t y, char ch, Color background, Color foreground, bool critical) -{ - SpinlockLocker lock(m_vga_lock); - // If we are in critical printing mode, we need to handle new lines here - // because there's no other responsible object to do that in the print call path - if (critical && (ch == '\r' || ch == '\n')) { - // Disable hardware VGA cursor - GraphicsManagement::the().disable_vga_text_mode_console_cursor(); - - m_x = 0; - m_y += 1; - if (m_y >= max_row()) { - m_y = max_row() - 1; - scroll_up(); - } - return; - } - - auto* buf = (u16*)m_current_vga_window.offset((x * 2) + (y * width() * 2)).as_ptr(); - *buf = foreground << 8 | background << 12 | ch; - m_x = x + 1; - - if (m_x >= max_column()) { - m_x = 0; - m_y = y + 1; - if (m_y >= max_row()) { - if (critical) { - m_y = max_row() - 1; - scroll_up(); - } else { - m_y = 0; - } - } - } -} - -void VGATextModeConsole::clear_vga_row(u16 row) -{ - clear(0, row, width()); -} - -void VGATextModeConsole::write(char ch, bool critical) -{ - write(m_x, m_y, ch, critical); -} - -} diff --git a/Kernel/Arch/x86_64/VGA/TextModeConsole.h b/Kernel/Arch/x86_64/VGA/TextModeConsole.h deleted file mode 100644 index 543a5c29a0d..00000000000 --- a/Kernel/Arch/x86_64/VGA/TextModeConsole.h +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright (c) 2021, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include - -namespace Kernel::Graphics { -class VGATextModeConsole final : public Console { -public: - static NonnullLockRefPtr initialize(); - virtual size_t chars_per_line() const override { return width(); } - - virtual bool has_hardware_cursor() const override { return true; } - virtual bool is_hardware_paged_capable() const override { return true; } - - virtual size_t bytes_per_base_glyph() const override { return 2; } - virtual void set_cursor(size_t x, size_t y) override; - virtual void clear(size_t x, size_t y, size_t length) override; - virtual void write(size_t x, size_t y, char ch, bool critical = false) override; - virtual void write(size_t x, size_t y, char ch, Color background, Color foreground, bool critical = false) override; - virtual void write(char ch, bool critical = false) override; - virtual void flush(size_t, size_t, size_t, size_t) override { } - - virtual void enable() override { } - virtual void disable() override { } - -private: - virtual void hide_cursor() override; - virtual void show_cursor() override; - - virtual void scroll_up() override; - - void clear_vga_row(u16 row); - - explicit VGATextModeConsole(NonnullOwnPtr); - - mutable Spinlock m_vga_lock {}; - - NonnullOwnPtr m_vga_window_region; - VirtualAddress m_current_vga_window; -}; - -} diff --git a/Kernel/Arch/x86_64/archctl.cpp b/Kernel/Arch/x86_64/archctl.cpp deleted file mode 100644 index 7e471746142..00000000000 --- a/Kernel/Arch/x86_64/archctl.cpp +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright (c) 2024, Sönke Holz - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include - -namespace Kernel { - -ErrorOr Process::sys$archctl(int option, FlatPtr arg1) -{ - VERIFY_NO_PROCESS_BIG_LOCK(this); - switch (option) { - case ARCHCTL_X86_64_SET_FS_BASE_FOR_CURRENT_THREAD: { - Thread::current()->arch_specific_data().fs_base = arg1; - Processor::set_fs_base(arg1); - return 0; - } - - default: - return EINVAL; - } -} - -} diff --git a/Kernel/Arch/x86_64/linker.ld b/Kernel/Arch/x86_64/linker.ld deleted file mode 100644 index 803d75b373e..00000000000 --- a/Kernel/Arch/x86_64/linker.ld +++ /dev/null @@ -1,111 +0,0 @@ -ENTRY(init) - -#define PF_X 0x1 -#define PF_W 0x2 -#define PF_R 0x4 - -PHDRS -{ - elf_headers PT_LOAD FILEHDR PHDRS FLAGS(PF_R) ; - text PT_LOAD FLAGS(PF_R | PF_X) ; - data PT_LOAD FLAGS(PF_R | PF_W) ; - bss PT_LOAD FLAGS(PF_R | PF_W) ; - dynamic_segment PT_LOAD FLAGS(PF_R | PF_W) ; - dynamic PT_DYNAMIC FLAGS(PF_R | PF_W) ; - ksyms PT_LOAD FLAGS(PF_R) ; -} - -SECTIONS -{ - start_of_kernel_image = .; - - .elf_headers (SIZEOF_HEADERS) : AT (ADDR(.elf_headers) + SIZEOF_HEADERS) - { - start_of_elf_headers = .; - } :elf_headers - - .text ALIGN(4K) : AT (ADDR(.text)) - { - start_of_kernel_text = .; - - start_of_safemem_text = .; - KEEP(*(.text.safemem)) - end_of_safemem_text = .; - start_of_safemem_atomic_text = .; - KEEP(*(.text.safemem.atomic)) - end_of_safemem_atomic_text = .; - - *(.text*) - } :text - - .driver_init ALIGN(4K) : AT (ADDR(.driver_init)) - { - driver_init_table_start = .; - *(.driver_init) - driver_init_table_end = .; - } :text - - .unmap_after_init ALIGN(4K) : AT (ADDR(.unmap_after_init)) - { - start_of_unmap_after_init = .; - *(.unmap_after_init*); - end_of_unmap_after_init = .; - - end_of_kernel_text = .; - } :text - - .rodata ALIGN(4K) : AT (ADDR(.rodata)) - { - start_heap_ctors = .; - *libkernel_heap.a:*(.ctors) - *libkernel_heap.a:*(.init_array) - end_heap_ctors = .; - - start_ctors = .; - *(.ctors) - *(.init_array) - end_ctors = .; - - *(.rodata*) - } :data - - .data ALIGN(4K) : AT (ADDR(.data)) - { - start_of_kernel_data = .; - *(.data*) - end_of_kernel_data = .; - } :data - - .ro_after_init ALIGN(4K) : AT(ADDR(.ro_after_init)) - { - start_of_ro_after_init = .; - *(.ro_after_init); - end_of_ro_after_init = .; - } :data - - .bss ALIGN(4K) (NOLOAD) : AT (ADDR(.bss)) - { - start_of_kernel_bss = .; - *(page_tables) - *(COMMON) - *(.bss*) - end_of_kernel_bss = .; - - . = ALIGN(4K); - *(.heap) - } :bss - - .dynamic ALIGN(4K) : AT (ADDR(.dynamic)) - { - *(.dynamic) - } :dynamic_segment :dynamic - - .ksyms ALIGN(4K) : AT (ADDR(.ksyms)) - { - start_of_kernel_ksyms = .; - *(.kernel_symbols) - end_of_kernel_ksyms = .; - } :ksyms - - end_of_kernel_image = .; -} diff --git a/Kernel/Arch/x86_64/mcontext.h b/Kernel/Arch/x86_64/mcontext.h deleted file mode 100644 index 798367593f3..00000000000 --- a/Kernel/Arch/x86_64/mcontext.h +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright (c) 2022, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include - -#ifdef __cplusplus -extern "C" { -#endif - -struct __attribute__((packed)) __mcontext { - uint64_t rax; - uint64_t rcx; - uint64_t rdx; - uint64_t rbx; - uint64_t rsp; - uint64_t rbp; - uint64_t rsi; - uint64_t rdi; - uint64_t rip; - uint64_t r8; - uint64_t r9; - uint64_t r10; - uint64_t r11; - uint64_t r12; - uint64_t r13; - uint64_t r14; - uint64_t r15; - uint64_t rflags; - uint32_t cs; - uint32_t ss; - uint32_t ds; - uint32_t es; - uint32_t fs; - uint32_t gs; -}; - -#ifdef __cplusplus -} -#endif diff --git a/Kernel/Boot/BootInfo.h b/Kernel/Boot/BootInfo.h deleted file mode 100644 index 7ba21c4a652..00000000000 --- a/Kernel/Boot/BootInfo.h +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright (c) 2021, Gunnar Beutner - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include - -namespace Kernel::Memory { -class PageTableEntry; -} - -extern "C" PhysicalAddress start_of_prekernel_image; -extern "C" PhysicalAddress end_of_prekernel_image; -extern "C" size_t physical_to_virtual_offset; -extern "C" FlatPtr kernel_mapping_base; -extern "C" FlatPtr kernel_load_base; -#if ARCH(X86_64) -extern "C" u32 gdt64ptr; -extern "C" u16 code64_sel; -#endif -extern "C" PhysicalAddress boot_pml4t; -extern "C" PhysicalAddress boot_pdpt; -extern "C" PhysicalAddress boot_pd0; -extern "C" PhysicalAddress boot_pd_kernel; -extern "C" Kernel::Memory::PageTableEntry* boot_pd_kernel_pt1023; -extern "C" StringView kernel_cmdline; -extern "C" u32 multiboot_flags; -extern "C" multiboot_memory_map_t* multiboot_memory_map; -extern "C" size_t multiboot_memory_map_count; -extern "C" PhysicalAddress multiboot_module_physical_ptr; -extern "C" size_t multiboot_module_length; -extern "C" PhysicalAddress multiboot_framebuffer_addr; -extern "C" u32 multiboot_framebuffer_pitch; -extern "C" u32 multiboot_framebuffer_width; -extern "C" u32 multiboot_framebuffer_height; -extern "C" u8 multiboot_framebuffer_bpp; -extern "C" u8 multiboot_framebuffer_type; diff --git a/Kernel/Boot/CommandLine.cpp b/Kernel/Boot/CommandLine.cpp deleted file mode 100644 index 2782daca807..00000000000 --- a/Kernel/Boot/CommandLine.cpp +++ /dev/null @@ -1,326 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include - -namespace Kernel { - -static char s_cmd_line[1024]; -static constexpr StringView s_embedded_cmd_line = ""sv; -static CommandLine* s_the; - -UNMAP_AFTER_INIT void CommandLine::early_initialize(StringView cmd_line) -{ - (void)cmd_line.copy_characters_to_buffer(s_cmd_line, sizeof(s_cmd_line)); -} - -bool CommandLine::was_initialized() -{ - return s_the != nullptr; -} - -CommandLine const& kernel_command_line() -{ - VERIFY(s_the); - return *s_the; -} - -UNMAP_AFTER_INIT void CommandLine::initialize() -{ - VERIFY(!s_the); - s_the = new CommandLine({ s_cmd_line, strlen(s_cmd_line) }); - dmesgln("Kernel Commandline: {}", kernel_command_line().string()); - // Validate the modes the user passed in. - (void)s_the->panic_mode(Validate::Yes); -} - -UNMAP_AFTER_INIT NonnullOwnPtr CommandLine::build_commandline(StringView cmdline_from_bootloader) -{ - StringBuilder builder; - builder.append(cmdline_from_bootloader); - if constexpr (!s_embedded_cmd_line.is_empty()) { - builder.append(' '); - builder.append(s_embedded_cmd_line); - } - return KString::must_create(builder.string_view()); -} - -UNMAP_AFTER_INIT void CommandLine::add_arguments(Vector const& args) -{ - for (auto&& str : args) { - if (str == ""sv) { - continue; - } - // Some boot loaders may include complex key-value pairs where the value is a composite entry, - // we handle this by only checking for the first equals sign in each command line parameter. - auto key = str.find_first_split_view('='); - if (key.length() == str.length()) - m_params.set(key, ""sv); - else - m_params.set(key, str.substring_view(key.length() + 1)); - } -} - -UNMAP_AFTER_INIT CommandLine::CommandLine(StringView cmdline_from_bootloader) - : m_string(build_commandline(cmdline_from_bootloader)) -{ - s_the = this; - auto const& args = m_string->view().split_view(' '); - MUST(m_params.try_ensure_capacity(args.size())); - add_arguments(args); -} - -Optional CommandLine::lookup(StringView key) const -{ - return m_params.get(key); -} - -bool CommandLine::contains(StringView key) const -{ - return m_params.contains(key); -} - -UNMAP_AFTER_INIT bool CommandLine::is_boot_profiling_enabled() const -{ - return contains("boot_prof"sv); -} - -UNMAP_AFTER_INIT bool CommandLine::is_smp_enabled() const -{ - // Note: We can't enable SMP mode without enabling the IOAPIC. - if (!is_ioapic_enabled()) - return false; - return lookup("smp"sv).value_or("off"sv) == "on"sv; -} - -UNMAP_AFTER_INIT bool CommandLine::is_smp_enabled_without_ioapic_enabled() const -{ - auto smp_enabled = lookup("smp"sv).value_or("off"sv) == "on"sv; - return smp_enabled && !is_ioapic_enabled(); -} - -UNMAP_AFTER_INIT bool CommandLine::is_ioapic_enabled() const -{ - auto value = lookup("enable_ioapic"sv).value_or("on"sv); - if (value == "on"sv) - return true; - if (value == "off"sv) - return false; - PANIC("Unknown enable_ioapic setting: {}", value); -} - -UNMAP_AFTER_INIT bool CommandLine::is_early_boot_console_disabled() const -{ - auto value = lookup("early_boot_console"sv).value_or("on"sv); - if (value == "on"sv) - return false; - if (value == "off"sv) - return true; - PANIC("Unknown early_boot_console setting: {}", value); -} - -UNMAP_AFTER_INIT bool CommandLine::i8042_enable_first_port_translation() const -{ - // FIXME: Disable first port translation when the keyboard works OK. - auto value = lookup("i8042_first_port_translation"sv).value_or("on"sv); - if (value == "off"sv) - return false; - if (value == "on"sv) - return true; - PANIC("Unknown i8042_enable_first_port_translation setting: {}", value); -} - -UNMAP_AFTER_INIT I8042PresenceMode CommandLine::i8042_presence_mode() const -{ - auto value = lookup("i8042_presence_mode"sv).value_or("auto"sv); - if (value == "auto"sv) - return I8042PresenceMode::Automatic; - if (value == "none"sv) - return I8042PresenceMode::None; - if (value == "force"sv) - return I8042PresenceMode::Force; - if (value == "aggressive-test"sv) - return I8042PresenceMode::AggressiveTest; - PANIC("Unknown i8042_presence_mode setting: {}", value); -} - -UNMAP_AFTER_INIT bool CommandLine::is_vmmouse_enabled() const -{ - return lookup("vmmouse"sv).value_or("on"sv) == "on"sv; -} - -UNMAP_AFTER_INIT PCIAccessLevel CommandLine::pci_access_level() const -{ - auto value = lookup("pci"sv).value_or("ecam"sv); - if (value == "ecam"sv) - return PCIAccessLevel::MemoryAddressing; -#if ARCH(X86_64) - if (value == "io"sv) - return PCIAccessLevel::IOAddressing; -#endif - if (value == "none"sv) - return PCIAccessLevel::None; - PANIC("Unknown PCI ECAM setting: {}", value); -} - -UNMAP_AFTER_INIT bool CommandLine::is_pci_disabled() const -{ - return lookup("pci"sv).value_or("ecam"sv) == "none"sv; -} - -UNMAP_AFTER_INIT bool CommandLine::is_legacy_time_enabled() const -{ - return lookup("time"sv).value_or("modern"sv) == "legacy"sv; -} - -bool CommandLine::is_pc_speaker_enabled() const -{ - auto value = lookup("pcspeaker"sv).value_or("off"sv); - if (value == "on"sv) - return true; - if (value == "off"sv) - return false; - PANIC("Unknown pcspeaker setting: {}", value); -} - -UNMAP_AFTER_INIT StringView CommandLine::root_device() const -{ - return lookup("root"sv).value_or("lun0:0:0"sv); -} - -bool CommandLine::is_nvme_polling_enabled() const -{ - return contains("nvme_poll"sv); -} - -UNMAP_AFTER_INIT AcpiFeatureLevel CommandLine::acpi_feature_level() const -{ - auto value = kernel_command_line().lookup("acpi"sv).value_or("limited"sv); - if (value == "limited"sv) - return AcpiFeatureLevel::Limited; - if (value == "off"sv) - return AcpiFeatureLevel::Disabled; - if (value == "on"sv) - return AcpiFeatureLevel::Enabled; - PANIC("Unknown ACPI feature level: {}", value); -} - -UNMAP_AFTER_INIT HPETMode CommandLine::hpet_mode() const -{ - auto hpet_mode = lookup("hpet"sv).value_or("periodic"sv); - if (hpet_mode == "periodic"sv) - return HPETMode::Periodic; - if (hpet_mode == "nonperiodic"sv) - return HPETMode::NonPeriodic; - PANIC("Unknown HPETMode: {}", hpet_mode); -} - -UNMAP_AFTER_INIT bool CommandLine::is_physical_networking_disabled() const -{ - return contains("disable_physical_networking"sv); -} - -UNMAP_AFTER_INIT bool CommandLine::disable_ps2_mouse() const -{ - return contains("disable_ps2_mouse"sv); -} - -UNMAP_AFTER_INIT bool CommandLine::disable_physical_storage() const -{ - return contains("disable_physical_storage"sv); -} - -UNMAP_AFTER_INIT bool CommandLine::disable_uhci_controller() const -{ - return contains("disable_uhci_controller"sv); -} - -UNMAP_AFTER_INIT bool CommandLine::disable_usb() const -{ - return contains("disable_usb"sv); -} - -UNMAP_AFTER_INIT bool CommandLine::disable_virtio() const -{ - return contains("disable_virtio"sv); -} - -UNMAP_AFTER_INIT AHCIResetMode CommandLine::ahci_reset_mode() const -{ - auto const ahci_reset_mode = lookup("ahci_reset_mode"sv).value_or("controllers"sv); - if (ahci_reset_mode == "controllers"sv) { - return AHCIResetMode::ControllerOnly; - } - if (ahci_reset_mode == "aggressive"sv) { - return AHCIResetMode::Aggressive; - } - PANIC("Unknown AHCIResetMode: {}", ahci_reset_mode); -} - -StringView CommandLine::system_mode() const -{ - return lookup("system_mode"sv).value_or("graphical"sv); -} - -PanicMode CommandLine::panic_mode(Validate should_validate) const -{ - auto const panic_mode = lookup("panic"sv).value_or("halt"sv); - if (panic_mode == "halt"sv) { - return PanicMode::Halt; - } - if (panic_mode == "shutdown"sv) { - return PanicMode::Shutdown; - } - - if (should_validate == Validate::Yes) - PANIC("Unknown PanicMode: {}", panic_mode); - - return PanicMode::Halt; -} - -UNMAP_AFTER_INIT CommandLine::GraphicsSubsystemMode CommandLine::graphics_subsystem_mode() const -{ - auto const graphics_subsystem_mode_value = lookup("graphics_subsystem_mode"sv).value_or("on"sv); - if (graphics_subsystem_mode_value == "on"sv) - return GraphicsSubsystemMode::Enabled; - if (graphics_subsystem_mode_value == "limited"sv) - return GraphicsSubsystemMode::Limited; - if (graphics_subsystem_mode_value == "off"sv) - return GraphicsSubsystemMode::Disabled; - PANIC("Invalid graphics_subsystem_mode value: {}", graphics_subsystem_mode_value); -} - -StringView CommandLine::userspace_init() const -{ - return lookup("init"sv).value_or("/init"sv); -} - -Vector> CommandLine::userspace_init_args() const -{ - Vector> args; - - auto init_args = lookup("init_args"sv).value_or(""sv).split_view(';'); - if (!init_args.is_empty()) - MUST(args.try_prepend(MUST(KString::try_create(userspace_init())))); - for (auto& init_arg : init_args) - args.append(MUST(KString::try_create(init_arg))); - return args; -} - -UNMAP_AFTER_INIT size_t CommandLine::switch_to_tty() const -{ - auto const default_tty = lookup("switch_to_tty"sv).value_or("1"sv); - auto switch_tty_number = default_tty.to_number(); - if (switch_tty_number.has_value() && switch_tty_number.value() >= 1) { - return switch_tty_number.value() - 1; - } - PANIC("Invalid default tty value: {}", default_tty); -} -} diff --git a/Kernel/Boot/CommandLine.h b/Kernel/Boot/CommandLine.h deleted file mode 100644 index ac5920da191..00000000000 --- a/Kernel/Boot/CommandLine.h +++ /dev/null @@ -1,116 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include - -namespace Kernel { - -enum class PanicMode { - Halt, - Shutdown, -}; - -enum class HPETMode { - Periodic, - NonPeriodic -}; - -enum class I8042PresenceMode { - Automatic, - AggressiveTest, - Force, - None, -}; - -enum class AcpiFeatureLevel { - Enabled, - Limited, - Disabled, -}; - -enum class PCIAccessLevel { - None, -#if ARCH(X86_64) - IOAddressing, -#endif - MemoryAddressing, -}; - -enum class AHCIResetMode { - ControllerOnly, - Aggressive, -}; - -class CommandLine { - -public: - static void early_initialize(StringView cmd_line); - static void initialize(); - static bool was_initialized(); - - enum class Validate { - Yes, - No, - }; - - enum class GraphicsSubsystemMode { - Enabled, - Limited, - Disabled - }; - - [[nodiscard]] StringView string() const { return m_string->view(); } - Optional lookup(StringView key) const; - [[nodiscard]] bool contains(StringView key) const; - - [[nodiscard]] bool is_boot_profiling_enabled() const; - [[nodiscard]] bool is_ioapic_enabled() const; - [[nodiscard]] bool is_smp_enabled_without_ioapic_enabled() const; - [[nodiscard]] bool is_smp_enabled() const; - [[nodiscard]] bool is_physical_networking_disabled() const; - [[nodiscard]] bool is_vmmouse_enabled() const; - [[nodiscard]] PCIAccessLevel pci_access_level() const; - [[nodiscard]] bool is_pci_disabled() const; - [[nodiscard]] bool is_legacy_time_enabled() const; - [[nodiscard]] bool is_pc_speaker_enabled() const; - [[nodiscard]] bool i8042_enable_first_port_translation() const; - [[nodiscard]] GraphicsSubsystemMode graphics_subsystem_mode() const; - [[nodiscard]] I8042PresenceMode i8042_presence_mode() const; - [[nodiscard]] AcpiFeatureLevel acpi_feature_level() const; - [[nodiscard]] StringView system_mode() const; - [[nodiscard]] PanicMode panic_mode(Validate should_validate = Validate::No) const; - [[nodiscard]] HPETMode hpet_mode() const; - [[nodiscard]] bool disable_physical_storage() const; - [[nodiscard]] bool disable_ps2_mouse() const; - [[nodiscard]] bool disable_uhci_controller() const; - [[nodiscard]] bool disable_usb() const; - [[nodiscard]] bool disable_virtio() const; - [[nodiscard]] bool is_early_boot_console_disabled() const; - [[nodiscard]] AHCIResetMode ahci_reset_mode() const; - [[nodiscard]] StringView userspace_init() const; - [[nodiscard]] Vector> userspace_init_args() const; - [[nodiscard]] StringView root_device() const; - [[nodiscard]] bool is_nvme_polling_enabled() const; - [[nodiscard]] size_t switch_to_tty() const; - -private: - CommandLine(StringView); - - void add_arguments(Vector const& args); - static NonnullOwnPtr build_commandline(StringView cmdline_from_bootloader); - - NonnullOwnPtr m_string; - HashMap m_params; -}; - -CommandLine const& kernel_command_line(); - -} diff --git a/Kernel/Boot/Multiboot.h b/Kernel/Boot/Multiboot.h deleted file mode 100644 index 91925551525..00000000000 --- a/Kernel/Boot/Multiboot.h +++ /dev/null @@ -1,130 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include - -struct multiboot_module_entry { - u32 start; - u32 end; - u32 string_addr; - u32 reserved; -}; -typedef struct multiboot_module_entry multiboot_module_entry_t; - -struct multiboot_aout_symbol_table { - u32 tabsize; - u32 strsize; - u32 addr; - u32 reserved; -}; -typedef struct multiboot_aout_symbol_table multiboot_aout_symbol_table_t; - -struct multiboot_elf_section_header_table { - u32 num; - u32 size; - u32 addr; - u32 shndx; -}; -typedef struct multiboot_elf_section_header_table multiboot_elf_section_header_table_t; - -#define MULTIBOOT_MEMORY_AVAILABLE 1 -#define MULTIBOOT_MEMORY_RESERVED 2 -#define MULTIBOOT_MEMORY_ACPI_RECLAIMABLE 3 -#define MULTIBOOT_MEMORY_NVS 4 -#define MULTIBOOT_MEMORY_BADRAM 5 - -struct multiboot_mmap_entry { - u32 size; - u64 addr; - u64 len; - u32 type; -#if ARCH(AARCH64) - // __attribute__((packed)) causes alignment issues on aarch64 -}; -#else -} __attribute__((packed)); -#endif -typedef struct multiboot_mmap_entry multiboot_memory_map_t; - -#define MULTIBOOT_INFO_FRAMEBUFFER_INFO (1 << 12) - -struct multiboot_info { - // Multiboot info version number. - u32 flags; - - // Available memory from BIOS. - u32 mem_lower; - u32 mem_upper; - - // "root" partition. - u32 boot_device; - - // Kernel command line. - u32 cmdline; - - // Boot-Module list. - u32 mods_count; - u32 mods_addr; - - union { - multiboot_aout_symbol_table_t aout_sym; - multiboot_elf_section_header_table_t elf_sec; - } u; - - // Memory Mapping buffer. - u32 mmap_length; - u32 mmap_addr; - - // Drive Info buffer. - u32 drives_length; - u32 drives_addr; - - // ROM configuration table. - u32 config_table; - - // Boot Loader Name. - u32 boot_loader_name; - - // APM table. - u32 apm_table; - - // Video. - u32 vbe_control_info; - u32 vbe_mode_info; - u16 vbe_mode; - u16 vbe_interface_seg; - u16 vbe_interface_off; - u16 vbe_interface_len; - - u64 framebuffer_addr; - u32 framebuffer_pitch; - u32 framebuffer_width; - u32 framebuffer_height; - u8 framebuffer_bpp; -#define MULTIBOOT_FRAMEBUFFER_TYPE_INDEXED 0 -#define MULTIBOOT_FRAMEBUFFER_TYPE_RGB 1 -#define MULTIBOOT_FRAMEBUFFER_TYPE_EGA_TEXT 2 - u8 framebuffer_type; - union { - struct - { - u32 framebuffer_palette_addr; - u16 framebuffer_palette_num_colors; - }; - struct - { - u8 framebuffer_red_field_position; - u8 framebuffer_red_mask_size; - u8 framebuffer_green_field_position; - u8 framebuffer_green_mask_size; - u8 framebuffer_blue_field_position; - u8 framebuffer_blue_mask_size; - }; - }; -}; -typedef struct multiboot_info multiboot_info_t; diff --git a/Kernel/Bus/PCI/API.cpp b/Kernel/Bus/PCI/API.cpp deleted file mode 100644 index c277e23288d..00000000000 --- a/Kernel/Bus/PCI/API.cpp +++ /dev/null @@ -1,314 +0,0 @@ -/* - * Copyright (c) 2021, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include - -namespace Kernel::PCI { - -void write8_locked(DeviceIdentifier const& identifier, PCI::RegisterOffset field, u8 value) -{ - VERIFY(identifier.operation_lock().is_locked()); - Access::the().write8_field(identifier, to_underlying(field), value); -} -void write16_locked(DeviceIdentifier const& identifier, PCI::RegisterOffset field, u16 value) -{ - VERIFY(identifier.operation_lock().is_locked()); - Access::the().write16_field(identifier, to_underlying(field), value); -} -void write32_locked(DeviceIdentifier const& identifier, PCI::RegisterOffset field, u32 value) -{ - VERIFY(identifier.operation_lock().is_locked()); - Access::the().write32_field(identifier, to_underlying(field), value); -} - -u8 read8_locked(DeviceIdentifier const& identifier, PCI::RegisterOffset field) -{ - VERIFY(identifier.operation_lock().is_locked()); - return Access::the().read8_field(identifier, to_underlying(field)); -} -u16 read16_locked(DeviceIdentifier const& identifier, PCI::RegisterOffset field) -{ - VERIFY(identifier.operation_lock().is_locked()); - return Access::the().read16_field(identifier, to_underlying(field)); -} -u32 read32_locked(DeviceIdentifier const& identifier, PCI::RegisterOffset field) -{ - VERIFY(identifier.operation_lock().is_locked()); - return Access::the().read32_field(identifier, to_underlying(field)); -} - -ErrorOr enumerate(Function callback) -{ - return Access::the().fast_enumerate(callback); -} - -HardwareID get_hardware_id(DeviceIdentifier const& identifier) -{ - SpinlockLocker locker(identifier.operation_lock()); - return { read16_locked(identifier, PCI::RegisterOffset::VENDOR_ID), read16_locked(identifier, PCI::RegisterOffset::DEVICE_ID) }; -} - -void enable_io_space(DeviceIdentifier const& identifier) -{ - SpinlockLocker locker(identifier.operation_lock()); - write16_locked(identifier, PCI::RegisterOffset::COMMAND, read16_locked(identifier, PCI::RegisterOffset::COMMAND) | (1 << 0)); -} -void disable_io_space(DeviceIdentifier const& identifier) -{ - SpinlockLocker locker(identifier.operation_lock()); - write16_locked(identifier, PCI::RegisterOffset::COMMAND, read16_locked(identifier, PCI::RegisterOffset::COMMAND) & ~(1 << 0)); -} - -void enable_memory_space(DeviceIdentifier const& identifier) -{ - SpinlockLocker locker(identifier.operation_lock()); - write16_locked(identifier, PCI::RegisterOffset::COMMAND, read16_locked(identifier, PCI::RegisterOffset::COMMAND) | (1 << 1)); -} -void disable_memory_space(DeviceIdentifier const& identifier) -{ - SpinlockLocker locker(identifier.operation_lock()); - write16_locked(identifier, PCI::RegisterOffset::COMMAND, read16_locked(identifier, PCI::RegisterOffset::COMMAND) & ~(1 << 1)); -} - -bool is_io_space_enabled(DeviceIdentifier const& identifier) -{ - SpinlockLocker locker(identifier.operation_lock()); - return (read16_locked(identifier, PCI::RegisterOffset::COMMAND) & 1) != 0; -} - -void enable_interrupt_line(DeviceIdentifier const& identifier) -{ - SpinlockLocker locker(identifier.operation_lock()); - write16_locked(identifier, PCI::RegisterOffset::COMMAND, read16_locked(identifier, PCI::RegisterOffset::COMMAND) & ~(1 << 10)); -} - -void disable_interrupt_line(DeviceIdentifier const& identifier) -{ - SpinlockLocker locker(identifier.operation_lock()); - write16_locked(identifier, PCI::RegisterOffset::COMMAND, read16_locked(identifier, PCI::RegisterOffset::COMMAND) | 1 << 10); -} - -u32 get_BAR0(DeviceIdentifier const& identifier) -{ - SpinlockLocker locker(identifier.operation_lock()); - return read32_locked(identifier, PCI::RegisterOffset::BAR0); -} - -u32 get_BAR1(DeviceIdentifier const& identifier) -{ - SpinlockLocker locker(identifier.operation_lock()); - return read32_locked(identifier, PCI::RegisterOffset::BAR1); -} - -u32 get_BAR2(DeviceIdentifier const& identifier) -{ - SpinlockLocker locker(identifier.operation_lock()); - return read32_locked(identifier, PCI::RegisterOffset::BAR2); -} - -u32 get_BAR3(DeviceIdentifier const& identifier) -{ - SpinlockLocker locker(identifier.operation_lock()); - return read32_locked(identifier, PCI::RegisterOffset::BAR3); -} - -u32 get_BAR4(DeviceIdentifier const& identifier) -{ - SpinlockLocker locker(identifier.operation_lock()); - return read32_locked(identifier, PCI::RegisterOffset::BAR4); -} - -u32 get_BAR5(DeviceIdentifier const& identifier) -{ - SpinlockLocker locker(identifier.operation_lock()); - return read32_locked(identifier, PCI::RegisterOffset::BAR5); -} - -u32 get_BAR(DeviceIdentifier const& identifier, HeaderType0BaseRegister pci_bar) -{ - VERIFY(to_underlying(pci_bar) <= 5); - switch (to_underlying(pci_bar)) { - case 0: - return get_BAR0(identifier); - case 1: - return get_BAR1(identifier); - case 2: - return get_BAR2(identifier); - case 3: - return get_BAR3(identifier); - case 4: - return get_BAR4(identifier); - case 5: - return get_BAR5(identifier); - default: - VERIFY_NOT_REACHED(); - } -} - -BARSpaceType get_BAR_space_type(u32 pci_bar_value) -{ - // Note: For IO space, bit 0 is set to 1. - if (pci_bar_value & (1 << 0)) - return BARSpaceType::IOSpace; - auto memory_space_type = (pci_bar_value >> 1) & 0b11; - switch (memory_space_type) { - case 0: - return BARSpaceType::Memory32BitSpace; - case 1: - return BARSpaceType::Memory16BitSpace; - case 2: - return BARSpaceType::Memory64BitSpace; - default: - VERIFY_NOT_REACHED(); - } -} - -void enable_bus_mastering(DeviceIdentifier const& identifier) -{ - SpinlockLocker locker(identifier.operation_lock()); - auto value = read16_locked(identifier, PCI::RegisterOffset::COMMAND); - value |= (1 << 2); - value |= (1 << 0); - write16_locked(identifier, PCI::RegisterOffset::COMMAND, value); -} - -void disable_bus_mastering(DeviceIdentifier const& identifier) -{ - SpinlockLocker locker(identifier.operation_lock()); - auto value = read16_locked(identifier, PCI::RegisterOffset::COMMAND); - value &= ~(1 << 2); - value |= (1 << 0); - write16_locked(identifier, PCI::RegisterOffset::COMMAND, value); -} - -static void write8_offsetted(DeviceIdentifier const& identifier, u32 field, u8 value) -{ - VERIFY(identifier.operation_lock().is_locked()); - Access::the().write8_field(identifier, field, value); -} -static void write16_offsetted(DeviceIdentifier const& identifier, u32 field, u16 value) -{ - VERIFY(identifier.operation_lock().is_locked()); - Access::the().write16_field(identifier, field, value); -} -static void write32_offsetted(DeviceIdentifier const& identifier, u32 field, u32 value) -{ - VERIFY(identifier.operation_lock().is_locked()); - Access::the().write32_field(identifier, field, value); -} -static u8 read8_offsetted(DeviceIdentifier const& identifier, u32 field) -{ - VERIFY(identifier.operation_lock().is_locked()); - return Access::the().read8_field(identifier, field); -} -static u16 read16_offsetted(DeviceIdentifier const& identifier, u32 field) -{ - VERIFY(identifier.operation_lock().is_locked()); - return Access::the().read16_field(identifier, field); -} -static u32 read32_offsetted(DeviceIdentifier const& identifier, u32 field) -{ - VERIFY(identifier.operation_lock().is_locked()); - return Access::the().read32_field(identifier, field); -} - -size_t get_BAR_space_size(DeviceIdentifier const& identifier, HeaderType0BaseRegister pci_bar) -{ - SpinlockLocker locker(identifier.operation_lock()); - // See PCI Spec 2.3, Page 222 - VERIFY(to_underlying(pci_bar) < 6); - u8 field = to_underlying(PCI::RegisterOffset::BAR0) + (to_underlying(pci_bar) << 2); - u32 bar_reserved = read32_offsetted(identifier, field); - write32_offsetted(identifier, field, 0xFFFFFFFF); - u32 space_size = read32_offsetted(identifier, field); - write32_offsetted(identifier, field, bar_reserved); - space_size &= bar_address_mask; - space_size = (~space_size) + 1; - return space_size; -} - -size_t get_expansion_rom_space_size(DeviceIdentifier const& identifier) -{ - SpinlockLocker locker(identifier.operation_lock()); - u8 field = to_underlying(PCI::RegisterOffset::EXPANSION_ROM_POINTER); - u32 bar_reserved = read32_offsetted(identifier, field); - write32_offsetted(identifier, field, 0xFFFFFFFF); - u32 space_size = read32_offsetted(identifier, field); - write32_offsetted(identifier, field, bar_reserved); - space_size &= bar_address_mask; - space_size = (~space_size) + 1; - return space_size; -} - -void raw_access(DeviceIdentifier const& identifier, u32 field, size_t access_size, u32 value) -{ - SpinlockLocker locker(identifier.operation_lock()); - VERIFY(access_size != 0); - if (access_size == 1) { - write8_offsetted(identifier, field, value); - return; - } - if (access_size == 2) { - write16_offsetted(identifier, field, value); - return; - } - if (access_size == 4) { - write32_offsetted(identifier, field, value); - return; - } - VERIFY_NOT_REACHED(); -} - -u8 Capability::read8(size_t offset) const -{ - auto& identifier = get_device_identifier(m_address); - SpinlockLocker locker(identifier.operation_lock()); - return read8_offsetted(identifier, m_ptr + offset); -} - -u16 Capability::read16(size_t offset) const -{ - auto& identifier = get_device_identifier(m_address); - SpinlockLocker locker(identifier.operation_lock()); - return read16_offsetted(identifier, m_ptr + offset); -} - -u32 Capability::read32(size_t offset) const -{ - auto& identifier = get_device_identifier(m_address); - SpinlockLocker locker(identifier.operation_lock()); - return read32_offsetted(identifier, m_ptr + offset); -} - -void Capability::write8(size_t offset, u8 value) const -{ - auto& identifier = get_device_identifier(m_address); - SpinlockLocker locker(identifier.operation_lock()); - write8_offsetted(identifier, m_ptr + offset, value); -} - -void Capability::write16(size_t offset, u16 value) const -{ - auto& identifier = get_device_identifier(m_address); - SpinlockLocker locker(identifier.operation_lock()); - write16_offsetted(identifier, m_ptr + offset, value); -} - -void Capability::write32(size_t offset, u32 value) const -{ - auto& identifier = get_device_identifier(m_address); - SpinlockLocker locker(identifier.operation_lock()); - write32_offsetted(identifier, m_ptr + offset, value); -} - -DeviceIdentifier const& get_device_identifier(Address address) -{ - return Access::the().get_device_identifier(address); -} - -} diff --git a/Kernel/Bus/PCI/API.h b/Kernel/Bus/PCI/API.h deleted file mode 100644 index 5f9a62ade33..00000000000 --- a/Kernel/Bus/PCI/API.h +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright (c) 2021, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include - -namespace Kernel::PCI { - -void write8_locked(DeviceIdentifier const&, PCI::RegisterOffset field, u8 value); -void write16_locked(DeviceIdentifier const&, PCI::RegisterOffset field, u16 value); -void write32_locked(DeviceIdentifier const&, PCI::RegisterOffset field, u32 value); -u8 read8_locked(DeviceIdentifier const&, PCI::RegisterOffset field); -u16 read16_locked(DeviceIdentifier const&, PCI::RegisterOffset field); -u32 read32_locked(DeviceIdentifier const&, PCI::RegisterOffset field); - -HardwareID get_hardware_id(DeviceIdentifier const&); -bool is_io_space_enabled(DeviceIdentifier const&); -ErrorOr enumerate(Function callback); -void enable_interrupt_line(DeviceIdentifier const&); -void disable_interrupt_line(DeviceIdentifier const&); -void raw_access(DeviceIdentifier const&, u32, size_t, u32); - -u32 get_BAR0(DeviceIdentifier const&); -u32 get_BAR1(DeviceIdentifier const&); -u32 get_BAR2(DeviceIdentifier const&); -u32 get_BAR3(DeviceIdentifier const&); -u32 get_BAR4(DeviceIdentifier const&); -u32 get_BAR5(DeviceIdentifier const&); -u32 get_BAR(DeviceIdentifier const&, HeaderType0BaseRegister); -size_t get_BAR_space_size(DeviceIdentifier const&, HeaderType0BaseRegister); -BARSpaceType get_BAR_space_type(u32 pci_bar_value); -size_t get_expansion_rom_space_size(DeviceIdentifier const&); - -void enable_bus_mastering(DeviceIdentifier const&); -void disable_bus_mastering(DeviceIdentifier const&); -void enable_io_space(DeviceIdentifier const&); -void disable_io_space(DeviceIdentifier const&); -void enable_memory_space(DeviceIdentifier const&); -void disable_memory_space(DeviceIdentifier const&); - -// FIXME: Remove this once we can use PCI::Capability with inline buffer -// so we don't need this method -DeviceIdentifier const& get_device_identifier(Address address); -} diff --git a/Kernel/Bus/PCI/Access.cpp b/Kernel/Bus/PCI/Access.cpp deleted file mode 100644 index cf09e989f05..00000000000 --- a/Kernel/Bus/PCI/Access.cpp +++ /dev/null @@ -1,273 +0,0 @@ -/* - * Copyright (c) 2020, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#if ARCH(X86_64) -# include -#endif -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace Kernel::PCI { - -#define PCI_MMIO_CONFIG_SPACE_SIZE 4096 - -static Access* s_access; - -Access& Access::the() -{ - if (s_access == nullptr) { - VERIFY_NOT_REACHED(); // We failed to initialize the PCI subsystem, so stop here! - } - return *s_access; -} - -bool Access::is_initialized() -{ - return (s_access != nullptr); -} - -bool Access::is_hardware_disabled() -{ - return g_pci_access_io_probe_failed.was_set(); -} - -bool Access::is_disabled() -{ - return g_pci_access_is_disabled_from_commandline.was_set() || g_pci_access_io_probe_failed.was_set(); -} - -UNMAP_AFTER_INIT bool Access::find_and_register_pci_host_bridges_from_acpi_mcfg_table(PhysicalAddress mcfg_table) -{ - u32 length = 0; - u8 revision = 0; - { - auto mapped_mcfg_table_or_error = Memory::map_typed(mcfg_table); - if (mapped_mcfg_table_or_error.is_error()) { - dbgln("Failed to map MCFG table"); - return false; - } - auto mapped_mcfg_table = mapped_mcfg_table_or_error.release_value(); - length = mapped_mcfg_table->length; - revision = mapped_mcfg_table->revision; - } - - if (length == sizeof(ACPI::Structures::SDTHeader)) - return false; - - dbgln("PCI: MCFG, length: {}, revision: {}", length, revision); - - if (Checked::addition_would_overflow(length, PAGE_SIZE)) { - dbgln("Overflow when adding extra page to allocation of length {}", length); - return false; - } - length += PAGE_SIZE; - auto region_size_or_error = Memory::page_round_up(length); - if (region_size_or_error.is_error()) { - dbgln("Failed to round up length of {} to pages", length); - return false; - } - auto mcfg_region_or_error = MM.allocate_mmio_kernel_region(mcfg_table.page_base(), region_size_or_error.value(), "PCI Parsing MCFG"sv, Memory::Region::Access::ReadWrite); - if (mcfg_region_or_error.is_error()) - return false; - auto& mcfg = *(ACPI::Structures::MCFG*)mcfg_region_or_error.value()->vaddr().offset(mcfg_table.offset_in_page()).as_ptr(); - dbgln_if(PCI_DEBUG, "PCI: Checking MCFG @ {}, {}", VirtualAddress(&mcfg), mcfg_table); - for (u32 index = 0; index < ((mcfg.header.length - sizeof(ACPI::Structures::MCFG)) / sizeof(ACPI::Structures::PCI_MMIO_Descriptor)); index++) { - u8 start_bus = mcfg.descriptors[index].start_pci_bus; - u8 end_bus = mcfg.descriptors[index].end_pci_bus; - u64 start_addr = mcfg.descriptors[index].base_addr; - - Domain pci_domain { index, start_bus, end_bus }; - dmesgln("PCI: New PCI domain @ {}, PCI buses ({}-{})", PhysicalAddress { start_addr }, start_bus, end_bus); - auto host_bridge = MemoryBackedHostBridge::must_create(pci_domain, PhysicalAddress { start_addr }); - add_host_controller(move(host_bridge)); - } - - return true; -} - -UNMAP_AFTER_INIT bool Access::initialize_for_multiple_pci_domains(PhysicalAddress mcfg_table) -{ - VERIFY(!Access::is_initialized()); - auto* access = new Access(); - if (!access->find_and_register_pci_host_bridges_from_acpi_mcfg_table(mcfg_table)) - return false; - access->rescan_hardware(); - dbgln_if(PCI_DEBUG, "PCI: access for multiple PCI domain initialised."); - return true; -} - -#if ARCH(X86_64) -UNMAP_AFTER_INIT bool Access::initialize_for_one_pci_domain() -{ - VERIFY(!Access::is_initialized()); - auto* access = new Access(); - auto host_bridge = PIIX4HostBridge::must_create_with_io_access(); - access->add_host_controller(move(host_bridge)); - access->rescan_hardware(); - dbgln_if(PCI_DEBUG, "PCI: access for one PCI domain initialised."); - return true; -} -#endif - -ErrorOr Access::add_host_controller_and_scan_for_devices(NonnullOwnPtr controller) -{ - SpinlockLocker locker(m_access_lock); - SpinlockLocker scan_locker(m_scan_lock); - auto domain_number = controller->domain_number(); - - VERIFY(!m_host_controllers.contains(domain_number)); - // Note: We need to register the new controller as soon as possible, and - // definitely before enumerating devices behind that. - m_host_controllers.set(domain_number, move(controller)); - m_host_controllers.get(domain_number).value()->enumerate_attached_devices([&](EnumerableDeviceIdentifier const& device_identifier) { - auto device_identifier_or_error = DeviceIdentifier::from_enumerable_identifier(device_identifier); - if (device_identifier_or_error.is_error()) { - dmesgln("Failed during PCI Access::rescan_hardware due to {}", device_identifier_or_error.error()); - VERIFY_NOT_REACHED(); - } - m_device_identifiers.append(device_identifier_or_error.release_value()); - }); - return {}; -} - -UNMAP_AFTER_INIT void Access::add_host_controller(NonnullOwnPtr controller) -{ - auto domain_number = controller->domain_number(); - m_host_controllers.set(domain_number, move(controller)); -} - -UNMAP_AFTER_INIT Access::Access() -{ - s_access = this; -} - -UNMAP_AFTER_INIT void Access::configure_pci_space(PCIConfiguration& config) -{ - SpinlockLocker locker(m_access_lock); - SpinlockLocker scan_locker(m_scan_lock); - for (auto& [_, host_controller] : m_host_controllers) - host_controller->configure_attached_devices(config); -} - -UNMAP_AFTER_INIT void Access::rescan_hardware() -{ - SpinlockLocker locker(m_access_lock); - SpinlockLocker scan_locker(m_scan_lock); - VERIFY(m_device_identifiers.is_empty()); - for (auto& [_, host_controller] : m_host_controllers) { - host_controller->enumerate_attached_devices([this](EnumerableDeviceIdentifier const& device_identifier) { - auto device_identifier_or_error = DeviceIdentifier::from_enumerable_identifier(device_identifier); - if (device_identifier_or_error.is_error()) { - dmesgln("Failed during PCI Access::rescan_hardware due to {}", device_identifier_or_error.error()); - VERIFY_NOT_REACHED(); - } - m_device_identifiers.append(device_identifier_or_error.release_value()); - }); - } -} - -ErrorOr Access::fast_enumerate(Function& callback) const -{ - // Note: We hold the m_access_lock for a brief moment just to ensure we get - // a complete Vector in case someone wants to mutate it. - Vector> device_identifiers; - { - SpinlockLocker locker(m_access_lock); - VERIFY(!m_device_identifiers.is_empty()); - TRY(device_identifiers.try_extend(m_device_identifiers)); - } - for (auto const& device_identifier : device_identifiers) { - callback(device_identifier); - } - return {}; -} - -DeviceIdentifier const& Access::get_device_identifier(Address address) const -{ - for (auto& device_identifier : m_device_identifiers) { - auto device_address = device_identifier->address(); - if (device_address.domain() == address.domain() - && device_address.bus() == address.bus() - && device_address.device() == address.device() - && device_address.function() == address.function()) { - return device_identifier; - } - } - VERIFY_NOT_REACHED(); -} - -void Access::write8_field(DeviceIdentifier const& identifier, u32 field, u8 value) -{ - VERIFY(identifier.operation_lock().is_locked()); - SpinlockLocker locker(m_access_lock); - VERIFY(m_host_controllers.contains(identifier.address().domain())); - auto& controller = *m_host_controllers.get(identifier.address().domain()).value(); - controller.write8_field(identifier.address().bus(), identifier.address().device(), identifier.address().function(), field, value); -} -void Access::write16_field(DeviceIdentifier const& identifier, u32 field, u16 value) -{ - VERIFY(identifier.operation_lock().is_locked()); - SpinlockLocker locker(m_access_lock); - VERIFY(m_host_controllers.contains(identifier.address().domain())); - auto& controller = *m_host_controllers.get(identifier.address().domain()).value(); - controller.write16_field(identifier.address().bus(), identifier.address().device(), identifier.address().function(), field, value); -} -void Access::write32_field(DeviceIdentifier const& identifier, u32 field, u32 value) -{ - VERIFY(identifier.operation_lock().is_locked()); - SpinlockLocker locker(m_access_lock); - VERIFY(m_host_controllers.contains(identifier.address().domain())); - auto& controller = *m_host_controllers.get(identifier.address().domain()).value(); - controller.write32_field(identifier.address().bus(), identifier.address().device(), identifier.address().function(), field, value); -} - -u8 Access::read8_field(DeviceIdentifier const& identifier, RegisterOffset field) -{ - VERIFY(identifier.operation_lock().is_locked()); - return read8_field(identifier, to_underlying(field)); -} -u16 Access::read16_field(DeviceIdentifier const& identifier, RegisterOffset field) -{ - VERIFY(identifier.operation_lock().is_locked()); - return read16_field(identifier, to_underlying(field)); -} - -u8 Access::read8_field(DeviceIdentifier const& identifier, u32 field) -{ - VERIFY(identifier.operation_lock().is_locked()); - SpinlockLocker locker(m_access_lock); - VERIFY(m_host_controllers.contains(identifier.address().domain())); - auto& controller = *m_host_controllers.get(identifier.address().domain()).value(); - return controller.read8_field(identifier.address().bus(), identifier.address().device(), identifier.address().function(), field); -} -u16 Access::read16_field(DeviceIdentifier const& identifier, u32 field) -{ - VERIFY(identifier.operation_lock().is_locked()); - SpinlockLocker locker(m_access_lock); - VERIFY(m_host_controllers.contains(identifier.address().domain())); - auto& controller = *m_host_controllers.get(identifier.address().domain()).value(); - return controller.read16_field(identifier.address().bus(), identifier.address().device(), identifier.address().function(), field); -} -u32 Access::read32_field(DeviceIdentifier const& identifier, u32 field) -{ - VERIFY(identifier.operation_lock().is_locked()); - SpinlockLocker locker(m_access_lock); - VERIFY(m_host_controllers.contains(identifier.address().domain())); - auto& controller = *m_host_controllers.get(identifier.address().domain()).value(); - return controller.read32_field(identifier.address().bus(), identifier.address().device(), identifier.address().function(), field); -} - -} diff --git a/Kernel/Bus/PCI/Access.h b/Kernel/Bus/PCI/Access.h deleted file mode 100644 index 93b1fa0da24..00000000000 --- a/Kernel/Bus/PCI/Access.h +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright (c) 2020, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace Kernel::PCI { - -class Access { -public: - static bool initialize_for_multiple_pci_domains(PhysicalAddress mcfg_table); - -#if ARCH(X86_64) - static bool initialize_for_one_pci_domain(); -#endif - - ErrorOr fast_enumerate(Function&) const; - void configure_pci_space(PCIConfiguration&); - void rescan_hardware(); - - static Access& the(); - static bool is_initialized(); - static bool is_disabled(); - static bool is_hardware_disabled(); - - void write8_field(DeviceIdentifier const&, u32 field, u8 value); - void write16_field(DeviceIdentifier const&, u32 field, u16 value); - void write32_field(DeviceIdentifier const&, u32 field, u32 value); - u8 read8_field(DeviceIdentifier const&, u32 field); - u16 read16_field(DeviceIdentifier const&, u32 field); - u32 read32_field(DeviceIdentifier const&, u32 field); - - // FIXME: Remove this once we can use PCI::Capability with inline buffer - // so we don't need this method - DeviceIdentifier const& get_device_identifier(Address address) const; - - Spinlock const& scan_lock() const { return m_scan_lock; } - RecursiveSpinlock const& access_lock() const { return m_access_lock; } - - ErrorOr add_host_controller_and_scan_for_devices(NonnullOwnPtr); - -private: - friend void PCI::initialize(); - - u8 read8_field(DeviceIdentifier const&, RegisterOffset field); - u16 read16_field(DeviceIdentifier const&, RegisterOffset field); - - void add_host_controller(NonnullOwnPtr); - bool find_and_register_pci_host_bridges_from_acpi_mcfg_table(PhysicalAddress mcfg); - Access(); - - mutable RecursiveSpinlock m_access_lock {}; - mutable Spinlock m_scan_lock {}; - - HashMap> m_host_controllers; - Vector> m_device_identifiers; -}; -} diff --git a/Kernel/Bus/PCI/BarMapping.h b/Kernel/Bus/PCI/BarMapping.h deleted file mode 100644 index 38e60874b52..00000000000 --- a/Kernel/Bus/PCI/BarMapping.h +++ /dev/null @@ -1,83 +0,0 @@ -/* - * Copyright (c) 2024, Leon Albrecht - * Copyright (c) 2022, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include - -namespace Kernel::PCI { - -inline ErrorOr get_bar_address(DeviceIdentifier const& device, HeaderType0BaseRegister bar) -{ - u64 pci_bar_value = get_BAR(device, bar); - auto pci_bar_space_type = get_BAR_space_type(pci_bar_value); - - if (pci_bar_space_type == BARSpaceType::IOSpace) - return EIO; - - if (pci_bar_space_type == BARSpaceType::Memory64BitSpace) { - // FIXME: In theory, BAR5 cannot be assigned to 64 bit as it is the last one... - // however, there might be 64 bit BAR5 for real bare metal hardware, so remove this - // if it makes a problem. - if (bar == HeaderType0BaseRegister::BAR5) { - return Error::from_errno(EINVAL); - } - u64 next_pci_bar_value = get_BAR(device, static_cast(to_underlying(bar) + 1)); - pci_bar_value |= next_pci_bar_value << 32; - - return PhysicalAddress { pci_bar_value & bar_address_mask }; - } - - return PhysicalAddress { pci_bar_value & bar_address_mask }; -} - -template -inline ErrorOr> map_bar(DeviceIdentifier const& device, HeaderType0BaseRegister bar, size_t size, Memory::Region::Access access = IsConst ? Memory::Region::Access::Read : Memory::Region::Access::ReadWrite) -{ - u64 pci_bar_value = get_BAR(device, bar); - auto pci_bar_space_type = get_BAR_space_type(pci_bar_value); - - if (pci_bar_space_type == PCI::BARSpaceType::IOSpace) - return EIO; - - auto bar_address = TRY(get_bar_address(device, bar)); - - auto pci_bar_space_size = PCI::get_BAR_space_size(device, bar); - if (pci_bar_space_size < size) - return Error::from_errno(EIO); - - if (pci_bar_space_type == PCI::BARSpaceType::Memory32BitSpace && Checked::addition_would_overflow(bar_address.get(), size)) - return Error::from_errno(EOVERFLOW); - if (pci_bar_space_type == PCI::BARSpaceType::Memory16BitSpace && Checked::addition_would_overflow(bar_address.get(), size)) - return Error::from_errno(EOVERFLOW); - if (pci_bar_space_type == PCI::BARSpaceType::Memory64BitSpace && Checked::addition_would_overflow(bar_address.get(), size)) - return Error::from_errno(EOVERFLOW); - - return Memory::map_typed(bar_address, size, access); -} - -template -inline ErrorOr> map_bar(DeviceIdentifier const& device, HeaderType0BaseRegister bar, Memory::Region::Access access = IsConst ? Memory::Region::Access::Read : Memory::Region::Access::ReadWrite) -{ - return map_bar(device, bar, PCI::get_BAR_space_size(device, bar), access); -} - -template -inline ErrorOr>> adopt_new_nonnull_own_bar_mapping(DeviceIdentifier const& device, HeaderType0BaseRegister bar, size_t size, Memory::Region::Access access = IsConst ? Memory::Region::Access::Read : Memory::Region::Access::ReadWrite) -{ - return adopt_nonnull_own_or_enomem(new (nothrow) Memory::TypedMapping(TRY(map_bar(device, bar, size, access)))); -} - -template -inline ErrorOr>> adopt_new_nonnull_own_bar_mapping(DeviceIdentifier const& device, HeaderType0BaseRegister bar, Memory::Region::Access access = IsConst ? Memory::Region::Access::Read : Memory::Region::Access::ReadWrite) -{ - return adopt_new_nonnull_own_bar_mapping(device, bar, PCI::get_BAR_space_size(device, bar), access); -} - -} diff --git a/Kernel/Bus/PCI/Controller/HostController.cpp b/Kernel/Bus/PCI/Controller/HostController.cpp deleted file mode 100644 index f9863371705..00000000000 --- a/Kernel/Bus/PCI/Controller/HostController.cpp +++ /dev/null @@ -1,319 +0,0 @@ -/* - * Copyright (c) 2022, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include -#include - -namespace Kernel::PCI { - -HostController::HostController(PCI::Domain const& domain) - : m_domain(domain) - , m_enumerated_buses(Bitmap::create(256, false).release_value_but_fixme_should_propagate_errors()) -{ -} - -void HostController::write8_field(BusNumber bus, DeviceNumber device, FunctionNumber function, u32 field, u8 value) -{ - SpinlockLocker locker(m_access_lock); - write8_field_locked(bus, device, function, field, value); -} - -void HostController::write16_field(BusNumber bus, DeviceNumber device, FunctionNumber function, u32 field, u16 value) -{ - SpinlockLocker locker(m_access_lock); - write16_field_locked(bus, device, function, field, value); -} - -void HostController::write32_field(BusNumber bus, DeviceNumber device, FunctionNumber function, u32 field, u32 value) -{ - SpinlockLocker locker(m_access_lock); - write32_field_locked(bus, device, function, field, value); -} - -u8 HostController::read8_field(BusNumber bus, DeviceNumber device, FunctionNumber function, u32 field) -{ - SpinlockLocker locker(m_access_lock); - return read8_field_locked(bus, device, function, field); -} - -u16 HostController::read16_field(BusNumber bus, DeviceNumber device, FunctionNumber function, u32 field) -{ - SpinlockLocker locker(m_access_lock); - return read16_field_locked(bus, device, function, field); -} - -u32 HostController::read32_field(BusNumber bus, DeviceNumber device, FunctionNumber function, u32 field) -{ - SpinlockLocker locker(m_access_lock); - return read32_field_locked(bus, device, function, field); -} - -UNMAP_AFTER_INIT Optional HostController::get_capabilities_pointer_for_function(BusNumber bus, DeviceNumber device, FunctionNumber function) -{ - if (read16_field(bus, device, function, PCI::RegisterOffset::STATUS) & (1 << 4)) { - return read8_field(bus, device, function, PCI::RegisterOffset::CAPABILITIES_POINTER); - } - return {}; -} - -UNMAP_AFTER_INIT Vector HostController::get_capabilities_for_function(BusNumber bus, DeviceNumber device, FunctionNumber function) -{ - auto capabilities_pointer = get_capabilities_pointer_for_function(bus, device, function); - if (!capabilities_pointer.has_value()) { - return {}; - } - Vector capabilities; - auto capability_pointer = capabilities_pointer.value(); - while (capability_pointer != 0) { - u16 capability_header = read16_field(bus, device, function, capability_pointer); - u8 capability_id = capability_header & 0xff; - - // FIXME: Don't attach a PCI address to a capability object - capabilities.append({ Address(domain_number(), bus.value(), device.value(), function.value()), capability_id, capability_pointer }); - capability_pointer = capability_header >> 8; - } - return capabilities; -} - -void HostController::write8_field(BusNumber bus, DeviceNumber device, FunctionNumber function, PCI::RegisterOffset field, u8 value) -{ - write8_field(bus, device, function, to_underlying(field), value); -} - -void HostController::write16_field(BusNumber bus, DeviceNumber device, FunctionNumber function, RegisterOffset field, u16 value) -{ - write16_field(bus, device, function, to_underlying(field), value); -} - -void HostController::write32_field(BusNumber bus, DeviceNumber device, FunctionNumber function, RegisterOffset field, u32 value) -{ - write32_field(bus, device, function, to_underlying(field), value); -} - -u8 HostController::read8_field(BusNumber bus, DeviceNumber device, FunctionNumber function, PCI::RegisterOffset field) -{ - return read8_field(bus, device, function, to_underlying(field)); -} -u16 HostController::read16_field(BusNumber bus, DeviceNumber device, FunctionNumber function, PCI::RegisterOffset field) -{ - return read16_field(bus, device, function, to_underlying(field)); -} - -UNMAP_AFTER_INIT void HostController::enumerate_functions(Function const& callback, Function& post_bridge_callback, BusNumber bus, DeviceNumber device, FunctionNumber function, bool recursive_search_into_bridges) -{ - dbgln_if(PCI_DEBUG, "PCI: Enumerating function, bus={}, device={}, function={}", bus, device, function); - Address address(domain_number(), bus.value(), device.value(), function.value()); - auto pci_class = (read8_field(bus, device, function, PCI::RegisterOffset::CLASS) << 8u) | read8_field(bus, device, function, PCI::RegisterOffset::SUBCLASS); - - HardwareID id = { read16_field(bus, device, function, PCI::RegisterOffset::VENDOR_ID), read16_field(bus, device, function, PCI::RegisterOffset::DEVICE_ID) }; - ClassCode class_code = read8_field(bus, device, function, PCI::RegisterOffset::CLASS); - SubclassCode subclass_code = read8_field(bus, device, function, PCI::RegisterOffset::SUBCLASS); - ProgrammingInterface prog_if = read8_field(bus, device, function, PCI::RegisterOffset::PROG_IF); - RevisionID revision_id = read8_field(bus, device, function, PCI::RegisterOffset::REVISION_ID); - SubsystemID subsystem_id = read16_field(bus, device, function, PCI::RegisterOffset::SUBSYSTEM_ID); - SubsystemVendorID subsystem_vendor_id = read16_field(bus, device, function, PCI::RegisterOffset::SUBSYSTEM_VENDOR_ID); - InterruptLine interrupt_line = read8_field(bus, device, function, PCI::RegisterOffset::INTERRUPT_LINE); - InterruptPin interrupt_pin = read8_field(bus, device, function, PCI::RegisterOffset::INTERRUPT_PIN); - auto capabilities = get_capabilities_for_function(bus, device, function); - auto device_ientifier = EnumerableDeviceIdentifier { address, id, revision_id, class_code, subclass_code, prog_if, subsystem_id, subsystem_vendor_id, interrupt_line, interrupt_pin, capabilities }; - callback(device_ientifier); - - if (pci_class == (to_underlying(PCI::ClassID::Bridge) << 8 | to_underlying(PCI::Bridge::SubclassID::PCI_TO_PCI)) - && recursive_search_into_bridges - && (!m_enumerated_buses.get(read8_field(bus, device, function, PCI::RegisterOffset::SECONDARY_BUS)))) { - u8 secondary_bus = read8_field(bus, device, function, PCI::RegisterOffset::SECONDARY_BUS); - dbgln_if(PCI_DEBUG, "PCI: Found secondary bus: {}", secondary_bus); - VERIFY(secondary_bus != bus); - m_enumerated_buses.set(secondary_bus, true); - enumerate_bus(callback, post_bridge_callback, secondary_bus, recursive_search_into_bridges); - - if (post_bridge_callback) - post_bridge_callback(device_ientifier); - } -} - -UNMAP_AFTER_INIT void HostController::enumerate_device(Function const& callback, Function& post_bridge_callback, BusNumber bus, DeviceNumber device, bool recursive_search_into_bridges) -{ - dbgln_if(PCI_DEBUG, "PCI: Enumerating device in bus={}, device={}", bus, device); - if (read16_field(bus, device, 0, PCI::RegisterOffset::VENDOR_ID) == PCI::none_value) - return; - enumerate_functions(callback, post_bridge_callback, bus, device, 0, recursive_search_into_bridges); - if (!(read8_field(bus, device, 0, PCI::RegisterOffset::HEADER_TYPE) & 0x80)) - return; - for (u8 function = 1; function < 8; ++function) { - if (read16_field(bus, device, function, PCI::RegisterOffset::VENDOR_ID) != PCI::none_value) - enumerate_functions(callback, post_bridge_callback, bus, device, function, recursive_search_into_bridges); - } -} - -UNMAP_AFTER_INIT void HostController::enumerate_bus(Function const& callback, Function& post_bridge_callback, BusNumber bus, bool recursive_search_into_bridges) -{ - dbgln_if(PCI_DEBUG, "PCI: Enumerating bus {}", bus); - for (u8 device = 0; device < 32; ++device) - enumerate_device(callback, post_bridge_callback, bus, device, recursive_search_into_bridges); -} - -UNMAP_AFTER_INIT void HostController::enumerate_attached_devices(Function callback, Function post_bridge_callback) -{ - VERIFY(Access::the().access_lock().is_locked()); - VERIFY(Access::the().scan_lock().is_locked()); - m_enumerated_buses.fill(false); - // First scan bus 0. Find any device on that bus, and if it's a PCI-to-PCI - // bridge, recursively scan it too. - m_enumerated_buses.set(m_domain.start_bus(), true); - enumerate_bus(callback, post_bridge_callback, m_domain.start_bus(), true); - - // Handle Multiple PCI host bridges on bus 0, device 0, functions 1-7 (function 0 - // is the main host bridge). - // If we happen to miss some PCI buses because they are not reachable through - // recursive PCI-to-PCI bridges starting from bus 0, we might find them here. - if ((read8_field(0, 0, 0, PCI::RegisterOffset::HEADER_TYPE) & 0x80) != 0) { - for (int bus_as_function_number = 1; bus_as_function_number < 8; ++bus_as_function_number) { - if (read16_field(0, 0, bus_as_function_number, PCI::RegisterOffset::VENDOR_ID) == PCI::none_value) - continue; - if (read8_field(0, 0, bus_as_function_number, PCI::RegisterOffset::CLASS) != to_underlying(PCI::ClassID::Bridge)) - continue; - if (Checked::addition_would_overflow(m_domain.start_bus(), bus_as_function_number)) - break; - if (m_enumerated_buses.get(m_domain.start_bus() + bus_as_function_number)) - continue; - enumerate_bus(callback, post_bridge_callback, m_domain.start_bus() + bus_as_function_number, false); - m_enumerated_buses.set(m_domain.start_bus() + bus_as_function_number, true); - } - } -} - -void HostController::configure_attached_devices(PCIConfiguration& config) -{ - // First, Assign PCI-to-PCI bridge bus numbering - u8 bus_id = 0; - enumerate_attached_devices([this, &bus_id](EnumerableDeviceIdentifier const& device_identifier) { - // called for each PCI device encountered (including bridges) - if (read8_field(device_identifier.address().bus(), device_identifier.address().device(), device_identifier.address().function(), PCI::RegisterOffset::CLASS) != PCI::ClassID::Bridge) - return; - if (read8_field(device_identifier.address().bus(), device_identifier.address().device(), device_identifier.address().function(), PCI::RegisterOffset::SUBCLASS) != PCI::Bridge::SubclassID::PCI_TO_PCI) - return; - write8_field(device_identifier.address().bus(), device_identifier.address().device(), device_identifier.address().function(), PCI::RegisterOffset::SECONDARY_BUS, ++bus_id); - write8_field(device_identifier.address().bus(), device_identifier.address().device(), device_identifier.address().function(), PCI::RegisterOffset::SUBORDINATE_BUS, 0xFF); }, - [this, &bus_id](EnumerableDeviceIdentifier const& device_identifier) { - // called after a bridge was recursively enumerated - write8_field(device_identifier.address().bus(), device_identifier.address().device(), device_identifier.address().function(), PCI::RegisterOffset::SUBORDINATE_BUS, bus_id); - }); - - // Second, Assign BAR addresses & Interrupt numbers - // TODO: We currently naively assign addresses bump-allocator style - Switch to a proper allocator if this is not good enough - enumerate_attached_devices([this, &config](EnumerableDeviceIdentifier const& device_identifier) { - // device-generic handling - auto header_type = read8_field(device_identifier.address().bus(), device_identifier.address().device(), device_identifier.address().function(), PCI::RegisterOffset::HEADER_TYPE); - auto const max_bar = (header_type == 0) ? RegisterOffset::BAR5 : RegisterOffset::BAR1; - for (u32 bar_offset = to_underlying(RegisterOffset::BAR0); bar_offset <= to_underlying(max_bar); bar_offset += 4) { - auto bar_value = read32_field(device_identifier.address().bus(), device_identifier.address().device(), device_identifier.address().function(), bar_offset); - auto bar_space_type = get_BAR_space_type(bar_value); - auto bar_prefetchable = (bar_value >> 3) & 1; - if (bar_space_type != BARSpaceType::Memory32BitSpace && bar_space_type != BARSpaceType::Memory64BitSpace) - continue; // We only support memory-mapped BAR configuration at the moment - if (bar_space_type == BARSpaceType::Memory32BitSpace) { - write32_field(device_identifier.address().bus(), device_identifier.address().device(), device_identifier.address().function(), bar_offset, 0xFFFFFFFF); - auto bar_size = read32_field(device_identifier.address().bus(), device_identifier.address().device(), device_identifier.address().function(), bar_offset); - write32_field(device_identifier.address().bus(), device_identifier.address().device(), device_identifier.address().function(), bar_offset, bar_value); - bar_size &= bar_address_mask; - bar_size = (~bar_size) + 1; - if (bar_size == 0) - continue; - auto mmio_32bit_address = align_up_to(config.mmio_32bit_base, bar_size); - if (mmio_32bit_address + bar_size <= config.mmio_32bit_end) { - write32_field(device_identifier.address().bus(), device_identifier.address().device(), device_identifier.address().function(), bar_offset, mmio_32bit_address); - config.mmio_32bit_base = mmio_32bit_address + bar_size; - continue; - } - auto mmio_64bit_address = align_up_to(config.mmio_64bit_base, bar_size); - if (bar_prefetchable && mmio_64bit_address + bar_size <= config.mmio_64bit_end && mmio_64bit_address + bar_size <= NumericLimits::max()) { - write32_field(device_identifier.address().bus(), device_identifier.address().device(), device_identifier.address().function(), bar_offset, mmio_64bit_address); - config.mmio_64bit_base = mmio_64bit_address + bar_size; - continue; - } - dmesgln("PCI: Ran out of 32-bit MMIO address space"); - VERIFY_NOT_REACHED(); - } - // 64-bit space - auto next_bar_value = read32_field(device_identifier.address().bus(), device_identifier.address().device(), device_identifier.address().function(), bar_offset + 4); - write32_field(device_identifier.address().bus(), device_identifier.address().device(), device_identifier.address().function(), bar_offset, 0xFFFFFFFF); - write32_field(device_identifier.address().bus(), device_identifier.address().device(), device_identifier.address().function(), bar_offset + 4, 0xFFFFFFFF); - u64 bar_size = read32_field(device_identifier.address().bus(), device_identifier.address().device(), device_identifier.address().function(), bar_offset); - bar_size |= (u64)read32_field(device_identifier.address().bus(), device_identifier.address().device(), device_identifier.address().function(), bar_offset + 4) << 32; - write32_field(device_identifier.address().bus(), device_identifier.address().device(), device_identifier.address().function(), bar_offset, bar_value); - write32_field(device_identifier.address().bus(), device_identifier.address().device(), device_identifier.address().function(), bar_offset + 4, next_bar_value); - bar_size &= bar_address_mask; - bar_size = (~bar_size) + 1; - if (bar_size == 0) { - bar_offset += 4; - continue; - } - auto mmio_64bit_address = align_up_to(config.mmio_64bit_base, bar_size); - if (bar_prefetchable && mmio_64bit_address + bar_size <= config.mmio_64bit_end) { - write32_field(device_identifier.address().bus(), device_identifier.address().device(), device_identifier.address().function(), bar_offset, mmio_64bit_address & 0xFFFFFFFF); - write32_field(device_identifier.address().bus(), device_identifier.address().device(), device_identifier.address().function(), bar_offset + 4, mmio_64bit_address >> 32); - config.mmio_64bit_base = mmio_64bit_address + bar_size; - bar_offset += 4; - continue; - } - auto mmio_32bit_address = align_up_to(config.mmio_32bit_base, bar_size); - if (mmio_32bit_address + bar_size <= config.mmio_32bit_end) { - write32_field(device_identifier.address().bus(), device_identifier.address().device(), device_identifier.address().function(), bar_offset, mmio_32bit_address & 0xFFFFFFFF); - write32_field(device_identifier.address().bus(), device_identifier.address().device(), device_identifier.address().function(), bar_offset + 4, mmio_32bit_address >> 32); - config.mmio_32bit_base = mmio_32bit_address + bar_size; - bar_offset += 4; - continue; - } - dmesgln("PCI: Ran out of 64-bit MMIO address space"); - VERIFY_NOT_REACHED(); - } - // enable memory space - auto command_value = read16_field(device_identifier.address().bus(), device_identifier.address().device(), device_identifier.address().function(), PCI::RegisterOffset::COMMAND); - command_value |= 1 << 1; // memory space enable - write16_field(device_identifier.address().bus(), device_identifier.address().device(), device_identifier.address().function(), PCI::RegisterOffset::COMMAND, command_value); - // assign interrupt number - auto interrupt_pin = read8_field(device_identifier.address().bus(), device_identifier.address().device(), device_identifier.address().function(), PCI::RegisterOffset::INTERRUPT_PIN); - auto masked_identifier = PCIInterruptSpecifier{ - .interrupt_pin = interrupt_pin, - .function = device_identifier.address().function(), - .device = device_identifier.address().device(), - .bus = device_identifier.address().bus() - }; - masked_identifier &= config.interrupt_mask; - auto interrupt_number = config.masked_interrupt_mapping.get(masked_identifier); - if (interrupt_number.has_value()) - write8_field(device_identifier.address().bus(), device_identifier.address().device(), device_identifier.address().function(), PCI::RegisterOffset::INTERRUPT_LINE, interrupt_number.value()); - - if (read8_field(device_identifier.address().bus(), device_identifier.address().device(), device_identifier.address().function(), PCI::RegisterOffset::CLASS) != PCI::ClassID::Bridge) - return; - if (read8_field(device_identifier.address().bus(), device_identifier.address().device(), device_identifier.address().function(), PCI::RegisterOffset::SUBCLASS) != PCI::Bridge::SubclassID::PCI_TO_PCI) - return; - // bridge-specific handling - config.mmio_32bit_base = align_up_to(config.mmio_32bit_base, MiB); - config.mmio_64bit_base = align_up_to(config.mmio_64bit_base, MiB); - write32_field(device_identifier.address().bus(), device_identifier.address().device(), device_identifier.address().function(), PCI::RegisterOffset::MEMORY_BASE, config.mmio_32bit_base >> 16); - write32_field(device_identifier.address().bus(), device_identifier.address().device(), device_identifier.address().function(), PCI::RegisterOffset::PREFETCHABLE_MEMORY_BASE, config.mmio_64bit_base >> 16); - write32_field(device_identifier.address().bus(), device_identifier.address().device(), device_identifier.address().function(), PCI::RegisterOffset::PREFETCHABLE_MEMORY_BASE_UPPER_32_BITS, config.mmio_64bit_base >> 32); }, - [this, &config](EnumerableDeviceIdentifier const& device_identifier) { - // called after a bridge was recursively enumerated - config.mmio_32bit_base = align_up_to(config.mmio_32bit_base, MiB); - config.mmio_64bit_base = align_up_to(config.mmio_64bit_base, MiB); - write32_field(device_identifier.address().bus(), device_identifier.address().device(), device_identifier.address().function(), PCI::RegisterOffset::MEMORY_LIMIT, config.mmio_32bit_base >> 16); - write32_field(device_identifier.address().bus(), device_identifier.address().device(), device_identifier.address().function(), PCI::RegisterOffset::PREFETCHABLE_MEMORY_LIMIT, config.mmio_64bit_base >> 16); - write32_field(device_identifier.address().bus(), device_identifier.address().device(), device_identifier.address().function(), PCI::RegisterOffset::PREFETCHABLE_MEMORY_LIMIT_UPPER_32_BITS, config.mmio_64bit_base >> 32); - // enable bridging - auto command_value = read16_field(device_identifier.address().bus(), device_identifier.address().device(), device_identifier.address().function(), PCI::RegisterOffset::COMMAND); - command_value |= 1 << 2; // enable forwarding of requests by the bridge - write16_field(device_identifier.address().bus(), device_identifier.address().device(), device_identifier.address().function(), PCI::RegisterOffset::COMMAND, command_value); - }); -} - -} diff --git a/Kernel/Bus/PCI/Controller/HostController.h b/Kernel/Bus/PCI/Controller/HostController.h deleted file mode 100644 index bacb2ca9ead..00000000000 --- a/Kernel/Bus/PCI/Controller/HostController.h +++ /dev/null @@ -1,123 +0,0 @@ -/* - * Copyright (c) 2022, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include - -namespace Kernel::PCI { - -AK_TYPEDEF_DISTINCT_ORDERED_ID(u8, BusNumber); -AK_TYPEDEF_DISTINCT_ORDERED_ID(u8, DeviceNumber); -AK_TYPEDEF_DISTINCT_ORDERED_ID(u8, FunctionNumber); - -struct PCIInterruptSpecifier { - u8 interrupt_pin { 0 }; - FunctionNumber function { 0 }; - DeviceNumber device { 0 }; - BusNumber bus { 0 }; - - bool operator==(PCIInterruptSpecifier const& other) const - { - return bus == other.bus && device == other.device && function == other.function && interrupt_pin == other.interrupt_pin; - } - PCIInterruptSpecifier operator&(PCIInterruptSpecifier other) const - { - return PCIInterruptSpecifier { - .interrupt_pin = static_cast(interrupt_pin & other.interrupt_pin), - .function = function.value() & other.function.value(), - .device = device.value() & other.device.value(), - .bus = bus.value() & other.bus.value(), - }; - } - - PCIInterruptSpecifier& operator&=(PCIInterruptSpecifier const& other) - { - *this = *this & other; - return *this; - } -}; - -} -namespace AK { -template<> -struct Traits : public DefaultTraits { - static unsigned hash(Kernel::PCI::PCIInterruptSpecifier value) - { - return int_hash(value.bus.value() << 24 | value.device.value() << 16 | value.function.value() << 8 | value.interrupt_pin); - } -}; - -} -namespace Kernel::PCI { - -struct PCIConfiguration { - FlatPtr mmio_32bit_base { 0 }; - FlatPtr mmio_32bit_end { 0 }; - FlatPtr mmio_64bit_base { 0 }; - FlatPtr mmio_64bit_end { 0 }; - // The keys contains the bus, device & function at the same offsets as OpenFirmware PCI addresses, - // with the least significant 8 bits being the interrupt pin. - HashMap masked_interrupt_mapping; - PCIInterruptSpecifier interrupt_mask; -}; - -class HostController { -public: - virtual ~HostController() = default; - - void write8_field(BusNumber, DeviceNumber, FunctionNumber, u32 field, u8 value); - void write16_field(BusNumber, DeviceNumber, FunctionNumber, u32 field, u16 value); - void write32_field(BusNumber, DeviceNumber, FunctionNumber, u32 field, u32 value); - - u8 read8_field(BusNumber, DeviceNumber, FunctionNumber, u32 field); - u16 read16_field(BusNumber, DeviceNumber, FunctionNumber, u32 field); - u32 read32_field(BusNumber, DeviceNumber, FunctionNumber, u32 field); - - u32 domain_number() const { return m_domain.domain_number(); } - - void enumerate_attached_devices(Function callback, Function post_bridge_callback = nullptr); - void configure_attached_devices(PCIConfiguration&); - -private: - void enumerate_bus(Function const& callback, Function& post_bridge_callback, BusNumber, bool recursive_search_into_bridges); - void enumerate_functions(Function const& callback, Function& post_bridge_callback, BusNumber, DeviceNumber, FunctionNumber, bool recursive_search_into_bridges); - void enumerate_device(Function const& callback, Function& post_bridge_callback, BusNumber bus, DeviceNumber device, bool recursive_search_into_bridges); - - void write8_field(BusNumber, DeviceNumber, FunctionNumber, RegisterOffset field, u8 value); - void write16_field(BusNumber, DeviceNumber, FunctionNumber, RegisterOffset field, u16 value); - void write32_field(BusNumber, DeviceNumber, FunctionNumber, RegisterOffset field, u32 value); - - u8 read8_field(BusNumber, DeviceNumber, FunctionNumber, RegisterOffset field); - u16 read16_field(BusNumber, DeviceNumber, FunctionNumber, RegisterOffset field); - - Optional get_capabilities_pointer_for_function(BusNumber, DeviceNumber, FunctionNumber); - Vector get_capabilities_for_function(BusNumber, DeviceNumber, FunctionNumber); - -protected: - virtual void write8_field_locked(BusNumber, DeviceNumber, FunctionNumber, u32 field, u8 value) = 0; - virtual void write16_field_locked(BusNumber, DeviceNumber, FunctionNumber, u32 field, u16 value) = 0; - virtual void write32_field_locked(BusNumber, DeviceNumber, FunctionNumber, u32 field, u32 value) = 0; - - virtual u8 read8_field_locked(BusNumber, DeviceNumber, FunctionNumber, u32 field) = 0; - virtual u16 read16_field_locked(BusNumber, DeviceNumber, FunctionNumber, u32 field) = 0; - virtual u32 read32_field_locked(BusNumber, DeviceNumber, FunctionNumber, u32 field) = 0; - - explicit HostController(PCI::Domain const& domain); - - const PCI::Domain m_domain; - - Spinlock m_access_lock; - -private: - Bitmap m_enumerated_buses; -}; - -} diff --git a/Kernel/Bus/PCI/Controller/MemoryBackedHostBridge.cpp b/Kernel/Bus/PCI/Controller/MemoryBackedHostBridge.cpp deleted file mode 100644 index ad48017224d..00000000000 --- a/Kernel/Bus/PCI/Controller/MemoryBackedHostBridge.cpp +++ /dev/null @@ -1,94 +0,0 @@ -/* - * Copyright (c) 2022, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include - -namespace Kernel::PCI { - -NonnullOwnPtr MemoryBackedHostBridge::must_create(Domain const& domain, PhysicalAddress start_address) -{ - return adopt_own_if_nonnull(new (nothrow) MemoryBackedHostBridge(domain, start_address)).release_nonnull(); -} - -MemoryBackedHostBridge::MemoryBackedHostBridge(PCI::Domain const& domain, PhysicalAddress start_address) - : HostController(domain) - , m_start_address(start_address) -{ -} - -u8 MemoryBackedHostBridge::read8_field_locked(BusNumber bus, DeviceNumber device, FunctionNumber function, u32 field) -{ - VERIFY(m_access_lock.is_locked()); - VERIFY(field <= 0xfff); - return *((u8 volatile*)(get_device_configuration_memory_mapped_space(bus, device, function).get() + (field & 0xfff))); -} -u16 MemoryBackedHostBridge::read16_field_locked(BusNumber bus, DeviceNumber device, FunctionNumber function, u32 field) -{ - VERIFY(m_access_lock.is_locked()); - VERIFY(field < 0xfff); - u16 data = 0; - ByteReader::load(get_device_configuration_memory_mapped_space(bus, device, function).offset(field & 0xfff).as_ptr(), data); - return data; -} -u32 MemoryBackedHostBridge::read32_field_locked(BusNumber bus, DeviceNumber device, FunctionNumber function, u32 field) -{ - VERIFY(m_access_lock.is_locked()); - VERIFY(field <= 0xffc); - u32 data = 0; - ByteReader::load(get_device_configuration_memory_mapped_space(bus, device, function).offset(field & 0xfff).as_ptr(), data); - return data; -} -void MemoryBackedHostBridge::write8_field_locked(BusNumber bus, DeviceNumber device, FunctionNumber function, u32 field, u8 value) -{ - VERIFY(m_access_lock.is_locked()); - VERIFY(field <= 0xfff); - *((u8 volatile*)(get_device_configuration_memory_mapped_space(bus, device, function).get() + (field & 0xfff))) = value; -} -void MemoryBackedHostBridge::write16_field_locked(BusNumber bus, DeviceNumber device, FunctionNumber function, u32 field, u16 value) -{ - VERIFY(m_access_lock.is_locked()); - VERIFY(field < 0xfff); - ByteReader::store(get_device_configuration_memory_mapped_space(bus, device, function).offset(field & 0xfff).as_ptr(), value); -} -void MemoryBackedHostBridge::write32_field_locked(BusNumber bus, DeviceNumber device, FunctionNumber function, u32 field, u32 value) -{ - VERIFY(m_access_lock.is_locked()); - VERIFY(field <= 0xffc); - ByteReader::store(get_device_configuration_memory_mapped_space(bus, device, function).offset(field & 0xfff).as_ptr(), value); -} - -void MemoryBackedHostBridge::map_bus_region(BusNumber bus) -{ - VERIFY(m_access_lock.is_locked()); - if (m_mapped_bus == bus && m_mapped_bus_region) - return; - auto bus_base_address = determine_memory_mapped_bus_base_address(bus); - auto region_or_error = MM.allocate_mmio_kernel_region(bus_base_address, memory_range_per_bus, "PCI ECAM"sv, Memory::Region::Access::ReadWrite); - // FIXME: Find a way to propagate error from here. - if (region_or_error.is_error()) - VERIFY_NOT_REACHED(); - m_mapped_bus_region = region_or_error.release_value(); - m_mapped_bus = bus; - dbgln_if(PCI_DEBUG, "PCI: New PCI ECAM Mapped region for bus {} @ {} {}", bus, m_mapped_bus_region->vaddr(), m_mapped_bus_region->physical_page(0)->paddr()); -} - -VirtualAddress MemoryBackedHostBridge::get_device_configuration_memory_mapped_space(BusNumber bus, DeviceNumber device, FunctionNumber function) -{ - VERIFY(m_access_lock.is_locked()); - map_bus_region(bus); - return m_mapped_bus_region->vaddr().offset(mmio_device_space_size * function.value() + (mmio_device_space_size * to_underlying(Limits::MaxFunctionsPerDevice)) * device.value()); -} - -PhysicalAddress MemoryBackedHostBridge::determine_memory_mapped_bus_base_address(BusNumber bus) const -{ - auto start_bus = min(bus.value(), m_domain.start_bus()); - return m_start_address.offset(memory_range_per_bus * (bus.value() - start_bus)); -} - -} diff --git a/Kernel/Bus/PCI/Controller/MemoryBackedHostBridge.h b/Kernel/Bus/PCI/Controller/MemoryBackedHostBridge.h deleted file mode 100644 index edf4a211308..00000000000 --- a/Kernel/Bus/PCI/Controller/MemoryBackedHostBridge.h +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright (c) 2022, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include - -namespace Kernel::PCI { - -class MemoryBackedHostBridge : public HostController { -public: - static NonnullOwnPtr must_create(Domain const&, PhysicalAddress); - -protected: - virtual void write8_field_locked(BusNumber, DeviceNumber, FunctionNumber, u32 field, u8 value) override; - virtual void write16_field_locked(BusNumber, DeviceNumber, FunctionNumber, u32 field, u16 value) override; - virtual void write32_field_locked(BusNumber, DeviceNumber, FunctionNumber, u32 field, u32 value) override; - - virtual u8 read8_field_locked(BusNumber, DeviceNumber, FunctionNumber, u32 field) override; - virtual u16 read16_field_locked(BusNumber, DeviceNumber, FunctionNumber, u32 field) override; - virtual u32 read32_field_locked(BusNumber, DeviceNumber, FunctionNumber, u32 field) override; - - MemoryBackedHostBridge(PCI::Domain const&, PhysicalAddress); - - // Memory-mapped access operations - void map_bus_region(BusNumber); - VirtualAddress get_device_configuration_memory_mapped_space(BusNumber, DeviceNumber, FunctionNumber); - PhysicalAddress determine_memory_mapped_bus_base_address(BusNumber) const; - - // Data-members for accessing Memory mapped PCI devices' configuration spaces - BusNumber m_mapped_bus { 0 }; - OwnPtr m_mapped_bus_region; - PhysicalAddress m_start_address; -}; - -} diff --git a/Kernel/Bus/PCI/Controller/VolumeManagementDevice.cpp b/Kernel/Bus/PCI/Controller/VolumeManagementDevice.cpp deleted file mode 100644 index 7e311a01b6a..00000000000 --- a/Kernel/Bus/PCI/Controller/VolumeManagementDevice.cpp +++ /dev/null @@ -1,87 +0,0 @@ -/* - * Copyright (c) 2022, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include - -namespace Kernel::PCI { - -static Atomic s_vmd_pci_domain_number = 0x10000; - -NonnullOwnPtr VolumeManagementDevice::must_create(PCI::DeviceIdentifier const& device_identifier) -{ - SpinlockLocker locker(device_identifier.operation_lock()); - u8 start_bus = 0; - switch ((PCI::read16_locked(device_identifier, static_cast(0x44)) >> 8) & 0x3) { - case 0: - break; - case 1: - start_bus = 128; - break; - case 2: - start_bus = 224; - break; - default: - dbgln("VMD @ {}: Unknown bus offset option was set to {}", device_identifier.address(), - ((PCI::read16_locked(device_identifier, static_cast(0x44)) >> 8) & 0x3)); - VERIFY_NOT_REACHED(); - } - - // FIXME: The end bus might not be 255, so we actually need to check it with the - // resource size of BAR0. - dbgln("VMD Host bridge @ {}: Start bus at {}, end bus {}", device_identifier.address(), start_bus, 0xff); - PCI::Domain domain { s_vmd_pci_domain_number++, start_bus, 0xff }; - auto start_address = PCI::get_bar_address(device_identifier, PCI::HeaderType0BaseRegister::BAR0).release_value_but_fixme_should_propagate_errors(); - return adopt_own_if_nonnull(new (nothrow) VolumeManagementDevice(domain, start_address)).release_nonnull(); -} - -void VolumeManagementDevice::write8_field_locked(BusNumber bus, DeviceNumber device, FunctionNumber function, u32 field, u8 value) -{ - SpinlockLocker locker(m_config_lock); - // Note: We must write then read to ensure completion before returning. - MemoryBackedHostBridge::write8_field_locked(bus, device, function, field, value); - MemoryBackedHostBridge::read8_field_locked(bus, device, function, field); -} -void VolumeManagementDevice::write16_field_locked(BusNumber bus, DeviceNumber device, FunctionNumber function, u32 field, u16 value) -{ - SpinlockLocker locker(m_config_lock); - // Note: We must write then read to ensure completion before returning. - MemoryBackedHostBridge::write16_field_locked(bus, device, function, field, value); - MemoryBackedHostBridge::read16_field_locked(bus, device, function, field); -} -void VolumeManagementDevice::write32_field_locked(BusNumber bus, DeviceNumber device, FunctionNumber function, u32 field, u32 value) -{ - SpinlockLocker locker(m_config_lock); - // Note: We must write then read to ensure completion before returning. - MemoryBackedHostBridge::write32_field_locked(bus, device, function, field, value); - MemoryBackedHostBridge::read32_field_locked(bus, device, function, field); -} - -u8 VolumeManagementDevice::read8_field_locked(BusNumber bus, DeviceNumber device, FunctionNumber function, u32 field) -{ - SpinlockLocker locker(m_config_lock); - return MemoryBackedHostBridge::read8_field_locked(bus, device, function, field); -} -u16 VolumeManagementDevice::read16_field_locked(BusNumber bus, DeviceNumber device, FunctionNumber function, u32 field) -{ - SpinlockLocker locker(m_config_lock); - return MemoryBackedHostBridge::read16_field_locked(bus, device, function, field); -} -u32 VolumeManagementDevice::read32_field_locked(BusNumber bus, DeviceNumber device, FunctionNumber function, u32 field) -{ - SpinlockLocker locker(m_config_lock); - return MemoryBackedHostBridge::read32_field_locked(bus, device, function, field); -} - -VolumeManagementDevice::VolumeManagementDevice(PCI::Domain const& domain, PhysicalAddress start_address) - : MemoryBackedHostBridge(domain, start_address) -{ -} - -} diff --git a/Kernel/Bus/PCI/Controller/VolumeManagementDevice.h b/Kernel/Bus/PCI/Controller/VolumeManagementDevice.h deleted file mode 100644 index 899281d1449..00000000000 --- a/Kernel/Bus/PCI/Controller/VolumeManagementDevice.h +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright (c) 2022, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include - -namespace Kernel::PCI { - -class VolumeManagementDevice final : public MemoryBackedHostBridge { -public: - static NonnullOwnPtr must_create(PCI::DeviceIdentifier const& device_identifier); - -private: - VolumeManagementDevice(PCI::Domain const&, PhysicalAddress); - - virtual void write8_field_locked(BusNumber, DeviceNumber, FunctionNumber, u32 field, u8 value) override; - virtual void write16_field_locked(BusNumber, DeviceNumber, FunctionNumber, u32 field, u16 value) override; - virtual void write32_field_locked(BusNumber, DeviceNumber, FunctionNumber, u32 field, u32 value) override; - virtual u8 read8_field_locked(BusNumber, DeviceNumber, FunctionNumber, u32 field) override; - virtual u16 read16_field_locked(BusNumber, DeviceNumber, FunctionNumber, u32 field) override; - virtual u32 read32_field_locked(BusNumber, DeviceNumber, FunctionNumber, u32 field) override; - - // Note: All read and writes must be done with a spinlock because - // Linux says that CPU might deadlock otherwise if access is not serialized. - Spinlock m_config_lock {}; -}; - -} diff --git a/Kernel/Bus/PCI/Definitions.h b/Kernel/Bus/PCI/Definitions.h deleted file mode 100644 index 49bcc19d108..00000000000 --- a/Kernel/Bus/PCI/Definitions.h +++ /dev/null @@ -1,557 +0,0 @@ -/* - * Copyright (c) 2020, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace Kernel::PCI { - -enum class HeaderType { - Device = 0, - Bridge = 1, -}; - -enum class HeaderType0BaseRegister { - BAR0 = 0, - BAR1, - BAR2, - BAR3, - BAR4, - BAR5, -}; - -enum class BARSpaceType { - IOSpace, - Memory16BitSpace, - Memory32BitSpace, - Memory64BitSpace, -}; - -enum class RegisterOffset : u32 { - VENDOR_ID = 0x00, // word - DEVICE_ID = 0x02, // word - COMMAND = 0x04, // word - STATUS = 0x06, // word - REVISION_ID = 0x08, // byte - PROG_IF = 0x09, // byte - SUBCLASS = 0x0a, // byte - CLASS = 0x0b, // byte - CACHE_LINE_SIZE = 0x0c, // byte - LATENCY_TIMER = 0x0d, // byte - HEADER_TYPE = 0x0e, // byte - BIST = 0x0f, // byte - BAR0 = 0x10, // u32 - BAR1 = 0x14, // u32 - BAR2 = 0x18, // u32 - SECONDARY_BUS = 0x19, // byte - SUBORDINATE_BUS = 0x1A, // byte - BAR3 = 0x1C, // u32 - BAR4 = 0x20, // u32 - MEMORY_BASE = 0x20, // u16 - MEMORY_LIMIT = 0x22, // u16 - BAR5 = 0x24, // u32 - PREFETCHABLE_MEMORY_BASE = 0x24, // u16 - PREFETCHABLE_MEMORY_LIMIT = 0x26, // u16 - PREFETCHABLE_MEMORY_BASE_UPPER_32_BITS = 0x28, // u32 - PREFETCHABLE_MEMORY_LIMIT_UPPER_32_BITS = 0x2C, // u32 - SUBSYSTEM_VENDOR_ID = 0x2C, // u16 - SUBSYSTEM_ID = 0x2E, // u16 - EXPANSION_ROM_POINTER = 0x30, // u32 - CAPABILITIES_POINTER = 0x34, // u8 - INTERRUPT_LINE = 0x3C, // byte - INTERRUPT_PIN = 0x3D, // byte -}; - -enum class Limits { - MaxDevicesPerBus = 32, - MaxBusesPerDomain = 256, - MaxFunctionsPerDevice = 8, -}; - -static constexpr u16 address_port = 0xcf8; -static constexpr u16 value_port = 0xcfc; - -static constexpr size_t mmio_device_space_size = 4096; -static constexpr u16 none_value = 0xffff; -static constexpr size_t memory_range_per_bus = mmio_device_space_size * to_underlying(Limits::MaxFunctionsPerDevice) * to_underlying(Limits::MaxDevicesPerBus); -static constexpr u64 bar_address_mask = ~0xfull; -static constexpr u8 msi_control_offset = 2; -static constexpr u16 msi_control_enable = 0x0001; -static constexpr u8 msi_address_low_offset = 4; -static constexpr u8 msi_address_high_or_data_offset = 8; -static constexpr u8 msi_data_offset = 0xc; -static constexpr u16 msi_address_format_mask = 0x80; -static constexpr u8 msi_mmc_format_mask = 0xe; -static constexpr u16 msix_control_table_mask = 0x07ff; -static constexpr u8 msix_table_bir_mask = 0x7; -static constexpr u16 msix_table_offset_mask = 0xfff8; -static constexpr u16 msix_control_enable = 0x8000; - -union OpenFirmwareAddress { - enum class SpaceType : u32 { - ConfigurationSpace = 0, - IOSpace = 1, - Memory32BitSpace = 2, - Memory64BitSpace = 3, - }; - struct { - // https://www.devicetree.org/open-firmware/bindings/pci/pci2_1.pdf - // Chapter: 2.2.1.1 - // phys.hi cell - u32 register_ : 8; // r - u32 function : 3; // f - u32 device : 5; // d - u32 bus : 8; // b - SpaceType space_type : 2; // s - u32 : 3; // 0 - u32 aliased : 1; // t - u32 prefetchable : 1; // p - u32 relocatable : 1; // n - }; - u32 raw; -}; -static_assert(AssertSize()); - -// Taken from https://pcisig.com/sites/default/files/files/PCI_Code-ID_r_1_11__v24_Jan_2019.pdf -enum class ClassID { - Legacy = 0x00, - MassStorage = 0x01, - Network = 0x02, - Display = 0x03, - Multimedia = 0x04, - Memory = 0x05, - Bridge = 0x06, - SimpleCommunication = 0x07, - Base = 0x08, - Input = 0x09, - DockingStation = 0x0A, - Processor = 0x0B, - SerialBus = 0x0C, - Wireless = 0x0D, - IntelligentIO = 0x0E, - SatelliteCommunication = 0x0F, - EncryptionDecryption = 0x10, - DataAcquisitionAndSignalProcessing = 0x11, - ProcessingAccelerator = 0x12, - NonEssentialInstrumentation = 0x13, -}; - -namespace Legacy { - -enum class SubclassID { - Any = 0x00, - VGACompatible = 0x01 -}; - -} -namespace MassStorage { - -enum class SubclassID { - SCSIController = 0x00, - IDEController = 0x01, - FloppyController = 0x02, - IPIController = 0x03, - RAIDController = 0x04, - ATAController = 0x05, - SATAController = 0x06, - SASController = 0x06, - NVMeController = 0x08 // Technically other non-volatile memory subsystems as well -}; - -enum class SATAProgIF { - AHCI = 0x1, -}; - -} - -namespace Network { - -enum class SubclassID { - Ethernet = 0x00, - TokenRing = 0x01, - FDD = 0x02, - ATM = 0x03, - ISDN = 0x04, - WorldFlip = 0x05, - PICMG_2_14_MultiComputing = 0x06, - InfiniBand = 0x07, - HostFabric = 0x08, -}; - -} - -namespace Display { - -enum class SubclassID { - VGA = 0x00, - XGA = 0x01, - ThreeD = 0x02, -}; - -} - -namespace Multimedia { - -enum class SubclassID { - Video = 0x00, - Audio = 0x01, - ComputerTelephony = 0x01, - HDACompatible = 0x3, -}; - -} - -namespace Bridge { - -enum class SubclassID { - PCI_TO_PCI = 0x4, -}; - -} - -namespace Base { - -enum class SubclassID { - PIC = 0x00, - DMAController = 0x01, - Timer = 0x02, - RTCController = 0x03, - PCIHotplugController = 0x04, - SDHostController = 0x5, - IOMMU = 0x06 -}; - -} - -namespace SerialBus { - -enum class SubclassID { - USB = 0x03, -}; - -enum class USBProgIf { - UHCI = 0x00, - OHCI = 0x10, - EHCI = 0x20, - xHCI = 0x30, - None = 0x80, - Device = 0xFE -}; - -} - -AK_TYPEDEF_DISTINCT_ORDERED_ID(u8, CapabilityID); - -namespace Capabilities { -enum ID { - Null = 0x0, - MSI = 0x5, - VendorSpecific = 0x9, - MSIX = 0x11, -}; -} - -struct HardwareID { - u16 vendor_id { 0 }; - u16 device_id { 0 }; - - bool is_null() const { return !vendor_id && !device_id; } - - bool operator==(HardwareID const& other) const - { - return vendor_id == other.vendor_id && device_id == other.device_id; - } - bool operator!=(HardwareID const& other) const - { - return vendor_id != other.vendor_id || device_id != other.device_id; - } -}; - -class Domain { -public: - Domain() = delete; - Domain(u32 domain_number, u8 start_bus, u8 end_bus) - : m_domain_number(domain_number) - , m_start_bus(start_bus) - , m_end_bus(end_bus) - { - } - u8 start_bus() const { return m_start_bus; } - u8 end_bus() const { return m_end_bus; } - u32 domain_number() const { return m_domain_number; } - -private: - u32 m_domain_number; - u8 m_start_bus; - u8 m_end_bus; -}; - -struct Address { -public: - Address() = default; - Address(u32 domain) - : m_domain(domain) - , m_bus(0) - , m_device(0) - , m_function(0) - { - } - Address(u32 domain, u8 bus, u8 device, u8 function) - : m_domain(domain) - , m_bus(bus) - , m_device(device) - , m_function(function) - { - } - - Address(Address const& address) = default; - - bool is_null() const { return !m_bus && !m_device && !m_function; } - operator bool() const { return !is_null(); } - - // Disable default implementations that would use surprising integer promotion. - bool operator<=(Address const&) const = delete; - bool operator>=(Address const&) const = delete; - bool operator<(Address const&) const = delete; - bool operator>(Address const&) const = delete; - - bool operator==(Address const& other) const - { - if (this == &other) - return true; - return m_domain == other.m_domain && m_bus == other.m_bus && m_device == other.m_device && m_function == other.m_function; - } - bool operator!=(Address const& other) const - { - return !(*this == other); - } - - u32 domain() const { return m_domain; } - u8 bus() const { return m_bus; } - u8 device() const { return m_device; } - u8 function() const { return m_function; } - -private: - u32 m_domain { 0 }; - u8 m_bus { 0 }; - u8 m_device { 0 }; - u8 m_function { 0 }; -}; - -class Capability { -public: - Capability(Address address, u8 id, u8 ptr) - : m_address(address) - , m_id(id) - , m_ptr(ptr) - { - } - - CapabilityID id() const { return m_id; } - - u8 read8(size_t offset) const; - u16 read16(size_t offset) const; - u32 read32(size_t offset) const; - void write8(size_t offset, u8 value) const; - void write16(size_t offset, u16 value) const; - void write32(size_t offset, u32 value) const; - -private: - Address const m_address; - CapabilityID const m_id; - u8 const m_ptr; -}; - -AK_TYPEDEF_DISTINCT_ORDERED_ID(u8, ClassCode); -AK_MAKE_DISTINCT_NUMERIC_COMPARABLE_TO_ENUM(ClassCode, ClassID) - -AK_TYPEDEF_DISTINCT_ORDERED_ID(u8, SubclassCode); -AK_MAKE_DISTINCT_NUMERIC_COMPARABLE_TO_ENUM(SubclassCode, Legacy::SubclassID); -AK_MAKE_DISTINCT_NUMERIC_COMPARABLE_TO_ENUM(SubclassCode, MassStorage::SubclassID); -AK_MAKE_DISTINCT_NUMERIC_COMPARABLE_TO_ENUM(SubclassCode, Network::SubclassID); -AK_MAKE_DISTINCT_NUMERIC_COMPARABLE_TO_ENUM(SubclassCode, Display::SubclassID); -AK_MAKE_DISTINCT_NUMERIC_COMPARABLE_TO_ENUM(SubclassCode, Multimedia::SubclassID); -AK_MAKE_DISTINCT_NUMERIC_COMPARABLE_TO_ENUM(SubclassCode, Bridge::SubclassID); -AK_MAKE_DISTINCT_NUMERIC_COMPARABLE_TO_ENUM(SubclassCode, Base::SubclassID); -AK_MAKE_DISTINCT_NUMERIC_COMPARABLE_TO_ENUM(SubclassCode, SerialBus::SubclassID); - -AK_TYPEDEF_DISTINCT_ORDERED_ID(u8, ProgrammingInterface); -AK_MAKE_DISTINCT_NUMERIC_COMPARABLE_TO_ENUM(ProgrammingInterface, MassStorage::SATAProgIF); -AK_MAKE_DISTINCT_NUMERIC_COMPARABLE_TO_ENUM(ProgrammingInterface, SerialBus::USBProgIf); - -AK_TYPEDEF_DISTINCT_ORDERED_ID(u8, RevisionID); -AK_TYPEDEF_DISTINCT_ORDERED_ID(u16, SubsystemID); -AK_TYPEDEF_DISTINCT_ORDERED_ID(u16, SubsystemVendorID); -AK_TYPEDEF_DISTINCT_ORDERED_ID(u8, InterruptLine); -AK_TYPEDEF_DISTINCT_ORDERED_ID(u8, InterruptPin); - -class Access; -class EnumerableDeviceIdentifier { -public: - EnumerableDeviceIdentifier(Address address, HardwareID hardware_id, RevisionID revision_id, ClassCode class_code, SubclassCode subclass_code, ProgrammingInterface prog_if, SubsystemID subsystem_id, SubsystemVendorID subsystem_vendor_id, InterruptLine interrupt_line, InterruptPin interrupt_pin, Vector const& capabilities) - : m_address(address) - , m_hardware_id(hardware_id) - , m_revision_id(revision_id) - , m_class_code(class_code) - , m_subclass_code(subclass_code) - , m_prog_if(prog_if) - , m_subsystem_id(subsystem_id) - , m_subsystem_vendor_id(subsystem_vendor_id) - , m_interrupt_line(interrupt_line) - , m_interrupt_pin(interrupt_pin) - , m_capabilities(capabilities) - { - if constexpr (PCI_DEBUG) { - for (auto const& capability : capabilities) - dbgln("{} has capability {}", address, capability.id()); - } - } - - Vector const& capabilities() const { return m_capabilities; } - HardwareID const& hardware_id() const { return m_hardware_id; } - Address const& address() const { return m_address; } - - RevisionID revision_id() const { return m_revision_id; } - ClassCode class_code() const { return m_class_code; } - SubclassCode subclass_code() const { return m_subclass_code; } - ProgrammingInterface prog_if() const { return m_prog_if; } - SubsystemID subsystem_id() const { return m_subsystem_id; } - SubsystemVendorID subsystem_vendor_id() const { return m_subsystem_vendor_id; } - - InterruptLine interrupt_line() const { return m_interrupt_line; } - InterruptPin interrupt_pin() const { return m_interrupt_pin; } - - void apply_subclass_code_change(Badge, SubclassCode new_subclass) - { - m_subclass_code = new_subclass; - } - void apply_prog_if_change(Badge, ProgrammingInterface new_progif) - { - m_prog_if = new_progif; - } - -protected: - Address m_address; - HardwareID m_hardware_id; - - RevisionID m_revision_id; - ClassCode m_class_code; - SubclassCode m_subclass_code; - ProgrammingInterface m_prog_if; - SubsystemID m_subsystem_id; - SubsystemVendorID m_subsystem_vendor_id; - - InterruptLine m_interrupt_line; - InterruptPin m_interrupt_pin; - - Vector m_capabilities; -}; - -class MSIxInfo { -public: - MSIxInfo(u16 table_size, u8 table_bar, u32 table_offset) - : table_size(table_size) - , table_bar(table_bar) - , table_offset(table_offset) - { - } - - MSIxInfo() = default; - - u16 table_size {}; - u8 table_bar {}; - u32 table_offset {}; -}; - -class MSIInfo { -public: - MSIInfo(bool message_address_64_bit_support, u8 count) - : message_address_64_bit_format(message_address_64_bit_support) - , count(count) - { - } - - MSIInfo() = default; - - bool message_address_64_bit_format { false }; - u8 count {}; -}; - -class DeviceIdentifier - : public RefCounted - , public EnumerableDeviceIdentifier { - AK_MAKE_NONCOPYABLE(DeviceIdentifier); - -public: - static ErrorOr> from_enumerable_identifier(EnumerableDeviceIdentifier const& other_identifier); - - void initialize(); - bool is_msix_capable() const { return m_msix_info.table_size > 0; } - u8 get_msix_table_bar() const { return m_msix_info.table_bar; } - u32 get_msix_table_offset() const { return m_msix_info.table_offset; } - - bool is_msi_capable() const { return m_msi_info.count > 0; } - bool is_msi_64bit_address_format() { return m_msi_info.message_address_64_bit_format; } - - Spinlock& operation_lock() { return m_operation_lock; } - Spinlock& operation_lock() const { return m_operation_lock; } - - virtual ~DeviceIdentifier() = default; - -private: - DeviceIdentifier(EnumerableDeviceIdentifier const& other_identifier) - : EnumerableDeviceIdentifier(other_identifier.address(), - other_identifier.hardware_id(), - other_identifier.revision_id(), - other_identifier.class_code(), - other_identifier.subclass_code(), - other_identifier.prog_if(), - other_identifier.subsystem_id(), - other_identifier.subsystem_vendor_id(), - other_identifier.interrupt_line(), - other_identifier.interrupt_pin(), - other_identifier.capabilities()) - { - } - - mutable Spinlock m_operation_lock; - MSIxInfo m_msix_info {}; - MSIInfo m_msi_info {}; -}; - -class Domain; -class Device; -} - -template<> -struct AK::Formatter : Formatter { - ErrorOr format(FormatBuilder& builder, Kernel::PCI::Address value) - { - return Formatter::format( - builder, - "PCI [{:04x}:{:02x}:{:02x}:{:02x}]"sv, value.domain(), value.bus(), value.device(), value.function()); - } -}; - -template<> -struct AK::Formatter : Formatter { - ErrorOr format(FormatBuilder& builder, Kernel::PCI::HardwareID value) - { - return Formatter::format( - builder, - "PCI::HardwareID [{:04x}:{:04x}]"sv, value.vendor_id, value.device_id); - } -}; diff --git a/Kernel/Bus/PCI/Device.cpp b/Kernel/Bus/PCI/Device.cpp deleted file mode 100644 index 45eecfc677b..00000000000 --- a/Kernel/Bus/PCI/Device.cpp +++ /dev/null @@ -1,204 +0,0 @@ -/* - * Copyright (c) 2020, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include -#include - -namespace Kernel::PCI { - -Device::Device(DeviceIdentifier const& pci_identifier) - : m_pci_identifier(pci_identifier) -{ - m_pci_identifier->initialize(); - m_interrupt_range.m_start_irq = m_pci_identifier->interrupt_line().value(); - m_interrupt_range.m_irq_count = 1; -} - -bool Device::is_msi_capable() const -{ - return m_pci_identifier->is_msi_capable(); -} -bool Device::is_msix_capable() const -{ - return m_pci_identifier->is_msix_capable(); -} - -void Device::enable_pin_based_interrupts() const -{ - PCI::enable_interrupt_line(m_pci_identifier); -} -void Device::disable_pin_based_interrupts() const -{ - PCI::disable_interrupt_line(m_pci_identifier); -} - -void Device::enable_message_signalled_interrupts() -{ - for (auto& capability : m_pci_identifier->capabilities()) { - if (capability.id().value() == PCI::Capabilities::ID::MSI) - capability.write16(msi_control_offset, capability.read16(msi_control_offset) | msi_control_enable); - } -} -void Device::disable_message_signalled_interrupts() -{ - for (auto& capability : m_pci_identifier->capabilities()) { - if (capability.id().value() == PCI::Capabilities::ID::MSI) - capability.write16(msi_control_offset, capability.read16(msi_control_offset) & ~(msi_control_enable)); - } -} - -void Device::enable_extended_message_signalled_interrupts() -{ - for (auto& capability : m_pci_identifier->capabilities()) { - if (capability.id().value() == PCI::Capabilities::ID::MSIX) - capability.write16(msi_control_offset, capability.read16(msi_control_offset) | msix_control_enable); - } -} - -void Device::disable_extended_message_signalled_interrupts() -{ - for (auto& capability : m_pci_identifier->capabilities()) { - if (capability.id().value() == PCI::Capabilities::ID::MSIX) - capability.write16(msi_control_offset, capability.read16(msi_control_offset) & ~(msix_control_enable)); - } -} - -PCI::InterruptType Device::get_interrupt_type() -{ - return m_interrupt_range.m_type; -} - -// Reserve `numbers_of_irqs` for this device. Returns the interrupt type -// that was reserved. It is a noop for pin based interrupts as there -// is nothing left to do. The second parameter `msi` is used by the -// driver to indicate its intent to use message signalled interrupts. -// MSI(x) is preferred over MSI if the device supports both. -ErrorOr Device::reserve_irqs(u8 number_of_irqs, bool msi) -{ - // Let us not allow partial allocation of IRQs for MSIx. - if (msi && is_msix_capable()) { - m_interrupt_range.m_start_irq = TRY(reserve_interrupt_handlers(number_of_irqs)); - m_interrupt_range.m_irq_count = number_of_irqs; - m_interrupt_range.m_type = InterruptType::MSIX; - // If MSIx is available, disable the pin based interrupts - disable_pin_based_interrupts(); - enable_extended_message_signalled_interrupts(); - } else if (msi && is_msi_capable()) { - // TODO: Add MME support. Fallback to pin-based until this support is added. - if (number_of_irqs > 1) - return m_interrupt_range.m_type; - - m_interrupt_range.m_start_irq = TRY(reserve_interrupt_handlers(number_of_irqs)); - m_interrupt_range.m_irq_count = number_of_irqs; - m_interrupt_range.m_type = InterruptType::MSI; - disable_pin_based_interrupts(); - enable_message_signalled_interrupts(); - } - return m_interrupt_range.m_type; -} - -PhysicalAddress Device::msix_table_entry_address(u8 irq) -{ - auto index = static_cast(irq) - m_interrupt_range.m_start_irq; - - VERIFY(index < m_interrupt_range.m_irq_count); - VERIFY(index >= 0); - auto table_bar_ptr = PCI::get_BAR(device_identifier(), static_cast(m_pci_identifier->get_msix_table_bar())) & PCI::bar_address_mask; - auto table_offset = m_pci_identifier->get_msix_table_offset(); - - return PhysicalAddress(table_bar_ptr + table_offset + (index * 16)); -} - -// This function is used to allocate an irq at an index and returns -// the actual IRQ that was programmed at that index. This function is -// mainly useful for MSI/MSIx based interrupt mechanism where the driver -// needs to program. If the PCI device doesn't support MSIx interrupts, then -// this function will just return the irq used for pin based interrupt. -ErrorOr Device::allocate_irq(u8 index) -{ - if (Checked::addition_would_overflow(m_interrupt_range.m_start_irq, index)) - return Error::from_errno(EINVAL); - - if ((m_interrupt_range.m_type == InterruptType::MSIX) && is_msix_capable()) { - auto entry_ptr = TRY(Memory::map_typed_writable(msix_table_entry_address(index + m_interrupt_range.m_start_irq))); - entry_ptr->data = msi_data_register(m_interrupt_range.m_start_irq + index, false, false); - // TODO: we map all the IRQs to cpu 0 by default. We could attach - // cpu affinity in the future where specific LAPIC id could be used. - u64 addr = msi_address_register(0, false, false); - entry_ptr->address_low = addr & 0xffffffff; - entry_ptr->address_high = addr >> 32; - - u32 vector_ctrl = msix_vector_control_register(entry_ptr->vector_control, true); - entry_ptr->vector_control = vector_ctrl; - - return m_interrupt_range.m_start_irq + index; - } else if ((m_interrupt_range.m_type == InterruptType::MSI) && is_msi_capable()) { - // TODO: Add MME support. - if (index > 0) - return Error::from_errno(EINVAL); - - auto data = msi_data_register(m_interrupt_range.m_start_irq + index, false, false); - auto addr = msi_address_register(0, false, false); - for (auto& capability : m_pci_identifier->capabilities()) { - if (capability.id().value() == PCI::Capabilities::ID::MSI) { - capability.write32(msi_address_low_offset, addr & 0xffffffff); - - if (!m_pci_identifier->is_msi_64bit_address_format()) { - capability.write16(msi_address_high_or_data_offset, data); - break; - } - - capability.write32(msi_address_high_or_data_offset, addr >> 32); - capability.write16(msi_data_offset, data); - } - } - return m_interrupt_range.m_start_irq + index; - } - // For pin based interrupts, we share the IRQ. - return m_interrupt_range.m_start_irq; -} - -void Device::enable_interrupt(u8 irq) -{ - if ((m_interrupt_range.m_type == InterruptType::MSIX) && is_msix_capable()) { - auto entry = Memory::map_typed_writable(PhysicalAddress(msix_table_entry_address(irq))); - - if (entry.is_error()) { - dmesgln_pci(*this, "Unable to map the MSIx table area"); - return; - } - - auto entry_ptr = entry.release_value(); - u32 vector_ctrl = msix_vector_control_register(entry_ptr->vector_control, false); - entry_ptr->vector_control = vector_ctrl; - } else if ((m_interrupt_range.m_type == InterruptType::MSI) && is_msi_capable()) { - enable_message_signalled_interrupts(); - } -} - -void Device::disable_interrupt(u8 irq) -{ - if ((m_interrupt_range.m_type == InterruptType::MSIX) && is_msix_capable()) { - auto entry = Memory::map_typed_writable(PhysicalAddress(msix_table_entry_address(irq))); - - if (entry.is_error()) { - dmesgln_pci(*this, "Unable to map the MSIx table area"); - return; - } - - auto entry_ptr = entry.release_value(); - u32 vector_ctrl = msix_vector_control_register(entry_ptr->vector_control, true); - entry_ptr->vector_control = vector_ctrl; - } else if ((m_interrupt_range.m_type == InterruptType::MSI) && is_msi_capable()) { - disable_message_signalled_interrupts(); - } -} - -} diff --git a/Kernel/Bus/PCI/Device.h b/Kernel/Bus/PCI/Device.h deleted file mode 100644 index 734493f205b..00000000000 --- a/Kernel/Bus/PCI/Device.h +++ /dev/null @@ -1,84 +0,0 @@ -/* - * Copyright (c) 2020, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include - -namespace Kernel::PCI { - -enum class InterruptType { - PIN, - MSI, - MSIX -}; - -struct InterruptRange { - u8 m_start_irq { 0 }; - u8 m_irq_count { 0 }; - InterruptType m_type { InterruptType::PIN }; -}; - -struct [[gnu::packed]] MSIxTableEntry { - u32 address_low; - u32 address_high; - u32 data; - u32 vector_control; -}; - -class Device { -public: - DeviceIdentifier const& device_identifier() const { return *m_pci_identifier; } - - virtual ~Device() = default; - - virtual StringView device_name() const = 0; - - void enable_pin_based_interrupts() const; - void disable_pin_based_interrupts() const; - - bool is_msi_capable() const; - bool is_msix_capable() const; - - void enable_message_signalled_interrupts(); - void disable_message_signalled_interrupts(); - - void enable_extended_message_signalled_interrupts(); - void disable_extended_message_signalled_interrupts(); - ErrorOr reserve_irqs(u8 number_of_irqs, bool msi); - ErrorOr allocate_irq(u8 index); - PCI::InterruptType get_interrupt_type(); - void enable_interrupt(u8 irq); - void disable_interrupt(u8 irq); - -protected: - explicit Device(DeviceIdentifier const& pci_identifier); - -private: - PhysicalAddress msix_table_entry_address(u8 irq); - -private: - NonnullRefPtr const m_pci_identifier; - InterruptRange m_interrupt_range; -}; - -template -void dmesgln_pci(Device const& device, AK::CheckedFormatString&& fmt, Parameters const&... parameters) -{ - AK::StringBuilder builder; - if (builder.try_append("{}: {}: "sv).is_error()) - return; - if (builder.try_append(fmt.view()).is_error()) - return; - AK::VariadicFormatParams variadic_format_params { device.device_name(), device.device_identifier().address(), parameters... }; - vdmesgln(builder.string_view(), variadic_format_params); -} - -} diff --git a/Kernel/Bus/PCI/DeviceIdentifier.cpp b/Kernel/Bus/PCI/DeviceIdentifier.cpp deleted file mode 100644 index 42cae6e67c7..00000000000 --- a/Kernel/Bus/PCI/DeviceIdentifier.cpp +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright (c) 2020, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include - -namespace Kernel::PCI { - -ErrorOr> DeviceIdentifier::from_enumerable_identifier(EnumerableDeviceIdentifier const& other_identifier) -{ - return adopt_nonnull_ref_or_enomem(new (nothrow) DeviceIdentifier(other_identifier)); -} - -void DeviceIdentifier::initialize() -{ - for (auto cap : capabilities()) { - if (cap.id() == PCI::Capabilities::ID::MSIX) { - auto msix_bir_bar = (cap.read8(4) & msix_table_bir_mask); - auto msix_bir_offset = (cap.read32(4) & msix_table_offset_mask); - auto msix_count = (cap.read16(2) & msix_control_table_mask) + 1; - m_msix_info = MSIxInfo(msix_count, msix_bir_bar, msix_bir_offset); - } - - if (cap.id() == PCI::Capabilities::ID::MSI) { - bool message_address_64_bit_format = (cap.read8(msi_control_offset) & msi_address_format_mask); - u8 count = 1; - u8 mme_count = (cap.read8(msi_control_offset) & msi_mmc_format_mask) >> 1; - if (mme_count) - count = mme_count; - m_msi_info = MSIInfo(message_address_64_bit_format, count); - } - } -} -} diff --git a/Kernel/Bus/PCI/IDs.h b/Kernel/Bus/PCI/IDs.h deleted file mode 100644 index 87d261a9e62..00000000000 --- a/Kernel/Bus/PCI/IDs.h +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright (c) 2021, Gunnar Beutner - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -namespace Kernel::PCI { - -enum VendorID { - VirtIO = 0x1af4, - Intel = 0x8086, - WCH = 0x1c00, - RedHat = 0x1b36, - Realtek = 0x10ec, - QEMUOld = 0x1234, - VirtualBox = 0x80ee, - VMWare = 0x15ad, - Tdfx = 0x121a, -}; - -enum DeviceID { - VirtIONetAdapter = 0x1000, - VirtIOBlockDevice = 0x1001, - VirtIOConsole = 0x1003, - VirtIOEntropy = 0x1005, - VirtIOGPU = 0x1050, - VirtIOInput = 0x1052, -}; - -} diff --git a/Kernel/Bus/PCI/Initializer.h b/Kernel/Bus/PCI/Initializer.h deleted file mode 100644 index 919a61744ee..00000000000 --- a/Kernel/Bus/PCI/Initializer.h +++ /dev/null @@ -1,18 +0,0 @@ -/* - * Copyright (c) 2020, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include - -namespace Kernel::PCI { - -extern SetOnce g_pci_access_io_probe_failed; -extern SetOnce g_pci_access_is_disabled_from_commandline; - -void initialize(); - -} diff --git a/Kernel/Bus/SerialIO/Controller.h b/Kernel/Bus/SerialIO/Controller.h deleted file mode 100644 index 6dee6430f1c..00000000000 --- a/Kernel/Bus/SerialIO/Controller.h +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright (c) 2023, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include -#include -#include - -namespace Kernel { - -class HIDManagement; -class SerialIOController : public AtomicRefCounted { - friend class HIDManagement; - -public: - enum class DeviceCommand : u8 { - GetDeviceID, - SetSampleRate, - EnablePacketStreaming, - DisablePacketStreaming, - SetDefaults, - }; - - AK_TYPEDEF_DISTINCT_ORDERED_ID(size_t, PortIndex); - - virtual ~SerialIOController() = default; - - virtual ErrorOr reset_device(PortIndex) = 0; - virtual ErrorOr send_command(PortIndex, DeviceCommand command) = 0; - virtual ErrorOr send_command(PortIndex, DeviceCommand command, u8 data) = 0; - - virtual ErrorOr read_from_device(PortIndex) = 0; - virtual ErrorOr prepare_for_input(PortIndex) = 0; - -protected: - SerialIOController() = default; - -private: - IntrusiveListNode> m_list_node; -}; - -} diff --git a/Kernel/Bus/SerialIO/Device.h b/Kernel/Bus/SerialIO/Device.h deleted file mode 100644 index 327c49d5a40..00000000000 --- a/Kernel/Bus/SerialIO/Device.h +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright (c) 2023, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include - -namespace Kernel { - -class SerialIODevice { -public: - virtual ~SerialIODevice() = default; - - virtual void handle_byte_read_from_serial_input(u8 byte) = 0; - - SerialIOController::PortIndex attached_port_index() const { return m_attached_port_index; } - SerialIOController& attached_controller() { return m_serial_io_controller; } - SerialIOController const& attached_controller() const { return m_serial_io_controller; } - -protected: - SerialIODevice(SerialIOController const& serial_io_controller, SerialIOController::PortIndex attached_port_index) - : m_serial_io_controller(serial_io_controller) - , m_attached_port_index(attached_port_index) - { - } - -private: - NonnullRefPtr const m_serial_io_controller; - SerialIOController::PortIndex m_attached_port_index { 0 }; -}; - -} diff --git a/Kernel/Bus/SerialIO/PS2Definitions.h b/Kernel/Bus/SerialIO/PS2Definitions.h deleted file mode 100644 index 0435c8c4d42..00000000000 --- a/Kernel/Bus/SerialIO/PS2Definitions.h +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright (c) 2023, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include - -namespace Kernel { - -// NOTE: This list is derived from https://wiki.osdev.org/%228042%22_PS/2_Controller#Detecting_PS.2F2_Device_Types -enum class PS2DeviceType { - Unknown, - ATKeyboard, - StandardMouse, - ScrollWheelMouse, - MouseWith5Buttons, - MF2Keyboard, - ThinkPadKeyboard, - NCDKeyboard, - HostConnected122KeysKeyboard, - StandardKeyboard, // 122 keys keyboard - JapaneseGKeyboard, - JapanesePKeyboard, - JapaneseAKeyboard, - NCDSunKeyboard, -}; - -} diff --git a/Kernel/Bus/USB/Drivers/HID/Codes.h b/Kernel/Bus/USB/Drivers/HID/Codes.h deleted file mode 100644 index 6d7e96a4c05..00000000000 --- a/Kernel/Bus/USB/Drivers/HID/Codes.h +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright (c) 2023, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include - -namespace Kernel::USB::HID { - -enum class SubclassCode : u8 { - BootProtocol = 0x01, -}; - -struct [[gnu::packed]] MouseBootProtocolPacket { - u8 buttons; - i8 x; - i8 y; - i8 z; - i8 reserved1; - i8 reserved2; -}; - -static_assert(AssertSize()); - -constexpr StringView subclass_string(SubclassCode code) -{ - switch (code) { - case SubclassCode::BootProtocol: - return "Boot Protocol"sv; - } - - return "Reserved"sv; -} - -enum class InterfaceProtocol : u8 { - Mouse = 0x02, -}; - -} diff --git a/Kernel/Bus/USB/Drivers/HID/MouseDriver.cpp b/Kernel/Bus/USB/Drivers/HID/MouseDriver.cpp deleted file mode 100644 index 4cbfe4aca2c..00000000000 --- a/Kernel/Bus/USB/Drivers/HID/MouseDriver.cpp +++ /dev/null @@ -1,83 +0,0 @@ -/* - * Copyright (c) 2023, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -namespace Kernel::USB { - -USB_DEVICE_DRIVER(MouseDriver); - -void MouseDriver::init() -{ - auto driver = MUST(adopt_nonnull_lock_ref_or_enomem(new MouseDriver())); - USBManagement::register_driver(driver); -} - -ErrorOr MouseDriver::checkout_interface(USB::Device& device, USBInterface const& interface) -{ - auto const& descriptor = interface.descriptor(); - if (descriptor.interface_class_code == USB_CLASS_HID - && descriptor.interface_sub_class_code == to_underlying(HID::SubclassCode::BootProtocol) - && descriptor.interface_protocol == to_underlying(HID::InterfaceProtocol::Mouse)) { - dmesgln("USB HID Mouse Interface for device {:#04x}:{:#04x} found", device.device_descriptor().vendor_id, device.device_descriptor().product_id); - return initialize_device(device, interface); - } - return ENOTSUP; -} - -ErrorOr MouseDriver::probe(USB::Device& device) -{ - if (device.device_descriptor().device_class != USB_CLASS_DEVICE - || device.device_descriptor().device_sub_class != 0x00 - || device.device_descriptor().device_protocol != 0x00) - return ENOTSUP; - // FIXME: Are we guaranteed to have one USB configuration for a mouse device? - if (device.configurations().size() != 1) - return ENOTSUP; - // FIXME: If we have multiple USB configurations for a mouse device, find the appropriate one - // and handle multiple interfaces for it. - if (device.configurations()[0].interfaces().size() != 1) - return ENOTSUP; - - TRY(checkout_interface(device, device.configurations()[0].interfaces()[0])); - - return ENOTSUP; -} - -ErrorOr MouseDriver::initialize_device(USB::Device& device, USBInterface const& interface) -{ - if (interface.endpoints().size() != 1) - return ENOTSUP; - auto const& configuration = interface.configuration(); - // FIXME: Should we check other configurations? - TRY(device.control_transfer( - USB_REQUEST_RECIPIENT_DEVICE | USB_REQUEST_TYPE_STANDARD | USB_REQUEST_TRANSFER_DIRECTION_HOST_TO_DEVICE, - USB_REQUEST_SET_CONFIGURATION, configuration.configuration_id(), 0, 0, nullptr)); - - auto const& endpoint_descriptor = interface.endpoints()[0]; - auto interrupt_in_pipe = TRY(USB::InterruptInPipe::create(device.controller(), endpoint_descriptor.endpoint_address, endpoint_descriptor.max_packet_size, device.address(), 10)); - auto mouse_device = TRY(USBMouseDevice::try_create_instance(device, endpoint_descriptor.max_packet_size, move(interrupt_in_pipe))); - HIDManagement::the().attach_standalone_hid_device(*mouse_device); - m_interfaces.append(mouse_device); - return {}; -} - -void MouseDriver::detach(USB::Device& device) -{ - auto&& mouse_device = AK::find_if(m_interfaces.begin(), m_interfaces.end(), [&device](auto& interface) { return &interface.device() == &device; }); - - HIDManagement::the().detach_standalone_hid_device(*mouse_device); - m_interfaces.remove(*mouse_device); -} - -} diff --git a/Kernel/Bus/USB/Drivers/HID/MouseDriver.h b/Kernel/Bus/USB/Drivers/HID/MouseDriver.h deleted file mode 100644 index b57f3a863cd..00000000000 --- a/Kernel/Bus/USB/Drivers/HID/MouseDriver.h +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright (c) 2023, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include - -namespace Kernel::USB { - -class MouseDriver final : public Driver { -public: - MouseDriver() - : Driver("USB Mouse"sv) - { - } - - static void init(); - - virtual ~MouseDriver() override = default; - - virtual ErrorOr probe(USB::Device&) override; - virtual void detach(USB::Device&) override; - -private: - USBMouseDevice::List m_interfaces; - - ErrorOr checkout_interface(USB::Device&, USBInterface const&); - - ErrorOr initialize_device(USB::Device&, USBInterface const&); -}; - -} diff --git a/Kernel/Bus/USB/Drivers/MassStorage/MassStorageDriver.cpp b/Kernel/Bus/USB/Drivers/MassStorage/MassStorageDriver.cpp deleted file mode 100644 index 284366b34e7..00000000000 --- a/Kernel/Bus/USB/Drivers/MassStorage/MassStorageDriver.cpp +++ /dev/null @@ -1,184 +0,0 @@ -/* - * Copyright (c) 2023, Leon Albrecht - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace Kernel::USB { - -using namespace MassStorage; - -USB_DEVICE_DRIVER(MassStorageDriver); - -void MassStorageDriver::init() -{ - auto driver = MUST(adopt_nonnull_lock_ref_or_enomem(new MassStorageDriver())); - USBManagement::register_driver(driver); -} - -ErrorOr MassStorageDriver::checkout_interface(USB::Device& device, USBInterface const& interface) -{ - auto const& descriptor = interface.descriptor(); - - if (descriptor.interface_class_code != USB_CLASS_MASS_STORAGE) - return ENOTSUP; - - dmesgln("USB MassStorage Interface for device {}:{} found:", device.device_descriptor().vendor_id, device.device_descriptor().product_id); - dmesgln(" Subclass: {} [{:#02x}]", MassStorage::subclass_string((SubclassCode)descriptor.interface_sub_class_code), descriptor.interface_sub_class_code); - dmesgln(" Protocol: {} [{:#02x}]", MassStorage::transport_protocol_string((TransportProtocol)descriptor.interface_protocol), descriptor.interface_protocol); - - // FIXME: Find a nice way of handling multiple device subclasses and protocols - if (descriptor.interface_protocol == to_underlying(TransportProtocol::BBB)) - return initialise_bulk_only_device(device, interface); - - return ENOTSUP; -} - -ErrorOr MassStorageDriver::probe(USB::Device& device) -{ - // USB massbulk Table 4.1: - if (device.device_descriptor().device_class != USB_CLASS_DEVICE - || device.device_descriptor().device_sub_class != 0x00 - || device.device_descriptor().device_protocol != 0x00) - return ENOTSUP; - - for (auto const& config : device.configurations()) { - // FIXME: There might be multiple MassStorage configs present, - // figure out how to decide which one to take, - // although that's very unlikely - bool has_accepted_an_interface = false; - for (auto const& interface : config.interfaces()) { - // FIXME: Handle multiple interfaces - // Interface may coexist at the same time, - // but having multiple handles on the same data storage seems like a bad idea, - // so: - // FIXME: Choose the best supported interface - // UAS for example is supposed to be better than BBB, but BBB will always - // be the first listed interface of them, when both are supported - auto result = checkout_interface(device, interface); - if (result.is_error()) - continue; - - has_accepted_an_interface = true; - } - - if (has_accepted_an_interface) - return {}; - } - - return ENOTSUP; -} - -ErrorOr MassStorageDriver::initialise_bulk_only_device(USB::Device& device, USBInterface const& interface) -{ - auto const& descriptor = interface.descriptor(); - auto const& configuration = interface.configuration(); - - if (descriptor.interface_sub_class_code != to_underlying(MassStorage::SubclassCode::SCSI_transparent)) - return ENOTSUP; - - TRY(device.control_transfer( - USB_REQUEST_RECIPIENT_DEVICE | USB_REQUEST_TYPE_STANDARD | USB_REQUEST_TRANSFER_DIRECTION_HOST_TO_DEVICE, - USB_REQUEST_SET_CONFIGURATION, configuration.configuration_id(), 0, 0, nullptr)); - - u8 max_luns; - TRY(device.control_transfer( - USB_REQUEST_TYPE_CLASS | USB_REQUEST_RECIPIENT_INTERFACE | USB_REQUEST_TRANSFER_DIRECTION_DEVICE_TO_HOST, - to_underlying(MassStorage::RequestCodes::GetMaxLun), 0, interface.descriptor().interface_id, 1, &max_luns)); - // FIXME: Devices that do not support multiple LUNs may STALL this command - // FIXME: Support multiple LUNs - if (max_luns != 0) - dmesgln("SCSI/BBB: WARNING: USB Mass Storage Device supports multiple LUNs ({}) only targetting first LUN", max_luns); - - u8 in_pipe_address = 0xff; - u8 in_max_packet_size; - u8 out_pipe_address = 0xff; - u8 out_max_packet_size; - - if (interface.descriptor().number_of_endpoints < 2) { - dmesgln("SCSI/BBB: Interface does not provide enough endpoints for advertised Bulk-only transfer protocol; Rejecting"); - return ENOTSUP; - } - - for (auto const& endpoint : interface.endpoints()) { - if (endpoint.endpoint_attributes_bitmap != USBEndpoint::ENDPOINT_ATTRIBUTES_TRANSFER_TYPE_BULK) - continue; - // The upper bit of the Endpoint address is set to 1, iff it is the Bulk-In Endpoint - if (endpoint.endpoint_address & 0x80) { - in_pipe_address = endpoint.endpoint_address & 0b1111; - in_max_packet_size = endpoint.max_packet_size; - } else { - out_pipe_address = endpoint.endpoint_address & 0b1111; - out_max_packet_size = endpoint.max_packet_size; - } - } - if (in_pipe_address == 0xff || out_pipe_address == 0xff) { - dmesgln("SCSI/BBB: Interface did not advertise two Bulk Endpoints; Rejecting"); - return ENOTSUP; - } - - auto in_pipe = TRY(BulkInPipe::create(device.controller(), in_pipe_address, in_max_packet_size, device.address())); - auto out_pipe = TRY(BulkOutPipe::create(device.controller(), out_pipe_address, out_max_packet_size, device.address())); - - CommandBlockWrapper command_block {}; - command_block.set_command(SCSI::ReadCapacity10 {}); - command_block.transfer_length = sizeof(SCSI::ReadCapacity10Parameters); - command_block.direction = CBWDirection::DataIn; - TRY(out_pipe->submit_bulk_out_transfer(31, &command_block)); - - SCSI::ReadCapacity10Parameters capacity; - TRY(in_pipe->submit_bulk_in_transfer(sizeof(capacity), &capacity)); - // FIXME: Handle Stalling - CommandStatusWrapper status; - TRY(in_pipe->submit_bulk_in_transfer(sizeof(status), &status)); - if (status.status != CSWStatus::Passed) { - dmesgln("SCSI/BBB: Failed to query USB Drive capacity; Rejecting"); - return ENOTSUP; - } - - dmesgln(" Block Size: {}B", capacity.block_size); - dmesgln(" Block Count: {}", capacity.block_count); - dmesgln(" Total Size: {}MiB", (u64)capacity.block_size * capacity.block_count / MiB); - - StorageDevice::LUNAddress lun = { - device.controller().storage_controller_id(), - device.address(), - // FIXME: Again, support multiple LUNs per device - 0 - }; - - auto bulk_scsi_interface = TRY(DeviceManagement::try_create_device( - lun, - device.address(), // FIXME: Figure out a better ID to put here - capacity.block_size, - capacity.block_count, - device, - move(in_pipe), - move(out_pipe))); - - m_interfaces.append(bulk_scsi_interface); - StorageManagement::the().add_device(bulk_scsi_interface); - - return {}; -} - -void MassStorageDriver::detach(USB::Device& device) -{ - auto&& interface = AK::find_if(m_interfaces.begin(), m_interfaces.end(), [&device](auto& interface) { return &interface.device() == &device; }); - - StorageManagement::the().remove_device(*interface); - m_interfaces.remove(*interface); -} - -} diff --git a/Kernel/Bus/USB/Drivers/MassStorage/MassStorageDriver.h b/Kernel/Bus/USB/Drivers/MassStorage/MassStorageDriver.h deleted file mode 100644 index b7e29260b5d..00000000000 --- a/Kernel/Bus/USB/Drivers/MassStorage/MassStorageDriver.h +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright (c) 2023, Leon Albrecht - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include - -namespace Kernel::USB { - -class MassStorageDriver final : public Driver { -public: - MassStorageDriver() - : Driver("USB MassStorage"sv) - { - } - - static void init(); - - virtual ~MassStorageDriver() override = default; - - virtual ErrorOr probe(USB::Device&) override; - virtual void detach(USB::Device&) override; - -private: - BulkSCSIInterface::List m_interfaces; - - ErrorOr checkout_interface(USB::Device&, USBInterface const&); - - ErrorOr initialise_bulk_only_device(USB::Device&, USBInterface const&); -}; - -} diff --git a/Kernel/Bus/USB/Drivers/USBDriver.h b/Kernel/Bus/USB/Drivers/USBDriver.h deleted file mode 100644 index 5f9ed3213a1..00000000000 --- a/Kernel/Bus/USB/Drivers/USBDriver.h +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright (c) 2023, Jesse Buhagiar - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include - -namespace Kernel::USB { - -using DriverInitFunction = void (*)(); -#define USB_DEVICE_DRIVER(driver_name) \ - DriverInitFunction driver_init_function_ptr_##driver_name __attribute__((section(".driver_init"), used)) = &driver_name::init - -class Device; -struct USBDeviceDescriptor; -class USBInterface; - -class Driver : public AtomicRefCounted { -public: - Driver(StringView name) - : m_driver_name(name) - { - } - - virtual ~Driver() = default; - - virtual ErrorOr probe(USB::Device&) = 0; - virtual void detach(USB::Device&) = 0; - StringView name() const { return m_driver_name; } - -protected: - StringView const m_driver_name; -}; -} diff --git a/Kernel/Bus/USB/EHCI/DataStructures.h b/Kernel/Bus/USB/EHCI/DataStructures.h deleted file mode 100644 index ee4114c3aae..00000000000 --- a/Kernel/Bus/USB/EHCI/DataStructures.h +++ /dev/null @@ -1,324 +0,0 @@ -/* - * Copyright (c) 2023, Leon Albrecht - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include - -namespace Kernel::USB::EHCI { - -// https://www.intel.com/content/www/us/en/products/docs/io/universal-serial-bus/ehci-specification-for-usb.html -// Section 3 (32 bit structures)and Appendix B (64 bit structures) - -// Table 3-1 Typ Field Value Definitions -enum class Typ : u8 { - iTD = 0b00, - QH = 0b01, - siTD = 0b10, - FSTN = 0b11 -}; - -struct IsochronousTransferDescriptor; -struct SplitTransactionIsochronousTransferDescriptor; -struct QueueElementTransferDescriptor; -struct QueueHead; -struct FrameSpanTraversalNode; - -// 3.1 Periodic Frame List -// Also for "3.3.1 Next Link Pointer" and the like -union FrameListElementPointer { - u32 link_pointer; - struct { - u32 terminate : 1; - Typ typ : 2; - u32 zero : 2; - u32 link_pointer_hi : 27; - }; - - template - static FrameListElementPointer make(PhysicalPtr addr); - template<> - FrameListElementPointer make(PhysicalPtr addr) - { - VERIFY((addr & 0b11111) == 0); - VERIFY(addr == (u32)addr); - return { .terminate = 0, .typ = Typ::iTD, .zero = 0, .link_pointer_hi = (u32)addr >> 5 }; - } - template<> - FrameListElementPointer make(PhysicalPtr addr) - { - VERIFY((addr & 0b11111) == 0); - VERIFY(addr == (u32)addr); - return { .terminate = 0, .typ = Typ::siTD, .zero = 0, .link_pointer_hi = (u32)addr >> 5 }; - } - template<> - FrameListElementPointer make(PhysicalPtr addr) - { - VERIFY((addr & 0b11111) == 0); - VERIFY(addr == (u32)addr); - return { .terminate = 0, .typ = Typ::QH, .zero = 0, .link_pointer_hi = (u32)addr >> 5 }; - } - template<> - FrameListElementPointer make(PhysicalPtr addr) - { - VERIFY((addr & 0b11111) == 0); - VERIFY(addr == (u32)addr); - return { .terminate = 0, .typ = Typ::FSTN, .zero = 0, .link_pointer_hi = (u32)addr >> 5 }; - } -}; - -// 3.3 Isochronous (High-Speed) Transfer Descriptor (iTD) -struct IsochronousTransferDescriptor { - FrameListElementPointer next_link_pointer; - // 3.3.2 iTD Transaction Status and Control List - struct TransactionStatusControl { - u32 transaction_x_offset : 11; // (RW) - u32 page_select : 3; // (RW) - u32 interrupt_on_complete : 1; - u32 transaction_x_length : 12; // RW - // Status Bit Field: // RW - u32 transaction_error : 1; - u32 babble_detected : 1; - u32 data_buffer_error : 1; - u32 active : 1; - } transaction_status_and_control[8]; - - // 3.3.3 iTD Buffer Page Pointer List (Plus) - union { - struct { - u32 reserved : 12; - u32 pointer_hi : 20; - } buffer_pointer_list[7]; - struct { - u32 device_address : 7; - u32 : 1; - u32 endpoint_number : 4; - u32 : 20; - - u32 maximum_packet_size : 11; - u32 direction : 1; - u32 : 20; - - u32 transactions_per_micro_frame : 2; // Multi - u32 : 10; - u32 : 20; - - u32 _[4]; - }; - }; -}; -static_assert(AssertSize()); - -struct IsochronousTransferDescriptor64 : public IsochronousTransferDescriptor { - u32 extended_buffer_pointer_list[7]; -}; -static_assert(AssertSize()); - -// 3.4 Split Transaction Isochronous Transfer Descriptor (siTD) -struct SplitTransactionIsochronousTransferDescriptor { - FrameListElementPointer next_link_pointer; - // 3.4.2 siTD Endpoint Capabilities/Characteristics - // Table 3-9. Endpoint and Transaction Translator Characteristics - struct { - u8 device_address : 6; - u8 reserved0 : 1 { 0 }; - u8 endpoint_number : 4; - u8 reserved1 : 4 { 0 }; - u8 hub_address : 7; - u8 reserved2 : 1 { 0 }; - u8 port_number : 7; - u8 direction : 1; - }; - // Table 3-10. Micro-frame Schedule Control - struct { - u8 split_start_mask : 8; - u8 split_completion_mask : 8; - u16 reserved { 0 }; - } schedule_control; - - // 3.4.3 siTD Transfer State - struct { - struct Status { - u8 reserved : 1 { 0 }; - u8 split_transaction_state : 1; - u8 missed_micro_frame : 1; - u8 transaction_error : 1; - u8 babble_detected : 1; - u8 data_buffer_error : 1; - u8 err : 1; - u8 active : 1; - } status; // RW - u8 micro_frame_complete_split_progress_mask; // RW - u16 total_bytes_to_transfer : 10; // RW - u16 reserved : 4; // RW - u16 page_select : 1; // RW - u16 interrupt_on_complete : 1; - } status_and_control; - - // 3.4.4 siTD Buffer Pointer List (plus) - enum class TransactionPosition : u32 { - All = 0b00, - Begin = 0b01, - Mid = 0b10, - End = 0b11 - }; - union { - struct { - u32 reserved : 12; - u32 pointer_hi : 20; - } buffer_pointer_list[2]; - struct { - u32 current_offset : 12; // RW - u32 : 20; - u32 transaction_count : 3; // RW - TransactionPosition transaction_position : 2; // RW - u32 reserved : 7 { 0 }; - u32 : 20; - }; - }; - // 3.4.5 siTD Back Link Pointer - struct { - u32 terminate : 1; - u32 reserved : 4 { 0 }; - u32 back_pointer_hi : 27; - } back_link_pointer; -}; -static_assert(AssertSize()); - -struct SplitTransactionIsochronousTransferDescriptor64 : public SplitTransactionIsochronousTransferDescriptor { - u32 extended_buffer_pointer_list[2]; -}; -static_assert(AssertSize()); - -// 3.5 Queue Element Transfer Descriptor (qTD) -struct QueueElementTransferDescriptor { - enum class PIDCode : u8 { - OUT = 0b00, // generates token (E1H) - IN = 0b01, // generates token (69H) - SETUP = 0b10, // generates token (2DH) - }; - // 3.5.1 Next qTD Pointer - // Note: the type field is not evaluated here, as is ignored: - // "These bits are reserved and their value has no effect on operation." - // ~ Table 3-14. qTD Next Element Transfer Pointer (DWord 0) - FrameListElementPointer next_qTD_pointer; - // 3.5.2 Alternate Next qTD Pointer - FrameListElementPointer alternate_next_qTD_pointer; - // 3.5.3 qTD Token - struct Status { - u8 ping_state : 1; - u8 split_transaction_state : 1; - u8 missed_micro_frame : 1; - u8 transaction_error : 1; - u8 babble_detected : 1; - u8 data_buffer_error : 1; - u8 halted : 1; - u8 active : 1; - } status; - PIDCode pid_code : 2; - u8 error_counter : 2; - u8 current_page : 3; - u8 interrupt_on_complete : 1; - u16 total_bytes_to_transfer : 15; - u16 data_toggle : 1; - - // 3.5.4 qTD Buffer Page Pointer List - union { - u32 buffer_pointer_list[5]; - struct { - u32 current_page_offset : 12 { 0 }; - u32 : 20; - // Table 3-22. Host-Controller Rules for Bits in Overlay (DWords 5, 6, 8 and 9) - // adds more fields here: - u32 split_transaction_complete_split_progress : 8; // (C-prog-mask) - u32 : 4; - u32 : 20; - u32 split_transaction_frame_tag : 5; - u32 s_bytes : 7; - u32 : 20; - u32 _[2]; - }; - }; -}; -static_assert(AssertSize()); - -struct QueueElementTransferDescriptor64 : public QueueElementTransferDescriptor { - u32 extended_buffer_pointer_list[5]; -}; -static_assert(AssertSize()); - -// 3.6 Queue Head -struct QueueHead { - // 3.6.1 Queue Head Horizontal Link Pointer - FrameListElementPointer queue_head_horizontal_link_pointer; - // 3.6.2 Endpoint Capabilities/Characteristics - struct EndpointCharacteristics { - u32 device_address : 6; - u32 inactive_on_next_transaction : 1; - u32 endpoint_number : 4; - enum class EndpointSpeed : u32 { - FullSpeed = 0b00, - LowSpeed = 0b01, - HighSpeed = 0b10, - } endpoint_speed : 2; - u32 data_toggle_control : 1; - u32 head_of_reclamation_list_flag : 1; - u32 maximum_packet_length : 11; - u32 control_endpoint_flag : 1; - u32 nak_count_reload : 4; - } endpoint_characteristics; - struct EndpointCapabilities { - u32 interrupt_shedule_mask : 8; - u32 split_completion_mask : 8; - u32 hub_address : 7; - u32 port_number : 7; - u32 high_bandwidth_multiplier : 2; - } endpoint_capabilities; - - // 3.6.3 Transfer Overlay - // Note: The lower bits (T, Typ) are ignored - FrameListElementPointer current_transaction_pointer; - // "The DWords 4-11 of a queue head are the transaction overlay area. This area has the same base structure as - // a Queue Element Transfer Descriptor, defined in Section 3.5. The queue head utilizes the reserved fields of - // the page pointers defined in Figure 3-7 to implement tracking the state of split transactions" - // Table 3-22. Host-Controller Rules for Bits in Overlay (DWords 5, 6, 8 and 9) - // FIXME: Do this with less code duplication - enum class PIDCode : u8 { - OUT = 0b00, // generates token (E1H) - IN = 0b01, // generates token (69H) - SETUP = 0b10, // generates token (2DH) - }; - // 3.5.1 Next qTD Pointer - // Note: the type field is not evaluated here, as is ignored: - // "These bits are reserved and their value has no effect on operation." - // ~ Table 3-14. qTD Next Element Transfer Pointer (DWord 0) - union { - QueueElementTransferDescriptor overlay; - struct { - u32 : 32; - u32 : 1; - - u32 nak_counter : 4; - u32 : 27; - u32 _[sizeof(QueueElementTransferDescriptor) / 4 - 2]; - }; - }; -}; -static_assert(AssertSize()); - -struct QueueHead64 : public QueueHead { - u32 extended_buffer_pointer_list[5]; -}; -static_assert(AssertSize()); - -// 3.7 Periodic Frame Span Traversal Node (FSTN) -struct FrameSpanTraversalNode { - FrameListElementPointer normal_path_link_pointer; - FrameListElementPointer back_path_link_pointer; -}; - -} diff --git a/Kernel/Bus/USB/EHCI/EHCIController.cpp b/Kernel/Bus/USB/EHCI/EHCIController.cpp deleted file mode 100644 index 484e1bf1bb6..00000000000 --- a/Kernel/Bus/USB/EHCI/EHCIController.cpp +++ /dev/null @@ -1,83 +0,0 @@ -/* - * Copyright (c) 2023, Leon Albrecht - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include - -namespace Kernel::USB::EHCI { - -ErrorOr> EHCIController::try_to_initialize(const PCI::DeviceIdentifier& pci_device_identifier) -{ - - // FIXME: This assumes the BIOS left us a physical region for the controller - auto pci_bar_address = TRY(PCI::get_bar_address(pci_device_identifier, SpaceBaseAddressRegister)); - auto pci_bar_space_size = PCI::get_BAR_space_size(pci_device_identifier, SpaceBaseAddressRegister); - - auto register_region_size = TRY(Memory::page_round_up(pci_bar_address.offset_in_page() + pci_bar_space_size)); - auto register_region = TRY(MM.allocate_mmio_kernel_region(pci_bar_address.page_base(), register_region_size, {}, Memory::Region::Access::ReadWrite)); - - VirtualAddress register_base_address = register_region->vaddr().offset(pci_bar_address.offset_in_page()); - - PCI::enable_bus_mastering(pci_device_identifier); - PCI::enable_memory_space(pci_device_identifier); - - auto controller = TRY(adopt_nonnull_lock_ref_or_enomem(new (nothrow) EHCIController(pci_device_identifier, move(register_region), register_base_address))); - - TRY(controller->initialize()); - - return controller; -} - -EHCIController::EHCIController(PCI::DeviceIdentifier const& pci_device_identifier, NonnullOwnPtr register_region, VirtualAddress register_base_address) - : PCI::Device(pci_device_identifier) - , m_register_region(move(register_region)) -{ - m_cap_regs = bit_cast(register_base_address.get()); - m_op_regs = bit_cast(register_base_address.get() + m_cap_regs->capability_length); -} - -ErrorOr EHCIController::initialize() -{ - dmesgln_pci(*this, "Controller found {} @ {}", PCI::get_hardware_id(device_identifier()), device_identifier().address()); - dmesgln_pci(*this, "Version {}.{}", m_cap_regs->interface_version.major, m_cap_regs->interface_version.minor); - u8 n_ports = m_cap_regs->structural_parameters.n_ports; - dmesgln_pci(*this, "NPorts: {}", n_ports); - u8 n_cc = m_cap_regs->structural_parameters.n_companion_controllers; - u8 n_pcc = m_cap_regs->structural_parameters.n_ports_per_companion_controller; - dmesgln_pci(*this, "Companion Controllers: {}", n_cc); - dmesgln_pci(*this, "Ports per Companion Controllers: {}", n_pcc); - - if (n_ports > n_cc * n_pcc) { - dmesgln_pci(*this, "Warning: Not all ports of the EHCI controller are addressable via companion controllers"); - dmesgln_pci(*this, " Some USB 2.0 ports might not be functional"); - } - - u8 EECP = m_cap_regs->capability_parameters.ehci_extended_capabilities_pointer; - if (EECP) { - SpinlockLocker locker(device_identifier().operation_lock()); - auto legacy_support = bit_cast(PCI::read32_locked(device_identifier(), PCI::RegisterOffset { EECP })); - if (legacy_support.HC_BIOS_owned_semaphore) - dmesgln_pci(*this, "Warning: EHCI controller is BIOS owned"); - } - - // FIXME: Decide which Interrupts we want - // FIXME: Detect and switch on 64 bit support - // FIXME: Allocate and initialize Task Lists - // * Synchronous - // * Asynchronous - // * Leave space for for the actual list items - // and IO scratch space in case we cannot use the buffer from the request - - // FIXME: Initialize the controller and start it - // * Setup the root hub emulation - // * Enable Software routing (CF) - // * Maybe configure port power - - return {}; -} - -} diff --git a/Kernel/Bus/USB/EHCI/EHCIController.h b/Kernel/Bus/USB/EHCI/EHCIController.h deleted file mode 100644 index 1517bd782be..00000000000 --- a/Kernel/Bus/USB/EHCI/EHCIController.h +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright (c) 2023, Leon Albrecht - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include - -namespace Kernel::USB::EHCI { - -class EHCIController : public USBController - , public PCI::Device { -public: - static ErrorOr> try_to_initialize(PCI::DeviceIdentifier const& pci_device_identifier); - virtual ~EHCIController() override = default; - - // ^PCI::Device - virtual StringView device_name() const override { return "EHCI"sv; } - - // ^USBController - virtual ErrorOr initialize() override; - - virtual ErrorOr reset() override { return ENOTSUP; } - virtual ErrorOr stop() override { return ENOTSUP; } - virtual ErrorOr start() override { return ENOTSUP; } - - virtual void cancel_async_transfer(NonnullLockRefPtr) override {}; - virtual ErrorOr submit_control_transfer(Transfer&) override { return ENOTSUP; } - virtual ErrorOr submit_bulk_transfer(Transfer&) override { return ENOTSUP; } - virtual ErrorOr submit_async_interrupt_transfer(NonnullLockRefPtr, u16) override { return ENOTSUP; } - -private: - EHCIController(PCI::DeviceIdentifier const& pci_device_identifier, NonnullOwnPtr register_region, VirtualAddress register_base_address); - - NonnullOwnPtr m_register_region; - CapabilityRegisters const* m_cap_regs; - OperationalRegisters volatile* m_op_regs; -}; - -} diff --git a/Kernel/Bus/USB/EHCI/Registers.h b/Kernel/Bus/USB/EHCI/Registers.h deleted file mode 100644 index 3a9caa26f0a..00000000000 --- a/Kernel/Bus/USB/EHCI/Registers.h +++ /dev/null @@ -1,284 +0,0 @@ -/* - * Copyright (c) 2023, Leon Albrecht - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include - -namespace Kernel::USB::EHCI { - -// https://www.intel.com/content/www/us/en/products/docs/io/universal-serial-bus/ehci-specification-for-usb.html - -// 2.1.3 USBBASE - Register Space Base Address Register -// Address Offset: 10−13h => BAR0 -constexpr auto SpaceBaseAddressRegister = PCI::HeaderType0BaseRegister::BAR0; - -union BaseRegister { - enum class MappingSupport64Bit : u32 { - No = 0b00, - Yes = 0b10 - }; - struct { - - u32 : 1; - MappingSupport64Bit mapping_support : 2; - u32 : 5; - u32 base_address_hi : 24; - }; - u32 raw; -}; -static_assert(AssertSize()); - -// 2.1.4 SBRN - Serial Bus Release Number Register -// Address Offset: 60h -// Attribute: RO -// Size: 8 bits -// Note: Assuming the layout based on the default value of 0x20 representing USB 2.0 -struct SBRN { - u8 minor : 4; - u8 major : 4; -}; -static_assert(AssertSize()); - -// 2.1.7 USBLEGSUP - USB Legacy Support Extended Capability -// Offset: EECP + 00h -// Attribute RO, R/W -// Size: 32 bits -struct LegacySupport { - u8 capability : 8; - u8 next_ehci_extended_capabilites_pointer : 8; - // These should be u8's as we want individual accesses to these bits, - // if we decide to cooperative ownership of the Controller with the BIOS - u8 HC_BIOS_owned_semaphore : 1; - u8 : 7; - u8 HC_OS_owned_semaphore : 1; - u8 : 7; -}; -static_assert(AssertSize()); - -// 2.1.8 USBLEGCTLSTS - USB Legacy Support Control/Status -// Offset: EECP + 04h -// Default Value 00000000h -// Size: 32 bits -struct LegacySupportControl { - u32 smi_enable : 1; - u32 smi_on_usb_error_enable : 1; - u32 smi_on_port_change_enable : 1; - u32 smi_on_frame_list_rollover_enable : 1; - u32 smi_on_sys_error_enable : 1; - u32 smi_on_async_advance_enable : 1; - u32 : 7; - u32 smi_on_os_ownership_enable : 1; - u32 smi_on_pci_command_enable : 1; - u32 smi_on_bar_enable : 1; - - u32 smi_on_usb_complete : 1; - u32 smi_on_usb_error : 1; - u32 smi_on_port_change_detected : 1; - u32 smi_on_frame_list_rollover : 1; - u32 smi_on_host_system_error : 1; - u32 smi_on_async_advance : 1; - u32 : 7; - u32 smi_on_os_ownership_change : 1; - u32 smi_on_pci_command : 1; - u32 smi_on_bar : 1; -}; -static_assert(AssertSize()); - -// 2.2 Host Controller Capability Registers -struct CapabilityRegisters { - // 2.2.1 CAPLENGTH - Capability Registers Length - u8 capability_length; // Offset to beginning to Operational Register - u8 : 8; - - // 2.2.2 HCIVERSION - Host Controller Interface Version Number - struct InterfaceVersion { - u8 minor; - u8 major; - } interface_version; - static_assert(AssertSize()); - - // 2.2.3 HCSPARAMS - Structural Parameters - struct StructuralParameters { - u32 n_ports : 4; - u32 port_power_control : 1; // N_PPC - u32 : 2; - u32 port_routing_rules : 1; - u32 n_ports_per_companion_controller : 4; // N_PCC - u32 n_companion_controllers : 4; // N_CC - u32 port_indicators : 1; // P_INDICATOR - u32 : 3; - u32 debug_port_number : 4; - u32 : 8; - } structural_parameters; - static_assert(AssertSize()); - - // 2.2.4 HCCPARAMS - Capability Parameters - struct CapabilityParameters { - u32 addressing_capability_64bit : 1; - u32 programmable_frame_list_flag : 1; - u32 asynchronous_schedule_park_capability : 1; - u32 : 1; - u32 isochronous_scheduling_threshold : 4; - u32 ehci_extended_capabilities_pointer : 8; // EECP - u32 : 16; - } capability_parameters; - static_assert(AssertSize()); - - // 2.2.5 HCSP-PORTROUTE - Companion Port Route Description - // Note: Technically only 60 bits - // Technically a u4[n_ports] - u32 companion_port_route_description[2]; -}; -// Table 2-5. Enhanced Host Controller Capability Registers -static_assert(__builtin_offsetof(CapabilityRegisters, capability_length) == 0x00); -static_assert(__builtin_offsetof(CapabilityRegisters, interface_version) == 0x02); -static_assert(__builtin_offsetof(CapabilityRegisters, structural_parameters) == 0x04); -static_assert(__builtin_offsetof(CapabilityRegisters, capability_parameters) == 0x08); -static_assert(__builtin_offsetof(CapabilityRegisters, companion_port_route_description) == 0x0C); - -// 2.3 Host Controller Operational Registers -struct OperationalRegisters { - // 2.3.1 USBCMD - USB Command Register - // Default Value: 00080000h (00080B00h if Asynchronous Schedule Park Capability is a one) - union CommandRegister { - struct { - u32 run_stop : 1; // RS - u32 reset : 1; // HCRESET - u32 frame_list_size : 2; // 1024 / N Elements | N < 0b11 - u32 periodic_schedule_enable : 1; - u32 asynchronous_schedule_enable : 1; - u32 interrupt_on_async_advance_doorbell : 1; - u32 light_host_controller_reset : 1; - u32 asynchronous_schedule_park_mode_count : 2; - u32 : 1; - u32 asynchronous_schedule_park_mode_enable : 1; - u32 : 4; - u32 interrupt_threshold_control : 8; - u32 : 8; - }; - u32 raw; - } command; - static_assert(AssertSize()); - - // 2.3.2 USBSTS - USB Status Register - // Default Value: 00001000h - union StatusRegister { - // To zero an interrupt use a selective write to raw, as otherwise other - // interrupt bits might be cleared as well - const struct { - u32 interrupt : 1; // R/WC - u32 error_interrupt : 1; // R/WC - u32 port_change_detect : 1; // R/WC - u32 frame_list_rollover : 1; // R/WC - u32 host_system_error : 1; // R/WC - u32 interrupt_on_async_advance : 1; // R/WC - u32 : 6; - u32 const hc_halted : 1; - u32 const periodic_schedule_status : 1; - u32 const asynchronous_schedule_status : 1; - u32 : 16; - }; - u32 raw; - } status; - static_assert(AssertSize()); - - // 2.3.3 USBINTR - USB Interrupt Enable Register - struct InterruptEnable { - u32 usb_interrupt_enable : 1; - u32 usb_error_interrupt_enable : 1; - u32 port_change_enable : 1; - u32 frame_list_rollover_enable : 1; - u32 host_system_error_enable : 1; - u32 interrupt_on_async_advance_enable : 1; - u32 : 26; - } interrupt_enable; - static_assert(AssertSize()); - - // 2.3.4 FRINDEX - Frame Index Register - // Note: We use `volatile` to ensure 32 bit writes - // Note: Only up to 14 bits are actually used, and the last 3 bits must never be `000` or `111` - u32 volatile frame_index; - - // 2.3.5 CTRLDSSEGMENT - Control Data Structure Segment Register - // Note: We use `volatile` to ensure 32 bit writes - // Note: Upper 32 bits of periodic-frame- and asynchronous-list pointers - u32 volatile segment_selector; - - // 2.3.6 PERIODICLISTBASE - Periodic Frame List Base Address Register - // Note: We use `volatile` to ensure 32 bit writes - // Note: Page-aligned addresses only - u32 volatile frame_list_base_address; - // 2.3.7 ASYNCLISTADDR - Current Asynchronous List Address Register - // Note: We use `volatile` to ensure 32 bit writes - // Note: 32 byte (cache-line) aligned addresses only - u32 volatile next_asynchronous_list_address; - - u32 _padding[9]; - - // 2.3.8 CONFIGFLAG - Configure Flag Register - u32 configured_flag; - - union PortStatusControl { - enum class LineStatus : u32 { - SE0 = 0b00, - J_State = 0b10, - K_State = 0b01, - Undefined = 0b11 - }; - enum class PortIndicatorControl : u32 { - Off = 0b00, - Amber = 0b01, - Green = 0b10, - Undefined = 0b11 - }; - enum class PortTestControl : u32 { - NotEnabled = 0b0000, - J_State = 0b0001, - K_State = 0b0010, - SE0_NAK = 0b0011, - Packet = 0b0100, - Force_Enable = 0b0101, - }; - - const struct { - u32 current_connect_status : 1; - u32 connect_status_change : 1; // R/WC - u32 port_enable : 1; - u32 port_enable_change : 1; // R/WC - u32 over_current_active : 1; - u32 over_current_change : 1; // R/WC - u32 force_resume : 1; - u32 suspend : 1; - u32 port_reset : 1; - u32 : 1; - LineStatus line_status : 2; - u32 port_power : 1; - u32 port_owner : 1; - PortIndicatorControl port_indicator_control : 2; - PortTestControl port_test_control : 4; - u32 wake_on_connect_enable : 1; // WKCNNT_E - u32 wake_on_disconnect_enable : 1; // WKDSCNNT_E - u32 wake_on_over_current_enable : 1; // WKOC_E - u32 : 9; - }; - u32 raw; - } port_status_control[]; - static_assert(AssertSize()); -}; -// Table 2-8. Host Controller Operational Registers -static_assert(__builtin_offsetof(OperationalRegisters, command) == 0x00); -static_assert(__builtin_offsetof(OperationalRegisters, status) == 0x04); -static_assert(__builtin_offsetof(OperationalRegisters, interrupt_enable) == 0x08); -static_assert(__builtin_offsetof(OperationalRegisters, frame_index) == 0x0C); -static_assert(__builtin_offsetof(OperationalRegisters, segment_selector) == 0x10); -static_assert(__builtin_offsetof(OperationalRegisters, frame_list_base_address) == 0x14); -static_assert(__builtin_offsetof(OperationalRegisters, next_asynchronous_list_address) == 0x18); -static_assert(__builtin_offsetof(OperationalRegisters, configured_flag) == 0x40); -static_assert(__builtin_offsetof(OperationalRegisters, port_status_control) == 0x44); - -} diff --git a/Kernel/Bus/USB/PacketTypes.h b/Kernel/Bus/USB/PacketTypes.h deleted file mode 100644 index 8dc65094ef2..00000000000 --- a/Kernel/Bus/USB/PacketTypes.h +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright (c) 2021, Jesse Buhagiar - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include - -namespace Kernel::USB { - -// Setup descriptor bit definitions -static constexpr u8 BM_REQUEST_HOST_TO_DEVICE = (0 << 7); -static constexpr u8 BM_REQUEST_DEVICE_TO_HOST = (1 << 7); -static constexpr u8 BM_REQUEST_TYPE_STANDARD = (0 << 5); -static constexpr u8 BM_REQUEST_TYPE_CLASS = (1 << 5); -static constexpr u8 BM_REQUEST_TYPE_VENDOR = (2 << 5); -static constexpr u8 BM_REQUEST_TYPE_RESERVED = (3 << 5); -static constexpr u8 BM_REQUEST_RECIPEINT_DEVICE = (0 << 0); -static constexpr u8 BM_REQUEST_RECIPIENT_INTERFACE = (1 << 0); -static constexpr u8 BM_REQUEST_RECIPIENT_ENDPOINT = (2 << 0); -static constexpr u8 BM_REQUEST_RECIPIENT_OTHER = (3 << 0); - -// -// This is also known as the "setup" packet. It's attached to the -// first TD in the chain and is the first piece of data sent to the -// USB device over the bus. -// https://beyondlogic.org/usbnutshell/usb6.shtml#StandardEndpointRequests -// -struct USBRequestData { - u8 request_type; - u8 request; - u16 value; - u16 index; - u16 length; -}; - -} diff --git a/Kernel/Bus/USB/UHCI/UHCIController.cpp b/Kernel/Bus/USB/UHCI/UHCIController.cpp deleted file mode 100644 index 2b2e66bf70b..00000000000 --- a/Kernel/Bus/USB/UHCI/UHCIController.cpp +++ /dev/null @@ -1,821 +0,0 @@ -/* - * Copyright (c) 2020-2021, Andreas Kling - * Copyright (c) 2020, Jesse Buhagiar - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -static constexpr u8 RETRY_COUNTER_RELOAD = 3; - -namespace Kernel::USB { - -static constexpr u16 UHCI_USBCMD_RUN = 0x0001; -static constexpr u16 UHCI_USBCMD_HOST_CONTROLLER_RESET = 0x0002; -static constexpr u16 UHCI_USBCMD_GLOBAL_RESET = 0x0004; -static constexpr u16 UHCI_USBCMD_ENTER_GLOBAL_SUSPEND_MODE = 0x0008; -static constexpr u16 UHCI_USBCMD_FORCE_GLOBAL_RESUME = 0x0010; -static constexpr u16 UHCI_USBCMD_SOFTWARE_DEBUG = 0x0020; -static constexpr u16 UHCI_USBCMD_CONFIGURE_FLAG = 0x0040; -static constexpr u16 UHCI_USBCMD_MAX_PACKET = 0x0080; - -static constexpr u16 UHCI_USBSTS_HOST_CONTROLLER_HALTED = 0x0020; -static constexpr u16 UHCI_USBSTS_HOST_CONTROLLER_PROCESS_ERROR = 0x0010; -static constexpr u16 UHCI_USBSTS_PCI_BUS_ERROR = 0x0008; -static constexpr u16 UHCI_USBSTS_RESUME_RECEIVED = 0x0004; -static constexpr u16 UHCI_USBSTS_USB_ERROR_INTERRUPT = 0x0002; -static constexpr u16 UHCI_USBSTS_USB_INTERRUPT = 0x0001; - -static constexpr u8 UHCI_USBINTR_TIMEOUT_CRC_ENABLE = 0x01; -static constexpr u8 UHCI_USBINTR_RESUME_INTR_ENABLE = 0x02; -static constexpr u8 UHCI_USBINTR_IOC_ENABLE = 0x04; -static constexpr u8 UHCI_USBINTR_SHORT_PACKET_INTR_ENABLE = 0x08; - -static constexpr u16 UHCI_FRAMELIST_FRAME_COUNT = 1024; // Each entry is 4 bytes in our allocated page -static constexpr u16 UHCI_FRAMELIST_FRAME_INVALID = 0x0001; - -// Port stuff -static constexpr u8 UHCI_ROOT_PORT_COUNT = 2; -static constexpr u16 UHCI_PORTSC_CURRENT_CONNECT_STATUS = 0x0001; -static constexpr u16 UHCI_PORTSC_CONNECT_STATUS_CHANGED = 0x0002; -static constexpr u16 UHCI_PORTSC_PORT_ENABLED = 0x0004; -static constexpr u16 UHCI_PORTSC_PORT_ENABLE_CHANGED = 0x0008; -static constexpr u16 UHCI_PORTSC_LINE_STATUS = 0x0030; -static constexpr u16 UHCI_PORTSC_RESUME_DETECT = 0x40; -static constexpr u16 UHCI_PORTSC_LOW_SPEED_DEVICE = 0x0100; -static constexpr u16 UHCI_PORTSC_PORT_RESET = 0x0200; -static constexpr u16 UHCI_PORTSC_SUSPEND = 0x1000; -static constexpr u16 UHCI_PORTSC_NON_WRITE_CLEAR_BIT_MASK = 0x1FF5; // This is used to mask out the Write Clear bits making sure we don't accidentally clear them. - -// *BSD and a few other drivers seem to use this number -static constexpr u8 UHCI_NUMBER_OF_ISOCHRONOUS_TDS = 128; -static constexpr u16 UHCI_NUMBER_OF_FRAMES = 1024; - -ErrorOr> UHCIController::try_to_initialize(PCI::DeviceIdentifier const& pci_device_identifier) -{ - // NOTE: This assumes that address is pointing to a valid UHCI controller. - auto registers_io_window = TRY(IOWindow::create_for_pci_device_bar(pci_device_identifier, PCI::HeaderType0BaseRegister::BAR4)); - auto controller = TRY(adopt_nonnull_lock_ref_or_enomem(new (nothrow) UHCIController(pci_device_identifier, move(registers_io_window)))); - TRY(controller->initialize()); - return controller; -} - -ErrorOr UHCIController::initialize() -{ - dmesgln_pci(*this, "Controller found {} @ {}", PCI::get_hardware_id(device_identifier()), device_identifier().address()); - dmesgln_pci(*this, "I/O base {}", m_registers_io_window); - dmesgln_pci(*this, "Interrupt line: {}", interrupt_number()); - - TRY(spawn_async_poll_process()); - TRY(spawn_port_process()); - - TRY(reset()); - return start(); -} - -UNMAP_AFTER_INIT UHCIController::UHCIController(PCI::DeviceIdentifier const& pci_device_identifier, NonnullOwnPtr registers_io_window) - : PCI::Device(const_cast(pci_device_identifier)) - , IRQHandler(pci_device_identifier.interrupt_line().value()) - , m_registers_io_window(move(registers_io_window)) -{ -} - -UNMAP_AFTER_INIT UHCIController::~UHCIController() = default; - -ErrorOr UHCIController::reset() -{ - TRY(stop()); - - write_usbcmd(UHCI_USBCMD_HOST_CONTROLLER_RESET); - - // FIXME: Timeout - for (;;) { - if (read_usbcmd() & UHCI_USBCMD_HOST_CONTROLLER_RESET) - continue; - break; - } - - // Let's allocate the physical page for the Frame List (which is 4KiB aligned) - m_framelist = TRY(MM.allocate_dma_buffer_page("UHCI Framelist"sv, Memory::Region::Access::Write)); - dbgln("UHCI: Allocated framelist at physical address {}", m_framelist->physical_page(0)->paddr()); - dbgln("UHCI: Framelist is at virtual address {}", m_framelist->vaddr()); - write_sofmod(64); // 1mS frame time - - TRY(create_structures()); - - setup_schedule(); - - write_flbaseadd(m_framelist->physical_page(0)->paddr().get()); // Frame list (physical) address - write_frnum(0); // Set the initial frame number - - // FIXME: Work out why interrupts lock up the entire system.... - // Disable UHCI Controller from raising an IRQ - write_usbintr(0); - dbgln("UHCI: Reset completed"); - - return {}; -} - -UNMAP_AFTER_INIT ErrorOr UHCIController::create_structures() -{ - m_queue_head_pool = TRY(UHCIDescriptorPool::try_create("Queue Head Pool"sv)); - - // Doesn't do anything other than give interrupt transfer queues something to set as prev QH so that we don't have to handle that as an extra edge case - m_schedule_begin_anchor = allocate_queue_head(); - - // Create the Interrupt, Full Speed, Low Speed Control and Bulk Queue Heads - m_interrupt_qh_anchor = allocate_queue_head(); - m_ls_control_qh_anchor = allocate_queue_head(); - m_fs_control_qh_anchor = allocate_queue_head(); - m_bulk_qh_anchor = allocate_queue_head(); - - // Now the Transfer Descriptor pool - m_transfer_descriptor_pool = TRY(UHCIDescriptorPool::try_create("Transfer Descriptor Pool"sv)); - - m_isochronous_transfer_pool = TRY(MM.allocate_dma_buffer_page("UHCI Isochronous Descriptor Pool"sv, Memory::Region::Access::ReadWrite)); - - // Set up the Isochronous Transfer Descriptor list - m_iso_td_list.resize(UHCI_NUMBER_OF_ISOCHRONOUS_TDS); - for (size_t i = 0; i < m_iso_td_list.size(); i++) { - auto placement_addr = reinterpret_cast(m_isochronous_transfer_pool->vaddr().get() + (i * sizeof(Kernel::USB::TransferDescriptor))); - auto paddr = static_cast(m_isochronous_transfer_pool->physical_page(0)->paddr().get() + (i * sizeof(Kernel::USB::TransferDescriptor))); - - // Place a new Transfer Descriptor with a 1:1 in our region - // The pointer returned by `new()` lines up exactly with the value - // that we store in `paddr`, meaning our member functions directly - // access the raw descriptor (that we later send to the controller) - m_iso_td_list.at(i) = new (placement_addr) Kernel::USB::TransferDescriptor(paddr); - auto transfer_descriptor = m_iso_td_list.at(i); - transfer_descriptor->set_in_use(true); // Isochronous transfers are ALWAYS marked as in use (in case we somehow get allocated one...) - transfer_descriptor->set_isochronous(); - - if constexpr (UHCI_VERBOSE_DEBUG) - transfer_descriptor->print(); - } - - if constexpr (UHCI_DEBUG) { - dbgln("UHCI: Pool information:"); - m_queue_head_pool->print_pool_information(); - m_transfer_descriptor_pool->print_pool_information(); - } - - return {}; -} - -UNMAP_AFTER_INIT void UHCIController::setup_schedule() -{ - // - // https://github.com/alkber/minix3-usbsubsystem/blob/master/usb/uhci-hcd.c - // - // This lad probably has the best explanation as to how this is actually done. I'll try and - // explain it here to so that there's no need for anyone to go hunting for this shit again, because - // the USB spec and Intel explain next to nothing. - // According to the USB spec (and the UHCI datasheet), 90% of the bandwidth should be used for - // Isochronous and """Interrupt""" related transfers, with the rest being used for control and bulk - // transfers. - // That is, most of the time, the schedule is going to be executing either an Isochronous transfer - // in our framelist, or an Interrupt transfer. The allocation in `create_structures` reflects this. - // - // Each frame has it's own Isochronous transfer Transfer Descriptor(s) that point to each other - // horizontally in the list. The end of these transfers then point to the Interrupt Queue Headers, - // in which we can attach Transfer Descriptors (related to Interrupt Transfers). These are attached - // to the Queue Head _vertically_. We need to ensure that these are executed every 8ms, so they are inserted - // at different points in the schedule (TODO: How do we do this?!?!). After the Interrupt Transfer Queue Heads, - // we attach the Control Queue Heads. We need two in total, one for Low Speed devices, and one for Full Speed - // USB devices. Finally, we attach the Bulk Transfer Queue Head. - // Not specified in the datasheet, however, is another Queue Head with an "inactive" Transfer Descriptor. This - // is to circumvent a bug in the silicon of the PIIX4's UHCI controller. - // https://github.com/openbsd/src/blob/master/sys/dev/usb/uhci.c#L390 - m_schedule_begin_anchor->link_next_queue_head(m_interrupt_qh_anchor); - m_schedule_begin_anchor->terminate_element_link_ptr(); - - m_interrupt_qh_anchor->link_next_queue_head(m_ls_control_qh_anchor); - m_interrupt_qh_anchor->terminate_element_link_ptr(); - - m_ls_control_qh_anchor->link_next_queue_head(m_fs_control_qh_anchor); - m_ls_control_qh_anchor->terminate_element_link_ptr(); - - m_fs_control_qh_anchor->link_next_queue_head(m_bulk_qh_anchor); - m_fs_control_qh_anchor->terminate_element_link_ptr(); - - auto piix4_td_hack = allocate_transfer_descriptor(); - piix4_td_hack->terminate(); - piix4_td_hack->set_max_len(0x7ff); // Null data packet - piix4_td_hack->set_device_address(0x7f); - piix4_td_hack->set_packet_id(PacketID::IN); - m_bulk_qh_anchor->link_next_queue_head(m_fs_control_qh_anchor); - m_bulk_qh_anchor->attach_transfer_descriptor_chain(piix4_td_hack); - - u32* framelist = reinterpret_cast(m_framelist->vaddr().as_ptr()); - for (int frame_num = 0; frame_num < UHCI_NUMBER_OF_FRAMES; frame_num++) { - auto& frame_iso_td = m_iso_td_list.at(frame_num % UHCI_NUMBER_OF_ISOCHRONOUS_TDS); - frame_iso_td->link_queue_head(m_schedule_begin_anchor->paddr()); - framelist[frame_num] = frame_iso_td->paddr(); - } - - m_interrupt_qh_anchor->print(); - m_ls_control_qh_anchor->print(); - m_fs_control_qh_anchor->print(); - m_bulk_qh_anchor->print(); -} - -QueueHead* UHCIController::allocate_queue_head() -{ - return m_queue_head_pool->try_take_free_descriptor(); -} - -TransferDescriptor* UHCIController::allocate_transfer_descriptor() -{ - return m_transfer_descriptor_pool->try_take_free_descriptor(); -} - -ErrorOr UHCIController::stop() -{ - write_usbcmd(read_usbcmd() & ~UHCI_USBCMD_RUN); - // FIXME: Timeout - for (;;) { - if (read_usbsts() & UHCI_USBSTS_HOST_CONTROLLER_HALTED) - break; - } - return {}; -} - -ErrorOr UHCIController::start() -{ - write_usbcmd(read_usbcmd() | UHCI_USBCMD_RUN); - // FIXME: Timeout - for (;;) { - if (!(read_usbsts() & UHCI_USBSTS_HOST_CONTROLLER_HALTED)) - break; - } - dbgln("UHCI: Started"); - - m_root_hub = TRY(UHCIRootHub::try_create(*this)); - TRY(m_root_hub->setup({})); - return {}; -} - -TransferDescriptor* UHCIController::create_transfer_descriptor(Pipe& pipe, PacketID direction, size_t data_len) -{ - TransferDescriptor* td = allocate_transfer_descriptor(); - if (td == nullptr) { - return nullptr; - } - - u16 max_len = (data_len > 0) ? (data_len - 1) : 0x7ff; - VERIFY(max_len <= 0x4FF || max_len == 0x7FF); // According to the datasheet, anything in the range of 0x500 to 0x7FE are illegal - - td->set_token((max_len << TD_TOKEN_MAXLEN_SHIFT) | ((pipe.data_toggle() ? 1 : 0) << TD_TOKEN_DATA_TOGGLE_SHIFT) | (pipe.endpoint_address() << TD_TOKEN_ENDPOINT_SHIFT) | (pipe.device_address() << TD_TOKEN_DEVICE_ADDR_SHIFT) | (static_cast(direction))); - pipe.set_toggle(!pipe.data_toggle()); - - if (pipe.type() == Pipe::Type::Isochronous) { - td->set_isochronous(); - } else { - if (direction == PacketID::IN) { - td->set_short_packet_detect(); - } - } - - // Set low-speed bit if the device connected to port is a low=speed device (probably unlikely...) - if (pipe.device_speed() == Pipe::DeviceSpeed::LowSpeed) { - td->set_lowspeed(); - } - - td->set_active(); - td->set_error_retry_counter(RETRY_COUNTER_RELOAD); - - return td; -} - -ErrorOr UHCIController::create_chain(Pipe& pipe, PacketID direction, Ptr32& buffer_address, size_t max_size, size_t transfer_size, TransferDescriptor** td_chain, TransferDescriptor** last_td) -{ - // We need to create `n` transfer descriptors based on the max - // size of each transfer (which we've learned from the device already by reading - // its device descriptor, or 8 bytes). Each TD then has its buffer pointer - // set to the initial buffer address + (max_size * index), where index is - // the ID of the TD in the chain. - size_t byte_count = 0; - TransferDescriptor* current_td = nullptr; - TransferDescriptor* prev_td = nullptr; - TransferDescriptor* first_td = nullptr; - - // Keep creating transfer descriptors while we still have some data - while (byte_count < transfer_size) { - size_t packet_size = transfer_size - byte_count; - if (packet_size > max_size) { - packet_size = max_size; - } - - current_td = create_transfer_descriptor(pipe, direction, packet_size); - if (current_td == nullptr) { - free_descriptor_chain(first_td); - return ENOMEM; - } - - if (Checked::addition_would_overflow(reinterpret_cast(&*buffer_address), byte_count)) - return EOVERFLOW; - - auto buffer_pointer = Ptr32(buffer_address + byte_count); - current_td->set_buffer_address(buffer_pointer); - byte_count += packet_size; - - if (prev_td != nullptr) - prev_td->insert_next_transfer_descriptor(current_td); - else - first_td = current_td; - - prev_td = current_td; - } - - *last_td = current_td; - *td_chain = first_td; - return {}; -} - -void UHCIController::free_descriptor_chain(TransferDescriptor* first_descriptor) -{ - TransferDescriptor* descriptor = first_descriptor; - - while (descriptor) { - TransferDescriptor* next = descriptor->next_td(); - - descriptor->free(); - m_transfer_descriptor_pool->release_to_pool(descriptor); - descriptor = next; - } -} - -void UHCIController::enqueue_qh(QueueHead* transfer_queue, QueueHead* anchor) -{ - SpinlockLocker locker(m_schedule_lock); - - auto prev_qh = anchor->prev_qh(); - prev_qh->link_next_queue_head(transfer_queue); - transfer_queue->link_next_queue_head(anchor); -} - -void UHCIController::dequeue_qh(QueueHead* transfer_queue) -{ - SpinlockLocker locker(m_schedule_lock); - transfer_queue->prev_qh()->link_next_queue_head(transfer_queue->next_qh()); -} - -ErrorOr UHCIController::create_transfer_queue(Transfer& transfer) -{ - Pipe& pipe = transfer.pipe(); - - // Create a new descriptor chain - TransferDescriptor* last_data_descriptor; - TransferDescriptor* data_descriptor_chain; - auto buffer_address = Ptr32(transfer.buffer_physical().as_ptr()); - TRY(create_chain(pipe, transfer.pipe().direction() == Pipe::Direction::In ? PacketID::IN : PacketID::OUT, buffer_address, pipe.max_packet_size(), transfer.transfer_data_size(), &data_descriptor_chain, &last_data_descriptor)); - - last_data_descriptor->terminate(); - - if constexpr (UHCI_VERBOSE_DEBUG) { - if (data_descriptor_chain) { - dbgln("Data TD"); - data_descriptor_chain->print(); - } - } - - QueueHead* transfer_queue = allocate_queue_head(); - if (!transfer_queue) { - free_descriptor_chain(data_descriptor_chain); - return ENOMEM; - } - - transfer_queue->attach_transfer_descriptor_chain(data_descriptor_chain); - transfer_queue->set_transfer(&transfer); - - return transfer_queue; -} - -ErrorOr UHCIController::submit_async_transfer(NonnullOwnPtr async_handle, QueueHead* anchor, QueueHead* transfer_queue) -{ - { - SpinlockLocker locker { m_async_lock }; - auto iter = find_if(m_active_async_transfers.begin(), m_active_async_transfers.end(), [](auto& handle) { return handle == nullptr; }); - if (iter == m_active_async_transfers.end()) - return ENOMEM; - *iter = move(async_handle); - } - - enqueue_qh(transfer_queue, anchor); - - return {}; -} - -void UHCIController::cancel_async_transfer(NonnullLockRefPtr transfer) -{ - SpinlockLocker locker { m_async_lock }; - - auto iter = find_if(m_active_async_transfers.begin(), m_active_async_transfers.end(), [transfer](auto& handle) { return handle != nullptr && handle->transfer.ptr() == transfer.ptr(); }); - if (iter == m_active_async_transfers.end()) { - dbgln("Error: couldn't cancel supplied async transfer"); - return; // We can't really do anything here, so just give up - } - - auto& transfer_queue = (*iter)->qh; - dequeue_qh(transfer_queue); - free_descriptor_chain(transfer_queue->get_first_td()); - transfer_queue->free(); - m_queue_head_pool->release_to_pool(transfer_queue); - *iter = nullptr; -} - -ErrorOr UHCIController::submit_control_transfer(Transfer& transfer) -{ - Pipe& pipe = transfer.pipe(); // Short circuit the pipe related to this transfer - bool direction_in = (transfer.request().request_type & USB_REQUEST_TRANSFER_DIRECTION_DEVICE_TO_HOST) == USB_REQUEST_TRANSFER_DIRECTION_DEVICE_TO_HOST; - - dbgln_if(UHCI_DEBUG, "UHCI: Received control transfer for address {}. Root Hub is at address {}.", pipe.device_address(), m_root_hub->device_address()); - - // Short-circuit the root hub. - if (pipe.device_address() == m_root_hub->device_address()) - return m_root_hub->handle_control_transfer(transfer); - - TransferDescriptor* setup_td = create_transfer_descriptor(pipe, PacketID::SETUP, sizeof(USBRequestData)); - if (!setup_td) - return ENOMEM; - - setup_td->set_buffer_address(transfer.buffer_physical().as_ptr()); - - // Create a new descriptor chain - TransferDescriptor* last_data_descriptor; - TransferDescriptor* data_descriptor_chain; - auto buffer_address = Ptr32(transfer.buffer_physical().as_ptr() + sizeof(USBRequestData)); - TRY(create_chain(pipe, direction_in ? PacketID::IN : PacketID::OUT, buffer_address, pipe.max_packet_size(), transfer.transfer_data_size(), &data_descriptor_chain, &last_data_descriptor)); - - // Status TD always has toggle set to 1 - pipe.set_toggle(true); - - TransferDescriptor* status_td = create_transfer_descriptor(pipe, direction_in ? PacketID::OUT : PacketID::IN, 0); - if (!status_td) { - free_descriptor_chain(data_descriptor_chain); - return ENOMEM; - } - status_td->terminate(); - - // Link transfers together - if (data_descriptor_chain) { - setup_td->insert_next_transfer_descriptor(data_descriptor_chain); - last_data_descriptor->insert_next_transfer_descriptor(status_td); - } else { - setup_td->insert_next_transfer_descriptor(status_td); - } - - // Cool, everything should be chained together now! Let's print it out - if constexpr (UHCI_VERBOSE_DEBUG) { - dbgln("Setup TD"); - setup_td->print(); - if (data_descriptor_chain) { - dbgln("Data TD"); - data_descriptor_chain->print(); - } - dbgln("Status TD"); - status_td->print(); - } - - QueueHead* transfer_queue = allocate_queue_head(); - if (!transfer_queue) { - free_descriptor_chain(data_descriptor_chain); - return ENOMEM; - } - - transfer_queue->attach_transfer_descriptor_chain(setup_td); - transfer_queue->set_transfer(&transfer); - - enqueue_qh(transfer_queue, m_fs_control_qh_anchor); - - size_t transfer_size = 0; - while (!transfer.complete()) { - dbgln_if(USB_DEBUG, "Control transfer size: {}", transfer_size); - transfer_size = poll_transfer_queue(*transfer_queue); - } - - dequeue_qh(transfer_queue); - free_descriptor_chain(transfer_queue->get_first_td()); - transfer_queue->free(); - m_queue_head_pool->release_to_pool(transfer_queue); - - return transfer_size; -} - -ErrorOr UHCIController::submit_bulk_transfer(Transfer& transfer) -{ - auto transfer_queue = TRY(create_transfer_queue(transfer)); - enqueue_qh(transfer_queue, m_bulk_qh_anchor); - - dbgln_if(UHCI_DEBUG, "UHCI: Received bulk transfer for address {}. Root Hub is at address {}.", transfer.pipe().device_address(), m_root_hub->device_address()); - - size_t transfer_size = 0; - while (!transfer.complete()) { - transfer_size = poll_transfer_queue(*transfer_queue); - dbgln_if(USB_DEBUG, "Bulk transfer size: {}", transfer_size); - } - - dequeue_qh(transfer_queue); - free_descriptor_chain(transfer_queue->get_first_td()); - transfer_queue->free(); - m_queue_head_pool->release_to_pool(transfer_queue); - - return transfer_size; -} - -ErrorOr UHCIController::submit_async_interrupt_transfer(NonnullLockRefPtr transfer, u16 ms_interval) -{ - dbgln_if(UHCI_DEBUG, "UHCI: Received interrupt transfer for address {}. Root Hub is at address {}.", transfer->pipe().device_address(), m_root_hub->device_address()); - - if (ms_interval == 0) { - return EINVAL; - } - - auto transfer_queue = TRY(create_transfer_queue(*transfer)); - auto async_transfer_handle = TRY(adopt_nonnull_own_or_enomem(new (nothrow) AsyncTransferHandle { transfer, transfer_queue, ms_interval })); - TRY(submit_async_transfer(move(async_transfer_handle), m_interrupt_qh_anchor, transfer_queue)); - - return {}; -} - -size_t UHCIController::poll_transfer_queue(QueueHead& transfer_queue) -{ - Transfer* transfer = transfer_queue.transfer(); - TransferDescriptor* descriptor = transfer_queue.get_first_td(); - bool transfer_still_in_progress = false; - size_t transfer_size = 0; - - while (descriptor) { - u32 status = descriptor->status(); - - if (status & TransferDescriptor::StatusBits::NAKReceived) { - transfer_still_in_progress = false; - break; - } - - if (status & TransferDescriptor::StatusBits::Active) { - transfer_still_in_progress = true; - break; - } - - if (status & TransferDescriptor::StatusBits::ErrorMask) { - transfer->set_complete(); - transfer->set_error_occurred(); - dbgln_if(UHCI_DEBUG, "UHCIController: Transfer failed! Reason: {:08x}", status); - return 0; - } - - transfer_size += descriptor->actual_packet_length(); - descriptor = descriptor->next_td(); - } - - if (!transfer_still_in_progress) - transfer->set_complete(); - - return transfer_size; -} - -ErrorOr UHCIController::spawn_port_process() -{ - TRY(Process::create_kernel_process("UHCI Hot Plug Task"sv, [&] { - while (!Process::current().is_dying()) { - if (m_root_hub) - m_root_hub->check_for_port_updates(); - - (void)Thread::current()->sleep(Duration::from_seconds(1)); - } - Process::current().sys$exit(0); - VERIFY_NOT_REACHED(); - })); - return {}; -} - -ErrorOr UHCIController::spawn_async_poll_process() -{ - TRY(Process::create_kernel_process("UHCI Async Poll Task"sv, [&] { - u16 poll_interval_ms = 1024; - while (!Process::current().is_dying()) { - { - SpinlockLocker locker { m_async_lock }; - for (OwnPtr& handle : m_active_async_transfers) { - if (handle != nullptr) { - poll_interval_ms = min(poll_interval_ms, handle->ms_poll_interval); - QueueHead* qh = handle->qh; - for (auto td = qh->get_first_td(); td != nullptr && !td->active(); td = td->next_td()) { - if (td->next_td() == nullptr) { // Finished QH - handle->transfer->invoke_async_callback(); - qh->reinitialize(); // Set the QH to be active again - } - } - } - } - } - (void)Thread::current()->sleep(Duration::from_milliseconds(poll_interval_ms)); - } - Process::current().sys$exit(0); - VERIFY_NOT_REACHED(); - })); - return {}; -} - -bool UHCIController::handle_irq(RegisterState const&) -{ - u32 status = read_usbsts(); - - // Shared IRQ. Not ours! - if (!status) - return false; - - if constexpr (UHCI_DEBUG) { - dbgln("UHCI: Interrupt happened!"); - dbgln("Value of USBSTS: {:#04x}", read_usbsts()); - } - - // Write back USBSTS to clear bits - write_usbsts(status); - return true; -} - -void UHCIController::get_port_status(Badge, u8 port, HubStatus& hub_port_status) -{ - // The check is done by UHCIRootHub. - VERIFY(port < NUMBER_OF_ROOT_PORTS); - - u16 status = port == 0 ? read_portsc1() : read_portsc2(); - - if (status & UHCI_PORTSC_CURRENT_CONNECT_STATUS) - hub_port_status.status |= PORT_STATUS_CURRENT_CONNECT_STATUS; - - if (status & UHCI_PORTSC_CONNECT_STATUS_CHANGED) - hub_port_status.change |= PORT_STATUS_CONNECT_STATUS_CHANGED; - - if (status & UHCI_PORTSC_PORT_ENABLED) - hub_port_status.status |= PORT_STATUS_PORT_ENABLED; - - if (status & UHCI_PORTSC_PORT_ENABLE_CHANGED) - hub_port_status.change |= PORT_STATUS_PORT_ENABLED_CHANGED; - - if (status & UHCI_PORTSC_LOW_SPEED_DEVICE) - hub_port_status.status |= PORT_STATUS_LOW_SPEED_DEVICE_ATTACHED; - - if (status & UHCI_PORTSC_PORT_RESET) - hub_port_status.status |= PORT_STATUS_RESET; - - if (m_port_reset_change_statuses & (1 << port)) - hub_port_status.change |= PORT_STATUS_RESET_CHANGED; - - if (status & UHCI_PORTSC_SUSPEND) - hub_port_status.status |= PORT_STATUS_SUSPEND; - - if (m_port_suspend_change_statuses & (1 << port)) - hub_port_status.change |= PORT_STATUS_SUSPEND_CHANGED; - - // UHCI ports are always powered. - hub_port_status.status |= PORT_STATUS_PORT_POWER; - - dbgln_if(UHCI_DEBUG, "UHCI: get_port_status status={:#04x} change={:#04x}", hub_port_status.status, hub_port_status.change); -} - -void UHCIController::reset_port(u8 port) -{ - // We still have to reset the port manually because UHCI does not automatically enable the port after reset. - // Additionally, the USB 2.0 specification says the SetPortFeature(PORT_ENABLE) request is not specified and that the _ideal_ behavior is to return a Request Error. - // Source: USB 2.0 Specification Section 11.24.2.7.1.2 - // This means the hub code cannot rely on using it. - - // The check is done by UHCIRootHub and set_port_feature. - VERIFY(port < NUMBER_OF_ROOT_PORTS); - - u16 port_data = port == 0 ? read_portsc1() : read_portsc2(); - port_data &= UHCI_PORTSC_NON_WRITE_CLEAR_BIT_MASK; - port_data |= UHCI_PORTSC_PORT_RESET; - if (port == 0) - write_portsc1(port_data); - else - write_portsc2(port_data); - - // Wait at least 50 ms for the port to reset. - // This is T DRSTR in the USB 2.0 Specification Page 186 Table 7-13. - constexpr u16 reset_delay = 50 * 1000; - microseconds_delay(reset_delay); - - port_data &= ~UHCI_PORTSC_PORT_RESET; - if (port == 0) - write_portsc1(port_data); - else - write_portsc2(port_data); - - // Wait 10 ms for the port to recover. - // This is T RSTRCY in the USB 2.0 Specification Page 188 Table 7-14. - constexpr u16 reset_recovery_delay = 10 * 1000; - microseconds_delay(reset_recovery_delay); - - port_data = port == 0 ? read_portsc1() : read_portsc2(); - port_data |= UHCI_PORTSC_PORT_ENABLED; - if (port == 0) - write_portsc1(port_data); - else - write_portsc2(port_data); - - dbgln_if(UHCI_DEBUG, "UHCI: Port should be enabled now: {:#04x}", port == 0 ? read_portsc1() : read_portsc2()); - m_port_reset_change_statuses |= (1 << port); -} - -ErrorOr UHCIController::set_port_feature(Badge, u8 port, HubFeatureSelector feature_selector) -{ - // The check is done by UHCIRootHub. - VERIFY(port < NUMBER_OF_ROOT_PORTS); - - dbgln_if(UHCI_DEBUG, "UHCI: set_port_feature: port={} feature_selector={}", port, (u8)feature_selector); - - switch (feature_selector) { - case HubFeatureSelector::PORT_POWER: - // Ignore the request. UHCI ports are always powered. - break; - case HubFeatureSelector::PORT_RESET: - reset_port(port); - break; - case HubFeatureSelector::PORT_SUSPEND: { - u16 port_data = port == 0 ? read_portsc1() : read_portsc2(); - port_data &= UHCI_PORTSC_NON_WRITE_CLEAR_BIT_MASK; - port_data |= UHCI_PORTSC_SUSPEND; - - if (port == 0) - write_portsc1(port_data); - else - write_portsc2(port_data); - - m_port_suspend_change_statuses |= (1 << port); - break; - } - default: - dbgln("UHCI: Unknown feature selector in set_port_feature: {}", (u8)feature_selector); - return EINVAL; - } - - return {}; -} - -ErrorOr UHCIController::clear_port_feature(Badge, u8 port, HubFeatureSelector feature_selector) -{ - // The check is done by UHCIRootHub. - VERIFY(port < NUMBER_OF_ROOT_PORTS); - - dbgln_if(UHCI_DEBUG, "UHCI: clear_port_feature: port={} feature_selector={}", port, (u8)feature_selector); - - u16 port_data = port == 0 ? read_portsc1() : read_portsc2(); - port_data &= UHCI_PORTSC_NON_WRITE_CLEAR_BIT_MASK; - - switch (feature_selector) { - case HubFeatureSelector::PORT_ENABLE: - port_data &= ~UHCI_PORTSC_PORT_ENABLED; - break; - case HubFeatureSelector::PORT_SUSPEND: - port_data &= ~UHCI_PORTSC_SUSPEND; - break; - case HubFeatureSelector::PORT_POWER: - // Ignore the request. UHCI ports are always powered. - break; - case HubFeatureSelector::C_PORT_CONNECTION: - // This field is Write Clear. - port_data |= UHCI_PORTSC_CONNECT_STATUS_CHANGED; - break; - case HubFeatureSelector::C_PORT_RESET: - m_port_reset_change_statuses &= ~(1 << port); - break; - case HubFeatureSelector::C_PORT_ENABLE: - // This field is Write Clear. - port_data |= UHCI_PORTSC_PORT_ENABLE_CHANGED; - break; - case HubFeatureSelector::C_PORT_SUSPEND: - m_port_suspend_change_statuses &= ~(1 << port); - break; - default: - dbgln("UHCI: Unknown feature selector in clear_port_feature: {}", (u8)feature_selector); - return EINVAL; - } - - dbgln_if(UHCI_DEBUG, "UHCI: clear_port_feature: writing {:#04x} to portsc{}.", port_data, port + 1); - - if (port == 0) - write_portsc1(port_data); - else - write_portsc2(port_data); - - return {}; -} - -} diff --git a/Kernel/Bus/USB/UHCI/UHCIController.h b/Kernel/Bus/USB/UHCI/UHCIController.h deleted file mode 100644 index 543de8e2e36..00000000000 --- a/Kernel/Bus/USB/UHCI/UHCIController.h +++ /dev/null @@ -1,130 +0,0 @@ -/* - * Copyright (c) 2020, Andreas Kling - * Copyright (c) 2020-2021, Jesse Buhagiar - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace Kernel::USB { - -class UHCIController final - : public USBController - , public PCI::Device - , public IRQHandler { - - static constexpr u8 MAXIMUM_NUMBER_OF_TDS = 128; // Upper pool limit. This consumes the second page we have allocated - static constexpr u8 MAXIMUM_NUMBER_OF_QHS = 64; - -public: - static constexpr u8 NUMBER_OF_ROOT_PORTS = 2; - static ErrorOr> try_to_initialize(PCI::DeviceIdentifier const& pci_device_identifier); - virtual ~UHCIController() override; - - virtual StringView purpose() const override { return "UHCI"sv; } - virtual StringView device_name() const override { return purpose(); } - - virtual ErrorOr initialize() override; - virtual ErrorOr reset() override; - virtual ErrorOr stop() override; - virtual ErrorOr start() override; - ErrorOr spawn_async_poll_process(); - ErrorOr spawn_port_process(); - - ErrorOr create_transfer_queue(Transfer& transfer); - ErrorOr submit_async_transfer(NonnullOwnPtr async_handle, QueueHead* anchor, QueueHead* transfer_queue); - - virtual void cancel_async_transfer(NonnullLockRefPtr transfer) override; - virtual ErrorOr submit_control_transfer(Transfer& transfer) override; - virtual ErrorOr submit_bulk_transfer(Transfer& transfer) override; - virtual ErrorOr submit_async_interrupt_transfer(NonnullLockRefPtr transfer, u16 ms_interval) override; - - void get_port_status(Badge, u8, HubStatus&); - ErrorOr set_port_feature(Badge, u8, HubFeatureSelector); - ErrorOr clear_port_feature(Badge, u8, HubFeatureSelector); - -private: - UHCIController(PCI::DeviceIdentifier const& pci_device_identifier, NonnullOwnPtr registers_io_window); - - u16 read_usbcmd() { return m_registers_io_window->read16(0); } - u16 read_usbsts() { return m_registers_io_window->read16(0x2); } - u16 read_usbintr() { return m_registers_io_window->read16(0x4); } - u16 read_frnum() { return m_registers_io_window->read16(0x6); } - u32 read_flbaseadd() { return m_registers_io_window->read32(0x8); } - u8 read_sofmod() { return m_registers_io_window->read8(0xc); } - u16 read_portsc1() { return m_registers_io_window->read16(0x10); } - u16 read_portsc2() { return m_registers_io_window->read16(0x12); } - - void write_usbcmd(u16 value) { m_registers_io_window->write16(0, value); } - void write_usbsts(u16 value) { m_registers_io_window->write16(0x2, value); } - void write_usbintr(u16 value) { m_registers_io_window->write16(0x4, value); } - void write_frnum(u16 value) { m_registers_io_window->write16(0x6, value); } - void write_flbaseadd(u32 value) { m_registers_io_window->write32(0x8, value); } - void write_sofmod(u8 value) { m_registers_io_window->write8(0xc, value); } - void write_portsc1(u16 value) { m_registers_io_window->write16(0x10, value); } - void write_portsc2(u16 value) { m_registers_io_window->write16(0x12, value); } - - virtual bool handle_irq(RegisterState const&) override; - - ErrorOr create_structures(); - void setup_schedule(); - - void enqueue_qh(QueueHead* transfer_queue, QueueHead* anchor); - void dequeue_qh(QueueHead* transfer_queue); - - size_t poll_transfer_queue(QueueHead& transfer_queue); - - TransferDescriptor* create_transfer_descriptor(Pipe& pipe, PacketID direction, size_t data_len); - ErrorOr create_chain(Pipe& pipe, PacketID direction, Ptr32& buffer_address, size_t max_size, size_t transfer_size, TransferDescriptor** td_chain, TransferDescriptor** last_td); - void free_descriptor_chain(TransferDescriptor* first_descriptor); - - QueueHead* allocate_queue_head(); - TransferDescriptor* allocate_transfer_descriptor(); - - void reset_port(u8); - - NonnullOwnPtr m_registers_io_window; - - Spinlock m_async_lock {}; - Spinlock m_schedule_lock {}; - - OwnPtr m_root_hub; - OwnPtr> m_queue_head_pool; - OwnPtr> m_transfer_descriptor_pool; - Vector m_iso_td_list; - Array, MAXIMUM_NUMBER_OF_QHS> m_active_async_transfers; - - QueueHead* m_schedule_begin_anchor; - QueueHead* m_interrupt_qh_anchor; - QueueHead* m_ls_control_qh_anchor; - QueueHead* m_fs_control_qh_anchor; - // Always final queue in the schedule, may loop back to previous QH for bandwidth - // reclamation instead of actually terminating - QueueHead* m_bulk_qh_anchor; - - OwnPtr m_framelist; - OwnPtr m_isochronous_transfer_pool; - - // Bitfield containing whether a given port should signal a change in reset or not. - u8 m_port_reset_change_statuses { 0 }; - - // Bitfield containing whether a given port should signal a change in suspend or not. - u8 m_port_suspend_change_statuses { 0 }; -}; -} diff --git a/Kernel/Bus/USB/UHCI/UHCIDescriptorPool.h b/Kernel/Bus/USB/UHCI/UHCIDescriptorPool.h deleted file mode 100644 index 4c980d9b055..00000000000 --- a/Kernel/Bus/USB/UHCI/UHCIDescriptorPool.h +++ /dev/null @@ -1,89 +0,0 @@ -/* - * Copyright (c) 2021, Jesse Buhagiar - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include -#include -#include - -namespace Kernel::USB { - -// This pool is bound by PAGE_SIZE / sizeof(T). The underlying allocation for the pointers -// is AK::Stack. As such, we never dynamically allocate any memory past the amount -// that can fit in a single page. -template -class UHCIDescriptorPool { - AK_MAKE_NONCOPYABLE(UHCIDescriptorPool); - AK_MAKE_NONMOVABLE(UHCIDescriptorPool); - - // Ensure that we can't get into a situation where we'll write past the page - // and blow up - static_assert(sizeof(T) <= PAGE_SIZE); - -public: - static ErrorOr>> try_create(StringView name) - { - auto pool_memory_block = TRY(MM.allocate_kernel_region(PAGE_SIZE, "UHCI Descriptor Pool"sv, Memory::Region::Access::ReadWrite)); - return adopt_nonnull_own_or_enomem(new (nothrow) UHCIDescriptorPool(move(pool_memory_block), name)); - } - - ~UHCIDescriptorPool() = default; - - [[nodiscard]] T* try_take_free_descriptor() - { - SpinlockLocker locker(m_pool_lock); - - // We're out of descriptors! - if (m_free_descriptor_stack.is_empty()) - return nullptr; - - dbgln_if(UHCI_VERBOSE_DEBUG, "Got a free UHCI Descriptor @ {} from pool {}", m_free_descriptor_stack.top(), m_pool_name); - T* descriptor = m_free_descriptor_stack.top(); - m_free_descriptor_stack.pop(); - - return descriptor; - } - - void release_to_pool(T* ptr) - { - SpinlockLocker locker(m_pool_lock); - - dbgln_if(UHCI_VERBOSE_DEBUG, "Returning descriptor @ {} to pool {}", ptr, m_pool_name); - if (!m_free_descriptor_stack.push(ptr)) - dbgln("Failed to return descriptor to pool {}. Stack overflow!", m_pool_name); - } - - void print_pool_information() const - { - dbgln("Pool {} allocated @ {}", m_pool_name, m_pool_region->physical_page(0)->paddr()); - } - -private: - UHCIDescriptorPool(NonnullOwnPtr pool_memory_block, StringView name) - : m_pool_name(name) - , m_pool_region(move(pool_memory_block)) - { - // Go through the number of descriptors to create in the pool, and create a virtual/physical address mapping - for (size_t i = 0; i < PAGE_SIZE / sizeof(T); i++) { - auto* placement_address = reinterpret_cast(m_pool_region->vaddr().get() + (i * sizeof(T))); - auto physical_address = static_cast(m_pool_region->physical_page(0)->paddr().get() + (i * sizeof(T))); - auto* object = new (placement_address) T(physical_address); - m_free_descriptor_stack.push(object); // Push the descriptor's pointer onto the free list - } - } - - StringView m_pool_name; // Name of this pool - NonnullOwnPtr m_pool_region; // Memory region where descriptors actually reside - Stack m_free_descriptor_stack; // Stack of currently free descriptor pointers - Spinlock m_pool_lock; -}; - -} diff --git a/Kernel/Bus/USB/UHCI/UHCIDescriptorTypes.h b/Kernel/Bus/USB/UHCI/UHCIDescriptorTypes.h deleted file mode 100644 index 4bf18b9c02b..00000000000 --- a/Kernel/Bus/USB/UHCI/UHCIDescriptorTypes.h +++ /dev/null @@ -1,385 +0,0 @@ -/* - * Copyright (c) 2021, Jesse Buhagiar - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include - -namespace Kernel::USB { - -enum class PacketID : u8 { - IN = 0x69, - OUT = 0xe1, - SETUP = 0x2d -}; - -// Transfer Descriptor register bit offsets/masks -constexpr u16 TD_CONTROL_STATUS_ACTLEN = 0x7ff; -constexpr u8 TD_CONTROL_STATUS_ACTIVE_SHIFT = 23; -constexpr u8 TD_CONTROL_STATUS_INT_ON_COMPLETE_SHIFT = 24; -constexpr u8 TD_CONTROL_STATUS_ISOCHRONOUS_SHIFT = 25; -constexpr u8 TD_CONTROL_STATUS_LS_DEVICE_SHIFT = 26; -constexpr u8 TD_CONTROL_STATUS_ERR_CTR_SHIFT_SHIFT = 27; -constexpr u8 TD_CONTROL_STATUS_SPD_SHIFT = 29; - -constexpr u8 TD_TOKEN_PACKET_ID_SHIFT = 0; -constexpr u8 TD_TOKEN_DEVICE_ADDR_SHIFT = 8; -constexpr u8 TD_TOKEN_ENDPOINT_SHIFT = 15; -constexpr u8 TD_TOKEN_DATA_TOGGLE_SHIFT = 19; -constexpr u8 TD_TOKEN_MAXLEN_SHIFT = 21; - -// -// Transfer Descriptor -// -// Describes a single transfer event from, or to the Universal Serial Bus. -// These are, generally, attached to Queue Heads, and then executed by the -// USB Host Controller. -// Must be 16-byte aligned -// -struct QueueHead; -struct alignas(16) TransferDescriptor final { - enum LinkPointerBits { - Terminate = 1, - QHSelect = 2, - DepthFlag = 4, - }; - - enum StatusBits { - Reserved = (1 << 16), - BitStuffError = (1 << 17), - CRCTimeoutError = (1 << 18), - NAKReceived = (1 << 19), - BabbleDetected = (1 << 20), - DataBufferError = (1 << 21), - Stalled = (1 << 22), - Active = (1 << 23), - ErrorMask = BitStuffError | CRCTimeoutError | NAKReceived | BabbleDetected | DataBufferError | Stalled - }; - - enum ControlBits { - InterruptOnComplete = (1 << 24), - IsochronousSelect = (1 << 25), - LowSpeedDevice = (1 << 26), - ShortPacketDetect = (1 << 29), - }; - - struct TransferDescriptorBookkeeping { - u32 paddr; // Physical 4-byte address where this TransferDescriptor is located - TransferDescriptor* next_td { nullptr }; // Pointer to first TD - TransferDescriptor* prev_td { nullptr }; // Pointer to the previous TD - bool in_use; // Has this TD been allocated (and therefore in use)? - }; - - TransferDescriptor() = delete; - TransferDescriptor(u32 paddr) - { - m_bookkeeping = new TransferDescriptorBookkeeping; - m_bookkeeping->paddr = paddr; - } - ~TransferDescriptor() = delete; // Prevent anything except placement new on this object - - u32 link_ptr() const { return m_link_ptr; } - u32 paddr() const { return m_bookkeeping->paddr; } - u32 status() const { return m_control_status; } - u32 token() const { return m_token; } - u32 buffer_ptr() const { return m_buffer_ptr; } - u16 actual_packet_length() const { return (m_control_status + 1) & 0x7ff; } - - bool in_use() const { return m_bookkeeping->in_use; } - bool stalled() const { return m_control_status & StatusBits::Stalled; } - bool last_in_chain() const { return m_link_ptr & LinkPointerBits::Terminate; } - bool active() const { return m_control_status & StatusBits::Active; } - - void set_active() - { - u32 ctrl = m_control_status; - ctrl |= StatusBits::Active; - m_control_status = ctrl; - } - - void set_isochronous() - { - u32 ctrl = m_control_status; - ctrl |= ControlBits::IsochronousSelect; - m_control_status = ctrl; - } - - void set_interrupt_on_complete() - { - u32 ctrl = m_control_status; - ctrl |= ControlBits::InterruptOnComplete; - m_control_status = ctrl; - } - - void set_lowspeed() - { - u32 ctrl = m_control_status; - ctrl |= ControlBits::LowSpeedDevice; - m_control_status = ctrl; - } - - void set_error_retry_counter(u8 num_retries) - { - VERIFY(num_retries <= 3); - u32 ctrl = m_control_status; - ctrl |= (num_retries << 27); - m_control_status = ctrl; - } - - void set_short_packet_detect() - { - u32 ctrl = m_control_status; - ctrl |= ControlBits::ShortPacketDetect; - m_control_status = ctrl; - } - - void set_control_status(u32 control_status) { m_control_status = control_status; } - void set_in_use(bool in_use) { m_bookkeeping->in_use = in_use; } - void set_max_len(u16 max_len) - { - VERIFY(max_len < 0x500 || max_len == 0x7ff); - m_token |= (max_len << 21); - } - - void set_device_endpoint(u8 endpoint) - { - VERIFY(endpoint <= 0xf); - m_token |= (endpoint << 18); - } - - void set_device_address(u8 address) - { - VERIFY(address <= 0x7f); - m_token |= (address << 8); - } - - void set_data_toggle(bool toggle) - { - m_token |= ((toggle ? (1 << 19) : 0)); - } - - void set_packet_id(PacketID pid) { m_token |= static_cast(pid); } - void link_queue_head(u32 qh_paddr) - { - m_link_ptr = qh_paddr; - m_link_ptr |= LinkPointerBits::QHSelect; - } - - void print() - { - dbgln("UHCI: TD({:#04x}) @ {:#04x}: link_ptr={:#04x}, status={:#04x}, token={:#04x}, buffer_ptr={:#04x}", this, m_bookkeeping->paddr, m_link_ptr, (u32)m_control_status, m_token, m_buffer_ptr); - - // Now let's print the flags! - dbgln("UHCI: TD({:#04x}) @ {:#04x}: link_ptr={}{}{}, status={}{}{}{}{}{}{}", - this, - m_bookkeeping->paddr, - (last_in_chain()) ? "T " : "", - (m_link_ptr & static_cast(LinkPointerBits::QHSelect)) ? "QH " : "", - (m_link_ptr & static_cast(LinkPointerBits::DepthFlag)) ? "Vf " : "", - (m_control_status & static_cast(StatusBits::BitStuffError)) ? "BITSTUFF " : "", - (m_control_status & static_cast(StatusBits::CRCTimeoutError)) ? "CRCTIMEOUT " : "", - (m_control_status & static_cast(StatusBits::NAKReceived)) ? "NAK " : "", - (m_control_status & static_cast(StatusBits::BabbleDetected)) ? "BABBLE " : "", - (m_control_status & static_cast(StatusBits::DataBufferError)) ? "DATAERR " : "", - (stalled()) ? "STALL " : "", - (active()) ? "ACTIVE " : ""); - } - - // FIXME: For the love of God, use AK SMART POINTERS PLEASE!! - TransferDescriptor* next_td() { return m_bookkeeping->next_td; } - TransferDescriptor const* next_td() const { return m_bookkeeping->next_td; } - void set_next_td(TransferDescriptor* td) { m_bookkeeping->next_td = td; } - - TransferDescriptor* prev_td() { return m_bookkeeping->prev_td; } - TransferDescriptor const* prev_td() const { return m_bookkeeping->prev_td; } - void set_previous_td(TransferDescriptor* td) { m_bookkeeping->prev_td = td; } - - void insert_next_transfer_descriptor(TransferDescriptor* td) - { - m_link_ptr = td->paddr(); - td->set_previous_td(this); - set_next_td(td); - - // Let's set some bits for the link ptr - m_link_ptr |= static_cast(LinkPointerBits::DepthFlag); - } - - void terminate() { m_link_ptr |= static_cast(LinkPointerBits::Terminate); } - - void set_buffer_address(Ptr32 buffer) - { - u8* buffer_address = &*buffer; - m_buffer_ptr = reinterpret_cast(buffer_address); - } - - // DEBUG FUNCTIONS! - void set_token(u32 token) - { - m_token = token; - } - - void set_status(u32 status) - { - m_control_status = status; - } - - void free() - { - m_link_ptr = 0; - m_control_status = 0; - m_token = 0; - m_bookkeeping->in_use = false; - m_bookkeeping->next_td = nullptr; - m_bookkeeping->prev_td = nullptr; - } - -private: - u32 m_link_ptr; // Points to another Queue Head or Transfer Descriptor - u32 volatile m_control_status; // Control and status field - u32 m_token; // Contains all information required to fill in a USB Start Token - u32 m_buffer_ptr; // Points to a data buffer for this transaction (i.e what we want to send or recv) - - // This structure pointer will be ignored by the controller, but we can use it for configuration and bookkeeping - TransferDescriptorBookkeeping* m_bookkeeping { nullptr }; -}; - -static_assert(AssertSize()); // Transfer Descriptor is always 8 Dwords - -// -// Queue Head -// -// Description here please! -// -struct alignas(16) QueueHead { - - struct QueueHeadBookkeeping { - u32 paddr { 0 }; // Physical 4-byte address where this QueueHead is located - QueueHead* next_qh { nullptr }; // Next QH - QueueHead* prev_qh { nullptr }; // Previous QH - TransferDescriptor* first_td { nullptr }; // Pointer to first TD - Transfer* transfer { nullptr }; // Pointer to transfer linked to this queue head - bool in_use { false }; // Is this QH currently in use? - }; - - // The number of padding bytes is the size of a full QueueHead descriptor (32-bytes), minus the two required members (8-bytes), minus the size - // of a pointer to the bookkeeping structure. - static constexpr size_t DESCRIPTOR_PAD_BYTES = 32u - 8 - sizeof(QueueHeadBookkeeping*); - - enum class LinkPointerBits : u32 { - Terminate = 1, - QHSelect = 2, - }; - - QueueHead() = delete; - QueueHead(u32 paddr) - { - m_bookkeeping = new QueueHeadBookkeeping; - m_bookkeeping->paddr = paddr; - } - ~QueueHead() = delete; // Prevent anything except placement new on this object - - u32 link_ptr() const { return m_link_ptr; } - u32 element_link_ptr() const { return m_element_link_ptr; } - - u32 paddr() const { return m_bookkeeping->paddr; } - bool in_use() const { return m_bookkeeping->in_use; } - void set_in_use(bool in_use) { m_bookkeeping->in_use = in_use; } - - QueueHead* next_qh() { return m_bookkeeping->next_qh; } - QueueHead const* next_qh() const { return m_bookkeeping->next_qh; } - - QueueHead* prev_qh() { return m_bookkeeping->prev_qh; } - QueueHead const* prev_qh() const { return m_bookkeeping->prev_qh; } - - void link_next_queue_head(QueueHead* qh) - { - m_link_ptr = qh->paddr(); - m_link_ptr |= static_cast(LinkPointerBits::QHSelect); - m_bookkeeping->next_qh = qh; - qh->m_bookkeeping->prev_qh = this; - } - - void attach_transfer_queue(QueueHead& qh) - { - m_element_link_ptr = qh.paddr(); - m_element_link_ptr = m_element_link_ptr | static_cast(LinkPointerBits::QHSelect); - } - - // TODO: Should we pass in an array or vector of TDs instead???? - void attach_transfer_descriptor_chain(TransferDescriptor* td) - { - m_bookkeeping->first_td = td; - m_element_link_ptr = td->paddr(); - } - - TransferDescriptor* get_first_td() - { - return m_bookkeeping->first_td; - } - - void terminate() { m_link_ptr |= static_cast(LinkPointerBits::Terminate); } - - void terminate_element_link_ptr() - { - m_element_link_ptr = static_cast(LinkPointerBits::Terminate); - } - - void set_transfer(Transfer* transfer) - { - m_bookkeeping->transfer = transfer; - } - - Transfer* transfer() - { - return m_bookkeeping->transfer; - } - - void print() - { - dbgln("UHCI: QH({:#04x}) @ {:#04x}: link_ptr={:#04x}, element_link_ptr={:#04x}", this, m_bookkeeping->paddr, m_link_ptr, (FlatPtr)m_element_link_ptr); - } - - void free() - { - m_link_ptr = 0; - m_element_link_ptr = 0; - m_bookkeeping->first_td = nullptr; - m_bookkeeping->transfer = nullptr; - m_bookkeeping->next_qh = nullptr; - m_bookkeeping->prev_qh = nullptr; - m_bookkeeping->in_use = false; - } - - void reinitialize() - { - - for (TransferDescriptor* iter = get_first_td(); iter != nullptr; iter = iter->next_td()) { - iter->set_active(); - } - attach_transfer_descriptor_chain(get_first_td()); - } - -private: - u32 m_link_ptr { 0 }; // Pointer to the next horizontal object that the controller will execute after this one - u32 volatile m_element_link_ptr { 0 }; // Pointer to the first data object in the queue (can be modified by hw) - - // This structure pointer will be ignored by the controller, but we can use it for configuration and bookkeeping - QueueHeadBookkeeping* m_bookkeeping { nullptr }; - u8 m_padding[DESCRIPTOR_PAD_BYTES]; -}; - -static_assert(AssertSize()); // Queue Head is always 8 Dwords - -struct AsyncTransferHandle { - NonnullLockRefPtr transfer; - QueueHead* qh; - u16 ms_poll_interval; -}; - -} diff --git a/Kernel/Bus/USB/UHCI/UHCIRootHub.cpp b/Kernel/Bus/USB/UHCI/UHCIRootHub.cpp deleted file mode 100644 index 15865eb3af6..00000000000 --- a/Kernel/Bus/USB/UHCI/UHCIRootHub.cpp +++ /dev/null @@ -1,254 +0,0 @@ -/* - * Copyright (c) 2021, Luke Wilde - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include -#include -#include - -namespace Kernel::USB { - -static USBDeviceDescriptor uhci_root_hub_device_descriptor = { - { - sizeof(USBDeviceDescriptor), // 18 bytes long - DESCRIPTOR_TYPE_DEVICE, - }, - 0x0110, // USB 1.1 - (u8)USB_CLASS_HUB, - 0, // Hubs use subclass 0 - 0, // Full Speed Hub - 64, // Max packet size - 0x0, // Vendor ID - 0x0, // Product ID - 0x0110, // Product version (can be anything, currently matching usb_spec_compliance_bcd) - 0, // Index of manufacturer string. FIXME: There is currently no support for string descriptors. - 0, // Index of product string. FIXME: There is currently no support for string descriptors. - 0, // Index of serial string. FIXME: There is currently no support for string descriptors. - 1, // One configuration descriptor -}; - -static USBConfigurationDescriptor uhci_root_hub_configuration_descriptor = { - { - sizeof(USBConfigurationDescriptor), // 9 bytes long - DESCRIPTOR_TYPE_CONFIGURATION, - }, - sizeof(USBConfigurationDescriptor) + sizeof(USBInterfaceDescriptor) + sizeof(USBEndpointDescriptor), // Combined length of configuration, interface and endpoint and descriptors. - 1, // One interface descriptor - 1, // Configuration #1 - 0, // Index of configuration string. FIXME: There is currently no support for string descriptors. - (1 << 7) | (1 << 6), // Bit 6 is set to indicate that the root hub is self powered. Bit 7 must always be 1. - 0, // 0 mA required from the bus (self-powered) -}; - -static USBInterfaceDescriptor uhci_root_hub_interface_descriptor = { - { - sizeof(USBInterfaceDescriptor), // 9 bytes long - DESCRIPTOR_TYPE_INTERFACE, - }, - 0, // Interface #0 - 0, // Alternate setting - 1, // One endpoint - (u8)USB_CLASS_HUB, - 0, // Hubs use subclass 0 - 0, // Full Speed Hub - 0, // Index of interface string. FIXME: There is currently no support for string descriptors -}; - -static USBEndpointDescriptor uhci_root_hub_endpoint_descriptor = { - { - sizeof(USBEndpointDescriptor), // 7 bytes long - DESCRIPTOR_TYPE_ENDPOINT, - }, - USBEndpoint::ENDPOINT_ADDRESS_DIRECTION_IN | 1, // IN Endpoint #1 - USBEndpoint::ENDPOINT_ATTRIBUTES_TRANSFER_TYPE_INTERRUPT, // Interrupt endpoint - 2, // Max Packet Size FIXME: I'm not sure what this is supposed to be as it is implementation defined. 2 is the number of bytes Get Port Status returns. - 0xFF, // Max possible interval -}; - -// NOTE: UHCI does not provide us anything for the Root Hub's Hub Descriptor. -static USBHubDescriptor uhci_root_hub_hub_descriptor = { - { - sizeof(USBHubDescriptor), // 7 bytes long. FIXME: Add the size of the VLAs at the end once they're supported. - DESCRIPTOR_TYPE_HUB, - }, - UHCIController::NUMBER_OF_ROOT_PORTS, // 2 ports - 0x0, // Ganged power switching, not a compound device, global over-current protection. - 0x0, // UHCI ports are always powered, so there's no time from power on to power good. - 0x0, // Self-powered -}; - -ErrorOr> UHCIRootHub::try_create(NonnullLockRefPtr uhci_controller) -{ - return adopt_nonnull_own_or_enomem(new (nothrow) UHCIRootHub(move(uhci_controller))); -} - -UHCIRootHub::UHCIRootHub(NonnullLockRefPtr uhci_controller) - : m_uhci_controller(move(uhci_controller)) -{ -} - -ErrorOr UHCIRootHub::setup(Badge) -{ - m_hub = TRY(Hub::try_create_root_hub(m_uhci_controller, Device::DeviceSpeed::FullSpeed)); - - // NOTE: The root hub will be on the default address at this point. - // The root hub must be the first device to be created, otherwise the HCD will intercept all default address transfers as though they're targeted at the root hub. - TRY(m_hub->enumerate_device()); - - // NOTE: The root hub is no longer on the default address. - TRY(m_hub->enumerate_and_power_on_hub()); - - return {}; -} - -ErrorOr UHCIRootHub::handle_control_transfer(Transfer& transfer) -{ - auto const& request = transfer.request(); - auto* request_data = transfer.buffer().as_ptr() + sizeof(USBRequestData); - - if constexpr (UHCI_DEBUG) { - dbgln("UHCIRootHub: Received control transfer."); - dbgln("UHCIRootHub: Request Type: {:#02x}", request.request_type); - dbgln("UHCIRootHub: Request: {:#02x}", request.request); - dbgln("UHCIRootHub: Value: {:#04x}", request.value); - dbgln("UHCIRootHub: Index: {:#04x}", request.index); - dbgln("UHCIRootHub: Length: {:#04x}", request.length); - } - - size_t length = 0; - - switch (request.request) { - case HubRequest::GET_STATUS: { - if (request.index > UHCIController::NUMBER_OF_ROOT_PORTS) - return EINVAL; - - length = min(transfer.transfer_data_size(), sizeof(HubStatus)); - VERIFY(length <= sizeof(HubStatus)); - HubStatus hub_status {}; - - if (request.index == 0) { - // If index == 0, the actual request is Get Hub Status - // UHCI does not provide "Local Power Source" or "Over-current" and their corresponding change flags. - // The members of hub_status are initialized to 0, so we can memcpy it straight away. - memcpy(request_data, (void*)&hub_status, length); - break; - } - - // If index != 0, the actual request is Get Port Status - m_uhci_controller->get_port_status({}, request.index - 1, hub_status); - memcpy(request_data, (void*)&hub_status, length); - break; - } - case HubRequest::GET_DESCRIPTOR: { - u8 descriptor_type = request.value >> 8; - switch (descriptor_type) { - case DESCRIPTOR_TYPE_DEVICE: - length = min(transfer.transfer_data_size(), sizeof(USBDeviceDescriptor)); - VERIFY(length <= sizeof(USBDeviceDescriptor)); - memcpy(request_data, (void*)&uhci_root_hub_device_descriptor, length); - break; - case DESCRIPTOR_TYPE_CONFIGURATION: { - auto index = 0u; - - // Send over the whole descriptor chain - length = uhci_root_hub_configuration_descriptor.total_length; - VERIFY(length <= sizeof(USBConfigurationDescriptor) + sizeof(USBInterfaceDescriptor) + sizeof(USBEndpointDescriptor)); - memcpy(request_data, (void*)&uhci_root_hub_configuration_descriptor, sizeof(USBConfigurationDescriptor)); - index += sizeof(uhci_root_hub_configuration_descriptor); - memcpy(request_data + index, (void*)&uhci_root_hub_interface_descriptor, sizeof(USBInterfaceDescriptor)); - index += sizeof(uhci_root_hub_interface_descriptor); - memcpy(request_data + index, (void*)&uhci_root_hub_endpoint_descriptor, sizeof(USBEndpointDescriptor)); - break; - } - case DESCRIPTOR_TYPE_INTERFACE: - length = min(transfer.transfer_data_size(), sizeof(USBInterfaceDescriptor)); - VERIFY(length <= sizeof(USBInterfaceDescriptor)); - memcpy(request_data, (void*)&uhci_root_hub_interface_descriptor, length); - break; - case DESCRIPTOR_TYPE_ENDPOINT: - length = min(transfer.transfer_data_size(), sizeof(USBEndpointDescriptor)); - VERIFY(length <= sizeof(USBEndpointDescriptor)); - memcpy(request_data, (void*)&uhci_root_hub_endpoint_descriptor, length); - break; - case DESCRIPTOR_TYPE_HUB: - length = min(transfer.transfer_data_size(), sizeof(USBHubDescriptor)); - VERIFY(length <= sizeof(USBHubDescriptor)); - memcpy(request_data, (void*)&uhci_root_hub_hub_descriptor, length); - break; - default: - return EINVAL; - } - break; - } - case USB_REQUEST_SET_ADDRESS: - dbgln_if(UHCI_DEBUG, "UHCIRootHub: Attempt to set address to {}, ignoring.", request.value); - if (request.value > USB_MAX_ADDRESS) - return EINVAL; - // Ignore SET_ADDRESS requests. USBDevice sets its internal address to the new allocated address that it just sent to us. - // The internal address is used to check if the request is directed at the root hub or not. - break; - case HubRequest::SET_FEATURE: { - if (request.index == 0) { - // If index == 0, the actual request is Set Hub Feature. - // UHCI does not provide "Local Power Source" or "Over-current" and their corresponding change flags. - // Therefore, ignore the request, but return an error if the value is not "Local Power Source" or "Over-current" - switch (request.value) { - case HubFeatureSelector::C_HUB_LOCAL_POWER: - case HubFeatureSelector::C_HUB_OVER_CURRENT: - break; - default: - return EINVAL; - } - - break; - } - - // If index != 0, the actual request is Set Port Feature. - u8 port = request.index & 0xFF; - if (port > UHCIController::NUMBER_OF_ROOT_PORTS) - return EINVAL; - - auto feature_selector = (HubFeatureSelector)request.value; - TRY(m_uhci_controller->set_port_feature({}, port - 1, feature_selector)); - break; - } - case HubRequest::CLEAR_FEATURE: { - if (request.index == 0) { - // If index == 0, the actual request is Clear Hub Feature. - // UHCI does not provide "Local Power Source" or "Over-current" and their corresponding change flags. - // Therefore, ignore the request, but return an error if the value is not "Local Power Source" or "Over-current" - switch (request.value) { - case HubFeatureSelector::C_HUB_LOCAL_POWER: - case HubFeatureSelector::C_HUB_OVER_CURRENT: - break; - default: - return EINVAL; - } - - break; - } - - // If index != 0, the actual request is Clear Port Feature. - u8 port = request.index & 0xFF; - if (port > UHCIController::NUMBER_OF_ROOT_PORTS) - return EINVAL; - - auto feature_selector = (HubFeatureSelector)request.value; - TRY(m_uhci_controller->clear_port_feature({}, port - 1, feature_selector)); - break; - } - default: - return EINVAL; - } - - transfer.set_complete(); - return length; -} - -} diff --git a/Kernel/Bus/USB/UHCI/UHCIRootHub.h b/Kernel/Bus/USB/UHCI/UHCIRootHub.h deleted file mode 100644 index 3bce1d54e99..00000000000 --- a/Kernel/Bus/USB/UHCI/UHCIRootHub.h +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright (c) 2021, Luke Wilde - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include - -namespace Kernel::USB { - -class UHCIController; - -class UHCIRootHub { -public: - static ErrorOr> try_create(NonnullLockRefPtr); - - UHCIRootHub(NonnullLockRefPtr); - ~UHCIRootHub() = default; - - ErrorOr setup(Badge); - - u8 device_address() const { return m_hub->address(); } - - ErrorOr handle_control_transfer(Transfer& transfer); - - void check_for_port_updates() { m_hub->check_for_port_updates(); } - -private: - NonnullLockRefPtr m_uhci_controller; - LockRefPtr m_hub; -}; - -} diff --git a/Kernel/Bus/USB/USBClasses.h b/Kernel/Bus/USB/USBClasses.h deleted file mode 100644 index a7ee20a3bf2..00000000000 --- a/Kernel/Bus/USB/USBClasses.h +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright (c) 2021, Luke Wilde - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include - -namespace Kernel::USB { - -// https://www.usb.org/defined-class-codes -static constexpr u8 USB_CLASS_DEVICE = 0x00; -static constexpr u8 USB_CLASS_AUDIO = 0x01; -static constexpr u8 USB_CLASS_COMMUNICATIONS_AND_CDC_CONTROL = 0x02; -static constexpr u8 USB_CLASS_HID = 0x03; -static constexpr u8 USB_CLASS_PHYSICAL = 0x05; -static constexpr u8 USB_CLASS_IMAGE = 0x06; -static constexpr u8 USB_CLASS_PRINTER = 0x07; -static constexpr u8 USB_CLASS_MASS_STORAGE = 0x08; -static constexpr u8 USB_CLASS_HUB = 0x09; -static constexpr u8 USB_CLASS_CDC_DATE = 0x0A; -static constexpr u8 USB_CLASS_SMART_CARD = 0x0B; -static constexpr u8 USB_CLASS_CONTENT_SECURITY = 0x0D; -static constexpr u8 USB_CLASS_VIDEO = 0x0E; -static constexpr u8 USB_CLASS_PERSONAL_HEALTHCARE = 0x0F; -static constexpr u8 USB_CLASS_AUDIO_VIDEO = 0x10; -static constexpr u8 USB_CLASS_BILLBOARD = 0x11; -static constexpr u8 USB_CLASS_TYPE_C_BRIDGE = 0x12; -static constexpr u8 USB_CLASS_DIAGNOSTIC = 0xDC; -static constexpr u8 USB_CLASS_WIRELESS_CONTROLLER = 0xE0; -static constexpr u8 USB_CLASS_MISCELLANEOUS = 0xEF; -static constexpr u8 USB_CLASS_APPLICATION_SPECIFIC = 0xFE; -static constexpr u8 USB_CLASS_VENDOR_SPECIFIC = 0xFF; - -} diff --git a/Kernel/Bus/USB/USBConfiguration.cpp b/Kernel/Bus/USB/USBConfiguration.cpp deleted file mode 100644 index 89b335ae724..00000000000 --- a/Kernel/Bus/USB/USBConfiguration.cpp +++ /dev/null @@ -1,79 +0,0 @@ -/* - * Copyright (c) 2022, Jesse Buhagiar - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include -#include - -namespace Kernel::USB { - -ErrorOr USBConfiguration::enumerate_interfaces() -{ - auto descriptor_hierarchy_buffer = TRY(FixedArray::create(m_descriptor.total_length)); // Buffer for us to store the entire hierarchy into - - // The USB spec is a little bit janky here... Interface and Endpoint descriptors aren't fetched - // through a `GET_DESCRIPTOR` request to the device. Instead, the _entire_ hierarchy is returned - // to us in one go. - auto transfer_length = TRY(m_device.control_transfer(USB_REQUEST_TRANSFER_DIRECTION_DEVICE_TO_HOST, USB_REQUEST_GET_DESCRIPTOR, (DESCRIPTOR_TYPE_CONFIGURATION << 8), 0, m_descriptor.total_length, descriptor_hierarchy_buffer.data())); - - // FIXME: Why does transfer length return the actual size +8 bytes? - if (transfer_length < m_descriptor.total_length) - return EIO; - - u8* interface_descriptors_base = descriptor_hierarchy_buffer.data() + sizeof(USBConfigurationDescriptor); - USBInterfaceDescriptor* interface_descriptor = reinterpret_cast(interface_descriptors_base); - Vector endpoint_descriptors; - TRY(m_interfaces.try_ensure_capacity(m_descriptor.number_of_interfaces)); - for (auto interface = 0u; interface < m_descriptor.number_of_interfaces; interface++) { - endpoint_descriptors.ensure_capacity(interface_descriptor->number_of_endpoints); - - if constexpr (USB_DEBUG) { - dbgln("Interface Descriptor {}", interface); - dbgln("interface_id: {:02x}", interface_descriptor->interface_id); - dbgln("alternate_setting: {:02x}", interface_descriptor->alternate_setting); - dbgln("number_of_endpoints: {:02x}", interface_descriptor->number_of_endpoints); - dbgln("interface_class_code: {:02x}", interface_descriptor->interface_class_code); - dbgln("interface_sub_class_code: {:02x}", interface_descriptor->interface_sub_class_code); - dbgln("interface_protocol: {:02x}", interface_descriptor->interface_protocol); - dbgln("interface_string_descriptor_index: {}", interface_descriptor->interface_string_descriptor_index); - } - - // Get all the endpoint descriptors - for (auto endpoint = 0u; endpoint < interface_descriptor->number_of_endpoints; endpoint++) { - u8* raw_endpoint_descriptor_offset = interface_descriptors_base + sizeof(USBInterfaceDescriptor) + (endpoint * sizeof(USBEndpointDescriptor)); - - // FIXME: It looks like HID descriptors come BEFORE the endpoint descriptors for a HID device, so we should load - // these too eventually. - // See here: https://www.usb.org/defined-class-codes - if (interface_descriptor->interface_class_code == USB_CLASS_HID) - raw_endpoint_descriptor_offset += sizeof(USBHIDDescriptor); // Skip the HID descriptor (this was worked out via buffer inspection) - - USBEndpointDescriptor endpoint_descriptor; - memcpy(&endpoint_descriptor, raw_endpoint_descriptor_offset, sizeof(USBEndpointDescriptor)); - - if constexpr (USB_DEBUG) { - dbgln("Endpoint Descriptor {}", endpoint); - dbgln("Endpoint Address: {}", endpoint_descriptor.endpoint_address); - dbgln("Endpoint Attribute Bitmap: {:08b}", endpoint_descriptor.endpoint_attributes_bitmap); - dbgln("Endpoint Maximum Packet Size: {}", endpoint_descriptor.max_packet_size); - dbgln("Endpoint Poll Interval (in frames): {}", endpoint_descriptor.poll_interval_in_frames); - } - - endpoint_descriptors.unchecked_append(endpoint_descriptor); - } - - USBInterface device_interface(*this, *interface_descriptor, move(endpoint_descriptors)); - m_interfaces.unchecked_append(move(device_interface)); - interface_descriptor += interface_descriptor->number_of_endpoints * sizeof(USBEndpointDescriptor); - } - - return {}; -} - -} diff --git a/Kernel/Bus/USB/USBConfiguration.h b/Kernel/Bus/USB/USBConfiguration.h deleted file mode 100644 index 0f8106af4b7..00000000000 --- a/Kernel/Bus/USB/USBConfiguration.h +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright (c) 2022, Jesse Buhagiar - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include - -namespace Kernel::USB { - -class Device; - -class USBConfiguration { -public: - USBConfiguration() = delete; - USBConfiguration(Device& device, USBConfigurationDescriptor const descriptor) - : m_device(device) - , m_descriptor(descriptor) - { - m_interfaces.ensure_capacity(descriptor.number_of_interfaces); - } - - Device const& device() const { return m_device; } - USBConfigurationDescriptor const& descriptor() const { return m_descriptor; } - - u8 interface_count() const { return m_descriptor.number_of_interfaces; } - u8 configuration_id() const { return m_descriptor.configuration_value; } - u8 attributes() const { return m_descriptor.attributes_bitmap; } - u16 max_power_ma() const { return m_descriptor.max_power_in_ma * 2u; } // Note: "Power" is used incorrectly here, however it's what it's called in the descriptor/documentation - - Vector const& interfaces() const { return m_interfaces; } - - ErrorOr enumerate_interfaces(); - -private: - Device& m_device; // Reference to the device linked to this configuration - USBConfigurationDescriptor const m_descriptor; // Descriptor that backs this configuration - Vector m_interfaces; // Interfaces for this device -}; - -} diff --git a/Kernel/Bus/USB/USBConstants.h b/Kernel/Bus/USB/USBConstants.h deleted file mode 100644 index 98747c315c0..00000000000 --- a/Kernel/Bus/USB/USBConstants.h +++ /dev/null @@ -1,16 +0,0 @@ -/* - * Copyright (c) 2021, Luke Wilde - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include - -namespace Kernel::USB { - -// USB 2.0 Specification Section 9.4.6 -static constexpr u8 USB_MAX_ADDRESS = 127; - -} diff --git a/Kernel/Bus/USB/USBController.cpp b/Kernel/Bus/USB/USBController.cpp deleted file mode 100644 index 90f2909aaa6..00000000000 --- a/Kernel/Bus/USB/USBController.cpp +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright (c) 2021, Luke Wilde - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include - -namespace Kernel::USB { - -USBController::USBController() - : m_storage_controller_id(StorageManagement::generate_controller_id()) -{ -} - -u8 USBController::allocate_address() -{ - // FIXME: This can be smarter. - return m_next_device_index++; -} - -} diff --git a/Kernel/Bus/USB/USBController.h b/Kernel/Bus/USB/USBController.h deleted file mode 100644 index 448cae2b585..00000000000 --- a/Kernel/Bus/USB/USBController.h +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright (c) 2021, Luke Wilde - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include - -namespace Kernel::USB { - -class USBController : public AtomicRefCounted { -public: - virtual ~USBController() = default; - - virtual ErrorOr initialize() = 0; - - virtual ErrorOr reset() = 0; - virtual ErrorOr stop() = 0; - virtual ErrorOr start() = 0; - - virtual void cancel_async_transfer(NonnullLockRefPtr transfer) = 0; - virtual ErrorOr submit_control_transfer(Transfer&) = 0; - virtual ErrorOr submit_bulk_transfer(Transfer& transfer) = 0; - virtual ErrorOr submit_async_interrupt_transfer(NonnullLockRefPtr transfer, u16 ms_interval) = 0; - - u32 storage_controller_id() const { return m_storage_controller_id; } - u8 allocate_address(); - -protected: - USBController(); - -private: - // Note: We are pseudo storage controller for the sake of generating LUNs - // And do not follow a hardware_relative_controller_id for the controller class "USB", - // as we also have to follow the device id and its internal LUN, leaving no room for that - u32 m_storage_controller_id { 0 }; - - u8 m_next_device_index { 1 }; - - IntrusiveListNode> m_controller_list_node; - -public: - using List = IntrusiveList<&USBController::m_controller_list_node>; -}; - -} diff --git a/Kernel/Bus/USB/USBDescriptors.h b/Kernel/Bus/USB/USBDescriptors.h deleted file mode 100644 index 09d11af99cc..00000000000 --- a/Kernel/Bus/USB/USBDescriptors.h +++ /dev/null @@ -1,132 +0,0 @@ -/* - * Copyright (c) 2021, Jesse Buhagiar - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include - -namespace Kernel::USB { - -struct [[gnu::packed]] USBDescriptorCommon { - u8 length; - u8 descriptor_type; -}; - -// -// Device Descriptor -// ================= -// -// This descriptor type (stored on the device), represents the device, and gives -// information related to it, such as the USB specification it complies to, -// as well as the vendor and product ID of the device. -// -// https://beyondlogic.org/usbnutshell/usb5.shtml#DeviceDescriptors -struct [[gnu::packed]] USBDeviceDescriptor { - USBDescriptorCommon descriptor_header; - u16 usb_spec_compliance_bcd; - u8 device_class; - u8 device_sub_class; - u8 device_protocol; - u8 max_packet_size; - u16 vendor_id; - u16 product_id; - u16 device_release_bcd; - u8 manufacturer_id_descriptor_index; - u8 product_string_descriptor_index; - u8 serial_number_descriptor_index; - u8 num_configurations; -}; -static_assert(sizeof(USBDeviceDescriptor) == 18); - -// -// Configuration Descriptor -// ======================== -// -// A USB device can have multiple configurations, which tells us about how the -// device is physically configured (e.g how it's powered, max power consumption etc). -// -struct [[gnu::packed]] USBConfigurationDescriptor { - USBDescriptorCommon descriptor_header; - u16 total_length; - u8 number_of_interfaces; - u8 configuration_value; - u8 configuration_string_descriptor_index; - u8 attributes_bitmap; - u8 max_power_in_ma; -}; - -// -// Interface Descriptor -// ==================== -// -// An interface descriptor describes to us one or more endpoints, grouped -// together to define a singular function of a device. -// As an example, a USB webcam might have two interface descriptors; one -// for the camera, and one for the microphone. -// -struct [[gnu::packed]] USBInterfaceDescriptor { - USBDescriptorCommon descriptor_header; - u8 interface_id; - u8 alternate_setting; - u8 number_of_endpoints; - u8 interface_class_code; - u8 interface_sub_class_code; - u8 interface_protocol; - u8 interface_string_descriptor_index; -}; - -// -// Endpoint Descriptor -// =================== -// -// The lowest leaf in the configuration tree. And endpoint descriptor describes -// the physical transfer properties of the endpoint (that isn't endpoint0). -// The description given by this structure is used by a pipe to create a -// "connection" from the host to the device. -// https://docs.microsoft.com/en-us/windows-hardware/drivers/usbcon/usb-endpoints-and-their-pipes -struct [[gnu::packed]] USBEndpointDescriptor { - USBDescriptorCommon descriptor_header; - u8 endpoint_address; - u8 endpoint_attributes_bitmap; - u16 max_packet_size; - u8 poll_interval_in_frames; -}; - -// -// USB 1.1/2.0 Hub Descriptor -// ============== -// -struct [[gnu::packed]] USBHubDescriptor { - USBDescriptorCommon descriptor_header; - u8 number_of_downstream_ports; - u16 hub_characteristics; - u8 power_on_to_power_good_time; - u8 hub_controller_current; - // NOTE: This does not contain DeviceRemovable or PortPwrCtrlMask because a struct cannot have two VLAs in a row. -}; - -// -// USB Human Interface Device (HID) Descriptor -// ============== -// -struct [[gnu::packed]] USBHIDDescriptor { - USBDescriptorCommon descriptor_header; - u16 hid_bcd; - u8 country_code; - u8 number_of_report_descriptors; - u8 following_descriptor_type; - u16 hid_report_descriptor_size; -}; - -static constexpr u8 DESCRIPTOR_TYPE_DEVICE = 0x01; -static constexpr u8 DESCRIPTOR_TYPE_CONFIGURATION = 0x02; -static constexpr u8 DESCRIPTOR_TYPE_STRING = 0x03; -static constexpr u8 DESCRIPTOR_TYPE_INTERFACE = 0x04; -static constexpr u8 DESCRIPTOR_TYPE_ENDPOINT = 0x05; -static constexpr u8 DESCRIPTOR_TYPE_DEVICE_QUALIFIER = 0x06; -static constexpr u8 DESCRIPTOR_TYPE_HUB = 0x29; - -} diff --git a/Kernel/Bus/USB/USBDevice.cpp b/Kernel/Bus/USB/USBDevice.cpp deleted file mode 100644 index d32785ff697..00000000000 --- a/Kernel/Bus/USB/USBDevice.cpp +++ /dev/null @@ -1,171 +0,0 @@ -/* - * Copyright (c) 2021-2023, Jesse Buhagiar - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace Kernel::USB { - -ErrorOr> Device::try_create(USBController const& controller, u8 port, DeviceSpeed speed) -{ - auto pipe = TRY(ControlPipe::create(controller, 0, 8, 0)); - auto device = TRY(adopt_nonnull_lock_ref_or_enomem(new (nothrow) Device(controller, port, speed, move(pipe)))); - auto sysfs_node = TRY(SysFSUSBDeviceInformation::create(*device)); - device->m_sysfs_device_info_node.with([&](auto& node) { - node = move(sysfs_node); - }); - TRY(device->enumerate_device()); - - // Attempt to find a driver for this device. If one is found, we call the driver's - // "probe" function, which initialises the local state for the device driver. - // It is currently the driver's responsibility to search the configuration/interface - // and take the appropriate action. - for (auto& driver : USBManagement::the().available_drivers()) { - // FIXME: Some devices have multiple configurations, for which we may have a better driver, - // than the first we find, or we have a vendor specific driver for the device, - // so we want a prioritization mechanism here - auto result = driver->probe(device); - if (result.is_error()) - continue; - dbgln_if(USB_DEBUG, "Found driver {} for device {:04x}:{:04x}!", driver->name(), device->m_vendor_id, device->m_product_id); - device->set_driver(driver); - break; - } - - return device; -} - -Device::Device(USBController const& controller, u8 port, DeviceSpeed speed, NonnullOwnPtr default_pipe) - : m_device_port(port) - , m_device_speed(speed) - , m_address(0) - , m_controller(controller) - , m_default_pipe(move(default_pipe)) -{ -} - -Device::Device(NonnullLockRefPtr controller, u8 address, u8 port, DeviceSpeed speed, NonnullOwnPtr default_pipe) - : m_device_port(port) - , m_device_speed(speed) - , m_address(address) - , m_controller(move(controller)) - , m_default_pipe(move(default_pipe)) -{ -} - -Device::Device(Device const& device, NonnullOwnPtr default_pipe) - : m_device_port(device.port()) - , m_device_speed(device.speed()) - , m_address(device.address()) - , m_device_descriptor(device.device_descriptor()) - , m_configurations(device.configurations()) - , m_controller(device.controller()) - , m_default_pipe(move(default_pipe)) -{ -} - -Device::~Device() = default; - -ErrorOr Device::enumerate_device() -{ - USBDeviceDescriptor dev_descriptor {}; - - // Send 8-bytes to get at least the `max_packet_size` from the device - constexpr u8 short_device_descriptor_length = 8; - auto transfer_length = TRY(m_default_pipe->submit_control_transfer(USB_REQUEST_TRANSFER_DIRECTION_DEVICE_TO_HOST, USB_REQUEST_GET_DESCRIPTOR, (DESCRIPTOR_TYPE_DEVICE << 8), 0, short_device_descriptor_length, &dev_descriptor)); - - // FIXME: This be "not equal to" instead of "less than", but control transfers report a higher transfer length than expected. - if (transfer_length < short_device_descriptor_length) { - dbgln("USB Device: Not enough bytes for short device descriptor. Expected {}, got {}.", short_device_descriptor_length, transfer_length); - return EIO; - } - - if constexpr (USB_DEBUG) { - dbgln("USB Short Device Descriptor:"); - dbgln("Descriptor length: {}", dev_descriptor.descriptor_header.length); - dbgln("Descriptor type: {}", dev_descriptor.descriptor_header.descriptor_type); - - dbgln("Device Class: {:02x}", dev_descriptor.device_class); - dbgln("Device Sub-Class: {:02x}", dev_descriptor.device_sub_class); - dbgln("Device Protocol: {:02x}", dev_descriptor.device_protocol); - dbgln("Max Packet Size: {:02x} bytes", dev_descriptor.max_packet_size); - } - - // Ensure that this is actually a valid device descriptor... - VERIFY(dev_descriptor.descriptor_header.descriptor_type == DESCRIPTOR_TYPE_DEVICE); - m_default_pipe->set_max_packet_size(dev_descriptor.max_packet_size); - - transfer_length = TRY(m_default_pipe->submit_control_transfer(USB_REQUEST_TRANSFER_DIRECTION_DEVICE_TO_HOST, USB_REQUEST_GET_DESCRIPTOR, (DESCRIPTOR_TYPE_DEVICE << 8), 0, sizeof(USBDeviceDescriptor), &dev_descriptor)); - - // FIXME: This be "not equal to" instead of "less than", but control transfers report a higher transfer length than expected. - if (transfer_length < sizeof(USBDeviceDescriptor)) { - dbgln("USB Device: Unexpected device descriptor length. Expected {}, got {}.", sizeof(USBDeviceDescriptor), transfer_length); - return EIO; - } - - // Ensure that this is actually a valid device descriptor... - VERIFY(dev_descriptor.descriptor_header.descriptor_type == DESCRIPTOR_TYPE_DEVICE); - - if constexpr (USB_DEBUG) { - dbgln("USB Device Descriptor for {:04x}:{:04x}", dev_descriptor.vendor_id, dev_descriptor.product_id); - dbgln("Device Class: {:02x}", dev_descriptor.device_class); - dbgln("Device Sub-Class: {:02x}", dev_descriptor.device_sub_class); - dbgln("Device Protocol: {:02x}", dev_descriptor.device_protocol); - dbgln("Max Packet Size: {:02x} bytes", dev_descriptor.max_packet_size); - dbgln("Number of configurations: {:02x}", dev_descriptor.num_configurations); - } - - auto new_address = m_controller->allocate_address(); - - // Attempt to set devices address on the bus - transfer_length = TRY(m_default_pipe->submit_control_transfer(USB_REQUEST_TRANSFER_DIRECTION_HOST_TO_DEVICE, USB_REQUEST_SET_ADDRESS, new_address, 0, 0, nullptr)); - - // This has to be set after we send out the "Set Address" request because it might be sent to the root hub. - // The root hub uses the address to intercept requests to itself. - m_address = new_address; - m_default_pipe->set_device_address(new_address); - - dbgln_if(USB_DEBUG, "USB Device: Set address to {}", m_address); - - memcpy(&m_device_descriptor, &dev_descriptor, sizeof(USBDeviceDescriptor)); - - // Fetch the configuration descriptors from the device - m_configurations.ensure_capacity(m_device_descriptor.num_configurations); - for (auto configuration = 0u; configuration < m_device_descriptor.num_configurations; configuration++) { - USBConfigurationDescriptor configuration_descriptor; - transfer_length = TRY(m_default_pipe->submit_control_transfer(USB_REQUEST_TRANSFER_DIRECTION_DEVICE_TO_HOST, USB_REQUEST_GET_DESCRIPTOR, (DESCRIPTOR_TYPE_CONFIGURATION << 8u) | configuration, 0, sizeof(USBConfigurationDescriptor), &configuration_descriptor)); - - if constexpr (USB_DEBUG) { - dbgln("USB Configuration Descriptor {}", configuration); - dbgln("Total Length: {}", configuration_descriptor.total_length); - dbgln("Number of interfaces: {}", configuration_descriptor.number_of_interfaces); - dbgln("Configuration Value: {}", configuration_descriptor.configuration_value); - dbgln("Attributes Bitmap: {:08b}", configuration_descriptor.attributes_bitmap); - dbgln("Maximum Power: {}mA", configuration_descriptor.max_power_in_ma * 2u); // This value is in 2mA steps - } - - USBConfiguration device_configuration(*this, configuration_descriptor); - TRY(device_configuration.enumerate_interfaces()); - m_configurations.append(device_configuration); - } - - return {}; -} - -ErrorOr Device::control_transfer(u8 request_type, u8 request, u16 value, u16 index, u16 length, void* data) -{ - return TRY(m_default_pipe->submit_control_transfer(request_type, request, value, index, length, data)); -} - -} diff --git a/Kernel/Bus/USB/USBDevice.h b/Kernel/Bus/USB/USBDevice.h deleted file mode 100644 index 81c2b7e04c8..00000000000 --- a/Kernel/Bus/USB/USBDevice.h +++ /dev/null @@ -1,99 +0,0 @@ -/* - * Copyright (c) 2021, Jesse Buhagiar - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include -#include -#include -#include - -namespace Kernel { -class SysFSUSBDeviceInformation; -} - -namespace Kernel::USB { - -class USBController; -class USBConfiguration; - -// -// Some nice info from FTDI on device enumeration and how some of this -// glues together: -// -// https://www.ftdichip.com/Support/Documents/TechnicalNotes/TN_113_Simplified%20Description%20of%20USB%20Device%20Enumeration.pdf -class Hub; -class Device : public AtomicRefCounted { -public: - enum class DeviceSpeed : u8 { - FullSpeed = 0, - LowSpeed - }; - - static ErrorOr> try_create(USBController const&, u8, DeviceSpeed); - - Device(USBController const&, u8, DeviceSpeed, NonnullOwnPtr default_pipe); - Device(Device const& device, NonnullOwnPtr default_pipe); - virtual ~Device(); - - ErrorOr enumerate_device(); - - u8 port() const { return m_device_port; } - DeviceSpeed speed() const { return m_device_speed; } - - u8 address() const { return m_address; } - - USBDeviceDescriptor const& device_descriptor() const { return m_device_descriptor; } - - USBController& controller() { return *m_controller; } - USBController const& controller() const { return *m_controller; } - - ErrorOr control_transfer(u8 request_type, u8 request, u16 value, u16 index, u16 length, void* data); - - Vector const& configurations() const { return m_configurations; } - - void set_driver(Driver& driver) { m_driver = driver; } - void detach() - { - if (m_driver) - m_driver->detach(*this); - m_driver = nullptr; - } - - SpinlockProtected, LockRank::None>& sysfs_device_info_node(Badge) { return m_sysfs_device_info_node; } - -protected: - Device(NonnullLockRefPtr controller, u8 address, u8 port, DeviceSpeed speed, NonnullOwnPtr default_pipe); - - u8 m_device_port { 0 }; // What port is this device attached to. NOTE: This is 1-based. - DeviceSpeed m_device_speed; // What speed is this device running at - u8 m_address { 0 }; // USB address assigned to this device - - // Device description - u16 m_vendor_id { 0 }; // This device's vendor ID assigned by the USB group - u16 m_product_id { 0 }; // This device's product ID assigned by the USB group - USBDeviceDescriptor m_device_descriptor {}; // Device Descriptor obtained from USB Device - Vector m_configurations; // Configurations for this device - - NonnullLockRefPtr m_controller; - NonnullOwnPtr m_default_pipe; // Default communication pipe (endpoint0) used during enumeration - - LockRefPtr m_driver; - -private: - IntrusiveListNode> m_hub_child_node; - -protected: - SpinlockProtected, LockRank::None> m_sysfs_device_info_node; - -public: - using List = IntrusiveList<&Device::m_hub_child_node>; -}; -} diff --git a/Kernel/Bus/USB/USBEndpoint.h b/Kernel/Bus/USB/USBEndpoint.h deleted file mode 100644 index fc34d3a8e40..00000000000 --- a/Kernel/Bus/USB/USBEndpoint.h +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright (c) 2021, Jesse Buhagiar - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include - -namespace Kernel::USB { - -// -// An endpoint is the "end point" of communication of a USB device. That is, data is read from and written -// to an endpoint via a USB pipe. As an example, during device enumeration (where we assign an address to the -// device), we communicate with the device over the default endpoint, endpoint0, which all devices _must_ -// contain to be compliant with the USB specification. -// -// And endpoint describes characteristics about the transfer between the host and the device, such as: -// - The endpoint number -// - Max packet size of send/recv of the endpoint -// - Transfer type (bulk, interrupt, isochronous etc) -// -// Take for example a USB multifunction device, such as a keyboard/mouse combination. The mouse -// may need to be polled every n milliseconds, meaning the transfer may be isochronous (streamed), -// while the keyboard part would only generate data once we push a key (hence an interrupt transfer). -// Each of these data sources would be a _different_ endpoint on the device that we read from. -class USBEndpoint { -public: - static constexpr u8 ENDPOINT_ADDRESS_NUMBER_MASK = 0x0f; - static constexpr u8 ENDPOINT_ADDRESS_DIRECTION_MASK = 0x80; - static constexpr u8 ENDPOINT_ADDRESS_DIRECTION_OUT = 0x00; - static constexpr u8 ENDPOINT_ADDRESS_DIRECTION_IN = 0x80; - - static constexpr u8 ENDPOINT_ATTRIBUTES_TRANSFER_TYPE_MASK = 0x03; - static constexpr u8 ENDPOINT_ATTRIBUTES_TRANSFER_TYPE_CONTROL = 0x00; - static constexpr u8 ENDPOINT_ATTRIBUTES_TRANSFER_TYPE_ISOCHRONOUS = 0x01; - static constexpr u8 ENDPOINT_ATTRIBUTES_TRANSFER_TYPE_BULK = 0x02; - static constexpr u8 ENDPOINT_ATTRIBUTES_TRANSFER_TYPE_INTERRUPT = 0x03; - - static constexpr u8 ENDPOINT_ATTRIBUTES_ISO_MODE_SYNC_TYPE = 0x0c; - static constexpr u8 ENDPOINT_ATTRIBUTES_ISO_MODE_USAGE_TYPE = 0x30; - - USBEndpointDescriptor const& descriptor() const { return m_descriptor; } - - bool is_control() const { return (m_descriptor.endpoint_attributes_bitmap & ENDPOINT_ATTRIBUTES_TRANSFER_TYPE_MASK) == ENDPOINT_ATTRIBUTES_TRANSFER_TYPE_CONTROL; } - bool is_isochronous() const { return (m_descriptor.endpoint_attributes_bitmap & ENDPOINT_ATTRIBUTES_TRANSFER_TYPE_MASK) == ENDPOINT_ATTRIBUTES_TRANSFER_TYPE_ISOCHRONOUS; } - bool is_bulk() const { return (m_descriptor.endpoint_attributes_bitmap & ENDPOINT_ATTRIBUTES_TRANSFER_TYPE_MASK) == ENDPOINT_ATTRIBUTES_TRANSFER_TYPE_BULK; } - bool is_interrupt() const { return (m_descriptor.endpoint_attributes_bitmap & ENDPOINT_ATTRIBUTES_TRANSFER_TYPE_MASK) == ENDPOINT_ATTRIBUTES_TRANSFER_TYPE_INTERRUPT; } - - u16 max_packet_size() const { return m_descriptor.max_packet_size; } - u8 polling_interval() const { return m_descriptor.poll_interval_in_frames; } - -private: - USBEndpointDescriptor m_descriptor; - - Pipe m_pipe; -}; - -} diff --git a/Kernel/Bus/USB/USBHub.cpp b/Kernel/Bus/USB/USBHub.cpp deleted file mode 100644 index 6557dc2d567..00000000000 --- a/Kernel/Bus/USB/USBHub.cpp +++ /dev/null @@ -1,319 +0,0 @@ -/* - * Copyright (c) 2021, Luke Wilde - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -namespace Kernel::USB { - -ErrorOr> Hub::try_create_root_hub(NonnullLockRefPtr controller, DeviceSpeed device_speed) -{ - // NOTE: Enumeration does not happen here, as the controller must know what the device address is at all times during enumeration to intercept requests. - auto pipe = TRY(ControlPipe::create(controller, 0, 8, 0)); - auto hub = TRY(adopt_nonnull_lock_ref_or_enomem(new (nothrow) Hub(controller, device_speed, move(pipe)))); - return hub; -} - -ErrorOr> Hub::try_create_from_device(Device const& device) -{ - auto pipe = TRY(ControlPipe::create(device.controller(), 0, device.device_descriptor().max_packet_size, device.address())); - auto hub = TRY(adopt_nonnull_lock_ref_or_enomem(new (nothrow) Hub(device, move(pipe)))); - TRY(hub->enumerate_and_power_on_hub()); - return hub; -} - -Hub::Hub(NonnullLockRefPtr controller, DeviceSpeed device_speed, NonnullOwnPtr default_pipe) - : Device(move(controller), 1 /* Port 1 */, device_speed, move(default_pipe)) -{ -} - -Hub::Hub(Device const& device, NonnullOwnPtr default_pipe) - : Device(device, move(default_pipe)) -{ -} - -ErrorOr Hub::enumerate_and_power_on_hub() -{ - // USBDevice::enumerate_device must be called before this. - VERIFY(m_address > 0); - - TRY(m_sysfs_device_info_node.with([&](auto& node) -> ErrorOr { - node = TRY(SysFSUSBDeviceInformation::create(*this)); - return {}; - })); - - if (m_device_descriptor.device_class != USB_CLASS_HUB) { - dbgln("USB Hub: Trying to enumerate and power on a device that says it isn't a hub."); - return EINVAL; - } - - dbgln_if(USB_DEBUG, "USB Hub: Enumerating and powering on for address {}", m_address); - - USBHubDescriptor descriptor {}; - - // Get the first hub descriptor. All hubs are required to have a hub descriptor at index 0. USB 2.0 Specification Section 11.24.2.5. - auto transfer_length = TRY(m_default_pipe->submit_control_transfer(USB_REQUEST_TRANSFER_DIRECTION_DEVICE_TO_HOST | USB_REQUEST_TYPE_CLASS, HubRequest::GET_DESCRIPTOR, (DESCRIPTOR_TYPE_HUB << 8), 0, sizeof(USBHubDescriptor), &descriptor)); - - // FIXME: This be "not equal to" instead of "less than", but control transfers report a higher transfer length than expected. - if (transfer_length < sizeof(USBHubDescriptor)) { - dbgln("USB Hub: Unexpected hub descriptor size. Expected {}, got {}", sizeof(USBHubDescriptor), transfer_length); - return EIO; - } - - if constexpr (USB_DEBUG) { - dbgln("USB Hub Descriptor for {:04x}:{:04x}", m_vendor_id, m_product_id); - dbgln("Number of Downstream Ports: {}", descriptor.number_of_downstream_ports); - dbgln("Hub Characteristics: {:#04x}", static_cast(descriptor.hub_characteristics)); - dbgln("Power On to Power Good Time: {} ms ({} * 2ms)", descriptor.power_on_to_power_good_time * 2, descriptor.power_on_to_power_good_time); - dbgln("Hub Controller Current: {} mA", descriptor.hub_controller_current); - } - - // FIXME: Queue the status change interrupt - - // Enable all the ports - for (u8 port_index = 0; port_index < descriptor.number_of_downstream_ports; ++port_index) { - auto result = m_default_pipe->submit_control_transfer(USB_REQUEST_TRANSFER_DIRECTION_HOST_TO_DEVICE | USB_REQUEST_TYPE_CLASS | USB_REQUEST_RECIPIENT_OTHER, HubRequest::SET_FEATURE, HubFeatureSelector::PORT_POWER, port_index + 1, 0, nullptr); - if (result.is_error()) - dbgln("USB: Failed to power on port {} on hub at address {}.", port_index + 1, m_address); - } - - // Wait for the ports to power up. power_on_to_power_good_time is in units of 2 ms and we want in us, so multiply by 2000. - microseconds_delay(descriptor.power_on_to_power_good_time * 2000); - - memcpy(&m_hub_descriptor, &descriptor, sizeof(USBHubDescriptor)); - - return {}; -} - -// USB 2.0 Specification Section 11.24.2.7 -ErrorOr Hub::get_port_status(u8 port, HubStatus& hub_status) -{ - // Ports are 1-based. - if (port == 0 || port > m_hub_descriptor.number_of_downstream_ports) - return EINVAL; - - auto transfer_length = TRY(m_default_pipe->submit_control_transfer(USB_REQUEST_TRANSFER_DIRECTION_DEVICE_TO_HOST | USB_REQUEST_TYPE_CLASS | USB_REQUEST_RECIPIENT_OTHER, HubRequest::GET_STATUS, 0, port, sizeof(HubStatus), &hub_status)); - - // FIXME: This be "not equal to" instead of "less than", but control transfers report a higher transfer length than expected. - if (transfer_length < sizeof(HubStatus)) { - dbgln("USB Hub: Unexpected hub status size. Expected {}, got {}.", sizeof(HubStatus), transfer_length); - return EIO; - } - - return {}; -} - -// USB 2.0 Specification Section 11.24.2.2 -ErrorOr Hub::clear_port_feature(u8 port, HubFeatureSelector feature_selector) -{ - // Ports are 1-based. - if (port == 0 || port > m_hub_descriptor.number_of_downstream_ports) - return EINVAL; - - TRY(m_default_pipe->submit_control_transfer(USB_REQUEST_TRANSFER_DIRECTION_HOST_TO_DEVICE | USB_REQUEST_TYPE_CLASS | USB_REQUEST_RECIPIENT_OTHER, HubRequest::CLEAR_FEATURE, feature_selector, port, 0, nullptr)); - return {}; -} - -// USB 2.0 Specification Section 11.24.2.13 -ErrorOr Hub::set_port_feature(u8 port, HubFeatureSelector feature_selector) -{ - // Ports are 1-based. - if (port == 0 || port > m_hub_descriptor.number_of_downstream_ports) - return EINVAL; - - TRY(m_default_pipe->submit_control_transfer(USB_REQUEST_TRANSFER_DIRECTION_HOST_TO_DEVICE | USB_REQUEST_TYPE_CLASS | USB_REQUEST_RECIPIENT_OTHER, HubRequest::SET_FEATURE, feature_selector, port, 0, nullptr)); - return {}; -} - -void Hub::remove_children_from_sysfs() -{ - for (auto& child : m_children) { - child.sysfs_device_info_node({}).with([](auto& node) { - SysFSUSBBusDirectory::the().unplug({}, *node); - }); - } -} - -void Hub::check_for_port_updates() -{ - for (u8 port_index = 0; port_index < m_hub_descriptor.number_of_downstream_ports; ++port_index) { - u8 port_number = port_index + 1; - dbgln_if(USB_DEBUG, "USB Hub: Checking for port updates on port {}...", port_number); - - HubStatus port_status {}; - if (auto result = get_port_status(port_number, port_status); result.is_error()) { - dbgln("USB Hub: Error occurred when getting status for port {}: {}. Checking next port instead.", port_number, result.error()); - continue; - } - - if (port_status.change & PORT_STATUS_CONNECT_STATUS_CHANGED) { - // Clear the connection status change notification. - if (auto result = clear_port_feature(port_number, HubFeatureSelector::C_PORT_CONNECTION); result.is_error()) { - dbgln("USB Hub: Error occurred when clearing port connection change for port {}: {}.", port_number, result.error()); - return; - } - - if (port_status.status & PORT_STATUS_CURRENT_CONNECT_STATUS) { - dbgln("USB Hub: Device attached to port {}!", port_number); - - // Debounce the port. USB 2.0 Specification Page 150 - // Debounce interval is 100 ms (100000 us). USB 2.0 Specification Page 188 Table 7-14. - constexpr u32 debounce_interval = 100 * 1000; - - // We must check if the device disconnected every so often. If it disconnects, we must reset the debounce timer. - // This doesn't seem to be specified. Let's check every 10ms (10000 us). - constexpr u32 debounce_disconnect_check_interval = 10 * 1000; - - u32 debounce_timer = 0; - - dbgln_if(USB_DEBUG, "USB Hub: Debouncing..."); - - // FIXME: Timeout - while (debounce_timer < debounce_interval) { - microseconds_delay(debounce_disconnect_check_interval); - debounce_timer += debounce_disconnect_check_interval; - - if (auto result = get_port_status(port_number, port_status); result.is_error()) { - dbgln("USB Hub: Error occurred when getting status while debouncing port {}: {}.", port_number, result.error()); - return; - } - - if (!(port_status.change & PORT_STATUS_CONNECT_STATUS_CHANGED)) - continue; - - dbgln_if(USB_DEBUG, "USB Hub: Connection status changed while debouncing, resetting debounce timer."); - debounce_timer = 0; - - if (auto result = clear_port_feature(port_number, HubFeatureSelector::C_PORT_CONNECTION); result.is_error()) { - dbgln("USB Hub: Error occurred when clearing port connection change while debouncing port {}: {}.", port_number, result.error()); - return; - } - } - - // Reset the port - dbgln_if(USB_DEBUG, "USB Hub: Debounce finished. Driving reset..."); - if (auto result = set_port_feature(port_number, HubFeatureSelector::PORT_RESET); result.is_error()) { - dbgln("USB Hub: Error occurred when resetting port {}: {}.", port_number, result.error()); - return; - } - - // FIXME: Timeout - for (;;) { - // Wait at least 10 ms for the port to reset. - // This is T DRST in the USB 2.0 Specification Page 186 Table 7-13. - constexpr u16 reset_delay = 10 * 1000; - microseconds_delay(reset_delay); - - if (auto result = get_port_status(port_number, port_status); result.is_error()) { - dbgln("USB Hub: Error occurred when getting status while resetting port {}: {}.", port_number, result.error()); - return; - } - - if (port_status.change & PORT_STATUS_RESET_CHANGED) - break; - } - - // Stop asserting reset. This also causes the port to become enabled. - - if (auto result = clear_port_feature(port_number, HubFeatureSelector::C_PORT_RESET); result.is_error()) { - dbgln("USB Hub: Error occurred when resetting port {}: {}.", port_number, result.error()); - return; - } - - // Wait 10 ms for the port to recover. - // This is T RSTRCY in the USB 2.0 Specification Page 188 Table 7-14. - constexpr u16 reset_recovery_delay = 10 * 1000; - microseconds_delay(reset_recovery_delay); - - dbgln_if(USB_DEBUG, "USB Hub: Reset complete!"); - - // The port is ready to go. This is where we start communicating with the device to set up a driver for it. - - if (auto result = get_port_status(port_number, port_status); result.is_error()) { - dbgln("USB Hub: Error occurred when getting status for port {} after reset: {}.", port_number, result.error()); - return; - } - - // FIXME: Check for high speed. - auto speed = port_status.status & PORT_STATUS_LOW_SPEED_DEVICE_ATTACHED ? USB::Device::DeviceSpeed::LowSpeed : USB::Device::DeviceSpeed::FullSpeed; - - auto device_or_error = USB::Device::try_create(m_controller, port_number, speed); - if (device_or_error.is_error()) { - dbgln("USB Hub: Failed to create device for port {}: {}", port_number, device_or_error.error()); - return; - } - - auto device = device_or_error.release_value(); - - dbgln_if(USB_DEBUG, "USB Hub: Created device with address {}!", device->address()); - - if (device->device_descriptor().device_class == USB_CLASS_HUB) { - auto hub_or_error = Hub::try_create_from_device(*device); - if (hub_or_error.is_error()) { - dbgln("USB Hub: Failed to upgrade device to hub for port {}: {}", port_number, device_or_error.error()); - return; - } - - dbgln_if(USB_DEBUG, "USB Hub: Upgraded device at address {} to hub!", device->address()); - - auto hub = hub_or_error.release_value(); - m_children.append(hub); - hub->sysfs_device_info_node({}).with([](auto& node) { - SysFSUSBBusDirectory::the().plug({}, *node); - }); - } else { - m_children.append(device); - device->sysfs_device_info_node({}).with([](auto& node) { - SysFSUSBBusDirectory::the().plug({}, *node); - }); - } - - } else { - dbgln("USB Hub: Device detached on port {}!", port_number); - - LockRefPtr device_to_remove = nullptr; - for (auto& child : m_children) { - if (port_number == child.port()) { - device_to_remove = &child; - break; - } - } - - if (device_to_remove) { - device_to_remove->sysfs_device_info_node({}).with([](auto& node) { - SysFSUSBBusDirectory::the().unplug({}, *node); - }); - if (device_to_remove->device_descriptor().device_class == USB_CLASS_HUB) { - auto* hub_child = static_cast(device_to_remove.ptr()); - hub_child->remove_children_from_sysfs(); - } - - device_to_remove->detach(); - - m_children.remove(*device_to_remove); - } else { - dbgln_if(USB_DEBUG, "USB Hub: No child set up on port {}, ignoring detachment.", port_number); - } - } - } - } - - for (auto& child : m_children) { - if (child.device_descriptor().device_class == USB_CLASS_HUB) { - auto& hub_child = static_cast(child); - dbgln_if(USB_DEBUG, "USB Hub: Checking for port updates on child hub at address {}...", child.address()); - hub_child.check_for_port_updates(); - } - } -} - -} diff --git a/Kernel/Bus/USB/USBHub.h b/Kernel/Bus/USB/USBHub.h deleted file mode 100644 index dc30e33674d..00000000000 --- a/Kernel/Bus/USB/USBHub.h +++ /dev/null @@ -1,108 +0,0 @@ -/* - * Copyright (c) 2021, Luke Wilde - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include - -namespace Kernel::USB { - -// USB 2.0 Specification Page 421 Table 11-16 -enum HubRequest : u8 { - GET_STATUS = 0, - CLEAR_FEATURE = 1, - // 2 is reserved. - SET_FEATURE = 3, - // 4-5 are reserved. - GET_DESCRIPTOR = 6, - SET_DESCRIPTOR = 7, - CLEAR_TT_BUFFER = 8, - RESET_TT = 9, - GET_TT_STATE = 10, - STOP_TT = 11, -}; - -// USB 2.0 Specification Pages 421-422 Table 11-17 -enum HubFeatureSelector : u8 { - C_HUB_LOCAL_POWER = 0, - C_HUB_OVER_CURRENT = 1, - PORT_CONNECTION = 0, - PORT_ENABLE = 1, - PORT_SUSPEND = 2, - PORT_OVER_CURRENT = 3, - PORT_RESET = 4, - PORT_POWER = 8, - PORT_LOW_SPEED = 9, - C_PORT_CONNECTION = 16, - C_PORT_ENABLE = 17, - C_PORT_SUSPEND = 18, - C_PORT_OVER_CURRENT = 19, - C_PORT_RESET = 20, - PORT_TEST = 21, - PORT_INDICATOR = 22, -}; - -// USB 2.0 Specification Section 11.24.2.{6,7} -// This is used to store both the hub status and port status, as they have the same layout. -struct [[gnu::packed]] HubStatus { - u16 status { 0 }; - u16 change { 0 }; -}; -static_assert(AssertSize()); - -static constexpr u16 HUB_STATUS_LOCAL_POWER_SOURCE = (1 << 0); -static constexpr u16 HUB_STATUS_OVER_CURRENT = (1 << 1); - -static constexpr u16 HUB_STATUS_LOCAL_POWER_SOURCE_CHANGED = (1 << 0); -static constexpr u16 HUB_STATUS_OVER_CURRENT_CHANGED = (1 << 1); - -static constexpr u16 PORT_STATUS_CURRENT_CONNECT_STATUS = (1 << 0); -static constexpr u16 PORT_STATUS_PORT_ENABLED = (1 << 1); -static constexpr u16 PORT_STATUS_SUSPEND = (1 << 2); -static constexpr u16 PORT_STATUS_OVER_CURRENT = (1 << 3); -static constexpr u16 PORT_STATUS_RESET = (1 << 4); -static constexpr u16 PORT_STATUS_PORT_POWER = (1 << 8); -static constexpr u16 PORT_STATUS_LOW_SPEED_DEVICE_ATTACHED = (1 << 9); -static constexpr u16 PORT_STATUS_HIGH_SPEED_DEVICE_ATTACHED = (1 << 10); -static constexpr u16 PORT_STATUS_PORT_STATUS_MODE = (1 << 11); -static constexpr u16 PORT_STATUS_PORT_INDICATOR_CONTROL = (1 << 12); - -static constexpr u16 PORT_STATUS_CONNECT_STATUS_CHANGED = (1 << 0); -static constexpr u16 PORT_STATUS_PORT_ENABLED_CHANGED = (1 << 1); -static constexpr u16 PORT_STATUS_SUSPEND_CHANGED = (1 << 2); -static constexpr u16 PORT_STATUS_OVER_CURRENT_INDICATOR_CHANGED = (1 << 3); -static constexpr u16 PORT_STATUS_RESET_CHANGED = (1 << 4); - -class Hub : public Device { -public: - static ErrorOr> try_create_root_hub(NonnullLockRefPtr, DeviceSpeed); - static ErrorOr> try_create_from_device(Device const&); - - virtual ~Hub() override = default; - - ErrorOr enumerate_and_power_on_hub(); - - ErrorOr get_port_status(u8, HubStatus&); - ErrorOr clear_port_feature(u8, HubFeatureSelector); - ErrorOr set_port_feature(u8, HubFeatureSelector); - - void check_for_port_updates(); - -private: - // Root Hub constructor - Hub(NonnullLockRefPtr, DeviceSpeed, NonnullOwnPtr default_pipe); - - Hub(Device const&, NonnullOwnPtr default_pipe); - - USBHubDescriptor m_hub_descriptor {}; - - Device::List m_children; - - void remove_children_from_sysfs(); -}; - -} diff --git a/Kernel/Bus/USB/USBInterface.h b/Kernel/Bus/USB/USBInterface.h deleted file mode 100644 index 75992ff76c6..00000000000 --- a/Kernel/Bus/USB/USBInterface.h +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright (c) 2022, Jesse Buhagiar - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include - -namespace Kernel::USB { - -class USBConfiguration; - -class USBInterface final { -public: - USBInterface() = delete; - USBInterface(USBConfiguration const& configuration, USBInterfaceDescriptor const descriptor, Vector endpoint_descriptors) - : m_configuration(configuration) - , m_descriptor(descriptor) - , m_endpoint_descriptors(move(endpoint_descriptors)) - { - m_endpoint_descriptors.ensure_capacity(descriptor.number_of_endpoints); - } - - Vector const& endpoints() const { return m_endpoint_descriptors; } - - USBInterfaceDescriptor const& descriptor() const { return m_descriptor; } - USBConfiguration const& configuration() const { return m_configuration; } - -private: - USBConfiguration const& m_configuration; // Configuration that this interface belongs to - USBInterfaceDescriptor const m_descriptor; // Descriptor backing this interface - Vector m_endpoint_descriptors; // Endpoint descriptors for this interface (that we can use to open an endpoint) -}; - -} diff --git a/Kernel/Bus/USB/USBManagement.cpp b/Kernel/Bus/USB/USBManagement.cpp deleted file mode 100644 index 9047ca8bcf8..00000000000 --- a/Kernel/Bus/USB/USBManagement.cpp +++ /dev/null @@ -1,117 +0,0 @@ -/* - * Copyright (c) 2021, Luke Wilde - * Copyright (c) 2023, Jesse Buhagiar - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace Kernel::USB { - -static Singleton s_the; -READONLY_AFTER_INIT bool s_initialized_sys_fs_directory = false; - -UNMAP_AFTER_INIT USBManagement::USBManagement() -{ - enumerate_controllers(); -} - -UNMAP_AFTER_INIT void USBManagement::enumerate_controllers() -{ - if (kernel_command_line().disable_usb()) - return; - - MUST(PCI::enumerate([this](PCI::DeviceIdentifier const& device_identifier) { - if (device_identifier.class_code() != PCI::ClassID::SerialBus - || device_identifier.subclass_code() != PCI::SerialBus::SubclassID::USB) - return; - auto progif = static_cast(device_identifier.prog_if().value()); - using enum PCI::SerialBus::USBProgIf; - switch (progif) { - case UHCI: - if (kernel_command_line().disable_uhci_controller()) - return; - - if (auto uhci_controller_or_error = UHCIController::try_to_initialize(device_identifier); !uhci_controller_or_error.is_error()) - m_controllers.append(uhci_controller_or_error.release_value()); - - return; - case OHCI: - dmesgln("USBManagement: OHCI controller found at {} is not currently supported.", device_identifier.address()); - return; - case EHCI: - dmesgln("USBManagement: EHCI controller found at {} is currently not fully supported.", device_identifier.address()); - if (auto ehci_controller_or_error = EHCI::EHCIController::try_to_initialize(device_identifier); !ehci_controller_or_error.is_error()) - m_controllers.append(ehci_controller_or_error.release_value()); - return; - case xHCI: - dmesgln("USBManagement: xHCI controller found at {} is not currently supported.", device_identifier.address()); - return; - case None: - dmesgln("USBManagement: Non interface-able controller found at {} is not currently supported.", device_identifier.address()); - return; - case Device: - dmesgln("USBManagement: Direct attached device at {} is not currently supported.", device_identifier.address()); - return; - } - dmesgln("USBManagement: Unknown/unsupported controller at {} with programming interface {:#02x}", device_identifier.address(), device_identifier.prog_if().value()); - })); -} - -bool USBManagement::initialized() -{ - return s_the.is_initialized(); -} - -UNMAP_AFTER_INIT void USBManagement::initialize() -{ - if (!s_initialized_sys_fs_directory) { - SysFSUSBBusDirectory::initialize(); - s_initialized_sys_fs_directory = true; - } - - s_the.ensure_instance(); -} - -void USBManagement::register_driver(NonnullLockRefPtr driver) -{ - if (!initialized()) - return; - dbgln_if(USB_DEBUG, "Registering driver {}", driver->name()); - the().m_available_drivers.append(driver); -} - -LockRefPtr USBManagement::get_driver_by_name(StringView name) -{ - if (!initialized()) - return nullptr; - auto it = the().m_available_drivers.find_if([name](auto driver) { return driver->name() == name; }); - return it.is_end() ? nullptr : LockRefPtr { *it }; -} - -void USBManagement::unregister_driver(NonnullLockRefPtr driver) -{ - if (!initialized()) - return; - auto& the_instance = the(); - dbgln_if(USB_DEBUG, "Unregistering driver {}", driver->name()); - auto const& found_driver = the_instance.m_available_drivers.find(driver); - if (!found_driver.is_end()) - the_instance.m_available_drivers.remove(found_driver.index()); -} - -USBManagement& USBManagement::the() -{ - return *s_the; -} - -} diff --git a/Kernel/Bus/USB/USBManagement.h b/Kernel/Bus/USB/USBManagement.h deleted file mode 100644 index ff0c05115e8..00000000000 --- a/Kernel/Bus/USB/USBManagement.h +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright (c) 2021, Luke Wilde - * Copyright (c) 2023, Jesse Buhagiar - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include - -namespace Kernel::USB { - -class USBManagement { - -public: - USBManagement(); - static bool initialized(); - static void initialize(); - static USBManagement& the(); - - static void register_driver(NonnullLockRefPtr driver); - static LockRefPtr get_driver_by_name(StringView name); - static void unregister_driver(NonnullLockRefPtr driver); - - Vector>& available_drivers() { return m_available_drivers; } - -private: - void enumerate_controllers(); - - USBController::List m_controllers; - Vector> m_available_drivers; -}; - -} diff --git a/Kernel/Bus/USB/USBPipe.cpp b/Kernel/Bus/USB/USBPipe.cpp deleted file mode 100644 index c8997f37ca2..00000000000 --- a/Kernel/Bus/USB/USBPipe.cpp +++ /dev/null @@ -1,195 +0,0 @@ -/* - * Copyright (c) 2021, Jesse Buhagiar - * Copyright (c) 2022, blackcat - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include - -namespace Kernel::USB { - -Pipe::Pipe(USBController const& controller, Type type, Direction direction, u8 endpoint_address, u16 max_packet_size, i8 device_address, NonnullOwnPtr dma_buffer) - : m_controller(controller) - , m_type(type) - , m_direction(direction) - , m_device_address(device_address) - , m_endpoint_address(endpoint_address) - , m_max_packet_size(max_packet_size) - , m_data_toggle(false) - , m_dma_buffer(move(dma_buffer)) -{ -} - -ErrorOr> ControlPipe::create(USBController const& controller, u8 endpoint_address, u16 max_packet_size, i8 device_address, size_t buffer_size) -{ - auto dma_buffer = TRY(MM.allocate_dma_buffer_pages(TRY(Memory::page_round_up(buffer_size)), "USB device DMA buffer"sv, Memory::Region::Access::ReadWrite)); - return adopt_nonnull_own_or_enomem(new (nothrow) ControlPipe(controller, endpoint_address, max_packet_size, device_address, move(dma_buffer))); -} - -ControlPipe::ControlPipe(USBController const& controller, u8 endpoint_address, u16 max_packet_size, i8 device_address, NonnullOwnPtr dma_buffer) - : Pipe(controller, Type::Control, Direction::Bidirectional, endpoint_address, max_packet_size, device_address, move(dma_buffer)) -{ -} - -ErrorOr ControlPipe::submit_control_transfer(u8 request_type, u8 request, u16 value, u16 index, size_t length, void* data) -{ - VERIFY(length <= m_dma_buffer->size()); - - MutexLocker lock(m_dma_buffer_lock); - - USBRequestData usb_request; - - usb_request.request_type = request_type; - usb_request.request = request; - usb_request.value = value; - usb_request.index = index; - usb_request.length = length; - - auto transfer = TRY(Transfer::create(*this, length, *m_dma_buffer)); - transfer->set_setup_packet(usb_request); - - dbgln_if(USB_DEBUG, "ControlPipe: Transfer allocated @ {}", transfer->buffer_physical()); - auto transfer_length = TRY(m_controller->submit_control_transfer(*transfer)); - - // TODO: Check transfer for completion and copy data from transfer buffer into data - if (length > 0) - memcpy(reinterpret_cast(data), transfer->buffer().as_ptr() + sizeof(USBRequestData), length); - - dbgln_if(USB_DEBUG, "Pipe: Control Transfer complete!"); - return transfer_length; -} - -ErrorOr> BulkInPipe::create(USBController const& controller, u8 endpoint_address, u16 max_packet_size, i8 device_address, size_t buffer_size) -{ - VERIFY(buffer_size >= max_packet_size); - auto dma_buffer = TRY(MM.allocate_dma_buffer_pages(TRY(Memory::page_round_up(buffer_size)), "USB pipe DMA buffer"sv, Memory::Region::Access::ReadWrite)); - return adopt_nonnull_own_or_enomem(new (nothrow) BulkInPipe(controller, endpoint_address, max_packet_size, device_address, move(dma_buffer))); -} - -BulkInPipe::BulkInPipe(USBController const& controller, u8 endpoint_address, u16 max_packet_size, i8 device_address, NonnullOwnPtr dma_buffer) - : Pipe(controller, Pipe::Type::Bulk, Direction::In, endpoint_address, max_packet_size, device_address, move(dma_buffer)) -{ -} - -ErrorOr BulkInPipe::submit_bulk_in_transfer(size_t length, void* data) -{ - VERIFY(length <= m_dma_buffer->size()); - - MutexLocker lock(m_dma_buffer_lock); - - size_t transfer_length = 0; - - auto transfer = TRY(Transfer::create(*this, length, *m_dma_buffer)); - - dbgln_if(USB_DEBUG, "Pipe: Bulk in transfer allocated @ {}", transfer->buffer_physical()); - transfer_length = TRY(m_controller->submit_bulk_transfer(*transfer)); - memcpy(data, transfer->buffer().as_ptr(), min(length, transfer_length)); - dbgln_if(USB_DEBUG, "Pipe: Bulk in transfer complete!"); - - return transfer_length; -} - -ErrorOr BulkInPipe::submit_bulk_in_transfer(size_t length, UserOrKernelBuffer data) -{ - VERIFY(length <= m_dma_buffer->size()); - - MutexLocker lock(m_dma_buffer_lock); - - auto transfer = TRY(Transfer::create(*this, length, *m_dma_buffer)); - - dbgln_if(USB_DEBUG, "Pipe: Bulk in transfer allocated @ {}", transfer->buffer_physical()); - size_t transfer_length = TRY(m_controller->submit_bulk_transfer(*transfer)); - TRY(data.write(transfer->buffer().as_ptr(), min(length, transfer_length))); - dbgln_if(USB_DEBUG, "Pipe: Bulk in transfer complete!"); - - return transfer_length; -} - -ErrorOr> BulkOutPipe::create(USBController const& controller, u8 endpoint_address, u16 max_packet_size, i8 device_address, size_t buffer_size) -{ - VERIFY(buffer_size >= max_packet_size); - auto dma_buffer = TRY(MM.allocate_dma_buffer_pages(TRY(Memory::page_round_up(buffer_size)), "USB pipe DMA buffer"sv, Memory::Region::Access::ReadWrite)); - return adopt_nonnull_own_or_enomem(new (nothrow) BulkOutPipe(controller, endpoint_address, max_packet_size, device_address, move(dma_buffer))); -} - -BulkOutPipe::BulkOutPipe(USBController const& controller, u8 endpoint_address, u16 max_packet_size, i8 device_address, NonnullOwnPtr dma_buffer) - : Pipe(controller, Type::Bulk, Direction::Out, endpoint_address, max_packet_size, device_address, move(dma_buffer)) - -{ -} - -ErrorOr BulkOutPipe::submit_bulk_out_transfer(size_t length, void* data) -{ - VERIFY(length <= m_dma_buffer->size()); - - MutexLocker lock(m_dma_buffer_lock); - - size_t transfer_length = 0; - auto transfer = TRY(Transfer::create(*this, length, *m_dma_buffer)); - - TRY(transfer->write_buffer(length, data)); - dbgln_if(USB_DEBUG, "Pipe: Bulk out transfer allocated @ {}", transfer->buffer_physical()); - transfer_length = TRY(m_controller->submit_bulk_transfer(*transfer)); - dbgln_if(USB_DEBUG, "Pipe: Bulk out transfer complete!"); - - return transfer_length; -} - -ErrorOr BulkOutPipe::submit_bulk_out_transfer(size_t length, UserOrKernelBuffer data) -{ - VERIFY(length <= m_dma_buffer->size()); - - MutexLocker lock(m_dma_buffer_lock); - - auto transfer = TRY(Transfer::create(*this, length, *m_dma_buffer)); - - TRY(transfer->write_buffer(length, data)); - dbgln_if(USB_DEBUG, "Pipe: Bulk out transfer allocated @ {}", transfer->buffer_physical()); - size_t transfer_length = TRY(m_controller->submit_bulk_transfer(*transfer)); - dbgln_if(USB_DEBUG, "Pipe: Bulk out transfer complete!"); - - return transfer_length; -} - -ErrorOr> InterruptInPipe::create(USBController const& controller, u8 endpoint_address, u16 max_packet_size, i8 device_address, u16 poll_interval, size_t buffer_size) -{ - VERIFY(buffer_size >= max_packet_size); - auto dma_buffer = TRY(MM.allocate_dma_buffer_pages(TRY(Memory::page_round_up(buffer_size)), "USB pipe DMA buffer"sv, Memory::Region::Access::ReadWrite)); - return adopt_nonnull_own_or_enomem(new (nothrow) InterruptInPipe(controller, endpoint_address, max_packet_size, device_address, poll_interval, move(dma_buffer))); -} - -InterruptInPipe::InterruptInPipe(USBController const& controller, u8 endpoint_address, u16 max_packet_size, i8 device_address, u16 poll_interval, NonnullOwnPtr dma_buffer) - : Pipe(controller, Type::Interrupt, Direction::In, endpoint_address, max_packet_size, device_address, move(dma_buffer)) - , m_poll_interval(poll_interval) -{ -} - -ErrorOr> InterruptInPipe::submit_interrupt_in_transfer(size_t length, u16 ms_interval, USBAsyncCallback callback) -{ - VERIFY(length <= m_dma_buffer->size()); - - auto transfer = TRY(Transfer::create(*this, length, *m_dma_buffer, move(callback))); - dbgln_if(USB_DEBUG, "Pipe: Interrupt in transfer allocated @ {}", transfer->buffer_physical()); - TRY(m_controller->submit_async_interrupt_transfer(transfer, ms_interval)); - return transfer; -} - -ErrorOr> InterruptOutPipe::create(USBController const& controller, u8 endpoint_address, u16 max_packet_size, i8 device_address, u16 poll_interval, size_t buffer_size) -{ - VERIFY(buffer_size >= max_packet_size); - auto dma_buffer = TRY(MM.allocate_dma_buffer_pages(TRY(Memory::page_round_up(buffer_size)), "USB pipe DMA buffer"sv, Memory::Region::Access::ReadWrite)); - return adopt_nonnull_own_or_enomem(new (nothrow) InterruptOutPipe(controller, endpoint_address, max_packet_size, device_address, poll_interval, move(dma_buffer))); -} - -InterruptOutPipe::InterruptOutPipe(USBController const& controller, u8 endpoint_address, u16 max_packet_size, i8 device_address, u16 poll_interval, NonnullOwnPtr dma_buffer) - : Pipe(controller, Type::Interrupt, Direction::In, endpoint_address, max_packet_size, device_address, move(dma_buffer)) - , m_poll_interval(poll_interval) -{ -} - -} diff --git a/Kernel/Bus/USB/USBPipe.h b/Kernel/Bus/USB/USBPipe.h deleted file mode 100644 index e052cb644d4..00000000000 --- a/Kernel/Bus/USB/USBPipe.h +++ /dev/null @@ -1,153 +0,0 @@ -/* - * Copyright (c) 2021, Jesse Buhagiar - * Copyright (c) 2022, blackcat - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include -#include - -namespace Kernel::USB { - -class USBController; -class Transfer; - -using USBAsyncCallback = Function; - -// -// A pipe is the logical connection between a memory buffer on the PC (host) and -// an endpoint on the device. In this implementation, the data buffer the pipe connects -// to is the physical buffer created when a Transfer is allocated. -// -class Pipe { -public: - enum class Type : u8 { - Control = 0, - Isochronous = 1, - Bulk = 2, - Interrupt = 3 - }; - - enum class Direction : u8 { - Out = 0, - In = 1, - Bidirectional = 2 - }; - - enum class DeviceSpeed : u8 { - LowSpeed, - FullSpeed - }; - - Type type() const { return m_type; } - Direction direction() const { return m_direction; } - DeviceSpeed device_speed() const { return m_speed; } - - i8 device_address() const { return m_device_address; } - u8 endpoint_address() const { return m_endpoint_address; } - u16 max_packet_size() const { return m_max_packet_size; } - bool data_toggle() const { return m_data_toggle; } - - void set_max_packet_size(u16 max_size) { m_max_packet_size = max_size; } - void set_toggle(bool toggle) { m_data_toggle = toggle; } - void set_device_address(i8 addr) { m_device_address = addr; } - -protected: - friend class Device; - - Pipe(USBController const& controller, Type type, Direction direction, u8 endpoint_address, u16 max_packet_size, i8 device_address, NonnullOwnPtr dma_buffer); - - NonnullLockRefPtr m_controller; - - Type m_type; - Direction m_direction; - DeviceSpeed m_speed; - - i8 m_device_address { 0 }; // Device address of this pipe - u8 m_endpoint_address { 0 }; // Corresponding endpoint address for this pipe - u16 m_max_packet_size { 0 }; // Max packet size for this pipe - bool m_data_toggle { false }; // Data toggle for stuffing bit - - Mutex m_dma_buffer_lock { "USB pipe mutex"sv }; - - NonnullOwnPtr m_dma_buffer; -}; - -class ControlPipe : public Pipe { -public: - static ErrorOr> create(USBController const& controller, u8 endpoint_address, u16 max_packet_size, i8 device_address, size_t buffer_size = PAGE_SIZE); - - ErrorOr submit_control_transfer(u8 request_type, u8 request, u16 value, u16 index, size_t length, void* data); - -private: - ControlPipe(USBController const& controller, u8 endpoint_address, u16 max_packet_size, i8 device_address, NonnullOwnPtr dma_buffer); -}; - -class BulkInPipe : public Pipe { -public: - static ErrorOr> create(USBController const& controller, u8 endpoint_address, u16 max_packet_size, i8 device_address, size_t buffer_size = PAGE_SIZE); - - ErrorOr submit_bulk_in_transfer(size_t length, void* data); - ErrorOr submit_bulk_in_transfer(size_t length, UserOrKernelBuffer data); - -private: - BulkInPipe(USBController const& controller, u8 endpoint_address, u16 max_packet_size, i8 device_address, NonnullOwnPtr dma_buffer); -}; - -class BulkOutPipe : public Pipe { -public: - static ErrorOr> create(USBController const& controller, u8 endpoint_address, u16 max_packet_size, i8 device_address, size_t buffer_size = PAGE_SIZE); - - ErrorOr submit_bulk_out_transfer(size_t length, void* data); - ErrorOr submit_bulk_out_transfer(size_t length, UserOrKernelBuffer data); - -private: - BulkOutPipe(USBController const& controller, u8 endpoint_address, u16 max_packet_size, i8 device_address, NonnullOwnPtr dma_buffer); -}; - -class InterruptInPipe : public Pipe { -public: - static ErrorOr> create(USBController const& controller, u8 endpoint_address, u16 max_packet_size, i8 device_address, u16 poll_interval, size_t buffer_size = PAGE_SIZE); - - ErrorOr> submit_interrupt_in_transfer(size_t length, u16 ms_interval, USBAsyncCallback callback); - - u16 poll_interval() const { return m_poll_interval; } - -private: - InterruptInPipe(USBController const& controller, u8 endpoint_address, u16 max_packet_size, i8 device_address, u16 poll_interval, NonnullOwnPtr dma_pool); - - u16 m_poll_interval; -}; - -class InterruptOutPipe : public Pipe { -public: - static ErrorOr> create(USBController const& controller, u8 endpoint_address, u16 max_packet_size, i8 device_address, u16 poll_interval, size_t buffer_size = PAGE_SIZE); - - u16 poll_interval() const { return m_poll_interval; } - -private: - InterruptOutPipe(USBController const& controller, u8 endpoint_address, u16 max_packet_size, i8 device_address, u16 poll_interval, NonnullOwnPtr dma_pool); - - u16 m_poll_interval; -}; - -class IsochronousInPipe : public Pipe { - // TODO -public: -private: -}; - -class IsochronousOutPipe : public Pipe { - // TODO -public: -private: -}; - -} diff --git a/Kernel/Bus/USB/USBRequest.h b/Kernel/Bus/USB/USBRequest.h deleted file mode 100644 index 10a67b62350..00000000000 --- a/Kernel/Bus/USB/USBRequest.h +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright (c) 2021, Jesse Buhagiar - * Copyright (c) 2021, Luke Wilde - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include - -namespace Kernel::USB { - -// -// bmRequestType fields -// -// As per Section 9.3 of the USB 2.0 Specification. -// Note that while some of these values are zero, there are here for convenience. -// This is because it makes reading the request type easier to read when constructing a USB request. -// -static constexpr u8 USB_REQUEST_TRANSFER_DIRECTION_DEVICE_TO_HOST = 0x80; -static constexpr u8 USB_REQUEST_TRANSFER_DIRECTION_HOST_TO_DEVICE = 0x00; -static constexpr u8 USB_REQUEST_TYPE_STANDARD = 0x00; -static constexpr u8 USB_REQUEST_TYPE_CLASS = 0x20; -static constexpr u8 USB_REQUEST_TYPE_VENDOR = 0x40; -static constexpr u8 USB_REQUEST_RECIPIENT_DEVICE = 0x00; -static constexpr u8 USB_REQUEST_RECIPIENT_INTERFACE = 0x01; -static constexpr u8 USB_REQUEST_RECIPIENT_ENDPOINT = 0x02; -static constexpr u8 USB_REQUEST_RECIPIENT_OTHER = 0x03; - -// -// Standard USB request types -// -// These are found in Section 9.4 of the USB Spec -// -static constexpr u8 USB_REQUEST_GET_STATUS = 0x00; -static constexpr u8 USB_REQUEST_CLEAR_FEATURE = 0x01; -static constexpr u8 USB_REQUEST_SET_FEATURE = 0x03; -static constexpr u8 USB_REQUEST_SET_ADDRESS = 0x05; -static constexpr u8 USB_REQUEST_GET_DESCRIPTOR = 0x06; -static constexpr u8 USB_REQUEST_SET_DESCRIPTOR = 0x07; -static constexpr u8 USB_REQUEST_GET_CONFIGURATION = 0x08; -static constexpr u8 USB_REQUEST_SET_CONFIGURATION = 0x09; - -} diff --git a/Kernel/Bus/USB/USBTransfer.cpp b/Kernel/Bus/USB/USBTransfer.cpp deleted file mode 100644 index 0509971275a..00000000000 --- a/Kernel/Bus/USB/USBTransfer.cpp +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright (c) 2021, Jesse Buhagiar - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include - -namespace Kernel::USB { - -ErrorOr> Transfer::create(Pipe& pipe, u16 length, Memory::Region& dma_buffer, USBAsyncCallback callback) -{ - return adopt_nonnull_lock_ref_or_enomem(new (nothrow) Transfer(pipe, length, dma_buffer, move(callback))); -} - -Transfer::Transfer(Pipe& pipe, u16 len, Memory::Region& dma_buffer, USBAsyncCallback callback) - : m_pipe(pipe) - , m_dma_buffer(dma_buffer) - , m_transfer_data_size(len) - , m_callback(move(callback)) -{ -} - -Transfer::~Transfer() = default; - -void Transfer::set_setup_packet(USBRequestData const& request) -{ - // Kind of a nasty hack... Because the kernel isn't in the business - // of handing out physical pointers that we can directly write to, - // we set the address of the setup packet to be the first 8 bytes of - // the data buffer, which we then set to the physical address. - auto* request_data = reinterpret_cast(buffer().as_ptr()); - - request_data->request_type = request.request_type; - request_data->request = request.request; - request_data->value = request.value; - request_data->index = request.index; - request_data->length = request.length; - - m_request = request; -} - -ErrorOr Transfer::write_buffer(u16 len, void* data) -{ - VERIFY(len <= m_dma_buffer.size()); - m_transfer_data_size = len; - memcpy(buffer().as_ptr(), data, len); - - return {}; -} - -ErrorOr Transfer::write_buffer(u16 len, UserOrKernelBuffer data) -{ - VERIFY(len <= m_dma_buffer.size()); - m_transfer_data_size = len; - return data.read(buffer().as_ptr(), len); -} - -void Transfer::invoke_async_callback() -{ - if (transfer_data_size() == 0) - return; - if (m_callback) - m_callback(this); -} - -} diff --git a/Kernel/Bus/USB/USBTransfer.h b/Kernel/Bus/USB/USBTransfer.h deleted file mode 100644 index 15234e5b1ad..00000000000 --- a/Kernel/Bus/USB/USBTransfer.h +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright (c) 2021, Jesse Buhagiar - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include -#include -#include -#include - -// TODO: Callback stuff in this class please! -namespace Kernel::USB { - -class Transfer final : public AtomicRefCounted { -public: - static ErrorOr> create(Pipe&, u16 length, Memory::Region& dma_buffer, USBAsyncCallback callback = nullptr); - - Transfer() = delete; - ~Transfer(); - - void set_setup_packet(USBRequestData const& request); - void set_complete() { m_complete = true; } - void set_error_occurred() { m_error_occurred = true; } - - ErrorOr write_buffer(u16 len, void* data); - ErrorOr write_buffer(u16 len, UserOrKernelBuffer data); - - // `const` here makes sure we don't blow up by writing to a physical address - USBRequestData const& request() const { return m_request; } - Pipe const& pipe() const { return m_pipe; } - Pipe& pipe() { return m_pipe; } - VirtualAddress buffer() const { return m_dma_buffer.vaddr(); } - PhysicalAddress buffer_physical() const { return m_dma_buffer.physical_page(0)->paddr(); } - u16 transfer_data_size() const { return m_transfer_data_size; } - bool complete() const { return m_complete; } - bool error_occurred() const { return m_error_occurred; } - - void invoke_async_callback(); - -private: - Transfer(Pipe& pipe, u16 len, Memory::Region& dma_buffer, USBAsyncCallback callback); - Pipe& m_pipe; // Pipe that initiated this transfer - Memory::Region& m_dma_buffer; // DMA buffer - USBRequestData m_request; // USB request - u16 m_transfer_data_size { 0 }; // Size of the transfer's data stage - bool m_complete { false }; // Has this transfer been completed? - bool m_error_occurred { false }; // Did an error occur during this transfer? - USBAsyncCallback m_callback { nullptr }; -}; - -} diff --git a/Kernel/Bus/VirtIO/Definitions.h b/Kernel/Bus/VirtIO/Definitions.h deleted file mode 100644 index 0e3322a8a5b..00000000000 --- a/Kernel/Bus/VirtIO/Definitions.h +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Copyright (c) 2021, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include - -namespace Kernel::VirtIO { - -#define REG_DEVICE_FEATURES 0x0 -#define REG_GUEST_FEATURES 0x4 -#define REG_QUEUE_ADDRESS 0x8 -#define REG_QUEUE_SIZE 0xc -#define REG_QUEUE_SELECT 0xe -#define REG_QUEUE_NOTIFY 0x10 -#define REG_DEVICE_STATUS 0x12 -#define REG_ISR_STATUS 0x13 - -#define DEVICE_STATUS_ACKNOWLEDGE (1 << 0) -#define DEVICE_STATUS_DRIVER (1 << 1) -#define DEVICE_STATUS_DRIVER_OK (1 << 2) -#define DEVICE_STATUS_FEATURES_OK (1 << 3) -#define DEVICE_STATUS_DEVICE_NEEDS_RESET (1 << 6) -#define DEVICE_STATUS_FAILED (1 << 7) - -#define VIRTIO_F_INDIRECT_DESC ((u64)1 << 28) -#define VIRTIO_F_VERSION_1 ((u64)1 << 32) -#define VIRTIO_F_RING_PACKED ((u64)1 << 34) -#define VIRTIO_F_IN_ORDER ((u64)1 << 35) - -#define VIRTIO_PCI_CAP_COMMON_CFG 1 -#define VIRTIO_PCI_CAP_NOTIFY_CFG 2 -#define VIRTIO_PCI_CAP_ISR_CFG 3 -#define VIRTIO_PCI_CAP_DEVICE_CFG 4 -#define VIRTIO_PCI_CAP_PCI_CFG 5 - -// virtio_pci_common_cfg -#define COMMON_CFG_DEVICE_FEATURE_SELECT 0x0 -#define COMMON_CFG_DEVICE_FEATURE 0x4 -#define COMMON_CFG_DRIVER_FEATURE_SELECT 0x8 -#define COMMON_CFG_DRIVER_FEATURE 0xc -#define COMMON_CFG_MSIX_CONFIG 0x10 -#define COMMON_CFG_NUM_QUEUES 0x12 -#define COMMON_CFG_DEVICE_STATUS 0x14 -#define COMMON_CFG_CONFIG_GENERATION 0x15 -#define COMMON_CFG_QUEUE_SELECT 0x16 -#define COMMON_CFG_QUEUE_SIZE 0x18 -#define COMMON_CFG_QUEUE_MSIX_VECTOR 0x1a -#define COMMON_CFG_QUEUE_ENABLE 0x1c -#define COMMON_CFG_QUEUE_NOTIFY_OFF 0x1e -#define COMMON_CFG_QUEUE_DESC 0x20 -#define COMMON_CFG_QUEUE_DRIVER 0x28 -#define COMMON_CFG_QUEUE_DEVICE 0x30 - -#define QUEUE_INTERRUPT 0x1 -#define DEVICE_CONFIG_INTERRUPT 0x2 - -enum class ConfigurationType : u8 { - Common = 1, - Notify = 2, - ISR = 3, - Device = 4, - PCICapabilitiesAccess = 5 -}; - -struct Configuration { - ConfigurationType cfg_type; - u8 resource_index; // NOTE: For PCI devices, this is the BAR index - u32 offset; - u32 length; -}; - -} diff --git a/Kernel/Bus/VirtIO/Device.cpp b/Kernel/Bus/VirtIO/Device.cpp deleted file mode 100644 index 01cf7b283a8..00000000000 --- a/Kernel/Bus/VirtIO/Device.cpp +++ /dev/null @@ -1,169 +0,0 @@ -/* - * Copyright (c) 2021, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include - -namespace Kernel::VirtIO { - -UNMAP_AFTER_INIT ErrorOr Device::initialize_virtio_resources() -{ - TRY(m_transport_entity->locate_configurations_and_resources({}, *this)); - // NOTE: We enable interrupts at least after the m_register_bases[0] ptr is - // assigned with an IOWindow, to ensure that in case of getting an interrupt - // we can access registers from that IO window range. - m_transport_entity->enable_interrupts({}); - - // NOTE: Status bits should be set to 0 to keep them in sync, because - // we reset the device shortly afterwards. - m_status = 0; - m_transport_entity->reset_device({}); - set_status_bit(DEVICE_STATUS_ACKNOWLEDGE); - set_status_bit(DEVICE_STATUS_DRIVER); - return {}; -} - -UNMAP_AFTER_INIT VirtIO::Device::Device(NonnullOwnPtr transport_entity) - : m_class_name(transport_entity->determine_device_class_name()) - , m_transport_entity(move(transport_entity)) -{ -} - -void Device::set_status_bit(u8 status_bit) -{ - m_status |= status_bit; - m_transport_entity->set_status_bits({}, m_status); -} - -ErrorOr Device::accept_device_features(u64 device_features, u64 accepted_features) -{ - VERIFY(!m_did_accept_features.was_set()); - m_did_accept_features.set(); - - if (is_feature_set(device_features, VIRTIO_F_VERSION_1)) { - accepted_features |= VIRTIO_F_VERSION_1; // let the device know were not a legacy driver - } - - if (is_feature_set(device_features, VIRTIO_F_RING_PACKED)) { - dbgln_if(VIRTIO_DEBUG, "{}: packed queues not yet supported", m_class_name); - accepted_features &= ~(VIRTIO_F_RING_PACKED); - } - - // TODO: implement indirect descriptors to allow queue_size buffers instead of buffers totalling (PAGE_SIZE * queue_size) bytes - if (is_feature_set(device_features, VIRTIO_F_INDIRECT_DESC)) { - // accepted_features |= VIRTIO_F_INDIRECT_DESC; - } - - if (is_feature_set(device_features, VIRTIO_F_IN_ORDER)) { - accepted_features |= VIRTIO_F_IN_ORDER; - } - - dbgln_if(VIRTIO_DEBUG, "{}: Device features: {}", m_class_name, device_features); - dbgln_if(VIRTIO_DEBUG, "{}: Accepted features: {}", m_class_name, accepted_features); - - m_transport_entity->accept_device_features({}, accepted_features); - set_status_bit(DEVICE_STATUS_FEATURES_OK); - m_status = m_transport_entity->read_status_bits(); - if (!(m_status & DEVICE_STATUS_FEATURES_OK)) { - set_status_bit(DEVICE_STATUS_FAILED); - dbgln("{}: Features not accepted by host!", m_class_name); - return Error::from_errno(EIO); - } - - m_accepted_features = accepted_features; - dbgln_if(VIRTIO_DEBUG, "{}: Features accepted by host", m_class_name); - return {}; -} - -ErrorOr Device::setup_queue(u16 queue_index) -{ - auto queue = TRY(m_transport_entity->setup_queue({}, queue_index)); - dbgln_if(VIRTIO_DEBUG, "{}: Queue[{}] configured with size: {}", m_class_name, queue_index, queue->size()); - - TRY(m_queues.try_append(move(queue))); - return {}; -} - -ErrorOr Device::setup_queues(u16 requested_queue_count) -{ - VERIFY(!m_did_setup_queues.was_set()); - m_did_setup_queues.set(); - - auto* common_cfg = TRY(m_transport_entity->get_config(ConfigurationType::Common)); - if (common_cfg) { - auto maximum_queue_count = m_transport_entity->config_read16(*common_cfg, COMMON_CFG_NUM_QUEUES); - if (requested_queue_count == 0) { - m_queue_count = maximum_queue_count; - } else if (requested_queue_count > maximum_queue_count) { - dbgln("{}: {} queues requested but only {} available!", m_class_name, m_queue_count, maximum_queue_count); - return Error::from_errno(ENXIO); - } else { - m_queue_count = requested_queue_count; - } - } else { - m_queue_count = requested_queue_count; - dbgln("{}: device's available queue count could not be determined!", m_class_name); - } - - dbgln_if(VIRTIO_DEBUG, "{}: Setting up {} queues", m_class_name, m_queue_count); - for (u16 i = 0; i < m_queue_count; i++) - TRY(setup_queue(i)); - - // NOTE: Queues can only be activated *after* all others queues were also configured - for (u16 i = 0; i < m_queue_count; i++) - TRY(m_transport_entity->activate_queue({}, i)); - return {}; -} - -void Device::finish_init() -{ - VERIFY(m_did_accept_features.was_set()); // ensure features were negotiated - VERIFY(m_did_setup_queues.was_set()); // ensure queues were set-up - VERIFY(!(m_status & DEVICE_STATUS_DRIVER_OK)); // ensure we didn't already finish the initialization - - set_status_bit(DEVICE_STATUS_DRIVER_OK); - dbgln_if(VIRTIO_DEBUG, "{}: Finished initialization", m_class_name); -} - -bool Device::handle_irq(Badge) -{ - u8 isr_type = m_transport_entity->isr_status(); - if ((isr_type & (QUEUE_INTERRUPT | DEVICE_CONFIG_INTERRUPT)) == 0) { - dbgln_if(VIRTIO_DEBUG, "{}: Handling interrupt with unknown type: {}", class_name(), isr_type); - return false; - } - if (isr_type & DEVICE_CONFIG_INTERRUPT) { - dbgln_if(VIRTIO_DEBUG, "{}: VirtIO Device config interrupt!", class_name()); - if (handle_device_config_change().is_error()) { - set_status_bit(DEVICE_STATUS_FAILED); - dbgln("{}: Failed to handle device config change!", class_name()); - } - } - if (isr_type & QUEUE_INTERRUPT) { - dbgln_if(VIRTIO_DEBUG, "{}: VirtIO Queue interrupt!", class_name()); - for (size_t i = 0; i < m_queues.size(); i++) { - if (get_queue(i).new_data_available()) { - handle_queue_update(i); - return true; - } - } - dbgln_if(VIRTIO_DEBUG, "{}: Got queue interrupt but all queues are up to date!", class_name()); - } - return true; -} - -void Device::supply_chain_and_notify(u16 queue_index, QueueChain& chain) -{ - auto& queue = get_queue(queue_index); - VERIFY(&chain.queue() == &queue); - VERIFY(queue.lock().is_locked()); - chain.submit_to_queue(); - auto descriptor = TransportEntity::NotifyQueueDescriptor { queue_index, get_queue(queue_index).notify_offset() }; - if (queue.should_notify()) - m_transport_entity->notify_queue({}, descriptor); -} - -} diff --git a/Kernel/Bus/VirtIO/Device.h b/Kernel/Bus/VirtIO/Device.h deleted file mode 100644 index 675106a7f94..00000000000 --- a/Kernel/Bus/VirtIO/Device.h +++ /dev/null @@ -1,98 +0,0 @@ -/* - * Copyright (c) 2021, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace Kernel::VirtIO { - -class Device { -public: - virtual ~Device() = default; - - virtual ErrorOr initialize_virtio_resources(); - - bool handle_irq(Badge); - -protected: - virtual StringView class_name() const { return "VirtIO::Device"sv; } - - explicit Device(NonnullOwnPtr); - - void mask_status_bits(u8 status_mask); - void set_status_bit(u8); - ErrorOr setup_queues(u16 requested_queue_count = 0); - void finish_init(); - - Queue& get_queue(u16 queue_index) - { - VERIFY(queue_index < m_queue_count); - return *m_queues[queue_index]; - } - - Queue const& get_queue(u16 queue_index) const - { - VERIFY(queue_index < m_queue_count); - return *m_queues[queue_index]; - } - - template - ErrorOr negotiate_features(F f) - { - u64 device_features = m_transport_entity->get_device_features(); - u64 accept_features = f(device_features); - VERIFY(!(~device_features & accept_features)); - return accept_device_features(device_features, accept_features); - } - - static bool is_feature_set(u64 feature_set, u64 test_feature) - { - // features can have more than one bit - return (feature_set & test_feature) == test_feature; - } - bool is_feature_accepted(u64 feature) const - { - VERIFY(m_did_accept_features.was_set()); - return is_feature_set(m_accepted_features, feature); - } - - void supply_chain_and_notify(u16 queue_index, QueueChain& chain); - - virtual ErrorOr handle_device_config_change() = 0; - virtual void handle_queue_update(u16 queue_index) = 0; - - TransportEntity& transport_entity() { return *m_transport_entity; } - -private: - ErrorOr accept_device_features(u64 device_features, u64 accepted_features); - - ErrorOr setup_queue(u16 queue_index); - ErrorOr activate_queue(u16 queue_index); - void notify_queue(u16 queue_index); - - Vector> m_queues; - - StringView const m_class_name; - - u16 m_queue_count { 0 }; - u8 m_status { 0 }; - u64 m_accepted_features { 0 }; - SetOnce m_did_accept_features; - SetOnce m_did_setup_queues; - - NonnullOwnPtr const m_transport_entity; -}; -} diff --git a/Kernel/Bus/VirtIO/Queue.cpp b/Kernel/Bus/VirtIO/Queue.cpp deleted file mode 100644 index 9f18278906b..00000000000 --- a/Kernel/Bus/VirtIO/Queue.cpp +++ /dev/null @@ -1,204 +0,0 @@ -/* - * Copyright (c) 2021, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include - -namespace Kernel::VirtIO { - -ErrorOr> Queue::try_create(u16 queue_size, u16 notify_offset) -{ - size_t size_of_descriptors = sizeof(QueueDescriptor) * queue_size; - size_t size_of_driver = sizeof(QueueDriver) + queue_size * sizeof(u16); - size_t size_of_device = sizeof(QueueDevice) + queue_size * sizeof(QueueDeviceItem); - auto queue_region_size = TRY(Memory::page_round_up(size_of_descriptors + size_of_driver + size_of_device)); - OwnPtr queue_region; - if (queue_region_size <= PAGE_SIZE) - queue_region = TRY(MM.allocate_kernel_region(queue_region_size, "VirtIO Queue"sv, Memory::Region::Access::ReadWrite)); - else - queue_region = TRY(MM.allocate_contiguous_kernel_region(queue_region_size, "VirtIO Queue"sv, Memory::Region::Access::ReadWrite)); - return adopt_nonnull_own_or_enomem(new (nothrow) Queue(queue_region.release_nonnull(), queue_size, notify_offset)); -} - -Queue::Queue(NonnullOwnPtr queue_region, u16 queue_size, u16 notify_offset) - : m_queue_size(queue_size) - , m_notify_offset(notify_offset) - , m_free_buffers(queue_size) - , m_queue_region(move(queue_region)) -{ - size_t size_of_descriptors = sizeof(QueueDescriptor) * queue_size; - size_t size_of_driver = sizeof(QueueDriver) + queue_size * sizeof(u16); - // TODO: ensure alignment!!! - u8* ptr = m_queue_region->vaddr().as_ptr(); - memset(ptr, 0, m_queue_region->size()); - m_descriptors = reinterpret_cast(ptr); - m_driver = reinterpret_cast(ptr + size_of_descriptors); - m_device = reinterpret_cast(ptr + size_of_descriptors + size_of_driver); - - for (auto i = 0; i + 1 < queue_size; i++) - m_descriptors[i].next = i + 1; // link all the descriptors in a line - - enable_interrupts(); -} - -Queue::~Queue() = default; - -void Queue::enable_interrupts() -{ - SpinlockLocker lock(m_lock); - m_driver->flags = 0; -} - -void Queue::disable_interrupts() -{ - SpinlockLocker lock(m_lock); - m_driver->flags = 1; -} - -bool Queue::new_data_available() const -{ - auto const index = AK::atomic_load(&m_device->index, AK::MemoryOrder::memory_order_relaxed); - auto const used_tail = AK::atomic_load(&m_used_tail, AK::MemoryOrder::memory_order_relaxed); - return index != used_tail; -} - -QueueChain Queue::pop_used_buffer_chain(size_t& used) -{ - VERIFY(m_lock.is_locked()); - if (!new_data_available()) { - used = 0; - return QueueChain(*this); - } - - full_memory_barrier(); - - // Determine used length - used = m_device->rings[m_used_tail % m_queue_size].length; - - // Determine start, end and number of nodes in chain - auto descriptor_index = m_device->rings[m_used_tail % m_queue_size].index; - size_t length_of_chain = 1; - auto last_index = descriptor_index; - while (m_descriptors[last_index].flags & VIRTQ_DESC_F_NEXT) { - ++length_of_chain; - last_index = m_descriptors[last_index].next; - } - - // We are now done with this buffer chain - m_used_tail++; - - return QueueChain(*this, descriptor_index, last_index, length_of_chain); -} - -void Queue::discard_used_buffers() -{ - VERIFY(m_lock.is_locked()); - size_t used; - for (auto buffer = pop_used_buffer_chain(used); !buffer.is_empty(); buffer = pop_used_buffer_chain(used)) { - buffer.release_buffer_slots_to_queue(); - } -} - -void Queue::reclaim_buffer_chain(u16 chain_start_index, u16 chain_end_index, size_t length_of_chain) -{ - VERIFY(m_lock.is_locked()); - m_descriptors[chain_end_index].next = m_free_head; - m_free_head = chain_start_index; - m_free_buffers += length_of_chain; -} - -bool Queue::has_free_slots() const -{ - auto const free_buffers = AK::atomic_load(&m_free_buffers, AK::MemoryOrder::memory_order_relaxed); - return free_buffers > 0; -} - -Optional Queue::take_free_slot() -{ - VERIFY(m_lock.is_locked()); - if (has_free_slots()) { - auto descriptor_index = m_free_head; - m_free_head = m_descriptors[descriptor_index].next; - --m_free_buffers; - return descriptor_index; - } - - return {}; -} - -bool Queue::should_notify() const -{ - VERIFY(m_lock.is_locked()); - auto device_flags = m_device->flags; - return !(device_flags & VIRTQ_USED_F_NO_NOTIFY); -} - -bool QueueChain::add_buffer_to_chain(PhysicalAddress buffer_start, size_t buffer_length, BufferType buffer_type) -{ - VERIFY(m_queue.lock().is_locked()); - - // Ensure that no readable pages will be inserted after a writable one, as required by the VirtIO spec - VERIFY(buffer_type == BufferType::DeviceWritable || !m_chain_has_writable_pages); - m_chain_has_writable_pages |= (buffer_type == BufferType::DeviceWritable); - - // Take a free slot from the queue - auto descriptor_index = m_queue.take_free_slot(); - if (!descriptor_index.has_value()) - return false; - - if (!m_start_of_chain_index.has_value()) { - // Set start of chain if it hasn't been set - m_start_of_chain_index = descriptor_index.value(); - } else { - // Link from previous element in QueueChain - m_queue.m_descriptors[m_end_of_chain_index.value()].flags |= VIRTQ_DESC_F_NEXT; - m_queue.m_descriptors[m_end_of_chain_index.value()].next = descriptor_index.value(); - } - - // Update end of chain - m_end_of_chain_index = descriptor_index.value(); - ++m_chain_length; - - // Populate buffer info - VERIFY(buffer_length <= NumericLimits::max()); - m_queue.m_descriptors[descriptor_index.value()].address = static_cast(buffer_start.get()); - m_queue.m_descriptors[descriptor_index.value()].flags = static_cast(buffer_type); - m_queue.m_descriptors[descriptor_index.value()].length = static_cast(buffer_length); - - return true; -} - -void QueueChain::submit_to_queue() -{ - VERIFY(m_queue.lock().is_locked()); - VERIFY(m_start_of_chain_index.has_value()); - - auto next_index = m_queue.m_driver_index_shadow % m_queue.m_queue_size; - m_queue.m_driver->rings[next_index] = m_start_of_chain_index.value(); - m_queue.m_driver_index_shadow++; - full_memory_barrier(); - m_queue.m_driver->index = m_queue.m_driver_index_shadow; - - // Reset internal chain state - m_start_of_chain_index = m_end_of_chain_index = {}; - m_chain_has_writable_pages = false; - m_chain_length = 0; -} - -void QueueChain::release_buffer_slots_to_queue() -{ - VERIFY(m_queue.lock().is_locked()); - if (m_start_of_chain_index.has_value()) { - // Add the currently stored chain back to the queue's free pool - m_queue.reclaim_buffer_chain(m_start_of_chain_index.value(), m_end_of_chain_index.value(), m_chain_length); - // Reset internal chain state - m_start_of_chain_index = m_end_of_chain_index = {}; - m_chain_has_writable_pages = false; - m_chain_length = 0; - } -} - -} diff --git a/Kernel/Bus/VirtIO/Queue.h b/Kernel/Bus/VirtIO/Queue.h deleted file mode 100644 index 457f9fee445..00000000000 --- a/Kernel/Bus/VirtIO/Queue.h +++ /dev/null @@ -1,190 +0,0 @@ -/* - * Copyright (c) 2021, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include - -namespace Kernel::VirtIO { - -class Device; -class QueueChain; - -#define VIRTQ_DESC_F_NEXT 1 -#define VIRTQ_DESC_F_INDIRECT 4 - -#define VIRTQ_AVAIL_F_NO_INTERRUPT 1 -#define VIRTQ_USED_F_NO_NOTIFY 1 - -enum class BufferType { - DeviceReadable = 0, - DeviceWritable = 2 -}; - -class Queue { -public: - static ErrorOr> try_create(u16 queue_size, u16 notify_offset); - - ~Queue(); - - u16 notify_offset() const { return m_notify_offset; } - - void enable_interrupts(); - void disable_interrupts(); - - PhysicalAddress descriptor_area() const { return to_physical(m_descriptors); } - PhysicalAddress driver_area() const { return to_physical(m_driver); } - PhysicalAddress device_area() const { return to_physical(m_device); } - - bool new_data_available() const; - bool has_free_slots() const; - Optional take_free_slot(); - QueueChain pop_used_buffer_chain(size_t& used); - void discard_used_buffers(); - - Spinlock& lock() { return m_lock; } - - bool should_notify() const; - - u16 size() const { return m_queue_size; } - -private: - Queue(NonnullOwnPtr queue_region, u16 queue_size, u16 notify_offset); - - void reclaim_buffer_chain(u16 chain_start_index, u16 chain_end_index, size_t length_of_chain); - - PhysicalAddress to_physical(void const* ptr) const - { - auto offset = FlatPtr(ptr) - m_queue_region->vaddr().get(); - return m_queue_region->physical_page(0)->paddr().offset(offset); - } - struct [[gnu::packed]] QueueDescriptor { - u64 address; - u32 length; - u16 flags; - u16 next; - }; - - struct [[gnu::packed]] QueueDriver { - u16 flags; - u16 index; - u16 rings[]; - }; - - struct [[gnu::packed]] QueueDeviceItem { - u32 index; - u32 length; - }; - - struct [[gnu::packed]] QueueDevice { - u16 flags; - u16 index; - QueueDeviceItem rings[]; - }; - - u16 const m_queue_size; - u16 const m_notify_offset; - u16 m_free_buffers; - u16 m_free_head { 0 }; - u16 m_used_tail { 0 }; - u16 m_driver_index_shadow { 0 }; - - QueueDescriptor* m_descriptors { nullptr }; - QueueDriver* m_driver { nullptr }; - QueueDevice* m_device { nullptr }; - NonnullOwnPtr m_queue_region; - Spinlock m_lock {}; - - friend class QueueChain; -}; - -class QueueChain { -public: - QueueChain(Queue& queue) - : m_queue(queue) - { - } - - QueueChain(Queue& queue, u16 start_index, u16 end_index, size_t chain_length) - : m_queue(queue) - , m_start_of_chain_index(start_index) - , m_end_of_chain_index(end_index) - , m_chain_length(chain_length) - { - } - - QueueChain(QueueChain&& other) - : m_queue(other.m_queue) - , m_start_of_chain_index(move(other.m_start_of_chain_index)) - , m_end_of_chain_index(move(other.m_end_of_chain_index)) - , m_chain_length(other.m_chain_length) - , m_chain_has_writable_pages(other.m_chain_has_writable_pages) - { - other.m_start_of_chain_index = {}; - other.m_end_of_chain_index = {}; - other.m_chain_length = 0; - other.m_chain_has_writable_pages = false; - } - - QueueChain& operator=(QueueChain&& other) - { - VERIFY(&m_queue == &other.m_queue); - ensure_chain_is_empty(); - m_start_of_chain_index = other.m_start_of_chain_index; - m_end_of_chain_index = other.m_end_of_chain_index; - m_chain_length = other.m_chain_length; - m_chain_has_writable_pages = other.m_chain_has_writable_pages; - other.m_start_of_chain_index = {}; - other.m_end_of_chain_index = {}; - other.m_chain_length = 0; - other.m_chain_has_writable_pages = false; - return *this; - } - - ~QueueChain() - { - ensure_chain_is_empty(); - } - - [[nodiscard]] Queue& queue() const { return m_queue; } - [[nodiscard]] bool is_empty() const { return m_chain_length == 0; } - [[nodiscard]] size_t length() const { return m_chain_length; } - bool add_buffer_to_chain(PhysicalAddress buffer_start, size_t buffer_length, BufferType buffer_type); - void submit_to_queue(); - void release_buffer_slots_to_queue(); - - void for_each(Function callback) - { - VERIFY(m_queue.lock().is_locked()); - if (!m_start_of_chain_index.has_value()) - return; - auto index = m_start_of_chain_index.value(); - for (size_t i = 0; i < m_chain_length; ++i) { - auto addr = m_queue.m_descriptors[index].address; - auto length = m_queue.m_descriptors[index].length; - callback(PhysicalAddress(addr), length); - index = m_queue.m_descriptors[index].next; - } - } - -private: - void ensure_chain_is_empty() const - { - VERIFY(!m_start_of_chain_index.has_value()); - VERIFY(!m_end_of_chain_index.has_value()); - VERIFY(m_chain_length == 0); - } - - Queue& m_queue; - Optional m_start_of_chain_index {}; - Optional m_end_of_chain_index {}; - size_t m_chain_length {}; - bool m_chain_has_writable_pages { false }; -}; - -} diff --git a/Kernel/Bus/VirtIO/Transport/Entity.cpp b/Kernel/Bus/VirtIO/Transport/Entity.cpp deleted file mode 100644 index 322d7ca9ac5..00000000000 --- a/Kernel/Bus/VirtIO/Transport/Entity.cpp +++ /dev/null @@ -1,168 +0,0 @@ -/* - * Copyright (c) 2023, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include - -namespace Kernel::VirtIO { - -auto TransportEntity::mapping_for_resource_index(u8 resource_index) -> IOWindow& -{ - VERIFY(m_use_mmio.was_set()); - VERIFY(m_register_bases[resource_index]); - return *m_register_bases[resource_index]; -} - -u8 TransportEntity::config_read8(Configuration const& config, u32 offset) -{ - return mapping_for_resource_index(config.resource_index).read8(config.offset + offset); -} - -u16 TransportEntity::config_read16(Configuration const& config, u32 offset) -{ - return mapping_for_resource_index(config.resource_index).read16(config.offset + offset); -} - -u32 TransportEntity::config_read32(Configuration const& config, u32 offset) -{ - return mapping_for_resource_index(config.resource_index).read32(config.offset + offset); -} - -void TransportEntity::config_write8(Configuration const& config, u32 offset, u8 value) -{ - mapping_for_resource_index(config.resource_index).write8(config.offset + offset, value); -} - -void TransportEntity::config_write16(Configuration const& config, u32 offset, u16 value) -{ - mapping_for_resource_index(config.resource_index).write16(config.offset + offset, value); -} - -void TransportEntity::config_write32(Configuration const& config, u32 offset, u32 value) -{ - mapping_for_resource_index(config.resource_index).write32(config.offset + offset, value); -} - -void TransportEntity::config_write64(Configuration const& config, u32 offset, u64 value) -{ - mapping_for_resource_index(config.resource_index).write32(config.offset + offset, (u32)(value & 0xFFFFFFFF)); - mapping_for_resource_index(config.resource_index).write32(config.offset + offset + 4, (u32)(value >> 32)); -} - -IOWindow& TransportEntity::base_io_window() -{ - VERIFY(m_register_bases[0]); - return *m_register_bases[0]; -} - -u8 TransportEntity::isr_status() -{ - if (!m_isr_cfg) - return base_io_window().read8(REG_ISR_STATUS); - return config_read8(*m_isr_cfg, 0); -} - -void TransportEntity::set_status_bits(Badge, u8 status_bits) -{ - return set_status_bits(status_bits); -} - -void TransportEntity::set_status_bits(u8 status_bits) -{ - if (!m_common_cfg) - base_io_window().write8(REG_DEVICE_STATUS, status_bits); - else - config_write8(*m_common_cfg, COMMON_CFG_DEVICE_STATUS, status_bits); -} - -ErrorOr> TransportEntity::setup_queue(Badge, u16 queue_index) -{ - if (!m_common_cfg) - return Error::from_errno(ENXIO); - - config_write16(*m_common_cfg, COMMON_CFG_QUEUE_SELECT, queue_index); - u16 queue_size = config_read16(*m_common_cfg, COMMON_CFG_QUEUE_SIZE); - if (queue_size == 0) { - dbgln_if(VIRTIO_DEBUG, "Queue[{}] is unavailable!", queue_index); - return Error::from_errno(ENXIO); - } - - u16 queue_notify_offset = config_read16(*m_common_cfg, COMMON_CFG_QUEUE_NOTIFY_OFF); - - auto queue = TRY(Queue::try_create(queue_size, queue_notify_offset)); - - config_write64(*m_common_cfg, COMMON_CFG_QUEUE_DESC, queue->descriptor_area().get()); - config_write64(*m_common_cfg, COMMON_CFG_QUEUE_DRIVER, queue->driver_area().get()); - config_write64(*m_common_cfg, COMMON_CFG_QUEUE_DEVICE, queue->device_area().get()); - return queue; -} - -void TransportEntity::accept_device_features(Badge, u64 accepted_features) -{ - if (!m_common_cfg) { - base_io_window().write32(REG_GUEST_FEATURES, accepted_features); - } else { - config_write32(*m_common_cfg, COMMON_CFG_DRIVER_FEATURE_SELECT, 0); - config_write32(*m_common_cfg, COMMON_CFG_DRIVER_FEATURE, accepted_features); - config_write32(*m_common_cfg, COMMON_CFG_DRIVER_FEATURE_SELECT, 1); - config_write32(*m_common_cfg, COMMON_CFG_DRIVER_FEATURE, accepted_features >> 32); - } -} - -void TransportEntity::reset_device(Badge) -{ - if (!m_common_cfg) { - set_status_bits(0); - while (read_status_bits() != 0) { - // TODO: delay a bit? - } - return; - } - config_write8(*m_common_cfg, COMMON_CFG_DEVICE_STATUS, 0); - while (config_read8(*m_common_cfg, COMMON_CFG_DEVICE_STATUS) != 0) { - // TODO: delay a bit? - } -} - -void TransportEntity::notify_queue(Badge, NotifyQueueDescriptor descriptor) -{ - dbgln_if(VIRTIO_DEBUG, "notifying about queue change at idx: {}", descriptor.queue_index); - if (!m_notify_cfg) - base_io_window().write16(REG_QUEUE_NOTIFY, descriptor.queue_index); - else - config_write16(*m_notify_cfg, descriptor.possible_notify_offset * m_notify_multiplier, descriptor.queue_index); -} - -ErrorOr TransportEntity::activate_queue(Badge, u16 queue_index) -{ - if (!m_common_cfg) - return Error::from_errno(ENXIO); - - config_write16(*m_common_cfg, COMMON_CFG_QUEUE_SELECT, queue_index); - config_write16(*m_common_cfg, COMMON_CFG_QUEUE_ENABLE, true); - - dbgln_if(VIRTIO_DEBUG, "Queue[{}] activated", queue_index); - return {}; -} - -u64 TransportEntity::get_device_features() -{ - if (!m_common_cfg) - return base_io_window().read32(REG_DEVICE_FEATURES); - config_write32(*m_common_cfg, COMMON_CFG_DEVICE_FEATURE_SELECT, 0); - auto lower_bits = config_read32(*m_common_cfg, COMMON_CFG_DEVICE_FEATURE); - config_write32(*m_common_cfg, COMMON_CFG_DEVICE_FEATURE_SELECT, 1); - u64 upper_bits = (u64)config_read32(*m_common_cfg, COMMON_CFG_DEVICE_FEATURE) << 32; - return upper_bits | lower_bits; -} - -u8 TransportEntity::read_status_bits() -{ - if (!m_common_cfg) - return base_io_window().read8(REG_DEVICE_STATUS); - return config_read8(*m_common_cfg, COMMON_CFG_DEVICE_STATUS); -} - -} diff --git a/Kernel/Bus/VirtIO/Transport/Entity.h b/Kernel/Bus/VirtIO/Transport/Entity.h deleted file mode 100644 index 7204e910b8f..00000000000 --- a/Kernel/Bus/VirtIO/Transport/Entity.h +++ /dev/null @@ -1,99 +0,0 @@ -/* - * Copyright (c) 2023, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include - -namespace Kernel::VirtIO { - -class TransportEntity { -public: - virtual ~TransportEntity() = default; - - virtual ErrorOr locate_configurations_and_resources(Badge, VirtIO::Device&) = 0; - virtual void disable_interrupts(Badge) = 0; - virtual void enable_interrupts(Badge) = 0; - - virtual StringView determine_device_class_name() const = 0; - - void accept_device_features(Badge, u64 accepted_features); - - struct NotifyQueueDescriptor { - u16 queue_index; - u16 possible_notify_offset; - }; - void notify_queue(Badge, NotifyQueueDescriptor); - ErrorOr activate_queue(Badge, u16 queue_index); - ErrorOr> setup_queue(Badge, u16 queue_index); - void set_status_bits(Badge, u8 status_bits); - void reset_device(Badge); - - u8 read_status_bits(); - u8 isr_status(); - u64 get_device_features(); - - ErrorOr get_config(ConfigurationType cfg_type, u32 index = 0) const - { - for (auto const& cfg : m_configs) { - if (cfg.cfg_type != cfg_type) - continue; - if (index > 0) { - index--; - continue; - } - return &cfg; - } - return Error::from_errno(ENXIO); - } - - u8 config_read8(Configuration const&, u32); - u16 config_read16(Configuration const&, u32); - u32 config_read32(Configuration const&, u32); - void config_write8(Configuration const&, u32, u8); - void config_write16(Configuration const&, u32, u16); - void config_write32(Configuration const&, u32, u32); - void config_write64(Configuration const&, u32, u64); - - template - void read_config_atomic(F f) - { - if (m_common_cfg) { - u8 generation_before, generation_after; - do { - generation_before = config_read8(*m_common_cfg, 0x15); - f(); - generation_after = config_read8(*m_common_cfg, 0x15); - } while (generation_before != generation_after); - } else { - f(); - } - } - -protected: - TransportEntity() = default; - - auto mapping_for_resource_index(u8) -> IOWindow&; - - void set_status_bits(u8 status_bits); - - Vector m_configs; - Configuration const* m_common_cfg { nullptr }; // Cached due to high usage - Configuration const* m_notify_cfg { nullptr }; // Cached due to high usage - Configuration const* m_isr_cfg { nullptr }; // Cached due to high usage - - IOWindow& base_io_window(); - Array, 6> m_register_bases; - SetOnce m_use_mmio; - - u32 m_notify_multiplier { 0 }; -}; - -}; diff --git a/Kernel/Bus/VirtIO/Transport/InterruptHandler.cpp b/Kernel/Bus/VirtIO/Transport/InterruptHandler.cpp deleted file mode 100644 index d544efa9176..00000000000 --- a/Kernel/Bus/VirtIO/Transport/InterruptHandler.cpp +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Copyright (c) 2023, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include - -namespace Kernel::VirtIO { - -TransportInterruptHandler::TransportInterruptHandler(VirtIO::Device& parent_device) - : m_parent_device(parent_device) -{ -} - -bool TransportInterruptHandler::notify_parent_device_on_interrupt() -{ - return m_parent_device.handle_irq({}); -} - -} diff --git a/Kernel/Bus/VirtIO/Transport/InterruptHandler.h b/Kernel/Bus/VirtIO/Transport/InterruptHandler.h deleted file mode 100644 index 647b636f60d..00000000000 --- a/Kernel/Bus/VirtIO/Transport/InterruptHandler.h +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright (c) 2023, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include - -namespace Kernel::VirtIO { - -class Device; -class TransportInterruptHandler { -protected: - TransportInterruptHandler(VirtIO::Device&); - - bool notify_parent_device_on_interrupt(); - -private: - VirtIO::Device& m_parent_device; -}; - -} diff --git a/Kernel/Bus/VirtIO/Transport/PCIe/Detect.cpp b/Kernel/Bus/VirtIO/Transport/PCIe/Detect.cpp deleted file mode 100644 index b3f26f46a9c..00000000000 --- a/Kernel/Bus/VirtIO/Transport/PCIe/Detect.cpp +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright (c) 2021, the SerenityOS developers. - * Copyright (c) 2023, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace Kernel::VirtIO { - -UNMAP_AFTER_INIT void detect_pci_instances() -{ - if (kernel_command_line().disable_virtio()) - return; - MUST(PCI::enumerate([&](PCI::DeviceIdentifier const& device_identifier) { - if (device_identifier.hardware_id().is_null()) - return; - // TODO: We should also be checking that the device_id is in between 0x1000 - 0x107F inclusive - if (device_identifier.hardware_id().vendor_id != PCI::VendorID::VirtIO) - return; - switch (device_identifier.hardware_id().device_id) { - case PCI::DeviceID::VirtIOConsole: { - auto& console = Console::must_create_for_pci_instance(device_identifier).leak_ref(); - MUST(console.initialize_virtio_resources()); - break; - } - case PCI::DeviceID::VirtIOEntropy: { - auto& rng = RNG::must_create_for_pci_instance(device_identifier).leak_ref(); - MUST(rng.initialize_virtio_resources()); - break; - } - case PCI::DeviceID::VirtIOGPU: { - // This should have been initialized by the graphics subsystem - break; - } - case PCI::DeviceID::VirtIOBlockDevice: { - // This should have been initialized by the storage subsystem - break; - } - case PCI::DeviceID::VirtIOInput: { - auto& input = Input::must_create_for_pci_instance(device_identifier).leak_ref(); - MUST(input.initialize_virtio_resources()); - break; - } - default: - dbgln_if(VIRTIO_DEBUG, "VirtIO: Unknown VirtIO device with ID: {}", device_identifier.hardware_id().device_id); - break; - } - })); -} - -} diff --git a/Kernel/Bus/VirtIO/Transport/PCIe/Detect.h b/Kernel/Bus/VirtIO/Transport/PCIe/Detect.h deleted file mode 100644 index 4c4602e1d5a..00000000000 --- a/Kernel/Bus/VirtIO/Transport/PCIe/Detect.h +++ /dev/null @@ -1,13 +0,0 @@ -/* - * Copyright (c) 2023, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -namespace Kernel::VirtIO { - -void detect_pci_instances(); - -} diff --git a/Kernel/Bus/VirtIO/Transport/PCIe/InterruptHandler.cpp b/Kernel/Bus/VirtIO/Transport/PCIe/InterruptHandler.cpp deleted file mode 100644 index 1ae2d83ed6b..00000000000 --- a/Kernel/Bus/VirtIO/Transport/PCIe/InterruptHandler.cpp +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright (c) 2021-2022, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include - -namespace Kernel::VirtIO { - -ErrorOr> PCIeTransportInterruptHandler::create(PCIeTransportLink& transport_link, VirtIO::Device& parent_device, u8 irq) -{ - return TRY(adopt_nonnull_own_or_enomem(new (nothrow) PCIeTransportInterruptHandler(transport_link, parent_device, irq))); -} - -PCIeTransportInterruptHandler::PCIeTransportInterruptHandler(PCIeTransportLink& transport_link, VirtIO::Device& parent_device, u8 irq) - : TransportInterruptHandler(parent_device) - , PCI::IRQHandler(transport_link, irq) -{ -} - -bool PCIeTransportInterruptHandler::handle_irq(RegisterState const&) -{ - return notify_parent_device_on_interrupt(); -} - -} diff --git a/Kernel/Bus/VirtIO/Transport/PCIe/InterruptHandler.h b/Kernel/Bus/VirtIO/Transport/PCIe/InterruptHandler.h deleted file mode 100644 index 3ba8c287129..00000000000 --- a/Kernel/Bus/VirtIO/Transport/PCIe/InterruptHandler.h +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright (c) 2023, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include - -namespace Kernel::VirtIO { - -class PCIeTransportInterruptHandler final - : public TransportInterruptHandler - , public PCI::IRQHandler { -public: - static ErrorOr> create(PCIeTransportLink&, VirtIO::Device&, u8 irq); - virtual ~PCIeTransportInterruptHandler() override = default; - - virtual StringView purpose() const override { return "VirtIO PCI IRQ Handler"sv; } - -private: - PCIeTransportInterruptHandler(PCIeTransportLink&, VirtIO::Device&, u8 irq); - - //^ IRQHandler - virtual bool handle_irq(RegisterState const&) override; -}; -} diff --git a/Kernel/Bus/VirtIO/Transport/PCIe/TransportLink.cpp b/Kernel/Bus/VirtIO/Transport/PCIe/TransportLink.cpp deleted file mode 100644 index 4864c1fb3ec..00000000000 --- a/Kernel/Bus/VirtIO/Transport/PCIe/TransportLink.cpp +++ /dev/null @@ -1,165 +0,0 @@ -/* - * Copyright (c) 2023, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include - -namespace Kernel::VirtIO { - -ErrorOr> PCIeTransportLink::create(PCI::DeviceIdentifier const& pci_identifier) -{ - return TRY(adopt_nonnull_own_or_enomem(new (nothrow) PCIeTransportLink(pci_identifier))); -} - -StringView PCIeTransportLink::determine_device_class_name() const -{ - if (device_identifier().revision_id().value() == 0) { - // Note: If the device is a legacy (or transitional) device, therefore, - // probe the subsystem ID in the PCI header and figure out the - auto subsystem_device_id = device_identifier().subsystem_id().value(); - switch (subsystem_device_id) { - case 1: - return "VirtIONetAdapter"sv; - case 2: - return "VirtIOBlockDevice"sv; - case 3: - return "VirtIOConsole"sv; - case 4: - return "VirtIORNG"sv; - case 18: - return "VirtIOInput"sv; - default: - dbgln("VirtIO: Unknown subsystem_device_id {}", subsystem_device_id); - VERIFY_NOT_REACHED(); - } - } - - auto id = device_identifier().hardware_id(); - VERIFY(id.vendor_id == PCI::VendorID::VirtIO); - switch (id.device_id) { - case PCI::DeviceID::VirtIONetAdapter: - return "VirtIONetAdapter"sv; - case PCI::DeviceID::VirtIOBlockDevice: - return "VirtIOBlockDevice"sv; - case PCI::DeviceID::VirtIOConsole: - return "VirtIOConsole"sv; - case PCI::DeviceID::VirtIOEntropy: - return "VirtIORNG"sv; - case PCI::DeviceID::VirtIOGPU: - return "VirtIOGPU"sv; - case PCI::DeviceID::VirtIOInput: - return "VirtIOInput"sv; - default: - dbgln("VirtIO: Unknown device_id {:#x}", id.device_id); - VERIFY_NOT_REACHED(); - } -} - -ErrorOr PCIeTransportLink::create_interrupt_handler(VirtIO::Device& parent_device) -{ - TRY(reserve_irqs(1, false)); - auto irq = MUST(allocate_irq(0)); - m_irq_handler = TRY(PCIeTransportInterruptHandler::create(*this, parent_device, irq)); - return {}; -} - -PCIeTransportLink::PCIeTransportLink(PCI::DeviceIdentifier const& pci_identifier) - : PCI::Device(pci_identifier) -{ - dbgln("{}: Found @ {}", determine_device_class_name(), device_identifier().address()); -} - -ErrorOr PCIeTransportLink::locate_configurations_and_resources(Badge, VirtIO::Device& parent_device) -{ - TRY(create_interrupt_handler(parent_device)); - PCI::enable_bus_mastering(device_identifier()); - - auto capabilities = device_identifier().capabilities(); - for (auto& capability : capabilities) { - if (capability.id().value() == PCI::Capabilities::ID::VendorSpecific) { - // We have a virtio_pci_cap - Configuration config {}; - auto raw_config_type = capability.read8(0x3); - // NOTE: The VirtIO specification allows iteration of configurations - // through a special PCI capbility structure with the VIRTIO_PCI_CAP_PCI_CFG tag: - // - // "Each structure can be mapped by a Base Address register (BAR) belonging to the function, or accessed via - // the special VIRTIO_PCI_CAP_PCI_CFG field in the PCI configuration space" - // - // "The VIRTIO_PCI_CAP_PCI_CFG capability creates an alternative (and likely suboptimal) access method - // to the common configuration, notification, ISR and device-specific configuration regions." - // - // Also, it is *very* likely to see this PCI capability as the first vendor-specific capbility of a certain PCI function, - // but this is not guaranteed by the VirtIO specification. - // Therefore, ignore this type of configuration as this is not needed by our implementation currently. - if (raw_config_type == static_cast(ConfigurationType::PCICapabilitiesAccess)) - continue; - if (raw_config_type < static_cast(ConfigurationType::Common) || raw_config_type > static_cast(ConfigurationType::PCICapabilitiesAccess)) { - dbgln("{}: Unknown capability configuration type: {}", device_name(), raw_config_type); - return Error::from_errno(ENXIO); - } - config.cfg_type = static_cast(raw_config_type); - auto cap_length = capability.read8(0x2); - if (cap_length < 0x10) { - dbgln("{}: Unexpected capability size: {}", device_name(), cap_length); - break; - } - config.resource_index = capability.read8(0x4); - if (config.resource_index > 0x5) { - dbgln("{}: Unexpected capability BAR value: {}", device_name(), config.resource_index); - break; - } - config.offset = capability.read32(0x8); - config.length = capability.read32(0xc); - // NOTE: Configuration length of zero is an invalid configuration, or at the very least a configuration - // type we don't know how to handle correctly... - // The VIRTIO_PCI_CAP_PCI_CFG configuration structure has length of 0 - // but because we ignore that type and all other types should have a length - // greater than 0, we should ignore any other configuration in case this condition is not met. - if (config.length == 0) { - dbgln("{}: Found configuration {}, with invalid length of 0", device_name(), (u32)config.cfg_type); - continue; - } - dbgln_if(VIRTIO_DEBUG, "{}: Found configuration {}, resource: {}, offset: {}, length: {}", device_name(), (u32)config.cfg_type, config.resource_index, config.offset, config.length); - if (config.cfg_type == ConfigurationType::Common) - m_use_mmio.set(); - else if (config.cfg_type == ConfigurationType::Notify) - m_notify_multiplier = capability.read32(0x10); - - m_configs.append(config); - } - } - - if (m_use_mmio.was_set()) { - for (auto& cfg : m_configs) { - auto mapping_io_window = TRY(IOWindow::create_for_pci_device_bar(device_identifier(), static_cast(cfg.resource_index))); - m_register_bases[cfg.resource_index] = move(mapping_io_window); - } - m_common_cfg = TRY(get_config(ConfigurationType::Common, 0)); - m_notify_cfg = TRY(get_config(ConfigurationType::Notify, 0)); - m_isr_cfg = TRY(get_config(ConfigurationType::ISR, 0)); - } else { - auto mapping_io_window = TRY(IOWindow::create_for_pci_device_bar(device_identifier(), PCI::HeaderType0BaseRegister::BAR0)); - m_register_bases[0] = move(mapping_io_window); - } - return {}; -} - -void PCIeTransportLink::disable_interrupts(Badge) -{ - disable_pin_based_interrupts(); - m_irq_handler->disable_irq(); -} - -void PCIeTransportLink::enable_interrupts(Badge) -{ - m_irq_handler->enable_irq(); - enable_pin_based_interrupts(); -} - -} diff --git a/Kernel/Bus/VirtIO/Transport/PCIe/TransportLink.h b/Kernel/Bus/VirtIO/Transport/PCIe/TransportLink.h deleted file mode 100644 index f1ad4e569ab..00000000000 --- a/Kernel/Bus/VirtIO/Transport/PCIe/TransportLink.h +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright (c) 2021, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include - -namespace Kernel::VirtIO { - -class PCIeTransportLink final - : public TransportEntity - , public PCI::Device { -public: - static ErrorOr> create(PCI::DeviceIdentifier const& pci_identifier); - - virtual StringView device_name() const override { return "VirtIOTransportLink"sv; } - virtual StringView determine_device_class_name() const override; - -private: - explicit PCIeTransportLink(PCI::DeviceIdentifier const& pci_identifier); - - // ^TransportEntity - virtual ErrorOr locate_configurations_and_resources(Badge, VirtIO::Device&) override; - virtual void disable_interrupts(Badge) override; - virtual void enable_interrupts(Badge) override; - - ErrorOr create_interrupt_handler(VirtIO::Device&); - - // FIXME: There could be multiple IRQ (MSI-X) handlers for a VirtIO device. - // Find a way to use all of them. - OwnPtr m_irq_handler; -}; - -}; diff --git a/Kernel/CMakeLists.txt b/Kernel/CMakeLists.txt deleted file mode 100644 index 81771e9f85d..00000000000 --- a/Kernel/CMakeLists.txt +++ /dev/null @@ -1,908 +0,0 @@ -if (ENABLE_EXTRA_KERNEL_DEBUG_SYMBOLS) - add_compile_options(-Og) - add_compile_options(-ggdb3) -else() - add_compile_options(-O2) -endif() - -if ("${SERENITY_ARCH}" STREQUAL "aarch64") - set(KERNEL_ARCH aarch64) -elseif("${SERENITY_ARCH}" STREQUAL "x86_64") - set(KERNEL_ARCH x86_64) -elseif("${SERENITY_ARCH}" STREQUAL "riscv64") - set(KERNEL_ARCH riscv64) -endif() - -set(KERNEL_HEAP_SOURCES - Heap/kmalloc.cpp -) - -set(KERNEL_SOURCES - Arch/init.cpp - Arch/DeferredCallPool.cpp - Arch/PageFault.cpp - Arch/Processor.cpp - Arch/TrapFrame.cpp - Boot/CommandLine.cpp - Bus/PCI/Controller/HostController.cpp - Bus/PCI/Controller/MemoryBackedHostBridge.cpp - Bus/PCI/Controller/VolumeManagementDevice.cpp - Bus/PCI/Access.cpp - Bus/PCI/API.cpp - Bus/PCI/Device.cpp - Bus/PCI/DeviceIdentifier.cpp - Bus/USB/EHCI/EHCIController.cpp - Bus/USB/UHCI/UHCIController.cpp - Bus/USB/UHCI/UHCIRootHub.cpp - Bus/USB/Drivers/HID/MouseDriver.cpp - Bus/USB/Drivers/MassStorage/MassStorageDriver.cpp - Bus/USB/USBConfiguration.cpp - Bus/USB/USBController.cpp - Bus/USB/USBDevice.cpp - Bus/USB/USBHub.cpp - Bus/USB/USBManagement.cpp - Bus/USB/USBPipe.cpp - Bus/USB/USBTransfer.cpp - Bus/VirtIO/Transport/PCIe/Detect.cpp - Bus/VirtIO/Transport/PCIe/InterruptHandler.cpp - Bus/VirtIO/Transport/PCIe/TransportLink.cpp - Bus/VirtIO/Transport/Entity.cpp - Bus/VirtIO/Transport/InterruptHandler.cpp - Bus/VirtIO/Device.cpp - Bus/VirtIO/Queue.cpp - Devices/AsyncDeviceRequest.cpp - Devices/Audio/AC97/AC97.cpp - Devices/Audio/Channel.cpp - Devices/Audio/IntelHDA/Codec.cpp - Devices/Audio/IntelHDA/Controller.cpp - Devices/Audio/IntelHDA/Format.cpp - Devices/Audio/IntelHDA/InterruptHandler.cpp - Devices/Audio/IntelHDA/Stream.cpp - Devices/Audio/Management.cpp - Devices/BlockDevice.cpp - Devices/CharacterDevice.cpp - Devices/Device.cpp - Devices/DeviceManagement.cpp - Devices/FUSEDevice.cpp - Devices/PCISerialDevice.cpp - Devices/SerialDevice.cpp - Devices/HID/AllMiceDevice.cpp - Devices/HID/KeyboardDevice.cpp - Devices/HID/Management.cpp - Devices/HID/MouseDevice.cpp - Devices/HID/PS2/KeyboardDevice.cpp - Devices/HID/PS2/MouseDevice.cpp - Devices/HID/USB/MouseDevice.cpp - Devices/HID/VirtIO/Input.cpp - Devices/Generic/ConsoleDevice.cpp - Devices/Generic/DeviceControlDevice.cpp - Devices/Generic/FullDevice.cpp - Devices/Generic/MemoryDevice.cpp - Devices/Generic/NullDevice.cpp - Devices/Generic/PCSpeakerDevice.cpp - Devices/Generic/RandomDevice.cpp - Devices/Generic/SelfTTYDevice.cpp - Devices/Generic/ZeroDevice.cpp - Devices/GPU/3dfx/GraphicsAdapter.cpp - Devices/GPU/3dfx/VoodooDisplayConnector.cpp - Devices/GPU/Bochs/GraphicsAdapter.cpp - Devices/GPU/Bochs/QEMUDisplayConnector.cpp - Devices/GPU/Console/BootFramebufferConsole.cpp - Devices/GPU/Console/GenericFramebufferConsole.cpp - Devices/GPU/Console/ContiguousFramebufferConsole.cpp - Devices/GPU/DisplayConnector.cpp - Devices/GPU/Generic/DisplayConnector.cpp - Devices/GPU/Management.cpp - Devices/GPU/Intel/Auxiliary/GMBusConnector.cpp - Devices/GPU/Intel/Plane/DisplayPlane.cpp - Devices/GPU/Intel/Plane/G33DisplayPlane.cpp - Devices/GPU/Intel/Transcoder/AnalogDisplayTranscoder.cpp - Devices/GPU/Intel/Transcoder/DisplayTranscoder.cpp - Devices/GPU/Intel/Transcoder/PLL.cpp - Devices/GPU/Intel/DisplayConnectorGroup.cpp - Devices/GPU/Intel/NativeDisplayConnector.cpp - Devices/GPU/Intel/NativeGraphicsAdapter.cpp - Devices/GPU/VMWare/Console.cpp - Devices/GPU/VMWare/GraphicsAdapter.cpp - Devices/GPU/VMWare/DisplayConnector.cpp - Devices/GPU/VirtIO/DisplayConnector.cpp - Devices/GPU/VirtIO/Console.cpp - Devices/GPU/VirtIO/GPU3DDevice.cpp - Devices/GPU/VirtIO/GraphicsAdapter.cpp - Devices/Loop/LoopDevice.cpp - Devices/Serial/VirtIO/Console.cpp - Devices/Serial/VirtIO/ConsolePort.cpp - Devices/Storage/AHCI/ATADevice.cpp - Devices/Storage/AHCI/ATADiskDevice.cpp - Devices/Storage/AHCI/Controller.cpp - Devices/Storage/AHCI/InterruptHandler.cpp - Devices/Storage/AHCI/Port.cpp - Devices/Storage/NVMe/NVMeController.cpp - Devices/Storage/NVMe/NVMeNameSpace.cpp - Devices/Storage/NVMe/NVMeInterruptQueue.cpp - Devices/Storage/NVMe/NVMePollQueue.cpp - Devices/Storage/NVMe/NVMeQueue.cpp - Devices/Storage/SD/PCISDHostController.cpp - Devices/Storage/SD/SDHostController.cpp - Devices/Storage/SD/SDMemoryCard.cpp - Devices/Storage/USB/BulkSCSIInterface.cpp - Devices/Storage/VirtIO/VirtIOBlockController.cpp - Devices/Storage/VirtIO/VirtIOBlockDevice.cpp - Devices/Storage/StorageController.cpp - Devices/Storage/StorageDevice.cpp - Devices/Storage/StorageManagement.cpp - Devices/Storage/StorageDevicePartition.cpp - FileSystem/AnonymousFile.cpp - FileSystem/BlockBasedFileSystem.cpp - FileSystem/Custody.cpp - FileSystem/CustodyBase.cpp - FileSystem/DevLoopFS/FileSystem.cpp - FileSystem/DevLoopFS/Inode.cpp - FileSystem/DevPtsFS/FileSystem.cpp - FileSystem/DevPtsFS/Inode.cpp - FileSystem/Ext2FS/FileSystem.cpp - FileSystem/Ext2FS/Inode.cpp - FileSystem/FATFS/FileSystem.cpp - FileSystem/FATFS/Inode.cpp - FileSystem/FIFO.cpp - FileSystem/File.cpp - FileSystem/FileBackedFileSystem.cpp - FileSystem/FileSystem.cpp - FileSystem/FUSE/FileSystem.cpp - FileSystem/FUSE/Inode.cpp - FileSystem/Inode.cpp - FileSystem/InodeFile.cpp - FileSystem/InodeMetadata.cpp - FileSystem/InodeWatcher.cpp - FileSystem/ISO9660FS/DirectoryIterator.cpp - FileSystem/ISO9660FS/FileSystem.cpp - FileSystem/ISO9660FS/Inode.cpp - FileSystem/Mount.cpp - FileSystem/MountFile.cpp - FileSystem/OpenFileDescription.cpp - FileSystem/Plan9FS/FileSystem.cpp - FileSystem/Plan9FS/Inode.cpp - FileSystem/Plan9FS/Message.cpp - FileSystem/ProcFS/FileSystem.cpp - FileSystem/ProcFS/Inode.cpp - FileSystem/ProcFS/ProcessExposed.cpp - FileSystem/RAMFS/FileSystem.cpp - FileSystem/RAMFS/Inode.cpp - FileSystem/SysFS/Component.cpp - FileSystem/SysFS/DirectoryInode.cpp - FileSystem/SysFS/FileSystem.cpp - FileSystem/SysFS/Inode.cpp - FileSystem/SysFS/LinkInode.cpp - FileSystem/SysFS/Registry.cpp - FileSystem/SysFS/RootDirectory.cpp - FileSystem/SysFS/Subsystems/Bus/PCI/BusDirectory.cpp - FileSystem/SysFS/Subsystems/Bus/PCI/DeviceAttribute.cpp - FileSystem/SysFS/Subsystems/Bus/PCI/DeviceDirectory.cpp - FileSystem/SysFS/Subsystems/Bus/PCI/DeviceExpansionROM.cpp - FileSystem/SysFS/Subsystems/Bus/USB/BusDirectory.cpp - FileSystem/SysFS/Subsystems/Bus/USB/DeviceInformation.cpp - FileSystem/SysFS/Subsystems/Bus/Directory.cpp - FileSystem/SysFS/Subsystems/DeviceIdentifiers/BlockDevicesDirectory.cpp - FileSystem/SysFS/Subsystems/DeviceIdentifiers/CharacterDevicesDirectory.cpp - FileSystem/SysFS/Subsystems/DeviceIdentifiers/DeviceComponent.cpp - FileSystem/SysFS/Subsystems/DeviceIdentifiers/Directory.cpp - FileSystem/SysFS/Subsystems/DeviceIdentifiers/SymbolicLinkDeviceComponent.cpp - FileSystem/SysFS/Subsystems/Devices/Storage/DeviceAttribute.cpp - FileSystem/SysFS/Subsystems/Devices/Storage/DeviceDirectory.cpp - FileSystem/SysFS/Subsystems/Devices/Storage/Directory.cpp - FileSystem/SysFS/Subsystems/Devices/Graphics/Directory.cpp - FileSystem/SysFS/Subsystems/Devices/Graphics/DisplayConnector/Directory.cpp - FileSystem/SysFS/Subsystems/Devices/Graphics/DisplayConnector/DeviceDirectory.cpp - FileSystem/SysFS/Subsystems/Devices/Graphics/DisplayConnector/DeviceAttribute.cpp - FileSystem/SysFS/Subsystems/Devices/Directory.cpp - FileSystem/SysFS/Subsystems/Firmware/Directory.cpp - FileSystem/SysFS/Subsystems/Kernel/Interrupts.cpp - FileSystem/SysFS/Subsystems/Kernel/Processes.cpp - FileSystem/SysFS/Subsystems/Kernel/CPUInfo.cpp - FileSystem/SysFS/Subsystems/Kernel/ConstantInformation.cpp - FileSystem/SysFS/Subsystems/Kernel/Jails.cpp - FileSystem/SysFS/Subsystems/Kernel/Keymap.cpp - FileSystem/SysFS/Subsystems/Kernel/Profile.cpp - FileSystem/SysFS/Subsystems/Kernel/Directory.cpp - FileSystem/SysFS/Subsystems/Kernel/DiskUsage.cpp - FileSystem/SysFS/Subsystems/Kernel/Log.cpp - FileSystem/SysFS/Subsystems/Kernel/RequestPanic.cpp - FileSystem/SysFS/Subsystems/Kernel/SystemStatistics.cpp - FileSystem/SysFS/Subsystems/Kernel/GlobalInformation.cpp - FileSystem/SysFS/Subsystems/Kernel/MemoryStatus.cpp - FileSystem/SysFS/Subsystems/Kernel/PowerStateSwitch.cpp - FileSystem/SysFS/Subsystems/Kernel/Uptime.cpp - FileSystem/SysFS/Subsystems/Kernel/Network/Adapters.cpp - FileSystem/SysFS/Subsystems/Kernel/Network/ARP.cpp - FileSystem/SysFS/Subsystems/Kernel/Network/Directory.cpp - FileSystem/SysFS/Subsystems/Kernel/Network/Local.cpp - FileSystem/SysFS/Subsystems/Kernel/Network/Route.cpp - FileSystem/SysFS/Subsystems/Kernel/Network/TCP.cpp - FileSystem/SysFS/Subsystems/Kernel/Network/UDP.cpp - FileSystem/SysFS/Subsystems/Kernel/Configuration/BooleanVariable.cpp - FileSystem/SysFS/Subsystems/Kernel/Configuration/CapsLockRemap.cpp - FileSystem/SysFS/Subsystems/Kernel/Configuration/CoredumpDirectory.cpp - FileSystem/SysFS/Subsystems/Kernel/Configuration/Directory.cpp - FileSystem/SysFS/Subsystems/Kernel/Configuration/DumpKmallocStack.cpp - FileSystem/SysFS/Subsystems/Kernel/Configuration/StringVariable.cpp - FileSystem/SysFS/Subsystems/Kernel/Configuration/UBSANDeadly.cpp - FileSystem/VirtualFileSystem.cpp - Firmware/ACPI/Initialize.cpp - Firmware/ACPI/Parser.cpp - Firmware/ACPI/StaticParsing.cpp - Interrupts/GenericInterruptHandler.cpp - Interrupts/IRQHandler.cpp - Interrupts/PCIIRQHandler.cpp - Interrupts/SharedIRQHandler.cpp - Interrupts/UnhandledInterruptHandler.cpp - KSyms.cpp - Memory/AddressSpace.cpp - Memory/AnonymousVMObject.cpp - Memory/InodeVMObject.cpp - Memory/MemoryManager.cpp - Memory/MMIOVMObject.cpp - Memory/PhysicalRAMPage.cpp - Memory/PhysicalRegion.cpp - Memory/PhysicalZone.cpp - Memory/PrivateInodeVMObject.cpp - Memory/Region.cpp - Memory/RegionTree.cpp - Memory/RingBuffer.cpp - Memory/ScatterGatherList.cpp - Memory/ScopedAddressSpaceSwitcher.cpp - Memory/SharedFramebufferVMObject.cpp - Memory/SharedInodeVMObject.cpp - Memory/VMObject.cpp - Memory/VirtualRange.cpp - Locking/LockRank.cpp - Locking/Mutex.cpp - Library/DoubleBuffer.cpp - Library/IOWindow.cpp - Library/MiniStdLib.cpp - Library/Panic.cpp - Library/ScopedCritical.cpp - Library/StdLib.cpp - Library/KBufferBuilder.cpp - Library/KLexicalPath.cpp - Library/KString.cpp - Library/UserOrKernelBuffer.cpp - Net/Intel/E1000ENetworkAdapter.cpp - Net/Intel/E1000NetworkAdapter.cpp - Net/Realtek/RTL8168NetworkAdapter.cpp - Net/VirtIO/VirtIONetworkAdapter.cpp - Net/IPv4Socket.cpp - Net/LocalSocket.cpp - Net/LoopbackAdapter.cpp - Net/NetworkAdapter.cpp - Net/NetworkTask.cpp - Net/NetworkingManagement.cpp - Net/Routing.cpp - Net/Socket.cpp - Net/TCPSocket.cpp - Net/UDPSocket.cpp - Security/Random/VirtIO/RNG.cpp - Security/AddressSanitizer.cpp - Security/Credentials.cpp - Security/Random.cpp - Security/Jail.cpp - Security/UBSanitizer.cpp - Syscalls/anon_create.cpp - Syscalls/alarm.cpp - Syscalls/chdir.cpp - Syscalls/chmod.cpp - Syscalls/chown.cpp - Syscalls/clock.cpp - Syscalls/debug.cpp - Syscalls/disown.cpp - Syscalls/dup2.cpp - Syscalls/emuctl.cpp - Syscalls/execve.cpp - Syscalls/exit.cpp - Syscalls/faccessat.cpp - Syscalls/fallocate.cpp - Syscalls/fcntl.cpp - Syscalls/fork.cpp - Syscalls/fsync.cpp - Syscalls/ftruncate.cpp - Syscalls/futex.cpp - Syscalls/get_dir_entries.cpp - Syscalls/get_stack_bounds.cpp - Syscalls/getrandom.cpp - Syscalls/getuid.cpp - Syscalls/hostname.cpp - Syscalls/ioctl.cpp - Syscalls/jail.cpp - Syscalls/keymap.cpp - Syscalls/kill.cpp - Syscalls/link.cpp - Syscalls/lseek.cpp - Syscalls/mkdir.cpp - Syscalls/mknod.cpp - Syscalls/mmap.cpp - Syscalls/mount.cpp - Syscalls/open.cpp - Syscalls/perf_event.cpp - Syscalls/pipe.cpp - Syscalls/pledge.cpp - Syscalls/poll.cpp - Syscalls/prctl.cpp - Syscalls/process.cpp - Syscalls/profiling.cpp - Syscalls/profiled_syscalls.cpp - Syscalls/ptrace.cpp - Syscalls/purge.cpp - Syscalls/read.cpp - Syscalls/readlink.cpp - Syscalls/realpath.cpp - Syscalls/rename.cpp - Syscalls/resource.cpp - Syscalls/rmdir.cpp - Syscalls/sched.cpp - Syscalls/sendfd.cpp - Syscalls/setpgid.cpp - Syscalls/setuid.cpp - Syscalls/sigaction.cpp - Syscalls/socket.cpp - Syscalls/stat.cpp - Syscalls/statvfs.cpp - Syscalls/sync.cpp - Syscalls/SyscallHandler.cpp - Syscalls/sysconf.cpp - Syscalls/thread.cpp - Syscalls/times.cpp - Syscalls/umask.cpp - Syscalls/uname.cpp - Syscalls/unlink.cpp - Syscalls/unveil.cpp - Syscalls/utime.cpp - Syscalls/utimensat.cpp - Syscalls/waitid.cpp - Syscalls/inode_watcher.cpp - Syscalls/write.cpp - Devices/TTY/ConsoleManagement.cpp - Devices/TTY/MasterPTY.cpp - Devices/TTY/PTYMultiplexer.cpp - Devices/TTY/SlavePTY.cpp - Devices/TTY/TTY.cpp - Devices/TTY/VirtualConsole.cpp - Tasks/Coredump.cpp - Tasks/CrashHandler.cpp - Tasks/FinalizerTask.cpp - Tasks/FutexQueue.cpp - Tasks/PerformanceEventBuffer.cpp - Tasks/PowerStateSwitchTask.cpp - Tasks/Process.cpp - Tasks/ProcessGroup.cpp - Tasks/ProcessList.cpp - Tasks/Scheduler.cpp - Tasks/SyncTask.cpp - Tasks/Thread.cpp - Tasks/ThreadBlockers.cpp - Tasks/ThreadTracer.cpp - Tasks/WaitQueue.cpp - Tasks/WorkQueue.cpp - Time/TimeManagement.cpp - Time/TimerQueue.cpp -) - -if ("${SERENITY_ARCH}" STREQUAL "x86_64") - set(KERNEL_SOURCES - ${KERNEL_SOURCES} - - Arch/x86_64/archctl.cpp - - Arch/x86_64/CMOS.cpp - Arch/x86_64/DebugOutput.cpp - Arch/x86_64/Delay.cpp - - Arch/x86_64/Firmware/ACPI/StaticParsing.cpp - Arch/x86_64/Firmware/MultiProcessor/Parser.cpp - Arch/x86_64/Firmware/PCBIOS/Mapper.cpp - Arch/x86_64/Firmware/PCBIOS/SysFSComponent.cpp - Arch/x86_64/Firmware/PCBIOS/SysFSDirectory.cpp - - Arch/x86_64/Hypervisor/BochsDisplayConnector.cpp - Arch/x86_64/Hypervisor/VMWareBackdoor.cpp - - Arch/x86_64/CurrentTime.cpp - - Arch/x86_64/I8042Reboot.cpp - Arch/x86_64/Interrupts/APIC.cpp - Arch/x86_64/Interrupts/IOAPIC.cpp - Arch/x86_64/Interrupts/PIC.cpp - Arch/x86_64/Time/APICTimer.cpp - Arch/x86_64/Time/HPET.cpp - Arch/x86_64/Time/HPETComparator.cpp - Arch/x86_64/Time/PIT.cpp - Arch/x86_64/Time/RTC.cpp - Arch/x86_64/PCSpeaker.cpp - - Arch/x86_64/ISABus/HID/VMWareMouseDevice.cpp - Arch/x86_64/ISABus/I8042Controller.cpp - Arch/x86_64/ISABus/SerialDevice.cpp - Arch/x86_64/PCI/Controller/PIIX4HostBridge.cpp - Arch/x86_64/PCI/Initializer.cpp - Arch/x86_64/PCI/MSI.cpp - - Arch/x86_64/VGA/IOArbiter.cpp - Arch/x86_64/VGA/TextModeConsole.cpp - - Arch/x86_64/PowerState.cpp - Arch/x86_64/RTC.cpp - Arch/x86_64/Shutdown.cpp - Arch/x86_64/SmapDisabler.cpp - - # TODO: Share these with the aarch64 build - Interrupts/SpuriousInterruptHandler.cpp - kprintf.cpp - ) - - set(KERNEL_SOURCES - ${KERNEL_SOURCES} - ${CMAKE_CURRENT_SOURCE_DIR}/Arch/x86_64/Boot/ap_setup.S - ${CMAKE_CURRENT_SOURCE_DIR}/Arch/x86_64/InterruptEntry.cpp - ) - - set(KERNEL_SOURCES - ${KERNEL_SOURCES} - - ${CMAKE_CURRENT_SOURCE_DIR}/Arch/x86_64/ASM_wrapper.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/Arch/x86_64/CPU.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/Arch/x86_64/CPUID.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/Arch/x86_64/InterruptManagement.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/Arch/x86_64/Interrupts.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/Arch/x86_64/PageDirectory.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/Arch/x86_64/Processor.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/Arch/x86_64/ProcessorInfo.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/Arch/x86_64/SafeMem.cpp - ) - - if("${SERENITY_ARCH}" STREQUAL "x86_64") - set(KERNEL_SOURCES - ${KERNEL_SOURCES} - ${CMAKE_CURRENT_SOURCE_DIR}/Arch/x86_64/SyscallEntry.cpp - ) - endif() -elseif("${SERENITY_ARCH}" STREQUAL "aarch64") - set(RPI_SOURCES - Arch/aarch64/RPi/DebugOutput.cpp - Arch/aarch64/RPi/Framebuffer.cpp - Arch/aarch64/RPi/GPIO.cpp - Arch/aarch64/RPi/InterruptController.cpp - Arch/aarch64/RPi/Mailbox.cpp - Arch/aarch64/RPi/MiniUART.cpp - Arch/aarch64/RPi/SDHostController.cpp - Arch/aarch64/RPi/Timer.cpp - Arch/aarch64/RPi/UART.cpp - Arch/aarch64/RPi/Watchdog.cpp - ) - set(SOURCES_RUNNING_WITHOUT_MMU - Arch/aarch64/Exceptions.cpp - Arch/aarch64/MMU.cpp - Arch/aarch64/pre_init.cpp - Arch/aarch64/RPi/MMIO.cpp - ) - set(KERNEL_SOURCES - ${KERNEL_SOURCES} - ${RPI_SOURCES} - ${SOURCES_RUNNING_WITHOUT_MMU} - - Arch/aarch64/Firmware/ACPI/StaticParsing.cpp - - Arch/aarch64/archctl.cpp - Arch/aarch64/boot.S - Arch/aarch64/BootPPMParser.cpp - Arch/aarch64/CPUID.cpp - Arch/aarch64/CurrentTime.cpp - Arch/aarch64/Dummy.cpp - Arch/aarch64/FPUState.S - Arch/aarch64/InterruptManagement.cpp - Arch/aarch64/Interrupts.cpp - Arch/aarch64/kprintf.cpp - Arch/aarch64/MainIdRegister.cpp - Arch/aarch64/PageDirectory.cpp - Arch/aarch64/Panic.cpp - Arch/aarch64/Processor.cpp - Arch/aarch64/PowerState.cpp - Arch/aarch64/SafeMem.cpp - Arch/aarch64/SmapDisabler.cpp - Arch/aarch64/vector_table.S - ) - - # Otherwise linker errors e.g undefined reference to `__aarch64_cas8_acq_rel' - add_compile_options(-mno-outline-atomics) - - # NOTE: These files cannot use a stack protector and sanitizers, as these will cause accesses to global variables to be inserted - # by the compiler. The CPU cannot access global variables without the MMU as the kernel is linked for a virtual address in high memory. - set_source_files_properties(${SOURCES_RUNNING_WITHOUT_MMU} PROPERTIES COMPILE_FLAGS "-fno-stack-protector -fno-sanitize=all") -elseif("${SERENITY_ARCH}" STREQUAL "riscv64") - set(SOURCES_RUNNING_WITHOUT_MMU - Arch/riscv64/MMU.cpp - Arch/riscv64/pre_init.cpp - - # FIXME: Don't disable stack protectors and sanitizers in SBI.cpp. - # Maybe implement SBI debug console printing directly in pre_init.cpp as well? - Arch/riscv64/SBI.cpp - ) - - set(KERNEL_SOURCES - ${KERNEL_SOURCES} - ${SOURCES_RUNNING_WITHOUT_MMU} - - Arch/Processor.cpp - kprintf.cpp - - Arch/riscv64/Firmware/ACPI/StaticParsing.cpp - - Arch/riscv64/archctl.cpp - Arch/riscv64/boot.S - Arch/riscv64/CPU.cpp - Arch/riscv64/CurrentTime.cpp - Arch/riscv64/DebugOutput.cpp - Arch/riscv64/Delay.cpp - Arch/riscv64/InterruptManagement.cpp - Arch/riscv64/Interrupts.cpp - Arch/riscv64/Interrupts/PLIC.cpp - Arch/riscv64/MMU.cpp - Arch/riscv64/PageDirectory.cpp - Arch/riscv64/Panic.cpp - Arch/riscv64/PCI/Initializer.cpp - Arch/riscv64/PowerState.cpp - Arch/riscv64/pre_init.cpp - Arch/riscv64/Processor.cpp - Arch/riscv64/SafeMem.cpp - Arch/riscv64/SBI.cpp - Arch/riscv64/SmapDisabler.cpp - Arch/riscv64/Timer.cpp - Arch/riscv64/trap_handler.S - ) - - # NOTE: These files cannot use a stack protector and sanitizers, as these will cause accesses to global variables to be inserted - # by the compiler. The CPU cannot access global variables without the MMU as the kernel is linked for a virtual address in high memory. - # On GCC, also prevent loops from being optimized into functions like memset, as those might use stack protectors and sanitizers. - # We also have to disable our -ftrivial-auto-var-init=pattern mitigation, as that would cause memsets to be inserted. - if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU") - set_source_files_properties(${SOURCES_RUNNING_WITHOUT_MMU} PROPERTIES COMPILE_FLAGS "-fno-stack-protector -fno-sanitize=all -ftrivial-auto-var-init=uninitialized -fno-tree-loop-distribution -fno-tree-loop-distribute-patterns") - else() - set_source_files_properties(${SOURCES_RUNNING_WITHOUT_MMU} PROPERTIES COMPILE_FLAGS "-fno-stack-protector -fno-sanitize=all -ftrivial-auto-var-init=uninitialized") - endif() -endif() - -set(AK_SOURCES - ../AK/DOSPackedTime.cpp - ../AK/GenericLexer.cpp - ../AK/Hex.cpp - ../AK/MemoryStream.cpp - ../AK/SipHash.cpp - ../AK/Stream.cpp - ../AK/StringBuilder.cpp - ../AK/StringUtils.cpp - ../AK/StringView.cpp - ../AK/Time.cpp - ../AK/Error.cpp - ../AK/Format.cpp - ../AK/UUID.cpp -) - -set(DT_SOURCES - ../Userland/Libraries/LibDeviceTree/DeviceTree.cpp - ../Userland/Libraries/LibDeviceTree/FlattenedDeviceTree.cpp - ../Userland/Libraries/LibDeviceTree/Validation.cpp -) - -set(EDID_SOURCES - ../Userland/Libraries/LibEDID/DMT.cpp - ../Userland/Libraries/LibEDID/EDID.cpp - ../Userland/Libraries/LibEDID/VIC.cpp -) - -set(ELF_SOURCES - ../Userland/Libraries/LibELF/Image.cpp - ../Userland/Libraries/LibELF/Validation.cpp -) - -add_custom_command( - COMMAND "${SerenityOS_SOURCE_DIR}/Kernel/generate-version-file.sh" "${CMAKE_CURRENT_BINARY_DIR}/Version.h.tmp" - COMMAND "${CMAKE_COMMAND}" -E copy_if_different "${CMAKE_CURRENT_BINARY_DIR}/Version.h.tmp" "${CMAKE_CURRENT_BINARY_DIR}/Version.h" - COMMAND "${CMAKE_COMMAND}" -E remove "${CMAKE_CURRENT_BINARY_DIR}/Version.h.tmp" - WORKING_DIRECTORY "${SerenityOS_SOURCE_DIR}" - COMMENT "Generating SerenityOS version information" - OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/Version.h" - VERBATIM -) -add_custom_target(generate_version_header DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/Version.h") -set(GENERATED_SOURCES "${CMAKE_CURRENT_BINARY_DIR}/Version.h") - -generate_state_machine(../Userland/Libraries/LibVT/StateMachine.txt ../Userland/Libraries/LibVT/EscapeSequenceStateMachine.h) - -set(VT_SOURCES - ../Userland/Libraries/LibVT/Terminal.cpp - ../Userland/Libraries/LibVT/Line.cpp - ../Userland/Libraries/LibVT/EscapeSequenceParser.cpp -) - -set(CRYPTO_SOURCES - ../Userland/Libraries/LibCrypto/Cipher/AES.cpp - ../Userland/Libraries/LibCrypto/Hash/SHA2.cpp -) - -set(PARTITION_SOURCES - ../Userland/Libraries/LibPartition/DiskPartitionMetadata.cpp - ../Userland/Libraries/LibPartition/EBRPartitionTable.cpp - ../Userland/Libraries/LibPartition/GUIDPartitionTable.cpp - ../Userland/Libraries/LibPartition/MBRPartitionTable.cpp - ../Userland/Libraries/LibPartition/PartitionableDevice.cpp - ../Userland/Libraries/LibPartition/PartitionTable.cpp -) - -add_compile_options(-fsigned-char) -add_compile_options(-Wno-unknown-warning-option -Wvla -Wnull-dereference) -add_compile_options(-fno-rtti -ffreestanding -fbuiltin) - -# We use __builtin_offsetof() on Processor, which inherits from ProcessorBase and is therefore a non-standard-layout type. -# This is an issue on non-Itanium ABIs (and in irrelevant edge cases), so we can safely ignore it. -add_compile_options(-Wno-invalid-offsetof) - -if ("${SERENITY_ARCH}" STREQUAL "x86_64") - add_compile_options(-mno-80387 -mno-mmx -mno-sse -mno-sse2) -elseif("${SERENITY_ARCH}" STREQUAL "aarch64") - add_compile_options(-mgeneral-regs-only) -endif() - -add_compile_options(-fno-asynchronous-unwind-tables) -add_compile_options(-fstack-protector-strong) -add_compile_options(-fno-exceptions) -add_compile_options(-nostdlib) - -# Auto initialize trivial types on the stack, we use "pattern" as -# it's the only option portable across compilers going forward. -# -# This is designed to help avoid uninitialized variables bugs and -# information disclosures coming from the kernel stack. -# -# FIXME: It appears to conflict with something during the boot of the -# aarch64 kernel, we should investigate and remove this special case. -if (NOT "${SERENITY_ARCH}" STREQUAL "aarch64") - add_compile_options(-ftrivial-auto-var-init=pattern) -endif() - -if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU") - # Apply any flags that are only available on >= GCC 11.1 - if (CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL "11.1") - # Zero any registers used within a function on return (to reduce data lifetime and ROP gadgets). - add_compile_options(-fzero-call-used-regs=used-gpr) - endif() - link_directories(${TOOLCHAIN_ROOT}/${SERENITY_ARCH}-pc-serenity/lib) - link_directories(${TOOLCHAIN_ROOT}/lib/gcc/${SERENITY_ARCH}-pc-serenity/${GCC_VERSION}/) - - set(TARGET_STRING "") - - - # Prevent naively implemented string functions (like strlen) from being "optimized" into a call to themselves. - set_source_files_properties(Library/MiniStdLib.cpp - PROPERTIES COMPILE_FLAGS "-fno-tree-loop-distribution -fno-tree-loop-distribute-patterns") - - if ("${SERENITY_ARCH}" STREQUAL "x86_64") - # The BFD linker only supports RELR relocations on x86 and POWER. - add_link_options(LINKER:-z,pack-relative-relocs) - endif() -else() # Assume Clang - add_compile_options(-Waddress-of-packed-member) - add_compile_options(-faligned-allocation) - - # We need this in order to pick up the #define __serenity__, otherwise we end up including unistd.h into the linker script - set(TARGET_STRING "--target=${CMAKE_CXX_COMPILER_TARGET}") - - add_link_options(LINKER:--build-id=none LINKER:--pack-dyn-relocs=relr) -endif() - -macro (set_new_alignment alignment) - if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU") - add_compile_options(-faligned-new=${alignment}) - elseif (CMAKE_CXX_COMPILER_ID MATCHES "Clang$") - add_compile_options(-fnew-alignment=${alignment}) - endif() -endmacro() - -if ("${SERENITY_ARCH}" STREQUAL "x86_64") - add_compile_options(-mno-red-zone) - set_new_alignment(8) -endif() - -set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -static-pie") - -# Kernel Coverage (KCOV) is an API to collect and expose program counters of -# kernel code that has been run to user space. It's rather slow and likely not -# secure to run in production builds. Useful for coverage guided fuzzing. -if (ENABLE_KERNEL_COVERAGE_COLLECTION) - add_compile_definitions(ENABLE_KERNEL_COVERAGE_COLLECTION) - if(ENABLE_KERNEL_COVERAGE_COLLECTION_DEBUG) - add_compile_definitions(ENABLE_KERNEL_COVERAGE_COLLECTION_DEBUG) - endif() - - set(KERNEL_SOURCES - ${KERNEL_SOURCES} - Devices/KCOVDevice.cpp - Devices/KCOVInstance.cpp - SanCov.cpp - ) - - add_compile_options(-fsanitize-coverage=trace-pc) - # To avoid __sanitizer_cov_trace_pc from causing an infinite recursion, - # don't instrument any even transitively used code. You can either skip - # individual function via the NO_SANITIZE_COVERAGE macro or by excluding - # the whole file by adding it to KCOV_EXCLUDED_SOURCES. - # Note that you can only add .cpp and not .h files to KCOV_EXCLUDED_SOURCES. - set(KCOV_EXCLUDED_SOURCES - # Skip core KCOV code. - ../Kernel/Devices/KCOVDevice.cpp - ../Kernel/Devices/KCOVInstance.cpp - ../Kernel/SanCov.cpp - # GCC assumes that the caller saves registers for functions according - # to the System V ABI and happily inserts coverage calls into the - # function prologue for all functions. This assumption is not true for - # interrupt handlers because their calling convention is not compatible - # with the System V ABI. - ../Kernel/Arch/x86_64/Interrupts.cpp - ../Kernel/Syscall/SyscallHandler.cpp - ) - set_source_files_properties(${KCOV_EXCLUDED_SOURCES} PROPERTIES COMPILE_FLAGS "-fno-sanitize-coverage=trace-pc") -endif() - -if (ENABLE_USERSPACE_COVERAGE_COLLECTION) - # Disable checking open() pledges and the veil for coverage data when building userspace with coverage - # so that binaries can write out coverage data even with pledges/veil - add_compile_definitions(SKIP_PATH_VALIDATION_FOR_COVERAGE_INSTRUMENTATION) -endif() - -if (ENABLE_KERNEL_UNDEFINED_SANITIZER) - # Kernel Undefined Behavior Sanitizer (KUBSAN) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=undefined") -endif() - -if (ENABLE_KERNEL_ADDRESS_SANITIZER) - add_compile_options(-fsanitize=kernel-address) - if(CMAKE_CXX_COMPILER_ID MATCHES "Clang$") - # TODO: Support inline KASAN for improved performance - add_compile_options("SHELL:-mllvm -asan-instrumentation-with-call-threshold=0") - # TODO: Support KASAN stack poisoning (inline) for use-after-return and use-after-scope detection - add_compile_options("SHELL:-mllvm -asan-stack=0") - endif() - set_source_files_properties(Security/AddressSanitizer.cpp PROPERTIES COMPILE_FLAGS "-fno-sanitize=kernel-address") - add_link_options(-fsanitize=kernel-address) -endif() - -if ("${SERENITY_ARCH}" STREQUAL "aarch64") - add_compile_options(-fno-threadsafe-statics) - - # Unaligned memory access will cause a trap, so to make sure the compiler doesn't generate - # those unaligned accesses, this flag is added. - add_compile_options(-mstrict-align -Wno-cast-align) -endif() - -set(SOURCES - ${KERNEL_SOURCES} - ${GENERATED_SOURCES} - ${AK_SOURCES} - ${DT_SOURCES} - ${EDID_SOURCES} - ${ELF_SOURCES} - ${VT_SOURCES} - ${CRYPTO_SOURCES} - ${PARTITION_SOURCES} -) - -add_compile_definitions(KERNEL) -add_link_options(LINKER:-z,notext) - -add_library(kernel_heap STATIC ${KERNEL_HEAP_SOURCES}) -add_dependencies(kernel_heap install_libc_headers) -target_link_libraries(kernel_heap PUBLIC GenericClangPlugin) - -add_executable(Kernel ${SOURCES}) -add_dependencies(Kernel generate_EscapeSequenceStateMachine.h generate_version_header install_libc_headers) -target_link_libraries(Kernel PUBLIC GenericClangPlugin) - -if("${SERENITY_ARCH}" STREQUAL "aarch64") - target_link_options(Kernel PRIVATE LINKER:-T ${CMAKE_CURRENT_SOURCE_DIR}/Arch/aarch64/linker.ld -nostdlib LINKER:--no-pie) - set_target_properties(Kernel PROPERTIES LINK_DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/Arch/aarch64/linker.ld) -elseif("${SERENITY_ARCH}" STREQUAL "riscv64") - # The final kernel binary for some reason includes temporary local symbols on riscv64 clang, which causes kernel.map to be too big to fit in its section in the kernel. - # Explicitly pass -X to the linker to remove them. - target_link_options(Kernel PRIVATE LINKER:-T ${CMAKE_CURRENT_SOURCE_DIR}/Arch/riscv64/linker.ld -nostdlib LINKER:--no-pie LINKER:-X) - - set_target_properties(Kernel PROPERTIES LINK_DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/Arch/riscv64/linker.ld) -elseif ("${SERENITY_ARCH}" STREQUAL "x86_64") - add_custom_command( - OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/linker.ld - COMMAND "${CMAKE_CXX_COMPILER}" ${TARGET_STRING} -E -P -x c -I${CMAKE_CURRENT_SOURCE_DIR}/.. "${CMAKE_CURRENT_SOURCE_DIR}/Arch/x86_64/linker.ld" -o "${CMAKE_CURRENT_BINARY_DIR}/linker.ld" - MAIN_DEPENDENCY "Arch/x86_64/linker.ld" - COMMENT "Preprocessing linker.ld" - VERBATIM - ) - add_custom_target(generate_kernel_linker_script DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/linker.ld) - target_link_options(Kernel PRIVATE LINKER:-T ${CMAKE_CURRENT_BINARY_DIR}/linker.ld -nostdlib -nodefaultlibs) - set_target_properties(Kernel PROPERTIES LINK_DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/linker.ld") - - if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU") - target_compile_options(Kernel PRIVATE -mpreferred-stack-boundary=3) - elseif(CMAKE_CXX_COMPILER_ID MATCHES "Clang$") - target_compile_options(Kernel PRIVATE -mstack-alignment=8) - endif() -endif() - -if (ENABLE_KERNEL_LTO) - include(CheckIPOSupported) - check_ipo_supported() - add_definitions(-DENABLE_KERNEL_LTO) - set_property(TARGET Kernel PROPERTY INTERPROCEDURAL_OPTIMIZATION TRUE) - if (NOT "${SERENITY_ARCH}" STREQUAL "aarch64") - set_property(TARGET kernel_heap PROPERTY INTERPROCEDURAL_OPTIMIZATION TRUE) - endif() -endif() - -if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU") - target_link_libraries(Kernel PRIVATE kernel_heap gcc) -elseif(CMAKE_CXX_COMPILER_ID MATCHES "Clang$") - target_link_libraries(Kernel PRIVATE kernel_heap clang_rt.builtins) -endif() - -if ("${SERENITY_ARCH}" STREQUAL "x86_64") - set(KERNEL_ELF_OBJCOPY_TARGET "elf64-x86-64") -endif() - -# In x86_64 the Kernel is linked to the Pre-kernel binary. -if ("${SERENITY_ARCH}" STREQUAL "x86_64") - add_custom_command( - TARGET Kernel POST_BUILD - COMMAND ${CMAKE_COMMAND} -E env NM=${CMAKE_NM} sh ${CMAKE_CURRENT_SOURCE_DIR}/mkmap.sh - COMMAND ${CMAKE_COMMAND} -E env OBJCOPY=${CMAKE_OBJCOPY} sh ${CMAKE_CURRENT_SOURCE_DIR}/embedmap.sh - COMMAND ${CMAKE_OBJCOPY} --only-keep-debug Kernel Kernel.debug - COMMAND ${CMAKE_OBJCOPY} --strip-debug Kernel - COMMAND ${CMAKE_OBJCOPY} --add-gnu-debuglink=Kernel.debug Kernel - COMMAND ${CMAKE_OBJCOPY} --set-section-flags .heap=load Kernel Kernel_shared_object - COMMAND ${CMAKE_OBJCOPY} -I binary -O ${KERNEL_ELF_OBJCOPY_TARGET} --rename-section .data=.Kernel_image --set-section-alignment .Kernel_image=4096 Kernel_shared_object Kernel.o - BYPRODUCTS ${CMAKE_CURRENT_BINARY_DIR}/Kernel.o ${CMAKE_CURRENT_BINARY_DIR}/kernel.map - ) -elseif ("${SERENITY_ARCH}" STREQUAL "aarch64" OR "${SERENITY_ARCH}" STREQUAL "riscv64") - add_custom_command( - TARGET Kernel POST_BUILD - COMMAND "${CMAKE_COMMAND}" -E env NM=${CMAKE_NM} sh ${CMAKE_CURRENT_SOURCE_DIR}/mkmap.sh - COMMAND "${CMAKE_COMMAND}" -E env OBJCOPY=${CMAKE_OBJCOPY} sh ${CMAKE_CURRENT_SOURCE_DIR}/embedmap.sh - COMMAND ${CMAKE_OBJCOPY} --only-keep-debug Kernel Kernel.debug - COMMAND ${CMAKE_OBJCOPY} --strip-debug Kernel - COMMAND ${CMAKE_OBJCOPY} --add-gnu-debuglink=Kernel.debug Kernel - BYPRODUCTS ${CMAKE_CURRENT_BINARY_DIR}/kernel.map - ) -endif() - -# Architectures (x86_64, aarch64, riscv64) share the same location in their respective architecture build folder. -# In x86_64 the Kernel is linked to the Pre-kernel binary and then generates the result Kernel binary. -# In aarch64 and riscv64 there's no Pre-kernel stage yet, so we immediately get a Kernel binary. -install(FILES "${CMAKE_CURRENT_BINARY_DIR}/Kernel" DESTINATION boot) - -install(FILES "${CMAKE_CURRENT_BINARY_DIR}/Kernel.debug" DESTINATION boot) -install(FILES "${CMAKE_CURRENT_BINARY_DIR}/kernel.map" DESTINATION res) - -if ("${SERENITY_ARCH}" STREQUAL "aarch64") - add_custom_command( - TARGET Kernel POST_BUILD - COMMAND ${CMAKE_OBJCOPY} -O binary Kernel kernel8.img - BYPRODUCTS ${CMAKE_CURRENT_BINARY_DIR}/kernel8.img - ) -elseif ("${SERENITY_ARCH}" STREQUAL "riscv64") - add_custom_command( - TARGET Kernel POST_BUILD - COMMAND ${CMAKE_OBJCOPY} -O binary Kernel Kernel.bin - BYPRODUCTS ${CMAKE_CURRENT_BINARY_DIR}/Kernel.bin - ) - install(FILES "${CMAKE_CURRENT_BINARY_DIR}/Kernel.bin" DESTINATION boot) -endif() - -serenity_install_headers(Kernel) -serenity_install_sources(Kernel) - -# Only x86 needs a Prekernel -if ("${SERENITY_ARCH}" STREQUAL "x86_64") - add_subdirectory(Prekernel) -endif() diff --git a/Kernel/Debug.h.in b/Kernel/Debug.h.in deleted file mode 100644 index 0810d2b7a9f..00000000000 --- a/Kernel/Debug.h.in +++ /dev/null @@ -1,356 +0,0 @@ -/* - * Copyright (c) 2021, Paul Scharnofske - * Copyright (c) 2022, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#ifndef AC97_DEBUG -#cmakedefine01 AC97_DEBUG -#endif - -#ifndef AHCI_DEBUG -#cmakedefine01 AHCI_DEBUG -#endif - -#ifndef ACPI_DEBUG -#cmakedefine01 ACPI_DEBUG -#endif - -#ifndef APIC_DEBUG -#cmakedefine01 APIC_DEBUG -#endif - -#ifndef APIC_SMP_DEBUG -#cmakedefine01 APIC_SMP_DEBUG -#endif - -#ifndef ARP_DEBUG -#cmakedefine01 ARP_DEBUG -#endif - -#ifndef ATA_DEBUG -#cmakedefine01 ATA_DEBUG -#endif - -#ifndef BBFS_DEBUG -#cmakedefine01 BBFS_DEBUG -#endif - -#ifndef BXVGA_DEBUG -#cmakedefine01 BXVGA_DEBUG -#endif - -#ifndef COMMIT_DEBUG -#cmakedefine01 COMMIT_DEBUG -#endif - -#ifndef CONTEXT_SWITCH_DEBUG -#cmakedefine01 CONTEXT_SWITCH_DEBUG -#endif - -#ifndef DUMP_REGIONS_ON_CRASH -#cmakedefine01 DUMP_REGIONS_ON_CRASH -#endif - -#ifndef E1000_DEBUG -#cmakedefine01 E1000_DEBUG -#endif - -#ifndef ETHERNET_DEBUG -#cmakedefine01 ETHERNET_DEBUG -#endif - -#ifndef EXEC_DEBUG -#cmakedefine01 EXEC_DEBUG -#endif - -#ifndef EXT2_BLOCKLIST_DEBUG -#cmakedefine01 EXT2_BLOCKLIST_DEBUG -#endif - -#ifndef EXT2_DEBUG -#cmakedefine01 EXT2_DEBUG -#endif - -#ifndef EXT2_VERY_DEBUG -#cmakedefine01 EXT2_VERY_DEBUG -#endif - -#ifndef FAT_DEBUG -#cmakedefine01 FAT_DEBUG -#endif - -#ifndef FORK_DEBUG -#cmakedefine01 FORK_DEBUG -#endif - -#ifndef FUSE_DEBUG -#cmakedefine01 FUSE_DEBUG -#endif - -#ifndef FUTEX_DEBUG -#cmakedefine01 FUTEX_DEBUG -#endif - -#ifndef FUTEXQUEUE_DEBUG -#cmakedefine01 FUTEXQUEUE_DEBUG -#endif - -#ifndef HPET_COMPARATOR_DEBUG -#cmakedefine01 HPET_COMPARATOR_DEBUG -#endif - -#ifndef HPET_DEBUG -#cmakedefine01 HPET_DEBUG -#endif - -#ifndef ICMP_DEBUG -#cmakedefine01 ICMP_DEBUG -#endif - -#ifndef INTEL_GRAPHICS_DEBUG -#cmakedefine01 INTEL_GRAPHICS_DEBUG -#endif - -#ifndef INTEL_HDA_DEBUG -#cmakedefine01 INTEL_HDA_DEBUG -#endif - -#ifndef INTERRUPT_DEBUG -#cmakedefine01 INTERRUPT_DEBUG -#endif - -#ifndef IO_DEBUG -#cmakedefine01 IO_DEBUG -#endif - -#ifndef IOAPIC_DEBUG -#cmakedefine01 IOAPIC_DEBUG -#endif - -#ifndef ISO9660_DEBUG -#cmakedefine01 ISO9660_DEBUG -#endif - -#ifndef ISO9660_VERY_DEBUG -#cmakedefine01 ISO9660_VERY_DEBUG -#endif - -#ifndef IPV4_DEBUG -#cmakedefine01 IPV4_DEBUG -#endif - -#ifndef IPV4_SOCKET_DEBUG -#cmakedefine01 IPV4_SOCKET_DEBUG -#endif - -#ifndef IRQ_DEBUG -#cmakedefine01 IRQ_DEBUG -#endif - -#ifndef KEYBOARD_DEBUG -#cmakedefine01 KEYBOARD_DEBUG -#endif - -#ifndef KMALLOC_DEBUG -#cmakedefine01 KMALLOC_DEBUG -#endif - -#ifndef KMALLOC_VERIFY_NO_SPINLOCK_HELD -#cmakedefine01 KMALLOC_VERIFY_NO_SPINLOCK_HELD -#endif - -#ifndef LOCAL_SOCKET_DEBUG -#cmakedefine01 LOCAL_SOCKET_DEBUG -#endif - -#ifndef LOCK_DEBUG -#cmakedefine01 LOCK_DEBUG -#endif - -#ifndef LOCK_IN_CRITICAL_DEBUG -#cmakedefine01 LOCK_IN_CRITICAL_DEBUG -#endif - -#ifndef LOCK_RANK_ENFORCEMENT -#cmakedefine01 LOCK_RANK_ENFORCEMENT -#endif - -#ifndef LOCK_RESTORE_DEBUG -#cmakedefine01 LOCK_RESTORE_DEBUG -#endif - -#ifndef LOCK_SHARED_UPGRADE_DEBUG -#cmakedefine01 LOCK_SHARED_UPGRADE_DEBUG -#endif - -#ifndef LOCK_TRACE_DEBUG -#cmakedefine01 LOCK_TRACE_DEBUG -#endif - -#ifndef LOOPBACK_DEBUG -#cmakedefine01 LOOPBACK_DEBUG -#endif - -#ifndef MASTERPTY_DEBUG -#cmakedefine01 MASTERPTY_DEBUG -#endif - -#ifndef MOUSE_DEBUG -#cmakedefine01 MOUSE_DEBUG -#endif - -#ifndef MEMORY_DEVICE_DEBUG -#cmakedefine01 MEMORY_DEVICE_DEBUG -#endif - -#ifndef MULTIPROCESSOR_DEBUG -#cmakedefine01 MULTIPROCESSOR_DEBUG -#endif - -#ifndef NETWORK_TASK_DEBUG -#cmakedefine01 NETWORK_TASK_DEBUG -#endif - -#ifndef NVME_DEBUG -#cmakedefine01 NVME_DEBUG -#endif - -#ifndef OFFD_DEBUG -#cmakedefine01 OFFD_DEBUG -#endif - -#ifndef PAGE_FAULT_DEBUG -#cmakedefine01 PAGE_FAULT_DEBUG -#endif - -#ifndef PATA_DEBUG -#cmakedefine01 PATA_DEBUG -#endif - -#ifndef PCI_DEBUG -#cmakedefine01 PCI_DEBUG -#endif - -#ifndef POLL_SELECT_DEBUG -#cmakedefine01 POLL_SELECT_DEBUG -#endif - -#ifndef PROCESS_DEBUG -#cmakedefine01 PROCESS_DEBUG -#endif - -#ifndef PROCFS_DEBUG -#cmakedefine01 PROCFS_DEBUG -#endif - -#ifndef PS2MOUSE_DEBUG -#cmakedefine01 PS2MOUSE_DEBUG -#endif - -#ifndef PTMX_DEBUG -#cmakedefine01 PTMX_DEBUG -#endif - -#ifndef ROUTING_DEBUG -#cmakedefine01 ROUTING_DEBUG -#endif - -#ifndef RTL8168_DEBUG -#cmakedefine01 RTL8168_DEBUG -#endif - -#ifndef SCHEDULER_DEBUG -#cmakedefine01 SCHEDULER_DEBUG -#endif - -#ifndef SCHEDULER_RUNNABLE_DEBUG -#cmakedefine01 SCHEDULER_RUNNABLE_DEBUG -#endif - -#ifndef SIGNAL_DEBUG -#cmakedefine01 SIGNAL_DEBUG -#endif - -#ifndef SLAVEPTY_DEBUG -#cmakedefine01 SLAVEPTY_DEBUG -#endif - -#ifndef SMP_DEBUG -#cmakedefine01 SMP_DEBUG -#endif - -#ifndef SOCKET_DEBUG -#cmakedefine01 SOCKET_DEBUG -#endif - -#ifndef STORAGE_DEVICE_DEBUG -#cmakedefine01 STORAGE_DEVICE_DEBUG -#endif - -#ifndef SYSFS_DEBUG -#cmakedefine01 SYSFS_DEBUG -#endif - -#ifndef TCP_DEBUG -#cmakedefine01 TCP_DEBUG -#endif - -#ifndef TCP_SOCKET_DEBUG -#cmakedefine01 TCP_SOCKET_DEBUG -#endif - -#ifndef TDFX_DEBUG -#cmakedefine01 TDFX_DEBUG -#endif - -#ifndef THREAD_DEBUG -#cmakedefine01 THREAD_DEBUG -#endif - -#ifndef TTY_DEBUG -#cmakedefine01 TTY_DEBUG -#endif - -#ifndef UDP_DEBUG -#cmakedefine01 UDP_DEBUG -#endif - -#ifndef UHCI_DEBUG -#cmakedefine01 UHCI_DEBUG -#endif - -#ifndef UHCI_VERBOSE_DEBUG -#cmakedefine01 UHCI_VERBOSE_DEBUG -#endif - -#ifndef USB_DEBUG -#cmakedefine01 USB_DEBUG -#endif - -#ifndef VFS_DEBUG -#cmakedefine01 VFS_DEBUG -#endif - -#ifndef VMWARE_BACKDOOR_DEBUG -#cmakedefine01 VMWARE_BACKDOOR_DEBUG -#endif - -#ifndef VIRTIO_DEBUG -#cmakedefine01 VIRTIO_DEBUG -#endif - -#ifndef VIRTUAL_CONSOLE_DEBUG -#cmakedefine01 VIRTUAL_CONSOLE_DEBUG -#endif - -#ifndef WAITBLOCK_DEBUG -#cmakedefine01 WAITBLOCK_DEBUG -#endif - -#ifndef WAITQUEUE_DEBUG -#cmakedefine01 WAITQUEUE_DEBUG -#endif diff --git a/Kernel/Devices/AsyncDeviceRequest.cpp b/Kernel/Devices/AsyncDeviceRequest.cpp deleted file mode 100644 index a45f70dea03..00000000000 --- a/Kernel/Devices/AsyncDeviceRequest.cpp +++ /dev/null @@ -1,149 +0,0 @@ -/* - * Copyright (c) 2020, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include - -namespace Kernel { - -AsyncDeviceRequest::AsyncDeviceRequest(Device& device) - : m_device(device) - , m_process(Process::current()) -{ -} - -AsyncDeviceRequest::~AsyncDeviceRequest() -{ - { - SpinlockLocker lock(m_lock); - VERIFY(is_completed_result(m_result)); - VERIFY(m_sub_requests_pending.is_empty()); - } - - // We should not need any locking here anymore. The destructor should - // only be called until either wait() or cancel() (once implemented) returned. - // At that point no sub-request should be adding more requests and all - // sub-requests should be completed (either succeeded, failed, or cancelled). - // Which means there should be no more pending sub-requests and the - // entire AsyncDeviceRequest hierarchy should be immutable. - while (!m_sub_requests_complete.is_empty()) { - // Note: sub_request is ref-counted, and we use this specific pattern - // to allow make sure the refcount is dropped properly. - auto sub_request = m_sub_requests_complete.take_first(); - VERIFY(is_completed_result(sub_request->m_result)); // Shouldn't need any locking anymore - VERIFY(sub_request->m_parent_request == this); - sub_request->m_parent_request = nullptr; - } -} - -void AsyncDeviceRequest::request_finished() -{ - if (m_parent_request) - m_parent_request->sub_request_finished(*this); - - // Trigger processing the next request - m_device.process_next_queued_request({}, *this); - - // Wake anyone who may be waiting - m_queue.wake_all(); -} - -auto AsyncDeviceRequest::wait(Duration* timeout) -> RequestWaitResult -{ - VERIFY(!m_parent_request); - auto request_result = get_request_result(); - if (is_completed_result(request_result)) - return { request_result, Thread::BlockResult::NotBlocked }; - auto wait_result = m_queue.wait_on(Thread::BlockTimeout(false, timeout), name()); - return { get_request_result(), wait_result }; -} - -auto AsyncDeviceRequest::get_request_result() const -> RequestResult -{ - SpinlockLocker lock(m_lock); - return m_result; -} - -void AsyncDeviceRequest::add_sub_request(NonnullLockRefPtr sub_request) -{ - // Sub-requests cannot be for the same device - VERIFY(&m_device != &sub_request->m_device); - VERIFY(sub_request->m_parent_request == nullptr); - sub_request->m_parent_request = this; - - SpinlockLocker lock(m_lock); - VERIFY(!is_completed_result(m_result)); - m_sub_requests_pending.append(sub_request); - if (m_result == Started) - sub_request->do_start(move(lock)); -} - -void AsyncDeviceRequest::sub_request_finished(AsyncDeviceRequest& sub_request) -{ - bool all_completed; - { - SpinlockLocker lock(m_lock); - VERIFY(m_result == Started); - - if (m_sub_requests_pending.contains(sub_request)) { - // Note: append handles removing from any previous intrusive list internally. - m_sub_requests_complete.append(sub_request); - } - - all_completed = m_sub_requests_pending.is_empty(); - if (all_completed) { - // Aggregate any errors - bool any_failures = false; - bool any_memory_faults = false; - for (auto& com_sub_request : m_sub_requests_complete) { - auto sub_result = com_sub_request.get_request_result(); - VERIFY(is_completed_result(sub_result)); - switch (sub_result) { - case Failure: - any_failures = true; - break; - case MemoryFault: - any_memory_faults = true; - break; - default: - break; - } - if (any_failures && any_memory_faults) - break; // Stop checking if all error conditions were found - } - if (any_failures) - m_result = Failure; - else if (any_memory_faults) - m_result = MemoryFault; - else - m_result = Success; - } - } - if (all_completed) - request_finished(); -} - -void AsyncDeviceRequest::complete(RequestResult result) -{ - VERIFY(result == Success || result == Failure || result == MemoryFault); - ScopedCritical critical; - { - SpinlockLocker lock(m_lock); - VERIFY(m_result == Started); - m_result = result; - } - if (Processor::current_in_irq()) { - ref(); // Make sure we don't get freed - Processor::deferred_call_queue([this]() { - request_finished(); - unref(); - }); - } else { - request_finished(); - } -} - -} diff --git a/Kernel/Devices/AsyncDeviceRequest.h b/Kernel/Devices/AsyncDeviceRequest.h deleted file mode 100644 index 4652c222dc5..00000000000 --- a/Kernel/Devices/AsyncDeviceRequest.h +++ /dev/null @@ -1,169 +0,0 @@ -/* - * Copyright (c) 2020, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include -#include -#include - -namespace Kernel { - -class Device; - -extern WorkQueue* g_io_work; - -class AsyncDeviceRequest : public AtomicRefCounted { - AK_MAKE_NONCOPYABLE(AsyncDeviceRequest); - AK_MAKE_NONMOVABLE(AsyncDeviceRequest); - -public: - enum [[nodiscard]] RequestResult { - Pending = 0, - Started, - Success, - Failure, - MemoryFault, - OutOfMemory, - Cancelled - }; - - class RequestWaitResult { - friend class AsyncDeviceRequest; - - public: - RequestResult request_result() const { return m_request_result; } - Thread::BlockResult wait_result() const { return m_wait_result; } - - private: - RequestWaitResult(RequestResult request_result, Thread::BlockResult wait_result) - : m_request_result(request_result) - , m_wait_result(wait_result) - { - } - - RequestResult m_request_result; - Thread::BlockResult m_wait_result; - }; - - virtual ~AsyncDeviceRequest(); - - virtual StringView name() const = 0; - virtual void start() = 0; - - void add_sub_request(NonnullLockRefPtr); - - [[nodiscard]] RequestWaitResult wait(Duration* = nullptr); - - void do_start(SpinlockLocker>&& requests_lock) - { - if (is_completed_result(m_result)) - return; - m_result = Started; - requests_lock.unlock(); - - start(); - } - - void complete(RequestResult result); - - void set_private(void* priv) - { - VERIFY(!m_private || !priv); - m_private = priv; - } - void* get_private() const { return m_private; } - - template - ErrorOr write_to_buffer(UserOrKernelBuffer& buffer, Args... args) - { - auto process = m_process.strong_ref(); - if (!process) - return Error::from_errno(ESRCH); - if (in_target_context(*process, buffer)) - return buffer.write(forward(args)...); - ScopedAddressSpaceSwitcher switcher(*process); - return buffer.write(forward(args)...); - } - - template - ErrorOr write_to_buffer_buffered(UserOrKernelBuffer& buffer, Args... args) - { - auto process = m_process.strong_ref(); - if (!process) - return Error::from_errno(ESRCH); - if (in_target_context(*process, buffer)) - return buffer.write_buffered(forward(args)...); - ScopedAddressSpaceSwitcher switcher(*process); - return buffer.write_buffered(forward(args)...); - } - - template - ErrorOr read_from_buffer(UserOrKernelBuffer const& buffer, Args... args) - { - auto process = m_process.strong_ref(); - if (!process) - return Error::from_errno(ESRCH); - if (in_target_context(*process, buffer)) - return buffer.read(forward(args)...); - ScopedAddressSpaceSwitcher switcher(*process); - return buffer.read(forward(args)...); - } - - template - ErrorOr read_from_buffer_buffered(UserOrKernelBuffer const& buffer, Args... args) - { - auto process = m_process.strong_ref(); - if (!process) - return Error::from_errno(ESRCH); - if (in_target_context(*process, buffer)) - return buffer.read_buffered(forward(args)...); - ScopedAddressSpaceSwitcher switcher(*process); - return buffer.read_buffered(forward(args)...); - } - -protected: - AsyncDeviceRequest(Device&); - - RequestResult get_request_result() const; - -private: - void sub_request_finished(AsyncDeviceRequest&); - void request_finished(); - - [[nodiscard]] bool in_target_context(Process& process, UserOrKernelBuffer const& buffer) const - { - if (buffer.is_kernel_buffer()) - return true; - return &process == &Process::current(); - } - - [[nodiscard]] static bool is_completed_result(RequestResult result) - { - return result > Started; - } - - Device& m_device; - - AsyncDeviceRequest* m_parent_request { nullptr }; - RequestResult m_result { Pending }; - IntrusiveListNode> m_list_node; - - using AsyncDeviceSubRequestList = IntrusiveList<&AsyncDeviceRequest::m_list_node>; - - AsyncDeviceSubRequestList m_sub_requests_pending; - AsyncDeviceSubRequestList m_sub_requests_complete; - WaitQueue m_queue; - LockWeakPtr const m_process; - void* m_private { nullptr }; - mutable Spinlock m_lock {}; -}; - -} diff --git a/Kernel/Devices/Audio/AC97/AC97.cpp b/Kernel/Devices/Audio/AC97/AC97.cpp deleted file mode 100644 index f505a0a58f4..00000000000 --- a/Kernel/Devices/Audio/AC97/AC97.cpp +++ /dev/null @@ -1,354 +0,0 @@ -/* - * Copyright (c) 2021-2022, Jelle Raaijmakers - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include -#include - -namespace Kernel { - -static constexpr int buffer_descriptor_list_max_entries = 32; - -static constexpr u16 pcm_fixed_sample_rate = 48000; - -// Valid output range - with double-rate enabled, sample rate can go up to 96kHZ -static constexpr u16 pcm_sample_rate_minimum = 8000; -static constexpr u16 pcm_sample_rate_maximum = 48000; - -UNMAP_AFTER_INIT ErrorOr> AC97::create(PCI::DeviceIdentifier const& pci_device_identifier) -{ - auto mixer_io_window = TRY(IOWindow::create_for_pci_device_bar(pci_device_identifier, PCI::HeaderType0BaseRegister::BAR0)); - auto bus_io_window = TRY(IOWindow::create_for_pci_device_bar(pci_device_identifier, PCI::HeaderType0BaseRegister::BAR1)); - - auto pcm_out_channel_io_window = TRY(bus_io_window->create_from_io_window_with_offset(NativeAudioBusChannel::PCMOutChannel)); - auto pcm_out_channel = TRY(AC97Channel::create_with_parent_pci_device(pci_device_identifier.address(), "PCMOut"sv, move(pcm_out_channel_io_window))); - - return TRY(adopt_nonnull_ref_or_enomem(new (nothrow) AC97(pci_device_identifier, move(pcm_out_channel), move(mixer_io_window), move(bus_io_window)))); -} - -UNMAP_AFTER_INIT ErrorOr AC97::probe(PCI::DeviceIdentifier const& device_identifier) -{ - VERIFY(device_identifier.class_code() == PCI::ClassID::Multimedia); - - // TODO: Check pci ids. - - if (PCI::get_BAR_space_size(device_identifier, PCI::HeaderType0BaseRegister::BAR0) <= NativeAudioMixerRegister::MaxUsedMixerOffset) - return Error::from_errno(EIO); - - // BAR registers are 32-bit. So if BAR0 is 64-bit then - // it occupies BAR0 and BAR1 and hence BAR1 isn't present on its own. - u64 pci_bar0_value = PCI::get_BAR(device_identifier, PCI::HeaderType0BaseRegister::BAR0); - if (PCI::get_BAR_space_type(pci_bar0_value) == PCI::BARSpaceType::Memory64BitSpace) - return Error::from_errno(EIO); - - if (PCI::get_BAR_space_size(device_identifier, PCI::HeaderType0BaseRegister::BAR1) <= NativeAudioBusRegister::MaxUsedBusOffset) - return Error::from_errno(EIO); - - return device_identifier.subclass_code() == PCI::Multimedia::SubclassID::Audio; -} - -UNMAP_AFTER_INIT AC97::AC97(PCI::DeviceIdentifier const& pci_device_identifier, NonnullOwnPtr pcm_out_channel, NonnullOwnPtr mixer_io_window, NonnullOwnPtr bus_io_window) - : PCI::Device(const_cast(pci_device_identifier)) - , IRQHandler(pci_device_identifier.interrupt_line().value()) - , m_mixer_io_window(move(mixer_io_window)) - , m_bus_io_window(move(bus_io_window)) - , m_pcm_out_channel(move(pcm_out_channel)) -{ -} - -UNMAP_AFTER_INIT AC97::~AC97() = default; - -bool AC97::handle_irq(RegisterState const&) -{ - auto pcm_out_status = m_pcm_out_channel->io_window().read16(AC97Channel::Register::Status); - dbgln_if(AC97_DEBUG, "AC97 @ {}: interrupt received - status: {:#05b}", device_identifier().address(), pcm_out_status); - - bool is_dma_halted = (pcm_out_status & AudioStatusRegisterFlag::DMAControllerHalted) > 0; - bool current_equals_last_valid = (pcm_out_status & AudioStatusRegisterFlag::CurrentEqualsLastValid) > 0; - bool is_completion_interrupt = (pcm_out_status & AudioStatusRegisterFlag::BufferCompletionInterruptStatus) > 0; - bool is_fifo_error = (pcm_out_status & AudioStatusRegisterFlag::FIFOError) > 0; - VERIFY(!is_fifo_error); - - // If there is no buffer completion, we're not going to do anything - if (!is_completion_interrupt) - return false; - - // On interrupt, we need to reset PCM interrupt flags by setting their bits - pcm_out_status = AudioStatusRegisterFlag::LastValidBufferCompletionInterrupt - | AudioStatusRegisterFlag::BufferCompletionInterruptStatus - | AudioStatusRegisterFlag::FIFOError; - m_pcm_out_channel->io_window().write16(AC97Channel::Register::Status, pcm_out_status); - - if (is_dma_halted) { - VERIFY(current_equals_last_valid); - m_pcm_out_channel->handle_dma_stopped(); - } - - if (!m_irq_queue.is_empty()) - m_irq_queue.wake_all(); - - return true; -} - -UNMAP_AFTER_INIT ErrorOr AC97::initialize(Badge) -{ - dbgln_if(AC97_DEBUG, "AC97 @ {}: mixer base: {:#04x}", device_identifier().address(), m_mixer_io_window); - dbgln_if(AC97_DEBUG, "AC97 @ {}: bus base: {:#04x}", device_identifier().address(), m_bus_io_window); - - // Read out AC'97 codec revision and vendor - auto extended_audio_id = m_mixer_io_window->read16(NativeAudioMixerRegister::ExtendedAudioID); - m_codec_revision = static_cast(((extended_audio_id & ExtendedAudioMask::Revision) >> 10) & 0b11); - dbgln_if(AC97_DEBUG, "AC97 @ {}: codec revision {:#02b}", device_identifier().address(), to_underlying(m_codec_revision)); - if (m_codec_revision == AC97Revision::Reserved) - return ENOTSUP; - - // Report vendor / device ID - u32 vendor_id = m_mixer_io_window->read16(NativeAudioMixerRegister::VendorID1) << 16 | m_mixer_io_window->read16(NativeAudioMixerRegister::VendorID2); - dmesgln_pci(*this, "Vendor ID: {:#8x}", vendor_id); - - // Bus cold reset, enable interrupts - enable_pin_based_interrupts(); - PCI::enable_bus_mastering(device_identifier()); - auto control = m_bus_io_window->read32(NativeAudioBusRegister::GlobalControl); - control |= GlobalControlFlag::GPIInterruptEnable; - control |= GlobalControlFlag::AC97ColdReset; - m_bus_io_window->write32(NativeAudioBusRegister::GlobalControl, control); - - // Reset mixer - m_mixer_io_window->write16(NativeAudioMixerRegister::Reset, 1); - - // Enable variable and double rate PCM audio if supported - auto extended_audio_status = m_mixer_io_window->read16(NativeAudioMixerRegister::ExtendedAudioStatusControl); - if ((extended_audio_id & ExtendedAudioMask::VariableRatePCMAudio) > 0) { - extended_audio_status |= ExtendedAudioStatusControlFlag::VariableRateAudio; - m_variable_rate_pcm_supported = true; - } - if (!m_variable_rate_pcm_supported) { - extended_audio_status &= ~ExtendedAudioStatusControlFlag::DoubleRateAudio; - } else if ((extended_audio_id & ExtendedAudioMask::DoubleRatePCMAudio) > 0) { - extended_audio_status |= ExtendedAudioStatusControlFlag::DoubleRateAudio; - m_double_rate_pcm_enabled = true; - } - m_mixer_io_window->write16(NativeAudioMixerRegister::ExtendedAudioStatusControl, extended_audio_status); - - // Get the device's current sample rate - m_sample_rate = read_pcm_output_sample_rate(); - - // Left and right volume of 0 means attenuation of 0 dB - set_master_output_volume(0, 0, Muted::No); - set_pcm_output_volume(0, 0, Muted::No); - - m_pcm_out_channel->reset(); - enable_irq(); - - m_audio_channel = TRY(AudioChannel::create(*this, 0)); - return {}; -} - -void AC97::set_master_output_volume(u8 left_channel, u8 right_channel, Muted mute) -{ - u16 volume_value = ((right_channel & 63) << 0) - | ((left_channel & 63) << 8) - | ((mute == Muted::Yes ? 1 : 0) << 15); - m_mixer_io_window->write16(NativeAudioMixerRegister::SetMasterOutputVolume, volume_value); -} - -u32 AC97::read_pcm_output_sample_rate() -{ - auto const double_rate_shift = m_double_rate_pcm_enabled ? 1 : 0; - return static_cast(m_mixer_io_window->read16(NativeAudioMixerRegister::PCMFrontDACRate)) << double_rate_shift; -} - -ErrorOr AC97::set_pcm_output_sample_rate(u32 sample_rate) -{ - if (m_sample_rate == sample_rate) - return {}; - - auto const double_rate_shift = m_double_rate_pcm_enabled ? 1 : 0; - auto shifted_sample_rate = sample_rate >> double_rate_shift; - if (!m_variable_rate_pcm_supported && shifted_sample_rate != pcm_fixed_sample_rate) - return ENOTSUP; - if (shifted_sample_rate < pcm_sample_rate_minimum || shifted_sample_rate > pcm_sample_rate_maximum) - return ENOTSUP; - - m_mixer_io_window->write16(NativeAudioMixerRegister::PCMFrontDACRate, shifted_sample_rate); - m_sample_rate = read_pcm_output_sample_rate(); - - dmesgln_pci(*this, "PCM front DAC rate set to {} Hz", m_sample_rate); - - // Setting the sample rate stops a running DMA engine, so restart it - if (m_pcm_out_channel->dma_running()) - m_pcm_out_channel->start_dma(); - - return {}; -} - -void AC97::set_pcm_output_volume(u8 left_channel, u8 right_channel, Muted mute) -{ - u16 volume_value = ((right_channel & 31) << 0) - | ((left_channel & 31) << 8) - | ((mute == Muted::Yes ? 1 : 0) << 15); - m_mixer_io_window->write16(NativeAudioMixerRegister::SetPCMOutputVolume, volume_value); -} - -RefPtr AC97::audio_channel(u32 index) const -{ - if (index == 0) - return m_audio_channel; - return {}; -} - -ErrorOr AC97::set_pcm_output_sample_rate(size_t channel_index, u32 samples_per_second_rate) -{ - if (channel_index != 0) - return ENODEV; - TRY(set_pcm_output_sample_rate(samples_per_second_rate)); - return {}; -} - -ErrorOr AC97::get_pcm_output_sample_rate(size_t channel_index) -{ - if (channel_index != 0) - return Error::from_errno(ENODEV); - return m_sample_rate; -} - -ErrorOr AC97::write(size_t channel_index, UserOrKernelBuffer const& data, size_t length) -{ - if (channel_index != 0) - return Error::from_errno(ENODEV); - - if (!m_output_buffer) - m_output_buffer = TRY(MM.allocate_dma_buffer_pages(m_output_buffer_page_count * PAGE_SIZE, "AC97 Output buffer"sv, Memory::Region::Access::Write)); - - if (!m_buffer_descriptor_list) { - size_t buffer_descriptor_list_size = buffer_descriptor_list_max_entries * sizeof(BufferDescriptorListEntry); - buffer_descriptor_list_size = TRY(Memory::page_round_up(buffer_descriptor_list_size)); - m_buffer_descriptor_list = TRY(MM.allocate_dma_buffer_pages(buffer_descriptor_list_size, "AC97 Buffer Descriptor List"sv, Memory::Region::Access::Write)); - } - - Checked remaining = length; - size_t offset = 0; - while (remaining > static_cast(0)) { - TRY(write_single_buffer(data, offset, min(remaining.value(), PAGE_SIZE))); - offset += PAGE_SIZE; - remaining.saturating_sub(PAGE_SIZE); - } - - return length; -} - -ErrorOr AC97::write_single_buffer(UserOrKernelBuffer const& data, size_t offset, size_t length) -{ - VERIFY(length <= PAGE_SIZE); - - { - // Block until we can write into an unused buffer - InterruptDisabler disabler; - do { - auto pcm_out_status = m_pcm_out_channel->io_window().read16(AC97Channel::Register::Status); - auto current_index = m_pcm_out_channel->io_window().read8(AC97Channel::Register::CurrentIndexValue); - int last_valid_index = m_pcm_out_channel->io_window().read8(AC97Channel::Register::LastValidIndex); - - auto head_distance = last_valid_index - current_index; - if (head_distance < 0) - head_distance += buffer_descriptor_list_max_entries; - if (m_pcm_out_channel->dma_running()) - ++head_distance; - - // Current index has _passed_ last valid index - move our list index up - if (head_distance > m_output_buffer_page_count) { - m_buffer_descriptor_list_index = current_index + 1; - break; - } - - // There is room for our data - if (head_distance < m_output_buffer_page_count) - break; - - dbgln_if(AC97_DEBUG, "AC97 @ {}: waiting on interrupt - status: {:#05b} CI: {} LVI: {}", device_identifier().address(), pcm_out_status, current_index, last_valid_index); - m_irq_queue.wait_forever("AC97"sv); - } while (m_pcm_out_channel->dma_running()); - } - // Copy data from userspace into one of our buffers - TRY(data.read(m_output_buffer->vaddr_from_page_index(m_output_buffer_page_index).as_ptr(), offset, length)); - - // Write the next entry to the buffer descriptor list - u16 number_of_samples = length / sizeof(u16); - auto list_entries = reinterpret_cast(m_buffer_descriptor_list->vaddr().get()); - auto list_entry = &list_entries[m_buffer_descriptor_list_index]; - list_entry->buffer_pointer = static_cast(m_output_buffer->physical_page(m_output_buffer_page_index)->paddr().get()); - list_entry->control_and_length = number_of_samples | BufferDescriptorListEntryFlags::InterruptOnCompletion; - - auto buffer_address = static_cast(m_buffer_descriptor_list->physical_page(0)->paddr().get()); - m_pcm_out_channel->set_last_valid_index(buffer_address, m_buffer_descriptor_list_index); - - if (!m_pcm_out_channel->dma_running()) - m_pcm_out_channel->start_dma(); - - m_output_buffer_page_index = (m_output_buffer_page_index + 1) % m_output_buffer_page_count; - m_buffer_descriptor_list_index = (m_buffer_descriptor_list_index + 1) % buffer_descriptor_list_max_entries; - - return {}; -} - -ErrorOr> AC97::AC97Channel::create_with_parent_pci_device(PCI::Address pci_device_address, StringView name, NonnullOwnPtr channel_io_base) -{ - return adopt_nonnull_own_or_enomem(new (nothrow) AC97::AC97Channel(pci_device_address, name, move(channel_io_base))); -} - -void AC97::AC97Channel::handle_dma_stopped() -{ - dbgln_if(AC97_DEBUG, "AC97 @ {}: channel {}: DMA engine has stopped", m_device_pci_address, name()); - m_dma_running.with([this](auto& dma_running) { - // NOTE: QEMU might send spurious interrupts while we're not running, so we don't want to panic here. - if (!dma_running) - dbgln("AC97 @ {}: received DMA interrupt while it wasn't running", m_device_pci_address); - dma_running = false; - }); -} - -void AC97::AC97Channel::reset() -{ - dbgln_if(AC97_DEBUG, "AC97 @ {}: channel {}: resetting", m_device_pci_address, name()); - - m_channel_io_window->write8(Register::Control, AudioControlRegisterFlag::ResetRegisters); - - while ((m_channel_io_window->read8(Register::Control) & AudioControlRegisterFlag::ResetRegisters) > 0) - microseconds_delay(50); - - m_dma_running.with([](auto& dma_running) { - dma_running = false; - }); -} - -void AC97::AC97Channel::set_last_valid_index(u32 buffer_address, u8 last_valid_index) -{ - dbgln_if(AC97_DEBUG, "AC97 @ {}: channel {}: setting buffer address: {:#x} LVI: {}", m_device_pci_address, name(), buffer_address, last_valid_index); - - m_channel_io_window->write32(Register::BufferDescriptorListBaseAddress, buffer_address); - m_channel_io_window->write8(Register::LastValidIndex, last_valid_index); -} - -void AC97::AC97Channel::start_dma() -{ - dbgln_if(AC97_DEBUG, "AC97 @ {}: channel {}: starting DMA engine", m_device_pci_address, name()); - - auto control = m_channel_io_window->read8(Register::Control); - control |= AudioControlRegisterFlag::RunPauseBusMaster; - control |= AudioControlRegisterFlag::FIFOErrorInterruptEnable; - control |= AudioControlRegisterFlag::InterruptOnCompletionEnable; - m_channel_io_window->write8(Register::Control, control); - - m_dma_running.with([](auto& dma_running) { - dma_running = true; - }); -} - -} diff --git a/Kernel/Devices/Audio/AC97/AC97.h b/Kernel/Devices/Audio/AC97/AC97.h deleted file mode 100644 index bf072dd8e10..00000000000 --- a/Kernel/Devices/Audio/AC97/AC97.h +++ /dev/null @@ -1,195 +0,0 @@ -/* - * Copyright (c) 2021-2022, Jelle Raaijmakers - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace Kernel { - -// See: https://www-inst.eecs.berkeley.edu/~cs150/Documents/ac97_r23.pdf -// And: https://www.intel.com/content/dam/doc/manual/io-controller-hub-7-hd-audio-ac97-manual.pdf - -class AC97 final - : public AudioController - , public PCI::Device - , public IRQHandler { - -public: - static ErrorOr probe(PCI::DeviceIdentifier const&); - static ErrorOr> create(PCI::DeviceIdentifier const&); - - virtual ~AC97() override; - - // ^PCI::Device - virtual StringView device_name() const override { return "AC97"sv; } - - // ^IRQHandler - virtual StringView purpose() const override { return "AC97"sv; } - -private: - enum NativeAudioMixerRegister : u8 { - Reset = 0x00, - SetMasterOutputVolume = 0x02, - SetPCMOutputVolume = 0x18, - ExtendedAudioID = 0x28, - ExtendedAudioStatusControl = 0x2a, - PCMFrontDACRate = 0x2c, - VendorID1 = 0x7c, - VendorID2 = 0x7e, - MaxUsedMixerOffset = 0x7f, - }; - - enum ExtendedAudioMask : u16 { - VariableRatePCMAudio = 1 << 0, - DoubleRatePCMAudio = 1 << 1, - Revision = 3 << 10, - }; - - enum ExtendedAudioStatusControlFlag : u16 { - VariableRateAudio = 1 << 0, - DoubleRateAudio = 1 << 1, - }; - - enum AC97Revision : u8 { - Revision21OrEarlier = 0b00, - Revision22 = 0b01, - Revision23 = 0b10, - Reserved = 0b11, - }; - - enum NativeAudioBusChannel : u8 { - PCMInChannel = 0x00, - PCMOutChannel = 0x10, - MicrophoneInChannel = 0x20, - Microphone2Channel = 0x40, - PCMIn2Channel = 0x50, - SPDIFChannel = 0x60, - }; - - enum NativeAudioBusRegister : u8 { - GlobalControl = 0x2c, - MaxUsedBusOffset = 0x2f - }; - - enum AudioStatusRegisterFlag : u16 { - DMAControllerHalted = 1 << 0, - CurrentEqualsLastValid = 1 << 1, - LastValidBufferCompletionInterrupt = 1 << 2, - BufferCompletionInterruptStatus = 1 << 3, - FIFOError = 1 << 4, - }; - - enum AudioControlRegisterFlag : u8 { - RunPauseBusMaster = 1 << 0, - ResetRegisters = 1 << 1, - FIFOErrorInterruptEnable = 1 << 3, - InterruptOnCompletionEnable = 1 << 4, - }; - - enum GlobalControlFlag : u32 { - GPIInterruptEnable = 1 << 0, - AC97ColdReset = 1 << 1, - }; - - enum Muted { - Yes, - No, - }; - - struct BufferDescriptorListEntry { - u32 buffer_pointer; - u32 control_and_length; - }; - - enum BufferDescriptorListEntryFlags : u32 { - BufferUnderrunPolicy = 1 << 30, - InterruptOnCompletion = 1u << 31, - }; - - class AC97Channel { - public: - enum Register : u8 { - BufferDescriptorListBaseAddress = 0x00, - CurrentIndexValue = 0x04, - LastValidIndex = 0x05, - Status = 0x06, - PositionInCurrentBuffer = 0x08, - PrefetchedIndexValue = 0x0a, - Control = 0x0b, - }; - - static ErrorOr> create_with_parent_pci_device(PCI::Address pci_device_address, StringView name, NonnullOwnPtr channel_io_base); - - bool dma_running() const - { - return m_dma_running.with([](auto value) { return value; }); - } - void handle_dma_stopped(); - StringView name() const { return m_name; } - void reset(); - void set_last_valid_index(u32 buffer_address, u8 last_valid_index); - void start_dma(); - - IOWindow& io_window() { return *m_channel_io_window; } - - private: - AC97Channel(PCI::Address pci_device_address, StringView name, NonnullOwnPtr channel_io_base) - : m_channel_io_window(move(channel_io_base)) - , m_device_pci_address(pci_device_address) - , m_name(name) - { - } - - NonnullOwnPtr m_channel_io_window; - PCI::Address m_device_pci_address; - SpinlockProtected m_dma_running { false }; - StringView m_name; - }; - - AC97(PCI::DeviceIdentifier const&, NonnullOwnPtr pcm_out_channel, NonnullOwnPtr mixer_io_window, NonnullOwnPtr bus_io_window); - - // ^IRQHandler - virtual bool handle_irq(RegisterState const&) override; - - void set_master_output_volume(u8, u8, Muted); - u32 read_pcm_output_sample_rate(); - ErrorOr set_pcm_output_sample_rate(u32); - void set_pcm_output_volume(u8, u8, Muted); - ErrorOr write_single_buffer(UserOrKernelBuffer const&, size_t, size_t); - - // ^AudioController - virtual ErrorOr initialize(Badge) override; - virtual RefPtr audio_channel(u32 index) const override; - virtual ErrorOr write(size_t channel_index, UserOrKernelBuffer const& data, size_t length) override; - virtual ErrorOr set_pcm_output_sample_rate(size_t channel_index, u32 samples_per_second_rate) override; - virtual ErrorOr get_pcm_output_sample_rate(size_t channel_index) override; - - OwnPtr m_buffer_descriptor_list; - u8 m_buffer_descriptor_list_index { 0 }; - AC97Revision m_codec_revision { AC97Revision::Revision21OrEarlier }; - bool m_double_rate_pcm_enabled { false }; - NonnullOwnPtr m_mixer_io_window; - NonnullOwnPtr m_bus_io_window; - WaitQueue m_irq_queue; - OwnPtr m_output_buffer; - u8 m_output_buffer_page_count { 4 }; - u8 m_output_buffer_page_index { 0 }; - NonnullOwnPtr m_pcm_out_channel; - u32 m_sample_rate { 0 }; - bool m_variable_rate_pcm_supported { false }; - RefPtr m_audio_channel; -}; - -} diff --git a/Kernel/Devices/Audio/Channel.cpp b/Kernel/Devices/Audio/Channel.cpp deleted file mode 100644 index 0f9da43d4df..00000000000 --- a/Kernel/Devices/Audio/Channel.cpp +++ /dev/null @@ -1,78 +0,0 @@ -/* - * Copyright (c) 2022, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include -#include - -namespace Kernel { - -UNMAP_AFTER_INIT ErrorOr> AudioChannel::create(AudioController const& controller, size_t channel_index) -{ - auto channel = TRY(DeviceManagement::try_create_device(controller, channel_index)); - - // FIXME: Ideally, we would want the audio controller to run a channel at a device's initial sample - // rate instead of hardcoding 44.1 KHz here. However, most audio is provided at 44.1 KHz and as - // long as Audio::Resampler introduces significant audio artifacts, let's set a sensible sample - // rate here. Remove this after implementing a higher quality Audio::Resampler. - TRY(const_cast(controller).set_pcm_output_sample_rate(channel_index, 44100)); - - return *channel; -} - -AudioChannel::AudioChannel(AudioController const& controller, size_t channel_index) - : CharacterDevice(AudioManagement::the().audio_type_major_number(), AudioManagement::the().generate_storage_minor_number()) - , m_controller(controller) - , m_channel_index(channel_index) -{ -} - -ErrorOr AudioChannel::ioctl(OpenFileDescription&, unsigned request, Userspace arg) -{ - auto controller = m_controller.strong_ref(); - if (!controller) - return Error::from_errno(EIO); - switch (request) { - case SOUNDCARD_IOCTL_GET_SAMPLE_RATE: { - auto output = static_ptr_cast(arg); - u32 sample_rate = 0; - sample_rate = TRY(controller->get_pcm_output_sample_rate(m_channel_index)); - return copy_to_user(output, &sample_rate); - } - case SOUNDCARD_IOCTL_SET_SAMPLE_RATE: { - auto sample_rate = static_cast(arg.ptr()); - TRY(controller->set_pcm_output_sample_rate(m_channel_index, sample_rate)); - return {}; - } - default: - return EINVAL; - } -} - -bool AudioChannel::can_read(OpenFileDescription const&, u64) const -{ - // FIXME: Implement input from device - return false; -} - -ErrorOr AudioChannel::read(OpenFileDescription&, u64, UserOrKernelBuffer&, size_t) -{ - // FIXME: Implement input from device - return Error::from_errno(ENOTIMPL); -} - -ErrorOr AudioChannel::write(OpenFileDescription&, u64, UserOrKernelBuffer const& buffer, size_t size) -{ - auto controller = m_controller.strong_ref(); - if (!controller) - return Error::from_errno(EIO); - return controller->write(m_channel_index, buffer, size); -} - -} diff --git a/Kernel/Devices/Audio/Channel.h b/Kernel/Devices/Audio/Channel.h deleted file mode 100644 index 2991c2e1f5f..00000000000 --- a/Kernel/Devices/Audio/Channel.h +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright (c) 2022, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include - -namespace Kernel { - -class AudioController; -class AudioChannel final - : public CharacterDevice { - friend class DeviceManagement; - -public: - static ErrorOr> create(AudioController const&, size_t channel_index); - virtual ~AudioChannel() override = default; - - // ^CharacterDevice - virtual bool can_read(OpenFileDescription const&, u64) const override; - virtual ErrorOr read(OpenFileDescription&, u64, UserOrKernelBuffer&, size_t) override; - virtual ErrorOr write(OpenFileDescription&, u64, UserOrKernelBuffer const&, size_t) override; - virtual bool can_write(OpenFileDescription const&, u64) const override { return true; } - - virtual ErrorOr ioctl(OpenFileDescription&, unsigned, Userspace) override; - -private: - AudioChannel(AudioController const&, size_t channel_index); - - // ^CharacterDevice - virtual StringView class_name() const override { return "AudioChannel"sv; } - - LockWeakPtr m_controller; - size_t const m_channel_index; -}; -} diff --git a/Kernel/Devices/Audio/Controller.h b/Kernel/Devices/Audio/Controller.h deleted file mode 100644 index 7600133435e..00000000000 --- a/Kernel/Devices/Audio/Controller.h +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright (c) 2022, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include -#include - -namespace Kernel { - -class AudioManagement; -class AudioController - : public AtomicRefCounted - , public LockWeakable { - friend class AudioManagement; - -public: - virtual ~AudioController() = default; - - virtual RefPtr audio_channel(u32 index) const = 0; - virtual ErrorOr write(size_t channel_index, UserOrKernelBuffer const& data, size_t length) = 0; - - virtual ErrorOr initialize(Badge) = 0; - - virtual ErrorOr set_pcm_output_sample_rate(size_t channel_index, u32 samples_per_second_rate) = 0; - // Note: The return value is rate of samples per second - virtual ErrorOr get_pcm_output_sample_rate(size_t channel_index) = 0; - -private: - IntrusiveListNode> m_node; -}; -} diff --git a/Kernel/Devices/Audio/IntelHDA/Codec.cpp b/Kernel/Devices/Audio/IntelHDA/Codec.cpp deleted file mode 100644 index 4d6abe3c21e..00000000000 --- a/Kernel/Devices/Audio/IntelHDA/Codec.cpp +++ /dev/null @@ -1,721 +0,0 @@ -/* - * Copyright (c) 2023, Jelle Raaijmakers - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "Codec.h" -#include -#include - -namespace Kernel::Audio::IntelHDA { - -// 7.3.4.7: Supported PCM Size, Rates -struct BitRateEncoding { - u8 flag; - u8 bit_rate; -}; -static constexpr Array bit_rate_encodings { { - // clang-format off - { 0x1, 8 }, - { 0x2, 16 }, - { 0x4, 20 }, - { 0x8, 24 }, - { 0x10, 32 }, - // clang-format on -} }; - -struct SampleRateEncoding { - u16 flag; - u32 sample_rate; -}; -static constexpr Array sample_rate_encodings { { - // clang-format off - { 0x1, 8'000 }, - { 0x2, 11'025 }, - { 0x4, 16'000 }, - { 0x8, 22'050 }, - { 0x10, 32'000 }, - { 0x20, 44'100 }, - { 0x40, 48'000 }, - { 0x80, 88'200 }, - { 0x100, 96'000 }, - { 0x200, 176'400 }, - { 0x400, 192'000 }, - { 0x800, 384'000 }, - // clang-format on -} }; - -ErrorOr Codec::register_node(NonnullRefPtr node) -{ - auto set_result = TRY(m_nodes_by_node_id.try_set(node->node_id(), node)); - VERIFY(set_result == HashSetResult::InsertedNewEntry); - return {}; -} - -ErrorOr Node::initialize() -{ - return codec().register_node(*this); -} - -ErrorOr Node::command(CodecControlVerb verb, u16 payload) -{ - auto& node_codec = codec(); - return node_codec.controller().send_command(node_codec.codec_address(), m_node_id, verb, payload); -} - -ErrorOr Node::parameter(GetParameterId get_parameter_id) -{ - return command(CodecControlVerb::GetParameter, to_underlying(get_parameter_id)); -} - -ErrorOr Node::set_power_state(PowerState power_state) -{ - // 7.3.3.10: Power State - TRY(command(CodecControlVerb::SetPowerState, to_underlying(power_state))); - return {}; -} - -template -void NodeWithChildren::for_each_child_node(Function callback) const -{ - auto number_of_child_nodes = m_child_nodes.size(); - for (size_t child_index = 0; child_index < number_of_child_nodes; ++child_index) - callback(m_child_nodes[child_index], child_index == number_of_child_nodes - 1); -} - -template -ErrorOr NodeWithChildren::initialize() -{ - TRY(Node::initialize()); - return populate_child_nodes(); -} - -template -ErrorOr NodeWithChildren::populate_child_nodes() -{ - VERIFY(m_child_nodes.is_empty()); - - // 7.3.4.3: Subordinate Node Count - auto subordinate_node_count = TRY(parameter(GetParameterId::SubordinateNodeCount)); - u8 starting_node_number = (subordinate_node_count >> 16) & 0xff; - u8 total_number_of_nodes = subordinate_node_count & 0xff; - TRY(m_child_nodes.try_ensure_capacity(total_number_of_nodes)); - for (int subnode_index = 0; subnode_index < total_number_of_nodes; ++subnode_index) - m_child_nodes.unchecked_append(TRY(Node::create(*this, starting_node_number + subnode_index))); - return {}; -} - -StringView WidgetNode::widget_type_name() const -{ - switch (m_widget_type) { - case WidgetType::AudioInput: - return "Audio Input"sv; - case WidgetType::AudioMixer: - return "Audio Mixer"sv; - case WidgetType::AudioOutput: - return "Audio Output"sv; - case WidgetType::AudioSelector: - return "Audio Selector"sv; - case WidgetType::BeepGenerator: - return "Beep Generator"sv; - case WidgetType::PinComplex: - return "Pin Complex"sv; - case WidgetType::Power: - return "Power"sv; - case WidgetType::VendorDefined: - return "Vendor Defined"sv; - case WidgetType::VolumeKnob: - return "Volume Knob"sv; - } - return "Reserved"sv; -} - -ErrorOr WidgetNode::initialize() -{ - TRY(Node::initialize()); - - // 7.3.4.6: Audio Widget Capabilities - auto widget_capabilities = TRY(parameter(GetParameterId::AudioWidgetCapabilities)); - m_widget_type = static_cast((widget_capabilities >> 20) & 0xf); - m_channel_count = (((widget_capabilities >> 15) & 0xe) | (widget_capabilities & 0x1)) + 1; - m_power_control_supported = (widget_capabilities & WidgetCapabilityFlag::PowerControlSupported) > 0; - m_connection_list_present = (widget_capabilities & WidgetCapabilityFlag::ConnectionListPresent) > 0; - m_format_override = (widget_capabilities & WidgetCapabilityFlag::FormatOverride) > 0; - m_amp_param_override = (widget_capabilities & WidgetCapabilityFlag::AmpParamOverride) > 0; - m_output_amp_present = (widget_capabilities & WidgetCapabilityFlag::OutputAmpPresent) > 0; - m_input_amp_present = (widget_capabilities & WidgetCapabilityFlag::InputAmpPresent) > 0; - - if (supports_stream()) { - // 7.3.3.11: Converter Stream, Channel - auto stream_channel = TRY(command(CodecControlVerb::GetConverterStreamChannel, 0)); - m_selected_stream = (stream_channel >> 4) & 0xf; - m_selected_channel = stream_channel & 0xf; - - TRY(populate_supported_pcm_size_rates()); - TRY(populate_supported_stream_formats()); - } - - // 7.3.4.10: Amplifier Capabilities - auto read_amp_capabilities = [](Node& node, GetParameterId type) -> ErrorOr { - auto capabilities = TRY(node.parameter(type)); - return AmplifierCapabilities { - .muting_supported = ((capabilities >> 31) & 0x1) > 0, - .step_size = static_cast((capabilities >> 16) & 0x7f), - .number_of_steps = static_cast(((capabilities >> 8) & 0x7f) + 1), - .offset = static_cast(capabilities & 0x7f), - }; - }; - Node& amp_params_node = amp_param_override() ? *this : *parent_node(); - if (output_amp_present()) - m_output_amp_capabilities = TRY(read_amp_capabilities(amp_params_node, GetParameterId::OutputAmplifierCapabilities)); - if (input_amp_present()) - m_input_amp_capabilities = TRY(read_amp_capabilities(amp_params_node, GetParameterId::InputAmplifierCapabilities)); - - if (widget_type() == WidgetType::PinComplex) { - // 7.3.4.9: Pin Capabilities - auto pin_capabilities = TRY(parameter(GetParameterId::PinCapabilities)); - m_pin_complex_input_supported = (pin_capabilities & PinCapabilityFlag::InputCapable) > 0; - m_pin_complex_output_supported = (pin_capabilities & PinCapabilityFlag::OutputCapable) > 0; - - TRY(populate_pin_configuration_default()); - } - - // Connection list - if (connection_list_present()) - TRY(populate_connection_list()); - - return {}; -} - -ErrorOr> WidgetNode::to_string() -{ - StringBuilder builder; - TRY(builder.try_appendff("WidgetNode(node_id={}, type={})", node_id(), widget_type_name())); - return KString::try_create(builder.string_view()); -} - -void WidgetNode::debug_dump(StringView group_spine, bool is_last) const -{ - dbgln("{} {} Widget (node #{}):", group_spine, is_last ? "└"sv : "├"sv, node_id()); - auto spine = is_last ? " "sv : "│"sv; - dbgln("{} {} ├ Type: {} ({:#x})", group_spine, spine, widget_type_name(), to_underlying(widget_type())); - dbgln("{} {} ├ Channel count: {}", group_spine, spine, channel_count()); - dbgln("{} {} ├ Power control supported: {}", group_spine, spine, m_power_control_supported ? "yes"sv : "no"sv); - - if (supports_stream()) { - dbgln("{} {} ├ Selected stream: {}", group_spine, spine, selected_stream()); - if (channel_count() == 1) - dbgln("{} {} ├ Selected channel: {}", group_spine, spine, selected_channel()); - else - dbgln("{} {} ├ Selected channels: {}-{}", group_spine, spine, selected_channel(), selected_channel() + channel_count() - 1); - - dbgln("{} {} ├ Format override: {}", group_spine, spine, format_override() ? "yes"sv : "no"sv); - dbgln("{} {} ├ Supported PCM bit sizes:", group_spine, spine); - for (auto supported_size : supported_pcm_sizes()) - dbgln("{} {} │ • {}", group_spine, spine, supported_size); - - dbgln("{} {} ├ Supported PCM rates:", group_spine, spine); - for (auto supported_rate : supported_pcm_rates()) - dbgln("{} {} │ • {}Hz", group_spine, spine, supported_rate); - - dbgln("{} {} ├ Supported stream formats:", group_spine, spine); - if (has_flag(supported_stream_formats(), StreamFormatFlag::PCM)) - dbgln("{} {} │ • PCM", group_spine, spine); - if (has_flag(supported_stream_formats(), StreamFormatFlag::Float32)) - dbgln("{} {} │ • Float32", group_spine, spine); - if (has_flag(supported_stream_formats(), StreamFormatFlag::AC3)) - dbgln("{} {} │ • AC3", group_spine, spine); - } - - dbgln("{} {} ├ Amplifier parameters override: {}", group_spine, spine, amp_param_override() ? "yes"sv : "no"sv); - dbgln("{} {} ├ Output amplifier present: {}", group_spine, spine, output_amp_present() ? "yes"sv : "no"sv); - if (output_amp_present()) { - auto amp_capabilities = output_amp_capabilities(); - dbgln("{} {} │ ├ Muting supported: {}", group_spine, spine, amp_capabilities.muting_supported ? "yes"sv : "no"sv); - dbgln("{} {} │ ├ Step size: {}", group_spine, spine, amp_capabilities.step_size); - dbgln("{} {} │ ├ Number of steps: {}", group_spine, spine, amp_capabilities.number_of_steps); - dbgln("{} {} │ └ Offset: {}", group_spine, spine, amp_capabilities.offset); - } - - dbgln("{} {} ├ Input amplifier present: {}", group_spine, spine, input_amp_present() ? "yes"sv : "no"sv); - if (input_amp_present()) { - auto amp_capabilities = input_amp_capabilities(); - dbgln("{} {} │ ├ Muting supported: {}", group_spine, spine, amp_capabilities.muting_supported ? "yes"sv : "no"sv); - dbgln("{} {} │ ├ Step size: {}", group_spine, spine, amp_capabilities.step_size); - dbgln("{} {} │ ├ Number of steps: {}", group_spine, spine, amp_capabilities.number_of_steps); - dbgln("{} {} │ └ Offset: {}", group_spine, spine, amp_capabilities.offset); - } - - if (widget_type() == WidgetType::PinComplex) { - dbgln("{} {} ├ Pin complex input supported: {}", group_spine, spine, pin_complex_input_supported()); - dbgln("{} {} ├ Pin complex output supported: {}", group_spine, spine, pin_complex_output_supported()); - dbgln("{} {} ├ Pin configuration default:", group_spine, spine); - dbgln("{} {} │ ├ Sequence: {}", group_spine, spine, m_pin_configuration_default.sequence); - dbgln("{} {} │ ├ Default association: {}", group_spine, spine, m_pin_configuration_default.default_association); - dbgln("{} {} │ ├ Jack detect override: {}", group_spine, spine, - ((static_cast(m_pin_configuration_default.misc) & to_underlying(PinMiscFlag::JackDetectOverride)) > 0) ? "yes"sv : "no"sv); - dbgln("{} {} │ ├ Color: {}", group_spine, spine, pin_color_name()); - dbgln("{} {} │ ├ Connection type: {}", group_spine, spine, pin_connection_type_name()); - dbgln("{} {} │ ├ Default device: {}", group_spine, spine, pin_default_device_name()); - dbgln("{} {} │ ├ Location: {}, {}", group_spine, spine, pin_gross_location_name(), pin_geometric_location_name()); - dbgln("{} {} │ └ Port connectivity: {}", group_spine, spine, pin_port_connectivity_name()); - } - - dbgln("{} {} └ Connection list:{}", group_spine, spine, connection_list_present() ? ""sv : " absent"sv); - if (connection_list_present()) { - auto selected_node_id = connection_selected_node_id(); - auto all_active = !supports_connection_select_control(); - for (auto connection_entry : connection_list()) { - dbgln("{} {} • Node #{}{}", group_spine, spine, connection_entry, - all_active || connection_entry == selected_node_id ? " (active)"sv : ""sv); - } - } -} - -ErrorOr WidgetNode::set_amplifier_gain_mute(SetAmplifierGainMute settings) -{ - // 7.3.3.7: Amplifier Gain/Mute - VERIFY(input_amp_present() || output_amp_present()); - u16 set_amp_gain_payload = ((output_amp_present() ? 1 : 0) << 15) - | ((input_amp_present() ? 1 : 0) << 15) - | ((settings.set_left ? 1 : 0) << 13) - | ((settings.set_right ? 1 : 0) << 12) - | ((settings.connection_index & 0xf) << 8) - | ((settings.mute ? 1 : 0) << 7) - | (settings.gain & 0x7f); - TRY(command(CodecControlVerb::SetAmplifierGainMute, set_amp_gain_payload)); - return {}; -} - -ErrorOr WidgetNode::set_connection_select(u8 connection_index) -{ - // 7.3.3.2: Connection Select Control - VERIFY(connection_list_present()); - VERIFY(connection_index < connection_list().size()); - TRY(command(CodecControlVerb::SetConnectionSelectControl, connection_index)); - return {}; -} - -ErrorOr WidgetNode::set_converter_stream_and_channel(u8 stream_index, u8 channel_index) -{ - // 7.3.3.11: Converter Stream, Channel - VERIFY(widget_type() == WidgetType::AudioInput || widget_type() == WidgetType::AudioOutput); - u16 stream_channel_payload = ((stream_index & 0xf) << 4) | (channel_index & 0xf); - TRY(command(CodecControlVerb::SetConverterStreamChannel, stream_channel_payload)); - return {}; -} - -ErrorOr WidgetNode::set_pin_control(PinControl pin_control) -{ - // 7.3.3.13: Pin Widget Control - VERIFY(widget_type() == WidgetType::PinComplex); - VERIFY(!pin_control.output_enabled || pin_complex_output_supported()); - VERIFY(!pin_control.input_enabled || pin_complex_input_supported()); - - u8 payload = ((pin_control.low_impedance_amplifier_enabled ? 1 : 0) << 7) - | ((pin_control.output_enabled ? 1 : 0) << 6) - | ((pin_control.input_enabled ? 1 : 0) << 5) - | (pin_control.voltage_reference_enable & 0x7); - TRY(command(CodecControlVerb::SetPinWidgetControl, payload)); - return {}; -} - -bool WidgetNode::supports_stream() const -{ - return widget_type() == WidgetType::AudioInput - || widget_type() == WidgetType::AudioOutput; -} - -bool WidgetNode::supports_connection_select_control() const -{ - return widget_type() == WidgetType::AudioInput - || widget_type() == WidgetType::AudioSelector - || widget_type() == WidgetType::PinComplex; -} - -ErrorOr WidgetNode::get_converter_format() -{ - // 7.3.3.8: Converter Format - VERIFY(widget_type() == WidgetType::AudioInput || widget_type() == WidgetType::AudioOutput); - u16 format = TRY(command(CodecControlVerb::GetConverterFormat, 0)) & 0xffffu; - return decode_format(format); -} - -ErrorOr WidgetNode::set_converter_format(FormatParameters format) -{ - // 7.3.3.8: Converter Format - VERIFY(widget_type() == WidgetType::AudioInput || widget_type() == WidgetType::AudioOutput); - u16 format_payload = TRY(encode_format(format)); - TRY(command(CodecControlVerb::SetConverterFormat, format_payload)); - return {}; -} - -ErrorOr WidgetNode::populate_supported_pcm_size_rates() -{ - VERIFY(m_supported_pcm_sizes.is_empty() && m_supported_pcm_rates.is_empty()); - - // 7.3.4.7: Supported PCM Size, Rates - Node& stream_support_node = format_override() ? *this : *parent_node(); - auto supported_pcm_size_and_rates = TRY(stream_support_node.parameter(GetParameterId::SupportedPCMSizeRates)); - - auto pcm_sizes = (supported_pcm_size_and_rates >> 16) & 0x1f; - TRY(m_supported_pcm_sizes.try_ensure_capacity(popcount(pcm_sizes))); - for (auto bit_rate_encoding : bit_rate_encodings) { - if ((pcm_sizes & bit_rate_encoding.flag) > 0) - m_supported_pcm_sizes.unchecked_append(bit_rate_encoding.bit_rate); - } - - auto pcm_rates = supported_pcm_size_and_rates & 0x7ff; - TRY(m_supported_pcm_rates.try_ensure_capacity(popcount(pcm_rates))); - for (auto sample_rate_encoding : sample_rate_encodings) { - if ((pcm_rates & sample_rate_encoding.flag) > 0) - m_supported_pcm_rates.unchecked_append(sample_rate_encoding.sample_rate); - } - - return {}; -} - -ErrorOr WidgetNode::populate_supported_stream_formats() -{ - VERIFY(m_supported_stream_formats == 0); - - // 7.3.4.8: Supported Stream Formats - Node& stream_support_node = format_override() ? *this : *parent_node(); - auto supported_stream_formats = TRY(stream_support_node.parameter(GetParameterId::SupportedStreamFormats)); - - if ((supported_stream_formats & 0x1) > 0) - m_supported_stream_formats |= StreamFormatFlag::PCM; - if ((supported_stream_formats & 0x2) > 0) - m_supported_stream_formats |= StreamFormatFlag::Float32; - if ((supported_stream_formats & 0x4) > 0) - m_supported_stream_formats |= StreamFormatFlag::AC3; - - return {}; -} - -ErrorOr WidgetNode::populate_connection_list() -{ - VERIFY(connection_list_present()); - VERIFY(m_connection_list.is_empty()); - - // 7.3.4.11: Connection List Length - auto connection_list_length_info = TRY(parameter(GetParameterId::ConnectionListLength)); - bool long_form = (connection_list_length_info >> 7) & 0x1; - u8 connection_list_length = connection_list_length_info & 0x7f; - u8 entries_per_request = long_form ? 2 : 4; - - // 7.3.3.3: Get Connection List Entry - for (u8 entry_offset = 0; entry_offset < connection_list_length; entry_offset += entries_per_request) { - auto entries = TRY(command(CodecControlVerb::GetConnectionListEntry, entry_offset)); - for (u8 entry_index = 0; entry_index < min(entries_per_request, static_cast(connection_list_length) - entry_offset); ++entry_index) { - u16 entry = entries & (long_form ? 0xffff : 0xff); - TRY(m_connection_list.try_append(entry)); - entries >>= (32 / entries_per_request); - } - } - - // 7.1.3: Widget Interconnection Rules - // "Connection_List_Length = 1 means there is only one (hard-wired) input possible and, - // therefore, there is no Connection_Selector field. The actual connection is read - // from the Connection List as usual." - if (connection_list_length == 1) { - m_connection_index = 0; - } else { - // 7.3.3.2: Connection Select Control - auto connection_selection_control = TRY(command(CodecControlVerb::GetConnectionSelectControl, 0)); - m_connection_index = connection_selection_control & 0xff; - } - - return {}; -} - -ErrorOr WidgetNode::populate_pin_configuration_default() -{ - VERIFY(widget_type() == WidgetType::PinComplex); - - u32 configuration_default = TRY(command(CodecControlVerb::GetConfigurationDefault, 0)); - m_pin_configuration_default.sequence = configuration_default & 0xf; - m_pin_configuration_default.default_association = (configuration_default >> 4) & 0xf; - m_pin_configuration_default.misc = static_cast((configuration_default >> 8) & 0xf); - m_pin_configuration_default.color = static_cast((configuration_default >> 12) & 0xf); - m_pin_configuration_default.connection_type = static_cast((configuration_default >> 16) & 0xf); - m_pin_configuration_default.default_device = static_cast((configuration_default >> 20) & 0xf); - m_pin_configuration_default.geometric_location = static_cast((configuration_default >> 24) & 0xf); - m_pin_configuration_default.gross_location = static_cast((configuration_default >> 28) & 0x3); - m_pin_configuration_default.port_connectivity = static_cast(configuration_default >> 30); - - return {}; -} - -StringView WidgetNode::pin_color_name() const -{ - auto underlying_color = to_underlying(m_pin_configuration_default.color); - if (underlying_color >= 0xa && underlying_color <= 0xd) - return "Reserved"sv; - - switch (m_pin_configuration_default.color) { - case PinColor::Unknown: - return "Unknown"sv; - case PinColor::Black: - return "Black"sv; - case PinColor::Grey: - return "Grey"sv; - case PinColor::Blue: - return "Blue"sv; - case PinColor::Green: - return "Green"sv; - case PinColor::Red: - return "Red"sv; - case PinColor::Orange: - return "Orange"sv; - case PinColor::Yellow: - return "Yellow"sv; - case PinColor::Purple: - return "Purple"sv; - case PinColor::Pink: - return "Pink"sv; - case PinColor::White: - return "White"sv; - case PinColor::Other: - return "Other"sv; - } - VERIFY_NOT_REACHED(); -} - -StringView WidgetNode::pin_connection_type_name() const -{ - switch (m_pin_configuration_default.connection_type) { - case PinConnectionType::Unknown: - return "Unknown"sv; - case PinConnectionType::EighthStereoMono: - return "1/8\" Stereo/Mono"sv; - case PinConnectionType::FourthStereoMono: - return "1/4\" Stereo/Mono"sv; - case PinConnectionType::ATAPIInternal: - return "ATAPI Internal"sv; - case PinConnectionType::RCA: - return "RCA"sv; - case PinConnectionType::Optical: - return "Optical"sv; - case PinConnectionType::OtherDigital: - return "Other Digital"sv; - case PinConnectionType::OtherAnalog: - return "Other Analog"sv; - case PinConnectionType::MultichannelAnalog: - return "Multichannel Analog"sv; - case PinConnectionType::XLRProfessional: - return "XLR / Professional"sv; - case PinConnectionType::RJ11: - return "RJ-11 (Modem)"sv; - case PinConnectionType::Combination: - return "Combination"sv; - case PinConnectionType::Other: - return "Other"sv; - } - VERIFY_NOT_REACHED(); -} - -StringView WidgetNode::pin_default_device_name() const -{ - switch (m_pin_configuration_default.default_device) { - case PinDefaultDevice::LineOut: - return "Line Out"sv; - case PinDefaultDevice::Speaker: - return "Speaker"sv; - case PinDefaultDevice::HPOut: - return "Headphones"sv; - case PinDefaultDevice::CD: - return "CD"sv; - case PinDefaultDevice::SPDIFOut: - return "S/PDIF Out"sv; - case PinDefaultDevice::DigitalOtherOut: - return "Digital Other Out"sv; - case PinDefaultDevice::ModemLineSide: - return "Modem Line Side"sv; - case PinDefaultDevice::ModemHandsetSide: - return "Modem Handset Side"sv; - case PinDefaultDevice::LineIn: - return "Line In"sv; - case PinDefaultDevice::AUX: - return "AUX"sv; - case PinDefaultDevice::MicIn: - return "Mic In"sv; - case PinDefaultDevice::Telephony: - return "Telephony"sv; - case PinDefaultDevice::SPDIFIn: - return "S/PDIF In"sv; - case PinDefaultDevice::DigitalOtherIn: - return "Digital Other In"sv; - case PinDefaultDevice::Reserved: - return "Reserved"sv; - case PinDefaultDevice::Other: - return "Other"sv; - } - VERIFY_NOT_REACHED(); -} - -StringView WidgetNode::pin_gross_location_name() const -{ - switch (m_pin_configuration_default.gross_location) { - case PinGrossLocation::ExternalOnPrimaryChassis: - return "External on Primary Chassis"sv; - case PinGrossLocation::Internal: - return "Internal"sv; - case PinGrossLocation::SeparateChassis: - return "Separate Chassis"sv; - case PinGrossLocation::Other: - return "Other"sv; - } - VERIFY_NOT_REACHED(); -} - -StringView WidgetNode::pin_geometric_location_name() const -{ - // 7.3.3.31: Configuration Default - special cases - if (m_pin_configuration_default.geometric_location == PinGeometricLocation::Special1) { - if (m_pin_configuration_default.gross_location == PinGrossLocation::ExternalOnPrimaryChassis) - return "Rear Panel"sv; - if (m_pin_configuration_default.gross_location == PinGrossLocation::Internal) - return "Riser"sv; - if (m_pin_configuration_default.gross_location == PinGrossLocation::Other) - return "Mobile Lid (Inside)"sv; - } else if (m_pin_configuration_default.geometric_location == PinGeometricLocation::Special2) { - if (m_pin_configuration_default.gross_location == PinGrossLocation::ExternalOnPrimaryChassis) - return "Drive Bay"sv; - if (m_pin_configuration_default.gross_location == PinGrossLocation::Internal) - return "Digital Display"sv; - if (m_pin_configuration_default.gross_location == PinGrossLocation::Other) - return "Mobile Lid (Outside)"sv; - } else if (m_pin_configuration_default.geometric_location == PinGeometricLocation::Special3) { - if (m_pin_configuration_default.gross_location == PinGrossLocation::Internal) - return "ATAPI"sv; - } - - switch (m_pin_configuration_default.geometric_location) { - case PinGeometricLocation::NotApplicable: - return "N/A"sv; - case PinGeometricLocation::Rear: - return "Rear"sv; - case PinGeometricLocation::Front: - return "Front"sv; - case PinGeometricLocation::Left: - return "Left"sv; - case PinGeometricLocation::Right: - return "Right"sv; - case PinGeometricLocation::Top: - return "Top"sv; - case PinGeometricLocation::Bottom: - return "Bottom"sv; - case PinGeometricLocation::Special1: - case PinGeometricLocation::Special2: - case PinGeometricLocation::Special3: - return "Special"sv; - } - VERIFY_NOT_REACHED(); -} - -StringView WidgetNode::pin_port_connectivity_name() const -{ - switch (m_pin_configuration_default.port_connectivity) { - case PinPortConnectivity::Jack: - return "Jack"sv; - case PinPortConnectivity::NoConnection: - return "No Physical Connection"sv; - case PinPortConnectivity::FixedFunction: - return "Fixed Function Device"sv; - case PinPortConnectivity::JackAndFixedFunction: - return "Jack and Fixed Function Device"sv; - } - VERIFY_NOT_REACHED(); -} - -StringView FunctionGroupNode::function_group_type_name() const -{ - switch (m_function_group_type) { - case FunctionGroupType::AudioFunctionGroup: - return "Audio Function Group"sv; - case FunctionGroupType::ModemFunctionGroup: - return "Modem Function Group"sv; - case FunctionGroupType::VendorFunctionGroup: - return "Vendor Function Group"sv; - case FunctionGroupType::Reserved: - return "Reserved"sv; - } - VERIFY_NOT_REACHED(); -} - -ErrorOr FunctionGroupNode::initialize() -{ - TRY(NodeWithChildren::initialize()); - - // 7.3.4.4: Function Group Type - auto function_group_type = TRY(parameter(GetParameterId::FunctionGroupType)); - if (function_group_type == 0x1) - m_function_group_type = FunctionGroupType::AudioFunctionGroup; - else if (function_group_type == 0x2) - m_function_group_type = FunctionGroupType::ModemFunctionGroup; - else if (function_group_type >= 0x80) - m_function_group_type = FunctionGroupType::VendorFunctionGroup; - else - m_function_group_type = FunctionGroupType::Reserved; - - return {}; -} - -ErrorOr> FunctionGroupNode::to_string() -{ - StringBuilder builder; - TRY(builder.try_appendff("FunctionGroupNode(node_id={})", node_id())); - return KString::try_create(builder.string_view()); -} - -void FunctionGroupNode::debug_dump(bool is_last) const -{ - dbgln("{} Function group (node #{}):", is_last ? "└"sv : "├"sv, node_id()); - auto spine = is_last ? " "sv : "│"sv; - dbgln("{} ├ Function group type: {} ({:#x})", spine, function_group_type_name(), to_underlying(function_group_type())); - - for_each_child_node([&spine](WidgetNode const& widget_node, bool is_last) -> void { - widget_node.debug_dump(spine, is_last); - }); -} - -ErrorOr RootNode::initialize() -{ - TRY(NodeWithChildren::initialize()); - - // 7.3.4.1: Vendor ID - auto vendor_id_response = TRY(parameter(GetParameterId::VendorID)); - m_vendor_id = (vendor_id_response >> 16) & 0xffff; - m_device_id = vendor_id_response & 0xffff; - - // 7.3.4.2: Revision ID - auto revision_id_response = TRY(parameter(GetParameterId::RevisionID)); - m_major_revision = (revision_id_response >> 20) & 0xf; - m_minor_revision = (revision_id_response >> 16) & 0xf; - if (m_major_revision != 1 || m_minor_revision != 0) - return ENOTSUP; - - return {}; -} - -ErrorOr> RootNode::to_string() -{ - StringBuilder builder; - TRY(builder.try_appendff("RootNode(node_id={})", node_id())); - return KString::try_create(builder.string_view()); -} - -void RootNode::debug_dump() const -{ - dbgln("Root (node #{}):", node_id()); - dbgln("├ Codec vendor: {:#04x}, device: {:#04x}", vendor_id(), device_id()); - dbgln("├ Codec HDA compatibility: {}.{}", major_revision(), minor_revision()); - - for_each_child_node([](FunctionGroupNode const& fg_node, bool is_last) -> void { - fg_node.debug_dump(is_last); - }); -} - -} diff --git a/Kernel/Devices/Audio/IntelHDA/Codec.h b/Kernel/Devices/Audio/IntelHDA/Codec.h deleted file mode 100644 index 3c05c231119..00000000000 --- a/Kernel/Devices/Audio/IntelHDA/Codec.h +++ /dev/null @@ -1,497 +0,0 @@ -/* - * Copyright (c) 2023, Jelle Raaijmakers - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace Kernel::Audio::IntelHDA { - -class Codec; -class Controller; -class RootNode; - -// 7.3.3: Controls -enum CodecControlVerb : u16 { - GetParameter = 0xf00, - GetConnectionSelectControl = 0xf01, - SetConnectionSelectControl = 0x701, - GetConnectionListEntry = 0xf02, - GetAmplifierGainMute = 0xb, - SetAmplifierGainMute = 0x3, - GetConverterFormat = 0xa, - SetConverterFormat = 0x2, - SetPowerState = 0x705, - GetConverterStreamChannel = 0xf06, - SetConverterStreamChannel = 0x706, - SetPinWidgetControl = 0x707, - GetConfigurationDefault = 0xf1c, -}; - -// 7.3.4.8: Supported Stream Formats, figure 88 -enum StreamFormatFlag : u8 { - PCM = 1u << 0, - Float32 = 1u << 1, - AC3 = 1u << 2, -}; -AK_ENUM_BITWISE_OPERATORS(StreamFormatFlag); - -class Node : public RefCounted { -public: - enum class NodeType { - Root, - FunctionGroup, - Widget, - }; - - // 7.3.3.10: Power State, table 83 - enum class PowerState : u8 { - D0 = 0b000, - D1 = 0b001, - D2 = 0b010, - D3 = 0b011, - D3Cold = 0b100, - }; - - // 7.3.4: Parameters - enum class GetParameterId : u8 { - VendorID = 0x00, - RevisionID = 0x02, - SubordinateNodeCount = 0x04, - FunctionGroupType = 0x05, - AudioFunctionGroupCapabilities = 0x08, - AudioWidgetCapabilities = 0x09, - SupportedPCMSizeRates = 0x0a, - SupportedStreamFormats = 0x0b, - PinCapabilities = 0x0c, - InputAmplifierCapabilities = 0x0d, - ConnectionListLength = 0x0e, - SupportedPowerStates = 0x0f, - ProcessingCapabilities = 0x10, - GPIOCount = 0x11, - OutputAmplifierCapabilities = 0x12, - VolumeKnobCapabilities = 0x13, - }; - - virtual ~Node() = default; - - template - static ErrorOr> create(Args&&... args) - requires(IsBaseOf) - { - auto node = TRY(adopt_nonnull_ref_or_enomem(new (nothrow) T(forward(args)...))); - TRY(node->initialize()); - return node; - } - - virtual Codec& codec() - { - VERIFY(m_parent_node); - return m_parent_node->codec(); - } - - NodeType node_type() const { return m_node_type; } - RefPtr parent_node() const { return m_parent_node; } - u8 node_id() const { return m_node_id; } - - ErrorOr command(CodecControlVerb, u16 payload); - ErrorOr parameter(GetParameterId); - ErrorOr set_power_state(PowerState); - virtual ErrorOr> to_string() = 0; - -protected: - Node(NodeType node_type, RefPtr parent_node, u8 node_id) - : m_node_type(node_type) - , m_parent_node(parent_node) - , m_node_id(node_id) - { - } - - virtual ErrorOr initialize(); - - NodeType m_node_type; - RefPtr m_parent_node; - u8 m_node_id; -}; - -template -class NodeWithChildren : public Node { - friend class Node; - -public: - Vector> child_nodes() { return m_child_nodes; } - void for_each_child_node(Function callback) const; - -protected: - NodeWithChildren(NodeType node_type, RefPtr parent_node, u8 node_id) - : Node(node_type, parent_node, node_id) - { - } - - ErrorOr initialize() override; - -private: - ErrorOr populate_child_nodes(); - - Vector> m_child_nodes {}; -}; - -class WidgetNode final : public Node { - friend class Node; - -public: - static constexpr NodeType Type = NodeType::Widget; - - // 7.3.4.6: Audio Widget Capabilities, figure 86 - enum WidgetCapabilityFlag : u32 { - InputAmpPresent = 1u << 1, - OutputAmpPresent = 1u << 2, - AmpParamOverride = 1u << 3, - FormatOverride = 1u << 4, - ConnectionListPresent = 1u << 8, - PowerControlSupported = 1u << 10, - }; - - // 7.3.4.6: Audio Widget Capabilities, table 138 - enum WidgetType : u8 { - AudioOutput = 0x0, - AudioInput = 0x1, - AudioMixer = 0x2, - AudioSelector = 0x3, - PinComplex = 0x4, - Power = 0x5, - VolumeKnob = 0x6, - BeepGenerator = 0x7, - VendorDefined = 0xf, - }; - - // 7.3.4.9: Pin Capabilities, figure 89 - enum PinCapabilityFlag : u32 { - OutputCapable = 1u << 4, - InputCapable = 1u << 5, - }; - - // 7.3.4.10: Amplifier Capabilities - struct AmplifierCapabilities { - bool muting_supported; - u8 step_size; - u8 number_of_steps; - u8 offset; - }; - - // 7.3.3.7: Amplifier Gain/Mute Set Payload - struct SetAmplifierGainMute { - bool set_left { true }; - bool set_right { true }; - u8 connection_index { 0 }; - bool mute; - u8 gain; - }; - - // 7.3.3.13: Pin Widget Control - struct PinControl { - bool low_impedance_amplifier_enabled { true }; - bool output_enabled { false }; - bool input_enabled { false }; - u8 voltage_reference_enable { 0 }; - }; - - // 7.3.3.31: Configuration Default, table 109 - enum class PinPortConnectivity : u8 { - Jack = 0b00, - NoConnection = 0b01, - FixedFunction = 0b10, - JackAndFixedFunction = 0b11, - }; - - // 7.3.3.31: Configuration Default, table 110 (rows) - enum class PinGrossLocation : u8 { - ExternalOnPrimaryChassis = 0b00, - Internal = 0b01, - SeparateChassis = 0b10, - Other = 0b11, - }; - - // 7.3.3.31: Configuration Default, table 110 (columns) - enum class PinGeometricLocation : u8 { - NotApplicable = 0x0, - Rear = 0x1, - Front = 0x2, - Left = 0x3, - Right = 0x4, - Top = 0x5, - Bottom = 0x6, - Special1 = 0x7, - Special2 = 0x8, - Special3 = 0x9, - }; - - // 7.3.3.31: Configuration Default, table 111 - enum class PinDefaultDevice : u8 { - LineOut = 0x0, - Speaker = 0x1, - HPOut = 0x2, - CD = 0x3, - SPDIFOut = 0x4, - DigitalOtherOut = 0x5, - ModemLineSide = 0x6, - ModemHandsetSide = 0x7, - LineIn = 0x8, - AUX = 0x9, - MicIn = 0xa, - Telephony = 0xb, - SPDIFIn = 0xc, - DigitalOtherIn = 0xd, - Reserved = 0xe, - Other = 0xf, - }; - - // 7.3.3.31: Configuration Default, table 112 - enum class PinConnectionType : u8 { - Unknown = 0x0, - EighthStereoMono = 0x1, - FourthStereoMono = 0x2, - ATAPIInternal = 0x3, - RCA = 0x4, - Optical = 0x5, - OtherDigital = 0x6, - OtherAnalog = 0x7, - MultichannelAnalog = 0x8, - XLRProfessional = 0x9, - RJ11 = 0xa, - Combination = 0xb, - Other = 0xf, - }; - - // 7.3.3.31: Configuration Default, table 113 - enum class PinColor : u8 { - Unknown = 0x0, - Black = 0x1, - Grey = 0x2, - Blue = 0x3, - Green = 0x4, - Red = 0x5, - Orange = 0x6, - Yellow = 0x7, - Purple = 0x8, - Pink = 0x9, - White = 0xe, - Other = 0xf, - }; - - // 7.3.3.31: Configuration Default, table 114 - enum class PinMiscFlag : u8 { - JackDetectOverride = 1u << 0, - }; - - // 7.3.3.31: Configuration Default, figure 74 - struct PinConfigurationDefault { - PinPortConnectivity port_connectivity; - PinGrossLocation gross_location; - PinGeometricLocation geometric_location; - PinDefaultDevice default_device; - PinConnectionType connection_type; - PinColor color; - PinMiscFlag misc; - u8 default_association; - u8 sequence; - }; - - WidgetType widget_type() const { return m_widget_type; } - StringView widget_type_name() const; - - u8 channel_count() const { return m_channel_count; } - bool power_control_supported() const { return m_power_control_supported; } - bool connection_list_present() const { return m_connection_list_present; } - bool format_override() const { return m_format_override; } - bool amp_param_override() const { return m_amp_param_override; } - bool output_amp_present() const { return m_output_amp_present; } - bool input_amp_present() const { return m_input_amp_present; } - u8 selected_stream() const { return m_selected_stream; } - u8 selected_channel() const { return m_selected_channel; } - Span supported_pcm_sizes() const { return m_supported_pcm_sizes.span(); } - Span supported_pcm_rates() const { return m_supported_pcm_rates.span(); } - StreamFormatFlag supported_stream_formats() const { return m_supported_stream_formats; } - AmplifierCapabilities output_amp_capabilities() const { return m_output_amp_capabilities; } - AmplifierCapabilities input_amp_capabilities() const { return m_input_amp_capabilities; } - bool pin_complex_input_supported() const { return m_pin_complex_input_supported; } - bool pin_complex_output_supported() const { return m_pin_complex_output_supported; } - Span connection_list() const { return m_connection_list.span(); } - u8 connection_selected_node_id() const { return m_connection_list[m_connection_index]; } - - PinConfigurationDefault pin_configuration_default() const { return m_pin_configuration_default; } - StringView pin_color_name() const; - StringView pin_connection_type_name() const; - StringView pin_default_device_name() const; - StringView pin_gross_location_name() const; - StringView pin_geometric_location_name() const; - StringView pin_port_connectivity_name() const; - - ErrorOr> to_string() override; - void debug_dump(StringView, bool) const; - ErrorOr set_amplifier_gain_mute(SetAmplifierGainMute); - ErrorOr set_connection_select(u8); - ErrorOr set_converter_stream_and_channel(u8 stream_index, u8 channel_index); - ErrorOr set_pin_control(PinControl); - bool supports_stream() const; - bool supports_connection_select_control() const; - - ErrorOr get_converter_format(); - ErrorOr set_converter_format(FormatParameters); - -protected: - ErrorOr initialize() override; - -private: - WidgetNode(NonnullRefPtr parent_node, u8 node_id) - : Node(NodeType::Widget, parent_node, node_id) - { - } - - ErrorOr populate_supported_pcm_size_rates(); - ErrorOr populate_supported_stream_formats(); - ErrorOr populate_connection_list(); - ErrorOr populate_pin_configuration_default(); - - WidgetType m_widget_type; - u8 m_channel_count; - bool m_power_control_supported; - bool m_connection_list_present; - bool m_format_override; - bool m_amp_param_override; - bool m_output_amp_present; - bool m_input_amp_present; - u8 m_selected_stream; - u8 m_selected_channel; - Vector m_supported_pcm_sizes {}; - Vector m_supported_pcm_rates {}; - StreamFormatFlag m_supported_stream_formats { 0 }; - AmplifierCapabilities m_output_amp_capabilities; - AmplifierCapabilities m_input_amp_capabilities; - bool m_pin_complex_input_supported; - bool m_pin_complex_output_supported; - PinConfigurationDefault m_pin_configuration_default; - Vector m_connection_list {}; - u8 m_connection_index; -}; - -class FunctionGroupNode final : public NodeWithChildren { - friend class Node; - -public: - static constexpr NodeType Type = NodeType::FunctionGroup; - - // 7.3.4.4: Function Group Type - enum class FunctionGroupType { - AudioFunctionGroup, - ModemFunctionGroup, - VendorFunctionGroup, - Reserved, - }; - - ErrorOr> to_string() override; - void debug_dump(bool) const; - - FunctionGroupType function_group_type() const { return m_function_group_type; } - StringView function_group_type_name() const; - -protected: - ErrorOr initialize() override; - -private: - FunctionGroupNode(NonnullRefPtr parent_node, u8 node_id) - : NodeWithChildren(NodeType::FunctionGroup, parent_node, node_id) - { - } - - FunctionGroupType m_function_group_type; -}; - -class RootNode final : public NodeWithChildren { - friend class Node; - -public: - static constexpr NodeType Type = NodeType::Root; - - Codec& codec() override { return m_codec; } - - u16 vendor_id() const { return m_vendor_id; } - u16 device_id() const { return m_device_id; } - u8 major_revision() const { return m_major_revision; } - u8 minor_revision() const { return m_minor_revision; } - - ErrorOr> to_string() override; - void debug_dump() const; - -protected: - ErrorOr initialize() override; - -private: - RootNode(Codec& codec) - : NodeWithChildren(NodeType::Root, {}, 0) - , m_codec(codec) - { - } - - Codec& m_codec; - u16 m_vendor_id; - u16 m_device_id; - u8 m_major_revision; - u8 m_minor_revision; -}; - -class Codec : public RefCounted { -public: - static ErrorOr> create(Controller& controller, u8 codec_address) - { - return adopt_nonnull_ref_or_enomem(new (nothrow) Codec(controller, codec_address)); - } - - Controller& controller() const { return m_controller; } - u8 codec_address() const { return m_codec_address; } - - RefPtr root_node() const { return m_root_node; } - void set_root_node(NonnullRefPtr root_node) { m_root_node = root_node; } - - ErrorOr register_node(NonnullRefPtr node); - Optional node_by_node_id(u8 node_id) { return m_nodes_by_node_id.get(node_id); } - - template - ErrorOr>> nodes_matching(TPredicate predicate) - { - Vector> results; - for (auto node_entry : m_nodes_by_node_id) { - if (node_entry.value->node_type() != T::Type) - continue; - auto node = NonnullRefPtr { *reinterpret_cast(node_entry.value.ptr()) }; - if (predicate(node)) - TRY(results.try_append(node)); - } - return results; - } - -private: - Codec(Controller& controller, u8 codec_address) - : m_controller(controller) - , m_codec_address(codec_address) - { - } - - Controller& m_controller; - u8 m_codec_address; - RefPtr m_root_node; - HashMap> m_nodes_by_node_id; -}; - -} diff --git a/Kernel/Devices/Audio/IntelHDA/Controller.cpp b/Kernel/Devices/Audio/IntelHDA/Controller.cpp deleted file mode 100644 index 1976fdc496d..00000000000 --- a/Kernel/Devices/Audio/IntelHDA/Controller.cpp +++ /dev/null @@ -1,366 +0,0 @@ -/* - * Copyright (c) 2023, Jelle Raaijmakers - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "Controller.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace Kernel::Audio::IntelHDA { - -UNMAP_AFTER_INIT ErrorOr Controller::probe(PCI::DeviceIdentifier const& device_identifier) -{ - VERIFY(device_identifier.class_code() == PCI::ClassID::Multimedia); - return device_identifier.subclass_code() == PCI::Multimedia::SubclassID::HDACompatible; -} - -UNMAP_AFTER_INIT ErrorOr> Controller::create(PCI::DeviceIdentifier const& pci_device_identifier) -{ - auto controller_io_window = TRY(IOWindow::create_for_pci_device_bar(pci_device_identifier, PCI::HeaderType0BaseRegister::BAR0)); - return TRY(adopt_nonnull_ref_or_enomem(new (nothrow) Controller(pci_device_identifier, move(controller_io_window)))); -} - -UNMAP_AFTER_INIT Controller::Controller(PCI::DeviceIdentifier const& pci_device_identifier, NonnullOwnPtr controller_io_window) - : PCI::Device(const_cast(pci_device_identifier)) - , m_controller_io_window(move(controller_io_window)) -{ -} - -UNMAP_AFTER_INIT ErrorOr Controller::initialize(Badge) -{ - // Enable DMA and interrupts - PCI::enable_bus_mastering(device_identifier()); - m_interrupt_handler = TRY(InterruptHandler::create(*this)); - - // 3.3.3, 3.3.4: Controller version - auto version_minor = m_controller_io_window->read8(ControllerRegister::VersionMinor); - auto version_major = m_controller_io_window->read8(ControllerRegister::VersionMajor); - dmesgln_pci(*this, "Intel High Definition Audio specification v{}.{}", version_major, version_minor); - if (version_major != 1 || version_minor != 0) - return ENOTSUP; - - // 3.3.2: Read capabilities - u16 capabilities = m_controller_io_window->read16(ControllerRegister::GlobalCapabilities); - dbgln_if(INTEL_HDA_DEBUG, "Controller capabilities:"); - m_number_of_output_streams = capabilities >> 12; - m_number_of_input_streams = (capabilities >> 8) & 0xf; - m_number_of_bidirectional_streams = (capabilities >> 3) & 0x1f; - bool is_64_bit_addressing_supported = (capabilities & 0x1) > 0; - dbgln_if(INTEL_HDA_DEBUG, "├ Number of output streams: {}", m_number_of_output_streams); - dbgln_if(INTEL_HDA_DEBUG, "├ Number of input streams: {}", m_number_of_input_streams); - dbgln_if(INTEL_HDA_DEBUG, "├ Number of bidirectional streams: {}", m_number_of_bidirectional_streams); - dbgln_if(INTEL_HDA_DEBUG, "└ 64-bit addressing supported: {}", is_64_bit_addressing_supported ? "yes" : "no"); - if (m_number_of_output_streams == 0) - return ENOTSUP; - if (!is_64_bit_addressing_supported && sizeof(FlatPtr) == 8) - return ENOTSUP; - - // Reset the controller - TRY(reset()); - - // Register CORB and RIRB - auto command_io_window = TRY(m_controller_io_window->create_from_io_window_with_offset(ControllerRegister::CommandOutboundRingBufferOffset)); - m_command_buffer = TRY(CommandOutboundRingBuffer::create("IntelHDA CORB"sv, move(command_io_window))); - TRY(m_command_buffer->register_with_controller()); - - auto response_io_window = TRY(m_controller_io_window->create_from_io_window_with_offset(ControllerRegister::ResponseInboundRingBufferOffset)); - m_response_buffer = TRY(ResponseInboundRingBuffer::create("IntelHDA RIRB"sv, move(response_io_window))); - TRY(m_response_buffer->register_with_controller()); - - dbgln_if(INTEL_HDA_DEBUG, "CORB ({} entries) and RIRB ({} entries) registered", m_command_buffer->capacity(), m_response_buffer->capacity()); - - // Initialize all codecs - // 3.3.9: State Change Status - u16 state_change_status = m_controller_io_window->read16(ControllerRegister::StateChangeStatus); - for (u8 codec_address = 0; codec_address < 14; ++codec_address) { - if ((state_change_status & (1 << codec_address)) > 0) { - dmesgln_pci(*this, "Found codec on address #{}", codec_address); - TRY(initialize_codec(codec_address)); - } - } - - auto result = configure_output_route(); - if (result.is_error()) { - dmesgln_pci(*this, "Failed to set up an output audio channel: {}", result.error()); - return result.release_error(); - } - - m_audio_channel = TRY(AudioChannel::create(*this, fixed_audio_channel_index)); - return {}; -} - -UNMAP_AFTER_INIT ErrorOr Controller::initialize_codec(u8 codec_address) -{ - auto codec = TRY(Codec::create(*this, codec_address)); - - auto root_node = TRY(Node::create(codec)); - if constexpr (INTEL_HDA_DEBUG) - root_node->debug_dump(); - codec->set_root_node(root_node); - - TRY(m_codecs.try_append(codec)); - - return {}; -} - -ErrorOr Controller::send_command(u8 codec_address, u8 node_id, CodecControlVerb verb, u16 payload) -{ - // Construct command - // 7.3: If the most significant 4 bits of 12-bits verb are 0xf or 0x7, extended mode is selected - u32 command_value = codec_address << 28 | (node_id << 20); - if (((verb & 0x700) > 0) || ((verb & 0xf00) > 0)) - command_value |= ((verb & 0xfff) << 8) | (payload & 0xff); - else - command_value |= ((verb & 0xf) << 16) | payload; - - dbgln_if(INTEL_HDA_DEBUG, "Controller::{}: codec {} node {} verb {:#x} payload {:#b}", - __FUNCTION__, codec_address, node_id, to_underlying(verb), payload); - TRY(m_command_buffer->write_value(command_value)); - - // Read response - Optional full_response; - TRY(wait_until(frame_delay_in_microseconds(1), controller_timeout_in_microseconds, [&]() -> ErrorOr { - full_response = TRY(m_response_buffer->read_value()); - return full_response.has_value(); - })); - u32 response = full_response.value() & 0xffffffffu; - dbgln_if(INTEL_HDA_DEBUG, "Controller::{}: response {:#032b}", __FUNCTION__, response); - return response; -} - -UNMAP_AFTER_INIT ErrorOr Controller::configure_output_route() -{ - Vector> queued_nodes; - Vector visited_nodes; - HashMap parents; - - auto create_output_path = [&](RefPtr found_node) -> ErrorOr> { - // Reconstruct path by traversing parent nodes - Vector> path; - auto path_node = found_node; - while (path_node) { - TRY(path.try_append(*path_node)); - path_node = parents.get(path_node).value_or(nullptr); - } - path.reverse(); - - // Create output stream - constexpr u8 output_stream_index = 0; - constexpr u8 output_stream_number = 1; - u64 output_stream_offset = ControllerRegister::StreamsOffset - + m_number_of_input_streams * 0x20 - + output_stream_index * 0x20; - auto stream_io_window = TRY(m_controller_io_window->create_from_io_window_with_offset(output_stream_offset)); - auto output_stream = TRY(OutputStream::create(move(stream_io_window), output_stream_number)); - - // Create output path - auto output_path = TRY(OutputPath::create(move(path), move(output_stream))); - TRY(output_path->activate()); - - // Enable controller and stream interrupts for this output stream - auto interrupt_control = m_controller_io_window->read32(ControllerRegister::InterruptControl); - interrupt_control |= InterruptControlFlag::GlobalInterruptEnable; - interrupt_control |= 1u << (m_number_of_input_streams + output_stream_index); - m_controller_io_window->write32(ControllerRegister::InterruptControl, interrupt_control); - - return output_path; - }; - - for (auto codec : m_codecs) { - // Start off by finding all candidate pin complexes - auto pin_widgets = TRY(codec->nodes_matching([](NonnullRefPtr node) { - // Find pin complexes that support output. - if (node->widget_type() != WidgetNode::WidgetType::PinComplex - || !node->pin_complex_output_supported()) - return false; - - // Only consider pin complexes that have: - // - a physical connection (jack or fixed function) - // - and a default device that is line out, speakers or headphones. - auto configuration_default = node->pin_configuration_default(); - auto port_connectivity = configuration_default.port_connectivity; - auto default_device = configuration_default.default_device; - - bool is_physically_connected = port_connectivity == WidgetNode::PinPortConnectivity::Jack - || port_connectivity == WidgetNode::PinPortConnectivity::FixedFunction - || port_connectivity == WidgetNode::PinPortConnectivity::JackAndFixedFunction; - bool is_output_device = default_device == WidgetNode::PinDefaultDevice::LineOut - || default_device == WidgetNode::PinDefaultDevice::Speaker - || default_device == WidgetNode::PinDefaultDevice::HPOut; - - return is_physically_connected && is_output_device; - })); - - // Perform a breadth-first search to find a path to an audio output widget - for (auto pin_widget : pin_widgets) { - VERIFY(queued_nodes.is_empty() && visited_nodes.is_empty() && parents.is_empty()); - - TRY(queued_nodes.try_append(pin_widget)); - Optional> found_node = {}; - while (!queued_nodes.is_empty()) { - auto current_node = queued_nodes.take_first(); - if (current_node->widget_type() == WidgetNode::AudioOutput) { - found_node = current_node; - break; - } - - TRY(visited_nodes.try_append(current_node.ptr())); - for (u8 connection_node_id : current_node->connection_list()) { - auto connection_node = codec->node_by_node_id(connection_node_id); - if (!connection_node.has_value() || connection_node.value()->node_type() != Node::NodeType::Widget) { - dmesgln_pci(*this, "Warning: connection node {} does not exist or is the wrong type", connection_node_id); - continue; - } - - auto connection_widget = NonnullRefPtr { *reinterpret_cast(connection_node.release_value()) }; - if (visited_nodes.contains_slow(connection_widget)) - continue; - - TRY(queued_nodes.try_append(connection_widget)); - TRY(parents.try_set(connection_widget, current_node.ptr())); - } - } - - if (found_node.has_value()) { - m_output_path = TRY(create_output_path(found_node.release_value())); - break; - } - - queued_nodes.clear_with_capacity(); - visited_nodes.clear_with_capacity(); - parents.clear_with_capacity(); - } - - if (m_output_path) - break; - } - - if (!m_output_path) { - dmesgln_pci(*this, "Failed to find an audio output path"); - return ENODEV; - } - - // We are ready to go! - dmesgln_pci(*this, "Successfully configured an audio output path"); - dbgln_if(INTEL_HDA_DEBUG, "{}", TRY(m_output_path->to_string())); - - return {}; -} - -ErrorOr Controller::reset() -{ - // 3.3.7: "Controller Reset (CRST): Writing a 0 to this bit causes the High Definition Audio - // controller to transition to the Reset state." - u32 global_control = m_controller_io_window->read32(ControllerRegister::GlobalControl); - global_control &= ~GlobalControlFlag::ControllerReset; - global_control &= ~GlobalControlFlag::AcceptUnsolicitedResponseEnable; - m_controller_io_window->write32(ControllerRegister::GlobalControl, global_control); - - // 3.3.7: "After the hardware has completed sequencing into the reset state, it will report - // a 0 in this bit. Software must read a 0 from this bit to verify that the - // controller is in reset." - TRY(wait_until(frame_delay_in_microseconds(1), controller_timeout_in_microseconds, [&]() { - global_control = m_controller_io_window->read32(ControllerRegister::GlobalControl); - return (global_control & GlobalControlFlag::ControllerReset) == 0; - })); - - // 3.3.7: "Writing a 1 to this bit causes the controller to exit its Reset state and - // de-assert the link RESET# signal. Software is responsible for - // setting/clearing this bit such that the minimum link RESET# signal assertion - // pulse width specification is met (see Section 5.5)." - microseconds_delay(100); - global_control |= GlobalControlFlag::ControllerReset; - m_controller_io_window->write32(ControllerRegister::GlobalControl, global_control); - - // 3.3.7: "When the controller hardware is ready to begin operation, it will report a 1 in - // this bit. Software must read a 1 from this bit before accessing any controller - // registers." - TRY(wait_until(frame_delay_in_microseconds(1), controller_timeout_in_microseconds, [&]() { - global_control = m_controller_io_window->read32(ControllerRegister::GlobalControl); - return (global_control & GlobalControlFlag::ControllerReset) > 0; - })); - - // 4.3 Codec Discovery: - // "The software must wait at least 521 us (25 frames) after reading CRST as a 1 before - // assuming that codecs have all made status change requests and have been registered - // by the controller." - microseconds_delay(frame_delay_in_microseconds(25)); - - dbgln_if(INTEL_HDA_DEBUG, "Controller reset"); - return {}; -} - -ErrorOr Controller::handle_interrupt(Badge) -{ - // Check if any interrupt status bit is set - auto interrupt_status = m_controller_io_window->read32(ControllerRegister::InterruptStatus); - if ((interrupt_status & InterruptStatusFlag::GlobalInterruptStatus) == 0) - return false; - - // FIXME: Actually look at interrupt_status and iterate over streams as soon as - // we support multiple streams. - if (m_output_path) - TRY(m_output_path->output_stream().handle_interrupt({})); - - return true; -} - -RefPtr Controller::audio_channel(u32 index) const -{ - if (index != fixed_audio_channel_index) - return {}; - return m_audio_channel; -} - -ErrorOr Controller::write(size_t channel_index, UserOrKernelBuffer const& data, size_t length) -{ - if (channel_index != fixed_audio_channel_index || !m_output_path) - return ENODEV; - return m_output_path->output_stream().write(data, length); -} - -ErrorOr Controller::set_pcm_output_sample_rate(size_t channel_index, u32 samples_per_second_rate) -{ - if (channel_index != fixed_audio_channel_index || !m_output_path) - return ENODEV; - - TRY(m_output_path->set_format({ - .sample_rate = samples_per_second_rate, - .pcm_bits = OutputPath::fixed_pcm_bits, - .number_of_channels = OutputPath::fixed_channel_count, - })); - dmesgln_pci(*this, "Set output channel #{} PCM rate: {} Hz", channel_index, samples_per_second_rate); - return {}; -} - -ErrorOr Controller::get_pcm_output_sample_rate(size_t channel_index) -{ - if (channel_index != fixed_audio_channel_index || !m_output_path) - return ENODEV; - - return m_output_path->output_stream().sample_rate(); -} - -ErrorOr wait_until(size_t delay_in_microseconds, size_t timeout_in_microseconds, Function()> condition) -{ - auto const timeout = Duration::from_microseconds(static_cast(timeout_in_microseconds)); - auto const& time_management = TimeManagement::the(); - auto start = time_management.monotonic_time(TimePrecision::Precise); - while (!TRY(condition())) { - microseconds_delay(delay_in_microseconds); - if (time_management.monotonic_time(TimePrecision::Precise) - start >= timeout) - return ETIMEDOUT; - } - return {}; -} - -} diff --git a/Kernel/Devices/Audio/IntelHDA/Controller.h b/Kernel/Devices/Audio/IntelHDA/Controller.h deleted file mode 100644 index 0cefa8b22b4..00000000000 --- a/Kernel/Devices/Audio/IntelHDA/Controller.h +++ /dev/null @@ -1,100 +0,0 @@ -/* - * Copyright (c) 2023, Jelle Raaijmakers - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace Kernel::Audio::IntelHDA { - -// Specification: https://www.intel.com/content/dam/www/public/us/en/documents/product-specifications/high-definition-audio-specification.pdf - -class Codec; - -class Controller final - : public AudioController - , public PCI::Device { -public: - static ErrorOr probe(PCI::DeviceIdentifier const&); - static ErrorOr> create(PCI::DeviceIdentifier const&); - virtual ~Controller() = default; - - // ^PCI::Device - virtual StringView device_name() const override { return "IntelHDA"sv; } - - ErrorOr handle_interrupt(Badge); - ErrorOr send_command(u8 codec_address, u8 node_id, CodecControlVerb verb, u16 payload); - -private: - static constexpr size_t fixed_audio_channel_index = 0; - - // 3.3: High Definition Audio Controller Register Set - enum ControllerRegister : u8 { - GlobalCapabilities = 0x00, - VersionMinor = 0x02, - VersionMajor = 0x03, - GlobalControl = 0x08, - StateChangeStatus = 0x0e, - InterruptControl = 0x20, - InterruptStatus = 0x24, - CommandOutboundRingBufferOffset = 0x40, - ResponseInboundRingBufferOffset = 0x50, - StreamsOffset = 0x80, - }; - - // 3.3.7: GCTL – Global Control - enum GlobalControlFlag : u32 { - ControllerReset = 1u << 0, - AcceptUnsolicitedResponseEnable = 1u << 8, - }; - - // 3.3.14: INTCTL – Interrupt Control - enum InterruptControlFlag : u32 { - GlobalInterruptEnable = 1u << 31, - }; - - // 3.3.15: INTSTS – Interrupt Status - enum InterruptStatusFlag : u32 { - GlobalInterruptStatus = 1u << 31, - }; - - Controller(PCI::DeviceIdentifier const&, NonnullOwnPtr); - - ErrorOr initialize_codec(u8 codec_address); - ErrorOr configure_output_route(); - ErrorOr reset(); - - // ^AudioController - virtual RefPtr audio_channel(u32 index) const override; - virtual ErrorOr write(size_t channel_index, UserOrKernelBuffer const& data, size_t length) override; - virtual ErrorOr initialize(Badge) override; - virtual ErrorOr set_pcm_output_sample_rate(size_t channel_index, u32 samples_per_second_rate) override; - virtual ErrorOr get_pcm_output_sample_rate(size_t channel_index) override; - - NonnullOwnPtr m_controller_io_window; - u8 m_number_of_output_streams; - u8 m_number_of_input_streams; - u8 m_number_of_bidirectional_streams; - OwnPtr m_command_buffer; - OwnPtr m_response_buffer; - RefPtr m_interrupt_handler; - Vector> m_codecs {}; - OwnPtr m_output_path; - RefPtr m_audio_channel; -}; - -} diff --git a/Kernel/Devices/Audio/IntelHDA/Format.cpp b/Kernel/Devices/Audio/IntelHDA/Format.cpp deleted file mode 100644 index 64f26e0379b..00000000000 --- a/Kernel/Devices/Audio/IntelHDA/Format.cpp +++ /dev/null @@ -1,140 +0,0 @@ -/* - * Copyright (c) 2023, Jelle Raaijmakers - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "Format.h" -#include - -namespace Kernel::Audio::IntelHDA { - -// 3.3.41: Input/Output/Bidirectional Stream Descriptor Format -// 3.7.1: Stream Format Structure -struct SampleRateParameters { - u32 sample_rate; - u8 base; - u8 multiple; - u8 divisor; -}; -static constexpr Array sample_rate_parameters { { - // clang-format off - { 6'000, 0b0, 0b000, 0b111 }, - { 8'000, 0b0, 0b000, 0b101 }, - { 9'600, 0b0, 0b000, 0b100 }, - { 11'025, 0b1, 0b000, 0b011 }, - { 16'000, 0b0, 0b000, 0b010 }, - { 22'050, 0b1, 0b000, 0b001 }, - { 24'000, 0b0, 0b000, 0b001 }, - { 32'000, 0b0, 0b001, 0b010 }, - { 44'100, 0b1, 0b000, 0b000 }, - { 48'000, 0b0, 0b000, 0b000 }, - { 88'200, 0b1, 0b001, 0b000 }, - { 96'000, 0b0, 0b001, 0b000 }, - { 144'000, 0b0, 0b010, 0b000 }, - { 176'400, 0b1, 0b011, 0b000 }, - { 192'000, 0b0, 0b011, 0b000 }, - // clang-format on -} }; - -struct PcmBitsParameters { - u8 pcm_bits; - u8 encoding; -}; -static constexpr Array pcm_bits_parameters { { - // clang-format off - { 8, 0b000 }, - { 16, 0b001 }, - { 20, 0b010 }, - { 24, 0b011 }, - { 32, 0b100 }, - // clang-format on -} }; - -ErrorOr encode_format(FormatParameters format) -{ - // 3.3.41: Input/Output/Bidirectional Stream Descriptor Format - // 3.7.1: Stream Format Structure - - // Stream type - // NOTE: we only support PCM streams - auto is_pcm = true; - - // Sample rate parameters - Optional selected_sample_rate {}; - for (auto sample_rate_parameter : sample_rate_parameters) { - if (sample_rate_parameter.sample_rate == format.sample_rate) { - selected_sample_rate = sample_rate_parameter; - break; - } - } - if (!selected_sample_rate.has_value()) - return ENOTSUP; - - // Bit size - Optional selected_bit_rate {}; - for (auto pcm_bits_parameter : pcm_bits_parameters) { - if (pcm_bits_parameter.pcm_bits == format.pcm_bits) { - selected_bit_rate = pcm_bits_parameter; - break; - } - } - if (!selected_bit_rate.has_value()) - return ENOTSUP; - - // Number of channels - if (format.number_of_channels < 1 || format.number_of_channels > 16) - return ENOTSUP; - - // Construct stream format - return ((is_pcm ? 0 : 1) << 15) - | ((selected_sample_rate->base & 0x1) << 14) - | ((selected_sample_rate->multiple & 0x7) << 11) - | ((selected_sample_rate->divisor & 0x7) << 8) - | ((selected_bit_rate->encoding & 0x7) << 4) - | ((format.number_of_channels - 1) & 0xf); -} - -ErrorOr decode_format(u16 format) -{ - // 3.3.41: Input/Output/Bidirectional Stream Descriptor Format - // 3.7.1: Stream Format Structure - - // Sample rate - u8 sample_rate_base = (format >> 14) & 0x1; - u8 sample_rate_multiple = (format >> 11) & 0x7; - u8 sample_rate_divisor = (format >> 8) & 0x7; - Optional found_sample_rate {}; - for (auto sample_rate_parameter : sample_rate_parameters) { - if (sample_rate_parameter.base == sample_rate_base - && sample_rate_parameter.multiple == sample_rate_multiple - && sample_rate_parameter.divisor == sample_rate_divisor) { - found_sample_rate = sample_rate_parameter; - break; - } - } - - // PCM bits - u8 pcm_bits = (format >> 4) & 0x7; - Optional found_pcm_bits {}; - for (auto pcm_bits_parameter : pcm_bits_parameters) { - if (pcm_bits_parameter.encoding == pcm_bits) { - found_pcm_bits = pcm_bits_parameter; - break; - } - } - - // Number of channels - u8 number_of_channels = (format & 0xf) + 1; - - if (!found_sample_rate.has_value() || !found_pcm_bits.has_value()) - return EINVAL; - - return FormatParameters { - .sample_rate = found_sample_rate.release_value().sample_rate, - .pcm_bits = found_pcm_bits.release_value().pcm_bits, - .number_of_channels = number_of_channels, - }; -} - -} diff --git a/Kernel/Devices/Audio/IntelHDA/Format.h b/Kernel/Devices/Audio/IntelHDA/Format.h deleted file mode 100644 index df2d8b43f53..00000000000 --- a/Kernel/Devices/Audio/IntelHDA/Format.h +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright (c) 2023, Jelle Raaijmakers - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include - -namespace Kernel::Audio::IntelHDA { - -struct FormatParameters { - u32 sample_rate; - u8 pcm_bits; - u8 number_of_channels; -}; - -ErrorOr encode_format(FormatParameters format); -ErrorOr decode_format(u16 format); - -} diff --git a/Kernel/Devices/Audio/IntelHDA/InterruptHandler.cpp b/Kernel/Devices/Audio/IntelHDA/InterruptHandler.cpp deleted file mode 100644 index 59cf9d527c9..00000000000 --- a/Kernel/Devices/Audio/IntelHDA/InterruptHandler.cpp +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright (c) 2023, Jelle Raaijmakers - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include - -namespace Kernel::Audio::IntelHDA { - -InterruptHandler::InterruptHandler(Controller& controller) - : PCI::IRQHandler(controller, controller.device_identifier().interrupt_line().value()) - , m_controller(controller) -{ - enable_irq(); -} - -bool InterruptHandler::handle_irq(RegisterState const&) -{ - auto result_or_error = m_controller.handle_interrupt({}); - if (result_or_error.is_error()) { - dmesgln("IntelHDA: Error during interrupt handling: {}", result_or_error.release_error()); - return false; - } - return result_or_error.release_value(); -} - -} diff --git a/Kernel/Devices/Audio/IntelHDA/InterruptHandler.h b/Kernel/Devices/Audio/IntelHDA/InterruptHandler.h deleted file mode 100644 index 43f8e108244..00000000000 --- a/Kernel/Devices/Audio/IntelHDA/InterruptHandler.h +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright (c) 2023, Jelle Raaijmakers - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include - -namespace Kernel::Audio::IntelHDA { - -class Controller; - -class InterruptHandler - : public PCI::IRQHandler - , public RefCounted { -public: - static ErrorOr> create(Controller& controller) - { - return adopt_nonnull_ref_or_enomem(new (nothrow) InterruptHandler(controller)); - } - - // ^PCI::IRQHandler - virtual StringView purpose() const override { return "IntelHDA IRQ Handler"sv; } - -private: - InterruptHandler(Controller& controller); - - // ^PCI::IRQHandler - virtual bool handle_irq(RegisterState const&) override; - - Controller& m_controller; -}; - -} diff --git a/Kernel/Devices/Audio/IntelHDA/OutputPath.h b/Kernel/Devices/Audio/IntelHDA/OutputPath.h deleted file mode 100644 index 7d6e8b57b43..00000000000 --- a/Kernel/Devices/Audio/IntelHDA/OutputPath.h +++ /dev/null @@ -1,149 +0,0 @@ -/* - * Copyright (c) 2023, Jelle Raaijmakers - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include -#include -#include - -namespace Kernel::Audio::IntelHDA { - -class WidgetNode; - -class OutputPath { -public: - static constexpr u8 fixed_pcm_bits = 16; - static constexpr u8 fixed_channel_count = 2; - - static ErrorOr> create(Vector> widget_path, NonnullOwnPtr output_stream) - { - return adopt_nonnull_own_or_enomem(new (nothrow) OutputPath(move(widget_path), move(output_stream))); - } - - OutputStream& output_stream() { return *m_output_stream; } - - ErrorOr activate() - { - // Power on the function group and all widgets that support it - auto output_widget = get(); - auto group = output_widget->parent_node(); - TRY(group->set_power_state(Node::PowerState::D0)); - for (auto& widget : m_widget_path) { - if (widget->power_control_supported()) - TRY(widget->set_power_state(Node::PowerState::D0)); - } - - // Link the audio output widget to the output stream number and first channel - TRY(output_widget->set_converter_stream_and_channel(m_output_stream->stream_number(), OutputStream::fixed_channel)); - - // Set full volume for all output amplifiers in the path - for (auto& widget : m_widget_path) { - if (!widget->output_amp_present()) - continue; - - // NOTE: setting gain to the offset means 0dB attenuation / 100% volume - TRY(widget->set_amplifier_gain_mute({ - .mute = false, - .gain = widget->output_amp_capabilities().offset, - })); - } - - // Walk through pairs of widgets and connect them to each other - for (size_t i = 0; i < m_widget_path.size() - 1; ++i) { - auto left_widget = m_widget_path[i]; - auto right_widget = m_widget_path[i + 1]; - - VERIFY(left_widget->connection_list_present()); - if (left_widget->connection_list().size() == 1) { - // If there is only one possible connection, it is fixed and we cannot change it. - VERIFY(left_widget->connection_selected_node_id() == right_widget->node_id()); - } else { - // Find the index of the right widget node id in the connection list - size_t connection_index = 0; - for (auto connection_node_id : left_widget->connection_list()) { - if (connection_node_id == right_widget->node_id()) - break; - ++connection_index; - } - VERIFY(connection_index < left_widget->connection_list().size()); - - // Select this index - TRY(left_widget->set_connection_select(connection_index)); - } - } - - // Enable pin complex output - auto pin_widget = get(); - TRY(pin_widget->set_pin_control({ .output_enabled = true })); - - // Finally, retrieve the active converter format for the output widget and set the same for our output stream - auto converter_format = TRY(output_widget->get_converter_format()); - TRY(set_format(converter_format)); - return {}; - } - - ErrorOr set_format(FormatParameters format) - { - // FIXME: support other PCM bit sizes and channel counts - format.pcm_bits = fixed_pcm_bits; - format.number_of_channels = fixed_channel_count; - - // 7.3.3.8: Converter Format - // "The Converter Format control determines the format the converter will use. This must match the - // format programmed into the Stream Descriptor on the controller so that the data format being - // transmitted on the link matches what is expected by the consumer of the data." - auto output_widget = get(); - if (!output_widget->supported_pcm_rates().contains_slow(format.sample_rate) - || !output_widget->supported_pcm_sizes().contains_slow(format.pcm_bits) - || format.number_of_channels > output_widget->channel_count()) - return ENOTSUP; - - TRY(m_output_stream->set_format(format)); - TRY(output_widget->set_converter_format(format)); - return {}; - } - - ErrorOr> to_string() - { - StringBuilder builder; - TRY(builder.try_append("OutputPath: ["sv)); - for (size_t i = 0; i < m_widget_path.size(); ++i) { - auto widget = m_widget_path[i]; - TRY(builder.try_append(TRY(widget->to_string())->view())); - if (i < m_widget_path.size() - 1) - TRY(builder.try_append(" → "sv)); - } - TRY(builder.try_append(']')); - return KString::try_create(builder.string_view()); - } - -private: - OutputPath(Vector> widget_path, NonnullOwnPtr output_stream) - : m_widget_path(move(widget_path)) - , m_output_stream(move(output_stream)) - { - } - - template - NonnullRefPtr get() - { - for (auto& widget : m_widget_path) { - if (widget->widget_type() == T) - return widget; - } - VERIFY_NOT_REACHED(); - } - - Vector> m_widget_path; - NonnullOwnPtr m_output_stream; -}; - -} diff --git a/Kernel/Devices/Audio/IntelHDA/RingBuffer.h b/Kernel/Devices/Audio/IntelHDA/RingBuffer.h deleted file mode 100644 index cb9b3d2c2f7..00000000000 --- a/Kernel/Devices/Audio/IntelHDA/RingBuffer.h +++ /dev/null @@ -1,250 +0,0 @@ -/* - * Copyright (c) 2023, Jelle Raaijmakers - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include - -namespace Kernel::Audio::IntelHDA { - -enum class RingBufferType { - Input, - Output, -}; - -// 4.4.1, 4.4.2: CORB and RIRB -template -class ControllerRingBuffer { -public: - ControllerRingBuffer(size_t capacity, NonnullOwnPtr buffer, NonnullOwnPtr register_window) - : m_capacity(capacity) - , m_buffer(move(buffer)) - , m_register_window(move(register_window)) - { - // 3.3.22, 3.3.29: Read DMA engine running bit - u8 control = m_register_window->read8(RingBufferRegisterOffset::Control); - m_running = (control & RingBufferControlFlag::DMAEnable) > 0; - } - - static ErrorOr> create(StringView name, NonnullOwnPtr register_window) - { - // 3.3.24, 3.3.31: Read the size capability - auto buffer_size = register_window->read8(RingBufferRegisterOffset::Size); - u8 size_capability = buffer_size >> 4; - size_t capacity = ((size_capability & SizeCapabilityFlag::Supports2) > 0) ? 2 : 0; - if ((size_capability & SizeCapabilityFlag::Supports16) > 0) - capacity = 16; - if ((size_capability & SizeCapabilityFlag::Supports256) > 0) - capacity = 256; - if (capacity == 0) - return Error::from_string_view_or_print_error_and_return_errno("RingBuffer reports invalid capacity"sv, ENOTSUP); - - // Create a DMA buffer page to holds the ring buffer - VERIFY(PAGE_SIZE >= capacity * sizeof(T)); - auto buffer_region = TRY(MM.allocate_dma_buffer_page(name, U == RingBufferType::Input ? Memory::Region::Access::Read : Memory::Region::Access::Write)); - - // 4.4.1.1, 4.4.2: The CORB buffer in memory must be allocated to start on a 128-byte boundary - // and in memory configured to match the access type being used. - VERIFY((buffer_region->physical_page(0)->paddr().get() & 0x7f) == 0); - - return adopt_nonnull_own_or_enomem(new (nothrow) ControllerRingBuffer(capacity, move(buffer_region), move(register_window))); - } - - size_t capacity() const { return m_capacity; } - - ErrorOr> read_value() - requires(U == RingBufferType::Input) - { - // 4.4.2: Response Inbound Ring Buffer - RIRB - auto write_pointer = controller_pointer(); - dbgln_if(INTEL_HDA_DEBUG, "ControllerRingBuffer({}) {}: current_pointer {} write_pointer {}", - U == RingBufferType::Input ? "input"sv : "output"sv, __FUNCTION__, m_current_pointer, write_pointer); - if (m_current_pointer == write_pointer) - return Optional {}; - - m_current_pointer = (m_current_pointer + 1) % m_capacity; - return Optional { *(reinterpret_cast(m_buffer->vaddr().get()) + m_current_pointer) }; - } - - // 4.4.1.3, 4.4.2.2: Initializing the CORB/RIRB - ErrorOr register_with_controller() - { - // 4.4.1.3, 4.4.2.2: Stop DMA engine - TRY(set_dma_engine_running(false)); - - // 3.3.18, 3.3.19, 3.3.25, 3.3.26, 4.4.1.3: Set base address - PhysicalPtr buffer_address = m_buffer->physical_page(0)->paddr().get(); - m_register_window->write32(RingBufferRegisterOffset::LowerBaseAddress, buffer_address & 0xffffff80u); - if constexpr (sizeof(PhysicalPtr) == 8) - m_register_window->write32(RingBufferRegisterOffset::UpperBaseAddress, buffer_address >> 32); - - // 3.3.24, 3.3.31, 4.4.1.3: Set buffer capacity if more than one capacity is supported - auto buffer_size = m_register_window->read8(RingBufferRegisterOffset::Size) & static_cast(~0b11); - u8 size_capability = buffer_size >> 4; - if (popcount(size_capability) > 1) { - switch (m_capacity) { - case 2: - break; - case 16: - buffer_size |= 0b01; - break; - case 256: - buffer_size |= 0b10; - break; - default: - VERIFY_NOT_REACHED(); - } - m_register_window->write8(RingBufferRegisterOffset::Size, buffer_size); - } - - // 4.4.1.3: Reset read and write pointers to 0 - TRY(reset_controller_pointer()); - if constexpr (U == RingBufferType::Output) - set_write_pointer(0); - - // FIXME: Qemu's Intel HDA device compares the RINTCNT register with the number of responses sent, even - // if interrupts are disabled. This is a workaround and allows us to receive 255 responses. We - // should try to fix this upstream or toggle this fix with device quirks logic. - if constexpr (U == RingBufferType::Input) - m_register_window->write16(RingBufferRegisterOffset::ResponseInterruptCount, 0xff); - - TRY(set_dma_engine_running(true)); - - return {}; - } - - ErrorOr write_value(T value) - requires(U == RingBufferType::Output) - { - // 4.4.1.4: Transmitting Commands via the CORB - auto read_pointer = controller_pointer(); - auto write_pointer = (m_current_pointer + 1) % m_capacity; - dbgln_if(INTEL_HDA_DEBUG, "ControllerRingBuffer({}) {}: read_pointer {} write_pointer {}", - U == RingBufferType::Input ? "input"sv : "output"sv, __FUNCTION__, read_pointer, write_pointer); - - if (write_pointer == read_pointer) - return ENOSPC; - - auto* target_slot = reinterpret_cast(m_buffer->vaddr().get()) + write_pointer; - *target_slot = value; - set_write_pointer(write_pointer); - return {}; - } - -private: - // 3.3: High Definition Audio Controller Register Set - CORB/RIRB - enum RingBufferRegisterOffset : u8 { - LowerBaseAddress = 0x0, - UpperBaseAddress = 0x4, - WritePointer = 0x8, - ReadPointer = 0xa, - ResponseInterruptCount = 0xa, - Control = 0xc, - Status = 0xd, - Size = 0xe, - }; - - // 3.3.21, 3.3.27: Read/Write Pointer - enum PointerFlag : u16 { - Reset = 1u << 15, - }; - - // 3.3.22, 3.3.29: Ring Buffer Control - enum RingBufferControlFlag : u8 { - DMAEnable = 1u << 1, - }; - - // 3.3.24, 3.3.31: Size - enum SizeCapabilityFlag : u8 { - Supports2 = 1u << 0, - Supports16 = 1u << 1, - Supports256 = 1u << 2, - }; - - u8 controller_pointer() - { - // 3.3.21, 3.3.27: Get the Read/Write pointer - auto offset = U == RingBufferType::Input - ? RingBufferRegisterOffset::WritePointer - : RingBufferRegisterOffset::ReadPointer; - return m_register_window->read16(offset) & 0xffu; - } - - ErrorOr reset_controller_pointer() - { - // 3.3.21, 3.3.27: Set the Read/Write pointer reset bit - auto offset = U == RingBufferType::Input - ? RingBufferRegisterOffset::WritePointer - : RingBufferRegisterOffset::ReadPointer; - m_register_window->write16(offset, PointerFlag::Reset); - - if constexpr (U == RingBufferType::Output) { - // 3.3.21: "The hardware will physically update this bit to 1 when the CORB pointer reset is - // complete. Software must read a 1 to verify that the reset completed correctly." - TRY(wait_until(frame_delay_in_microseconds(1), controller_timeout_in_microseconds, [&]() { - u16 read_pointer = m_register_window->read16(offset); - return (read_pointer & PointerFlag::Reset) > 0; - })); - - // 3.3.21: "Software must clear this bit back to 0, by writing a 0, and then read back the 0 - // to verify that the clear completed correctly." - m_register_window->write16(offset, 0); - TRY(wait_until(frame_delay_in_microseconds(1), controller_timeout_in_microseconds, [&]() { - u16 read_pointer = m_register_window->read16(offset); - return (read_pointer & PointerFlag::Reset) == 0; - })); - } - - dbgln_if(INTEL_HDA_DEBUG, "ControllerRingBuffer({}) {}", - U == RingBufferType::Input ? "input"sv : "output"sv, __FUNCTION__); - - return {}; - } - - ErrorOr set_dma_engine_running(bool running) - { - if (m_running == running) - return {}; - - // 3.3.22, 3.3.29: Set DMA engine running bit - u8 control = m_register_window->read8(RingBufferRegisterOffset::Control); - if (running) - control |= RingBufferControlFlag::DMAEnable; - else - control &= ~RingBufferControlFlag::DMAEnable; - dbgln_if(INTEL_HDA_DEBUG, "ControllerRingBuffer({}) {}: {:#08b}", - U == RingBufferType::Input ? "input"sv : "output"sv, __FUNCTION__, control); - m_register_window->write8(RingBufferRegisterOffset::Control, control); - - // Must read the value back - TRY(wait_until(frame_delay_in_microseconds(1), controller_timeout_in_microseconds, [&]() { - control = m_register_window->read8(RingBufferRegisterOffset::Control); - return (control & RingBufferControlFlag::DMAEnable) == (running ? RingBufferControlFlag::DMAEnable : 0); - })); - m_running = running; - return {}; - } - - void set_write_pointer(u8 pointer) - requires(U == RingBufferType::Output) - { - // 3.3.20: CORBWP – CORB Write Pointer - m_register_window->write16(RingBufferRegisterOffset::WritePointer, pointer); - m_current_pointer = pointer; - } - - size_t m_capacity; - NonnullOwnPtr m_buffer; - NonnullOwnPtr m_register_window; - bool m_running { false }; - u8 m_current_pointer { 0 }; -}; - -using CommandOutboundRingBuffer = ControllerRingBuffer; -using ResponseInboundRingBuffer = ControllerRingBuffer; - -} diff --git a/Kernel/Devices/Audio/IntelHDA/Stream.cpp b/Kernel/Devices/Audio/IntelHDA/Stream.cpp deleted file mode 100644 index a2fc7e07c66..00000000000 --- a/Kernel/Devices/Audio/IntelHDA/Stream.cpp +++ /dev/null @@ -1,271 +0,0 @@ -/* - * Copyright (c) 2023, Jelle Raaijmakers - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "Stream.h" - -#include -#include -#include -#include -#include - -namespace Kernel::Audio::IntelHDA { - -Stream::~Stream() -{ - if (m_running) - MUST(stop()); -} - -u32 Stream::read_control() -{ - // 3.3.35: Input/Output/Bidirectional Stream Descriptor Control - u32 control_and_status = m_stream_io_window->read32(StreamRegisterOffset::Control); - return control_and_status & 0xffffffu; -} - -void Stream::write_control(u32 control) -{ - // 3.3.35: Input/Output/Bidirectional Stream Descriptor Control - auto status = m_stream_io_window->read8(StreamRegisterOffset::Status); - u32 control_and_status = (status << 24) - | ((m_stream_number & 0xf) << 20) - | (control & 0xfffffu); - m_stream_io_window->write32(StreamRegisterOffset::Control, control_and_status); -} - -static constexpr u8 container_size_in_bytes(u8 bit_size) -{ - // 4.5.1: Stream Data In Memory - if (bit_size > 16) - return 4; - else if (bit_size > 8) - return 2; - return 1; -} - -ErrorOr Stream::initialize_buffer() -{ - VERIFY(m_format_parameters.sample_rate > 0); - VERIFY(m_format_parameters.pcm_bits > 0); - VERIFY(m_format_parameters.number_of_channels > 0); - - // 4.5.1: Stream Data In Memory - // NOTE: we ignore the number of blocks per packet since we are only required to have an integer number - // of samples per buffer, and we always have at least one packet per buffer. - size_t block_size_in_bytes = container_size_in_bytes(m_format_parameters.pcm_bits) * m_format_parameters.number_of_channels; - size_t number_of_blocks_in_buffer = PAGE_SIZE / block_size_in_bytes; - VERIFY(number_of_blocks_in_buffer > 0); - - size_t number_of_blocks_required_for_cyclic_buffer_size = ceil_div(cyclic_buffer_size_in_ms * m_format_parameters.sample_rate, 1'000); - size_t number_of_buffers_required_for_cyclic_buffer_size = AK::max(ceil_div(number_of_blocks_required_for_cyclic_buffer_size, number_of_blocks_in_buffer), minimum_number_of_buffers); - VERIFY(number_of_buffers_required_for_cyclic_buffer_size > 0 && number_of_buffers_required_for_cyclic_buffer_size <= 256); - - size_t cyclic_buffer_size_in_bytes = number_of_buffers_required_for_cyclic_buffer_size * PAGE_SIZE; - - TRY(m_buffers.with([&](auto& buffers) -> ErrorOr { - buffers = TRY(MM.allocate_dma_buffer_pages(cyclic_buffer_size_in_bytes, "IntelHDA Stream Buffers"sv, Memory::Region::Access::ReadWrite)); - - // 3.3.38 Input/Output/Bidirectional Stream Descriptor Cyclic Buffer Length - m_stream_io_window->write32(StreamRegisterOffset::CyclicBufferLength, buffers->size()); - - // 3.3.39: Input/Output/Bidirectional Stream Descriptor Last Valid Index - m_stream_io_window->write16(StreamRegisterOffset::LastValidIndex, number_of_buffers_required_for_cyclic_buffer_size - 1); - - // 3.6.2: Buffer Descriptor List - m_buffer_descriptor_list = TRY(MM.allocate_dma_buffer_page("IntelHDA Stream BDL"sv, Memory::Region::Access::ReadWrite)); - auto bdl_physical_address = m_buffer_descriptor_list->physical_page(0)->paddr().get(); - m_stream_io_window->write32(StreamRegisterOffset::BDLLowerBaseAddress, bdl_physical_address & 0xffffffffu); - m_stream_io_window->write32(StreamRegisterOffset::BDLUpperBaseAddress, bdl_physical_address >> 32); - - // 3.6.3: Buffer Descriptor List Entry - auto* buffer_descriptors = bit_cast(m_buffer_descriptor_list->vaddr().as_ptr()); - for (size_t buffer_index = 0; buffer_index < buffers->page_count(); ++buffer_index) { - auto* entry = &buffer_descriptors[buffer_index]; - entry->address = buffers->physical_page(buffer_index)->paddr().get(); - entry->size = PAGE_SIZE; - entry->flags = BufferDescriptorEntryFlag::InterruptOnCompletion; - } - return {}; - })); - return {}; -} - -ErrorOr Stream::reset() -{ - // 3.3.35: Input/Output/Bidirectional Stream Descriptor Control - if (m_running) - TRY(stop()); - - // Writing a 1 causes the corresponding stream to be reset. The Stream Descriptor registers - // (except the SRST bit itself), FIFO's, and cadence generator for the corresponding stream - // are reset. - auto control = read_control(); - control |= StreamControlFlag::StreamReset; - write_control(control); - - // After the stream hardware has completed sequencing into the reset state, it will report a - // 1 in this bit. Software must read a 1 from this bit to verify that the stream is in reset. - TRY(wait_until(frame_delay_in_microseconds(1), controller_timeout_in_microseconds, [&]() { - control = read_control(); - return (control & StreamControlFlag::StreamReset) > 0; - })); - - // Writing a 0 causes the corresponding stream to exit reset. - control &= ~StreamControlFlag::StreamReset; - write_control(control); - - // When the stream hardware is ready to begin operation, it will report a 0 in this bit. - // Software must read a 0 from this bit before accessing any of the stream registers - return wait_until(frame_delay_in_microseconds(1), controller_timeout_in_microseconds, [&]() { - control = read_control(); - return (control & StreamControlFlag::StreamReset) == 0; - }); -} - -void Stream::start() -{ - // 3.3.35: Input/Output/Bidirectional Stream Descriptor Control - VERIFY(!m_running); - dbgln_if(INTEL_HDA_DEBUG, "IntelHDA: Starting stream"); - - auto control = read_control(); - control |= StreamControlFlag::StreamRun; - control |= StreamControlFlag::InterruptOnCompletionEnable; - write_control(control); - m_running = true; -} - -ErrorOr Stream::stop() -{ - // 3.3.35: Input/Output/Bidirectional Stream Descriptor Control - VERIFY(m_running); - dbgln_if(INTEL_HDA_DEBUG, "IntelHDA: Stopping stream"); - - auto control = read_control(); - control &= ~StreamControlFlag::StreamRun; - write_control(control); - - // 4.5.4: Stopping Streams - // Wait until RUN bit is 0 - TRY(wait_until(frame_delay_in_microseconds(1), controller_timeout_in_microseconds, [&]() { - control = read_control(); - return (control & StreamControlFlag::StreamRun) == 0; - })); - - m_running = false; - m_buffer_position = 0; - return {}; -} - -ErrorOr Stream::set_format(FormatParameters format) -{ - // Reset the stream so we can set a new buffer - TRY(reset()); - - // Write the sample rate payload - auto format_payload = TRY(encode_format(format)); - m_stream_io_window->write16(StreamRegisterOffset::Format, format_payload); - m_format_parameters = format; - - // Re-initialize the bufer - TRY(initialize_buffer()); - return {}; -} - -ErrorOr OutputStream::handle_interrupt(Badge) -{ - auto interrupt_status = m_stream_io_window->read8(StreamRegisterOffset::Status); - - if ((interrupt_status & StreamStatusFlag::BufferCompletionInterruptStatus) > 0) { - // 3.3.36: BCIS remains active until software clears it by writing a 1 to this bit position. - m_stream_io_window->write8(StreamRegisterOffset::Status, interrupt_status); - - // Wake up thread waiting for new buffers to write to - m_irq_queue.wake_all(); - - // If the read head is past our last written buffer, stop the stream. There are three possible - // condition combinations of last & new link, and current buffer position for our circular buffer. - auto new_link_position = m_stream_io_window->read32(StreamRegisterOffset::LinkPosition); - if ((m_last_link_position < m_buffer_position && m_buffer_position < new_link_position) - || (new_link_position < m_last_link_position && m_last_link_position < m_buffer_position) - || (m_buffer_position < new_link_position && new_link_position < m_last_link_position)) { - dbgln_if(INTEL_HDA_DEBUG, "OutputStream::{}: Stopping because of stream underrun (link position: {} → {}, buffer position: {})", - __FUNCTION__, m_last_link_position, new_link_position, m_buffer_position); - TRY(stop()); - } - m_last_link_position = new_link_position; - } - - return {}; -} - -ErrorOr OutputStream::write(UserOrKernelBuffer const& data, size_t length) -{ - auto wait_until_buffer_index_can_be_written = [&](u8 buffer_index) { - while (m_running) { - m_last_link_position = m_stream_io_window->read32(StreamRegisterOffset::LinkPosition); - auto read_buffer_index = m_last_link_position / PAGE_SIZE; - if (read_buffer_index != buffer_index) - return; - - dbgln_if(INTEL_HDA_DEBUG, "IntelHDA: Waiting until buffer {} becomes writeable", buffer_index); - - m_irq_queue.wait_forever("IntelHDA"sv); - } - }; - - auto write_into_single_buffer = [&](UserOrKernelBuffer const& data, size_t data_offset, size_t length, size_t offset_within_buffer) -> ErrorOr { - u8 buffer_index = m_buffer_position / PAGE_SIZE; - VERIFY(length <= PAGE_SIZE - offset_within_buffer); - - wait_until_buffer_index_can_be_written(buffer_index); - - TRY(m_buffers.with([&](auto& buffers) -> ErrorOr { - // NOTE: if the buffers were reinitialized, we might point to an out-of-bounds page - if (buffer_index >= buffers->page_count()) - return EAGAIN; - - auto* buffer = buffers->vaddr_from_page_index(buffer_index).as_ptr() + offset_within_buffer; - TRY(data.read(buffer, data_offset, length)); - - // Cycle back to position 0 when we reach the end - m_buffer_position += length; - VERIFY(m_buffer_position <= buffers->size()); - if (m_buffer_position == buffers->size()) - m_buffer_position = 0; - return {}; - })); - return buffer_index; - }; - - // FIXME: support PCM bit sizes other than 16 - VERIFY(m_format_parameters.pcm_bits == 16); - - // Split up input data into separate buffer writes - size_t length_remaining = length; - size_t data_offset = 0; - u8 last_buffer_index = 0; - while (length_remaining > 0) { - size_t offset_within_current_buffer = m_buffer_position % PAGE_SIZE; - size_t length_to_write = AK::min(length_remaining, PAGE_SIZE - offset_within_current_buffer); - - last_buffer_index = TRY(write_into_single_buffer(data, data_offset, length_to_write, offset_within_current_buffer)); - - data_offset += length_to_write; - length_remaining -= length_to_write; - } - - // Start this stream if not already running - // 3.3.39: LVI must be at least 1; i.e., there must be at least two valid entries in - // the buffer descriptor list before DMA operations can begin. - if (!m_running && last_buffer_index >= 2) - start(); - - return length; -} - -} diff --git a/Kernel/Devices/Audio/IntelHDA/Stream.h b/Kernel/Devices/Audio/IntelHDA/Stream.h deleted file mode 100644 index 90f223e6728..00000000000 --- a/Kernel/Devices/Audio/IntelHDA/Stream.h +++ /dev/null @@ -1,132 +0,0 @@ -/* - * Copyright (c) 2023, Jelle Raaijmakers - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace Kernel::Audio::IntelHDA { - -class Stream { -public: - static constexpr u32 cyclic_buffer_size_in_ms = 40; - - virtual ~Stream(); - - u8 stream_number() const { return m_stream_number; } - bool running() const { return m_running; } - u32 sample_rate() const { return m_format_parameters.sample_rate; } - - void start(); - ErrorOr stop(); - virtual ErrorOr handle_interrupt(Badge) = 0; - - ErrorOr set_format(FormatParameters); - -protected: - // We always need 2 filled buffers, plus an additional one to prevent buffer underrun - static constexpr u8 minimum_number_of_buffers = 3; - - // 3.3: High Definition Audio Controller Register Set - streams - enum StreamRegisterOffset : u8 { - Control = 0x00, - Status = 0x03, - LinkPosition = 0x04, - CyclicBufferLength = 0x08, - LastValidIndex = 0x0c, - Format = 0x12, - BDLLowerBaseAddress = 0x18, - BDLUpperBaseAddress = 0x1c, - }; - - // 3.3.35: Input/Output/Bidirectional Stream Descriptor Control - enum StreamControlFlag : u32 { - StreamReset = 1u << 0, - StreamRun = 1u << 1, - InterruptOnCompletionEnable = 1u << 2, - }; - - // 3.3.36 : Input/Output/Bidirectional Stream Descriptor Status - enum StreamStatusFlag : u8 { - BufferCompletionInterruptStatus = 1u << 2, - }; - - // 3.6.3: Buffer Descriptor List Entry - enum BufferDescriptorEntryFlag : u32 { - InterruptOnCompletion = 1u << 0, - }; - - // 3.6.3: Buffer Descriptor List Entry - struct BufferDescriptorEntry { - u64 address; - u32 size; - BufferDescriptorEntryFlag flags; - }; - - Stream(NonnullOwnPtr stream_io_window, u8 stream_number) - : m_stream_io_window(move(stream_io_window)) - , m_stream_number(stream_number) - { - } - - u32 read_control(); - void write_control(u32); - - ErrorOr initialize_buffer(); - ErrorOr reset(); - - NonnullOwnPtr m_stream_io_window; - u8 m_stream_number; - OwnPtr m_buffer_descriptor_list; - SpinlockProtected, LockRank::None> m_buffers; - size_t m_buffer_position { 0 }; - WaitQueue m_irq_queue; - bool m_running { false }; - FormatParameters m_format_parameters; -}; - -class OutputStream : public Stream { -public: - static constexpr u8 fixed_channel = 0; - - static ErrorOr> create(NonnullOwnPtr stream_io_window, u8 stream_number) - { - return adopt_nonnull_own_or_enomem(new (nothrow) OutputStream(move(stream_io_window), stream_number)); - } - - ~OutputStream() = default; - - // ^Stream - ErrorOr handle_interrupt(Badge) override; - - ErrorOr write(UserOrKernelBuffer const&, size_t); - -private: - OutputStream(NonnullOwnPtr stream_io_window, u8 stream_number) - : Stream(move(stream_io_window), stream_number) - { - // 3.3.35: Input/Output/Bidirectional Stream Descriptor Control - // "Although the controller hardware is capable of transmitting any stream number, - // by convention stream 0 is reserved as unused by software, so that converters - // whose stream numbers have been reset to 0 do not unintentionally decode data - // not intended for them." - VERIFY(stream_number >= 1); - } - - u32 m_last_link_position { 0 }; -}; - -// FIXME: implement InputStream and BidirectionalStream - -} diff --git a/Kernel/Devices/Audio/IntelHDA/Timing.h b/Kernel/Devices/Audio/IntelHDA/Timing.h deleted file mode 100644 index 0c8db54e572..00000000000 --- a/Kernel/Devices/Audio/IntelHDA/Timing.h +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright (c) 2023, Jelle Raaijmakers - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include - -namespace Kernel::Audio::IntelHDA { - -// We define an arbitrary controller timeout of 300ms for most actions. -constexpr size_t controller_timeout_in_microseconds = 300'000; - -consteval u32 frame_delay_in_microseconds(u32 frames) -{ - // NOTE: the link operates at this _fixed_ frequency and is independent of the streams' rates. - constexpr u32 link_frame_frequency_hz = 48'000; - - // 2.2: Streams and Channels - // A new frame starts exactly every 20.83 μs, corresponding to the common 48-kHz sample rate. - VERIFY(frames <= 4294); - return frames * 1'000'000u / link_frame_frequency_hz + 1u; -} - -ErrorOr wait_until(size_t delay_in_microseconds, size_t timeout_in_microseconds, Function()> condition); - -} diff --git a/Kernel/Devices/Audio/Management.cpp b/Kernel/Devices/Audio/Management.cpp deleted file mode 100644 index a7f109de8ef..00000000000 --- a/Kernel/Devices/Audio/Management.cpp +++ /dev/null @@ -1,97 +0,0 @@ -/* - * Copyright (c) 2021, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include -#include - -namespace Kernel { - -static Singleton s_the; -static Atomic s_device_minor_number; - -MajorNumber AudioManagement::audio_type_major_number() -{ - return 116; -} -MinorNumber AudioManagement::generate_storage_minor_number() -{ - auto minor_number = s_device_minor_number.load(); - s_device_minor_number++; - return minor_number; -} - -AudioManagement& AudioManagement::the() -{ - return *s_the; -} - -UNMAP_AFTER_INIT AudioManagement::AudioManagement() -{ -} - -struct PCIAudioDriverInitializer { - ErrorOr (*probe)(PCI::DeviceIdentifier const&) = nullptr; - ErrorOr> (*create)(PCI::DeviceIdentifier const&) = nullptr; -}; - -static constexpr PCIAudioDriverInitializer s_initializers[] = { - { AC97::probe, AC97::create }, - { Audio::IntelHDA::Controller::probe, Audio::IntelHDA::Controller::create }, -}; - -UNMAP_AFTER_INIT ErrorOr> AudioManagement::determine_audio_device(PCI::DeviceIdentifier const& device_identifier) const -{ - for (auto& initializer : s_initializers) { - auto initializer_probe_found_driver_match_or_error = initializer.probe(device_identifier); - if (initializer_probe_found_driver_match_or_error.is_error()) { - dmesgln("AudioManagement: Failed to probe device {}, due to {}", device_identifier.address(), initializer_probe_found_driver_match_or_error.error()); - continue; - } - auto initializer_probe_found_driver_match = initializer_probe_found_driver_match_or_error.release_value(); - if (initializer_probe_found_driver_match) { - auto device = TRY(initializer.create(device_identifier)); - TRY(device->initialize({})); - return device; - } - } - dmesgln("AudioManagement: Failed to initialize device {}, unsupported audio device", device_identifier.address()); - return Error::from_errno(ENODEV); -} - -UNMAP_AFTER_INIT void AudioManagement::enumerate_hardware_controllers() -{ - if (PCI::Access::is_disabled()) - return; - MUST(PCI::enumerate([&](PCI::DeviceIdentifier const& device_identifier) { - // Only consider PCI multimedia devices - if (device_identifier.class_code() != PCI::ClassID::Multimedia) - return; - - auto result = determine_audio_device(device_identifier); - if (result.is_error()) { - dmesgln("Failed to initialize audio device ({} {}): {}", device_identifier.address(), device_identifier.hardware_id(), result.error()); - return; - } - m_controllers_list.with([&](auto& list) { list.append(result.release_value()); }); - })); -} - -UNMAP_AFTER_INIT bool AudioManagement::initialize() -{ - enumerate_hardware_controllers(); - auto list_empty = m_controllers_list.with([&](auto& list) -> bool { - return list.is_empty(); - }); - if (list_empty) - dbgln("AudioManagement: no audio controller was initialized."); - return !list_empty; -} - -} diff --git a/Kernel/Devices/Audio/Management.h b/Kernel/Devices/Audio/Management.h deleted file mode 100644 index e479e66c77a..00000000000 --- a/Kernel/Devices/Audio/Management.h +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright (c) 2022, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include - -namespace Kernel { - -class AudioManagement { - -public: - AudioManagement(); - static AudioManagement& the(); - - static MajorNumber audio_type_major_number(); - static MinorNumber generate_storage_minor_number(); - - bool initialize(); - -private: - ErrorOr> determine_audio_device(PCI::DeviceIdentifier const& device_identifier) const; - - void enumerate_hardware_controllers(); - SpinlockProtected, LockRank::None> m_controllers_list; -}; - -} diff --git a/Kernel/Devices/BlockDevice.cpp b/Kernel/Devices/BlockDevice.cpp deleted file mode 100644 index 1d4bd8fce6c..00000000000 --- a/Kernel/Devices/BlockDevice.cpp +++ /dev/null @@ -1,116 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include - -namespace Kernel { - -AsyncBlockDeviceRequest::AsyncBlockDeviceRequest(Device& block_device, RequestType request_type, u64 block_index, u32 block_count, UserOrKernelBuffer const& buffer, size_t buffer_size) - : AsyncDeviceRequest(block_device) - , m_block_device(static_cast(block_device)) - , m_request_type(request_type) - , m_block_index(block_index) - , m_block_count(block_count) - , m_buffer(buffer) - , m_buffer_size(buffer_size) -{ -} - -void AsyncBlockDeviceRequest::start() -{ - m_block_device.start_request(*this); -} - -BlockDevice::~BlockDevice() = default; - -void BlockDevice::after_inserting_add_symlink_to_device_identifier_directory() -{ - VERIFY(m_symlink_sysfs_component); - SysFSBlockDevicesDirectory::the().devices_list({}).with([&](auto& list) -> void { - list.append(*m_symlink_sysfs_component); - }); -} - -void BlockDevice::before_will_be_destroyed_remove_symlink_from_device_identifier_directory() -{ - VERIFY(m_symlink_sysfs_component); - SysFSBlockDevicesDirectory::the().devices_list({}).with([&](auto& list) -> void { - list.remove(*m_symlink_sysfs_component); - }); -} - -// FIXME: This method will be eventually removed after all nodes in /sys/dev/block/ are symlinks -void BlockDevice::after_inserting_add_to_device_identifier_directory() -{ - VERIFY(m_sysfs_component); - SysFSBlockDevicesDirectory::the().devices_list({}).with([&](auto& list) -> void { - list.append(*m_sysfs_component); - }); -} - -// FIXME: This method will be eventually removed after all nodes in /sys/dev/block/ are symlinks -void BlockDevice::before_will_be_destroyed_remove_from_device_identifier_directory() -{ - VERIFY(m_sysfs_component); - SysFSBlockDevicesDirectory::the().devices_list({}).with([&](auto& list) -> void { - list.remove(*m_sysfs_component); - }); -} - -bool BlockDevice::read_block(u64 index, UserOrKernelBuffer& buffer) -{ - auto read_request_or_error = try_make_request(AsyncBlockDeviceRequest::Read, index, 1, buffer, m_block_size); - if (read_request_or_error.is_error()) { - dbgln("BlockDevice::read_block({}): try_make_request failed", index); - return false; - } - auto read_request = read_request_or_error.release_value(); - switch (read_request->wait().request_result()) { - case AsyncDeviceRequest::Success: - return true; - case AsyncDeviceRequest::Failure: - dbgln("BlockDevice::read_block({}) IO error", index); - break; - case AsyncDeviceRequest::MemoryFault: - dbgln("BlockDevice::read_block({}) EFAULT", index); - break; - case AsyncDeviceRequest::Cancelled: - dbgln("BlockDevice::read_block({}) cancelled", index); - break; - default: - VERIFY_NOT_REACHED(); - } - return false; -} - -bool BlockDevice::write_block(u64 index, UserOrKernelBuffer const& buffer) -{ - auto write_request_or_error = try_make_request(AsyncBlockDeviceRequest::Write, index, 1, buffer, m_block_size); - if (write_request_or_error.is_error()) { - dbgln("BlockDevice::write_block({}): try_make_request failed", index); - return false; - } - auto write_request = write_request_or_error.release_value(); - switch (write_request->wait().request_result()) { - case AsyncDeviceRequest::Success: - return true; - case AsyncDeviceRequest::Failure: - dbgln("BlockDevice::write_block({}) IO error", index); - break; - case AsyncDeviceRequest::MemoryFault: - dbgln("BlockDevice::write_block({}) EFAULT", index); - break; - case AsyncDeviceRequest::Cancelled: - dbgln("BlockDevice::write_block({}) cancelled", index); - break; - default: - VERIFY_NOT_REACHED(); - } - return false; -} - -} diff --git a/Kernel/Devices/BlockDevice.h b/Kernel/Devices/BlockDevice.h deleted file mode 100644 index f262b4b8683..00000000000 --- a/Kernel/Devices/BlockDevice.h +++ /dev/null @@ -1,95 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include - -namespace Kernel { - -class AsyncBlockDeviceRequest; - -class BlockDevice : public Device { -public: - virtual ~BlockDevice() override; - - size_t block_size() const { return m_block_size; } - u8 block_size_log() const { return m_block_size_log; } - virtual bool is_seekable() const override { return true; } - - bool read_block(u64 index, UserOrKernelBuffer&); - bool write_block(u64 index, UserOrKernelBuffer const&); - - virtual void start_request(AsyncBlockDeviceRequest&) = 0; - -protected: - BlockDevice(MajorNumber major, MinorNumber minor, size_t block_size = PAGE_SIZE) - : Device(major, minor) - , m_block_size(block_size) - { - // 512 is the minimum sector size in most block devices - VERIFY(m_block_size >= 512); - VERIFY(is_power_of_two(m_block_size)); - m_block_size_log = AK::log2(m_block_size); - } - -protected: - virtual bool is_block_device() const final { return true; } - - virtual void after_inserting_add_symlink_to_device_identifier_directory() override final; - virtual void before_will_be_destroyed_remove_symlink_from_device_identifier_directory() override final; - -private: - // FIXME: These methods will be eventually removed after all nodes in /sys/dev/block/ are symlinks - virtual void after_inserting_add_to_device_identifier_directory() override final; - virtual void before_will_be_destroyed_remove_from_device_identifier_directory() override final; - - size_t m_block_size { 0 }; - u8 m_block_size_log { 0 }; -}; - -class AsyncBlockDeviceRequest final : public AsyncDeviceRequest { -public: - enum RequestType { - Read, - Write - }; - AsyncBlockDeviceRequest(Device& block_device, RequestType request_type, - u64 block_index, u32 block_count, UserOrKernelBuffer const& buffer, size_t buffer_size); - - RequestType request_type() const { return m_request_type; } - u64 block_index() const { return m_block_index; } - u32 block_count() const { return m_block_count; } - size_t block_size() const { return m_block_device.block_size(); } - UserOrKernelBuffer& buffer() { return m_buffer; } - UserOrKernelBuffer const& buffer() const { return m_buffer; } - size_t buffer_size() const { return m_buffer_size; } - - virtual void start() override; - virtual StringView name() const override - { - switch (m_request_type) { - case Read: - return "BlockDeviceRequest (read)"sv; - case Write: - return "BlockDeviceRequest (write)"sv; - default: - VERIFY_NOT_REACHED(); - } - } - -private: - BlockDevice& m_block_device; - RequestType const m_request_type; - u64 const m_block_index; - u32 const m_block_count; - UserOrKernelBuffer m_buffer; - size_t const m_buffer_size; -}; - -} diff --git a/Kernel/Devices/CharacterDevice.cpp b/Kernel/Devices/CharacterDevice.cpp deleted file mode 100644 index 3379139dfa0..00000000000 --- a/Kernel/Devices/CharacterDevice.cpp +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include - -namespace Kernel { - -CharacterDevice::~CharacterDevice() = default; - -void CharacterDevice::after_inserting_add_symlink_to_device_identifier_directory() -{ - VERIFY(m_symlink_sysfs_component); - SysFSCharacterDevicesDirectory::the().devices_list({}).with([&](auto& list) -> void { - list.append(*m_symlink_sysfs_component); - }); -} - -void CharacterDevice::before_will_be_destroyed_remove_symlink_from_device_identifier_directory() -{ - VERIFY(m_symlink_sysfs_component); - SysFSCharacterDevicesDirectory::the().devices_list({}).with([&](auto& list) -> void { - list.remove(*m_symlink_sysfs_component); - }); -} - -// FIXME: This method will be eventually removed after all nodes in /sys/dev/char/ are symlinks -void CharacterDevice::after_inserting_add_to_device_identifier_directory() -{ - VERIFY(m_sysfs_component); - SysFSCharacterDevicesDirectory::the().devices_list({}).with([&](auto& list) -> void { - list.append(*m_sysfs_component); - }); -} - -// FIXME: This method will be eventually removed after all nodes in /sys/dev/char/ are symlinks -void CharacterDevice::before_will_be_destroyed_remove_from_device_identifier_directory() -{ - VERIFY(m_sysfs_component); - SysFSCharacterDevicesDirectory::the().devices_list({}).with([&](auto& list) -> void { - list.remove(*m_sysfs_component); - }); -} - -} diff --git a/Kernel/Devices/CharacterDevice.h b/Kernel/Devices/CharacterDevice.h deleted file mode 100644 index 37bf085aecd..00000000000 --- a/Kernel/Devices/CharacterDevice.h +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include - -namespace Kernel { - -class CharacterDevice : public Device { -public: - virtual ~CharacterDevice() override; - -protected: - CharacterDevice(MajorNumber major, MinorNumber minor) - : Device(major, minor) - { - } - - virtual void after_inserting_add_symlink_to_device_identifier_directory() override final; - virtual void before_will_be_destroyed_remove_symlink_from_device_identifier_directory() override final; - -private: - virtual bool is_character_device() const final { return true; } - - // FIXME: These methods will be eventually removed after all nodes in /sys/dev/char/ are symlinks - virtual void after_inserting_add_to_device_identifier_directory() override final; - virtual void before_will_be_destroyed_remove_from_device_identifier_directory() override final; -}; - -} diff --git a/Kernel/Devices/Device.cpp b/Kernel/Devices/Device.cpp deleted file mode 100644 index cb013b5c8b0..00000000000 --- a/Kernel/Devices/Device.cpp +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -namespace Kernel { - -Device::Device(MajorNumber major, MinorNumber minor) - : m_major(major) - , m_minor(minor) -{ -} - -void Device::before_will_be_destroyed_remove_from_device_management() -{ - DeviceManagement::the().before_device_removal({}, *this); - m_state = State::BeingRemoved; -} - -void Device::after_inserting_add_to_device_management() -{ - DeviceManagement::the().after_inserting_device({}, *this); -} - -ErrorOr Device::after_inserting() -{ - VERIFY(!m_sysfs_component); - auto sys_fs_component = SysFSDeviceComponent::must_create(*this); - m_sysfs_component = sys_fs_component; - after_inserting_add_to_device_identifier_directory(); - after_inserting_add_to_device_management(); - return {}; -} - -void Device::will_be_destroyed() -{ - VERIFY(m_sysfs_component); - before_will_be_destroyed_remove_from_device_management(); - before_will_be_destroyed_remove_from_device_identifier_directory(); -} - -Device::~Device() -{ - VERIFY(m_state == State::BeingRemoved); -} - -ErrorOr> Device::pseudo_path(OpenFileDescription const&) const -{ - return KString::formatted("device:{},{}", major(), minor()); -} - -ErrorOr> Device::open(int options) -{ - TRY(Process::current().jail().with([&](auto const& my_jail) -> ErrorOr { - if (my_jail && !is_openable_by_jailed_processes()) - return Error::from_errno(EPERM); - return {}; - })); - return File::open(options); -} - -void Device::process_next_queued_request(Badge, AsyncDeviceRequest const& completed_request) -{ - SpinlockLocker lock(m_requests_lock); - VERIFY(!m_requests.is_empty()); - VERIFY(m_requests.first().ptr() == &completed_request); - m_requests.remove(m_requests.begin()); - if (!m_requests.is_empty()) { - auto* next_request = m_requests.first().ptr(); - next_request->do_start(move(lock)); - } - - evaluate_block_conditions(); -} - -} diff --git a/Kernel/Devices/Device.h b/Kernel/Devices/Device.h deleted file mode 100644 index 1c650163047..00000000000 --- a/Kernel/Devices/Device.h +++ /dev/null @@ -1,98 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -// Device is the base class of everything that lives in the /dev directory. -// -// To expose a Device to the filesystem, simply pass two unique numbers to the constructor, -// and then mknod a file in /dev with those numbers. -// -// There are two main subclasses: -// - BlockDevice (random access) -// - CharacterDevice (sequential) -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace Kernel { - -class Device : public File { -protected: - enum class State { - Normal, - BeingRemoved, - }; - -public: - virtual ~Device() override; - - MajorNumber major() const { return m_major; } - MinorNumber minor() const { return m_minor; } - - virtual ErrorOr> pseudo_path(OpenFileDescription const&) const override; - virtual ErrorOr> open(int options) override; - - virtual bool is_device() const override { return true; } - virtual void will_be_destroyed() override; - virtual ErrorOr after_inserting(); - virtual bool is_openable_by_jailed_processes() const { return false; } - void process_next_queued_request(Badge, AsyncDeviceRequest const&); - - template - ErrorOr> try_make_request(Args&&... args) - { - auto request = TRY(adopt_nonnull_lock_ref_or_enomem(new (nothrow) AsyncRequestType(*this, forward(args)...))); - SpinlockLocker lock(m_requests_lock); - bool was_empty = m_requests.is_empty(); - TRY(m_requests.try_append(request)); - if (was_empty) - request->do_start(move(lock)); - return request; - } - -protected: - Device(MajorNumber major, MinorNumber minor); - - void after_inserting_add_to_device_management(); - void before_will_be_destroyed_remove_from_device_management(); - - virtual void after_inserting_add_symlink_to_device_identifier_directory() = 0; - virtual void before_will_be_destroyed_remove_symlink_from_device_identifier_directory() = 0; - - // FIXME: These methods will be eventually removed after all nodes in /sys/dev/block/ and - // /sys/dev/char/ are symlinks. - virtual void after_inserting_add_to_device_identifier_directory() = 0; - virtual void before_will_be_destroyed_remove_from_device_identifier_directory() = 0; - -private: - MajorNumber const m_major { 0 }; - MinorNumber const m_minor { 0 }; - - State m_state { State::Normal }; - - Spinlock m_requests_lock {}; - DoublyLinkedList> m_requests; - -protected: - // FIXME: This pointer will be eventually removed after all nodes in /sys/dev/block/ and - // /sys/dev/char/ are symlinks. - RefPtr m_sysfs_component; - - RefPtr m_symlink_sysfs_component; - RefPtr m_sysfs_device_directory; -}; - -} diff --git a/Kernel/Devices/DeviceManagement.cpp b/Kernel/Devices/DeviceManagement.cpp deleted file mode 100644 index b40892f67a7..00000000000 --- a/Kernel/Devices/DeviceManagement.cpp +++ /dev/null @@ -1,137 +0,0 @@ -/* - * Copyright (c) 2021, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include - -namespace Kernel { - -static Singleton s_the; - -UNMAP_AFTER_INIT DeviceManagement::DeviceManagement() -{ -} -UNMAP_AFTER_INIT void DeviceManagement::initialize() -{ - s_the.ensure_instance(); -} - -UNMAP_AFTER_INIT void DeviceManagement::attach_console_device(ConsoleDevice const& device) -{ - m_console_device = device; -} - -UNMAP_AFTER_INIT void DeviceManagement::attach_null_device(NullDevice const& device) -{ - m_null_device = device; -} - -UNMAP_AFTER_INIT void DeviceManagement::attach_device_control_device(DeviceControlDevice const& device) -{ - m_device_control_device = device; -} - -DeviceManagement& DeviceManagement::the() -{ - return *s_the; -} - -RefPtr DeviceManagement::get_device(MajorNumber major, MinorNumber minor) -{ - return m_devices.with([&](auto& map) -> RefPtr { - auto it = map.find(encoded_device(major.value(), minor.value())); - if (it == map.end()) - return nullptr; - return *it->value; - }); -} - -void DeviceManagement::before_device_removal(Badge, Device& device) -{ - u64 device_id = encoded_device(device.major(), device.minor()); - m_devices.with([&](auto& map) -> void { - VERIFY(map.contains(device_id)); - map.remove(encoded_device(device.major(), device.minor())); - }); - - m_event_queue.with([&](auto& queue) { - DeviceEvent event { DeviceEvent::State::Removed, device.is_block_device(), device.major().value(), device.minor().value() }; - queue.enqueue(event); - }); - - if (m_device_control_device) - m_device_control_device->evaluate_block_conditions(); -} - -SpinlockProtected, LockRank::None>& DeviceManagement::event_queue(Badge) -{ - return m_event_queue; -} - -void DeviceManagement::after_inserting_device(Badge, Device& device) -{ - u64 device_id = encoded_device(device.major(), device.minor()); - m_devices.with([&](auto& map) -> void { - if (map.contains(device_id)) { - dbgln("Already registered {},{}: {}", device.major(), device.minor(), device.class_name()); - VERIFY_NOT_REACHED(); - } - auto result = map.set(device_id, &device); - if (result != AK::HashSetResult::InsertedNewEntry) { - dbgln("Failed to register {},{}: {}", device.major(), device.minor(), device.class_name()); - VERIFY_NOT_REACHED(); - } - }); - - m_event_queue.with([&](auto& queue) { - DeviceEvent event { DeviceEvent::State::Inserted, device.is_block_device(), device.major().value(), device.minor().value() }; - queue.enqueue(event); - }); - - if (m_device_control_device) - m_device_control_device->evaluate_block_conditions(); -} - -void DeviceManagement::for_each(Function callback) -{ - m_devices.with([&](auto& map) -> void { - for (auto& entry : map) - callback(*entry.value); - }); -} - -ErrorOr DeviceManagement::try_for_each(Function(Device&)> callback) -{ - return m_devices.with([&](auto& map) -> ErrorOr { - for (auto& entry : map) - TRY(callback(*entry.value)); - return {}; - }); -} - -NullDevice& DeviceManagement::null_device() -{ - return *m_null_device; -} - -NullDevice const& DeviceManagement::null_device() const -{ - return *m_null_device; -} - -ConsoleDevice const& DeviceManagement::console_device() const -{ - return *m_console_device; -} -ConsoleDevice& DeviceManagement::console_device() -{ - return *m_console_device; -} - -} diff --git a/Kernel/Devices/DeviceManagement.h b/Kernel/Devices/DeviceManagement.h deleted file mode 100644 index ccb5d518288..00000000000 --- a/Kernel/Devices/DeviceManagement.h +++ /dev/null @@ -1,79 +0,0 @@ -/* - * Copyright (c) 2021, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace Kernel { - -class DeviceManagement { - -public: - DeviceManagement(); - static void initialize(); - static DeviceManagement& the(); - void attach_null_device(NullDevice const&); - - void attach_device_control_device(DeviceControlDevice const&); - - bool is_console_device_attached() const { return !m_console_device.is_null(); } - void attach_console_device(ConsoleDevice const&); - - void after_inserting_device(Badge, Device&); - void before_device_removal(Badge, Device&); - - void for_each(Function); - ErrorOr try_for_each(Function(Device&)>); - RefPtr get_device(MajorNumber major, MinorNumber minor); - - NullDevice const& null_device() const; - NullDevice& null_device(); - - ConsoleDevice const& console_device() const; - ConsoleDevice& console_device(); - - template - static inline ErrorOr> try_create_device(Args&&... args) - requires(requires(Args... args) { DeviceType::try_create(args...); }) - { - auto device = TRY(DeviceType::try_create(forward(args)...)); - TRY(device->after_inserting()); - return device; - } - - template - static inline ErrorOr> try_create_device(Args&&... args) - { - auto device = TRY(adopt_nonnull_lock_ref_or_enomem(new (nothrow) DeviceType(forward(args)...))); - TRY(device->after_inserting()); - return device; - } - - SpinlockProtected, LockRank::None>& event_queue(Badge); - -private: - LockRefPtr m_null_device; - LockRefPtr m_console_device; - LockRefPtr m_device_control_device; - SpinlockProtected, LockRank::None> m_devices {}; - SpinlockProtected, LockRank::None> m_event_queue {}; -}; - -} diff --git a/Kernel/Devices/FUSEDevice.cpp b/Kernel/Devices/FUSEDevice.cpp deleted file mode 100644 index 615b2de674c..00000000000 --- a/Kernel/Devices/FUSEDevice.cpp +++ /dev/null @@ -1,186 +0,0 @@ -/* - * Copyright (c) 2024, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include - -namespace Kernel { - -UNMAP_AFTER_INIT NonnullLockRefPtr FUSEDevice::must_create() -{ - return MUST(DeviceManagement::try_create_device()); -} - -UNMAP_AFTER_INIT FUSEDevice::FUSEDevice() - : CharacterDevice(10, 229) -{ -} - -UNMAP_AFTER_INIT FUSEDevice::~FUSEDevice() = default; - -ErrorOr FUSEDevice::initialize_instance(OpenFileDescription const& description) -{ - return m_instances.with([&](auto& instances) -> ErrorOr { - VERIFY(!instances.contains(&description)); - TRY(instances.try_set(&description, {})); - return {}; - }); -} - -bool FUSEDevice::can_read(OpenFileDescription const& description, u64) const -{ - bool drop = m_closing_instances.with([&](auto& closing_instances) { - auto iterator = closing_instances.find(&description); - return iterator != closing_instances.end(); - }); - - if (drop) - return true; - - return m_instances.with([&](auto& instances) { - auto instance_iterator = instances.find(&description); - if (instance_iterator == instances.end()) { - VERIFY(instances.is_empty()); - return false; - } - - auto const& requests_for_instance = (*instance_iterator).value; - for (auto const& request : requests_for_instance.in_reverse()) { - if (request.buffer_ready) - return true; - } - return false; - }); -} - -bool FUSEDevice::can_write(OpenFileDescription const&, u64) const -{ - return true; -} - -ErrorOr FUSEDevice::read(OpenFileDescription& description, u64, UserOrKernelBuffer& buffer, size_t size) -{ - TRY(m_closing_instances.with([&](auto& closing_instances) -> ErrorOr { - bool removed = closing_instances.remove_first_matching([&](auto const* closing_description) { return closing_description == &description; }); - if (removed) - return Error::from_errno(ENODEV); - - return {}; - })); - - if (size < 0x21000) - return Error::from_errno(EIO); - - return m_instances.with([&](auto& instances) -> ErrorOr { - auto instance_iterator = instances.find(&description); - if (instance_iterator == instances.end()) - return Error::from_errno(ENODEV); - - auto& requests_for_instance = (*instance_iterator).value; - - for (auto& request : requests_for_instance.in_reverse()) { - if (!request.buffer_ready) - continue; - - TRY(buffer.write(request.pending_request->bytes())); - request.buffer_ready = false; - return request.pending_request->size(); - } - - return Error::from_errno(ENOENT); - }); -} - -ErrorOr FUSEDevice::write(OpenFileDescription& description, u64, UserOrKernelBuffer const& buffer, size_t size) -{ - return m_instances.with([&](auto& instances) -> ErrorOr { - auto instance_iterator = instances.find(&description); - - if (instance_iterator == instances.end()) - return Error::from_errno(ENODEV); - - auto& requests_for_instance = (*instance_iterator).value; - auto& instance = requests_for_instance.last(); - - if (instance.expecting_header) { - memset(instance.response->data(), 0, instance.response->size()); - - fuse_out_header header; - TRY(buffer.read(&header, 0, sizeof(fuse_out_header))); - - dbgln_if(FUSE_DEBUG, "header: length: {}, error: {}, unique: {}", header.len, header.error, header.unique); - memcpy(instance.response->data(), &header, sizeof(fuse_out_header)); - - if (header.len > sizeof(fuse_out_header)) - instance.expecting_header = false; - else - instance.response_ready = true; - } else { - fuse_out_header* existing_header = bit_cast(instance.response->data()); - - instance.expecting_header = true; - if (existing_header->len > instance.response->size()) - return Error::from_errno(EINVAL); - - instance.response_ready = true; - u64 length = existing_header->len - sizeof(fuse_out_header); - dbgln_if(FUSE_DEBUG, "request: response length: {}", length); - TRY(buffer.read(instance.response->data() + sizeof(fuse_out_header), 0, length)); - } - - return size; - }); -} - -ErrorOr> FUSEDevice::send_request_and_wait_for_a_reply(OpenFileDescription const& description, Bytes bytes) -{ - return m_instances.with([&](auto& instances) -> ErrorOr> { - auto instance_iterator = instances.find(&description); - VERIFY(instance_iterator != instances.end()); - auto& requests_for_instance = (*instance_iterator).value; - - TRY(requests_for_instance.try_append({ - &description, - TRY(KBuffer::try_create_with_size("FUSE: Pending request buffer"sv, 0x21000)), - TRY(KBuffer::try_create_with_size("FUSE: Response buffer"sv, 0x21000)), - })); - - size_t instance_index = requests_for_instance.size() - 1; - auto& instance = requests_for_instance.last(); - VERIFY(bytes.size() <= 0x21000); - - memset(instance.pending_request->data(), 0, instance.pending_request->size()); - memcpy(instance.pending_request->data(), bytes.data(), bytes.size()); - instance.buffer_ready = true; - evaluate_block_conditions(); - - while (!instance.response_ready) - (void)Thread::current()->sleep(Duration::from_microseconds(100)); - - auto result = KBuffer::try_create_with_bytes("FUSEDevice: Response"sv, instance.response->bytes()); - requests_for_instance.remove(instance_index); - - return result; - }); -} - -void FUSEDevice::shutdown_for_description(OpenFileDescription const& description) -{ - m_instances.with([&](auto& instances) { - VERIFY(instances.remove(&description)); - }); - - m_closing_instances.with([&](auto& closing_instances) { - closing_instances.append(&description); - }); - - evaluate_block_conditions(); -} - -} diff --git a/Kernel/Devices/FUSEDevice.h b/Kernel/Devices/FUSEDevice.h deleted file mode 100644 index 3ad0e18ec23..00000000000 --- a/Kernel/Devices/FUSEDevice.h +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright (c) 2024, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include - -namespace Kernel { - -struct FUSEInstance { - OpenFileDescription const* fd = nullptr; - NonnullOwnPtr pending_request; - NonnullOwnPtr response; - bool buffer_ready = false; - bool response_ready = false; - bool expecting_header = true; -}; - -class FUSEDevice final : public CharacterDevice { - friend class DeviceManagement; - -public: - static NonnullLockRefPtr must_create(); - virtual ~FUSEDevice() override; - - ErrorOr initialize_instance(OpenFileDescription const&); - ErrorOr> send_request_and_wait_for_a_reply(OpenFileDescription const&, Bytes); - void shutdown_for_description(OpenFileDescription const&); - -private: - FUSEDevice(); - - // ^Device - virtual bool is_openable_by_jailed_processes() const override { return false; } - - // ^CharacterDevice - virtual ErrorOr read(OpenFileDescription&, u64, UserOrKernelBuffer&, size_t) override; - virtual ErrorOr write(OpenFileDescription&, u64, UserOrKernelBuffer const&, size_t) override; - virtual bool can_read(OpenFileDescription const&, u64) const override; - virtual bool can_write(OpenFileDescription const&, u64) const override; - virtual StringView class_name() const override { return "FUSEDevice"sv; } - - SpinlockProtected>, LockRank::None> m_instances; - SpinlockProtected, LockRank::None> m_closing_instances; -}; - -} diff --git a/Kernel/Devices/GPU/3dfx/Definitions.h b/Kernel/Devices/GPU/3dfx/Definitions.h deleted file mode 100644 index 8a0e2a76546..00000000000 --- a/Kernel/Devices/GPU/3dfx/Definitions.h +++ /dev/null @@ -1,209 +0,0 @@ -/* - * Copyright (c) 2023, Edwin Rijkee - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include - -namespace Kernel::VoodooGraphics { - -enum class VGAPort : u16 { - AttributeController = 0x3c0, - MiscOutputWrite = 0x3c2, - SequencerIndex = 0x3c4, - SequencerData = 0x3c5, - MiscOutputRead = 0x3cc, - GraphicsControllerIndex = 0x3ce, - GraphicsControllerData = 0x3cf, - CrtcIndex = 0x3d4, - CrtcData = 0x3d5, - InputStatus1 = 0x3da, -}; - -enum CRTCHorizontalBlankingEndFlags : u8 { - CompatibilityRead = 1 << 7 -}; - -enum CRTCVerticalSyncEndFlags : u8 { - EnableVertInt = 1 << 5, - CRTCRegsWriteProt = 1 << 7 -}; - -enum CRTCModeControlFlags : u8 { - ByteWordMode = 1 << 6, - TimingEnable = 1 << 7 -}; - -enum GraphicsControllerMiscellaneousFlags : u8 { - MemoryMapEGAVGAExtended = 1 << 2, -}; - -enum AttributeControllerModeFlags : u8 { - GraphicsMode = 1 << 0, - PixelWidth = 1 << 6, -}; - -enum SequencerResetFlags : u8 { - AsynchronousReset = 1 << 0, - SynchronousReset = 1 << 1, -}; - -enum SequencerClockingModeFlags : u8 { - DotClock8 = 1 << 0, -}; - -enum MiscellaneousOutputFlags : u8 { - CRTCAddressColor = 1 << 0, - ClockSelectPLL = 0b1100, - VerticalSyncPositive = 1 << 7, - HorizontalSyncPositive = 1 << 6, -}; - -enum DacModeFlags : u32 { - DacMode2x = 1 << 0, -}; - -enum VgaInit0Flags : u32 { - FIFODepth8Bit = 1 << 2, - EnableVgaExtensions = 1 << 6, - WakeUpSelect3C3 = 1 << 8, - EnableAltReadback = 1 << 10, - ExtendedShiftOut = 1 << 12, -}; - -enum VidProcCfgFlags : u32 { - VideoProcessorEnable = 1 << 0, - DesktopSurfaceEnable = 1 << 7, - DesktopCLUTBypass = 1 << 10, - DesktopPixelFormat32Bit = 0b11 << 18, - TwoXMode = 1 << 26, -}; - -struct PLLSettings { - static i32 const reference_frequency_in_khz = 14318; - i32 m = 0; - i32 n = 0; - i32 k = 0; - - int frequency_in_khz() const - { - return (reference_frequency_in_khz * (n + 2) / (m + 2)) >> k; - } - - u32 register_value() const - { - return (n << 8) | (m << 2) | k; - } -}; - -// CRT Controller Registers -struct CRRegisters { - u8 horizontal_total; // CR0 - u8 horizontal_display_enable_end; // CR1 - u8 horizontal_blanking_start; // CR2 - u8 horizontal_blanking_end; // CR3 - u8 horizontal_sync_start; // CR4 - u8 horizontal_sync_end; // CR5 - u8 vertical_total; // CR6 - u8 overflow; // CR7 - u8 reserved_0; // CR8 - u8 maximum_scan_line; // CR9 - u8 reserved_1[6]; - u8 vertical_sync_start; // CR10 - u8 vertical_sync_end; // CR11 - u8 vertical_display_enable_end; // CR12 - u8 reserved_2[2]; - u8 vertical_blanking_start; // CR15 - u8 vertical_blanking_end; // CR16 - u8 mode_control; // CR17 - u8 reserved_3[2]; - u8 horizontal_extensions; // CR1A - u8 vertical_extensions; // CR1B -}; - -// Graphics Controller Registers -struct GRRegisters { - u8 reserved_0[6]; - u8 graphics_controller_miscellaneous; // GR6 - u8 reserved_1[2]; -}; - -// Attribute Controller Registers -struct ARRegisters { - u8 reserved_0[15]; - u8 attribute_controller_mode; // AR10 - u8 reserved_1[5]; -}; - -// Sequencer Registers -struct SRRegisters { - u8 sequencer_reset; // SR0 - u8 sequencer_clocking_mode; // SR1 - u8 reserved[3]; -}; - -struct ModeRegisters { - u32 vid_screen_size = 0; - u32 vid_desktop_overlay_stride = 0; - u8 misc_out_reg = 0; - u32 vga_init0 = 0; - u32 vid_proc_cfg = 0; - u32 dac_mode = 0; - u32 pll_ctrl0 = 0; - - union { - Array cr_data = { 0 }; - CRRegisters cr; - }; - - union { - Array gr_data = { 0 }; - GRRegisters gr; - }; - - union { - Array ar_data = { 0 }; - ARRegisters ar; - }; - - union { - Array sr_data = { 0 }; - SRRegisters sr; - }; -}; - -static_assert(sizeof(ModeRegisters::cr_data) == sizeof(ModeRegisters::cr)); -static_assert(sizeof(ModeRegisters::gr_data) == sizeof(ModeRegisters::gr)); -static_assert(sizeof(ModeRegisters::ar_data) == sizeof(ModeRegisters::ar)); - -struct [[gnu::packed]] RegisterMap { - u32 status; - u32 reserved_0[9]; - u32 vga_init0; - u32 reserved_1[5]; - u32 pll_ctrl0; - u32 reserved_2[2]; - u32 dac_mode; - u32 reserved_3[3]; - - u32 vid_proc_cfg; - u32 reserved_4[14]; - u32 vid_screen_size; - u32 reserved_5[18]; - u32 vid_desktop_start_addr; - u32 vid_desktop_overlay_stride; -}; - -static_assert(__builtin_offsetof(RegisterMap, status) == 0); -static_assert(__builtin_offsetof(RegisterMap, vga_init0) == 0x28); -static_assert(__builtin_offsetof(RegisterMap, pll_ctrl0) == 0x40); -static_assert(__builtin_offsetof(RegisterMap, dac_mode) == 0x4c); -static_assert(__builtin_offsetof(RegisterMap, vid_proc_cfg) == 0x5c); -static_assert(__builtin_offsetof(RegisterMap, vid_screen_size) == 0x98); -static_assert(__builtin_offsetof(RegisterMap, vid_desktop_start_addr) == 0xe4); -static_assert(__builtin_offsetof(RegisterMap, vid_desktop_overlay_stride) == 0xe8); -} diff --git a/Kernel/Devices/GPU/3dfx/GraphicsAdapter.cpp b/Kernel/Devices/GPU/3dfx/GraphicsAdapter.cpp deleted file mode 100644 index 9d5d3ca9d44..00000000000 --- a/Kernel/Devices/GPU/3dfx/GraphicsAdapter.cpp +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Copyright (c) 2023, Edwin Rijkee - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include -#include - -namespace Kernel { - -static constexpr u16 supported_models[] { - // 0x0003, // Banshee (untested) - 0x0005, // Voodoo 3 - // 0x0009 // Voodoo 4 / Voodoo 5 (untested) -}; - -static bool is_supported_model(u16 device_id) -{ - for (auto& id : supported_models) { - if (id == device_id) - return true; - } - return false; -} - -UNMAP_AFTER_INIT ErrorOr VoodooGraphicsAdapter::probe(PCI::DeviceIdentifier const& pci_device_identifier) -{ - PCI::HardwareID id = pci_device_identifier.hardware_id(); - return id.vendor_id == PCI::VendorID::Tdfx && is_supported_model(id.device_id); -} - -UNMAP_AFTER_INIT ErrorOr> VoodooGraphicsAdapter::create(PCI::DeviceIdentifier const& pci_device_identifier) -{ - auto adapter = TRY(adopt_nonnull_lock_ref_or_enomem(new (nothrow) VoodooGraphicsAdapter(pci_device_identifier))); - MUST(adapter->initialize_adapter(pci_device_identifier)); - return adapter; -} - -UNMAP_AFTER_INIT VoodooGraphicsAdapter::VoodooGraphicsAdapter(PCI::DeviceIdentifier const& device_identifier) - : PCI::Device(const_cast(device_identifier)) -{ -} - -UNMAP_AFTER_INIT ErrorOr VoodooGraphicsAdapter::initialize_adapter(PCI::DeviceIdentifier const& pci_device_identifier) -{ - PCI::enable_io_space(device_identifier()); - PCI::enable_memory_space(device_identifier()); - - auto mmio_mapping = TRY(PCI::map_bar(pci_device_identifier, PCI::HeaderType0BaseRegister::BAR0)); - dbgln_if(TDFX_DEBUG, "3dfx mmio addr {} size {}", mmio_mapping.paddr, mmio_mapping.length); - - auto vmem_addr = TRY(PCI::get_bar_address(pci_device_identifier, PCI::HeaderType0BaseRegister::BAR1)); - auto vmem_size = PCI::get_BAR_space_size(pci_device_identifier, PCI::HeaderType0BaseRegister::BAR1); - dbgln_if(TDFX_DEBUG, "3dfx vmem addr {} size {}", vmem_addr, vmem_size); - - auto io_window = TRY(IOWindow::create_for_pci_device_bar(pci_device_identifier, PCI::HeaderType0BaseRegister::BAR2)); - - m_display_connector = VoodooGraphics::VoodooDisplayConnector::must_create(vmem_addr, vmem_size, move(mmio_mapping), move(io_window)); - TRY(m_display_connector->set_safe_mode_setting()); - - return {}; -} - -} diff --git a/Kernel/Devices/GPU/3dfx/GraphicsAdapter.h b/Kernel/Devices/GPU/3dfx/GraphicsAdapter.h deleted file mode 100644 index d6d319574eb..00000000000 --- a/Kernel/Devices/GPU/3dfx/GraphicsAdapter.h +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright (c) 2023, Edwin Rijkee - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include - -namespace Kernel { - -class VoodooGraphicsAdapter final : public GPUDevice - , public PCI::Device { - -public: - static ErrorOr probe(PCI::DeviceIdentifier const&); - static ErrorOr> create(PCI::DeviceIdentifier const&); - virtual ~VoodooGraphicsAdapter() = default; - virtual StringView device_name() const override { return "VoodooGraphicsAdapter"sv; } - -private: - ErrorOr initialize_adapter(PCI::DeviceIdentifier const&); - - explicit VoodooGraphicsAdapter(PCI::DeviceIdentifier const&); - - LockRefPtr m_display_connector; -}; -} diff --git a/Kernel/Devices/GPU/3dfx/VoodooDisplayConnector.cpp b/Kernel/Devices/GPU/3dfx/VoodooDisplayConnector.cpp deleted file mode 100644 index 72f51bb1979..00000000000 --- a/Kernel/Devices/GPU/3dfx/VoodooDisplayConnector.cpp +++ /dev/null @@ -1,416 +0,0 @@ -/* - * Copyright (c) 2023, Edwin Rijkee - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include - -namespace Kernel::VoodooGraphics { - -NonnullLockRefPtr VoodooDisplayConnector::must_create(PhysicalAddress framebuffer_address, size_t framebuffer_resource_size, Memory::TypedMapping registers_mapping, NonnullOwnPtr io_window) -{ - auto device_or_error = DeviceManagement::try_create_device(framebuffer_address, framebuffer_resource_size, move(registers_mapping), move(io_window)); - VERIFY(!device_or_error.is_error()); - auto connector = device_or_error.release_value(); - MUST(connector->create_attached_framebuffer_console()); - MUST(connector->fetch_and_initialize_edid()); - return connector; -} - -ErrorOr VoodooDisplayConnector::fetch_and_initialize_edid() -{ - // TODO: actually fetch the EDID. - return initialize_edid_for_generic_monitor({}); -} - -ErrorOr VoodooDisplayConnector::create_attached_framebuffer_console() -{ - m_framebuffer_console = Graphics::ContiguousFramebufferConsole::initialize(m_framebuffer_address.value(), 1024, 768, 1024 * sizeof(u32)); - GraphicsManagement::the().set_console(*m_framebuffer_console); - return {}; -} - -VoodooDisplayConnector::VoodooDisplayConnector(PhysicalAddress framebuffer_address, size_t framebuffer_resource_size, Memory::TypedMapping registers_mapping, NonnullOwnPtr io_window) - : DisplayConnector(framebuffer_address, framebuffer_resource_size, true) - , m_registers(move(registers_mapping)) - , m_io_window(move(io_window)) -{ -} - -ErrorOr VoodooDisplayConnector::for_each_dmt_timing_in_edid(Function callback) const -{ - IterationDecision iteration_decision = TRY(m_edid_parser->for_each_standard_timing([&](auto& standard_timing) { - auto timing = EDID::DMT::find_timing_by_dmt_id(standard_timing.dmt_id()); - if (!timing) { - return IterationDecision::Continue; - } - - return callback(*timing); - })); - - if (iteration_decision == IterationDecision::Break) { - return iteration_decision; - } - - iteration_decision = TRY(m_edid_parser->for_each_established_timing([&](auto& established_timing) { - auto timing = EDID::DMT::find_timing_by_dmt_id(established_timing.dmt_id()); - if (!timing) { - return IterationDecision::Continue; - } - - return callback(*timing); - })); - - return iteration_decision; -} - -auto VoodooDisplayConnector::find_suitable_mode(ModeSetting const& requested_mode) const -> ErrorOr -{ - u32 width = requested_mode.horizontal_active; - u32 height = requested_mode.vertical_active; - ModeSetting result = requested_mode; - - if (requested_mode.horizontal_stride == 0) { - result.horizontal_stride = sizeof(u32) * width; - } - - if (requested_mode.pixel_clock_in_khz != 0) { - dbgln_if(TDFX_DEBUG, "3dfx: Requested mode {}x{} includes timing information", width, height); - return result; - } - - dbgln_if(TDFX_DEBUG, "3dfx: Looking for suitable mode with resolution {}x{}", width, height); - - IterationDecision iteration_decision = TRY(m_edid_parser->for_each_detailed_timing([&](auto& timing, unsigned) { - dbgln_if(TDFX_DEBUG, "3dfx: Considering detailed timing {}x{} @ {}", timing.horizontal_addressable_pixels(), timing.vertical_addressable_lines(), timing.refresh_rate()); - - if (timing.is_interlaced() || timing.horizontal_addressable_pixels() != width || timing.vertical_addressable_lines() != height) { - return IterationDecision::Continue; - } - - result.pixel_clock_in_khz = timing.pixel_clock_khz(); - result.horizontal_front_porch_pixels = timing.horizontal_front_porch_pixels(); - result.horizontal_sync_time_pixels = timing.horizontal_sync_pulse_width_pixels(); - result.horizontal_blank_pixels = timing.horizontal_blanking_pixels(); - result.vertical_front_porch_lines = timing.vertical_front_porch_lines(); - result.vertical_sync_time_lines = timing.vertical_sync_pulse_width_lines(); - result.vertical_blank_lines = timing.vertical_blanking_lines(); - return IterationDecision::Break; - })); - - if (iteration_decision == IterationDecision::Break) { - return result; - } - - iteration_decision = TRY(for_each_dmt_timing_in_edid([&](auto& timing) { - dbgln_if(TDFX_DEBUG, "3dfx: Considering DMT timing {}x{} @ {}", timing.horizontal_pixels, timing.vertical_lines, timing.vertical_frequency_hz()); - - if (timing.scan_type != EDID::DMT::MonitorTiming::ScanType::NonInterlaced || timing.horizontal_pixels != width || timing.vertical_lines != height) { - return IterationDecision::Continue; - } - - result.pixel_clock_in_khz = timing.pixel_clock_khz; - result.horizontal_front_porch_pixels = timing.horizontal_front_porch_pixels; - result.horizontal_sync_time_pixels = timing.horizontal_sync_time_pixels; - result.horizontal_blank_pixels = timing.horizontal_blank_pixels; - result.vertical_front_porch_lines = timing.vertical_front_porch_lines; - result.vertical_sync_time_lines = timing.vertical_sync_time_lines; - result.vertical_blank_lines = timing.vertical_blank_lines; - return IterationDecision::Break; - })); - - if (iteration_decision == IterationDecision::Break) { - return result; - } - - dbgln_if(TDFX_DEBUG, "3dfx: No timing information available for display mode {}x{}", width, height); - return EINVAL; -} - -ErrorOr -VoodooDisplayConnector::set_mode_setting(ModeSetting const& requested_mode_setting) -{ - SpinlockLocker locker(m_modeset_lock); - VERIFY(m_framebuffer_console); - - ModeSetting mode_setting = TRY(find_suitable_mode(requested_mode_setting)); - dbgln_if(TDFX_DEBUG, "VoodooDisplayConnector resolution registers set to - {}x{}", mode_setting.horizontal_active, mode_setting.vertical_active); - - auto regs = TRY(prepare_mode_switch(mode_setting)); - TRY(perform_mode_switch(regs)); - - m_framebuffer_console->set_resolution(mode_setting.horizontal_active, mode_setting.vertical_active, mode_setting.horizontal_stride); - m_current_mode_setting = mode_setting; - return {}; -} - -ErrorOr VoodooDisplayConnector::set_y_offset(size_t) -{ - return ENOTIMPL; -} - -ErrorOr VoodooDisplayConnector::set_safe_mode_setting() -{ - DisplayConnector::ModeSetting safe_mode_set { - .horizontal_stride = 1024 * sizeof(u32), - .pixel_clock_in_khz = 65000, - .horizontal_active = 1024, - .horizontal_front_porch_pixels = 24, - .horizontal_sync_time_pixels = 136, - .horizontal_blank_pixels = 320, - .vertical_active = 768, - .vertical_front_porch_lines = 3, - .vertical_sync_time_lines = 6, - .vertical_blank_lines = 38, - .horizontal_offset = 0, - .vertical_offset = 0, - }; - return set_mode_setting(safe_mode_set); -} - -ErrorOr VoodooDisplayConnector::unblank() -{ - return ENOTIMPL; -} - -ErrorOr VoodooDisplayConnector::flush_first_surface() -{ - return ENOTSUP; -} - -void VoodooDisplayConnector::enable_console() -{ - VERIFY(m_control_lock.is_locked()); - VERIFY(m_framebuffer_console); - m_framebuffer_console->enable(); -} - -void VoodooDisplayConnector::disable_console() -{ - VERIFY(m_control_lock.is_locked()); - VERIFY(m_framebuffer_console); - m_framebuffer_console->disable(); -} - -u8 VoodooDisplayConnector::read_vga(VGAPort port) -{ - return m_io_window->read8(static_cast(port) - 0x300); -} - -u8 VoodooDisplayConnector::read_vga_indexed(VGAPort index_port, VGAPort data_port, u8 index) -{ - m_io_window->write8(static_cast(index_port) - 0x300, index); - return m_io_window->read8(static_cast(data_port) - 0x300); -} - -void VoodooDisplayConnector::write_vga(VGAPort port, u8 value) -{ - m_io_window->write8(static_cast(port) - 0x300, value - 0x300); -} - -void VoodooDisplayConnector::write_vga_indexed(VGAPort index_port, VGAPort data_port, u8 index, u8 value) -{ - m_io_window->write8(static_cast(index_port) - 0x300, index); - m_io_window->write8(static_cast(data_port) - 0x300, value); -} - -ErrorOr VoodooDisplayConnector::wait_for_fifo_space(u32 entries) -{ - VERIFY(entries < 32); - - auto deadline = TimeManagement::the().monotonic_time() + Duration::from_seconds(1); - do { - if ((m_registers->status & 0x1f) >= entries) { - return {}; - } - (void)Thread::current()->sleep(Duration::from_milliseconds(1)); - } while (TimeManagement::the().monotonic_time() < deadline); - - dbgln_if(TDFX_DEBUG, "3dfx: timed out waiting for fifo space"); - return EIO; -} - -PLLSettings VoodooDisplayConnector::calculate_pll(i32 desired_frequency_in_khz) -{ - VoodooGraphics::PLLSettings current; - VoodooGraphics::PLLSettings best; - i32 best_difference; - - best_difference = desired_frequency_in_khz; - - for (current.m = 0; current.m < 64; current.m++) { - for (current.n = 0; current.n < 256; current.n++) { - for (current.k = 0; current.k < 4; current.k++) { - auto frequency_in_khz = current.frequency_in_khz(); - - auto error = AK::abs(frequency_in_khz - desired_frequency_in_khz); - if (error < best_difference) { - best_difference = error; - best = current; - } - } - } - } - - return best; -} - -ErrorOr VoodooDisplayConnector::prepare_mode_switch(ModeSetting const& mode_setting) -{ - u32 width = mode_setting.horizontal_active; - u32 height = mode_setting.vertical_active; - - ModeRegisters regs; - - regs.vga_init0 = EnableVgaExtensions | WakeUpSelect3C3 | EnableAltReadback | FIFODepth8Bit | ExtendedShiftOut; - regs.vid_proc_cfg |= VideoProcessorEnable | DesktopSurfaceEnable | DesktopPixelFormat32Bit | DesktopCLUTBypass; - - // We only want to touch the 2X flag of the DAC Mode register, the other flags are preserved - regs.dac_mode = m_registers->dac_mode & ~DacMode2x; - - u32 const max_pixel_clock_in_khz = 270000; - if (mode_setting.pixel_clock_in_khz > max_pixel_clock_in_khz) { - return ENOTSUP; - } - - u32 horizontal_divisor = 8; - if (mode_setting.pixel_clock_in_khz > max_pixel_clock_in_khz / 2) { - horizontal_divisor = 16; - regs.dac_mode |= DacMode2x; - regs.vid_proc_cfg |= TwoXMode; - } - - dbgln_if(TDFX_DEBUG, "3dfx: Calculating best PLL settings for pixel clock {} KHz", mode_setting.pixel_clock_in_khz); - auto pll = calculate_pll(mode_setting.pixel_clock_in_khz); - dbgln_if(TDFX_DEBUG, "3dfx: Best matching PLL settings: m={}, n={}, k={}. Frequency: {} KHz", pll.m, pll.n, pll.k, pll.frequency_in_khz()); - regs.pll_ctrl0 = pll.register_value(); - regs.vid_screen_size = width | (height << 12); - regs.vid_desktop_overlay_stride = mode_setting.horizontal_stride; - regs.misc_out_reg = ClockSelectPLL | CRTCAddressColor; - if (height < 768) { - regs.misc_out_reg |= VerticalSyncPositive | HorizontalSyncPositive; - } - - u32 hor_total = mode_setting.horizontal_total() / horizontal_divisor - 5; - u32 hor_disp_en_end = width / horizontal_divisor - 1; - u32 hor_sync_start = mode_setting.horizontal_sync_start() / horizontal_divisor; - u32 hor_sync_end = (mode_setting.horizontal_sync_end() / horizontal_divisor) & 0x1f; - u32 hor_blank_start = hor_disp_en_end; - u32 hor_blank_end = hor_total & 0x7f; - - u32 vert_total = mode_setting.vertical_total() - 2; - u32 vert_disp_en_end = height - 1; - u32 vert_sync_start = mode_setting.vertical_sync_start(); - u32 vert_sync_end = mode_setting.vertical_sync_end() & 0xf; - u32 vert_blank_start = mode_setting.vertical_blanking_start() - 1; - u32 vert_blank_end = (mode_setting.vertical_total() - 1) & 0xff; - - if (hor_total > 0x1ff || // 9-bit field - hor_disp_en_end > 0x1ff || // 9-bit field - hor_sync_start > 0x1ff || // 9-bit field - hor_sync_end > 0x1f || // 5-bit field - hor_blank_start > 0x1ff || // 9-bit field - hor_blank_end > 0x7f || // 7-bit field - vert_total > 0x7ff || // 11-bit field - vert_disp_en_end > 0x7ff || // 11-bit field - vert_sync_start > 0x7ff || // 11-bit field - vert_sync_end > 0x0f || // 4-bit field - vert_blank_start > 0x7ff || // 11-bit field - vert_blank_end > 0xff // 8-bit field - ) { - dbgln_if(TDFX_DEBUG, "3dfx: One of the timing values is too large to fit in its register"); - return EOVERFLOW; - } - - // CRT Controller Registers - regs.cr.horizontal_total = hor_total; // bit 0-7 of hor_total - regs.cr.horizontal_display_enable_end = hor_disp_en_end; // bit 0-7 of hor_disp_en_end - regs.cr.horizontal_blanking_start = hor_blank_start; // bit 0-7 of hor_blank_start - regs.cr.horizontal_blanking_end = (hor_blank_end & 0x1f) // bit 0-4 of hor_blank_end - | CompatibilityRead; - regs.cr.horizontal_sync_start = hor_sync_start; // bit 0-7 of hor_sync_start - regs.cr.horizontal_sync_end = ((hor_sync_end & 0x1f) // bit 0-4 of hor_sync_end - | ((hor_blank_end & 0x20) << 2)); // bit 5 of hor_blank_end - regs.cr.vertical_total = vert_total; // bit 0-7 of vert_total - regs.cr.overflow = (((vert_total & 0x100) >> 8) // bit 8 of vert_total - | ((vert_disp_en_end & 0x100) >> 7) // bit 8 of vert_disp_en_end - | ((vert_sync_start & 0x100) >> 6) // bit 8 of vert_sync_start - | ((vert_blank_start & 0x100) >> 5) // bit 8 of vert_blank_start - | ((vert_total & 0x200) >> 4) // bit 9 of vert_disp_en_end - | ((vert_disp_en_end & 0x200) >> 3) // bit 9 of vert_disp_en_end - | ((vert_sync_start & 0x200) >> 2)); // bit 9 of vert_sync_start - regs.cr.maximum_scan_line = ((vert_blank_start & 0x200) >> 4); // bit 9 of vert_blank_start - regs.cr.vertical_sync_start = vert_sync_start; // bit 0-7 of vert_sync_start - regs.cr.vertical_sync_end = (vert_sync_end & 0x0f) // bit 0-3 of vert_sync_end - | EnableVertInt; - regs.cr.vertical_display_enable_end = vert_disp_en_end; // bit 0-7 of vert_disp_en_end - regs.cr.vertical_blanking_start = vert_blank_start; // bit 0-7 of vert_blank_start - regs.cr.vertical_blanking_end = vert_blank_end; // bit 0-7 of vert_blank_end - regs.cr.mode_control = TimingEnable | ByteWordMode; - regs.cr.horizontal_extensions = (hor_total & 0x100) >> 8 // bit 8 of hor_total - | (hor_disp_en_end & 0x100) >> 6 // bit 8 of hor_disp_en_end - | (hor_blank_start & 0x100) >> 4 // bit 8 of hor_blank_start - | (hor_blank_end & 0x40) >> 1 // bit 6 of hor_blank_end - | (hor_sync_start & 0x100) >> 2 // bit 8 of hor_sync_start - | (hor_sync_end & 0x20) << 2; // bit 5 of hor_sync_end - regs.cr.vertical_extensions = (vert_total & 0x400) >> 10 // bit 10 of vert_total - | (vert_disp_en_end & 0x400) >> 8 // bit 10 of vert_disp_en_endx - | (vert_blank_start & 0x400) >> 6 // bit 10 of vert_blank_start - | (vert_blank_end & 0x400) >> 4 // bit 10 of vert_blank_end - | (vert_sync_start & 0x400) >> 4; // bit 10 of vert_sync_start - // Graphics Controller Registers - regs.gr.graphics_controller_miscellaneous = MemoryMapEGAVGAExtended; - - // Attribute Controller Registers - regs.ar.attribute_controller_mode = GraphicsMode | PixelWidth; - - // Sequencer Registers - regs.sr.sequencer_reset = AsynchronousReset | SynchronousReset; - regs.sr.sequencer_clocking_mode = DotClock8; - - return regs; -} - -ErrorOr VoodooDisplayConnector::perform_mode_switch(ModeRegisters const& regs) -{ - TRY(wait_for_fifo_space(2)); - m_registers->vid_proc_cfg = 0; - m_registers->pll_ctrl0 = regs.pll_ctrl0; - - write_vga(VGAPort::MiscOutputWrite, regs.misc_out_reg); - for (u8 i = 0; i < regs.sr_data.size(); i++) { - write_vga_indexed(VGAPort::SequencerIndex, VGAPort::SequencerData, i, regs.sr_data[i]); - } - - // first unprotect CR0 - CR7 - write_vga_indexed(VGAPort::CrtcIndex, VGAPort::CrtcData, 0x11, read_vga_indexed(VGAPort::CrtcIndex, VGAPort::CrtcData, 0x11) & ~CRTCRegsWriteProt); - for (u8 i = 0; i < regs.cr_data.size(); i++) { - write_vga_indexed(VGAPort::CrtcIndex, VGAPort::CrtcData, i, regs.cr_data[i]); - } - - for (u8 i = 0; i < regs.gr_data.size(); i++) { - write_vga_indexed(VGAPort::GraphicsControllerIndex, VGAPort::GraphicsControllerData, i, regs.gr_data[i]); - } - - // The AttributeController IO port flips between the index and the data register on every write. - // Reading InputStatus1 has the side effect of resetting this, this way we know it is in the state we expect - read_vga(VGAPort::InputStatus1); - for (u8 i = 0; i < regs.ar_data.size(); i++) { - write_vga_indexed(VGAPort::AttributeController, VGAPort::AttributeController, i, regs.ar_data[i]); - } - - TRY(wait_for_fifo_space(6)); - m_registers->vga_init0 = regs.vga_init0; - m_registers->dac_mode = regs.dac_mode; - m_registers->vid_desktop_overlay_stride = regs.vid_desktop_overlay_stride; - m_registers->vid_screen_size = regs.vid_screen_size; - m_registers->vid_desktop_start_addr = 0; - m_registers->vid_proc_cfg = regs.vid_proc_cfg; - - return {}; -} -} diff --git a/Kernel/Devices/GPU/3dfx/VoodooDisplayConnector.h b/Kernel/Devices/GPU/3dfx/VoodooDisplayConnector.h deleted file mode 100644 index 9c13bf21101..00000000000 --- a/Kernel/Devices/GPU/3dfx/VoodooDisplayConnector.h +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright (c) 2023, Edwin Rijkee - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include -#include - -namespace Kernel::VoodooGraphics { - -class VoodooDisplayConnector final - : public DisplayConnector { - friend class VoodooGraphicsAdapter; - friend class Kernel::DeviceManagement; - -public: - static NonnullLockRefPtr must_create(PhysicalAddress framebuffer_address, size_t framebuffer_resource_size, Memory::TypedMapping, NonnullOwnPtr io_window); - -private: - ErrorOr fetch_and_initialize_edid(); - ErrorOr create_attached_framebuffer_console(); - VoodooDisplayConnector(PhysicalAddress framebuffer_address, size_t framebuffer_resource_size, Memory::TypedMapping, NonnullOwnPtr io_window); - - virtual bool mutable_mode_setting_capable() const override final { return false; } - virtual bool double_framebuffering_capable() const override { return false; } - virtual ErrorOr set_mode_setting(ModeSetting const&) override; - virtual ErrorOr set_y_offset(size_t y) override; - virtual ErrorOr set_safe_mode_setting() override final; - virtual ErrorOr unblank() override; - virtual bool partial_flush_support() const override final { return false; } - virtual bool flush_support() const override final { return false; } - virtual bool refresh_rate_support() const override final { return false; } - virtual ErrorOr flush_first_surface() override final; - virtual void enable_console() override final; - virtual void disable_console() override final; - - ErrorOr for_each_dmt_timing_in_edid(Function) const; - ErrorOr find_suitable_mode(ModeSetting const& requested_mode) const; - u8 read_vga(VGAPort port); - u8 read_vga_indexed(VGAPort index_port, VGAPort data_port, u8 index); - void write_vga(VGAPort port, u8 value); - void write_vga_indexed(VGAPort index_port, VGAPort data_port, u8 index, u8 value); - ErrorOr wait_for_fifo_space(u32 minimum_entries); - static PLLSettings calculate_pll(i32 desired_frequency_in_khz); - ErrorOr prepare_mode_switch(ModeSetting const& mode_setting); - ErrorOr perform_mode_switch(ModeRegisters const& regs); - - LockRefPtr m_framebuffer_console; - Memory::TypedMapping m_registers; - NonnullOwnPtr m_io_window; -}; -} diff --git a/Kernel/Devices/GPU/Bochs/Definitions.h b/Kernel/Devices/GPU/Bochs/Definitions.h deleted file mode 100644 index fb41d31cb61..00000000000 --- a/Kernel/Devices/GPU/Bochs/Definitions.h +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Copyright (c) 2022, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include - -namespace Kernel { - -#define VBE_DISPI_IOPORT_INDEX 0x01CE -#define VBE_DISPI_IOPORT_DATA 0x01CF - -#define BOCHS_DISPLAY_LITTLE_ENDIAN 0x1e1e1e1e -#define BOCHS_DISPLAY_BIG_ENDIAN 0xbebebebe - -#define VBE_DISPI_ID5 0xB0C5 - -enum class BochsFramebufferSettings { - Enabled = 0x1, - LinearFramebuffer = 0x40, -}; - -enum class BochsDISPIRegisters { - ID = 0x0, - XRES = 0x1, - YRES = 0x2, - BPP = 0x3, - ENABLE = 0x4, - BANK = 0x5, - VIRT_WIDTH = 0x6, - VIRT_HEIGHT = 0x7, - X_OFFSET = 0x8, - Y_OFFSET = 0x9, - VIDEO_RAM_64K_CHUNKS_COUNT = 0xA, -}; - -struct DISPIInterface { - u16 index_id; - u16 xres; - u16 yres; - u16 bpp; - u16 enable; - u16 bank; - u16 virt_width; - u16 virt_height; - u16 x_offset; - u16 y_offset; - u16 vram_64k_chunks_count; -}; -static_assert(AssertSize()); - -struct ExtensionRegisters { - u32 region_size; - u32 framebuffer_byteorder; -}; -static_assert(AssertSize()); - -struct BochsDisplayMMIORegisters { - u8 edid_data[0x400]; - u16 vga_ioports[0x10]; - u8 reserved[0xE0]; - DISPIInterface bochs_regs; - u8 reserved2[0x100 - sizeof(DISPIInterface)]; - ExtensionRegisters extension_regs; -}; -static_assert(AssertSize()); - -} diff --git a/Kernel/Devices/GPU/Bochs/GraphicsAdapter.cpp b/Kernel/Devices/GPU/Bochs/GraphicsAdapter.cpp deleted file mode 100644 index ab3fb118a3a..00000000000 --- a/Kernel/Devices/GPU/Bochs/GraphicsAdapter.cpp +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Copyright (c) 2021, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#if ARCH(X86_64) -# include -#endif -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace Kernel { - -UNMAP_AFTER_INIT ErrorOr BochsGraphicsAdapter::probe(PCI::DeviceIdentifier const& pci_device_identifier) -{ - PCI::HardwareID id = pci_device_identifier.hardware_id(); - if (id.vendor_id == PCI::VendorID::QEMUOld && id.device_id == 0x1111) - return true; - if (id.vendor_id == PCI::VendorID::VirtualBox && id.device_id == 0xbeef) - return true; - return false; -} - -UNMAP_AFTER_INIT ErrorOr> BochsGraphicsAdapter::create(PCI::DeviceIdentifier const& pci_device_identifier) -{ - auto adapter = TRY(adopt_nonnull_lock_ref_or_enomem(new (nothrow) BochsGraphicsAdapter(pci_device_identifier))); - MUST(adapter->initialize_adapter(pci_device_identifier)); - return adapter; -} - -UNMAP_AFTER_INIT BochsGraphicsAdapter::BochsGraphicsAdapter(PCI::DeviceIdentifier const& device_identifier) - : PCI::Device(const_cast(device_identifier)) -{ -} - -UNMAP_AFTER_INIT ErrorOr BochsGraphicsAdapter::initialize_adapter(PCI::DeviceIdentifier const& pci_device_identifier) -{ - // Note: If we use VirtualBox graphics adapter (which is based on Bochs one), we need to use IO ports - // Note: Bochs (the real bochs graphics adapter in the Bochs emulator) uses revision ID of 0x0 - // and doesn't support memory-mapped IO registers. - - // Note: In non x86-builds, we should never encounter VirtualBox hardware nor Pure Bochs VBE graphics, - // so just assume we can use the QEMU BochsVBE-compatible graphics adapter only. - auto bar0_space_size = PCI::get_BAR_space_size(pci_device_identifier, PCI::HeaderType0BaseRegister::BAR0); -#if ARCH(X86_64) - bool virtual_box_hardware = (pci_device_identifier.hardware_id().vendor_id == 0x80ee && pci_device_identifier.hardware_id().device_id == 0xbeef); - if (pci_device_identifier.revision_id().value() == 0x0 || virtual_box_hardware) { - m_display_connector = BochsDisplayConnector::must_create(PhysicalAddress(TRY(PCI::get_bar_address(pci_device_identifier, PCI::HeaderType0BaseRegister::BAR0))), bar0_space_size, virtual_box_hardware); - } else { - auto registers_mapping = TRY(PCI::map_bar(pci_device_identifier, PCI::HeaderType0BaseRegister::BAR2)); - VERIFY(registers_mapping.region); - m_display_connector = QEMUDisplayConnector::must_create(PhysicalAddress(TRY(PCI::get_bar_address(pci_device_identifier, PCI::HeaderType0BaseRegister::BAR0))), bar0_space_size, move(registers_mapping)); - } -#else - auto registers_mapping = TRY(PCI::map_bar(pci_device_identifier, PCI::HeaderType0BaseRegister::BAR2)); - VERIFY(registers_mapping.region); - m_display_connector = QEMUDisplayConnector::must_create(PhysicalAddress(TRY(PCI::get_bar_address(pci_device_identifier, PCI::HeaderType0BaseRegister::BAR0))), bar0_space_size, move(registers_mapping)); -#endif - - // Note: According to Gerd Hoffmann - "The linux driver simply does - // the unblank unconditionally. With bochs-display this is not needed but - // it also has no bad side effect". - // FIXME: If the error is ENOTIMPL, ignore it for now until we implement - // unblank support for VBoxDisplayConnector class too. - auto result = m_display_connector->unblank(); - if (result.is_error() && result.error().code() != ENOTIMPL) - return result; - - TRY(m_display_connector->set_safe_mode_setting()); - - return {}; -} - -} diff --git a/Kernel/Devices/GPU/Bochs/GraphicsAdapter.h b/Kernel/Devices/GPU/Bochs/GraphicsAdapter.h deleted file mode 100644 index 4929bb0489e..00000000000 --- a/Kernel/Devices/GPU/Bochs/GraphicsAdapter.h +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright (c) 2021, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include -#include -#include - -namespace Kernel { - -class GraphicsManagement; -struct BochsDisplayMMIORegisters; - -class BochsGraphicsAdapter final : public GPUDevice - , public PCI::Device { - friend class GraphicsManagement; - -public: - static ErrorOr probe(PCI::DeviceIdentifier const&); - static ErrorOr> create(PCI::DeviceIdentifier const&); - virtual ~BochsGraphicsAdapter() = default; - virtual StringView device_name() const override { return "BochsGraphicsAdapter"sv; } - -private: - ErrorOr initialize_adapter(PCI::DeviceIdentifier const&); - - explicit BochsGraphicsAdapter(PCI::DeviceIdentifier const&); - - LockRefPtr m_display_connector; -}; -} diff --git a/Kernel/Devices/GPU/Bochs/QEMUDisplayConnector.cpp b/Kernel/Devices/GPU/Bochs/QEMUDisplayConnector.cpp deleted file mode 100644 index abf2679630e..00000000000 --- a/Kernel/Devices/GPU/Bochs/QEMUDisplayConnector.cpp +++ /dev/null @@ -1,183 +0,0 @@ -/* - * Copyright (c) 2022, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include -#include - -namespace Kernel { - -NonnullLockRefPtr QEMUDisplayConnector::must_create(PhysicalAddress framebuffer_address, size_t framebuffer_resource_size, Memory::TypedMapping registers_mapping) -{ - auto device_or_error = DeviceManagement::try_create_device(framebuffer_address, framebuffer_resource_size, move(registers_mapping)); - VERIFY(!device_or_error.is_error()); - auto connector = device_or_error.release_value(); - MUST(connector->create_attached_framebuffer_console()); - MUST(connector->fetch_and_initialize_edid()); - return connector; -} - -ErrorOr QEMUDisplayConnector::fetch_and_initialize_edid() -{ - Array bochs_edid; - static_assert(sizeof(BochsDisplayMMIORegisters::edid_data) >= sizeof(EDID::Definitions::EDID)); - memcpy(bochs_edid.data(), (u8 const*)(m_registers.base_address().offset(__builtin_offsetof(BochsDisplayMMIORegisters, edid_data)).as_ptr()), sizeof(bochs_edid)); - set_edid_bytes(bochs_edid); - return {}; -} - -ErrorOr QEMUDisplayConnector::create_attached_framebuffer_console() -{ - // We assume safe resolution is 1024x768x32 - m_framebuffer_console = Graphics::ContiguousFramebufferConsole::initialize(m_framebuffer_address.value(), 1024, 768, 1024 * sizeof(u32)); - GraphicsManagement::the().set_console(*m_framebuffer_console); - return {}; -} - -void QEMUDisplayConnector::enable_console() -{ - VERIFY(m_control_lock.is_locked()); - VERIFY(m_framebuffer_console); - m_framebuffer_console->enable(); -} - -void QEMUDisplayConnector::disable_console() -{ - VERIFY(m_control_lock.is_locked()); - VERIFY(m_framebuffer_console); - m_framebuffer_console->disable(); -} - -ErrorOr QEMUDisplayConnector::flush_first_surface() -{ - return Error::from_errno(ENOTSUP); -} - -ErrorOr QEMUDisplayConnector::set_safe_mode_setting() -{ - DisplayConnector::ModeSetting safe_mode_set { - .horizontal_stride = 1024 * sizeof(u32), - .pixel_clock_in_khz = 0, // Note: There's no pixel clock in paravirtualized hardware - .horizontal_active = 1024, - .horizontal_front_porch_pixels = 0, // Note: There's no horizontal_front_porch_pixels in paravirtualized hardware - .horizontal_sync_time_pixels = 0, // Note: There's no horizontal_sync_time_pixels in paravirtualized hardware - .horizontal_blank_pixels = 0, // Note: There's no horizontal_blank_pixels in paravirtualized hardware - .vertical_active = 768, - .vertical_front_porch_lines = 0, // Note: There's no vertical_front_porch_lines in paravirtualized hardware - .vertical_sync_time_lines = 0, // Note: There's no vertical_sync_time_lines in paravirtualized hardware - .vertical_blank_lines = 0, // Note: There's no vertical_blank_lines in paravirtualized hardware - .horizontal_offset = 0, - .vertical_offset = 0, - }; - return set_mode_setting(safe_mode_set); -} - -QEMUDisplayConnector::QEMUDisplayConnector(PhysicalAddress framebuffer_address, size_t framebuffer_resource_size, Memory::TypedMapping registers_mapping) - : DisplayConnector(framebuffer_address, framebuffer_resource_size, false) - , m_registers(move(registers_mapping)) -{ -} - -QEMUDisplayConnector::IndexID QEMUDisplayConnector::index_id() const -{ - return m_registers->bochs_regs.index_id; -} - -void QEMUDisplayConnector::set_framebuffer_to_big_endian_format() -{ - VERIFY(m_modeset_lock.is_locked()); - dbgln_if(BXVGA_DEBUG, "QEMUDisplayConnector set_framebuffer_to_big_endian_format"); - full_memory_barrier(); - if (m_registers->extension_regs.region_size == 0xFFFFFFFF || m_registers->extension_regs.region_size == 0) - return; - full_memory_barrier(); - m_registers->extension_regs.framebuffer_byteorder = BOCHS_DISPLAY_BIG_ENDIAN; - full_memory_barrier(); -} - -void QEMUDisplayConnector::set_framebuffer_to_little_endian_format() -{ - VERIFY(m_modeset_lock.is_locked()); - dbgln_if(BXVGA_DEBUG, "QEMUDisplayConnector set_framebuffer_to_little_endian_format"); - full_memory_barrier(); - if (m_registers->extension_regs.region_size == 0xFFFFFFFF || m_registers->extension_regs.region_size == 0) - return; - full_memory_barrier(); - m_registers->extension_regs.framebuffer_byteorder = BOCHS_DISPLAY_LITTLE_ENDIAN; - full_memory_barrier(); -} - -ErrorOr QEMUDisplayConnector::unblank() -{ - SpinlockLocker locker(m_modeset_lock); - full_memory_barrier(); - m_registers->vga_ioports[0] = 0x20; - full_memory_barrier(); - return {}; -} - -ErrorOr QEMUDisplayConnector::set_y_offset(size_t y_offset) -{ - VERIFY(m_modeset_lock.is_locked()); - m_registers->bochs_regs.y_offset = y_offset; - return {}; -} - -ErrorOr QEMUDisplayConnector::set_mode_setting(ModeSetting const& mode_setting) -{ - SpinlockLocker locker(m_modeset_lock); - VERIFY(m_framebuffer_console); - size_t width = mode_setting.horizontal_active; - size_t height = mode_setting.vertical_active; - - if (Checked::multiplication_would_overflow(width, height, sizeof(u32))) - return EOVERFLOW; - - dbgln_if(BXVGA_DEBUG, "QEMUDisplayConnector resolution registers set to - {}x{}", width, height); - m_registers->bochs_regs.enable = 0; - full_memory_barrier(); - m_registers->bochs_regs.xres = width; - m_registers->bochs_regs.yres = height; - m_registers->bochs_regs.virt_width = width; - m_registers->bochs_regs.virt_height = height * 2; - m_registers->bochs_regs.bpp = 32; - full_memory_barrier(); - m_registers->bochs_regs.enable = to_underlying(BochsFramebufferSettings::Enabled) | to_underlying(BochsFramebufferSettings::LinearFramebuffer); - full_memory_barrier(); - m_registers->bochs_regs.bank = 0; - if (index_id().value() == VBE_DISPI_ID5) { - set_framebuffer_to_little_endian_format(); - } - - if ((u16)width != m_registers->bochs_regs.xres || (u16)height != m_registers->bochs_regs.yres) { - return Error::from_errno(ENOTIMPL); - } - - m_framebuffer_console->set_resolution(width, height, width * sizeof(u32)); - - DisplayConnector::ModeSetting mode_set { - .horizontal_stride = m_registers->bochs_regs.xres * sizeof(u32), - .pixel_clock_in_khz = 0, // Note: There's no pixel clock in paravirtualized hardware - .horizontal_active = m_registers->bochs_regs.xres, - .horizontal_front_porch_pixels = 0, // Note: There's no horizontal_front_porch_pixels in paravirtualized hardware - .horizontal_sync_time_pixels = 0, // Note: There's no horizontal_sync_time_pixels in paravirtualized hardware - .horizontal_blank_pixels = 0, // Note: There's no horizontal_blank_pixels in paravirtualized hardware - .vertical_active = m_registers->bochs_regs.yres, - .vertical_front_porch_lines = 0, // Note: There's no vertical_front_porch_lines in paravirtualized hardware - .vertical_sync_time_lines = 0, // Note: There's no vertical_sync_time_lines in paravirtualized hardware - .vertical_blank_lines = 0, // Note: There's no vertical_blank_lines in paravirtualized hardware - .horizontal_offset = 0, - .vertical_offset = 0, - }; - - m_current_mode_setting = mode_set; - return {}; -} - -} diff --git a/Kernel/Devices/GPU/Bochs/QEMUDisplayConnector.h b/Kernel/Devices/GPU/Bochs/QEMUDisplayConnector.h deleted file mode 100644 index 496e231e9e1..00000000000 --- a/Kernel/Devices/GPU/Bochs/QEMUDisplayConnector.h +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright (c) 2022, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include -#include -#include - -namespace Kernel { - -struct BochsDisplayMMIORegisters; -class QEMUDisplayConnector final - : public DisplayConnector { - friend class BochsGraphicsAdapter; - friend class DeviceManagement; - -public: - AK_TYPEDEF_DISTINCT_ORDERED_ID(u16, IndexID); - - static NonnullLockRefPtr must_create(PhysicalAddress framebuffer_address, size_t framebuffer_resource_size, Memory::TypedMapping); - -private: - IndexID index_id() const; - - ErrorOr fetch_and_initialize_edid(); - ErrorOr create_attached_framebuffer_console(); - QEMUDisplayConnector(PhysicalAddress framebuffer_address, size_t framebuffer_resource_size, Memory::TypedMapping); - - virtual bool mutable_mode_setting_capable() const override final { return true; } - virtual bool double_framebuffering_capable() const override { return true; } - virtual ErrorOr set_mode_setting(ModeSetting const&) override; - virtual ErrorOr set_y_offset(size_t y) override; - virtual ErrorOr set_safe_mode_setting() override final; - virtual ErrorOr unblank() override; - virtual bool partial_flush_support() const override final { return false; } - virtual bool flush_support() const override final { return false; } - // Note: Paravirtualized hardware doesn't require a defined refresh rate for modesetting. - virtual bool refresh_rate_support() const override final { return false; } - virtual ErrorOr flush_first_surface() override final; - - void set_framebuffer_to_big_endian_format(); - void set_framebuffer_to_little_endian_format(); - - virtual void enable_console() override final; - virtual void disable_console() override final; - - LockRefPtr m_framebuffer_console; - - Memory::TypedMapping m_registers; -}; -} diff --git a/Kernel/Devices/GPU/Console/BootFramebufferConsole.cpp b/Kernel/Devices/GPU/Console/BootFramebufferConsole.cpp deleted file mode 100644 index 8a77ec04b64..00000000000 --- a/Kernel/Devices/GPU/Console/BootFramebufferConsole.cpp +++ /dev/null @@ -1,91 +0,0 @@ -/* - * Copyright (c) 2022, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include - -namespace Kernel::Graphics { - -BootFramebufferConsole::BootFramebufferConsole(PhysicalAddress framebuffer_addr, size_t width, size_t height, size_t pitch) - : GenericFramebufferConsoleImpl(width, height, pitch) -{ - // NOTE: We're very early in the boot process, memory allocations shouldn't really fail - auto framebuffer_end = Memory::page_round_up(framebuffer_addr.offset(height * pitch).get()).release_value(); - m_framebuffer = MM.allocate_mmio_kernel_region(framebuffer_addr.page_base(), framebuffer_end - framebuffer_addr.page_base().get(), "Boot Framebuffer"sv, Memory::Region::Access::ReadWrite).release_value(); - - [[maybe_unused]] auto result = m_framebuffer->set_write_combine(true); - m_framebuffer_data = m_framebuffer->vaddr().offset(framebuffer_addr.offset_in_page()).as_ptr(); - memset(m_framebuffer_data, 0, height * pitch); -} - -void BootFramebufferConsole::clear(size_t x, size_t y, size_t length) -{ - SpinlockLocker lock(m_lock); - if (m_framebuffer_data) - GenericFramebufferConsoleImpl::clear(x, y, length); -} - -void BootFramebufferConsole::clear_glyph(size_t x, size_t y) -{ - - VERIFY(m_lock.is_locked()); - GenericFramebufferConsoleImpl::clear_glyph(x, y); -} - -void BootFramebufferConsole::enable() -{ - // Once disabled, ignore requests to re-enable -} - -void BootFramebufferConsole::disable() -{ - SpinlockLocker lock(m_lock); - GenericFramebufferConsoleImpl::disable(); - m_framebuffer = nullptr; - m_framebuffer_data = nullptr; -} - -void BootFramebufferConsole::write(size_t x, size_t y, char ch, Color background, Color foreground, bool critical) -{ - SpinlockLocker lock(m_lock); - if (m_framebuffer_data) - GenericFramebufferConsoleImpl::write(x, y, ch, background, foreground, critical); -} - -void BootFramebufferConsole::set_cursor(size_t x, size_t y) -{ - // Note: To ensure we don't trigger a deadlock, let's assert in - // case we already locked the spinlock, so we know there's a bug - // in the call path. - VERIFY(!m_lock.is_locked()); - SpinlockLocker lock(m_lock); - hide_cursor(); - m_x = x; - m_y = y; - show_cursor(); -} - -void BootFramebufferConsole::hide_cursor() -{ - VERIFY(m_lock.is_locked()); - GenericFramebufferConsoleImpl::hide_cursor(); -} - -void BootFramebufferConsole::show_cursor() -{ - VERIFY(m_lock.is_locked()); - GenericFramebufferConsoleImpl::show_cursor(); -} - -u8* BootFramebufferConsole::framebuffer_data() -{ - VERIFY(m_lock.is_locked()); - VERIFY(m_framebuffer_data); - return m_framebuffer_data; -} - -} diff --git a/Kernel/Devices/GPU/Console/BootFramebufferConsole.h b/Kernel/Devices/GPU/Console/BootFramebufferConsole.h deleted file mode 100644 index a280c54401e..00000000000 --- a/Kernel/Devices/GPU/Console/BootFramebufferConsole.h +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright (c) 2022, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include - -namespace Kernel::Graphics { - -class BootFramebufferConsole : public GenericFramebufferConsoleImpl { -public: - virtual void clear(size_t x, size_t y, size_t length) override; - virtual void write(size_t x, size_t y, char ch, Color background, Color foreground, bool critical = false) override; - using GenericFramebufferConsoleImpl::write; - - virtual void enable() override; - virtual void disable() override; - - virtual void flush(size_t, size_t, size_t, size_t) override { } - virtual void set_resolution(size_t, size_t, size_t) override { } - - u8* unsafe_framebuffer_data() { return m_framebuffer_data; } - - BootFramebufferConsole(PhysicalAddress framebuffer_addr, size_t width, size_t height, size_t pitch); - -private: - virtual void set_cursor(size_t x, size_t y) override; - virtual void hide_cursor() override; - virtual void show_cursor() override; - -protected: - virtual void clear_glyph(size_t x, size_t y) override; - - virtual u8* framebuffer_data() override; - - OwnPtr m_framebuffer; - u8* m_framebuffer_data {}; - mutable Spinlock m_lock {}; -}; - -} diff --git a/Kernel/Devices/GPU/Console/Console.h b/Kernel/Devices/GPU/Console/Console.h deleted file mode 100644 index 5d4dee47606..00000000000 --- a/Kernel/Devices/GPU/Console/Console.h +++ /dev/null @@ -1,83 +0,0 @@ -/* - * Copyright (c) 2021, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include - -namespace Kernel::Graphics { - -class Console : public AtomicRefCounted { -public: - // Stanadard VGA text mode colors - enum Color : u8 { - Black = 0, - Blue, - Green, - Cyan, - Red, - Magenta, - Brown, - LightGray, - DarkGray, - BrightBlue, - BrightGreen, - BrightCyan, - BrightRed, - BrightMagenta, - Yellow, - White, - }; - -public: - size_t width() const { return m_width; } - size_t height() const { return m_height; } - size_t pitch() const { return bytes_per_base_glyph() * width(); } - virtual size_t max_column() const { return m_width; } - virtual size_t max_row() const { return m_height; } - virtual size_t bytes_per_base_glyph() const = 0; - virtual size_t chars_per_line() const = 0; - - virtual void enable() = 0; - virtual void disable() = 0; - - virtual bool is_hardware_paged_capable() const = 0; - virtual bool has_hardware_cursor() const = 0; - - virtual void set_cursor(size_t x, size_t y) = 0; - - virtual void clear(size_t x, size_t y, size_t length) = 0; - virtual void write(size_t x, size_t y, char ch, Color background, Color foreground, bool critical = false) = 0; - virtual void write(size_t x, size_t y, char ch, bool critical = false) = 0; - virtual void write(char ch, bool critical = false) = 0; - virtual void flush(size_t x, size_t y, size_t width, size_t height) = 0; - - virtual ~Console() = default; - -protected: - virtual void hide_cursor() = 0; - virtual void show_cursor() = 0; - - virtual void scroll_up() = 0; - - Console(size_t width, size_t height) - : m_width(width) - , m_height(height) - { - m_enabled.store(true); - } - - Atomic m_enabled; - Color m_default_foreground_color { Color::White }; - Color m_default_background_color { Color::Black }; - size_t m_width; - size_t m_height; - mutable size_t m_x { 0 }; - mutable size_t m_y { 0 }; -}; -} diff --git a/Kernel/Devices/GPU/Console/ContiguousFramebufferConsole.cpp b/Kernel/Devices/GPU/Console/ContiguousFramebufferConsole.cpp deleted file mode 100644 index eecc63160ee..00000000000 --- a/Kernel/Devices/GPU/Console/ContiguousFramebufferConsole.cpp +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright (c) 2021, Sahan Fernando - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include - -namespace Kernel::Graphics { - -NonnullLockRefPtr ContiguousFramebufferConsole::initialize(PhysicalAddress framebuffer_address, size_t width, size_t height, size_t pitch) -{ - return adopt_lock_ref(*new ContiguousFramebufferConsole(framebuffer_address, width, height, pitch)); -} - -ContiguousFramebufferConsole::ContiguousFramebufferConsole(PhysicalAddress framebuffer_address, size_t width, size_t height, size_t pitch) - : GenericFramebufferConsole(width, height, pitch) - , m_framebuffer_address(framebuffer_address) -{ - set_resolution(width, height, pitch); -} - -void ContiguousFramebufferConsole::set_resolution(size_t width, size_t height, size_t pitch) -{ - m_width = width; - m_height = height; - m_pitch = pitch; - - size_t size = Memory::page_round_up(pitch * height).release_value_but_fixme_should_propagate_errors(); - dbgln("Framebuffer Console: taking {} bytes", size); - auto region_or_error = MM.allocate_mmio_kernel_region(m_framebuffer_address, size, "Framebuffer Console"sv, Memory::Region::Access::ReadWrite, Memory::Region::Cacheable::Yes); - VERIFY(!region_or_error.is_error()); - m_framebuffer_region = region_or_error.release_value(); - - // Just to start cleanly, we clean the entire framebuffer - memset(m_framebuffer_region->vaddr().as_ptr(), 0, pitch * height); - - ConsoleManagement::the().resolution_was_changed(); -} - -} diff --git a/Kernel/Devices/GPU/Console/ContiguousFramebufferConsole.h b/Kernel/Devices/GPU/Console/ContiguousFramebufferConsole.h deleted file mode 100644 index 0ae8f2d3fab..00000000000 --- a/Kernel/Devices/GPU/Console/ContiguousFramebufferConsole.h +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright (c) 2021, Sahan Fernando - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include - -namespace Kernel::Graphics { - -class ContiguousFramebufferConsole final : public GenericFramebufferConsole { -public: - static NonnullLockRefPtr initialize(PhysicalAddress, size_t width, size_t height, size_t pitch); - - virtual void set_resolution(size_t width, size_t height, size_t pitch) override; - virtual void flush(size_t, size_t, size_t, size_t) override { } - -private: - virtual u8* framebuffer_data() override - { - return m_framebuffer_region->vaddr().as_ptr(); - } - OwnPtr m_framebuffer_region; - ContiguousFramebufferConsole(PhysicalAddress, size_t width, size_t height, size_t pitch); - PhysicalAddress m_framebuffer_address; -}; - -} diff --git a/Kernel/Devices/GPU/Console/GenericFramebufferConsole.cpp b/Kernel/Devices/GPU/Console/GenericFramebufferConsole.cpp deleted file mode 100644 index ebc10e2ddc5..00000000000 --- a/Kernel/Devices/GPU/Console/GenericFramebufferConsole.cpp +++ /dev/null @@ -1,406 +0,0 @@ -/* - * Copyright (c) 2021, Liav A. - * Copyright (c) 2022, MacDue - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include - -namespace Kernel::Graphics { - -// Note: This bitmap was generated from CathodeRegular10.font -constexpr unsigned char const font_cathode_8x16[128][16] = { - { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // U+0000 (nul) - { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // U+0001 - { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // U+0002 - { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // U+0003 - { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // U+0004 - { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // U+0005 - { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // U+0006 - { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // U+0007 - { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // U+0008 - { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // U+0009 - { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // U+000A - { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // U+000B - { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // U+000C - { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // U+000D - { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // U+000E - { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // U+000F - { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // U+0010 - { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // U+0011 - { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // U+0012 - { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // U+0013 - { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // U+0014 - { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // U+0015 - { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // U+0016 - { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // U+0017 - { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // U+0018 - { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // U+0019 - { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // U+001A - { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // U+001B - { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // U+001C - { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // U+001D - { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // U+001E - { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // U+001F - { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // U+0020 ( ) - { 0x00, 0x00, 0x30, 0x78, 0x78, 0x78, 0x78, 0x78, 0x30, 0x30, 0x00, 0x30, 0x30, 0x00, 0x00, 0x00 }, // U+0021 (!) - { 0x00, 0x00, 0x6C, 0x6C, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // U+0022 (") - { 0x00, 0x00, 0x6C, 0x6C, 0x6C, 0xFE, 0x6C, 0x6C, 0x6C, 0xFE, 0x6C, 0x6C, 0x6C, 0x00, 0x00, 0x00 }, // U+0023 (#) - { 0x00, 0x00, 0x30, 0x30, 0x78, 0xCC, 0xCC, 0x60, 0x18, 0xCC, 0xCC, 0x78, 0x30, 0x30, 0x00, 0x00 }, // U+0024 ($) - { 0x00, 0x00, 0x00, 0x00, 0x00, 0xC2, 0xC4, 0x08, 0x10, 0x20, 0x46, 0x86, 0x00, 0x00, 0x00, 0x00 }, // U+0025 (%) - { 0x00, 0x00, 0x78, 0xCC, 0xCC, 0xCC, 0x78, 0x36, 0x5C, 0xCC, 0xCC, 0xCC, 0x76, 0x00, 0x00, 0x00 }, // U+0026 (&) - { 0x00, 0x00, 0x18, 0x18, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // U+0027 (') - { 0x00, 0x00, 0x0C, 0x18, 0x30, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x30, 0x18, 0x0C, 0x00, 0x00 }, // U+0028 (() - { 0x00, 0x00, 0x60, 0x30, 0x18, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x18, 0x30, 0x60, 0x00, 0x00 }, // U+0029 ()) - { 0x00, 0x00, 0x00, 0x00, 0x00, 0xCC, 0x30, 0xFC, 0x30, 0xCC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // U+002A (*) - { 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x30, 0xFC, 0x30, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // U+002B (+) - { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x30, 0x00, 0x00 }, // U+002C (,) - { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // U+002D (-) - { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x30, 0x00, 0x00, 0x00 }, // U+002E (.) - { 0x00, 0x00, 0x02, 0x06, 0x04, 0x0C, 0x18, 0x10, 0x30, 0x60, 0x40, 0xC0, 0x80, 0x00, 0x00, 0x00 }, // U+002F (/) - { 0x00, 0x00, 0x7C, 0xC6, 0xCE, 0xCE, 0xDE, 0xD6, 0xF6, 0xE6, 0xC6, 0xC6, 0x7C, 0x00, 0x00, 0x00 }, // U+0030 (0) - { 0x00, 0x00, 0x30, 0x70, 0xF0, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0xFC, 0x00, 0x00, 0x00 }, // U+0031 (1) - { 0x00, 0x00, 0x7C, 0xC6, 0x06, 0x06, 0x06, 0x0C, 0x18, 0x30, 0x60, 0xC6, 0xFE, 0x00, 0x00, 0x00 }, // U+0032 (2) - { 0x00, 0x00, 0x7C, 0xC6, 0x06, 0x06, 0x04, 0x38, 0x04, 0x06, 0x06, 0xC6, 0x7C, 0x00, 0x00, 0x00 }, // U+0033 (3) - { 0x00, 0x00, 0x0C, 0x1C, 0x3C, 0x6C, 0xCC, 0xFE, 0x0C, 0x0C, 0x0C, 0x0C, 0x1E, 0x00, 0x00, 0x00 }, // U+0034 (4) - { 0x00, 0x00, 0xFE, 0xC0, 0xC0, 0xC0, 0xFC, 0x06, 0x06, 0x06, 0x06, 0xC6, 0x7C, 0x00, 0x00, 0x00 }, // U+0035 (5) - { 0x00, 0x00, 0x1C, 0x30, 0x60, 0xC0, 0xC0, 0xFC, 0xC6, 0xC6, 0xC6, 0xC6, 0x7C, 0x00, 0x00, 0x00 }, // U+0036 (6) - { 0x00, 0x00, 0xFE, 0xC6, 0x06, 0x0C, 0x18, 0x30, 0x60, 0x60, 0x60, 0x60, 0x60, 0x00, 0x00, 0x00 }, // U+0037 (7) - { 0x00, 0x00, 0x7C, 0xC6, 0xC6, 0xC6, 0xC6, 0x7C, 0xC6, 0xC6, 0xC6, 0xC6, 0x7C, 0x00, 0x00, 0x00 }, // U+0038 (8) - { 0x00, 0x00, 0x7C, 0xC6, 0xC6, 0xC6, 0xC6, 0x7E, 0x06, 0x06, 0x0C, 0x18, 0x70, 0x00, 0x00, 0x00 }, // U+0039 (9) - { 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x30, 0x00, 0x00, 0x00, 0x00, 0x30, 0x30, 0x00, 0x00, 0x00 }, // U+003A (:) - { 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x30, 0x00, 0x00, 0x00, 0x00, 0x30, 0x30, 0x60, 0x00, 0x00 }, // U+003B (;) - { 0x00, 0x00, 0x06, 0x0C, 0x18, 0x30, 0x60, 0xE0, 0x60, 0x30, 0x18, 0x0C, 0x06, 0x00, 0x00, 0x00 }, // U+003C (<) - { 0x00, 0x00, 0x00, 0x00, 0x00, 0xFC, 0x00, 0x00, 0x00, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // U+003D (=) - { 0x00, 0x00, 0xC0, 0x60, 0x30, 0x18, 0x0C, 0x0E, 0x0C, 0x18, 0x30, 0x60, 0xC0, 0x00, 0x00, 0x00 }, // U+003E (>) - { 0x00, 0x00, 0x7C, 0xC6, 0xC6, 0x06, 0x0C, 0x18, 0x30, 0x30, 0x00, 0x30, 0x30, 0x00, 0x00, 0x00 }, // U+003F (?) - { 0x00, 0x00, 0x7C, 0xC6, 0xC6, 0xDE, 0xDE, 0xDE, 0xDE, 0xDE, 0xDC, 0xC0, 0x7C, 0x00, 0x00, 0x00 }, // U+0040 (@) - { 0x00, 0x00, 0x10, 0x38, 0x6C, 0xC6, 0xC6, 0xFE, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0x00, 0x00, 0x00 }, // U+0041 (A) - { 0x00, 0x00, 0xFC, 0x66, 0x66, 0x66, 0x66, 0x7C, 0x66, 0x66, 0x66, 0x66, 0xFC, 0x00, 0x00, 0x00 }, // U+0042 (B) - { 0x00, 0x00, 0x3C, 0x66, 0xC2, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC2, 0x66, 0x3C, 0x00, 0x00, 0x00 }, // U+0043 (C) - { 0x00, 0x00, 0xF8, 0x6C, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x6C, 0xF8, 0x00, 0x00, 0x00 }, // U+0044 (D) - { 0x00, 0x00, 0xFE, 0x66, 0x62, 0x60, 0x68, 0x78, 0x68, 0x60, 0x62, 0x66, 0xFE, 0x00, 0x00, 0x00 }, // U+0045 (E) - { 0x00, 0x00, 0xFE, 0x66, 0x62, 0x60, 0x64, 0x7C, 0x64, 0x60, 0x60, 0x60, 0xF0, 0x00, 0x00, 0x00 }, // U+0046 (F) - { 0x00, 0x00, 0x3C, 0x66, 0xC2, 0xC0, 0xC0, 0xDE, 0xC6, 0xC6, 0xC6, 0x66, 0x3A, 0x00, 0x00, 0x00 }, // U+0047 (G) - { 0x00, 0x00, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0xFE, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0x00, 0x00, 0x00 }, // U+0048 (H) - { 0x00, 0x00, 0x78, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x78, 0x00, 0x00, 0x00 }, // U+0049 (I) - { 0x00, 0x00, 0x1E, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0xCC, 0xCC, 0x78, 0x00, 0x00, 0x00 }, // U+004A (J) - { 0x00, 0x00, 0xE6, 0x66, 0x6C, 0x6C, 0x68, 0x78, 0x68, 0x6C, 0x6C, 0x66, 0xE6, 0x00, 0x00, 0x00 }, // U+004B (K) - { 0x00, 0x00, 0xF0, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x62, 0x66, 0xFE, 0x00, 0x00, 0x00 }, // U+004C (L) - { 0x00, 0x00, 0xC6, 0xEE, 0xFE, 0xFE, 0xD6, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0x00, 0x00, 0x00 }, // U+004D (M) - { 0x00, 0x00, 0xC6, 0xE6, 0xF6, 0xFE, 0xFE, 0xDE, 0xCE, 0xC6, 0xC6, 0xC6, 0xC6, 0x00, 0x00, 0x00 }, // U+004E (N) - { 0x00, 0x00, 0x38, 0x6C, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0x6C, 0x38, 0x00, 0x00, 0x00 }, // U+004F (O) - { 0x00, 0x00, 0xFC, 0x66, 0x66, 0x66, 0x66, 0x7C, 0x60, 0x60, 0x60, 0x60, 0xF0, 0x00, 0x00, 0x00 }, // U+0050 (P) - { 0x00, 0x00, 0x7C, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0xD6, 0xDE, 0x7C, 0x0E, 0x00, 0x00 }, // U+0051 (Q) - { 0x00, 0x00, 0xFC, 0x66, 0x66, 0x66, 0x66, 0x7C, 0x6C, 0x66, 0x66, 0x66, 0xF6, 0x00, 0x00, 0x00 }, // U+0052 (R) - { 0x00, 0x00, 0x7C, 0xC6, 0xC6, 0xC0, 0x60, 0x30, 0x18, 0x0C, 0xC6, 0xC6, 0x7C, 0x00, 0x00, 0x00 }, // U+0053 (S) - { 0x00, 0x00, 0xFC, 0xFC, 0xB4, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x78, 0x00, 0x00, 0x00 }, // U+0054 (T) - { 0x00, 0x00, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0x7C, 0x00, 0x00, 0x00 }, // U+0055 (U) - { 0x00, 0x00, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0x6C, 0x38, 0x10, 0x00, 0x00, 0x00 }, // U+0056 (V) - { 0x00, 0x00, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0xD6, 0xD6, 0xD6, 0xFE, 0x7C, 0x6C, 0x00, 0x00, 0x00 }, // U+0057 (W) - { 0x00, 0x00, 0xC6, 0xC6, 0x6C, 0x38, 0x38, 0x38, 0x38, 0x38, 0x6C, 0xC6, 0xC6, 0x00, 0x00, 0x00 }, // U+0058 (X) - { 0x00, 0x00, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0x78, 0x30, 0x30, 0x30, 0x30, 0x78, 0x00, 0x00, 0x00 }, // U+0059 (Y) - { 0x00, 0x00, 0xFE, 0xC6, 0x86, 0x0C, 0x18, 0x30, 0x60, 0x40, 0xC2, 0xC6, 0xFE, 0x00, 0x00, 0x00 }, // U+005A (Z) - { 0x00, 0x00, 0xFC, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xFC, 0x00, 0x00, 0x00 }, // U+005B ([) - { 0x00, 0x00, 0x80, 0xC0, 0x40, 0x60, 0x30, 0x10, 0x18, 0x0C, 0x04, 0x06, 0x02, 0x00, 0x00, 0x00 }, // U+005C (\) - { 0x00, 0x00, 0xFC, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0xFC, 0x00, 0x00, 0x00 }, // U+005D (]) - { 0x00, 0x00, 0x20, 0x70, 0xD8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // U+005E (^) - { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFC, 0x00, 0x00, 0x00 }, // U+005F (_) - { 0x00, 0x00, 0x60, 0x70, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // U+0060 (`) - { 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x0C, 0x0C, 0x7C, 0xCC, 0xCC, 0xCC, 0x76, 0x00, 0x00, 0x00 }, // U+0061 (a) - { 0x00, 0x00, 0xE0, 0x60, 0x60, 0x78, 0x6C, 0x66, 0x66, 0x66, 0x66, 0x66, 0x7C, 0x00, 0x00, 0x00 }, // U+0062 (b) - { 0x00, 0x00, 0x00, 0x00, 0x00, 0x7C, 0xC6, 0xC0, 0xC0, 0xC0, 0xC0, 0xC6, 0x7C, 0x00, 0x00, 0x00 }, // U+0063 (c) - { 0x00, 0x00, 0x1C, 0x0C, 0x0C, 0x3C, 0x6C, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0x76, 0x00, 0x00, 0x00 }, // U+0064 (d) - { 0x00, 0x00, 0x00, 0x00, 0x00, 0x7C, 0xC6, 0xFE, 0xC0, 0xC0, 0xC0, 0xC6, 0x7C, 0x00, 0x00, 0x00 }, // U+0065 (e) - { 0x00, 0x00, 0x38, 0x6C, 0x64, 0x60, 0xF0, 0x60, 0x60, 0x60, 0x60, 0x60, 0xF0, 0x00, 0x00, 0x00 }, // U+0066 (f) - { 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0xCC, 0xCC, 0xCC, 0xCC, 0x7C, 0x0C, 0xCC, 0x78, 0x00, 0x00 }, // U+0067 (g) - { 0x00, 0x00, 0xE0, 0x60, 0x60, 0x6C, 0x76, 0x66, 0x66, 0x66, 0x66, 0x66, 0xE6, 0x00, 0x00, 0x00 }, // U+0068 (h) - { 0x00, 0x00, 0x30, 0x30, 0x00, 0x70, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x78, 0x00, 0x00, 0x00 }, // U+0069 (i) - { 0x00, 0x00, 0x06, 0x06, 0x00, 0x0E, 0x06, 0x06, 0x06, 0x06, 0x06, 0xC6, 0xC6, 0x7C, 0x00, 0x00 }, // U+006A (j) - { 0x00, 0x00, 0xE0, 0x60, 0x60, 0x66, 0x6C, 0x68, 0x70, 0x68, 0x6C, 0x66, 0xE6, 0x00, 0x00, 0x00 }, // U+006B (k) - { 0x00, 0x00, 0x70, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x78, 0x00, 0x00, 0x00 }, // U+006C (l) - { 0x00, 0x00, 0x00, 0x00, 0x00, 0xEC, 0xFE, 0xD6, 0xD6, 0xD6, 0xD6, 0xC6, 0xC6, 0x00, 0x00, 0x00 }, // U+006D (m) - { 0x00, 0x00, 0x00, 0x00, 0x00, 0xDC, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x00, 0x00, 0x00 }, // U+006E (n) - { 0x00, 0x00, 0x00, 0x00, 0x00, 0x7C, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0x7C, 0x00, 0x00, 0x00 }, // U+006F (o) - { 0x00, 0x00, 0x00, 0x00, 0x00, 0xDC, 0x66, 0x66, 0x66, 0x66, 0x7C, 0x60, 0x60, 0xF0, 0x00, 0x00 }, // U+0070 (p) - { 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0xCC, 0xCC, 0xCC, 0xCC, 0x7C, 0x0C, 0x0C, 0x1E, 0x00, 0x00 }, // U+0071 (q) - { 0x00, 0x00, 0x00, 0x00, 0x00, 0xD8, 0x76, 0x66, 0x60, 0x60, 0x60, 0x60, 0xF0, 0x00, 0x00, 0x00 }, // U+0072 (r) - { 0x00, 0x00, 0x00, 0x00, 0x00, 0x7C, 0xC6, 0x40, 0x70, 0x1C, 0x04, 0xC6, 0x7C, 0x00, 0x00, 0x00 }, // U+0073 (s) - { 0x00, 0x00, 0x10, 0x30, 0x30, 0xFC, 0x30, 0x30, 0x30, 0x30, 0x30, 0x36, 0x1C, 0x00, 0x00, 0x00 }, // U+0074 (t) - { 0x00, 0x00, 0x00, 0x00, 0x00, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0x76, 0x00, 0x00, 0x00 }, // U+0075 (u) - { 0x00, 0x00, 0x00, 0x00, 0x00, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0x78, 0x30, 0x00, 0x00, 0x00 }, // U+0076 (v) - { 0x00, 0x00, 0x00, 0x00, 0x00, 0xC6, 0xC6, 0xC6, 0xD6, 0xD6, 0xD6, 0xFE, 0x6C, 0x00, 0x00, 0x00 }, // U+0077 (w) - { 0x00, 0x00, 0x00, 0x00, 0x00, 0xC6, 0x6C, 0x38, 0x38, 0x38, 0x38, 0x6C, 0xC6, 0x00, 0x00, 0x00 }, // U+0078 (x) - { 0x00, 0x00, 0x00, 0x00, 0x00, 0xC6, 0xC6, 0xC6, 0xC6, 0x7E, 0x06, 0x06, 0x0C, 0xF8, 0x00, 0x00 }, // U+0079 (y) - { 0x00, 0x00, 0x00, 0x00, 0x00, 0xFE, 0xCC, 0x0C, 0x18, 0x30, 0x60, 0x66, 0xFE, 0x00, 0x00, 0x00 }, // U+007A (z) - { 0x00, 0x00, 0x1E, 0x30, 0x30, 0x30, 0x30, 0xE0, 0x30, 0x30, 0x30, 0x30, 0x1E, 0x00, 0x00, 0x00 }, // U+007B ({) - { 0x00, 0x00, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x00, 0x00, 0x00 }, // U+007C (|) - { 0x00, 0x00, 0xF0, 0x18, 0x18, 0x18, 0x18, 0x0E, 0x18, 0x18, 0x18, 0x18, 0xF0, 0x00, 0x00, 0x00 }, // U+007D (}) - { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0xDC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // U+007E (~) - { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } // U+007F -}; - -// FIXME: This assumes 32 bit BGR (Blue-Green-Red) palette -enum BGRColor : u32 { - Black = 0, - Blue = 0x0000FF, - Green = 0x00FF00, - Cyan = 0x0000FFFF, - Red = 0xFF0000, - Magenta = 0x00FF00FF, - Brown = 0x00964B00, - LightGray = 0x00D3D3D3, - DarkGray = 0x00A9A9A9, - BrightBlue = 0x0ADD8E6, - BrightGreen = 0x0090EE90, - BrightCyan = 0x00E0FFFF, - BrightRed = 0x00D70A53, - BrightMagenta = 0x00F984E5, - Yellow = 0x00FFE135, - White = 0x00FFFFFF, -}; - -static inline BGRColor convert_standard_color_to_bgr_color(Console::Color color) -{ - switch (color) { - case Console::Color::Black: - return BGRColor::Black; - case Console::Color::Red: - return BGRColor::Red; - case Console::Color::Brown: - return BGRColor::Brown; - case Console::Color::Blue: - return BGRColor::Blue; - case Console::Color::Magenta: - return BGRColor::Magenta; - case Console::Color::Green: - return BGRColor::Green; - case Console::Color::Cyan: - return BGRColor::Cyan; - case Console::Color::LightGray: - return BGRColor::LightGray; - case Console::Color::DarkGray: - return BGRColor::DarkGray; - case Console::Color::BrightRed: - return BGRColor::BrightRed; - case Console::Color::BrightGreen: - return BGRColor::BrightGreen; - case Console::Color::Yellow: - return BGRColor::Yellow; - case Console::Color::BrightBlue: - return BGRColor::BrightBlue; - case Console::Color::BrightMagenta: - return BGRColor::BrightMagenta; - case Console::Color::BrightCyan: - return BGRColor::BrightCyan; - case Console::Color::White: - return BGRColor::White; - default: - VERIFY_NOT_REACHED(); - } -} - -size_t GenericFramebufferConsoleImpl::bytes_per_base_glyph() const -{ - // FIXME: We assume we have 32 bit bpp framebuffer. - return 8 * 32; -} - -size_t GenericFramebufferConsoleImpl::chars_per_line() const -{ - return max_column(); -} - -void GenericFramebufferConsoleImpl::set_cursor(size_t x, size_t y) -{ - hide_cursor(); - m_x = x; - m_y = y; - show_cursor(); -} - -GenericFramebufferConsoleImpl::FramebufferOffset GenericFramebufferConsoleImpl::framebuffer_offset(size_t x, size_t y) -{ - return { (&framebuffer_data()[x * sizeof(u32) * (m_glyph_columns + m_glyph_spacing) + y * m_glyph_rows * framebuffer_pitch()]) }; -} - -void GenericFramebufferConsoleImpl::hide_cursor() -{ - auto offset_in_framebuffer = framebuffer_offset(m_x, m_y); - offset_in_framebuffer.bytes += framebuffer_pitch() * (m_glyph_rows - 1); - for (size_t glyph_column = 0; glyph_column < m_glyph_columns; glyph_column++) - offset_in_framebuffer.pixels[glyph_column] = m_cursor_overriden_pixels[glyph_column]; -} - -void GenericFramebufferConsoleImpl::show_cursor() -{ - auto offset_in_framebuffer = framebuffer_offset(m_x, m_y); - offset_in_framebuffer.bytes += framebuffer_pitch() * (m_glyph_rows - 1); - for (size_t glyph_column = 0; glyph_column < m_glyph_columns; glyph_column++) { - m_cursor_overriden_pixels[glyph_column] = offset_in_framebuffer.pixels[glyph_column]; - memset(offset_in_framebuffer.pixels + glyph_column, 0xff, sizeof(u32)); - } -} - -void GenericFramebufferConsoleImpl::clear(size_t x, size_t y, size_t length) -{ - if (x == 0 && length == max_column()) { - // If we need to clear the entire row, just clean it with quick memset :) - auto offset_in_framebuffer = framebuffer_offset(x, y); - for (size_t glyph_row = 0; glyph_row < m_glyph_rows; glyph_row++) { - memset(offset_in_framebuffer.pixels, 0, framebuffer_pitch()); - offset_in_framebuffer.bytes += framebuffer_pitch(); - } - flush(0, m_glyph_rows * y, (m_glyph_columns + m_glyph_spacing) * length, 1); - return; - } - for (size_t index = 0; index < length; index++) { - if (x >= max_column()) { - x = 0; - y++; - if (y >= max_row()) - y = 0; - } - clear_glyph(x, y); - } -} - -void GenericFramebufferConsoleImpl::clear_glyph(size_t x, size_t y) -{ - auto offset_in_framebuffer = framebuffer_offset(x, y); - for (size_t glyph_row = 0; glyph_row < m_glyph_rows; glyph_row++) { - memset(offset_in_framebuffer.pixels, 0, (m_glyph_columns + m_glyph_spacing) * sizeof(u32)); - offset_in_framebuffer.bytes += framebuffer_pitch(); - } - flush_glyph(x, y); -} - -void GenericFramebufferConsoleImpl::enable() -{ - memset(framebuffer_data(), 0, height() * framebuffer_pitch()); - m_enabled.store(true); -} - -void GenericFramebufferConsoleImpl::disable() -{ - m_enabled.store(false); -} - -void GenericFramebufferConsoleImpl::scroll_up() -{ - for (size_t row = 1; row < max_row(); row++) { - auto prev_line = framebuffer_offset(0, row - 1); - auto line = framebuffer_offset(0, row); - for (size_t glyph_row = 0; glyph_row < m_glyph_rows; glyph_row++) { - memmove(prev_line.pixels, line.pixels, width() * sizeof(*line.pixels)); - prev_line.bytes += framebuffer_pitch(); - line.bytes += framebuffer_pitch(); - } - } - - for (size_t column = 0; column < max_column(); column++) - clear_glyph(column, m_y); -} - -void GenericFramebufferConsoleImpl::write(size_t x, size_t y, char ch, Color background, Color foreground, bool critical) -{ - if (!m_enabled.load()) - return; - - // If we are in critical printing mode, we need to handle new lines here - // because there's no other responsible object to do that in the print call path - if (critical && (ch == '\r' || ch == '\n')) { - m_x = 0; - m_y += 1; - if (m_y >= max_row()) { - m_y = max_row() - 1; - scroll_up(); - } - return; - } - - if ((int)ch < 0x20 || (int)ch == 0x7f) { - // FIXME: There's no point in printing empty glyphs... - // Maybe try to add these special glyphs and print them. - return; - } - - clear_glyph(x, y); - auto bitmap = font_cathode_8x16[(int)ch]; - auto offset_in_framebuffer = framebuffer_offset(x, y); - BGRColor foreground_color = convert_standard_color_to_bgr_color(foreground); - BGRColor background_color = convert_standard_color_to_bgr_color(background); - for (size_t glyph_row = 0; glyph_row < m_glyph_rows; glyph_row++) { - for (size_t glyph_column = m_glyph_columns; glyph_column > 0; glyph_column--) { - bool pixel_set = bitmap[glyph_row] & (1 << glyph_column); - offset_in_framebuffer.pixels[m_glyph_columns - glyph_column] = pixel_set ? foreground_color : background_color; - } - for (size_t spacing_column = 0; spacing_column < m_glyph_spacing; spacing_column++) - offset_in_framebuffer.pixels[m_glyph_columns + spacing_column] = background_color; - offset_in_framebuffer.bytes += framebuffer_pitch(); - } - flush_glyph(x, y); - - m_x = x + 1; - if (m_x >= max_column()) { - m_x = 0; - m_y = y + 1; - if (m_y >= max_row()) { - if (critical) { - m_y = max_row() - 1; - scroll_up(); - } else { - m_y = 0; - } - } - } -} - -void GenericFramebufferConsoleImpl::flush_glyph(size_t x, size_t y) -{ - flush((m_glyph_columns + m_glyph_spacing) * x, m_glyph_rows * y, m_glyph_columns + m_glyph_spacing, m_glyph_rows); -} - -void GenericFramebufferConsoleImpl::write(size_t x, size_t y, char ch, bool critical) -{ - write(x, y, ch, m_default_background_color, m_default_foreground_color, critical); -} - -void GenericFramebufferConsoleImpl::write(char ch, bool critical) -{ - write(m_x, m_y, ch, m_default_background_color, m_default_foreground_color, critical); -} - -void GenericFramebufferConsole::clear(size_t x, size_t y, size_t length) -{ - SpinlockLocker lock(m_lock); - GenericFramebufferConsoleImpl::clear(x, y, length); -} - -void GenericFramebufferConsole::clear_glyph(size_t x, size_t y) -{ - VERIFY(m_lock.is_locked()); - GenericFramebufferConsoleImpl::clear_glyph(x, y); -} - -void GenericFramebufferConsole::enable() -{ - SpinlockLocker lock(m_lock); - GenericFramebufferConsoleImpl::enable(); -} - -void GenericFramebufferConsole::disable() -{ - SpinlockLocker lock(m_lock); - GenericFramebufferConsoleImpl::disable(); -} - -void GenericFramebufferConsole::write(size_t x, size_t y, char ch, Color background, Color foreground, bool critical) -{ - SpinlockLocker lock(m_lock); - GenericFramebufferConsoleImpl::write(x, y, ch, background, foreground, critical); -} - -} diff --git a/Kernel/Devices/GPU/Console/GenericFramebufferConsole.h b/Kernel/Devices/GPU/Console/GenericFramebufferConsole.h deleted file mode 100644 index bb3cbda7a99..00000000000 --- a/Kernel/Devices/GPU/Console/GenericFramebufferConsole.h +++ /dev/null @@ -1,89 +0,0 @@ -/* - * Copyright (c) 2021, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include - -namespace Kernel::Graphics { - -class GenericFramebufferConsoleImpl : public Console { -public: - virtual size_t bytes_per_base_glyph() const override; - virtual size_t chars_per_line() const override; - - virtual size_t max_column() const override { return m_width / (m_glyph_columns + m_glyph_spacing); } - virtual size_t max_row() const override { return m_height / m_glyph_rows; } - - virtual bool is_hardware_paged_capable() const override { return false; } - virtual bool has_hardware_cursor() const override { return false; } - - virtual void set_cursor(size_t x, size_t y) override; - - virtual void clear(size_t x, size_t y, size_t length) override; - virtual void write(size_t x, size_t y, char ch, Color background, Color foreground, bool critical = false) override; - virtual void write(size_t x, size_t y, char ch, bool critical = false) override; - virtual void write(char ch, bool critical = false) override; - - virtual void enable() override; - virtual void disable() override; - - virtual void set_resolution(size_t width, size_t height, size_t pitch) = 0; - -protected: - virtual void hide_cursor() override; - virtual void show_cursor() override; - - virtual void scroll_up() override; - - GenericFramebufferConsoleImpl(size_t width, size_t height, size_t pitch) - : Console(width, height) - , m_pitch(pitch) - { - m_cursor_overriden_pixels.fill(0); - } - virtual u8* framebuffer_data() = 0; - size_t framebuffer_pitch() const { return m_pitch; } - virtual void clear_glyph(size_t x, size_t y); - - union FramebufferOffset { - u8* bytes; - u32* pixels; - }; - FramebufferOffset framebuffer_offset(size_t x, size_t y); - void flush_glyph(size_t x, size_t y); - - size_t const m_glyph_spacing { 1 }; - size_t const m_glyph_columns { 8 }; - size_t const m_glyph_rows { 16 }; - - Array m_cursor_overriden_pixels; - - size_t m_pitch; -}; - -class GenericFramebufferConsole : public GenericFramebufferConsoleImpl { -public: - virtual void clear(size_t x, size_t y, size_t length) override; - virtual void write(size_t x, size_t y, char ch, Color background, Color foreground, bool critical = false) override; - - virtual void enable() override; - virtual void disable() override; - -protected: - GenericFramebufferConsole(size_t width, size_t height, size_t pitch) - : GenericFramebufferConsoleImpl(width, height, pitch) - { - } - - virtual void clear_glyph(size_t x, size_t y) override; - - mutable Spinlock m_lock {}; -}; - -} diff --git a/Kernel/Devices/GPU/Definitions.h b/Kernel/Devices/GPU/Definitions.h deleted file mode 100644 index 343a5125ed0..00000000000 --- a/Kernel/Devices/GPU/Definitions.h +++ /dev/null @@ -1,16 +0,0 @@ -/* - * Copyright (c) 2021-2022, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include - -namespace Kernel::Graphics { - -// Note: Address 0x50 is expected to be the DDC2 (EDID) i2c address. -static constexpr u8 ddc2_i2c_address = 0x50; - -} diff --git a/Kernel/Devices/GPU/DisplayConnector.cpp b/Kernel/Devices/GPU/DisplayConnector.cpp deleted file mode 100644 index 4da885db441..00000000000 --- a/Kernel/Devices/GPU/DisplayConnector.cpp +++ /dev/null @@ -1,523 +0,0 @@ -/* - * Copyright (c) 2022, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include -#include -#include - -namespace Kernel { - -DisplayConnector::DisplayConnector(PhysicalAddress framebuffer_address, size_t framebuffer_resource_size, bool enable_write_combine_optimization) - : CharacterDevice(226, GraphicsManagement::the().allocate_minor_device_number()) - , m_enable_write_combine_optimization(enable_write_combine_optimization) - , m_framebuffer_at_arbitrary_physical_range(false) - , m_framebuffer_address(framebuffer_address) - , m_framebuffer_resource_size(framebuffer_resource_size) -{ -} - -DisplayConnector::DisplayConnector(size_t framebuffer_resource_size, bool enable_write_combine_optimization) - : CharacterDevice(226, GraphicsManagement::the().allocate_minor_device_number()) - , m_enable_write_combine_optimization(enable_write_combine_optimization) - , m_framebuffer_at_arbitrary_physical_range(true) - , m_framebuffer_address({}) - , m_framebuffer_resource_size(framebuffer_resource_size) -{ -} - -ErrorOr> DisplayConnector::vmobject_for_mmap(Process&, Memory::VirtualRange const&, u64& offset, bool) -{ - VERIFY(m_shared_framebuffer_vmobject); - if (offset != 0) - return Error::from_errno(ENOTSUP); - - return *m_shared_framebuffer_vmobject; -} - -ErrorOr DisplayConnector::read(OpenFileDescription&, u64, UserOrKernelBuffer&, size_t) -{ - return Error::from_errno(ENOTIMPL); -} - -ErrorOr DisplayConnector::write(OpenFileDescription&, u64, UserOrKernelBuffer const&, size_t) -{ - return Error::from_errno(ENOTIMPL); -} - -void DisplayConnector::will_be_destroyed() -{ - GraphicsManagement::the().detach_display_connector({}, *this); - - // NOTE: We check if m_symlink_sysfs_component is not null, because if we failed - // at some point in DisplayConnector::after_inserting(), then that method will tear down - // the object internal members safely, so we don't want to do it again here. - if (m_symlink_sysfs_component) { - before_will_be_destroyed_remove_symlink_from_device_identifier_directory(); - m_symlink_sysfs_component.clear(); - } - - // NOTE: We check if m_sysfs_device_directory is not null, because if we failed - // at some point in DisplayConnector::after_inserting(), then that method will tear down - // the object internal members safely, so we don't want to do it again here. - if (m_sysfs_device_directory) { - SysFSDisplayConnectorsDirectory::the().unplug({}, *m_sysfs_device_directory); - m_sysfs_device_directory.clear(); - } - - before_will_be_destroyed_remove_from_device_management(); -} - -ErrorOr DisplayConnector::allocate_framebuffer_resources(size_t rounded_size) -{ - VERIFY((rounded_size % PAGE_SIZE) == 0); - if (!m_framebuffer_at_arbitrary_physical_range) { - VERIFY(m_framebuffer_address.value().page_base() == m_framebuffer_address.value()); - m_shared_framebuffer_vmobject = TRY(Memory::SharedFramebufferVMObject::try_create_for_physical_range(m_framebuffer_address.value(), rounded_size)); - m_framebuffer_region = TRY(MM.allocate_mmio_kernel_region(m_framebuffer_address.value().page_base(), rounded_size, "Framebuffer"sv, Memory::Region::Access::ReadWrite)); - } else { - m_shared_framebuffer_vmobject = TRY(Memory::SharedFramebufferVMObject::try_create_at_arbitrary_physical_range(rounded_size)); - m_framebuffer_region = TRY(MM.allocate_kernel_region_with_vmobject(m_shared_framebuffer_vmobject->real_writes_framebuffer_vmobject(), rounded_size, "Framebuffer"sv, Memory::Region::Access::ReadWrite)); - } - - m_framebuffer_data = m_framebuffer_region->vaddr().as_ptr(); - m_fake_writes_framebuffer_region = TRY(MM.allocate_kernel_region_with_vmobject(m_shared_framebuffer_vmobject->fake_writes_framebuffer_vmobject(), rounded_size, "Fake Writes Framebuffer"sv, Memory::Region::Access::ReadWrite)); - return {}; -} - -ErrorOr DisplayConnector::after_inserting() -{ - ArmedScopeGuard clean_from_device_management([&] { - before_will_be_destroyed_remove_from_device_management(); - }); - - auto sysfs_display_connector_device_directory = DisplayConnectorSysFSDirectory::create(SysFSDisplayConnectorsDirectory::the(), *this); - m_sysfs_device_directory = sysfs_display_connector_device_directory; - SysFSDisplayConnectorsDirectory::the().plug({}, *sysfs_display_connector_device_directory); - ArmedScopeGuard clean_from_sysfs_display_connector_device_directory([&] { - SysFSDisplayConnectorsDirectory::the().unplug({}, *m_sysfs_device_directory); - m_sysfs_device_directory.clear(); - }); - - VERIFY(!m_symlink_sysfs_component); - auto sys_fs_component = TRY(SysFSSymbolicLinkDeviceComponent::try_create(SysFSCharacterDevicesDirectory::the(), *this, *m_sysfs_device_directory)); - m_symlink_sysfs_component = sys_fs_component; - after_inserting_add_symlink_to_device_identifier_directory(); - - ArmedScopeGuard clean_symlink_to_device_identifier_directory([&] { - VERIFY(m_symlink_sysfs_component); - before_will_be_destroyed_remove_symlink_from_device_identifier_directory(); - m_symlink_sysfs_component.clear(); - }); - - if (auto result_or_error = Memory::page_round_up(m_framebuffer_resource_size); result_or_error.is_error()) { - // NOTE: The amount of framebuffer resource being specified is erroneous, then default to 16 MiB. - TRY(allocate_framebuffer_resources(16 * MiB)); - m_framebuffer_resource_size = 16 * MiB; - } else { - if (auto allocation_result = allocate_framebuffer_resources(result_or_error.release_value()); allocation_result.is_error()) { - // NOTE: The amount of framebuffer resource being specified is too big, use 16 MiB just to get going. - TRY(allocate_framebuffer_resources(16 * MiB)); - m_framebuffer_resource_size = 16 * MiB; - } - } - - clean_from_device_management.disarm(); - clean_from_sysfs_display_connector_device_directory.disarm(); - clean_symlink_to_device_identifier_directory.disarm(); - - GraphicsManagement::the().attach_new_display_connector({}, *this); - if (m_enable_write_combine_optimization) { - [[maybe_unused]] auto result = m_framebuffer_region->set_write_combine(true); - } - after_inserting_add_to_device_management(); - return {}; -} - -bool DisplayConnector::console_mode() const -{ - VERIFY(m_control_lock.is_locked()); - return m_console_mode; -} - -void DisplayConnector::set_display_mode(Badge, DisplayMode mode) -{ - SpinlockLocker locker(m_control_lock); - - { - SpinlockLocker locker(m_modeset_lock); - [[maybe_unused]] auto result = set_y_offset(0); - } - - m_console_mode = mode == DisplayMode::Console ? true : false; - if (m_console_mode) { - VERIFY(m_framebuffer_region->size() == m_fake_writes_framebuffer_region->size()); - memcpy(m_fake_writes_framebuffer_region->vaddr().as_ptr(), m_framebuffer_region->vaddr().as_ptr(), m_framebuffer_region->size()); - m_shared_framebuffer_vmobject->switch_to_fake_sink_framebuffer_writes({}); - enable_console(); - } else { - disable_console(); - m_shared_framebuffer_vmobject->switch_to_real_framebuffer_writes({}); - VERIFY(m_framebuffer_region->size() == m_fake_writes_framebuffer_region->size()); - memcpy(m_framebuffer_region->vaddr().as_ptr(), m_fake_writes_framebuffer_region->vaddr().as_ptr(), m_framebuffer_region->size()); - } -} - -ErrorOr DisplayConnector::initialize_edid_for_generic_monitor(Optional> possible_manufacturer_id_string) -{ - u8 raw_manufacturer_id[2] = { 0x0, 0x0 }; - if (possible_manufacturer_id_string.has_value()) { - Array manufacturer_id_string = possible_manufacturer_id_string.release_value(); - u8 byte1 = (((static_cast(manufacturer_id_string[0]) - '@') & 0x1f) << 2) | (((static_cast(manufacturer_id_string[1]) - '@') >> 3) & 3); - u8 byte2 = ((static_cast(manufacturer_id_string[2]) - '@') & 0x1f) | (((static_cast(manufacturer_id_string[1]) - '@') << 5) & 0xe0); - Array manufacturer_id_string_packed_bytes = { byte1, byte2 }; - raw_manufacturer_id[0] = manufacturer_id_string_packed_bytes[1]; - raw_manufacturer_id[1] = manufacturer_id_string_packed_bytes[0]; - } - - Array virtual_monitor_edid = { - 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, /* header */ - raw_manufacturer_id[1], raw_manufacturer_id[0], /* manufacturer */ - 0x00, 0x00, /* product code */ - 0x00, 0x00, 0x00, 0x00, /* serial number goes here */ - 0x01, /* week of manufacture */ - 0x00, /* year of manufacture */ - 0x01, 0x03, /* EDID version */ - 0x80, /* capabilities - digital */ - 0x00, /* horiz. res in cm, zero for projectors */ - 0x00, /* vert. res in cm */ - 0x78, /* display gamma (120 == 2.2). */ - 0xEE, /* features (standby, suspend, off, RGB, std */ - /* colour space, preferred timing mode) */ - 0xEE, 0x91, 0xA3, 0x54, 0x4C, 0x99, 0x26, 0x0F, 0x50, 0x54, - /* chromaticity for standard colour space. */ - 0x21, 0x08, 0x00, /* default timings: 640x480@60, 800x600@60, 1024x768@60 */ - 0xd1, 0xc0, /* standard timing 1920x1080 @ 60 Hz */ - 0xb3, 0x00, /* standard timing 1680x1050 @ 60 Hz */ - 0xa9, 0xc0, /* standard timing 1600x900 @ 60 Hz */ - 0x95, 0x00, /* standard timing 1440x900 @ 60 Hz */ - 0x8b, 0xc0, /* standard timing 1360x768 @ 60 Hz */ - 0x81, 0x80, /* standard timing 1280x1024 @ 60 Hz */ - 0x81, 0x40, /* standard timing 1280x960 @ 60 Hz */ - 0x81, 0xc0, /* standard timing 1280x720 @ 60 Hz */ - 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x06, 0x00, 0x02, 0x02, - 0x02, 0x02, - /* descriptor block 1 goes below */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - /* descriptor block 2, monitor ranges */ - 0x00, 0x00, 0x00, 0xFD, 0x00, - 0x00, 0xC8, 0x00, 0xC8, 0x64, 0x00, 0x0A, 0x20, 0x20, 0x20, - 0x20, 0x20, - /* 0-200Hz vertical, 0-200KHz horizontal, 1000MHz pixel clock */ - 0x20, - /* descriptor block 3, monitor name */ - 0x00, 0x00, 0x00, 0xFC, 0x00, - 'G', 'e', 'n', 'e', 'r', 'i', 'c', 'S', 'c', 'r', 'e', 'e', 'n', - /* descriptor block 4: dummy data */ - 0x00, 0x00, 0x00, 0x10, 0x00, - 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, - 0x00, /* number of extensions */ - 0x00 /* checksum goes here */ - }; - // Note: Fix checksum to avoid warnings about checksum mismatch. - size_t checksum = 0; - // Note: Read all 127 bytes to add them to the checksum. Byte 128 is zeroed so - // we could technically add it to the sum result, but it could lead to an error if it contained - // a non-zero value, so we are not using it. - for (size_t index = 0; index < sizeof(virtual_monitor_edid) - 1; index++) - checksum += virtual_monitor_edid[index]; - virtual_monitor_edid[127] = 0x100 - checksum; - set_edid_bytes(virtual_monitor_edid); - return {}; -} - -void DisplayConnector::set_edid_bytes(Array const& edid_bytes, bool might_be_invalid) -{ - memcpy((u8*)m_edid_bytes, edid_bytes.data(), sizeof(m_edid_bytes)); - if (auto parsed_edid = EDID::Parser::from_bytes({ m_edid_bytes, sizeof(m_edid_bytes) }); !parsed_edid.is_error()) { - m_edid_parser = parsed_edid.release_value(); - m_edid_valid = true; - } else { - if (!might_be_invalid) { - dmesgln("DisplayConnector: Print offending EDID"); - for (size_t x = 0; x < 128; x = x + 16) { - dmesgln("{:02x} {:02x} {:02x} {:02x} {:02x} {:02x} {:02x} {:02x} {:02x} {:02x} {:02x} {:02x} {:02x} {:02x} {:02x} {:02x}", - m_edid_bytes[x], m_edid_bytes[x + 1], m_edid_bytes[x + 2], m_edid_bytes[x + 3], - m_edid_bytes[x + 4], m_edid_bytes[x + 5], m_edid_bytes[x + 6], m_edid_bytes[x + 7], - m_edid_bytes[x + 8], m_edid_bytes[x + 9], m_edid_bytes[x + 10], m_edid_bytes[x + 11], - m_edid_bytes[x + 12], m_edid_bytes[x + 13], m_edid_bytes[x + 14], m_edid_bytes[x + 15]); - } - dmesgln("DisplayConnector: Parsing EDID failed: {}", parsed_edid.error()); - } - } -} - -ErrorOr DisplayConnector::flush_rectangle(size_t, FBRect const&) -{ - return Error::from_errno(ENOTSUP); -} - -DisplayConnector::ModeSetting DisplayConnector::current_mode_setting() const -{ - SpinlockLocker locker(m_modeset_lock); - return m_current_mode_setting; -} - -ErrorOr DisplayConnector::get_edid() const -{ - if (!m_edid_valid) - return Error::from_errno(ENODEV); - return ByteBuffer::copy(m_edid_bytes, sizeof(m_edid_bytes)); -} - -struct GraphicsIOCtlChecker { - unsigned ioctl_number; - StringView name; - bool requires_ownership { false }; -}; - -static constexpr GraphicsIOCtlChecker s_checkers[] = { - { GRAPHICS_IOCTL_GET_PROPERTIES, "GRAPHICS_IOCTL_GET_PROPERTIES"sv, false }, - { GRAPHICS_IOCTL_SET_HEAD_VERTICAL_OFFSET_BUFFER, "GRAPHICS_IOCTL_SET_HEAD_VERTICAL_OFFSET_BUFFER"sv, true }, - { GRAPHICS_IOCTL_GET_HEAD_VERTICAL_OFFSET_BUFFER, "GRAPHICS_IOCTL_GET_HEAD_VERTICAL_OFFSET_BUFFER"sv, false }, - { GRAPHICS_IOCTL_FLUSH_HEAD_BUFFERS, "GRAPHICS_IOCTL_FLUSH_HEAD_BUFFERS"sv, true }, - { GRAPHICS_IOCTL_FLUSH_HEAD, "GRAPHICS_IOCTL_FLUSH_HEAD"sv, true }, - { GRAPHICS_IOCTL_SET_HEAD_MODE_SETTING, "GRAPHICS_IOCTL_SET_HEAD_MODE_SETTING"sv, true }, - { GRAPHICS_IOCTL_GET_HEAD_MODE_SETTING, "GRAPHICS_IOCTL_GET_HEAD_MODE_SETTING"sv, false }, - { GRAPHICS_IOCTL_SET_SAFE_HEAD_MODE_SETTING, "GRAPHICS_IOCTL_SET_SAFE_HEAD_MODE_SETTING"sv, true }, - { GRAPHICS_IOCTL_SET_RESPONSIBLE, "GRAPHICS_IOCTL_SET_RESPONSIBLE"sv, false }, - { GRAPHICS_IOCTL_UNSET_RESPONSIBLE, "GRAPHICS_IOCTL_UNSET_RESPONSIBLE"sv, true }, -}; - -static StringView ioctl_to_stringview(unsigned request) -{ - for (auto& checker : s_checkers) { - if (checker.ioctl_number == request) - return checker.name; - } - return "unknown"sv; -} - -ErrorOr DisplayConnector::ioctl_requires_ownership(unsigned request) const -{ - for (auto& checker : s_checkers) { - if (checker.ioctl_number == request) - return checker.requires_ownership; - } - // Note: In case of unknown ioctl, return EINVAL. - return Error::from_errno(EINVAL); -} - -ErrorOr DisplayConnector::ioctl(OpenFileDescription&, unsigned request, Userspace arg) -{ - TRY(Process::current().require_promise(Pledge::video)); - - // Note: We only allow to set responsibility on a DisplayConnector, - // get the current ModeSetting or the hardware framebuffer properties without the - // need of having an established responsibility on a DisplayConnector. - auto needs_ownership = TRY(ioctl_requires_ownership(request)); - if (needs_ownership) { - auto process = m_responsible_process.strong_ref(); - if (!process || process.ptr() != &Process::current()) { - dbgln("DisplayConnector::ioctl: {} requires ownership over the device", ioctl_to_stringview(request)); - return Error::from_errno(EPERM); - } - } - - switch (request) { - case GRAPHICS_IOCTL_SET_RESPONSIBLE: { - SpinlockLocker locker(m_responsible_process_lock); - auto process = m_responsible_process.strong_ref(); - // Note: If there's already a process being responsible, just return an error. - // We could technically return 0 if the requesting process was already - // set to be responsible for this DisplayConnector, but it services - // no good purpose and should be considered a bug if this happens anyway. - if (process) - return Error::from_errno(EPERM); - m_responsible_process = Process::current(); - return {}; - } - case GRAPHICS_IOCTL_UNSET_RESPONSIBLE: { - SpinlockLocker locker(m_responsible_process_lock); - auto process = m_responsible_process.strong_ref(); - if (!process) - return Error::from_errno(ESRCH); - if (process.ptr() != &Process::current()) - return Error::from_errno(EPERM); - m_responsible_process.clear(); - return {}; - } - case GRAPHICS_IOCTL_GET_PROPERTIES: { - VERIFY(m_shared_framebuffer_vmobject); - auto user_properties = static_ptr_cast(arg); - GraphicsConnectorProperties properties {}; - properties.flushing_support = flush_support(); - properties.doublebuffer_support = double_framebuffering_capable(); - properties.partial_flushing_support = partial_flush_support(); - properties.refresh_rate_support = refresh_rate_support(); - properties.max_buffer_bytes = m_shared_framebuffer_vmobject->size(); - - return copy_to_user(user_properties, &properties); - } - case GRAPHICS_IOCTL_GET_HEAD_MODE_SETTING: { - auto user_head_mode_setting = static_ptr_cast(arg); - GraphicsHeadModeSetting head_mode_setting {}; - TRY(copy_from_user(&head_mode_setting, user_head_mode_setting)); - { - SpinlockLocker control_locker(m_control_lock); - head_mode_setting.horizontal_stride = m_current_mode_setting.horizontal_stride; - head_mode_setting.pixel_clock_in_khz = m_current_mode_setting.pixel_clock_in_khz; - head_mode_setting.horizontal_active = m_current_mode_setting.horizontal_active; - head_mode_setting.horizontal_front_porch_pixels = m_current_mode_setting.horizontal_front_porch_pixels; - head_mode_setting.horizontal_sync_time_pixels = m_current_mode_setting.horizontal_sync_time_pixels; - head_mode_setting.horizontal_blank_pixels = m_current_mode_setting.horizontal_blank_pixels; - head_mode_setting.vertical_active = m_current_mode_setting.vertical_active; - head_mode_setting.vertical_front_porch_lines = m_current_mode_setting.vertical_front_porch_lines; - head_mode_setting.vertical_sync_time_lines = m_current_mode_setting.vertical_sync_time_lines; - head_mode_setting.vertical_blank_lines = m_current_mode_setting.vertical_blank_lines; - head_mode_setting.horizontal_offset = m_current_mode_setting.horizontal_offset; - head_mode_setting.vertical_offset = m_current_mode_setting.vertical_offset; - } - return copy_to_user(user_head_mode_setting, &head_mode_setting); - } - case GRAPHICS_IOCTL_SET_HEAD_MODE_SETTING: { - auto user_mode_setting = static_ptr_cast(arg); - auto head_mode_setting = TRY(copy_typed_from_user(user_mode_setting)); - - if (head_mode_setting.horizontal_stride < 0) - return Error::from_errno(EINVAL); - if (head_mode_setting.pixel_clock_in_khz < 0) - return Error::from_errno(EINVAL); - if (head_mode_setting.horizontal_active < 0) - return Error::from_errno(EINVAL); - if (head_mode_setting.horizontal_front_porch_pixels < 0) - return Error::from_errno(EINVAL); - if (head_mode_setting.horizontal_sync_time_pixels < 0) - return Error::from_errno(EINVAL); - if (head_mode_setting.horizontal_blank_pixels < 0) - return Error::from_errno(EINVAL); - if (head_mode_setting.vertical_active < 0) - return Error::from_errno(EINVAL); - if (head_mode_setting.vertical_front_porch_lines < 0) - return Error::from_errno(EINVAL); - if (head_mode_setting.vertical_sync_time_lines < 0) - return Error::from_errno(EINVAL); - if (head_mode_setting.vertical_blank_lines < 0) - return Error::from_errno(EINVAL); - if (head_mode_setting.horizontal_offset < 0) - return Error::from_errno(EINVAL); - if (head_mode_setting.vertical_offset < 0) - return Error::from_errno(EINVAL); - { - SpinlockLocker control_locker(m_control_lock); - ModeSetting requested_mode_setting; - requested_mode_setting.horizontal_stride = 0; - requested_mode_setting.pixel_clock_in_khz = head_mode_setting.pixel_clock_in_khz; - requested_mode_setting.horizontal_active = head_mode_setting.horizontal_active; - requested_mode_setting.horizontal_front_porch_pixels = head_mode_setting.horizontal_front_porch_pixels; - requested_mode_setting.horizontal_sync_time_pixels = head_mode_setting.horizontal_sync_time_pixels; - requested_mode_setting.horizontal_blank_pixels = head_mode_setting.horizontal_blank_pixels; - requested_mode_setting.vertical_active = head_mode_setting.vertical_active; - requested_mode_setting.vertical_front_porch_lines = head_mode_setting.vertical_front_porch_lines; - requested_mode_setting.vertical_sync_time_lines = head_mode_setting.vertical_sync_time_lines; - requested_mode_setting.vertical_blank_lines = head_mode_setting.vertical_blank_lines; - requested_mode_setting.horizontal_offset = head_mode_setting.horizontal_offset; - requested_mode_setting.vertical_offset = head_mode_setting.vertical_offset; - - TRY(set_mode_setting(requested_mode_setting)); - } - return {}; - } - - case GRAPHICS_IOCTL_SET_SAFE_HEAD_MODE_SETTING: { - SpinlockLocker control_locker(m_control_lock); - TRY(set_safe_mode_setting()); - return {}; - } - - case GRAPHICS_IOCTL_SET_HEAD_VERTICAL_OFFSET_BUFFER: { - // FIXME: We silently ignore the request if we are in console mode. - // WindowServer is not ready yet to handle errors such as EBUSY currently. - SpinlockLocker control_locker(m_control_lock); - if (console_mode()) { - return {}; - } - - auto user_head_vertical_buffer_offset = static_ptr_cast(arg); - auto head_vertical_buffer_offset = TRY(copy_typed_from_user(user_head_vertical_buffer_offset)); - - SpinlockLocker locker(m_modeset_lock); - - if (head_vertical_buffer_offset.offsetted < 0 || head_vertical_buffer_offset.offsetted > 1) - return Error::from_errno(EINVAL); - TRY(set_y_offset(head_vertical_buffer_offset.offsetted == 0 ? 0 : m_current_mode_setting.vertical_active)); - if (head_vertical_buffer_offset.offsetted == 0) - m_vertical_offsetted = false; - else - m_vertical_offsetted = true; - return {}; - } - case GRAPHICS_IOCTL_GET_HEAD_VERTICAL_OFFSET_BUFFER: { - auto user_head_vertical_buffer_offset = static_ptr_cast(arg); - GraphicsHeadVerticalOffset head_vertical_buffer_offset {}; - TRY(copy_from_user(&head_vertical_buffer_offset, user_head_vertical_buffer_offset)); - - head_vertical_buffer_offset.offsetted = m_vertical_offsetted; - return copy_to_user(user_head_vertical_buffer_offset, &head_vertical_buffer_offset); - } - case GRAPHICS_IOCTL_FLUSH_HEAD_BUFFERS: { - if (console_mode()) - return {}; - if (!partial_flush_support()) - return Error::from_errno(ENOTSUP); - MutexLocker locker(m_flushing_lock); - auto user_flush_rects = static_ptr_cast(arg); - auto flush_rects = TRY(copy_typed_from_user(user_flush_rects)); - if (Checked::multiplication_would_overflow(flush_rects.count, sizeof(FBRect))) - return Error::from_errno(EFAULT); - if (flush_rects.count > 0) { - for (unsigned i = 0; i < flush_rects.count; i++) { - FBRect user_dirty_rect; - TRY(copy_from_user(&user_dirty_rect, &flush_rects.rects[i])); - { - SpinlockLocker control_locker(m_control_lock); - if (console_mode()) { - return {}; - } - TRY(flush_rectangle(flush_rects.buffer_index, user_dirty_rect)); - } - } - } - return {}; - }; - case GRAPHICS_IOCTL_FLUSH_HEAD: { - // FIXME: We silently ignore the request if we are in console mode. - // WindowServer is not ready yet to handle errors such as EBUSY currently. - MutexLocker locker(m_flushing_lock); - SpinlockLocker control_locker(m_control_lock); - if (console_mode()) { - return {}; - } - - if (!flush_support()) - return Error::from_errno(ENOTSUP); - - TRY(flush_first_surface()); - return {}; - } - } - // Note: We already verify that the IOCTL is supported and not unknown in - // the call to the ioctl_requires_ownership method, so if we reached this - // section of the code, this is bug. - VERIFY_NOT_REACHED(); -} - -} diff --git a/Kernel/Devices/GPU/DisplayConnector.h b/Kernel/Devices/GPU/DisplayConnector.h deleted file mode 100644 index 4e9f9634ddd..00000000000 --- a/Kernel/Devices/GPU/DisplayConnector.h +++ /dev/null @@ -1,174 +0,0 @@ -/* - * Copyright (c) 2022, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include - -namespace Kernel { - -class GraphicsManagement; -class DisplayConnector : public CharacterDevice { - friend class GraphicsManagement; - friend class DeviceManagement; - -public: - struct ModeSetting { - size_t horizontal_blanking_start() const - { - return horizontal_active; - } - size_t horizontal_sync_start() const - { - return horizontal_active + horizontal_front_porch_pixels; - } - size_t horizontal_sync_end() const - { - return horizontal_active + horizontal_front_porch_pixels + horizontal_sync_time_pixels; - } - size_t horizontal_total() const - { - return horizontal_active + horizontal_blank_pixels; - } - - size_t vertical_blanking_start() const - { - return vertical_active; - } - size_t vertical_sync_start() const - { - return vertical_active + vertical_front_porch_lines; - } - size_t vertical_sync_end() const - { - return vertical_active + vertical_front_porch_lines + vertical_sync_time_lines; - } - size_t vertical_total() const - { - return vertical_active + vertical_blank_lines; - } - - size_t horizontal_stride; // Note: This is commonly known as "pitch" - size_t pixel_clock_in_khz; - - size_t horizontal_active; - size_t horizontal_front_porch_pixels; - size_t horizontal_sync_time_pixels; - size_t horizontal_blank_pixels; - - size_t vertical_active; - size_t vertical_front_porch_lines; - size_t vertical_sync_time_lines; - size_t vertical_blank_lines; - - size_t horizontal_offset; // Note: This is commonly known as "x offset" - size_t vertical_offset; // Note: This is commonly known as "y offset" - }; - -public: - enum class DisplayMode { - Graphical, - Console, - }; - -public: - virtual ~DisplayConnector() = default; - - virtual bool mutable_mode_setting_capable() const = 0; - virtual bool double_framebuffering_capable() const = 0; - virtual bool flush_support() const = 0; - virtual bool partial_flush_support() const = 0; - // Note: This can indicate to userland if the underlying hardware requires - // a defined refresh rate being supplied when modesetting the screen resolution. - // Paravirtualized hardware don't need such setting and can safely ignore this. - virtual bool refresh_rate_support() const = 0; - - bool console_mode() const; - ErrorOr get_edid() const; - virtual ErrorOr set_mode_setting(ModeSetting const&) = 0; - virtual ErrorOr set_safe_mode_setting() = 0; - ModeSetting current_mode_setting() const; - virtual ErrorOr set_y_offset(size_t y) = 0; - virtual ErrorOr unblank() = 0; - - void set_display_mode(Badge, DisplayMode); - - Memory::Region const& framebuffer_region() const { return *m_framebuffer_region; } - -protected: - void set_edid_bytes(Array const& edid_bytes, bool might_be_invalid = false); - - DisplayConnector(PhysicalAddress framebuffer_address, size_t framebuffer_resource_size, bool enable_write_combine_optimization); - DisplayConnector(size_t framebuffer_resource_size, bool enable_write_combine_optimization); - virtual void enable_console() = 0; - virtual void disable_console() = 0; - virtual ErrorOr flush_first_surface() = 0; - virtual ErrorOr flush_rectangle(size_t buffer_index, FBRect const& rect); - - ErrorOr initialize_edid_for_generic_monitor(Optional> manufacturer_id_string); - - mutable Spinlock m_control_lock {}; - mutable Mutex m_flushing_lock; - - bool m_console_mode { false }; - - bool m_vertical_offsetted { false }; - - mutable Spinlock m_modeset_lock {}; - ModeSetting m_current_mode_setting {}; - - Optional m_edid_parser; - EDID::Parser::RawBytes m_edid_bytes {}; - bool m_edid_valid { false }; - - u8* framebuffer_data() { return m_framebuffer_data; } - -private: - // ^File - virtual bool is_seekable() const override { return true; } - virtual bool can_read(OpenFileDescription const&, u64) const final override { return true; } - virtual bool can_write(OpenFileDescription const&, u64) const final override { return true; } - virtual ErrorOr read(OpenFileDescription&, u64, UserOrKernelBuffer&, size_t) override final; - virtual ErrorOr write(OpenFileDescription&, u64, UserOrKernelBuffer const&, size_t) override final; - virtual ErrorOr> vmobject_for_mmap(Process&, Memory::VirtualRange const&, u64&, bool) override final; - virtual ErrorOr ioctl(OpenFileDescription&, unsigned request, Userspace arg) override final; - virtual StringView class_name() const override final { return "DisplayConnector"sv; } - - DisplayConnector& operator=(DisplayConnector const&) = delete; - DisplayConnector& operator=(DisplayConnector&&) = delete; - DisplayConnector(DisplayConnector&&) = delete; - - virtual void will_be_destroyed() override; - virtual ErrorOr after_inserting() override; - - ErrorOr allocate_framebuffer_resources(size_t rounded_size); - - ErrorOr ioctl_requires_ownership(unsigned request) const; - - OwnPtr m_framebuffer_region; - OwnPtr m_fake_writes_framebuffer_region; - u8* m_framebuffer_data {}; - - bool const m_enable_write_combine_optimization { false }; - bool const m_framebuffer_at_arbitrary_physical_range { false }; - -protected: - Optional const m_framebuffer_address; - size_t m_framebuffer_resource_size; - -private: - LockRefPtr m_shared_framebuffer_vmobject; - - LockWeakPtr m_responsible_process; - Spinlock m_responsible_process_lock {}; - - IntrusiveListNode> m_list_node; -}; -} diff --git a/Kernel/Devices/GPU/GPUDevice.h b/Kernel/Devices/GPU/GPUDevice.h deleted file mode 100644 index 32e12c3ae35..00000000000 --- a/Kernel/Devices/GPU/GPUDevice.h +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright (c) 2021, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include - -namespace Kernel { -class GPUDevice - : public AtomicRefCounted - , public LockWeakable { -public: - virtual ~GPUDevice() = default; - -protected: - GPUDevice() = default; -}; - -} diff --git a/Kernel/Devices/GPU/Generic/DisplayConnector.cpp b/Kernel/Devices/GPU/Generic/DisplayConnector.cpp deleted file mode 100644 index 90d4de9ef2d..00000000000 --- a/Kernel/Devices/GPU/Generic/DisplayConnector.cpp +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright (c) 2022, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include - -namespace Kernel { - -NonnullLockRefPtr GenericDisplayConnector::must_create_with_preset_resolution(PhysicalAddress framebuffer_address, size_t width, size_t height, size_t pitch) -{ - auto device_or_error = DeviceManagement::try_create_device(framebuffer_address, width, height, pitch); - VERIFY(!device_or_error.is_error()); - auto connector = device_or_error.release_value(); - MUST(connector->create_attached_framebuffer_console()); - MUST(connector->initialize_edid_for_generic_monitor({})); - return connector; -} - -GenericDisplayConnector::GenericDisplayConnector(PhysicalAddress framebuffer_address, size_t width, size_t height, size_t pitch) - : DisplayConnector(framebuffer_address, height * pitch, true) -{ - m_current_mode_setting.horizontal_active = width; - m_current_mode_setting.vertical_active = height; - m_current_mode_setting.horizontal_stride = pitch; -} - -ErrorOr GenericDisplayConnector::create_attached_framebuffer_console() -{ - auto width = m_current_mode_setting.horizontal_active; - auto height = m_current_mode_setting.vertical_active; - auto pitch = m_current_mode_setting.horizontal_stride; - - m_framebuffer_console = Graphics::ContiguousFramebufferConsole::initialize(m_framebuffer_address.value(), width, height, pitch); - GraphicsManagement::the().set_console(*m_framebuffer_console); - return {}; -} - -void GenericDisplayConnector::enable_console() -{ - VERIFY(m_control_lock.is_locked()); - VERIFY(m_framebuffer_console); - m_framebuffer_console->enable(); -} - -void GenericDisplayConnector::disable_console() -{ - VERIFY(m_control_lock.is_locked()); - VERIFY(m_framebuffer_console); - m_framebuffer_console->disable(); -} - -ErrorOr GenericDisplayConnector::flush_first_surface() -{ - return Error::from_errno(ENOTSUP); -} - -} diff --git a/Kernel/Devices/GPU/Generic/DisplayConnector.h b/Kernel/Devices/GPU/Generic/DisplayConnector.h deleted file mode 100644 index 438398b77c8..00000000000 --- a/Kernel/Devices/GPU/Generic/DisplayConnector.h +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright (c) 2022, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include -#include - -namespace Kernel { - -class GenericDisplayConnector - : public DisplayConnector { - friend class DeviceManagement; - -public: - static NonnullLockRefPtr must_create_with_preset_resolution(PhysicalAddress framebuffer_address, size_t width, size_t height, size_t pitch); - -protected: - ErrorOr create_attached_framebuffer_console(); - - GenericDisplayConnector(PhysicalAddress framebuffer_address, size_t width, size_t height, size_t pitch); - - virtual bool mutable_mode_setting_capable() const override final { return false; } - virtual bool double_framebuffering_capable() const override { return false; } - virtual ErrorOr set_mode_setting(ModeSetting const&) override { return Error::from_errno(ENOTSUP); } - virtual ErrorOr set_safe_mode_setting() override { return {}; } - virtual ErrorOr set_y_offset(size_t) override { return Error::from_errno(ENOTSUP); } - virtual ErrorOr unblank() override { return Error::from_errno(ENOTSUP); } - - virtual bool partial_flush_support() const override final { return false; } - virtual bool flush_support() const override final { return false; } - // Note: This is "possibly" a paravirtualized hardware, but since we don't know, we assume there's no refresh rate... - // We rely on the BIOS and/or the bootloader to initialize the hardware for us, so we don't really care about - // the specific implementation and settings that were chosen with the given hardware as long as we just - // have a dummy framebuffer to work with. - virtual bool refresh_rate_support() const override final { return false; } - - virtual ErrorOr flush_first_surface() override final; - - virtual void enable_console() override final; - virtual void disable_console() override final; - - LockRefPtr m_framebuffer_console; -}; -} diff --git a/Kernel/Devices/GPU/Intel/Auxiliary/GMBusConnector.cpp b/Kernel/Devices/GPU/Intel/Auxiliary/GMBusConnector.cpp deleted file mode 100644 index a7f7ff1b883..00000000000 --- a/Kernel/Devices/GPU/Intel/Auxiliary/GMBusConnector.cpp +++ /dev/null @@ -1,123 +0,0 @@ -/* - * Copyright (c) 2022, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include - -namespace Kernel { - -enum class GMBusStatus { - TransactionCompletion, - HardwareReady -}; - -enum GMBusCycle { - Wait = 1, - Stop = 4, -}; - -ErrorOr> GMBusConnector::create_with_physical_address(PhysicalAddress gmbus_start_address) -{ - auto registers_mapping = TRY(map_typed(gmbus_start_address, sizeof(GMBusRegisters), Memory::Region::Access::ReadWrite)); - return adopt_nonnull_own_or_enomem(new (nothrow) GMBusConnector(move(registers_mapping))); -} - -GMBusConnector::GMBusConnector(Memory::TypedMapping registers_mapping) - : m_gmbus_registers(move(registers_mapping)) -{ - set_default_rate(); - set_pin_pair(PinPair::DedicatedAnalog); -} - -bool GMBusConnector::wait_for(GMBusStatus desired_status, size_t milliseconds_timeout) -{ - VERIFY(m_access_lock.is_locked()); - size_t milliseconds_passed = 0; - while (1) { - if (milliseconds_timeout < milliseconds_passed) - return false; - full_memory_barrier(); - u32 status = m_gmbus_registers->status; - full_memory_barrier(); - VERIFY(!(status & (1 << 10))); // error happened - switch (desired_status) { - case GMBusStatus::HardwareReady: - if (status & (1 << 11)) - return true; - break; - case GMBusStatus::TransactionCompletion: - if (status & (1 << 14)) - return true; - break; - default: - VERIFY_NOT_REACHED(); - } - microseconds_delay(1000); - milliseconds_passed++; - } -} - -ErrorOr GMBusConnector::write(unsigned address, u32 data) -{ - VERIFY(address < 256); - SpinlockLocker locker(m_access_lock); - full_memory_barrier(); - m_gmbus_registers->data = data; - full_memory_barrier(); - m_gmbus_registers->command = ((address << 1) | (1 << 16) | (GMBusCycle::Wait << 25) | (1 << 30)); - full_memory_barrier(); - if (!wait_for(GMBusStatus::TransactionCompletion, 250)) - return Error::from_errno(EBUSY); - return {}; -} - -void GMBusConnector::set_default_rate() -{ - // FIXME: Verify GMBUS Rate Select is set only when GMBUS is idle - SpinlockLocker locker(m_access_lock); - // Set the rate to 100KHz - m_gmbus_registers->clock = m_gmbus_registers->clock & ~(0b111 << 8); -} - -void GMBusConnector::set_pin_pair(PinPair pin_pair) -{ - // FIXME: Verify GMBUS is idle - SpinlockLocker locker(m_access_lock); - m_gmbus_registers->clock = (m_gmbus_registers->clock & (~0b111)) | (pin_pair & 0b111); -} - -ErrorOr GMBusConnector::read(unsigned address, u8* buf, size_t length) -{ - VERIFY(address < 256); - SpinlockLocker locker(m_access_lock); - size_t nread = 0; - auto read_set = [&] { - full_memory_barrier(); - u32 data = m_gmbus_registers->data; - full_memory_barrier(); - for (size_t index = 0; index < 4; index++) { - if (nread == length) - break; - buf[nread] = (data >> (8 * index)) & 0xFF; - nread++; - } - }; - - full_memory_barrier(); - m_gmbus_registers->command = (1 | (address << 1) | (length << 16) | (GMBusCycle::Wait << 25) | (1 << 30)); - full_memory_barrier(); - while (nread < length) { - if (!wait_for(GMBusStatus::HardwareReady, 250)) - return Error::from_errno(EBUSY); - read_set(); - } - if (!wait_for(GMBusStatus::TransactionCompletion, 250)) - return Error::from_errno(EBUSY); - return {}; -} - -} diff --git a/Kernel/Devices/GPU/Intel/Auxiliary/GMBusConnector.h b/Kernel/Devices/GPU/Intel/Auxiliary/GMBusConnector.h deleted file mode 100644 index fbcb787355c..00000000000 --- a/Kernel/Devices/GPU/Intel/Auxiliary/GMBusConnector.h +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright (c) 2022, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include - -namespace Kernel { - -struct [[gnu::packed]] GMBusRegisters { - u32 clock; - u32 command; - u32 status; - u32 data; -}; - -enum class GMBusStatus; - -class GMBusConnector { -public: - enum PinPair : u8 { - None = 0, - DedicatedControl = 1, - DedicatedAnalog = 0b10, - IntegratedDigital = 0b11, - sDVO = 0b101, - Dconnector = 0b111, - }; - -public: - static ErrorOr> create_with_physical_address(PhysicalAddress gmbus_start_address); - - ErrorOr write(unsigned address, u32 data); - ErrorOr read(unsigned address, u8* buf, size_t length); - void set_default_rate(); - -private: - void set_pin_pair(PinPair pin_pair); - - bool wait_for(GMBusStatus desired_status, size_t milliseconds_timeout); - - explicit GMBusConnector(Memory::TypedMapping); - Spinlock m_access_lock; - Memory::TypedMapping m_gmbus_registers; -}; -} diff --git a/Kernel/Devices/GPU/Intel/Definitions.h b/Kernel/Devices/GPU/Intel/Definitions.h deleted file mode 100644 index 2e3116cb476..00000000000 --- a/Kernel/Devices/GPU/Intel/Definitions.h +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright (c) 2022, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include - -namespace Kernel::IntelGraphics { - -enum class Generation { - Gen4, - Gen9, -}; - -struct PLLSettings; - -struct PLLParameterLimit { - size_t min, max; -}; - -struct PLLMaxSettings { - PLLParameterLimit dot_clock, vco, n, m, m1, m2, p, p1, p2; -}; - -struct PLLSettings { - bool is_valid() const { return (n != 0 && m1 != 0 && m2 != 0 && p1 != 0 && p2 != 0); } - u64 compute_dot_clock(u64 refclock) const - { - return (refclock * (5 * m1 + m2) / n) / (p1 * p2); - } - - u64 compute_vco(u64 refclock) const - { - return refclock * (5 * m1 + m2) / n; - } - - u64 compute_m() const - { - return 5 * m1 + m2; - } - - u64 compute_p() const - { - return p1 * p2; - } - u64 n { 0 }; - u64 m1 { 0 }; - u64 m2 { 0 }; - u64 p1 { 0 }; - u64 p2 { 0 }; -}; -} diff --git a/Kernel/Devices/GPU/Intel/DisplayConnectorGroup.cpp b/Kernel/Devices/GPU/Intel/DisplayConnectorGroup.cpp deleted file mode 100644 index ea537ed9aa6..00000000000 --- a/Kernel/Devices/GPU/Intel/DisplayConnectorGroup.cpp +++ /dev/null @@ -1,277 +0,0 @@ -/* - * Copyright (c) 2022, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace Kernel { - -ErrorOr> IntelDisplayConnectorGroup::try_create(Badge, IntelGraphics::Generation generation, MMIORegion const& first_region, MMIORegion const& second_region) -{ - auto registers_region = TRY(MM.allocate_mmio_kernel_region(first_region.pci_bar_paddr, first_region.pci_bar_space_length, "Intel Native Graphics Registers"sv, Memory::Region::Access::ReadWrite)); - // NOTE: 0x5100 is the offset of the start of the GMBus registers - auto gmbus_connector = TRY(GMBusConnector::create_with_physical_address(first_region.pci_bar_paddr.offset(0x5100))); - auto connector_group = TRY(adopt_nonnull_lock_ref_or_enomem(new (nothrow) IntelDisplayConnectorGroup(generation, move(gmbus_connector), move(registers_region), first_region, second_region))); - TRY(connector_group->initialize_connectors()); - return connector_group; -} - -IntelDisplayConnectorGroup::IntelDisplayConnectorGroup(IntelGraphics::Generation generation, NonnullOwnPtr gmbus_connector, NonnullOwnPtr registers_region, MMIORegion const& first_region, MMIORegion const& second_region) - : m_mmio_first_region(first_region) - , m_mmio_second_region(second_region) - , m_assigned_mmio_registers_region(m_mmio_first_region) - , m_generation(generation) - , m_registers_region(move(registers_region)) - , m_gmbus_connector(move(gmbus_connector)) -{ -} - -ErrorOr IntelDisplayConnectorGroup::initialize_gen4_connectors() -{ - // NOTE: Just assume we will need one Gen4 "transcoder" - // NOTE: Main block of registers starting at HorizontalTotalA register (0x60000) - auto transcoder_registers_paddr = m_mmio_first_region.pci_bar_paddr.offset(0x60000); - // NOTE: Main block of Pipe registers starting at PipeA_DSL register (0x70000) - auto pipe_registers_paddr = m_mmio_first_region.pci_bar_paddr.offset(0x70000); - // NOTE: DPLL registers starting at DPLLDivisorA0 register (0x6040) - auto dpll_registers_paddr = m_mmio_first_region.pci_bar_paddr.offset(0x6040); - // NOTE: DPLL A control registers starting at 0x6014 (DPLL A Control register), - // DPLL A Multiplier is at 0x601C, between them (at 0x6018) there is the DPLL B Control register. - auto dpll_control_registers_paddr = m_mmio_first_region.pci_bar_paddr.offset(0x6014); - m_transcoders[0] = TRY(IntelAnalogDisplayTranscoder::create_with_physical_addresses(transcoder_registers_paddr, pipe_registers_paddr, dpll_registers_paddr, dpll_control_registers_paddr)); - m_planes[0] = TRY(IntelG33DisplayPlane::create_with_physical_address(m_mmio_first_region.pci_bar_paddr.offset(0x70180))); - Array crt_edid_bytes {}; - { - SpinlockLocker control_lock(m_control_lock); - TRY(m_gmbus_connector->write(Graphics::ddc2_i2c_address, 0)); - TRY(m_gmbus_connector->read(Graphics::ddc2_i2c_address, crt_edid_bytes.data(), crt_edid_bytes.size())); - } - m_connectors[0] = TRY(IntelNativeDisplayConnector::try_create_with_display_connector_group(*this, IntelNativeDisplayConnector::ConnectorIndex::PortA, IntelNativeDisplayConnector::Type::Analog, m_mmio_second_region.pci_bar_paddr, m_mmio_second_region.pci_bar_space_length)); - m_connectors[0]->set_edid_bytes({}, crt_edid_bytes); - return {}; -} - -ErrorOr IntelDisplayConnectorGroup::initialize_connectors() -{ - - // NOTE: Intel Graphics Generation 4 is pretty ancient beast, and we should not - // assume we can find a VBT for it. Just initialize the (assumed) CRT connector and be done with it. - if (m_generation == IntelGraphics::Generation::Gen4) { - TRY(initialize_gen4_connectors()); - } else { - VERIFY_NOT_REACHED(); - } - - for (size_t connector_index = 0; connector_index < m_connectors.size(); connector_index++) { - if (!m_connectors[connector_index]) - continue; - if (!m_connectors[connector_index]->m_edid_valid) - continue; - TRY(m_connectors[connector_index]->set_safe_mode_setting()); - TRY(m_connectors[connector_index]->create_attached_framebuffer_console({})); - } - return {}; -} - -ErrorOr IntelDisplayConnectorGroup::set_safe_mode_setting(Badge, IntelNativeDisplayConnector& connector) -{ - VERIFY(connector.m_modeset_lock.is_locked()); - if (!connector.m_edid_parser.has_value()) - return Error::from_errno(ENOTSUP); - if (!connector.m_edid_parser.value().detailed_timing(0).has_value()) - return Error::from_errno(ENOTSUP); - auto details = connector.m_edid_parser.value().detailed_timing(0).release_value(); - - DisplayConnector::ModeSetting modesetting { - // Note: We assume that we always use 32 bit framebuffers. - .horizontal_stride = details.horizontal_addressable_pixels() * sizeof(u32), - .pixel_clock_in_khz = details.pixel_clock_khz(), - .horizontal_active = details.horizontal_addressable_pixels(), - .horizontal_front_porch_pixels = details.horizontal_front_porch_pixels(), - .horizontal_sync_time_pixels = details.horizontal_sync_pulse_width_pixels(), - .horizontal_blank_pixels = details.horizontal_blanking_pixels(), - .vertical_active = details.vertical_addressable_lines(), - .vertical_front_porch_lines = details.vertical_front_porch_lines(), - .vertical_sync_time_lines = details.vertical_sync_pulse_width_lines(), - .vertical_blank_lines = details.vertical_blanking_lines(), - .horizontal_offset = 0, - .vertical_offset = 0, - }; - - return set_mode_setting(connector, modesetting); -} - -ErrorOr IntelDisplayConnectorGroup::set_mode_setting(Badge, IntelNativeDisplayConnector& connector, DisplayConnector::ModeSetting const& mode_setting) -{ - return set_mode_setting(connector, mode_setting); -} - -ErrorOr IntelDisplayConnectorGroup::set_mode_setting(IntelNativeDisplayConnector& connector, DisplayConnector::ModeSetting const& mode_setting) -{ - VERIFY(connector.m_modeset_lock.is_locked()); - - VERIFY(to_underlying(connector.connector_index()) < m_connectors.size()); - VERIFY(&connector == m_connectors[to_underlying(connector.connector_index())].ptr()); - - DisplayConnector::ModeSetting actual_mode_setting = mode_setting; - actual_mode_setting.horizontal_stride = actual_mode_setting.horizontal_active * sizeof(u32); - VERIFY(actual_mode_setting.horizontal_stride != 0); - if (m_generation == IntelGraphics::Generation::Gen4) { - TRY(set_gen4_mode_setting(connector, actual_mode_setting)); - } else { - VERIFY_NOT_REACHED(); - } - - connector.m_current_mode_setting = actual_mode_setting; - if (!connector.m_framebuffer_console.is_null()) - static_cast(connector.m_framebuffer_console.ptr())->set_resolution(actual_mode_setting.horizontal_active, actual_mode_setting.vertical_active, actual_mode_setting.horizontal_stride); - return {}; -} - -ErrorOr IntelDisplayConnectorGroup::set_gen4_mode_setting(IntelNativeDisplayConnector& connector, DisplayConnector::ModeSetting const& mode_setting) -{ - VERIFY(connector.m_modeset_lock.is_locked()); - SpinlockLocker control_lock(m_control_lock); - SpinlockLocker modeset_lock(m_modeset_lock); - if (!set_crt_resolution(mode_setting)) - return Error::from_errno(ENOTSUP); - return {}; -} - -void IntelDisplayConnectorGroup::enable_vga_plane() -{ - VERIFY(m_control_lock.is_locked()); - VERIFY(m_modeset_lock.is_locked()); -} - -StringView IntelDisplayConnectorGroup::convert_analog_output_register_to_string(AnalogOutputRegisterOffset index) const -{ - switch (index) { - case AnalogOutputRegisterOffset::AnalogDisplayPort: - return "AnalogDisplayPort"sv; - case AnalogOutputRegisterOffset::VGADisplayPlaneControl: - return "VGADisplayPlaneControl"sv; - default: - VERIFY_NOT_REACHED(); - } -} - -void IntelDisplayConnectorGroup::write_to_general_register(RegisterOffset offset, u32 value) -{ - VERIFY(m_control_lock.is_locked()); - SpinlockLocker lock(m_registers_lock); - auto* reg = (u32 volatile*)m_registers_region->vaddr().offset(offset.value()).as_ptr(); - *reg = value; -} -u32 IntelDisplayConnectorGroup::read_from_general_register(RegisterOffset offset) const -{ - VERIFY(m_control_lock.is_locked()); - SpinlockLocker lock(m_registers_lock); - auto* reg = (u32 volatile*)m_registers_region->vaddr().offset(offset.value()).as_ptr(); - u32 value = *reg; - return value; -} - -void IntelDisplayConnectorGroup::write_to_analog_output_register(AnalogOutputRegisterOffset index, u32 value) -{ - dbgln_if(INTEL_GRAPHICS_DEBUG, "Intel Graphics Display Connector:: Write to {} value of {:x}", convert_analog_output_register_to_string(index), value); - write_to_general_register(to_underlying(index), value); -} - -u32 IntelDisplayConnectorGroup::read_from_analog_output_register(AnalogOutputRegisterOffset index) const -{ - u32 value = read_from_general_register(to_underlying(index)); - dbgln_if(INTEL_GRAPHICS_DEBUG, "Intel Graphics Display Connector: Read from {} value of {:x}", convert_analog_output_register_to_string(index), value); - return value; -} - -static size_t compute_dac_multiplier(size_t pixel_clock_in_khz) -{ - dbgln_if(INTEL_GRAPHICS_DEBUG, "Intel native graphics: Pixel clock is {} KHz", pixel_clock_in_khz); - VERIFY(pixel_clock_in_khz >= 25000); - if (pixel_clock_in_khz >= 100000) { - return 1; - } else if (pixel_clock_in_khz >= 50000) { - return 2; - } else { - return 4; - } -} - -bool IntelDisplayConnectorGroup::set_crt_resolution(DisplayConnector::ModeSetting const& mode_setting) -{ - VERIFY(m_control_lock.is_locked()); - VERIFY(m_modeset_lock.is_locked()); - - // Note: Just in case we still allow access to VGA IO ports, disable it now. - GraphicsManagement::the().disable_vga_emulation_access_permanently(); - - auto dac_multiplier = compute_dac_multiplier(mode_setting.pixel_clock_in_khz); - auto pll_settings = create_pll_settings(m_generation, (1000 * mode_setting.pixel_clock_in_khz * dac_multiplier), 96'000'000); - if (!pll_settings.has_value()) - return false; - auto settings = pll_settings.value(); - - disable_dac_output(); - MUST(m_planes[0]->disable({})); - MUST(m_transcoders[0]->disable_pipe({})); - MUST(m_transcoders[0]->disable_dpll({})); - disable_vga_emulation(); - - dbgln_if(INTEL_GRAPHICS_DEBUG, "PLL settings for {} {} {} {} {}", settings.n, settings.m1, settings.m2, settings.p1, settings.p2); - MUST(m_transcoders[0]->set_dpll_settings({}, settings, dac_multiplier)); - MUST(m_transcoders[0]->disable_dpll({})); - MUST(m_transcoders[0]->enable_dpll_without_vga({})); - MUST(m_transcoders[0]->set_mode_setting_timings({}, mode_setting)); - - VERIFY(!m_transcoders[0]->pipe_enabled({})); - MUST(m_transcoders[0]->enable_pipe({})); - - MUST(m_planes[0]->set_aperture_base({}, m_mmio_second_region.pci_bar_paddr)); - MUST(m_planes[0]->set_pipe({}, IntelDisplayPlane::PipeSelect::PipeA)); - MUST(m_planes[0]->set_horizontal_stride({}, mode_setting.horizontal_active * 4)); - MUST(m_planes[0]->set_horizontal_active_pixels_count({}, mode_setting.horizontal_active)); - // Note: This doesn't affect anything on the plane settings for Gen4, but we still - // do it for the sake of "completeness". - MUST(m_planes[0]->set_vertical_active_pixels_count({}, mode_setting.vertical_active)); - MUST(m_planes[0]->enable({})); - enable_dac_output(); - - return true; -} - -void IntelDisplayConnectorGroup::disable_dac_output() -{ - VERIFY(m_control_lock.is_locked()); - VERIFY(m_modeset_lock.is_locked()); - write_to_analog_output_register(AnalogOutputRegisterOffset::AnalogDisplayPort, 0b11 << 10); -} - -void IntelDisplayConnectorGroup::enable_dac_output() -{ - VERIFY(m_control_lock.is_locked()); - VERIFY(m_modeset_lock.is_locked()); - write_to_analog_output_register(AnalogOutputRegisterOffset::AnalogDisplayPort, (1 << 31)); -} - -void IntelDisplayConnectorGroup::disable_vga_emulation() -{ - VERIFY(m_control_lock.is_locked()); - VERIFY(m_modeset_lock.is_locked()); - write_to_analog_output_register(AnalogOutputRegisterOffset::VGADisplayPlaneControl, (1 << 31)); - read_from_analog_output_register(AnalogOutputRegisterOffset::VGADisplayPlaneControl); -} - -} diff --git a/Kernel/Devices/GPU/Intel/DisplayConnectorGroup.h b/Kernel/Devices/GPU/Intel/DisplayConnectorGroup.h deleted file mode 100644 index 45a4220afdd..00000000000 --- a/Kernel/Devices/GPU/Intel/DisplayConnectorGroup.h +++ /dev/null @@ -1,97 +0,0 @@ -/* - * Copyright (c) 2022, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace Kernel { - -class IntelNativeGraphicsAdapter; -class IntelDisplayConnectorGroup : public RefCounted { - friend class IntelNativeGraphicsAdapter; - -public: - struct MMIORegion { - enum class BARAssigned { - BAR0, - BAR2, - }; - BARAssigned pci_bar_assigned; - PhysicalAddress pci_bar_paddr; - size_t pci_bar_space_length; - }; - -private: - AK_TYPEDEF_DISTINCT_ORDERED_ID(size_t, RegisterOffset); - - enum class AnalogOutputRegisterOffset { - AnalogDisplayPort = 0x61100, - VGADisplayPlaneControl = 0x71400, - }; - -public: - static ErrorOr> try_create(Badge, IntelGraphics::Generation, MMIORegion const&, MMIORegion const&); - - ErrorOr set_safe_mode_setting(Badge, IntelNativeDisplayConnector&); - ErrorOr set_mode_setting(Badge, IntelNativeDisplayConnector&, DisplayConnector::ModeSetting const&); - -private: - IntelDisplayConnectorGroup(IntelGraphics::Generation generation, NonnullOwnPtr, NonnullOwnPtr registers_region, MMIORegion const&, MMIORegion const&); - - ErrorOr set_mode_setting(IntelNativeDisplayConnector&, DisplayConnector::ModeSetting const&); - - StringView convert_analog_output_register_to_string(AnalogOutputRegisterOffset index) const; - void write_to_analog_output_register(AnalogOutputRegisterOffset, u32 value); - u32 read_from_analog_output_register(AnalogOutputRegisterOffset) const; - void write_to_general_register(RegisterOffset offset, u32 value); - u32 read_from_general_register(RegisterOffset offset) const; - - // DisplayConnector initialization related methods - ErrorOr initialize_connectors(); - ErrorOr initialize_gen4_connectors(); - - // General Modesetting methods - ErrorOr set_gen4_mode_setting(IntelNativeDisplayConnector&, DisplayConnector::ModeSetting const&); - - bool set_crt_resolution(DisplayConnector::ModeSetting const&); - - void disable_vga_emulation(); - void enable_vga_plane(); - - void disable_dac_output(); - void enable_dac_output(); - - Spinlock m_control_lock; - Spinlock m_modeset_lock; - mutable Spinlock m_registers_lock; - - // Note: The linux driver specifies an enum of possible ports and there is only - // 9 ports (PORT_{A-I}). PORT_TC{1-6} are mapped to PORT_{D-I}. - Array, 9> m_connectors; - - Array, 5> m_transcoders; - Array, 3> m_planes; - - MMIORegion const m_mmio_first_region; - MMIORegion const m_mmio_second_region; - MMIORegion const& m_assigned_mmio_registers_region; - - IntelGraphics::Generation const m_generation; - NonnullOwnPtr m_registers_region; - NonnullOwnPtr m_gmbus_connector; -}; -} diff --git a/Kernel/Devices/GPU/Intel/NativeDisplayConnector.cpp b/Kernel/Devices/GPU/Intel/NativeDisplayConnector.cpp deleted file mode 100644 index d05af869812..00000000000 --- a/Kernel/Devices/GPU/Intel/NativeDisplayConnector.cpp +++ /dev/null @@ -1,95 +0,0 @@ -/* - * Copyright (c) 2022, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -namespace Kernel { - -ErrorOr> IntelNativeDisplayConnector::try_create_with_display_connector_group(IntelDisplayConnectorGroup const& parent_connector_group, ConnectorIndex connector_index, Type type, PhysicalAddress framebuffer_address, size_t framebuffer_resource_size) -{ - return TRY(DeviceManagement::try_create_device(parent_connector_group, connector_index, type, framebuffer_address, framebuffer_resource_size)); -} - -ErrorOr IntelNativeDisplayConnector::create_attached_framebuffer_console(Badge) -{ - size_t width = 0; - size_t height = 0; - size_t pitch = 0; - { - SpinlockLocker control_locker(m_control_lock); - SpinlockLocker mode_set_locker(m_modeset_lock); - width = m_current_mode_setting.horizontal_active; - height = m_current_mode_setting.vertical_active; - pitch = m_current_mode_setting.horizontal_stride; - } - m_framebuffer_console = Graphics::ContiguousFramebufferConsole::initialize(m_framebuffer_address.value(), width, height, pitch); - GraphicsManagement::the().set_console(*m_framebuffer_console); - return {}; -} - -IntelNativeDisplayConnector::IntelNativeDisplayConnector(IntelDisplayConnectorGroup const& parent_connector_group, ConnectorIndex connector_index, Type type, PhysicalAddress framebuffer_address, size_t framebuffer_resource_size) - : DisplayConnector(framebuffer_address, framebuffer_resource_size, true) - , m_type(type) - , m_connector_index(connector_index) - , m_parent_connector_group(parent_connector_group) -{ -} - -void IntelNativeDisplayConnector::set_edid_bytes(Badge, Array const& raw_bytes) -{ - // Note: The provided EDID might be invalid (because there's no attached monitor) - // Therefore, set might_be_invalid to true to indicate that. - DisplayConnector::set_edid_bytes(raw_bytes, true); -} - -ErrorOr IntelNativeDisplayConnector::set_y_offset(size_t) -{ - return Error::from_errno(ENOTIMPL); -} - -ErrorOr IntelNativeDisplayConnector::unblank() -{ - return Error::from_errno(ENOTIMPL); -} - -ErrorOr IntelNativeDisplayConnector::set_safe_mode_setting() -{ - SpinlockLocker locker(m_modeset_lock); - return m_parent_connector_group->set_safe_mode_setting({}, *this); -} - -void IntelNativeDisplayConnector::enable_console() -{ - VERIFY(m_control_lock.is_locked()); - if (m_framebuffer_console) - m_framebuffer_console->enable(); -} - -void IntelNativeDisplayConnector::disable_console() -{ - VERIFY(m_control_lock.is_locked()); - if (m_framebuffer_console) - m_framebuffer_console->disable(); -} - -ErrorOr IntelNativeDisplayConnector::flush_first_surface() -{ - return Error::from_errno(ENOTSUP); -} - -ErrorOr IntelNativeDisplayConnector::set_mode_setting(DisplayConnector::ModeSetting const&) -{ - return Error::from_errno(ENOTIMPL); -} - -} diff --git a/Kernel/Devices/GPU/Intel/NativeDisplayConnector.h b/Kernel/Devices/GPU/Intel/NativeDisplayConnector.h deleted file mode 100644 index 6aca2869e86..00000000000 --- a/Kernel/Devices/GPU/Intel/NativeDisplayConnector.h +++ /dev/null @@ -1,80 +0,0 @@ -/* - * Copyright (c) 2022, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include -#include -#include - -namespace Kernel { - -class IntelDisplayConnectorGroup; -class IntelNativeDisplayConnector final - : public DisplayConnector { - friend class IntelDisplayConnectorGroup; - friend class DeviceManagement; - -public: - enum class Type { - Invalid, - Analog, - DVO, - LVDS, - TVOut, - HDMI, - DisplayPort, - EmbeddedDisplayPort, - }; - - enum class ConnectorIndex : size_t { - PortA = 0, - PortB = 1, - PortC = 2, - PortD = 3, - PortE = 4, - PortF = 5, - PortH = 6, - PortG = 7, - PortI = 8, - }; - - static ErrorOr> try_create_with_display_connector_group(IntelDisplayConnectorGroup const&, ConnectorIndex, Type, PhysicalAddress framebuffer_address, size_t framebuffer_resource_size); - - void set_edid_bytes(Badge, Array const& edid_bytes); - ErrorOr create_attached_framebuffer_console(Badge); - - ConnectorIndex connector_index() const { return m_connector_index; } - -private: - // ^DisplayConnector - // FIXME: Implement modesetting capabilities in runtime from userland... - virtual bool mutable_mode_setting_capable() const override { return false; } - // FIXME: Implement double buffering capabilities in runtime from userland... - virtual bool double_framebuffering_capable() const override { return false; } - virtual ErrorOr set_mode_setting(ModeSetting const&) override; - virtual ErrorOr set_safe_mode_setting() override; - virtual ErrorOr set_y_offset(size_t y) override; - virtual ErrorOr unblank() override; - virtual ErrorOr flush_first_surface() override final; - virtual void enable_console() override; - virtual void disable_console() override; - virtual bool partial_flush_support() const override { return false; } - virtual bool flush_support() const override { return false; } - // Note: Paravirtualized hardware doesn't require a defined refresh rate for modesetting. - virtual bool refresh_rate_support() const override { return true; } - - IntelNativeDisplayConnector(IntelDisplayConnectorGroup const&, ConnectorIndex connector_index, Type, PhysicalAddress framebuffer_address, size_t framebuffer_resource_size); - Type const m_type { Type::Analog }; - ConnectorIndex const m_connector_index { 0 }; - NonnullLockRefPtr m_parent_connector_group; - LockRefPtr m_framebuffer_console; -}; -} diff --git a/Kernel/Devices/GPU/Intel/NativeGraphicsAdapter.cpp b/Kernel/Devices/GPU/Intel/NativeGraphicsAdapter.cpp deleted file mode 100644 index e11e96e78d3..00000000000 --- a/Kernel/Devices/GPU/Intel/NativeGraphicsAdapter.cpp +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Copyright (c) 2021, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include -#include -#include - -namespace Kernel { - -static constexpr u16 supported_models[] { - 0x29c2, // Intel G35 Adapter -}; - -static bool is_supported_model(u16 device_id) -{ - for (auto& id : supported_models) { - if (id == device_id) - return true; - } - return false; -} - -ErrorOr IntelNativeGraphicsAdapter::probe(PCI::DeviceIdentifier const& pci_device_identifier) -{ - return is_supported_model(pci_device_identifier.hardware_id().device_id); -} - -ErrorOr> IntelNativeGraphicsAdapter::create(PCI::DeviceIdentifier const& pci_device_identifier) -{ - auto adapter = TRY(adopt_nonnull_lock_ref_or_enomem(new (nothrow) IntelNativeGraphicsAdapter(pci_device_identifier))); - TRY(adapter->initialize_adapter()); - return adapter; -} - -ErrorOr IntelNativeGraphicsAdapter::initialize_adapter() -{ - dbgln_if(INTEL_GRAPHICS_DEBUG, "Intel Native Graphics Adapter @ {}", device_identifier().address()); - auto bar0_space_size = PCI::get_BAR_space_size(device_identifier(), PCI::HeaderType0BaseRegister::BAR0); - auto bar0_space_address = TRY(PCI::get_bar_address(device_identifier(), PCI::HeaderType0BaseRegister::BAR0)); - auto bar2_space_size = PCI::get_BAR_space_size(device_identifier(), PCI::HeaderType0BaseRegister::BAR2); - auto bar2_space_address = TRY(PCI::get_bar_address(device_identifier(), PCI::HeaderType0BaseRegister::BAR2)); - dmesgln_pci(*this, "MMIO @ {}, space size is {:x} bytes", bar0_space_address, bar0_space_size); - dmesgln_pci(*this, "framebuffer @ {}", bar2_space_address); - - using MMIORegion = IntelDisplayConnectorGroup::MMIORegion; - MMIORegion first_region { MMIORegion::BARAssigned::BAR0, bar0_space_address, bar0_space_size }; - MMIORegion second_region { MMIORegion::BARAssigned::BAR2, bar2_space_address, bar2_space_size }; - - PCI::enable_bus_mastering(device_identifier()); - PCI::enable_io_space(device_identifier()); - PCI::enable_memory_space(device_identifier()); - - switch (device_identifier().hardware_id().device_id) { - case 0x29c2: - m_connector_group = TRY(IntelDisplayConnectorGroup::try_create({}, IntelGraphics::Generation::Gen4, first_region, second_region)); - return {}; - default: - return Error::from_errno(ENODEV); - } -} - -IntelNativeGraphicsAdapter::IntelNativeGraphicsAdapter(PCI::DeviceIdentifier const& pci_device_identifier) - : GPUDevice() - , PCI::Device(const_cast(pci_device_identifier)) -{ -} - -} diff --git a/Kernel/Devices/GPU/Intel/NativeGraphicsAdapter.h b/Kernel/Devices/GPU/Intel/NativeGraphicsAdapter.h deleted file mode 100644 index 8d849e859a5..00000000000 --- a/Kernel/Devices/GPU/Intel/NativeGraphicsAdapter.h +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright (c) 2021, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include -#include -#include - -namespace Kernel { - -class IntelNativeGraphicsAdapter final - : public GPUDevice - , public PCI::Device { - -public: - static ErrorOr probe(PCI::DeviceIdentifier const&); - static ErrorOr> create(PCI::DeviceIdentifier const&); - - virtual ~IntelNativeGraphicsAdapter() = default; - - virtual StringView device_name() const override { return "IntelNativeGraphicsAdapter"sv; } - -private: - ErrorOr initialize_adapter(); - - explicit IntelNativeGraphicsAdapter(PCI::DeviceIdentifier const&); - - LockRefPtr m_connector_group; -}; -} diff --git a/Kernel/Devices/GPU/Intel/Plane/DisplayPlane.cpp b/Kernel/Devices/GPU/Intel/Plane/DisplayPlane.cpp deleted file mode 100644 index de8f033af51..00000000000 --- a/Kernel/Devices/GPU/Intel/Plane/DisplayPlane.cpp +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright (c) 2022, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include - -namespace Kernel { - -IntelDisplayPlane::IntelDisplayPlane(Memory::TypedMapping plane_registers_mapping) - : m_plane_registers(move(plane_registers_mapping)) -{ -} - -IntelDisplayPlane::ShadowRegisters IntelDisplayPlane::shadow_registers() const -{ - SpinlockLocker locker(m_access_lock); - return m_shadow_registers; -} - -ErrorOr IntelDisplayPlane::set_horizontal_active_pixels_count(Badge, size_t horizontal_active_pixels_count) -{ - SpinlockLocker locker(m_access_lock); - m_horizontal_active_pixels_count = horizontal_active_pixels_count; - return {}; -} -ErrorOr IntelDisplayPlane::set_vertical_active_pixels_count(Badge, size_t vertical_active_pixels_count) -{ - SpinlockLocker locker(m_access_lock); - m_vertical_active_pixels_count = vertical_active_pixels_count; - return {}; -} -ErrorOr IntelDisplayPlane::set_horizontal_stride(Badge, size_t horizontal_stride) -{ - SpinlockLocker locker(m_access_lock); - m_horizontal_stride = horizontal_stride; - return {}; -} -ErrorOr IntelDisplayPlane::set_aperture_base(Badge, PhysicalAddress aperture_start) -{ - SpinlockLocker locker(m_access_lock); - m_aperture_start.set(aperture_start.get()); - return {}; -} -ErrorOr IntelDisplayPlane::set_pipe(Badge, PipeSelect pipe_select) -{ - SpinlockLocker locker(m_access_lock); - m_pipe_select = pipe_select; - return {}; -} - -bool IntelDisplayPlane::is_enabled(Badge) -{ - SpinlockLocker locker(m_access_lock); - return m_shadow_registers.control & (1 << 31); -} - -ErrorOr IntelDisplayPlane::disable(Badge) -{ - SpinlockLocker locker(m_access_lock); - // Note: We use the shadow register so we don't have the already set - // settings being lost. - m_shadow_registers.control &= ~(1 << 31); - m_plane_registers->control = m_shadow_registers.control; - return {}; -} - -} diff --git a/Kernel/Devices/GPU/Intel/Plane/DisplayPlane.h b/Kernel/Devices/GPU/Intel/Plane/DisplayPlane.h deleted file mode 100644 index 1cbecd41b18..00000000000 --- a/Kernel/Devices/GPU/Intel/Plane/DisplayPlane.h +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright (c) 2022, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include -#include -#include - -namespace Kernel { - -class IntelDisplayConnectorGroup; -class IntelDisplayPlane { -public: - enum class PipeSelect { - PipeA, - PipeB, - PipeC, - PipeD, - }; - - // Note: This is used to "cache" all the registers we wrote to, because - // we might not be able to read them directly from hardware later. - struct ShadowRegisters { - u32 control; - u32 linear_offset; - u32 stride; - u32 surface_base; - }; - -public: - static ErrorOr> create_with_physical_address(PhysicalAddress plane_registers_start_address); - - ErrorOr set_horizontal_active_pixels_count(Badge, size_t horizontal_active_pixels_count); - ErrorOr set_vertical_active_pixels_count(Badge, size_t vertical_active_pixels_count); - ErrorOr set_horizontal_stride(Badge, size_t horizontal_stride); - ErrorOr set_aperture_base(Badge, PhysicalAddress aperture_start); - ErrorOr set_pipe(Badge, PipeSelect); - - virtual ErrorOr enable(Badge) = 0; - bool is_enabled(Badge); - ErrorOr disable(Badge); - - ShadowRegisters shadow_registers() const; - - virtual ~IntelDisplayPlane() = default; - -protected: - struct [[gnu::packed]] PlaneRegisters { - u32 control; - u32 linear_offset; - u32 stride; - u8 padding[24]; // Note: This might contain other registers, don't touch them. - u32 surface_base; - }; - - explicit IntelDisplayPlane(Memory::TypedMapping registers_mapping); - mutable Spinlock m_access_lock; - ShadowRegisters m_shadow_registers {}; - Memory::TypedMapping m_plane_registers; - - // Note: The PipeSelect value is used only in planes until Skylake graphics. - PipeSelect m_pipe_select { PipeSelect::PipeA }; - - PhysicalAddress m_aperture_start; - size_t m_horizontal_stride { 0 }; - size_t m_horizontal_active_pixels_count { 0 }; - size_t m_vertical_active_pixels_count { 0 }; -}; -} diff --git a/Kernel/Devices/GPU/Intel/Plane/G33DisplayPlane.cpp b/Kernel/Devices/GPU/Intel/Plane/G33DisplayPlane.cpp deleted file mode 100644 index a0ac74be955..00000000000 --- a/Kernel/Devices/GPU/Intel/Plane/G33DisplayPlane.cpp +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright (c) 2022, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include - -namespace Kernel { - -ErrorOr> IntelG33DisplayPlane::create_with_physical_address(PhysicalAddress plane_registers_start_address) -{ - auto registers_mapping = TRY(Memory::map_typed(plane_registers_start_address, sizeof(PlaneRegisters), Memory::Region::Access::ReadWrite)); - return adopt_nonnull_own_or_enomem(new (nothrow) IntelG33DisplayPlane(move(registers_mapping))); -} - -IntelG33DisplayPlane::IntelG33DisplayPlane(Memory::TypedMapping registers_mapping) - : IntelDisplayPlane(move(registers_mapping)) -{ -} - -ErrorOr IntelG33DisplayPlane::enable(Badge) -{ - SpinlockLocker locker(m_access_lock); - VERIFY(((m_horizontal_active_pixels_count * 4) % 64 == 0)); - VERIFY(((m_horizontal_stride) % 64 == 0)); - - u32 control_value = 0; - - switch (m_pipe_select) { - case PipeSelect::PipeA: - control_value |= (0b00 << 24); - break; - case PipeSelect::PipeB: - control_value |= (0b01 << 24); - break; - case PipeSelect::PipeC: - control_value |= (0b10 << 24); - break; - case PipeSelect::PipeD: - control_value |= (0b11 << 24); - break; - } - - // Note: Set the plane to work with 32 bit BGRX (Ignore Alpha channel). - // Note: Bit 31 is set to turn on the plane. - control_value |= (0b0110 << 26) | (1 << 31); - - m_plane_registers->stride = m_horizontal_stride; - m_shadow_registers.stride = m_horizontal_stride; - m_plane_registers->linear_offset = 0; - m_shadow_registers.linear_offset = 0; - m_plane_registers->surface_base = m_aperture_start.get(); - m_shadow_registers.surface_base = m_aperture_start.get(); - m_plane_registers->control = control_value; - m_shadow_registers.control = control_value; - return {}; -} -} diff --git a/Kernel/Devices/GPU/Intel/Plane/G33DisplayPlane.h b/Kernel/Devices/GPU/Intel/Plane/G33DisplayPlane.h deleted file mode 100644 index 185446dadf1..00000000000 --- a/Kernel/Devices/GPU/Intel/Plane/G33DisplayPlane.h +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright (c) 2022, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include - -namespace Kernel { - -class IntelDisplayConnectorGroup; -class IntelG33DisplayPlane final : public IntelDisplayPlane { -public: - static ErrorOr> create_with_physical_address(PhysicalAddress plane_registers_start_address); - - virtual ErrorOr enable(Badge) override; - -private: - explicit IntelG33DisplayPlane(Memory::TypedMapping plane_registers_mapping); -}; -} diff --git a/Kernel/Devices/GPU/Intel/Transcoder/AnalogDisplayTranscoder.cpp b/Kernel/Devices/GPU/Intel/Transcoder/AnalogDisplayTranscoder.cpp deleted file mode 100644 index 26e2ba49b7a..00000000000 --- a/Kernel/Devices/GPU/Intel/Transcoder/AnalogDisplayTranscoder.cpp +++ /dev/null @@ -1,89 +0,0 @@ -/* - * Copyright (c) 2022, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include - -namespace Kernel { - -ErrorOr> IntelAnalogDisplayTranscoder::create_with_physical_addresses(PhysicalAddress transcoder_registers_start_address, - PhysicalAddress pipe_registers_start_address, PhysicalAddress dpll_registers_start_address, PhysicalAddress dpll_multiplier_register_start_address) -{ - auto transcoder_registers_mapping = TRY(Memory::map_typed(transcoder_registers_start_address, sizeof(IntelDisplayTranscoder::TranscoderRegisters), Memory::Region::Access::ReadWrite)); - auto pipe_registers_mapping = TRY(Memory::map_typed(pipe_registers_start_address, sizeof(IntelDisplayTranscoder::PipeRegisters), Memory::Region::Access::ReadWrite)); - auto dpll_registers_mapping = TRY(Memory::map_typed(dpll_registers_start_address, sizeof(DPLLRegisters), Memory::Region::Access::ReadWrite)); - auto dpll_control_mapping = TRY(Memory::map_typed(dpll_multiplier_register_start_address, sizeof(DPLLControlRegisters), Memory::Region::Access::ReadWrite)); - return adopt_nonnull_own_or_enomem(new (nothrow) IntelAnalogDisplayTranscoder(move(transcoder_registers_mapping), move(pipe_registers_mapping), move(dpll_registers_mapping), move(dpll_control_mapping))); -} - -IntelAnalogDisplayTranscoder::IntelAnalogDisplayTranscoder(Memory::TypedMapping transcoder_registers_mapping, - Memory::TypedMapping pipe_registers_mapping, Memory::TypedMapping dpll_registers_mapping, Memory::TypedMapping dpll_control_registers) - : IntelDisplayTranscoder(move(transcoder_registers_mapping), move(pipe_registers_mapping)) - , m_dpll_registers(move(dpll_registers_mapping)) - , m_dpll_control_registers(move(dpll_control_registers)) -{ -} - -ErrorOr IntelAnalogDisplayTranscoder::set_dpll_settings(Badge, IntelGraphics::PLLSettings const& settings, size_t dac_multiplier) -{ - SpinlockLocker locker(m_access_lock); - u32 value = (settings.m2 - 2) | ((settings.m1 - 2) << 8) | ((settings.n - 2) << 16); - m_dpll_registers->divisor_a0 = value; - m_dpll_registers->divisor_a1 = value; - m_shadow_registers.dpll_divisor_a0 = value; - m_shadow_registers.dpll_divisor_a1 = value; - - // Note: We don't set the DAC multiplier now but reserve it for later usage (e.g. when enabling the DPLL) - m_shadow_registers.dpll_reserved_dac_multiplier = dac_multiplier; - // Note: We don't set the DPLL P1 now but reserve it for later usage (e.g. when enabling the DPLL) - m_shadow_registers.dpll_p1 = settings.p1; - return {}; -} - -ErrorOr IntelAnalogDisplayTranscoder::enable_dpll_without_vga(Badge) -{ - SpinlockLocker locker(m_access_lock); - // Explanation for Gen4 DPLL control bits: - // 1. 0b0110 in bits 9 to 12 - use clock phase 6 (Default) - // 2. bits 24,25 - set to 0b00 to ensure FPA0/FPA1 (DPLL A Divisor 0, 1) divide by 10 (used for DAC modes under 270 MHz) - // 3. bit 26 - set to 0b1 to ensure mode select to DAC mode - // 4. bit 28 - set to 0b1 to disable VGA mode - // 5. bit 31 - enable DPLL VCO (DPLL enabled and operational) - u32 control_value = (6 << 9) | (m_shadow_registers.dpll_p1) << 16 | (1 << 26) | (1 << 28) | (1 << 31); - m_dpll_control_registers->control = control_value; - m_shadow_registers.dpll_control = control_value; - - // Explanation for Gen4 DPLL multiplier bits: - // 1. 0b0110 in bits 9 to 12 - use clock phase 6 (Default) - // 2. bits 24,25 - set to 0b00 to ensure FPA0/FPA1 (DPLL A Divisor 0, 1) divide by 10 (used for DAC modes under 270 MHz) - // 3. bit 26 - set to 0b1 to ensure mode select to DAC mode - // 4. bit 28 - set to 0b1 to disable VGA mode - // 5. bit 31 - enable DPLL VCO (DPLL enabled and operational) - u32 dac_multiplier_value = (m_shadow_registers.dpll_reserved_dac_multiplier - 1) | ((m_shadow_registers.dpll_reserved_dac_multiplier - 1) << 8); - m_dpll_control_registers->multiplier = dac_multiplier_value; - m_shadow_registers.dpll_raw_dac_multiplier = dac_multiplier_value; - - // The specification says we should wait (at least) about 150 microseconds - // after enabling the DPLL to allow the clock to stabilize - microseconds_delay(200); - for (size_t milliseconds_elapsed = 0; milliseconds_elapsed < 5; milliseconds_elapsed++) { - u32 control_value = m_dpll_control_registers->control; - if (control_value & (1 << 31)) - return {}; - } - return Error::from_errno(EBUSY); -} - -ErrorOr IntelAnalogDisplayTranscoder::disable_dpll(Badge) -{ - SpinlockLocker locker(m_access_lock); - m_dpll_control_registers->control = 0; - m_shadow_registers.dpll_control = 0; - return {}; -} - -} diff --git a/Kernel/Devices/GPU/Intel/Transcoder/AnalogDisplayTranscoder.h b/Kernel/Devices/GPU/Intel/Transcoder/AnalogDisplayTranscoder.h deleted file mode 100644 index 11c0caffec1..00000000000 --- a/Kernel/Devices/GPU/Intel/Transcoder/AnalogDisplayTranscoder.h +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright (c) 2022, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include - -namespace Kernel { - -class IntelDisplayConnectorGroup; -class IntelAnalogDisplayTranscoder final : public IntelDisplayTranscoder { -public: - static ErrorOr> create_with_physical_addresses(PhysicalAddress transcoder_registers_start_address, - PhysicalAddress pipe_registers_start_address, PhysicalAddress dpll_registers_start_address, PhysicalAddress dpll_control_registers_start_address); - - virtual ErrorOr set_dpll_settings(Badge, IntelGraphics::PLLSettings const& settings, size_t dac_multiplier) override; - virtual ErrorOr enable_dpll_without_vga(Badge) override; - virtual ErrorOr disable_dpll(Badge) override; - -private: - struct [[gnu::packed]] DPLLRegisters { - u32 divisor_a0; - u32 divisor_a1; - }; - - struct [[gnu::packed]] DPLLControlRegisters { - u32 control; - u32 padding; // On Gen4, this is the control register of DPLL B, don't touch this - u32 multiplier; - }; - - IntelAnalogDisplayTranscoder(Memory::TypedMapping, Memory::TypedMapping, Memory::TypedMapping, Memory::TypedMapping); - Memory::TypedMapping m_dpll_registers; - Memory::TypedMapping m_dpll_control_registers; -}; -} diff --git a/Kernel/Devices/GPU/Intel/Transcoder/DisplayTranscoder.cpp b/Kernel/Devices/GPU/Intel/Transcoder/DisplayTranscoder.cpp deleted file mode 100644 index f4cc5849c35..00000000000 --- a/Kernel/Devices/GPU/Intel/Transcoder/DisplayTranscoder.cpp +++ /dev/null @@ -1,110 +0,0 @@ -/* - * Copyright (c) 2022, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include - -namespace Kernel { - -IntelDisplayTranscoder::IntelDisplayTranscoder(Memory::TypedMapping registers_mapping, Memory::TypedMapping pipe_registers_mapping) - : m_transcoder_registers(move(registers_mapping)) - , m_pipe_registers(move(pipe_registers_mapping)) -{ -} - -IntelDisplayTranscoder::ShadowRegisters IntelDisplayTranscoder::current_registers_state() const -{ - SpinlockLocker locker(m_access_lock); - return m_shadow_registers; -} - -ErrorOr IntelDisplayTranscoder::set_mode_setting_timings(Badge, DisplayConnector::ModeSetting const& mode_setting) -{ - SpinlockLocker locker(m_access_lock); - - dbgln_if(INTEL_GRAPHICS_DEBUG, "htotal - {}, {}", (mode_setting.horizontal_active - 1), (mode_setting.horizontal_total() - 1)); - m_shadow_registers.horizontal_total = ((mode_setting.horizontal_active - 1) | (mode_setting.horizontal_total() - 1) << 16); - m_transcoder_registers->horizontal_total = ((mode_setting.horizontal_active - 1) | (mode_setting.horizontal_total() - 1) << 16); - - dbgln_if(INTEL_GRAPHICS_DEBUG, "hblank - {}, {}", (mode_setting.horizontal_blanking_start() - 1), (mode_setting.horizontal_blanking_start() + mode_setting.horizontal_blank_pixels - 1)); - m_shadow_registers.horizontal_blank = ((mode_setting.horizontal_blanking_start() - 1) | (mode_setting.horizontal_blanking_start() + mode_setting.horizontal_blank_pixels - 1) << 16); - m_transcoder_registers->horizontal_blank = ((mode_setting.horizontal_blanking_start() - 1) | (mode_setting.horizontal_blanking_start() + mode_setting.horizontal_blank_pixels - 1) << 16); - - dbgln_if(INTEL_GRAPHICS_DEBUG, "hsync - {}, {}", (mode_setting.horizontal_sync_start() - 1), (mode_setting.horizontal_sync_end() - 1)); - m_shadow_registers.horizontal_sync = ((mode_setting.horizontal_sync_start() - 1) | (mode_setting.horizontal_sync_end() - 1) << 16); - m_transcoder_registers->horizontal_sync = ((mode_setting.horizontal_sync_start() - 1) | (mode_setting.horizontal_sync_end() - 1) << 16); - - dbgln_if(INTEL_GRAPHICS_DEBUG, "vtotal - {}, {}", (mode_setting.vertical_active - 1), (mode_setting.vertical_blanking_start() + mode_setting.vertical_blank_lines - 1)); - m_shadow_registers.vertical_total = ((mode_setting.vertical_active - 1) | (mode_setting.vertical_blanking_start() + mode_setting.vertical_blank_lines - 1) << 16); - m_transcoder_registers->vertical_total = ((mode_setting.vertical_active - 1) | (mode_setting.vertical_blanking_start() + mode_setting.vertical_blank_lines - 1) << 16); - - dbgln_if(INTEL_GRAPHICS_DEBUG, "vblank - {}, {}", (mode_setting.vertical_blanking_start() - 1), (mode_setting.vertical_blanking_start() + mode_setting.vertical_blank_lines - 1)); - m_shadow_registers.vertical_blank = ((mode_setting.vertical_blanking_start() - 1) | (mode_setting.vertical_blanking_start() + mode_setting.vertical_blank_lines - 1) << 16); - m_transcoder_registers->vertical_blank = ((mode_setting.vertical_blanking_start() - 1) | (mode_setting.vertical_blanking_start() + mode_setting.vertical_blank_lines - 1) << 16); - - dbgln_if(INTEL_GRAPHICS_DEBUG, "vsync - {}, {}", (mode_setting.vertical_sync_start() - 1), (mode_setting.vertical_sync_end() - 1)); - m_shadow_registers.vertical_sync = ((mode_setting.vertical_sync_start() - 1) | (mode_setting.vertical_sync_end() - 1) << 16); - m_transcoder_registers->vertical_sync = ((mode_setting.vertical_sync_start() - 1) | (mode_setting.vertical_sync_end() - 1) << 16); - - dbgln_if(INTEL_GRAPHICS_DEBUG, "sourceSize - {}, {}", (mode_setting.vertical_active - 1), (mode_setting.horizontal_active - 1)); - m_shadow_registers.pipe_source = ((mode_setting.vertical_active - 1) | (mode_setting.horizontal_active - 1) << 16); - m_transcoder_registers->pipe_source = ((mode_setting.vertical_active - 1) | (mode_setting.horizontal_active - 1) << 16); - return {}; -} - -ErrorOr IntelDisplayTranscoder::disable_pipe(Badge) -{ - SpinlockLocker locker(m_access_lock); - m_pipe_registers->pipe_configuration = 0; - m_shadow_registers.pipe_conf = 0; - dbgln_if(INTEL_GRAPHICS_DEBUG, "Disabling Pipe"); - size_t milliseconds_elapsed = 0; - while (milliseconds_elapsed < 100) { - u32 value = m_pipe_registers->pipe_configuration; - if (!(value & (1 << 30))) - return {}; - microseconds_delay(1000); - milliseconds_elapsed++; - } - return Error::from_errno(EBUSY); -} - -ErrorOr IntelDisplayTranscoder::enable_pipe(Badge) -{ - SpinlockLocker locker(m_access_lock); - u32 value = m_pipe_registers->pipe_configuration; - // Note: Just verify these are not already enabled... - if ((value & (1 << 30)) && (value & (1 << 31))) - return {}; - - // Note: Set the pipe configuration register with these bits: - // 1. Bit 31 - to enable the Pipe - // 2. Bit 24 - to enable Gamma Unit Mode to 10 bit Gamma mode. - // 3. Bits 21-23 are set to zero to indicate Progressive mode (non Interlaced mode) - // 4. Bits 18 and 19 are set to zero to indicate Normal operations of assigned - // Cursor and Display planes. - m_pipe_registers->pipe_configuration = (1 << 31) | (1 << 24); - m_shadow_registers.pipe_conf = (1 << 31) | (1 << 24); - dbgln_if(INTEL_GRAPHICS_DEBUG, "Enabling Pipe"); - size_t milliseconds_elapsed = 0; - while (milliseconds_elapsed < 100) { - u32 value = m_pipe_registers->pipe_configuration; - if ((value & (1 << 30))) - return {}; - microseconds_delay(1000); - milliseconds_elapsed++; - } - // FIXME: Seems like my video card is buggy and doesn't set the enabled bit (bit 30)!! - return {}; -} -bool IntelDisplayTranscoder::pipe_enabled(Badge) const -{ - SpinlockLocker locker(m_access_lock); - u32 value = m_pipe_registers->pipe_configuration; - return (value & (1 << 30)); -} -} diff --git a/Kernel/Devices/GPU/Intel/Transcoder/DisplayTranscoder.h b/Kernel/Devices/GPU/Intel/Transcoder/DisplayTranscoder.h deleted file mode 100644 index 3341740b537..00000000000 --- a/Kernel/Devices/GPU/Intel/Transcoder/DisplayTranscoder.h +++ /dev/null @@ -1,118 +0,0 @@ -/* - * Copyright (c) 2022, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include -#include -#include - -namespace Kernel { - -class IntelDisplayConnectorGroup; -class IntelDisplayTranscoder { -public: - // Note: This is used to "cache" all the registers we wrote to, because - // we might not be able to read them directly from hardware later. - struct ShadowRegisters { - u32 horizontal_total; - u32 horizontal_blank; - u32 horizontal_sync; - u32 vertical_total; - u32 vertical_blank; - u32 vertical_sync; - u32 exit_line; - u32 pipe_source; - u32 pipe_border_color_pattern; - u32 reserved; - u32 vsync_shift; - u32 pipe_mult; - u32 dpll_reserved_dac_multiplier; - u32 dpll_raw_dac_multiplier; - u32 dpll_divisor_a0; - u32 dpll_divisor_a1; - u32 dpll_p1; - u32 dpll_control; - u32 m1_value; - u32 n1_value; - u32 m2_value; - u32 n2_value; - u32 m1_link; - u32 n1_link; - u32 m2_link; - u32 n2_link; - u32 pipe_conf; - }; - - ErrorOr set_mode_setting_timings(Badge, DisplayConnector::ModeSetting const&); - virtual ErrorOr set_dpll_settings(Badge, IntelGraphics::PLLSettings const& settings, size_t dac_multiplier) = 0; - virtual ErrorOr enable_dpll_without_vga(Badge) = 0; - virtual ErrorOr disable_dpll(Badge) = 0; - - ErrorOr disable_pipe(Badge); - ErrorOr enable_pipe(Badge); - bool pipe_enabled(Badge) const; - - ShadowRegisters current_registers_state() const; - - virtual ~IntelDisplayTranscoder() = default; - -protected: - struct [[gnu::packed]] TranscoderRegisters { - u32 horizontal_total; - u32 horizontal_blank; - u32 horizontal_sync; - u32 vertical_total; - u32 vertical_blank; - u32 vertical_sync; - u32 exit_line; - u32 pipe_source; - u32 pipe_border_color_pattern; - u32 reserved; - u32 vsync_shift; - u32 pipe_mult; - u32 m1_value; - u32 n1_value; - u32 m2_value; - u32 n2_value; - u32 m1_link; - u32 n1_link; - u32 m2_link; - u32 n2_link; - }; - - struct [[gnu::packed]] PipeRegisters { - u32 pipe_display_scan_line; - u32 pipe_display_scan_line_count_range_compare; - u32 pipe_configuration; - u32 reserved; - u32 pipe_gamma_correction_max_red; - u32 pipe_gamma_correction_max_green; - u32 pipe_gamma_correction_max_blue; - u32 reserved2[2]; - u32 pipe_display_status; - u32 reserved3[2]; - u32 display_arbitration_control; - u32 display_fifo_watermark_control1; - u32 display_fifo_watermark_control2; - u32 display_fifo_watermark_control3; - u32 pipe_frame_count_high; - // Note: The specification calls this "Pipe Frame Count Low and Pixel Count" - u32 pipe_frame_count_low; - }; - - IntelDisplayTranscoder(Memory::TypedMapping, Memory::TypedMapping); - mutable Spinlock m_access_lock; - - ShadowRegisters m_shadow_registers {}; - Memory::TypedMapping m_transcoder_registers; - Memory::TypedMapping m_pipe_registers; -}; -} diff --git a/Kernel/Devices/GPU/Intel/Transcoder/PLL.cpp b/Kernel/Devices/GPU/Intel/Transcoder/PLL.cpp deleted file mode 100644 index ce024196120..00000000000 --- a/Kernel/Devices/GPU/Intel/Transcoder/PLL.cpp +++ /dev/null @@ -1,125 +0,0 @@ -/* - * Copyright (c) 2023, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include - -namespace Kernel::IntelGraphics { - -static constexpr PLLMaxSettings g35limits { - { 20'000'000, 400'000'000 }, // values in Hz, dot_clock - { 1'400'000'000, 2'800'000'000 }, // values in Hz, VCO - { 3, 8 }, // n - { 70, 120 }, // m - { 10, 20 }, // m1 - { 5, 9 }, // m2 - { 5, 80 }, // p - { 1, 8 }, // p1 - { 5, 10 } // p2 -}; - -PLLMaxSettings const& pll_max_settings_for_generation(Generation generation) -{ - switch (generation) { - case Generation::Gen4: - return g35limits; - default: - VERIFY_NOT_REACHED(); - } -} - -static size_t find_absolute_difference(u64 target_frequency, u64 checked_frequency) -{ - if (target_frequency >= checked_frequency) - return target_frequency - checked_frequency; - return checked_frequency - target_frequency; -} - -Optional create_pll_settings(Generation generation, u64 target_frequency, u64 reference_clock) -{ - PLLSettings settings {}; - PLLSettings best_settings {}; - auto& limits = pll_max_settings_for_generation(generation); - // FIXME: Is this correct for all Intel Native graphics cards? - settings.p2 = 10; - dbgln_if(INTEL_GRAPHICS_DEBUG, "Check PLL settings for ref clock of {} Hz, for target of {} Hz", reference_clock, target_frequency); - u64 best_difference = 0xffffffff; - for (settings.n = limits.n.min; settings.n <= limits.n.max; ++settings.n) { - for (settings.m1 = limits.m1.max; settings.m1 >= limits.m1.min; --settings.m1) { - for (settings.m2 = limits.m2.max; settings.m2 >= limits.m2.min; --settings.m2) { - for (settings.p1 = limits.p1.max; settings.p1 >= limits.p1.min; --settings.p1) { - dbgln_if(INTEL_GRAPHICS_DEBUG, "Check PLL settings for {} {} {} {} {}", settings.n, settings.m1, settings.m2, settings.p1, settings.p2); - if (!check_pll_settings(settings, reference_clock, limits)) - continue; - auto current_dot_clock = settings.compute_dot_clock(reference_clock); - if (current_dot_clock == target_frequency) - return settings; - auto difference = find_absolute_difference(target_frequency, current_dot_clock); - if (difference < best_difference && (current_dot_clock > target_frequency)) { - best_settings = settings; - best_difference = difference; - } - } - } - } - } - if (best_settings.is_valid()) - return best_settings; - return {}; -} - -bool check_pll_settings(PLLSettings const& settings, size_t reference_clock, PLLMaxSettings const& limits) -{ - if (settings.n < limits.n.min || settings.n > limits.n.max) { - dbgln_if(INTEL_GRAPHICS_DEBUG, "N is invalid {}", settings.n); - return false; - } - if (settings.m1 < limits.m1.min || settings.m1 > limits.m1.max) { - dbgln_if(INTEL_GRAPHICS_DEBUG, "m1 is invalid {}", settings.m1); - return false; - } - if (settings.m2 < limits.m2.min || settings.m2 > limits.m2.max) { - dbgln_if(INTEL_GRAPHICS_DEBUG, "m2 is invalid {}", settings.m2); - return false; - } - if (settings.p1 < limits.p1.min || settings.p1 > limits.p1.max) { - dbgln_if(INTEL_GRAPHICS_DEBUG, "p1 is invalid {}", settings.p1); - return false; - } - - if (settings.m1 <= settings.m2) { - dbgln_if(INTEL_GRAPHICS_DEBUG, "m2 is invalid {} as it is bigger than m1 {}", settings.m2, settings.m1); - return false; - } - - auto m = settings.compute_m(); - auto p = settings.compute_p(); - - if (m < limits.m.min || m > limits.m.max) { - dbgln_if(INTEL_GRAPHICS_DEBUG, "m invalid {}", m); - return false; - } - if (p < limits.p.min || p > limits.p.max) { - dbgln_if(INTEL_GRAPHICS_DEBUG, "p invalid {}", p); - return false; - } - - auto dot = settings.compute_dot_clock(reference_clock); - auto vco = settings.compute_vco(reference_clock); - - if (dot < limits.dot_clock.min || dot > limits.dot_clock.max) { - dbgln_if(INTEL_GRAPHICS_DEBUG, "Dot clock invalid {}", dot); - return false; - } - if (vco < limits.vco.min || vco > limits.vco.max) { - dbgln_if(INTEL_GRAPHICS_DEBUG, "VCO clock invalid {}", vco); - return false; - } - return true; -} - -} diff --git a/Kernel/Devices/GPU/Intel/Transcoder/PLL.h b/Kernel/Devices/GPU/Intel/Transcoder/PLL.h deleted file mode 100644 index 7f9488553a4..00000000000 --- a/Kernel/Devices/GPU/Intel/Transcoder/PLL.h +++ /dev/null @@ -1,18 +0,0 @@ -/* - * Copyright (c) 2023, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include - -namespace Kernel::IntelGraphics { - -PLLMaxSettings const& pll_max_settings_for_generation(Generation); -Optional create_pll_settings(Generation, u64 target_frequency, u64 reference_clock); -bool check_pll_settings(PLLSettings const& settings, size_t reference_clock, PLLMaxSettings const& limits); - -} diff --git a/Kernel/Devices/GPU/Management.cpp b/Kernel/Devices/GPU/Management.cpp deleted file mode 100644 index 7e58121b296..00000000000 --- a/Kernel/Devices/GPU/Management.cpp +++ /dev/null @@ -1,278 +0,0 @@ -/* - * Copyright (c) 2021, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#if ARCH(X86_64) -# include -#endif -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace Kernel { - -static Singleton s_the; - -extern Atomic g_boot_console; - -GraphicsManagement& GraphicsManagement::the() -{ - return *s_the; -} - -bool GraphicsManagement::is_initialized() -{ - return s_the.is_initialized(); -} - -UNMAP_AFTER_INIT GraphicsManagement::GraphicsManagement() -{ -} - -void GraphicsManagement::disable_vga_emulation_access_permanently() -{ -#if ARCH(X86_64) - if (!m_vga_arbiter) - return; - m_vga_arbiter->disable_vga_emulation_access_permanently({}); -#endif -} - -void GraphicsManagement::enable_vga_text_mode_console_cursor() -{ -#if ARCH(X86_64) - if (!m_vga_arbiter) - return; - m_vga_arbiter->enable_vga_text_mode_console_cursor({}); -#endif -} - -void GraphicsManagement::disable_vga_text_mode_console_cursor() -{ -#if ARCH(X86_64) - if (!m_vga_arbiter) - return; - m_vga_arbiter->disable_vga_text_mode_console_cursor({}); -#endif -} - -void GraphicsManagement::set_vga_text_mode_cursor([[maybe_unused]] size_t console_width, [[maybe_unused]] size_t x, [[maybe_unused]] size_t y) -{ -#if ARCH(X86_64) - if (!m_vga_arbiter) - return; - m_vga_arbiter->set_vga_text_mode_cursor({}, console_width, x, y); -#endif -} - -void GraphicsManagement::deactivate_graphical_mode() -{ - return m_display_connector_nodes.with([&](auto& display_connectors) { - for (auto& connector : display_connectors) - connector.set_display_mode({}, DisplayConnector::DisplayMode::Console); - }); -} -void GraphicsManagement::activate_graphical_mode() -{ - return m_display_connector_nodes.with([&](auto& display_connectors) { - for (auto& connector : display_connectors) - connector.set_display_mode({}, DisplayConnector::DisplayMode::Graphical); - }); -} - -void GraphicsManagement::attach_new_display_connector(Badge, DisplayConnector& connector) -{ - return m_display_connector_nodes.with([&](auto& display_connectors) { - display_connectors.append(connector); - }); -} -void GraphicsManagement::detach_display_connector(Badge, DisplayConnector& connector) -{ - return m_display_connector_nodes.with([&](auto& display_connectors) { - display_connectors.remove(connector); - }); -} - -static inline bool is_vga_compatible_pci_device(PCI::DeviceIdentifier const& device_identifier) -{ - // Note: Check for Display Controller, VGA Compatible Controller or - // Unclassified, VGA-Compatible Unclassified Device - auto is_display_controller_vga_compatible = device_identifier.class_code() == PCI::ClassID::Display && device_identifier.subclass_code() == PCI::Display::SubclassID::VGA; - auto is_general_pci_vga_compatible = device_identifier.class_code() == PCI::ClassID::Legacy && device_identifier.subclass_code() == PCI::Legacy::SubclassID::VGACompatible; - return is_display_controller_vga_compatible || is_general_pci_vga_compatible; -} - -static inline bool is_display_controller_pci_device(PCI::DeviceIdentifier const& device_identifier) -{ - return device_identifier.class_code() == PCI::ClassID::Display; -} - -struct PCIGraphicsDriverInitializer { - ErrorOr (*probe)(PCI::DeviceIdentifier const&) = nullptr; - ErrorOr> (*create)(PCI::DeviceIdentifier const&) = nullptr; -}; - -static constexpr PCIGraphicsDriverInitializer s_initializers[] = { - { IntelNativeGraphicsAdapter::probe, IntelNativeGraphicsAdapter::create }, - { BochsGraphicsAdapter::probe, BochsGraphicsAdapter::create }, - { VirtIOGraphicsAdapter::probe, VirtIOGraphicsAdapter::create }, - { VMWareGraphicsAdapter::probe, VMWareGraphicsAdapter::create }, - { VoodooGraphicsAdapter::probe, VoodooGraphicsAdapter::create }, -}; - -UNMAP_AFTER_INIT ErrorOr GraphicsManagement::determine_and_initialize_graphics_device(PCI::DeviceIdentifier const& device_identifier) -{ - VERIFY(is_vga_compatible_pci_device(device_identifier) || is_display_controller_pci_device(device_identifier)); - for (auto& initializer : s_initializers) { - auto initializer_probe_found_driver_match_or_error = initializer.probe(device_identifier); - if (initializer_probe_found_driver_match_or_error.is_error()) { - dmesgln("Graphics: Failed to probe device {}, due to {}", device_identifier.address(), initializer_probe_found_driver_match_or_error.error()); - continue; - } - auto initializer_probe_found_driver_match = initializer_probe_found_driver_match_or_error.release_value(); - if (initializer_probe_found_driver_match) { - auto adapter = TRY(initializer.create(device_identifier)); - TRY(m_graphics_devices.try_append(*adapter)); - return {}; - } - } - return {}; -} - -UNMAP_AFTER_INIT void GraphicsManagement::initialize_preset_resolution_generic_display_connector() -{ - VERIFY(!multiboot_framebuffer_addr.is_null()); - VERIFY(multiboot_framebuffer_type == MULTIBOOT_FRAMEBUFFER_TYPE_RGB); - dmesgln("Graphics: Using a preset resolution from the bootloader, without knowing the PCI device"); - m_preset_resolution_generic_display_connector = GenericDisplayConnector::must_create_with_preset_resolution( - multiboot_framebuffer_addr, - multiboot_framebuffer_width, - multiboot_framebuffer_height, - multiboot_framebuffer_pitch); -} - -UNMAP_AFTER_INIT bool GraphicsManagement::initialize() -{ - - /* Explanation on the flow here: - * - * If the user chose to disable graphics support entirely, then all we can do - * is to set up a plain old VGA text console and exit this function. - * Otherwise, we either try to find a device that we natively support so - * we can initialize it, and in case we don't find any device to initialize, - * we try to initialize a simple DisplayConnector to support a pre-initialized - * framebuffer. - * - * Note: If the user disabled PCI access, the kernel behaves like it's running - * on a pure ISA PC machine and therefore the kernel will try to initialize - * a variant that is suitable for ISA VGA handling, and not PCI adapters. - */ - - ScopeGuard assign_console_on_initialization_exit([this] { - if (!m_console) { - // If no graphics driver was instantiated and we had a bootloader provided - // framebuffer console we can simply re-use it. - if (auto* boot_console = g_boot_console.load()) { - m_console = *boot_console; - boot_console->unref(); // Drop the leaked reference from Kernel::init() - } - } - }); -#if ARCH(X86_64) - m_vga_arbiter = VGAIOArbiter::must_create({}); -#endif - - auto graphics_subsystem_mode = kernel_command_line().graphics_subsystem_mode(); - if (graphics_subsystem_mode == CommandLine::GraphicsSubsystemMode::Disabled) { - VERIFY(!m_console); - return true; - } - - // Note: Don't try to initialize an ISA Bochs VGA adapter if PCI hardware is - // present but the user decided to disable its usage nevertheless. - // Otherwise we risk using the Bochs VBE driver on a wrong physical address - // for the framebuffer. - if (PCI::Access::is_hardware_disabled() && !(graphics_subsystem_mode == CommandLine::GraphicsSubsystemMode::Limited && !multiboot_framebuffer_addr.is_null() && multiboot_framebuffer_type == MULTIBOOT_FRAMEBUFFER_TYPE_RGB)) { -#if ARCH(X86_64) - auto vga_isa_bochs_display_connector = BochsDisplayConnector::try_create_for_vga_isa_connector(); - if (vga_isa_bochs_display_connector) { - dmesgln("Graphics: Using a Bochs ISA VGA compatible adapter"); - MUST(vga_isa_bochs_display_connector->set_safe_mode_setting()); - m_platform_board_specific_display_connector = vga_isa_bochs_display_connector; - dmesgln("Graphics: Invoking manual blanking with VGA ISA ports"); - m_vga_arbiter->unblank_screen({}); - return true; - } -#endif - } - - if (graphics_subsystem_mode == CommandLine::GraphicsSubsystemMode::Limited && !multiboot_framebuffer_addr.is_null() && multiboot_framebuffer_type == MULTIBOOT_FRAMEBUFFER_TYPE_RGB) { - initialize_preset_resolution_generic_display_connector(); - return true; - } - - if (!PCI::Access::is_disabled()) { - MUST(PCI::enumerate([&](PCI::DeviceIdentifier const& device_identifier) { - // Note: Each graphics controller will try to set its native screen resolution - // upon creation. Later on, if we don't want to have framebuffer devices, a - // framebuffer console will take the control instead. - if (!is_vga_compatible_pci_device(device_identifier) && !is_display_controller_pci_device(device_identifier)) - return; - if (auto result = determine_and_initialize_graphics_device(device_identifier); result.is_error()) - dbgln("Failed to initialize device {}, due to {}", device_identifier.address(), result.error()); - })); - } else { -#if ARCH(X86_64) - dmesgln("Graphics: Using an assumed-to-exist ISA VGA compatible generic adapter"); - return true; -#endif - } - - // Note: If we failed to find any graphics device to be used natively, but the - // bootloader prepared a framebuffer for us to use, then just create a DisplayConnector - // for it so the user can still use the system in graphics mode. - // Prekernel sets the framebuffer address to 0 if MULTIBOOT_INFO_FRAMEBUFFER_INFO - // is not present, as there is likely never a valid framebuffer at this physical address. - // Note: We only support RGB framebuffers. Any other format besides RGBX (and RGBA) or BGRX (and BGRA) is obsolete - // and is not useful for us. - if (m_graphics_devices.is_empty() && !multiboot_framebuffer_addr.is_null() && multiboot_framebuffer_type == MULTIBOOT_FRAMEBUFFER_TYPE_RGB) { - initialize_preset_resolution_generic_display_connector(); - return true; - } - - if (m_graphics_devices.is_empty()) { - dbgln("No graphics adapter was initialized."); - return false; - } - return true; -} - -void GraphicsManagement::set_console(Graphics::Console& console) -{ - m_console = console; - - if (auto* boot_console = g_boot_console.exchange(nullptr)) { - // Disable the initial boot framebuffer console permanently - boot_console->disable(); - // TODO: Even though we swapped the pointer and disabled the console - // we technically can't safely destroy it as other CPUs might still - // try to use it. Once we solve this problem we can drop the reference - // that we intentionally leaked in Kernel::init(). - } -} - -} diff --git a/Kernel/Devices/GPU/Management.h b/Kernel/Devices/GPU/Management.h deleted file mode 100644 index cbbfd81572a..00000000000 --- a/Kernel/Devices/GPU/Management.h +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright (c) 2021-2022, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#if ARCH(X86_64) -# include -#endif -#include -#include -#include -#include -#include -#include -#include -#include - -namespace Kernel { - -class GraphicsManagement { - -public: - static GraphicsManagement& the(); - static bool is_initialized(); - bool initialize(); - - unsigned allocate_minor_device_number() { return m_current_minor_number++; } - GraphicsManagement(); - - void attach_new_display_connector(Badge, DisplayConnector&); - void detach_display_connector(Badge, DisplayConnector&); - - void set_vga_text_mode_cursor(size_t console_width, size_t x, size_t y); - void disable_vga_text_mode_console_cursor(); - void disable_vga_emulation_access_permanently(); - - LockRefPtr console() const { return m_console; } - void set_console(Graphics::Console&); - - void deactivate_graphical_mode(); - void activate_graphical_mode(); - -private: - void enable_vga_text_mode_console_cursor(); - - ErrorOr determine_and_initialize_graphics_device(PCI::DeviceIdentifier const&); - - void initialize_preset_resolution_generic_display_connector(); - - Vector> m_graphics_devices; - LockRefPtr m_console; - - // Note: This is only used when booting with kernel commandline that includes "graphics_subsystem_mode=limited" - LockRefPtr m_preset_resolution_generic_display_connector; - - LockRefPtr m_platform_board_specific_display_connector; - - unsigned m_current_minor_number { 0 }; - - SpinlockProtected, LockRank::None> m_display_connector_nodes {}; -#if ARCH(X86_64) - OwnPtr m_vga_arbiter; -#endif -}; - -} diff --git a/Kernel/Devices/GPU/VMWare/Console.cpp b/Kernel/Devices/GPU/VMWare/Console.cpp deleted file mode 100644 index 76fc9bef209..00000000000 --- a/Kernel/Devices/GPU/VMWare/Console.cpp +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Copyright (c) 2022, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include - -namespace Kernel { - -constexpr static AK::Duration refresh_interval = AK::Duration::from_milliseconds(16); - -NonnullLockRefPtr VMWareFramebufferConsole::initialize(VMWareDisplayConnector& parent_display_connector) -{ - auto current_resolution = parent_display_connector.current_mode_setting(); - return adopt_lock_ref(*new (nothrow) VMWareFramebufferConsole(parent_display_connector, current_resolution)); -} - -VMWareFramebufferConsole::VMWareFramebufferConsole(VMWareDisplayConnector const& parent_display_connector, DisplayConnector::ModeSetting current_resolution) - : GenericFramebufferConsole(current_resolution.horizontal_active, current_resolution.vertical_active, current_resolution.horizontal_stride) - , m_parent_display_connector(parent_display_connector) -{ - enqueue_refresh_timer(); -} - -void VMWareFramebufferConsole::set_resolution(size_t width, size_t height, size_t pitch) -{ - m_width = width; - m_height = height; - m_pitch = pitch; - m_dirty = true; -} - -void VMWareFramebufferConsole::flush(size_t, size_t, size_t, size_t) -{ - m_dirty = true; -} - -void VMWareFramebufferConsole::enqueue_refresh_timer() -{ - auto refresh_timer = adopt_nonnull_ref_or_enomem(new (nothrow) Timer()).release_value_but_fixme_should_propagate_errors(); - refresh_timer->setup(CLOCK_MONOTONIC, refresh_interval, [this]() { - if (m_enabled.load() && m_dirty) { - MUST(g_io_work->try_queue([this]() { - MUST(m_parent_display_connector->flush_first_surface()); - m_dirty = false; - })); - } - enqueue_refresh_timer(); - }); - TimerQueue::the().add_timer(move(refresh_timer)); -} - -void VMWareFramebufferConsole::enable() -{ - auto current_resolution = m_parent_display_connector->current_mode_setting(); - GenericFramebufferConsole::enable(); - m_width = current_resolution.horizontal_active; - m_height = current_resolution.vertical_active; - m_pitch = current_resolution.horizontal_stride; -} - -u8* VMWareFramebufferConsole::framebuffer_data() -{ - return m_parent_display_connector->framebuffer_data(); -} - -} diff --git a/Kernel/Devices/GPU/VMWare/Console.h b/Kernel/Devices/GPU/VMWare/Console.h deleted file mode 100644 index c1e0366a04d..00000000000 --- a/Kernel/Devices/GPU/VMWare/Console.h +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright (c) 2022, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include - -namespace Kernel { - -class VMWareFramebufferConsole final : public Graphics::GenericFramebufferConsole { -public: - static NonnullLockRefPtr initialize(VMWareDisplayConnector& parent_display_connector); - - virtual void set_resolution(size_t width, size_t height, size_t pitch) override; - virtual void flush(size_t x, size_t y, size_t width, size_t height) override; - virtual void enable() override; - -private: - void enqueue_refresh_timer(); - virtual u8* framebuffer_data() override; - - VMWareFramebufferConsole(VMWareDisplayConnector const& parent_display_connector, DisplayConnector::ModeSetting current_resolution); - LockRefPtr m_parent_display_connector; - bool m_dirty { false }; -}; - -} diff --git a/Kernel/Devices/GPU/VMWare/Definitions.h b/Kernel/Devices/GPU/VMWare/Definitions.h deleted file mode 100644 index dfbdb8769df..00000000000 --- a/Kernel/Devices/GPU/VMWare/Definitions.h +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright (c) 2022, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include - -namespace Kernel { - -static constexpr size_t vmware_svga_version_2_id = (0x900000UL << 8 | (2)); - -enum class VMWareDisplayRegistersOffset { - ID = 0, - ENABLE = 1, - WIDTH = 2, - HEIGHT = 3, - MAX_WIDTH = 4, - MAX_HEIGHT = 5, - DEPTH = 6, - BITS_PER_PIXEL = 7, /* Current bpp in the guest */ - PSEUDOCOLOR = 8, - RED_MASK = 9, - GREEN_MASK = 10, - BLUE_MASK = 11, - BYTES_PER_LINE = 12, - FB_OFFSET = 14, - VRAM_SIZE = 15, - FB_SIZE = 16, - - CAPABILITIES = 17, - MEM_SIZE = 19, - CONFIG_DONE = 20, /* Set when memory area configured */ - SYNC = 21, /* See "FIFO Synchronization Registers" */ - BUSY = 22, /* See "FIFO Synchronization Registers" */ - SCRATCH_SIZE = 29, /* Number of scratch registers */ - MEM_REGS = 30, /* Number of FIFO registers */ - PITCHLOCK = 32, /* Fixed pitch for all modes */ - IRQMASK = 33, /* Interrupt mask */ - - GMR_ID = 41, - GMR_DESCRIPTOR = 42, - GMR_MAX_IDS = 43, - GMR_MAX_DESCRIPTOR_LENGTH = 44, - - TRACES = 45, /* Enable trace-based updates even when FIFO is on */ - GMRS_MAX_PAGES = 46, /* Maximum number of 4KB pages for all GMRs */ - MEMORY_SIZE = 47, /* Total dedicated device memory excluding FIFO */ -}; - -struct [[gnu::packed]] VMWareDisplayFIFORegisters { - u32 start; - u32 size; - u32 next_command; - u32 stop; - u32 commands[]; -}; - -} diff --git a/Kernel/Devices/GPU/VMWare/DisplayConnector.cpp b/Kernel/Devices/GPU/VMWare/DisplayConnector.cpp deleted file mode 100644 index 07e76d217a0..00000000000 --- a/Kernel/Devices/GPU/VMWare/DisplayConnector.cpp +++ /dev/null @@ -1,133 +0,0 @@ -/* - * Copyright (c) 2022, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include - -namespace Kernel { - -NonnullLockRefPtr VMWareDisplayConnector::must_create(VMWareGraphicsAdapter const& parent_adapter, PhysicalAddress framebuffer_address, size_t framebuffer_resource_size) -{ - auto connector = MUST(DeviceManagement::try_create_device(parent_adapter, framebuffer_address, framebuffer_resource_size)); - MUST(connector->create_attached_framebuffer_console()); - MUST(connector->initialize_edid_for_generic_monitor(Array { 'V', 'M', 'W' })); - return connector; -} - -ErrorOr VMWareDisplayConnector::create_attached_framebuffer_console() -{ - m_framebuffer_console = VMWareFramebufferConsole::initialize(*this); - GraphicsManagement::the().set_console(*m_framebuffer_console); - return {}; -} - -VMWareDisplayConnector::VMWareDisplayConnector(VMWareGraphicsAdapter const& parent_adapter, PhysicalAddress framebuffer_address, size_t framebuffer_resource_size) - : DisplayConnector(framebuffer_address, framebuffer_resource_size, false) - , m_parent_adapter(parent_adapter) -{ -} - -ErrorOr VMWareDisplayConnector::set_safe_mode_setting() -{ - // We assume safe resolution is 1024x768x32 - DisplayConnector::ModeSetting safe_mode_setting { - .horizontal_stride = 1024 * sizeof(u32), - .pixel_clock_in_khz = 0, // Note: There's no pixel clock in paravirtualized hardware - .horizontal_active = 1024, - .horizontal_front_porch_pixels = 0, // Note: There's no horizontal_front_porch_pixels in paravirtualized hardware - .horizontal_sync_time_pixels = 0, // Note: There's no horizontal_sync_time_pixels in paravirtualized hardware - .horizontal_blank_pixels = 0, // Note: There's no horizontal_blank_pixels in paravirtualized hardware - .vertical_active = 768, - .vertical_front_porch_lines = 0, // Note: There's no vertical_front_porch_lines in paravirtualized hardware - .vertical_sync_time_lines = 0, // Note: There's no vertical_sync_time_lines in paravirtualized hardware - .vertical_blank_lines = 0, // Note: There's no vertical_blank_lines in paravirtualized hardware - .horizontal_offset = 0, - .vertical_offset = 0, - }; - return set_mode_setting(safe_mode_setting); -} - -ErrorOr VMWareDisplayConnector::unblank() -{ - return Error::from_errno(ENOTIMPL); -} - -void VMWareDisplayConnector::enable_console() -{ - VERIFY(m_control_lock.is_locked()); - VERIFY(m_framebuffer_console); - m_framebuffer_console->enable(); -} - -void VMWareDisplayConnector::disable_console() -{ - VERIFY(m_control_lock.is_locked()); - VERIFY(m_framebuffer_console); - m_framebuffer_console->disable(); -} - -ErrorOr VMWareDisplayConnector::flush_first_surface() -{ - // FIXME: Cache these values but keep them in sync with the parent adapter. - auto width = m_parent_adapter->primary_screen_width({}); - auto height = m_parent_adapter->primary_screen_height({}); - m_parent_adapter->primary_screen_flush({}, width, height); - return {}; -} - -ErrorOr VMWareDisplayConnector::set_y_offset(size_t) -{ - return Error::from_errno(ENOTSUP); -} - -ErrorOr VMWareDisplayConnector::flush_rectangle(size_t, FBRect const&) -{ - // FIXME: It costs really nothing to flush the entire screen (at least in QEMU). - // Try to implement better partial rectangle flush method instead here. - VERIFY(m_flushing_lock.is_locked()); - // FIXME: Cache these values but keep them in sync with the parent adapter. - auto width = m_parent_adapter->primary_screen_width({}); - auto height = m_parent_adapter->primary_screen_height({}); - m_parent_adapter->primary_screen_flush({}, width, height); - return {}; -} - -ErrorOr VMWareDisplayConnector::set_mode_setting(ModeSetting const& mode_setting) -{ - SpinlockLocker locker(m_modeset_lock); - VERIFY(m_framebuffer_console); - size_t width = mode_setting.horizontal_active; - size_t height = mode_setting.vertical_active; - - if (Checked::multiplication_would_overflow(width, height, sizeof(u32))) - return EOVERFLOW; - - TRY(m_parent_adapter->modeset_primary_screen_resolution({}, width, height)); - - m_framebuffer_console->set_resolution(width, height, width * sizeof(u32)); - - auto pitch = m_parent_adapter->primary_screen_pitch({}); - DisplayConnector::ModeSetting current_mode_setting { - .horizontal_stride = pitch, - .pixel_clock_in_khz = 0, // Note: There's no pixel clock in paravirtualized hardware - .horizontal_active = width, - .horizontal_front_porch_pixels = 0, // Note: There's no horizontal_front_porch_pixels in paravirtualized hardware - .horizontal_sync_time_pixels = 0, // Note: There's no horizontal_sync_time_pixels in paravirtualized hardware - .horizontal_blank_pixels = 0, // Note: There's no horizontal_blank_pixels in paravirtualized hardware - .vertical_active = height, - .vertical_front_porch_lines = 0, // Note: There's no vertical_front_porch_lines in paravirtualized hardware - .vertical_sync_time_lines = 0, // Note: There's no vertical_sync_time_lines in paravirtualized hardware - .vertical_blank_lines = 0, // Note: There's no vertical_blank_lines in paravirtualized hardware - .horizontal_offset = 0, - .vertical_offset = 0, - }; - m_current_mode_setting = current_mode_setting; - return {}; -} - -} diff --git a/Kernel/Devices/GPU/VMWare/DisplayConnector.h b/Kernel/Devices/GPU/VMWare/DisplayConnector.h deleted file mode 100644 index 5275e7e7773..00000000000 --- a/Kernel/Devices/GPU/VMWare/DisplayConnector.h +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright (c) 2022, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include -#include -#include - -namespace Kernel { - -class VMWareFramebufferConsole; -class VMWareDisplayConnector : public DisplayConnector { - friend class VMWareGraphicsAdapter; - friend class VMWareFramebufferConsole; - friend class DeviceManagement; - -public: - static NonnullLockRefPtr must_create(VMWareGraphicsAdapter const& parent_adapter, PhysicalAddress framebuffer_address, size_t framebuffer_resource_size); - -private: - VMWareDisplayConnector(VMWareGraphicsAdapter const& parent_adapter, PhysicalAddress framebuffer_address, size_t framebuffer_resource_size); - ErrorOr create_attached_framebuffer_console(); - - virtual bool mutable_mode_setting_capable() const override { return true; } - virtual bool double_framebuffering_capable() const override { return false; } - virtual ErrorOr set_mode_setting(ModeSetting const&) override; - virtual ErrorOr set_safe_mode_setting() override; - virtual ErrorOr set_y_offset(size_t y) override; - virtual ErrorOr unblank() override; - - virtual bool partial_flush_support() const override { return true; } - virtual bool flush_support() const override { return true; } - // Note: Paravirtualized hardware doesn't require a defined refresh rate for modesetting. - virtual bool refresh_rate_support() const override { return false; } - - virtual ErrorOr flush_first_surface() override; - virtual ErrorOr flush_rectangle(size_t buffer_index, FBRect const& rect) override; - - virtual void enable_console() override; - virtual void disable_console() override; - -private: - NonnullLockRefPtr m_parent_adapter; - LockRefPtr m_framebuffer_console; -}; -} diff --git a/Kernel/Devices/GPU/VMWare/GraphicsAdapter.cpp b/Kernel/Devices/GPU/VMWare/GraphicsAdapter.cpp deleted file mode 100644 index 597f4259c0b..00000000000 --- a/Kernel/Devices/GPU/VMWare/GraphicsAdapter.cpp +++ /dev/null @@ -1,194 +0,0 @@ -/* - * Copyright (c) 2021, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace Kernel { - -ErrorOr VMWareGraphicsAdapter::probe(PCI::DeviceIdentifier const& pci_device_identifier) -{ - PCI::HardwareID id = pci_device_identifier.hardware_id(); - // Note: We only support VMWare SVGA II adapter - return id.vendor_id == PCI::VendorID::VMWare && id.device_id == 0x0405; -} - -ErrorOr> VMWareGraphicsAdapter::create(PCI::DeviceIdentifier const& pci_device_identifier) -{ - auto registers_io_window = TRY(IOWindow::create_for_pci_device_bar(pci_device_identifier, PCI::HeaderType0BaseRegister::BAR0)); - auto adapter = TRY(adopt_nonnull_lock_ref_or_enomem(new (nothrow) VMWareGraphicsAdapter(pci_device_identifier, move(registers_io_window)))); - TRY(adapter->initialize_adapter()); - return adapter; -} - -UNMAP_AFTER_INIT VMWareGraphicsAdapter::VMWareGraphicsAdapter(PCI::DeviceIdentifier const& pci_device_identifier, NonnullOwnPtr registers_io_window) - : PCI::Device(const_cast(pci_device_identifier)) - , m_registers_io_window(move(registers_io_window)) -{ - dbgln("VMWare SVGA @ {}, {}", pci_device_identifier.address(), m_registers_io_window); -} - -u32 VMWareGraphicsAdapter::read_io_register(VMWareDisplayRegistersOffset register_offset) const -{ - SpinlockLocker locker(m_io_access_lock); - m_registers_io_window->write32(0, to_underlying(register_offset)); - return m_registers_io_window->read32_unaligned(1); -} -void VMWareGraphicsAdapter::write_io_register(VMWareDisplayRegistersOffset register_offset, u32 value) -{ - SpinlockLocker locker(m_io_access_lock); - m_registers_io_window->write32(0, to_underlying(register_offset)); - m_registers_io_window->write32_unaligned(1, value); -} - -UNMAP_AFTER_INIT ErrorOr VMWareGraphicsAdapter::negotiate_device_version() -{ - write_io_register(VMWareDisplayRegistersOffset::ID, vmware_svga_version_2_id); - auto accepted_version = read_io_register(VMWareDisplayRegistersOffset::ID); - dbgln("VMWare SVGA @ {}: Accepted version {}", device_identifier().address(), accepted_version); - if (read_io_register(VMWareDisplayRegistersOffset::ID) == vmware_svga_version_2_id) - return {}; - return Error::from_errno(ENOTSUP); -} - -UNMAP_AFTER_INIT ErrorOr VMWareGraphicsAdapter::initialize_fifo_registers() -{ - auto framebuffer_size = read_io_register(VMWareDisplayRegistersOffset::FB_SIZE); - auto fifo_size = read_io_register(VMWareDisplayRegistersOffset::MEM_SIZE); - auto fifo_physical_address = TRY(PCI::get_bar_address(device_identifier(), PCI::HeaderType0BaseRegister::BAR2)); - - dbgln("VMWare SVGA @ {}: framebuffer size {} bytes, FIFO size {} bytes @ {}", device_identifier().address(), framebuffer_size, fifo_size, fifo_physical_address); - if (framebuffer_size < 0x100000 || fifo_size < 0x10000) { - dbgln("VMWare SVGA @ {}: invalid framebuffer or fifo size", device_identifier().address()); - return Error::from_errno(ENOTSUP); - } - - m_fifo_registers = TRY(PCI::map_bar(device_identifier(), PCI::HeaderType0BaseRegister::BAR2, fifo_size)); - m_fifo_registers->start = 16; - m_fifo_registers->size = 16 + (10 * 1024); - m_fifo_registers->next_command = 16; - m_fifo_registers->stop = 16; - return {}; -} - -UNMAP_AFTER_INIT void VMWareGraphicsAdapter::print_svga_capabilities() const -{ - auto svga_capabilities = read_io_register(VMWareDisplayRegistersOffset::CAPABILITIES); - dbgln("VMWare SVGA capabilities (raw {:x}):", svga_capabilities); - if (svga_capabilities & (1 << 1)) - dbgln("\tRect copy"); - if (svga_capabilities & (1 << 5)) - dbgln("\tCursor"); - if (svga_capabilities & (1 << 6)) - dbgln("\tCursor Bypass"); - if (svga_capabilities & (1 << 7)) - dbgln("\tCursor Bypass 2"); - if (svga_capabilities & (1 << 8)) - dbgln("\t8 Bit emulation"); - if (svga_capabilities & (1 << 9)) - dbgln("\tAlpha Cursor"); - if (svga_capabilities & (1 << 14)) - dbgln("\t3D acceleration"); - if (svga_capabilities & (1 << 15)) - dbgln("\tExtended FIFO"); - if (svga_capabilities & (1 << 16)) - dbgln("\tMulti-monitor (legacy)"); - if (svga_capabilities & (1 << 17)) - dbgln("\tPitch lock"); - if (svga_capabilities & (1 << 18)) - dbgln("\tIRQ masking"); - if (svga_capabilities & (1 << 19)) - dbgln("\tDisplay topology"); - if (svga_capabilities & (1 << 20)) - dbgln("\tGMR"); - if (svga_capabilities & (1 << 21)) - dbgln("\tTraces"); - if (svga_capabilities & (1 << 22)) - dbgln("\tGMR2"); - if (svga_capabilities & (1 << 23)) - dbgln("\tScreen object 2"); -} - -ErrorOr VMWareGraphicsAdapter::modeset_primary_screen_resolution(Badge, size_t width, size_t height) -{ - auto max_width = read_io_register(VMWareDisplayRegistersOffset::MAX_WIDTH); - auto max_height = read_io_register(VMWareDisplayRegistersOffset::MAX_HEIGHT); - if (width > max_width || height > max_height) - return Error::from_errno(ENOTSUP); - modeset_primary_screen_resolution(width, height); - return {}; -} - -size_t VMWareGraphicsAdapter::primary_screen_width(Badge) const -{ - SpinlockLocker locker(m_operation_lock); - return read_io_register(VMWareDisplayRegistersOffset::WIDTH); -} -size_t VMWareGraphicsAdapter::primary_screen_height(Badge) const -{ - SpinlockLocker locker(m_operation_lock); - return read_io_register(VMWareDisplayRegistersOffset::HEIGHT); -} -size_t VMWareGraphicsAdapter::primary_screen_pitch(Badge) const -{ - SpinlockLocker locker(m_operation_lock); - return read_io_register(VMWareDisplayRegistersOffset::BYTES_PER_LINE); -} - -void VMWareGraphicsAdapter::primary_screen_flush(Badge, size_t current_width, size_t current_height) -{ - SpinlockLocker locker(m_operation_lock); - m_fifo_registers->start = 16; - m_fifo_registers->size = 16 + (10 * 1024); - m_fifo_registers->next_command = 16 + 4 * 5; - m_fifo_registers->stop = 16; - m_fifo_registers->commands[0] = 1; - m_fifo_registers->commands[1] = 0; - m_fifo_registers->commands[2] = 0; - m_fifo_registers->commands[3] = current_width; - m_fifo_registers->commands[4] = current_height; - write_io_register(VMWareDisplayRegistersOffset::SYNC, 1); -} - -void VMWareGraphicsAdapter::modeset_primary_screen_resolution(size_t width, size_t height) -{ - SpinlockLocker locker(m_operation_lock); - write_io_register(VMWareDisplayRegistersOffset::ENABLE, 0); - write_io_register(VMWareDisplayRegistersOffset::WIDTH, width); - write_io_register(VMWareDisplayRegistersOffset::HEIGHT, height); - write_io_register(VMWareDisplayRegistersOffset::BITS_PER_PIXEL, 32); - write_io_register(VMWareDisplayRegistersOffset::ENABLE, 1); - write_io_register(VMWareDisplayRegistersOffset::CONFIG_DONE, 1); -} - -UNMAP_AFTER_INIT ErrorOr VMWareGraphicsAdapter::initialize_adapter() -{ - TRY(negotiate_device_version()); - print_svga_capabilities(); - TRY(initialize_fifo_registers()); - // Note: enable the device by modesetting the primary screen resolution - modeset_primary_screen_resolution(640, 480); - - auto bar1_space_size = PCI::get_BAR_space_size(device_identifier(), PCI::HeaderType0BaseRegister::BAR1); - - m_display_connector = VMWareDisplayConnector::must_create(*this, TRY(PCI::get_bar_address(device_identifier(), PCI::HeaderType0BaseRegister::BAR1)), bar1_space_size); - TRY(m_display_connector->set_safe_mode_setting()); - return {}; -} - -} diff --git a/Kernel/Devices/GPU/VMWare/GraphicsAdapter.h b/Kernel/Devices/GPU/VMWare/GraphicsAdapter.h deleted file mode 100644 index 6e0f9311c59..00000000000 --- a/Kernel/Devices/GPU/VMWare/GraphicsAdapter.h +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright (c) 2021, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include -#include -#include -#include - -namespace Kernel { - -class GraphicsManagement; - -class VMWareDisplayConnector; -class VMWareGraphicsAdapter final - : public GPUDevice - , public PCI::Device { - friend class GraphicsManagement; - -public: - static ErrorOr probe(PCI::DeviceIdentifier const&); - static ErrorOr> create(PCI::DeviceIdentifier const&); - virtual ~VMWareGraphicsAdapter() = default; - - virtual StringView device_name() const override { return "VMWareGraphicsAdapter"sv; } - - ErrorOr modeset_primary_screen_resolution(Badge, size_t width, size_t height); - size_t primary_screen_width(Badge) const; - size_t primary_screen_height(Badge) const; - size_t primary_screen_pitch(Badge) const; - void primary_screen_flush(Badge, size_t current_width, size_t current_height); - -private: - ErrorOr initialize_adapter(); - ErrorOr initialize_fifo_registers(); - ErrorOr negotiate_device_version(); - - u32 read_io_register(VMWareDisplayRegistersOffset) const; - void write_io_register(VMWareDisplayRegistersOffset, u32 value); - - void print_svga_capabilities() const; - void modeset_primary_screen_resolution(size_t width, size_t height); - - VMWareGraphicsAdapter(PCI::DeviceIdentifier const&, NonnullOwnPtr registers_io_window); - - Memory::TypedMapping m_fifo_registers; - LockRefPtr m_display_connector; - mutable NonnullOwnPtr m_registers_io_window; - mutable Spinlock m_io_access_lock {}; - mutable RecursiveSpinlock m_operation_lock {}; -}; - -} diff --git a/Kernel/Devices/GPU/VirtIO/Console.cpp b/Kernel/Devices/GPU/VirtIO/Console.cpp deleted file mode 100644 index 638054a1e41..00000000000 --- a/Kernel/Devices/GPU/VirtIO/Console.cpp +++ /dev/null @@ -1,103 +0,0 @@ -/* - * Copyright (c) 2021, Sahan Fernando - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include - -namespace Kernel::Graphics::VirtIOGPU { - -constexpr static AK::Duration refresh_interval = AK::Duration::from_milliseconds(16); - -NonnullLockRefPtr Console::initialize(VirtIODisplayConnector& parent_display_connector) -{ - auto current_resolution = parent_display_connector.current_mode_setting(); - return adopt_lock_ref(*new Console(parent_display_connector, current_resolution)); -} - -Console::Console(VirtIODisplayConnector const& parent_display_connector, DisplayConnector::ModeSetting current_resolution) - : GenericFramebufferConsole(current_resolution.horizontal_active, current_resolution.vertical_active, current_resolution.horizontal_stride) - , m_parent_display_connector(parent_display_connector) -{ - // NOTE: Clear the framebuffer, in case it's left with some garbage. - memset(framebuffer_data(), 0, current_resolution.horizontal_stride * current_resolution.vertical_active); - enqueue_refresh_timer(); -} - -void Console::set_resolution(size_t width, size_t height, size_t pitch) -{ - m_width = width; - m_height = height; - m_pitch = pitch; - - // Just to start cleanly, we clean the entire framebuffer - memset(framebuffer_data(), 0, pitch * height); - - ConsoleManagement::the().resolution_was_changed(); -} - -void Console::set_cursor(size_t x, size_t y) -{ - GenericFramebufferConsole::hide_cursor(); - m_x = x; - m_y = y; - GenericFramebufferConsole::show_cursor(); - m_dirty = true; -} - -void Console::hide_cursor() -{ - GenericFramebufferConsole::hide_cursor(); - m_dirty = true; -} - -void Console::show_cursor() -{ - GenericFramebufferConsole::show_cursor(); - m_dirty = true; -} - -void Console::flush(size_t, size_t, size_t, size_t) -{ - m_dirty = true; -} - -void Console::enqueue_refresh_timer() -{ - auto refresh_timer = adopt_nonnull_ref_or_enomem(new (nothrow) Timer()).release_value_but_fixme_should_propagate_errors(); - refresh_timer->setup(CLOCK_MONOTONIC, refresh_interval, [this]() { - if (m_enabled.load() && m_dirty) { - MUST(g_io_work->try_queue([this]() { - { - MutexLocker locker(m_parent_display_connector->m_flushing_lock); - MUST(m_parent_display_connector->flush_first_surface()); - } - m_dirty = false; - })); - } - enqueue_refresh_timer(); - }); - TimerQueue::the().add_timer(move(refresh_timer)); -} - -void Console::enable() -{ - // FIXME: Do we need some locking here to ensure the resolution doesn't change - // while we enable the console? - auto current_resolution = m_parent_display_connector->current_mode_setting(); - m_width = current_resolution.horizontal_active; - m_height = current_resolution.vertical_active; - m_pitch = current_resolution.horizontal_stride; - GenericFramebufferConsole::enable(); - m_dirty = true; -} - -u8* Console::framebuffer_data() -{ - return m_parent_display_connector->framebuffer_data(); -} - -} diff --git a/Kernel/Devices/GPU/VirtIO/Console.h b/Kernel/Devices/GPU/VirtIO/Console.h deleted file mode 100644 index 68802cb37e5..00000000000 --- a/Kernel/Devices/GPU/VirtIO/Console.h +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright (c) 2021, Sahan Fernando - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include - -namespace Kernel::Graphics::VirtIOGPU { - -class Console final : public GenericFramebufferConsole { -public: - static NonnullLockRefPtr initialize(VirtIODisplayConnector& parent_display_connector); - - virtual void set_resolution(size_t width, size_t height, size_t pitch) override; - virtual void flush(size_t x, size_t y, size_t width, size_t height) override; - virtual void enable() override; - - virtual void set_cursor(size_t x, size_t y) override; - -private: - void enqueue_refresh_timer(); - virtual u8* framebuffer_data() override; - - virtual void hide_cursor() override; - virtual void show_cursor() override; - - Console(VirtIODisplayConnector const& parent_display_connector, DisplayConnector::ModeSetting current_resolution); - NonnullLockRefPtr m_parent_display_connector; - bool m_dirty { false }; -}; - -} diff --git a/Kernel/Devices/GPU/VirtIO/DisplayConnector.cpp b/Kernel/Devices/GPU/VirtIO/DisplayConnector.cpp deleted file mode 100644 index 753764a0646..00000000000 --- a/Kernel/Devices/GPU/VirtIO/DisplayConnector.cpp +++ /dev/null @@ -1,201 +0,0 @@ -/* - * Copyright (c) 2021, Sahan Fernando - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -namespace Kernel { - -NonnullLockRefPtr VirtIODisplayConnector::must_create(VirtIOGraphicsAdapter& graphics_adapter, Graphics::VirtIOGPU::ScanoutID scanout_id) -{ - auto device_or_error = DeviceManagement::try_create_device(graphics_adapter, scanout_id); - VERIFY(!device_or_error.is_error()); - auto connector = device_or_error.release_value(); - return connector; -} - -static_assert((MAX_VIRTIOGPU_RESOLUTION_WIDTH * MAX_VIRTIOGPU_RESOLUTION_HEIGHT * sizeof(u32) * 2) % PAGE_SIZE == 0); - -VirtIODisplayConnector::VirtIODisplayConnector(VirtIOGraphicsAdapter& graphics_adapter, Graphics::VirtIOGPU::ScanoutID scanout_id) - : DisplayConnector((MAX_VIRTIOGPU_RESOLUTION_WIDTH * MAX_VIRTIOGPU_RESOLUTION_HEIGHT * sizeof(u32) * 2), false) - , m_graphics_adapter(graphics_adapter) - , m_scanout_id(scanout_id) -{ -} - -void VirtIODisplayConnector::initialize_console(Badge) -{ - m_console = Kernel::Graphics::VirtIOGPU::Console::initialize(*this); - GraphicsManagement::the().set_console(*m_console); -} - -void VirtIODisplayConnector::set_safe_mode_setting_after_initialization(Badge) -{ - MUST(set_safe_mode_setting()); -} - -ErrorOr VirtIODisplayConnector::set_mode_setting(ModeSetting const& mode_setting) -{ - SpinlockLocker locker(m_modeset_lock); - if (mode_setting.horizontal_active > MAX_VIRTIOGPU_RESOLUTION_WIDTH || mode_setting.vertical_active > MAX_VIRTIOGPU_RESOLUTION_HEIGHT) - return Error::from_errno(ENOTSUP); - - auto& info = m_display_info; - info.rect = { - .x = 0, - .y = 0, - .width = (u32)mode_setting.horizontal_active, - .height = (u32)mode_setting.vertical_active, - }; - - TRY(m_graphics_adapter->mode_set_resolution({}, *this, mode_setting.horizontal_active, mode_setting.vertical_active)); - - if (m_console) - m_console->set_resolution(info.rect.width, info.rect.height, info.rect.width * sizeof(u32)); - DisplayConnector::ModeSetting mode_set { - .horizontal_stride = info.rect.width * sizeof(u32), - .pixel_clock_in_khz = 0, // Note: There's no pixel clock in paravirtualized hardware - .horizontal_active = info.rect.width, - .horizontal_front_porch_pixels = 0, // Note: There's no horizontal_front_porch_pixels in paravirtualized hardware - .horizontal_sync_time_pixels = 0, // Note: There's no horizontal_sync_time_pixels in paravirtualized hardware - .horizontal_blank_pixels = 0, // Note: There's no horizontal_blank_pixels in paravirtualized hardware - .vertical_active = info.rect.height, - .vertical_front_porch_lines = 0, // Note: There's no vertical_front_porch_lines in paravirtualized hardware - .vertical_sync_time_lines = 0, // Note: There's no vertical_sync_time_lines in paravirtualized hardware - .vertical_blank_lines = 0, // Note: There's no vertical_blank_lines in paravirtualized hardware - .horizontal_offset = 0, - .vertical_offset = 0, - }; - m_current_mode_setting = mode_set; - - m_display_info.enabled = 1; - return {}; -} -ErrorOr VirtIODisplayConnector::set_safe_mode_setting() -{ - DisplayConnector::ModeSetting safe_mode_setting { - .horizontal_stride = 1024 * sizeof(u32), - .pixel_clock_in_khz = 0, // Note: There's no pixel clock in paravirtualized hardware - .horizontal_active = 1024, - .horizontal_front_porch_pixels = 0, // Note: There's no horizontal_front_porch_pixels in paravirtualized hardware - .horizontal_sync_time_pixels = 0, // Note: There's no horizontal_sync_time_pixels in paravirtualized hardware - .horizontal_blank_pixels = 0, // Note: There's no horizontal_blank_pixels in paravirtualized hardware - .vertical_active = 768, - .vertical_front_porch_lines = 0, // Note: There's no vertical_front_porch_lines in paravirtualized hardware - .vertical_sync_time_lines = 0, // Note: There's no vertical_sync_time_lines in paravirtualized hardware - .vertical_blank_lines = 0, // Note: There's no vertical_blank_lines in paravirtualized hardware - .horizontal_offset = 0, - .vertical_offset = 0, - }; - return set_mode_setting(safe_mode_setting); -} - -ErrorOr VirtIODisplayConnector::set_y_offset(size_t) -{ - // NOTE (FIXME?): We don't do double buffering because when using double buffering, - // perfomance visually looks terrible (everything look sluggish) compared to not using it, - // so until we figure out why (and we might not figure this and double buffering is simply not needed) - // this happens, we simply don't support it. - return Error::from_errno(ENOTSUP); -} -ErrorOr VirtIODisplayConnector::unblank() -{ - return Error::from_errno(ENOTIMPL); -} - -ErrorOr VirtIODisplayConnector::flush_rectangle(size_t buffer_index, FBRect const& rect) -{ - VERIFY(m_flushing_lock.is_locked()); - if (!is_valid_buffer_index(buffer_index)) - return Error::from_errno(EINVAL); - SpinlockLocker locker(m_graphics_adapter->operation_lock()); - Graphics::VirtIOGPU::Protocol::Rect dirty_rect { - .x = rect.x, - .y = rect.y, - .width = rect.width, - .height = rect.height - }; - - TRY(m_graphics_adapter->transfer_framebuffer_data_to_host({}, *this, dirty_rect, true)); - // Flushing directly to screen - TRY(flush_displayed_image(dirty_rect, true)); - return {}; -} - -ErrorOr VirtIODisplayConnector::flush_first_surface() -{ - VERIFY(m_flushing_lock.is_locked()); - SpinlockLocker locker(m_graphics_adapter->operation_lock()); - Graphics::VirtIOGPU::Protocol::Rect dirty_rect { - .x = 0, - .y = 0, - .width = m_display_info.rect.width, - .height = m_display_info.rect.height - }; - - TRY(m_graphics_adapter->transfer_framebuffer_data_to_host({}, *this, dirty_rect, true)); - // Flushing directly to screen - TRY(flush_displayed_image(dirty_rect, true)); - return {}; -} - -void VirtIODisplayConnector::enable_console() -{ - VERIFY(m_control_lock.is_locked()); - VERIFY(m_console); - m_console->enable(); -} - -void VirtIODisplayConnector::disable_console() -{ - VERIFY(m_control_lock.is_locked()); - VERIFY(m_console); - m_console->disable(); -} - -void VirtIODisplayConnector::set_edid_bytes(Badge, Array const& edid_bytes) -{ - DisplayConnector::set_edid_bytes(edid_bytes); -} - -Graphics::VirtIOGPU::Protocol::DisplayInfoResponse::Display VirtIODisplayConnector::display_information(Badge) const -{ - return m_display_info; -} - -void VirtIODisplayConnector::clear_to_black() -{ - size_t width = m_display_info.rect.width; - size_t height = m_display_info.rect.height; - u8* data = framebuffer_data(); - for (size_t i = 0; i < width * height; ++i) { - data[4 * i + 0] = 0x00; - data[4 * i + 1] = 0x00; - data[4 * i + 2] = 0x00; - data[4 * i + 3] = 0xff; - } -} - -ErrorOr VirtIODisplayConnector::flush_displayed_image(Graphics::VirtIOGPU::Protocol::Rect const& dirty_rect, bool main_buffer) -{ - VERIFY(m_graphics_adapter->operation_lock().is_locked()); - TRY(m_graphics_adapter->flush_displayed_image({}, *this, dirty_rect, main_buffer)); - return {}; -} - -void VirtIODisplayConnector::set_dirty_displayed_rect(Graphics::VirtIOGPU::Protocol::Rect const& dirty_rect, bool main_buffer) -{ - VERIFY(m_graphics_adapter->operation_lock().is_locked()); - m_graphics_adapter->set_dirty_displayed_rect({}, *this, dirty_rect, main_buffer); -} - -} diff --git a/Kernel/Devices/GPU/VirtIO/DisplayConnector.h b/Kernel/Devices/GPU/VirtIO/DisplayConnector.h deleted file mode 100644 index 37c9b555ced..00000000000 --- a/Kernel/Devices/GPU/VirtIO/DisplayConnector.h +++ /dev/null @@ -1,87 +0,0 @@ -/* - * Copyright (c) 2021, Sahan Fernando - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace Kernel::Graphics::VirtIOGPU { - -class Console; - -} - -namespace Kernel { - -class VirtIOGraphicsAdapter; -class VirtIODisplayConnector final : public DisplayConnector { - friend class Graphics::VirtIOGPU::Console; - friend class DeviceManagement; - -public: - static NonnullLockRefPtr must_create(VirtIOGraphicsAdapter& graphics_adapter, Graphics::VirtIOGPU::ScanoutID scanout_id); - - void set_edid_bytes(Badge, Array const& edid_bytes); - void set_safe_mode_setting_after_initialization(Badge); - Graphics::VirtIOGPU::ScanoutID scanout_id() const { return m_scanout_id; } - Graphics::VirtIOGPU::Protocol::DisplayInfoResponse::Display display_information(Badge) const; - - void initialize_console(Badge); - -private: - virtual bool mutable_mode_setting_capable() const override { return true; } - virtual bool double_framebuffering_capable() const override { return false; } - virtual bool partial_flush_support() const override { return true; } - virtual ErrorOr set_mode_setting(ModeSetting const&) override; - virtual ErrorOr set_safe_mode_setting() override; - virtual ErrorOr set_y_offset(size_t y) override; - virtual ErrorOr unblank() override; - - // Note: VirtIO hardware requires a constant refresh to keep the screen in sync to the user. - virtual bool flush_support() const override { return true; } - // Note: Paravirtualized hardware doesn't require a defined refresh rate for modesetting. - virtual bool refresh_rate_support() const override { return false; } - - virtual ErrorOr flush_first_surface() override; - virtual ErrorOr flush_rectangle(size_t buffer_index, FBRect const& rect) override; - - virtual void enable_console() override; - virtual void disable_console() override; - - static bool is_valid_buffer_index(size_t buffer_index) - { - return buffer_index == 0 || buffer_index == 1; - } - -private: - VirtIODisplayConnector(VirtIOGraphicsAdapter& graphics_adapter, Graphics::VirtIOGPU::ScanoutID scanout_id); - - ErrorOr flush_displayed_image(Graphics::VirtIOGPU::Protocol::Rect const& dirty_rect, bool main_buffer); - void set_dirty_displayed_rect(Graphics::VirtIOGPU::Protocol::Rect const& dirty_rect, bool main_buffer); - - void clear_to_black(); - - // Member data - // Context used for kernel operations (e.g. flushing resources to scanout) - Graphics::VirtIOGPU::ContextID m_kernel_context_id; - - NonnullLockRefPtr m_graphics_adapter; - LockRefPtr m_console; - Graphics::VirtIOGPU::Protocol::DisplayInfoResponse::Display m_display_info {}; - Graphics::VirtIOGPU::ScanoutID m_scanout_id; - - constexpr static size_t NUM_TRANSFER_REGION_PAGES = 256; -}; - -} diff --git a/Kernel/Devices/GPU/VirtIO/GPU3DDevice.cpp b/Kernel/Devices/GPU/VirtIO/GPU3DDevice.cpp deleted file mode 100644 index a00a1651af3..00000000000 --- a/Kernel/Devices/GPU/VirtIO/GPU3DDevice.cpp +++ /dev/null @@ -1,158 +0,0 @@ -/* - * Copyright (c) 2021, Sahan Fernando - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -namespace Kernel { - -VirtIOGPU3DDevice::PerContextState::PerContextState(OpenFileDescription& description, Graphics::VirtIOGPU::ContextID context_id, OwnPtr transfer_buffer_region) - : m_context_id(context_id) - , m_transfer_buffer_region(move(transfer_buffer_region)) - , m_attached_file_description(description) -{ -} - -ErrorOr> VirtIOGPU3DDevice::try_create(VirtIOGraphicsAdapter& adapter) -{ - // Setup memory transfer region - auto region_result = TRY(MM.allocate_kernel_region( - NUM_TRANSFER_REGION_PAGES * PAGE_SIZE, - "VIRGL3D kernel upload buffer"sv, - Memory::Region::Access::ReadWrite, - AllocationStrategy::AllocateNow)); - auto kernel_context_id = TRY(adapter.create_context()); - return TRY(DeviceManagement::try_create_device(adapter, move(region_result), kernel_context_id)); -} - -VirtIOGPU3DDevice::VirtIOGPU3DDevice(VirtIOGraphicsAdapter const& graphics_adapter, NonnullOwnPtr transfer_buffer_region, Graphics::VirtIOGPU::ContextID kernel_context_id) - : CharacterDevice(28, 0) - , m_graphics_adapter(graphics_adapter) - , m_kernel_context_id(kernel_context_id) - , m_transfer_buffer_region(move(transfer_buffer_region)) -{ -} - -void VirtIOGPU3DDevice::detach(OpenFileDescription& description) -{ - m_context_state_list.with([&description](auto& list) { - for (auto& context : list) { - if (&context.description() == &description) { - context.m_list_node.remove(); - // NOTE: We break here because there shouldn't be another context - // being attached to this OpenFileDescription. - break; - } - } - }); - CharacterDevice::detach(description); -} - -ErrorOr VirtIOGPU3DDevice::ioctl(OpenFileDescription& description, unsigned request, Userspace arg) -{ - - auto get_context_for_description = [](ContextList& list, OpenFileDescription& description) -> ErrorOr> { - for (auto& context : list) { - if (&context.description() == &description) - return context; - } - return EBADF; - }; - - // TODO: We really should have ioctls for destroying resources as well - switch (request) { - case VIRGL_IOCTL_CREATE_CONTEXT: { - return m_context_state_list.with([get_context_for_description, &description, this](auto& list) -> ErrorOr { - if (auto result = get_context_for_description(list, description); !result.is_error()) - return EEXIST; - SpinlockLocker locker(m_graphics_adapter->operation_lock()); - // TODO: Delete the context if it fails to be set in m_context_state_lookup - auto context_id = TRY(m_graphics_adapter->create_context()); - auto per_context_state = TRY(PerContextState::try_create(description, context_id)); - list.append(per_context_state); - return {}; - }); - } - case VIRGL_IOCTL_TRANSFER_DATA: { - auto user_transfer_descriptor = static_ptr_cast(arg); - auto transfer_descriptor = TRY(copy_typed_from_user(user_transfer_descriptor)); - if (Checked::addition_would_overflow(transfer_descriptor.offset_in_region, transfer_descriptor.num_bytes)) { - return EOVERFLOW; - } - if (transfer_descriptor.offset_in_region + transfer_descriptor.num_bytes > NUM_TRANSFER_REGION_PAGES * PAGE_SIZE) { - return EOVERFLOW; - } - - return m_context_state_list.with([get_context_for_description, &description, &transfer_descriptor](auto& list) -> ErrorOr { - auto context = TRY(get_context_for_description(list, description)); - auto& transfer_buffer_region = context->transfer_buffer_region(); - if (transfer_descriptor.direction == VIRGL_DATA_DIR_GUEST_TO_HOST) { - auto target = transfer_buffer_region.vaddr().offset(transfer_descriptor.offset_in_region).as_ptr(); - return copy_from_user(target, transfer_descriptor.data, transfer_descriptor.num_bytes); - } else if (transfer_descriptor.direction == VIRGL_DATA_DIR_HOST_TO_GUEST) { - auto source = transfer_buffer_region.vaddr().offset(transfer_descriptor.offset_in_region).as_ptr(); - return copy_to_user(transfer_descriptor.data, source, transfer_descriptor.num_bytes); - } else { - return EINVAL; - } - }); - } - case VIRGL_IOCTL_SUBMIT_CMD: { - auto user_command_buffer = static_ptr_cast(arg); - auto command_buffer = TRY(copy_typed_from_user(user_command_buffer)); - return m_context_state_list.with([get_context_for_description, &description, &command_buffer, this](auto& list) -> ErrorOr { - auto context = TRY(get_context_for_description(list, description)); - auto context_id = context->context_id(); - SpinlockLocker locker(m_graphics_adapter->operation_lock()); - TRY(m_graphics_adapter->submit_command_buffer(context_id, [&](Bytes buffer) { - auto num_bytes = command_buffer.num_elems * sizeof(u32); - VERIFY(num_bytes <= buffer.size()); - MUST(copy_from_user(buffer.data(), command_buffer.data, num_bytes)); - return num_bytes; - })); - return {}; - }); - } - case VIRGL_IOCTL_CREATE_RESOURCE: { - auto user_spec = static_ptr_cast(arg); - VirGL3DResourceSpec spec = TRY(copy_typed_from_user(user_spec)); - Graphics::VirtIOGPU::Protocol::Resource3DSpecification const resource_spec = { - .target = static_cast(spec.target), - .format = spec.format, - .bind = spec.bind, - .width = spec.width, - .height = spec.height, - .depth = spec.depth, - .array_size = spec.array_size, - .last_level = spec.last_level, - .nr_samples = spec.nr_samples, - .flags = spec.flags, - .padding = 0, - }; - return m_context_state_list.with([get_context_for_description, &description, &resource_spec, &spec, &arg, this](auto& list) -> ErrorOr { - auto context = TRY(get_context_for_description(list, description)); - SpinlockLocker locker(m_graphics_adapter->operation_lock()); - // FIXME: What would be an appropriate resource free-ing mechanism to use in case anything - // after this fails? - auto resource_id = TRY(m_graphics_adapter->create_3d_resource(resource_spec)); - TRY(m_graphics_adapter->attach_resource_to_context(resource_id, context->context_id())); - TRY(m_graphics_adapter->ensure_backing_storage(resource_id, context->transfer_buffer_region(), 0, NUM_TRANSFER_REGION_PAGES * PAGE_SIZE)); - spec.created_resource_id = resource_id.value(); - // FIXME: We should delete the resource we just created if we fail to copy the resource id out - return copy_to_user(static_ptr_cast(arg), &spec); - }); - } - } - return EINVAL; -} - -} diff --git a/Kernel/Devices/GPU/VirtIO/GPU3DDevice.h b/Kernel/Devices/GPU/VirtIO/GPU3DDevice.h deleted file mode 100644 index 171785ba390..00000000000 --- a/Kernel/Devices/GPU/VirtIO/GPU3DDevice.h +++ /dev/null @@ -1,158 +0,0 @@ -/* - * Copyright (c) 2021, Sahan Fernando - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include -#include - -namespace Kernel::Graphics::VirtIOGPU { - -enum class VirGLCommand : u32 { - NOP = 0, - CREATE_OBJECT = 1, - BIND_OBJECT, - DESTROY_OBJECT, - SET_VIEWPORT_STATE, - SET_FRAMEBUFFER_STATE, - SET_VERTEX_BUFFERS, - CLEAR, - DRAW_VBO, - RESOURCE_INLINE_WRITE, - SET_SAMPLER_VIEWS, - SET_INDEX_BUFFER, - SET_CONSTANT_BUFFER, - SET_STENCIL_REF, - SET_BLEND_COLOR, - SET_SCISSOR_STATE, - BLIT, - RESOURCE_COPY_REGION, - BIND_SAMPLER_STATES, - BEGIN_QUERY, - END_QUERY, - GET_QUERY_RESULT, - SET_POLYGON_STIPPLE, - SET_CLIP_STATE, - SET_SAMPLE_MASK, - SET_STREAMOUT_TARGETS, - SET_RENDER_CONDITION, - SET_UNIFORM_BUFFER, - - SET_SUB_CTX, - CREATE_SUB_CTX, - DESTROY_SUB_CTX, - BIND_SHADER, - SET_TESS_STATE, - SET_MIN_SAMPLES, - SET_SHADER_BUFFERS, - SET_SHADER_IMAGES, - MEMORY_BARRIER, - LAUNCH_GRID, - SET_FRAMEBUFFER_STATE_NO_ATTACH, - TEXTURE_BARRIER, - SET_ATOMIC_BUFFERS, - SET_DBG_FLAGS, - GET_QUERY_RESULT_QBO, - TRANSFER3D, - END_TRANSFERS, - COPY_TRANSFER3D, - SET_TWEAKS, - CLEAR_TEXTURE, - PIPE_RESOURCE_CREATE, - PIPE_RESOURCE_SET_TYPE, - GET_MEMORY_INFO, - SEND_STRING_MARKER, - MAX_COMMANDS -}; - -union ClearType { - struct { - u32 depth : 1; - u32 stencil : 1; - u32 color0 : 1; - u32 color1 : 1; - u32 color2 : 1; - u32 color3 : 1; - u32 color4 : 1; - u32 color5 : 1; - u32 color6 : 1; - u32 color7 : 1; - } flags; - u32 value; -}; - -} - -namespace Kernel { - -class VirtIOGraphicsAdapter; -class VirtIOGPU3DDevice : public CharacterDevice { - friend class DeviceManagement; - -public: - static ErrorOr> try_create(VirtIOGraphicsAdapter&); - -private: - VirtIOGPU3DDevice(VirtIOGraphicsAdapter const& graphics_adapter, NonnullOwnPtr transfer_buffer_region, Graphics::VirtIOGPU::ContextID kernel_context_id); - - class PerContextState final : public AtomicRefCounted { - friend class VirtIOGPU3DDevice; - - public: - static ErrorOr> try_create(OpenFileDescription& description, Graphics::VirtIOGPU::ContextID context_id) - { - auto region_result = TRY(MM.allocate_kernel_region( - NUM_TRANSFER_REGION_PAGES * PAGE_SIZE, - "VIRGL3D userspace upload buffer"sv, - Memory::Region::Access::ReadWrite, - AllocationStrategy::AllocateNow)); - return TRY(adopt_nonnull_ref_or_enomem(new (nothrow) PerContextState(description, context_id, move(region_result)))); - } - Graphics::VirtIOGPU::ContextID context_id() { return m_context_id; } - Memory::Region& transfer_buffer_region() { return *m_transfer_buffer_region; } - - OpenFileDescription& description() { return m_attached_file_description; } - - private: - PerContextState() = delete; - PerContextState(OpenFileDescription&, Graphics::VirtIOGPU::ContextID context_id, OwnPtr transfer_buffer_region); - Graphics::VirtIOGPU::ContextID m_context_id; - OwnPtr m_transfer_buffer_region; - - // NOTE: We clean this whole object when the file description is closed, therefore we need to hold - // a raw reference here instead of a strong reference pointer (e.g. RefPtr, which will make it - // possible to leak the attached OpenFileDescription for a context in this device). - OpenFileDescription& m_attached_file_description; - - IntrusiveListNode> m_list_node; - }; - - virtual bool can_read(OpenFileDescription const&, u64) const override { return true; } - virtual bool can_write(OpenFileDescription const&, u64) const override { return true; } - virtual ErrorOr read(OpenFileDescription&, u64, UserOrKernelBuffer&, size_t) override { return ENOTSUP; } - virtual ErrorOr write(OpenFileDescription&, u64, UserOrKernelBuffer const&, size_t) override { return ENOTSUP; } - virtual StringView class_name() const override { return "virgl3d"sv; } - - virtual ErrorOr ioctl(OpenFileDescription&, unsigned request, Userspace arg) override; - virtual void detach(OpenFileDescription&) override; - - using ContextList = IntrusiveListRelaxedConst<&PerContextState::m_list_node>; - -private: - NonnullLockRefPtr m_graphics_adapter; - // Context used for kernel operations (e.g. flushing resources to scanout) - Graphics::VirtIOGPU::ContextID m_kernel_context_id; - SpinlockProtected m_context_state_list; - // Memory management for backing buffers - NonnullOwnPtr m_transfer_buffer_region; - constexpr static size_t NUM_TRANSFER_REGION_PAGES = 1024; -}; - -} diff --git a/Kernel/Devices/GPU/VirtIO/GraphicsAdapter.cpp b/Kernel/Devices/GPU/VirtIO/GraphicsAdapter.cpp deleted file mode 100644 index ee03c0a02aa..00000000000 --- a/Kernel/Devices/GPU/VirtIO/GraphicsAdapter.cpp +++ /dev/null @@ -1,555 +0,0 @@ -/* - * Copyright (c) 2021, Sahan Fernando - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace Kernel { - -#define DEVICE_EVENTS_READ 0x0 -#define DEVICE_EVENTS_CLEAR 0x4 -#define DEVICE_NUM_SCANOUTS 0x8 - -ErrorOr VirtIOGraphicsAdapter::probe(PCI::DeviceIdentifier const& device_identifier) -{ - return device_identifier.hardware_id().vendor_id == PCI::VendorID::VirtIO; -} - -ErrorOr> VirtIOGraphicsAdapter::create(PCI::DeviceIdentifier const& device_identifier) -{ - // Setup memory transfer region - auto scratch_space_region = TRY(MM.allocate_contiguous_kernel_region( - 32 * PAGE_SIZE, - "VirtGPU Scratch Space"sv, - Memory::Region::Access::ReadWrite)); - - auto active_context_ids = TRY(Bitmap::create(VREND_MAX_CTX, false)); - auto pci_transport_link = TRY(VirtIO::PCIeTransportLink::create(device_identifier)); - auto adapter = TRY(adopt_nonnull_lock_ref_or_enomem(new (nothrow) VirtIOGraphicsAdapter(move(pci_transport_link), move(active_context_ids), move(scratch_space_region)))); - TRY(adapter->initialize_virtio_resources()); - TRY(adapter->initialize_adapter()); - return adapter; -} - -ErrorOr VirtIOGraphicsAdapter::initialize_adapter() -{ - VERIFY(m_num_scanouts <= VIRTIO_GPU_MAX_SCANOUTS); - TRY(initialize_3d_device()); - for (size_t index = 0; index < m_num_scanouts; index++) { - auto display_connector = VirtIODisplayConnector::must_create(*this, index); - m_scanouts[index].display_connector = display_connector; - TRY(query_and_set_edid(index, *display_connector)); - display_connector->set_safe_mode_setting_after_initialization({}); - display_connector->initialize_console({}); - } - return {}; -} - -ErrorOr VirtIOGraphicsAdapter::mode_set_resolution(Badge, VirtIODisplayConnector& connector, size_t width, size_t height) -{ - SpinlockLocker locker(m_operation_lock); - VERIFY(connector.scanout_id() < VIRTIO_GPU_MAX_SCANOUTS); - auto rounded_buffer_size = TRY(calculate_framebuffer_size(width, height)); - TRY(attach_physical_range_to_framebuffer(connector, true, 0, rounded_buffer_size)); - return {}; -} - -void VirtIOGraphicsAdapter::set_dirty_displayed_rect(Badge, VirtIODisplayConnector& connector, Graphics::VirtIOGPU::Protocol::Rect const& dirty_rect, bool main_buffer) -{ - VERIFY(m_operation_lock.is_locked()); - VERIFY(connector.scanout_id() < VIRTIO_GPU_MAX_SCANOUTS); - Scanout::PhysicalBuffer& buffer = main_buffer ? m_scanouts[connector.scanout_id().value()].main_buffer : m_scanouts[connector.scanout_id().value()].back_buffer; - if (buffer.dirty_rect.width == 0 || buffer.dirty_rect.height == 0) { - buffer.dirty_rect = dirty_rect; - } else { - auto current_dirty_right = buffer.dirty_rect.x + buffer.dirty_rect.width; - auto current_dirty_bottom = buffer.dirty_rect.y + buffer.dirty_rect.height; - buffer.dirty_rect.x = min(buffer.dirty_rect.x, dirty_rect.x); - buffer.dirty_rect.y = min(buffer.dirty_rect.y, dirty_rect.y); - buffer.dirty_rect.width = max(current_dirty_right, dirty_rect.x + dirty_rect.width) - buffer.dirty_rect.x; - buffer.dirty_rect.height = max(current_dirty_bottom, dirty_rect.y + dirty_rect.height) - buffer.dirty_rect.y; - } -} - -ErrorOr VirtIOGraphicsAdapter::flush_displayed_image(Badge, VirtIODisplayConnector& connector, Graphics::VirtIOGPU::Protocol::Rect const& dirty_rect, bool main_buffer) -{ - VERIFY(m_operation_lock.is_locked()); - VERIFY(connector.scanout_id() < VIRTIO_GPU_MAX_SCANOUTS); - Scanout::PhysicalBuffer& buffer = main_buffer ? m_scanouts[connector.scanout_id().value()].main_buffer : m_scanouts[connector.scanout_id().value()].back_buffer; - TRY(flush_displayed_image(buffer.resource_id, dirty_rect)); - buffer.dirty_rect = {}; - return {}; -} - -ErrorOr VirtIOGraphicsAdapter::transfer_framebuffer_data_to_host(Badge, VirtIODisplayConnector& connector, Graphics::VirtIOGPU::Protocol::Rect const& rect, bool main_buffer) -{ - VERIFY(m_operation_lock.is_locked()); - VERIFY(connector.scanout_id() < VIRTIO_GPU_MAX_SCANOUTS); - Scanout::PhysicalBuffer& buffer = main_buffer ? m_scanouts[connector.scanout_id().value()].main_buffer : m_scanouts[connector.scanout_id().value()].back_buffer; - TRY(transfer_framebuffer_data_to_host(connector.scanout_id(), buffer.resource_id, rect)); - return {}; -} - -ErrorOr VirtIOGraphicsAdapter::attach_physical_range_to_framebuffer(VirtIODisplayConnector& connector, bool main_buffer, size_t framebuffer_offset, size_t framebuffer_size) -{ - VERIFY(m_operation_lock.is_locked()); - Scanout::PhysicalBuffer& buffer = main_buffer ? m_scanouts[connector.scanout_id().value()].main_buffer : m_scanouts[connector.scanout_id().value()].back_buffer; - buffer.framebuffer_offset = framebuffer_offset; - - // 1. Create BUFFER using VIRTIO_GPU_CMD_RESOURCE_CREATE_2D - if (buffer.resource_id.value() != 0) { - // FIXME: Do we need to remove the resource regardless of this condition? - // Do we need to remove it if any of the code below fails for some reason? - TRY(delete_resource(buffer.resource_id)); - } - - auto display_info = connector.display_information({}); - buffer.resource_id = TRY(create_2d_resource(display_info.rect)); - - // 2. Attach backing storage using VIRTIO_GPU_CMD_RESOURCE_ATTACH_BACKING - TRY(ensure_backing_storage(buffer.resource_id, connector.framebuffer_region(), buffer.framebuffer_offset, framebuffer_size)); - // 3. Use VIRTIO_GPU_CMD_SET_SCANOUT to link the framebuffer to a display scanout. - TRY(set_scanout_resource(connector.scanout_id(), buffer.resource_id, display_info.rect)); - - // Make sure we constrain the existing dirty rect (if any) - if (buffer.dirty_rect.width != 0 || buffer.dirty_rect.height != 0) { - auto dirty_right = buffer.dirty_rect.x + buffer.dirty_rect.width; - auto dirty_bottom = buffer.dirty_rect.y + buffer.dirty_rect.height; - buffer.dirty_rect.width = min(dirty_right, display_info.rect.x + display_info.rect.width) - buffer.dirty_rect.x; - buffer.dirty_rect.height = min(dirty_bottom, display_info.rect.y + display_info.rect.height) - buffer.dirty_rect.y; - } - return {}; -} - -VirtIOGraphicsAdapter::VirtIOGraphicsAdapter(NonnullOwnPtr transport_entity, Bitmap&& active_context_ids, NonnullOwnPtr scratch_space_region) - : VirtIO::Device(move(transport_entity)) - , m_scratch_space(move(scratch_space_region)) -{ - m_active_context_ids.with([&](Bitmap& my_active_context_ids) { - my_active_context_ids = move(active_context_ids); - // Note: Context ID 0 is invalid, so mark it as in use. - my_active_context_ids.set(0, true); - }); -} - -ErrorOr VirtIOGraphicsAdapter::initialize_virtio_resources() -{ - TRY(VirtIO::Device::initialize_virtio_resources()); - auto* config = TRY(transport_entity().get_config(VirtIO::ConfigurationType::Device)); - m_device_configuration = config; - TRY(negotiate_features([&](u64 supported_features) { - u64 negotiated = 0; - if (is_feature_set(supported_features, VIRTIO_GPU_F_VIRGL)) { - dbgln_if(VIRTIO_DEBUG, "VirtIO::GraphicsAdapter: VirGL is available, enabling"); - negotiated |= VIRTIO_GPU_F_VIRGL; - m_has_virgl_support = true; - } - if (is_feature_set(supported_features, VIRTIO_GPU_F_EDID)) - negotiated |= VIRTIO_GPU_F_EDID; - return negotiated; - })); - transport_entity().read_config_atomic([&]() { - m_num_scanouts = transport_entity().config_read32(*config, DEVICE_NUM_SCANOUTS); - }); - dbgln_if(VIRTIO_DEBUG, "VirtIO::GraphicsAdapter: num_scanouts: {}", m_num_scanouts); - TRY(setup_queues(2)); // CONTROLQ + CURSORQ - finish_init(); - return {}; -} - -ErrorOr VirtIOGraphicsAdapter::handle_device_config_change() -{ - auto events = get_pending_events(); - if (events & VIRTIO_GPU_EVENT_DISPLAY) { - // The host window was resized, in SerenityOS we completely ignore this event - dbgln_if(VIRTIO_DEBUG, "VirtIO::GraphicsAdapter: Ignoring virtio gpu display resize event"); - clear_pending_events(VIRTIO_GPU_EVENT_DISPLAY); - } - if (events & ~VIRTIO_GPU_EVENT_DISPLAY) { - dbgln("VirtIO::GraphicsAdapter: Got unknown device config change event: {:#x}", events); - return Error::from_errno(EIO); - } - return {}; -} - -void VirtIOGraphicsAdapter::handle_queue_update(u16) -{ -} - -u32 VirtIOGraphicsAdapter::get_pending_events() -{ - return transport_entity().config_read32(*m_device_configuration, DEVICE_EVENTS_READ); -} - -void VirtIOGraphicsAdapter::clear_pending_events(u32 event_bitmask) -{ - transport_entity().config_write32(*m_device_configuration, DEVICE_EVENTS_CLEAR, event_bitmask); -} - -static void populate_virtio_gpu_request_header(Graphics::VirtIOGPU::Protocol::ControlHeader& header, Graphics::VirtIOGPU::Protocol::CommandType ctrl_type, u32 flags) -{ - header.type = to_underlying(ctrl_type); - header.flags = flags; - header.fence_id = 0; - header.context_id = 0; - header.padding = 0; -} - -ErrorOr VirtIOGraphicsAdapter::query_and_set_edid(u32 scanout_id, VirtIODisplayConnector& display_connector) -{ - SpinlockLocker locker(m_operation_lock); - if (!is_feature_accepted(VIRTIO_GPU_F_EDID)) - return Error::from_errno(ENOTSUP); - - auto writer = create_scratchspace_writer(); - auto& request = writer.append_structure(); - auto& response = writer.append_structure(); - - populate_virtio_gpu_request_header(request.header, Graphics::VirtIOGPU::Protocol::CommandType::VIRTIO_GPU_CMD_GET_EDID, 0); - - request.scanout_id = scanout_id; - request.padding = 0; - - TRY(synchronous_virtio_gpu_command(10000, start_of_scratch_space(), sizeof(request), sizeof(response))); - - if (response.header.type != to_underlying(Graphics::VirtIOGPU::Protocol::CommandType::VIRTIO_GPU_RESP_OK_EDID)) { - dmesgln("VirtIO::GraphicsAdapter: Failed to get EDID"); - return Error::from_errno(ENOTSUP); - } - - if (response.size == 0) { - dmesgln("VirtIO::GraphicsAdapter: Failed to get EDID, empty buffer"); - return Error::from_errno(EIO); - } - - Array raw_edid; - memcpy(raw_edid.data(), response.edid, min(sizeof(raw_edid), response.size)); - display_connector.set_edid_bytes({}, raw_edid); - return {}; -} - -ErrorOr VirtIOGraphicsAdapter::create_2d_resource(Graphics::VirtIOGPU::Protocol::Rect rect) -{ - VERIFY(m_operation_lock.is_locked()); - auto writer = create_scratchspace_writer(); - auto& request = writer.append_structure(); - auto& response = writer.append_structure(); - - populate_virtio_gpu_request_header(request.header, Graphics::VirtIOGPU::Protocol::CommandType::VIRTIO_GPU_CMD_RESOURCE_CREATE_2D, 0); - - auto resource_id = allocate_resource_id(); - request.resource_id = resource_id.value(); - request.width = rect.width; - request.height = rect.height; - request.format = to_underlying(Graphics::VirtIOGPU::Protocol::TextureFormat::VIRTIO_GPU_FORMAT_B8G8R8X8_UNORM); - - TRY(synchronous_virtio_gpu_command(10000, start_of_scratch_space(), sizeof(request), sizeof(response))); - - if (response.type == to_underlying(Graphics::VirtIOGPU::Protocol::CommandType::VIRTIO_GPU_RESP_OK_NODATA)) { - dbgln_if(VIRTIO_DEBUG, "VirtIO::GraphicsAdapter: Allocated 2d resource with id {}", resource_id.value()); - return resource_id; - } - return EIO; -} - -ErrorOr VirtIOGraphicsAdapter::create_3d_resource(Graphics::VirtIOGPU::Protocol::Resource3DSpecification const& resource_3d_specification) -{ - VERIFY(m_operation_lock.is_locked()); - auto writer = create_scratchspace_writer(); - auto& request = writer.append_structure(); - auto& response = writer.append_structure(); - - populate_virtio_gpu_request_header(request.header, Graphics::VirtIOGPU::Protocol::CommandType::VIRTIO_GPU_CMD_RESOURCE_CREATE_3D, 0); - - // FIXME: What would be an appropriate resource free-ing mechanism to use in case anything - // after this fails? - auto resource_id = allocate_resource_id(); - request.resource_id = resource_id.value(); - // TODO: Abstract this out a bit more - u32* start_of_copied_fields = &request.target; - - // Validate that the sub copy from the resource_3d_specification to the offset of the request fits. - static_assert((sizeof(request) - offsetof(Graphics::VirtIOGPU::Protocol::ResourceCreate3D, target) == sizeof(resource_3d_specification))); - memcpy(start_of_copied_fields, &resource_3d_specification, sizeof(resource_3d_specification)); - - TRY(synchronous_virtio_gpu_command(10000, start_of_scratch_space(), sizeof(request), sizeof(response))); - - if (response.type == to_underlying(Graphics::VirtIOGPU::Protocol::CommandType::VIRTIO_GPU_RESP_OK_NODATA)) { - dbgln_if(VIRTIO_DEBUG, "VirtIO::GraphicsAdapter: Allocated 3d resource with id {}", resource_id.value()); - return resource_id; - } - return EIO; -} - -ErrorOr VirtIOGraphicsAdapter::ensure_backing_storage(Graphics::VirtIOGPU::ResourceID resource_id, Memory::Region const& region, size_t buffer_offset, size_t buffer_length) -{ - VERIFY(m_operation_lock.is_locked()); - - VERIFY(buffer_offset % PAGE_SIZE == 0); - VERIFY(buffer_length % PAGE_SIZE == 0); - auto first_page_index = buffer_offset / PAGE_SIZE; - size_t num_mem_regions = buffer_length / PAGE_SIZE; - - auto writer = create_scratchspace_writer(); - auto& request = writer.append_structure(); - size_t const header_block_size = sizeof(request) + num_mem_regions * sizeof(Graphics::VirtIOGPU::Protocol::MemoryEntry); - - populate_virtio_gpu_request_header(request.header, Graphics::VirtIOGPU::Protocol::CommandType::VIRTIO_GPU_CMD_RESOURCE_ATTACH_BACKING, 0); - request.resource_id = resource_id.value(); - request.num_entries = num_mem_regions; - for (size_t i = 0; i < num_mem_regions; ++i) { - auto& memory_entry = writer.append_structure(); - memory_entry.address = region.physical_page(first_page_index + i)->paddr().get(); - memory_entry.length = PAGE_SIZE; - } - - auto& response = writer.append_structure(); - - TRY(synchronous_virtio_gpu_command(10000, start_of_scratch_space(), header_block_size, sizeof(response))); - - if (response.type == to_underlying(Graphics::VirtIOGPU::Protocol::CommandType::VIRTIO_GPU_RESP_OK_NODATA)) { - dbgln_if(VIRTIO_DEBUG, "VirtIO::GraphicsAdapter: Allocated backing storage"); - return {}; - } - return EIO; -} - -ErrorOr VirtIOGraphicsAdapter::detach_backing_storage(Graphics::VirtIOGPU::ResourceID resource_id) -{ - VERIFY(m_operation_lock.is_locked()); - auto writer = create_scratchspace_writer(); - auto& request = writer.append_structure(); - auto& response = writer.append_structure(); - - populate_virtio_gpu_request_header(request.header, Graphics::VirtIOGPU::Protocol::CommandType::VIRTIO_GPU_CMD_RESOURCE_DETACH_BACKING, 0); - request.resource_id = resource_id.value(); - - TRY(synchronous_virtio_gpu_command(10000, start_of_scratch_space(), sizeof(request), sizeof(response))); - - if (response.type == to_underlying(Graphics::VirtIOGPU::Protocol::CommandType::VIRTIO_GPU_RESP_OK_NODATA)) { - dbgln_if(VIRTIO_DEBUG, "VirtIO::GraphicsAdapter: Detached backing storage"); - return {}; - } - return EIO; -} - -ErrorOr VirtIOGraphicsAdapter::set_scanout_resource(Graphics::VirtIOGPU::ScanoutID scanout, Graphics::VirtIOGPU::ResourceID resource_id, Graphics::VirtIOGPU::Protocol::Rect rect) -{ - VERIFY(m_operation_lock.is_locked()); - // We need to scope the request/response here so that we can query display information later on - auto writer = create_scratchspace_writer(); - auto& request = writer.append_structure(); - auto& response = writer.append_structure(); - - populate_virtio_gpu_request_header(request.header, Graphics::VirtIOGPU::Protocol::CommandType::VIRTIO_GPU_CMD_SET_SCANOUT, 0); - request.resource_id = resource_id.value(); - request.scanout_id = scanout.value(); - request.rect = rect; - - TRY(synchronous_virtio_gpu_command(10000, start_of_scratch_space(), sizeof(request), sizeof(response))); - - if (response.type == to_underlying(Graphics::VirtIOGPU::Protocol::CommandType::VIRTIO_GPU_RESP_OK_NODATA)) { - dbgln_if(VIRTIO_DEBUG, "VirtIO::GraphicsAdapter: Set backing scanout"); - return {}; - } - return EIO; -} - -ErrorOr VirtIOGraphicsAdapter::transfer_framebuffer_data_to_host(Graphics::VirtIOGPU::ScanoutID scanout, Graphics::VirtIOGPU::ResourceID resource_id, Graphics::VirtIOGPU::Protocol::Rect const& dirty_rect) -{ - VERIFY(m_operation_lock.is_locked()); - auto writer = create_scratchspace_writer(); - auto& request = writer.append_structure(); - auto& response = writer.append_structure(); - - populate_virtio_gpu_request_header(request.header, Graphics::VirtIOGPU::Protocol::CommandType::VIRTIO_GPU_CMD_TRANSFER_TO_HOST_2D, 0); - request.offset = (dirty_rect.x + (dirty_rect.y * m_scanouts[scanout.value()].display_connector->display_information({}).rect.width)) * sizeof(u32); - request.resource_id = resource_id.value(); - request.rect = dirty_rect; - - TRY(synchronous_virtio_gpu_command(10000, start_of_scratch_space(), sizeof(request), sizeof(response))); - - if (response.type == to_underlying(Graphics::VirtIOGPU::Protocol::CommandType::VIRTIO_GPU_RESP_OK_NODATA)) - return {}; - return EIO; -} - -ErrorOr VirtIOGraphicsAdapter::flush_displayed_image(Graphics::VirtIOGPU::ResourceID resource_id, Graphics::VirtIOGPU::Protocol::Rect const& dirty_rect) -{ - VERIFY(m_operation_lock.is_locked()); - auto writer = create_scratchspace_writer(); - auto& request = writer.append_structure(); - auto& response = writer.append_structure(); - - populate_virtio_gpu_request_header(request.header, Graphics::VirtIOGPU::Protocol::CommandType::VIRTIO_GPU_CMD_RESOURCE_FLUSH, 0); - request.resource_id = resource_id.value(); - request.rect = dirty_rect; - - TRY(synchronous_virtio_gpu_command(10000, start_of_scratch_space(), sizeof(request), sizeof(response))); - - if (response.type == to_underlying(Graphics::VirtIOGPU::Protocol::CommandType::VIRTIO_GPU_RESP_OK_NODATA)) - return {}; - return EIO; -} - -ErrorOr VirtIOGraphicsAdapter::synchronous_virtio_gpu_command(size_t microseconds_timeout, PhysicalAddress buffer_start, size_t request_size, size_t response_size) -{ - VERIFY(m_operation_lock.is_locked()); - VERIFY(microseconds_timeout > 10); - VERIFY(microseconds_timeout < 100000); - auto& queue = get_queue(CONTROLQ); - queue.disable_interrupts(); - SpinlockLocker lock(queue.lock()); - VirtIO::QueueChain chain { queue }; - chain.add_buffer_to_chain(buffer_start, request_size, VirtIO::BufferType::DeviceReadable); - chain.add_buffer_to_chain(buffer_start.offset(request_size), response_size, VirtIO::BufferType::DeviceWritable); - supply_chain_and_notify(CONTROLQ, chain); - full_memory_barrier(); - size_t current_time = 0; - ScopeGuard clear_used_buffers([&] { - queue.discard_used_buffers(); - }); - while (current_time < microseconds_timeout) { - if (queue.new_data_available()) - return {}; - microseconds_delay(1); - current_time++; - } - return Error::from_errno(EBUSY); -} - -ErrorOr VirtIOGraphicsAdapter::flush_dirty_rectangle(Graphics::VirtIOGPU::ScanoutID scanout_id, Graphics::VirtIOGPU::ResourceID resource_id, Graphics::VirtIOGPU::Protocol::Rect const& dirty_rect) -{ - VERIFY(m_operation_lock.is_locked()); - TRY(transfer_framebuffer_data_to_host(scanout_id, resource_id, dirty_rect)); - TRY(flush_displayed_image(resource_id, dirty_rect)); - return {}; -} - -Graphics::VirtIOGPU::ResourceID VirtIOGraphicsAdapter::allocate_resource_id() -{ - return m_resource_id_counter++; -} - -ErrorOr VirtIOGraphicsAdapter::delete_resource(Graphics::VirtIOGPU::ResourceID resource_id) -{ - VERIFY(m_operation_lock.is_locked()); - auto writer = create_scratchspace_writer(); - auto& request = writer.append_structure(); - auto& response = writer.append_structure(); - - populate_virtio_gpu_request_header(request.header, Graphics::VirtIOGPU::Protocol::CommandType::VIRTIO_GPU_CMD_RESOURCE_UNREF, 0); - request.resource_id = resource_id.value(); - - TRY(synchronous_virtio_gpu_command(10000, start_of_scratch_space(), sizeof(request), sizeof(response))); - - if (response.type == to_underlying(Graphics::VirtIOGPU::Protocol::CommandType::VIRTIO_GPU_RESP_OK_NODATA)) - return {}; - return EIO; -} - -ErrorOr VirtIOGraphicsAdapter::initialize_3d_device() -{ - if (m_has_virgl_support) { - SpinlockLocker locker(m_operation_lock); - m_3d_device = TRY(VirtIOGPU3DDevice::try_create(*this)); - } - return {}; -} - -ErrorOr VirtIOGraphicsAdapter::create_context() -{ - VERIFY(m_operation_lock.is_locked()); - return m_active_context_ids.with([&](Bitmap& active_context_ids) -> ErrorOr { - auto maybe_available_id = active_context_ids.find_first_unset(); - if (!maybe_available_id.has_value()) { - dmesgln("VirtIO::GraphicsAdapter: No available context IDs."); - return Error::from_errno(ENXIO); - } - auto new_context_id = static_cast(maybe_available_id.value()); - - auto writer = create_scratchspace_writer(); - auto& request = writer.append_structure(); - auto& response = writer.append_structure(); - - constexpr char const* region_name = "Serenity VirGL3D Context"; - populate_virtio_gpu_request_header(request.header, Graphics::VirtIOGPU::Protocol::CommandType::VIRTIO_GPU_CMD_CTX_CREATE, 0); - request.header.context_id = new_context_id; - request.name_length = strlen(region_name); - memset(request.debug_name.data(), 0, 64); - VERIFY(request.name_length <= 64); - memcpy(request.debug_name.data(), region_name, request.name_length); - - TRY(synchronous_virtio_gpu_command(10000, start_of_scratch_space(), sizeof(request), sizeof(response))); - - if (response.type == to_underlying(Graphics::VirtIOGPU::Protocol::CommandType::VIRTIO_GPU_RESP_OK_NODATA)) { - active_context_ids.set(maybe_available_id.value(), true); - return static_cast(new_context_id); - } - return Error::from_errno(EIO); - }); -} - -ErrorOr VirtIOGraphicsAdapter::submit_command_buffer(Graphics::VirtIOGPU::ContextID context_id, Function buffer_writer) -{ - VERIFY(m_operation_lock.is_locked()); - auto writer = create_scratchspace_writer(); - auto& request = writer.append_structure(); - - populate_virtio_gpu_request_header(request.header, Graphics::VirtIOGPU::Protocol::CommandType::VIRTIO_GPU_CMD_SUBMIT_3D, 0); - request.header.context_id = context_id.value(); - - auto max_command_buffer_length = m_scratch_space->size() - sizeof(request) - sizeof(Graphics::VirtIOGPU::Protocol::ControlHeader); - // Truncate to nearest multiple of alignment, to ensure padding loop doesn't exhaust allocated space - max_command_buffer_length -= max_command_buffer_length % alignof(Graphics::VirtIOGPU::Protocol::ControlHeader); - Bytes command_buffer_buffer(m_scratch_space->vaddr().offset(sizeof(request)).as_ptr(), max_command_buffer_length); - request.size = buffer_writer(command_buffer_buffer); - writer.skip_bytes(request.size); - // The alignment of a ControlHeader may be a few words larger than the length of a command buffer, so - // we pad with no-ops until we reach the correct alignment - while (writer.current_offset() % alignof(Graphics::VirtIOGPU::Protocol::ControlHeader) != 0) { - VERIFY((writer.current_offset() % alignof(Graphics::VirtIOGPU::Protocol::ControlHeader)) % sizeof(u32) == 0); - writer.append_structure() = to_underlying(Graphics::VirtIOGPU::VirGLCommand::NOP); - request.size += 4; - } - dbgln_if(VIRTIO_DEBUG, "VirtIO::GraphicsAdapter: Sending command buffer of length {}", request.size); - auto& response = writer.append_structure(); - - TRY(synchronous_virtio_gpu_command(10000, start_of_scratch_space(), sizeof(request) + request.size, sizeof(response))); - - if (response.type == to_underlying(Graphics::VirtIOGPU::Protocol::CommandType::VIRTIO_GPU_RESP_OK_NODATA)) - return {}; - return EIO; -} - -ErrorOr VirtIOGraphicsAdapter::attach_resource_to_context(Graphics::VirtIOGPU::ResourceID resource_id, Graphics::VirtIOGPU::ContextID context_id) -{ - VERIFY(m_operation_lock.is_locked()); - auto writer = create_scratchspace_writer(); - auto& request = writer.append_structure(); - auto& response = writer.append_structure(); - populate_virtio_gpu_request_header(request.header, Graphics::VirtIOGPU::Protocol::CommandType::VIRTIO_GPU_CMD_CTX_ATTACH_RESOURCE, 0); - request.header.context_id = context_id.value(); - request.resource_id = resource_id.value(); - - TRY(synchronous_virtio_gpu_command(10000, start_of_scratch_space(), sizeof(request), sizeof(response))); - - if (response.type == to_underlying(Graphics::VirtIOGPU::Protocol::CommandType::VIRTIO_GPU_RESP_OK_NODATA)) - return {}; - return EIO; -} - -} diff --git a/Kernel/Devices/GPU/VirtIO/GraphicsAdapter.h b/Kernel/Devices/GPU/VirtIO/GraphicsAdapter.h deleted file mode 100644 index 1f89e07c97f..00000000000 --- a/Kernel/Devices/GPU/VirtIO/GraphicsAdapter.h +++ /dev/null @@ -1,123 +0,0 @@ -/* - * Copyright (c) 2021, Sahan Fernando - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include -#include - -namespace Kernel { - -#define VIRTIO_GPU_F_VIRGL (1 << 0) -#define VIRTIO_GPU_F_EDID (1 << 1) - -#define VIRTIO_GPU_FLAG_FENCE (1 << 0) - -#define CONTROLQ 0 -#define CURSORQ 1 - -#define MAX_VIRTIOGPU_RESOLUTION_WIDTH 3840 -#define MAX_VIRTIOGPU_RESOLUTION_HEIGHT 2160 - -#define VIRTIO_GPU_EVENT_DISPLAY (1 << 0) - -class VirtIODisplayConnector; -class VirtIOGPU3DDevice; -class VirtIOGraphicsAdapter final - : public GPUDevice - , public VirtIO::Device { - friend class VirtIODisplayConnector; - friend class VirtIOGPU3DDevice; - -public: - static ErrorOr probe(PCI::DeviceIdentifier const&); - static ErrorOr> create(PCI::DeviceIdentifier const&); - - virtual ErrorOr initialize_virtio_resources() override; - - ErrorOr mode_set_resolution(Badge, VirtIODisplayConnector&, size_t width, size_t height); - void set_dirty_displayed_rect(Badge, VirtIODisplayConnector&, Graphics::VirtIOGPU::Protocol::Rect const& dirty_rect, bool main_buffer); - ErrorOr flush_displayed_image(Badge, VirtIODisplayConnector&, Graphics::VirtIOGPU::Protocol::Rect const& dirty_rect, bool main_buffer); - ErrorOr transfer_framebuffer_data_to_host(Badge, VirtIODisplayConnector&, Graphics::VirtIOGPU::Protocol::Rect const& rect, bool main_buffer); - -private: - ErrorOr attach_physical_range_to_framebuffer(VirtIODisplayConnector& connector, bool main_buffer, size_t framebuffer_offset, size_t framebuffer_size); - - ErrorOr initialize_3d_device(); - - ErrorOr flush_dirty_rectangle(Graphics::VirtIOGPU::ScanoutID, Graphics::VirtIOGPU::ResourceID, Graphics::VirtIOGPU::Protocol::Rect const& dirty_rect); - struct Scanout { - struct PhysicalBuffer { - size_t framebuffer_offset { 0 }; - Graphics::VirtIOGPU::Protocol::Rect dirty_rect {}; - Graphics::VirtIOGPU::ResourceID resource_id { 0 }; - }; - - LockRefPtr display_connector; - PhysicalBuffer main_buffer; - PhysicalBuffer back_buffer; - }; - - VirtIOGraphicsAdapter(NonnullOwnPtr, Bitmap&& active_context_ids, NonnullOwnPtr scratch_space_region); - - ErrorOr initialize_adapter(); - - virtual ErrorOr handle_device_config_change() override; - virtual void handle_queue_update(u16 queue_index) override; - u32 get_pending_events(); - void clear_pending_events(u32 event_bitmask); - - // 2D framebuffer stuff - static ErrorOr calculate_framebuffer_size(size_t width, size_t height) - { - // VirtIO resources can only map on page boundaries! - return Memory::page_round_up(sizeof(u32) * width * height); - } - - // 3D Command stuff - ErrorOr create_context(); - ErrorOr attach_resource_to_context(Graphics::VirtIOGPU::ResourceID resource_id, Graphics::VirtIOGPU::ContextID context_id); - ErrorOr submit_command_buffer(Graphics::VirtIOGPU::ContextID, Function buffer_writer); - Graphics::VirtIOGPU::Protocol::TextureFormat get_framebuffer_format() const { return Graphics::VirtIOGPU::Protocol::TextureFormat::VIRTIO_GPU_FORMAT_B8G8R8X8_UNORM; } - - auto& operation_lock() { return m_operation_lock; } - Graphics::VirtIOGPU::ResourceID allocate_resource_id(); - - PhysicalAddress start_of_scratch_space() const { return m_scratch_space->physical_page(0)->paddr(); } - AK::BinaryBufferWriter create_scratchspace_writer() - { - return { Bytes(m_scratch_space->vaddr().as_ptr(), m_scratch_space->size()) }; - } - ErrorOr synchronous_virtio_gpu_command(size_t microseconds_timeout, PhysicalAddress buffer_start, size_t request_size, size_t response_size); - - ErrorOr create_2d_resource(Graphics::VirtIOGPU::Protocol::Rect rect); - ErrorOr create_3d_resource(Graphics::VirtIOGPU::Protocol::Resource3DSpecification const& resource_3d_specification); - ErrorOr delete_resource(Graphics::VirtIOGPU::ResourceID resource_id); - ErrorOr ensure_backing_storage(Graphics::VirtIOGPU::ResourceID resource_id, Memory::Region const& region, size_t buffer_offset, size_t buffer_length); - ErrorOr detach_backing_storage(Graphics::VirtIOGPU::ResourceID resource_id); - ErrorOr set_scanout_resource(Graphics::VirtIOGPU::ScanoutID scanout, Graphics::VirtIOGPU::ResourceID resource_id, Graphics::VirtIOGPU::Protocol::Rect rect); - ErrorOr transfer_framebuffer_data_to_host(Graphics::VirtIOGPU::ScanoutID scanout, Graphics::VirtIOGPU::ResourceID resource_id, Graphics::VirtIOGPU::Protocol::Rect const& rect); - ErrorOr flush_displayed_image(Graphics::VirtIOGPU::ResourceID resource_id, Graphics::VirtIOGPU::Protocol::Rect const& dirty_rect); - ErrorOr query_and_set_edid(u32 scanout_id, VirtIODisplayConnector& display_connector); - - size_t m_num_scanouts { 0 }; - Scanout m_scanouts[VIRTIO_GPU_MAX_SCANOUTS]; - - VirtIO::Configuration const* m_device_configuration { nullptr }; - // Note: Resource ID 0 is invalid, and we must not allocate 0 as the first resource ID. - Atomic m_resource_id_counter { 1 }; - SpinlockProtected m_active_context_ids {}; - LockRefPtr m_3d_device; - bool m_has_virgl_support { false }; - - Spinlock m_operation_lock {}; - NonnullOwnPtr m_scratch_space; -}; -} diff --git a/Kernel/Devices/GPU/VirtIO/Protocol.h b/Kernel/Devices/GPU/VirtIO/Protocol.h deleted file mode 100644 index 16538da1f0e..00000000000 --- a/Kernel/Devices/GPU/VirtIO/Protocol.h +++ /dev/null @@ -1,334 +0,0 @@ -/* - * Copyright (c) 2021, Sahan Fernando - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include - -#define VIRTIO_GPU_MAX_SCANOUTS 16 - -namespace Kernel::Graphics::VirtIOGPU { -AK_TYPEDEF_DISTINCT_ORDERED_ID(u32, ContextID); -AK_TYPEDEF_DISTINCT_ORDERED_ID(u32, ResourceID); -AK_TYPEDEF_DISTINCT_ORDERED_ID(u32, ScanoutID); -}; - -#define VREND_MAX_CTX 64 - -#define VIRGL_BIND_DEPTH_STENCIL (1 << 0) -#define VIRGL_BIND_RENDER_TARGET (1 << 1) -#define VIRGL_BIND_SAMPLER_VIEW (1 << 3) -#define VIRGL_BIND_VERTEX_BUFFER (1 << 4) -#define VIRGL_BIND_INDEX_BUFFER (1 << 5) -#define VIRGL_BIND_CONSTANT_BUFFER (1 << 6) -#define VIRGL_BIND_DISPLAY_TARGET (1 << 7) -#define VIRGL_BIND_COMMAND_ARGS (1 << 8) -#define VIRGL_BIND_STREAM_OUTPUT (1 << 11) -#define VIRGL_BIND_SHADER_BUFFER (1 << 14) -#define VIRGL_BIND_QUERY_BUFFER (1 << 15) -#define VIRGL_BIND_CURSOR (1 << 16) -#define VIRGL_BIND_CUSTOM (1 << 17) -#define VIRGL_BIND_SCANOUT (1 << 18) - -namespace Kernel::Graphics::VirtIOGPU::Protocol { - -// Specification equivalent: enum virtio_gpu_ctrl_type -enum class CommandType : u32 { - /* 2d commands */ - VIRTIO_GPU_CMD_GET_DISPLAY_INFO = 0x0100, - VIRTIO_GPU_CMD_RESOURCE_CREATE_2D, - VIRTIO_GPU_CMD_RESOURCE_UNREF, - VIRTIO_GPU_CMD_SET_SCANOUT, - VIRTIO_GPU_CMD_RESOURCE_FLUSH, - VIRTIO_GPU_CMD_TRANSFER_TO_HOST_2D, - VIRTIO_GPU_CMD_RESOURCE_ATTACH_BACKING, - VIRTIO_GPU_CMD_RESOURCE_DETACH_BACKING, - VIRTIO_GPU_CMD_GET_CAPSET_INFO, - VIRTIO_GPU_CMD_GET_CAPSET, - VIRTIO_GPU_CMD_GET_EDID, - - /* 3d commands */ - VIRTIO_GPU_CMD_CTX_CREATE = 0x0200, - VIRTIO_GPU_CMD_CTX_DESTROY, - VIRTIO_GPU_CMD_CTX_ATTACH_RESOURCE, - VIRTIO_GPU_CMD_CTX_DETACH_RESOURCE, - VIRTIO_GPU_CMD_RESOURCE_CREATE_3D, - VIRTIO_GPU_CMD_TRANSFER_TO_HOST_3D, - VIRTIO_GPU_CMD_TRANSFER_FROM_HOST_3D, - VIRTIO_GPU_CMD_SUBMIT_3D, - VIRTIO_GPU_CMD_RESOURCE_MAP_BLOB, - VIRTIO_GPU_CMD_RESOURCE_UNMAP_BLOB, - - /* cursor commands */ - VIRTIO_GPU_CMD_UPDATE_CURSOR = 0x0300, - VIRTIO_GPU_CMD_MOVE_CURSOR, - - /* success responses */ - VIRTIO_GPU_RESP_OK_NODATA = 0x1100, - VIRTIO_GPU_RESP_OK_DISPLAY_INFO, - VIRTIO_GPU_RESP_OK_CAPSET_INFO, - VIRTIO_GPU_RESP_OK_CAPSET, - VIRTIO_GPU_RESP_OK_EDID, - - /* error responses */ - VIRTIO_GPU_RESP_ERR_UNSPEC = 0x1200, - VIRTIO_GPU_RESP_ERR_OUT_OF_MEMORY, - VIRTIO_GPU_RESP_ERR_INVALID_SCANOUT_ID, - VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID, - VIRTIO_GPU_RESP_ERR_INVALID_CONTEXT_ID, - VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER, -}; - -enum class ObjectType : u32 { - NONE, - BLEND, - RASTERIZER, - DSA, - SHADER, - VERTEX_ELEMENTS, - SAMPLER_VIEW, - SAMPLER_STATE, - SURFACE, - QUERY, - STREAMOUT_TARGET, - MSAA_SURFACE, - MAX_OBJECTS, -}; - -enum class PipeTextureTarget : u32 { - BUFFER = 0, - TEXTURE_1D, - TEXTURE_2D, - TEXTURE_3D, - TEXTURE_CUBE, - TEXTURE_RECT, - TEXTURE_1D_ARRAY, - TEXTURE_2D_ARRAY, - TEXTURE_CUBE_ARRAY, - MAX -}; - -enum class PipePrimitiveTypes : u32 { - POINTS = 0, - LINES, - LINE_LOOP, - LINE_STRIP, - TRIANGLES, - TRIANGLE_STRIP, - TRIANGLE_FAN, - QUADS, - QUAD_STRIP, - POLYGON, - LINES_ADJACENCY, - LINE_STRIP_ADJACENCY, - TRIANGLES_ADJACENCY, - TRIANGLE_STRIP_ADJACENCY, - PATCHES, - MAX -}; - -// Specification equivalent: struct virtio_gpu_ctrl_hdr -struct ControlHeader { - u32 type; - u32 flags; - u64 fence_id; - u32 context_id; - u32 padding; -}; - -// Specification equivalent: struct virtio_gpu_rect -struct Rect { - u32 x; - u32 y; - u32 width; - u32 height; -}; - -// Specification equivalent: struct virtio_gpu_resp_display_info -struct DisplayInfoResponse { - ControlHeader header; - // Specification equivalent: struct virtio_gpu_display_one - struct Display { - Rect rect; - u32 enabled; - u32 flags; - } scanout_modes[VIRTIO_GPU_MAX_SCANOUTS]; -}; - -// Specification equivalent: enum virtio_gpu_formats -enum class TextureFormat : u32 { - VIRTIO_GPU_FORMAT_B8G8R8A8_UNORM = 1, - VIRTIO_GPU_FORMAT_B8G8R8X8_UNORM = 2, - VIRTIO_GPU_FORMAT_A8R8G8B8_UNORM = 3, - VIRTIO_GPU_FORMAT_X8R8G8B8_UNORM = 4, - - VIRTIO_GPU_FORMAT_R8G8B8A8_UNORM = 67, - VIRTIO_GPU_FORMAT_X8B8G8R8_UNORM = 68, - - VIRTIO_GPU_FORMAT_A8B8G8R8_UNORM = 121, - VIRTIO_GPU_FORMAT_R8G8B8X8_UNORM = 134, -}; - -// Specification equivalent: struct virtio_gpu_resource_create_2d -struct ResourceCreate2D { - ControlHeader header; - u32 resource_id; - u32 format; - u32 width; - u32 height; -}; - -// Specification equivalent: struct virtio_gpu_resource_create_3d -struct ResourceCreate3D { - ControlHeader header; - u32 resource_id; - u32 target; - u32 format; - u32 bind; - u32 width; - u32 height; - u32 depth; - u32 array_size; - u32 last_level; - u32 nr_samples; - u32 flags; - u32 padding; -}; - -// Specification equivalent: struct virtio_gpu_resource_unref -struct ResourceUnref { - ControlHeader header; - u32 resource_id; - u32 padding; -}; - -// Specification equivalent: struct virtio_gpu_set_scanout -struct SetScanOut { - ControlHeader header; - Rect rect; - u32 scanout_id; - u32 resource_id; -}; - -// Specification equivalent: struct virtio_gpu_mem_entry -struct MemoryEntry { - u64 address; - u32 length; - u32 padding; -}; - -// Specification equivalent: struct virtio_gpu_resource_attach_backing -struct ResourceAttachBacking { - ControlHeader header; - u32 resource_id; - u32 num_entries; -}; - -// Specification equivalent: struct virtio_gpu_resource_detach_backing -struct ResourceDetachBacking { - ControlHeader header; - u32 resource_id; - u32 padding; -}; - -// Specification equivalent: struct virtio_gpu_transfer_to_host_2d -struct TransferToHost2D { - ControlHeader header; - Rect rect; - u64 offset; - u32 resource_id; - u32 padding; -}; - -// Specification equivalent: struct virtio_gpu_resource_flush -struct ResourceFlush { - ControlHeader header; - Rect rect; - u32 resource_id; - u32 padding; -}; - -// Specification equivalent: struct virtio_gpu_get_edid -struct GetEDID { - ControlHeader header; - u32 scanout_id; - u32 padding; -}; - -// Specification equivalent: struct virtio_gpu_resp_edid -struct GetEDIDResponse { - ControlHeader header; - u32 size; - u32 padding; - u8 edid[1024]; -}; - -// No equivalent in specification -struct ContextCreate { - ControlHeader header; - u32 name_length; - u32 padding; - AK::Array debug_name; -}; - -static_assert(sizeof(ContextCreate::debug_name) == 64); - -// No equivalent in specification -struct ContextAttachResource { - ControlHeader header; - u32 resource_id; - u32 padding; -}; - -// No equivalent in specification -struct CommandSubmit { - ControlHeader header; - u32 size; - u32 padding; -}; - -namespace Gallium { - -enum class PipeTextureTarget : u32 { - BUFFER, - TEXTURE_1D, - TEXTURE_2D, - TEXTURE_3D, - TEXTURE_CUBE, - TEXTURE_RECT, - TEXTURE_1D_ARRAY, - TEXTURE_2D_ARRAY, - TEXTURE_CUBE_ARRAY, - MAX_TEXTURE_TYPES, -}; - -enum class ShaderType : u32 { - SHADER_VERTEX = 0, - SHADER_FRAGMENT, - SHADER_GEOMETRY, - SHADER_TESS_CTRL, - SHADER_TESS_EVAL, - SHADER_COMPUTE, - SHADER_TYPES -}; - -} - -struct Resource3DSpecification { - Gallium::PipeTextureTarget target; - u32 format; - u32 bind; - u32 width; - u32 height; - u32 depth; - u32 array_size; - u32 last_level; - u32 nr_samples; - u32 flags; - u32 padding; -}; - -} diff --git a/Kernel/Devices/Generic/ConsoleDevice.cpp b/Kernel/Devices/Generic/ConsoleDevice.cpp deleted file mode 100644 index 53e4d4015c8..00000000000 --- a/Kernel/Devices/Generic/ConsoleDevice.cpp +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#if ARCH(X86_64) -# include -#endif -#include -#include -#include -#include -#include - -namespace Kernel { - -Spinlock g_console_lock {}; - -UNMAP_AFTER_INIT NonnullLockRefPtr ConsoleDevice::must_create() -{ - auto device_or_error = DeviceManagement::try_create_device(); - VERIFY(!device_or_error.is_error()); - return device_or_error.release_value(); -} - -UNMAP_AFTER_INIT ConsoleDevice::ConsoleDevice() - : CharacterDevice(5, 1) -{ -} - -UNMAP_AFTER_INIT ConsoleDevice::~ConsoleDevice() = default; - -bool ConsoleDevice::can_read(Kernel::OpenFileDescription const&, u64) const -{ - return false; -} - -ErrorOr ConsoleDevice::read(OpenFileDescription&, u64, Kernel::UserOrKernelBuffer&, size_t) -{ - // FIXME: Implement reading from the console. - // Maybe we could use a ring buffer for this device? - return 0; -} - -ErrorOr ConsoleDevice::write(OpenFileDescription&, u64, Kernel::UserOrKernelBuffer const& data, size_t size) -{ - if (!size) - return 0; - - return data.read_buffered<256>(size, [&](ReadonlyBytes readonly_bytes) { - for (auto const& byte : readonly_bytes) - put_char(byte); - return readonly_bytes.size(); - }); -} - -void ConsoleDevice::put_char(char ch) -{ - Kernel::SpinlockLocker lock(g_console_lock); - dbgputchar(ch); - m_logbuffer.enqueue(ch); -} - -} diff --git a/Kernel/Devices/Generic/ConsoleDevice.h b/Kernel/Devices/Generic/ConsoleDevice.h deleted file mode 100644 index d9eb6377f7d..00000000000 --- a/Kernel/Devices/Generic/ConsoleDevice.h +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include - -namespace Kernel { - -extern Spinlock g_console_lock; - -class ConsoleDevice final : public CharacterDevice { - friend class DeviceManagement; - -public: - static NonnullLockRefPtr must_create(); - - virtual ~ConsoleDevice() override; - - // ^CharacterDevice - virtual bool can_read(Kernel::OpenFileDescription const&, u64) const override; - virtual bool can_write(Kernel::OpenFileDescription const&, u64) const override { return true; } - virtual ErrorOr read(OpenFileDescription&, u64, Kernel::UserOrKernelBuffer&, size_t) override; - virtual ErrorOr write(OpenFileDescription&, u64, Kernel::UserOrKernelBuffer const&, size_t) override; - virtual StringView class_name() const override { return "Console"sv; } - // ^Device - virtual bool is_openable_by_jailed_processes() const override { return true; } - - void put_char(char); - - CircularQueue const& logbuffer() const { return m_logbuffer; } - -private: - ConsoleDevice(); - CircularQueue m_logbuffer; -}; - -} diff --git a/Kernel/Devices/Generic/DeviceControlDevice.cpp b/Kernel/Devices/Generic/DeviceControlDevice.cpp deleted file mode 100644 index fdf3a8b7a6c..00000000000 --- a/Kernel/Devices/Generic/DeviceControlDevice.cpp +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Copyright (c) 2021, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include - -namespace Kernel { - -UNMAP_AFTER_INIT NonnullLockRefPtr DeviceControlDevice::must_create() -{ - auto device_control_device_or_error = DeviceManagement::try_create_device(); - // FIXME: Find a way to propagate errors - VERIFY(!device_control_device_or_error.is_error()); - return device_control_device_or_error.release_value(); -} - -bool DeviceControlDevice::can_read(OpenFileDescription const&, u64) const -{ - return DeviceManagement::the().event_queue({}).with([](auto& queue) -> bool { - return !queue.is_empty(); - }); -} - -UNMAP_AFTER_INIT DeviceControlDevice::DeviceControlDevice() - : CharacterDevice(2, 10) -{ -} - -UNMAP_AFTER_INIT DeviceControlDevice::~DeviceControlDevice() = default; - -ErrorOr DeviceControlDevice::read(OpenFileDescription&, u64 offset, UserOrKernelBuffer& buffer, size_t size) -{ - if (offset != 0) - return Error::from_errno(EINVAL); - if ((size % sizeof(DeviceEvent)) != 0) - return Error::from_errno(EOVERFLOW); - - return DeviceManagement::the().event_queue({}).with([&](auto& queue) -> ErrorOr { - size_t nread = 0; - for (size_t event_index = 0; event_index < (size / sizeof(DeviceEvent)); event_index++) { - if (queue.is_empty()) - break; - auto event = queue.dequeue(); - TRY(buffer.write(&event, nread, sizeof(DeviceEvent))); - nread += sizeof(DeviceEvent); - } - return nread; - }); -} - -ErrorOr DeviceControlDevice::ioctl(OpenFileDescription&, unsigned request, Userspace arg) -{ - switch (request) { - case DEVCTL_CREATE_LOOP_DEVICE: { - unsigned fd { 0 }; - TRY(copy_from_user(&fd, static_ptr_cast(arg))); - auto file_description = TRY(Process::current().open_file_description(fd)); - auto device = TRY(LoopDevice::create_with_file_description(file_description)); - unsigned index = device->index(); - return copy_to_user(static_ptr_cast(arg), &index); - } - case DEVCTL_DESTROY_LOOP_DEVICE: { - unsigned index { 0 }; - TRY(copy_from_user(&index, static_ptr_cast(arg))); - return LoopDevice::all_instances().with([index](auto& list) -> ErrorOr { - for (auto& device : list) { - if (device.index() == index) { - device.remove({}); - return {}; - } - } - return Error::from_errno(ENODEV); - }); - } - default: - return Error::from_errno(EINVAL); - }; -} - -} diff --git a/Kernel/Devices/Generic/DeviceControlDevice.h b/Kernel/Devices/Generic/DeviceControlDevice.h deleted file mode 100644 index aa98f373dc4..00000000000 --- a/Kernel/Devices/Generic/DeviceControlDevice.h +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright (c) 2021, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include - -namespace Kernel { - -class DeviceControlDevice final : public CharacterDevice { - friend class DeviceManagement; - -public: - static NonnullLockRefPtr must_create(); - virtual ~DeviceControlDevice() override; - -private: - DeviceControlDevice(); - - // ^CharacterDevice - virtual ErrorOr ioctl(OpenFileDescription&, unsigned request, Userspace arg) override; - virtual ErrorOr read(OpenFileDescription&, u64, UserOrKernelBuffer&, size_t) override; - virtual ErrorOr write(OpenFileDescription&, u64, UserOrKernelBuffer const&, size_t) override { return Error::from_errno(ENOTSUP); } - virtual bool can_read(OpenFileDescription const&, u64) const override; - virtual bool can_write(OpenFileDescription const&, u64) const override { return false; } - virtual StringView class_name() const override { return "DeviceControlDevice"sv; } -}; - -} diff --git a/Kernel/Devices/Generic/FullDevice.cpp b/Kernel/Devices/Generic/FullDevice.cpp deleted file mode 100644 index 344dcc45d9d..00000000000 --- a/Kernel/Devices/Generic/FullDevice.cpp +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include - -namespace Kernel { - -UNMAP_AFTER_INIT NonnullLockRefPtr FullDevice::must_create() -{ - auto full_device_or_error = DeviceManagement::try_create_device(); - // FIXME: Find a way to propagate errors - VERIFY(!full_device_or_error.is_error()); - return full_device_or_error.release_value(); -} - -UNMAP_AFTER_INIT FullDevice::FullDevice() - : CharacterDevice(1, 7) -{ -} - -UNMAP_AFTER_INIT FullDevice::~FullDevice() = default; - -bool FullDevice::can_read(OpenFileDescription const&, u64) const -{ - return true; -} - -ErrorOr FullDevice::read(OpenFileDescription&, u64, UserOrKernelBuffer& buffer, size_t size) -{ - TRY(buffer.memset(0, size)); - return size; -} - -ErrorOr FullDevice::write(OpenFileDescription&, u64, UserOrKernelBuffer const&, size_t size) -{ - if (size == 0) - return 0; - return ENOSPC; -} -} diff --git a/Kernel/Devices/Generic/FullDevice.h b/Kernel/Devices/Generic/FullDevice.h deleted file mode 100644 index 443d07721fe..00000000000 --- a/Kernel/Devices/Generic/FullDevice.h +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include - -namespace Kernel { - -class FullDevice final : public CharacterDevice { - friend class DeviceManagement; - -public: - static NonnullLockRefPtr must_create(); - virtual ~FullDevice() override; - -private: - FullDevice(); - - // ^Device - virtual bool is_openable_by_jailed_processes() const override { return true; } - - // ^CharacterDevice - virtual ErrorOr read(OpenFileDescription&, u64, UserOrKernelBuffer&, size_t) override; - virtual ErrorOr write(OpenFileDescription&, u64, UserOrKernelBuffer const&, size_t) override; - virtual bool can_read(OpenFileDescription const&, u64) const override; - virtual bool can_write(OpenFileDescription const&, u64) const override { return true; } - virtual StringView class_name() const override { return "FullDevice"sv; } -}; - -} diff --git a/Kernel/Devices/Generic/MemoryDevice.cpp b/Kernel/Devices/Generic/MemoryDevice.cpp deleted file mode 100644 index e718383d136..00000000000 --- a/Kernel/Devices/Generic/MemoryDevice.cpp +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright (c) 2021, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include - -namespace Kernel { - -UNMAP_AFTER_INIT NonnullLockRefPtr MemoryDevice::must_create() -{ - auto memory_device_or_error = DeviceManagement::try_create_device(); - // FIXME: Find a way to propagate errors - VERIFY(!memory_device_or_error.is_error()); - return memory_device_or_error.release_value(); -} - -UNMAP_AFTER_INIT MemoryDevice::MemoryDevice() - : CharacterDevice(1, 1) -{ -} - -UNMAP_AFTER_INIT MemoryDevice::~MemoryDevice() = default; - -ErrorOr MemoryDevice::read(OpenFileDescription&, u64 offset, UserOrKernelBuffer& buffer, size_t length) -{ - if (!MM.is_allowed_to_read_physical_memory_for_userspace(PhysicalAddress(offset), length)) { - dbgln_if(MEMORY_DEVICE_DEBUG, "MemoryDevice: Trying to read physical memory at {} for range of {} bytes failed due to violation of access", PhysicalAddress(offset), length); - return EINVAL; - } - auto mapping = TRY(Memory::map_typed(PhysicalAddress(offset), length)); - - auto bytes = ReadonlyBytes { mapping.ptr(), length }; - TRY(buffer.write(bytes)); - return length; -} - -ErrorOr> MemoryDevice::vmobject_for_mmap(Process&, Memory::VirtualRange const& range, u64& offset, bool) -{ - auto viewed_address = PhysicalAddress(offset); - - // Note: This check happens to guard against possible memory leak. - // For example, if we try to mmap physical memory from 0x1000 to 0x2000 and you - // can actually mmap only from 0x1001, then we would fail as usual. - // However, in such case if we mmap from 0x1002, we are technically not violating - // any rules, besides the fact that we mapped an entire page with two bytes which we - // were not supposed to see. To prevent that, if we use mmap(2) syscall, we should - // always consider the start page to be aligned on PAGE_SIZE, or to be more precise - // is to be set to the page base of that start address. - VERIFY(viewed_address == viewed_address.page_base()); - - if (!MM.is_allowed_to_read_physical_memory_for_userspace(viewed_address, range.size())) { - dbgln_if(MEMORY_DEVICE_DEBUG, "MemoryDevice: Trying to mmap physical memory at {} for range of {} bytes failed due to violation of access", viewed_address, range.size()); - return EINVAL; - } - - offset = 0; - return TRY(Memory::AnonymousVMObject::try_create_for_physical_range(viewed_address, range.size())); -} - -} diff --git a/Kernel/Devices/Generic/MemoryDevice.h b/Kernel/Devices/Generic/MemoryDevice.h deleted file mode 100644 index e2ded373a62..00000000000 --- a/Kernel/Devices/Generic/MemoryDevice.h +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright (c) 2021, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include - -namespace Kernel { - -class MemoryDevice final : public CharacterDevice { - friend class DeviceManagement; - -public: - static NonnullLockRefPtr must_create(); - ~MemoryDevice(); - - virtual ErrorOr> vmobject_for_mmap(Process&, Memory::VirtualRange const&, u64& offset, bool shared) override; - -private: - MemoryDevice(); - - virtual StringView class_name() const override { return "MemoryDevice"sv; } - virtual bool can_read(OpenFileDescription const&, u64) const override { return true; } - virtual bool can_write(OpenFileDescription const&, u64) const override { return false; } - virtual bool is_seekable() const override { return true; } - virtual ErrorOr read(OpenFileDescription&, u64, UserOrKernelBuffer&, size_t) override; - virtual ErrorOr write(OpenFileDescription&, u64, UserOrKernelBuffer const&, size_t) override { return EINVAL; } -}; - -} diff --git a/Kernel/Devices/Generic/NullDevice.cpp b/Kernel/Devices/Generic/NullDevice.cpp deleted file mode 100644 index b82e4ae0725..00000000000 --- a/Kernel/Devices/Generic/NullDevice.cpp +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include - -namespace Kernel { - -UNMAP_AFTER_INIT NonnullLockRefPtr NullDevice::must_initialize() -{ - auto null_device_or_error = DeviceManagement::try_create_device(); - // FIXME: Find a way to propagate errors - VERIFY(!null_device_or_error.is_error()); - return null_device_or_error.release_value(); -} - -UNMAP_AFTER_INIT NullDevice::NullDevice() - : CharacterDevice(1, 3) -{ -} - -UNMAP_AFTER_INIT NullDevice::~NullDevice() = default; - -bool NullDevice::can_read(OpenFileDescription const&, u64) const -{ - return true; -} - -ErrorOr NullDevice::read(OpenFileDescription&, u64, UserOrKernelBuffer&, size_t) -{ - return 0; -} - -ErrorOr NullDevice::write(OpenFileDescription&, u64, UserOrKernelBuffer const&, size_t buffer_size) -{ - return buffer_size; -} - -} diff --git a/Kernel/Devices/Generic/NullDevice.h b/Kernel/Devices/Generic/NullDevice.h deleted file mode 100644 index c7767a1d589..00000000000 --- a/Kernel/Devices/Generic/NullDevice.h +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include - -namespace Kernel { - -class NullDevice final : public CharacterDevice { - friend class DeviceManagement; - -public: - virtual ~NullDevice() override; - - static NonnullLockRefPtr must_initialize(); - -private: - NullDevice(); - - // ^Device - virtual bool is_openable_by_jailed_processes() const override { return true; } - - // ^CharacterDevice - virtual ErrorOr read(OpenFileDescription&, u64, UserOrKernelBuffer&, size_t) override; - virtual ErrorOr write(OpenFileDescription&, u64, UserOrKernelBuffer const&, size_t) override; - virtual bool can_write(OpenFileDescription const&, u64) const override { return true; } - virtual bool can_read(OpenFileDescription const&, u64) const override; - virtual StringView class_name() const override { return "NullDevice"sv; } - virtual bool is_seekable() const override { return true; } -}; - -} diff --git a/Kernel/Devices/Generic/PCSpeakerDevice.cpp b/Kernel/Devices/Generic/PCSpeakerDevice.cpp deleted file mode 100644 index 1b41ff8f02e..00000000000 --- a/Kernel/Devices/Generic/PCSpeakerDevice.cpp +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright (c) 2023, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#if ARCH(X86_64) -# include -#endif -#include -#include -#include -#include - -namespace Kernel { - -UNMAP_AFTER_INIT NonnullRefPtr PCSpeakerDevice::must_create() -{ - auto device = MUST(DeviceManagement::try_create_device()); - return *device; -} - -UNMAP_AFTER_INIT PCSpeakerDevice::PCSpeakerDevice() - : CharacterDevice(1, 10) -{ -} - -UNMAP_AFTER_INIT PCSpeakerDevice::~PCSpeakerDevice() = default; - -bool PCSpeakerDevice::can_read(OpenFileDescription const&, u64) const -{ - return true; -} - -ErrorOr PCSpeakerDevice::read(OpenFileDescription&, u64, UserOrKernelBuffer&, size_t) -{ - return Error::from_errno(ENOTIMPL); -} - -ErrorOr PCSpeakerDevice::write(OpenFileDescription&, u64, UserOrKernelBuffer const& buffer, size_t buffer_size) -{ - if (!kernel_command_line().is_pc_speaker_enabled()) - return Error::from_errno(ENOTSUP); - if (buffer_size % sizeof(BeepInstruction) != 0) - return Error::from_errno(EINVAL); - BeepInstruction instruction {}; - TRY(buffer.read(&instruction, sizeof(BeepInstruction))); - if (instruction.tone < 20 || instruction.tone > 20000) - return Error::from_errno(EINVAL); - if (instruction.milliseconds_duration == 0) - return Error::from_errno(EINVAL); -#if ARCH(X86_64) - PCSpeaker::tone_on(instruction.tone); - auto result = Thread::current()->sleep(Duration::from_milliseconds(instruction.milliseconds_duration)); - PCSpeaker::tone_off(); - if (result.was_interrupted()) - return Error::from_errno(EINTR); - return sizeof(BeepInstruction); -#else - return Error::from_errno(ENOTIMPL); -#endif -} - -} diff --git a/Kernel/Devices/Generic/PCSpeakerDevice.h b/Kernel/Devices/Generic/PCSpeakerDevice.h deleted file mode 100644 index 663bac86837..00000000000 --- a/Kernel/Devices/Generic/PCSpeakerDevice.h +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright (c) 2023, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include - -namespace Kernel { - -class PCSpeakerDevice final : public CharacterDevice { - friend class DeviceManagement; - -public: - virtual ~PCSpeakerDevice() override; - - static NonnullRefPtr must_create(); - -private: - PCSpeakerDevice(); - - // ^Device - virtual bool is_openable_by_jailed_processes() const override { return true; } - - // ^CharacterDevice - virtual ErrorOr read(OpenFileDescription&, u64, UserOrKernelBuffer&, size_t) override; - virtual ErrorOr write(OpenFileDescription&, u64, UserOrKernelBuffer const&, size_t) override; - virtual bool can_write(OpenFileDescription const&, u64) const override { return true; } - virtual bool can_read(OpenFileDescription const&, u64) const override; - virtual StringView class_name() const override { return "PCSpeakerDevice"sv; } - virtual bool is_seekable() const override { return true; } -}; - -} diff --git a/Kernel/Devices/Generic/RandomDevice.cpp b/Kernel/Devices/Generic/RandomDevice.cpp deleted file mode 100644 index b59c5a80434..00000000000 --- a/Kernel/Devices/Generic/RandomDevice.cpp +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include - -namespace Kernel { - -UNMAP_AFTER_INIT NonnullLockRefPtr RandomDevice::must_create() -{ - auto random_device_or_error = DeviceManagement::try_create_device(); - // FIXME: Find a way to propagate errors - VERIFY(!random_device_or_error.is_error()); - return random_device_or_error.release_value(); -} - -UNMAP_AFTER_INIT RandomDevice::RandomDevice() - : CharacterDevice(1, 8) -{ -} - -UNMAP_AFTER_INIT RandomDevice::~RandomDevice() = default; - -bool RandomDevice::can_read(OpenFileDescription const&, u64) const -{ - return true; -} - -ErrorOr RandomDevice::read(OpenFileDescription&, u64, UserOrKernelBuffer& buffer, size_t size) -{ - return buffer.write_buffered<256>(size, [&](Bytes bytes) { - get_good_random_bytes(bytes); - return bytes.size(); - }); -} - -ErrorOr RandomDevice::write(OpenFileDescription&, u64, UserOrKernelBuffer const&, size_t size) -{ - // FIXME: Use input for entropy? I guess that could be a neat feature? - return size; -} - -} diff --git a/Kernel/Devices/Generic/RandomDevice.h b/Kernel/Devices/Generic/RandomDevice.h deleted file mode 100644 index 9983635df76..00000000000 --- a/Kernel/Devices/Generic/RandomDevice.h +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include - -namespace Kernel { - -class RandomDevice final : public CharacterDevice { - friend class DeviceManagement; - -public: - static NonnullLockRefPtr must_create(); - virtual ~RandomDevice() override; - -private: - RandomDevice(); - - // ^Device - virtual bool is_openable_by_jailed_processes() const override { return true; } - - // ^CharacterDevice - virtual ErrorOr read(OpenFileDescription&, u64, UserOrKernelBuffer&, size_t) override; - virtual ErrorOr write(OpenFileDescription&, u64, UserOrKernelBuffer const&, size_t) override; - virtual bool can_read(OpenFileDescription const&, u64) const override; - virtual bool can_write(OpenFileDescription const&, u64) const override { return true; } - virtual StringView class_name() const override { return "RandomDevice"sv; } -}; - -} diff --git a/Kernel/Devices/Generic/SelfTTYDevice.cpp b/Kernel/Devices/Generic/SelfTTYDevice.cpp deleted file mode 100644 index d4fd310341f..00000000000 --- a/Kernel/Devices/Generic/SelfTTYDevice.cpp +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright (c) 2022, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include - -namespace Kernel { - -UNMAP_AFTER_INIT NonnullLockRefPtr SelfTTYDevice::must_create() -{ - auto self_tty_device_or_error = DeviceManagement::try_create_device(); - // FIXME: Find a way to propagate errors - VERIFY(!self_tty_device_or_error.is_error()); - return self_tty_device_or_error.release_value(); -} - -ErrorOr> SelfTTYDevice::open(int options) -{ - // Note: If for some odd reason we try to open this device (early on boot?) - // while there's no current Process assigned, don't fail and return an error. - if (!Process::has_current()) - return Error::from_errno(ESRCH); - auto& current_process = Process::current(); - auto tty = current_process.tty(); - if (!tty) - return Error::from_errno(ENXIO); - auto description = TRY(OpenFileDescription::try_create(*tty)); - description->set_rw_mode(options); - description->set_file_flags(options); - return description; -} - -bool SelfTTYDevice::can_read(OpenFileDescription const&, u64) const -{ - VERIFY_NOT_REACHED(); -} - -bool SelfTTYDevice::can_write(OpenFileDescription const&, u64) const -{ - VERIFY_NOT_REACHED(); -} - -ErrorOr SelfTTYDevice::read(OpenFileDescription&, u64, UserOrKernelBuffer&, size_t) -{ - VERIFY_NOT_REACHED(); -} - -ErrorOr SelfTTYDevice::write(OpenFileDescription&, u64, UserOrKernelBuffer const&, size_t) -{ - VERIFY_NOT_REACHED(); -} - -UNMAP_AFTER_INIT SelfTTYDevice::SelfTTYDevice() - : CharacterDevice(5, 0) -{ -} - -UNMAP_AFTER_INIT SelfTTYDevice::~SelfTTYDevice() -{ -} - -} diff --git a/Kernel/Devices/Generic/SelfTTYDevice.h b/Kernel/Devices/Generic/SelfTTYDevice.h deleted file mode 100644 index 868a44973f1..00000000000 --- a/Kernel/Devices/Generic/SelfTTYDevice.h +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright (c) 2022, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include - -namespace Kernel { - -class SelfTTYDevice final : public CharacterDevice { - friend class DeviceManagement; - -public: - static NonnullLockRefPtr must_create(); - virtual ~SelfTTYDevice() override; - -private: - SelfTTYDevice(); - - // ^Device - virtual bool is_openable_by_jailed_processes() const override { return true; } - - // ^CharacterDevice - virtual ErrorOr> open(int options) override; - virtual ErrorOr read(OpenFileDescription&, u64, UserOrKernelBuffer&, size_t) override; - virtual ErrorOr write(OpenFileDescription&, u64, UserOrKernelBuffer const&, size_t) override; - virtual bool can_read(OpenFileDescription const&, u64) const override; - virtual bool can_write(OpenFileDescription const&, u64) const override; - virtual StringView class_name() const override { return "SelfTTYDevice"sv; } -}; - -} diff --git a/Kernel/Devices/Generic/ZeroDevice.cpp b/Kernel/Devices/Generic/ZeroDevice.cpp deleted file mode 100644 index fa7efc8d517..00000000000 --- a/Kernel/Devices/Generic/ZeroDevice.cpp +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include - -namespace Kernel { - -UNMAP_AFTER_INIT NonnullLockRefPtr ZeroDevice::must_create() -{ - auto zero_device_or_error = DeviceManagement::try_create_device(); - // FIXME: Find a way to propagate errors - VERIFY(!zero_device_or_error.is_error()); - return zero_device_or_error.release_value(); -} - -UNMAP_AFTER_INIT ZeroDevice::ZeroDevice() - : CharacterDevice(1, 5) -{ -} - -UNMAP_AFTER_INIT ZeroDevice::~ZeroDevice() = default; - -bool ZeroDevice::can_read(OpenFileDescription const&, u64) const -{ - return true; -} - -ErrorOr ZeroDevice::read(OpenFileDescription&, u64, UserOrKernelBuffer& buffer, size_t size) -{ - TRY(buffer.memset(0, size)); - return size; -} - -ErrorOr ZeroDevice::write(OpenFileDescription&, u64, UserOrKernelBuffer const&, size_t size) -{ - return size; -} - -} diff --git a/Kernel/Devices/Generic/ZeroDevice.h b/Kernel/Devices/Generic/ZeroDevice.h deleted file mode 100644 index c539d4f0481..00000000000 --- a/Kernel/Devices/Generic/ZeroDevice.h +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include - -namespace Kernel { - -class ZeroDevice final : public CharacterDevice { - friend class DeviceManagement; - -public: - static NonnullLockRefPtr must_create(); - virtual ~ZeroDevice() override; - -private: - ZeroDevice(); - - // ^Device - virtual bool is_openable_by_jailed_processes() const override { return true; } - - // ^CharacterDevice - virtual ErrorOr read(OpenFileDescription&, u64, UserOrKernelBuffer&, size_t) override; - virtual ErrorOr write(OpenFileDescription&, u64, UserOrKernelBuffer const&, size_t) override; - virtual bool can_read(OpenFileDescription const&, u64) const override; - virtual bool can_write(OpenFileDescription const&, u64) const override { return true; } - virtual StringView class_name() const override { return "ZeroDevice"sv; } -}; - -} diff --git a/Kernel/Devices/HID/AllMiceDevice.cpp b/Kernel/Devices/HID/AllMiceDevice.cpp deleted file mode 100644 index 1f0716de306..00000000000 --- a/Kernel/Devices/HID/AllMiceDevice.cpp +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright (c) 2023, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include - -namespace Kernel { - -NonnullRefPtr AllMiceDevice::must_create() -{ - return *MUST(DeviceManagement::try_create_device()); -} - -AllMiceDevice::AllMiceDevice() - : CharacterDevice(12, 0) -{ -} - -void AllMiceDevice::enqueue_mouse_packet(MousePacket packet) -{ - { - SpinlockLocker lock(m_queue_lock); - m_queue.enqueue(packet); - } - evaluate_block_conditions(); -} - -AllMiceDevice::~AllMiceDevice() = default; - -bool AllMiceDevice::can_read(OpenFileDescription const&, u64) const -{ - SpinlockLocker lock(m_queue_lock); - return !m_queue.is_empty(); -} - -ErrorOr AllMiceDevice::read(OpenFileDescription&, u64, UserOrKernelBuffer& buffer, size_t size) -{ - VERIFY(size > 0); - size_t nread = 0; - size_t remaining_space_in_buffer = static_cast(size) - nread; - SpinlockLocker lock(m_queue_lock); - while (!m_queue.is_empty() && remaining_space_in_buffer) { - auto packet = m_queue.dequeue(); - - size_t bytes_read_from_packet = min(remaining_space_in_buffer, sizeof(MousePacket)); - TRY(buffer.write(&packet, nread, bytes_read_from_packet)); - nread += bytes_read_from_packet; - remaining_space_in_buffer -= bytes_read_from_packet; - } - return nread; -} - -} diff --git a/Kernel/Devices/HID/AllMiceDevice.h b/Kernel/Devices/HID/AllMiceDevice.h deleted file mode 100644 index 0c2580d06a3..00000000000 --- a/Kernel/Devices/HID/AllMiceDevice.h +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright (c) 2023, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include - -namespace Kernel { - -class AllMiceDevice final : public CharacterDevice { - friend class DeviceManagement; - -public: - static NonnullRefPtr must_create(); - - virtual ~AllMiceDevice() override; - - void enqueue_mouse_packet(MousePacket packet); - -private: - AllMiceDevice(); - - // ^CharacterDevice - virtual StringView class_name() const override { return "AllMiceDevice"sv; } - virtual ErrorOr read(OpenFileDescription&, u64, UserOrKernelBuffer&, size_t) override; - virtual bool can_read(OpenFileDescription const&, u64) const override; - virtual ErrorOr write(OpenFileDescription&, u64, UserOrKernelBuffer const&, size_t) override { return EINVAL; } - virtual bool can_write(OpenFileDescription const&, u64) const override { return true; } - - mutable Spinlock m_queue_lock {}; - CircularQueue m_queue; -}; -} diff --git a/Kernel/Devices/HID/Definitions.h b/Kernel/Devices/HID/Definitions.h deleted file mode 100644 index cc00914d6a8..00000000000 --- a/Kernel/Devices/HID/Definitions.h +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright (c) 2023, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include - -namespace Kernel { - -enum class ScanCodeSet { - Set1 = 1, - Set2 = 2, - Set3 = 3, -}; - -struct KeyCodeEntry { - KeyCode key_code; - u8 map_entry_index; -}; - -struct RawKeyEvent { - KeyCodeEntry code_entry; - u64 scancode { 0 }; - bool is_press_down { false }; - bool is_press() const { return is_press_down; } -}; - -} diff --git a/Kernel/Devices/HID/Device.h b/Kernel/Devices/HID/Device.h deleted file mode 100644 index 0d0a57279bb..00000000000 --- a/Kernel/Devices/HID/Device.h +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright (c) 2021, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include - -namespace Kernel { - -class HIDManagement; -class HIDDevice : public CharacterDevice { - friend class HIDManagement; - -protected: - HIDDevice(MajorNumber major, MinorNumber minor) - : CharacterDevice(major, minor) - { - } - - EntropySource m_entropy_source; - - IntrusiveListNode> m_list_node; -}; - -} diff --git a/Kernel/Devices/HID/KeyboardDevice.cpp b/Kernel/Devices/HID/KeyboardDevice.cpp deleted file mode 100644 index 5121f9548d4..00000000000 --- a/Kernel/Devices/HID/KeyboardDevice.cpp +++ /dev/null @@ -1,156 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * Copyright (c) 2021-2023, Liav A. - * Copyright (c) 2021, Edwin Hoksberg - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace Kernel { - -void KeyboardDevice::handle_input_event(KeyEvent queued_event) -{ - if (queued_event.key == Key_NumLock && queued_event.is_press()) - m_num_lock_on = !m_num_lock_on; - - queued_event.flags |= m_modifiers; - - if (queued_event.is_press() && (m_modifiers == (Mod_Alt | Mod_Shift) || m_modifiers == (Mod_Ctrl | Mod_Alt | Mod_Shift)) && queued_event.key == Key_F12) { - // Alt+Shift+F12 pressed, dump some kernel state to the debug console. - ConsoleManagement::the().switch_to_debug(); - Scheduler::dump_scheduler_state(m_modifiers == (Mod_Ctrl | Mod_Alt | Mod_Shift)); - } - - { - auto key = queued_event.key; - if (queued_event.is_press() && (m_modifiers & Mod_Alt) != 0 && key >= Key_1 && key < Key_1 + ConsoleManagement::s_max_virtual_consoles) { - // FIXME: Do something sanely here if we can't allocate a work queue? - MUST(g_io_work->try_queue([key]() { - ConsoleManagement::the().switch_to(key - Key_1); - })); - } - } - - if (!g_caps_lock_remapped_to_ctrl && queued_event.key == Key_CapsLock && queued_event.is_press()) - m_caps_lock_on = !m_caps_lock_on; - - queued_event.caps_lock_on = m_caps_lock_on; - - if (g_caps_lock_remapped_to_ctrl && queued_event.key == Key_CapsLock) { - m_caps_lock_to_ctrl_pressed = queued_event.is_press(); - update_modifier(Mod_Ctrl, m_caps_lock_to_ctrl_pressed); - } - - if (queued_event.map_entry_index != 0xFF) - queued_event.code_point = HIDManagement::the().get_char_from_character_map(queued_event, queued_event.map_entry_index); - - // If using a non-QWERTY layout, queued_event.key needs to be updated to be the same as event.code_point - KeyCode mapped_key = code_point_to_key_code(queued_event.code_point); - if (mapped_key != KeyCode::Key_Invalid) - queued_event.key = mapped_key; - - { - SpinlockLocker locker(HIDManagement::the().m_client_lock); - if (HIDManagement::the().m_client) - HIDManagement::the().m_client->on_key_pressed(queued_event); - } - - { - SpinlockLocker lock(m_queue_lock); - m_queue.enqueue(queued_event); - } - - evaluate_block_conditions(); -} - -ErrorOr> KeyboardDevice::try_to_initialize() -{ - return *TRY(DeviceManagement::try_create_device()); -} - -// FIXME: UNMAP_AFTER_INIT is fine for now, but for hot-pluggable devices -// like USB keyboards, we need to remove this -UNMAP_AFTER_INIT KeyboardDevice::KeyboardDevice() - : HIDDevice(85, HIDManagement::the().generate_minor_device_number_for_keyboard()) -{ -} - -// FIXME: UNMAP_AFTER_INIT is fine for now, but for hot-pluggable devices -// like USB keyboards, we need to remove this -UNMAP_AFTER_INIT KeyboardDevice::~KeyboardDevice() = default; - -bool KeyboardDevice::can_read(OpenFileDescription const&, u64) const -{ - return !m_queue.is_empty(); -} - -ErrorOr KeyboardDevice::read(OpenFileDescription&, u64, UserOrKernelBuffer& buffer, size_t size) -{ - size_t nread = 0; - SpinlockLocker lock(m_queue_lock); - while (nread < size) { - if (m_queue.is_empty()) - break; - // Don't return partial data frames. - if (size - nread < sizeof(Event)) - break; - auto event = m_queue.dequeue(); - - lock.unlock(); - - auto result = TRY(buffer.write_buffered(sizeof(Event), [&](Bytes bytes) { - memcpy(bytes.data(), &event, sizeof(Event)); - return bytes.size(); - })); - VERIFY(result == sizeof(Event)); - nread += sizeof(Event); - - lock.lock(); - } - return nread; -} - -ErrorOr KeyboardDevice::ioctl(OpenFileDescription&, unsigned request, Userspace arg) -{ - switch (request) { - case KEYBOARD_IOCTL_GET_NUM_LOCK: { - auto output = static_ptr_cast(arg); - return copy_to_user(output, &m_num_lock_on); - } - case KEYBOARD_IOCTL_SET_NUM_LOCK: { - // In this case we expect the value to be a boolean and not a pointer. - auto num_lock_value = static_cast(arg.ptr()); - if (num_lock_value != 0 && num_lock_value != 1) - return EINVAL; - m_num_lock_on = !!num_lock_value; - return {}; - } - case KEYBOARD_IOCTL_GET_CAPS_LOCK: { - auto output = static_ptr_cast(arg); - return copy_to_user(output, &m_caps_lock_on); - } - case KEYBOARD_IOCTL_SET_CAPS_LOCK: { - auto caps_lock_value = static_cast(arg.ptr()); - if (caps_lock_value != 0 && caps_lock_value != 1) - return EINVAL; - m_caps_lock_on = !!caps_lock_value; - return {}; - } - default: - return EINVAL; - }; -} - -} diff --git a/Kernel/Devices/HID/KeyboardDevice.h b/Kernel/Devices/HID/KeyboardDevice.h deleted file mode 100644 index 10b70de12a7..00000000000 --- a/Kernel/Devices/HID/KeyboardDevice.h +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright (c) 2021-2023, Liav A. - * Copyright (c) 2021, Edwin Hoksberg - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include -#include -#include -#include - -namespace Kernel { - -class KeyboardDevice : public HIDDevice { - friend class DeviceManagement; - -public: - using Event = KeyEvent; - - static ErrorOr> try_to_initialize(); - - virtual ~KeyboardDevice() override; - - // ^CharacterDevice - virtual ErrorOr read(OpenFileDescription&, u64, UserOrKernelBuffer&, size_t) override; - virtual bool can_read(OpenFileDescription const&, u64) const override; - virtual ErrorOr write(OpenFileDescription&, u64, UserOrKernelBuffer const&, size_t) override { return EINVAL; } - virtual bool can_write(OpenFileDescription const&, u64) const override { return true; } - - // ^File - virtual ErrorOr ioctl(OpenFileDescription&, unsigned request, Userspace arg) override; - - void update_modifier(u8 modifier, bool state) - { - if (state) - m_modifiers |= modifier; - else - m_modifiers &= ~modifier; - } - - u8 modifiers() const { return m_modifiers; } - - void handle_input_event(KeyEvent); - bool num_lock_on() const { return m_num_lock_on; } - -protected: - KeyboardDevice(); - mutable Spinlock m_queue_lock {}; - CircularQueue m_queue; - // ^CharacterDevice - virtual StringView class_name() const override { return "KeyboardDevice"sv; } - - u8 m_modifiers { 0 }; - bool m_caps_lock_to_ctrl_pressed { false }; - bool m_caps_lock_on { false }; - bool m_num_lock_on { false }; - - void key_state_changed(u8 raw, bool pressed); -}; -} diff --git a/Kernel/Devices/HID/Management.cpp b/Kernel/Devices/HID/Management.cpp deleted file mode 100644 index 61ef61e797f..00000000000 --- a/Kernel/Devices/HID/Management.cpp +++ /dev/null @@ -1,228 +0,0 @@ -/* - * Copyright (c) 2021, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#if ARCH(X86_64) -# include -#endif -#include -#include -#include -#include -#include - -namespace Kernel { - -Atomic g_caps_lock_remapped_to_ctrl; -static Singleton s_the; - -// clang-format off -static constexpr Keyboard::CharacterMapData DEFAULT_CHARACTER_MAP = -{ - .map = { - 0, '\033', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '-', '=', 0x08, - '\t', 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p', '[', ']', '\n', - 0, 'a', 's', 'd', 'f', 'g', 'h', 'j', 'k', 'l', ';', '\'', '`', 0, - '\\', 'z', 'x', 'c', 'v', 'b', 'n', 'm', ',', '.', '/', 0, '*', 0, - ' ', 0, 0, - //60 70 80 - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, '7', '8', '9', '-', '4', '5', '6', '+', '1', '2', '3', '0', '.', 0, 0, '\\', 0, 0, 0, - - }, - - .shift_map = { - 0, '\033', '!', '@', '#', '$', '%', '^', '&', '*', '(', ')', '_', '+', 0x08, - '\t', 'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', 'O', 'P', '{', '}', '\n', - 0, 'A', 'S', 'D', 'F', 'G', 'H', 'J', 'K', 'L', ':', '"', '~', 0, - '|', 'Z', 'X', 'C', 'V', 'B', 'N', 'M', '<', '>', '?', 0, '*', 0, - ' ', 0, 0, - //60 70 80 - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, '7', '8', '9', '-', '4', '5', '6', '+', '1', '2', '3', '0', '.', 0, 0, '|', 0, 0, 0, - - }, - - .alt_map = { - 0, '\033', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '-', '=', 0x08, - '\t', 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p', '[', ']', '\n', - 0, 'a', 's', 'd', 'f', 'g', 'h', 'j', 'k', 'l', ';', '\'', '`', 0, - '\\', 'z', 'x', 'c', 'v', 'b', 'n', 'm', ',', '.', '/', 0, '*', 0, - ' ', 0, 0, - - //60 70 80 - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, '7', '8', '9', '-', '4', '5', '6', '+', '1', '2', '3', '0', '.', 0, 0, '\\', 0, 0, 0, - - }, - - .altgr_map = { - 0, '\033', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '-', '=', 0x08, - '\t', 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p', '[', ']', '\n', - 0, 'a', 's', 'd', 'f', 'g', 'h', 'j', 'k', 'l', ';', '\'', '`', 0, - '\\', 'z', 'x', 'c', 'v', 'b', 'n', 'm', ',', '.', '/', 0, '*', 0, - ' ', 0, 0, - //60 70 80 - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, '7', '8', '9', '-', '4', '5', '6', '+', '1', '2', '3', '0', '.', 0, 0, '\\', 0, 0, 0, - }, - - .shift_altgr_map = { - 0, '\033', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '-', '=', 0x08, - '\t', 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p', '[', ']', '\n', - 0, 'a', 's', 'd', 'f', 'g', 'h', 'j', 'k', 'l', ';', '\'', '`', 0, - '\\', 'z', 'x', 'c', 'v', 'b', 'n', 'm', ',', '.', '/', 0, '*', 0, - ' ', 0, 0, - //60 70 80 - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, '7', '8', '9', '-', '4', '5', '6', '+', '1', '2', '3', '0', '.', 0, 0, '\\', 0, 0, 0, - }, -}; -// clang-format on - -void HIDManagement::enqueue_mouse_packet(Badge, MousePacket packet) -{ - m_all_mice_device->enqueue_mouse_packet(packet); -} - -void HIDManagement::set_client(KeyboardClient* client) -{ - SpinlockLocker locker(m_client_lock); - m_client = client; -} - -size_t HIDManagement::generate_minor_device_number_for_mouse() -{ - // FIXME: Lock this to prevent race conditions with hot-plugging devices! - return m_mouse_minor_number++; -} -size_t HIDManagement::generate_minor_device_number_for_keyboard() -{ - // FIXME: Lock this to prevent race conditions with hot-plugging devices! - return m_keyboard_minor_number++; -} - -UNMAP_AFTER_INIT HIDManagement::KeymapData::KeymapData() - : character_map_name(KString::must_create("en-us"sv)) - , character_map(DEFAULT_CHARACTER_MAP) -{ -} - -UNMAP_AFTER_INIT HIDManagement::HIDManagement() - : m_all_mice_device(AllMiceDevice::must_create()) -{ -} - -void HIDManagement::set_maps(NonnullOwnPtr character_map_name, Keyboard::CharacterMapData const& character_map_data) -{ - m_keymap_data.with([&](auto& keymap_data) { - keymap_data.character_map_name = move(character_map_name); - keymap_data.character_map = character_map_data; - dbgln("New Character map '{}' passed in by client.", keymap_data.character_map_name); - }); -} - -void HIDManagement::detach_standalone_hid_device(HIDDevice& device) -{ - m_standalone_hid_devices.with([&](auto& list) { - list.remove(device); - }); -} - -void HIDManagement::attach_standalone_hid_device(HIDDevice& device) -{ - m_standalone_hid_devices.with([&](auto& list) { - list.append(device); - }); -} - -UNMAP_AFTER_INIT ErrorOr HIDManagement::enumerate() -{ - // FIXME: When we have USB HID support, we should ensure that we disable - // emulation of the PS/2 controller if it was set by the BIOS. - // If ACPI indicates we have an i8042 controller and the USB controller was - // set to emulate PS/2, we should not initialize the PS/2 controller. -#if ARCH(X86_64) - auto has_i8042_controller = false; - auto i8042_controller = TRY(I8042Controller::create()); - switch (kernel_command_line().i8042_presence_mode()) { - case I8042PresenceMode::Automatic: { - // Note: If ACPI is disabled or doesn't indicate that we have an i8042, we - // still perform a manual existence check via probing, which is relevant on - // QEMU, for example. This probing check is known to not work on bare metal - // in all cases, so if we can get a 'yes' from ACPI, we skip it. - if (ACPI::Parser::the() && ACPI::Parser::the()->have_8042()) - has_i8042_controller = true; - else if (i8042_controller->check_existence_via_probing({})) - has_i8042_controller = true; - break; - } - case I8042PresenceMode::Force: { - has_i8042_controller = true; - break; - } - case I8042PresenceMode::None: { - break; - } - case I8042PresenceMode::AggressiveTest: { - if (i8042_controller->check_existence_via_probing({})) - has_i8042_controller = true; - break; - } - } - - // Note: If we happen to not have i8042 just return "gracefully" for now. - if (!has_i8042_controller) - return {}; - auto i8042_enable_first_port_translation = kernel_command_line().i8042_enable_first_port_translation() ? I8042Controller::EnableKeyboardFirstPortTranslation::Yes : I8042Controller::EnableKeyboardFirstPortTranslation::No; - if (auto result_or_error = i8042_controller->detect_devices(i8042_enable_first_port_translation); result_or_error.is_error()) - return {}; - m_hid_serial_io_controllers.with([&](auto& list) { - list.append(i8042_controller); - }); -#endif - return {}; -} - -UNMAP_AFTER_INIT ErrorOr HIDManagement::initialize() -{ - VERIFY(!s_the.is_initialized()); - s_the.ensure_instance(); - return s_the->enumerate(); -} - -HIDManagement& HIDManagement::the() -{ - return *s_the; -} - -u32 HIDManagement::get_char_from_character_map(KeyEvent event, u8 index) const -{ - VERIFY(index < CHAR_MAP_SIZE); - auto modifiers = event.modifiers(); - auto caps_lock_on = event.caps_lock_on; - - u32 code_point = 0; - m_keymap_data.with([&](auto& keymap_data) { - if (modifiers & Mod_Alt) - code_point = keymap_data.character_map.alt_map[index]; - else if ((modifiers & Mod_Shift) && (modifiers & Mod_AltGr)) - code_point = keymap_data.character_map.shift_altgr_map[index]; - else if (modifiers & Mod_Shift) - code_point = keymap_data.character_map.shift_map[index]; - else if (modifiers & Mod_AltGr) - code_point = keymap_data.character_map.altgr_map[index]; - else - code_point = keymap_data.character_map.map[index]; - }); - - if (caps_lock_on && (modifiers == 0 || modifiers == Mod_Shift)) { - if (code_point >= 'a' && code_point <= 'z') - code_point &= ~0x20; - else if (code_point >= 'A' && code_point <= 'Z') - code_point |= 0x20; - } - - return code_point; -} - -} diff --git a/Kernel/Devices/HID/Management.h b/Kernel/Devices/HID/Management.h deleted file mode 100644 index 1e9e7cf3532..00000000000 --- a/Kernel/Devices/HID/Management.h +++ /dev/null @@ -1,88 +0,0 @@ -/* - * Copyright (c) 2021, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace Kernel { - -extern Atomic g_caps_lock_remapped_to_ctrl; - -class MouseDevice; -class KeyboardDevice; -class KeyboardClient; -class HIDManagement { - friend class KeyboardDevice; - friend class MouseDevice; - friend class AllMiceDevice; - -public: - HIDManagement(); - static ErrorOr initialize(); - static HIDManagement& the(); - - ErrorOr enumerate(); - - struct KeymapData { - KeymapData(); - NonnullOwnPtr character_map_name; - Keyboard::CharacterMapData character_map; - }; - - SpinlockProtected& keymap_data() { return m_keymap_data; } - - u32 get_char_from_character_map(KeyEvent, u8) const; - - void set_client(KeyboardClient* client); - void set_maps(NonnullOwnPtr character_map_name, Keyboard::CharacterMapData const& character_map); - - void attach_standalone_hid_device(HIDDevice&); - void detach_standalone_hid_device(HIDDevice&); - - void enqueue_mouse_packet(Badge, MousePacket); - -private: - size_t generate_minor_device_number_for_mouse(); - size_t generate_minor_device_number_for_keyboard(); - - SpinlockProtected m_keymap_data {}; - size_t m_mouse_minor_number { 0 }; - size_t m_keyboard_minor_number { 0 }; - KeyboardClient* m_client { nullptr }; - - NonnullRefPtr m_all_mice_device; - - SpinlockProtected, LockRank::None> m_hid_serial_io_controllers; - // NOTE: This list is used for standalone devices, like USB HID devices - // (which are not attached via a SerialIO controller in the sense that - // there's no specific serial IO controller to coordinate their usage). - SpinlockProtected, LockRank::None> m_standalone_hid_devices; - Spinlock m_client_lock; -}; - -class KeyboardClient { -public: - virtual ~KeyboardClient() = default; - virtual void on_key_pressed(KeyEvent) = 0; -}; - -} diff --git a/Kernel/Devices/HID/MouseDevice.cpp b/Kernel/Devices/HID/MouseDevice.cpp deleted file mode 100644 index c97bc3ee799..00000000000 --- a/Kernel/Devices/HID/MouseDevice.cpp +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright (c) 2021-2023, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include - -namespace Kernel { - -ErrorOr> MouseDevice::try_to_initialize() -{ - return *TRY(DeviceManagement::try_create_device()); -} - -MouseDevice::MouseDevice() - : HIDDevice(10, HIDManagement::the().generate_minor_device_number_for_mouse()) -{ -} - -void MouseDevice::handle_mouse_packet_input_event(MousePacket packet) -{ - m_entropy_source.add_random_event(packet); - HIDManagement::the().enqueue_mouse_packet({}, packet); - { - SpinlockLocker lock(m_queue_lock); - m_queue.enqueue(packet); - } - evaluate_block_conditions(); -} - -MouseDevice::~MouseDevice() = default; - -bool MouseDevice::can_read(OpenFileDescription const&, u64) const -{ - SpinlockLocker lock(m_queue_lock); - return !m_queue.is_empty(); -} - -ErrorOr MouseDevice::read(OpenFileDescription&, u64, UserOrKernelBuffer& buffer, size_t size) -{ - VERIFY(size > 0); - size_t nread = 0; - size_t remaining_space_in_buffer = static_cast(size) - nread; - SpinlockLocker lock(m_queue_lock); - while (!m_queue.is_empty() && remaining_space_in_buffer) { - auto packet = m_queue.dequeue(); - - dbgln_if(MOUSE_DEBUG, "Mouse Read: Buttons {:x}", packet.buttons); - dbgln_if(MOUSE_DEBUG, "Mouse: X {}, Y {}, Z {}, W {}, Relative {}", packet.x, packet.y, packet.z, packet.w, packet.buttons); - dbgln_if(MOUSE_DEBUG, "Mouse Read: Filter packets"); - - size_t bytes_read_from_packet = min(remaining_space_in_buffer, sizeof(MousePacket)); - TRY(buffer.write(&packet, nread, bytes_read_from_packet)); - nread += bytes_read_from_packet; - remaining_space_in_buffer -= bytes_read_from_packet; - } - return nread; -} - -} diff --git a/Kernel/Devices/HID/MouseDevice.h b/Kernel/Devices/HID/MouseDevice.h deleted file mode 100644 index 06e31626e47..00000000000 --- a/Kernel/Devices/HID/MouseDevice.h +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright (c) 2021-2023, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include -#include -#include -#include - -namespace Kernel { - -class MouseDevice : public HIDDevice { - friend class DeviceManagement; - -public: - static ErrorOr> try_to_initialize(); - virtual ~MouseDevice() override; - - // ^CharacterDevice - virtual ErrorOr read(OpenFileDescription&, u64, UserOrKernelBuffer&, size_t) override; - virtual bool can_read(OpenFileDescription const&, u64) const override; - virtual ErrorOr write(OpenFileDescription&, u64, UserOrKernelBuffer const&, size_t) override { return EINVAL; } - virtual bool can_write(OpenFileDescription const&, u64) const override { return true; } - - void handle_mouse_packet_input_event(MousePacket); - -protected: - MouseDevice(); - // ^CharacterDevice - virtual StringView class_name() const override { return "MouseDevice"sv; } - - mutable Spinlock m_queue_lock {}; - CircularQueue m_queue; -}; - -} diff --git a/Kernel/Devices/HID/PS2/KeyboardDevice.cpp b/Kernel/Devices/HID/PS2/KeyboardDevice.cpp deleted file mode 100644 index 03ce2d48c67..00000000000 --- a/Kernel/Devices/HID/PS2/KeyboardDevice.cpp +++ /dev/null @@ -1,774 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * Copyright (c) 2021-2023, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace Kernel { - -// clang-format off -static constexpr KeyCodeEntry unshifted_scan_code_set1_key_map[0x80] = { - { Key_Invalid, 0xFF }, { Key_Escape, 1 }, { Key_1, 2 }, { Key_2, 3 }, - { Key_3, 4 }, { Key_4, 5 }, { Key_5, 6 }, { Key_6, 7 }, - { Key_7, 8 }, { Key_8, 9 }, { Key_9, 0x0A }, { Key_0, 0x0B }, - { Key_Minus, 0x0C }, { Key_Equal, 0x0D }, { Key_Backspace, 0x0E }, { Key_Tab, 0x0F }, - { Key_Q, 0x10 }, { Key_W, 0x11 }, { Key_E, 0x12 }, { Key_R, 0x13 }, - { Key_T, 0x14 }, { Key_Y, 0x15 }, { Key_U, 0x16 }, { Key_I, 0x17 }, - { Key_O, 0x18 }, { Key_P, 0x19 }, { Key_LeftBracket, 0x1A }, { Key_RightBracket, 0x1B }, - { Key_Return, 0x1C }, { Key_Control, 0x1D }, { Key_A, 0x1E }, { Key_S, 0x1F }, - { Key_D, 0x20 }, { Key_F, 0x21 }, { Key_G, 0x22 }, { Key_H, 0x23 }, - { Key_J, 0x24 }, { Key_K, 0x25 }, { Key_L, 0x26 }, { Key_Semicolon, 0x27 }, - { Key_Apostrophe, 0x28 }, { Key_Backtick, 0x29 }, { Key_LeftShift, 0xFF }, { Key_Backslash, 0x2B }, - { Key_Z, 0x2C }, { Key_X, 0x2D }, { Key_C, 0x2E }, { Key_V, 0x2F }, - { Key_B, 0x30 }, { Key_N, 0x31 }, { Key_M, 0x32 }, { Key_Comma, 0x33 }, - { Key_Period, 0x34 }, { Key_Slash, 0x35 }, { Key_RightShift, 0xFF }, { Key_Asterisk, 0x37 }, - { Key_Alt, 0xFF }, { Key_Space, 0x39 }, { Key_CapsLock, 0xFF }, { Key_F1, 0xFF }, - { Key_F2, 0xFF }, { Key_F3, 0xFF }, { Key_F4, 0xFF }, { Key_F5, 0xFF }, - { Key_F6, 0xFF }, { Key_F7, 0xFF }, { Key_F8, 0xFF }, { Key_F9, 0xFF }, - { Key_F10, 0xFF }, { Key_NumLock, 0x45 }, { Key_ScrollLock, 0xFF }, { Key_Home, 0xFF }, - { Key_Up, 0xFF }, { Key_PageUp, 0xFF }, { Key_Minus, 0x4A }, { Key_Left, 0xFF }, - { Key_Invalid, 0xFF }, { Key_Right, 0xFF }, { Key_Plus, 0x4E }, { Key_End, 0xFF }, - { Key_Down, 0xFF }, { Key_PageDown, 0xFF }, { Key_Insert, 0xFF }, { Key_Delete, 0xFF }, - { Key_Invalid, 0xFF }, { Key_Invalid, 0xFF }, { Key_Backslash, 0x56 }, { Key_F11, 0xFF }, - { Key_F12, 0xFF }, { Key_Invalid, 0xFF }, { Key_Invalid, 0xFF }, { Key_Super, 0xFF }, - { Key_Invalid, 0xFF }, { Key_Menu, 0xFF }, -}; -// clang-format on - -// clang-format off -static constexpr KeyCodeEntry shifted_scan_code_set1_key_map[0x100] = { - { Key_Invalid, 0xFF }, { Key_Escape, 1 }, { Key_ExclamationPoint, 2 }, { Key_AtSign, 3 }, - { Key_Hashtag, 4 }, { Key_Dollar, 5 }, { Key_Percent, 6 }, { Key_Circumflex, 7 }, - { Key_Ampersand, 8 }, { Key_Asterisk, 9 }, { Key_LeftParen, 0x0A }, { Key_RightParen, 0x0B }, - { Key_Underscore, 0xC }, { Key_Plus, 0x4E }, { Key_Backspace, 0x0E }, { Key_Tab, 0x0F }, - { Key_Q, 0x10 }, { Key_W, 0x11 }, { Key_E, 0x12 }, { Key_R, 0x13 }, - { Key_T, 0x14 }, { Key_Y, 0x15 }, { Key_U, 0x16 }, { Key_I, 0x17 }, - { Key_O, 0x18 }, { Key_P, 0x19 }, { Key_LeftBrace, 0x1A }, { Key_RightBrace, 0x1B }, - { Key_Return, 0x1C }, { Key_Control, 0x1D }, { Key_A, 0x1E }, { Key_S, 0x1F }, - { Key_D, 0x20 }, { Key_F, 0x21 }, { Key_G, 0x22 }, { Key_H, 0x23 }, - { Key_J, 0x24 }, { Key_K, 0x25 }, { Key_L, 0x26 }, { Key_Colon, 0x27 }, - { Key_DoubleQuote, 0x28 }, { Key_Tilde, 0x29 }, { Key_LeftShift, 0xFF }, { Key_Pipe, 0x2B }, - { Key_Z, 0x2C }, { Key_X, 0x2D }, { Key_C, 0x2E }, { Key_V, 0x2F }, - { Key_B, 0x30 }, { Key_N, 0x31 }, { Key_M, 0x32 }, { Key_LessThan, 0x33 }, - { Key_GreaterThan, 0x34 }, { Key_QuestionMark, 0x35 }, { Key_RightShift, 0xFF }, { Key_Asterisk, 0x37 }, - { Key_Alt, 0xFF }, { Key_Space, 0x39 }, { Key_CapsLock, 0xFF }, { Key_F1, 0xFF }, - { Key_F2, 0xFF }, { Key_F3, 0xFF }, { Key_F4, 0xFF }, { Key_F5, 0xFF }, - { Key_F6, 0xFF }, { Key_F7, 0xFF }, { Key_F8, 0xFF }, { Key_F9, 0xFF }, - { Key_F10, 0xFF }, { Key_NumLock, 0xFF }, { Key_ScrollLock, 0xFF }, { Key_Home, 0xFF }, - { Key_Up, 0xFF }, { Key_PageUp, 0xFF }, { Key_Minus, 0x4A }, { Key_Left, 0xFF }, - { Key_Invalid, 0xFF }, { Key_Right, 0xFF }, { Key_Plus, 0x4E }, { Key_End, 0xFF }, - { Key_Down, 0xFF }, { Key_PageDown, 0xFF }, { Key_Insert, 0xFF }, { Key_Delete, 0xFF }, - { Key_Invalid, 0xFF }, { Key_Invalid, 0xFF }, { Key_Pipe, 0x56 }, { Key_F11, 0xFF }, - { Key_F12, 0xFF }, { Key_Invalid, 0xFF }, { Key_Invalid, 0xFF }, { Key_Super, 0xFF }, - { Key_Invalid, 0xFF }, { Key_Menu, 0xFF }, -}; -// clang-format on - -// clang-format off -static constexpr KeyCodeEntry unshifted_simple_scan_code_set2_key_map_with_key_num_pad[0x84] = { - { Key_Invalid, 0xFF }, { Key_F9, 0x43 }, { Key_Invalid, 0xFF }, { Key_F5, 0x3F }, - { Key_F3, 0x3D }, { Key_F1, 0x3B }, { Key_F2, 0x3C }, { Key_F12, 0xFF }, - { Key_Invalid, 0xFF }, { Key_F10, 0x44 }, { Key_F8, 0x42 }, { Key_F6, 0x40 }, - { Key_F4, 0x3E }, { Key_Tab, 0x0F }, { Key_Backtick, 0x29 }, { Key_Invalid, 0xFF }, - { Key_Invalid, 0xFF }, { Key_Alt, 0x38 }, { Key_LeftShift, 0x2A }, { Key_Invalid, 0xFF }, - { Key_Control, 0x1D }, { Key_Q, 0x10 }, { Key_1, 2 }, { Key_Invalid, 0xFF }, - { Key_Invalid, 0xFF }, { Key_Invalid, 0xFF }, { Key_Z, 0x2C }, { Key_S, 0x1F }, - { Key_A, 0x1E }, { Key_W, 0x11 }, { Key_2, 3 }, { Key_Invalid, 0xFF }, - { Key_Invalid, 0xFF }, { Key_C, 0x2E }, { Key_X, 0x2D }, { Key_D, 0x20 }, - { Key_E, 0x12 }, { Key_4, 5 }, { Key_3, 4 }, { Key_Invalid, 0xFF }, - { Key_Invalid, 0xFF }, { Key_Space, 0x39 }, { Key_V, 0x2F }, { Key_F, 0x21 }, - { Key_T, 0x14 }, { Key_R, 0x13 }, { Key_5, 6 }, { Key_Invalid, 0xFF }, - { Key_Invalid, 0xFF }, { Key_N, 0x31 }, { Key_B, 0x30 }, { Key_H, 0x23 }, - { Key_G, 0x22 }, { Key_Y, 0x15 }, { Key_6, 7 }, { Key_Invalid, 0xFF }, - { Key_Invalid, 0xFF }, { Key_Invalid, 0xFF }, { Key_M, 0x32 }, { Key_J, 0x24 }, - { Key_U, 0x16 }, { Key_7, 8 }, { Key_8, 9 }, { Key_Invalid, 0xFF }, - { Key_Invalid, 0xFF }, { Key_Comma, 0x33 }, { Key_K, 0x25 }, { Key_I, 0x17 }, - { Key_O, 0x18 }, { Key_0, 0x0B }, { Key_9, 0x0A }, { Key_Invalid, 0xFF }, - { Key_Invalid, 0xFF }, { Key_Period, 0x34 }, { Key_Slash, 0x35 }, { Key_L, 0x26 }, - { Key_Semicolon, 0x27 }, { Key_P, 0x19 }, { Key_Minus, 0x0C }, { Key_Invalid, 0xFF }, - { Key_Invalid, 0xFF }, { Key_Invalid, 0xFF }, { Key_Apostrophe, 0x28 }, { Key_Invalid, 0xFF }, - { Key_LeftBracket, 0x1A }, { Key_Equal, 0x0D }, { Key_Invalid, 0xFF }, { Key_Invalid, 0xFF }, - { Key_CapsLock, 0x3A }, { Key_RightShift, 0x36 }, { Key_Return, 0x1C }, { Key_RightBracket, 0x1B }, - { Key_Invalid, 0xFF }, { Key_Invalid, 0xFF }, { Key_Invalid, 0xFF }, { Key_Invalid, 0xFF }, - { Key_Invalid, 0xFF }, { Key_Backslash, 0x2B }, { Key_Invalid, 0xFF }, { Key_Invalid, 0xFF }, - { Key_Invalid, 0xFF }, { Key_Invalid, 0xFF }, { Key_Backspace, 0x0E }, { Key_Invalid, 0xFF }, - // Keypad numbers from here - { Key_Invalid, 0xFF }, { Key_1, 2 }, { Key_Invalid, 0xFF }, { Key_4, 5 }, - { Key_7, 8 }, { Key_Invalid, 0xFF }, { Key_Invalid, 0xFF }, { Key_Invalid, 0xFF }, - { Key_0, 0x0B }, { Key_Period, 0x34 }, { Key_2, 3 }, { Key_5, 6 }, - { Key_6, 7 }, { Key_8, 9 }, { Key_Escape, 1 }, { Key_NumLock, 0x45 }, - { Key_F11, 0xFF }, { Key_Plus, 0x4E }, { Key_3, 4 }, { Key_Minus, 0x0C }, - { Key_Asterisk, 0x37 }, { Key_9, 0x0A }, { Key_ScrollLock, 0x46 }, { Key_Invalid, 0xFF }, - { Key_Invalid, 0xFF }, { Key_Invalid, 0xFF }, { Key_Invalid, 0xFF }, { Key_F7, 0x41 }, -}; -// clang-format on - -// clang-format off -static constexpr KeyCodeEntry unshifted_simple_scan_code_set2_key_map_with_disabled_key_num_pad[0x84] = { - { Key_Invalid, 0xFF }, { Key_F9, 0x43 }, { Key_Invalid, 0xFF }, { Key_F5, 0x3F }, - { Key_F3, 0x3D }, { Key_F1, 0x3B }, { Key_F2, 0x3C }, { Key_F12, 0xFF }, - { Key_Invalid, 0xFF }, { Key_F10, 0x44 }, { Key_F8, 0x42 }, { Key_F6, 0x40 }, - { Key_F4, 0x3E }, { Key_Tab, 0x0F }, { Key_Backtick, 0x29 }, { Key_Invalid, 0xFF }, - { Key_Invalid, 0xFF }, { Key_Alt, 0x38 }, { Key_LeftShift, 0x2A }, { Key_Invalid, 0xFF }, - { Key_Control, 0x1D }, { Key_Q, 0x10 }, { Key_1, 2 }, { Key_Invalid, 0xFF }, - { Key_Invalid, 0xFF }, { Key_Invalid, 0xFF }, { Key_Z, 0x2C }, { Key_S, 0x1F }, - { Key_A, 0x1E }, { Key_W, 0x11 }, { Key_2, 3 }, { Key_Invalid, 0xFF }, - { Key_Invalid, 0xFF }, { Key_C, 0x2E }, { Key_X, 0x2D }, { Key_D, 0x20 }, - { Key_E, 0x12 }, { Key_4, 5 }, { Key_3, 4 }, { Key_Invalid, 0xFF }, - { Key_Invalid, 0xFF }, { Key_Space, 0x39 }, { Key_V, 0x2F }, { Key_F, 0x21 }, - { Key_T, 0x14 }, { Key_R, 0x13 }, { Key_5, 6 }, { Key_Invalid, 0xFF }, - { Key_Invalid, 0xFF }, { Key_N, 0x31 }, { Key_B, 0x30 }, { Key_H, 0x23 }, - { Key_G, 0x22 }, { Key_Y, 0x15 }, { Key_6, 7 }, { Key_Invalid, 0xFF }, - { Key_Invalid, 0xFF }, { Key_Invalid, 0xFF }, { Key_M, 0x32 }, { Key_J, 0x24 }, - { Key_U, 0x16 }, { Key_7, 8 }, { Key_8, 9 }, { Key_Invalid, 0xFF }, - { Key_Invalid, 0xFF }, { Key_Comma, 0x33 }, { Key_K, 0x25 }, { Key_I, 0x17 }, - { Key_O, 0x18 }, { Key_0, 0x0B }, { Key_9, 0x0A }, { Key_Invalid, 0xFF }, - { Key_Invalid, 0xFF }, { Key_Period, 0x34 }, { Key_Slash, 0x35 }, { Key_L, 0x26 }, - { Key_Semicolon, 0x27 }, { Key_P, 0x19 }, { Key_Underscore, 0x0C }, { Key_Invalid, 0xFF }, - { Key_Invalid, 0xFF }, { Key_Invalid, 0xFF }, { Key_Apostrophe, 0x28 }, { Key_Invalid, 0xFF }, - { Key_LeftBracket, 0x1A }, { Key_Equal, 0x0D }, { Key_Invalid, 0xFF }, { Key_Invalid, 0xFF }, - { Key_CapsLock, 0x3A }, { Key_RightShift, 0x36 }, { Key_Return, 0x1C }, { Key_RightBracket, 0x1B }, - { Key_Invalid, 0xFF }, { Key_Invalid, 0xFF }, { Key_Invalid, 0xFF }, { Key_Invalid, 0xFF }, - { Key_Invalid, 0xFF }, { Key_Backslash, 0x2B }, { Key_Invalid, 0xFF }, { Key_Invalid, 0xFF }, - { Key_Invalid, 0xFF }, { Key_Invalid, 0xFF }, { Key_Backspace, 0x0E }, { Key_Invalid, 0xFF }, - // Keypad numbers from here, and disabled or converted to arrows - { Key_Invalid, 0xFF }, { Key_Invalid, 0xFF }, { Key_Invalid, 0xFF }, { Key_Left, 0x4B }, - { Key_Invalid, 0xFF }, { Key_Invalid, 0xFF }, { Key_Invalid, 0xFF }, { Key_Invalid, 0xFF }, - { Key_Insert, 0xFF }, { Key_Delete, 0xFF }, { Key_Down, 0xFF }, { Key_Invalid, 0xFF }, - { Key_Right, 0xFF }, { Key_Up, 0x48 }, { Key_Escape, 1 }, { Key_NumLock, 0x45 }, - { Key_F11, 0xFF }, { Key_Plus, 0x4E }, { Key_Invalid, 0xFF }, { Key_Minus, 0x0C }, - { Key_Asterisk, 0x37 }, { Key_Invalid, 0xFF }, { Key_ScrollLock, 0x46 }, { Key_Invalid, 0xFF }, - { Key_Invalid, 0xFF }, { Key_Invalid, 0xFF }, { Key_Invalid, 0xFF }, { Key_F7, 0x41 }, -}; -// clang-format on - -// clang-format off -static constexpr KeyCodeEntry shifted_simple_scan_code_set2_key_map_with_key_num_pad[0x84] = { - { Key_Invalid, 0xFF }, { Key_F9, 0x43 }, { Key_Invalid, 0xFF }, { Key_F5, 0x3F }, - { Key_F3, 0x3D }, { Key_F1, 0x3B }, { Key_F2, 0x3C }, { Key_F12, 0xFF }, - { Key_Invalid, 0xFF }, { Key_F10, 0x44 }, { Key_F8, 0x42 }, { Key_F6, 0x40 }, - { Key_F4, 0x3E }, { Key_Tab, 0x0F }, { Key_Backtick, 0x29 }, { Key_Invalid, 0xFF }, - { Key_Invalid, 0xFF }, { Key_Alt, 0x38 }, { Key_LeftShift, 0x2A }, { Key_Invalid, 0xFF }, - { Key_Control, 0x1D }, { Key_Q, 0x10 }, { Key_Escape, 2 }, { Key_Invalid, 0xFF }, - { Key_Invalid, 0xFF }, { Key_Invalid, 0xFF }, { Key_Z, 0x2C }, { Key_S, 0x1F }, - { Key_A, 0x1E }, { Key_W, 0x11 }, { Key_AtSign, 3 }, { Key_Invalid, 0xFF }, - { Key_Invalid, 0xFF }, { Key_C, 0x2E }, { Key_X, 0x2D }, { Key_D, 0x20 }, - { Key_E, 0x12 }, { Key_Dollar, 5 }, { Key_Hashtag, 4 }, { Key_Invalid, 0xFF }, - { Key_Invalid, 0xFF }, { Key_Space, 0x39 }, { Key_V, 0x2F }, { Key_F, 0x21 }, - { Key_T, 0x14 }, { Key_R, 0x13 }, { Key_Percent, 6 }, { Key_Invalid, 0xFF }, - { Key_Invalid, 0xFF }, { Key_N, 0x31 }, { Key_B, 0x30 }, { Key_H, 0x23 }, - { Key_G, 0x22 }, { Key_Y, 0x15 }, { Key_Circumflex, 7 }, { Key_Invalid, 0xFF }, - { Key_Invalid, 0xFF }, { Key_Invalid, 0xFF }, { Key_M, 0x32 }, { Key_J, 0x24 }, - { Key_U, 0x16 }, { Key_Ampersand, 8 }, { Key_Asterisk, 0x37 }, { Key_Invalid, 0xFF }, - { Key_Invalid, 0xFF }, { Key_LessThan, 0x33 }, { Key_K, 0x25 }, { Key_I, 0x17 }, - { Key_O, 0x18 }, { Key_RightParen, 0x0B }, { Key_LeftParen, 0x0A }, { Key_Invalid, 0xFF }, - { Key_Invalid, 0xFF }, { Key_GreaterThan, 0x34 }, { Key_Slash, 0x35 }, { Key_L, 0x26 }, - { Key_Semicolon, 0x27 }, { Key_P, 0x19 }, { Key_Minus, 0x0C }, { Key_Invalid, 0xFF }, - { Key_Invalid, 0xFF }, { Key_Invalid, 0xFF }, { Key_DoubleQuote, 0x28 }, { Key_Invalid, 0xFF }, - { Key_LeftBrace, 0x1A }, { Key_Plus, 0x4E }, { Key_Invalid, 0xFF }, { Key_Invalid, 0xFF }, - { Key_CapsLock, 0x3A }, { Key_RightShift, 0x36 }, { Key_Return, 0x1C }, { Key_RightBrace, 0x1B }, - { Key_Invalid, 0xFF }, { Key_Invalid, 0xFF }, { Key_Invalid, 0xFF }, { Key_Invalid, 0xFF }, - { Key_Invalid, 0xFF }, { Key_Pipe, 0x2B }, { Key_Invalid, 0xFF }, { Key_Invalid, 0xFF }, - { Key_Invalid, 0xFF }, { Key_Invalid, 0xFF }, { Key_Backspace, 0x0E }, { Key_Invalid, 0xFF }, - // Keypad numbers from here - { Key_Invalid, 0xFF }, { Key_1, 2 }, { Key_Invalid, 0xFF }, { Key_4, 5 }, - { Key_7, 8 }, { Key_Invalid, 0xFF }, { Key_Invalid, 0xFF }, { Key_Invalid, 0xFF }, - { Key_0, 0x0B }, { Key_Period, 0x34 }, { Key_2, 3 }, { Key_5, 6 }, - { Key_6, 7 }, { Key_8, 9 }, { Key_Escape, 1 }, { Key_NumLock, 0x45 }, - { Key_F11, 0xFF }, { Key_Plus, 0x4E }, { Key_3, 4 }, { Key_Minus, 0x0C }, - { Key_Asterisk, 0x37 }, { Key_9, 0x0A }, { Key_ScrollLock, 0x46 }, { Key_Invalid, 0xFF }, - { Key_Invalid, 0xFF }, { Key_Invalid, 0xFF }, { Key_Invalid, 0xFF }, { Key_F7, 0x41 }, -}; -// clang-format on - -// clang-format off -static constexpr KeyCodeEntry shifted_simple_scan_code_set2_key_map_with_disabled_key_num_pad[0x84] = { - { Key_Invalid, 0xFF }, { Key_F9, 0x43 }, { Key_Invalid, 0xFF }, { Key_F5, 0x3F }, - { Key_F3, 0x3D }, { Key_F1, 0x3B }, { Key_F2, 0x3C }, { Key_F12, 0xFF }, - { Key_Invalid, 0xFF }, { Key_F10, 0x44 }, { Key_F8, 0x42 }, { Key_F6, 0x40 }, - { Key_F4, 0x3E }, { Key_Tab, 0x0F }, { Key_Backtick, 0x29 }, { Key_Invalid, 0xFF }, - { Key_Invalid, 0xFF }, { Key_Alt, 0x38 }, { Key_LeftShift, 0x2A }, { Key_Invalid, 0xFF }, - { Key_Control, 0x1D }, { Key_Q, 0x10 }, { Key_Escape, 2 }, { Key_Invalid, 0xFF }, - { Key_Invalid, 0xFF }, { Key_Invalid, 0xFF }, { Key_Z, 0x2C }, { Key_S, 0x1F }, - { Key_A, 0x1E }, { Key_W, 0x11 }, { Key_AtSign, 3 }, { Key_Invalid, 0xFF }, - { Key_Invalid, 0xFF }, { Key_C, 0x2E }, { Key_X, 0x2D }, { Key_D, 0x20 }, - { Key_E, 0x12 }, { Key_Dollar, 5 }, { Key_Hashtag, 4 }, { Key_Invalid, 0xFF }, - { Key_Invalid, 0xFF }, { Key_Space, 0x39 }, { Key_V, 0x2F }, { Key_F, 0x21 }, - { Key_T, 0x14 }, { Key_R, 0x13 }, { Key_Percent, 6 }, { Key_Invalid, 0xFF }, - { Key_Invalid, 0xFF }, { Key_N, 0x31 }, { Key_B, 0x30 }, { Key_H, 0x23 }, - { Key_G, 0x22 }, { Key_Y, 0x15 }, { Key_Circumflex, 7 }, { Key_Invalid, 0xFF }, - { Key_Invalid, 0xFF }, { Key_Invalid, 0xFF }, { Key_M, 0x32 }, { Key_J, 0x24 }, - { Key_U, 0x16 }, { Key_Ampersand, 8 }, { Key_Asterisk, 0x37 }, { Key_Invalid, 0xFF }, - { Key_Invalid, 0xFF }, { Key_LessThan, 0xFF}, { Key_K, 0x25 }, { Key_I, 0x17 }, - { Key_O, 0x18 }, { Key_RightParen, 0x0B }, { Key_LeftParen, 0x0A }, { Key_Invalid, 0xFF }, - { Key_Invalid, 0xFF }, { Key_Period, 0x34 }, { Key_Slash, 0x35 }, { Key_L, 0x26 }, - { Key_Semicolon, 0x27 }, { Key_P, 0x19 }, { Key_Underscore, 0x0C }, { Key_Invalid, 0xFF }, - { Key_Invalid, 0xFF }, { Key_Invalid, 0xFF }, { Key_DoubleQuote, 0x28 }, { Key_Invalid, 0xFF }, - { Key_LeftBrace, 0x1A }, { Key_Plus, 0x4E }, { Key_Invalid, 0xFF }, { Key_Invalid, 0xFF }, - { Key_CapsLock, 0x3A }, { Key_RightShift, 0x36 }, { Key_Return, 0x1C }, { Key_RightBrace, 0x1B }, - { Key_Invalid, 0xFF }, { Key_Invalid, 0xFF }, { Key_Invalid, 0xFF }, { Key_Invalid, 0xFF }, - { Key_Invalid, 0xFF }, { Key_Pipe, 0x2B }, { Key_Invalid, 0xFF }, { Key_Invalid, 0xFF }, - { Key_Invalid, 0xFF }, { Key_Invalid, 0xFF }, { Key_Backspace, 0x0E }, { Key_Invalid, 0xFF }, - // Keypad numbers from here, and disabled or converted to arrows - { Key_Invalid, 0xFF }, { Key_Invalid, 0xFF }, { Key_Invalid, 0xFF }, { Key_Left, 0x4B }, - { Key_7, 8 }, { Key_Invalid, 0xFF }, { Key_Invalid, 0xFF }, { Key_Invalid, 0xFF }, - { Key_Insert, 0xFF }, { Key_Delete, 0xFF }, { Key_Down, 0xFF }, { Key_Invalid, 0xFF }, - { Key_Right, 0xFF }, { Key_Up, 0x48 }, { Key_Escape, 1 }, { Key_NumLock, 0x45 }, - { Key_F11, 0xFF }, { Key_Plus, 0x4E }, { Key_Invalid, 0xFF }, { Key_Minus, 0x0C }, - { Key_Asterisk, 0x37 }, { Key_Invalid, 0xFF }, { Key_ScrollLock, 0x46 }, { Key_Invalid, 0xFF }, - { Key_Invalid, 0xFF }, { Key_Invalid, 0xFF }, { Key_Invalid, 0xFF }, { Key_F7, 0x41 }, -}; -// clang-format on - -// Note: First scan code starts at actual 0xE0, 0x10, but we start from 0xE0, 0x0 -// Note: All keycode are for pressing buttons, not releasing... -// clang-format off -static constexpr KeyCodeEntry unshifted_scan_code_set2_e0_key_map[0x80] = { - { Key_Invalid, 0xFF }, { Key_Invalid, 0xFF }, { Key_Invalid, 0xFF }, { Key_Invalid, 0xFF }, - { Key_Invalid, 0xFF }, { Key_Invalid, 0xFF }, { Key_Invalid, 0xFF }, { Key_Invalid, 0xFF }, - { Key_Invalid, 0xFF }, { Key_Invalid, 0xFF }, { Key_Invalid, 0xFF }, { Key_Invalid, 0xFF }, - { Key_Invalid, 0xFF }, { Key_Invalid, 0xFF }, { Key_Invalid, 0xFF }, { Key_Invalid, 0xFF }, - { Key_BrowserSearch, 0xFF }, { Key_RightAlt, 0xFF }, { Key_Invalid, 0xFF }, { Key_Invalid, 0xFF }, - { Key_RightControl, 0xFF }, { Key_PreviousTrack, 0xFF }, { Key_Invalid, 0xFF }, { Key_Invalid, 0xFF }, - { Key_BrowserFavorites, 0xFF }, { Key_Invalid, 0xFF }, { Key_Invalid, 0xFF }, { Key_Invalid, 0xFF }, - { Key_Invalid, 0xFF }, { Key_Invalid, 0xFF }, { Key_Invalid, 0xFF }, { Key_LeftGUI, 0xFF }, - { Key_BrowserRefresh, 0xFF }, { Key_VolumeDown, 0xFF }, { Key_Invalid, 0xFF }, { Key_Mute, 0xFF }, - { Key_Invalid, 0xFF }, { Key_Invalid, 0xFF }, { Key_Invalid, 0xFF }, { Key_RightGUI, 0xFF }, - { Key_BrowserStop, 0xFF }, { Key_Invalid, 0xFF }, { Key_Invalid, 0xFF }, { Key_Calculator, 0xFF }, - { Key_Invalid, 0xFF }, { Key_Invalid, 0xFF }, { Key_Invalid, 0xFF }, { Key_Apps, 0xFF }, - { Key_BrowserForward, 0xFF }, { Key_Invalid, 0xFF }, { Key_VolumeUp, 0xFF }, { Key_Invalid, 0xFF }, - { Key_PlayPause, 0xFF }, { Key_Invalid, 0xFF }, { Key_Invalid, 0xFF }, { Key_Power, 0xFF }, - { Key_BrowserBack, 0xFF }, { Key_Invalid, 0xFF }, { Key_BrowserHome, 0xFF }, { Key_Stop, 0xFF }, - { Key_Invalid, 0xFF }, { Key_Invalid, 0xFF }, { Key_Invalid, 0xFF }, { Key_Sleep, 0xFF }, - { Key_MyComputer, 0xFF }, { Key_Invalid, 0xFF }, { Key_Invalid, 0xFF }, { Key_Invalid, 0xFF }, - { Key_Invalid, 0xFF }, { Key_Invalid, 0xFF }, { Key_Invalid, 0xFF }, { Key_Invalid, 0xFF }, - { Key_Email, 0xFF }, { Key_Invalid, 0xFF }, { Key_Slash, 0x35 }, { Key_Invalid, 0xFF }, - { Key_Invalid, 0xFF }, { Key_NextTrack, 0xFF }, { Key_Invalid, 0xFF }, { Key_Invalid, 0xFF }, - { Key_MediaSelect, 0xFF}, { Key_Invalid, 0xFF }, { Key_Invalid, 0xFF }, { Key_Invalid, 0xFF }, - { Key_Invalid, 0xFF }, { Key_Invalid, 0xFF }, { Key_Invalid, 0xFF }, { Key_Invalid, 0xFF }, - { Key_Invalid, 0xFF }, { Key_Invalid, 0xFF }, { Key_Return, 0x1C }, { Key_Invalid, 0xFF }, - { Key_Invalid, 0xFF }, { Key_Invalid, 0xFF }, { Key_Wake, 0xFF }, { Key_Invalid, 0xFF }, - { Key_Invalid, 0xFF }, { Key_Invalid, 0xFF }, { Key_Invalid, 0xFF }, { Key_Invalid, 0xFF }, - { Key_Invalid, 0xFF }, { Key_Invalid, 0xFF }, { Key_Invalid, 0xFF }, { Key_Invalid, 0xFF }, - { Key_Invalid, 0xFF }, { Key_End, 0xFF }, { Key_Invalid, 0xFF }, { Key_Left, 0xFF }, - { Key_Home, 0x47 }, { Key_Invalid, 0xFF }, { Key_Invalid, 0xFF }, { Key_Invalid, 0xFF }, - { Key_Insert, 0xFF }, { Key_Delete, 0xFF }, { Key_Down, 0xFF }, { Key_Invalid, 0xFF }, - { Key_Right, 0xFF }, { Key_Up, 0xFF }, { Key_Invalid, 0xFF }, { Key_Invalid, 0xFF }, - { Key_Invalid, 0xFF }, { Key_Invalid, 0xFF }, { Key_PageDown, 0xFF }, { Key_Invalid, 0xFF }, - { Key_Invalid, 0xFF }, { Key_PageUp, 0x49 }, { Key_Invalid, 0xFF }, { Key_Invalid, 0xFF }, -}; -// clang-format on - -RawKeyEvent PS2KeyboardDevice::generate_raw_key_event_input_from_set1(ScanCodeEvent event) -{ - RawKeyEvent key_event {}; - VERIFY(event.sent_scan_code_set == ScanCodeSet::Set1); - bool has_e0_prefix = event.scan_code_bytes[0] == 0xe0; - if (has_e0_prefix) - VERIFY(event.bytes_count == 2); - else - VERIFY(event.bytes_count == 1); - - u8 byte = has_e0_prefix ? event.scan_code_bytes[1] : event.scan_code_bytes[0]; - bool pressed = !(byte & 0x80); - u8 ch = byte & 0x7f; - - key_event.is_press_down = pressed; - - m_entropy_source.add_random_event(byte); - - switch (ch) { - case 0x38: - if (has_e0_prefix) - m_keyboard_device->update_modifier(Mod_AltGr, key_event.is_press()); - else - m_keyboard_device->update_modifier(Mod_Alt, key_event.is_press()); - break; - case 0x1d: - m_keyboard_device->update_modifier(Mod_Ctrl, key_event.is_press()); - break; - case 0x5b: - m_left_super_pressed = key_event.is_press(); - m_keyboard_device->update_modifier(Mod_Super, m_left_super_pressed || m_right_super_pressed); - break; - case 0x5c: - m_right_super_pressed = key_event.is_press(); - m_keyboard_device->update_modifier(Mod_Super, m_left_super_pressed || m_right_super_pressed); - break; - case 0x2a: - m_left_shift_pressed = key_event.is_press(); - m_keyboard_device->update_modifier(Mod_Shift, m_left_shift_pressed || m_right_shift_pressed); - break; - case 0x36: - m_right_shift_pressed = key_event.is_press(); - m_keyboard_device->update_modifier(Mod_Shift, m_left_shift_pressed || m_right_shift_pressed); - break; - case 0x1c: - case 0x35: - if (has_e0_prefix) - m_keyboard_device->update_modifier(Mod_Keypad, key_event.is_press()); - break; - case 0x37: - case 0x47: - case 0x48: - case 0x49: - case 0x4a: - case 0x4b: - case 0x4c: - case 0x4d: - case 0x4e: - case 0x4f: - case 0x50: - case 0x51: - case 0x52: - case 0x53: - if (!has_e0_prefix) - m_keyboard_device->update_modifier(Mod_Keypad, key_event.is_press()); - break; - } - - key_event.code_entry = (m_keyboard_device->modifiers() & Mod_Shift) ? shifted_scan_code_set1_key_map[ch] : unshifted_scan_code_set1_key_map[ch]; - key_event.scancode = has_e0_prefix ? 0xe000 + ch : ch; - return key_event; -} - -Optional PS2KeyboardDevice::generate_raw_key_event_input_from_set2(ScanCodeEvent event) -{ - VERIFY(event.sent_scan_code_set == ScanCodeSet::Set2); - - auto get_key_from_standard_key_map = [this](u8 byte) -> KeyCodeEntry { - if (!(m_keyboard_device->modifiers() & Mod_Shift)) - return (m_keyboard_device->num_lock_on()) ? unshifted_simple_scan_code_set2_key_map_with_key_num_pad[byte] : unshifted_simple_scan_code_set2_key_map_with_disabled_key_num_pad[byte]; - return (m_keyboard_device->num_lock_on()) ? shifted_simple_scan_code_set2_key_map_with_key_num_pad[byte] : shifted_simple_scan_code_set2_key_map_with_disabled_key_num_pad[byte]; - }; - - RawKeyEvent key_event {}; - if (event.bytes_count == 1) { - auto byte = event.scan_code_bytes[0]; - key_event.code_entry = get_key_from_standard_key_map(byte); - key_event.scancode = byte; - key_event.is_press_down = true; - m_entropy_source.add_random_event(byte); - } else if (event.bytes_count == 2) { - auto byte_prefix = event.scan_code_bytes[0]; - auto byte = event.scan_code_bytes[1]; - if (byte_prefix == 0xe0) { - key_event.code_entry = unshifted_scan_code_set2_e0_key_map[byte]; - key_event.scancode = 0xe000 + byte; - key_event.is_press_down = true; - } else if (byte_prefix == 0xf0) { - key_event.code_entry = get_key_from_standard_key_map(byte); - key_event.scancode = 0xf000 + byte; - } else { - VERIFY_NOT_REACHED(); - } - m_entropy_source.add_random_event(byte); - } else if (event.bytes_count == 3) { - auto first_byte_prefix = event.scan_code_bytes[0]; - auto second_byte_prefix = event.scan_code_bytes[1]; - if (first_byte_prefix != 0xe0 - || second_byte_prefix != 0xf0) { - return Optional {}; - } - - auto byte = event.scan_code_bytes[2]; - key_event.code_entry = unshifted_scan_code_set2_e0_key_map[byte]; - key_event.scancode = 0xe0f000 + byte; - m_entropy_source.add_random_event(byte); - } else if (event.bytes_count == 4) { - // 0xE0, 0x12, 0xE0, 0x7C - print screen pressed - auto first_byte_prefix = event.scan_code_bytes[0]; - auto second_byte_prefix = event.scan_code_bytes[1]; - auto third_byte_prefix = event.scan_code_bytes[2]; - auto fourth_byte_prefix = event.scan_code_bytes[3]; - if (first_byte_prefix != 0xe0 - || second_byte_prefix != 0x12 - || third_byte_prefix != 0xe0 - || fourth_byte_prefix != 0x7c) { - return Optional {}; - } - - key_event.code_entry = KeyCodeEntry { Key_PrintScreen, 0xFF }; - key_event.scancode = 0xe012e07c; - key_event.is_press_down = true; - } else if (event.bytes_count == 6) { - // 0xE0, 0xF0, 0x7C, 0xE0, 0xF0, 0x12 - print screen released - auto first_byte_prefix = event.scan_code_bytes[0]; - auto second_byte_prefix = event.scan_code_bytes[1]; - auto third_byte_prefix = event.scan_code_bytes[2]; - auto fourth_byte_prefix = event.scan_code_bytes[3]; - auto fifth_byte_prefix = event.scan_code_bytes[4]; - auto sixth_byte_prefix = event.scan_code_bytes[5]; - if (first_byte_prefix != 0xe0 - || second_byte_prefix != 0xf0 - || third_byte_prefix != 0x7c - || fourth_byte_prefix != 0xe0 - || fifth_byte_prefix != 0xf0 - || sixth_byte_prefix != 0x12) { - return Optional {}; - } - - key_event.code_entry = KeyCodeEntry { Key_PrintScreen, 0xFF }; - key_event.scancode = 0xe0f07ce0f012; - } else if (event.bytes_count == 8) { - // 0xE1, 0x14, 0x77, 0xE1, 0xF0, 0x14, 0xF0, 0x77 - pause pressed - auto first_byte_prefix = event.scan_code_bytes[0]; - auto second_byte_prefix = event.scan_code_bytes[1]; - auto third_byte_prefix = event.scan_code_bytes[2]; - auto fourth_byte_prefix = event.scan_code_bytes[3]; - auto fifth_byte_prefix = event.scan_code_bytes[4]; - auto sixth_byte_prefix = event.scan_code_bytes[5]; - auto seventh_byte_prefix = event.scan_code_bytes[6]; - auto eight_byte_prefix = event.scan_code_bytes[7]; - if (first_byte_prefix != 0xe1 - || second_byte_prefix != 0x14 - || third_byte_prefix != 0x77 - || fourth_byte_prefix != 0xe1 - || fifth_byte_prefix != 0xf0 - || sixth_byte_prefix != 0x14 - || seventh_byte_prefix != 0xf0 - || eight_byte_prefix != 0x77) { - return Optional {}; - } - - key_event.code_entry = KeyCodeEntry { Key_PauseBreak, 0xFF }; - key_event.scancode = 0xe11477e1f014f077; - } - - switch (key_event.code_entry.key_code) { - case Key_RightAlt: - m_keyboard_device->update_modifier(Mod_AltGr, key_event.is_press()); - break; - case Key_Alt: - m_keyboard_device->update_modifier(Mod_Alt, key_event.is_press()); - break; - case Key_Control: - m_keyboard_device->update_modifier(Mod_Ctrl, key_event.is_press()); - break; - case Key_Super: - m_left_super_pressed = key_event.is_press(); - m_keyboard_device->update_modifier(Mod_Super, m_left_super_pressed || m_right_super_pressed); - break; - case Key_LeftShift: - m_left_shift_pressed = key_event.is_press(); - m_keyboard_device->update_modifier(Mod_Shift, m_left_shift_pressed || m_right_shift_pressed); - break; - case Key_RightShift: - m_right_shift_pressed = key_event.is_press(); - m_keyboard_device->update_modifier(Mod_Shift, m_left_shift_pressed || m_right_shift_pressed); - break; - default: - break; - } - - return key_event; -} - -void PS2KeyboardDevice::handle_scan_code_input_event(ScanCodeEvent event) -{ - RawKeyEvent raw_event {}; - if (event.sent_scan_code_set == ScanCodeSet::Set1) { - raw_event = generate_raw_key_event_input_from_set1(event); - } else if (event.sent_scan_code_set == ScanCodeSet::Set2) { - auto possible_raw_event = generate_raw_key_event_input_from_set2(event); - if (!possible_raw_event.has_value()) { - dmesgln("PS2KeyboardDevice BUG: Invalid scan code (set 2) event, length: {}, bytes: {}", event.bytes_count, event.scan_code_bytes.span().trim(event.bytes_count)); - return; - } - raw_event = possible_raw_event.release_value(); - } else if (event.sent_scan_code_set == ScanCodeSet::Set3) { - // FIXME: Implement support for scan code set 3! - VERIFY_NOT_REACHED(); - } else { - VERIFY_NOT_REACHED(); - } - - KeyEvent queued_event = { - .key = raw_event.code_entry.key_code, - .map_entry_index = raw_event.code_entry.map_entry_index, - .scancode = raw_event.scancode, - .flags = raw_event.is_press() ? (u8)Is_Press : (u8)0, - }; - - // NOTE: This piece of code is needed for the ScanCodeSet::Set1 to ensure some keys could - // function properly. - if (event.sent_scan_code_set == ScanCodeSet::Set1) { - if ((queued_event.scancode & 0xe000) && queued_event.key == Key_Slash) { - // FIXME: Find a way to propagate this when the keyboard is "shifted"! - // If Key_Slash (scancode = 0x35) mapped to other form "/", we fix num pad key of "/" with this case. - queued_event.code_point = '/'; - } else if ((queued_event.scancode & 0xe000) && queued_event.key != Key_Return) { - // Except for `keypad-/` and 'keypad-return', all e0 scan codes are not actually characters. i.e., `keypad-0` and - // `Insert` have the same scancode except for the prefix, but insert should not have a code_point. - queued_event.code_point = 0; - } - } - - // NOTE: This piece of code is needed for the ScanCodeSet::Set1 when NumLock is enabled - // because we don't have special mappings when NumLock is enabled for this scan code set. - // Scan code set 2 handling code in handle_scan_code_input_event_set2() already handles this fine. - if (event.sent_scan_code_set == ScanCodeSet::Set1 && m_keyboard_device->num_lock_on()) { - if (queued_event.scancode >= 0x47 && queued_event.scancode <= 0x53) { - u8 index = queued_event.scancode - 0x47; - constexpr KeyCodeEntry numpad_key_map[13] = { - { Key_7, 8 }, - { Key_8, 9 }, - { Key_9, 10 }, - { Key_Invalid, 0xFF }, - { Key_4, 5 }, - { Key_5, 6 }, - { Key_6, 7 }, - { Key_Invalid, 0xFF }, - { Key_1, 2 }, - { Key_2, 3 }, - { Key_3, 4 }, - { Key_0, 0x0B }, - { Key_Period, 0x34 }, - }; - - if (numpad_key_map[index].key_code != Key_Invalid) { - queued_event.key = numpad_key_map[index].key_code; - queued_event.map_entry_index = numpad_key_map[index].map_entry_index; - } - } - } - - m_keyboard_device->handle_input_event(queued_event); -} - -void PS2KeyboardDevice::handle_byte_read_for_scan_code_set1(u8 byte) -{ - u8 ch = byte & 0x7f; - bool pressed = !(byte & 0x80); - dbgln_if(KEYBOARD_DEBUG, "Keyboard::handle_byte_read_for_scan_code_set1: {:#02x} {}", ch, (pressed ? "down" : "up")); - if (byte == 0xe0) { - m_has_e0_prefix = true; - return; - } - - ScanCodeEvent event {}; - event.sent_scan_code_set = ScanCodeSet::Set1; - if (m_has_e0_prefix) { - event.scan_code_bytes[0] = 0xe0; - event.scan_code_bytes[1] = byte; - event.bytes_count = 2; - } else { - event.scan_code_bytes[0] = byte; - event.bytes_count = 1; - } - m_has_e0_prefix = false; - handle_scan_code_input_event(event); -} - -void PS2KeyboardDevice::handle_byte_read_for_scan_code_set2(u8 byte) -{ - dbgln_if(KEYBOARD_DEBUG, "Keyboard::handle_byte_read_for_scan_code_set2: {:#02x}", byte); - - ScanCodeEvent event {}; - event.sent_scan_code_set = ScanCodeSet::Set2; - if (m_received_bytes_count == 0) { - if (byte == 0xe0 || byte == 0xf0 || byte == 0xe1) { - m_received_bytes[0] = byte; - m_received_bytes_count++; - return; - } - event.scan_code_bytes[0] = byte; - event.bytes_count = 1; - m_received_bytes_count = 0; - handle_scan_code_input_event(event); - return; - } else if (m_received_bytes_count == 1) { - if (byte == 0xf0) { - VERIFY(m_received_bytes[0] == 0xe0); - m_received_bytes[1] = byte; - m_received_bytes_count++; - return; - } - if (m_received_bytes[0] == 0xe0 && byte == 0x12) { - m_received_bytes[1] = byte; - m_received_bytes_count++; - return; - } - - if (m_received_bytes[0] == 0xe1 && byte == 0x14) { - m_received_bytes[1] = byte; - m_received_bytes_count++; - return; - } - - event.scan_code_bytes[0] = m_received_bytes[0]; - event.scan_code_bytes[1] = byte; - event.bytes_count = 2; - m_received_bytes_count = 0; - handle_scan_code_input_event(event); - return; - } else if (m_received_bytes_count == 2) { - if (m_received_bytes[0] == 0xe0 && m_received_bytes[1] == 0x12 && byte == 0xe0) { - m_received_bytes[2] = byte; - m_received_bytes_count++; - return; - } - - if (m_received_bytes[0] == 0xe0 && m_received_bytes[1] == 0xf0 && byte == 0x7c) { - m_received_bytes[2] = byte; - m_received_bytes_count++; - return; - } - - if (m_received_bytes[0] == 0xe1) { - VERIFY(m_received_bytes[1] == 0x14); - m_received_bytes[2] = byte; - m_received_bytes_count++; - return; - } - - event.scan_code_bytes[0] = m_received_bytes[0]; - event.scan_code_bytes[1] = m_received_bytes[1]; - event.scan_code_bytes[2] = byte; - event.bytes_count = 3; - m_received_bytes_count = 0; - handle_scan_code_input_event(event); - return; - } else if (m_received_bytes_count == 3) { - if (m_received_bytes[0] == 0xe0 - && m_received_bytes[1] == 0x12 - && m_received_bytes[2] == 0xe0 - && byte == 0x7c) { - ScanCodeEvent event {}; - event.sent_scan_code_set = ScanCodeSet::Set2; - event.scan_code_bytes[0] = m_received_bytes[0]; - event.scan_code_bytes[1] = m_received_bytes[1]; - event.scan_code_bytes[2] = m_received_bytes[2]; - event.scan_code_bytes[3] = byte; - event.bytes_count = 4; - m_received_bytes_count = 0; - handle_scan_code_input_event(event); - return; - } - - m_received_bytes[3] = byte; - m_received_bytes_count++; - return; - } else if (m_received_bytes_count == 4) { - m_received_bytes[4] = byte; - m_received_bytes_count++; - return; - } else if (m_received_bytes_count == 5) { - if (m_received_bytes[0] == 0xe0 - && m_received_bytes[1] == 0xf0 - && m_received_bytes[2] == 0x7c - && m_received_bytes[3] == 0xe0 - && m_received_bytes[4] == 0xf0 - && byte == 0x12) { - - event.scan_code_bytes[0] = m_received_bytes[0]; - event.scan_code_bytes[1] = m_received_bytes[1]; - event.scan_code_bytes[2] = m_received_bytes[2]; - event.scan_code_bytes[3] = m_received_bytes[3]; - event.scan_code_bytes[4] = m_received_bytes[4]; - event.scan_code_bytes[5] = byte; - event.bytes_count = 6; - m_received_bytes_count = 0; - handle_scan_code_input_event(event); - return; - } - m_received_bytes[5] = byte; - m_received_bytes_count++; - return; - } else if (m_received_bytes_count == 6) { - m_received_bytes[6] = byte; - m_received_bytes_count++; - return; - } else if (m_received_bytes_count == 7) { - event.scan_code_bytes[0] = m_received_bytes[0]; - event.scan_code_bytes[1] = m_received_bytes[1]; - event.scan_code_bytes[2] = m_received_bytes[2]; - event.scan_code_bytes[3] = m_received_bytes[3]; - event.scan_code_bytes[4] = m_received_bytes[4]; - event.scan_code_bytes[5] = m_received_bytes[5]; - event.scan_code_bytes[6] = m_received_bytes[6]; - event.scan_code_bytes[7] = byte; - event.bytes_count = 8; - m_received_bytes_count = 0; - handle_scan_code_input_event(event); - return; - } else { - VERIFY_NOT_REACHED(); - } -} - -void PS2KeyboardDevice::handle_byte_read_from_serial_input(u8 byte) -{ - switch (m_scan_code_set) { - case ScanCodeSet::Set1: - handle_byte_read_for_scan_code_set1(byte); - break; - case ScanCodeSet::Set2: - handle_byte_read_for_scan_code_set2(byte); - break; - default: - VERIFY_NOT_REACHED(); - } -} - -UNMAP_AFTER_INIT ErrorOr> PS2KeyboardDevice::try_to_initialize(SerialIOController const& serial_io_controller, SerialIOController::PortIndex port_index, ScanCodeSet scan_code_set, KeyboardDevice const& keyboard_device) -{ - auto device = TRY(adopt_nonnull_own_or_enomem(new (nothrow) PS2KeyboardDevice(serial_io_controller, port_index, scan_code_set, keyboard_device))); - TRY(device->initialize()); - return device; -} - -UNMAP_AFTER_INIT ErrorOr PS2KeyboardDevice::initialize() -{ - ErrorOr err = attached_controller().reset_device(attached_port_index()); - - if (err.is_error()) { - TRY(attached_controller().send_command(attached_port_index(), SerialIOController::DeviceCommand::GetDeviceID)); - ErrorOr res = attached_controller().read_from_device(attached_port_index()); - if (res.is_error()) { - return err; - } - switch (res.value()) { - // Regular and NCD Sun keyboards. - case 0xab: - case 0xac: - // Trust keyboard, raw and translated - case 0x2b: - case 0x5d: - // NMB SGI keyboard, raw and translated - case 0x60: - case 0x47: - return {}; - } - } - - return err; -} - -// FIXME: UNMAP_AFTER_INIT might not be correct, because in practice PS/2 devices -// are hot pluggable. -UNMAP_AFTER_INIT PS2KeyboardDevice::PS2KeyboardDevice(SerialIOController const& serial_io_controller, SerialIOController::PortIndex port_index, ScanCodeSet scan_code_set, KeyboardDevice const& keyboard_device) - : SerialIODevice(serial_io_controller, port_index) - , m_keyboard_device(keyboard_device) - , m_scan_code_set(scan_code_set) -{ -} - -// FIXME: UNMAP_AFTER_INIT might not be correct, because in practice PS/2 devices -// are hot pluggable. -UNMAP_AFTER_INIT PS2KeyboardDevice::~PS2KeyboardDevice() = default; - -} diff --git a/Kernel/Devices/HID/PS2/KeyboardDevice.h b/Kernel/Devices/HID/PS2/KeyboardDevice.h deleted file mode 100644 index 2ab126a1298..00000000000 --- a/Kernel/Devices/HID/PS2/KeyboardDevice.h +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * Copyright (c) 2023, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include -#include - -namespace Kernel { - -class PS2KeyboardDevice final : public SerialIODevice { - friend class DeviceManagement; - -public: - static ErrorOr> try_to_initialize(SerialIOController const&, SerialIOController::PortIndex port_index, ScanCodeSet scan_code_set, KeyboardDevice const&); - virtual ~PS2KeyboardDevice() override; - ErrorOr initialize(); - - // ^SerialIODevice - virtual void handle_byte_read_from_serial_input(u8 byte) override; - -private: - PS2KeyboardDevice(SerialIOController const&, SerialIOController::PortIndex port_index, ScanCodeSet scan_code_set, KeyboardDevice const&); - - RawKeyEvent generate_raw_key_event_input_from_set1(ScanCodeEvent); - Optional generate_raw_key_event_input_from_set2(ScanCodeEvent); - - void handle_scan_code_input_event(ScanCodeEvent event); - - void handle_byte_read_for_scan_code_set1(u8 byte); - void handle_byte_read_for_scan_code_set2(u8 byte); - - // NOTE: This boolean variable is only used with ScanCodeSet::Set1 - // because it only has one prefix defined in the scan code set. - bool m_has_e0_prefix { false }; - - // NOTE: This array and its counter are used only when m_scan_code_set - // is set to ScanCodeSet::Set2, because that scan code requires us to - // manage scan codes with multiple bytes. - // According to the scan code set 2 table, a key press (or release) - // can generate up to 8 bytes. - Array m_received_bytes; - size_t m_received_bytes_count { 0 }; - - bool m_left_shift_pressed { false }; - bool m_right_shift_pressed { false }; - bool m_left_super_pressed { false }; - bool m_right_super_pressed { false }; - - NonnullRefPtr const m_keyboard_device; - ScanCodeSet const m_scan_code_set { ScanCodeSet::Set1 }; - - EntropySource m_entropy_source; -}; - -} diff --git a/Kernel/Devices/HID/PS2/MouseDevice.cpp b/Kernel/Devices/HID/PS2/MouseDevice.cpp deleted file mode 100644 index fd5c8376422..00000000000 --- a/Kernel/Devices/HID/PS2/MouseDevice.cpp +++ /dev/null @@ -1,197 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * Copyright (c) 2023, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include - -namespace Kernel { - -#define PS2MOUSE_INTELLIMOUSE_ID 0x03 -#define PS2MOUSE_INTELLIMOUSE_EXPLORER_ID 0x04 - -UNMAP_AFTER_INIT PS2MouseDevice::PS2MouseDevice(SerialIOController const& serial_io_controller, SerialIOController::PortIndex port_index, MouseDevice const& mouse_device) - : SerialIODevice(serial_io_controller, port_index) - , m_mouse_device(mouse_device) -{ -} - -UNMAP_AFTER_INIT PS2MouseDevice::~PS2MouseDevice() = default; - -void PS2MouseDevice::handle_byte_read_from_serial_input(u8 byte) -{ - auto commit_packet = [this]() { - m_data_state = 0; - dbgln_if(PS2MOUSE_DEBUG, "PS2Mouse: {}, {} {} {}", - m_data.bytes[1], - m_data.bytes[2], - (m_data.bytes[0] & 1) ? "Left" : "", - (m_data.bytes[0] & 2) ? "Right" : ""); - m_mouse_device->handle_mouse_packet_input_event(parse_data_packet(m_data)); - }; - - VERIFY(m_data_state < sizeof(m_data.bytes) / sizeof(m_data.bytes[0])); - m_data.bytes[m_data_state] = byte; - - switch (m_data_state) { - case 0: - if (!(byte & 0x08)) { - dbgln("PS2Mouse: Stream out of sync."); - return; - } - ++m_data_state; - return; - case 1: - ++m_data_state; - return; - case 2: - if (m_has_wheel) { - ++m_data_state; - return; - } - commit_packet(); - return; - case 3: - VERIFY(m_has_wheel); - commit_packet(); - return; - } - VERIFY_NOT_REACHED(); -} - -MousePacket PS2MouseDevice::parse_data_packet(RawPacket const& raw_packet) -{ - int x = raw_packet.bytes[1]; - int y = raw_packet.bytes[2]; - int z = 0; - int w = 0; - if (m_has_wheel) { - // FIXME: For non-Intellimouse, this is a full byte. - // However, for now, m_has_wheel is only set for Intellimouse. - z = (char)(raw_packet.bytes[3] & 0x0f); - - // -1 in 4 bits - if (z == 15) - z = -1; - - if ((raw_packet.bytes[3] & 0xc0) == 0x40) { - // FIXME: Scroll only functions correctly when the sign is flipped there - w = -z; - z = 0; - } else { - w = 0; - } - } - bool x_overflow = raw_packet.bytes[0] & 0x40; - bool y_overflow = raw_packet.bytes[0] & 0x80; - bool x_sign = raw_packet.bytes[0] & 0x10; - bool y_sign = raw_packet.bytes[0] & 0x20; - if (x && x_sign) - x -= 0x100; - if (y && y_sign) - y -= 0x100; - if (x_overflow || y_overflow) { - x = 0; - y = 0; - } - MousePacket packet; - packet.x = x; - packet.y = y; - packet.z = z; - packet.w = w; - packet.buttons = raw_packet.bytes[0] & 0x07; - - if (m_has_five_buttons) { - if (raw_packet.bytes[3] & 0x10) - packet.buttons |= MousePacket::BackwardButton; - if (raw_packet.bytes[3] & 0x20) - packet.buttons |= MousePacket::ForwardButton; - } - - packet.is_relative = true; - dbgln_if(PS2MOUSE_DEBUG, "PS2 Relative Mouse: Buttons {:x}", packet.buttons); - dbgln_if(PS2MOUSE_DEBUG, "Mouse: X {}, Y {}, Z {}, W {}", packet.x, packet.y, packet.z, packet.w); - return packet; -} - -ErrorOr PS2MouseDevice::get_device_id() -{ - TRY(send_command(SerialIOController::DeviceCommand::GetDeviceID)); - return read_from_device(); -} - -ErrorOr PS2MouseDevice::read_from_device() -{ - return attached_controller().read_from_device(attached_port_index()); -} - -ErrorOr PS2MouseDevice::send_command(SerialIOController::DeviceCommand command) -{ - TRY(attached_controller().send_command(attached_port_index(), command)); - return {}; -} - -ErrorOr PS2MouseDevice::send_command(SerialIOController::DeviceCommand command, u8 data) -{ - TRY(attached_controller().send_command(attached_port_index(), command, data)); - return {}; -} - -ErrorOr PS2MouseDevice::set_sample_rate(u8 rate) -{ - TRY(send_command(SerialIOController::DeviceCommand::SetSampleRate, rate)); - return {}; -} - -UNMAP_AFTER_INIT ErrorOr> PS2MouseDevice::try_to_initialize(SerialIOController const& serial_io_controller, SerialIOController::PortIndex port_index, MouseDevice const& mouse_device) -{ - auto device = TRY(adopt_nonnull_own_or_enomem(new (nothrow) PS2MouseDevice(serial_io_controller, port_index, mouse_device))); - TRY(device->initialize()); - return device; -} - -UNMAP_AFTER_INIT ErrorOr PS2MouseDevice::initialize() -{ - TRY(attached_controller().reset_device(attached_port_index())); - - u8 device_id = TRY(read_from_device()); - - TRY(send_command(SerialIOController::DeviceCommand::SetDefaults)); - TRY(send_command(SerialIOController::DeviceCommand::EnablePacketStreaming)); - - if (device_id != PS2MOUSE_INTELLIMOUSE_ID) { - // Send magical wheel initiation sequence. - TRY(set_sample_rate(200)); - TRY(set_sample_rate(100)); - TRY(set_sample_rate(80)); - device_id = TRY(get_device_id()); - } - if (device_id == PS2MOUSE_INTELLIMOUSE_ID) { - m_has_wheel = true; - dmesgln("PS2MouseDevice: Mouse wheel enabled!"); - } else { - dmesgln("PS2MouseDevice: No mouse wheel detected!"); - } - - if (device_id == PS2MOUSE_INTELLIMOUSE_ID) { - // Try to enable 5 buttons as well! - TRY(set_sample_rate(200)); - TRY(set_sample_rate(200)); - TRY(set_sample_rate(80)); - device_id = TRY(get_device_id()); - } - - if (device_id == PS2MOUSE_INTELLIMOUSE_EXPLORER_ID) { - m_has_five_buttons = true; - dmesgln("PS2MouseDevice: 5 buttons enabled!"); - } - return {}; -} - -} diff --git a/Kernel/Devices/HID/PS2/MouseDevice.h b/Kernel/Devices/HID/PS2/MouseDevice.h deleted file mode 100644 index 53116676580..00000000000 --- a/Kernel/Devices/HID/PS2/MouseDevice.h +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * Copyright (c) 2023, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include - -namespace Kernel { -class PS2MouseDevice : public SerialIODevice { - friend class DeviceManagement; - -public: - static ErrorOr> try_to_initialize(SerialIOController const&, SerialIOController::PortIndex, MouseDevice const&); - ErrorOr initialize(); - - virtual ~PS2MouseDevice() override; - - // ^SerialIODevice - virtual void handle_byte_read_from_serial_input(u8 byte) override; - -protected: - PS2MouseDevice(SerialIOController const&, SerialIOController::PortIndex, MouseDevice const&); - - struct RawPacket { - union { - u32 dword; - u8 bytes[4]; - }; - }; - - ErrorOr read_from_device(); - ErrorOr send_command(SerialIOController::DeviceCommand command); - ErrorOr send_command(SerialIOController::DeviceCommand command, u8 data); - MousePacket parse_data_packet(RawPacket const&); - ErrorOr set_sample_rate(u8); - ErrorOr get_device_id(); - - u8 m_data_state { 0 }; - RawPacket m_data; - bool m_has_wheel { false }; - bool m_has_five_buttons { false }; - - NonnullRefPtr const m_mouse_device; -}; - -} diff --git a/Kernel/Devices/HID/ScanCodeEvent.h b/Kernel/Devices/HID/ScanCodeEvent.h deleted file mode 100644 index 3a1d8a44c98..00000000000 --- a/Kernel/Devices/HID/ScanCodeEvent.h +++ /dev/null @@ -1,20 +0,0 @@ -/* - * Copyright (c) 2023, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include - -namespace Kernel { - -struct ScanCodeEvent { - Array scan_code_bytes; - ScanCodeSet sent_scan_code_set { ScanCodeSet::Set1 }; - u8 bytes_count { 0 }; -}; - -} diff --git a/Kernel/Devices/HID/USB/MouseDevice.cpp b/Kernel/Devices/HID/USB/MouseDevice.cpp deleted file mode 100644 index e7f93490ba9..00000000000 --- a/Kernel/Devices/HID/USB/MouseDevice.cpp +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright (c) 2022, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace Kernel { - -ErrorOr> USBMouseDevice::try_create_instance(USB::Device const& usb_device, size_t max_packet_size, NonnullOwnPtr pipe) -{ - if (max_packet_size < 4) - return Error::from_errno(ENOTSUP); - auto device = TRY(DeviceManagement::try_create_device(usb_device, move(pipe))); - TRY(device->create_and_start_polling_process(max_packet_size)); - return *device; -} - -ErrorOr USBMouseDevice::create_and_start_polling_process(size_t max_packet_size) -{ - VERIFY(max_packet_size >= 4); - [[maybe_unused]] auto interrupt_in_transfer = TRY(m_interrupt_in_pipe->submit_interrupt_in_transfer(max_packet_size, 10, [this](auto* transfer) { - USB::HID::MouseBootProtocolPacket packet_raw; - memcpy(&packet_raw, transfer->buffer().as_ptr(), 4); - MousePacket packet; - packet.buttons = packet_raw.buttons & 0x07; - packet.x = packet_raw.x; - packet.y = -packet_raw.y; - packet.z = -packet_raw.z; - packet.w = 0; - packet.is_relative = true; - - handle_mouse_packet_input_event(packet); - })); - return {}; -} - -USBMouseDevice::USBMouseDevice(USB::Device const& usb_device, NonnullOwnPtr pipe) - : MouseDevice() - , m_interrupt_in_pipe(move(pipe)) - , m_attached_usb_device(usb_device) -{ -} - -} diff --git a/Kernel/Devices/HID/USB/MouseDevice.h b/Kernel/Devices/HID/USB/MouseDevice.h deleted file mode 100644 index dad964f6772..00000000000 --- a/Kernel/Devices/HID/USB/MouseDevice.h +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright (c) 2022-2023, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace Kernel { - -class USBMouseDevice final : public MouseDevice { - friend class DeviceManagement; - -public: - static ErrorOr> try_create_instance(USB::Device const&, size_t max_packet_size, NonnullOwnPtr pipe); - virtual ~USBMouseDevice() override {}; - - USB::Device const& device() const { return *m_attached_usb_device; } - -private: - ErrorOr create_and_start_polling_process(size_t max_packet_size); - - USBMouseDevice(USB::Device const&, NonnullOwnPtr pipe); - NonnullOwnPtr m_interrupt_in_pipe; - NonnullRefPtr m_attached_usb_device; - - IntrusiveListNode> m_list_node; - -public: - using List = IntrusiveList<&USBMouseDevice::m_list_node>; -}; - -} diff --git a/Kernel/Devices/HID/VirtIO/EvDevDefinitions.h b/Kernel/Devices/HID/VirtIO/EvDevDefinitions.h deleted file mode 100644 index 59eb8e104c8..00000000000 --- a/Kernel/Devices/HID/VirtIO/EvDevDefinitions.h +++ /dev/null @@ -1,988 +0,0 @@ -/*- - * Copyright (c) 2016 Oleksandr Tymoshenko - * Copyright (c) 2015-2016 Vladimir Kondratyev - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#ifndef _EVDEV_INPUT_EVENT_CODES_H -#define _EVDEV_INPUT_EVENT_CODES_H - -/* - * Device properties and quirks - */ - -#define INPUT_PROP_POINTER 0x00 /* needs a pointer */ -#define INPUT_PROP_DIRECT 0x01 /* direct input devices */ -#define INPUT_PROP_BUTTONPAD 0x02 /* has button(s) under pad */ -#define INPUT_PROP_SEMI_MT 0x03 /* touch rectangle only */ -#define INPUT_PROP_TOPBUTTONPAD 0x04 /* softbuttons at top of pad */ -#define INPUT_PROP_POINTING_STICK 0x05 /* is a pointing stick */ -#define INPUT_PROP_ACCELEROMETER 0x06 /* has accelerometer */ - -#define INPUT_PROP_MAX 0x1f -#define INPUT_PROP_CNT (INPUT_PROP_MAX + 1) - -/* - * Event types - */ - -#define EV_SYN 0x00 -#define EV_KEY 0x01 -#define EV_REL 0x02 -#define EV_ABS 0x03 -#define EV_MSC 0x04 -#define EV_SW 0x05 -#define EV_LED 0x11 -#define EV_SND 0x12 -#define EV_REP 0x14 -#define EV_FF 0x15 -#define EV_PWR 0x16 -#define EV_FF_STATUS 0x17 -#define EV_MAX 0x1f -#define EV_CNT (EV_MAX+1) - -/* - * Synchronization events. - */ - -#define SYN_REPORT 0 -#define SYN_CONFIG 1 -#define SYN_MT_REPORT 2 -#define SYN_DROPPED 3 -#define SYN_MAX 0xf -#define SYN_CNT (SYN_MAX+1) - -/* - * Keys and buttons - * - * Most of the keys/buttons are modeled after USB HUT 1.12 - * (see http://www.usb.org/developers/hidpage). - * Abbreviations in the comments: - * AC - Application Control - * AL - Application Launch Button - * SC - System Control - */ - -#define KEY_RESERVED 0 -#define KEY_ESC 1 -#define KEY_1 2 -#define KEY_2 3 -#define KEY_3 4 -#define KEY_4 5 -#define KEY_5 6 -#define KEY_6 7 -#define KEY_7 8 -#define KEY_8 9 -#define KEY_9 10 -#define KEY_0 11 -#define KEY_MINUS 12 -#define KEY_EQUAL 13 -#define KEY_BACKSPACE 14 -#define KEY_TAB 15 -#define KEY_Q 16 -#define KEY_W 17 -#define KEY_E 18 -#define KEY_R 19 -#define KEY_T 20 -#define KEY_Y 21 -#define KEY_U 22 -#define KEY_I 23 -#define KEY_O 24 -#define KEY_P 25 -#define KEY_LEFTBRACE 26 -#define KEY_RIGHTBRACE 27 -#define KEY_ENTER 28 -#define KEY_LEFTCTRL 29 -#define KEY_A 30 -#define KEY_S 31 -#define KEY_D 32 -#define KEY_F 33 -#define KEY_G 34 -#define KEY_H 35 -#define KEY_J 36 -#define KEY_K 37 -#define KEY_L 38 -#define KEY_SEMICOLON 39 -#define KEY_APOSTROPHE 40 -#define KEY_GRAVE 41 -#define KEY_LEFTSHIFT 42 -#define KEY_BACKSLASH 43 -#define KEY_Z 44 -#define KEY_X 45 -#define KEY_C 46 -#define KEY_V 47 -#define KEY_B 48 -#define KEY_N 49 -#define KEY_M 50 -#define KEY_COMMA 51 -#define KEY_DOT 52 -#define KEY_SLASH 53 -#define KEY_RIGHTSHIFT 54 -#define KEY_KPASTERISK 55 -#define KEY_LEFTALT 56 -#define KEY_SPACE 57 -#define KEY_CAPSLOCK 58 -#define KEY_F1 59 -#define KEY_F2 60 -#define KEY_F3 61 -#define KEY_F4 62 -#define KEY_F5 63 -#define KEY_F6 64 -#define KEY_F7 65 -#define KEY_F8 66 -#define KEY_F9 67 -#define KEY_F10 68 -#define KEY_NUMLOCK 69 -#define KEY_SCROLLLOCK 70 -#define KEY_KP7 71 -#define KEY_KP8 72 -#define KEY_KP9 73 -#define KEY_KPMINUS 74 -#define KEY_KP4 75 -#define KEY_KP5 76 -#define KEY_KP6 77 -#define KEY_KPPLUS 78 -#define KEY_KP1 79 -#define KEY_KP2 80 -#define KEY_KP3 81 -#define KEY_KP0 82 -#define KEY_KPDOT 83 - -#define KEY_ZENKAKUHANKAKU 85 -#define KEY_102ND 86 -#define KEY_F11 87 -#define KEY_F12 88 -#define KEY_RO 89 -#define KEY_KATAKANA 90 -#define KEY_HIRAGANA 91 -#define KEY_HENKAN 92 -#define KEY_KATAKANAHIRAGANA 93 -#define KEY_MUHENKAN 94 -#define KEY_KPJPCOMMA 95 -#define KEY_KPENTER 96 -#define KEY_RIGHTCTRL 97 -#define KEY_KPSLASH 98 -#define KEY_SYSRQ 99 -#define KEY_RIGHTALT 100 -#define KEY_LINEFEED 101 -#define KEY_HOME 102 -#define KEY_UP 103 -#define KEY_PAGEUP 104 -#define KEY_LEFT 105 -#define KEY_RIGHT 106 -#define KEY_END 107 -#define KEY_DOWN 108 -#define KEY_PAGEDOWN 109 -#define KEY_INSERT 110 -#define KEY_DELETE 111 -#define KEY_MACRO 112 -#define KEY_MUTE 113 -#define KEY_VOLUMEDOWN 114 -#define KEY_VOLUMEUP 115 -#define KEY_POWER 116 /* SC System Power Down */ -#define KEY_KPEQUAL 117 -#define KEY_KPPLUSMINUS 118 -#define KEY_PAUSE 119 -#define KEY_SCALE 120 /* AL Compiz Scale (Expose) */ - -#define KEY_KPCOMMA 121 -#define KEY_HANGEUL 122 -#define KEY_HANGUEL KEY_HANGEUL -#define KEY_HANJA 123 -#define KEY_YEN 124 -#define KEY_LEFTMETA 125 -#define KEY_RIGHTMETA 126 -#define KEY_COMPOSE 127 - -#define KEY_STOP 128 /* AC Stop */ -#define KEY_AGAIN 129 -#define KEY_PROPS 130 /* AC Properties */ -#define KEY_UNDO 131 /* AC Undo */ -#define KEY_FRONT 132 -#define KEY_COPY 133 /* AC Copy */ -#define KEY_OPEN 134 /* AC Open */ -#define KEY_PASTE 135 /* AC Paste */ -#define KEY_FIND 136 /* AC Search */ -#define KEY_CUT 137 /* AC Cut */ -#define KEY_HELP 138 /* AL Integrated Help Center */ -#define KEY_MENU 139 /* Menu (show menu) */ -#define KEY_CALC 140 /* AL Calculator */ -#define KEY_SETUP 141 -#define KEY_SLEEP 142 /* SC System Sleep */ -#define KEY_WAKEUP 143 /* System Wake Up */ -#define KEY_FILE 144 /* AL Local Machine Browser */ -#define KEY_SENDFILE 145 -#define KEY_DELETEFILE 146 -#define KEY_XFER 147 -#define KEY_PROG1 148 -#define KEY_PROG2 149 -#define KEY_WWW 150 /* AL Internet Browser */ -#define KEY_MSDOS 151 -#define KEY_COFFEE 152 /* AL Terminal Lock/Screensaver */ -#define KEY_SCREENLOCK KEY_COFFEE -#define KEY_ROTATE_DISPLAY 153 /* Display orientation for e.g. tablets */ -#define KEY_DIRECTION KEY_ROTATE_DISPLAY -#define KEY_CYCLEWINDOWS 154 -#define KEY_MAIL 155 -#define KEY_BOOKMARKS 156 /* AC Bookmarks */ -#define KEY_COMPUTER 157 -#define KEY_BACK 158 /* AC Back */ -#define KEY_FORWARD 159 /* AC Forward */ -#define KEY_CLOSECD 160 -#define KEY_EJECTCD 161 -#define KEY_EJECTCLOSECD 162 -#define KEY_NEXTSONG 163 -#define KEY_PLAYPAUSE 164 -#define KEY_PREVIOUSSONG 165 -#define KEY_STOPCD 166 -#define KEY_RECORD 167 -#define KEY_REWIND 168 -#define KEY_PHONE 169 /* Media Select Telephone */ -#define KEY_ISO 170 -#define KEY_CONFIG 171 /* AL Consumer Control Configuration */ -#define KEY_HOMEPAGE 172 /* AC Home */ -#define KEY_REFRESH 173 /* AC Refresh */ -#define KEY_EXIT 174 /* AC Exit */ -#define KEY_MOVE 175 -#define KEY_EDIT 176 -#define KEY_SCROLLUP 177 -#define KEY_SCROLLDOWN 178 -#define KEY_KPLEFTPAREN 179 -#define KEY_KPRIGHTPAREN 180 -#define KEY_NEW 181 /* AC New */ -#define KEY_REDO 182 /* AC Redo/Repeat */ - -#define KEY_F13 183 -#define KEY_F14 184 -#define KEY_F15 185 -#define KEY_F16 186 -#define KEY_F17 187 -#define KEY_F18 188 -#define KEY_F19 189 -#define KEY_F20 190 -#define KEY_F21 191 -#define KEY_F22 192 -#define KEY_F23 193 -#define KEY_F24 194 - -#define KEY_PLAYCD 200 -#define KEY_PAUSECD 201 -#define KEY_PROG3 202 -#define KEY_PROG4 203 -#define KEY_ALL_APPLICATIONS 204 /* AC Desktop Show All Applications */ -#define KEY_DASHBOARD KEY_ALL_APPLICATIONS -#define KEY_SUSPEND 205 -#define KEY_CLOSE 206 /* AC Close */ -#define KEY_PLAY 207 -#define KEY_FASTFORWARD 208 -#define KEY_BASSBOOST 209 -#define KEY_PRINT 210 /* AC Print */ -#define KEY_HP 211 -#define KEY_CAMERA 212 -#define KEY_SOUND 213 -#define KEY_QUESTION 214 -#define KEY_EMAIL 215 -#define KEY_CHAT 216 -#define KEY_SEARCH 217 -#define KEY_CONNECT 218 -#define KEY_FINANCE 219 /* AL Checkbook/Finance */ -#define KEY_SPORT 220 -#define KEY_SHOP 221 -#define KEY_ALTERASE 222 -#define KEY_CANCEL 223 /* AC Cancel */ -#define KEY_BRIGHTNESSDOWN 224 -#define KEY_BRIGHTNESSUP 225 -#define KEY_MEDIA 226 - -#define KEY_SWITCHVIDEOMODE 227 /* Cycle between available video - outputs (Monitor/LCD/TV-out/etc) */ -#define KEY_KBDILLUMTOGGLE 228 -#define KEY_KBDILLUMDOWN 229 -#define KEY_KBDILLUMUP 230 - -#define KEY_SEND 231 /* AC Send */ -#define KEY_REPLY 232 /* AC Reply */ -#define KEY_FORWARDMAIL 233 /* AC Forward Msg */ -#define KEY_SAVE 234 /* AC Save */ -#define KEY_DOCUMENTS 235 - -#define KEY_BATTERY 236 - -#define KEY_BLUETOOTH 237 -#define KEY_WLAN 238 -#define KEY_UWB 239 - -#define KEY_UNKNOWN 240 - -#define KEY_VIDEO_NEXT 241 /* drive next video source */ -#define KEY_VIDEO_PREV 242 /* drive previous video source */ -#define KEY_BRIGHTNESS_CYCLE 243 /* brightness up, after max is min */ -#define KEY_BRIGHTNESS_AUTO 244 /* Set Auto Brightness: manual - brightness control is off, - rely on ambient */ -#define KEY_BRIGHTNESS_ZERO KEY_BRIGHTNESS_AUTO -#define KEY_DISPLAY_OFF 245 /* display device to off state */ - -#define KEY_WWAN 246 /* Wireless WAN (LTE, UMTS, GSM, etc.) */ -#define KEY_WIMAX KEY_WWAN -#define KEY_RFKILL 247 /* Key that controls all radios */ - -#define KEY_MICMUTE 248 /* Mute / unmute the microphone */ - -/* Code 255 is reserved for special needs of AT keyboard driver */ - -#define BTN_MISC 0x100 -#define BTN_0 0x100 -#define BTN_1 0x101 -#define BTN_2 0x102 -#define BTN_3 0x103 -#define BTN_4 0x104 -#define BTN_5 0x105 -#define BTN_6 0x106 -#define BTN_7 0x107 -#define BTN_8 0x108 -#define BTN_9 0x109 - -#define BTN_MOUSE 0x110 -#define BTN_LEFT 0x110 -#define BTN_RIGHT 0x111 -#define BTN_MIDDLE 0x112 -#define BTN_SIDE 0x113 -#define BTN_EXTRA 0x114 -#define BTN_FORWARD 0x115 -#define BTN_BACK 0x116 -#define BTN_TASK 0x117 - -#define BTN_JOYSTICK 0x120 -#define BTN_TRIGGER 0x120 -#define BTN_THUMB 0x121 -#define BTN_THUMB2 0x122 -#define BTN_TOP 0x123 -#define BTN_TOP2 0x124 -#define BTN_PINKIE 0x125 -#define BTN_BASE 0x126 -#define BTN_BASE2 0x127 -#define BTN_BASE3 0x128 -#define BTN_BASE4 0x129 -#define BTN_BASE5 0x12a -#define BTN_BASE6 0x12b -#define BTN_DEAD 0x12f - -#define BTN_GAMEPAD 0x130 -#define BTN_SOUTH 0x130 -#define BTN_A BTN_SOUTH -#define BTN_EAST 0x131 -#define BTN_B BTN_EAST -#define BTN_C 0x132 -#define BTN_NORTH 0x133 -#define BTN_X BTN_NORTH -#define BTN_WEST 0x134 -#define BTN_Y BTN_WEST -#define BTN_Z 0x135 -#define BTN_TL 0x136 -#define BTN_TR 0x137 -#define BTN_TL2 0x138 -#define BTN_TR2 0x139 -#define BTN_SELECT 0x13a -#define BTN_START 0x13b -#define BTN_MODE 0x13c -#define BTN_THUMBL 0x13d -#define BTN_THUMBR 0x13e - -#define BTN_DIGI 0x140 -#define BTN_TOOL_PEN 0x140 -#define BTN_TOOL_RUBBER 0x141 -#define BTN_TOOL_BRUSH 0x142 -#define BTN_TOOL_PENCIL 0x143 -#define BTN_TOOL_AIRBRUSH 0x144 -#define BTN_TOOL_FINGER 0x145 -#define BTN_TOOL_MOUSE 0x146 -#define BTN_TOOL_LENS 0x147 -#define BTN_TOOL_QUINTTAP 0x148 /* Five fingers on trackpad */ -#define BTN_STYLUS3 0x149 -#define BTN_TOUCH 0x14a -#define BTN_STYLUS 0x14b -#define BTN_STYLUS2 0x14c -#define BTN_TOOL_DOUBLETAP 0x14d -#define BTN_TOOL_TRIPLETAP 0x14e -#define BTN_TOOL_QUADTAP 0x14f /* Four fingers on trackpad */ - -#define BTN_WHEEL 0x150 -#define BTN_GEAR_DOWN 0x150 -#define BTN_GEAR_UP 0x151 - -#define KEY_OK 0x160 -#define KEY_SELECT 0x161 -#define KEY_GOTO 0x162 -#define KEY_CLEAR 0x163 -#define KEY_POWER2 0x164 -#define KEY_OPTION 0x165 -#define KEY_INFO 0x166 /* AL OEM Features/Tips/Tutorial */ -#define KEY_TIME 0x167 -#define KEY_VENDOR 0x168 -#define KEY_ARCHIVE 0x169 -#define KEY_PROGRAM 0x16a /* Media Select Program Guide */ -#define KEY_CHANNEL 0x16b -#define KEY_FAVORITES 0x16c -#define KEY_EPG 0x16d -#define KEY_PVR 0x16e /* Media Select Home */ -#define KEY_MHP 0x16f -#define KEY_LANGUAGE 0x170 -#define KEY_TITLE 0x171 -#define KEY_SUBTITLE 0x172 -#define KEY_ANGLE 0x173 -#define KEY_FULL_SCREEN 0x174 /* AC View Toggle */ -#define KEY_ZOOM KEY_FULL_SCREEN -#define KEY_MODE 0x175 -#define KEY_KEYBOARD 0x176 -#define KEY_ASPECT_RATIO 0x177 /* HUTRR37: Aspect */ -#define KEY_SCREEN KEY_ASPECT_RATIO -#define KEY_PC 0x178 /* Media Select Computer */ -#define KEY_TV 0x179 /* Media Select TV */ -#define KEY_TV2 0x17a /* Media Select Cable */ -#define KEY_VCR 0x17b /* Media Select VCR */ -#define KEY_VCR2 0x17c /* VCR Plus */ -#define KEY_SAT 0x17d /* Media Select Satellite */ -#define KEY_SAT2 0x17e -#define KEY_CD 0x17f /* Media Select CD */ -#define KEY_TAPE 0x180 /* Media Select Tape */ -#define KEY_RADIO 0x181 -#define KEY_TUNER 0x182 /* Media Select Tuner */ -#define KEY_PLAYER 0x183 -#define KEY_TEXT 0x184 -#define KEY_DVD 0x185 /* Media Select DVD */ -#define KEY_AUX 0x186 -#define KEY_MP3 0x187 -#define KEY_AUDIO 0x188 /* AL Audio Browser */ -#define KEY_VIDEO 0x189 /* AL Movie Browser */ -#define KEY_DIRECTORY 0x18a -#define KEY_LIST 0x18b -#define KEY_MEMO 0x18c /* Media Select Messages */ -#define KEY_CALENDAR 0x18d -#define KEY_RED 0x18e -#define KEY_GREEN 0x18f -#define KEY_YELLOW 0x190 -#define KEY_BLUE 0x191 -#define KEY_CHANNELUP 0x192 /* Channel Increment */ -#define KEY_CHANNELDOWN 0x193 /* Channel Decrement */ -#define KEY_FIRST 0x194 -#define KEY_LAST 0x195 /* Recall Last */ -#define KEY_AB 0x196 -#define KEY_NEXT 0x197 -#define KEY_RESTART 0x198 -#define KEY_SLOW 0x199 -#define KEY_SHUFFLE 0x19a -#define KEY_BREAK 0x19b -#define KEY_PREVIOUS 0x19c -#define KEY_DIGITS 0x19d -#define KEY_TEEN 0x19e -#define KEY_TWEN 0x19f -#define KEY_VIDEOPHONE 0x1a0 /* Media Select Video Phone */ -#define KEY_GAMES 0x1a1 /* Media Select Games */ -#define KEY_ZOOMIN 0x1a2 /* AC Zoom In */ -#define KEY_ZOOMOUT 0x1a3 /* AC Zoom Out */ -#define KEY_ZOOMRESET 0x1a4 /* AC Zoom */ -#define KEY_WORDPROCESSOR 0x1a5 /* AL Word Processor */ -#define KEY_EDITOR 0x1a6 /* AL Text Editor */ -#define KEY_SPREADSHEET 0x1a7 /* AL Spreadsheet */ -#define KEY_GRAPHICSEDITOR 0x1a8 /* AL Graphics Editor */ -#define KEY_PRESENTATION 0x1a9 /* AL Presentation App */ -#define KEY_DATABASE 0x1aa /* AL Database App */ -#define KEY_NEWS 0x1ab /* AL Newsreader */ -#define KEY_VOICEMAIL 0x1ac /* AL Voicemail */ -#define KEY_ADDRESSBOOK 0x1ad /* AL Contacts/Address Book */ -#define KEY_MESSENGER 0x1ae /* AL Instant Messaging */ -#define KEY_DISPLAYTOGGLE 0x1af /* Turn display (LCD) on and off */ -#define KEY_BRIGHTNESS_TOGGLE KEY_DISPLAYTOGGLE -#define KEY_SPELLCHECK 0x1b0 /* AL Spell Check */ -#define KEY_LOGOFF 0x1b1 /* AL Logoff */ - -#define KEY_DOLLAR 0x1b2 -#define KEY_EURO 0x1b3 - -#define KEY_FRAMEBACK 0x1b4 /* Consumer - transport controls */ -#define KEY_FRAMEFORWARD 0x1b5 -#define KEY_CONTEXT_MENU 0x1b6 /* GenDesc - system context menu */ -#define KEY_MEDIA_REPEAT 0x1b7 /* Consumer - transport control */ -#define KEY_10CHANNELSUP 0x1b8 /* 10 channels up (10+) */ -#define KEY_10CHANNELSDOWN 0x1b9 /* 10 channels down (10-) */ -#define KEY_IMAGES 0x1ba /* AL Image Browser */ -#define KEY_NOTIFICATION_CENTER 0x1bc /* Show/hide the notification center */ -#define KEY_PICKUP_PHONE 0x1bd /* Answer incoming call */ -#define KEY_HANGUP_PHONE 0x1be /* Decline incoming call */ - -#define KEY_DEL_EOL 0x1c0 -#define KEY_DEL_EOS 0x1c1 -#define KEY_INS_LINE 0x1c2 -#define KEY_DEL_LINE 0x1c3 - -#define KEY_FN 0x1d0 -#define KEY_FN_ESC 0x1d1 -#define KEY_FN_F1 0x1d2 -#define KEY_FN_F2 0x1d3 -#define KEY_FN_F3 0x1d4 -#define KEY_FN_F4 0x1d5 -#define KEY_FN_F5 0x1d6 -#define KEY_FN_F6 0x1d7 -#define KEY_FN_F7 0x1d8 -#define KEY_FN_F8 0x1d9 -#define KEY_FN_F9 0x1da -#define KEY_FN_F10 0x1db -#define KEY_FN_F11 0x1dc -#define KEY_FN_F12 0x1dd -#define KEY_FN_1 0x1de -#define KEY_FN_2 0x1df -#define KEY_FN_D 0x1e0 -#define KEY_FN_E 0x1e1 -#define KEY_FN_F 0x1e2 -#define KEY_FN_S 0x1e3 -#define KEY_FN_B 0x1e4 -#define KEY_FN_RIGHT_SHIFT 0x1e5 - -#define KEY_BRL_DOT1 0x1f1 -#define KEY_BRL_DOT2 0x1f2 -#define KEY_BRL_DOT3 0x1f3 -#define KEY_BRL_DOT4 0x1f4 -#define KEY_BRL_DOT5 0x1f5 -#define KEY_BRL_DOT6 0x1f6 -#define KEY_BRL_DOT7 0x1f7 -#define KEY_BRL_DOT8 0x1f8 -#define KEY_BRL_DOT9 0x1f9 -#define KEY_BRL_DOT10 0x1fa - -#define KEY_NUMERIC_0 0x200 /* used by phones, remote controls, */ -#define KEY_NUMERIC_1 0x201 /* and other keypads */ -#define KEY_NUMERIC_2 0x202 -#define KEY_NUMERIC_3 0x203 -#define KEY_NUMERIC_4 0x204 -#define KEY_NUMERIC_5 0x205 -#define KEY_NUMERIC_6 0x206 -#define KEY_NUMERIC_7 0x207 -#define KEY_NUMERIC_8 0x208 -#define KEY_NUMERIC_9 0x209 -#define KEY_NUMERIC_STAR 0x20a -#define KEY_NUMERIC_POUND 0x20b -#define KEY_NUMERIC_A 0x20c /* Phone key A - HUT Telephony 0xb9 */ -#define KEY_NUMERIC_B 0x20d -#define KEY_NUMERIC_C 0x20e -#define KEY_NUMERIC_D 0x20f - -#define KEY_CAMERA_FOCUS 0x210 -#define KEY_WPS_BUTTON 0x211 /* WiFi Protected Setup key */ - -#define KEY_TOUCHPAD_TOGGLE 0x212 /* Request switch touchpad on or off */ -#define KEY_TOUCHPAD_ON 0x213 -#define KEY_TOUCHPAD_OFF 0x214 - -#define KEY_CAMERA_ZOOMIN 0x215 -#define KEY_CAMERA_ZOOMOUT 0x216 -#define KEY_CAMERA_UP 0x217 -#define KEY_CAMERA_DOWN 0x218 -#define KEY_CAMERA_LEFT 0x219 -#define KEY_CAMERA_RIGHT 0x21a - -#define KEY_ATTENDANT_ON 0x21b -#define KEY_ATTENDANT_OFF 0x21c -#define KEY_ATTENDANT_TOGGLE 0x21d /* Attendant call on or off */ -#define KEY_LIGHTS_TOGGLE 0x21e /* Reading light on or off */ - -#define BTN_DPAD_UP 0x220 -#define BTN_DPAD_DOWN 0x221 -#define BTN_DPAD_LEFT 0x222 -#define BTN_DPAD_RIGHT 0x223 - -#define KEY_ALS_TOGGLE 0x230 /* Ambient light sensor */ -#define KEY_ROTATE_LOCK_TOGGLE 0x231 /* Display rotation lock */ - -#define KEY_BUTTONCONFIG 0x240 /* AL Button Configuration */ -#define KEY_TASKMANAGER 0x241 /* AL Task/Project Manager */ -#define KEY_JOURNAL 0x242 /* AL Log/Journal/Timecard */ -#define KEY_CONTROLPANEL 0x243 /* AL Control Panel */ -#define KEY_APPSELECT 0x244 /* AL Select Task/Application */ -#define KEY_SCREENSAVER 0x245 /* AL Screen Saver */ -#define KEY_VOICECOMMAND 0x246 /* Listening Voice Command */ -#define KEY_ASSISTANT 0x247 /* AL Context-aware desktop assistant */ -#define KEY_KBD_LAYOUT_NEXT 0x248 /* AC Next Keyboard Layout Select */ -#define KEY_EMOJI_PICKER 0x249 /* Show/hide emoji picker (HUTRR101) */ -#define KEY_DICTATE 0x24a /* Start or Stop Voice Dictation Session (HUTRR99) */ -#define KEY_CAMERA_ACCESS_ENABLE 0x24b /* Enables programmatic access to camera devices. (HUTRR72) */ -#define KEY_CAMERA_ACCESS_DISABLE 0x24c /* Disables programmatic access to camera devices. (HUTRR72) */ -#define KEY_CAMERA_ACCESS_TOGGLE 0x24d /* Toggles the current state of the camera access control. (HUTRR72) */ - -#define KEY_BRIGHTNESS_MIN 0x250 /* Set Brightness to Minimum */ -#define KEY_BRIGHTNESS_MAX 0x251 /* Set Brightness to Maximum */ - -#define KEY_KBDINPUTASSIST_PREV 0x260 -#define KEY_KBDINPUTASSIST_NEXT 0x261 -#define KEY_KBDINPUTASSIST_PREVGROUP 0x262 -#define KEY_KBDINPUTASSIST_NEXTGROUP 0x263 -#define KEY_KBDINPUTASSIST_ACCEPT 0x264 -#define KEY_KBDINPUTASSIST_CANCEL 0x265 - -/* Diagonal movement keys */ -#define KEY_RIGHT_UP 0x266 -#define KEY_RIGHT_DOWN 0x267 -#define KEY_LEFT_UP 0x268 -#define KEY_LEFT_DOWN 0x269 - -#define KEY_ROOT_MENU 0x26a /* Show Device's Root Menu */ -/* Show Top Menu of the Media (e.g. DVD) */ -#define KEY_MEDIA_TOP_MENU 0x26b -#define KEY_NUMERIC_11 0x26c -#define KEY_NUMERIC_12 0x26d -/* - * Toggle Audio Description: refers to an audio service that helps blind and - * visually impaired consumers understand the action in a program. Note: in - * some countries this is referred to as "Video Description". - */ -#define KEY_AUDIO_DESC 0x26e -#define KEY_3D_MODE 0x26f -#define KEY_NEXT_FAVORITE 0x270 -#define KEY_STOP_RECORD 0x271 -#define KEY_PAUSE_RECORD 0x272 -#define KEY_VOD 0x273 /* Video on Demand */ -#define KEY_UNMUTE 0x274 -#define KEY_FASTREVERSE 0x275 -#define KEY_SLOWREVERSE 0x276 -/* - * Control a data application associated with the currently viewed channel, - * e.g. teletext or data broadcast application (MHEG, MHP, HbbTV, etc.) - */ -#define KEY_DATA 0x277 -#define KEY_ONSCREEN_KEYBOARD 0x278 -/* Electronic privacy screen control */ -#define KEY_PRIVACY_SCREEN_TOGGLE 0x279 - -/* Select an area of screen to be copied */ -#define KEY_SELECTIVE_SCREENSHOT 0x27a - -/* Move the focus to the next or previous user controllable element within a UI container */ -#define KEY_NEXT_ELEMENT 0x27b -#define KEY_PREVIOUS_ELEMENT 0x27c - -/* Toggle Autopilot engagement */ -#define KEY_AUTOPILOT_ENGAGE_TOGGLE 0x27d - -/* Shortcut Keys */ -#define KEY_MARK_WAYPOINT 0x27e -#define KEY_SOS 0x27f -#define KEY_NAV_CHART 0x280 -#define KEY_FISHING_CHART 0x281 -#define KEY_SINGLE_RANGE_RADAR 0x282 -#define KEY_DUAL_RANGE_RADAR 0x283 -#define KEY_RADAR_OVERLAY 0x284 -#define KEY_TRADITIONAL_SONAR 0x285 -#define KEY_CLEARVU_SONAR 0x286 -#define KEY_SIDEVU_SONAR 0x287 -#define KEY_NAV_INFO 0x288 -#define KEY_BRIGHTNESS_MENU 0x289 - -/* - * Some keyboards have keys which do not have a defined meaning, these keys - * are intended to be programmed / bound to macros by the user. For most - * keyboards with these macro-keys the key-sequence to inject, or action to - * take, is all handled by software on the host side. So from the kernel's - * point of view these are just normal keys. - * - * The KEY_MACRO# codes below are intended for such keys, which may be labeled - * e.g. G1-G18, or S1 - S30. The KEY_MACRO# codes MUST NOT be used for keys - * where the marking on the key does indicate a defined meaning / purpose. - * - * The KEY_MACRO# codes MUST also NOT be used as fallback for when no existing - * KEY_FOO define matches the marking / purpose. In this case a new KEY_FOO - * define MUST be added. - */ -#define KEY_MACRO1 0x290 -#define KEY_MACRO2 0x291 -#define KEY_MACRO3 0x292 -#define KEY_MACRO4 0x293 -#define KEY_MACRO5 0x294 -#define KEY_MACRO6 0x295 -#define KEY_MACRO7 0x296 -#define KEY_MACRO8 0x297 -#define KEY_MACRO9 0x298 -#define KEY_MACRO10 0x299 -#define KEY_MACRO11 0x29a -#define KEY_MACRO12 0x29b -#define KEY_MACRO13 0x29c -#define KEY_MACRO14 0x29d -#define KEY_MACRO15 0x29e -#define KEY_MACRO16 0x29f -#define KEY_MACRO17 0x2a0 -#define KEY_MACRO18 0x2a1 -#define KEY_MACRO19 0x2a2 -#define KEY_MACRO20 0x2a3 -#define KEY_MACRO21 0x2a4 -#define KEY_MACRO22 0x2a5 -#define KEY_MACRO23 0x2a6 -#define KEY_MACRO24 0x2a7 -#define KEY_MACRO25 0x2a8 -#define KEY_MACRO26 0x2a9 -#define KEY_MACRO27 0x2aa -#define KEY_MACRO28 0x2ab -#define KEY_MACRO29 0x2ac -#define KEY_MACRO30 0x2ad - -/* - * Some keyboards with the macro-keys described above have some extra keys - * for controlling the host-side software responsible for the macro handling: - * -A macro recording start/stop key. Note that not all keyboards which emit - * KEY_MACRO_RECORD_START will also emit KEY_MACRO_RECORD_STOP if - * KEY_MACRO_RECORD_STOP is not advertised, then KEY_MACRO_RECORD_START - * should be interpreted as a recording start/stop toggle; - * -Keys for switching between different macro (pre)sets, either a key for - * cycling through the configured presets or keys to directly select a preset. - */ -#define KEY_MACRO_RECORD_START 0x2b0 -#define KEY_MACRO_RECORD_STOP 0x2b1 -#define KEY_MACRO_PRESET_CYCLE 0x2b2 -#define KEY_MACRO_PRESET1 0x2b3 -#define KEY_MACRO_PRESET2 0x2b4 -#define KEY_MACRO_PRESET3 0x2b5 - -/* - * Some keyboards have a buildin LCD panel where the contents are controlled - * by the host. Often these have a number of keys directly below the LCD - * intended for controlling a menu shown on the LCD. These keys often don't - * have any labeling so we just name them KEY_KBD_LCD_MENU# - */ -#define KEY_KBD_LCD_MENU1 0x2b8 -#define KEY_KBD_LCD_MENU2 0x2b9 -#define KEY_KBD_LCD_MENU3 0x2ba -#define KEY_KBD_LCD_MENU4 0x2bb -#define KEY_KBD_LCD_MENU5 0x2bc - -#define BTN_TRIGGER_HAPPY 0x2c0 -#define BTN_TRIGGER_HAPPY1 0x2c0 -#define BTN_TRIGGER_HAPPY2 0x2c1 -#define BTN_TRIGGER_HAPPY3 0x2c2 -#define BTN_TRIGGER_HAPPY4 0x2c3 -#define BTN_TRIGGER_HAPPY5 0x2c4 -#define BTN_TRIGGER_HAPPY6 0x2c5 -#define BTN_TRIGGER_HAPPY7 0x2c6 -#define BTN_TRIGGER_HAPPY8 0x2c7 -#define BTN_TRIGGER_HAPPY9 0x2c8 -#define BTN_TRIGGER_HAPPY10 0x2c9 -#define BTN_TRIGGER_HAPPY11 0x2ca -#define BTN_TRIGGER_HAPPY12 0x2cb -#define BTN_TRIGGER_HAPPY13 0x2cc -#define BTN_TRIGGER_HAPPY14 0x2cd -#define BTN_TRIGGER_HAPPY15 0x2ce -#define BTN_TRIGGER_HAPPY16 0x2cf -#define BTN_TRIGGER_HAPPY17 0x2d0 -#define BTN_TRIGGER_HAPPY18 0x2d1 -#define BTN_TRIGGER_HAPPY19 0x2d2 -#define BTN_TRIGGER_HAPPY20 0x2d3 -#define BTN_TRIGGER_HAPPY21 0x2d4 -#define BTN_TRIGGER_HAPPY22 0x2d5 -#define BTN_TRIGGER_HAPPY23 0x2d6 -#define BTN_TRIGGER_HAPPY24 0x2d7 -#define BTN_TRIGGER_HAPPY25 0x2d8 -#define BTN_TRIGGER_HAPPY26 0x2d9 -#define BTN_TRIGGER_HAPPY27 0x2da -#define BTN_TRIGGER_HAPPY28 0x2db -#define BTN_TRIGGER_HAPPY29 0x2dc -#define BTN_TRIGGER_HAPPY30 0x2dd -#define BTN_TRIGGER_HAPPY31 0x2de -#define BTN_TRIGGER_HAPPY32 0x2df -#define BTN_TRIGGER_HAPPY33 0x2e0 -#define BTN_TRIGGER_HAPPY34 0x2e1 -#define BTN_TRIGGER_HAPPY35 0x2e2 -#define BTN_TRIGGER_HAPPY36 0x2e3 -#define BTN_TRIGGER_HAPPY37 0x2e4 -#define BTN_TRIGGER_HAPPY38 0x2e5 -#define BTN_TRIGGER_HAPPY39 0x2e6 -#define BTN_TRIGGER_HAPPY40 0x2e7 - -/* We avoid low common keys in module aliases so they don't get huge. */ -#define KEY_MIN_INTERESTING KEY_MUTE -#define KEY_MAX 0x2ff -#define KEY_CNT (KEY_MAX+1) - -/* - * Relative axes - */ - -#define REL_X 0x00 -#define REL_Y 0x01 -#define REL_Z 0x02 -#define REL_RX 0x03 -#define REL_RY 0x04 -#define REL_RZ 0x05 -#define REL_HWHEEL 0x06 -#define REL_DIAL 0x07 -#define REL_WHEEL 0x08 -#define REL_MISC 0x09 -/* - * 0x0a is reserved and should not be used in input drivers. - * It was used by HID as REL_MISC+1 and userspace needs to detect if - * the next REL_* event is correct or is just REL_MISC + n. - * We define here REL_RESERVED so userspace can rely on it and detect - * the situation described above. - */ -#define REL_RESERVED 0x0a -#define REL_WHEEL_HI_RES 0x0b -#define REL_HWHEEL_HI_RES 0x0c -#define REL_MAX 0x0f -#define REL_CNT (REL_MAX+1) - -/* - * Absolute axes - */ - -#define ABS_X 0x00 -#define ABS_Y 0x01 -#define ABS_Z 0x02 -#define ABS_RX 0x03 -#define ABS_RY 0x04 -#define ABS_RZ 0x05 -#define ABS_THROTTLE 0x06 -#define ABS_RUDDER 0x07 -#define ABS_WHEEL 0x08 -#define ABS_GAS 0x09 -#define ABS_BRAKE 0x0a -#define ABS_HAT0X 0x10 -#define ABS_HAT0Y 0x11 -#define ABS_HAT1X 0x12 -#define ABS_HAT1Y 0x13 -#define ABS_HAT2X 0x14 -#define ABS_HAT2Y 0x15 -#define ABS_HAT3X 0x16 -#define ABS_HAT3Y 0x17 -#define ABS_PRESSURE 0x18 -#define ABS_DISTANCE 0x19 -#define ABS_TILT_X 0x1a -#define ABS_TILT_Y 0x1b -#define ABS_TOOL_WIDTH 0x1c - -#define ABS_VOLUME 0x20 -#define ABS_PROFILE 0x21 - -#define ABS_MISC 0x28 - -/* - * 0x2e is reserved and should not be used in input drivers. - * It was used by HID as ABS_MISC+6 and userspace needs to detect if - * the next ABS_* event is correct or is just ABS_MISC + n. - * We define here ABS_RESERVED so userspace can rely on it and detect - * the situation described above. - */ -#define ABS_RESERVED 0x2e - -#define ABS_MT_SLOT 0x2f /* MT slot being modified */ -#define ABS_MT_TOUCH_MAJOR 0x30 /* Major axis of touching ellipse */ -#define ABS_MT_TOUCH_MINOR 0x31 /* Minor axis (omit if circular) */ -#define ABS_MT_WIDTH_MAJOR 0x32 /* Major axis of approaching ellipse */ -#define ABS_MT_WIDTH_MINOR 0x33 /* Minor axis (omit if circular) */ -#define ABS_MT_ORIENTATION 0x34 /* Ellipse orientation */ -#define ABS_MT_POSITION_X 0x35 /* Center X touch position */ -#define ABS_MT_POSITION_Y 0x36 /* Center Y touch position */ -#define ABS_MT_TOOL_TYPE 0x37 /* Type of touching device */ -#define ABS_MT_BLOB_ID 0x38 /* Group a set of packets as a blob */ -#define ABS_MT_TRACKING_ID 0x39 /* Unique ID of initiated contact */ -#define ABS_MT_PRESSURE 0x3a /* Pressure on contact area */ -#define ABS_MT_DISTANCE 0x3b /* Contact hover distance */ -#define ABS_MT_TOOL_X 0x3c /* Center X tool position */ -#define ABS_MT_TOOL_Y 0x3d /* Center Y tool position */ - -#define ABS_MAX 0x3f -#define ABS_CNT (ABS_MAX+1) - -/* - * Switch events - */ - -#define SW_LID 0x00 /* set = lid shut */ -#define SW_TABLET_MODE 0x01 /* set = tablet mode */ -#define SW_HEADPHONE_INSERT 0x02 /* set = inserted */ -#define SW_RFKILL_ALL 0x03 /* rfkill master switch, type "any" - set = radio enabled */ -#define SW_RADIO SW_RFKILL_ALL /* deprecated */ -#define SW_MICROPHONE_INSERT 0x04 /* set = inserted */ -#define SW_DOCK 0x05 /* set = plugged into dock */ -#define SW_LINEOUT_INSERT 0x06 /* set = inserted */ -#define SW_JACK_PHYSICAL_INSERT 0x07 /* set = mechanical switch set */ -#define SW_VIDEOOUT_INSERT 0x08 /* set = inserted */ -#define SW_CAMERA_LENS_COVER 0x09 /* set = lens covered */ -#define SW_KEYPAD_SLIDE 0x0a /* set = keypad slide out */ -#define SW_FRONT_PROXIMITY 0x0b /* set = front proximity sensor active */ -#define SW_ROTATE_LOCK 0x0c /* set = rotate locked/disabled */ -#define SW_LINEIN_INSERT 0x0d /* set = inserted */ -#define SW_MUTE_DEVICE 0x0e /* set = device disabled */ -#define SW_PEN_INSERTED 0x0f /* set = pen inserted */ -#define SW_MACHINE_COVER 0x10 /* set = cover closed */ -#define SW_MAX 0x10 -#define SW_CNT (SW_MAX+1) - -/* - * Misc events - */ - -#define MSC_SERIAL 0x00 -#define MSC_PULSELED 0x01 -#define MSC_GESTURE 0x02 -#define MSC_RAW 0x03 -#define MSC_SCAN 0x04 -#define MSC_TIMESTAMP 0x05 -#define MSC_MAX 0x07 -#define MSC_CNT (MSC_MAX+1) - -/* - * LEDs - */ - -#define LED_NUML 0x00 -#define LED_CAPSL 0x01 -#define LED_SCROLLL 0x02 -#define LED_COMPOSE 0x03 -#define LED_KANA 0x04 -#define LED_SLEEP 0x05 -#define LED_SUSPEND 0x06 -#define LED_MUTE 0x07 -#define LED_MISC 0x08 -#define LED_MAIL 0x09 -#define LED_CHARGING 0x0a -#define LED_MAX 0x0f -#define LED_CNT (LED_MAX+1) - -/* - * Autorepeat values - */ - -#define REP_DELAY 0x00 -#define REP_PERIOD 0x01 -#define REP_MAX 0x01 -#define REP_CNT (REP_MAX+1) - -/* - * Sounds - */ - -#define SND_CLICK 0x00 -#define SND_BELL 0x01 -#define SND_TONE 0x02 -#define SND_MAX 0x07 -#define SND_CNT (SND_MAX+1) - -#endif /* _EVDEV_INPUT_EVENT_CODES_H */ diff --git a/Kernel/Devices/HID/VirtIO/Input.cpp b/Kernel/Devices/HID/VirtIO/Input.cpp deleted file mode 100644 index 7ec5a271b14..00000000000 --- a/Kernel/Devices/HID/VirtIO/Input.cpp +++ /dev/null @@ -1,446 +0,0 @@ -/* - * Copyright (c) 2024, Sönke Holz - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include -#include -#include - -namespace Kernel::VirtIO { - -struct VirtIOInputEvent { - LittleEndian type; - LittleEndian code; - LittleEndian value; -}; -static_assert(AssertSize()); - -struct VirtIOInputConfig { - enum class Select : u8 { - Unset = 0x00, - IDName = 0x01, - IDSerial = 0x02, - IDDevIDs = 0x03, - PropBits = 0x10, - EvBits = 0x11, - AbsInfo = 0x12, - }; - - struct AbsInfo { - LittleEndian min; - LittleEndian max; - LittleEndian fuzz; - LittleEndian flat; - LittleEndian res; - }; - static_assert(AssertSize()); - - struct DevIDs { - LittleEndian bustype; - LittleEndian vendor; - LittleEndian product; - LittleEndian version; - }; - static_assert(AssertSize()); - - Select select; - u8 subsel; - u8 size; - u8 reserved[5]; - union { - char string[128]; - u8 bitmap[128]; - AbsInfo abs; - DevIDs ids; - } u; -}; -static_assert(AssertSize()); - -// clang-format off -static constexpr auto unshifted_evdev_key_map = to_array({ - // 0x00-0x0f - { Key_Invalid, 0xff }, { Key_Escape, 0x01 }, { Key_1, 0x02 }, { Key_2, 0x03 }, - { Key_3, 0x04 }, { Key_4, 0x05 }, { Key_5, 0x06 }, { Key_6, 0x07 }, - { Key_7, 0x08 }, { Key_8, 0x09 }, { Key_9, 0x0a }, { Key_0, 0x0b }, - { Key_Minus, 0x0c }, { Key_Equal, 0x0d }, { Key_Backspace, 0x0e }, { Key_Tab, 0x0f }, - - // 0x10-0x1f - { Key_Q, 0x10 }, { Key_W, 0x11 }, { Key_E, 0x12 }, { Key_R, 0x13 }, - { Key_T, 0x14 }, { Key_Y, 0x15 }, { Key_U, 0x16 }, { Key_I, 0x17 }, - { Key_O, 0x18 }, { Key_P, 0x19 }, { Key_LeftBracket, 0x1a }, { Key_RightBracket, 0x1b }, - { Key_Return, 0x1c }, { Key_Control, 0x1d }, { Key_A, 0x1e }, { Key_S, 0x1f }, - - // 0x20-0x2f - { Key_D, 0x20 }, { Key_F, 0x21 }, { Key_G, 0x22 }, { Key_H, 0x23 }, - { Key_J, 0x24 }, { Key_K, 0x25 }, { Key_L, 0x26 }, { Key_Semicolon, 0x27 }, - { Key_Apostrophe, 0x28 }, { Key_Backtick, 0x29 }, { Key_LeftShift, 0xff }, { Key_Backslash, 0x2b }, - { Key_Z, 0x2c }, { Key_X, 0x2d }, { Key_C, 0x2e }, { Key_V, 0x2f }, - - // 0x30-0x3f - { Key_B, 0x30 }, { Key_N, 0x31 }, { Key_M, 0x32 }, { Key_Comma, 0x33 }, - { Key_Period, 0x34 }, { Key_Slash, 0x35 }, { Key_RightShift, 0xff }, { Key_Asterisk, 0x37 }, - { Key_Alt, 0xff }, { Key_Space, 0x39 }, { Key_CapsLock, 0xff }, { Key_F1, 0xff }, - { Key_F2, 0xff }, { Key_F3, 0xff }, { Key_F4, 0xff }, { Key_F5, 0xff }, - - // 0x40-0x4f - { Key_F6, 0xff }, { Key_F7, 0xff }, { Key_F8, 0xff }, { Key_F9, 0xff }, - { Key_F10, 0xff }, { Key_NumLock, 0x45 }, { Key_ScrollLock, 0xff }, { Key_Home, 0xff }, - { Key_Up, 0xff }, { Key_PageUp, 0xff }, { Key_Minus, 0x4a }, { Key_Left, 0xff }, - { Key_Invalid, 0xff }, { Key_Right, 0xff }, { Key_Plus, 0x4e }, { Key_End, 0xff }, - - // 0x50-0x5f - { Key_Down, 0xff }, { Key_PageDown, 0xff }, { Key_Insert, 0xff }, { Key_Delete, 0xff }, - { Key_Invalid, 0xff }, { Key_Invalid, 0xff }, { Key_Backslash, 0x56 }, { Key_F11, 0xff }, - { Key_F12, 0xff }, { Key_Invalid, 0xff }, { Key_Invalid, 0xff }, { Key_Invalid, 0xff }, - { Key_Invalid, 0xff }, { Key_Invalid, 0xff }, { Key_Invalid, 0xff }, { Key_Invalid, 0xff }, - - // 0x60-0x6f - // FIXME: Add Numpad "/" key to character map for key code 0x62 - { Key_Return, 0xff }, { Key_RightControl, 0xff }, { Key_Slash, 0xff }, { Key_SysRq, 0xff }, - { Key_RightAlt, 0xff }, { Key_Invalid, 0xff }, { Key_Home, 0xff }, { Key_Up, 0xff }, - { Key_PageUp, 0xff }, { Key_Left, 0xff }, { Key_Right, 0xff }, { Key_End, 0xff }, - { Key_Down, 0xff }, { Key_PageDown, 0xff }, { Key_Insert, 0xff }, { Key_Delete, 0xff }, - - // 0x70-0x7f - { Key_Invalid, 0xff }, { Key_Mute, 0xff }, { Key_VolumeDown, 0xff }, { Key_VolumeUp, 0xff }, - { Key_Power, 0xff }, { Key_Equal, 0xff }, { Key_Invalid, 0xff }, { Key_PauseBreak, 0xff }, - { Key_Invalid, 0xff }, { Key_Invalid, 0xff }, { Key_Invalid, 0xff }, { Key_Invalid, 0xff }, - { Key_Invalid, 0xff }, { Key_Super, 0xff }, { Key_Super, 0xff }, { Key_Menu, 0xff }, - - // 0x80-0x8f - { Key_Stop, 0xff }, { Key_Invalid, 0xff }, { Key_Invalid, 0xff }, { Key_Invalid, 0xff }, - { Key_Invalid, 0xff }, { Key_Invalid, 0xff }, { Key_Invalid, 0xff }, { Key_Invalid, 0xff }, - { Key_Invalid, 0xff }, { Key_Invalid, 0xff }, { Key_Invalid, 0xff }, { Key_Invalid, 0xff }, - { Key_Calculator, 0xff }, { Key_Invalid, 0xff }, { Key_Sleep, 0xff }, { Key_Wake, 0xff }, -}); -// clang-format on - -// clang-format off -static constexpr auto shifted_evdev_key_map = to_array({ - // 0x00-0x0f - { Key_Invalid, 0xff }, { Key_Escape, 0x01 }, { Key_ExclamationPoint, 0x02 }, { Key_AtSign, 0x03 }, - { Key_Hashtag, 0x04 }, { Key_Dollar, 0x05 }, { Key_Percent, 0x06 }, { Key_Circumflex, 0x07 }, - { Key_Ampersand, 0x08 }, { Key_Asterisk, 0x09 }, { Key_LeftParen, 0x0a }, { Key_RightParen, 0x0b }, - { Key_Underscore, 0x0c }, { Key_Plus, 0x0d }, { Key_Backspace, 0x0e }, { Key_Tab, 0x0f }, - - // 0x10-0x1f - { Key_Q, 0x10 }, { Key_W, 0x11 }, { Key_E, 0x12 }, { Key_R, 0x13 }, - { Key_T, 0x14 }, { Key_Y, 0x15 }, { Key_U, 0x16 }, { Key_I, 0x17 }, - { Key_O, 0x18 }, { Key_P, 0x19 }, { Key_LeftBrace, 0x1a }, { Key_RightBrace, 0x1b }, - { Key_Return, 0x1c }, { Key_Control, 0x1d }, { Key_A, 0x1e }, { Key_S, 0x1f }, - - // 0x20-0x2f - { Key_D, 0x20 }, { Key_F, 0x21 }, { Key_G, 0x22 }, { Key_H, 0x23 }, - { Key_J, 0x24 }, { Key_K, 0x25 }, { Key_L, 0x26 }, { Key_Colon, 0x27 }, - { Key_DoubleQuote, 0x28 }, { Key_Tilde, 0x29 }, { Key_LeftShift, 0xff }, { Key_Pipe, 0x2b }, - { Key_Z, 0x2c }, { Key_X, 0x2d }, { Key_C, 0x2e }, { Key_V, 0x2f }, - - // 0x30-0x3f - { Key_B, 0x30 }, { Key_N, 0x31 }, { Key_M, 0x32 }, { Key_LessThan, 0x33 }, - { Key_GreaterThan, 0x34 }, { Key_QuestionMark, 0x35 }, { Key_RightShift, 0xff }, { Key_Asterisk, 0x37 }, - { Key_Alt, 0xff }, { Key_Space, 0x39 }, { Key_CapsLock, 0xff }, { Key_F1, 0xff }, - { Key_F2, 0xff }, { Key_F3, 0xff }, { Key_F4, 0xff }, { Key_F5, 0xff }, - - // 0x40-0x4f - { Key_F6, 0xff }, { Key_F7, 0xff }, { Key_F8, 0xff }, { Key_F9, 0xff }, - { Key_F10, 0xff }, { Key_NumLock, 0xff }, { Key_ScrollLock, 0xff }, { Key_Home, 0xff }, - { Key_Up, 0xff }, { Key_PageUp, 0xff }, { Key_Minus, 0x4a }, { Key_Left, 0xff }, - { Key_Invalid, 0xff }, { Key_Right, 0xff }, { Key_Plus, 0x4e }, { Key_End, 0xff }, - - // 0x50-0x5f - { Key_Down, 0xff }, { Key_PageDown, 0xff }, { Key_Insert, 0xff }, { Key_Delete, 0xff }, - { Key_Invalid, 0xff }, { Key_Invalid, 0xff }, { Key_Pipe, 0x56 }, { Key_F11, 0xff }, - { Key_F12, 0xff }, { Key_Invalid, 0xff }, { Key_Invalid, 0xff }, { Key_Invalid, 0xff }, - { Key_Invalid, 0xff }, { Key_Invalid, 0xff }, { Key_Invalid, 0xff }, { Key_Invalid, 0xff }, -}); -// clang-format on - -UNMAP_AFTER_INIT NonnullLockRefPtr Input::must_create_for_pci_instance(PCI::DeviceIdentifier const& device_identifier) -{ - auto pci_transport_link = MUST(PCIeTransportLink::create(device_identifier)); - return adopt_lock_ref_if_nonnull(new Input(move(pci_transport_link))).release_nonnull(); -} - -UNMAP_AFTER_INIT ErrorOr Input::initialize_virtio_resources() -{ - TRY(Device::initialize_virtio_resources()); - auto const* cfg = TRY(transport_entity().get_config(VirtIO::ConfigurationType::Device)); - TRY(negotiate_features([&](auto) { return 0; })); - - transport_entity().config_write8(*cfg, offsetof(VirtIOInputConfig, subsel), 0); - - OwnPtr name; - - transport_entity().config_write8(*cfg, offsetof(VirtIOInputConfig, select), to_underlying(VirtIOInputConfig::Select::IDName)); - transport_entity().read_config_atomic([&]() { - auto size = transport_entity().config_read8(*cfg, offsetof(VirtIOInputConfig, size)); - if (size == 0) - return; - - VERIFY(size <= sizeof(VirtIOInputConfig::u.string)); - - char* name_chars = nullptr; - name = MUST(KString::try_create_uninitialized(size, name_chars)); - - for (size_t i = 0; i < size; i++) - name_chars[i] = static_cast(transport_entity().config_read8(*cfg, offsetof(VirtIOInputConfig, u.string) + i)); - }); - - if (name) - dbgln("VirtIO::Input: Device name: {}", name); - - transport_entity().config_write8(*cfg, offsetof(VirtIOInputConfig, select), to_underlying(VirtIOInputConfig::Select::AbsInfo)); - transport_entity().read_config_atomic([&]() { - auto size = transport_entity().config_read8(*cfg, offsetof(VirtIOInputConfig, size)); - if (size == 0) - return; - - VERIFY(size == sizeof(VirtIOInputConfig::u.abs)); - - m_abs_min = bit_cast>(transport_entity().config_read32(*cfg, offsetof(VirtIOInputConfig, u.abs.min))); - m_abs_max = bit_cast>(transport_entity().config_read32(*cfg, offsetof(VirtIOInputConfig, u.abs.max))); - }); - - TRY(setup_queues(2)); - finish_init(); - - auto& event_queue = get_queue(EVENTQ); - SpinlockLocker event_queue_lock(event_queue.lock()); - - m_event_buffer_region = TRY(MM.allocate_contiguous_kernel_region(TRY(Memory::page_round_up(event_queue.size() * sizeof(VirtIOInputEvent))), "VirtIO::Input eventq"sv, Memory::Region::Access::ReadWrite)); - - QueueChain event_queue_chain(event_queue); - - for (size_t queue_idx = 0; queue_idx < event_queue.size(); ++queue_idx) { - auto buffer_start = m_event_buffer_region->physical_page(0)->paddr().offset(queue_idx * sizeof(VirtIOInputEvent)); - auto did_add_buffer = event_queue_chain.add_buffer_to_chain(buffer_start, sizeof(VirtIOInputEvent), BufferType::DeviceWritable); - VERIFY(did_add_buffer); - supply_chain_and_notify(EVENTQ, event_queue_chain); - } - - HIDManagement::the().attach_standalone_hid_device(*m_mouse_device); - HIDManagement::the().attach_standalone_hid_device(*m_keyboard_device); - - return {}; -} - -UNMAP_AFTER_INIT Input::Input(NonnullOwnPtr transport_entity) - : VirtIO::Device(move(transport_entity)) - , m_mouse_device(MouseDevice::try_to_initialize().release_value_but_fixme_should_propagate_errors()) - , m_keyboard_device(KeyboardDevice::try_to_initialize().release_value_but_fixme_should_propagate_errors()) -{ -} - -ErrorOr Input::handle_device_config_change() -{ - return {}; -} - -void Input::handle_queue_update(u16 queue_index) -{ - VERIFY(queue_index == EVENTQ); - - auto& queue = get_queue(EVENTQ); - SpinlockLocker queue_lock(queue.lock()); - - size_t used; - QueueChain popped_chain = queue.pop_used_buffer_chain(used); - while (!popped_chain.is_empty()) { - popped_chain.for_each([this](PhysicalAddress paddr, size_t) { - size_t offset = paddr.get() - m_event_buffer_region->physical_page(0)->paddr().get(); - auto const& event = *reinterpret_cast(m_event_buffer_region->vaddr().offset(offset).as_ptr()); - handle_event(event); - }); - - supply_chain_and_notify(EVENTQ, popped_chain); - popped_chain = queue.pop_used_buffer_chain(used); - } -} - -void Input::handle_event(VirtIOInputEvent const& event) -{ - // TODO: Set lock key LEDs - - switch (event.type) { - case EV_SYN: - switch (event.code) { - case SYN_REPORT: - m_mouse_device->handle_mouse_packet_input_event(m_current_mouse_packet); - - // Don't reset the x/y values if the last event was an absolute event, as otherwise the mouse would jump to the top left corner on events other than mouse movement. - if (m_current_mouse_packet.is_relative) { - m_current_mouse_packet.x = 0; - m_current_mouse_packet.y = 0; - } - - m_current_mouse_packet.z = 0; - m_current_mouse_packet.w = 0; - - break; - - default: - dbgln_if(VIRTIO_DEBUG, "VirtIO::Input: Unknown EV_SYN event code: {:#x}", event.code); - break; - } - break; - - case EV_KEY: - switch (event.code) { - case BTN_LEFT: - if (event.value == 1) - m_current_mouse_packet.buttons |= MousePacket::Button::LeftButton; - else - m_current_mouse_packet.buttons &= ~MousePacket::Button::LeftButton; - break; - - case BTN_RIGHT: - if (event.value == 1) - m_current_mouse_packet.buttons |= MousePacket::Button::RightButton; - else - m_current_mouse_packet.buttons &= ~MousePacket::Button::RightButton; - break; - - case BTN_MIDDLE: - if (event.value == 1) - m_current_mouse_packet.buttons |= MousePacket::Button::MiddleButton; - else - m_current_mouse_packet.buttons &= ~MousePacket::Button::MiddleButton; - break; - - default: - // NOTE: We only supply entropy from the keyboard device, as each MouseDevice already has a EntropySource attached to it. - m_entropy_source.add_random_event(event.code); - - m_keyboard_device->update_modifier(Mod_Keypad, false); - - RawKeyEvent raw_key_event; - raw_key_event.is_press_down = event.value == 1; - raw_key_event.scancode = event.code; - - switch (event.code) { - case KEY_LEFTALT: - m_keyboard_device->update_modifier(Mod_Alt, raw_key_event.is_press()); - break; - - case KEY_LEFTCTRL: - case KEY_RIGHTCTRL: - m_keyboard_device->update_modifier(Mod_Ctrl, raw_key_event.is_press()); - break; - - case KEY_LEFTSHIFT: - case KEY_RIGHTSHIFT: - m_keyboard_device->update_modifier(Mod_Shift, raw_key_event.is_press()); - break; - - case KEY_LEFTMETA: - case KEY_RIGHTMETA: - m_keyboard_device->update_modifier(Mod_Super, raw_key_event.is_press()); - break; - - case KEY_RIGHTALT: - m_keyboard_device->update_modifier(Mod_AltGr, raw_key_event.is_press()); - break; - - default: - break; - } - - if ((event.code >= KEY_KP7 && event.code <= KEY_KPDOT) - || event.code == KEY_KPASTERISK - || event.code == KEY_KPENTER - || event.code == KEY_KPEQUAL - || event.code == KEY_KPSLASH) { - m_keyboard_device->update_modifier(Mod_Keypad, true); - } - - // The shift key only applies to small key codes, so only use the shifted key map if the event code is small enough. - bool use_shifted_key_map = (m_keyboard_device->modifiers() & Mod_Shift) != 0 && event.code < shifted_evdev_key_map.size(); - - auto key_map = use_shifted_key_map ? Span(shifted_evdev_key_map) : Span(unshifted_evdev_key_map); - - if (event.code >= key_map.size()) { - dbgln_if(VIRTIO_DEBUG, "VirtIO::Input: Unknown EV_KEY event code: {:#x}", event.code); - return; - } - - raw_key_event.code_entry = key_map[event.code]; - - KeyEvent key_event { - .key = raw_key_event.code_entry.key_code, - .map_entry_index = raw_key_event.code_entry.map_entry_index, - .scancode = raw_key_event.scancode, - .flags = raw_key_event.is_press() ? static_cast(Is_Press) : static_cast(0), - }; - - if (m_keyboard_device->num_lock_on() && (m_keyboard_device->modifiers() & Mod_Shift) == 0) { - if (key_event.scancode >= KEY_KP7 && raw_key_event.scancode <= KEY_KPDOT) { - auto index = key_event.scancode - KEY_KP7; - static constexpr auto numpad_key_map = to_array({ - { Key_7, 0x08 }, - { Key_8, 0x09 }, - { Key_9, 0x0a }, - { Key_Invalid, 0xff }, - { Key_4, 0x05 }, - { Key_5, 0x06 }, - { Key_6, 0x07 }, - { Key_Invalid, 0xff }, - { Key_1, 0x02 }, - { Key_2, 0x03 }, - { Key_3, 0x04 }, - { Key_0, 0x0b }, - { Key_Period, 0x34 }, - }); - - if (numpad_key_map[index].key_code != Key_Invalid) { - key_event.key = numpad_key_map[index].key_code; - key_event.map_entry_index = numpad_key_map[index].map_entry_index; - } - } - } - - m_keyboard_device->handle_input_event(key_event); - break; - } - break; - - case EV_REL: { - if (event.code == REL_X) { - m_current_mouse_packet.is_relative = true; - m_current_mouse_packet.x = static_cast(event.value); - } else if (event.code == REL_Y) { - m_current_mouse_packet.is_relative = true; - m_current_mouse_packet.y = -static_cast(event.value); - } else if (event.code == REL_WHEEL) { - m_current_mouse_packet.z = -static_cast(event.value); - } else { - dbgln_if(VIRTIO_DEBUG, "VirtIO::Input: Unknown EV_REL event code: {:#x}", event.code); - } - break; - } - - case EV_ABS: { - if (event.code == ABS_X) { - m_current_mouse_packet.is_relative = false; - m_current_mouse_packet.x = static_cast((event.value - m_abs_min) * 0xffff / (m_abs_max - m_abs_min)); - } else if (event.code == ABS_Y) { - m_current_mouse_packet.is_relative = false; - m_current_mouse_packet.y = static_cast((event.value - m_abs_min) * 0xffff / (m_abs_max - m_abs_min)); - } else { - dbgln_if(VIRTIO_DEBUG, "VirtIO::Input: Unknown EV_ABS event code: {:#x}", event.code); - } - break; - } - - default: - dbgln_if(VIRTIO_DEBUG, "VirtIO::Input: Unknown event type: {:#x}", event.type); - break; - } -} - -} diff --git a/Kernel/Devices/HID/VirtIO/Input.h b/Kernel/Devices/HID/VirtIO/Input.h deleted file mode 100644 index d8411911582..00000000000 --- a/Kernel/Devices/HID/VirtIO/Input.h +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright (c) 2024, Sönke Holz - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include -#include -#include - -namespace Kernel::VirtIO { - -struct VirtIOInputEvent; - -class Input final - : public AtomicRefCounted - , public VirtIO::Device { -public: - static NonnullLockRefPtr must_create_for_pci_instance(PCI::DeviceIdentifier const&); - ~Input() override = default; - - ErrorOr initialize_virtio_resources() override; - -private: - static constexpr u16 EVENTQ = 0; - static constexpr u16 STATUSQ = 1; - - StringView class_name() const override { return "VirtIOInput"sv; } - explicit Input(NonnullOwnPtr); - ErrorOr handle_device_config_change() override; - void handle_queue_update(u16 queue_index) override; - - void handle_event(VirtIOInputEvent const&); - - OwnPtr m_event_buffer_region; - - NonnullRefPtr m_mouse_device; - MousePacket m_current_mouse_packet; - - NonnullRefPtr m_keyboard_device; - - EntropySource m_entropy_source; - - u32 m_abs_min { 0 }; - u32 m_abs_max { 0xffff }; -}; - -} diff --git a/Kernel/Devices/KCOVDevice.cpp b/Kernel/Devices/KCOVDevice.cpp deleted file mode 100644 index 5e48b5a7eaf..00000000000 --- a/Kernel/Devices/KCOVDevice.cpp +++ /dev/null @@ -1,87 +0,0 @@ -/* - * Copyright (c) 2021, Patrick Meyer - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include -#include -#include - -#include - -namespace Kernel { - -UNMAP_AFTER_INIT NonnullLockRefPtr KCOVDevice::must_create() -{ - auto kcov_device_or_error = DeviceManagement::try_create_device(); - // FIXME: Find a way to propagate errors - VERIFY(!kcov_device_or_error.is_error()); - return kcov_device_or_error.release_value(); -} - -UNMAP_AFTER_INIT KCOVDevice::KCOVDevice() - : BlockDevice(30, 0) -{ - dbgln("KCOVDevice created"); -} - -ErrorOr> KCOVDevice::open(int options) -{ - auto& proc = Process::current(); - auto* kcov_instance = proc.kcov_instance(); - if (kcov_instance != nullptr) - return EBUSY; // This process already open()ed the kcov device - - proc.set_kcov_instance(new KCOVInstance(proc.pid())); - return Device::open(options); -} - -ErrorOr KCOVDevice::ioctl(OpenFileDescription&, unsigned request, Userspace arg) -{ - auto& proc = Process::current(); - auto* thread = Thread::current(); - auto* kcov_instance = proc.kcov_instance(); - if (kcov_instance == nullptr) { - VERIFY(!Process::is_kcov_busy()); // No thread should be tracing at this point. - return ENXIO; // This proc hasn't opened the kcov dev yet - } - - SpinlockLocker locker(kcov_instance->spinlock()); - switch (request) { - case KCOV_SETBUFSIZE: - if (Process::is_kcov_busy()) - // Buffer is shared among all proc threads, hence we need to check if any of them - // are currently tracing. If so, don't mess with the buffer. - return EBUSY; - return kcov_instance->buffer_allocate((FlatPtr)arg.unsafe_userspace_ptr()); - case KCOV_ENABLE: - if (!kcov_instance->has_buffer()) - return ENOBUFS; - thread->m_kcov_enabled = true; - return {}; - case KCOV_DISABLE: { - thread->m_kcov_enabled = false; - return {}; - } - default: - return EINVAL; - } -} - -ErrorOr> KCOVDevice::vmobject_for_mmap(Process& process, Memory::VirtualRange const&, u64&, bool) -{ - auto* kcov_instance = process.kcov_instance(); - VERIFY(kcov_instance != nullptr); // Should have happened on fd open() - - if (!kcov_instance->vmobject()) - return ENOBUFS; // mmaped, before KCOV_SETBUFSIZE - - return *kcov_instance->vmobject(); -} - -} diff --git a/Kernel/Devices/KCOVDevice.h b/Kernel/Devices/KCOVDevice.h deleted file mode 100644 index 86409f4a6fe..00000000000 --- a/Kernel/Devices/KCOVDevice.h +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright (c) 2021, Patrick Meyer - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include - -namespace Kernel { -class KCOVDevice final : public BlockDevice { - friend class DeviceManagement; - -public: - static NonnullLockRefPtr must_create(); - static void free_thread(); - static void free_process(); - - // ^File - ErrorOr> vmobject_for_mmap(Process&, Memory::VirtualRange const&, u64& offset, bool shared) override; - virtual ErrorOr> open(int options) override; - -protected: - KCOVDevice(); - - virtual StringView class_name() const override { return "KCOVDevice"sv; } - - virtual bool can_read(OpenFileDescription const&, u64) const override final { return true; } - virtual bool can_write(OpenFileDescription const&, u64) const override final { return true; } - virtual void start_request(AsyncBlockDeviceRequest& request) override final { request.complete(AsyncDeviceRequest::Failure); } - virtual ErrorOr read(OpenFileDescription&, u64, UserOrKernelBuffer&, size_t) override { return EINVAL; } - virtual ErrorOr write(OpenFileDescription&, u64, UserOrKernelBuffer const&, size_t) override { return EINVAL; } - virtual ErrorOr ioctl(OpenFileDescription&, unsigned request, Userspace arg) override; -}; - -} diff --git a/Kernel/Devices/KCOVInstance.cpp b/Kernel/Devices/KCOVInstance.cpp deleted file mode 100644 index 2dc2c446865..00000000000 --- a/Kernel/Devices/KCOVInstance.cpp +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright (c) 2021, Patrick Meyer - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include - -namespace Kernel { - -KCOVInstance::KCOVInstance(ProcessID pid) -{ - m_pid = pid; -} - -ErrorOr KCOVInstance::buffer_allocate(size_t buffer_size_in_entries) -{ - if (buffer_size_in_entries < 2 || buffer_size_in_entries > KCOV_MAX_ENTRIES) - return EINVAL; - - // first entry contains index of last PC - m_buffer_size_in_entries = buffer_size_in_entries - 1; - m_buffer_size_in_bytes = TRY(Memory::page_round_up(buffer_size_in_entries * KCOV_ENTRY_SIZE)); - - // one single vmobject is representing the buffer - // - we allocate one kernel region using that vmobject - // - when an mmap call comes in, we allocate another userspace region, - // backed by the same vmobject - m_vmobject = TRY(Memory::AnonymousVMObject::try_create_with_size(m_buffer_size_in_bytes, AllocationStrategy::AllocateNow)); - - auto region_name = TRY(KString::formatted("kcov_{}", m_pid)); - m_kernel_region = TRY(MM.allocate_kernel_region_with_vmobject( - *m_vmobject, m_buffer_size_in_bytes, region_name->view(), - Memory::Region::Access::ReadWrite)); - - m_buffer = (u64*)m_kernel_region->vaddr().as_ptr(); - return {}; -} - -void KCOVInstance::buffer_add_pc(u64 pc) -{ - auto idx = (u64)m_buffer[0]; - if (idx >= m_buffer_size_in_entries) { - // the buffer is already full - return; - } - - m_buffer[idx + 1] = pc; - m_buffer[0] = idx + 1; -} - -} diff --git a/Kernel/Devices/KCOVInstance.h b/Kernel/Devices/KCOVInstance.h deleted file mode 100644 index baf13001e51..00000000000 --- a/Kernel/Devices/KCOVInstance.h +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright (c) 2021, Patrick Meyer - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include - -namespace Kernel { - -#define KCOV_MAX_ENTRIES (10 * 1024 * 1024) - -/* - * 1. When a thread opens /dev/kcov for the first time, a KCOVInstance is - * allocated and tracked via an OwnPtr on the Kernel::Process object. - * 2. When a thread in the same process then uses the KCOV_SETBUFSIZE ioctl - * on the block device, a Memory::Region is allocated and tracked via an - * OwnPtr on the KCOVInstance. - * 3. When a thread in the same process then uses the KCOV_ENABLE ioctl on - * the block device, a flag is set in the Thread object and __sanitizer_cov_trace_pc - * will start recording this threads visited code paths . - * 3. When the same thread then uses the KCOV_DISABLE ioctl on the block device, - * a flag is unset in the Thread object and __sanitizer_cov_trace_pc will - * no longer record this threads visited code paths. - * 4. When the Process dies, the KCOVInstance and Memory::Region are GCed. - */ -class KCOVInstance final { -public: - explicit KCOVInstance(ProcessID pid); - - ErrorOr buffer_allocate(size_t buffer_size_in_entries); - bool has_buffer() const { return m_buffer != nullptr; } - void buffer_add_pc(u64 pc); - - Memory::VMObject* vmobject() { return m_vmobject; } - - Spinlock& spinlock() { return m_lock; } - -private: - ProcessID m_pid { 0 }; - u64 m_buffer_size_in_entries { 0 }; - size_t m_buffer_size_in_bytes { 0 }; - kcov_pc_t* m_buffer { nullptr }; - LockRefPtr m_vmobject; - - // Here to ensure it's not garbage collected at the end of open() - OwnPtr m_kernel_region; - - Spinlock m_lock {}; -}; - -} diff --git a/Kernel/Devices/Loop/LoopDevice.cpp b/Kernel/Devices/Loop/LoopDevice.cpp deleted file mode 100644 index ebbd3911b05..00000000000 --- a/Kernel/Devices/Loop/LoopDevice.cpp +++ /dev/null @@ -1,131 +0,0 @@ -/* - * Copyright (c) 2024, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include - -namespace Kernel { - -static Singleton> s_all_instances; -static Atomic s_loop_device_id { 0 }; - -SpinlockProtected& LoopDevice::all_instances() -{ - return s_all_instances; -} - -void LoopDevice::remove(Badge) -{ - LoopDevice::all_instances().with([&](auto&) { - m_list_node.remove(); - }); -} - -bool LoopDevice::unref() const -{ - bool did_hit_zero = LoopDevice::all_instances().with([&](auto&) { - if (deref_base()) - return false; - const_cast(*this).revoke_weak_ptrs(); - return true; - }); - if (did_hit_zero) { - const_cast(*this).will_be_destroyed(); - delete this; - } - return did_hit_zero; -} - -ErrorOr> LoopDevice::create_with_file_description(OpenFileDescription& description) -{ - auto custody = description.custody(); - if (!custody) - return Error::from_errno(EINVAL); - - // NOTE: We only support regular inode files, because anything else - // just doesn't make sense (could be non-seekable files or char devices) - if (!custody->inode().metadata().is_regular_file()) - return Error::from_errno(ENOTSUP); - - // NOTE: We could technically allow the user to create a loop device from a file - // on SysFS, ProcFS, etc, but the added value from allowing this is non-existent - // because there's simply no good reason to ever do this kind of operation. - // - // If you need more justification, some filesystems (like ProcFS, SysFS, etc) don't - // support keeping Inode objects and instead keep re-creating them - this has serious - // consequences on the integrity of loop devices, as we rely on the backing Inode to - // be consistent while the LoopDevice is alive. - if (!custody->inode().fs().supports_backing_loop_devices()) - return Error::from_errno(ENOTSUP); - - return TRY(LoopDevice::all_instances().with([custody](auto& all_instances_list) -> ErrorOr> { - NonnullRefPtr device = *TRY(DeviceManagement::try_create_device(*custody, s_loop_device_id.fetch_add(1))); - all_instances_list.append(*device); - return device; - })); -} - -void LoopDevice::start_request(AsyncBlockDeviceRequest& request) -{ - auto work_item_creation_result = g_io_work->try_queue([this, &request]() { - if (request.request_type() == AsyncBlockDeviceRequest::RequestType::Read) { - auto result = m_backing_custody->inode().read_bytes(request.block_index() * request.block_size(), request.buffer_size(), request.buffer(), nullptr); - if (result.is_error()) - request.complete(AsyncDeviceRequest::Failure); - else - request.complete(AsyncDeviceRequest::Success); - return; - } else if (request.request_type() == AsyncBlockDeviceRequest::RequestType::Write) { - auto result = m_backing_custody->inode().write_bytes(request.block_index() * request.block_size(), request.buffer_size(), request.buffer(), nullptr); - if (result.is_error()) - request.complete(AsyncDeviceRequest::Failure); - else - request.complete(AsyncDeviceRequest::Success); - return; - } - VERIFY_NOT_REACHED(); - }); - if (work_item_creation_result.is_error()) - request.complete(AsyncDeviceRequest::OutOfMemory); -} - -bool LoopDevice::can_read(OpenFileDescription const&, u64) const -{ - return true; -} - -bool LoopDevice::can_write(OpenFileDescription const&, u64) const -{ - return true; -} - -ErrorOr LoopDevice::read(OpenFileDescription& description, u64 offset, UserOrKernelBuffer& buffer, size_t size) -{ - return m_backing_custody->inode().read_bytes(offset, size, buffer, &description); -} - -ErrorOr LoopDevice::write(OpenFileDescription& description, u64 offset, UserOrKernelBuffer const& buffer, size_t size) -{ - return m_backing_custody->inode().write_bytes(offset, size, buffer, &description); -} - -// FIXME: Allow passing different block sizes to the constructor -LoopDevice::LoopDevice(NonnullRefPtr backing_custody, unsigned index) - : BlockDevice(20, index, 512) - , m_backing_custody(backing_custody) - , m_index(index) -{ -} - -ErrorOr LoopDevice::ioctl(OpenFileDescription&, unsigned, Userspace) -{ - return EINVAL; -} - -} diff --git a/Kernel/Devices/Loop/LoopDevice.h b/Kernel/Devices/Loop/LoopDevice.h deleted file mode 100644 index eacfcada09b..00000000000 --- a/Kernel/Devices/Loop/LoopDevice.h +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright (c) 2024, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include -#include - -namespace Kernel { - -class LoopDevice final : public BlockDevice { - friend class DeviceManagement; - -public: - virtual bool unref() const override; - virtual ~LoopDevice() = default; - - void remove(Badge); - static ErrorOr> create_with_file_description(OpenFileDescription&); - - virtual StringView class_name() const override { return "LoopDevice"sv; } - - virtual ErrorOr read(OpenFileDescription&, u64, UserOrKernelBuffer&, size_t) override; - virtual ErrorOr write(OpenFileDescription&, u64, UserOrKernelBuffer const&, size_t) override; - virtual bool can_read(OpenFileDescription const&, u64) const override; - virtual bool can_write(OpenFileDescription const&, u64) const override; - virtual ErrorOr ioctl(OpenFileDescription&, unsigned request, Userspace arg) override; - - virtual bool is_loop_device() const override { return true; } - - unsigned index() const { return m_index; } - - Inode const& inode() const { return m_backing_custody->inode(); } - Inode& inode() { return m_backing_custody->inode(); } - - Custody const& custody() const { return *m_backing_custody; } - Custody& custody() { return *m_backing_custody; } - -private: - virtual void start_request(AsyncBlockDeviceRequest&) override; - - LoopDevice(NonnullRefPtr, unsigned index); - - NonnullRefPtr const m_backing_custody; - unsigned const m_index { 0 }; - - mutable IntrusiveListNode> m_list_node; - -public: - using List = IntrusiveList<&LoopDevice::m_list_node>; - - static SpinlockProtected& all_instances(); -}; - -} diff --git a/Kernel/Devices/PCISerialDevice.cpp b/Kernel/Devices/PCISerialDevice.cpp deleted file mode 100644 index db64084c45a..00000000000 --- a/Kernel/Devices/PCISerialDevice.cpp +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright (c) 2021, Idan Horowitz - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include - -namespace Kernel { - -static SerialDevice* s_the = nullptr; - -UNMAP_AFTER_INIT void PCISerialDevice::detect() -{ - size_t current_device_minor = 68; - MUST(PCI::enumerate([&](PCI::DeviceIdentifier const& device_identifier) { - for (auto& board_definition : board_definitions) { - if (board_definition.device_id != device_identifier.hardware_id()) - continue; - - auto registers_io_window = IOWindow::create_for_pci_device_bar(device_identifier, static_cast(board_definition.pci_bar)).release_value_but_fixme_should_propagate_errors(); - auto first_offset_registers_io_window = registers_io_window->create_from_io_window_with_offset(board_definition.first_offset).release_value_but_fixme_should_propagate_errors(); - - for (size_t i = 0; i < board_definition.port_count; i++) { - auto port_registers_io_window = first_offset_registers_io_window->create_from_io_window_with_offset(board_definition.port_size * i).release_value_but_fixme_should_propagate_errors(); - auto serial_device = new SerialDevice(move(port_registers_io_window), current_device_minor++); - if (board_definition.baud_rate != SerialDevice::Baud::Baud38400) // non-default baud - serial_device->set_baud(board_definition.baud_rate); - - // If this is the first port of the first pci serial device, store it as the debug PCI serial port (TODO: Make this configurable somehow?) - if (!is_available()) - s_the = serial_device; - // NOTE: We intentionally leak the reference to serial_device here. - } - - dmesgln("PCISerialDevice: Found {} @ {}", board_definition.name, device_identifier.address()); - return; - } - })); -} - -SerialDevice& PCISerialDevice::the() -{ - VERIFY(s_the); - return *s_the; -} - -bool PCISerialDevice::is_available() -{ - return s_the; -} - -} diff --git a/Kernel/Devices/PCISerialDevice.h b/Kernel/Devices/PCISerialDevice.h deleted file mode 100644 index 39062ef38f2..00000000000 --- a/Kernel/Devices/PCISerialDevice.h +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright (c) 2021, Idan Horowitz - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include - -namespace Kernel { - -class PCISerialDevice { -public: - static void detect(); - static SerialDevice& the(); - static bool is_available(); - -private: - struct BoardDefinition { - PCI::HardwareID device_id; - StringView name; - u32 port_count { 0 }; - u32 pci_bar { 0 }; - u32 first_offset { 0 }; - u32 port_size { 0 }; - SerialDevice::Baud baud_rate { SerialDevice::Baud::Baud38400 }; - }; - - static constexpr BoardDefinition board_definitions[] = { - { { PCI::VendorID::RedHat, 0x0002 }, "QEMU PCI 16550A"sv, 1, 0, 0, 8, SerialDevice::Baud::Baud115200 }, - { { PCI::VendorID::RedHat, 0x0003 }, "QEMU PCI Dual-port 16550A"sv, 2, 0, 0, 8, SerialDevice::Baud::Baud115200 }, - { { PCI::VendorID::RedHat, 0x0004 }, "QEMU PCI Quad-port 16550A"sv, 4, 0, 0, 8, SerialDevice::Baud::Baud115200 }, - { { PCI::VendorID::WCH, 0x2273 }, "WCH CH351"sv, 2, 0, 0, 8, SerialDevice::Baud::Baud115200 }, - { { PCI::VendorID::WCH, 0x3253 }, "WCH CH382 2S"sv, 2, 0, 0xC0, 8, SerialDevice::Baud::Baud115200 } - }; -}; - -} diff --git a/Kernel/Devices/Serial/VirtIO/Console.cpp b/Kernel/Devices/Serial/VirtIO/Console.cpp deleted file mode 100644 index 7bbb5f1e0b9..00000000000 --- a/Kernel/Devices/Serial/VirtIO/Console.cpp +++ /dev/null @@ -1,239 +0,0 @@ -/* - * Copyright (c) 2021, the SerenityOS developers. - * Copyright (c) 2021, Kyle Pereira - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include - -namespace Kernel::VirtIO { - -unsigned Console::next_device_id = 0; - -UNMAP_AFTER_INIT NonnullLockRefPtr Console::must_create_for_pci_instance(PCI::DeviceIdentifier const& pci_device_identifier) -{ - auto pci_transport_link = MUST(PCIeTransportLink::create(pci_device_identifier)); - return adopt_lock_ref_if_nonnull(new (nothrow) Console(move(pci_transport_link))).release_nonnull(); -} - -UNMAP_AFTER_INIT ErrorOr Console::initialize_virtio_resources() -{ - TRY(Device::initialize_virtio_resources()); - auto const* cfg = TRY(transport_entity().get_config(VirtIO::ConfigurationType::Device)); - TRY(negotiate_features([&](u64 supported_features) { - u64 negotiated = 0; - if (is_feature_set(supported_features, VIRTIO_CONSOLE_F_SIZE)) - dbgln("VirtIO::Console: Console size is not yet supported!"); - if (is_feature_set(supported_features, VIRTIO_CONSOLE_F_MULTIPORT)) - negotiated |= VIRTIO_CONSOLE_F_MULTIPORT; - return negotiated; - })); - u32 max_nr_ports = 0; - u16 cols = 0, rows = 0; - transport_entity().read_config_atomic([&]() { - if (is_feature_accepted(VIRTIO_CONSOLE_F_SIZE)) { - cols = transport_entity().config_read16(*cfg, 0x0); - rows = transport_entity().config_read16(*cfg, 0x2); - } - if (is_feature_accepted(VIRTIO_CONSOLE_F_MULTIPORT)) { - max_nr_ports = transport_entity().config_read32(*cfg, 0x4); - m_ports.resize(max_nr_ports); - } - }); - dbgln("VirtIO::Console: cols: {}, rows: {}, max nr ports {}", cols, rows, max_nr_ports); - // Base receiveq/transmitq for port0 + optional control queues and 2 per every additional port - TRY(setup_queues(2 + max_nr_ports > 0 ? 2 + 2 * max_nr_ports : 0)); - finish_init(); - - if (is_feature_accepted(VIRTIO_CONSOLE_F_MULTIPORT)) { - setup_multiport(); - } else { - auto port = TRY(DeviceManagement::the().try_create_device(0u, *this)); - port->init_receive_buffer({}); - m_ports.append(port); - } - return {}; -} - -UNMAP_AFTER_INIT Console::Console(NonnullOwnPtr transport_entity) - : VirtIO::Device(move(transport_entity)) - , m_device_id(next_device_id++) -{ -} - -ErrorOr Console::handle_device_config_change() -{ - dbgln("VirtIO::Console: Handle device config change"); - return {}; -} - -void Console::handle_queue_update(u16 queue_index) -{ - dbgln_if(VIRTIO_DEBUG, "VirtIO::Console: Handle queue update {}", queue_index); - - if (queue_index == CONTROL_RECEIVEQ) { - SpinlockLocker ringbuffer_lock(m_control_receive_buffer->lock()); - auto& queue = get_queue(CONTROL_RECEIVEQ); - SpinlockLocker queue_lock(queue.lock()); - size_t used; - QueueChain popped_chain = queue.pop_used_buffer_chain(used); - - while (!popped_chain.is_empty()) { - popped_chain.for_each([&](auto addr, auto) { - auto offset = addr.as_ptr() - m_control_receive_buffer->start_of_region().as_ptr(); - auto* message = reinterpret_cast(m_control_receive_buffer->vaddr().offset(offset).as_ptr()); - process_control_message(*message); - }); - - supply_chain_and_notify(CONTROL_RECEIVEQ, popped_chain); - popped_chain = queue.pop_used_buffer_chain(used); - } - } else if (queue_index == CONTROL_TRANSMITQ) { - SpinlockLocker ringbuffer_lock(m_control_transmit_buffer->lock()); - auto& queue = get_queue(CONTROL_TRANSMITQ); - SpinlockLocker queue_lock(queue.lock()); - size_t used; - QueueChain popped_chain = queue.pop_used_buffer_chain(used); - auto number_of_messages = 0; - do { - popped_chain.for_each([this](PhysicalAddress address, size_t length) { - m_control_transmit_buffer->reclaim_space(address, length); - }); - popped_chain.release_buffer_slots_to_queue(); - popped_chain = queue.pop_used_buffer_chain(used); - number_of_messages++; - } while (!popped_chain.is_empty()); - m_control_wait_queue.wake_n(number_of_messages); - } else { - u32 port_index = queue_index < 2 ? 0 : (queue_index - 2) / 2; - if (port_index >= m_ports.size() || !m_ports.at(port_index)) { - dbgln("Invalid queue_index {}", queue_index); - return; - } - m_ports.at(port_index)->handle_queue_update({}, queue_index); - } -} - -void Console::setup_multiport() -{ - m_control_receive_buffer = Memory::RingBuffer::try_create("VirtIOConsole control receive queue"sv, CONTROL_BUFFER_SIZE).release_value_but_fixme_should_propagate_errors(); - m_control_transmit_buffer = Memory::RingBuffer::try_create("VirtIOConsole control transmit queue"sv, CONTROL_BUFFER_SIZE).release_value_but_fixme_should_propagate_errors(); - - auto& queue = get_queue(CONTROL_RECEIVEQ); - SpinlockLocker queue_lock(queue.lock()); - QueueChain chain(queue); - auto offset = 0ul; - - while (offset < CONTROL_BUFFER_SIZE) { - auto buffer_start = m_control_receive_buffer->start_of_region().offset(offset); - auto did_add_buffer = chain.add_buffer_to_chain(buffer_start, CONTROL_MESSAGE_SIZE, BufferType::DeviceWritable); - VERIFY(did_add_buffer); - offset += CONTROL_MESSAGE_SIZE; - supply_chain_and_notify(CONTROL_RECEIVEQ, chain); - } - - ControlMessage ready_event { - .id = 0, // Unused - .event = (u16)ControlEvent::DeviceReady, - .value = (u16)ControlMessage::Status::Success - }; - write_control_message(ready_event); -} - -void Console::process_control_message(ControlMessage message) -{ - switch (message.event) { - case (u16)ControlEvent::DeviceAdd: { - // FIXME: Do something sanely here if we can't allocate a work queue? - MUST(g_io_work->try_queue([message, this]() -> void { - u32 id = message.id; - if (id >= m_ports.size()) { - dbgln("Device provided an invalid port number {}. max_nr_ports: {}", id, m_ports.size()); - return; - } - if (!m_ports.at(id).is_null()) { - dbgln("Device tried to add port {} which was already added!", id); - return; - } - - auto port = MUST(DeviceManagement::the().try_create_device(id, *this)); - port->init_receive_buffer({}); - m_ports.at(id) = port; - - ControlMessage ready_event { - .id = static_cast(id), - .event = (u16)ControlEvent::PortReady, - .value = (u16)ControlMessage::Status::Success - }; - write_control_message(ready_event); - })); - - break; - } - case (u16)ControlEvent::ConsolePort: - case (u16)ControlEvent::PortOpen: { - if (message.id >= m_ports.size()) { - dbgln("Device provided an invalid port number {}. max_nr_ports: {}", message.id, m_ports.size()); - return; - } - if (m_ports.at(message.id).is_null()) { - dbgln("Device tried to open port {} which was not added!", message.id); - return; - } - - if (message.value == (u16)ControlMessage::PortStatus::Open) { - auto is_open = m_ports.at(message.id)->is_open(); - if (!is_open) { - m_ports.at(message.id)->set_open({}, true); - send_open_control_message(message.id, true); - } - } else if (message.value == (u16)ControlMessage::PortStatus::Close) { - m_ports.at(message.id)->set_open({}, false); - } else { - dbgln("Device specified invalid value {}. Must be 0 or 1.", message.value); - } - break; - } - default: - dbgln("Unhandled message event {}!", message.event); - } -} -void Console::write_control_message(ControlMessage message) -{ - SpinlockLocker ringbuffer_lock(m_control_transmit_buffer->lock()); - - PhysicalAddress start_of_chunk; - size_t length_of_chunk; - - auto data = UserOrKernelBuffer::for_kernel_buffer((u8*)&message); - while (!m_control_transmit_buffer->copy_data_in(data, 0, sizeof(message), start_of_chunk, length_of_chunk)) { - ringbuffer_lock.unlock(); - m_control_wait_queue.wait_forever(); - ringbuffer_lock.lock(); - } - - auto& queue = get_queue(CONTROL_TRANSMITQ); - SpinlockLocker queue_lock(queue.lock()); - QueueChain chain(queue); - - bool did_add_buffer = chain.add_buffer_to_chain(start_of_chunk, length_of_chunk, BufferType::DeviceReadable); - VERIFY(did_add_buffer); - - supply_chain_and_notify(CONTROL_TRANSMITQ, chain); -} - -void Console::send_open_control_message(unsigned port_number, bool open) -{ - ControlMessage port_open { - .id = static_cast(port_number), - .event = (u16)ControlEvent::PortOpen, - .value = open - }; - write_control_message(port_open); -} -} diff --git a/Kernel/Devices/Serial/VirtIO/Console.h b/Kernel/Devices/Serial/VirtIO/Console.h deleted file mode 100644 index ca1531a9ab1..00000000000 --- a/Kernel/Devices/Serial/VirtIO/Console.h +++ /dev/null @@ -1,80 +0,0 @@ -/* - * Copyright (c) 2021, Kyle Pereira - * Copyright (c) 2021, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include - -namespace Kernel::VirtIO { -class Console - : public VirtIO::Device - , public AtomicRefCounted { - friend VirtIO::ConsolePort; - -public: - static NonnullLockRefPtr must_create_for_pci_instance(PCI::DeviceIdentifier const&); - virtual ~Console() override = default; - - unsigned device_id() const - { - return m_device_id; - } - - virtual ErrorOr initialize_virtio_resources() override; - -private: - virtual StringView class_name() const override { return "VirtIOConsole"sv; } - explicit Console(NonnullOwnPtr); - enum class ControlEvent : u16 { - DeviceReady = 0, - DeviceAdd = 1, - PortReady = 3, - ConsolePort = 4, - PortOpen = 6, - }; - struct [[gnu::packed]] ControlMessage { - u32 id; - u16 event; - u16 value; - - enum class Status : u16 { - Success = 1, - Failure = 0 - }; - - enum class PortStatus : u16 { - Open = 1, - Close = 0 - }; - }; - - constexpr static u16 CONTROL_RECEIVEQ = 2; - constexpr static u16 CONTROL_TRANSMITQ = 3; - constexpr static size_t CONTROL_MESSAGE_SIZE = sizeof(ControlMessage); - constexpr static size_t CONTROL_BUFFER_SIZE = CONTROL_MESSAGE_SIZE * 32; - - virtual ErrorOr handle_device_config_change() override; - virtual void handle_queue_update(u16 queue_index) override; - - Vector> m_ports; - void setup_multiport(); - void process_control_message(ControlMessage message); - void write_control_message(ControlMessage message); - void send_open_control_message(unsigned port_number, bool open); - - unsigned m_device_id; - - OwnPtr m_control_transmit_buffer; - OwnPtr m_control_receive_buffer; - - WaitQueue m_control_wait_queue; - - static unsigned next_device_id; -}; -} diff --git a/Kernel/Devices/Serial/VirtIO/ConsolePort.cpp b/Kernel/Devices/Serial/VirtIO/ConsolePort.cpp deleted file mode 100644 index b6a3336d927..00000000000 --- a/Kernel/Devices/Serial/VirtIO/ConsolePort.cpp +++ /dev/null @@ -1,172 +0,0 @@ -/* - * Copyright (c) 2021, the SerenityOS developers. - * Copyright (c) 2021, Kyle Pereira - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include - -namespace Kernel::VirtIO { - -unsigned ConsolePort::next_device_id = 0; - -ErrorOr> ConsolePort::try_create(unsigned port, Console& console) -{ - auto receive_buffer = TRY(Memory::RingBuffer::try_create("VirtIO::ConsolePort Receive"sv, RINGBUFFER_SIZE)); - auto transmit_buffer = TRY(Memory::RingBuffer::try_create("VirtIO::ConsolePort Transmit"sv, RINGBUFFER_SIZE)); - return adopt_nonnull_lock_ref_or_enomem(new (nothrow) ConsolePort(port, console, move(receive_buffer), move(transmit_buffer))); -} - -ConsolePort::ConsolePort(unsigned port, VirtIO::Console& console, NonnullOwnPtr receive_buffer, NonnullOwnPtr transmit_buffer) - : CharacterDevice(229, next_device_id++) - , m_receive_buffer(move(receive_buffer)) - , m_transmit_buffer(move(transmit_buffer)) - , m_console(console) - , m_port(port) -{ - m_receive_queue = m_port == 0 ? 0 : m_port * 2 + 2; - m_transmit_queue = m_port == 0 ? 1 : m_port * 2 + 3; -} - -void ConsolePort::init_receive_buffer(Badge) -{ - auto& queue = m_console.get_queue(m_receive_queue); - SpinlockLocker queue_lock(queue.lock()); - QueueChain chain(queue); - - auto buffer_start = m_receive_buffer->start_of_region(); - auto did_add_buffer = chain.add_buffer_to_chain(buffer_start, RINGBUFFER_SIZE, BufferType::DeviceWritable); - VERIFY(did_add_buffer); - m_console.supply_chain_and_notify(m_receive_queue, chain); -} - -void ConsolePort::handle_queue_update(Badge, u16 queue_index) -{ - dbgln_if(VIRTIO_DEBUG, "VirtIO::ConsolePort: Handle queue update for port {}", m_port); - VERIFY(queue_index == m_transmit_queue || queue_index == m_receive_queue); - if (queue_index == m_receive_queue) { - auto& queue = m_console.get_queue(m_receive_queue); - SpinlockLocker queue_lock(queue.lock()); - size_t used; - QueueChain popped_chain = queue.pop_used_buffer_chain(used); - - SpinlockLocker ringbuffer_lock(m_receive_buffer->lock()); - auto used_space_or_error = m_receive_buffer->reserve_space(used); - if (used_space_or_error.is_error()) { - TODO(); - } - auto used_space = used_space_or_error.release_value(); - auto remaining_space = m_receive_buffer->bytes_till_end(); - - // Our algorithm always has only one buffer in the queue. - VERIFY(popped_chain.length() == 1); - VERIFY(!queue.new_data_available()); - popped_chain.release_buffer_slots_to_queue(); - - QueueChain new_chain(queue); - if (remaining_space != 0) { - new_chain.add_buffer_to_chain(used_space.offset(used), remaining_space, BufferType::DeviceWritable); - m_console.supply_chain_and_notify(m_receive_queue, new_chain); - } else { - m_receive_buffer_exhausted = true; - } - - evaluate_block_conditions(); - } else { - SpinlockLocker ringbuffer_lock(m_transmit_buffer->lock()); - auto& queue = m_console.get_queue(m_transmit_queue); - SpinlockLocker queue_lock(queue.lock()); - size_t used; - QueueChain popped_chain = queue.pop_used_buffer_chain(used); - do { - popped_chain.for_each([this](PhysicalAddress address, size_t length) { - m_transmit_buffer->reclaim_space(address, length); - }); - popped_chain.release_buffer_slots_to_queue(); - popped_chain = queue.pop_used_buffer_chain(used); - } while (!popped_chain.is_empty()); - // Unblock any IO tasks that were blocked because can_write() returned false - evaluate_block_conditions(); - } -} - -bool ConsolePort::can_read(OpenFileDescription const&, u64) const -{ - return m_receive_buffer->used_bytes() > 0; -} - -ErrorOr ConsolePort::read(OpenFileDescription& desc, u64, UserOrKernelBuffer& buffer, size_t size) -{ - if (!size) - return 0; - - SpinlockLocker ringbuffer_lock(m_receive_buffer->lock()); - - if (!can_read(desc, size)) - return EAGAIN; - - auto bytes_copied = TRY(m_receive_buffer->copy_data_out(size, buffer)); - m_receive_buffer->reclaim_space(m_receive_buffer->start_of_used(), bytes_copied); - - if (m_receive_buffer_exhausted && m_receive_buffer->used_bytes() == 0) { - auto& queue = m_console.get_queue(m_receive_queue); - SpinlockLocker queue_lock(queue.lock()); - QueueChain new_chain(queue); - new_chain.add_buffer_to_chain(m_receive_buffer->start_of_region(), RINGBUFFER_SIZE, BufferType::DeviceWritable); - m_console.supply_chain_and_notify(m_receive_queue, new_chain); - m_receive_buffer_exhausted = false; - } - - return bytes_copied; -} - -bool ConsolePort::can_write(OpenFileDescription const&, u64) const -{ - return m_console.get_queue(m_transmit_queue).has_free_slots() && m_transmit_buffer->has_space(); -} - -ErrorOr ConsolePort::write(OpenFileDescription& desc, u64, UserOrKernelBuffer const& data, size_t size) -{ - if (!size) - return 0; - - SpinlockLocker ringbuffer_lock(m_transmit_buffer->lock()); - auto& queue = m_console.get_queue(m_transmit_queue); - SpinlockLocker queue_lock(queue.lock()); - - if (!can_write(desc, size)) - return EAGAIN; - - QueueChain chain(queue); - - size_t total_bytes_copied = 0; - do { - PhysicalAddress start_of_chunk; - size_t length_of_chunk; - - if (!m_transmit_buffer->copy_data_in(data, total_bytes_copied, size - total_bytes_copied, start_of_chunk, length_of_chunk)) { - chain.release_buffer_slots_to_queue(); - return EINVAL; - } - - bool did_add_buffer = chain.add_buffer_to_chain(start_of_chunk, length_of_chunk, BufferType::DeviceReadable); - VERIFY(did_add_buffer); - total_bytes_copied += length_of_chunk; - } while (total_bytes_copied < size && can_write(desc, size)); - - m_console.supply_chain_and_notify(m_transmit_queue, chain); - - return total_bytes_copied; -} - -ErrorOr> ConsolePort::open(int options) -{ - if (!m_open) - m_console.send_open_control_message(m_port, true); - - return Device::open(options); -} - -} diff --git a/Kernel/Devices/Serial/VirtIO/ConsolePort.h b/Kernel/Devices/Serial/VirtIO/ConsolePort.h deleted file mode 100644 index ac6f51980a4..00000000000 --- a/Kernel/Devices/Serial/VirtIO/ConsolePort.h +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright (c) 2021, the SerenityOS developers. - * Copyright (c) 2021, Kyle Pereira - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include - -namespace Kernel::VirtIO { - -class Console; - -#define VIRTIO_CONSOLE_F_SIZE (1 << 0) -#define VIRTIO_CONSOLE_F_MULTIPORT (1 << 1) -#define VIRTIO_CONSOLE_F_EMERG_WRITE (1 << 2) - -class ConsolePort - : public CharacterDevice { -public: - static ErrorOr> try_create(unsigned port, VirtIO::Console&); - - void handle_queue_update(Badge, u16 queue_index); - - void set_open(Badge, bool state) { m_open = state; } - bool is_open() const { return m_open; } - - void init_receive_buffer(Badge); - -private: - constexpr static size_t RINGBUFFER_SIZE = 2 * PAGE_SIZE; - - ConsolePort(unsigned port, VirtIO::Console& console, NonnullOwnPtr receive_buffer, NonnullOwnPtr transmit_buffer); - - virtual StringView class_name() const override { return "VirtIOConsolePort"sv; } - - virtual bool can_read(OpenFileDescription const&, u64) const override; - virtual ErrorOr read(OpenFileDescription&, u64, UserOrKernelBuffer&, size_t) override; - virtual bool can_write(OpenFileDescription const&, u64) const override; - virtual ErrorOr write(OpenFileDescription&, u64, UserOrKernelBuffer const&, size_t) override; - virtual ErrorOr> open(int options) override; - - static unsigned next_device_id; - u16 m_receive_queue {}; - u16 m_transmit_queue {}; - - NonnullOwnPtr m_receive_buffer; - NonnullOwnPtr m_transmit_buffer; - - VirtIO::Console& m_console; - unsigned m_port; - - bool m_open { false }; - Atomic m_receive_buffer_exhausted; -}; - -} diff --git a/Kernel/Devices/SerialDevice.cpp b/Kernel/Devices/SerialDevice.cpp deleted file mode 100644 index 25307516838..00000000000 --- a/Kernel/Devices/SerialDevice.cpp +++ /dev/null @@ -1,134 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * Copyright (c) 2021, Idan Horowitz - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include - -namespace Kernel { - -SerialDevice::SerialDevice(NonnullOwnPtr registers_io_window, unsigned minor) - : CharacterDevice(4, minor) - , m_registers_io_window(move(registers_io_window)) -{ - set_interrupts(false); - set_baud(Baud38400); - set_line_control(None, One, EightBits); - set_fifo_control(EnableFIFO | ClearReceiveFIFO | ClearTransmitFIFO | TriggerLevel4); - set_modem_control(RequestToSend | DataTerminalReady); -} - -SerialDevice::~SerialDevice() = default; - -bool SerialDevice::can_read(OpenFileDescription const&, u64) const -{ - return (get_line_status() & DataReady) != 0; -} - -ErrorOr SerialDevice::read(OpenFileDescription&, u64, UserOrKernelBuffer& buffer, size_t size) -{ - if (!size) - return 0; - - SpinlockLocker lock(m_serial_lock); - if (!(get_line_status() & DataReady)) - return 0; - - return buffer.write_buffered<128>(size, [&](Bytes bytes) { - for (auto& byte : bytes) - byte = m_registers_io_window->read8(0); - return bytes.size(); - }); -} - -bool SerialDevice::can_write(OpenFileDescription const&, u64) const -{ - return (get_line_status() & EmptyTransmitterHoldingRegister) != 0; -} - -ErrorOr SerialDevice::write(OpenFileDescription& description, u64, UserOrKernelBuffer const& buffer, size_t size) -{ - if (!size) - return 0; - - SpinlockLocker lock(m_serial_lock); - if (!can_write(description, size)) - return EAGAIN; - - return buffer.read_buffered<128>(size, [&](ReadonlyBytes bytes) { - for (auto const& byte : bytes) - put_char(byte); - return bytes.size(); - }); -} - -void SerialDevice::put_char(char ch) -{ - while ((get_line_status() & EmptyTransmitterHoldingRegister) == 0) - Processor::wait_check(); - - if (ch == '\n' && !m_last_put_char_was_carriage_return) - m_registers_io_window->write8(0, '\r'); - - m_registers_io_window->write8(0, ch); - - m_last_put_char_was_carriage_return = (ch == '\r'); -} - -void SerialDevice::set_interrupts(bool interrupt_enable) -{ - m_interrupt_enable = interrupt_enable; - - m_registers_io_window->write8(1, interrupt_enable); -} - -void SerialDevice::set_baud(Baud baud) -{ - m_baud = baud; - - m_registers_io_window->write8(3, m_registers_io_window->read8(3) | 0x80); // turn on DLAB - m_registers_io_window->write8(0, ((u8)(baud)) & 0xff); // lower half of divisor - m_registers_io_window->write8(1, ((u8)(baud)) >> 2); // lower half of divisor - m_registers_io_window->write8(3, m_registers_io_window->read8(3) & 0x7f); // turn off DLAB -} - -void SerialDevice::set_fifo_control(u8 fifo_control) -{ - m_fifo_control = fifo_control; - m_registers_io_window->write8(2, fifo_control); -} - -void SerialDevice::set_line_control(ParitySelect parity_select, StopBits stop_bits, WordLength word_length) -{ - m_parity_select = parity_select; - m_stop_bits = stop_bits; - m_word_length = word_length; - - m_registers_io_window->write8(3, (m_registers_io_window->read8(3) & ~0x3f) | parity_select | stop_bits | word_length); -} - -void SerialDevice::set_break_enable(bool break_enable) -{ - m_break_enable = break_enable; - - m_registers_io_window->write8(3, m_registers_io_window->read8(3) & (break_enable ? 0xff : 0xbf)); -} - -void SerialDevice::set_modem_control(u8 modem_control) -{ - m_modem_control = modem_control; - - m_registers_io_window->write8(4, modem_control); -} - -u8 SerialDevice::get_line_status() const -{ - return m_registers_io_window->read8(5); -} - -} diff --git a/Kernel/Devices/SerialDevice.h b/Kernel/Devices/SerialDevice.h deleted file mode 100644 index 5802872830b..00000000000 --- a/Kernel/Devices/SerialDevice.h +++ /dev/null @@ -1,136 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include - -namespace Kernel { - -class SerialDevice final : public CharacterDevice { - friend class DeviceManagement; - -public: - static NonnullLockRefPtr must_create(size_t com_number); - - virtual ~SerialDevice() override; - - // ^CharacterDevice - virtual bool can_read(OpenFileDescription const&, u64) const override; - virtual ErrorOr read(OpenFileDescription&, u64, UserOrKernelBuffer&, size_t) override; - virtual bool can_write(OpenFileDescription const&, u64) const override; - virtual ErrorOr write(OpenFileDescription&, u64, UserOrKernelBuffer const&, size_t) override; - - void put_char(char); - - enum InterruptEnable { - LowPowerMode = 0x01 << 5, - SleepMode = 0x01 << 4, - ModemStatusInterrupt = 0x01 << 3, - ReceiverLineStatusInterrupt = 0x01 << 2, - TransmitterHoldingRegisterEmptyInterrupt = 0x01 << 1, - ReceivedDataAvailableInterrupt = 0x01 << 0 - }; - - enum Baud { - Baud50 = 2304, - Baud110 = 1047, - Baud220 = 524, - Baud300 = 384, - Baud600 = 192, - Baud1200 = 96, - Baud2400 = 48, - Baud4800 = 24, - Baud9600 = 12, - Baud19200 = 6, - Baud38400 = 3, - Baud57600 = 2, - Baud115200 = 1 - }; - - enum ParitySelect { - None = 0x00 << 3, - Odd = 0x01 << 3, - Even = 0x03 << 3, - Mark = 0x05 << 3, - Space = 0x07 << 3 - }; - - enum StopBits { - One = 0x00 << 2, - Two = 0x01 << 2 - }; - - enum WordLength { - FiveBits = 0x00, - SixBits = 0x01, - SevenBits = 0x02, - EightBits = 0x03 - }; - - enum FIFOControl { - EnableFIFO = 0x01 << 0, - ClearReceiveFIFO = 0x01 << 1, - ClearTransmitFIFO = 0x01 << 2, - Enable64ByteFIFO = 0x01 << 5, - TriggerLevel1 = 0x00 << 6, - TriggerLevel2 = 0x01 << 6, - TriggerLevel3 = 0x02 << 6, - TriggerLevel4 = 0x03 << 6 - }; - - enum ModemControl { - AutoflowControlEnabled = 0x01 << 5, - LoopbackMode = 0x01 << 4, - AuxiliaryOutput2 = 0x01 << 3, - AuxiliaryOutput1 = 0x01 << 2, - RequestToSend = 0x01 << 1, - DataTerminalReady = 0x01 << 0 - }; - - enum LineStatus { - ErrorInReceivedFIFO = 0x01 << 7, - EmptyDataHoldingRegisters = 0x01 << 6, - EmptyTransmitterHoldingRegister = 0x01 << 5, - BreakInterrupt = 0x01 << 4, - FramingError = 0x01 << 3, - ParityError = 0x01 << 2, - OverrunError = 0x01 << 1, - DataReady = 0x01 << 0 - }; - -private: - SerialDevice(NonnullOwnPtr registers_io_window, unsigned minor); - - friend class PCISerialDevice; - - // ^CharacterDevice - virtual StringView class_name() const override { return "SerialDevice"sv; } - - void initialize(); - void set_interrupts(bool interrupt_enable); - void set_baud(Baud); - void set_fifo_control(u8 fifo_control); - void set_line_control(ParitySelect, StopBits, WordLength); - void set_break_enable(bool break_enable); - void set_modem_control(u8 modem_control); - u8 get_line_status() const; - - mutable NonnullOwnPtr m_registers_io_window; - bool m_interrupt_enable { false }; - u8 m_fifo_control { 0 }; - Baud m_baud { Baud38400 }; - ParitySelect m_parity_select { None }; - StopBits m_stop_bits { One }; - WordLength m_word_length { EightBits }; - bool m_break_enable { false }; - u8 m_modem_control { 0 }; - bool m_last_put_char_was_carriage_return { false }; - Spinlock m_serial_lock {}; -}; - -} diff --git a/Kernel/Devices/Storage/AHCI/ATADevice.cpp b/Kernel/Devices/Storage/AHCI/ATADevice.cpp deleted file mode 100644 index fd1276b4db2..00000000000 --- a/Kernel/Devices/Storage/AHCI/ATADevice.cpp +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright (c) 2021, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include - -namespace Kernel { - -static StorageDevice::LUNAddress convert_ata_address_to_lun_address(AHCIController const& controller, ATA::Address ata_address) -{ - return StorageDevice::LUNAddress { controller.controller_id(), ata_address.port, ata_address.subport }; -} - -ATADevice::ATADevice(AHCIController const& controller, ATA::Address ata_address, u16 capabilities, u16 logical_sector_size, u64 max_addressable_block) - : StorageDevice(convert_ata_address_to_lun_address(controller, ata_address), controller.hardware_relative_controller_id(), logical_sector_size, max_addressable_block) - , m_controller(controller) - , m_ata_address(ata_address) - , m_capabilities(capabilities) -{ -} - -ATADevice::~ATADevice() = default; - -void ATADevice::start_request(AsyncBlockDeviceRequest& request) -{ - VERIFY(m_controller); - m_controller->start_request(m_ata_address, request); -} - -} diff --git a/Kernel/Devices/Storage/AHCI/ATADevice.h b/Kernel/Devices/Storage/AHCI/ATADevice.h deleted file mode 100644 index 507a7b9279f..00000000000 --- a/Kernel/Devices/Storage/AHCI/ATADevice.h +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright (c) 2021, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include - -namespace Kernel { - -class ATADevice : public StorageDevice { -public: - virtual ~ATADevice() override; - - // ^BlockDevice - virtual void start_request(AsyncBlockDeviceRequest&) override; - - u16 ata_capabilites() const { return m_capabilities; } - ATA::Address const& ata_address() const { return m_ata_address; } - -protected: - ATADevice(AHCIController const&, ATA::Address, u16, u16, u64); - - // FIXME: Add proper locking to ensure hotplug can work. - LockRefPtr m_controller; - ATA::Address const m_ata_address; - u16 const m_capabilities; -}; - -} diff --git a/Kernel/Devices/Storage/AHCI/ATADiskDevice.cpp b/Kernel/Devices/Storage/AHCI/ATADiskDevice.cpp deleted file mode 100644 index c81c518589f..00000000000 --- a/Kernel/Devices/Storage/AHCI/ATADiskDevice.cpp +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright (c) 2021, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include - -namespace Kernel { - -NonnullLockRefPtr ATADiskDevice::create(AHCIController const& controller, ATA::Address ata_address, u16 capabilities, u16 logical_sector_size, u64 max_addressable_block) -{ - auto disk_device_or_error = DeviceManagement::try_create_device(controller, ata_address, capabilities, logical_sector_size, max_addressable_block); - // FIXME: Find a way to propagate errors - VERIFY(!disk_device_or_error.is_error()); - return disk_device_or_error.release_value(); -} - -ATADiskDevice::ATADiskDevice(AHCIController const& controller, ATA::Address ata_address, u16 capabilities, u16 logical_sector_size, u64 max_addressable_block) - : ATADevice(controller, ata_address, capabilities, logical_sector_size, max_addressable_block) -{ -} - -ATADiskDevice::~ATADiskDevice() = default; - -StringView ATADiskDevice::class_name() const -{ - return "ATADiskDevice"sv; -} - -} diff --git a/Kernel/Devices/Storage/AHCI/ATADiskDevice.h b/Kernel/Devices/Storage/AHCI/ATADiskDevice.h deleted file mode 100644 index 3a656a48124..00000000000 --- a/Kernel/Devices/Storage/AHCI/ATADiskDevice.h +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright (c) 2021, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include - -namespace Kernel { - -class IDEController; -class ATADiskDevice final : public ATADevice { - friend class IDEController; - friend class DeviceManagement; - -public: - static NonnullLockRefPtr create(AHCIController const&, ATA::Address, u16 capabilities, u16 logical_sector_size, u64 max_addressable_block); - virtual ~ATADiskDevice() override; - - // ^StorageDevice - virtual CommandSet command_set() const override { return CommandSet::ATA; } - -private: - ATADiskDevice(AHCIController const&, ATA::Address, u16, u16, u64); - - // ^DiskDevice - virtual StringView class_name() const override; -}; - -} diff --git a/Kernel/Devices/Storage/AHCI/Controller.cpp b/Kernel/Devices/Storage/AHCI/Controller.cpp deleted file mode 100644 index 053febde2ca..00000000000 --- a/Kernel/Devices/Storage/AHCI/Controller.cpp +++ /dev/null @@ -1,228 +0,0 @@ -/* - * Copyright (c) 2021-2022, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace Kernel { - -UNMAP_AFTER_INIT ErrorOr> AHCIController::initialize(PCI::DeviceIdentifier const& pci_device_identifier) -{ - auto controller = adopt_ref_if_nonnull(new (nothrow) AHCIController(pci_device_identifier)).release_nonnull(); - TRY(controller->initialize_hba(pci_device_identifier)); - return controller; -} - -ErrorOr AHCIController::reset() -{ - dmesgln_pci(*this, "{}: AHCI controller reset", device_identifier().address()); - { - SpinlockLocker locker(m_hba_control_lock); - hba().control_regs.ghc = 1; - - dbgln_if(AHCI_DEBUG, "{}: AHCI Controller reset", device_identifier().address()); - - full_memory_barrier(); - size_t retry = 0; - - // Note: The HBA is locked or hung if we waited more than 1 second! - while (true) { - if (retry > 1000) - return Error::from_errno(ETIMEDOUT); - if (!(hba().control_regs.ghc & 1)) - break; - microseconds_delay(1000); - retry++; - } - // Note: Turn on AHCI HBA and Global HBA Interrupts. - full_memory_barrier(); - hba().control_regs.ghc = (1 << 31) | (1 << 1); - full_memory_barrier(); - } - - // Note: According to the AHCI spec the PI register indicates which ports are exposed by the HBA. - // It is loaded by the BIOS. It indicates which ports that the HBA supports are available for software to use. - // For example, on an HBA that supports 6 ports as indicated in CAP.NP, only ports 1 and 3 could be available, - // with ports 0, 2, 4, and 5 being unavailable. - // Which means that even without clearing the AHCI ports array, we are never able to encounter - // a case that we would have stale left-over ports in there. We still clear the array - // for the sake of clarity and completeness, as it doesn't harm anything anyway. - m_ports.fill({}); - - auto implemented_ports = AHCI::MaskedBitField((u32 volatile&)(hba().control_regs.pi)); - for (auto index : implemented_ports.to_vector()) { - auto port = TRY(AHCIPort::create(*this, m_hba_capabilities, static_cast(hba().port_regs[index]), index)); - m_ports[index] = port; - port->reset(); - } - return {}; -} - -size_t AHCIController::devices_count() const -{ - SpinlockLocker locker(m_hba_control_lock); - size_t count = 0; - for (auto port : m_ports) { - if (port && port->connected_device()) - count++; - } - return count; -} - -void AHCIController::start_request(ATA::Address address, AsyncBlockDeviceRequest& request) -{ - auto port = m_ports[address.port]; - VERIFY(port); - port->start_request(request); -} - -void AHCIController::complete_current_request(AsyncDeviceRequest::RequestResult) -{ - VERIFY_NOT_REACHED(); -} - -volatile AHCI::PortRegisters& AHCIController::port(size_t port_number) const -{ - VERIFY(port_number < (size_t)AHCI::Limits::MaxPorts); - return static_cast(hba().port_regs[port_number]); -} - -volatile AHCI::HBA& AHCIController::hba() const -{ - return const_cast(*m_hba_mapping); -} - -UNMAP_AFTER_INIT AHCIController::AHCIController(PCI::DeviceIdentifier const& pci_device_identifier) - : StorageController(StorageManagement::generate_relative_ahci_controller_id({})) - , PCI::Device(const_cast(pci_device_identifier)) -{ -} - -UNMAP_AFTER_INIT AHCI::HBADefinedCapabilities AHCIController::capabilities() const -{ - u32 capabilities = hba().control_regs.cap; - u32 extended_capabilities = hba().control_regs.cap2; - - dbgln_if(AHCI_DEBUG, "{}: AHCI Controller Capabilities = {:#08x}, Extended Capabilities = {:#08x}", device_identifier().address(), capabilities, extended_capabilities); - - return (AHCI::HBADefinedCapabilities) { - (capabilities & 0b11111) + 1, - ((capabilities >> 8) & 0b11111) + 1, - (u8)((capabilities >> 20) & 0b1111), - (capabilities & (u32)(AHCI::HBACapabilities::SXS)) != 0, - (capabilities & (u32)(AHCI::HBACapabilities::EMS)) != 0, - (capabilities & (u32)(AHCI::HBACapabilities::CCCS)) != 0, - (capabilities & (u32)(AHCI::HBACapabilities::PSC)) != 0, - (capabilities & (u32)(AHCI::HBACapabilities::SSC)) != 0, - (capabilities & (u32)(AHCI::HBACapabilities::PMD)) != 0, - (capabilities & (u32)(AHCI::HBACapabilities::FBSS)) != 0, - (capabilities & (u32)(AHCI::HBACapabilities::SPM)) != 0, - (capabilities & (u32)(AHCI::HBACapabilities::SAM)) != 0, - (capabilities & (u32)(AHCI::HBACapabilities::SCLO)) != 0, - (capabilities & (u32)(AHCI::HBACapabilities::SAL)) != 0, - (capabilities & (u32)(AHCI::HBACapabilities::SALP)) != 0, - (capabilities & (u32)(AHCI::HBACapabilities::SSS)) != 0, - (capabilities & (u32)(AHCI::HBACapabilities::SMPS)) != 0, - (capabilities & (u32)(AHCI::HBACapabilities::SSNTF)) != 0, - (capabilities & (u32)(AHCI::HBACapabilities::SNCQ)) != 0, - (capabilities & (u32)(AHCI::HBACapabilities::S64A)) != 0, - (extended_capabilities & (u32)(AHCI::HBACapabilitiesExtended::BOH)) != 0, - (extended_capabilities & (u32)(AHCI::HBACapabilitiesExtended::NVMP)) != 0, - (extended_capabilities & (u32)(AHCI::HBACapabilitiesExtended::APST)) != 0, - (extended_capabilities & (u32)(AHCI::HBACapabilitiesExtended::SDS)) != 0, - (extended_capabilities & (u32)(AHCI::HBACapabilitiesExtended::SADM)) != 0, - (extended_capabilities & (u32)(AHCI::HBACapabilitiesExtended::DESO)) != 0 - }; -} - -UNMAP_AFTER_INIT ErrorOr> AHCIController::map_default_hba_region(PCI::DeviceIdentifier const& pci_device_identifier) -{ - return PCI::map_bar(pci_device_identifier, PCI::HeaderType0BaseRegister::BAR5); -} - -AHCIController::~AHCIController() = default; - -UNMAP_AFTER_INIT ErrorOr AHCIController::initialize_hba(PCI::DeviceIdentifier const& pci_device_identifier) -{ - m_hba_mapping = TRY(map_default_hba_region(pci_device_identifier)); - m_hba_capabilities = capabilities(); - - u32 version = hba().control_regs.version; - - hba().control_regs.ghc = 0x80000000; // Ensure that HBA knows we are AHCI aware. - PCI::enable_bus_mastering(device_identifier()); - TRY(reserve_irqs(1, true)); - auto irq = MUST(allocate_irq(0)); - enable_global_interrupts(); - - auto implemented_ports = AHCI::MaskedBitField((u32 volatile&)(hba().control_regs.pi)); - m_irq_handler = TRY(AHCIInterruptHandler::create(*this, irq, implemented_ports)); - TRY(reset()); - - dbgln_if(AHCI_DEBUG, "{}: AHCI Controller Version = {:#08x}", device_identifier().address(), version); - dbgln("{}: AHCI command list entries count - {}", device_identifier().address(), m_hba_capabilities.max_command_list_entries_count); - - return {}; -} - -void AHCIController::handle_interrupt_for_port(Badge, u32 port_index) const -{ - auto port = m_ports[port_index]; - VERIFY(port); - port->handle_interrupt(); -} - -void AHCIController::disable_global_interrupts() const -{ - hba().control_regs.ghc = hba().control_regs.ghc & 0xfffffffd; -} -void AHCIController::enable_global_interrupts() const -{ - hba().control_regs.ghc = hba().control_regs.ghc | (1 << 1); -} - -LockRefPtr AHCIController::device_by_port(u32 port_index) const -{ - SpinlockLocker locker(m_hba_control_lock); - auto port = m_ports[port_index]; - if (!port) - return {}; - SpinlockLocker port_hard_locker(port->m_hard_lock); - return port->connected_device(); -} - -LockRefPtr AHCIController::device(u32 index) const -{ - Vector> connected_devices; - u32 pi = hba().control_regs.pi; - u32 bit = bit_scan_forward(pi); - while (bit) { - dbgln_if(AHCI_DEBUG, "Checking implemented port {}, pi {:b}", bit - 1, pi); - pi &= ~(1u << (bit - 1)); - auto checked_device = device_by_port(bit - 1); - bit = bit_scan_forward(pi); - if (checked_device.is_null()) - continue; - connected_devices.append(checked_device.release_nonnull()); - } - dbgln_if(AHCI_DEBUG, "Connected device count: {}, Index: {}", connected_devices.size(), index); - if (index >= connected_devices.size()) - return nullptr; - return connected_devices[index]; -} - -} diff --git a/Kernel/Devices/Storage/AHCI/Controller.h b/Kernel/Devices/Storage/AHCI/Controller.h deleted file mode 100644 index b265aa7e3ad..00000000000 --- a/Kernel/Devices/Storage/AHCI/Controller.h +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Copyright (c) 2021-2022, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include -#include -#include -#include - -namespace Kernel { - -class AsyncBlockDeviceRequest; -class AHCIInterruptHandler; -class AHCIPort; -class AHCIController final : public StorageController - , public PCI::Device { - friend class AHCIInterruptHandler; - -public: - static ErrorOr> initialize(PCI::DeviceIdentifier const& pci_device_identifier); - virtual ~AHCIController() override; - - virtual StringView device_name() const override { return "AHCI"sv; } - - virtual LockRefPtr device(u32 index) const override; - virtual size_t devices_count() const override; - virtual void complete_current_request(AsyncDeviceRequest::RequestResult) override; - - void start_request(ATA::Address, AsyncBlockDeviceRequest&); - - void handle_interrupt_for_port(Badge, u32 port_index) const; - -private: - ErrorOr reset(); - - void disable_global_interrupts() const; - void enable_global_interrupts() const; - - explicit AHCIController(PCI::DeviceIdentifier const&); - ErrorOr initialize_hba(PCI::DeviceIdentifier const&); - - AHCI::HBADefinedCapabilities capabilities() const; - LockRefPtr device_by_port(u32 index) const; - - volatile AHCI::PortRegisters& port(size_t port_number) const; - ErrorOr> map_default_hba_region(PCI::DeviceIdentifier const&); - volatile AHCI::HBA& hba() const; - - Array, 32> m_ports; - Memory::TypedMapping m_hba_mapping; - AHCI::HBADefinedCapabilities m_hba_capabilities; - - // FIXME: There could be multiple IRQ (MSI) handlers for AHCI. Find a way to use all of them. - OwnPtr m_irq_handler; - - // Note: This lock is intended to be locked when doing changes to HBA registers - // that affect its core functionality in a manner that controls all attached storage devices - // to the HBA SATA ports. - mutable Spinlock m_hba_control_lock {}; -}; -} diff --git a/Kernel/Devices/Storage/AHCI/Definitions.h b/Kernel/Devices/Storage/AHCI/Definitions.h deleted file mode 100644 index 03c866f2c7a..00000000000 --- a/Kernel/Devices/Storage/AHCI/Definitions.h +++ /dev/null @@ -1,664 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * Copyright (c) 2021, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include - -namespace Kernel::FIS { - -enum class Type : u8 { - RegisterHostToDevice = 0x27, - RegisterDeviceToHost = 0x34, - DMAActivate = 0x39, - DMASetup = 0x41, - Data = 0x46, - BISTActivate = 0x58, - PIOSetup = 0x5F, - SetDeviceBits = 0xA1 -}; - -enum class DwordCount : size_t { - RegisterHostToDevice = 5, - RegisterDeviceToHost = 5, - DMAActivate = 1, - DMASetup = 7, - PIOSetup = 5, - SetDeviceBits = 2 -}; - -enum HeaderAttributes : u8 { - C = (1 << 7), /* Updates Command register */ -}; - -struct [[gnu::packed]] Header { - u8 fis_type; - u8 port_muliplier; -}; -static_assert(AssertSize()); - -} - -namespace Kernel::FIS::HostToDevice { - -struct [[gnu::packed]] Register { - Header header; - u8 command; - u8 features_low; - u8 lba_low[3]; - u8 device; - u8 lba_high[3]; - u8 features_high; - u16 count; - u8 icc; /* Isochronous Command Completion */ - u8 control; - u32 reserved; -}; -static_assert(AssertSize()); - -}; - -namespace Kernel::FIS::DeviceToHost { - -struct [[gnu::packed]] Register { - Header header; - u8 status; - u8 error; - u8 lba_low[3]; - u8 device; - u8 lba_high[3]; - u8 reserved; - u16 count; - u8 reserved2[6]; -}; -static_assert(AssertSize()); - -struct [[gnu::packed]] SetDeviceBits { - Header header; - u8 status; - u8 error; - u32 protocol_specific; -}; -static_assert(AssertSize()); - -struct [[gnu::packed]] DMAActivate { - Header header; - u16 reserved; -}; -static_assert(AssertSize()); - -struct [[gnu::packed]] PIOSetup { - Header header; - u8 status; - u8 error; - u8 lba_low[3]; - u8 device; - u8 lba_high[3]; - u8 reserved; - u16 count; - u8 reserved2; - u8 e_status; - u16 transfer_count; - u16 reserved3; -}; -static_assert(AssertSize()); - -} - -namespace Kernel::FIS::BiDirectional { - -struct [[gnu::packed]] Data { - Header header; - u16 reserved; - u32 data[]; -}; -static_assert(AssertSize()); - -struct [[gnu::packed]] BISTActivate { -}; -struct [[gnu::packed]] DMASetup { - Header header; - u16 reserved; - u32 dma_buffer_identifier_low; - u32 dma_buffer_identifier_high; - u32 reserved2; - u32 dma_buffer_offset; - u32 dma_transfer_count; - u32 reserved3; -}; -static_assert(AssertSize()); - -} - -namespace Kernel::AHCI { - -class MaskedBitField { - -public: - explicit MaskedBitField(u32 volatile& bitfield_register) - : m_bitfield(bitfield_register) - , m_bit_mask(0xffffffff) - { - } - - MaskedBitField(u32 volatile& bitfield_register, u32 bit_mask) - : m_bitfield(bitfield_register) - , m_bit_mask(bit_mask) - { - } - - void set_at(u8 index) const - { - VERIFY(((1u << index) & m_bit_mask) != 0); - m_bitfield = m_bitfield | ((1u << index) & m_bit_mask); - } - - void set_all() const - { - m_bitfield = m_bitfield | (0xffffffff & m_bit_mask); - } - - bool is_set_at(u8 port_index) const - { - return m_bitfield & ((1u << port_index) & m_bit_mask); - } - - bool is_zeroed() const - { - return (m_bitfield & m_bit_mask) == 0; - } - - Vector to_vector() const - { - // FIXME: Add a sync mechanism! - Vector indices; - u32 bitfield = m_bitfield & m_bit_mask; - for (size_t index = 0; index < 32; index++) { - if (bitfield & 1) { - indices.append(index); - } - bitfield >>= 1; - } - return indices; - } - - u32 bit_mask() const { return m_bit_mask; } - - // Disable default implementations that would use surprising integer promotion. - bool operator==(MaskedBitField const&) const = delete; - bool operator<=(MaskedBitField const&) const = delete; - bool operator>=(MaskedBitField const&) const = delete; - bool operator<(MaskedBitField const&) const = delete; - bool operator>(MaskedBitField const&) const = delete; - -private: - u32 volatile& m_bitfield; - u32 const m_bit_mask; -}; - -enum Limits : u16 { - MaxPorts = 32, - MaxCommands = 32, - MaxMultiplierConnectedPorts = 16, -}; - -enum CommandHeaderAttributes : u16 { - C = (1 << 10), /* Clear Busy upon R_OK */ - P = (1 << 7), /* Prefetchable */ - W = (1 << 6), /* Write */ - A = (1 << 5), /* ATAPI */ - R = (1 << 8) /* Reset */ -}; - -enum HBACapabilities : u32 { - S64A = (u32)1 << 31, /* Supports 64-bit Addressing */ - SNCQ = 1 << 30, /* Supports Native Command Queuing */ - SSNTF = 1 << 29, /* Supports SNotification Register */ - SMPS = 1 << 28, /* Supports Mechanical Presence Switch */ - SSS = 1 << 27, /* Supports Staggered Spin-up */ - SALP = 1 << 26, /* Supports Aggressive Link Power Management */ - SAL = 1 << 25, /* Supports Activity LED */ - SCLO = 1 << 24, /* Supports Command List Override */ - SAM = 1 << 18, /* Supports AHCI mode only */ - SPM = 1 << 17, /* Supports Port Multiplier */ - FBSS = 1 << 16, /* FIS-based Switching Supported */ - PMD = 1 << 15, /* PIO Multiple DRQ Block */ - SSC = 1 << 14, /* Slumber State Capable */ - PSC = 1 << 13, /* Partial State Capable */ - CCCS = 1 << 7, /* Command Completion Coalescing Supported */ - EMS = 1 << 6, /* Enclosure Management Supported */ - SXS = 1 << 5 /* Supports External SATA */ -}; - -enum HBACapabilitiesExtended : u32 { - DESO = 1 << 5, /* DevSleep Entrance from Slumber Only */ - SADM = 1 << 4, /* Supports Aggressive Device Sleep Management */ - SDS = 1 << 3, /* Supports Device Sleep */ - APST = 1 << 2, /* Automatic Partial to Slumber Transitions */ - NVMP = 1 << 1, /* NVMHCI Present */ - BOH = 1 << 0, /* BIOS/OS Handoff */ -}; - -// This structure is not defined by the AHCI spec, but is used within the code -struct [[gnu::packed]] HBADefinedCapabilities { - size_t ports_count { 1 }; - size_t max_command_list_entries_count { 1 }; - u8 interface_speed_generation { 1 }; - bool external_sata_supported : 1 { false }; - bool enclosure_management_supported : 1 { false }; - bool command_completion_coalescing_supported : 1 { false }; - bool partial_state_capable : 1 { false }; - bool slumber_state_capable : 1 { false }; - bool pio_multiple_drq_block : 1 { false }; - bool fis_based_switching_supported : 1 { false }; - bool port_multiplier_supported : 1 { false }; - bool ahci_mode_only : 1 { true }; - bool command_list_override_supported : 1 { false }; - bool activity_led_supported : 1 { false }; - bool aggressive_link_power_management_supported : 1 { false }; - bool staggered_spin_up_supported : 1 { false }; - bool mechanical_presence_switch_supported : 1 { false }; - bool snotification_register_supported : 1 { false }; - bool native_command_queuing_supported : 1 { false }; - bool addressing_64_bit_supported : 1 { false }; - bool bios_os_handoff : 1 { false }; - bool nvmhci_present : 1 { false }; - bool automatic_partial_to_slumber_transitions : 1 { false }; - bool device_sleep_supported : 1 { false }; - bool aggressive_device_sleep_management_supported : 1 { false }; - bool devsleep_entrance_from_slumber_only : 1 { false }; -}; -static_assert(AssertSize()); - -enum class DeviceDetectionInitialization { - NoActionRequested, - PerformInterfaceInitializationSequence, - DisableInterface -}; - -enum PortInterruptFlag : u32 { - CPD = (u32)1 << 31, /* Cold Port Detect */ - TFE = 1 << 30, /* Task File Error */ - HBF = 1 << 29, /* Host Bus Fatal Error */ - HBD = 1 << 28, /* Host Bus Data Error */ - IF = 1 << 27, /* Interface Fatal Error */ - INF = 1 << 26, /* Interface Non-fatal Error */ - OF = 1 << 24, /* Overflow */ - IPM = 1 << 23, /* Incorrect Port Multiplier */ - PRC = 1 << 22, /* PhyRdy Change */ - DMP = 1 << 7, /* Device Mechanical Presence */ - PC = 1 << 6, /* Port Connect Change */ - DP = 1 << 5, /* Descriptor Processed */ - UF = 1 << 4, /* Unknown FIS */ - SDB = 1 << 3, /* Set Device FIS */ - DS = 1 << 2, /* DMA Setup FIS */ - PS = 1 << 1, /* PIO Setup FIS */ - DHR = 1 << 0 /* Device to Host Register FIS */ -}; - -enum SErr : u32 { - DIAG_X = 1 << 26, /* Exchanged */ - DIAG_F = 1 << 25, /* Unknown FIS Type */ - DIAG_T = 1 << 24, /* Transport state transition error */ - DIAG_S = 1 << 23, /* Link sequence error */ - DIAG_H = 1 << 22, /* Handshake error */ - DIAG_C = 1 << 21, /* CRC error */ - DIAG_D = 1 << 20, /* Disparity error */ - DIAG_B = 1 << 19, /* 10B to 8B decode error */ - DIAG_W = 1 << 18, /* Comm Wake */ - DIAG_I = 1 << 17, /* Phy Internal Error */ - DIAG_N = 1 << 16, /* PhyRdy Change */ - ERR_E = 1 << 11, /* Internal error */ - ERR_P = 1 << 10, /* Protocol error */ - ERR_C = 1 << 9, /* Persistent communication or data integrity error */ - ERR_T = 1 << 8, /* Transient data integrity error */ - ERR_M = 1 << 1, /* Received communications error */ - ERR_I = 1 << 0, /* Recovered data integrity error */ -}; - -class PortInterruptStatusBitField { - -public: - explicit PortInterruptStatusBitField(u32 volatile& bitfield_register) - : m_bitfield(bitfield_register) - { - } - - u32 raw_value() const { return m_bitfield; } - bool is_set(PortInterruptFlag flag) const { return m_bitfield & (u32)flag; } - void clear() { m_bitfield = 0xffffffff; } - - // Disable default implementations that would use surprising integer promotion. - bool operator==(MaskedBitField const&) const = delete; - bool operator<=(MaskedBitField const&) const = delete; - bool operator>=(MaskedBitField const&) const = delete; - bool operator<(MaskedBitField const&) const = delete; - bool operator>(MaskedBitField const&) const = delete; - -private: - u32 volatile& m_bitfield; -}; - -class PortInterruptEnableBitField { - -public: - explicit PortInterruptEnableBitField(u32 volatile& bitfield_register) - : m_bitfield(bitfield_register) - { - } - - u32 raw_value() const { return m_bitfield; } - bool is_set(PortInterruptFlag flag) { return m_bitfield & (u32)flag; } - void set_at(PortInterruptFlag flag) { m_bitfield = m_bitfield | static_cast(flag); } - void clear() { m_bitfield = 0; } - bool is_cleared() const { return m_bitfield == 0; } - void set_all() { m_bitfield = 0xffffffff; } - - // Disable default implementations that would use surprising integer promotion. - bool operator==(MaskedBitField const&) const = delete; - bool operator<=(MaskedBitField const&) const = delete; - bool operator>=(MaskedBitField const&) const = delete; - bool operator<(MaskedBitField const&) const = delete; - bool operator>(MaskedBitField const&) const = delete; - -private: - u32 volatile& m_bitfield; -}; - -struct [[gnu::packed]] PortRegisters { - u32 clb; /* Port x Command List Base Address */ - u32 clbu; /* Port x Command List Base Address Upper 32-Bits */ - u32 fb; /* Port x FIS Base Address */ - u32 fbu; /* Port x FIS Base Address Upper 32-Bits */ - u32 is; /* Port x Interrupt Status */ - u32 ie; /* Port x Interrupt Enable */ - u32 cmd; /* Port x Command and Status */ - u32 reserved; - u32 tfd; /* Port x Task File Data */ - u32 sig; /* Port x Signature */ - u32 ssts; /* Port x Serial ATA Status (SCR0: SStatus) */ - u32 sctl; /* Port x Serial ATA Control (SCR2: SControl) */ - u32 serr; /* Port x Serial ATA Error (SCR1: SError) */ - u32 sact; /* Port x Serial ATA Active (SCR3: SActive) */ - u32 ci; /* Port x Command Issue */ - u32 sntf; /* Port x Serial ATA Notification (SCR4: SNotification) */ - u32 fbs; /* Port x FIS-based Switching Control */ - u32 devslp; /* Port x Device Sleep */ - u8 reserved2[0x70 - 0x48]; - u8 vs[16]; /* Port x Vendor Specific */ -}; -static_assert(AssertSize()); - -struct [[gnu::packed]] GenericHostControl { - u32 cap; /* Host Capabilities */ - u32 ghc; /* Global Host Control */ - u32 is; /* Interrupt Status */ - u32 pi; /* Ports Implemented */ - u32 version; - u32 ccc_ctl; /* Command Completion Coalescing Control */ - u32 ccc_ports; /* Command Completion Coalsecing Ports */ - u32 em_loc; /* Enclosure Management Location */ - u32 em_ctl; /* Enclosure Management Control */ - u32 cap2; /* Host Capabilities Extended */ - u32 bohc; /* BIOS/OS Handoff Control and Status */ -}; -static_assert(AssertSize()); - -struct [[gnu::packed]] HBA { - GenericHostControl control_regs; - u8 reserved[52]; - u8 nvmhci[64]; - u8 vendor_specific[96]; - PortRegisters port_regs[]; -}; -static_assert(AssertSize()); -static_assert(__builtin_offsetof(HBA, port_regs[32]) == 0x1100); - -struct [[gnu::packed]] CommandHeader { - u16 attributes; - u16 prdtl; /* Physical Region Descriptor Table Length */ - u32 prdbc; /* Physical Region Descriptor Byte Count */ - u32 ctba; /* Command Table Descriptor Base Address */ - u32 ctbau; /* Command Table Descriptor Base Address Upper 32-bits */ - u32 reserved[4]; -}; -static_assert(AssertSize()); - -struct [[gnu::packed]] PhysicalRegionDescriptor { - u32 base_low; - u32 base_high; - u32 reserved; - u32 byte_count; /* Bit 31 - Interrupt completion, Bit 0 to 21 - Data Byte Count */ -}; -static_assert(AssertSize()); - -struct [[gnu::packed]] CommandTable { - u8 command_fis[64]; - u8 atapi_command[32]; - u8 reserved[32]; - PhysicalRegionDescriptor descriptors[]; -}; -static_assert(AssertSize()); - -} - -namespace Kernel::ATA { - -// NOTE: For SATA drives (AHCI driven HBAs), a port can be a number from 0 to 31, -// and subport can be a number from 0 to 14 (only 15 devices are allowed to -// be connected to one SATA port multiplier). -struct Address { - // FIXME: u32 for this value is wasteful, because even AHCI only support 32 ports - u32 port; - u8 subport; -}; - -enum DeviceSignature : u32 { - ATA = 0x00000101, - ATAPI = 0xEB140101, - EnclosureManagementBridge = 0xC33C0101, - PortMultiplier = 0x96690101, - Unconnected = 0xFFFFFFFF -}; - -} - -#define ATA_SR_BSY 0x80 -#define ATA_SR_DRDY 0x40 -#define ATA_SR_DF 0x20 -#define ATA_SR_DSC 0x10 -#define ATA_SR_DRQ 0x08 -#define ATA_SR_CORR 0x04 -#define ATA_SR_IDX 0x02 -#define ATA_SR_ERR 0x01 - -#define ATA_ER_BBK 0x80 -#define ATA_ER_UNC 0x40 -#define ATA_ER_MC 0x20 -#define ATA_ER_IDNF 0x10 -#define ATA_ER_MCR 0x08 -#define ATA_ER_ABRT 0x04 -#define ATA_ER_TK0NF 0x02 -#define ATA_ER_AMNF 0x01 - -#define ATA_CMD_READ_PIO 0x20 -#define ATA_CMD_READ_PIO_EXT 0x24 -#define ATA_CMD_READ_DMA 0xC8 -#define ATA_CMD_READ_DMA_EXT 0x25 -#define ATA_CMD_WRITE_PIO 0x30 -#define ATA_CMD_WRITE_PIO_EXT 0x34 -#define ATA_CMD_WRITE_DMA 0xCA -#define ATA_CMD_WRITE_DMA_EXT 0x35 -#define ATA_CMD_CACHE_FLUSH 0xE7 -#define ATA_CMD_CACHE_FLUSH_EXT 0xEA -#define ATA_CMD_PACKET 0xA0 -#define ATA_CMD_IDENTIFY_PACKET 0xA1 -#define ATA_CMD_IDENTIFY 0xEC - -#define ATAPI_CMD_READ 0xA8 -#define ATAPI_CMD_EJECT 0x1B - -#define ATA_IDENT_DEVICETYPE 0 -#define ATA_IDENT_CYLINDERS 2 -#define ATA_IDENT_HEADS 6 -#define ATA_IDENT_SECTORS 12 -#define ATA_IDENT_SERIAL 20 -#define ATA_IDENT_MODEL 54 -#define ATA_IDENT_CAPABILITIES 98 -#define ATA_IDENT_FIELDVALID 106 -#define ATA_IDENT_MAX_LBA 120 -#define ATA_IDENT_COMMANDSETS 164 -#define ATA_IDENT_MAX_LBA_EXT 200 - -#define ATA_USE_LBA_ADDRESSING (1 << 6) - -#define IDE_ATA 0x00 -#define IDE_ATAPI 0x01 - -#define ATA_REG_DATA 0x00 -#define ATA_REG_ERROR 0x01 -#define ATA_REG_FEATURES 0x01 -#define ATA_REG_SECCOUNT0 0x02 -#define ATA_REG_LBA0 0x03 -#define ATA_REG_LBA1 0x04 -#define ATA_REG_LBA2 0x05 -#define ATA_REG_HDDEVSEL 0x06 -#define ATA_REG_COMMAND 0x07 -#define ATA_REG_STATUS 0x07 -#define ATA_REG_SECCOUNT1 0x08 -#define ATA_REG_LBA3 0x09 -#define ATA_REG_LBA4 0x0A -#define ATA_REG_LBA5 0x0B -#define ATA_CTL_CONTROL 0x00 -#define ATA_CTL_ALTSTATUS 0x00 -#define ATA_CTL_DEVADDRESS 0x01 - -#define ATA_CAP_LBA 0x200 - -namespace Kernel { - -struct [[gnu::packed]] ATAIdentifyBlock { - u16 general_configuration; - u16 obsolete; - u16 specific_configuration; - - u16 obsolete2; - u16 retired[2]; - u16 obsolete3; - - u16 reserved_for_cfa[2]; - u16 retired2; - u16 serial_number[10]; - - u16 retired3[2]; - u16 obsolete4; - - u16 firmware_revision[4]; - u16 model_number[20]; - - u16 maximum_logical_sectors_per_drq; - u16 trusted_computing_features; - u16 capabilities[2]; - u16 obsolete5[2]; - u16 validity_flags; - u16 obsolete6[5]; - - u16 security_features; - - u32 max_28_bit_addressable_logical_sector; - u16 obsolete7; - u16 dma_modes; - u16 pio_modes; - - u16 minimum_multiword_dma_transfer_cycle; - u16 recommended_multiword_dma_transfer_cycle; - - u16 minimum_multiword_pio_transfer_cycle_without_flow_control; - u16 minimum_multiword_pio_transfer_cycle_with_flow_control; - - u16 additional_supported; - u16 reserved3[5]; - u16 queue_depth; - - u16 serial_ata_capabilities; - u16 serial_ata_additional_capabilities; - u16 serial_ata_features_supported; - u16 serial_ata_features_enabled; - u16 major_version_number; - u16 minor_version_number; - u16 commands_and_feature_sets_supported[3]; - u16 commands_and_feature_sets_supported_or_enabled[3]; - u16 ultra_dma_modes; - - u16 timing_for_security_features[2]; - u16 apm_level; - u16 master_password_id; - - u16 hardware_reset_results; - u16 obsolete8; - - u16 stream_minimum_request_time; - u16 streaming_transfer_time_for_dma; - u16 streaming_access_latency; - u16 streaming_performance_granularity[2]; - - u64 user_addressable_logical_sectors_count; - - u16 streaming_transfer_time_for_pio; - u16 max_512_byte_blocks_per_data_set_management_command; - u16 physical_sector_size_to_logical_sector_size; - u16 inter_seek_delay_for_acoustic_testing; - u16 world_wide_name[4]; - u16 reserved4[4]; - u16 obsolete9; - - u32 logical_sector_size; - - u16 commands_and_feature_sets_supported2; - u16 commands_and_feature_sets_supported_or_enabled2; - - u16 reserved_for_expanded_supported_and_enabled_settings[6]; - u16 obsolete10; - - u16 security_status; - u16 vendor_specific[31]; - u16 reserved_for_cfa2[8]; - u16 device_nominal_form_factor; - u16 data_set_management_command_support; - u16 additional_product_id[4]; - u16 reserved5[2]; - u16 current_media_serial_number[30]; - u16 sct_command_transport; - u16 reserved6[2]; - - u16 logical_sectors_alignment_within_physical_sector; - - u32 write_read_verify_sector_mode_3_count; - u32 write_read_verify_sector_mode_2_count; - - u16 obsolete11[3]; - u16 nominal_media_rotation_rate; - u16 reserved7; - u16 obsolete12; - u16 write_read_verify_feature_set_current_mode; - u16 reserved8; - u16 transport_major_version_number; - u16 transport_minor_version_number; - u16 reserved9[6]; - - u64 extended_user_addressable_logical_sectors_count; - - u16 minimum_512_byte_data_blocks_per_download_microcode_operation; - u16 max_512_byte_data_blocks_per_download_microcode_operation; - - u16 reserved10[19]; - u16 integrity; -}; - -}; diff --git a/Kernel/Devices/Storage/AHCI/InterruptHandler.cpp b/Kernel/Devices/Storage/AHCI/InterruptHandler.cpp deleted file mode 100644 index 1435a0dd59a..00000000000 --- a/Kernel/Devices/Storage/AHCI/InterruptHandler.cpp +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright (c) 2021-2022, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include - -namespace Kernel { - -UNMAP_AFTER_INIT ErrorOr> AHCIInterruptHandler::create(AHCIController& controller, u8 irq, AHCI::MaskedBitField taken_ports) -{ - auto port_handler = TRY(adopt_nonnull_own_or_enomem(new (nothrow) AHCIInterruptHandler(controller, irq, taken_ports))); - port_handler->allocate_resources_and_initialize_ports(); - return port_handler; -} - -void AHCIInterruptHandler::allocate_resources_and_initialize_ports() -{ - // Clear pending interrupts, if there are any! - m_pending_ports_interrupts.set_all(); - enable_irq(); -} - -UNMAP_AFTER_INIT AHCIInterruptHandler::AHCIInterruptHandler(AHCIController& controller, u8 irq, AHCI::MaskedBitField taken_ports) - : PCI::IRQHandler(controller, irq) - , m_parent_controller(controller) - , m_taken_ports(taken_ports) - , m_pending_ports_interrupts(create_pending_ports_interrupts_bitfield()) -{ - dbgln_if(AHCI_DEBUG, "AHCI Port Handler: IRQ {}", irq); -} - -AHCI::MaskedBitField AHCIInterruptHandler::create_pending_ports_interrupts_bitfield() const -{ - return AHCI::MaskedBitField((u32 volatile&)m_parent_controller->hba().control_regs.is, m_taken_ports.bit_mask()); -} - -AHCIInterruptHandler::~AHCIInterruptHandler() = default; - -bool AHCIInterruptHandler::handle_irq(RegisterState const&) -{ - dbgln_if(AHCI_DEBUG, "AHCI Port Handler: IRQ received"); - if (m_pending_ports_interrupts.is_zeroed()) - return false; - for (auto port_index : m_pending_ports_interrupts.to_vector()) { - dbgln_if(AHCI_DEBUG, "AHCI Port Handler: Handling IRQ for port {}", port_index); - m_parent_controller->handle_interrupt_for_port({}, port_index); - // We do this to clear the pending interrupt after we handled it. - m_pending_ports_interrupts.set_at(port_index); - } - return true; -} - -} diff --git a/Kernel/Devices/Storage/AHCI/InterruptHandler.h b/Kernel/Devices/Storage/AHCI/InterruptHandler.h deleted file mode 100644 index 835a2f6ffaf..00000000000 --- a/Kernel/Devices/Storage/AHCI/InterruptHandler.h +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright (c) 2021-2022, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace Kernel { - -class AsyncBlockDeviceRequest; - -class AHCIController; -class AHCIPort; -class AHCIInterruptHandler final : public PCI::IRQHandler { - friend class AHCIController; - -public: - static ErrorOr> create(AHCIController&, u8 irq, AHCI::MaskedBitField taken_ports); - virtual ~AHCIInterruptHandler() override; - - virtual StringView purpose() const override { return "SATA IRQ Handler"sv; } - - bool is_responsible_for_port_index(u32 port_index) const { return m_taken_ports.is_set_at(port_index); } - -private: - AHCIInterruptHandler(AHCIController&, u8 irq, AHCI::MaskedBitField taken_ports); - - void allocate_resources_and_initialize_ports(); - - //^ IRQHandler - virtual bool handle_irq(RegisterState const&) override; - - enum class Direction : u8 { - Read, - Write, - }; - - AHCI::MaskedBitField create_pending_ports_interrupts_bitfield() const; - - // Data members - NonnullLockRefPtr m_parent_controller; - AHCI::MaskedBitField m_taken_ports; - AHCI::MaskedBitField m_pending_ports_interrupts; -}; -} diff --git a/Kernel/Devices/Storage/AHCI/Port.cpp b/Kernel/Devices/Storage/AHCI/Port.cpp deleted file mode 100644 index 35fbad6e273..00000000000 --- a/Kernel/Devices/Storage/AHCI/Port.cpp +++ /dev/null @@ -1,800 +0,0 @@ -/* - * Copyright (c) 2021, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -// For more information about locking in this code -// please look at Documentation/Kernel/AHCILocking.md - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace Kernel { - -UNMAP_AFTER_INIT ErrorOr> AHCIPort::create(AHCIController const& controller, AHCI::HBADefinedCapabilities hba_capabilities, volatile AHCI::PortRegisters& registers, u32 port_index) -{ - auto identify_buffer_page = MUST(MM.allocate_physical_page()); - auto port = TRY(adopt_nonnull_lock_ref_or_enomem(new (nothrow) AHCIPort(controller, move(identify_buffer_page), hba_capabilities, registers, port_index))); - TRY(port->allocate_resources_and_initialize_ports()); - return port; -} - -ErrorOr AHCIPort::allocate_resources_and_initialize_ports() -{ - if (is_interface_disabled()) { - m_disabled_by_firmware = true; - return {}; - } - - m_fis_receive_page = TRY(MM.allocate_physical_page()); - - for (size_t index = 0; index < 1; index++) { - auto dma_page = TRY(MM.allocate_physical_page()); - m_dma_buffers.append(move(dma_page)); - } - for (size_t index = 0; index < 1; index++) { - auto command_table_page = TRY(MM.allocate_physical_page()); - m_command_table_pages.append(move(command_table_page)); - } - - m_command_list_region = TRY(MM.allocate_dma_buffer_page("AHCI Port Command List"sv, Memory::Region::Access::ReadWrite, m_command_list_page)); - - dbgln_if(AHCI_DEBUG, "AHCI Port {}: Command list page at {}", representative_port_index(), m_command_list_page->paddr()); - dbgln_if(AHCI_DEBUG, "AHCI Port {}: FIS receive page at {}", representative_port_index(), m_fis_receive_page->paddr()); - dbgln_if(AHCI_DEBUG, "AHCI Port {}: Command list region at {}", representative_port_index(), m_command_list_region->vaddr()); - return {}; -} - -UNMAP_AFTER_INIT AHCIPort::AHCIPort(AHCIController const& controller, NonnullRefPtr identify_buffer_page, AHCI::HBADefinedCapabilities hba_capabilities, volatile AHCI::PortRegisters& registers, u32 port_index) - : m_port_index(port_index) - , m_hba_capabilities(hba_capabilities) - , m_identify_buffer_page(move(identify_buffer_page)) - , m_port_registers(registers) - , m_parent_controller(controller) - , m_interrupt_status((u32 volatile&)m_port_registers.is) - , m_interrupt_enable((u32 volatile&)m_port_registers.ie) -{ -} - -void AHCIPort::clear_sata_error_register() const -{ - dbgln_if(AHCI_DEBUG, "AHCI Port {}: Clearing SATA error register.", representative_port_index()); - m_port_registers.serr = m_port_registers.serr; -} - -void AHCIPort::handle_interrupt() -{ - dbgln_if(AHCI_DEBUG, "AHCI Port {}: Interrupt handled, PxIS {}", representative_port_index(), m_interrupt_status.raw_value()); - if (m_interrupt_status.raw_value() == 0) { - return; - } - if (m_interrupt_status.is_set(AHCI::PortInterruptFlag::PRC) && m_interrupt_status.is_set(AHCI::PortInterruptFlag::PC)) { - clear_sata_error_register(); - if ((m_port_registers.ssts & 0xf) != 3 && m_connected_device) { - m_connected_device->prepare_for_unplug(); - StorageManagement::the().remove_device(*m_connected_device); - auto work_item_creation_result = g_io_work->try_queue([this]() { - m_connected_device.clear(); - }); - if (work_item_creation_result.is_error()) { - auto current_request = m_current_request; - m_current_request.clear(); - current_request->complete(AsyncDeviceRequest::OutOfMemory); - } - } else { - auto work_item_creation_result = g_io_work->try_queue([this]() { - reset(); - }); - if (work_item_creation_result.is_error()) { - auto current_request = m_current_request; - m_current_request.clear(); - current_request->complete(AsyncDeviceRequest::OutOfMemory); - } - } - return; - } - if (m_interrupt_status.is_set(AHCI::PortInterruptFlag::PRC)) { - clear_sata_error_register(); - } - if (m_interrupt_status.is_set(AHCI::PortInterruptFlag::INF)) { - // We need to defer the reset, because we can receive interrupts when - // resetting the device. - auto work_item_creation_result = g_io_work->try_queue([this]() { - reset(); - }); - if (work_item_creation_result.is_error()) { - auto current_request = m_current_request; - m_current_request.clear(); - current_request->complete(AsyncDeviceRequest::OutOfMemory); - } - return; - } - if (m_interrupt_status.is_set(AHCI::PortInterruptFlag::IF) || m_interrupt_status.is_set(AHCI::PortInterruptFlag::TFE) || m_interrupt_status.is_set(AHCI::PortInterruptFlag::HBD) || m_interrupt_status.is_set(AHCI::PortInterruptFlag::HBF)) { - auto work_item_creation_result = g_io_work->try_queue([this]() { - recover_from_fatal_error(); - }); - if (work_item_creation_result.is_error()) { - auto current_request = m_current_request; - m_current_request.clear(); - current_request->complete(AsyncDeviceRequest::OutOfMemory); - } - return; - } - if (m_interrupt_status.is_set(AHCI::PortInterruptFlag::DHR) || m_interrupt_status.is_set(AHCI::PortInterruptFlag::PS)) { - m_wait_for_completion = false; - - // Now schedule reading/writing the buffer as soon as we leave the irq handler. - // This is important so that we can safely access the buffers, which could - // trigger page faults - if (!m_current_request) { - dbgln_if(AHCI_DEBUG, "AHCI Port {}: Request handled, probably identify request", representative_port_index()); - } else { - auto work_item_creation_result = g_io_work->try_queue([this]() { - dbgln_if(AHCI_DEBUG, "AHCI Port {}: Request handled", representative_port_index()); - MutexLocker locker(m_lock); - VERIFY(m_current_request); - VERIFY(m_current_scatter_list); - if (!m_connected_device) { - dbgln_if(AHCI_DEBUG, "AHCI Port {}: Request success", representative_port_index()); - complete_current_request(AsyncDeviceRequest::Failure); - return; - } - if (m_current_request->request_type() == AsyncBlockDeviceRequest::Read) { - if (auto result = m_current_request->write_to_buffer(m_current_request->buffer(), m_current_scatter_list->dma_region().as_ptr(), m_connected_device->block_size() * m_current_request->block_count()); result.is_error()) { - dbgln_if(AHCI_DEBUG, "AHCI Port {}: Request failure, memory fault occurred when reading in data.", representative_port_index()); - m_current_scatter_list = nullptr; - complete_current_request(AsyncDeviceRequest::MemoryFault); - return; - } - } - m_current_scatter_list = nullptr; - dbgln_if(AHCI_DEBUG, "AHCI Port {}: Request success", representative_port_index()); - complete_current_request(AsyncDeviceRequest::Success); - }); - if (work_item_creation_result.is_error()) { - auto current_request = m_current_request; - m_current_request.clear(); - current_request->complete(AsyncDeviceRequest::OutOfMemory); - } - } - } - - m_interrupt_status.clear(); -} - -bool AHCIPort::is_interrupts_enabled() const -{ - return !m_interrupt_enable.is_cleared(); -} - -void AHCIPort::recover_from_fatal_error() -{ - MutexLocker locker(m_lock); - SpinlockLocker lock(m_hard_lock); - - dmesgln("{}: AHCI Port {} fatal error, shutting down!", m_parent_controller->device_identifier().address(), representative_port_index()); - dmesgln("{}: AHCI Port {} fatal error, SError {}", m_parent_controller->device_identifier().address(), representative_port_index(), (u32)m_port_registers.serr); - stop_command_list_processing(); - stop_fis_receiving(); - m_interrupt_enable.clear(); -} - -bool AHCIPort::reset() -{ - MutexLocker locker(m_lock); - SpinlockLocker lock(m_hard_lock); - - dbgln_if(AHCI_DEBUG, "AHCI Port {}: Resetting", representative_port_index()); - - if (m_disabled_by_firmware) { - dmesgln("AHCI Port {}: Disabled by firmware ", representative_port_index()); - return false; - } - full_memory_barrier(); - m_interrupt_enable.clear(); - m_interrupt_status.clear(); - full_memory_barrier(); - start_fis_receiving(); - full_memory_barrier(); - clear_sata_error_register(); - full_memory_barrier(); - if (!initiate_sata_reset()) { - return false; - } - return initialize(); -} - -UNMAP_AFTER_INIT bool AHCIPort::initialize_without_reset() -{ - MutexLocker locker(m_lock); - SpinlockLocker lock(m_hard_lock); - dmesgln("AHCI Port {}: {}", representative_port_index(), try_disambiguate_sata_status()); - return initialize(); -} - -bool AHCIPort::initialize() -{ - VERIFY(m_lock.is_locked()); - dbgln_if(AHCI_DEBUG, "AHCI Port {}: Initialization. Signature = {:#08x}", representative_port_index(), static_cast(m_port_registers.sig)); - if (!is_phy_enabled()) { - // Note: If PHY is not enabled, just clear the interrupt status and enable interrupts, in case - // we are going to hotplug a device later. - m_interrupt_status.clear(); - m_interrupt_enable.set_all(); - dbgln_if(AHCI_DEBUG, "AHCI Port {}: Bailing initialization, Phy is not enabled.", representative_port_index()); - return false; - } - rebase(); - power_on(); - spin_up(); - clear_sata_error_register(); - start_fis_receiving(); - set_active_state(); - m_interrupt_status.clear(); - m_interrupt_enable.set_all(); - - full_memory_barrier(); - // This actually enables the port... - start_command_list_processing(); - full_memory_barrier(); - - size_t logical_sector_size = 512; - size_t physical_sector_size = 512; - u64 max_addressable_sector = 0; - - if (identify_device()) { - auto identify_block = Memory::map_typed(m_identify_buffer_page->paddr()).release_value_but_fixme_should_propagate_errors(); - // Check if word 106 is valid before using it! - if ((identify_block->physical_sector_size_to_logical_sector_size >> 14) == 1) { - if (identify_block->physical_sector_size_to_logical_sector_size & (1 << 12)) { - VERIFY(identify_block->logical_sector_size != 0); - logical_sector_size = identify_block->logical_sector_size; - } - if (identify_block->physical_sector_size_to_logical_sector_size & (1 << 13)) { - physical_sector_size = logical_sector_size << (identify_block->physical_sector_size_to_logical_sector_size & 0xf); - } - } - // Check if the device supports LBA48 mode - if (identify_block->commands_and_feature_sets_supported[1] & (1 << 10)) { - max_addressable_sector = identify_block->user_addressable_logical_sectors_count; - } else { - max_addressable_sector = identify_block->max_28_bit_addressable_logical_sector; - } - if (is_atapi_attached()) { - m_port_registers.cmd = m_port_registers.cmd | (1 << 24); - } - - dmesgln("AHCI Port {}: Device found, Capacity={}, Bytes per logical sector={}, Bytes per physical sector={}", representative_port_index(), max_addressable_sector * logical_sector_size, logical_sector_size, physical_sector_size); - - // FIXME: We don't support ATAPI devices yet, so for now we don't "create" them - if (!is_atapi_attached()) { - m_connected_device = ATADiskDevice::create(*m_parent_controller, { m_port_index, 0 }, 0, logical_sector_size, max_addressable_sector); - } else { - dbgln("AHCI Port {}: Ignoring ATAPI devices as we don't support them.", representative_port_index()); - } - } - return true; -} - -char const* AHCIPort::try_disambiguate_sata_status() -{ - switch (m_port_registers.ssts & 0xf) { - case 0: - return "Device not detected, Phy not enabled"; - case 1: - return "Device detected, Phy disabled"; - case 3: - return "Device detected, Phy enabled"; - case 4: - return "interface disabled"; - } - VERIFY_NOT_REACHED(); -} - -void AHCIPort::try_disambiguate_sata_error() -{ - dmesgln("AHCI Port {}: SErr breakdown:", representative_port_index()); - dmesgln("AHCI Port {}: Diagnostics:", representative_port_index()); - - constexpr u32 diagnostics_bitfield = 0xFFFF0000; - if ((m_port_registers.serr & diagnostics_bitfield) > 0) { - if (m_port_registers.serr & AHCI::SErr::DIAG_X) - dmesgln("AHCI Port {}: - Exchanged", representative_port_index()); - if (m_port_registers.serr & AHCI::SErr::DIAG_F) - dmesgln("AHCI Port {}: - Unknown FIS Type", representative_port_index()); - if (m_port_registers.serr & AHCI::SErr::DIAG_T) - dmesgln("AHCI Port {}: - Transport state transition error", representative_port_index()); - if (m_port_registers.serr & AHCI::SErr::DIAG_S) - dmesgln("AHCI Port {}: - Link sequence error", representative_port_index()); - if (m_port_registers.serr & AHCI::SErr::DIAG_H) - dmesgln("AHCI Port {}: - Handshake error", representative_port_index()); - if (m_port_registers.serr & AHCI::SErr::DIAG_C) - dmesgln("AHCI Port {}: - CRC error", representative_port_index()); - if (m_port_registers.serr & AHCI::SErr::DIAG_D) - dmesgln("AHCI Port {}: - Disparity error", representative_port_index()); - if (m_port_registers.serr & AHCI::SErr::DIAG_B) - dmesgln("AHCI Port {}: - 10B to 8B decode error", representative_port_index()); - if (m_port_registers.serr & AHCI::SErr::DIAG_W) - dmesgln("AHCI Port {}: - Comm Wake", representative_port_index()); - if (m_port_registers.serr & AHCI::SErr::DIAG_I) - dmesgln("AHCI Port {}: - Phy Internal Error", representative_port_index()); - if (m_port_registers.serr & AHCI::SErr::DIAG_N) - dmesgln("AHCI Port {}: - PhyRdy Change", representative_port_index()); - } else { - dmesgln("AHCI Port {}: - No diagnostic information provided.", representative_port_index()); - } - - dmesgln("AHCI Port {}: Error(s):", representative_port_index()); - - constexpr u32 error_bitfield = 0xFFFF; - if ((m_port_registers.serr & error_bitfield) > 0) { - if (m_port_registers.serr & AHCI::SErr::ERR_E) - dmesgln("AHCI Port {}: - Internal error", representative_port_index()); - if (m_port_registers.serr & AHCI::SErr::ERR_P) - dmesgln("AHCI Port {}: - Protocol error", representative_port_index()); - if (m_port_registers.serr & AHCI::SErr::ERR_C) - dmesgln("AHCI Port {}: - Persistent communication or data integrity error", representative_port_index()); - if (m_port_registers.serr & AHCI::SErr::ERR_T) - dmesgln("AHCI Port {}: - Transient data integrity error", representative_port_index()); - if (m_port_registers.serr & AHCI::SErr::ERR_M) - dmesgln("AHCI Port {}: - Recovered communications error", representative_port_index()); - if (m_port_registers.serr & AHCI::SErr::ERR_I) - dmesgln("AHCI Port {}: - Recovered data integrity error", representative_port_index()); - } else { - dmesgln("AHCI Port {}: - No error information provided.", representative_port_index()); - } -} - -void AHCIPort::rebase() -{ - VERIFY(m_lock.is_locked()); - VERIFY(m_hard_lock.is_locked()); - VERIFY(!m_command_list_page.is_null() && !m_fis_receive_page.is_null()); - dbgln_if(AHCI_DEBUG, "AHCI Port {}: Rebasing.", representative_port_index()); - full_memory_barrier(); - stop_command_list_processing(); - stop_fis_receiving(); - full_memory_barrier(); - - // Try to wait 1 second for HBA to clear Command List Running and FIS Receive Running - wait_until_condition_met_or_timeout(1000, 1000, [this]() -> bool { - return !(m_port_registers.cmd & (1 << 15)) && !(m_port_registers.cmd & (1 << 14)); - }); - full_memory_barrier(); - m_port_registers.clbu = 0; - m_port_registers.clb = m_command_list_page->paddr().get(); - m_port_registers.fbu = 0; - m_port_registers.fb = m_fis_receive_page->paddr().get(); -} - -bool AHCIPort::is_operable() const -{ - // Note: The definition of "operable" is somewhat ambiguous, but we determine it - // by 3 parameters as shown below. - return (!m_command_list_page.is_null()) - && (!m_fis_receive_page.is_null()) - && ((m_port_registers.cmd & (1 << 14)) != 0); -} - -void AHCIPort::set_active_state() const -{ - VERIFY(m_lock.is_locked()); - VERIFY(m_hard_lock.is_locked()); - dbgln_if(AHCI_DEBUG, "AHCI Port {}: Switching to active state.", representative_port_index()); - m_port_registers.cmd = (m_port_registers.cmd & 0x0ffffff) | (1 << 28); -} - -void AHCIPort::set_sleep_state() const -{ - VERIFY(m_lock.is_locked()); - VERIFY(m_hard_lock.is_locked()); - m_port_registers.cmd = (m_port_registers.cmd & 0x0ffffff) | (0b1000 << 28); -} - -size_t AHCIPort::calculate_descriptors_count(size_t block_count) const -{ - VERIFY(m_connected_device); - size_t needed_dma_regions_count = Memory::page_round_up((block_count * m_connected_device->block_size())).value() / PAGE_SIZE; - VERIFY(needed_dma_regions_count <= m_dma_buffers.size()); - return needed_dma_regions_count; -} - -Optional AHCIPort::prepare_and_set_scatter_list(AsyncBlockDeviceRequest& request) -{ - VERIFY(m_lock.is_locked()); - VERIFY(request.block_count() > 0); - - Vector> allocated_dma_regions; - for (size_t index = 0; index < calculate_descriptors_count(request.block_count()); index++) { - allocated_dma_regions.append(m_dma_buffers.at(index)); - } - - m_current_scatter_list = Memory::ScatterGatherList::try_create(request, allocated_dma_regions.span(), m_connected_device->block_size(), "AHCI Scattered DMA"sv).release_value_but_fixme_should_propagate_errors(); - if (!m_current_scatter_list) - return AsyncDeviceRequest::Failure; - if (request.request_type() == AsyncBlockDeviceRequest::Write) { - if (auto result = request.read_from_buffer(request.buffer(), m_current_scatter_list->dma_region().as_ptr(), m_connected_device->block_size() * request.block_count()); result.is_error()) { - return AsyncDeviceRequest::MemoryFault; - } - } - return {}; -} - -void AHCIPort::start_request(AsyncBlockDeviceRequest& request) -{ - MutexLocker locker(m_lock); - dbgln_if(AHCI_DEBUG, "AHCI Port {}: Request start", representative_port_index()); - VERIFY(!m_current_request); - VERIFY(!m_current_scatter_list); - - m_current_request = request; - - auto result = prepare_and_set_scatter_list(request); - if (result.has_value()) { - dbgln_if(AHCI_DEBUG, "AHCI Port {}: Request failure.", representative_port_index()); - locker.unlock(); - complete_current_request(result.value()); - return; - } - - auto success = access_device(request.request_type(), request.block_index(), request.block_count()); - if (!success) { - dbgln_if(AHCI_DEBUG, "AHCI Port {}: Request failure.", representative_port_index()); - locker.unlock(); - complete_current_request(AsyncDeviceRequest::Failure); - return; - } -} - -void AHCIPort::complete_current_request(AsyncDeviceRequest::RequestResult result) -{ - VERIFY(m_current_request); - auto current_request = m_current_request; - m_current_request.clear(); - current_request->complete(result); -} - -bool AHCIPort::spin_until_ready() const -{ - VERIFY(m_lock.is_locked()); - size_t spin = 0; - dbgln_if(AHCI_DEBUG, "AHCI Port {}: Spinning until ready.", representative_port_index()); - while ((m_port_registers.tfd & (ATA_SR_BSY | ATA_SR_DRQ)) && spin <= 100) { - microseconds_delay(1000); - spin++; - } - if (spin == 100) { - dbgln_if(AHCI_DEBUG, "AHCI Port {}: SPIN exceeded 100 milliseconds threshold", representative_port_index()); - return false; - } - return true; -} - -bool AHCIPort::access_device(AsyncBlockDeviceRequest::RequestType direction, u64 lba, u8 block_count) -{ - VERIFY(m_connected_device); - VERIFY(is_operable()); - VERIFY(m_lock.is_locked()); - VERIFY(m_current_scatter_list); - SpinlockLocker lock(m_hard_lock); - - dbgln_if(AHCI_DEBUG, "AHCI Port {}: Do a {}, lba {}, block count {}", representative_port_index(), direction == AsyncBlockDeviceRequest::RequestType::Write ? "write" : "read", lba, block_count); - if (!spin_until_ready()) - return false; - - auto unused_command_header = try_to_find_unused_command_header(); - VERIFY(unused_command_header.has_value()); - auto* command_list_entries = (volatile AHCI::CommandHeader*)m_command_list_region->vaddr().as_ptr(); - command_list_entries[unused_command_header.value()].ctba = m_command_table_pages[unused_command_header.value()]->paddr().get(); - command_list_entries[unused_command_header.value()].ctbau = 0; - command_list_entries[unused_command_header.value()].prdbc = 0; - command_list_entries[unused_command_header.value()].prdtl = m_current_scatter_list->scatters_count(); - - // Note: we must set the correct Dword count in this register. Real hardware - // AHCI controllers do care about this field! QEMU doesn't care if we don't - // set the correct CFL field in this register, real hardware will set an - // handshake error bit in PxSERR register if CFL is incorrect. - command_list_entries[unused_command_header.value()].attributes = (size_t)FIS::DwordCount::RegisterHostToDevice | AHCI::CommandHeaderAttributes::P | (is_atapi_attached() ? AHCI::CommandHeaderAttributes::A : 0) | (direction == AsyncBlockDeviceRequest::RequestType::Write ? AHCI::CommandHeaderAttributes::W : 0); - - dbgln_if(AHCI_DEBUG, "AHCI Port {}: CLE: ctba={:#08x}, ctbau={:#08x}, prdbc={:#08x}, prdtl={:#04x}, attributes={:#04x}", representative_port_index(), (u32)command_list_entries[unused_command_header.value()].ctba, (u32)command_list_entries[unused_command_header.value()].ctbau, (u32)command_list_entries[unused_command_header.value()].prdbc, (u16)command_list_entries[unused_command_header.value()].prdtl, (u16)command_list_entries[unused_command_header.value()].attributes); - - auto command_table_region = MM.allocate_kernel_region_with_physical_pages({ &m_command_table_pages[unused_command_header.value()], 1 }, "AHCI Command Table"sv, Memory::Region::Access::ReadWrite, Memory::Region::Cacheable::No).release_value(); - auto& command_table = *(volatile AHCI::CommandTable*)command_table_region->vaddr().as_ptr(); - - dbgln_if(AHCI_DEBUG, "AHCI Port {}: Allocated command table at {}", representative_port_index(), command_table_region->vaddr()); - - memset(const_cast(command_table.command_fis), 0, 64); - - size_t scatter_entry_index = 0; - size_t data_transfer_count = (block_count * m_connected_device->block_size()); - for (auto scatter_page : m_current_scatter_list->vmobject().physical_pages()) { - VERIFY(data_transfer_count != 0); - VERIFY(scatter_page); - dbgln_if(AHCI_DEBUG, "AHCI Port {}: Add a transfer scatter entry @ {}", representative_port_index(), scatter_page->paddr()); - command_table.descriptors[scatter_entry_index].base_high = 0; - command_table.descriptors[scatter_entry_index].base_low = scatter_page->paddr().get(); - if (data_transfer_count <= PAGE_SIZE) { - command_table.descriptors[scatter_entry_index].byte_count = data_transfer_count - 1; - data_transfer_count = 0; - } else { - command_table.descriptors[scatter_entry_index].byte_count = PAGE_SIZE - 1; - data_transfer_count -= PAGE_SIZE; - } - scatter_entry_index++; - } - command_table.descriptors[scatter_entry_index].byte_count = (PAGE_SIZE - 1) | (1 << 31); - - memset(const_cast(command_table.atapi_command), 0, 32); - - auto& fis = *(volatile FIS::HostToDevice::Register*)command_table.command_fis; - fis.header.fis_type = (u8)FIS::Type::RegisterHostToDevice; - if (is_atapi_attached()) { - fis.command = ATA_CMD_PACKET; - TODO(); - } else { - if (direction == AsyncBlockDeviceRequest::RequestType::Write) - fis.command = ATA_CMD_WRITE_DMA_EXT; - else - fis.command = ATA_CMD_READ_DMA_EXT; - } - - full_memory_barrier(); - - fis.device = ATA_USE_LBA_ADDRESSING; - fis.header.port_muliplier = (u8)FIS::HeaderAttributes::C; - - fis.lba_high[0] = (lba >> 24) & 0xff; - fis.lba_high[1] = (lba >> 32) & 0xff; - fis.lba_high[2] = (lba >> 40) & 0xff; - fis.lba_low[0] = lba & 0xff; - fis.lba_low[1] = (lba >> 8) & 0xff; - fis.lba_low[2] = (lba >> 16) & 0xff; - fis.count = (block_count); - - // The below loop waits until the port is no longer busy before issuing a new command - if (!spin_until_ready()) - return false; - - full_memory_barrier(); - mark_command_header_ready_to_process(unused_command_header.value()); - full_memory_barrier(); - - dbgln_if(AHCI_DEBUG, "AHCI Port {}: Do a {}, lba {}, block count {} @ {}, ended", representative_port_index(), direction == AsyncBlockDeviceRequest::RequestType::Write ? "write" : "read", lba, block_count, m_dma_buffers[0]->paddr()); - return true; -} - -bool AHCIPort::identify_device() -{ - VERIFY(m_lock.is_locked()); - VERIFY(is_operable()); - if (!spin_until_ready()) - return false; - - auto unused_command_header = try_to_find_unused_command_header(); - VERIFY(unused_command_header.has_value()); - auto* command_list_entries = (volatile AHCI::CommandHeader*)m_command_list_region->vaddr().as_ptr(); - command_list_entries[unused_command_header.value()].ctba = m_command_table_pages[unused_command_header.value()]->paddr().get(); - command_list_entries[unused_command_header.value()].ctbau = 0; - command_list_entries[unused_command_header.value()].prdbc = 512; - command_list_entries[unused_command_header.value()].prdtl = 1; - - // Note: we must set the correct Dword count in this register. Real hardware AHCI controllers do care about this field! - // QEMU doesn't care if we don't set the correct CFL field in this register, real hardware will set an handshake error bit in PxSERR register. - command_list_entries[unused_command_header.value()].attributes = (size_t)FIS::DwordCount::RegisterHostToDevice | AHCI::CommandHeaderAttributes::P; - - auto command_table_region = MM.allocate_kernel_region_with_physical_pages({ &m_command_table_pages[unused_command_header.value()], 1 }, "AHCI Command Table"sv, Memory::Region::Access::ReadWrite).release_value(); - auto& command_table = *(volatile AHCI::CommandTable*)command_table_region->vaddr().as_ptr(); - memset(const_cast(command_table.command_fis), 0, 64); - command_table.descriptors[0].base_high = 0; - command_table.descriptors[0].base_low = m_identify_buffer_page->paddr().get(); - command_table.descriptors[0].byte_count = 512 - 1; - auto& fis = *(volatile FIS::HostToDevice::Register*)command_table.command_fis; - fis.header.fis_type = (u8)FIS::Type::RegisterHostToDevice; - fis.command = m_port_registers.sig == ATA::DeviceSignature::ATAPI ? ATA_CMD_IDENTIFY_PACKET : ATA_CMD_IDENTIFY; - fis.device = 0; - fis.header.port_muliplier = fis.header.port_muliplier | (u8)FIS::HeaderAttributes::C; - - // The below loop waits until the port is no longer busy before issuing a new command - if (!spin_until_ready()) - return false; - - // Just in case we have a pending interrupt. - m_interrupt_enable.clear(); - m_interrupt_status.clear(); - - full_memory_barrier(); - dbgln_if(AHCI_DEBUG, "AHCI Port {}: Marking command header at index {} as ready to identify device", representative_port_index(), unused_command_header.value()); - m_port_registers.ci = 1 << unused_command_header.value(); - full_memory_barrier(); - - size_t time_elapsed = 0; - bool success = false; - while (1) { - // Note: We allow it to spin for 256 milliseconds, which should be enough for a device to respond. - if (time_elapsed >= 256) { - break; - } - if (m_port_registers.serr != 0) { - dbgln("AHCI Port {}: Identify failed, SError {:#08x}", representative_port_index(), (u32)m_port_registers.serr); - try_disambiguate_sata_error(); - break; - } - if (!(m_port_registers.ci & (1 << unused_command_header.value()))) { - success = true; - break; - } - microseconds_delay(1000); // delay with 1 milliseconds - time_elapsed++; - } - - // Note: We probably ended up triggering an interrupt but we don't really want to handle it, - // so just get rid of it. - // FIXME: Do that in a better way so we don't need to actually remember this every time - // we need to do this. - m_interrupt_status.clear(); - m_interrupt_enable.set_all(); - - return success; -} - -void AHCIPort::wait_until_condition_met_or_timeout(size_t delay_in_microseconds, size_t retries, Function condition_being_met) const -{ - size_t retry = 0; - while (retry < retries) { - if (condition_being_met()) - break; - microseconds_delay(delay_in_microseconds); - retry++; - } -} - -bool AHCIPort::shutdown() -{ - MutexLocker locker(m_lock); - SpinlockLocker lock(m_hard_lock); - rebase(); - set_interface_state(AHCI::DeviceDetectionInitialization::DisableInterface); - return true; -} - -Optional AHCIPort::try_to_find_unused_command_header() -{ - VERIFY(m_lock.is_locked()); - u32 commands_issued = m_port_registers.ci; - for (size_t index = 0; index < 32; index++) { - if (!(commands_issued & 1)) { - dbgln_if(AHCI_DEBUG, "AHCI Port {}: unused command header at index {}", representative_port_index(), index); - return index; - } - commands_issued >>= 1; - } - return {}; -} - -void AHCIPort::start_command_list_processing() const -{ - VERIFY(m_lock.is_locked()); - VERIFY(m_hard_lock.is_locked()); - VERIFY(is_operable()); - dbgln_if(AHCI_DEBUG, "AHCI Port {}: Starting command list processing.", representative_port_index()); - m_port_registers.cmd = m_port_registers.cmd | 1; -} - -void AHCIPort::mark_command_header_ready_to_process(u8 command_header_index) const -{ - VERIFY(m_lock.is_locked()); - VERIFY(m_hard_lock.is_locked()); - VERIFY(is_operable()); - VERIFY(!m_wait_for_completion); - m_wait_for_completion = true; - dbgln_if(AHCI_DEBUG, "AHCI Port {}: Marking command header at index {} as ready to process.", representative_port_index(), command_header_index); - m_port_registers.ci = 1 << command_header_index; -} - -void AHCIPort::stop_command_list_processing() const -{ - VERIFY(m_lock.is_locked()); - VERIFY(m_hard_lock.is_locked()); - dbgln_if(AHCI_DEBUG, "AHCI Port {}: Stopping command list processing.", representative_port_index()); - m_port_registers.cmd = m_port_registers.cmd & 0xfffffffe; -} - -void AHCIPort::start_fis_receiving() const -{ - VERIFY(m_lock.is_locked()); - VERIFY(m_hard_lock.is_locked()); - dbgln_if(AHCI_DEBUG, "AHCI Port {}: Starting FIS receiving.", representative_port_index()); - m_port_registers.cmd = m_port_registers.cmd | (1 << 4); -} - -void AHCIPort::power_on() const -{ - VERIFY(m_lock.is_locked()); - VERIFY(m_hard_lock.is_locked()); - dbgln_if(AHCI_DEBUG, "AHCI Port {}: Power on. Cold presence detection? {}", representative_port_index(), (bool)(m_port_registers.cmd & (1 << 20))); - if (!(m_port_registers.cmd & (1 << 20))) - return; - dbgln_if(AHCI_DEBUG, "AHCI Port {}: Powering on device.", representative_port_index()); - m_port_registers.cmd = m_port_registers.cmd | (1 << 2); -} - -void AHCIPort::spin_up() const -{ - VERIFY(m_lock.is_locked()); - VERIFY(m_hard_lock.is_locked()); - dbgln_if(AHCI_DEBUG, "AHCI Port {}: Spin up. Staggered spin up? {}", representative_port_index(), m_hba_capabilities.staggered_spin_up_supported); - if (!m_hba_capabilities.staggered_spin_up_supported) - return; - dbgln_if(AHCI_DEBUG, "AHCI Port {}: Spinning up device.", representative_port_index()); - m_port_registers.cmd = m_port_registers.cmd | (1 << 1); -} - -void AHCIPort::stop_fis_receiving() const -{ - VERIFY(m_lock.is_locked()); - VERIFY(m_hard_lock.is_locked()); - dbgln_if(AHCI_DEBUG, "AHCI Port {}: Stopping FIS receiving.", representative_port_index()); - m_port_registers.cmd = m_port_registers.cmd & 0xFFFFFFEF; -} - -bool AHCIPort::initiate_sata_reset() -{ - VERIFY(m_lock.is_locked()); - VERIFY(m_hard_lock.is_locked()); - dbgln_if(AHCI_DEBUG, "AHCI Port {}: Initiate SATA reset", representative_port_index()); - stop_command_list_processing(); - full_memory_barrier(); - - // Note: The AHCI specification says to wait now a 500 milliseconds - // Try to wait 1 second for HBA to clear Command List Running - wait_until_condition_met_or_timeout(100, 5000, [this]() -> bool { - return !(m_port_registers.cmd & (1 << 15)); - }); - - full_memory_barrier(); - spin_up(); - full_memory_barrier(); - set_interface_state(AHCI::DeviceDetectionInitialization::PerformInterfaceInitializationSequence); - // The AHCI specification says to wait now a 1 millisecond - microseconds_delay(1000); - full_memory_barrier(); - set_interface_state(AHCI::DeviceDetectionInitialization::NoActionRequested); - full_memory_barrier(); - - wait_until_condition_met_or_timeout(10, 1000, [this]() -> bool { - return is_phy_enabled(); - }); - - dmesgln("AHCI Port {}: {}", representative_port_index(), try_disambiguate_sata_status()); - - full_memory_barrier(); - clear_sata_error_register(); - return (m_port_registers.ssts & 0xf) == 3; -} - -void AHCIPort::set_interface_state(AHCI::DeviceDetectionInitialization requested_action) -{ - switch (requested_action) { - case AHCI::DeviceDetectionInitialization::NoActionRequested: - m_port_registers.sctl = (m_port_registers.sctl & 0xfffffff0); - return; - case AHCI::DeviceDetectionInitialization::PerformInterfaceInitializationSequence: - m_port_registers.sctl = (m_port_registers.sctl & 0xfffffff0) | 1; - return; - case AHCI::DeviceDetectionInitialization::DisableInterface: - m_port_registers.sctl = (m_port_registers.sctl & 0xfffffff0) | 4; - return; - } - VERIFY_NOT_REACHED(); -} - -} diff --git a/Kernel/Devices/Storage/AHCI/Port.h b/Kernel/Devices/Storage/AHCI/Port.h deleted file mode 100644 index c2cab5afa28..00000000000 --- a/Kernel/Devices/Storage/AHCI/Port.h +++ /dev/null @@ -1,135 +0,0 @@ -/* - * Copyright (c) 2021-2022, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace Kernel { - -class AsyncBlockDeviceRequest; - -class AHCIInterruptHandler; -class AHCIPort - : public AtomicRefCounted - , public LockWeakable { - friend class AHCIController; - -public: - static ErrorOr> create(AHCIController const&, AHCI::HBADefinedCapabilities, volatile AHCI::PortRegisters&, u32 port_index); - - u32 port_index() const { return m_port_index; } - u32 representative_port_index() const { return port_index() + 1; } - bool is_operable() const; - bool is_atapi_attached() const { return m_port_registers.sig == (u32)ATA::DeviceSignature::ATAPI; } - - LockRefPtr connected_device() const { return m_connected_device; } - - bool reset(); - bool initialize_without_reset(); - void handle_interrupt(); - -private: - ErrorOr allocate_resources_and_initialize_ports(); - - bool is_phy_enabled() const { return (m_port_registers.ssts & 0xf) == 3; } - bool initialize(); - - AHCIPort(AHCIController const&, NonnullRefPtr identify_buffer_page, AHCI::HBADefinedCapabilities, volatile AHCI::PortRegisters&, u32 port_index); - - ALWAYS_INLINE void clear_sata_error_register() const; - - char const* try_disambiguate_sata_status(); - void try_disambiguate_sata_error(); - - bool initiate_sata_reset(); - void rebase(); - void recover_from_fatal_error(); - bool shutdown(); - ALWAYS_INLINE void spin_up() const; - ALWAYS_INLINE void power_on() const; - - void start_request(AsyncBlockDeviceRequest&); - void complete_current_request(AsyncDeviceRequest::RequestResult); - bool access_device(AsyncBlockDeviceRequest::RequestType, u64 lba, u8 block_count); - size_t calculate_descriptors_count(size_t block_count) const; - [[nodiscard]] Optional prepare_and_set_scatter_list(AsyncBlockDeviceRequest& request); - - ALWAYS_INLINE bool is_interrupts_enabled() const; - - bool spin_until_ready() const; - - bool identify_device(); - - ALWAYS_INLINE void start_command_list_processing() const; - ALWAYS_INLINE void mark_command_header_ready_to_process(u8 command_header_index) const; - ALWAYS_INLINE void stop_command_list_processing() const; - - ALWAYS_INLINE void start_fis_receiving() const; - ALWAYS_INLINE void stop_fis_receiving() const; - - ALWAYS_INLINE void set_active_state() const; - ALWAYS_INLINE void set_sleep_state() const; - - void set_interface_state(AHCI::DeviceDetectionInitialization); - - Optional try_to_find_unused_command_header(); - - ALWAYS_INLINE bool is_interface_disabled() const { return (m_port_registers.ssts & 0xf) == 4; } - - ALWAYS_INLINE void wait_until_condition_met_or_timeout(size_t delay_in_microseconds, size_t retries, Function condition_being_met) const; - - // Data members - - EntropySource m_entropy_source; - LockRefPtr m_current_request; - Spinlock m_hard_lock {}; - Mutex m_lock { "AHCIPort"sv }; - - mutable bool m_wait_for_completion { false }; - - Vector> m_dma_buffers; - Vector> m_command_table_pages; - RefPtr m_command_list_page; - OwnPtr m_command_list_region; - RefPtr m_fis_receive_page; - LockRefPtr m_connected_device; - - u32 m_port_index; - - // Note: Ideally the AHCIController should be the only object to hold this data - // but because using the m_parent_controller means we need to take a strong ref, - // it's probably better to just "cache" this here instead. - AHCI::HBADefinedCapabilities const m_hba_capabilities; - - NonnullRefPtr const m_identify_buffer_page; - - volatile AHCI::PortRegisters& m_port_registers; - NonnullRefPtr const m_parent_controller; - AHCI::PortInterruptStatusBitField m_interrupt_status; - AHCI::PortInterruptEnableBitField m_interrupt_enable; - - LockRefPtr m_current_scatter_list; - bool m_disabled_by_firmware { false }; -}; -} diff --git a/Kernel/Devices/Storage/NVMe/NVMeController.cpp b/Kernel/Devices/Storage/NVMe/NVMeController.cpp deleted file mode 100644 index 3b9e35c6ce8..00000000000 --- a/Kernel/Devices/Storage/NVMe/NVMeController.cpp +++ /dev/null @@ -1,439 +0,0 @@ -/* - * Copyright (c) 2021, Pankaj R - * Copyright (c) 2022, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace Kernel { - -UNMAP_AFTER_INIT ErrorOr> NVMeController::try_initialize(Kernel::PCI::DeviceIdentifier const& device_identifier, bool is_queue_polled) -{ - auto controller = TRY(adopt_nonnull_ref_or_enomem(new NVMeController(device_identifier, StorageManagement::generate_relative_nvme_controller_id({})))); - TRY(controller->initialize(is_queue_polled)); - return controller; -} - -UNMAP_AFTER_INIT NVMeController::NVMeController(const PCI::DeviceIdentifier& device_identifier, u32 hardware_relative_controller_id) - : PCI::Device(const_cast(device_identifier)) - , StorageController(hardware_relative_controller_id) -{ -} - -UNMAP_AFTER_INIT ErrorOr NVMeController::initialize(bool is_queue_polled) -{ - // Nr of queues = one queue per core - auto nr_of_queues = Processor::count(); - auto queue_type = is_queue_polled ? QueueType::Polled : QueueType::IRQ; - - PCI::enable_memory_space(device_identifier()); - PCI::enable_bus_mastering(device_identifier()); - m_bar = TRY(PCI::get_bar_address(device_identifier(), PCI::HeaderType0BaseRegister::BAR0)); - static_assert(sizeof(ControllerRegister) == REG_SQ0TDBL_START); - static_assert(sizeof(NVMeSubmission) == (1 << SQ_WIDTH)); - - // Map only until doorbell register for the controller - // Queues will individually map the doorbell register respectively - m_controller_regs = TRY(Memory::map_typed_writable(m_bar)); - - auto caps = m_controller_regs->cap; - m_ready_timeout = Duration::from_milliseconds((CAP_TO(caps) + 1) * 500); // CAP.TO is in 500ms units - - calculate_doorbell_stride(); - if (queue_type == QueueType::IRQ) { - // IO queues + 1 admin queue - m_irq_type = TRY(reserve_irqs(nr_of_queues + 1, true)); - } - - TRY(create_admin_queue(queue_type)); - VERIFY(m_admin_queue_ready == true); - - VERIFY(IO_QUEUE_SIZE < MQES(caps)); - dbgln_if(NVME_DEBUG, "NVMe: IO queue depth is: {}", IO_QUEUE_SIZE); - - TRY(identify_and_init_controller()); - // Create an IO queue per core - for (u32 cpuid = 0; cpuid < nr_of_queues; ++cpuid) { - // qid is zero is used for admin queue - TRY(create_io_queue(cpuid + 1, queue_type)); - } - TRY(identify_and_init_namespaces()); - return {}; -} - -bool NVMeController::wait_for_ready(bool expected_ready_bit_value) -{ - constexpr size_t one_ms_io_delay = 1000; - auto wait_iterations = m_ready_timeout.to_milliseconds(); - - u32 expected_rdy = expected_ready_bit_value ? 1 : 0; - while (((m_controller_regs->csts >> CSTS_RDY_BIT) & 0x1) != expected_rdy) { - microseconds_delay(one_ms_io_delay); - - if (--wait_iterations == 0) { - if (((m_controller_regs->csts >> CSTS_RDY_BIT) & 0x1) != expected_rdy) { - dbgln_if(NVME_DEBUG, "NVMEController: CSTS.RDY still not set to {} after {} ms", expected_rdy, m_ready_timeout.to_milliseconds()); - return false; - } - break; - } - } - return true; -} - -ErrorOr NVMeController::reset_controller() -{ - if ((m_controller_regs->cc & (1 << CC_EN_BIT)) != 0) { - // If the EN bit is already set, we need to wait - // until the RDY bit is 1, otherwise the behavior is undefined - if (!wait_for_ready(true)) - return Error::from_errno(ETIMEDOUT); - } - - auto cc = m_controller_regs->cc; - - cc = cc & ~(1 << CC_EN_BIT); - - m_controller_regs->cc = cc; - - full_memory_barrier(); - - // Wait until the RDY bit is cleared - if (!wait_for_ready(false)) - return Error::from_errno(ETIMEDOUT); - - return {}; -} - -ErrorOr NVMeController::start_controller() -{ - if (!(m_controller_regs->cc & (1 << CC_EN_BIT))) { - // If the EN bit is not already set, we need to wait - // until the RDY bit is 0, otherwise the behavior is undefined - if (!wait_for_ready(false)) - return Error::from_errno(ETIMEDOUT); - } - - auto cc = m_controller_regs->cc; - - cc = cc | (1 << CC_EN_BIT); - cc = cc | (CQ_WIDTH << CC_IOCQES_BIT); - cc = cc | (SQ_WIDTH << CC_IOSQES_BIT); - - m_controller_regs->cc = cc; - - full_memory_barrier(); - - // Wait until the RDY bit is set - if (!wait_for_ready(true)) - return Error::from_errno(ETIMEDOUT); - - return {}; -} - -UNMAP_AFTER_INIT void NVMeController::set_admin_q_depth() -{ - // Queue depth is 0 based - u16 queue_depth = ADMIN_QUEUE_SIZE - 1; - m_controller_regs->aqa = queue_depth | (queue_depth << AQA_ACQ_SHIFT); -} - -UNMAP_AFTER_INIT ErrorOr NVMeController::identify_and_init_namespaces() -{ - - RefPtr prp_dma_buffer; - OwnPtr prp_dma_region; - auto namespace_data_struct = TRY(ByteBuffer::create_zeroed(NVMe_IDENTIFY_SIZE)); - u32 active_namespace_list[NVMe_IDENTIFY_SIZE / sizeof(u32)]; - - { - auto buffer = TRY(MM.allocate_dma_buffer_page("Identify PRP"sv, Memory::Region::Access::ReadWrite, prp_dma_buffer)); - prp_dma_region = move(buffer); - } - - // Get the active namespace - { - NVMeSubmission sub {}; - u16 status = 0; - sub.op = OP_ADMIN_IDENTIFY; - sub.identify.data_ptr.prp1 = reinterpret_cast(AK::convert_between_host_and_little_endian(prp_dma_buffer->paddr().as_ptr())); - sub.identify.cns = NVMe_CNS_ID_ACTIVE_NS & 0xff; - status = submit_admin_command(sub, true); - if (status) { - dmesgln_pci(*this, "Failed to identify active namespace command"); - return EFAULT; - } - if (void* fault_at; !safe_memcpy(active_namespace_list, prp_dma_region->vaddr().as_ptr(), NVMe_IDENTIFY_SIZE, fault_at)) { - return EFAULT; - } - } - // Get the NAMESPACE attributes - { - NVMeSubmission sub {}; - IdentifyNamespace id_ns {}; - u16 status = 0; - for (auto nsid : active_namespace_list) { - memset(prp_dma_region->vaddr().as_ptr(), 0, NVMe_IDENTIFY_SIZE); - // Invalid NS - if (nsid == 0) - break; - sub.op = OP_ADMIN_IDENTIFY; - sub.identify.data_ptr.prp1 = reinterpret_cast(AK::convert_between_host_and_little_endian(prp_dma_buffer->paddr().as_ptr())); - sub.identify.cns = NVMe_CNS_ID_NS & 0xff; - sub.identify.nsid = nsid; - status = submit_admin_command(sub, true); - if (status) { - dmesgln_pci(*this, "Failed identify namespace with nsid {}", nsid); - return EFAULT; - } - static_assert(sizeof(IdentifyNamespace) == NVMe_IDENTIFY_SIZE); - if (void* fault_at; !safe_memcpy(&id_ns, prp_dma_region->vaddr().as_ptr(), NVMe_IDENTIFY_SIZE, fault_at)) { - return EFAULT; - } - auto [block_counts, lba_size] = get_ns_features(id_ns); - auto block_size = 1 << lba_size; - - dbgln_if(NVME_DEBUG, "NVMe: Block count is {} and Block size is {}", block_counts, block_size); - - m_namespaces.append(TRY(NVMeNameSpace::try_create(*this, m_queues, nsid, block_counts, block_size))); - m_device_count++; - dbgln_if(NVME_DEBUG, "NVMe: Initialized namespace with NSID: {}", nsid); - } - } - return {}; -} - -ErrorOr NVMeController::identify_and_init_controller() -{ - RefPtr prp_dma_buffer; - OwnPtr prp_dma_region; - IdentifyController ctrl {}; - - { - auto buffer = TRY(MM.allocate_dma_buffer_page("Identify PRP"sv, Memory::Region::Access::ReadWrite, prp_dma_buffer)); - prp_dma_region = move(buffer); - } - - // Check if the controller supports shadow doorbell - { - NVMeSubmission sub {}; - u16 status = 0; - sub.op = OP_ADMIN_IDENTIFY; - sub.identify.data_ptr.prp1 = reinterpret_cast(AK::convert_between_host_and_little_endian(prp_dma_buffer->paddr().as_ptr())); - sub.identify.cns = NVMe_CNS_ID_CTRL & 0xff; - status = submit_admin_command(sub, true); - if (status) { - dmesgln_pci(*this, "Failed to identify active namespace command"); - return EFAULT; - } - if (void* fault_at; !safe_memcpy(&ctrl, prp_dma_region->vaddr().as_ptr(), NVMe_IDENTIFY_SIZE, fault_at)) { - return EFAULT; - } - } - - if (ctrl.oacs & ID_CTRL_SHADOW_DBBUF_MASK) { - OwnPtr dbbuf_dma_region; - OwnPtr eventidx_dma_region; - - { - auto buffer = TRY(MM.allocate_dma_buffer_page("shadow dbbuf"sv, Memory::Region::Access::ReadWrite, m_dbbuf_shadow_page)); - dbbuf_dma_region = move(buffer); - memset(dbbuf_dma_region->vaddr().as_ptr(), 0, PAGE_SIZE); - } - - { - auto buffer = TRY(MM.allocate_dma_buffer_page("eventidx"sv, Memory::Region::Access::ReadWrite, m_dbbuf_eventidx_page)); - eventidx_dma_region = move(buffer); - memset(eventidx_dma_region->vaddr().as_ptr(), 0, PAGE_SIZE); - } - - { - NVMeSubmission sub {}; - sub.op = OP_ADMIN_DBBUF_CONFIG; - sub.dbbuf_cmd.data_ptr.prp1 = reinterpret_cast(AK::convert_between_host_and_little_endian(m_dbbuf_shadow_page->paddr().as_ptr())); - sub.dbbuf_cmd.data_ptr.prp2 = reinterpret_cast(AK::convert_between_host_and_little_endian(m_dbbuf_eventidx_page->paddr().as_ptr())); - - submit_admin_command(sub, true); - } - - dbgln_if(NVME_DEBUG, "Shadow doorbell Enabled!"); - } - - return {}; -} - -UNMAP_AFTER_INIT NVMeController::NSFeatures NVMeController::get_ns_features(IdentifyNamespace& identify_data_struct) -{ - auto flbas = identify_data_struct.flbas & FLBA_SIZE_MASK; - auto namespace_size = identify_data_struct.nsze; - auto lba_format = identify_data_struct.lbaf[flbas]; - - auto lba_size = (lba_format & LBA_SIZE_MASK) >> 16; - return { namespace_size, static_cast(lba_size) }; -} - -LockRefPtr NVMeController::device(u32 index) const -{ - return m_namespaces.at(index); -} - -size_t NVMeController::devices_count() const -{ - return m_device_count; -} - -ErrorOr NVMeController::reset() -{ - TRY(reset_controller()); - TRY(start_controller()); - return {}; -} - -void NVMeController::complete_current_request([[maybe_unused]] AsyncDeviceRequest::RequestResult result) -{ - VERIFY_NOT_REACHED(); -} - -UNMAP_AFTER_INIT ErrorOr NVMeController::create_admin_queue(QueueType queue_type) -{ - OwnPtr cq_dma_region; - Vector> cq_dma_pages; - OwnPtr sq_dma_region; - Vector> sq_dma_pages; - set_admin_q_depth(); - auto cq_size = round_up_to_power_of_two(CQ_SIZE(ADMIN_QUEUE_SIZE), 4096); - auto sq_size = round_up_to_power_of_two(SQ_SIZE(ADMIN_QUEUE_SIZE), 4096); - auto maybe_error = reset_controller(); - if (maybe_error.is_error()) { - dmesgln_pci(*this, "Failed to reset the NVMe controller"); - return maybe_error; - } - { - auto buffer = TRY(MM.allocate_dma_buffer_pages(cq_size, "Admin CQ queue"sv, Memory::Region::Access::ReadWrite, cq_dma_pages)); - cq_dma_region = move(buffer); - } - - // Phase bit is important to determine completion, so zero out the space - // so that we don't get any garbage phase bit value - memset(cq_dma_region->vaddr().as_ptr(), 0, cq_size); - - { - auto buffer = TRY(MM.allocate_dma_buffer_pages(sq_size, "Admin SQ queue"sv, Memory::Region::Access::ReadWrite, sq_dma_pages)); - sq_dma_region = move(buffer); - } - auto doorbell_regs = TRY(Memory::map_typed_writable(m_bar.offset(REG_SQ0TDBL_START))); - Doorbell doorbell = { - .mmio_reg = move(doorbell_regs), - .dbbuf_shadow = {}, - .dbbuf_eventidx = {}, - }; - - m_controller_regs->acq = reinterpret_cast(AK::convert_between_host_and_little_endian(cq_dma_pages.first()->paddr().as_ptr())); - m_controller_regs->asq = reinterpret_cast(AK::convert_between_host_and_little_endian(sq_dma_pages.first()->paddr().as_ptr())); - - Optional irq; - if (queue_type == QueueType::IRQ) - irq = TRY(allocate_irq(0)); // Admin queue always uses the 0th index when using MSIx - - maybe_error = start_controller(); - if (maybe_error.is_error()) { - dmesgln_pci(*this, "Failed to restart the NVMe controller"); - return maybe_error; - } - set_admin_queue_ready_flag(); - m_admin_queue = TRY(NVMeQueue::try_create(*this, 0, irq, ADMIN_QUEUE_SIZE, move(cq_dma_region), move(sq_dma_region), move(doorbell), queue_type)); - - dbgln_if(NVME_DEBUG, "NVMe: Admin queue created"); - return {}; -} - -UNMAP_AFTER_INIT ErrorOr NVMeController::create_io_queue(u8 qid, QueueType queue_type) -{ - OwnPtr cq_dma_region; - Vector> cq_dma_pages; - OwnPtr sq_dma_region; - Vector> sq_dma_pages; - auto cq_size = round_up_to_power_of_two(CQ_SIZE(IO_QUEUE_SIZE), 4096); - auto sq_size = round_up_to_power_of_two(SQ_SIZE(IO_QUEUE_SIZE), 4096); - - { - auto buffer = TRY(MM.allocate_dma_buffer_pages(cq_size, "IO CQ queue"sv, Memory::Region::Access::ReadWrite, cq_dma_pages)); - cq_dma_region = move(buffer); - } - - // Phase bit is important to determine completion, so zero out the space - // so that we don't get any garbage phase bit value - memset(cq_dma_region->vaddr().as_ptr(), 0, cq_size); - - { - auto buffer = TRY(MM.allocate_dma_buffer_pages(sq_size, "IO SQ queue"sv, Memory::Region::Access::ReadWrite, sq_dma_pages)); - sq_dma_region = move(buffer); - } - - { - NVMeSubmission sub {}; - sub.op = OP_ADMIN_CREATE_COMPLETION_QUEUE; - sub.create_cq.prp1 = reinterpret_cast(AK::convert_between_host_and_little_endian(cq_dma_pages.first()->paddr().as_ptr())); - sub.create_cq.cqid = qid; - // The queue size is 0 based - sub.create_cq.qsize = AK::convert_between_host_and_little_endian(IO_QUEUE_SIZE - 1); - auto flags = (queue_type == QueueType::IRQ) ? QUEUE_IRQ_ENABLED : QUEUE_IRQ_DISABLED; - flags |= QUEUE_PHY_CONTIGUOUS; - // When using MSIx interrupts, qid is used as an index into the interrupt table - if (m_irq_type.has_value() && m_irq_type.value() != PCI::InterruptType::PIN) - sub.create_cq.irq_vector = qid; - else - sub.create_cq.irq_vector = 0; - sub.create_cq.cq_flags = AK::convert_between_host_and_little_endian(flags & 0xFFFF); - submit_admin_command(sub, true); - } - { - NVMeSubmission sub {}; - sub.op = OP_ADMIN_CREATE_SUBMISSION_QUEUE; - sub.create_sq.prp1 = reinterpret_cast(AK::convert_between_host_and_little_endian(sq_dma_pages.first()->paddr().as_ptr())); - sub.create_sq.sqid = qid; - // The queue size is 0 based - sub.create_sq.qsize = AK::convert_between_host_and_little_endian(IO_QUEUE_SIZE - 1); - auto flags = QUEUE_PHY_CONTIGUOUS; - sub.create_sq.cqid = qid; - sub.create_sq.sq_flags = AK::convert_between_host_and_little_endian(flags); - submit_admin_command(sub, true); - } - - auto queue_doorbell_offset = (2 * qid) * (4 << m_dbl_stride); - auto doorbell_regs = TRY(Memory::map_typed_writable(m_bar.offset(REG_SQ0TDBL_START + queue_doorbell_offset))); - Memory::TypedMapping shadow_doorbell_regs {}; - Memory::TypedMapping eventidx_doorbell_regs {}; - - if (!m_dbbuf_shadow_page.is_null()) { - shadow_doorbell_regs = TRY(Memory::map_typed_writable(m_dbbuf_shadow_page->paddr().offset(queue_doorbell_offset))); - eventidx_doorbell_regs = TRY(Memory::map_typed_writable(m_dbbuf_eventidx_page->paddr().offset(queue_doorbell_offset))); - } - - Doorbell doorbell = { - .mmio_reg = move(doorbell_regs), - .dbbuf_shadow = move(shadow_doorbell_regs), - .dbbuf_eventidx = move(eventidx_doorbell_regs), - }; - - auto irq = TRY(allocate_irq(qid)); - - m_queues.append(TRY(NVMeQueue::try_create(*this, qid, irq, IO_QUEUE_SIZE, move(cq_dma_region), move(sq_dma_region), move(doorbell), queue_type))); - dbgln_if(NVME_DEBUG, "NVMe: Created IO Queue with QID{}", m_queues.size()); - return {}; -} -} diff --git a/Kernel/Devices/Storage/NVMe/NVMeController.h b/Kernel/Devices/Storage/NVMe/NVMeController.h deleted file mode 100644 index e35c758dab5..00000000000 --- a/Kernel/Devices/Storage/NVMe/NVMeController.h +++ /dev/null @@ -1,90 +0,0 @@ -/* - * Copyright (c) 2021, Pankaj R - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace Kernel { - -class NVMeController : public PCI::Device - , public StorageController { -public: - static ErrorOr> try_initialize(PCI::DeviceIdentifier const&, bool is_queue_polled); - ErrorOr initialize(bool is_queue_polled); - LockRefPtr device(u32 index) const override; - size_t devices_count() const override; - virtual StringView device_name() const override { return "NVMeController"sv; } - -protected: - ErrorOr reset(); - void complete_current_request(AsyncDeviceRequest::RequestResult result) override; - -public: - ErrorOr reset_controller(); - ErrorOr start_controller(); - - u16 submit_admin_command(NVMeSubmission& sub, bool sync = false) - { - // First queue is always the admin queue - if (sync) { - return m_admin_queue->submit_sync_sqe(sub); - } - m_admin_queue->submit_sqe(sub); - return 0; - } - - bool is_admin_queue_ready() { return m_admin_queue_ready; } - void set_admin_queue_ready_flag() { m_admin_queue_ready = true; } - -private: - struct NSFeatures { - u64 namespace_size; - u8 lba_size; - }; - - NVMeController(PCI::DeviceIdentifier const&, u32 hardware_relative_controller_id); - - void set_admin_q_depth(); - ErrorOr identify_and_init_namespaces(); - ErrorOr identify_and_init_controller(); - NSFeatures get_ns_features(IdentifyNamespace& identify_data_struct); - ErrorOr create_admin_queue(QueueType queue_type); - ErrorOr create_io_queue(u8 qid, QueueType queue_type); - void calculate_doorbell_stride() - { - m_dbl_stride = (m_controller_regs->cap >> CAP_DBL_SHIFT) & CAP_DBL_MASK; - } - bool wait_for_ready(bool); - -private: - LockRefPtr m_admin_queue; - Vector> m_queues; - Vector> m_namespaces; - Memory::TypedMapping m_controller_regs; - RefPtr m_dbbuf_shadow_page; - RefPtr m_dbbuf_eventidx_page; - bool m_admin_queue_ready { false }; - size_t m_device_count { 0 }; - AK::Duration m_ready_timeout; - PhysicalAddress m_bar { 0 }; - u8 m_dbl_stride { 0 }; - Optional m_irq_type; - QueueType m_queue_type { QueueType::IRQ }; - static Atomic s_controller_id; -}; -} diff --git a/Kernel/Devices/Storage/NVMe/NVMeDefinitions.h b/Kernel/Devices/Storage/NVMe/NVMeDefinitions.h deleted file mode 100644 index 890ab84a3fa..00000000000 --- a/Kernel/Devices/Storage/NVMe/NVMeDefinitions.h +++ /dev/null @@ -1,229 +0,0 @@ -/* - * Copyright (c) 2021, Pankaj R - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include - -struct ControllerRegister { - u64 cap; - u32 vs; - u32 intms; - u32 intmc; - u32 cc; - u32 rsvd1; - u32 csts; - u32 nssr; - u32 aqa; - u64 asq; - u64 acq; - u64 rsvd2[505]; -}; - -struct IdentifyNamespace { - u64 nsze; - u64 ncap; - u8 rsdv1[10]; - u8 flbas; - u8 rsvd2[100]; - u32 lbaf[16]; - u64 rsvd3[488]; -}; - -// FIXME: For now only one value is used. Once we start using -// more values from id_ctrl command, use separate member variables -// instead of using rsd array. -struct IdentifyController { - u8 rsdv1[256]; - u16 oacs; - u8 rsdv2[3838]; -}; - -// DOORBELL -static constexpr u32 REG_SQ0TDBL_START = 0x1000; -static constexpr u32 REG_SQ0TDBL_END = 0x1003; -static constexpr u8 DBL_REG_SIZE = 8; -static constexpr u16 ID_CTRL_SHADOW_DBBUF_MASK = 0x0100; - -// CAP -static constexpr u8 CAP_DBL_SHIFT = 32; -static constexpr u8 CAP_DBL_MASK = 0xf; -static constexpr u8 CAP_TO_SHIFT = 24; -static constexpr u64 CAP_TO_MASK = 0xffu << CAP_TO_SHIFT; -static constexpr u32 MQES(u64 cap) -{ - return (cap & 0xffff) + 1; -} - -static constexpr u32 CAP_TO(u64 cap) -{ - return (cap & CAP_TO_MASK) >> CAP_TO_SHIFT; -} - -// CC – Controller Configuration -static constexpr u8 CC_EN_BIT = 0x0; -static constexpr u8 CSTS_RDY_BIT = 0x0; -static constexpr u8 CSTS_SHST_SHIFT = 2; -static constexpr u32 CSTS_SHST_MASK = 0x3 << CSTS_SHST_SHIFT; -static constexpr u8 CC_IOSQES_BIT = 16; -static constexpr u8 CC_IOCQES_BIT = 20; - -static constexpr u32 CSTS_SHST(u32 x) -{ - return (x & CSTS_SHST_MASK) >> CSTS_SHST_SHIFT; -} - -static constexpr u16 AQA_ACQ_SHIFT = 16; -static constexpr u8 CQ_WIDTH = 4; // CQ is 16 bytes(2^4) in size. -static constexpr u8 SQ_WIDTH = 6; // SQ size is 64 bytes(2^6) in size. -static constexpr u16 CQ_SIZE(u16 q_depth) -{ - return q_depth << CQ_WIDTH; -} -static constexpr u16 SQ_SIZE(u16 q_depth) -{ - return q_depth << SQ_WIDTH; -} -static constexpr u8 PHASE_TAG(u16 x) -{ - return x & 0x1; -} -static constexpr u16 CQ_STATUS_FIELD_MASK = 0xfffe; -static constexpr u16 CQ_STATUS_FIELD(u16 x) -{ - return (x & CQ_STATUS_FIELD_MASK) >> 1; -} - -static constexpr u16 ADMIN_QUEUE_SIZE = 2; -static constexpr u16 IO_QUEUE_SIZE = 64; // TODO:Need to be configurable - -// IDENTIFY -static constexpr u16 NVMe_IDENTIFY_SIZE = 4096; -static constexpr u8 NVMe_CNS_ID_NS = 0x0; -static constexpr u8 NVMe_CNS_ID_CTRL = 0x1; -static constexpr u8 NVMe_CNS_ID_ACTIVE_NS = 0x2; -static constexpr u8 FLBA_SIZE_INDEX = 26; -static constexpr u8 FLBA_SIZE_MASK = 0xf; -static constexpr u8 LBA_FORMAT_SUPPORT_INDEX = 128; -static constexpr u32 LBA_SIZE_MASK = 0x00ff0000; - -// OPCODES -// ADMIN COMMAND SET -enum AdminCommandOpCode { - OP_ADMIN_CREATE_COMPLETION_QUEUE = 0x5, - OP_ADMIN_CREATE_SUBMISSION_QUEUE = 0x1, - OP_ADMIN_IDENTIFY = 0x6, - OP_ADMIN_DBBUF_CONFIG = 0x7C, -}; - -// IO opcodes -enum IOCommandOpcode { - OP_NVME_WRITE = 0x1, - OP_NVME_READ = 0x2 -}; - -// FLAGS -static constexpr u8 QUEUE_PHY_CONTIGUOUS = (1 << 0); -static constexpr u8 QUEUE_IRQ_ENABLED = (1 << 1); -static constexpr u8 QUEUE_IRQ_DISABLED = (0 << 1); - -struct [[gnu::packed]] NVMeCompletion { - LittleEndian cmd_spec; - LittleEndian res; - - LittleEndian sq_head; /* how much of this queue may be reclaimed */ - LittleEndian sq_id; /* submission queue that generated this entry */ - - u16 command_id; /* of the command which completed */ - LittleEndian status; /* did the command fail, and if so, why? */ -}; - -struct [[gnu::packed]] DataPtr { - LittleEndian prp1; - LittleEndian prp2; -}; - -struct [[gnu::packed]] NVMeGenericCmd { - LittleEndian nsid; - LittleEndian rsvd; - LittleEndian metadata; - struct DataPtr data_ptr; - LittleEndian cdw10; - LittleEndian cdw11; - LittleEndian cdw12; - LittleEndian cdw13; - LittleEndian cdw14; - LittleEndian cdw15; -}; - -struct [[gnu::packed]] NVMeRWCmd { - LittleEndian nsid; - LittleEndian rsvd; - LittleEndian metadata; - struct DataPtr data_ptr; - LittleEndian slba; - LittleEndian length; - LittleEndian control; - LittleEndian dsmgmt; - LittleEndian reftag; - LittleEndian apptag; - LittleEndian appmask; -}; - -struct [[gnu::packed]] NVMeIdentifyCmd { - LittleEndian nsid; - LittleEndian rsvd1[2]; - struct DataPtr data_ptr; - u8 cns; - u8 rsvd2; - LittleEndian ctrlid; - u8 rsvd3[3]; - u8 csi; - u64 rsvd4[2]; -}; - -struct [[gnu::packed]] NVMeCreateCQCmd { - u32 rsvd1[5]; - LittleEndian prp1; - u64 rsvd2; - LittleEndian cqid; - LittleEndian qsize; - LittleEndian cq_flags; - LittleEndian irq_vector; - u64 rsvd12[2]; -}; - -struct [[gnu::packed]] NVMeCreateSQCmd { - u32 rsvd1[5]; - LittleEndian prp1; - u64 rsvd2; - LittleEndian sqid; - LittleEndian qsize; - LittleEndian sq_flags; - LittleEndian cqid; - u64 rsvd12[2]; -}; - -struct [[gnu::packed]] NVMeDBBUFCmd { - u32 rsvd1[5]; - struct DataPtr data_ptr; - u32 rsvd12[6]; -}; - -struct [[gnu::packed]] NVMeSubmission { - u8 op; - u8 flags; - LittleEndian cmdid; - union [[gnu::packed]] { - NVMeGenericCmd generic; - NVMeIdentifyCmd identify; - NVMeRWCmd rw; - NVMeCreateCQCmd create_cq; - NVMeCreateSQCmd create_sq; - NVMeDBBUFCmd dbbuf_cmd; - }; -}; diff --git a/Kernel/Devices/Storage/NVMe/NVMeInterruptQueue.cpp b/Kernel/Devices/Storage/NVMe/NVMeInterruptQueue.cpp deleted file mode 100644 index aad90f719ce..00000000000 --- a/Kernel/Devices/Storage/NVMe/NVMeInterruptQueue.cpp +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright (c) 2022, Pankaj R - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include - -namespace Kernel { - -ErrorOr> NVMeInterruptQueue::try_create(PCI::Device& device, NonnullOwnPtr rw_dma_region, NonnullRefPtr rw_dma_page, u16 qid, u8 irq, u32 q_depth, OwnPtr cq_dma_region, OwnPtr sq_dma_region, Doorbell db_regs) -{ - auto queue = TRY(adopt_nonnull_lock_ref_or_enomem(new (nothrow) NVMeInterruptQueue(device, move(rw_dma_region), rw_dma_page, qid, irq, q_depth, move(cq_dma_region), move(sq_dma_region), move(db_regs)))); - queue->initialize_interrupt_queue(); - return queue; -} - -UNMAP_AFTER_INIT NVMeInterruptQueue::NVMeInterruptQueue(PCI::Device& device, NonnullOwnPtr rw_dma_region, NonnullRefPtr rw_dma_page, u16 qid, u8 irq, u32 q_depth, OwnPtr cq_dma_region, OwnPtr sq_dma_region, Doorbell db_regs) - : NVMeQueue(move(rw_dma_region), rw_dma_page, qid, q_depth, move(cq_dma_region), move(sq_dma_region), move(db_regs)) - , PCI::IRQHandler(device, irq) -{ -} - -void NVMeInterruptQueue::initialize_interrupt_queue() -{ - enable_irq(); -} - -bool NVMeInterruptQueue::handle_irq(RegisterState const&) -{ - return process_cq() ? true : false; -} - -void NVMeInterruptQueue::submit_sqe(NVMeSubmission& sub) -{ - NVMeQueue::submit_sqe(sub); -} - -void NVMeInterruptQueue::complete_current_request(u16 cmdid, u16 status) -{ - auto work_item_creation_result = g_io_work->try_queue([this, cmdid, status]() { - NVMeQueue::complete_current_request(cmdid, status); - }); - - if (work_item_creation_result.is_error()) { - m_requests.with([cmdid, status](auto& requests) { - auto& request_pdu = requests.get(cmdid).release_value(); - auto current_request = request_pdu.request; - - current_request->complete(AsyncDeviceRequest::OutOfMemory); - if (request_pdu.end_io_handler) - request_pdu.end_io_handler(status); - request_pdu.clear(); - }); - } -} -} diff --git a/Kernel/Devices/Storage/NVMe/NVMeInterruptQueue.h b/Kernel/Devices/Storage/NVMe/NVMeInterruptQueue.h deleted file mode 100644 index a17dad3b28a..00000000000 --- a/Kernel/Devices/Storage/NVMe/NVMeInterruptQueue.h +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright (c) 2022, Pankaj R - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include - -namespace Kernel { - -class NVMeInterruptQueue : public NVMeQueue - , public PCI::IRQHandler { -public: - static ErrorOr> try_create(PCI::Device& device, NonnullOwnPtr rw_dma_region, NonnullRefPtr rw_dma_page, u16 qid, u8 irq, u32 q_depth, OwnPtr cq_dma_region, OwnPtr sq_dma_region, Doorbell db_regs); - void submit_sqe(NVMeSubmission& submission) override; - virtual ~NVMeInterruptQueue() override {}; - virtual StringView purpose() const override { return "NVMe"sv; } - void initialize_interrupt_queue(); - -protected: - NVMeInterruptQueue(PCI::Device& device, NonnullOwnPtr rw_dma_region, NonnullRefPtr rw_dma_page, u16 qid, u8 irq, u32 q_depth, OwnPtr cq_dma_region, OwnPtr sq_dma_region, Doorbell db_regs); - -private: - virtual void complete_current_request(u16 cmdid, u16 status) override; - bool handle_irq(RegisterState const&) override; -}; -} diff --git a/Kernel/Devices/Storage/NVMe/NVMeNameSpace.cpp b/Kernel/Devices/Storage/NVMe/NVMeNameSpace.cpp deleted file mode 100644 index 64449103107..00000000000 --- a/Kernel/Devices/Storage/NVMe/NVMeNameSpace.cpp +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright (c) 2021, Pankaj R - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include - -namespace Kernel { - -UNMAP_AFTER_INIT ErrorOr> NVMeNameSpace::try_create(NVMeController const& controller, Vector> queues, u16 nsid, size_t storage_size, size_t lba_size) -{ - auto device = TRY(DeviceManagement::try_create_device(StorageDevice::LUNAddress { controller.controller_id(), nsid, 0 }, controller.hardware_relative_controller_id(), move(queues), storage_size, lba_size, nsid)); - return device; -} - -UNMAP_AFTER_INIT NVMeNameSpace::NVMeNameSpace(LUNAddress logical_unit_number_address, u32 hardware_relative_controller_id, Vector> queues, size_t max_addresable_block, size_t lba_size, u16 nsid) - : StorageDevice(logical_unit_number_address, hardware_relative_controller_id, lba_size, max_addresable_block) - , m_nsid(nsid) - , m_queues(move(queues)) -{ -} - -void NVMeNameSpace::start_request(AsyncBlockDeviceRequest& request) -{ - auto index = Processor::current_id(); - auto& queue = m_queues.at(index); - // TODO: For now we support only IO transfers of size PAGE_SIZE (Going along with the current constraint in the block layer) - // Eventually remove this constraint by using the PRP2 field in the submission struct and remove block layer constraint for NVMe driver. - VERIFY(request.block_count() <= (PAGE_SIZE / block_size())); - - if (request.request_type() == AsyncBlockDeviceRequest::Read) { - queue->read(request, m_nsid, request.block_index(), request.block_count()); - } else { - queue->write(request, m_nsid, request.block_index(), request.block_count()); - } -} -} diff --git a/Kernel/Devices/Storage/NVMe/NVMeNameSpace.h b/Kernel/Devices/Storage/NVMe/NVMeNameSpace.h deleted file mode 100644 index 342c7d24e2f..00000000000 --- a/Kernel/Devices/Storage/NVMe/NVMeNameSpace.h +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright (c) 2021, Pankaj R - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace Kernel { - -class NVMeController; -class NVMeNameSpace : public StorageDevice { - friend class DeviceManagement; - -public: - static ErrorOr> try_create(NVMeController const&, Vector> queues, u16 nsid, size_t storage_size, size_t lba_size); - - CommandSet command_set() const override { return CommandSet::NVMe; } - void start_request(AsyncBlockDeviceRequest& request) override; - -private: - NVMeNameSpace(LUNAddress, u32 hardware_relative_controller_id, Vector> queues, size_t storage_size, size_t lba_size, u16 nsid); - - u16 m_nsid; - Vector> m_queues; -}; - -} diff --git a/Kernel/Devices/Storage/NVMe/NVMePollQueue.cpp b/Kernel/Devices/Storage/NVMe/NVMePollQueue.cpp deleted file mode 100644 index 4c401293cd4..00000000000 --- a/Kernel/Devices/Storage/NVMe/NVMePollQueue.cpp +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright (c) 2022, Pankaj R - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include - -namespace Kernel { - -ErrorOr> NVMePollQueue::try_create(NonnullOwnPtr rw_dma_region, NonnullRefPtr rw_dma_page, u16 qid, u32 q_depth, OwnPtr cq_dma_region, OwnPtr sq_dma_region, Doorbell db_regs) -{ - return TRY(adopt_nonnull_lock_ref_or_enomem(new (nothrow) NVMePollQueue(move(rw_dma_region), rw_dma_page, qid, q_depth, move(cq_dma_region), move(sq_dma_region), move(db_regs)))); -} - -UNMAP_AFTER_INIT NVMePollQueue::NVMePollQueue(NonnullOwnPtr rw_dma_region, NonnullRefPtr rw_dma_page, u16 qid, u32 q_depth, OwnPtr cq_dma_region, OwnPtr sq_dma_region, Doorbell db_regs) - : NVMeQueue(move(rw_dma_region), rw_dma_page, qid, q_depth, move(cq_dma_region), move(sq_dma_region), move(db_regs)) -{ -} - -void NVMePollQueue::submit_sqe(NVMeSubmission& sub) -{ - NVMeQueue::submit_sqe(sub); - SpinlockLocker lock_cq(m_cq_lock); - while (!process_cq()) { - microseconds_delay(1); - } -} - -} diff --git a/Kernel/Devices/Storage/NVMe/NVMePollQueue.h b/Kernel/Devices/Storage/NVMe/NVMePollQueue.h deleted file mode 100644 index 5c6ccad8304..00000000000 --- a/Kernel/Devices/Storage/NVMe/NVMePollQueue.h +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright (c) 2022, Pankaj R - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include - -namespace Kernel { - -class NVMePollQueue : public NVMeQueue { -public: - static ErrorOr> try_create(NonnullOwnPtr rw_dma_region, NonnullRefPtr rw_dma_page, u16 qid, u32 q_depth, OwnPtr cq_dma_region, OwnPtr sq_dma_region, Doorbell db_regs); - void submit_sqe(NVMeSubmission& submission) override; - virtual ~NVMePollQueue() override {}; - -protected: - NVMePollQueue(NonnullOwnPtr rw_dma_region, NonnullRefPtr rw_dma_page, u16 qid, u32 q_depth, OwnPtr cq_dma_region, OwnPtr sq_dma_region, Doorbell db_regs); - -private: - Spinlock m_cq_lock {}; -}; -} diff --git a/Kernel/Devices/Storage/NVMe/NVMeQueue.cpp b/Kernel/Devices/Storage/NVMe/NVMeQueue.cpp deleted file mode 100644 index db4096fff46..00000000000 --- a/Kernel/Devices/Storage/NVMe/NVMeQueue.cpp +++ /dev/null @@ -1,208 +0,0 @@ -/* - * Copyright (c) 2021, Pankaj R - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include -#include - -namespace Kernel { -ErrorOr> NVMeQueue::try_create(NVMeController& device, u16 qid, Optional irq, u32 q_depth, OwnPtr cq_dma_region, OwnPtr sq_dma_region, Doorbell db_regs, QueueType queue_type) -{ - // Note: Allocate DMA region for RW operation. For now the requests don't exceed more than 4096 bytes (Storage device takes care of it) - RefPtr rw_dma_page; - auto rw_dma_region = TRY(MM.allocate_dma_buffer_page("NVMe Queue Read/Write DMA"sv, Memory::Region::Access::ReadWrite, rw_dma_page)); - - if (rw_dma_page.is_null()) - return ENOMEM; - - if (queue_type == QueueType::Polled) { - auto queue = NVMePollQueue::try_create(move(rw_dma_region), rw_dma_page.release_nonnull(), qid, q_depth, move(cq_dma_region), move(sq_dma_region), move(db_regs)); - return queue; - } - - auto queue = NVMeInterruptQueue::try_create(device, move(rw_dma_region), rw_dma_page.release_nonnull(), qid, irq.release_value(), q_depth, move(cq_dma_region), move(sq_dma_region), move(db_regs)); - return queue; -} - -UNMAP_AFTER_INIT NVMeQueue::NVMeQueue(NonnullOwnPtr rw_dma_region, Memory::PhysicalRAMPage const& rw_dma_page, u16 qid, u32 q_depth, OwnPtr cq_dma_region, OwnPtr sq_dma_region, Doorbell db_regs) - : m_rw_dma_region(move(rw_dma_region)) - , m_qid(qid) - , m_admin_queue(qid == 0) - , m_qdepth(q_depth) - , m_cq_dma_region(move(cq_dma_region)) - , m_sq_dma_region(move(sq_dma_region)) - , m_db_regs(move(db_regs)) - , m_rw_dma_page(rw_dma_page) - -{ - m_requests.with([q_depth](auto& requests) { - requests.try_ensure_capacity(q_depth).release_value_but_fixme_should_propagate_errors(); - }); - m_sqe_array = { reinterpret_cast(m_sq_dma_region->vaddr().as_ptr()), m_qdepth }; - m_cqe_array = { reinterpret_cast(m_cq_dma_region->vaddr().as_ptr()), m_qdepth }; -} - -bool NVMeQueue::cqe_available() -{ - return PHASE_TAG(m_cqe_array[m_cq_head].status) == m_cq_valid_phase; -} - -void NVMeQueue::update_cqe_head() -{ - // To prevent overflow, use a temp variable - u32 temp_cq_head = m_cq_head + 1; - if (temp_cq_head == m_qdepth) { - m_cq_head = 0; - m_cq_valid_phase ^= 1; - } else { - m_cq_head = temp_cq_head; - } -} - -u32 NVMeQueue::process_cq() -{ - u32 nr_of_processed_cqes = 0; - m_requests.with([this, &nr_of_processed_cqes](auto& requests) { - while (cqe_available()) { - u16 status; - u16 cmdid; - ++nr_of_processed_cqes; - status = CQ_STATUS_FIELD(m_cqe_array[m_cq_head].status); - cmdid = m_cqe_array[m_cq_head].command_id; - dbgln_if(NVME_DEBUG, "NVMe: Completion with status {:x} and command identifier {}. CQ_HEAD: {}", status, cmdid, m_cq_head); - - if (!requests.contains(cmdid)) { - dmesgln("Bogus cmd id: {}", cmdid); - VERIFY_NOT_REACHED(); - } - complete_current_request(cmdid, status); - update_cqe_head(); - } - }); - if (nr_of_processed_cqes) { - update_cq_doorbell(); - } - return nr_of_processed_cqes; -} - -void NVMeQueue::submit_sqe(NVMeSubmission& sub) -{ - SpinlockLocker lock(m_sq_lock); - - memcpy(&m_sqe_array[m_sq_tail], &sub, sizeof(NVMeSubmission)); - { - u32 temp_sq_tail = m_sq_tail + 1; - if (temp_sq_tail == m_qdepth) - m_sq_tail = 0; - else - m_sq_tail = temp_sq_tail; - } - - dbgln_if(NVME_DEBUG, "NVMe: Submission with command identifier {}. SQ_TAIL: {}", sub.cmdid, m_sq_tail); - update_sq_doorbell(); -} - -void NVMeQueue::complete_current_request(u16 cmdid, u16 status) -{ - m_requests.with([this, cmdid, status](auto& requests) { - auto& request_pdu = requests.get(cmdid).release_value(); - auto current_request = request_pdu.request; - AsyncDeviceRequest::RequestResult req_result = AsyncDeviceRequest::Success; - - ScopeGuard guard = [&req_result, status, &request_pdu] { - if (request_pdu.request) - request_pdu.request->complete(req_result); - if (request_pdu.end_io_handler) - request_pdu.end_io_handler(status); - request_pdu.clear(); - }; - - // There can be submission without any request associated with it such as with - // admin queue commands during init. If there is no request, we are done - if (!current_request) - return; - - if (status) { - req_result = AsyncBlockDeviceRequest::Failure; - return; - } - - if (current_request->request_type() == AsyncBlockDeviceRequest::RequestType::Read) { - if (auto result = current_request->write_to_buffer(current_request->buffer(), m_rw_dma_region->vaddr().as_ptr(), current_request->buffer_size()); result.is_error()) { - req_result = AsyncBlockDeviceRequest::MemoryFault; - return; - } - } - }); -} - -u16 NVMeQueue::submit_sync_sqe(NVMeSubmission& sub) -{ - // For now let's use sq tail as a unique command id. - u16 cmd_status; - u16 cid = get_request_cid(); - sub.cmdid = cid; - - m_requests.with([this, &sub, &cmd_status](auto& requests) { - requests.set(sub.cmdid, { nullptr, [this, &cmd_status](u16 status) mutable { cmd_status = status; m_sync_wait_queue.wake_all(); } }); - }); - submit_sqe(sub); - - // FIXME: Only sync submissions (usually used for admin commands) use a WaitQueue based IO. Eventually we need to - // move this logic into the block layer instead of sprinkling them in the driver code. - m_sync_wait_queue.wait_forever("NVMe sync submit"sv); - return cmd_status; -} - -void NVMeQueue::read(AsyncBlockDeviceRequest& request, u16 nsid, u64 index, u32 count) -{ - NVMeSubmission sub {}; - sub.op = OP_NVME_READ; - sub.rw.nsid = nsid; - sub.rw.slba = AK::convert_between_host_and_little_endian(index); - // No. of lbas is 0 based - sub.rw.length = AK::convert_between_host_and_little_endian((count - 1) & 0xFFFF); - sub.rw.data_ptr.prp1 = reinterpret_cast(AK::convert_between_host_and_little_endian(m_rw_dma_page->paddr().as_ptr())); - sub.cmdid = get_request_cid(); - - m_requests.with([&sub, &request](auto& requests) { - requests.set(sub.cmdid, { request, nullptr }); - }); - - full_memory_barrier(); - submit_sqe(sub); -} - -void NVMeQueue::write(AsyncBlockDeviceRequest& request, u16 nsid, u64 index, u32 count) -{ - NVMeSubmission sub {}; - - sub.op = OP_NVME_WRITE; - sub.rw.nsid = nsid; - sub.rw.slba = AK::convert_between_host_and_little_endian(index); - // No. of lbas is 0 based - sub.rw.length = AK::convert_between_host_and_little_endian((count - 1) & 0xFFFF); - sub.rw.data_ptr.prp1 = reinterpret_cast(AK::convert_between_host_and_little_endian(m_rw_dma_page->paddr().as_ptr())); - sub.cmdid = get_request_cid(); - - m_requests.with([&sub, &request](auto& requests) { - requests.set(sub.cmdid, { request, nullptr }); - }); - - if (auto result = request.read_from_buffer(request.buffer(), m_rw_dma_region->vaddr().as_ptr(), request.buffer_size()); result.is_error()) { - complete_current_request(sub.cmdid, AsyncDeviceRequest::MemoryFault); - return; - } - - full_memory_barrier(); - submit_sqe(sub); -} - -UNMAP_AFTER_INIT NVMeQueue::~NVMeQueue() = default; -} diff --git a/Kernel/Devices/Storage/NVMe/NVMeQueue.h b/Kernel/Devices/Storage/NVMe/NVMeQueue.h deleted file mode 100644 index 2f674417925..00000000000 --- a/Kernel/Devices/Storage/NVMe/NVMeQueue.h +++ /dev/null @@ -1,135 +0,0 @@ -/* - * Copyright (c) 2021, Pankaj R - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace Kernel { - -struct DoorbellRegister { - u32 sq_tail; - u32 cq_head; -}; - -struct Doorbell { - Memory::TypedMapping mmio_reg; - Memory::TypedMapping dbbuf_shadow; - Memory::TypedMapping dbbuf_eventidx; -}; - -enum class QueueType { - Polled, - IRQ -}; - -class AsyncBlockDeviceRequest; - -struct NVMeIO { - void clear() - { - request = nullptr; - end_io_handler = nullptr; - } - RefPtr request; - Function end_io_handler; -}; - -class NVMeController; -class NVMeQueue : public AtomicRefCounted { -public: - static ErrorOr> try_create(NVMeController& device, u16 qid, Optional irq, u32 q_depth, OwnPtr cq_dma_region, OwnPtr sq_dma_region, Doorbell db_regs, QueueType queue_type); - bool is_admin_queue() { return m_admin_queue; } - u16 submit_sync_sqe(NVMeSubmission&); - void read(AsyncBlockDeviceRequest& request, u16 nsid, u64 index, u32 count); - void write(AsyncBlockDeviceRequest& request, u16 nsid, u64 index, u32 count); - virtual void submit_sqe(NVMeSubmission&); - virtual ~NVMeQueue(); - -protected: - u32 process_cq(); - - // Updates the shadow buffer and returns if mmio is needed - bool update_shadow_buf(u16 new_value, u32* dbbuf, u32* ei) - { - u32 const old = *dbbuf; - - *dbbuf = new_value; - AK::full_memory_barrier(); - - bool need_mmio = static_cast(new_value - *ei - 1) < static_cast(new_value - old); - return need_mmio; - } - - void update_sq_doorbell() - { - full_memory_barrier(); - if (m_db_regs.dbbuf_shadow.paddr.is_null() - || update_shadow_buf(m_sq_tail, &m_db_regs.dbbuf_shadow->sq_tail, &m_db_regs.dbbuf_eventidx->sq_tail)) - m_db_regs.mmio_reg->sq_tail = m_sq_tail; - } - - NVMeQueue(NonnullOwnPtr rw_dma_region, Memory::PhysicalRAMPage const& rw_dma_page, u16 qid, u32 q_depth, OwnPtr cq_dma_region, OwnPtr sq_dma_region, Doorbell db_regs); - - [[nodiscard]] u32 get_request_cid() - { - u32 expected_tag = m_tag.load(AK::memory_order_acquire); - - for (;;) { - u32 cid = expected_tag + 1; - if (cid == m_qdepth) - cid = 0; - if (m_tag.compare_exchange_strong(expected_tag, cid, AK::memory_order_acquire)) - return cid; - } - } - - virtual void complete_current_request(u16 cmdid, u16 status); - -private: - bool cqe_available(); - void update_cqe_head(); - void update_cq_doorbell() - { - full_memory_barrier(); - if (m_db_regs.dbbuf_shadow.paddr.is_null() - || update_shadow_buf(m_cq_head, &m_db_regs.dbbuf_shadow->cq_head, &m_db_regs.dbbuf_eventidx->cq_head)) - m_db_regs.mmio_reg->cq_head = m_cq_head; - } - -protected: - SpinlockProtected, LockRank::None> m_requests; - NonnullOwnPtr m_rw_dma_region; - -private: - u16 m_qid {}; - u8 m_cq_valid_phase { 1 }; - u16 m_sq_tail {}; - u16 m_cq_head {}; - bool m_admin_queue { false }; - u32 m_qdepth {}; - Atomic m_tag { 0 }; // used for the cid in a submission queue entry - Spinlock m_sq_lock {}; - OwnPtr m_cq_dma_region; - Span m_sqe_array; - OwnPtr m_sq_dma_region; - Span m_cqe_array; - WaitQueue m_sync_wait_queue; - Doorbell m_db_regs; - NonnullRefPtr const m_rw_dma_page; -}; -} diff --git a/Kernel/Devices/Storage/SD/Commands.h b/Kernel/Devices/Storage/SD/Commands.h deleted file mode 100644 index 01164d2b56d..00000000000 --- a/Kernel/Devices/Storage/SD/Commands.h +++ /dev/null @@ -1,405 +0,0 @@ -/* - * Copyright (c) 2023, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include - -namespace Kernel::SD { - -// Relevant Specifications: -// * (SDHC): SD Host Controller Simplified Specification (https://www.sdcard.org/downloads/pls/) -// * (PLSS) Physical Layer Simplified Specification (https://www.sdcard.org/downloads/pls/) - -// PLSS 4.7.4: "Detailed Command Description" -enum class CommandIndex : u8 { - GoIdleState = 0, - AllSendCid = 2, - SendRelativeAddr = 3, - AppSetBusWidth = 6, - SelectCard = 7, - SendIfCond = 8, - SendCsd = 9, - GoInactiveState = 15, - SetBlockLen = 16, - ReadSingleBlock = 17, - ReadMultipleBlock = 18, - WriteSingleBlock = 24, - WriteMultipleBlock = 25, - AppSendOpCond = 41, - AppSendScr = 51, - AppCmd = 55, -}; - -enum class CommandType : u8 { - Normal, - Suspend, - Resume, - Abort -}; - -enum class ResponseType : u8 { - NoResponse, - ResponseOf136Bits, - ResponseOf48Bits, - ResponseOf48BitsWithBusy -}; - -enum class DataTransferDirection : u8 { - HostToCard, - CardToHost -}; - -enum class SendAutoCommand : u8 { - Disabled, - Command12, - Command23 -}; - -// SDHC 2.2.5 & 2.2.6: "Transfer Mode Register" & "Command Register" -union Command { - u32 raw; - - struct { - u32 dma_enable : 1; - u32 block_counter : 1; - SendAutoCommand auto_command : 2; - DataTransferDirection direction : 1; - u32 multiblock : 1; - u32 response_type_r1r5 : 1; // v4.10 - u32 response_error_check : 1; // v4.10 - u32 response_interrupt_disable : 1; // v4.10 - u32 reserved1 : 7; - - ResponseType response_type : 2; - u32 sub_command_flag : 1; // v4.10 - u32 crc_enable : 1; - u32 idx_enable : 1; - u32 is_data : 1; - CommandType type : 2; - CommandIndex index : 6; - u32 reserved3 : 2; - }; - - bool requires_dat_line() const - { - return is_data; - } - - bool uses_transfer_complete_interrupt() const - { - // FIXME: I don't know how to determine this. - return false; - } -}; -static_assert(AssertSize()); - -namespace Commands { - -constexpr Command go_idle_state = { - .dma_enable = 0, - .block_counter = 0, - .auto_command = SendAutoCommand::Disabled, - .direction = DataTransferDirection::HostToCard, - .multiblock = 0, - .response_type_r1r5 = 0, - .response_error_check = 0, - .response_interrupt_disable = 0, - .reserved1 = 0, - .response_type = ResponseType::NoResponse, - .sub_command_flag = 0, - .crc_enable = 0, - .idx_enable = 0, - .is_data = 0, - .type = CommandType::Normal, - .index = CommandIndex::GoIdleState, - .reserved3 = 0 -}; - -constexpr Command all_send_cid = { - .dma_enable = 0, - .block_counter = 0, - .auto_command = SendAutoCommand::Disabled, - .direction = DataTransferDirection::HostToCard, - .multiblock = 0, - .response_type_r1r5 = 0, - .response_error_check = 0, - .response_interrupt_disable = 0, - .reserved1 = 0, - .response_type = ResponseType::ResponseOf136Bits, - .sub_command_flag = 0, - .crc_enable = 1, - .idx_enable = 0, - .is_data = 0, - .type = CommandType::Normal, - .index = CommandIndex::AllSendCid, - .reserved3 = 0 -}; - -constexpr Command send_relative_addr = { - .dma_enable = 0, - .block_counter = 0, - .auto_command = SendAutoCommand::Disabled, - .direction = DataTransferDirection::HostToCard, - .multiblock = 0, - .response_type_r1r5 = 0, - .response_error_check = 0, - .response_interrupt_disable = 0, - .reserved1 = 0, - .response_type = ResponseType::ResponseOf48Bits, - .sub_command_flag = 0, - .crc_enable = 1, - .idx_enable = 0, - .is_data = 0, - .type = CommandType::Normal, - .index = CommandIndex::SendRelativeAddr, - .reserved3 = 0 -}; - -constexpr Command app_set_bus_width = { - .dma_enable = 0, - .block_counter = 0, - .auto_command = SendAutoCommand::Disabled, - .direction = DataTransferDirection::HostToCard, - .multiblock = 0, - .response_type_r1r5 = 0, - .response_error_check = 0, - .response_interrupt_disable = 0, - .reserved1 = 0, - .response_type = ResponseType::ResponseOf48Bits, - .sub_command_flag = 0, - .crc_enable = 1, - .idx_enable = 0, - .is_data = 0, - .type = CommandType::Normal, - .index = CommandIndex::AppSetBusWidth, - .reserved3 = 0 -}; - -constexpr Command select_card = { - .dma_enable = 0, - .block_counter = 0, - .auto_command = SendAutoCommand::Disabled, - .direction = DataTransferDirection::HostToCard, - .multiblock = 0, - .response_type_r1r5 = 0, - .response_error_check = 0, - .response_interrupt_disable = 0, - .reserved1 = 0, - .response_type = ResponseType::ResponseOf48BitsWithBusy, - .sub_command_flag = 0, - .crc_enable = 1, - .idx_enable = 0, - .is_data = 0, - .type = CommandType::Normal, - .index = CommandIndex::SelectCard, - .reserved3 = 0 -}; - -constexpr Command send_if_cond = { - .dma_enable = 0, - .block_counter = 0, - .auto_command = SendAutoCommand::Disabled, - .direction = DataTransferDirection::HostToCard, - .multiblock = 0, - .response_type_r1r5 = 0, - .response_error_check = 0, - .response_interrupt_disable = 0, - .reserved1 = 0, - .response_type = ResponseType::ResponseOf48Bits, - .sub_command_flag = 0, - .crc_enable = 1, - .idx_enable = 0, - .is_data = 0, - .type = CommandType::Normal, - .index = CommandIndex::SendIfCond, - .reserved3 = 0 -}; - -constexpr Command send_csd = { - .dma_enable = 0, - .block_counter = 0, - .auto_command = SendAutoCommand::Disabled, - .direction = DataTransferDirection::HostToCard, - .multiblock = 0, - .response_type_r1r5 = 0, - .response_error_check = 0, - .response_interrupt_disable = 0, - .reserved1 = 0, - .response_type = ResponseType::ResponseOf136Bits, - .sub_command_flag = 0, - .crc_enable = 1, - .idx_enable = 0, - .is_data = 0, - .type = CommandType::Normal, - .index = CommandIndex::SendCsd, - .reserved3 = 0 -}; - -constexpr Command set_block_len = { - .dma_enable = 0, - .block_counter = 0, - .auto_command = SendAutoCommand::Disabled, - .direction = DataTransferDirection::HostToCard, - .multiblock = 0, - .response_type_r1r5 = 0, - .response_error_check = 0, - .response_interrupt_disable = 0, - .reserved1 = 0, - .response_type = ResponseType::ResponseOf48Bits, - .sub_command_flag = 0, - .crc_enable = 0, - .idx_enable = 0, - .is_data = 0, - .type = CommandType::Normal, - .index = CommandIndex::SetBlockLen, - .reserved3 = 0 -}; - -constexpr Command read_single_block = { - .dma_enable = 0, - .block_counter = 0, - .auto_command = SendAutoCommand::Disabled, - .direction = DataTransferDirection::CardToHost, - .multiblock = 0, - .response_type_r1r5 = 0, - .response_error_check = 0, - .response_interrupt_disable = 0, - .reserved1 = 0, - .response_type = ResponseType::ResponseOf48Bits, - .sub_command_flag = 0, - .crc_enable = 1, - .idx_enable = 0, - .is_data = 1, - .type = CommandType::Normal, - .index = CommandIndex::ReadSingleBlock, - .reserved3 = 0 -}; - -constexpr Command read_multiple_block = { - .dma_enable = 0, - .block_counter = 1, - .auto_command = SendAutoCommand::Command12, - .direction = DataTransferDirection::CardToHost, - .multiblock = 1, - .response_type_r1r5 = 0, - .response_error_check = 0, - .response_interrupt_disable = 0, - .reserved1 = 0, - .response_type = ResponseType::ResponseOf48Bits, - .sub_command_flag = 0, - .crc_enable = 1, - .idx_enable = 0, - .is_data = 1, - .type = CommandType::Normal, - .index = CommandIndex::ReadMultipleBlock, - .reserved3 = 0 -}; - -constexpr Command write_single_block = { - .dma_enable = 0, - .block_counter = 0, - .auto_command = SendAutoCommand::Disabled, - .direction = DataTransferDirection::HostToCard, - .multiblock = 0, - .response_type_r1r5 = 0, - .response_error_check = 0, - .response_interrupt_disable = 0, - .reserved1 = 0, - .response_type = ResponseType::ResponseOf48Bits, - .sub_command_flag = 0, - .crc_enable = 1, - .idx_enable = 0, - .is_data = 1, - .type = CommandType::Normal, - .index = CommandIndex::WriteSingleBlock, - .reserved3 = 0 -}; - -constexpr Command write_multiple_block = { - .dma_enable = 0, - .block_counter = 1, - .auto_command = SendAutoCommand::Command12, - .direction = DataTransferDirection::HostToCard, - .multiblock = 1, - .response_type_r1r5 = 0, - .response_error_check = 0, - .response_interrupt_disable = 0, - .reserved1 = 0, - .response_type = ResponseType::ResponseOf48Bits, - .sub_command_flag = 0, - .crc_enable = 1, - .idx_enable = 0, - .is_data = 1, - .type = CommandType::Normal, - .index = CommandIndex::WriteMultipleBlock, - .reserved3 = 0 -}; - -constexpr Command app_send_op_cond = { - .dma_enable = 0, - .block_counter = 0, - .auto_command = SendAutoCommand::Disabled, - .direction = DataTransferDirection::HostToCard, - .multiblock = 0, - .response_type_r1r5 = 0, - .response_error_check = 0, - .response_interrupt_disable = 0, - .reserved1 = 0, - .response_type = ResponseType::ResponseOf48Bits, - .sub_command_flag = 0, - .crc_enable = 0, - .idx_enable = 0, - .is_data = 0, - .type = CommandType::Normal, - .index = CommandIndex::AppSendOpCond, - .reserved3 = 0 -}; - -constexpr Command app_send_scr = { - .dma_enable = 0, - .block_counter = 0, - .auto_command = SendAutoCommand::Disabled, - .direction = DataTransferDirection::CardToHost, - .multiblock = 0, - .response_type_r1r5 = 0, - .response_error_check = 0, - .response_interrupt_disable = 0, - .reserved1 = 0, - .response_type = ResponseType::ResponseOf48Bits, - .sub_command_flag = 0, - .crc_enable = 0, - .idx_enable = 0, - .is_data = 1, - .type = CommandType::Normal, - .index = CommandIndex::AppSendScr, - .reserved3 = 0 -}; - -constexpr Command app_cmd = { - .dma_enable = 0, - .block_counter = 0, - .auto_command = SendAutoCommand::Disabled, - .direction = DataTransferDirection::HostToCard, - .multiblock = 0, - .response_type_r1r5 = 0, - .response_error_check = 0, - .response_interrupt_disable = 0, - .reserved1 = 0, - .response_type = ResponseType::ResponseOf48Bits, - .sub_command_flag = 0, - .crc_enable = 1, - .idx_enable = 0, - .is_data = 0, - .type = CommandType::Normal, - .index = CommandIndex::AppCmd, - .reserved3 = 0 -}; - -} - -} diff --git a/Kernel/Devices/Storage/SD/PCISDHostController.cpp b/Kernel/Devices/Storage/SD/PCISDHostController.cpp deleted file mode 100644 index 9f0bfc0b1e9..00000000000 --- a/Kernel/Devices/Storage/SD/PCISDHostController.cpp +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright (c) 2023, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include - -namespace Kernel { - -ErrorOr> PCISDHostController::try_initialize(PCI::DeviceIdentifier const& device_identifier) -{ - auto sdhc = TRY(adopt_nonnull_ref_or_enomem(new (nothrow) PCISDHostController(device_identifier))); - TRY(sdhc->initialize()); - - PCI::enable_bus_mastering(sdhc->device_identifier()); - PCI::enable_memory_space(sdhc->device_identifier()); - sdhc->try_enable_dma(); - - return sdhc; -} - -PCISDHostController::PCISDHostController(PCI::DeviceIdentifier const& device_identifier) - : PCI::Device(device_identifier) - , SDHostController() -{ - auto slot_information_register = read_slot_information(); - - if (slot_information_register.slots_available() != 1) { - // TODO: Support multiple slots - dmesgln("SD Host Controller has {} slots, but we currently only support using only one", slot_information_register.slots_available()); - } - - auto physical_address_of_sdhc_registers = PhysicalAddress { - PCI::get_BAR(device_identifier, static_cast(slot_information_register.first_bar_number)) - }; - m_registers = Memory::map_typed_writable(physical_address_of_sdhc_registers).release_value_but_fixme_should_propagate_errors(); -} - -} diff --git a/Kernel/Devices/Storage/SD/PCISDHostController.h b/Kernel/Devices/Storage/SD/PCISDHostController.h deleted file mode 100644 index ef6426e8fa9..00000000000 --- a/Kernel/Devices/Storage/SD/PCISDHostController.h +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright (c) 2023, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include - -namespace Kernel { - -class PCISDHostController : public PCI::Device - , public SDHostController { -public: - static ErrorOr> try_initialize(PCI::DeviceIdentifier const& device_identifier); - - // ^PCI::Device - virtual StringView device_name() const override { return "SD Host Controller"sv; } - -protected: - // ^SDHostController - virtual SD::HostControlRegisterMap volatile* get_register_map_base_address() override { return m_registers.ptr(); } - -private: - PCISDHostController(PCI::DeviceIdentifier const& device_identifier); - - struct [[gnu::packed]] SlotInformationRegister { - u8 first_bar_number : 3; - u8 : 1; - u8 number_of_slots : 3; - u8 : 1; - - u8 slots_available() const { return number_of_slots + 1; } - }; - static_assert(AssertSize()); - - SlotInformationRegister read_slot_information() const - { - SpinlockLocker locker(device_identifier().operation_lock()); - return bit_cast(PCI::Access::the().read8_field(device_identifier(), 0x40)); - } - - Memory::TypedMapping m_registers; -}; - -} diff --git a/Kernel/Devices/Storage/SD/Registers.h b/Kernel/Devices/Storage/SD/Registers.h deleted file mode 100644 index ba890fe08b3..00000000000 --- a/Kernel/Devices/Storage/SD/Registers.h +++ /dev/null @@ -1,356 +0,0 @@ -/* - * Copyright (c) 2023, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include - -namespace Kernel::SD { - -// Relevant Specifications: -// * (SDHC): SD Host Controller Simplified Specification (https://www.sdcard.org/downloads/pls/) -// * (PLSS) Physical Layer Simplified Specification (https://www.sdcard.org/downloads/pls/) -// * (BCM2835) BCM2835 ARM Peripherals (https://www.raspberrypi.org/app/uploads/2012/02/BCM2835-ARM-Peripherals.pdf) - -enum class HostVersion : u8 { - Version1, - Version2, - Version3, - Unknown -}; - -enum class ADMAErrorState : u32 { - Stop = 0b00, - FetchDescriptor = 0b01, - Reserved = 0b10, - TransferData = 0b11 -}; - -// SDHC 2.1.1 "SD Host Control Register Map" -// NOTE: The registers must be 32 bits, because of a quirk in the RPI. -struct HostControlRegisterMap { - u32 argument_2; - u32 block_size_and_block_count; - u32 argument_1; - u32 transfer_mode_and_command; - u32 response_0; - u32 response_1; - u32 response_2; - u32 response_3; - u32 buffer_data_port; - union PresentState { - struct { // SDHC 2.2.9 Present State Register (Cat.C Offset 024h) - u32 command_inhibit_cmd : 1; - u32 command_inhibit_dat : 1; - u32 dat_line_active : 1; - u32 re_tuning_request : 1; - u32 dat_7_4_line_signal_level : 4; - u32 write_transfer_active : 1; - u32 read_transfer_active : 1; - u32 buffer_write_enable : 1; - u32 buffer_read_enable : 1; - u32 : 4; - u32 card_inserted : 1; - u32 card_state_stable : 1; - u32 card_detect_pin_level : 1; - u32 write_protect_switch_pin_level : 1; - u32 dat_3_0_line_signal_level : 4; - u32 cmd_line_signal_level : 1; - u32 host_regulator_voltage_stable : 1; - u32 : 1; - u32 command_not_issued_by_error : 1; - u32 sub_command_status : 1; - u32 in_dormant_state : 1; - u32 lane_synchronization : 1; - u32 uhs_2_if_detection : 1; - }; - u32 raw; - } present_state; - u32 host_configuration_0; - u32 host_configuration_1; - union InterruptStatus { - struct { // SDHC 2.2.18 Normal Interrupt Status Register (Cat.C Offset 030h) - u32 command_complete : 1; - u32 transfer_complete : 1; - u32 block_gap_event : 1; - u32 dma_interrupt : 1; - u32 buffer_write_ready : 1; - u32 buffer_read_ready : 1; - u32 card_insertion : 1; - u32 card_removal : 1; - u32 card_interrupt : 1; - u32 int_a : 1; - u32 int_b : 1; - u32 int_c : 1; - u32 retuning_event : 1; - u32 fx_event : 1; - u32 : 1; - u32 error_interrupt : 1; - // SDHC 2.2.19 Error Interrupt Status Register (Cat.C Offset 032 - u32 command_timeout_error : 1; - u32 command_crc_error : 1; - u32 cammand_index_error : 1; - u32 data_timeout_error : 1; - u32 data_crc_error : 1; - u32 data_end_bit_error : 1; - u32 current_limit_error : 1; - u32 auto_cmd_error : 1; - u32 adma_error : 1; - u32 tuning_error : 1; - u32 response_error : 1; - u32 vendor_specific_error : 1; - } const; - u32 raw; - } interrupt_status; - u32 interrupt_status_enable; - u32 interrupt_signal_enable; - u32 host_configuration_2; - // SDHC 2.2.26 Capabilities Register (Cat.C Offset 040h) - struct CapabilitesRegister { - u32 timeout_clock_frequency : 6; - u32 : 1; - u32 timeout_clock_unit : 1; - u32 base_clock_frequency : 8; - u32 max_block_length : 2; - u32 eight_bit_support_for_embedded_devices : 1; - u32 adma2 : 1; - u32 : 1; - u32 high_speed : 1; - u32 sdma : 1; - u32 suspend_resume : 1; - u32 three_point_three_volt : 1; - u32 three_point_zero_volt : 1; - u32 one_point_eight_volt : 1; - u32 dma_64_bit_addressing_v4 : 1; - u32 dma_64_bit_addressing_v3 : 1; - u32 async_interrupt : 1; - u32 slot_type : 2; - u32 sdr50 : 1; - u32 sdr140 : 1; - u32 ddr50 : 1; - u32 uhs_ii : 1; - u32 driver_type_A : 1; - u32 driver_type_C : 1; - u32 driver_type_D : 1; - u32 : 1; - u32 timer_count_for_retuning : 4; - u32 : 1; - u32 use_tuning_for_sdr50 : 1; - u32 retuning_modes : 2; - u32 clock_multiplier : 8; - u32 : 3; - u32 adma3 : 1; - u32 one_point_eight_vdd2 : 1; - u32 : 3; - } capabilities; - - u32 maximum_current_capabilities; - u32 maximum_current_capabilities_reserved; - u32 force_event_for_auto_cmd_error_status; - struct { - ADMAErrorState state : 2; - u32 length_mismatch_error : 1; - u32 : 5; - u32 : 24; - } adma_error_status; - u32 adma_system_address[2]; - u32 preset_value[4]; - u32 reserved_0[28]; - u32 shared_bus_control; - u32 reserved_1[6]; - struct [[gnu::packed]] { - u8 interrupt_signal_for_each_slot; - u8 : 8; - HostVersion specification_version_number; - u8 vendor_version_number; - } slot_interrupt_status_and_version; -}; -static_assert(AssertSize()); - -// SDHC Figure 1-10 : General Descriptor Table Format -enum class DMAAction : u8 { - // ADMA 2 - Nop = 0b000, - Rsv0 = 0b010, - Tran = 0b100, - Link = 0b110, - // ADMA 3 - CommandDescriptor_SD = 0b001, - CommandDescriptor_UHS_II = 0b011, - Rsv1 = 0b101, - IntegratedDescriptor = 0b111, -}; - -// Both of these represent the ADMA2 version, ADMA3 might have slight differences -// SDHC 1.13.3.1 ADMA2 Descriptor Format -struct alignas(4) DMADescriptor64 { - u32 valid : 1; - u32 end : 1; - u32 interrupt : 1; - DMAAction action : 3; - u32 length_upper : 10; // Version 4.10+ only - u32 length_lower : 16; - u32 address : 32; -}; -static_assert(AssertSize()); - -struct alignas(8) DMADescriptor128 { - u32 valid : 1; - u32 end : 1; - u32 interrupt : 1; - DMAAction action : 3; - u32 length_upper : 10; // Version 4.10+ only - u32 length_lower : 16; - u32 address_low : 32; - u32 address_high : 32; - u32 : 32; -}; -static_assert(AssertSize()); - -// PLSS 5.1: "OCR Register" -union OperatingConditionRegister { - u32 raw; - struct { - u32 : 15; - u32 vdd_voltage_window_27_28 : 1; - u32 vdd_voltage_window_28_29 : 1; - u32 vdd_voltage_window_29_30 : 1; - u32 vdd_voltage_window_30_31 : 1; - u32 vdd_voltage_window_31_32 : 1; - u32 vdd_voltage_window_32_33 : 1; - u32 vdd_voltage_window_33_34 : 1; - u32 vdd_voltage_window_34_35 : 1; - u32 vdd_voltage_window_35_36 : 1; - u32 switching_to_18v_accepted : 1; - u32 : 2; - u32 over_2tb_support_status : 1; - u32 : 1; - u32 uhs2_card_status : 1; - u32 card_capacity_status : 1; - u32 card_power_up_status : 1; - }; -}; -static_assert(AssertSize()); - -// PLSS 5.2: "CID Register" -union CardIdentificationRegister { - u32 raw[4]; - - struct [[gnu::packed]] { - u64 manufacturing_date : 12; - u64 : 4; - u64 product_serial_number : 32; - u64 product_revision : 8; - u64 product_name : 40; - u64 oem_id : 16; - u64 manufacturer_id : 8; - }; -}; -static_assert(AssertSize()); - -// PLSS 5.3.2: "CSD Register (CSD Version 1.0)" -union CardSpecificDataRegister { - u64 raw[2]; - - struct [[gnu::packed]] { - // Note that the physical layer spec says there are 7 bits of checksum and 1 reserved bit here, - // but they are removed - u32 : 1; - u32 write_protection_until_power_cycle : 1; - u32 file_format : 2; - u32 temporary_write_protection : 1; - u32 permanent_write_protection : 1; - u32 copy_flag : 1; - u32 file_format_group : 1; - u32 : 5; - u32 partial_blocks_for_write_allowed : 1; - u32 max_write_data_block_length : 4; - u32 write_speed_factor : 3; - u32 : 2; - u32 write_protect_group_enable : 1; - u32 write_protect_group_size : 7; - u32 erase_sector_size : 7; - u32 erase_single_block_enable : 1; - u32 device_size_multiplier : 3; - u32 max_write_current_at_vdd_max : 3; - u32 max_write_current_at_vdd_min : 3; - u32 max_read_current_at_vdd_max : 3; - u32 max_read_current_at_vdd_min : 3; - u32 device_size : 12; - u32 : 2; - u32 dsr_implemented : 1; - u32 read_block_misalignment : 1; - u32 write_block_misalignment : 1; - u32 partial_blocks_for_read_allowed : 1; - u32 max_read_data_block_length : 4; - u32 card_command_classes : 12; - u32 max_data_transfer_rate : 8; - u32 data_read_access_time2 : 8; - u32 data_read_access_time1 : 8; - u32 : 6; - u32 csd_structure : 2; - }; -}; -static_assert(AssertSize()); - -// PLSS 5.6: "SCR Register" -union SDConfigurationRegister { - u8 raw[8]; - - struct { - u32 scr_structure : 4; - u32 sd_specification : 4; - u32 data_status_after_erase : 1; - u32 sd_security : 3; - u32 sd_bus_widths : 4; - u32 sd_specification3 : 1; - u32 extended_security : 4; - u32 sd_specification4 : 1; - u32 sd_specification_x : 4; - u32 : 1; - u32 command_support : 5; - u32 : 32; - }; -}; -static_assert(AssertSize()); - -// PLSS 4.10.1: "Card Status" -union CardStatus { - u32 raw; - - struct { - u32 : 3; - u32 ake_seq_error : 1; - u32 : 1; - u32 app_cmd : 1; - u32 fx_event : 1; - u32 : 1; - u32 ready_for_data : 1; - u32 current_state : 4; - u32 erase_reset : 1; - u32 card_ecc_disabled : 1; - u32 wp_erase_skip : 1; - u32 csd_overwrite : 1; - u32 : 2; - u32 error : 1; - u32 cc_error : 1; - u32 card_ecc_failed : 1; - u32 illegal_command : 1; - u32 com_crc_error : 1; - u32 lock_unlock_failed : 1; - u32 card_is_locked : 1; - u32 wp_violation : 1; - u32 erase_param : 1; - u32 erase_seq_error : 1; - u32 block_len_error : 1; - u32 address_error : 1; - u32 out_of_range : 1; - }; -}; -static_assert(AssertSize()); - -} diff --git a/Kernel/Devices/Storage/SD/SDHostController.cpp b/Kernel/Devices/Storage/SD/SDHostController.cpp deleted file mode 100644 index da8dc2ac179..00000000000 --- a/Kernel/Devices/Storage/SD/SDHostController.cpp +++ /dev/null @@ -1,1020 +0,0 @@ -/* - * Copyright (c) 2023, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include -#include -#include -#if ARCH(AARCH64) -# include -#endif - -namespace Kernel { - -// Relevant Specifications: -// * (SDHC): SD Host Controller Simplified Specification (https://www.sdcard.org/downloads/pls/) -// * (PLSS) Physical Layer Simplified Specification (https://www.sdcard.org/downloads/pls/) -// * (BCM2835) BCM2835 ARM Peripherals (https://www.raspberrypi.org/app/uploads/2012/02/BCM2835-ARM-Peripherals.pdf) - -static void delay(i64 nanoseconds) -{ - auto start = TimeManagement::the().monotonic_time(); - auto end = start + Duration::from_nanoseconds(nanoseconds); - while (TimeManagement::the().monotonic_time() < end) - Processor::pause(); -} - -constexpr u32 max_supported_sdsc_frequency = 25000000; -constexpr u32 max_supported_sdsc_frequency_high_speed = 50000000; - -// In "m_registers->host_configuration_0" -// 2.2.11 Host Control 1 Register -constexpr u32 data_transfer_width_4bit = 1 << 1; -constexpr u32 high_speed_enable = 1 << 2; -constexpr u32 dma_select_adma2_32 = 0b10 << 3; -constexpr u32 dma_select_adma2_64 = 0b11 << 3; - -// In "m_registers->host_configuration_1" -// In sub-register "Clock Control" -constexpr u32 internal_clock_enable = 1 << 0; -constexpr u32 internal_clock_stable = 1 << 1; -constexpr u32 sd_clock_enable = 1 << 2; -constexpr u32 sd_clock_divisor_mask = 0x0000ffc0; - -// In sub-register "Timeout Control" -constexpr u32 data_timeout_counter_value_mask = 0b1111 << 16; -constexpr u32 data_timeout_counter_value_max = 0b1110 << 16; - -// In sub-register "Software Reset" -constexpr u32 software_reset_for_all = 0x01000000; - -// In Interrupt Status Register -constexpr u32 command_complete = 1 << 0; -constexpr u32 transfer_complete = 1 << 1; -constexpr u32 buffer_write_ready = 1 << 4; -constexpr u32 buffer_read_ready = 1 << 5; -constexpr u32 card_interrupt = 1 << 8; - -// PLSS 5.1: all voltage windows -constexpr u32 acmd41_voltage = 0x00ff8000; -// PLSS 4.2.3.1: All voltage windows, XPC = 1, SDHC = 1 -constexpr u32 acmd41_arg = 0x50ff8000; - -constexpr size_t block_len = 512; - -SDHostController::SDHostController() - : StorageController(StorageManagement::generate_relative_sd_controller_id({})) -{ -} - -void SDHostController::complete_current_request(AsyncDeviceRequest::RequestResult) -{ - VERIFY_NOT_REACHED(); -} - -ErrorOr SDHostController::initialize() -{ - m_registers = get_register_map_base_address(); - if (!m_registers) - return EIO; - - if (host_version() != SD::HostVersion::Version3 && host_version() != SD::HostVersion::Version2) - return ENOTSUP; - - TRY(reset_host_controller()); - - m_registers->interrupt_status_enable = 0xffffffff; - - auto card_or_error = try_initialize_inserted_card(); - if (card_or_error.is_error() && card_or_error.error().code() != ENODEV) { - dmesgln("SDHostController: Failed to initialize inserted card: {}", card_or_error.error()); - } else if (!card_or_error.is_error()) { - m_card = card_or_error.release_value(); - } - - return {}; -} - -void SDHostController::try_enable_dma() -{ - if (m_registers->capabilities.adma2) { - auto maybe_dma_buffer = MM.allocate_dma_buffer_pages(dma_region_size, "SDHC DMA Buffer"sv, Memory::Region::Access::ReadWrite); - if (maybe_dma_buffer.is_error()) { - dmesgln("Could not allocate DMA pages for SDHC: {}", maybe_dma_buffer.error()); - } else { - m_dma_region = maybe_dma_buffer.release_value(); - dbgln("Allocated SDHC DMA buffer at {}", m_dma_region->physical_page(0)->paddr()); - // FIXME: This check does not seem to work, qemu supports 64 bit addressing, but we don't seem to detect it - // FIXME: Hardcoding to use the 64 bit mode leads to transfer timeouts, without any errors reported from qemu - if (host_version() != SD::HostVersion::Version3 && m_registers->capabilities.dma_64_bit_addressing_v3) { - dbgln("Setting SDHostController to operate using ADMA2 with 64 bit addressing"); - m_mode = OperatingMode::ADMA2_64; - m_registers->host_configuration_0 = m_registers->host_configuration_0 | dma_select_adma2_64; - } else { - // FIXME: Use a way that guarantees memory addresses below the 32 bit threshold - VERIFY(m_dma_region->physical_page(0)->paddr().get() >> 32 == 0); - VERIFY(m_dma_region->physical_page(dma_region_size / PAGE_SIZE - 1)->paddr().get() >> 32 == 0); - - dbgln("Setting SDHostController to operate using ADMA2 with 32 bit addressing"); - m_mode = OperatingMode::ADMA2_32; - m_registers->host_configuration_0 = m_registers->host_configuration_0 | dma_select_adma2_32; - } - } - } -} - -ErrorOr> SDHostController::try_initialize_inserted_card() -{ - if (!is_card_inserted()) - return ENODEV; - - // PLSS 4.2: "Card Identification Mode" - // "After power-on ...the cards are initialized with ... 400KHz clock frequency." - - // NOTE: The SDHC might already have been initialized (e.g. by the bootloader), let's reset it to a known configuration - if (is_sd_clock_enabled()) - TRY(sd_clock_stop()); - TRY(sd_clock_supply(400000)); - - // PLSS 4.2.3: "Card Initialization and Identification Process" - // Also see Figure 4-2 in the PLSS spec for a flowchart of the initialization process. - // Note that the steps correspond to the steps in the flowchart, although I made up the numbering and text - - // 1. Send CMD0 (GO_IDLE_STATE) to the card - TRY(issue_command(SD::Commands::go_idle_state, 0)); - TRY(wait_for_response()); - - // 2. Send CMD8 (SEND_IF_COND) to the card - // SD interface condition: 7:0 = check pattern, 11:8 = supply voltage - // 0x1aa: check pattern = 10101010, supply voltage = 1 => 2.7-3.6V - u32 const voltage_window = 0x1aa; - TRY(issue_command(SD::Commands::send_if_cond, voltage_window)); - auto interface_condition_response = wait_for_response(); - - // 3. If the card does not respond to CMD8 it means that (Ver2.00 or later - // SD Memory Card(voltage mismatch) or Ver1.X SD Memory Card or not SD - // Memory Card) - if (interface_condition_response.is_error()) { - // TODO: This is supposed to be the "No Response" branch of the - // flowchart in Figure 4-2 of the PLSS spec - return ENOTSUP; - } - - // 4. If the card responds to CMD8, but it's not a valid response then the - // card is not usable - if (interface_condition_response.value().response[0] != voltage_window) { - // FIXME: We should probably try again with a lower voltage window - return ENODEV; - } - - // 5. Send ACMD41 (SEND_OP_COND) with HCS=1 to the card, repeat this until the card is ready or timeout - SD::OperatingConditionRegister ocr = {}; - bool card_is_usable = true; - if (!retry_with_timeout([&]() { - if (issue_command(SD::Commands::app_cmd, 0).is_error() || wait_for_response().is_error()) - return false; - - if (issue_command(SD::Commands::app_send_op_cond, acmd41_arg).is_error()) - return false; - - if (auto acmd41_response = wait_for_response(); - !acmd41_response.is_error()) { - - // 20. Check if the card supports the voltage windows we requested and SDHC - u32 response = acmd41_response.value().response[0]; - if ((response & acmd41_voltage) != acmd41_voltage) { - card_is_usable = false; - return false; - } - - ocr.raw = acmd41_response.value().response[0]; - } - - return ocr.card_power_up_status == 1; - })) { - return card_is_usable ? EIO : ENODEV; - } - - // 6. If you requested to switch to 1.8V, and the card accepts, execute a voltage switch sequence - // (we didn't ask it) - - // 7. Send CMD2 (ALL_SEND_CID) to the card - TRY(issue_command(SD::Commands::all_send_cid, 0)); - auto all_send_cid_response = TRY(wait_for_response()); - auto cid = bit_cast(all_send_cid_response.response); - - // 8. Send CMD3 (SEND_RELATIVE_ADDR) to the card - TRY(issue_command(SD::Commands::send_relative_addr, 0)); - auto send_relative_addr_response = TRY(wait_for_response()); - u32 rca = send_relative_addr_response.response[0]; // FIXME: Might need to clear some bits here - - // Extra steps: - - TRY(issue_command(SD::Commands::send_csd, rca)); - auto send_csd_response = TRY(wait_for_response()); - auto csd = bit_cast(send_csd_response.response); - - u32 block_count = (csd.device_size + 1) * (1 << (csd.device_size_multiplier + 2)); - u32 block_size = (1 << csd.max_read_data_block_length); - u64 capacity = static_cast(block_count) * block_size; - u64 card_capacity_in_blocks = capacity / block_len; - - if (m_registers->capabilities.high_speed) { - dbgln("SDHC: Enabling High Speed mode"); - m_registers->host_configuration_0 = m_registers->host_configuration_0 | high_speed_enable; - TRY(sd_clock_frequency_change(max_supported_sdsc_frequency_high_speed)); - } else { - TRY(sd_clock_frequency_change(max_supported_sdsc_frequency)); - } - - TRY(issue_command(SD::Commands::select_card, rca)); - TRY(wait_for_response()); - - // Set block length to 512 if the card is SDSC. - // All other models only support 512 byte blocks so they don't need to be explicitly told - if (!ocr.card_capacity_status) { - TRY(issue_command(SD::Commands::set_block_len, block_len)); - TRY(wait_for_response()); - } - - auto scr = TRY(retrieve_sd_configuration_register(rca)); - - // SDHC 3.4: "Changing Bus Width" - - // 1. Set Card Interrupt Status Enable in the Normal Interrupt Status Enable register to 0 for - // masking incorrect interrupts that may occur while changing the bus width. - m_registers->interrupt_status_enable &= ~card_interrupt; - // 2. In case of SD memory only card, go to step (4). In case of other card, go to step (3). - // 4. Change the bus width mode for an SD card. SD Memory Card bus width is changed by ACMD6 - // and SDIO card bus width is changed by setting Bus Width of Bus Interface Control register in - // CCCR. - TRY(issue_command(SD::Commands::app_cmd, rca)); - TRY(wait_for_response()); - TRY(issue_command(SD::Commands::app_set_bus_width, 0x2)); // 0b00=1 bit bus, 0b10=4 bit bus - TRY(wait_for_response()); - // 5. In case of changing to 4-bit mode, set Data Transfer Width to 1 in the Host Control 1 register. - // In another case (1-bit mode), set this bit to 0. - m_registers->host_configuration_0 |= data_transfer_width_4bit; - // 6. In case of SD memory only card, go to the 'End'. In case of other card, go to step (7). - - return TRY(DeviceManagement::try_create_device( - *this, - StorageDevice::LUNAddress { controller_id(), 0, 0 }, - hardware_relative_controller_id(), block_len, - card_capacity_in_blocks, rca, ocr, cid, scr)); -} - -bool SDHostController::retry_with_timeout(Function f, i64 delay_between_tries) -{ - int timeout = 1000; - bool success = false; - while (!success && timeout > 0) { - success = f(); - if (!success) - delay(delay_between_tries); - timeout--; - } - return timeout > 0; -} - -ErrorOr SDHostController::issue_command(SD::Command const& cmd, u32 argument) -{ - // SDHC 3.7.1: "Transaction Control without Data Transfer Using DAT Line" - - // 1. Check Command Inhibit (CMD) in the Present State register. - // Repeat this step until Command Inhibit (CMD) is 0. - // That is, when Command Inhibit (CMD) is 1, the Host Driver - // shall not issue an SD Command. - if (!retry_with_timeout([&]() { return !m_registers->present_state.command_inhibit_cmd; })) { - return EIO; - } - - // 2. If the Host Driver issues an SD Command using DAT lines - // including busy signal, go to step (3). - // If without using DAT lines including busy signal, go to step (5). - // 3. If the Host Driver is issuing an abort command, go to step (5). In the - // case of non-abort command, go to step (4). - if (cmd.requires_dat_line() && cmd.type != SD::CommandType::Abort) { - - // 4. Check Command Inhibit (DAT) in the Present State register. Repeat - // this step until Command Inhibit (DAT) is set to 0. - if (!retry_with_timeout([&]() { return !m_registers->present_state.command_inhibit_dat; })) { - return EIO; - } - } - - // 5. Set registers as described in Table 1-2 except Command register. - m_registers->argument_1 = argument; - - // 6. Set the Command register. - m_registers->transfer_mode_and_command = cmd.raw; - - // 7. Perform Command Completion Sequence in accordance with 3.7.1.2. - // Done in wait_for_response() - - return {}; -} - -ErrorOr SDHostController::wait_for_response() -{ - // SDHC 3.7.1.2 The Sequence to Finalize a Command - - // 1. Wait for the Command Complete Interrupt. If the Command Complete - // Interrupt has occurred, go to step (2). - if (!retry_with_timeout( - [&]() { return m_registers->interrupt_status.command_complete; })) { - return EIO; - } - - // 2. Write 1 to Command Complete in the Normal Interrupt Status register to clear this bit - m_registers->interrupt_status.raw = command_complete; - - // 3. Read the Response register(s) to get the response. - // NOTE: We read fewer bits than ResponseType because the missing bits are only - // relevant for the physical layer, and the device filters them before they - // reach us - Response r = {}; - auto cmd = last_sent_command(); - switch (cmd.response_type) { - case SD::ResponseType::NoResponse: - break; - case SD::ResponseType::ResponseOf136Bits: - r.response[0] = m_registers->response_0; - r.response[1] = m_registers->response_1; - r.response[2] = m_registers->response_2; - r.response[3] = m_registers->response_3; - break; - case SD::ResponseType::ResponseOf48Bits: - r.response[0] = m_registers->response_0; - break; - case SD::ResponseType::ResponseOf48BitsWithBusy: - // FIXME: Idk what to do here - break; - } - - // 4. Judge whether the command uses the Transfer Complete Interrupt or not. - // If it uses Transfer Complete, go to step (5). If not, go to step (7). - if (last_sent_command().uses_transfer_complete_interrupt()) - TODO(); - - // 7. Check for errors in Response Data. If there is no error, go to step (8). If there is an error, go to step (9). - if (cmd.response_type != SD::ResponseType::ResponseOf136Bits) { - if (card_status_contains_errors(cmd, r.response[0])) { - return EIO; - } - } - - // NOTE: Steps 7, 8 and 9 consist of checking the response for errors, which - // are specific to each command therefore those steps are not fully implemented - // here. - - return { r }; -} - -bool SDHostController::is_sd_clock_enabled() -{ - return m_registers->host_configuration_1 & sd_clock_enable; -} - -ErrorOr SDHostController::calculate_sd_clock_divisor(u32 sd_clock_frequency, u32 frequency) -{ - // SDHC 2.2.14: "Clock Control Register" - - // (1) 10-bit Divisor Mode - // This mode is supported by the Host Controller Version 1.00 and 2.00. - // The frequency is not programmed directly; rather this register holds the divisor of - // the Base Clock Frequency For SD Clock in the Capabilities register. Only - // the following settings are allowed. - // - // +-----+---------------------------+ - // | 80h | base clock divided by 256 | - // | 40h | base clock divided by 128 | - // | 20h | base clock divided by 64 | - // | 10h | base clock divided by 32 | - // | 08h | base clock divided by 16 | - // | 04h | base clock divided by 8 | - // | 02h | base clock divided by 4 | - // | 01h | base clock divided by 2 | - // | 00h | Base clock (10MHz-63MHz) | - // +-----+---------------------------+ - // - if (host_version() == SD::HostVersion::Version2 || host_version() == SD::HostVersion::Version1) { - for (u32 divisor = 1; divisor <= 256; divisor *= 2) { - if (sd_clock_frequency / divisor <= frequency) - return divisor >> 1; - } - - dmesgln("SDHostController: Could not find a suitable divisor for the requested frequency"); - return ENOTSUP; - } - - // (2) 10-bit Divided Clock Mode - // Host Controller Version 3.00 supports this mandatory mode instead of the - // 8-bit Divided Clock Mode. The length of divider is extended to 10 bits and all - // divider values shall be supported. - // - // +------+-------------------------------+ - // | 3FFh | 1/2046 Divided Clock | - // | .... | ............................. | - // | N | 1/2N Divided Clock (Duty 50%) | - // | .... | ............................. | - // | 002h | 1/4 Divided Clock | - // | 001h | 1/2 Divided Clock | - // | 000h | Base Clock (10MHz-255MHz) | - // +------+-------------------------------+ - // - if (host_version() == SD::HostVersion::Version3) { - if (frequency == sd_clock_frequency) - return 0; - - auto divisor = AK::ceil_div(sd_clock_frequency, 2 * frequency); - if (divisor > 0x3ff) { - dmesgln("SDHostController: Cannot represent the divisor for the requested frequency"); - return ENOTSUP; - } - - return divisor; - } - - VERIFY_NOT_REACHED(); -} - -ErrorOr SDHostController::sd_clock_supply(u32 frequency) -{ - // SDHC 3.2.1: "SD Clock Supply Sequence" - // The *Clock Control* register is in the lower 16 bits of *Host Configuration 1* - VERIFY((m_registers->host_configuration_1 & sd_clock_enable) == 0); - - // 1. Find out the divisor to determine the SD Clock Frequency - u32 const sd_clock_frequency = TRY(retrieve_sd_clock_frequency()); - u32 divisor = TRY(calculate_sd_clock_divisor(sd_clock_frequency, frequency)); - - // 2. Set Internal Clock Enable and SDCLK Frequency Select in the Clock Control register - u32 const eight_lower_bits_of_sdclk_frequency_select = (divisor & 0xff) << 8; - u32 sdclk_frequency_select = eight_lower_bits_of_sdclk_frequency_select; - if (host_version() == SD::HostVersion::Version3) { - u32 const two_upper_bits_of_sdclk_frequency_select = (divisor >> 8 & 0x3) << 6; - sdclk_frequency_select |= two_upper_bits_of_sdclk_frequency_select; - } - m_registers->host_configuration_1 = (m_registers->host_configuration_1 & ~sd_clock_divisor_mask) | internal_clock_enable | sdclk_frequency_select; - - // 3. Check Internal Clock Stable in the Clock Control register until it is 1 - if (!retry_with_timeout([&] { return m_registers->host_configuration_1 & internal_clock_stable; })) { - return EIO; - } - - // FIXME: With the default timeout value, reading will sometimes fail on the Raspberry Pi. - // We should be a bit smarter with choosing the right timeout value and handling errors. - m_registers->host_configuration_1 = (m_registers->host_configuration_1 & ~data_timeout_counter_value_mask) | data_timeout_counter_value_max; - - // 4. Set SD Clock Enable in the Clock Control register to 1 - m_registers->host_configuration_1 = m_registers->host_configuration_1 | sd_clock_enable; - - return {}; -} - -ErrorOr SDHostController::sd_clock_stop() -{ - // SDHC 3.2.2: "SD Clock Stop Sequence" - - // The Host Driver shall not clear SD Clock Enable while an SD transaction is executing on the SD Bus -- - // namely, while either Command Inhibit (DAT) or Command Inhibit (CMD) in the Present State register - // is set to 1 - if (!retry_with_timeout([&] { return !m_registers->present_state.command_inhibit_dat && !m_registers->present_state.command_inhibit_cmd; })) { - return EIO; - } - - // 1. Set SD Clock Enable in the Clock Control register to 0 - m_registers->host_configuration_1 = m_registers->host_configuration_1 & ~sd_clock_enable; - return {}; -} - -ErrorOr SDHostController::sd_clock_frequency_change(u32 new_frequency) -{ - // SDHC 3.2.3: "SD Clock Frequency Change Sequence" - - // 1. Execute the SD Clock Stop Sequence - TRY(sd_clock_stop()); - - // 2. Execute the SD Clock Supply Sequence - return sd_clock_supply(new_frequency); -} - -ErrorOr SDHostController::reset_host_controller() -{ - m_registers->host_configuration_0 = 0; - m_registers->host_configuration_1 = m_registers->host_configuration_1 | software_reset_for_all; - if (!retry_with_timeout( - [&] { - return (m_registers->host_configuration_1 & software_reset_for_all) == 0; - })) { - return EIO; - } - - return {}; -} - -ErrorOr SDHostController::transaction_control_with_data_transfer_using_the_dat_line_without_dma( - SD::Command const& command, - u32 argument, - u32 block_count, - u32 block_size, - UserOrKernelBuffer buf, - DataTransferType data_transfer_type) -{ - // SDHC 3.7.2: "Transaction Control with Data Transfer Using DAT Line (without DMA)" - - // 1. Set the value corresponding to the executed data byte length of one block to Block Size register. - // 2. Set the value corresponding to the executed data block count to Block Count register in accordance with Table 2-8. - m_registers->block_size_and_block_count = (block_count << 16) | block_size; - - // 3. Set the argument value to Argument 1 register. - m_registers->argument_1 = argument; - - // 4. Set the value to the Transfer Mode register. The host driver - // determines Multi / Single Block - // Select, Block Count Enable, Data Transfer Direction, Auto CMD12 Enable - // and DMA Enable. Multi / Single Block Select and Block Count Enable are - // determined according to Table 2-8. (NOTE: We assume `cmd` already has - // the correct flags set) - // 5. Set the value to Command register. - m_registers->transfer_mode_and_command = command.raw; - - // 6. Then, wait for the Command Complete Interrupt. - if (!retry_with_timeout([&]() { return m_registers->interrupt_status.command_complete; })) { - return EIO; - } - - // 7. Write 1 to the Command Complete in the Normal Interrupt Status - // register for clearing this bit. - m_registers->interrupt_status.raw = command_complete; - - // 8. Read Response register and get necessary information of the issued - // command - // (FIXME: Return the value for better error handling) - - // 9. In the case where this sequence is for write to a card, go to step - // (10). - // In case of read from a card, go to step (14). - if (data_transfer_type == DataTransferType::Write) { - - for (u32 i = 0; i < block_count; i++) { - // 10. Then wait for Buffer Write Ready Interrupt. - if (!retry_with_timeout( - [&]() { - return m_registers->interrupt_status.buffer_write_ready; - })) { - return EIO; - } - - // 11. Write 1 to the Buffer Write Ready in the Normal Interrupt Status register for clearing this bit. - m_registers->interrupt_status.raw = buffer_write_ready; - - // 12. Write block data (in according to the number of bytes specified at the step (1)) to Buffer Data Port register. - u32 temp; - for (u32 j = 0; j < block_size / sizeof(u32); j++) { - TRY(buf.read(&temp, i * block_size + sizeof(u32) * j, sizeof(u32))); - m_registers->buffer_data_port = temp; - } - - // 13. Repeat until all blocks are sent and then go to step (18). - } - } else { - for (u32 i = 0; i < block_count; i++) { - // 14. Then wait for the Buffer Read Ready Interrupt. - if (!retry_with_timeout([&]() { return m_registers->interrupt_status.buffer_read_ready; })) { - return EIO; - } - - // 15. Write 1 to the Buffer Read Ready in the Normal Interrupt Status - // register for clearing this bit. - m_registers->interrupt_status.raw = buffer_read_ready; - - // 16. Read block data (in according to the number of bytes specified at - // the step (1)) from the Buffer Data Port register - u32 temp; - for (u32 j = 0; j < block_size / sizeof(u32); j++) { - temp = m_registers->buffer_data_port; - TRY(buf.write(&temp, i * block_size + sizeof(u32) * j, sizeof(u32))); - } - - // 17. Repeat until all blocks are received and then go to step (18). - } - } - - // 18. If this sequence is for Single or Multiple Block Transfer, go to step - // (19). In case of Infinite Block Transfer, go to step (21) - - // 19. Wait for Transfer Complete Interrupt. - if (!retry_with_timeout( - [&]() { return m_registers->interrupt_status.transfer_complete; })) { - return EIO; - } - - // 20. Write 1 to the Transfer Complete in the Normal Interrupt Status - // register for clearing this bit - m_registers->interrupt_status.raw = transfer_complete; - return {}; -} - -u32 SDHostController::make_adma_descriptor_table(u32 block_count) -{ - // FIXME: We might be able to write to the destination buffer directly - // Especially with 64 bit addressing enabled - // This might cost us more descriptor entries but avoids the memcpy at the end - // of each read cycle - - FlatPtr adma_descriptor_physical = m_dma_region->physical_page(0)->paddr().get(); - FlatPtr adma_dma_region_physical = adma_descriptor_physical + PAGE_SIZE; - - FlatPtr adma_descriptor_virtual = m_dma_region->vaddr().get(); - - u32 offset = 0; - u32 blocks_transferred = 0; - u32 blocks_per_descriptor = (1 << 16) / block_len; - - using enum OperatingMode; - switch (m_mode) { - case ADMA2_32: { - u32 i = 0; - Array& command_buffer = *bit_cast*>(adma_descriptor_virtual); - for (; i < 64; ++i) { - FlatPtr physical_transfer_address = adma_dma_region_physical + offset; - VERIFY(physical_transfer_address >> 32 == 0); - // If the remaining block count is less than the maximum addressable blocks - // we need to set the actual length and break out of the loop - if (block_count - blocks_transferred < blocks_per_descriptor) { - u32 blocks_to_transfer = block_count - blocks_transferred; - command_buffer[i] = SD::DMADescriptor64 { - .valid = 1, - .end = 1, - .interrupt = 0, - .action = SD::DMAAction::Tran, - .length_upper = 0, - .length_lower = static_cast(blocks_to_transfer * block_len), - .address = static_cast(physical_transfer_address), - }; - blocks_transferred += blocks_to_transfer; - offset += static_cast(blocks_to_transfer) * block_len; - break; - } - - command_buffer[i] = SD::DMADescriptor64 { - .valid = 1, - .end = 0, - .interrupt = 0, - .action = SD::DMAAction::Tran, - .length_upper = 0, - .length_lower = 0, // length of 0 means 1<<16 bytes - .address = static_cast(physical_transfer_address), - }; - - blocks_transferred += blocks_per_descriptor; - offset += (1 << 16); - } - command_buffer[min(i, 63)].end = 1; - break; - } - case ADMA2_64: { - u32 i = 0; - Array& command_buffer = *bit_cast*>(adma_descriptor_virtual); - for (; i < 32; ++i) { - FlatPtr physical_transfer_address = adma_dma_region_physical + offset; - VERIFY(physical_transfer_address >> 32 == 0); - // If the remaining block count is less than the maximum addressable blocks - // we need to set the actual length and break out of the loop - if (block_count - blocks_transferred < blocks_per_descriptor) { - u32 blocks_to_read = block_count - blocks_transferred; - command_buffer[i] = SD::DMADescriptor128 { - .valid = 1, - .end = 1, - .interrupt = 0, - .action = SD::DMAAction::Tran, - .length_upper = 0, - .length_lower = static_cast(blocks_to_read * block_len), - .address_low = static_cast((physical_transfer_address + offset) & 0xFFFF'FFFF), - .address_high = static_cast((physical_transfer_address + offset) >> 32), - }; - blocks_transferred += blocks_to_read; - offset += static_cast(blocks_to_read) * block_len; - break; - } - - command_buffer[i] = SD::DMADescriptor128 { - .valid = 1, - .end = 0, - .interrupt = 0, - .action = SD::DMAAction::Tran, - .length_upper = 0, - .length_lower = 0, // length of 0 means 1<<16 bytes - .address_low = static_cast((physical_transfer_address + offset) & 0xFFFF'FFFF), - .address_high = static_cast((physical_transfer_address + offset) >> 32), - }; - - blocks_transferred += blocks_per_descriptor; - offset += (1 << 16); - } - command_buffer[min(i, 31)].end = 1; - break; - } - case PIO: - VERIFY_NOT_REACHED(); - } - - return blocks_transferred; -} - -ErrorOr SDHostController::transfer_blocks_adma2(u32 block_address, u32 block_count, UserOrKernelBuffer out, SD::DataTransferDirection direction) -{ - using enum OperatingMode; - - FlatPtr adma_descriptor_physical = m_dma_region->physical_page(0)->paddr().get(); - - FlatPtr adma_descriptor_virtual = m_dma_region->vaddr().get(); - FlatPtr adma_dma_region_virtual = adma_descriptor_virtual + PAGE_SIZE; - - AK::ArmedScopeGuard abort_guard { - [] { - dbgln("Aborting SDHC ADMA read"); - TODO(); - } - }; - - // 3.7.2.3 Using ADMA - u32 blocks_per_descriptor = (1 << 16) / block_len; - u32 addressable_blocks_per_transfer = blocks_per_descriptor * (m_mode == ADMA2_32 ? 64 : 32); - size_t host_offset = 0; - size_t card_offset = 0; - u32 blocks_transferred_total = 0; - - while (blocks_transferred_total < block_count) { - // When writing to the card we must prime the transfer buffer with the data we want to write - // FIXME: We might be able to transfer to/from the destination/origin buffer directly - // Especially with 64 bit addressing enabled - // This might cost us more descriptor entries, when the physical range is segmented, - // but avoids the memcpy at the end of each transfer cycle - if (direction == SD::DataTransferDirection::HostToCard) - TRY(out.read(bit_cast(adma_dma_region_virtual), host_offset, min(block_count - blocks_transferred_total, addressable_blocks_per_transfer) * block_len)); - - // (1) Create Descriptor table for ADMA in the system memory - u32 blocks_transferred = make_adma_descriptor_table(block_count); - card_offset += blocks_transferred * block_len; - - // (2) Set the Descriptor address for ADMA in the ADMA System Address register. - m_registers->adma_system_address[0] = static_cast(adma_descriptor_physical & 0xFFFF'FFFF); - if (m_mode == ADMA2_64) - m_registers->adma_system_address[1] = static_cast(adma_descriptor_physical >> 32); - - // (3) Set the value corresponding to the executed data byte length of one block in the Block Size - // register. - // (4) Set the value corresponding to the executed data block count in the Block Count register in - // accordance with Table 2-9. Refer to Section 1.15 for more details. - // Note: To avoid the restriction of the 16 bit block count we disable the block counter - // and do not set the block count, resulting in an "Infinite Transfer" (SDHC Table 2-9) - // ADMA has its own way of encoding block counts and to signal transfer termination - m_registers->block_size_and_block_count = block_len; - - // (5) Set the argument value to the Argument register. - m_registers->argument_1 = block_address; - - // (6) Set the value to the Transfer Mode register. The Host Driver determines Multi / Single Block - // Select, Block Count Enable, Data Transfer Direction, Auto CMD12 Enable and DMA - // Enable. Multi / Single Block Select and Block Count Enable are determined according to - // Table 2-9. - // If response check is enabled (Response Error Check Enable =1), set Response Interrupt - // Disable to 1 and select Response Type R1 / R5 - SD::Command command = { - .dma_enable = 1, - .block_counter = 0, - .auto_command = blocks_transferred > 1 ? SD::SendAutoCommand::Command12 : SD::SendAutoCommand::Disabled, - .direction = direction, - .multiblock = blocks_transferred > 1, - .response_type_r1r5 = 0, - .response_error_check = 0, - .response_interrupt_disable = 0, - .reserved1 = 0, - .response_type = SD::ResponseType::ResponseOf48Bits, - .sub_command_flag = 0, - .crc_enable = 1, - .idx_enable = 0, - .is_data = 1, - .type = SD::CommandType::Normal, - .index = direction == SD::DataTransferDirection::HostToCard ? (blocks_transferred > 1 ? SD::CommandIndex::WriteMultipleBlock : SD::CommandIndex::WriteSingleBlock) - : (blocks_transferred > 1 ? SD::CommandIndex::ReadMultipleBlock : SD::CommandIndex::ReadSingleBlock), - .reserved3 = 0 - }; - - // (7) Set the value to the Command register. - // Note: When writing to the upper byte [3] of the Command register, the SD command is issued - // and DMA is started. - m_registers->transfer_mode_and_command = command.raw; - - // (8) If response check is enabled, go to stop (11) else wait for the Command Complete Interrupt. - // Note: We never enabled response checking - if (!retry_with_timeout([this]() { return m_registers->interrupt_status.command_complete; })) { - dbgln("SDHC: ADMA2 command response timed out"); - } - // (9) Write 1 to the Command Complete in the Normal Interrupt Status register to clear this bit. - // Note: We cannot write to the nit field member directly, due to that also possibly - // setting the already completed `transfer_complete` flag, making the next check time out. - m_registers->interrupt_status.raw = command_complete; - // TODO: (10) Read Response register and get necessary information of the issued command - - // (11) Wait for the Transfer Complete Interrupt and ADMA Error Interrupt. - // FIXME: Especially with big transfers this might timeout before the transfer is finished, although - // No error has has happened - // We should set this up so that it actually waits for the interrupts via a designated handler - // Note, that the SDHC has a way to detect transfer timeouts on its own - if (!retry_with_timeout([this]() { return m_registers->interrupt_status.transfer_complete || m_registers->interrupt_status.adma_error; })) { - dbgln("SDHC: ADMA2 transfer timed out"); - return EIO; - } - // (12) If Transfer Complete is set to 1, go to Step (13) - if (m_registers->interrupt_status.transfer_complete) { - // (13) Write 1 to the Transfer Complete Status in the Normal Interrupt Status register to clear this bit. - m_registers->interrupt_status.transfer_complete = 1; - } - // else if ADMA Error Interrupt is set to 1, go to Step (14). - else if (m_registers->interrupt_status.adma_error) { - // (14) Write 1 to the ADMA Error Interrupt Status in the Error Interrupt Status register to clear this bit. - m_registers->interrupt_status.adma_error = 1; - // (15) Abort ADMA operation. SD card operation should be stopped by issuing abort command. If - // necessary, the Host Driver checks ADMA Error Status register to detect why ADMA error is - // generated - dmesgln("SDHC transfer failed, ADMA Error Status: {:2b}", AK::to_underlying(m_registers->adma_error_status.state)); - // The scope guard will handle the Abort - return EIO; - } else { - VERIFY_NOT_REACHED(); - } - - // Copy the read data to the correct memory location - // FIXME: As described above, we may be able to target the destination buffer directly - if (direction == SD::DataTransferDirection::CardToHost) - TRY(out.write(bit_cast(adma_dma_region_virtual), host_offset, blocks_transferred * block_len)); - - blocks_transferred_total += blocks_transferred; - host_offset = card_offset; - block_address += card_offset; - card_offset = 0; - } - - abort_guard.disarm(); - return {}; -} - -ErrorOr SDHostController::read_block(Badge, u32 block_address, u32 block_count, UserOrKernelBuffer out) -{ - VERIFY(is_card_inserted()); - - using enum OperatingMode; - switch (m_mode) { - case OperatingMode::ADMA2_32: - case OperatingMode::ADMA2_64: - return transfer_blocks_adma2(block_address, block_count, out, SD::DataTransferDirection::CardToHost); - case PIO: { - if (block_count > 1) { - return transaction_control_with_data_transfer_using_the_dat_line_without_dma( - SD::Commands::read_multiple_block, - block_address, - block_count, - block_len, - out, - DataTransferType::Read); - } - - return transaction_control_with_data_transfer_using_the_dat_line_without_dma( - SD::Commands::read_single_block, - block_address, - block_count, - block_len, - out, - DataTransferType::Read); - } - default: - VERIFY_NOT_REACHED(); - } -} - -ErrorOr SDHostController::write_block(Badge, u32 block_address, u32 block_count, UserOrKernelBuffer in) -{ - VERIFY(is_card_inserted()); - using enum OperatingMode; - switch (m_mode) { - case OperatingMode::ADMA2_32: - case OperatingMode::ADMA2_64: - return transfer_blocks_adma2(block_address, block_count, in, SD::DataTransferDirection::HostToCard); - case PIO: { - if (block_count > 1) { - return transaction_control_with_data_transfer_using_the_dat_line_without_dma( - SD::Commands::write_multiple_block, - block_address, - block_count, - block_len, - in, - DataTransferType::Write); - } - return transaction_control_with_data_transfer_using_the_dat_line_without_dma( - SD::Commands::write_single_block, - block_address, - block_count, - block_len, - in, - DataTransferType::Write); - } - default: - VERIFY_NOT_REACHED(); - }; -} - -ErrorOr SDHostController::retrieve_sd_configuration_register(u32 relative_card_address) -{ - SD::SDConfigurationRegister scr; - - TRY(issue_command(SD::Commands::app_cmd, relative_card_address)); - TRY(wait_for_response()); - TRY(transaction_control_with_data_transfer_using_the_dat_line_without_dma( - SD::Commands::app_send_scr, - 0, 1, 8, - UserOrKernelBuffer::for_kernel_buffer(scr.raw), DataTransferType::Read)); - - return scr; -} - -ErrorOr SDHostController::retrieve_sd_clock_frequency() -{ - if (m_registers->capabilities.base_clock_frequency == 0) { - // Spec says: - // If these bits are all 0, the Host System has to get information via another method - TODO(); - } - i64 const one_mhz = 1'000'000; - return { m_registers->capabilities.base_clock_frequency * one_mhz }; -} - -// PLSS Table 4-43 : Card Status Field/Command -bool SDHostController::card_status_contains_errors(SD::Command const& command, u32 resp) -{ - SD::CardStatus status; - // PLSS 4.9.5 R6 - if (command.index == SD::CommandIndex::SendRelativeAddr) { - status.raw = (resp & 0x1fff) | ((resp & 0x2000) << 6) | ((resp & 0x4000) << 8) | ((resp & 0x8000) << 8); - } else { - status.raw = resp; - } - - bool common_errors = status.error || status.cc_error || status.card_ecc_failed || status.illegal_command || status.com_crc_error || status.lock_unlock_failed || status.card_is_locked || status.wp_violation || status.erase_param || status.csd_overwrite; - - bool contains_errors = false; - switch (command.index) { - case SD::CommandIndex::SendRelativeAddr: - if (status.error || status.illegal_command || status.com_crc_error) { - contains_errors = true; - } - break; - case SD::CommandIndex::SelectCard: - if (common_errors) { - contains_errors = true; - } - break; - case SD::CommandIndex::SetBlockLen: - if (common_errors || status.block_len_error) { - contains_errors = true; - } - break; - case SD::CommandIndex::ReadSingleBlock: - case SD::CommandIndex::ReadMultipleBlock: - if (common_errors || status.address_error || status.out_of_range) { - contains_errors = true; - } - break; - case SD::CommandIndex::WriteSingleBlock: - case SD::CommandIndex::WriteMultipleBlock: - if (common_errors || status.block_len_error || status.address_error || status.out_of_range) { - contains_errors = true; - } - break; - case SD::CommandIndex::AppSendScr: - if (common_errors) { - contains_errors = true; - } - break; - case SD::CommandIndex::AppCmd: - if (common_errors) { - contains_errors = true; - } - break; - default: - break; - } - - return contains_errors; -} - -} diff --git a/Kernel/Devices/Storage/SD/SDHostController.h b/Kernel/Devices/Storage/SD/SDHostController.h deleted file mode 100644 index 2d5e625b370..00000000000 --- a/Kernel/Devices/Storage/SD/SDHostController.h +++ /dev/null @@ -1,110 +0,0 @@ -/* - * Copyright (c) 2023, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include -#include -#include -#include - -namespace Kernel { - -class SDHostController : public StorageController { -public: - SDHostController(); - ErrorOr initialize(); - - virtual ~SDHostController() = default; - - virtual LockRefPtr device(u32 index) const override { return index == 0 ? m_card : nullptr; } - virtual size_t devices_count() const override { return m_card ? 1 : 0; } - virtual void complete_current_request(AsyncDeviceRequest::RequestResult) override; - - ErrorOr read_block(Badge, u32 block_address, u32 block_count, UserOrKernelBuffer out); - ErrorOr write_block(Badge, u32 block_address, u32 block_count, UserOrKernelBuffer in); - - void try_enable_dma(); - -protected: - virtual SD::HostControlRegisterMap volatile* get_register_map_base_address() = 0; - -private: - ErrorOr> try_initialize_inserted_card(); - - bool is_card_inserted() const - { - return m_registers->present_state.card_inserted; - } - - SD::HostVersion host_version() { return m_registers->slot_interrupt_status_and_version.specification_version_number; } - - ErrorOr reset_host_controller(); - - SD::Command last_sent_command() - { - SD::Command command {}; - command.raw = m_registers->transfer_mode_and_command; - return command; - } - bool currently_active_command_uses_transfer_complete_interrupt(); - - ErrorOr calculate_sd_clock_divisor(u32 sd_clock_frequency, u32 frequency); - bool is_sd_clock_enabled(); - ErrorOr sd_clock_supply(u32 frequency); - ErrorOr sd_clock_stop(); - ErrorOr sd_clock_frequency_change(u32 frequency); - ErrorOr retrieve_sd_clock_frequency(); - - struct Response { - u32 response[4]; - }; - ErrorOr issue_command(SD::Command const&, u32 argument); - ErrorOr wait_for_response(); - - bool card_status_contains_errors(SD::Command const&, u32); - - bool retry_with_timeout(Function, i64 delay_between_tries = 100); - - enum class DataTransferType { - Read, - Write - }; - enum class OperatingMode { - PIO, - ADMA2_32, - ADMA2_64 - }; - - ErrorOr transaction_control_with_data_transfer_using_the_dat_line_without_dma(SD::Command const&, u32 argument, u32 block_count, u32 block_size, UserOrKernelBuffer, DataTransferType data_transfer_type); - ErrorOr transfer_blocks_adma2(u32 block_address, u32 block_count, UserOrKernelBuffer, SD::DataTransferDirection); - ErrorOr retrieve_sd_configuration_register(u32 relative_card_address); - - u32 make_adma_descriptor_table(u32 block_count); - - volatile SD::HostControlRegisterMap* m_registers; - LockRefPtr m_card { nullptr }; - - u32 m_hardware_relative_controller_id { 0 }; - OperatingMode m_mode { OperatingMode::PIO }; - Mutex m_lock { "SDHostController"sv }; - - // For ADMA2 - // One page of descriptor tables with 16 bit lengths can address writes of - // Up to 4 MiB ADMA2_32 - // Up to 2 MiB ADMA2_64 - // To not over allocate we use a buffer of just 16 pages - // FIXME: Investigate the average usage and adjust this - constexpr static size_t dma_rw_buffer_size = 16 * PAGE_SIZE; - constexpr static size_t dma_region_size = PAGE_SIZE + dma_rw_buffer_size; - OwnPtr m_dma_region; -}; - -} diff --git a/Kernel/Devices/Storage/SD/SDMemoryCard.cpp b/Kernel/Devices/Storage/SD/SDMemoryCard.cpp deleted file mode 100644 index db960cdac99..00000000000 --- a/Kernel/Devices/Storage/SD/SDMemoryCard.cpp +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright (c) 2023, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include - -namespace Kernel { - -SDMemoryCard::SDMemoryCard(SDHostController& sdhc, StorageDevice::LUNAddress lun_address, u32 hardware_relative_controller_id, u32 block_len, u64 capacity_in_blocks, u32 relative_card_address, SD::OperatingConditionRegister ocr, SD::CardIdentificationRegister cid, SD::SDConfigurationRegister scr) - : StorageDevice(lun_address, hardware_relative_controller_id, block_len, capacity_in_blocks) - , m_sdhc(sdhc) - , m_relative_card_address(relative_card_address) - , m_ocr(ocr) - , m_cid(cid) - , m_scr(scr) -{ -} - -void SDMemoryCard::start_request(AsyncBlockDeviceRequest& request) -{ - // FIXME: Make this asynchronous - MutexLocker locker(m_lock); - - VERIFY(request.block_size() == block_size()); - - auto buffer = request.buffer(); - u32 block_address = request.block_index(); - if (card_addressing_mode() == CardAddressingMode::ByteAddressing) { - block_address *= block_size(); - } - - if (request.request_type() == AsyncBlockDeviceRequest::RequestType::Write) { - if (m_sdhc.write_block({}, block_address, request.block_count(), buffer).is_error()) { - request.complete(AsyncDeviceRequest::Failure); - return; - } - } else { - if (m_sdhc.read_block({}, block_address, request.block_count(), buffer).is_error()) { - request.complete(AsyncDeviceRequest::Failure); - return; - } - } - - request.complete(AsyncDeviceRequest::Success); -} - -} diff --git a/Kernel/Devices/Storage/SD/SDMemoryCard.h b/Kernel/Devices/Storage/SD/SDMemoryCard.h deleted file mode 100644 index 98985d35f38..00000000000 --- a/Kernel/Devices/Storage/SD/SDMemoryCard.h +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright (c) 2023, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include -#include - -namespace Kernel { - -class SDHostController; - -class SDMemoryCard : public StorageDevice { -public: - SDMemoryCard(SDHostController& sdhc, StorageDevice::LUNAddress, u32 hardware_relative_controller_id, u32 block_len, u64 capacity_in_blocks, u32 relative_card_address, SD::OperatingConditionRegister ocr, SD::CardIdentificationRegister cid, SD::SDConfigurationRegister scr); - - // ^StorageDevice - virtual CommandSet command_set() const override { return CommandSet::SD; } - - // ^BlockDevice - virtual void start_request(AsyncBlockDeviceRequest&) override; - -private: - enum class CardAddressingMode { - ByteAddressing, - BlockAddressing - }; - CardAddressingMode card_addressing_mode() const - { - return m_ocr.card_capacity_status ? CardAddressingMode::BlockAddressing : CardAddressingMode::ByteAddressing; - } - - Mutex m_lock { "SDMemoryCard"sv }; - SDHostController& m_sdhc; - - u32 m_relative_card_address; - SD::OperatingConditionRegister m_ocr; - SD::CardIdentificationRegister m_cid; - SD::SDConfigurationRegister m_scr; -}; - -} diff --git a/Kernel/Devices/Storage/StorageController.cpp b/Kernel/Devices/Storage/StorageController.cpp deleted file mode 100644 index 2e104bc2773..00000000000 --- a/Kernel/Devices/Storage/StorageController.cpp +++ /dev/null @@ -1,18 +0,0 @@ -/* - * Copyright (c) 2022, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include - -namespace Kernel { - -StorageController::StorageController(u32 hardware_relative_controller_id) - : m_controller_id(StorageManagement::generate_controller_id()) - , m_hardware_relative_controller_id(hardware_relative_controller_id) -{ -} - -} diff --git a/Kernel/Devices/Storage/StorageController.h b/Kernel/Devices/Storage/StorageController.h deleted file mode 100644 index 2aec91af77e..00000000000 --- a/Kernel/Devices/Storage/StorageController.h +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright (c) 2020, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace Kernel { - -class AsyncBlockDeviceRequest; -class StorageDevice; -class StorageController : public AtomicRefCounted { - -public: - virtual ~StorageController() = default; - - virtual LockRefPtr device(u32 index) const = 0; - virtual size_t devices_count() const = 0; - - u32 controller_id() const { return m_controller_id; } - u32 hardware_relative_controller_id() const { return m_hardware_relative_controller_id; } - -protected: - virtual void complete_current_request(AsyncDeviceRequest::RequestResult) = 0; - - explicit StorageController(u32 hardware_relative_controller_id); - -private: - u32 const m_controller_id { 0 }; - - u32 const m_hardware_relative_controller_id { 0 }; -}; -} diff --git a/Kernel/Devices/Storage/StorageDevice.cpp b/Kernel/Devices/Storage/StorageDevice.cpp deleted file mode 100644 index 0022d720021..00000000000 --- a/Kernel/Devices/Storage/StorageDevice.cpp +++ /dev/null @@ -1,254 +0,0 @@ -/* - * Copyright (c) 2020, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace Kernel { - -StorageDevice::StorageDevice(LUNAddress logical_unit_number_address, u32 hardware_relative_controller_id, size_t sector_size, u64 max_addressable_block) - : BlockDevice(StorageManagement::storage_type_major_number(), StorageManagement::generate_storage_minor_number(), sector_size) - , m_logical_unit_number_address(logical_unit_number_address) - , m_hardware_relative_controller_id(hardware_relative_controller_id) - , m_max_addressable_block(max_addressable_block) - , m_blocks_per_page(PAGE_SIZE / block_size()) -{ -} - -ErrorOr StorageDevice::after_inserting() -{ - auto sysfs_storage_device_directory = StorageDeviceSysFSDirectory::create(SysFSStorageDirectory::the(), *this); - m_sysfs_device_directory = sysfs_storage_device_directory; - SysFSStorageDirectory::the().plug({}, *sysfs_storage_device_directory); - VERIFY(!m_symlink_sysfs_component); - auto sys_fs_component = TRY(SysFSSymbolicLinkDeviceComponent::try_create(SysFSBlockDevicesDirectory::the(), *this, *m_sysfs_device_directory)); - m_symlink_sysfs_component = sys_fs_component; - after_inserting_add_symlink_to_device_identifier_directory(); - after_inserting_add_to_device_management(); - return {}; -} - -void StorageDevice::will_be_destroyed() -{ - // NOTE: We check if m_symlink_sysfs_component is not null, because if we failed - // in StorageDevice::after_inserting(), then that method will not set m_symlink_sysfs_component. - if (m_symlink_sysfs_component) { - before_will_be_destroyed_remove_symlink_from_device_identifier_directory(); - m_symlink_sysfs_component.clear(); - } - SysFSStorageDirectory::the().unplug({}, *m_sysfs_device_directory); - before_will_be_destroyed_remove_from_device_management(); -} - -StringView StorageDevice::class_name() const -{ - return "StorageDevice"sv; -} - -StringView StorageDevice::command_set_to_string_view() const -{ - switch (command_set()) { - case CommandSet::SCSI: - return "scsi"sv; - case CommandSet::ATA: - return "ata"sv; - case CommandSet::NVMe: - return "nvme"sv; - case CommandSet::SD: - return "sd"sv; - default: - break; - } - VERIFY_NOT_REACHED(); -} - -ErrorOr StorageDevice::read(OpenFileDescription&, u64 offset, UserOrKernelBuffer& outbuf, size_t len) -{ - // NOTE: The last available offset is actually just after the last addressable block. - if (offset >= (max_mathematical_addressable_block() * block_size())) - return 0; - size_t nread = min(static_cast((max_mathematical_addressable_block() * block_size()) - offset), len); - u64 index = offset >> block_size_log(); - off_t offset_within_block = 0; - size_t whole_blocks = nread >> block_size_log(); - size_t remaining = nread - (whole_blocks << block_size_log()); - - // PATAChannel will chuck a wobbly if we try to read more than PAGE_SIZE - // at a time, because it uses a single page for its DMA buffer. - if (whole_blocks >= m_blocks_per_page) { - whole_blocks = m_blocks_per_page; - remaining = 0; - } - - if (nread < block_size()) - offset_within_block = offset - (index << block_size_log()); - - dbgln_if(STORAGE_DEVICE_DEBUG, "StorageDevice::read() index={}, whole_blocks={}, remaining={}", index, whole_blocks, remaining); - - if (whole_blocks > 0) { - auto read_request = TRY(try_make_request(AsyncBlockDeviceRequest::Read, index, whole_blocks, outbuf, whole_blocks * block_size())); - auto result = read_request->wait(); - if (result.wait_result().was_interrupted()) - return EINTR; - switch (result.request_result()) { - case AsyncDeviceRequest::Failure: - case AsyncDeviceRequest::Cancelled: - return EIO; - case AsyncDeviceRequest::MemoryFault: - return EFAULT; - default: - break; - } - } - - off_t pos = whole_blocks * block_size(); - - if (remaining > 0) { - auto data = TRY(ByteBuffer::create_uninitialized(block_size())); - auto data_buffer = UserOrKernelBuffer::for_kernel_buffer(data.data()); - auto read_request = TRY(try_make_request(AsyncBlockDeviceRequest::Read, index + whole_blocks, 1, data_buffer, block_size())); - auto result = read_request->wait(); - if (result.wait_result().was_interrupted()) - return EINTR; - switch (result.request_result()) { - case AsyncDeviceRequest::Failure: - return pos; - case AsyncDeviceRequest::Cancelled: - return EIO; - case AsyncDeviceRequest::MemoryFault: - // This should never happen, we're writing to a kernel buffer! - VERIFY_NOT_REACHED(); - default: - break; - } - TRY(outbuf.write(data.offset_pointer(offset_within_block), pos, remaining)); - } - - return pos + remaining; -} - -ErrorOr StorageDevice::write(OpenFileDescription&, u64 offset, UserOrKernelBuffer const& inbuf, size_t len) -{ - // NOTE: The last available offset is actually just after the last addressable block. - if (offset >= (max_mathematical_addressable_block() * block_size())) - return Error::from_errno(ENOSPC); - size_t nwrite = min(static_cast((max_mathematical_addressable_block() * block_size()) - offset), len); - u64 index = offset >> block_size_log(); - off_t offset_within_block = 0; - size_t whole_blocks = nwrite >> block_size_log(); - size_t remaining = nwrite - (whole_blocks << block_size_log()); - - // PATAChannel will chuck a wobbly if we try to write more than PAGE_SIZE - // at a time, because it uses a single page for its DMA buffer. - if (whole_blocks >= m_blocks_per_page) { - whole_blocks = m_blocks_per_page; - remaining = 0; - } - - if (nwrite < block_size()) - offset_within_block = offset - (index << block_size_log()); - - // We try to allocate the temporary block buffer for partial writes *before* we start any full block writes, - // to try and prevent partial writes - Optional partial_write_block; - if (remaining > 0) - partial_write_block = TRY(ByteBuffer::create_zeroed(block_size())); - - dbgln_if(STORAGE_DEVICE_DEBUG, "StorageDevice::write() index={}, whole_blocks={}, remaining={}", index, whole_blocks, remaining); - - if (whole_blocks > 0) { - auto write_request = TRY(try_make_request(AsyncBlockDeviceRequest::Write, index, whole_blocks, inbuf, whole_blocks * block_size())); - auto result = write_request->wait(); - if (result.wait_result().was_interrupted()) - return EINTR; - switch (result.request_result()) { - case AsyncDeviceRequest::Failure: - case AsyncDeviceRequest::Cancelled: - return EIO; - case AsyncDeviceRequest::MemoryFault: - return EFAULT; - default: - break; - } - } - - off_t pos = whole_blocks * block_size(); - - // since we can only write in block_size() increments, if we want to do a - // partial write, we have to read the block's content first, modify it, - // then write the whole block back to the disk. - if (remaining > 0) { - auto data_buffer = UserOrKernelBuffer::for_kernel_buffer(partial_write_block->data()); - { - auto read_request = TRY(try_make_request(AsyncBlockDeviceRequest::Read, index + whole_blocks, 1, data_buffer, block_size())); - auto result = read_request->wait(); - if (result.wait_result().was_interrupted()) - return EINTR; - switch (result.request_result()) { - case AsyncDeviceRequest::Failure: - return pos; - case AsyncDeviceRequest::Cancelled: - return EIO; - case AsyncDeviceRequest::MemoryFault: - // This should never happen, we're writing to a kernel buffer! - VERIFY_NOT_REACHED(); - default: - break; - } - } - - TRY(inbuf.read(partial_write_block->offset_pointer(offset_within_block), pos, remaining)); - - { - auto write_request = TRY(try_make_request(AsyncBlockDeviceRequest::Write, index + whole_blocks, 1, data_buffer, block_size())); - auto result = write_request->wait(); - if (result.wait_result().was_interrupted()) - return EINTR; - switch (result.request_result()) { - case AsyncDeviceRequest::Failure: - return pos; - case AsyncDeviceRequest::Cancelled: - return EIO; - case AsyncDeviceRequest::MemoryFault: - // This should never happen, we're writing to a kernel buffer! - VERIFY_NOT_REACHED(); - default: - break; - } - } - } - - return pos + remaining; -} - -ErrorOr StorageDevice::ioctl(OpenFileDescription&, unsigned request, Userspace arg) -{ - switch (request) { - case STORAGE_DEVICE_GET_SIZE: { - u64 disk_size = max_mathematical_addressable_block() * block_size(); - return copy_to_user(static_ptr_cast(arg), &disk_size); - break; - } - case STORAGE_DEVICE_GET_BLOCK_SIZE: { - size_t size = block_size(); - return copy_to_user(static_ptr_cast(arg), &size); - break; - } - default: - return EINVAL; - } -} - -} diff --git a/Kernel/Devices/Storage/StorageDevice.h b/Kernel/Devices/Storage/StorageDevice.h deleted file mode 100644 index 8eea8a1b57d..00000000000 --- a/Kernel/Devices/Storage/StorageDevice.h +++ /dev/null @@ -1,111 +0,0 @@ -/* - * Copyright (c) 2020, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include -#include - -namespace Kernel { - -class StorageDevice : public BlockDevice { - friend class StorageManagement; - friend class DeviceManagement; - -public: - // Note: this attribute describes the internal command set of a Storage device. - // For example, an ordinary harddrive utilizes the ATA command set, while - // an ATAPI device (e.g. Optical drive) that is connected to the ATA bus, - // is actually using SCSI commands (packets) encapsulated inside an ATA command. - // The IDE controller code being aware of the possibility of ATAPI devices attached - // to the ATA bus, will check whether the Command set is ATA or SCSI and will act - // accordingly. - // Note: For now, there's simply no distinction between the interface type and the commandset. - // As mentioned above, ATAPI devices use the ATA interface with actual SCSI packets so - // the commandset is SCSI while the interface type is ATA. We simply don't support SCSI over ATA (ATAPI) - // and ATAPI is the exception to no-distinction rule. If we ever put SCSI support in the kernel, - // we can create another enum class to put the distinction. - enum class CommandSet { - SCSI, - ATA, - NVMe, - SD, - }; - - // Note: The most reliable way to address this device from userspace interfaces, - // such as SysFS, is to have one way to enumerate everything in the eyes of userspace. - // Therefore, SCSI LUN (logical unit number) addressing seem to be the most generic way to do this. - // For example, on a legacy ATA instance, one might connect an harddrive to the second IDE controller, - // to the Primary channel as a slave device, which translates to LUN 1:0:1. - // On NVMe, for example, connecting a second PCIe NVMe storage device as a sole NVMe namespace translates - // to LUN 1:1:0. - struct LUNAddress { - u32 controller_id; - u32 target_id; - u32 disk_id; - }; - -public: - u64 max_addressable_block() const { return m_max_addressable_block; } - - // NOTE: This method should be used when we need to calculate the actual - // end of the storage device, because LBAs start counting at 0, which is not - // practical in many cases for verifying IO operation boundaries. - u64 max_mathematical_addressable_block() const { return m_max_addressable_block + 1; } - - // ^BlockDevice - virtual ErrorOr read(OpenFileDescription&, u64, UserOrKernelBuffer&, size_t) override; - virtual bool can_read(OpenFileDescription const&, u64) const override { return true; } - virtual ErrorOr write(OpenFileDescription&, u64, UserOrKernelBuffer const&, size_t) override; - virtual bool can_write(OpenFileDescription const&, u64) const override { return true; } - virtual void prepare_for_unplug() { m_partitions.clear(); } - - Vector> const& partitions() const { return m_partitions; } - - void add_partition(NonnullLockRefPtr disk_partition) { MUST(m_partitions.try_append(disk_partition)); } - - LUNAddress const& logical_unit_number_address() const { return m_logical_unit_number_address; } - - u32 parent_controller_hardware_relative_id() const { return m_hardware_relative_controller_id; } - - virtual CommandSet command_set() const = 0; - - StringView command_set_to_string_view() const; - - // ^File - virtual ErrorOr ioctl(OpenFileDescription&, unsigned request, Userspace arg) final; - -protected: - StorageDevice(LUNAddress, u32 hardware_relative_controller_id, size_t sector_size, u64); - - // ^DiskDevice - virtual StringView class_name() const override; - -private: - virtual ErrorOr after_inserting() override; - virtual void will_be_destroyed() override; - - mutable IntrusiveListNode> m_list_node; - Vector> m_partitions; - - LUNAddress const m_logical_unit_number_address; - - // Note: This data member should be used with LUNAddress target_id and disk_id. - // LUNs are agnostic system-wide addresses, so they are assigned without caring about the specific hardware interfaces. - // This class member on the other side, is meant to be assigned *per hardware type*, - // which means in contrast to the LUNAddress controller_id struct member, we take the index of the hardware - // controller among its fellow controllers of the same hardware type in the system. - u32 const m_hardware_relative_controller_id { 0 }; - - u64 const m_max_addressable_block { 0 }; - size_t const m_blocks_per_page { 0 }; -}; - -} diff --git a/Kernel/Devices/Storage/StorageDevicePartition.cpp b/Kernel/Devices/Storage/StorageDevicePartition.cpp deleted file mode 100644 index 08000eca2ca..00000000000 --- a/Kernel/Devices/Storage/StorageDevicePartition.cpp +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include - -namespace Kernel { - -NonnullLockRefPtr StorageDevicePartition::create(StorageDevice& device, MinorNumber minor_number, Partition::DiskPartitionMetadata metadata) -{ - auto partition_or_error = DeviceManagement::try_create_device(device, minor_number, metadata); - // FIXME: Find a way to propagate errors - VERIFY(!partition_or_error.is_error()); - return partition_or_error.release_value(); -} - -StorageDevicePartition::StorageDevicePartition(StorageDevice& device, MinorNumber minor_number, Partition::DiskPartitionMetadata metadata) - : BlockDevice(100, minor_number, device.block_size()) - , m_device(device) - , m_metadata(metadata) -{ -} - -StorageDevicePartition::~StorageDevicePartition() = default; - -Partition::DiskPartitionMetadata const& StorageDevicePartition::metadata() const -{ - return m_metadata; -} - -void StorageDevicePartition::start_request(AsyncBlockDeviceRequest& request) -{ - auto device = m_device.strong_ref(); - if (!device) - request.complete(AsyncBlockDeviceRequest::RequestResult::Failure); - auto sub_request_or_error = device->try_make_request(request.request_type(), - request.block_index() + m_metadata.start_block(), request.block_count(), request.buffer(), request.buffer_size()); - if (sub_request_or_error.is_error()) - TODO(); - request.add_sub_request(sub_request_or_error.release_value()); -} - -ErrorOr StorageDevicePartition::read(OpenFileDescription& fd, u64 offset, UserOrKernelBuffer& outbuf, size_t len) -{ - auto device = m_device.strong_ref(); - if (!device) - return Error::from_errno(EIO); - // NOTE: The last available offset is actually just after the last addressable block. - if (offset >= (m_metadata.end_block() - m_metadata.start_block() + 1) * block_size()) - return 0; - size_t nread = min(static_cast((m_metadata.end_block() - m_metadata.start_block() + 1) - offset), len); - u64 adjust = m_metadata.start_block() * block_size(); - dbgln_if(OFFD_DEBUG, "StorageDevicePartition::read offset={}, adjust={}, len={}", fd.offset(), adjust, nread); - return device->read(fd, offset + adjust, outbuf, nread); -} - -ErrorOr StorageDevicePartition::write(OpenFileDescription& fd, u64 offset, UserOrKernelBuffer const& inbuf, size_t len) -{ - auto device = m_device.strong_ref(); - if (!device) - return Error::from_errno(EIO); - // NOTE: The last available offset is actually just after the last addressable block. - if (offset >= (m_metadata.end_block() - m_metadata.start_block() + 1) * block_size()) - return Error::from_errno(ENOSPC); - size_t nwrite = min(static_cast((m_metadata.end_block() - m_metadata.start_block() + 1) - offset), len); - u64 adjust = m_metadata.start_block() * block_size(); - dbgln_if(OFFD_DEBUG, "StorageDevicePartition::write offset={}, adjust={}, len={}", offset, adjust, nwrite); - return device->write(fd, offset + adjust, inbuf, nwrite); -} - -StringView StorageDevicePartition::class_name() const -{ - return "StorageDevicePartition"sv; -} - -} diff --git a/Kernel/Devices/Storage/StorageDevicePartition.h b/Kernel/Devices/Storage/StorageDevicePartition.h deleted file mode 100644 index 634cbbb34d4..00000000000 --- a/Kernel/Devices/Storage/StorageDevicePartition.h +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include - -namespace Kernel { - -class StorageDevice; -class StorageDevicePartition final : public BlockDevice { - friend class DeviceManagement; - -public: - static NonnullLockRefPtr create(StorageDevice&, MinorNumber, Partition::DiskPartitionMetadata); - virtual ~StorageDevicePartition(); - - virtual void start_request(AsyncBlockDeviceRequest&) override; - - // ^BlockDevice - virtual ErrorOr read(OpenFileDescription&, u64, UserOrKernelBuffer&, size_t) override; - virtual ErrorOr write(OpenFileDescription&, u64, UserOrKernelBuffer const&, size_t) override; - - // NOTE: Technically we need to query the underlying StorageDevice, - // but since it will always return true in both of these methods, - // we can return true here too. - virtual bool can_read(OpenFileDescription const&, u64) const override { return true; } - virtual bool can_write(OpenFileDescription const&, u64) const override { return true; } - - Partition::DiskPartitionMetadata const& metadata() const; - -private: - StorageDevicePartition(StorageDevice&, MinorNumber, Partition::DiskPartitionMetadata); - virtual StringView class_name() const override; - - LockWeakPtr m_device; - Partition::DiskPartitionMetadata m_metadata; -}; - -} diff --git a/Kernel/Devices/Storage/StorageManagement.cpp b/Kernel/Devices/Storage/StorageManagement.cpp deleted file mode 100644 index cfbee68eba1..00000000000 --- a/Kernel/Devices/Storage/StorageManagement.cpp +++ /dev/null @@ -1,498 +0,0 @@ -/* - * Copyright (c) 2020-2022, Liav A. - * Copyright (c) 2022, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#if ARCH(AARCH64) -# include -#endif -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace Kernel { - -static Singleton s_the; -static Atomic s_storage_device_minor_number; -static Atomic s_partition_device_minor_number; -static Atomic s_controller_id; - -static Atomic s_relative_ahci_controller_id; -static Atomic s_relative_nvme_controller_id; -static Atomic s_relative_sd_controller_id; - -static constexpr StringView partition_uuid_prefix = "PARTUUID:"sv; - -static constexpr StringView partition_number_prefix = "part"sv; -static constexpr StringView block_device_prefix = "block"sv; - -static constexpr StringView ahci_device_prefix = "ahci"sv; -static constexpr StringView nvme_device_prefix = "nvme"sv; -static constexpr StringView logical_unit_number_device_prefix = "lun"sv; -static constexpr StringView sd_device_prefix = "sd"sv; - -UNMAP_AFTER_INIT StorageManagement::StorageManagement() -{ -} - -u32 StorageManagement::generate_relative_nvme_controller_id(Badge) -{ - auto controller_id = s_relative_nvme_controller_id.load(); - s_relative_nvme_controller_id++; - return controller_id; -} - -u32 StorageManagement::generate_relative_ahci_controller_id(Badge) -{ - auto controller_id = s_relative_ahci_controller_id.load(); - s_relative_ahci_controller_id++; - return controller_id; -} - -u32 StorageManagement::generate_relative_sd_controller_id(Badge) -{ - auto controller_id = s_relative_sd_controller_id.load(); - s_relative_sd_controller_id++; - return controller_id; -} - -void StorageManagement::add_device(StorageDevice& device) -{ - m_storage_devices.append(device); - // FIXME: Maybe handle this error in some way shape or form - (void)enumerate_device_partitions(device); -} - -void StorageManagement::remove_device(StorageDevice& device) -{ - m_storage_devices.remove(device); -} - -UNMAP_AFTER_INIT void StorageManagement::enumerate_pci_controllers(bool nvme_poll) -{ - VERIFY(m_controllers.is_empty()); - - if (!kernel_command_line().disable_physical_storage()) { - // NOTE: Search for VMD devices before actually searching for storage controllers - // because the VMD device is only a bridge to such (NVMe) controllers. - MUST(PCI::enumerate([&](PCI::DeviceIdentifier const& device_identifier) -> void { - constexpr PCI::HardwareID vmd_device = { 0x8086, 0x9a0b }; - if (device_identifier.hardware_id() == vmd_device) { - auto controller = PCI::VolumeManagementDevice::must_create(device_identifier); - MUST(PCI::Access::the().add_host_controller_and_scan_for_devices(move(controller))); - } - })); - - RefPtr virtio_controller; - - auto const& handle_mass_storage_device = [&](PCI::DeviceIdentifier const& device_identifier) { - using SubclassID = PCI::MassStorage::SubclassID; - - auto subclass_code = static_cast(device_identifier.subclass_code().value()); - if (subclass_code == SubclassID::SATAController - && device_identifier.prog_if() == PCI::MassStorage::SATAProgIF::AHCI) { - if (auto ahci_controller_or_error = AHCIController::initialize(device_identifier); !ahci_controller_or_error.is_error()) - m_controllers.append(ahci_controller_or_error.value()); - else - dmesgln("Unable to initialize AHCI controller: {}", ahci_controller_or_error.error()); - } - if (subclass_code == SubclassID::NVMeController) { - auto controller = NVMeController::try_initialize(device_identifier, nvme_poll); - if (controller.is_error()) { - dmesgln("Unable to initialize NVMe controller: {}", controller.error()); - } else { - m_controllers.append(controller.release_value()); - } - } - if (VirtIOBlockController::is_handled(device_identifier)) { - if (virtio_controller.is_null()) { - auto controller = make_ref_counted(); - m_controllers.append(controller); - virtio_controller = controller; - } - if (auto res = virtio_controller->add_device(device_identifier); res.is_error()) { - dmesgln("Unable to initialize VirtIO block device: {}", res.error()); - } - } - }; - - auto const& handle_base_device = [&](PCI::DeviceIdentifier const& device_identifier) { - using SubclassID = PCI::Base::SubclassID; - - auto subclass_code = static_cast(device_identifier.subclass_code().value()); - if (subclass_code == SubclassID::SDHostController) { - - auto sdhc_or_error = PCISDHostController::try_initialize(device_identifier); - if (sdhc_or_error.is_error()) { - dmesgln("PCI: Failed to initialize SD Host Controller ({} - {}): {}", device_identifier.address(), device_identifier.hardware_id(), sdhc_or_error.error()); - } else { - m_controllers.append(sdhc_or_error.release_value()); - } - } - }; - - MUST(PCI::enumerate([&](PCI::DeviceIdentifier const& device_identifier) -> void { - auto class_code = device_identifier.class_code(); - if (class_code == PCI::ClassID::MassStorage) { - handle_mass_storage_device(device_identifier); - } else if (class_code == PCI::ClassID::Base) { - handle_base_device(device_identifier); - } - })); - } -} - -UNMAP_AFTER_INIT void StorageManagement::enumerate_storage_devices() -{ - VERIFY(!m_controllers.is_empty()); - for (auto& controller : m_controllers) { - for (size_t device_index = 0; device_index < controller->devices_count(); device_index++) { - auto device = controller->device(device_index); - if (device.is_null()) - continue; - m_storage_devices.append(device.release_nonnull()); - } - } -} - -UNMAP_AFTER_INIT void StorageManagement::dump_storage_devices_and_partitions() const -{ - dbgln("StorageManagement: Detected {} storage devices", m_storage_devices.size_slow()); - for (auto const& storage_device : m_storage_devices) { - auto const& partitions = storage_device.partitions(); - if (partitions.is_empty()) { - dbgln(" Device: block{}:{} (no partitions)", storage_device.major(), storage_device.minor()); - } else { - dbgln(" Device: block{}:{} ({} partitions)", storage_device.major(), storage_device.minor(), partitions.size()); - unsigned partition_number = 1; - for (auto const& partition : partitions) { - dbgln(" Partition: {}, block{}:{} (UUID {})", partition_number, partition->major(), partition->minor(), partition->metadata().unique_guid().to_string()); - partition_number++; - } - } - } -} - -ErrorOr> StorageManagement::try_to_initialize_partition_table(StorageDevice& device) const -{ - auto mbr_table_or_error = Partition::MBRPartitionTable::try_to_initialize(device); - if (!mbr_table_or_error.is_error()) - return mbr_table_or_error.release_value(); - auto ebr_table_or_error = Partition::EBRPartitionTable::try_to_initialize(device); - if (!ebr_table_or_error.is_error()) { - return ebr_table_or_error.release_value(); - } - return TRY(Partition::GUIDPartitionTable::try_to_initialize(device)); -} - -ErrorOr StorageManagement::enumerate_device_partitions(StorageDevice& device) -{ - auto partition_table = TRY(try_to_initialize_partition_table(device)); - for (auto partition_metadata : partition_table->partitions()) { - auto disk_partition = StorageDevicePartition::create(device, generate_partition_minor_number(), partition_metadata); - device.add_partition(disk_partition); - } - - return {}; -} - -UNMAP_AFTER_INIT void StorageManagement::enumerate_disk_partitions() -{ - for (auto& device : m_storage_devices) { - // FIXME: Maybe handle this error in some way shape or form - (void)enumerate_device_partitions(device); - } -} - -UNMAP_AFTER_INIT Optional StorageManagement::extract_boot_device_partition_number_parameter(StringView device_prefix) -{ - VERIFY(m_boot_argument.starts_with(device_prefix)); - VERIFY(!m_boot_argument.starts_with(partition_uuid_prefix)); - auto storage_device_relative_address_view = m_boot_argument.substring_view(device_prefix.length()); - auto parameter_view = storage_device_relative_address_view.find_last_split_view(';'); - if (parameter_view == storage_device_relative_address_view) - return {}; - if (!parameter_view.starts_with(partition_number_prefix)) { - PANIC("StorageManagement: Invalid root boot parameter."); - } - - auto parameter_number = parameter_view.substring_view(partition_number_prefix.length()).to_number(); - if (!parameter_number.has_value()) { - PANIC("StorageManagement: Invalid root boot parameter."); - } - - return parameter_number.value(); -} - -UNMAP_AFTER_INIT Array StorageManagement::extract_boot_device_address_parameters(StringView device_prefix) -{ - VERIFY(!m_boot_argument.starts_with(partition_uuid_prefix)); - Array address_parameters; - auto parameters_view = m_boot_argument.substring_view(device_prefix.length()).find_first_split_view(';'); - size_t parts_count = 0; - bool parse_failure = false; - parameters_view.for_each_split_view(':', SplitBehavior::Nothing, [&](StringView parameter_view) { - if (parse_failure) - return; - if (parts_count > 2) - return; - auto parameter_number = parameter_view.to_number(); - if (!parameter_number.has_value()) { - parse_failure = true; - return; - } - address_parameters[parts_count] = parameter_number.value(); - parts_count++; - }); - - if (parts_count > 3) { - dbgln("StorageManagement: Detected {} parts in boot device parameter.", parts_count); - PANIC("StorageManagement: Invalid root boot parameter."); - } - if (parse_failure) { - PANIC("StorageManagement: Invalid root boot parameter."); - } - - return address_parameters; -} - -UNMAP_AFTER_INIT void StorageManagement::resolve_partition_from_boot_device_parameter(StorageDevice const& chosen_storage_device, StringView boot_device_prefix) -{ - auto possible_partition_number = extract_boot_device_partition_number_parameter(boot_device_prefix); - if (!possible_partition_number.has_value()) - return; - - auto partition_number = possible_partition_number.value(); - if (chosen_storage_device.partitions().size() <= partition_number) - PANIC("StorageManagement: Invalid partition number parameter."); - m_boot_block_device = chosen_storage_device.partitions()[partition_number]; -} - -UNMAP_AFTER_INIT void StorageManagement::determine_hardware_relative_boot_device(StringView relative_hardware_prefix, Function filter_device_callback) -{ - VERIFY(m_boot_argument.starts_with(relative_hardware_prefix)); - auto address_parameters = extract_boot_device_address_parameters(relative_hardware_prefix); - - RefPtr chosen_storage_device; - - for (auto& storage_device : m_storage_devices) { - if (!filter_device_callback(storage_device)) - continue; - auto storage_device_lun = storage_device.logical_unit_number_address(); - if (storage_device.parent_controller_hardware_relative_id() == address_parameters[0] - && storage_device_lun.target_id == address_parameters[1] - && storage_device_lun.disk_id == address_parameters[2]) { - m_boot_block_device = storage_device; - chosen_storage_device = storage_device; - break; - } - } - - if (chosen_storage_device) - resolve_partition_from_boot_device_parameter(*chosen_storage_device, relative_hardware_prefix); -} - -UNMAP_AFTER_INIT void StorageManagement::determine_ata_boot_device() -{ - determine_hardware_relative_boot_device(ahci_device_prefix, [](StorageDevice const& device) -> bool { - return device.command_set() == StorageDevice::CommandSet::ATA; - }); -} - -UNMAP_AFTER_INIT void StorageManagement::determine_nvme_boot_device() -{ - determine_hardware_relative_boot_device(nvme_device_prefix, [](StorageDevice const& device) -> bool { - return device.command_set() == StorageDevice::CommandSet::NVMe; - }); -} - -UNMAP_AFTER_INIT void StorageManagement::determine_sd_boot_device() -{ - determine_hardware_relative_boot_device(sd_device_prefix, [](StorageDevice const& device) -> bool { - return device.command_set() == StorageDevice::CommandSet::SD; - }); -} - -UNMAP_AFTER_INIT void StorageManagement::determine_block_boot_device() -{ - VERIFY(m_boot_argument.starts_with(block_device_prefix)); - auto parameters_view = extract_boot_device_address_parameters(block_device_prefix); - - // Note: We simply fetch the corresponding BlockDevice with the major and minor parameters. - // We don't try to accept and resolve a partition number as it will make this code much more - // complicated. This rule is also explained in the boot_device_addressing(7) manual page. - auto device = DeviceManagement::the().get_device(parameters_view[0], parameters_view[1]); - if (device && device->is_block_device()) - m_boot_block_device = *static_ptr_cast(device); -} - -UNMAP_AFTER_INIT void StorageManagement::determine_boot_device_with_logical_unit_number() -{ - VERIFY(m_boot_argument.starts_with(logical_unit_number_device_prefix)); - auto address_parameters = extract_boot_device_address_parameters(logical_unit_number_device_prefix); - - RefPtr chosen_storage_device; - - for (auto& storage_device : m_storage_devices) { - auto storage_device_lun = storage_device.logical_unit_number_address(); - if (storage_device_lun.controller_id == address_parameters[0] - && storage_device_lun.target_id == address_parameters[1] - && storage_device_lun.disk_id == address_parameters[2]) { - m_boot_block_device = storage_device; - chosen_storage_device = storage_device; - break; - } - } - - if (chosen_storage_device) - resolve_partition_from_boot_device_parameter(*chosen_storage_device, logical_unit_number_device_prefix); -} - -UNMAP_AFTER_INIT bool StorageManagement::determine_boot_device(StringView boot_argument) -{ - VERIFY(!m_controllers.is_empty()); - m_boot_argument = boot_argument; - - if (m_boot_argument.starts_with(block_device_prefix)) { - determine_block_boot_device(); - return m_boot_block_device; - } - - if (m_boot_argument.starts_with(partition_uuid_prefix)) { - determine_boot_device_with_partition_uuid(); - return m_boot_block_device; - } - - if (m_boot_argument.starts_with(logical_unit_number_device_prefix)) { - determine_boot_device_with_logical_unit_number(); - return m_boot_block_device; - } - - if (m_boot_argument.starts_with(ahci_device_prefix)) { - determine_ata_boot_device(); - return m_boot_block_device; - } - - if (m_boot_argument.starts_with(nvme_device_prefix)) { - determine_nvme_boot_device(); - return m_boot_block_device; - } - - if (m_boot_argument.starts_with(sd_device_prefix)) { - determine_sd_boot_device(); - return m_boot_block_device; - } - PANIC("StorageManagement: Invalid root boot parameter."); -} - -UNMAP_AFTER_INIT void StorageManagement::determine_boot_device_with_partition_uuid() -{ - VERIFY(!m_storage_devices.is_empty()); - VERIFY(m_boot_argument.starts_with(partition_uuid_prefix)); - - auto partition_uuid = UUID(m_boot_argument.substring_view(partition_uuid_prefix.length()), UUID::Endianness::Mixed); - - for (auto& storage_device : m_storage_devices) { - for (auto& partition : storage_device.partitions()) { - if (partition->metadata().unique_guid().is_zero()) - continue; - if (partition->metadata().unique_guid() == partition_uuid) { - m_boot_block_device = partition; - break; - } - } - } -} - -LockRefPtr StorageManagement::boot_block_device() const -{ - return m_boot_block_device.strong_ref(); -} - -MajorNumber StorageManagement::storage_type_major_number() -{ - return 3; -} -MinorNumber StorageManagement::generate_storage_minor_number() -{ - return s_storage_device_minor_number.fetch_add(1); -} - -MinorNumber StorageManagement::generate_partition_minor_number() -{ - return s_partition_device_minor_number.fetch_add(1); -} - -u32 StorageManagement::generate_controller_id() -{ - return s_controller_id.fetch_add(1); -} - -NonnullRefPtr StorageManagement::root_filesystem() const -{ - auto boot_device_description = boot_block_device(); - if (!boot_device_description) { - dump_storage_devices_and_partitions(); - PANIC("StorageManagement: Couldn't find a suitable device to boot from"); - } - auto description_or_error = OpenFileDescription::try_create(boot_device_description.release_nonnull()); - VERIFY(!description_or_error.is_error()); - - Array mount_specific_data; - mount_specific_data.fill(0); - auto file_system = Ext2FS::try_create(description_or_error.release_value(), mount_specific_data.span()).release_value(); - - if (auto result = file_system->initialize(); result.is_error()) { - dump_storage_devices_and_partitions(); - PANIC("StorageManagement: Couldn't open root filesystem: {}", result.error()); - } - return file_system; -} - -UNMAP_AFTER_INIT void StorageManagement::initialize(bool poll) -{ - VERIFY(s_storage_device_minor_number == 0); - if (!PCI::Access::is_disabled()) { - enumerate_pci_controllers(poll); - } - -#if ARCH(AARCH64) - auto& rpi_sdhc = RPi::SDHostController::the(); - if (auto maybe_error = rpi_sdhc.initialize(); maybe_error.is_error()) { - dmesgln("Unable to initialize RaspberryPi's SD Host Controller: {}", maybe_error.error()); - } else { - m_controllers.append(rpi_sdhc); - } -#endif - - enumerate_storage_devices(); - enumerate_disk_partitions(); -} - -StorageManagement& StorageManagement::the() -{ - return *s_the; -} - -} diff --git a/Kernel/Devices/Storage/StorageManagement.h b/Kernel/Devices/Storage/StorageManagement.h deleted file mode 100644 index 665c76d5c71..00000000000 --- a/Kernel/Devices/Storage/StorageManagement.h +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright (c) 2020-2022, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace Kernel { - -class AHCIController; -class NVMeController; -class StorageManagement { - -public: - StorageManagement(); - void initialize(bool nvme_poll); - static StorageManagement& the(); - - bool determine_boot_device(StringView boot_argument); - NonnullRefPtr root_filesystem() const; - - static MajorNumber storage_type_major_number(); - static MinorNumber generate_storage_minor_number(); - - static MinorNumber generate_partition_minor_number(); - - static u32 generate_controller_id(); - - static u32 generate_relative_nvme_controller_id(Badge); - static u32 generate_relative_ahci_controller_id(Badge); - static u32 generate_relative_sd_controller_id(Badge); - - void add_device(StorageDevice&); - void remove_device(StorageDevice&); - -private: - void enumerate_pci_controllers(bool nvme_poll); - void enumerate_storage_devices(); - ErrorOr enumerate_device_partitions(StorageDevice&); - void enumerate_disk_partitions(); - - void determine_boot_device_with_partition_uuid(); - - void resolve_partition_from_boot_device_parameter(StorageDevice const& chosen_storage_device, StringView boot_device_prefix); - void determine_boot_device_with_logical_unit_number(); - void determine_block_boot_device(); - void determine_nvme_boot_device(); - void determine_sd_boot_device(); - void determine_ata_boot_device(); - void determine_hardware_relative_boot_device(StringView relative_hardware_prefix, Function filter_device_callback); - Array extract_boot_device_address_parameters(StringView device_prefix); - Optional extract_boot_device_partition_number_parameter(StringView device_prefix); - - void dump_storage_devices_and_partitions() const; - - ErrorOr> try_to_initialize_partition_table(StorageDevice&) const; - - LockRefPtr boot_block_device() const; - - StringView m_boot_argument; - LockWeakPtr m_boot_block_device; - Vector> m_controllers; - IntrusiveList<&StorageDevice::m_list_node> m_storage_devices; -}; - -} diff --git a/Kernel/Devices/Storage/USB/BulkSCSIInterface.cpp b/Kernel/Devices/Storage/USB/BulkSCSIInterface.cpp deleted file mode 100644 index c87efc339b9..00000000000 --- a/Kernel/Devices/Storage/USB/BulkSCSIInterface.cpp +++ /dev/null @@ -1,125 +0,0 @@ -/* - * Copyright (c) 2023, Leon Albrecht - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include - -namespace Kernel::USB { - -BulkSCSIInterface::BulkSCSIInterface(LUNAddress logical_unit_number_address, u32 hardware_relative_controller_id, size_t sector_size, u64 max_addressable_block, USB::Device& device, NonnullOwnPtr in_pipe, NonnullOwnPtr out_pipe) - : StorageDevice(logical_unit_number_address, hardware_relative_controller_id, sector_size, max_addressable_block) - , m_device(device) - , m_in_pipe(move(in_pipe)) - , m_out_pipe(move(out_pipe)) -{ -} - -void BulkSCSIInterface::start_request(AsyncBlockDeviceRequest& request) -{ - if (request.request_type() == AsyncBlockDeviceRequest::RequestType::Read) { - if (do_read(request.block_index(), request.block_count(), request.buffer(), request.buffer_size()).is_error()) { - request.complete(AsyncDeviceRequest::RequestResult::Failure); - } else { - request.complete(AsyncDeviceRequest::RequestResult::Success); - } - } else { - if (do_write(request.block_index(), request.block_count(), request.buffer(), request.buffer_size()).is_error()) { - request.complete(AsyncDeviceRequest::RequestResult::Failure); - } else { - request.complete(AsyncDeviceRequest::RequestResult::Success); - } - } -} - -ErrorOr BulkSCSIInterface::do_read(u32 block_index, u32 block_count, UserOrKernelBuffer& buffer, size_t) -{ - // FIXME: Error Handling and proper device reset on exit - CommandBlockWrapper command; - SCSI::Read10 read_command; - - u32 block_index_to_read = block_index; - u32 blocks_read = 0; - UserOrKernelBuffer destination_buffer = buffer; - while (blocks_read < block_count) { - read_command.logical_block_address = block_index_to_read; - - u16 transfer_length_bytes = min((block_count - blocks_read) * block_size(), AK::NumericLimits::max()); - - read_command.transfer_length = transfer_length_bytes / block_size(); - - command.transfer_length = transfer_length_bytes; - command.direction = CBWDirection::DataIn; - command.set_command(read_command); - - TRY(m_out_pipe->submit_bulk_out_transfer(sizeof(command), &command)); - - TRY(m_in_pipe->submit_bulk_in_transfer(transfer_length_bytes, destination_buffer)); - - CommandStatusWrapper status; - TRY(m_in_pipe->submit_bulk_in_transfer(sizeof(status), &status)); - - if (status.status != CSWStatus::Passed) { - // FIXME: Actually handle the error - // See usbmassbulk 5.3, 6.4 and 6.5 - dmesgln("SCSI/BBB: Read failed with code {}", to_underlying(status.status)); - return EIO; - } - - u32 bytes_transferred = transfer_length_bytes - status.data_residue; - u32 blocks_read_in_transfer = bytes_transferred / block_size(); - - blocks_read += blocks_read_in_transfer; - block_index_to_read += blocks_read_in_transfer; - } - - return {}; -} - -ErrorOr BulkSCSIInterface::do_write(u32 block_index, u32 block_count, UserOrKernelBuffer& buffer, size_t) -{ - // FIXME: Error Handling and proper device reset on exit - - CommandBlockWrapper command; - SCSI::Write10 read_command; - - u32 block_index_to_read = block_index; - u32 blocks_read = 0; - UserOrKernelBuffer destination_buffer = buffer; - while (blocks_read < block_count) { - read_command.logical_block_address = block_index_to_read; - - u16 transfer_length_bytes = min((block_count - blocks_read) * block_size(), AK::NumericLimits::max()); - - read_command.transfer_length = transfer_length_bytes / block_size(); - - command.transfer_length = transfer_length_bytes; - command.direction = CBWDirection::DataOut; - command.set_command(read_command); - - TRY(m_out_pipe->submit_bulk_out_transfer(sizeof(command), &command)); - - TRY(m_out_pipe->submit_bulk_out_transfer(transfer_length_bytes, destination_buffer)); - - CommandStatusWrapper status; - TRY(m_in_pipe->submit_bulk_in_transfer(sizeof(status), &status)); - - if (status.status != CSWStatus::Passed) { - // FIXME: Actually handle the error - // See usbmassbulk 5.3, 6.4 and 6.5 - dmesgln("SCSI/BBB: Write failed with code {}", to_underlying(status.status)); - return EIO; - } - - u32 bytes_transferred = transfer_length_bytes - status.data_residue; - u32 blocks_read_in_transfer = bytes_transferred / block_size(); - blocks_read += blocks_read_in_transfer; - block_index_to_read += blocks_read_in_transfer; - } - - return {}; -} -} diff --git a/Kernel/Devices/Storage/USB/BulkSCSIInterface.h b/Kernel/Devices/Storage/USB/BulkSCSIInterface.h deleted file mode 100644 index 78cba89f034..00000000000 --- a/Kernel/Devices/Storage/USB/BulkSCSIInterface.h +++ /dev/null @@ -1,85 +0,0 @@ -/* - * Copyright (c) 2023, Leon Albrecht - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include - -namespace Kernel::USB { - -enum class CBWDirection : u8 { - DataOut = 0, - DataIn = 1 -}; - -struct CommandBlockWrapper { - LittleEndian signature { 0x43425355 }; - LittleEndian tag { 0 }; - LittleEndian transfer_length { 0 }; - union { - u8 flags { 0 }; - struct { - u8 flag_reserved : 6; - u8 flag_obsolete : 1; - CBWDirection direction : 1; - }; - }; - u8 lun { 0 }; // only 4 bits - u8 command_length { 0 }; // 5 bits, range 1-16 - u8 command_block[16] { 0 }; - - template - requires(sizeof(T) <= 16) - void set_command(T const& command) - { - command_length = sizeof(command); - memcpy(&command_block, &command, sizeof(command)); - } -}; -static_assert(AssertSize()); - -enum class CSWStatus : u8 { - Passed = 0x00, - Failed = 0x01, - PhaseError = 0x02 -}; - -struct CommandStatusWrapper { - LittleEndian signature; - LittleEndian tag; - LittleEndian data_residue; - CSWStatus status; -}; -static_assert(AssertSize()); - -class BulkSCSIInterface : public StorageDevice { - // https://www.usb.org/sites/default/files/usbmassbulk_10.pdf -public: - BulkSCSIInterface(LUNAddress logical_unit_number_address, u32 hardware_relative_controller_id, size_t sector_size, u64 max_addressable_block, USB::Device& device, NonnullOwnPtr in_pipe, NonnullOwnPtr out_pipe); - - USB::Device const& device() const { return m_device; } - - virtual void start_request(AsyncBlockDeviceRequest&) override; - virtual CommandSet command_set() const override { return CommandSet::SCSI; } - -private: - USB::Device& m_device; - NonnullOwnPtr m_in_pipe; - NonnullOwnPtr m_out_pipe; - - ErrorOr do_read(u32 block_index, u32 block_count, UserOrKernelBuffer& buffer, size_t buffer_size); - ErrorOr do_write(u32 block_index, u32 block_count, UserOrKernelBuffer& buffer, size_t buffer_size); - - IntrusiveListNode> m_list_node; - -public: - using List = IntrusiveList<&BulkSCSIInterface::m_list_node>; -}; - -} diff --git a/Kernel/Devices/Storage/USB/Codes.h b/Kernel/Devices/Storage/USB/Codes.h deleted file mode 100644 index 3215aa44435..00000000000 --- a/Kernel/Devices/Storage/USB/Codes.h +++ /dev/null @@ -1,108 +0,0 @@ -/* - * Copyright (c) 2023, Leon Albrecht - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include - -namespace Kernel::USB::MassStorage { -// https://www.usb.org/sites/default/files/Mass_Storage_Specification_Overview_v1.4_2-19-2010.pdf -// 2 -enum class SubclassCode : u8 { - NotReported = 0x00, - RBC = 0x01, - MMC5 = 0x02, // ATAPI - Obsolete_QIC157 = 0x03, - UFI = 0x04, // Floppy - Obsolete_SFF8070i = 0x05, - SCSI_transparent = 0x06, - LSD_FS = 0x07, - IEEE1667 = 0x08, - // Reserved: 0x09 - 0xFE - VendorSpecific = 0xFF -}; - -constexpr StringView subclass_string(SubclassCode code) -{ - switch (code) { - case SubclassCode::NotReported: - return "Not Reported"sv; - case SubclassCode::RBC: - return "RBC"sv; - case SubclassCode::MMC5: - return "MMC-5 (ATAPI)"sv; - case SubclassCode::Obsolete_QIC157: - return "QIC157 (Obsolete)"sv; - case SubclassCode::UFI: - return "UFI"sv; - case SubclassCode::Obsolete_SFF8070i: - return "SFF8070i (Obsolete)"sv; - case SubclassCode::SCSI_transparent: - return "SCSI-transparent"sv; - case SubclassCode::LSD_FS: - return "LSD FS"sv; - case SubclassCode::IEEE1667: - return "IEEE1667"sv; - case SubclassCode::VendorSpecific: - return "Vendor Specific"sv; - } - - return "Reserved"sv; -} - -// 3 -enum class TransportProtocol : u8 { - CBI_completion_interrupt = 0x00, // Control/Bulk/Interrupt - CBI_no_completion_interrupt = 0x01, // Control/Bulk/Interrupt - Obsolete = 0x02, - // Reserved: 0x03 - 0x4F - BBB = 0x50, // Bulk-only - // Reserved: 0x51 - 0x61 - UAS = 0x62, - // Reserved: 0x63 - 0xFE - VendorSpecific = 0xFF -}; - -constexpr StringView transport_protocol_string(TransportProtocol protocol) -{ - switch (protocol) { - case TransportProtocol::CBI_completion_interrupt: - return "Control/Bulk/Interrupt with completion interrupt"sv; - case TransportProtocol::CBI_no_completion_interrupt: - return "Control/Bulk/Interrupt without completion interrupt"sv; - case TransportProtocol::Obsolete: - return "Obsolete"sv; - case TransportProtocol::BBB: - return "Bulk only"sv; - case TransportProtocol::UAS: - return "UAS"sv; - case TransportProtocol::VendorSpecific: - return "Vendor Specific"sv; - } - - return "Reserved"sv; -} - -// 4 -enum class RequestCodes : u8 { - ADSC = 0x00, // Accept Device Specific Command (CBI) - also alias USB-request 00h Get Status - // Reserved/alias USB-bRequest: 0x01 - 0x0D - // Reserved: 0x0E - 0XFB - GetRequest = 0xFC, - PutRequest = 0xFD, - GetMaxLun = 0xFE, // GML (BBB) - BulkOnlyMassStorageReset = 0xFF // BOMSR (BBB) -}; - -// 5 -enum class ClassSpecificDescriptorCodes : u8 { - // Undefined by Mass Storage: 0x00 - 0x23 - PipeUsageClassSpecific = 0x24 // UAS - // Undefined by Mass Storage: 0x25 - 0xFF -}; - -} diff --git a/Kernel/Devices/Storage/USB/SCSIComands.h b/Kernel/Devices/Storage/USB/SCSIComands.h deleted file mode 100644 index 68d01da0cb5..00000000000 --- a/Kernel/Devices/Storage/USB/SCSIComands.h +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright (c) 2023, Leon Albrecht - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include - -// https://www.seagate.com/files/staticfiles/support/docs/manual/Interface%20manuals/100293068j.pdf - -namespace Kernel::SCSI { -// 3.22.1 -struct ReadCapacity10 { - u8 opcode { 0x25 }; - u8 reserved1 { 0 }; - BigEndian oboslete_logical_block_address { 0 }; - u16 reserved2 { 0 }; - u8 reserved3 { 0 }; - u8 control { 0 }; -}; -static_assert(AssertSize()); -// 3.22.2 -struct ReadCapacity10Parameters { - BigEndian block_count; - BigEndian block_size; -}; -static_assert(AssertSize()); - -// 3.16 -struct Read10 { - u8 operation_code { 0x28 }; - union { - u8 settings { 0 }; - struct { - u8 obsolete : 2; - u8 rarc : 1; - u8 fua : 1; - u8 dpo : 1; - u8 rdprotect : 3; - }; - }; - BigEndian logical_block_address; - u8 group_number { 0 }; // only bottom 5 bits - BigEndian transfer_length; - u8 control { 0 }; -}; -static_assert(AssertSize()); - -// 3.60 -struct Write10 { - u8 operation_code { 0x2A }; - union { - u8 settings { 0 }; - struct { - u8 obsolete : 2; - u8 reserved : 1; - u8 fua : 1; - u8 dpo : 1; - u8 wrprotect : 3; - }; - }; - BigEndian logical_block_address; - u8 group_number { 0 }; // only bottom 5 bits - BigEndian transfer_length; - u8 control { 0 }; -}; -static_assert(AssertSize()); -} diff --git a/Kernel/Devices/Storage/VirtIO/VirtIOBlockController.cpp b/Kernel/Devices/Storage/VirtIO/VirtIOBlockController.cpp deleted file mode 100644 index 5b24f82a7a2..00000000000 --- a/Kernel/Devices/Storage/VirtIO/VirtIOBlockController.cpp +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright (c) 2023, Kirill Nikolaev - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include -#include - -namespace Kernel { - -VirtIOBlockController::VirtIOBlockController() - : StorageController(StorageManagement::generate_controller_id()) -{ -} - -bool VirtIOBlockController::is_handled(PCI::DeviceIdentifier const& device_identifier) -{ - return device_identifier.hardware_id().vendor_id == PCI::VendorID::VirtIO - && device_identifier.hardware_id().device_id == PCI::DeviceID::VirtIOBlockDevice; -} - -ErrorOr VirtIOBlockController::add_device(PCI::DeviceIdentifier const& device_identifier) -{ - // NB: Thread-unsafe, but device initialization is single threaded anyway. - auto index = m_devices.size(); - auto lun = StorageDevice::LUNAddress { controller_id(), (u32)index, 0 }; - auto cid = hardware_relative_controller_id(); - - auto transport_link = TRY(VirtIO::PCIeTransportLink::create(device_identifier)); - - auto device = TRY(adopt_nonnull_lock_ref_or_enomem(new (nothrow) VirtIOBlockDevice(move(transport_link), lun, cid))); - TRY(device->initialize_virtio_resources()); - - m_devices.append(device); - return {}; -} - -LockRefPtr VirtIOBlockController::device(u32 index) const -{ - return m_devices[index]; -} - -void VirtIOBlockController::complete_current_request(AsyncDeviceRequest::RequestResult) -{ - VERIFY_NOT_REACHED(); -} - -} diff --git a/Kernel/Devices/Storage/VirtIO/VirtIOBlockController.h b/Kernel/Devices/Storage/VirtIO/VirtIOBlockController.h deleted file mode 100644 index 09e4f6cf427..00000000000 --- a/Kernel/Devices/Storage/VirtIO/VirtIOBlockController.h +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright (c) 2023, Kirill Nikolaev - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include - -namespace Kernel { - -class VirtIOBlockDevice; - -class VirtIOBlockController : public StorageController { -public: - VirtIOBlockController(); - - static bool is_handled(PCI::DeviceIdentifier const& device_identifier); - ErrorOr add_device(PCI::DeviceIdentifier const& device_identifier); - - // ^StorageController - virtual LockRefPtr device(u32 index) const override; - virtual size_t devices_count() const override { return m_devices.size(); } - -protected: - virtual void complete_current_request(AsyncDeviceRequest::RequestResult) override; - -private: - Vector> m_devices; -}; - -} diff --git a/Kernel/Devices/Storage/VirtIO/VirtIOBlockDevice.cpp b/Kernel/Devices/Storage/VirtIO/VirtIOBlockDevice.cpp deleted file mode 100644 index dc003d5a2ee..00000000000 --- a/Kernel/Devices/Storage/VirtIO/VirtIOBlockDevice.cpp +++ /dev/null @@ -1,252 +0,0 @@ -/* - * Copyright (c) 2023, Kirill Nikolaev - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include - -namespace Kernel { - -namespace VirtIO { - -// From Virtual I/O Device (VIRTIO) Version 1.2 spec: -// https://docs.oasis-open.org/virtio/virtio/v1.2/csd01/virtio-v1.2-csd01.html#x1-2740002 - -static constexpr u64 VIRTIO_BLK_F_BARRIER = 1ull << 0; // Device supports request barriers. -static constexpr u64 VIRTIO_BLK_F_SIZE_MAX = 1ull << 1; // Maximum size of any single segment is in size_max. -static constexpr u64 VIRTIO_BLK_F_SEG_MAX = 1ull << 2; // Maximum number of segments in a request is in seg_max. -static constexpr u64 VIRTIO_BLK_F_GEOMETRY = 1ull << 4; // Disk-style geometry specified in geometry. -static constexpr u64 VIRTIO_BLK_F_RO = 1ull << 5; // Device is read-only. -static constexpr u64 VIRTIO_BLK_F_BLK_SIZE = 1ull << 6; // Block size of disk is in blk_size. -static constexpr u64 VIRTIO_BLK_F_SCSI = 1ull << 7; // Device supports scsi packet commands. -static constexpr u64 VIRTIO_BLK_F_FLUSH = 1ull << 9; // Cache flush command support. -static constexpr u64 VIRTIO_BLK_F_TOPOLOGY = 1ull << 10; // Device exports information on optimal I/O alignment. -static constexpr u64 VIRTIO_BLK_F_CONFIG_WCE = 1ull << 11; // Device can toggle its cache between writeback and writethrough modes. -static constexpr u64 VIRTIO_BLK_F_DISCARD = 1ull << 13; // Device can support discard command, maximum discard sectors size in max_discard_sectors and maximum discard segment number in max_discard_seg. -static constexpr u64 VIRTIO_BLK_F_WRITE_ZEROES = 1ull << 14; // Device can support write zeroes command, maximum write zeroes sectors size in max_write_zeroes_sectors and maximum write zeroes segment number in max_write_zeroes_seg. - -static constexpr u64 VIRTIO_BLK_T_IN = 0; -static constexpr u64 VIRTIO_BLK_T_OUT = 1; -static constexpr u64 VIRTIO_BLK_T_FLUSH = 4; -static constexpr u64 VIRTIO_BLK_T_GET_ID = 8; -static constexpr u64 VIRTIO_BLK_T_GET_LIFETIME = 10; -static constexpr u64 VIRTIO_BLK_T_DISCARD = 11; -static constexpr u64 VIRTIO_BLK_T_WRITE_ZEROES = 13; -static constexpr u64 VIRTIO_BLK_T_SECURE_ERASE = 14; - -static constexpr u64 VIRTIO_BLK_S_OK = 0; -static constexpr u64 VIRTIO_BLK_S_IOERR = 1; -static constexpr u64 VIRTIO_BLK_S_UNSUPP = 2; - -struct [[gnu::packed]] VirtIOBlkConfig { - LittleEndian capacity; - LittleEndian size_max; - LittleEndian seg_max; - struct [[gnu::packed]] VirtIOBlkGeometry { - LittleEndian cylinders; - u8 heads; - u8 sectors; - } geometry; - LittleEndian blk_size; - struct [[gnu::packed]] VirtIOBlkTopology { - // # of logical blocks per physical block (log2) - u8 physical_block_exp; - // offset of first aligned logical block - u8 alignment_offset; - // suggested minimum I/O size in blocks - LittleEndian min_io_size; - // optimal (suggested maximum) I/O size in blocks - LittleEndian opt_io_size; - } topology; - u8 writeback; - u8 unused0[3]; - LittleEndian max_discard_sectors; - LittleEndian max_discard_seg; - LittleEndian discard_sector_alignment; - LittleEndian max_write_zeroes_sectors; - LittleEndian max_write_zeroes_seg; - u8 write_zeroes_may_unmap; - u8 unused1[3]; -}; - -struct [[gnu::packed]] VirtIOBlkReqHeader { - LittleEndian type; - LittleEndian reserved; - LittleEndian sector; -}; - -struct [[gnu::packed]] VirtIOBlkReqTrailer { - u8 status; -}; - -struct [[gnu::packed]] VirtIOBlkReq { - VirtIOBlkReqHeader header; - VirtIOBlkReqTrailer trailer; -}; - -} - -using namespace VirtIO; - -static constexpr u16 REQUESTQ = 0; -static constexpr u64 SECTOR_SIZE = 512; -static constexpr u64 INFLIGHT_BUFFER_SIZE = PAGE_SIZE * 16; // 128 blocks -static constexpr u64 MAX_ADDRESSABLE_BLOCK = 1ull << 32; // FIXME: Supply effective device size. - -UNMAP_AFTER_INIT VirtIOBlockDevice::VirtIOBlockDevice( - NonnullOwnPtr transport, - StorageDevice::LUNAddress lun, - u32 hardware_relative_controller_id) - : StorageDevice(lun, hardware_relative_controller_id, SECTOR_SIZE, MAX_ADDRESSABLE_BLOCK) - , VirtIO::Device(move(transport)) -{ -} - -UNMAP_AFTER_INIT ErrorOr VirtIOBlockDevice::initialize_virtio_resources() -{ - dbgln_if(VIRTIO_DEBUG, "VirtIOBlockDevice::initialize_virtio_resources"); - TRY(VirtIO::Device::initialize_virtio_resources()); - - m_header_buf = TRY(MM.allocate_contiguous_kernel_region( - PAGE_SIZE, "VirtIOBlockDevice header_buf"sv, Memory::Region::Access::Read | Memory::Region::Access::Write)); - m_data_buf = TRY(MM.allocate_contiguous_kernel_region( - INFLIGHT_BUFFER_SIZE, "VirtIOBlockDevice data_buf"sv, Memory::Region::Access::Read | Memory::Region::Access::Write)); - - TRY(negotiate_features([&](u64) { - return 0; // We rely on the basic feature set. - })); - TRY(setup_queues(1)); // REQUESTQ - finish_init(); - return {}; -} - -ErrorOr VirtIOBlockDevice::handle_device_config_change() -{ - dbgln_if(VIRTIO_DEBUG, "VirtIOBlockDevice::handle_device_config_change"); - return {}; -} - -void VirtIOBlockDevice::start_request(AsyncBlockDeviceRequest& request) -{ - dbgln_if(VIRTIO_DEBUG, "VirtIOBlockDevice::start_request type={}", (int)request.request_type()); - - m_current_request.with([&](auto& current_request) { - VERIFY(current_request.is_null()); - current_request = request; - }); - - if (maybe_start_request(request).is_error()) { - m_current_request.with([&](auto& current_request) { - VERIFY(current_request == request); - current_request.clear(); - }); - request.complete(AsyncDeviceRequest::Failure); - } -} - -ErrorOr VirtIOBlockDevice::maybe_start_request(AsyncBlockDeviceRequest& request) -{ - auto& queue = get_queue(REQUESTQ); - SpinlockLocker queue_lock(queue.lock()); - VirtIO::QueueChain chain(queue); - - u64 data_size = block_size() * request.block_count(); - if (request.buffer_size() < data_size) { - dmesgln("VirtIOBlockDevice: not enough space in the request buffer."); - return Error::from_errno(EINVAL); - } - if (m_data_buf->size() < data_size + sizeof(VirtIOBlkReqTrailer)) { - // TODO: Supply the provider buffer instead to avoid copies. - dmesgln("VirtIOBlockDevice: not enough space in the internal buffer."); - return Error::from_errno(ENOMEM); - } - - // m_header_buf contains VirtIOBlkReqHeader and VirtIOBlkReqTrailer contingously - // When adding to chain we insert the parts of m_header_buf (as device-readable) - // and the data buffer in between (as device-writable if needed). - VirtIOBlkReq* device_req = (VirtIOBlkReq*)m_header_buf->vaddr().as_ptr(); - - device_req->header.reserved = 0; - device_req->header.sector = request.block_index(); - device_req->trailer.status = 0; - BufferType buffer_type; - if (request.request_type() == AsyncBlockDeviceRequest::Read) { - device_req->header.type = VIRTIO_BLK_T_IN; - buffer_type = BufferType::DeviceWritable; - } else if (request.request_type() == AsyncBlockDeviceRequest::Write) { - device_req->header.type = VIRTIO_BLK_T_OUT; - buffer_type = BufferType::DeviceReadable; - TRY(request.read_from_buffer(request.buffer(), m_data_buf->vaddr().as_ptr(), data_size)); - } else { - return Error::from_errno(EINVAL); - } - - chain.add_buffer_to_chain(m_header_buf->physical_page(0)->paddr(), sizeof(VirtIOBlkReqHeader), BufferType::DeviceReadable); - chain.add_buffer_to_chain(m_data_buf->physical_page(0)->paddr(), data_size, buffer_type); - chain.add_buffer_to_chain(m_header_buf->physical_page(0)->paddr().offset(sizeof(VirtIOBlkReqHeader)), sizeof(VirtIOBlkReqTrailer), BufferType::DeviceWritable); - supply_chain_and_notify(REQUESTQ, chain); - return {}; -} - -void VirtIOBlockDevice::handle_queue_update(u16 queue_index) -{ - dbgln_if(VIRTIO_DEBUG, "VirtIOBlockDevice::handle_queue_update {}", queue_index); - - if (queue_index == REQUESTQ) { - auto& queue = get_queue(REQUESTQ); - SpinlockLocker queue_lock(queue.lock()); - - size_t used; - VirtIO::QueueChain popped_chain = queue.pop_used_buffer_chain(used); - // Exactly one request is completed. - VERIFY(popped_chain.length() == 3); - VERIFY(!queue.new_data_available()); - - auto work_res = g_io_work->try_queue([this]() { - respond(); - }); - if (work_res.is_error()) { - dmesgln("VirtIOBlockDevice::handle_queue_update error starting response: {}", work_res.error()); - } - popped_chain.release_buffer_slots_to_queue(); - } else { - dmesgln("VirtIOBlockDevice::handle_queue_update unexpected update for queue {}", queue_index); - } -} - -void VirtIOBlockDevice::respond() -{ - RefPtr request; - - m_current_request.with([&](auto& current_request) { - VERIFY(current_request); - request = current_request; - }); - - u64 data_size = block_size() * request->block_count(); - VirtIOBlkReq* device_req = (VirtIOBlkReq*)(m_header_buf->vaddr().as_ptr()); - - // The order is important: - // * first we finish reading up the data buf; - // * then we unblock new requests by clearing m_current_request (thus new requests will be free to use the data buf) - // * then unblock the caller (who may immediately come with another request and need m_current_request cleared). - - if (device_req->trailer.status == VIRTIO_BLK_S_OK && request->request_type() == AsyncBlockDeviceRequest::Read) { - if (auto res = request->write_to_buffer(request->buffer(), m_data_buf->vaddr().as_ptr(), data_size); res.is_error()) { - dmesgln("VirtIOBlockDevice::respond failed to read buffer: {}", res.error()); - } - } - - m_current_request.with([&](auto& current_request) { - current_request.clear(); - }); - - request->complete(device_req->trailer.status == VIRTIO_BLK_S_OK - ? AsyncDeviceRequest::Success - : AsyncDeviceRequest::Failure); -} - -} diff --git a/Kernel/Devices/Storage/VirtIO/VirtIOBlockDevice.h b/Kernel/Devices/Storage/VirtIO/VirtIOBlockDevice.h deleted file mode 100644 index ce8599736ff..00000000000 --- a/Kernel/Devices/Storage/VirtIO/VirtIOBlockDevice.h +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright (c) 2023, Kirill Nikolaev - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include -#include - -namespace Kernel { - -class VirtIOBlockDevice : public StorageDevice - , VirtIO::Device { -public: - // ^StorageDevice - virtual CommandSet command_set() const override { return CommandSet::SCSI; } - - // ^BlockDevice - virtual void start_request(AsyncBlockDeviceRequest&) override; - -protected: - // ^VirtIO::Device - virtual ErrorOr initialize_virtio_resources() override; - virtual void handle_queue_update(u16 queue_index) override; - ErrorOr handle_device_config_change() override; - -private: - friend class VirtIOBlockController; - VirtIOBlockDevice(NonnullOwnPtr transport, - StorageDevice::LUNAddress lun, - u32 hardware_relative_controller_id); - - ErrorOr maybe_start_request(AsyncBlockDeviceRequest&); - void respond(); - -private: - OwnPtr m_header_buf; - OwnPtr m_data_buf; - - SpinlockProtected, LockRank::None> m_current_request {}; -}; - -} diff --git a/Kernel/Devices/TTY/ConsoleManagement.cpp b/Kernel/Devices/TTY/ConsoleManagement.cpp deleted file mode 100644 index 96d997805f1..00000000000 --- a/Kernel/Devices/TTY/ConsoleManagement.cpp +++ /dev/null @@ -1,96 +0,0 @@ -/* - * Copyright (c) 2021, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -namespace Kernel { - -static Singleton s_the; - -void ConsoleManagement::resolution_was_changed() -{ - for (auto& console : m_consoles) { - console->refresh_after_resolution_change(); - } -} - -bool ConsoleManagement::is_initialized() -{ - if (!s_the.is_initialized()) - return false; - if (s_the->m_consoles.is_empty()) - return false; - if (!s_the->m_active_console) - return false; - return true; -} - -ConsoleManagement& ConsoleManagement::the() -{ - return *s_the; -} - -UNMAP_AFTER_INIT ConsoleManagement::ConsoleManagement() -{ -} - -UNMAP_AFTER_INIT void ConsoleManagement::initialize() -{ - for (size_t index = 0; index < s_max_virtual_consoles; index++) { - // FIXME: Better determine the debug TTY we chose... - if (index == 1) { - VERIFY(DeviceManagement::the().is_console_device_attached()); - m_consoles.append(VirtualConsole::create_with_preset_log(index, DeviceManagement::the().console_device().logbuffer())); - continue; - } - m_consoles.append(VirtualConsole::create(index)); - } - // Note: By default the active console is the first one. - auto tty_number = kernel_command_line().switch_to_tty(); - if (tty_number > m_consoles.size()) { - PANIC("Switch to tty value is invalid: {} ", tty_number); - } - m_active_console = m_consoles[tty_number]; - SpinlockLocker lock(m_lock); - m_active_console->set_active(true); - if (!m_active_console->is_graphical()) - m_active_console->clear(); -} - -void ConsoleManagement::switch_to(unsigned index) -{ - SpinlockLocker lock(m_lock); - VERIFY(m_active_console); - VERIFY(index < m_consoles.size()); - if (m_active_console->index() == index) - return; - - bool was_graphical = m_active_console->is_graphical(); - m_active_console->set_active(false); - m_active_console = m_consoles[index]; - dbgln_if(VIRTUAL_CONSOLE_DEBUG, "Console: Switch to {}", index); - - // Before setting current console to be "active", switch between graphical mode to "textual" mode - // if needed. This will ensure we clear the screen and also that WindowServer won't print anything - // in between. - if (m_active_console->is_graphical() && !was_graphical) { - m_active_console->set_active(true); - GraphicsManagement::the().activate_graphical_mode(); - return; - } else if (!m_active_console->is_graphical() && was_graphical) { - GraphicsManagement::the().deactivate_graphical_mode(); - } - m_active_console->set_active(true); -} - -} diff --git a/Kernel/Devices/TTY/ConsoleManagement.h b/Kernel/Devices/TTY/ConsoleManagement.h deleted file mode 100644 index 3ce50a1e69b..00000000000 --- a/Kernel/Devices/TTY/ConsoleManagement.h +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright (c) 2021, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include - -namespace Kernel { - -class ConsoleManagement { - friend class VirtualConsole; - -public: - ConsoleManagement(); - - static constexpr size_t s_max_virtual_consoles = 6; - - static bool is_initialized(); - static ConsoleManagement& the(); - - void switch_to(unsigned); - void initialize(); - - void resolution_was_changed(); - - void switch_to_debug() { switch_to(1); } - - NonnullLockRefPtr first_tty() const { return m_consoles[0]; } - NonnullLockRefPtr debug_tty() const { return m_consoles[1]; } - - RecursiveSpinlock& tty_write_lock() { return m_tty_write_lock; } - -private: - Vector, s_max_virtual_consoles> m_consoles; - VirtualConsole* m_active_console { nullptr }; - Spinlock m_lock {}; - RecursiveSpinlock m_tty_write_lock {}; -}; - -}; diff --git a/Kernel/Devices/TTY/MasterPTY.cpp b/Kernel/Devices/TTY/MasterPTY.cpp deleted file mode 100644 index 0fcd9edee46..00000000000 --- a/Kernel/Devices/TTY/MasterPTY.cpp +++ /dev/null @@ -1,159 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace Kernel { - -ErrorOr> MasterPTY::try_create(unsigned int index) -{ - auto buffer = TRY(DoubleBuffer::try_create("MasterPTY: Buffer"sv)); - auto master_pty = TRY(adopt_nonnull_ref_or_enomem(new (nothrow) MasterPTY(index, move(buffer)))); - auto credentials = Process::current().credentials(); - auto slave_pty = TRY(adopt_nonnull_ref_or_enomem(new (nothrow) SlavePTY(*master_pty, credentials->uid(), credentials->gid(), index))); - master_pty->m_slave.with([&slave_pty](auto& slave) { - slave = *slave_pty; - }); - TRY(master_pty->after_inserting()); - TRY(slave_pty->after_inserting()); - return master_pty; -} - -MasterPTY::MasterPTY(unsigned index, NonnullOwnPtr buffer) - : CharacterDevice(200, index) - , m_index(index) - , m_buffer(move(buffer)) -{ - m_buffer->set_unblock_callback([this]() { - // Note that has_slave() takes and then releases the m_slave spinlock. - // Not holding the spinlock while calling evaluate_block_conditions is legal, - // as the call will trigger a check to see if waiters may be unblocked, - // and if it was called spuriously (i.e. because the slave disappeared between - // calling the unblock callback and the actual block condition evaluation), - // the waiters will simply not unblock. - if (has_slave()) - evaluate_block_conditions(); - }); -} - -MasterPTY::~MasterPTY() -{ - dbgln_if(MASTERPTY_DEBUG, "~MasterPTY({})", m_index); - PTYMultiplexer::the().notify_master_destroyed({}, m_index); -} - -ErrorOr MasterPTY::read(OpenFileDescription&, u64, UserOrKernelBuffer& buffer, size_t size) -{ - // Note that has_slave() takes and then releases the m_slave spinlock. - // Not holding the spinlock while calling m_buffer->read is legal, because slave starts non-null, - // and can only change its state to null once (and never back to non-null) in notify_slave_closed. - // So if the check happens, and it returns non-null, and then it turns null concurrently, - // and we call m_buffer->read, the behaviour from the perspective of the read caller is - // the same as if the slave turned null after we called m_buffer->read. On the other hand, - // if the check happens and returns null, then it can't possibly change to non-null after. - if (!has_slave() && m_buffer->is_empty()) - return 0; - return m_buffer->read(buffer, size); -} - -ErrorOr MasterPTY::write(OpenFileDescription&, u64, UserOrKernelBuffer const& buffer, size_t size) -{ - return m_slave.with([&](auto& slave) -> ErrorOr { - if (!slave) - return EIO; - slave->on_master_write(buffer, size); - return size; - }); -} - -bool MasterPTY::can_read(OpenFileDescription const&, u64) const -{ - return m_slave.with([this](auto& slave) -> bool { - if (!slave) - return true; - return !m_buffer->is_empty(); - }); -} - -bool MasterPTY::can_write(OpenFileDescription const&, u64) const -{ - return true; -} - -void MasterPTY::notify_slave_closed(Badge) -{ - m_slave.with([this](auto& slave) { - dbgln_if(MASTERPTY_DEBUG, "MasterPTY({}): slave closed, my retains: {}, slave retains: {}", m_index, ref_count(), slave->ref_count()); - // +1 ref for my MasterPTY::m_slave - // +1 ref for OpenFileDescription::m_device - if (slave->ref_count() == 2) - slave = nullptr; - }); -} - -ErrorOr MasterPTY::on_slave_write(UserOrKernelBuffer const& data, size_t size) -{ - if (m_closed) - return EIO; - return m_buffer->write(data, size); -} - -bool MasterPTY::can_write_from_slave() const -{ - if (m_closed) - return true; - return m_buffer->space_for_writing() >= 2; -} - -ErrorOr MasterPTY::close() -{ - InterruptDisabler disabler; - // After the closing OpenFileDescription dies, slave is the only thing keeping me alive. - // From this point, let's consider ourselves closed. - m_closed = true; - - m_slave.with([](auto& slave) { - if (slave) - slave->hang_up(); - }); - - return {}; -} - -ErrorOr MasterPTY::ioctl(OpenFileDescription& description, unsigned request, Userspace arg) -{ - TRY(Process::current().require_promise(Pledge::tty)); - return m_slave.with([&](auto& slave) -> ErrorOr { - if (!slave) - return EIO; - switch (request) { - case TIOCGPTN: { - int master_pty_index = index(); - return copy_to_user(static_ptr_cast(arg), &master_pty_index); - } - case TIOCSWINSZ: - case TIOCGPGRP: - return slave->ioctl(description, request, arg); - default: - return EINVAL; - } - }); -} - -ErrorOr> MasterPTY::pseudo_path(OpenFileDescription const&) const -{ - return KString::formatted("ptm:{}", m_index); -} - -} diff --git a/Kernel/Devices/TTY/MasterPTY.h b/Kernel/Devices/TTY/MasterPTY.h deleted file mode 100644 index 803af8abd6a..00000000000 --- a/Kernel/Devices/TTY/MasterPTY.h +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include - -namespace Kernel { - -class SlavePTY; - -class MasterPTY final : public CharacterDevice { -public: - static ErrorOr> try_create(unsigned index); - virtual ~MasterPTY() override; - - unsigned index() const { return m_index; } - ErrorOr on_slave_write(UserOrKernelBuffer const&, size_t); - bool can_write_from_slave() const; - void notify_slave_closed(Badge); - bool is_closed() const { return m_closed; } - - virtual ErrorOr> pseudo_path(OpenFileDescription const&) const override; - -private: - explicit MasterPTY(unsigned index, NonnullOwnPtr buffer); - - // ^Device - virtual bool is_openable_by_jailed_processes() const override { return true; } - - // ^CharacterDevice - virtual ErrorOr read(OpenFileDescription&, u64, UserOrKernelBuffer&, size_t) override; - virtual ErrorOr write(OpenFileDescription&, u64, UserOrKernelBuffer const&, size_t) override; - virtual bool can_read(OpenFileDescription const&, u64) const override; - virtual bool can_write(OpenFileDescription const&, u64) const override; - virtual ErrorOr close() override; - virtual bool is_master_pty() const override { return true; } - virtual ErrorOr ioctl(OpenFileDescription&, unsigned request, Userspace arg) override; - virtual StringView class_name() const override { return "MasterPTY"sv; } - - bool has_slave() const - { - return m_slave.with([](auto& slave) -> bool { return slave; }); - } - - SpinlockProtected, LockRank::None> m_slave; - unsigned m_index; - bool m_closed { false }; - NonnullOwnPtr m_buffer; -}; - -} diff --git a/Kernel/Devices/TTY/PTYMultiplexer.cpp b/Kernel/Devices/TTY/PTYMultiplexer.cpp deleted file mode 100644 index 6aa2fad22cf..00000000000 --- a/Kernel/Devices/TTY/PTYMultiplexer.cpp +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include -#include -#include - -namespace Kernel { - -static Singleton s_the; - -PTYMultiplexer& PTYMultiplexer::the() -{ - return *s_the; -} - -UNMAP_AFTER_INIT PTYMultiplexer::PTYMultiplexer() - : CharacterDevice(5, 2) -{ - m_freelist.with([&](auto& freelist) { - freelist.ensure_capacity(max_pty_pairs); - for (int i = max_pty_pairs; i > 0; --i) - freelist.unchecked_append(i - 1); - }); -} - -UNMAP_AFTER_INIT PTYMultiplexer::~PTYMultiplexer() = default; - -UNMAP_AFTER_INIT void PTYMultiplexer::initialize() -{ - MUST(the().after_inserting()); -} - -ErrorOr> PTYMultiplexer::open(int options) -{ - return m_freelist.with([&](auto& freelist) -> ErrorOr> { - if (freelist.is_empty()) - return EBUSY; - - auto master_index = freelist.take_last(); - auto master = TRY(MasterPTY::try_create(master_index)); - dbgln_if(PTMX_DEBUG, "PTYMultiplexer::open: Vending master {}", master->index()); - auto description = TRY(OpenFileDescription::try_create(*master)); - description->set_rw_mode(options); - description->set_file_flags(options); - return description; - }); -} - -void PTYMultiplexer::notify_master_destroyed(Badge, unsigned index) -{ - m_freelist.with([&](auto& freelist) { - freelist.append(index); - dbgln_if(PTMX_DEBUG, "PTYMultiplexer: {} added to freelist", index); - }); -} - -} diff --git a/Kernel/Devices/TTY/PTYMultiplexer.h b/Kernel/Devices/TTY/PTYMultiplexer.h deleted file mode 100644 index 441a7169df2..00000000000 --- a/Kernel/Devices/TTY/PTYMultiplexer.h +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright (c) 2018-2021, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include - -namespace Kernel { - -class MasterPTY; - -class PTYMultiplexer final : public CharacterDevice { -public: - PTYMultiplexer(); - virtual ~PTYMultiplexer() override; - - static void initialize(); - static PTYMultiplexer& the(); - - // ^CharacterDevice - virtual ErrorOr> open(int options) override; - virtual ErrorOr read(OpenFileDescription&, u64, UserOrKernelBuffer&, size_t) override { return 0; } - virtual ErrorOr write(OpenFileDescription&, u64, UserOrKernelBuffer const&, size_t) override { return 0; } - virtual bool can_read(OpenFileDescription const&, u64) const override { return true; } - virtual bool can_write(OpenFileDescription const&, u64) const override { return true; } - - void notify_master_destroyed(Badge, unsigned index); - -private: - // ^CharacterDevice - virtual StringView class_name() const override { return "PTYMultiplexer"sv; } - - static constexpr size_t max_pty_pairs = 64; - SpinlockProtected, LockRank::None> m_freelist {}; -}; - -} diff --git a/Kernel/Devices/TTY/SlavePTY.cpp b/Kernel/Devices/TTY/SlavePTY.cpp deleted file mode 100644 index ad8ba4861f7..00000000000 --- a/Kernel/Devices/TTY/SlavePTY.cpp +++ /dev/null @@ -1,114 +0,0 @@ -/* - * Copyright (c) 2018-2021, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include - -namespace Kernel { - -static Singleton> s_all_instances; - -SpinlockProtected& SlavePTY::all_instances() -{ - return s_all_instances; -} - -bool SlavePTY::unref() const -{ - bool did_hit_zero = SlavePTY::all_instances().with([&](auto&) { - if (deref_base()) - return false; - m_list_node.remove(); - const_cast(*this).revoke_weak_ptrs(); - return true; - }); - if (did_hit_zero) { - const_cast(*this).will_be_destroyed(); - delete this; - } - return did_hit_zero; -} - -SlavePTY::SlavePTY(NonnullRefPtr master, UserID uid, GroupID gid, unsigned index) - : TTY(201, index) - , m_master(move(master)) - , m_index(index) - , m_uid(uid) - , m_gid(gid) -{ - set_size(80, 25); - SlavePTY::all_instances().with([&](auto& list) { list.append(*this); }); -} - -SlavePTY::~SlavePTY() -{ - dbgln_if(SLAVEPTY_DEBUG, "~SlavePTY({})", m_index); -} - -ErrorOr> SlavePTY::pseudo_name() const -{ - return KString::formatted("pts:{}", m_index); -} - -void SlavePTY::echo(u8 ch) -{ - if (should_echo_input()) { - auto buffer = UserOrKernelBuffer::for_kernel_buffer(&ch); - [[maybe_unused]] auto result = m_master->on_slave_write(buffer, 1); - } -} - -void SlavePTY::on_master_write(UserOrKernelBuffer const& buffer, size_t size) -{ - auto result = buffer.read_buffered<128>(size, [&](ReadonlyBytes data) { - for (auto const& byte : data) - emit(byte, false); - return data.size(); - }); - if (!result.is_error()) - evaluate_block_conditions(); -} - -ErrorOr SlavePTY::on_tty_write(UserOrKernelBuffer const& data, size_t size) -{ - m_time_of_last_write = kgettimeofday(); - return m_master->on_slave_write(data, size); -} - -bool SlavePTY::can_write(OpenFileDescription const&, u64) const -{ - return m_master->can_write_from_slave(); -} - -bool SlavePTY::can_read(OpenFileDescription const& description, u64 offset) const -{ - if (m_master->is_closed()) - return true; - return TTY::can_read(description, offset); -} - -ErrorOr SlavePTY::read(OpenFileDescription& description, u64 offset, UserOrKernelBuffer& buffer, size_t size) -{ - if (m_master->is_closed()) - return 0; - return TTY::read(description, offset, buffer, size); -} - -ErrorOr SlavePTY::close() -{ - m_master->notify_slave_closed({}); - return {}; -} - -FileBlockerSet& SlavePTY::blocker_set() -{ - return m_master->blocker_set(); -} - -} diff --git a/Kernel/Devices/TTY/SlavePTY.h b/Kernel/Devices/TTY/SlavePTY.h deleted file mode 100644 index 092b8d9649b..00000000000 --- a/Kernel/Devices/TTY/SlavePTY.h +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright (c) 2018-2021, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include - -namespace Kernel { - -class MasterPTY; - -class SlavePTY final : public TTY { -public: - virtual bool unref() const override; - virtual ~SlavePTY() override; - - void on_master_write(UserOrKernelBuffer const&, size_t); - unsigned index() const { return m_index; } - - UnixDateTime time_of_last_write() const { return m_time_of_last_write; } - - virtual FileBlockerSet& blocker_set() override; - - UserID uid() const { return m_uid; } - GroupID gid() const { return m_gid; } - -private: - // ^Device - virtual bool is_openable_by_jailed_processes() const override { return true; } - - // ^TTY - virtual ErrorOr> pseudo_name() const override; - virtual ErrorOr on_tty_write(UserOrKernelBuffer const&, size_t) override; - virtual void echo(u8) override; - - // ^CharacterDevice - virtual bool can_read(OpenFileDescription const&, u64) const override; - virtual ErrorOr read(OpenFileDescription&, u64, UserOrKernelBuffer&, size_t) override; - virtual bool can_write(OpenFileDescription const&, u64) const override; - virtual StringView class_name() const override { return "SlavePTY"sv; } - virtual ErrorOr close() override; - - friend class MasterPTY; - SlavePTY(NonnullRefPtr, UserID, GroupID, unsigned index); - - NonnullRefPtr const m_master; - UnixDateTime m_time_of_last_write {}; - unsigned m_index { 0 }; - - UserID const m_uid { 0 }; - GroupID const m_gid { 0 }; - - mutable IntrusiveListNode m_list_node; - -public: - using List = IntrusiveList<&SlavePTY::m_list_node>; - static SpinlockProtected& all_instances(); -}; - -} diff --git a/Kernel/Devices/TTY/TTY.cpp b/Kernel/Devices/TTY/TTY.cpp deleted file mode 100644 index f1e7361e20e..00000000000 --- a/Kernel/Devices/TTY/TTY.cpp +++ /dev/null @@ -1,584 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * Copyright (c) 2022, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace Kernel { - -TTY::TTY(MajorNumber major, MinorNumber minor) - : CharacterDevice(major, minor) -{ - set_default_termios(); -} - -TTY::~TTY() = default; - -void TTY::set_default_termios() -{ - memset(&m_termios, 0, sizeof(m_termios)); - m_termios.c_iflag = TTYDEF_IFLAG; - m_termios.c_oflag = TTYDEF_OFLAG; - m_termios.c_cflag = TTYDEF_CFLAG; - m_termios.c_lflag = TTYDEF_LFLAG; - m_termios.c_ispeed = TTYDEF_SPEED; - m_termios.c_ospeed = TTYDEF_SPEED; - memcpy(m_termios.c_cc, ttydefchars, sizeof(ttydefchars)); -} - -ErrorOr TTY::read(OpenFileDescription&, u64, UserOrKernelBuffer& buffer, size_t size) -{ - if (Process::current().pgid() != pgid()) { - // FIXME: Should we propagate this error path somehow? - [[maybe_unused]] auto rc = Process::current().send_signal(SIGTTIN, nullptr); - return EINTR; - } - if (m_input_buffer.size() < static_cast(size)) - size = m_input_buffer.size(); - - bool need_evaluate_block_conditions = false; - auto result = buffer.write_buffered<512>(size, [&](Bytes data) { - size_t bytes_written = 0; - for (; bytes_written < data.size(); ++bytes_written) { - auto bit_index = m_input_buffer.head_index(); - bool is_special_character = m_special_character_bitmask[bit_index / 8] & (1 << (bit_index % 8)); - if (in_canonical_mode() && is_special_character) { - u8 ch = m_input_buffer.dequeue(); - if (ch == '\0') { - // EOF - m_available_lines--; - need_evaluate_block_conditions = true; - break; - } else { - // '\n' or EOL - data[bytes_written++] = ch; - m_available_lines--; - break; - } - } - data[bytes_written] = m_input_buffer.dequeue(); - } - return bytes_written; - }); - if ((!result.is_error() && result.value() > 0) || need_evaluate_block_conditions) - evaluate_block_conditions(); - return result; -} - -ErrorOr TTY::write(OpenFileDescription&, u64, UserOrKernelBuffer const& buffer, size_t size) -{ - if (m_termios.c_lflag & TOSTOP && Process::current().pgid() != pgid()) { - [[maybe_unused]] auto rc = Process::current().send_signal(SIGTTOU, nullptr); - return EINTR; - } - - constexpr size_t num_chars = 256; - return buffer.read_buffered(size, [&](ReadonlyBytes bytes) -> ErrorOr { - u8 modified_data[num_chars * 2]; - size_t modified_data_size = 0; - for (auto const& byte : bytes) { - process_output(byte, [&modified_data, &modified_data_size](u8 out_ch) { - modified_data[modified_data_size++] = out_ch; - }); - } - auto bytes_written_or_error = on_tty_write(UserOrKernelBuffer::for_kernel_buffer(modified_data), modified_data_size); - if (bytes_written_or_error.is_error() || !(m_termios.c_oflag & OPOST) || !(m_termios.c_oflag & ONLCR)) - return bytes_written_or_error; - auto bytes_written = bytes_written_or_error.value(); - if (bytes_written == modified_data_size) - return bytes.size(); - - // Degenerate case where we converted some newlines and encountered a partial write - - // Calculate where in the input buffer the last character would have been - size_t pos_data = 0; - for (size_t pos_modified_data = 0; pos_modified_data < bytes_written; ++pos_data) { - if (bytes[pos_data] == '\n') - pos_modified_data += 2; - else - pos_modified_data += 1; - - // Handle case where the '\r' got written but not the '\n' - // FIXME: Our strategy is to retry writing both. We should really be queuing a write for the corresponding '\n' - if (pos_modified_data > bytes_written) - --pos_data; - } - return pos_data; - }); -} - -void TTY::echo_with_processing(u8 ch) -{ - process_output(ch, [this](u8 out_ch) { echo(out_ch); }); -} - -template -void TTY::process_output(u8 ch, Functor put_char) -{ - if (m_termios.c_oflag & OPOST) { - if (ch == '\n' && (m_termios.c_oflag & ONLCR)) - put_char('\r'); - put_char(ch); - } else { - put_char(ch); - } -} - -bool TTY::can_read(OpenFileDescription const&, u64) const -{ - if (in_canonical_mode()) { - return m_available_lines > 0; - } - return !m_input_buffer.is_empty(); -} - -bool TTY::can_write(OpenFileDescription const&, u64) const -{ - return true; -} - -bool TTY::is_eol(u8 ch) const -{ - return ch == m_termios.c_cc[VEOL]; -} - -bool TTY::is_eof(u8 ch) const -{ - return ch == m_termios.c_cc[VEOF]; -} - -bool TTY::is_kill(u8 ch) const -{ - return ch == m_termios.c_cc[VKILL]; -} - -bool TTY::is_erase(u8 ch) const -{ - return ch == m_termios.c_cc[VERASE]; -} - -bool TTY::is_werase(u8 ch) const -{ - return ch == m_termios.c_cc[VWERASE]; -} - -void TTY::emit(u8 ch, bool do_evaluate_block_conditions) -{ - if (m_termios.c_iflag & ISTRIP) - ch &= 0x7F; - - if (should_generate_signals()) { - if (ch == m_termios.c_cc[VINFO]) { - generate_signal(SIGINFO); - return; - } - if (ch == m_termios.c_cc[VINTR]) { - generate_signal(SIGINT); - return; - } - if (ch == m_termios.c_cc[VQUIT]) { - generate_signal(SIGQUIT); - return; - } - if (ch == m_termios.c_cc[VSUSP]) { - generate_signal(SIGTSTP); - if (auto original_process_parent = m_original_process_parent.strong_ref()) { - [[maybe_unused]] auto rc = original_process_parent->send_signal(SIGCHLD, nullptr); - } - // TODO: Else send it to the session leader maybe? - return; - } - } - - ScopeGuard guard([&]() { - if (do_evaluate_block_conditions) - evaluate_block_conditions(); - }); - - if (ch == '\r' && (m_termios.c_iflag & ICRNL)) - ch = '\n'; - else if (ch == '\n' && (m_termios.c_iflag & INLCR)) - ch = '\r'; - - auto current_char_head_index = (m_input_buffer.head_index() + m_input_buffer.size()) % TTY_BUFFER_SIZE; - m_special_character_bitmask[current_char_head_index / 8] &= ~(1u << (current_char_head_index % 8)); - - auto set_special_bit = [&] { - m_special_character_bitmask[current_char_head_index / 8] |= (1u << (current_char_head_index % 8)); - }; - - if (in_canonical_mode()) { - if (is_eof(ch)) { - // Since EOF might change between when the data came in and when it is read, - // we use '\0' along with the bitmask to signal EOF. Any non-zero byte with - // the special bit set signals an end-of-line. - set_special_bit(); - m_available_lines++; - m_input_buffer.enqueue('\0'); - return; - } - if (is_kill(ch) && m_termios.c_lflag & ECHOK) { - kill_line(); - return; - } - if (is_erase(ch) && m_termios.c_lflag & ECHOE) { - do_backspace(); - return; - } - if (is_werase(ch)) { - erase_word(); - return; - } - - if (ch == '\n') { - if (m_termios.c_lflag & ECHO || m_termios.c_lflag & ECHONL) - echo_with_processing('\n'); - - set_special_bit(); - m_input_buffer.enqueue('\n'); - m_available_lines++; - return; - } - - if (is_eol(ch)) { - set_special_bit(); - m_available_lines++; - } - } - - m_input_buffer.enqueue(ch); - if (m_termios.c_lflag & ECHO) - echo_with_processing(ch); -} - -bool TTY::can_do_backspace() const -{ - // can't do back space if we're empty. Plus, we don't want to - // remove any lines "committed" by newlines or ^D. - if (!m_input_buffer.is_empty() && !is_eol(m_input_buffer.last()) && m_input_buffer.last() != '\0') { - return true; - } - return false; -} - -static size_t length_with_tabs(CircularDeque const& line) -{ - size_t length = 0; - for (auto& ch : line) { - length += (ch == '\t') ? 8 - (length % 8) : 1; - } - return length; -} - -void TTY::do_backspace() -{ - if (can_do_backspace()) { - auto ch = m_input_buffer.dequeue_end(); - size_t to_delete = 1; - - if (ch == '\t') { - auto length = length_with_tabs(m_input_buffer); - to_delete = 8 - (length % 8); - } - - for (size_t i = 0; i < to_delete; ++i) { - // We deliberately don't process the output here. - echo('\b'); - echo(' '); - echo('\b'); - } - - evaluate_block_conditions(); - } -} - -// TODO: Currently, both erase_word() and kill_line work by sending -// a lot of VERASE characters; this is done because Terminal.cpp -// doesn't currently support VWERASE and VKILL. When these are -// implemented we could just send a VKILL or VWERASE. - -void TTY::erase_word() -{ - // Note: if we have leading whitespace before the word - // we want to delete we have to also delete that. - bool first_char = false; - bool did_dequeue = false; - while (can_do_backspace()) { - u8 ch = m_input_buffer.last(); - if (ch == ' ' && first_char) - break; - if (ch != ' ') - first_char = true; - m_input_buffer.dequeue_end(); - did_dequeue = true; - erase_character(); - } - if (did_dequeue) - evaluate_block_conditions(); -} - -void TTY::kill_line() -{ - bool did_dequeue = false; - while (can_do_backspace()) { - m_input_buffer.dequeue_end(); - did_dequeue = true; - erase_character(); - } - if (did_dequeue) - evaluate_block_conditions(); -} - -void TTY::erase_character() -{ - // We deliberately don't process the output here. - echo(m_termios.c_cc[VERASE]); - echo(' '); - echo(m_termios.c_cc[VERASE]); -} - -void TTY::generate_signal(int signal) -{ - if (!pgid()) - return; - if (should_flush_on_signal()) - flush_input(); - dbgln_if(TTY_DEBUG, "Send signal {} to everyone in pgrp {}", signal, pgid().value()); - InterruptDisabler disabler; // FIXME: Iterate over a set of process handles instead? - MUST(Process::current().for_each_in_pgrp_in_same_jail(pgid(), [&](auto& process) -> ErrorOr { - dbgln_if(TTY_DEBUG, "Send signal {} to {}", signal, process); - // FIXME: Should this error be propagated somehow? - [[maybe_unused]] auto rc = process.send_signal(signal, nullptr); - return {}; - })); -} - -void TTY::flush_input() -{ - m_available_lines = 0; - m_input_buffer.clear(); - evaluate_block_conditions(); -} - -ErrorOr TTY::set_termios(OpenFileDescription& description, termios const& t) -{ - ErrorOr rc; - m_termios = t; - - dbgln_if(TTY_DEBUG, "set_termios: ECHO={}, ISIG={}, ICANON={}, ECHOE={}, ECHOK={}, ECHONL={}, ISTRIP={}, ICRNL={}, INLCR={}, IGNCR={}, OPOST={}, ONLCR={}", - should_echo_input(), - should_generate_signals(), - in_canonical_mode(), - ((m_termios.c_lflag & ECHOE) != 0), - ((m_termios.c_lflag & ECHOK) != 0), - ((m_termios.c_lflag & ECHONL) != 0), - ((m_termios.c_iflag & ISTRIP) != 0), - ((m_termios.c_iflag & ICRNL) != 0), - ((m_termios.c_iflag & INLCR) != 0), - ((m_termios.c_iflag & IGNCR) != 0), - ((m_termios.c_oflag & OPOST) != 0), - ((m_termios.c_oflag & ONLCR) != 0)); - - struct FlagDescription { - tcflag_t value; - StringView name; - }; - - constexpr FlagDescription unimplemented_iflags[] = { - { IGNBRK, "IGNBRK"sv }, - { BRKINT, "BRKINT"sv }, - { IGNPAR, "IGNPAR"sv }, - { PARMRK, "PARMRK"sv }, - { INPCK, "INPCK"sv }, - { IGNCR, "IGNCR"sv }, - { IUCLC, "IUCLC"sv }, - { IXON, "IXON"sv }, - { IXANY, "IXANY"sv }, - { IXOFF, "IXOFF"sv }, - { IMAXBEL, "IMAXBEL"sv }, - { IUTF8, "IUTF8"sv } - }; - for (auto flag : unimplemented_iflags) { - if (m_termios.c_iflag & flag.value) { - dbgln("FIXME: iflag {} unimplemented", flag.name); - rc = ENOTIMPL; - } - } - - constexpr FlagDescription unimplemented_oflags[] = { - { OLCUC, "OLCUC"sv }, - { ONOCR, "ONOCR"sv }, - { ONLRET, "ONLRET"sv }, - { OFILL, "OFILL"sv }, - { OFDEL, "OFDEL"sv } - }; - for (auto flag : unimplemented_oflags) { - if (m_termios.c_oflag & flag.value) { - dbgln("FIXME: oflag {} unimplemented", flag.name); - rc = ENOTIMPL; - } - } - - if ((m_termios.c_cflag & CSIZE) != CS8) { - dbgln("FIXME: Character sizes other than 8 bits are not supported"); - rc = ENOTIMPL; - } - - constexpr FlagDescription unimplemented_cflags[] = { - { CSTOPB, "CSTOPB"sv }, - { CREAD, "CREAD"sv }, - { PARENB, "PARENB"sv }, - { PARODD, "PARODD"sv }, - { HUPCL, "HUPCL"sv }, - { CLOCAL, "CLOCAL"sv } - }; - for (auto flag : unimplemented_cflags) { - if (m_termios.c_cflag & flag.value) { - dbgln("FIXME: cflag {} unimplemented", flag.name); - rc = ENOTIMPL; - } - } - - constexpr FlagDescription unimplemented_lflags[] = { - { TOSTOP, "TOSTOP"sv }, - { IEXTEN, "IEXTEN"sv } - }; - for (auto flag : unimplemented_lflags) { - if (m_termios.c_lflag & flag.value) { - dbgln("FIXME: lflag {} unimplemented", flag.name); - rc = ENOTIMPL; - } - } - - // FIXME: decouple VMIN/VTIME support from the description's blocking status - // FIXME: support VMIN > 1 where we should block until VMIN characters are available - // FIXME: implement support for VTIME as an interbyte timeout - description.set_blocking(in_canonical_mode() || m_termios.c_cc[VMIN] > 0); - - return rc; -} - -ErrorOr TTY::ioctl(OpenFileDescription& description, unsigned request, Userspace arg) -{ - auto& current_process = Process::current(); - TRY(current_process.require_promise(Pledge::tty)); -#if 0 - // FIXME: When should we block things? - // How do we make this work together with MasterPTY forwarding to us? - if (current_process.tty() && current_process.tty() != this) { - return ENOTTY; - } -#endif - switch (request) { - case TIOCGPGRP: { - auto user_pgid = static_ptr_cast(arg); - auto pgid = this->pgid().value(); - return copy_to_user(user_pgid, &pgid); - } - case TIOCSPGRP: { - ProcessGroupID pgid = static_cast(arg.ptr()); - if (pgid <= 0) - return EINVAL; - InterruptDisabler disabler; - auto process_group = ProcessGroup::from_pgid(pgid); - // Disallow setting a nonexistent PGID. - if (!process_group) - return EINVAL; - - auto process = Process::from_pid_in_same_jail(ProcessID(pgid.value())); - SessionID new_sid = process ? process->sid() : Process::get_sid_from_pgid(pgid); - if (!new_sid || new_sid != current_process.sid()) - return EPERM; - if (process && pgid != process->pgid()) - return EPERM; - m_pg = TRY(process_group->try_make_weak_ptr()); - - if (process) { - if (auto parent = Process::from_pid_ignoring_jails(process->ppid())) { - m_original_process_parent = *parent; - return {}; - } - } - - m_original_process_parent = nullptr; - return {}; - } - case TCGETS: { - auto user_termios = static_ptr_cast(arg); - return copy_to_user(user_termios, &m_termios); - } - case TCSETS: - case TCSETSF: - case TCSETSW: { - auto user_termios = static_ptr_cast(arg); - auto termios = TRY(copy_typed_from_user(user_termios)); - auto rc = set_termios(description, termios); - if (request == TCSETSF) - flush_input(); - return rc; - } - case TCFLSH: { - // Serenity's TTY implementation does not use an output buffer, so ignore TCOFLUSH. - auto operation = static_cast(arg.ptr()); - if (operation == TCIFLUSH || operation == TCIOFLUSH) { - flush_input(); - } else if (operation != TCOFLUSH) { - return EINVAL; - } - return {}; - } - case TIOCGWINSZ: { - auto user_winsize = static_ptr_cast(arg); - winsize ws {}; - ws.ws_row = m_rows; - ws.ws_col = m_columns; - ws.ws_xpixel = 0; - ws.ws_ypixel = 0; - return copy_to_user(user_winsize, &ws); - } - case TIOCSWINSZ: { - auto user_winsize = static_ptr_cast(arg); - auto ws = TRY(copy_typed_from_user(user_winsize)); - if (ws.ws_col == m_columns && ws.ws_row == m_rows) - return {}; - m_rows = ws.ws_row; - m_columns = ws.ws_col; - generate_signal(SIGWINCH); - return {}; - } - case TIOCSCTTY: - current_process.set_tty(this); - return {}; - case TIOCSTI: - return EIO; - case TIOCNOTTY: - current_process.set_tty(nullptr); - return {}; - } - return EINVAL; -} - -void TTY::set_size(unsigned short columns, unsigned short rows) -{ - m_rows = rows; - m_columns = columns; -} - -void TTY::hang_up() -{ - generate_signal(SIGHUP); -} -} diff --git a/Kernel/Devices/TTY/TTY.h b/Kernel/Devices/TTY/TTY.h deleted file mode 100644 index 57c54d77d49..00000000000 --- a/Kernel/Devices/TTY/TTY.h +++ /dev/null @@ -1,96 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include -#include - -#define TTY_BUFFER_SIZE 1024 - -namespace Kernel { - -class TTY : public CharacterDevice { -public: - virtual ~TTY() override; - - virtual ErrorOr read(OpenFileDescription&, u64, UserOrKernelBuffer&, size_t) override; - virtual ErrorOr write(OpenFileDescription&, u64, UserOrKernelBuffer const&, size_t) override; - virtual bool can_read(OpenFileDescription const&, u64) const override; - virtual bool can_write(OpenFileDescription const&, u64) const override; - virtual ErrorOr ioctl(OpenFileDescription&, unsigned request, Userspace arg) override; - - unsigned short rows() const { return m_rows; } - unsigned short columns() const { return m_columns; } - - ProcessGroupID pgid() const - { - if (auto pg = m_pg.strong_ref()) - return pg->pgid(); - return 0; - } - - bool should_generate_signals() const { return (m_termios.c_lflag & ISIG) == ISIG; } - bool should_flush_on_signal() const { return (m_termios.c_lflag & NOFLSH) != NOFLSH; } - bool should_echo_input() const { return (m_termios.c_lflag & ECHO) == ECHO; } - bool in_canonical_mode() const { return (m_termios.c_lflag & ICANON) == ICANON; } - - void set_default_termios(); - void hang_up(); - - virtual ErrorOr> pseudo_name() const = 0; - -protected: - virtual ErrorOr on_tty_write(UserOrKernelBuffer const&, size_t) = 0; - void set_size(unsigned short columns, unsigned short rows); - - TTY(MajorNumber major, MinorNumber minor); - void emit(u8, bool do_evaluate_block_conditions = false); - void echo_with_processing(u8); - - bool can_do_backspace() const; - void do_backspace(); - void erase_word(); - void erase_character(); - void kill_line(); - void flush_input(); - - bool is_eol(u8) const; - bool is_eof(u8) const; - bool is_kill(u8) const; - bool is_erase(u8) const; - bool is_werase(u8) const; - - void generate_signal(int signal); - - int m_available_lines { 0 }; - -private: - // ^CharacterDevice - virtual bool is_tty() const final override { return true; } - - virtual void echo(u8) = 0; - ErrorOr set_termios(OpenFileDescription&, termios const&); - - template - void process_output(u8, Functor put_char); - - CircularDeque m_input_buffer; - // FIXME: use something like AK::Bitmap but which takes a size template parameter - u8 m_special_character_bitmask[TTY_BUFFER_SIZE / 8]; - - LockWeakPtr m_original_process_parent; - LockWeakPtr m_pg; - termios m_termios; - unsigned short m_rows { 0 }; - unsigned short m_columns { 0 }; -}; - -} diff --git a/Kernel/Devices/TTY/VirtualConsole.cpp b/Kernel/Devices/TTY/VirtualConsole.cpp deleted file mode 100644 index 7e746b2bc10..00000000000 --- a/Kernel/Devices/TTY/VirtualConsole.cpp +++ /dev/null @@ -1,512 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * Copyright (c) 2020, Sergey Bugaev - * Copyright (c) 2021, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#if ARCH(X86_64) -# include -#endif -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace Kernel { - -ConsoleImpl::ConsoleImpl(VirtualConsole& client) - : Terminal(client) -{ -} - -void ConsoleImpl::invalidate_cursor() -{ -} -void ConsoleImpl::clear() -{ - m_client.clear(); -} -void ConsoleImpl::clear_history() -{ -} - -void ConsoleImpl::set_size(u16 determined_columns, u16 determined_rows) -{ - VERIFY(determined_columns); - VERIFY(determined_rows); - - if (determined_columns == columns() && determined_rows == rows()) - return; - - m_columns = determined_columns; - m_rows = determined_rows; - - m_scroll_region_top = 0; - m_scroll_region_bottom = determined_rows - 1; - - m_current_state.cursor.clamp(rows() - 1, columns() - 1); - m_normal_saved_state.cursor.clamp(rows() - 1, columns() - 1); - m_alternate_saved_state.cursor.clamp(rows() - 1, columns() - 1); - m_saved_cursor_position.clamp(rows() - 1, columns() - 1); - m_horizontal_tabs.resize(determined_columns); - for (unsigned i = 0; i < determined_columns; ++i) - m_horizontal_tabs[i] = (i % 8) == 0; - // Rightmost column is always last tab on line. - m_horizontal_tabs[determined_columns - 1] = 1; - m_client.terminal_did_resize(m_columns, m_rows); -} -void ConsoleImpl::scroll_up(u16 region_top, u16 region_bottom, size_t count) -{ - // NOTE: We have to invalidate the cursor first. - m_client.invalidate_cursor(cursor_row()); - m_client.scroll_up(region_top, region_bottom, count); -} - -void ConsoleImpl::scroll_down(u16 region_top, u16 region_bottom, size_t count) -{ - m_client.invalidate_cursor(cursor_row()); - m_client.scroll_down(region_top, region_bottom, count); -} - -void ConsoleImpl::put_character_at(unsigned row, unsigned column, u32 ch) -{ - m_client.put_character_at(row, column, ch, m_current_state.attribute); - m_last_code_point = ch; -} - -void ConsoleImpl::clear_in_line(u16 row, u16 first_column, u16 last_column) -{ - m_client.clear_in_line(row, first_column, last_column); -} - -void ConsoleImpl::scroll_left(u16 row, u16 column, size_t count) -{ - m_client.scroll_left(row, column, count); -} - -void ConsoleImpl::scroll_right(u16 row, u16 column, size_t count) -{ - m_client.scroll_right(row, column, count); -} - -void VirtualConsole::set_graphical(bool graphical) -{ - m_graphical = graphical; -} - -ErrorOr> VirtualConsole::pseudo_name() const -{ - return KString::formatted("tty:{}", m_index); -} - -UNMAP_AFTER_INIT NonnullLockRefPtr VirtualConsole::create(size_t index) -{ - auto virtual_console_or_error = DeviceManagement::try_create_device(index); - // FIXME: Find a way to propagate errors - VERIFY(!virtual_console_or_error.is_error()); - return virtual_console_or_error.release_value(); -} - -UNMAP_AFTER_INIT NonnullLockRefPtr VirtualConsole::create_with_preset_log(size_t index, CircularQueue const& log) -{ - auto virtual_console = VirtualConsole::create(index); - // HACK: We have to go through the TTY layer for correct newline handling. - // It would be nice to not have to make all these calls, but we can't get the underlying data pointer - // and head index. If we did that, we could reduce this to at most 2 calls. - for (auto ch : log) { - virtual_console->emit_char(ch); - } - return virtual_console; -} - -UNMAP_AFTER_INIT void VirtualConsole::initialize() -{ - VERIFY(GraphicsManagement::the().console()); - set_size(GraphicsManagement::the().console()->max_column(), GraphicsManagement::the().console()->max_row()); - m_console_impl.set_size(GraphicsManagement::the().console()->max_column(), GraphicsManagement::the().console()->max_row()); - - // Allocate twice of the max row * max column * sizeof(Cell) to ensure we can have some sort of history mechanism... - auto size = GraphicsManagement::the().console()->max_column() * GraphicsManagement::the().console()->max_row() * sizeof(Cell) * 2; - m_cells = MM.allocate_kernel_region(Memory::page_round_up(size).release_value_but_fixme_should_propagate_errors(), "Virtual Console Cells"sv, Memory::Region::Access::ReadWrite, AllocationStrategy::AllocateNow).release_value(); - - // Add the lines, so we also ensure they will be flushed now - for (size_t row = 0; row < rows(); row++) { - m_lines.append({ true, 0 }); - } - VERIFY(m_cells); -} - -void VirtualConsole::refresh_after_resolution_change() -{ - auto old_rows_count = rows(); - auto old_columns_count = columns(); - set_size(GraphicsManagement::the().console()->max_column(), GraphicsManagement::the().console()->max_row()); - m_console_impl.set_size(GraphicsManagement::the().console()->max_column(), GraphicsManagement::the().console()->max_row()); - - // Note: From now on, columns() and rows() are updated with the new settings. - - auto size = GraphicsManagement::the().console()->max_column() * GraphicsManagement::the().console()->max_row() * sizeof(Cell) * 2; - auto new_cells = MM.allocate_kernel_region(Memory::page_round_up(size).release_value_but_fixme_should_propagate_errors(), "Virtual Console Cells"sv, Memory::Region::Access::ReadWrite, AllocationStrategy::AllocateNow).release_value(); - - if (rows() < old_rows_count) { - m_lines.shrink(rows()); - } else { - for (size_t row = 0; row < (size_t)(rows() - old_rows_count); row++) { - m_lines.append({ true, 0 }); - } - } - - // Note: A potential loss of displayed data occur when resolution width shrinks. - auto common_rows_count = min(old_rows_count, rows()); - auto common_columns_count = min(old_columns_count, columns()); - for (size_t row = 0; row < common_rows_count; row++) { - auto& line = m_lines[row]; - memcpy(new_cells->vaddr().offset(row * columns() * sizeof(Cell)).as_ptr(), m_cells->vaddr().offset(row * old_columns_count * sizeof(Cell)).as_ptr(), common_columns_count * sizeof(Cell)); - line.dirty = true; - } - - // Update the new cells Region - m_cells = move(new_cells); - m_console_impl.m_need_full_flush = true; - flush_dirty_lines(); -} - -UNMAP_AFTER_INIT VirtualConsole::VirtualConsole(unsigned const index) - : TTY(4, index) - , m_index(index) - , m_console_impl(*this) -{ - initialize(); -} - -UNMAP_AFTER_INIT VirtualConsole::~VirtualConsole() -{ - VERIFY_NOT_REACHED(); -} - -ErrorOr VirtualConsole::ioctl(OpenFileDescription& description, unsigned request, Userspace arg) -{ - TRY(Process::current().require_promise(Pledge::tty)); - switch (request) { - case KDSETMODE: { - auto mode = static_cast(arg.ptr()); - if (mode != KD_TEXT && mode != KD_GRAPHICS) - return EINVAL; - - set_graphical(mode == KD_GRAPHICS); - return {}; - } - case KDGETMODE: { - auto mode_ptr = static_ptr_cast(arg); - int mode = (is_graphical()) ? KD_GRAPHICS : KD_TEXT; - return copy_to_user(mode_ptr, &mode); - } - } - return TTY::ioctl(description, request, arg); -} - -static inline Graphics::Console::Color ansi_color_to_standard_vga_color(VT::Color::ANSIColor color) -{ - switch (color) { - case VT::Color::ANSIColor::DefaultBackground: - case VT::Color::ANSIColor::Black: - return Graphics::Console::Color::Black; - case VT::Color::ANSIColor::Red: - return Graphics::Console::Color::Red; - case VT::Color::ANSIColor::Green: - return Graphics::Console::Color::Green; - case VT::Color::ANSIColor::Yellow: - // VGA only has bright yellow, and treats normal yellow as a brownish orange color. - return Graphics::Console::Color::Brown; - case VT::Color::ANSIColor::Blue: - return Graphics::Console::Color::Blue; - case VT::Color::ANSIColor::Magenta: - return Graphics::Console::Color::Magenta; - case VT::Color::ANSIColor::Cyan: - return Graphics::Console::Color::Cyan; - case VT::Color::ANSIColor::DefaultForeground: - case VT::Color::ANSIColor::White: - return Graphics::Console::Color::LightGray; - case VT::Color::ANSIColor::BrightBlack: - return Graphics::Console::Color::DarkGray; - case VT::Color::ANSIColor::BrightRed: - return Graphics::Console::Color::BrightRed; - case VT::Color::ANSIColor::BrightGreen: - return Graphics::Console::Color::BrightGreen; - case VT::Color::ANSIColor::BrightYellow: - return Graphics::Console::Color::Yellow; - case VT::Color::ANSIColor::BrightBlue: - return Graphics::Console::Color::BrightBlue; - case VT::Color::ANSIColor::BrightMagenta: - return Graphics::Console::Color::BrightMagenta; - case VT::Color::ANSIColor::BrightCyan: - return Graphics::Console::Color::BrightCyan; - case VT::Color::ANSIColor::BrightWhite: - return Graphics::Console::Color::White; - } - VERIFY_NOT_REACHED(); -} - -static inline Graphics::Console::Color terminal_to_standard_color(VT::Color color) -{ - switch (color.kind()) { - case VT::Color::Kind::Named: - return ansi_color_to_standard_vga_color(color.as_named()); - default: - return Graphics::Console::Color::LightGray; - } -} - -void VirtualConsole::on_key_pressed(KeyEvent event) -{ - // Ignore keyboard in graphical mode. - if (m_graphical) - return; - - if (!event.is_press()) - return; - - Processor::deferred_call_queue([this, event]() { - m_console_impl.handle_key_press(event.key, event.code_point, event.flags); - }); -} - -ErrorOr VirtualConsole::on_tty_write(UserOrKernelBuffer const& data, size_t size) -{ - SpinlockLocker global_lock(ConsoleManagement::the().tty_write_lock()); - auto result = data.read_buffered<512>(size, [&](ReadonlyBytes buffer) { - for (auto const& byte : buffer) - m_console_impl.on_input(byte); - return buffer.size(); - }); - if (m_active) - flush_dirty_lines(); - return result; -} - -void VirtualConsole::set_active(bool active) -{ - VERIFY(ConsoleManagement::the().m_lock.is_locked()); - VERIFY(m_active != active); - m_active = active; - - if (active) { - HIDManagement::the().set_client(this); - - m_console_impl.m_need_full_flush = true; - flush_dirty_lines(); - } else { - HIDManagement::the().set_client(nullptr); - } -} - -void VirtualConsole::emit_char(char ch) -{ - // Since we are standards-compliant by not moving to column 1 on '\n', we have to add an extra carriage return to - // do newlines properly. The `TTY` layer handles adding it. - echo_with_processing(static_cast(ch)); -} - -void VirtualConsole::flush_dirty_lines() -{ - if (!m_active) - return; - VERIFY(GraphicsManagement::is_initialized()); - VERIFY(GraphicsManagement::the().console()); - for (u16 visual_row = 0; visual_row < rows(); ++visual_row) { - auto& line = m_lines[visual_row]; - if (!line.dirty && !m_console_impl.m_need_full_flush) - continue; - for (size_t column = 0; column < columns(); ++column) { - auto& cell = cell_at(column, visual_row); - - auto foreground_color = terminal_to_standard_color(cell.attribute.effective_foreground_color()); - if (has_flag(cell.attribute.flags, VT::Attribute::Flags::Bold)) - foreground_color = (Graphics::Console::Color)((u8)foreground_color | 0x08); - GraphicsManagement::the().console()->write(column, - visual_row, - ((u8)cell.ch < 128 ? cell.ch : '?'), - terminal_to_standard_color(cell.attribute.effective_background_color()), - foreground_color); - } - line.dirty = false; - } - GraphicsManagement::the().console()->set_cursor(m_console_impl.cursor_column(), m_console_impl.cursor_row()); - m_console_impl.m_need_full_flush = false; -} - -void VirtualConsole::beep() -{ - if (!kernel_command_line().is_pc_speaker_enabled()) - return; -#if ARCH(X86_64) - PCSpeaker::tone_on(440); - microseconds_delay(10000); - PCSpeaker::tone_off(); -#endif -} - -void VirtualConsole::set_window_title(StringView) -{ - // Do nothing. -} - -void VirtualConsole::set_window_progress(int, int) -{ - // Do nothing. -} - -void VirtualConsole::terminal_did_resize(u16 columns, u16 rows) -{ - // FIXME: Allocate more Region(s) or deallocate them if needed... - dbgln("VC {}: Resized to {} x {}", index(), columns, rows); -} - -void VirtualConsole::terminal_history_changed(int) -{ - // Do nothing, I guess? -} - -void VirtualConsole::terminal_did_perform_possibly_partial_clear() -{ - // Do nothing, we're not going to hit this anyway. -} - -void VirtualConsole::emit(u8 const* data, size_t size) -{ - for (size_t i = 0; i < size; i++) - TTY::emit(data[i], true); -} - -void VirtualConsole::set_cursor_shape(VT::CursorShape) -{ - // Do nothing -} - -void VirtualConsole::set_cursor_blinking(bool) -{ - // Do nothing -} - -void VirtualConsole::echo(u8 ch) -{ - m_console_impl.on_input(ch); - if (m_active) - flush_dirty_lines(); -} - -VirtualConsole::Cell& VirtualConsole::cell_at(size_t x, size_t y) -{ - auto* ptr = (VirtualConsole::Cell*)(m_cells->vaddr().as_ptr()); - ptr += (y * columns()) + x; - return *ptr; -} - -void VirtualConsole::clear() -{ - auto* cell = (Cell*)m_cells->vaddr().as_ptr(); - for (size_t y = 0; y < rows(); y++) { - m_lines[y].dirty = true; - for (size_t x = 0; x < columns(); x++) { - cell[x].clear(); - } - cell += columns(); - } - m_console_impl.set_cursor(0, 0); -} - -void VirtualConsole::scroll_up(u16 region_top, u16 region_bottom, size_t count) -{ - VERIFY(region_top <= region_bottom); - size_t region_size = region_bottom - region_top + 1; - count = min(count, region_size); - size_t line_bytes = (columns() * sizeof(Cell)); - memmove(m_cells->vaddr().offset(line_bytes * region_top).as_ptr(), m_cells->vaddr().offset(line_bytes * (region_top + count)).as_ptr(), line_bytes * (region_size - count)); - for (size_t i = 0; i < count; ++i) - clear_line(region_bottom - i); - for (u16 row = region_top; row <= region_bottom; ++row) - m_lines[row].dirty = true; -} - -void VirtualConsole::scroll_down(u16 region_top, u16 region_bottom, size_t count) -{ - VERIFY(region_top <= region_bottom); - size_t region_size = region_bottom - region_top + 1; - count = min(count, region_size); - size_t line_bytes = (columns() * sizeof(Cell)); - memmove(m_cells->vaddr().offset(line_bytes * (region_top + count)).as_ptr(), m_cells->vaddr().offset(line_bytes * region_top).as_ptr(), line_bytes * (region_size - count)); - for (size_t i = 0; i < count; ++i) - clear_line(region_top + i); - for (u16 row = region_top; row <= region_bottom; ++row) - m_lines[row].dirty = true; -} - -void VirtualConsole::scroll_left(u16 row, u16 column, size_t count) -{ - VERIFY(row < rows()); - VERIFY(column < columns()); - count = min(count, columns() - column); - memmove(&cell_at(column, row), &cell_at(column + count, row), sizeof(Cell) * (columns() - column - count)); - for (size_t i = column + count; i < columns(); ++i) - cell_at(i, row).clear(); - m_lines[row].dirty = true; -} - -void VirtualConsole::scroll_right(u16 row, u16 column, size_t count) -{ - VERIFY(row < rows()); - VERIFY(column < columns()); - count = min(count, columns() - column); - memmove(&cell_at(column + count, row), &cell_at(column, row), sizeof(Cell) * (columns() - column - count)); - for (size_t i = column; i < column + count; ++i) - cell_at(i, row).clear(); - m_lines[row].dirty = true; -} - -void VirtualConsole::clear_in_line(u16 row, u16 first_column, u16 last_column) -{ - VERIFY(row < rows()); - VERIFY(first_column <= last_column); - VERIFY(last_column < columns()); - m_lines[row].dirty = true; - for (size_t x = first_column; x <= last_column; x++) - cell_at(x, row).clear(); -} - -void VirtualConsole::put_character_at(unsigned row, unsigned column, u32 code_point, const VT::Attribute& attribute) -{ - VERIFY(row < rows()); - VERIFY(column < columns()); - auto& line = m_lines[row]; - auto& cell = cell_at(column, row); - cell.attribute.foreground_color = attribute.foreground_color; - cell.attribute.background_color = attribute.background_color; - cell.attribute.flags = attribute.flags; - if (code_point > 128) - cell.ch = ' '; - else - cell.ch = code_point; - cell.attribute.flags |= VT::Attribute::Flags::Touched; - line.dirty = true; - // FIXME: Maybe we should consider to change length after printing a special char in a column - if (code_point <= 20) - return; - line.length = max(line.length, column); -} - -void VirtualConsole::invalidate_cursor(size_t row) -{ - m_lines[row].dirty = true; -} - -} diff --git a/Kernel/Devices/TTY/VirtualConsole.h b/Kernel/Devices/TTY/VirtualConsole.h deleted file mode 100644 index 98e66664cf7..00000000000 --- a/Kernel/Devices/TTY/VirtualConsole.h +++ /dev/null @@ -1,144 +0,0 @@ -/* - * Copyright (c) 2018-2021, Andreas Kling - * Copyright (c) 2021, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace Kernel { - -class ConsoleManagement; -class VirtualConsole; -// FIXME: This implementation has no knowledge about keeping terminal history... -class ConsoleImpl final : public VT::Terminal { -public: - explicit ConsoleImpl(VirtualConsole&); - - virtual void set_size(u16 columns, u16 rows) override; - -private: - virtual void invalidate_cursor() override; - virtual void clear() override; - virtual void clear_history() override; - - virtual void scroll_up(u16 region_top, u16 region_bottom, size_t count) override; - virtual void scroll_down(u16 region_top, u16 region_bottom, size_t count) override; - virtual void scroll_left(u16 row, u16 column, size_t count) override; - virtual void scroll_right(u16 row, u16 column, size_t count) override; - virtual void put_character_at(unsigned row, unsigned column, u32 ch) override; - virtual void clear_in_line(u16 row, u16 first_column, u16 last_column) override; -}; - -class VirtualConsole final : public TTY - , public KeyboardClient - , public VT::TerminalClient { - friend class ConsoleManagement; - friend class DeviceManagement; - friend class ConsoleImpl; - friend class VT::Terminal; - -public: - struct Line { - bool dirty; - size_t length; - }; - - struct Cell { - void clear() - { - ch = ' '; - attribute.reset(); - } - char ch; - VT::Attribute attribute; - }; - -public: - static NonnullLockRefPtr create(size_t index); - static NonnullLockRefPtr create_with_preset_log(size_t index, CircularQueue const&); - - virtual ~VirtualConsole() override; - - size_t index() const { return m_index; } - - void refresh_after_resolution_change(); - - bool is_graphical() const { return m_graphical; } - void set_graphical(bool graphical); - - void emit_char(char); - -private: - explicit VirtualConsole(unsigned const index); - // ^KeyboardClient - virtual void on_key_pressed(KeyEvent) override; - - // ^TTY - virtual ErrorOr> pseudo_name() const override; - virtual ErrorOr on_tty_write(UserOrKernelBuffer const&, size_t) override; - virtual ErrorOr ioctl(OpenFileDescription&, unsigned request, Userspace arg) override; - virtual void echo(u8) override; - - // ^TerminalClient - virtual void beep() override; - virtual void set_window_title(StringView) override; - virtual void set_window_progress(int, int) override; - virtual void terminal_did_resize(u16 columns, u16 rows) override; - virtual void terminal_history_changed(int) override; - virtual void terminal_did_perform_possibly_partial_clear() override; - virtual void emit(u8 const*, size_t) override; - virtual void set_cursor_shape(VT::CursorShape) override; - virtual void set_cursor_blinking(bool) override; - - // ^CharacterDevice - virtual StringView class_name() const override { return "VirtualConsole"sv; } - - void set_active(bool); - void flush_dirty_lines(); - - unsigned m_index; - bool m_active { false }; - bool m_graphical { false }; - -private: - void initialize(); - - void invalidate_cursor(size_t row); - - void clear(); - - Cell& cell_at(size_t column, size_t row); - - using ParamVector = Vector; - - void scroll_down(u16 region_top, u16 region_bottom, size_t count); - void scroll_up(u16 region_top, u16 region_bottom, size_t count); - void scroll_left(u16 row, u16 column, size_t count); - void scroll_right(u16 row, u16 column, size_t count); - void clear_line(size_t index) - { - clear_in_line(index, 0, m_console_impl.columns() - 1); - } - void clear_in_line(u16 row, u16 first_column, u16 last_column); - void put_character_at(unsigned row, unsigned column, u32 ch, const VT::Attribute&); - - OwnPtr m_cells; - Vector m_lines; - ConsoleImpl m_console_impl; -}; - -} diff --git a/Kernel/FileSystem/AnonymousFile.cpp b/Kernel/FileSystem/AnonymousFile.cpp deleted file mode 100644 index 573b22b3651..00000000000 --- a/Kernel/FileSystem/AnonymousFile.cpp +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright (c) 2021-2022, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include - -namespace Kernel { - -AnonymousFile::AnonymousFile(NonnullLockRefPtr vmobject) - : m_vmobject(move(vmobject)) -{ -} - -AnonymousFile::~AnonymousFile() = default; - -ErrorOr> AnonymousFile::vmobject_for_mmap(Process&, Memory::VirtualRange const&, u64& offset, bool) -{ - if (offset != 0) - return EINVAL; - - return m_vmobject; -} - -ErrorOr> AnonymousFile::pseudo_path(OpenFileDescription const&) const -{ - return KString::try_create(":anonymous-file:"sv); -} - -} diff --git a/Kernel/FileSystem/AnonymousFile.h b/Kernel/FileSystem/AnonymousFile.h deleted file mode 100644 index 3ec333ff5a1..00000000000 --- a/Kernel/FileSystem/AnonymousFile.h +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright (c) 2021-2022, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include - -namespace Kernel { - -class AnonymousFile final : public File { -public: - static ErrorOr> try_create(NonnullLockRefPtr vmobject) - { - return adopt_nonnull_ref_or_enomem(new (nothrow) AnonymousFile(move(vmobject))); - } - - virtual ~AnonymousFile() override; - - virtual ErrorOr> vmobject_for_mmap(Process&, Memory::VirtualRange const&, u64& offset, bool shared) override; - -private: - virtual StringView class_name() const override { return "AnonymousFile"sv; } - virtual ErrorOr> pseudo_path(OpenFileDescription const&) const override; - virtual bool can_read(OpenFileDescription const&, u64) const override { return false; } - virtual bool can_write(OpenFileDescription const&, u64) const override { return false; } - virtual ErrorOr read(OpenFileDescription&, u64, UserOrKernelBuffer&, size_t) override { return ENOTSUP; } - virtual ErrorOr write(OpenFileDescription&, u64, UserOrKernelBuffer const&, size_t) override { return ENOTSUP; } - - explicit AnonymousFile(NonnullLockRefPtr); - - NonnullLockRefPtr m_vmobject; -}; - -} diff --git a/Kernel/FileSystem/BlockBasedFileSystem.cpp b/Kernel/FileSystem/BlockBasedFileSystem.cpp deleted file mode 100644 index c60a2e1caa5..00000000000 --- a/Kernel/FileSystem/BlockBasedFileSystem.cpp +++ /dev/null @@ -1,314 +0,0 @@ -/* - * Copyright (c) 2018-2022, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include - -namespace Kernel { - -struct CacheEntry { - IntrusiveListNode list_node; - BlockBasedFileSystem::BlockIndex block_index { 0 }; - u8* data { nullptr }; - bool has_data { false }; -}; - -class DiskCache { -public: - static constexpr size_t EntryCount = 10000; - explicit DiskCache(BlockBasedFileSystem& fs, NonnullOwnPtr cached_block_data, NonnullOwnPtr entries_buffer) - : m_fs(fs) - , m_cached_block_data(move(cached_block_data)) - , m_entries(move(entries_buffer)) - { - for (size_t i = 0; i < EntryCount; ++i) { - entries()[i].data = m_cached_block_data->data() + i * m_fs->logical_block_size(); - m_clean_list.append(entries()[i]); - } - } - - ~DiskCache() = default; - - bool is_dirty() const { return !m_dirty_list.is_empty(); } - bool entry_is_dirty(CacheEntry const& entry) const { return m_dirty_list.contains(entry); } - - void mark_all_clean() - { - while (auto* entry = m_dirty_list.first()) - m_clean_list.prepend(*entry); - } - - void mark_dirty(CacheEntry& entry) - { - m_dirty_list.prepend(entry); - } - - void mark_clean(CacheEntry& entry) - { - m_clean_list.prepend(entry); - } - - CacheEntry* get(BlockBasedFileSystem::BlockIndex block_index) const - { - auto it = m_hash.find(block_index); - if (it == m_hash.end()) - return nullptr; - auto& entry = const_cast(*it->value); - VERIFY(entry.block_index == block_index); - if (!entry_is_dirty(entry) && (m_clean_list.first() != &entry)) { - // Cache hit! Promote the entry to the front of the list. - m_clean_list.prepend(entry); - } - return &entry; - } - - ErrorOr ensure(BlockBasedFileSystem::BlockIndex block_index) const - { - if (auto* entry = get(block_index)) - return entry; - - if (m_clean_list.is_empty()) { - // Not a single clean entry! Flush writes and try again. - // NOTE: We want to make sure we only call FileBackedFileSystem flush here, - // not some FileBackedFileSystem subclass flush! - m_fs->flush_writes_impl(); - return ensure(block_index); - } - - VERIFY(m_clean_list.last()); - auto& new_entry = *m_clean_list.last(); - m_clean_list.prepend(new_entry); - - m_hash.remove(new_entry.block_index); - TRY(m_hash.try_set(block_index, &new_entry)); - - new_entry.block_index = block_index; - new_entry.has_data = false; - - return &new_entry; - } - - CacheEntry const* entries() const { return (CacheEntry const*)m_entries->data(); } - CacheEntry* entries() { return (CacheEntry*)m_entries->data(); } - - template - void for_each_dirty_entry(Callback callback) - { - for (auto& entry : m_dirty_list) - callback(entry); - } - -private: - mutable NonnullRefPtr m_fs; - NonnullOwnPtr m_cached_block_data; - - // NOTE: m_entries must be declared before m_dirty_list and m_clean_list because their entries are allocated from it. - // We need to ensure that the destructors of m_dirty_list and m_clean_list are called before m_entries is destroyed. - NonnullOwnPtr m_entries; - mutable IntrusiveList<&CacheEntry::list_node> m_dirty_list; - mutable IntrusiveList<&CacheEntry::list_node> m_clean_list; - mutable HashMap m_hash; -}; - -BlockBasedFileSystem::BlockBasedFileSystem(OpenFileDescription& file_description) - : FileBackedFileSystem(file_description) -{ - VERIFY(file_description.file().is_seekable()); -} - -BlockBasedFileSystem::~BlockBasedFileSystem() = default; - -void BlockBasedFileSystem::remove_disk_cache_before_last_unmount() -{ - VERIFY(m_lock.is_locked()); - m_cache.with_exclusive([&](auto& cache) { - cache.clear(); - }); -} - -ErrorOr BlockBasedFileSystem::initialize_while_locked() -{ - VERIFY(m_lock.is_locked()); - VERIFY(!is_initialized_while_locked()); - VERIFY(logical_block_size() != 0); - auto cached_block_data = TRY(KBuffer::try_create_with_size("BlockBasedFS: Cache blocks"sv, DiskCache::EntryCount * logical_block_size())); - auto entries_data = TRY(KBuffer::try_create_with_size("BlockBasedFS: Cache entries"sv, DiskCache::EntryCount * sizeof(CacheEntry))); - auto disk_cache = TRY(adopt_nonnull_own_or_enomem(new (nothrow) DiskCache(*this, move(cached_block_data), move(entries_data)))); - - m_cache.with_exclusive([&](auto& cache) { - cache = move(disk_cache); - }); - return {}; -} - -ErrorOr BlockBasedFileSystem::write_block(BlockIndex index, UserOrKernelBuffer const& data, size_t count, u64 offset, bool allow_cache) -{ - VERIFY(m_device_block_size); - VERIFY(offset + count <= logical_block_size()); - dbgln_if(BBFS_DEBUG, "BlockBasedFileSystem::write_block {}, size={}", index, count); - - // NOTE: We copy the `data` to write into a local buffer before taking the cache lock. - // This makes sure any page faults caused by accessing the data will occur before - // we tie down the cache. - auto buffered_data = TRY(ByteBuffer::create_uninitialized(count)); - - TRY(data.read(buffered_data.bytes())); - - return m_cache.with_exclusive([&](auto& cache) -> ErrorOr { - if (!allow_cache) { - flush_specific_block_if_needed(index); - u64 base_offset = index.value() * logical_block_size() + offset; - auto nwritten = TRY(file_description().write(base_offset, data, count)); - VERIFY(nwritten == count); - return {}; - } - - auto entry = TRY(cache->ensure(index)); - if (count < logical_block_size()) { - // Fill the cache first. - TRY(read_block(index, nullptr, logical_block_size())); - } - memcpy(entry->data + offset, buffered_data.data(), count); - - cache->mark_dirty(*entry); - entry->has_data = true; - return {}; - }); -} - -ErrorOr BlockBasedFileSystem::raw_read(BlockIndex index, UserOrKernelBuffer& buffer) -{ - auto base_offset = index.value() * m_device_block_size; - auto nread = TRY(file_description().read(buffer, base_offset, m_device_block_size)); - VERIFY(nread == m_device_block_size); - return {}; -} - -ErrorOr BlockBasedFileSystem::raw_write(BlockIndex index, UserOrKernelBuffer const& buffer) -{ - auto base_offset = index.value() * m_device_block_size; - auto nwritten = TRY(file_description().write(base_offset, buffer, m_device_block_size)); - VERIFY(nwritten == m_device_block_size); - return {}; -} - -ErrorOr BlockBasedFileSystem::raw_read_blocks(BlockIndex index, size_t count, UserOrKernelBuffer& buffer) -{ - auto current = buffer; - for (auto block = index.value(); block < (index.value() + count); block++) { - TRY(raw_read(BlockIndex { block }, current)); - current = current.offset(device_block_size()); - } - return {}; -} - -ErrorOr BlockBasedFileSystem::raw_write_blocks(BlockIndex index, size_t count, UserOrKernelBuffer const& buffer) -{ - auto current = buffer; - for (auto block = index.value(); block < (index.value() + count); block++) { - TRY(raw_write(block, current)); - current = current.offset(device_block_size()); - } - return {}; -} - -ErrorOr BlockBasedFileSystem::write_blocks(BlockIndex index, unsigned count, UserOrKernelBuffer const& data, bool allow_cache) -{ - VERIFY(m_device_block_size); - dbgln_if(BBFS_DEBUG, "BlockBasedFileSystem::write_blocks {}, count={}", index, count); - for (unsigned i = 0; i < count; ++i) { - TRY(write_block(BlockIndex { index.value() + i }, data.offset(i * logical_block_size()), logical_block_size(), 0, allow_cache)); - } - return {}; -} - -ErrorOr BlockBasedFileSystem::read_block(BlockIndex index, UserOrKernelBuffer* buffer, size_t count, u64 offset, bool allow_cache) const -{ - VERIFY(m_device_block_size); - VERIFY(offset + count <= logical_block_size()); - dbgln_if(BBFS_DEBUG, "BlockBasedFileSystem::read_block {}", index); - - return m_cache.with_exclusive([&](auto& cache) -> ErrorOr { - if (!allow_cache) { - const_cast(this)->flush_specific_block_if_needed(index); - u64 base_offset = index.value() * logical_block_size() + offset; - auto nread = TRY(file_description().read(*buffer, base_offset, count)); - VERIFY(nread == count); - return {}; - } - - auto* entry = TRY(cache->ensure(index)); - if (!entry->has_data) { - auto base_offset = index.value() * logical_block_size(); - auto entry_data_buffer = UserOrKernelBuffer::for_kernel_buffer(entry->data); - auto nread = TRY(file_description().read(entry_data_buffer, base_offset, logical_block_size())); - VERIFY(nread == logical_block_size()); - entry->has_data = true; - } - if (buffer) - TRY(buffer->write(entry->data + offset, count)); - return {}; - }); -} - -ErrorOr BlockBasedFileSystem::read_blocks(BlockIndex index, unsigned count, UserOrKernelBuffer& buffer, bool allow_cache) const -{ - VERIFY(m_device_block_size); - if (!count) - return EINVAL; - if (count == 1) - return read_block(index, &buffer, logical_block_size(), 0, allow_cache); - auto out = buffer; - for (unsigned i = 0; i < count; ++i) { - TRY(read_block(BlockIndex { index.value() + i }, &out, logical_block_size(), 0, allow_cache)); - out = out.offset(logical_block_size()); - } - - return {}; -} - -void BlockBasedFileSystem::flush_specific_block_if_needed(BlockIndex index) -{ - m_cache.with_exclusive([&](auto& cache) { - if (!cache->is_dirty()) - return; - auto* entry = cache->get(index); - if (!entry) - return; - if (!cache->entry_is_dirty(*entry)) - return; - size_t base_offset = entry->block_index.value() * logical_block_size(); - auto entry_data_buffer = UserOrKernelBuffer::for_kernel_buffer(entry->data); - (void)file_description().write(base_offset, entry_data_buffer, logical_block_size()); - }); -} - -void BlockBasedFileSystem::flush_writes_impl() -{ - size_t count = 0; - m_cache.with_exclusive([&](auto& cache) { - if (!cache->is_dirty()) - return; - cache->for_each_dirty_entry([&](CacheEntry& entry) { - auto base_offset = entry.block_index.value() * logical_block_size(); - auto entry_data_buffer = UserOrKernelBuffer::for_kernel_buffer(entry.data); - [[maybe_unused]] auto rc = file_description().write(base_offset, entry_data_buffer, logical_block_size()); - ++count; - }); - cache->mark_all_clean(); - dbgln("{}: Flushed {} blocks to disk", class_name(), count); - }); -} - -ErrorOr BlockBasedFileSystem::flush_writes() -{ - flush_writes_impl(); - return {}; -} - -} diff --git a/Kernel/FileSystem/BlockBasedFileSystem.h b/Kernel/FileSystem/BlockBasedFileSystem.h deleted file mode 100644 index 4b1b23ffaec..00000000000 --- a/Kernel/FileSystem/BlockBasedFileSystem.h +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright (c) 2018-2021, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include - -namespace Kernel { - -class BlockBasedFileSystem : public FileBackedFileSystem { -public: - AK_TYPEDEF_DISTINCT_ORDERED_ID(u64, BlockIndex); - - virtual ~BlockBasedFileSystem() override; - - u64 device_block_size() const { return m_device_block_size; } - - virtual ErrorOr flush_writes() override; - void flush_writes_impl(); - -protected: - explicit BlockBasedFileSystem(OpenFileDescription&); - - virtual ErrorOr initialize_while_locked() override; - - ErrorOr read_block(BlockIndex, UserOrKernelBuffer*, size_t count, u64 offset = 0, bool allow_cache = true) const; - ErrorOr read_blocks(BlockIndex, unsigned count, UserOrKernelBuffer&, bool allow_cache = true) const; - - ErrorOr raw_read(BlockIndex, UserOrKernelBuffer&); - ErrorOr raw_write(BlockIndex, UserOrKernelBuffer const&); - - ErrorOr raw_read_blocks(BlockIndex index, size_t count, UserOrKernelBuffer&); - ErrorOr raw_write_blocks(BlockIndex index, size_t count, UserOrKernelBuffer const&); - - ErrorOr write_block(BlockIndex, UserOrKernelBuffer const&, size_t count, u64 offset = 0, bool allow_cache = true); - ErrorOr write_blocks(BlockIndex, unsigned count, UserOrKernelBuffer const&, bool allow_cache = true); - - u64 m_device_block_size { 512 }; - - void remove_disk_cache_before_last_unmount(); - -private: - void flush_specific_block_if_needed(BlockIndex index); - - mutable MutexProtected> m_cache; -}; - -} diff --git a/Kernel/FileSystem/Custody.cpp b/Kernel/FileSystem/Custody.cpp deleted file mode 100644 index bf949344134..00000000000 --- a/Kernel/FileSystem/Custody.cpp +++ /dev/null @@ -1,89 +0,0 @@ -/* - * Copyright (c) 2018-2022, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include -#include -#include - -namespace Kernel { - -static Singleton> s_all_instances; - -SpinlockProtected& Custody::all_instances() -{ - return s_all_instances; -} - -ErrorOr> Custody::try_create(Custody* parent, StringView name, Inode& inode, int mount_flags) -{ - return all_instances().with([&](auto& all_custodies) -> ErrorOr> { - for (Custody& custody : all_custodies) { - if (custody.parent() == parent - && custody.name() == name - && &custody.inode() == &inode - && custody.mount_flags() == mount_flags) { - return NonnullRefPtr { custody }; - } - } - - auto name_kstring = TRY(KString::try_create(name)); - auto custody = TRY(adopt_nonnull_ref_or_enomem(new (nothrow) Custody(parent, move(name_kstring), inode, mount_flags))); - all_custodies.prepend(*custody); - return custody; - }); -} - -Custody::Custody(Custody* parent, NonnullOwnPtr name, Inode& inode, int mount_flags) - : m_parent(parent) - , m_name(move(name)) - , m_inode(inode) - , m_mount_flags(mount_flags) -{ -} - -Custody::~Custody() = default; - -ErrorOr> Custody::try_serialize_absolute_path() const -{ - if (!parent()) - return KString::try_create("/"sv); - - Vector custody_chain; - size_t path_length = 0; - for (auto const* custody = this; custody; custody = custody->parent()) { - TRY(custody_chain.try_append(custody)); - path_length += custody->m_name->length() + 1; - } - VERIFY(path_length > 0); - - char* buffer; - auto string = TRY(KString::try_create_uninitialized(path_length - 1, buffer)); - size_t string_index = 0; - for (size_t custody_index = custody_chain.size() - 1; custody_index > 0; --custody_index) { - buffer[string_index] = '/'; - ++string_index; - auto const& custody_name = *custody_chain[custody_index - 1]->m_name; - __builtin_memcpy(buffer + string_index, custody_name.characters(), custody_name.length()); - string_index += custody_name.length(); - } - VERIFY(string->length() == string_index); - buffer[string_index] = 0; - return string; -} - -bool Custody::is_readonly() const -{ - if (m_mount_flags & MS_RDONLY) - return true; - - return m_inode->fs().is_readonly(); -} - -} diff --git a/Kernel/FileSystem/Custody.h b/Kernel/FileSystem/Custody.h deleted file mode 100644 index 7dac3a9a41d..00000000000 --- a/Kernel/FileSystem/Custody.h +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright (c) 2018-2022, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include -#include -#include - -namespace Kernel { - -class Custody final : public ListedRefCounted { -public: - static ErrorOr> try_create(Custody* parent, StringView name, Inode&, int mount_flags); - - ~Custody(); - - RefPtr parent() { return m_parent; } - RefPtr parent() const { return m_parent; } - Inode& inode() { return *m_inode; } - Inode const& inode() const { return *m_inode; } - StringView name() const { return m_name->view(); } - ErrorOr> try_serialize_absolute_path() const; - - int mount_flags() const { return m_mount_flags; } - bool is_readonly() const; - -private: - Custody(Custody* parent, NonnullOwnPtr name, Inode&, int mount_flags); - - RefPtr m_parent; - NonnullOwnPtr m_name; - NonnullRefPtr const m_inode; - int m_mount_flags { 0 }; - - mutable IntrusiveListNode m_all_custodies_list_node; - -public: - using AllCustodiesList = IntrusiveList<&Custody::m_all_custodies_list_node>; - static SpinlockProtected& all_instances(); -}; - -} diff --git a/Kernel/FileSystem/DevLoopFS/FileSystem.cpp b/Kernel/FileSystem/DevLoopFS/FileSystem.cpp deleted file mode 100644 index 5f2557c3dec..00000000000 --- a/Kernel/FileSystem/DevLoopFS/FileSystem.cpp +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Copyright (c) 2024, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include -#include - -namespace Kernel { - -ErrorOr> DevLoopFS::try_create(ReadonlyBytes) -{ - return TRY(adopt_nonnull_ref_or_enomem(new (nothrow) DevLoopFS)); -} - -DevLoopFS::DevLoopFS() = default; -DevLoopFS::~DevLoopFS() = default; - -u8 DevLoopFS::internal_file_type_to_directory_entry_type(DirectoryEntryView const& entry) const -{ - return ram_backed_file_type_to_directory_entry_type(entry); -} - -ErrorOr DevLoopFS::initialize() -{ - m_root_inode = TRY(adopt_nonnull_ref_or_enomem(new (nothrow) DevLoopFSInode(*this))); - m_root_inode->m_metadata.inode = { fsid(), 1 }; - m_root_inode->m_metadata.mode = S_IFDIR - | S_IROTH | S_IRGRP | S_IRUSR - | S_IXUSR | S_IXGRP | S_IXOTH; - m_root_inode->m_metadata.uid = 0; - m_root_inode->m_metadata.gid = 0; - m_root_inode->m_metadata.size = 0; - m_root_inode->m_metadata.mtime = TimeManagement::boot_time(); - return {}; -} - -static unsigned inode_index_to_loop_index(InodeIndex inode_index) -{ - VERIFY(inode_index > 1); - return inode_index.value() - 2; -} - -Inode& DevLoopFS::root_inode() -{ - return *m_root_inode; -} - -ErrorOr> DevLoopFS::get_inode(InodeIdentifier inode_id) const -{ - if (inode_id.index() == 1) - return *m_root_inode; - - unsigned loop_index = inode_index_to_loop_index(inode_id.index()); - auto device = DeviceManagement::the().get_device(20, loop_index); - VERIFY(device); - - auto& loop_device = static_cast(*device); - auto inode = TRY(adopt_nonnull_ref_or_enomem(new (nothrow) DevLoopFSInode(const_cast(*this), inode_id.index(), loop_device))); - inode->m_metadata.inode = inode_id; - inode->m_metadata.size = 0; - inode->m_metadata.uid = 0; - inode->m_metadata.gid = 0; - inode->m_metadata.mode = S_IFCHR | S_IRUSR | S_IWUSR; - inode->m_metadata.major_device = device->major(); - inode->m_metadata.minor_device = device->minor(); - inode->m_metadata.mtime = TimeManagement::boot_time(); - return inode; -} - -} diff --git a/Kernel/FileSystem/DevLoopFS/FileSystem.h b/Kernel/FileSystem/DevLoopFS/FileSystem.h deleted file mode 100644 index 76bca0c39f8..00000000000 --- a/Kernel/FileSystem/DevLoopFS/FileSystem.h +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright (c) 2024, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include - -namespace Kernel { - -class LoopDevice; -class DevLoopFSInode; - -class DevLoopFS final : public FileSystem { - friend class DevLoopFSInode; - -public: - virtual ~DevLoopFS() override; - static ErrorOr> try_create(ReadonlyBytes); - - virtual ErrorOr initialize() override; - virtual StringView class_name() const override { return "DevLoopFS"sv; } - - virtual Inode& root_inode() override; - -private: - virtual u8 internal_file_type_to_directory_entry_type(DirectoryEntryView const& entry) const override; - - DevLoopFS(); - ErrorOr> get_inode(InodeIdentifier) const; - - RefPtr m_root_inode; -}; - -} diff --git a/Kernel/FileSystem/DevLoopFS/Inode.cpp b/Kernel/FileSystem/DevLoopFS/Inode.cpp deleted file mode 100644 index e5bbae783d0..00000000000 --- a/Kernel/FileSystem/DevLoopFS/Inode.cpp +++ /dev/null @@ -1,124 +0,0 @@ -/* - * Copyright (c) 2024, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include - -namespace Kernel { - -static InodeIndex loop_index_to_inode_index(unsigned loop_index) -{ - return loop_index + 2; -} - -DevLoopFSInode::DevLoopFSInode(DevLoopFS& fs, InodeIndex index, LoopDevice& loop_device) - : Inode(fs, index) - , m_loop_device(loop_device) -{ -} - -// NOTE: This constructor is used for the root inode only. -DevLoopFSInode::DevLoopFSInode(DevLoopFS& fs) - : Inode(fs, 1) -{ -} - -DevLoopFSInode::~DevLoopFSInode() = default; - -ErrorOr DevLoopFSInode::read_bytes_locked(off_t, size_t, UserOrKernelBuffer&, OpenFileDescription*) const -{ - VERIFY_NOT_REACHED(); -} - -ErrorOr DevLoopFSInode::write_bytes_locked(off_t, size_t, UserOrKernelBuffer const&, OpenFileDescription*) -{ - VERIFY_NOT_REACHED(); -} - -InodeMetadata DevLoopFSInode::metadata() const -{ - return m_metadata; -} - -ErrorOr DevLoopFSInode::traverse_as_directory(Function(FileSystem::DirectoryEntryView const&)> callback) const -{ - if (identifier().index() > 1) - return ENOTDIR; - - TRY(callback({ "."sv, identifier(), to_underlying(RAMBackedFileType::Directory) })); - TRY(callback({ ".."sv, identifier(), to_underlying(RAMBackedFileType::Directory) })); - - return LoopDevice::all_instances().with([&](auto& list) -> ErrorOr { - StringBuilder builder; - for (LoopDevice& loop_device : list) { - builder.clear(); - TRY(builder.try_appendff("{}", loop_device.index())); - TRY(callback({ builder.string_view(), { fsid(), loop_index_to_inode_index(loop_device.index()) }, to_underlying(RAMBackedFileType::Block) })); - } - return {}; - }); -} - -ErrorOr> DevLoopFSInode::lookup(StringView name) -{ - VERIFY(identifier().index() == 1); - - if (name == "." || name == "..") - return *this; - - auto loop_index = name.to_number(); - if (!loop_index.has_value()) - return ENOENT; - - return LoopDevice::all_instances().with([&](auto& list) -> ErrorOr> { - for (LoopDevice& loop_device : list) { - if (loop_device.index() != loop_index.value()) - continue; - return fs().get_inode({ fsid(), loop_index_to_inode_index(loop_index.value()) }); - } - return ENOENT; - }); -} - -ErrorOr DevLoopFSInode::flush_metadata() -{ - return {}; -} - -ErrorOr DevLoopFSInode::add_child(Inode&, StringView, mode_t) -{ - return EROFS; -} - -ErrorOr> DevLoopFSInode::create_child(StringView, mode_t, dev_t, UserID, GroupID) -{ - return EROFS; -} - -ErrorOr DevLoopFSInode::remove_child(StringView) -{ - return EROFS; -} - -ErrorOr DevLoopFSInode::replace_child(StringView, Inode&) -{ - return EROFS; -} - -ErrorOr DevLoopFSInode::chmod(mode_t) -{ - return EROFS; -} - -ErrorOr DevLoopFSInode::chown(UserID, GroupID) -{ - return EROFS; -} - -} diff --git a/Kernel/FileSystem/DevLoopFS/Inode.h b/Kernel/FileSystem/DevLoopFS/Inode.h deleted file mode 100644 index 30864c38103..00000000000 --- a/Kernel/FileSystem/DevLoopFS/Inode.h +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright (c) 2024, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include - -namespace Kernel { - -class DevLoopFSInode final : public Inode { - friend class DevLoopFS; - -public: - virtual ~DevLoopFSInode() override; - - DevLoopFS& fs() { return static_cast(Inode::fs()); } - DevLoopFS const& fs() const { return static_cast(Inode::fs()); } - -private: - DevLoopFSInode(DevLoopFS&, InodeIndex, LoopDevice&); - - // NOTE: This constructor is used for the root inode only. - DevLoopFSInode(DevLoopFS&); - - // ^Inode - virtual ErrorOr read_bytes_locked(off_t, size_t, UserOrKernelBuffer& buffer, OpenFileDescription*) const override; - virtual InodeMetadata metadata() const override; - virtual ErrorOr traverse_as_directory(Function(FileSystem::DirectoryEntryView const&)>) const override; - virtual ErrorOr> lookup(StringView name) override; - virtual ErrorOr flush_metadata() override; - virtual ErrorOr write_bytes_locked(off_t, size_t, UserOrKernelBuffer const& buffer, OpenFileDescription*) override; - virtual ErrorOr> create_child(StringView name, mode_t, dev_t, UserID, GroupID) override; - virtual ErrorOr add_child(Inode&, StringView name, mode_t) override; - virtual ErrorOr remove_child(StringView name) override; - virtual ErrorOr replace_child(StringView name, Inode& child) override; - virtual ErrorOr chmod(mode_t) override; - virtual ErrorOr chown(UserID, GroupID) override; - - LockWeakPtr m_loop_device; - InodeMetadata m_metadata; -}; - -} diff --git a/Kernel/FileSystem/DevPtsFS/FileSystem.cpp b/Kernel/FileSystem/DevPtsFS/FileSystem.cpp deleted file mode 100644 index 499b3a3c4cc..00000000000 --- a/Kernel/FileSystem/DevPtsFS/FileSystem.cpp +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Copyright (c) 2019-2020, Sergey Bugaev - * Copyright (c) 2021, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include - -namespace Kernel { - -ErrorOr> DevPtsFS::try_create(ReadonlyBytes) -{ - return TRY(adopt_nonnull_ref_or_enomem(new (nothrow) DevPtsFS)); -} - -DevPtsFS::DevPtsFS() = default; -DevPtsFS::~DevPtsFS() = default; - -ErrorOr DevPtsFS::initialize() -{ - m_root_inode = TRY(adopt_nonnull_ref_or_enomem(new (nothrow) DevPtsFSInode(*this))); - m_root_inode->m_metadata.inode = { fsid(), 1 }; - m_root_inode->m_metadata.mode = 0040555; - m_root_inode->m_metadata.uid = 0; - m_root_inode->m_metadata.gid = 0; - m_root_inode->m_metadata.size = 0; - m_root_inode->m_metadata.mtime = TimeManagement::boot_time(); - return {}; -} - -static unsigned inode_index_to_pty_index(InodeIndex inode_index) -{ - VERIFY(inode_index > 1); - return inode_index.value() - 2; -} - -Inode& DevPtsFS::root_inode() -{ - return *m_root_inode; -} - -u8 DevPtsFS::internal_file_type_to_directory_entry_type(DirectoryEntryView const& entry) const -{ - return ram_backed_file_type_to_directory_entry_type(entry); -} - -ErrorOr> DevPtsFS::get_inode(InodeIdentifier inode_id) const -{ - if (inode_id.index() == 1) - return *m_root_inode; - - unsigned pty_index = inode_index_to_pty_index(inode_id.index()); - auto device = DeviceManagement::the().get_device(201, pty_index); - VERIFY(device); - - auto& pts_device = static_cast(*device); - auto inode = TRY(adopt_nonnull_ref_or_enomem(new (nothrow) DevPtsFSInode(const_cast(*this), inode_id.index(), pts_device))); - inode->m_metadata.inode = inode_id; - inode->m_metadata.size = 0; - inode->m_metadata.uid = pts_device.uid(); - inode->m_metadata.gid = pts_device.gid(); - inode->m_metadata.mode = 0020600; - inode->m_metadata.major_device = device->major(); - inode->m_metadata.minor_device = device->minor(); - inode->m_metadata.mtime = TimeManagement::boot_time(); - return inode; -} - -} diff --git a/Kernel/FileSystem/DevPtsFS/FileSystem.h b/Kernel/FileSystem/DevPtsFS/FileSystem.h deleted file mode 100644 index be2629606bf..00000000000 --- a/Kernel/FileSystem/DevPtsFS/FileSystem.h +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright (c) 2019-2020, Sergey Bugaev - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include - -namespace Kernel { - -class SlavePTY; -class DevPtsFSInode; - -class DevPtsFS final : public FileSystem { - friend class DevPtsFSInode; - -public: - virtual ~DevPtsFS() override; - static ErrorOr> try_create(ReadonlyBytes); - - virtual ErrorOr initialize() override; - virtual StringView class_name() const override { return "DevPtsFS"sv; } - - virtual Inode& root_inode() override; - -private: - virtual u8 internal_file_type_to_directory_entry_type(DirectoryEntryView const& entry) const override; - - DevPtsFS(); - ErrorOr> get_inode(InodeIdentifier) const; - - RefPtr m_root_inode; -}; - -} diff --git a/Kernel/FileSystem/DevPtsFS/Inode.cpp b/Kernel/FileSystem/DevPtsFS/Inode.cpp deleted file mode 100644 index 291f0f13067..00000000000 --- a/Kernel/FileSystem/DevPtsFS/Inode.cpp +++ /dev/null @@ -1,130 +0,0 @@ -/* - * Copyright (c) 2019-2020, Sergey Bugaev - * Copyright (c) 2021, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include - -namespace Kernel { - -static InodeIndex pty_index_to_inode_index(unsigned pty_index) -{ - return pty_index + 2; -} - -// NOTE: This constructor is used for the root inode only. -DevPtsFSInode::DevPtsFSInode(DevPtsFS& fs) - : Inode(fs, 1) -{ -} - -DevPtsFSInode::DevPtsFSInode(DevPtsFS& fs, InodeIndex index, SlavePTY& pty) - : Inode(fs, index) - , m_pty(pty) -{ -} - -DevPtsFSInode::~DevPtsFSInode() = default; - -ErrorOr DevPtsFSInode::read_bytes_locked(off_t, size_t, UserOrKernelBuffer&, OpenFileDescription*) const -{ - VERIFY_NOT_REACHED(); -} - -ErrorOr DevPtsFSInode::write_bytes_locked(off_t, size_t, UserOrKernelBuffer const&, OpenFileDescription*) -{ - VERIFY_NOT_REACHED(); -} - -InodeMetadata DevPtsFSInode::metadata() const -{ - if (auto pty = m_pty.strong_ref()) { - auto metadata = m_metadata; - metadata.mtime = pty->time_of_last_write(); - return metadata; - } - return m_metadata; -} - -ErrorOr DevPtsFSInode::traverse_as_directory(Function(FileSystem::DirectoryEntryView const&)> callback) const -{ - if (identifier().index() > 1) - return ENOTDIR; - - TRY(callback({ "."sv, identifier(), to_underlying(RAMBackedFileType::Directory) })); - TRY(callback({ ".."sv, identifier(), to_underlying(RAMBackedFileType::Directory) })); - - return SlavePTY::all_instances().with([&](auto& list) -> ErrorOr { - StringBuilder builder; - for (SlavePTY& slave_pty : list) { - builder.clear(); - TRY(builder.try_appendff("{}", slave_pty.index())); - // NOTE: We represent directory entries with DT_CHR as all - // inodes in this filesystem are assumed to be char devices. - TRY(callback({ builder.string_view(), { fsid(), pty_index_to_inode_index(slave_pty.index()) }, to_underlying(RAMBackedFileType::Character) })); - } - return {}; - }); -} - -ErrorOr> DevPtsFSInode::lookup(StringView name) -{ - VERIFY(identifier().index() == 1); - - if (name == "." || name == "..") - return *this; - - auto pty_index = name.to_number(); - if (!pty_index.has_value()) - return ENOENT; - - return SlavePTY::all_instances().with([&](auto& list) -> ErrorOr> { - for (SlavePTY& slave_pty : list) { - if (slave_pty.index() != pty_index.value()) - continue; - return fs().get_inode({ fsid(), pty_index_to_inode_index(pty_index.value()) }); - } - return ENOENT; - }); -} - -ErrorOr DevPtsFSInode::flush_metadata() -{ - return {}; -} - -ErrorOr DevPtsFSInode::add_child(Inode&, StringView, mode_t) -{ - return EROFS; -} - -ErrorOr> DevPtsFSInode::create_child(StringView, mode_t, dev_t, UserID, GroupID) -{ - return EROFS; -} - -ErrorOr DevPtsFSInode::remove_child(StringView) -{ - return EROFS; -} - -ErrorOr DevPtsFSInode::replace_child(StringView, Inode&) -{ - return EROFS; -} - -ErrorOr DevPtsFSInode::chmod(mode_t) -{ - return EROFS; -} - -ErrorOr DevPtsFSInode::chown(UserID, GroupID) -{ - return EROFS; -} - -} diff --git a/Kernel/FileSystem/DevPtsFS/Inode.h b/Kernel/FileSystem/DevPtsFS/Inode.h deleted file mode 100644 index 8a78a6c3ced..00000000000 --- a/Kernel/FileSystem/DevPtsFS/Inode.h +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright (c) 2019-2020, Sergey Bugaev - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include - -namespace Kernel { - -class DevPtsFSInode final : public Inode { - friend class DevPtsFS; - -public: - virtual ~DevPtsFSInode() override; - - DevPtsFS& fs() { return static_cast(Inode::fs()); } - DevPtsFS const& fs() const { return static_cast(Inode::fs()); } - -private: - DevPtsFSInode(DevPtsFS&, InodeIndex, SlavePTY&); - - // NOTE: This constructor is used for the root inode only. - DevPtsFSInode(DevPtsFS&); - - // ^Inode - virtual ErrorOr read_bytes_locked(off_t, size_t, UserOrKernelBuffer& buffer, OpenFileDescription*) const override; - virtual InodeMetadata metadata() const override; - virtual ErrorOr traverse_as_directory(Function(FileSystem::DirectoryEntryView const&)>) const override; - virtual ErrorOr> lookup(StringView name) override; - virtual ErrorOr flush_metadata() override; - virtual ErrorOr write_bytes_locked(off_t, size_t, UserOrKernelBuffer const& buffer, OpenFileDescription*) override; - virtual ErrorOr> create_child(StringView name, mode_t, dev_t, UserID, GroupID) override; - virtual ErrorOr add_child(Inode&, StringView name, mode_t) override; - virtual ErrorOr remove_child(StringView name) override; - virtual ErrorOr replace_child(StringView name, Inode& child) override; - virtual ErrorOr chmod(mode_t) override; - virtual ErrorOr chown(UserID, GroupID) override; - - LockWeakPtr m_pty; - InodeMetadata m_metadata; -}; - -} diff --git a/Kernel/FileSystem/Ext2FS/Definitions.h b/Kernel/FileSystem/Ext2FS/Definitions.h deleted file mode 100644 index 8d18b673556..00000000000 --- a/Kernel/FileSystem/Ext2FS/Definitions.h +++ /dev/null @@ -1,735 +0,0 @@ -/* - * linux/include/linux/ext2_fs.h - * - * Copyright (C) 1992, 1993, 1994, 1995 - * Remy Card (card@masi.ibp.fr) - * Laboratoire MASI - Institut Blaise Pascal - * Universite Pierre et Marie Curie (Paris VI) - * - * from - * - * linux/include/linux/minix_fs.h - * - * Copyright (C) 1991, 1992 Linus Torvalds - */ - -#ifndef _LINUX_EXT2_FS_H -#define _LINUX_EXT2_FS_H - -static constexpr size_t max_block_size = 4096; - -#include "ext2_types.h" /* Changed from linux/types.h */ - -/* - * The second extended filesystem constants/structures - */ - -/* - * Define EXT2_PREALLOCATE to preallocate data blocks for expanding files - */ -#define EXT2_PREALLOCATE -#define EXT2_DEFAULT_PREALLOC_BLOCKS 8 - -/* - * The second extended file system version - */ -#define EXT2FS_DATE "95/08/09" -#define EXT2FS_VERSION "0.5b" - -/* - * Special inode numbers - */ -#define EXT2_BAD_INO 1 /* Bad blocks inode */ -#define EXT2_ROOT_INO 2 /* Root inode */ -#define EXT2_ACL_IDX_INO 3 /* ACL inode */ -#define EXT2_ACL_DATA_INO 4 /* ACL inode */ -#define EXT2_BOOT_LOADER_INO 5 /* Boot loader inode */ -#define EXT2_UNDEL_DIR_INO 6 /* Undelete directory inode */ -#define EXT2_RESIZE_INO 7 /* Reserved group descriptors inode */ -#define EXT2_JOURNAL_INO 8 /* Journal inode */ - -/* First non-reserved inode for old ext2 filesystems */ -#define EXT2_GOOD_OLD_FIRST_INO 11 - -/* - * The second extended file system magic number - */ -#define EXT2_SUPER_MAGIC 0xEF53 - -#ifdef __KERNEL__ -# define EXT2_SB(sb) (&((sb)->u.ext2_sb)) -#else -/* Assume that user mode programs are passing in an ext2fs superblock, not - * a kernel struct super_block. This will allow us to call the feature-test - * macros from user land. */ -# define EXT2_SB(sb) (sb) -#endif - -/* - * Maximal count of links to a file - */ -#define EXT2_LINK_MAX 65000 - -/* - * Macro-instructions used to manage several block sizes - */ -#define EXT2_MIN_BLOCK_LOG_SIZE 10 /* 1024 */ -#define EXT2_MAX_BLOCK_LOG_SIZE 16 /* 65536 */ -#define EXT2_MIN_BLOCK_SIZE (1 << EXT2_MIN_BLOCK_LOG_SIZE) -#define EXT2_MAX_BLOCK_SIZE (1 << EXT2_MAX_BLOCK_LOG_SIZE) -#ifdef __KERNEL__ -# define EXT2_BLOCK_SIZE(s) ((s)->s_blocksize) -# define EXT2_BLOCK_SIZE_BITS(s) ((s)->s_blocksize_bits) -# define EXT2_ADDR_PER_BLOCK_BITS(s) (EXT2_SB(s)->addr_per_block_bits) -# define EXT2_INODE_SIZE(s) (EXT2_SB(s)->s_inode_size) -# define EXT2_FIRST_INO(s) (EXT2_SB(s)->s_first_ino) -#else -# define EXT2_BLOCK_SIZE(s) (EXT2_MIN_BLOCK_SIZE << (s)->s_log_block_size) -# define EXT2_BLOCK_SIZE_BITS(s) ((s)->s_log_block_size + 10) -# define EXT2_INODE_SIZE(s) (((s)->s_rev_level == EXT2_GOOD_OLD_REV) ? EXT2_GOOD_OLD_INODE_SIZE : (s)->s_inode_size) -# define EXT2_FIRST_INO(s) (((s)->s_rev_level == EXT2_GOOD_OLD_REV) ? EXT2_GOOD_OLD_FIRST_INO : (s)->s_first_ino) -#endif -#define EXT2_ADDR_PER_BLOCK(s) (EXT2_BLOCK_SIZE(s) / sizeof(__u32)) - -/* - * Macro-instructions used to manage fragments - */ -#define EXT2_MIN_FRAG_SIZE EXT2_MIN_BLOCK_SIZE -#define EXT2_MAX_FRAG_SIZE EXT2_MAX_BLOCK_SIZE -#define EXT2_MIN_FRAG_LOG_SIZE EXT2_MIN_BLOCK_LOG_SIZE -#ifdef __KERNEL__ -# define EXT2_FRAG_SIZE(s) (EXT2_SB(s)->s_frag_size) -# define EXT2_FRAGS_PER_BLOCK(s) (EXT2_SB(s)->s_frags_per_block) -#else -# define EXT2_FRAG_SIZE(s) (EXT2_MIN_FRAG_SIZE << (s)->s_log_frag_size) -# define EXT2_FRAGS_PER_BLOCK(s) (EXT2_BLOCK_SIZE(s) / EXT2_FRAG_SIZE(s)) -#endif - -/* - * ACL structures - */ -struct ext2_acl_header /* Header of Access Control Lists */ -{ - __u32 aclh_size; - __u32 aclh_file_count; - __u32 aclh_acle_count; - __u32 aclh_first_acle; -}; - -struct ext2_acl_entry /* Access Control List Entry */ -{ - __u32 acle_size; - __u16 acle_perms; /* Access permissions */ - __u16 acle_type; /* Type of entry */ - __u16 acle_tag; /* User or group identity */ - __u16 acle_pad1; - __u32 acle_next; /* Pointer on next entry for the */ - /* same inode or on next free entry */ -}; - -/* - * Structure of a blocks group descriptor - */ -struct ext2_group_desc { - __u32 bg_block_bitmap; /* Blocks bitmap block */ - __u32 bg_inode_bitmap; /* Inodes bitmap block */ - __u32 bg_inode_table; /* Inodes table block */ - __u16 bg_free_blocks_count; /* Free blocks count */ - __u16 bg_free_inodes_count; /* Free inodes count */ - __u16 bg_used_dirs_count; /* Directories count */ - __u16 bg_flags; - __u32 bg_reserved[2]; - __u16 bg_itable_unused; /* Unused inodes count */ - __u16 bg_checksum; /* crc16(s_uuid+group_num+group_desc)*/ -}; - -struct ext4_group_desc { - __u32 bg_block_bitmap; /* Blocks bitmap block */ - __u32 bg_inode_bitmap; /* Inodes bitmap block */ - __u32 bg_inode_table; /* Inodes table block */ - __u16 bg_free_blocks_count; /* Free blocks count */ - __u16 bg_free_inodes_count; /* Free inodes count */ - __u16 bg_used_dirs_count; /* Directories count */ - __u16 bg_flags; - __u32 bg_reserved[2]; - __u16 bg_itable_unused; /* Unused inodes count */ - __u16 bg_checksum; /* crc16(s_uuid+group_num+group_desc)*/ - __u32 bg_block_bitmap_hi; /* Blocks bitmap block MSB */ - __u32 bg_inode_bitmap_hi; /* Inodes bitmap block MSB */ - __u32 bg_inode_table_hi; /* Inodes table block MSB */ - __u16 bg_free_blocks_count_hi; /* Free blocks count MSB */ - __u16 bg_free_inodes_count_hi; /* Free inodes count MSB */ - __u16 bg_used_dirs_count_hi; /* Directories count MSB */ - __u16 bg_pad; - __u32 bg_reserved2[3]; -}; - -#define EXT2_BG_INODE_UNINIT 0x0001 /* Inode table/bitmap not initialized */ -#define EXT2_BG_BLOCK_UNINIT 0x0002 /* Block bitmap not initialized */ -#define EXT2_BG_INODE_ZEROED 0x0004 /* On-disk itable initialized to zero */ - -/* - * Data structures used by the directory indexing feature - * - * Note: all of the multibyte integer fields are little endian. - */ - -/* - * Note: dx_root_info is laid out so that if it should somehow get - * overlaid by a dirent the two low bits of the hash version will be - * zero. Therefore, the hash version mod 4 should never be 0. - * Sincerely, the paranoia department. - */ -struct ext2_dx_root_info { - __u32 reserved_zero; - __u8 hash_version; /* 0 now, 1 at release */ - __u8 info_length; /* 8 */ - __u8 indirect_levels; - __u8 unused_flags; -}; - -#define EXT2_HASH_LEGACY 0 -#define EXT2_HASH_HALF_MD4 1 -#define EXT2_HASH_TEA 2 -#define EXT2_HASH_LEGACY_UNSIGNED 3 /* reserved for userspace lib */ -#define EXT2_HASH_HALF_MD4_UNSIGNED 4 /* reserved for userspace lib */ -#define EXT2_HASH_TEA_UNSIGNED 5 /* reserved for userspace lib */ - -#define EXT2_HASH_FLAG_INCOMPAT 0x1 - -struct ext2_dx_entry { - __u32 hash; - __u32 block; -}; - -struct ext2_dx_countlimit { - __u16 limit; - __u16 count; -}; - -/* - * Macro-instructions used to manage group descriptors - */ -#define EXT2_MIN_DESC_SIZE 32 -#define EXT2_MIN_DESC_SIZE_64BIT 64 -#define EXT2_MAX_DESC_SIZE EXT2_MIN_BLOCK_SIZE -#define EXT2_DESC_SIZE(s) \ - ((EXT2_SB(s)->s_feature_incompat & EXT4_FEATURE_INCOMPAT_64BIT) ? (s)->s_desc_size : EXT2_MIN_DESC_SIZE) - -#define EXT2_BLOCKS_PER_GROUP(s) (EXT2_SB(s)->s_blocks_per_group) -#define EXT2_INODES_PER_GROUP(s) (EXT2_SB(s)->s_inodes_per_group) -#define EXT2_INODES_PER_BLOCK(s) (EXT2_BLOCK_SIZE(s) / EXT2_INODE_SIZE(s)) -/* limits imposed by 16-bit value gd_free_{blocks,inode}_count */ -#define EXT2_MAX_BLOCKS_PER_GROUP(s) ((1 << 16) - 8) -#define EXT2_MAX_INODES_PER_GROUP(s) ((1 << 16) - EXT2_INODES_PER_BLOCK(s)) -#ifdef __KERNEL__ -# define EXT2_DESC_PER_BLOCK(s) (EXT2_SB(s)->s_desc_per_block) -# define EXT2_DESC_PER_BLOCK_BITS(s) (EXT2_SB(s)->s_desc_per_block_bits) -#else -# define EXT2_DESC_PER_BLOCK(s) (EXT2_BLOCK_SIZE(s) / EXT2_DESC_SIZE(s)) -#endif - -/* - * Constants relative to the data blocks - */ -#define EXT2_NDIR_BLOCKS 12 -#define EXT2_IND_BLOCK EXT2_NDIR_BLOCKS -#define EXT2_DIND_BLOCK (EXT2_IND_BLOCK + 1) -#define EXT2_TIND_BLOCK (EXT2_DIND_BLOCK + 1) -#define EXT2_N_BLOCKS (EXT2_TIND_BLOCK + 1) - -/* - * Inode flags - */ -#define EXT2_SECRM_FL 0x00000001 /* Secure deletion */ -#define EXT2_UNRM_FL 0x00000002 /* Undelete */ -#define EXT2_COMPR_FL 0x00000004 /* Compress file */ -#define EXT2_SYNC_FL 0x00000008 /* Synchronous updates */ -#define EXT2_IMMUTABLE_FL 0x00000010 /* Immutable file */ -#define EXT2_APPEND_FL 0x00000020 /* writes to file may only append */ -#define EXT2_NODUMP_FL 0x00000040 /* do not dump file */ -#define EXT2_NOATIME_FL 0x00000080 /* do not update atime */ -/* Reserved for compression usage... */ -#define EXT2_DIRTY_FL 0x00000100 -#define EXT2_COMPRBLK_FL 0x00000200 /* One or more compressed clusters */ -#define EXT2_NOCOMPR_FL 0x00000400 /* Access raw compressed data */ -#define EXT2_ECOMPR_FL 0x00000800 /* Compression error */ -/* End compression flags --- maybe not all used */ -#define EXT2_BTREE_FL 0x00001000 /* btree format dir */ -#define EXT2_INDEX_FL 0x00001000 /* hash-indexed directory */ -#define EXT2_IMAGIC_FL 0x00002000 -#define EXT3_JOURNAL_DATA_FL 0x00004000 /* file data should be journaled */ -#define EXT2_NOTAIL_FL 0x00008000 /* file tail should not be merged */ -#define EXT2_DIRSYNC_FL 0x00010000 /* Synchronous directory modifications */ -#define EXT2_TOPDIR_FL 0x00020000 /* Top of directory hierarchies*/ -#define EXT4_HUGE_FILE_FL 0x00040000 /* Set to each huge file */ -#define EXT4_EXTENTS_FL 0x00080000 /* Inode uses extents */ -#define EXT2_RESERVED_FL 0x80000000 /* reserved for ext2 lib */ - -#define EXT2_FL_USER_VISIBLE 0x000BDFFF /* User visible flags */ -#define EXT2_FL_USER_MODIFIABLE 0x000080FF /* User modifiable flags */ - -/* - * ioctl commands - */ - -/* Used for online resize */ -struct ext2_new_group_input { - __u32 group; /* Group number for this data */ - __u32 block_bitmap; /* Absolute block number of block bitmap */ - __u32 inode_bitmap; /* Absolute block number of inode bitmap */ - __u32 inode_table; /* Absolute block number of inode table start */ - __u32 blocks_count; /* Total number of blocks in this group */ - __u16 reserved_blocks; /* Number of reserved blocks in this group */ - __u16 unused; /* Number of reserved GDT blocks in group */ -}; - -struct ext4_new_group_input { - __u32 group; /* Group number for this data */ - __u64 block_bitmap; /* Absolute block number of block bitmap */ - __u64 inode_bitmap; /* Absolute block number of inode bitmap */ - __u64 inode_table; /* Absolute block number of inode table start */ - __u32 blocks_count; /* Total number of blocks in this group */ - __u16 reserved_blocks; /* Number of reserved blocks in this group */ - __u16 unused; -}; - -#ifdef __GNU__ /* Needed for the Hurd */ -# define _IOT_ext2_new_group_input _IOT(_IOTS(__u32), 5, _IOTS(__u16), 2, 0, 0) -#endif - -#define EXT2_IOC_GETFLAGS _IOR('f', 1, long) -#define EXT2_IOC_SETFLAGS _IOW('f', 2, long) -#define EXT2_IOC_GETVERSION _IOR('v', 1, long) -#define EXT2_IOC_SETVERSION _IOW('v', 2, long) -#define EXT2_IOC_GETVERSION_NEW _IOR('f', 3, long) -#define EXT2_IOC_SETVERSION_NEW _IOW('f', 4, long) -#define EXT2_IOC_GROUP_EXTEND _IOW('f', 7, unsigned long) -#define EXT2_IOC_GROUP_ADD _IOW('f', 8, struct ext2_new_group_input) -#define EXT4_IOC_GROUP_ADD _IOW('f', 8, struct ext4_new_group_input) - -/* - * Structure of an inode on the disk - */ -struct ext2_inode { - __u16 i_mode; /* File mode */ - __u16 i_uid; /* Low 16 bits of Owner Uid */ - __u32 i_size; /* Size in bytes */ - __u32 i_atime; /* Access time */ - __u32 i_ctime; /* Inode change time */ - __u32 i_mtime; /* Modification time */ - __u32 i_dtime; /* Deletion Time */ - __u16 i_gid; /* Low 16 bits of Group Id */ - __u16 i_links_count; /* Links count */ - __u32 i_blocks; /* Blocks count */ - __u32 i_flags; /* File flags */ - union { - struct { - __u32 l_i_version; /* was l_i_reserved1 */ - } linux1; - struct { - __u32 h_i_translator; - } hurd1; - } osd1; /* OS dependent 1 */ - __u32 i_block[EXT2_N_BLOCKS]; /* Pointers to blocks */ - __u32 i_generation; /* File version (for NFS) */ - __u32 i_file_acl; /* File ACL */ - __u32 i_dir_acl; /* Directory ACL */ - __u32 i_faddr; /* Fragment address */ - union { - struct { - __u16 l_i_blocks_hi; - __u16 l_i_file_acl_high; - __u16 l_i_uid_high; /* these 2 fields */ - __u16 l_i_gid_high; /* were reserved2[0] */ - __u32 l_i_reserved2; - } linux2; - struct { - __u8 h_i_frag; /* Fragment number */ - __u8 h_i_fsize; /* Fragment size */ - __u16 h_i_mode_high; - __u16 h_i_uid_high; - __u16 h_i_gid_high; - __u32 h_i_author; - } hurd2; - } osd2; /* OS dependent 2 */ -}; - -/* - * Permanent part of an large inode on the disk - */ -struct ext2_inode_large { - __u16 i_mode; /* File mode */ - __u16 i_uid; /* Low 16 bits of Owner Uid */ - __u32 i_size; /* Size in bytes */ - __u32 i_atime; /* Access time */ - __u32 i_ctime; /* Inode Change time */ - __u32 i_mtime; /* Modification time */ - __u32 i_dtime; /* Deletion Time */ - __u16 i_gid; /* Low 16 bits of Group Id */ - __u16 i_links_count; /* Links count */ - __u32 i_blocks; /* Blocks count */ - __u32 i_flags; /* File flags */ - union { - struct { - __u32 l_i_version; /* was l_i_reserved1 */ - } linux1; - struct { - __u32 h_i_translator; - } hurd1; - } osd1; /* OS dependent 1 */ - __u32 i_block[EXT2_N_BLOCKS]; /* Pointers to blocks */ - __u32 i_generation; /* File version (for NFS) */ - __u32 i_file_acl; /* File ACL */ - __u32 i_dir_acl; /* Directory ACL */ - __u32 i_faddr; /* Fragment address */ - union { - struct { - __u16 l_i_blocks_hi; - __u16 l_i_file_acl_high; - __u16 l_i_uid_high; /* these 2 fields */ - __u16 l_i_gid_high; /* were reserved2[0] */ - __u32 l_i_reserved2; - } linux2; - struct { - __u8 h_i_frag; /* Fragment number */ - __u8 h_i_fsize; /* Fragment size */ - __u16 h_i_mode_high; - __u16 h_i_uid_high; - __u16 h_i_gid_high; - __u32 h_i_author; - } hurd2; - } osd2; /* OS dependent 2 */ - __u16 i_extra_isize; - __u16 i_pad1; - __u32 i_ctime_extra; /* extra Change time (nsec << 2 | epoch) */ - __u32 i_mtime_extra; /* extra Modification time (nsec << 2 | epoch) */ - __u32 i_atime_extra; /* extra Access time (nsec << 2 | epoch) */ - __u32 i_crtime; /* File creation time */ - __u32 i_crtime_extra; /* extra File creation time (nsec << 2 | epoch)*/ - __u32 i_version_hi; /* high 32 bits for 64-bit version */ -}; - -#define i_size_high i_dir_acl - -#if defined(__KERNEL__) || defined(__linux__) -# define i_reserved1 osd1.linux1.l_i_reserved1 -# define i_frag osd2.linux2.l_i_frag -# define i_fsize osd2.linux2.l_i_fsize -# define i_uid_low i_uid -# define i_gid_low i_gid -# define i_uid_high osd2.linux2.l_i_uid_high -# define i_gid_high osd2.linux2.l_i_gid_high -# define i_reserved2 osd2.linux2.l_i_reserved2 -#else -# if defined(__GNU__) - -# define i_translator osd1.hurd1.h_i_translator -# define i_frag osd2.hurd2.h_i_frag; -# define i_fsize osd2.hurd2.h_i_fsize; -# define i_uid_high osd2.hurd2.h_i_uid_high -# define i_gid_high osd2.hurd2.h_i_gid_high -# define i_author osd2.hurd2.h_i_author - -# endif /* __GNU__ */ -#endif /* defined(__KERNEL__) || defined(__linux__) */ - -#define inode_uid(inode) ((inode).i_uid | (inode).osd2.linux2.l_i_uid_high << 16) -#define inode_gid(inode) ((inode).i_gid | (inode).osd2.linux2.l_i_gid_high << 16) -#define ext2fs_set_i_uid_high(inode, x) ((inode).osd2.linux2.l_i_uid_high = (x)) -#define ext2fs_set_i_gid_high(inode, x) ((inode).osd2.linux2.l_i_gid_high = (x)) - -/* - * File system states - */ -#define EXT2_VALID_FS 0x0001 /* Unmounted cleanly */ -#define EXT2_ERROR_FS 0x0002 /* Errors detected */ -#define EXT3_ORPHAN_FS 0x0004 /* Orphans being recovered */ - -/* - * Misc. filesystem flags - */ -#define EXT2_FLAGS_SIGNED_HASH 0x0001 /* Signed dirhash in use */ -#define EXT2_FLAGS_UNSIGNED_HASH 0x0002 /* Unsigned dirhash in use */ -#define EXT2_FLAGS_TEST_FILESYS 0x0004 /* OK for use on development code */ - -/* - * Mount flags - */ -#define EXT2_MOUNT_CHECK 0x0001 /* Do mount-time checks */ -#define EXT2_MOUNT_GRPID 0x0004 /* Create files with directory's group */ -#define EXT2_MOUNT_DEBUG 0x0008 /* Some debugging messages */ -#define EXT2_MOUNT_ERRORS_CONT 0x0010 /* Continue on errors */ -#define EXT2_MOUNT_ERRORS_RO 0x0020 /* Remount fs ro on errors */ -#define EXT2_MOUNT_ERRORS_PANIC 0x0040 /* Panic on errors */ -#define EXT2_MOUNT_MINIX_DF 0x0080 /* Mimics the Minix statfs */ -#define EXT2_MOUNT_NO_UID32 0x0200 /* Disable 32-bit UIDs */ - -#define clear_opt(o, opt) o &= ~EXT2_MOUNT_##opt -#define set_opt(o, opt) o |= EXT2_MOUNT_##opt -#define test_opt(sb, opt) (EXT2_SB(sb)->s_mount_opt & EXT2_MOUNT_##opt) -/* - * Maximal mount counts between two filesystem checks - */ -#define EXT2_DFL_MAX_MNT_COUNT 20 /* Allow 20 mounts */ -#define EXT2_DFL_CHECKINTERVAL 0 /* Don't use interval check */ - -/* - * Behavior when detecting errors - */ -#define EXT2_ERRORS_CONTINUE 1 /* Continue execution */ -#define EXT2_ERRORS_RO 2 /* Remount fs read-only */ -#define EXT2_ERRORS_PANIC 3 /* Panic */ -#define EXT2_ERRORS_DEFAULT EXT2_ERRORS_CONTINUE - -/* - * Structure of the super block - */ -struct ext2_super_block { - __u32 s_inodes_count; /* Inodes count */ - __u32 s_blocks_count; /* Blocks count */ - __u32 s_r_blocks_count; /* Reserved blocks count */ - __u32 s_free_blocks_count; /* Free blocks count */ - __u32 s_free_inodes_count; /* Free inodes count */ - __u32 s_first_data_block; /* First Data Block */ - __u32 s_log_block_size; /* Block size */ - __s32 s_log_frag_size; /* Fragment size */ - __u32 s_blocks_per_group; /* # Blocks per group */ - __u32 s_frags_per_group; /* # Fragments per group */ - __u32 s_inodes_per_group; /* # Inodes per group */ - __u32 s_mtime; /* Mount time */ - __u32 s_wtime; /* Write time */ - __u16 s_mnt_count; /* Mount count */ - __s16 s_max_mnt_count; /* Maximal mount count */ - __u16 s_magic; /* Magic signature */ - __u16 s_state; /* File system state */ - __u16 s_errors; /* Behavior when detecting errors */ - __u16 s_minor_rev_level; /* minor revision level */ - __u32 s_lastcheck; /* time of last check */ - __u32 s_checkinterval; /* max. time between checks */ - __u32 s_creator_os; /* OS */ - __u32 s_rev_level; /* Revision level */ - __u16 s_def_resuid; /* Default uid for reserved blocks */ - __u16 s_def_resgid; /* Default gid for reserved blocks */ - /* - * These fields are for EXT2_DYNAMIC_REV superblocks only. - * - * Note: the difference between the compatible feature set and - * the incompatible feature set is that if there is a bit set - * in the incompatible feature set that the kernel doesn't - * know about, it should refuse to mount the filesystem. - * - * e2fsck's requirements are more strict; if it doesn't know - * about a feature in either the compatible or incompatible - * feature set, it must abort and not try to meddle with - * things it doesn't understand... - */ - __u32 s_first_ino; /* First non-reserved inode */ - __u16 s_inode_size; /* size of inode structure */ - __u16 s_block_group_nr; /* block group # of this superblock */ - __u32 s_feature_compat; /* compatible feature set */ - __u32 s_feature_incompat; /* incompatible feature set */ - __u32 s_feature_ro_compat; /* readonly-compatible feature set */ - __u8 s_uuid[16]; /* 128-bit uuid for volume */ - char s_volume_name[16]; /* volume name */ - char s_last_mounted[64]; /* directory where last mounted */ - __u32 s_algorithm_usage_bitmap; /* For compression */ - /* - * Performance hints. Directory preallocation should only - * happen if the EXT2_FEATURE_COMPAT_DIR_PREALLOC flag is on. - */ - __u8 s_prealloc_blocks; /* Nr of blocks to try to preallocate*/ - __u8 s_prealloc_dir_blocks; /* Nr to preallocate for dirs */ - __u16 s_reserved_gdt_blocks; /* Per group table for online growth */ - /* - * Journaling support valid if EXT2_FEATURE_COMPAT_HAS_JOURNAL set. - */ - __u8 s_journal_uuid[16]; /* uuid of journal superblock */ - __u32 s_journal_inum; /* inode number of journal file */ - __u32 s_journal_dev; /* device number of journal file */ - __u32 s_last_orphan; /* start of list of inodes to delete */ - __u32 s_hash_seed[4]; /* HTREE hash seed */ - __u8 s_def_hash_version; /* Default hash version to use */ - __u8 s_jnl_backup_type; /* Default type of journal backup */ - __u16 s_desc_size; /* Group desc. size: INCOMPAT_64BIT */ - __u32 s_default_mount_opts; - __u32 s_first_meta_bg; /* First metablock group */ - __u32 s_mkfs_time; /* When the filesystem was created */ - __u32 s_jnl_blocks[17]; /* Backup of the journal inode */ - __u32 s_blocks_count_hi; /* Blocks count high 32bits */ - __u32 s_r_blocks_count_hi; /* Reserved blocks count high 32 bits*/ - __u32 s_free_blocks_hi; /* Free blocks count */ - __u16 s_min_extra_isize; /* All inodes have at least # bytes */ - __u16 s_want_extra_isize; /* New inodes should reserve # bytes */ - __u32 s_flags; /* Miscellaneous flags */ - __u16 s_raid_stride; /* RAID stride */ - __u16 s_mmp_interval; /* # seconds to wait in MMP checking */ - __u64 s_mmp_block; /* Block for multi-mount protection */ - __u32 s_raid_stripe_width; /* blocks on all data disks (N*stride)*/ - __u8 s_log_groups_per_flex; /* FLEX_BG group size */ - __u8 s_reserved_char_pad; - __u16 s_reserved_pad; /* Padding to next 32bits */ - __u32 s_reserved[162]; /* Padding to the end of the block */ -}; - -/* - * Codes for operating systems - */ -#define EXT2_OS_LINUX 0 -#define EXT2_OS_HURD 1 -#define EXT2_OBSO_OS_MASIX 2 -#define EXT2_OS_FREEBSD 3 -#define EXT2_OS_LITES 4 - -/* - * Revision levels - */ -#define EXT2_GOOD_OLD_REV 0 /* The good old (original) format */ -#define EXT2_DYNAMIC_REV 1 /* V2 format w/ dynamic inode sizes */ - -#define EXT2_CURRENT_REV EXT2_GOOD_OLD_REV -#define EXT2_MAX_SUPP_REV EXT2_DYNAMIC_REV - -#define EXT2_GOOD_OLD_INODE_SIZE 128 - -/* - * Journal inode backup types - */ -#define EXT3_JNL_BACKUP_BLOCKS 1 - -/* - * Feature set definitions - */ - -#define EXT2_HAS_COMPAT_FEATURE(sb, mask) \ - (EXT2_SB(sb)->s_feature_compat & (mask)) -#define EXT2_HAS_RO_COMPAT_FEATURE(sb, mask) \ - (EXT2_SB(sb)->s_feature_ro_compat & (mask)) -#define EXT2_HAS_INCOMPAT_FEATURE(sb, mask) \ - (EXT2_SB(sb)->s_feature_incompat & (mask)) - -#define EXT2_FEATURE_COMPAT_DIR_PREALLOC 0x0001 -#define EXT2_FEATURE_COMPAT_IMAGIC_INODES 0x0002 -#define EXT3_FEATURE_COMPAT_HAS_JOURNAL 0x0004 -#define EXT2_FEATURE_COMPAT_EXT_ATTR 0x0008 -#define EXT2_FEATURE_COMPAT_RESIZE_INODE 0x0010 -#define EXT2_FEATURE_COMPAT_DIR_INDEX 0x0020 -#define EXT2_FEATURE_COMPAT_LAZY_BG 0x0040 - -#define EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER 0x0001 -#define EXT2_FEATURE_RO_COMPAT_LARGE_FILE 0x0002 -/* #define EXT2_FEATURE_RO_COMPAT_BTREE_DIR 0x0004 not used */ -#define EXT4_FEATURE_RO_COMPAT_HUGE_FILE 0x0008 -#define EXT4_FEATURE_RO_COMPAT_GDT_CSUM 0x0010 -#define EXT4_FEATURE_RO_COMPAT_DIR_NLINK 0x0020 -#define EXT4_FEATURE_RO_COMPAT_EXTRA_ISIZE 0x0040 - -#define EXT2_FEATURE_INCOMPAT_COMPRESSION 0x0001 -#define EXT2_FEATURE_INCOMPAT_FILETYPE 0x0002 -#define EXT3_FEATURE_INCOMPAT_RECOVER 0x0004 /* Needs recovery */ -#define EXT3_FEATURE_INCOMPAT_JOURNAL_DEV 0x0008 /* Journal device */ -#define EXT2_FEATURE_INCOMPAT_META_BG 0x0010 -#define EXT3_FEATURE_INCOMPAT_EXTENTS 0x0040 -#define EXT4_FEATURE_INCOMPAT_64BIT 0x0080 -#define EXT4_FEATURE_INCOMPAT_MMP 0x0100 -#define EXT4_FEATURE_INCOMPAT_FLEX_BG 0x0200 - -#define EXT2_FEATURE_COMPAT_SUPP 0 -#define EXT2_FEATURE_INCOMPAT_SUPP (EXT2_FEATURE_INCOMPAT_FILETYPE) -#define EXT2_FEATURE_RO_COMPAT_SUPP (EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER | EXT2_FEATURE_RO_COMPAT_LARGE_FILE | EXT4_FEATURE_RO_COMPAT_DIR_NLINK | EXT2_FEATURE_RO_COMPAT_BTREE_DIR) - -/* - * Default values for user and/or group using reserved blocks - */ -#define EXT2_DEF_RESUID 0 -#define EXT2_DEF_RESGID 0 - -/* - * Default mount options - */ -#define EXT2_DEFM_DEBUG 0x0001 -#define EXT2_DEFM_BSDGROUPS 0x0002 -#define EXT2_DEFM_XATTR_USER 0x0004 -#define EXT2_DEFM_ACL 0x0008 -#define EXT2_DEFM_UID16 0x0010 -#define EXT3_DEFM_JMODE 0x0060 -#define EXT3_DEFM_JMODE_DATA 0x0020 -#define EXT3_DEFM_JMODE_ORDERED 0x0040 -#define EXT3_DEFM_JMODE_WBACK 0x0060 - -/* - * Structure of a directory entry - */ -#define EXT2_NAME_LEN 255 - -struct ext2_dir_entry { - __u32 inode; /* Inode number */ - __u16 rec_len; /* Directory entry length */ - __u16 name_len; /* Name length */ - char name[EXT2_NAME_LEN]; /* Filename */ -}; - -/* - * The new version of the directory entry. Since EXT2 structures are - * stored in intel byte order, and the name_len field could never be - * bigger than 255 chars, it's safe to reclaim the extra byte for the - * file_type field. - */ -struct ext2_dir_entry_2 { - __u32 inode; /* Inode number */ - __u16 rec_len; /* Directory entry length */ - __u8 name_len; /* Name length */ - __u8 file_type; - char name[EXT2_NAME_LEN]; /* Filename */ -}; - -/* - * Ext2 directory file types. Only the low 3 bits are used. The - * other bits are reserved for now. - */ -#define EXT2_FT_UNKNOWN 0 -#define EXT2_FT_REG_FILE 1 -#define EXT2_FT_DIR 2 -#define EXT2_FT_CHRDEV 3 -#define EXT2_FT_BLKDEV 4 -#define EXT2_FT_FIFO 5 -#define EXT2_FT_SOCK 6 -#define EXT2_FT_SYMLINK 7 - -#define EXT2_FT_MAX 8 - -/* - * EXT2_DIR_PAD defines the directory entries boundaries - * - * NOTE: It must be a multiple of 4 - */ -#define EXT2_DIR_PAD 4 -#define EXT2_DIR_ROUND (EXT2_DIR_PAD - 1) -#define EXT2_DIR_REC_LEN(name_len) (((name_len) + 8 + EXT2_DIR_ROUND) & ~EXT2_DIR_ROUND) - -/* - * This structure will be used for multiple mount protection. It will be - * written into the block number saved in the s_mmp_block field in the - * superblock. - */ -#define EXT2_MMP_MAGIC 0x004D4D50 /* ASCII for MMP */ -#define EXT2_MMP_CLEAN 0xFF4D4D50 /* Value of mmp_seq for clean unmount */ -#define EXT2_MMP_FSCK_ON 0xE24D4D50 /* Value of mmp_seq when being fscked */ - -struct mmp_struct { - __u32 mmp_magic; - __u32 mmp_seq; - __u64 mmp_time; - char mmp_nodename[64]; - char mmp_bdevname[32]; - __u16 mmp_interval; - __u16 mmp_pad1; - __u32 mmp_pad2; -}; - -/* - * Interval in number of seconds to update the MMP sequence number. - */ -#define EXT2_MMP_DEF_INTERVAL 5 - -#endif /* _LINUX_EXT2_FS_H */ diff --git a/Kernel/FileSystem/Ext2FS/DirectoryEntry.h b/Kernel/FileSystem/Ext2FS/DirectoryEntry.h deleted file mode 100644 index cf522c4ce71..00000000000 --- a/Kernel/FileSystem/Ext2FS/DirectoryEntry.h +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright (c) 2022, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include - -namespace Kernel { - -struct Ext2FSDirectoryEntry { - NonnullOwnPtr name; - InodeIndex inode_index { 0 }; - u8 file_type { 0 }; - u16 record_length { 0 }; -}; - -} diff --git a/Kernel/FileSystem/Ext2FS/FileSystem.cpp b/Kernel/FileSystem/Ext2FS/FileSystem.cpp deleted file mode 100644 index 63dd488f981..00000000000 --- a/Kernel/FileSystem/Ext2FS/FileSystem.cpp +++ /dev/null @@ -1,782 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * Copyright (c) 2021, sin-ack - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include -#include - -namespace Kernel { - -ErrorOr> Ext2FS::try_create(OpenFileDescription& file_description, ReadonlyBytes) -{ - return TRY(adopt_nonnull_ref_or_enomem(new (nothrow) Ext2FS(file_description))); -} - -Ext2FS::Ext2FS(OpenFileDescription& file_description) - : BlockBasedFileSystem(file_description) -{ -} - -Ext2FS::~Ext2FS() = default; - -ErrorOr Ext2FS::flush_super_block() -{ - MutexLocker locker(m_lock); - auto super_block_buffer = UserOrKernelBuffer::for_kernel_buffer((u8*)&m_super_block); - auto const superblock_physical_block_count = (sizeof(ext2_super_block) / device_block_size()); - - // FIXME: We currently have no ability of writing within a device block, but the ability to do so would allow us to use device block sizes larger than 1024. - VERIFY((sizeof(ext2_super_block) % device_block_size()) == 0); - // First superblock is always at offset 1024 (physical block index 2). - TRY(raw_write_blocks(1024 / device_block_size(), superblock_physical_block_count, super_block_buffer)); - - auto is_sparse = has_flag(get_features_readonly(), FeaturesReadOnly::SparseSuperblock); - - for (auto group = 1u; group < m_block_group_count; ++group) { - auto first_block_in_group = first_block_of_group(group); - // Superblock copies with sparse layout are in group number 2 and powers of 3, 5, and 7. - if (!is_sparse || group == 2 || AK::is_power_of<3>(group - 1) || AK::is_power_of<5>(group - 1) || AK::is_power_of<7>(group - 1)) { - dbgln_if(EXT2_DEBUG, "Writing superblock backup to block group {} (block {})", group, first_block_in_group); - TRY(write_blocks(first_block_in_group, 1, super_block_buffer)); - } - } - - return {}; -} - -ext2_group_desc const& Ext2FS::group_descriptor(GroupIndex group_index) const -{ - // FIXME: Should this fail gracefully somehow? - VERIFY(group_index <= m_block_group_count); - VERIFY(group_index > 0); - return block_group_descriptors()[group_index.value() - 1]; -} - -bool Ext2FS::is_initialized_while_locked() -{ - VERIFY(m_lock.is_locked()); - return !m_root_inode.is_null(); -} - -ErrorOr Ext2FS::initialize_while_locked() -{ - VERIFY(m_lock.is_locked()); - VERIFY(!is_initialized_while_locked()); - - VERIFY((sizeof(ext2_super_block) % device_block_size()) == 0); - auto super_block_buffer = UserOrKernelBuffer::for_kernel_buffer((u8*)&m_super_block); - TRY(raw_read_blocks(1024 / device_block_size(), (sizeof(ext2_super_block) / device_block_size()), super_block_buffer)); - - auto const& super_block = this->super_block(); - if constexpr (EXT2_DEBUG) { - dmesgln("Ext2FS: super block magic: {:04x} (super block size: {})", super_block.s_magic, sizeof(ext2_super_block)); - } - if (super_block.s_magic != EXT2_SUPER_MAGIC) { - dmesgln("Ext2FS: Bad super block magic"); - return EINVAL; - } - - if (super_block.s_state == EXT2_ERROR_FS) - dmesgln("Ext2FS: Was not unmounted cleanly, file system may be erroneous!"); - - if constexpr (EXT2_DEBUG) { - dmesgln("Ext2FS: {} inodes, {} blocks", super_block.s_inodes_count, super_block.s_blocks_count); - dmesgln("Ext2FS: Block size: {}", EXT2_BLOCK_SIZE(&super_block)); - dmesgln("Ext2FS: First data block: {}", super_block.s_first_data_block); - dmesgln("Ext2FS: Inodes per block: {}", inodes_per_block()); - dmesgln("Ext2FS: Inodes per group: {}", inodes_per_group()); - dmesgln("Ext2FS: Free inodes: {}", super_block.s_free_inodes_count); - dmesgln("Ext2FS: Descriptors per block: {}", EXT2_DESC_PER_BLOCK(&super_block)); - dmesgln("Ext2FS: Descriptor size: {}", EXT2_DESC_SIZE(&super_block)); - } - - set_logical_block_size(EXT2_BLOCK_SIZE(&super_block)); - set_fragment_size(EXT2_FRAG_SIZE(&super_block)); - - // Note: This depends on the block size being available. - TRY(BlockBasedFileSystem::initialize_while_locked()); - - VERIFY(logical_block_size() <= (int)max_block_size); - - m_block_group_count = ceil_div(super_block.s_blocks_count, super_block.s_blocks_per_group); - - if (m_block_group_count == 0) { - dmesgln("Ext2FS: no block groups :("); - return EINVAL; - } - - auto blocks_to_read = ceil_div(m_block_group_count * sizeof(ext2_group_desc), logical_block_size()); - BlockIndex first_block_of_bgdt = first_block_of_block_group_descriptors(); - m_cached_group_descriptor_table = TRY(KBuffer::try_create_with_size("Ext2FS: Block group descriptors"sv, logical_block_size() * blocks_to_read, Memory::Region::Access::ReadWrite)); - auto buffer = UserOrKernelBuffer::for_kernel_buffer(m_cached_group_descriptor_table->data()); - TRY(read_blocks(first_block_of_bgdt, blocks_to_read, buffer)); - - if constexpr (EXT2_DEBUG) { - for (unsigned i = 1; i <= m_block_group_count; ++i) { - auto const& group = group_descriptor(i); - dbgln("Ext2FS: group[{}] ( block_bitmap: {}, inode_bitmap: {}, inode_table: {} )", i, group.bg_block_bitmap, group.bg_inode_bitmap, group.bg_inode_table); - } - } - - m_root_inode = TRY(build_root_inode()); - - // Set filesystem to "error" state until we unmount cleanly. - dmesgln("Ext2FS: Mount successful, setting superblock to error state."); - m_super_block.s_state = EXT2_ERROR_FS; - TRY(flush_super_block()); - - return {}; -} - -Inode& Ext2FS::root_inode() -{ - return *m_root_inode; -} - -bool Ext2FS::find_block_containing_inode(InodeIndex inode, BlockIndex& block_index, unsigned& offset) const -{ - auto const& super_block = this->super_block(); - - if (inode != EXT2_ROOT_INO && inode < EXT2_FIRST_INO(&super_block)) - return false; - - if (inode > super_block.s_inodes_count) - return false; - - auto const& bgd = group_descriptor(group_index_from_inode(inode)); - - u64 full_offset = ((inode.value() - 1) % inodes_per_group()) * inode_size(); - block_index = bgd.bg_inode_table + (full_offset >> EXT2_BLOCK_SIZE_BITS(&super_block)); - offset = full_offset & (logical_block_size() - 1); - - return true; -} - -Ext2FS::BlockListShape Ext2FS::compute_block_list_shape(unsigned blocks) const -{ - BlockListShape shape; - unsigned const entries_per_block = EXT2_ADDR_PER_BLOCK(&super_block()); - unsigned blocks_remaining = blocks; - - shape.direct_blocks = min((unsigned)EXT2_NDIR_BLOCKS, blocks_remaining); - blocks_remaining -= shape.direct_blocks; - if (!blocks_remaining) - return shape; - - shape.indirect_blocks = min(blocks_remaining, entries_per_block); - shape.meta_blocks += 1; - blocks_remaining -= shape.indirect_blocks; - if (!blocks_remaining) - return shape; - - shape.doubly_indirect_blocks = min(blocks_remaining, entries_per_block * entries_per_block); - shape.meta_blocks += 1; - shape.meta_blocks += ceil_div(shape.doubly_indirect_blocks, entries_per_block); - blocks_remaining -= shape.doubly_indirect_blocks; - if (!blocks_remaining) - return shape; - - shape.triply_indirect_blocks = min(blocks_remaining, entries_per_block * entries_per_block * entries_per_block); - shape.meta_blocks += 1; - shape.meta_blocks += ceil_div(shape.triply_indirect_blocks, entries_per_block * entries_per_block); - shape.meta_blocks += ceil_div(shape.triply_indirect_blocks, entries_per_block); - blocks_remaining -= shape.triply_indirect_blocks; - VERIFY(blocks_remaining == 0); - return shape; -} - -u8 Ext2FS::internal_file_type_to_directory_entry_type(DirectoryEntryView const& entry) const -{ - switch (entry.file_type) { - case EXT2_FT_REG_FILE: - return DT_REG; - case EXT2_FT_DIR: - return DT_DIR; - case EXT2_FT_CHRDEV: - return DT_CHR; - case EXT2_FT_BLKDEV: - return DT_BLK; - case EXT2_FT_FIFO: - return DT_FIFO; - case EXT2_FT_SOCK: - return DT_SOCK; - case EXT2_FT_SYMLINK: - return DT_LNK; - default: - return DT_UNKNOWN; - } -} - -Ext2FS::FeaturesOptional Ext2FS::get_features_optional() const -{ - if (m_super_block.s_rev_level > 0) - return static_cast(m_super_block.s_feature_compat); - return Ext2FS::FeaturesOptional::None; -} - -Ext2FS::FeaturesReadOnly Ext2FS::get_features_readonly() const -{ - if (m_super_block.s_rev_level > 0) - return static_cast(m_super_block.s_feature_ro_compat); - return Ext2FS::FeaturesReadOnly::None; -} - -u64 Ext2FS::inodes_per_block() const -{ - return EXT2_INODES_PER_BLOCK(&super_block()); -} - -u64 Ext2FS::inodes_per_group() const -{ - return EXT2_INODES_PER_GROUP(&super_block()); -} - -u64 Ext2FS::inode_size() const -{ - return EXT2_INODE_SIZE(&super_block()); -} -u64 Ext2FS::blocks_per_group() const -{ - return EXT2_BLOCKS_PER_GROUP(&super_block()); -} - -ErrorOr Ext2FS::write_ext2_inode(InodeIndex inode, ext2_inode const& e2inode) -{ - BlockIndex block_index; - unsigned offset; - if (!find_block_containing_inode(inode, block_index, offset)) - return EINVAL; - auto buffer = UserOrKernelBuffer::for_kernel_buffer(const_cast((u8 const*)&e2inode)); - return write_block(block_index, buffer, inode_size(), offset); -} - -auto Ext2FS::allocate_blocks(GroupIndex preferred_group_index, size_t count) -> ErrorOr> -{ - dbgln_if(EXT2_DEBUG, "Ext2FS: allocate_blocks(preferred group: {}, count {})", preferred_group_index, count); - if (count == 0) - return Vector {}; - - Vector blocks; - TRY(blocks.try_ensure_capacity(count)); - - MutexLocker locker(m_lock); - auto group_index = preferred_group_index; - - if (!group_descriptor(preferred_group_index).bg_free_blocks_count) { - group_index = 1; - } - - while (blocks.size() < count) { - bool found_a_group = false; - if (group_descriptor(group_index).bg_free_blocks_count) { - found_a_group = true; - } else { - if (group_index == preferred_group_index) - group_index = 1; - for (; group_index <= m_block_group_count; group_index = GroupIndex { group_index.value() + 1 }) { - if (group_descriptor(group_index).bg_free_blocks_count) { - found_a_group = true; - break; - } - } - } - - VERIFY(found_a_group); - auto const& bgd = group_descriptor(group_index); - - auto* cached_bitmap = TRY(get_bitmap_block(bgd.bg_block_bitmap)); - - int blocks_in_group = min(blocks_per_group(), super_block().s_blocks_count); - auto block_bitmap = cached_bitmap->bitmap(blocks_in_group); - - BlockIndex first_block_in_group = first_block_of_group(group_index); - size_t free_region_size = 0; - auto first_unset_bit_index = block_bitmap.find_longest_range_of_unset_bits(count - blocks.size(), free_region_size); - VERIFY(first_unset_bit_index.has_value()); - dbgln_if(EXT2_DEBUG, "Ext2FS: allocating free region of size: {} [{}]", free_region_size, group_index); - for (size_t i = 0; i < free_region_size; ++i) { - BlockIndex block_index = (first_unset_bit_index.value() + i) + first_block_in_group.value(); - TRY(set_block_allocation_state(block_index, true)); - blocks.unchecked_append(block_index); - dbgln_if(EXT2_DEBUG, " allocated > {}", block_index); - } - } - - VERIFY(blocks.size() == count); - return blocks; -} - -ErrorOr Ext2FS::allocate_inode(GroupIndex preferred_group) -{ - dbgln_if(EXT2_DEBUG, "Ext2FS: allocate_inode(preferred_group: {})", preferred_group); - MutexLocker locker(m_lock); - - // FIXME: We shouldn't refuse to allocate an inode if there is no group that can house the whole thing. - // In those cases we should just spread it across multiple groups. - auto is_suitable_group = [this](auto group_index) { - auto& bgd = group_descriptor(group_index); - return bgd.bg_free_inodes_count && bgd.bg_free_blocks_count >= 1; - }; - - GroupIndex group_index; - if (preferred_group.value() && is_suitable_group(preferred_group)) { - group_index = preferred_group; - } else { - for (unsigned i = 1; i <= m_block_group_count; ++i) { - if (is_suitable_group(i)) { - group_index = i; - break; - } - } - } - - if (!group_index) { - dmesgln("Ext2FS: allocate_inode: no suitable group found for new inode"); - return ENOSPC; - } - - dbgln_if(EXT2_DEBUG, "Ext2FS: allocate_inode: found suitable group [{}] for new inode :^)", group_index); - - auto const& bgd = group_descriptor(group_index); - unsigned inodes_in_group = min(inodes_per_group(), super_block().s_inodes_count); - InodeIndex first_inode_in_group = (group_index.value() - 1) * inodes_per_group() + 1; - - auto* cached_bitmap = TRY(get_bitmap_block(bgd.bg_inode_bitmap)); - auto inode_bitmap = cached_bitmap->bitmap(inodes_in_group); - for (size_t i = 0; i < inode_bitmap.size(); ++i) { - if (inode_bitmap.get(i)) - continue; - inode_bitmap.set(i, true); - - auto inode_index = InodeIndex(first_inode_in_group.value() + i); - - cached_bitmap->dirty = true; - m_super_block.s_free_inodes_count--; - m_super_block_dirty = true; - const_cast(bgd).bg_free_inodes_count--; - m_block_group_descriptors_dirty = true; - - // In case the inode cache had this cached as "non-existent", uncache that info. - m_inode_cache.remove(inode_index.value()); - - return inode_index; - } - - dmesgln("Ext2FS: allocate_inode found no available inode, despite bgd claiming there are inodes :("); - return EIO; -} - -Ext2FS::GroupIndex Ext2FS::group_index_from_block_index(BlockIndex block_index) const -{ - if (!block_index) - return 0; - return (block_index.value() - first_block_index().value()) / blocks_per_group() + 1; -} - -Ext2FS::BlockIndex Ext2FS::first_block_of_group(GroupIndex group_index) const -{ - return (group_index.value() - 1) * blocks_per_group() + first_block_index().value(); -} - -Ext2FS::BlockIndex Ext2FS::first_block_of_block_group_descriptors() const -{ - return logical_block_size() == 1024 ? 2 : 1; -} - -auto Ext2FS::group_index_from_inode(InodeIndex inode) const -> GroupIndex -{ - if (!inode) - return 0; - return (inode.value() - 1) / inodes_per_group() + 1; -} - -ErrorOr Ext2FS::get_inode_allocation_state(InodeIndex index) const -{ - MutexLocker locker(m_lock); - if (index == 0) - return EINVAL; - auto group_index = group_index_from_inode(index); - auto const& bgd = group_descriptor(group_index); - unsigned index_in_group = index.value() - ((group_index.value() - 1) * inodes_per_group()); - unsigned bit_index = (index_in_group - 1) % inodes_per_group(); - - auto* cached_bitmap = TRY(const_cast(*this).get_bitmap_block(bgd.bg_inode_bitmap)); - return cached_bitmap->bitmap(inodes_per_group()).get(bit_index); -} - -ErrorOr Ext2FS::update_bitmap_block(BlockIndex bitmap_block, size_t bit_index, bool new_state, u32& super_block_counter, u16& group_descriptor_counter) -{ - auto* cached_bitmap = TRY(get_bitmap_block(bitmap_block)); - bool current_state = cached_bitmap->bitmap(blocks_per_group()).get(bit_index); - if (current_state == new_state) { - dbgln("Ext2FS: Bit {} in bitmap block {} had unexpected state {}", bit_index, bitmap_block, current_state); - return EIO; - } - cached_bitmap->bitmap(blocks_per_group()).set(bit_index, new_state); - cached_bitmap->dirty = true; - - if (new_state) { - --super_block_counter; - --group_descriptor_counter; - } else { - ++super_block_counter; - ++group_descriptor_counter; - } - - m_super_block_dirty = true; - m_block_group_descriptors_dirty = true; - return {}; -} - -ErrorOr Ext2FS::set_inode_allocation_state(InodeIndex inode_index, bool new_state) -{ - MutexLocker locker(m_lock); - auto group_index = group_index_from_inode(inode_index); - unsigned index_in_group = inode_index.value() - ((group_index.value() - 1) * inodes_per_group()); - unsigned bit_index = (index_in_group - 1) % inodes_per_group(); - - dbgln_if(EXT2_DEBUG, "Ext2FS: set_inode_allocation_state: Inode {} -> {}", inode_index, new_state); - auto& bgd = const_cast(group_descriptor(group_index)); - return update_bitmap_block(bgd.bg_inode_bitmap, bit_index, new_state, m_super_block.s_free_inodes_count, bgd.bg_free_inodes_count); -} - -Ext2FS::BlockIndex Ext2FS::first_block_index() const -{ - return logical_block_size() == 1024 ? 1 : 0; -} - -ErrorOr Ext2FS::get_bitmap_block(BlockIndex bitmap_block_index) -{ - for (auto& cached_bitmap : m_cached_bitmaps) { - if (cached_bitmap->bitmap_block_index == bitmap_block_index) - return cached_bitmap.ptr(); - } - - auto block = TRY(KBuffer::try_create_with_size("Ext2FS: Cached bitmap block"sv, logical_block_size(), Memory::Region::Access::ReadWrite)); - auto buffer = UserOrKernelBuffer::for_kernel_buffer(block->data()); - TRY(read_block(bitmap_block_index, &buffer, logical_block_size())); - auto new_bitmap = TRY(adopt_nonnull_own_or_enomem(new (nothrow) CachedBitmap(bitmap_block_index, move(block)))); - TRY(m_cached_bitmaps.try_append(move(new_bitmap))); - return m_cached_bitmaps.last().ptr(); -} - -ErrorOr Ext2FS::set_block_allocation_state(BlockIndex block_index, bool new_state) -{ - VERIFY(block_index != 0); - MutexLocker locker(m_lock); - - auto group_index = group_index_from_block_index(block_index); - unsigned index_in_group = (block_index.value() - first_block_index().value()) - ((group_index.value() - 1) * blocks_per_group()); - unsigned bit_index = index_in_group % blocks_per_group(); - auto& bgd = const_cast(group_descriptor(group_index)); - - dbgln_if(EXT2_DEBUG, "Ext2FS: Block {} state -> {} (in bitmap block {})", block_index, new_state, bgd.bg_block_bitmap); - return update_bitmap_block(bgd.bg_block_bitmap, bit_index, new_state, m_super_block.s_free_blocks_count, bgd.bg_free_blocks_count); -} - -ErrorOr> Ext2FS::create_directory(Ext2FSInode& parent_inode, StringView name, mode_t mode, UserID uid, GroupID gid) -{ - MutexLocker locker(m_lock); - VERIFY(is_directory(mode)); - - auto inode = TRY(create_inode(parent_inode, name, mode, 0, uid, gid)); - - dbgln_if(EXT2_DEBUG, "Ext2FS: create_directory: created new directory named '{} with inode {}", name, inode->index()); - - Vector entries; - auto current_directory_name = TRY(KString::try_create("."sv)); - TRY(entries.try_empend(move(current_directory_name), inode->index(), static_cast(EXT2_FT_DIR))); - auto parent_directory_name = TRY(KString::try_create(".."sv)); - TRY(entries.try_empend(move(parent_directory_name), parent_inode.index(), static_cast(EXT2_FT_DIR))); - - TRY(static_cast(*inode).write_directory(entries)); - TRY(parent_inode.increment_link_count()); - - auto& bgd = const_cast(group_descriptor(group_index_from_inode(inode->identifier().index()))); - ++bgd.bg_used_dirs_count; - m_block_group_descriptors_dirty = true; - - return inode; -} - -ErrorOr> Ext2FS::create_inode(Ext2FSInode& parent_inode, StringView name, mode_t mode, dev_t dev, UserID uid, GroupID gid) -{ - if (name.length() > EXT2_NAME_LEN) - return ENAMETOOLONG; - - if (parent_inode.m_raw_inode.i_links_count == 0) - return ENOENT; - - ext2_inode e2inode {}; - auto now = kgettimeofday().truncated_seconds_since_epoch(); - e2inode.i_mode = mode; - e2inode.i_uid = static_cast(uid.value()); - ext2fs_set_i_uid_high(e2inode, uid.value() >> 16); - e2inode.i_gid = static_cast(gid.value()); - ext2fs_set_i_gid_high(e2inode, gid.value() >> 16); - e2inode.i_size = 0; - e2inode.i_atime = now; - e2inode.i_ctime = now; - e2inode.i_mtime = now; - e2inode.i_dtime = 0; - e2inode.i_flags = 0; - - // For directories, add +1 link count for the "." entry in self. - e2inode.i_links_count = is_directory(mode); - - if (is_character_device(mode)) - e2inode.i_block[0] = dev; - else if (is_block_device(mode)) - e2inode.i_block[1] = dev; - - auto inode_id = TRY(allocate_inode()); - - dbgln_if(EXT2_DEBUG, "Ext2FS: writing initial metadata for inode {}", inode_id.value()); - TRY(write_ext2_inode(inode_id, e2inode)); - - auto new_inode = TRY(get_inode({ fsid(), inode_id })); - - dbgln_if(EXT2_DEBUG, "Ext2FS: Adding inode '{}' (mode {:o}) to parent directory {}", name, mode, parent_inode.index()); - TRY(parent_inode.add_child(*new_inode, name, mode)); - return new_inode; -} - -void Ext2FS::uncache_inode(InodeIndex index) -{ - MutexLocker locker(m_lock); - m_inode_cache.remove(index); -} - -unsigned Ext2FS::total_block_count() const -{ - MutexLocker locker(m_lock); - return super_block().s_blocks_count; -} - -unsigned Ext2FS::free_block_count() const -{ - MutexLocker locker(m_lock); - return super_block().s_free_blocks_count; -} - -unsigned Ext2FS::total_inode_count() const -{ - MutexLocker locker(m_lock); - return super_block().s_inodes_count; -} - -unsigned Ext2FS::free_inode_count() const -{ - MutexLocker locker(m_lock); - return super_block().s_free_inodes_count; -} - -ErrorOr Ext2FS::prepare_to_clear_last_mount(Inode& mount_guest_inode) -{ - MutexLocker locker(m_lock); - bool any_inode_busy = false; - for (auto& it : m_inode_cache) { - // We hold the last reference to the root inode, and the VFS Mount object holds the last reference to the mount_guest_inode, - // so they are allowed to have one more reference. - if ((it.value == m_root_inode || it.value->identifier() == mount_guest_inode.identifier()) && it.value->ref_count() > 2) { - dbgln_if(EXT2_DEBUG, "Ext2FS: Ignoring root or mount point inode's last reference"); - continue; - } - // The Inode::all_instances list always holds one reference to all inodes, which we disregard. - if (it.value->ref_count() > 1) { - dbgln_if(EXT2_DEBUG, "Ext2FS: Busy inode {} ({} refs)", it.value->index(), it.value->ref_count()); - any_inode_busy = true; - } - } - if (any_inode_busy) - return EBUSY; - - m_inode_cache.clear(); - m_root_inode = nullptr; - - // Mark filesystem as valid before unmount. - dmesgln("Ext2FS: Clean unmount, setting superblock to valid state"); - m_super_block.s_state = EXT2_VALID_FS; - TRY(flush_super_block()); - BlockBasedFileSystem::remove_disk_cache_before_last_unmount(); - - return {}; -} - -ErrorOr Ext2FS::free_inode(Ext2FSInode& inode) -{ - MutexLocker locker(m_lock); - VERIFY(inode.m_raw_inode.i_links_count == 0); - dbgln_if(EXT2_DEBUG, "Ext2FS[{}]::free_inode(): Inode {} has no more links, time to delete!", fsid(), inode.index()); - - // Mark all blocks used by this inode as free. - { - auto blocks = TRY(inode.compute_block_list_with_meta_blocks()); - for (auto block_index : blocks) { - VERIFY(block_index <= super_block().s_blocks_count); - if (block_index.value()) - TRY(set_block_allocation_state(block_index, false)); - } - } - - // If the inode being freed is a directory, update block group directory counter. - if (inode.is_directory()) { - auto& bgd = const_cast(group_descriptor(group_index_from_inode(inode.index()))); - --bgd.bg_used_dirs_count; - dbgln_if(EXT2_DEBUG, "Ext2FS[{}]::free_inode(): Decremented bg_used_dirs_count to {} for inode {}", fsid(), bgd.bg_used_dirs_count, inode.index()); - m_block_group_descriptors_dirty = true; - } - - // NOTE: After this point, the inode metadata is wiped. - memset(&inode.m_raw_inode, 0, sizeof(ext2_inode)); - inode.m_raw_inode.i_dtime = kgettimeofday().truncated_seconds_since_epoch(); - TRY(write_ext2_inode(inode.index(), inode.m_raw_inode)); - - // Mark the inode as free. - TRY(set_inode_allocation_state(inode.index(), false)); - - return {}; -} - -void Ext2FS::flush_block_group_descriptor_table() -{ - MutexLocker locker(m_lock); - auto blocks_to_write = ceil_div(m_block_group_count * sizeof(ext2_group_desc), logical_block_size()); - auto first_block_of_bgdt = first_block_of_block_group_descriptors(); - auto buffer = UserOrKernelBuffer::for_kernel_buffer((u8*)block_group_descriptors()); - auto write_bgdt_to_block = [&](BlockIndex index) { - if (auto result = write_blocks(index, blocks_to_write, buffer); result.is_error()) - dbgln("Ext2FS[{}]::flush_block_group_descriptor_table(): Failed to write blocks: {}", fsid(), result.error()); - }; - - write_bgdt_to_block(first_block_of_bgdt); - - auto is_sparse = has_flag(get_features_readonly(), FeaturesReadOnly::SparseSuperblock); - - for (auto group = 1u; group < m_block_group_count; ++group) { - // First block is occupied by the super block - BlockIndex second_block_in_group = first_block_of_group(group).value() + 1; - // BGDT copies with sparse layout are in group number 2 and powers of 3, 5, and 7. - if (!is_sparse || group == 2 || AK::is_power_of<3>(group - 1) || AK::is_power_of<5>(group - 1) || AK::is_power_of<7>(group - 1)) { - dbgln_if(EXT2_DEBUG, "Writing block group descriptor table backup to block group {} (block {})", group, second_block_in_group); - write_bgdt_to_block(second_block_in_group); - } - } -} - -ErrorOr Ext2FS::flush_writes() -{ - { - MutexLocker locker(m_lock); - if (m_super_block_dirty) { - auto result = flush_super_block(); - if (result.is_error()) { - dbgln("Ext2FS[{}]::flush_writes(): Failed to write superblock: {}", fsid(), result.error()); - return result.release_error(); - } - m_super_block_dirty = false; - } - if (m_block_group_descriptors_dirty) { - flush_block_group_descriptor_table(); - m_block_group_descriptors_dirty = false; - } - for (auto& cached_bitmap : m_cached_bitmaps) { - if (cached_bitmap->dirty) { - auto buffer = UserOrKernelBuffer::for_kernel_buffer(cached_bitmap->buffer->data()); - if (auto result = write_block(cached_bitmap->bitmap_block_index, buffer, logical_block_size()); result.is_error()) { - dbgln("Ext2FS[{}]::flush_writes(): Failed to write blocks: {}", fsid(), result.error()); - } - cached_bitmap->dirty = false; - dbgln_if(EXT2_DEBUG, "Ext2FS[{}]::flush_writes(): Flushed bitmap block {}", fsid(), cached_bitmap->bitmap_block_index); - } - } - - // Uncache Inodes that are only kept alive by the index-to-inode lookup cache. - // We don't uncache Inodes that are being watched by at least one InodeWatcher. - - // FIXME: It would be better to keep a capped number of Inodes around. - // The problem is that they are quite heavy objects, and use a lot of heap memory - // for their (child name lookup) and (block list) caches. - - m_inode_cache.remove_all_matching([](InodeIndex, RefPtr const& cached_inode) { - // NOTE: If we're asked to look up an inode by number (via get_inode) and it turns out - // to not exist, we remember the fact that it doesn't exist by caching a nullptr. - // This seems like a reasonable time to uncache ideas about unknown inodes, so do that. - if (cached_inode == nullptr) - return true; - - return cached_inode->ref_count() == 1 && !cached_inode->has_watchers(); - }); - } - - auto result = BlockBasedFileSystem::flush_writes(); - if (result.is_error()) { - dbgln("Ext2FS[{}]::flush_writes(): Failed to flush writes: {}", BlockBasedFileSystem::fsid(), result.error()); - return result.release_error(); - } - - return {}; -} - -ErrorOr> Ext2FS::build_root_inode() const -{ - MutexLocker locker(m_lock); - BlockIndex block_index; - unsigned offset; - if (!find_block_containing_inode(EXT2_ROOT_INO, block_index, offset)) - return EINVAL; - - auto inode = TRY(adopt_nonnull_ref_or_enomem(new (nothrow) Ext2FSInode(const_cast(*this), EXT2_ROOT_INO))); - - auto buffer = UserOrKernelBuffer::for_kernel_buffer(reinterpret_cast(&inode->m_raw_inode)); - TRY(read_block(block_index, &buffer, sizeof(ext2_inode), offset)); - return inode; -} - -ErrorOr> Ext2FS::get_inode(InodeIdentifier inode) const -{ - MutexLocker locker(m_lock); - VERIFY(inode.fsid() == fsid()); - VERIFY(m_root_inode); - - if (inode.index() == EXT2_ROOT_INO) - return *m_root_inode; - - { - auto it = m_inode_cache.find(inode.index()); - if (it != m_inode_cache.end()) { - if (!it->value) - return ENOENT; - return NonnullRefPtr { *it->value }; - } - } - - auto inode_allocation_state = TRY(get_inode_allocation_state(inode.index())); - - if (!inode_allocation_state) { - TRY(m_inode_cache.try_set(inode.index(), nullptr)); - return ENOENT; - } - - BlockIndex block_index; - unsigned offset; - if (!find_block_containing_inode(inode.index(), block_index, offset)) - return EINVAL; - - auto new_inode = TRY(adopt_nonnull_ref_or_enomem(new (nothrow) Ext2FSInode(const_cast(*this), inode.index()))); - - auto buffer = UserOrKernelBuffer::for_kernel_buffer(reinterpret_cast(&new_inode->m_raw_inode)); - TRY(read_block(block_index, &buffer, sizeof(ext2_inode), offset)); - - TRY(m_inode_cache.try_set(inode.index(), new_inode)); - return new_inode; -} - -} diff --git a/Kernel/FileSystem/Ext2FS/FileSystem.h b/Kernel/FileSystem/Ext2FS/FileSystem.h deleted file mode 100644 index a71f1668ba7..00000000000 --- a/Kernel/FileSystem/Ext2FS/FileSystem.h +++ /dev/null @@ -1,145 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include -#include -#include - -namespace Kernel { - -class Ext2FSInode; - -class Ext2FS final : public BlockBasedFileSystem { - friend class Ext2FSInode; - -public: - // s_feature_compat - enum class FeaturesOptional : u32 { - None = 0, - ExtendedAttributes = EXT2_FEATURE_COMPAT_EXT_ATTR, - }; - AK_ENUM_BITWISE_FRIEND_OPERATORS(FeaturesOptional); - - // s_feature_ro_compat - enum class FeaturesReadOnly : u32 { - None = 0, - SparseSuperblock = EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER, - FileSize64bits = EXT2_FEATURE_RO_COMPAT_LARGE_FILE, - }; - AK_ENUM_BITWISE_FRIEND_OPERATORS(FeaturesReadOnly); - - static ErrorOr> try_create(OpenFileDescription&, ReadonlyBytes); - - virtual ~Ext2FS() override; - - virtual unsigned total_block_count() const override; - virtual unsigned free_block_count() const override; - virtual unsigned total_inode_count() const override; - virtual unsigned free_inode_count() const override; - - virtual bool supports_watchers() const override { return true; } - virtual bool supports_backing_loop_devices() const override { return true; } - - virtual u8 internal_file_type_to_directory_entry_type(DirectoryEntryView const& entry) const override; - - FeaturesOptional get_features_optional() const; - FeaturesReadOnly get_features_readonly() const; - - virtual StringView class_name() const override { return "Ext2FS"sv; } - virtual Inode& root_inode() override; - -private: - AK_TYPEDEF_DISTINCT_ORDERED_ID(unsigned, GroupIndex); - - explicit Ext2FS(OpenFileDescription&); - - ext2_super_block const& super_block() const { return m_super_block; } - ext2_group_desc const& group_descriptor(GroupIndex) const; - ext2_group_desc* block_group_descriptors() { return (ext2_group_desc*)m_cached_group_descriptor_table->data(); } - ext2_group_desc const* block_group_descriptors() const { return (ext2_group_desc const*)m_cached_group_descriptor_table->data(); } - void flush_block_group_descriptor_table(); - u64 inodes_per_block() const; - u64 inodes_per_group() const; - u64 blocks_per_group() const; - u64 inode_size() const; - - ErrorOr> build_root_inode() const; - - ErrorOr write_ext2_inode(InodeIndex, ext2_inode const&); - bool find_block_containing_inode(InodeIndex, BlockIndex& block_index, unsigned& offset) const; - - ErrorOr flush_super_block(); - - virtual ErrorOr initialize_while_locked() override; - virtual bool is_initialized_while_locked() override; - - virtual ErrorOr prepare_to_clear_last_mount(Inode& mount_guest_inode) override; - ErrorOr> get_inode(InodeIdentifier) const; - ErrorOr> create_inode(Ext2FSInode& parent_inode, StringView name, mode_t, dev_t, UserID, GroupID); - ErrorOr> create_directory(Ext2FSInode& parent_inode, StringView name, mode_t, UserID, GroupID); - virtual ErrorOr flush_writes() override; - - BlockIndex first_block_index() const; - BlockIndex first_block_of_block_group_descriptors() const; - ErrorOr allocate_inode(GroupIndex preferred_group = 0); - ErrorOr> allocate_blocks(GroupIndex preferred_group_index, size_t count); - GroupIndex group_index_from_inode(InodeIndex) const; - GroupIndex group_index_from_block_index(BlockIndex) const; - BlockIndex first_block_of_group(GroupIndex) const; - - ErrorOr get_inode_allocation_state(InodeIndex) const; - ErrorOr set_inode_allocation_state(InodeIndex, bool); - ErrorOr set_block_allocation_state(BlockIndex, bool); - - void uncache_inode(InodeIndex); - ErrorOr free_inode(Ext2FSInode&); - - struct BlockListShape { - unsigned direct_blocks { 0 }; - unsigned indirect_blocks { 0 }; - unsigned doubly_indirect_blocks { 0 }; - unsigned triply_indirect_blocks { 0 }; - unsigned meta_blocks { 0 }; - }; - - BlockListShape compute_block_list_shape(unsigned blocks) const; - - u64 m_block_group_count { 0 }; - - mutable ext2_super_block m_super_block {}; - mutable OwnPtr m_cached_group_descriptor_table; - - mutable HashMap> m_inode_cache; - - bool m_super_block_dirty { false }; - bool m_block_group_descriptors_dirty { false }; - - struct CachedBitmap { - CachedBitmap(BlockIndex bi, NonnullOwnPtr buf) - : bitmap_block_index(bi) - , buffer(move(buf)) - { - } - BlockIndex bitmap_block_index { 0 }; - bool dirty { false }; - NonnullOwnPtr buffer; - Bitmap bitmap(u32 blocks_per_group) { return Bitmap { buffer->data(), blocks_per_group }; } - }; - - ErrorOr get_bitmap_block(BlockIndex); - ErrorOr update_bitmap_block(BlockIndex bitmap_block, size_t bit_index, bool new_state, u32& super_block_counter, u16& group_descriptor_counter); - - Vector> m_cached_bitmaps; - RefPtr m_root_inode; -}; - -} diff --git a/Kernel/FileSystem/Ext2FS/Inode.cpp b/Kernel/FileSystem/Ext2FS/Inode.cpp deleted file mode 100644 index a2930d91270..00000000000 --- a/Kernel/FileSystem/Ext2FS/Inode.cpp +++ /dev/null @@ -1,1099 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * Copyright (c) 2021, sin-ack - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include -#include -#include - -namespace Kernel { - -static constexpr size_t max_inline_symlink_length = 60; - -static u8 to_ext2_file_type(mode_t mode) -{ - if (is_regular_file(mode)) - return EXT2_FT_REG_FILE; - if (is_directory(mode)) - return EXT2_FT_DIR; - if (is_character_device(mode)) - return EXT2_FT_CHRDEV; - if (is_block_device(mode)) - return EXT2_FT_BLKDEV; - if (is_fifo(mode)) - return EXT2_FT_FIFO; - if (is_socket(mode)) - return EXT2_FT_SOCK; - if (is_symlink(mode)) - return EXT2_FT_SYMLINK; - return EXT2_FT_UNKNOWN; -} - -ErrorOr Ext2FSInode::write_indirect_block(BlockBasedFileSystem::BlockIndex block, Span blocks_indices) -{ - auto const entries_per_block = EXT2_ADDR_PER_BLOCK(&fs().super_block()); - VERIFY(blocks_indices.size() <= entries_per_block); - - auto block_contents = TRY(ByteBuffer::create_zeroed(fs().logical_block_size())); - FixedMemoryStream stream { block_contents.bytes() }; - auto buffer = UserOrKernelBuffer::for_kernel_buffer(block_contents.data()); - - VERIFY(blocks_indices.size() <= EXT2_ADDR_PER_BLOCK(&fs().super_block())); - for (unsigned i = 0; i < blocks_indices.size(); ++i) - MUST(stream.write_value(blocks_indices[i].value())); - - return fs().write_block(block, buffer, block_contents.size()); -} - -ErrorOr Ext2FSInode::grow_doubly_indirect_block(BlockBasedFileSystem::BlockIndex block, size_t old_blocks_length, Span blocks_indices, Vector& new_meta_blocks, unsigned& meta_blocks) -{ - auto const entries_per_block = EXT2_ADDR_PER_BLOCK(&fs().super_block()); - auto const entries_per_doubly_indirect_block = entries_per_block * entries_per_block; - auto const old_indirect_blocks_length = ceil_div(old_blocks_length, entries_per_block); - auto const new_indirect_blocks_length = ceil_div(blocks_indices.size(), entries_per_block); - VERIFY(blocks_indices.size() > 0); - VERIFY(blocks_indices.size() > old_blocks_length); - VERIFY(blocks_indices.size() <= entries_per_doubly_indirect_block); - - auto block_contents = TRY(ByteBuffer::create_zeroed(fs().logical_block_size())); - auto* block_as_pointers = (unsigned*)block_contents.data(); - FixedMemoryStream stream { block_contents.bytes() }; - auto buffer = UserOrKernelBuffer::for_kernel_buffer(block_contents.data()); - - if (old_blocks_length > 0) { - TRY(fs().read_block(block, &buffer, fs().logical_block_size())); - } - - // Grow the doubly indirect block. - for (unsigned i = 0; i < old_indirect_blocks_length; i++) - MUST(stream.write_value(block_as_pointers[i])); - for (unsigned i = old_indirect_blocks_length; i < new_indirect_blocks_length; i++) { - auto new_block = new_meta_blocks.take_last().value(); - dbgln_if(EXT2_BLOCKLIST_DEBUG, "Ext2FSInode[{}]::grow_doubly_indirect_block(): Allocating indirect block {} at index {}", identifier(), new_block, i); - MUST(stream.write_value(new_block)); - meta_blocks++; - } - - // Write out the indirect blocks. - for (unsigned i = old_blocks_length / entries_per_block; i < new_indirect_blocks_length; i++) { - auto const offset_block = i * entries_per_block; - TRY(write_indirect_block(block_as_pointers[i], blocks_indices.slice(offset_block, min(blocks_indices.size() - offset_block, entries_per_block)))); - } - - // Write out the doubly indirect block. - return fs().write_block(block, buffer, block_contents.size()); -} - -ErrorOr Ext2FSInode::shrink_doubly_indirect_block(BlockBasedFileSystem::BlockIndex block, size_t old_blocks_length, size_t new_blocks_length, unsigned& meta_blocks) -{ - auto const entries_per_block = EXT2_ADDR_PER_BLOCK(&fs().super_block()); - auto const entries_per_doubly_indirect_block = entries_per_block * entries_per_block; - auto const old_indirect_blocks_length = ceil_div(old_blocks_length, entries_per_block); - auto const new_indirect_blocks_length = ceil_div(new_blocks_length, entries_per_block); - VERIFY(old_blocks_length > 0); - VERIFY(old_blocks_length >= new_blocks_length); - VERIFY(new_blocks_length <= entries_per_doubly_indirect_block); - - auto block_contents = TRY(ByteBuffer::create_uninitialized(fs().logical_block_size())); - auto* block_as_pointers = (unsigned*)block_contents.data(); - auto buffer = UserOrKernelBuffer::for_kernel_buffer(reinterpret_cast(block_as_pointers)); - TRY(fs().read_block(block, &buffer, fs().logical_block_size())); - - // Free the unused indirect blocks. - for (unsigned i = new_indirect_blocks_length; i < old_indirect_blocks_length; i++) { - dbgln_if(EXT2_BLOCKLIST_DEBUG, "Ext2FSInode[{}]::shrink_doubly_indirect_block(): Freeing indirect block {} at index {}", identifier(), block_as_pointers[i], i); - TRY(fs().set_block_allocation_state(block_as_pointers[i], false)); - meta_blocks--; - } - - // Free the doubly indirect block if no longer needed. - if (new_blocks_length == 0) { - dbgln_if(EXT2_BLOCKLIST_DEBUG, "Ext2FSInode[{}]::shrink_doubly_indirect_block(): Freeing doubly indirect block {}", identifier(), block); - TRY(fs().set_block_allocation_state(block, false)); - meta_blocks--; - } - - return {}; -} - -ErrorOr Ext2FSInode::grow_triply_indirect_block(BlockBasedFileSystem::BlockIndex block, size_t old_blocks_length, Span blocks_indices, Vector& new_meta_blocks, unsigned& meta_blocks) -{ - auto const entries_per_block = EXT2_ADDR_PER_BLOCK(&fs().super_block()); - auto const entries_per_doubly_indirect_block = entries_per_block * entries_per_block; - auto const entries_per_triply_indirect_block = entries_per_block * entries_per_block; - auto const old_doubly_indirect_blocks_length = ceil_div(old_blocks_length, entries_per_doubly_indirect_block); - auto const new_doubly_indirect_blocks_length = ceil_div(blocks_indices.size(), entries_per_doubly_indirect_block); - VERIFY(blocks_indices.size() > 0); - VERIFY(blocks_indices.size() > old_blocks_length); - VERIFY(blocks_indices.size() <= entries_per_triply_indirect_block); - - auto block_contents = TRY(ByteBuffer::create_zeroed(fs().logical_block_size())); - auto* block_as_pointers = (unsigned*)block_contents.data(); - FixedMemoryStream stream { block_contents.bytes() }; - auto buffer = UserOrKernelBuffer::for_kernel_buffer(block_contents.data()); - - if (old_blocks_length > 0) { - TRY(fs().read_block(block, &buffer, fs().logical_block_size())); - } - - // Grow the triply indirect block. - for (unsigned i = 0; i < old_doubly_indirect_blocks_length; i++) - MUST(stream.write_value(block_as_pointers[i])); - for (unsigned i = old_doubly_indirect_blocks_length; i < new_doubly_indirect_blocks_length; i++) { - auto new_block = new_meta_blocks.take_last().value(); - dbgln_if(EXT2_BLOCKLIST_DEBUG, "Ext2FSInode[{}]::grow_triply_indirect_block(): Allocating doubly indirect block {} at index {}", identifier(), new_block, i); - MUST(stream.write_value(new_block)); - meta_blocks++; - } - - // Write out the doubly indirect blocks. - for (unsigned i = old_blocks_length / entries_per_doubly_indirect_block; i < new_doubly_indirect_blocks_length; i++) { - auto const processed_blocks = i * entries_per_doubly_indirect_block; - auto const old_doubly_indirect_blocks_length = min(old_blocks_length > processed_blocks ? old_blocks_length - processed_blocks : 0, entries_per_doubly_indirect_block); - auto const new_doubly_indirect_blocks_length = min(blocks_indices.size() > processed_blocks ? blocks_indices.size() - processed_blocks : 0, entries_per_doubly_indirect_block); - TRY(grow_doubly_indirect_block(block_as_pointers[i], old_doubly_indirect_blocks_length, blocks_indices.slice(processed_blocks, new_doubly_indirect_blocks_length), new_meta_blocks, meta_blocks)); - } - - // Write out the triply indirect block. - return fs().write_block(block, buffer, block_contents.size()); -} - -ErrorOr Ext2FSInode::shrink_triply_indirect_block(BlockBasedFileSystem::BlockIndex block, size_t old_blocks_length, size_t new_blocks_length, unsigned& meta_blocks) -{ - auto const entries_per_block = EXT2_ADDR_PER_BLOCK(&fs().super_block()); - auto const entries_per_doubly_indirect_block = entries_per_block * entries_per_block; - auto const entries_per_triply_indirect_block = entries_per_doubly_indirect_block * entries_per_block; - auto const old_triply_indirect_blocks_length = ceil_div(old_blocks_length, entries_per_doubly_indirect_block); - auto const new_triply_indirect_blocks_length = new_blocks_length / entries_per_doubly_indirect_block; - VERIFY(old_blocks_length > 0); - VERIFY(old_blocks_length >= new_blocks_length); - VERIFY(new_blocks_length <= entries_per_triply_indirect_block); - - auto block_contents = TRY(ByteBuffer::create_uninitialized(fs().logical_block_size())); - auto* block_as_pointers = (unsigned*)block_contents.data(); - auto buffer = UserOrKernelBuffer::for_kernel_buffer(reinterpret_cast(block_as_pointers)); - TRY(fs().read_block(block, &buffer, fs().logical_block_size())); - - // Shrink the doubly indirect blocks. - for (unsigned i = new_triply_indirect_blocks_length; i < old_triply_indirect_blocks_length; i++) { - auto const processed_blocks = i * entries_per_doubly_indirect_block; - auto const old_doubly_indirect_blocks_length = min(old_blocks_length > processed_blocks ? old_blocks_length - processed_blocks : 0, entries_per_doubly_indirect_block); - auto const new_doubly_indirect_blocks_length = min(new_blocks_length > processed_blocks ? new_blocks_length - processed_blocks : 0, entries_per_doubly_indirect_block); - dbgln_if(EXT2_BLOCKLIST_DEBUG, "Ext2FSInode[{}]::shrink_triply_indirect_block(): Shrinking doubly indirect block {} at index {}", identifier(), block_as_pointers[i], i); - TRY(shrink_doubly_indirect_block(block_as_pointers[i], old_doubly_indirect_blocks_length, new_doubly_indirect_blocks_length, meta_blocks)); - } - - // Free the triply indirect block if no longer needed. - if (new_blocks_length == 0) { - dbgln_if(EXT2_BLOCKLIST_DEBUG, "Ext2FSInode[{}]::shrink_triply_indirect_block(): Freeing triply indirect block {}", identifier(), block); - TRY(fs().set_block_allocation_state(block, false)); - meta_blocks--; - } - - return {}; -} - -ErrorOr Ext2FSInode::flush_block_list() -{ - MutexLocker locker(m_inode_lock); - - if (m_block_list.is_empty()) { - m_raw_inode.i_blocks = 0; - memset(m_raw_inode.i_block, 0, sizeof(m_raw_inode.i_block)); - set_metadata_dirty(true); - return {}; - } - - // NOTE: There is a mismatch between i_blocks and blocks.size() since i_blocks includes meta blocks and blocks.size() does not. - auto const old_block_count = ceil_div(size(), static_cast(fs().logical_block_size())); - - auto old_shape = fs().compute_block_list_shape(old_block_count); - auto const new_shape = fs().compute_block_list_shape(m_block_list.size()); - - Vector new_meta_blocks; - if (new_shape.meta_blocks > old_shape.meta_blocks) { - new_meta_blocks = TRY(fs().allocate_blocks(fs().group_index_from_inode(index()), new_shape.meta_blocks - old_shape.meta_blocks)); - } - - m_raw_inode.i_blocks = (m_block_list.size() + new_shape.meta_blocks) * (fs().logical_block_size() / 512); - dbgln_if(EXT2_BLOCKLIST_DEBUG, "Ext2FSInode[{}]::flush_block_list(): Old shape=({};{};{};{}:{}), new shape=({};{};{};{}:{})", identifier(), old_shape.direct_blocks, old_shape.indirect_blocks, old_shape.doubly_indirect_blocks, old_shape.triply_indirect_blocks, old_shape.meta_blocks, new_shape.direct_blocks, new_shape.indirect_blocks, new_shape.doubly_indirect_blocks, new_shape.triply_indirect_blocks, new_shape.meta_blocks); - - unsigned output_block_index = 0; - unsigned remaining_blocks = m_block_list.size(); - - // Deal with direct blocks. - bool inode_dirty = false; - VERIFY(new_shape.direct_blocks <= EXT2_NDIR_BLOCKS); - for (unsigned i = 0; i < new_shape.direct_blocks; ++i) { - if (BlockBasedFileSystem::BlockIndex(m_raw_inode.i_block[i]) != m_block_list[output_block_index]) - inode_dirty = true; - m_raw_inode.i_block[i] = m_block_list[output_block_index].value(); - ++output_block_index; - --remaining_blocks; - } - // e2fsck considers all blocks reachable through any of the pointers in - // m_raw_inode.i_block as part of this inode regardless of the value in - // m_raw_inode.i_size. When it finds more blocks than the amount that - // is indicated by i_size or i_blocks it offers to repair the filesystem - // by changing those values. That will actually cause further corruption. - // So we must zero all pointers to blocks that are now unused. - for (unsigned i = new_shape.direct_blocks; i < EXT2_NDIR_BLOCKS; ++i) { - m_raw_inode.i_block[i] = 0; - } - if (inode_dirty) { - if constexpr (EXT2_DEBUG) { - dbgln("Ext2FSInode[{}]::flush_block_list(): Writing {} direct block(s) to i_block array of inode {}", identifier(), min((size_t)EXT2_NDIR_BLOCKS, m_block_list.size()), index()); - for (size_t i = 0; i < min((size_t)EXT2_NDIR_BLOCKS, m_block_list.size()); ++i) - dbgln(" + {}", m_block_list[i]); - } - set_metadata_dirty(true); - } - - // Deal with indirect blocks. - if (old_shape.indirect_blocks != new_shape.indirect_blocks) { - if (new_shape.indirect_blocks > old_shape.indirect_blocks) { - // Write out the indirect block. - if (old_shape.indirect_blocks == 0) { - auto new_block = new_meta_blocks.take_last().value(); - dbgln_if(EXT2_BLOCKLIST_DEBUG, "Ext2FSInode[{}]::flush_block_list(): Allocating indirect block: {}", identifier(), new_block); - m_raw_inode.i_block[EXT2_IND_BLOCK] = new_block; - set_metadata_dirty(true); - old_shape.meta_blocks++; - } - - TRY(write_indirect_block(m_raw_inode.i_block[EXT2_IND_BLOCK], m_block_list.span().slice(output_block_index, new_shape.indirect_blocks))); - } else if ((new_shape.indirect_blocks == 0) && (old_shape.indirect_blocks != 0)) { - dbgln_if(EXT2_BLOCKLIST_DEBUG, "Ext2FSInode[{}]::flush_block_list(): Freeing indirect block: {}", identifier(), m_raw_inode.i_block[EXT2_IND_BLOCK]); - TRY(fs().set_block_allocation_state(m_raw_inode.i_block[EXT2_IND_BLOCK], false)); - old_shape.meta_blocks--; - m_raw_inode.i_block[EXT2_IND_BLOCK] = 0; - } - } - - remaining_blocks -= new_shape.indirect_blocks; - output_block_index += new_shape.indirect_blocks; - - if (old_shape.doubly_indirect_blocks != new_shape.doubly_indirect_blocks) { - // Write out the doubly indirect block. - if (new_shape.doubly_indirect_blocks > old_shape.doubly_indirect_blocks) { - if (old_shape.doubly_indirect_blocks == 0) { - auto new_block = new_meta_blocks.take_last().value(); - dbgln_if(EXT2_BLOCKLIST_DEBUG, "Ext2FSInode[{}]::flush_block_list(): Allocating doubly indirect block: {}", identifier(), new_block); - m_raw_inode.i_block[EXT2_DIND_BLOCK] = new_block; - set_metadata_dirty(true); - old_shape.meta_blocks++; - } - TRY(grow_doubly_indirect_block(m_raw_inode.i_block[EXT2_DIND_BLOCK], old_shape.doubly_indirect_blocks, m_block_list.span().slice(output_block_index, new_shape.doubly_indirect_blocks), new_meta_blocks, old_shape.meta_blocks)); - } else { - TRY(shrink_doubly_indirect_block(m_raw_inode.i_block[EXT2_DIND_BLOCK], old_shape.doubly_indirect_blocks, new_shape.doubly_indirect_blocks, old_shape.meta_blocks)); - if (new_shape.doubly_indirect_blocks == 0) - m_raw_inode.i_block[EXT2_DIND_BLOCK] = 0; - } - } - - remaining_blocks -= new_shape.doubly_indirect_blocks; - output_block_index += new_shape.doubly_indirect_blocks; - - if (old_shape.triply_indirect_blocks != new_shape.triply_indirect_blocks) { - // Write out the triply indirect block. - if (new_shape.triply_indirect_blocks > old_shape.triply_indirect_blocks) { - if (old_shape.triply_indirect_blocks == 0) { - auto new_block = new_meta_blocks.take_last().value(); - dbgln_if(EXT2_BLOCKLIST_DEBUG, "Ext2FSInode[{}]::flush_block_list(): Allocating triply indirect block: {}", identifier(), new_block); - m_raw_inode.i_block[EXT2_TIND_BLOCK] = new_block; - set_metadata_dirty(true); - old_shape.meta_blocks++; - } - TRY(grow_triply_indirect_block(m_raw_inode.i_block[EXT2_TIND_BLOCK], old_shape.triply_indirect_blocks, m_block_list.span().slice(output_block_index, new_shape.triply_indirect_blocks), new_meta_blocks, old_shape.meta_blocks)); - } else { - TRY(shrink_triply_indirect_block(m_raw_inode.i_block[EXT2_TIND_BLOCK], old_shape.triply_indirect_blocks, new_shape.triply_indirect_blocks, old_shape.meta_blocks)); - if (new_shape.triply_indirect_blocks == 0) - m_raw_inode.i_block[EXT2_TIND_BLOCK] = 0; - } - } - - remaining_blocks -= new_shape.triply_indirect_blocks; - output_block_index += new_shape.triply_indirect_blocks; - - dbgln_if(EXT2_BLOCKLIST_DEBUG, "Ext2FSInode[{}]::flush_block_list(): New meta blocks count at {}, expecting {}", identifier(), old_shape.meta_blocks, new_shape.meta_blocks); - VERIFY(new_meta_blocks.size() == 0); - VERIFY(old_shape.meta_blocks == new_shape.meta_blocks); - if (!remaining_blocks) - return {}; - - dbgln("we don't know how to write qind ext2fs blocks, they don't exist anyway!"); - VERIFY_NOT_REACHED(); -} - -ErrorOr> Ext2FSInode::compute_block_list() const -{ - return compute_block_list_impl(false); -} - -ErrorOr> Ext2FSInode::compute_block_list_with_meta_blocks() const -{ - return compute_block_list_impl(true); -} - -ErrorOr> Ext2FSInode::compute_block_list_impl(bool include_block_list_blocks) const -{ - // FIXME: This is really awkwardly factored.. foo_impl_internal :| - auto block_list = TRY(compute_block_list_impl_internal(m_raw_inode, include_block_list_blocks)); - while (!block_list.is_empty() && block_list.last() == 0) - block_list.take_last(); - return block_list; -} - -ErrorOr> Ext2FSInode::compute_block_list_impl_internal(ext2_inode const& e2inode, bool include_block_list_blocks) const -{ - unsigned entries_per_block = EXT2_ADDR_PER_BLOCK(&fs().super_block()); - - unsigned block_count = ceil_div(size(), static_cast(fs().logical_block_size())); - - // If we are handling a symbolic link, the path is stored in the 60 bytes in - // the inode that are used for the 12 direct and 3 indirect block pointers, - // If the path is longer than 60 characters, a block is allocated, and the - // block contains the destination path. The file size corresponds to the - // path length of the destination. - if (Kernel::is_symlink(e2inode.i_mode) && e2inode.i_blocks == 0) - block_count = 0; - - dbgln_if(EXT2_DEBUG, "Ext2FSInode[{}]::block_list_for_inode(): i_size={}, i_blocks={}, block_count={}", identifier(), e2inode.i_size, e2inode.i_blocks, block_count); - - unsigned blocks_remaining = block_count; - - if (include_block_list_blocks) { - auto shape = fs().compute_block_list_shape(block_count); - blocks_remaining += shape.meta_blocks; - } - - Vector list; - - auto add_block = [&](auto bi) -> ErrorOr { - if (blocks_remaining) { - TRY(list.try_append(bi)); - --blocks_remaining; - } - return {}; - }; - - if (include_block_list_blocks) { - // This seems like an excessive over-estimate but w/e. - TRY(list.try_ensure_capacity(blocks_remaining * 2)); - } else { - TRY(list.try_ensure_capacity(blocks_remaining)); - } - - unsigned direct_count = min(block_count, (unsigned)EXT2_NDIR_BLOCKS); - for (unsigned i = 0; i < direct_count; ++i) { - auto block_index = e2inode.i_block[i]; - TRY(add_block(block_index)); - } - - if (!blocks_remaining) - return list; - - // Don't need to make copy of add_block, since this capture will only - // be called before compute_block_list_impl_internal finishes. - auto process_block_array = [&](auto array_block_index, auto&& callback) -> ErrorOr { - if (include_block_list_blocks) - TRY(add_block(array_block_index)); - auto count = min(blocks_remaining, entries_per_block); - if (!count) - return {}; - size_t read_size = count * sizeof(u32); - auto array_storage = TRY(ByteBuffer::create_uninitialized(read_size)); - auto* array = (u32*)array_storage.data(); - auto buffer = UserOrKernelBuffer::for_kernel_buffer((u8*)array); - TRY(fs().read_block(array_block_index, &buffer, read_size, 0)); - for (unsigned i = 0; i < count; ++i) - TRY(callback(Ext2FS::BlockIndex(array[i]))); - return {}; - }; - - TRY(process_block_array(e2inode.i_block[EXT2_IND_BLOCK], [&](auto block_index) -> ErrorOr { - return add_block(block_index); - })); - - if (!blocks_remaining) - return list; - - TRY(process_block_array(e2inode.i_block[EXT2_DIND_BLOCK], [&](auto block_index) -> ErrorOr { - return process_block_array(block_index, [&](auto block_index2) -> ErrorOr { - return add_block(block_index2); - }); - })); - - if (!blocks_remaining) - return list; - - TRY(process_block_array(e2inode.i_block[EXT2_TIND_BLOCK], [&](auto block_index) -> ErrorOr { - return process_block_array(block_index, [&](auto block_index2) -> ErrorOr { - return process_block_array(block_index2, [&](auto block_index3) -> ErrorOr { - return add_block(block_index3); - }); - }); - })); - - return list; -} - -Ext2FSInode::Ext2FSInode(Ext2FS& fs, InodeIndex index) - : Inode(fs, index) -{ -} - -Ext2FSInode::~Ext2FSInode() -{ - if (m_raw_inode.i_links_count == 0) { - // Alas, we have nowhere to propagate any errors that occur here. - (void)fs().free_inode(*this); - } -} - -u64 Ext2FSInode::size() const -{ - if (Kernel::is_regular_file(m_raw_inode.i_mode) && ((u32)fs().get_features_readonly() & (u32)Ext2FS::FeaturesReadOnly::FileSize64bits)) - return static_cast(m_raw_inode.i_dir_acl) << 32 | m_raw_inode.i_size; - return m_raw_inode.i_size; -} - -InodeMetadata Ext2FSInode::metadata() const -{ - MutexLocker locker(m_inode_lock); - InodeMetadata metadata; - metadata.inode = identifier(); - metadata.size = size(); - metadata.mode = m_raw_inode.i_mode; - metadata.uid = inode_uid(m_raw_inode); - metadata.gid = inode_gid(m_raw_inode); - metadata.link_count = m_raw_inode.i_links_count; - metadata.atime = UnixDateTime::from_seconds_since_epoch(m_raw_inode.i_atime); - metadata.ctime = UnixDateTime::from_seconds_since_epoch(m_raw_inode.i_ctime); - metadata.mtime = UnixDateTime::from_seconds_since_epoch(m_raw_inode.i_mtime); - metadata.dtime = UnixDateTime::from_seconds_since_epoch(m_raw_inode.i_dtime); - metadata.block_size = fs().logical_block_size(); - metadata.block_count = m_raw_inode.i_blocks; - - if (Kernel::is_character_device(m_raw_inode.i_mode) || Kernel::is_block_device(m_raw_inode.i_mode)) { - unsigned dev = m_raw_inode.i_block[0]; - if (!dev) - dev = m_raw_inode.i_block[1]; - metadata.major_device = (dev & 0xfff00) >> 8; - metadata.minor_device = (dev & 0xff) | ((dev >> 12) & 0xfff00); - } - return metadata; -} - -ErrorOr Ext2FSInode::flush_metadata() -{ - MutexLocker locker(m_inode_lock); - if (!is_metadata_dirty()) - return {}; - - dbgln_if(EXT2_DEBUG, "Ext2FSInode[{}]::flush_metadata(): Flushing inode", identifier()); - TRY(fs().write_ext2_inode(index(), m_raw_inode)); - if (is_directory()) { - // Unless we're about to go away permanently, invalidate the lookup cache. - if (m_raw_inode.i_links_count != 0) { - // FIXME: This invalidation is way too hardcore. It's sad to throw away the whole cache. - m_lookup_cache.clear(); - } - } - set_metadata_dirty(false); - return {}; -} - -ErrorOr Ext2FSInode::compute_block_list_with_exclusive_locking() -{ - // Note: We verify that the inode mutex is being held locked. Because only the read_bytes_locked() - // method uses this method and the mutex can be locked in shared mode when reading the Inode if - // it is an ext2 regular file, but also in exclusive mode, when the Inode is an ext2 directory and being - // traversed, we use another exclusive lock to ensure we always mutate the block list safely. - VERIFY(m_inode_lock.is_locked()); - MutexLocker block_list_locker(m_block_list_lock); - if (m_block_list.is_empty()) - m_block_list = TRY(compute_block_list()); - return {}; -} - -ErrorOr Ext2FSInode::read_bytes_locked(off_t offset, size_t count, UserOrKernelBuffer& buffer, OpenFileDescription* description) const -{ - VERIFY(m_inode_lock.is_locked()); - VERIFY(offset >= 0); - if (m_raw_inode.i_size == 0) - return 0; - - if (static_cast(offset) >= size()) - return 0; - - // Symbolic links shorter than 60 characters are store inline inside the i_block array. - // This avoids wasting an entire block on short links. (Most links are short.) - if (is_symlink() && size() < max_inline_symlink_length) { - VERIFY(offset == 0); - size_t nread = min((off_t)size() - offset, static_cast(count)); - TRY(buffer.write(((u8 const*)m_raw_inode.i_block) + offset, nread)); - return nread; - } - - // Note: We bypass the const declaration of this method, but this is a strong - // requirement to be able to accomplish the read operation successfully. - // We call this special method because it locks a separate mutex to ensure we - // update the block list of the inode safely, as the m_inode_lock is locked in - // shared mode. - TRY(const_cast(*this).compute_block_list_with_exclusive_locking()); - - if (m_block_list.is_empty()) { - dmesgln("Ext2FSInode[{}]::read_bytes(): Empty block list", identifier()); - return EIO; - } - - bool allow_cache = !description || !description->is_direct(); - - int const block_size = fs().logical_block_size(); - - BlockBasedFileSystem::BlockIndex first_block_logical_index = offset / block_size; - BlockBasedFileSystem::BlockIndex last_block_logical_index = (offset + count) / block_size; - if (last_block_logical_index >= m_block_list.size()) - last_block_logical_index = m_block_list.size() - 1; - - int offset_into_first_block = offset % block_size; - - size_t nread = 0; - auto remaining_count = min((off_t)count, (off_t)size() - offset); - - dbgln_if(EXT2_VERY_DEBUG, "Ext2FSInode[{}]::read_bytes(): Reading up to {} bytes, {} bytes into inode to {}", identifier(), count, offset, buffer.user_or_kernel_ptr()); - - for (auto bi = first_block_logical_index; remaining_count && bi <= last_block_logical_index; bi = bi.value() + 1) { - auto block_index = m_block_list[bi.value()]; - size_t offset_into_block = (bi == first_block_logical_index) ? offset_into_first_block : 0; - size_t num_bytes_to_copy = min((size_t)block_size - offset_into_block, (size_t)remaining_count); - auto buffer_offset = buffer.offset(nread); - if (block_index.value() == 0) { - // This is a hole, act as if it's filled with zeroes. - TRY(buffer_offset.memset(0, num_bytes_to_copy)); - } else { - if (auto result = fs().read_block(block_index, &buffer_offset, num_bytes_to_copy, offset_into_block, allow_cache); result.is_error()) { - dmesgln("Ext2FSInode[{}]::read_bytes(): Failed to read block {} (index {})", identifier(), block_index.value(), bi); - return result.release_error(); - } - } - remaining_count -= num_bytes_to_copy; - nread += num_bytes_to_copy; - } - - return nread; -} - -ErrorOr Ext2FSInode::resize(u64 new_size) -{ - VERIFY(m_inode_lock.is_locked()); - auto old_size = size(); - if (old_size == new_size) - return {}; - - if (!((u32)fs().get_features_readonly() & (u32)Ext2FS::FeaturesReadOnly::FileSize64bits) && (new_size >= static_cast(-1))) - return ENOSPC; - - u64 block_size = fs().logical_block_size(); - auto blocks_needed_before = ceil_div(old_size, block_size); - auto blocks_needed_after = ceil_div(new_size, block_size); - - if constexpr (EXT2_DEBUG) { - dbgln("Ext2FSInode[{}]::resize(): Blocks needed before (size was {}): {}", identifier(), old_size, blocks_needed_before); - dbgln("Ext2FSInode[{}]::resize(): Blocks needed after (size is {}): {}", identifier(), new_size, blocks_needed_after); - } - - if (blocks_needed_after > blocks_needed_before) { - auto additional_blocks_needed = blocks_needed_after - blocks_needed_before; - if (additional_blocks_needed > fs().super_block().s_free_blocks_count) - return ENOSPC; - } - - if (m_block_list.is_empty()) - m_block_list = TRY(compute_block_list()); - - if (blocks_needed_after > blocks_needed_before) { - auto blocks = TRY(fs().allocate_blocks(fs().group_index_from_inode(index()), blocks_needed_after - blocks_needed_before)); - TRY(m_block_list.try_extend(move(blocks))); - } else if (blocks_needed_after < blocks_needed_before) { - if constexpr (EXT2_VERY_DEBUG) { - dbgln("Ext2FSInode[{}]::resize(): Shrinking inode, old block list is {} entries:", identifier(), m_block_list.size()); - for (auto block_index : m_block_list) { - dbgln(" # {}", block_index); - } - } - while (m_block_list.size() != blocks_needed_after) { - auto block_index = m_block_list.take_last(); - if (block_index.value()) { - if (auto result = fs().set_block_allocation_state(block_index, false); result.is_error()) { - dbgln("Ext2FSInode[{}]::resize(): Failed to free block {}: {}", identifier(), block_index, result.error()); - return result; - } - } - } - } - - TRY(flush_block_list()); - - m_raw_inode.i_size = new_size; - if (Kernel::is_regular_file(m_raw_inode.i_mode)) - m_raw_inode.i_dir_acl = new_size >> 32; - - set_metadata_dirty(true); - - if (new_size > old_size) { - // If we're growing the inode, make sure we zero out all the new space. - // FIXME: There are definitely more efficient ways to achieve this. - auto bytes_to_clear = new_size - old_size; - auto clear_from = old_size; - u8 zero_buffer[PAGE_SIZE] {}; - while (bytes_to_clear) { - auto nwritten = TRY(prepare_and_write_bytes_locked(clear_from, min(static_cast(sizeof(zero_buffer)), bytes_to_clear), UserOrKernelBuffer::for_kernel_buffer(zero_buffer), nullptr)); - VERIFY(nwritten != 0); - bytes_to_clear -= nwritten; - clear_from += nwritten; - } - } - - return {}; -} - -ErrorOr Ext2FSInode::write_bytes_locked(off_t offset, size_t count, UserOrKernelBuffer const& data, OpenFileDescription* description) -{ - VERIFY(m_inode_lock.is_locked()); - VERIFY(offset >= 0); - - if (count == 0) - return 0; - - if (is_symlink()) { - VERIFY(offset == 0); - if (max((size_t)(offset + count), (size_t)m_raw_inode.i_size) < max_inline_symlink_length) { - dbgln_if(EXT2_DEBUG, "Ext2FSInode[{}]::write_bytes_locked(): Poking into i_block array for inline symlink ({} bytes)", identifier(), count); - TRY(data.read(((u8*)m_raw_inode.i_block) + offset, count)); - if ((size_t)(offset + count) > (size_t)m_raw_inode.i_size) - m_raw_inode.i_size = offset + count; - set_metadata_dirty(true); - return count; - } - } - - bool allow_cache = !description || !description->is_direct(); - - auto const block_size = fs().logical_block_size(); - auto new_size = max(static_cast(offset) + count, size()); - - TRY(resize(new_size)); - - if (m_block_list.is_empty()) - m_block_list = TRY(compute_block_list()); - - if (m_block_list.is_empty()) { - dbgln("Ext2FSInode[{}]::write_bytes(): Empty block list", identifier()); - return EIO; - } - - BlockBasedFileSystem::BlockIndex first_block_logical_index = offset / block_size; - BlockBasedFileSystem::BlockIndex last_block_logical_index = (offset + count) / block_size; - if (last_block_logical_index >= m_block_list.size()) - last_block_logical_index = m_block_list.size() - 1; - - size_t offset_into_first_block = offset % block_size; - - size_t nwritten = 0; - auto remaining_count = min((off_t)count, (off_t)new_size - offset); - - dbgln_if(EXT2_VERY_DEBUG, "Ext2FSInode[{}]::write_bytes_locked(): Writing {} bytes, {} bytes into inode from {}", identifier(), count, offset, data.user_or_kernel_ptr()); - - for (auto bi = first_block_logical_index; remaining_count && bi <= last_block_logical_index; bi = bi.value() + 1) { - size_t offset_into_block = (bi == first_block_logical_index) ? offset_into_first_block : 0; - size_t num_bytes_to_copy = min((size_t)block_size - offset_into_block, (size_t)remaining_count); - dbgln_if(EXT2_DEBUG, "Ext2FSInode[{}]::write_bytes_locked(): Writing block {} (offset_into_block: {})", identifier(), m_block_list[bi.value()], offset_into_block); - if (auto result = fs().write_block(m_block_list[bi.value()], data.offset(nwritten), num_bytes_to_copy, offset_into_block, allow_cache); result.is_error()) { - dbgln("Ext2FSInode[{}]::write_bytes_locked(): Failed to write block {} (index {})", identifier(), m_block_list[bi.value()], bi); - return result.release_error(); - } - remaining_count -= num_bytes_to_copy; - nwritten += num_bytes_to_copy; - } - - did_modify_contents(); - - dbgln_if(EXT2_VERY_DEBUG, "Ext2FSInode[{}]::write_bytes_locked(): After write, i_size={}, i_blocks={} ({} blocks in list)", identifier(), size(), m_raw_inode.i_blocks, m_block_list.size()); - return nwritten; -} - -ErrorOr Ext2FSInode::traverse_as_directory(Function(FileSystem::DirectoryEntryView const&)> callback) const -{ - MutexLocker locker(m_inode_lock); - VERIFY(is_directory()); - - u8 buffer[max_block_size]; - auto buf = UserOrKernelBuffer::for_kernel_buffer(buffer); - - auto block_size = fs().logical_block_size(); - auto file_size = size(); - - bool has_file_type_attribute = has_flag(fs().get_features_optional(), Ext2FS::FeaturesOptional::ExtendedAttributes); - - // Directory entries are guaranteed not to span multiple blocks, - // so we can iterate over blocks separately. - - for (u64 offset = 0; offset < file_size; offset += block_size) { - TRY(read_bytes(offset, block_size, buf, nullptr)); - - using ext2_extended_dir_entry = ext2_dir_entry_2; - auto* entry = reinterpret_cast(buffer); - auto* entries_end = reinterpret_cast(buffer + block_size); - while (entry < entries_end) { - if (entry->inode != 0) { - dbgln_if(EXT2_DEBUG, "Ext2FSInode[{}]::traverse_as_directory(): inode {}, name_len: {}, rec_len: {}, file_type: {}, name: {}", identifier(), entry->inode, entry->name_len, entry->rec_len, entry->file_type, StringView(entry->name, entry->name_len)); - TRY(callback({ { entry->name, entry->name_len }, { fsid(), entry->inode }, has_file_type_attribute ? entry->file_type : (u8)EXT2_FT_UNKNOWN })); - } - entry = (ext2_extended_dir_entry*)((char*)entry + entry->rec_len); - } - } - - return {}; -} - -ErrorOr Ext2FSInode::write_directory(Vector& entries) -{ - MutexLocker locker(m_inode_lock); - auto block_size = fs().logical_block_size(); - - // Calculate directory size and record length of entries so that - // the following constraints are met: - // - All used blocks must be entirely filled. - // - Entries are aligned on a 4-byte boundary. - // - No entry may span multiple blocks. - size_t directory_size = 0; - size_t space_in_block = block_size; - for (size_t i = 0; i < entries.size(); ++i) { - auto& entry = entries[i]; - entry.record_length = EXT2_DIR_REC_LEN(entry.name->length()); - space_in_block -= entry.record_length; - if (i + 1 < entries.size()) { - if (EXT2_DIR_REC_LEN(entries[i + 1].name->length()) > space_in_block) { - entry.record_length += space_in_block; - space_in_block = block_size; - } - } else { - entry.record_length += space_in_block; - } - directory_size += entry.record_length; - } - - dbgln_if(EXT2_DEBUG, "Ext2FSInode[{}]::write_directory(): New directory contents to write (size {}):", identifier(), directory_size); - - auto directory_data = TRY(ByteBuffer::create_uninitialized(directory_size)); - FixedMemoryStream stream { directory_data.bytes() }; - bool has_file_type_attribute = has_flag(fs().get_features_optional(), Ext2FS::FeaturesOptional::ExtendedAttributes); - - for (auto& entry : entries) { - dbgln_if(EXT2_DEBUG, "Ext2FSInode[{}]::write_directory(): Writing inode: {}, name_len: {}, rec_len: {}, file_type: {}, name: {}", identifier(), entry.inode_index, u16(entry.name->length()), u16(entry.record_length), u8(entry.file_type), entry.name); - - MUST(stream.write_value(entry.inode_index.value())); - MUST(stream.write_value(entry.record_length)); - MUST(stream.write_value(entry.name->length())); - MUST(stream.write_value(has_file_type_attribute ? entry.file_type : EXT2_FT_UNKNOWN)); - MUST(stream.write_until_depleted(entry.name->bytes())); - int padding = entry.record_length - entry.name->length() - 8; - for (int j = 0; j < padding; ++j) - MUST(stream.write_value(0)); - } - - auto serialized_bytes_count = TRY(stream.tell()); - VERIFY(serialized_bytes_count == directory_size); - - TRY(resize(serialized_bytes_count)); - - auto buffer = UserOrKernelBuffer::for_kernel_buffer(directory_data.data()); - auto nwritten = TRY(prepare_and_write_bytes_locked(0, serialized_bytes_count, buffer, nullptr)); - set_metadata_dirty(true); - if (nwritten != directory_data.size()) - return EIO; - return {}; -} - -ErrorOr> Ext2FSInode::create_child(StringView name, mode_t mode, dev_t dev, UserID uid, GroupID gid) -{ - if (Kernel::is_directory(mode)) - return fs().create_directory(*this, name, mode, uid, gid); - return fs().create_inode(*this, name, mode, dev, uid, gid); -} - -ErrorOr Ext2FSInode::add_child(Inode& child, StringView name, mode_t mode) -{ - MutexLocker locker(m_inode_lock); - VERIFY(is_directory()); - - if (name.length() > EXT2_NAME_LEN) - return ENAMETOOLONG; - - dbgln_if(EXT2_DEBUG, "Ext2FSInode[{}]::add_child(): Adding inode {} with name '{}' and mode {:o} to directory {}", identifier(), child.index(), name, mode, index()); - bool has_file_type_attribute = has_flag(fs().get_features_optional(), Ext2FS::FeaturesOptional::ExtendedAttributes); - - Vector entries; - TRY(traverse_as_directory([&](auto& entry) -> ErrorOr { - if (name == entry.name) - return EEXIST; - auto entry_name = TRY(KString::try_create(entry.name)); - TRY(entries.try_append({ move(entry_name), entry.inode.index(), has_file_type_attribute ? entry.file_type : (u8)EXT2_FT_UNKNOWN })); - return {}; - })); - - TRY(child.increment_link_count()); - - auto entry_name = TRY(KString::try_create(name)); - TRY(entries.try_empend(move(entry_name), child.index(), has_file_type_attribute ? to_ext2_file_type(mode) : (u8)EXT2_FT_UNKNOWN)); - - TRY(write_directory(entries)); - TRY(populate_lookup_cache()); - - auto cache_entry_name = TRY(KString::try_create(name)); - TRY(m_lookup_cache.try_set(move(cache_entry_name), child.index())); - did_add_child(child.identifier(), name); - return {}; -} - -ErrorOr Ext2FSInode::remove_child(StringView name) -{ - MutexLocker locker(m_inode_lock); - dbgln_if(EXT2_DEBUG, "Ext2FSInode[{}]::remove_child(): Removing '{}'", identifier(), name); - VERIFY(is_directory()); - - TRY(populate_lookup_cache()); - - auto it = m_lookup_cache.find(name); - if (it == m_lookup_cache.end()) - return ENOENT; - auto child_inode_index = (*it).value; - - InodeIdentifier child_id { fsid(), child_inode_index }; - bool has_file_type_attribute = has_flag(fs().get_features_optional(), Ext2FS::FeaturesOptional::ExtendedAttributes); - - Vector entries; - TRY(traverse_as_directory([&](auto& entry) -> ErrorOr { - if (name != entry.name) { - auto entry_name = TRY(KString::try_create(entry.name)); - TRY(entries.try_append({ move(entry_name), entry.inode.index(), has_file_type_attribute ? entry.file_type : (u8)EXT2_FT_UNKNOWN })); - } - return {}; - })); - - TRY(write_directory(entries)); - - m_lookup_cache.remove(it); - - auto child_inode = TRY(fs().get_inode(child_id)); - TRY(child_inode->decrement_link_count()); - - did_remove_child(child_id, name); - return {}; -} - -ErrorOr Ext2FSInode::replace_child(StringView name, Inode& child) -{ - MutexLocker locker(m_inode_lock); - dbgln_if(EXT2_DEBUG, "Ext2FSInode[{}]::replace_child(): Replacing '{}' with inode {}", identifier(), name, child.index()); - VERIFY(is_directory()); - - TRY(populate_lookup_cache()); - - if (name.length() > EXT2_NAME_LEN) - return ENAMETOOLONG; - - Vector entries; - bool has_file_type_attribute = has_flag(fs().get_features_optional(), Ext2FS::FeaturesOptional::ExtendedAttributes); - - Optional old_child_index; - TRY(traverse_as_directory([&](auto& entry) -> ErrorOr { - auto is_replacing_this_inode = name == entry.name; - auto inode_index = is_replacing_this_inode ? child.index() : entry.inode.index(); - - auto entry_name = TRY(KString::try_create(entry.name)); - TRY(entries.try_empend(move(entry_name), inode_index, has_file_type_attribute ? to_ext2_file_type(child.mode()) : (u8)EXT2_FT_UNKNOWN)); - if (is_replacing_this_inode) - old_child_index = entry.inode.index(); - - return {}; - })); - - if (!old_child_index.has_value()) - return ENOENT; - - auto old_child = TRY(fs().get_inode({ fsid(), *old_child_index })); - - auto old_index_it = m_lookup_cache.find(name); - VERIFY(old_index_it != m_lookup_cache.end()); - old_index_it->value = child.index(); - - // NOTE: Between this line and the write_directory line, all operations must - // be atomic. Any changes made should be reverted. - TRY(child.increment_link_count()); - - auto maybe_decrement_error = old_child->decrement_link_count(); - if (maybe_decrement_error.is_error()) { - old_index_it->value = *old_child_index; - MUST(child.decrement_link_count()); - return maybe_decrement_error; - } - - // FIXME: The filesystem is left in an inconsistent state if this fails. - // Revert the changes made above if we can't write_directory. - // Ideally, decrement should be the last operation, but we currently - // can't "un-write" a directory entry list. - TRY(write_directory(entries)); - - // TODO: Emit a did_replace_child event. - - return {}; -} - -ErrorOr Ext2FSInode::populate_lookup_cache() -{ - VERIFY(m_inode_lock.is_exclusively_locked_by_current_thread()); - if (!m_lookup_cache.is_empty()) - return {}; - HashMap, InodeIndex> children; - - TRY(traverse_as_directory([&children](auto& entry) -> ErrorOr { - auto entry_name = TRY(KString::try_create(entry.name)); - TRY(children.try_set(move(entry_name), entry.inode.index())); - return {}; - })); - - VERIFY(m_lookup_cache.is_empty()); - m_lookup_cache = move(children); - return {}; -} - -ErrorOr> Ext2FSInode::lookup(StringView name) -{ - VERIFY(is_directory()); - dbgln_if(EXT2_DEBUG, "Ext2FSInode[{}]:lookup(): Looking up '{}'", identifier(), name); - - InodeIndex inode_index; - { - MutexLocker locker(m_inode_lock); - TRY(populate_lookup_cache()); - auto it = m_lookup_cache.find(name); - if (it == m_lookup_cache.end()) { - dbgln_if(EXT2_DEBUG, "Ext2FSInode[{}]:lookup(): '{}' not found", identifier(), name); - return ENOENT; - } - inode_index = it->value; - } - - return fs().get_inode({ fsid(), inode_index }); -} - -ErrorOr Ext2FSInode::update_timestamps(Optional atime, Optional ctime, Optional mtime) -{ - MutexLocker locker(m_inode_lock); - if (fs().is_readonly()) - return EROFS; - if (atime.value_or({}).to_timespec().tv_sec > NumericLimits::max()) - return EINVAL; - if (ctime.value_or({}).to_timespec().tv_sec > NumericLimits::max()) - return EINVAL; - if (mtime.value_or({}).to_timespec().tv_sec > NumericLimits::max()) - return EINVAL; - if (atime.has_value()) - m_raw_inode.i_atime = atime.value().to_timespec().tv_sec; - if (ctime.has_value()) - m_raw_inode.i_ctime = ctime.value().to_timespec().tv_sec; - if (mtime.has_value()) - m_raw_inode.i_mtime = mtime.value().to_timespec().tv_sec; - set_metadata_dirty(true); - return {}; -} - -ErrorOr Ext2FSInode::increment_link_count() -{ - MutexLocker locker(m_inode_lock); - if (fs().is_readonly()) - return EROFS; - constexpr size_t max_link_count = 65535; - if (m_raw_inode.i_links_count == max_link_count) - return EMLINK; - ++m_raw_inode.i_links_count; - set_metadata_dirty(true); - return {}; -} - -ErrorOr Ext2FSInode::decrement_link_count() -{ - MutexLocker locker(m_inode_lock); - if (fs().is_readonly()) - return EROFS; - VERIFY(m_raw_inode.i_links_count); - - --m_raw_inode.i_links_count; - set_metadata_dirty(true); - if (m_raw_inode.i_links_count == 0) - did_delete_self(); - - if (ref_count() == 1 && m_raw_inode.i_links_count == 0) - fs().uncache_inode(index()); - - return {}; -} - -ErrorOr Ext2FSInode::chmod(mode_t mode) -{ - MutexLocker locker(m_inode_lock); - if (m_raw_inode.i_mode == mode) - return {}; - m_raw_inode.i_mode = mode; - set_metadata_dirty(true); - return {}; -} - -ErrorOr Ext2FSInode::chown(UserID uid, GroupID gid) -{ - MutexLocker locker(m_inode_lock); - if (inode_uid(m_raw_inode) == uid && inode_gid(m_raw_inode) == gid) - return {}; - m_raw_inode.i_uid = static_cast(uid.value()); - ext2fs_set_i_uid_high(m_raw_inode, uid.value() >> 16); - m_raw_inode.i_gid = static_cast(gid.value()); - ext2fs_set_i_gid_high(m_raw_inode, gid.value() >> 16); - set_metadata_dirty(true); - return {}; -} - -ErrorOr Ext2FSInode::truncate_locked(u64 size) -{ - VERIFY(m_inode_lock.is_locked()); - if (static_cast(m_raw_inode.i_size) == size) - return {}; - TRY(resize(size)); - set_metadata_dirty(true); - did_modify_contents(); - return {}; -} - -ErrorOr Ext2FSInode::get_block_address(int index) -{ - MutexLocker locker(m_inode_lock); - - if (m_block_list.is_empty()) - m_block_list = TRY(compute_block_list()); - - if (index < 0 || (size_t)index >= m_block_list.size()) - return 0; - - return m_block_list[index].value(); -} - -} diff --git a/Kernel/FileSystem/Ext2FS/Inode.h b/Kernel/FileSystem/Ext2FS/Inode.h deleted file mode 100644 index 620bc9b1834..00000000000 --- a/Kernel/FileSystem/Ext2FS/Inode.h +++ /dev/null @@ -1,85 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include -#include - -namespace Kernel { - -class Ext2FSInode final : public Inode { - friend class Ext2FS; - -public: - virtual ~Ext2FSInode() override; - - u64 size() const; - bool is_symlink() const { return Kernel::is_symlink(m_raw_inode.i_mode); } - bool is_directory() const { return Kernel::is_directory(m_raw_inode.i_mode); } - -private: - // ^Inode - virtual ErrorOr read_bytes_locked(off_t, size_t, UserOrKernelBuffer& buffer, OpenFileDescription*) const override; - virtual InodeMetadata metadata() const override; - virtual ErrorOr traverse_as_directory(Function(FileSystem::DirectoryEntryView const&)>) const override; - virtual ErrorOr> lookup(StringView name) override; - virtual ErrorOr flush_metadata() override; - virtual ErrorOr write_bytes_locked(off_t, size_t, UserOrKernelBuffer const& data, OpenFileDescription*) override; - virtual ErrorOr> create_child(StringView name, mode_t, dev_t, UserID, GroupID) override; - virtual ErrorOr add_child(Inode& child, StringView name, mode_t) override; - virtual ErrorOr remove_child(StringView name) override; - virtual ErrorOr replace_child(StringView name, Inode& child) override; - virtual ErrorOr update_timestamps(Optional atime, Optional ctime, Optional mtime) override; - virtual ErrorOr increment_link_count() override; - virtual ErrorOr decrement_link_count() override; - virtual ErrorOr chmod(mode_t) override; - virtual ErrorOr chown(UserID, GroupID) override; - virtual ErrorOr truncate_locked(u64) override; - virtual ErrorOr get_block_address(int) override; - - ErrorOr write_directory(Vector&); - ErrorOr populate_lookup_cache(); - ErrorOr resize(u64); - ErrorOr write_indirect_block(BlockBasedFileSystem::BlockIndex, Span); - ErrorOr grow_doubly_indirect_block(BlockBasedFileSystem::BlockIndex, size_t, Span, Vector&, unsigned&); - ErrorOr shrink_doubly_indirect_block(BlockBasedFileSystem::BlockIndex, size_t, size_t, unsigned&); - ErrorOr grow_triply_indirect_block(BlockBasedFileSystem::BlockIndex, size_t, Span, Vector&, unsigned&); - ErrorOr shrink_triply_indirect_block(BlockBasedFileSystem::BlockIndex, size_t, size_t, unsigned&); - ErrorOr flush_block_list(); - - ErrorOr compute_block_list_with_exclusive_locking(); - ErrorOr> compute_block_list() const; - ErrorOr> compute_block_list_with_meta_blocks() const; - ErrorOr> compute_block_list_impl(bool include_block_list_blocks) const; - ErrorOr> compute_block_list_impl_internal(ext2_inode const&, bool include_block_list_blocks) const; - - Ext2FS& fs(); - Ext2FS const& fs() const; - Ext2FSInode(Ext2FS&, InodeIndex); - - Vector m_block_list; - HashMap, InodeIndex> m_lookup_cache; - ext2_inode m_raw_inode {}; - - Mutex m_block_list_lock { "BlockList"sv }; -}; - -inline Ext2FS& Ext2FSInode::fs() -{ - return static_cast(Inode::fs()); -} - -inline Ext2FS const& Ext2FSInode::fs() const -{ - return static_cast(Inode::fs()); -} - -} diff --git a/Kernel/FileSystem/Ext2FS/ext2_types.h b/Kernel/FileSystem/Ext2FS/ext2_types.h deleted file mode 100644 index 420e6236fb6..00000000000 --- a/Kernel/FileSystem/Ext2FS/ext2_types.h +++ /dev/null @@ -1,154 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -/* - * If linux/types.h is already been included, assume it has defined - * everything we need. (cross fingers) Other header files may have - * also defined the types that we need. - */ -#if (!defined(_LINUX_TYPES_H) && !defined(_BLKID_TYPES_H) && !defined(_EXT2_TYPES_H)) -# define _EXT2_TYPES_H - -# define __S8_TYPEDEF __signed__ char -# define __U8_TYPEDEF unsigned char -# define __S16_TYPEDEF __signed__ short -# define __U16_TYPEDEF unsigned short -# define __S32_TYPEDEF __signed__ int -# define __U32_TYPEDEF unsigned int -# define __S64_TYPEDEF __signed__ long long -# define __U64_TYPEDEF unsigned long long - -# ifdef __U8_TYPEDEF -typedef __U8_TYPEDEF __u8; -# else -typedef unsigned char __u8; -# endif - -# ifdef __S8_TYPEDEF -typedef __S8_TYPEDEF __s8; -# else -typedef signed char __s8; -# endif - -# ifdef __U16_TYPEDEF -typedef __U16_TYPEDEF __u16; -# else -# if (4 == 2) -typedef unsigned int __u16; -# else -# if (2 == 2) -typedef unsigned short __u16; -# else -? == error : undefined 16 bit type -# endif /* SIZEOF_SHORT == 2 */ -# endif /* SIZEOF_INT == 2 */ -# endif /* __U16_TYPEDEF */ - -# ifdef __S16_TYPEDEF -typedef __S16_TYPEDEF __s16; -# else -# if (4 == 2) -typedef int __s16; -# else -# if (2 == 2) -typedef short __s16; -# else - ? == error - : undefined 16 bit type -# endif /* SIZEOF_SHORT == 2 */ -# endif /* SIZEOF_INT == 2 */ -# endif /* __S16_TYPEDEF */ - -# ifdef __U32_TYPEDEF -typedef __U32_TYPEDEF __u32; -# else -# if (4 == 4) -typedef unsigned int __u32; -# else -# if (4 == 4) -typedef unsigned long __u32; -# else -# if (2 == 4) - typedef unsigned short __u32; -# else - ? == error - : undefined 32 bit type -# endif /* SIZEOF_SHORT == 4 */ -# endif /* SIZEOF_LONG == 4 */ -# endif /* SIZEOF_INT == 4 */ -# endif /* __U32_TYPEDEF */ - -# ifdef __S32_TYPEDEF -typedef __S32_TYPEDEF __s32; -# else -# if (4 == 4) -typedef int __s32; -# else -# if (4 == 4) -typedef long __s32; -# else -# if (2 == 4) -typedef short __s32; -# else - ? == error - : undefined 32 bit type -# endif /* SIZEOF_SHORT == 4 */ -# endif /* SIZEOF_LONG == 4 */ -# endif /* SIZEOF_INT == 4 */ -# endif /* __S32_TYPEDEF */ - -# ifdef __U64_TYPEDEF -typedef __U64_TYPEDEF __u64; -# else -# if (4 == 8) -typedef unsigned int __u64; -# else -# if (4 == 8) -typedef unsigned long __u64; -# else -# if (8 == 8) -typedef unsigned long long __u64; -# endif /* SIZEOF_LONG_LONG == 8 */ -# endif /* SIZEOF_LONG == 8 */ -# endif /* SIZEOF_INT == 8 */ -# endif /* __U64_TYPEDEF */ - -# ifdef __S64_TYPEDEF -typedef __S64_TYPEDEF __s64; -# else -# if (4 == 8) -typedef int __s64; -# else -# if (4 == 8) -typedef long __s64; -# else -# if (8 == 8) -# if defined(__GNUC__) -typedef __signed__ long long __s64; -# else -typedef signed long long __s64; -# endif /* __GNUC__ */ -# endif /* SIZEOF_LONG_LONG == 8 */ -# endif /* SIZEOF_LONG == 8 */ -# endif /* SIZEOF_INT == 8 */ -# endif /* __S64_TYPEDEF */ - -# undef __S8_TYPEDEF -# undef __U8_TYPEDEF -# undef __S16_TYPEDEF -# undef __U16_TYPEDEF -# undef __S32_TYPEDEF -# undef __U32_TYPEDEF -# undef __S64_TYPEDEF -# undef __U64_TYPEDEF - -#endif /* _*_TYPES_H */ - -/* These defines are needed for the public ext2fs.h header file */ -#define HAVE_SYS_TYPES_H 1 -#undef WORDS_BIGENDIAN diff --git a/Kernel/FileSystem/FATFS/Definitions.h b/Kernel/FileSystem/FATFS/Definitions.h deleted file mode 100644 index 32938c16496..00000000000 --- a/Kernel/FileSystem/FATFS/Definitions.h +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Copyright (c) 2022, Undefine - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include - -namespace Kernel { - -enum DOSBIOSParameterBlockVersion { - DOS_BPB_UNKNOWN, - DOS_BPB_3, // Version 3.4. - DOS_BPB_4, // Version 4.0 - DOS_BPB_7 // Version 7.0 -}; - -enum class FATVersion { - FAT12, - FAT16, - FAT32, -}; - -enum class FATAttributes : u8 { - ReadOnly = 0x01, - Hidden = 0x02, - System = 0x04, - VolumeID = 0x08, - Directory = 0x10, - Archive = 0x20, - LongFileName = 0x0F -}; - -AK_ENUM_BITWISE_OPERATORS(FATAttributes); - -struct [[gnu::packed]] FATEntry { - char filename[8]; - char extension[3]; - FATAttributes attributes; - u8 unused1; - u8 creation_time_seconds; - DOSPackedTime creation_time; - DOSPackedDate creation_date; - DOSPackedDate last_accessed_date; - u16 first_cluster_high; - DOSPackedTime modification_time; - DOSPackedDate modification_date; - u16 first_cluster_low; - u32 file_size; -}; -static_assert(AssertSize()); - -struct [[gnu::packed]] FATLongFileNameEntry { - u8 entry_index; - u16 characters1[5]; - FATAttributes attributes; - u8 entry_type; - u8 checksum; - u16 characters2[6]; - u16 zero; - u16 characters3[2]; -}; -static_assert(AssertSize()); - -} diff --git a/Kernel/FileSystem/FATFS/FileSystem.cpp b/Kernel/FileSystem/FATFS/FileSystem.cpp deleted file mode 100644 index 80d2abfef68..00000000000 --- a/Kernel/FileSystem/FATFS/FileSystem.cpp +++ /dev/null @@ -1,531 +0,0 @@ -/* - * Copyright (c) 2022-2024, Undefine - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include - -namespace Kernel { - -DOSBIOSParameterBlockVersion DOSBIOSParameterBlock::bpb_version() const -{ - bool dos3_valid = m_dos4_block->signature == 0x28; - bool dos4_valid = m_dos4_block->signature == 0x29; - bool dos7_valid = m_dos7_block->signature == 0x28 || m_dos7_block->signature == 0x29; - // A DOS 7 EBPB should _never_ contain the values 0x28 or 0x29 at - // the offset associated with `m_dos4_block->signature` - // (aka `m_dos7_block->sectors_per_fat_32bit`) due to the maximum number of - // clusters ensuring the number of sectors per fat will not exceed 0x200000. - // As a result, it should be safe to determine BPB version through the - // signature fields by checking the DOS 4 signature offset prior to the DOS 7 one. - // - // With a DOS 3 or DOS 4 EBPB, the DOS 7 signature offset references uninitialized - // space. While unlikely to be set to a valid signature value, it is not implausible. - // We warn the user here, but because it does not represent an invalid FS configuration, - // do not error. - if ((dos3_valid || dos4_valid) && dos7_valid) - dbgln("FATFS: DOS 4 and DOS 7 EBPB signatures detected, EBPB/FAT version detection may be incorrect."); - - if (dos3_valid) - return DOS_BPB_3; - else if (dos4_valid) - return DOS_BPB_4; - else if (dos7_valid) - return DOS_BPB_7; - else - return DOS_BPB_UNKNOWN; -} - -DOS3BIOSParameterBlock const* DOSBIOSParameterBlock::common_bpb() const -{ - return m_common_block; -} - -DOS4BIOSParameterBlock const* DOSBIOSParameterBlock::dos4_bpb() const -{ - // Only return parameter block if signature indicates this portion of the block is filled out. - if (m_dos4_block->signature == 0x28 || m_dos4_block->signature == 0x29) - return m_dos4_block; - else - return nullptr; -} - -DOS7BIOSParameterBlock const* DOSBIOSParameterBlock::dos7_bpb() const -{ - // Only return parameter block if signature indicates this portion of the block is filled out. - if (m_dos7_block->signature == 0x28 || m_dos7_block->signature == 0x29) - return m_dos7_block; - else - return nullptr; -} - -u16 DOSBIOSParameterBlock::sectors_per_fat() const -{ - return common_bpb()->sectors_per_fat_16bit != 0 ? common_bpb()->sectors_per_fat_16bit : m_dos7_block->sectors_per_fat_32bit; -} - -u32 DOSBIOSParameterBlock::sector_count() const -{ - if (common_bpb()->sector_count_16bit != 0) { - // The `16bit` field is only used on partitions smaller than 32 MB, - // and never for FAT32. - // It is set to `0` when the 32 bit field contains the sector count. - return common_bpb()->sector_count_16bit; - } else { - return common_bpb()->sector_count_32bit; - // FIXME: If this is 0 for a FAT32 EBPB with a signature of 0x29, - // read 0x052, which is a 64-bit wide sector count. - } -} - -u8 DOSBIOSParameterBlock::signature() const -{ - if (bpb_version() == DOS_BPB_3 || bpb_version() == DOS_BPB_4) - return m_dos4_block->signature; - else - return m_dos7_block->signature; -} - -ErrorOr> FATFS::try_create(OpenFileDescription& file_description, ReadonlyBytes) -{ - return TRY(adopt_nonnull_ref_or_enomem(new (nothrow) FATFS(file_description))); -} - -FATFS::FATFS(OpenFileDescription& file_description) - : BlockBasedFileSystem(file_description) -{ -} - -bool FATFS::is_initialized_while_locked() -{ - VERIFY(m_lock.is_locked()); - return !m_root_inode.is_null(); -} - -ErrorOr FATFS::initialize_while_locked() -{ - VERIFY(m_lock.is_locked()); - VERIFY(!is_initialized_while_locked()); - - m_boot_record = TRY(KBuffer::try_create_with_size("FATFS: Boot Record"sv, m_device_block_size)); - auto boot_record_buffer = UserOrKernelBuffer::for_kernel_buffer(m_boot_record->data()); - TRY(raw_read(0, boot_record_buffer)); - m_parameter_block = TRY(adopt_nonnull_own_or_enomem(new (nothrow) DOSBIOSParameterBlock(m_boot_record))); - - // Alias for extended BPB. - DOSBIOSParameterBlock& ebpb = *m_parameter_block; - // Alias for block of common parameters in BPB. - DOS3BIOSParameterBlock const* block = ebpb.common_bpb(); - - if constexpr (FAT_DEBUG) { - dbgln("FATFS: oem_identifier: {}", block->oem_identifier); - dbgln("FATFS: bytes_per_sector: {}", static_cast(block->bytes_per_sector)); - dbgln("FATFS: sectors_per_cluster: {}", block->sectors_per_cluster); - dbgln("FATFS: reserved_sector_count: {}", block->reserved_sector_count); - dbgln("FATFS: fat_count: {}", block->fat_count); - dbgln("FATFS: root_directory_entry_count: {}", static_cast(block->root_directory_entry_count)); - dbgln("FATFS: media_descriptor_type: {}", block->media_descriptor_type); - dbgln("FATFS: sectors_per_track: {}", block->sectors_per_track); - dbgln("FATFS: head_count: {}", block->head_count); - dbgln("FATFS: hidden_sector_count: {}", block->hidden_sector_count); - dbgln("FATFS: sector_count: {}", ebpb.sector_count()); - dbgln("FATFS: sectors_per_fat: {}", ebpb.sectors_per_fat()); - - auto ebpb_version = ebpb.bpb_version(); - if (ebpb_version == DOSBIOSParameterBlockVersion::DOS_BPB_7) { - DOS7BIOSParameterBlock const* dos7_boot_record = ebpb.dos7_bpb(); - dbgln("FATFS: EBPB: DOS 7"); - dbgln("FATFS: flags: {}", dos7_boot_record->flags); - dbgln("FATFS: fat_version: {}", dos7_boot_record->fat_version); - dbgln("FATFS: root_directory_cluster: {}", dos7_boot_record->root_directory_cluster); - dbgln("FATFS: fs_info_sector: {}", dos7_boot_record->fs_info_sector); - dbgln("FATFS: backup_boot_sector: {}", dos7_boot_record->backup_boot_sector); - dbgln("FATFS: drive_number: {}", dos7_boot_record->drive_number); - dbgln("FATFS: volume_id: {}", static_cast(dos7_boot_record->volume_id)); - } else if (ebpb_version == DOSBIOSParameterBlockVersion::DOS_BPB_3 || ebpb_version == DOSBIOSParameterBlockVersion::DOS_BPB_4) { - DOS4BIOSParameterBlock const* dos4_boot_record = ebpb.dos4_bpb(); - if (ebpb_version == DOSBIOSParameterBlockVersion::DOS_BPB_3) { - dbgln("FATFS: EBPB: DOS 3.4"); - } else if (ebpb_version == DOSBIOSParameterBlockVersion::DOS_BPB_4) { - dbgln("FATFS: EBPB: DOS 4"); - } - dbgln("FATFS: drive_number: {}", dos4_boot_record->drive_number); - dbgln("FATFS: flags: {}", dos4_boot_record->flags); - dbgln("FATFS: volume_id: {}", static_cast(dos4_boot_record->volume_id)); - - // volume_label_string and file_system_type are only valid when - // ebpb_version == DOSBIOSParameterBlockVersion::DOS4. - } - } - - if (ebpb.signature() != signature_1 && ebpb.signature() != signature_2) { - dbgln("FATFS: Invalid signature"); - return EINVAL; - } - - DOS3BIOSParameterBlock const* ebpb_block = ebpb.common_bpb(); - // The number of data area sectors is what DOS/Windows used to determine - // if a partition was a FAT12, FAT16, or FAT32 file system. - // From "FAT Type Determination" section of Microsoft FAT Specification - // (fatgen103.doc): - // The FAT type—one of FAT12, FAT16, or FAT32—is determined by the count - // of clusters on the volume and nothing else. - // - // The following calculations are based on the equations provided in this - // section. - - // "RootDirSectors" from MS FAT Specification. This is calculated as: - // Number of bytes occupied by root directory area (0 on FAT32) - // + - // Bytes to fill final sector (ie, round up) - // Converted into sector count (by dividing by bytes per sector). - u32 root_directory_sectors = ((ebpb_block->root_directory_entry_count * sizeof(FATEntry)) + (ebpb_block->bytes_per_sector - 1)) / ebpb_block->bytes_per_sector; - - // "DataSec" from MS FAT Specification. - u32 data_area_sectors = ebpb.sector_count() - ((ebpb_block->reserved_sector_count) + (ebpb_block->fat_count * ebpb.sectors_per_fat()) + root_directory_sectors); - - // CountofClusters from MS FAT Specification. - u32 data_area_clusters = data_area_sectors / ebpb_block->sectors_per_cluster; - - // Cluster thresholds and operators as defined in MS FAT Specification. - if (data_area_clusters < 4085) { - dbgln("FATFS: Detected FAT12 with {} data area clusters", data_area_clusters); - m_fat_version = FATVersion::FAT12; - } else if (data_area_clusters < 65525) { - dbgln("FATFS: Detected FAT16 with {} data area clusters", data_area_clusters); - m_fat_version = FATVersion::FAT16; - } else { - dbgln("FATFS: Assuming FAT32 with {} data area clusters", data_area_clusters); - m_fat_version = FATVersion::FAT32; - } - - m_device_block_size = ebpb_block->bytes_per_sector; - set_logical_block_size(m_device_block_size); - - m_first_data_sector = block->reserved_sector_count + (block->fat_count * ebpb.sectors_per_fat()) + root_directory_sectors; - - TRY(BlockBasedFileSystem::initialize_while_locked()); - - FATEntry root_entry {}; - - if (m_fat_version == FATVersion::FAT32) { - // FAT32 stores the root directory within the FAT (at the clusters specified - // in the boot record), as opposed to the root directory area - // (as done by FAT 12/16). - - DOS7BIOSParameterBlock const* boot_record = ebpb.dos7_bpb(); - // Ensure we have a DOS7 BPB (so that we can find the root directory cluster). - if (boot_record == nullptr) { - dbgln("FATFS: Non-DOS7 BPB for FAT32 FS."); - return EINVAL; - } - root_entry.first_cluster_low = boot_record->root_directory_cluster & 0xFFFF; - root_entry.first_cluster_high = boot_record->root_directory_cluster >> 16; - } else { - // FAT12/FAT16. - // Use cluster = 0 as a signal to `first_block_of_cluster()` to look in the - // root directory area for the root entry. - // Clusters 0 and 1 hold special values, and will never be used to store file - // data. - root_entry.first_cluster_low = 0; - root_entry.first_cluster_high = 0; - } - - root_entry.attributes = FATAttributes::Directory; - m_root_inode = TRY(FATInode::create(*this, root_entry, { 0, 1 })); - - if (m_fat_version == FATVersion::FAT32) { - auto fs_info_buffer = UserOrKernelBuffer::for_kernel_buffer(bit_cast(&m_fs_info)); - // We know that there is a DOS7 BPB, because if it wasn't present - // we would have returned EINVAL above. - TRY(read_block(ebpb.dos7_bpb()->fs_info_sector, &fs_info_buffer, sizeof(m_fs_info))); - - if (m_fs_info.lead_signature != fs_info_signature_1 || m_fs_info.struct_signature != fs_info_signature_2 || m_fs_info.trailing_signature != fs_info_signature_3) { - dbgln("FATFS: Invalid FSInfo struct signature"); - dbgln_if(FAT_DEBUG, "FATFS: FSInfo signature1: {:#x}, expected: {:#x}", m_fs_info.lead_signature, fs_info_signature_1); - dbgln_if(FAT_DEBUG, "FATFS: FSInfo signature2: {:#x}, expected: {:#x}", m_fs_info.struct_signature, fs_info_signature_2); - dbgln_if(FAT_DEBUG, "FATFS: FSInfo signature3: {:#x}, expected: {:#x}", m_fs_info.trailing_signature, fs_info_signature_3); - return Error::from_errno(EINVAL); - } - - dbgln_if(FAT_DEBUG, "FATFS: fs_info.last_known_free_cluster_count: {}", m_fs_info.last_known_free_cluster_count); - dbgln_if(FAT_DEBUG, "FATFS: fs_info.next_free_cluster_hint: {}", m_fs_info.next_free_cluster_hint); - } - - return {}; -} - -Inode& FATFS::root_inode() -{ - return *m_root_inode; -} - -FatBlockSpan FATFS::first_block_of_cluster(u32 cluster) const -{ - // For FAT12/16, we use a value of cluster 0 to indicate this is a cluster for the root directory. - // Cluster 0 and cluster 1 hold special values (cluster 0 holds the FAT ID, and cluster 1 - // the "end of chain marker"), neither of which will be present in the table or associated - // with any file. - // "Entries with the Volume Label flag, subdirectory ".." pointing to the FAT12 and FAT16 root, and empty files with size 0 should have first cluster 0." - // --Wikipedia - // - DOSBIOSParameterBlock ebpb(m_boot_record); - DOS3BIOSParameterBlock const* ebpb_block = ebpb.common_bpb(); - if (m_fat_version != FATVersion::FAT32 && cluster == 0) { - // Root directory area follows the FATs after the reserved sectors. - return FatBlockSpan { - ebpb_block->reserved_sector_count + (ebpb_block->fat_count * ebpb.sectors_per_fat()), - (ebpb_block->root_directory_entry_count * sizeof(FATEntry)) / ebpb_block->bytes_per_sector - }; - } else { - return FatBlockSpan { - ((cluster - first_data_cluster) * ebpb_block->sectors_per_cluster) + m_first_data_sector, - ebpb_block->sectors_per_cluster - }; - } -} - -size_t FATFS::fat_offset_for_cluster(u32 cluster) const -{ - switch (m_fat_version) { - case FATVersion::FAT12: { - // In FAT12, a cluster entry is stored in a byte, plus - // the low/high nibble of an adjacent byte. - // - // CLSTR: 0 1 2 3 4 5 - // INDEX: [0 1 2], [3 4 5], [6 7 8] - - // Every 2 clusters are represented using 3 bytes. - return (cluster * 3) / 2; - } break; - case FATVersion::FAT16: - return cluster * 2; // Each cluster is stored in 2 bytes. - case FATVersion::FAT32: - return cluster * 4; // Each cluster is stored in 4 bytes. - default: - VERIFY_NOT_REACHED(); - } -} - -u32 FATFS::cluster_number(KBuffer const& fat_sector, u32 entry_cluster_number, u32 entry_offset) const -{ - u32 cluster = 0; - switch (m_fat_version) { - case FATVersion::FAT12: { - u16 fat12_bytes_le = 0; - // Two FAT12 entries get stored in a total of 3 bytes, as follows: - // AB CD EF are grouped as [D AB] and [E FC] (little-endian). - // For a given cluster, we interpret the associated 2 bytes as a little-endian - // 16-bit value ({CD AB} or {EF CD}), and then shift/mask the extra high or low nibble. - ByteReader::load(fat_sector.bytes().offset(entry_offset), fat12_bytes_le); - cluster = AK::convert_between_host_and_little_endian(fat12_bytes_le); - if (entry_cluster_number % 2 == 0) { - // CD AB -> D AB - cluster &= 0x0FFF; - } else { - // EF CD -> E FC. - cluster = cluster >> 4; - } - break; - } - case FATVersion::FAT16: { - u16 cluster_u16_le = 0; - ByteReader::load(fat_sector.bytes().offset(entry_offset), cluster_u16_le); - cluster = AK::convert_between_host_and_little_endian(cluster_u16_le); - break; - } - case FATVersion::FAT32: { - u32 cluster_u32_le = 0; - ByteReader::load(fat_sector.bytes().offset(entry_offset), cluster_u32_le); - cluster = AK::convert_between_host_and_little_endian(cluster_u32_le); - // FAT32 entries use 28-bits to represent the cluster number. The top 4 bits - // may contain flags or other data and must be masked off. - cluster &= 0x0FFFFFFF; - break; - } - default: - VERIFY_NOT_REACHED(); - } - return cluster; -} - -u32 FATFS::end_of_chain_marker() const -{ - // Returns the end of chain entry for the given file system. - // Any FAT entry of this value or greater signifies the end - // of the chain has been reached for a given entry. - switch (m_fat_version) { - case FATVersion::FAT12: - return 0xFF8; - case FATVersion::FAT16: - return 0xFFF8; - case FATVersion::FAT32: - return 0x0FFFFFF8; - default: - VERIFY_NOT_REACHED(); - } -} - -ErrorOr FATFS::set_free_cluster_count(u32 value) -{ - VERIFY(m_fat_version == FATVersion::FAT32); - - m_fs_info.last_known_free_cluster_count = value; - auto fs_info_buffer = UserOrKernelBuffer::for_kernel_buffer(bit_cast(&m_fs_info)); - TRY(write_block(m_parameter_block->dos7_bpb()->fs_info_sector, fs_info_buffer, sizeof(m_fs_info))); - - return {}; -} - -ErrorOr FATFS::allocate_cluster() -{ - u32 start_cluster; - if (m_fat_version == FATVersion::FAT32) { - // If we have a hint, start there. - if (m_fs_info.next_free_cluster_hint != fs_info_data_unknown) { - start_cluster = m_fs_info.next_free_cluster_hint; - } else { - // Otherwise, start at the beginning of the data area. - start_cluster = first_data_cluster; - } - } else { - // For FAT12/16, start at the beginning of the data area, as there is no - // FSInfo struct to store the hint. - start_cluster = first_data_cluster; - } - - MutexLocker locker(m_lock); - - for (u32 i = start_cluster; i < m_parameter_block->sector_count() / m_parameter_block->common_bpb()->sectors_per_cluster; i++) { - if (TRY(fat_read(i)) == 0) { - dbgln_if(FAT_DEBUG, "FATFS: Allocating cluster {}", i); - - if (m_fat_version == FATVersion::FAT32 && m_fs_info.last_known_free_cluster_count != fs_info_data_unknown) - TRY(set_free_cluster_count(m_fs_info.last_known_free_cluster_count - 1)); - - TRY(fat_write(i, end_of_chain_marker())); - return i; - } - } - - return Error::from_errno(ENOSPC); -} - -ErrorOr FATFS::notify_cluster_freed() -{ - if (m_fat_version == FATVersion::FAT32 && m_fs_info.last_known_free_cluster_count != fs_info_data_unknown) - TRY(set_free_cluster_count(m_fs_info.last_known_free_cluster_count + 1)); - - return {}; -} - -ErrorOr FATFS::fat_read(u32 cluster) -{ - dbgln_if(FAT_DEBUG, "FATFS: Reading FAT entry for cluster {}", cluster); - - u32 fat_offset = fat_offset_for_cluster(cluster); - u32 fat_sector_index = m_parameter_block->common_bpb()->reserved_sector_count + (fat_offset / m_device_block_size); - u32 entry_offset = fat_offset % m_device_block_size; - - // NOTE: On FAT12, FATs aren't necessarily block aligned, so in the worst case we have to read - // an extra byte from the next block. - bool read_extra_block = m_fat_version == FATVersion::FAT12 && entry_offset == m_device_block_size - 1; - size_t buffer_size = m_device_block_size; - if (read_extra_block) - buffer_size += m_device_block_size; - - auto fat_sector = TRY(KBuffer::try_create_with_size("FATFS: FAT read buffer"sv, buffer_size)); - auto fat_sector_buffer = UserOrKernelBuffer::for_kernel_buffer(fat_sector->data()); - - MutexLocker locker(m_lock); - - if (read_extra_block) - TRY(read_blocks(fat_sector_index, 2, fat_sector_buffer)); - else - TRY(read_block(fat_sector_index, &fat_sector_buffer, m_device_block_size)); - - // Look up the next cluster to read, or read End of Chain marker from table. - return cluster_number(*fat_sector, cluster, entry_offset); -} - -ErrorOr FATFS::fat_write(u32 cluster, u32 value) -{ - dbgln_if(FAT_DEBUG, "FATFS: Writing FAT entry for cluster {} with value {}", cluster, value); - - u32 fat_offset = fat_offset_for_cluster(cluster); - u32 fat_sector_index = m_parameter_block->common_bpb()->reserved_sector_count + (fat_offset / m_device_block_size); - u32 entry_offset = fat_offset % m_device_block_size; - - // See the comment in fat_read(). - bool need_extra_block = m_fat_version == FATVersion::FAT12 && entry_offset == m_device_block_size - 1; - size_t buffer_size = m_device_block_size; - if (need_extra_block) - buffer_size += m_device_block_size; - - auto fat_sector = TRY(KBuffer::try_create_with_size("FATFS: FAT read buffer"sv, buffer_size)); - auto fat_sector_buffer = UserOrKernelBuffer::for_kernel_buffer(fat_sector->data()); - - MutexLocker locker(m_lock); - - if (need_extra_block) - TRY(read_blocks(fat_sector_index, 2, fat_sector_buffer)); - else - TRY(read_block(fat_sector_index, &fat_sector_buffer, m_device_block_size)); - - switch (m_fat_version) { - case FATVersion::FAT12: { - auto write_misaligned_u16 = [&](u16 word) { - *bit_cast(&fat_sector->data()[entry_offset]) = word & 0xFF; - *(bit_cast(&fat_sector->data()[entry_offset]) + 1) = word >> 8; - }; - u16 existing_bytes_le = 0; - ByteReader::load(fat_sector->bytes().offset(entry_offset), existing_bytes_le); - u16 existing_bytes = AK::convert_between_host_and_little_endian(existing_bytes_le); - if (cluster % 2 == 0) { - existing_bytes &= 0xF000; - existing_bytes |= static_cast(value) & 0xFFF; - } else { - existing_bytes &= 0x000F; - existing_bytes |= static_cast(value) << 4; - } - write_misaligned_u16(AK::convert_between_host_and_little_endian(existing_bytes)); - break; - } - case FATVersion::FAT16: { - *bit_cast(&fat_sector->data()[entry_offset]) = AK::convert_between_host_and_little_endian(static_cast(value)); - break; - } - case FATVersion::FAT32: { - *bit_cast(&fat_sector->data()[entry_offset]) = AK::convert_between_host_and_little_endian(value); - break; - } - } - - if (need_extra_block) - TRY(write_blocks(fat_sector_index, 2, fat_sector_buffer)); - else - TRY(write_block(fat_sector_index, fat_sector_buffer, m_device_block_size)); - - return {}; -} - -u8 FATFS::internal_file_type_to_directory_entry_type(DirectoryEntryView const& entry) const -{ - FATAttributes attrib = static_cast(entry.file_type); - if (has_flag(attrib, FATAttributes::Directory)) { - return DT_DIR; - } else if (has_flag(attrib, FATAttributes::VolumeID)) { - return DT_UNKNOWN; - } else { - // ReadOnly, Hidden, System, Archive, LongFileName. - return DT_REG; - } - return DT_UNKNOWN; -} - -} diff --git a/Kernel/FileSystem/FATFS/FileSystem.h b/Kernel/FileSystem/FATFS/FileSystem.h deleted file mode 100644 index fa211fbdd3d..00000000000 --- a/Kernel/FileSystem/FATFS/FileSystem.h +++ /dev/null @@ -1,116 +0,0 @@ -/* - * Copyright (c) 2022-2024, Undefine - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include -#include -#include -#include - -namespace Kernel { - -class DOSBIOSParameterBlock final { -private: - KBuffer const* const m_boot_record; - const struct DOS4BIOSParameterBlock* m_dos4_block; - const struct DOS7BIOSParameterBlock* m_dos7_block; - const struct DOS3BIOSParameterBlock* m_common_block; - -public: - DOSBIOSParameterBlock(KBuffer const* const boot_record) - : m_boot_record(boot_record) - , m_dos4_block(reinterpret_cast(boot_record->bytes().offset_pointer(0x024))) - , m_dos7_block(reinterpret_cast(boot_record->bytes().offset_pointer(0x024))) - , m_common_block(reinterpret_cast(boot_record->bytes().data())) - { - } - - DOSBIOSParameterBlockVersion bpb_version() const; - - DOS3BIOSParameterBlock const* common_bpb() const; - - DOS4BIOSParameterBlock const* dos4_bpb() const; - - DOS7BIOSParameterBlock const* dos7_bpb() const; - - u16 sectors_per_fat() const; - - u32 sector_count() const; - - u8 signature() const; -}; - -// Represents a block of contiguous sectors to read. This typically represents a -// cluster, but is also used to define areas of the root directory region. -struct FatBlockSpan { - BlockBasedFileSystem::BlockIndex start_block; - size_t number_of_sectors; -}; - -class FATFS final : public BlockBasedFileSystem { - friend FATInode; - -public: - static ErrorOr> try_create(OpenFileDescription&, ReadonlyBytes); - - virtual ~FATFS() override = default; - virtual StringView class_name() const override { return "FATFS"sv; } - virtual Inode& root_inode() override; - virtual u8 internal_file_type_to_directory_entry_type(DirectoryEntryView const& entry) const override; - -private: - virtual ErrorOr initialize_while_locked() override; - virtual bool is_initialized_while_locked() override; - // FIXME: This is not a proper way to clear last mount of a FAT filesystem, - // but for now we simply have no other way to properly do it. - virtual ErrorOr prepare_to_clear_last_mount(Inode&) override { return {}; } - - FATFS(OpenFileDescription&); - - static constexpr u8 signature_1 = 0x28; - static constexpr u8 signature_2 = 0x29; - - static constexpr u32 fs_info_signature_1 = 0x41615252; - static constexpr u32 fs_info_signature_2 = 0x61417272; - static constexpr u32 fs_info_signature_3 = 0xAA550000; - - static constexpr u32 fs_info_data_unknown = 0xFFFFFFFF; - - static constexpr u32 first_data_cluster = 2; - - FatBlockSpan first_block_of_cluster(u32 cluster) const; - - ErrorOr set_free_cluster_count(u32); - ErrorOr allocate_cluster(); - ErrorOr notify_cluster_freed(); - - size_t fat_offset_for_cluster(u32 cluster) const; - - // Reads the cluster number located at the offset within the table. - u32 cluster_number(KBuffer const& fat_sector, u32 entry_cluster_number, u32 entry_offset) const; - - // Returns cluster number value that indicates the end of the chain - // has been reached. Any cluster value >= this value indicates this - // is the last cluster. - u32 end_of_chain_marker() const; - - ErrorOr fat_read(u32 cluster); - ErrorOr fat_write(u32 cluster, u32 value); - - OwnPtr m_boot_record; - FAT32FSInfo m_fs_info; - OwnPtr m_parameter_block; - RefPtr m_root_inode; - u32 m_first_data_sector { 0 }; - FATVersion m_fat_version; -}; - -} diff --git a/Kernel/FileSystem/FATFS/Inode.cpp b/Kernel/FileSystem/FATFS/Inode.cpp deleted file mode 100644 index 23eca07da97..00000000000 --- a/Kernel/FileSystem/FATFS/Inode.cpp +++ /dev/null @@ -1,804 +0,0 @@ -/* - * Copyright (c) 2022-2024, Undefine - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include - -namespace Kernel { - -ErrorOr> FATInode::create(FATFS& fs, FATEntry entry, FATEntryLocation inode_metadata_location, Vector const& lfn_entries) -{ - auto filename = TRY(compute_filename(entry, lfn_entries)); - u32 entry_first_cluster = entry.first_cluster_low; - if (fs.m_fat_version == FATVersion::FAT32) - entry_first_cluster |= (static_cast(entry.first_cluster_high) << 16); - auto inode = TRY(adopt_nonnull_ref_or_enomem(new (nothrow) FATInode(fs, entry, inode_metadata_location, move(filename)))); - MutexLocker locker(inode->m_inode_lock); - inode->m_cluster_list = TRY(inode->compute_cluster_list(fs, entry_first_cluster)); - return inode; -} - -FATInode::FATInode(FATFS& fs, FATEntry entry, FATEntryLocation inode_metadata_location, NonnullOwnPtr filename) - : Inode(fs, first_cluster(fs.m_fat_version)) - , m_entry(entry) - , m_inode_metadata_location(inode_metadata_location) - , m_filename(move(filename)) -{ - dbgln_if(FAT_DEBUG, "FATInode[{}]::FATInode(): Creating inode with filename \"{}\"", identifier(), m_filename); -} - -ErrorOr> FATInode::compute_cluster_list(FATFS& fs, u32 first_cluster) -{ - VERIFY(m_inode_lock.is_locked()); - - dbgln_if(FAT_DEBUG, "FATInode::compute_cluster_list(): computing block list starting with cluster {}", first_cluster); - - u32 cluster = first_cluster; - - Vector cluster_list; - - while (cluster < fs.end_of_chain_marker()) { - dbgln_if(FAT_DEBUG, "FATInode::compute_cluster_list(): Appending cluster {} to cluster chain starting with {}", cluster, first_cluster); - - TRY(cluster_list.try_append(cluster)); - - // Clusters 0 and 1 are reserved in the FAT, and their entries in the FAT will - // not point to another valid cluster in the chain (Cluster 0 typically holds - // the "FAT ID" field with some flags, Cluster 1 should be the end of chain - // marker). - // Internally, we use `cluster == 0` to represent the root directory Inode, - // which is a signal to read the root directory region blocks on FAT12/16 - // file systems. (`fs().first_block_of_cluster` will return the appropriate - // block/sectors to read given cluster == 0). - // Therefore, we read one set of sectors for these invalud cluster numbers, - // and then terminate the loop becuase the FAT entry at `cluster` for these - // values does not represent the next step in the chain (because there is - // nothing else to read). - if (cluster <= 1) { - break; - } - - // Look up the next cluster to read, or read End of Chain marker from table. - cluster = TRY(fs.fat_read(cluster)); - } - - return cluster_list; -} - -u8 FATInode::lfn_entry_checksum(FATEntry const& entry) -{ - u8 checksum = entry.filename[0]; - for (size_t i = 1; i < normal_filename_length; i++) - checksum = (checksum << 7) + (checksum >> 1) + entry.filename[i]; - for (size_t i = 0; i < normal_extension_length; i++) - checksum = (checksum << 7) + (checksum >> 1) + entry.extension[i]; - return checksum; -} - -void FATInode::create_83_filename_for(FATEntry& entry, StringView name) -{ - // FIXME: Implement the correct algorithm based on 3.2.4 from http://www.osdever.net/documents/LongFileName.pdf - for (size_t i = 0; i < min(name.length(), normal_filename_length); i++) - entry.filename[i] = to_ascii_uppercase(name[i]); -} - -ErrorOr> FATInode::create_lfn_entries(StringView name, u8 checksum) -{ - u32 lfn_entry_count = ceil_div(name.length(), characters_per_lfn_entry); - - Vector lfn_entries; - TRY(lfn_entries.try_ensure_capacity(lfn_entry_count)); - - auto characters_left = name.length(); - - for (u32 i = 0; i < lfn_entry_count; i++) { - FATLongFileNameEntry lfn_entry {}; - - size_t characters_in_part = min(characters_left, lfn_entry_characters_part_1_length); - - for (size_t j = 0; j < characters_in_part; j++) { - lfn_entry.characters1[j] = name[name.length() - characters_left]; - characters_left--; - } - - if (characters_left > 0) { - characters_in_part = min(characters_left, lfn_entry_characters_part_2_length); - - for (size_t j = 0; j < characters_in_part; j++) { - lfn_entry.characters2[j] = name[name.length() - characters_left]; - characters_left--; - } - } - - if (characters_left > 0) { - characters_in_part = min(characters_left, lfn_entry_characters_part_3_length); - - for (size_t j = 0; j < characters_in_part; j++) { - lfn_entry.characters3[j] = name[name.length() - characters_left]; - characters_left--; - } - } - - lfn_entry.entry_index = (i + 1) | (i + 1 == lfn_entry_count ? last_lfn_entry_mask : 0); - lfn_entry.checksum = checksum; - lfn_entry.attributes = FATAttributes::LongFileName; - - lfn_entries.unchecked_append(lfn_entry); - } - - return lfn_entries; -} - -ErrorOr> FATInode::get_block_list() -{ - VERIFY(m_inode_lock.is_locked()); - - dbgln_if(FAT_DEBUG, "FATInode[{}]::get_block_list(): getting block list", identifier()); - - Vector block_list; - - for (auto cluster : m_cluster_list) { - auto span = fs().first_block_of_cluster(cluster); - for (size_t i = 0; i < span.number_of_sectors; i++) { - dbgln_if(FAT_DEBUG, "FATInode[{}]::get_block_list(): Appending block {} to block list", identifier(), BlockBasedFileSystem::BlockIndex { span.start_block.value() + i }); - TRY(block_list.try_append(BlockBasedFileSystem::BlockIndex { span.start_block.value() + i })); - } - } - - return block_list; -} - -ErrorOr> FATInode::read_block_list() -{ - VERIFY(m_inode_lock.is_locked()); - - auto block_list = TRY(get_block_list()); - - dbgln_if(FAT_DEBUG, "FATInode[{}]::read_block_list(): reading block list ({} blocks)", identifier(), block_list.size()); - - auto builder = TRY(KBufferBuilder::try_create()); - - u8 buffer[512]; - VERIFY(fs().m_device_block_size <= sizeof(buffer)); - auto buf = UserOrKernelBuffer::for_kernel_buffer(buffer); - - for (BlockBasedFileSystem::BlockIndex block : block_list) { - dbgln_if(FAT_DEBUG, "FATInode[{}]::read_block_list(): reading block: {}", identifier(), block); - TRY(fs().read_block(block, &buf, sizeof(buffer))); - TRY(builder.append((char const*)buffer, fs().m_device_block_size)); - } - - auto blocks = builder.build(); - if (!blocks) - return ENOMEM; - return blocks.release_nonnull(); -} - -ErrorOr FATInode::replace_child(StringView name, Inode& inode) -{ - // FIXME: Implement this properly - TRY(remove_child(name)); - TRY(add_child(inode, name, inode.mode())); - return {}; -} - -ErrorOr> FATInode::traverse(Function(RefPtr)> callback) -{ - VERIFY(has_flag(m_entry.attributes, FATAttributes::Directory)); - - Vector lfn_entries; - auto blocks = TRY(read_block_list()); - - u32 bytes_per_cluster = fs().m_device_block_size * fs().m_parameter_block->common_bpb()->sectors_per_cluster; - - for (u32 i = 0; i < blocks->size() / sizeof(FATEntry); i++) { - auto* entry = reinterpret_cast(blocks->data() + i * sizeof(FATEntry)); - if (entry->filename[0] == end_entry_byte) { - dbgln_if(FAT_DEBUG, "FATInode[{}]::traverse(): Found end entry", identifier()); - return nullptr; - } else if (static_cast(entry->filename[0]) == unused_entry_byte) { - dbgln_if(FAT_DEBUG, "FATInode[{}]::traverse(): Found unused entry", identifier()); - lfn_entries.clear(); - } else if (entry->attributes == FATAttributes::LongFileName) { - dbgln_if(FAT_DEBUG, "FATInode[{}]::traverse(): Found LFN entry", identifier()); - TRY(lfn_entries.try_append(*reinterpret_cast(entry))); - } else if ((entry->first_cluster_high << 16 | entry->first_cluster_low) <= 1 && entry->file_size > 0) { - // Because clusters 0 and 1 are reserved, only empty files (size == 0 files) - // should specify these clusters. - // This driver uses a cluster number == 0 to represent the root directory inode - // on FAT12/16 file systems (a signal to look in the root directory region), - // so we ensure that no entries read off the file system have a cluster number - // that would also point to this region. - dbgln_if(FAT_DEBUG, "FATInode[{}]::traverse(): Invalid cluster for entry", identifier()); - return EINVAL; - } else { - auto entry_number_bytes = i * sizeof(FATEntry); - auto cluster = m_cluster_list[entry_number_bytes / bytes_per_cluster]; - auto block = BlockBasedFileSystem::BlockIndex { fs().first_block_of_cluster(cluster).start_block.value() + (entry_number_bytes % bytes_per_cluster) / fs().m_device_block_size }; - - auto entries_per_sector = fs().m_device_block_size / sizeof(FATEntry); - u32 block_entry = i % entries_per_sector; - - dbgln_if(FAT_DEBUG, "FATInode[{}]::traverse(): Found 8.3 entry at block {}, entry {}", identifier(), block, block_entry); - lfn_entries.reverse(); - auto inode = TRY(FATInode::create(fs(), *entry, { block, block_entry }, lfn_entries)); - if (TRY(callback(inode))) - return inode; - lfn_entries.clear(); - } - } - - return EINVAL; -} - -ErrorOr> FATInode::compute_filename(FATEntry& entry, Vector const& lfn_entries) -{ - if (lfn_entries.is_empty()) { - StringBuilder filename; - TRY(filename.try_append(byte_terminated_string(StringView(entry.filename, normal_filename_length), ' '))); - if (entry.extension[0] != ' ') { - TRY(filename.try_append('.')); - TRY(filename.try_append(byte_terminated_string(StringView(entry.extension, normal_extension_length), ' '))); - } - return TRY(KString::try_create(filename.string_view())); - } else { - StringBuilder filename; - for (auto& lfn_entry : lfn_entries) { - TRY(filename.try_append(lfn_entry.characters1[0])); - TRY(filename.try_append(lfn_entry.characters1[1])); - TRY(filename.try_append(lfn_entry.characters1[2])); - TRY(filename.try_append(lfn_entry.characters1[3])); - TRY(filename.try_append(lfn_entry.characters1[4])); - TRY(filename.try_append(lfn_entry.characters2[0])); - TRY(filename.try_append(lfn_entry.characters2[1])); - TRY(filename.try_append(lfn_entry.characters2[2])); - TRY(filename.try_append(lfn_entry.characters2[3])); - TRY(filename.try_append(lfn_entry.characters2[4])); - TRY(filename.try_append(lfn_entry.characters2[5])); - TRY(filename.try_append(lfn_entry.characters3[0])); - TRY(filename.try_append(lfn_entry.characters3[1])); - } - - // Long Filenames have two terminators: - // 1. Completely unused "entries" (the `characterN` fields of - // `lfn_entry`) are filled with 0xFF (`lfn_entry_unused_byte`). - // 2. Partially used entries (within `characterN`) are null-padded. - // - // `filename` is truncated first to eliminate unused entries, and - // then further truncated to remove any existing null padding characters. - // - // Page 8 of the Long Filename Specification - // (http://www.osdever.net/documents/LongFileName.pdf) - // details this encoding ("If the long name does not fill..."). - return TRY(KString::try_create( - byte_terminated_string( - byte_terminated_string(filename.string_view(), lfn_entry_unused_byte), lfn_entry_character_termination))); - } - - VERIFY_NOT_REACHED(); -} - -StringView FATInode::byte_terminated_string(StringView string, u8 fill_byte) -{ - if (auto index = string.find_last_not(fill_byte); index.has_value()) - return string.substring_view(0, index.value() + 1); - return string; -} - -u32 FATInode::first_cluster() const -{ - return first_cluster(fs().m_fat_version); -} - -u32 FATInode::first_cluster(FATVersion const version) const -{ - if (version == FATVersion::FAT32) { - return (static_cast(m_entry.first_cluster_high) << 16) | m_entry.first_cluster_low; - } - // The space occupied in a directory entry by `first_cluster_high` (0x14) - // is reserved in FAT12/16, and may be used to store file meta-data. - // As a result, do not include it on FAT12/16 file systems. - return m_entry.first_cluster_low; -} - -ErrorOr FATInode::allocate_and_add_cluster_to_chain() -{ - VERIFY(m_inode_lock.is_locked()); - - u32 allocated_cluster = TRY(fs().allocate_cluster()); - dbgln_if(FAT_DEBUG, "FATInode[{}]::allocate_and_add_cluster_to_chain(): allocated cluster {}", identifier(), allocated_cluster); - - if (m_cluster_list.is_empty() || (m_cluster_list.size() == 1 && first_cluster() <= 1)) { - // This is the first cluster in the chain, so update the inode metadata. - if (fs().m_fat_version == FATVersion::FAT32) { - // Only FAT32 uses the `first_cluster_high` field. - m_entry.first_cluster_high = allocated_cluster >> 16; - } - - m_entry.first_cluster_low = allocated_cluster & 0xFFFF; - - set_metadata_dirty(true); - } else { - // This is not the first cluster in the chain, so we need to update the - // FAT entry for the last cluster in the chain to point to the newly - // allocated cluster. - TRY(fs().fat_write(m_cluster_list[m_cluster_list.size() - 1], allocated_cluster)); - } - - m_cluster_list.append(allocated_cluster); - - return {}; -} - -ErrorOr FATInode::remove_last_cluster_from_chain() -{ - VERIFY(m_inode_lock.is_locked()); - VERIFY(m_cluster_list.size() > 0); - - u32 last_cluster = m_cluster_list[m_cluster_list.size() - 1]; - TRY(fs().fat_write(last_cluster, 0x0)); - - dbgln_if(FAT_DEBUG, "FATInode[{}]::remove_last_cluster_from_chain(): freeing cluster {}", identifier(), last_cluster); - - TRY(fs().notify_cluster_freed()); - m_cluster_list.remove(m_cluster_list.size() - 1); - - if (m_cluster_list.is_empty() || (m_cluster_list.size() == 1 && first_cluster() <= 1)) { - // We have removed the last cluster in the chain, so update the inode metadata. - if (fs().m_fat_version == FATVersion::FAT32) { - // Only FAT32 uses the `first_cluster_high` field. - m_entry.first_cluster_high = 0; - } - - m_entry.first_cluster_low = 0; - - set_metadata_dirty(true); - } else { - // We have removed a cluster from the chain, so update the FAT entry for - // the last cluster in the chain mark it as the end of the chain. - last_cluster = m_cluster_list[m_cluster_list.size() - 1]; - TRY(fs().fat_write(last_cluster, fs().end_of_chain_marker())); - } - - return {}; -} - -ErrorOr> FATInode::allocate_entries(u32 count) -{ - // FIXME: This function ignores unused entries, we should make use of them - // FIXME: If we fail anywhere here, we should make sure the end entry is at the correct location - - auto blocks = TRY(read_block_list()); - auto entries = bit_cast(blocks->data()); - - auto const entries_per_block = fs().logical_block_size() / sizeof(FATEntry); - - auto block_list = TRY(get_block_list()); - - Vector locations; - TRY(locations.try_ensure_capacity(count)); - - for (u32 current_entry_index = 0; current_entry_index < blocks->size() / sizeof(FATEntry); current_entry_index++) { - auto& entry = entries[current_entry_index]; - if (entry.filename[0] != end_entry_byte) - continue; - - while (current_entry_index < blocks->size() / sizeof(FATEntry) && locations.size() < count) { - u32 chosen_block_index = current_entry_index / entries_per_block; - u32 chosen_entry_index = current_entry_index % entries_per_block; - locations.unchecked_append({ block_list[chosen_block_index], chosen_entry_index }); - dbgln_if(FAT_DEBUG, "FATInode[{}]::allocate_entries(): allocated new entry at block {}, offset {}", identifier(), block_list[chosen_block_index], chosen_entry_index); - current_entry_index++; - } - if (locations.size() == count) { - u32 block_index = current_entry_index / entries_per_block; - u32 entry_index = current_entry_index % entries_per_block; - dbgln_if(FAT_DEBUG, "FATInode[{}]::allocate_entries(): putting new end entry at block {}, offset {}", identifier(), block_list[block_index], entry_index); - - FATEntry end_entry {}; - end_entry.filename[0] = end_entry_byte; - TRY(fs().write_block(block_list[block_index], UserOrKernelBuffer::for_kernel_buffer(bit_cast(&end_entry)), sizeof(FATEntry), entry_index * sizeof(FATEntry))); - break; - } - } - - if (locations.size() < count) { - TRY(allocate_and_add_cluster_to_chain()); - u32 new_block_index = block_list.size() - fs().m_parameter_block->common_bpb()->sectors_per_cluster - 1; - u32 entry_index; - for (entry_index = 0; entry_index < count - locations.size(); entry_index++) { - locations.unchecked_append({ block_list[new_block_index], entry_index }); - dbgln_if(FAT_DEBUG, "FATInode[{}]::allocate_entries(): allocated new entry at block {}, offset {}", identifier(), block_list[new_block_index], entry_index); - } - - dbgln_if(FAT_DEBUG, "FATInode[{}]::allocate_entries(): putting new end entry at block {}, offset {}", identifier(), block_list[new_block_index], entry_index); - - FATEntry end_entry {}; - end_entry.filename[0] = end_entry_byte; - TRY(fs().write_block(block_list[new_block_index], UserOrKernelBuffer::for_kernel_buffer(bit_cast(&end_entry)), sizeof(FATEntry), entry_index * sizeof(FATEntry))); - } - - return locations; -} - -ErrorOr FATInode::read_bytes_locked(off_t offset, size_t size, UserOrKernelBuffer& buffer, OpenFileDescription*) const -{ - dbgln_if(FAT_DEBUG, "FATInode[{}]::read_bytes_locked(): Reading {} bytes at offset {}", identifier(), size, offset); - VERIFY(offset >= 0); - if (offset >= m_entry.file_size) - return 0; - - auto block_list = TRY(const_cast(*this).get_block_list()); - - u32 first_block_index = offset / fs().m_device_block_size; - u32 last_block_index = (offset + size - 1) / fs().m_device_block_size; - - size_t offset_into_first_block = offset - first_block_index * fs().m_device_block_size; - - size_t nread = 0; - size_t remaining_count = size; - for (u32 block_index = first_block_index; block_index <= last_block_index; ++block_index) { - size_t offset_into_block = block_index == first_block_index ? offset_into_first_block : 0; - size_t to_read = min(fs().m_device_block_size - offset_into_block, remaining_count); - auto buffer_offset = buffer.offset(nread); - - dbgln_if(FAT_DEBUG, "FATInode[{}]::read_bytes_locked(): Reading {} byte(s) from block {} at offset {}", identifier(), to_read, block_list[block_index], offset_into_block); - - TRY(fs().read_block(block_list[block_index], &buffer_offset, to_read, offset_into_block)); - - nread += to_read; - remaining_count -= to_read; - } - - return size; -} - -InodeMetadata FATInode::metadata() const -{ - return { - .inode = identifier(), - .size = m_entry.file_size, - // FIXME: Linux also removes the write permission if the file has the read only attribute set. - .mode = static_cast((has_flag(m_entry.attributes, FATAttributes::Directory) ? S_IFDIR : S_IFREG) | 0777), - .uid = 0, - .gid = 0, - .link_count = 0, - .atime = time_from_packed_dos(m_entry.last_accessed_date, { 0 }), - .ctime = time_from_packed_dos(m_entry.creation_date, m_entry.creation_time), - .mtime = time_from_packed_dos(m_entry.modification_date, m_entry.modification_time), - .dtime = {}, - .block_count = m_cluster_list.size() * fs().m_parameter_block->common_bpb()->sectors_per_cluster, - .block_size = fs().m_device_block_size, - .major_device = 0, - .minor_device = 0, - }; -} - -ErrorOr FATInode::traverse_as_directory(Function(FileSystem::DirectoryEntryView const&)> callback) const -{ - MutexLocker locker(m_inode_lock); - - VERIFY(has_flag(m_entry.attributes, FATAttributes::Directory)); - - [[maybe_unused]] auto inode = TRY(const_cast(*this).traverse([&callback](auto inode) -> ErrorOr { - TRY(callback({ inode->m_filename->view(), inode->identifier(), static_cast(inode->m_entry.attributes) })); - return false; - })); - - return {}; -} - -ErrorOr> FATInode::lookup(StringView name) -{ - MutexLocker locker(m_inode_lock); - - VERIFY(has_flag(m_entry.attributes, FATAttributes::Directory)); - - auto inode = TRY(traverse([name](auto child) -> ErrorOr { - return child->m_filename->view() == name; - })); - - if (inode.is_null()) - return ENOENT; - return inode.release_nonnull(); -} - -ErrorOr FATInode::write_bytes_locked(off_t offset, size_t size, UserOrKernelBuffer const& buffer, OpenFileDescription*) -{ - dbgln_if(FAT_DEBUG, "FATInode[{}]::write_bytes_locked(): Writing size: {} offset: {}", identifier(), size, offset); - - u32 new_size = max(m_entry.file_size, offset + size); - if (new_size != m_entry.file_size) - TRY(resize(new_size)); - - auto block_list = TRY(get_block_list()); - - u32 first_block_index = offset / fs().m_device_block_size; - u32 last_block_index = (offset + size - 1) / fs().m_device_block_size; - - size_t offset_into_first_block = offset - first_block_index * fs().m_device_block_size; - - size_t nwritten = 0; - size_t remaining_count = size; - for (u32 block_index = first_block_index; block_index <= last_block_index; ++block_index) { - size_t offset_into_block = block_index == first_block_index ? offset_into_first_block : 0; - - size_t to_write = min(fs().m_device_block_size - offset_into_block, remaining_count); - dbgln_if(FAT_DEBUG, "FATInode[{}]::write_bytes_locked(): Writing {} byte(s) to block {} at offset {}", identifier(), to_write, block_list[block_index], offset_into_block); - - TRY(fs().write_block(block_list[block_index], buffer.offset(nwritten), to_write, offset_into_block)); - - nwritten += to_write; - remaining_count -= to_write; - } - - return size; -} - -ErrorOr> FATInode::create_child(StringView name, mode_t mode, dev_t, UserID, GroupID) -{ - VERIFY(has_flag(m_entry.attributes, FATAttributes::Directory)); - - dbgln_if(FAT_DEBUG, "FATInode[{}]::create_child(): creating inode \"{}\"", identifier(), name); - - FATEntry entry {}; - create_83_filename_for(entry, name); - - // TODO: We should set the hidden attribute if the file starts with a dot or read only (the same way Linux does this). - if (mode & S_IFDIR) - entry.attributes |= FATAttributes::Directory; - - // FIXME: Set the dates - - // FIXME: For some filenames lfn entries are not necessary - auto lfn_entries = TRY(create_lfn_entries(name, lfn_entry_checksum(entry))); - - MutexLocker locker(m_inode_lock); - - auto entries = TRY(allocate_entries(lfn_entries.size() + 1)); - u32 allocated_cluster = TRY(fs().allocate_cluster()); - if (fs().m_fat_version == FATVersion::FAT32) - entry.first_cluster_high = allocated_cluster >> 16; - - entry.first_cluster_low = allocated_cluster & 0xFFFF; - - if (mode & S_IFDIR) { - auto create_directory_entry = [&](StringView entry_name) { - VERIFY(entry_name.length() <= 8); - FATEntry directory_entry {}; - memset(directory_entry.filename, ' ', 8); - memset(directory_entry.extension, ' ', 3); - for (size_t i = 0; i < entry_name.length(); ++i) - directory_entry.filename[i] = entry_name[i]; - directory_entry.attributes |= FATAttributes::Directory; - return directory_entry; - }; - - FATEntry current_directory = create_directory_entry("."sv); - - current_directory.first_cluster_low = entry.first_cluster_low; - if (fs().m_fat_version == FATVersion::FAT32) - current_directory.first_cluster_high = entry.first_cluster_high; - - FATEntry parent_directory = create_directory_entry(".."sv); - - auto block = BlockBasedFileSystem::BlockIndex { fs().first_block_of_cluster(allocated_cluster).start_block.value() }; - TRY(fs().write_block(block, UserOrKernelBuffer::for_kernel_buffer(bit_cast(¤t_directory)), sizeof(FATEntry), 0)); - TRY(fs().write_block(block, UserOrKernelBuffer::for_kernel_buffer(bit_cast(&parent_directory)), sizeof(FATEntry), sizeof(FATEntry))); - } - - // FIXME: If we fail here we should clean up the entries we wrote - TRY(fs().write_block(entries[lfn_entries.size()].block, UserOrKernelBuffer::for_kernel_buffer(bit_cast(&entry)), sizeof(FATEntry), entries[lfn_entries.size()].entry * sizeof(FATEntry))); - - for (u32 i = 0; i < lfn_entries.size(); i++) { - auto location = entries[lfn_entries.size() - i - 1]; - TRY(fs().write_block(location.block, UserOrKernelBuffer::for_kernel_buffer(bit_cast(&lfn_entries[i])), sizeof(FATLongFileNameEntry), location.entry * sizeof(FATLongFileNameEntry))); - } - - return TRY(FATInode::create(fs(), entry, entries[lfn_entries.size()], lfn_entries)); -} - -ErrorOr FATInode::add_child(Inode& inode, StringView name, mode_t mode) -{ - VERIFY(has_flag(m_entry.attributes, FATAttributes::Directory)); - VERIFY(inode.fsid() == fsid()); - - // FIXME: There's a lot of similar code between this function and create_child, we should try to factor out some of the common code. - - dbgln_if(FAT_DEBUG, "FATInode[{}]::add_child(): appending inode {} as \"{}\"", identifier(), inode.identifier(), name); - - auto entry = bit_cast(&inode)->m_entry; - create_83_filename_for(entry, name); - - // TODO: We should set the hidden attribute if the file starts with a dot or read only (the same way Linux does this). - if (mode & S_IFDIR) - entry.attributes |= FATAttributes::Directory; - - // FIXME: Set the dates - - // FIXME: For some filenames lfn entries are not necessary - auto lfn_entries = TRY(create_lfn_entries(name, lfn_entry_checksum(entry))); - - MutexLocker locker(m_inode_lock); - - auto entries = TRY(allocate_entries(lfn_entries.size() + 1)); - - // FIXME: If we fail here we should clean up the entries we wrote - TRY(fs().write_block(entries[lfn_entries.size()].block, UserOrKernelBuffer::for_kernel_buffer(bit_cast(&entry)), sizeof(FATEntry), entries[lfn_entries.size()].entry * sizeof(FATEntry))); - - for (u32 i = 0; i < lfn_entries.size(); i++) { - auto location = entries[lfn_entries.size() - i - 1]; - TRY(fs().write_block(location.block, UserOrKernelBuffer::for_kernel_buffer(bit_cast(&lfn_entries[i])), sizeof(FATLongFileNameEntry), location.entry * sizeof(FATLongFileNameEntry))); - } - - return {}; -} - -ErrorOr FATInode::remove_child(StringView name) -{ - MutexLocker locker(m_inode_lock); - - dbgln_if(FAT_DEBUG, "FATInode[{}]::remove_child(): removing inode \"{}\"", identifier(), name); - - VERIFY(has_flag(m_entry.attributes, FATAttributes::Directory)); - - Vector lfn_entries; - TRY(lfn_entries.try_ensure_capacity(ceil_div(max_filename_length, characters_per_lfn_entry))); - - Vector lfn_entry_locations; - TRY(lfn_entry_locations.try_ensure_capacity(ceil_div(max_filename_length, characters_per_lfn_entry))); - - auto block_list = TRY(get_block_list()); - auto block_buffer = TRY(read_block_list()); - - for (u32 i = 0; i < block_buffer->size() / sizeof(FATEntry); i++) { - auto* entry = bit_cast(block_buffer->data() + i * sizeof(FATEntry)); - - auto entry_number_bytes = i * sizeof(FATEntry); - auto block = block_list[entry_number_bytes / fs().logical_block_size()]; - - auto entries_per_sector = fs().logical_block_size() / sizeof(FATEntry); - u32 block_entry = i % entries_per_sector; - - if (entry->filename[0] == end_entry_byte) { - dbgln_if(FAT_DEBUG, "FATInode[{}]::remove_child(): Found end entry", identifier()); - return ENOENT; - } else if (static_cast(entry->filename[0]) == unused_entry_byte) { - dbgln_if(FAT_DEBUG, "FATInode[{}]::remove_child(): Found unused entry", identifier()); - lfn_entries.clear_with_capacity(); - lfn_entry_locations.clear_with_capacity(); - } else if (entry->attributes == FATAttributes::LongFileName) { - dbgln_if(FAT_DEBUG, "FATInode[{}]::remove_child(): Found LFN entry", identifier()); - lfn_entries.unchecked_append(*bit_cast(entry)); - lfn_entry_locations.unchecked_append({ block, block_entry }); - } else { - dbgln_if(FAT_DEBUG, "FATInode[{}]::remove_child(): Found 8.3 entry at block {}, entry {}", identifier(), block, block_entry); - lfn_entries.reverse(); - auto filename = TRY(compute_filename(*entry, lfn_entries)); - if (filename->view() == name) { - // FIXME: If it's the last entry move the end entry instead of unused entries - FATEntry unused_entry {}; - unused_entry.filename[0] = unused_entry_byte; - TRY(fs().write_block(block, UserOrKernelBuffer::for_kernel_buffer(bit_cast(&unused_entry)), sizeof(FATEntry), block_entry * sizeof(FATEntry))); - - for (auto const& lfn_entry_location : lfn_entry_locations) - TRY(fs().write_block(lfn_entry_location.block, UserOrKernelBuffer::for_kernel_buffer(bit_cast(&unused_entry)), sizeof(FATEntry), lfn_entry_location.entry * sizeof(FATEntry))); - - u32 entry_first_cluster = entry->first_cluster_low; - if (fs().m_fat_version == FATVersion::FAT32) - entry_first_cluster |= (static_cast(entry->first_cluster_high) << 16); - - auto cluster_list = TRY(compute_cluster_list(fs(), entry_first_cluster)); - - // NOTE: The '.' directory entry has the same first cluster as its parent directory. - if (entry_first_cluster != first_cluster()) { - for (auto cluster : cluster_list) { - if (cluster <= 1) - continue; - - TRY(fs().notify_cluster_freed()); - TRY(fs().fat_write(cluster, 0)); - } - } - - return {}; - } - lfn_entries.clear_with_capacity(); - lfn_entry_locations.clear_with_capacity(); - } - } - - return EINVAL; -} - -ErrorOr FATInode::chmod(mode_t) -{ - // TODO: Linux actually does do some stuff here, like setting the hidden attribute if the file starts with a dot. - return Error::from_errno(ENOTSUP); -} - -ErrorOr FATInode::chown(UserID, GroupID) -{ - return Error::from_errno(ENOTSUP); -} - -ErrorOr FATInode::resize(u64 size) -{ - VERIFY(m_inode_lock.is_locked()); - VERIFY(size != m_entry.file_size); - - u32 old_size = m_entry.file_size; - u64 bytes_per_cluster = fs().m_device_block_size * fs().m_parameter_block->common_bpb()->sectors_per_cluster; - - u64 size_rounded_up_to_bytes_per_cluster = size; - if (size == 0) - size_rounded_up_to_bytes_per_cluster = bytes_per_cluster; - else if (size % bytes_per_cluster != 0) - size_rounded_up_to_bytes_per_cluster = (size + bytes_per_cluster) - (size % bytes_per_cluster); - - if (size > m_entry.file_size) { - while (m_cluster_list.size() * bytes_per_cluster < size_rounded_up_to_bytes_per_cluster) - TRY(allocate_and_add_cluster_to_chain()); - } else { - while (m_cluster_list.size() * bytes_per_cluster > size_rounded_up_to_bytes_per_cluster) - TRY(remove_last_cluster_from_chain()); - } - - m_entry.file_size = size; - set_metadata_dirty(true); - - if (size > old_size) { - // There will likely be stale data following the old end of the file, - // so make sure to zero out all of the newly-allocated data. - u64 bytes_to_clear = size - old_size; - u64 clear_from = old_size; - u8 zero_buffer[PAGE_SIZE] {}; - while (bytes_to_clear) { - auto nwritten = TRY(write_bytes_locked(clear_from, min(static_cast(sizeof(zero_buffer)), bytes_to_clear), UserOrKernelBuffer::for_kernel_buffer(zero_buffer), nullptr)); - VERIFY(nwritten != 0); - bytes_to_clear -= nwritten; - clear_from += nwritten; - } - } - - return {}; -} - -ErrorOr FATInode::truncate_locked(u64 size) -{ - VERIFY(m_inode_lock.is_locked()); - if (m_entry.file_size == size) - return {}; - - dbgln_if(FAT_DEBUG, "FATInode[{}]::truncate_locked(): truncating to {}", identifier(), size); - TRY(resize(size)); - - return {}; -} - -ErrorOr FATInode::flush_metadata() -{ - if (m_inode_metadata_location.block == 0) - return {}; - - dbgln_if(FAT_DEBUG, "FATInode[{}]::flush_metadata(): Writing entry at block {}, entry {} (size: {}, cluster_low: {}, cluster_high: {})", identifier().index(), m_inode_metadata_location.block, m_inode_metadata_location.entry, m_entry.file_size, m_entry.first_cluster_low, m_entry.first_cluster_high); - - TRY(fs().write_block(m_inode_metadata_location.block, UserOrKernelBuffer::for_kernel_buffer(bit_cast(&m_entry)), sizeof(FATEntry), m_inode_metadata_location.entry * sizeof(FATEntry))); - - set_metadata_dirty(false); - return {}; -} - -ErrorOr FATInode::update_timestamps(Optional, Optional, Optional) -{ - // FIXME: Implement FATInode::update_timestamps - return {}; -} - -} diff --git a/Kernel/FileSystem/FATFS/Inode.h b/Kernel/FileSystem/FATFS/Inode.h deleted file mode 100644 index 7b9d71c8733..00000000000 --- a/Kernel/FileSystem/FATFS/Inode.h +++ /dev/null @@ -1,100 +0,0 @@ -/* - * Copyright (c) 2022-2024, Undefine - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include -#include -#include - -namespace Kernel { - -struct FATEntryLocation { - BlockBasedFileSystem::BlockIndex block; - u32 entry; -}; - -class FATInode final : public Inode { - friend FATFS; - -public: - virtual ~FATInode() override = default; - - static ErrorOr> create(FATFS&, FATEntry, FATEntryLocation inode_metadata_location, Vector const& = {}); - - FATFS& fs() { return static_cast(Inode::fs()); } - FATFS const& fs() const { return static_cast(Inode::fs()); } - -private: - FATInode(FATFS&, FATEntry, FATEntryLocation inode_metadata_location, NonnullOwnPtr filename); - - static constexpr u8 end_entry_byte = 0x00; - static constexpr u8 unused_entry_byte = 0xE5; - - static constexpr u8 lfn_entry_character_termination = 0x00; - static constexpr u8 lfn_entry_unused_byte = 0xFF; - - static constexpr u8 normal_filename_length = 8; - static constexpr u8 normal_extension_length = 3; - - static constexpr size_t lfn_entry_characters_part_1_length = 5; - static constexpr size_t lfn_entry_characters_part_2_length = 6; - static constexpr size_t lfn_entry_characters_part_3_length = 2; - - static constexpr size_t characters_per_lfn_entry = lfn_entry_characters_part_1_length + lfn_entry_characters_part_2_length + lfn_entry_characters_part_3_length; - static constexpr u8 last_lfn_entry_mask = 0x40; - - static constexpr size_t max_filename_length = 255; - - static ErrorOr> compute_filename(FATEntry&, Vector const& = {}); - static StringView byte_terminated_string(StringView, u8); - static u8 lfn_entry_checksum(FATEntry const& entry); - static void create_83_filename_for(FATEntry& entry, StringView name); - static ErrorOr> create_lfn_entries(StringView name, u8 checksum); - - ErrorOr> compute_cluster_list(FATFS&, u32 first_cluster); - ErrorOr> get_block_list(); - ErrorOr> read_block_list(); - ErrorOr> traverse(Function(RefPtr)> callback); - u32 first_cluster() const; - // This overload of `first_cluster` does not rely on the base Inode - // already being created to determine the FAT version. It is used - // during FATInode creation (create()). - u32 first_cluster(FATVersion const version) const; - ErrorOr allocate_and_add_cluster_to_chain(); - ErrorOr remove_last_cluster_from_chain(); - ErrorOr> allocate_entries(u32 count); - - ErrorOr resize(u64 size); - - // ^Inode - virtual ErrorOr write_bytes_locked(off_t, size_t, UserOrKernelBuffer const& data, OpenFileDescription*) override; - virtual ErrorOr read_bytes_locked(off_t, size_t, UserOrKernelBuffer& buffer, OpenFileDescription*) const override; - - virtual InodeMetadata metadata() const override; - virtual ErrorOr traverse_as_directory(Function(FileSystem::DirectoryEntryView const&)>) const override; - virtual ErrorOr> lookup(StringView name) override; - virtual ErrorOr> create_child(StringView name, mode_t, dev_t, UserID, GroupID) override; - virtual ErrorOr add_child(Inode&, StringView name, mode_t) override; - virtual ErrorOr remove_child(StringView name) override; - virtual ErrorOr replace_child(StringView name, Inode& child) override; - virtual ErrorOr chmod(mode_t) override; - virtual ErrorOr chown(UserID, GroupID) override; - virtual ErrorOr truncate_locked(u64) override; - virtual ErrorOr flush_metadata() override; - virtual ErrorOr update_timestamps(Optional atime, Optional ctime, Optional mtime) override; - - Vector m_cluster_list; - FATEntry m_entry; - FATEntryLocation m_inode_metadata_location; - NonnullOwnPtr m_filename; -}; - -} diff --git a/Kernel/FileSystem/FIFO.cpp b/Kernel/FileSystem/FIFO.cpp deleted file mode 100644 index bcd1d4236eb..00000000000 --- a/Kernel/FileSystem/FIFO.cpp +++ /dev/null @@ -1,140 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include -#include -#include - -namespace Kernel { - -static Atomic s_next_fifo_id = 1; - -ErrorOr> FIFO::try_create(UserID uid) -{ - auto buffer = TRY(DoubleBuffer::try_create("FIFO: Buffer"sv)); - return adopt_nonnull_ref_or_enomem(new (nothrow) FIFO(uid, move(buffer))); -} - -ErrorOr> FIFO::open_direction(FIFO::Direction direction) -{ - auto description = TRY(OpenFileDescription::try_create(*this)); - if (direction == Direction::Reader) { - ++m_readers; - } else if (direction == Direction::Writer) { - ++m_writers; - } - evaluate_block_conditions(); - description->set_fifo_direction({}, direction); - return description; -} - -ErrorOr> FIFO::open_direction_blocking(FIFO::Direction direction) -{ - MutexLocker locker(m_open_lock); - - auto description = TRY(open_direction(direction)); - - if (direction == Direction::Reader) { - m_read_open_queue.wake_all(); - - if (m_writers == 0) { - locker.unlock(); - m_write_open_queue.wait_forever("FIFO"sv); - locker.lock(); - } - } - - if (direction == Direction::Writer) { - m_write_open_queue.wake_all(); - - if (m_readers == 0) { - locker.unlock(); - m_read_open_queue.wait_forever("FIFO"sv); - locker.lock(); - } - } - - return description; -} - -FIFO::FIFO(UserID uid, NonnullOwnPtr buffer) - : m_buffer(move(buffer)) - , m_uid(uid) -{ - m_fifo_id = ++s_next_fifo_id; - - // Use the same block condition for read and write - m_buffer->set_unblock_callback([this]() { - evaluate_block_conditions(); - }); -} - -FIFO::~FIFO() = default; - -void FIFO::detach(OpenFileDescription& description) -{ - File::detach(description); - - auto direction = description.fifo_direction(); - if (direction == Direction::Reader) { - VERIFY(m_readers); - --m_readers; - } else if (direction == Direction::Writer) { - VERIFY(m_writers); - --m_writers; - } - - evaluate_block_conditions(); -} - -bool FIFO::can_read(OpenFileDescription const&, u64) const -{ - return !m_buffer->is_empty() || !m_writers; -} - -bool FIFO::can_write(OpenFileDescription const&, u64) const -{ - return m_buffer->space_for_writing() || !m_readers; -} - -ErrorOr FIFO::read(OpenFileDescription& fd, u64, UserOrKernelBuffer& buffer, size_t size) -{ - if (m_buffer->is_empty()) { - if (!m_writers) - return 0; - if (!fd.is_blocking()) - return EAGAIN; - } - return m_buffer->read(buffer, size); -} - -ErrorOr FIFO::write(OpenFileDescription& fd, u64, UserOrKernelBuffer const& buffer, size_t size) -{ - if (!m_readers) - return EPIPE; - if (!fd.is_blocking() && m_buffer->space_for_writing() == 0) - return EAGAIN; - - return m_buffer->write(buffer, size); -} - -ErrorOr> FIFO::pseudo_path(OpenFileDescription const&) const -{ - return KString::formatted("fifo:{}", m_fifo_id); -} - -ErrorOr FIFO::stat() const -{ - struct stat st = {}; - st.st_mode = S_IFIFO; - return st; -} - -} diff --git a/Kernel/FileSystem/FIFO.h b/Kernel/FileSystem/FIFO.h deleted file mode 100644 index 974c6a090c2..00000000000 --- a/Kernel/FileSystem/FIFO.h +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include - -namespace Kernel { - -class OpenFileDescription; - -class FIFO final : public File { -public: - enum class Direction : u8 { - Neither, - Reader, - Writer - }; - - static ErrorOr> try_create(UserID); - virtual ~FIFO() override; - - UserID uid() const { return m_uid; } - - ErrorOr> open_direction(Direction); - ErrorOr> open_direction_blocking(Direction); - -private: - // ^File - virtual ErrorOr write(OpenFileDescription&, u64, UserOrKernelBuffer const&, size_t) override; - virtual ErrorOr read(OpenFileDescription&, u64, UserOrKernelBuffer&, size_t) override; - virtual ErrorOr stat() const override; - virtual void detach(OpenFileDescription&) override; - virtual bool can_read(OpenFileDescription const&, u64) const override; - virtual bool can_write(OpenFileDescription const&, u64) const override; - virtual ErrorOr> pseudo_path(OpenFileDescription const&) const override; - virtual StringView class_name() const override { return "FIFO"sv; } - virtual bool is_fifo() const override { return true; } - - explicit FIFO(UserID, NonnullOwnPtr buffer); - - unsigned m_writers { 0 }; - unsigned m_readers { 0 }; - NonnullOwnPtr m_buffer; - - UserID m_uid { 0 }; - - int m_fifo_id { 0 }; - - WaitQueue m_read_open_queue; - WaitQueue m_write_open_queue; - Mutex m_open_lock; -}; - -} diff --git a/Kernel/FileSystem/FUSE/Definitions.h b/Kernel/FileSystem/FUSE/Definitions.h deleted file mode 100644 index 8edf3de31bd..00000000000 --- a/Kernel/FileSystem/FUSE/Definitions.h +++ /dev/null @@ -1,313 +0,0 @@ -/* - * Copyright (C) 2001-2007 Miklos Szeredi. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * This file is based on include/uapi/linux/fuse.h from Linux. That header - * is dual-licensed under the GPLv2 and the above 2-Clause BSD license. - * Consequentially, we are sublicensing this file under the latter license, - * which is why we have to include it above verbatim. - */ - -#pragma once - -#include - -#define FUSE_KERNEL_VERSION 7 -#define FUSE_KERNEL_MINOR_VERSION 39 - -struct fuse_attr { - uint64_t ino; - uint64_t size; - uint64_t blocks; - uint64_t atime; - uint64_t mtime; - uint64_t ctime; - uint32_t atimensec; - uint32_t mtimensec; - uint32_t ctimensec; - uint32_t mode; - uint32_t nlink; - uint32_t uid; - uint32_t gid; - uint32_t rdev; - uint32_t blksize; - uint32_t flags; -}; - -// Bitmasks for fuse_setattr_in.valid -#define FATTR_MODE (1 << 0) -#define FATTR_UID (1 << 1) -#define FATTR_GID (1 << 2) -#define FATTR_SIZE (1 << 3) -#define FATTR_ATIME (1 << 4) -#define FATTR_MTIME (1 << 5) -#define FATTR_FH (1 << 6) -#define FATTR_ATIME_NOW (1 << 7) -#define FATTR_MTIME_NOW (1 << 8) -#define FATTR_LOCKOWNER (1 << 9) -#define FATTR_CTIME (1 << 10) -#define FATTR_KILL_SUIDGID (1 << 11) - -enum class FUSEOpcode { - FUSE_LOOKUP = 1, - FUSE_FORGET = 2, // no reply - FUSE_GETATTR = 3, - FUSE_SETATTR = 4, - FUSE_READLINK = 5, - FUSE_SYMLINK = 6, - FUSE_MKNOD = 8, - FUSE_MKDIR = 9, - FUSE_UNLINK = 10, - FUSE_RMDIR = 11, - FUSE_RENAME = 12, - FUSE_LINK = 13, - FUSE_OPEN = 14, - FUSE_READ = 15, - FUSE_WRITE = 16, - FUSE_STATFS = 17, - FUSE_RELEASE = 18, - FUSE_FSYNC = 20, - FUSE_SETXATTR = 21, - FUSE_GETXATTR = 22, - FUSE_LISTXATTR = 23, - FUSE_REMOVEXATTR = 24, - FUSE_FLUSH = 25, - FUSE_INIT = 26, - FUSE_OPENDIR = 27, - FUSE_READDIR = 28, - FUSE_RELEASEDIR = 29, - FUSE_FSYNCDIR = 30, - FUSE_GETLK = 31, - FUSE_SETLK = 32, - FUSE_SETLKW = 33, - FUSE_ACCESS = 34, - FUSE_CREATE = 35, - FUSE_INTERRUPT = 36, - FUSE_BMAP = 37, - FUSE_DESTROY = 38, - FUSE_IOCTL = 39, - FUSE_POLL = 40, - FUSE_NOTIFY_REPLY = 41, - FUSE_BATCH_FORGET = 42, - FUSE_FALLOCATE = 43, - FUSE_READDIRPLUS = 44, - FUSE_RENAME2 = 45, - FUSE_LSEEK = 46, - FUSE_COPY_FILE_RANGE = 47, - FUSE_SETUPMAPPING = 48, - FUSE_REMOVEMAPPING = 49, - FUSE_SYNCFS = 50, - FUSE_TMPFILE = 51, - FUSE_STATX = 52, - - // CUSE specific operations - CUSE_INIT = 4096, - - // Reserved opcodes: helpful to detect structure endian-ness - CUSE_INIT_BSWAP_RESERVED = 1048576, // CUSE_INIT << 8 - FUSE_INIT_BSWAP_RESERVED = 436207616, // FUSE_INIT << 24 -}; - -struct fuse_entry_out { - uint64_t nodeid; /* Inode ID */ - uint64_t generation; /* Inode generation: nodeid:gen must - be unique for the fs's lifetime */ - uint64_t entry_valid; /* Cache timeout for the name */ - uint64_t attr_valid; /* Cache timeout for the attributes */ - uint32_t entry_valid_nsec; - uint32_t attr_valid_nsec; - struct fuse_attr attr; -}; - -struct fuse_getattr_in { - uint32_t getattr_flags; - uint32_t dummy; - uint64_t fh; -}; - -struct fuse_attr_out { - uint64_t attr_valid; /* Cache timeout for the attributes */ - uint32_t attr_valid_nsec; - uint32_t dummy; - struct fuse_attr attr; -}; - -struct fuse_mknod_in { - uint32_t mode; - uint32_t rdev; - uint32_t umask; - uint32_t padding; -}; - -struct fuse_mkdir_in { - uint32_t mode; - uint32_t umask; -}; - -struct fuse_rename_in { - uint64_t newdir; -}; - -struct fuse_link_in { - uint64_t oldnodeid; -}; - -struct fuse_setattr_in { - uint32_t valid; - uint32_t padding; - uint64_t fh; - uint64_t size; - uint64_t lock_owner; - uint64_t atime; - uint64_t mtime; - uint64_t ctime; - uint32_t atimensec; - uint32_t mtimensec; - uint32_t ctimensec; - uint32_t mode; - uint32_t unused4; - uint32_t uid; - uint32_t gid; - uint32_t unused5; -}; - -struct fuse_open_in { - uint32_t flags; - uint32_t open_flags; /* FUSE_OPEN_... */ -}; - -struct fuse_create_in { - uint32_t flags; - uint32_t mode; - uint32_t umask; - uint32_t open_flags; /* FUSE_OPEN_... */ -}; - -struct fuse_open_out { - uint64_t fh; - uint32_t open_flags; - uint32_t padding; -}; - -struct fuse_release_in { - uint64_t fh; - uint32_t flags; - uint32_t release_flags; - uint64_t lock_owner; -}; - -struct fuse_flush_in { - uint64_t fh; - uint32_t unused; - uint32_t padding; - uint64_t lock_owner; -}; - -struct fuse_read_in { - uint64_t fh; - uint64_t offset; - uint32_t size; - uint32_t read_flags; - uint64_t lock_owner; - uint32_t flags; - uint32_t padding; -}; - -struct fuse_write_in { - uint64_t fh; - uint64_t offset; - uint32_t size; - uint32_t write_flags; - uint64_t lock_owner; - uint32_t flags; - uint32_t padding; -}; - -struct fuse_write_out { - uint32_t size; - uint32_t padding; -}; - -struct fuse_access_in { - uint32_t mask; - uint32_t padding; -}; - -struct fuse_init_in { - uint32_t major; - uint32_t minor; - uint32_t max_readahead; - uint32_t flags; - uint32_t flags2; - uint32_t unused[11]; -}; - -struct fuse_init_out { - uint32_t major; - uint32_t minor; - uint32_t max_readahead; - uint32_t flags; - uint16_t max_background; - uint16_t congestion_threshold; - uint32_t max_write; - uint32_t time_gran; - uint16_t max_pages; - uint16_t map_alignment; - uint32_t flags2; - uint32_t unused[7]; -}; - -struct fuse_in_header { - uint32_t len; - uint32_t opcode; - uint64_t unique; - uint64_t nodeid; - uint32_t uid; - uint32_t gid; - uint32_t pid; - uint16_t total_extlen; /* length of extensions in 8byte units */ - uint16_t padding; -}; - -struct fuse_out_header { - uint32_t len; - int32_t error; - uint64_t unique; -}; - -struct fuse_dirent { - uint64_t ino; - uint64_t off; - uint32_t namelen; - uint32_t type; - char name[]; -}; - -/* Align variable length records to 64bit boundary */ -#define FUSE_REC_ALIGN(x) \ - (((x) + sizeof(uint64_t) - 1) & ~(sizeof(uint64_t) - 1)) - -#define FUSE_NAME_OFFSET offsetof(struct fuse_dirent, name) -#define FUSE_DIRENT_ALIGN(x) FUSE_REC_ALIGN(x) -#define FUSE_DIRENT_SIZE(d) \ - FUSE_DIRENT_ALIGN(FUSE_NAME_OFFSET + (d)->namelen) diff --git a/Kernel/FileSystem/FUSE/FUSEConnection.h b/Kernel/FileSystem/FUSE/FUSEConnection.h deleted file mode 100644 index ceee1f32634..00000000000 --- a/Kernel/FileSystem/FUSE/FUSEConnection.h +++ /dev/null @@ -1,145 +0,0 @@ -/* - * Copyright (c) 2024, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include - -namespace Kernel { - -class FUSEConnection : public RefCounted { -public: - static ErrorOr> try_create(NonnullRefPtr description) - { - if (!description->is_device()) - return Error::from_errno(EINVAL); - - if (description->device()->class_name() != "FUSEDevice"sv) - return Error::from_errno(EINVAL); - - auto connection = TRY(adopt_nonnull_ref_or_enomem(new (nothrow) FUSEConnection(description))); - - auto* device = bit_cast(description->device()); - TRY(device->initialize_instance(*description)); - - return connection; - } - - static ErrorOr> create_request(FUSEOpcode opcode, u32 nodeid, u32 unique, ReadonlyBytes request_body) - { - size_t request_length = sizeof(fuse_in_header) + request_body.size(); - auto request = TRY(KBuffer::try_create_with_size("FUSE: Request"sv, request_length)); - memset(request->data(), 0, request_length); - fuse_in_header* header = bit_cast(request->data()); - header->len = request_length; - header->opcode = to_underlying(opcode); - header->unique = unique; - header->nodeid = nodeid; - - auto current_process_credentials = Process::current().credentials(); - header->uid = current_process_credentials->euid().value(); - header->gid = current_process_credentials->egid().value(); - header->pid = Process::current().pid().value(); - - u8* payload = bit_cast(request->data() + sizeof(fuse_in_header)); - memcpy(payload, request_body.data(), request_body.size()); - - return request; - } - - ErrorOr> send_request_and_wait_for_a_reply(FUSEOpcode opcode, u32 nodeid, ReadonlyBytes request_body) - { - auto* device = bit_cast(m_description->device()); - - // FIXME: Send the init request from the filesystem itself right after it has been - // mounted. (Without blocking the mount syscall.) - if (!m_initialized) - TRY(handle_init()); - - u32 unique = m_unique++; - auto request = TRY(create_request(opcode, nodeid, unique, request_body)); - auto response = TRY(device->send_request_and_wait_for_a_reply(m_description, request->bytes())); - - if (validate_response(*response, unique).is_error()) - return Error::from_errno(EIO); - - return response; - } - - ~FUSEConnection() - { - auto* device = bit_cast(m_description->device()); - // Unblock the userspace daemon and tell it to shut down. - device->shutdown_for_description(m_description); - (void)m_description->close(); - } - -private: - FUSEConnection(NonnullRefPtr description) - : m_description(description) - { - } - - ErrorOr validate_response(KBuffer const& response, u32 unique) - { - if (response.size() < sizeof(fuse_out_header)) { - dmesgln("FUSE: Received a request with a malformed header"); - return Error::from_errno(EINVAL); - } - - fuse_out_header* header = bit_cast(response.data()); - if (header->unique != unique) { - dmesgln("FUSE: Received a mismatched request (expected #{}, received #{})", unique, header->unique); - return Error::from_errno(EINVAL); - } - - if (header->len > response.size()) { - dmesgln("FUSE: Received an excessively large request"); - return Error::from_errno(EINVAL); - } - - return {}; - } - - ErrorOr handle_init() - { - fuse_init_in init_request; - init_request.major = FUSE_KERNEL_VERSION; - init_request.minor = FUSE_KERNEL_MINOR_VERSION; - init_request.max_readahead = 512; - init_request.flags = 0; - - auto* device = bit_cast(m_description->device()); - - auto request = TRY(create_request(FUSEOpcode::FUSE_INIT, 0, 0, { &init_request, sizeof(init_request) })); - auto response = TRY(device->send_request_and_wait_for_a_reply(m_description, request->bytes())); - - if (validate_response(*response, 0).is_error()) - return Error::from_errno(EIO); - - fuse_init_out* init = bit_cast(response->data() + sizeof(fuse_out_header)); - - m_major = init->major; - m_minor = init->minor; - - m_initialized = true; - - return {}; - } - - NonnullRefPtr m_description; - bool m_initialized { false }; - Atomic m_unique { 0 }; - - u32 m_major { 0 }; - u32 m_minor { 0 }; -}; - -} diff --git a/Kernel/FileSystem/FUSE/FileSystem.cpp b/Kernel/FileSystem/FUSE/FileSystem.cpp deleted file mode 100644 index 7a0ef643189..00000000000 --- a/Kernel/FileSystem/FUSE/FileSystem.cpp +++ /dev/null @@ -1,90 +0,0 @@ -/* - * Copyright (c) 2024, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include -#include - -namespace Kernel { - -struct [[gnu::packed]] FUSESpecificFlagsBytes { - u64 pid; - u64 device_inode; - u64 rootmode; - u64 gid; - u64 uid; -}; - -ErrorOr> FUSE::try_create(ReadonlyBytes mount_flags) -{ - auto* fuse_mount_flags = reinterpret_cast(mount_flags.data()); - - auto description = TRY(Process::current().open_file_description(fuse_mount_flags->device_inode)); - auto connection = TRY(FUSEConnection::try_create(description)); - - u64 rootmode = AK::reinterpret_as_octal(fuse_mount_flags->rootmode); - u64 gid = fuse_mount_flags->gid; - u64 uid = fuse_mount_flags->uid; - return TRY(adopt_nonnull_ref_or_enomem(new (nothrow) FUSE(connection, rootmode, uid, gid))); -} - -ErrorOr FUSE::handle_mount_unsigned_integer_flag(Bytes mount_file_specific_flags_buffer, StringView key, u64 value) -{ - auto* fuse_mount_flags = reinterpret_cast(mount_file_specific_flags_buffer.data()); - if (key == "fd") { - fuse_mount_flags->device_inode = value; - return {}; - } - if (key == "rootmode") { - fuse_mount_flags->rootmode = value; - return {}; - } - if (key == "gid") { - fuse_mount_flags->gid = value; - return {}; - } - if (key == "uid") { - fuse_mount_flags->uid = value; - return {}; - } - return EINVAL; -} - -FUSE::FUSE(NonnullRefPtr connection, u64 rootmode, u64 gid, u64 uid) - : m_connection(connection) - , m_rootmode(rootmode) - , m_gid(gid) - , m_uid(uid) -{ -} - -FUSE::~FUSE() = default; - -ErrorOr FUSE::initialize() -{ - m_root_inode = TRY(adopt_nonnull_ref_or_enomem(new (nothrow) FUSEInode(*this))); - m_root_inode->m_metadata.mode = m_rootmode; - m_root_inode->m_metadata.uid = m_gid; - m_root_inode->m_metadata.gid = m_uid; - m_root_inode->m_metadata.size = 0; - m_root_inode->m_metadata.mtime = TimeManagement::boot_time(); - return {}; -} - -Inode& FUSE::root_inode() -{ - return *m_root_inode; -} - -u8 FUSE::internal_file_type_to_directory_entry_type(DirectoryEntryView const& entry) const -{ - return ram_backed_file_type_to_directory_entry_type(entry); -} - -} diff --git a/Kernel/FileSystem/FUSE/FileSystem.h b/Kernel/FileSystem/FUSE/FileSystem.h deleted file mode 100644 index 36b181d1317..00000000000 --- a/Kernel/FileSystem/FUSE/FileSystem.h +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright (c) 2024, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include - -namespace Kernel { - -class FUSEInode; - -class FUSE final : public FileSystem { - friend class FUSEInode; - -public: - virtual ~FUSE() override; - static ErrorOr> try_create(ReadonlyBytes mount_flags); - - static ErrorOr handle_mount_unsigned_integer_flag(Bytes mount_file_specific_flags_buffer, StringView key, u64); - - virtual ErrorOr initialize() override; - virtual StringView class_name() const override { return "FUSE"sv; } - - virtual Inode& root_inode() override; - -private: - virtual u8 internal_file_type_to_directory_entry_type(DirectoryEntryView const& entry) const override; - - FUSE(NonnullRefPtr connection, u64 rootmode, u64 gid, u64 uid); - - RefPtr m_root_inode; - NonnullRefPtr m_connection; - u64 m_rootmode { 0 }; - u64 m_gid { 0 }; - u64 m_uid { 0 }; -}; - -} diff --git a/Kernel/FileSystem/FUSE/Inode.cpp b/Kernel/FileSystem/FUSE/Inode.cpp deleted file mode 100644 index fd0fd66fb74..00000000000 --- a/Kernel/FileSystem/FUSE/Inode.cpp +++ /dev/null @@ -1,333 +0,0 @@ -/* - * Copyright (c) 2024, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include - -namespace Kernel { - -FUSEInode::FUSEInode(FUSE& fs, InodeIndex index) - : Inode(fs, index) -{ -} - -FUSEInode::FUSEInode(FUSE& fs) - : Inode(fs, 1) -{ -} - -FUSEInode::~FUSEInode() = default; - -ErrorOr FUSEInode::read_bytes_locked(off_t offset, size_t size, UserOrKernelBuffer& buffer, OpenFileDescription*) const -{ - VERIFY(m_inode_lock.is_locked()); - VERIFY(!is_directory()); - - constexpr size_t max_read_size = 0x21000 - sizeof(fuse_in_header) - sizeof(fuse_read_in); - u64 id = TRY(try_open(false, O_RDONLY)); - u32 nodeid = identifier().index().value(); - - size_t nread = 0; - size_t target_size = size; - while (target_size) { - size_t chunk_size = min(size, max_read_size); - fuse_read_in payload {}; - payload.fh = id; - payload.offset = offset + nread; - payload.size = chunk_size; - auto response = TRY(fs().m_connection->send_request_and_wait_for_a_reply(FUSEOpcode::FUSE_READ, nodeid, { &payload, sizeof(payload) })); - - fuse_out_header* header = bit_cast(response->data()); - if (header->error) - return Error::from_errno(-header->error); - - u32 data_size = min(target_size, header->len - sizeof(fuse_out_header)); - if (data_size == 0) - break; - - u8* data = bit_cast(response->data() + sizeof(fuse_out_header)); - - TRY(buffer.write(data, nread, data_size)); - nread += data_size; - target_size -= data_size; - } - - TRY(try_flush(id)); - TRY(try_release(id, false)); - - return nread; -} - -ErrorOr FUSEInode::write_bytes_locked(off_t offset, size_t size, UserOrKernelBuffer const& buffer, OpenFileDescription*) -{ - VERIFY(m_inode_lock.is_locked()); - VERIFY(!is_directory()); - VERIFY(offset >= 0); - - constexpr size_t max_write_size = 0x21000 - sizeof(fuse_in_header) - sizeof(fuse_write_in); - u64 id = TRY(try_open(false, O_WRONLY)); - u32 nodeid = identifier().index().value(); - - size_t nwritten = 0; - while (size) { - size_t chunk_size = min(size, max_write_size); - auto request_buffer = TRY(KBuffer::try_create_with_size("FUSE: Write buffer"sv, sizeof(fuse_write_in) + chunk_size)); - fuse_write_in* write_header = bit_cast(request_buffer->data()); - write_header->fh = id; - write_header->offset = offset + nwritten; - write_header->size = chunk_size; - TRY(buffer.read(request_buffer->data() + sizeof(fuse_write_in), nwritten, chunk_size)); - - auto response = TRY(fs().m_connection->send_request_and_wait_for_a_reply(FUSEOpcode::FUSE_WRITE, nodeid, request_buffer->bytes())); - if (response->size() < sizeof(fuse_out_header) + sizeof(fuse_write_out)) - return Error::from_errno(EIO); - - fuse_out_header* header = bit_cast(response->data()); - if (header->error) - return Error::from_errno(-header->error); - - fuse_write_out* write_response = bit_cast(response->data() + sizeof(fuse_out_header)); - - nwritten += write_response->size; - size -= write_response->size; - } - - TRY(try_flush(id)); - TRY(try_release(id, false)); - - return nwritten; -} - -InodeMetadata FUSEInode::metadata() const -{ - InodeMetadata metadata; - metadata.inode = identifier(); - u32 id = identifier().index().value(); - - fuse_getattr_in payload {}; - - auto response_or_error = fs().m_connection->send_request_and_wait_for_a_reply(FUSEOpcode::FUSE_GETATTR, id, { &payload, sizeof(payload) }); - - if (response_or_error.is_error()) - return {}; - - auto response = response_or_error.release_value(); - fuse_out_header* header = bit_cast(response->data()); - if (header->error || response->size() < sizeof(fuse_out_header) + sizeof(fuse_attr_out)) - return {}; - - fuse_attr_out* getattr_response = bit_cast(response->data() + sizeof(fuse_out_header)); - - metadata.mode = getattr_response->attr.mode; - metadata.size = getattr_response->attr.size; - metadata.block_size = getattr_response->attr.blksize; - metadata.block_count = getattr_response->attr.blocks; - - metadata.uid = getattr_response->attr.uid; - metadata.gid = getattr_response->attr.gid; - metadata.link_count = getattr_response->attr.nlink; - metadata.atime = UnixDateTime::from_seconds_since_epoch(getattr_response->attr.atime); - metadata.ctime = UnixDateTime::from_seconds_since_epoch(getattr_response->attr.ctime); - metadata.mtime = UnixDateTime::from_seconds_since_epoch(getattr_response->attr.mtime); - metadata.major_device = major_from_encoded_device(getattr_response->attr.rdev); - metadata.minor_device = minor_from_encoded_device(getattr_response->attr.rdev); - - return metadata; -} - -ErrorOr FUSEInode::try_open(bool directory, u32 flags) const -{ - u32 id = identifier().index().value(); - - fuse_open_in payload {}; - payload.flags = flags; - - auto opcode = directory ? FUSEOpcode::FUSE_OPENDIR : FUSEOpcode::FUSE_OPEN; - auto response = TRY(fs().m_connection->send_request_and_wait_for_a_reply(opcode, id, { &payload, sizeof(payload) })); - if (response->size() < sizeof(fuse_out_header) + sizeof(fuse_open_out)) - return Error::from_errno(EIO); - - fuse_out_header* header = bit_cast(response->data()); - if (header->error) - return Error::from_errno(-header->error); - - fuse_open_out* open_response = bit_cast(response->data() + sizeof(fuse_out_header)); - return open_response->fh; -} - -ErrorOr FUSEInode::try_flush(u64 id) const -{ - u32 nodeid = identifier().index().value(); - - fuse_flush_in payload {}; - payload.fh = id; - (void)TRY(fs().m_connection->send_request_and_wait_for_a_reply(FUSEOpcode::FUSE_FLUSH, nodeid, { &payload, sizeof(payload) })); - - return {}; -} - -ErrorOr FUSEInode::try_release(u64 id, bool directory) const -{ - u32 nodeid = identifier().index().value(); - - fuse_release_in payload {}; - payload.fh = id; - auto opcode = directory ? FUSEOpcode::FUSE_RELEASEDIR : FUSEOpcode::FUSE_RELEASE; - (void)TRY(fs().m_connection->send_request_and_wait_for_a_reply(opcode, nodeid, { &payload, sizeof(payload) })); - - return {}; -} - -static size_t get_dirent_entry_length(size_t name_length) -{ - return name_length + FUSE_NAME_OFFSET; -} - -static size_t get_dirent_entry_length_padded(size_t name_length) -{ - return FUSE_DIRENT_ALIGN(get_dirent_entry_length(name_length)); -} - -ErrorOr FUSEInode::traverse_as_directory(Function(FileSystem::DirectoryEntryView const&)> callback) const -{ - u64 id = TRY(try_open(true, 0)); - u32 nodeid = identifier().index().value(); - - fuse_read_in payload {}; - payload.fh = id; - payload.size = 4096; - while (true) { - auto response = TRY(fs().m_connection->send_request_and_wait_for_a_reply(FUSEOpcode::FUSE_READDIR, nodeid, { &payload, sizeof(payload) })); - fuse_out_header* header = bit_cast(response->data()); - if (header->len == sizeof(fuse_out_header)) - break; - char* dirents = bit_cast(response->data() + sizeof(fuse_out_header)); - u32 total_size = header->len - sizeof(fuse_out_header); - u32 offset = 0; - while (offset < total_size) { - fuse_dirent* dirent = bit_cast(dirents + offset); - if (dirent->ino == 0) - break; - - if (dirent->namelen > NAME_MAX) - return Error::from_errno(EIO); - - TRY(callback({ { dirent->name, dirent->namelen }, { fsid(), dirent->ino }, to_underlying(ram_backed_file_type_from_mode(dirent->type << 12)) })); - offset += get_dirent_entry_length_padded(dirent->namelen); - } - payload.offset = offset; - } - - TRY(try_release(id, true)); - return {}; -} - -ErrorOr> FUSEInode::lookup(StringView name) -{ - auto name_buffer = TRY(KBuffer::try_create_with_size("FUSE: Lookup name string"sv, name.length() + 1)); - memset(name_buffer->data(), 0, name_buffer->size()); - memcpy(name_buffer->data(), name.characters_without_null_termination(), name.length()); - - auto response = TRY(fs().m_connection->send_request_and_wait_for_a_reply(FUSEOpcode::FUSE_LOOKUP, identifier().index().value(), name_buffer->bytes())); - if (response->size() < sizeof(fuse_out_header) + sizeof(fuse_entry_out)) - return Error::from_errno(EIO); - - fuse_out_header* header = bit_cast(response->data()); - if (header->error) - return Error::from_errno(-header->error); - - fuse_entry_out* entry = bit_cast(response->data() + sizeof(fuse_out_header)); - - return TRY(adopt_nonnull_ref_or_enomem(new (nothrow) FUSEInode(fs(), entry->nodeid))); -} - -ErrorOr FUSEInode::flush_metadata() -{ - return {}; -} - -ErrorOr FUSEInode::add_child(Inode&, StringView, mode_t) -{ - return ENOTIMPL; -} - -ErrorOr> FUSEInode::create_child(StringView, mode_t, dev_t, UserID, GroupID) -{ - return ENOTIMPL; -} - -ErrorOr FUSEInode::remove_child(StringView) -{ - return ENOTIMPL; -} - -ErrorOr FUSEInode::replace_child(StringView, Inode&) -{ - return ENOTIMPL; -} - -ErrorOr FUSEInode::chmod(mode_t) -{ - return ENOTIMPL; -} - -ErrorOr FUSEInode::chown(UserID, GroupID) -{ - return ENOTIMPL; -} - -ErrorOr FUSEInode::truncate_locked(u64 new_size) -{ - VERIFY(m_inode_lock.is_locked()); - VERIFY(!is_directory()); - u64 id = TRY(try_open(is_directory(), 0)); - - fuse_setattr_in setattr {}; - setattr.fh = id; - setattr.valid = FATTR_SIZE; - setattr.size = new_size; - - auto response = TRY(fs().m_connection->send_request_and_wait_for_a_reply(FUSEOpcode::FUSE_SETATTR, identifier().index().value(), { &setattr, sizeof(setattr) })); - - fuse_out_header* header = bit_cast(response->data()); - if (header->error) - return Error::from_errno(-header->error); - - return try_release(id, is_directory()); -} - -ErrorOr FUSEInode::update_timestamps(Optional atime, Optional ctime, Optional mtime) -{ - MutexLocker locker(m_inode_lock); - - u64 id = TRY(try_open(is_directory(), 0)); - fuse_setattr_in setattr {}; - setattr.fh = id; - - if (atime.has_value()) { - setattr.valid |= FATTR_ATIME; - setattr.atime = atime.value().to_timespec().tv_sec; - } - if (ctime.has_value()) { - setattr.valid |= FATTR_CTIME; - setattr.ctime = ctime.value().to_timespec().tv_sec; - } - if (mtime.has_value()) { - setattr.valid |= FATTR_MTIME; - setattr.mtime = mtime.value().to_timespec().tv_sec; - } - - auto response = TRY(fs().m_connection->send_request_and_wait_for_a_reply(FUSEOpcode::FUSE_SETATTR, identifier().index().value(), { &setattr, sizeof(setattr) })); - - fuse_out_header* header = bit_cast(response->data()); - if (header->error) - return Error::from_errno(-header->error); - - return try_release(id, is_directory()); -} - -} diff --git a/Kernel/FileSystem/FUSE/Inode.h b/Kernel/FileSystem/FUSE/Inode.h deleted file mode 100644 index 3f6333044da..00000000000 --- a/Kernel/FileSystem/FUSE/Inode.h +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright (c) 2024, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include - -namespace Kernel { - -class FUSEInode final : public Inode { - friend class FUSE; - -public: - virtual ~FUSEInode() override; - - FUSE& fs() { return static_cast(Inode::fs()); } - FUSE const& fs() const { return static_cast(Inode::fs()); } - -private: - FUSEInode(FUSE&, InodeIndex); - FUSEInode(FUSE&); - - // ^Inode - virtual ErrorOr read_bytes_locked(off_t, size_t, UserOrKernelBuffer& buffer, OpenFileDescription*) const override; - virtual InodeMetadata metadata() const override; - virtual ErrorOr traverse_as_directory(Function(FileSystem::DirectoryEntryView const&)>) const override; - virtual ErrorOr> lookup(StringView name) override; - virtual ErrorOr flush_metadata() override; - virtual ErrorOr write_bytes_locked(off_t, size_t, UserOrKernelBuffer const& buffer, OpenFileDescription*) override; - virtual ErrorOr> create_child(StringView name, mode_t, dev_t, UserID, GroupID) override; - virtual ErrorOr add_child(Inode&, StringView name, mode_t) override; - virtual ErrorOr remove_child(StringView name) override; - virtual ErrorOr replace_child(StringView name, Inode& child) override; - virtual ErrorOr chmod(mode_t) override; - virtual ErrorOr chown(UserID, GroupID) override; - virtual ErrorOr truncate_locked(u64) override; - virtual ErrorOr update_timestamps(Optional atime, Optional ctime, Optional mtime) override; - - ErrorOr try_open(bool directory, u32 flags) const; - ErrorOr try_flush(u64 id) const; - ErrorOr try_release(u64 id, bool directory) const; - - InodeMetadata m_metadata; -}; - -} diff --git a/Kernel/FileSystem/File.cpp b/Kernel/FileSystem/File.cpp deleted file mode 100644 index e8efaefd113..00000000000 --- a/Kernel/FileSystem/File.cpp +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include - -namespace Kernel { - -File::File() = default; -File::~File() = default; - -ErrorOr> File::open(int options) -{ - auto description = OpenFileDescription::try_create(*this); - if (!description.is_error()) { - description.value()->set_rw_mode(options); - description.value()->set_file_flags(options); - } - return description; -} - -ErrorOr File::close() -{ - return {}; -} - -ErrorOr File::ioctl(OpenFileDescription&, unsigned, Userspace) -{ - return ENOTTY; -} - -ErrorOr> File::vmobject_for_mmap(Process&, Memory::VirtualRange const&, u64&, bool) -{ - return ENODEV; -} - -ErrorOr File::attach(OpenFileDescription&) -{ - m_attach_count++; - return {}; -} - -void File::detach(OpenFileDescription&) -{ - m_attach_count--; -} -} diff --git a/Kernel/FileSystem/File.h b/Kernel/FileSystem/File.h deleted file mode 100644 index be5fb07ceb7..00000000000 --- a/Kernel/FileSystem/File.h +++ /dev/null @@ -1,155 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace Kernel { - -class File; - -class FileBlockerSet final : public Thread::BlockerSet { -public: - FileBlockerSet() { } - - virtual bool should_add_blocker(Thread::Blocker& b, void* data) override - { - VERIFY(b.blocker_type() == Thread::Blocker::Type::File); - auto& blocker = static_cast(b); - return !blocker.unblock_if_conditions_are_met(true, data); - } - - void unblock_all_blockers_whose_conditions_are_met() - { - SpinlockLocker lock(m_lock); - BlockerSet::unblock_all_blockers_whose_conditions_are_met_locked([&](auto& b, void* data, bool&) { - VERIFY(b.blocker_type() == Thread::Blocker::Type::File); - auto& blocker = static_cast(b); - return blocker.unblock_if_conditions_are_met(false, data); - }); - } -}; - -// File is the base class for anything that can be referenced by a OpenFileDescription. -// -// The most important functions in File are: -// -// read() and write() -// - Implement reading and writing. -// - Return the number of bytes read/written, OR a negative error code. -// -// can_read() and can_write() -// -// - Used to implement blocking I/O, and the select() and poll() syscalls. -// - Return true if read() or write() would succeed, respectively. -// - Note that can_read() should return true in EOF conditions, -// and a subsequent call to read() should return 0. -// -// ioctl() -// -// - Optional. If unimplemented, ioctl() on this File will fail with -ENOTTY. -// - Can be overridden in subclasses to implement arbitrary functionality. -// - Subclasses should take care to validate incoming addresses before dereferencing. -// -// vmobject_for_mmap() -// -// - Optional. If unimplemented, mmap() on this File will fail with -ENODEV. -// - Called by mmap() when userspace wants to memory-map this File somewhere. -// - Should return a VMObject suitable for mapping into the calling process. - -class File - : public AtomicRefCounted - , public LockWeakable { -public: - virtual bool unref() const { return AtomicRefCounted::unref(); } - virtual void will_be_destroyed() { } - virtual ~File(); - - virtual ErrorOr> open(int options); - virtual ErrorOr close(); - - virtual bool can_read(OpenFileDescription const&, u64) const = 0; - virtual bool can_write(OpenFileDescription const&, u64) const = 0; - - virtual ErrorOr attach(OpenFileDescription&); - virtual void detach(OpenFileDescription&); - virtual void did_seek(OpenFileDescription&, off_t) { } - virtual ErrorOr read(OpenFileDescription&, u64, UserOrKernelBuffer&, size_t) = 0; - virtual ErrorOr write(OpenFileDescription&, u64, UserOrKernelBuffer const&, size_t) = 0; - virtual ErrorOr ioctl(OpenFileDescription&, unsigned request, Userspace arg); - virtual ErrorOr> vmobject_for_mmap(Process&, Memory::VirtualRange const&, u64& offset, bool shared); - virtual ErrorOr stat() const { return EBADF; } - - // Although this might be better described "name" or "description", these terms already have other meanings. - virtual ErrorOr> pseudo_path(OpenFileDescription const&) const = 0; - - virtual ErrorOr truncate(u64) { return EINVAL; } - virtual ErrorOr sync() { return EINVAL; } - virtual ErrorOr chown(Credentials const&, OpenFileDescription&, UserID, GroupID) { return EBADF; } - virtual ErrorOr chmod(Credentials const&, OpenFileDescription&, mode_t) { return EBADF; } - - virtual StringView class_name() const = 0; - - virtual bool is_seekable() const { return false; } - - virtual bool is_inode() const { return false; } - virtual bool is_fifo() const { return false; } - virtual bool is_device() const { return false; } - virtual bool is_tty() const { return false; } - virtual bool is_master_pty() const { return false; } - virtual bool is_block_device() const { return false; } - virtual bool is_character_device() const { return false; } - virtual bool is_socket() const { return false; } - virtual bool is_inode_watcher() const { return false; } - virtual bool is_mount_file() const { return false; } - virtual bool is_loop_device() const { return false; } - - virtual bool is_regular_file() const { return false; } - - virtual FileBlockerSet& blocker_set() { return m_blocker_set; } - - size_t attach_count() const { return m_attach_count; } - -protected: - File(); - - void evaluate_block_conditions() - { - if (Processor::current_in_irq() != 0) { - // If called from an IRQ handler we need to delay evaluation - // and unblocking of waiting threads. Note that this File - // instance may be deleted until the deferred call is executed! - Processor::deferred_call_queue([self = try_make_weak_ptr().release_value_but_fixme_should_propagate_errors()]() { - if (auto file = self.strong_ref()) - file->do_evaluate_block_conditions(); - }); - } else { - do_evaluate_block_conditions(); - } - } - -private: - ALWAYS_INLINE void do_evaluate_block_conditions() - { - VERIFY(!Processor::current_in_irq()); - blocker_set().unblock_all_blockers_whose_conditions_are_met(); - } - - FileBlockerSet m_blocker_set; - size_t m_attach_count { 0 }; -}; - -} diff --git a/Kernel/FileSystem/FileBackedFileSystem.cpp b/Kernel/FileSystem/FileBackedFileSystem.cpp deleted file mode 100644 index f67a75f48b2..00000000000 --- a/Kernel/FileSystem/FileBackedFileSystem.cpp +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright (c) 2018-2021, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include - -namespace Kernel { - -FileBackedFileSystem::FileBackedFileSystem(OpenFileDescription& file_description) - : m_file_description(file_description) -{ -} - -FileBackedFileSystem::~FileBackedFileSystem() = default; - -ErrorOr FileBackedFileSystem::initialize() -{ - MutexLocker locker(m_lock); - if (is_initialized_while_locked()) - return {}; - return initialize_while_locked(); -} - -} diff --git a/Kernel/FileSystem/FileBackedFileSystem.h b/Kernel/FileSystem/FileBackedFileSystem.h deleted file mode 100644 index 70a82cc1332..00000000000 --- a/Kernel/FileSystem/FileBackedFileSystem.h +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright (c) 2018-2021, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include - -namespace Kernel { - -class FileBackedFileSystem : public FileSystem { - friend class VirtualFileSystem; - -public: - virtual ~FileBackedFileSystem() override; - - File& file() { return m_file_description->file(); } - OpenFileDescription& file_description() { return *m_file_description; } - File const& file() const { return m_file_description->file(); } - OpenFileDescription& file_description() const { return *m_file_description; } - -protected: - explicit FileBackedFileSystem(OpenFileDescription&); - - // Note: We require all FileBackedFileSystem to implement something that actually - // takes into account the fact that we will clean the last mount of the filesystem, - // therefore, removing the file system with it from the Kernel memory. - virtual ErrorOr prepare_to_clear_last_mount(Inode& mount_guest_inode) override = 0; - - virtual ErrorOr initialize_while_locked() = 0; - virtual bool is_initialized_while_locked() = 0; - -private: - virtual ErrorOr initialize() override final; - virtual bool is_file_backed() const override { return true; } - - IntrusiveListNode m_file_backed_file_system_node; - NonnullRefPtr const m_file_description; -}; -} diff --git a/Kernel/FileSystem/FileSystem.cpp b/Kernel/FileSystem/FileSystem.cpp deleted file mode 100644 index 42c1f9c8c84..00000000000 --- a/Kernel/FileSystem/FileSystem.cpp +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace Kernel { - -static u32 s_lastFileSystemID; - -FileSystem::FileSystem() - : m_fsid(++s_lastFileSystemID) -{ -} - -FileSystem::~FileSystem() -{ -} - -ErrorOr FileSystem::prepare_to_unmount(Inode& mount_guest_inode) -{ - return m_attach_count.with([&](auto& attach_count) -> ErrorOr { - dbgln_if(VFS_DEBUG, "VFS: File system {} (id {}) is attached {} time(s)", class_name(), m_fsid.value(), attach_count); - if (attach_count == 1) - return prepare_to_clear_last_mount(mount_guest_inode); - return {}; - }); -} - -FileSystem::DirectoryEntryView::DirectoryEntryView(StringView n, InodeIdentifier i, u8 ft) - : name(n) - , inode(i) - , file_type(ft) -{ -} - -void FileSystem::sync() -{ - Inode::sync_all(); - VirtualFileSystem::the().sync_filesystems(); -} - -void FileSystem::lock_all() -{ - VirtualFileSystem::the().lock_all_filesystems(); -} - -} diff --git a/Kernel/FileSystem/FileSystem.h b/Kernel/FileSystem/FileSystem.h deleted file mode 100644 index a6c2ee78238..00000000000 --- a/Kernel/FileSystem/FileSystem.h +++ /dev/null @@ -1,100 +0,0 @@ -/* - * Copyright (c) 2018-2021, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace Kernel { - -class FileSystem : public AtomicRefCounted { - friend class Inode; - friend class VirtualFileSystem; - -public: - virtual ~FileSystem(); - - FileSystemID fsid() const { return m_fsid; } - static void sync(); - static void lock_all(); - - virtual ErrorOr initialize() = 0; - virtual StringView class_name() const = 0; - virtual Inode& root_inode() = 0; - virtual bool supports_watchers() const { return false; } - - // FIXME: We should aim to provide more concise mechanism to ensure - // that backing Inodes from the FileSystem are kept intact so we can - // attach them to a loop device. - virtual bool supports_backing_loop_devices() const { return false; } - - bool is_readonly() const { return m_readonly; } - - virtual unsigned total_block_count() const { return 0; } - virtual unsigned free_block_count() const { return 0; } - virtual unsigned total_inode_count() const { return 0; } - virtual unsigned free_inode_count() const { return 0; } - - ErrorOr prepare_to_unmount(Inode& mount_guest_inode); - - struct DirectoryEntryView { - DirectoryEntryView(StringView name, InodeIdentifier, u8 file_type); - - StringView name; - InodeIdentifier inode; - u8 file_type { 0 }; - }; - - virtual ErrorOr flush_writes() { return {}; } - - u64 logical_block_size() const { return m_logical_block_size; } - size_t fragment_size() const { return m_fragment_size; } - - virtual bool is_file_backed() const { return false; } - - // Converts file types that are used internally by the filesystem to DT_* types - virtual u8 internal_file_type_to_directory_entry_type(DirectoryEntryView const& entry) const { return entry.file_type; } - - SpinlockProtected& mounted_count(Badge) { return m_attach_count; } - -protected: - FileSystem(); - - void set_logical_block_size(u64 size) { m_logical_block_size = size; } - void set_fragment_size(size_t size) { m_fragment_size = size; } - - virtual ErrorOr prepare_to_clear_last_mount([[maybe_unused]] Inode& mount_guest_inode) { return {}; } - - mutable Mutex m_lock { "FS"sv }; - -private: - FileSystemID m_fsid; - u64 m_logical_block_size { 0 }; - size_t m_fragment_size { 0 }; - bool m_readonly { false }; - - SpinlockProtected m_attach_count { 0 }; - IntrusiveListNode m_file_system_node; -}; - -} - -namespace AK { - -template<> -struct Traits : public DefaultTraits { - static unsigned hash(Kernel::InodeIdentifier const& inode) { return pair_int_hash(inode.fsid().value(), inode.index().value()); } -}; - -} diff --git a/Kernel/FileSystem/ISO9660FS/Definitions.h b/Kernel/FileSystem/ISO9660FS/Definitions.h deleted file mode 100644 index 4b71e4b77a1..00000000000 --- a/Kernel/FileSystem/ISO9660FS/Definitions.h +++ /dev/null @@ -1,270 +0,0 @@ -/* - * Copyright (c) 2021, sin-ack - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include - -namespace Kernel { - -namespace ISO { - -// The implemented spec here is ECMA 119, available at: -// https://www.ecma-international.org/wp-content/uploads/ECMA-119_4th_edition_june_2019.pdf - -template -struct [[gnu::packed]] LittleAndBigEndian { - T little; - T big; -}; - -// 8.4.26.1 Date and Time Format -struct [[gnu::packed]] AsciiDateAndTime { - // All of these fields are ASCII digits. :^) - u8 year[4]; - u8 month[2]; - u8 day[2]; - - u8 hour[2]; - u8 minute[2]; - u8 second[2]; - u8 hundredths_of_second[2]; - - // From OSDev wiki: - // Time zone offset from GMT in 15 minute intervals, starting at - // interval -48 (west) and running up to interval 52 (east). So value 0 - // indicates interval -48 which equals GMT-12 hours, and value 100 - // indicates interval 52 which equals GMT+13 hours. - u8 timezone_offset; -}; -static_assert(sizeof(AsciiDateAndTime) == 17); - -// 9.1.5 Recording Date and Time (BP 19 to 25) -struct [[gnu::packed]] NumericalDateAndTime { - u8 years_since_1900; - u8 month; - u8 day; - u8 hour; - u8 minute; - u8 second; - // Same format as AsciiDateAndTime. - u8 timezone_offset; -}; -static_assert(sizeof(NumericalDateAndTime) == 7); - -// --- Path Table --- - -// 9.4 Format of a Path Table Record -struct [[gnu::packed]] PathTableRecord { - u8 directory_identifier_length; - u8 extended_attribute_record_length; - u32 extent_location; - u16 parent_directory_number; - - u8 directory_identifier[]; -}; -static_assert(sizeof(PathTableRecord) == 8); - -// --- Extended Attribute Record --- - -// 9.5.3 Permissions -enum class ExtendedPermissions : u16 { - SystemGroupReadable = 1 << 0, - SystemGroupExecutable = 1 << 2, - UserReadable = 1 << 4, - UserExecutable = 1 << 6, - GroupReadable = 1 << 8, - GroupExecutable = 1 << 10, - OtherReadable = 1 << 12, - OtherExecutable = 1 << 14, -}; -AK_ENUM_BITWISE_OPERATORS(ExtendedPermissions); - -// 9.5.8 Record Format -enum class RecordFormat : u8 { - NotSpecified = 0, - FixedLengthRecords = 1, - LittleEndianVariableRecords = 2, - BigEndianVariableRecords = 3, - // 4-127 are reserved for future standardization. - // 128-255 are reserved for system use. -}; - -// 9.5.9 Record Attributes -enum class RecordAttributes : u8 { - // This value means the record is stored like: \n123456\r. - LfCrDelimited = 0, - FortranVerticalSpacing = 1, - ContainsControlInformation = 2, - // 3-255 are reserved for future standardization. -}; - -// 9.5 Format of an Extended Attribute Record -struct [[gnu::packed]] ExtendedAttributeRecord { - LittleAndBigEndian owner_identification; - LittleAndBigEndian group_identification; - ExtendedPermissions permissions; - - AsciiDateAndTime file_creation_date_and_time; - AsciiDateAndTime file_modification_date_and_time; - AsciiDateAndTime file_expiration_date_and_time; - AsciiDateAndTime file_effective_date_and_time; - - RecordFormat record_format; - u8 record_attributes; - - LittleAndBigEndian record_length; - - u8 system_identifier[32]; - u8 system_use[64]; - - u8 extended_attribute_record_version; - u8 escape_sequence_length; - - u8 reserved[64]; - - LittleAndBigEndian application_use_length; - - // NOTE: Application use is immediately followed by escape sequences (no - // padding). - u8 application_use_and_escape_sequences[]; -}; -static_assert(sizeof(ExtendedAttributeRecord) == 250); - -// --- Files and Directories --- - -// 9.1.6 File Flags -enum class FileFlags : u8 { - Hidden = 1 << 0, // The "existence" flag - Directory = 1 << 1, - AssociatedFile = 1 << 2, - Record = 1 << 3, - Protection = 1 << 4, - // 5 and 6 are reserved. - MultiExtent = 1 << 7, -}; - -AK_ENUM_BITWISE_OPERATORS(FileFlags); - -struct [[gnu::packed]] DirectoryRecordHeader { - u8 length; - u8 extended_attribute_record_length; - LittleAndBigEndian extent_location; - LittleAndBigEndian data_length; - NumericalDateAndTime recording_date_and_time; - FileFlags file_flags; - u8 file_unit_size; - u8 interleave_gap_size; - LittleAndBigEndian volume_sequence_number; - u8 file_identifier_length; - - // NOTE: The file identifier itself is of variable length, so it and the - // fields following it are not included in this struct. Instead, they are: - // - // 34 to (33+file_identifier_length) - file identifier - // 1 byte of padding, if file_identifier_length is even - // - // The remaining bytes are system use (ISO9660 extensions). -}; -static_assert(sizeof(DirectoryRecordHeader) == 33); - -// --- Volume Descriptors --- - -enum class VolumeDescriptorType : u8 { - BootRecord = 0, - PrimaryVolumeDescriptor = 1, - SupplementaryOrEnhancedVolumeDescriptor = 2, - VolumePartitionDescriptor = 3, - // 4-254 are reserved. - VolumeDescriptorSetTerminator = 255, -}; - -// 8.1 Format of a Volume Descriptor -struct [[gnu::packed]] VolumeDescriptorHeader { - VolumeDescriptorType type; - // NOTE: Contains exactly "CD001". - u8 identifier[5]; - u8 version; -}; -static_assert(sizeof(VolumeDescriptorHeader) == 7); - -// 8.2 Boot Record -struct [[gnu::packed]] BootRecord { - VolumeDescriptorHeader header; - u8 boot_system_identifier[32]; - u8 boot_identifier[32]; - u8 boot_system_use[1977]; -}; -static_assert(sizeof(BootRecord) == 2048); - -// 8.3 Volume Descriptor Set Terminator -struct [[gnu::packed]] VolumeDescriptorSetTerminator { - VolumeDescriptorHeader header; - u8 zeros[2041]; -}; -static_assert(sizeof(VolumeDescriptorSetTerminator) == 2048); - -// 8.4 Primary Volume Descriptor -struct [[gnu::packed]] PrimaryVolumeDescriptor { - VolumeDescriptorHeader header; - u8 unused1; - u8 system_identifier[32]; - u8 volume_identifier[32]; - u64 unused2; - LittleAndBigEndian volume_space_size; - u8 unused3[32]; - LittleAndBigEndian volume_set_size; - LittleAndBigEndian volume_sequence_number; - LittleAndBigEndian logical_block_size; - LittleAndBigEndian path_table_size; - - u32 l_path_table_occurrence_location; - u32 l_path_table_optional_occurrence_location; - u32 m_path_table_occurrence_location; - u32 m_path_table_optional_occurrence_location; - - DirectoryRecordHeader root_directory_record_header; - u8 root_directory_identifier; // Exactly 0x00. - - u8 volume_set_identifier[128]; - u8 publisher_identifier[128]; - u8 data_preparer_identifier[128]; - u8 application_identifier[128]; - - u8 copyright_file_identifier[37]; - u8 abstract_file_identifier[37]; - u8 bibliographic_file_identifier[37]; - - AsciiDateAndTime volume_creation_date_and_time; - AsciiDateAndTime volume_modification_date_and_time; - AsciiDateAndTime volume_expiration_date_and_time; - AsciiDateAndTime volume_effective_date_and_time; - - u8 file_structure_version; // Always 0x01. - u8 unused4; - u8 application_use[512]; - u8 reserved[653]; -}; -static_assert(sizeof(PrimaryVolumeDescriptor) == 2048); - -// 8.6 Volume Partition Descriptor -struct [[gnu::packed]] VolumePartitionDescriptor { - VolumeDescriptorHeader header; - u8 unused; - - u8 system_identifier[32]; - u8 volume_partition_identifier[32]; - LittleAndBigEndian volume_partition_location; - LittleAndBigEndian volume_partition_size; - - u8 system_use[1960]; -}; -static_assert(sizeof(VolumePartitionDescriptor) == 2048); - -} - -} diff --git a/Kernel/FileSystem/ISO9660FS/DirectoryEntry.h b/Kernel/FileSystem/ISO9660FS/DirectoryEntry.h deleted file mode 100644 index efb82f5907d..00000000000 --- a/Kernel/FileSystem/ISO9660FS/DirectoryEntry.h +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright (c) 2021, sin-ack - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include - -namespace Kernel { - -struct ISO9660FSDirectoryEntry final : public AtomicRefCounted { - u32 extent { 0 }; - u32 length { 0 }; - - // NOTE: This can never be empty if we read the directory successfully. - // We need it as an OwnPtr to default-construct this struct. - OwnPtr blocks; - - static ErrorOr> try_create(u32 extent, u32 length, OwnPtr blocks) - { - return adopt_nonnull_lock_ref_or_enomem(new (nothrow) ISO9660FSDirectoryEntry(extent, length, move(blocks))); - } - -private: - ISO9660FSDirectoryEntry(u32 extent, u32 length, OwnPtr blocks) - : extent(extent) - , length(length) - , blocks(move(blocks)) - { - } -}; - -struct ISO9660FSDirectoryState { - LockRefPtr entry; - u32 offset { 0 }; -}; - -} diff --git a/Kernel/FileSystem/ISO9660FS/DirectoryIterator.cpp b/Kernel/FileSystem/ISO9660FS/DirectoryIterator.cpp deleted file mode 100644 index cedd8d0bc89..00000000000 --- a/Kernel/FileSystem/ISO9660FS/DirectoryIterator.cpp +++ /dev/null @@ -1,134 +0,0 @@ -/* - * Copyright (c) 2021, sin-ack - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include - -namespace Kernel { - -ISO9660DirectoryIterator::ISO9660DirectoryIterator(ISO9660FS& fs, ISO::DirectoryRecordHeader const& header) - : m_fs(fs) - , m_current_header(&header) -{ - // FIXME: Panic or alternative method? - (void)read_directory_contents(); - get_header(); -} - -ErrorOr ISO9660DirectoryIterator::next() -{ - if (done()) - return false; - dbgln_if(ISO9660_VERY_DEBUG, "next(): Called"); - - if (has_flag(m_current_header->file_flags, ISO::FileFlags::Directory)) { - dbgln_if(ISO9660_VERY_DEBUG, "next(): Recursing"); - { - TRY(m_directory_stack.try_append(move(m_current_directory))); - } - - dbgln_if(ISO9660_VERY_DEBUG, "next(): Pushed into directory stack"); - - TRY(read_directory_contents()); - - dbgln_if(ISO9660_VERY_DEBUG, "next(): Read directory contents"); - - m_current_directory.offset = 0; - get_header(); - if (m_current_header->length == 0) { - // We have found an empty directory, let's continue with the - // next one. - if (!go_up()) - return false; - } else { - // We cannot skip here, as this is the first record in this - // extent. - return true; - } - } - - return skip(); -} - -bool ISO9660DirectoryIterator::skip() -{ - VERIFY(m_current_directory.entry); - - if (m_current_directory.offset >= m_current_directory.entry->length) { - dbgln_if(ISO9660_VERY_DEBUG, "skip(): Was at last item already"); - return false; - } - - m_current_directory.offset += m_current_header->length; - get_header(); - if (m_current_header->length == 0) { - // According to ECMA 119, if a logical block contains directory - // records, then the leftover bytes in the logical block are - // all zeros. So if our directory header has a length of 0, - // we're probably looking at padding. - // - // Of course, this doesn't mean we're done; it only means that there - // are no more directory entries in *this* logical block. If we - // have at least one more logical block of data length to go, we - // need to snap to the next logical block, because directory records - // cannot span multiple logical blocks. - u32 remaining_bytes = m_current_directory.entry->length - m_current_directory.offset; - if (remaining_bytes > m_fs.device_block_size()) { - m_current_directory.offset += remaining_bytes % m_fs.device_block_size(); - get_header(); - - dbgln_if(ISO9660_VERY_DEBUG, "skip(): Snapped to next logical block (succeeded)"); - return true; - } - - dbgln_if(ISO9660_VERY_DEBUG, "skip(): Was at the last logical block, at padding now (offset {}, data length {})", m_current_directory.entry->length, m_current_directory.offset); - return false; - } - - dbgln_if(ISO9660_VERY_DEBUG, "skip(): Skipped to next item"); - return true; -} - -bool ISO9660DirectoryIterator::go_up() -{ - if (m_directory_stack.is_empty()) { - dbgln_if(ISO9660_VERY_DEBUG, "go_up(): Empty directory stack"); - return false; - } - - m_current_directory = m_directory_stack.take_last(); - get_header(); - - dbgln_if(ISO9660_VERY_DEBUG, "go_up(): Went up a directory"); - return true; -} - -bool ISO9660DirectoryIterator::done() const -{ - VERIFY(m_current_directory.entry); - auto result = m_directory_stack.is_empty() && m_current_directory.offset >= m_current_directory.entry->length; - dbgln_if(ISO9660_VERY_DEBUG, "done(): {}", result); - return result; -} - -ErrorOr ISO9660DirectoryIterator::read_directory_contents() -{ - m_current_directory.entry = TRY(m_fs.directory_entry_for_record({}, m_current_header)); - return {}; -} - -void ISO9660DirectoryIterator::get_header() -{ - VERIFY(m_current_directory.entry); - if (!m_current_directory.entry->blocks) - return; - - m_current_header = reinterpret_cast(m_current_directory.entry->blocks->data() + m_current_directory.offset); -} - -} diff --git a/Kernel/FileSystem/ISO9660FS/DirectoryIterator.h b/Kernel/FileSystem/ISO9660FS/DirectoryIterator.h deleted file mode 100644 index 783a57ac16b..00000000000 --- a/Kernel/FileSystem/ISO9660FS/DirectoryIterator.h +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright (c) 2021, sin-ack - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include - -namespace Kernel { - -class ISO9660DirectoryIterator { -public: - ISO9660DirectoryIterator(ISO9660FS& fs, ISO::DirectoryRecordHeader const& header); - - ISO::DirectoryRecordHeader const* operator*() { return m_current_header; } - - // Recurses into subdirectories. May fail. - ErrorOr next(); - - // Skips to the directory in the list, returns whether there was a next one. - // No allocation here, cannot fail. - bool skip(); - - bool go_up(); - bool done() const; - -private: - ErrorOr read_directory_contents(); - - void get_header(); - - ISO9660FS& m_fs; - - ISO9660FSDirectoryState m_current_directory; - ISO::DirectoryRecordHeader const* m_current_header { nullptr }; - - Vector m_directory_stack; -}; - -} diff --git a/Kernel/FileSystem/ISO9660FS/FileSystem.cpp b/Kernel/FileSystem/ISO9660FS/FileSystem.cpp deleted file mode 100644 index 08235ee2145..00000000000 --- a/Kernel/FileSystem/ISO9660FS/FileSystem.cpp +++ /dev/null @@ -1,265 +0,0 @@ -/* - * Copyright (c) 2021, sin-ack - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include - -namespace Kernel { - -// NOTE: According to the spec, logical blocks 0 to 15 are system use. -constexpr u32 first_data_area_block = 16; -constexpr u32 logical_sector_size = 2048; -constexpr u32 max_cached_directory_entries = 128; - -ErrorOr> ISO9660FS::try_create(OpenFileDescription& description, ReadonlyBytes) -{ - return TRY(adopt_nonnull_ref_or_enomem(new (nothrow) ISO9660FS(description))); -} - -ISO9660FS::ISO9660FS(OpenFileDescription& description) - : BlockBasedFileSystem(description) -{ - set_logical_block_size(logical_sector_size); - m_device_block_size = logical_sector_size; -} - -ISO9660FS::~ISO9660FS() = default; - -bool ISO9660FS::is_initialized_while_locked() -{ - VERIFY(m_lock.is_locked()); - return !m_root_inode.is_null(); -} - -ErrorOr ISO9660FS::initialize_while_locked() -{ - VERIFY(m_lock.is_locked()); - VERIFY(!is_initialized_while_locked()); - - TRY(BlockBasedFileSystem::initialize_while_locked()); - TRY(parse_volume_set()); - TRY(create_root_inode()); - return {}; -} - -Inode& ISO9660FS::root_inode() -{ - VERIFY(!m_root_inode.is_null()); - return *m_root_inode; -} - -unsigned ISO9660FS::total_block_count() const -{ - return LittleEndian { m_primary_volume->volume_space_size.little }; -} - -unsigned ISO9660FS::total_inode_count() const -{ - if (!m_cached_inode_count) { - auto result = calculate_inode_count(); - if (result.is_error()) { - // FIXME: This should be able to return a ErrorOr. - return 0; - } - } - - return m_cached_inode_count; -} - -u8 ISO9660FS::internal_file_type_to_directory_entry_type(DirectoryEntryView const& entry) const -{ - if (has_flag(static_cast(entry.file_type), ISO::FileFlags::Directory)) { - return DT_DIR; - } - - return DT_REG; -} - -ErrorOr ISO9660FS::prepare_to_clear_last_mount(Inode&) -{ - // FIXME: Do proper cleaning here. - BlockBasedFileSystem::remove_disk_cache_before_last_unmount(); - return {}; -} - -ErrorOr ISO9660FS::parse_volume_set() -{ - VERIFY(!m_primary_volume); - - auto block = TRY(KBuffer::try_create_with_size("ISO9660FS: Temporary volume descriptor storage"sv, m_device_block_size, Memory::Region::Access::Read | Memory::Region::Access::Write)); - auto block_buffer = UserOrKernelBuffer::for_kernel_buffer(block->data()); - - auto current_block_index = first_data_area_block; - while (true) { - auto result = raw_read(BlockIndex { current_block_index }, block_buffer); - if (result.is_error()) { - dbgln_if(ISO9660_DEBUG, "Failed to read volume descriptor from ISO file: {}", result.error()); - return result; - } - - auto const* header = reinterpret_cast(block->data()); - if (StringView { header->identifier, 5 } != "CD001"sv) { - dbgln_if(ISO9660_DEBUG, "Header magic at volume descriptor {} is not valid", current_block_index - first_data_area_block); - return EIO; - } - - switch (header->type) { - case ISO::VolumeDescriptorType::PrimaryVolumeDescriptor: { - auto const* primary_volume = reinterpret_cast(header); - m_primary_volume = adopt_own_if_nonnull(new ISO::PrimaryVolumeDescriptor(*primary_volume)); - break; - } - case ISO::VolumeDescriptorType::BootRecord: - case ISO::VolumeDescriptorType::SupplementaryOrEnhancedVolumeDescriptor: - case ISO::VolumeDescriptorType::VolumePartitionDescriptor: { - break; - } - case ISO::VolumeDescriptorType::VolumeDescriptorSetTerminator: { - goto all_headers_read; - } - default: - dbgln_if(ISO9660_DEBUG, "Unexpected volume descriptor type {} in volume set", static_cast(header->type)); - return EIO; - } - - current_block_index++; - } - -all_headers_read: - if (!m_primary_volume) { - dbgln_if(ISO9660_DEBUG, "Could not find primary volume"); - return EIO; - } - - m_device_block_size = LittleEndian { m_primary_volume->logical_block_size.little }; - return {}; -} - -ErrorOr ISO9660FS::create_root_inode() -{ - if (!m_primary_volume) { - dbgln_if(ISO9660_DEBUG, "Primary volume doesn't exist, can't create root inode"); - return EIO; - } - - m_root_inode = TRY(ISO9660Inode::try_create_from_directory_record(*this, m_primary_volume->root_directory_record_header, {})); - return {}; -} - -ErrorOr ISO9660FS::calculate_inode_count() const -{ - if (!m_primary_volume) { - dbgln_if(ISO9660_DEBUG, "Primary volume doesn't exist, can't calculate inode count"); - return EIO; - } - - size_t inode_count = 1; - - TRY(visit_directory_record(m_primary_volume->root_directory_record_header, [&](ISO::DirectoryRecordHeader const* header) { - if (header == nullptr) { - return RecursionDecision::Continue; - } - - inode_count += 1; - - if (has_flag(header->file_flags, ISO::FileFlags::Directory)) { - if (header->file_identifier_length == 1) { - auto file_identifier = reinterpret_cast(header + 1); - if (file_identifier[0] == '\0' || file_identifier[0] == '\1') { - return RecursionDecision::Continue; - } - } - - return RecursionDecision::Recurse; - } - - return RecursionDecision::Continue; - })); - - m_cached_inode_count = inode_count; - return {}; -} - -ErrorOr ISO9660FS::visit_directory_record(ISO::DirectoryRecordHeader const& record, Function(ISO::DirectoryRecordHeader const*)> const& visitor) const -{ - if (!has_flag(record.file_flags, ISO::FileFlags::Directory)) { - return {}; - } - - ISO9660DirectoryIterator iterator { const_cast(*this), record }; - - while (!iterator.done()) { - auto decision = TRY(visitor(*iterator)); - switch (decision) { - case RecursionDecision::Recurse: { - auto has_moved = TRY(iterator.next()); - if (!has_moved) { - // If next() hasn't moved then we have read through all the - // directories, and can exit. - return {}; - } - - continue; - } - case RecursionDecision::Continue: { - while (!iterator.done()) { - if (iterator.skip()) - break; - if (!iterator.go_up()) - return {}; - } - - continue; - } - case RecursionDecision::Break: - return {}; - } - } - - return {}; -} - -ErrorOr> ISO9660FS::directory_entry_for_record(Badge, ISO::DirectoryRecordHeader const* record) -{ - u32 extent_location = LittleEndian { record->extent_location.little }; - u32 data_length = LittleEndian { record->data_length.little }; - - auto key = calculate_directory_entry_cache_key(*record); - auto it = m_directory_entry_cache.find(key); - if (it != m_directory_entry_cache.end()) { - dbgln_if(ISO9660_DEBUG, "Cache hit for dirent @ {}", extent_location); - return it->value; - } - dbgln_if(ISO9660_DEBUG, "Cache miss for dirent @ {} :^(", extent_location); - - if (m_directory_entry_cache.size() == max_cached_directory_entries) { - // FIXME: A smarter algorithm would probably be nicer. - m_directory_entry_cache.remove(m_directory_entry_cache.begin()); - } - - if (!(data_length % device_block_size() == 0)) { - dbgln_if(ISO9660_DEBUG, "Found a directory with non-logical block size aligned data length!"); - return EIO; - } - - auto blocks = TRY(KBuffer::try_create_with_size("ISO9660FS: Directory traversal buffer"sv, data_length, Memory::Region::Access::Read | Memory::Region::Access::Write)); - auto blocks_buffer = UserOrKernelBuffer::for_kernel_buffer(blocks->data()); - TRY(raw_read_blocks(BlockBasedFileSystem::BlockIndex { extent_location }, data_length / device_block_size(), blocks_buffer)); - auto entry = TRY(ISO9660FSDirectoryEntry::try_create(extent_location, data_length, move(blocks))); - m_directory_entry_cache.set(key, entry); - - dbgln_if(ISO9660_DEBUG, "Cached dirent @ {}", extent_location); - return entry; -} - -u32 ISO9660FS::calculate_directory_entry_cache_key(ISO::DirectoryRecordHeader const& record) -{ - return LittleEndian { record.extent_location.little }; -} - -} diff --git a/Kernel/FileSystem/ISO9660FS/FileSystem.h b/Kernel/FileSystem/ISO9660FS/FileSystem.h deleted file mode 100644 index 1cf60cb9b36..00000000000 --- a/Kernel/FileSystem/ISO9660FS/FileSystem.h +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright (c) 2021, sin-ack - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace Kernel { - -class ISO9660Inode; -class ISO9660DirectoryIterator; - -class ISO9660FS final : public BlockBasedFileSystem { - friend ISO9660Inode; - friend ISO9660DirectoryIterator; - -public: - static ErrorOr> try_create(OpenFileDescription&, ReadonlyBytes); - - virtual ~ISO9660FS() override; - virtual StringView class_name() const override { return "ISO9660FS"sv; } - virtual Inode& root_inode() override; - - virtual unsigned total_block_count() const override; - virtual unsigned total_inode_count() const override; - - virtual u8 internal_file_type_to_directory_entry_type(DirectoryEntryView const& entry) const override; - - ErrorOr> directory_entry_for_record(Badge, ISO::DirectoryRecordHeader const* record); - -private: - ISO9660FS(OpenFileDescription&); - - virtual ErrorOr prepare_to_clear_last_mount(Inode&) override; - - virtual bool is_initialized_while_locked() override; - virtual ErrorOr initialize_while_locked() override; - - ErrorOr parse_volume_set(); - ErrorOr create_root_inode(); - ErrorOr calculate_inode_count() const; - - u32 calculate_directory_entry_cache_key(ISO::DirectoryRecordHeader const&); - - ErrorOr visit_directory_record(ISO::DirectoryRecordHeader const& record, Function(ISO::DirectoryRecordHeader const*)> const& visitor) const; - - OwnPtr m_primary_volume; - RefPtr m_root_inode; - - mutable u32 m_cached_inode_count { 0 }; - HashMap> m_directory_entry_cache; -}; - -} diff --git a/Kernel/FileSystem/ISO9660FS/Inode.cpp b/Kernel/FileSystem/ISO9660FS/Inode.cpp deleted file mode 100644 index ff7c2a8cd2f..00000000000 --- a/Kernel/FileSystem/ISO9660FS/Inode.cpp +++ /dev/null @@ -1,252 +0,0 @@ -/* - * Copyright (c) 2021, sin-ack - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include - -namespace Kernel { - -ErrorOr ISO9660Inode::read_bytes_locked(off_t offset, size_t size, UserOrKernelBuffer& buffer, OpenFileDescription*) const -{ - VERIFY(m_inode_lock.is_locked()); - - u32 data_length = LittleEndian { m_record.data_length.little }; - u32 extent_location = LittleEndian { m_record.extent_location.little }; - - if (static_cast(offset) >= data_length) - return 0; - - auto block = TRY(KBuffer::try_create_with_size("ISO9660FS: Inode read buffer"sv, fs().device_block_size())); - auto block_buffer = UserOrKernelBuffer::for_kernel_buffer(block->data()); - - size_t total_bytes = min(size, data_length - offset); - size_t nread = 0; - size_t blocks_already_read = offset / fs().device_block_size(); - size_t initial_offset = offset % fs().device_block_size(); - - auto current_block_index = BlockBasedFileSystem::BlockIndex { extent_location + blocks_already_read }; - while (nread != total_bytes) { - size_t bytes_to_read = min(total_bytes - nread, fs().device_block_size() - initial_offset); - auto buffer_offset = buffer.offset(nread); - dbgln_if(ISO9660_VERY_DEBUG, "ISO9660Inode::read_bytes: Reading {} bytes into buffer offset {}/{}, logical block index: {}", bytes_to_read, nread, total_bytes, current_block_index.value()); - - TRY(const_cast(fs()).raw_read(current_block_index, block_buffer)); - TRY(buffer_offset.write(block->data() + initial_offset, bytes_to_read)); - - nread += bytes_to_read; - initial_offset = 0; - current_block_index = BlockBasedFileSystem::BlockIndex { current_block_index.value() + 1 }; - } - - return nread; -} - -InodeMetadata ISO9660Inode::metadata() const -{ - return m_metadata; -} - -ErrorOr ISO9660Inode::traverse_as_directory(Function(FileSystem::DirectoryEntryView const&)> visitor) const -{ - Array file_identifier_buffer; - ErrorOr result; - - return fs().visit_directory_record(m_record, [&](ISO::DirectoryRecordHeader const* record) { - StringView filename = get_normalized_filename(*record, file_identifier_buffer); - dbgln_if(ISO9660_VERY_DEBUG, "traverse_as_directory(): Found {}", filename); - - InodeIdentifier id { fsid(), get_inode_index(*record, filename) }; - auto entry = FileSystem::DirectoryEntryView(filename, id, static_cast(record->file_flags)); - - result = visitor(entry); - if (result.is_error()) - return RecursionDecision::Break; - - return RecursionDecision::Continue; - }); -} - -ErrorOr ISO9660Inode::replace_child(StringView, Inode&) -{ - return EROFS; -} - -ErrorOr> ISO9660Inode::lookup(StringView name) -{ - RefPtr inode; - Array file_identifier_buffer; - - TRY(fs().visit_directory_record(m_record, [&](ISO::DirectoryRecordHeader const* record) { - StringView filename = get_normalized_filename(*record, file_identifier_buffer); - - if (filename == name) { - auto maybe_inode = ISO9660Inode::try_create_from_directory_record(fs(), *record, filename); - if (maybe_inode.is_error()) { - // FIXME: The Inode API does not handle allocation failures very - // well... we can't return a ErrorOr from here. It - // would be nice if we could return a ErrorOr(Or) from - // any place where allocation may happen. - dbgln("Could not allocate inode for lookup!"); - } else { - inode = maybe_inode.release_value(); - } - return RecursionDecision::Break; - } - - return RecursionDecision::Continue; - })); - - if (!inode) - return ENOENT; - return inode.release_nonnull(); -} - -ErrorOr ISO9660Inode::flush_metadata() -{ - return {}; -} - -ErrorOr ISO9660Inode::write_bytes_locked(off_t, size_t, UserOrKernelBuffer const&, OpenFileDescription*) -{ - return EROFS; -} - -ErrorOr> ISO9660Inode::create_child(StringView, mode_t, dev_t, UserID, GroupID) -{ - return EROFS; -} - -ErrorOr ISO9660Inode::add_child(Inode&, StringView, mode_t) -{ - return EROFS; -} - -ErrorOr ISO9660Inode::remove_child(StringView) -{ - return EROFS; -} - -ErrorOr ISO9660Inode::chmod(mode_t) -{ - return EROFS; -} - -ErrorOr ISO9660Inode::chown(UserID, GroupID) -{ - return EROFS; -} - -ErrorOr ISO9660Inode::truncate_locked(u64) -{ - return EROFS; -} - -ErrorOr ISO9660Inode::update_timestamps(Optional, Optional, Optional) -{ - return EROFS; -} - -ISO9660Inode::ISO9660Inode(ISO9660FS& fs, ISO::DirectoryRecordHeader const& record, StringView name) - : Inode(fs, get_inode_index(record, name)) - , m_record(record) -{ - dbgln_if(ISO9660_VERY_DEBUG, "Creating inode #{}", index()); - create_metadata(); -} - -ISO9660Inode::~ISO9660Inode() = default; - -ErrorOr> ISO9660Inode::try_create_from_directory_record(ISO9660FS& fs, ISO::DirectoryRecordHeader const& record, StringView name) -{ - return adopt_nonnull_ref_or_enomem(new (nothrow) ISO9660Inode(fs, record, name)); -} - -void ISO9660Inode::create_metadata() -{ - u32 data_length = LittleEndian { m_record.data_length.little }; - bool is_directory = has_flag(m_record.file_flags, ISO::FileFlags::Directory); - auto recorded_at = parse_numerical_date_time(m_record.recording_date_and_time); - - m_metadata = { - .inode = identifier(), - .size = data_length, - .mode = static_cast((is_directory ? S_IFDIR : S_IFREG) | (is_directory ? 0555 : 0444)), - .uid = 0, - .gid = 0, - .link_count = 1, - .atime = recorded_at, - .ctime = recorded_at, - .mtime = recorded_at, - .dtime = {}, - .block_count = 0, - .block_size = 0, - .major_device = 0, - .minor_device = 0, - }; -} - -UnixDateTime ISO9660Inode::parse_numerical_date_time(ISO::NumericalDateAndTime const& date) -{ - i32 year_offset = date.years_since_1900 - 70; - - // FIXME: This ignores timezone information in date. - return UnixDateTime::from_unix_time_parts(year_offset, date.month, date.day, date.hour, date.minute, date.second, 0); -} - -StringView ISO9660Inode::get_normalized_filename(ISO::DirectoryRecordHeader const& record, Bytes buffer) -{ - auto const* file_identifier = reinterpret_cast(&record + 1); - auto filename = StringView { file_identifier, record.file_identifier_length }; - - if (filename.length() == 1) { - if (filename[0] == '\0') { - filename = "."sv; - } - - if (filename[0] == '\1') { - filename = ".."sv; - } - } - - if (!has_flag(record.file_flags, ISO::FileFlags::Directory)) { - // FIXME: We currently strip the file version from the filename, - // but that may be used later down the line if the file actually - // has multiple versions on the disk. - Optional semicolon = filename.find(';'); - if (semicolon.has_value()) { - filename = filename.substring_view(0, semicolon.value()); - } - - if (filename[filename.length() - 1] == '.') { - filename = filename.substring_view(0, filename.length() - 1); - } - } - - if (filename.length() > buffer.size()) { - // FIXME: Rock Ridge allows filenames up to 255 characters, so we should - // probably support that instead of truncating. - filename = filename.substring_view(0, buffer.size()); - } - - for (size_t i = 0; i < filename.length(); i++) { - buffer[i] = to_ascii_lowercase(filename[i]); - } - - return { buffer.data(), filename.length() }; -} - -InodeIndex ISO9660Inode::get_inode_index(ISO::DirectoryRecordHeader const& record, StringView name) -{ - if (name.is_null()) { - // NOTE: This is the index of the root inode. - return 1; - } - - return { pair_int_hash(LittleEndian { record.extent_location.little }, string_hash(name.characters_without_null_termination(), name.length())) }; -} - -} diff --git a/Kernel/FileSystem/ISO9660FS/Inode.h b/Kernel/FileSystem/ISO9660FS/Inode.h deleted file mode 100644 index 83ddacebb8a..00000000000 --- a/Kernel/FileSystem/ISO9660FS/Inode.h +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright (c) 2021, sin-ack - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include - -namespace Kernel { - -class ISO9660Inode final : public Inode { - friend ISO9660FS; - -public: - virtual ~ISO9660Inode() override; - - ISO9660FS& fs() { return static_cast(Inode::fs()); } - ISO9660FS const& fs() const { return static_cast(Inode::fs()); } - - // ^Inode - virtual InodeMetadata metadata() const override; - virtual ErrorOr traverse_as_directory(Function(FileSystem::DirectoryEntryView const&)>) const override; - virtual ErrorOr> lookup(StringView name) override; - virtual ErrorOr flush_metadata() override; - virtual ErrorOr> create_child(StringView name, mode_t, dev_t, UserID, GroupID) override; - virtual ErrorOr add_child(Inode&, StringView name, mode_t) override; - virtual ErrorOr remove_child(StringView name) override; - virtual ErrorOr replace_child(StringView name, Inode& child) override; - virtual ErrorOr chmod(mode_t) override; - virtual ErrorOr chown(UserID, GroupID) override; - virtual ErrorOr truncate_locked(u64) override; - virtual ErrorOr update_timestamps(Optional atime, Optional ctime, Optional mtime) override; - -private: - // HACK: The base ISO 9660 standard says the maximum filename length is 37 - // bytes large; however, we can read filenames longer than that right now - // without any problems, so let's allow it anyway. - static constexpr size_t max_file_identifier_length = 256 - sizeof(ISO::DirectoryRecordHeader); - - // ^Inode - virtual ErrorOr read_bytes_locked(off_t, size_t, UserOrKernelBuffer& buffer, OpenFileDescription*) const override; - virtual ErrorOr write_bytes_locked(off_t, size_t, UserOrKernelBuffer const& buffer, OpenFileDescription*) override; - - ISO9660Inode(ISO9660FS&, ISO::DirectoryRecordHeader const& record, StringView name); - static ErrorOr> try_create_from_directory_record(ISO9660FS&, ISO::DirectoryRecordHeader const& record, StringView name); - - static InodeIndex get_inode_index(ISO::DirectoryRecordHeader const& record, StringView name); - static StringView get_normalized_filename(ISO::DirectoryRecordHeader const& record, Bytes buffer); - - void create_metadata(); - UnixDateTime parse_numerical_date_time(ISO::NumericalDateAndTime const&); - - InodeMetadata m_metadata; - ISO::DirectoryRecordHeader m_record; -}; - -} - -using Kernel::ISO::has_any_flag; -using Kernel::ISO::has_flag; diff --git a/Kernel/FileSystem/Initializer.h b/Kernel/FileSystem/Initializer.h deleted file mode 100644 index e1eb094be5c..00000000000 --- a/Kernel/FileSystem/Initializer.h +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright (c) 2022, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include -#include - -namespace Kernel { - -struct FileSystemInitializer { - StringView short_name; - StringView name; - bool requires_open_file_description { false }; - bool requires_block_device { false }; - bool requires_seekable_file { false }; - ErrorOr> (*create_with_fd)(OpenFileDescription&, ReadonlyBytes) = nullptr; - ErrorOr> (*create)(ReadonlyBytes) = nullptr; - ErrorOr (*handle_mount_boolean_flag)(Span, StringView key, bool) = nullptr; - ErrorOr (*handle_mount_unsigned_integer_flag)(Span, StringView key, u64) = nullptr; - ErrorOr (*handle_mount_signed_integer_flag)(Span, StringView key, i64) = nullptr; - ErrorOr (*handle_mount_ascii_string_flag)(Span, StringView key, StringView value) = nullptr; -}; - -} diff --git a/Kernel/FileSystem/Inode.cpp b/Kernel/FileSystem/Inode.cpp deleted file mode 100644 index 2918fa41a5a..00000000000 --- a/Kernel/FileSystem/Inode.cpp +++ /dev/null @@ -1,435 +0,0 @@ -/* - * Copyright (c) 2018-2021, Andreas Kling - * Copyright (c) 2021, sin-ack - * Copyright (c) 2022, Idan Horowitz - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace Kernel { - -static Singleton> s_all_instances; - -SpinlockProtected& Inode::all_instances() -{ - return s_all_instances; -} - -void Inode::sync_all() -{ - Vector, 32> inodes; - Inode::all_instances().with([&](auto& all_inodes) { - for (auto& inode : all_inodes) { - if (inode.is_metadata_dirty()) - inodes.append(inode); - } - }); - - for (auto& inode : inodes) { - (void)inode->flush_metadata(); - } -} - -void Inode::sync() -{ - (void)flush_metadata(); - auto result = fs().flush_writes(); - if (result.is_error()) { - // TODO: Figure out how to propagate error to a higher function. - } -} - -ErrorOr> Inode::resolve_as_link(Credentials const& credentials, CustodyBase const& base, RefPtr* out_parent, int options, int symlink_recursion_level) const -{ - // The default implementation simply treats the stored - // contents as a path and resolves that. That is, it - // behaves exactly how you would expect a symlink to work. - - // Make sure that our assumptions about the path length hold up. - // Note that this doesn't mean that the reported size can be trusted, some inodes just report zero. - VERIFY(size() <= MAXPATHLEN); - - Array contents; - auto read_bytes = TRY(read_until_filled_or_end(0, contents.size(), UserOrKernelBuffer::for_kernel_buffer(contents.data()), nullptr)); - return VirtualFileSystem::the().resolve_path(credentials, StringView { contents.span().trim(read_bytes) }, base, out_parent, options, symlink_recursion_level); -} - -Inode::Inode(FileSystem& fs, InodeIndex index) - : m_file_system(fs) - , m_index(index) -{ - Inode::all_instances().with([&](auto& all_inodes) { all_inodes.append(*this); }); -} - -Inode::~Inode() -{ - m_watchers.for_each([&](auto& watcher) { - watcher->unregister_by_inode({}, identifier()); - }); -} - -void Inode::will_be_destroyed() -{ - MutexLocker locker(m_inode_lock); - if (m_metadata_dirty) - (void)flush_metadata(); -} - -ErrorOr Inode::truncate(u64 size) -{ - MutexLocker locker(m_inode_lock); - return truncate_locked(size); -} - -ErrorOr Inode::write_bytes(off_t offset, size_t length, UserOrKernelBuffer const& target_buffer, OpenFileDescription* open_description) -{ - MutexLocker locker(m_inode_lock); - return prepare_and_write_bytes_locked(offset, length, target_buffer, open_description); -} - -ErrorOr Inode::prepare_and_write_bytes_locked(off_t offset, size_t length, UserOrKernelBuffer const& target_buffer, OpenFileDescription* open_description) -{ - VERIFY(m_inode_lock.is_locked()); - TRY(prepare_to_write_data()); - return write_bytes_locked(offset, length, target_buffer, open_description); -} - -ErrorOr Inode::read_bytes(off_t offset, size_t length, UserOrKernelBuffer& buffer, OpenFileDescription* open_description) const -{ - MutexLocker locker(m_inode_lock, Mutex::Mode::Shared); - return read_bytes_locked(offset, length, buffer, open_description); -} - -ErrorOr Inode::read_until_filled_or_end(off_t offset, size_t length, UserOrKernelBuffer buffer, OpenFileDescription* open_description) const -{ - auto remaining_length = length; - - while (remaining_length > 0) { - auto filled_bytes = TRY(read_bytes(offset, remaining_length, buffer, open_description)); - if (filled_bytes == 0) - break; - offset += filled_bytes; - remaining_length -= filled_bytes; - } - - return length - remaining_length; -} - -ErrorOr Inode::update_timestamps([[maybe_unused]] Optional atime, [[maybe_unused]] Optional ctime, [[maybe_unused]] Optional mtime) -{ - return ENOTIMPL; -} - -ErrorOr Inode::increment_link_count() -{ - return ENOTIMPL; -} - -ErrorOr Inode::decrement_link_count() -{ - return ENOTIMPL; -} - -ErrorOr Inode::set_shared_vmobject(Memory::SharedInodeVMObject& vmobject) -{ - MutexLocker locker(m_inode_lock); - m_shared_vmobject = TRY(vmobject.try_make_weak_ptr()); - return {}; -} - -LockRefPtr Inode::bound_socket() const -{ - return m_bound_socket.strong_ref(); -} - -bool Inode::bind_socket(LocalSocket& socket) -{ - MutexLocker locker(m_inode_lock); - if (m_bound_socket) - return false; - m_bound_socket = socket; - return true; -} - -bool Inode::unbind_socket() -{ - MutexLocker locker(m_inode_lock); - if (!m_bound_socket) - return false; - m_bound_socket = nullptr; - return true; -} - -ErrorOr Inode::register_watcher(Badge, InodeWatcher& watcher) -{ - return m_watchers.with([&](auto& watchers) -> ErrorOr { - VERIFY(!watchers.contains(&watcher)); - TRY(watchers.try_set(&watcher)); - return {}; - }); -} - -void Inode::unregister_watcher(Badge, InodeWatcher& watcher) -{ - m_watchers.with([&](auto& watchers) { - VERIFY(watchers.contains(&watcher)); - watchers.remove(&watcher); - }); -} - -ErrorOr> Inode::fifo() -{ - MutexLocker locker(m_inode_lock); - VERIFY(metadata().is_fifo()); - - // FIXME: Release m_fifo when it is closed by all readers and writers - if (!m_fifo) - m_fifo = TRY(FIFO::try_create(metadata().uid)); - - return NonnullRefPtr { *m_fifo }; -} - -void Inode::set_metadata_dirty(bool metadata_dirty) -{ - MutexLocker locker(m_inode_lock); - - if (metadata_dirty) { - // Sanity check. - VERIFY(!fs().is_readonly()); - } - - if (m_metadata_dirty == metadata_dirty) - return; - - m_metadata_dirty = metadata_dirty; - if (m_metadata_dirty) { - // FIXME: Maybe we should hook into modification events somewhere else, I'm not sure where. - // We don't always end up on this particular code path, for instance when writing to an ext2fs file. - m_watchers.for_each([&](auto& watcher) { - watcher->notify_inode_event({}, identifier(), InodeWatcherEvent::Type::MetadataModified); - }); - } -} - -void Inode::did_add_child(InodeIdentifier, StringView name) -{ - m_watchers.for_each([&](auto& watcher) { - watcher->notify_inode_event({}, identifier(), InodeWatcherEvent::Type::ChildCreated, name); - }); -} - -void Inode::did_remove_child(InodeIdentifier, StringView name) -{ - if (name == "." || name == "..") { - // These are just aliases and are not interesting to userspace. - return; - } - - m_watchers.for_each([&](auto& watcher) { - watcher->notify_inode_event({}, identifier(), InodeWatcherEvent::Type::ChildDeleted, name); - }); -} - -void Inode::did_modify_contents() -{ - // FIXME: What happens if this fails? - // ENOTIMPL would be a meaningless error to return here - auto now = kgettimeofday(); - (void)update_timestamps({}, now, now); - - m_watchers.for_each([&](auto& watcher) { - watcher->notify_inode_event({}, identifier(), InodeWatcherEvent::Type::ContentModified); - }); -} - -void Inode::did_delete_self() -{ - m_watchers.for_each([&](auto& watcher) { - watcher->notify_inode_event({}, identifier(), InodeWatcherEvent::Type::Deleted); - }); -} - -ErrorOr Inode::prepare_to_write_data() -{ - VERIFY(m_inode_lock.is_locked()); - if (fs().is_readonly()) - return EROFS; - auto metadata = this->metadata(); - if (metadata.is_setuid() || metadata.is_setgid()) { - dbgln("Inode::prepare_to_write_data(): Stripping SUID/SGID bits from {}", identifier()); - return chmod(metadata.mode & ~(04000 | 02000)); - } - return {}; -} - -LockRefPtr Inode::shared_vmobject() const -{ - MutexLocker locker(m_inode_lock); - return m_shared_vmobject.strong_ref(); -} - -template -static inline bool range_overlap(T start1, T len1, T start2, T len2) -{ - return ((start1 < start2 + len2) || len2 == 0) && ((start2 < start1 + len1) || len1 == 0); -} - -static inline ErrorOr normalize_flock(OpenFileDescription const& description, flock& lock) -{ - off_t start; - switch (lock.l_whence) { - case SEEK_SET: - start = lock.l_start; - break; - case SEEK_CUR: - start = description.offset() + lock.l_start; - break; - case SEEK_END: - // FIXME: Implement SEEK_END and negative lengths. - return ENOTSUP; - default: - return EINVAL; - } - lock = { lock.l_type, SEEK_SET, start, lock.l_len, 0 }; - return {}; -} - -bool Inode::can_apply_flock(flock const& new_lock, Optional description) const -{ - VERIFY(new_lock.l_whence == SEEK_SET); - - if (new_lock.l_type == F_UNLCK) - return true; - - return m_flocks.with([&](auto& flocks) { - for (auto const& lock : flocks) { - if (!range_overlap(lock.start, lock.len, new_lock.l_start, new_lock.l_len)) - continue; - - // There are two cases where we can attempt downgrade: - // - // 1) We're the owner of this lock. The downgrade will immediately - // succeed. - // 2) We're not the owner of this lock. Our downgrade attempt will - // fail, and the thread will start blocking on an FlockBlocker. - // - // For the first case, we get the description from try_apply_flock - // below. For the second case, the check below would always be - // false, so there is no need to store the description in the - // blocker in the first place. - if (new_lock.l_type == F_RDLCK && lock.type == F_WRLCK) - return description.has_value() && lock.owner == &description.value() && lock.start == new_lock.l_start && lock.len == new_lock.l_len; - - if (new_lock.l_type == F_WRLCK) - return false; - } - return true; - }); -} - -ErrorOr Inode::try_apply_flock(Process const& process, OpenFileDescription const& description, flock const& new_lock) -{ - return m_flocks.with([&](auto& flocks) -> ErrorOr { - if (!can_apply_flock(new_lock, description)) - return false; - - bool did_manipulate_lock = false; - for (size_t i = 0; i < flocks.size(); ++i) { - auto const& lock = flocks[i]; - - bool is_potential_downgrade = new_lock.l_type == F_RDLCK && lock.type == F_WRLCK; - bool is_potential_unlock = new_lock.l_type == F_UNLCK; - - bool is_lock_owner = &description == lock.owner; - bool lock_range_exactly_matches = lock.start == new_lock.l_start && lock.len == new_lock.l_len; - bool can_manage_this_lock = is_lock_owner && lock_range_exactly_matches; - - if ((is_potential_downgrade || is_potential_unlock) && can_manage_this_lock) { - flocks.remove(i); - did_manipulate_lock = true; - break; - } - } - - if (new_lock.l_type != F_UNLCK) - TRY(flocks.try_append(Flock { new_lock.l_start, new_lock.l_len, &description, process.pid().value(), new_lock.l_type })); - - if (did_manipulate_lock) - m_flock_blocker_set.unblock_all_blockers_whose_conditions_are_met(); - - // Judging by the Linux implementation, unlocking a non-existent lock - // also works. - return true; - }); -} - -ErrorOr Inode::apply_flock(Process const& process, OpenFileDescription const& description, Userspace input_lock, ShouldBlock should_block) -{ - auto new_lock = TRY(copy_typed_from_user(input_lock)); - TRY(normalize_flock(description, new_lock)); - - while (true) { - auto success = TRY(try_apply_flock(process, description, new_lock)); - if (success) - return {}; - - if (should_block == ShouldBlock::No) - return EAGAIN; - - if (Thread::current()->block({}, *this, new_lock).was_interrupted()) - return EINTR; - } -} - -ErrorOr Inode::get_flock(OpenFileDescription const& description, Userspace reference_lock) const -{ - flock lookup = {}; - TRY(copy_from_user(&lookup, reference_lock)); - TRY(normalize_flock(description, lookup)); - - return m_flocks.with([&](auto& flocks) { - for (auto const& lock : flocks) { - if (!range_overlap(lock.start, lock.len, lookup.l_start, lookup.l_len)) - continue; - - // Locks with the same owner can't conflict with each other. - if (lock.pid == Process::current().pid()) - continue; - - if ((lookup.l_type == F_RDLCK && lock.type == F_WRLCK) || lookup.l_type == F_WRLCK) { - lookup = { lock.type, SEEK_SET, lock.start, lock.len, lock.pid }; - return copy_to_user(reference_lock, &lookup); - } - } - - lookup.l_type = F_UNLCK; - return copy_to_user(reference_lock, &lookup); - }); -} - -void Inode::remove_flocks_for_description(OpenFileDescription const& description) -{ - m_flocks.with([&](auto& flocks) { - flocks.remove_all_matching([&](auto& entry) { return entry.owner == &description; }); - }); -} - -bool Inode::has_watchers() const -{ - return !m_watchers.with([&](auto& watchers) { return watchers.is_empty(); }); -} - -} diff --git a/Kernel/FileSystem/Inode.h b/Kernel/FileSystem/Inode.h deleted file mode 100644 index da0e1ad91e0..00000000000 --- a/Kernel/FileSystem/Inode.h +++ /dev/null @@ -1,159 +0,0 @@ -/* - * Copyright (c) 2018-2021, Andreas Kling - * Copyright (c) 2021, sin-ack - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace Kernel { - -enum class ShouldBlock { - No = 0, - Yes = 1 -}; - -class Inode : public ListedRefCounted - , public LockWeakable { - friend class VirtualFileSystem; - friend class FileSystem; - friend class InodeFile; - -public: - virtual ~Inode(); - - virtual void remove_from_secondary_lists() { } - - FileSystem& fs() { return m_file_system; } - FileSystem const& fs() const { return m_file_system; } - FileSystemID fsid() const { return m_file_system.fsid(); } - InodeIndex index() const { return m_index; } - - size_t size() const { return metadata().size; } - bool is_symlink() const { return metadata().is_symlink(); } - bool is_directory() const { return metadata().is_directory(); } - bool is_character_device() const { return metadata().is_character_device(); } - mode_t mode() const { return metadata().mode; } - - InodeIdentifier identifier() const { return { fsid(), index() }; } - virtual InodeMetadata metadata() const = 0; - - ErrorOr write_bytes(off_t, size_t, UserOrKernelBuffer const& data, OpenFileDescription*); - ErrorOr read_bytes(off_t, size_t, UserOrKernelBuffer& buffer, OpenFileDescription*) const; - ErrorOr read_until_filled_or_end(off_t, size_t, UserOrKernelBuffer buffer, OpenFileDescription*) const; - ErrorOr truncate(u64); - - virtual ErrorOr attach(OpenFileDescription&) { return {}; } - virtual void detach(OpenFileDescription&) { } - virtual void did_seek(OpenFileDescription&, off_t) { } - virtual ErrorOr traverse_as_directory(Function(FileSystem::DirectoryEntryView const&)>) const = 0; - virtual ErrorOr> lookup(StringView name) = 0; - virtual ErrorOr> create_child(StringView name, mode_t, dev_t, UserID, GroupID) = 0; - virtual ErrorOr add_child(Inode&, StringView name, mode_t) = 0; - virtual ErrorOr remove_child(StringView name) = 0; - /// Replace child atomically, incrementing the link count of the replacement - /// inode and decrementing the older inode's. - virtual ErrorOr replace_child(StringView name, Inode&) = 0; - virtual ErrorOr chmod(mode_t) = 0; - virtual ErrorOr chown(UserID, GroupID) = 0; - - ErrorOr> resolve_as_link(Credentials const&, CustodyBase const& base, RefPtr* out_parent, int options, int symlink_recursion_level) const; - - virtual ErrorOr get_block_address(int) { return ENOTSUP; } - - LockRefPtr bound_socket() const; - bool bind_socket(LocalSocket&); - bool unbind_socket(); - - bool is_metadata_dirty() const { return m_metadata_dirty; } - - virtual ErrorOr update_timestamps(Optional atime, Optional ctime, Optional mtime); - virtual ErrorOr increment_link_count(); - virtual ErrorOr decrement_link_count(); - - virtual ErrorOr flush_metadata() = 0; - - void will_be_destroyed(); - - ErrorOr set_shared_vmobject(Memory::SharedInodeVMObject&); - LockRefPtr shared_vmobject() const; - - static void sync_all(); - void sync(); - - bool has_watchers() const; - - ErrorOr register_watcher(Badge, InodeWatcher&); - void unregister_watcher(Badge, InodeWatcher&); - - ErrorOr> fifo(); - - bool can_apply_flock(flock const&, Optional = {}) const; - ErrorOr apply_flock(Process const&, OpenFileDescription const&, Userspace, ShouldBlock); - ErrorOr get_flock(OpenFileDescription const&, Userspace) const; - void remove_flocks_for_description(OpenFileDescription const&); - Thread::FlockBlockerSet& flock_blocker_set() { return m_flock_blocker_set; } - -protected: - Inode(FileSystem&, InodeIndex); - void set_metadata_dirty(bool); - ErrorOr prepare_to_write_data(); - - void did_add_child(InodeIdentifier child_id, StringView); - void did_remove_child(InodeIdentifier child_id, StringView); - void did_modify_contents(); - void did_delete_self(); - - mutable Mutex m_inode_lock { "Inode"sv }; - - ErrorOr prepare_and_write_bytes_locked(off_t, size_t, UserOrKernelBuffer const& data, OpenFileDescription*); - - virtual ErrorOr write_bytes_locked(off_t, size_t, UserOrKernelBuffer const& data, OpenFileDescription*) = 0; - virtual ErrorOr read_bytes_locked(off_t, size_t, UserOrKernelBuffer& buffer, OpenFileDescription*) const = 0; - virtual ErrorOr truncate_locked(u64) { return {}; } - -private: - ErrorOr try_apply_flock(Process const&, OpenFileDescription const&, flock const&); - - FileSystem& m_file_system; - InodeIndex m_index { 0 }; - LockWeakPtr m_shared_vmobject; - LockWeakPtr m_bound_socket; - SpinlockProtected, LockRank::None> m_watchers {}; - bool m_metadata_dirty { false }; - RefPtr m_fifo; - IntrusiveListNode m_inode_list_node; - - struct Flock { - off_t start; - off_t len; - OpenFileDescription const* owner; - pid_t pid; - short type; - }; - - Thread::FlockBlockerSet m_flock_blocker_set; - SpinlockProtected, LockRank::None> m_flocks {}; - -public: - using AllInstancesList = IntrusiveList<&Inode::m_inode_list_node>; - static SpinlockProtected& all_instances(); -}; - -} diff --git a/Kernel/FileSystem/InodeFile.cpp b/Kernel/FileSystem/InodeFile.cpp deleted file mode 100644 index b646e39b7c4..00000000000 --- a/Kernel/FileSystem/InodeFile.cpp +++ /dev/null @@ -1,130 +0,0 @@ -/* - * Copyright (c) 2018-2022, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace Kernel { - -InodeFile::InodeFile(NonnullRefPtr inode) - : m_inode(move(inode)) -{ -} - -InodeFile::~InodeFile() = default; - -ErrorOr InodeFile::read(OpenFileDescription& description, u64 offset, UserOrKernelBuffer& buffer, size_t count) -{ - if (Checked::addition_would_overflow(offset, count)) - return EOVERFLOW; - - auto nread = TRY(m_inode->read_bytes(offset, count, buffer, &description)); - if (nread > 0) { - Thread::current()->did_file_read(nread); - evaluate_block_conditions(); - } - return nread; -} - -ErrorOr InodeFile::write(OpenFileDescription& description, u64 offset, UserOrKernelBuffer const& data, size_t count) -{ - if (Checked::addition_would_overflow(offset, count)) - return EOVERFLOW; - - size_t nwritten = TRY(m_inode->write_bytes(offset, count, data, &description)); - if (nwritten > 0) { - auto mtime_result = m_inode->update_timestamps({}, {}, kgettimeofday()); - Thread::current()->did_file_write(nwritten); - evaluate_block_conditions(); - if (mtime_result.is_error()) - return mtime_result.release_error(); - } - return nwritten; -} - -ErrorOr InodeFile::ioctl(OpenFileDescription& description, unsigned request, Userspace arg) -{ - switch (request) { - case FIBMAP: { - auto current_process_credentials = Process::current().credentials(); - if (!current_process_credentials->is_superuser()) - return EPERM; - - auto user_block_number = static_ptr_cast(arg); - int block_number = 0; - TRY(copy_from_user(&block_number, user_block_number)); - - if (block_number < 0) - return EINVAL; - - auto block_address = TRY(inode().get_block_address(block_number)); - return copy_to_user(user_block_number, &block_address); - } - case FIONREAD: { - int remaining_bytes = inode().size() - description.offset(); - return copy_to_user(static_ptr_cast(arg), &remaining_bytes); - } - default: - return EINVAL; - } -} - -ErrorOr> InodeFile::vmobject_for_mmap(Process&, Memory::VirtualRange const& range, u64& offset, bool shared) -{ - if (shared) - return TRY(Memory::SharedInodeVMObject::try_create_with_inode_and_range(inode(), offset, range.size())); - return TRY(Memory::PrivateInodeVMObject::try_create_with_inode_and_range(inode(), offset, range.size())); -} - -ErrorOr> InodeFile::pseudo_path(OpenFileDescription const&) const -{ - // If it has an inode, then it has a path, and therefore the caller should have been able to get a custody at some point. - VERIFY_NOT_REACHED(); -} - -ErrorOr InodeFile::truncate(u64 size) -{ - TRY(m_inode->truncate(size)); - // FIXME: Make sure that the timestamps are updated by Inode::truncate for all filesystems before removing this. - auto truncated_at = kgettimeofday(); - TRY(m_inode->update_timestamps({}, truncated_at, truncated_at)); - return {}; -} - -ErrorOr InodeFile::sync() -{ - m_inode->sync(); - return {}; -} - -ErrorOr InodeFile::chown(Credentials const& credentials, OpenFileDescription& description, UserID uid, GroupID gid) -{ - VERIFY(description.inode() == m_inode); - VERIFY(description.custody()); - return VirtualFileSystem::the().chown(credentials, *description.custody(), uid, gid); -} - -ErrorOr InodeFile::chmod(Credentials const& credentials, OpenFileDescription& description, mode_t mode) -{ - VERIFY(description.inode() == m_inode); - VERIFY(description.custody()); - return VirtualFileSystem::the().chmod(credentials, *description.custody(), mode); -} - -bool InodeFile::is_regular_file() const -{ - return inode().metadata().is_regular_file(); -} - -} diff --git a/Kernel/FileSystem/InodeFile.h b/Kernel/FileSystem/InodeFile.h deleted file mode 100644 index dc94da0eb45..00000000000 --- a/Kernel/FileSystem/InodeFile.h +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright (c) 2018-2022, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include - -namespace Kernel { - -class Inode; - -class InodeFile final : public File { -public: - static ErrorOr> create(NonnullRefPtr inode) - { - auto file = adopt_ref_if_nonnull(new (nothrow) InodeFile(move(inode))); - if (!file) - return ENOMEM; - return file.release_nonnull(); - } - - virtual ~InodeFile() override; - - Inode const& inode() const { return *m_inode; } - Inode& inode() { return *m_inode; } - - virtual bool can_read(OpenFileDescription const&, u64) const override { return true; } - virtual bool can_write(OpenFileDescription const&, u64) const override { return true; } - - virtual ErrorOr read(OpenFileDescription&, u64, UserOrKernelBuffer&, size_t) override; - virtual ErrorOr write(OpenFileDescription&, u64, UserOrKernelBuffer const&, size_t) override; - virtual ErrorOr ioctl(OpenFileDescription&, unsigned request, Userspace arg) override; - virtual ErrorOr> vmobject_for_mmap(Process&, Memory::VirtualRange const&, u64& offset, bool shared) override; - virtual ErrorOr stat() const override { return inode().metadata().stat(); } - - virtual ErrorOr> pseudo_path(OpenFileDescription const&) const override; - - virtual ErrorOr truncate(u64) override; - virtual ErrorOr sync() override; - virtual ErrorOr chown(Credentials const&, OpenFileDescription&, UserID, GroupID) override; - virtual ErrorOr chmod(Credentials const&, OpenFileDescription&, mode_t) override; - - virtual StringView class_name() const override { return "InodeFile"sv; } - - virtual bool is_seekable() const override { return true; } - virtual bool is_inode() const override { return true; } - -private: - virtual bool is_regular_file() const override; - - explicit InodeFile(NonnullRefPtr); - NonnullRefPtr const m_inode; -}; - -} diff --git a/Kernel/FileSystem/InodeIdentifier.h b/Kernel/FileSystem/InodeIdentifier.h deleted file mode 100644 index 5edcf1e1dc4..00000000000 --- a/Kernel/FileSystem/InodeIdentifier.h +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include - -namespace Kernel { - -class FileSystem; -struct InodeMetadata; - -AK_TYPEDEF_DISTINCT_ORDERED_ID(u32, FileSystemID); -AK_TYPEDEF_DISTINCT_ORDERED_ID(u64, InodeIndex); - -class InodeIdentifier { -public: - InodeIdentifier() = default; - InodeIdentifier(FileSystemID fsid, InodeIndex inode) - : m_fsid(fsid) - , m_index(inode) - { - } - - bool is_valid() const { return m_fsid != 0 && m_index != 0; } - - FileSystemID fsid() const { return m_fsid; } - InodeIndex index() const { return m_index; } - - bool operator==(InodeIdentifier const& other) const - { - return m_fsid == other.m_fsid && m_index == other.m_index; - } - - bool operator!=(InodeIdentifier const& other) const - { - return m_fsid != other.m_fsid || m_index != other.m_index; - } - -private: - FileSystemID m_fsid { 0 }; - InodeIndex m_index { 0 }; -}; - -} - -template<> -struct AK::Formatter : AK::Formatter { - ErrorOr format(FormatBuilder& builder, Kernel::InodeIdentifier value) - { - return AK::Formatter::format(builder, "{}:{}"sv, value.fsid(), value.index()); - } -}; diff --git a/Kernel/FileSystem/InodeMetadata.cpp b/Kernel/FileSystem/InodeMetadata.cpp deleted file mode 100644 index 6cbfb157fee..00000000000 --- a/Kernel/FileSystem/InodeMetadata.cpp +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright (c) 2022, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include - -namespace Kernel { - -bool InodeMetadata::may_read(Credentials const& credentials, UseEffectiveIDs use_effective_ids) const -{ - bool eids = use_effective_ids == UseEffectiveIDs::Yes; - return may_read(eids ? credentials.euid() : credentials.uid(), eids ? credentials.egid() : credentials.gid(), credentials.extra_gids()); -} - -bool InodeMetadata::may_write(Credentials const& credentials, UseEffectiveIDs use_effective_ids) const -{ - bool eids = use_effective_ids == UseEffectiveIDs::Yes; - return may_write(eids ? credentials.euid() : credentials.uid(), eids ? credentials.egid() : credentials.gid(), credentials.extra_gids()); -} - -bool InodeMetadata::may_execute(Credentials const& credentials, UseEffectiveIDs use_effective_ids) const -{ - bool eids = use_effective_ids == UseEffectiveIDs::Yes; - return may_execute(eids ? credentials.euid() : credentials.uid(), eids ? credentials.egid() : credentials.gid(), credentials.extra_gids()); -} - -} diff --git a/Kernel/FileSystem/InodeMetadata.h b/Kernel/FileSystem/InodeMetadata.h deleted file mode 100644 index ef3ec271b10..00000000000 --- a/Kernel/FileSystem/InodeMetadata.h +++ /dev/null @@ -1,133 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include -#include -#include - -namespace Kernel { - -class Process; - -constexpr u64 encoded_device(MajorNumber major, MinorNumber minor) -{ - return (minor.value() & 0xff) | (major.value() << 8) | ((minor.value() & ~0xff) << 12); -} -static inline MajorNumber major_from_encoded_device(dev_t dev) { return (dev & 0xfff00u) >> 8u; } -static inline MinorNumber minor_from_encoded_device(dev_t dev) { return (dev & 0xffu) | ((dev >> 12u) & 0xfff00u); } - -inline bool is_directory(mode_t mode) { return (mode & S_IFMT) == S_IFDIR; } -inline bool is_character_device(mode_t mode) { return (mode & S_IFMT) == S_IFCHR; } -inline bool is_block_device(mode_t mode) { return (mode & S_IFMT) == S_IFBLK; } -inline bool is_regular_file(mode_t mode) { return (mode & S_IFMT) == S_IFREG; } -inline bool is_fifo(mode_t mode) { return (mode & S_IFMT) == S_IFIFO; } -inline bool is_symlink(mode_t mode) { return (mode & S_IFMT) == S_IFLNK; } -inline bool is_socket(mode_t mode) { return (mode & S_IFMT) == S_IFSOCK; } -inline bool is_sticky(mode_t mode) { return (mode & S_ISVTX) == S_ISVTX; } -inline bool is_setuid(mode_t mode) { return (mode & S_ISUID) == S_ISUID; } -inline bool is_setgid(mode_t mode) { return (mode & S_ISGID) == S_ISGID; } - -enum class UseEffectiveIDs { - Yes, - No -}; - -struct InodeMetadata { - bool is_valid() const { return inode.is_valid(); } - - bool may_read(Credentials const&, UseEffectiveIDs = UseEffectiveIDs::Yes) const; - bool may_write(Credentials const&, UseEffectiveIDs = UseEffectiveIDs::Yes) const; - bool may_execute(Credentials const&, UseEffectiveIDs = UseEffectiveIDs::Yes) const; - - bool may_read(UserID u, GroupID g, ReadonlySpan eg) const - { - if (u == 0) - return true; - if (uid == u) - return (mode & S_IRUSR) == S_IRUSR; - if (gid == g || eg.contains_slow(gid)) - return (mode & S_IRGRP) == S_IRGRP; - return (mode & S_IROTH) == S_IROTH; - } - - bool may_write(UserID u, GroupID g, ReadonlySpan eg) const - { - if (u == 0) - return true; - if (uid == u) - return (mode & S_IWUSR) == S_IWUSR; - if (gid == g || eg.contains_slow(gid)) - return (mode & S_IWGRP) == S_IWGRP; - return (mode & S_IWOTH) == S_IWOTH; - } - - bool may_execute(UserID u, GroupID g, ReadonlySpan eg) const - { - if (u == 0) - return true; - if (uid == u) - return (mode & S_IXUSR) == S_IXUSR; - if (gid == g || eg.contains_slow(gid)) - return (mode & S_IXGRP) == S_IXGRP; - return (mode & S_IXOTH) == S_IXOTH; - } - - bool is_directory() const { return Kernel::is_directory(mode); } - bool is_character_device() const { return Kernel::is_character_device(mode); } - bool is_block_device() const { return Kernel::is_block_device(mode); } - bool is_device() const { return is_character_device() || is_block_device(); } - bool is_regular_file() const { return Kernel::is_regular_file(mode); } - bool is_fifo() const { return Kernel::is_fifo(mode); } - bool is_symlink() const { return Kernel::is_symlink(mode); } - bool is_socket() const { return Kernel::is_socket(mode); } - bool is_sticky() const { return Kernel::is_sticky(mode); } - bool is_setuid() const { return Kernel::is_setuid(mode); } - bool is_setgid() const { return Kernel::is_setgid(mode); } - - ErrorOr stat() const - { - if (!is_valid()) - return EIO; - struct stat buffer = {}; - buffer.st_rdev = encoded_device(major_device, minor_device); - buffer.st_ino = inode.index().value(); - buffer.st_mode = mode; - buffer.st_nlink = link_count; - buffer.st_uid = uid.value(); - buffer.st_gid = gid.value(); - buffer.st_dev = inode.fsid().value(); - buffer.st_size = size; - buffer.st_blksize = block_size; - buffer.st_blocks = block_count; - buffer.st_atim = atime.to_timespec(); - buffer.st_mtim = mtime.to_timespec(); - buffer.st_ctim = ctime.to_timespec(); - return buffer; - } - - InodeIdentifier inode; - off_t size { 0 }; - mode_t mode { 0 }; - UserID uid { 0 }; - GroupID gid { 0 }; - nlink_t link_count { 0 }; - UnixDateTime atime {}; - UnixDateTime ctime {}; - UnixDateTime mtime {}; - UnixDateTime dtime {}; - blkcnt_t block_count { 0 }; - blksize_t block_size { 0 }; - MajorNumber major_device { 0 }; - MinorNumber minor_device { 0 }; -}; - -} diff --git a/Kernel/FileSystem/InodeWatcher.cpp b/Kernel/FileSystem/InodeWatcher.cpp deleted file mode 100644 index 4929c2abee2..00000000000 --- a/Kernel/FileSystem/InodeWatcher.cpp +++ /dev/null @@ -1,178 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * Copyright (c) 2021, sin-ack - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include - -namespace Kernel { - -ErrorOr> InodeWatcher::try_create() -{ - return adopt_nonnull_ref_or_enomem(new (nothrow) InodeWatcher); -} - -InodeWatcher::~InodeWatcher() -{ - (void)close(); -} - -bool InodeWatcher::can_read(OpenFileDescription const&, u64) const -{ - return m_queue.with([](auto& queue) { return !queue.is_empty(); }); -} - -ErrorOr InodeWatcher::read(OpenFileDescription&, u64, UserOrKernelBuffer& buffer, size_t buffer_size) -{ - auto event = TRY(m_queue.with([](auto& queue) -> ErrorOr { - if (queue.is_empty()) { - // can_read will catch the blocking case. - return EAGAIN; - } - - return queue.dequeue(); - })); - - size_t bytes_to_write = sizeof(InodeWatcherEvent); - if (event.path) - bytes_to_write += event.path->length() + 1; - - if (buffer_size < bytes_to_write) - return EINVAL; - - auto result = buffer.write_buffered(bytes_to_write, [&](Bytes bytes) { - size_t offset = 0; - - memcpy(bytes.offset(offset), &event.wd, sizeof(InodeWatcherEvent::watch_descriptor)); - offset += sizeof(InodeWatcherEvent::watch_descriptor); - memcpy(bytes.offset(offset), &event.type, sizeof(InodeWatcherEvent::type)); - offset += sizeof(InodeWatcherEvent::type); - - if (event.path) { - size_t name_length = event.path->length() + 1; - memcpy(bytes.offset(offset), &name_length, sizeof(InodeWatcherEvent::name_length)); - offset += sizeof(InodeWatcherEvent::name_length); - memcpy(bytes.offset(offset), event.path->characters(), name_length); - } else { - memset(bytes.offset(offset), 0, sizeof(InodeWatcherEvent::name_length)); - } - - return bytes.size(); - }); - evaluate_block_conditions(); - return result; -} - -ErrorOr InodeWatcher::close() -{ - m_watch_maps.with([this](auto& watch_maps) { - for (auto& entry : watch_maps.wd_to_watches) { - auto& inode = const_cast(entry.value->inode); - inode.unregister_watcher({}, *this); - } - - watch_maps.inode_to_watches.clear(); - watch_maps.wd_to_watches.clear(); - }); - return {}; -} - -ErrorOr> InodeWatcher::pseudo_path(OpenFileDescription const&) const -{ - return m_watch_maps.with([](auto& watch_maps) -> ErrorOr> { - return KString::formatted("InodeWatcher:({})", watch_maps.wd_to_watches.size()); - }); -} - -void InodeWatcher::notify_inode_event(Badge, InodeIdentifier inode_id, InodeWatcherEvent::Type event_type, StringView name) -{ - m_watch_maps.with([this, inode_id, event_type, name](auto& watch_maps) { - auto it = watch_maps.inode_to_watches.find(inode_id); - if (it == watch_maps.inode_to_watches.end()) - return; - - auto& watcher = *it->value; - if (!(watcher.event_mask & static_cast(event_type))) - return; - - m_queue.with([watcher, event_type, name](auto& queue) { - OwnPtr path; - if (!name.is_null()) - path = KString::try_create(name).release_value_but_fixme_should_propagate_errors(); - queue.enqueue({ watcher.wd, event_type, move(path) }); - }); - }); - - evaluate_block_conditions(); -} - -ErrorOr InodeWatcher::register_inode(Inode& inode, unsigned event_mask) -{ - return m_watch_maps.with([this, &inode, event_mask](auto& watch_maps) -> ErrorOr { - if (watch_maps.inode_to_watches.find(inode.identifier()) != watch_maps.inode_to_watches.end()) - return EEXIST; - - int wd = -1; - do { - wd = m_wd_counter.value(); - - m_wd_counter++; - if (m_wd_counter.has_overflow()) - m_wd_counter = 1; - } while (watch_maps.wd_to_watches.find(wd) != watch_maps.wd_to_watches.end()); - - auto description = TRY(WatchDescription::create(wd, inode, event_mask)); - - TRY(watch_maps.inode_to_watches.try_set(inode.identifier(), description.ptr())); - auto set_result = watch_maps.wd_to_watches.try_set(wd, move(description)); - if (set_result.is_error()) { - watch_maps.inode_to_watches.remove(inode.identifier()); - return set_result.release_error(); - } - - auto register_result = inode.register_watcher({}, *this); - if (register_result.is_error()) { - watch_maps.inode_to_watches.remove(inode.identifier()); - watch_maps.wd_to_watches.remove(wd); - return register_result.release_error(); - } - - return wd; - }); -} - -ErrorOr InodeWatcher::unregister_by_wd(int wd) -{ - TRY(m_watch_maps.with([this, wd](auto& watch_maps) -> ErrorOr { - auto it = watch_maps.wd_to_watches.find(wd); - if (it == watch_maps.wd_to_watches.end()) - return ENOENT; - - auto& inode = it->value->inode; - inode.unregister_watcher({}, *this); - - watch_maps.inode_to_watches.remove(inode.identifier()); - watch_maps.wd_to_watches.remove(it); - return {}; - })); - return {}; -} - -void InodeWatcher::unregister_by_inode(Badge, InodeIdentifier identifier) -{ - m_watch_maps.with([identifier](auto& watch_maps) { - auto it = watch_maps.inode_to_watches.find(identifier); - if (it == watch_maps.inode_to_watches.end()) - return; - - // NOTE: no need to call unregister_watcher here, the Inode calls us. - watch_maps.inode_to_watches.remove(identifier); - watch_maps.wd_to_watches.remove(it->value->wd); - }); -} - -} diff --git a/Kernel/FileSystem/InodeWatcher.h b/Kernel/FileSystem/InodeWatcher.h deleted file mode 100644 index cded67fb70e..00000000000 --- a/Kernel/FileSystem/InodeWatcher.h +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * Copyright (c) 2021, sin-ack - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace Kernel { - -// A specific description of a watch. -struct WatchDescription { - int wd; - Inode& inode; - unsigned event_mask; - - static ErrorOr> create(int wd, Inode& inode, unsigned event_mask) - { - return adopt_nonnull_own_or_enomem(new (nothrow) WatchDescription(wd, inode, event_mask)); - } - -private: - WatchDescription(int wd, Inode& inode, unsigned event_mask) - : wd(wd) - , inode(inode) - , event_mask(event_mask) - { - } -}; - -class InodeWatcher final : public File { -public: - static ErrorOr> try_create(); - virtual ~InodeWatcher() override; - - virtual bool can_read(OpenFileDescription const&, u64) const override; - virtual ErrorOr read(OpenFileDescription&, u64, UserOrKernelBuffer&, size_t) override; - // Can't write to an inode watcher. - virtual bool can_write(OpenFileDescription const&, u64) const override { return true; } - virtual ErrorOr write(OpenFileDescription&, u64, UserOrKernelBuffer const&, size_t) override { return EIO; } - virtual ErrorOr close() override; - - virtual ErrorOr> pseudo_path(OpenFileDescription const&) const override; - virtual StringView class_name() const override { return "InodeWatcher"sv; } - virtual bool is_inode_watcher() const override { return true; } - - void notify_inode_event(Badge, InodeIdentifier, InodeWatcherEvent::Type, StringView name = {}); - - ErrorOr register_inode(Inode&, unsigned event_mask); - ErrorOr unregister_by_wd(int); - void unregister_by_inode(Badge, InodeIdentifier); - -private: - explicit InodeWatcher() { } - - struct Event { - int wd { 0 }; - InodeWatcherEvent::Type type { InodeWatcherEvent::Type::Invalid }; - OwnPtr path; - }; - SpinlockProtected, LockRank::None> m_queue; - Checked m_wd_counter { 1 }; - - // NOTE: These two hashmaps provide two different ways of reaching the same - // watch description, so they will overlap. - struct WatchMaps { - HashMap> wd_to_watches; - HashMap inode_to_watches; - }; - - mutable SpinlockProtected m_watch_maps; -}; - -} diff --git a/Kernel/FileSystem/Mount.cpp b/Kernel/FileSystem/Mount.cpp deleted file mode 100644 index 815807ecb4f..00000000000 --- a/Kernel/FileSystem/Mount.cpp +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright (c) 2018-2023, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include - -namespace Kernel { - -Mount::Mount(NonnullRefPtr guest_fs, RefPtr host_custody, int flags) - : m_guest_fs(move(guest_fs)) - , m_guest(m_guest_fs->root_inode()) - , m_host_custody(move(host_custody)) - , m_flags(flags) -{ -} - -Mount::Mount(NonnullRefPtr source, NonnullRefPtr host_custody, int flags) - : m_guest_fs(source->fs()) - , m_guest(move(source)) - , m_host_custody(move(host_custody)) - , m_flags(flags) -{ -} - -ErrorOr> Mount::absolute_path() const -{ - if (!m_host_custody) - return KString::try_create("/"sv); - return m_host_custody->try_serialize_absolute_path(); -} - -RefPtr Mount::host() -{ - if (!m_host_custody) - return nullptr; - return m_host_custody->inode(); -} - -RefPtr Mount::host() const -{ - if (!m_host_custody) - return nullptr; - return m_host_custody->inode(); -} - -RefPtr Mount::host_custody() const -{ - return m_host_custody; -} - -RefPtr Mount::host_custody() -{ - return m_host_custody; -} - -} diff --git a/Kernel/FileSystem/Mount.h b/Kernel/FileSystem/Mount.h deleted file mode 100644 index 9e6525fffb2..00000000000 --- a/Kernel/FileSystem/Mount.h +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright (c) 2018-2023, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include -#include - -namespace Kernel { - -class VirtualFileSystem; -class Mount { - AK_MAKE_NONCOPYABLE(Mount); - AK_MAKE_NONMOVABLE(Mount); - friend class VirtualFileSystem; - -public: - Mount(NonnullRefPtr, RefPtr host_custody, int flags); - Mount(NonnullRefPtr source, NonnullRefPtr host_custody, int flags); - - RefPtr host() const; - RefPtr host(); - - RefPtr host_custody() const; - RefPtr host_custody(); - - Inode const& guest() const { return *m_guest; } - Inode& guest() { return *m_guest; } - - FileSystem const& guest_fs() const { return *m_guest_fs; } - FileSystem& guest_fs() { return *m_guest_fs; } - - ErrorOr> absolute_path() const; - - int flags() const { return m_flags; } - void set_flags(int flags) { m_flags = flags; } - -private: - NonnullRefPtr const m_guest_fs; - NonnullRefPtr const m_guest; - RefPtr const m_host_custody; - int m_flags { 0 }; - - IntrusiveListNode m_vfs_list_node; -}; - -} diff --git a/Kernel/FileSystem/MountFile.cpp b/Kernel/FileSystem/MountFile.cpp deleted file mode 100644 index 4d9a1df3f1a..00000000000 --- a/Kernel/FileSystem/MountFile.cpp +++ /dev/null @@ -1,125 +0,0 @@ -/* - * Copyright (c) 2022-2023, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace Kernel { - -ErrorOr> MountFile::create(FileSystemInitializer const& file_system_initializer, int flags) -{ - // NOTE: We should not open a MountFile if someone wants to either remount or bindmount. - // There's a check for this in the fsopen syscall entry handler, but here we just assert - // to ensure this never happens. - VERIFY(!(flags & MS_BIND)); - VERIFY(!(flags & MS_REMOUNT)); - auto mount_specific_data_buffer = TRY(KBuffer::try_create_with_size("Mount Specific Data"sv, PAGE_SIZE, Memory::Region::Access::ReadWrite, AllocationStrategy::AllocateNow)); - return TRY(adopt_nonnull_lock_ref_or_enomem(new (nothrow) MountFile(file_system_initializer, flags, move(mount_specific_data_buffer)))); -} - -MountFile::MountFile(FileSystemInitializer const& file_system_initializer, int flags, NonnullOwnPtr mount_specific_data) - : m_flags(flags) - , m_file_system_initializer(file_system_initializer) -{ - m_mount_specific_data.with_exclusive([&](auto& our_mount_specific_data) { - our_mount_specific_data = move(mount_specific_data); - memset(our_mount_specific_data->data(), 0, our_mount_specific_data->size()); - }); -} - -MountFile::~MountFile() = default; - -ErrorOr MountFile::ioctl(OpenFileDescription&, unsigned request, Userspace arg) -{ - return m_mount_specific_data.with_exclusive([&](auto& our_mount_specific_data) -> ErrorOr { - switch (request) { - case MOUNT_IOCTL_SET_MOUNT_SPECIFIC_FLAG: { - auto user_mount_specific_data = static_ptr_cast(arg); - auto mount_specific_data = TRY(copy_typed_from_user(user_mount_specific_data)); - if ((mount_specific_data.value_type == MountSpecificFlag::ValueType::SignedInteger || mount_specific_data.value_type == MountSpecificFlag::ValueType::UnsignedInteger) && mount_specific_data.value_length != 8) - return EDOM; - - Syscall::StringArgument user_key_string { reinterpret_cast(mount_specific_data.key_string_addr), static_cast(mount_specific_data.key_string_length) }; - auto key_string = TRY(Process::get_syscall_name_string_fixed_buffer(user_key_string)); - - if (mount_specific_data.value_type != MountSpecificFlag::ValueType::Boolean && mount_specific_data.value_length == 0) - return EINVAL; - if (mount_specific_data.value_type != MountSpecificFlag::ValueType::Boolean && mount_specific_data.value_addr == nullptr) - return EFAULT; - - // NOTE: We put these limits in place because we assume that don't need to handle huge - // amounts of bytes when trying to handle a mount fs-specific flag. - // Anything larger than these constants (which could be changed if needed) is deemed to - // potentially cause OOM condition, and cannot represent any reasonable and "honest" data - // from userspace. - if (mount_specific_data.value_type != MountSpecificFlag::ValueType::ASCIIString && mount_specific_data.value_length > MOUNT_SPECIFIC_FLAG_NON_ASCII_STRING_TYPE_MAX_LENGTH) - return E2BIG; - if (mount_specific_data.value_type == MountSpecificFlag::ValueType::ASCIIString && mount_specific_data.value_length > MOUNT_SPECIFIC_FLAG_ASCII_STRING_TYPE_MAX_LENGTH) - return E2BIG; - - // NOTE: We enforce that the passed argument will be either i64 or u64, so it will always be - // exactly 8 bytes. We do that to simplify handling of integers as well as to ensure ABI correctness - // in all possible cases. - switch (mount_specific_data.value_type) { - // NOTE: This is actually considered as simply boolean flag. - case MountSpecificFlag::ValueType::Boolean: { - VERIFY(m_file_system_initializer.handle_mount_boolean_flag); - Userspace user_value_addr(reinterpret_cast(mount_specific_data.value_addr)); - auto value_integer = TRY(copy_typed_from_user(user_value_addr)); - if (value_integer != 0 && value_integer != 1) - return EDOM; - bool value = (value_integer == 1) ? true : false; - TRY(m_file_system_initializer.handle_mount_boolean_flag(our_mount_specific_data->bytes(), key_string.representable_view(), value)); - return {}; - } - case MountSpecificFlag::ValueType::UnsignedInteger: { - VERIFY(m_file_system_initializer.handle_mount_unsigned_integer_flag); - Userspace user_value_addr(reinterpret_cast(mount_specific_data.value_addr)); - auto value_integer = TRY(copy_typed_from_user(user_value_addr)); - TRY(m_file_system_initializer.handle_mount_unsigned_integer_flag(our_mount_specific_data->bytes(), key_string.representable_view(), value_integer)); - return {}; - } - case MountSpecificFlag::ValueType::SignedInteger: { - VERIFY(m_file_system_initializer.handle_mount_signed_integer_flag); - Userspace user_value_addr(reinterpret_cast(mount_specific_data.value_addr)); - auto value_integer = TRY(copy_typed_from_user(user_value_addr)); - TRY(m_file_system_initializer.handle_mount_signed_integer_flag(our_mount_specific_data->bytes(), key_string.representable_view(), value_integer)); - return {}; - } - case MountSpecificFlag::ValueType::ASCIIString: { - VERIFY(m_file_system_initializer.handle_mount_ascii_string_flag); - auto value_string = TRY(try_copy_kstring_from_user(reinterpret_cast(mount_specific_data.value_addr), static_cast(mount_specific_data.value_length))); - TRY(m_file_system_initializer.handle_mount_ascii_string_flag(our_mount_specific_data->bytes(), key_string.representable_view(), value_string->view())); - return {}; - } - default: - return EINVAL; - } - } - default: - return EINVAL; - } - }); -} - -ErrorOr> MountFile::pseudo_path(OpenFileDescription const&) const -{ - return KString::try_create(":mount-file:"sv); -} - -} diff --git a/Kernel/FileSystem/MountFile.h b/Kernel/FileSystem/MountFile.h deleted file mode 100644 index bc800b80bb2..00000000000 --- a/Kernel/FileSystem/MountFile.h +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright (c) 2022, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include - -namespace Kernel { - -class MountFile final : public File { -public: - static ErrorOr> create(FileSystemInitializer const&, int flags); - virtual ~MountFile() override; - - virtual bool can_read(OpenFileDescription const&, u64) const override { return true; } - virtual bool can_write(OpenFileDescription const&, u64) const override { return true; } - virtual ErrorOr read(OpenFileDescription&, u64, UserOrKernelBuffer&, size_t) override { return ENOTSUP; } - virtual ErrorOr write(OpenFileDescription&, u64, UserOrKernelBuffer const&, size_t) override { return ENOTSUP; } - virtual ErrorOr ioctl(OpenFileDescription&, unsigned request, Userspace arg) override; - virtual ErrorOr> pseudo_path(OpenFileDescription const&) const override; - virtual StringView class_name() const override { return "MountFile"sv; } - - int mount_flags() const { return m_flags; } - - MutexProtected>& mount_file_system_specific_data() { return m_mount_specific_data; } - FileSystemInitializer const& file_system_initializer() const { return m_file_system_initializer; } - -private: - virtual bool is_mount_file() const override { return true; } - - MountFile(FileSystemInitializer const&, int flags, NonnullOwnPtr); - - int const m_flags; - FileSystemInitializer const& m_file_system_initializer; - MutexProtected> m_mount_specific_data; -}; - -} diff --git a/Kernel/FileSystem/OpenFileDescription.cpp b/Kernel/FileSystem/OpenFileDescription.cpp deleted file mode 100644 index 603ab0fdad2..00000000000 --- a/Kernel/FileSystem/OpenFileDescription.cpp +++ /dev/null @@ -1,557 +0,0 @@ -/* - * Copyright (c) 2018-2022, Andreas Kling - * Copyright (c) 2021, sin-ack - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace Kernel { - -ErrorOr> OpenFileDescription::try_create(Custody& custody) -{ - auto inode_file = TRY(InodeFile::create(custody.inode())); - auto description = TRY(adopt_nonnull_ref_or_enomem(new (nothrow) OpenFileDescription(move(inode_file)))); - - description->m_state.with([&](auto& state) { state.custody = custody; }); - TRY(description->attach()); - return description; -} - -ErrorOr> OpenFileDescription::try_create(File& file) -{ - auto description = TRY(adopt_nonnull_ref_or_enomem(new (nothrow) OpenFileDescription(file))); - TRY(description->attach()); - return description; -} - -OpenFileDescription::OpenFileDescription(File& file) - : m_file(file) -{ - if (file.is_inode()) - m_inode = static_cast(file).inode(); - - auto metadata = this->metadata(); - m_state.with([&](auto& state) { state.is_directory = metadata.is_directory(); }); -} - -OpenFileDescription::~OpenFileDescription() -{ - m_file->detach(*this); - // FIXME: Should this error path be observed somehow? - (void)m_file->close(); - if (m_inode) - m_inode->detach(*this); - - if (m_inode) - m_inode->remove_flocks_for_description(*this); -} - -ErrorOr OpenFileDescription::attach() -{ - if (m_inode) - TRY(m_inode->attach(*this)); - return m_file->attach(*this); -} - -void OpenFileDescription::set_original_custody(Badge, Custody& custody) -{ - m_state.with([&](auto& state) { state.custody = custody; }); -} - -Thread::FileBlocker::BlockFlags OpenFileDescription::should_unblock(Thread::FileBlocker::BlockFlags block_flags) const -{ - using BlockFlags = Thread::FileBlocker::BlockFlags; - BlockFlags unblock_flags = BlockFlags::None; - if (has_flag(block_flags, BlockFlags::Read) && can_read()) - unblock_flags |= BlockFlags::Read; - if (has_flag(block_flags, BlockFlags::Write) && can_write()) - unblock_flags |= BlockFlags::Write; - // TODO: Implement Thread::FileBlocker::BlockFlags::Exception - - if (has_any_flag(block_flags, BlockFlags::SocketFlags)) { - auto const* sock = socket(); - VERIFY(sock); - if (has_flag(block_flags, BlockFlags::Accept) && sock->can_accept()) - unblock_flags |= BlockFlags::Accept; - if (has_flag(block_flags, BlockFlags::Connect) && sock->setup_state() == Socket::SetupState::Completed) - unblock_flags |= BlockFlags::Connect; - } - return unblock_flags; -} - -ErrorOr OpenFileDescription::stat() -{ - // FIXME: This is due to the Device class not overriding File::stat(). - if (m_inode) - return m_inode->metadata().stat(); - return m_file->stat(); -} - -ErrorOr OpenFileDescription::seek(off_t offset, int whence) -{ - if (!m_file->is_seekable()) - return ESPIPE; - - auto metadata = this->metadata(); - - auto new_offset = TRY(m_state.with([&](auto& state) -> ErrorOr { - off_t new_offset; - switch (whence) { - case SEEK_SET: - new_offset = offset; - break; - case SEEK_CUR: - if (Checked::addition_would_overflow(state.current_offset, offset)) - return EOVERFLOW; - new_offset = state.current_offset + offset; - break; - case SEEK_END: - if (!metadata.is_valid()) - return EIO; - if (Checked::addition_would_overflow(metadata.size, offset)) - return EOVERFLOW; - new_offset = metadata.size + offset; - break; - default: - return EINVAL; - } - if (new_offset < 0) - return EINVAL; - state.current_offset = new_offset; - return new_offset; - })); - - // FIXME: Return EINVAL if attempting to seek past the end of a seekable device. - - m_file->did_seek(*this, new_offset); - if (m_inode) - m_inode->did_seek(*this, new_offset); - evaluate_block_conditions(); - return new_offset; -} - -ErrorOr OpenFileDescription::read(UserOrKernelBuffer& buffer, u64 offset, size_t count) -{ - if (Checked::addition_would_overflow(offset, count)) - return EOVERFLOW; - return m_file->read(*this, offset, buffer, count); -} - -ErrorOr OpenFileDescription::write(u64 offset, UserOrKernelBuffer const& data, size_t data_size) -{ - if (Checked::addition_would_overflow(offset, data_size)) - return EOVERFLOW; - return m_file->write(*this, offset, data, data_size); -} - -ErrorOr OpenFileDescription::read(UserOrKernelBuffer& buffer, size_t count) -{ - auto offset = TRY(m_state.with([&](auto& state) -> ErrorOr { - if (Checked::addition_would_overflow(state.current_offset, count)) - return EOVERFLOW; - return state.current_offset; - })); - auto nread = TRY(m_file->read(*this, offset, buffer, count)); - if (m_file->is_seekable()) - m_state.with([&](auto& state) { state.current_offset = offset + nread; }); - evaluate_block_conditions(); - return nread; -} - -ErrorOr OpenFileDescription::write(UserOrKernelBuffer const& data, size_t size) -{ - auto offset = TRY(m_state.with([&](auto& state) -> ErrorOr { - if (Checked::addition_would_overflow(state.current_offset, size)) - return EOVERFLOW; - return state.current_offset; - })); - auto nwritten = TRY(m_file->write(*this, offset, data, size)); - - if (m_file->is_seekable()) - m_state.with([&](auto& state) { state.current_offset = offset + nwritten; }); - - evaluate_block_conditions(); - return nwritten; -} - -bool OpenFileDescription::can_write() const -{ - return m_file->can_write(*this, offset()); -} - -bool OpenFileDescription::can_read() const -{ - return m_file->can_read(*this, offset()); -} - -ErrorOr OpenFileDescription::get_dir_entries(UserOrKernelBuffer& output_buffer, size_t size) -{ - if (!is_directory()) - return ENOTDIR; - - auto metadata = this->metadata(); - if (!metadata.is_valid()) - return EIO; - - size_t remaining = size; - u8 stack_buffer[PAGE_SIZE]; - Bytes temp_buffer(stack_buffer, sizeof(stack_buffer)); - FixedMemoryStream stream { temp_buffer }; - - auto flush_stream_to_output_buffer = [&stream, &remaining, &temp_buffer, &output_buffer]() -> ErrorOr { - auto buffered_size = TRY(stream.tell()); - - if (buffered_size == 0) - return {}; - - if (remaining < buffered_size) - return Error::from_errno(EINVAL); - - TRY(output_buffer.write(temp_buffer.trim(buffered_size))); - output_buffer = output_buffer.offset(buffered_size); - remaining -= buffered_size; - TRY(stream.seek(0)); - return {}; - }; - - ErrorOr result = VirtualFileSystem::the().traverse_directory_inode(*m_inode, [&flush_stream_to_output_buffer, &stream, this](auto& entry) -> ErrorOr { - // FIXME: Double check the calculation, at least the type for the name length mismatches. - size_t serialized_size = sizeof(ino_t) + sizeof(u8) + sizeof(size_t) + sizeof(char) * entry.name.length(); - if (serialized_size > TRY(stream.size()) - TRY(stream.tell())) - TRY(flush_stream_to_output_buffer()); - - MUST(stream.write_value(entry.inode.index().value())); - MUST(stream.write_value(m_inode->fs().internal_file_type_to_directory_entry_type(entry))); - MUST(stream.write_value(entry.name.length())); - MUST(stream.write_until_depleted(entry.name.bytes())); - return {}; - }); - - if (result.is_error()) { - // We should only return EFAULT when the userspace buffer is too small, - // so that userspace can reliably use it as a signal to increase its - // buffer size. - VERIFY(result.error().code() != EFAULT); - return result.release_error(); - } - - TRY(flush_stream_to_output_buffer()); - - return size - remaining; -} - -bool OpenFileDescription::is_device() const -{ - return m_file->is_device(); -} - -Device const* OpenFileDescription::device() const -{ - if (!is_device()) - return nullptr; - return static_cast(m_file.ptr()); -} - -Device* OpenFileDescription::device() -{ - if (!is_device()) - return nullptr; - return static_cast(m_file.ptr()); -} - -bool OpenFileDescription::is_tty() const -{ - return m_file->is_tty(); -} - -const TTY* OpenFileDescription::tty() const -{ - if (!is_tty()) - return nullptr; - return static_cast(m_file.ptr()); -} - -TTY* OpenFileDescription::tty() -{ - if (!is_tty()) - return nullptr; - return static_cast(m_file.ptr()); -} - -bool OpenFileDescription::is_inode_watcher() const -{ - return m_file->is_inode_watcher(); -} - -InodeWatcher const* OpenFileDescription::inode_watcher() const -{ - if (!is_inode_watcher()) - return nullptr; - return static_cast(m_file.ptr()); -} - -InodeWatcher* OpenFileDescription::inode_watcher() -{ - if (!is_inode_watcher()) - return nullptr; - return static_cast(m_file.ptr()); -} - -bool OpenFileDescription::is_mount_file() const -{ - return m_file->is_mount_file(); -} - -MountFile const* OpenFileDescription::mount_file() const -{ - if (!is_mount_file()) - return nullptr; - return static_cast(m_file.ptr()); -} - -MountFile* OpenFileDescription::mount_file() -{ - if (!is_mount_file()) - return nullptr; - return static_cast(m_file.ptr()); -} - -bool OpenFileDescription::is_master_pty() const -{ - return m_file->is_master_pty(); -} - -MasterPTY const* OpenFileDescription::master_pty() const -{ - if (!is_master_pty()) - return nullptr; - return static_cast(m_file.ptr()); -} - -MasterPTY* OpenFileDescription::master_pty() -{ - if (!is_master_pty()) - return nullptr; - return static_cast(m_file.ptr()); -} - -ErrorOr OpenFileDescription::close() -{ - if (m_file->attach_count() > 0) - return {}; - return m_file->close(); -} - -ErrorOr> OpenFileDescription::original_absolute_path() const -{ - if (auto custody = this->custody()) - return custody->try_serialize_absolute_path(); - return ENOENT; -} - -ErrorOr> OpenFileDescription::pseudo_path() const -{ - if (auto custody = this->custody()) - return custody->try_serialize_absolute_path(); - return m_file->pseudo_path(*this); -} - -InodeMetadata OpenFileDescription::metadata() const -{ - if (m_inode) - return m_inode->metadata(); - return {}; -} - -ErrorOr> OpenFileDescription::vmobject_for_mmap(Process& process, Memory::VirtualRange const& range, u64& offset, bool shared) -{ - return m_file->vmobject_for_mmap(process, range, offset, shared); -} - -ErrorOr OpenFileDescription::truncate(u64 length) -{ - return m_file->truncate(length); -} - -ErrorOr OpenFileDescription::sync() -{ - return m_file->sync(); -} - -bool OpenFileDescription::is_fifo() const -{ - return m_file->is_fifo(); -} - -FIFO* OpenFileDescription::fifo() -{ - if (!is_fifo()) - return nullptr; - return static_cast(m_file.ptr()); -} - -bool OpenFileDescription::is_socket() const -{ - return m_file->is_socket(); -} - -Socket* OpenFileDescription::socket() -{ - if (!is_socket()) - return nullptr; - return static_cast(m_file.ptr()); -} - -Socket const* OpenFileDescription::socket() const -{ - if (!is_socket()) - return nullptr; - return static_cast(m_file.ptr()); -} - -void OpenFileDescription::set_file_flags(u32 flags) -{ - m_state.with([&](auto& state) { - state.is_blocking = !(flags & O_NONBLOCK); - state.should_append = flags & O_APPEND; - state.direct = flags & O_DIRECT; - state.file_flags = flags; - }); -} - -ErrorOr OpenFileDescription::chmod(Credentials const& credentials, mode_t mode) -{ - return m_file->chmod(credentials, *this, mode); -} - -ErrorOr OpenFileDescription::chown(Credentials const& credentials, UserID uid, GroupID gid) -{ - return m_file->chown(credentials, *this, uid, gid); -} - -FileBlockerSet& OpenFileDescription::blocker_set() -{ - return m_file->blocker_set(); -} - -ErrorOr OpenFileDescription::apply_flock(Process const& process, Userspace lock, ShouldBlock should_block) -{ - if (!m_inode) - return EBADF; - - return m_inode->apply_flock(process, *this, lock, should_block); -} - -ErrorOr OpenFileDescription::get_flock(Userspace lock) const -{ - if (!m_inode) - return EBADF; - - return m_inode->get_flock(*this, lock); -} -bool OpenFileDescription::is_readable() const -{ - return m_state.with([](auto& state) { return state.readable; }); -} - -bool OpenFileDescription::is_writable() const -{ - return m_state.with([](auto& state) { return state.writable; }); -} - -void OpenFileDescription::set_readable(bool b) -{ - m_state.with([&](auto& state) { state.readable = b; }); -} - -void OpenFileDescription::set_writable(bool b) -{ - m_state.with([&](auto& state) { state.writable = b; }); -} - -void OpenFileDescription::set_rw_mode(int options) -{ - m_state.with([&](auto& state) { - state.readable = (options & O_RDONLY) == O_RDONLY; - state.writable = (options & O_WRONLY) == O_WRONLY; - }); -} - -bool OpenFileDescription::is_direct() const -{ - return m_state.with([](auto& state) { return state.direct; }); -} - -bool OpenFileDescription::is_directory() const -{ - return m_state.with([](auto& state) { return state.is_directory; }); -} - -bool OpenFileDescription::is_blocking() const -{ - return m_state.with([](auto& state) { return state.is_blocking; }); -} - -void OpenFileDescription::set_blocking(bool b) -{ - m_state.with([&](auto& state) { state.is_blocking = b; }); -} - -bool OpenFileDescription::should_append() const -{ - return m_state.with([](auto& state) { return state.should_append; }); -} - -u32 OpenFileDescription::file_flags() const -{ - return m_state.with([](auto& state) { return state.file_flags; }); -} - -FIFO::Direction OpenFileDescription::fifo_direction() const -{ - return m_state.with([](auto& state) { return state.fifo_direction; }); -} - -void OpenFileDescription::set_fifo_direction(Badge, FIFO::Direction direction) -{ - m_state.with([&](auto& state) { state.fifo_direction = direction; }); -} - -OwnPtr& OpenFileDescription::data() -{ - return m_state.with([](auto& state) -> OwnPtr& { return state.data; }); -} - -off_t OpenFileDescription::offset() const -{ - return m_state.with([](auto& state) { return state.current_offset; }); -} - -RefPtr OpenFileDescription::custody() const -{ - return m_state.with([](auto& state) { return state.custody; }); -} - -RefPtr OpenFileDescription::custody() -{ - return m_state.with([](auto& state) { return state.custody; }); -} - -} diff --git a/Kernel/FileSystem/OpenFileDescription.h b/Kernel/FileSystem/OpenFileDescription.h deleted file mode 100644 index 7f0f7fab996..00000000000 --- a/Kernel/FileSystem/OpenFileDescription.h +++ /dev/null @@ -1,164 +0,0 @@ -/* - * Copyright (c) 2018-2022, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace Kernel { - -class OpenFileDescriptionData { -public: - virtual ~OpenFileDescriptionData() = default; -}; - -class OpenFileDescription final : public AtomicRefCounted { -public: - static ErrorOr> try_create(Custody&); - static ErrorOr> try_create(File&); - ~OpenFileDescription(); - - Thread::FileBlocker::BlockFlags should_unblock(Thread::FileBlocker::BlockFlags) const; - - bool is_readable() const; - void set_readable(bool); - - bool is_writable() const; - void set_writable(bool); - - void set_rw_mode(int options); - - ErrorOr close(); - - ErrorOr seek(off_t, int whence); - ErrorOr read(UserOrKernelBuffer&, size_t); - ErrorOr write(UserOrKernelBuffer const& data, size_t); - ErrorOr stat(); - - // NOTE: These ignore the current offset of this file description. - ErrorOr read(UserOrKernelBuffer&, u64 offset, size_t); - ErrorOr write(u64 offset, UserOrKernelBuffer const&, size_t); - - ErrorOr chmod(Credentials const& credentials, mode_t); - - bool can_read() const; - bool can_write() const; - - ErrorOr get_dir_entries(UserOrKernelBuffer& buffer, size_t); - - ErrorOr> original_absolute_path() const; - ErrorOr> pseudo_path() const; - - bool is_direct() const; - - bool is_directory() const; - - File& file() { return *m_file; } - File const& file() const { return *m_file; } - - bool is_device() const; - Device const* device() const; - Device* device(); - - bool is_tty() const; - const TTY* tty() const; - TTY* tty(); - - bool is_inode_watcher() const; - InodeWatcher const* inode_watcher() const; - InodeWatcher* inode_watcher(); - - bool is_mount_file() const; - MountFile const* mount_file() const; - MountFile* mount_file(); - - bool is_master_pty() const; - MasterPTY const* master_pty() const; - MasterPTY* master_pty(); - - InodeMetadata metadata() const; - Inode* inode() { return m_inode.ptr(); } - Inode const* inode() const { return m_inode.ptr(); } - - RefPtr custody(); - RefPtr custody() const; - - ErrorOr> vmobject_for_mmap(Process&, Memory::VirtualRange const&, u64& offset, bool shared); - - bool is_blocking() const; - void set_blocking(bool b); - - bool should_append() const; - - u32 file_flags() const; - void set_file_flags(u32); - - bool is_socket() const; - Socket* socket(); - Socket const* socket() const; - - bool is_fifo() const; - FIFO* fifo(); - FIFO::Direction fifo_direction() const; - void set_fifo_direction(Badge, FIFO::Direction direction); - - OwnPtr& data(); - - void set_original_inode(Badge, NonnullRefPtr inode) { m_inode = move(inode); } - void set_original_custody(Badge, Custody& custody); - - ErrorOr truncate(u64); - ErrorOr sync(); - - off_t offset() const; - - ErrorOr chown(Credentials const& credentials, UserID, GroupID); - - FileBlockerSet& blocker_set(); - - ErrorOr apply_flock(Process const&, Userspace, ShouldBlock); - ErrorOr get_flock(Userspace) const; - -private: - friend class VirtualFileSystem; - explicit OpenFileDescription(File&); - - ErrorOr attach(); - - void evaluate_block_conditions() - { - blocker_set().unblock_all_blockers_whose_conditions_are_met(); - } - - RefPtr m_inode; - NonnullRefPtr const m_file; - - struct State { - OwnPtr data; - RefPtr custody; - off_t current_offset { 0 }; - u32 file_flags { 0 }; - bool readable : 1 { false }; - bool writable : 1 { false }; - bool is_blocking : 1 { true }; - bool is_directory : 1 { false }; - bool should_append : 1 { false }; - bool direct : 1 { false }; - FIFO::Direction fifo_direction : 2 { FIFO::Direction::Neither }; - }; - - SpinlockProtected m_state {}; -}; -} diff --git a/Kernel/FileSystem/Plan9FS/Definitions.h b/Kernel/FileSystem/Plan9FS/Definitions.h deleted file mode 100644 index e2220db3fdf..00000000000 --- a/Kernel/FileSystem/Plan9FS/Definitions.h +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Copyright (c) 2020, Sergey Bugaev - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include - -namespace Kernel { - -struct Plan9FSQIdentifier { - u8 type; - u32 version; - u64 path; -}; - -} diff --git a/Kernel/FileSystem/Plan9FS/FileSystem.cpp b/Kernel/FileSystem/Plan9FS/FileSystem.cpp deleted file mode 100644 index 6adfa69a2b0..00000000000 --- a/Kernel/FileSystem/Plan9FS/FileSystem.cpp +++ /dev/null @@ -1,363 +0,0 @@ -/* - * Copyright (c) 2020, Sergey Bugaev - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include - -namespace Kernel { - -ErrorOr> Plan9FS::try_create(OpenFileDescription& file_description, ReadonlyBytes) -{ - return TRY(adopt_nonnull_ref_or_enomem(new (nothrow) Plan9FS(file_description))); -} - -Plan9FS::Plan9FS(OpenFileDescription& file_description) - : FileBackedFileSystem(file_description) - , m_completion_blocker(*this) -{ -} - -ErrorOr Plan9FS::prepare_to_clear_last_mount(Inode&) -{ - // FIXME: Do proper cleaning here. - return {}; -} - -Plan9FS::~Plan9FS() -{ - // Make sure to destroy the root inode before the FS gets destroyed. - if (m_root_inode) { - VERIFY(m_root_inode->ref_count() == 1); - m_root_inode = nullptr; - } -} - -bool Plan9FS::is_initialized_while_locked() -{ - VERIFY(m_lock.is_locked()); - return !m_root_inode.is_null(); -} - -ErrorOr Plan9FS::initialize_while_locked() -{ - VERIFY(m_lock.is_locked()); - VERIFY(!is_initialized_while_locked()); - - ensure_thread(); - - Plan9FSMessage version_message { *this, Plan9FSMessage::Type::Tversion }; - version_message << (u32)m_max_message_size << "9P2000.L"sv; - - TRY(post_message_and_wait_for_a_reply(version_message)); - - u32 msize; - StringView remote_protocol_version; - version_message >> msize >> remote_protocol_version; - dbgln("Remote supports msize={} and protocol version {}", msize, remote_protocol_version); - m_remote_protocol_version = parse_protocol_version(remote_protocol_version); - m_max_message_size = min(m_max_message_size, (size_t)msize); - - // TODO: auth - - u32 root_fid = allocate_fid(); - Plan9FSMessage attach_message { *this, Plan9FSMessage::Type::Tattach }; - // FIXME: This needs a user name and an "export" name; but how do we get them? - // Perhaps initialize() should accept a string of FS-specific options... - attach_message << root_fid << (u32)-1 << "sergey"sv - << "/"sv; - if (m_remote_protocol_version >= ProtocolVersion::v9P2000u) - attach_message << (u32)-1; - - TRY(post_message_and_wait_for_a_reply(attach_message)); - m_root_inode = TRY(Plan9FSInode::try_create(*this, root_fid)); - return {}; -} - -Plan9FS::ProtocolVersion Plan9FS::parse_protocol_version(StringView s) const -{ - if (s == "9P2000.L") - return ProtocolVersion::v9P2000L; - if (s == "9P2000.u") - return ProtocolVersion::v9P2000u; - return ProtocolVersion::v9P2000; -} - -Inode& Plan9FS::root_inode() -{ - return *m_root_inode; -} - -Plan9FS::ReceiveCompletion::ReceiveCompletion(u16 tag) - : tag(tag) -{ -} - -Plan9FS::ReceiveCompletion::~ReceiveCompletion() = default; - -bool Plan9FS::Blocker::unblock(u16 tag) -{ - { - SpinlockLocker lock(m_lock); - if (m_did_unblock) - return false; - m_did_unblock = true; - - if (m_completion->tag != tag) - return false; - if (!m_completion->result.is_error()) - m_message = move(*m_completion->message); - } - return unblock(); -} - -bool Plan9FS::Blocker::setup_blocker() -{ - return add_to_blocker_set(m_fs.m_completion_blocker); -} - -void Plan9FS::Blocker::will_unblock_immediately_without_blocking(UnblockImmediatelyReason) -{ - { - SpinlockLocker lock(m_lock); - if (m_did_unblock) - return; - } - - m_fs.m_completion_blocker.try_unblock(*this); -} - -bool Plan9FS::Blocker::is_completed() const -{ - SpinlockLocker lock(m_completion->lock); - return m_completion->completed; -} - -bool Plan9FS::Plan9FSBlockerSet::should_add_blocker(Thread::Blocker& b, void*) -{ - // NOTE: m_lock is held already! - auto& blocker = static_cast(b); - return !blocker.is_completed(); -} - -void Plan9FS::Plan9FSBlockerSet::unblock_completed(u16 tag) -{ - unblock_all_blockers_whose_conditions_are_met([&](Thread::Blocker& b, void*, bool&) { - VERIFY(b.blocker_type() == Thread::Blocker::Type::Plan9FS); - auto& blocker = static_cast(b); - return blocker.unblock(tag); - }); -} - -void Plan9FS::Plan9FSBlockerSet::unblock_all() -{ - unblock_all_blockers_whose_conditions_are_met([&](Thread::Blocker& b, void*, bool&) { - VERIFY(b.blocker_type() == Thread::Blocker::Type::Plan9FS); - auto& blocker = static_cast(b); - return blocker.unblock(); - }); -} - -void Plan9FS::Plan9FSBlockerSet::try_unblock(Plan9FS::Blocker& blocker) -{ - if (m_fs.is_complete(*blocker.completion())) { - SpinlockLocker lock(m_lock); - blocker.unblock(blocker.completion()->tag); - } -} - -bool Plan9FS::is_complete(ReceiveCompletion const& completion) -{ - MutexLocker locker(m_lock); - if (m_completions.contains(completion.tag)) { - // If it's still in the map then it can't be complete - VERIFY(!completion.completed); - return false; - } - - // if it's not in the map anymore, it must be complete. But we MUST - // hold m_lock to be able to check completion.completed! - VERIFY(completion.completed); - return true; -} - -ErrorOr Plan9FS::post_message(Plan9FSMessage& message, LockRefPtr completion) -{ - auto const& buffer = message.build(); - u8 const* data = buffer.data(); - size_t size = buffer.size(); - auto& description = file_description(); - - MutexLocker locker(m_send_lock); - - if (completion) { - // Save the completion record *before* we send the message. This - // ensures that it exists when the thread reads the response - MutexLocker locker(m_lock); - auto tag = completion->tag; - m_completions.set(tag, completion.release_nonnull()); - // TODO: What if there is a collision? Do we need to wait until - // the existing record with the tag completes before queueing - // this one? - } - - while (size > 0) { - if (!description.can_write()) { - auto unblock_flags = Thread::FileBlocker::BlockFlags::None; - if (Thread::current()->block({}, description, unblock_flags).was_interrupted()) - return EINTR; - } - auto data_buffer = UserOrKernelBuffer::for_kernel_buffer(const_cast(data)); - auto nwritten = TRY(description.write(data_buffer, size)); - data += nwritten; - size -= nwritten; - } - - return {}; -} - -ErrorOr Plan9FS::do_read(u8* data, size_t size) -{ - auto& description = file_description(); - while (size > 0) { - if (!description.can_read()) { - auto unblock_flags = Thread::FileBlocker::BlockFlags::None; - if (Thread::current()->block({}, description, unblock_flags).was_interrupted()) - return EINTR; - } - auto data_buffer = UserOrKernelBuffer::for_kernel_buffer(data); - auto nread = TRY(description.read(data_buffer, size)); - if (nread == 0) - return EIO; - data += nread; - size -= nread; - } - return {}; -} - -ErrorOr Plan9FS::read_and_dispatch_one_message() -{ - struct [[gnu::packed]] Header { - u32 size; - u8 type; - u16 tag; - }; - Header header; - TRY(do_read(reinterpret_cast(&header), sizeof(header))); - - auto buffer = TRY(KBuffer::try_create_with_size("Plan9FS: Plan9FSMessage read buffer"sv, header.size, Memory::Region::Access::ReadWrite)); - // Copy the already read header into the buffer. - memcpy(buffer->data(), &header, sizeof(header)); - TRY(do_read(buffer->data() + sizeof(header), header.size - sizeof(header))); - - MutexLocker locker(m_lock); - - auto optional_completion = m_completions.get(header.tag); - if (optional_completion.has_value()) { - auto* completion = optional_completion.value(); - SpinlockLocker lock(completion->lock); - completion->result = {}; - completion->message = adopt_own_if_nonnull(new (nothrow) Plan9FSMessage { move(buffer) }); - completion->completed = true; - - m_completions.remove(header.tag); - m_completion_blocker.unblock_completed(header.tag); - } else { - dbgln("Received a 9p message of type {} with an unexpected tag {}, dropping", header.type, header.tag); - } - - return {}; -} - -ErrorOr Plan9FS::post_message_and_explicitly_ignore_reply(Plan9FSMessage& message) -{ - return post_message(message, {}); -} - -ErrorOr Plan9FS::post_message_and_wait_for_a_reply(Plan9FSMessage& message) -{ - auto request_type = message.type(); - auto tag = message.tag(); - auto completion = adopt_lock_ref(*new ReceiveCompletion(tag)); - TRY(post_message(message, completion)); - if (Thread::current()->block({}, *this, message, completion).was_interrupted()) - return EINTR; - - if (completion->result.is_error()) { - dbgln("Plan9FS: Plan9FSMessage was aborted with error {}", completion->result.error()); - return EIO; - } - - auto reply_type = message.type(); - - if (reply_type == Plan9FSMessage::Type::Rlerror) { - // Contains a numerical Linux errno; hopefully our errno numbers match. - u32 error_code; - message >> error_code; - return Error::from_errno((ErrnoCode)error_code); - } - if (reply_type == Plan9FSMessage::Type::Rerror) { - // Contains an error message. We could attempt to parse it, but for now - // we simply return EIO instead. In 9P200.u, it can also contain a - // numerical errno in an unspecified encoding; we ignore those too. - StringView error_name; - message >> error_name; - dbgln("Plan9FS: Received error name {}", error_name); - return EIO; - } - if ((u8)reply_type != (u8)request_type + 1) { - // Other than those error messages. we only expect the matching reply - // message type. - dbgln("Plan9FS: Received unexpected message type {} in response to {}", (u8)reply_type, (u8)request_type); - return EIO; - } - - return {}; -} - -size_t Plan9FS::adjust_buffer_size(size_t size) const -{ - size_t max_size = m_max_message_size - Plan9FSMessage::max_header_size; - return min(size, max_size); -} - -void Plan9FS::thread_main() -{ - dbgln("Plan9FS: Thread running"); - while (!Process::current().is_dying()) { - auto result = read_and_dispatch_one_message(); - if (result.is_error()) { - // If we fail to read, wake up everyone with an error. - MutexLocker locker(m_lock); - - for (auto& it : m_completions) { - it.value->result = Error::copy(result.error()); - it.value->completed = true; - } - m_completions.clear(); - m_completion_blocker.unblock_all(); - dbgln("Plan9FS: Thread terminating, error reading"); - return; - } - } - dbgln("Plan9FS: Thread terminating"); -} - -void Plan9FS::ensure_thread() -{ - SpinlockLocker lock(m_thread_lock); - if (!m_thread_running.exchange(true, AK::MemoryOrder::memory_order_acq_rel)) { - auto [_, thread] = Process::create_kernel_process("Plan9FS"sv, [&]() { - thread_main(); - m_thread_running.store(false, AK::MemoryOrder::memory_order_release); - Process::current().sys$exit(0); - VERIFY_NOT_REACHED(); - }).release_value_but_fixme_should_propagate_errors(); - m_thread = move(thread); - } -} - -} diff --git a/Kernel/FileSystem/Plan9FS/FileSystem.h b/Kernel/FileSystem/Plan9FS/FileSystem.h deleted file mode 100644 index 99b0d40dfc6..00000000000 --- a/Kernel/FileSystem/Plan9FS/FileSystem.h +++ /dev/null @@ -1,144 +0,0 @@ -/* - * Copyright (c) 2020, Sergey Bugaev - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include -#include - -namespace Kernel { - -class Plan9FSInode; - -class Plan9FS final : public FileBackedFileSystem { - friend class Plan9FSInode; - -public: - virtual ~Plan9FS() override; - static ErrorOr> try_create(OpenFileDescription&, ReadonlyBytes); - - virtual bool supports_watchers() const override { return false; } - - virtual Inode& root_inode() override; - - u16 allocate_tag() { return m_next_tag++; } - u32 allocate_fid() { return m_next_fid++; } - - enum class ProtocolVersion { - v9P2000, - v9P2000u, - v9P2000L - }; - -private: - Plan9FS(OpenFileDescription&); - - virtual ErrorOr prepare_to_clear_last_mount(Inode&) override; - - virtual bool is_initialized_while_locked() override; - virtual ErrorOr initialize_while_locked() override; - - class Blocker; - - class Plan9FSBlockerSet final : public Thread::BlockerSet { - public: - Plan9FSBlockerSet(Plan9FS& fs) - : m_fs(fs) - { - } - - void unblock_completed(u16); - void unblock_all(); - void try_unblock(Blocker&); - - protected: - virtual bool should_add_blocker(Thread::Blocker&, void*) override; - - private: - Plan9FS& m_fs; - mutable Spinlock m_lock {}; - }; - - struct ReceiveCompletion final : public AtomicRefCounted { - mutable Spinlock lock {}; - bool completed { false }; - u16 const tag; - OwnPtr message; - ErrorOr result; - - ReceiveCompletion(u16 tag); - ~ReceiveCompletion(); - }; - - class Blocker final : public Thread::Blocker { - public: - Blocker(Plan9FS& fs, Plan9FSMessage& message, NonnullLockRefPtr completion) - : m_fs(fs) - , m_message(message) - , m_completion(move(completion)) - { - } - virtual bool setup_blocker() override; - virtual StringView state_string() const override { return "Waiting"sv; } - virtual Type blocker_type() const override { return Type::Plan9FS; } - virtual void will_unblock_immediately_without_blocking(UnblockImmediatelyReason) override; - - NonnullLockRefPtr const& completion() const { return m_completion; } - u16 tag() const { return m_completion->tag; } - bool is_completed() const; - - bool unblock() - { - unblock_from_blocker(); - return true; - } - - bool unblock(u16 tag); - - private: - Plan9FS& m_fs; - Plan9FSMessage& m_message; - NonnullLockRefPtr m_completion; - bool m_did_unblock { false }; - }; - friend class Blocker; - - virtual StringView class_name() const override { return "Plan9FS"sv; } - - bool is_complete(ReceiveCompletion const&); - ErrorOr post_message(Plan9FSMessage&, LockRefPtr); - ErrorOr do_read(u8* buffer, size_t); - ErrorOr read_and_dispatch_one_message(); - ErrorOr post_message_and_wait_for_a_reply(Plan9FSMessage&); - ErrorOr post_message_and_explicitly_ignore_reply(Plan9FSMessage&); - - ProtocolVersion parse_protocol_version(StringView) const; - size_t adjust_buffer_size(size_t size) const; - - void thread_main(); - void ensure_thread(); - - RefPtr m_root_inode; - Atomic m_next_tag { (u16)-1 }; - Atomic m_next_fid { 1 }; - - ProtocolVersion m_remote_protocol_version { ProtocolVersion::v9P2000 }; - size_t m_max_message_size { 4 * KiB }; - - Mutex m_send_lock { "Plan9FS send"sv }; - Plan9FSBlockerSet m_completion_blocker; - HashMap> m_completions; - - Spinlock m_thread_lock {}; - RefPtr m_thread; - Atomic m_thread_running { false }; -}; - -} diff --git a/Kernel/FileSystem/Plan9FS/Inode.cpp b/Kernel/FileSystem/Plan9FS/Inode.cpp deleted file mode 100644 index e5df76b9ca1..00000000000 --- a/Kernel/FileSystem/Plan9FS/Inode.cpp +++ /dev/null @@ -1,308 +0,0 @@ -/* - * Copyright (c) 2020, Sergey Bugaev - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include - -namespace Kernel { - -Plan9FSInode::Plan9FSInode(Plan9FS& fs, u32 fid) - : Inode(fs, fid) -{ -} - -ErrorOr> Plan9FSInode::try_create(Plan9FS& fs, u32 fid) -{ - return adopt_nonnull_ref_or_enomem(new (nothrow) Plan9FSInode(fs, fid)); -} - -Plan9FSInode::~Plan9FSInode() -{ - Plan9FSMessage clunk_request { fs(), Plan9FSMessage::Type::Tclunk }; - clunk_request << fid(); - // FIXME: Should we observe this error somehow? - [[maybe_unused]] auto rc = fs().post_message_and_explicitly_ignore_reply(clunk_request); -} - -ErrorOr Plan9FSInode::ensure_open_for_mode(int mode) -{ - bool use_lopen = fs().m_remote_protocol_version >= Plan9FS::ProtocolVersion::v9P2000L; - u32 l_mode = 0; - u8 p9_mode = 0; - - { - MutexLocker locker(m_inode_lock); - - // If it's already open in this mode, we're done. - if ((m_open_mode & mode) == mode) - return {}; - - m_open_mode |= mode; - - if ((m_open_mode & O_RDWR) == O_RDWR) { - l_mode |= 2; - p9_mode |= 2; - } else if (m_open_mode & O_WRONLY) { - l_mode |= 1; - p9_mode |= 1; - } else if (m_open_mode & O_RDONLY) { - // Leave the values at 0. - } - } - - if (use_lopen) { - Plan9FSMessage message { fs(), Plan9FSMessage::Type::Tlopen }; - message << fid() << l_mode; - return fs().post_message_and_wait_for_a_reply(message); - } - - Plan9FSMessage message { fs(), Plan9FSMessage::Type::Topen }; - message << fid() << p9_mode; - return fs().post_message_and_wait_for_a_reply(message); -} - -ErrorOr Plan9FSInode::read_bytes_locked(off_t offset, size_t size, UserOrKernelBuffer& buffer, OpenFileDescription*) const -{ - TRY(const_cast(*this).ensure_open_for_mode(O_RDONLY)); - - size = fs().adjust_buffer_size(size); - - Plan9FSMessage message { fs(), Plan9FSMessage::Type::Treadlink }; - StringView data; - - // Try readlink first. - bool readlink_succeeded = false; - if (fs().m_remote_protocol_version >= Plan9FS::ProtocolVersion::v9P2000L && offset == 0) { - message << fid(); - if (auto result = fs().post_message_and_wait_for_a_reply(message); !result.is_error()) { - readlink_succeeded = true; - message >> data; - } - } - - if (!readlink_succeeded) { - message = Plan9FSMessage { fs(), Plan9FSMessage::Type::Tread }; - message << fid() << (u64)offset << (u32)size; - TRY(fs().post_message_and_wait_for_a_reply(message)); - data = message.read_data(); - } - - // Guard against the server returning more data than requested. - size_t nread = min(data.length(), size); - TRY(buffer.write(data.characters_without_null_termination(), nread)); - return nread; -} - -ErrorOr Plan9FSInode::replace_child(StringView, Inode&) -{ - // TODO - return ENOTIMPL; -} - -ErrorOr Plan9FSInode::write_bytes_locked(off_t offset, size_t size, UserOrKernelBuffer const& data, OpenFileDescription*) -{ - TRY(ensure_open_for_mode(O_WRONLY)); - size = fs().adjust_buffer_size(size); - - auto data_copy = TRY(data.try_copy_into_kstring(size)); // FIXME: this seems ugly - - Plan9FSMessage message { fs(), Plan9FSMessage::Type::Twrite }; - message << fid() << (u64)offset; - TRY(message.append_data(data_copy->view())); - TRY(fs().post_message_and_wait_for_a_reply(message)); - - u32 nwritten; - message >> nwritten; - return nwritten; -} - -InodeMetadata Plan9FSInode::metadata() const -{ - InodeMetadata metadata; - metadata.inode = identifier(); - - // 9P2000.L; TODO: 9P2000 & 9P2000.u - Plan9FSMessage message { fs(), Plan9FSMessage::Type::Tgetattr }; - message << fid() << (u64)GetAttrMask::Basic; - auto result = fs().post_message_and_wait_for_a_reply(message); - if (result.is_error()) { - // Just return blank metadata; hopefully that's enough to result in an - // error at some upper layer. Ideally, there would be a way for - // Inode::metadata() to return failure. - return metadata; - } - - u64 valid; - Plan9FSQIdentifier qid; - u32 mode; - u32 uid; - u32 gid; - u64 nlink; - u64 rdev; - u64 size; - u64 blksize; - u64 blocks; - message >> valid >> qid >> mode >> uid >> gid >> nlink >> rdev >> size >> blksize >> blocks; - // TODO: times... - - if (valid & (u64)GetAttrMask::Mode) - metadata.mode = mode; - if (valid & (u64)GetAttrMask::NLink) - metadata.link_count = nlink; - -#if 0 - // FIXME: Map UID/GID somehow? Or what do we do? - if (valid & (u64)GetAttrMask::UID) - metadata.uid = uid; - if (valid & (u64)GetAttrMask::GID) - metadata.uid = gid; - // FIXME: What about device nodes? - if (valid & (u64)GetAttrMask::RDev) - metadata.encoded_device = 0; // TODO -#endif - - if (valid & (u64)GetAttrMask::Size) - metadata.size = size; - if (valid & (u64)GetAttrMask::Blocks) { - metadata.block_size = blksize; - metadata.block_count = blocks; - } - - return metadata; -} - -ErrorOr Plan9FSInode::flush_metadata() -{ - // Do nothing. - return {}; -} - -ErrorOr Plan9FSInode::traverse_as_directory(Function(FileSystem::DirectoryEntryView const&)> callback) const -{ - // TODO: Should we synthesize "." and ".." here? - - if (fs().m_remote_protocol_version >= Plan9FS::ProtocolVersion::v9P2000L) { - // Start by cloning the fid and opening it. - auto clone_fid = fs().allocate_fid(); - { - Plan9FSMessage clone_message { fs(), Plan9FSMessage::Type::Twalk }; - clone_message << fid() << clone_fid << (u16)0; - TRY(fs().post_message_and_wait_for_a_reply(clone_message)); - Plan9FSMessage open_message { fs(), Plan9FSMessage::Type::Tlopen }; - open_message << clone_fid << (u32)0; - auto result = fs().post_message_and_wait_for_a_reply(open_message); - if (result.is_error()) { - Plan9FSMessage close_message { fs(), Plan9FSMessage::Type::Tclunk }; - close_message << clone_fid; - // FIXME: Should we observe this error? - [[maybe_unused]] auto rc = fs().post_message_and_explicitly_ignore_reply(close_message); - return result; - } - } - - u64 offset = 0; - u32 count = fs().adjust_buffer_size(8 * MiB); - ErrorOr result; - - while (true) { - Plan9FSMessage message { fs(), Plan9FSMessage::Type::Treaddir }; - message << clone_fid << offset << count; - result = fs().post_message_and_wait_for_a_reply(message); - if (result.is_error()) - break; - - StringView data = message.read_data(); - if (data.is_empty()) { - // We've reached the end. - break; - } - - for (Plan9FSMessage::Decoder decoder { data }; decoder.has_more_data();) { - Plan9FSQIdentifier qid; - u8 type; - StringView name; - decoder >> qid >> offset >> type >> name; - result = callback({ name, { fsid(), fs().allocate_fid() }, 0 }); - if (result.is_error()) - break; - } - - if (result.is_error()) - break; - } - - Plan9FSMessage close_message { fs(), Plan9FSMessage::Type::Tclunk }; - close_message << clone_fid; - // FIXME: Should we observe this error? - [[maybe_unused]] auto rc = fs().post_message_and_explicitly_ignore_reply(close_message); - return result; - } - - // TODO - return ENOTIMPL; -} - -ErrorOr> Plan9FSInode::lookup(StringView name) -{ - u32 newfid = fs().allocate_fid(); - Plan9FSMessage message { fs(), Plan9FSMessage::Type::Twalk }; - message << fid() << newfid << (u16)1 << name; - TRY(fs().post_message_and_wait_for_a_reply(message)); - return TRY(Plan9FSInode::try_create(fs(), newfid)); -} - -ErrorOr> Plan9FSInode::create_child(StringView, mode_t, dev_t, UserID, GroupID) -{ - // TODO - return ENOTIMPL; -} - -ErrorOr Plan9FSInode::add_child(Inode&, StringView, mode_t) -{ - // TODO - return ENOTIMPL; -} - -ErrorOr Plan9FSInode::remove_child(StringView) -{ - // TODO - return ENOTIMPL; -} - -ErrorOr Plan9FSInode::chmod(mode_t) -{ - // TODO - return ENOTIMPL; -} - -ErrorOr Plan9FSInode::chown(UserID, GroupID) -{ - // TODO - return ENOTIMPL; -} - -ErrorOr Plan9FSInode::truncate_locked(u64 new_size) -{ - VERIFY(m_inode_lock.is_locked()); - if (fs().m_remote_protocol_version >= Plan9FS::ProtocolVersion::v9P2000L) { - Plan9FSMessage message { fs(), Plan9FSMessage::Type::Tsetattr }; - SetAttrMask valid = SetAttrMask::Size; - u32 mode = 0; - u32 uid = 0; - u32 gid = 0; - u64 atime_sec = 0; - u64 atime_nsec = 0; - u64 mtime_sec = 0; - u64 mtime_nsec = 0; - message << fid() << (u64)valid << mode << uid << gid << new_size << atime_sec << atime_nsec << mtime_sec << mtime_nsec; - return fs().post_message_and_wait_for_a_reply(message); - } - - // TODO: wstat version - return {}; -} - -} diff --git a/Kernel/FileSystem/Plan9FS/Inode.h b/Kernel/FileSystem/Plan9FS/Inode.h deleted file mode 100644 index bb818c1bd24..00000000000 --- a/Kernel/FileSystem/Plan9FS/Inode.h +++ /dev/null @@ -1,90 +0,0 @@ -/* - * Copyright (c) 2020, Sergey Bugaev - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include - -namespace Kernel { - -class Plan9FSInode final : public Inode { - friend class Plan9FS; - -public: - virtual ~Plan9FSInode() override; - - u32 fid() const { return index().value(); } - - // ^Inode - virtual InodeMetadata metadata() const override; - virtual ErrorOr flush_metadata() override; - virtual ErrorOr traverse_as_directory(Function(FileSystem::DirectoryEntryView const&)>) const override; - virtual ErrorOr> lookup(StringView name) override; - virtual ErrorOr> create_child(StringView name, mode_t, dev_t, UserID, GroupID) override; - virtual ErrorOr add_child(Inode&, StringView name, mode_t) override; - virtual ErrorOr remove_child(StringView name) override; - virtual ErrorOr replace_child(StringView name, Inode& child) override; - virtual ErrorOr chmod(mode_t) override; - virtual ErrorOr chown(UserID, GroupID) override; - virtual ErrorOr truncate_locked(u64) override; - -private: - // ^Inode - virtual ErrorOr read_bytes_locked(off_t, size_t, UserOrKernelBuffer& buffer, OpenFileDescription*) const override; - virtual ErrorOr write_bytes_locked(off_t, size_t, UserOrKernelBuffer const& data, OpenFileDescription*) override; - - Plan9FSInode(Plan9FS&, u32 fid); - static ErrorOr> try_create(Plan9FS&, u32 fid); - - enum class GetAttrMask : u64 { - Mode = 0x1, - NLink = 0x2, - UID = 0x4, - GID = 0x8, - RDev = 0x10, - ATime = 0x20, - MTime = 0x40, - CTime = 0x80, - Ino = 0x100, - Size = 0x200, - Blocks = 0x400, - - BTime = 0x800, - Gen = 0x1000, - DataVersion = 0x2000, - - Basic = 0x7ff, - All = 0x3fff - }; - - enum class SetAttrMask : u64 { - Mode = 0x1, - UID = 0x2, - GID = 0x4, - Size = 0x8, - ATime = 0x10, - MTime = 0x20, - CTime = 0x40, - ATimeSet = 0x80, - MTimeSet = 0x100 - }; - - // Mode in which the file is already open, using SerenityOS constants. - int m_open_mode { 0 }; - ErrorOr ensure_open_for_mode(int mode); - - Plan9FS& fs() { return reinterpret_cast(Inode::fs()); } - Plan9FS& fs() const - { - return const_cast(reinterpret_cast(Inode::fs())); - } -}; - -} diff --git a/Kernel/FileSystem/Plan9FS/Message.cpp b/Kernel/FileSystem/Plan9FS/Message.cpp deleted file mode 100644 index 69272b63f49..00000000000 --- a/Kernel/FileSystem/Plan9FS/Message.cpp +++ /dev/null @@ -1,165 +0,0 @@ -/* - * Copyright (c) 2020, Sergey Bugaev - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include - -namespace Kernel { - -Plan9FSMessage& Plan9FSMessage::operator<<(u8 number) -{ - return append_number(number); -} - -Plan9FSMessage& Plan9FSMessage::operator<<(u16 number) -{ - return append_number(number); -} - -Plan9FSMessage& Plan9FSMessage::operator<<(u32 number) -{ - return append_number(number); -} - -Plan9FSMessage& Plan9FSMessage::operator<<(u64 number) -{ - return append_number(number); -} - -Plan9FSMessage& Plan9FSMessage::operator<<(StringView string) -{ - *this << static_cast(string.length()); - // FIXME: Handle append failure. - (void)m_builder.append(string); - return *this; -} - -ErrorOr Plan9FSMessage::append_data(StringView data) -{ - *this << static_cast(data.length()); - TRY(m_builder.append(data)); - return {}; -} - -Plan9FSMessage::Decoder& Plan9FSMessage::Decoder::operator>>(u8& number) -{ - return read_number(number); -} - -Plan9FSMessage::Decoder& Plan9FSMessage::Decoder::operator>>(u16& number) -{ - return read_number(number); -} - -Plan9FSMessage::Decoder& Plan9FSMessage::Decoder::operator>>(u32& number) -{ - return read_number(number); -} - -Plan9FSMessage::Decoder& Plan9FSMessage::Decoder::operator>>(u64& number) -{ - return read_number(number); -} - -Plan9FSMessage::Decoder& Plan9FSMessage::Decoder::operator>>(Plan9FSQIdentifier& qid) -{ - return *this >> qid.type >> qid.version >> qid.path; -} - -Plan9FSMessage::Decoder& Plan9FSMessage::Decoder::operator>>(StringView& string) -{ - u16 length; - *this >> length; - VERIFY(length <= m_data.length()); - string = m_data.substring_view(0, length); - m_data = m_data.substring_view_starting_after_substring(string); - return *this; -} - -StringView Plan9FSMessage::Decoder::read_data() -{ - u32 length; - *this >> length; - VERIFY(length <= m_data.length()); - auto data = m_data.substring_view(0, length); - m_data = m_data.substring_view_starting_after_substring(data); - return data; -} - -Plan9FSMessage::Plan9FSMessage(Plan9FS& fs, Type type) - : m_builder(KBufferBuilder::try_create().release_value()) // FIXME: Don't assume KBufferBuilder allocation success. - , m_tag(fs.allocate_tag()) - , m_type(type) - , m_have_been_built(false) -{ - u32 size_placeholder = 0; - *this << size_placeholder << (u8)type << m_tag; -} - -Plan9FSMessage::Plan9FSMessage(NonnullOwnPtr&& buffer) - : m_built { move(buffer), Decoder({ buffer->bytes() }) } - , m_have_been_built(true) -{ - u32 size; - u8 raw_type; - *this >> size >> raw_type >> m_tag; - m_type = (Type)raw_type; -} - -Plan9FSMessage::~Plan9FSMessage() -{ - if (m_have_been_built) { - m_built.buffer.~NonnullOwnPtr(); - m_built.decoder.~Decoder(); - } else { - m_builder.~KBufferBuilder(); - } -} - -Plan9FSMessage& Plan9FSMessage::operator=(Plan9FSMessage&& message) -{ - m_tag = message.m_tag; - m_type = message.m_type; - - if (m_have_been_built) { - m_built.buffer.~NonnullOwnPtr(); - m_built.decoder.~Decoder(); - } else { - m_builder.~KBufferBuilder(); - } - - m_have_been_built = message.m_have_been_built; - if (m_have_been_built) { - new (&m_built.buffer) NonnullOwnPtr(move(message.m_built.buffer)); - new (&m_built.decoder) Decoder(move(message.m_built.decoder)); - } else { - new (&m_builder) KBufferBuilder(move(message.m_builder)); - } - - return *this; -} - -KBuffer const& Plan9FSMessage::build() -{ - VERIFY(!m_have_been_built); - - auto tmp_buffer = m_builder.build(); - - // FIXME: We should not assume success here. - VERIFY(tmp_buffer); - - m_have_been_built = true; - m_builder.~KBufferBuilder(); - - new (&m_built.buffer) NonnullOwnPtr(tmp_buffer.release_nonnull()); - new (&m_built.decoder) Decoder({ m_built.buffer->data(), m_built.buffer->size() }); - u32* size = reinterpret_cast(m_built.buffer->data()); - *size = m_built.buffer->size(); - return *m_built.buffer; -} - -} diff --git a/Kernel/FileSystem/Plan9FS/Message.h b/Kernel/FileSystem/Plan9FS/Message.h deleted file mode 100644 index 33d953228a6..00000000000 --- a/Kernel/FileSystem/Plan9FS/Message.h +++ /dev/null @@ -1,189 +0,0 @@ -/* - * Copyright (c) 2020, Sergey Bugaev - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include - -namespace Kernel { - -class Plan9FS; -class Plan9FSInode; - -class Plan9FSMessage { - friend class Plan9FS; - friend class Plan9FSInode; - -public: - enum class Type : u8 { - // 9P2000.L - Tlerror = 6, - Rlerror = 7, - Tstatfs = 8, - Rstatfs = 9, - - Tlopen = 12, - Rlopen = 13, - Tlcreate = 14, - Rlcreate = 15, - Tsymlink = 16, - Rsymlink = 17, - Tmknod = 18, - Rmknod = 19, - Trename = 20, - Rrename = 21, - Treadlink = 22, - Rreadlink = 23, - Tgetattr = 24, - Rgetattr = 25, - Tsetattr = 26, - Rsetattr = 27, - - Txattrwalk = 30, - Rxattrwalk = 31, - Txattrcreate = 32, - Rxattrcreate = 33, - - Treaddir = 40, - Rreaddir = 41, - - Tfsync = 50, - Rfsync = 51, - Tlock = 52, - Rlock = 53, - Tgetlock = 54, - Rgetlock = 55, - - Tlink = 70, - Rlink = 71, - Tmkdir = 72, - Rmkdir = 73, - Trenameat = 74, - Rrenameat = 75, - Tunlinkat = 76, - Runlinkat = 77, - - // 9P2000 - Tversion = 100, - Rversion = 101, - Tauth = 102, - Rauth = 103, - Tattach = 104, - Rattach = 105, - Terror = 106, - Rerror = 107, - Tflush = 108, - Rflush = 109, - Twalk = 110, - Rwalk = 111, - Topen = 112, - Ropen = 113, - Tcreate = 114, - Rcreate = 115, - Tread = 116, - Rread = 117, - Twrite = 118, - Rwrite = 119, - Tclunk = 120, - Rclunk = 121, - Tremove = 122, - Rremove = 123, - Tstat = 124, - Rstat = 125, - Twstat = 126, - Rwstat = 127 - }; - - class Decoder { - public: - explicit Decoder(StringView data) - : m_data(data) - { - } - - Decoder& operator>>(u8&); - Decoder& operator>>(u16&); - Decoder& operator>>(u32&); - Decoder& operator>>(u64&); - Decoder& operator>>(StringView&); - Decoder& operator>>(Plan9FSQIdentifier&); - StringView read_data(); - - bool has_more_data() const { return !m_data.is_empty(); } - - private: - StringView m_data; - - template - Decoder& read_number(N& number) - { - VERIFY(sizeof(number) <= m_data.length()); - memcpy(&number, m_data.characters_without_null_termination(), sizeof(number)); - m_data = m_data.substring_view(sizeof(number), m_data.length() - sizeof(number)); - return *this; - } - }; - - Plan9FSMessage& operator<<(u8); - Plan9FSMessage& operator<<(u16); - Plan9FSMessage& operator<<(u32); - Plan9FSMessage& operator<<(u64); - Plan9FSMessage& operator<<(StringView); - ErrorOr append_data(StringView); - - template - Plan9FSMessage& operator>>(T& t) - { - VERIFY(m_have_been_built); - m_built.decoder >> t; - return *this; - } - - StringView read_data() - { - VERIFY(m_have_been_built); - return m_built.decoder.read_data(); - } - - Type type() const { return m_type; } - u16 tag() const { return m_tag; } - - Plan9FSMessage(Plan9FS&, Type); - Plan9FSMessage(NonnullOwnPtr&&); - ~Plan9FSMessage(); - Plan9FSMessage& operator=(Plan9FSMessage&&); - - KBuffer const& build(); - - static constexpr size_t max_header_size = 24; - -private: - template - Plan9FSMessage& append_number(N number) - { - VERIFY(!m_have_been_built); - // FIXME: Handle append failure. - (void)m_builder.append(reinterpret_cast(&number), sizeof(number)); - return *this; - } - - union { - KBufferBuilder m_builder; - struct { - NonnullOwnPtr buffer; - Decoder decoder; - } m_built; - }; - - u16 m_tag { 0 }; - Type m_type { 0 }; - bool m_have_been_built { false }; -}; - -} diff --git a/Kernel/FileSystem/ProcFS/Definitions.h b/Kernel/FileSystem/ProcFS/Definitions.h deleted file mode 100644 index 63303bc3c6c..00000000000 --- a/Kernel/FileSystem/ProcFS/Definitions.h +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright (c) 2023, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include - -namespace Kernel { - -struct segmented_global_inode_index { - StringView name; - RAMBackedFileType file_type; - u32 primary; - u16 subdirectory; - u32 property; -}; - -constexpr segmented_global_inode_index global_inode_ids[] = { - { "."sv, RAMBackedFileType::Directory, 0, 0, 1 }, // NOTE: This is here for the root directory - { "self"sv, RAMBackedFileType::Directory, 0, 0, 2 } -}; - -struct segmented_process_directory_entry { - StringView name; - RAMBackedFileType file_type; - u16 subdirectory; - u32 property; -}; - -constexpr segmented_process_directory_entry main_process_directory_root_entry = { "."sv, RAMBackedFileType::Directory, 0, 0 }; -constexpr segmented_process_directory_entry process_fd_subdirectory_root_entry = { "."sv, RAMBackedFileType::Directory, 1, 0 }; -constexpr segmented_process_directory_entry process_stacks_subdirectory_root_entry = { "."sv, RAMBackedFileType::Directory, 2, 0 }; -constexpr segmented_process_directory_entry process_children_subdirectory_root_entry = { "."sv, RAMBackedFileType::Directory, 3, 0 }; - -constexpr segmented_process_directory_entry process_fd_directory_entry = { "fd"sv, RAMBackedFileType::Directory, 1, 0 }; -constexpr segmented_process_directory_entry process_stacks_directory_entry = { "stacks"sv, RAMBackedFileType::Directory, 2, 0 }; -constexpr segmented_process_directory_entry process_children_directory_entry = { "children"sv, RAMBackedFileType::Directory, 3, 0 }; -constexpr segmented_process_directory_entry process_unveil_list_entry = { "unveil"sv, RAMBackedFileType::Regular, 0, 1 }; -constexpr segmented_process_directory_entry process_pledge_list_entry = { "pledge"sv, RAMBackedFileType::Regular, 0, 2 }; -constexpr segmented_process_directory_entry process_fds_list_entry = { "fds"sv, RAMBackedFileType::Regular, 0, 3 }; -constexpr segmented_process_directory_entry process_exe_symlink_entry = { "exe"sv, RAMBackedFileType::Link, 0, 4 }; -constexpr segmented_process_directory_entry process_cwd_symlink_entry = { "cwd"sv, RAMBackedFileType::Link, 0, 5 }; -constexpr segmented_process_directory_entry process_perf_events_entry = { "perf_events"sv, RAMBackedFileType::Regular, 0, 6 }; -constexpr segmented_process_directory_entry process_vm_entry = { "vm"sv, RAMBackedFileType::Regular, 0, 7 }; -constexpr segmented_process_directory_entry process_cmdline_entry = { "cmdline"sv, RAMBackedFileType::Regular, 0, 8 }; -constexpr segmented_process_directory_entry main_process_directory_entries[] = { - process_fd_directory_entry, - process_stacks_directory_entry, - process_children_directory_entry, - process_unveil_list_entry, - process_pledge_list_entry, - process_fds_list_entry, - process_exe_symlink_entry, - process_cwd_symlink_entry, - process_perf_events_entry, - process_vm_entry, - process_cmdline_entry, -}; - -} diff --git a/Kernel/FileSystem/ProcFS/FileSystem.cpp b/Kernel/FileSystem/ProcFS/FileSystem.cpp deleted file mode 100644 index bd0f751a76d..00000000000 --- a/Kernel/FileSystem/ProcFS/FileSystem.cpp +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright (c) 2018-2021, Andreas Kling - * Copyright (c) 2021, Spencer Dixon - * Copyright (c) 2021, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include - -namespace Kernel { - -ErrorOr> ProcFS::try_create(ReadonlyBytes) -{ - return TRY(adopt_nonnull_ref_or_enomem(new (nothrow) ProcFS)); -} - -ProcFS::ProcFS() = default; -ProcFS::~ProcFS() = default; - -ErrorOr> ProcFS::get_inode(InodeIdentifier inode_id) const -{ - if (inode_id.index() == 1) - return *m_root_inode; - return TRY(adopt_nonnull_ref_or_enomem(new (nothrow) ProcFSInode(const_cast(*this), inode_id.index()))); -} - -ErrorOr ProcFS::initialize() -{ - m_root_inode = TRY(adopt_nonnull_ref_or_enomem(new (nothrow) ProcFSInode(const_cast(*this), 1))); - return {}; -} - -u8 ProcFS::internal_file_type_to_directory_entry_type(DirectoryEntryView const& entry) const -{ - return ram_backed_file_type_to_directory_entry_type(entry); -} - -Inode& ProcFS::root_inode() -{ - return *m_root_inode; -} - -} diff --git a/Kernel/FileSystem/ProcFS/FileSystem.h b/Kernel/FileSystem/ProcFS/FileSystem.h deleted file mode 100644 index ec483300065..00000000000 --- a/Kernel/FileSystem/ProcFS/FileSystem.h +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include - -namespace Kernel { - -class ProcFSInode; -class ProcFS final : public FileSystem { - friend class ProcFSInode; - friend class Process; - -public: - virtual ~ProcFS() override; - static ErrorOr> try_create(ReadonlyBytes); - - virtual ErrorOr initialize() override; - virtual StringView class_name() const override { return "ProcFS"sv; } - - virtual Inode& root_inode() override; - -private: - virtual u8 internal_file_type_to_directory_entry_type(DirectoryEntryView const& entry) const override; - - ProcFS(); - - ErrorOr> get_inode(InodeIdentifier) const; - - RefPtr m_root_inode; -}; - -} diff --git a/Kernel/FileSystem/ProcFS/Inode.cpp b/Kernel/FileSystem/ProcFS/Inode.cpp deleted file mode 100644 index 8c93ddb756f..00000000000 --- a/Kernel/FileSystem/ProcFS/Inode.cpp +++ /dev/null @@ -1,442 +0,0 @@ -/* - * Copyright (c) 2018-2021, Andreas Kling - * Copyright (c) 2021, Spencer Dixon - * Copyright (c) 2021-2024, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include - -namespace Kernel { - -ProcFSInode::~ProcFSInode() = default; - -static mode_t determine_procfs_process_inode_mode(u32 subdirectory, u32 property) -{ - if (subdirectory == process_fd_subdirectory_root_entry.subdirectory) - return S_IFLNK | 0400; - if (subdirectory == process_stacks_subdirectory_root_entry.subdirectory) - return S_IFREG | 0400; - if (subdirectory == process_children_subdirectory_root_entry.subdirectory) - return S_IFLNK | 0400; - VERIFY(subdirectory == main_process_directory_root_entry.subdirectory); - if (property == process_exe_symlink_entry.property) - return S_IFLNK | 0777; - if (property == process_cwd_symlink_entry.property) - return S_IFLNK | 0777; - return S_IFREG | 0400; -} - -static u16 extract_subdirectory_index_from_inode_index(InodeIndex inode_index) -{ - return (inode_index.value() >> 20) & 0xFFFF; -} - -static u32 extract_property_index_from_inode_index(InodeIndex inode_index) -{ - return inode_index.value() & 0xFFFFF; -} - -InodeIndex ProcFSInode::create_index_from_global_directory_entry(segmented_global_inode_index entry) -{ - u64 inode_index = 0; - VERIFY(entry.primary < 0x10000000); - u64 tmp = entry.primary; - inode_index |= tmp << 36; - - // NOTE: The sub-directory part is already limited to 0xFFFF, so no need to VERIFY it. - tmp = entry.subdirectory; - inode_index |= tmp << 20; - - VERIFY(entry.property < 0x100000); - inode_index |= entry.property; - return inode_index; -} - -InodeIndex ProcFSInode::create_index_from_process_directory_entry(ProcessID pid, segmented_process_directory_entry entry) -{ - u64 inode_index = 0; - // NOTE: We use 0xFFFFFFF because PID part (bits 64-36) as 0 is reserved for global inodes. - VERIFY(pid.value() < 0xFFFFFFF); - u64 tmp = (pid.value() + 1); - inode_index |= tmp << 36; - // NOTE: The sub-directory part is already limited to 0xFFFF, so no need to VERIFY it. - tmp = entry.subdirectory; - inode_index |= tmp << 20; - VERIFY(entry.property < 0x100000); - inode_index |= entry.property; - return inode_index; -} - -static Optional extract_possible_pid_from_inode_index(InodeIndex inode_index) -{ - auto pid_part = inode_index.value() >> 36; - // NOTE: pid_part is set to 0 for global inodes. - if (pid_part == 0) - return {}; - return pid_part - 1; -} - -ProcFSInode::ProcFSInode(ProcFS const& procfs_instance, InodeIndex inode_index) - : Inode(const_cast(procfs_instance), inode_index) - , m_associated_pid(extract_possible_pid_from_inode_index(inode_index)) - , m_subdirectory(extract_subdirectory_index_from_inode_index(inode_index)) - , m_property(extract_property_index_from_inode_index(inode_index)) -{ - if (inode_index == 1) { - m_type = Type::RootDirectory; - return; - } - if (inode_index == 2) { - m_type = Type::SelfProcessLink; - return; - } - - if (m_property == 0) { - if (m_subdirectory > 0) - m_type = Type::ProcessSubdirectory; - else - m_type = Type::ProcessDirectory; - return; - } - - m_type = Type::ProcessProperty; -} - -ErrorOr ProcFSInode::traverse_as_root_directory(Function(FileSystem::DirectoryEntryView const&)> callback) const -{ - TRY(callback({ "."sv, { fsid(), to_underlying(RAMBackedFileType::Directory) }, 0 })); - TRY(callback({ ".."sv, { fsid(), to_underlying(RAMBackedFileType::Directory) }, 0 })); - TRY(callback({ "self"sv, { fsid(), 2 }, to_underlying(RAMBackedFileType::Link) })); - - return Process::for_each_in_same_jail([&](Process& process) -> ErrorOr { - VERIFY(!(process.pid() < 0)); - u64 process_id = (u64)process.pid().value(); - InodeIdentifier identifier = { fsid(), static_cast(process_id << 36) }; - auto process_id_string = TRY(KString::formatted("{:d}", process_id)); - TRY(callback({ process_id_string->view(), identifier, to_underlying(RAMBackedFileType::Directory) })); - return {}; - }); -} - -ErrorOr ProcFSInode::traverse_as_directory(Function(FileSystem::DirectoryEntryView const&)> callback) const -{ - if (m_type == Type::ProcessSubdirectory) { - VERIFY(m_associated_pid.has_value()); - auto process = Process::from_pid_in_same_jail(m_associated_pid.value()); - if (!process) - return EINVAL; - switch (m_subdirectory) { - case process_fd_subdirectory_root_entry.subdirectory: - return process->traverse_file_descriptions_directory(procfs().fsid(), move(callback)); - case process_stacks_subdirectory_root_entry.subdirectory: - return process->traverse_stacks_directory(procfs().fsid(), move(callback)); - case process_children_subdirectory_root_entry.subdirectory: - return process->traverse_children_directory(procfs().fsid(), move(callback)); - default: - VERIFY_NOT_REACHED(); - } - VERIFY_NOT_REACHED(); - } - - if (m_type == Type::RootDirectory) { - return traverse_as_root_directory(move(callback)); - } - - VERIFY(m_type == Type::ProcessDirectory); - VERIFY(m_associated_pid.has_value()); - auto process = Process::from_pid_in_same_jail(m_associated_pid.value()); - if (!process) - return EINVAL; - return process->traverse_as_directory(procfs().fsid(), move(callback)); -} - -ErrorOr> ProcFSInode::lookup_as_root_directory(StringView name) -{ - if (name == "self"sv) - return procfs().get_inode({ fsid(), 2 }); - - auto pid = name.to_number(); - if (!pid.has_value()) - return ESRCH; - auto actual_pid = pid.value(); - - if (auto maybe_process = Process::from_pid_in_same_jail(actual_pid)) { - InodeIndex id = (static_cast(maybe_process->pid().value()) + 1) << 36; - return procfs().get_inode({ fsid(), id }); - } - return ENOENT; -} - -ErrorOr> ProcFSInode::lookup(StringView name) -{ - if (m_type == Type::ProcessSubdirectory) { - VERIFY(m_associated_pid.has_value()); - auto process = Process::from_pid_in_same_jail(m_associated_pid.value()); - if (!process) - return ESRCH; - switch (m_subdirectory) { - case process_fd_subdirectory_root_entry.subdirectory: - return process->lookup_file_descriptions_directory(procfs(), name); - case process_stacks_subdirectory_root_entry.subdirectory: - return process->lookup_stacks_directory(procfs(), name); - case process_children_subdirectory_root_entry.subdirectory: - return process->lookup_children_directory(procfs(), name); - default: - VERIFY_NOT_REACHED(); - } - VERIFY_NOT_REACHED(); - } - - if (m_type == Type::RootDirectory) { - return lookup_as_root_directory(name); - } - - VERIFY(m_type == Type::ProcessDirectory); - VERIFY(m_associated_pid.has_value()); - auto process = Process::from_pid_in_same_jail(m_associated_pid.value()); - if (!process) - return ESRCH; - return process->lookup_as_directory(procfs(), name); -} - -ErrorOr ProcFSInode::attach(OpenFileDescription& description) -{ - if (m_type == Type::RootDirectory || m_type == Type::SelfProcessLink || m_type == Type::ProcessDirectory || m_type == Type::ProcessSubdirectory) - return {}; - VERIFY(m_type == Type::ProcessProperty); - return refresh_process_property_data(description); -} - -void ProcFSInode::did_seek(OpenFileDescription& description, off_t offset) -{ - if (m_type == Type::SelfProcessLink) { - return; - } - VERIFY(m_type == Type::ProcessProperty); - if (offset != 0) - return; - (void)refresh_process_property_data(description); -} - -ErrorOr ProcFSInode::read_bytes_locked(off_t offset, size_t count, UserOrKernelBuffer& buffer, OpenFileDescription* description) const -{ - dbgln_if(PROCFS_DEBUG, "ProcFSInode: read_bytes_locked offset: {} count: {}", offset, count); - VERIFY(offset >= 0); - VERIFY(buffer.user_or_kernel_ptr()); - - if (m_type == Type::SelfProcessLink) { - auto builder = TRY(KBufferBuilder::try_create()); - TRY(builder.appendff("{}", Process::current().pid().value())); - auto data_buffer = builder.build(); - if (!data_buffer) - return Error::from_errno(EFAULT); - if ((size_t)offset >= data_buffer->size()) - return 0; - ssize_t nread = min(static_cast(data_buffer->size() - offset), static_cast(count)); - TRY(buffer.write(data_buffer->data() + offset, nread)); - return nread; - } - - VERIFY(m_type == Type::ProcessProperty); - - if (!description) { - auto builder = TRY(KBufferBuilder::try_create()); - VERIFY(m_associated_pid.has_value()); - auto process = Process::from_pid_in_same_jail(m_associated_pid.value()); - if (!process) - return Error::from_errno(ESRCH); - TRY(try_fetch_process_property_data(*process, builder)); - auto data_buffer = builder.build(); - if (!data_buffer) - return Error::from_errno(EFAULT); - if ((size_t)offset >= data_buffer->size()) - return 0; - ssize_t nread = min(static_cast(data_buffer->size() - offset), static_cast(count)); - TRY(buffer.write(data_buffer->data() + offset, nread)); - return nread; - } - - if (!description->data()) { - dbgln("ProcFS Process Information: Do not have cached data!"); - return Error::from_errno(EIO); - } - - MutexLocker locker(m_refresh_lock); - - auto& typed_cached_data = static_cast(*description->data()); - auto& data_buffer = typed_cached_data.buffer; - - if (!data_buffer || (size_t)offset >= data_buffer->size()) - return 0; - - ssize_t nread = min(static_cast(data_buffer->size() - offset), static_cast(count)); - TRY(buffer.write(data_buffer->data() + offset, nread)); - return nread; -} - -static ErrorOr build_from_cached_data(KBufferBuilder& builder, ProcFSInodeData& cached_data) -{ - cached_data.buffer = builder.build(); - if (!cached_data.buffer) - return ENOMEM; - return {}; -} - -ErrorOr ProcFSInode::try_fetch_process_property_data(NonnullRefPtr process, KBufferBuilder& builder) const -{ - VERIFY(m_type == Type::ProcessProperty); - if (m_subdirectory == process_fd_subdirectory_root_entry.subdirectory) { - // NOTE: All property numbers should start from 1 as 0 is reserved for the directory itself. - // Therefore subtract 1 to get the actual correct fd number. - TRY(process->procfs_get_file_description_link(m_property - 1, builder)); - return {}; - } - if (m_subdirectory == process_stacks_subdirectory_root_entry.subdirectory) { - // NOTE: All property numbers should start from 1 as 0 is reserved for the directory itself. - // Therefore subtract 1 to get the actual correct thread stack number. - TRY(process->procfs_get_thread_stack(m_property - 1, builder)); - return {}; - } - if (m_subdirectory == process_children_subdirectory_root_entry.subdirectory) { - // NOTE: All property numbers should start from 1 as 0 is reserved for the directory itself. - // Therefore subtract 1 to get the actual correct child process index number for a correct symlink. - TRY(process->procfs_get_child_process_link(m_property - 1, builder)); - return {}; - } - - VERIFY(m_subdirectory == main_process_directory_root_entry.subdirectory); - switch (m_property) { - case process_unveil_list_entry.property: - return process->procfs_get_unveil_stats(builder); - case process_pledge_list_entry.property: - return process->procfs_get_pledge_stats(builder); - case process_fds_list_entry.property: - return process->procfs_get_fds_stats(builder); - case process_exe_symlink_entry.property: - return process->procfs_get_binary_link(builder); - case process_cwd_symlink_entry.property: - return process->procfs_get_current_work_directory_link(builder); - case process_perf_events_entry.property: - return process->procfs_get_perf_events(builder); - case process_vm_entry.property: - return process->procfs_get_virtual_memory_stats(builder); - case process_cmdline_entry.property: - return process->procfs_get_command_line(builder); - default: - VERIFY_NOT_REACHED(); - } -} - -ErrorOr ProcFSInode::refresh_process_property_data(OpenFileDescription& description) -{ - // For process-specific inodes, hold the process's ptrace lock across refresh - // and refuse to load data if the process is not dumpable. - // Without this, files opened before a process went non-dumpable could still be used for dumping. - VERIFY(m_type == Type::ProcessProperty); - VERIFY(m_associated_pid.has_value()); - auto process = Process::from_pid_in_same_jail(m_associated_pid.value()); - if (!process) - return Error::from_errno(ESRCH); - process->ptrace_lock().lock(); - if (!process->is_dumpable()) { - process->ptrace_lock().unlock(); - return EPERM; - } - ScopeGuard guard = [&] { - process->ptrace_lock().unlock(); - }; - MutexLocker locker(m_refresh_lock); - auto& cached_data = description.data(); - if (!cached_data) { - cached_data = adopt_own_if_nonnull(new (nothrow) ProcFSInodeData); - if (!cached_data) - return ENOMEM; - } - auto builder = TRY(KBufferBuilder::try_create()); - TRY(try_fetch_process_property_data(*process, builder)); - return build_from_cached_data(builder, static_cast(*cached_data)); -} - -InodeMetadata ProcFSInode::metadata() const -{ - InodeMetadata metadata; - switch (m_type) { - case Type::SelfProcessLink: { - metadata.inode = { fsid(), 2 }; - metadata.mode = S_IFLNK | 0777; - metadata.uid = 0; - metadata.gid = 0; - metadata.size = 0; - metadata.mtime = TimeManagement::boot_time(); - break; - } - case Type::RootDirectory: { - metadata.inode = { fsid(), 1 }; - metadata.mode = S_IFDIR | 0555; - metadata.uid = 0; - metadata.gid = 0; - metadata.size = 0; - metadata.mtime = TimeManagement::boot_time(); - break; - } - case Type::ProcessProperty: { - VERIFY(m_associated_pid.has_value()); - auto process = Process::from_pid_in_same_jail(m_associated_pid.value()); - if (!process) - return {}; - metadata.inode = identifier(); - metadata.mode = determine_procfs_process_inode_mode(m_subdirectory, m_property); - auto credentials = process->credentials(); - metadata.uid = credentials->uid(); - metadata.gid = credentials->gid(); - metadata.size = 0; - auto creation_time = process->creation_time(); - metadata.atime = creation_time; - metadata.ctime = creation_time; - metadata.mtime = creation_time; - break; - } - case Type::ProcessDirectory: { - VERIFY(m_associated_pid.has_value()); - auto process = Process::from_pid_in_same_jail(m_associated_pid.value()); - if (!process) - return {}; - metadata.inode = identifier(); - metadata.mode = S_IFDIR | 0555; - auto credentials = process->credentials(); - metadata.uid = credentials->uid(); - metadata.gid = credentials->gid(); - metadata.size = 0; - auto creation_time = process->creation_time(); - metadata.atime = creation_time; - metadata.ctime = creation_time; - metadata.mtime = creation_time; - break; - } - case Type::ProcessSubdirectory: { - VERIFY(m_associated_pid.has_value()); - auto process = Process::from_pid_in_same_jail(m_associated_pid.value()); - if (!process) - return {}; - metadata.inode = identifier(); - metadata.mode = S_IFDIR | 0555; - auto credentials = process->credentials(); - metadata.uid = credentials->uid(); - metadata.gid = credentials->gid(); - metadata.size = 0; - auto creation_time = process->creation_time(); - metadata.atime = creation_time; - metadata.ctime = creation_time; - metadata.mtime = creation_time; - break; - } - } - return metadata; -} - -} diff --git a/Kernel/FileSystem/ProcFS/Inode.h b/Kernel/FileSystem/ProcFS/Inode.h deleted file mode 100644 index 408021fc864..00000000000 --- a/Kernel/FileSystem/ProcFS/Inode.h +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * Copyright (c) 2022-2023, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include -#include - -namespace Kernel { -struct ProcFSInodeData : public OpenFileDescriptionData { - OwnPtr buffer; -}; - -class ProcFSInode final : public Inode { - friend class ProcFS; - -public: - enum class Type { - RootDirectory, - SelfProcessLink, - ProcessProperty, - ProcessDirectory, - ProcessSubdirectory, - }; - - static InodeIndex create_index_from_global_directory_entry(segmented_global_inode_index entry); - static InodeIndex create_index_from_process_directory_entry(ProcessID pid, segmented_process_directory_entry entry); - - virtual ~ProcFSInode() override; - -private: - ProcFSInode(ProcFS const&, InodeIndex); - - ProcFS& procfs() { return static_cast(Inode::fs()); } - ProcFS const& procfs() const { return static_cast(Inode::fs()); } - - // ^Inode (EROFS handling) - virtual ErrorOr> create_child(StringView, mode_t, dev_t, UserID, GroupID) override { return EROFS; } - virtual ErrorOr add_child(Inode&, StringView, mode_t) override { return EROFS; } - virtual ErrorOr remove_child(StringView) override { return EROFS; } - virtual ErrorOr replace_child(StringView, Inode&) override { return EROFS; } - virtual ErrorOr chmod(mode_t) override { return EROFS; } - virtual ErrorOr chown(UserID, GroupID) override { return EROFS; } - virtual ErrorOr write_bytes_locked(off_t, size_t, UserOrKernelBuffer const&, OpenFileDescription*) override { return EROFS; } - virtual ErrorOr truncate_locked(u64) override { return EROFS; } - - // ^Inode (Silent ignore handling) - virtual ErrorOr flush_metadata() override { return {}; } - virtual ErrorOr update_timestamps(Optional, Optional, Optional) override { return {}; } - - // ^Inode - virtual ErrorOr attach(OpenFileDescription& description) override; - virtual void did_seek(OpenFileDescription&, off_t) override; - ErrorOr traverse_as_root_directory(Function(FileSystem::DirectoryEntryView const&)>) const; - virtual ErrorOr traverse_as_directory(Function(FileSystem::DirectoryEntryView const&)>) const override; - - virtual InodeMetadata metadata() const override; - virtual ErrorOr read_bytes_locked(off_t, size_t, UserOrKernelBuffer& buffer, OpenFileDescription*) const override; - - ErrorOr> lookup_as_root_directory(StringView name); - virtual ErrorOr> lookup(StringView name) override final; - - ErrorOr refresh_process_property_data(OpenFileDescription& description); - ErrorOr try_fetch_process_property_data(NonnullRefPtr, KBufferBuilder& builder) const; - - Type m_type; - Optional const m_associated_pid {}; - u16 const m_subdirectory { 0 }; - u32 const m_property { 0 }; - - mutable Mutex m_refresh_lock; -}; - -} diff --git a/Kernel/FileSystem/ProcFS/ProcessExposed.cpp b/Kernel/FileSystem/ProcFS/ProcessExposed.cpp deleted file mode 100644 index c1eccd7c64a..00000000000 --- a/Kernel/FileSystem/ProcFS/ProcessExposed.cpp +++ /dev/null @@ -1,372 +0,0 @@ -/* - * Copyright (c) 2021-2024, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace Kernel { - -ErrorOr Process::traverse_as_directory(FileSystemID fsid, Function(FileSystem::DirectoryEntryView const&)> callback) const -{ - TRY(callback({ main_process_directory_root_entry.name, { fsid, ProcFSInode::create_index_from_process_directory_entry(pid(), main_process_directory_root_entry) }, to_underlying(main_process_directory_root_entry.file_type) })); - TRY(callback({ ".."sv, { fsid, ProcFSInode::create_index_from_global_directory_entry(global_inode_ids[0]) }, to_underlying(global_inode_ids[0].file_type) })); - - for (auto& entry : main_process_directory_entries) { - TRY(callback({ entry.name, { fsid, ProcFSInode::create_index_from_process_directory_entry(pid(), entry) }, to_underlying(entry.file_type) })); - } - return {}; -} - -ErrorOr> Process::lookup_as_directory(ProcFS& procfs, StringView name) const -{ - for (auto& entry : main_process_directory_entries) { - if (entry.name == name) - return procfs.get_inode({ procfs.fsid(), ProcFSInode::create_index_from_process_directory_entry(pid(), entry) }); - } - return ENOENT; -} - -ErrorOr Process::procfs_get_thread_stack(ThreadID thread_id, KBufferBuilder& builder) const -{ - auto array = TRY(JsonArraySerializer<>::try_create(builder)); - auto thread = Thread::from_tid_in_same_jail(thread_id); - if (!thread) - return ESRCH; - auto current_process_credentials = Process::current().credentials(); - bool show_kernel_addresses = current_process_credentials->is_superuser(); - bool kernel_address_added = false; - for (auto address : TRY(Processor::capture_stack_trace(*thread, 1024))) { - if (!show_kernel_addresses && !Memory::is_user_address(VirtualAddress { address })) { - if (kernel_address_added) - continue; - address = 0xdeadc0de; - kernel_address_added = true; - } - TRY(array.add(address)); - } - - TRY(array.finish()); - return {}; -} - -ErrorOr Process::traverse_stacks_directory(FileSystemID fsid, Function(FileSystem::DirectoryEntryView const&)> callback) const -{ - TRY(callback({ "."sv, { fsid, ProcFSInode::create_index_from_process_directory_entry(pid(), process_stacks_subdirectory_root_entry) }, to_underlying(RAMBackedFileType::Directory) })); - TRY(callback({ ".."sv, { fsid, ProcFSInode::create_index_from_process_directory_entry(pid(), main_process_directory_root_entry) }, to_underlying(main_process_directory_root_entry.file_type) })); - - return thread_list().with([&](auto& list) -> ErrorOr { - for (auto const& thread : list) { - // NOTE: All property numbers should start from 1 as 0 is reserved for the directory itself. - auto entry = segmented_process_directory_entry { {}, RAMBackedFileType::Regular, process_stacks_subdirectory_root_entry.subdirectory, static_cast(thread.tid().value() + 1) }; - InodeIdentifier identifier = { fsid, ProcFSInode::create_index_from_process_directory_entry(pid(), entry) }; - auto name = TRY(KString::number(thread.tid().value())); - TRY(callback({ name->view(), identifier, to_underlying(RAMBackedFileType::Regular) })); - } - return {}; - }); -} - -ErrorOr> Process::lookup_stacks_directory(ProcFS& procfs, StringView name) const -{ - auto maybe_needle = name.to_number(); - if (!maybe_needle.has_value()) - return ENOENT; - auto needle = maybe_needle.release_value(); - - ErrorOr> thread_stack_inode { ENOENT }; - for_each_thread([&](Thread const& thread) { - int tid = thread.tid().value(); - VERIFY(!(tid < 0)); - if (needle == (unsigned)tid) { - // NOTE: All property numbers should start from 1 as 0 is reserved for the directory itself. - auto entry = segmented_process_directory_entry { {}, RAMBackedFileType::Regular, process_stacks_subdirectory_root_entry.subdirectory, static_cast(thread.tid().value() + 1) }; - thread_stack_inode = procfs.get_inode({ procfs.fsid(), ProcFSInode::create_index_from_process_directory_entry(pid(), entry) }); - return IterationDecision::Break; - } - return IterationDecision::Continue; - }); - - if (thread_stack_inode.is_error()) - return thread_stack_inode.release_error(); - return thread_stack_inode.release_value(); -} - -ErrorOr Process::traverse_children_directory(FileSystemID fsid, Function(FileSystem::DirectoryEntryView const&)> callback) const -{ - TRY(callback({ "."sv, { fsid, ProcFSInode::create_index_from_process_directory_entry(pid(), process_children_subdirectory_root_entry) }, to_underlying(RAMBackedFileType::Directory) })); - TRY(callback({ ".."sv, { fsid, ProcFSInode::create_index_from_process_directory_entry(pid(), main_process_directory_root_entry) }, to_underlying(main_process_directory_root_entry.file_type) })); - return Process::for_each_in_same_jail([&](Process& process) -> ErrorOr { - if (process.ppid() == pid()) { - auto name = TRY(KString::number(process.pid().value())); - // NOTE: All property numbers should start from 1 as 0 is reserved for the directory itself. - auto entry = segmented_process_directory_entry { {}, RAMBackedFileType::Link, process_children_subdirectory_root_entry.subdirectory, static_cast(process.pid().value() + 1) }; - InodeIdentifier identifier = { fsid, ProcFSInode::create_index_from_process_directory_entry(pid(), entry) }; - TRY(callback({ name->view(), identifier, to_underlying(RAMBackedFileType::Link) })); - } - return {}; - }); -} - -ErrorOr> Process::lookup_children_directory(ProcFS& procfs, StringView name) const -{ - auto maybe_pid = name.to_number(); - if (!maybe_pid.has_value()) - return ENOENT; - - auto child_process = Process::from_pid_in_same_jail(*maybe_pid); - if (!child_process || child_process->ppid() != pid()) - return ENOENT; - - // NOTE: All property numbers should start from 1 as 0 is reserved for the directory itself. - auto entry = segmented_process_directory_entry { {}, RAMBackedFileType::Link, process_children_subdirectory_root_entry.subdirectory, (maybe_pid.value() + 1) }; - return procfs.get_inode({ procfs.fsid(), ProcFSInode::create_index_from_process_directory_entry(pid(), entry) }); -} - -ErrorOr Process::procfs_get_child_process_link(ProcessID child_pid, KBufferBuilder& builder) const -{ - TRY(builder.appendff("../../{}", child_pid.value())); - return builder.length(); -} - -ErrorOr Process::procfs_get_file_description_link(unsigned fd, KBufferBuilder& builder) const -{ - auto file_description = TRY(open_file_description(fd)); - // Note: These links are not guaranteed to point to actual VFS paths, just like in other kernels. - auto data = TRY(file_description->pseudo_path()); - TRY(builder.append(data->view())); - return data->length(); -} - -ErrorOr Process::traverse_file_descriptions_directory(FileSystemID fsid, Function(FileSystem::DirectoryEntryView const&)> callback) const -{ - TRY(callback({ "."sv, { fsid, ProcFSInode::create_index_from_process_directory_entry(pid(), process_fd_subdirectory_root_entry) }, to_underlying(RAMBackedFileType::Directory) })); - TRY(callback({ ".."sv, { fsid, ProcFSInode::create_index_from_process_directory_entry(pid(), main_process_directory_root_entry) }, to_underlying(main_process_directory_root_entry.file_type) })); - u32 count = 0; - TRY(fds().with_shared([&](auto& fds) -> ErrorOr { - return fds.try_enumerate([&](auto& file_description_metadata) -> ErrorOr { - if (!file_description_metadata.is_valid()) { - count++; - return {}; - } - auto name = TRY(KString::number(count)); - // NOTE: All property numbers should start from 1 as 0 is reserved for the directory itself. - auto entry = segmented_process_directory_entry { {}, RAMBackedFileType::Link, process_fd_subdirectory_root_entry.subdirectory, count + 1 }; - InodeIdentifier identifier = { fsid, ProcFSInode::create_index_from_process_directory_entry(pid(), entry) }; - TRY(callback({ name->view(), identifier, to_underlying(RAMBackedFileType::Link) })); - count++; - return {}; - }); - })); - return {}; -} - -ErrorOr> Process::lookup_file_descriptions_directory(ProcFS& procfs, StringView name) const -{ - auto maybe_index = name.to_number(); - if (!maybe_index.has_value()) - return ENOENT; - - if (!m_fds.with_shared([&](auto& fds) { return fds.get_if_valid(*maybe_index); })) - return ENOENT; - - // NOTE: All property numbers should start from 1 as 0 is reserved for the directory itself. - auto entry = segmented_process_directory_entry { {}, RAMBackedFileType::Link, process_fd_subdirectory_root_entry.subdirectory, (maybe_index.value() + 1) }; - return procfs.get_inode({ procfs.fsid(), ProcFSInode::create_index_from_process_directory_entry(pid(), entry) }); -} - -ErrorOr Process::procfs_get_pledge_stats(KBufferBuilder& builder) const -{ - auto obj = TRY(JsonObjectSerializer<>::try_create(builder)); -#define __ENUMERATE_PLEDGE_PROMISE(x) \ - if (has_promised(Pledge::x)) { \ - if (!promises_builder.is_empty()) \ - TRY(promises_builder.try_append(' ')); \ - TRY(promises_builder.try_append(#x##sv)); \ - } - if (has_promises()) { - StringBuilder promises_builder; - ENUMERATE_PLEDGE_PROMISES - TRY(obj.add("promises"sv, promises_builder.string_view())); - } -#undef __ENUMERATE_PLEDGE_PROMISE - TRY(obj.finish()); - return {}; -} - -ErrorOr Process::procfs_get_unveil_stats(KBufferBuilder& builder) const -{ - auto array = TRY(JsonArraySerializer<>::try_create(builder)); - TRY(m_unveil_data.with([&](auto& unveil_data) -> ErrorOr { - TRY(unveil_data.paths.for_each_node_in_tree_order([&](auto const& unveiled_path) -> ErrorOr { - if (!unveiled_path.was_explicitly_unveiled()) - return IterationDecision::Continue; - auto obj = TRY(array.add_object()); - TRY(obj.add("path"sv, unveiled_path.path())); - StringBuilder permissions_builder; - if (unveiled_path.permissions() & UnveilAccess::Read) - permissions_builder.append('r'); - if (unveiled_path.permissions() & UnveilAccess::Write) - permissions_builder.append('w'); - if (unveiled_path.permissions() & UnveilAccess::Execute) - permissions_builder.append('x'); - if (unveiled_path.permissions() & UnveilAccess::CreateOrRemove) - permissions_builder.append('c'); - if (unveiled_path.permissions() & UnveilAccess::Browse) - permissions_builder.append('b'); - TRY(obj.add("permissions"sv, permissions_builder.string_view())); - TRY(obj.finish()); - return IterationDecision::Continue; - })); - return {}; - })); - TRY(array.finish()); - return {}; -} - -ErrorOr Process::procfs_get_perf_events(KBufferBuilder& builder) const -{ - InterruptDisabler disabler; - if (!perf_events()) { - dbgln("ProcFS: No perf events for {}", pid()); - return Error::from_errno(ENOBUFS); - } - return perf_events()->to_json(builder); -} - -ErrorOr Process::procfs_get_fds_stats(KBufferBuilder& builder) const -{ - auto array = TRY(JsonArraySerializer<>::try_create(builder)); - - return fds().with_shared([&](auto& fds) -> ErrorOr { - if (fds.open_count() == 0) { - TRY(array.finish()); - return {}; - } - - size_t count = 0; - TRY(fds.try_enumerate([&](auto& file_description_metadata) -> ErrorOr { - if (!file_description_metadata.is_valid()) { - count++; - return {}; - } - bool cloexec = file_description_metadata.flags() & FD_CLOEXEC; - auto const* description = file_description_metadata.description(); - auto description_object = TRY(array.add_object()); - TRY(description_object.add("fd"sv, count)); - // TODO: Better OOM handling. - auto pseudo_path_or_error = description->pseudo_path(); - TRY(description_object.add("absolute_path"sv, pseudo_path_or_error.is_error() ? "???"sv : pseudo_path_or_error.value()->view())); - TRY(description_object.add("seekable"sv, description->file().is_seekable())); - TRY(description_object.add("class"sv, description->file().class_name())); - TRY(description_object.add("offset"sv, description->offset())); - TRY(description_object.add("cloexec"sv, cloexec)); - TRY(description_object.add("blocking"sv, description->is_blocking())); - TRY(description_object.add("can_read"sv, description->can_read())); - TRY(description_object.add("can_write"sv, description->can_write())); - Inode const* inode = description->inode(); - if (inode != nullptr) { - auto inode_object = TRY(description_object.add_object("inode"sv)); - TRY(inode_object.add("fsid"sv, inode->fsid().value())); - TRY(inode_object.add("index"sv, inode->index().value())); - TRY(inode_object.finish()); - } - TRY(description_object.finish()); - count++; - return {}; - })); - - TRY(array.finish()); - return {}; - }); -} - -ErrorOr Process::procfs_get_virtual_memory_stats(KBufferBuilder& builder) const -{ - auto array = TRY(JsonArraySerializer<>::try_create(builder)); - TRY(address_space().with([&](auto& space) -> ErrorOr { - for (auto const& region : space->region_tree().regions()) { - auto current_process_credentials = Process::current().credentials(); - if (!region.is_user() && !current_process_credentials->is_superuser()) - continue; - auto region_object = TRY(array.add_object()); - TRY(region_object.add("readable"sv, region.is_readable())); - TRY(region_object.add("writable"sv, region.is_writable())); - TRY(region_object.add("executable"sv, region.is_executable())); - TRY(region_object.add("stack"sv, region.is_stack())); - TRY(region_object.add("shared"sv, region.is_shared())); - TRY(region_object.add("syscall"sv, region.is_syscall_region())); - TRY(region_object.add("purgeable"sv, region.vmobject().is_anonymous())); - if (region.vmobject().is_anonymous()) { - TRY(region_object.add("volatile"sv, static_cast(region.vmobject()).is_volatile())); - } - TRY(region_object.add("cacheable"sv, region.is_cacheable())); - TRY(region_object.add("address"sv, region.vaddr().get())); - TRY(region_object.add("size"sv, region.size())); - TRY(region_object.add("amount_resident"sv, region.amount_resident())); - TRY(region_object.add("amount_dirty"sv, region.amount_dirty())); - TRY(region_object.add("cow_pages"sv, region.cow_pages())); - TRY(region_object.add("name"sv, region.name())); - TRY(region_object.add("vmobject"sv, region.vmobject().class_name())); - - StringBuilder pagemap_builder; - for (size_t i = 0; i < region.page_count(); ++i) { - auto page = region.physical_page(i); - if (!page) - pagemap_builder.append('N'); - else if (page->is_shared_zero_page() || page->is_lazy_committed_page()) - pagemap_builder.append('Z'); - else - pagemap_builder.append('P'); - } - TRY(region_object.add("pagemap"sv, pagemap_builder.string_view())); - TRY(region_object.finish()); - } - return {}; - })); - TRY(array.finish()); - return {}; -} - -ErrorOr Process::procfs_get_current_work_directory_link(KBufferBuilder& builder) const -{ - return builder.append(TRY(const_cast(*this).current_directory()->try_serialize_absolute_path())->view()); -} - -ErrorOr Process::procfs_get_command_line(KBufferBuilder& builder) const -{ - auto array = TRY(JsonArraySerializer<>::try_create(builder)); - for (auto const& arg : arguments()) { - TRY(array.add(arg->view())); - } - TRY(array.finish()); - return {}; -} - -mode_t Process::binary_link_required_mode() const -{ - if (!executable()) - return 0; - return 0555; -} - -ErrorOr Process::procfs_get_binary_link(KBufferBuilder& builder) const -{ - auto custody = executable(); - if (!custody) - return Error::from_errno(ENOEXEC); - return builder.append(TRY(custody->try_serialize_absolute_path())->view()); -} - -} diff --git a/Kernel/FileSystem/RAMBackedFileType.h b/Kernel/FileSystem/RAMBackedFileType.h deleted file mode 100644 index 8234c5902d0..00000000000 --- a/Kernel/FileSystem/RAMBackedFileType.h +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Copyright (c) 2024, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include - -namespace Kernel { - -enum class RAMBackedFileType : u8 { - Directory, - Character, - Block, - Regular, - FIFO, - Link, - Socket, - Unknown, -}; - -inline RAMBackedFileType ram_backed_file_type_from_mode(mode_t mode) -{ - switch (mode & S_IFMT) { - case S_IFDIR: - return RAMBackedFileType::Directory; - case S_IFCHR: - return RAMBackedFileType::Character; - case S_IFBLK: - return RAMBackedFileType::Block; - case S_IFREG: - return RAMBackedFileType::Regular; - case S_IFIFO: - return RAMBackedFileType::FIFO; - case S_IFLNK: - return RAMBackedFileType::Link; - case S_IFSOCK: - return RAMBackedFileType::Socket; - default: - return RAMBackedFileType::Unknown; - } -} - -inline u8 ram_backed_file_type_to_directory_entry_type(FileSystem::DirectoryEntryView const& entry) -{ - switch (static_cast(entry.file_type)) { - case RAMBackedFileType::Directory: - return DT_DIR; - case RAMBackedFileType::Character: - return DT_CHR; - case RAMBackedFileType::Block: - return DT_BLK; - case RAMBackedFileType::Regular: - return DT_REG; - case RAMBackedFileType::FIFO: - return DT_FIFO; - case RAMBackedFileType::Link: - return DT_LNK; - case RAMBackedFileType::Socket: - return DT_SOCK; - case RAMBackedFileType::Unknown: - default: - return DT_UNKNOWN; - } -} - -} diff --git a/Kernel/FileSystem/RAMFS/FileSystem.cpp b/Kernel/FileSystem/RAMFS/FileSystem.cpp deleted file mode 100644 index 452648b72af..00000000000 --- a/Kernel/FileSystem/RAMFS/FileSystem.cpp +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright (c) 2019-2020, Sergey Bugaev - * Copyright (c) 2022-2024, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include - -namespace Kernel { - -ErrorOr> RAMFS::try_create(ReadonlyBytes) -{ - return TRY(adopt_nonnull_ref_or_enomem(new (nothrow) RAMFS)); -} - -RAMFS::RAMFS() = default; -RAMFS::~RAMFS() = default; - -ErrorOr RAMFS::initialize() -{ - m_root_inode = TRY(RAMFSInode::try_create_root(*this)); - return {}; -} - -Inode& RAMFS::root_inode() -{ - VERIFY(!m_root_inode.is_null()); - return *m_root_inode; -} - -unsigned RAMFS::next_inode_index() -{ - MutexLocker locker(m_lock); - - return m_next_inode_index++; -} - -u8 RAMFS::internal_file_type_to_directory_entry_type(DirectoryEntryView const& entry) const -{ - return ram_backed_file_type_to_directory_entry_type(entry); -} - -} diff --git a/Kernel/FileSystem/RAMFS/FileSystem.h b/Kernel/FileSystem/RAMFS/FileSystem.h deleted file mode 100644 index 92e8cae4914..00000000000 --- a/Kernel/FileSystem/RAMFS/FileSystem.h +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright (c) 2019-2020, Sergey Bugaev - * Copyright (c) 2022-2024, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include - -namespace Kernel { - -class RAMFS final : public FileSystem { - friend class RAMFSInode; - -public: - virtual ~RAMFS() override; - static ErrorOr> try_create(ReadonlyBytes); - virtual ErrorOr initialize() override; - - virtual StringView class_name() const override { return "RAMFS"sv; } - - virtual bool supports_watchers() const override { return true; } - virtual bool supports_backing_loop_devices() const override { return true; } - - virtual Inode& root_inode() override; - - virtual u8 internal_file_type_to_directory_entry_type(DirectoryEntryView const& entry) const override; - -private: - RAMFS(); - - RefPtr m_root_inode; - - // NOTE: We start by assigning InodeIndex of 2, because 0 is invalid and 1 - // is reserved for the root directory inode. - unsigned m_next_inode_index { 2 }; - unsigned next_inode_index(); -}; - -} diff --git a/Kernel/FileSystem/RAMFS/Inode.cpp b/Kernel/FileSystem/RAMFS/Inode.cpp deleted file mode 100644 index ba8cdfd65a5..00000000000 --- a/Kernel/FileSystem/RAMFS/Inode.cpp +++ /dev/null @@ -1,401 +0,0 @@ -/* - * Copyright (c) 2019-2020, Sergey Bugaev - * Copyright (c) 2022-2024, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include - -namespace Kernel { - -RAMFSInode::RAMFSInode(RAMFS& fs, InodeMetadata const& metadata, LockWeakPtr parent) - : Inode(fs, fs.next_inode_index()) - , m_metadata(metadata) - , m_parent(move(parent)) -{ - m_metadata.inode = identifier(); -} - -RAMFSInode::RAMFSInode(RAMFS& fs) - : Inode(fs, 1) - , m_root_directory_inode(true) -{ - auto now = kgettimeofday(); - m_metadata.inode = identifier(); - m_metadata.atime = now; - m_metadata.ctime = now; - m_metadata.mtime = now; - m_metadata.mode = S_IFDIR | 0755; -} - -RAMFSInode::~RAMFSInode() = default; - -ErrorOr> RAMFSInode::try_create(RAMFS& fs, InodeMetadata const& metadata, LockWeakPtr parent) -{ - return adopt_nonnull_ref_or_enomem(new (nothrow) RAMFSInode(fs, metadata, move(parent))); -} - -ErrorOr> RAMFSInode::try_create_root(RAMFS& fs) -{ - return adopt_nonnull_ref_or_enomem(new (nothrow) RAMFSInode(fs)); -} - -InodeMetadata RAMFSInode::metadata() const -{ - MutexLocker locker(m_inode_lock, Mutex::Mode::Shared); - - return m_metadata; -} - -ErrorOr RAMFSInode::traverse_as_directory(Function(FileSystem::DirectoryEntryView const&)> callback) const -{ - MutexLocker locker(m_inode_lock, Mutex::Mode::Shared); - - if (!is_directory()) - return ENOTDIR; - - TRY(callback({ "."sv, identifier(), to_underlying(RAMBackedFileType::Directory) })); - if (m_root_directory_inode) { - TRY(callback({ ".."sv, identifier(), to_underlying(RAMBackedFileType::Directory) })); - } else if (auto parent = m_parent.strong_ref()) { - TRY(callback({ ".."sv, parent->identifier(), to_underlying(RAMBackedFileType::Directory) })); - } - - for (auto& child : m_children) { - TRY(callback({ child.name->view(), child.inode->identifier(), to_underlying(ram_backed_file_type_from_mode(child.inode->metadata().mode)) })); - } - return {}; -} - -ErrorOr RAMFSInode::replace_child(StringView name, Inode& new_child) -{ - MutexLocker locker(m_inode_lock); - VERIFY(is_directory()); - VERIFY(new_child.fsid() == fsid()); - - auto* child = find_child_by_name(name); - if (!child) - return ENOENT; - - auto old_child = child->inode; - child->inode = static_cast(new_child); - - old_child->did_delete_self(); - - // TODO: Emit a did_replace_child event. - - return {}; -} - -ErrorOr> RAMFSInode::DataBlock::create() -{ - auto data_block_buffer_vmobject = TRY(Memory::AnonymousVMObject::try_create_with_size(DataBlock::block_size, AllocationStrategy::AllocateNow)); - return TRY(adopt_nonnull_own_or_enomem(new (nothrow) DataBlock(move(data_block_buffer_vmobject)))); -} - -ErrorOr RAMFSInode::ensure_allocated_blocks(size_t offset, size_t io_size) -{ - VERIFY(m_inode_lock.is_locked()); - size_t block_start_index = offset / DataBlock::block_size; - size_t block_last_index = ((offset + io_size) / DataBlock::block_size) + (((offset + io_size) % DataBlock::block_size) == 0 ? 0 : 1); - VERIFY(block_start_index <= block_last_index); - - size_t original_size = m_blocks.size(); - Vector allocated_block_indices; - ArmedScopeGuard clean_allocated_blocks_on_failure([&] { - for (auto index : allocated_block_indices) - m_blocks[index].clear(); - MUST(m_blocks.try_resize(original_size)); - }); - - if (m_blocks.size() < (block_last_index)) - TRY(m_blocks.try_resize(block_last_index)); - - for (size_t block_index = block_start_index; block_index < block_last_index; block_index++) { - if (!m_blocks[block_index]) { - TRY(allocated_block_indices.try_append(block_index)); - m_blocks[block_index] = TRY(DataBlock::create()); - } - } - clean_allocated_blocks_on_failure.disarm(); - return {}; -} - -ErrorOr RAMFSInode::read_bytes_from_content_space(size_t offset, size_t io_size, UserOrKernelBuffer& buffer) const -{ - VERIFY(m_inode_lock.is_locked()); - VERIFY(m_metadata.size >= 0); - if (offset >= static_cast(m_metadata.size)) - return 0; - auto mapping_region = TRY(MM.allocate_kernel_region(DataBlock::block_size, "RAMFSInode Mapping Region"sv, Memory::Region::Access::Read, AllocationStrategy::Reserve)); - return const_cast(*this).do_io_on_content_space(*mapping_region, offset, io_size, buffer, false); -} - -ErrorOr RAMFSInode::read_bytes_locked(off_t offset, size_t size, UserOrKernelBuffer& buffer, OpenFileDescription*) const -{ - VERIFY(m_inode_lock.is_locked()); - VERIFY(!is_directory()); - return read_bytes_from_content_space(offset, size, buffer); -} - -ErrorOr RAMFSInode::write_bytes_to_content_space(size_t offset, size_t io_size, UserOrKernelBuffer const& buffer) -{ - VERIFY(m_inode_lock.is_locked()); - auto mapping_region = TRY(MM.allocate_kernel_region(DataBlock::block_size, "RAMFSInode Mapping Region"sv, Memory::Region::Access::Write, AllocationStrategy::Reserve)); - return do_io_on_content_space(*mapping_region, offset, io_size, const_cast(buffer), true); -} - -ErrorOr RAMFSInode::write_bytes_locked(off_t offset, size_t size, UserOrKernelBuffer const& buffer, OpenFileDescription*) -{ - VERIFY(m_inode_lock.is_locked()); - VERIFY(!is_directory()); - VERIFY(offset >= 0); - - TRY(ensure_allocated_blocks(offset, size)); - auto nwritten = TRY(write_bytes_to_content_space(offset, size, buffer)); - - off_t old_size = m_metadata.size; - off_t new_size = m_metadata.size; - if (static_cast(offset + size) > new_size) - new_size = offset + size; - - if (new_size > old_size) { - m_metadata.size = new_size; - set_metadata_dirty(true); - } - did_modify_contents(); - return nwritten; -} - -ErrorOr RAMFSInode::do_io_on_content_space(Memory::Region& mapping_region, size_t offset, size_t io_size, UserOrKernelBuffer& buffer, bool write) -{ - VERIFY(m_inode_lock.is_locked()); - size_t remaining_bytes = 0; - if (!write) { - // Note: For read operations, only perform read until the last byte. - // If we are beyond the last byte, return 0 to indicate EOF. - remaining_bytes = min(io_size, m_metadata.size - offset); - if (remaining_bytes == 0) - return 0; - } else { - remaining_bytes = io_size; - } - VERIFY(remaining_bytes != 0); - - UserOrKernelBuffer current_buffer = buffer.offset(0); - auto block_start_index = offset / DataBlock::block_size; - auto offset_in_block = offset % DataBlock::block_size; - u64 block_index = block_start_index; - size_t nio = 0; - while (remaining_bytes > 0) { - size_t current_io_size = min(DataBlock::block_size - offset_in_block, remaining_bytes); - auto& block = m_blocks[block_index]; - if (!block && !write) { - // Note: If the block does not exist then it's just a gap in the file, - // so the buffer should be placed with zeroes in that section. - TRY(current_buffer.memset(0, 0, current_io_size)); - remaining_bytes -= current_io_size; - current_buffer = current_buffer.offset(current_io_size); - nio += current_io_size; - block_index++; - // Note: Clear offset_in_block to zero to ensure that if we started from a middle of - // a block, then next writes are just going to happen from the start of each block until the end. - offset_in_block = 0; - continue; - } else if (!block) { - return Error::from_errno(EIO); - } - - NonnullLockRefPtr block_vmobject = block->vmobject(); - mapping_region.set_vmobject(block_vmobject); - mapping_region.remap(); - if (write) - TRY(current_buffer.read(mapping_region.vaddr().offset(offset_in_block).as_ptr(), 0, current_io_size)); - else - TRY(current_buffer.write(mapping_region.vaddr().offset(offset_in_block).as_ptr(), 0, current_io_size)); - current_buffer = current_buffer.offset(current_io_size); - nio += current_io_size; - remaining_bytes -= current_io_size; - block_index++; - // Note: Clear offset_in_block to zero to ensure that if we started from a middle of - // a block, then next writes are just going to happen from the start of each block until the end. - offset_in_block = 0; - } - VERIFY(nio <= io_size); - return nio; -} - -ErrorOr RAMFSInode::truncate_to_block_index(size_t block_index) -{ - VERIFY(m_inode_lock.is_locked()); - TRY(m_blocks.try_resize(block_index)); - return {}; -} - -ErrorOr> RAMFSInode::lookup(StringView name) -{ - MutexLocker locker(m_inode_lock, Mutex::Mode::Shared); - VERIFY(is_directory()); - - if (name == ".") - return *this; - if (name == "..") { - if (auto parent = m_parent.strong_ref()) - return *parent; - return ENOENT; - } - - auto* child = find_child_by_name(name); - if (!child) - return ENOENT; - return child->inode; -} - -RAMFSInode::Child* RAMFSInode::find_child_by_name(StringView name) -{ - for (auto& child : m_children) { - if (child.name->view() == name) - return &child; - } - return nullptr; -} - -ErrorOr RAMFSInode::flush_metadata() -{ - // We don't really have any metadata that could become dirty. - // The only reason we even call set_metadata_dirty() is - // to let the watchers know we have updates. Once that is - // switched to a different mechanism, we can stop ever marking - // our metadata as dirty at all. - set_metadata_dirty(false); - return {}; -} - -ErrorOr RAMFSInode::chmod(mode_t mode) -{ - MutexLocker locker(m_inode_lock); - - m_metadata.mode = mode; - set_metadata_dirty(true); - return {}; -} - -ErrorOr RAMFSInode::chown(UserID uid, GroupID gid) -{ - MutexLocker locker(m_inode_lock); - - m_metadata.uid = uid; - m_metadata.gid = gid; - set_metadata_dirty(true); - return {}; -} - -ErrorOr> RAMFSInode::create_child(StringView name, mode_t mode, dev_t dev, UserID uid, GroupID gid) -{ - MutexLocker locker(m_inode_lock); - auto now = kgettimeofday(); - - InodeMetadata metadata; - metadata.mode = mode; - metadata.uid = uid; - metadata.gid = gid; - metadata.atime = now; - metadata.ctime = now; - metadata.mtime = now; - metadata.major_device = major_from_encoded_device(dev); - metadata.minor_device = minor_from_encoded_device(dev); - - auto child = TRY(RAMFSInode::try_create(fs(), metadata, *this)); - TRY(add_child(*child, name, mode)); - return child; -} - -ErrorOr RAMFSInode::add_child(Inode& child, StringView name, mode_t) -{ - VERIFY(is_directory()); - VERIFY(child.fsid() == fsid()); - - if (name.length() > NAME_MAX) - return ENAMETOOLONG; - - MutexLocker locker(m_inode_lock); - for (auto const& existing_child : m_children) { - if (existing_child.name->view() == name) - return EEXIST; - } - - auto name_kstring = TRY(KString::try_create(name)); - // Balanced by `delete` in remove_child() - - auto* child_entry = new (nothrow) Child { move(name_kstring), static_cast(child) }; - if (!child_entry) - return ENOMEM; - - m_children.append(*child_entry); - did_add_child(child.identifier(), name); - return {}; -} - -ErrorOr RAMFSInode::remove_child(StringView name) -{ - MutexLocker locker(m_inode_lock); - VERIFY(is_directory()); - - if (name == "." || name == "..") - return {}; - - auto* child = find_child_by_name(name); - if (!child) - return ENOENT; - - auto child_id = child->inode->identifier(); - child->inode->did_delete_self(); - m_children.remove(*child); - did_remove_child(child_id, name); - // Balanced by `new` in add_child() - delete child; - return {}; -} - -ErrorOr RAMFSInode::truncate_locked(u64 size) -{ - VERIFY(m_inode_lock.is_locked()); - VERIFY(!is_directory()); - - u64 block_index = size / DataBlock::block_size + ((size % DataBlock::block_size == 0) ? 0 : 1); - TRY(truncate_to_block_index(block_index)); - - u64 last_possible_block_index = size / DataBlock::block_size; - if ((size % DataBlock::block_size != 0) && m_blocks[last_possible_block_index]) { - auto mapping_region = TRY(MM.allocate_kernel_region(DataBlock::block_size, "RAMFSInode Mapping Region"sv, Memory::Region::Access::Write, AllocationStrategy::Reserve)); - VERIFY(m_blocks[last_possible_block_index]); - NonnullLockRefPtr block_vmobject = m_blocks[last_possible_block_index]->vmobject(); - mapping_region->set_vmobject(block_vmobject); - mapping_region->remap(); - memset(mapping_region->vaddr().offset(size % DataBlock::block_size).as_ptr(), 0, DataBlock::block_size - (size % DataBlock::block_size)); - } - m_metadata.size = size; - set_metadata_dirty(true); - did_modify_contents(); - return {}; -} - -ErrorOr RAMFSInode::update_timestamps(Optional atime, Optional ctime, Optional mtime) -{ - MutexLocker locker(m_inode_lock); - - if (atime.has_value()) - m_metadata.atime = atime.value(); - if (ctime.has_value()) - m_metadata.ctime = ctime.value(); - if (mtime.has_value()) - m_metadata.mtime = mtime.value(); - set_metadata_dirty(true); - return {}; -} - -} diff --git a/Kernel/FileSystem/RAMFS/Inode.h b/Kernel/FileSystem/RAMFS/Inode.h deleted file mode 100644 index 3c24369cff5..00000000000 --- a/Kernel/FileSystem/RAMFS/Inode.h +++ /dev/null @@ -1,95 +0,0 @@ -/* - * Copyright (c) 2019-2020, Sergey Bugaev - * Copyright (c) 2022-2023, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include - -namespace Kernel { - -class RAMFSInode final : public Inode { - friend class RAMFS; - -public: - virtual ~RAMFSInode() override; - - RAMFS& fs() { return static_cast(Inode::fs()); } - RAMFS const& fs() const { return static_cast(Inode::fs()); } - - // ^Inode - virtual InodeMetadata metadata() const override; - virtual ErrorOr traverse_as_directory(Function(FileSystem::DirectoryEntryView const&)>) const override; - virtual ErrorOr> lookup(StringView name) override; - virtual ErrorOr flush_metadata() override; - virtual ErrorOr> create_child(StringView name, mode_t, dev_t, UserID, GroupID) override; - virtual ErrorOr add_child(Inode&, StringView name, mode_t) override; - virtual ErrorOr remove_child(StringView name) override; - virtual ErrorOr replace_child(StringView name, Inode& child) override; - virtual ErrorOr chmod(mode_t) override; - virtual ErrorOr chown(UserID, GroupID) override; - virtual ErrorOr truncate_locked(u64) override; - virtual ErrorOr update_timestamps(Optional atime, Optional ctime, Optional mtime) override; - -private: - RAMFSInode(RAMFS& fs, InodeMetadata const& metadata, LockWeakPtr parent); - explicit RAMFSInode(RAMFS& fs); - static ErrorOr> try_create(RAMFS&, InodeMetadata const& metadata, LockWeakPtr parent); - static ErrorOr> try_create_root(RAMFS&); - - // ^Inode - virtual ErrorOr read_bytes_locked(off_t, size_t, UserOrKernelBuffer& buffer, OpenFileDescription*) const override; - virtual ErrorOr write_bytes_locked(off_t, size_t, UserOrKernelBuffer const& buffer, OpenFileDescription*) override; - - ErrorOr do_io_on_content_space(Memory::Region& mapping_region, size_t offset, size_t io_size, UserOrKernelBuffer& buffer, bool write); - - struct Child { - NonnullOwnPtr name; - NonnullRefPtr inode; - IntrusiveListNode list_node {}; - using List = IntrusiveList<&Child::list_node>; - }; - - Child* find_child_by_name(StringView); - - InodeMetadata m_metadata; - LockWeakPtr m_parent; - - ErrorOr ensure_allocated_blocks(size_t offset, size_t io_size); - ErrorOr truncate_to_block_index(size_t block_index); - ErrorOr read_bytes_from_content_space(size_t offset, size_t io_size, UserOrKernelBuffer& buffer) const; - ErrorOr write_bytes_to_content_space(size_t offset, size_t io_size, UserOrKernelBuffer const& buffer); - - struct DataBlock { - public: - using List = Vector>; - - static ErrorOr> create(); - - constexpr static size_t block_size = 128 * KiB; - - Memory::AnonymousVMObject& vmobject() { return *m_content_buffer_vmobject; } - Memory::AnonymousVMObject const& vmobject() const { return *m_content_buffer_vmobject; } - - private: - explicit DataBlock(NonnullLockRefPtr content_buffer_vmobject) - : m_content_buffer_vmobject(move(content_buffer_vmobject)) - { - } - - NonnullLockRefPtr m_content_buffer_vmobject; - }; - - bool const m_root_directory_inode { false }; - - DataBlock::List m_blocks; - Child::List m_children; -}; - -} diff --git a/Kernel/FileSystem/SysFS/Component.cpp b/Kernel/FileSystem/SysFS/Component.cpp deleted file mode 100644 index 5df5eb18a3d..00000000000 --- a/Kernel/FileSystem/SysFS/Component.cpp +++ /dev/null @@ -1,159 +0,0 @@ -/* - * Copyright (c) 2021-2024, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include -#include - -namespace Kernel { - -static Spinlock s_index_lock {}; -static InodeIndex s_next_inode_index { 0 }; - -static size_t allocate_inode_index() -{ - SpinlockLocker lock(s_index_lock); - s_next_inode_index = s_next_inode_index.value() + 1; - VERIFY(s_next_inode_index > 0); - return s_next_inode_index.value(); -} - -SysFSComponent::SysFSComponent(SysFSDirectory const& parent_directory) - : m_parent_directory(parent_directory) - , m_component_index(allocate_inode_index()) -{ -} - -SysFSComponent::SysFSComponent() - : m_component_index(allocate_inode_index()) -{ -} - -ErrorOr> SysFSComponent::relative_path(NonnullOwnPtr name, size_t current_hop) const -{ - if (current_hop >= 128) - return Error::from_errno(ELOOP); - if (!m_parent_directory) - return name; - auto joined_name = TRY(KLexicalPath::try_join(m_parent_directory->name(), name->view())); - return m_parent_directory->relative_path(move(joined_name), current_hop + 1); -} - -ErrorOr SysFSComponent::relative_path_hops_count_from_mountpoint(size_t current_hop) const -{ - if (current_hop >= 128) - return Error::from_errno(ELOOP); - if (!m_parent_directory) - return current_hop; - return m_parent_directory->relative_path_hops_count_from_mountpoint(current_hop + 1); -} - -mode_t SysFSComponent::permissions() const -{ - return S_IRUSR | S_IRGRP | S_IROTH; -} - -ErrorOr SysFSSymbolicLink::read_bytes(off_t offset, size_t count, UserOrKernelBuffer& buffer, OpenFileDescription*) const -{ - auto blob = TRY(try_to_generate_buffer()); - - if ((size_t)offset >= blob->size()) - return 0; - - ssize_t nread = min(static_cast(blob->size() - offset), static_cast(count)); - TRY(buffer.write(blob->data() + offset, nread)); - return nread; -} - -ErrorOr> SysFSSymbolicLink::try_to_generate_buffer() const -{ - auto return_path_to_mount_point = TRY(try_generate_return_path_to_mount_point()); - auto pointed_component_base_name = MUST(KString::try_create(m_pointed_component->name())); - auto pointed_component_relative_path = MUST(m_pointed_component->relative_path(move(pointed_component_base_name), 0)); - auto full_return_and_target_path = TRY(KString::formatted("{}{}", return_path_to_mount_point->view(), pointed_component_relative_path->view())); - return KBuffer::try_create_with_bytes("SysFSSymbolicLink"sv, full_return_and_target_path->view().bytes()); -} - -static ErrorOr> generate_return_path_to_mount_point(NonnullOwnPtr current_path, size_t remaining_hop) -{ - if (remaining_hop == 0) - return current_path; - auto new_path = TRY(KString::formatted("../{}"sv, current_path->view())); - remaining_hop--; - return generate_return_path_to_mount_point(move(new_path), remaining_hop); -} - -ErrorOr> SysFSSymbolicLink::try_generate_return_path_to_mount_point() const -{ - VERIFY(m_parent_directory); - auto hops_from_mountpoint = TRY(m_parent_directory->relative_path_hops_count_from_mountpoint()); - if (hops_from_mountpoint == 0) - return KString::try_create("./"sv); - auto start_return_path = TRY(KString::try_create("./"sv)); - return generate_return_path_to_mount_point(move(start_return_path), hops_from_mountpoint); -} - -SysFSSymbolicLink::SysFSSymbolicLink(SysFSDirectory const& parent_directory, SysFSComponent const& pointed_component) - : SysFSComponent(parent_directory) - , m_pointed_component(pointed_component) -{ -} - -ErrorOr SysFSDirectory::traverse_as_directory(FileSystemID fsid, Function(FileSystem::DirectoryEntryView const&)> callback) const -{ - TRY(callback({ "."sv, { fsid, component_index() }, to_underlying(RAMBackedFileType::Directory) })); - if (is_root_directory()) { - TRY(callback({ ".."sv, { fsid, component_index() }, to_underlying(RAMBackedFileType::Directory) })); - } else { - VERIFY(m_parent_directory); - TRY(callback({ ".."sv, { fsid, m_parent_directory->component_index() }, to_underlying(RAMBackedFileType::Directory) })); - } - - return m_child_components.with([&](auto& list) -> ErrorOr { - for (auto& child_component : list) { - InodeIdentifier identifier = { fsid, child_component.component_index() }; - TRY(callback({ child_component.name(), identifier, to_underlying(child_component.type()) })); - } - return {}; - }); -} - -RefPtr SysFSDirectory::lookup(StringView name) -{ - return m_child_components.with([&](auto& list) -> RefPtr { - for (auto& child_component : list) { - if (child_component.name() == name) { - return child_component; - } - } - return nullptr; - }); -} - -SysFSDirectory::SysFSDirectory(SysFSDirectory const& parent_directory) - : SysFSComponent(parent_directory) -{ -} - -ErrorOr> SysFSDirectory::to_inode(SysFS const& sysfs_instance) const -{ - return TRY(SysFSDirectoryInode::try_create(sysfs_instance, *this)); -} - -ErrorOr> SysFSSymbolicLink::to_inode(SysFS const& sysfs_instance) const -{ - return TRY(SysFSLinkInode::try_create(sysfs_instance, *this)); -} - -ErrorOr> SysFSComponent::to_inode(SysFS const& sysfs_instance) const -{ - return SysFSInode::try_create(sysfs_instance, *this); -} - -} diff --git a/Kernel/FileSystem/SysFS/Component.h b/Kernel/FileSystem/SysFS/Component.h deleted file mode 100644 index c3b0c212f4a..00000000000 --- a/Kernel/FileSystem/SysFS/Component.h +++ /dev/null @@ -1,100 +0,0 @@ -/* - * Copyright (c) 2021-2024, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace Kernel { - -struct SysFSInodeData : public OpenFileDescriptionData { - OwnPtr buffer; -}; - -class SysFSDirectory; -class SysFSComponent : public AtomicRefCounted { - friend class SysFSDirectory; - -public: - // NOTE: It is safe to assume that the regular file type is largely - // the most used file type in the SysFS filesystem. - virtual RAMBackedFileType type() const { return RAMBackedFileType::Regular; } - - virtual StringView name() const = 0; - virtual ErrorOr read_bytes(off_t, size_t, UserOrKernelBuffer&, OpenFileDescription*) const { return Error::from_errno(ENOTIMPL); } - virtual ErrorOr traverse_as_directory(FileSystemID, Function(FileSystem::DirectoryEntryView const&)>) const { VERIFY_NOT_REACHED(); } - virtual RefPtr lookup(StringView) { VERIFY_NOT_REACHED(); } - virtual mode_t permissions() const; - virtual ErrorOr truncate(u64) { return EPERM; } - virtual size_t size() const { return 0; } - virtual ErrorOr write_bytes(off_t, size_t, UserOrKernelBuffer const&, OpenFileDescription*) { return EROFS; } - virtual ErrorOr refresh_data(OpenFileDescription&) const { return {}; } - - virtual ErrorOr> to_inode(SysFS const&) const; - - InodeIndex component_index() const { return m_component_index; } - - virtual ~SysFSComponent() = default; - - ErrorOr> relative_path(NonnullOwnPtr, size_t current_hop = 0) const; - ErrorOr relative_path_hops_count_from_mountpoint(size_t current_hop = 0) const; - -protected: - explicit SysFSComponent(SysFSDirectory const& parent_directory); - SysFSComponent(); - - RefPtr const m_parent_directory; - - IntrusiveListNode> m_list_node; - -private: - InodeIndex m_component_index {}; -}; - -class SysFSSymbolicLink : public SysFSComponent { -public: - virtual RAMBackedFileType type() const override final { return RAMBackedFileType::Link; } - virtual ErrorOr read_bytes(off_t, size_t, UserOrKernelBuffer&, OpenFileDescription*) const override final; - virtual ErrorOr> to_inode(SysFS const& sysfs_instance) const override final; - -protected: - ErrorOr> try_generate_return_path_to_mount_point() const; - ErrorOr> try_to_generate_buffer() const; - - explicit SysFSSymbolicLink(SysFSDirectory const& parent_directory, SysFSComponent const& pointed_component); - - NonnullRefPtr const m_pointed_component; -}; - -class SysFSDirectory : public SysFSComponent { -public: - virtual RAMBackedFileType type() const override final { return RAMBackedFileType::Directory; } - virtual ErrorOr traverse_as_directory(FileSystemID, Function(FileSystem::DirectoryEntryView const&)>) const override final; - virtual RefPtr lookup(StringView name) override final; - - virtual ErrorOr> to_inode(SysFS const& sysfs_instance) const override final; - - using ChildList = SpinlockProtected, LockRank::None>; - -protected: - virtual bool is_root_directory() const { return false; } - - SysFSDirectory() {}; - explicit SysFSDirectory(SysFSDirectory const& parent_directory); - ChildList m_child_components {}; -}; - -} diff --git a/Kernel/FileSystem/SysFS/DirectoryInode.cpp b/Kernel/FileSystem/SysFS/DirectoryInode.cpp deleted file mode 100644 index 6f270b4bf16..00000000000 --- a/Kernel/FileSystem/SysFS/DirectoryInode.cpp +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright (c) 2021, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include - -namespace Kernel { - -ErrorOr> SysFSDirectoryInode::try_create(SysFS const& sysfs, SysFSComponent const& component) -{ - return adopt_nonnull_ref_or_enomem(new (nothrow) SysFSDirectoryInode(sysfs, component)); -} - -SysFSDirectoryInode::SysFSDirectoryInode(SysFS const& fs, SysFSComponent const& component) - : SysFSInode(fs, component) -{ -} - -SysFSDirectoryInode::~SysFSDirectoryInode() = default; - -InodeMetadata SysFSDirectoryInode::metadata() const -{ - // NOTE: No locking required as m_associated_component or its component index will never change during our lifetime. - InodeMetadata metadata; - metadata.inode = { fsid(), m_associated_component->component_index() }; - metadata.mode = S_IFDIR | 0755; - metadata.uid = 0; - metadata.gid = 0; - metadata.size = 0; - metadata.mtime = TimeManagement::boot_time(); - return metadata; -} - -ErrorOr SysFSDirectoryInode::traverse_as_directory(Function(FileSystem::DirectoryEntryView const&)> callback) const -{ - return m_associated_component->traverse_as_directory(fs().fsid(), move(callback)); -} - -ErrorOr> SysFSDirectoryInode::lookup(StringView name) -{ - auto component = m_associated_component->lookup(name); - if (!component) - return ENOENT; - return TRY(component->to_inode(fs())); -} - -} diff --git a/Kernel/FileSystem/SysFS/DirectoryInode.h b/Kernel/FileSystem/SysFS/DirectoryInode.h deleted file mode 100644 index f5168e1c0a8..00000000000 --- a/Kernel/FileSystem/SysFS/DirectoryInode.h +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright (c) 2021, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include - -namespace Kernel { - -class SysFSDirectoryInode : public SysFSInode { - friend class SysFS; - -public: - static ErrorOr> try_create(SysFS const&, SysFSComponent const&); - virtual ~SysFSDirectoryInode() override; - - SysFS& fs() { return static_cast(Inode::fs()); } - SysFS const& fs() const { return static_cast(Inode::fs()); } - -protected: - SysFSDirectoryInode(SysFS const&, SysFSComponent const&); - // ^Inode - virtual InodeMetadata metadata() const override; - virtual ErrorOr traverse_as_directory(Function(FileSystem::DirectoryEntryView const&)>) const override; - virtual ErrorOr> lookup(StringView name) override; -}; - -} diff --git a/Kernel/FileSystem/SysFS/FileSystem.cpp b/Kernel/FileSystem/SysFS/FileSystem.cpp deleted file mode 100644 index 0f01714c760..00000000000 --- a/Kernel/FileSystem/SysFS/FileSystem.cpp +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright (c) 2021, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include - -namespace Kernel { - -ErrorOr> SysFS::try_create(ReadonlyBytes) -{ - return TRY(adopt_nonnull_ref_or_enomem(new (nothrow) SysFS)); -} - -SysFS::SysFS() = default; -SysFS::~SysFS() = default; - -ErrorOr SysFS::initialize() -{ - m_root_inode = TRY(SysFSComponentRegistry::the().root_directory().to_inode(*this)); - return {}; -} - -u8 SysFS::internal_file_type_to_directory_entry_type(DirectoryEntryView const& entry) const -{ - return ram_backed_file_type_to_directory_entry_type(entry); -} - -Inode& SysFS::root_inode() -{ - return *m_root_inode; -} - -} diff --git a/Kernel/FileSystem/SysFS/FileSystem.h b/Kernel/FileSystem/SysFS/FileSystem.h deleted file mode 100644 index 698249308cd..00000000000 --- a/Kernel/FileSystem/SysFS/FileSystem.h +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright (c) 2021, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include - -namespace Kernel { - -class SysFS final : public FileSystem { - friend class SysFSInode; - friend class SysFSDirectoryInode; - -public: - virtual ~SysFS() override; - static ErrorOr> try_create(ReadonlyBytes); - - virtual ErrorOr initialize() override; - virtual StringView class_name() const override { return "SysFS"sv; } - - virtual Inode& root_inode() override; - -private: - virtual u8 internal_file_type_to_directory_entry_type(DirectoryEntryView const& entry) const override; - - SysFS(); - - RefPtr m_root_inode; -}; - -} diff --git a/Kernel/FileSystem/SysFS/Inode.cpp b/Kernel/FileSystem/SysFS/Inode.cpp deleted file mode 100644 index a19f668f13d..00000000000 --- a/Kernel/FileSystem/SysFS/Inode.cpp +++ /dev/null @@ -1,119 +0,0 @@ -/* - * Copyright (c) 2021, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include - -namespace Kernel { - -ErrorOr> SysFSInode::try_create(SysFS const& fs, SysFSComponent const& component) -{ - return adopt_nonnull_ref_or_enomem(new (nothrow) SysFSInode(fs, component)); -} - -SysFSInode::SysFSInode(SysFS const& fs, SysFSComponent const& component) - : Inode(const_cast(fs), component.component_index()) - , m_associated_component(component) -{ -} - -void SysFSInode::did_seek(OpenFileDescription& description, off_t new_offset) -{ - if (new_offset != 0) - return; - auto result = m_associated_component->refresh_data(description); - if (result.is_error()) { - // Subsequent calls to read will return EIO! - dbgln("SysFS: Could not refresh contents: {}", result.error()); - } -} - -ErrorOr SysFSInode::attach(OpenFileDescription& description) -{ - return m_associated_component->refresh_data(description); -} - -ErrorOr SysFSInode::read_bytes_locked(off_t offset, size_t count, UserOrKernelBuffer& buffer, OpenFileDescription* fd) const -{ - return m_associated_component->read_bytes(offset, count, buffer, fd); -} - -ErrorOr SysFSInode::traverse_as_directory(Function(FileSystem::DirectoryEntryView const&)>) const -{ - VERIFY_NOT_REACHED(); -} - -ErrorOr> SysFSInode::lookup(StringView) -{ - VERIFY_NOT_REACHED(); -} - -InodeMetadata SysFSInode::metadata() const -{ - // NOTE: No locking required as m_associated_component or its component index will never change during our lifetime. - InodeMetadata metadata; - metadata.inode = { fsid(), m_associated_component->component_index() }; - metadata.mode = S_IFREG | m_associated_component->permissions(); - metadata.uid = 0; - metadata.gid = 0; - metadata.size = m_associated_component->size(); - metadata.mtime = TimeManagement::boot_time(); - return metadata; -} - -ErrorOr SysFSInode::flush_metadata() -{ - return {}; -} - -ErrorOr SysFSInode::write_bytes_locked(off_t offset, size_t count, UserOrKernelBuffer const& buffer, OpenFileDescription* fd) -{ - return m_associated_component->write_bytes(offset, count, buffer, fd); -} - -ErrorOr> SysFSInode::create_child(StringView, mode_t, dev_t, UserID, GroupID) -{ - return EROFS; -} - -ErrorOr SysFSInode::add_child(Inode&, StringView, mode_t) -{ - return EROFS; -} - -ErrorOr SysFSInode::remove_child(StringView) -{ - return EROFS; -} - -ErrorOr SysFSInode::replace_child(StringView, Inode&) -{ - return EROFS; -} - -ErrorOr SysFSInode::chmod(mode_t) -{ - return EPERM; -} - -ErrorOr SysFSInode::chown(UserID, GroupID) -{ - return EPERM; -} - -ErrorOr SysFSInode::truncate_locked(u64 size) -{ - VERIFY(m_inode_lock.is_locked()); - return m_associated_component->truncate(size); -} - -ErrorOr SysFSInode::update_timestamps(Optional, Optional, Optional) -{ - return {}; -} - -} diff --git a/Kernel/FileSystem/SysFS/Inode.h b/Kernel/FileSystem/SysFS/Inode.h deleted file mode 100644 index edcecdac0e7..00000000000 --- a/Kernel/FileSystem/SysFS/Inode.h +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright (c) 2021, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include - -namespace Kernel { - -class SysFSInode : public Inode { - friend class SysFS; - friend class SysFSDirectoryInode; - -public: - static ErrorOr> try_create(SysFS const&, SysFSComponent const&); - StringView name() const { return m_associated_component->name(); } - -protected: - SysFSInode(SysFS const&, SysFSComponent const&); - virtual ErrorOr read_bytes_locked(off_t, size_t, UserOrKernelBuffer& buffer, OpenFileDescription*) const override; - virtual ErrorOr traverse_as_directory(Function(FileSystem::DirectoryEntryView const&)>) const override; - virtual ErrorOr> lookup(StringView name) override; - virtual ErrorOr flush_metadata() override; - virtual InodeMetadata metadata() const override; - virtual ErrorOr write_bytes_locked(off_t, size_t, UserOrKernelBuffer const&, OpenFileDescription*) override; - virtual ErrorOr> create_child(StringView name, mode_t, dev_t, UserID, GroupID) override; - virtual ErrorOr add_child(Inode&, StringView name, mode_t) override; - virtual ErrorOr remove_child(StringView name) override; - virtual ErrorOr replace_child(StringView name, Inode& child) override; - virtual ErrorOr chmod(mode_t) override; - virtual ErrorOr chown(UserID, GroupID) override; - virtual ErrorOr truncate_locked(u64) override; - virtual ErrorOr update_timestamps(Optional atime, Optional ctime, Optional mtime) override; - - virtual ErrorOr attach(OpenFileDescription& description) override final; - virtual void did_seek(OpenFileDescription&, off_t) override final; - - NonnullRefPtr const m_associated_component; -}; - -} diff --git a/Kernel/FileSystem/SysFS/LinkInode.cpp b/Kernel/FileSystem/SysFS/LinkInode.cpp deleted file mode 100644 index ac065bb2883..00000000000 --- a/Kernel/FileSystem/SysFS/LinkInode.cpp +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright (c) 2021, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include - -namespace Kernel { - -ErrorOr> SysFSLinkInode::try_create(SysFS const& sysfs, SysFSComponent const& component) -{ - return adopt_nonnull_ref_or_enomem(new (nothrow) SysFSLinkInode(sysfs, component)); -} - -SysFSLinkInode::SysFSLinkInode(SysFS const& fs, SysFSComponent const& component) - : SysFSInode(fs, component) -{ -} - -SysFSLinkInode::~SysFSLinkInode() = default; - -InodeMetadata SysFSLinkInode::metadata() const -{ - // NOTE: No locking required as m_associated_component or its component index will never change during our lifetime. - InodeMetadata metadata; - metadata.inode = { fsid(), m_associated_component->component_index() }; - metadata.mode = S_IFLNK | S_IRUSR | S_IRGRP | S_IROTH | S_IXOTH; - metadata.uid = 0; - metadata.gid = 0; - metadata.size = 0; - metadata.mtime = TimeManagement::boot_time(); - return metadata; -} - -} diff --git a/Kernel/FileSystem/SysFS/LinkInode.h b/Kernel/FileSystem/SysFS/LinkInode.h deleted file mode 100644 index 835bffe45ec..00000000000 --- a/Kernel/FileSystem/SysFS/LinkInode.h +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright (c) 2021, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include - -namespace Kernel { - -class SysFSLinkInode : public SysFSInode { - friend class SysFS; - -public: - static ErrorOr> try_create(SysFS const&, SysFSComponent const&); - virtual ~SysFSLinkInode() override; - -protected: - SysFSLinkInode(SysFS const&, SysFSComponent const&); - // ^Inode - virtual InodeMetadata metadata() const override; -}; - -} diff --git a/Kernel/FileSystem/SysFS/Registry.cpp b/Kernel/FileSystem/SysFS/Registry.cpp deleted file mode 100644 index 8f93c257271..00000000000 --- a/Kernel/FileSystem/SysFS/Registry.cpp +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright (c) 2022, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include - -namespace Kernel { - -static Singleton s_the; - -SysFSComponentRegistry& SysFSComponentRegistry::the() -{ - return *s_the; -} - -UNMAP_AFTER_INIT void SysFSComponentRegistry::initialize() -{ - VERIFY(!s_the.is_initialized()); - s_the.ensure_instance(); -} - -UNMAP_AFTER_INIT SysFSComponentRegistry::SysFSComponentRegistry() - : m_root_directory(SysFSRootDirectory::create()) -{ -} - -UNMAP_AFTER_INIT void SysFSComponentRegistry::register_new_component(SysFSComponent& component) -{ - SpinlockLocker locker(m_root_directory_lock); - MUST(m_root_directory->m_child_components.with([&](auto& list) -> ErrorOr { - list.append(component); - return {}; - })); -} - -SysFSBusDirectory& SysFSComponentRegistry::buses_directory() -{ - return *m_root_directory->m_buses_directory; -} - -void SysFSComponentRegistry::register_new_bus_directory(SysFSDirectory& new_bus_directory) -{ - MUST(m_root_directory->m_buses_directory->m_child_components.with([&](auto& list) -> ErrorOr { - list.append(new_bus_directory); - return {}; - })); -} - -} diff --git a/Kernel/FileSystem/SysFS/Registry.h b/Kernel/FileSystem/SysFS/Registry.h deleted file mode 100644 index f3a4ca68bd1..00000000000 --- a/Kernel/FileSystem/SysFS/Registry.h +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright (c) 2022, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include -#include - -namespace Kernel { - -class SysFSComponentRegistry { - -public: - static SysFSComponentRegistry& the(); - - static void initialize(); - - SysFSComponentRegistry(); - void register_new_component(SysFSComponent&); - - SysFSDirectory& root_directory() { return m_root_directory; } - - void register_new_bus_directory(SysFSDirectory&); - SysFSBusDirectory& buses_directory(); - -private: - NonnullRefPtr const m_root_directory; - Spinlock m_root_directory_lock {}; -}; - -} diff --git a/Kernel/FileSystem/SysFS/RootDirectory.cpp b/Kernel/FileSystem/SysFS/RootDirectory.cpp deleted file mode 100644 index f5ae02c33b6..00000000000 --- a/Kernel/FileSystem/SysFS/RootDirectory.cpp +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright (c) 2022, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include -#include -#include - -namespace Kernel { - -NonnullRefPtr SysFSRootDirectory::create() -{ - return adopt_ref(*new (nothrow) SysFSRootDirectory); -} - -SysFSRootDirectory::SysFSRootDirectory() - : m_buses_directory(SysFSBusDirectory::must_create(*this)) -{ - auto device_identifiers_directory = SysFSDeviceIdentifiersDirectory::must_create(*this); - auto devices_directory = SysFSDevicesDirectory::must_create(*this); - auto global_kernel_stats_directory = SysFSGlobalKernelStatsDirectory::must_create(*this); - MUST(m_child_components.with([&](auto& list) -> ErrorOr { - list.append(m_buses_directory); - list.append(device_identifiers_directory); - list.append(devices_directory); - list.append(global_kernel_stats_directory); - return {}; - })); -} - -} diff --git a/Kernel/FileSystem/SysFS/RootDirectory.h b/Kernel/FileSystem/SysFS/RootDirectory.h deleted file mode 100644 index e09054cdf45..00000000000 --- a/Kernel/FileSystem/SysFS/RootDirectory.h +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright (c) 2022, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include - -namespace Kernel { - -class SysFSRootDirectory final : public SysFSDirectory { - friend class SysFSComponentRegistry; - -public: - virtual StringView name() const override { return "."sv; } - static NonnullRefPtr create(); - -private: - virtual bool is_root_directory() const override final { return true; } - SysFSRootDirectory(); - NonnullRefPtr const m_buses_directory; -}; - -} diff --git a/Kernel/FileSystem/SysFS/Subsystems/Bus/Directory.cpp b/Kernel/FileSystem/SysFS/Subsystems/Bus/Directory.cpp deleted file mode 100644 index f9d1a2bf63f..00000000000 --- a/Kernel/FileSystem/SysFS/Subsystems/Bus/Directory.cpp +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright (c) 2022, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include - -namespace Kernel { - -UNMAP_AFTER_INIT NonnullRefPtr SysFSBusDirectory::must_create(SysFSRootDirectory const& parent_directory) -{ - auto directory = adopt_ref(*new (nothrow) SysFSBusDirectory(parent_directory)); - return directory; -} - -UNMAP_AFTER_INIT SysFSBusDirectory::SysFSBusDirectory(SysFSRootDirectory const& parent_directory) - : SysFSDirectory(parent_directory) -{ -} - -} diff --git a/Kernel/FileSystem/SysFS/Subsystems/Bus/Directory.h b/Kernel/FileSystem/SysFS/Subsystems/Bus/Directory.h deleted file mode 100644 index c8ccb14c74a..00000000000 --- a/Kernel/FileSystem/SysFS/Subsystems/Bus/Directory.h +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright (c) 2022, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include - -namespace Kernel { - -class SysFSBusDirectory : public SysFSDirectory { - friend class SysFSComponentRegistry; - -public: - virtual StringView name() const override { return "bus"sv; } - static NonnullRefPtr must_create(SysFSRootDirectory const&); - -private: - explicit SysFSBusDirectory(SysFSRootDirectory const&); -}; - -} diff --git a/Kernel/FileSystem/SysFS/Subsystems/Bus/PCI/BusDirectory.cpp b/Kernel/FileSystem/SysFS/Subsystems/Bus/PCI/BusDirectory.cpp deleted file mode 100644 index 5fc3eaf25f5..00000000000 --- a/Kernel/FileSystem/SysFS/Subsystems/Bus/PCI/BusDirectory.cpp +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright (c) 2021, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include -#include - -namespace Kernel { - -UNMAP_AFTER_INIT void PCIBusSysFSDirectory::initialize() -{ - auto pci_directory = adopt_ref(*new (nothrow) PCIBusSysFSDirectory()); - SysFSComponentRegistry::the().register_new_bus_directory(pci_directory); -} - -UNMAP_AFTER_INIT PCIBusSysFSDirectory::PCIBusSysFSDirectory() - : SysFSDirectory(SysFSComponentRegistry::the().buses_directory()) -{ - MUST(m_child_components.with([&](auto& list) -> ErrorOr { - MUST(PCI::enumerate([&](PCI::DeviceIdentifier const& device_identifier) { - auto pci_device = PCIDeviceSysFSDirectory::create(*this, device_identifier); - list.append(pci_device); - })); - return {}; - })); -} - -} diff --git a/Kernel/FileSystem/SysFS/Subsystems/Bus/PCI/BusDirectory.h b/Kernel/FileSystem/SysFS/Subsystems/Bus/PCI/BusDirectory.h deleted file mode 100644 index 02df68b6bf1..00000000000 --- a/Kernel/FileSystem/SysFS/Subsystems/Bus/PCI/BusDirectory.h +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright (c) 2021, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include - -namespace Kernel { - -class PCIBusSysFSDirectory final : public SysFSDirectory { -public: - static void initialize(); - virtual StringView name() const override { return "pci"sv; } - -private: - PCIBusSysFSDirectory(); -}; - -} diff --git a/Kernel/FileSystem/SysFS/Subsystems/Bus/PCI/DeviceAttribute.cpp b/Kernel/FileSystem/SysFS/Subsystems/Bus/PCI/DeviceAttribute.cpp deleted file mode 100644 index 87427dea58d..00000000000 --- a/Kernel/FileSystem/SysFS/Subsystems/Bus/PCI/DeviceAttribute.cpp +++ /dev/null @@ -1,95 +0,0 @@ -/* - * Copyright (c) 2021, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include - -namespace Kernel { - -StringView PCIDeviceAttributeSysFSComponent::name() const -{ - switch (m_offset) { - case PCI::RegisterOffset::VENDOR_ID: - return "vendor"sv; - case PCI::RegisterOffset::DEVICE_ID: - return "device_id"sv; - case PCI::RegisterOffset::CLASS: - return "class"sv; - case PCI::RegisterOffset::SUBCLASS: - return "subclass"sv; - case PCI::RegisterOffset::REVISION_ID: - return "revision"sv; - case PCI::RegisterOffset::PROG_IF: - return "progif"sv; - case PCI::RegisterOffset::SUBSYSTEM_VENDOR_ID: - return "subsystem_vendor"sv; - case PCI::RegisterOffset::SUBSYSTEM_ID: - return "subsystem_id"sv; - case PCI::RegisterOffset::BAR0: - return "bar0"sv; - case PCI::RegisterOffset::BAR1: - return "bar1"sv; - case PCI::RegisterOffset::BAR2: - return "bar2"sv; - case PCI::RegisterOffset::BAR3: - return "bar3"sv; - case PCI::RegisterOffset::BAR4: - return "bar4"sv; - case PCI::RegisterOffset::BAR5: - return "bar5"sv; - default: - VERIFY_NOT_REACHED(); - } -} - -NonnullRefPtr PCIDeviceAttributeSysFSComponent::create(PCIDeviceSysFSDirectory const& device, PCI::RegisterOffset offset, size_t field_bytes_width) -{ - return adopt_ref(*new (nothrow) PCIDeviceAttributeSysFSComponent(device, offset, field_bytes_width)); -} - -PCIDeviceAttributeSysFSComponent::PCIDeviceAttributeSysFSComponent(PCIDeviceSysFSDirectory const& device, PCI::RegisterOffset offset, size_t field_bytes_width) - : SysFSComponent() - , m_device(device) - , m_offset(offset) - , m_field_bytes_width(field_bytes_width) -{ -} - -ErrorOr PCIDeviceAttributeSysFSComponent::read_bytes(off_t offset, size_t count, UserOrKernelBuffer& buffer, OpenFileDescription*) const -{ - auto blob = TRY(try_to_generate_buffer()); - - if ((size_t)offset >= blob->size()) - return 0; - - ssize_t nread = min(static_cast(blob->size() - offset), static_cast(count)); - TRY(buffer.write(blob->data() + offset, nread)); - return nread; -} - -ErrorOr> PCIDeviceAttributeSysFSComponent::try_to_generate_buffer() const -{ - OwnPtr value; - SpinlockLocker locker(m_device->device_identifier().operation_lock()); - switch (m_field_bytes_width) { - case 1: - value = TRY(KString::formatted("{:#x}", PCI::read8_locked(m_device->device_identifier(), m_offset))); - break; - case 2: - value = TRY(KString::formatted("{:#x}", PCI::read16_locked(m_device->device_identifier(), m_offset))); - break; - case 4: - value = TRY(KString::formatted("{:#x}", PCI::read32_locked(m_device->device_identifier(), m_offset))); - break; - default: - VERIFY_NOT_REACHED(); - } - - return KBuffer::try_create_with_bytes("PCIDeviceAttributeSysFSComponent: Device address"sv, value->view().bytes()); -} -} diff --git a/Kernel/FileSystem/SysFS/Subsystems/Bus/PCI/DeviceAttribute.h b/Kernel/FileSystem/SysFS/Subsystems/Bus/PCI/DeviceAttribute.h deleted file mode 100644 index e361c28c137..00000000000 --- a/Kernel/FileSystem/SysFS/Subsystems/Bus/PCI/DeviceAttribute.h +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright (c) 2021, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include - -namespace Kernel { - -class PCIDeviceAttributeSysFSComponent : public SysFSComponent { -public: - static NonnullRefPtr create(PCIDeviceSysFSDirectory const& device, PCI::RegisterOffset offset, size_t field_bytes_width); - - virtual ErrorOr read_bytes(off_t, size_t, UserOrKernelBuffer&, OpenFileDescription*) const override; - virtual ~PCIDeviceAttributeSysFSComponent() {}; - - virtual StringView name() const override; - -protected: - ErrorOr> try_to_generate_buffer() const; - PCIDeviceAttributeSysFSComponent(PCIDeviceSysFSDirectory const& device, PCI::RegisterOffset offset, size_t field_bytes_width); - NonnullRefPtr m_device; - PCI::RegisterOffset m_offset; - size_t m_field_bytes_width; -}; - -} diff --git a/Kernel/FileSystem/SysFS/Subsystems/Bus/PCI/DeviceDirectory.cpp b/Kernel/FileSystem/SysFS/Subsystems/Bus/PCI/DeviceDirectory.cpp deleted file mode 100644 index f193c5005ae..00000000000 --- a/Kernel/FileSystem/SysFS/Subsystems/Bus/PCI/DeviceDirectory.cpp +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright (c) 2021, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include -#include - -namespace Kernel { - -UNMAP_AFTER_INIT NonnullRefPtr PCIDeviceSysFSDirectory::create(SysFSDirectory const& parent_directory, PCI::DeviceIdentifier const& device_identifier) -{ - // FIXME: Handle allocation failure gracefully - auto& address = device_identifier.address(); - auto device_name = MUST(KString::formatted("{:04x}:{:02x}:{:02x}.{}", address.domain(), address.bus(), address.device(), address.function())); - auto directory = adopt_ref(*new (nothrow) PCIDeviceSysFSDirectory(move(device_name), parent_directory, device_identifier)); - MUST(directory->m_child_components.with([&](auto& list) -> ErrorOr { - list.append(PCIDeviceAttributeSysFSComponent::create(*directory, PCI::RegisterOffset::VENDOR_ID, 2)); - list.append(PCIDeviceAttributeSysFSComponent::create(*directory, PCI::RegisterOffset::DEVICE_ID, 2)); - list.append(PCIDeviceAttributeSysFSComponent::create(*directory, PCI::RegisterOffset::CLASS, 1)); - list.append(PCIDeviceAttributeSysFSComponent::create(*directory, PCI::RegisterOffset::SUBCLASS, 1)); - list.append(PCIDeviceAttributeSysFSComponent::create(*directory, PCI::RegisterOffset::REVISION_ID, 1)); - list.append(PCIDeviceAttributeSysFSComponent::create(*directory, PCI::RegisterOffset::PROG_IF, 1)); - list.append(PCIDeviceAttributeSysFSComponent::create(*directory, PCI::RegisterOffset::SUBSYSTEM_VENDOR_ID, 2)); - list.append(PCIDeviceAttributeSysFSComponent::create(*directory, PCI::RegisterOffset::SUBSYSTEM_ID, 2)); - - list.append(PCIDeviceAttributeSysFSComponent::create(*directory, PCI::RegisterOffset::BAR0, 4)); - list.append(PCIDeviceAttributeSysFSComponent::create(*directory, PCI::RegisterOffset::BAR1, 4)); - list.append(PCIDeviceAttributeSysFSComponent::create(*directory, PCI::RegisterOffset::BAR2, 4)); - list.append(PCIDeviceAttributeSysFSComponent::create(*directory, PCI::RegisterOffset::BAR3, 4)); - list.append(PCIDeviceAttributeSysFSComponent::create(*directory, PCI::RegisterOffset::BAR4, 4)); - list.append(PCIDeviceAttributeSysFSComponent::create(*directory, PCI::RegisterOffset::BAR5, 4)); - list.append(PCIDeviceExpansionROMSysFSComponent::create(*directory)); - return {}; - })); - return directory; -} - -UNMAP_AFTER_INIT PCIDeviceSysFSDirectory::PCIDeviceSysFSDirectory(NonnullOwnPtr device_directory_name, SysFSDirectory const& parent_directory, PCI::DeviceIdentifier const& device_identifier) - : SysFSDirectory(parent_directory) - , m_device_identifier(device_identifier) - , m_device_directory_name(move(device_directory_name)) -{ -} - -} diff --git a/Kernel/FileSystem/SysFS/Subsystems/Bus/PCI/DeviceDirectory.h b/Kernel/FileSystem/SysFS/Subsystems/Bus/PCI/DeviceDirectory.h deleted file mode 100644 index 63c489c8f63..00000000000 --- a/Kernel/FileSystem/SysFS/Subsystems/Bus/PCI/DeviceDirectory.h +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright (c) 2021, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include - -namespace Kernel { - -class PCIDeviceSysFSDirectory final : public SysFSDirectory { -public: - static NonnullRefPtr create(SysFSDirectory const&, PCI::DeviceIdentifier const&); - PCI::DeviceIdentifier const& device_identifier() const { return *m_device_identifier; } - - virtual StringView name() const override { return m_device_directory_name->view(); } - -private: - PCIDeviceSysFSDirectory(NonnullOwnPtr device_directory_name, SysFSDirectory const&, PCI::DeviceIdentifier const&); - - NonnullRefPtr const m_device_identifier; - - NonnullOwnPtr m_device_directory_name; -}; - -} diff --git a/Kernel/FileSystem/SysFS/Subsystems/Bus/PCI/DeviceExpansionROM.cpp b/Kernel/FileSystem/SysFS/Subsystems/Bus/PCI/DeviceExpansionROM.cpp deleted file mode 100644 index ebc7b846d31..00000000000 --- a/Kernel/FileSystem/SysFS/Subsystems/Bus/PCI/DeviceExpansionROM.cpp +++ /dev/null @@ -1,102 +0,0 @@ -/* - * Copyright (c) 2021, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include - -namespace Kernel { - -NonnullRefPtr PCIDeviceExpansionROMSysFSComponent::create(PCIDeviceSysFSDirectory const& device) -{ - auto option_rom_size = PCI::get_expansion_rom_space_size(device.device_identifier()); - return adopt_ref(*new (nothrow) PCIDeviceExpansionROMSysFSComponent(device, option_rom_size)); -} - -PCIDeviceExpansionROMSysFSComponent::PCIDeviceExpansionROMSysFSComponent(PCIDeviceSysFSDirectory const& device, size_t option_rom_size) - : SysFSComponent() - , m_device(device) - , m_option_rom_size(option_rom_size) -{ -} - -ErrorOr PCIDeviceExpansionROMSysFSComponent::read_bytes(off_t offset, size_t count, UserOrKernelBuffer& buffer, OpenFileDescription*) const -{ - // NOTE: It might be that the PCI option ROM size is zero, indicating non-existing ROM. - if (m_option_rom_size == 0) - return Error::from_errno(EIO); - // NOTE: This takes into account that `off_t offset` might be a negative number and - // there's no meaningful way to handle negative values, so just return with an error. - if (offset < 0) - return Error::from_errno(EINVAL); - auto unsigned_offset = static_cast(offset); - // NOTE: If the offset is beyond the PCI option ROM size, return EOF. - if (unsigned_offset >= m_option_rom_size) - return 0; - - ssize_t nread = min(static_cast(m_option_rom_size - offset), static_cast(count)); - auto blob = TRY(try_to_generate_buffer(unsigned_offset, nread)); - TRY(buffer.write(blob->bytes())); - return nread; -} - -ErrorOr> PCIDeviceExpansionROMSysFSComponent::try_to_generate_buffer(size_t offset_in_rom, size_t count) const -{ - // NOTE: If the offset is beyond the PCI option ROM size, panic!. - VERIFY(offset_in_rom < m_option_rom_size); - auto temporary_buffer_size = TRY(Memory::page_round_up(count)); - auto temporary_buffer = TRY(KBuffer::try_create_with_size("SysFS DeviceExpansionROM Device"sv, temporary_buffer_size, Memory::Region::Access::ReadWrite)); - - SpinlockLocker locker(m_device->device_identifier().operation_lock()); - // NOTE: These checks takes into account a couple of cases: - // 1. Option ROM doesn't exist so the value of the ROM physical pointer is 0, and in that case - // we should not allow mapping. - // 2. Option ROM exists but for some (odd) reason, it is found in non-reserved (usable) physical memory - // region, so access to it should be forbidden from this sysfs node. - auto pci_option_rom_physical_pointer = PCI::read32_locked(m_device->device_identifier(), PCI::RegisterOffset::EXPANSION_ROM_POINTER); - if (pci_option_rom_physical_pointer == 0) - return Error::from_errno(EIO); - - if (pci_option_rom_physical_pointer & 1) - dbgln("SysFS DeviceExpansionROM: Possible firmware bug! PCI option ROM was found already to be enabled."); - - auto offested_option_rom_memory_mapped_start_address = PhysicalAddress(pci_option_rom_physical_pointer + offset_in_rom); - auto mapping_size = min(static_cast(m_option_rom_size - offset_in_rom), static_cast(count)); - if (!MM.is_allowed_to_read_physical_memory_for_userspace(offested_option_rom_memory_mapped_start_address, mapping_size)) - return Error::from_errno(EPERM); - - ScopeGuard unmap_option_rom_on_return([&] { - // NOTE: In general, there's probably nothing wrong in leaving Option ROM being - // mapped into physical memory. - // For the sake of completeness, let's ensure we don't leave the Option ROM being - // mapped into physical memory. - // NOTE: It might be that in the future some driver will need to have the expansion ROM - // being present in the physical address space. If that's the case then we should add a flag - // to the PCI::DeviceIdentifier to indicate this condition! - PCI::write32_locked(m_device->device_identifier(), PCI::RegisterOffset::EXPANSION_ROM_POINTER, pci_option_rom_physical_pointer); - }); - // Note: Write the original value ORed with 1 to enable mapping into physical memory - PCI::write32_locked(m_device->device_identifier(), PCI::RegisterOffset::EXPANSION_ROM_POINTER, pci_option_rom_physical_pointer | 1); - - auto do_io_transaction = [](Bytes bytes_buffer, Memory::TypedMapping const& mapping, size_t length_to_map) { - VERIFY(length_to_map <= PAGE_SIZE); - memcpy(bytes_buffer.data(), mapping.region->vaddr().offset(mapping.offset).as_ptr(), length_to_map); - }; - - size_t remaining_length = count; - size_t nprocessed = 0; - while (remaining_length > 0) { - size_t length_to_map = min(PAGE_SIZE, remaining_length); - auto mapping = TRY(Memory::map_typed(offested_option_rom_memory_mapped_start_address.offset(nprocessed), length_to_map, Memory::Region::Access::Read)); - do_io_transaction(temporary_buffer->bytes().slice(nprocessed, length_to_map), mapping, length_to_map); - nprocessed += length_to_map; - remaining_length -= length_to_map; - } - return temporary_buffer; -} -} diff --git a/Kernel/FileSystem/SysFS/Subsystems/Bus/PCI/DeviceExpansionROM.h b/Kernel/FileSystem/SysFS/Subsystems/Bus/PCI/DeviceExpansionROM.h deleted file mode 100644 index bd5d946a724..00000000000 --- a/Kernel/FileSystem/SysFS/Subsystems/Bus/PCI/DeviceExpansionROM.h +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright (c) 2021, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include - -namespace Kernel { - -class PCIDeviceExpansionROMSysFSComponent : public SysFSComponent { -public: - static NonnullRefPtr create(PCIDeviceSysFSDirectory const& device); - - virtual ErrorOr read_bytes(off_t, size_t, UserOrKernelBuffer&, OpenFileDescription*) const override; - virtual ~PCIDeviceExpansionROMSysFSComponent() {}; - - virtual StringView name() const override { return "rom"sv; } - -protected: - ErrorOr> try_to_generate_buffer(size_t offset_in_rom, size_t count) const; - PCIDeviceExpansionROMSysFSComponent(PCIDeviceSysFSDirectory const& device, size_t option_rom_size); - NonnullRefPtr m_device; - size_t const m_option_rom_size { 0 }; -}; - -} diff --git a/Kernel/FileSystem/SysFS/Subsystems/Bus/USB/BusDirectory.cpp b/Kernel/FileSystem/SysFS/Subsystems/Bus/USB/BusDirectory.cpp deleted file mode 100644 index fe91659116e..00000000000 --- a/Kernel/FileSystem/SysFS/Subsystems/Bus/USB/BusDirectory.cpp +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright (c) 2021, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include - -namespace Kernel { - -static SysFSUSBBusDirectory* s_sysfs_usb_bus_directory; - -void SysFSUSBBusDirectory::plug(Badge, SysFSUSBDeviceInformation& new_device_info_node) -{ - MUST(m_child_components.with([&](auto& list) -> ErrorOr { - list.append(new_device_info_node); - return {}; - })); -} -void SysFSUSBBusDirectory::unplug(Badge, SysFSUSBDeviceInformation& removed_device_info_node) -{ - MUST(m_child_components.with([&](auto& list) -> ErrorOr { - list.remove(removed_device_info_node); - return {}; - })); -} - -SysFSUSBBusDirectory& SysFSUSBBusDirectory::the() -{ - VERIFY(s_sysfs_usb_bus_directory); - return *s_sysfs_usb_bus_directory; -} - -UNMAP_AFTER_INIT SysFSUSBBusDirectory::SysFSUSBBusDirectory(SysFSBusDirectory& buses_directory) - : SysFSDirectory(buses_directory) -{ -} - -UNMAP_AFTER_INIT void SysFSUSBBusDirectory::initialize() -{ - auto directory = adopt_ref(*new SysFSUSBBusDirectory(SysFSComponentRegistry::the().buses_directory())); - SysFSComponentRegistry::the().register_new_bus_directory(directory); - s_sysfs_usb_bus_directory = directory; -} - -} diff --git a/Kernel/FileSystem/SysFS/Subsystems/Bus/USB/BusDirectory.h b/Kernel/FileSystem/SysFS/Subsystems/Bus/USB/BusDirectory.h deleted file mode 100644 index 36761345b9b..00000000000 --- a/Kernel/FileSystem/SysFS/Subsystems/Bus/USB/BusDirectory.h +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright (c) 2021-2022, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include -#include - -namespace Kernel { - -class SysFSUSBBusDirectory final : public SysFSDirectory { -public: - static void initialize(); - static SysFSUSBBusDirectory& the(); - - virtual StringView name() const override { return "usb"sv; } - - void plug(Badge, SysFSUSBDeviceInformation&); - void unplug(Badge, SysFSUSBDeviceInformation&); - -private: - explicit SysFSUSBBusDirectory(SysFSBusDirectory&); - mutable Spinlock m_lock {}; -}; - -} diff --git a/Kernel/FileSystem/SysFS/Subsystems/Bus/USB/DeviceInformation.cpp b/Kernel/FileSystem/SysFS/Subsystems/Bus/USB/DeviceInformation.cpp deleted file mode 100644 index d4018a2d025..00000000000 --- a/Kernel/FileSystem/SysFS/Subsystems/Bus/USB/DeviceInformation.cpp +++ /dev/null @@ -1,144 +0,0 @@ -/* - * Copyright (c) 2021-2022, Liav A. - * Copyright (c) 2022, Jesse Buhagiar - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include - -namespace Kernel { - -ErrorOr> SysFSUSBDeviceInformation::create(USB::Device& device) -{ - auto device_name = TRY(KString::number(device.address())); - return adopt_nonnull_ref_or_enomem(new (nothrow) SysFSUSBDeviceInformation(move(device_name), device)); -} - -SysFSUSBDeviceInformation::SysFSUSBDeviceInformation(NonnullOwnPtr device_name, USB::Device& device) - : SysFSComponent() - , m_device(device) - , m_device_name(move(device_name)) -{ -} - -SysFSUSBDeviceInformation::~SysFSUSBDeviceInformation() = default; - -ErrorOr SysFSUSBDeviceInformation::try_generate(KBufferBuilder& builder) -{ - VERIFY(m_lock.is_locked()); - auto array = TRY(JsonArraySerializer<>::try_create(builder)); - - auto obj = TRY(array.add_object()); - TRY(obj.add("device_address"sv, m_device->address())); - TRY(obj.add("usb_spec_compliance_bcd"sv, m_device->device_descriptor().usb_spec_compliance_bcd)); - TRY(obj.add("device_class"sv, m_device->device_descriptor().device_class)); - TRY(obj.add("device_sub_class"sv, m_device->device_descriptor().device_sub_class)); - TRY(obj.add("device_protocol"sv, m_device->device_descriptor().device_protocol)); - TRY(obj.add("max_packet_size"sv, m_device->device_descriptor().max_packet_size)); - TRY(obj.add("vendor_id"sv, m_device->device_descriptor().vendor_id)); - TRY(obj.add("product_id"sv, m_device->device_descriptor().product_id)); - TRY(obj.add("device_release_bcd"sv, m_device->device_descriptor().device_release_bcd)); - TRY(obj.add("manufacturer_id_descriptor_index"sv, m_device->device_descriptor().manufacturer_id_descriptor_index)); - TRY(obj.add("product_string_descriptor_index"sv, m_device->device_descriptor().product_string_descriptor_index)); - TRY(obj.add("serial_number_descriptor_index"sv, m_device->device_descriptor().serial_number_descriptor_index)); - TRY(obj.add("num_configurations"sv, m_device->device_descriptor().num_configurations)); - TRY(obj.add("length"sv, m_device->device_descriptor().descriptor_header.length)); - TRY(obj.add("descriptor_type"sv, m_device->device_descriptor().descriptor_header.descriptor_type)); - - auto configuration_array = TRY(obj.add_array("configurations"sv)); - for (auto const& configuration : m_device->configurations()) { - auto configuration_object = TRY(configuration_array.add_object()); - auto const& configuration_descriptor = configuration.descriptor(); - TRY(configuration_object.add("length"sv, configuration_descriptor.descriptor_header.length)); - TRY(configuration_object.add("descriptor_type"sv, configuration_descriptor.descriptor_header.descriptor_type)); - TRY(configuration_object.add("total_length"sv, configuration_descriptor.total_length)); - TRY(configuration_object.add("number_of_interfaces"sv, configuration_descriptor.number_of_interfaces)); - TRY(configuration_object.add("attributes_bitmap"sv, configuration_descriptor.attributes_bitmap)); - TRY(configuration_object.add("max_power"sv, configuration_descriptor.max_power_in_ma)); - - auto interface_array = TRY(configuration_object.add_array("interfaces"sv)); - for (auto const& interface : configuration.interfaces()) { - auto interface_object = TRY(interface_array.add_object()); - auto const& interface_descriptor = interface.descriptor(); - TRY(interface_object.add("length"sv, interface_descriptor.descriptor_header.length)); - TRY(interface_object.add("descriptor_type"sv, interface_descriptor.descriptor_header.descriptor_type)); - TRY(interface_object.add("interface_number"sv, interface_descriptor.interface_id)); - TRY(interface_object.add("alternate_setting"sv, interface_descriptor.alternate_setting)); - TRY(interface_object.add("num_endpoints"sv, interface_descriptor.number_of_endpoints)); - TRY(interface_object.add("interface_class_code"sv, interface_descriptor.interface_class_code)); - TRY(interface_object.add("interface_sub_class_code"sv, interface_descriptor.interface_sub_class_code)); - TRY(interface_object.add("interface_protocol"sv, interface_descriptor.interface_protocol)); - TRY(interface_object.add("interface_string_desc_index"sv, interface_descriptor.interface_string_descriptor_index)); - - auto endpoint_array = TRY(interface_object.add_array("endpoints"sv)); - for (auto const& endpoint : interface.endpoints()) { - auto endpoint_object = TRY(endpoint_array.add_object()); - TRY(endpoint_object.add("length"sv, endpoint.descriptor_header.length)); - TRY(endpoint_object.add("descriptor_length"sv, endpoint.descriptor_header.descriptor_type)); - TRY(endpoint_object.add("endpoint_address"sv, endpoint.endpoint_address)); - TRY(endpoint_object.add("attribute_bitmap"sv, endpoint.endpoint_attributes_bitmap)); - TRY(endpoint_object.add("max_packet_size"sv, endpoint.max_packet_size)); - TRY(endpoint_object.add("polling_interval"sv, endpoint.poll_interval_in_frames)); - TRY(endpoint_object.finish()); - } - TRY(endpoint_array.finish()); - TRY(interface_object.finish()); - } - TRY(interface_array.finish()); - TRY(configuration_object.finish()); - } - TRY(configuration_array.finish()); - - TRY(obj.finish()); - TRY(array.finish()); - return {}; -} - -ErrorOr SysFSUSBDeviceInformation::refresh_data(OpenFileDescription& description) const -{ - MutexLocker lock(m_lock); - auto& cached_data = description.data(); - if (!cached_data) { - cached_data = TRY(adopt_nonnull_own_or_enomem(new (nothrow) SysFSInodeData)); - } - auto builder = TRY(KBufferBuilder::try_create()); - TRY(const_cast(*this).try_generate(builder)); - auto& typed_cached_data = static_cast(*cached_data); - typed_cached_data.buffer = builder.build(); - if (!typed_cached_data.buffer) - return ENOMEM; - return {}; -} - -ErrorOr SysFSUSBDeviceInformation::read_bytes(off_t offset, size_t count, UserOrKernelBuffer& buffer, OpenFileDescription* description) const -{ - dbgln_if(SYSFS_DEBUG, "SysFSUSBDeviceInformation @ {}: read_bytes offset: {} count: {}", name(), offset, count); - - VERIFY(offset >= 0); - VERIFY(buffer.user_or_kernel_ptr()); - - if (!description) - return Error::from_errno(EIO); - - MutexLocker locker(m_lock); - - if (!description->data()) { - dbgln("SysFSUSBDeviceInformation: Do not have cached data!"); - return Error::from_errno(EIO); - } - - auto& typed_cached_data = static_cast(*description->data()); - auto& data_buffer = typed_cached_data.buffer; - - if (!data_buffer || (size_t)offset >= data_buffer->size()) - return 0; - - ssize_t nread = min(static_cast(data_buffer->size() - offset), static_cast(count)); - TRY(buffer.write(data_buffer->data() + offset, nread)); - return nread; -} - -} diff --git a/Kernel/FileSystem/SysFS/Subsystems/Bus/USB/DeviceInformation.h b/Kernel/FileSystem/SysFS/Subsystems/Bus/USB/DeviceInformation.h deleted file mode 100644 index 0a4af580e51..00000000000 --- a/Kernel/FileSystem/SysFS/Subsystems/Bus/USB/DeviceInformation.h +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright (c) 2021-2022, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include - -namespace Kernel { - -class SysFSUSBDeviceInformation : public SysFSComponent { - friend class SysFSUSBBusDirectory; - -public: - virtual ~SysFSUSBDeviceInformation() override; - - static ErrorOr> create(USB::Device&); - virtual StringView name() const override { return m_device_name->view(); } - -protected: - SysFSUSBDeviceInformation(NonnullOwnPtr device_name, USB::Device& device); - - virtual ErrorOr read_bytes(off_t offset, size_t count, UserOrKernelBuffer& buffer, OpenFileDescription*) const override; - - NonnullRefPtr m_device; - -private: - ErrorOr try_generate(KBufferBuilder&); - virtual ErrorOr refresh_data(OpenFileDescription& description) const override; - mutable Mutex m_lock { "SysFSUSBDeviceInformation"sv }; - NonnullOwnPtr m_device_name; -}; - -} diff --git a/Kernel/FileSystem/SysFS/Subsystems/DeviceIdentifiers/BlockDevicesDirectory.cpp b/Kernel/FileSystem/SysFS/Subsystems/DeviceIdentifiers/BlockDevicesDirectory.cpp deleted file mode 100644 index 234f6d37a5f..00000000000 --- a/Kernel/FileSystem/SysFS/Subsystems/DeviceIdentifiers/BlockDevicesDirectory.cpp +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright (c) 2022, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include - -namespace Kernel { - -static SysFSBlockDevicesDirectory* s_the { nullptr }; - -NonnullRefPtr SysFSBlockDevicesDirectory::must_create(SysFSDeviceIdentifiersDirectory const& devices_directory) -{ - return adopt_ref_if_nonnull(new SysFSBlockDevicesDirectory(devices_directory)).release_nonnull(); -} -SysFSBlockDevicesDirectory::SysFSBlockDevicesDirectory(SysFSDeviceIdentifiersDirectory const& devices_directory) - : SysFSDirectory(devices_directory) -{ - s_the = this; -} - -SysFSBlockDevicesDirectory& SysFSBlockDevicesDirectory::the() -{ - VERIFY(s_the); - return *s_the; -} - -} diff --git a/Kernel/FileSystem/SysFS/Subsystems/DeviceIdentifiers/BlockDevicesDirectory.h b/Kernel/FileSystem/SysFS/Subsystems/DeviceIdentifiers/BlockDevicesDirectory.h deleted file mode 100644 index c90b5ede671..00000000000 --- a/Kernel/FileSystem/SysFS/Subsystems/DeviceIdentifiers/BlockDevicesDirectory.h +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright (c) 2022, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include - -namespace Kernel { - -class BlockDevice; -class SysFSBlockDevicesDirectory final : public SysFSDirectory { - -public: - virtual StringView name() const override { return "block"sv; } - static NonnullRefPtr must_create(SysFSDeviceIdentifiersDirectory const&); - - static SysFSBlockDevicesDirectory& the(); - - ChildList& devices_list(Badge) { return m_child_components; } - -private: - explicit SysFSBlockDevicesDirectory(SysFSDeviceIdentifiersDirectory const&); -}; - -} diff --git a/Kernel/FileSystem/SysFS/Subsystems/DeviceIdentifiers/CharacterDevicesDirectory.cpp b/Kernel/FileSystem/SysFS/Subsystems/DeviceIdentifiers/CharacterDevicesDirectory.cpp deleted file mode 100644 index 4bc3fc6983f..00000000000 --- a/Kernel/FileSystem/SysFS/Subsystems/DeviceIdentifiers/CharacterDevicesDirectory.cpp +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright (c) 2022, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include - -namespace Kernel { - -static SysFSCharacterDevicesDirectory* s_the { nullptr }; - -NonnullRefPtr SysFSCharacterDevicesDirectory::must_create(SysFSDeviceIdentifiersDirectory const& devices_directory) -{ - return adopt_ref_if_nonnull(new SysFSCharacterDevicesDirectory(devices_directory)).release_nonnull(); -} -SysFSCharacterDevicesDirectory::SysFSCharacterDevicesDirectory(SysFSDeviceIdentifiersDirectory const& devices_directory) - : SysFSDirectory(devices_directory) -{ - s_the = this; -} - -SysFSCharacterDevicesDirectory& SysFSCharacterDevicesDirectory::the() -{ - VERIFY(s_the); - return *s_the; -} - -} diff --git a/Kernel/FileSystem/SysFS/Subsystems/DeviceIdentifiers/CharacterDevicesDirectory.h b/Kernel/FileSystem/SysFS/Subsystems/DeviceIdentifiers/CharacterDevicesDirectory.h deleted file mode 100644 index ab22bc5e709..00000000000 --- a/Kernel/FileSystem/SysFS/Subsystems/DeviceIdentifiers/CharacterDevicesDirectory.h +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright (c) 2022, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include - -namespace Kernel { - -class CharacterDevice; -class SysFSCharacterDevicesDirectory final : public SysFSDirectory { - -public: - virtual StringView name() const override { return "char"sv; } - static NonnullRefPtr must_create(SysFSDeviceIdentifiersDirectory const&); - - static SysFSCharacterDevicesDirectory& the(); - - ChildList& devices_list(Badge) { return m_child_components; } - -private: - explicit SysFSCharacterDevicesDirectory(SysFSDeviceIdentifiersDirectory const&); -}; - -} diff --git a/Kernel/FileSystem/SysFS/Subsystems/DeviceIdentifiers/DeviceComponent.cpp b/Kernel/FileSystem/SysFS/Subsystems/DeviceIdentifiers/DeviceComponent.cpp deleted file mode 100644 index 05cd3b71fdb..00000000000 --- a/Kernel/FileSystem/SysFS/Subsystems/DeviceIdentifiers/DeviceComponent.cpp +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright (c) 2022, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include - -namespace Kernel { - -NonnullRefPtr SysFSDeviceComponent::must_create(Device const& device) -{ - // FIXME: Handle allocation failure gracefully - auto device_name = MUST(KString::formatted("{}:{}", device.major(), device.minor())); - return adopt_ref_if_nonnull(new SysFSDeviceComponent(move(device_name), device)).release_nonnull(); -} -SysFSDeviceComponent::SysFSDeviceComponent(NonnullOwnPtr major_minor_formatted_device_name, Device const& device) - : SysFSComponent() - , m_block_device(device.is_block_device()) - , m_major_minor_formatted_device_name(move(major_minor_formatted_device_name)) -{ - VERIFY(device.is_block_device() || device.is_character_device()); -} - -} diff --git a/Kernel/FileSystem/SysFS/Subsystems/DeviceIdentifiers/DeviceComponent.h b/Kernel/FileSystem/SysFS/Subsystems/DeviceIdentifiers/DeviceComponent.h deleted file mode 100644 index 1c34694d803..00000000000 --- a/Kernel/FileSystem/SysFS/Subsystems/DeviceIdentifiers/DeviceComponent.h +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright (c) 2022, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include - -namespace Kernel { - -class SysFSDeviceComponent final - : public SysFSComponent - , public LockWeakable { - friend class SysFSBlockDevicesDirectory; - friend class SysFSCharacterDevicesDirectory; - -public: - static NonnullRefPtr must_create(Device const&); - virtual StringView name() const override { return m_major_minor_formatted_device_name->view(); } - bool is_block_device() const { return m_block_device; } - -private: - SysFSDeviceComponent(NonnullOwnPtr major_minor_formatted_device_name, Device const&); - bool m_block_device; - - NonnullOwnPtr m_major_minor_formatted_device_name; -}; - -} diff --git a/Kernel/FileSystem/SysFS/Subsystems/DeviceIdentifiers/Directory.cpp b/Kernel/FileSystem/SysFS/Subsystems/DeviceIdentifiers/Directory.cpp deleted file mode 100644 index 36539482461..00000000000 --- a/Kernel/FileSystem/SysFS/Subsystems/DeviceIdentifiers/Directory.cpp +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright (c) 2022, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include - -namespace Kernel { - -static SysFSDeviceIdentifiersDirectory* s_the { nullptr }; - -SysFSDeviceIdentifiersDirectory& SysFSDeviceIdentifiersDirectory::the() -{ - VERIFY(s_the); - return *s_the; -} - -UNMAP_AFTER_INIT NonnullRefPtr SysFSDeviceIdentifiersDirectory::must_create(SysFSRootDirectory const& root_directory) -{ - auto devices_directory = adopt_ref_if_nonnull(new SysFSDeviceIdentifiersDirectory(root_directory)).release_nonnull(); - MUST(devices_directory->m_child_components.with([&](auto& list) -> ErrorOr { - list.append(SysFSBlockDevicesDirectory::must_create(*devices_directory)); - list.append(SysFSCharacterDevicesDirectory::must_create(*devices_directory)); - return {}; - })); - s_the = devices_directory; - return devices_directory; -} -SysFSDeviceIdentifiersDirectory::SysFSDeviceIdentifiersDirectory(SysFSRootDirectory const& root_directory) - : SysFSDirectory(root_directory) -{ -} - -} diff --git a/Kernel/FileSystem/SysFS/Subsystems/DeviceIdentifiers/Directory.h b/Kernel/FileSystem/SysFS/Subsystems/DeviceIdentifiers/Directory.h deleted file mode 100644 index 9bc56f2fa22..00000000000 --- a/Kernel/FileSystem/SysFS/Subsystems/DeviceIdentifiers/Directory.h +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright (c) 2022, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include - -namespace Kernel { - -class SysFSDeviceIdentifiersDirectory final : public SysFSDirectory { -public: - virtual StringView name() const override { return "dev"sv; } - static NonnullRefPtr must_create(SysFSRootDirectory const&); - - static SysFSDeviceIdentifiersDirectory& the(); - -private: - explicit SysFSDeviceIdentifiersDirectory(SysFSRootDirectory const&); -}; - -} diff --git a/Kernel/FileSystem/SysFS/Subsystems/DeviceIdentifiers/SymbolicLinkDeviceComponent.cpp b/Kernel/FileSystem/SysFS/Subsystems/DeviceIdentifiers/SymbolicLinkDeviceComponent.cpp deleted file mode 100644 index 56725b58bba..00000000000 --- a/Kernel/FileSystem/SysFS/Subsystems/DeviceIdentifiers/SymbolicLinkDeviceComponent.cpp +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright (c) 2022, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include - -namespace Kernel { - -ErrorOr> SysFSSymbolicLinkDeviceComponent::try_create(SysFSCharacterDevicesDirectory const& parent_directory, Device const& device, SysFSComponent const& pointed_component) -{ - auto device_name = TRY(KString::formatted("{}:{}", device.major(), device.minor())); - return adopt_nonnull_ref_or_enomem(new (nothrow) SysFSSymbolicLinkDeviceComponent(parent_directory, move(device_name), device, pointed_component)); -} - -ErrorOr> SysFSSymbolicLinkDeviceComponent::try_create(SysFSBlockDevicesDirectory const& parent_directory, Device const& device, SysFSComponent const& pointed_component) -{ - auto device_name = TRY(KString::formatted("{}:{}", device.major(), device.minor())); - return adopt_nonnull_ref_or_enomem(new (nothrow) SysFSSymbolicLinkDeviceComponent(parent_directory, move(device_name), device, pointed_component)); -} - -SysFSSymbolicLinkDeviceComponent::SysFSSymbolicLinkDeviceComponent(SysFSCharacterDevicesDirectory const& parent_directory, NonnullOwnPtr major_minor_formatted_device_name, Device const& device, SysFSComponent const& pointed_component) - : SysFSSymbolicLink(parent_directory, pointed_component) - , m_block_device(device.is_block_device()) - , m_major_minor_formatted_device_name(move(major_minor_formatted_device_name)) -{ - VERIFY(device.is_character_device()); -} - -SysFSSymbolicLinkDeviceComponent::SysFSSymbolicLinkDeviceComponent(SysFSBlockDevicesDirectory const& parent_directory, NonnullOwnPtr major_minor_formatted_device_name, Device const& device, SysFSComponent const& pointed_component) - : SysFSSymbolicLink(parent_directory, pointed_component) - , m_block_device(device.is_block_device()) - , m_major_minor_formatted_device_name(move(major_minor_formatted_device_name)) -{ - VERIFY(device.is_block_device()); -} - -} diff --git a/Kernel/FileSystem/SysFS/Subsystems/DeviceIdentifiers/SymbolicLinkDeviceComponent.h b/Kernel/FileSystem/SysFS/Subsystems/DeviceIdentifiers/SymbolicLinkDeviceComponent.h deleted file mode 100644 index 2c80b0ee692..00000000000 --- a/Kernel/FileSystem/SysFS/Subsystems/DeviceIdentifiers/SymbolicLinkDeviceComponent.h +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright (c) 2022, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include - -namespace Kernel { - -class SysFSDeviceIdentifiersDirectory; -class SysFSSymbolicLinkDeviceComponent final - : public SysFSSymbolicLink - , public LockWeakable { - friend class SysFSComponentRegistry; - -public: - static ErrorOr> try_create(SysFSCharacterDevicesDirectory const& parent_directory, Device const& device, SysFSComponent const& pointed_component); - static ErrorOr> try_create(SysFSBlockDevicesDirectory const& parent_directory, Device const& device, SysFSComponent const& pointed_component); - - virtual StringView name() const override { return m_major_minor_formatted_device_name->view(); } - bool is_block_device() const { return m_block_device; } - -private: - SysFSSymbolicLinkDeviceComponent(SysFSCharacterDevicesDirectory const& parent_directory, NonnullOwnPtr major_minor_formatted_device_name, Device const&, SysFSComponent const& pointed_component); - SysFSSymbolicLinkDeviceComponent(SysFSBlockDevicesDirectory const& parent_directory, NonnullOwnPtr major_minor_formatted_device_name, Device const&, SysFSComponent const& pointed_component); - - IntrusiveListNode> m_list_node; - bool const m_block_device { false }; - NonnullOwnPtr m_major_minor_formatted_device_name; -}; - -} diff --git a/Kernel/FileSystem/SysFS/Subsystems/Devices/Directory.cpp b/Kernel/FileSystem/SysFS/Subsystems/Devices/Directory.cpp deleted file mode 100644 index 0d4178159f7..00000000000 --- a/Kernel/FileSystem/SysFS/Subsystems/Devices/Directory.cpp +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright (c) 2022, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include - -namespace Kernel { - -UNMAP_AFTER_INIT NonnullRefPtr SysFSDevicesDirectory::must_create(SysFSRootDirectory const& root_directory) -{ - auto devices_directory = adopt_ref_if_nonnull(new (nothrow) SysFSDevicesDirectory(root_directory)).release_nonnull(); - MUST(devices_directory->m_child_components.with([&](auto& list) -> ErrorOr { - list.append(SysFSStorageDirectory::must_create(*devices_directory)); - list.append(SysFSGraphicsDirectory::must_create(*devices_directory)); - return {}; - })); - return devices_directory; -} -SysFSDevicesDirectory::SysFSDevicesDirectory(SysFSRootDirectory const& root_directory) - : SysFSDirectory(root_directory) -{ -} - -} diff --git a/Kernel/FileSystem/SysFS/Subsystems/Devices/Directory.h b/Kernel/FileSystem/SysFS/Subsystems/Devices/Directory.h deleted file mode 100644 index 0d1e08ba45e..00000000000 --- a/Kernel/FileSystem/SysFS/Subsystems/Devices/Directory.h +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright (c) 2022, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include - -namespace Kernel { - -class SysFSDevicesDirectory final : public SysFSDirectory { -public: - virtual StringView name() const override { return "devices"sv; } - static NonnullRefPtr must_create(SysFSRootDirectory const&); - -private: - explicit SysFSDevicesDirectory(SysFSRootDirectory const&); -}; - -} diff --git a/Kernel/FileSystem/SysFS/Subsystems/Devices/Graphics/Directory.cpp b/Kernel/FileSystem/SysFS/Subsystems/Devices/Graphics/Directory.cpp deleted file mode 100644 index febfbf3b008..00000000000 --- a/Kernel/FileSystem/SysFS/Subsystems/Devices/Graphics/Directory.cpp +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright (c) 2022, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include - -namespace Kernel { - -UNMAP_AFTER_INIT NonnullRefPtr SysFSGraphicsDirectory::must_create(SysFSDevicesDirectory const& parent_directory) -{ - auto directory = adopt_ref(*new (nothrow) SysFSGraphicsDirectory(parent_directory)); - MUST(directory->m_child_components.with([&](auto& list) -> ErrorOr { - list.append(SysFSDisplayConnectorsDirectory::must_create(*directory)); - return {}; - })); - return directory; -} - -UNMAP_AFTER_INIT SysFSGraphicsDirectory::SysFSGraphicsDirectory(SysFSDevicesDirectory const& parent_directory) - : SysFSDirectory(parent_directory) -{ -} - -} diff --git a/Kernel/FileSystem/SysFS/Subsystems/Devices/Graphics/Directory.h b/Kernel/FileSystem/SysFS/Subsystems/Devices/Graphics/Directory.h deleted file mode 100644 index 7378d0ebfc9..00000000000 --- a/Kernel/FileSystem/SysFS/Subsystems/Devices/Graphics/Directory.h +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright (c) 2022, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include - -namespace Kernel { - -class SysFSGraphicsDirectory : public SysFSDirectory { - friend class SysFSComponentRegistry; - -public: - virtual StringView name() const override { return "graphics"sv; } - static NonnullRefPtr must_create(SysFSDevicesDirectory const&); - -private: - explicit SysFSGraphicsDirectory(SysFSDevicesDirectory const&); -}; - -} diff --git a/Kernel/FileSystem/SysFS/Subsystems/Devices/Graphics/DisplayConnector/DeviceAttribute.cpp b/Kernel/FileSystem/SysFS/Subsystems/Devices/Graphics/DisplayConnector/DeviceAttribute.cpp deleted file mode 100644 index 2cbccfb6928..00000000000 --- a/Kernel/FileSystem/SysFS/Subsystems/Devices/Graphics/DisplayConnector/DeviceAttribute.cpp +++ /dev/null @@ -1,85 +0,0 @@ -/* - * Copyright (c) 2022, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include - -namespace Kernel { - -StringView DisplayConnectorAttributeSysFSComponent::name() const -{ - switch (m_type) { - case Type::MutableModeSettingCapable: - return "mutable_mode_setting_capable"sv; - case Type::DoubleFrameBufferingCapable: - return "double_framebuffering_capable"sv; - case Type::FlushSupport: - return "flush_support"sv; - case Type::PartialFlushSupport: - return "partial_flush_support"sv; - case Type::RefreshRateSupport: - return "refresh_rate_support"sv; - case Type::EDID: - return "edid"sv; - default: - VERIFY_NOT_REACHED(); - } -} - -NonnullRefPtr DisplayConnectorAttributeSysFSComponent::must_create(DisplayConnectorSysFSDirectory const& device_directory, Type type) -{ - return adopt_ref(*new (nothrow) DisplayConnectorAttributeSysFSComponent(device_directory, type)); -} - -DisplayConnectorAttributeSysFSComponent::DisplayConnectorAttributeSysFSComponent(DisplayConnectorSysFSDirectory const& device_directory, Type type) - : SysFSComponent() - , m_device(device_directory.device({})) - , m_type(type) -{ -} - -ErrorOr DisplayConnectorAttributeSysFSComponent::read_bytes(off_t offset, size_t count, UserOrKernelBuffer& buffer, OpenFileDescription*) const -{ - auto blob = TRY(try_to_generate_buffer()); - - if ((size_t)offset >= blob->size()) - return 0; - - ssize_t nread = min(static_cast(blob->size() - offset), static_cast(count)); - TRY(buffer.write(blob->data() + offset, nread)); - return nread; -} - -ErrorOr> DisplayConnectorAttributeSysFSComponent::try_to_generate_buffer() const -{ - OwnPtr value; - switch (m_type) { - case Type::MutableModeSettingCapable: - value = TRY(KString::formatted("{:d}", m_device->mutable_mode_setting_capable())); - break; - case Type::DoubleFrameBufferingCapable: - value = TRY(KString::formatted("{:d}", m_device->double_framebuffering_capable())); - break; - case Type::FlushSupport: - value = TRY(KString::formatted("{:d}", m_device->flush_support())); - break; - case Type::PartialFlushSupport: - value = TRY(KString::formatted("{:d}", m_device->partial_flush_support())); - break; - case Type::RefreshRateSupport: - value = TRY(KString::formatted("{:d}", m_device->refresh_rate_support())); - break; - case Type::EDID: { - auto edid_buffer = TRY(m_device->get_edid()); - return KBuffer::try_create_with_bytes("SysFS DisplayConnectorAttributeSysFSComponent EDID buffer"sv, edid_buffer.bytes()); - } - default: - VERIFY_NOT_REACHED(); - } - return KBuffer::try_create_with_bytes("SysFS DisplayConnectorAttributeSysFSComponent buffer"sv, value->view().bytes()); -} -} diff --git a/Kernel/FileSystem/SysFS/Subsystems/Devices/Graphics/DisplayConnector/DeviceAttribute.h b/Kernel/FileSystem/SysFS/Subsystems/Devices/Graphics/DisplayConnector/DeviceAttribute.h deleted file mode 100644 index 959a8d742a4..00000000000 --- a/Kernel/FileSystem/SysFS/Subsystems/Devices/Graphics/DisplayConnector/DeviceAttribute.h +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright (c) 2022, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include - -namespace Kernel { - -class DisplayConnectorAttributeSysFSComponent : public SysFSComponent { -public: - enum class Type { - MutableModeSettingCapable, - DoubleFrameBufferingCapable, - FlushSupport, - PartialFlushSupport, - RefreshRateSupport, - EDID, - }; - -public: - static NonnullRefPtr must_create(DisplayConnectorSysFSDirectory const& device_directory, Type); - - virtual ErrorOr read_bytes(off_t, size_t, UserOrKernelBuffer&, OpenFileDescription*) const override; - virtual ~DisplayConnectorAttributeSysFSComponent() {}; - - virtual StringView name() const override; - -protected: - ErrorOr> try_to_generate_buffer() const; - DisplayConnectorAttributeSysFSComponent(DisplayConnectorSysFSDirectory const& device, Type); - NonnullRefPtr m_device; - Type const m_type { Type::MutableModeSettingCapable }; -}; - -} diff --git a/Kernel/FileSystem/SysFS/Subsystems/Devices/Graphics/DisplayConnector/DeviceDirectory.cpp b/Kernel/FileSystem/SysFS/Subsystems/Devices/Graphics/DisplayConnector/DeviceDirectory.cpp deleted file mode 100644 index f3013a8e2b4..00000000000 --- a/Kernel/FileSystem/SysFS/Subsystems/Devices/Graphics/DisplayConnector/DeviceDirectory.cpp +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright (c) 2022, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include - -namespace Kernel { - -DisplayConnector const& DisplayConnectorSysFSDirectory::device(Badge) const -{ - return *m_device; -} - -UNMAP_AFTER_INIT NonnullRefPtr DisplayConnectorSysFSDirectory::create(SysFSDirectory const& parent_directory, DisplayConnector const& device) -{ - // FIXME: Handle allocation failure gracefully - auto device_name = MUST(KString::formatted("{}", device.minor())); - auto directory = adopt_ref(*new (nothrow) DisplayConnectorSysFSDirectory(move(device_name), parent_directory, device)); - MUST(directory->m_child_components.with([&](auto& list) -> ErrorOr { - list.append(DisplayConnectorAttributeSysFSComponent::must_create(*directory, DisplayConnectorAttributeSysFSComponent::Type::MutableModeSettingCapable)); - list.append(DisplayConnectorAttributeSysFSComponent::must_create(*directory, DisplayConnectorAttributeSysFSComponent::Type::DoubleFrameBufferingCapable)); - list.append(DisplayConnectorAttributeSysFSComponent::must_create(*directory, DisplayConnectorAttributeSysFSComponent::Type::FlushSupport)); - list.append(DisplayConnectorAttributeSysFSComponent::must_create(*directory, DisplayConnectorAttributeSysFSComponent::Type::PartialFlushSupport)); - list.append(DisplayConnectorAttributeSysFSComponent::must_create(*directory, DisplayConnectorAttributeSysFSComponent::Type::RefreshRateSupport)); - list.append(DisplayConnectorAttributeSysFSComponent::must_create(*directory, DisplayConnectorAttributeSysFSComponent::Type::EDID)); - return {}; - })); - return directory; -} - -UNMAP_AFTER_INIT DisplayConnectorSysFSDirectory::DisplayConnectorSysFSDirectory(NonnullOwnPtr device_directory_name, SysFSDirectory const& parent_directory, DisplayConnector const& device) - : SysFSDirectory(parent_directory) - , m_device(device) - , m_device_directory_name(move(device_directory_name)) -{ -} - -} diff --git a/Kernel/FileSystem/SysFS/Subsystems/Devices/Graphics/DisplayConnector/DeviceDirectory.h b/Kernel/FileSystem/SysFS/Subsystems/Devices/Graphics/DisplayConnector/DeviceDirectory.h deleted file mode 100644 index 6ed3cfc7976..00000000000 --- a/Kernel/FileSystem/SysFS/Subsystems/Devices/Graphics/DisplayConnector/DeviceDirectory.h +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright (c) 2022, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include - -namespace Kernel { - -class DisplayConnectorAttributeSysFSComponent; -class DisplayConnectorSysFSDirectory final : public SysFSDirectory { -public: - static NonnullRefPtr create(SysFSDirectory const&, DisplayConnector const&); - - virtual StringView name() const override { return m_device_directory_name->view(); } - - DisplayConnector const& device(Badge) const; - -private: - DisplayConnectorSysFSDirectory(NonnullOwnPtr device_directory_name, SysFSDirectory const&, DisplayConnector const&); - NonnullRefPtr const m_device; - NonnullOwnPtr m_device_directory_name; -}; - -} diff --git a/Kernel/FileSystem/SysFS/Subsystems/Devices/Graphics/DisplayConnector/Directory.cpp b/Kernel/FileSystem/SysFS/Subsystems/Devices/Graphics/DisplayConnector/Directory.cpp deleted file mode 100644 index 3960bc4021a..00000000000 --- a/Kernel/FileSystem/SysFS/Subsystems/Devices/Graphics/DisplayConnector/Directory.cpp +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright (c) 2022, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include -#include -#include - -namespace Kernel { - -static SysFSDisplayConnectorsDirectory* s_the { nullptr }; - -UNMAP_AFTER_INIT NonnullRefPtr SysFSDisplayConnectorsDirectory::must_create(SysFSGraphicsDirectory const& parent_directory) -{ - auto directory = adopt_ref(*new (nothrow) SysFSDisplayConnectorsDirectory(parent_directory)); - s_the = directory; - return directory; -} - -SysFSDisplayConnectorsDirectory& SysFSDisplayConnectorsDirectory::the() -{ - VERIFY(s_the); - return *s_the; -} - -void SysFSDisplayConnectorsDirectory::plug(Badge, DisplayConnectorSysFSDirectory& new_device_directory) -{ - MUST(m_child_components.with([&](auto& list) -> ErrorOr { - list.append(new_device_directory); - auto pointed_component_base_name = MUST(KString::try_create(new_device_directory.name())); - auto pointed_component_relative_path = MUST(new_device_directory.relative_path(move(pointed_component_base_name), 0)); - return {}; - })); -} -void SysFSDisplayConnectorsDirectory::unplug(Badge, SysFSDirectory& removed_device_directory) -{ - MUST(m_child_components.with([&](auto& list) -> ErrorOr { - list.remove(removed_device_directory); - return {}; - })); -} - -UNMAP_AFTER_INIT SysFSDisplayConnectorsDirectory::SysFSDisplayConnectorsDirectory(SysFSGraphicsDirectory const& parent_directory) - : SysFSDirectory(parent_directory) -{ -} - -} diff --git a/Kernel/FileSystem/SysFS/Subsystems/Devices/Graphics/DisplayConnector/Directory.h b/Kernel/FileSystem/SysFS/Subsystems/Devices/Graphics/DisplayConnector/Directory.h deleted file mode 100644 index 04038277d1a..00000000000 --- a/Kernel/FileSystem/SysFS/Subsystems/Devices/Graphics/DisplayConnector/Directory.h +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright (c) 2022, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include - -namespace Kernel { - -class DisplayConnectorSysFSDirectory; -class DisplayConnector; -class SysFSGraphicsDirectory; -class SysFSDisplayConnectorsDirectory : public SysFSDirectory { - friend class SysFSComponentRegistry; - -public: - virtual StringView name() const override { return "connectors"sv; } - static SysFSDisplayConnectorsDirectory& the(); - static NonnullRefPtr must_create(SysFSGraphicsDirectory const&); - - void plug(Badge, DisplayConnectorSysFSDirectory&); - void unplug(Badge, SysFSDirectory&); - -private: - explicit SysFSDisplayConnectorsDirectory(SysFSGraphicsDirectory const&); -}; - -} diff --git a/Kernel/FileSystem/SysFS/Subsystems/Devices/Storage/DeviceAttribute.cpp b/Kernel/FileSystem/SysFS/Subsystems/Devices/Storage/DeviceAttribute.cpp deleted file mode 100644 index fec12066455..00000000000 --- a/Kernel/FileSystem/SysFS/Subsystems/Devices/Storage/DeviceAttribute.cpp +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Copyright (c) 2022, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include - -namespace Kernel { - -StringView StorageDeviceAttributeSysFSComponent::name() const -{ - switch (m_type) { - case Type::EndLBA: - return "last_lba"sv; - case Type::SectorSize: - return "sector_size"sv; - case Type::CommandSet: - return "command_set"sv; - default: - VERIFY_NOT_REACHED(); - } -} - -NonnullRefPtr StorageDeviceAttributeSysFSComponent::must_create(StorageDeviceSysFSDirectory const& device_directory, Type type) -{ - return adopt_ref(*new (nothrow) StorageDeviceAttributeSysFSComponent(device_directory, type)); -} - -StorageDeviceAttributeSysFSComponent::StorageDeviceAttributeSysFSComponent(StorageDeviceSysFSDirectory const& device_directory, Type type) - : SysFSComponent() - , m_device(device_directory.device({})) - , m_type(type) -{ -} - -ErrorOr StorageDeviceAttributeSysFSComponent::read_bytes(off_t offset, size_t count, UserOrKernelBuffer& buffer, OpenFileDescription*) const -{ - auto blob = TRY(try_to_generate_buffer()); - - if ((size_t)offset >= blob->size()) - return 0; - - ssize_t nread = min(static_cast(blob->size() - offset), static_cast(count)); - TRY(buffer.write(blob->data() + offset, nread)); - return nread; -} - -ErrorOr> StorageDeviceAttributeSysFSComponent::try_to_generate_buffer() const -{ - OwnPtr value; - switch (m_type) { - case Type::EndLBA: - value = TRY(KString::formatted("{}", m_device->max_addressable_block())); - break; - case Type::SectorSize: - value = TRY(KString::formatted("{}", m_device->block_size())); - break; - case Type::CommandSet: - value = TRY(KString::formatted("{}", m_device->command_set_to_string_view())); - break; - default: - VERIFY_NOT_REACHED(); - } - return KBuffer::try_create_with_bytes("SysFS StorageDeviceAttributeComponent buffer"sv, value->view().bytes()); -} -} diff --git a/Kernel/FileSystem/SysFS/Subsystems/Devices/Storage/DeviceAttribute.h b/Kernel/FileSystem/SysFS/Subsystems/Devices/Storage/DeviceAttribute.h deleted file mode 100644 index 31d92101330..00000000000 --- a/Kernel/FileSystem/SysFS/Subsystems/Devices/Storage/DeviceAttribute.h +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright (c) 2022, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include - -namespace Kernel { - -class StorageDeviceAttributeSysFSComponent : public SysFSComponent { -public: - enum class Type { - EndLBA, - SectorSize, - CommandSet, - }; - -public: - static NonnullRefPtr must_create(StorageDeviceSysFSDirectory const& device_directory, Type); - - virtual ErrorOr read_bytes(off_t, size_t, UserOrKernelBuffer&, OpenFileDescription*) const override; - virtual ~StorageDeviceAttributeSysFSComponent() {}; - - virtual StringView name() const override; - -protected: - ErrorOr> try_to_generate_buffer() const; - StorageDeviceAttributeSysFSComponent(StorageDeviceSysFSDirectory const& device, Type); - NonnullRefPtr m_device; - Type const m_type { Type::EndLBA }; -}; - -} diff --git a/Kernel/FileSystem/SysFS/Subsystems/Devices/Storage/DeviceDirectory.cpp b/Kernel/FileSystem/SysFS/Subsystems/Devices/Storage/DeviceDirectory.cpp deleted file mode 100644 index 784a273193f..00000000000 --- a/Kernel/FileSystem/SysFS/Subsystems/Devices/Storage/DeviceDirectory.cpp +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright (c) 2022, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include - -namespace Kernel { - -StorageDevice const& StorageDeviceSysFSDirectory::device(Badge) const -{ - return *m_device; -} - -NonnullRefPtr StorageDeviceSysFSDirectory::create(SysFSDirectory const& parent_directory, StorageDevice const& device) -{ - // FIXME: Handle allocation failure gracefully - auto lun_address = device.logical_unit_number_address(); - auto device_name = MUST(KString::formatted("{:02x}:{:02x}.{}", lun_address.controller_id, lun_address.target_id, lun_address.disk_id)); - auto directory = adopt_ref(*new (nothrow) StorageDeviceSysFSDirectory(move(device_name), parent_directory, device)); - MUST(directory->m_child_components.with([&](auto& list) -> ErrorOr { - list.append(StorageDeviceAttributeSysFSComponent::must_create(*directory, StorageDeviceAttributeSysFSComponent::Type::EndLBA)); - list.append(StorageDeviceAttributeSysFSComponent::must_create(*directory, StorageDeviceAttributeSysFSComponent::Type::SectorSize)); - list.append(StorageDeviceAttributeSysFSComponent::must_create(*directory, StorageDeviceAttributeSysFSComponent::Type::CommandSet)); - return {}; - })); - return directory; -} - -StorageDeviceSysFSDirectory::StorageDeviceSysFSDirectory(NonnullOwnPtr device_directory_name, SysFSDirectory const& parent_directory, StorageDevice const& device) - : SysFSDirectory(parent_directory) - , m_device(device) - , m_device_directory_name(move(device_directory_name)) -{ -} - -} diff --git a/Kernel/FileSystem/SysFS/Subsystems/Devices/Storage/DeviceDirectory.h b/Kernel/FileSystem/SysFS/Subsystems/Devices/Storage/DeviceDirectory.h deleted file mode 100644 index f17ceb46ef8..00000000000 --- a/Kernel/FileSystem/SysFS/Subsystems/Devices/Storage/DeviceDirectory.h +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright (c) 2022, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include - -namespace Kernel { - -class StorageDeviceAttributeSysFSComponent; -class StorageDeviceSysFSDirectory final : public SysFSDirectory { -public: - static NonnullRefPtr create(SysFSDirectory const&, StorageDevice const&); - - virtual StringView name() const override { return m_device_directory_name->view(); } - - StorageDevice const& device(Badge) const; - -private: - StorageDeviceSysFSDirectory(NonnullOwnPtr device_directory_name, SysFSDirectory const&, StorageDevice const&); - RefPtr m_device; - NonnullOwnPtr m_device_directory_name; -}; - -} diff --git a/Kernel/FileSystem/SysFS/Subsystems/Devices/Storage/Directory.cpp b/Kernel/FileSystem/SysFS/Subsystems/Devices/Storage/Directory.cpp deleted file mode 100644 index 24a5cae233d..00000000000 --- a/Kernel/FileSystem/SysFS/Subsystems/Devices/Storage/Directory.cpp +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright (c) 2022, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include -#include - -namespace Kernel { - -static SysFSStorageDirectory* s_the { nullptr }; - -UNMAP_AFTER_INIT NonnullRefPtr SysFSStorageDirectory::must_create(SysFSDevicesDirectory const& parent_directory) -{ - auto directory = adopt_ref(*new (nothrow) SysFSStorageDirectory(parent_directory)); - s_the = directory; - return directory; -} - -SysFSStorageDirectory& SysFSStorageDirectory::the() -{ - VERIFY(s_the); - return *s_the; -} - -void SysFSStorageDirectory::plug(Badge, StorageDeviceSysFSDirectory& new_device_directory) -{ - MUST(m_child_components.with([&](auto& list) -> ErrorOr { - list.append(new_device_directory); - auto pointed_component_base_name = MUST(KString::try_create(new_device_directory.name())); - auto pointed_component_relative_path = MUST(new_device_directory.relative_path(move(pointed_component_base_name), 0)); - return {}; - })); -} -void SysFSStorageDirectory::unplug(Badge, SysFSDirectory& removed_device_directory) -{ - MUST(m_child_components.with([&](auto& list) -> ErrorOr { - list.remove(removed_device_directory); - return {}; - })); -} - -UNMAP_AFTER_INIT SysFSStorageDirectory::SysFSStorageDirectory(SysFSDevicesDirectory const& parent_directory) - : SysFSDirectory(parent_directory) -{ -} - -} diff --git a/Kernel/FileSystem/SysFS/Subsystems/Devices/Storage/Directory.h b/Kernel/FileSystem/SysFS/Subsystems/Devices/Storage/Directory.h deleted file mode 100644 index 80170fb9016..00000000000 --- a/Kernel/FileSystem/SysFS/Subsystems/Devices/Storage/Directory.h +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright (c) 2022, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include - -namespace Kernel { - -class StorageDeviceSysFSDirectory; -class StorageDevice; -class SysFSStorageDirectory : public SysFSDirectory { - friend class SysFSComponentRegistry; - -public: - virtual StringView name() const override { return "storage"sv; } - static SysFSStorageDirectory& the(); - static NonnullRefPtr must_create(SysFSDevicesDirectory const&); - - void plug(Badge, StorageDeviceSysFSDirectory&); - void unplug(Badge, SysFSDirectory&); - -private: - explicit SysFSStorageDirectory(SysFSDevicesDirectory const&); -}; - -} diff --git a/Kernel/FileSystem/SysFS/Subsystems/Firmware/Directory.cpp b/Kernel/FileSystem/SysFS/Subsystems/Firmware/Directory.cpp deleted file mode 100644 index 904ec88e585..00000000000 --- a/Kernel/FileSystem/SysFS/Subsystems/Firmware/Directory.cpp +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright (c) 2021, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#if ARCH(X86_64) -# include -#endif -#include -#include -#include -#include - -namespace Kernel { - -UNMAP_AFTER_INIT void SysFSFirmwareDirectory::initialize() -{ - auto firmware_directory = adopt_ref_if_nonnull(new (nothrow) SysFSFirmwareDirectory()).release_nonnull(); - SysFSComponentRegistry::the().register_new_component(firmware_directory); - firmware_directory->create_components(); -} - -void SysFSFirmwareDirectory::create_components() -{ - MUST(m_child_components.with([&](auto& list) -> ErrorOr { -#if ARCH(X86_64) - list.append(SysFSBIOSDirectory::must_create(*this)); -#endif - if (ACPI::is_enabled()) - list.append(ACPI::ACPISysFSDirectory::must_create(*this)); - return {}; - })); -} - -UNMAP_AFTER_INIT SysFSFirmwareDirectory::SysFSFirmwareDirectory() - : SysFSDirectory(SysFSComponentRegistry::the().root_directory()) -{ -} - -} diff --git a/Kernel/FileSystem/SysFS/Subsystems/Firmware/Directory.h b/Kernel/FileSystem/SysFS/Subsystems/Firmware/Directory.h deleted file mode 100644 index d818c41dc59..00000000000 --- a/Kernel/FileSystem/SysFS/Subsystems/Firmware/Directory.h +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright (c) 2021, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include - -namespace Kernel { - -class SysFSFirmwareDirectory : public SysFSDirectory { -public: - virtual StringView name() const override { return "firmware"sv; } - static void initialize(); - - void create_components(); - -private: - SysFSFirmwareDirectory(); -}; - -} diff --git a/Kernel/FileSystem/SysFS/Subsystems/Kernel/CPUInfo.cpp b/Kernel/FileSystem/SysFS/Subsystems/Kernel/CPUInfo.cpp deleted file mode 100644 index 379dd2902c7..00000000000 --- a/Kernel/FileSystem/SysFS/Subsystems/Kernel/CPUInfo.cpp +++ /dev/null @@ -1,93 +0,0 @@ -/* - * Copyright (c) 2022, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#if ARCH(X86_64) -# include -#endif -#include -#include - -namespace Kernel { - -UNMAP_AFTER_INIT SysFSCPUInformation::SysFSCPUInformation(SysFSDirectory const& parent_directory) - : SysFSGlobalInformation(parent_directory) -{ -} - -UNMAP_AFTER_INIT NonnullRefPtr SysFSCPUInformation::must_create(SysFSDirectory const& parent_directory) -{ - return adopt_ref_if_nonnull(new (nothrow) SysFSCPUInformation(parent_directory)).release_nonnull(); -} - -ErrorOr SysFSCPUInformation::try_generate(KBufferBuilder& builder) -{ -#if ARCH(X86_64) - auto array = TRY(JsonArraySerializer<>::try_create(builder)); - TRY(Processor::try_for_each( - [&](Processor& proc) -> ErrorOr { - auto& info = proc.info(); - auto obj = TRY(array.add_object()); - TRY(obj.add("processor"sv, proc.id())); - TRY(obj.add("vendor_id"sv, info.vendor_id_string())); - TRY(obj.add("family"sv, info.display_family())); - if (!info.hypervisor_vendor_id_string().is_null()) - TRY(obj.add("hypervisor_vendor_id"sv, info.hypervisor_vendor_id_string())); - - auto features_array = TRY(obj.add_array("features"sv)); - auto keep_empty = SplitBehavior::KeepEmpty; - - TRY(info.features_string().for_each_split_view(' ', keep_empty, [&](StringView feature) { - return features_array.add(feature); - })); - - TRY(features_array.finish()); - - TRY(obj.add("model"sv, info.display_model())); - TRY(obj.add("stepping"sv, info.stepping())); - TRY(obj.add("type"sv, info.type())); - TRY(obj.add("brand"sv, info.brand_string())); - - auto caches = TRY(obj.add_object("caches"sv)); - - auto add_cache_info = [&](StringView name, ProcessorInfo::Cache const& cache) -> ErrorOr { - auto cache_object = TRY(caches.add_object(name)); - TRY(cache_object.add("size"sv, cache.size)); - TRY(cache_object.add("line_size"sv, cache.line_size)); - TRY(cache_object.finish()); - return {}; - }; - - if (info.l1_data_cache().has_value()) - TRY(add_cache_info("l1_data"sv, *info.l1_data_cache())); - if (info.l1_instruction_cache().has_value()) - TRY(add_cache_info("l1_instruction"sv, *info.l1_instruction_cache())); - if (info.l2_cache().has_value()) - TRY(add_cache_info("l2"sv, *info.l2_cache())); - if (info.l3_cache().has_value()) - TRY(add_cache_info("l3"sv, *info.l3_cache())); - - TRY(caches.finish()); - - TRY(obj.finish()); - return {}; - })); - TRY(array.finish()); - return {}; -#elif ARCH(AARCH64) - (void)builder; - dmesgln("TODO: Implement ProcessorInfo for AArch64!"); - return Error::from_errno(EINVAL); -#elif ARCH(RISCV64) - (void)builder; - dmesgln("TODO: Implement ProcessorInfo for riscv64!"); - return Error::from_errno(EINVAL); -#else -# error Unknown architecture -#endif -} - -} diff --git a/Kernel/FileSystem/SysFS/Subsystems/Kernel/CPUInfo.h b/Kernel/FileSystem/SysFS/Subsystems/Kernel/CPUInfo.h deleted file mode 100644 index fe994c2112c..00000000000 --- a/Kernel/FileSystem/SysFS/Subsystems/Kernel/CPUInfo.h +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright (c) 2022, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include - -namespace Kernel { - -class SysFSCPUInformation final : public SysFSGlobalInformation { -public: - virtual StringView name() const override { return "cpuinfo"sv; } - - static NonnullRefPtr must_create(SysFSDirectory const& parent_directory); - -private: - SysFSCPUInformation(SysFSDirectory const& parent_directory); - virtual ErrorOr try_generate(KBufferBuilder& builder) override; -}; - -} diff --git a/Kernel/FileSystem/SysFS/Subsystems/Kernel/Configuration/BooleanVariable.cpp b/Kernel/FileSystem/SysFS/Subsystems/Kernel/Configuration/BooleanVariable.cpp deleted file mode 100644 index 947dbb5851c..00000000000 --- a/Kernel/FileSystem/SysFS/Subsystems/Kernel/Configuration/BooleanVariable.cpp +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright (c) 2022-2023, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include - -namespace Kernel { - -ErrorOr SysFSSystemBooleanVariable::try_generate(KBufferBuilder& builder) -{ - return builder.appendff("{}\n", static_cast(value())); -} - -ErrorOr SysFSSystemBooleanVariable::write_bytes(off_t, size_t count, UserOrKernelBuffer const& buffer, OpenFileDescription*) -{ - MutexLocker locker(m_refresh_lock); - // Note: We do all of this code before taking the spinlock because then we disable - // interrupts so page faults will not work. - char value = 0; - TRY(buffer.read(&value, 1)); - - // NOTE: If we are in a jail, don't let the current process to change the variable. - if (Process::current().is_currently_in_jail()) - return Error::from_errno(EPERM); - - if (count != 1) - return Error::from_errno(EINVAL); - if (value == '0') { - set_value(false); - return 1; - } else if (value == '1') { - set_value(true); - return 1; - } - return Error::from_errno(EINVAL); -} - -ErrorOr SysFSSystemBooleanVariable::truncate(u64 size) -{ - if (size != 0) - return EPERM; - return {}; -} - -} diff --git a/Kernel/FileSystem/SysFS/Subsystems/Kernel/Configuration/BooleanVariable.h b/Kernel/FileSystem/SysFS/Subsystems/Kernel/Configuration/BooleanVariable.h deleted file mode 100644 index 0ef3d9f7287..00000000000 --- a/Kernel/FileSystem/SysFS/Subsystems/Kernel/Configuration/BooleanVariable.h +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright (c) 2022-2023, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace Kernel { - -class SysFSSystemBooleanVariable : public SysFSGlobalInformation { -protected: - explicit SysFSSystemBooleanVariable(SysFSDirectory const& parent_directory) - : SysFSGlobalInformation(parent_directory) - { - } - virtual bool value() const = 0; - virtual void set_value(bool new_value) = 0; - -private: - // ^SysFSGlobalInformation - virtual ErrorOr try_generate(KBufferBuilder&) override final; - - // ^SysFSExposedComponent - virtual ErrorOr write_bytes(off_t, size_t, UserOrKernelBuffer const&, OpenFileDescription*) override final; - virtual mode_t permissions() const override final { return 0644; } - virtual ErrorOr truncate(u64) override final; -}; - -} diff --git a/Kernel/FileSystem/SysFS/Subsystems/Kernel/Configuration/CapsLockRemap.cpp b/Kernel/FileSystem/SysFS/Subsystems/Kernel/Configuration/CapsLockRemap.cpp deleted file mode 100644 index aba11c3aa68..00000000000 --- a/Kernel/FileSystem/SysFS/Subsystems/Kernel/Configuration/CapsLockRemap.cpp +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright (c) 2022-2023, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include - -namespace Kernel { - -UNMAP_AFTER_INIT SysFSCapsLockRemap::SysFSCapsLockRemap(SysFSDirectory const& parent_directory) - : SysFSSystemBooleanVariable(parent_directory) -{ -} - -UNMAP_AFTER_INIT NonnullRefPtr SysFSCapsLockRemap::must_create(SysFSDirectory const& parent_directory) -{ - return adopt_ref_if_nonnull(new (nothrow) SysFSCapsLockRemap(parent_directory)).release_nonnull(); -} - -bool SysFSCapsLockRemap::value() const -{ - SpinlockLocker locker(m_lock); - return g_caps_lock_remapped_to_ctrl.load(); -} -void SysFSCapsLockRemap::set_value(bool new_value) -{ - SpinlockLocker locker(m_lock); - g_caps_lock_remapped_to_ctrl.exchange(new_value); -} - -} diff --git a/Kernel/FileSystem/SysFS/Subsystems/Kernel/Configuration/CapsLockRemap.h b/Kernel/FileSystem/SysFS/Subsystems/Kernel/Configuration/CapsLockRemap.h deleted file mode 100644 index 47ba4dd8d30..00000000000 --- a/Kernel/FileSystem/SysFS/Subsystems/Kernel/Configuration/CapsLockRemap.h +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright (c) 2022-2023, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include - -namespace Kernel { - -class SysFSCapsLockRemap final : public SysFSSystemBooleanVariable { -public: - virtual StringView name() const override { return "caps_lock_to_ctrl"sv; } - static NonnullRefPtr must_create(SysFSDirectory const&); - -private: - virtual bool value() const override; - virtual void set_value(bool new_value) override; - - explicit SysFSCapsLockRemap(SysFSDirectory const&); - - mutable Spinlock m_lock {}; -}; - -} diff --git a/Kernel/FileSystem/SysFS/Subsystems/Kernel/Configuration/CoredumpDirectory.cpp b/Kernel/FileSystem/SysFS/Subsystems/Kernel/Configuration/CoredumpDirectory.cpp deleted file mode 100644 index 0e84049eccd..00000000000 --- a/Kernel/FileSystem/SysFS/Subsystems/Kernel/Configuration/CoredumpDirectory.cpp +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright (c) 2022-2023, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include - -namespace Kernel { - -UNMAP_AFTER_INIT SysFSCoredumpDirectory::SysFSCoredumpDirectory(SysFSDirectory const& parent_directory) - : SysFSSystemStringVariable(parent_directory) -{ -} - -UNMAP_AFTER_INIT NonnullRefPtr SysFSCoredumpDirectory::must_create(SysFSDirectory const& parent_directory) -{ - return adopt_ref_if_nonnull(new (nothrow) SysFSCoredumpDirectory(parent_directory)).release_nonnull(); -} - -ErrorOr> SysFSCoredumpDirectory::value() const -{ - return Coredump::directory_path().with([&](auto& coredump_directory_path) -> ErrorOr> { - if (coredump_directory_path) - return KString::try_create(coredump_directory_path->view()); - return KString::try_create(""sv); - }); -} -void SysFSCoredumpDirectory::set_value(NonnullOwnPtr new_value) -{ - Coredump::directory_path().with([&](auto& coredump_directory_path) { - coredump_directory_path = move(new_value); - }); -} - -mode_t SysFSCoredumpDirectory::permissions() const -{ - // NOTE: Let's not allow users to randomly change the coredump path, but only - // allow this for the root user. - return S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH; -} - -} diff --git a/Kernel/FileSystem/SysFS/Subsystems/Kernel/Configuration/CoredumpDirectory.h b/Kernel/FileSystem/SysFS/Subsystems/Kernel/Configuration/CoredumpDirectory.h deleted file mode 100644 index fee5f43f10e..00000000000 --- a/Kernel/FileSystem/SysFS/Subsystems/Kernel/Configuration/CoredumpDirectory.h +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright (c) 2022-2023, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include - -namespace Kernel { - -class SysFSCoredumpDirectory final : public SysFSSystemStringVariable { -public: - virtual StringView name() const override { return "coredump_directory"sv; } - static NonnullRefPtr must_create(SysFSDirectory const&); - -private: - virtual ErrorOr> value() const override; - virtual void set_value(NonnullOwnPtr new_value) override; - - explicit SysFSCoredumpDirectory(SysFSDirectory const&); - - virtual mode_t permissions() const override; -}; - -} diff --git a/Kernel/FileSystem/SysFS/Subsystems/Kernel/Configuration/Directory.cpp b/Kernel/FileSystem/SysFS/Subsystems/Kernel/Configuration/Directory.cpp deleted file mode 100644 index 05a8de62a42..00000000000 --- a/Kernel/FileSystem/SysFS/Subsystems/Kernel/Configuration/Directory.cpp +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright (c) 2022-2023, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -namespace Kernel { - -UNMAP_AFTER_INIT NonnullRefPtr SysFSKernelConfigurationDirectory::must_create(SysFSDirectory const& parent_directory) -{ - auto global_variables_directory = adopt_ref_if_nonnull(new (nothrow) SysFSKernelConfigurationDirectory(parent_directory)).release_nonnull(); - MUST(global_variables_directory->m_child_components.with([&](auto& list) -> ErrorOr { - list.append(SysFSCapsLockRemap::must_create(*global_variables_directory)); - list.append(SysFSDumpKmallocStacks::must_create(*global_variables_directory)); - list.append(SysFSUBSANDeadly::must_create(*global_variables_directory)); - list.append(SysFSCoredumpDirectory::must_create(*global_variables_directory)); - return {}; - })); - return global_variables_directory; -} - -UNMAP_AFTER_INIT SysFSKernelConfigurationDirectory::SysFSKernelConfigurationDirectory(SysFSDirectory const& parent_directory) - : SysFSDirectory(parent_directory) -{ -} - -} diff --git a/Kernel/FileSystem/SysFS/Subsystems/Kernel/Configuration/Directory.h b/Kernel/FileSystem/SysFS/Subsystems/Kernel/Configuration/Directory.h deleted file mode 100644 index 15a054b496b..00000000000 --- a/Kernel/FileSystem/SysFS/Subsystems/Kernel/Configuration/Directory.h +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright (c) 2021-2023, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include - -namespace Kernel { - -class SysFSKernelConfigurationDirectory : public SysFSDirectory { -public: - static NonnullRefPtr must_create(SysFSDirectory const&); - virtual StringView name() const override { return "conf"sv; } - -private: - explicit SysFSKernelConfigurationDirectory(SysFSDirectory const&); -}; - -} diff --git a/Kernel/FileSystem/SysFS/Subsystems/Kernel/Configuration/DumpKmallocStack.cpp b/Kernel/FileSystem/SysFS/Subsystems/Kernel/Configuration/DumpKmallocStack.cpp deleted file mode 100644 index 17d8a8eb55e..00000000000 --- a/Kernel/FileSystem/SysFS/Subsystems/Kernel/Configuration/DumpKmallocStack.cpp +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright (c) 2022-2023, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include - -namespace Kernel { - -UNMAP_AFTER_INIT SysFSDumpKmallocStacks::SysFSDumpKmallocStacks(SysFSDirectory const& parent_directory) - : SysFSSystemBooleanVariable(parent_directory) -{ -} - -UNMAP_AFTER_INIT NonnullRefPtr SysFSDumpKmallocStacks::must_create(SysFSDirectory const& parent_directory) -{ - return adopt_ref_if_nonnull(new (nothrow) SysFSDumpKmallocStacks(parent_directory)).release_nonnull(); -} - -bool SysFSDumpKmallocStacks::value() const -{ - SpinlockLocker locker(m_lock); - return g_dump_kmalloc_stacks; -} - -void SysFSDumpKmallocStacks::set_value(bool new_value) -{ - SpinlockLocker locker(m_lock); - g_dump_kmalloc_stacks = new_value; -} - -} diff --git a/Kernel/FileSystem/SysFS/Subsystems/Kernel/Configuration/DumpKmallocStack.h b/Kernel/FileSystem/SysFS/Subsystems/Kernel/Configuration/DumpKmallocStack.h deleted file mode 100644 index d2c0c3c254e..00000000000 --- a/Kernel/FileSystem/SysFS/Subsystems/Kernel/Configuration/DumpKmallocStack.h +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright (c) 2022-2023, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include - -namespace Kernel { - -class SysFSDumpKmallocStacks final : public SysFSSystemBooleanVariable { -public: - virtual StringView name() const override { return "kmalloc_stacks"sv; } - static NonnullRefPtr must_create(SysFSDirectory const&); - -private: - virtual bool value() const override; - virtual void set_value(bool new_value) override; - - explicit SysFSDumpKmallocStacks(SysFSDirectory const&); - - mutable Spinlock m_lock {}; -}; - -} diff --git a/Kernel/FileSystem/SysFS/Subsystems/Kernel/Configuration/KASANDeadly.cpp b/Kernel/FileSystem/SysFS/Subsystems/Kernel/Configuration/KASANDeadly.cpp deleted file mode 100644 index 5c65cdf3e34..00000000000 --- a/Kernel/FileSystem/SysFS/Subsystems/Kernel/Configuration/KASANDeadly.cpp +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright (c) 2023, Idan Horowitz - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include - -namespace Kernel { - -UNMAP_AFTER_INIT SysFSKASANDeadly::SysFSKASANDeadly(SysFSDirectory const& parent_directory) - : SysFSSystemBooleanVariable(parent_directory) -{ -} - -UNMAP_AFTER_INIT NonnullRefPtr SysFSKASANDeadly::must_create(SysFSDirectory const& parent_directory) -{ - return adopt_ref_if_nonnull(new (nothrow) SysFSKASANDeadly(parent_directory)).release_nonnull(); -} - -bool SysFSKASANDeadly::value() const -{ - return AddressSanitizer::g_kasan_is_deadly; -} -void SysFSKASANDeadly::set_value(bool new_value) -{ - AddressSanitizer::g_kasan_is_deadly = new_value; -} - -} diff --git a/Kernel/FileSystem/SysFS/Subsystems/Kernel/Configuration/KASANDeadly.h b/Kernel/FileSystem/SysFS/Subsystems/Kernel/Configuration/KASANDeadly.h deleted file mode 100644 index a4600a3e41a..00000000000 --- a/Kernel/FileSystem/SysFS/Subsystems/Kernel/Configuration/KASANDeadly.h +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright (c) 2023, Idan Horowitz - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include - -namespace Kernel { - -class SysFSKASANDeadly final : public SysFSSystemBooleanVariable { -public: - virtual StringView name() const override { return "kasan_is_deadly"sv; } - static NonnullRefPtr must_create(SysFSDirectory const&); - -private: - virtual bool value() const override; - virtual void set_value(bool new_value) override; - - explicit SysFSKASANDeadly(SysFSDirectory const&); -}; - -} diff --git a/Kernel/FileSystem/SysFS/Subsystems/Kernel/Configuration/StringVariable.cpp b/Kernel/FileSystem/SysFS/Subsystems/Kernel/Configuration/StringVariable.cpp deleted file mode 100644 index f6ab78410d3..00000000000 --- a/Kernel/FileSystem/SysFS/Subsystems/Kernel/Configuration/StringVariable.cpp +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright (c) 2022-2023, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include - -namespace Kernel { - -ErrorOr SysFSSystemStringVariable::try_generate(KBufferBuilder& builder) -{ - auto string_value = TRY(value()); - return builder.appendff("{}\n", string_value->view()); -} - -ErrorOr SysFSSystemStringVariable::write_bytes(off_t, size_t count, UserOrKernelBuffer const& buffer, OpenFileDescription*) -{ - MutexLocker locker(m_refresh_lock); - // Note: We do all of this code before taking the spinlock because then we disable - // interrupts so page faults will not work. - char* value = nullptr; - auto new_value = TRY(KString::try_create_uninitialized(count, value)); - TRY(buffer.read(value, count)); - auto new_value_without_possible_newlines = TRY(KString::try_create(new_value->view().trim("\n"sv))); - // NOTE: If we are in a jail, don't let the current process to change the variable. - if (Process::current().is_currently_in_jail()) - return Error::from_errno(EPERM); - set_value(move(new_value_without_possible_newlines)); - return count; -} - -ErrorOr SysFSSystemStringVariable::truncate(u64 size) -{ - if (size != 0) - return EPERM; - return {}; -} - -} diff --git a/Kernel/FileSystem/SysFS/Subsystems/Kernel/Configuration/StringVariable.h b/Kernel/FileSystem/SysFS/Subsystems/Kernel/Configuration/StringVariable.h deleted file mode 100644 index d02a2accba5..00000000000 --- a/Kernel/FileSystem/SysFS/Subsystems/Kernel/Configuration/StringVariable.h +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright (c) 2022-2023, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace Kernel { - -class SysFSSystemStringVariable : public SysFSGlobalInformation { -protected: - explicit SysFSSystemStringVariable(SysFSDirectory const& parent_directory) - : SysFSGlobalInformation(parent_directory) - { - } - virtual ErrorOr> value() const = 0; - virtual void set_value(NonnullOwnPtr new_value) = 0; - -private: - // ^SysFSGlobalInformation - virtual ErrorOr try_generate(KBufferBuilder&) override final; - - // ^SysFSExposedComponent - virtual ErrorOr write_bytes(off_t, size_t, UserOrKernelBuffer const&, OpenFileDescription*) override final; - virtual mode_t permissions() const override { return 0644; } - virtual ErrorOr truncate(u64) override final; -}; - -} diff --git a/Kernel/FileSystem/SysFS/Subsystems/Kernel/Configuration/UBSANDeadly.cpp b/Kernel/FileSystem/SysFS/Subsystems/Kernel/Configuration/UBSANDeadly.cpp deleted file mode 100644 index cf103b67470..00000000000 --- a/Kernel/FileSystem/SysFS/Subsystems/Kernel/Configuration/UBSANDeadly.cpp +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright (c) 2022-2023, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include - -namespace Kernel { - -UNMAP_AFTER_INIT SysFSUBSANDeadly::SysFSUBSANDeadly(SysFSDirectory const& parent_directory) - : SysFSSystemBooleanVariable(parent_directory) -{ -} - -UNMAP_AFTER_INIT NonnullRefPtr SysFSUBSANDeadly::must_create(SysFSDirectory const& parent_directory) -{ - return adopt_ref_if_nonnull(new (nothrow) SysFSUBSANDeadly(parent_directory)).release_nonnull(); -} - -bool SysFSUBSANDeadly::value() const -{ - return AK::UBSanitizer::g_ubsan_is_deadly; -} -void SysFSUBSANDeadly::set_value(bool new_value) -{ - AK::UBSanitizer::g_ubsan_is_deadly = new_value; -} - -} diff --git a/Kernel/FileSystem/SysFS/Subsystems/Kernel/Configuration/UBSANDeadly.h b/Kernel/FileSystem/SysFS/Subsystems/Kernel/Configuration/UBSANDeadly.h deleted file mode 100644 index 2508dc5305a..00000000000 --- a/Kernel/FileSystem/SysFS/Subsystems/Kernel/Configuration/UBSANDeadly.h +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright (c) 2022-2023, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include - -namespace Kernel { - -class SysFSUBSANDeadly final : public SysFSSystemBooleanVariable { -public: - virtual StringView name() const override { return "ubsan_is_deadly"sv; } - static NonnullRefPtr must_create(SysFSDirectory const&); - -private: - virtual bool value() const override; - virtual void set_value(bool new_value) override; - - explicit SysFSUBSANDeadly(SysFSDirectory const&); -}; - -} diff --git a/Kernel/FileSystem/SysFS/Subsystems/Kernel/ConstantInformation.cpp b/Kernel/FileSystem/SysFS/Subsystems/Kernel/ConstantInformation.cpp deleted file mode 100644 index b317a341992..00000000000 --- a/Kernel/FileSystem/SysFS/Subsystems/Kernel/ConstantInformation.cpp +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright (c) 2023, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include - -namespace Kernel { - -StringView SysFSSystemConstantInformation::name() const -{ - switch (m_node_name) { - case NodeName::LoadBase: - return "load_base"sv; - case NodeName::CommandLine: - return "cmdline"sv; - case NodeName::SystemMode: - return "system_mode"sv; - default: - VERIFY_NOT_REACHED(); - } -} - -NonnullRefPtr SysFSSystemConstantInformation::must_create(SysFSDirectory const& parent_directory, NonnullOwnPtr constant_data_buffer, mode_t mode, ReadableByJailedProcesses readable_by_jailed_processes, NodeName name) -{ - auto node = adopt_ref_if_nonnull(new (nothrow) SysFSSystemConstantInformation(parent_directory, move(constant_data_buffer), mode, readable_by_jailed_processes, name)).release_nonnull(); - return node; -} - -ErrorOr SysFSSystemConstantInformation::read_bytes(off_t offset, size_t count, UserOrKernelBuffer& buffer, OpenFileDescription*) const -{ - if (offset < 0) - return EINVAL; - if (static_cast(offset) >= m_constant_data_buffer->size()) - return 0; - TRY(Process::current().jail().with([&](auto const& my_jail) -> ErrorOr { - if (my_jail && m_readable_by_jailed_processes == ReadableByJailedProcesses::No) - return Error::from_errno(EPERM); - return {}; - })); - ssize_t nread = min(static_cast(m_constant_data_buffer->size() - offset), static_cast(count)); - TRY(buffer.write(m_constant_data_buffer->data() + offset, nread)); - return nread; -} - -} diff --git a/Kernel/FileSystem/SysFS/Subsystems/Kernel/ConstantInformation.h b/Kernel/FileSystem/SysFS/Subsystems/Kernel/ConstantInformation.h deleted file mode 100644 index 1bbcd2b6d58..00000000000 --- a/Kernel/FileSystem/SysFS/Subsystems/Kernel/ConstantInformation.h +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright (c) 2023, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include - -namespace Kernel { - -class SysFSSystemConstantInformation final : public SysFSComponent { -public: - enum class NodeName { - LoadBase, - CommandLine, - SystemMode, - }; - - enum class ReadableByJailedProcesses { - Yes, - No, - }; - - virtual StringView name() const override; - static NonnullRefPtr must_create(SysFSDirectory const& parent_directory, NonnullOwnPtr constant_data_buffer, mode_t mode, ReadableByJailedProcesses readable_by_jailed_processes, NodeName name); - - virtual ErrorOr read_bytes(off_t, size_t, UserOrKernelBuffer&, OpenFileDescription*) const override; - -private: - SysFSSystemConstantInformation(SysFSDirectory const& parent_directory, NonnullOwnPtr constant_data_buffer, mode_t mode, ReadableByJailedProcesses readable_by_jailed_processes, NodeName name) - : SysFSComponent(parent_directory) - , m_constant_data_buffer(move(constant_data_buffer)) - , m_permissions(mode) - , m_readable_by_jailed_processes(readable_by_jailed_processes) - , m_node_name(name) - { - } - - virtual size_t size() const override { return m_constant_data_buffer->size(); } - virtual mode_t permissions() const override { return m_permissions; } - - NonnullOwnPtr m_constant_data_buffer; - mode_t const m_permissions { 0644 }; - ReadableByJailedProcesses const m_readable_by_jailed_processes { ReadableByJailedProcesses::No }; - NodeName const m_node_name { NodeName::LoadBase }; -}; - -} diff --git a/Kernel/FileSystem/SysFS/Subsystems/Kernel/Directory.cpp b/Kernel/FileSystem/SysFS/Subsystems/Kernel/Directory.cpp deleted file mode 100644 index 192bcb1991c..00000000000 --- a/Kernel/FileSystem/SysFS/Subsystems/Kernel/Directory.cpp +++ /dev/null @@ -1,88 +0,0 @@ -/* - * Copyright (c) 2022, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace Kernel { - -UNMAP_AFTER_INIT NonnullRefPtr SysFSGlobalKernelStatsDirectory::must_create(SysFSRootDirectory const& root_directory) -{ - auto global_kernel_stats_directory = adopt_ref_if_nonnull(new (nothrow) SysFSGlobalKernelStatsDirectory(root_directory)).release_nonnull(); - MUST(global_kernel_stats_directory->m_child_components.with([&](auto& list) -> ErrorOr { - list.append(SysFSDiskUsage::must_create(*global_kernel_stats_directory)); - list.append(SysFSMemoryStatus::must_create(*global_kernel_stats_directory)); - list.append(SysFSSystemStatistics::must_create(*global_kernel_stats_directory)); - list.append(SysFSOverallProcesses::must_create(*global_kernel_stats_directory)); - list.append(SysFSCPUInformation::must_create(*global_kernel_stats_directory)); - list.append(SysFSKernelLog::must_create(*global_kernel_stats_directory)); - list.append(SysFSInterrupts::must_create(*global_kernel_stats_directory)); - list.append(SysFSKeymap::must_create(*global_kernel_stats_directory)); - list.append(SysFSUptime::must_create(*global_kernel_stats_directory)); - list.append(SysFSProfile::must_create(*global_kernel_stats_directory)); - list.append(SysFSPowerStateSwitchNode::must_create(*global_kernel_stats_directory)); - list.append(SysFSJails::must_create(*global_kernel_stats_directory)); - list.append(SysFSSystemRequestPanic::must_create(*global_kernel_stats_directory)); - - list.append(SysFSGlobalNetworkStatsDirectory::must_create(*global_kernel_stats_directory)); - list.append(SysFSKernelConfigurationDirectory::must_create(*global_kernel_stats_directory)); - - { - auto builder = TRY(KBufferBuilder::try_create()); - MUST(builder.appendff("{}", kernel_load_base)); - auto load_base_buffer = builder.build(); - VERIFY(load_base_buffer); - list.append(SysFSSystemConstantInformation::must_create(*global_kernel_stats_directory, load_base_buffer.release_nonnull(), S_IRUSR, SysFSSystemConstantInformation::ReadableByJailedProcesses::No, SysFSSystemConstantInformation::NodeName::LoadBase)); - } - - { - auto builder = TRY(KBufferBuilder::try_create()); - MUST(builder.append(kernel_command_line().string())); - MUST(builder.append('\n')); - auto command_line_buffer = builder.build(); - VERIFY(command_line_buffer); - list.append(SysFSSystemConstantInformation::must_create(*global_kernel_stats_directory, command_line_buffer.release_nonnull(), S_IRUSR | S_IRGRP | S_IROTH, SysFSSystemConstantInformation::ReadableByJailedProcesses::No, SysFSSystemConstantInformation::NodeName::CommandLine)); - } - - { - auto builder = TRY(KBufferBuilder::try_create()); - MUST(builder.append(kernel_command_line().system_mode())); - MUST(builder.append('\n')); - auto system_mode_buffer = builder.build(); - VERIFY(system_mode_buffer); - list.append(SysFSSystemConstantInformation::must_create(*global_kernel_stats_directory, system_mode_buffer.release_nonnull(), S_IRUSR | S_IRGRP | S_IROTH, SysFSSystemConstantInformation::ReadableByJailedProcesses::No, SysFSSystemConstantInformation::NodeName::SystemMode)); - } - return {}; - })); - return global_kernel_stats_directory; -} - -UNMAP_AFTER_INIT SysFSGlobalKernelStatsDirectory::SysFSGlobalKernelStatsDirectory(SysFSDirectory const& root_directory) - : SysFSDirectory(root_directory) -{ -} - -} diff --git a/Kernel/FileSystem/SysFS/Subsystems/Kernel/Directory.h b/Kernel/FileSystem/SysFS/Subsystems/Kernel/Directory.h deleted file mode 100644 index fee2dd59550..00000000000 --- a/Kernel/FileSystem/SysFS/Subsystems/Kernel/Directory.h +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright (c) 2021, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include - -namespace Kernel { - -class SysFSGlobalKernelStatsDirectory : public SysFSDirectory { -public: - static NonnullRefPtr must_create(SysFSRootDirectory const&); - virtual StringView name() const override { return "kernel"sv; } - -private: - explicit SysFSGlobalKernelStatsDirectory(SysFSDirectory const&); -}; - -} diff --git a/Kernel/FileSystem/SysFS/Subsystems/Kernel/DiskUsage.cpp b/Kernel/FileSystem/SysFS/Subsystems/Kernel/DiskUsage.cpp deleted file mode 100644 index b2985d89854..00000000000 --- a/Kernel/FileSystem/SysFS/Subsystems/Kernel/DiskUsage.cpp +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Copyright (c) 2022, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include -#include -#include - -namespace Kernel { - -UNMAP_AFTER_INIT NonnullRefPtr SysFSDiskUsage::must_create(SysFSDirectory const& parent_directory) -{ - return adopt_ref_if_nonnull(new (nothrow) SysFSDiskUsage(parent_directory)).release_nonnull(); -} - -UNMAP_AFTER_INIT SysFSDiskUsage::SysFSDiskUsage(SysFSDirectory const& parent_directory) - : SysFSGlobalInformation(parent_directory) -{ -} - -ErrorOr SysFSDiskUsage::try_generate(KBufferBuilder& builder) -{ - auto array = TRY(JsonArraySerializer<>::try_create(builder)); - TRY(VirtualFileSystem::the().for_each_mount([&array](auto& mount) -> ErrorOr { - auto& fs = mount.guest_fs(); - auto fs_object = TRY(array.add_object()); - TRY(fs_object.add("class_name"sv, fs.class_name())); - TRY(fs_object.add("total_block_count"sv, fs.total_block_count())); - TRY(fs_object.add("free_block_count"sv, fs.free_block_count())); - TRY(fs_object.add("total_inode_count"sv, fs.total_inode_count())); - TRY(fs_object.add("free_inode_count"sv, fs.free_inode_count())); - auto mount_point = TRY(mount.absolute_path()); - TRY(fs_object.add("mount_point"sv, mount_point->view())); - TRY(fs_object.add("block_size"sv, static_cast(fs.logical_block_size()))); - TRY(fs_object.add("readonly"sv, fs.is_readonly())); - TRY(fs_object.add("mount_flags"sv, mount.flags())); - - if (mount.flags() & MS_SRCHIDDEN) { - TRY(fs_object.add("source"sv, "unknown")); - } else { - if (fs.is_file_backed()) { - auto& file = static_cast(fs).file(); - if (file.is_loop_device()) { - auto& device = static_cast(file); - auto path = TRY(device.custody().try_serialize_absolute_path()); - TRY(fs_object.add("source"sv, path->view())); - } else { - auto pseudo_path = TRY(static_cast(fs).file_description().pseudo_path()); - TRY(fs_object.add("source"sv, pseudo_path->view())); - } - } else { - TRY(fs_object.add("source"sv, "none")); - } - } - - TRY(fs_object.finish()); - return {}; - })); - TRY(array.finish()); - return {}; -} - -} diff --git a/Kernel/FileSystem/SysFS/Subsystems/Kernel/DiskUsage.h b/Kernel/FileSystem/SysFS/Subsystems/Kernel/DiskUsage.h deleted file mode 100644 index 52608ad7d4f..00000000000 --- a/Kernel/FileSystem/SysFS/Subsystems/Kernel/DiskUsage.h +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright (c) 2022, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include - -namespace Kernel { - -class SysFSDiskUsage final : public SysFSGlobalInformation { -public: - virtual StringView name() const override { return "df"sv; } - - static NonnullRefPtr must_create(SysFSDirectory const& parent_directory); - -private: - SysFSDiskUsage(SysFSDirectory const& parent_directory); - virtual ErrorOr try_generate(KBufferBuilder& builder) override; - - virtual bool is_readable_by_jailed_processes() const override { return true; } -}; - -} diff --git a/Kernel/FileSystem/SysFS/Subsystems/Kernel/GlobalInformation.cpp b/Kernel/FileSystem/SysFS/Subsystems/Kernel/GlobalInformation.cpp deleted file mode 100644 index 95d9c3b430c..00000000000 --- a/Kernel/FileSystem/SysFS/Subsystems/Kernel/GlobalInformation.cpp +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright (c) 2022, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include - -namespace Kernel { - -ErrorOr SysFSGlobalInformation::read_bytes(off_t offset, size_t count, UserOrKernelBuffer& buffer, OpenFileDescription* description) const -{ - dbgln_if(SYSFS_DEBUG, "SysFSGlobalInformation @ {}: read_bytes offset: {} count: {}", name(), offset, count); - - VERIFY(offset >= 0); - VERIFY(buffer.user_or_kernel_ptr()); - - if (!description) - return Error::from_errno(EIO); - - MutexLocker locker(m_refresh_lock); - - if (!description->data()) { - dbgln("SysFSGlobalInformation: Do not have cached data!"); - return Error::from_errno(EIO); - } - - auto& typed_cached_data = static_cast(*description->data()); - auto& data_buffer = typed_cached_data.buffer; - - if (!data_buffer || (size_t)offset >= data_buffer->size()) - return 0; - - ssize_t nread = min(static_cast(data_buffer->size() - offset), static_cast(count)); - TRY(buffer.write(data_buffer->data() + offset, nread)); - return nread; -} - -SysFSGlobalInformation::SysFSGlobalInformation(SysFSDirectory const& parent_directory) - : SysFSComponent(parent_directory) -{ -} - -ErrorOr SysFSGlobalInformation::refresh_data(OpenFileDescription& description) const -{ - MutexLocker lock(m_refresh_lock); - auto& cached_data = description.data(); - if (!cached_data) { - cached_data = adopt_own_if_nonnull(new (nothrow) SysFSInodeData); - if (!cached_data) - return ENOMEM; - } - auto builder = TRY(KBufferBuilder::try_create()); - TRY(Process::current().jail().with([&](auto const& my_jail) -> ErrorOr { - if (my_jail && !is_readable_by_jailed_processes()) - return Error::from_errno(EPERM); - return {}; - })); - TRY(const_cast(*this).try_generate(builder)); - auto& typed_cached_data = static_cast(*cached_data); - typed_cached_data.buffer = builder.build(); - if (!typed_cached_data.buffer) - return ENOMEM; - return {}; -} - -} diff --git a/Kernel/FileSystem/SysFS/Subsystems/Kernel/GlobalInformation.h b/Kernel/FileSystem/SysFS/Subsystems/Kernel/GlobalInformation.h deleted file mode 100644 index 9cc93517786..00000000000 --- a/Kernel/FileSystem/SysFS/Subsystems/Kernel/GlobalInformation.h +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright (c) 2022, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace Kernel { - -class SysFSGlobalInformation : public SysFSComponent { -public: - virtual ErrorOr read_bytes(off_t offset, size_t count, UserOrKernelBuffer& buffer, OpenFileDescription* description) const override; - -protected: - explicit SysFSGlobalInformation(SysFSDirectory const& parent_directory); - virtual ErrorOr refresh_data(OpenFileDescription&) const override; - virtual ErrorOr try_generate(KBufferBuilder&) = 0; - - virtual bool is_readable_by_jailed_processes() const { return false; } - - mutable Mutex m_refresh_lock; -}; - -} diff --git a/Kernel/FileSystem/SysFS/Subsystems/Kernel/Interrupts.cpp b/Kernel/FileSystem/SysFS/Subsystems/Kernel/Interrupts.cpp deleted file mode 100644 index 47e1d088a1e..00000000000 --- a/Kernel/FileSystem/SysFS/Subsystems/Kernel/Interrupts.cpp +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright (c) 2022, Liav A. - * Copyright (c) 2022, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include - -namespace Kernel { - -UNMAP_AFTER_INIT SysFSInterrupts::SysFSInterrupts(SysFSDirectory const& parent_directory) - : SysFSGlobalInformation(parent_directory) -{ -} - -UNMAP_AFTER_INIT NonnullRefPtr SysFSInterrupts::must_create(SysFSDirectory const& parent_directory) -{ - return adopt_ref_if_nonnull(new (nothrow) SysFSInterrupts(parent_directory)).release_nonnull(); -} - -ErrorOr SysFSInterrupts::try_generate(KBufferBuilder& builder) -{ - auto array = TRY(JsonArraySerializer<>::try_create(builder)); - ErrorOr result; // FIXME: Make this nicer - InterruptManagement::the().enumerate_interrupt_handlers([&array, &result](GenericInterruptHandler& handler) { - if (result.is_error()) - return; - result = ([&]() -> ErrorOr { - auto obj = TRY(array.add_object()); - TRY(obj.add("purpose"sv, handler.purpose())); - TRY(obj.add("interrupt_line"sv, handler.interrupt_number())); - TRY(obj.add("controller"sv, handler.controller())); - TRY(obj.add("device_sharing"sv, (unsigned)handler.sharing_devices_count())); - auto per_cpu_call_counts = TRY(obj.add_array("per_cpu_call_counts"sv)); - for (auto call_count : handler.per_cpu_call_counts()) { - TRY(per_cpu_call_counts.add(call_count)); - } - TRY(per_cpu_call_counts.finish()); - TRY(obj.finish()); - return {}; - })(); - }); - TRY(result); - TRY(array.finish()); - return {}; -} - -} diff --git a/Kernel/FileSystem/SysFS/Subsystems/Kernel/Interrupts.h b/Kernel/FileSystem/SysFS/Subsystems/Kernel/Interrupts.h deleted file mode 100644 index e75dfdf7597..00000000000 --- a/Kernel/FileSystem/SysFS/Subsystems/Kernel/Interrupts.h +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright (c) 2022, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include - -namespace Kernel { - -class SysFSInterrupts final : public SysFSGlobalInformation { -public: - virtual StringView name() const override { return "interrupts"sv; } - - static NonnullRefPtr must_create(SysFSDirectory const& parent_directory); - -private: - explicit SysFSInterrupts(SysFSDirectory const& parent_directory); - virtual ErrorOr try_generate(KBufferBuilder& builder) override; - - virtual bool is_readable_by_jailed_processes() const override { return true; } -}; - -} diff --git a/Kernel/FileSystem/SysFS/Subsystems/Kernel/Jails.cpp b/Kernel/FileSystem/SysFS/Subsystems/Kernel/Jails.cpp deleted file mode 100644 index 61ce06c60c7..00000000000 --- a/Kernel/FileSystem/SysFS/Subsystems/Kernel/Jails.cpp +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright (c) 2022-2023, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include - -namespace Kernel { - -UNMAP_AFTER_INIT SysFSJails::SysFSJails(SysFSDirectory const& parent_directory) - : SysFSGlobalInformation(parent_directory) -{ -} - -UNMAP_AFTER_INIT NonnullRefPtr SysFSJails::must_create(SysFSDirectory const& parent_directory) -{ - return adopt_ref_if_nonnull(new (nothrow) SysFSJails(parent_directory)).release_nonnull(); -} - -ErrorOr SysFSJails::try_generate(KBufferBuilder& builder) -{ - auto array = TRY(JsonArraySerializer<>::try_create(builder)); - TRY(Jail::for_each_when_process_is_not_jailed([&array](Jail const& jail) -> ErrorOr { - auto obj = TRY(array.add_object()); - TRY(obj.add("index"sv, jail.index().value())); - TRY(obj.add("name"sv, jail.name())); - TRY(obj.finish()); - return {}; - })); - TRY(array.finish()); - return {}; -} - -} diff --git a/Kernel/FileSystem/SysFS/Subsystems/Kernel/Jails.h b/Kernel/FileSystem/SysFS/Subsystems/Kernel/Jails.h deleted file mode 100644 index 219c8e85aa2..00000000000 --- a/Kernel/FileSystem/SysFS/Subsystems/Kernel/Jails.h +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright (c) 2022, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include - -namespace Kernel { - -class SysFSJails final : public SysFSGlobalInformation { -public: - virtual StringView name() const override { return "jails"sv; } - - static NonnullRefPtr must_create(SysFSDirectory const& parent_directory); - -private: - explicit SysFSJails(SysFSDirectory const& parent_directory); - virtual ErrorOr try_generate(KBufferBuilder& builder) override; -}; - -} diff --git a/Kernel/FileSystem/SysFS/Subsystems/Kernel/Keymap.cpp b/Kernel/FileSystem/SysFS/Subsystems/Kernel/Keymap.cpp deleted file mode 100644 index 149987c01c0..00000000000 --- a/Kernel/FileSystem/SysFS/Subsystems/Kernel/Keymap.cpp +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright (c) 2022, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include - -namespace Kernel { - -UNMAP_AFTER_INIT SysFSKeymap::SysFSKeymap(SysFSDirectory const& parent_directory) - : SysFSGlobalInformation(parent_directory) -{ -} - -UNMAP_AFTER_INIT NonnullRefPtr SysFSKeymap::must_create(SysFSDirectory const& parent_directory) -{ - return adopt_ref_if_nonnull(new (nothrow) SysFSKeymap(parent_directory)).release_nonnull(); -} - -ErrorOr SysFSKeymap::try_generate(KBufferBuilder& builder) -{ - auto json = TRY(JsonObjectSerializer<>::try_create(builder)); - TRY(HIDManagement::the().keymap_data().with([&](auto const& keymap_data) { - return json.add("keymap"sv, keymap_data.character_map_name->view()); - })); - TRY(json.finish()); - return {}; -} - -} diff --git a/Kernel/FileSystem/SysFS/Subsystems/Kernel/Keymap.h b/Kernel/FileSystem/SysFS/Subsystems/Kernel/Keymap.h deleted file mode 100644 index b4ea1a7460f..00000000000 --- a/Kernel/FileSystem/SysFS/Subsystems/Kernel/Keymap.h +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright (c) 2022, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include - -namespace Kernel { - -class SysFSKeymap final : public SysFSGlobalInformation { -public: - virtual StringView name() const override { return "keymap"sv; } - - static NonnullRefPtr must_create(SysFSDirectory const& parent_directory); - -private: - explicit SysFSKeymap(SysFSDirectory const& parent_directory); - virtual ErrorOr try_generate(KBufferBuilder& builder) override; - - virtual bool is_readable_by_jailed_processes() const override { return true; } -}; - -} diff --git a/Kernel/FileSystem/SysFS/Subsystems/Kernel/Log.cpp b/Kernel/FileSystem/SysFS/Subsystems/Kernel/Log.cpp deleted file mode 100644 index 89d3a9d2b53..00000000000 --- a/Kernel/FileSystem/SysFS/Subsystems/Kernel/Log.cpp +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright (c) 2022, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include - -namespace Kernel { - -UNMAP_AFTER_INIT SysFSKernelLog::SysFSKernelLog(SysFSDirectory const& parent_directory) - : SysFSGlobalInformation(parent_directory) -{ -} - -UNMAP_AFTER_INIT NonnullRefPtr SysFSKernelLog::must_create(SysFSDirectory const& parent_directory) -{ - return adopt_ref_if_nonnull(new (nothrow) SysFSKernelLog(parent_directory)).release_nonnull(); -} - -mode_t SysFSKernelLog::permissions() const -{ - return S_IRUSR; -} - -ErrorOr SysFSKernelLog::try_generate(KBufferBuilder& builder) -{ - VERIFY(DeviceManagement::the().is_console_device_attached()); - SpinlockLocker lock(g_console_lock); - for (char ch : DeviceManagement::the().console_device().logbuffer()) { - TRY(builder.append(ch)); - } - return {}; -} - -} diff --git a/Kernel/FileSystem/SysFS/Subsystems/Kernel/Log.h b/Kernel/FileSystem/SysFS/Subsystems/Kernel/Log.h deleted file mode 100644 index 2b1f51aee4b..00000000000 --- a/Kernel/FileSystem/SysFS/Subsystems/Kernel/Log.h +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright (c) 2022, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include - -namespace Kernel { - -class SysFSKernelLog final : public SysFSGlobalInformation { -public: - virtual StringView name() const override { return "dmesg"sv; } - static NonnullRefPtr must_create(SysFSDirectory const& parent_directory); - - virtual mode_t permissions() const override; - -private: - explicit SysFSKernelLog(SysFSDirectory const& parent_directory); - virtual ErrorOr try_generate(KBufferBuilder& builder) override; -}; - -} diff --git a/Kernel/FileSystem/SysFS/Subsystems/Kernel/MemoryStatus.cpp b/Kernel/FileSystem/SysFS/Subsystems/Kernel/MemoryStatus.cpp deleted file mode 100644 index dec9c0a04ec..00000000000 --- a/Kernel/FileSystem/SysFS/Subsystems/Kernel/MemoryStatus.cpp +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright (c) 2022, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include - -namespace Kernel { - -UNMAP_AFTER_INIT SysFSMemoryStatus::SysFSMemoryStatus(SysFSDirectory const& parent_directory) - : SysFSGlobalInformation(parent_directory) -{ -} - -UNMAP_AFTER_INIT NonnullRefPtr SysFSMemoryStatus::must_create(SysFSDirectory const& parent_directory) -{ - return adopt_ref_if_nonnull(new (nothrow) SysFSMemoryStatus(parent_directory)).release_nonnull(); -} - -ErrorOr SysFSMemoryStatus::try_generate(KBufferBuilder& builder) -{ - kmalloc_stats stats; - get_kmalloc_stats(stats); - - auto system_memory = MM.get_system_memory_info(); - - auto json = TRY(JsonObjectSerializer<>::try_create(builder)); - TRY(json.add("kmalloc_allocated"sv, stats.bytes_allocated)); - TRY(json.add("kmalloc_available"sv, stats.bytes_free)); - TRY(json.add("physical_allocated"sv, system_memory.physical_pages_used)); - TRY(json.add("physical_available"sv, system_memory.physical_pages - system_memory.physical_pages_used)); - TRY(json.add("physical_committed"sv, system_memory.physical_pages_committed)); - TRY(json.add("physical_uncommitted"sv, system_memory.physical_pages_uncommitted)); - TRY(json.add("kmalloc_call_count"sv, stats.kmalloc_call_count)); - TRY(json.add("kfree_call_count"sv, stats.kfree_call_count)); - TRY(json.finish()); - return {}; -} - -} diff --git a/Kernel/FileSystem/SysFS/Subsystems/Kernel/MemoryStatus.h b/Kernel/FileSystem/SysFS/Subsystems/Kernel/MemoryStatus.h deleted file mode 100644 index f4416b6cf31..00000000000 --- a/Kernel/FileSystem/SysFS/Subsystems/Kernel/MemoryStatus.h +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright (c) 2022, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include - -namespace Kernel { - -class SysFSMemoryStatus final : public SysFSGlobalInformation { -public: - virtual StringView name() const override { return "memstat"sv; } - - static NonnullRefPtr must_create(SysFSDirectory const& parent_directory); - -private: - explicit SysFSMemoryStatus(SysFSDirectory const& parent_directory); - virtual ErrorOr try_generate(KBufferBuilder& builder) override; - - virtual bool is_readable_by_jailed_processes() const override { return true; } -}; - -} diff --git a/Kernel/FileSystem/SysFS/Subsystems/Kernel/Network/ARP.cpp b/Kernel/FileSystem/SysFS/Subsystems/Kernel/Network/ARP.cpp deleted file mode 100644 index b210a5810f8..00000000000 --- a/Kernel/FileSystem/SysFS/Subsystems/Kernel/Network/ARP.cpp +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright (c) 2022, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include - -namespace Kernel { - -UNMAP_AFTER_INIT SysFSNetworkARPStats::SysFSNetworkARPStats(SysFSDirectory const& parent_directory) - : SysFSGlobalInformation(parent_directory) -{ -} - -UNMAP_AFTER_INIT NonnullRefPtr SysFSNetworkARPStats::must_create(SysFSDirectory const& parent_directory) -{ - return adopt_ref_if_nonnull(new (nothrow) SysFSNetworkARPStats(parent_directory)).release_nonnull(); -} - -ErrorOr SysFSNetworkARPStats::try_generate(KBufferBuilder& builder) -{ - auto array = TRY(JsonArraySerializer<>::try_create(builder)); - TRY(arp_table().with([&](auto const& table) -> ErrorOr { - for (auto& it : table) { - auto obj = TRY(array.add_object()); - auto mac_address = TRY(it.value.to_string()); - TRY(obj.add("mac_address"sv, mac_address->view())); - auto ip_address = TRY(it.key.to_string()); - TRY(obj.add("ip_address"sv, ip_address->view())); - TRY(obj.finish()); - } - return {}; - })); - TRY(array.finish()); - return {}; -} - -} diff --git a/Kernel/FileSystem/SysFS/Subsystems/Kernel/Network/ARP.h b/Kernel/FileSystem/SysFS/Subsystems/Kernel/Network/ARP.h deleted file mode 100644 index cb7b5e1d8c0..00000000000 --- a/Kernel/FileSystem/SysFS/Subsystems/Kernel/Network/ARP.h +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright (c) 2022, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include - -namespace Kernel { - -class SysFSNetworkARPStats final : public SysFSGlobalInformation { -public: - virtual StringView name() const override { return "arp"sv; } - static NonnullRefPtr must_create(SysFSDirectory const&); - -private: - explicit SysFSNetworkARPStats(SysFSDirectory const&); - virtual ErrorOr try_generate(KBufferBuilder& builder) override; - - virtual bool is_readable_by_jailed_processes() const override { return true; } -}; - -} diff --git a/Kernel/FileSystem/SysFS/Subsystems/Kernel/Network/Adapters.cpp b/Kernel/FileSystem/SysFS/Subsystems/Kernel/Network/Adapters.cpp deleted file mode 100644 index 9390ed49da8..00000000000 --- a/Kernel/FileSystem/SysFS/Subsystems/Kernel/Network/Adapters.cpp +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright (c) 2022, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include - -namespace Kernel { - -UNMAP_AFTER_INIT SysFSNetworkAdaptersStats::SysFSNetworkAdaptersStats(SysFSDirectory const& parent_directory) - : SysFSGlobalInformation(parent_directory) -{ -} - -UNMAP_AFTER_INIT NonnullRefPtr SysFSNetworkAdaptersStats::must_create(SysFSDirectory const& parent_directory) -{ - return adopt_ref_if_nonnull(new (nothrow) SysFSNetworkAdaptersStats(parent_directory)).release_nonnull(); -} - -ErrorOr SysFSNetworkAdaptersStats::try_generate(KBufferBuilder& builder) -{ - auto array = TRY(JsonArraySerializer<>::try_create(builder)); - TRY(NetworkingManagement::the().try_for_each([&array](auto& adapter) -> ErrorOr { - auto obj = TRY(array.add_object()); - TRY(obj.add("name"sv, adapter.name())); - TRY(obj.add("class_name"sv, adapter.class_name())); - auto mac_address = TRY(adapter.mac_address().to_string()); - TRY(obj.add("mac_address"sv, mac_address->view())); - if (!adapter.ipv4_address().is_zero()) { - auto ipv4_address = TRY(adapter.ipv4_address().to_string()); - TRY(obj.add("ipv4_address"sv, ipv4_address->view())); - auto ipv4_netmask = TRY(adapter.ipv4_netmask().to_string()); - TRY(obj.add("ipv4_netmask"sv, ipv4_netmask->view())); - } - TRY(obj.add("packets_in"sv, adapter.packets_in())); - TRY(obj.add("bytes_in"sv, adapter.bytes_in())); - TRY(obj.add("packets_out"sv, adapter.packets_out())); - TRY(obj.add("bytes_out"sv, adapter.bytes_out())); - TRY(obj.add("link_up"sv, adapter.link_up())); - TRY(obj.add("link_speed"sv, adapter.link_speed())); - TRY(obj.add("link_full_duplex"sv, adapter.link_full_duplex())); - TRY(obj.add("mtu"sv, adapter.mtu())); - TRY(obj.add("packets_dropped"sv, adapter.packets_dropped())); - TRY(obj.finish()); - return {}; - })); - TRY(array.finish()); - return {}; -} - -} diff --git a/Kernel/FileSystem/SysFS/Subsystems/Kernel/Network/Adapters.h b/Kernel/FileSystem/SysFS/Subsystems/Kernel/Network/Adapters.h deleted file mode 100644 index 2929e008192..00000000000 --- a/Kernel/FileSystem/SysFS/Subsystems/Kernel/Network/Adapters.h +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright (c) 2022, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include - -namespace Kernel { - -class SysFSNetworkAdaptersStats final : public SysFSGlobalInformation { -public: - virtual StringView name() const override { return "adapters"sv; } - static NonnullRefPtr must_create(SysFSDirectory const&); - -private: - explicit SysFSNetworkAdaptersStats(SysFSDirectory const&); - virtual ErrorOr try_generate(KBufferBuilder& builder) override; - - virtual bool is_readable_by_jailed_processes() const override { return true; } -}; - -} diff --git a/Kernel/FileSystem/SysFS/Subsystems/Kernel/Network/Directory.cpp b/Kernel/FileSystem/SysFS/Subsystems/Kernel/Network/Directory.cpp deleted file mode 100644 index 9027043b9fd..00000000000 --- a/Kernel/FileSystem/SysFS/Subsystems/Kernel/Network/Directory.cpp +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright (c) 2022, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace Kernel { - -UNMAP_AFTER_INIT NonnullRefPtr SysFSGlobalNetworkStatsDirectory::must_create(SysFSDirectory const& parent_directory) -{ - auto global_network_stats_directory = adopt_ref_if_nonnull(new (nothrow) SysFSGlobalNetworkStatsDirectory(parent_directory)).release_nonnull(); - MUST(global_network_stats_directory->m_child_components.with([&](auto& list) -> ErrorOr { - list.append(SysFSNetworkAdaptersStats::must_create(*global_network_stats_directory)); - list.append(SysFSNetworkARPStats::must_create(*global_network_stats_directory)); - list.append(SysFSNetworkRouteStats::must_create(*global_network_stats_directory)); - list.append(SysFSNetworkTCPStats::must_create(*global_network_stats_directory)); - list.append(SysFSLocalNetStats::must_create(*global_network_stats_directory)); - list.append(SysFSNetworkUDPStats::must_create(*global_network_stats_directory)); - return {}; - })); - return global_network_stats_directory; -} - -UNMAP_AFTER_INIT SysFSGlobalNetworkStatsDirectory::SysFSGlobalNetworkStatsDirectory(SysFSDirectory const& parent_directory) - : SysFSDirectory(parent_directory) -{ -} - -} diff --git a/Kernel/FileSystem/SysFS/Subsystems/Kernel/Network/Directory.h b/Kernel/FileSystem/SysFS/Subsystems/Kernel/Network/Directory.h deleted file mode 100644 index 7679193aa52..00000000000 --- a/Kernel/FileSystem/SysFS/Subsystems/Kernel/Network/Directory.h +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright (c) 2021, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include - -namespace Kernel { - -class SysFSGlobalNetworkStatsDirectory : public SysFSDirectory { -public: - static NonnullRefPtr must_create(SysFSDirectory const&); - virtual StringView name() const override { return "net"sv; } - -private: - explicit SysFSGlobalNetworkStatsDirectory(SysFSDirectory const&); -}; - -} diff --git a/Kernel/FileSystem/SysFS/Subsystems/Kernel/Network/Local.cpp b/Kernel/FileSystem/SysFS/Subsystems/Kernel/Network/Local.cpp deleted file mode 100644 index d44e6653763..00000000000 --- a/Kernel/FileSystem/SysFS/Subsystems/Kernel/Network/Local.cpp +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright (c) 2022, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include - -namespace Kernel { - -UNMAP_AFTER_INIT SysFSLocalNetStats::SysFSLocalNetStats(SysFSDirectory const& parent_directory) - : SysFSGlobalInformation(parent_directory) -{ -} - -UNMAP_AFTER_INIT NonnullRefPtr SysFSLocalNetStats::must_create(SysFSDirectory const& parent_directory) -{ - return adopt_ref_if_nonnull(new (nothrow) SysFSLocalNetStats(parent_directory)).release_nonnull(); -} - -ErrorOr SysFSLocalNetStats::try_generate(KBufferBuilder& builder) -{ - auto array = TRY(JsonArraySerializer<>::try_create(builder)); - TRY(LocalSocket::try_for_each([&array](auto& socket) -> ErrorOr { - auto obj = TRY(array.add_object()); - TRY(obj.add("path"sv, socket.socket_path())); - TRY(obj.add("origin_pid"sv, socket.origin_pid().value())); - TRY(obj.add("origin_uid"sv, socket.origin_uid().value())); - TRY(obj.add("origin_gid"sv, socket.origin_gid().value())); - TRY(obj.add("acceptor_pid"sv, socket.acceptor_pid().value())); - TRY(obj.add("acceptor_uid"sv, socket.acceptor_uid().value())); - TRY(obj.add("acceptor_gid"sv, socket.acceptor_gid().value())); - TRY(obj.finish()); - return {}; - })); - TRY(array.finish()); - return {}; -} - -} diff --git a/Kernel/FileSystem/SysFS/Subsystems/Kernel/Network/Local.h b/Kernel/FileSystem/SysFS/Subsystems/Kernel/Network/Local.h deleted file mode 100644 index 6ae04a07e4b..00000000000 --- a/Kernel/FileSystem/SysFS/Subsystems/Kernel/Network/Local.h +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright (c) 2022, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include - -namespace Kernel { - -class SysFSLocalNetStats final : public SysFSGlobalInformation { -public: - virtual StringView name() const override { return "local"sv; } - static NonnullRefPtr must_create(SysFSDirectory const&); - -private: - explicit SysFSLocalNetStats(SysFSDirectory const&); - virtual ErrorOr try_generate(KBufferBuilder& builder) override; - - virtual bool is_readable_by_jailed_processes() const override { return true; } -}; - -} diff --git a/Kernel/FileSystem/SysFS/Subsystems/Kernel/Network/Route.cpp b/Kernel/FileSystem/SysFS/Subsystems/Kernel/Network/Route.cpp deleted file mode 100644 index e6d0ba58e06..00000000000 --- a/Kernel/FileSystem/SysFS/Subsystems/Kernel/Network/Route.cpp +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright (c) 2022, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include - -namespace Kernel { - -UNMAP_AFTER_INIT SysFSNetworkRouteStats::SysFSNetworkRouteStats(SysFSDirectory const& parent_directory) - : SysFSGlobalInformation(parent_directory) -{ -} - -UNMAP_AFTER_INIT NonnullRefPtr SysFSNetworkRouteStats::must_create(SysFSDirectory const& parent_directory) -{ - return adopt_ref_if_nonnull(new (nothrow) SysFSNetworkRouteStats(parent_directory)).release_nonnull(); -} - -ErrorOr SysFSNetworkRouteStats::try_generate(KBufferBuilder& builder) -{ - auto array = TRY(JsonArraySerializer<>::try_create(builder)); - TRY(routing_table().with([&](auto const& table) -> ErrorOr { - for (auto& it : table) { - auto obj = TRY(array.add_object()); - auto destination = TRY(it.destination.to_string()); - TRY(obj.add("destination"sv, destination->view())); - auto gateway = TRY(it.gateway.to_string()); - TRY(obj.add("gateway"sv, gateway->view())); - auto netmask = TRY(it.netmask.to_string()); - TRY(obj.add("genmask"sv, netmask->view())); - TRY(obj.add("flags"sv, it.flags)); - TRY(obj.add("interface"sv, it.adapter->name())); - TRY(obj.finish()); - } - return {}; - })); - TRY(array.finish()); - return {}; -} - -} diff --git a/Kernel/FileSystem/SysFS/Subsystems/Kernel/Network/Route.h b/Kernel/FileSystem/SysFS/Subsystems/Kernel/Network/Route.h deleted file mode 100644 index 20369de970e..00000000000 --- a/Kernel/FileSystem/SysFS/Subsystems/Kernel/Network/Route.h +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright (c) 2022, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include - -namespace Kernel { - -class SysFSNetworkRouteStats final : public SysFSGlobalInformation { -public: - virtual StringView name() const override { return "route"sv; } - static NonnullRefPtr must_create(SysFSDirectory const&); - -private: - explicit SysFSNetworkRouteStats(SysFSDirectory const&); - virtual ErrorOr try_generate(KBufferBuilder& builder) override; - - virtual bool is_readable_by_jailed_processes() const override { return true; } -}; - -} diff --git a/Kernel/FileSystem/SysFS/Subsystems/Kernel/Network/TCP.cpp b/Kernel/FileSystem/SysFS/Subsystems/Kernel/Network/TCP.cpp deleted file mode 100644 index a7e10b38b9c..00000000000 --- a/Kernel/FileSystem/SysFS/Subsystems/Kernel/Network/TCP.cpp +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright (c) 2022, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include -#include - -namespace Kernel { - -UNMAP_AFTER_INIT SysFSNetworkTCPStats::SysFSNetworkTCPStats(SysFSDirectory const& parent_directory) - : SysFSGlobalInformation(parent_directory) -{ -} - -UNMAP_AFTER_INIT NonnullRefPtr SysFSNetworkTCPStats::must_create(SysFSDirectory const& parent_directory) -{ - return adopt_ref_if_nonnull(new (nothrow) SysFSNetworkTCPStats(parent_directory)).release_nonnull(); -} - -ErrorOr SysFSNetworkTCPStats::try_generate(KBufferBuilder& builder) -{ - auto array = TRY(JsonArraySerializer<>::try_create(builder)); - TRY(TCPSocket::try_for_each([&array](auto& socket) -> ErrorOr { - auto obj = TRY(array.add_object()); - auto local_address = TRY(socket.local_address().to_string()); - TRY(obj.add("local_address"sv, local_address->view())); - TRY(obj.add("local_port"sv, socket.local_port())); - auto peer_address = TRY(socket.peer_address().to_string()); - TRY(obj.add("peer_address"sv, peer_address->view())); - TRY(obj.add("peer_port"sv, socket.peer_port())); - TRY(obj.add("state"sv, TCPSocket::to_string(socket.state()))); - TRY(obj.add("ack_number"sv, socket.ack_number())); - TRY(obj.add("sequence_number"sv, socket.sequence_number())); - TRY(obj.add("packets_in"sv, socket.packets_in())); - TRY(obj.add("bytes_in"sv, socket.bytes_in())); - TRY(obj.add("packets_out"sv, socket.packets_out())); - TRY(obj.add("bytes_out"sv, socket.bytes_out())); - auto current_process_credentials = Process::current().credentials(); - if (current_process_credentials->is_superuser() || current_process_credentials->uid() == socket.origin_uid()) { - TRY(obj.add("origin_pid"sv, socket.origin_pid().value())); - TRY(obj.add("origin_uid"sv, socket.origin_uid().value())); - TRY(obj.add("origin_gid"sv, socket.origin_gid().value())); - } - TRY(obj.finish()); - return {}; - })); - TRY(array.finish()); - return {}; -} - -} diff --git a/Kernel/FileSystem/SysFS/Subsystems/Kernel/Network/TCP.h b/Kernel/FileSystem/SysFS/Subsystems/Kernel/Network/TCP.h deleted file mode 100644 index 4cd5942a97d..00000000000 --- a/Kernel/FileSystem/SysFS/Subsystems/Kernel/Network/TCP.h +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright (c) 2022, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include - -namespace Kernel { - -class SysFSNetworkTCPStats final : public SysFSGlobalInformation { -public: - virtual StringView name() const override { return "tcp"sv; } - static NonnullRefPtr must_create(SysFSDirectory const&); - -private: - explicit SysFSNetworkTCPStats(SysFSDirectory const&); - virtual ErrorOr try_generate(KBufferBuilder& builder) override; - - virtual bool is_readable_by_jailed_processes() const override { return true; } -}; - -} diff --git a/Kernel/FileSystem/SysFS/Subsystems/Kernel/Network/UDP.cpp b/Kernel/FileSystem/SysFS/Subsystems/Kernel/Network/UDP.cpp deleted file mode 100644 index d1ea5135063..00000000000 --- a/Kernel/FileSystem/SysFS/Subsystems/Kernel/Network/UDP.cpp +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright (c) 2022, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include - -namespace Kernel { - -UNMAP_AFTER_INIT SysFSNetworkUDPStats::SysFSNetworkUDPStats(SysFSDirectory const& parent_directory) - : SysFSGlobalInformation(parent_directory) -{ -} - -UNMAP_AFTER_INIT NonnullRefPtr SysFSNetworkUDPStats::must_create(SysFSDirectory const& parent_directory) -{ - return adopt_ref_if_nonnull(new (nothrow) SysFSNetworkUDPStats(parent_directory)).release_nonnull(); -} - -ErrorOr SysFSNetworkUDPStats::try_generate(KBufferBuilder& builder) -{ - auto array = TRY(JsonArraySerializer<>::try_create(builder)); - TRY(UDPSocket::try_for_each([&array](auto& socket) -> ErrorOr { - auto obj = TRY(array.add_object()); - auto local_address = TRY(socket.local_address().to_string()); - TRY(obj.add("local_address"sv, local_address->view())); - TRY(obj.add("local_port"sv, socket.local_port())); - auto peer_address = TRY(socket.peer_address().to_string()); - TRY(obj.add("peer_address"sv, peer_address->view())); - TRY(obj.add("peer_port"sv, socket.peer_port())); - auto current_process_credentials = Process::current().credentials(); - if (current_process_credentials->is_superuser() || current_process_credentials->uid() == socket.origin_uid()) { - TRY(obj.add("origin_pid"sv, socket.origin_pid().value())); - TRY(obj.add("origin_uid"sv, socket.origin_uid().value())); - TRY(obj.add("origin_gid"sv, socket.origin_gid().value())); - } - TRY(obj.finish()); - return {}; - })); - TRY(array.finish()); - return {}; -} - -} diff --git a/Kernel/FileSystem/SysFS/Subsystems/Kernel/Network/UDP.h b/Kernel/FileSystem/SysFS/Subsystems/Kernel/Network/UDP.h deleted file mode 100644 index 2696e12d3ba..00000000000 --- a/Kernel/FileSystem/SysFS/Subsystems/Kernel/Network/UDP.h +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright (c) 2022, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include - -namespace Kernel { - -class SysFSNetworkUDPStats final : public SysFSGlobalInformation { -public: - virtual StringView name() const override { return "udp"sv; } - static NonnullRefPtr must_create(SysFSDirectory const&); - -private: - explicit SysFSNetworkUDPStats(SysFSDirectory const&); - virtual ErrorOr try_generate(KBufferBuilder& builder) override; - - virtual bool is_readable_by_jailed_processes() const override { return true; } -}; - -} diff --git a/Kernel/FileSystem/SysFS/Subsystems/Kernel/PowerStateSwitch.cpp b/Kernel/FileSystem/SysFS/Subsystems/Kernel/PowerStateSwitch.cpp deleted file mode 100644 index ef9e487cfdd..00000000000 --- a/Kernel/FileSystem/SysFS/Subsystems/Kernel/PowerStateSwitch.cpp +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * Copyright (c) 2021, Liav A. - * Copyright (c) 2023, kleines Filmröllchen - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include - -namespace Kernel { - -mode_t SysFSPowerStateSwitchNode::permissions() const -{ - return S_IRUSR | S_IRGRP | S_IWUSR | S_IWGRP; -} - -UNMAP_AFTER_INIT NonnullRefPtr SysFSPowerStateSwitchNode::must_create(SysFSDirectory const& parent_directory) -{ - return adopt_ref_if_nonnull(new (nothrow) SysFSPowerStateSwitchNode(parent_directory)).release_nonnull(); -} - -UNMAP_AFTER_INIT SysFSPowerStateSwitchNode::SysFSPowerStateSwitchNode(SysFSDirectory const& parent_directory) - : SysFSComponent(parent_directory) -{ -} - -ErrorOr SysFSPowerStateSwitchNode::truncate(u64 size) -{ - // Note: This node doesn't store any useful data anyway, so we can safely - // truncate this to zero (essentially ignoring the request without failing). - if (size != 0) - return EPERM; - return {}; -} - -ErrorOr SysFSPowerStateSwitchNode::write_bytes(off_t offset, size_t count, UserOrKernelBuffer const& data, OpenFileDescription*) -{ - // Note: If we are in a jail, don't let the current process to change the power state. - if (Process::current().is_currently_in_jail()) - return Error::from_errno(EPERM); - if (Checked::addition_would_overflow(offset, count)) - return Error::from_errno(EOVERFLOW); - if (offset > 0) - return Error::from_errno(EINVAL); - if (count > 1) - return Error::from_errno(EINVAL); - char buf[1]; - TRY(data.read(buf, 1)); - switch (buf[0]) { - case '1': - PowerStateSwitchTask::reboot(); - return 1; - case '2': - PowerStateSwitchTask::shutdown(); - return 1; - default: - return Error::from_errno(EINVAL); - } -} - -} diff --git a/Kernel/FileSystem/SysFS/Subsystems/Kernel/PowerStateSwitch.h b/Kernel/FileSystem/SysFS/Subsystems/Kernel/PowerStateSwitch.h deleted file mode 100644 index 47ee58f1c59..00000000000 --- a/Kernel/FileSystem/SysFS/Subsystems/Kernel/PowerStateSwitch.h +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright (c) 2020, Andreas Kling - * Copyright (c) 2021, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace Kernel { - -class SysFSPowerStateSwitchNode final : public SysFSComponent { -public: - virtual StringView name() const override { return "power_state"sv; } - static NonnullRefPtr must_create(SysFSDirectory const&); - virtual mode_t permissions() const override; - virtual ErrorOr write_bytes(off_t, size_t, UserOrKernelBuffer const&, OpenFileDescription*) override; - virtual ErrorOr truncate(u64) override; - -private: - explicit SysFSPowerStateSwitchNode(SysFSDirectory const&); -}; - -} diff --git a/Kernel/FileSystem/SysFS/Subsystems/Kernel/Processes.cpp b/Kernel/FileSystem/SysFS/Subsystems/Kernel/Processes.cpp deleted file mode 100644 index a7c0566aa29..00000000000 --- a/Kernel/FileSystem/SysFS/Subsystems/Kernel/Processes.cpp +++ /dev/null @@ -1,168 +0,0 @@ -/* - * Copyright (c) 2022, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include -#include -#include - -namespace Kernel { - -UNMAP_AFTER_INIT SysFSOverallProcesses::SysFSOverallProcesses(SysFSDirectory const& parent_directory) - : SysFSGlobalInformation(parent_directory) -{ -} - -UNMAP_AFTER_INIT NonnullRefPtr SysFSOverallProcesses::must_create(SysFSDirectory const& parent_directory) -{ - return adopt_ref_if_nonnull(new (nothrow) SysFSOverallProcesses(parent_directory)).release_nonnull(); -} - -ErrorOr SysFSOverallProcesses::try_generate(KBufferBuilder& builder) -{ - auto json = TRY(JsonObjectSerializer<>::try_create(builder)); - - // Keep this in sync with CProcessStatistics. - auto build_process = [&](JsonArraySerializer& array, Process const& process) -> ErrorOr { - auto process_object = TRY(array.add_object()); - - if (process.is_user_process()) { - StringBuilder pledge_builder; - -#define __ENUMERATE_PLEDGE_PROMISE(promise) \ - if (process.has_promised(Pledge::promise)) \ - TRY(pledge_builder.try_append(#promise " "sv)); - ENUMERATE_PLEDGE_PROMISES -#undef __ENUMERATE_PLEDGE_PROMISE - - TRY(process_object.add("pledge"sv, pledge_builder.string_view())); - - switch (process.veil_state()) { - case VeilState::None: - TRY(process_object.add("veil"sv, "None")); - break; - case VeilState::Dropped: - TRY(process_object.add("veil"sv, "Dropped")); - break; - case VeilState::Locked: - TRY(process_object.add("veil"sv, "Locked")); - break; - case VeilState::LockedInherited: - // Note: We don't reveal if the locked state is either by our choice - // or someone else applied it. - TRY(process_object.add("veil"sv, "Locked")); - break; - } - } else { - TRY(process_object.add("pledge"sv, ""sv)); - TRY(process_object.add("veil"sv, ""sv)); - } - - TRY(process_object.add("pid"sv, process.pid().value())); - ProcessGroupID tty_pgid = 0; - if (auto tty = process.tty()) - tty_pgid = tty->pgid(); - TRY(process_object.add("pgid"sv, tty_pgid.value())); - TRY(process_object.add("pgp"sv, process.pgid().value())); - TRY(process_object.add("sid"sv, process.sid().value())); - auto credentials = process.credentials(); - TRY(process_object.add("uid"sv, credentials->uid().value())); - TRY(process_object.add("gid"sv, credentials->gid().value())); - TRY(process_object.add("ppid"sv, process.ppid().value())); - if (process.tty()) { - auto tty_pseudo_name = TRY(process.tty()->pseudo_name()); - TRY(process_object.add("tty"sv, tty_pseudo_name->view())); - } else { - TRY(process_object.add("tty"sv, "")); - } - TRY(process.name().with([&](auto& process_name) { return process_object.add("name"sv, process_name.representable_view()); })); - TRY(process_object.add("executable"sv, process.executable() ? TRY(process.executable()->try_serialize_absolute_path())->view() : ""sv)); - TRY(process_object.add("creation_time"sv, process.creation_time().nanoseconds_since_epoch())); - - size_t amount_virtual = 0; - size_t amount_resident = 0; - size_t amount_dirty_private = 0; - size_t amount_clean_inode = 0; - size_t amount_shared = 0; - size_t amount_purgeable_volatile = 0; - size_t amount_purgeable_nonvolatile = 0; - - TRY(process.address_space().with([&](auto& space) -> ErrorOr { - amount_virtual = space->amount_virtual(); - amount_resident = space->amount_resident(); - amount_dirty_private = space->amount_dirty_private(); - amount_clean_inode = TRY(space->amount_clean_inode()); - amount_shared = space->amount_shared(); - amount_purgeable_volatile = space->amount_purgeable_volatile(); - amount_purgeable_nonvolatile = space->amount_purgeable_nonvolatile(); - return {}; - })); - - TRY(process_object.add("amount_virtual"sv, amount_virtual)); - TRY(process_object.add("amount_resident"sv, amount_resident)); - TRY(process_object.add("amount_dirty_private"sv, amount_dirty_private)); - TRY(process_object.add("amount_clean_inode"sv, amount_clean_inode)); - TRY(process_object.add("amount_shared"sv, amount_shared)); - TRY(process_object.add("amount_purgeable_volatile"sv, amount_purgeable_volatile)); - TRY(process_object.add("amount_purgeable_nonvolatile"sv, amount_purgeable_nonvolatile)); - TRY(process_object.add("dumpable"sv, process.is_dumpable())); - TRY(process_object.add("kernel"sv, process.is_kernel_process())); - auto thread_array = TRY(process_object.add_array("threads"sv)); - TRY(process.try_for_each_thread([&](Thread const& thread) -> ErrorOr { - SpinlockLocker locker(thread.get_lock()); - auto thread_object = TRY(thread_array.add_object()); -#if LOCK_DEBUG - TRY(thread_object.add("lock_count"sv, thread.lock_count())); -#endif - TRY(thread_object.add("tid"sv, thread.tid().value())); - TRY(thread.name().with([&](auto& thread_name) { return thread_object.add("name"sv, thread_name.representable_view()); })); - TRY(thread_object.add("times_scheduled"sv, thread.times_scheduled())); - TRY(thread_object.add("time_user"sv, thread.time_in_user())); - TRY(thread_object.add("time_kernel"sv, thread.time_in_kernel())); - TRY(thread_object.add("state"sv, thread.state_string())); - TRY(thread_object.add("cpu"sv, thread.cpu())); - TRY(thread_object.add("priority"sv, thread.priority())); - TRY(thread_object.add("syscall_count"sv, thread.syscall_count())); - TRY(thread_object.add("inode_faults"sv, thread.inode_faults())); - TRY(thread_object.add("zero_faults"sv, thread.zero_faults())); - TRY(thread_object.add("cow_faults"sv, thread.cow_faults())); - TRY(thread_object.add("file_read_bytes"sv, thread.file_read_bytes())); - TRY(thread_object.add("file_write_bytes"sv, thread.file_write_bytes())); - TRY(thread_object.add("unix_socket_read_bytes"sv, thread.unix_socket_read_bytes())); - TRY(thread_object.add("unix_socket_write_bytes"sv, thread.unix_socket_write_bytes())); - TRY(thread_object.add("ipv4_socket_read_bytes"sv, thread.ipv4_socket_read_bytes())); - TRY(thread_object.add("ipv4_socket_write_bytes"sv, thread.ipv4_socket_write_bytes())); - - TRY(thread_object.finish()); - return {}; - })); - TRY(thread_array.finish()); - TRY(process_object.finish()); - return {}; - }; - - { - auto array = TRY(json.add_array("processes"sv)); - // FIXME: Do we actually want to expose the colonel process in a Jail environment? - TRY(build_process(array, *Scheduler::colonel())); - TRY(Process::for_each_in_same_jail([&](Process& process) -> ErrorOr { - TRY(build_process(array, process)); - return {}; - })); - TRY(array.finish()); - } - - auto total_time_scheduled = Scheduler::get_total_time_scheduled(); - TRY(json.add("total_time"sv, total_time_scheduled.total)); - TRY(json.add("total_time_kernel"sv, total_time_scheduled.total_kernel)); - TRY(json.finish()); - return {}; -} - -} diff --git a/Kernel/FileSystem/SysFS/Subsystems/Kernel/Processes.h b/Kernel/FileSystem/SysFS/Subsystems/Kernel/Processes.h deleted file mode 100644 index d75165940f8..00000000000 --- a/Kernel/FileSystem/SysFS/Subsystems/Kernel/Processes.h +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright (c) 2022, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include - -namespace Kernel { - -class SysFSOverallProcesses final : public SysFSGlobalInformation { -public: - virtual StringView name() const override { return "processes"sv; } - - static NonnullRefPtr must_create(SysFSDirectory const& parent_directory); - -private: - explicit SysFSOverallProcesses(SysFSDirectory const& parent_directory); - virtual ErrorOr try_generate(KBufferBuilder& builder) override; - - virtual bool is_readable_by_jailed_processes() const override { return true; } -}; - -} diff --git a/Kernel/FileSystem/SysFS/Subsystems/Kernel/Profile.cpp b/Kernel/FileSystem/SysFS/Subsystems/Kernel/Profile.cpp deleted file mode 100644 index 51622d358d1..00000000000 --- a/Kernel/FileSystem/SysFS/Subsystems/Kernel/Profile.cpp +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright (c) 2022, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include - -namespace Kernel { - -UNMAP_AFTER_INIT SysFSProfile::SysFSProfile(SysFSDirectory const& parent_directory) - : SysFSGlobalInformation(parent_directory) -{ -} - -UNMAP_AFTER_INIT NonnullRefPtr SysFSProfile::must_create(SysFSDirectory const& parent_directory) -{ - return adopt_ref_if_nonnull(new (nothrow) SysFSProfile(parent_directory)).release_nonnull(); -} - -ErrorOr SysFSProfile::try_generate(KBufferBuilder& builder) -{ - if (!g_global_perf_events) - return ENOENT; - TRY(g_global_perf_events->to_json(builder)); - return {}; -} - -mode_t SysFSProfile::permissions() const -{ - return S_IRUSR; -} - -} diff --git a/Kernel/FileSystem/SysFS/Subsystems/Kernel/Profile.h b/Kernel/FileSystem/SysFS/Subsystems/Kernel/Profile.h deleted file mode 100644 index 1aec67990f9..00000000000 --- a/Kernel/FileSystem/SysFS/Subsystems/Kernel/Profile.h +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright (c) 2022, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include - -namespace Kernel { - -class SysFSProfile final : public SysFSGlobalInformation { -public: - virtual StringView name() const override { return "profile"sv; } - - static NonnullRefPtr must_create(SysFSDirectory const& parent_directory); - -private: - virtual mode_t permissions() const override; - - explicit SysFSProfile(SysFSDirectory const& parent_directory); - virtual ErrorOr try_generate(KBufferBuilder& builder) override; -}; - -} diff --git a/Kernel/FileSystem/SysFS/Subsystems/Kernel/RequestPanic.cpp b/Kernel/FileSystem/SysFS/Subsystems/Kernel/RequestPanic.cpp deleted file mode 100644 index efc12a9e56d..00000000000 --- a/Kernel/FileSystem/SysFS/Subsystems/Kernel/RequestPanic.cpp +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright (c) 2023, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include - -namespace Kernel { - -UNMAP_AFTER_INIT NonnullRefPtr SysFSSystemRequestPanic::must_create(SysFSDirectory const& parent_directory) -{ - return adopt_ref_if_nonnull(new (nothrow) SysFSSystemRequestPanic(parent_directory)).release_nonnull(); -} - -static ErrorOr check_current_process_not_jailed() -{ - return Process::current().jail().with([&](auto const& my_jail) -> ErrorOr { - if (my_jail) - return Error::from_errno(EPERM); - return {}; - }); -} - -ErrorOr SysFSSystemRequestPanic::read_bytes(off_t, size_t, UserOrKernelBuffer&, OpenFileDescription*) const -{ - TRY(check_current_process_not_jailed()); - return Error::from_errno(ENOTSUP); -} - -ErrorOr SysFSSystemRequestPanic::write_bytes(off_t, size_t, UserOrKernelBuffer const&, OpenFileDescription*) -{ - TRY(check_current_process_not_jailed()); - PANIC("SysFSSystemRequestPanic::write_bytes"); - VERIFY_NOT_REACHED(); -} - -ErrorOr SysFSSystemRequestPanic::truncate(u64) -{ - TRY(check_current_process_not_jailed()); - PANIC("SysFSSystemRequestPanic::truncate"); - VERIFY_NOT_REACHED(); -} - -} diff --git a/Kernel/FileSystem/SysFS/Subsystems/Kernel/RequestPanic.h b/Kernel/FileSystem/SysFS/Subsystems/Kernel/RequestPanic.h deleted file mode 100644 index a4daaf9835a..00000000000 --- a/Kernel/FileSystem/SysFS/Subsystems/Kernel/RequestPanic.h +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright (c) 2023, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include -#include -#include -#include - -namespace Kernel { - -class SysFSSystemRequestPanic final : public SysFSComponent { -public: - static NonnullRefPtr must_create(SysFSDirectory const& parent_directory); - virtual StringView name() const override { return "request_panic"sv; } - -private: - explicit SysFSSystemRequestPanic(SysFSDirectory const& parent_directory) - : SysFSComponent(parent_directory) - { - } - - // ^SysFSComponent - virtual ErrorOr read_bytes(off_t offset, size_t count, UserOrKernelBuffer& buffer, OpenFileDescription* description) const override; - virtual ErrorOr write_bytes(off_t, size_t, UserOrKernelBuffer const&, OpenFileDescription*) override; - virtual mode_t permissions() const override { return 0600; } - virtual ErrorOr truncate(u64) override; -}; - -} diff --git a/Kernel/FileSystem/SysFS/Subsystems/Kernel/SystemStatistics.cpp b/Kernel/FileSystem/SysFS/Subsystems/Kernel/SystemStatistics.cpp deleted file mode 100644 index 909ab9f1056..00000000000 --- a/Kernel/FileSystem/SysFS/Subsystems/Kernel/SystemStatistics.cpp +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright (c) 2022, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include - -namespace Kernel { - -UNMAP_AFTER_INIT SysFSSystemStatistics::SysFSSystemStatistics(SysFSDirectory const& parent_directory) - : SysFSGlobalInformation(parent_directory) -{ -} - -UNMAP_AFTER_INIT NonnullRefPtr SysFSSystemStatistics::must_create(SysFSDirectory const& parent_directory) -{ - return adopt_ref_if_nonnull(new (nothrow) SysFSSystemStatistics(parent_directory)).release_nonnull(); -} - -ErrorOr SysFSSystemStatistics::try_generate(KBufferBuilder& builder) -{ - auto json = TRY(JsonObjectSerializer<>::try_create(builder)); - auto total_time_scheduled = Scheduler::get_total_time_scheduled(); - TRY(json.add("total_time"sv, total_time_scheduled.total)); - TRY(json.add("kernel_time"sv, total_time_scheduled.total_kernel)); - TRY(json.add("user_time"sv, total_time_scheduled.total - total_time_scheduled.total_kernel)); - u64 idle_time = 0; - Processor::for_each([&](Processor& processor) { - idle_time += processor.time_spent_idle(); - }); - TRY(json.add("idle_time"sv, idle_time)); - TRY(json.finish()); - return {}; -} - -} diff --git a/Kernel/FileSystem/SysFS/Subsystems/Kernel/SystemStatistics.h b/Kernel/FileSystem/SysFS/Subsystems/Kernel/SystemStatistics.h deleted file mode 100644 index b7c552eacdc..00000000000 --- a/Kernel/FileSystem/SysFS/Subsystems/Kernel/SystemStatistics.h +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright (c) 2022, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include - -namespace Kernel { - -class SysFSSystemStatistics final : public SysFSGlobalInformation { -public: - virtual StringView name() const override { return "stats"sv; } - - static NonnullRefPtr must_create(SysFSDirectory const& parent_directory); - -private: - explicit SysFSSystemStatistics(SysFSDirectory const& parent_directory); - virtual ErrorOr try_generate(KBufferBuilder& builder) override; - - virtual bool is_readable_by_jailed_processes() const override { return true; } -}; - -} diff --git a/Kernel/FileSystem/SysFS/Subsystems/Kernel/Uptime.cpp b/Kernel/FileSystem/SysFS/Subsystems/Kernel/Uptime.cpp deleted file mode 100644 index 0598cf0eac0..00000000000 --- a/Kernel/FileSystem/SysFS/Subsystems/Kernel/Uptime.cpp +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright (c) 2022, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include - -namespace Kernel { - -UNMAP_AFTER_INIT SysFSUptime::SysFSUptime(SysFSDirectory const& parent_directory) - : SysFSGlobalInformation(parent_directory) -{ -} - -UNMAP_AFTER_INIT NonnullRefPtr SysFSUptime::must_create(SysFSDirectory const& parent_directory) -{ - return adopt_ref_if_nonnull(new (nothrow) SysFSUptime(parent_directory)).release_nonnull(); -} - -ErrorOr SysFSUptime::try_generate(KBufferBuilder& builder) -{ - return builder.appendff("{}\n", TimeManagement::the().uptime_ms() / 1000); -} - -} diff --git a/Kernel/FileSystem/SysFS/Subsystems/Kernel/Uptime.h b/Kernel/FileSystem/SysFS/Subsystems/Kernel/Uptime.h deleted file mode 100644 index 6e6ed4b8197..00000000000 --- a/Kernel/FileSystem/SysFS/Subsystems/Kernel/Uptime.h +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright (c) 2022, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include - -namespace Kernel { - -class SysFSUptime final : public SysFSGlobalInformation { -public: - virtual StringView name() const override { return "uptime"sv; } - static NonnullRefPtr must_create(SysFSDirectory const& parent_directory); - -private: - explicit SysFSUptime(SysFSDirectory const& parent_directory); - virtual ErrorOr try_generate(KBufferBuilder& builder) override; - - virtual bool is_readable_by_jailed_processes() const override { return true; } -}; - -} diff --git a/Kernel/FileSystem/UnveilNode.h b/Kernel/FileSystem/UnveilNode.h deleted file mode 100644 index da541e2e342..00000000000 --- a/Kernel/FileSystem/UnveilNode.h +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright (c) 2020, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include - -namespace Kernel { - -enum UnveilAccess { - Read = 1, - Write = 2, - Execute = 4, - CreateOrRemove = 8, - Browse = 16, - - None = 0, -}; - -struct UnveilNode; - -struct UnveilMetadata { - NonnullOwnPtr full_path; - UnveilAccess permissions { None }; - bool explicitly_unveiled { false }; - - UnveilMetadata(UnveilMetadata const&) = delete; - UnveilMetadata(UnveilMetadata&&) = default; - - // Note: Intentionally not explicit. - UnveilMetadata(NonnullOwnPtr&& full_path, UnveilAccess permissions = None, bool explicitly_unveiled = false) - : full_path(move(full_path)) - , permissions(permissions) - , explicitly_unveiled(explicitly_unveiled) - { - } - - ErrorOr copy() const - { - return UnveilMetadata { - TRY(full_path->try_clone()), - permissions, - explicitly_unveiled, - }; - } -}; - -struct UnveilNode final : public Trie, UnveilMetadata, Traits>, UnveilNode> { - using Trie, UnveilMetadata, Traits>, UnveilNode>::Trie; - - bool was_explicitly_unveiled() const { return this->metadata_value().explicitly_unveiled; } - UnveilAccess permissions() const { return this->metadata_value().permissions; } - StringView path() const { return this->metadata_value().full_path->view(); } -}; - -} diff --git a/Kernel/FileSystem/VirtualFileSystem.cpp b/Kernel/FileSystem/VirtualFileSystem.cpp deleted file mode 100644 index 32243f78e62..00000000000 --- a/Kernel/FileSystem/VirtualFileSystem.cpp +++ /dev/null @@ -1,1289 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * Copyright (c) 2022-2023, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace Kernel { - -static Singleton s_the; -static constexpr int root_mount_flags = 0; - -static ErrorOr handle_mount_boolean_flag_as_invalid(Span, StringView, bool) -{ - return EINVAL; -} - -static ErrorOr handle_mount_unsigned_integer_flag_as_invalid(Span, StringView, u64) -{ - return EINVAL; -} - -static ErrorOr handle_mount_signed_integer_flag_as_invalid(Span, StringView, i64) -{ - return EINVAL; -} - -static ErrorOr handle_mount_ascii_string_flag_as_invalid(Span, StringView, StringView) -{ - return EINVAL; -} - -static constexpr FileSystemInitializer s_initializers[] = { - { "proc"sv, "ProcFS"sv, false, false, false, {}, ProcFS::try_create, handle_mount_boolean_flag_as_invalid, handle_mount_unsigned_integer_flag_as_invalid, handle_mount_signed_integer_flag_as_invalid, handle_mount_ascii_string_flag_as_invalid }, - { "devpts"sv, "DevPtsFS"sv, false, false, false, {}, DevPtsFS::try_create, handle_mount_boolean_flag_as_invalid, handle_mount_unsigned_integer_flag_as_invalid, handle_mount_signed_integer_flag_as_invalid, handle_mount_ascii_string_flag_as_invalid }, - { "sys"sv, "SysFS"sv, false, false, false, {}, SysFS::try_create, handle_mount_boolean_flag_as_invalid, handle_mount_unsigned_integer_flag_as_invalid, handle_mount_signed_integer_flag_as_invalid, handle_mount_ascii_string_flag_as_invalid }, - { "ram"sv, "RAMFS"sv, false, false, false, {}, RAMFS::try_create, handle_mount_boolean_flag_as_invalid, handle_mount_unsigned_integer_flag_as_invalid, handle_mount_signed_integer_flag_as_invalid, handle_mount_ascii_string_flag_as_invalid }, - { "ext2"sv, "Ext2FS"sv, true, true, true, Ext2FS::try_create, {}, handle_mount_boolean_flag_as_invalid, handle_mount_unsigned_integer_flag_as_invalid, handle_mount_signed_integer_flag_as_invalid, handle_mount_ascii_string_flag_as_invalid }, - { "9p"sv, "Plan9FS"sv, true, true, true, Plan9FS::try_create, {}, handle_mount_boolean_flag_as_invalid, handle_mount_unsigned_integer_flag_as_invalid, handle_mount_signed_integer_flag_as_invalid, handle_mount_ascii_string_flag_as_invalid }, - { "iso9660"sv, "ISO9660FS"sv, true, true, true, ISO9660FS::try_create, {}, handle_mount_boolean_flag_as_invalid, handle_mount_unsigned_integer_flag_as_invalid, handle_mount_signed_integer_flag_as_invalid, handle_mount_ascii_string_flag_as_invalid }, - { "fat"sv, "FATFS"sv, true, true, true, FATFS::try_create, {}, handle_mount_boolean_flag_as_invalid, handle_mount_unsigned_integer_flag_as_invalid, handle_mount_signed_integer_flag_as_invalid, handle_mount_ascii_string_flag_as_invalid }, - { "devloop"sv, "DevLoopFS"sv, false, false, false, {}, DevLoopFS::try_create, handle_mount_boolean_flag_as_invalid, handle_mount_unsigned_integer_flag_as_invalid, handle_mount_signed_integer_flag_as_invalid, handle_mount_ascii_string_flag_as_invalid }, - { "fuse"sv, "FUSE"sv, false, false, false, {}, FUSE::try_create, handle_mount_boolean_flag_as_invalid, FUSE::handle_mount_unsigned_integer_flag, handle_mount_signed_integer_flag_as_invalid, handle_mount_ascii_string_flag_as_invalid }, -}; - -ErrorOr VirtualFileSystem::find_filesystem_type_initializer(StringView fs_type) -{ - for (auto& initializer_entry : s_initializers) { - if (fs_type == initializer_entry.short_name || fs_type == initializer_entry.name) - return &initializer_entry; - } - return ENODEV; -} - -UNMAP_AFTER_INIT void VirtualFileSystem::initialize() -{ - s_the.ensure_instance(); -} - -VirtualFileSystem& VirtualFileSystem::the() -{ - return *s_the; -} - -UNMAP_AFTER_INIT VirtualFileSystem::VirtualFileSystem() -{ -} - -UNMAP_AFTER_INIT VirtualFileSystem::~VirtualFileSystem() = default; - -bool VirtualFileSystem::check_matching_absolute_path_hierarchy(Custody const& first_custody, Custody const& second_custody) -{ - // Are both custodies the root mount? - if (!first_custody.parent() && !second_custody.parent()) - return true; - if (first_custody.name() != second_custody.name()) - return false; - auto const* custody1 = &first_custody; - auto const* custody2 = &second_custody; - while (custody1->parent()) { - if (!custody2->parent()) - return false; - if (custody1->parent().ptr() != custody2->parent().ptr()) - return false; - custody1 = custody1->parent(); - custody2 = custody2->parent(); - } - return true; -} - -bool VirtualFileSystem::mount_point_exists_at_custody(Custody& mount_point) -{ - return m_mounts.with([&](auto& mounts) -> bool { - return any_of(mounts, [&mount_point](auto const& existing_mount) { - return existing_mount.host_custody() && check_matching_absolute_path_hierarchy(*existing_mount.host_custody(), mount_point); - }); - }); -} - -ErrorOr VirtualFileSystem::add_file_system_to_mount_table(FileSystem& file_system, Custody& mount_point, int flags) -{ - auto new_mount = TRY(adopt_nonnull_own_or_enomem(new (nothrow) Mount(file_system, &mount_point, flags))); - return m_mounts.with([&](auto& mounts) -> ErrorOr { - auto& mount_point_inode = mount_point.inode(); - dbgln("VirtualFileSystem: FileSystemID {}, Mounting {} at inode {} with flags {}", - file_system.fsid(), - file_system.class_name(), - mount_point_inode.identifier(), - flags); - if (mount_point_exists_at_custody(mount_point)) { - dbgln("VirtualFileSystem: Mounting unsuccessful - inode {} is already a mount-point.", mount_point_inode.identifier()); - return EBUSY; - } - // Note: Actually add a mount for the filesystem and increment the filesystem mounted count - new_mount->guest_fs().mounted_count({}).with([&](auto& mounted_count) { - mounted_count++; - - // When this is the first time this FileSystem is mounted, - // begin managing the FileSystem by adding it to the list of - // managed file systems. This is symmetric with - // VirtualFileSystem::unmount()'s `remove()` calls (which remove - // the FileSystem once it is no longer mounted). - if (mounted_count == 1) { - m_file_systems_list.with([&](auto& fs_list) { - fs_list.append(file_system); - }); - } - }); - - // NOTE: Leak the mount pointer so it can be added to the mount list, but it won't be - // deleted after being added. - mounts.append(*new_mount.leak_ptr()); - return {}; - }); -} - -ErrorOr VirtualFileSystem::mount(MountFile& mount_file, OpenFileDescription* source_description, Custody& mount_point, int flags) -{ - auto const& file_system_initializer = mount_file.file_system_initializer(); - if (!source_description) { - if (file_system_initializer.requires_open_file_description) - return ENOTSUP; - if (!file_system_initializer.create) - return ENOTSUP; - RefPtr fs; - TRY(mount_file.mount_file_system_specific_data().with_exclusive([&](auto& mount_specific_data) -> ErrorOr { - fs = TRY(file_system_initializer.create(mount_specific_data->bytes())); - return {}; - })); - VERIFY(fs); - TRY(fs->initialize()); - TRY(add_file_system_to_mount_table(*fs, mount_point, flags)); - return {}; - } - - // NOTE: Although it might be OK to support creating filesystems - // without providing an actual file descriptor to their create() method - // because the caller of this function actually supplied a valid file descriptor, - // this will only make things complicated in the future, so we should block - // this kind of behavior. - if (!file_system_initializer.requires_open_file_description) - return ENOTSUP; - - if (file_system_initializer.requires_block_device && !source_description->file().is_block_device()) - return ENOTBLK; - if (file_system_initializer.requires_seekable_file && !source_description->file().is_seekable()) { - dbgln("mount: this is not a seekable file"); - return ENODEV; - } - - // NOTE: If there's an associated file description with the filesystem, we could - // try to first find it from the VirtualFileSystem filesystem list and if it was not found, - // then create it and add it. - VERIFY(file_system_initializer.create_with_fd); - return m_file_backed_file_systems_list.with_exclusive([&](auto& list) -> ErrorOr { - RefPtr fs; - for (auto& node : list) { - if ((&node.file_description() == source_description) || (&node.file() == &source_description->file())) { - fs = node; - break; - } - } - if (!fs) { - TRY(mount_file.mount_file_system_specific_data().with_exclusive([&](auto& mount_specific_data) -> ErrorOr { - fs = TRY(file_system_initializer.create_with_fd(*source_description, mount_specific_data->bytes())); - return {}; - })); - TRY(fs->initialize()); - } - if (source_description->file().is_loop_device()) { - auto& device = static_cast(source_description->file()); - auto path = TRY(device.custody().try_serialize_absolute_path()); - dbgln("VirtualFileSystem: mounting from loop device {}, originated from {}", device.index(), path->view()); - } - - TRY(add_file_system_to_mount_table(*fs, mount_point, flags)); - list.append(static_cast(*fs)); - return {}; - }); -} - -ErrorOr VirtualFileSystem::bind_mount(Custody& source, Custody& mount_point, int flags) -{ - auto new_mount = TRY(adopt_nonnull_own_or_enomem(new (nothrow) Mount(source.inode(), mount_point, flags))); - return m_mounts.with([&](auto& mounts) -> ErrorOr { - auto& inode = mount_point.inode(); - dbgln("VirtualFileSystem: Bind-mounting inode {} at inode {}", source.inode().identifier(), inode.identifier()); - if (mount_point_exists_at_custody(mount_point)) { - dbgln("VirtualFileSystem: Bind-mounting unsuccessful - inode {} is already a mount-point.", - mount_point.inode().identifier()); - return EBUSY; - } - - // A bind mount also counts as a normal mount from the perspective of unmount(), - // so we need to keep track of it in order for prepare_to_clear_last_mount() to work properly. - new_mount->guest_fs().mounted_count({}).with([&](auto& count) { count++; }); - // NOTE: Leak the mount pointer so it can be added to the mount list, but it won't be - // deleted after being added. - mounts.append(*new_mount.leak_ptr()); - return {}; - }); -} - -ErrorOr VirtualFileSystem::remount(Custody& mount_point, int new_flags) -{ - dbgln("VirtualFileSystem: Remounting inode {}", mount_point.inode().identifier()); - - TRY(apply_to_mount_for_host_custody(mount_point, [new_flags](auto& mount) { - mount.set_flags(new_flags); - })); - return {}; -} - -void VirtualFileSystem::sync_filesystems() -{ - Vector, 32> file_systems; - m_file_systems_list.with([&](auto const& list) { - for (auto& fs : list) - file_systems.append(fs); - }); - - for (auto& fs : file_systems) { - auto result = fs->flush_writes(); - if (result.is_error()) { - // TODO: Figure out how to propagate error to a higher function. - } - } -} - -void VirtualFileSystem::lock_all_filesystems() -{ - Vector, 32> file_systems; - m_file_systems_list.with([&](auto const& list) { - for (auto& fs : list) - file_systems.append(fs); - }); - - for (auto& fs : file_systems) - fs->m_lock.lock(); -} - -ErrorOr VirtualFileSystem::unmount(Custody& mountpoint_custody) -{ - auto& guest_inode = mountpoint_custody.inode(); - auto custody_path = TRY(mountpoint_custody.try_serialize_absolute_path()); - return unmount(guest_inode, custody_path->view()); -} - -ErrorOr VirtualFileSystem::unmount(Inode& guest_inode, StringView custody_path) -{ - return m_file_backed_file_systems_list.with_exclusive([&](auto& file_backed_fs_list) -> ErrorOr { - TRY(m_mounts.with([&](auto& mounts) -> ErrorOr { - for (auto& mount : mounts) { - if (&mount.guest() != &guest_inode) - continue; - auto mountpoint_path = TRY(mount.absolute_path()); - if (custody_path != mountpoint_path->view()) - continue; - NonnullRefPtr fs = mount.guest_fs(); - TRY(fs->prepare_to_unmount(mount.guest())); - fs->mounted_count({}).with([&](auto& mounted_count) { - VERIFY(mounted_count > 0); - if (mounted_count == 1) { - dbgln("VirtualFileSystem: Unmounting file system {} for the last time...", fs->fsid()); - m_file_systems_list.with([&](auto& list) { - list.remove(*fs); - }); - if (fs->is_file_backed()) { - dbgln("VirtualFileSystem: Unmounting file backed file system {} for the last time...", fs->fsid()); - auto& file_backed_fs = static_cast(*fs); - file_backed_fs_list.remove(file_backed_fs); - } - } else { - mounted_count--; - } - }); - dbgln("VirtualFileSystem: Unmounting file system {}...", fs->fsid()); - mount.m_vfs_list_node.remove(); - // NOTE: This is balanced by a `new` statement that is happening in various places before inserting the Mount object to the list. - delete &mount; - return {}; - } - dbgln("VirtualFileSystem: Nothing mounted on inode {}", guest_inode.identifier()); - return ENODEV; - })); - return {}; - }); -} - -ErrorOr VirtualFileSystem::mount_root(FileSystem& fs) -{ - if (m_root_inode) { - dmesgln("VirtualFileSystem: mount_root can't mount another root"); - return EEXIST; - } - - auto new_mount = TRY(adopt_nonnull_own_or_enomem(new (nothrow) Mount(fs, nullptr, root_mount_flags))); - auto& root_inode = fs.root_inode(); - if (!root_inode.is_directory()) { - dmesgln("VirtualFileSystem: root inode ({}) for / is not a directory :(", root_inode.identifier()); - return ENOTDIR; - } - - m_root_inode = root_inode; - if (fs.is_file_backed()) { - auto pseudo_path = TRY(static_cast(fs).file_description().pseudo_path()); - dmesgln("VirtualFileSystem: mounted root({}) from {} ({})", fs.fsid(), fs.class_name(), pseudo_path); - m_file_backed_file_systems_list.with_exclusive([&](auto& list) { - list.append(static_cast(fs)); - }); - } else { - dmesgln("VirtualFileSystem: mounted root({}) from {}", fs.fsid(), fs.class_name()); - } - - m_file_systems_list.with([&](auto& fs_list) { - fs_list.append(fs); - }); - - fs.mounted_count({}).with([&](auto& mounted_count) { - mounted_count++; - }); - - // Note: Actually add a mount for the filesystem and increment the filesystem mounted count - m_mounts.with([&](auto& mounts) { - // NOTE: Leak the mount pointer so it can be added to the mount list, but it won't be - // deleted after being added. - mounts.append(*new_mount.leak_ptr()); - }); - - RefPtr new_root_custody = TRY(Custody::try_create(nullptr, ""sv, *m_root_inode, root_mount_flags)); - m_root_custody.with([&](auto& root_custody) { - swap(root_custody, new_root_custody); - }); - return {}; -} - -ErrorOr VirtualFileSystem::apply_to_mount_for_host_custody(Custody const& current_custody, Function callback) -{ - return m_mounts.with([&](auto& mounts) -> ErrorOr { - // NOTE: We either search for the root mount or for a mount that has a parent custody! - if (!current_custody.parent()) { - for (auto& mount : mounts) { - if (!mount.host_custody()) { - callback(mount); - return {}; - } - } - // NOTE: There must be a root mount entry, so fail if we don't find it. - VERIFY_NOT_REACHED(); - } else { - for (auto& mount : mounts) { - if (mount.host_custody() && check_matching_absolute_path_hierarchy(*mount.host_custody(), current_custody)) { - callback(mount); - return {}; - } - } - } - return Error::from_errno(ENODEV); - }); -} - -ErrorOr VirtualFileSystem::traverse_directory_inode(Inode& dir_inode, Function(FileSystem::DirectoryEntryView const&)> callback) -{ - return dir_inode.traverse_as_directory([&](auto& entry) -> ErrorOr { - TRY(callback({ entry.name, entry.inode, entry.file_type })); - return {}; - }); -} - -ErrorOr VirtualFileSystem::utime(Credentials const& credentials, StringView path, CustodyBase const& base, time_t atime, time_t mtime) -{ - auto custody = TRY(resolve_path(credentials, path, base)); - auto& inode = custody->inode(); - if (!credentials.is_superuser() && inode.metadata().uid != credentials.euid()) - return EACCES; - if (custody->is_readonly()) - return EROFS; - - TRY(inode.update_timestamps(UnixDateTime::from_seconds_since_epoch(atime), {}, UnixDateTime::from_seconds_since_epoch(mtime))); - return {}; -} - -ErrorOr VirtualFileSystem::utimensat(Credentials const& credentials, StringView path, CustodyBase const& base, timespec const& atime, timespec const& mtime, int options) -{ - auto custody = TRY(resolve_path(credentials, path, base, nullptr, options)); - return do_utimens(credentials, custody, atime, mtime); -} - -ErrorOr VirtualFileSystem::do_utimens(Credentials const& credentials, Custody& custody, timespec const& atime, timespec const& mtime) -{ - auto& inode = custody.inode(); - if (!credentials.is_superuser() && inode.metadata().uid != credentials.euid()) - return EACCES; - if (custody.is_readonly()) - return EROFS; - - // NOTE: A standard ext2 inode cannot store nanosecond timestamps. - TRY(inode.update_timestamps( - (atime.tv_nsec != UTIME_OMIT) ? UnixDateTime::from_unix_timespec(atime) : Optional {}, - {}, - (mtime.tv_nsec != UTIME_OMIT) ? UnixDateTime::from_unix_timespec(mtime) : Optional {})); - - return {}; -} - -ErrorOr VirtualFileSystem::lookup_metadata(Credentials const& credentials, StringView path, CustodyBase const& base, int options) -{ - auto custody = TRY(resolve_path(credentials, path, base, nullptr, options)); - return custody->inode().metadata(); -} - -ErrorOr> VirtualFileSystem::open(Credentials const& credentials, StringView path, int options, mode_t mode, CustodyBase const& base, Optional owner) -{ - return open(Process::current(), credentials, path, options, mode, base, owner); -} - -ErrorOr> VirtualFileSystem::open(Process const& process, Credentials const& credentials, StringView path, int options, mode_t mode, CustodyBase const& base, Optional owner) -{ - if ((options & O_CREAT) && (options & O_DIRECTORY)) - return EINVAL; - - RefPtr parent_custody; - auto custody_or_error = resolve_path(process, credentials, path, base, &parent_custody, options); - if (custody_or_error.is_error()) { - // NOTE: ENOENT with a non-null parent custody signals us that the immediate parent - // of the file exists, but the file itself does not. - if ((options & O_CREAT) && custody_or_error.error().code() == ENOENT && parent_custody) - return create(process, credentials, path, options, mode, *parent_custody, move(owner)); - return custody_or_error.release_error(); - } - - if ((options & O_CREAT) && (options & O_EXCL)) - return EEXIST; - - auto& custody = *custody_or_error.value(); - auto& inode = custody.inode(); - auto metadata = inode.metadata(); - - if (metadata.is_regular_file() && (custody.mount_flags() & MS_NOREGULAR)) - return EACCES; - - if ((options & O_DIRECTORY) && !metadata.is_directory()) - return ENOTDIR; - - bool should_truncate_file = false; - - if ((options & O_RDONLY) && !metadata.may_read(credentials)) - return EACCES; - - if (options & O_WRONLY) { - if (!metadata.may_write(credentials)) - return EACCES; - if (metadata.is_directory()) - return EISDIR; - should_truncate_file = options & O_TRUNC; - } - if (options & O_EXEC) { - if (!metadata.may_execute(credentials) || (custody.mount_flags() & MS_NOEXEC)) - return EACCES; - } - - if (metadata.is_fifo()) { - auto fifo = TRY(inode.fifo()); - if (options & O_WRONLY) { - auto description = TRY(fifo->open_direction_blocking(FIFO::Direction::Writer)); - description->set_rw_mode(options); - description->set_file_flags(options); - description->set_original_inode({}, inode); - return description; - } else if (options & O_RDONLY) { - auto description = TRY(fifo->open_direction_blocking(FIFO::Direction::Reader)); - description->set_rw_mode(options); - description->set_file_flags(options); - description->set_original_inode({}, inode); - return description; - } - return EINVAL; - } - - if (metadata.is_device()) { - if (custody.mount_flags() & MS_NODEV) - return EACCES; - auto device = DeviceManagement::the().get_device(metadata.major_device, metadata.minor_device); - if (device == nullptr) { - return ENODEV; - } - auto description = TRY(device->open(options)); - description->set_original_inode({}, inode); - description->set_original_custody({}, custody); - return description; - } - - // Check for read-only FS. Do this after handling devices, but before modifying the inode in any way. - if ((options & O_WRONLY) && custody.is_readonly()) - return EROFS; - - if (should_truncate_file) { - TRY(inode.truncate(0)); - TRY(inode.update_timestamps({}, {}, kgettimeofday())); - } - auto description = TRY(OpenFileDescription::try_create(custody)); - description->set_rw_mode(options); - description->set_file_flags(options); - return description; -} - -ErrorOr VirtualFileSystem::mknod(Credentials const& credentials, StringView path, mode_t mode, dev_t dev, CustodyBase const& base) -{ - if (!is_regular_file(mode) && !is_block_device(mode) && !is_character_device(mode) && !is_fifo(mode) && !is_socket(mode)) - return EINVAL; - - RefPtr parent_custody; - auto existing_file_or_error = resolve_path(credentials, path, base, &parent_custody); - if (!existing_file_or_error.is_error()) - return EEXIST; - if (!parent_custody) - return ENOENT; - if (existing_file_or_error.error().code() != ENOENT) - return existing_file_or_error.release_error(); - auto& parent_inode = parent_custody->inode(); - if (!parent_inode.metadata().may_write(credentials)) - return EACCES; - if (parent_custody->is_readonly()) - return EROFS; - - auto basename = KLexicalPath::basename(path); - dbgln_if(VFS_DEBUG, "VirtualFileSystem::mknod: '{}' mode={} dev={} in {}", basename, mode, dev, parent_inode.identifier()); - (void)TRY(parent_inode.create_child(basename, mode, dev, credentials.euid(), credentials.egid())); - return {}; -} - -ErrorOr> VirtualFileSystem::create(Credentials const& credentials, StringView path, int options, mode_t mode, Custody& parent_custody, Optional owner) -{ - return create(Process::current(), credentials, path, options, mode, parent_custody, owner); -} - -ErrorOr> VirtualFileSystem::create(Process const& process, Credentials const& credentials, StringView path, int options, mode_t mode, Custody& parent_custody, Optional owner) -{ - auto basename = KLexicalPath::basename(path); - auto parent_path = TRY(parent_custody.try_serialize_absolute_path()); - auto full_path = TRY(KLexicalPath::try_join(parent_path->view(), basename)); - TRY(validate_path_against_process_veil(process, full_path->view(), options)); - - if (!is_socket(mode) && !is_fifo(mode) && !is_block_device(mode) && !is_character_device(mode)) { - // Turn it into a regular file. (This feels rather hackish.) - mode |= 0100000; - } - - auto& parent_inode = parent_custody.inode(); - if (!parent_inode.metadata().may_write(credentials)) - return EACCES; - if (parent_custody.is_readonly()) - return EROFS; - if (is_regular_file(mode) && (parent_custody.mount_flags() & MS_NOREGULAR)) - return EACCES; - - dbgln_if(VFS_DEBUG, "VirtualFileSystem::create: '{}' in {}", basename, parent_inode.identifier()); - auto uid = owner.has_value() ? owner.value().uid : credentials.euid(); - auto gid = owner.has_value() ? owner.value().gid : credentials.egid(); - - auto inode = TRY(parent_inode.create_child(basename, mode, 0, uid, gid)); - auto custody = TRY(Custody::try_create(&parent_custody, basename, inode, parent_custody.mount_flags())); - - auto description = TRY(OpenFileDescription::try_create(move(custody))); - description->set_rw_mode(options); - description->set_file_flags(options); - return description; -} - -ErrorOr VirtualFileSystem::mkdir(Credentials const& credentials, StringView path, mode_t mode, CustodyBase const& base) -{ - // Unlike in basically every other case, where it's only the last - // path component (the one being created) that is allowed not to - // exist, POSIX allows mkdir'ed path to have trailing slashes. - // Let's handle that case by trimming any trailing slashes. - path = path.trim("/"sv, TrimMode::Right); - if (path.is_empty()) { - // NOTE: This means the path was a series of slashes, which resolves to "/". - path = "/"sv; - } - - RefPtr parent_custody; - // FIXME: The errors returned by resolve_path_without_veil can leak information about paths that are not unveiled, - // e.g. when the error is EACCESS or similar. - auto base_custody = TRY(base.resolve()); - auto result = resolve_path_without_veil(credentials, path, base_custody, &parent_custody); - if (!result.is_error()) - return EEXIST; - else if (!parent_custody) - return result.release_error(); - // NOTE: If resolve_path fails with a non-null parent custody, the error should be ENOENT. - VERIFY(result.error().code() == ENOENT); - - TRY(validate_path_against_process_veil(*parent_custody, O_CREAT)); - auto& parent_inode = parent_custody->inode(); - if (!parent_inode.metadata().may_write(credentials)) - return EACCES; - if (parent_custody->is_readonly()) - return EROFS; - - auto basename = KLexicalPath::basename(path); - dbgln_if(VFS_DEBUG, "VirtualFileSystem::mkdir: '{}' in {}", basename, parent_inode.identifier()); - (void)TRY(parent_inode.create_child(basename, S_IFDIR | mode, 0, credentials.euid(), credentials.egid())); - return {}; -} - -ErrorOr VirtualFileSystem::access(Credentials const& credentials, StringView path, int mode, CustodyBase const& base, AccessFlags access_flags) -{ - auto should_follow_symlinks = !has_flag(access_flags, AccessFlags::DoNotFollowSymlinks); - auto custody = TRY(resolve_path(credentials, path, base, nullptr, should_follow_symlinks ? 0 : O_NOFOLLOW_NOERROR)); - - auto& inode = custody->inode(); - auto metadata = inode.metadata(); - auto use_effective_ids = has_flag(access_flags, AccessFlags::EffectiveAccess) ? UseEffectiveIDs::Yes : UseEffectiveIDs::No; - if (mode & R_OK) { - if (!metadata.may_read(credentials, use_effective_ids)) - return EACCES; - } - if (mode & W_OK) { - if (!metadata.may_write(credentials, use_effective_ids)) - return EACCES; - if (custody->is_readonly()) - return EROFS; - } - if (mode & X_OK) { - if (!metadata.may_execute(credentials, use_effective_ids)) - return EACCES; - } - return {}; -} - -ErrorOr> VirtualFileSystem::open_directory(Credentials const& credentials, StringView path, CustodyBase const& base) -{ - auto custody = TRY(resolve_path(credentials, path, base)); - auto& inode = custody->inode(); - if (!inode.is_directory()) - return ENOTDIR; - if (!inode.metadata().may_execute(credentials)) - return EACCES; - return custody; -} - -ErrorOr VirtualFileSystem::chmod(Credentials const& credentials, Custody& custody, mode_t mode) -{ - auto& inode = custody.inode(); - - if (credentials.euid() != inode.metadata().uid && !credentials.is_superuser()) - return EPERM; - if (custody.is_readonly()) - return EROFS; - - // Only change the permission bits. - mode = (inode.mode() & ~07777u) | (mode & 07777u); - return inode.chmod(mode); -} - -ErrorOr VirtualFileSystem::chmod(Credentials const& credentials, StringView path, mode_t mode, CustodyBase const& base, int options) -{ - auto custody = TRY(resolve_path(credentials, path, base, nullptr, options)); - return chmod(credentials, custody, mode); -} - -ErrorOr VirtualFileSystem::rename(Credentials const& credentials, CustodyBase const& old_base, StringView old_path, CustodyBase const& new_base, StringView new_path) -{ - RefPtr old_parent_custody; - auto old_custody = TRY(resolve_path(credentials, old_path, old_base, &old_parent_custody, O_NOFOLLOW_NOERROR)); - auto& old_inode = old_custody->inode(); - - RefPtr new_parent_custody; - auto new_custody_or_error = resolve_path(credentials, new_path, new_base, &new_parent_custody); - if (new_custody_or_error.is_error()) { - if (new_custody_or_error.error().code() != ENOENT || !new_parent_custody) - return new_custody_or_error.release_error(); - } - - if (!old_parent_custody || !new_parent_custody) { - return EPERM; - } - - if (!new_custody_or_error.is_error()) { - auto& new_inode = new_custody_or_error.value()->inode(); - - if (old_inode.index() != new_inode.index() && old_inode.is_directory() && new_inode.is_directory()) { - size_t child_count = 0; - TRY(new_inode.traverse_as_directory([&child_count](auto&) -> ErrorOr { - ++child_count; - return {}; - })); - if (child_count > 2) - return ENOTEMPTY; - } - } - - auto& old_parent_inode = old_parent_custody->inode(); - auto& new_parent_inode = new_parent_custody->inode(); - - if (&old_parent_inode.fs() != &new_parent_inode.fs()) - return EXDEV; - - for (auto* new_ancestor = new_parent_custody.ptr(); new_ancestor; new_ancestor = new_ancestor->parent()) { - if (&old_inode == &new_ancestor->inode()) - return EDIRINTOSELF; - } - - if (!new_parent_inode.metadata().may_write(credentials)) - return EACCES; - - if (!old_parent_inode.metadata().may_write(credentials)) - return EACCES; - - if (old_parent_inode.metadata().is_sticky()) { - if (!credentials.is_superuser() && old_parent_inode.metadata().uid != credentials.euid() && old_inode.metadata().uid != credentials.euid()) - return EACCES; - } - - if (old_parent_custody->is_readonly() || new_parent_custody->is_readonly()) - return EROFS; - - auto old_basename = KLexicalPath::basename(old_path); - if (old_basename.is_empty() || old_basename == "."sv || old_basename == ".."sv) - return EINVAL; - - auto new_basename = KLexicalPath::basename(new_path); - if (new_basename.is_empty() || new_basename == "."sv || new_basename == ".."sv) - return EINVAL; - - if (old_basename == new_basename && old_parent_inode.index() == new_parent_inode.index()) - return {}; - - if (!new_custody_or_error.is_error()) { - auto& new_custody = *new_custody_or_error.value(); - auto& new_inode = new_custody.inode(); - // When the source/dest inodes are the same (in other words, - // when `old_path` and `new_path` are the same), perform a no-op - // and return success. - // Linux (`vfs_rename()`) and OpenBSD (`dorenameat()`) appear to have - // this same no-op behavior. - if (&new_inode == &old_inode) - return {}; - if (new_parent_inode.metadata().is_sticky()) { - if (!credentials.is_superuser() && new_inode.metadata().uid != credentials.euid()) - return EACCES; - } - if (new_inode.is_directory() && !old_inode.is_directory()) - return EISDIR; - TRY(new_parent_inode.remove_child(new_basename)); - } - - TRY(new_parent_inode.add_child(old_inode, new_basename, old_inode.mode())); - TRY(old_parent_inode.remove_child(old_basename)); - - // If the inode that we moved is a directory and we changed parent - // directories, then we also have to make .. point to the new parent inode, - // because .. is its own inode. - if (old_inode.is_directory() && old_parent_inode.index() != new_parent_inode.index()) { - TRY(old_inode.replace_child(".."sv, new_parent_inode)); - } - - return {}; -} - -ErrorOr VirtualFileSystem::chown(Credentials const& credentials, Custody& custody, UserID a_uid, GroupID a_gid) -{ - auto& inode = custody.inode(); - auto metadata = inode.metadata(); - - if (credentials.euid() != metadata.uid && !credentials.is_superuser()) - return EPERM; - - UserID new_uid = metadata.uid; - GroupID new_gid = metadata.gid; - - if (a_uid != (uid_t)-1) { - if (credentials.euid() != a_uid && !credentials.is_superuser()) - return EPERM; - new_uid = a_uid; - } - if (a_gid != (gid_t)-1) { - if (!credentials.in_group(a_gid) && !credentials.is_superuser()) - return EPERM; - new_gid = a_gid; - } - - if (custody.is_readonly()) - return EROFS; - - dbgln_if(VFS_DEBUG, "VirtualFileSystem::chown(): inode {} <- uid={} gid={}", inode.identifier(), new_uid, new_gid); - - if (metadata.is_setuid() || metadata.is_setgid()) { - dbgln_if(VFS_DEBUG, "VirtualFileSystem::chown(): Stripping SUID/SGID bits from {}", inode.identifier()); - TRY(inode.chmod(metadata.mode & ~(04000 | 02000))); - } - - return inode.chown(new_uid, new_gid); -} - -ErrorOr VirtualFileSystem::chown(Credentials const& credentials, StringView path, UserID a_uid, GroupID a_gid, CustodyBase const& base, int options) -{ - auto custody = TRY(resolve_path(credentials, path, base, nullptr, options)); - return chown(credentials, custody, a_uid, a_gid); -} - -static bool hard_link_allowed(Credentials const& credentials, Inode const& inode) -{ - auto metadata = inode.metadata(); - - if (credentials.euid() == metadata.uid) - return true; - - if (metadata.is_regular_file() - && !metadata.is_setuid() - && !(metadata.is_setgid() && metadata.mode & S_IXGRP) - && metadata.may_write(credentials)) { - return true; - } - - return false; -} - -ErrorOr VirtualFileSystem::link(Credentials const& credentials, StringView old_path, StringView new_path, CustodyBase const& base) -{ - // NOTE: To prevent unveil bypass by creating an hardlink after unveiling a path as read-only, - // check that if write permission is allowed by the veil info on the old_path. - auto old_custody = TRY(resolve_path(credentials, old_path, base, nullptr, O_RDWR)); - auto& old_inode = old_custody->inode(); - - RefPtr parent_custody; - auto new_custody_or_error = resolve_path(credentials, new_path, base, &parent_custody); - if (!new_custody_or_error.is_error()) - return EEXIST; - - if (!parent_custody) - return ENOENT; - - auto& parent_inode = parent_custody->inode(); - - if (parent_inode.fsid() != old_inode.fsid()) - return EXDEV; - - if (!parent_inode.metadata().may_write(credentials)) - return EACCES; - - if (old_inode.is_directory()) - return EPERM; - - if (parent_custody->is_readonly()) - return EROFS; - - if (!hard_link_allowed(credentials, old_inode)) - return EPERM; - - return parent_inode.add_child(old_inode, KLexicalPath::basename(new_path), old_inode.mode()); -} - -ErrorOr VirtualFileSystem::unlink(Credentials const& credentials, StringView path, CustodyBase const& base) -{ - RefPtr parent_custody; - auto custody = TRY(resolve_path(credentials, path, base, &parent_custody, O_WRONLY | O_NOFOLLOW_NOERROR | O_UNLINK_INTERNAL)); - auto& inode = custody->inode(); - - if (inode.is_directory()) - return EISDIR; - - // We have just checked that the inode is not a directory, and thus it's not - // the root. So it should have a parent. Note that this would be invalidated - // if we were to support bind-mounting regular files on top of the root. - VERIFY(parent_custody); - - auto& parent_inode = parent_custody->inode(); - if (!parent_inode.metadata().may_write(credentials)) - return EACCES; - - if (parent_inode.metadata().is_sticky()) { - if (!credentials.is_superuser() && parent_inode.metadata().uid != credentials.euid() && inode.metadata().uid != credentials.euid()) - return EACCES; - } - - if (parent_custody->is_readonly()) - return EROFS; - - return parent_inode.remove_child(KLexicalPath::basename(path)); -} - -ErrorOr VirtualFileSystem::symlink(Credentials const& credentials, StringView target, StringView linkpath, CustodyBase const& base) -{ - auto base_custody = TRY(base.resolve()); - // NOTE: Check that the actual target (if it exists right now) is unveiled and prevent creating symlinks on veiled paths! - if (auto target_custody_or_error = resolve_path_without_veil(credentials, target, base_custody, nullptr, O_RDWR, 0); !target_custody_or_error.is_error()) { - auto target_custody = target_custody_or_error.release_value(); - TRY(validate_path_against_process_veil(*target_custody, O_RDWR)); - } - - RefPtr parent_custody; - auto existing_custody_or_error = resolve_path(credentials, linkpath, base, &parent_custody, O_RDWR); - if (!existing_custody_or_error.is_error()) - return EEXIST; - if (!parent_custody) - return ENOENT; - - // NOTE: VERY IMPORTANT! We prevent creating symlinks in case the program didn't unveil the parent_custody - // path! For example, say the program wanted to create a symlink in /tmp/symlink to /tmp/test/pointee_symlink - // and unveiled the /tmp/test/ directory path beforehand, but not the /tmp directory path - the symlink syscall will - // fail here because we can't create the symlink in a parent directory path we didn't unveil beforehand. - TRY(validate_path_against_process_veil(*parent_custody, O_RDWR)); - - if (existing_custody_or_error.is_error() && existing_custody_or_error.error().code() != ENOENT) - return existing_custody_or_error.release_error(); - auto& parent_inode = parent_custody->inode(); - if (!parent_inode.metadata().may_write(credentials)) - return EACCES; - if (parent_custody->is_readonly()) - return EROFS; - - auto basename = KLexicalPath::basename(linkpath); - dbgln_if(VFS_DEBUG, "VirtualFileSystem::symlink: '{}' (-> '{}') in {}", basename, target, parent_inode.identifier()); - - auto inode = TRY(parent_inode.create_child(basename, S_IFLNK | 0644, 0, credentials.euid(), credentials.egid())); - - auto target_buffer = UserOrKernelBuffer::for_kernel_buffer(const_cast((u8 const*)target.characters_without_null_termination())); - TRY(inode->write_bytes(0, target.length(), target_buffer, nullptr)); - return {}; -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/rmdir.html -ErrorOr VirtualFileSystem::rmdir(Credentials const& credentials, StringView path, CustodyBase const& base) -{ - RefPtr parent_custody; - auto custody = TRY(resolve_path(credentials, path, base, &parent_custody, O_CREAT)); - auto& inode = custody->inode(); - - auto last_component = KLexicalPath::basename(path); - - // [EINVAL] The path argument contains a last component that is dot. - if (last_component == "."sv) - return EINVAL; - - // [ENOTDIR] A component of path names an existing file that is neither a directory - // nor a symbolic link to a directory. - if (!inode.is_directory()) - return ENOTDIR; - - // [EBUSY] The directory to be removed is currently in use by the system or some process - // and the implementation considers this to be an error. - // NOTE: If there is no parent, that means we're trying to rmdir the root directory! - if (!parent_custody) - return EBUSY; - - auto& parent_inode = parent_custody->inode(); - auto parent_metadata = parent_inode.metadata(); - - // [EACCES] Search permission is denied on a component of the path prefix, - // or write permission is denied on the parent directory of the directory to be removed. - if (!parent_metadata.may_write(credentials)) - return EACCES; - - if (parent_metadata.is_sticky()) { - // [EACCES] The S_ISVTX flag is set on the directory containing the file referred to by the path argument - // and the process does not satisfy the criteria specified in XBD Directory Protection. - if (!credentials.is_superuser() - && inode.metadata().uid != credentials.euid() - && parent_metadata.uid != credentials.euid()) { - return EACCES; - } - } - - size_t child_count = 0; - TRY(inode.traverse_as_directory([&child_count](auto&) -> ErrorOr { - ++child_count; - return {}; - })); - - // [ENOTEMPTY] The path argument names a directory that is not an empty directory, - // or there are hard links to the directory other than dot or a single entry in dot-dot. - if (child_count != 2) - return ENOTEMPTY; - - // [EROFS] The directory entry to be removed resides on a read-only file system. - if (custody->is_readonly()) - return EROFS; - - TRY(inode.remove_child("."sv)); - TRY(inode.remove_child(".."sv)); - - return parent_inode.remove_child(KLexicalPath::basename(path)); -} - -ErrorOr VirtualFileSystem::for_each_mount(Function(Mount const&)> callback) const -{ - return m_mounts.with([&](auto& mounts) -> ErrorOr { - for (auto& mount : mounts) - TRY(callback(mount)); - return {}; - }); -} - -void VirtualFileSystem::sync() -{ - FileSystem::sync(); -} - -NonnullRefPtr VirtualFileSystem::root_custody() -{ - return m_root_custody.with([](auto& root_custody) -> NonnullRefPtr { return *root_custody; }); -} - -UnveilNode const& VirtualFileSystem::find_matching_unveiled_path(Process const& process, StringView path) -{ - VERIFY(process.veil_state() != VeilState::None); - return process.unveil_data().with([&](auto const& unveil_data) -> UnveilNode const& { - auto path_parts = KLexicalPath::parts(path); - return unveil_data.paths.traverse_until_last_accessible_node(path_parts.begin(), path_parts.end()); - }); -} - -ErrorOr VirtualFileSystem::validate_path_against_process_veil(Custody const& custody, int options) -{ - return validate_path_against_process_veil(Process::current(), custody, options); -} - -ErrorOr VirtualFileSystem::validate_path_against_process_veil(Process const& process, Custody const& custody, int options) -{ - if (process.veil_state() == VeilState::None) - return {}; - auto absolute_path = TRY(custody.try_serialize_absolute_path()); - return validate_path_against_process_veil(process, absolute_path->view(), options); -} - -ErrorOr VirtualFileSystem::validate_path_against_process_veil(Process const& process, StringView path, int options) -{ - if (process.veil_state() == VeilState::None) - return {}; - - VERIFY(path.starts_with('/')); - VERIFY(!path.contains("/../"sv) && !path.ends_with("/.."sv)); - VERIFY(!path.contains("/./"sv) && !path.ends_with("/."sv)); - -#ifdef SKIP_PATH_VALIDATION_FOR_COVERAGE_INSTRUMENTATION - // Skip veil validation against profile data when coverage is enabled for userspace - // so that all processes can write out coverage data even with veils in place - if (KLexicalPath::basename(path).ends_with(".profraw"sv)) - return {}; -#endif - - auto log_veiled_path = [&](Optional const& with_permissions = {}) { - if (with_permissions.has_value()) - dbgln("\033[31;1mRejecting path '{}' because it hasn't been unveiled with {} permissions\033[0m", path, *with_permissions); - else - dbgln("\033[31;1mRejecting path '{}' because it hasn't been unveiled\033[0m", path); - - dump_backtrace(); - }; - - auto& unveiled_path = find_matching_unveiled_path(process, path); - if (unveiled_path.permissions() == UnveilAccess::None) { - log_veiled_path(); - return ENOENT; - } - - if (options & O_CREAT) { - if (!(unveiled_path.permissions() & UnveilAccess::CreateOrRemove)) { - log_veiled_path("'c'"sv); - return EACCES; - } - } - if (options & O_UNLINK_INTERNAL) { - if (!(unveiled_path.permissions() & UnveilAccess::CreateOrRemove)) { - log_veiled_path("'c'"sv); - return EACCES; - } - return {}; - } - if (options & O_RDONLY) { - if (options & O_DIRECTORY) { - if (!(unveiled_path.permissions() & (UnveilAccess::Read | UnveilAccess::Browse))) { - log_veiled_path("'r' or 'b'"sv); - return EACCES; - } - } else { - if (!(unveiled_path.permissions() & UnveilAccess::Read)) { - log_veiled_path("'r'"sv); - return EACCES; - } - } - } - if (options & O_WRONLY) { - if (!(unveiled_path.permissions() & UnveilAccess::Write)) { - log_veiled_path("'w'"sv); - return EACCES; - } - } - if (options & O_EXEC) { - if (!(unveiled_path.permissions() & UnveilAccess::Execute)) { - log_veiled_path("'x'"sv); - return EACCES; - } - } - return {}; -} - -ErrorOr VirtualFileSystem::validate_path_against_process_veil(StringView path, int options) -{ - return validate_path_against_process_veil(Process::current(), path, options); -} - -ErrorOr> VirtualFileSystem::resolve_path(Credentials const& credentials, StringView path, CustodyBase const& base, RefPtr* out_parent, int options, int symlink_recursion_level) -{ - return resolve_path(Process::current(), credentials, path, base, out_parent, options, symlink_recursion_level); -} - -ErrorOr> VirtualFileSystem::resolve_path(Process const& process, Credentials const& credentials, StringView path, CustodyBase const& base, RefPtr* out_parent, int options, int symlink_recursion_level) -{ - auto base_custody = TRY(base.resolve()); - // FIXME: The errors returned by resolve_path_without_veil can leak information about paths that are not unveiled, - // e.g. when the error is EACCESS or similar. - auto custody = TRY(resolve_path_without_veil(credentials, path, base_custody, out_parent, options, symlink_recursion_level)); - if (auto result = validate_path_against_process_veil(process, *custody, options); result.is_error()) { - if (out_parent) - out_parent->clear(); - return result.release_error(); - } - return custody; -} - -static bool safe_to_follow_symlink(Credentials const& credentials, Inode const& inode, InodeMetadata const& parent_metadata) -{ - auto metadata = inode.metadata(); - if (credentials.euid() == metadata.uid) - return true; - - if (!(parent_metadata.is_sticky() && parent_metadata.mode & S_IWOTH)) - return true; - - if (metadata.uid == parent_metadata.uid) - return true; - - return false; -} - -ErrorOr> VirtualFileSystem::resolve_path_without_veil(Credentials const& credentials, StringView path, NonnullRefPtr base, RefPtr* out_parent, int options, int symlink_recursion_level) -{ - if (symlink_recursion_level >= symlink_recursion_limit) - return ELOOP; - - if (path.is_empty()) - return EINVAL; - - GenericLexer path_lexer(path); - - NonnullRefPtr custody = path[0] == '/' ? root_custody() : base; - bool extra_iteration = path[path.length() - 1] == '/'; - - while (!path_lexer.is_eof() || extra_iteration) { - if (path_lexer.is_eof()) - extra_iteration = false; - auto part = path_lexer.consume_until('/'); - path_lexer.ignore(); - - Custody& parent = custody; - auto parent_metadata = parent.inode().metadata(); - if (!parent_metadata.is_directory()) - return ENOTDIR; - // Ensure the current user is allowed to resolve paths inside this directory. - if (!parent_metadata.may_execute(credentials)) - return EACCES; - - bool have_more_parts = !path_lexer.is_eof() || extra_iteration; - - if (part == "..") { - // If we encounter a "..", take a step back, but don't go beyond the root. - if (custody->parent()) - custody = *custody->parent(); - continue; - } else if (part == "." || part.is_empty()) { - continue; - } - - // Okay, let's look up this part. - auto child_or_error = parent.inode().lookup(part); - if (child_or_error.is_error()) { - if (out_parent) { - // ENOENT with a non-null parent custody signals to caller that - // we found the immediate parent of the file, but the file itself - // does not exist yet. - *out_parent = have_more_parts ? nullptr : &parent; - } - return child_or_error.release_error(); - } - auto child_inode = child_or_error.release_value(); - - int mount_flags_for_child = parent.mount_flags(); - - auto current_custody = TRY(Custody::try_create(&parent, part, *child_inode, mount_flags_for_child)); - - // See if there's something mounted on the child; in that case - // we would need to return the guest inode, not the host inode. - auto found_mount_or_error = apply_to_mount_for_host_custody(current_custody, [&child_inode, &mount_flags_for_child](auto& mount) { - child_inode = mount.guest(); - mount_flags_for_child = mount.flags(); - }); - if (!found_mount_or_error.is_error()) { - custody = TRY(Custody::try_create(&parent, part, *child_inode, mount_flags_for_child)); - } else { - custody = current_custody; - } - - if (child_inode->metadata().is_symlink()) { - if (!have_more_parts) { - if (options & O_NOFOLLOW) - return ELOOP; - if (options & O_NOFOLLOW_NOERROR) - break; - } - - if (!safe_to_follow_symlink(credentials, *child_inode, parent_metadata)) - return EACCES; - - TRY(validate_path_against_process_veil(*custody, options)); - - auto symlink_target = TRY(child_inode->resolve_as_link(credentials, parent, out_parent, options, symlink_recursion_level + 1)); - if (!have_more_parts) - return symlink_target; - - // Now, resolve the remaining path relative to the symlink target. - // We prepend a "." to it to ensure that it's not empty and that - // any initial slashes it might have get interpreted properly. - StringBuilder remaining_path; - TRY(remaining_path.try_append('.')); - TRY(remaining_path.try_append(path.substring_view_starting_after_substring(part))); - - return resolve_path_without_veil(credentials, remaining_path.string_view(), symlink_target, out_parent, options, symlink_recursion_level + 1); - } - } - - if (out_parent) - *out_parent = custody->parent(); - return custody; -} -} diff --git a/Kernel/FileSystem/VirtualFileSystem.h b/Kernel/FileSystem/VirtualFileSystem.h deleted file mode 100644 index b577f9fd6f5..00000000000 --- a/Kernel/FileSystem/VirtualFileSystem.h +++ /dev/null @@ -1,136 +0,0 @@ -/* - * Copyright (c) 2018-2021, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace Kernel { - -// Kernel internal options. -#define O_NOFOLLOW_NOERROR (1 << 29) -#define O_UNLINK_INTERNAL (1 << 30) - -struct UidAndGid { - UserID uid; - GroupID gid; -}; - -enum class AccessFlags { - None = 0, - EffectiveAccess = 1 << 0, - DoNotFollowSymlinks = 1 << 1, -}; - -AK_ENUM_BITWISE_OPERATORS(AccessFlags); - -class VirtualFileSystem { -public: - // Required to be at least 8 by POSIX - // https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/limits.h.html - static constexpr int symlink_recursion_limit = 8; - - static void initialize(); - static VirtualFileSystem& the(); - - static ErrorOr find_filesystem_type_initializer(StringView fs_type); - - VirtualFileSystem(); - ~VirtualFileSystem(); - - ErrorOr mount_root(FileSystem&); - ErrorOr mount(MountFile&, OpenFileDescription*, Custody& mount_point, int flags); - ErrorOr bind_mount(Custody& source, Custody& mount_point, int flags); - ErrorOr remount(Custody& mount_point, int new_flags); - ErrorOr unmount(Custody& mount_point); - ErrorOr unmount(Inode& guest_inode, StringView custody_path); - - ErrorOr> open(Credentials const&, StringView path, int options, mode_t mode, CustodyBase const& base, Optional = {}); - ErrorOr> open(Process const&, Credentials const&, StringView path, int options, mode_t mode, CustodyBase const& base, Optional = {}); - ErrorOr> create(Credentials const&, StringView path, int options, mode_t mode, Custody& parent_custody, Optional = {}); - ErrorOr> create(Process const&, Credentials const&, StringView path, int options, mode_t mode, Custody& parent_custody, Optional = {}); - ErrorOr mkdir(Credentials const&, StringView path, mode_t mode, CustodyBase const& base); - ErrorOr link(Credentials const&, StringView old_path, StringView new_path, CustodyBase const& base); - ErrorOr unlink(Credentials const&, StringView path, CustodyBase const& base); - ErrorOr symlink(Credentials const&, StringView target, StringView linkpath, CustodyBase const& base); - ErrorOr rmdir(Credentials const&, StringView path, CustodyBase const& base); - ErrorOr chmod(Credentials const&, StringView path, mode_t, CustodyBase const& base, int options = 0); - ErrorOr chmod(Credentials const&, Custody&, mode_t); - ErrorOr chown(Credentials const&, StringView path, UserID, GroupID, CustodyBase const& base, int options); - ErrorOr chown(Credentials const&, Custody&, UserID, GroupID); - ErrorOr access(Credentials const&, StringView path, int mode, CustodyBase const& base, AccessFlags); - ErrorOr lookup_metadata(Credentials const&, StringView path, CustodyBase const& base, int options = 0); - ErrorOr utime(Credentials const&, StringView path, CustodyBase const& base, time_t atime, time_t mtime); - ErrorOr utimensat(Credentials const&, StringView path, CustodyBase const& base, timespec const& atime, timespec const& mtime, int options = 0); - ErrorOr do_utimens(Credentials const& credentials, Custody& custody, timespec const& atime, timespec const& mtime); - ErrorOr rename(Credentials const&, CustodyBase const& old_base, StringView oldpath, CustodyBase const& new_base, StringView newpath); - ErrorOr mknod(Credentials const&, StringView path, mode_t, dev_t, CustodyBase const& base); - ErrorOr> open_directory(Credentials const&, StringView path, CustodyBase const& base); - - ErrorOr for_each_mount(Function(Mount const&)>) const; - - void sync_filesystems(); - void lock_all_filesystems(); - - static void sync(); - - NonnullRefPtr root_custody(); - ErrorOr> resolve_path(Credentials const&, StringView path, CustodyBase const& base, RefPtr* out_parent = nullptr, int options = 0, int symlink_recursion_level = 0); - ErrorOr> resolve_path(Process const&, Credentials const&, StringView path, CustodyBase const& base, RefPtr* out_parent = nullptr, int options = 0, int symlink_recursion_level = 0); - ErrorOr> resolve_path_without_veil(Credentials const&, StringView path, NonnullRefPtr base, RefPtr* out_parent = nullptr, int options = 0, int symlink_recursion_level = 0); - -private: - friend class OpenFileDescription; - - UnveilNode const& find_matching_unveiled_path(Process const&, StringView path); - ErrorOr validate_path_against_process_veil(Process const&, StringView path, int options); - ErrorOr validate_path_against_process_veil(Process const& process, Custody const& custody, int options); - ErrorOr validate_path_against_process_veil(Custody const& path, int options); - ErrorOr validate_path_against_process_veil(StringView path, int options); - - ErrorOr add_file_system_to_mount_table(FileSystem& file_system, Custody& mount_point, int flags); - - ErrorOr traverse_directory_inode(Inode&, Function(FileSystem::DirectoryEntryView const&)>); - - static bool check_matching_absolute_path_hierarchy(Custody const& first_custody, Custody const& second_custody); - bool mount_point_exists_at_custody(Custody& mount_point); - - ErrorOr apply_to_mount_for_host_custody(Custody const& current_custody, Function); - - RefPtr m_root_inode; - - SpinlockProtected, LockRank::None> m_root_custody {}; - - SpinlockProtected, LockRank::None> m_mounts {}; - - // NOTE: The FileBackedFileSystem list is protected by a mutex because we need to scan it - // to search for existing filesystems for already used block devices and therefore when doing - // that we could fail to find a filesystem so we need to create a new filesystem which might - // need to do disk access (i.e. taking Mutexes in other places) and then register that new filesystem - // in this list, to avoid TOCTOU bugs. - MutexProtected> m_file_backed_file_systems_list {}; - - SpinlockProtected, LockRank::FileSystem> m_file_systems_list {}; -}; - -} diff --git a/Kernel/Firmware/ACPI/Definitions.h b/Kernel/Firmware/ACPI/Definitions.h deleted file mode 100644 index 08c45341b48..00000000000 --- a/Kernel/Firmware/ACPI/Definitions.h +++ /dev/null @@ -1,330 +0,0 @@ -/* - * Copyright (c) 2020, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include - -namespace Kernel::ACPI { - -namespace FADTFlags { - -// https://uefi.org/specs/ACPI/6.4/05_ACPI_Software_Programming_Model/ACPI_Software_Programming_Model.html#fixed-acpi-description-table-fixed-feature-flags -enum class FeatureFlags : u32 { - WBINVD = 1 << 0, - WBINVD_FLUSH = 1 << 1, - PROC_C1 = 1 << 2, - P_LVL2_UP = 1 << 3, - PWR_BUTTON = 1 << 4, - SLP_BUTTON = 1 << 5, - FIX_RTC = 1 << 6, - RTC_s4 = 1 << 7, - TMR_VAL_EXT = 1 << 8, - DCK_CAP = 1 << 9, - RESET_REG_SUPPORTED = 1 << 10, - SEALED_CASE = 1 << 11, - HEADLESS = 1 << 12, - CPU_SW_SLP = 1 << 13, - PCI_EXP_WAK = 1 << 14, - USE_PLATFORM_CLOCK = 1 << 15, - S4_RTC_STS_VALID = 1 << 16, - REMOTE_POWER_ON_CAPABLE = 1 << 17, - FORCE_APIC_CLUSTER_MODEL = 1 << 18, - FORCE_APIC_PHYSICAL_DESTINATION_MODE = 1 << 19, - HW_REDUCED_ACPI = 1 << 20, - LOW_POWER_S0_IDLE_CAPABLE = 1 << 21 -}; - -// https://uefi.org/specs/ACPI/6.4/05_ACPI_Software_Programming_Model/ACPI_Software_Programming_Model.html#fixed-acpi-description-table-boot-ia-pc-boot-architecture-flags -enum class IA_PC_Flags : u8 { - Legacy_Devices = 1 << 0, - PS2_8042 = 1 << 1, - VGA_Not_Present = 1 << 2, - MSI_Not_Supported = 1 << 3, - PCIe_ASPM_Controls = 1 << 4, - CMOS_RTC_Not_Present = 1 << 5 -}; - -struct [[gnu::packed]] HardwareFeatures { - bool wbinvd : 1; - bool wbinvd_flush : 1; - bool processor_c1 : 1; - bool multiprocessor_c2 : 1; - bool power_button : 1; - bool sleep_button : 1; - bool fix_rtc : 1; - bool rtc_s4 : 1; - bool timer_value_extension : 1; - bool docking_capability : 1; - bool reset_register_supported : 1; - bool sealed_case : 1; - bool headless : 1; - bool cpu_software_sleep : 1; - bool pci_express_wake : 1; - bool use_platform_clock : 1; - bool s4_rtc_status_valid : 1; - bool remote_power_on_capable : 1; - bool force_apic_cluster_model : 1; - bool force_apic_physical_destination_mode : 1; - bool hardware_reduced_acpi : 1; - bool low_power_s0_idle_capable : 1; -}; -struct [[gnu::packed]] x86_Specific_Flags { - bool legacy_devices : 1; - bool keyboard_8042 : 1; - bool vga_not_present : 1; - bool msi_not_supported : 1; - bool cmos_rtc_not_present : 1; -}; -}; - -namespace GenericAddressStructure { -enum class AddressSpace { - SystemMemory = 0, - SystemIO = 1, - PCIConfigurationSpace = 2, - EmbeddedController = 3, - SMBus = 4, - PCC = 0xA, - FunctionalFixedHardware = 0x7F -}; -enum class AccessSize { - Undefined = 0, - Byte = 1, - Word = 2, - DWord = 3, - QWord = 4 -}; -enum class BitWidth { - Undefined = 0, - Byte = 8, - Word = 16, - DWord = 32, - QWord = 64 -}; -} - -namespace Structures { - -// https://uefi.org/specs/ACPI/6.4/05_ACPI_Software_Programming_Model/ACPI_Software_Programming_Model.html#root-system-description-pointer-rsdp-structure -struct [[gnu::packed]] RSDPDescriptor { - char sig[8]; - u8 checksum; - char oem_id[6]; - u8 revision; - u32 rsdt_ptr; -}; - -struct [[gnu::packed]] RSDPDescriptor20 { - RSDPDescriptor base; - u32 length; - u64 xsdt_ptr; - u8 ext_checksum; - u8 reserved[3]; -}; - -// https://uefi.org/specs/ACPI/6.4/05_ACPI_Software_Programming_Model/ACPI_Software_Programming_Model.html#system-description-table-header -struct [[gnu::packed]] SDTHeader { - char sig[4]; - u32 length; - u8 revision; - u8 checksum; - char oem_id[6]; - char oem_table_id[8]; - u32 oem_revision; - u32 creator_id; - u32 creator_revision; -}; - -// https://uefi.org/specs/ACPI/6.4/05_ACPI_Software_Programming_Model/ACPI_Software_Programming_Model.html#root-system-description-table-rsdt -struct [[gnu::packed]] RSDT { - SDTHeader h; - u32 table_ptrs[]; -}; - -// https://uefi.org/specs/ACPI/6.4/05_ACPI_Software_Programming_Model/ACPI_Software_Programming_Model.html#extended-system-description-table-xsdt -struct [[gnu::packed]] XSDT { - SDTHeader h; - u64 table_ptrs[]; -}; - -struct [[gnu::packed]] GenericAddressStructure { - u8 address_space; - u8 bit_width; - u8 bit_offset; - u8 access_size; - u64 address; -}; - -struct [[gnu::packed]] HPET { - SDTHeader h; - u8 hardware_revision_id; - u8 attributes; - u16 pci_vendor_id; - GenericAddressStructure event_timer_block; - u8 hpet_number; - u16 mininum_clock_tick; - u8 page_protection; -}; - -// https://uefi.org/specs/ACPI/6.4/05_ACPI_Software_Programming_Model/ACPI_Software_Programming_Model.html#fixed-acpi-description-table-fadt -struct [[gnu::packed]] FADT { - SDTHeader h; - u32 firmware_ctrl; - u32 dsdt_ptr; - u8 reserved; - u8 preferred_pm_profile; - u16 sci_int; - u32 smi_cmd; - u8 acpi_enable_value; - u8 acpi_disable_value; - u8 s4bios_req; - u8 pstate_cnt; - u32 PM1a_EVT_BLK; - u32 PM1b_EVT_BLK; - u32 PM1a_CNT_BLK; - u32 PM1b_CNT_BLK; - u32 PM2_CNT_BLK; - u32 PM_TMR_BLK; - u32 GPE0_BLK; - u32 GPE1_BLK; - u8 PM1_EVT_LEN; - u8 PM1_CNT_LEN; - u8 PM2_CNT_LEN; - u8 PM_TMR_LEN; - u8 GPE0_BLK_LEN; - u8 GPE1_BLK_LEN; - u8 GPE1_BASE; - u8 cst_cnt; - u16 P_LVL2_LAT; - u16 P_LVL3_LAT; - u16 flush_size; - u16 flush_stride; - u8 duty_offset; - u8 duty_width; - u8 day_alrm; - u8 mon_alrm; - u8 century; - u16 ia_pc_boot_arch_flags; - u8 reserved2; - u32 flags; - GenericAddressStructure reset_reg; - u8 reset_value; - u16 arm_boot_arch; - u8 fadt_minor_version; - u64 x_firmware_ctrl; - u64 x_dsdt; - GenericAddressStructure x_pm1a_evt_blk; - GenericAddressStructure x_pm1b_evt_blk; - GenericAddressStructure x_pm1a_cnt_blk; - GenericAddressStructure x_pm1b_cnt_blk; - GenericAddressStructure x_pm2_cnt_blk; - GenericAddressStructure x_pm_tmr_blk; - GenericAddressStructure x_gpe0_blk; - GenericAddressStructure x_gpe1_blk; - GenericAddressStructure sleep_control; - GenericAddressStructure sleep_status; - u64 hypervisor_vendor_identity; -}; - -// https://uefi.org/specs/ACPI/6.4/05_ACPI_Software_Programming_Model/ACPI_Software_Programming_Model.html#interrupt-controller-structure-types -enum class MADTEntryType { - LocalAPIC = 0x0, - IOAPIC = 0x1, - InterruptSourceOverride = 0x2, - NMI_Source = 0x3, - LocalAPIC_NMI = 0x4, - LocalAPIC_Address_Override = 0x5, - IO_SAPIC = 0x6, - Local_SAPIC = 0x7, - Platform_interrupt_Sources = 0x8, - Local_x2APIC = 0x9, - Local_x2APIC_NMI = 0xA, - GIC_CPU = 0xB, - GIC_Distributor = 0xC, - GIC_MSI = 0xD, - GIC_Redistrbutor = 0xE, - GIC_Interrupt_Translation = 0xF -}; - -struct [[gnu::packed]] MADTEntryHeader { - u8 type; - u8 length; -}; - -namespace MADTEntries { - -// https://uefi.org/specs/ACPI/6.4/05_ACPI_Software_Programming_Model/ACPI_Software_Programming_Model.html#i-o-apic-structure -struct [[gnu::packed]] IOAPIC { - MADTEntryHeader h; - u8 ioapic_id; - u8 reserved; - u32 ioapic_address; - u32 gsi_base; -}; - -// https://uefi.org/specs/ACPI/6.4/05_ACPI_Software_Programming_Model/ACPI_Software_Programming_Model.html#processor-local-apic-structure -struct [[gnu::packed]] ProcessorLocalAPIC { - MADTEntryHeader h; - u8 acpi_processor_id; - u8 apic_id; - u32 flags; -}; - -// https://uefi.org/specs/ACPI/6.4/05_ACPI_Software_Programming_Model/ACPI_Software_Programming_Model.html#processor-local-x2apic-structure -struct [[gnu::packed]] ProcessorLocalX2APIC { - MADTEntryHeader h; - u16 reserved; - u32 apic_id; - u32 flags; - u32 acpi_processor_id; -}; - -// https://uefi.org/specs/ACPI/6.4/05_ACPI_Software_Programming_Model/ACPI_Software_Programming_Model.html#interrupt-source-override-structure -struct [[gnu::packed]] InterruptSourceOverride { - MADTEntryHeader h; - u8 bus; - u8 source; - u32 global_system_interrupt; - u16 flags; -}; -} - -// https://uefi.org/specs/ACPI/6.4/05_ACPI_Software_Programming_Model/ACPI_Software_Programming_Model.html#multiple-apic-description-table-madt-format -struct [[gnu::packed]] MADT { - SDTHeader h; - u32 lapic_address; - u32 flags; - MADTEntryHeader entries[]; -}; - -struct [[gnu::packed]] AMLTable { - SDTHeader h; - char aml_code[]; -}; - -struct [[gnu::packed]] PCI_MMIO_Descriptor { - u64 base_addr; - u16 seg_group_number; - u8 start_pci_bus; - u8 end_pci_bus; - u32 reserved; -}; - -struct [[gnu::packed]] MCFG { - SDTHeader header; - u64 reserved; - PCI_MMIO_Descriptor descriptors[]; -}; - -struct [[gnu::packed]] DSDT { - SDTHeader h; - unsigned char definition_block[]; -}; -} - -} diff --git a/Kernel/Firmware/ACPI/Initialize.cpp b/Kernel/Firmware/ACPI/Initialize.cpp deleted file mode 100644 index 77e06e18555..00000000000 --- a/Kernel/Firmware/ACPI/Initialize.cpp +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright (c) 2020-2021, Liav A. - * Copyright (c) 2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include - -namespace Kernel::ACPI { - -UNMAP_AFTER_INIT void initialize() -{ - auto feature_level = kernel_command_line().acpi_feature_level(); - if (feature_level == AcpiFeatureLevel::Disabled) - return; - - auto rsdp = MUST(StaticParsing::find_rsdp_in_platform_specific_memory_locations()); - if (!rsdp.has_value()) - return; - - auto facp = MUST(StaticParsing::find_table(rsdp.value(), "FACP"sv)); - if (!facp.has_value()) - return; - auto facp_table_or_error = Memory::map_typed(facp.value()); - if (facp_table_or_error.is_error()) - return; - u8 irq_line = facp_table_or_error.value()->sci_int; - - Parser::must_initialize(rsdp.value(), facp.value(), irq_line); - if (kernel_command_line().acpi_feature_level() == AcpiFeatureLevel::Enabled) - Parser::the()->enable_aml_parsing(); -} - -bool is_enabled() -{ - return Parser::the(); -} - -} diff --git a/Kernel/Firmware/ACPI/Initialize.h b/Kernel/Firmware/ACPI/Initialize.h deleted file mode 100644 index 4bf3946c296..00000000000 --- a/Kernel/Firmware/ACPI/Initialize.h +++ /dev/null @@ -1,14 +0,0 @@ -/* - * Copyright (c) 2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -namespace Kernel::ACPI { - -bool is_enabled(); -void initialize(); - -} diff --git a/Kernel/Firmware/ACPI/Parser.cpp b/Kernel/Firmware/ACPI/Parser.cpp deleted file mode 100644 index 8f65b93da13..00000000000 --- a/Kernel/Firmware/ACPI/Parser.cpp +++ /dev/null @@ -1,417 +0,0 @@ -/* - * Copyright (c) 2020-2021, Liav A. - * Copyright (c) 2020-2021, Andreas Kling - * Copyright (c) 2022, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include -#if ARCH(X86_64) -# include -# include -#endif -#include -#include -#include -#include -#include -#include - -namespace Kernel::ACPI { - -static Parser* s_acpi_parser; - -Parser* Parser::the() -{ - return s_acpi_parser; -} - -void Parser::must_initialize(PhysicalAddress rsdp, PhysicalAddress fadt, u8 irq_number) -{ - VERIFY(!s_acpi_parser); - s_acpi_parser = new (nothrow) Parser(rsdp, fadt, irq_number); - VERIFY(s_acpi_parser); -} - -UNMAP_AFTER_INIT NonnullLockRefPtr ACPISysFSComponent::create(StringView name, PhysicalAddress paddr, size_t table_size) -{ - // FIXME: Handle allocation failure gracefully - auto table_name = KString::must_create(name); - return adopt_lock_ref(*new (nothrow) ACPISysFSComponent(move(table_name), paddr, table_size)); -} - -ErrorOr ACPISysFSComponent::read_bytes(off_t offset, size_t count, UserOrKernelBuffer& buffer, OpenFileDescription*) const -{ - auto blob = TRY(try_to_generate_buffer()); - - if ((size_t)offset >= blob->size()) - return 0; - - ssize_t nread = min(static_cast(blob->size() - offset), static_cast(count)); - TRY(buffer.write(blob->data() + offset, nread)); - return nread; -} - -ErrorOr> ACPISysFSComponent::try_to_generate_buffer() const -{ - auto acpi_blob = TRY(Memory::map_typed((m_paddr), m_length)); - return KBuffer::try_create_with_bytes("ACPISysFSComponent: Blob"sv, Span { acpi_blob.ptr(), m_length }); -} - -UNMAP_AFTER_INIT ACPISysFSComponent::ACPISysFSComponent(NonnullOwnPtr table_name, PhysicalAddress paddr, size_t table_size) - : SysFSComponent() - , m_paddr(paddr) - , m_length(table_size) - , m_table_name(move(table_name)) -{ -} - -UNMAP_AFTER_INIT void ACPISysFSDirectory::find_tables_and_register_them_as_components() -{ - size_t ssdt_count = 0; - MUST(m_child_components.with([&](auto& list) -> ErrorOr { - ACPI::Parser::the()->enumerate_static_tables([&](StringView signature, PhysicalAddress p_table, size_t length) { - if (signature == "SSDT") { - auto component_name = KString::formatted("{:4s}{}", signature.characters_without_null_termination(), ssdt_count).release_value_but_fixme_should_propagate_errors(); - list.append(ACPISysFSComponent::create(component_name->view(), p_table, length)); - ssdt_count++; - return; - } - list.append(ACPISysFSComponent::create(signature, p_table, length)); - }); - return {}; - })); - - MUST(m_child_components.with([&](auto& list) -> ErrorOr { - auto rsdp = Memory::map_typed(ACPI::Parser::the()->rsdp()).release_value_but_fixme_should_propagate_errors(); - list.append(ACPISysFSComponent::create("RSDP"sv, ACPI::Parser::the()->rsdp(), rsdp->base.revision == 0 ? sizeof(Structures::RSDPDescriptor) : rsdp->length)); - auto main_system_description_table = Memory::map_typed(ACPI::Parser::the()->main_system_description_table()).release_value_but_fixme_should_propagate_errors(); - if (ACPI::Parser::the()->is_xsdt_supported()) { - list.append(ACPISysFSComponent::create("XSDT"sv, ACPI::Parser::the()->main_system_description_table(), main_system_description_table->length)); - } else { - list.append(ACPISysFSComponent::create("RSDT"sv, ACPI::Parser::the()->main_system_description_table(), main_system_description_table->length)); - } - return {}; - })); -} - -UNMAP_AFTER_INIT NonnullLockRefPtr ACPISysFSDirectory::must_create(SysFSFirmwareDirectory& firmware_directory) -{ - auto acpi_directory = MUST(adopt_nonnull_lock_ref_or_enomem(new (nothrow) ACPISysFSDirectory(firmware_directory))); - acpi_directory->find_tables_and_register_them_as_components(); - return acpi_directory; -} - -UNMAP_AFTER_INIT ACPISysFSDirectory::ACPISysFSDirectory(SysFSFirmwareDirectory& firmware_directory) - : SysFSDirectory(firmware_directory) -{ -} - -void Parser::enumerate_static_tables(Function callback) -{ - for (auto& p_table : m_sdt_pointers) { - auto table = Memory::map_typed(p_table).release_value_but_fixme_should_propagate_errors(); - callback({ table->sig, 4 }, p_table, table->length); - } -} - -static bool validate_table(Structures::SDTHeader const&, size_t length); - -UNMAP_AFTER_INIT void Parser::locate_static_data() -{ - locate_main_system_description_table(); - initialize_main_system_description_table(); - process_fadt_data(); - process_dsdt(); -} - -UNMAP_AFTER_INIT Optional Parser::find_table(StringView signature) -{ - dbgln_if(ACPI_DEBUG, "ACPI: Calling Find Table method!"); - for (auto p_sdt : m_sdt_pointers) { - auto sdt_or_error = Memory::map_typed(p_sdt); - if (sdt_or_error.is_error()) { - dbgln_if(ACPI_DEBUG, "ACPI: Failed mapping Table @ {}", p_sdt); - continue; - } - dbgln_if(ACPI_DEBUG, "ACPI: Examining Table @ {}", p_sdt); - if (!strncmp(sdt_or_error.value()->sig, signature.characters_without_null_termination(), 4)) { - dbgln_if(ACPI_DEBUG, "ACPI: Found Table @ {}", p_sdt); - return p_sdt; - } - } - return {}; -} - -bool Parser::handle_irq(RegisterState const&) -{ - TODO(); -} - -UNMAP_AFTER_INIT void Parser::enable_aml_parsing() -{ - // FIXME: When enabled, do other things to "parse AML". - m_can_process_bytecode = true; -} - -UNMAP_AFTER_INIT void Parser::process_fadt_data() -{ - dmesgln("ACPI: Initializing Fixed ACPI data"); - - VERIFY(!m_fadt.is_null()); - dbgln_if(ACPI_DEBUG, "ACPI: FADT @ {}", m_fadt); - - auto sdt = Memory::map_typed(m_fadt).release_value_but_fixme_should_propagate_errors(); - dmesgln("ACPI: Fixed ACPI data, Revision {}, length: {} bytes", (size_t)sdt->h.revision, (size_t)sdt->h.length); - m_x86_specific_flags.cmos_rtc_not_present = (sdt->ia_pc_boot_arch_flags & (u8)FADTFlags::IA_PC_Flags::CMOS_RTC_Not_Present); - - // FIXME: QEMU doesn't report that we have an i8042 controller in these flags, even if it should (when FADT revision is 3), - // Later on, we need to make sure that we enumerate the ACPI namespace (AML encoded), instead of just using this value. - m_x86_specific_flags.keyboard_8042 = (sdt->h.revision <= 3) || (sdt->ia_pc_boot_arch_flags & (u8)FADTFlags::IA_PC_Flags::PS2_8042); - - m_x86_specific_flags.legacy_devices = (sdt->ia_pc_boot_arch_flags & (u8)FADTFlags::IA_PC_Flags::Legacy_Devices); - m_x86_specific_flags.msi_not_supported = (sdt->ia_pc_boot_arch_flags & (u8)FADTFlags::IA_PC_Flags::MSI_Not_Supported); - m_x86_specific_flags.vga_not_present = (sdt->ia_pc_boot_arch_flags & (u8)FADTFlags::IA_PC_Flags::VGA_Not_Present); - - m_hardware_flags.cpu_software_sleep = (sdt->flags & (u32)FADTFlags::FeatureFlags::CPU_SW_SLP); - m_hardware_flags.docking_capability = (sdt->flags & (u32)FADTFlags::FeatureFlags::DCK_CAP); - m_hardware_flags.fix_rtc = (sdt->flags & (u32)FADTFlags::FeatureFlags::FIX_RTC); - m_hardware_flags.force_apic_cluster_model = (sdt->flags & (u32)FADTFlags::FeatureFlags::FORCE_APIC_CLUSTER_MODEL); - m_hardware_flags.force_apic_physical_destination_mode = (sdt->flags & (u32)FADTFlags::FeatureFlags::FORCE_APIC_PHYSICAL_DESTINATION_MODE); - m_hardware_flags.hardware_reduced_acpi = (sdt->flags & (u32)FADTFlags::FeatureFlags::HW_REDUCED_ACPI); - m_hardware_flags.headless = (sdt->flags & (u32)FADTFlags::FeatureFlags::HEADLESS); - m_hardware_flags.low_power_s0_idle_capable = (sdt->flags & (u32)FADTFlags::FeatureFlags::LOW_POWER_S0_IDLE_CAPABLE); - m_hardware_flags.multiprocessor_c2 = (sdt->flags & (u32)FADTFlags::FeatureFlags::P_LVL2_UP); - m_hardware_flags.pci_express_wake = (sdt->flags & (u32)FADTFlags::FeatureFlags::PCI_EXP_WAK); - m_hardware_flags.power_button = (sdt->flags & (u32)FADTFlags::FeatureFlags::PWR_BUTTON); - m_hardware_flags.processor_c1 = (sdt->flags & (u32)FADTFlags::FeatureFlags::PROC_C1); - m_hardware_flags.remote_power_on_capable = (sdt->flags & (u32)FADTFlags::FeatureFlags::REMOTE_POWER_ON_CAPABLE); - m_hardware_flags.reset_register_supported = (sdt->flags & (u32)FADTFlags::FeatureFlags::RESET_REG_SUPPORTED); - m_hardware_flags.rtc_s4 = (sdt->flags & (u32)FADTFlags::FeatureFlags::RTC_s4); - m_hardware_flags.s4_rtc_status_valid = (sdt->flags & (u32)FADTFlags::FeatureFlags::S4_RTC_STS_VALID); - m_hardware_flags.sealed_case = (sdt->flags & (u32)FADTFlags::FeatureFlags::SEALED_CASE); - m_hardware_flags.sleep_button = (sdt->flags & (u32)FADTFlags::FeatureFlags::SLP_BUTTON); - m_hardware_flags.timer_value_extension = (sdt->flags & (u32)FADTFlags::FeatureFlags::TMR_VAL_EXT); - m_hardware_flags.use_platform_clock = (sdt->flags & (u32)FADTFlags::FeatureFlags::USE_PLATFORM_CLOCK); - m_hardware_flags.wbinvd = (sdt->flags & (u32)FADTFlags::FeatureFlags::WBINVD); - m_hardware_flags.wbinvd_flush = (sdt->flags & (u32)FADTFlags::FeatureFlags::WBINVD_FLUSH); -} - -UNMAP_AFTER_INIT void Parser::process_dsdt() -{ - auto sdt = Memory::map_typed(m_fadt).release_value_but_fixme_should_propagate_errors(); - - // Add DSDT-pointer to expose the full table in /sys/firmware/acpi/ - m_sdt_pointers.append(PhysicalAddress(sdt->dsdt_ptr)); - - auto dsdt_or_error = Memory::map_typed(PhysicalAddress(sdt->dsdt_ptr)); - if (dsdt_or_error.is_error()) { - dmesgln("ACPI: DSDT is unmappable"); - return; - } - dmesgln("ACPI: Using DSDT @ {} with {} bytes", PhysicalAddress(sdt->dsdt_ptr), dsdt_or_error.value()->h.length); -} - -bool Parser::can_reboot() -{ - auto fadt_or_error = Memory::map_typed(m_fadt); - if (fadt_or_error.is_error()) - return false; - if (fadt_or_error.value()->h.revision < 2) - return false; - return m_hardware_flags.reset_register_supported; -} - -void Parser::access_generic_address(Structures::GenericAddressStructure const& structure, u32 value) -{ - switch ((GenericAddressStructure::AddressSpace)structure.address_space) { - case GenericAddressStructure::AddressSpace::SystemIO: { -#if ARCH(X86_64) - IOAddress address(structure.address); - dbgln("ACPI: Sending value {:x} to {}", value, address); - switch (structure.access_size) { - case (u8)GenericAddressStructure::AccessSize::QWord: { - dbgln("Trying to send QWord to IO port"); - VERIFY_NOT_REACHED(); - break; - } - case (u8)GenericAddressStructure::AccessSize::Undefined: { - dbgln("ACPI Warning: Unknown access size {}", structure.access_size); - VERIFY(structure.bit_width != (u8)GenericAddressStructure::BitWidth::QWord); - VERIFY(structure.bit_width != (u8)GenericAddressStructure::BitWidth::Undefined); - dbgln("ACPI: Bit Width - {} bits", structure.bit_width); - address.out(value, structure.bit_width); - break; - } - default: - address.out(value, (8 << (structure.access_size - 1))); - break; - } -#endif - return; - } - case GenericAddressStructure::AddressSpace::SystemMemory: { - dbgln("ACPI: Sending value {:x} to {}", value, PhysicalAddress(structure.address)); - switch ((GenericAddressStructure::AccessSize)structure.access_size) { - case GenericAddressStructure::AccessSize::Byte: - *Memory::map_typed(PhysicalAddress(structure.address)).release_value_but_fixme_should_propagate_errors() = value; - break; - case GenericAddressStructure::AccessSize::Word: - *Memory::map_typed(PhysicalAddress(structure.address)).release_value_but_fixme_should_propagate_errors() = value; - break; - case GenericAddressStructure::AccessSize::DWord: - *Memory::map_typed(PhysicalAddress(structure.address)).release_value_but_fixme_should_propagate_errors() = value; - break; - case GenericAddressStructure::AccessSize::QWord: { - *Memory::map_typed(PhysicalAddress(structure.address)).release_value_but_fixme_should_propagate_errors() = value; - break; - } - default: - VERIFY_NOT_REACHED(); - } - return; - } - case GenericAddressStructure::AddressSpace::PCIConfigurationSpace: { - // According to https://uefi.org/specs/ACPI/6.4/05_ACPI_Software_Programming_Model/ACPI_Software_Programming_Model.html#address-space-format, - // PCI addresses must be confined to devices on Segment group 0, bus 0. - auto pci_address = PCI::Address(0, 0, ((structure.address >> 24) & 0xFF), ((structure.address >> 16) & 0xFF)); - dbgln("ACPI: Sending value {:x} to {}", value, pci_address); - u32 offset_in_pci_address = structure.address & 0xFFFF; - if (structure.access_size == (u8)GenericAddressStructure::AccessSize::QWord) { - dbgln("Trying to send QWord to PCI configuration space"); - VERIFY_NOT_REACHED(); - } - VERIFY(structure.access_size != (u8)GenericAddressStructure::AccessSize::Undefined); - auto& pci_device_identifier = PCI::get_device_identifier(pci_address); - PCI::raw_access(pci_device_identifier, offset_in_pci_address, (1 << (structure.access_size - 1)), value); - return; - } - default: - VERIFY_NOT_REACHED(); - } - VERIFY_NOT_REACHED(); -} - -bool Parser::validate_reset_register(Memory::TypedMapping const& fadt) -{ - // According to https://uefi.org/specs/ACPI/6.4/04_ACPI_Hardware_Specification/ACPI_Hardware_Specification.html#reset-register, - // the reset register can only be located in I/O bus, PCI bus or memory-mapped. - return (fadt->reset_reg.address_space == (u8)GenericAddressStructure::AddressSpace::PCIConfigurationSpace || fadt->reset_reg.address_space == (u8)GenericAddressStructure::AddressSpace::SystemMemory || fadt->reset_reg.address_space == (u8)GenericAddressStructure::AddressSpace::SystemIO); -} - -void Parser::try_acpi_reboot() -{ - InterruptDisabler disabler; - if (!can_reboot()) { - dmesgln("ACPI: Reboot not supported!"); - return; - } - dbgln_if(ACPI_DEBUG, "ACPI: Rebooting, probing FADT ({})", m_fadt); - - auto fadt_or_error = Memory::map_typed(m_fadt); - if (fadt_or_error.is_error()) { - dmesgln("ACPI: Failed probing FADT {}", fadt_or_error.error()); - return; - } - auto fadt = fadt_or_error.release_value(); - VERIFY(validate_reset_register(fadt)); - access_generic_address(fadt->reset_reg, fadt->reset_value); - Processor::halt(); -} - -void Parser::try_acpi_shutdown() -{ - dmesgln("ACPI: Shutdown is not supported with the current configuration, aborting!"); -} - -size_t Parser::get_table_size(PhysicalAddress table_header) -{ - InterruptDisabler disabler; - dbgln_if(ACPI_DEBUG, "ACPI: Checking SDT Length"); - return Memory::map_typed(table_header).release_value_but_fixme_should_propagate_errors()->length; -} - -u8 Parser::get_table_revision(PhysicalAddress table_header) -{ - InterruptDisabler disabler; - dbgln_if(ACPI_DEBUG, "ACPI: Checking SDT Revision"); - return Memory::map_typed(table_header).release_value_but_fixme_should_propagate_errors()->revision; -} - -UNMAP_AFTER_INIT void Parser::initialize_main_system_description_table() -{ - dbgln_if(ACPI_DEBUG, "ACPI: Checking Main SDT Length to choose the correct mapping size"); - VERIFY(!m_main_system_description_table.is_null()); - auto length = get_table_size(m_main_system_description_table); - auto revision = get_table_revision(m_main_system_description_table); - - auto sdt = Memory::map_typed(m_main_system_description_table, length).release_value_but_fixme_should_propagate_errors(); - - dmesgln("ACPI: Main Description Table valid? {}", validate_table(*sdt, length)); - - if (m_xsdt_supported) { - auto& xsdt = (Structures::XSDT const&)*sdt; - dmesgln("ACPI: Using XSDT, enumerating tables @ {}", m_main_system_description_table); - dmesgln("ACPI: XSDT revision {}, total length: {}", revision, length); - dbgln_if(ACPI_DEBUG, "ACPI: XSDT pointer @ {}", VirtualAddress { &xsdt }); - for (u32 i = 0; i < ((length - sizeof(Structures::SDTHeader)) / sizeof(u64)); i++) { - dbgln_if(ACPI_DEBUG, "ACPI: Found new table [{0}], @ V{1:p} - P{1:p}", i, &xsdt.table_ptrs[i]); - m_sdt_pointers.append(PhysicalAddress(xsdt.table_ptrs[i])); - } - } else { - auto& rsdt = (Structures::RSDT const&)*sdt; - dmesgln("ACPI: Using RSDT, enumerating tables @ {}", m_main_system_description_table); - dmesgln("ACPI: RSDT revision {}, total length: {}", revision, length); - dbgln_if(ACPI_DEBUG, "ACPI: RSDT pointer @ V{}", &rsdt); - for (u32 i = 0; i < ((length - sizeof(Structures::SDTHeader)) / sizeof(u32)); i++) { - dbgln_if(ACPI_DEBUG, "ACPI: Found new table [{0}], @ V{1:p} - P{1:p}", i, &rsdt.table_ptrs[i]); - m_sdt_pointers.append(PhysicalAddress(rsdt.table_ptrs[i])); - } - } -} - -UNMAP_AFTER_INIT void Parser::locate_main_system_description_table() -{ - auto rsdp = Memory::map_typed(m_rsdp).release_value_but_fixme_should_propagate_errors(); - if (rsdp->base.revision == 0) { - m_xsdt_supported = false; - } else if (rsdp->base.revision >= 2) { - if (rsdp->xsdt_ptr != (u64) nullptr) { - m_xsdt_supported = true; - } else { - m_xsdt_supported = false; - } - } - if (!m_xsdt_supported) { - m_main_system_description_table = PhysicalAddress(rsdp->base.rsdt_ptr); - } else { - m_main_system_description_table = PhysicalAddress(rsdp->xsdt_ptr); - } -} - -UNMAP_AFTER_INIT Parser::Parser(PhysicalAddress rsdp, PhysicalAddress fadt, u8 irq_number) - : IRQHandler(irq_number) - , m_rsdp(rsdp) - , m_fadt(fadt) -{ - dmesgln("ACPI: Using RSDP @ {}", rsdp); - locate_static_data(); -} - -static bool validate_table(Structures::SDTHeader const& v_header, size_t length) -{ - u8 checksum = 0; - auto* sdt = (u8 const*)&v_header; - for (size_t i = 0; i < length; i++) - checksum += sdt[i]; - if (checksum == 0) - return true; - return false; -} - -} diff --git a/Kernel/Firmware/ACPI/Parser.h b/Kernel/Firmware/ACPI/Parser.h deleted file mode 100644 index f5168cc3269..00000000000 --- a/Kernel/Firmware/ACPI/Parser.h +++ /dev/null @@ -1,111 +0,0 @@ -/* - * Copyright (c) 2020-2021, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace Kernel::ACPI { - -class ACPISysFSDirectory : public SysFSDirectory { -public: - virtual StringView name() const override { return "acpi"sv; } - static NonnullLockRefPtr must_create(SysFSFirmwareDirectory& firmware_directory); - -private: - void find_tables_and_register_them_as_components(); - explicit ACPISysFSDirectory(SysFSFirmwareDirectory& firmware_directory); -}; - -class ACPISysFSComponent : public SysFSComponent { -public: - static NonnullLockRefPtr create(StringView name, PhysicalAddress, size_t table_size); - virtual StringView name() const override { return m_table_name->view(); } - virtual ErrorOr read_bytes(off_t, size_t, UserOrKernelBuffer&, OpenFileDescription*) const override; - - virtual size_t size() const override final { return m_length; } - -protected: - ErrorOr> try_to_generate_buffer() const; - ACPISysFSComponent(NonnullOwnPtr table_name, PhysicalAddress, size_t table_size); - - PhysicalAddress m_paddr; - size_t m_length { 0 }; - NonnullOwnPtr m_table_name; -}; - -class Parser final : public IRQHandler { -public: - static Parser* the(); - - static void must_initialize(PhysicalAddress rsdp, PhysicalAddress fadt, u8 irq_number); - - virtual StringView purpose() const override { return "ACPI Parser"sv; } - virtual bool handle_irq(RegisterState const&) override; - - Optional find_table(StringView signature); - - void try_acpi_reboot(); - bool can_reboot(); - void try_acpi_shutdown(); - bool can_shutdown() { return false; } - - void enable_aml_parsing(); - - PhysicalAddress rsdp() const { return m_rsdp; } - PhysicalAddress main_system_description_table() const { return m_main_system_description_table; } - bool is_xsdt_supported() const { return m_xsdt_supported; } - - void enumerate_static_tables(Function); - - virtual bool have_8042() const - { - return m_x86_specific_flags.keyboard_8042; - } - - FADTFlags::HardwareFeatures const& hardware_features() const { return m_hardware_flags; } - FADTFlags::x86_Specific_Flags const& x86_specific_flags() const { return m_x86_specific_flags; } - - ~Parser() = default; - -private: - Parser(PhysicalAddress rsdp, PhysicalAddress fadt, u8 irq_number); - - void locate_static_data(); - void locate_main_system_description_table(); - void initialize_main_system_description_table(); - size_t get_table_size(PhysicalAddress); - u8 get_table_revision(PhysicalAddress); - void process_fadt_data(); - void process_dsdt(); - - bool validate_reset_register(Memory::TypedMapping const&); - void access_generic_address(Structures::GenericAddressStructure const&, u32 value); - - PhysicalAddress m_rsdp; - PhysicalAddress m_main_system_description_table; - - Vector m_sdt_pointers; - PhysicalAddress m_fadt; - - bool m_xsdt_supported { false }; - bool m_can_process_bytecode { false }; - FADTFlags::HardwareFeatures m_hardware_flags; - FADTFlags::x86_Specific_Flags m_x86_specific_flags; -}; - -} diff --git a/Kernel/Firmware/ACPI/StaticParsing.cpp b/Kernel/Firmware/ACPI/StaticParsing.cpp deleted file mode 100644 index 2b967f01bda..00000000000 --- a/Kernel/Firmware/ACPI/StaticParsing.cpp +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Copyright (c) 2023, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include - -namespace Kernel::ACPI::StaticParsing { - -static bool match_table_signature(PhysicalAddress table_header, StringView signature) -{ - // FIXME: There's no validation of ACPI tables here. Use the checksum to validate the tables. - VERIFY(signature.length() == 4); - - auto table = Memory::map_typed(table_header).release_value_but_fixme_should_propagate_errors(); - return !strncmp(table->h.sig, signature.characters_without_null_termination(), 4); -} - -ErrorOr> search_table_in_xsdt(PhysicalAddress xsdt_address, StringView signature) -{ - // FIXME: There's no validation of ACPI tables here. Use the checksum to validate the tables. - VERIFY(signature.length() == 4); - - auto xsdt = TRY(Memory::map_typed(xsdt_address)); - - for (size_t i = 0; i < ((xsdt->h.length - sizeof(Structures::SDTHeader)) / sizeof(u64)); ++i) { - if (match_table_signature(PhysicalAddress((PhysicalPtr)xsdt->table_ptrs[i]), signature)) - return PhysicalAddress((PhysicalPtr)xsdt->table_ptrs[i]); - } - return Optional {}; -} - -ErrorOr> find_table(PhysicalAddress rsdp_address, StringView signature) -{ - // FIXME: There's no validation of ACPI tables here. Use the checksum to validate the tables. - VERIFY(signature.length() == 4); - - auto rsdp = TRY(Memory::map_typed(rsdp_address)); - - if (rsdp->base.revision == 0) - return search_table_in_rsdt(PhysicalAddress(rsdp->base.rsdt_ptr), signature); - - if (rsdp->base.revision >= 2) { - if (rsdp->xsdt_ptr) - return search_table_in_xsdt(PhysicalAddress(rsdp->xsdt_ptr), signature); - return search_table_in_rsdt(PhysicalAddress(rsdp->base.rsdt_ptr), signature); - } - VERIFY_NOT_REACHED(); -} - -ErrorOr> search_table_in_rsdt(PhysicalAddress rsdt_address, StringView signature) -{ - // FIXME: There's no validation of ACPI tables here. Use the checksum to validate the tables. - VERIFY(signature.length() == 4); - - auto rsdt = TRY(Memory::map_typed(rsdt_address)); - - for (u32 i = 0; i < ((rsdt->h.length - sizeof(Structures::SDTHeader)) / sizeof(u32)); i++) { - if (match_table_signature(PhysicalAddress((PhysicalPtr)rsdt->table_ptrs[i]), signature)) - return PhysicalAddress((PhysicalPtr)rsdt->table_ptrs[i]); - } - return Optional {}; -} - -} diff --git a/Kernel/Firmware/ACPI/StaticParsing.h b/Kernel/Firmware/ACPI/StaticParsing.h deleted file mode 100644 index fec03ff1f4a..00000000000 --- a/Kernel/Firmware/ACPI/StaticParsing.h +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright (c) 2023, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include - -namespace Kernel::ACPI::StaticParsing { - -ErrorOr> find_table(PhysicalAddress rsdp, StringView signature); -ErrorOr> search_table_in_xsdt(PhysicalAddress xsdt, StringView signature); -ErrorOr> search_table_in_rsdt(PhysicalAddress rsdt, StringView signature); - -// NOTE: This function is implemented for each CPU architecture in a subdirectory -// under the Kernel/Arch directory. -ErrorOr> find_rsdp_in_platform_specific_memory_locations(); - -} diff --git a/Kernel/Forward.h b/Kernel/Forward.h deleted file mode 100644 index 81d990ed93d..00000000000 --- a/Kernel/Forward.h +++ /dev/null @@ -1,105 +0,0 @@ -/* - * Copyright (c) 2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include - -namespace Kernel { - -enum class LockRank; - -class BlockDevice; -class CharacterDevice; -class Coredump; -class Credentials; -class CustodyBase; -class Custody; -class Device; -class DeviceControlDevice; -class DiskCache; -class DoubleBuffer; -class File; -class FATInode; -class OpenFileDescription; -class DisplayConnector; -class FileSystem; -class FutexQueue; -class IPv4Socket; -class Inode; -class InodeIdentifier; -class InodeWatcher; -class MountFile; -class Jail; -class KBuffer; -class KString; -class LocalSocket; -class LoopDevice; -class Mutex; -class MasterPTY; -class Mount; -class PerformanceEventBuffer; -class ProcFS; -class ProcFSInode; -class Process; -class ProcessGroup; -class RAMFS; -template -class RecursiveSpinlock; -class Scheduler; -class Socket; -class SysFS; -class SysFSDirectory; -class SysFSRootDirectory; -class SysFSBusDirectory; -class SysFSDevicesDirectory; -class SysFSDirectoryInode; -class SysFSInode; -class TCPSocket; -class TTY; -class Thread; -class ThreadTracer; -class RAMFSInode; -class UDPSocket; -class UserOrKernelBuffer; -class VirtualFileSystem; -class WaitQueue; -class WorkQueue; - -namespace Memory { -class AddressSpace; -class AnonymousVMObject; -class InodeVMObject; -class MappedROM; -class MemoryManager; -class PageDirectory; -class PhysicalRAMPage; -class PhysicalRegion; -class PrivateInodeVMObject; -class Region; -class SharedInodeVMObject; -class VMObject; -class VirtualRange; -} - -template -class Spinlock; -template -class SpinlockLocker; - -struct InodeMetadata; -struct TrapFrame; - -AK_TYPEDEF_DISTINCT_ORDERED_ID(pid_t, ProcessID); -AK_TYPEDEF_DISTINCT_ORDERED_ID(pid_t, ThreadID); -AK_TYPEDEF_DISTINCT_ORDERED_ID(pid_t, SessionID); -AK_TYPEDEF_DISTINCT_ORDERED_ID(pid_t, ProcessGroupID); - -AK_TYPEDEF_DISTINCT_ORDERED_ID(uid_t, UserID); -AK_TYPEDEF_DISTINCT_ORDERED_ID(gid_t, GroupID); - -} diff --git a/Kernel/Heap/Heap.h b/Kernel/Heap/Heap.h deleted file mode 100644 index a21ead3cdaf..00000000000 --- a/Kernel/Heap/Heap.h +++ /dev/null @@ -1,182 +0,0 @@ -/* - * Copyright (c) 2020, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include -#include - -namespace Kernel { - -enum class CallerWillInitializeMemory { - No, - Yes, -}; - -template -class Heap { - AK_MAKE_NONCOPYABLE(Heap); - - struct AllocationHeader { - size_t allocation_size_in_chunks; -#if ARCH(X86_64) || ARCH(AARCH64) - // FIXME: Get rid of this somehow - size_t alignment_dummy; -#endif - u8 data[0]; - }; - - static_assert(CHUNK_SIZE >= sizeof(AllocationHeader)); - - ALWAYS_INLINE AllocationHeader* allocation_header(void* ptr) - { - return (AllocationHeader*)((((u8*)ptr) - sizeof(AllocationHeader))); - } - ALWAYS_INLINE AllocationHeader const* allocation_header(void const* ptr) const - { - return (AllocationHeader const*)((((u8 const*)ptr) - sizeof(AllocationHeader))); - } - - static size_t calculate_chunks(size_t memory_size) - { - return (sizeof(u8) * memory_size) / (sizeof(u8) * CHUNK_SIZE + 1); - } - -public: - static constexpr size_t AllocationHeaderSize = sizeof(AllocationHeader); - - Heap(u8* memory, size_t memory_size) - : m_total_chunks(calculate_chunks(memory_size)) - , m_chunks(memory) - , m_bitmap(memory + m_total_chunks * CHUNK_SIZE, m_total_chunks) - { - // To keep the alignment of the memory passed in, place the bitmap - // at the end of the memory block. - VERIFY(m_total_chunks * CHUNK_SIZE + (m_total_chunks + 7) / 8 <= memory_size); - } - ~Heap() = default; - - static size_t calculate_memory_for_bytes(size_t bytes) - { - size_t needed_chunks = (sizeof(AllocationHeader) + bytes + CHUNK_SIZE - 1) / CHUNK_SIZE; - return needed_chunks * CHUNK_SIZE + (needed_chunks + 7) / 8; - } - - void* allocate(size_t size, size_t alignment, [[maybe_unused]] CallerWillInitializeMemory caller_will_initialize_memory) - { - // The minimum possible alignment is CHUNK_SIZE, since we only track chunks here, nothing smaller. - if (alignment < CHUNK_SIZE) - alignment = CHUNK_SIZE; - - // We need space for the AllocationHeader at the head of the block. - size_t real_size = size + sizeof(AllocationHeader); - size_t chunks_needed = (real_size + CHUNK_SIZE - 1) / CHUNK_SIZE; - size_t chunk_alignment = (alignment + CHUNK_SIZE - 1) / CHUNK_SIZE; - - if (chunks_needed > free_chunks()) - return nullptr; - - Optional first_chunk; - - // Choose the right policy for allocation. - // FIXME: These should utilize the alignment directly instead of trying to allocate `size + alignment`. - constexpr u32 best_fit_threshold = 128; - if (chunks_needed < best_fit_threshold) { - first_chunk = m_bitmap.find_first_fit(chunks_needed + chunk_alignment); - } else { - first_chunk = m_bitmap.find_best_fit(chunks_needed + chunk_alignment); - } - - if (!first_chunk.has_value()) - return nullptr; - - auto* a = (AllocationHeader*)(m_chunks + (first_chunk.value() * CHUNK_SIZE)); - - // Align the starting address and verify that we haven't gone outside the calculated free area. - a = (AllocationHeader*)((FlatPtr)a + alignment - (FlatPtr)a->data % alignment); - auto aligned_first_chunk = ((FlatPtr)a - (FlatPtr)m_chunks) / CHUNK_SIZE; - VERIFY(first_chunk.value() <= aligned_first_chunk); - VERIFY(aligned_first_chunk + chunks_needed <= first_chunk.value() + chunks_needed + chunk_alignment); - -#ifdef HAS_ADDRESS_SANITIZER - AddressSanitizer::mark_region((FlatPtr)a, real_size, (chunks_needed * CHUNK_SIZE), AddressSanitizer::ShadowType::Malloc); -#endif - - u8* ptr = a->data; - a->allocation_size_in_chunks = chunks_needed; - - m_bitmap.set_range_and_verify_that_all_bits_flip(aligned_first_chunk, chunks_needed, true); - - m_allocated_chunks += chunks_needed; -#ifndef HAS_ADDRESS_SANITIZER - if (caller_will_initialize_memory == CallerWillInitializeMemory::No) { - if constexpr (HEAP_SCRUB_BYTE_ALLOC != 0) { - __builtin_memset(ptr, HEAP_SCRUB_BYTE_ALLOC, (chunks_needed * CHUNK_SIZE) - sizeof(AllocationHeader)); - } - } -#endif - - VERIFY((FlatPtr)ptr % alignment == 0); - return ptr; - } - - void deallocate(void* ptr) - { - if (!ptr) - return; - auto* a = allocation_header(ptr); - VERIFY((u8*)a >= m_chunks && (u8*)ptr < m_chunks + m_total_chunks * CHUNK_SIZE); - FlatPtr start = ((FlatPtr)a - (FlatPtr)m_chunks) / CHUNK_SIZE; - - // First, verify that the start of the allocation at `ptr` is actually allocated. - VERIFY(m_bitmap.get(start)); - - VERIFY((u8*)a + a->allocation_size_in_chunks * CHUNK_SIZE <= m_chunks + m_total_chunks * CHUNK_SIZE); - m_bitmap.set_range_and_verify_that_all_bits_flip(start, a->allocation_size_in_chunks, false); - - VERIFY(m_allocated_chunks >= a->allocation_size_in_chunks); - m_allocated_chunks -= a->allocation_size_in_chunks; - -#ifdef HAS_ADDRESS_SANITIZER - AddressSanitizer::fill_shadow((FlatPtr)a, a->allocation_size_in_chunks * CHUNK_SIZE, AddressSanitizer::ShadowType::Free); -#else - if constexpr (HEAP_SCRUB_BYTE_FREE != 0) { - __builtin_memset(a, HEAP_SCRUB_BYTE_FREE, a->allocation_size_in_chunks * CHUNK_SIZE); - } -#endif - } - - bool contains(void const* ptr) const - { - auto const* a = allocation_header(ptr); - if ((u8 const*)a < m_chunks) - return false; - if ((u8 const*)ptr >= m_chunks + m_total_chunks * CHUNK_SIZE) - return false; - return true; - } - - u8* memory() const { return m_chunks; } - - size_t total_chunks() const { return m_total_chunks; } - size_t total_bytes() const { return m_total_chunks * CHUNK_SIZE; } - size_t free_chunks() const { return m_total_chunks - m_allocated_chunks; } - size_t free_bytes() const { return free_chunks() * CHUNK_SIZE; } - size_t allocated_chunks() const { return m_allocated_chunks; } - size_t allocated_bytes() const { return m_allocated_chunks * CHUNK_SIZE; } - -private: - size_t m_total_chunks { 0 }; - size_t m_allocated_chunks { 0 }; - u8* m_chunks { nullptr }; - Bitmap m_bitmap; -}; - -} diff --git a/Kernel/Heap/kmalloc.cpp b/Kernel/Heap/kmalloc.cpp deleted file mode 100644 index 76c66658730..00000000000 --- a/Kernel/Heap/kmalloc.cpp +++ /dev/null @@ -1,607 +0,0 @@ -/* - * Copyright (c) 2018-2021, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#if ARCH(X86_64) || ARCH(AARCH64) || ARCH(RISCV64) -static constexpr size_t CHUNK_SIZE = 64; -#else -# error Unknown architecture -#endif -static_assert(is_power_of_two(CHUNK_SIZE)); - -static constexpr size_t INITIAL_KMALLOC_MEMORY_SIZE = 2 * MiB; -static constexpr size_t KMALLOC_DEFAULT_ALIGNMENT = 16; - -// Treat the heap as logically separate from .bss -__attribute__((section(".heap"))) static u8 initial_kmalloc_memory[INITIAL_KMALLOC_MEMORY_SIZE]; - -namespace std { -nothrow_t const nothrow; -} - -// FIXME: Figure out whether this can be MemoryManager. -static RecursiveSpinlock s_lock {}; // needs to be recursive because of dump_backtrace() - -struct KmallocSubheap { - KmallocSubheap(u8* base, size_t size) - : allocator(base, size) - { - } - - IntrusiveListNode list_node; - using List = IntrusiveList<&KmallocSubheap::list_node>; - Heap allocator; -}; - -class KmallocSlabBlock { -public: - static constexpr size_t block_size = 64 * KiB; - static constexpr FlatPtr block_mask = ~(block_size - 1); - - KmallocSlabBlock(size_t slab_size) - : m_slab_size(slab_size) - , m_slab_count((block_size - sizeof(KmallocSlabBlock)) / slab_size) - { - for (size_t i = 0; i < m_slab_count; ++i) { - auto* freelist_entry = (FreelistEntry*)(void*)(&m_data[i * slab_size]); - freelist_entry->next = m_freelist; - m_freelist = freelist_entry; - } - } - - void* allocate([[maybe_unused]] size_t requested_size) - { - VERIFY(m_freelist); - ++m_allocated_slabs; -#ifdef HAS_ADDRESS_SANITIZER - AddressSanitizer::fill_shadow((FlatPtr)m_freelist, sizeof(FreelistEntry::next), Kernel::AddressSanitizer::ShadowType::Unpoisoned8Bytes); -#endif - auto* ptr = exchange(m_freelist, m_freelist->next); -#ifdef HAS_ADDRESS_SANITIZER - AddressSanitizer::mark_region((FlatPtr)ptr, requested_size, m_slab_size, AddressSanitizer::ShadowType::Malloc); -#endif - return ptr; - } - - void deallocate(void* ptr) - { - VERIFY(ptr >= &m_data && ptr < ((u8*)this + block_size)); - --m_allocated_slabs; - auto* freelist_entry = (FreelistEntry*)ptr; -#ifdef HAS_ADDRESS_SANITIZER - AddressSanitizer::fill_shadow((FlatPtr)freelist_entry, sizeof(FreelistEntry::next), Kernel::AddressSanitizer::ShadowType::Unpoisoned8Bytes); -#endif - freelist_entry->next = m_freelist; -#ifdef HAS_ADDRESS_SANITIZER - AddressSanitizer::fill_shadow((FlatPtr)freelist_entry, m_slab_size, AddressSanitizer::ShadowType::Free); -#endif - m_freelist = freelist_entry; - } - - bool is_full() const - { - return m_freelist == nullptr; - } - - size_t allocated_bytes() const - { - return m_allocated_slabs * m_slab_size; - } - - size_t free_bytes() const - { - return (m_slab_count - m_allocated_slabs) * m_slab_size; - } - - IntrusiveListNode list_node; - using List = IntrusiveList<&KmallocSlabBlock::list_node>; - -private: - struct FreelistEntry { - FreelistEntry* next; - }; - - FreelistEntry* m_freelist { nullptr }; - - size_t m_slab_size { 0 }; - size_t m_slab_count { 0 }; - size_t m_allocated_slabs { 0 }; - - [[gnu::aligned(16)]] u8 m_data[]; -}; - -class KmallocSlabheap { -public: - KmallocSlabheap(size_t slab_size) - : m_slab_size(slab_size) - { - } - - size_t slab_size() const { return m_slab_size; } - - void* allocate(size_t requested_size, [[maybe_unused]] CallerWillInitializeMemory caller_will_initialize_memory) - { - if (m_usable_blocks.is_empty()) { - // FIXME: This allocation wastes `block_size` bytes due to the implementation of kmalloc_aligned(). - // Handle this with a custom VM+page allocator instead of using kmalloc_aligned(). - auto* slot = kmalloc_aligned(KmallocSlabBlock::block_size, KmallocSlabBlock::block_size); - if (!slot) { - dbgln_if(KMALLOC_DEBUG, "OOM while growing slabheap ({})", m_slab_size); - return nullptr; - } - auto* block = new (slot) KmallocSlabBlock(m_slab_size); - m_usable_blocks.append(*block); - } - auto* block = m_usable_blocks.first(); - auto* ptr = block->allocate(requested_size); - if (block->is_full()) - m_full_blocks.append(*block); - -#ifndef HAS_ADDRESS_SANITIZER - if (caller_will_initialize_memory == CallerWillInitializeMemory::No) { - memset(ptr, KMALLOC_SCRUB_BYTE, m_slab_size); - } -#endif - return ptr; - } - - void deallocate(void* ptr) - { -#ifndef HAS_ADDRESS_SANITIZER - memset(ptr, KFREE_SCRUB_BYTE, m_slab_size); -#endif - - auto* block = (KmallocSlabBlock*)((FlatPtr)ptr & KmallocSlabBlock::block_mask); - bool block_was_full = block->is_full(); - block->deallocate(ptr); - if (block_was_full) - m_usable_blocks.append(*block); - } - - size_t allocated_bytes() const - { - size_t total = m_full_blocks.size_slow() * KmallocSlabBlock::block_size; - for (auto const& slab_block : m_usable_blocks) - total += slab_block.allocated_bytes(); - return total; - } - - size_t free_bytes() const - { - size_t total = 0; - for (auto const& slab_block : m_usable_blocks) - total += slab_block.free_bytes(); - return total; - } - - bool try_purge() - { - bool did_purge = false; - - // Note: We cannot remove children from the list when using a structured loop, - // Because we need to advance the iterator before we delete the underlying - // value, so we have to iterate manually - - auto block = m_usable_blocks.begin(); - while (block != m_usable_blocks.end()) { - if (block->allocated_bytes() != 0) { - ++block; - continue; - } - auto& block_to_remove = *block; - ++block; - block_to_remove.list_node.remove(); - block_to_remove.~KmallocSlabBlock(); - kfree_sized(&block_to_remove, KmallocSlabBlock::block_size); - - did_purge = true; - } - return did_purge; - } - -private: - size_t m_slab_size { 0 }; - - KmallocSlabBlock::List m_usable_blocks; - KmallocSlabBlock::List m_full_blocks; -}; - -struct KmallocGlobalData { - static constexpr size_t minimum_subheap_size = 1 * MiB; - - KmallocGlobalData(u8* initial_heap, size_t initial_heap_size) - { - add_subheap(initial_heap, initial_heap_size); - } - - void add_subheap(u8* storage, size_t storage_size) - { - dbgln_if(KMALLOC_DEBUG, "Adding kmalloc subheap @ {} with size {}", storage, storage_size); - static_assert(sizeof(KmallocSubheap) <= PAGE_SIZE); - auto* subheap = new (storage) KmallocSubheap(storage + PAGE_SIZE, storage_size - PAGE_SIZE); - subheaps.append(*subheap); - } - - void* allocate(size_t size, size_t alignment, CallerWillInitializeMemory caller_will_initialize_memory) - { - VERIFY(!expansion_in_progress); - - for (auto& slabheap : slabheaps) { - if (size <= slabheap.slab_size() && alignment <= slabheap.slab_size()) - return slabheap.allocate(size, caller_will_initialize_memory); - } - - for (auto& subheap : subheaps) { - if (auto* ptr = subheap.allocator.allocate(size, alignment, caller_will_initialize_memory)) - return ptr; - } - - // NOTE: This size calculation is a mirror of kmalloc_aligned(KmallocSlabBlock) - if (size <= KmallocSlabBlock::block_size * 2 + sizeof(ptrdiff_t) + sizeof(size_t)) { - // FIXME: We should propagate a freed pointer, to find the specific subheap it belonged to - // This would save us iterating over them in the next step and remove a recursion - bool did_purge = false; - for (auto& slabheap : slabheaps) { - if (slabheap.try_purge()) { - dbgln_if(KMALLOC_DEBUG, "Kmalloc purged block(s) from slabheap of size {} to avoid expansion", slabheap.slab_size()); - did_purge = true; - break; - } - } - if (did_purge) - return allocate(size, alignment, caller_will_initialize_memory); - } - - if (!try_expand(size)) { - dbgln_if(KMALLOC_DEBUG, "OOM when trying to expand kmalloc heap"); - return nullptr; - } - - return allocate(size, alignment, caller_will_initialize_memory); - } - - void deallocate(void* ptr, size_t size) - { - VERIFY(!expansion_in_progress); - VERIFY(is_valid_kmalloc_address(VirtualAddress { ptr })); - - for (auto& slabheap : slabheaps) { - if (size <= slabheap.slab_size()) - return slabheap.deallocate(ptr); - } - - for (auto& subheap : subheaps) { - if (subheap.allocator.contains(ptr)) { - subheap.allocator.deallocate(ptr); - return; - } - } - - PANIC("Bogus pointer passed to kfree_sized({:p}, {})", ptr, size); - } - - size_t allocated_bytes() const - { - size_t total = 0; - for (auto const& subheap : subheaps) - total += subheap.allocator.allocated_bytes(); - for (auto const& slabheap : slabheaps) - total += slabheap.allocated_bytes(); - return total; - } - - size_t free_bytes() const - { - size_t total = 0; - for (auto const& subheap : subheaps) - total += subheap.allocator.free_bytes(); - for (auto const& slabheap : slabheaps) - total += slabheap.free_bytes(); - return total; - } - - bool try_expand(size_t allocation_request) - { - VERIFY(!expansion_in_progress); - TemporaryChange change(expansion_in_progress, true); - - auto new_subheap_base = expansion_data->next_virtual_address; - Checked padded_allocation_request = allocation_request; - padded_allocation_request *= 2; - padded_allocation_request += PAGE_SIZE; - if (padded_allocation_request.has_overflow()) { - PANIC("Integer overflow during kmalloc heap expansion"); - } - auto rounded_allocation_request = Memory::page_round_up(padded_allocation_request.value()); - if (rounded_allocation_request.is_error()) { - PANIC("Integer overflow computing pages for kmalloc heap expansion"); - } - size_t new_subheap_size = max(minimum_subheap_size, rounded_allocation_request.value()); - - dbgln_if(KMALLOC_DEBUG, "Unable to allocate {}, expanding kmalloc heap", allocation_request); - - if (!expansion_data->virtual_range.contains(new_subheap_base, new_subheap_size)) { - dbgln_if(KMALLOC_DEBUG, "Out of address space when expanding kmalloc heap"); - return false; - } - - auto physical_pages_or_error = MM.commit_physical_pages(new_subheap_size / PAGE_SIZE); - if (physical_pages_or_error.is_error()) { - dbgln_if(KMALLOC_DEBUG, "Out of address space when expanding kmalloc heap"); - return false; - } - auto physical_pages = physical_pages_or_error.release_value(); - - expansion_data->next_virtual_address = expansion_data->next_virtual_address.offset(new_subheap_size); - - auto cpu_supports_nx = Processor::current().has_nx(); - - SpinlockLocker pd_locker(MM.kernel_page_directory().get_lock()); - - for (auto vaddr = new_subheap_base; !physical_pages.is_empty(); vaddr = vaddr.offset(PAGE_SIZE)) { - // FIXME: We currently leak physical memory when mapping it into the kmalloc heap. - auto& page = physical_pages.take_one().leak_ref(); - auto* pte = MM.pte(MM.kernel_page_directory(), vaddr); - VERIFY(pte); - pte->set_physical_page_base(page.paddr().get()); - pte->set_global(true); - pte->set_user_allowed(false); - pte->set_writable(true); - if (cpu_supports_nx) - pte->set_execute_disabled(true); - pte->set_present(true); - } - - add_subheap(new_subheap_base.as_ptr(), new_subheap_size); - return true; - } - - void enable_expansion() - { - // FIXME: This range can be much bigger on 64-bit, but we need to figure something out for 32-bit. - auto reserved_region = MUST(MM.allocate_unbacked_region_anywhere(64 * MiB, 1 * MiB)); - - expansion_data = KmallocGlobalData::ExpansionData { - .virtual_range = reserved_region->range(), - .next_virtual_address = reserved_region->range().base(), - }; - - // Make sure the entire kmalloc VM range is backed by page tables. - // This avoids having to deal with lazy page table allocation during heap expansion. - SpinlockLocker pd_locker(MM.kernel_page_directory().get_lock()); - for (auto vaddr = reserved_region->range().base(); vaddr < reserved_region->range().end(); vaddr = vaddr.offset(PAGE_SIZE)) { - MM.ensure_pte(MM.kernel_page_directory(), vaddr); - } - - (void)reserved_region.leak_ptr(); - } - - struct ExpansionData { - Memory::VirtualRange virtual_range; - VirtualAddress next_virtual_address; - }; - Optional expansion_data; - - bool is_valid_kmalloc_address(VirtualAddress vaddr) const - { - if (vaddr.as_ptr() >= initial_kmalloc_memory && vaddr.as_ptr() < (initial_kmalloc_memory + INITIAL_KMALLOC_MEMORY_SIZE)) - return true; - - if (!expansion_data.has_value()) - return false; - - return expansion_data->virtual_range.contains(vaddr); - } - - KmallocSubheap::List subheaps; - - KmallocSlabheap slabheaps[6] = { 16, 32, 64, 128, 256, 512 }; - - bool expansion_in_progress { false }; -}; - -READONLY_AFTER_INIT static KmallocGlobalData* g_kmalloc_global; -alignas(KmallocGlobalData) static u8 g_kmalloc_global_heap[sizeof(KmallocGlobalData)]; - -static size_t g_kmalloc_call_count; -static size_t g_kfree_call_count; -static size_t g_nested_kfree_calls; -bool g_dump_kmalloc_stacks; - -void kmalloc_enable_expand() -{ - g_kmalloc_global->enable_expansion(); -} - -UNMAP_AFTER_INIT void kmalloc_init() -{ - // Zero out heap since it's placed after end_of_kernel_bss. - memset(initial_kmalloc_memory, 0, sizeof(initial_kmalloc_memory)); - g_kmalloc_global = new (g_kmalloc_global_heap) KmallocGlobalData(initial_kmalloc_memory, sizeof(initial_kmalloc_memory)); - - s_lock.initialize(); -} - -static void* kmalloc_impl(size_t size, size_t alignment, CallerWillInitializeMemory caller_will_initialize_memory) -{ - // Catch bad callers allocating under spinlock. - if constexpr (KMALLOC_VERIFY_NO_SPINLOCK_HELD) { - Processor::verify_no_spinlocks_held(); - } - - // Alignment must be a power of two. - VERIFY(is_power_of_two(alignment)); - - SpinlockLocker lock(s_lock); - ++g_kmalloc_call_count; - - if (g_dump_kmalloc_stacks && Kernel::g_kernel_symbols_available.was_set()) { - dbgln("kmalloc({})", size); - Kernel::dump_backtrace(); - } - - void* ptr = g_kmalloc_global->allocate(size, alignment, caller_will_initialize_memory); - - Thread* current_thread = Thread::current(); - if (!current_thread) - current_thread = Processor::idle_thread(); - if (current_thread) { - // FIXME: By the time we check this, we have already allocated above. - // This means that in the case of an infinite recursion, we can't catch it this way. - VERIFY(current_thread->is_allocation_enabled()); - PerformanceManager::add_kmalloc_perf_event(*current_thread, size, (FlatPtr)ptr); - } - - return ptr; -} - -void* kmalloc(size_t size) -{ - return kmalloc_impl(size, KMALLOC_DEFAULT_ALIGNMENT, CallerWillInitializeMemory::No); -} - -void* kcalloc(size_t count, size_t size) -{ - if (Checked::multiplication_would_overflow(count, size)) - return nullptr; - size_t new_size = count * size; - auto* ptr = kmalloc_impl(new_size, KMALLOC_DEFAULT_ALIGNMENT, CallerWillInitializeMemory::Yes); - if (ptr) - memset(ptr, 0, new_size); - return ptr; -} - -void kfree_sized(void* ptr, size_t size) -{ - if (!ptr) - return; - - VERIFY(size > 0); - - // Catch bad callers allocating under spinlock. - if constexpr (KMALLOC_VERIFY_NO_SPINLOCK_HELD) { - Processor::verify_no_spinlocks_held(); - } - - SpinlockLocker lock(s_lock); - ++g_kfree_call_count; - ++g_nested_kfree_calls; - - if (g_nested_kfree_calls == 1) { - Thread* current_thread = Thread::current(); - if (!current_thread) - current_thread = Processor::idle_thread(); - if (current_thread) { - VERIFY(current_thread->is_allocation_enabled()); - PerformanceManager::add_kfree_perf_event(*current_thread, 0, (FlatPtr)ptr); - } - } - - g_kmalloc_global->deallocate(ptr, size); - --g_nested_kfree_calls; -} - -size_t kmalloc_good_size(size_t size) -{ - VERIFY(size > 0); - // NOTE: There's no need to take the kmalloc lock, as the kmalloc slab-heaps (and their sizes) are constant - for (auto const& slabheap : g_kmalloc_global->slabheaps) { - if (size <= slabheap.slab_size()) - return slabheap.slab_size(); - } - return round_up_to_power_of_two(size + Heap::AllocationHeaderSize, CHUNK_SIZE) - Heap::AllocationHeaderSize; -} - -void* kmalloc_aligned(size_t size, size_t alignment) -{ - return kmalloc_impl(size, alignment, CallerWillInitializeMemory::No); -} - -void* operator new(size_t size) -{ - void* ptr = kmalloc(size); - VERIFY(ptr); - return ptr; -} - -void* operator new(size_t size, std::nothrow_t const&) noexcept -{ - return kmalloc(size); -} - -void* operator new(size_t size, std::align_val_t al) -{ - void* ptr = kmalloc_aligned(size, (size_t)al); - VERIFY(ptr); - return ptr; -} - -void* operator new(size_t size, std::align_val_t al, std::nothrow_t const&) noexcept -{ - return kmalloc_aligned(size, (size_t)al); -} - -void* operator new[](size_t size) -{ - void* ptr = kmalloc(size); - VERIFY(ptr); - return ptr; -} - -void* operator new[](size_t size, std::nothrow_t const&) noexcept -{ - return kmalloc(size); -} - -void operator delete(void*) noexcept -{ - // All deletes in kernel code should have a known size. - VERIFY_NOT_REACHED(); -} - -void operator delete(void* ptr, size_t size) noexcept -{ - return kfree_sized(ptr, size); -} - -void operator delete(void* ptr, size_t size, std::align_val_t) noexcept -{ - return kfree_sized(ptr, size); -} - -void operator delete[](void*) noexcept -{ - // All deletes in kernel code should have a known size. - VERIFY_NOT_REACHED(); -} - -void operator delete[](void* ptr, size_t size) noexcept -{ - return kfree_sized(ptr, size); -} - -void get_kmalloc_stats(kmalloc_stats& stats) -{ - SpinlockLocker lock(s_lock); - stats.bytes_allocated = g_kmalloc_global->allocated_bytes(); - stats.bytes_free = g_kmalloc_global->free_bytes(); - stats.kmalloc_call_count = g_kmalloc_call_count; - stats.kfree_call_count = g_kfree_call_count; -} diff --git a/Kernel/Heap/kmalloc.h b/Kernel/Heap/kmalloc.h deleted file mode 100644 index bc75b33e8b0..00000000000 --- a/Kernel/Heap/kmalloc.h +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include // For PAGE_SIZE. Everyone needs PAGE_SIZE - -#define KMALLOC_SCRUB_BYTE 0xbb -#define KFREE_SCRUB_BYTE 0xaa - -#define MAKE_ALIGNED_ALLOCATED(type, alignment) \ -public: \ - [[nodiscard]] void* operator new(size_t) \ - { \ - void* ptr = kmalloc_aligned(sizeof(type), alignment); \ - VERIFY(ptr); \ - return ptr; \ - } \ - [[nodiscard]] void* operator new(size_t, std::nothrow_t const&) noexcept \ - { \ - return kmalloc_aligned(sizeof(type), alignment); \ - } \ - void operator delete(void* ptr) noexcept \ - { \ - kfree_sized(ptr, sizeof(type)); \ - } \ - \ -private: - -// The C++ standard specifies that the nothrow allocation tag should live in the std namespace. -// Otherwise, `new (std::nothrow)` calls wouldn't get resolved. -namespace std { // NOLINT(cert-dcl58-cpp) These declarations must be in ::std and we are not using -struct nothrow_t { - explicit nothrow_t() = default; -}; - -extern nothrow_t const nothrow; - -enum class align_val_t : size_t {}; -}; - -void kmalloc_init(); - -void kfree_sized(void*, size_t); - -struct kmalloc_stats { - size_t bytes_allocated; - size_t bytes_free; - size_t kmalloc_call_count; - size_t kfree_call_count; -}; -void get_kmalloc_stats(kmalloc_stats&); - -extern bool g_dump_kmalloc_stacks; - -inline void* operator new(size_t, void* p) { return p; } -inline void* operator new[](size_t, void* p) { return p; } - -[[nodiscard]] void* operator new(size_t size); -[[nodiscard]] void* operator new(size_t size, std::nothrow_t const&) noexcept; -[[nodiscard]] void* operator new(size_t size, std::align_val_t); -[[nodiscard]] void* operator new(size_t size, std::align_val_t, std::nothrow_t const&) noexcept; - -void operator delete(void* ptr) noexcept DISALLOW("All deletes in the kernel should have a known size."); -void operator delete(void* ptr, size_t) noexcept; -void operator delete(void* ptr, std::align_val_t) noexcept DISALLOW("All deletes in the kernel should have a known size."); -void operator delete(void* ptr, size_t, std::align_val_t) noexcept; - -[[nodiscard]] void* operator new[](size_t size); -[[nodiscard]] void* operator new[](size_t size, std::nothrow_t const&) noexcept; - -void operator delete[](void* ptrs) noexcept DISALLOW("All deletes in the kernel should have a known size."); -void operator delete[](void* ptr, size_t) noexcept; - -[[gnu::malloc, gnu::alloc_size(1)]] void* kmalloc(size_t); -[[gnu::malloc, gnu::alloc_size(1, 2)]] void* kcalloc(size_t, size_t); - -[[gnu::malloc, gnu::alloc_size(1), gnu::alloc_align(2)]] void* kmalloc_aligned(size_t size, size_t alignment); - -size_t kmalloc_good_size(size_t); - -void kmalloc_enable_expand(); diff --git a/Kernel/Interrupts/GenericInterruptHandler.cpp b/Kernel/Interrupts/GenericInterruptHandler.cpp deleted file mode 100644 index 530f60154ef..00000000000 --- a/Kernel/Interrupts/GenericInterruptHandler.cpp +++ /dev/null @@ -1,80 +0,0 @@ -/* - * Copyright (c) 2020, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include - -namespace Kernel { -GenericInterruptHandler& GenericInterruptHandler::from(u8 interrupt_number) -{ - return get_interrupt_handler(interrupt_number); -} - -GenericInterruptHandler::GenericInterruptHandler(u8 interrupt_number, bool disable_remap) - : m_interrupt_number(interrupt_number) - , m_disable_remap(disable_remap) -{ - // NOTE: We cannot register or unregister the handler while the object - // is being constructed or deconstructed! -} - -void GenericInterruptHandler::will_be_destroyed() -{ - // This will be called for RefCounted interrupt handlers before the - // object is being destroyed. As soon as the destructor is invoked - // it is no longer advisable to unregister the handler (which causes - // calls to virtual functions), so let's do this right before - // invoking it - unregister_interrupt_handler(); -} - -void GenericInterruptHandler::register_interrupt_handler() -{ - if (m_registered) - return; - if (m_disable_remap) - register_generic_interrupt_handler(m_interrupt_number, *this); - else - register_generic_interrupt_handler(InterruptManagement::acquire_mapped_interrupt_number(m_interrupt_number), *this); - m_registered = true; -} - -void GenericInterruptHandler::unregister_interrupt_handler() -{ - if (!m_registered) - return; - if (m_disable_remap) - unregister_generic_interrupt_handler(m_interrupt_number, *this); - else - unregister_generic_interrupt_handler(InterruptManagement::acquire_mapped_interrupt_number(m_interrupt_number), *this); - m_registered = false; -} - -void GenericInterruptHandler::change_interrupt_number(u8 number) -{ - VERIFY_INTERRUPTS_DISABLED(); - VERIFY(!m_disable_remap); - if (m_registered) { - unregister_generic_interrupt_handler(InterruptManagement::acquire_mapped_interrupt_number(interrupt_number()), *this); - m_registered = false; - } - m_interrupt_number = number; - register_generic_interrupt_handler(InterruptManagement::acquire_mapped_interrupt_number(interrupt_number()), *this); -} - -ReadonlySpan GenericInterruptHandler::per_cpu_call_counts() const -{ - return m_per_cpu_call_counts.span().slice(0, Processor::count()); -} - -void GenericInterruptHandler::increment_call_count() -{ - ++m_per_cpu_call_counts[Processor::current_id()]; -} - -} diff --git a/Kernel/Interrupts/GenericInterruptHandler.h b/Kernel/Interrupts/GenericInterruptHandler.h deleted file mode 100644 index 40249ba7a5b..00000000000 --- a/Kernel/Interrupts/GenericInterruptHandler.h +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Copyright (c) 2020, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include - -namespace Kernel { - -enum class HandlerType : u8 { - IRQHandler = 1, - SharedIRQHandler = 2, - UnhandledInterruptHandler = 3, - SpuriousInterruptHandler = 4 -}; - -class GenericInterruptHandler { -public: - static GenericInterruptHandler& from(u8 interrupt_number); - virtual ~GenericInterruptHandler() - { - VERIFY(!m_registered); - } - // Note: this method returns boolean value, to indicate if the handler handled - // the interrupt or not. This is useful for shared handlers mostly. - virtual bool handle_interrupt(RegisterState const& regs) = 0; - - void will_be_destroyed(); - bool is_registered() const { return m_registered; } - void register_interrupt_handler(); - void unregister_interrupt_handler(); - - u8 interrupt_number() const { return m_interrupt_number; } - - ReadonlySpan per_cpu_call_counts() const; - - virtual size_t sharing_devices_count() const = 0; - virtual bool is_shared_handler() const = 0; - - virtual HandlerType type() const = 0; - virtual StringView purpose() const = 0; - virtual StringView controller() const = 0; - - virtual bool eoi() = 0; - void increment_call_count(); - void set_reserved() { m_reserved = true; } - bool reserved() const { return m_reserved; } - -protected: - void change_interrupt_number(u8 number); - GenericInterruptHandler(u8 interrupt_number, bool disable_remap = false); - - void disable_remap() { m_disable_remap = true; } - -private: - Array m_per_cpu_call_counts {}; - - u8 m_interrupt_number { 0 }; - bool m_disable_remap { false }; - bool m_registered { false }; - bool m_reserved { false }; - - IntrusiveListNode m_list_node; - -public: - using List = IntrusiveList<&GenericInterruptHandler::m_list_node>; -}; -} diff --git a/Kernel/Interrupts/IRQHandler.cpp b/Kernel/Interrupts/IRQHandler.cpp deleted file mode 100644 index 8ea82011a83..00000000000 --- a/Kernel/Interrupts/IRQHandler.cpp +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include - -namespace Kernel { - -IRQHandler::IRQHandler(u8 irq) - : GenericInterruptHandler(irq) - , m_responsible_irq_controller(InterruptManagement::the().get_responsible_irq_controller(irq)) -{ - if (is_registered()) - disable_irq(); -} - -IRQHandler::~IRQHandler() = default; - -bool IRQHandler::eoi() -{ - dbgln_if(IRQ_DEBUG, "EOI IRQ {}", interrupt_number()); - if (!m_shared_with_others) { - m_responsible_irq_controller->eoi(*this); - return true; - } - return false; -} - -void IRQHandler::enable_irq() -{ - dbgln_if(IRQ_DEBUG, "Enable IRQ {}", interrupt_number()); - if (!is_registered()) - register_interrupt_handler(); - m_enabled = true; - if (!m_shared_with_others) - m_responsible_irq_controller->enable(*this); -} - -void IRQHandler::disable_irq() -{ - dbgln_if(IRQ_DEBUG, "Disable IRQ {}", interrupt_number()); - m_enabled = false; - if (!m_shared_with_others) - m_responsible_irq_controller->disable(*this); -} - -void IRQHandler::change_irq_number(u8 irq) -{ - InterruptDisabler disabler; - change_interrupt_number(irq); - m_responsible_irq_controller = InterruptManagement::the().get_responsible_irq_controller(irq); -} - -} diff --git a/Kernel/Interrupts/IRQHandler.h b/Kernel/Interrupts/IRQHandler.h deleted file mode 100644 index 9e800c18e9e..00000000000 --- a/Kernel/Interrupts/IRQHandler.h +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include - -namespace Kernel { - -class IRQHandler : public GenericInterruptHandler { -public: - virtual ~IRQHandler(); - - virtual bool handle_interrupt(RegisterState const& regs) override { return handle_irq(regs); } - virtual bool handle_irq(RegisterState const&) = 0; - - void enable_irq(); - void disable_irq(); - - virtual bool eoi() override; - - virtual HandlerType type() const override { return HandlerType::IRQHandler; } - virtual StringView purpose() const override { return "IRQ Handler"sv; } - virtual StringView controller() const override { return m_responsible_irq_controller->model(); } - - virtual size_t sharing_devices_count() const override { return 0; } - virtual bool is_shared_handler() const override { return false; } - void set_shared_with_others(bool status) { m_shared_with_others = status; } - -protected: - void change_irq_number(u8 irq); - explicit IRQHandler(u8 irq); - -private: - bool m_shared_with_others { false }; - bool m_enabled { false }; - NonnullLockRefPtr m_responsible_irq_controller; -}; - -} diff --git a/Kernel/Interrupts/InterruptDisabler.h b/Kernel/Interrupts/InterruptDisabler.h deleted file mode 100644 index d4e49bb9b4b..00000000000 --- a/Kernel/Interrupts/InterruptDisabler.h +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright (c) 2022, Timon Kruiper - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include - -namespace Kernel { - -class InterruptDisabler { -public: - InterruptDisabler() - : m_interrupts_were_enabled(Processor::are_interrupts_enabled()) - { - Processor::disable_interrupts(); - } - - ~InterruptDisabler() - { - if (m_interrupts_were_enabled) - Processor::enable_interrupts(); - } - -private: - bool m_interrupts_were_enabled; -}; - -} diff --git a/Kernel/Interrupts/PCIIRQHandler.cpp b/Kernel/Interrupts/PCIIRQHandler.cpp deleted file mode 100644 index 8b3b7969694..00000000000 --- a/Kernel/Interrupts/PCIIRQHandler.cpp +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Copyright (c) 2023, Pankaj R - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include - -namespace Kernel::PCI { - -IRQHandler::IRQHandler(PCI::Device& device, u8 irq) - : GenericInterruptHandler(irq) - , device(device) -{ - auto type = device.get_interrupt_type(); - - if (type == PCI::InterruptType::PIN) - m_responsible_irq_controller = InterruptManagement::the().get_responsible_irq_controller(irq); - - if (is_registered()) - disable_irq(); -} - -bool IRQHandler::eoi() -{ - dbgln_if(IRQ_DEBUG, "EOI IRQ {}", interrupt_number()); - if (m_shared_with_others) - return false; - if (!m_responsible_irq_controller.is_null()) - m_responsible_irq_controller->eoi(*this); - else - msi_signal_eoi(); - return true; -} - -void IRQHandler::enable_irq() -{ - dbgln_if(IRQ_DEBUG, "Enable IRQ {}", interrupt_number()); - if (!is_registered()) - register_interrupt_handler(); - m_enabled = true; - if (m_shared_with_others) - return; - if (!m_responsible_irq_controller.is_null()) - m_responsible_irq_controller->enable(*this); - else - device.enable_interrupt(interrupt_number()); -} - -void IRQHandler::disable_irq() -{ - dbgln_if(IRQ_DEBUG, "Disable IRQ {}", interrupt_number()); - m_enabled = false; - - if (m_shared_with_others) - return; - if (!m_responsible_irq_controller.is_null()) - m_responsible_irq_controller->disable(*this); - else - device.disable_interrupt(interrupt_number()); -} - -bool IRQHandler::handle_interrupt(RegisterState const& regs) -{ - return handle_irq(regs); -} - -} diff --git a/Kernel/Interrupts/PCIIRQHandler.h b/Kernel/Interrupts/PCIIRQHandler.h deleted file mode 100644 index 96dcd6d4f00..00000000000 --- a/Kernel/Interrupts/PCIIRQHandler.h +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright (c) 2023, Pankaj R - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include - -namespace Kernel::PCI { - -class IRQHandler : public GenericInterruptHandler { -public: - virtual ~IRQHandler() = default; - - virtual bool handle_interrupt(RegisterState const& regs) override; - virtual bool handle_irq(RegisterState const&) = 0; - - void enable_irq(); - void disable_irq(); - - virtual bool eoi() override; - - virtual HandlerType type() const override { return HandlerType::IRQHandler; } - virtual StringView purpose() const override { return "IRQ Handler"sv; } - virtual StringView controller() const override { return m_responsible_irq_controller.is_null() ? "PCI-MSI"sv : m_responsible_irq_controller->model(); } - - virtual size_t sharing_devices_count() const override { return 0; } - virtual bool is_shared_handler() const override { return false; } - void set_shared_with_others(bool status) { m_shared_with_others = status; } - -protected: - IRQHandler(PCI::Device& device, u8 irq); - -private: - bool m_shared_with_others { false }; - bool m_enabled { false }; - LockRefPtr m_responsible_irq_controller { nullptr }; - PCI::Device& device; -}; - -} diff --git a/Kernel/Interrupts/SharedIRQHandler.cpp b/Kernel/Interrupts/SharedIRQHandler.cpp deleted file mode 100644 index 7cb41dbe0cd..00000000000 --- a/Kernel/Interrupts/SharedIRQHandler.cpp +++ /dev/null @@ -1,103 +0,0 @@ -/* - * Copyright (c) 2020, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include -#include - -namespace Kernel { - -UNMAP_AFTER_INIT void SharedIRQHandler::initialize(u8 interrupt_number) -{ - auto* handler = new SharedIRQHandler(interrupt_number); - handler->register_interrupt_handler(); - handler->disable_interrupt_vector(); -} - -void SharedIRQHandler::register_handler(GenericInterruptHandler& handler) -{ - dbgln_if(INTERRUPT_DEBUG, "Interrupt Handler registered @ Shared Interrupt Handler {}", interrupt_number()); - m_handlers.with([&handler](auto& list) { list.append(handler); }); - enable_interrupt_vector(); -} -void SharedIRQHandler::unregister_handler(GenericInterruptHandler& handler) -{ - dbgln_if(INTERRUPT_DEBUG, "Interrupt Handler unregistered @ Shared Interrupt Handler {}", interrupt_number()); - m_handlers.with([&handler, this](auto& list) { - list.remove(handler); - if (list.is_empty()) - disable_interrupt_vector(); - }); -} - -bool SharedIRQHandler::eoi() -{ - dbgln_if(INTERRUPT_DEBUG, "EOI IRQ {}", interrupt_number()); - m_responsible_irq_controller->eoi(*this); - return true; -} - -void SharedIRQHandler::enumerate_handlers(Function& callback) -{ - m_handlers.for_each([&](auto& handler) { callback(handler); }); -} - -SharedIRQHandler::SharedIRQHandler(u8 irq) - : GenericInterruptHandler(irq) - , m_responsible_irq_controller(InterruptManagement::the().get_responsible_irq_controller(irq)) -{ - dbgln_if(INTERRUPT_DEBUG, "Shared Interrupt Handler registered @ {}", interrupt_number()); -} - -SharedIRQHandler::~SharedIRQHandler() -{ - dbgln_if(INTERRUPT_DEBUG, "Shared Interrupt Handler unregistered @ {}", interrupt_number()); - disable_interrupt_vector(); -} - -bool SharedIRQHandler::handle_interrupt(RegisterState const& regs) -{ - VERIFY_INTERRUPTS_DISABLED(); - - if constexpr (INTERRUPT_DEBUG) { - dbgln("Interrupt @ {}", interrupt_number()); - dbgln("Interrupt Handlers registered - {}", sharing_devices_count()); - } - int i = 0; - bool was_handled = false; - m_handlers.for_each([&](auto& handler) { - dbgln_if(INTERRUPT_DEBUG, "Going for Interrupt Handling @ {}, Shared Interrupt {}", i, interrupt_number()); - if (handler.handle_interrupt(regs)) { - handler.increment_call_count(); - was_handled = true; - } - dbgln_if(INTERRUPT_DEBUG, "Going for Interrupt Handling @ {}, Shared Interrupt {} - End", i, interrupt_number()); - i++; - }); - - return was_handled; -} - -void SharedIRQHandler::enable_interrupt_vector() -{ - if (m_enabled) - return; - m_enabled = true; - m_responsible_irq_controller->enable(*this); -} - -void SharedIRQHandler::disable_interrupt_vector() -{ - if (!m_enabled) - return; - m_enabled = false; - m_responsible_irq_controller->disable(*this); -} - -} diff --git a/Kernel/Interrupts/SharedIRQHandler.h b/Kernel/Interrupts/SharedIRQHandler.h deleted file mode 100644 index 79b642c94e8..00000000000 --- a/Kernel/Interrupts/SharedIRQHandler.h +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright (c) 2020, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include -#include - -namespace Kernel { -class IRQHandler; -class SharedIRQHandler final : public GenericInterruptHandler { -public: - static void initialize(u8 interrupt_number); - virtual ~SharedIRQHandler(); - virtual bool handle_interrupt(RegisterState const& regs) override; - - void register_handler(GenericInterruptHandler&); - void unregister_handler(GenericInterruptHandler&); - - virtual bool eoi() override; - - void enumerate_handlers(Function&); - - virtual size_t sharing_devices_count() const override - { - return m_handlers.with([](auto& list) { return list.size_slow(); }); - } - virtual bool is_shared_handler() const override { return true; } - - virtual HandlerType type() const override { return HandlerType::SharedIRQHandler; } - virtual StringView purpose() const override { return "Shared IRQ Handler"sv; } - virtual StringView controller() const override { return m_responsible_irq_controller->model(); } - -private: - void enable_interrupt_vector(); - void disable_interrupt_vector(); - explicit SharedIRQHandler(u8 interrupt_number); - bool m_enabled { true }; - SpinlockProtected m_handlers; - NonnullLockRefPtr m_responsible_irq_controller; -}; -} diff --git a/Kernel/Interrupts/SpuriousInterruptHandler.cpp b/Kernel/Interrupts/SpuriousInterruptHandler.cpp deleted file mode 100644 index 893230a0da8..00000000000 --- a/Kernel/Interrupts/SpuriousInterruptHandler.cpp +++ /dev/null @@ -1,115 +0,0 @@ -/* - * Copyright (c) 2020, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include - -namespace Kernel { - -UNMAP_AFTER_INIT void SpuriousInterruptHandler::initialize(u8 interrupt_number) -{ - auto* handler = new SpuriousInterruptHandler(interrupt_number); - handler->register_interrupt_handler(); -} - -void SpuriousInterruptHandler::initialize_for_disabled_master_pic() -{ - auto* handler = new SpuriousInterruptHandler(7); - register_disabled_interrupt_handler(7, *handler); - handler->enable_interrupt_vector_for_disabled_pic(); -} - -void SpuriousInterruptHandler::initialize_for_disabled_slave_pic() -{ - auto* handler = new SpuriousInterruptHandler(15); - register_disabled_interrupt_handler(15, *handler); - handler->enable_interrupt_vector_for_disabled_pic(); -} - -void SpuriousInterruptHandler::register_handler(GenericInterruptHandler& handler) -{ - VERIFY(!m_real_handler); - m_real_handler = adopt_own_if_nonnull(&handler); -} -void SpuriousInterruptHandler::unregister_handler(GenericInterruptHandler&) -{ - TODO(); -} - -bool SpuriousInterruptHandler::eoi() -{ - // Actually check if IRQ7 or IRQ15 are spurious, and if not, call EOI with the correct interrupt number. - if (m_real_irq) { - m_responsible_irq_controller->eoi(*this); - m_real_irq = false; // return to default state! - return true; - } - m_responsible_irq_controller->spurious_eoi(*this); - return false; -} - -StringView SpuriousInterruptHandler::purpose() const -{ - if (!m_real_handler) - return "Spurious Interrupt Handler"sv; - return m_real_handler->purpose(); -} - -SpuriousInterruptHandler::SpuriousInterruptHandler(u8 irq) - : GenericInterruptHandler(irq) - , m_responsible_irq_controller(InterruptManagement::the().get_responsible_irq_controller(irq)) -{ -} - -SpuriousInterruptHandler::~SpuriousInterruptHandler() = default; - -bool SpuriousInterruptHandler::handle_interrupt(RegisterState const& state) -{ - // Actually check if IRQ7 or IRQ15 are spurious, and if not, call the real handler to handle the IRQ. - if (m_responsible_irq_controller->get_isr() & (1 << interrupt_number())) { - m_real_irq = true; // remember that we had a real IRQ, when EOI later! - if (m_real_handler->handle_interrupt(state)) { - m_real_handler->increment_call_count(); - return true; - } - return false; - } - dbgln("Spurious interrupt, vector {}", interrupt_number()); - return true; -} - -void SpuriousInterruptHandler::enable_interrupt_vector_for_disabled_pic() -{ - m_enabled = true; - m_responsible_irq_controller = InterruptManagement::the().get_responsible_irq_controller(IRQControllerType::i8259, interrupt_number()); -} - -void SpuriousInterruptHandler::enable_interrupt_vector() -{ - if (m_enabled) - return; - m_enabled = true; - m_responsible_irq_controller->enable(*this); -} - -void SpuriousInterruptHandler::disable_interrupt_vector() -{ - VERIFY(!m_real_irq); // this flag should not be set when we call this method - if (!m_enabled) - return; - m_enabled = false; - m_responsible_irq_controller->disable(*this); -} - -StringView SpuriousInterruptHandler::controller() const -{ - if (m_responsible_irq_controller->type() == IRQControllerType::i82093AA) - return ""sv; - return m_responsible_irq_controller->model(); -} -} diff --git a/Kernel/Interrupts/SpuriousInterruptHandler.h b/Kernel/Interrupts/SpuriousInterruptHandler.h deleted file mode 100644 index 00352bb3bf7..00000000000 --- a/Kernel/Interrupts/SpuriousInterruptHandler.h +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright (c) 2020, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include - -namespace Kernel { - -class SpuriousInterruptHandler final : public GenericInterruptHandler { -public: - static void initialize(u8 interrupt_number); - static void initialize_for_disabled_master_pic(); - static void initialize_for_disabled_slave_pic(); - virtual ~SpuriousInterruptHandler(); - virtual bool handle_interrupt(RegisterState const& regs) override; - - void register_handler(GenericInterruptHandler&); - void unregister_handler(GenericInterruptHandler&); - - virtual bool eoi() override; - - virtual size_t sharing_devices_count() const override { return 1; } - virtual bool is_shared_handler() const override { return false; } - - virtual HandlerType type() const override { return HandlerType::SpuriousInterruptHandler; } - virtual StringView purpose() const override; - virtual StringView controller() const override; - - void enable_interrupt_vector_for_disabled_pic(); - -private: - void enable_interrupt_vector(); - void disable_interrupt_vector(); - explicit SpuriousInterruptHandler(u8 interrupt_number); - bool m_enabled { false }; - bool m_real_irq { false }; - NonnullLockRefPtr m_responsible_irq_controller; - OwnPtr m_real_handler; -}; -} diff --git a/Kernel/Interrupts/UnhandledInterruptHandler.cpp b/Kernel/Interrupts/UnhandledInterruptHandler.cpp deleted file mode 100644 index 8ef0a7b3d3e..00000000000 --- a/Kernel/Interrupts/UnhandledInterruptHandler.cpp +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright (c) 2020, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include - -namespace Kernel { -UnhandledInterruptHandler::UnhandledInterruptHandler(u8 interrupt_vector) - : GenericInterruptHandler(interrupt_vector) -{ -} - -bool UnhandledInterruptHandler::handle_interrupt(RegisterState const&) -{ - PANIC("Interrupt: Unhandled vector {} was invoked for handle_interrupt(RegisterState&).", interrupt_number()); -} - -[[noreturn]] bool UnhandledInterruptHandler::eoi() -{ - PANIC("Interrupt: Unhandled vector {} was invoked for eoi().", interrupt_number()); -} - -UnhandledInterruptHandler::~UnhandledInterruptHandler() = default; -} diff --git a/Kernel/Interrupts/UnhandledInterruptHandler.h b/Kernel/Interrupts/UnhandledInterruptHandler.h deleted file mode 100644 index ae414372073..00000000000 --- a/Kernel/Interrupts/UnhandledInterruptHandler.h +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright (c) 2020, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include - -namespace Kernel { -class UnhandledInterruptHandler final : public GenericInterruptHandler { -public: - explicit UnhandledInterruptHandler(u8 interrupt_vector); - virtual ~UnhandledInterruptHandler(); - - virtual bool handle_interrupt(RegisterState const&) override; - - [[noreturn]] virtual bool eoi() override; - - virtual HandlerType type() const override { return HandlerType::UnhandledInterruptHandler; } - virtual StringView purpose() const override { return "Unhandled Interrupt Handler"sv; } - virtual StringView controller() const override { VERIFY_NOT_REACHED(); } - - virtual size_t sharing_devices_count() const override { return 0; } - virtual bool is_shared_handler() const override { return false; } - -private: -}; -} diff --git a/Kernel/KSyms.cpp b/Kernel/KSyms.cpp deleted file mode 100644 index 461d11ead59..00000000000 --- a/Kernel/KSyms.cpp +++ /dev/null @@ -1,209 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace Kernel { - -FlatPtr g_lowest_kernel_symbol_address = 0xffffffff; -FlatPtr g_highest_kernel_symbol_address = 0; -SetOnce g_kernel_symbols_available; - -extern "C" { -__attribute__((section(".kernel_symbols"))) char kernel_symbols[5 * MiB] {}; -} - -static KernelSymbol* s_symbols; -static size_t s_symbol_count = 0; - -UNMAP_AFTER_INIT static u8 parse_hex_digit(char nibble) -{ - if (nibble >= '0' && nibble <= '9') - return nibble - '0'; - VERIFY(nibble >= 'a' && nibble <= 'f'); - return 10 + (nibble - 'a'); -} - -FlatPtr address_for_kernel_symbol(StringView name) -{ - for (size_t i = 0; i < s_symbol_count; ++i) { - auto const& symbol = s_symbols[i]; - if (name == symbol.name) - return symbol.address; - } - return 0; -} - -KernelSymbol const* symbolicate_kernel_address(FlatPtr address) -{ - if (address < g_lowest_kernel_symbol_address || address > g_highest_kernel_symbol_address) - return nullptr; - for (unsigned i = 0; i < s_symbol_count; ++i) { - if (address < s_symbols[i + 1].address) - return &s_symbols[i]; - } - return nullptr; -} - -UNMAP_AFTER_INIT static void load_kernel_symbols_from_data(Bytes buffer) -{ - g_lowest_kernel_symbol_address = 0xffffffff; - g_highest_kernel_symbol_address = 0; - - auto* bufptr = (char*)buffer.data(); - auto* start_of_name = bufptr; - FlatPtr address = 0; - - for (size_t i = 0; i < 8; ++i) - s_symbol_count = (s_symbol_count << 4) | parse_hex_digit(*(bufptr++)); - s_symbols = static_cast(kmalloc(sizeof(KernelSymbol) * s_symbol_count)); - ++bufptr; // skip newline - - dmesgln("Loading kernel symbol table..."); - - size_t current_symbol_index = 0; - - while ((u8 const*)bufptr < buffer.data() + buffer.size()) { - for (size_t i = 0; i < sizeof(void*) * 2; ++i) - address = (address << 4) | parse_hex_digit(*(bufptr++)); - bufptr += 3; - start_of_name = bufptr; - while (*(++bufptr)) { - if (*bufptr == '\n') { - break; - } - } - auto& ksym = s_symbols[current_symbol_index]; - - // FIXME: Remove this ifdef once the aarch64 kernel is loaded by the Prekernel. - // Currently, the aarch64 kernel is linked at a high virtual memory address, instead - // of zero, so the address of a symbol does not need to be offset by the kernel_load_base. -#if ARCH(X86_64) - ksym.address = kernel_load_base + address; -#elif ARCH(AARCH64) || ARCH(RISCV64) - ksym.address = address; -#else -# error "Unknown architecture" -#endif - ksym.name = start_of_name; - - *bufptr = '\0'; - - if (ksym.address < g_lowest_kernel_symbol_address) - g_lowest_kernel_symbol_address = ksym.address; - if (ksym.address > g_highest_kernel_symbol_address) - g_highest_kernel_symbol_address = ksym.address; - - ++bufptr; - ++current_symbol_index; - } - g_kernel_symbols_available.set(); -} - -NEVER_INLINE static void dump_backtrace_impl(FlatPtr frame_pointer, bool use_ksyms, PrintToScreen print_to_screen) -{ -#define PRINT_LINE(fmtstr, ...) \ - do { \ - if (print_to_screen == PrintToScreen::No) \ - dbgln(fmtstr, __VA_ARGS__); \ - else \ - critical_dmesgln(fmtstr, __VA_ARGS__); \ - } while (0) - - SmapDisabler disabler; - if (use_ksyms && !g_kernel_symbols_available.was_set()) - Processor::halt(); - - struct RecognizedSymbol { - FlatPtr address; - KernelSymbol const* symbol { nullptr }; - }; - - constexpr size_t max_recognized_symbol_count = 256; - RecognizedSymbol recognized_symbols[max_recognized_symbol_count]; - size_t recognized_symbol_count = 0; - - MUST(AK::unwind_stack_from_frame_pointer( - frame_pointer, - [](FlatPtr address) -> ErrorOr { - if (address < kernel_mapping_base) - return EINVAL; - - FlatPtr value; - void* fault_at; - if (!safe_memcpy(&value, bit_cast(address), sizeof(FlatPtr), fault_at)) - return EFAULT; - - return value; - }, - [use_ksyms, print_to_screen, &recognized_symbol_count, &recognized_symbols](AK::StackFrame stack_frame) -> ErrorOr { - if (use_ksyms) { - if (recognized_symbol_count >= max_recognized_symbol_count) - return IterationDecision::Break; - - recognized_symbols[recognized_symbol_count++] = { stack_frame.return_address, symbolicate_kernel_address(stack_frame.return_address) }; - } else { - PRINT_LINE("{:p}", stack_frame.return_address); - } - return IterationDecision::Continue; - })); - - if (!use_ksyms) - return; - - VERIFY(recognized_symbol_count <= max_recognized_symbol_count); - for (size_t i = 0; i < recognized_symbol_count; ++i) { - auto& symbol = recognized_symbols[i]; - if (!symbol.address) - break; - if (!symbol.symbol) { - PRINT_LINE("Kernel + {:p}", symbol.address - kernel_load_base); - continue; - } - size_t offset = symbol.address - symbol.symbol->address; - if (symbol.symbol->address == g_highest_kernel_symbol_address && offset > 4096) - PRINT_LINE("Kernel + {:p}", symbol.address - kernel_load_base); - else - PRINT_LINE("Kernel + {:p} {} +{:#x}", symbol.address - kernel_load_base, symbol.symbol->name, offset); - } -} - -void dump_backtrace_from_base_pointer(FlatPtr base_pointer) -{ - // FIXME: Change signature of dump_backtrace_impl to use an enum instead of a bool. - dump_backtrace_impl(base_pointer, /*use_ksym=*/false, PrintToScreen::No); -} - -void dump_backtrace(PrintToScreen print_to_screen) -{ - static bool in_dump_backtrace = false; - if (in_dump_backtrace) - return; - TemporaryChange change(in_dump_backtrace, true); - TemporaryChange disable_kmalloc_stacks(g_dump_kmalloc_stacks, false); - - FlatPtr base_pointer = (FlatPtr)__builtin_frame_address(0); - dump_backtrace_impl(base_pointer, g_kernel_symbols_available.was_set(), print_to_screen); -} - -UNMAP_AFTER_INIT void load_kernel_symbol_table() -{ - auto kernel_symbols_size = strnlen(kernel_symbols, sizeof(kernel_symbols)); - // If we're hitting this VERIFY the kernel symbol file has grown beyond - // the array size of kernel_symbols. Try making the array larger. - VERIFY(kernel_symbols_size != sizeof(kernel_symbols)); - load_kernel_symbols_from_data({ kernel_symbols, kernel_symbols_size }); -} - -} diff --git a/Kernel/KSyms.h b/Kernel/KSyms.h deleted file mode 100644 index da5f8614f0a..00000000000 --- a/Kernel/KSyms.h +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include - -namespace Kernel { - -struct KernelSymbol { - FlatPtr address; - char const* name; -}; - -enum class PrintToScreen { - No, - Yes, -}; - -FlatPtr address_for_kernel_symbol(StringView name); -KernelSymbol const* symbolicate_kernel_address(FlatPtr); -void load_kernel_symbol_table(); - -extern SetOnce g_kernel_symbols_available; -extern FlatPtr g_lowest_kernel_symbol_address; -extern FlatPtr g_highest_kernel_symbol_address; - -void dump_backtrace(PrintToScreen print_to_screen = PrintToScreen::No); -void dump_backtrace_from_base_pointer(FlatPtr base_pointer); - -} diff --git a/Kernel/Library/Assertions.h b/Kernel/Library/Assertions.h deleted file mode 100644 index a989ba3d2e8..00000000000 --- a/Kernel/Library/Assertions.h +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include - -#define __STRINGIFY_HELPER(x) #x -#define __STRINGIFY(x) __STRINGIFY_HELPER(x) - -[[noreturn]] void __assertion_failed(char const* msg, char const* file, unsigned line, char const* func); -#define VERIFY(expr) \ - do { \ - if (!static_cast(expr)) [[unlikely]] \ - __assertion_failed(#expr, __FILE__, __LINE__, __PRETTY_FUNCTION__); \ - } while (0) - -#define VERIFY_NOT_REACHED() __assertion_failed("not reached", __FILE__, __LINE__, __PRETTY_FUNCTION__) - -extern "C" { -[[noreturn]] void _abort(); -[[noreturn]] void abort(); -} - -#define TODO() __assertion_failed("TODO", __FILE__, __LINE__, __PRETTY_FUNCTION__) -#define TODO_AARCH64() __assertion_failed("TODO_AARCH64", __FILE__, __LINE__, __PRETTY_FUNCTION__) -#define TODO_RISCV64() __assertion_failed("TODO_RISCV64", __FILE__, __LINE__, __PRETTY_FUNCTION__) - -#define VERIFY_INTERRUPTS_DISABLED() VERIFY(!(Processor::are_interrupts_enabled())) -#define VERIFY_INTERRUPTS_ENABLED() VERIFY(Processor::are_interrupts_enabled()) diff --git a/Kernel/Library/DoubleBuffer.cpp b/Kernel/Library/DoubleBuffer.cpp deleted file mode 100644 index f471c1f9c91..00000000000 --- a/Kernel/Library/DoubleBuffer.cpp +++ /dev/null @@ -1,93 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include - -namespace Kernel { - -inline void DoubleBuffer::compute_lockfree_metadata() -{ - InterruptDisabler disabler; - m_empty = m_read_buffer_index >= m_read_buffer->size && m_write_buffer->size == 0; - m_space_for_writing = m_capacity - m_write_buffer->size; -} - -ErrorOr> DoubleBuffer::try_create(StringView name, size_t capacity) -{ - auto storage = TRY(KBuffer::try_create_with_size(name, capacity * 2, Memory::Region::Access::ReadWrite)); - return adopt_nonnull_own_or_enomem(new (nothrow) DoubleBuffer(capacity, move(storage))); -} - -DoubleBuffer::DoubleBuffer(size_t capacity, NonnullOwnPtr storage) - : m_write_buffer(&m_buffer1) - , m_read_buffer(&m_buffer2) - , m_storage(move(storage)) - , m_capacity(capacity) -{ - m_buffer1.data = m_storage->data(); - m_buffer1.size = 0; - m_buffer2.data = m_storage->data() + capacity; - m_buffer2.size = 0; - m_space_for_writing = capacity; -} - -void DoubleBuffer::flip() -{ - VERIFY(m_read_buffer_index == m_read_buffer->size); - swap(m_read_buffer, m_write_buffer); - m_write_buffer->size = 0; - m_read_buffer_index = 0; - compute_lockfree_metadata(); -} - -ErrorOr DoubleBuffer::write(UserOrKernelBuffer const& data, size_t size) -{ - if (!size) - return 0; - MutexLocker locker(m_lock); - size_t bytes_to_write = min(size, m_space_for_writing); - u8* write_ptr = m_write_buffer->data + m_write_buffer->size; - TRY(data.read(write_ptr, bytes_to_write)); - m_write_buffer->size += bytes_to_write; - compute_lockfree_metadata(); - if (m_unblock_callback && !m_empty) - m_unblock_callback(); - return bytes_to_write; -} - -ErrorOr DoubleBuffer::read_impl(UserOrKernelBuffer& data, size_t size, MutexLocker&, bool advance_buffer_index) -{ - if (size == 0) - return 0; - if (m_read_buffer_index >= m_read_buffer->size && m_write_buffer->size != 0) - flip(); - if (m_read_buffer_index >= m_read_buffer->size) - return 0; - size_t nread = min(m_read_buffer->size - m_read_buffer_index, size); - TRY(data.write(m_read_buffer->data + m_read_buffer_index, nread)); - if (advance_buffer_index) - m_read_buffer_index += nread; - compute_lockfree_metadata(); - if (m_unblock_callback && m_space_for_writing > 0) - m_unblock_callback(); - return nread; -} - -ErrorOr DoubleBuffer::read(UserOrKernelBuffer& data, size_t size) -{ - MutexLocker locker(m_lock); - return read_impl(data, size, locker, true); -} - -ErrorOr DoubleBuffer::peek(UserOrKernelBuffer& data, size_t size) -{ - MutexLocker locker(m_lock); - return read_impl(data, size, locker, false); -} - -} diff --git a/Kernel/Library/DoubleBuffer.h b/Kernel/Library/DoubleBuffer.h deleted file mode 100644 index 56e71deeabb..00000000000 --- a/Kernel/Library/DoubleBuffer.h +++ /dev/null @@ -1,78 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include - -namespace Kernel { - -class DoubleBuffer { -public: - static ErrorOr> try_create(StringView name, size_t capacity = 65536); - ErrorOr write(UserOrKernelBuffer const&, size_t); - ErrorOr write(u8 const* data, size_t size) - { - return write(UserOrKernelBuffer::for_kernel_buffer(const_cast(data)), size); - } - ErrorOr read(UserOrKernelBuffer&, size_t); - ErrorOr read(u8* data, size_t size) - { - auto buffer = UserOrKernelBuffer::for_kernel_buffer(data); - return read(buffer, size); - } - ErrorOr peek(UserOrKernelBuffer&, size_t); - ErrorOr peek(u8* data, size_t size) - { - auto buffer = UserOrKernelBuffer::for_kernel_buffer(data); - return peek(buffer, size); - } - - bool is_empty() const { return m_empty; } - - size_t space_for_writing() const { return m_space_for_writing; } - size_t immediately_readable() const - { - return (m_read_buffer->size - m_read_buffer_index) + m_write_buffer->size; - } - - void set_unblock_callback(Function callback) - { - VERIFY(!m_unblock_callback); - m_unblock_callback = move(callback); - } - -private: - explicit DoubleBuffer(size_t capacity, NonnullOwnPtr storage); - void flip(); - void compute_lockfree_metadata(); - - ErrorOr read_impl(UserOrKernelBuffer&, size_t, MutexLocker&, bool advance_buffer_index); - - struct InnerBuffer { - u8* data { nullptr }; - size_t size { 0 }; - }; - - InnerBuffer* m_write_buffer { nullptr }; - InnerBuffer* m_read_buffer { nullptr }; - InnerBuffer m_buffer1; - InnerBuffer m_buffer2; - - NonnullOwnPtr m_storage; - Function m_unblock_callback; - size_t m_capacity { 0 }; - size_t m_read_buffer_index { 0 }; - size_t m_space_for_writing { 0 }; - bool m_empty { true }; - mutable Mutex m_lock { "DoubleBuffer"sv }; -}; - -} diff --git a/Kernel/Library/IOWindow.cpp b/Kernel/Library/IOWindow.cpp deleted file mode 100644 index de6d5beddf8..00000000000 --- a/Kernel/Library/IOWindow.cpp +++ /dev/null @@ -1,254 +0,0 @@ -/* - * Copyright (c) 2022, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include - -namespace Kernel { - -#if ARCH(X86_64) -ErrorOr> IOWindow::create_for_io_space(IOAddress address, u64 space_length) -{ - VERIFY(!Checked::addition_would_overflow(address.get(), space_length)); - auto io_address_range = TRY(adopt_nonnull_own_or_enomem(new (nothrow) IOAddressData(address.get(), space_length))); - return TRY(adopt_nonnull_own_or_enomem(new (nothrow) IOWindow(move(io_address_range)))); -} - -IOWindow::IOWindow(NonnullOwnPtr io_range) - : m_space_type(SpaceType::IO) - , m_io_range(move(io_range)) -{ -} -#endif - -ErrorOr> IOWindow::create_from_io_window_with_offset(u64 offset, u64 space_length) -{ -#if ARCH(X86_64) - if (m_space_type == SpaceType::IO) { - VERIFY(m_io_range); - if (Checked::addition_would_overflow(m_io_range->address(), space_length)) - return Error::from_errno(EOVERFLOW); - auto io_address_range = TRY(adopt_nonnull_own_or_enomem(new (nothrow) IOAddressData(as_io_address().offset(offset).get(), space_length))); - return TRY(adopt_nonnull_own_or_enomem(new (nothrow) IOWindow(move(io_address_range)))); - } -#endif - VERIFY(space_type() == SpaceType::Memory); - VERIFY(m_memory_mapped_range); - - if (Checked::addition_would_overflow(m_memory_mapped_range->paddr.get(), offset)) - return Error::from_errno(EOVERFLOW); - if (Checked::addition_would_overflow(m_memory_mapped_range->paddr.get() + offset, space_length)) - return Error::from_errno(EOVERFLOW); - - auto memory_mapped_range = TRY(Memory::adopt_new_nonnull_own_typed_mapping(m_memory_mapped_range->paddr.offset(offset), space_length, Memory::Region::Access::ReadWrite)); - return TRY(adopt_nonnull_own_or_enomem(new (nothrow) IOWindow(move(memory_mapped_range)))); -} - -ErrorOr> IOWindow::create_from_io_window_with_offset(u64 offset) -{ - -#if ARCH(X86_64) - if (m_space_type == SpaceType::IO) { - VERIFY(m_io_range); - VERIFY(m_io_range->space_length() >= offset); - return create_from_io_window_with_offset(offset, m_io_range->space_length() - offset); - } -#endif - VERIFY(space_type() == SpaceType::Memory); - VERIFY(m_memory_mapped_range); - VERIFY(m_memory_mapped_range->length >= offset); - return create_from_io_window_with_offset(offset, m_memory_mapped_range->length - offset); -} - -ErrorOr> IOWindow::create_for_pci_device_bar(PCI::DeviceIdentifier const& pci_device_identifier, PCI::HeaderType0BaseRegister pci_bar, u64 space_length) -{ - u64 pci_bar_value = PCI::get_BAR(pci_device_identifier, pci_bar); - auto pci_bar_space_type = PCI::get_BAR_space_type(pci_bar_value); - - if (pci_bar_space_type == PCI::BARSpaceType::IOSpace) { -#if ARCH(X86_64) - auto pci_bar_space_size = PCI::get_BAR_space_size(pci_device_identifier, pci_bar); - if (pci_bar_space_size < space_length) - return Error::from_errno(EIO); - - // X86 IO instructions use DX -a 16 bit register- as the "address" - // So we need to check against u16's - if (pci_bar_value > AK::NumericLimits::max()) - return Error::from_errno(EOVERFLOW); - if (Checked::addition_would_overflow(static_cast(pci_bar_value), static_cast(space_length))) - return Error::from_errno(EOVERFLOW); - auto io_address_range = TRY(adopt_nonnull_own_or_enomem(new (nothrow) IOAddressData((pci_bar_value & 0xfffffffc), space_length))); - return TRY(adopt_nonnull_own_or_enomem(new (nothrow) IOWindow(move(io_address_range)))); -#else - // Note: For non-x86 platforms, IO PCI BARs are simply not useable. - return Error::from_errno(ENOTSUP); -#endif - } - - auto memory_mapped_range = TRY(PCI::adopt_new_nonnull_own_bar_mapping(pci_device_identifier, pci_bar, space_length)); - return TRY(adopt_nonnull_own_or_enomem(new (nothrow) IOWindow(move(memory_mapped_range)))); -} - -ErrorOr> IOWindow::create_for_pci_device_bar(PCI::DeviceIdentifier const& pci_device_identifier, PCI::HeaderType0BaseRegister pci_bar) -{ - u64 pci_bar_space_size = PCI::get_BAR_space_size(pci_device_identifier, pci_bar); - return create_for_pci_device_bar(pci_device_identifier, pci_bar, pci_bar_space_size); -} - -IOWindow::IOWindow(NonnullOwnPtr> memory_mapped_range) - : m_space_type(SpaceType::Memory) - , m_memory_mapped_range(move(memory_mapped_range)) -{ -} - -IOWindow::~IOWindow() = default; - -bool IOWindow::is_access_aligned(u64 offset, size_t byte_size_access) const -{ - return (offset % byte_size_access) == 0; -} - -bool IOWindow::is_access_in_range(u64 offset, size_t byte_size_access) const -{ - if (Checked::addition_would_overflow(offset, byte_size_access)) - return false; -#if ARCH(X86_64) - if (m_space_type == SpaceType::IO) { - VERIFY(m_io_range); - VERIFY(!Checked::addition_would_overflow(m_io_range->address(), m_io_range->space_length())); - // To understand how we treat IO address space with the corresponding calculation, the Intel Software Developer manual - // helps us to understand the layout of the IO address space - - // - // Intel® 64 and IA-32 Architectures Software Developer’s Manual, Volume 1: Basic Architecture, 16.3 I/O ADDRESS SPACE, page 16-1 wrote: - // Any two consecutive 8-bit ports can be treated as a 16-bit port, and any four consecutive ports can be a 32-bit port. - // In this manner, the processor can transfer 8, 16, or 32 bits to or from a device in the I/O address space. - // Like words in memory, 16-bit ports should be aligned to even addresses (0, 2, 4, ...) so that all 16 bits can be transferred in a single bus cycle. - // Likewise, 32-bit ports should be aligned to addresses that are multiples of four (0, 4, 8, ...). - // The processor supports data transfers to unaligned ports, but there is a performance penalty because one or more - // extra bus cycle must be used. - return (m_io_range->address() + m_io_range->space_length()) >= (offset + byte_size_access); - } -#endif - VERIFY(space_type() == SpaceType::Memory); - VERIFY(m_memory_mapped_range); - VERIFY(!Checked::addition_would_overflow(m_memory_mapped_range->offset, m_memory_mapped_range->length)); - return (m_memory_mapped_range->offset + m_memory_mapped_range->length) >= (offset + byte_size_access); -} - -u8 IOWindow::read8(u64 offset) -{ - VERIFY(is_access_in_range(offset, sizeof(u8))); - u8 data { 0 }; - in(offset, data); - return data; -} -u16 IOWindow::read16(u64 offset) -{ - // Note: Although it might be OK to allow unaligned access on regular memory, - // for memory mapped IO access, it should always be considered a bug. - // The same goes for port mapped IO access, because in x86 unaligned access to ports - // is possible but there's a performance penalty. - VERIFY(is_access_in_range(offset, sizeof(u16))); - VERIFY(is_access_aligned(offset, sizeof(u16))); - u16 data { 0 }; - in(offset, data); - return data; -} -u32 IOWindow::read32(u64 offset) -{ - // Note: Although it might be OK to allow unaligned access on regular memory, - // for memory mapped IO access, it should always be considered a bug. - // The same goes for port mapped IO access, because in x86 unaligned access to ports - // is possible but there's a performance penalty. - VERIFY(is_access_in_range(offset, sizeof(u32))); - VERIFY(is_access_aligned(offset, sizeof(u32))); - u32 data { 0 }; - in(offset, data); - return data; -} - -void IOWindow::write8(u64 offset, u8 data) -{ - VERIFY(is_access_in_range(offset, sizeof(u8))); - out(offset, data); -} -void IOWindow::write16(u64 offset, u16 data) -{ - // Note: Although it might be OK to allow unaligned access on regular memory, - // for memory mapped IO access, it should always be considered a bug. - // The same goes for port mapped IO access, because in x86 unaligned access to ports - // is possible but there's a performance penalty. - VERIFY(is_access_in_range(offset, sizeof(u16))); - VERIFY(is_access_aligned(offset, sizeof(u16))); - out(offset, data); -} -void IOWindow::write32(u64 offset, u32 data) -{ - // Note: Although it might be OK to allow unaligned access on regular memory, - // for memory mapped IO access, it should always be considered a bug. - // The same goes for port mapped IO access, because in x86 unaligned access to ports - // is possible but there's a performance penalty. - VERIFY(is_access_in_range(offset, sizeof(u32))); - VERIFY(is_access_aligned(offset, sizeof(u32))); - out(offset, data); -} - -void IOWindow::write32_unaligned(u64 offset, u32 data) -{ - // Note: We only verify that we access IO in the expected range. - // Note: for port mapped IO access, because in x86 unaligned access to ports - // is possible but there's a performance penalty, we can still allow that to happen. - // However, it should be noted that most cases should not use unaligned access - // to hardware IO, so this is a valid case in emulators or hypervisors only. - // Note: Using this for memory mapped IO will fail for unaligned access, because - // there's no valid use case for it (yet). - VERIFY(space_type() != SpaceType::Memory); - VERIFY(is_access_in_range(offset, sizeof(u32))); - out(offset, data); -} - -u32 IOWindow::read32_unaligned(u64 offset) -{ - // Note: We only verify that we access IO in the expected range. - // Note: for port mapped IO access, because in x86 unaligned access to ports - // is possible but there's a performance penalty, we can still allow that to happen. - // However, it should be noted that most cases should not use unaligned access - // to hardware IO, so this is a valid case in emulators or hypervisors only. - // Note: Using this for memory mapped IO will fail for unaligned access, because - // there's no valid use case for it (yet). - VERIFY(space_type() != SpaceType::Memory); - VERIFY(is_access_in_range(offset, sizeof(u32))); - u32 data { 0 }; - in(offset, data); - return data; -} - -PhysicalAddress IOWindow::as_physical_memory_address() const -{ - VERIFY(space_type() == SpaceType::Memory); - VERIFY(m_memory_mapped_range); - return m_memory_mapped_range->paddr; -} - -u8 volatile* IOWindow::as_memory_address_pointer() -{ - VERIFY(space_type() == SpaceType::Memory); - VERIFY(m_memory_mapped_range); - return m_memory_mapped_range->ptr(); -} - -#if ARCH(X86_64) -IOAddress IOWindow::as_io_address() const -{ - VERIFY(space_type() == SpaceType::IO); - VERIFY(m_io_range); - return IOAddress(m_io_range->address()); -} -#endif - -} diff --git a/Kernel/Library/IOWindow.h b/Kernel/Library/IOWindow.h deleted file mode 100644 index d07793681b8..00000000000 --- a/Kernel/Library/IOWindow.h +++ /dev/null @@ -1,155 +0,0 @@ -/* - * Copyright (c) 2022, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#if ARCH(X86_64) -# include -#endif -#include -#include -#include - -namespace Kernel { - -class IOWindow { -public: - enum class SpaceType { -#if ARCH(X86_64) - IO, -#endif - Memory, - }; - - SpaceType space_type() const { return m_space_type; } - -#if ARCH(X86_64) - static ErrorOr> create_for_io_space(IOAddress, u64 space_length); -#endif - static ErrorOr> create_for_pci_device_bar(PCI::DeviceIdentifier const&, PCI::HeaderType0BaseRegister, u64 space_length); - static ErrorOr> create_for_pci_device_bar(PCI::DeviceIdentifier const&, PCI::HeaderType0BaseRegister); - - ErrorOr> create_from_io_window_with_offset(u64 offset, u64 space_length); - ErrorOr> create_from_io_window_with_offset(u64 offset); - - u8 read8(u64 offset); - u16 read16(u64 offset); - u32 read32(u64 offset); - - void write8(u64 offset, u8); - void write16(u64 offset, u16); - void write32(u64 offset, u32); - - // Note: These methods are useful in exceptional cases where we need to do unaligned - // access. This mostly happens on emulators and hypervisors (such as VMWare) because they don't enforce aligned access - // to IO and sometimes even require such access, so we have to use these functions. - void write32_unaligned(u64 offset, u32); - u32 read32_unaligned(u64 offset); - - bool operator==(IOWindow const& other) const = delete; - bool operator!=(IOWindow const& other) const = delete; - bool operator>(IOWindow const& other) const = delete; - bool operator>=(IOWindow const& other) const = delete; - bool operator<(IOWindow const& other) const = delete; - bool operator<=(IOWindow const& other) const = delete; - - ~IOWindow(); - - PhysicalAddress as_physical_memory_address() const; -#if ARCH(X86_64) - IOAddress as_io_address() const; -#endif - -private: - explicit IOWindow(NonnullOwnPtr>); - - u8 volatile* as_memory_address_pointer(); - -#if ARCH(X86_64) - struct IOAddressData { - public: - IOAddressData(u64 address, u64 space_length) - : m_address(address) - , m_space_length(space_length) - { - } - u64 address() const { return m_address; } - u64 space_length() const { return m_space_length; } - - private: - u64 m_address { 0 }; - u64 m_space_length { 0 }; - }; - - explicit IOWindow(NonnullOwnPtr); -#endif - - bool is_access_in_range(u64 offset, size_t byte_size_access) const; - bool is_access_aligned(u64 offset, size_t byte_size_access) const; - - template - ALWAYS_INLINE void in(u64 start_offset, T& data) - { -#if ARCH(X86_64) - if (m_space_type == SpaceType::IO) { - data = as_io_address().offset(start_offset).in(); - return; - } -#endif - VERIFY(m_space_type == SpaceType::Memory); - VERIFY(m_memory_mapped_range); - // Note: For memory-mapped IO we simply never allow unaligned access as it - // can cause problems with strict bare metal hardware. For example, some XHCI USB controllers - // might completely lock up because of an unaligned memory access to their registers. - VERIFY((start_offset % sizeof(T)) == 0); - data = *(T volatile*)(as_memory_address_pointer() + start_offset); - } - - template - ALWAYS_INLINE void out(u64 start_offset, T value) - { -#if ARCH(X86_64) - if (m_space_type == SpaceType::IO) { - VERIFY(m_io_range); - as_io_address().offset(start_offset).out(value); - return; - } -#endif - VERIFY(m_space_type == SpaceType::Memory); - VERIFY(m_memory_mapped_range); - // Note: For memory-mapped IO we simply never allow unaligned access as it - // can cause problems with strict bare metal hardware. For example, some XHCI USB controllers - // might completely lock up because of an unaligned memory access to their registers. - VERIFY((start_offset % sizeof(T)) == 0); - *(T volatile*)(as_memory_address_pointer() + start_offset) = value; - } - - SpaceType m_space_type { SpaceType::Memory }; - - OwnPtr> m_memory_mapped_range; - -#if ARCH(X86_64) - OwnPtr m_io_range; -#endif -}; - -} - -template<> -struct AK::Formatter : AK::Formatter { - ErrorOr format(FormatBuilder& builder, Kernel::IOWindow const& value) - { -#if ARCH(X86_64) - if (value.space_type() == Kernel::IOWindow::SpaceType::IO) - return Formatter::format(builder, "{}"sv, value.as_io_address()); -#endif - VERIFY(value.space_type() == Kernel::IOWindow::SpaceType::Memory); - return Formatter::format(builder, "Memory {}"sv, value.as_physical_memory_address()); - } -}; diff --git a/Kernel/Library/KBuffer.h b/Kernel/Library/KBuffer.h deleted file mode 100644 index b5694b3a924..00000000000 --- a/Kernel/Library/KBuffer.h +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright (c) 2018-2021, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -// KBuffer: Memory buffer backed by a kernel region. -// -// The memory is allocated via the global kernel-only page allocator, rather than via -// kmalloc() which is what ByteBuffer/Vector/etc will use. -// -// This makes KBuffer a little heavier to allocate, but much better for large and/or -// long-lived allocations, since they don't put all that weight and pressure on the -// severely limited kmalloc heap. - -#include -#include -#include // For memcpy. FIXME: Make memcpy less expensive to access a declaration of in the Kernel. -#include - -namespace Kernel { - -class [[nodiscard]] KBuffer { -public: - static ErrorOr> try_create_with_size(StringView name, size_t size, Memory::Region::Access access = Memory::Region::Access::ReadWrite, AllocationStrategy strategy = AllocationStrategy::Reserve) - { - auto rounded_size = TRY(Memory::page_round_up(size)); - auto region = TRY(MM.allocate_kernel_region(rounded_size, name, access, strategy)); - return TRY(adopt_nonnull_own_or_enomem(new (nothrow) KBuffer { size, move(region) })); - } - - static ErrorOr> try_create_with_bytes(StringView name, ReadonlyBytes bytes, Memory::Region::Access access = Memory::Region::Access::ReadWrite, AllocationStrategy strategy = AllocationStrategy::Reserve) - { - auto buffer = TRY(try_create_with_size(name, bytes.size(), access, strategy)); - memcpy(buffer->data(), bytes.data(), bytes.size()); - return buffer; - } - - [[nodiscard]] u8* data() { return m_region->vaddr().as_ptr(); } - [[nodiscard]] u8 const* data() const { return m_region->vaddr().as_ptr(); } - [[nodiscard]] size_t size() const { return m_size; } - [[nodiscard]] size_t capacity() const { return m_region->size(); } - - [[nodiscard]] ReadonlyBytes bytes() const { return { data(), size() }; } - [[nodiscard]] Bytes bytes() { return { data(), size() }; } - - void set_size(size_t size) - { - VERIFY(size <= capacity()); - m_size = size; - } - -private: - explicit KBuffer(size_t size, NonnullOwnPtr region) - : m_size(size) - , m_region(move(region)) - { - } - - size_t m_size { 0 }; - NonnullOwnPtr m_region; -}; - -} diff --git a/Kernel/Library/KBufferBuilder.cpp b/Kernel/Library/KBufferBuilder.cpp deleted file mode 100644 index e772096aff3..00000000000 --- a/Kernel/Library/KBufferBuilder.cpp +++ /dev/null @@ -1,132 +0,0 @@ -/* - * Copyright (c) 2018-2021, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include - -namespace Kernel { - -inline bool KBufferBuilder::check_expand(size_t size) -{ - if (!m_buffer) - return false; - if ((m_size + size) < m_buffer->capacity()) - return true; - if (Checked::addition_would_overflow(m_size, size)) - return false; - size_t new_buffer_size = m_size + size; - if (Checked::addition_would_overflow(new_buffer_size, 1 * MiB)) - return false; - auto rounded_new_buffer_size_or_error = Memory::page_round_up(new_buffer_size + 1 * MiB); - if (rounded_new_buffer_size_or_error.is_error()) { - return false; - } - auto new_buffer_or_error = KBuffer::try_create_with_size("KBufferBuilder"sv, rounded_new_buffer_size_or_error.value()); - if (new_buffer_or_error.is_error()) - return false; - auto new_buffer = new_buffer_or_error.release_value(); - memcpy(new_buffer->data(), m_buffer->data(), m_buffer->size()); - m_buffer = move(new_buffer); - return true; -} - -bool KBufferBuilder::flush() -{ - if (!m_buffer) - return false; - m_buffer->set_size(m_size); - return true; -} - -OwnPtr KBufferBuilder::build() -{ - if (!flush()) - return {}; - - return move(m_buffer); -} - -ErrorOr KBufferBuilder::try_create() -{ - auto buffer = TRY(KBuffer::try_create_with_size("KBufferBuilder"sv, 4 * MiB, Memory::Region::Access::ReadWrite)); - return KBufferBuilder { move(buffer) }; -} - -KBufferBuilder::KBufferBuilder(NonnullOwnPtr buffer) - : m_buffer(move(buffer)) -{ -} - -ErrorOr KBufferBuilder::append_bytes(ReadonlyBytes bytes) -{ - if (!check_expand(bytes.size())) - return ENOMEM; - memcpy(insertion_ptr(), bytes.data(), bytes.size()); - m_size += bytes.size(); - return {}; -} - -ErrorOr KBufferBuilder::append(StringView str) -{ - if (str.is_empty()) - return {}; - if (!check_expand(str.length())) - return ENOMEM; - memcpy(insertion_ptr(), str.characters_without_null_termination(), str.length()); - m_size += str.length(); - return {}; -} - -ErrorOr KBufferBuilder::append(char const* characters, int length) -{ - if (!length) - return {}; - if (!check_expand(length)) - return ENOMEM; - memcpy(insertion_ptr(), characters, length); - m_size += length; - return {}; -} - -ErrorOr KBufferBuilder::append(char ch) -{ - if (!check_expand(1)) - return ENOMEM; - insertion_ptr()[0] = ch; - m_size += 1; - return {}; -} - -ErrorOr KBufferBuilder::append_escaped_for_json(StringView string) -{ - for (auto ch : string) { - switch (ch) { - case '\b': - TRY(append("\\b"sv)); - break; - case '\n': - TRY(append("\\n"sv)); - break; - case '\t': - TRY(append("\\t"sv)); - break; - case '\"': - TRY(append("\\\""sv)); - break; - case '\\': - TRY(append("\\\\"sv)); - break; - default: - if (ch >= 0 && ch <= 0x1f) - TRY(appendff("\\u{:04x}", ch)); - else - TRY(append(ch)); - } - } - return {}; -} - -} diff --git a/Kernel/Library/KBufferBuilder.h b/Kernel/Library/KBufferBuilder.h deleted file mode 100644 index efa1134169b..00000000000 --- a/Kernel/Library/KBufferBuilder.h +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Copyright (c) 2018-2021, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include - -namespace Kernel { - -class KBufferBuilder { - AK_MAKE_NONCOPYABLE(KBufferBuilder); - AK_MAKE_DEFAULT_MOVABLE(KBufferBuilder); - -public: - using OutputType = KBuffer; - - static ErrorOr try_create(); - - ~KBufferBuilder() = default; - - ErrorOr append(StringView); - ErrorOr append(char); - ErrorOr append(char const*, int); - - ErrorOr append_escaped_for_json(StringView); - ErrorOr append_bytes(ReadonlyBytes); - - template - ErrorOr appendff(CheckedFormatString&& fmtstr, Parameters const&... parameters) - { - // FIXME: This really not ideal, but vformat expects StringBuilder. - StringBuilder builder; - AK::VariadicFormatParams variadic_format_params { parameters... }; - TRY(vformat(builder, fmtstr.view(), variadic_format_params)); - return append_bytes(builder.string_view().bytes()); - } - - bool flush(); - OwnPtr build(); - - ReadonlyBytes bytes() const - { - if (!m_buffer) - return {}; - return m_buffer->bytes(); - } - - size_t length() const - { - return m_size; - } - -private: - explicit KBufferBuilder(NonnullOwnPtr); - - bool check_expand(size_t); - u8* insertion_ptr() - { - if (!m_buffer) - return nullptr; - return m_buffer->data() + m_size; - } - - OwnPtr m_buffer; - size_t m_size { 0 }; -}; - -} diff --git a/Kernel/Library/KLexicalPath.cpp b/Kernel/Library/KLexicalPath.cpp deleted file mode 100644 index 011cd1c3295..00000000000 --- a/Kernel/Library/KLexicalPath.cpp +++ /dev/null @@ -1,89 +0,0 @@ -/* - * Copyright (c) 2021, Max Wipfli - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include - -namespace Kernel::KLexicalPath { - -static StringView const s_single_dot = "."sv; - -bool is_absolute(StringView path) -{ - return !path.is_empty() && path[0] == '/'; -} - -bool is_canonical(StringView path) -{ - // FIXME: This can probably be done more efficiently. - if (path.is_empty()) - return false; - if (path.ends_with('/') && path.length() != 1) - return false; - if (path.starts_with("./"sv) || path.contains("/./"sv) || path.ends_with("/."sv)) - return false; - if (path.starts_with("../"sv) || path.contains("/../"sv) || path.ends_with("/.."sv)) - return false; - if (path.contains("//"sv)) - return false; - return true; -} - -StringView basename(StringView a_path) -{ - if (a_path == "/"sv) - return a_path; - if (a_path.is_empty()) - return s_single_dot; - auto path = a_path.trim("/"sv, TrimMode::Right); - // NOTE: If it's empty now, it means the path was just a series of slashes. - if (path.is_empty()) - return a_path.substring_view(0, 1); - auto slash_index = path.find_last('/'); - if (!slash_index.has_value()) - return path; - auto basename = path.substring_view(*slash_index + 1); - return basename; -} - -StringView dirname(StringView path) -{ - VERIFY(is_canonical(path)); - auto slash_index = path.find_last('/'); - VERIFY(slash_index.has_value()); - return path.substring_view(0, *slash_index); -} - -Vector parts(StringView path) -{ - VERIFY(is_canonical(path)); - return path.split_view('/'); -} - -ErrorOr> try_join(StringView first, StringView second) -{ - VERIFY(is_canonical(first)); - VERIFY(is_canonical(second)); - VERIFY(!is_absolute(second)); - - if (first == "/"sv) { - char* buffer; - auto string = TRY(KString::try_create_uninitialized(1 + second.length(), buffer)); - buffer[0] = '/'; - __builtin_memcpy(buffer + 1, second.characters_without_null_termination(), second.length()); - buffer[string->length()] = 0; - return string; - } - char* buffer; - auto string = TRY(KString::try_create_uninitialized(first.length() + 1 + second.length(), buffer)); - __builtin_memcpy(buffer, first.characters_without_null_termination(), first.length()); - buffer[first.length()] = '/'; - __builtin_memcpy(buffer + first.length() + 1, second.characters_without_null_termination(), second.length()); - buffer[string->length()] = 0; - return string; -} - -} diff --git a/Kernel/Library/KLexicalPath.h b/Kernel/Library/KLexicalPath.h deleted file mode 100644 index abf7eb0591e..00000000000 --- a/Kernel/Library/KLexicalPath.h +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Copyright (c) 2021, Max Wipfli - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include - -namespace Kernel::KLexicalPath { - -bool is_absolute(StringView); -bool is_canonical(StringView); -StringView basename(StringView); -StringView dirname(StringView); -Vector parts(StringView); - -ErrorOr> try_join(StringView, StringView); - -} diff --git a/Kernel/Library/KString.cpp b/Kernel/Library/KString.cpp deleted file mode 100644 index 9435782ee07..00000000000 --- a/Kernel/Library/KString.cpp +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright (c) 2021, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include - -extern SetOnce g_not_in_early_boot; - -namespace Kernel { - -ErrorOr> KString::try_create(StringView string) -{ - char* characters = nullptr; - size_t length = string.length(); - auto new_string = TRY(KString::try_create_uninitialized(length, characters)); - if (!string.is_empty()) - __builtin_memcpy(characters, string.characters_without_null_termination(), length); - characters[length] = '\0'; - return new_string; -} - -ErrorOr> KString::vformatted(StringView fmtstr, AK::TypeErasedFormatParams& params) -{ - StringBuilder builder; - TRY(AK::vformat(builder, fmtstr, params)); - return try_create(builder.string_view()); -} - -NonnullOwnPtr KString::must_create(StringView string) -{ - // We can only enforce success during early boot. - VERIFY(!g_not_in_early_boot.was_set()); - return KString::try_create(string).release_value(); -} - -ErrorOr> KString::try_create_uninitialized(size_t length, char*& characters) -{ - size_t allocation_size = sizeof(KString) + (sizeof(char) * length) + sizeof(char); - auto* slot = kmalloc(allocation_size); - if (!slot) - return ENOMEM; - auto new_string = TRY(adopt_nonnull_own_or_enomem(new (slot) KString(length))); - characters = new_string->m_characters; - return new_string; -} - -NonnullOwnPtr KString::must_create_uninitialized(size_t length, char*& characters) -{ - // We can only enforce success during early boot. - VERIFY(!g_not_in_early_boot.was_set()); - return KString::try_create_uninitialized(length, characters).release_value(); -} - -ErrorOr> KString::try_clone() const -{ - return try_create(view()); -} - -void KString::operator delete(void* string) -{ - if (!string) - return; - size_t allocation_size = sizeof(KString) + (sizeof(char) * static_cast(string)->m_length) + sizeof(char); - kfree_sized(string, allocation_size); -} - -} diff --git a/Kernel/Library/KString.h b/Kernel/Library/KString.h deleted file mode 100644 index 5e299bb3b15..00000000000 --- a/Kernel/Library/KString.h +++ /dev/null @@ -1,131 +0,0 @@ -/* - * Copyright (c) 2021, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include - -namespace Kernel { - -class KString { - AK_MAKE_NONCOPYABLE(KString); - AK_MAKE_NONMOVABLE(KString); - -public: - [[nodiscard]] static ErrorOr> try_create_uninitialized(size_t, char*&); - [[nodiscard]] static NonnullOwnPtr must_create_uninitialized(size_t, char*&); - [[nodiscard]] static ErrorOr> try_create(StringView); - [[nodiscard]] static NonnullOwnPtr must_create(StringView); - - [[nodiscard]] static ErrorOr> vformatted(StringView fmtstr, AK::TypeErasedFormatParams&); - - template - [[nodiscard]] static ErrorOr> formatted(CheckedFormatString&& fmtstr, Parameters const&... parameters) - { - AK::VariadicFormatParams variadic_format_parameters { parameters... }; - return vformatted(fmtstr.view(), variadic_format_parameters); - } - - [[nodiscard]] static ErrorOr> number(Arithmetic auto value) - { - return formatted("{}", value); - } - - void operator delete(void*); - - ErrorOr> try_clone() const; - - [[nodiscard]] bool is_empty() const { return m_length == 0; } - [[nodiscard]] size_t length() const { return m_length; } - [[nodiscard]] char const* characters() const { return m_characters; } - [[nodiscard]] StringView view() const { return { characters(), length() }; } - [[nodiscard]] ReadonlyBytes bytes() const { return { characters(), length() }; } - -private: - explicit KString(size_t length) - : m_length(length) - { - } - - size_t m_length { 0 }; - char m_characters[0]; -}; - -} - -namespace AK { - -template<> -struct Formatter : Formatter { - ErrorOr format(FormatBuilder& builder, Kernel::KString const& value) - { - return Formatter::format(builder, value.view()); - } -}; - -template<> -struct Formatter> : Formatter { - ErrorOr format(FormatBuilder& builder, OwnPtr const& value) - { - if (value) - return Formatter::format(builder, value->view()); - return Formatter::format(builder, "[out of memory]"sv); - } -}; - -template<> -struct Formatter> : Formatter { - ErrorOr format(FormatBuilder& builder, NonnullOwnPtr const& value) - { - return Formatter::format(builder, value->view()); - } -}; - -template<> -struct Traits> : public DefaultTraits> { - using PeekType = Kernel::KString*; - using ConstPeekType = Kernel::KString const*; - static unsigned hash(NonnullOwnPtr const& p) { return string_hash(p->characters(), p->length()); } - static bool equals(NonnullOwnPtr const& a, NonnullOwnPtr const& b) { return a->view() == b->view(); } - static bool equals(NonnullOwnPtr const& a, StringView b) { return a->view() == b; } -}; - -template<> -struct Traits> : public DefaultTraits> { - using PeekType = Kernel::KString*; - using ConstPeekType = Kernel::KString const*; - static unsigned hash(OwnPtr const& p) - { - if (!p) - return ptr_hash(nullptr); - return string_hash(p->characters(), p->length()); - } - static bool equals(OwnPtr const& a, OwnPtr const& b) - { - if (!a || !b) - return a.ptr() == b.ptr(); - if (a == b) - return true; - - return a->view() == b->view(); - } - static bool equals(OwnPtr const& a, StringView b) - { - if (!a) - return b.is_null(); - return a->view() == b; - } -}; - -namespace Detail { -template<> -inline constexpr bool IsHashCompatible> = true; -template<> -inline constexpr bool IsHashCompatible> = true; -} - -} diff --git a/Kernel/Library/ListedRefCounted.h b/Kernel/Library/ListedRefCounted.h deleted file mode 100644 index 9374516264a..00000000000 --- a/Kernel/Library/ListedRefCounted.h +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright (c) 2021, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include - -namespace Kernel { - -// ListedRefCounted is a slot-in replacement for RefCounted to use in classes -// that add themselves to a {Spinlock, Mutex}Protected when constructed. -// The custom unref() implementation here ensures that the list is locked during -// unref(), and that the T is removed from the list before ~T() is invoked. - -enum class LockType { - Spinlock, - Mutex, -}; - -template -class ListedRefCounted : public AtomicRefCountedBase { -public: - bool unref() const - { - auto* that = const_cast(static_cast(this)); - - auto callback = [&](auto& list) { - auto new_ref_count = deref_base(); - if (new_ref_count == 0) { - list.remove(const_cast(*that)); - if constexpr (requires { that->revoke_weak_ptrs(); }) { - that->revoke_weak_ptrs(); - } - if constexpr (requires { that->remove_from_secondary_lists(); }) - that->remove_from_secondary_lists(); - } - return new_ref_count; - }; - - RefCountType new_ref_count; - if constexpr (Lock == LockType::Spinlock) - new_ref_count = T::all_instances().with(callback); - else if constexpr (Lock == LockType::Mutex) - new_ref_count = T::all_instances().with_exclusive(callback); - if (new_ref_count == 0) { - if constexpr (requires { that->will_be_destroyed(); }) - that->will_be_destroyed(); - delete that; - } - return new_ref_count == 0; - } -}; - -} diff --git a/Kernel/Library/LockRefPtr.h b/Kernel/Library/LockRefPtr.h deleted file mode 100644 index 3a66bd8e07e..00000000000 --- a/Kernel/Library/LockRefPtr.h +++ /dev/null @@ -1,530 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include -#include -#include -#include -#ifdef KERNEL -# include -# include -#endif - -#define LOCKREFPTR_SCRUB_BYTE 0xa0 - -namespace AK { - -template -struct LockRefPtrTraits { - ALWAYS_INLINE static T* as_ptr(FlatPtr bits) - { - return (T*)(bits & ~(FlatPtr)1); - } - - ALWAYS_INLINE static FlatPtr as_bits(T* ptr) - { - VERIFY(((FlatPtr)ptr & 1) == 0); - return (FlatPtr)ptr; - } - - template - ALWAYS_INLINE static FlatPtr convert_from(FlatPtr bits) - { - if (PtrTraits::is_null(bits)) - return default_null_value; - return as_bits(PtrTraits::as_ptr(bits)); - } - - ALWAYS_INLINE static bool is_null(FlatPtr bits) - { - return (bits & ~(FlatPtr)1) == 0; - } - - ALWAYS_INLINE static FlatPtr exchange(Atomic& atomic_var, FlatPtr new_value) - { - // Only exchange when lock is not held - VERIFY((new_value & 1) == 0); - FlatPtr expected = atomic_var.load(AK::MemoryOrder::memory_order_relaxed); - for (;;) { - expected &= ~(FlatPtr)1; // only if lock bit is not set - if (atomic_var.compare_exchange_strong(expected, new_value, AK::MemoryOrder::memory_order_acq_rel)) - break; -#ifdef KERNEL - Kernel::Processor::wait_check(); -#endif - } - return expected; - } - - ALWAYS_INLINE static bool exchange_if_null(Atomic& atomic_var, FlatPtr new_value) - { - // Only exchange when lock is not held - VERIFY((new_value & 1) == 0); - for (;;) { - FlatPtr expected = default_null_value; // only if lock bit is not set - if (atomic_var.compare_exchange_strong(expected, new_value, AK::MemoryOrder::memory_order_acq_rel)) - break; - if (!is_null(expected)) - return false; -#ifdef KERNEL - Kernel::Processor::wait_check(); -#endif - } - return true; - } - - ALWAYS_INLINE static FlatPtr lock(Atomic& atomic_var) - { - // This sets the lock bit atomically, preventing further modifications. - // This is important when e.g. copying a LockRefPtr where the source - // might be released and freed too quickly. This allows us - // to temporarily lock the pointer so we can add a reference, then - // unlock it - FlatPtr bits; - for (;;) { - bits = atomic_var.fetch_or(1, AK::MemoryOrder::memory_order_acq_rel); - if ((bits & 1) == 0) - break; -#ifdef KERNEL - Kernel::Processor::wait_check(); -#endif - } - VERIFY((bits & 1) == 0); - return bits; - } - - ALWAYS_INLINE static void unlock(Atomic& atomic_var, FlatPtr new_value) - { - VERIFY((new_value & 1) == 0); - atomic_var.store(new_value, AK::MemoryOrder::memory_order_release); - } - - static constexpr FlatPtr default_null_value = 0; - - using NullType = nullptr_t; -}; - -template -class [[nodiscard]] LockRefPtr { - template - friend class LockRefPtr; - template - friend class LockWeakPtr; - -public: - enum AdoptTag { - Adopt - }; - - LockRefPtr() = default; - LockRefPtr(T const* ptr) - : m_bits(PtrTraits::as_bits(const_cast(ptr))) - { - ref_if_not_null(const_cast(ptr)); - } - LockRefPtr(T const& object) - : m_bits(PtrTraits::as_bits(const_cast(&object))) - { - T* ptr = const_cast(&object); - VERIFY(ptr); - VERIFY(!is_null()); - ptr->ref(); - } - LockRefPtr(AdoptTag, T& object) - : m_bits(PtrTraits::as_bits(&object)) - { - VERIFY(!is_null()); - } - LockRefPtr(LockRefPtr&& other) - : m_bits(other.leak_ref_raw()) - { - } - ALWAYS_INLINE LockRefPtr(NonnullLockRefPtr const& other) - : m_bits(PtrTraits::as_bits(const_cast(other.add_ref()))) - { - } - template - ALWAYS_INLINE LockRefPtr(NonnullLockRefPtr const& other) - requires(IsConvertible) - : m_bits(PtrTraits::as_bits(const_cast(other.add_ref()))) - { - } - template - ALWAYS_INLINE LockRefPtr(NonnullLockRefPtr&& other) - requires(IsConvertible) - : m_bits(PtrTraits::as_bits(&other.leak_ref())) - { - VERIFY(!is_null()); - } - template> - LockRefPtr(LockRefPtr&& other) - requires(IsConvertible) - : m_bits(PtrTraits::template convert_from(other.leak_ref_raw())) - { - } - LockRefPtr(LockRefPtr const& other) - : m_bits(other.add_ref_raw()) - { - } - template> - LockRefPtr(LockRefPtr const& other) - requires(IsConvertible) - : m_bits(other.add_ref_raw()) - { - } - ALWAYS_INLINE ~LockRefPtr() - { - clear(); -#ifdef SANITIZE_PTRS - m_bits.store(explode_byte(LOCKREFPTR_SCRUB_BYTE), AK::MemoryOrder::memory_order_relaxed); -#endif - } - - template - LockRefPtr(OwnPtr const&) = delete; - template - LockRefPtr& operator=(OwnPtr const&) = delete; - - void swap(LockRefPtr& other) - { - if (this == &other) - return; - - // NOTE: swap is not atomic! - FlatPtr other_bits = PtrTraits::exchange(other.m_bits, PtrTraits::default_null_value); - FlatPtr bits = PtrTraits::exchange(m_bits, other_bits); - PtrTraits::exchange(other.m_bits, bits); - } - - template> - void swap(LockRefPtr& other) - requires(IsConvertible) - { - // NOTE: swap is not atomic! - FlatPtr other_bits = P::exchange(other.m_bits, P::default_null_value); - FlatPtr bits = PtrTraits::exchange(m_bits, PtrTraits::template convert_from(other_bits)); - P::exchange(other.m_bits, P::template convert_from(bits)); - } - - ALWAYS_INLINE LockRefPtr& operator=(LockRefPtr&& other) - { - if (this != &other) - assign_raw(other.leak_ref_raw()); - return *this; - } - - template> - ALWAYS_INLINE LockRefPtr& operator=(LockRefPtr&& other) - requires(IsConvertible) - { - assign_raw(PtrTraits::template convert_from(other.leak_ref_raw())); - return *this; - } - - template - ALWAYS_INLINE LockRefPtr& operator=(NonnullLockRefPtr&& other) - requires(IsConvertible) - { - assign_raw(PtrTraits::as_bits(&other.leak_ref())); - return *this; - } - - ALWAYS_INLINE LockRefPtr& operator=(NonnullLockRefPtr const& other) - { - assign_raw(PtrTraits::as_bits(other.add_ref())); - return *this; - } - - template - ALWAYS_INLINE LockRefPtr& operator=(NonnullLockRefPtr const& other) - requires(IsConvertible) - { - assign_raw(PtrTraits::as_bits(other.add_ref())); - return *this; - } - - ALWAYS_INLINE LockRefPtr& operator=(LockRefPtr const& other) - { - if (this != &other) - assign_raw(other.add_ref_raw()); - return *this; - } - - template - ALWAYS_INLINE LockRefPtr& operator=(LockRefPtr const& other) - requires(IsConvertible) - { - assign_raw(other.add_ref_raw()); - return *this; - } - - ALWAYS_INLINE LockRefPtr& operator=(T const* ptr) - { - ref_if_not_null(const_cast(ptr)); - assign_raw(PtrTraits::as_bits(const_cast(ptr))); - return *this; - } - - ALWAYS_INLINE LockRefPtr& operator=(T const& object) - { - const_cast(object).ref(); - assign_raw(PtrTraits::as_bits(const_cast(&object))); - return *this; - } - - LockRefPtr& operator=(nullptr_t) - { - clear(); - return *this; - } - - ALWAYS_INLINE bool assign_if_null(LockRefPtr&& other) - { - if (this == &other) - return is_null(); - return PtrTraits::exchange_if_null(m_bits, other.leak_ref_raw()); - } - - template> - ALWAYS_INLINE bool assign_if_null(LockRefPtr&& other) - { - if (this == &other) - return is_null(); - return PtrTraits::exchange_if_null(m_bits, PtrTraits::template convert_from(other.leak_ref_raw())); - } - - ALWAYS_INLINE void clear() - { - assign_raw(PtrTraits::default_null_value); - } - - bool operator!() const { return PtrTraits::is_null(m_bits.load(AK::MemoryOrder::memory_order_relaxed)); } - - [[nodiscard]] T* leak_ref() - { - FlatPtr bits = PtrTraits::exchange(m_bits, PtrTraits::default_null_value); - return PtrTraits::as_ptr(bits); - } - - NonnullLockRefPtr release_nonnull() - { - FlatPtr bits = PtrTraits::exchange(m_bits, PtrTraits::default_null_value); - VERIFY(!PtrTraits::is_null(bits)); - return NonnullLockRefPtr(NonnullLockRefPtr::Adopt, *PtrTraits::as_ptr(bits)); - } - - ALWAYS_INLINE T* ptr() { return as_ptr(); } - ALWAYS_INLINE T const* ptr() const { return as_ptr(); } - - ALWAYS_INLINE T* operator->() - { - return as_nonnull_ptr(); - } - - ALWAYS_INLINE T const* operator->() const - { - return as_nonnull_ptr(); - } - - ALWAYS_INLINE T& operator*() - { - return *as_nonnull_ptr(); - } - - ALWAYS_INLINE T const& operator*() const - { - return *as_nonnull_ptr(); - } - - ALWAYS_INLINE operator T const*() const { return as_ptr(); } - ALWAYS_INLINE operator T*() { return as_ptr(); } - - ALWAYS_INLINE operator bool() { return !is_null(); } - - bool operator==(nullptr_t) const { return is_null(); } - bool operator!=(nullptr_t) const { return !is_null(); } - - bool operator==(LockRefPtr const& other) const { return as_ptr() == other.as_ptr(); } - bool operator!=(LockRefPtr const& other) const { return as_ptr() != other.as_ptr(); } - - bool operator==(LockRefPtr& other) { return as_ptr() == other.as_ptr(); } - bool operator!=(LockRefPtr& other) { return as_ptr() != other.as_ptr(); } - - bool operator==(T const* other) const { return as_ptr() == other; } - bool operator!=(T const* other) const { return as_ptr() != other; } - - bool operator==(T* other) { return as_ptr() == other; } - bool operator!=(T* other) { return as_ptr() != other; } - - ALWAYS_INLINE bool is_null() const { return PtrTraits::is_null(m_bits.load(AK::MemoryOrder::memory_order_relaxed)); } - - template - typename PtrTraits::NullType null_value() const - requires(IsSame && !IsNullPointer) - { - // make sure we are holding a null value - FlatPtr bits = m_bits.load(AK::MemoryOrder::memory_order_relaxed); - VERIFY(PtrTraits::is_null(bits)); - return PtrTraits::to_null_value(bits); - } - template - void set_null_value(typename PtrTraits::NullType value) - requires(IsSame && !IsNullPointer) - { - // make sure that new null value would be interpreted as a null value - FlatPtr bits = PtrTraits::from_null_value(value); - VERIFY(PtrTraits::is_null(bits)); - assign_raw(bits); - } - -private: - template - void do_while_locked(F f) const - { -#ifdef KERNEL - // We don't want to be pre-empted while we have the lock bit set - Kernel::ScopedCritical critical; -#endif - FlatPtr bits = PtrTraits::lock(m_bits); - T* ptr = PtrTraits::as_ptr(bits); - f(ptr); - PtrTraits::unlock(m_bits, bits); - } - - [[nodiscard]] ALWAYS_INLINE FlatPtr leak_ref_raw() - { - return PtrTraits::exchange(m_bits, PtrTraits::default_null_value); - } - - [[nodiscard]] ALWAYS_INLINE FlatPtr add_ref_raw() const - { -#ifdef KERNEL - // We don't want to be pre-empted while we have the lock bit set - Kernel::ScopedCritical critical; -#endif - // This prevents a race condition between thread A and B: - // 1. Thread A copies LockRefPtr, e.g. through assignment or copy constructor, - // gets the pointer from source, but is pre-empted before adding - // another reference - // 2. Thread B calls clear, leak_ref, or release_nonnull on source, and - // then drops the last reference, causing the object to be deleted - // 3. Thread A finishes step #1 by attempting to add a reference to - // the object that was already deleted in step #2 - FlatPtr bits = PtrTraits::lock(m_bits); - if (T* ptr = PtrTraits::as_ptr(bits)) - ptr->ref(); - PtrTraits::unlock(m_bits, bits); - return bits; - } - - ALWAYS_INLINE void assign_raw(FlatPtr bits) - { - FlatPtr prev_bits = PtrTraits::exchange(m_bits, bits); - unref_if_not_null(PtrTraits::as_ptr(prev_bits)); - } - - ALWAYS_INLINE T* as_ptr() const - { - return PtrTraits::as_ptr(m_bits.load(AK::MemoryOrder::memory_order_relaxed)); - } - - ALWAYS_INLINE T* as_nonnull_ptr() const - { - return as_nonnull_ptr(m_bits.load(AK::MemoryOrder::memory_order_relaxed)); - } - - ALWAYS_INLINE T* as_nonnull_ptr(FlatPtr bits) const - { - VERIFY(!PtrTraits::is_null(bits)); - return PtrTraits::as_ptr(bits); - } - - mutable Atomic m_bits { PtrTraits::default_null_value }; -}; - -template -struct Formatter> : Formatter { - ErrorOr format(FormatBuilder& builder, LockRefPtr const& value) - { - return Formatter::format(builder, value.ptr()); - } -}; - -template -struct Traits> : public DefaultTraits> { - using PeekType = T*; - using ConstPeekType = T const*; - static unsigned hash(LockRefPtr const& p) { return ptr_hash(p.ptr()); } - static bool equals(LockRefPtr const& a, LockRefPtr const& b) { return a.ptr() == b.ptr(); } -}; - -template -inline NonnullLockRefPtr static_ptr_cast(NonnullLockRefPtr const& ptr) -{ - return NonnullLockRefPtr(static_cast(*ptr)); -} - -template> -inline LockRefPtr static_ptr_cast(LockRefPtr const& ptr) -{ - return LockRefPtr(static_cast(ptr.ptr())); -} - -template -inline void swap(LockRefPtr& a, LockRefPtr& b) -requires(IsConvertible) -{ - a.swap(b); -} - -template -inline LockRefPtr adopt_lock_ref_if_nonnull(T* object) -{ - if (object) - return LockRefPtr(LockRefPtr::Adopt, *object); - return {}; -} - -template -requires(IsConstructible) inline ErrorOr> try_make_lock_ref_counted(Args&&... args) -{ - return adopt_nonnull_lock_ref_or_enomem(new (nothrow) T(forward(args)...)); -} - -#ifdef AK_COMPILER_APPLE_CLANG -// FIXME: Remove once P0960R3 is available in Apple Clang. -template -inline ErrorOr> try_make_lock_ref_counted(Args&&... args) -{ - return adopt_nonnull_lock_ref_or_enomem(new (nothrow) T { forward(args)... }); -} -#endif - -template -inline ErrorOr> adopt_nonnull_lock_ref_or_enomem(T* object) -{ - auto result = adopt_lock_ref_if_nonnull(object); - if (!result) - return Error::from_errno(ENOMEM); - return result.release_nonnull(); -} - -} - -using AK::adopt_lock_ref_if_nonnull; -using AK::LockRefPtr; -using AK::static_ptr_cast; -using AK::try_make_lock_ref_counted; - -#ifdef KERNEL -using AK::adopt_nonnull_lock_ref_or_enomem; -#endif diff --git a/Kernel/Library/LockWeakPtr.h b/Kernel/Library/LockWeakPtr.h deleted file mode 100644 index f2fd204d946..00000000000 --- a/Kernel/Library/LockWeakPtr.h +++ /dev/null @@ -1,239 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include - -namespace AK { - -template -class [[nodiscard]] LockWeakPtr { - template - friend class LockWeakable; - -public: - LockWeakPtr() = default; - - template - LockWeakPtr(WeakPtr const& other) - requires(IsBaseOf) - : m_link(other.m_link) - { - } - - template - LockWeakPtr(WeakPtr&& other) - requires(IsBaseOf) - : m_link(other.take_link()) - { - } - - template - LockWeakPtr& operator=(WeakPtr&& other) - requires(IsBaseOf) - { - m_link = other.take_link(); - return *this; - } - - template - LockWeakPtr& operator=(WeakPtr const& other) - requires(IsBaseOf) - { - if ((void const*)this != (void const*)&other) - m_link = other.m_link; - return *this; - } - - LockWeakPtr& operator=(nullptr_t) - { - clear(); - return *this; - } - - template - LockWeakPtr(U const& object) - requires(IsBaseOf) - : m_link(object.template try_make_weak_ptr().release_value_but_fixme_should_propagate_errors().take_link()) - { - } - - template - LockWeakPtr(U const* object) - requires(IsBaseOf) - { - if (object) - m_link = object->template try_make_weak_ptr().release_value_but_fixme_should_propagate_errors().take_link(); - } - - template - LockWeakPtr(LockRefPtr const& object) - requires(IsBaseOf) - { - object.do_while_locked([&](U* obj) { - if (obj) - m_link = obj->template try_make_weak_ptr().release_value_but_fixme_should_propagate_errors().take_link(); - }); - } - - template - LockWeakPtr(NonnullLockRefPtr const& object) - requires(IsBaseOf) - { - object.do_while_locked([&](U* obj) { - if (obj) - m_link = obj->template try_make_weak_ptr().release_value_but_fixme_should_propagate_errors().take_link(); - }); - } - - template - LockWeakPtr& operator=(U const& object) - requires(IsBaseOf) - { - m_link = object.template try_make_weak_ptr().release_value_but_fixme_should_propagate_errors().take_link(); - return *this; - } - - template - LockWeakPtr& operator=(U const* object) - requires(IsBaseOf) - { - if (object) - m_link = object->template try_make_weak_ptr().release_value_but_fixme_should_propagate_errors().take_link(); - else - m_link = nullptr; - return *this; - } - - template - LockWeakPtr& operator=(LockRefPtr const& object) - requires(IsBaseOf) - { - object.do_while_locked([&](U* obj) { - if (obj) - m_link = obj->template try_make_weak_ptr().release_value_but_fixme_should_propagate_errors().take_link(); - else - m_link = nullptr; - }); - return *this; - } - - template - LockWeakPtr& operator=(NonnullLockRefPtr const& object) - requires(IsBaseOf) - { - object.do_while_locked([&](U* obj) { - if (obj) - m_link = obj->template try_make_weak_ptr().release_value_but_fixme_should_propagate_errors().take_link(); - else - m_link = nullptr; - }); - return *this; - } - - [[nodiscard]] LockRefPtr strong_ref() const - { - // This only works with RefCounted objects, but it is the only - // safe way to get a strong reference from a LockWeakPtr. Any code - // that uses objects not derived from RefCounted will have to - // use unsafe_ptr(), but as the name suggests, it is not safe... - LockRefPtr ref; - // Using do_while_locked protects against a race with clear()! - m_link.do_while_locked([&](LockWeakLink* link) { - if (link) - ref = link->template strong_ref(); - }); - return ref; - } - - [[nodiscard]] T* unsafe_ptr() const - { - T* ptr = nullptr; - m_link.do_while_locked([&](LockWeakLink* link) { - if (link) - ptr = link->unsafe_ptr(); - }); - return ptr; - } - - operator bool() const { return m_link ? !m_link->is_null() : false; } - - [[nodiscard]] bool is_null() const { return !m_link || m_link->is_null(); } - void clear() { m_link = nullptr; } - - [[nodiscard]] LockRefPtr take_link() { return move(m_link); } - -private: - LockWeakPtr(LockRefPtr const& link) - : m_link(link) - { - } - - LockRefPtr m_link; -}; - -template -template -inline ErrorOr> LockWeakable::try_make_weak_ptr() const -{ - if constexpr (IsBaseOf) { - // Checking m_being_destroyed isn't sufficient when dealing with - // a RefCounted type.The reference count will drop to 0 before the - // destructor is invoked and revoke_weak_ptrs is called. So, try - // to add a ref (which should fail if the ref count is at 0) so - // that we prevent the destructor and revoke_weak_ptrs from being - // triggered until we're done. - if (!static_cast(this)->try_ref()) - return LockWeakPtr {}; - } else { - // For non-RefCounted types this means a weak reference can be - // obtained until the ~LockWeakable destructor is invoked! - if (m_being_destroyed.load(AK::MemoryOrder::memory_order_acquire)) - return LockWeakPtr {}; - } - if (!m_link) { - // There is a small chance that we create a new LockWeakLink and throw - // it away because another thread beat us to it. But the window is - // pretty small and the overhead isn't terrible. - m_link.assign_if_null(TRY(adopt_nonnull_lock_ref_or_enomem(new (nothrow) LockWeakLink(const_cast(static_cast(*this)))))); - } - - LockWeakPtr weak_ptr(m_link); - - if constexpr (IsBaseOf) { - // Now drop the reference we temporarily added - if (static_cast(this)->unref()) { - // We just dropped the last reference, which should have called - // revoke_weak_ptrs, which should have invalidated our weak_ptr - VERIFY(!weak_ptr.strong_ref()); - return LockWeakPtr {}; - } - } - return weak_ptr; -} - -template -struct Formatter> : Formatter { - ErrorOr format(FormatBuilder& builder, LockWeakPtr const& value) - { - auto ref = value.strong_ref(); - return Formatter::format(builder, ref.ptr()); - } -}; - -template -ErrorOr> try_make_weak_ptr_if_nonnull(T const* ptr) -{ - if (ptr) { - return ptr->template try_make_weak_ptr(); - } - return LockWeakPtr {}; -} - -} - -using AK::LockWeakPtr; diff --git a/Kernel/Library/LockWeakable.h b/Kernel/Library/LockWeakable.h deleted file mode 100644 index 0869ae4018c..00000000000 --- a/Kernel/Library/LockWeakable.h +++ /dev/null @@ -1,124 +0,0 @@ -/* - * Copyright (c) 2018-2022, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include -#include -#include - -namespace AK { - -template -class LockWeakable; -template -class LockWeakPtr; - -class LockWeakLink final : public AtomicRefCounted { - template - friend class LockWeakable; - template - friend class LockWeakPtr; - -public: - template> - LockRefPtr strong_ref() const - requires(IsBaseOf) - { - LockRefPtr ref; - - { - // We don't want to be preempted while we are trying to obtain - // a strong reference - Kernel::ScopedCritical critical; - if (!(m_consumers.fetch_add(1u << 1, AK::MemoryOrder::memory_order_acquire) & 1u)) { - T* ptr = (T*)m_ptr.load(AK::MemoryOrder::memory_order_acquire); - if (ptr && ptr->try_ref()) - ref = adopt_lock_ref(*ptr); - } - m_consumers.fetch_sub(1u << 1, AK::MemoryOrder::memory_order_release); - } - - return ref; - } - - template - T* unsafe_ptr() const - { - if (m_consumers.load(AK::MemoryOrder::memory_order_relaxed) & 1u) - return nullptr; - // NOTE: This may return a non-null pointer even if revocation - // has been triggered as there is a possible race! But it's "unsafe" - // anyway because we return a raw pointer without ensuring a - // reference... - return (T*)m_ptr.load(AK::MemoryOrder::memory_order_acquire); - } - - bool is_null() const - { - return unsafe_ptr() == nullptr; - } - - void revoke() - { - auto current_consumers = m_consumers.fetch_or(1u, AK::MemoryOrder::memory_order_relaxed); - VERIFY(!(current_consumers & 1u)); - // We flagged revocation, now wait until everyone trying to obtain - // a strong reference is done - while (current_consumers > 0) { - Kernel::Processor::wait_check(); - current_consumers = m_consumers.load(AK::MemoryOrder::memory_order_acquire) & ~1u; - } - // No one is trying to use it (anymore) - m_ptr.store(nullptr, AK::MemoryOrder::memory_order_release); - } - -private: - template - explicit LockWeakLink(T& weakable) - : m_ptr(&weakable) - { - } - mutable Atomic m_ptr; - mutable Atomic m_consumers; // LSB indicates revocation in progress -}; - -template -class LockWeakable { -private: - class Link; - -public: - template - ErrorOr> try_make_weak_ptr() const; - -protected: - LockWeakable() = default; - - ~LockWeakable() - { - m_being_destroyed.store(true, AK::MemoryOrder::memory_order_release); - revoke_weak_ptrs(); - } - - void revoke_weak_ptrs() - { - if (auto link = move(m_link)) - link->revoke(); - } - -private: - mutable LockRefPtr m_link; - Atomic m_being_destroyed { false }; -}; - -} - -using AK::LockWeakable; diff --git a/Kernel/Library/MiniStdLib.cpp b/Kernel/Library/MiniStdLib.cpp deleted file mode 100644 index 023f9f1155d..00000000000 --- a/Kernel/Library/MiniStdLib.cpp +++ /dev/null @@ -1,148 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include - -extern "C" { - -void* memcpy(void* dest_ptr, void const* src_ptr, size_t n) -{ -#if ARCH(X86_64) - size_t dest = (size_t)dest_ptr; - size_t src = (size_t)src_ptr; - // FIXME: Support starting at an unaligned address. - if (!(dest & 0x3) && !(src & 0x3) && n >= 12) { - size_t size_ts = n / sizeof(size_t); - asm volatile( - "rep movsq\n" - : "=S"(src), "=D"(dest) - : "S"(src), "D"(dest), "c"(size_ts) - : "memory"); - n -= size_ts * sizeof(size_t); - if (n == 0) - return dest_ptr; - } - asm volatile( - "rep movsb\n" ::"S"(src), "D"(dest), "c"(n) - : "memory"); -#else - u8* pd = (u8*)dest_ptr; - u8 const* ps = (u8 const*)src_ptr; - for (; n--;) - *pd++ = *ps++; -#endif - return dest_ptr; -} - -void* memmove(void* dest, void const* src, size_t n) -{ - if (dest < src) - return memcpy(dest, src, n); - - u8* pd = (u8*)dest; - u8 const* ps = (u8 const*)src; - for (pd += n, ps += n; n--;) - *--pd = *--ps; - return dest; -} - -void* memset(void* dest_ptr, int c, size_t n) -{ -#if ARCH(X86_64) - size_t dest = (size_t)dest_ptr; - // FIXME: Support starting at an unaligned address. - if (!(dest & 0x3) && n >= 12) { - size_t size_ts = n / sizeof(size_t); - size_t expanded_c = explode_byte((u8)c); - asm volatile( - "rep stosq\n" - : "=D"(dest) - : "D"(dest), "c"(size_ts), "a"(expanded_c) - : "memory"); - n -= size_ts * sizeof(size_t); - if (n == 0) - return dest_ptr; - } - asm volatile( - "rep stosb\n" - : "=D"(dest), "=c"(n) - : "0"(dest), "1"(n), "a"(c) - : "memory"); -#else - u8* pd = (u8*)dest_ptr; - for (; n--;) - *pd++ = c; -#endif - return dest_ptr; -} - -size_t strlen(char const* str) -{ - size_t len = 0; - while (*(str++)) - ++len; - return len; -} - -size_t strnlen(char const* str, size_t maxlen) -{ - size_t len = 0; - for (; len < maxlen && *str; str++) - len++; - return len; -} - -int strcmp(char const* s1, char const* s2) -{ - for (; *s1 == *s2; ++s1, ++s2) { - if (*s1 == 0) - return 0; - } - return *(u8 const*)s1 < *(u8 const*)s2 ? -1 : 1; -} - -int memcmp(void const* v1, void const* v2, size_t n) -{ - auto const* s1 = (u8 const*)v1; - auto const* s2 = (u8 const*)v2; - while (n-- > 0) { - if (*s1++ != *s2++) - return s1[-1] < s2[-1] ? -1 : 1; - } - return 0; -} - -int strncmp(char const* s1, char const* s2, size_t n) -{ - if (!n) - return 0; - do { - if (*s1 != *s2++) - return *(unsigned char const*)s1 - *(unsigned char const*)--s2; - if (*s1++ == 0) - break; - } while (--n); - return 0; -} - -char* strstr(char const* haystack, char const* needle) -{ - char nch; - char hch; - - if ((nch = *needle++) != 0) { - size_t len = strlen(needle); - do { - do { - if ((hch = *haystack++) == 0) - return nullptr; - } while (hch != nch); - } while (strncmp(haystack, needle, len) != 0); - --haystack; - } - return const_cast(haystack); -} -} diff --git a/Kernel/Library/NonnullLockRefPtr.h b/Kernel/Library/NonnullLockRefPtr.h deleted file mode 100644 index d6d1442b661..00000000000 --- a/Kernel/Library/NonnullLockRefPtr.h +++ /dev/null @@ -1,340 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include -#include -#ifdef KERNEL -# include -# include -#endif - -#define NONNULLLOCKREFPTR_SCRUB_BYTE 0xa1 - -namespace AK { - -template -class LockRefPtr; - -template -class [[nodiscard]] NonnullLockRefPtr { - template - friend class LockRefPtr; - template - friend class NonnullLockRefPtr; - template - friend class LockWeakPtr; - -public: - using ElementType = T; - - enum AdoptTag { Adopt }; - - ALWAYS_INLINE NonnullLockRefPtr(T const& object) - : m_bits((FlatPtr)&object) - { - VERIFY(!(m_bits & 1)); - const_cast(object).ref(); - } - template - ALWAYS_INLINE NonnullLockRefPtr(U const& object) - requires(IsConvertible) - : m_bits((FlatPtr) static_cast(&object)) - { - VERIFY(!(m_bits & 1)); - const_cast(static_cast(object)).ref(); - } - ALWAYS_INLINE NonnullLockRefPtr(AdoptTag, T& object) - : m_bits((FlatPtr)&object) - { - VERIFY(!(m_bits & 1)); - } - ALWAYS_INLINE NonnullLockRefPtr(NonnullLockRefPtr&& other) - : m_bits((FlatPtr)&other.leak_ref()) - { - VERIFY(!(m_bits & 1)); - } - template - ALWAYS_INLINE NonnullLockRefPtr(NonnullLockRefPtr&& other) - requires(IsConvertible) - : m_bits((FlatPtr)&other.leak_ref()) - { - VERIFY(!(m_bits & 1)); - } - ALWAYS_INLINE NonnullLockRefPtr(NonnullLockRefPtr const& other) - : m_bits((FlatPtr)other.add_ref()) - { - VERIFY(!(m_bits & 1)); - } - template - ALWAYS_INLINE NonnullLockRefPtr(NonnullLockRefPtr const& other) - requires(IsConvertible) - : m_bits((FlatPtr)other.add_ref()) - { - VERIFY(!(m_bits & 1)); - } - ALWAYS_INLINE ~NonnullLockRefPtr() - { - assign(nullptr); -#ifdef SANITIZE_PTRS - m_bits.store(explode_byte(NONNULLLOCKREFPTR_SCRUB_BYTE), AK::MemoryOrder::memory_order_relaxed); -#endif - } - - template - NonnullLockRefPtr(OwnPtr const&) = delete; - template - NonnullLockRefPtr& operator=(OwnPtr const&) = delete; - - template - NonnullLockRefPtr(LockRefPtr const&) = delete; - template - NonnullLockRefPtr& operator=(LockRefPtr const&) = delete; - NonnullLockRefPtr(LockRefPtr const&) = delete; - NonnullLockRefPtr& operator=(LockRefPtr const&) = delete; - - NonnullLockRefPtr& operator=(NonnullLockRefPtr const& other) - { - if (this != &other) - assign(other.add_ref()); - return *this; - } - - template - NonnullLockRefPtr& operator=(NonnullLockRefPtr const& other) - requires(IsConvertible) - { - assign(other.add_ref()); - return *this; - } - - ALWAYS_INLINE NonnullLockRefPtr& operator=(NonnullLockRefPtr&& other) - { - if (this != &other) - assign(&other.leak_ref()); - return *this; - } - - template - NonnullLockRefPtr& operator=(NonnullLockRefPtr&& other) - requires(IsConvertible) - { - assign(&other.leak_ref()); - return *this; - } - - NonnullLockRefPtr& operator=(T const& object) - { - const_cast(object).ref(); - assign(const_cast(&object)); - return *this; - } - - [[nodiscard]] ALWAYS_INLINE T& leak_ref() - { - T* ptr = exchange(nullptr); - VERIFY(ptr); - return *ptr; - } - - ALWAYS_INLINE RETURNS_NONNULL T* ptr() - { - return as_nonnull_ptr(); - } - ALWAYS_INLINE RETURNS_NONNULL T const* ptr() const - { - return as_nonnull_ptr(); - } - - ALWAYS_INLINE RETURNS_NONNULL T* operator->() - { - return as_nonnull_ptr(); - } - ALWAYS_INLINE RETURNS_NONNULL T const* operator->() const - { - return as_nonnull_ptr(); - } - - ALWAYS_INLINE T& operator*() - { - return *as_nonnull_ptr(); - } - ALWAYS_INLINE T const& operator*() const - { - return *as_nonnull_ptr(); - } - - ALWAYS_INLINE RETURNS_NONNULL operator T*() - { - return as_nonnull_ptr(); - } - ALWAYS_INLINE RETURNS_NONNULL operator T const*() const - { - return as_nonnull_ptr(); - } - - ALWAYS_INLINE operator T&() - { - return *as_nonnull_ptr(); - } - ALWAYS_INLINE operator T const&() const - { - return *as_nonnull_ptr(); - } - - operator bool() const = delete; - bool operator!() const = delete; - - void swap(NonnullLockRefPtr& other) - { - if (this == &other) - return; - - // NOTE: swap is not atomic! - T* other_ptr = other.exchange(nullptr); - T* ptr = exchange(other_ptr); - other.exchange(ptr); - } - - template - void swap(NonnullLockRefPtr& other) - requires(IsConvertible) - { - // NOTE: swap is not atomic! - U* other_ptr = other.exchange(nullptr); - T* ptr = exchange(other_ptr); - other.exchange(ptr); - } - -private: - NonnullLockRefPtr() = delete; - - ALWAYS_INLINE T* as_ptr() const - { - return (T*)(m_bits.load(AK::MemoryOrder::memory_order_relaxed) & ~(FlatPtr)1); - } - - ALWAYS_INLINE RETURNS_NONNULL T* as_nonnull_ptr() const - { - T* ptr = (T*)(m_bits.load(AK::MemoryOrder::memory_order_relaxed) & ~(FlatPtr)1); - VERIFY(ptr); - return ptr; - } - - template - void do_while_locked(F f) const - { -#ifdef KERNEL - // We don't want to be pre-empted while we have the lock bit set - Kernel::ScopedCritical critical; -#endif - FlatPtr bits; - for (;;) { - bits = m_bits.fetch_or(1, AK::MemoryOrder::memory_order_acq_rel); - if (!(bits & 1)) - break; -#ifdef KERNEL - Kernel::Processor::wait_check(); -#endif - } - VERIFY(!(bits & 1)); - f((T*)bits); - m_bits.store(bits, AK::MemoryOrder::memory_order_release); - } - - ALWAYS_INLINE void assign(T* new_ptr) - { - T* prev_ptr = exchange(new_ptr); - unref_if_not_null(prev_ptr); - } - - ALWAYS_INLINE T* exchange(T* new_ptr) - { - VERIFY(!((FlatPtr)new_ptr & 1)); -#ifdef KERNEL - // We don't want to be pre-empted while we have the lock bit set - Kernel::ScopedCritical critical; -#endif - // Only exchange while not locked - FlatPtr expected = m_bits.load(AK::MemoryOrder::memory_order_relaxed); - for (;;) { - expected &= ~(FlatPtr)1; // only if lock bit is not set - if (m_bits.compare_exchange_strong(expected, (FlatPtr)new_ptr, AK::MemoryOrder::memory_order_acq_rel)) - break; -#ifdef KERNEL - Kernel::Processor::wait_check(); -#endif - } - VERIFY(!(expected & 1)); - return (T*)expected; - } - - T* add_ref() const - { -#ifdef KERNEL - // We don't want to be pre-empted while we have the lock bit set - Kernel::ScopedCritical critical; -#endif - // Lock the pointer - FlatPtr expected = m_bits.load(AK::MemoryOrder::memory_order_relaxed); - for (;;) { - expected &= ~(FlatPtr)1; // only if lock bit is not set - if (m_bits.compare_exchange_strong(expected, expected | 1, AK::MemoryOrder::memory_order_acq_rel)) - break; -#ifdef KERNEL - Kernel::Processor::wait_check(); -#endif - } - - // Add a reference now that we locked the pointer - ref_if_not_null((T*)expected); - - // Unlock the pointer again - m_bits.store(expected, AK::MemoryOrder::memory_order_release); - return (T*)expected; - } - - mutable Atomic m_bits { 0 }; -}; - -template -inline NonnullLockRefPtr adopt_lock_ref(T& object) -{ - return NonnullLockRefPtr(NonnullLockRefPtr::Adopt, object); -} - -template -struct Formatter> : Formatter { - ErrorOr format(FormatBuilder& builder, NonnullLockRefPtr const& value) - { - return Formatter::format(builder, value.ptr()); - } -}; - -template -inline void swap(NonnullLockRefPtr& a, NonnullLockRefPtr& b) -requires(IsConvertible) -{ - a.swap(b); -} - -} - -template -struct Traits> : public DefaultTraits> { - using PeekType = T*; - using ConstPeekType = T const*; - static unsigned hash(NonnullLockRefPtr const& p) { return ptr_hash(p.ptr()); } - static bool equals(NonnullLockRefPtr const& a, NonnullLockRefPtr const& b) { return a.ptr() == b.ptr(); } -}; - -using AK::adopt_lock_ref; -using AK::NonnullLockRefPtr; diff --git a/Kernel/Library/Panic.cpp b/Kernel/Library/Panic.cpp deleted file mode 100644 index 3f3dec925c0..00000000000 --- a/Kernel/Library/Panic.cpp +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright (c) 2021, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#if ARCH(X86_64) -# include -#elif ARCH(AARCH64) -# include -#elif ARCH(RISCV64) -# include -#endif -#include -#include -#include -#include - -namespace Kernel { - -[[noreturn]] static void __shutdown() -{ -#if ARCH(X86_64) - qemu_shutdown(); - virtualbox_shutdown(); -#elif ARCH(AARCH64) - RPi::Watchdog::the().system_shutdown(); -#elif ARCH(RISCV64) - auto ret = SBI::SystemReset::system_reset(SBI::SystemReset::ResetType::Shutdown, SBI::SystemReset::ResetReason::SystemFailure); - dbgln("SBI: Failed to shut down: {}", ret); - dbgln("SBI: Attempting to shut down using the legacy extension..."); - SBI::Legacy::shutdown(); -#endif - // Note: If we failed to invoke platform shutdown, we need to halt afterwards - // to ensure no further execution on any CPU still happens. - Processor::halt(); -} - -void __panic(char const* file, unsigned int line, char const* function) -{ - // Avoid lock ranking checks on crashing paths, just try to get some debugging messages out. - auto* thread = Thread::current(); - if (thread) - thread->set_crashing(); - - critical_dmesgln("at {}:{} in {}", file, line, function); - dump_backtrace(PrintToScreen::Yes); - if (!CommandLine::was_initialized()) - Processor::halt(); - switch (kernel_command_line().panic_mode()) { - case PanicMode::Shutdown: - __shutdown(); - case PanicMode::Halt: - [[fallthrough]]; - default: - Processor::halt(); - } -} -} diff --git a/Kernel/Library/Panic.h b/Kernel/Library/Panic.h deleted file mode 100644 index 0829964026f..00000000000 --- a/Kernel/Library/Panic.h +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Copyright (c) 2021, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include - -namespace Kernel { - -[[noreturn]] void __panic(char const* file, unsigned int line, char const* function); - -#define PANIC(...) \ - do { \ - critical_dmesgln("KERNEL PANIC! :^("); \ - critical_dmesgln(__VA_ARGS__); \ - __panic(__FILE__, __LINE__, __PRETTY_FUNCTION__); \ - } while (0) - -} diff --git a/Kernel/Library/ScopedCritical.cpp b/Kernel/Library/ScopedCritical.cpp deleted file mode 100644 index 164a036233a..00000000000 --- a/Kernel/Library/ScopedCritical.cpp +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright (c) 2018-2021, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include - -#include - -namespace Kernel { - -ScopedCritical::ScopedCritical() -{ - enter(); -} - -ScopedCritical::~ScopedCritical() -{ - if (m_valid) - leave(); -} - -ScopedCritical::ScopedCritical(ScopedCritical&& from) - : m_valid(exchange(from.m_valid, false)) -{ -} - -ScopedCritical& ScopedCritical::operator=(ScopedCritical&& from) -{ - if (&from != this) { - m_valid = exchange(from.m_valid, false); - } - return *this; -} - -void ScopedCritical::leave() -{ - VERIFY(m_valid); - m_valid = false; - Processor::leave_critical(); -} - -void ScopedCritical::enter() -{ - VERIFY(!m_valid); - m_valid = true; - Processor::enter_critical(); -} - -} diff --git a/Kernel/Library/ScopedCritical.h b/Kernel/Library/ScopedCritical.h deleted file mode 100644 index 678069e4f6f..00000000000 --- a/Kernel/Library/ScopedCritical.h +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright (c) 2018-2021, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include - -namespace Kernel { - -class ScopedCritical { - AK_MAKE_NONCOPYABLE(ScopedCritical); - -public: - ScopedCritical(); - ~ScopedCritical(); - ScopedCritical(ScopedCritical&& from); - - ScopedCritical& operator=(ScopedCritical&& from); - - void leave(); - void enter(); - -private: - bool m_valid { false }; -}; - -} diff --git a/Kernel/Library/StdLib.cpp b/Kernel/Library/StdLib.cpp deleted file mode 100644 index 31a8b756864..00000000000 --- a/Kernel/Library/StdLib.cpp +++ /dev/null @@ -1,255 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include -#include -#include - -ErrorOr> try_copy_kstring_from_user(Userspace user_str, size_t user_str_size) -{ - bool is_user = Kernel::Memory::is_user_range(user_str.vaddr(), user_str_size); - if (!is_user) - return EFAULT; - Kernel::SmapDisabler disabler; - void* fault_at; - ssize_t length = Kernel::safe_strnlen(user_str.unsafe_userspace_ptr(), user_str_size, fault_at); - if (length < 0) { - dbgln("copy_kstring_from_user({:p}, {}) failed at {} (strnlen)", static_cast(user_str.unsafe_userspace_ptr()), user_str_size, VirtualAddress { fault_at }); - return EFAULT; - } - char* buffer; - auto new_string = TRY(Kernel::KString::try_create_uninitialized(length, buffer)); - - buffer[length] = '\0'; - - if (length == 0) - return new_string; - - if (!Kernel::safe_memcpy(buffer, user_str.unsafe_userspace_ptr(), (size_t)length, fault_at)) { - dbgln("copy_kstring_from_user({:p}, {}) failed at {} (memcpy)", static_cast(user_str.unsafe_userspace_ptr()), user_str_size, VirtualAddress { fault_at }); - return EFAULT; - } - return new_string; -} - -ErrorOr copy_time_from_user(timespec const* ts_user) -{ - timespec ts {}; - TRY(copy_from_user(&ts, ts_user, sizeof(timespec))); - return Duration::from_timespec(ts); -} - -ErrorOr copy_time_from_user(timeval const* tv_user) -{ - timeval tv {}; - TRY(copy_from_user(&tv, tv_user, sizeof(timeval))); - return Duration::from_timeval(tv); -} - -template<> -ErrorOr copy_time_from_user(Userspace src) { return copy_time_from_user(src.unsafe_userspace_ptr()); } -template<> -ErrorOr copy_time_from_user(Userspace src) { return copy_time_from_user(src.unsafe_userspace_ptr()); } -template<> -ErrorOr copy_time_from_user(Userspace src) { return copy_time_from_user(src.unsafe_userspace_ptr()); } -template<> -ErrorOr copy_time_from_user(Userspace src) { return copy_time_from_user(src.unsafe_userspace_ptr()); } - -Optional user_atomic_fetch_add_relaxed(u32 volatile* var, u32 val) -{ - if (FlatPtr(var) & 3) - return {}; // not aligned! - bool is_user = Kernel::Memory::is_user_range(VirtualAddress(FlatPtr(var)), sizeof(*var)); - if (!is_user) - return {}; - Kernel::SmapDisabler disabler; - return Kernel::safe_atomic_fetch_add_relaxed(var, val); -} - -Optional user_atomic_exchange_relaxed(u32 volatile* var, u32 val) -{ - if (FlatPtr(var) & 3) - return {}; // not aligned! - bool is_user = Kernel::Memory::is_user_range(VirtualAddress(FlatPtr(var)), sizeof(*var)); - if (!is_user) - return {}; - Kernel::SmapDisabler disabler; - return Kernel::safe_atomic_exchange_relaxed(var, val); -} - -Optional user_atomic_load_relaxed(u32 volatile* var) -{ - if (FlatPtr(var) & 3) - return {}; // not aligned! - bool is_user = Kernel::Memory::is_user_range(VirtualAddress(FlatPtr(var)), sizeof(*var)); - if (!is_user) - return {}; - Kernel::SmapDisabler disabler; - return Kernel::safe_atomic_load_relaxed(var); -} - -bool user_atomic_store_relaxed(u32 volatile* var, u32 val) -{ - if (FlatPtr(var) & 3) - return false; // not aligned! - bool is_user = Kernel::Memory::is_user_range(VirtualAddress(FlatPtr(var)), sizeof(*var)); - if (!is_user) - return false; - Kernel::SmapDisabler disabler; - return Kernel::safe_atomic_store_relaxed(var, val); -} - -Optional user_atomic_compare_exchange_relaxed(u32 volatile* var, u32& expected, u32 val) -{ - if (FlatPtr(var) & 3) - return {}; // not aligned! - VERIFY(!Kernel::Memory::is_user_range(VirtualAddress(&expected), sizeof(expected))); - bool is_user = Kernel::Memory::is_user_range(VirtualAddress(FlatPtr(var)), sizeof(*var)); - if (!is_user) - return {}; - Kernel::SmapDisabler disabler; - return Kernel::safe_atomic_compare_exchange_relaxed(var, expected, val); -} - -Optional user_atomic_fetch_and_relaxed(u32 volatile* var, u32 val) -{ - if (FlatPtr(var) & 3) - return {}; // not aligned! - bool is_user = Kernel::Memory::is_user_range(VirtualAddress(FlatPtr(var)), sizeof(*var)); - if (!is_user) - return {}; - Kernel::SmapDisabler disabler; - return Kernel::safe_atomic_fetch_and_relaxed(var, val); -} - -Optional user_atomic_fetch_and_not_relaxed(u32 volatile* var, u32 val) -{ - if (FlatPtr(var) & 3) - return {}; // not aligned! - bool is_user = Kernel::Memory::is_user_range(VirtualAddress(FlatPtr(var)), sizeof(*var)); - if (!is_user) - return {}; - Kernel::SmapDisabler disabler; - return Kernel::safe_atomic_fetch_and_not_relaxed(var, val); -} - -Optional user_atomic_fetch_or_relaxed(u32 volatile* var, u32 val) -{ - if (FlatPtr(var) & 3) - return {}; // not aligned! - bool is_user = Kernel::Memory::is_user_range(VirtualAddress(FlatPtr(var)), sizeof(*var)); - if (!is_user) - return {}; - Kernel::SmapDisabler disabler; - return Kernel::safe_atomic_fetch_or_relaxed(var, val); -} - -Optional user_atomic_fetch_xor_relaxed(u32 volatile* var, u32 val) -{ - if (FlatPtr(var) & 3) - return {}; // not aligned! - bool is_user = Kernel::Memory::is_user_range(VirtualAddress(FlatPtr(var)), sizeof(*var)); - if (!is_user) - return {}; - Kernel::SmapDisabler disabler; - return Kernel::safe_atomic_fetch_xor_relaxed(var, val); -} - -ErrorOr copy_to_user(void* dest_ptr, void const* src_ptr, size_t n) -{ - if (!Kernel::Memory::is_user_range(VirtualAddress(dest_ptr), n)) - return EFAULT; - VERIFY(!Kernel::Memory::is_user_range(VirtualAddress(src_ptr), n)); - Kernel::SmapDisabler disabler; - void* fault_at; - if (!Kernel::safe_memcpy(dest_ptr, src_ptr, n, fault_at)) { - VERIFY(VirtualAddress(fault_at) >= VirtualAddress(dest_ptr) && VirtualAddress(fault_at) <= VirtualAddress((FlatPtr)dest_ptr + n)); - dbgln("copy_to_user({:p}, {:p}, {}) failed at {}", dest_ptr, src_ptr, n, VirtualAddress { fault_at }); - return EFAULT; - } - return {}; -} - -ErrorOr copy_from_user(void* dest_ptr, void const* src_ptr, size_t n) -{ - if (!Kernel::Memory::is_user_range(VirtualAddress(src_ptr), n)) - return EFAULT; - VERIFY(!Kernel::Memory::is_user_range(VirtualAddress(dest_ptr), n)); - Kernel::SmapDisabler disabler; - void* fault_at; - if (!Kernel::safe_memcpy(dest_ptr, src_ptr, n, fault_at)) { - VERIFY(VirtualAddress(fault_at) >= VirtualAddress(src_ptr) && VirtualAddress(fault_at) <= VirtualAddress((FlatPtr)src_ptr + n)); - dbgln("copy_from_user({:p}, {:p}, {}) failed at {}", dest_ptr, src_ptr, n, VirtualAddress { fault_at }); - return EFAULT; - } - return {}; -} - -ErrorOr memset_user(void* dest_ptr, int c, size_t n) -{ - bool is_user = Kernel::Memory::is_user_range(VirtualAddress(dest_ptr), n); - if (!is_user) - return EFAULT; - Kernel::SmapDisabler disabler; - void* fault_at; - if (!Kernel::safe_memset(dest_ptr, c, n, fault_at)) { - dbgln("memset_user({:p}, {}, {}) failed at {}", dest_ptr, c, n, VirtualAddress { fault_at }); - return EFAULT; - } - return {}; -} - -#if defined(AK_COMPILER_CLANG) && defined(ENABLE_KERNEL_LTO) -// Due to a chicken-and-egg situation, certain linker-defined symbols that are added on-demand (like the GOT) -// need to be present before LTO bitcode files are compiled. And since we don't link to any native object files, -// the linker does not know that _GLOBAL_OFFSET_TABLE_ is needed, so it doesn't define it, so linking as a PIE fails. -// See https://bugs.llvm.org/show_bug.cgi?id=39634 -FlatPtr missing_got_workaround() -{ - extern FlatPtr volatile _GLOBAL_OFFSET_TABLE_; - return _GLOBAL_OFFSET_TABLE_; -} -#endif - -extern "C" { - -void const* memmem(void const* haystack, size_t haystack_length, void const* needle, size_t needle_length) -{ - return AK::memmem(haystack, haystack_length, needle, needle_length); -} - -// Functions that are automatically called by the C++ compiler. -// Declare them first, to tell the silly compiler that they are indeed being used. -[[noreturn]] void __stack_chk_fail() __attribute__((used)); -[[noreturn]] void __stack_chk_fail_local() __attribute__((used)); -extern "C" int __cxa_atexit(void (*)(void*), void*, void*); -[[noreturn]] void __cxa_pure_virtual(); - -[[noreturn]] void __stack_chk_fail() -{ - VERIFY_NOT_REACHED(); -} - -[[noreturn]] void __stack_chk_fail_local() -{ - VERIFY_NOT_REACHED(); -} - -extern "C" int __cxa_atexit(void (*)(void*), void*, void*) -{ - VERIFY_NOT_REACHED(); - return 0; -} - -[[noreturn]] void __cxa_pure_virtual() -{ - VERIFY_NOT_REACHED(); -} -} diff --git a/Kernel/Library/StdLib.h b/Kernel/Library/StdLib.h deleted file mode 100644 index 9887400068e..00000000000 --- a/Kernel/Library/StdLib.h +++ /dev/null @@ -1,214 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * Copyright (c) 2023, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -ErrorOr> try_copy_kstring_from_user(Userspace, size_t); - -template -ErrorOr try_copy_string_from_user_into_fixed_string_buffer(Userspace user_str, FixedStringBuffer& buffer, size_t user_str_size) -{ - if (user_str_size > Size) - return E2BIG; - TRY(buffer.copy_characters_from_user(user_str, user_str_size)); - return {}; -} - -template -ErrorOr try_copy_name_from_user_into_fixed_string_buffer(Userspace user_str, FixedStringBuffer& buffer, size_t user_str_size) -{ - if (user_str_size > Size) - return ENAMETOOLONG; - TRY(buffer.copy_characters_from_user(user_str, user_str_size)); - return {}; -} - -ErrorOr copy_time_from_user(timespec const*); -ErrorOr copy_time_from_user(timeval const*); -template -ErrorOr copy_time_from_user(Userspace); - -[[nodiscard]] Optional user_atomic_fetch_add_relaxed(u32 volatile* var, u32 val); -[[nodiscard]] Optional user_atomic_exchange_relaxed(u32 volatile* var, u32 val); -[[nodiscard]] Optional user_atomic_load_relaxed(u32 volatile* var); -[[nodiscard]] bool user_atomic_store_relaxed(u32 volatile* var, u32 val); -[[nodiscard]] Optional user_atomic_compare_exchange_relaxed(u32 volatile* var, u32& expected, u32 val); -[[nodiscard]] Optional user_atomic_fetch_and_relaxed(u32 volatile* var, u32 val); -[[nodiscard]] Optional user_atomic_fetch_and_not_relaxed(u32 volatile* var, u32 val); -[[nodiscard]] Optional user_atomic_fetch_or_relaxed(u32 volatile* var, u32 val); -[[nodiscard]] Optional user_atomic_fetch_xor_relaxed(u32 volatile* var, u32 val); - -ErrorOr copy_to_user(void*, void const*, size_t); -ErrorOr copy_from_user(void*, void const*, size_t); -ErrorOr memset_user(void*, int, size_t); - -extern "C" { - -void* memcpy(void*, void const*, size_t); -[[nodiscard]] int strncmp(char const* s1, char const* s2, size_t n); -[[nodiscard]] char* strstr(char const* haystack, char const* needle); -[[nodiscard]] int strcmp(char const*, char const*); -[[nodiscard]] size_t strlen(char const*); -[[nodiscard]] size_t strnlen(char const*, size_t); -void* memset(void*, int, size_t); -[[nodiscard]] int memcmp(void const*, void const*, size_t); -void* memmove(void* dest, void const* src, size_t n); -void const* memmem(void const* haystack, size_t, void const* needle, size_t); - -[[nodiscard]] inline u16 ntohs(u16 w) { return (w & 0xff) << 8 | ((w >> 8) & 0xff); } -[[nodiscard]] inline u16 htons(u16 w) { return (w & 0xff) << 8 | ((w >> 8) & 0xff); } -} - -template -[[nodiscard]] inline ErrorOr copy_from_user(T* dest, T const* src) -{ - static_assert(IsTriviallyCopyable); - return copy_from_user(dest, src, sizeof(T)); -} - -template -[[nodiscard]] inline ErrorOr copy_to_user(T* dest, T const* src) -{ - static_assert(IsTriviallyCopyable); - return copy_to_user(dest, src, sizeof(T)); -} - -template -[[nodiscard]] inline ErrorOr copy_from_user(T* dest, Userspace src) -{ - static_assert(IsTriviallyCopyable); - return copy_from_user(dest, src.unsafe_userspace_ptr(), sizeof(T)); -} - -template -[[nodiscard]] inline ErrorOr copy_from_user(T* dest, Userspace src) -{ - static_assert(IsTriviallyCopyable); - return copy_from_user(dest, src.unsafe_userspace_ptr(), sizeof(T)); -} - -#define DEPRECATE_COPY_FROM_USER_TYPE(T, REPLACEMENT) \ - template<> \ - [[nodiscard]] inline __attribute__((deprecated("use " #REPLACEMENT " instead"))) ErrorOr copy_from_user(T*, const T*) \ - { \ - VERIFY_NOT_REACHED(); \ - } \ - template<> \ - [[nodiscard]] inline __attribute__((deprecated("use " #REPLACEMENT " instead"))) ErrorOr copy_from_user(T*, Userspace) \ - { \ - VERIFY_NOT_REACHED(); \ - } \ - template<> \ - [[nodiscard]] inline __attribute__((deprecated("use " #REPLACEMENT " instead"))) ErrorOr copy_from_user(T*, Userspace) \ - { \ - VERIFY_NOT_REACHED(); \ - } - -DEPRECATE_COPY_FROM_USER_TYPE(timespec, copy_time_from_user) -DEPRECATE_COPY_FROM_USER_TYPE(timeval, copy_time_from_user) - -template -[[nodiscard]] inline ErrorOr copy_to_user(Userspace dest, T const* src) -{ - static_assert(IsTriviallyCopyable); - return copy_to_user(dest.unsafe_userspace_ptr(), src, sizeof(T)); -} - -template -[[nodiscard]] inline ErrorOr copy_to_user(Userspace dest, void const* src, size_t size) -{ - static_assert(IsTriviallyCopyable); - return copy_to_user(dest.unsafe_userspace_ptr(), src, size); -} - -template -[[nodiscard]] inline ErrorOr copy_from_user(void* dest, Userspace src, size_t size) -{ - static_assert(IsTriviallyCopyable); - return copy_from_user(dest, src.unsafe_userspace_ptr(), size); -} - -template -[[nodiscard]] inline ErrorOr copy_n_from_user(T* dest, T const* src, size_t count) -{ - static_assert(IsTriviallyCopyable); - Checked size = sizeof(T); - size *= count; - if (size.has_overflow()) - return EOVERFLOW; - return copy_from_user(dest, src, size.value()); -} - -template -[[nodiscard]] inline ErrorOr copy_n_to_user(T* dest, T const* src, size_t count) -{ - static_assert(IsTriviallyCopyable); - Checked size = sizeof(T); - size *= count; - if (size.has_overflow()) - return EOVERFLOW; - return copy_to_user(dest, src, size.value()); -} - -template -[[nodiscard]] inline ErrorOr copy_n_from_user(T* dest, Userspace src, size_t count) -{ - static_assert(IsTriviallyCopyable); - Checked size = sizeof(T); - size *= count; - if (size.has_overflow()) - return EOVERFLOW; - return copy_from_user(dest, src.unsafe_userspace_ptr(), size.value()); -} - -template -[[nodiscard]] inline ErrorOr copy_n_to_user(Userspace dest, T const* src, size_t count) -{ - static_assert(IsTriviallyCopyable); - Checked size = sizeof(T); - size *= count; - if (size.has_overflow()) - return EOVERFLOW; - return copy_to_user(dest.unsafe_userspace_ptr(), src, size.value()); -} - -template -inline ErrorOr copy_typed_from_user(Userspace user_data) -{ - T data {}; - TRY(copy_from_user(&data, user_data)); - return data; -} - -template -inline ErrorOr copy_typed_from_user(Userspace user_data) -{ - T data {}; - TRY(copy_from_user(&data, user_data)); - return data; -} - -template -ErrorOr copy_fixed_string_buffer_including_null_char_to_user(Userspace dest, size_t buffer_size, FixedStringBuffer const& buffer) -{ - FixedStringBuffer name_with_null_char {}; - name_with_null_char.store_characters(buffer.representable_view()); - if (name_with_null_char.stored_length() + 1 > buffer_size) - return ENAMETOOLONG; - auto name_with_null_char_view = name_with_null_char.span_view_ensuring_ending_null_char(); - return copy_to_user(dest, name_with_null_char_view.data(), name_with_null_char_view.size()); -} diff --git a/Kernel/Library/UserOrKernelBuffer.cpp b/Kernel/Library/UserOrKernelBuffer.cpp deleted file mode 100644 index 0dcefa5a470..00000000000 --- a/Kernel/Library/UserOrKernelBuffer.cpp +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright (c) 2020, the SerenityOS developers. - * Copyright (c) 2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include - -namespace Kernel { - -bool UserOrKernelBuffer::is_kernel_buffer() const -{ - return !Memory::is_user_address(VirtualAddress(m_buffer)); -} - -ErrorOr> UserOrKernelBuffer::try_copy_into_kstring(size_t size) const -{ - if (!m_buffer) - return EINVAL; - if (Memory::is_user_address(VirtualAddress(m_buffer))) { - char* buffer; - auto kstring = TRY(KString::try_create_uninitialized(size, buffer)); - TRY(copy_from_user(buffer, m_buffer, size)); - return kstring; - } - - return KString::try_create(ReadonlyBytes { m_buffer, size }); -} - -ErrorOr UserOrKernelBuffer::write(void const* src, size_t offset, size_t len) -{ - if (!m_buffer) - return EFAULT; - - if (Memory::is_user_address(VirtualAddress(m_buffer))) - return copy_to_user(m_buffer + offset, src, len); - - memcpy(m_buffer + offset, src, len); - return {}; -} - -ErrorOr UserOrKernelBuffer::read(void* dest, size_t offset, size_t len) const -{ - if (!m_buffer) - return EFAULT; - - if (Memory::is_user_address(VirtualAddress(m_buffer))) - return copy_from_user(dest, m_buffer + offset, len); - - memcpy(dest, m_buffer + offset, len); - return {}; -} - -ErrorOr UserOrKernelBuffer::memset(int value, size_t offset, size_t len) -{ - if (!m_buffer) - return EFAULT; - - if (Memory::is_user_address(VirtualAddress(m_buffer))) - return memset_user(m_buffer + offset, value, len); - - ::memset(m_buffer + offset, value, len); - return {}; -} - -} diff --git a/Kernel/Library/UserOrKernelBuffer.h b/Kernel/Library/UserOrKernelBuffer.h deleted file mode 100644 index 41f9fd4dad2..00000000000 --- a/Kernel/Library/UserOrKernelBuffer.h +++ /dev/null @@ -1,165 +0,0 @@ -/* - * Copyright (c) 2020, the SerenityOS developers. - * Copyright (c) 2021, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include -#include - -namespace Kernel { - -class [[nodiscard]] UserOrKernelBuffer { -public: - UserOrKernelBuffer() = delete; - - static UserOrKernelBuffer for_kernel_buffer(u8* kernel_buffer) - { - VERIFY(!kernel_buffer || !Memory::is_user_address(VirtualAddress(kernel_buffer))); - return UserOrKernelBuffer(kernel_buffer); - } - - static ErrorOr for_user_buffer(u8* user_buffer, size_t size) - { - if (user_buffer && !Memory::is_user_range(VirtualAddress(user_buffer), size)) - return Error::from_errno(EFAULT); - return UserOrKernelBuffer(user_buffer); - } - - template - static ErrorOr for_user_buffer(UserspaceType userspace, size_t size) - { - if (!Memory::is_user_range(VirtualAddress(userspace.unsafe_userspace_ptr()), size)) - return Error::from_errno(EFAULT); - return UserOrKernelBuffer(const_cast((u8 const*)userspace.unsafe_userspace_ptr())); - } - - [[nodiscard]] bool is_kernel_buffer() const; - [[nodiscard]] void const* user_or_kernel_ptr() const { return m_buffer; } - - [[nodiscard]] UserOrKernelBuffer offset(size_t offset) const - { - if (!m_buffer) - return *this; - UserOrKernelBuffer offset_buffer = *this; - offset_buffer.m_buffer += offset; - VERIFY(offset_buffer.is_kernel_buffer() == is_kernel_buffer()); - return offset_buffer; - } - - ErrorOr> try_copy_into_kstring(size_t) const; - ErrorOr write(void const* src, size_t offset, size_t len); - ErrorOr write(void const* src, size_t len) - { - return write(src, 0, len); - } - ErrorOr write(ReadonlyBytes bytes) - { - return write(bytes.data(), bytes.size()); - } - - ErrorOr read(void* dest, size_t offset, size_t len) const; - ErrorOr read(void* dest, size_t len) const - { - return read(dest, 0, len); - } - - ErrorOr read(Bytes bytes) const - { - return read(bytes.data(), bytes.size()); - } - - ErrorOr memset(int value, size_t offset, size_t len); - ErrorOr memset(int value, size_t len) - { - return memset(value, 0, len); - } - - template - ErrorOr write_buffered(size_t offset, size_t len, F f) - { - if (!m_buffer) - return EFAULT; - if (is_kernel_buffer()) { - // We're transferring directly to a kernel buffer, bypass - Bytes bytes { m_buffer + offset, len }; - return f(bytes); - } - - // The purpose of using a buffer on the stack is that we can - // avoid a bunch of small (e.g. 1-byte) copy_to_user calls - u8 buffer[BUFFER_BYTES]; - size_t nwritten = 0; - while (nwritten < len) { - auto to_copy = min(sizeof(buffer), len - nwritten); - Bytes bytes { buffer, to_copy }; - ErrorOr copied_or_error = f(bytes); - if (copied_or_error.is_error()) - return copied_or_error.release_error(); - auto copied = copied_or_error.release_value(); - VERIFY(copied <= to_copy); - TRY(write(buffer, nwritten, copied)); - nwritten += copied; - if (copied < to_copy) - break; - } - return nwritten; - } - template - ErrorOr write_buffered(size_t len, F f) - { - return write_buffered(0, len, f); - } - - template - ErrorOr read_buffered(size_t offset, size_t len, F f) const - { - if (!m_buffer) - return EFAULT; - if (is_kernel_buffer()) { - // We're transferring directly from a kernel buffer, bypass - return f({ m_buffer + offset, len }); - } - - // The purpose of using a buffer on the stack is that we can - // avoid a bunch of small (e.g. 1-byte) copy_from_user calls - u8 buffer[BUFFER_BYTES]; - size_t nread = 0; - while (nread < len) { - auto to_copy = min(sizeof(buffer), len - nread); - TRY(read(buffer, nread, to_copy)); - ReadonlyBytes read_only_bytes { buffer, to_copy }; - ErrorOr copied_or_error = f(read_only_bytes); - if (copied_or_error.is_error()) - return copied_or_error.release_error(); - auto copied = copied_or_error.release_value(); - VERIFY(copied <= to_copy); - nread += copied; - if (copied < to_copy) - break; - } - return nread; - } - template - ErrorOr read_buffered(size_t len, F f) const - { - return read_buffered(0, len, f); - } - -private: - explicit UserOrKernelBuffer(u8* buffer) - : m_buffer(buffer) - { - } - - u8* m_buffer; -}; - -} diff --git a/Kernel/Locking/LockLocation.h b/Kernel/Locking/LockLocation.h deleted file mode 100644 index 91cb3af2041..00000000000 --- a/Kernel/Locking/LockLocation.h +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright (c) 2021, Brian Gianforcaro - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#if LOCK_DEBUG -# include -#endif - -// Abstract SourceLocation away from the kernel's locking API to avoid a -// significant amount of #ifdefs in Mutex / MutexLocker / etc. -// -// To do this we declare LockLocation to be a zero sized struct which will -// get optimized out during normal compilation. When LOCK_DEBUG is enabled, -// we forward the implementation to AK::SourceLocation and get rich debugging -// information for every caller. - -namespace Kernel { - -#if LOCK_DEBUG -using LockLocation = SourceLocation; -#else -struct LockLocation { - static constexpr LockLocation current() { return {}; } - -private: - constexpr LockLocation() = default; -}; -#endif - -} diff --git a/Kernel/Locking/LockMode.h b/Kernel/Locking/LockMode.h deleted file mode 100644 index 9430326313b..00000000000 --- a/Kernel/Locking/LockMode.h +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Copyright (c) 2020, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include - -namespace Kernel { - -enum class LockMode : u8 { - Unlocked, - Shared, - Exclusive -}; - -} diff --git a/Kernel/Locking/LockRank.cpp b/Kernel/Locking/LockRank.cpp deleted file mode 100644 index ccb12a6d5f7..00000000000 --- a/Kernel/Locking/LockRank.cpp +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright (c) 2021, Brian Gianforcaro - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include - -// Note: These stubs can't be in LockRank.h as that would create -// a cyclic dependency in the header include graph of the Kernel. - -namespace Kernel { - -void track_lock_acquire(LockRank rank) -{ - if constexpr (LOCK_RANK_ENFORCEMENT) { - auto* thread = Thread::current(); - if (thread && !thread->is_crashing()) - thread->track_lock_acquire(rank); - } -} - -void track_lock_release(LockRank rank) -{ - if constexpr (LOCK_RANK_ENFORCEMENT) { - auto* thread = Thread::current(); - if (thread && !thread->is_crashing()) - thread->track_lock_release(rank); - } -} - -} diff --git a/Kernel/Locking/LockRank.h b/Kernel/Locking/LockRank.h deleted file mode 100644 index 9aff2bf6c0d..00000000000 --- a/Kernel/Locking/LockRank.h +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright (c) 2021, Brian Gianforcaro - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include - -namespace Kernel { -// To catch bugs where locks are taken out of order, we annotate all locks -// in the kernel with a rank. The rank describes the order in which locks -// are allowed to be taken. If a lock is acquired, and it is of an incompatible -// rank with the lock held by the executing thread then the system can detect -// the lock order violation and respond appropriately (crash with error). -// -// A thread holding a lower ranked lock cannot acquire a lock of a greater or equal rank. -enum class LockRank : int { - // Special marker for locks which haven't been annotated yet. - // Note: This should be removed once all locks are annotated. - None = 0x000, - - // We need to be able to handle page faults from anywhere, so - // memory manager locks are our lowest rank lock. - MemoryManager = 0x001, - - Interrupts = 0x002, - - FileSystem = 0x004, - - Thread = 0x008, - - // Process locks are the highest rank, as they normally are taken - // first thing when processing syscalls. - Process = 0x010, -}; - -AK_ENUM_BITWISE_OPERATORS(LockRank); - -void track_lock_acquire(LockRank); -void track_lock_release(LockRank); -} diff --git a/Kernel/Locking/Mutex.cpp b/Kernel/Locking/Mutex.cpp deleted file mode 100644 index f6952eb62da..00000000000 --- a/Kernel/Locking/Mutex.cpp +++ /dev/null @@ -1,374 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * Copyright (c) 2022, Idan Horowitz - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include -#include -#include - -extern SetOnce g_not_in_early_boot; - -namespace Kernel { - -void Mutex::lock(Mode mode, [[maybe_unused]] LockLocation const& location) -{ - // NOTE: This may be called from an interrupt handler (not an IRQ handler) - // and also from within critical sections! - VERIFY(!Processor::current_in_irq()); - if constexpr (LOCK_IN_CRITICAL_DEBUG) { - // There are no interrupts enabled in early boot. - if (g_not_in_early_boot.was_set()) - VERIFY_INTERRUPTS_ENABLED(); - } - VERIFY(mode != Mode::Unlocked); - auto* current_thread = Thread::current(); - - SpinlockLocker lock(m_lock); - bool did_block = false; - Mode current_mode = m_mode; - switch (current_mode) { - case Mode::Unlocked: { - dbgln_if(LOCK_TRACE_DEBUG, "Mutex::lock @ ({}) {}: acquire {}, currently unlocked", this, m_name, mode_to_string(mode)); - m_mode = mode; - VERIFY(!m_holder); - VERIFY(m_shared_holders == 0); - if (mode == Mode::Exclusive) { - m_holder = bit_cast(current_thread); - } else { - VERIFY(mode == Mode::Shared); - ++m_shared_holders; -#if LOCK_SHARED_UPGRADE_DEBUG - m_shared_holders_map.set(bit_cast(current_thread), 1); -#endif - } - VERIFY(m_times_locked == 0); - m_times_locked++; - -#if LOCK_DEBUG - if (current_thread) { - current_thread->holding_lock(*this, 1, location); - } -#endif - return; - } - case Mode::Exclusive: { - VERIFY(m_holder); - if (m_holder != bit_cast(current_thread)) { - block(*current_thread, mode, lock, 1); - did_block = true; - // If we blocked then m_mode should have been updated to what we requested - VERIFY(m_mode == mode); - } - - if (m_mode == Mode::Exclusive) { - VERIFY(m_holder == bit_cast(current_thread)); - VERIFY(m_shared_holders == 0); - } else if (did_block && mode == Mode::Shared) { - // Only if we blocked trying to acquire a shared lock the lock would have been converted - VERIFY(!m_holder); - VERIFY(m_shared_holders > 0); - } - - if constexpr (LOCK_TRACE_DEBUG) { - if (mode == Mode::Exclusive) - dbgln("Mutex::lock @ {} ({}): acquire {}, currently exclusive, holding: {}", this, m_name, mode_to_string(mode), m_times_locked); - else - dbgln("Mutex::lock @ {} ({}): acquire exclusive (requested {}), currently exclusive, holding: {}", this, m_name, mode_to_string(mode), m_times_locked); - } - - VERIFY(m_times_locked > 0); - if (!did_block) { - // if we didn't block we must still be an exclusive lock - VERIFY(m_mode == Mode::Exclusive); - m_times_locked++; - } - -#if LOCK_DEBUG - current_thread->holding_lock(*this, 1, location); -#endif - return; - } - case Mode::Shared: { - VERIFY(m_behavior == MutexBehavior::Regular); - VERIFY(!m_holder); - if (mode == Mode::Exclusive) { - dbgln_if(LOCK_TRACE_DEBUG, "Mutex::lock @ {} ({}): blocking for exclusive access, currently shared, locks held {}", this, m_name, m_times_locked); -#if LOCK_SHARED_UPGRADE_DEBUG - VERIFY(m_shared_holders_map.size() != 1 || m_shared_holders_map.begin()->key != bit_cast(current_thread)); -#endif - // WARNING: The following block will deadlock if the current thread is the only shared locker of this Mutex - // and is asking to upgrade the lock to be exclusive without first releasing the shared lock. We have no - // allocation-free way to detect such a scenario, so if you suspect that this is the cause of your deadlock, - // try turning on LOCK_SHARED_UPGRADE_DEBUG. - block(*current_thread, mode, lock, 1); - did_block = true; - VERIFY(m_mode == mode); - } - - dbgln_if(LOCK_TRACE_DEBUG, "Mutex::lock @ {} ({}): acquire {}, currently shared, locks held {}", this, m_name, mode_to_string(mode), m_times_locked); - - VERIFY(m_times_locked > 0); - if (m_mode == Mode::Shared) { - VERIFY(!m_holder); - VERIFY(!did_block); - } else if (did_block) { - VERIFY(mode == Mode::Exclusive); - VERIFY(m_holder == bit_cast(current_thread)); - VERIFY(m_shared_holders == 0); - } - - if (!did_block) { - // if we didn't block we must still be a shared lock - VERIFY(m_mode == Mode::Shared); - m_times_locked++; - VERIFY(m_shared_holders > 0); - ++m_shared_holders; -#if LOCK_SHARED_UPGRADE_DEBUG - m_shared_holders_map.ensure(bit_cast(current_thread), [] { return 0; })++; -#endif - } - -#if LOCK_DEBUG - current_thread->holding_lock(*this, 1, location); -#endif - return; - } - default: - VERIFY_NOT_REACHED(); - } -} - -void Mutex::unlock() -{ - // NOTE: This may be called from an interrupt handler (not an IRQ handler) - // and also from within critical sections! - VERIFY(!Processor::current_in_irq()); - if constexpr (LOCK_IN_CRITICAL_DEBUG) { - // There are no interrupts enabled in early boot. - if (g_not_in_early_boot.was_set()) - VERIFY_INTERRUPTS_ENABLED(); - } - auto* current_thread = Thread::current(); - SpinlockLocker lock(m_lock); - Mode current_mode = m_mode; - if constexpr (LOCK_TRACE_DEBUG) { - if (current_mode == Mode::Shared) - dbgln("Mutex::unlock @ {} ({}): release {}, locks held: {}", this, m_name, mode_to_string(current_mode), m_times_locked); - else - dbgln("Mutex::unlock @ {} ({}): release {}, holding: {}", this, m_name, mode_to_string(current_mode), m_times_locked); - } - - VERIFY(current_mode != Mode::Unlocked); - - VERIFY(m_times_locked > 0); - m_times_locked--; - - switch (current_mode) { - case Mode::Exclusive: - VERIFY(m_holder == bit_cast(current_thread)); - VERIFY(m_shared_holders == 0); - if (m_times_locked == 0) - m_holder = 0; - break; - case Mode::Shared: { - VERIFY(!m_holder); - VERIFY(m_shared_holders > 0); - --m_shared_holders; -#if LOCK_SHARED_UPGRADE_DEBUG - auto it = m_shared_holders_map.find(bit_cast(current_thread)); - if (it->value > 1) - it->value--; - else - m_shared_holders_map.remove(it); -#endif - break; - } - default: - VERIFY_NOT_REACHED(); - } - -#if LOCK_DEBUG - if (current_thread) { - current_thread->holding_lock(*this, -1, {}); - } -#endif - - if (m_times_locked == 0) { - VERIFY(current_mode == Mode::Exclusive ? !m_holder : m_shared_holders == 0); - - m_mode = Mode::Unlocked; - unblock_waiters(current_mode); - } -} - -void Mutex::block(Thread& current_thread, Mode mode, SpinlockLocker>& lock, u32 requested_locks) -{ - if constexpr (LOCK_IN_CRITICAL_DEBUG) { - // There are no interrupts enabled in early boot. - if (g_not_in_early_boot.was_set()) - VERIFY_INTERRUPTS_ENABLED(); - } - m_blocked_thread_lists.with([&](auto& lists) { - auto append_to_list = [&](L& list) { - VERIFY(!list.contains(current_thread)); - list.append(current_thread); - }; - - if (m_behavior == MutexBehavior::BigLock) - append_to_list(lists.exclusive_big_lock); - else - append_to_list(lists.list_for_mode(mode)); - }); - - dbgln_if(LOCK_TRACE_DEBUG, "Mutex::lock @ {} ({}) waiting...", this, m_name); - current_thread.block(*this, lock, requested_locks); - dbgln_if(LOCK_TRACE_DEBUG, "Mutex::lock @ {} ({}) waited", this, m_name); - - m_blocked_thread_lists.with([&](auto& lists) { - auto remove_from_list = [&](L& list) { - VERIFY(list.contains(current_thread)); - list.remove(current_thread); - }; - - if (m_behavior == MutexBehavior::BigLock) - remove_from_list(lists.exclusive_big_lock); - else - remove_from_list(lists.list_for_mode(mode)); - }); -} - -void Mutex::unblock_waiters(Mode previous_mode) -{ - VERIFY(m_times_locked == 0); - VERIFY(m_mode == Mode::Unlocked); - - m_blocked_thread_lists.with([&](auto& lists) { - auto unblock_shared = [&]() { - if (lists.shared.is_empty()) - return false; - VERIFY(m_behavior == MutexBehavior::Regular); - m_mode = Mode::Shared; - for (auto& thread : lists.shared) { - auto requested_locks = thread.unblock_from_mutex(*this); - m_shared_holders += requested_locks; -#if LOCK_SHARED_UPGRADE_DEBUG - auto set_result = m_shared_holders_map.set(bit_cast(&thread), requested_locks); - VERIFY(set_result == AK::HashSetResult::InsertedNewEntry); -#endif - m_times_locked += requested_locks; - } - return true; - }; - auto unblock_exclusive = [&](L& list) { - if (auto* next_exclusive_thread = list.first()) { - m_mode = Mode::Exclusive; - m_times_locked = next_exclusive_thread->unblock_from_mutex(*this); - m_holder = bit_cast(next_exclusive_thread); - return true; - } - return false; - }; - - if (m_behavior == MutexBehavior::BigLock) { - unblock_exclusive(lists.exclusive_big_lock); - } else if (previous_mode == Mode::Exclusive) { - if (!unblock_shared()) - unblock_exclusive(lists.exclusive); - } else { - if (!unblock_exclusive(lists.exclusive)) - unblock_shared(); - } - }); -} - -auto Mutex::force_unlock_exclusive_if_locked(u32& lock_count_to_restore) -> Mode -{ - VERIFY(m_behavior == MutexBehavior::BigLock); - // NOTE: This may be called from an interrupt handler (not an IRQ handler) - // and also from within critical sections! - VERIFY(!Processor::current_in_irq()); - - auto* current_thread = Thread::current(); - SpinlockLocker lock(m_lock); - auto current_mode = m_mode; - switch (current_mode) { - case Mode::Exclusive: { - if (m_holder != bit_cast(current_thread)) { - lock_count_to_restore = 0; - return Mode::Unlocked; - } - - dbgln_if(LOCK_RESTORE_DEBUG, "Mutex::force_unlock_exclusive_if_locked @ {}: unlocking exclusive with lock count: {}", this, m_times_locked); -#if LOCK_DEBUG - current_thread->holding_lock(*this, -(int)m_times_locked, {}); -#endif - m_holder = 0; - VERIFY(m_times_locked > 0); - lock_count_to_restore = m_times_locked; - m_times_locked = 0; - m_mode = Mode::Unlocked; - unblock_waiters(Mode::Exclusive); - break; - } - case Mode::Unlocked: { - lock_count_to_restore = 0; - break; - } - default: - VERIFY_NOT_REACHED(); - } - return current_mode; -} - -void Mutex::restore_exclusive_lock(u32 lock_count, [[maybe_unused]] LockLocation const& location) -{ - VERIFY(m_behavior == MutexBehavior::BigLock); - VERIFY(lock_count > 0); - VERIFY(!Processor::current_in_irq()); - - auto* current_thread = Thread::current(); - bool did_block = false; - SpinlockLocker lock(m_lock); - [[maybe_unused]] auto previous_mode = m_mode; - if (m_mode == Mode::Exclusive && m_holder != bit_cast(current_thread)) { - block(*current_thread, Mode::Exclusive, lock, lock_count); - did_block = true; - // If we blocked then m_mode should have been updated to what we requested - VERIFY(m_mode == Mode::Exclusive); - } - - dbgln_if(LOCK_RESTORE_DEBUG, "Mutex::restore_exclusive_lock @ {}: restoring exclusive with lock count {}, was {}", this, lock_count, mode_to_string(previous_mode)); - - VERIFY(m_mode != Mode::Shared); - VERIFY(m_shared_holders == 0); - if (did_block) { - VERIFY(m_times_locked > 0); - VERIFY(m_holder == bit_cast(current_thread)); - } else { - if (m_mode == Mode::Unlocked) { - m_mode = Mode::Exclusive; - VERIFY(m_times_locked == 0); - m_times_locked = lock_count; - VERIFY(!m_holder); - m_holder = bit_cast(current_thread); - } else { - VERIFY(m_mode == Mode::Exclusive); - VERIFY(m_holder == bit_cast(current_thread)); - VERIFY(m_times_locked > 0); - m_times_locked += lock_count; - } - } - -#if LOCK_DEBUG - current_thread->holding_lock(*this, (int)lock_count, location); -#endif -} - -} diff --git a/Kernel/Locking/Mutex.h b/Kernel/Locking/Mutex.h deleted file mode 100644 index d2352688a90..00000000000 --- a/Kernel/Locking/Mutex.h +++ /dev/null @@ -1,183 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * Copyright (c) 2022, Idan Horowitz - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include -#include -#include -#include - -namespace Kernel { - -class Mutex { - friend class Thread; - - AK_MAKE_NONCOPYABLE(Mutex); - AK_MAKE_NONMOVABLE(Mutex); - -public: - using Mode = LockMode; - - // FIXME: remove this after annihilating Process::m_big_lock - enum class MutexBehavior { - Regular, - BigLock, - }; - - Mutex(StringView name = {}, MutexBehavior behavior = MutexBehavior::Regular) - : m_name(name) - , m_behavior(behavior) - { - } - ~Mutex() = default; - - void lock(Mode mode = Mode::Exclusive, LockLocation const& location = LockLocation::current()); - void restore_exclusive_lock(u32, LockLocation const& location = LockLocation::current()); - - void unlock(); - [[nodiscard]] Mode force_unlock_exclusive_if_locked(u32&); - [[nodiscard]] bool is_locked() const - { - SpinlockLocker lock(m_lock); - return m_mode != Mode::Unlocked; - } - - [[nodiscard]] bool is_exclusively_locked_by_current_thread() const - { - SpinlockLocker lock(m_lock); - VERIFY(m_mode != Mode::Shared); // This method should only be used on exclusively-held locks - if (m_mode == Mode::Unlocked) - return false; - return m_holder == bit_cast(Thread::current()); - } - - [[nodiscard]] StringView name() const { return m_name; } - - static StringView mode_to_string(Mode mode) - { - switch (mode) { - case Mode::Unlocked: - return "unlocked"sv; - case Mode::Exclusive: - return "exclusive"sv; - case Mode::Shared: - return "shared"sv; - default: - return "invalid"sv; - } - } - -private: - using BlockedThreadList = IntrusiveList<&Thread::m_blocked_threads_list_node>; - - // FIXME: remove this after annihilating Process::m_big_lock - using BigLockBlockedThreadList = IntrusiveList<&Thread::m_big_lock_blocked_threads_list_node>; - - // FIXME: Allow any lock rank. - void block(Thread&, Mode, SpinlockLocker>&, u32); - void unblock_waiters(Mode); - - StringView m_name; - Mode m_mode { Mode::Unlocked }; - - // FIXME: remove this after annihilating Process::m_big_lock - MutexBehavior m_behavior; - - // When locked exclusively, only the thread already holding the lock can - // lock it again. When locked in shared mode, any thread can do that. - u32 m_times_locked { 0 }; - - // The address of one of the threads that hold this lock, or 0. - // When locked in shared mode, this is stored on best effort basis: 0 does *not* mean - // the lock is unlocked, it just means we don't know which threads hold it. - // When locked exclusively, this is always the one thread that holds the lock. - uintptr_t m_holder { 0 }; - size_t m_shared_holders { 0 }; - - struct BlockedThreadLists { - BlockedThreadList exclusive; - BlockedThreadList shared; - - // FIXME: remove this after annihilating Process::m_big_lock - BigLockBlockedThreadList exclusive_big_lock; - - ALWAYS_INLINE BlockedThreadList& list_for_mode(Mode mode) - { - VERIFY(mode == Mode::Exclusive || mode == Mode::Shared); - return mode == Mode::Exclusive ? exclusive : shared; - } - }; - // FIXME: Use a specific lock rank passed by constructor. - SpinlockProtected m_blocked_thread_lists {}; - - // FIXME: See above. - mutable Spinlock m_lock {}; - -#if LOCK_SHARED_UPGRADE_DEBUG - HashMap m_shared_holders_map; -#endif -}; - -class MutexLocker { - AK_MAKE_NONCOPYABLE(MutexLocker); - -public: - ALWAYS_INLINE explicit MutexLocker() - : m_lock(nullptr) - , m_locked(false) - { - } - - ALWAYS_INLINE explicit MutexLocker(Mutex& l, Mutex::Mode mode = Mutex::Mode::Exclusive, LockLocation const& location = LockLocation::current()) - : m_lock(&l) - { - m_lock->lock(mode, location); - } - - ALWAYS_INLINE ~MutexLocker() - { - if (m_locked) - unlock(); - } - - ALWAYS_INLINE void unlock() - { - VERIFY(m_lock); - VERIFY(m_locked); - m_locked = false; - m_lock->unlock(); - } - - ALWAYS_INLINE void attach_and_lock(Mutex& lock, Mutex::Mode mode = Mutex::Mode::Exclusive, LockLocation const& location = LockLocation::current()) - { - VERIFY(!m_locked); - m_lock = &lock; - m_locked = true; - - m_lock->lock(mode, location); - } - - ALWAYS_INLINE void lock(Mutex::Mode mode = Mutex::Mode::Exclusive, LockLocation const& location = LockLocation::current()) - { - VERIFY(m_lock); - VERIFY(!m_locked); - m_locked = true; - - m_lock->lock(mode, location); - } - -private: - Mutex* m_lock; - bool m_locked { true }; -}; - -} diff --git a/Kernel/Locking/MutexProtected.h b/Kernel/Locking/MutexProtected.h deleted file mode 100644 index d8742601b5b..00000000000 --- a/Kernel/Locking/MutexProtected.h +++ /dev/null @@ -1,103 +0,0 @@ -/* - * Copyright (c) 2021, the SerenityOS developers. - * Copyright (c) 2021, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include - -namespace Kernel { - -template -class MutexProtected { - AK_MAKE_NONCOPYABLE(MutexProtected); - AK_MAKE_NONMOVABLE(MutexProtected); - -private: - template - class Locked { - AK_MAKE_NONCOPYABLE(Locked); - AK_MAKE_NONMOVABLE(Locked); - - public: - Locked(U& value, Mutex& mutex, LockLocation const& location) - : m_value(value) - , m_locker(mutex, lock_mode, location) - { - } - - ALWAYS_INLINE U const* operator->() const { return &m_value; } - ALWAYS_INLINE U const& operator*() const { return m_value; } - - ALWAYS_INLINE U* operator->() - requires(!IsConst) - { - return &m_value; - } - ALWAYS_INLINE U& operator*() - requires(!IsConst) - { - return m_value; - } - - ALWAYS_INLINE U const& get() const { return &m_value; } - ALWAYS_INLINE U& get() - requires(!IsConst) - { - return &m_value; - } - - private: - U& m_value; - MutexLocker m_locker; - }; - - auto lock_shared(LockLocation const& location) const { return Locked(m_value, m_mutex, location); } - auto lock_exclusive(LockLocation const& location) { return Locked(m_value, m_mutex, location); } - -public: - MutexProtected() = default; - - template - decltype(auto) with_shared(Callback callback, LockLocation const& location = LockLocation::current()) const - { - auto lock = lock_shared(location); - return callback(*lock); - } - - template - decltype(auto) with_exclusive(Callback callback, LockLocation const& location = LockLocation::current()) - { - auto lock = lock_exclusive(location); - return callback(*lock); - } - - template - void for_each_shared(Callback callback, LockLocation const& location = LockLocation::current()) const - { - with_shared([&](auto const& value) { - for (auto& item : value) - callback(item); - }, - location); - } - - template - void for_each_exclusive(Callback callback, LockLocation const& location = LockLocation::current()) - { - with_exclusive([&](auto& value) { - for (auto& item : value) - callback(item); - }, - location); - } - -private: - T m_value; - Mutex mutable m_mutex; -}; - -} diff --git a/Kernel/Locking/Spinlock.h b/Kernel/Locking/Spinlock.h deleted file mode 100644 index ccc33207dcc..00000000000 --- a/Kernel/Locking/Spinlock.h +++ /dev/null @@ -1,184 +0,0 @@ -/* - * Copyright (c) 2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include - -namespace Kernel { - -template -class Spinlock { - AK_MAKE_NONCOPYABLE(Spinlock); - AK_MAKE_NONMOVABLE(Spinlock); - -public: - Spinlock() = default; - - InterruptsState lock() - { - InterruptsState previous_interrupts_state = Processor::interrupts_state(); - Processor::enter_critical(); - Processor::disable_interrupts(); - while (m_lock.exchange(1, AK::memory_order_acquire) != 0) - Processor::wait_check(); - track_lock_acquire(m_rank); - return previous_interrupts_state; - } - - void unlock(InterruptsState previous_interrupts_state) - { - VERIFY(is_locked()); - track_lock_release(m_rank); - m_lock.store(0, AK::memory_order_release); - - Processor::leave_critical(); - Processor::restore_interrupts_state(previous_interrupts_state); - } - - [[nodiscard]] ALWAYS_INLINE bool is_locked() const - { - return m_lock.load(AK::memory_order_relaxed) != 0; - } - - ALWAYS_INLINE void initialize() - { - m_lock.store(0, AK::memory_order_relaxed); - } - -private: - Atomic m_lock { 0 }; - static constexpr LockRank const m_rank { Rank }; -}; - -template -class RecursiveSpinlock { - AK_MAKE_NONCOPYABLE(RecursiveSpinlock); - AK_MAKE_NONMOVABLE(RecursiveSpinlock); - -public: - RecursiveSpinlock() = default; - - InterruptsState lock() - { - InterruptsState previous_interrupts_state = Processor::interrupts_state(); - Processor::disable_interrupts(); - Processor::enter_critical(); - auto& proc = Processor::current(); - FlatPtr cpu = FlatPtr(&proc); - FlatPtr expected = 0; - while (!m_lock.compare_exchange_strong(expected, cpu, AK::memory_order_acq_rel)) { - if (expected == cpu) - break; - Processor::wait_check(); - expected = 0; - } - if (m_recursions == 0) - track_lock_acquire(m_rank); - m_recursions++; - return previous_interrupts_state; - } - - void unlock(InterruptsState previous_interrupts_state) - { - VERIFY_INTERRUPTS_DISABLED(); - VERIFY(m_recursions > 0); - VERIFY(m_lock.load(AK::memory_order_relaxed) == FlatPtr(&Processor::current())); - if (--m_recursions == 0) { - track_lock_release(m_rank); - m_lock.store(0, AK::memory_order_release); - } - - Processor::leave_critical(); - Processor::restore_interrupts_state(previous_interrupts_state); - } - - [[nodiscard]] ALWAYS_INLINE bool is_locked() const - { - return m_lock.load(AK::memory_order_relaxed) != 0; - } - - [[nodiscard]] ALWAYS_INLINE bool is_locked_by_current_processor() const - { - return m_lock.load(AK::memory_order_relaxed) == FlatPtr(&Processor::current()); - } - - ALWAYS_INLINE void initialize() - { - m_lock.store(0, AK::memory_order_relaxed); - } - -private: - Atomic m_lock { 0 }; - u32 m_recursions { 0 }; - static constexpr LockRank const m_rank { Rank }; -}; - -template -class [[nodiscard]] SpinlockLocker { - AK_MAKE_NONCOPYABLE(SpinlockLocker); - -public: - SpinlockLocker() = delete; - SpinlockLocker& operator=(SpinlockLocker&&) = delete; - - SpinlockLocker(LockType& lock) - : m_lock(&lock) - { - VERIFY(m_lock); - m_previous_interrupts_state = m_lock->lock(); - m_have_lock = true; - } - - SpinlockLocker(SpinlockLocker&& from) - : m_lock(from.m_lock) - , m_previous_interrupts_state(from.m_previous_interrupts_state) - , m_have_lock(from.m_have_lock) - { - from.m_lock = nullptr; - from.m_previous_interrupts_state = InterruptsState::Disabled; - from.m_have_lock = false; - } - - ~SpinlockLocker() - { - if (m_lock && m_have_lock) { - m_lock->unlock(m_previous_interrupts_state); - } - } - - ALWAYS_INLINE void lock() - { - VERIFY(m_lock); - VERIFY(!m_have_lock); - m_previous_interrupts_state = m_lock->lock(); - m_have_lock = true; - } - - ALWAYS_INLINE void unlock() - { - VERIFY(m_lock); - VERIFY(m_have_lock); - m_lock->unlock(m_previous_interrupts_state); - m_previous_interrupts_state = InterruptsState::Disabled; - m_have_lock = false; - } - - [[nodiscard]] ALWAYS_INLINE bool have_lock() const - { - return m_have_lock; - } - -private: - LockType* m_lock { nullptr }; - InterruptsState m_previous_interrupts_state { InterruptsState::Disabled }; - bool m_have_lock { false }; -}; - -} diff --git a/Kernel/Locking/SpinlockProtected.h b/Kernel/Locking/SpinlockProtected.h deleted file mode 100644 index ced5e941cba..00000000000 --- a/Kernel/Locking/SpinlockProtected.h +++ /dev/null @@ -1,93 +0,0 @@ -/* - * Copyright (c) 2021, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include - -namespace Kernel { - -template -class SpinlockProtected { - AK_MAKE_NONCOPYABLE(SpinlockProtected); - AK_MAKE_NONMOVABLE(SpinlockProtected); - -private: - template - class Locked { - AK_MAKE_NONCOPYABLE(Locked); - AK_MAKE_NONMOVABLE(Locked); - - public: - Locked(U& value, RecursiveSpinlock& spinlock) - : m_value(value) - , m_locker(spinlock) - { - } - - ALWAYS_INLINE U const* operator->() const { return &m_value; } - ALWAYS_INLINE U const& operator*() const { return m_value; } - - ALWAYS_INLINE U* operator->() { return &m_value; } - ALWAYS_INLINE U& operator*() { return m_value; } - - ALWAYS_INLINE U const& get() const { return m_value; } - ALWAYS_INLINE U& get() { return m_value; } - - private: - U& m_value; - SpinlockLocker> m_locker; - }; - - auto lock_const() const { return Locked(m_value, m_spinlock); } - auto lock_mutable() { return Locked(m_value, m_spinlock); } - -public: - template - SpinlockProtected(Args&&... args) - : m_value(forward(args)...) - { - } - - template - decltype(auto) with(Callback callback) const - { - auto lock = lock_const(); - return callback(*lock); - } - - template - decltype(auto) with(Callback callback) - { - auto lock = lock_mutable(); - return callback(*lock); - } - - template - void for_each_const(Callback callback) const - { - with([&](auto const& value) { - for (auto& item : value) - callback(item); - }); - } - - template - void for_each(Callback callback) - { - with([&](auto& value) { - for (auto& item : value) - callback(item); - }); - } - -private: - T m_value; - RecursiveSpinlock mutable m_spinlock; - static constexpr LockRank const m_rank { Rank }; -}; - -} diff --git a/Kernel/Memory/AddressSpace.cpp b/Kernel/Memory/AddressSpace.cpp deleted file mode 100644 index 9ea53b88a1e..00000000000 --- a/Kernel/Memory/AddressSpace.cpp +++ /dev/null @@ -1,423 +0,0 @@ -/* - * Copyright (c) 2021-2022, Andreas Kling - * Copyright (c) 2021, Leon Albrecht - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace Kernel::Memory { - -ErrorOr> AddressSpace::try_create(Process& process, AddressSpace const* parent) -{ - auto page_directory = TRY(PageDirectory::try_create_for_userspace(process)); - - VirtualRange total_range = [&]() -> VirtualRange { - if (parent) - return parent->m_region_tree.total_range(); - constexpr FlatPtr userspace_range_base = USER_RANGE_BASE; - FlatPtr const userspace_range_ceiling = USER_RANGE_CEILING; - size_t random_offset = (get_fast_random() % 2 * MiB) & PAGE_MASK; - FlatPtr base = userspace_range_base + random_offset; - return VirtualRange(VirtualAddress { base }, userspace_range_ceiling - base); - }(); - - return adopt_nonnull_own_or_enomem(new (nothrow) AddressSpace(move(page_directory), total_range)); -} - -AddressSpace::AddressSpace(NonnullLockRefPtr page_directory, VirtualRange total_range) - : m_page_directory(move(page_directory)) - , m_region_tree(total_range) -{ -} - -AddressSpace::~AddressSpace() = default; - -ErrorOr AddressSpace::unmap_mmap_range(VirtualAddress addr, size_t size) -{ - if (!size) - return EINVAL; - - auto range_to_unmap = TRY(VirtualRange::expand_to_page_boundaries(addr.get(), size)); - - if (!is_user_range(range_to_unmap)) - return EFAULT; - - if (auto* whole_region = find_region_from_range(range_to_unmap)) { - if (!whole_region->is_mmap()) - return EPERM; - if (whole_region->is_immutable()) - return EPERM; - - PerformanceManager::add_unmap_perf_event(Process::current(), whole_region->range()); - - deallocate_region(*whole_region); - return {}; - } - - if (auto* old_region = find_region_containing(range_to_unmap)) { - if (!old_region->is_mmap()) - return EPERM; - if (old_region->is_immutable()) - return EPERM; - - // Remove the old region from our regions tree, since were going to add another region - // with the exact same start address. - auto region = take_region(*old_region); - region->unmap(); - - auto new_regions = TRY(try_split_region_around_range(*region, range_to_unmap)); - - // And finally we map the new region(s) using our page directory (they were just allocated and don't have one). - for (auto* new_region : new_regions) { - // TODO: Ideally we should do this in a way that can be rolled back on failure, as failing here - // leaves the caller in an undefined state. - TRY(new_region->map(page_directory())); - } - - PerformanceManager::add_unmap_perf_event(Process::current(), range_to_unmap); - - return {}; - } - - // Try again while checking multiple regions at a time. - auto const& regions = TRY(find_regions_intersecting(range_to_unmap)); - if (regions.is_empty()) - return {}; - - // Check if any of the regions is not mmap'ed, to not accidentally - // error out with just half a region map left. - for (auto* region : regions) { - if (!region->is_mmap()) - return EPERM; - if (region->is_immutable()) - return EPERM; - } - - Vector new_regions; - - for (auto* old_region : regions) { - // If it's a full match we can remove the entire old region. - if (old_region->range().intersect(range_to_unmap).size() == old_region->size()) { - deallocate_region(*old_region); - continue; - } - - // Remove the old region from our regions tree, since were going to add another region - // with the exact same start address. - auto region = take_region(*old_region); - region->unmap(); - - // Otherwise, split the regions and collect them for future mapping. - auto split_regions = TRY(try_split_region_around_range(*region, range_to_unmap)); - TRY(new_regions.try_extend(split_regions)); - } - - // And finally map the new region(s) into our page directory. - for (auto* new_region : new_regions) { - // TODO: Ideally we should do this in a way that can be rolled back on failure, as failing here - // leaves the caller in an undefined state. - TRY(new_region->map(page_directory())); - } - - PerformanceManager::add_unmap_perf_event(Process::current(), range_to_unmap); - - return {}; -} - -ErrorOr AddressSpace::try_allocate_split_region(Region const& source_region, VirtualRange const& range, size_t offset_in_vmobject) -{ - OwnPtr region_name; - if (!source_region.name().is_null()) - region_name = TRY(KString::try_create(source_region.name())); - - auto new_region = TRY(Region::create_unplaced( - source_region.vmobject(), offset_in_vmobject, move(region_name), source_region.access(), source_region.is_cacheable() ? Region::Cacheable::Yes : Region::Cacheable::No, source_region.is_shared())); - new_region->set_syscall_region(source_region.is_syscall_region()); - new_region->set_mmap(source_region.is_mmap(), source_region.mmapped_from_readable(), source_region.mmapped_from_writable()); - new_region->set_stack(source_region.is_stack()); - size_t page_offset_in_source_region = (offset_in_vmobject - source_region.offset_in_vmobject()) / PAGE_SIZE; - for (size_t i = 0; i < new_region->page_count(); ++i) { - if (source_region.should_cow(page_offset_in_source_region + i)) - TRY(new_region->set_should_cow(i, true)); - } - TRY(m_region_tree.place_specifically(*new_region, range)); - return new_region.leak_ptr(); -} - -ErrorOr AddressSpace::allocate_region(RandomizeVirtualAddress randomize_virtual_address, VirtualAddress requested_address, size_t requested_size, size_t requested_alignment, StringView name, int prot, AllocationStrategy strategy) -{ - if (!requested_address.is_page_aligned()) - return EINVAL; - auto size = TRY(Memory::page_round_up(requested_size)); - auto alignment = TRY(Memory::page_round_up(requested_alignment)); - OwnPtr region_name; - if (!name.is_null()) - region_name = TRY(KString::try_create(name)); - auto vmobject = TRY(AnonymousVMObject::try_create_with_size(size, strategy)); - auto region = TRY(Region::create_unplaced(move(vmobject), 0, move(region_name), prot_to_region_access_flags(prot))); - if (requested_address.is_null()) { - TRY(m_region_tree.place_anywhere(*region, randomize_virtual_address, size, alignment)); - } else { - TRY(m_region_tree.place_specifically(*region, VirtualRange { requested_address, size })); - } - TRY(region->map(page_directory(), ShouldFlushTLB::No)); - return region.leak_ptr(); -} - -ErrorOr AddressSpace::allocate_region_with_vmobject(VirtualRange requested_range, NonnullLockRefPtr vmobject, size_t offset_in_vmobject, StringView name, int prot, bool shared) -{ - return allocate_region_with_vmobject(RandomizeVirtualAddress::Yes, requested_range.base(), requested_range.size(), PAGE_SIZE, move(vmobject), offset_in_vmobject, name, prot, shared); -} - -ErrorOr AddressSpace::allocate_region_with_vmobject(RandomizeVirtualAddress randomize_virtual_address, VirtualAddress requested_address, size_t requested_size, size_t requested_alignment, NonnullLockRefPtr vmobject, size_t offset_in_vmobject, StringView name, int prot, bool shared) -{ - if (!requested_address.is_page_aligned()) - return EINVAL; - auto size = TRY(page_round_up(requested_size)); - auto alignment = TRY(page_round_up(requested_alignment)); - - if (Checked::addition_would_overflow(offset_in_vmobject, requested_size)) - return EOVERFLOW; - - size_t end_in_vmobject = offset_in_vmobject + requested_size; - if (offset_in_vmobject >= vmobject->size()) { - dbgln("allocate_region_with_vmobject: Attempt to allocate a region with an offset past the end of its VMObject."); - return EINVAL; - } - if (end_in_vmobject > vmobject->size()) { - dbgln("allocate_region_with_vmobject: Attempt to allocate a region with an end past the end of its VMObject."); - return EINVAL; - } - offset_in_vmobject &= PAGE_MASK; - OwnPtr region_name; - if (!name.is_null()) - region_name = TRY(KString::try_create(name)); - - auto region = TRY(Region::create_unplaced(move(vmobject), offset_in_vmobject, move(region_name), prot_to_region_access_flags(prot), Region::Cacheable::Yes, shared)); - - if (requested_address.is_null()) - TRY(m_region_tree.place_anywhere(*region, randomize_virtual_address, size, alignment)); - else - TRY(m_region_tree.place_specifically(*region, VirtualRange { VirtualAddress { requested_address }, size })); - - ArmedScopeGuard remove_region_from_tree_on_failure = [&] { - // At this point the region is already part of the Process region tree, so we have to make sure - // we remove it from the tree before returning an error, or else the Region tree will contain - // a dangling pointer to the free'd Region instance - m_region_tree.remove(*region); - }; - - if (prot == PROT_NONE) { - // For PROT_NONE mappings, we don't have to set up any page table mappings. - // We do still need to attach the region to the page_directory though. - region->set_page_directory(page_directory()); - } else { - TRY(region->map(page_directory(), ShouldFlushTLB::No)); - } - remove_region_from_tree_on_failure.disarm(); - return region.leak_ptr(); -} - -void AddressSpace::deallocate_region(Region& region) -{ - (void)take_region(region); -} - -NonnullOwnPtr AddressSpace::take_region(Region& region) -{ - auto did_remove = m_region_tree.remove(region); - VERIFY(did_remove); - return NonnullOwnPtr { NonnullOwnPtr::Adopt, region }; -} - -Region* AddressSpace::find_region_from_range(VirtualRange const& range) -{ - auto* found_region = m_region_tree.regions().find(range.base().get()); - if (!found_region) - return nullptr; - auto& region = *found_region; - auto rounded_range_size = page_round_up(range.size()); - if (rounded_range_size.is_error() || region.size() != rounded_range_size.value()) - return nullptr; - return ®ion; -} - -Region* AddressSpace::find_region_containing(VirtualRange const& range) -{ - return m_region_tree.find_region_containing(range); -} - -ErrorOr> AddressSpace::find_regions_intersecting(VirtualRange const& range) -{ - Vector regions = {}; - size_t total_size_collected = 0; - - auto* found_region = m_region_tree.regions().find_largest_not_above(range.base().get()); - if (!found_region) - return regions; - for (auto iter = m_region_tree.regions().begin_from(*found_region); !iter.is_end(); ++iter) { - auto const& iter_range = (*iter).range(); - if (iter_range.base() < range.end() && iter_range.end() > range.base()) { - TRY(regions.try_append(&*iter)); - - total_size_collected += (*iter).size() - iter_range.intersect(range).size(); - if (total_size_collected == range.size()) - break; - } - } - - return regions; -} - -// Carve out a virtual address range from a region and return the two regions on either side -ErrorOr> AddressSpace::try_split_region_around_range(Region const& source_region, VirtualRange const& desired_range) -{ - VirtualRange old_region_range = source_region.range(); - auto remaining_ranges_after_unmap = old_region_range.carve(desired_range); - - VERIFY(!remaining_ranges_after_unmap.is_empty()); - auto try_make_replacement_region = [&](VirtualRange const& new_range) -> ErrorOr { - VERIFY(old_region_range.contains(new_range)); - size_t new_range_offset_in_vmobject = source_region.offset_in_vmobject() + (new_range.base().get() - old_region_range.base().get()); - return try_allocate_split_region(source_region, new_range, new_range_offset_in_vmobject); - }; - Vector new_regions; - for (auto& new_range : remaining_ranges_after_unmap) { - auto* new_region = TRY(try_make_replacement_region(new_range)); - new_regions.unchecked_append(new_region); - } - return new_regions; -} - -void AddressSpace::dump_regions() -{ - dbgln("Process regions:"); - char const* addr_padding = " "; - dbgln("BEGIN{} END{} SIZE{} ACCESS NAME", - addr_padding, addr_padding, addr_padding); - - for (auto const& region : m_region_tree.regions()) { - dbgln("{:p} -- {:p} {:p} {:c}{:c}{:c}{:c}{:c}{:c} {}", region.vaddr().get(), region.vaddr().offset(region.size() - 1).get(), region.size(), - region.is_readable() ? 'R' : ' ', - region.is_writable() ? 'W' : ' ', - region.is_executable() ? 'X' : ' ', - region.is_shared() ? 'S' : ' ', - region.is_stack() ? 'T' : ' ', - region.is_syscall_region() ? 'C' : ' ', - region.name()); - } - MM.dump_kernel_regions(); -} - -void AddressSpace::remove_all_regions(Badge) -{ - if (!g_in_system_shutdown) - VERIFY(Thread::current() == g_finalizer); - - { - SpinlockLocker pd_locker(m_page_directory->get_lock()); - for (auto& region : m_region_tree.regions()) - region.unmap_with_locks_held(ShouldFlushTLB::No, pd_locker); - } - - m_region_tree.delete_all_regions_assuming_they_are_unmapped(); -} - -size_t AddressSpace::amount_dirty_private() const -{ - // FIXME: This gets a bit more complicated for Regions sharing the same underlying VMObject. - // The main issue I'm thinking of is when the VMObject has physical pages that none of the Regions are mapping. - // That's probably a situation that needs to be looked at in general. - size_t amount = 0; - for (auto const& region : m_region_tree.regions()) { - if (!region.is_shared()) - amount += region.amount_dirty(); - } - return amount; -} - -ErrorOr AddressSpace::amount_clean_inode() const -{ - HashTable> vmobjects; - for (auto const& region : m_region_tree.regions()) { - if (region.vmobject().is_inode()) - TRY(vmobjects.try_set(&static_cast(region.vmobject()))); - } - size_t amount = 0; - for (auto& vmobject : vmobjects) - amount += vmobject->amount_clean(); - return amount; -} - -size_t AddressSpace::amount_virtual() const -{ - size_t amount = 0; - for (auto const& region : m_region_tree.regions()) { - amount += region.size(); - } - return amount; -} - -size_t AddressSpace::amount_resident() const -{ - // FIXME: This will double count if multiple regions use the same physical page. - size_t amount = 0; - for (auto const& region : m_region_tree.regions()) { - amount += region.amount_resident(); - } - return amount; -} - -size_t AddressSpace::amount_shared() const -{ - // FIXME: This will double count if multiple regions use the same physical page. - // FIXME: It doesn't work at the moment, since it relies on PhysicalPage ref counts, - // and each PhysicalPage is only reffed by its VMObject. This needs to be refactored - // so that every Region contributes +1 ref to each of its PhysicalPages. - size_t amount = 0; - for (auto const& region : m_region_tree.regions()) { - amount += region.amount_shared(); - } - return amount; -} - -size_t AddressSpace::amount_purgeable_volatile() const -{ - size_t amount = 0; - for (auto const& region : m_region_tree.regions()) { - if (!region.vmobject().is_anonymous()) - continue; - auto const& vmobject = static_cast(region.vmobject()); - if (vmobject.is_purgeable() && vmobject.is_volatile()) - amount += region.amount_resident(); - } - return amount; -} - -size_t AddressSpace::amount_purgeable_nonvolatile() const -{ - size_t amount = 0; - for (auto const& region : m_region_tree.regions()) { - if (!region.vmobject().is_anonymous()) - continue; - auto const& vmobject = static_cast(region.vmobject()); - if (vmobject.is_purgeable() && !vmobject.is_volatile()) - amount += region.amount_resident(); - } - return amount; -} - -} diff --git a/Kernel/Memory/AddressSpace.h b/Kernel/Memory/AddressSpace.h deleted file mode 100644 index 91004d3382e..00000000000 --- a/Kernel/Memory/AddressSpace.h +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Copyright (c) 2018-2021, Andreas Kling - * Copyright (c) 2021, Leon Albrecht - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace Kernel::Memory { - -class AddressSpace { -public: - static ErrorOr> try_create(Process&, AddressSpace const* parent); - ~AddressSpace(); - - PageDirectory& page_directory() { return *m_page_directory; } - PageDirectory const& page_directory() const { return *m_page_directory; } - - RegionTree& region_tree() { return m_region_tree; } - RegionTree const& region_tree() const { return m_region_tree; } - - void dump_regions(); - - ErrorOr unmap_mmap_range(VirtualAddress, size_t); - - ErrorOr allocate_region_with_vmobject(VirtualRange requested_range, NonnullLockRefPtr, size_t offset_in_vmobject, StringView name, int prot, bool shared); - ErrorOr allocate_region_with_vmobject(RandomizeVirtualAddress, VirtualAddress requested_address, size_t requested_size, size_t requested_alignment, NonnullLockRefPtr, size_t offset_in_vmobject, StringView name, int prot, bool shared); - ErrorOr allocate_region(RandomizeVirtualAddress, VirtualAddress requested_address, size_t requested_size, size_t requested_alignment, StringView name, int prot = PROT_READ | PROT_WRITE, AllocationStrategy strategy = AllocationStrategy::Reserve); - void deallocate_region(Region& region); - NonnullOwnPtr take_region(Region& region); - - ErrorOr try_allocate_split_region(Region const& source_region, VirtualRange const&, size_t offset_in_vmobject); - ErrorOr> try_split_region_around_range(Region const& source_region, VirtualRange const&); - - Region* find_region_from_range(VirtualRange const&); - Region* find_region_containing(VirtualRange const&); - - ErrorOr> find_regions_intersecting(VirtualRange const&); - - bool enforces_syscall_regions() const { return m_enforces_syscall_regions.was_set(); } - void set_enforces_syscall_regions() { m_enforces_syscall_regions.set(); } - - void remove_all_regions(Badge); - - ErrorOr amount_clean_inode() const; - size_t amount_dirty_private() const; - size_t amount_virtual() const; - size_t amount_resident() const; - size_t amount_shared() const; - size_t amount_purgeable_volatile() const; - size_t amount_purgeable_nonvolatile() const; - -private: - AddressSpace(NonnullLockRefPtr, VirtualRange total_range); - - LockRefPtr m_page_directory; - - RegionTree m_region_tree; - - SetOnce m_enforces_syscall_regions; -}; - -} diff --git a/Kernel/Memory/AllocationStrategy.h b/Kernel/Memory/AllocationStrategy.h deleted file mode 100644 index 28cff4c9c08..00000000000 --- a/Kernel/Memory/AllocationStrategy.h +++ /dev/null @@ -1,17 +0,0 @@ -/* - * Copyright (c) 2020, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -namespace Kernel { - -enum class AllocationStrategy { - Reserve = 0, - AllocateNow, - None -}; - -} diff --git a/Kernel/Memory/AnonymousVMObject.cpp b/Kernel/Memory/AnonymousVMObject.cpp deleted file mode 100644 index 961295f3364..00000000000 --- a/Kernel/Memory/AnonymousVMObject.cpp +++ /dev/null @@ -1,403 +0,0 @@ -/* - * Copyright (c) 2018-2022, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include -#include -#include - -namespace Kernel::Memory { - -ErrorOr> AnonymousVMObject::try_clone() -{ - // We need to acquire our lock so we copy a sane state - SpinlockLocker lock(m_lock); - - if (is_purgeable() && is_volatile()) { - // If this object is purgeable+volatile, create a new zero-filled purgeable+volatile - // object, effectively "pre-purging" it in the child process. - auto clone = TRY(try_create_purgeable_with_size(size(), AllocationStrategy::None)); - clone->m_volatile = true; - clone->m_was_purged = true; - return clone; - } - - // We're the parent. Since we're about to become COW we need to - // commit the number of pages that we need to potentially allocate - // so that the parent is still guaranteed to be able to have all - // non-volatile memory available. - size_t new_cow_pages_needed = 0; - for (auto const& page : m_physical_pages) { - if (!page->is_shared_zero_page()) - ++new_cow_pages_needed; - } - - if (new_cow_pages_needed == 0) - return TRY(try_create_with_size(size(), AllocationStrategy::None)); - - dbgln_if(COMMIT_DEBUG, "Cloning {:p}, need {} committed cow pages", this, new_cow_pages_needed); - - auto committed_pages = TRY(MM.commit_physical_pages(new_cow_pages_needed)); - - // Create or replace the committed cow pages. When cloning a previously - // cloned vmobject, we want to essentially "fork", leaving us and the - // new clone with one set of shared committed cow pages, and the original - // one would keep the one it still has. This ensures that the original - // one and this one, as well as the clone have sufficient resources - // to cow all pages as needed - auto new_shared_committed_cow_pages = TRY(adopt_nonnull_lock_ref_or_enomem(new (nothrow) SharedCommittedCowPages(move(committed_pages)))); - auto new_physical_pages = TRY(this->try_clone_physical_pages()); - auto clone = TRY(try_create_with_shared_cow(*this, *new_shared_committed_cow_pages, move(new_physical_pages))); - - // Both original and clone become COW. So create a COW map for ourselves - // or reset all pages to be copied again if we were previously cloned - TRY(ensure_or_reset_cow_map()); - - m_shared_committed_cow_pages = move(new_shared_committed_cow_pages); - - if (m_unused_committed_pages.has_value() && !m_unused_committed_pages->is_empty()) { - // The parent vmobject didn't use up all committed pages. When - // cloning (fork) we will overcommit. For this purpose we drop all - // lazy-commit references and replace them with shared zero pages. - for (size_t i = 0; i < page_count(); i++) { - auto& page = clone->m_physical_pages[i]; - if (page && page->is_lazy_committed_page()) { - page = MM.shared_zero_page(); - } - } - } - - return clone; -} - -ErrorOr> AnonymousVMObject::try_create_with_size(size_t size, AllocationStrategy strategy) -{ - Optional committed_pages; - if (strategy == AllocationStrategy::Reserve || strategy == AllocationStrategy::AllocateNow) { - committed_pages = TRY(MM.commit_physical_pages(ceil_div(size, static_cast(PAGE_SIZE)))); - } - - auto new_physical_pages = TRY(VMObject::try_create_physical_pages(size)); - - return adopt_nonnull_lock_ref_or_enomem(new (nothrow) AnonymousVMObject(move(new_physical_pages), strategy, move(committed_pages))); -} - -ErrorOr> AnonymousVMObject::try_create_physically_contiguous_with_size(size_t size) -{ - auto contiguous_physical_pages = TRY(MM.allocate_contiguous_physical_pages(size)); - - auto new_physical_pages = TRY(FixedArray>::create(contiguous_physical_pages.span())); - - return adopt_nonnull_lock_ref_or_enomem(new (nothrow) AnonymousVMObject(move(new_physical_pages))); -} - -ErrorOr> AnonymousVMObject::try_create_purgeable_with_size(size_t size, AllocationStrategy strategy) -{ - Optional committed_pages; - if (strategy == AllocationStrategy::Reserve || strategy == AllocationStrategy::AllocateNow) { - committed_pages = TRY(MM.commit_physical_pages(ceil_div(size, static_cast(PAGE_SIZE)))); - } - - auto new_physical_pages = TRY(VMObject::try_create_physical_pages(size)); - - auto vmobject = TRY(adopt_nonnull_lock_ref_or_enomem(new (nothrow) AnonymousVMObject(move(new_physical_pages), strategy, move(committed_pages)))); - vmobject->m_purgeable = true; - return vmobject; -} - -ErrorOr> AnonymousVMObject::try_create_with_physical_pages(Span> physical_pages) -{ - auto new_physical_pages = TRY(FixedArray>::create(physical_pages)); - return adopt_nonnull_lock_ref_or_enomem(new (nothrow) AnonymousVMObject(move(new_physical_pages))); -} - -ErrorOr> AnonymousVMObject::try_create_for_physical_range(PhysicalAddress paddr, size_t size) -{ - if (paddr.offset(size) < paddr) { - dbgln("Shenanigans! try_create_for_physical_range({}, {}) would wrap around", paddr, size); - // Since we can't wrap around yet, let's pretend to OOM. - return ENOMEM; - } - - auto new_physical_pages = TRY(VMObject::try_create_physical_pages(size)); - - return adopt_nonnull_lock_ref_or_enomem(new (nothrow) AnonymousVMObject(paddr, move(new_physical_pages))); -} - -ErrorOr> AnonymousVMObject::try_create_with_shared_cow(AnonymousVMObject const& other, NonnullLockRefPtr shared_committed_cow_pages, FixedArray>&& new_physical_pages) -{ - auto weak_parent = TRY(other.try_make_weak_ptr()); - auto vmobject = TRY(adopt_nonnull_lock_ref_or_enomem(new (nothrow) AnonymousVMObject(move(weak_parent), move(shared_committed_cow_pages), move(new_physical_pages)))); - - TRY(vmobject->ensure_cow_map()); - - return vmobject; -} - -AnonymousVMObject::AnonymousVMObject(FixedArray>&& new_physical_pages, AllocationStrategy strategy, Optional committed_pages) - : VMObject(move(new_physical_pages)) - , m_unused_committed_pages(move(committed_pages)) -{ - if (strategy == AllocationStrategy::AllocateNow) { - // Allocate all pages right now. We know we can get all because we committed the amount needed - for (size_t i = 0; i < page_count(); ++i) - physical_pages()[i] = m_unused_committed_pages->take_one(); - } else { - auto& initial_page = (strategy == AllocationStrategy::Reserve) ? MM.lazy_committed_page() : MM.shared_zero_page(); - for (size_t i = 0; i < page_count(); ++i) - physical_pages()[i] = initial_page; - } -} - -AnonymousVMObject::AnonymousVMObject(PhysicalAddress paddr, FixedArray>&& new_physical_pages) - : VMObject(move(new_physical_pages)) -{ - VERIFY(paddr.page_base() == paddr); - for (size_t i = 0; i < page_count(); ++i) - physical_pages()[i] = PhysicalRAMPage::create(paddr.offset(i * PAGE_SIZE), MayReturnToFreeList::No); -} - -AnonymousVMObject::AnonymousVMObject(FixedArray>&& new_physical_pages) - : VMObject(move(new_physical_pages)) -{ -} - -AnonymousVMObject::AnonymousVMObject(LockWeakPtr other, NonnullLockRefPtr shared_committed_cow_pages, FixedArray>&& new_physical_pages) - : VMObject(move(new_physical_pages)) - , m_cow_parent(move(other)) - , m_shared_committed_cow_pages(move(shared_committed_cow_pages)) - , m_purgeable(m_cow_parent.strong_ref()->m_purgeable) -{ -} - -AnonymousVMObject::~AnonymousVMObject() -{ - if (!m_shared_committed_cow_pages || m_shared_committed_cow_pages->is_empty()) - return; - auto cow_parent = m_cow_parent.strong_ref(); - if (!cow_parent) - return; - SpinlockLocker lock(cow_parent->m_lock); - if (cow_parent->m_shared_committed_cow_pages == m_shared_committed_cow_pages) - cow_parent->m_shared_committed_cow_pages.clear(); -} - -size_t AnonymousVMObject::purge() -{ - SpinlockLocker lock(m_lock); - - if (!is_purgeable() || !is_volatile()) - return 0; - - size_t total_pages_purged = 0; - - for (auto& page : m_physical_pages) { - VERIFY(page); - if (page->is_shared_zero_page()) - continue; - page = MM.shared_zero_page(); - ++total_pages_purged; - } - - m_was_purged = true; - - for_each_region([](Region& region) { - region.remap(); - }); - - return total_pages_purged; -} - -ErrorOr AnonymousVMObject::set_volatile(bool is_volatile, bool& was_purged) -{ - VERIFY(is_purgeable()); - - SpinlockLocker locker(m_lock); - - was_purged = m_was_purged; - if (m_volatile == is_volatile) - return {}; - - if (is_volatile) { - // When a VMObject is made volatile, it gives up all of its committed memory. - // Any physical pages already allocated remain in the VMObject for now, but the kernel is free to take them at any moment. - for (auto& page : m_physical_pages) { - if (page && page->is_lazy_committed_page()) - page = MM.shared_zero_page(); - } - - m_unused_committed_pages = {}; - m_shared_committed_cow_pages = nullptr; - - if (!m_cow_map.is_null()) - m_cow_map = {}; - - m_volatile = true; - m_was_purged = false; - - for_each_region([&](auto& region) { region.remap(); }); - return {}; - } - // When a VMObject is made non-volatile, we try to commit however many pages are not currently available. - // If that fails, we return false to indicate that memory allocation failed. - size_t committed_pages_needed = 0; - for (auto& page : m_physical_pages) { - VERIFY(page); - if (page->is_shared_zero_page()) - ++committed_pages_needed; - } - - if (!committed_pages_needed) { - m_volatile = false; - return {}; - } - - m_unused_committed_pages = TRY(MM.commit_physical_pages(committed_pages_needed)); - - for (auto& page : m_physical_pages) { - if (page->is_shared_zero_page()) - page = MM.lazy_committed_page(); - } - - m_volatile = false; - m_was_purged = false; - for_each_region([&](auto& region) { region.remap(); }); - return {}; -} - -NonnullRefPtr AnonymousVMObject::allocate_committed_page(Badge) -{ - return m_unused_committed_pages->take_one(); -} - -ErrorOr AnonymousVMObject::ensure_cow_map() -{ - if (m_cow_map.is_null()) - m_cow_map = TRY(Bitmap::create(page_count(), true)); - return {}; -} - -ErrorOr AnonymousVMObject::ensure_or_reset_cow_map() -{ - if (m_cow_map.is_null()) - TRY(ensure_cow_map()); - else - m_cow_map.fill(true); - return {}; -} - -bool AnonymousVMObject::should_cow(size_t page_index, bool is_shared) const -{ - auto const& page = physical_pages()[page_index]; - if (page && (page->is_shared_zero_page() || page->is_lazy_committed_page())) - return true; - if (is_shared) - return false; - return !m_cow_map.is_null() && m_cow_map.get(page_index); -} - -ErrorOr AnonymousVMObject::set_should_cow(size_t page_index, bool cow) -{ - TRY(ensure_cow_map()); - m_cow_map.set(page_index, cow); - return {}; -} - -size_t AnonymousVMObject::cow_pages() const -{ - if (m_cow_map.is_null()) - return 0; - return m_cow_map.count_slow(true); -} - -PageFaultResponse AnonymousVMObject::handle_cow_fault(size_t page_index, VirtualAddress vaddr) -{ - SpinlockLocker lock(m_lock); - - if (is_volatile()) { - // A COW fault in a volatile region? Userspace is writing to volatile memory, this is a bug. Crash. - dbgln("COW fault in volatile region, will crash."); - return PageFaultResponse::ShouldCrash; - } - - auto& page_slot = physical_pages()[page_index]; - - // If we were sharing committed COW pages with another process, and the other process - // has exhausted the supply, we can stop counting the shared pages. - if (m_shared_committed_cow_pages && m_shared_committed_cow_pages->is_empty()) - m_shared_committed_cow_pages = nullptr; - - if (page_slot->ref_count() == 1) { - dbgln_if(PAGE_FAULT_DEBUG, " >> It's a COW page but nobody is sharing it anymore. Remap r/w"); - MUST(set_should_cow(page_index, false)); // If we received a COW fault, we already have a cow map allocated, so this is infallible - - if (m_shared_committed_cow_pages) { - m_shared_committed_cow_pages->uncommit_one(); - if (m_shared_committed_cow_pages->is_empty()) - m_shared_committed_cow_pages = nullptr; - } - return PageFaultResponse::Continue; - } - - RefPtr page; - if (m_shared_committed_cow_pages) { - dbgln_if(PAGE_FAULT_DEBUG, " >> It's a committed COW page and it's time to COW!"); - page = m_shared_committed_cow_pages->take_one(); - } else { - dbgln_if(PAGE_FAULT_DEBUG, " >> It's a COW page and it's time to COW!"); - auto page_or_error = MM.allocate_physical_page(MemoryManager::ShouldZeroFill::No); - if (page_or_error.is_error()) { - dmesgln("MM: handle_cow_fault was unable to allocate a physical page"); - return PageFaultResponse::OutOfMemory; - } - page = page_or_error.release_value(); - } - - dbgln_if(PAGE_FAULT_DEBUG, " >> COW {} <- {}", page->paddr(), page_slot->paddr()); - { - u8* dest_ptr = MM.quickmap_page(*page); - SmapDisabler disabler; - void* fault_at; - if (!safe_memcpy(dest_ptr, vaddr.as_ptr(), PAGE_SIZE, fault_at)) { - if ((u8*)fault_at >= dest_ptr && (u8*)fault_at <= dest_ptr + PAGE_SIZE) - dbgln(" >> COW: error copying page {}/{} to {}/{}: failed to write to page at {}", - page_slot->paddr(), vaddr, page->paddr(), VirtualAddress(dest_ptr), VirtualAddress(fault_at)); - else if ((u8*)fault_at >= vaddr.as_ptr() && (u8*)fault_at <= vaddr.as_ptr() + PAGE_SIZE) - dbgln(" >> COW: error copying page {}/{} to {}/{}: failed to read from page at {}", - page_slot->paddr(), vaddr, page->paddr(), VirtualAddress(dest_ptr), VirtualAddress(fault_at)); - else - VERIFY_NOT_REACHED(); - } - MM.unquickmap_page(); - } - page_slot = move(page); - MUST(set_should_cow(page_index, false)); // If we received a COW fault, we already have a cow map allocated, so this is infallible - return PageFaultResponse::Continue; -} - -AnonymousVMObject::SharedCommittedCowPages::SharedCommittedCowPages(CommittedPhysicalPageSet&& committed_pages) - : m_committed_pages(move(committed_pages)) -{ -} - -AnonymousVMObject::SharedCommittedCowPages::~SharedCommittedCowPages() = default; - -NonnullRefPtr AnonymousVMObject::SharedCommittedCowPages::take_one() -{ - SpinlockLocker locker(m_lock); - return m_committed_pages.take_one(); -} - -void AnonymousVMObject::SharedCommittedCowPages::uncommit_one() -{ - SpinlockLocker locker(m_lock); - m_committed_pages.uncommit_one(); -} - -} diff --git a/Kernel/Memory/AnonymousVMObject.h b/Kernel/Memory/AnonymousVMObject.h deleted file mode 100644 index d98e031be95..00000000000 --- a/Kernel/Memory/AnonymousVMObject.h +++ /dev/null @@ -1,93 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include - -namespace Kernel::Memory { - -class AnonymousVMObject final : public VMObject { -public: - virtual ~AnonymousVMObject() override; - - static ErrorOr> try_create_with_size(size_t, AllocationStrategy); - static ErrorOr> try_create_for_physical_range(PhysicalAddress paddr, size_t size); - static ErrorOr> try_create_with_physical_pages(Span>); - static ErrorOr> try_create_purgeable_with_size(size_t, AllocationStrategy); - static ErrorOr> try_create_physically_contiguous_with_size(size_t); - virtual ErrorOr> try_clone() override; - - [[nodiscard]] NonnullRefPtr allocate_committed_page(Badge); - PageFaultResponse handle_cow_fault(size_t, VirtualAddress); - size_t cow_pages() const; - bool should_cow(size_t page_index, bool) const; - ErrorOr set_should_cow(size_t page_index, bool); - - bool is_purgeable() const { return m_purgeable; } - bool is_volatile() const { return m_volatile; } - - ErrorOr set_volatile(bool is_volatile, bool& was_purged); - - size_t purge(); - -private: - class SharedCommittedCowPages; - - static ErrorOr> try_create_with_shared_cow(AnonymousVMObject const&, NonnullLockRefPtr, FixedArray>&&); - - explicit AnonymousVMObject(FixedArray>&&, AllocationStrategy, Optional); - explicit AnonymousVMObject(PhysicalAddress, FixedArray>&&); - explicit AnonymousVMObject(FixedArray>&&); - explicit AnonymousVMObject(LockWeakPtr, NonnullLockRefPtr, FixedArray>&&); - - virtual StringView class_name() const override { return "AnonymousVMObject"sv; } - - AnonymousVMObject& operator=(AnonymousVMObject const&) = delete; - AnonymousVMObject& operator=(AnonymousVMObject&&) = delete; - AnonymousVMObject(AnonymousVMObject&&) = delete; - - virtual bool is_anonymous() const override { return true; } - - ErrorOr ensure_cow_map(); - ErrorOr ensure_or_reset_cow_map(); - - Optional m_unused_committed_pages; - Bitmap m_cow_map; - - // AnonymousVMObject shares committed COW pages with cloned children (happens on fork) - class SharedCommittedCowPages final : public AtomicRefCounted { - AK_MAKE_NONCOPYABLE(SharedCommittedCowPages); - - public: - SharedCommittedCowPages() = delete; - - explicit SharedCommittedCowPages(CommittedPhysicalPageSet&&); - ~SharedCommittedCowPages(); - - [[nodiscard]] bool is_empty() const { return m_committed_pages.is_empty(); } - - [[nodiscard]] NonnullRefPtr take_one(); - void uncommit_one(); - - private: - Spinlock m_lock {}; - CommittedPhysicalPageSet m_committed_pages; - }; - - LockWeakPtr m_cow_parent; - LockRefPtr m_shared_committed_cow_pages; - - bool m_purgeable { false }; - bool m_volatile { false }; - bool m_was_purged { false }; -}; - -} diff --git a/Kernel/Memory/InodeVMObject.cpp b/Kernel/Memory/InodeVMObject.cpp deleted file mode 100644 index 5a8a97c3d6d..00000000000 --- a/Kernel/Memory/InodeVMObject.cpp +++ /dev/null @@ -1,99 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include - -namespace Kernel::Memory { - -InodeVMObject::InodeVMObject(Inode& inode, FixedArray>&& new_physical_pages, Bitmap dirty_pages) - : VMObject(move(new_physical_pages)) - , m_inode(inode) - , m_dirty_pages(move(dirty_pages)) -{ -} - -InodeVMObject::InodeVMObject(InodeVMObject const& other, FixedArray>&& new_physical_pages, Bitmap dirty_pages) - : VMObject(move(new_physical_pages)) - , m_inode(other.m_inode) - , m_dirty_pages(move(dirty_pages)) -{ - for (size_t i = 0; i < page_count(); ++i) - m_dirty_pages.set(i, other.m_dirty_pages.get(i)); -} - -InodeVMObject::~InodeVMObject() = default; - -size_t InodeVMObject::amount_clean() const -{ - size_t count = 0; - VERIFY(page_count() == m_dirty_pages.size()); - for (size_t i = 0; i < page_count(); ++i) { - if (!m_dirty_pages.get(i) && m_physical_pages[i]) - ++count; - } - return count * PAGE_SIZE; -} - -size_t InodeVMObject::amount_dirty() const -{ - size_t count = 0; - for (size_t i = 0; i < m_dirty_pages.size(); ++i) { - if (m_dirty_pages.get(i)) - ++count; - } - return count * PAGE_SIZE; -} - -int InodeVMObject::release_all_clean_pages() -{ - SpinlockLocker locker(m_lock); - - int count = 0; - for (size_t i = 0; i < page_count(); ++i) { - if (!m_dirty_pages.get(i) && m_physical_pages[i]) { - m_physical_pages[i] = nullptr; - ++count; - } - } - if (count) { - for_each_region([](auto& region) { - region.remap(); - }); - } - return count; -} - -int InodeVMObject::try_release_clean_pages(int page_amount) -{ - SpinlockLocker locker(m_lock); - - int count = 0; - for (size_t i = 0; i < page_count() && count < page_amount; ++i) { - if (!m_dirty_pages.get(i) && m_physical_pages[i]) { - m_physical_pages[i] = nullptr; - ++count; - } - } - if (count) { - for_each_region([](auto& region) { - region.remap(); - }); - } - return count; -} - -u32 InodeVMObject::writable_mappings() const -{ - u32 count = 0; - const_cast(*this).for_each_region([&](auto& region) { - if (region.is_writable()) - ++count; - }); - return count; -} - -} diff --git a/Kernel/Memory/InodeVMObject.h b/Kernel/Memory/InodeVMObject.h deleted file mode 100644 index 99e02ad32bc..00000000000 --- a/Kernel/Memory/InodeVMObject.h +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include - -namespace Kernel::Memory { - -class InodeVMObject : public VMObject { -public: - virtual ~InodeVMObject() override; - - Inode& inode() { return *m_inode; } - Inode const& inode() const { return *m_inode; } - - size_t amount_dirty() const; - size_t amount_clean() const; - - int release_all_clean_pages(); - int try_release_clean_pages(int page_amount); - - u32 writable_mappings() const; - -protected: - explicit InodeVMObject(Inode&, FixedArray>&&, Bitmap dirty_pages); - explicit InodeVMObject(InodeVMObject const&, FixedArray>&&, Bitmap dirty_pages); - - InodeVMObject& operator=(InodeVMObject const&) = delete; - InodeVMObject& operator=(InodeVMObject&&) = delete; - InodeVMObject(InodeVMObject&&) = delete; - - virtual bool is_inode() const final { return true; } - - NonnullRefPtr const m_inode; - Bitmap m_dirty_pages; -}; - -} diff --git a/Kernel/Memory/MMIOVMObject.cpp b/Kernel/Memory/MMIOVMObject.cpp deleted file mode 100644 index 58d7f72778f..00000000000 --- a/Kernel/Memory/MMIOVMObject.cpp +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright (c) 2024, Idan Horowitz - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include - -namespace Kernel::Memory { - -ErrorOr> MMIOVMObject::try_create_for_physical_range(PhysicalAddress paddr, size_t size) -{ - if (paddr.offset(size) < paddr) { - dbgln("Shenanigans! MMIOVMObject::try_create_for_physical_range({}, {}) would wrap around", paddr, size); - // Since we can't wrap around yet, let's pretend to OOM. - return ENOMEM; - } - - // FIXME: We have to make this allocation because VMObject determines the size of the VMObject based on the physical pages array - auto new_physical_pages = TRY(VMObject::try_create_physical_pages(size)); - - return adopt_nonnull_lock_ref_or_enomem(new (nothrow) MMIOVMObject(paddr, move(new_physical_pages))); -} - -MMIOVMObject::MMIOVMObject(PhysicalAddress paddr, FixedArray>&& new_physical_pages) - : VMObject(move(new_physical_pages)) -{ - VERIFY(paddr.page_base() == paddr); -} - -} diff --git a/Kernel/Memory/MMIOVMObject.h b/Kernel/Memory/MMIOVMObject.h deleted file mode 100644 index 638e9b25ed9..00000000000 --- a/Kernel/Memory/MMIOVMObject.h +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright (c) 2024, Idan Horowitz - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include - -namespace Kernel::Memory { - -class MMIOVMObject final : public VMObject { -public: - static ErrorOr> try_create_for_physical_range(PhysicalAddress paddr, size_t size); - - virtual ErrorOr> try_clone() override { return ENOTSUP; } - -private: - MMIOVMObject(PhysicalAddress, FixedArray>&&); - - virtual StringView class_name() const override { return "MMIOVMObject"sv; } -}; - -} diff --git a/Kernel/Memory/MappedROM.h b/Kernel/Memory/MappedROM.h deleted file mode 100644 index dfef6d1a116..00000000000 --- a/Kernel/Memory/MappedROM.h +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright (c) 2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include - -namespace Kernel::Memory { - -class MappedROM { -public: - u8 const* base() const { return region->vaddr().offset(offset).as_ptr(); } - u8 const* end() const { return base() + size; } - OwnPtr region; - size_t size { 0 }; - size_t offset { 0 }; - PhysicalAddress paddr; - - Optional find_chunk_starting_with(StringView prefix, size_t chunk_size) const - { - auto prefix_length = prefix.length(); - if (size < prefix_length) - return {}; - for (auto* candidate = base(); candidate <= end() - prefix_length; candidate += chunk_size) { - if (!__builtin_memcmp(prefix.characters_without_null_termination(), candidate, prefix.length())) - return paddr_of(candidate); - } - return {}; - } - - PhysicalAddress paddr_of(u8 const* ptr) const { return paddr.offset(ptr - this->base()); } -}; - -} diff --git a/Kernel/Memory/MemoryManager.cpp b/Kernel/Memory/MemoryManager.cpp deleted file mode 100644 index e1a99969d51..00000000000 --- a/Kernel/Memory/MemoryManager.cpp +++ /dev/null @@ -1,1544 +0,0 @@ -/* - * Copyright (c) 2018-2022, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -extern u8 start_of_kernel_image[]; -extern u8 end_of_kernel_image[]; -extern u8 start_of_kernel_text[]; -extern u8 start_of_kernel_data[]; -extern u8 end_of_kernel_bss[]; -extern u8 start_of_ro_after_init[]; -extern u8 end_of_ro_after_init[]; -extern u8 start_of_unmap_after_init[]; -extern u8 end_of_unmap_after_init[]; -extern u8 start_of_kernel_ksyms[]; -extern u8 end_of_kernel_ksyms[]; - -extern multiboot_module_entry_t multiboot_copy_boot_modules_array[16]; -extern size_t multiboot_copy_boot_modules_count; - -namespace Kernel::Memory { - -ErrorOr page_round_up(FlatPtr x) -{ - if (x > (explode_byte(0xFF) & ~0xFFF)) { - return Error::from_errno(EINVAL); - } - return (((FlatPtr)(x)) + PAGE_SIZE - 1) & (~(PAGE_SIZE - 1)); -} - -// NOTE: We can NOT use Singleton for this class, because -// MemoryManager::initialize is called *before* global constructors are -// run. If we do, then Singleton would get re-initialized, causing -// the memory manager to be initialized twice! -static MemoryManager* s_the; - -MemoryManager& MemoryManager::the() -{ - return *s_the; -} - -bool MemoryManager::is_initialized() -{ - return s_the != nullptr; -} - -static UNMAP_AFTER_INIT VirtualRange kernel_virtual_range() -{ -#if ARCH(X86_64) - size_t kernel_range_start = kernel_mapping_base + 2 * MiB; // The first 2 MiB are used for mapping the pre-kernel - return VirtualRange { VirtualAddress(kernel_range_start), KERNEL_PD_END - kernel_range_start }; -#elif ARCH(AARCH64) || ARCH(RISCV64) - // NOTE: This is not the same as x86_64, because the aarch64 and riscv64 kernels currently don't use the pre-kernel. - return VirtualRange { VirtualAddress(kernel_mapping_base), KERNEL_PD_END - kernel_mapping_base }; -#else -# error Unknown architecture -#endif -} - -MemoryManager::GlobalData::GlobalData() - : region_tree(kernel_virtual_range()) -{ -} - -UNMAP_AFTER_INIT MemoryManager::MemoryManager() -{ - s_the = this; - - parse_memory_map(); - activate_kernel_page_directory(kernel_page_directory()); - protect_kernel_image(); - - // We're temporarily "committing" to two pages that we need to allocate below - auto committed_pages = commit_physical_pages(2).release_value(); - - m_shared_zero_page = committed_pages.take_one(); - - // We're wasting a page here, we just need a special tag (physical - // address) so that we know when we need to lazily allocate a page - // that we should be drawing this page from the committed pool rather - // than potentially failing if no pages are available anymore. - // By using a tag we don't have to query the VMObject for every page - // whether it was committed or not - m_lazy_committed_page = committed_pages.take_one(); - -#ifdef HAS_ADDRESS_SANITIZER - initialize_kasan_shadow_memory(); -#endif -} - -UNMAP_AFTER_INIT MemoryManager::~MemoryManager() = default; - -UNMAP_AFTER_INIT void MemoryManager::protect_kernel_image() -{ - SpinlockLocker page_lock(kernel_page_directory().get_lock()); - // Disable writing to the kernel text and rodata segments. - for (auto const* i = start_of_kernel_text; i < start_of_kernel_data; i += PAGE_SIZE) { - auto& pte = *ensure_pte(kernel_page_directory(), VirtualAddress(i)); - pte.set_writable(false); - } - if (Processor::current().has_nx()) { - // Disable execution of the kernel data, bss and heap segments. - for (auto const* i = start_of_kernel_data; i < end_of_kernel_image; i += PAGE_SIZE) { - auto& pte = *ensure_pte(kernel_page_directory(), VirtualAddress(i)); - pte.set_execute_disabled(true); - } - } -} - -UNMAP_AFTER_INIT void MemoryManager::unmap_prekernel() -{ - SpinlockLocker page_lock(kernel_page_directory().get_lock()); - - auto start = start_of_prekernel_image.page_base().get(); - auto end = end_of_prekernel_image.page_base().get(); - - for (auto i = start; i <= end; i += PAGE_SIZE) - release_pte(kernel_page_directory(), VirtualAddress(i), i == end ? IsLastPTERelease::Yes : IsLastPTERelease::No); - flush_tlb(&kernel_page_directory(), VirtualAddress(start), (end - start) / PAGE_SIZE); -} - -UNMAP_AFTER_INIT void MemoryManager::protect_readonly_after_init_memory() -{ - SpinlockLocker page_lock(kernel_page_directory().get_lock()); - // Disable writing to the .ro_after_init section - for (auto i = (FlatPtr)&start_of_ro_after_init; i < (FlatPtr)&end_of_ro_after_init; i += PAGE_SIZE) { - auto& pte = *ensure_pte(kernel_page_directory(), VirtualAddress(i)); - pte.set_writable(false); - flush_tlb(&kernel_page_directory(), VirtualAddress(i)); - } -} - -void MemoryManager::unmap_text_after_init() -{ - SpinlockLocker page_lock(kernel_page_directory().get_lock()); - - auto start = page_round_down((FlatPtr)&start_of_unmap_after_init); - auto end = page_round_up((FlatPtr)&end_of_unmap_after_init).release_value_but_fixme_should_propagate_errors(); - - // Unmap the entire .unmap_after_init section - for (auto i = start; i < end; i += PAGE_SIZE) { - auto& pte = *ensure_pte(kernel_page_directory(), VirtualAddress(i)); - pte.clear(); - flush_tlb(&kernel_page_directory(), VirtualAddress(i)); - } - - dmesgln("Unmapped {} KiB of kernel text after init! :^)", (end - start) / KiB); -} - -UNMAP_AFTER_INIT void MemoryManager::protect_ksyms_after_init() -{ - SpinlockLocker page_lock(kernel_page_directory().get_lock()); - - auto start = page_round_down((FlatPtr)start_of_kernel_ksyms); - auto end = page_round_up((FlatPtr)end_of_kernel_ksyms).release_value_but_fixme_should_propagate_errors(); - - for (auto i = start; i < end; i += PAGE_SIZE) { - auto& pte = *ensure_pte(kernel_page_directory(), VirtualAddress(i)); - pte.set_writable(false); - flush_tlb(&kernel_page_directory(), VirtualAddress(i)); - } - - dmesgln("Write-protected kernel symbols after init."); -} - -IterationDecision MemoryManager::for_each_physical_memory_range(Function callback) -{ - return m_global_data.with([&](auto& global_data) { - VERIFY(!global_data.physical_memory_ranges.is_empty()); - for (auto& current_range : global_data.physical_memory_ranges) { - IterationDecision decision = callback(current_range); - if (decision != IterationDecision::Continue) - return decision; - } - return IterationDecision::Continue; - }); -} - -UNMAP_AFTER_INIT void MemoryManager::register_reserved_ranges() -{ - m_global_data.with([&](auto& global_data) { - VERIFY(!global_data.physical_memory_ranges.is_empty()); - ContiguousReservedMemoryRange range; - for (auto& current_range : global_data.physical_memory_ranges) { - if (current_range.type != PhysicalMemoryRangeType::Reserved) { - if (range.start.is_null()) - continue; - global_data.reserved_memory_ranges.append(ContiguousReservedMemoryRange { range.start, current_range.start.get() - range.start.get() }); - range.start.set((FlatPtr) nullptr); - continue; - } - if (!range.start.is_null()) { - continue; - } - range.start = current_range.start; - } - if (global_data.physical_memory_ranges.last().type != PhysicalMemoryRangeType::Reserved) - return; - if (range.start.is_null()) - return; - global_data.reserved_memory_ranges.append(ContiguousReservedMemoryRange { range.start, global_data.physical_memory_ranges.last().start.get() + global_data.physical_memory_ranges.last().length - range.start.get() }); - }); -} - -bool MemoryManager::is_allowed_to_read_physical_memory_for_userspace(PhysicalAddress start_address, size_t read_length) const -{ - // Note: Guard against overflow in case someone tries to mmap on the edge of - // the RAM - if (start_address.offset_addition_would_overflow(read_length)) - return false; - auto end_address = start_address.offset(read_length); - - return m_global_data.with([&](auto& global_data) { - for (auto const& current_range : global_data.reserved_memory_ranges) { - if (current_range.start > start_address) - continue; - if (current_range.start.offset(current_range.length) < end_address) - continue; - return true; - } - return false; - }); -} - -UNMAP_AFTER_INIT void MemoryManager::parse_memory_map() -{ - // Register used memory regions that we know of. - m_global_data.with([this](auto& global_data) { - global_data.used_memory_ranges.ensure_capacity(4); -#if ARCH(X86_64) - // NOTE: We don't touch the first 1 MiB of RAM on x86-64 even if it's usable as indicated - // by a certain memory map. There are 2 reasons for this: - // - // The first reason is specified for Linux doing the same thing in - // https://cateee.net/lkddb/web-lkddb/X86_RESERVE_LOW.html - - // "By default we reserve the first 64K of physical RAM, as a number of BIOSes are known - // to corrupt that memory range during events such as suspend/resume or monitor cable insertion, - // so it must not be used by the kernel." - // - // Linux also allows configuring this knob in compiletime for this reserved range length, that might - // also include the EBDA and other potential ranges in the first 1 MiB that could be corrupted by the BIOS: - // "You can set this to 4 if you are absolutely sure that you trust the BIOS to get all its memory - // reservations and usages right. If you know your BIOS have problems beyond the default 64K area, - // you can set this to 640 to avoid using the entire low memory range." - // - // The second reason is that the first 1 MiB memory range should also include the actual BIOS blob - // together with possible execution blob code for various option ROMs, which should not be touched - // by our kernel. - // - // **To be completely on the safe side** and never worry about where the EBDA is located, how BIOS might - // corrupt the low memory range during power state changing, other bad behavior of some BIOS might change - // a value in the very first 64k bytes of RAM, etc - we should just ignore this range completely. - global_data.used_memory_ranges.append(UsedMemoryRange { UsedMemoryRangeType::LowMemory, PhysicalAddress(0x00000000), PhysicalAddress(1 * MiB) }); -#endif - global_data.used_memory_ranges.append(UsedMemoryRange { UsedMemoryRangeType::Kernel, PhysicalAddress(virtual_to_low_physical((FlatPtr)start_of_kernel_image)), PhysicalAddress(page_round_up(virtual_to_low_physical((FlatPtr)end_of_kernel_image)).release_value_but_fixme_should_propagate_errors()) }); - -#if ARCH(RISCV64) - // FIXME: AARCH64 might be able to make use of this code path - // Some x86 platforms also provide flattened device trees - parse_memory_map_fdt(global_data, s_fdt_storage); -#else - parse_memory_map_multiboot(global_data); -#endif - - // Now we need to setup the physical regions we will use later - struct ContiguousPhysicalVirtualRange { - PhysicalAddress lower; - PhysicalAddress upper; - }; - Optional last_contiguous_physical_range; - for (auto range : global_data.physical_memory_ranges) { - if (range.type != PhysicalMemoryRangeType::Usable) - continue; - auto address = range.start.get(); - auto length = range.length; - - // Fix up unaligned memory regions. - auto diff = (FlatPtr)address % PAGE_SIZE; - if (diff != 0) { - dmesgln("MM: Got an unaligned usable physical_region from the bootloader; correcting {:p} by {} bytes", address, diff); - diff = PAGE_SIZE - diff; - address += diff; - length -= diff; - } - if ((length % PAGE_SIZE) != 0) { - dmesgln("MM: Got an unaligned usable physical_region from the bootloader; correcting length {} by {} bytes", length, length % PAGE_SIZE); - length -= length % PAGE_SIZE; - } - if (length < PAGE_SIZE) { - dmesgln("MM: Memory usable physical_region from bootloader is too small; we want >= {} bytes, but got {} bytes", PAGE_SIZE, length); - continue; - } - - // FIXME: This might have a nicer solution than slicing the ranges apart, - // to just put them back together when we dont find a used range in them - for (PhysicalSize page_base = address; page_base <= (address + length); page_base += PAGE_SIZE) { - auto addr = PhysicalAddress(page_base); - - // Skip used memory ranges. - bool should_skip = false; - for (auto& used_range : global_data.used_memory_ranges) { - if (addr.get() >= used_range.start.get() && addr.get() <= used_range.end.get()) { - should_skip = true; - page_base = used_range.end.get(); - break; - } - } - if (should_skip) - continue; - - if (!last_contiguous_physical_range.has_value() || last_contiguous_physical_range->upper.offset(PAGE_SIZE) != addr) { - if (last_contiguous_physical_range.has_value()) { - auto range = last_contiguous_physical_range.release_value(); - // FIXME: OOM? - global_data.physical_regions.append(PhysicalRegion::try_create(range.lower, range.upper).release_nonnull()); - } - last_contiguous_physical_range = ContiguousPhysicalVirtualRange { .lower = addr, .upper = addr }; - } else { - last_contiguous_physical_range->upper = addr; - } - } - // FIXME: If this is ever false, theres a good chance that all physical memory is already spent - if (last_contiguous_physical_range.has_value()) { - auto range = last_contiguous_physical_range.release_value(); - // FIXME: OOM? - global_data.physical_regions.append(PhysicalRegion::try_create(range.lower, range.upper).release_nonnull()); - } - } - - for (auto& region : global_data.physical_regions) - global_data.system_memory_info.physical_pages += region->size(); - - register_reserved_ranges(); - for (auto& range : global_data.reserved_memory_ranges) { - dmesgln("MM: Contiguous reserved range from {}, length is {}", range.start, range.length); - } - - initialize_physical_pages(); - - VERIFY(global_data.system_memory_info.physical_pages > 0); - - // We start out with no committed pages - global_data.system_memory_info.physical_pages_uncommitted = global_data.system_memory_info.physical_pages; - - for (auto& used_range : global_data.used_memory_ranges) { - dmesgln("MM: {} range @ {} - {} (size {:#x})", UserMemoryRangeTypeNames[to_underlying(used_range.type)], used_range.start, used_range.end.offset(-1), used_range.end.as_ptr() - used_range.start.as_ptr()); - } - - for (auto& region : global_data.physical_regions) { - dmesgln("MM: User physical region: {} - {} (size {:#x})", region->lower(), region->upper().offset(-1), PAGE_SIZE * region->size()); - region->initialize_zones(); - } - }); -} - -UNMAP_AFTER_INIT void MemoryManager::parse_memory_map_fdt(MemoryManager::GlobalData& global_data, u8 const* fdt_addr) -{ - auto const& fdt_header = *reinterpret_cast(fdt_addr); - auto fdt_buffer = ReadonlyBytes(fdt_addr, fdt_header.totalsize); - - auto const* mem_reserve_block = reinterpret_cast(&fdt_buffer[fdt_header.off_mem_rsvmap]); - - u64 next_block_offset = fdt_header.off_mem_rsvmap + sizeof(DeviceTree::FlattenedDeviceTreeReserveEntry); - while ((next_block_offset < fdt_header.off_dt_struct) && (*mem_reserve_block != DeviceTree::FlattenedDeviceTreeReserveEntry {})) { - dbgln("MM: Reserved Range /memreserve/: address: {} size {:#x}", PhysicalAddress { mem_reserve_block->address }, mem_reserve_block->size); - global_data.physical_memory_ranges.append(PhysicalMemoryRange { PhysicalMemoryRangeType::Reserved, PhysicalAddress { mem_reserve_block->address }, mem_reserve_block->size }); - // FIXME: Not all of these are "used", only those in "memory" are actually "used" - global_data.used_memory_ranges.append(UsedMemoryRange { UsedMemoryRangeType::BootModule, PhysicalAddress { mem_reserve_block->address }, PhysicalAddress { mem_reserve_block->address + mem_reserve_block->size } }); - ++mem_reserve_block; - next_block_offset += sizeof(DeviceTree::FlattenedDeviceTreeReserveEntry); - } - - // Schema: - // https://github.com/devicetree-org/dt-schema/blob/main/dtschema/schemas/root-node.yaml - // -> /#address-cells ∈ [1,2], /#size-cells ∈ [1,2] - // Reserved Memory: - // https://android.googlesource.com/kernel/msm/+/android-7.1.0_r0.2/Documentation/devicetree/bindings/reserved-memory/reserved-memory.txt - // -> #address-cells === /#address-cells, #size-cells === /#size-cells - // https://github.com/devicetree-org/dt-schema/blob/main/dtschema/schemas/reserved-memory/reserved-memory.yaml - // Memory: - // https://github.com/devicetree-org/dt-schema/blob/main/dtschema/schemas/memory.yaml - // -> #address-cells: /#address-cells , #size-cells: /#size-cells - - // FIXME: When booting from UEFI, the /memory node may not be relied upon - enum class State { - Root, - InReservedMemory, - InReservedMemoryChild, - - InMemory - }; - - struct { - u32 depth = 0; - State state = State::Root; - Optional start {}; - Optional size {}; - u32 address_cells = 0; - u32 size_cells = 0; - } state; - - MUST(DeviceTree::walk_device_tree( - fdt_header, fdt_buffer, - DeviceTree::DeviceTreeCallbacks { - .on_node_begin = [&state](StringView node_name) -> ErrorOr { - switch (state.state) { - case State::Root: - if (state.depth != 1) - break; - if (node_name == "reserved-memory") - state.state = State::InReservedMemory; - else if (node_name.starts_with("memory"sv)) - state.state = State::InMemory; - break; - case State::InReservedMemory: - // FIXME: The node names may hint to the purpose - state.state = State::InReservedMemoryChild; - state.start = {}; - state.size = {}; - break; - case State::InReservedMemoryChild: - case State::InMemory: - // We should never be here - VERIFY_NOT_REACHED(); - } - state.depth++; - return IterationDecision::Continue; - }, - .on_node_end = [&global_data, &state](StringView node_name) -> ErrorOr { - switch (state.state) { - case State::Root: - break; - case State::InReservedMemory: - state.state = State::Root; - break; - case State::InMemory: - VERIFY(state.start.has_value() && state.size.has_value()); - dbgln("MM: Memory Range {}: address: {} size {:#x}", node_name, PhysicalAddress { state.start.value() }, state.size.value()); - global_data.physical_memory_ranges.append(PhysicalMemoryRange { PhysicalMemoryRangeType::Usable, PhysicalAddress { state.start.value() }, state.size.value() }); - state.state = State::Root; - break; - case State::InReservedMemoryChild: - // FIXME: Handle non static allocations, - if (!state.start.has_value()) { - VERIFY(state.size.has_value()); - dbgln("MM: Non static reserved memory range {} of size {:#x}, skipping for now", node_name, state.size.value()); - state.state = State::InReservedMemory; - break; - } - VERIFY(state.start.has_value() && state.size.has_value()); - dbgln("MM: Reserved Range {}: address: {} size {:#x}", node_name, PhysicalAddress { state.start.value() }, state.size.value()); - global_data.physical_memory_ranges.append(PhysicalMemoryRange { PhysicalMemoryRangeType::Reserved, PhysicalAddress { state.start.value() }, state.size.value() }); - // FIXME: Not all of these are "used", only those in "memory" are actually "used" - // There might be for example debug DMA control registers, which are marked as reserved - global_data.used_memory_ranges.append(UsedMemoryRange { UsedMemoryRangeType::BootModule, PhysicalAddress { state.start.value() }, PhysicalAddress { state.start.value() + state.size.value() } }); - state.state = State::InReservedMemory; - break; - } - state.depth--; - return IterationDecision::Continue; - }, - .on_property = [&state](StringView property_name, ReadonlyBytes data) -> ErrorOr { - switch (state.state) { - case State::Root: - if (state.depth != 1) - break; - if (property_name == "#address-cells"sv) { - BigEndian data_as_int; - __builtin_memcpy(&data_as_int, data.data(), sizeof(u32)); - state.address_cells = data_as_int; - VERIFY(state.address_cells != 0); - VERIFY(state.address_cells <= 2); - } else if (property_name == "#size-cells"sv) { - BigEndian data_as_int; - __builtin_memcpy(&data_as_int, data.data(), sizeof(u32)); - state.size_cells = data_as_int; - VERIFY(state.size_cells != 0); - VERIFY(state.size_cells <= 2); - } - break; - case State::InReservedMemory: - // FIXME: We could check and verify that the address and size cells - // are the same as in the root node - // FIXME: Handle the ranges attribute if not empty - if (property_name == "ranges"sv && data.size() != 0) - TODO(); - break; - case State::InReservedMemoryChild: - case State::InMemory: - if (property_name == "reg"sv) { - VERIFY(state.address_cells); - VERIFY(state.size_cells); - // FIXME: We may get more than one range here - if (data.size() > (state.address_cells + state.size_cells) * sizeof(u32)) - TODO(); - if (state.address_cells == 1) { - BigEndian data_as_int; - __builtin_memcpy(&data_as_int, data.data(), sizeof(u32)); - state.start = data_as_int; - data = data.slice(sizeof(u32)); - } else { - BigEndian data_as_int; - __builtin_memcpy(&data_as_int, data.data(), sizeof(u64)); - state.start = data_as_int; - data = data.slice(sizeof(u64)); - } - - if (state.size_cells == 1) { - BigEndian data_as_int; - __builtin_memcpy(&data_as_int, data.data(), sizeof(u32)); - state.size = data_as_int; - data = data.slice(sizeof(u32)); - } else { - BigEndian data_as_int; - __builtin_memcpy(&data_as_int, data.data(), sizeof(u64)); - state.size = data_as_int; - data = data.slice(sizeof(u64)); - } - } else { - // Reserved Memory: - // FIXME: Handle `compatible: "framebuffer";` - // FIMXE: Handle `compatible: "shared-dma-pool";`, `compatible: "restricted-dma-pool";` - // FIXME: Handle "iommu-addresses" property - // FIXME: Support "size" and "align" property - // Also "alloc-ranges" - // FIXME: Support no-map - // FIXME: Support no-map-fixup - // FIXME: Support reusable - } - break; - } - - return IterationDecision::Continue; - }, - .on_noop = []() -> ErrorOr { return IterationDecision::Continue; }, - .on_end = []() -> ErrorOr { return {}; }, - })); - - // FDTs do not seem to be fully sort memory ranges, especially as we get them from at least two structures - quick_sort(global_data.physical_memory_ranges, [](auto& a, auto& b) -> bool { return a.start > b.start; }); -} - -UNMAP_AFTER_INIT void MemoryManager::parse_memory_map_multiboot(MemoryManager::GlobalData& global_data) -{ - // Register used memory regions that we know of. - if (multiboot_flags & 0x4 && !multiboot_module_physical_ptr.is_null()) { - dmesgln("MM: Multiboot module @ {}, length={}", multiboot_module_physical_ptr, multiboot_module_length); - VERIFY(multiboot_module_length != 0); - global_data.used_memory_ranges.append(UsedMemoryRange { UsedMemoryRangeType::BootModule, multiboot_module_physical_ptr, multiboot_module_physical_ptr.offset(multiboot_module_length) }); - } - - auto* mmap_begin = multiboot_memory_map; - auto* mmap_end = multiboot_memory_map + multiboot_memory_map_count; - - struct ContiguousPhysicalVirtualRange { - PhysicalAddress lower; - PhysicalAddress upper; - }; - - Optional last_contiguous_physical_range; - for (auto* mmap = mmap_begin; mmap < mmap_end; mmap++) { - // We have to copy these onto the stack, because we take a reference to these when printing them out, - // and doing so on a packed struct field is UB. - auto address = mmap->addr; - auto length = mmap->len; - ArmedScopeGuard write_back_guard = [&]() { - mmap->addr = address; - mmap->len = length; - }; - - dmesgln("MM: Multiboot mmap: address={:p}, length={}, type={}", address, length, mmap->type); - - auto start_address = PhysicalAddress(address); - switch (mmap->type) { - case (MULTIBOOT_MEMORY_AVAILABLE): - global_data.physical_memory_ranges.append(PhysicalMemoryRange { PhysicalMemoryRangeType::Usable, start_address, length }); - break; - case (MULTIBOOT_MEMORY_RESERVED): -#if ARCH(X86_64) - // Workaround for https://gitlab.com/qemu-project/qemu/-/commit/8504f129450b909c88e199ca44facd35d38ba4de - // That commit added a reserved 12GiB entry for the benefit of virtual firmware. - // We can safely ignore this block as it isn't actually reserved on any real hardware. - // From: https://lore.kernel.org/all/20220701161014.3850-1-joao.m.martins@oracle.com/ - // "Always add the HyperTransport range into e820 even when the relocation isn't - // done *and* there's >= 40 phys bit that would put max phyusical boundary to 1T - // This should allow virtual firmware to avoid the reserved range at the - // 1T boundary on VFs with big bars." - if (address != 0x000000fd00000000 || length != (0x000000ffffffffff - 0x000000fd00000000) + 1) -#endif - global_data.physical_memory_ranges.append(PhysicalMemoryRange { PhysicalMemoryRangeType::Reserved, start_address, length }); - break; - case (MULTIBOOT_MEMORY_ACPI_RECLAIMABLE): - global_data.physical_memory_ranges.append(PhysicalMemoryRange { PhysicalMemoryRangeType::ACPI_Reclaimable, start_address, length }); - break; - case (MULTIBOOT_MEMORY_NVS): - global_data.physical_memory_ranges.append(PhysicalMemoryRange { PhysicalMemoryRangeType::ACPI_NVS, start_address, length }); - break; - case (MULTIBOOT_MEMORY_BADRAM): - dmesgln("MM: Warning, detected bad memory range!"); - global_data.physical_memory_ranges.append(PhysicalMemoryRange { PhysicalMemoryRangeType::BadMemory, start_address, length }); - break; - default: - dbgln("MM: Unknown range!"); - global_data.physical_memory_ranges.append(PhysicalMemoryRange { PhysicalMemoryRangeType::Unknown, start_address, length }); - break; - } - } -} - -UNMAP_AFTER_INIT void MemoryManager::initialize_physical_pages() -{ - m_global_data.with([&](auto& global_data) { - // We assume that the physical page range is contiguous and doesn't contain huge gaps! - PhysicalAddress highest_physical_address; -#if ARCH(X86_64) - // On x86 LAPIC is at 0xfee00000 or a similar address. Round up to 0x100000000LL to cover variations. - highest_physical_address = PhysicalAddress { 0x100000000LL }; -#endif - for (auto& range : global_data.used_memory_ranges) { - if (range.end.get() > highest_physical_address.get()) - highest_physical_address = range.end; - } - for (auto& region : global_data.physical_memory_ranges) { - auto range_end = PhysicalAddress(region.start).offset(region.length); - if (range_end.get() > highest_physical_address.get()) - highest_physical_address = range_end; - } - -#if ARCH(X86_64) - // Map multiboot framebuffer - if ((multiboot_flags & MULTIBOOT_INFO_FRAMEBUFFER_INFO) && !multiboot_framebuffer_addr.is_null() && multiboot_framebuffer_type == MULTIBOOT_FRAMEBUFFER_TYPE_RGB) { - PhysicalAddress multiboot_framebuffer_addr_end = multiboot_framebuffer_addr.offset(multiboot_framebuffer_height * multiboot_framebuffer_pitch); - if (multiboot_framebuffer_addr_end > highest_physical_address) - highest_physical_address = multiboot_framebuffer_addr_end; - } -#endif - - // Calculate how many total physical pages the array will have - m_physical_page_entries_count = PhysicalAddress::physical_page_index(highest_physical_address.get()) + 1; - VERIFY(m_physical_page_entries_count != 0); - VERIFY(!Checked::multiplication_would_overflow(m_physical_page_entries_count, sizeof(PhysicalPageEntry))); - - // Calculate how many bytes the array will consume - auto physical_page_array_size = m_physical_page_entries_count * sizeof(PhysicalPageEntry); - auto physical_page_array_pages = page_round_up(physical_page_array_size).release_value_but_fixme_should_propagate_errors() / PAGE_SIZE; - VERIFY(physical_page_array_pages * PAGE_SIZE >= physical_page_array_size); - - // Calculate how many page tables we will need to be able to map them all - auto needed_page_table_count = (physical_page_array_pages + 512 - 1) / 512; - - auto physical_page_array_pages_and_page_tables_count = physical_page_array_pages + needed_page_table_count; - - // Now that we know how much memory we need for a contiguous array of PhysicalPage instances, find a memory region that can fit it - PhysicalRegion* found_region { nullptr }; - Optional found_region_index; - for (size_t i = 0; i < global_data.physical_regions.size(); ++i) { - auto& region = global_data.physical_regions[i]; - if (region->size() >= physical_page_array_pages_and_page_tables_count) { - found_region = region; - found_region_index = i; - break; - } - } - - if (!found_region) { - dmesgln("MM: Need {} bytes for physical page management, but no memory region is large enough!", physical_page_array_pages_and_page_tables_count); - VERIFY_NOT_REACHED(); - } - - VERIFY(global_data.system_memory_info.physical_pages >= physical_page_array_pages_and_page_tables_count); - global_data.system_memory_info.physical_pages -= physical_page_array_pages_and_page_tables_count; - - if (found_region->size() == physical_page_array_pages_and_page_tables_count) { - // We're stealing the entire region - global_data.physical_pages_region = global_data.physical_regions.take(*found_region_index); - } else { - global_data.physical_pages_region = found_region->try_take_pages_from_beginning(physical_page_array_pages_and_page_tables_count); - } - global_data.used_memory_ranges.append({ UsedMemoryRangeType::PhysicalPages, global_data.physical_pages_region->lower(), global_data.physical_pages_region->upper() }); - - // Create the bare page directory. This is not a fully constructed page directory and merely contains the allocators! - m_kernel_page_directory = PageDirectory::must_create_kernel_page_directory(); - - { - // Carve out the whole page directory covering the kernel image to make MemoryManager::initialize_physical_pages() happy - FlatPtr start_of_range = ((FlatPtr)start_of_kernel_image & ~(FlatPtr)0x1fffff); - FlatPtr end_of_range = ((FlatPtr)end_of_kernel_image & ~(FlatPtr)0x1fffff) + 0x200000; - MUST(global_data.region_tree.place_specifically(*MUST(Region::create_unbacked()).leak_ptr(), VirtualRange { VirtualAddress(start_of_range), end_of_range - start_of_range })); - } - - // Allocate a virtual address range for our array - // This looks awkward, but it basically creates a dummy region to occupy the address range permanently. - auto& region = *MUST(Region::create_unbacked()).leak_ptr(); - MUST(global_data.region_tree.place_anywhere(region, RandomizeVirtualAddress::No, physical_page_array_pages * PAGE_SIZE)); - auto range = region.range(); - - // Now that we have our special m_physical_pages_region region with enough pages to hold the entire array - // try to map the entire region into kernel space so we always have it - // We can't use ensure_pte here because it would try to allocate a PhysicalPage and we don't have the array - // mapped yet so we can't create them - - // Create page tables at the beginning of m_physical_pages_region, followed by the PhysicalPageEntry array - auto page_tables_base = global_data.physical_pages_region->lower(); - auto physical_page_array_base = page_tables_base.offset(needed_page_table_count * PAGE_SIZE); - auto physical_page_array_current_page = physical_page_array_base.get(); - auto virtual_page_array_base = range.base().get(); - auto virtual_page_array_current_page = virtual_page_array_base; - for (size_t pt_index = 0; pt_index < needed_page_table_count; pt_index++) { - auto virtual_page_base_for_this_pt = virtual_page_array_current_page; - auto pt_paddr = page_tables_base.offset(pt_index * PAGE_SIZE); - auto* pt = reinterpret_cast(quickmap_page(pt_paddr)); - __builtin_memset(pt, 0, PAGE_SIZE); - for (size_t pte_index = 0; pte_index < PAGE_SIZE / sizeof(PageTableEntry); pte_index++) { - auto& pte = pt[pte_index]; - pte.set_physical_page_base(physical_page_array_current_page); - pte.set_user_allowed(false); - pte.set_writable(true); - if (Processor::current().has_nx()) - pte.set_execute_disabled(false); - pte.set_global(true); - pte.set_present(true); - - physical_page_array_current_page += PAGE_SIZE; - virtual_page_array_current_page += PAGE_SIZE; - } - unquickmap_page(); - - // Hook the page table into the kernel page directory - u32 page_directory_index = (virtual_page_base_for_this_pt >> 21) & 0x1ff; - auto* pd = reinterpret_cast(quickmap_page(boot_pd_kernel)); - PageDirectoryEntry& pde = pd[page_directory_index]; - - VERIFY(!pde.is_present()); // Nothing should be using this PD yet - - // We can't use ensure_pte quite yet! - pde.set_page_table_base(pt_paddr.get()); - pde.set_user_allowed(false); - pde.set_present(true); - pde.set_writable(true); - pde.set_global(true); - - unquickmap_page(); - - flush_tlb_local(VirtualAddress(virtual_page_base_for_this_pt)); - } - - // We now have the entire PhysicalPageEntry array mapped! - m_physical_page_entries = (PhysicalPageEntry*)range.base().get(); - for (size_t i = 0; i < m_physical_page_entries_count; i++) - new (&m_physical_page_entries[i]) PageTableEntry(); - - // Now we should be able to allocate PhysicalPage instances, - // so finish setting up the kernel page directory - m_kernel_page_directory->allocate_kernel_directory(); - - // Now create legit PhysicalPage objects for the page tables we created. - virtual_page_array_current_page = virtual_page_array_base; - for (size_t pt_index = 0; pt_index < needed_page_table_count; pt_index++) { - VERIFY(virtual_page_array_current_page <= range.end().get()); - auto pt_paddr = page_tables_base.offset(pt_index * PAGE_SIZE); - auto physical_page_index = PhysicalAddress::physical_page_index(pt_paddr.get()); - auto& physical_page_entry = m_physical_page_entries[physical_page_index]; - auto physical_page = adopt_lock_ref(*new (&physical_page_entry.allocated.physical_page) PhysicalRAMPage(MayReturnToFreeList::No)); - - // NOTE: This leaked ref is matched by the unref in MemoryManager::release_pte() - (void)physical_page.leak_ref(); - - virtual_page_array_current_page += (PAGE_SIZE / sizeof(PageTableEntry)) * PAGE_SIZE; - } - - dmesgln("MM: Physical page entries: {}", range); - }); -} - -#ifdef HAS_ADDRESS_SANITIZER -void MemoryManager::initialize_kasan_shadow_memory() -{ - m_global_data.with([&](auto& global_data) { - // We map every 8 bytes of normal memory to 1 byte of shadow memory, so we need a 1/9 of total memory for the shadow memory. - auto virtual_range = global_data.region_tree.total_range(); - auto shadow_range_size = MUST(page_round_up(ceil_div(virtual_range.size(), 9ul))); - dbgln("MM: Reserving {} bytes for KASAN shadow memory", shadow_range_size); - - auto vmobject = MUST(AnonymousVMObject::try_create_with_size(shadow_range_size, AllocationStrategy::AllocateNow)); - auto* shadow_region = MUST(Region::create_unplaced(move(vmobject), 0, {}, Memory::Region::Access::ReadWrite)).leak_ptr(); - auto shadow_range = VirtualRange { virtual_range.base().offset(virtual_range.size() - shadow_range_size), shadow_range_size }; - MUST(global_data.region_tree.place_specifically(*shadow_region, shadow_range)); - MUST(shadow_region->map(kernel_page_directory())); - - AddressSanitizer::init(shadow_region->vaddr().get()); - }); -} -#endif - -PhysicalPageEntry& MemoryManager::get_physical_page_entry(PhysicalAddress physical_address) -{ - auto physical_page_entry_index = PhysicalAddress::physical_page_index(physical_address.get()); - VERIFY(physical_page_entry_index < m_physical_page_entries_count); - return m_physical_page_entries[physical_page_entry_index]; -} - -PhysicalAddress MemoryManager::get_physical_address(PhysicalRAMPage const& physical_page) -{ - PhysicalPageEntry const& physical_page_entry = *reinterpret_cast((u8 const*)&physical_page - __builtin_offsetof(PhysicalPageEntry, allocated.physical_page)); - size_t physical_page_entry_index = &physical_page_entry - m_physical_page_entries; - VERIFY(physical_page_entry_index < m_physical_page_entries_count); - return PhysicalAddress((PhysicalPtr)physical_page_entry_index * PAGE_SIZE); -} - -PageTableEntry* MemoryManager::pte(PageDirectory& page_directory, VirtualAddress vaddr) -{ - VERIFY_INTERRUPTS_DISABLED(); - VERIFY(page_directory.get_lock().is_locked_by_current_processor()); - u32 page_directory_table_index = (vaddr.get() >> 30) & 0x1ff; - u32 page_directory_index = (vaddr.get() >> 21) & 0x1ff; - u32 page_table_index = (vaddr.get() >> 12) & 0x1ff; - - auto* pd = quickmap_pd(const_cast(page_directory), page_directory_table_index); - PageDirectoryEntry const& pde = pd[page_directory_index]; - if (!pde.is_present()) - return nullptr; - - return &quickmap_pt(PhysicalAddress((FlatPtr)pde.page_table_base()))[page_table_index]; -} - -PageTableEntry* MemoryManager::ensure_pte(PageDirectory& page_directory, VirtualAddress vaddr) -{ - VERIFY_INTERRUPTS_DISABLED(); - VERIFY(page_directory.get_lock().is_locked_by_current_processor()); - u32 page_directory_table_index = (vaddr.get() >> 30) & 0x1ff; - u32 page_directory_index = (vaddr.get() >> 21) & 0x1ff; - u32 page_table_index = (vaddr.get() >> 12) & 0x1ff; - - auto* pd = quickmap_pd(page_directory, page_directory_table_index); - auto& pde = pd[page_directory_index]; - if (pde.is_present()) - return &quickmap_pt(PhysicalAddress(pde.page_table_base()))[page_table_index]; - - bool did_purge = false; - auto page_table_or_error = allocate_physical_page(ShouldZeroFill::Yes, &did_purge); - if (page_table_or_error.is_error()) { - dbgln("MM: Unable to allocate page table to map {}", vaddr); - return nullptr; - } - auto page_table = page_table_or_error.release_value(); - if (did_purge) { - // If any memory had to be purged, ensure_pte may have been called as part - // of the purging process. So we need to re-map the pd in this case to ensure - // we're writing to the correct underlying physical page - pd = quickmap_pd(page_directory, page_directory_table_index); - VERIFY(&pde == &pd[page_directory_index]); // Sanity check - - VERIFY(!pde.is_present()); // Should have not changed - } - pde.set_page_table_base(page_table->paddr().get()); - pde.set_user_allowed(true); - pde.set_present(true); - pde.set_writable(true); - pde.set_global(&page_directory == m_kernel_page_directory.ptr()); - - // NOTE: This leaked ref is matched by the unref in MemoryManager::release_pte() - (void)page_table.leak_ref(); - - return &quickmap_pt(PhysicalAddress(pde.page_table_base()))[page_table_index]; -} - -void MemoryManager::release_pte(PageDirectory& page_directory, VirtualAddress vaddr, IsLastPTERelease is_last_pte_release) -{ - VERIFY_INTERRUPTS_DISABLED(); - VERIFY(page_directory.get_lock().is_locked_by_current_processor()); - u32 page_directory_table_index = (vaddr.get() >> 30) & 0x1ff; - u32 page_directory_index = (vaddr.get() >> 21) & 0x1ff; - u32 page_table_index = (vaddr.get() >> 12) & 0x1ff; - - auto* pd = quickmap_pd(page_directory, page_directory_table_index); - PageDirectoryEntry& pde = pd[page_directory_index]; - if (pde.is_present()) { - auto* page_table = quickmap_pt(PhysicalAddress((FlatPtr)pde.page_table_base())); - auto& pte = page_table[page_table_index]; - pte.clear(); - - if (is_last_pte_release == IsLastPTERelease::Yes || page_table_index == 0x1ff) { - // If this is the last PTE in a region or the last PTE in a page table then - // check if we can also release the page table - bool all_clear = true; - for (u32 i = 0; i <= 0x1ff; i++) { - if (!page_table[i].is_null()) { - all_clear = false; - break; - } - } - if (all_clear) { - get_physical_page_entry(PhysicalAddress { pde.page_table_base() }).allocated.physical_page.unref(); - pde.clear(); - } - } - } -} - -UNMAP_AFTER_INIT void MemoryManager::initialize(u32 cpu) -{ - dmesgln("Initialize MMU"); - ProcessorSpecific::initialize(); - - if (cpu == 0) { - new MemoryManager; - kmalloc_enable_expand(); - } -} - -Region* MemoryManager::find_user_region_from_vaddr(AddressSpace& space, VirtualAddress vaddr) -{ - return space.find_region_containing({ vaddr, 1 }); -} - -void MemoryManager::validate_syscall_preconditions(Process& process, RegisterState const& regs) -{ - bool should_crash = false; - char const* crash_description = nullptr; - int crash_signal = 0; - - auto unlock_and_handle_crash = [&](char const* description, int signal) { - should_crash = true; - crash_description = description; - crash_signal = signal; - }; - - process.address_space().with([&](auto& space) -> void { - VirtualAddress userspace_sp = VirtualAddress { regs.userspace_sp() }; - if (!MM.validate_user_stack(*space, userspace_sp)) { - dbgln("Invalid stack pointer: {}", userspace_sp); - return unlock_and_handle_crash("Bad stack on syscall entry", SIGSEGV); - } - - VirtualAddress ip = VirtualAddress { regs.ip() }; - auto* calling_region = MM.find_user_region_from_vaddr(*space, ip); - if (!calling_region) { - dbgln("Syscall from {:p} which has no associated region", ip); - return unlock_and_handle_crash("Syscall from unknown region", SIGSEGV); - } - - if (calling_region->is_writable()) { - dbgln("Syscall from writable memory at {:p}", ip); - return unlock_and_handle_crash("Syscall from writable memory", SIGSEGV); - } - - if (space->enforces_syscall_regions() && !calling_region->is_syscall_region()) { - dbgln("Syscall from non-syscall region"); - return unlock_and_handle_crash("Syscall from non-syscall region", SIGSEGV); - } - }); - - if (should_crash) { - handle_crash(regs, crash_description, crash_signal); - } -} - -PageFaultResponse MemoryManager::handle_page_fault(PageFault const& fault) -{ - auto faulted_in_range = [&fault](auto const* start, auto const* end) { - return fault.vaddr() >= VirtualAddress { start } && fault.vaddr() < VirtualAddress { end }; - }; - - if (faulted_in_range(&start_of_ro_after_init, &end_of_ro_after_init)) { - dbgln("Attempt to write into READONLY_AFTER_INIT section"); - return PageFaultResponse::ShouldCrash; - } - - if (faulted_in_range(&start_of_unmap_after_init, &end_of_unmap_after_init)) { - auto const* kernel_symbol = symbolicate_kernel_address(fault.vaddr().get()); - dbgln("Attempt to access UNMAP_AFTER_INIT section ({}: {})", fault.vaddr(), kernel_symbol ? kernel_symbol->name : "(Unknown)"); - return PageFaultResponse::ShouldCrash; - } - - if (faulted_in_range(&start_of_kernel_ksyms, &end_of_kernel_ksyms)) { - dbgln("Attempt to access KSYMS section"); - return PageFaultResponse::ShouldCrash; - } - - if (Processor::current_in_irq()) { - dbgln("CPU[{}] BUG! Page fault while handling IRQ! code={}, vaddr={}, irq level: {}", - Processor::current_id(), fault.code(), fault.vaddr(), Processor::current_in_irq()); - dump_kernel_regions(); - return PageFaultResponse::ShouldCrash; - } - dbgln_if(PAGE_FAULT_DEBUG, "MM: CPU[{}] handle_page_fault({:#04x}) at {}", Processor::current_id(), fault.code(), fault.vaddr()); - - // The faulting region may be unmapped concurrently to handling this page fault, and since - // regions are singly-owned it would usually result in the region being immediately - // de-allocated. To ensure the region is not de-allocated while we're still handling the - // fault we increase a page fault counter on the region, and the region will refrain from - // de-allocating itself until the counter reaches zero. (Since unmapping the region also - // includes removing it from the region tree while holding the address space spinlock, and - // because we increment the counter while still holding the spinlock it is guaranteed that - // we always increment the counter before it gets a chance to be deleted) - Region* region = nullptr; - if (is_user_address(fault.vaddr())) { - auto page_directory = PageDirectory::find_current(); - if (!page_directory) - return PageFaultResponse::ShouldCrash; - auto* process = page_directory->process(); - VERIFY(process); - region = process->address_space().with([&](auto& space) -> Region* { - auto* region = find_user_region_from_vaddr(*space, fault.vaddr()); - if (!region) - return nullptr; - region->start_handling_page_fault({}); - return region; - }); - } else { - region = MM.m_global_data.with([&](auto& global_data) -> Region* { - auto* region = global_data.region_tree.find_region_containing(fault.vaddr()); - if (!region) - return nullptr; - region->start_handling_page_fault({}); - return region; - }); - } - if (!region) - return PageFaultResponse::ShouldCrash; - - auto response = region->handle_fault(fault); - region->finish_handling_page_fault({}); - return response; -} - -ErrorOr> MemoryManager::allocate_contiguous_kernel_region(size_t size, StringView name, Region::Access access, Region::Cacheable cacheable) -{ - VERIFY(!(size % PAGE_SIZE)); - OwnPtr name_kstring; - if (!name.is_null()) - name_kstring = TRY(KString::try_create(name)); - auto vmobject = TRY(AnonymousVMObject::try_create_physically_contiguous_with_size(size)); - auto region = TRY(Region::create_unplaced(move(vmobject), 0, move(name_kstring), access, cacheable)); - TRY(m_global_data.with([&](auto& global_data) { return global_data.region_tree.place_anywhere(*region, RandomizeVirtualAddress::No, size); })); - TRY(region->map(kernel_page_directory())); - return region; -} - -ErrorOr> MemoryManager::allocate_dma_buffer_page(StringView name, Memory::Region::Access access, RefPtr& dma_buffer_page) -{ - auto page = TRY(allocate_physical_page()); - dma_buffer_page = page; - // Do not enable Cache for this region as physical memory transfers are performed (Most architectures have this behavior by default) - return allocate_kernel_region_with_physical_pages({ &page, 1 }, name, access, Region::Cacheable::No); -} - -ErrorOr> MemoryManager::allocate_dma_buffer_page(StringView name, Memory::Region::Access access) -{ - RefPtr dma_buffer_page; - - return allocate_dma_buffer_page(name, access, dma_buffer_page); -} - -ErrorOr> MemoryManager::allocate_dma_buffer_pages(size_t size, StringView name, Memory::Region::Access access, Vector>& dma_buffer_pages) -{ - VERIFY(!(size % PAGE_SIZE)); - dma_buffer_pages = TRY(allocate_contiguous_physical_pages(size)); - // Do not enable Cache for this region as physical memory transfers are performed (Most architectures have this behavior by default) - return allocate_kernel_region_with_physical_pages(dma_buffer_pages, name, access, Region::Cacheable::No); -} - -ErrorOr> MemoryManager::allocate_dma_buffer_pages(size_t size, StringView name, Memory::Region::Access access) -{ - VERIFY(!(size % PAGE_SIZE)); - Vector> dma_buffer_pages; - - return allocate_dma_buffer_pages(size, name, access, dma_buffer_pages); -} - -ErrorOr> MemoryManager::allocate_kernel_region(size_t size, StringView name, Region::Access access, AllocationStrategy strategy, Region::Cacheable cacheable) -{ - VERIFY(!(size % PAGE_SIZE)); - OwnPtr name_kstring; - if (!name.is_null()) - name_kstring = TRY(KString::try_create(name)); - auto vmobject = TRY(AnonymousVMObject::try_create_with_size(size, strategy)); - auto region = TRY(Region::create_unplaced(move(vmobject), 0, move(name_kstring), access, cacheable)); - TRY(m_global_data.with([&](auto& global_data) { return global_data.region_tree.place_anywhere(*region, RandomizeVirtualAddress::No, size); })); - TRY(region->map(kernel_page_directory())); - return region; -} - -ErrorOr> MemoryManager::allocate_kernel_region_with_physical_pages(Span> pages, StringView name, Region::Access access, Region::Cacheable cacheable) -{ - auto vmobject = TRY(AnonymousVMObject::try_create_with_physical_pages(pages)); - OwnPtr name_kstring; - if (!name.is_null()) - name_kstring = TRY(KString::try_create(name)); - auto region = TRY(Region::create_unplaced(move(vmobject), 0, move(name_kstring), access, cacheable)); - TRY(m_global_data.with([&](auto& global_data) { return global_data.region_tree.place_anywhere(*region, RandomizeVirtualAddress::No, pages.size() * PAGE_SIZE, PAGE_SIZE); })); - TRY(region->map(kernel_page_directory())); - return region; -} - -ErrorOr> MemoryManager::allocate_mmio_kernel_region(PhysicalAddress paddr, size_t size, StringView name, Region::Access access, Region::Cacheable cacheable) -{ - VERIFY(!(size % PAGE_SIZE)); - auto vmobject = TRY(MMIOVMObject::try_create_for_physical_range(paddr, size)); - OwnPtr name_kstring; - if (!name.is_null()) - name_kstring = TRY(KString::try_create(name)); - auto region = TRY(Region::create_unplaced(move(vmobject), 0, move(name_kstring), access, cacheable)); - TRY(m_global_data.with([&](auto& global_data) { return global_data.region_tree.place_anywhere(*region, RandomizeVirtualAddress::No, size, PAGE_SIZE); })); - TRY(region->map(kernel_page_directory(), paddr)); - return region; -} - -ErrorOr> MemoryManager::allocate_kernel_region_with_vmobject(VMObject& vmobject, size_t size, StringView name, Region::Access access, Region::Cacheable cacheable) -{ - VERIFY(!(size % PAGE_SIZE)); - - OwnPtr name_kstring; - if (!name.is_null()) - name_kstring = TRY(KString::try_create(name)); - - auto region = TRY(Region::create_unplaced(vmobject, 0, move(name_kstring), access, cacheable)); - TRY(m_global_data.with([&](auto& global_data) { return global_data.region_tree.place_anywhere(*region, RandomizeVirtualAddress::No, size); })); - TRY(region->map(kernel_page_directory())); - return region; -} - -ErrorOr MemoryManager::commit_physical_pages(size_t page_count) -{ - VERIFY(page_count > 0); - auto result = m_global_data.with([&](auto& global_data) -> ErrorOr { - if (global_data.system_memory_info.physical_pages_uncommitted < page_count) { - dbgln("MM: Unable to commit {} pages, have only {}", page_count, global_data.system_memory_info.physical_pages_uncommitted); - return ENOMEM; - } - - global_data.system_memory_info.physical_pages_uncommitted -= page_count; - global_data.system_memory_info.physical_pages_committed += page_count; - return CommittedPhysicalPageSet { {}, page_count }; - }); - if (result.is_error()) { - Process::for_each_ignoring_jails([&](Process const& process) { - size_t amount_resident = 0; - size_t amount_shared = 0; - size_t amount_virtual = 0; - process.address_space().with([&](auto& space) { - amount_resident = space->amount_resident(); - amount_shared = space->amount_shared(); - amount_virtual = space->amount_virtual(); - }); - process.name().with([&](auto& process_name) { - dbgln("{}({}) resident:{}, shared:{}, virtual:{}", - process_name.representable_view(), - process.pid(), - amount_resident / PAGE_SIZE, - amount_shared / PAGE_SIZE, - amount_virtual / PAGE_SIZE); - }); - return IterationDecision::Continue; - }); - } - return result; -} - -void MemoryManager::uncommit_physical_pages(Badge, size_t page_count) -{ - VERIFY(page_count > 0); - - m_global_data.with([&](auto& global_data) { - VERIFY(global_data.system_memory_info.physical_pages_committed >= page_count); - - global_data.system_memory_info.physical_pages_uncommitted += page_count; - global_data.system_memory_info.physical_pages_committed -= page_count; - }); -} - -void MemoryManager::deallocate_physical_page(PhysicalAddress paddr) -{ - return m_global_data.with([&](auto& global_data) { - // Are we returning a user page? - for (auto& region : global_data.physical_regions) { - if (!region->contains(paddr)) - continue; - - region->return_page(paddr); - --global_data.system_memory_info.physical_pages_used; - - // Always return pages to the uncommitted pool. Pages that were - // committed and allocated are only freed upon request. Once - // returned there is no guarantee being able to get them back. - ++global_data.system_memory_info.physical_pages_uncommitted; - return; - } - PANIC("MM: deallocate_physical_page couldn't figure out region for page @ {}", paddr); - }); -} - -RefPtr MemoryManager::find_free_physical_page(bool committed) -{ - RefPtr page; - m_global_data.with([&](auto& global_data) { - if (committed) { - // Draw from the committed pages pool. We should always have these pages available - VERIFY(global_data.system_memory_info.physical_pages_committed > 0); - global_data.system_memory_info.physical_pages_committed--; - } else { - // We need to make sure we don't touch pages that we have committed to - if (global_data.system_memory_info.physical_pages_uncommitted == 0) - return; - global_data.system_memory_info.physical_pages_uncommitted--; - } - for (auto& region : global_data.physical_regions) { - page = region->take_free_page(); - if (!page.is_null()) { - ++global_data.system_memory_info.physical_pages_used; - break; - } - } - }); - - if (page.is_null()) - dbgln("MM: couldn't find free physical page. Continuing..."); - - return page; -} - -NonnullRefPtr MemoryManager::allocate_committed_physical_page(Badge, ShouldZeroFill should_zero_fill) -{ - auto page = find_free_physical_page(true); - VERIFY(page); - if (should_zero_fill == ShouldZeroFill::Yes) { - InterruptDisabler disabler; - auto* ptr = quickmap_page(*page); - memset(ptr, 0, PAGE_SIZE); - unquickmap_page(); - } - return page.release_nonnull(); -} - -ErrorOr> MemoryManager::allocate_physical_page(ShouldZeroFill should_zero_fill, bool* did_purge) -{ - return m_global_data.with([&](auto&) -> ErrorOr> { - auto page = find_free_physical_page(false); - bool purged_pages = false; - - if (!page) { - // We didn't have a single free physical page. Let's try to free something up! - // First, we look for a purgeable VMObject in the volatile state. - for_each_vmobject([&](auto& vmobject) { - if (!vmobject.is_anonymous()) - return IterationDecision::Continue; - auto& anonymous_vmobject = static_cast(vmobject); - if (!anonymous_vmobject.is_purgeable() || !anonymous_vmobject.is_volatile()) - return IterationDecision::Continue; - if (auto purged_page_count = anonymous_vmobject.purge()) { - dbgln("MM: Purge saved the day! Purged {} pages from AnonymousVMObject", purged_page_count); - page = find_free_physical_page(false); - purged_pages = true; - VERIFY(page); - return IterationDecision::Break; - } - return IterationDecision::Continue; - }); - } - if (!page) { - // Second, we look for a file-backed VMObject with clean pages. - for_each_vmobject([&](auto& vmobject) { - if (!vmobject.is_inode()) - return IterationDecision::Continue; - auto& inode_vmobject = static_cast(vmobject); - if (auto released_page_count = inode_vmobject.try_release_clean_pages(1)) { - dbgln("MM: Clean inode release saved the day! Released {} pages from InodeVMObject", released_page_count); - page = find_free_physical_page(false); - VERIFY(page); - return IterationDecision::Break; - } - return IterationDecision::Continue; - }); - } - if (!page) { - dmesgln("MM: no physical pages available"); - return ENOMEM; - } - - if (should_zero_fill == ShouldZeroFill::Yes) { - auto* ptr = quickmap_page(*page); - memset(ptr, 0, PAGE_SIZE); - unquickmap_page(); - } - - if (did_purge) - *did_purge = purged_pages; - return page.release_nonnull(); - }); -} - -ErrorOr>> MemoryManager::allocate_contiguous_physical_pages(size_t size) -{ - VERIFY(!(size % PAGE_SIZE)); - size_t page_count = ceil_div(size, static_cast(PAGE_SIZE)); - - auto physical_pages = TRY(m_global_data.with([&](auto& global_data) -> ErrorOr>> { - // We need to make sure we don't touch pages that we have committed to - if (global_data.system_memory_info.physical_pages_uncommitted < page_count) - return ENOMEM; - - for (auto& physical_region : global_data.physical_regions) { - auto physical_pages = physical_region->take_contiguous_free_pages(page_count); - if (!physical_pages.is_empty()) { - global_data.system_memory_info.physical_pages_uncommitted -= page_count; - global_data.system_memory_info.physical_pages_used += page_count; - return physical_pages; - } - } - dmesgln("MM: no contiguous physical pages available"); - return ENOMEM; - })); - - { - auto cleanup_region = TRY(MM.allocate_kernel_region_with_physical_pages(physical_pages, {}, Region::Access::Read | Region::Access::Write)); - memset(cleanup_region->vaddr().as_ptr(), 0, PAGE_SIZE * page_count); - } - return physical_pages; -} - -void MemoryManager::enter_process_address_space(Process& process) -{ - process.address_space().with([](auto& space) { - enter_address_space(*space); - }); -} - -void MemoryManager::enter_address_space(AddressSpace& space) -{ - auto* current_thread = Thread::current(); - VERIFY(current_thread != nullptr); - activate_page_directory(space.page_directory(), current_thread); -} - -void MemoryManager::flush_tlb_local(VirtualAddress vaddr, size_t page_count) -{ - Processor::flush_tlb_local(vaddr, page_count); -} - -void MemoryManager::flush_tlb(PageDirectory const* page_directory, VirtualAddress vaddr, size_t page_count) -{ - Processor::flush_tlb(page_directory, vaddr, page_count); -} - -PageDirectoryEntry* MemoryManager::quickmap_pd(PageDirectory& directory, size_t pdpt_index) -{ - VERIFY_INTERRUPTS_DISABLED(); - - VirtualAddress vaddr(KERNEL_QUICKMAP_PD_PER_CPU_BASE + Processor::current_id() * PAGE_SIZE); - size_t pte_index = (vaddr.get() - KERNEL_PT1024_BASE) / PAGE_SIZE; - - auto& pte = boot_pd_kernel_pt1023[pte_index]; - auto pd_paddr = directory.m_directory_pages[pdpt_index]->paddr(); - if (pte.physical_page_base() != pd_paddr.get()) { - pte.set_physical_page_base(pd_paddr.get()); - pte.set_present(true); - pte.set_writable(true); - pte.set_user_allowed(false); - flush_tlb_local(vaddr); - } - return (PageDirectoryEntry*)vaddr.get(); -} - -PageTableEntry* MemoryManager::quickmap_pt(PhysicalAddress pt_paddr) -{ - VERIFY_INTERRUPTS_DISABLED(); - - VirtualAddress vaddr(KERNEL_QUICKMAP_PT_PER_CPU_BASE + Processor::current_id() * PAGE_SIZE); - size_t pte_index = (vaddr.get() - KERNEL_PT1024_BASE) / PAGE_SIZE; - - auto& pte = ((PageTableEntry*)boot_pd_kernel_pt1023)[pte_index]; - if (pte.physical_page_base() != pt_paddr.get()) { - pte.set_physical_page_base(pt_paddr.get()); - pte.set_present(true); - pte.set_writable(true); - pte.set_user_allowed(false); - flush_tlb_local(vaddr); - } - return (PageTableEntry*)vaddr.get(); -} - -u8* MemoryManager::quickmap_page(PhysicalAddress const& physical_address) -{ - VERIFY_INTERRUPTS_DISABLED(); - auto& mm_data = get_data(); - mm_data.m_quickmap_previous_interrupts_state = mm_data.m_quickmap_in_use.lock(); - - VirtualAddress vaddr(KERNEL_QUICKMAP_PER_CPU_BASE + Processor::current_id() * PAGE_SIZE); - u32 pte_idx = (vaddr.get() - KERNEL_PT1024_BASE) / PAGE_SIZE; - - auto& pte = ((PageTableEntry*)boot_pd_kernel_pt1023)[pte_idx]; - if (pte.physical_page_base() != physical_address.get()) { - pte.set_physical_page_base(physical_address.get()); - pte.set_present(true); - pte.set_writable(true); - pte.set_user_allowed(false); - flush_tlb_local(vaddr); - } - return vaddr.as_ptr(); -} - -void MemoryManager::unquickmap_page() -{ - VERIFY_INTERRUPTS_DISABLED(); - auto& mm_data = get_data(); - VERIFY(mm_data.m_quickmap_in_use.is_locked()); - VirtualAddress vaddr(KERNEL_QUICKMAP_PER_CPU_BASE + Processor::current_id() * PAGE_SIZE); - u32 pte_idx = (vaddr.get() - KERNEL_PT1024_BASE) / PAGE_SIZE; - auto& pte = ((PageTableEntry*)boot_pd_kernel_pt1023)[pte_idx]; - pte.clear(); - flush_tlb_local(vaddr); - mm_data.m_quickmap_in_use.unlock(mm_data.m_quickmap_previous_interrupts_state); -} - -bool MemoryManager::validate_user_stack(AddressSpace& space, VirtualAddress vaddr) const -{ - if (!is_user_address(vaddr)) - return false; - - auto* region = find_user_region_from_vaddr(space, vaddr); - return region && region->is_user() && region->is_stack(); -} - -void MemoryManager::unregister_kernel_region(Region& region) -{ - VERIFY(region.is_kernel()); - m_global_data.with([&](auto& global_data) { global_data.region_tree.remove(region); }); -} - -void MemoryManager::dump_kernel_regions() -{ - dbgln("Kernel regions:"); - char const* addr_padding = " "; - dbgln("BEGIN{} END{} SIZE{} ACCESS NAME", - addr_padding, addr_padding, addr_padding); - m_global_data.with([&](auto& global_data) { - for (auto& region : global_data.region_tree.regions()) { - dbgln("{:p} -- {:p} {:p} {:c}{:c}{:c}{:c}{:c}{:c} {}", - region.vaddr().get(), - region.vaddr().offset(region.size() - 1).get(), - region.size(), - region.is_readable() ? 'R' : ' ', - region.is_writable() ? 'W' : ' ', - region.is_executable() ? 'X' : ' ', - region.is_shared() ? 'S' : ' ', - region.is_stack() ? 'T' : ' ', - region.is_syscall_region() ? 'C' : ' ', - region.name()); - } - }); -} - -void MemoryManager::set_page_writable_direct(VirtualAddress vaddr, bool writable) -{ - SpinlockLocker page_lock(kernel_page_directory().get_lock()); - auto* pte = ensure_pte(kernel_page_directory(), vaddr); - VERIFY(pte); - if (pte->is_writable() == writable) - return; - pte->set_writable(writable); - flush_tlb(&kernel_page_directory(), vaddr); -} - -CommittedPhysicalPageSet::~CommittedPhysicalPageSet() -{ - if (m_page_count) - MM.uncommit_physical_pages({}, m_page_count); -} - -NonnullRefPtr CommittedPhysicalPageSet::take_one() -{ - VERIFY(m_page_count > 0); - --m_page_count; - return MM.allocate_committed_physical_page({}, MemoryManager::ShouldZeroFill::Yes); -} - -void CommittedPhysicalPageSet::uncommit_one() -{ - VERIFY(m_page_count > 0); - --m_page_count; - MM.uncommit_physical_pages({}, 1); -} - -void MemoryManager::copy_physical_page(PhysicalRAMPage& physical_page, u8 page_buffer[PAGE_SIZE]) -{ - auto* quickmapped_page = quickmap_page(physical_page); - memcpy(page_buffer, quickmapped_page, PAGE_SIZE); - unquickmap_page(); -} - -ErrorOr> MemoryManager::create_identity_mapped_region(PhysicalAddress address, size_t size) -{ - auto vmobject = TRY(Memory::AnonymousVMObject::try_create_for_physical_range(address, size)); - auto region = TRY(Memory::Region::create_unplaced(move(vmobject), 0, {}, Memory::Region::Access::ReadWriteExecute)); - Memory::VirtualRange range { VirtualAddress { (FlatPtr)address.get() }, size }; - region->m_range = range; - TRY(region->map(MM.kernel_page_directory())); - return region; -} - -ErrorOr> MemoryManager::allocate_unbacked_region_anywhere(size_t size, size_t alignment) -{ - auto region = TRY(Region::create_unbacked()); - TRY(m_global_data.with([&](auto& global_data) { return global_data.region_tree.place_anywhere(*region, RandomizeVirtualAddress::No, size, alignment); })); - return region; -} - -MemoryManager::SystemMemoryInfo MemoryManager::get_system_memory_info() -{ - return m_global_data.with([&](auto& global_data) { - auto physical_pages_unused = global_data.system_memory_info.physical_pages_committed + global_data.system_memory_info.physical_pages_uncommitted; - VERIFY(global_data.system_memory_info.physical_pages == (global_data.system_memory_info.physical_pages_used + physical_pages_unused)); - return global_data.system_memory_info; - }); -} -} diff --git a/Kernel/Memory/MemoryManager.h b/Kernel/Memory/MemoryManager.h deleted file mode 100644 index fd7a0e745c5..00000000000 --- a/Kernel/Memory/MemoryManager.h +++ /dev/null @@ -1,345 +0,0 @@ -/* - * Copyright (c) 2018-2022, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -struct KmallocGlobalData; - -namespace Kernel::Memory { - -class PageDirectoryEntry; -class PageTableEntry; - -ErrorOr page_round_up(FlatPtr x); - -constexpr FlatPtr page_round_down(FlatPtr x) -{ - return ((FlatPtr)(x)) & ~(PAGE_SIZE - 1); -} - -inline FlatPtr virtual_to_low_physical(FlatPtr virtual_) -{ - return virtual_ - physical_to_virtual_offset; -} - -enum class UsedMemoryRangeType { - LowMemory = 0, - Kernel, - BootModule, - PhysicalPages, - __Count -}; - -static constexpr StringView UserMemoryRangeTypeNames[] { - "Low memory"sv, - "Kernel"sv, - "Boot module"sv, - "Physical Pages"sv -}; -static_assert(array_size(UserMemoryRangeTypeNames) == to_underlying(UsedMemoryRangeType::__Count)); - -struct UsedMemoryRange { - UsedMemoryRangeType type {}; - PhysicalAddress start; - PhysicalAddress end; -}; - -struct ContiguousReservedMemoryRange { - PhysicalAddress start; - PhysicalSize length {}; -}; - -enum class PhysicalMemoryRangeType { - Usable = 0, - Reserved, - ACPI_Reclaimable, - ACPI_NVS, - BadMemory, - Unknown, -}; - -struct PhysicalMemoryRange { - PhysicalMemoryRangeType type { PhysicalMemoryRangeType::Unknown }; - PhysicalAddress start; - PhysicalSize length {}; -}; - -#define MM Kernel::Memory::MemoryManager::the() - -struct MemoryManagerData { - static ProcessorSpecificDataID processor_specific_data_id() { return ProcessorSpecificDataID::MemoryManager; } - - Spinlock m_quickmap_in_use {}; - InterruptsState m_quickmap_previous_interrupts_state; -}; - -// This class represents a set of committed physical pages. -// When you ask MemoryManager to commit pages for you, you get one of these in return. -// You can allocate pages from it via `take_one()` -// It will uncommit any (unallocated) remaining pages when destroyed. -class CommittedPhysicalPageSet { - AK_MAKE_NONCOPYABLE(CommittedPhysicalPageSet); - -public: - CommittedPhysicalPageSet(Badge, size_t page_count) - : m_page_count(page_count) - { - } - - CommittedPhysicalPageSet(CommittedPhysicalPageSet&& other) - : m_page_count(exchange(other.m_page_count, 0)) - { - } - - ~CommittedPhysicalPageSet(); - - bool is_empty() const { return m_page_count == 0; } - size_t page_count() const { return m_page_count; } - - [[nodiscard]] NonnullRefPtr take_one(); - void uncommit_one(); - - void operator=(CommittedPhysicalPageSet&&) = delete; - -private: - size_t m_page_count { 0 }; -}; - -class MemoryManager { - friend class PageDirectory; - friend class AnonymousVMObject; - friend class Region; - friend class RegionTree; - friend class VMObject; - friend struct ::KmallocGlobalData; - -public: - static MemoryManager& the(); - static bool is_initialized(); - - static void initialize(u32 cpu); - - static inline MemoryManagerData& get_data() - { - return ProcessorSpecific::get(); - } - - PageFaultResponse handle_page_fault(PageFault const&); - - void set_page_writable_direct(VirtualAddress, bool); - - void protect_readonly_after_init_memory(); - void unmap_prekernel(); - void unmap_text_after_init(); - void protect_ksyms_after_init(); - - static void enter_process_address_space(Process&); - static void enter_address_space(AddressSpace&); - - bool validate_user_stack(AddressSpace&, VirtualAddress) const; - - enum class ShouldZeroFill { - No, - Yes - }; - - ErrorOr commit_physical_pages(size_t page_count); - void uncommit_physical_pages(Badge, size_t page_count); - - NonnullRefPtr allocate_committed_physical_page(Badge, ShouldZeroFill = ShouldZeroFill::Yes); - ErrorOr> allocate_physical_page(ShouldZeroFill = ShouldZeroFill::Yes, bool* did_purge = nullptr); - ErrorOr>> allocate_contiguous_physical_pages(size_t size); - void deallocate_physical_page(PhysicalAddress); - - ErrorOr> allocate_contiguous_kernel_region(size_t, StringView name, Region::Access access, Region::Cacheable = Region::Cacheable::Yes); - ErrorOr> allocate_dma_buffer_page(StringView name, Memory::Region::Access access, RefPtr& dma_buffer_page); - ErrorOr> allocate_dma_buffer_page(StringView name, Memory::Region::Access access); - ErrorOr> allocate_dma_buffer_pages(size_t size, StringView name, Memory::Region::Access access, Vector>& dma_buffer_pages); - ErrorOr> allocate_dma_buffer_pages(size_t size, StringView name, Memory::Region::Access access); - ErrorOr> allocate_kernel_region(size_t, StringView name, Region::Access access, AllocationStrategy strategy = AllocationStrategy::Reserve, Region::Cacheable = Region::Cacheable::Yes); - ErrorOr> allocate_mmio_kernel_region(PhysicalAddress, size_t, StringView name, Region::Access access, Region::Cacheable = Region::Cacheable::Yes); - ErrorOr> allocate_kernel_region_with_physical_pages(Span>, StringView name, Region::Access access, Region::Cacheable = Region::Cacheable::Yes); - ErrorOr> allocate_kernel_region_with_vmobject(VMObject&, size_t, StringView name, Region::Access access, Region::Cacheable = Region::Cacheable::Yes); - ErrorOr> allocate_unbacked_region_anywhere(size_t size, size_t alignment); - ErrorOr> create_identity_mapped_region(PhysicalAddress, size_t); - - struct SystemMemoryInfo { - PhysicalSize physical_pages { 0 }; - PhysicalSize physical_pages_used { 0 }; - PhysicalSize physical_pages_committed { 0 }; - PhysicalSize physical_pages_uncommitted { 0 }; - }; - - SystemMemoryInfo get_system_memory_info(); - - template Callback> - static void for_each_vmobject(Callback callback) - { - VMObject::all_instances().with([&](auto& list) { - for (auto& vmobject : list) { - if (callback(vmobject) == IterationDecision::Break) - break; - } - }); - } - - template Callback> - static void for_each_vmobject(Callback callback) - { - VMObject::all_instances().with([&](auto& list) { - for (auto& vmobject : list) { - callback(vmobject); - } - }); - } - - static Region* find_user_region_from_vaddr(AddressSpace&, VirtualAddress); - static void validate_syscall_preconditions(Process&, RegisterState const&); - - void dump_kernel_regions(); - - PhysicalRAMPage& shared_zero_page() { return *m_shared_zero_page; } - PhysicalRAMPage& lazy_committed_page() { return *m_lazy_committed_page; } - - PageDirectory& kernel_page_directory() { return *m_kernel_page_directory; } - - template - void for_each_used_memory_range(Callback callback) - { - m_global_data.template with([&](auto& global_data) { - for (auto& range : global_data.used_memory_ranges) - callback(range); - }); - } - bool is_allowed_to_read_physical_memory_for_userspace(PhysicalAddress, size_t read_length) const; - - PhysicalPageEntry& get_physical_page_entry(PhysicalAddress); - PhysicalAddress get_physical_address(PhysicalRAMPage const&); - - void copy_physical_page(PhysicalRAMPage&, u8 page_buffer[PAGE_SIZE]); - - IterationDecision for_each_physical_memory_range(Function); - -private: - MemoryManager(); - ~MemoryManager(); - - struct GlobalData { - GlobalData(); - - SystemMemoryInfo system_memory_info; - - Vector> physical_regions; - OwnPtr physical_pages_region; - - RegionTree region_tree; - - Vector used_memory_ranges; - Vector physical_memory_ranges; - Vector reserved_memory_ranges; - }; - - void initialize_physical_pages(); - void register_reserved_ranges(); - -#ifdef HAS_ADDRESS_SANITIZER - void initialize_kasan_shadow_memory(); -#endif - - void unregister_kernel_region(Region&); - - void protect_kernel_image(); - void parse_memory_map(); - void parse_memory_map_fdt(GlobalData&, u8 const* fdt_addr); - void parse_memory_map_multiboot(GlobalData&); - static void flush_tlb_local(VirtualAddress, size_t page_count = 1); - static void flush_tlb(PageDirectory const*, VirtualAddress, size_t page_count = 1); - - RefPtr find_free_physical_page(bool); - - ALWAYS_INLINE u8* quickmap_page(PhysicalRAMPage& page) - { - return quickmap_page(page.paddr()); - } - u8* quickmap_page(PhysicalAddress const&); - void unquickmap_page(); - - PageDirectoryEntry* quickmap_pd(PageDirectory&, size_t pdpt_index); - PageTableEntry* quickmap_pt(PhysicalAddress); - - PageTableEntry* pte(PageDirectory&, VirtualAddress); - PageTableEntry* ensure_pte(PageDirectory&, VirtualAddress); - enum class IsLastPTERelease { - Yes, - No - }; - void release_pte(PageDirectory&, VirtualAddress, IsLastPTERelease); - - // NOTE: These are outside of GlobalData as they are only assigned on startup, - // and then never change. Atomic ref-counting covers that case without - // the need for additional synchronization. - LockRefPtr m_kernel_page_directory; - RefPtr m_shared_zero_page; - RefPtr m_lazy_committed_page; - - // NOTE: These are outside of GlobalData as they are initialized on startup, - // and then never change. - PhysicalPageEntry* m_physical_page_entries { nullptr }; - size_t m_physical_page_entries_count { 0 }; - - SpinlockProtected m_global_data; -}; - -inline bool PhysicalRAMPage::is_shared_zero_page() const -{ - return this == &MM.shared_zero_page(); -} - -inline bool PhysicalRAMPage::is_lazy_committed_page() const -{ - return this == &MM.lazy_committed_page(); -} - -inline ErrorOr expand_range_to_page_boundaries(FlatPtr address, size_t size) -{ - if ((address + size) < address) - return EINVAL; - - auto base = VirtualAddress { address }.page_base(); - auto end = TRY(Memory::page_round_up(address + size)); - - return Memory::VirtualRange { base, end - base.get() }; -} - -inline ErrorOr shrink_range_to_page_boundaries(FlatPtr address, size_t size) -{ - if ((address + size) < address) - return EINVAL; - - auto base = TRY(Memory::page_round_up(address)); - auto end = Memory::page_round_down(address + size); - - if (end < base) - return EINVAL; - - return Memory::VirtualRange { VirtualAddress(base), end - base }; -} - -} diff --git a/Kernel/Memory/MemorySections.h b/Kernel/Memory/MemorySections.h deleted file mode 100644 index bc9bada9400..00000000000 --- a/Kernel/Memory/MemorySections.h +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright (c) 2023, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include - -namespace Kernel::Memory { - -inline bool is_user_address(VirtualAddress vaddr) -{ - return vaddr.get() < USER_RANGE_CEILING; -} - -inline bool is_user_range(VirtualAddress vaddr, size_t size) -{ - if (vaddr.offset(size) < vaddr) - return false; - if (!is_user_address(vaddr)) - return false; - if (size <= 1) - return true; - return is_user_address(vaddr.offset(size - 1)); -} - -inline bool is_user_range(VirtualRange const& range) -{ - return is_user_range(range.base(), range.size()); -} - -} diff --git a/Kernel/Memory/PageFaultResponse.h b/Kernel/Memory/PageFaultResponse.h deleted file mode 100644 index 0a5c4732308..00000000000 --- a/Kernel/Memory/PageFaultResponse.h +++ /dev/null @@ -1,18 +0,0 @@ -/* - * Copyright (c) 2020, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -namespace Kernel { - -enum class PageFaultResponse { - ShouldCrash, - BusError, - OutOfMemory, - Continue, -}; - -} diff --git a/Kernel/Memory/PhysicalAddress.h b/Kernel/Memory/PhysicalAddress.h deleted file mode 100644 index 8610992ab5d..00000000000 --- a/Kernel/Memory/PhysicalAddress.h +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include - -typedef u64 PhysicalPtr; -typedef u64 PhysicalSize; - -class PhysicalAddress { -public: - ALWAYS_INLINE static PhysicalPtr physical_page_base(PhysicalPtr page_address) { return page_address & ~(PhysicalPtr)0xfff; } - ALWAYS_INLINE static size_t physical_page_index(PhysicalPtr page_address) - { - auto page_index = page_address >> 12; - if constexpr (sizeof(size_t) < sizeof(PhysicalPtr)) - VERIFY(!(page_index & ~(PhysicalPtr)((size_t)-1))); - return (size_t)(page_index); - } - - PhysicalAddress() = default; - explicit PhysicalAddress(PhysicalPtr address) - : m_address(address) - { - } - - [[nodiscard]] PhysicalAddress offset(PhysicalPtr o) const { return PhysicalAddress(m_address + o); } - [[nodiscard]] bool offset_addition_would_overflow(PhysicalPtr o) const { return Checked::addition_would_overflow(m_address, o); } - [[nodiscard]] PhysicalPtr get() const { return m_address; } - void set(PhysicalPtr address) { m_address = address; } - void mask(PhysicalPtr m) { m_address &= m; } - - [[nodiscard]] bool is_null() const { return m_address == 0; } - - // NOLINTNEXTLINE(readability-make-member-function-const) const PhysicalAddress shouldn't be allowed to modify the underlying memory - [[nodiscard]] u8* as_ptr() { return reinterpret_cast(m_address); } - [[nodiscard]] u8 const* as_ptr() const { return reinterpret_cast(m_address); } - - [[nodiscard]] PhysicalAddress page_base() const { return PhysicalAddress(physical_page_base(m_address)); } - [[nodiscard]] PhysicalPtr offset_in_page() const { return PhysicalAddress(m_address & 0xfff).get(); } - - bool operator==(PhysicalAddress const& other) const { return m_address == other.m_address; } - bool operator!=(PhysicalAddress const& other) const { return m_address != other.m_address; } - bool operator>(PhysicalAddress const& other) const { return m_address > other.m_address; } - bool operator>=(PhysicalAddress const& other) const { return m_address >= other.m_address; } - bool operator<(PhysicalAddress const& other) const { return m_address < other.m_address; } - bool operator<=(PhysicalAddress const& other) const { return m_address <= other.m_address; } - -private: - PhysicalPtr m_address { 0 }; -}; - -template<> -struct AK::Formatter : AK::Formatter { - ErrorOr format(FormatBuilder& builder, PhysicalAddress value) - { - if constexpr (sizeof(PhysicalPtr) == sizeof(u64)) - return AK::Formatter::format(builder, "P{:016x}"sv, value.get()); - else - return AK::Formatter::format(builder, "P{}"sv, value.as_ptr()); - } -}; diff --git a/Kernel/Memory/PhysicalRAMPage.cpp b/Kernel/Memory/PhysicalRAMPage.cpp deleted file mode 100644 index 6c578d864cc..00000000000 --- a/Kernel/Memory/PhysicalRAMPage.cpp +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include - -namespace Kernel::Memory { - -NonnullRefPtr PhysicalRAMPage::create(PhysicalAddress paddr, MayReturnToFreeList may_return_to_freelist) -{ - auto& physical_page_entry = MM.get_physical_page_entry(paddr); - return adopt_ref(*new (&physical_page_entry.allocated.physical_page) PhysicalRAMPage(may_return_to_freelist)); -} - -PhysicalRAMPage::PhysicalRAMPage(MayReturnToFreeList may_return_to_freelist) - : m_may_return_to_freelist(may_return_to_freelist) -{ -} - -PhysicalAddress PhysicalRAMPage::paddr() const -{ - return MM.get_physical_address(*this); -} - -void PhysicalRAMPage::free_this() const -{ - auto paddr = MM.get_physical_address(*this); - if (m_may_return_to_freelist == MayReturnToFreeList::Yes) { - auto& this_as_freelist_entry = MM.get_physical_page_entry(paddr).freelist; - this->~PhysicalRAMPage(); // delete in place - this_as_freelist_entry.next_index = -1; - this_as_freelist_entry.prev_index = -1; - MM.deallocate_physical_page(paddr); - } else { - this->~PhysicalRAMPage(); // delete in place - } -} - -} diff --git a/Kernel/Memory/PhysicalRAMPage.h b/Kernel/Memory/PhysicalRAMPage.h deleted file mode 100644 index c4ad1cb8a21..00000000000 --- a/Kernel/Memory/PhysicalRAMPage.h +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Copyright (c) 2018-2022, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include - -namespace Kernel::Memory { - -enum class MayReturnToFreeList : bool { - No, - Yes -}; - -class PhysicalRAMPage { - AK_MAKE_NONCOPYABLE(PhysicalRAMPage); - AK_MAKE_NONMOVABLE(PhysicalRAMPage); - - friend class MemoryManager; - -public: - PhysicalAddress paddr() const; - - void ref() const - { - m_ref_count.fetch_add(1, AK::memory_order_relaxed); - } - - void unref() const - { - if (m_ref_count.fetch_sub(1, AK::memory_order_acq_rel) == 1) - free_this(); - } - - static NonnullRefPtr create(PhysicalAddress, MayReturnToFreeList may_return_to_freelist = MayReturnToFreeList::Yes); - - u32 ref_count() const { return m_ref_count.load(AK::memory_order_consume); } - - bool is_shared_zero_page() const; - bool is_lazy_committed_page() const; - -private: - explicit PhysicalRAMPage(MayReturnToFreeList may_return_to_freelist); - ~PhysicalRAMPage() = default; - - void free_this() const; - - mutable Atomic m_ref_count { 1 }; - MayReturnToFreeList m_may_return_to_freelist { MayReturnToFreeList::Yes }; -}; - -struct PhysicalPageEntry { - union { - // If it's a live PhysicalPage object: - struct { - PhysicalRAMPage physical_page; - } allocated; - - // If it's an entry in a PhysicalZone::Bucket's freelist. - struct { - i16 next_index; - i16 prev_index; - } freelist; - }; -}; - -} diff --git a/Kernel/Memory/PhysicalRegion.cpp b/Kernel/Memory/PhysicalRegion.cpp deleted file mode 100644 index 004bdd2178c..00000000000 --- a/Kernel/Memory/PhysicalRegion.cpp +++ /dev/null @@ -1,140 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include -#include - -namespace Kernel::Memory { - -static constexpr u32 next_power_of_two(u32 value) -{ - value--; - value |= value >> 1; - value |= value >> 2; - value |= value >> 4; - value |= value >> 8; - value |= value >> 16; - value++; - return value; -} - -PhysicalRegion::~PhysicalRegion() = default; - -PhysicalRegion::PhysicalRegion(PhysicalAddress lower, PhysicalAddress upper) - : m_lower(lower) - , m_upper(upper) -{ - m_pages = (m_upper.get() - m_lower.get()) / PAGE_SIZE; -} - -void PhysicalRegion::initialize_zones() -{ - size_t remaining_pages = m_pages; - auto base_address = m_lower; - - auto make_zones = [&](size_t zone_size) -> size_t { - size_t pages_per_zone = zone_size / PAGE_SIZE; - size_t zone_count = 0; - auto first_address = base_address; - while (remaining_pages >= pages_per_zone) { - m_zones.append(adopt_nonnull_own_or_enomem(new (nothrow) PhysicalZone(base_address, pages_per_zone)).release_value_but_fixme_should_propagate_errors()); - base_address = base_address.offset(pages_per_zone * PAGE_SIZE); - m_usable_zones.append(*m_zones.last()); - remaining_pages -= pages_per_zone; - ++zone_count; - } - if (zone_count) - dmesgln(" * {}x PhysicalZone ({} MiB) @ {:016x}-{:016x}", zone_count, pages_per_zone / 256, first_address.get(), base_address.get() - pages_per_zone * PAGE_SIZE - 1); - return zone_count; - }; - - // First make 16 MiB zones (with 4096 pages each) - m_large_zones = make_zones(large_zone_size); - - // Then divide any remaining space into 1 MiB zones (with 256 pages each) - make_zones(small_zone_size); -} - -OwnPtr PhysicalRegion::try_take_pages_from_beginning(size_t page_count) -{ - VERIFY(page_count > 0); - VERIFY(page_count < m_pages); - auto taken_lower = m_lower; - auto taken_upper = taken_lower.offset((PhysicalPtr)page_count * PAGE_SIZE); - m_lower = m_lower.offset((PhysicalPtr)page_count * PAGE_SIZE); - m_pages = (m_upper.get() - m_lower.get()) / PAGE_SIZE; - - return try_create(taken_lower, taken_upper); -} - -Vector> PhysicalRegion::take_contiguous_free_pages(size_t count) -{ - auto rounded_page_count = next_power_of_two(count); - auto order = count_trailing_zeroes(rounded_page_count); - - Optional page_base; - for (auto& zone : m_usable_zones) { - page_base = zone.allocate_block(order); - if (page_base.has_value()) { - if (zone.is_empty()) { - // We've exhausted this zone, move it to the full zones list. - m_full_zones.append(zone); - } - break; - } - } - - if (!page_base.has_value()) - return {}; - - Vector> physical_pages; - physical_pages.ensure_capacity(count); - - for (size_t i = 0; i < count; ++i) - physical_pages.append(PhysicalRAMPage::create(page_base.value().offset(i * PAGE_SIZE))); - return physical_pages; -} - -RefPtr PhysicalRegion::take_free_page() -{ - if (m_usable_zones.is_empty()) - return nullptr; - - auto& zone = *m_usable_zones.first(); - auto page = zone.allocate_block(0); - VERIFY(page.has_value()); - - if (zone.is_empty()) { - // We've exhausted this zone, move it to the full zones list. - m_full_zones.append(zone); - } - - return PhysicalRAMPage::create(page.value()); -} - -void PhysicalRegion::return_page(PhysicalAddress paddr) -{ - auto large_zone_base = lower().get(); - auto small_zone_base = lower().get() + (m_large_zones * large_zone_size); - - size_t zone_index; - if (paddr.get() < small_zone_base) - zone_index = (paddr.get() - large_zone_base) / large_zone_size; - else - zone_index = m_large_zones + (paddr.get() - small_zone_base) / small_zone_size; - - auto& zone = m_zones[zone_index]; - VERIFY(zone->contains(paddr)); - zone->deallocate_block(paddr, 0); - if (m_full_zones.contains(*zone)) - m_usable_zones.append(*zone); -} - -} diff --git a/Kernel/Memory/PhysicalRegion.h b/Kernel/Memory/PhysicalRegion.h deleted file mode 100644 index 52067b1599e..00000000000 --- a/Kernel/Memory/PhysicalRegion.h +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright (c) 2018-2021, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include - -namespace Kernel::Memory { - -class PhysicalRegion { - AK_MAKE_NONCOPYABLE(PhysicalRegion); - AK_MAKE_NONMOVABLE(PhysicalRegion); - -public: - static OwnPtr try_create(PhysicalAddress lower, PhysicalAddress upper) - { - return adopt_own_if_nonnull(new PhysicalRegion { lower, upper }); - } - - ~PhysicalRegion(); - - void initialize_zones(); - - PhysicalAddress lower() const { return m_lower; } - PhysicalAddress upper() const { return m_upper; } - size_t size() const { return m_pages; } - bool contains(PhysicalAddress paddr) const { return paddr >= m_lower && paddr < m_upper; } - - OwnPtr try_take_pages_from_beginning(size_t); - - RefPtr take_free_page(); - Vector> take_contiguous_free_pages(size_t count); - void return_page(PhysicalAddress); - -private: - PhysicalRegion(PhysicalAddress lower, PhysicalAddress upper); - - static constexpr size_t large_zone_size = 16 * MiB; - static constexpr size_t small_zone_size = 1 * MiB; - - Vector> m_zones; - - size_t m_large_zones { 0 }; - - PhysicalZone::List m_usable_zones; - PhysicalZone::List m_full_zones; - - PhysicalAddress m_lower; - PhysicalAddress m_upper; - size_t m_pages { 0 }; -}; - -} diff --git a/Kernel/Memory/PhysicalZone.cpp b/Kernel/Memory/PhysicalZone.cpp deleted file mode 100644 index 3126a878959..00000000000 --- a/Kernel/Memory/PhysicalZone.cpp +++ /dev/null @@ -1,199 +0,0 @@ -/* - * Copyright (c) 2021, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include - -namespace Kernel::Memory { - -PhysicalPageEntry& PhysicalZone::get_freelist_entry(ChunkIndex index) const -{ - return MM.get_physical_page_entry(m_base_address.offset(index * ZONE_CHUNK_SIZE)); -} - -PhysicalZone::PhysicalZone(PhysicalAddress base_address, size_t page_count) - : m_base_address(base_address) - , m_page_count(page_count) - , m_used_chunks(0) -{ - size_t const chunk_count = page_count * 2; - for (int order = max_order; order >= 0; --order) { - auto& bucket = m_buckets[order]; - size_t block_size = 2u << order; - size_t bitmap_size_for_order = ceil_div((size_t)(chunk_count / block_size), (size_t)2); - bucket.order = order; - if (bitmap_size_for_order) - bucket.bitmap.grow(bitmap_size_for_order, false); - } - - auto first_order = count_trailing_zeroes(page_count); - size_t block_size = 2u << first_order; - auto& bucket = m_buckets[first_order]; - size_t remaining_chunk_count = chunk_count; - size_t initial_bundle_count = remaining_chunk_count / block_size; - - size_t offset = 0; - for (size_t i = 0; i < initial_bundle_count; ++i) { - ChunkIndex index = offset + i; - bucket.set_buddy_bit(index, true); - - auto& freelist_entry = get_freelist_entry(index).freelist; - freelist_entry.next_index = bucket.freelist; - freelist_entry.prev_index = -1; - bucket.freelist = index; - - remaining_chunk_count -= block_size; - offset += block_size; - } -} - -Optional PhysicalZone::allocate_block(size_t order) -{ - size_t block_size = 2u << order; - auto result = allocate_block_impl(order); - if (!result.has_value()) - return {}; - m_used_chunks += block_size; - VERIFY(!(result.value() & 1)); - return m_base_address.offset(result.value() * ZONE_CHUNK_SIZE); -} - -Optional PhysicalZone::allocate_block_impl(size_t order) -{ - if (order > max_order) - return {}; - size_t block_size = 2u << order; - auto& bucket = m_buckets[order]; - if (bucket.freelist == -1) { - // The freelist for this order is empty, try to allocate a block from one order higher, and split it. - auto buddies = allocate_block_impl(order + 1); - - if (!buddies.has_value()) { - // Looks like we're unable to satisfy this allocation request. - return {}; - } - - // Split the block from order+1 into two parts. - // We keep one (in the freelist for this order) and return the other. - - ChunkIndex index = buddies.value(); - - // First half goes in the freelist - auto& freelist_entry = get_freelist_entry(index).freelist; - freelist_entry.next_index = -1; - freelist_entry.prev_index = -1; - bucket.freelist = index; - - VERIFY(bucket.get_buddy_bit(index) == false); - - // Set buddy bit to 1 (one used, one unused). - bucket.set_buddy_bit(index, true); - - // Second half is returned. - return index + block_size; - } - - // Freelist has at least one entry, return that. - ChunkIndex index = bucket.freelist; - - bucket.freelist = get_freelist_entry(bucket.freelist).freelist.next_index; - if (bucket.freelist != -1) { - get_freelist_entry(bucket.freelist).freelist.prev_index = -1; - } - - VERIFY(bucket.get_buddy_bit(index) == true); - bucket.set_buddy_bit(index, false); - - return index; -} - -void PhysicalZone::deallocate_block(PhysicalAddress address, size_t order) -{ - size_t block_size = 2u << order; - ChunkIndex index = (address.get() - m_base_address.get()) / ZONE_CHUNK_SIZE; - deallocate_block_impl(index, order); - m_used_chunks -= block_size; -} - -void PhysicalZone::deallocate_block_impl(ChunkIndex index, size_t order) -{ - size_t block_size = 2u << order; - - // Basic algorithm: - // If the buddy block is free (buddy bit is 1 -- because this block was the only used one): - // Then, - // 1. Merge with buddy. - // 2. Return the merged block to order+1. - // Else (buddy bit is 0 -- because both blocks are used) - // 1. Add the block to the freelist. - // 2. Set buddy bit to 1. - auto& bucket = m_buckets[order]; - - if (bucket.get_buddy_bit(index)) { - // Buddy is free! Merge with buddy and coalesce upwards to the next order. - auto buddy_bit_index = bucket.buddy_bit_index(index); - ChunkIndex buddy_base_index = (buddy_bit_index << 1) << (1 + order); - - if (index == buddy_base_index) - remove_from_freelist(bucket, buddy_base_index + block_size); - else - remove_from_freelist(bucket, buddy_base_index); - - bucket.set_buddy_bit(index, false); - deallocate_block_impl(buddy_base_index, order + 1); - } else { - // Buddy is in use. Add freed block to freelist and set buddy bit to 1. - - if (bucket.freelist != -1) { - get_freelist_entry(bucket.freelist).freelist.prev_index = index; - } - - auto& freelist_entry = get_freelist_entry(index).freelist; - freelist_entry.next_index = bucket.freelist; - freelist_entry.prev_index = -1; - bucket.freelist = index; - - bucket.set_buddy_bit(index, true); - } -} - -void PhysicalZone::remove_from_freelist(BuddyBucket& bucket, ChunkIndex index) -{ - auto& freelist_entry = get_freelist_entry(index).freelist; - VERIFY(freelist_entry.prev_index >= -1); - VERIFY(freelist_entry.next_index >= -1); - if (freelist_entry.prev_index != -1) { - auto& prev_entry = get_freelist_entry(freelist_entry.prev_index).freelist; - prev_entry.next_index = freelist_entry.next_index; - } - if (freelist_entry.next_index != -1) { - auto& next_entry = get_freelist_entry(freelist_entry.next_index).freelist; - next_entry.prev_index = freelist_entry.prev_index; - } - if (bucket.freelist == index) - bucket.freelist = freelist_entry.next_index; - freelist_entry.next_index = -1; - freelist_entry.prev_index = -1; -} - -void PhysicalZone::dump() const -{ - dbgln("(( {} used, {} available, page_count: {} ))", m_used_chunks, available(), m_page_count); - for (size_t i = 0; i <= max_order; ++i) { - auto const& bucket = m_buckets[i]; - dbgln("[{:2} / {:4}] ", i, (size_t)(2u << i)); - auto entry = bucket.freelist; - while (entry != -1) { - dbgln(" {}", entry); - entry = get_freelist_entry(entry).freelist.next_index; - } - } -} - -} diff --git a/Kernel/Memory/PhysicalZone.h b/Kernel/Memory/PhysicalZone.h deleted file mode 100644 index 964d642b627..00000000000 --- a/Kernel/Memory/PhysicalZone.h +++ /dev/null @@ -1,94 +0,0 @@ -/* - * Copyright (c) 2021, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include - -namespace Kernel::Memory { - -// A PhysicalZone is an allocator that manages a sub-area of a PhysicalRegion. -// Its total size is always a power of two. -// You allocate chunks at a time. One chunk is PAGE_SIZE/2, and the minimum allocation size is 2 chunks. -// The allocator uses a buddy block scheme internally. - -class PhysicalZone { - AK_MAKE_NONCOPYABLE(PhysicalZone); - AK_MAKE_NONMOVABLE(PhysicalZone); - -public: - static constexpr size_t ZONE_CHUNK_SIZE = PAGE_SIZE / 2; - using ChunkIndex = i16; - - PhysicalZone(PhysicalAddress base, size_t page_count); - - Optional allocate_block(size_t order); - void deallocate_block(PhysicalAddress, size_t order); - - void dump() const; - size_t available() const { return m_page_count - (m_used_chunks / 2); } - - bool is_empty() const { return available() == 0; } - - PhysicalAddress base() const { return m_base_address; } - bool contains(PhysicalAddress paddr) const - { - return paddr >= m_base_address && paddr < m_base_address.offset(m_page_count * PAGE_SIZE); - } - -private: - Optional allocate_block_impl(size_t order); - void deallocate_block_impl(ChunkIndex, size_t order); - - struct BuddyBucket { - bool get_buddy_bit(ChunkIndex index) const - { - return bitmap.get(buddy_bit_index(index)); - } - - void set_buddy_bit(ChunkIndex index, bool value) - { - bitmap.set(buddy_bit_index(index), value); - } - - size_t buddy_bit_index(ChunkIndex index) const - { - // NOTE: We cut the index in half since one chunk is half a page. - return (index >> 1) >> (1 + order); - } - - // This bucket's index in the m_buckets array. (Redundant data kept here for convenience.) - size_t order { 0 }; - - // This is the start of the freelist for this buddy size. - // It's an index into the global PhysicalPageEntry array (offset by this PhysicalRegion's base.) - // A value of -1 indicates an empty freelist. - ChunkIndex freelist { -1 }; - - // Bitmap with 1 bit per buddy pair. - // 0 == Both blocks either free or used. - // 1 == One block free, one block used. - Bitmap bitmap; - }; - - static constexpr size_t max_order = 12; - BuddyBucket m_buckets[max_order + 1]; - - PhysicalPageEntry& get_freelist_entry(ChunkIndex) const; - void remove_from_freelist(BuddyBucket&, ChunkIndex); - - PhysicalAddress m_base_address { 0 }; - size_t m_page_count { 0 }; - size_t m_used_chunks { 0 }; - - IntrusiveListNode m_list_node; - -public: - using List = IntrusiveList<&PhysicalZone::m_list_node>; -}; - -} diff --git a/Kernel/Memory/PrivateInodeVMObject.cpp b/Kernel/Memory/PrivateInodeVMObject.cpp deleted file mode 100644 index 16be887ff65..00000000000 --- a/Kernel/Memory/PrivateInodeVMObject.cpp +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright (c) 2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include - -namespace Kernel::Memory { - -ErrorOr> PrivateInodeVMObject::try_create_with_inode(Inode& inode) -{ - if (inode.size() == 0) - return EINVAL; - return try_create_with_inode_and_range(inode, 0, inode.size()); -} - -ErrorOr> PrivateInodeVMObject::try_create_with_inode_and_range(Inode& inode, u64 offset, size_t range_size) -{ - // Note: To ensure further allocation of a Region with this VMObject will not complain - // on "smaller" VMObject than the requested Region, we simply take the max size between both values. - auto size = max(inode.size(), (offset + range_size)); - VERIFY(size > 0); - auto new_physical_pages = TRY(VMObject::try_create_physical_pages(size)); - auto dirty_pages = TRY(Bitmap::create(new_physical_pages.size(), false)); - return adopt_nonnull_lock_ref_or_enomem(new (nothrow) PrivateInodeVMObject(inode, move(new_physical_pages), move(dirty_pages))); -} - -ErrorOr> PrivateInodeVMObject::try_clone() -{ - auto new_physical_pages = TRY(this->try_clone_physical_pages()); - auto dirty_pages = TRY(Bitmap::create(new_physical_pages.size(), false)); - return adopt_nonnull_lock_ref_or_enomem(new (nothrow) PrivateInodeVMObject(*this, move(new_physical_pages), move(dirty_pages))); -} - -PrivateInodeVMObject::PrivateInodeVMObject(Inode& inode, FixedArray>&& new_physical_pages, Bitmap dirty_pages) - : InodeVMObject(inode, move(new_physical_pages), move(dirty_pages)) -{ -} - -PrivateInodeVMObject::PrivateInodeVMObject(PrivateInodeVMObject const& other, FixedArray>&& new_physical_pages, Bitmap dirty_pages) - : InodeVMObject(other, move(new_physical_pages), move(dirty_pages)) -{ -} - -PrivateInodeVMObject::~PrivateInodeVMObject() = default; - -} diff --git a/Kernel/Memory/PrivateInodeVMObject.h b/Kernel/Memory/PrivateInodeVMObject.h deleted file mode 100644 index ffedc70d7d5..00000000000 --- a/Kernel/Memory/PrivateInodeVMObject.h +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright (c) 2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include - -namespace Kernel::Memory { - -class PrivateInodeVMObject final : public InodeVMObject { - AK_MAKE_NONMOVABLE(PrivateInodeVMObject); - -public: - virtual ~PrivateInodeVMObject() override; - - static ErrorOr> try_create_with_inode(Inode&); - static ErrorOr> try_create_with_inode_and_range(Inode&, u64 offset, size_t range_size); - virtual ErrorOr> try_clone() override; - -private: - virtual bool is_private_inode() const override { return true; } - - explicit PrivateInodeVMObject(Inode&, FixedArray>&&, Bitmap dirty_pages); - explicit PrivateInodeVMObject(PrivateInodeVMObject const&, FixedArray>&&, Bitmap dirty_pages); - - virtual StringView class_name() const override { return "PrivateInodeVMObject"sv; } - - PrivateInodeVMObject& operator=(PrivateInodeVMObject const&) = delete; -}; - -} diff --git a/Kernel/Memory/Region.cpp b/Kernel/Memory/Region.cpp deleted file mode 100644 index 56b05a2edae..00000000000 --- a/Kernel/Memory/Region.cpp +++ /dev/null @@ -1,653 +0,0 @@ -/* - * Copyright (c) 2018-2022, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace Kernel::Memory { - -Region::Region() - : m_range(VirtualRange({}, 0)) -{ -} - -Region::Region(NonnullLockRefPtr vmobject, size_t offset_in_vmobject, OwnPtr name, Region::Access access, Cacheable cacheable, bool shared) - : m_range(VirtualRange({}, 0)) - , m_offset_in_vmobject(offset_in_vmobject) - , m_vmobject(move(vmobject)) - , m_name(move(name)) - , m_access(access | ((access & 0x7) << 4)) - , m_shared(shared) - , m_cacheable(cacheable == Cacheable::Yes) -{ - m_vmobject->add_region(*this); -} - -Region::Region(VirtualRange const& range, NonnullLockRefPtr vmobject, size_t offset_in_vmobject, OwnPtr name, Region::Access access, Cacheable cacheable, bool shared) - : m_range(range) - , m_offset_in_vmobject(offset_in_vmobject) - , m_vmobject(move(vmobject)) - , m_name(move(name)) - , m_access(access | ((access & 0x7) << 4)) - , m_shared(shared) - , m_cacheable(cacheable == Cacheable::Yes) -{ - VERIFY(m_range.base().is_page_aligned()); - VERIFY(m_range.size()); - VERIFY((m_range.size() % PAGE_SIZE) == 0); - - m_vmobject->add_region(*this); -} - -Region::~Region() -{ - if (is_writable() && vmobject().is_shared_inode()) { - // FIXME: This is very aggressive. Find a way to do less work! - (void)static_cast(vmobject()).sync(); - } - - m_vmobject->remove_region(*this); - - if (m_page_directory) { - SpinlockLocker pd_locker(m_page_directory->get_lock()); - if (!is_readable() && !is_writable() && !is_executable()) { - // If the region is "PROT_NONE", we didn't map it in the first place. - } else { - unmap_with_locks_held(ShouldFlushTLB::Yes, pd_locker); - VERIFY(!m_page_directory); - } - } - - if (is_kernel()) - MM.unregister_kernel_region(*this); - - // Extend the lifetime of the region if there are any page faults in progress for this region's pages. - // Both the removal of regions from the region trees and the fetching of the regions from the tree - // during the start of page fault handling are serialized under the address space spinlock. This means - // that once the region is removed no more page faults on this region can start, so this counter will - // eventually reach 0. And similarly since we can only reach the region destructor once the region was - // removed from the appropriate region tree, it is guaranteed that any page faults that are still being - // handled have already increased this counter, and will be allowed to finish before deallocation. - while (m_in_progress_page_faults) - Processor::wait_check(); -} - -ErrorOr> Region::create_unbacked() -{ - return adopt_nonnull_own_or_enomem(new (nothrow) Region); -} - -ErrorOr> Region::create_unplaced(NonnullLockRefPtr vmobject, size_t offset_in_vmobject, OwnPtr name, Region::Access access, Cacheable cacheable, bool shared) -{ - return adopt_nonnull_own_or_enomem(new (nothrow) Region(move(vmobject), offset_in_vmobject, move(name), access, cacheable, shared)); -} - -ErrorOr> Region::try_clone() -{ - VERIFY(Process::has_current()); - - if (m_shared) { - VERIFY(!m_stack); - if (vmobject().is_inode()) - VERIFY(vmobject().is_shared_inode()); - - // Create a new region backed by the same VMObject. - - OwnPtr region_name; - if (m_name) - region_name = TRY(m_name->try_clone()); - - auto region = TRY(Region::try_create_user_accessible( - m_range, vmobject(), m_offset_in_vmobject, move(region_name), access(), m_cacheable ? Cacheable::Yes : Cacheable::No, m_shared)); - region->set_mmap(m_mmap, m_mmapped_from_readable, m_mmapped_from_writable); - region->set_shared(m_shared); - region->set_syscall_region(is_syscall_region()); - return region; - } - - if (vmobject().is_inode()) - VERIFY(vmobject().is_private_inode()); - - auto vmobject_clone = TRY(vmobject().try_clone()); - - // Set up a COW region. The parent (this) region becomes COW as well! - if (is_writable()) - remap(); - - OwnPtr clone_region_name; - if (m_name) - clone_region_name = TRY(m_name->try_clone()); - - auto clone_region = TRY(Region::try_create_user_accessible( - m_range, move(vmobject_clone), m_offset_in_vmobject, move(clone_region_name), access(), m_cacheable ? Cacheable::Yes : Cacheable::No, m_shared)); - - if (m_stack) { - VERIFY(vmobject().is_anonymous()); - clone_region->set_stack(true); - } - clone_region->set_syscall_region(is_syscall_region()); - clone_region->set_mmap(m_mmap, m_mmapped_from_readable, m_mmapped_from_writable); - return clone_region; -} - -void Region::set_vmobject(NonnullLockRefPtr&& obj) -{ - if (m_vmobject.ptr() == obj.ptr()) - return; - m_vmobject->remove_region(*this); - m_vmobject = move(obj); - m_vmobject->add_region(*this); -} - -size_t Region::cow_pages() const -{ - if (!vmobject().is_anonymous()) - return 0; - return static_cast(vmobject()).cow_pages(); -} - -size_t Region::amount_dirty() const -{ - if (!vmobject().is_inode()) - return amount_resident(); - return static_cast(vmobject()).amount_dirty(); -} - -size_t Region::amount_resident() const -{ - size_t bytes = 0; - for (size_t i = 0; i < page_count(); ++i) { - auto page = physical_page(i); - if (page && !page->is_shared_zero_page() && !page->is_lazy_committed_page()) - bytes += PAGE_SIZE; - } - return bytes; -} - -size_t Region::amount_shared() const -{ - size_t bytes = 0; - for (size_t i = 0; i < page_count(); ++i) { - auto page = physical_page(i); - if (page && page->ref_count() > 1 && !page->is_shared_zero_page() && !page->is_lazy_committed_page()) - bytes += PAGE_SIZE; - } - return bytes; -} - -ErrorOr> Region::try_create_user_accessible(VirtualRange const& range, NonnullLockRefPtr vmobject, size_t offset_in_vmobject, OwnPtr name, Region::Access access, Cacheable cacheable, bool shared) -{ - return adopt_nonnull_own_or_enomem(new (nothrow) Region(range, move(vmobject), offset_in_vmobject, move(name), access, cacheable, shared)); -} - -bool Region::should_cow(size_t page_index) const -{ - if (!vmobject().is_anonymous()) - return false; - return static_cast(vmobject()).should_cow(first_page_index() + page_index, m_shared); -} - -ErrorOr Region::set_should_cow(size_t page_index, bool cow) -{ - VERIFY(!m_shared); - if (vmobject().is_anonymous()) - TRY(static_cast(vmobject()).set_should_cow(first_page_index() + page_index, cow)); - return {}; -} - -bool Region::map_individual_page_impl(size_t page_index, RefPtr page) -{ - if (!page) - return map_individual_page_impl(page_index, {}, false, false); - return map_individual_page_impl(page_index, page->paddr(), is_readable(), is_writable() && !page->is_shared_zero_page() && !page->is_lazy_committed_page()); -} - -bool Region::map_individual_page_impl(size_t page_index, PhysicalAddress paddr) -{ - return map_individual_page_impl(page_index, paddr, is_readable(), is_writable()); -} - -bool Region::map_individual_page_impl(size_t page_index, PhysicalAddress paddr, bool readable, bool writeable) -{ - VERIFY(m_page_directory->get_lock().is_locked_by_current_processor()); - - auto page_vaddr = vaddr_from_page_index(page_index); - - bool user_allowed = page_vaddr.get() >= USER_RANGE_BASE && is_user_address(page_vaddr); - if (is_mmap() && !user_allowed) { - PANIC("About to map mmap'ed page at a kernel address"); - } - - auto* pte = MM.ensure_pte(*m_page_directory, page_vaddr); - if (!pte) - return false; - - if (!readable && !writeable) { - pte->clear(); - return true; - } - - pte->set_cache_disabled(!m_cacheable); - pte->set_physical_page_base(paddr.get()); - pte->set_present(true); - pte->set_writable(writeable && !should_cow(page_index)); - if (Processor::current().has_nx()) - pte->set_execute_disabled(!is_executable()); - if (Processor::current().has_pat()) - pte->set_pat(is_write_combine()); - pte->set_user_allowed(user_allowed); - - return true; -} - -bool Region::map_individual_page_impl(size_t page_index) -{ - RefPtr page; - { - SpinlockLocker vmobject_locker(vmobject().m_lock); - page = physical_page(page_index); - } - - return map_individual_page_impl(page_index, page); -} - -bool Region::remap_vmobject_page(size_t page_index, NonnullRefPtr physical_page) -{ - SpinlockLocker page_lock(m_page_directory->get_lock()); - - // NOTE: `page_index` is a VMObject page index, so first we convert it to a Region page index. - if (!translate_vmobject_page(page_index)) - return false; - - bool success = map_individual_page_impl(page_index, physical_page); - MemoryManager::flush_tlb(m_page_directory, vaddr_from_page_index(page_index)); - return success; -} - -void Region::unmap(ShouldFlushTLB should_flush_tlb) -{ - if (!m_page_directory) - return; - SpinlockLocker pd_locker(m_page_directory->get_lock()); - unmap_with_locks_held(should_flush_tlb, pd_locker); -} - -void Region::unmap_with_locks_held(ShouldFlushTLB should_flush_tlb, SpinlockLocker>&) -{ - if (!m_page_directory) - return; - size_t count = page_count(); - for (size_t i = 0; i < count; ++i) { - auto vaddr = vaddr_from_page_index(i); - MM.release_pte(*m_page_directory, vaddr, i == count - 1 ? MemoryManager::IsLastPTERelease::Yes : MemoryManager::IsLastPTERelease::No); - } - if (should_flush_tlb == ShouldFlushTLB::Yes) - MemoryManager::flush_tlb(m_page_directory, vaddr(), page_count()); - m_page_directory = nullptr; -} - -void Region::set_page_directory(PageDirectory& page_directory) -{ - VERIFY(!m_page_directory || m_page_directory == &page_directory); - m_page_directory = page_directory; -} - -ErrorOr Region::map(PageDirectory& page_directory, ShouldFlushTLB should_flush_tlb) -{ - SpinlockLocker page_lock(page_directory.get_lock()); - - // FIXME: Find a better place for this sanity check(?) - if (is_user() && !is_shared()) { - VERIFY(!vmobject().is_shared_inode()); - } - - set_page_directory(page_directory); - size_t page_index = 0; - while (page_index < page_count()) { - if (!map_individual_page_impl(page_index)) - break; - ++page_index; - } - if (page_index > 0) { - if (should_flush_tlb == ShouldFlushTLB::Yes) - MemoryManager::flush_tlb(m_page_directory, vaddr(), page_index); - if (page_index == page_count()) - return {}; - } - return ENOMEM; -} - -ErrorOr Region::map(PageDirectory& page_directory, PhysicalAddress paddr, ShouldFlushTLB should_flush_tlb) -{ - SpinlockLocker page_lock(page_directory.get_lock()); - set_page_directory(page_directory); - size_t page_index = 0; - while (page_index < page_count()) { - if (!map_individual_page_impl(page_index, paddr)) - break; - ++page_index; - paddr = paddr.offset(PAGE_SIZE); - } - if (page_index > 0) { - if (should_flush_tlb == ShouldFlushTLB::Yes) - MemoryManager::flush_tlb(m_page_directory, vaddr(), page_index); - if (page_index == page_count()) - return {}; - } - return ENOMEM; -} - -void Region::remap() -{ - VERIFY(m_page_directory); - auto result = map(*m_page_directory); - if (result.is_error()) - TODO(); -} - -ErrorOr Region::set_write_combine(bool enable) -{ - if (enable && !Processor::current().has_pat()) { - dbgln("PAT is not supported, implement MTRR fallback if available"); - return Error::from_errno(ENOTSUP); - } - - m_write_combine = enable; - remap(); - return {}; -} - -void Region::clear_to_zero() -{ - VERIFY(vmobject().is_anonymous()); - SpinlockLocker locker(vmobject().m_lock); - for (auto i = 0u; i < page_count(); ++i) { - auto& page = physical_page_slot(i); - VERIFY(page); - if (page->is_shared_zero_page()) - continue; - page = MM.shared_zero_page(); - } -} - -PageFaultResponse Region::handle_fault(PageFault const& fault) -{ -#if !ARCH(RISCV64) - auto page_index_in_region = page_index_from_address(fault.vaddr()); - if (fault.type() == PageFault::Type::PageNotPresent) { - if (fault.is_read() && !is_readable()) { - dbgln("NP(non-readable) fault in Region({})[{}]", this, page_index_in_region); - return PageFaultResponse::ShouldCrash; - } - if (fault.is_write() && !is_writable()) { - dbgln("NP(non-writable) write fault in Region({})[{}] at {}", this, page_index_in_region, fault.vaddr()); - return PageFaultResponse::ShouldCrash; - } - if (vmobject().is_inode()) { - dbgln_if(PAGE_FAULT_DEBUG, "NP(inode) fault in Region({})[{}]", this, page_index_in_region); - return handle_inode_fault(page_index_in_region); - } - - SpinlockLocker vmobject_locker(vmobject().m_lock); - auto& page_slot = physical_page_slot(page_index_in_region); - if (page_slot->is_lazy_committed_page()) { - auto page_index_in_vmobject = translate_to_vmobject_page(page_index_in_region); - VERIFY(m_vmobject->is_anonymous()); - page_slot = static_cast(*m_vmobject).allocate_committed_page({}); - if (!remap_vmobject_page(page_index_in_vmobject, *page_slot)) - return PageFaultResponse::OutOfMemory; - return PageFaultResponse::Continue; - } - dbgln("BUG! Unexpected NP fault at {}", fault.vaddr()); - dbgln(" - Physical page slot pointer: {:p}", page_slot.ptr()); - if (page_slot) { - dbgln(" - Physical page: {}", page_slot->paddr()); - dbgln(" - Lazy committed: {}", page_slot->is_lazy_committed_page()); - dbgln(" - Shared zero: {}", page_slot->is_shared_zero_page()); - } - return PageFaultResponse::ShouldCrash; - } - VERIFY(fault.type() == PageFault::Type::ProtectionViolation); - if (fault.access() == PageFault::Access::Write && is_writable() && should_cow(page_index_in_region)) { - dbgln_if(PAGE_FAULT_DEBUG, "PV(cow) fault in Region({})[{}] at {}", this, page_index_in_region, fault.vaddr()); - auto phys_page = physical_page(page_index_in_region); - if (phys_page->is_shared_zero_page() || phys_page->is_lazy_committed_page()) { - dbgln_if(PAGE_FAULT_DEBUG, "NP(zero) fault in Region({})[{}] at {}", this, page_index_in_region, fault.vaddr()); - return handle_zero_fault(page_index_in_region, *phys_page); - } - return handle_cow_fault(page_index_in_region); - } - dbgln("PV(error) fault in Region({})[{}] at {}", this, page_index_in_region, fault.vaddr()); - return PageFaultResponse::ShouldCrash; -#else - // FIXME: Consider to merge the RISC-V page fault handling code with the x86_64/aarch64 implementation. - // RISC-V doesn't tell you *why* a memory access failed, only the original access type (r/w/x). - // We probably should take the page fault reason into account, if the processor provides it. - - auto page_index_in_region = page_index_from_address(fault.vaddr()); - - if (fault.is_read() && !is_readable()) { - dbgln("Read page fault in non-readable Region({})[{}]", this, page_index_in_region); - return PageFaultResponse::ShouldCrash; - } - - if (fault.is_write() && !is_writable()) { - dbgln("Write page fault in non-writable Region({})[{}] at {}", this, page_index_in_region, fault.vaddr()); - return PageFaultResponse::ShouldCrash; - } - - if (fault.is_instruction_fetch() && !is_executable()) { - dbgln("Instruction fetch page fault in non-executable Region({})[{}] at {}", this, page_index_in_region, fault.vaddr()); - return PageFaultResponse::ShouldCrash; - } - - if (fault.is_write() && is_writable() && should_cow(page_index_in_region)) { - dbgln_if(PAGE_FAULT_DEBUG, "CoW page fault in Region({})[{}] at {}", this, page_index_in_region, fault.vaddr()); - auto phys_page = physical_page(page_index_in_region); - if (phys_page->is_shared_zero_page() || phys_page->is_lazy_committed_page()) { - dbgln_if(PAGE_FAULT_DEBUG, "Zero page fault in Region({})[{}] at {}", this, page_index_in_region, fault.vaddr()); - return handle_zero_fault(page_index_in_region, *phys_page); - } - return handle_cow_fault(page_index_in_region); - } - - if (vmobject().is_inode()) { - dbgln_if(PAGE_FAULT_DEBUG, "Inode page fault in Region({})[{}]", this, page_index_in_region); - return handle_inode_fault(page_index_in_region); - } - - SpinlockLocker vmobject_locker(vmobject().m_lock); - auto& page_slot = physical_page_slot(page_index_in_region); - if (page_slot->is_lazy_committed_page()) { - auto page_index_in_vmobject = translate_to_vmobject_page(page_index_in_region); - VERIFY(m_vmobject->is_anonymous()); - page_slot = static_cast(*m_vmobject).allocate_committed_page({}); - if (!remap_vmobject_page(page_index_in_vmobject, *page_slot)) - return PageFaultResponse::OutOfMemory; - return PageFaultResponse::Continue; - } - - dbgln("Unexpected page fault in Region({})[{}] at {}", this, page_index_in_region, fault.vaddr()); - return PageFaultResponse::ShouldCrash; -#endif -} - -PageFaultResponse Region::handle_zero_fault(size_t page_index_in_region, PhysicalRAMPage& page_in_slot_at_time_of_fault) -{ - VERIFY(vmobject().is_anonymous()); - - auto page_index_in_vmobject = translate_to_vmobject_page(page_index_in_region); - - auto current_thread = Thread::current(); - if (current_thread != nullptr) - current_thread->did_zero_fault(); - - RefPtr new_physical_page; - - if (page_in_slot_at_time_of_fault.is_lazy_committed_page()) { - VERIFY(m_vmobject->is_anonymous()); - new_physical_page = static_cast(*m_vmobject).allocate_committed_page({}); - dbgln_if(PAGE_FAULT_DEBUG, " >> ALLOCATED COMMITTED {}", new_physical_page->paddr()); - } else { - auto page_or_error = MM.allocate_physical_page(MemoryManager::ShouldZeroFill::Yes); - if (page_or_error.is_error()) { - dmesgln("MM: handle_zero_fault was unable to allocate a physical page"); - return PageFaultResponse::OutOfMemory; - } - new_physical_page = page_or_error.release_value(); - dbgln_if(PAGE_FAULT_DEBUG, " >> ALLOCATED {}", new_physical_page->paddr()); - } - - bool already_handled = false; - - { - SpinlockLocker locker(vmobject().m_lock); - auto& page_slot = physical_page_slot(page_index_in_region); - already_handled = !page_slot.is_null() && !page_slot->is_shared_zero_page() && !page_slot->is_lazy_committed_page(); - if (already_handled) { - // Someone else already faulted in a new page in this slot. That's fine, we'll just remap with their page. - new_physical_page = page_slot; - } else { - // Install the newly allocated page into the VMObject. - page_slot = new_physical_page; - } - } - - if (!remap_vmobject_page(page_index_in_vmobject, *new_physical_page)) { - dmesgln("MM: handle_zero_fault was unable to allocate a page table to map {}", new_physical_page); - return PageFaultResponse::OutOfMemory; - } - return PageFaultResponse::Continue; -} - -PageFaultResponse Region::handle_cow_fault(size_t page_index_in_region) -{ - auto current_thread = Thread::current(); - if (current_thread) - current_thread->did_cow_fault(); - - if (!vmobject().is_anonymous()) - return PageFaultResponse::ShouldCrash; - - auto page_index_in_vmobject = translate_to_vmobject_page(page_index_in_region); - auto response = reinterpret_cast(vmobject()).handle_cow_fault(page_index_in_vmobject, vaddr().offset(page_index_in_region * PAGE_SIZE)); - if (!remap_vmobject_page(page_index_in_vmobject, *vmobject().physical_pages()[page_index_in_vmobject])) - return PageFaultResponse::OutOfMemory; - return response; -} - -PageFaultResponse Region::handle_inode_fault(size_t page_index_in_region) -{ - VERIFY(vmobject().is_inode()); - VERIFY(!g_scheduler_lock.is_locked_by_current_processor()); - - auto& inode_vmobject = static_cast(vmobject()); - - auto page_index_in_vmobject = translate_to_vmobject_page(page_index_in_region); - auto& vmobject_physical_page_slot = inode_vmobject.physical_pages()[page_index_in_vmobject]; - - { - // NOTE: The VMObject lock is required when manipulating the VMObject's physical page slot. - SpinlockLocker locker(inode_vmobject.m_lock); - if (!vmobject_physical_page_slot.is_null()) { - dbgln_if(PAGE_FAULT_DEBUG, "handle_inode_fault: Page faulted in by someone else before reading, remapping."); - if (!remap_vmobject_page(page_index_in_vmobject, *vmobject_physical_page_slot)) - return PageFaultResponse::OutOfMemory; - return PageFaultResponse::Continue; - } - } - - dbgln_if(PAGE_FAULT_DEBUG, "Inode fault in {} page index: {}", name(), page_index_in_region); - - auto current_thread = Thread::current(); - if (current_thread) - current_thread->did_inode_fault(); - - u8 page_buffer[PAGE_SIZE]; - auto& inode = inode_vmobject.inode(); - - auto buffer = UserOrKernelBuffer::for_kernel_buffer(page_buffer); - auto result = inode.read_bytes(page_index_in_vmobject * PAGE_SIZE, PAGE_SIZE, buffer, nullptr); - - if (result.is_error()) { - dmesgln("handle_inode_fault: Error ({}) while reading from inode", result.error()); - return PageFaultResponse::ShouldCrash; - } - - auto nread = result.value(); - // Note: If we received 0, it means we are at the end of file or after it, - // which means we should return bus error. - if (nread == 0) - return PageFaultResponse::BusError; - - if (nread < PAGE_SIZE) { - // If we read less than a page, zero out the rest to avoid leaking uninitialized data. - memset(page_buffer + nread, 0, PAGE_SIZE - nread); - } - - // Allocate a new physical page, and copy the read inode contents into it. - auto new_physical_page_or_error = MM.allocate_physical_page(MemoryManager::ShouldZeroFill::No); - if (new_physical_page_or_error.is_error()) { - dmesgln("MM: handle_inode_fault was unable to allocate a physical page"); - return PageFaultResponse::OutOfMemory; - } - auto new_physical_page = new_physical_page_or_error.release_value(); - { - InterruptDisabler disabler; - u8* dest_ptr = MM.quickmap_page(*new_physical_page); - memcpy(dest_ptr, page_buffer, PAGE_SIZE); - MM.unquickmap_page(); - } - - { - // NOTE: The VMObject lock is required when manipulating the VMObject's physical page slot. - SpinlockLocker locker(inode_vmobject.m_lock); - - if (!vmobject_physical_page_slot.is_null()) { - // Someone else faulted in this page while we were reading from the inode. - // No harm done (other than some duplicate work), remap the page here and return. - dbgln_if(PAGE_FAULT_DEBUG, "handle_inode_fault: Page faulted in by someone else, remapping."); - if (!remap_vmobject_page(page_index_in_vmobject, *vmobject_physical_page_slot)) - return PageFaultResponse::OutOfMemory; - return PageFaultResponse::Continue; - } - - vmobject_physical_page_slot = new_physical_page; - } - - if (!remap_vmobject_page(page_index_in_vmobject, *vmobject_physical_page_slot)) - return PageFaultResponse::OutOfMemory; - - return PageFaultResponse::Continue; -} - -RefPtr Region::physical_page(size_t index) const -{ - SpinlockLocker vmobject_locker(vmobject().m_lock); - VERIFY(index < page_count()); - return vmobject().physical_pages()[first_page_index() + index]; -} - -RefPtr& Region::physical_page_slot(size_t index) -{ - VERIFY(vmobject().m_lock.is_locked_by_current_processor()); - VERIFY(index < page_count()); - return vmobject().physical_pages()[first_page_index() + index]; -} - -} diff --git a/Kernel/Memory/Region.h b/Kernel/Memory/Region.h deleted file mode 100644 index 4be8f59f83a..00000000000 --- a/Kernel/Memory/Region.h +++ /dev/null @@ -1,309 +0,0 @@ -/* - * Copyright (c) 2018-2022, Andreas Kling - * Copyright (c) 2022, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace Kernel { -class PageFault; -} - -namespace Kernel::Memory { - -enum class ShouldFlushTLB { - No, - Yes, -}; - -class Region final - : public LockWeakable { - friend class AddressSpace; - friend class MemoryManager; - friend class RegionTree; - -public: - enum Access : u8 { - None = 0, - Read = 1, - Write = 2, - Execute = 4, - ReadOnly = Read, - ReadWrite = Read | Write, - ReadWriteExecute = Read | Write | Execute, - }; - - enum class Cacheable { - No = 0, - Yes, - }; - - static ErrorOr> try_create_user_accessible(VirtualRange const&, NonnullLockRefPtr, size_t offset_in_vmobject, OwnPtr name, Region::Access access, Cacheable, bool shared); - static ErrorOr> create_unbacked(); - static ErrorOr> create_unplaced(NonnullLockRefPtr, size_t offset_in_vmobject, OwnPtr name, Region::Access access, Cacheable = Cacheable::Yes, bool shared = false); - - ~Region(); - - [[nodiscard]] VirtualRange const& range() const { return m_range; } - [[nodiscard]] VirtualAddress vaddr() const { return m_range.base(); } - [[nodiscard]] size_t size() const { return m_range.size(); } - [[nodiscard]] bool is_readable() const { return (m_access & Access::Read) == Access::Read; } - [[nodiscard]] bool is_writable() const { return (m_access & Access::Write) == Access::Write; } - [[nodiscard]] bool is_executable() const { return (m_access & Access::Execute) == Access::Execute; } - - [[nodiscard]] bool has_been_readable() const { return m_has_been_readable.was_set(); } - [[nodiscard]] bool has_been_writable() const { return m_has_been_writable.was_set(); } - [[nodiscard]] bool has_been_executable() const { return m_has_been_executable.was_set(); } - - [[nodiscard]] bool is_cacheable() const { return m_cacheable; } - [[nodiscard]] StringView name() const { return m_name ? m_name->view() : StringView {}; } - [[nodiscard]] OwnPtr take_name() { return move(m_name); } - [[nodiscard]] Region::Access access() const { return static_cast(m_access); } - - void set_name(OwnPtr name) { m_name = move(name); } - - [[nodiscard]] VMObject const& vmobject() const { return *m_vmobject; } - [[nodiscard]] VMObject& vmobject() { return *m_vmobject; } - void set_vmobject(NonnullLockRefPtr&&); - - [[nodiscard]] bool is_shared() const { return m_shared; } - void set_shared(bool shared) { m_shared = shared; } - - [[nodiscard]] bool is_stack() const { return m_stack; } - void set_stack(bool stack) { m_stack = stack; } - - [[nodiscard]] bool is_immutable() const { return m_immutable.was_set(); } - void set_immutable() { m_immutable.set(); } - - [[nodiscard]] bool is_mmap() const { return m_mmap; } - - void set_mmap(bool mmap, bool description_was_readable, bool description_was_writable) - { - m_mmap = mmap; - m_mmapped_from_readable = description_was_readable; - m_mmapped_from_writable = description_was_writable; - } - - [[nodiscard]] bool is_initially_loaded_executable_segment() const { return m_initially_loaded_executable_segment.was_set(); } - void set_initially_loaded_executable_segment() { m_initially_loaded_executable_segment.set(); } - - [[nodiscard]] bool is_write_combine() const { return m_write_combine; } - ErrorOr set_write_combine(bool); - - [[nodiscard]] bool is_user() const { return !is_kernel(); } - [[nodiscard]] bool is_kernel() const { return vaddr().get() < USER_RANGE_BASE || vaddr().get() >= kernel_mapping_base; } - - PageFaultResponse handle_fault(PageFault const&); - - ErrorOr> try_clone(); - - [[nodiscard]] bool contains(VirtualAddress vaddr) const - { - return m_range.contains(vaddr); - } - - [[nodiscard]] bool contains(VirtualRange const& range) const - { - return m_range.contains(range); - } - - [[nodiscard]] unsigned page_index_from_address(VirtualAddress vaddr) const - { - return (vaddr - m_range.base()).get() / PAGE_SIZE; - } - - [[nodiscard]] VirtualAddress vaddr_from_page_index(size_t page_index) const - { - return vaddr().offset(page_index * PAGE_SIZE); - } - - [[nodiscard]] bool translate_vmobject_page(size_t& index) const - { - auto first_index = first_page_index(); - if (index < first_index) { - index = first_index; - return false; - } - index -= first_index; - auto total_page_count = this->page_count(); - if (index >= total_page_count) { - index = first_index + total_page_count - 1; - return false; - } - return true; - } - - [[nodiscard]] ALWAYS_INLINE size_t translate_to_vmobject_page(size_t page_index) const - { - return first_page_index() + page_index; - } - - [[nodiscard]] size_t first_page_index() const - { - return m_offset_in_vmobject / PAGE_SIZE; - } - - [[nodiscard]] size_t page_count() const - { - return size() / PAGE_SIZE; - } - - RefPtr physical_page(size_t index) const; - RefPtr& physical_page_slot(size_t index); - - [[nodiscard]] size_t offset_in_vmobject() const - { - return m_offset_in_vmobject; - } - - [[nodiscard]] size_t offset_in_vmobject_from_vaddr(VirtualAddress vaddr) const - { - return m_offset_in_vmobject + vaddr.get() - this->vaddr().get(); - } - - [[nodiscard]] size_t amount_resident() const; - [[nodiscard]] size_t amount_shared() const; - [[nodiscard]] size_t amount_dirty() const; - - [[nodiscard]] bool should_cow(size_t page_index) const; - ErrorOr set_should_cow(size_t page_index, bool); - - [[nodiscard]] size_t cow_pages() const; - - void set_readable(bool b) - { - set_access_bit(Access::Read, b); - if (b) - m_has_been_readable.set(); - } - void set_writable(bool b) - { - set_access_bit(Access::Write, b); - if (b) - m_has_been_writable.set(); - } - void set_executable(bool b) - { - set_access_bit(Access::Execute, b); - if (b) - m_has_been_executable.set(); - } - - void unsafe_clear_access() { m_access = Region::None; } - - void set_page_directory(PageDirectory&); - ErrorOr map(PageDirectory&, ShouldFlushTLB = ShouldFlushTLB::Yes); - ErrorOr map(PageDirectory&, PhysicalAddress, ShouldFlushTLB = ShouldFlushTLB::Yes); - void unmap(ShouldFlushTLB = ShouldFlushTLB::Yes); - void unmap_with_locks_held(ShouldFlushTLB, SpinlockLocker>& pd_locker); - - void remap(); - - [[nodiscard]] bool is_mapped() const { return m_page_directory != nullptr; } - - void clear_to_zero(); - - [[nodiscard]] bool is_syscall_region() const { return m_syscall_region; } - void set_syscall_region(bool b) { m_syscall_region = b; } - - [[nodiscard]] bool mmapped_from_readable() const { return m_mmapped_from_readable; } - [[nodiscard]] bool mmapped_from_writable() const { return m_mmapped_from_writable; } - - void start_handling_page_fault(Badge) { m_in_progress_page_faults++; } - void finish_handling_page_fault(Badge) { m_in_progress_page_faults--; } - -private: - Region(); - Region(NonnullLockRefPtr, size_t offset_in_vmobject, OwnPtr, Region::Access access, Cacheable, bool shared); - Region(VirtualRange const&, NonnullLockRefPtr, size_t offset_in_vmobject, OwnPtr, Region::Access access, Cacheable, bool shared); - - [[nodiscard]] bool remap_vmobject_page(size_t page_index, NonnullRefPtr); - - void set_access_bit(Access access, bool b) - { - if (b) - m_access |= access; - else - m_access &= ~access; - } - - [[nodiscard]] PageFaultResponse handle_cow_fault(size_t page_index); - [[nodiscard]] PageFaultResponse handle_inode_fault(size_t page_index); - [[nodiscard]] PageFaultResponse handle_zero_fault(size_t page_index, PhysicalRAMPage& page_in_slot_at_time_of_fault); - - [[nodiscard]] bool map_individual_page_impl(size_t page_index); - [[nodiscard]] bool map_individual_page_impl(size_t page_index, RefPtr); - [[nodiscard]] bool map_individual_page_impl(size_t page_index, PhysicalAddress); - [[nodiscard]] bool map_individual_page_impl(size_t page_index, PhysicalAddress, bool readable, bool writeable); - - LockRefPtr m_page_directory; - VirtualRange m_range; - size_t m_offset_in_vmobject { 0 }; - LockRefPtr m_vmobject; - OwnPtr m_name; - Atomic m_in_progress_page_faults; - u8 m_access { Region::None }; - bool m_shared : 1 { false }; - bool m_cacheable : 1 { false }; - bool m_stack : 1 { false }; - bool m_mmap : 1 { false }; - bool m_syscall_region : 1 { false }; - bool m_write_combine : 1 { false }; - bool m_mmapped_from_readable : 1 { false }; - bool m_mmapped_from_writable : 1 { false }; - - SetOnce m_immutable; - SetOnce m_initially_loaded_executable_segment; - SetOnce m_has_been_readable; - SetOnce m_has_been_writable; - SetOnce m_has_been_executable; - - IntrusiveRedBlackTreeNode> m_tree_node; - IntrusiveListNode m_vmobject_list_node; - -public: - using ListInVMObject = IntrusiveList<&Region::m_vmobject_list_node>; -}; - -AK_ENUM_BITWISE_OPERATORS(Region::Access) - -constexpr Region::Access prot_to_region_access_flags(int prot) -{ - Region::Access access = Region::Access::None; - if ((prot & PROT_READ) == PROT_READ) - access |= Region::Access::Read; - if ((prot & PROT_WRITE) == PROT_WRITE) - access |= Region::Access::Write; - if ((prot & PROT_EXEC) == PROT_EXEC) - access |= Region::Access::Execute; - return access; -} - -constexpr int region_access_flags_to_prot(Region::Access access) -{ - int prot = 0; - if ((access & Region::Access::Read) == Region::Access::Read) - prot |= PROT_READ; - if ((access & Region::Access::Write) == Region::Access::Write) - prot |= PROT_WRITE; - if ((access & Region::Access::Execute) == Region::Access::Execute) - prot |= PROT_EXEC; - return prot; -} - -} diff --git a/Kernel/Memory/RegionTree.cpp b/Kernel/Memory/RegionTree.cpp deleted file mode 100644 index 505fe21f7da..00000000000 --- a/Kernel/Memory/RegionTree.cpp +++ /dev/null @@ -1,169 +0,0 @@ -/* - * Copyright (c) 2022, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include - -namespace Kernel::Memory { - -RegionTree::~RegionTree() -{ - delete_all_regions_assuming_they_are_unmapped(); -} - -void RegionTree::delete_all_regions_assuming_they_are_unmapped() -{ - // FIXME: This could definitely be done in a more efficient manner. - while (!m_regions.is_empty()) { - auto& region = *m_regions.begin(); - m_regions.remove(region.vaddr().get()); - delete ®ion; - } -} - -ErrorOr RegionTree::allocate_range_anywhere(size_t size, size_t alignment) -{ - if (!size) - return EINVAL; - - VERIFY((size % PAGE_SIZE) == 0); - VERIFY((alignment % PAGE_SIZE) == 0); - - if (Checked::addition_would_overflow(size, alignment)) - return EOVERFLOW; - - VirtualAddress window_start = m_total_range.base(); - - auto allocate_from_window = [&](VirtualRange const& window) -> Optional { - // FIXME: This check is probably excluding some valid candidates when using a large alignment. - if (window.size() < (size + alignment)) - return {}; - - FlatPtr initial_base = window.base().get(); - FlatPtr aligned_base = round_up_to_power_of_two(initial_base, alignment); - - VERIFY(size); - - return VirtualRange { VirtualAddress(aligned_base), size }; - }; - - for (auto it = m_regions.begin(); !it.is_end(); ++it) { - auto& region = *it; - - if (window_start == region.vaddr()) { - window_start = region.range().end(); - continue; - } - - VirtualRange window { window_start, region.vaddr().get() - window_start.get() }; - window_start = region.range().end(); - - if (auto maybe_range = allocate_from_window(window); maybe_range.has_value()) - return maybe_range.release_value(); - } - - VirtualRange window { window_start, m_total_range.end().get() - window_start.get() }; - if (m_total_range.contains(window)) { - if (auto maybe_range = allocate_from_window(window); maybe_range.has_value()) - return maybe_range.release_value(); - } - - dmesgln("RegionTree: Failed to allocate anywhere: size={}, alignment={}", size, alignment); - return ENOMEM; -} - -ErrorOr RegionTree::allocate_range_specific(VirtualAddress base, size_t size) -{ - if (!size) - return EINVAL; - - VERIFY(base.is_page_aligned()); - VERIFY((size % PAGE_SIZE) == 0); - - VirtualRange const range { base, size }; - if (!m_total_range.contains(range)) - return ENOMEM; - - auto* region = m_regions.find_largest_not_above(base.offset(size - 1).get()); - if (!region) { - // The range can be accommodated below the current lowest range. - return range; - } - - if (region->range().intersects(range)) { - // Requested range overlaps an existing range. - return ENOMEM; - } - - // Requested range fits between first region and its next neighbor. - return range; -} - -ErrorOr RegionTree::allocate_range_randomized(size_t size, size_t alignment) -{ - if (!size) - return EINVAL; - - VERIFY((size % PAGE_SIZE) == 0); - VERIFY((alignment % PAGE_SIZE) == 0); - - // FIXME: I'm sure there's a smarter way to do this. - constexpr size_t maximum_randomization_attempts = 1000; - for (size_t i = 0; i < maximum_randomization_attempts; ++i) { - VirtualAddress random_address { round_up_to_power_of_two(get_fast_random() % m_total_range.end().get(), alignment) }; - - if (!m_total_range.contains(random_address, size)) - continue; - - auto range_or_error = allocate_range_specific(random_address, size); - if (!range_or_error.is_error()) - return range_or_error.release_value(); - } - - return allocate_range_anywhere(size, alignment); -} - -ErrorOr RegionTree::place_anywhere(Region& region, RandomizeVirtualAddress randomize_virtual_address, size_t size, size_t alignment) -{ - auto range = TRY(randomize_virtual_address == RandomizeVirtualAddress::Yes ? allocate_range_randomized(size, alignment) : allocate_range_anywhere(size, alignment)); - region.m_range = range; - m_regions.insert(region.vaddr().get(), region); - return {}; -} - -ErrorOr RegionTree::place_specifically(Region& region, VirtualRange const& range) -{ - auto allocated_range = TRY(allocate_range_specific(range.base(), range.size())); - region.m_range = allocated_range; - m_regions.insert(region.vaddr().get(), region); - return {}; -} - -bool RegionTree::remove(Region& region) -{ - return m_regions.remove(region.range().base().get()); -} - -Region* RegionTree::find_region_containing(VirtualAddress address) -{ - auto* region = m_regions.find_largest_not_above(address.get()); - if (!region || !region->contains(address)) - return nullptr; - return region; -} - -Region* RegionTree::find_region_containing(VirtualRange range) -{ - auto* region = m_regions.find_largest_not_above(range.base().get()); - if (!region || !region->contains(range)) - return nullptr; - return region; -} - -} diff --git a/Kernel/Memory/RegionTree.h b/Kernel/Memory/RegionTree.h deleted file mode 100644 index d1b06f3ec1a..00000000000 --- a/Kernel/Memory/RegionTree.h +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright (c) 2022, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include -#include - -namespace Kernel::Memory { - -enum class RandomizeVirtualAddress { - No, - Yes, -}; - -// RegionTree represents a virtual address space. -// It is used by MemoryManager for kernel VM and by AddressSpace for user VM. -// Regions are stored in an intrusive data structure and there are no allocations when interacting with it. -class RegionTree { - AK_MAKE_NONCOPYABLE(RegionTree); - AK_MAKE_NONMOVABLE(RegionTree); - -public: - explicit RegionTree(VirtualRange total_range) - : m_total_range(total_range) - { - } - - ~RegionTree(); - - auto& regions() { return m_regions; } - auto const& regions() const { return m_regions; } - - VirtualRange total_range() const { return m_total_range; } - - ErrorOr place_anywhere(Region&, RandomizeVirtualAddress, size_t size, size_t alignment = PAGE_SIZE); - ErrorOr place_specifically(Region&, VirtualRange const&); - - void delete_all_regions_assuming_they_are_unmapped(); - - bool remove(Region&); - - Region* find_region_containing(VirtualAddress); - Region* find_region_containing(VirtualRange); - -private: - ErrorOr allocate_range_anywhere(size_t size, size_t alignment = PAGE_SIZE); - ErrorOr allocate_range_specific(VirtualAddress base, size_t size); - ErrorOr allocate_range_randomized(size_t size, size_t alignment = PAGE_SIZE); - - IntrusiveRedBlackTree<&Region::m_tree_node> m_regions; - VirtualRange const m_total_range; -}; - -} diff --git a/Kernel/Memory/RingBuffer.cpp b/Kernel/Memory/RingBuffer.cpp deleted file mode 100644 index e400f937092..00000000000 --- a/Kernel/Memory/RingBuffer.cpp +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Copyright (c) 2021, Sahan Fernando . - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include - -namespace Kernel::Memory { - -ErrorOr> RingBuffer::try_create(StringView region_name, size_t capacity) -{ - auto region_size = TRY(page_round_up(capacity)); - auto region = TRY(MM.allocate_contiguous_kernel_region(region_size, region_name, Region::Access::Read | Region::Access::Write)); - return adopt_nonnull_own_or_enomem(new (nothrow) RingBuffer(move(region), capacity)); -} - -RingBuffer::RingBuffer(NonnullOwnPtr region, size_t capacity) - : m_region(move(region)) - , m_capacity_in_bytes(capacity) -{ -} - -bool RingBuffer::copy_data_in(UserOrKernelBuffer const& buffer, size_t offset, size_t length, PhysicalAddress& start_of_copied_data, size_t& bytes_copied) -{ - size_t start_of_free_area = (m_start_of_used + m_num_used_bytes) % m_capacity_in_bytes; - bytes_copied = min(m_capacity_in_bytes - m_num_used_bytes, min(m_capacity_in_bytes - start_of_free_area, length)); - if (bytes_copied == 0) - return false; - if (auto result = buffer.read(m_region->vaddr().offset(start_of_free_area).as_ptr(), offset, bytes_copied); result.is_error()) - return false; - m_num_used_bytes += bytes_copied; - start_of_copied_data = m_region->physical_page(start_of_free_area / PAGE_SIZE)->paddr().offset(start_of_free_area % PAGE_SIZE); - return true; -} - -ErrorOr RingBuffer::copy_data_out(size_t size, UserOrKernelBuffer& buffer) const -{ - auto start = m_start_of_used % m_capacity_in_bytes; - auto num_bytes = min(min(m_num_used_bytes, size), m_capacity_in_bytes - start); - TRY(buffer.write(m_region->vaddr().offset(start).as_ptr(), num_bytes)); - return num_bytes; -} - -ErrorOr RingBuffer::reserve_space(size_t size) -{ - if (m_capacity_in_bytes < m_num_used_bytes + size) - return ENOSPC; - size_t start_of_free_area = (m_start_of_used + m_num_used_bytes) % m_capacity_in_bytes; - m_num_used_bytes += size; - PhysicalAddress start_of_reserved_space = m_region->physical_page(start_of_free_area / PAGE_SIZE)->paddr().offset(start_of_free_area % PAGE_SIZE); - return start_of_reserved_space; -} - -void RingBuffer::reclaim_space(PhysicalAddress chunk_start, size_t chunk_size) -{ - VERIFY(start_of_used() == chunk_start); - VERIFY(m_num_used_bytes >= chunk_size); - m_num_used_bytes -= chunk_size; - m_start_of_used += chunk_size; -} - -PhysicalAddress RingBuffer::start_of_used() const -{ - size_t start = m_start_of_used % m_capacity_in_bytes; - return m_region->physical_page(start / PAGE_SIZE)->paddr().offset(start % PAGE_SIZE); -} - -} diff --git a/Kernel/Memory/RingBuffer.h b/Kernel/Memory/RingBuffer.h deleted file mode 100644 index e66c8ca2ca2..00000000000 --- a/Kernel/Memory/RingBuffer.h +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright (c) 2021, Sahan Fernando . - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include - -namespace Kernel::Memory { - -class RingBuffer { -public: - static ErrorOr> try_create(StringView region_name, size_t capacity); - - bool has_space() const { return m_num_used_bytes < m_capacity_in_bytes; } - bool copy_data_in(UserOrKernelBuffer const& buffer, size_t offset, size_t length, PhysicalAddress& start_of_copied_data, size_t& bytes_copied); - ErrorOr copy_data_out(size_t size, UserOrKernelBuffer& buffer) const; - ErrorOr reserve_space(size_t size); - void reclaim_space(PhysicalAddress chunk_start, size_t chunk_size); - PhysicalAddress start_of_used() const; - - Spinlock& lock() { return m_lock; } - size_t used_bytes() const { return m_num_used_bytes; } - size_t available_bytes() const { return m_capacity_in_bytes - m_num_used_bytes; } - PhysicalAddress start_of_region() const { return m_region->physical_page(0)->paddr(); } - VirtualAddress vaddr() const { return m_region->vaddr(); } - size_t bytes_till_end() const { return (m_capacity_in_bytes - ((m_start_of_used + m_num_used_bytes) % m_capacity_in_bytes)) % m_capacity_in_bytes; } - -private: - RingBuffer(NonnullOwnPtr region, size_t capacity); - - NonnullOwnPtr m_region; - Spinlock m_lock {}; - size_t m_start_of_used {}; - size_t m_num_used_bytes {}; - size_t m_capacity_in_bytes {}; -}; - -} diff --git a/Kernel/Memory/ScatterGatherList.cpp b/Kernel/Memory/ScatterGatherList.cpp deleted file mode 100644 index f5521d5369a..00000000000 --- a/Kernel/Memory/ScatterGatherList.cpp +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright (c) 2021, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include - -namespace Kernel::Memory { - -ErrorOr> ScatterGatherList::try_create(AsyncBlockDeviceRequest& request, Span> allocated_pages, size_t device_block_size, StringView region_name) -{ - auto vm_object = TRY(AnonymousVMObject::try_create_with_physical_pages(allocated_pages)); - auto size = TRY(page_round_up((request.block_count() * device_block_size))); - auto region = TRY(MM.allocate_kernel_region_with_vmobject(vm_object, size, region_name, Region::Access::Read | Region::Access::Write, Region::Cacheable::Yes)); - - return adopt_lock_ref_if_nonnull(new (nothrow) ScatterGatherList(vm_object, move(region))); -} - -ScatterGatherList::ScatterGatherList(NonnullLockRefPtr vm_object, NonnullOwnPtr dma_region) - : m_vm_object(move(vm_object)) - , m_dma_region(move(dma_region)) -{ -} - -} diff --git a/Kernel/Memory/ScatterGatherList.h b/Kernel/Memory/ScatterGatherList.h deleted file mode 100644 index 11e33970f8c..00000000000 --- a/Kernel/Memory/ScatterGatherList.h +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright (c) 2021, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include -#include - -namespace Kernel::Memory { - -// A Scatter-Gather List type that owns its buffers - -class ScatterGatherList final : public AtomicRefCounted { -public: - static ErrorOr> try_create(AsyncBlockDeviceRequest&, Span> allocated_pages, size_t device_block_size, StringView region_name); - VMObject const& vmobject() const { return m_vm_object; } - VirtualAddress dma_region() const { return m_dma_region->vaddr(); } - size_t scatters_count() const { return m_vm_object->physical_pages().size(); } - -private: - ScatterGatherList(NonnullLockRefPtr, NonnullOwnPtr dma_region); - NonnullLockRefPtr m_vm_object; - NonnullOwnPtr m_dma_region; -}; - -} diff --git a/Kernel/Memory/ScopedAddressSpaceSwitcher.cpp b/Kernel/Memory/ScopedAddressSpaceSwitcher.cpp deleted file mode 100644 index 2bf7465ab5f..00000000000 --- a/Kernel/Memory/ScopedAddressSpaceSwitcher.cpp +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright (c) 2020, Andreas Kling - * Copyright (c) 2023, Timon Kruiper - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include - -namespace Kernel { - -ScopedAddressSpaceSwitcher::ScopedAddressSpaceSwitcher(Process& process) -{ - VERIFY(Thread::current() != nullptr); - m_previous_page_directory = Memory::PageDirectory::find_current(); - Memory::MemoryManager::enter_process_address_space(process); -} - -ScopedAddressSpaceSwitcher::~ScopedAddressSpaceSwitcher() -{ - InterruptDisabler disabler; - Memory::activate_page_directory(*m_previous_page_directory, Thread::current()); -} - -} diff --git a/Kernel/Memory/ScopedAddressSpaceSwitcher.h b/Kernel/Memory/ScopedAddressSpaceSwitcher.h deleted file mode 100644 index a0c7ceb363a..00000000000 --- a/Kernel/Memory/ScopedAddressSpaceSwitcher.h +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright (c) 2020-2021, Andreas Kling - * Copyright (c) 2023, Timon Kruiper - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include - -namespace Kernel { - -class ScopedAddressSpaceSwitcher { -public: - explicit ScopedAddressSpaceSwitcher(Process&); - ~ScopedAddressSpaceSwitcher(); - -private: - LockRefPtr m_previous_page_directory; -}; - -} diff --git a/Kernel/Memory/SharedFramebufferVMObject.cpp b/Kernel/Memory/SharedFramebufferVMObject.cpp deleted file mode 100644 index 5e8b4b91d5a..00000000000 --- a/Kernel/Memory/SharedFramebufferVMObject.cpp +++ /dev/null @@ -1,120 +0,0 @@ -/* - * Copyright (c) 2022, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include - -namespace Kernel::Memory { - -ErrorOr> SharedFramebufferVMObject::try_create_for_physical_range(PhysicalAddress paddr, size_t size) -{ - auto real_framebuffer_vmobject = TRY(AnonymousVMObject::try_create_for_physical_range(paddr, size)); - auto new_physical_pages = TRY(VMObject::try_create_physical_pages(size)); - auto committed_pages = TRY(MM.commit_physical_pages(ceil_div(size, static_cast(PAGE_SIZE)))); - auto vm_object = TRY(adopt_nonnull_lock_ref_or_enomem(new (nothrow) SharedFramebufferVMObject(move(new_physical_pages), move(committed_pages), real_framebuffer_vmobject))); - TRY(vm_object->create_fake_writes_framebuffer_vm_object()); - TRY(vm_object->create_real_writes_framebuffer_vm_object()); - return vm_object; -} - -ErrorOr> SharedFramebufferVMObject::try_create_at_arbitrary_physical_range(size_t size) -{ - auto real_framebuffer_vmobject = TRY(AnonymousVMObject::try_create_with_size(size, AllocationStrategy::AllocateNow)); - auto new_physical_pages = TRY(VMObject::try_create_physical_pages(size)); - auto committed_pages = TRY(MM.commit_physical_pages(ceil_div(size, static_cast(PAGE_SIZE)))); - auto vm_object = TRY(adopt_nonnull_lock_ref_or_enomem(new (nothrow) SharedFramebufferVMObject(move(new_physical_pages), move(committed_pages), real_framebuffer_vmobject))); - TRY(vm_object->create_fake_writes_framebuffer_vm_object()); - TRY(vm_object->create_real_writes_framebuffer_vm_object()); - return vm_object; -} - -ErrorOr> SharedFramebufferVMObject::FakeWritesFramebufferVMObject::try_create(Badge, SharedFramebufferVMObject const& parent_object) -{ - auto new_physical_pages = TRY(VMObject::try_create_physical_pages(0)); - return adopt_nonnull_lock_ref_or_enomem(new (nothrow) FakeWritesFramebufferVMObject(parent_object, move(new_physical_pages))); -} - -ErrorOr> SharedFramebufferVMObject::RealWritesFramebufferVMObject::try_create(Badge, SharedFramebufferVMObject const& parent_object) -{ - auto new_physical_pages = TRY(VMObject::try_create_physical_pages(0)); - return adopt_nonnull_lock_ref_or_enomem(new (nothrow) RealWritesFramebufferVMObject(parent_object, move(new_physical_pages))); -} - -ErrorOr SharedFramebufferVMObject::create_fake_writes_framebuffer_vm_object() -{ - m_fake_writes_framebuffer_vmobject = TRY(FakeWritesFramebufferVMObject::try_create({}, *this)); - return {}; -} - -ErrorOr SharedFramebufferVMObject::create_real_writes_framebuffer_vm_object() -{ - m_real_writes_framebuffer_vmobject = TRY(RealWritesFramebufferVMObject::try_create({}, *this)); - return {}; -} - -Span> SharedFramebufferVMObject::real_framebuffer_physical_pages() -{ - return m_real_framebuffer_vmobject->physical_pages(); -} -ReadonlySpan> SharedFramebufferVMObject::real_framebuffer_physical_pages() const -{ - return m_real_framebuffer_vmobject->physical_pages(); -} - -Span> SharedFramebufferVMObject::fake_sink_framebuffer_physical_pages() -{ - return m_physical_pages.span(); -} - -ReadonlySpan> SharedFramebufferVMObject::fake_sink_framebuffer_physical_pages() const -{ - return m_physical_pages.span(); -} - -void SharedFramebufferVMObject::switch_to_fake_sink_framebuffer_writes(Badge) -{ - SpinlockLocker locker(m_writes_state_lock); - m_writes_are_faked = true; - for_each_region([](Region& region) { - region.remap(); - }); -} -void SharedFramebufferVMObject::switch_to_real_framebuffer_writes(Badge) -{ - SpinlockLocker locker(m_writes_state_lock); - m_writes_are_faked = false; - for_each_region([](Region& region) { - region.remap(); - }); -} - -ReadonlySpan> SharedFramebufferVMObject::physical_pages() const -{ - SpinlockLocker locker(m_writes_state_lock); - if (m_writes_are_faked) - return VMObject::physical_pages(); - return m_real_framebuffer_vmobject->physical_pages(); -} -Span> SharedFramebufferVMObject::physical_pages() -{ - SpinlockLocker locker(m_writes_state_lock); - if (m_writes_are_faked) - return VMObject::physical_pages(); - return m_real_framebuffer_vmobject->physical_pages(); -} - -SharedFramebufferVMObject::SharedFramebufferVMObject(FixedArray>&& new_physical_pages, CommittedPhysicalPageSet committed_pages, AnonymousVMObject& real_framebuffer_vmobject) - : VMObject(move(new_physical_pages)) - , m_real_framebuffer_vmobject(real_framebuffer_vmobject) - , m_committed_pages(move(committed_pages)) -{ - // Allocate all pages right now. We know we can get all because we committed the amount needed - for (size_t i = 0; i < page_count(); ++i) - m_physical_pages[i] = m_committed_pages.take_one(); -} - -} diff --git a/Kernel/Memory/SharedFramebufferVMObject.h b/Kernel/Memory/SharedFramebufferVMObject.h deleted file mode 100644 index 8f8b05b6c84..00000000000 --- a/Kernel/Memory/SharedFramebufferVMObject.h +++ /dev/null @@ -1,94 +0,0 @@ -/* - * Copyright (c) 2022, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include -#include - -namespace Kernel::Memory { - -class SharedFramebufferVMObject final : public VMObject { -public: - class FakeWritesFramebufferVMObject final : public VMObject { - public: - static ErrorOr> try_create(Badge, SharedFramebufferVMObject const& parent_object); - - private: - FakeWritesFramebufferVMObject(SharedFramebufferVMObject const& parent_object, FixedArray>&& new_physical_pages) - : VMObject(move(new_physical_pages)) - , m_parent_object(parent_object) - { - } - virtual StringView class_name() const override { return "FakeWritesFramebufferVMObject"sv; } - virtual ErrorOr> try_clone() override { return Error::from_errno(ENOTIMPL); } - virtual ReadonlySpan> physical_pages() const override { return m_parent_object->fake_sink_framebuffer_physical_pages(); } - virtual Span> physical_pages() override { return m_parent_object->fake_sink_framebuffer_physical_pages(); } - NonnullLockRefPtr m_parent_object; - }; - - class RealWritesFramebufferVMObject final : public VMObject { - public: - static ErrorOr> try_create(Badge, SharedFramebufferVMObject const& parent_object); - - private: - RealWritesFramebufferVMObject(SharedFramebufferVMObject const& parent_object, FixedArray>&& new_physical_pages) - : VMObject(move(new_physical_pages)) - , m_parent_object(parent_object) - { - } - virtual StringView class_name() const override { return "RealWritesFramebufferVMObject"sv; } - virtual ErrorOr> try_clone() override { return Error::from_errno(ENOTIMPL); } - virtual ReadonlySpan> physical_pages() const override { return m_parent_object->real_framebuffer_physical_pages(); } - virtual Span> physical_pages() override { return m_parent_object->real_framebuffer_physical_pages(); } - NonnullLockRefPtr m_parent_object; - }; - - virtual ~SharedFramebufferVMObject() override = default; - - static ErrorOr> try_create_for_physical_range(PhysicalAddress paddr, size_t size); - static ErrorOr> try_create_at_arbitrary_physical_range(size_t size); - virtual ErrorOr> try_clone() override { return Error::from_errno(ENOTIMPL); } - - void switch_to_fake_sink_framebuffer_writes(Badge); - void switch_to_real_framebuffer_writes(Badge); - - virtual ReadonlySpan> physical_pages() const override; - virtual Span> physical_pages() override; - - Span> fake_sink_framebuffer_physical_pages(); - ReadonlySpan> fake_sink_framebuffer_physical_pages() const; - - Span> real_framebuffer_physical_pages(); - ReadonlySpan> real_framebuffer_physical_pages() const; - - FakeWritesFramebufferVMObject const& fake_writes_framebuffer_vmobject() const { return *m_fake_writes_framebuffer_vmobject; } - FakeWritesFramebufferVMObject& fake_writes_framebuffer_vmobject() { return *m_fake_writes_framebuffer_vmobject; } - - RealWritesFramebufferVMObject const& real_writes_framebuffer_vmobject() const { return *m_real_writes_framebuffer_vmobject; } - RealWritesFramebufferVMObject& real_writes_framebuffer_vmobject() { return *m_real_writes_framebuffer_vmobject; } - -private: - SharedFramebufferVMObject(FixedArray>&& new_physical_pages, CommittedPhysicalPageSet, AnonymousVMObject& real_framebuffer_vmobject); - - virtual StringView class_name() const override { return "SharedFramebufferVMObject"sv; } - - ErrorOr create_fake_writes_framebuffer_vm_object(); - ErrorOr create_real_writes_framebuffer_vm_object(); - - NonnullLockRefPtr m_real_framebuffer_vmobject; - LockRefPtr m_fake_writes_framebuffer_vmobject; - LockRefPtr m_real_writes_framebuffer_vmobject; - bool m_writes_are_faked { false }; - mutable RecursiveSpinlock m_writes_state_lock {}; - CommittedPhysicalPageSet m_committed_pages; -}; - -} diff --git a/Kernel/Memory/SharedInodeVMObject.cpp b/Kernel/Memory/SharedInodeVMObject.cpp deleted file mode 100644 index 7433943dec9..00000000000 --- a/Kernel/Memory/SharedInodeVMObject.cpp +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright (c) 2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include - -namespace Kernel::Memory { - -ErrorOr> SharedInodeVMObject::try_create_with_inode(Inode& inode) -{ - if (inode.size() == 0) - return EINVAL; - return try_create_with_inode_and_range(inode, 0, inode.size()); -} - -ErrorOr> SharedInodeVMObject::try_create_with_inode_and_range(Inode& inode, u64 offset, size_t range_size) -{ - // Note: To ensure further allocation of a Region with this VMObject will not complain - // on "smaller" VMObject than the requested Region, we simply take the max size between both values. - auto size = max(inode.size(), (offset + range_size)); - VERIFY(size > 0); - if (auto shared_vmobject = inode.shared_vmobject()) - return shared_vmobject.release_nonnull(); - auto new_physical_pages = TRY(VMObject::try_create_physical_pages(size)); - auto dirty_pages = TRY(Bitmap::create(new_physical_pages.size(), false)); - auto vmobject = TRY(adopt_nonnull_lock_ref_or_enomem(new (nothrow) SharedInodeVMObject(inode, move(new_physical_pages), move(dirty_pages)))); - TRY(vmobject->inode().set_shared_vmobject(*vmobject)); - return vmobject; -} - -ErrorOr> SharedInodeVMObject::try_clone() -{ - auto new_physical_pages = TRY(this->try_clone_physical_pages()); - auto dirty_pages = TRY(Bitmap::create(new_physical_pages.size(), false)); - return adopt_nonnull_lock_ref_or_enomem(new (nothrow) SharedInodeVMObject(*this, move(new_physical_pages), move(dirty_pages))); -} - -SharedInodeVMObject::SharedInodeVMObject(Inode& inode, FixedArray>&& new_physical_pages, Bitmap dirty_pages) - : InodeVMObject(inode, move(new_physical_pages), move(dirty_pages)) -{ -} - -SharedInodeVMObject::SharedInodeVMObject(SharedInodeVMObject const& other, FixedArray>&& new_physical_pages, Bitmap dirty_pages) - : InodeVMObject(other, move(new_physical_pages), move(dirty_pages)) -{ -} - -ErrorOr SharedInodeVMObject::sync(off_t offset_in_pages, size_t pages) -{ - SpinlockLocker locker(m_lock); - - size_t highest_page_to_flush = min(page_count(), offset_in_pages + pages); - - for (size_t page_index = offset_in_pages; page_index < highest_page_to_flush; ++page_index) { - auto& physical_page = m_physical_pages[page_index]; - if (!physical_page) - continue; - - u8 page_buffer[PAGE_SIZE]; - MM.copy_physical_page(*physical_page, page_buffer); - - TRY(m_inode->write_bytes(page_index * PAGE_SIZE, PAGE_SIZE, UserOrKernelBuffer::for_kernel_buffer(page_buffer), nullptr)); - } - - return {}; -} - -} diff --git a/Kernel/Memory/SharedInodeVMObject.h b/Kernel/Memory/SharedInodeVMObject.h deleted file mode 100644 index fb1837dd3ab..00000000000 --- a/Kernel/Memory/SharedInodeVMObject.h +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include - -namespace Kernel::Memory { - -class SharedInodeVMObject final : public InodeVMObject { - AK_MAKE_NONMOVABLE(SharedInodeVMObject); - -public: - static ErrorOr> try_create_with_inode(Inode&); - static ErrorOr> try_create_with_inode_and_range(Inode&, u64 offset, size_t range_size); - virtual ErrorOr> try_clone() override; - - ErrorOr sync(off_t offset_in_pages = 0, size_t pages = -1); - -private: - virtual bool is_shared_inode() const override { return true; } - - explicit SharedInodeVMObject(Inode&, FixedArray>&&, Bitmap dirty_pages); - explicit SharedInodeVMObject(SharedInodeVMObject const&, FixedArray>&&, Bitmap dirty_pages); - - virtual StringView class_name() const override { return "SharedInodeVMObject"sv; } - - SharedInodeVMObject& operator=(SharedInodeVMObject const&) = delete; -}; - -} diff --git a/Kernel/Memory/TypedMapping.h b/Kernel/Memory/TypedMapping.h deleted file mode 100644 index 2a18cd41c26..00000000000 --- a/Kernel/Memory/TypedMapping.h +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright (c) 2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include - -namespace Kernel::Memory { - -template -struct TypedMapping { - T const* ptr() const { return reinterpret_cast(region->vaddr().offset(offset).as_ptr()); } - T* ptr() { return reinterpret_cast(region->vaddr().offset(offset).as_ptr()); } - VirtualAddress base_address() const { return region->vaddr().offset(offset); } - T const* operator->() const { return ptr(); } - T* operator->() { return ptr(); } - T const& operator*() const { return *ptr(); } - T& operator*() { return *ptr(); } - OwnPtr region; - PhysicalAddress paddr; - size_t offset { 0 }; - size_t length { 0 }; -}; - -template -static ErrorOr>> adopt_new_nonnull_own_typed_mapping(PhysicalAddress paddr, size_t length, Region::Access access = Region::Access::Read) -{ - auto mapping_length = TRY(page_round_up(paddr.offset_in_page() + length)); - auto region = TRY(MM.allocate_mmio_kernel_region(paddr.page_base(), mapping_length, {}, access)); - auto table = TRY(adopt_nonnull_own_or_enomem(new (nothrow) Memory::TypedMapping())); - table->region = move(region); - table->offset = paddr.offset_in_page(); - table->paddr = paddr; - table->length = length; - return table; -} - -template -static ErrorOr> map_typed(PhysicalAddress paddr, size_t length, Region::Access access = Region::Access::Read) -{ - TypedMapping table; - auto mapping_length = TRY(page_round_up(paddr.offset_in_page() + length)); - table.region = TRY(MM.allocate_mmio_kernel_region(paddr.page_base(), mapping_length, {}, access)); - table.offset = paddr.offset_in_page(); - table.paddr = paddr; - table.length = length; - return table; -} - -template -static ErrorOr> map_typed(PhysicalAddress paddr) -{ - return map_typed(paddr, sizeof(T)); -} - -template -static ErrorOr> map_typed_writable(PhysicalAddress paddr) -{ - return map_typed(paddr, sizeof(T), Region::Access::Read | Region::Access::Write); -} - -} diff --git a/Kernel/Memory/VMObject.cpp b/Kernel/Memory/VMObject.cpp deleted file mode 100644 index 0ec93823058..00000000000 --- a/Kernel/Memory/VMObject.cpp +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright (c) 2018-2021, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include - -namespace Kernel::Memory { - -static Singleton> s_all_instances; - -SpinlockProtected& VMObject::all_instances() -{ - return s_all_instances; -} - -ErrorOr>> VMObject::try_clone_physical_pages() const -{ - return m_physical_pages.clone(); -} - -ErrorOr>> VMObject::try_create_physical_pages(size_t size) -{ - return FixedArray>::create(ceil_div(size, static_cast(PAGE_SIZE))); -} - -VMObject::VMObject(FixedArray>&& new_physical_pages) - : m_physical_pages(move(new_physical_pages)) -{ - all_instances().with([&](auto& list) { list.append(*this); }); -} - -VMObject::~VMObject() -{ - VERIFY(m_regions.is_empty()); -} - -} diff --git a/Kernel/Memory/VMObject.h b/Kernel/Memory/VMObject.h deleted file mode 100644 index 5e7023db9b1..00000000000 --- a/Kernel/Memory/VMObject.h +++ /dev/null @@ -1,91 +0,0 @@ -/* - * Copyright (c) 2018-2021, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include -#include -#include -#include - -namespace Kernel::Memory { - -class VMObject - : public ListedRefCounted - , public LockWeakable { - friend class MemoryManager; - friend class Region; - -public: - virtual ~VMObject(); - - virtual ErrorOr> try_clone() = 0; - - virtual bool is_anonymous() const { return false; } - virtual bool is_inode() const { return false; } - virtual bool is_shared_inode() const { return false; } - virtual bool is_private_inode() const { return false; } - - size_t page_count() const { return m_physical_pages.size(); } - - virtual ReadonlySpan> physical_pages() const { return m_physical_pages.span(); } - virtual Span> physical_pages() { return m_physical_pages.span(); } - - size_t size() const { return m_physical_pages.size() * PAGE_SIZE; } - - virtual StringView class_name() const = 0; - - ALWAYS_INLINE void add_region(Region& region) - { - SpinlockLocker locker(m_lock); - m_regions.append(region); - } - - ALWAYS_INLINE void remove_region(Region& region) - { - SpinlockLocker locker(m_lock); - m_regions.remove(region); - } - -protected: - static ErrorOr>> try_create_physical_pages(size_t); - ErrorOr>> try_clone_physical_pages() const; - explicit VMObject(FixedArray>&&); - - template - void for_each_region(Callback); - - IntrusiveListNode m_list_node; - FixedArray> m_physical_pages; - - mutable RecursiveSpinlock m_lock {}; - -private: - VMObject& operator=(VMObject const&) = delete; - VMObject& operator=(VMObject&&) = delete; - VMObject(VMObject&&) = delete; - - Region::ListInVMObject m_regions; - -public: - using AllInstancesList = IntrusiveList<&VMObject::m_list_node>; - static SpinlockProtected& all_instances(); -}; - -template -inline void VMObject::for_each_region(Callback callback) -{ - SpinlockLocker lock(m_lock); - for (auto& region : m_regions) { - callback(region); - } -} - -} diff --git a/Kernel/Memory/VirtualRange.cpp b/Kernel/Memory/VirtualRange.cpp deleted file mode 100644 index eea79f785a0..00000000000 --- a/Kernel/Memory/VirtualRange.cpp +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright (c) 2018-2021, Andreas Kling - * Copyright (c) 2021, Leon Albrecht - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include - -namespace Kernel::Memory { - -Vector VirtualRange::carve(VirtualRange const& taken) const -{ - VERIFY((taken.size() % PAGE_SIZE) == 0); - - Vector parts; - if (taken == *this) - return {}; - if (taken.base() > base()) - parts.append({ base(), taken.base().get() - base().get() }); - if (taken.end() < end()) - parts.append({ taken.end(), end().get() - taken.end().get() }); - return parts; -} - -bool VirtualRange::intersects(VirtualRange const& other) const -{ - auto a = *this; - auto b = other; - - if (a.base() > b.base()) - swap(a, b); - - return a.base() < b.end() && b.base() < a.end(); -} - -VirtualRange VirtualRange::intersect(VirtualRange const& other) const -{ - if (*this == other) { - return *this; - } - auto new_base = max(base(), other.base()); - auto new_end = min(end(), other.end()); - VERIFY(new_base < new_end); - return VirtualRange(new_base, (new_end - new_base).get()); -} - -ErrorOr VirtualRange::expand_to_page_boundaries(FlatPtr address, size_t size) -{ - if ((address + size) < address) - return EINVAL; - - auto base = VirtualAddress { address }.page_base(); - auto end = TRY(page_round_up(address + size)); - return VirtualRange { base, end - base.get() }; -} - -} diff --git a/Kernel/Memory/VirtualRange.h b/Kernel/Memory/VirtualRange.h deleted file mode 100644 index ef6a27bdb89..00000000000 --- a/Kernel/Memory/VirtualRange.h +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Copyright (c) 2018-2021, Andreas Kling - * Copyright (c) 2021, Leon Albrecht - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include - -namespace Kernel::Memory { - -class VirtualRange { -public: - VirtualRange() = delete; - VirtualRange(VirtualAddress base, size_t size) - : m_base(base) - , m_size(size) - { - } - - VirtualAddress base() const { return m_base; } - size_t size() const { return m_size; } - bool is_valid() const { return !m_base.is_null(); } - - bool contains(VirtualAddress vaddr) const { return vaddr >= base() && vaddr < end(); } - - VirtualAddress end() const { return m_base.offset(m_size); } - - bool operator==(VirtualRange const& other) const - { - return m_base == other.m_base && m_size == other.m_size; - } - - bool contains(VirtualAddress base, size_t size) const - { - if (base.offset(size) < base) - return false; - return base >= m_base && base.offset(size) <= end(); - } - - bool contains(VirtualRange const& other) const - { - return contains(other.base(), other.size()); - } - - Vector carve(VirtualRange const&) const; - VirtualRange intersect(VirtualRange const&) const; - - bool intersects(VirtualRange const&) const; - - static ErrorOr expand_to_page_boundaries(FlatPtr address, size_t size); - -private: - VirtualAddress m_base; - size_t m_size { 0 }; -}; - -} - -template<> -struct AK::Formatter : Formatter { - ErrorOr format(FormatBuilder& builder, Kernel::Memory::VirtualRange value) - { - return Formatter::format(builder, "{} - {} (size {:p})"sv, value.base().as_ptr(), value.base().offset(value.size() - 1).as_ptr(), value.size()); - } -}; diff --git a/Kernel/Net/ARP.h b/Kernel/Net/ARP.h deleted file mode 100644 index fe984b94df1..00000000000 --- a/Kernel/Net/ARP.h +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include - -namespace Kernel { - -struct ARPOperation { - enum : u16 { - Request = 1, - Response = 2, - }; -}; - -struct ARPHardwareType { - enum : u16 { - Ethernet = 1, - }; -}; - -class [[gnu::packed]] ARPPacket { -public: - u16 hardware_type() const { return m_hardware_type; } - void set_hardware_type(u16 w) { m_hardware_type = w; } - - u16 protocol_type() const { return m_protocol_type; } - void set_protocol_type(u16 w) { m_protocol_type = w; } - - u8 hardware_address_length() const { return m_hardware_address_length; } - void set_hardware_address_length(u8 b) { m_hardware_address_length = b; } - - u8 protocol_address_length() const { return m_protocol_address_length; } - void set_protocol_address_length(u8 b) { m_protocol_address_length = b; } - - u16 operation() const { return m_operation; } - void set_operation(u16 w) { m_operation = w; } - - MACAddress const& sender_hardware_address() const { return m_sender_hardware_address; } - void set_sender_hardware_address(MACAddress const& address) { m_sender_hardware_address = address; } - - IPv4Address const& sender_protocol_address() const { return m_sender_protocol_address; } - void set_sender_protocol_address(IPv4Address const& address) { m_sender_protocol_address = address; } - - MACAddress const& target_hardware_address() const { return m_target_hardware_address; } - void set_target_hardware_address(MACAddress const& address) { m_target_hardware_address = address; } - - IPv4Address const& target_protocol_address() const { return m_target_protocol_address; } - void set_target_protocol_address(IPv4Address const& address) { m_target_protocol_address = address; } - -private: - NetworkOrdered m_hardware_type { ARPHardwareType::Ethernet }; - NetworkOrdered m_protocol_type { EtherType::IPv4 }; - u8 m_hardware_address_length { sizeof(MACAddress) }; - u8 m_protocol_address_length { sizeof(IPv4Address) }; - NetworkOrdered m_operation; - MACAddress m_sender_hardware_address; - IPv4Address m_sender_protocol_address; - MACAddress m_target_hardware_address; - IPv4Address m_target_protocol_address; -}; - -static_assert(sizeof(ARPPacket) == 28); - -} diff --git a/Kernel/Net/EtherType.h b/Kernel/Net/EtherType.h deleted file mode 100644 index a37dd0d2748..00000000000 --- a/Kernel/Net/EtherType.h +++ /dev/null @@ -1,17 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include - -struct EtherType { - enum : u16 { - ARP = 0x0806, - IPv4 = 0x0800, - IPv6 = 0x86DD, - }; -}; diff --git a/Kernel/Net/EthernetFrameHeader.h b/Kernel/Net/EthernetFrameHeader.h deleted file mode 100644 index 8468d0b588d..00000000000 --- a/Kernel/Net/EthernetFrameHeader.h +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include - -#pragma GCC diagnostic ignored "-Warray-bounds" - -class [[gnu::packed]] EthernetFrameHeader { -public: - EthernetFrameHeader() = default; - ~EthernetFrameHeader() = default; - - MACAddress destination() const { return m_destination; } - void set_destination(MACAddress const& address) { m_destination = address; } - - MACAddress source() const { return m_source; } - void set_source(MACAddress const& address) { m_source = address; } - - u16 ether_type() const { return m_ether_type; } - void set_ether_type(u16 ether_type) { m_ether_type = ether_type; } - - void const* payload() const { return &m_payload[0]; } - void* payload() { return &m_payload[0]; } - -private: - MACAddress m_destination; - MACAddress m_source; - NetworkOrdered m_ether_type; - u32 m_payload[0]; -}; - -static_assert(sizeof(EthernetFrameHeader) == 14); diff --git a/Kernel/Net/ICMP.h b/Kernel/Net/ICMP.h deleted file mode 100644 index 8f5ea3623a8..00000000000 --- a/Kernel/Net/ICMP.h +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include - -struct ICMPType { - enum { - EchoReply = 0, - EchoRequest = 8, - }; -}; - -class [[gnu::packed]] ICMPHeader { -public: - ICMPHeader() = default; - ~ICMPHeader() = default; - - u8 type() const { return m_type; } - void set_type(u8 b) { m_type = b; } - - u8 code() const { return m_code; } - void set_code(u8 b) { m_code = b; } - - u16 checksum() const { return m_checksum; } - void set_checksum(u16 w) { m_checksum = w; } - - void const* payload() const { return this + 1; } - void* payload() { return this + 1; } - -private: - u8 m_type { 0 }; - u8 m_code { 0 }; - NetworkOrdered m_checksum { 0 }; - // NOTE: The rest of the header is 4 bytes -}; - -static_assert(AssertSize()); - -struct [[gnu::packed]] ICMPEchoPacket { - ICMPHeader header; - NetworkOrdered identifier; - NetworkOrdered sequence_number; - void* payload() { return this + 1; } - void const* payload() const { return this + 1; } -}; diff --git a/Kernel/Net/IPv4.h b/Kernel/Net/IPv4.h deleted file mode 100644 index 621ee71c481..00000000000 --- a/Kernel/Net/IPv4.h +++ /dev/null @@ -1,124 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include - -namespace Kernel { - -enum class IPv4Protocol : u16 { - ICMP = 1, - TCP = 6, - UDP = 17, -}; - -enum class IPv4PacketFlags : u16 { - DontFragment = 0x4000, - MoreFragments = 0x2000, -}; - -NetworkOrdered internet_checksum(void const*, size_t); - -class [[gnu::packed]] IPv4Packet { -public: - u8 version() const { return (m_version_and_ihl >> 4) & 0xf; } - void set_version(u8 version) { m_version_and_ihl = (m_version_and_ihl & 0x0f) | (version << 4); } - - u8 dscp_and_ecn() const { return m_dscp_and_ecn; } - void set_dscp_and_ecn(u8 dscp_and_ecn) { m_dscp_and_ecn = dscp_and_ecn; } - - u8 internet_header_length() const { return m_version_and_ihl & 0xf; } - void set_internet_header_length(u8 ihl) { m_version_and_ihl = (m_version_and_ihl & 0xf0) | (ihl & 0x0f); } - - u16 length() const { return m_length; } - void set_length(u16 length) { m_length = length; } - - u16 ident() const { return m_ident; } - void set_ident(u16 ident) { m_ident = ident; } - - u8 ttl() const { return m_ttl; } - void set_ttl(u8 ttl) { m_ttl = ttl; } - - u8 protocol() const { return m_protocol; } - void set_protocol(u8 protocol) { m_protocol = protocol; } - - u16 checksum() const { return m_checksum; } - void set_checksum(u16 checksum) { m_checksum = checksum; } - - IPv4Address const& source() const { return m_source; } - void set_source(IPv4Address const& address) { m_source = address; } - - IPv4Address const& destination() const { return m_destination; } - void set_destination(IPv4Address const& address) { m_destination = address; } - - void* payload() { return this + 1; } - void const* payload() const { return this + 1; } - - u16 flags_and_fragment() const { return m_flags_and_fragment; } - u16 fragment_offset() const { return ((u16)m_flags_and_fragment & 0x1fff); } - u16 flags() const { return (((u16)m_flags_and_fragment) & (((u16)IPv4PacketFlags::MoreFragments) | ((u16)IPv4PacketFlags::DontFragment))); } - - void set_has_more_fragments(bool more_fragments) - { - if (more_fragments) - m_flags_and_fragment = (u16)m_flags_and_fragment | ((u16)IPv4PacketFlags::MoreFragments); - else - m_flags_and_fragment = (u16)m_flags_and_fragment & ((u16)IPv4PacketFlags::MoreFragments); - } - void set_fragment_offset(u16 offset) - { - m_flags_and_fragment = flags() | (offset & 0x1fff); - } - - bool is_a_fragment() const - { - // either has More-Fragments set, or has a fragment offset - return (((u16)m_flags_and_fragment) & ((u16)IPv4PacketFlags::MoreFragments)) || ((u16)m_flags_and_fragment & 0x1fff); - } - - u16 payload_size() const { return m_length - sizeof(IPv4Packet); } - - NetworkOrdered compute_checksum() const - { - VERIFY(!m_checksum); - return internet_checksum(this, sizeof(IPv4Packet)); - } - -private: - u8 m_version_and_ihl { 0 }; - u8 m_dscp_and_ecn { 0 }; - NetworkOrdered m_length; - NetworkOrdered m_ident; - NetworkOrdered m_flags_and_fragment; - u8 m_ttl { 0 }; - NetworkOrdered m_protocol; - NetworkOrdered m_checksum; - IPv4Address m_source; - IPv4Address m_destination; -}; - -static_assert(AssertSize()); - -inline NetworkOrdered internet_checksum(void const* ptr, size_t count) -{ - u32 checksum = 0; - auto* w = (u16 const*)ptr; - while (count > 1) { - checksum += AK::convert_between_host_and_network_endian(*w++); - if (checksum & 0x80000000) - checksum = (checksum & 0xffff) | (checksum >> 16); - count -= 2; - } - while (checksum >> 16) - checksum = (checksum & 0xffff) + (checksum >> 16); - return ~checksum & 0xffff; -} - -} diff --git a/Kernel/Net/IPv4Socket.cpp b/Kernel/Net/IPv4Socket.cpp deleted file mode 100644 index 0aa88e25556..00000000000 --- a/Kernel/Net/IPv4Socket.cpp +++ /dev/null @@ -1,904 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace Kernel { - -static Singleton> s_all_sockets; - -using BlockFlags = Thread::OpenFileDescriptionBlocker::BlockFlags; - -MutexProtected& IPv4Socket::all_sockets() -{ - return *s_all_sockets; -} - -ErrorOr> IPv4Socket::try_create_receive_buffer() -{ - return DoubleBuffer::try_create("IPv4Socket: Receive buffer"sv, receive_buffer_size); -} - -ErrorOr> IPv4Socket::create(int type, int protocol) -{ - auto receive_buffer = TRY(IPv4Socket::try_create_receive_buffer()); - - if (type == SOCK_STREAM) - return TRY(TCPSocket::try_create(protocol, move(receive_buffer))); - if (type == SOCK_DGRAM) - return TRY(UDPSocket::try_create(protocol, move(receive_buffer))); - if (type == SOCK_RAW) { - auto raw_socket = adopt_ref_if_nonnull(new (nothrow) IPv4Socket(type, protocol, move(receive_buffer), {})); - if (raw_socket) - return raw_socket.release_nonnull(); - return ENOMEM; - } - return EINVAL; -} - -IPv4Socket::IPv4Socket(int type, int protocol, NonnullOwnPtr receive_buffer, OwnPtr optional_scratch_buffer) - : Socket(AF_INET, type, protocol) - , m_receive_buffer(move(receive_buffer)) - , m_scratch_buffer(move(optional_scratch_buffer)) -{ - dbgln_if(IPV4_SOCKET_DEBUG, "IPv4Socket({}) created with type={}, protocol={}", this, type, protocol); - m_buffer_mode = type == SOCK_STREAM ? BufferMode::Bytes : BufferMode::Packets; - if (m_buffer_mode == BufferMode::Bytes) { - VERIFY(m_scratch_buffer); - } - - all_sockets().with_exclusive([&](auto& table) { - table.append(*this); - }); -} - -IPv4Socket::~IPv4Socket() -{ - all_sockets().with_exclusive([&](auto& table) { - table.remove(*this); - }); -} - -void IPv4Socket::get_local_address(sockaddr* address, socklen_t* address_size) -{ - sockaddr_in local_address = { AF_INET, htons(m_local_port), { m_local_address.to_in_addr_t() }, { 0 } }; - memcpy(address, &local_address, min(static_cast(*address_size), sizeof(sockaddr_in))); - *address_size = sizeof(sockaddr_in); -} - -void IPv4Socket::get_peer_address(sockaddr* address, socklen_t* address_size) -{ - sockaddr_in peer_address = { AF_INET, htons(m_peer_port), { m_peer_address.to_in_addr_t() }, { 0 } }; - memcpy(address, &peer_address, min(static_cast(*address_size), sizeof(sockaddr_in))); - *address_size = sizeof(sockaddr_in); -} - -ErrorOr IPv4Socket::ensure_bound() -{ - dbgln_if(IPV4_SOCKET_DEBUG, "IPv4Socket::ensure_bound() m_bound {}", m_bound.was_set()); - if (m_bound.was_set()) - return {}; - - auto result = protocol_bind(); - if (!result.is_error()) - m_bound.set(); - return result; -} - -ErrorOr IPv4Socket::bind(Credentials const& credentials, Userspace user_address, socklen_t address_size) -{ - if (m_bound.was_set()) - return set_so_error(EINVAL); - - VERIFY(setup_state() == SetupState::Unstarted); - if (address_size != sizeof(sockaddr_in)) - return set_so_error(EINVAL); - - sockaddr_in address {}; - SOCKET_TRY(copy_from_user(&address, user_address, sizeof(sockaddr_in))); - - if (address.sin_family != AF_INET) - return set_so_error(EINVAL); - - auto requested_local_port = ntohs(address.sin_port); - if (!credentials.is_superuser()) { - if (requested_local_port > 0 && requested_local_port < 1024) { - dbgln("UID {} attempted to bind {} to port {}", credentials.uid(), class_name(), requested_local_port); - return set_so_error(EACCES); - } - } - - m_local_address = IPv4Address((u8 const*)&address.sin_addr.s_addr); - m_local_port = requested_local_port; - - dbgln_if(IPV4_SOCKET_DEBUG, "IPv4Socket::bind {}({}) to {}:{}", class_name(), this, m_local_address, m_local_port); - - return ensure_bound(); -} - -ErrorOr IPv4Socket::listen(size_t backlog) -{ - MutexLocker locker(mutex()); - TRY(ensure_bound()); - set_backlog(backlog); - set_role(Role::Listener); - evaluate_block_conditions(); - - dbgln_if(IPV4_SOCKET_DEBUG, "IPv4Socket({}) listening with backlog={}", this, backlog); - - return protocol_listen(); -} - -ErrorOr IPv4Socket::connect(Credentials const&, OpenFileDescription& description, Userspace address, socklen_t address_size) -{ - if (address_size != sizeof(sockaddr_in)) - return set_so_error(EINVAL); - u16 sa_family_copy; - auto* user_address = reinterpret_cast(address.unsafe_userspace_ptr()); - SOCKET_TRY(copy_from_user(&sa_family_copy, &user_address->sa_family, sizeof(u16))); - if (sa_family_copy != AF_INET) - return set_so_error(EINVAL); - if (m_role == Role::Connected) - return set_so_error(EISCONN); - - sockaddr_in safe_address {}; - SOCKET_TRY(copy_from_user(&safe_address, (sockaddr_in const*)user_address, sizeof(sockaddr_in))); - - m_peer_address = IPv4Address((u8 const*)&safe_address.sin_addr.s_addr); - if (m_peer_address == IPv4Address { 0, 0, 0, 0 }) - m_peer_address = IPv4Address { 127, 0, 0, 1 }; - m_peer_port = ntohs(safe_address.sin_port); - - return protocol_connect(description); -} - -bool IPv4Socket::can_read(OpenFileDescription const&, u64) const -{ - if (m_role == Role::Listener) - return can_accept(); - if (protocol_is_disconnected()) - return true; - return m_can_read; -} - -bool IPv4Socket::can_write(OpenFileDescription const&, u64) const -{ - return true; -} - -ErrorOr IPv4Socket::sendto(OpenFileDescription&, UserOrKernelBuffer const& data, size_t data_length, [[maybe_unused]] int flags, Userspace addr, socklen_t addr_length) -{ - MutexLocker locker(mutex()); - - if (addr && addr_length != sizeof(sockaddr_in)) - return set_so_error(EINVAL); - - if (addr) { - sockaddr_in ia {}; - SOCKET_TRY(copy_from_user(&ia, Userspace(addr.ptr()))); - - if (ia.sin_family != AF_INET) { - dmesgln("sendto: Bad address family: {} is not AF_INET", ia.sin_family); - return set_so_error(EAFNOSUPPORT); - } - - if (type() != SOCK_STREAM) { - m_peer_address = IPv4Address((u8 const*)&ia.sin_addr.s_addr); - m_peer_port = ntohs(ia.sin_port); - } - } - - if (!is_connected() && m_peer_address.is_zero()) - return set_so_error(EPIPE); - - auto allow_broadcast = m_broadcast_allowed ? AllowBroadcast::Yes : AllowBroadcast::No; - auto allow_using_gateway = ((flags & MSG_DONTROUTE) || m_routing_disabled) ? AllowUsingGateway::No : AllowUsingGateway::Yes; - auto adapter = bound_interface().with([](auto& bound_device) -> RefPtr { return bound_device; }); - auto routing_decision = route_to(m_peer_address, m_local_address, adapter, allow_broadcast, allow_using_gateway); - if (routing_decision.is_zero()) - return set_so_error(EHOSTUNREACH); - - if (m_local_address.to_u32() == 0) - m_local_address = routing_decision.adapter->ipv4_address(); - - TRY(ensure_bound()); - - dbgln_if(IPV4_SOCKET_DEBUG, "sendto: destination={}:{}", m_peer_address, m_peer_port); - - if (type() == SOCK_RAW) { - auto ipv4_payload_offset = routing_decision.adapter->ipv4_payload_offset(); - data_length = min(data_length, routing_decision.adapter->mtu() - ipv4_payload_offset); - auto packet = routing_decision.adapter->acquire_packet_buffer(ipv4_payload_offset + data_length); - if (!packet) - return set_so_error(ENOMEM); - routing_decision.adapter->fill_in_ipv4_header(*packet, local_address(), routing_decision.next_hop, - m_peer_address, (IPv4Protocol)protocol(), data_length, m_type_of_service, m_ttl); - if (auto result = data.read(packet->buffer->data() + ipv4_payload_offset, data_length); result.is_error()) { - routing_decision.adapter->release_packet_buffer(*packet); - return set_so_error(result.release_error()); - } - routing_decision.adapter->send_packet(packet->bytes()); - routing_decision.adapter->release_packet_buffer(*packet); - return data_length; - } - - auto nsent_or_error = protocol_send(data, data_length); - if (!nsent_or_error.is_error()) - Thread::current()->did_ipv4_socket_write(nsent_or_error.value()); - return nsent_or_error; -} - -ErrorOr IPv4Socket::receive_byte_buffered(OpenFileDescription& description, UserOrKernelBuffer& buffer, size_t buffer_length, int flags, Userspace, Userspace, bool blocking) -{ - MutexLocker locker(mutex()); - - VERIFY(m_receive_buffer); - - if (m_receive_buffer->is_empty()) { - if (protocol_is_disconnected()) - return 0; - if (!blocking) - return set_so_error(EAGAIN); - - locker.unlock(); - auto unblocked_flags = BlockFlags::None; - auto res = Thread::current()->block({}, description, unblocked_flags); - locker.lock(); - - if (!has_flag(unblocked_flags, BlockFlags::Read)) { - if (res.was_interrupted()) - return set_so_error(EINTR); - - // Unblocked due to timeout. - return set_so_error(EAGAIN); - } - } - - ErrorOr nreceived_or_error { 0 }; - if (flags & MSG_PEEK) - nreceived_or_error = m_receive_buffer->peek(buffer, buffer_length); - else - nreceived_or_error = m_receive_buffer->read(buffer, buffer_length); - - if (!nreceived_or_error.is_error() && nreceived_or_error.value() > 0 && !(flags & MSG_PEEK)) - Thread::current()->did_ipv4_socket_read(nreceived_or_error.value()); - - set_can_read(!m_receive_buffer->is_empty()); - return nreceived_or_error; -} - -ErrorOr IPv4Socket::receive_packet_buffered(OpenFileDescription& description, UserOrKernelBuffer& buffer, size_t buffer_length, int flags, Userspace addr, Userspace addr_length, UnixDateTime& packet_timestamp, bool blocking) -{ - MutexLocker locker(mutex()); - ReceivedPacket taken_packet; - ReceivedPacket* packet { nullptr }; - { - if (m_receive_queue.is_empty()) { - // FIXME: Shouldn't this return ENOTCONN instead of EOF? - // But if so, we still need to deliver at least one EOF read to userspace.. right? - if (protocol_is_disconnected()) - return 0; - if (!blocking) - return set_so_error(EAGAIN); - } - - if (!m_receive_queue.is_empty()) { - if (flags & MSG_PEEK) { - packet = &m_receive_queue.first(); - } else { - taken_packet = m_receive_queue.take_first(); - packet = &taken_packet; - } - - set_can_read(!m_receive_queue.is_empty()); - - dbgln_if(IPV4_SOCKET_DEBUG, "IPv4Socket({}): recvfrom without blocking {} bytes, packets in queue: {}", - this, - packet->data->size(), - m_receive_queue.size()); - } - } - - if (!packet) { - if (protocol_is_disconnected()) { - dbgln("IPv4Socket({}) is protocol-disconnected, returning 0 in recvfrom!", this); - return 0; - } - - locker.unlock(); - auto unblocked_flags = BlockFlags::None; - auto res = Thread::current()->block({}, description, unblocked_flags); - locker.lock(); - - if (!has_flag(unblocked_flags, BlockFlags::Read)) { - if (res.was_interrupted()) - return set_so_error(EINTR); - - // Unblocked due to timeout. - return set_so_error(EAGAIN); - } - VERIFY(m_can_read); - VERIFY(!m_receive_queue.is_empty()); - - if (flags & MSG_PEEK) { - packet = &m_receive_queue.first(); - } else { - taken_packet = m_receive_queue.take_first(); - packet = &taken_packet; - } - - set_can_read(!m_receive_queue.is_empty()); - - dbgln_if(IPV4_SOCKET_DEBUG, "IPv4Socket({}): recvfrom with blocking {} bytes, packets in queue: {}", - this, - packet->data->size(), - m_receive_queue.size()); - } - VERIFY(packet->data); - - packet_timestamp = packet->timestamp; - - if (addr) { - dbgln_if(IPV4_SOCKET_DEBUG, "Incoming packet is from: {}:{}", packet->peer_address, packet->peer_port); - - sockaddr_in out_addr {}; - memcpy(&out_addr.sin_addr, &packet->peer_address, sizeof(IPv4Address)); - out_addr.sin_port = htons(packet->peer_port); - out_addr.sin_family = AF_INET; - Userspace dest_addr = addr.ptr(); - SOCKET_TRY(copy_to_user(dest_addr, &out_addr)); - - socklen_t out_length = sizeof(sockaddr_in); - VERIFY(addr_length); - SOCKET_TRY(copy_to_user(addr_length, &out_length)); - } - - if (type() == SOCK_RAW) { - size_t bytes_written = min(packet->data->size(), buffer_length); - SOCKET_TRY(buffer.write(packet->data->data(), bytes_written)); - return bytes_written; - } - - return protocol_receive(packet->data->bytes(), buffer, buffer_length, flags); -} - -ErrorOr IPv4Socket::recvfrom(OpenFileDescription& description, UserOrKernelBuffer& buffer, size_t buffer_length, int flags, Userspace user_addr, Userspace user_addr_length, UnixDateTime& packet_timestamp, bool blocking) -{ - if (user_addr_length) { - socklen_t addr_length; - SOCKET_TRY(copy_from_user(&addr_length, user_addr_length.unsafe_userspace_ptr())); - if (addr_length < sizeof(sockaddr_in)) - return set_so_error(EINVAL); - } - - dbgln_if(IPV4_SOCKET_DEBUG, "recvfrom: type={}, local_port={}", type(), local_port()); - - ErrorOr total_nreceived = 0; - do { - auto offset_buffer = buffer.offset(total_nreceived.value()); - auto offset_buffer_length = buffer_length - total_nreceived.value(); - - ErrorOr nreceived = 0; - if (buffer_mode() == BufferMode::Bytes) - nreceived = receive_byte_buffered(description, offset_buffer, offset_buffer_length, flags, user_addr, user_addr_length, blocking); - else - nreceived = receive_packet_buffered(description, offset_buffer, offset_buffer_length, flags, user_addr, user_addr_length, packet_timestamp, blocking); - - if (nreceived.is_error()) - total_nreceived = move(nreceived); - else - total_nreceived.value() += nreceived.value(); - } while ((flags & MSG_WAITALL) && !total_nreceived.is_error() && total_nreceived.value() < buffer_length); - - if (!total_nreceived.is_error()) - Thread::current()->did_ipv4_socket_read(total_nreceived.value()); - return total_nreceived; -} - -bool IPv4Socket::did_receive(IPv4Address const& source_address, u16 source_port, ReadonlyBytes packet, UnixDateTime const& packet_timestamp) -{ - MutexLocker locker(mutex()); - - if (is_shut_down_for_reading()) - return false; - - auto packet_size = packet.size(); - - if (buffer_mode() == BufferMode::Bytes) { - VERIFY(m_receive_buffer); - - size_t space_in_receive_buffer = m_receive_buffer->space_for_writing(); - if (packet_size > space_in_receive_buffer) { - dbgln("IPv4Socket({}): did_receive refusing packet since buffer is full.", this); - VERIFY(m_can_read); - return false; - } - auto scratch_buffer = UserOrKernelBuffer::for_kernel_buffer(m_scratch_buffer->data()); - auto nreceived_or_error = protocol_receive(packet, scratch_buffer, m_scratch_buffer->size(), 0); - if (nreceived_or_error.is_error()) - return false; - auto nwritten_or_error = m_receive_buffer->write(scratch_buffer, nreceived_or_error.value()); - if (nwritten_or_error.is_error()) - return false; - set_can_read(!m_receive_buffer->is_empty()); - } else { - if (m_receive_queue.size() > 2000) { - dbgln("IPv4Socket({}): did_receive refusing packet since queue is full.", this); - return false; - } - auto data_or_error = KBuffer::try_create_with_bytes("IPv4Socket: Packet buffer"sv, packet); - if (data_or_error.is_error()) { - dbgln("IPv4Socket: did_receive unable to allocate storage for incoming packet."); - return false; - } - auto result = m_receive_queue.try_append({ source_address, source_port, packet_timestamp, data_or_error.release_value() }); - if (result.is_error()) { - dbgln("IPv4Socket: Dropped incoming packet because appending to the receive queue failed."); - return false; - } - set_can_read(true); - } - m_bytes_received += packet_size; - - if constexpr (IPV4_SOCKET_DEBUG) { - if (buffer_mode() == BufferMode::Bytes) - dbgln("IPv4Socket({}): did_receive {} bytes, total_received={}", this, packet_size, m_bytes_received); - else - dbgln("IPv4Socket({}): did_receive {} bytes, total_received={}, packets in queue: {}", - this, - packet_size, - m_bytes_received, - m_receive_queue.size()); - } - - return true; -} - -ErrorOr> IPv4Socket::pseudo_path(OpenFileDescription const&) const -{ - if (m_role == Role::None) - return KString::try_create("socket"sv); - - StringBuilder builder; - TRY(builder.try_append("socket:"sv)); - - TRY(builder.try_appendff("{}:{}", TRY(m_local_address.to_string()), m_local_port)); - if (m_role == Role::Accepted || m_role == Role::Connected) - TRY(builder.try_appendff(" / {}:{}", TRY(m_peer_address.to_string()), m_peer_port)); - - switch (m_role) { - case Role::Listener: - TRY(builder.try_append(" (listening)"sv)); - break; - case Role::Accepted: - TRY(builder.try_append(" (accepted)"sv)); - break; - case Role::Connected: - TRY(builder.try_append(" (connected)"sv)); - break; - case Role::Connecting: - TRY(builder.try_append(" (connecting)"sv)); - break; - default: - VERIFY_NOT_REACHED(); - } - - return KString::try_create(builder.string_view()); -} - -ErrorOr IPv4Socket::setsockopt(int level, int option, Userspace user_value, socklen_t user_value_size) -{ - if (level != IPPROTO_IP) - return Socket::setsockopt(level, option, user_value, user_value_size); - - MutexLocker locker(mutex()); - - switch (option) { - case IP_TTL: { - if (user_value_size < sizeof(int)) - return EINVAL; - int value; - TRY(copy_from_user(&value, static_ptr_cast(user_value))); - if (value < 0 || value > 255) - return EINVAL; - m_ttl = value; - return {}; - } - case IP_TOS: { - if (user_value_size < sizeof(int)) - return EINVAL; - int value; - TRY(copy_from_user(&value, static_ptr_cast(user_value))); - if (value < 0 || value > 255) - return EINVAL; - m_type_of_service = value; - return {}; - } - case IP_MULTICAST_LOOP: { - if (user_value_size != 1) - return EINVAL; - u8 value; - TRY(copy_from_user(&value, static_ptr_cast(user_value))); - if (value != 0 && value != 1) - return EINVAL; - m_multicast_loop = value; - return {}; - } - case IP_ADD_MEMBERSHIP: { - if (user_value_size != sizeof(ip_mreq)) - return EINVAL; - ip_mreq mreq; - TRY(copy_from_user(&mreq, static_ptr_cast(user_value))); - if (mreq.imr_interface.s_addr != INADDR_ANY) - return ENOTSUP; - IPv4Address address { (u8 const*)&mreq.imr_multiaddr.s_addr }; - if (!m_multicast_memberships.contains_slow(address)) - m_multicast_memberships.append(address); - return {}; - } - case IP_DROP_MEMBERSHIP: { - if (user_value_size != sizeof(ip_mreq)) - return EINVAL; - ip_mreq mreq; - TRY(copy_from_user(&mreq, static_ptr_cast(user_value))); - if (mreq.imr_interface.s_addr != INADDR_ANY) - return ENOTSUP; - IPv4Address address { (u8 const*)&mreq.imr_multiaddr.s_addr }; - m_multicast_memberships.remove_first_matching([&address](auto& a) { return a == address; }); - return {}; - } - default: - return ENOPROTOOPT; - } -} - -ErrorOr IPv4Socket::getsockopt(OpenFileDescription& description, int level, int option, Userspace value, Userspace value_size) -{ - if (level != IPPROTO_IP) - return Socket::getsockopt(description, level, option, value, value_size); - - MutexLocker locker(mutex()); - - socklen_t size; - TRY(copy_from_user(&size, value_size.unsafe_userspace_ptr())); - - switch (option) { - case IP_TTL: { - if (size < sizeof(int)) - return EINVAL; - int ttl = m_ttl; - TRY(copy_to_user(static_ptr_cast(value), (int*)&ttl)); - size = sizeof(int); - return copy_to_user(value_size, &size); - } - case IP_TOS: { - if (size < sizeof(int)) - return EINVAL; - int type_of_service = m_type_of_service; - TRY(copy_to_user(static_ptr_cast(value), (int*)&type_of_service)); - size = sizeof(int); - return copy_to_user(value_size, &size); - } - case IP_MULTICAST_LOOP: { - if (size < 1) - return EINVAL; - TRY(copy_to_user(static_ptr_cast(value), (u8 const*)&m_multicast_loop)); - size = 1; - return copy_to_user(value_size, &size); - } - default: - return ENOPROTOOPT; - } -} - -ErrorOr IPv4Socket::ioctl(OpenFileDescription&, unsigned request, Userspace arg) -{ - TRY(Process::current().require_promise(Pledge::inet)); - - MutexLocker locker(mutex()); - - auto ioctl_route = [request, arg]() -> ErrorOr { - auto user_route = static_ptr_cast(arg); - rtentry route; - TRY(copy_from_user(&route, user_route)); - - Userspace user_rt_dev((FlatPtr)route.rt_dev); - auto ifname = TRY(Process::get_syscall_name_string_fixed_buffer(user_rt_dev)); - auto adapter = NetworkingManagement::the().lookup_by_name(ifname.representable_view()); - if (!adapter) - return ENODEV; - - switch (request) { - case SIOCADDRT: { - auto current_process_credentials = Process::current().credentials(); - if (!current_process_credentials->is_superuser()) - return EPERM; - if (route.rt_gateway.sa_family != AF_INET) - return EAFNOSUPPORT; - if (!(route.rt_flags & RTF_UP)) - return EINVAL; // FIXME: Find the correct value to return - - auto destination = IPv4Address(((sockaddr_in&)route.rt_dst).sin_addr.s_addr); - auto gateway = IPv4Address(((sockaddr_in&)route.rt_gateway).sin_addr.s_addr); - auto genmask = IPv4Address(((sockaddr_in&)route.rt_genmask).sin_addr.s_addr); - - return update_routing_table(destination, gateway, genmask, route.rt_flags, adapter, UpdateTable::Set); - } - case SIOCDELRT: - auto current_process_credentials = Process::current().credentials(); - if (!current_process_credentials->is_superuser()) - return EPERM; - if (route.rt_gateway.sa_family != AF_INET) - return EAFNOSUPPORT; - - auto destination = IPv4Address(((sockaddr_in&)route.rt_dst).sin_addr.s_addr); - auto gateway = IPv4Address(((sockaddr_in&)route.rt_gateway).sin_addr.s_addr); - auto genmask = IPv4Address(((sockaddr_in&)route.rt_genmask).sin_addr.s_addr); - - return update_routing_table(destination, gateway, genmask, route.rt_flags, adapter, UpdateTable::Delete); - } - - return EINVAL; - }; - - auto ioctl_arp = [request, arg]() -> ErrorOr { - auto user_req = static_ptr_cast(arg); - arpreq arp_req; - TRY(copy_from_user(&arp_req, user_req)); - - auto current_process_credentials = Process::current().credentials(); - - switch (request) { - case SIOCSARP: - if (!current_process_credentials->is_superuser()) - return EPERM; - if (arp_req.arp_pa.sa_family != AF_INET) - return EAFNOSUPPORT; - update_arp_table(IPv4Address(((sockaddr_in&)arp_req.arp_pa).sin_addr.s_addr), *(MACAddress*)&arp_req.arp_ha.sa_data[0], UpdateTable::Set); - return {}; - - case SIOCDARP: - if (!current_process_credentials->is_superuser()) - return EPERM; - if (arp_req.arp_pa.sa_family != AF_INET) - return EAFNOSUPPORT; - update_arp_table(IPv4Address(((sockaddr_in&)arp_req.arp_pa).sin_addr.s_addr), *(MACAddress*)&arp_req.arp_ha.sa_data[0], UpdateTable::Delete); - return {}; - } - - return EINVAL; - }; - - auto ioctl_interface = [request, arg]() -> ErrorOr { - auto user_ifr = static_ptr_cast(arg); - ifreq ifr; - TRY(copy_from_user(&ifr, user_ifr)); - - if (request == SIOCGIFNAME) { - // NOTE: Network devices are 1-indexed since index 0 denotes an invalid device - if (ifr.ifr_index == 0) - return EINVAL; - - size_t index = 1; - Optional result {}; - - NetworkingManagement::the().for_each([&ifr, &index, &result](auto& adapter) { - if (index == ifr.ifr_index) - result = adapter.name(); - ++index; - }); - - if (result.has_value()) { - auto name = result.release_value(); - auto succ = name.copy_characters_to_buffer(ifr.ifr_name, IFNAMSIZ); - if (!succ) { - return EFAULT; - } - return copy_to_user(user_ifr, &ifr); - } - - return ENODEV; - } - - char namebuf[IFNAMSIZ + 1]; - memcpy(namebuf, ifr.ifr_name, IFNAMSIZ); - namebuf[sizeof(namebuf) - 1] = '\0'; - - if (request == SIOCGIFINDEX) { - StringView name { namebuf, strlen(namebuf) }; - size_t index = 1; - Optional result {}; - - NetworkingManagement::the().for_each([&name, &index, &result](auto& adapter) { - if (adapter.name() == name) - result = index; - ++index; - }); - - if (result.has_value()) { - ifr.ifr_index = result.release_value(); - return copy_to_user(user_ifr, &ifr); - } - - return ENODEV; - } - - auto adapter = NetworkingManagement::the().lookup_by_name({ namebuf, strlen(namebuf) }); - if (!adapter) - return ENODEV; - - auto current_process_credentials = Process::current().credentials(); - - switch (request) { - case SIOCSIFADDR: - if (!current_process_credentials->is_superuser()) - return EPERM; - if (ifr.ifr_addr.sa_family != AF_INET) - return EAFNOSUPPORT; - adapter->set_ipv4_address(IPv4Address(((sockaddr_in&)ifr.ifr_addr).sin_addr.s_addr)); - return {}; - - case SIOCSIFNETMASK: - if (!current_process_credentials->is_superuser()) - return EPERM; - if (ifr.ifr_addr.sa_family != AF_INET) - return EAFNOSUPPORT; - adapter->set_ipv4_netmask(IPv4Address(((sockaddr_in&)ifr.ifr_netmask).sin_addr.s_addr)); - return {}; - - case SIOCGIFADDR: { - auto ip4_addr = adapter->ipv4_address().to_u32(); - auto& socket_address_in = reinterpret_cast(ifr.ifr_addr); - socket_address_in.sin_family = AF_INET; - socket_address_in.sin_addr.s_addr = ip4_addr; - return copy_to_user(user_ifr, &ifr); - } - - case SIOCGIFNETMASK: { - auto ip4_netmask = adapter->ipv4_netmask().to_u32(); - auto& socket_address_in = reinterpret_cast(ifr.ifr_addr); - socket_address_in.sin_family = AF_INET; - // NOTE: NOT ifr_netmask. - socket_address_in.sin_addr.s_addr = ip4_netmask; - - return copy_to_user(user_ifr, &ifr); - } - - case SIOCGIFHWADDR: { - auto mac_address = adapter->mac_address(); - switch (adapter->adapter_type()) { - case NetworkAdapter::Type::Loopback: - ifr.ifr_hwaddr.sa_family = ARPHRD_LOOPBACK; - break; - case NetworkAdapter::Type::Ethernet: - ifr.ifr_hwaddr.sa_family = ARPHRD_ETHER; - break; - default: - VERIFY_NOT_REACHED(); - } - mac_address.copy_to(Bytes { ifr.ifr_hwaddr.sa_data, sizeof(ifr.ifr_hwaddr.sa_data) }); - return copy_to_user(user_ifr, &ifr); - } - - case SIOCGIFBRDADDR: { - // Broadcast address is basically the reverse of the netmask, i.e. - // instead of zeroing out the end, you OR with 1 instead. - auto ip4_netmask = adapter->ipv4_netmask().to_u32(); - auto broadcast_addr = adapter->ipv4_address().to_u32() | ~ip4_netmask; - auto& socket_address_in = reinterpret_cast(ifr.ifr_addr); - socket_address_in.sin_family = AF_INET; - socket_address_in.sin_addr.s_addr = broadcast_addr; - return copy_to_user(user_ifr, &ifr); - } - - case SIOCGIFMTU: { - auto ip4_metric = adapter->mtu(); - - ifr.ifr_addr.sa_family = AF_INET; - ifr.ifr_metric = ip4_metric; - return copy_to_user(user_ifr, &ifr); - } - - case SIOCGIFFLAGS: { - // FIXME: stub! - constexpr short flags = 1; - ifr.ifr_addr.sa_family = AF_INET; - ifr.ifr_flags = flags; - return copy_to_user(user_ifr, &ifr); - } - - case SIOCGIFCONF: { - // FIXME: stub! - return EINVAL; - } - } - - return EINVAL; - }; - - switch (request) { - case SIOCSIFADDR: - case SIOCSIFNETMASK: - case SIOCGIFADDR: - case SIOCGIFHWADDR: - case SIOCGIFNETMASK: - case SIOCGIFBRDADDR: - case SIOCGIFMTU: - case SIOCGIFFLAGS: - case SIOCGIFCONF: - case SIOCGIFNAME: - case SIOCGIFINDEX: - return ioctl_interface(); - - case SIOCADDRT: - case SIOCDELRT: - return ioctl_route(); - - case SIOCSARP: - case SIOCDARP: - return ioctl_arp(); - - case FIONREAD: { - int readable = 0; - if (buffer_mode() == BufferMode::Bytes) { - readable = static_cast(m_receive_buffer->immediately_readable()); - } else { - if (m_receive_queue.size() != 0u) { - readable = static_cast(TRY(protocol_size(m_receive_queue.first().data->bytes()))); - } - } - - return copy_to_user(static_ptr_cast(arg), &readable); - } - } - - return EINVAL; -} - -ErrorOr IPv4Socket::close() -{ - [[maybe_unused]] auto rc = shutdown(SHUT_RDWR); - return {}; -} - -void IPv4Socket::shut_down_for_reading() -{ - Socket::shut_down_for_reading(); - set_can_read(true); -} - -void IPv4Socket::set_can_read(bool value) -{ - m_can_read = value; - if (value) - evaluate_block_conditions(); -} - -void IPv4Socket::drop_receive_buffer() -{ - m_receive_buffer = nullptr; -} - -} diff --git a/Kernel/Net/IPv4Socket.h b/Kernel/Net/IPv4Socket.h deleted file mode 100644 index 9a70bcd5810..00000000000 --- a/Kernel/Net/IPv4Socket.h +++ /dev/null @@ -1,146 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace Kernel { - -class NetworkAdapter; -class TCPPacket; -class TCPSocket; - -class IPv4Socket : public Socket { -public: - static ErrorOr> create(int type, int protocol); - virtual ~IPv4Socket() override; - - virtual ErrorOr close() override; - virtual ErrorOr bind(Credentials const&, Userspace, socklen_t) override; - virtual ErrorOr connect(Credentials const&, OpenFileDescription&, Userspace, socklen_t) override; - virtual ErrorOr listen(size_t) override; - virtual void get_local_address(sockaddr*, socklen_t*) override; - virtual void get_peer_address(sockaddr*, socklen_t*) override; - virtual bool can_read(OpenFileDescription const&, u64) const override; - virtual bool can_write(OpenFileDescription const&, u64) const override; - virtual ErrorOr sendto(OpenFileDescription&, UserOrKernelBuffer const&, size_t, int, Userspace, socklen_t) override; - virtual ErrorOr recvfrom(OpenFileDescription&, UserOrKernelBuffer&, size_t, int flags, Userspace, Userspace, UnixDateTime&, bool blocking) override; - virtual ErrorOr setsockopt(int level, int option, Userspace, socklen_t) override; - virtual ErrorOr getsockopt(OpenFileDescription&, int level, int option, Userspace, Userspace) override; - - virtual ErrorOr ioctl(OpenFileDescription&, unsigned request, Userspace arg) override; - - bool did_receive(IPv4Address const& peer_address, u16 peer_port, ReadonlyBytes, UnixDateTime const&); - - IPv4Address const& local_address() const { return m_local_address; } - u16 local_port() const { return m_local_port; } - void set_local_port(u16 port) { m_local_port = port; } - bool has_specific_local_address() { return m_local_address.to_u32() != 0; } - - IPv4Address const& peer_address() const { return m_peer_address; } - u16 peer_port() const { return m_peer_port; } - void set_peer_port(u16 port) { m_peer_port = port; } - - Vector const& multicast_memberships() const { return m_multicast_memberships; } - - IPv4SocketTuple tuple() const { return IPv4SocketTuple(m_local_address, m_local_port, m_peer_address, m_peer_port); } - - ErrorOr> pseudo_path(OpenFileDescription const& description) const override; - - u8 type_of_service() const { return m_type_of_service; } - u8 ttl() const { return m_ttl; } - - enum class BufferMode { - Packets, - Bytes, - }; - BufferMode buffer_mode() const { return m_buffer_mode; } - -protected: - static constexpr size_t receive_buffer_size = 256 * KiB; - - IPv4Socket(int type, int protocol, NonnullOwnPtr receive_buffer, OwnPtr optional_scratch_buffer); - virtual StringView class_name() const override { return "IPv4Socket"sv; } - - void set_bound() { m_bound.set(); } - ErrorOr ensure_bound(); - - virtual ErrorOr protocol_bind() { return {}; } - virtual ErrorOr protocol_listen() { return {}; } - virtual ErrorOr protocol_receive(ReadonlyBytes /* raw_ipv4_packet */, UserOrKernelBuffer&, size_t, int) { return ENOTIMPL; } - virtual ErrorOr protocol_send(UserOrKernelBuffer const&, size_t) { return ENOTIMPL; } - virtual ErrorOr protocol_connect(OpenFileDescription&) { return {}; } - virtual ErrorOr protocol_size(ReadonlyBytes /* raw_ipv4_packet */) { return ENOTIMPL; } - virtual bool protocol_is_disconnected() const { return false; } - - virtual void shut_down_for_reading() override; - - void set_local_address(IPv4Address address) { m_local_address = address; } - void set_peer_address(IPv4Address address) { m_peer_address = address; } - - static ErrorOr> try_create_receive_buffer(); - void drop_receive_buffer(); - - size_t available_space_in_receive_buffer() const { return m_receive_buffer ? m_receive_buffer->space_for_writing() : 0; } - -private: - virtual bool is_ipv4() const override { return true; } - - ErrorOr receive_byte_buffered(OpenFileDescription&, UserOrKernelBuffer& buffer, size_t buffer_length, int flags, Userspace, Userspace, bool blocking); - ErrorOr receive_packet_buffered(OpenFileDescription&, UserOrKernelBuffer& buffer, size_t buffer_length, int flags, Userspace, Userspace, UnixDateTime&, bool blocking); - - void set_can_read(bool); - - IPv4Address m_local_address; - IPv4Address m_peer_address; - - Vector m_multicast_memberships; - bool m_multicast_loop { true }; - SetOnce m_bound; - - struct ReceivedPacket { - IPv4Address peer_address; - u16 peer_port; - UnixDateTime timestamp; - OwnPtr data; - }; - - SinglyLinkedList m_receive_queue; - - OwnPtr m_receive_buffer; - - u16 m_local_port { 0 }; - u16 m_peer_port { 0 }; - - u32 m_bytes_received { 0 }; - - u8 m_type_of_service { IPTOS_LOWDELAY }; - u8 m_ttl { 64 }; - - bool m_can_read { false }; - - BufferMode m_buffer_mode { BufferMode::Packets }; - - OwnPtr m_scratch_buffer; - - IntrusiveListNode m_list_node; - -public: - using List = IntrusiveList<&IPv4Socket::m_list_node>; - - static MutexProtected& all_sockets(); -}; - -} diff --git a/Kernel/Net/IPv4SocketTuple.h b/Kernel/Net/IPv4SocketTuple.h deleted file mode 100644 index f9d5ea4638b..00000000000 --- a/Kernel/Net/IPv4SocketTuple.h +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include -#include -#include - -namespace Kernel { - -class IPv4SocketTuple { -public: - IPv4SocketTuple(IPv4Address local_address, u16 local_port, IPv4Address peer_address, u16 peer_port) - : m_local_address(local_address) - , m_local_port(local_port) - , m_peer_address(peer_address) - , m_peer_port(peer_port) {}; - - IPv4Address local_address() const { return m_local_address; } - u16 local_port() const { return m_local_port; } - IPv4Address peer_address() const { return m_peer_address; } - u16 peer_port() const { return m_peer_port; } - - bool operator==(IPv4SocketTuple const& other) const - { - return other.local_address() == m_local_address && other.local_port() == m_local_port && other.peer_address() == m_peer_address && other.peer_port() == m_peer_port; - } - - ErrorOr> to_string() const - { - return KString::formatted( - "{}:{} -> {}:{}", - m_local_address, - m_local_port, - m_peer_address, - m_peer_port); - } - -private: - IPv4Address m_local_address; - u16 m_local_port { 0 }; - IPv4Address m_peer_address; - u16 m_peer_port { 0 }; -}; - -} - -namespace AK { - -template<> -struct Traits : public DefaultTraits { - static unsigned hash(Kernel::IPv4SocketTuple const& tuple) - { - auto h1 = pair_int_hash(tuple.local_address().to_u32(), tuple.local_port()); - auto h2 = pair_int_hash(tuple.peer_address().to_u32(), tuple.peer_port()); - return pair_int_hash(h1, h2); - } -}; - -} diff --git a/Kernel/Net/Intel/E1000ENetworkAdapter.cpp b/Kernel/Net/Intel/E1000ENetworkAdapter.cpp deleted file mode 100644 index 0d7084882fd..00000000000 --- a/Kernel/Net/Intel/E1000ENetworkAdapter.cpp +++ /dev/null @@ -1,271 +0,0 @@ -/* - * Copyright (c) 2018-2021, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include -#include - -namespace Kernel { - -#define REG_EECD 0x0010 -#define REG_EEPROM 0x0014 - -// EECD Register - -#define EECD_PRES 0x100 - -static bool is_valid_device_id(u16 device_id) -{ - // Note: All ids listed here are valid, but only the ones that are verified working are set to 'true' - switch (device_id) { - case 0x10D3: // 82574L - return true; - case 0x1000: // 82542 - case 0x0438: // DH89XXCC_SGMII - case 0x043A: // DH89XXCC_SERDES - case 0x043C: // DH89XXCC_BACKPLANE - case 0x0440: // DH89XXCC_SFP - case 0x1001: // 82543GC_FIBER - case 0x1004: // 82543GC_COPPER - case 0x1008: // 82544EI_COPPER - case 0x1009: // 82544EI_FIBER - case 0x100C: // 82544GC_COPPER - case 0x100D: // 82544GC_LOM - case 0x100E: // 82540EM - case 0x100F: // 82545EM_COPPER - case 0x1010: // 82546EB_COPPER - case 0x1011: // 82545EM_FIBER - case 0x1012: // 82546EB_FIBER - case 0x1013: // 82541EI - case 0x1014: // 82541ER_LOM - case 0x1015: // 82540EM_LOM - case 0x1016: // 82540EP_LOM - case 0x1017: // 82540EP - case 0x1018: // 82541EI_MOBILE - case 0x1019: // 82547EI - case 0x101A: // 82547EI_MOBILE - case 0x101D: // 82546EB_QUAD_COPPER - case 0x101E: // 82540EP_LP - case 0x1026: // 82545GM_COPPER - case 0x1027: // 82545GM_FIBER - case 0x1028: // 82545GM_SERDES - case 0x1049: // ICH8_IGP_M_AMT - case 0x104A: // ICH8_IGP_AMT - case 0x104B: // ICH8_IGP_C - case 0x104C: // ICH8_IFE - case 0x104D: // ICH8_IGP_M - case 0x105E: // 82571EB_COPPER - case 0x105F: // 82571EB_FIBER - case 0x1060: // 82571EB_SERDES - case 0x1075: // 82547GI - case 0x1076: // 82541GI - case 0x1077: // 82541GI_MOBILE - case 0x1078: // 82541ER - case 0x1079: // 82546GB_COPPER - case 0x107A: // 82546GB_FIBER - case 0x107B: // 82546GB_SERDES - case 0x107C: // 82541GI_LF - case 0x107D: // 82572EI_COPPER - case 0x107E: // 82572EI_FIBER - case 0x107F: // 82572EI_SERDES - case 0x108A: // 82546GB_PCIE - case 0x108B: // 82573E - case 0x108C: // 82573E_IAMT - case 0x1096: // 80003ES2LAN_COPPER_DPT - case 0x1098: // 80003ES2LAN_SERDES_DPT - case 0x1099: // 82546GB_QUAD_COPPER - case 0x109A: // 82573L - case 0x10A4: // 82571EB_QUAD_COPPER - case 0x10A5: // 82571EB_QUAD_FIBER - case 0x10A7: // 82575EB_COPPER - case 0x10A9: // 82575EB_FIBER_SERDES - case 0x10B5: // 82546GB_QUAD_COPPER_KSP3 - case 0x10B9: // 82572EI - case 0x10BA: // 80003ES2LAN_COPPER_SPT - case 0x10BB: // 80003ES2LAN_SERDES_SPT - case 0x10BC: // 82571EB_QUAD_COPPER_LP - case 0x10BD: // ICH9_IGP_AMT - case 0x10BF: // ICH9_IGP_M - case 0x10C0: // ICH9_IFE - case 0x10C2: // ICH9_IFE_G - case 0x10C3: // ICH9_IFE_GT - case 0x10C4: // ICH8_IFE_GT - case 0x10C5: // ICH8_IFE_G - case 0x10C9: // 82576 - case 0x10CA: // 82576_VF - case 0x10CB: // ICH9_IGP_M_V - case 0x10CC: // ICH10_R_BM_LM - case 0x10CD: // ICH10_R_BM_LF - case 0x10CE: // ICH10_R_BM_V - case 0x10D5: // 82571PT_QUAD_COPPER - case 0x10D6: // 82575GB_QUAD_COPPER - case 0x10D9: // 82571EB_SERDES_DUAL - case 0x10DA: // 82571EB_SERDES_QUAD - case 0x10DE: // ICH10_D_BM_LM - case 0x10DF: // ICH10_D_BM_LF - case 0x10E5: // ICH9_BM - case 0x10E6: // 82576_FIBER - case 0x10E7: // 82576_SERDES - case 0x10E8: // 82576_QUAD_COPPER - case 0x10EA: // PCH_M_HV_LM - case 0x10EB: // PCH_M_HV_LC - case 0x10EF: // PCH_D_HV_DM - case 0x10F0: // PCH_D_HV_DC - case 0x10F5: // ICH9_IGP_M_AMT - case 0x10F6: // 82574LA - case 0x1501: // ICH8_82567V_3 - case 0x1502: // PCH2_LV_LM - case 0x1503: // PCH2_LV_V - case 0x150A: // 82576_NS - case 0x150C: // 82583V - case 0x150D: // 82576_SERDES_QUAD - case 0x150E: // 82580_COPPER - case 0x150F: // 82580_FIBER - case 0x1510: // 82580_SERDES - case 0x1511: // 82580_SGMII - case 0x1516: // 82580_COPPER_DUAL - case 0x1518: // 82576_NS_SERDES - case 0x1520: // I350_VF - case 0x1521: // I350_COPPER - case 0x1522: // I350_FIBER - case 0x1523: // I350_SERDES - case 0x1524: // I350_SGMII - case 0x1525: // ICH10_D_BM_V - case 0x1526: // 82576_QUAD_COPPER_ET2 - case 0x1527: // 82580_QUAD_FIBER - case 0x152D: // 82576_VF_HV - case 0x152F: // I350_VF_HV - case 0x1533: // I210_COPPER - case 0x1534: // I210_COPPER_OEM1 - case 0x1535: // I210_COPPER_IT - case 0x1536: // I210_FIBER - case 0x1537: // I210_SERDES - case 0x1538: // I210_SGMII - case 0x1539: // I211_COPPER - case 0x153A: // PCH_LPT_I217_LM - case 0x153B: // PCH_LPT_I217_V - case 0x1546: // I350_DA4 - case 0x1559: // PCH_LPTLP_I218_V - case 0x155A: // PCH_LPTLP_I218_LM - case 0x156F: // PCH_SPT_I219_LM - case 0x1570: // PCH_SPT_I219_V - case 0x157B: // I210_COPPER_FLASHLESS - case 0x157C: // I210_SERDES_FLASHLESS - case 0x15A0: // PCH_I218_LM2 - case 0x15A1: // PCH_I218_V2 - case 0x15A2: // PCH_I218_LM3 - case 0x15A3: // PCH_I218_V3 - case 0x15B7: // PCH_SPT_I219_LM2 - case 0x15B8: // PCH_SPT_I219_V2 - case 0x15B9: // PCH_LBG_I219_LM3 - case 0x15BB: // PCH_CNP_I219_LM7 - case 0x15BC: // PCH_CNP_I219_V7 - case 0x15BD: // PCH_CNP_I219_LM6 - case 0x15BE: // PCH_CNP_I219_V6 - case 0x15D6: // PCH_SPT_I219_V5 - case 0x15D7: // PCH_SPT_I219_LM4 - case 0x15D8: // PCH_SPT_I219_V4 - case 0x15DF: // PCH_ICP_I219_LM8 - case 0x15E0: // PCH_ICP_I219_V8 - case 0x15E1: // PCH_ICP_I219_LM9 - case 0x15E2: // PCH_ICP_I219_V9 - case 0x15E3: // PCH_SPT_I219_LM5 - case 0x1F40: // I354_BACKPLANE_1GBPS - case 0x1F41: // I354_SGMII - case 0x1F45: // I354_BACKPLANE_2_5GBPS - case 0x294C: // ICH9_IGP_C - return false; - default: - return false; - } -} - -UNMAP_AFTER_INIT ErrorOr E1000ENetworkAdapter::probe(PCI::DeviceIdentifier const& pci_device_identifier) -{ - if (pci_device_identifier.hardware_id().vendor_id != PCI::VendorID::Intel) - return false; - return is_valid_device_id(pci_device_identifier.hardware_id().device_id); -} - -UNMAP_AFTER_INIT ErrorOr> E1000ENetworkAdapter::create(PCI::DeviceIdentifier const& pci_device_identifier) -{ - u8 irq = pci_device_identifier.interrupt_line().value(); - auto interface_name = TRY(NetworkingManagement::generate_interface_name_from_pci_address(pci_device_identifier)); - auto registers_io_window = TRY(IOWindow::create_for_pci_device_bar(pci_device_identifier, PCI::HeaderType0BaseRegister::BAR0)); - - auto rx_buffer_region = TRY(MM.allocate_contiguous_kernel_region(rx_buffer_size * number_of_rx_descriptors, "E1000 RX buffers"sv, Memory::Region::Access::ReadWrite)); - auto tx_buffer_region = MM.allocate_contiguous_kernel_region(tx_buffer_size * number_of_tx_descriptors, "E1000 TX buffers"sv, Memory::Region::Access::ReadWrite).release_value(); - auto rx_descriptors_region = TRY(MM.allocate_contiguous_kernel_region(TRY(Memory::page_round_up(sizeof(e1000_rx_desc) * number_of_rx_descriptors)), "E1000 RX Descriptors"sv, Memory::Region::Access::ReadWrite)); - auto tx_descriptors_region = TRY(MM.allocate_contiguous_kernel_region(TRY(Memory::page_round_up(sizeof(e1000_tx_desc) * number_of_tx_descriptors)), "E1000 TX Descriptors"sv, Memory::Region::Access::ReadWrite)); - - return TRY(adopt_nonnull_ref_or_enomem(new (nothrow) E1000ENetworkAdapter(interface_name.representable_view(), - pci_device_identifier, - irq, move(registers_io_window), - move(rx_buffer_region), - move(tx_buffer_region), - move(rx_descriptors_region), - move(tx_descriptors_region)))); -} - -UNMAP_AFTER_INIT ErrorOr E1000ENetworkAdapter::initialize(Badge) -{ - dmesgln("E1000e: Found @ {}", device_identifier().address()); - enable_bus_mastering(device_identifier()); - - dmesgln("E1000e: IO base: {}", m_registers_io_window); - dmesgln("E1000e: Interrupt line: {}", interrupt_number()); - detect_eeprom(); - dmesgln("E1000e: Has EEPROM? {}", m_has_eeprom.was_set()); - read_mac_address(); - auto const& mac = mac_address(); - dmesgln("E1000e: MAC address: {}", mac.to_string()); - - initialize_rx_descriptors(); - initialize_tx_descriptors(); - - setup_link(); - setup_interrupts(); - return {}; -} - -UNMAP_AFTER_INIT E1000ENetworkAdapter::E1000ENetworkAdapter(StringView interface_name, - PCI::DeviceIdentifier const& device_identifier, u8 irq, - NonnullOwnPtr registers_io_window, NonnullOwnPtr rx_buffer_region, - NonnullOwnPtr tx_buffer_region, NonnullOwnPtr rx_descriptors_region, - NonnullOwnPtr tx_descriptors_region) - : E1000NetworkAdapter(interface_name, device_identifier, irq, move(registers_io_window), - move(rx_buffer_region), - move(tx_buffer_region), - move(rx_descriptors_region), - move(tx_descriptors_region)) -{ -} - -UNMAP_AFTER_INIT E1000ENetworkAdapter::~E1000ENetworkAdapter() = default; - -UNMAP_AFTER_INIT void E1000ENetworkAdapter::detect_eeprom() -{ - // Section 13.4.3 of https://www.intel.com/content/dam/doc/manual/pci-pci-x-family-gbe-controllers-software-dev-manual.pdf - if (in32(REG_EECD) & EECD_PRES) - m_has_eeprom.set(); -} - -UNMAP_AFTER_INIT u32 E1000ENetworkAdapter::read_eeprom(u8 address) -{ - VERIFY(m_has_eeprom.was_set()); - u16 data = 0; - u32 tmp = 0; - out32(REG_EEPROM, ((u32)address << 2) | 1); - while (!((tmp = in32(REG_EEPROM)) & (1 << 1))) - Processor::wait_check(); - data = (tmp >> 16) & 0xffff; - return data; -} - -} diff --git a/Kernel/Net/Intel/E1000ENetworkAdapter.h b/Kernel/Net/Intel/E1000ENetworkAdapter.h deleted file mode 100644 index e6598cf6911..00000000000 --- a/Kernel/Net/Intel/E1000ENetworkAdapter.h +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright (c) 2021, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include -#include -#include -#include - -namespace Kernel { - -class E1000ENetworkAdapter final - : public E1000NetworkAdapter { -public: - static ErrorOr probe(PCI::DeviceIdentifier const&); - static ErrorOr> create(PCI::DeviceIdentifier const&); - virtual ErrorOr initialize(Badge) override; - - virtual ~E1000ENetworkAdapter() override; - - virtual StringView purpose() const override { return class_name(); } - -private: - E1000ENetworkAdapter(StringView interface_name, PCI::DeviceIdentifier const&, u8 irq, - NonnullOwnPtr registers_io_window, NonnullOwnPtr rx_buffer_region, - NonnullOwnPtr tx_buffer_region, NonnullOwnPtr rx_descriptors_region, - NonnullOwnPtr tx_descriptors_region); - - virtual StringView class_name() const override { return "E1000ENetworkAdapter"sv; } - - virtual void detect_eeprom() override; - virtual u32 read_eeprom(u8 address) override; -}; -} diff --git a/Kernel/Net/Intel/E1000NetworkAdapter.cpp b/Kernel/Net/Intel/E1000NetworkAdapter.cpp deleted file mode 100644 index 04145b27f03..00000000000 --- a/Kernel/Net/Intel/E1000NetworkAdapter.cpp +++ /dev/null @@ -1,475 +0,0 @@ -/* - * Copyright (c) 2018-2021, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include -#include -#include - -namespace Kernel { - -#define REG_CTRL 0x0000 -#define REG_STATUS 0x0008 -#define REG_EEPROM 0x0014 -#define REG_CTRL_EXT 0x0018 -#define REG_INTERRUPT_CAUSE_READ 0x00C0 -#define REG_INTERRUPT_RATE 0x00C4 -#define REG_INTERRUPT_MASK_SET 0x00D0 -#define REG_INTERRUPT_MASK_CLEAR 0x00D8 -#define REG_RCTRL 0x0100 -#define REG_RXDESCLO 0x2800 -#define REG_RXDESCHI 0x2804 -#define REG_RXDESCLEN 0x2808 -#define REG_RXDESCHEAD 0x2810 -#define REG_RXDESCTAIL 0x2818 -#define REG_TCTRL 0x0400 -#define REG_TXDESCLO 0x3800 -#define REG_TXDESCHI 0x3804 -#define REG_TXDESCLEN 0x3808 -#define REG_TXDESCHEAD 0x3810 -#define REG_TXDESCTAIL 0x3818 -#define REG_RDTR 0x2820 // RX Delay Timer Register -#define REG_RXDCTL 0x3828 // RX Descriptor Control -#define REG_RADV 0x282C // RX Int. Absolute Delay Timer -#define REG_RSRPD 0x2C00 // RX Small Packet Detect Interrupt -#define REG_TIPG 0x0410 // Transmit Inter Packet Gap -#define ECTRL_SLU 0x40 // set link up -#define RCTL_EN (1 << 1) // Receiver Enable -#define RCTL_SBP (1 << 2) // Store Bad Packets -#define RCTL_UPE (1 << 3) // Unicast Promiscuous Enabled -#define RCTL_MPE (1 << 4) // Multicast Promiscuous Enabled -#define RCTL_LPE (1 << 5) // Long Packet Reception Enable -#define RCTL_LBM_NONE (0 << 6) // No Loopback -#define RCTL_LBM_PHY (3 << 6) // PHY or external SerDesc loopback -#define RTCL_RDMTS_HALF (0 << 8) // Free Buffer Threshold is 1/2 of RDLEN -#define RTCL_RDMTS_QUARTER (1 << 8) // Free Buffer Threshold is 1/4 of RDLEN -#define RTCL_RDMTS_EIGHTH (2 << 8) // Free Buffer Threshold is 1/8 of RDLEN -#define RCTL_MO_36 (0 << 12) // Multicast Offset - bits 47:36 -#define RCTL_MO_35 (1 << 12) // Multicast Offset - bits 46:35 -#define RCTL_MO_34 (2 << 12) // Multicast Offset - bits 45:34 -#define RCTL_MO_32 (3 << 12) // Multicast Offset - bits 43:32 -#define RCTL_BAM (1 << 15) // Broadcast Accept Mode -#define RCTL_VFE (1 << 18) // VLAN Filter Enable -#define RCTL_CFIEN (1 << 19) // Canonical Form Indicator Enable -#define RCTL_CFI (1 << 20) // Canonical Form Indicator Bit Value -#define RCTL_DPF (1 << 22) // Discard Pause Frames -#define RCTL_PMCF (1 << 23) // Pass MAC Control Frames -#define RCTL_SECRC (1 << 26) // Strip Ethernet CRC - -// Buffer Sizes -#define RCTL_BSIZE_256 (3 << 16) -#define RCTL_BSIZE_512 (2 << 16) -#define RCTL_BSIZE_1024 (1 << 16) -#define RCTL_BSIZE_2048 (0 << 16) -#define RCTL_BSIZE_4096 ((3 << 16) | (1 << 25)) -#define RCTL_BSIZE_8192 ((2 << 16) | (1 << 25)) -#define RCTL_BSIZE_16384 ((1 << 16) | (1 << 25)) - -// Transmit Command - -#define CMD_EOP (1 << 0) // End of Packet -#define CMD_IFCS (1 << 1) // Insert FCS -#define CMD_IC (1 << 2) // Insert Checksum -#define CMD_RS (1 << 3) // Report Status -#define CMD_RPS (1 << 4) // Report Packet Sent -#define CMD_VLE (1 << 6) // VLAN Packet Enable -#define CMD_IDE (1 << 7) // Interrupt Delay Enable - -// TCTL Register - -#define TCTL_EN (1 << 1) // Transmit Enable -#define TCTL_PSP (1 << 3) // Pad Short Packets -#define TCTL_CT_SHIFT 4 // Collision Threshold -#define TCTL_COLD_SHIFT 12 // Collision Distance -#define TCTL_SWXOFF (1 << 22) // Software XOFF Transmission -#define TCTL_RTLC (1 << 24) // Re-transmit on Late Collision - -#define TSTA_DD (1 << 0) // Descriptor Done -#define TSTA_EC (1 << 1) // Excess Collisions -#define TSTA_LC (1 << 2) // Late Collision -#define LSTA_TU (1 << 3) // Transmit Underrun - -// STATUS Register - -#define STATUS_FD 0x01 -#define STATUS_LU 0x02 -#define STATUS_TXOFF 0x08 -#define STATUS_SPEED 0xC0 -#define STATUS_SPEED_10MB 0x00 -#define STATUS_SPEED_100MB 0x40 -#define STATUS_SPEED_1000MB1 0x80 -#define STATUS_SPEED_1000MB2 0xC0 - -// Interrupt Masks - -#define INTERRUPT_TXDW (1 << 0) -#define INTERRUPT_TXQE (1 << 1) -#define INTERRUPT_LSC (1 << 2) -#define INTERRUPT_RXSEQ (1 << 3) -#define INTERRUPT_RXDMT0 (1 << 4) -#define INTERRUPT_RXO (1 << 6) -#define INTERRUPT_RXT0 (1 << 7) -#define INTERRUPT_MDAC (1 << 9) -#define INTERRUPT_RXCFG (1 << 10) -#define INTERRUPT_PHYINT (1 << 12) -#define INTERRUPT_TXD_LOW (1 << 15) -#define INTERRUPT_SRPD (1 << 16) - -// https://www.intel.com/content/dam/doc/manual/pci-pci-x-family-gbe-controllers-software-dev-manual.pdf Section 5.2 -UNMAP_AFTER_INIT static bool is_valid_device_id(u16 device_id) -{ - // FIXME: It would be nice to distinguish which particular device it is. - // Especially since it's needed to determine which registers we can access. - // The reason I haven't done it now is because there's some IDs with multiple devices - // and some devices with multiple IDs. - switch (device_id) { - case 0x1019: // 82547EI-A0, 82547EI-A1, 82547EI-B0, 82547GI-B0 - case 0x101A: // 82547EI-B0 - case 0x1010: // 82546EB-A1 - case 0x1012: // 82546EB-A1 - case 0x101D: // 82546EB-A1 - case 0x1079: // 82546GB-B0 - case 0x107A: // 82546GB-B0 - case 0x107B: // 82546GB-B0 - case 0x100F: // 82545EM-A - case 0x1011: // 82545EM-A - case 0x1026: // 82545GM-B - case 0x1027: // 82545GM-B - case 0x1028: // 82545GM-B - case 0x1107: // 82544EI-A4 - case 0x1112: // 82544GC-A4 - case 0x1013: // 82541EI-A0, 82541EI-B0 - case 0x1018: // 82541EI-B0 - case 0x1076: // 82541GI-B1, 82541PI-C0 - case 0x1077: // 82541GI-B1 - case 0x1078: // 82541ER-C0 - case 0x1017: // 82540EP-A - case 0x1016: // 82540EP-A - case 0x100E: // 82540EM-A - case 0x1015: // 82540EM-A - return true; - default: - return false; - } -} - -UNMAP_AFTER_INIT ErrorOr E1000NetworkAdapter::probe(PCI::DeviceIdentifier const& pci_device_identifier) -{ - if (pci_device_identifier.hardware_id().vendor_id != PCI::VendorID::Intel) - return false; - return is_valid_device_id(pci_device_identifier.hardware_id().device_id); -} - -UNMAP_AFTER_INIT ErrorOr> E1000NetworkAdapter::create(PCI::DeviceIdentifier const& pci_device_identifier) -{ - u8 irq = pci_device_identifier.interrupt_line().value(); - auto interface_name = TRY(NetworkingManagement::generate_interface_name_from_pci_address(pci_device_identifier)); - auto registers_io_window = TRY(IOWindow::create_for_pci_device_bar(pci_device_identifier, PCI::HeaderType0BaseRegister::BAR0)); - - auto rx_buffer_region = TRY(MM.allocate_contiguous_kernel_region(rx_buffer_size * number_of_rx_descriptors, "E1000 RX buffers"sv, Memory::Region::Access::ReadWrite)); - auto tx_buffer_region = MM.allocate_contiguous_kernel_region(tx_buffer_size * number_of_tx_descriptors, "E1000 TX buffers"sv, Memory::Region::Access::ReadWrite).release_value(); - auto rx_descriptors_region = TRY(MM.allocate_contiguous_kernel_region(TRY(Memory::page_round_up(sizeof(e1000_rx_desc) * number_of_rx_descriptors)), "E1000 RX Descriptors"sv, Memory::Region::Access::ReadWrite)); - auto tx_descriptors_region = TRY(MM.allocate_contiguous_kernel_region(TRY(Memory::page_round_up(sizeof(e1000_tx_desc) * number_of_tx_descriptors)), "E1000 TX Descriptors"sv, Memory::Region::Access::ReadWrite)); - - return TRY(adopt_nonnull_ref_or_enomem(new (nothrow) E1000NetworkAdapter(interface_name.representable_view(), - pci_device_identifier, - irq, move(registers_io_window), - move(rx_buffer_region), - move(tx_buffer_region), - move(rx_descriptors_region), - move(tx_descriptors_region)))); -} - -UNMAP_AFTER_INIT ErrorOr E1000NetworkAdapter::initialize(Badge) -{ - dmesgln_pci(*this, "Found @ {}", device_identifier().address()); - - enable_bus_mastering(device_identifier()); - - dmesgln_pci(*this, "IO base: {}", m_registers_io_window); - dmesgln_pci(*this, "Interrupt line: {}", interrupt_number()); - detect_eeprom(); - dmesgln_pci(*this, "Has EEPROM? {}", m_has_eeprom.was_set()); - read_mac_address(); - auto const& mac = mac_address(); - dmesgln_pci(*this, "MAC address: {}", mac.to_string()); - - initialize_rx_descriptors(); - initialize_tx_descriptors(); - - setup_link(); - setup_interrupts(); - - m_link_up = ((in32(REG_STATUS) & STATUS_LU) != 0); - - return {}; -} - -UNMAP_AFTER_INIT void E1000NetworkAdapter::setup_link() -{ - u32 flags = in32(REG_CTRL); - out32(REG_CTRL, flags | ECTRL_SLU); -} - -UNMAP_AFTER_INIT void E1000NetworkAdapter::setup_interrupts() -{ - out32(REG_INTERRUPT_RATE, 6000); // Interrupt rate of 1.536 milliseconds - out32(REG_INTERRUPT_MASK_SET, INTERRUPT_LSC | INTERRUPT_RXT0 | INTERRUPT_RXO); - in32(REG_INTERRUPT_CAUSE_READ); - enable_irq(); -} - -UNMAP_AFTER_INIT E1000NetworkAdapter::E1000NetworkAdapter(StringView interface_name, - PCI::DeviceIdentifier const& device_identifier, u8 irq, - NonnullOwnPtr registers_io_window, NonnullOwnPtr rx_buffer_region, - NonnullOwnPtr tx_buffer_region, NonnullOwnPtr rx_descriptors_region, - NonnullOwnPtr tx_descriptors_region) - : NetworkAdapter(interface_name) - , PCI::Device(device_identifier) - , IRQHandler(irq) - , m_registers_io_window(move(registers_io_window)) - , m_rx_descriptors_region(move(rx_descriptors_region)) - , m_tx_descriptors_region(move(tx_descriptors_region)) - , m_rx_buffer_region(move(rx_buffer_region)) - , m_tx_buffer_region(move(tx_buffer_region)) -{ -} - -UNMAP_AFTER_INIT E1000NetworkAdapter::~E1000NetworkAdapter() = default; - -bool E1000NetworkAdapter::handle_irq(RegisterState const&) -{ - u32 status = in32(REG_INTERRUPT_CAUSE_READ); - - m_entropy_source.add_random_event(status); - - if (status == 0) - return false; - - if (status & INTERRUPT_LSC) { - u32 flags = in32(REG_CTRL); - out32(REG_CTRL, flags | ECTRL_SLU); - - m_link_up = ((in32(REG_STATUS) & STATUS_LU) != 0); - } - if (status & INTERRUPT_RXDMT0) { - // Threshold OK? - } - if (status & INTERRUPT_RXO) { - dbgln_if(E1000_DEBUG, "E1000: RX buffer overrun"); - } - if (status & INTERRUPT_RXT0) { - receive(); - } - - m_wait_queue.wake_all(); - - out32(REG_INTERRUPT_CAUSE_READ, 0xffffffff); - return true; -} - -UNMAP_AFTER_INIT void E1000NetworkAdapter::detect_eeprom() -{ - out32(REG_EEPROM, 0x1); - for (int i = 0; i < 999; ++i) { - u32 data = in32(REG_EEPROM); - if (data & 0x10) { - m_has_eeprom.set(); - return; - } - } -} - -UNMAP_AFTER_INIT u32 E1000NetworkAdapter::read_eeprom(u8 address) -{ - u16 data = 0; - u32 tmp = 0; - if (m_has_eeprom.was_set()) { - out32(REG_EEPROM, ((u32)address << 8) | 1); - while (!((tmp = in32(REG_EEPROM)) & (1 << 4))) - Processor::wait_check(); - } else { - out32(REG_EEPROM, ((u32)address << 2) | 1); - while (!((tmp = in32(REG_EEPROM)) & (1 << 1))) - Processor::wait_check(); - } - data = (tmp >> 16) & 0xffff; - return data; -} - -UNMAP_AFTER_INIT void E1000NetworkAdapter::read_mac_address() -{ - if (m_has_eeprom.was_set()) { - MACAddress mac {}; - u32 tmp = read_eeprom(0); - mac[0] = tmp & 0xff; - mac[1] = tmp >> 8; - tmp = read_eeprom(1); - mac[2] = tmp & 0xff; - mac[3] = tmp >> 8; - tmp = read_eeprom(2); - mac[4] = tmp & 0xff; - mac[5] = tmp >> 8; - set_mac_address(mac); - } else { - VERIFY_NOT_REACHED(); - } -} - -UNMAP_AFTER_INIT void E1000NetworkAdapter::initialize_rx_descriptors() -{ - auto* rx_descriptors = (e1000_tx_desc*)m_rx_descriptors_region->vaddr().as_ptr(); - constexpr auto rx_buffer_page_count = rx_buffer_size / PAGE_SIZE; - for (size_t i = 0; i < number_of_rx_descriptors; ++i) { - auto& descriptor = rx_descriptors[i]; - m_rx_buffers[i] = m_rx_buffer_region->vaddr().as_ptr() + rx_buffer_size * i; - descriptor.addr = m_rx_buffer_region->physical_page(rx_buffer_page_count * i)->paddr().get(); - descriptor.status = 0; - } - - out32(REG_RXDESCLO, m_rx_descriptors_region->physical_page(0)->paddr().get()); - out32(REG_RXDESCHI, 0); - out32(REG_RXDESCLEN, number_of_rx_descriptors * sizeof(e1000_rx_desc)); - out32(REG_RXDESCHEAD, 0); - out32(REG_RXDESCTAIL, number_of_rx_descriptors - 1); - - out32(REG_RCTRL, RCTL_EN | RCTL_SBP | RCTL_UPE | RCTL_MPE | RCTL_LBM_NONE | RTCL_RDMTS_HALF | RCTL_BAM | RCTL_SECRC | RCTL_BSIZE_8192); -} - -UNMAP_AFTER_INIT void E1000NetworkAdapter::initialize_tx_descriptors() -{ - auto* tx_descriptors = (e1000_tx_desc*)m_tx_descriptors_region->vaddr().as_ptr(); - constexpr auto tx_buffer_page_count = tx_buffer_size / PAGE_SIZE; - - for (size_t i = 0; i < number_of_tx_descriptors; ++i) { - auto& descriptor = tx_descriptors[i]; - m_tx_buffers[i] = m_tx_buffer_region->vaddr().as_ptr() + tx_buffer_size * i; - descriptor.addr = m_tx_buffer_region->physical_page(tx_buffer_page_count * i)->paddr().get(); - descriptor.cmd = 0; - } - - out32(REG_TXDESCLO, m_tx_descriptors_region->physical_page(0)->paddr().get()); - out32(REG_TXDESCHI, 0); - out32(REG_TXDESCLEN, number_of_tx_descriptors * sizeof(e1000_tx_desc)); - out32(REG_TXDESCHEAD, 0); - out32(REG_TXDESCTAIL, 0); - - out32(REG_TCTRL, in32(REG_TCTRL) | TCTL_EN | TCTL_PSP); - out32(REG_TIPG, 0x0060200A); -} - -void E1000NetworkAdapter::out8(u16 address, u8 data) -{ - dbgln_if(E1000_DEBUG, "E1000: OUT8 {:#02x} @ {:#04x}", data, address); - m_registers_io_window->write8(address, data); -} - -void E1000NetworkAdapter::out16(u16 address, u16 data) -{ - dbgln_if(E1000_DEBUG, "E1000: OUT16 {:#04x} @ {:#04x}", data, address); - m_registers_io_window->write16(address, data); -} - -void E1000NetworkAdapter::out32(u16 address, u32 data) -{ - dbgln_if(E1000_DEBUG, "E1000: OUT32 {:#08x} @ {:#04x}", data, address); - m_registers_io_window->write32(address, data); -} - -u8 E1000NetworkAdapter::in8(u16 address) -{ - dbgln_if(E1000_DEBUG, "E1000: IN8 @ {:#04x}", address); - return m_registers_io_window->read8(address); -} - -u16 E1000NetworkAdapter::in16(u16 address) -{ - dbgln_if(E1000_DEBUG, "E1000: IN16 @ {:#04x}", address); - return m_registers_io_window->read16(address); -} - -u32 E1000NetworkAdapter::in32(u16 address) -{ - dbgln_if(E1000_DEBUG, "E1000: IN32 @ {:#04x}", address); - return m_registers_io_window->read32(address); -} - -void E1000NetworkAdapter::send_raw(ReadonlyBytes payload) -{ - disable_irq(); - size_t tx_current = in32(REG_TXDESCTAIL) % number_of_tx_descriptors; - dbgln_if(E1000_DEBUG, "E1000: Sending packet ({} bytes)", payload.size()); - auto* tx_descriptors = (e1000_tx_desc*)m_tx_descriptors_region->vaddr().as_ptr(); - auto& descriptor = tx_descriptors[tx_current]; - VERIFY(payload.size() <= 8192); - auto* vptr = (void*)m_tx_buffers[tx_current]; - memcpy(vptr, payload.data(), payload.size()); - descriptor.length = payload.size(); - descriptor.status = 0; - descriptor.cmd = CMD_EOP | CMD_IFCS | CMD_RS; - dbgln_if(E1000_DEBUG, "E1000: Using tx descriptor {} (head is at {})", tx_current, in32(REG_TXDESCHEAD)); - tx_current = (tx_current + 1) % number_of_tx_descriptors; - Processor::disable_interrupts(); - enable_irq(); - out32(REG_TXDESCTAIL, tx_current); - for (;;) { - if (descriptor.status) { - Processor::enable_interrupts(); - break; - } - m_wait_queue.wait_forever("E1000NetworkAdapter"sv); - } - dbgln_if(E1000_DEBUG, "E1000: Sent packet, status is now {:#02x}!", (u8)descriptor.status); -} - -void E1000NetworkAdapter::receive() -{ - auto* rx_descriptors = (e1000_tx_desc*)m_rx_descriptors_region->vaddr().as_ptr(); - u32 rx_current; - for (;;) { - rx_current = in32(REG_RXDESCTAIL) % number_of_rx_descriptors; - rx_current = (rx_current + 1) % number_of_rx_descriptors; - if (!(rx_descriptors[rx_current].status & 1)) - break; - auto* buffer = m_rx_buffers[rx_current]; - u16 length = rx_descriptors[rx_current].length; - VERIFY(length <= 8192); - dbgln_if(E1000_DEBUG, "E1000: Received 1 packet @ {:p} ({} bytes)", buffer, length); - did_receive({ buffer, length }); - rx_descriptors[rx_current].status = 0; - out32(REG_RXDESCTAIL, rx_current); - } -} - -i32 E1000NetworkAdapter::link_speed() -{ - if (!link_up()) - return NetworkAdapter::LINKSPEED_INVALID; - - u32 speed = in32(REG_STATUS) & STATUS_SPEED; - switch (speed) { - case STATUS_SPEED_10MB: - return 10; - case STATUS_SPEED_100MB: - return 100; - case STATUS_SPEED_1000MB1: - case STATUS_SPEED_1000MB2: - return 1000; - default: - return NetworkAdapter::LINKSPEED_INVALID; - } -} - -bool E1000NetworkAdapter::link_full_duplex() -{ - u32 status = in32(REG_STATUS); - return !!(status & STATUS_FD); -} - -} diff --git a/Kernel/Net/Intel/E1000NetworkAdapter.h b/Kernel/Net/Intel/E1000NetworkAdapter.h deleted file mode 100644 index 876b5d5e74b..00000000000 --- a/Kernel/Net/Intel/E1000NetworkAdapter.h +++ /dev/null @@ -1,106 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include -#include -#include -#include - -namespace Kernel { - -class E1000NetworkAdapter : public NetworkAdapter - , public PCI::Device - , public IRQHandler { -public: - static ErrorOr probe(PCI::DeviceIdentifier const&); - static ErrorOr> create(PCI::DeviceIdentifier const&); - virtual ErrorOr initialize(Badge) override; - - virtual ~E1000NetworkAdapter() override; - - virtual void send_raw(ReadonlyBytes) override; - virtual bool link_up() override { return m_link_up; } - virtual i32 link_speed() override; - virtual bool link_full_duplex() override; - - virtual StringView purpose() const override { return class_name(); } - virtual StringView device_name() const override { return "E1000"sv; } - virtual Type adapter_type() const override { return Type::Ethernet; } - -protected: - static constexpr size_t rx_buffer_size = 8192; - static constexpr size_t tx_buffer_size = 8192; - - void setup_interrupts(); - void setup_link(); - - E1000NetworkAdapter(StringView, PCI::DeviceIdentifier const&, u8 irq, - NonnullOwnPtr registers_io_window, NonnullOwnPtr rx_buffer_region, - NonnullOwnPtr tx_buffer_region, NonnullOwnPtr rx_descriptors_region, - NonnullOwnPtr tx_descriptors_region); - - virtual bool handle_irq(RegisterState const&) override; - virtual StringView class_name() const override { return "E1000NetworkAdapter"sv; } - - struct [[gnu::packed]] e1000_rx_desc { - uint64_t volatile addr { 0 }; - uint16_t volatile length { 0 }; - uint16_t volatile checksum { 0 }; - uint8_t volatile status { 0 }; - uint8_t volatile errors { 0 }; - uint16_t volatile special { 0 }; - }; - - struct [[gnu::packed]] e1000_tx_desc { - uint64_t volatile addr { 0 }; - uint16_t volatile length { 0 }; - uint8_t volatile cso { 0 }; - uint8_t volatile cmd { 0 }; - uint8_t volatile status { 0 }; - uint8_t volatile css { 0 }; - uint16_t volatile special { 0 }; - }; - - virtual void detect_eeprom(); - virtual u32 read_eeprom(u8 address); - void read_mac_address(); - - void initialize_rx_descriptors(); - void initialize_tx_descriptors(); - - void out8(u16 address, u8); - void out16(u16 address, u16); - void out32(u16 address, u32); - u8 in8(u16 address); - u16 in16(u16 address); - u32 in32(u16 address); - - void receive(); - - static constexpr size_t number_of_rx_descriptors = 256; - static constexpr size_t number_of_tx_descriptors = 256; - - NonnullOwnPtr m_registers_io_window; - - NonnullOwnPtr m_rx_descriptors_region; - NonnullOwnPtr m_tx_descriptors_region; - NonnullOwnPtr m_rx_buffer_region; - NonnullOwnPtr m_tx_buffer_region; - Array m_rx_buffers; - Array m_tx_buffers; - SetOnce m_has_eeprom; - bool m_link_up { false }; - EntropySource m_entropy_source; - - WaitQueue m_wait_queue; -}; -} diff --git a/Kernel/Net/LocalSocket.cpp b/Kernel/Net/LocalSocket.cpp deleted file mode 100644 index 1760d5d8952..00000000000 --- a/Kernel/Net/LocalSocket.cpp +++ /dev/null @@ -1,552 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace Kernel { - -static Singleton> s_list; - -static MutexProtected& all_sockets() -{ - return *s_list; -} - -void LocalSocket::for_each(Function callback) -{ - all_sockets().for_each_shared([&](auto const& socket) { - callback(socket); - }); -} - -ErrorOr LocalSocket::try_for_each(Function(LocalSocket const&)> callback) -{ - return all_sockets().with_shared([&](auto const& sockets) -> ErrorOr { - for (auto& socket : sockets) - TRY(callback(socket)); - return {}; - }); -} - -ErrorOr> LocalSocket::try_create(int type) -{ - auto client_buffer = TRY(DoubleBuffer::try_create("LocalSocket: Client buffer"sv)); - auto server_buffer = TRY(DoubleBuffer::try_create("LocalSocket: Server buffer"sv)); - return adopt_nonnull_ref_or_enomem(new (nothrow) LocalSocket(type, move(client_buffer), move(server_buffer))); -} - -ErrorOr LocalSocket::try_create_connected_pair(int type) -{ - auto socket = TRY(LocalSocket::try_create(type)); - auto description1 = TRY(OpenFileDescription::try_create(*socket)); - - TRY(socket->try_set_path("[socketpair]"sv)); - - socket->set_acceptor(Process::current()); - socket->set_connected(true); - socket->set_connect_side_role(Role::Connected); - socket->set_role(Role::Accepted); - - auto description2 = TRY(OpenFileDescription::try_create(*socket)); - - return SocketPair { move(description1), move(description2) }; -} - -LocalSocket::LocalSocket(int type, NonnullOwnPtr client_buffer, NonnullOwnPtr server_buffer) - : Socket(AF_LOCAL, type, 0) - , m_for_client(move(client_buffer)) - , m_for_server(move(server_buffer)) -{ - auto& current_process = Process::current(); - auto current_process_credentials = current_process.credentials(); - m_prebind_uid = current_process_credentials->euid(); - m_prebind_gid = current_process_credentials->egid(); - m_prebind_mode = 0666; - - m_for_client->set_unblock_callback([this]() { - evaluate_block_conditions(); - }); - m_for_server->set_unblock_callback([this]() { - evaluate_block_conditions(); - }); - - all_sockets().with_exclusive([&](auto& list) { - list.append(*this); - }); - - dbgln_if(LOCAL_SOCKET_DEBUG, "LocalSocket({}) created with type={}", this, type); -} - -LocalSocket::~LocalSocket() -{ - all_sockets().with_exclusive([&](auto& list) { - list.remove(*this); - }); -} - -void LocalSocket::get_local_address(sockaddr* address, socklen_t* address_size) -{ - auto& address_un = *reinterpret_cast(address); - address_un = { - .sun_family = AF_UNIX, - .sun_path = {}, - }; - - if (!m_path || m_path->is_empty()) { - *address_size = sizeof(address_un.sun_family); - return; - } - - size_t bytes_to_copy = min(m_path->length() + 1, min(static_cast(*address_size), sizeof(address_un.sun_path))); - memcpy(address_un.sun_path, m_path->characters(), bytes_to_copy); - *address_size = sizeof(address_un.sun_family) + bytes_to_copy; -} - -void LocalSocket::get_peer_address(sockaddr* address, socklen_t* address_size) -{ - get_local_address(address, address_size); -} - -ErrorOr LocalSocket::bind(Credentials const& credentials, Userspace user_address, socklen_t address_size) -{ - VERIFY(setup_state() == SetupState::Unstarted); - if (address_size > sizeof(sockaddr_un)) - return set_so_error(EINVAL); - - sockaddr_un address = {}; - SOCKET_TRY(copy_from_user(&address, user_address, address_size)); - - if (address.sun_family != AF_LOCAL) - return set_so_error(EINVAL); - - auto path = SOCKET_TRY(KString::try_create(StringView { address.sun_path, strnlen(address.sun_path, sizeof(address.sun_path)) })); - dbgln_if(LOCAL_SOCKET_DEBUG, "LocalSocket({}) bind({})", this, path); - - mode_t mode = S_IFSOCK | (m_prebind_mode & 0777); - UidAndGid owner { m_prebind_uid, m_prebind_gid }; - auto result = VirtualFileSystem::the().open(credentials, path->view(), O_CREAT | O_EXCL | O_NOFOLLOW_NOERROR, mode, Process::current().current_directory(), owner); - if (result.is_error()) { - if (result.error().code() == EEXIST) - return set_so_error(EADDRINUSE); - return result.release_error(); - } - - auto file = move(result.value()); - auto inode = file->inode(); - - VERIFY(inode); - if (!inode->bind_socket(*this)) - return set_so_error(EADDRINUSE); - - m_inode = inode; - - m_path = move(path); - m_bound.set(); - return {}; -} - -ErrorOr LocalSocket::connect(Credentials const& credentials, OpenFileDescription& description, Userspace user_address, socklen_t address_size) -{ - if (m_bound.was_set()) - return set_so_error(EISCONN); - - if (address_size > sizeof(sockaddr_un)) - return set_so_error(EINVAL); - - sockaddr_un address = {}; - SOCKET_TRY(copy_from_user(&address, user_address, address_size)); - - if (address.sun_family != AF_LOCAL) - return set_so_error(EINVAL); - - if (is_connected()) - return set_so_error(EISCONN); - - auto path = SOCKET_TRY(KString::try_create(StringView { address.sun_path, strnlen(address.sun_path, sizeof(address.sun_path)) })); - dbgln_if(LOCAL_SOCKET_DEBUG, "LocalSocket({}) connect({})", this, *path); - - auto file = SOCKET_TRY(VirtualFileSystem::the().open(credentials, path->view(), O_RDWR, 0, Process::current().current_directory())); - auto inode = file->inode(); - m_inode = inode; - - VERIFY(inode); - - auto peer = inode->bound_socket(); - if (!peer) - return set_so_error(ECONNREFUSED); - - m_path = move(path); - - VERIFY(m_connect_side_fd == &description); - set_connect_side_role(Role::Connecting); - - auto result = peer->queue_connection_from(*this); - if (result.is_error()) { - set_connect_side_role(Role::None); - return result; - } - - if (is_connected()) { - set_connect_side_role(Role::Connected); - return {}; - } - - auto unblock_flags = Thread::OpenFileDescriptionBlocker::BlockFlags::None; - if (Thread::current()->block({}, description, unblock_flags).was_interrupted()) { - set_connect_side_role(Role::None); - return set_so_error(EINTR); - } - - dbgln_if(LOCAL_SOCKET_DEBUG, "LocalSocket({}) connect({}) status is {}", this, *m_path, to_string(setup_state())); - - if (!has_flag(unblock_flags, Thread::OpenFileDescriptionBlocker::BlockFlags::Connect)) { - set_connect_side_role(Role::None); - return set_so_error(ECONNREFUSED); - } - set_connect_side_role(Role::Connected); - return {}; -} - -ErrorOr LocalSocket::listen(size_t backlog) -{ - MutexLocker locker(mutex()); - if (type() != SOCK_STREAM) - return set_so_error(EOPNOTSUPP); - set_backlog(backlog); - auto previous_role = m_role; - set_role(Role::Listener); - set_connect_side_role(Role::Listener, previous_role != m_role); - - dbgln_if(LOCAL_SOCKET_DEBUG, "LocalSocket({}) listening with backlog={}", this, backlog); - - return {}; -} - -ErrorOr LocalSocket::attach(OpenFileDescription& description) -{ - VERIFY(!m_accept_side_fd_open); - if (m_connect_side_role == Role::None) { - VERIFY(m_connect_side_fd == nullptr); - m_connect_side_fd = &description; - } else { - VERIFY(m_connect_side_fd != &description); - m_accept_side_fd_open = true; - } - - evaluate_block_conditions(); - return {}; -} - -void LocalSocket::detach(OpenFileDescription& description) -{ - if (m_connect_side_fd == &description) { - m_connect_side_fd = nullptr; - } else { - VERIFY(m_accept_side_fd_open); - m_accept_side_fd_open = false; - - if (m_bound.was_set()) { - if (m_inode) - m_inode->unbind_socket(); - } - } - - evaluate_block_conditions(); -} - -bool LocalSocket::can_read(OpenFileDescription const& description, u64) const -{ - auto role = this->role(description); - if (role == Role::Listener) - return can_accept(); - if (role == Role::Accepted) - return !has_attached_peer(description) || !m_for_server->is_empty(); - if (role == Role::Connected) - return !has_attached_peer(description) || !m_for_client->is_empty(); - return false; -} - -bool LocalSocket::has_attached_peer(OpenFileDescription const& description) const -{ - auto role = this->role(description); - if (role == Role::Accepted) - return m_connect_side_fd != nullptr; - if (role == Role::Connected) - return m_accept_side_fd_open; - return false; -} - -bool LocalSocket::can_write(OpenFileDescription const& description, u64) const -{ - auto role = this->role(description); - if (role == Role::Accepted) - return !has_attached_peer(description) || m_for_client->space_for_writing(); - if (role == Role::Connected) - return !has_attached_peer(description) || m_for_server->space_for_writing(); - return false; -} - -ErrorOr LocalSocket::sendto(OpenFileDescription& description, UserOrKernelBuffer const& data, size_t data_size, int, Userspace, socklen_t) -{ - if (!has_attached_peer(description)) - return set_so_error(EPIPE); - auto* socket_buffer = send_buffer_for(description); - if (!socket_buffer) - return set_so_error(EINVAL); - auto nwritten_or_error = socket_buffer->write(data, data_size); - if (!nwritten_or_error.is_error() && nwritten_or_error.value() > 0) - Thread::current()->did_unix_socket_write(nwritten_or_error.value()); - return nwritten_or_error; -} - -DoubleBuffer* LocalSocket::receive_buffer_for(OpenFileDescription& description) -{ - auto role = this->role(description); - if (role == Role::Accepted) - return m_for_server.ptr(); - if (role == Role::Connected) - return m_for_client.ptr(); - return nullptr; -} - -DoubleBuffer* LocalSocket::send_buffer_for(OpenFileDescription& description) -{ - auto role = this->role(description); - if (role == Role::Connected) - return m_for_server.ptr(); - if (role == Role::Accepted) - return m_for_client.ptr(); - return nullptr; -} - -ErrorOr LocalSocket::recvfrom(OpenFileDescription& description, UserOrKernelBuffer& buffer, size_t buffer_size, int, Userspace, Userspace, UnixDateTime&, bool blocking) -{ - auto* socket_buffer = receive_buffer_for(description); - if (!socket_buffer) - return set_so_error(EINVAL); - if (!blocking) { - if (socket_buffer->is_empty()) { - if (!has_attached_peer(description)) - return 0; - return set_so_error(EAGAIN); - } - } else if (!can_read(description, 0)) { - auto unblock_flags = Thread::OpenFileDescriptionBlocker::BlockFlags::None; - if (Thread::current()->block({}, description, unblock_flags).was_interrupted()) - return set_so_error(EINTR); - } - if (!has_attached_peer(description) && socket_buffer->is_empty()) - return 0; - VERIFY(!socket_buffer->is_empty()); - auto nread_or_error = socket_buffer->read(buffer, buffer_size); - if (!nread_or_error.is_error() && nread_or_error.value() > 0) - Thread::current()->did_unix_socket_read(nread_or_error.value()); - return nread_or_error; -} - -StringView LocalSocket::socket_path() const -{ - if (!m_path) - return {}; - return m_path->view(); -} - -ErrorOr> LocalSocket::pseudo_path(OpenFileDescription const& description) const -{ - StringBuilder builder; - TRY(builder.try_append("socket:"sv)); - TRY(builder.try_append(socket_path())); - - switch (role(description)) { - case Role::Listener: - TRY(builder.try_append(" (listening)"sv)); - break; - case Role::Accepted: - TRY(builder.try_appendff(" (accepted from pid {})", origin_pid())); - break; - case Role::Connected: - TRY(builder.try_appendff(" (connected to pid {})", acceptor_pid())); - break; - case Role::Connecting: - TRY(builder.try_append(" (connecting)"sv)); - break; - default: - break; - } - - return KString::try_create(builder.string_view()); -} - -ErrorOr LocalSocket::getsockopt(OpenFileDescription& description, int level, int option, Userspace value, Userspace value_size) -{ - if (level != SOL_SOCKET) - return Socket::getsockopt(description, level, option, value, value_size); - - MutexLocker locker(mutex()); - - socklen_t size; - TRY(copy_from_user(&size, value_size.unsafe_userspace_ptr())); - - switch (option) { - case SO_SNDBUF: - return ENOTSUP; - case SO_RCVBUF: - return ENOTSUP; - case SO_PEERCRED: { - if (size < sizeof(ucred)) - return EINVAL; - switch (role(description)) { - case Role::Accepted: - TRY(copy_to_user(static_ptr_cast(value), &m_origin)); - size = sizeof(ucred); - TRY(copy_to_user(value_size, &size)); - return {}; - case Role::Connected: - TRY(copy_to_user(static_ptr_cast(value), &m_acceptor)); - size = sizeof(ucred); - TRY(copy_to_user(value_size, &size)); - return {}; - case Role::Connecting: - return ENOTCONN; - default: - return EINVAL; - } - VERIFY_NOT_REACHED(); - } - default: - return Socket::getsockopt(description, level, option, value, value_size); - } -} - -ErrorOr LocalSocket::ioctl(OpenFileDescription& description, unsigned request, Userspace arg) -{ - switch (request) { - case FIONREAD: { - int readable = receive_buffer_for(description)->immediately_readable(); - return copy_to_user(static_ptr_cast(arg), &readable); - } - } - - return EINVAL; -} - -ErrorOr LocalSocket::chmod(Credentials const& credentials, OpenFileDescription& description, mode_t mode) -{ - if (m_inode) { - if (auto custody = description.custody()) - return VirtualFileSystem::the().chmod(credentials, *custody, mode); - VERIFY_NOT_REACHED(); - } - - m_prebind_mode = mode & 0777; - return {}; -} - -ErrorOr LocalSocket::chown(Credentials const& credentials, OpenFileDescription& description, UserID uid, GroupID gid) -{ - if (m_inode) { - if (auto custody = description.custody()) - return VirtualFileSystem::the().chown(credentials, *custody, uid, gid); - VERIFY_NOT_REACHED(); - } - - if (!credentials.is_superuser() && (credentials.euid() != uid || !credentials.in_group(gid))) - return set_so_error(EPERM); - - m_prebind_uid = uid; - m_prebind_gid = gid; - return {}; -} - -Vector>& LocalSocket::recvfd_queue_for(OpenFileDescription const& description) -{ - VERIFY(mutex().is_exclusively_locked_by_current_thread()); - auto role = this->role(description); - if (role == Role::Connected) - return m_fds_for_client; - if (role == Role::Accepted) - return m_fds_for_server; - VERIFY_NOT_REACHED(); -} - -Vector>& LocalSocket::sendfd_queue_for(OpenFileDescription const& description) -{ - VERIFY(mutex().is_exclusively_locked_by_current_thread()); - auto role = this->role(description); - if (role == Role::Connected) - return m_fds_for_server; - if (role == Role::Accepted) - return m_fds_for_client; - VERIFY_NOT_REACHED(); -} - -ErrorOr LocalSocket::sendfd(OpenFileDescription const& socket_description, NonnullRefPtr passing_description) -{ - MutexLocker locker(mutex()); - auto role = this->role(socket_description); - if (role != Role::Connected && role != Role::Accepted) - return set_so_error(EINVAL); - auto& queue = sendfd_queue_for(socket_description); - // FIXME: Figure out how we should limit this properly. - if (queue.size() > 128) - return set_so_error(EBUSY); - SOCKET_TRY(queue.try_append(move(passing_description))); - return {}; -} - -ErrorOr> LocalSocket::recvfd(OpenFileDescription const& socket_description) -{ - MutexLocker locker(mutex()); - auto role = this->role(socket_description); - if (role != Role::Connected && role != Role::Accepted) - return set_so_error(EINVAL); - auto& queue = recvfd_queue_for(socket_description); - if (queue.is_empty()) { - // FIXME: Figure out the perfect error code for this. - return set_so_error(EAGAIN); - } - return queue.take_first(); -} - -ErrorOr>> LocalSocket::recvfds(OpenFileDescription const& socket_description, int n) -{ - MutexLocker locker(mutex()); - Vector> fds; - - auto role = this->role(socket_description); - if (role != Role::Connected && role != Role::Accepted) - return set_so_error(EINVAL); - auto& queue = recvfd_queue_for(socket_description); - - for (int i = 0; i < n; ++i) { - if (queue.is_empty()) - break; - - fds.append(queue.take_first()); - } - - return fds; -} - -ErrorOr LocalSocket::try_set_path(StringView path) -{ - m_path = TRY(KString::try_create(path)); - return {}; -} - -} diff --git a/Kernel/Net/LocalSocket.h b/Kernel/Net/LocalSocket.h deleted file mode 100644 index f9325b4dc36..00000000000 --- a/Kernel/Net/LocalSocket.h +++ /dev/null @@ -1,114 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include - -namespace Kernel { - -class OpenFileDescription; - -struct SocketPair { - NonnullRefPtr description0; - NonnullRefPtr description1; -}; - -class LocalSocket final : public Socket { - -public: - static ErrorOr> try_create(int type); - static ErrorOr try_create_connected_pair(int type); - virtual ~LocalSocket() override; - - ErrorOr sendfd(OpenFileDescription const& socket_description, NonnullRefPtr passing_description); - ErrorOr> recvfd(OpenFileDescription const& socket_description); - ErrorOr>> recvfds(OpenFileDescription const& socket_description, int n); - - static void for_each(Function); - static ErrorOr try_for_each(Function(LocalSocket const&)>); - - StringView socket_path() const; - ErrorOr> pseudo_path(OpenFileDescription const& description) const override; - - // ^Socket - virtual ErrorOr bind(Credentials const&, Userspace, socklen_t) override; - virtual ErrorOr connect(Credentials const&, OpenFileDescription&, Userspace, socklen_t) override; - virtual ErrorOr listen(size_t) override; - virtual void get_local_address(sockaddr*, socklen_t*) override; - virtual void get_peer_address(sockaddr*, socklen_t*) override; - virtual ErrorOr attach(OpenFileDescription&) override; - virtual void detach(OpenFileDescription&) override; - virtual bool can_read(OpenFileDescription const&, u64) const override; - virtual bool can_write(OpenFileDescription const&, u64) const override; - virtual ErrorOr sendto(OpenFileDescription&, UserOrKernelBuffer const&, size_t, int, Userspace, socklen_t) override; - virtual ErrorOr recvfrom(OpenFileDescription&, UserOrKernelBuffer&, size_t, int flags, Userspace, Userspace, UnixDateTime&, bool blocking) override; - virtual ErrorOr getsockopt(OpenFileDescription&, int level, int option, Userspace, Userspace) override; - virtual ErrorOr ioctl(OpenFileDescription&, unsigned request, Userspace arg) override; - virtual ErrorOr chown(Credentials const&, OpenFileDescription&, UserID, GroupID) override; - virtual ErrorOr chmod(Credentials const&, OpenFileDescription&, mode_t) override; - -private: - explicit LocalSocket(int type, NonnullOwnPtr client_buffer, NonnullOwnPtr server_buffer); - virtual StringView class_name() const override { return "LocalSocket"sv; } - virtual bool is_local() const override { return true; } - bool has_attached_peer(OpenFileDescription const&) const; - DoubleBuffer* receive_buffer_for(OpenFileDescription&); - DoubleBuffer* send_buffer_for(OpenFileDescription&); - Vector>& sendfd_queue_for(OpenFileDescription const&); - Vector>& recvfd_queue_for(OpenFileDescription const&); - - void set_connect_side_role(Role connect_side_role, bool force_evaluate_block_conditions = false) - { - auto previous = m_connect_side_role; - m_connect_side_role = connect_side_role; - if (previous != m_connect_side_role || force_evaluate_block_conditions) - evaluate_block_conditions(); - } - - ErrorOr try_set_path(StringView); - - // The inode this socket is bound to. - RefPtr m_inode; - - UserID m_prebind_uid { 0 }; - GroupID m_prebind_gid { 0 }; - mode_t m_prebind_mode { 0 }; - - // A single LocalSocket is shared between two file descriptions - // on the connect side and the accept side; so we need to store - // an additional role for the connect side and differentiate - // between them. - Role m_connect_side_role { Role::None }; - OpenFileDescription* m_connect_side_fd { nullptr }; - - virtual Role role(OpenFileDescription const& description) const override - { - if (m_connect_side_fd == &description) - return m_connect_side_role; - return m_role; - } - - SetOnce m_bound; - bool m_accept_side_fd_open { false }; - OwnPtr m_path; - - NonnullOwnPtr m_for_client; - NonnullOwnPtr m_for_server; - - Vector> m_fds_for_client; - Vector> m_fds_for_server; - - IntrusiveListNode m_list_node; - -public: - using List = IntrusiveList<&LocalSocket::m_list_node>; -}; - -} diff --git a/Kernel/Net/LoopbackAdapter.cpp b/Kernel/Net/LoopbackAdapter.cpp deleted file mode 100644 index d88adb13f00..00000000000 --- a/Kernel/Net/LoopbackAdapter.cpp +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include - -namespace Kernel { - -static bool s_loopback_initialized = false; - -ErrorOr> LoopbackAdapter::try_create() -{ - return TRY(adopt_nonnull_ref_or_enomem(new (nothrow) LoopbackAdapter("loop"sv))); -} - -LoopbackAdapter::LoopbackAdapter(StringView interface_name) - : NetworkAdapter(interface_name) -{ - VERIFY(!s_loopback_initialized); - s_loopback_initialized = true; - // The networking subsystem currently assumes all adapters are Ethernet adapters, including the LoopbackAdapter, - // so all packets are pre-pended with an Ethernet Frame header. Since the MTU must not include any overhead added - // by the data-link (Ethernet in this case) or physical layers, we need to subtract it from the MTU. - set_mtu(65536 - sizeof(EthernetFrameHeader)); - set_mac_address({ 19, 85, 2, 9, 0x55, 0xaa }); -} - -LoopbackAdapter::~LoopbackAdapter() = default; - -void LoopbackAdapter::send_raw(ReadonlyBytes payload) -{ - dbgln_if(LOOPBACK_DEBUG, "LoopbackAdapter: Sending {} byte(s) to myself.", payload.size()); - did_receive(payload); -} - -} diff --git a/Kernel/Net/LoopbackAdapter.h b/Kernel/Net/LoopbackAdapter.h deleted file mode 100644 index f1fbda008fc..00000000000 --- a/Kernel/Net/LoopbackAdapter.h +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include - -namespace Kernel { - -class LoopbackAdapter final : public NetworkAdapter { -private: - LoopbackAdapter(StringView); - -public: - static ErrorOr> try_create(); - virtual ~LoopbackAdapter() override; - - virtual ErrorOr initialize(Badge) override { VERIFY_NOT_REACHED(); } - - virtual void send_raw(ReadonlyBytes) override; - virtual StringView class_name() const override { return "LoopbackAdapter"sv; } - virtual Type adapter_type() const override { return Type::Loopback; } - virtual bool link_up() override { return true; } - virtual bool link_full_duplex() override { return true; } - virtual int link_speed() override { return 1000; } -}; - -} diff --git a/Kernel/Net/NetworkAdapter.cpp b/Kernel/Net/NetworkAdapter.cpp deleted file mode 100644 index 8b0692d98e4..00000000000 --- a/Kernel/Net/NetworkAdapter.cpp +++ /dev/null @@ -1,162 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include -#include -#include - -namespace Kernel { - -NetworkAdapter::NetworkAdapter(StringView interface_name) -{ - m_name.store_characters(interface_name); -} - -NetworkAdapter::~NetworkAdapter() = default; - -void NetworkAdapter::send_packet(ReadonlyBytes packet) -{ - m_packets_out++; - m_bytes_out += packet.size(); - send_raw(packet); -} - -void NetworkAdapter::send(MACAddress const& destination, ARPPacket const& packet) -{ - size_t size_in_bytes = sizeof(EthernetFrameHeader) + sizeof(ARPPacket); - auto buffer_result = NetworkByteBuffer::create_zeroed(size_in_bytes); - if (buffer_result.is_error()) { - dbgln("Dropping ARP packet targeted at {} as there is not enough memory to buffer it", packet.target_hardware_address().to_string()); - return; - } - auto* eth = (EthernetFrameHeader*)buffer_result.value().data(); - eth->set_source(mac_address()); - eth->set_destination(destination); - eth->set_ether_type(EtherType::ARP); - memcpy(eth->payload(), &packet, sizeof(ARPPacket)); - send_packet({ (u8 const*)eth, size_in_bytes }); -} - -void NetworkAdapter::fill_in_ipv4_header(PacketWithTimestamp& packet, IPv4Address const& source_ipv4, MACAddress const& destination_mac, IPv4Address const& destination_ipv4, IPv4Protocol protocol, size_t payload_size, u8 type_of_service, u8 ttl) -{ - size_t ipv4_packet_size = sizeof(IPv4Packet) + payload_size; - VERIFY(ipv4_packet_size <= mtu()); - - size_t ethernet_frame_size = ipv4_payload_offset() + payload_size; - VERIFY(packet.buffer->size() == ethernet_frame_size); - memset(packet.buffer->data(), 0, ipv4_payload_offset()); - auto& eth = *(EthernetFrameHeader*)packet.buffer->data(); - eth.set_source(mac_address()); - eth.set_destination(destination_mac); - eth.set_ether_type(EtherType::IPv4); - auto& ipv4 = *(IPv4Packet*)eth.payload(); - ipv4.set_version(4); - ipv4.set_internet_header_length(5); - ipv4.set_dscp_and_ecn(type_of_service); - ipv4.set_source(source_ipv4); - ipv4.set_destination(destination_ipv4); - ipv4.set_protocol((u8)protocol); - ipv4.set_length(ipv4_packet_size); - ipv4.set_ident(1); - ipv4.set_ttl(ttl); - ipv4.set_checksum(ipv4.compute_checksum()); -} - -void NetworkAdapter::did_receive(ReadonlyBytes payload) -{ - InterruptDisabler disabler; - m_packets_in++; - m_bytes_in += payload.size(); - - if (m_packet_queue_size == max_packet_buffers) { - m_packets_dropped++; - return; - } - - auto packet = acquire_packet_buffer(payload.size()); - if (!packet) { - dbgln("Discarding packet because we're out of memory"); - return; - } - - memcpy(packet->buffer->data(), payload.data(), payload.size()); - - m_packet_queue.append(*packet); - m_packet_queue_size++; - - if (on_receive) - on_receive(); -} - -size_t NetworkAdapter::dequeue_packet(u8* buffer, size_t buffer_size, UnixDateTime& packet_timestamp) -{ - InterruptDisabler disabler; - if (m_packet_queue.is_empty()) - return 0; - auto packet_with_timestamp = m_packet_queue.take_first(); - m_packet_queue_size--; - packet_timestamp = packet_with_timestamp->timestamp; - auto& packet_buffer = packet_with_timestamp->buffer; - size_t packet_size = packet_buffer->size(); - VERIFY(packet_size <= buffer_size); - memcpy(buffer, packet_buffer->data(), packet_size); - release_packet_buffer(*packet_with_timestamp); - return packet_size; -} - -RefPtr NetworkAdapter::acquire_packet_buffer(size_t size) -{ - auto packet = m_unused_packets.with([size](auto& unused_packets) -> RefPtr { - if (unused_packets.is_empty()) - return nullptr; - - auto unused_packet = unused_packets.take_first(); - - if (unused_packet->buffer->capacity() >= size) - return unused_packet; - - unused_packets.append(*unused_packet); - return nullptr; - }); - - if (packet) { - packet->timestamp = kgettimeofday(); - packet->buffer->set_size(size); - return packet; - } - - auto buffer_or_error = KBuffer::try_create_with_size("NetworkAdapter: Packet buffer"sv, size, Memory::Region::Access::ReadWrite, AllocationStrategy::AllocateNow); - if (buffer_or_error.is_error()) - return {}; - packet = adopt_ref_if_nonnull(new (nothrow) PacketWithTimestamp { buffer_or_error.release_value(), kgettimeofday() }); - if (!packet) - return {}; - packet->buffer->set_size(size); - return packet; -} - -void NetworkAdapter::release_packet_buffer(PacketWithTimestamp& packet) -{ - m_unused_packets.with([&packet](auto& unused_packets) { - unused_packets.append(packet); - }); -} - -void NetworkAdapter::set_ipv4_address(IPv4Address const& address) -{ - m_ipv4_address = address; -} - -void NetworkAdapter::set_ipv4_netmask(IPv4Address const& netmask) -{ - m_ipv4_netmask = netmask; -} - -} diff --git a/Kernel/Net/NetworkAdapter.h b/Kernel/Net/NetworkAdapter.h deleted file mode 100644 index d676550122b..00000000000 --- a/Kernel/Net/NetworkAdapter.h +++ /dev/null @@ -1,133 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace Kernel { - -class NetworkAdapter; - -using NetworkByteBuffer = AK::Detail::ByteBuffer<1500>; - -struct PacketWithTimestamp final : public AtomicRefCounted { - PacketWithTimestamp(NonnullOwnPtr buffer, UnixDateTime timestamp) - : buffer(move(buffer)) - , timestamp(timestamp) - { - } - - ReadonlyBytes bytes() { return buffer->bytes(); } - - NonnullOwnPtr buffer; - UnixDateTime timestamp; - IntrusiveListNode> packet_node; -}; - -class NetworkingManagement; -class NetworkAdapter - : public AtomicRefCounted - , public LockWeakable { -public: - enum class Type { - Loopback, - Ethernet - }; - - static constexpr i32 LINKSPEED_INVALID = -1; - - virtual ~NetworkAdapter(); - - virtual StringView class_name() const = 0; - virtual Type adapter_type() const = 0; - virtual ErrorOr initialize(Badge) = 0; - - StringView name() const { return m_name.representable_view(); } - MACAddress mac_address() { return m_mac_address; } - IPv4Address ipv4_address() const { return m_ipv4_address; } - IPv4Address ipv4_netmask() const { return m_ipv4_netmask; } - IPv4Address ipv4_broadcast() const { return IPv4Address { (m_ipv4_address.to_u32() & m_ipv4_netmask.to_u32()) | ~m_ipv4_netmask.to_u32() }; } - virtual bool link_up() { return false; } - virtual i32 link_speed() - { - // In Mbit/sec. - return LINKSPEED_INVALID; - } - virtual bool link_full_duplex() { return false; } - - void set_ipv4_address(IPv4Address const&); - void set_ipv4_netmask(IPv4Address const&); - - void send(MACAddress const&, ARPPacket const&); - void fill_in_ipv4_header(PacketWithTimestamp&, IPv4Address const&, MACAddress const&, IPv4Address const&, IPv4Protocol, size_t, u8 type_of_service, u8 ttl); - - size_t dequeue_packet(u8* buffer, size_t buffer_size, UnixDateTime& packet_timestamp); - - bool has_queued_packets() const { return !m_packet_queue.is_empty(); } - - u32 mtu() const { return m_mtu; } - void set_mtu(u32 mtu) { m_mtu = mtu; } - - u32 packets_in() const { return m_packets_in; } - u32 bytes_in() const { return m_bytes_in; } - u32 packets_out() const { return m_packets_out; } - u32 bytes_out() const { return m_bytes_out; } - u32 packets_dropped() const { return m_packets_dropped; } - - RefPtr acquire_packet_buffer(size_t); - void release_packet_buffer(PacketWithTimestamp&); - - constexpr size_t layer3_payload_offset() const { return sizeof(EthernetFrameHeader); } - constexpr size_t ipv4_payload_offset() const { return layer3_payload_offset() + sizeof(IPv4Packet); } - - Function on_receive; - - void send_packet(ReadonlyBytes); - -protected: - NetworkAdapter(StringView); - void set_mac_address(MACAddress const& mac_address) { m_mac_address = mac_address; } - void did_receive(ReadonlyBytes); - virtual void send_raw(ReadonlyBytes) = 0; - -private: - MACAddress m_mac_address; - IPv4Address m_ipv4_address; - IPv4Address m_ipv4_netmask; - - // FIXME: Make this configurable - static constexpr size_t max_packet_buffers = 1024; - - using PacketList = IntrusiveList<&PacketWithTimestamp::packet_node>; - - PacketList m_packet_queue; - size_t m_packet_queue_size { 0 }; - SpinlockProtected m_unused_packets {}; - FixedStringBuffer m_name; - u32 m_packets_in { 0 }; - u32 m_bytes_in { 0 }; - u32 m_packets_out { 0 }; - u32 m_bytes_out { 0 }; - u32 m_mtu { 1500 }; - u32 m_packets_dropped { 0 }; -}; - -} diff --git a/Kernel/Net/NetworkTask.cpp b/Kernel/Net/NetworkTask.cpp deleted file mode 100644 index 18ab549c1fb..00000000000 --- a/Kernel/Net/NetworkTask.cpp +++ /dev/null @@ -1,707 +0,0 @@ -/* - * Copyright (c) 2018-2021, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace Kernel { - -static void handle_arp(EthernetFrameHeader const&, size_t frame_size); -static void handle_ipv4(EthernetFrameHeader const&, size_t frame_size, UnixDateTime const& packet_timestamp); -static void handle_icmp(EthernetFrameHeader const&, IPv4Packet const&, UnixDateTime const& packet_timestamp); -static void handle_udp(IPv4Packet const&, UnixDateTime const& packet_timestamp); -static void handle_tcp(IPv4Packet const&, UnixDateTime const& packet_timestamp); -static void send_delayed_tcp_ack(TCPSocket& socket); -static void send_tcp_rst(IPv4Packet const& ipv4_packet, TCPPacket const& tcp_packet, RefPtr adapter); -static void flush_delayed_tcp_acks(); -static void retransmit_tcp_packets(); - -static Thread* network_task = nullptr; -static HashTable>* delayed_ack_sockets; - -[[noreturn]] static void NetworkTask_main(void*); - -void NetworkTask::spawn() -{ - auto [_, first_thread] = MUST(Process::create_kernel_process("Network Task"sv, NetworkTask_main, nullptr)); - network_task = first_thread; -} - -bool NetworkTask::is_current() -{ - return Thread::current() == network_task; -} - -void NetworkTask_main(void*) -{ - delayed_ack_sockets = new HashTable>; - - WaitQueue packet_wait_queue; - int pending_packets = 0; - NetworkingManagement::the().for_each([&](auto& adapter) { - dmesgln("NetworkTask: {} network adapter found: hw={}", adapter.class_name(), adapter.mac_address().to_string()); - - if (adapter.class_name() == "LoopbackAdapter"sv) { - adapter.set_ipv4_address({ 127, 0, 0, 1 }); - adapter.set_ipv4_netmask({ 255, 0, 0, 0 }); - } - - adapter.on_receive = [&]() { - pending_packets++; - packet_wait_queue.wake_all(); - }; - }); - - auto dequeue_packet = [&pending_packets](u8* buffer, size_t buffer_size, UnixDateTime& packet_timestamp) -> size_t { - if (pending_packets == 0) - return 0; - size_t packet_size = 0; - NetworkingManagement::the().for_each([&](auto& adapter) { - if (packet_size || !adapter.has_queued_packets()) - return; - packet_size = adapter.dequeue_packet(buffer, buffer_size, packet_timestamp); - pending_packets--; - dbgln_if(NETWORK_TASK_DEBUG, "NetworkTask: Dequeued packet from {} ({} bytes)", adapter.name(), packet_size); - }); - return packet_size; - }; - - size_t buffer_size = 64 * KiB; - auto region_or_error = MM.allocate_kernel_region(buffer_size, "Kernel Packet Buffer"sv, Memory::Region::Access::ReadWrite); - if (region_or_error.is_error()) - TODO(); - auto buffer_region = region_or_error.release_value(); - auto buffer = (u8*)buffer_region->vaddr().get(); - UnixDateTime packet_timestamp; - - while (!Process::current().is_dying()) { - flush_delayed_tcp_acks(); - retransmit_tcp_packets(); - size_t packet_size = dequeue_packet(buffer, buffer_size, packet_timestamp); - if (!packet_size) { - auto timeout_time = Duration::from_milliseconds(500); - auto timeout = Thread::BlockTimeout { false, &timeout_time }; - [[maybe_unused]] auto result = packet_wait_queue.wait_on(timeout, "NetworkTask"sv); - continue; - } - if (packet_size < sizeof(EthernetFrameHeader)) { - dbgln("NetworkTask: Packet is too small to be an Ethernet packet! ({})", packet_size); - continue; - } - auto& eth = *(EthernetFrameHeader const*)buffer; - dbgln_if(ETHERNET_DEBUG, "NetworkTask: From {} to {}, ether_type={:#04x}, packet_size={}", eth.source().to_string(), eth.destination().to_string(), eth.ether_type(), packet_size); - - switch (eth.ether_type()) { - case EtherType::ARP: - handle_arp(eth, packet_size); - break; - case EtherType::IPv4: - handle_ipv4(eth, packet_size, packet_timestamp); - break; - case EtherType::IPv6: - // ignore - break; - default: - dbgln_if(ETHERNET_DEBUG, "NetworkTask: Unknown ethernet type {:#04x}", eth.ether_type()); - } - } - Process::current().sys$exit(0); - VERIFY_NOT_REACHED(); -} - -void handle_arp(EthernetFrameHeader const& eth, size_t frame_size) -{ - constexpr size_t minimum_arp_frame_size = sizeof(EthernetFrameHeader) + sizeof(ARPPacket); - if (frame_size < minimum_arp_frame_size) { - dbgln("handle_arp: Frame too small ({}, need {})", frame_size, minimum_arp_frame_size); - return; - } - auto& packet = *static_cast(eth.payload()); - if (packet.hardware_type() != 1 || packet.hardware_address_length() != sizeof(MACAddress)) { - dbgln("handle_arp: Hardware type not ethernet ({:#04x}, len={})", packet.hardware_type(), packet.hardware_address_length()); - return; - } - if (packet.protocol_type() != EtherType::IPv4 || packet.protocol_address_length() != sizeof(IPv4Address)) { - dbgln("handle_arp: Protocol type not IPv4 ({:#04x}, len={})", packet.protocol_type(), packet.protocol_address_length()); - return; - } - - dbgln_if(ARP_DEBUG, "handle_arp: operation={:#04x}, sender={}/{}, target={}/{}", - packet.operation(), - packet.sender_hardware_address().to_string(), - packet.sender_protocol_address().to_string(), - packet.target_hardware_address().to_string(), - packet.target_protocol_address().to_string()); - - if (!packet.sender_hardware_address().is_zero() && !packet.sender_protocol_address().is_zero()) { - // Someone has this IPv4 address. I guess we can try to remember that. - // FIXME: Protect against ARP spamming. - update_arp_table(packet.sender_protocol_address(), packet.sender_hardware_address(), UpdateTable::Set); - } - - if (packet.operation() == ARPOperation::Request) { - // Who has this IP address? - if (auto adapter = NetworkingManagement::the().from_ipv4_address(packet.target_protocol_address())) { - // We do! - dbgln("handle_arp: Responding to ARP request for my IPv4 address ({})", adapter->ipv4_address()); - ARPPacket response; - response.set_operation(ARPOperation::Response); - response.set_target_hardware_address(packet.sender_hardware_address()); - response.set_target_protocol_address(packet.sender_protocol_address()); - response.set_sender_hardware_address(adapter->mac_address()); - response.set_sender_protocol_address(adapter->ipv4_address()); - - adapter->send(packet.sender_hardware_address(), response); - } - return; - } -} - -void handle_ipv4(EthernetFrameHeader const& eth, size_t frame_size, UnixDateTime const& packet_timestamp) -{ - constexpr size_t minimum_ipv4_frame_size = sizeof(EthernetFrameHeader) + sizeof(IPv4Packet); - if (frame_size < minimum_ipv4_frame_size) { - dbgln("handle_ipv4: Frame too small ({}, need {})", frame_size, minimum_ipv4_frame_size); - return; - } - auto& packet = *static_cast(eth.payload()); - - if (packet.length() < sizeof(IPv4Packet)) { - dbgln("handle_ipv4: IPv4 packet too short ({}, need {})", packet.length(), sizeof(IPv4Packet)); - return; - } - - size_t actual_ipv4_packet_length = frame_size - sizeof(EthernetFrameHeader); - if (packet.length() > actual_ipv4_packet_length) { - dbgln("handle_ipv4: IPv4 packet claims to be longer than it is ({}, actually {})", packet.length(), actual_ipv4_packet_length); - return; - } - - dbgln_if(IPV4_DEBUG, "handle_ipv4: source={}, destination={}", packet.source(), packet.destination()); - - NetworkingManagement::the().for_each([&](auto& adapter) { - if (adapter.ipv4_address().is_zero() || !adapter.link_up()) - return; - - auto my_net = adapter.ipv4_address().to_u32() & adapter.ipv4_netmask().to_u32(); - auto their_net = packet.source().to_u32() & adapter.ipv4_netmask().to_u32(); - if (my_net == their_net) - update_arp_table(packet.source(), eth.source(), UpdateTable::Set); - }); - - switch ((IPv4Protocol)packet.protocol()) { - case IPv4Protocol::ICMP: - return handle_icmp(eth, packet, packet_timestamp); - case IPv4Protocol::UDP: - return handle_udp(packet, packet_timestamp); - case IPv4Protocol::TCP: - return handle_tcp(packet, packet_timestamp); - default: - dbgln_if(IPV4_DEBUG, "handle_ipv4: Unhandled protocol {:#02x}", packet.protocol()); - break; - } -} - -void handle_icmp(EthernetFrameHeader const& eth, IPv4Packet const& ipv4_packet, UnixDateTime const& packet_timestamp) -{ - auto& icmp_header = *static_cast(ipv4_packet.payload()); - dbgln_if(ICMP_DEBUG, "handle_icmp: source={}, destination={}, type={:#02x}, code={:#02x}", ipv4_packet.source().to_string(), ipv4_packet.destination().to_string(), icmp_header.type(), icmp_header.code()); - - { - Vector> icmp_sockets; - IPv4Socket::all_sockets().with_exclusive([&](auto& sockets) { - for (auto& socket : sockets) { - if (socket.protocol() == (unsigned)IPv4Protocol::ICMP) - icmp_sockets.append(socket); - } - }); - for (auto& socket : icmp_sockets) - socket->did_receive(ipv4_packet.source(), 0, { &ipv4_packet, sizeof(IPv4Packet) + ipv4_packet.payload_size() }, packet_timestamp); - } - - auto adapter = NetworkingManagement::the().from_ipv4_address(ipv4_packet.destination()); - if (!adapter) - return; - - if (icmp_header.type() == ICMPType::EchoRequest) { - auto& request = reinterpret_cast(icmp_header); - dbgln("handle_icmp: EchoRequest from {}: id={}, seq={}", ipv4_packet.source(), (u16)request.identifier, (u16)request.sequence_number); - size_t icmp_packet_size = ipv4_packet.payload_size(); - if (icmp_packet_size < sizeof(ICMPEchoPacket)) { - dbgln("handle_icmp: EchoRequest packet is too small, ignoring."); - return; - } - auto ipv4_payload_offset = adapter->ipv4_payload_offset(); - auto packet = adapter->acquire_packet_buffer(ipv4_payload_offset + icmp_packet_size); - if (!packet) { - dbgln("Could not allocate packet buffer while sending ICMP packet"); - return; - } - adapter->fill_in_ipv4_header(*packet, adapter->ipv4_address(), eth.source(), ipv4_packet.source(), IPv4Protocol::ICMP, icmp_packet_size, 0, 64); - memset(packet->buffer->data() + ipv4_payload_offset, 0, sizeof(ICMPEchoPacket)); - auto& response = *(ICMPEchoPacket*)(packet->buffer->data() + ipv4_payload_offset); - response.header.set_type(ICMPType::EchoReply); - response.header.set_code(0); - response.identifier = request.identifier; - response.sequence_number = request.sequence_number; - if (size_t icmp_payload_size = icmp_packet_size - sizeof(ICMPEchoPacket)) - memcpy(response.payload(), request.payload(), icmp_payload_size); - response.header.set_checksum(internet_checksum(&response, icmp_packet_size)); - // FIXME: What is the right TTL value here? Is 64 ok? Should we use the same TTL as the echo request? - adapter->send_packet(packet->bytes()); - adapter->release_packet_buffer(*packet); - } -} - -void handle_udp(IPv4Packet const& ipv4_packet, UnixDateTime const& packet_timestamp) -{ - if (ipv4_packet.payload_size() < sizeof(UDPPacket)) { - dbgln("handle_udp: Packet too small ({}, need {})", ipv4_packet.payload_size(), sizeof(UDPPacket)); - return; - } - - auto& udp_packet = *static_cast(ipv4_packet.payload()); - dbgln_if(UDP_DEBUG, "handle_udp: source={}:{}, destination={}:{}, length={}", - ipv4_packet.source(), udp_packet.source_port(), - ipv4_packet.destination(), udp_packet.destination_port(), - udp_packet.length()); - - auto socket = UDPSocket::from_port(udp_packet.destination_port()); - if (!socket) { - dbgln_if(UDP_DEBUG, "handle_udp: No local UDP socket for {}:{}", ipv4_packet.destination(), udp_packet.destination_port()); - return; - } - - VERIFY(socket->type() == SOCK_DGRAM); - VERIFY(socket->local_port() == udp_packet.destination_port()); - - auto& destination = ipv4_packet.destination(); - - if (destination == IPv4Address(255, 255, 255, 255) || NetworkingManagement::the().from_ipv4_address(destination) || socket->multicast_memberships().contains_slow(destination)) - socket->did_receive(ipv4_packet.source(), udp_packet.source_port(), { &ipv4_packet, sizeof(IPv4Packet) + ipv4_packet.payload_size() }, packet_timestamp); -} - -void send_delayed_tcp_ack(TCPSocket& socket) -{ - VERIFY(socket.mutex().is_locked()); - if (!socket.should_delay_next_ack()) { - [[maybe_unused]] auto result = socket.send_ack(); - return; - } - - delayed_ack_sockets->set(move(socket)); -} - -void flush_delayed_tcp_acks() -{ - Vector, 32> remaining_sockets; - for (auto& socket : *delayed_ack_sockets) { - MutexLocker locker(socket->mutex()); - if (socket->should_delay_next_ack()) { - MUST(remaining_sockets.try_append(*socket)); - continue; - } - [[maybe_unused]] auto result = socket->send_ack(); - } - - if (remaining_sockets.size() != delayed_ack_sockets->size()) { - delayed_ack_sockets->clear(); - if (remaining_sockets.size() > 0) - dbgln("flush_delayed_tcp_acks: {} sockets remaining", remaining_sockets.size()); - for (auto&& socket : remaining_sockets) - delayed_ack_sockets->set(move(socket)); - } -} - -void send_tcp_rst(IPv4Packet const& ipv4_packet, TCPPacket const& tcp_packet, RefPtr adapter) -{ - auto routing_decision = route_to(ipv4_packet.source(), ipv4_packet.destination(), adapter); - if (routing_decision.is_zero()) - return; - - auto ipv4_payload_offset = routing_decision.adapter->ipv4_payload_offset(); - - size_t const options_size = 0; - size_t const tcp_header_size = sizeof(TCPPacket) + options_size; - size_t const buffer_size = ipv4_payload_offset + tcp_header_size; - - auto packet = routing_decision.adapter->acquire_packet_buffer(buffer_size); - if (!packet) - return; - routing_decision.adapter->fill_in_ipv4_header(*packet, ipv4_packet.destination(), - routing_decision.next_hop, ipv4_packet.source(), IPv4Protocol::TCP, - buffer_size - ipv4_payload_offset, 0, 64); - - auto& rst_packet = *(TCPPacket*)(packet->buffer->data() + ipv4_payload_offset); - rst_packet = {}; - rst_packet.set_source_port(tcp_packet.destination_port()); - rst_packet.set_destination_port(tcp_packet.source_port()); - rst_packet.set_window_size(0); - rst_packet.set_sequence_number(0); - rst_packet.set_ack_number(tcp_packet.sequence_number() + 1); - rst_packet.set_data_offset(tcp_header_size / sizeof(u32)); - rst_packet.set_flags(TCPFlags::RST | TCPFlags::ACK); - rst_packet.set_checksum(TCPSocket::compute_tcp_checksum(ipv4_packet.source(), ipv4_packet.destination(), rst_packet, 0)); - - routing_decision.adapter->send_packet(packet->bytes()); - routing_decision.adapter->release_packet_buffer(*packet); -} - -void handle_tcp(IPv4Packet const& ipv4_packet, UnixDateTime const& packet_timestamp) -{ - if (ipv4_packet.payload_size() < sizeof(TCPPacket)) { - dbgln("handle_tcp: IPv4 payload is too small to be a TCP packet ({}, need {})", ipv4_packet.payload_size(), sizeof(TCPPacket)); - return; - } - - auto& tcp_packet = *static_cast(ipv4_packet.payload()); - - size_t minimum_tcp_header_size = 5 * sizeof(u32); - size_t maximum_tcp_header_size = 15 * sizeof(u32); - if (tcp_packet.header_size() < minimum_tcp_header_size || tcp_packet.header_size() > maximum_tcp_header_size) { - dbgln("handle_tcp: TCP packet header has invalid size {}", tcp_packet.header_size()); - } - - if (ipv4_packet.payload_size() < tcp_packet.header_size()) { - dbgln("handle_tcp: IPv4 payload is smaller than TCP header claims ({}, supposedly {})", ipv4_packet.payload_size(), tcp_packet.header_size()); - return; - } - - size_t payload_size = ipv4_packet.payload_size() - tcp_packet.header_size(); - - dbgln_if(TCP_DEBUG, "handle_tcp: source={}:{}, destination={}:{}, seq_no={}, ack_no={}, flags={:#04x} ({}{}{}{}), window_size={}, payload_size={}", - ipv4_packet.source().to_string(), - tcp_packet.source_port(), - ipv4_packet.destination().to_string(), - tcp_packet.destination_port(), - tcp_packet.sequence_number(), - tcp_packet.ack_number(), - tcp_packet.flags(), - tcp_packet.has_syn() ? "SYN " : "", - tcp_packet.has_ack() ? "ACK " : "", - tcp_packet.has_fin() ? "FIN " : "", - tcp_packet.has_rst() ? "RST " : "", - tcp_packet.window_size(), - payload_size); - - auto adapter = NetworkingManagement::the().from_ipv4_address(ipv4_packet.destination()); - if (!adapter) { - dbgln("handle_tcp: this packet is not for me, it's for {}", ipv4_packet.destination()); - return; - } - - IPv4SocketTuple tuple(ipv4_packet.destination(), tcp_packet.destination_port(), ipv4_packet.source(), tcp_packet.source_port()); - - dbgln_if(TCP_DEBUG, "handle_tcp: looking for socket; tuple={}", tuple.to_string()); - - auto socket = TCPSocket::from_tuple(tuple); - if (!socket) { - if (!tcp_packet.has_rst()) { - dbgln("handle_tcp: No TCP socket for tuple {}. Sending RST.", tuple.to_string()); - send_tcp_rst(ipv4_packet, tcp_packet, adapter); - } - return; - } - - MutexLocker locker(socket->mutex()); - - VERIFY(socket->type() == SOCK_STREAM); - VERIFY(socket->local_port() == tcp_packet.destination_port()); - - dbgln_if(TCP_DEBUG, "handle_tcp: got socket {}; state={}", socket->tuple().to_string(), TCPSocket::to_string(socket->state())); - - socket->receive_tcp_packet(tcp_packet, ipv4_packet.payload_size()); - Optional send_window_scale; - if (tcp_packet.has_syn()) { - tcp_packet.for_each_option([&send_window_scale](auto const& option) { - if (option.kind() != TCPOptionKind::WindowScale) - return; - if (option.length() != sizeof(TCPOptionWindowScale)) - return; - auto scale = static_cast(option).value(); - if (scale > 14) - return; // Maximum allowed as per RFC7323 - send_window_scale = scale; - }); - } - - switch (socket->state()) { - case TCPSocket::State::Closed: - dbgln("handle_tcp: unexpected flags in Closed state ({:x}) for socket with tuple {}", tcp_packet.flags(), tuple.to_string()); - if (tcp_packet.has_rst()) { - return; - } - socket->set_sequence_number(tcp_packet.has_ack() ? tcp_packet.ack_number() : 0); - socket->set_ack_number(tcp_packet.sequence_number() + payload_size + 1); - (void)socket->send_tcp_packet(TCPFlags::RST | TCPFlags::ACK); - return; - case TCPSocket::State::TimeWait: - dbgln("handle_tcp: unexpected flags in TimeWait state ({:x}) for socket with tuple {}", tcp_packet.flags(), tuple.to_string()); - (void)socket->send_tcp_packet(TCPFlags::RST); - socket->set_state(TCPSocket::State::Closed); - return; - case TCPSocket::State::Listen: - switch (tcp_packet.flags()) { - case TCPFlags::SYN: { - dbgln_if(TCP_DEBUG, "handle_tcp: incoming connection"); - auto& local_address = ipv4_packet.destination(); - auto& peer_address = ipv4_packet.source(); - auto client_or_error = socket->try_create_client(local_address, tcp_packet.destination_port(), peer_address, tcp_packet.source_port()); - if (client_or_error.is_error()) { - dmesgln("handle_tcp: couldn't create client socket: {}", client_or_error.error()); - return; - } - auto client = client_or_error.release_value(); - MutexLocker locker(client->mutex()); - dbgln_if(TCP_DEBUG, "handle_tcp: created new client socket with tuple {}", client->tuple().to_string()); - client->set_sequence_number(1000); - client->set_ack_number(tcp_packet.sequence_number() + payload_size + 1); - [[maybe_unused]] auto rc2 = client->send_tcp_packet(TCPFlags::SYN | TCPFlags::ACK); - client->set_state(TCPSocket::State::SynReceived); - if (send_window_scale.has_value()) - client->set_send_window_scale(*send_window_scale); - return; - } - default: - dbgln("handle_tcp: unexpected flags in Listen state ({:x})", tcp_packet.flags()); - // socket->send_tcp_packet(TCPFlags::RST); - return; - } - case TCPSocket::State::SynSent: - switch (tcp_packet.flags()) { - case TCPFlags::SYN: - socket->set_ack_number(tcp_packet.sequence_number() + payload_size + 1); - (void)socket->send_tcp_packet(TCPFlags::SYN | TCPFlags::ACK); - socket->set_state(TCPSocket::State::SynReceived); - if (send_window_scale.has_value()) - socket->set_send_window_scale(*send_window_scale); - return; - case TCPFlags::ACK | TCPFlags::SYN: - socket->set_ack_number(tcp_packet.sequence_number() + payload_size + 1); - (void)socket->send_ack(true); - socket->set_state(TCPSocket::State::Established); - socket->set_setup_state(Socket::SetupState::Completed); - socket->set_connected(true); - if (send_window_scale.has_value()) - socket->set_send_window_scale(*send_window_scale); - return; - case TCPFlags::ACK | TCPFlags::FIN: - socket->set_ack_number(tcp_packet.sequence_number() + payload_size + 1); - send_delayed_tcp_ack(*socket); - socket->set_state(TCPSocket::State::Closed); - socket->set_error(TCPSocket::Error::FINDuringConnect); - socket->set_setup_state(Socket::SetupState::Completed); - return; - case TCPFlags::ACK | TCPFlags::RST: - socket->set_state(TCPSocket::State::Closed); - socket->set_error(TCPSocket::Error::RSTDuringConnect); - socket->set_setup_state(Socket::SetupState::Completed); - return; - default: - dbgln("handle_tcp: unexpected flags in SynSent state ({:x})", tcp_packet.flags()); - (void)socket->send_tcp_packet(TCPFlags::RST); - socket->set_state(TCPSocket::State::Closed); - socket->set_error(TCPSocket::Error::UnexpectedFlagsDuringConnect); - socket->set_setup_state(Socket::SetupState::Completed); - return; - } - case TCPSocket::State::SynReceived: - switch (tcp_packet.flags()) { - case TCPFlags::ACK: - socket->set_ack_number(tcp_packet.sequence_number() + payload_size); - - switch (socket->direction()) { - case TCPSocket::Direction::Incoming: - if (!socket->has_originator()) { - dbgln("handle_tcp: connection doesn't have an originating socket; maybe it went away?"); - (void)socket->send_tcp_packet(TCPFlags::RST); - socket->set_state(TCPSocket::State::Closed); - return; - } - - socket->set_state(TCPSocket::State::Established); - socket->set_setup_state(Socket::SetupState::Completed); - socket->release_to_originator(); - return; - case TCPSocket::Direction::Outgoing: - socket->set_state(TCPSocket::State::Established); - socket->set_setup_state(Socket::SetupState::Completed); - socket->set_connected(true); - return; - default: - dbgln("handle_tcp: got ACK in SynReceived state but direction is invalid ({})", TCPSocket::to_string(socket->direction())); - (void)socket->send_tcp_packet(TCPFlags::RST); - socket->set_state(TCPSocket::State::Closed); - return; - } - VERIFY_NOT_REACHED(); - - case TCPFlags::SYN: - dbgln("handle_tcp: ignoring SYN for partially established connection"); - return; - default: - dbgln("handle_tcp: unexpected flags in SynReceived state ({:x})", tcp_packet.flags()); - (void)socket->send_tcp_packet(TCPFlags::RST); - socket->set_state(TCPSocket::State::Closed); - return; - } - case TCPSocket::State::CloseWait: - switch (tcp_packet.flags()) { - default: - dbgln("handle_tcp: unexpected flags in CloseWait state ({:x})", tcp_packet.flags()); - (void)socket->send_tcp_packet(TCPFlags::RST); - socket->set_state(TCPSocket::State::Closed); - return; - } - case TCPSocket::State::LastAck: - switch (tcp_packet.flags()) { - case TCPFlags::ACK: - socket->set_ack_number(tcp_packet.sequence_number() + payload_size); - socket->set_state(TCPSocket::State::Closed); - return; - default: - dbgln("handle_tcp: unexpected flags in LastAck state ({:x})", tcp_packet.flags()); - (void)socket->send_tcp_packet(TCPFlags::RST); - socket->set_state(TCPSocket::State::Closed); - return; - } - case TCPSocket::State::FinWait1: - switch (tcp_packet.flags()) { - case TCPFlags::ACK: - socket->set_ack_number(tcp_packet.sequence_number() + payload_size); - socket->set_state(TCPSocket::State::FinWait2); - return; - case TCPFlags::FIN: - socket->set_ack_number(tcp_packet.sequence_number() + payload_size + 1); - socket->set_state(TCPSocket::State::Closing); - (void)socket->send_ack(true); - return; - case TCPFlags::FIN | TCPFlags::ACK: - socket->set_ack_number(tcp_packet.sequence_number() + payload_size + 1); - socket->set_state(TCPSocket::State::TimeWait); - (void)socket->send_ack(true); - return; - default: - dbgln("handle_tcp: unexpected flags in FinWait1 state ({:x})", tcp_packet.flags()); - (void)socket->send_tcp_packet(TCPFlags::RST); - socket->set_state(TCPSocket::State::Closed); - return; - } - case TCPSocket::State::FinWait2: - switch (tcp_packet.flags()) { - case TCPFlags::FIN | TCPFlags::ACK: // Fallthrough - case TCPFlags::FIN: - socket->set_ack_number(tcp_packet.sequence_number() + payload_size + 1); - socket->set_state(TCPSocket::State::TimeWait); - (void)socket->send_ack(true); - return; - case TCPFlags::ACK | TCPFlags::RST: - // FIXME: Verify that this transition is legitimate. - socket->set_state(TCPSocket::State::Closed); - return; - case TCPFlags::ACK: - if (payload_size) { - if (socket->did_receive(ipv4_packet.source(), tcp_packet.source_port(), { &ipv4_packet, sizeof(IPv4Packet) + ipv4_packet.payload_size() }, packet_timestamp)) { - socket->set_ack_number(tcp_packet.sequence_number() + payload_size + 1); - dbgln_if(TCP_DEBUG, "Got packet with ack_no={}, seq_no={}, payload_size={}, acking it with new ack_no={}, seq_no={}", - tcp_packet.ack_number(), tcp_packet.sequence_number(), payload_size, socket->ack_number(), socket->sequence_number()); - send_delayed_tcp_ack(*socket); - } - } - return; - - default: - dbgln("handle_tcp: unexpected flags in FinWait2 state ({:x})", tcp_packet.flags()); - (void)socket->send_tcp_packet(TCPFlags::RST); - socket->set_state(TCPSocket::State::Closed); - return; - } - case TCPSocket::State::Closing: - switch (tcp_packet.flags()) { - case TCPFlags::ACK: - socket->set_ack_number(tcp_packet.sequence_number() + payload_size); - socket->set_state(TCPSocket::State::TimeWait); - return; - default: - dbgln("handle_tcp: unexpected flags in Closing state ({:x})", tcp_packet.flags()); - (void)socket->send_tcp_packet(TCPFlags::RST); - socket->set_state(TCPSocket::State::Closed); - return; - } - case TCPSocket::State::Established: - if (tcp_packet.has_rst()) { - socket->set_state(TCPSocket::State::Closed); - return; - } - - if (tcp_packet.sequence_number() != socket->ack_number()) { - dbgln_if(TCP_DEBUG, "Discarding out of order packet: seq {} vs. ack {}", tcp_packet.sequence_number(), socket->ack_number()); - if (socket->duplicate_acks() < TCPSocket::maximum_duplicate_acks) { - dbgln_if(TCP_DEBUG, "Sending ACK with same ack number to trigger fast retransmission"); - socket->set_duplicate_acks(socket->duplicate_acks() + 1); - [[maybe_unused]] auto result = socket->send_ack(true); - } - return; - } - - socket->set_duplicate_acks(0); - - if (tcp_packet.has_fin()) { - if (payload_size != 0) - socket->did_receive(ipv4_packet.source(), tcp_packet.source_port(), { &ipv4_packet, sizeof(IPv4Packet) + ipv4_packet.payload_size() }, packet_timestamp); - - socket->set_ack_number(tcp_packet.sequence_number() + payload_size + 1); - send_delayed_tcp_ack(*socket); - socket->set_state(TCPSocket::State::CloseWait); - socket->set_connected(false); - return; - } - - if (payload_size) { - if (socket->did_receive(ipv4_packet.source(), tcp_packet.source_port(), { &ipv4_packet, sizeof(IPv4Packet) + ipv4_packet.payload_size() }, packet_timestamp)) { - socket->set_ack_number(tcp_packet.sequence_number() + payload_size); - dbgln_if(TCP_DEBUG, "Got packet with ack_no={}, seq_no={}, payload_size={}, acking it with new ack_no={}, seq_no={}", - tcp_packet.ack_number(), tcp_packet.sequence_number(), payload_size, socket->ack_number(), socket->sequence_number()); - send_delayed_tcp_ack(*socket); - } - } - } -} - -void retransmit_tcp_packets() -{ - // We must keep the sockets alive until after we've unlocked the hash table - // in case retransmit_packets() realizes that it wants to close the socket. - Vector, 16> sockets; - TCPSocket::sockets_for_retransmit().for_each_shared([&](auto const& socket) { - // We ignore allocation failures above the first 16 guaranteed socket slots, as - // we will just retransmit their packets the next time around - (void)sockets.try_append(socket); - }); - - for (auto& socket : sockets) { - MutexLocker socket_locker(socket->mutex()); - socket->retransmit_packets(); - } -} - -} diff --git a/Kernel/Net/NetworkTask.h b/Kernel/Net/NetworkTask.h deleted file mode 100644 index 821c85b4308..00000000000 --- a/Kernel/Net/NetworkTask.h +++ /dev/null @@ -1,15 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -namespace Kernel { -class NetworkTask { -public: - static void spawn(); - static bool is_current(); -}; -} diff --git a/Kernel/Net/NetworkingManagement.cpp b/Kernel/Net/NetworkingManagement.cpp deleted file mode 100644 index f81a036cdc3..00000000000 --- a/Kernel/Net/NetworkingManagement.cpp +++ /dev/null @@ -1,146 +0,0 @@ -/* - * Copyright (c) 2021, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace Kernel { - -static Singleton s_the; - -NetworkingManagement& NetworkingManagement::the() -{ - return *s_the; -} - -bool NetworkingManagement::is_initialized() -{ - return s_the.is_initialized(); -} - -UNMAP_AFTER_INIT NetworkingManagement::NetworkingManagement() -{ -} - -NonnullRefPtr NetworkingManagement::loopback_adapter() const -{ - return *m_loopback_adapter; -} - -void NetworkingManagement::for_each(Function callback) -{ - m_adapters.for_each([&](auto& adapter) { - callback(adapter); - }); -} - -ErrorOr NetworkingManagement::try_for_each(Function(NetworkAdapter&)> callback) -{ - return m_adapters.with([&](auto& adapters) -> ErrorOr { - for (auto& adapter : adapters) - TRY(callback(adapter)); - return {}; - }); -} - -RefPtr NetworkingManagement::from_ipv4_address(IPv4Address const& address) const -{ - if (address[0] == 0 && address[1] == 0 && address[2] == 0 && address[3] == 0) - return m_loopback_adapter; - if (address[0] == 127) - return m_loopback_adapter; - return m_adapters.with([&](auto& adapters) -> RefPtr { - for (auto& adapter : adapters) { - if (adapter->ipv4_address() == address || adapter->ipv4_broadcast() == address) - return adapter; - } - return nullptr; - }); -} - -RefPtr NetworkingManagement::lookup_by_name(StringView name) const -{ - return m_adapters.with([&](auto& adapters) -> RefPtr { - for (auto& adapter : adapters) { - if (adapter->name() == name) - return adapter; - } - return nullptr; - }); -} - -ErrorOr> NetworkingManagement::generate_interface_name_from_pci_address(PCI::DeviceIdentifier const& device_identifier) -{ - VERIFY(device_identifier.class_code().value() == 0x2); - // Note: This stands for e - "Ethernet", p - "Port" as for PCI bus, "s" for slot as for PCI slot - auto name = TRY(FixedStringBuffer::formatted("ep{}s{}", device_identifier.address().bus(), device_identifier.address().device())); - VERIFY(!NetworkingManagement::the().lookup_by_name(name.representable_view())); - return name; -} - -struct PCINetworkDriverInitializer { - ErrorOr (*probe)(PCI::DeviceIdentifier const&) = nullptr; - ErrorOr> (*create)(PCI::DeviceIdentifier const&) = nullptr; -}; - -static constexpr PCINetworkDriverInitializer s_initializers[] = { - { RTL8168NetworkAdapter::probe, RTL8168NetworkAdapter::create }, - { E1000NetworkAdapter::probe, E1000NetworkAdapter::create }, - { E1000ENetworkAdapter::probe, E1000ENetworkAdapter::create }, - { VirtIONetworkAdapter::probe, VirtIONetworkAdapter::create }, -}; - -UNMAP_AFTER_INIT ErrorOr> NetworkingManagement::determine_network_device(PCI::DeviceIdentifier const& device_identifier) const -{ - for (auto& initializer : s_initializers) { - auto initializer_probe_found_driver_match_or_error = initializer.probe(device_identifier); - if (initializer_probe_found_driver_match_or_error.is_error()) { - dmesgln("Networking: Failed to probe device {}, due to {}", device_identifier.address(), initializer_probe_found_driver_match_or_error.error()); - continue; - } - auto initializer_probe_found_driver_match = initializer_probe_found_driver_match_or_error.release_value(); - if (initializer_probe_found_driver_match) { - auto adapter = TRY(initializer.create(device_identifier)); - TRY(adapter->initialize({})); - return adapter; - } - } - dmesgln("Networking: Failed to initialize device {}, unsupported network adapter", device_identifier.address()); - return Error::from_errno(ENODEV); -} - -bool NetworkingManagement::initialize() -{ - if (!kernel_command_line().is_physical_networking_disabled() && !PCI::Access::is_disabled()) { - MUST(PCI::enumerate([&](PCI::DeviceIdentifier const& device_identifier) { - // Note: PCI class 2 is the class of Network devices - if (device_identifier.class_code().value() != 0x02) - return; - auto result = determine_network_device(device_identifier); - if (result.is_error()) { - dmesgln("Failed to initialize network adapter ({} {}): {}", device_identifier.address(), device_identifier.hardware_id(), result.error()); - return; - } - m_adapters.with([&](auto& adapters) { adapters.append(*result.release_value()); }); - })); - } - auto loopback = MUST(LoopbackAdapter::try_create()); - m_adapters.with([&](auto& adapters) { adapters.append(*loopback); }); - m_loopback_adapter = *loopback; - return true; -} -} diff --git a/Kernel/Net/NetworkingManagement.h b/Kernel/Net/NetworkingManagement.h deleted file mode 100644 index 19cfdc9edaf..00000000000 --- a/Kernel/Net/NetworkingManagement.h +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright (c) 2021, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include -#include -#include -#include - -namespace Kernel { - -class NetworkAdapter; -class NetworkingManagement { - friend class NetworkAdapter; - -public: - static NetworkingManagement& the(); - static bool is_initialized(); - bool initialize(); - - static ErrorOr> generate_interface_name_from_pci_address(PCI::DeviceIdentifier const&); - - NetworkingManagement(); - - void for_each(Function); - ErrorOr try_for_each(Function(NetworkAdapter&)>); - - RefPtr from_ipv4_address(IPv4Address const&) const; - RefPtr lookup_by_name(StringView) const; - - NonnullRefPtr loopback_adapter() const; - -private: - ErrorOr> determine_network_device(PCI::DeviceIdentifier const&) const; - - SpinlockProtected>, LockRank::None> m_adapters {}; - RefPtr m_loopback_adapter; -}; - -} diff --git a/Kernel/Net/Realtek/RTL8168NetworkAdapter.cpp b/Kernel/Net/Realtek/RTL8168NetworkAdapter.cpp deleted file mode 100644 index c8cd5af7051..00000000000 --- a/Kernel/Net/Realtek/RTL8168NetworkAdapter.cpp +++ /dev/null @@ -1,1876 +0,0 @@ -/* - * Copyright (c) 2021, Idan Horowitz - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -namespace Kernel { - -#define REG_MAC 0x00 -#define REG_MAR0 0x08 -#define REG_MAR4 0x0C -#define REG_EEE_LED 0x1B -#define REG_TXADDR 0x20 -#define REG_COMMAND 0x37 -#define REG_TXSTART 0x38 -#define REG_IMR 0x3C -#define REG_ISR 0x3E -#define REG_TXCFG 0x40 -#define REG_RXCFG 0x44 -#define REG_MPC 0x4C -#define REG_CFG9346 0x50 -#define REG_CONFIG1 0x52 -#define REG_CONFIG2 0x53 -#define REG_CONFIG3 0x54 -#define REG_CONFIG4 0x55 -#define REG_CONFIG5 0x56 -#define REG_MULTIINTR 0x5C -#define REG_PHYACCESS 0x60 -#define REG_CSI_DATA 0x64 -#define REG_CSI_ADDR 0x68 -#define REG_PHYSTATUS 0x6C -#define REG_MACDBG 0x6D -#define REG_GPIO 0x6E -#define REG_PMCH 0x6F -#define REG_ERI_DATA 0x70 -#define REG_ERI_ADDR 0x74 -#define REG_EPHYACCESS 0x80 -#define REG_OCP_DATA 0xB0 -#define REG_OCP_ADDR 0xB4 -#define REG_GPHY_OCP 0xB8 -#define REG_DLLPR 0xD0 -#define REG_DBG 0xD1 -#define REG_MCU 0xD3 -#define REG_RMS 0xDA -#define REG_CPLUS_COMMAND 0xE0 -#define REG_INT_MOD 0xE2 -#define REG_RXADDR 0xE4 -#define REG_MTPS 0xEC -#define REG_MISC 0xF0 -#define REG_MISC2 0xF2 -#define REG_IBCR0 0xF8 -#define REG_IBCR2 0xF9 -#define REG_IBISR0 0xFB - -#define COMMAND_TX_ENABLE 0x4 -#define COMMAND_RX_ENABLE 0x8 -#define COMMAND_RESET 0x10 -#define COMMAND_STOP 0x80 - -#define CPLUS_COMMAND_VERIFY_CHECKSUM 0x20 -#define CPLUS_COMMAND_VLAN_STRIP 0x40 -#define CPLUS_COMMAND_MAC_DBGO_SEL 0x1C -#define CPLUS_COMMAND_PACKET_CONTROL_DISABLE 0x80 -#define CPLUS_COMMAND_ASF 0x100 -#define CPLUS_COMMAND_CXPL_DBG_SEL 0x200 -#define CPLUS_COMMAND_FORCE_TXFLOW_ENABLE 0x400 -#define CPLUS_COMMAND_FORCE_RXFLOW_ENABLE 0x800 -#define CPLUS_COMMAND_FORCE_HALF_DUP 0x1000 -#define CPLUS_COMMAND_MAC_DBGO_OE 0x4000 -#define CPLUS_COMMAND_ENABLE_BIST 0x8000 - -#define INT_RXOK 0x1 -#define INT_RXERR 0x2 -#define INT_TXOK 0x4 -#define INT_TXERR 0x8 -#define INT_RX_OVERFLOW 0x10 -#define INT_LINK_CHANGE 0x20 -#define INT_RX_FIFO_OVERFLOW 0x40 -#define INT_SYS_ERR 0x8000 - -#define CFG9346_NONE 0x00 -#define CFG9346_EEM0 0x40 -#define CFG9346_EEM1 0x80 -#define CFG9346_UNLOCK (CFG9346_EEM0 | CFG9346_EEM1) - -#define TXCFG_AUTO_FIFO 0x80 -#define TXCFG_MAX_DMA_UNLIMITED 0x700 -#define TXCFG_EMPTY 0x800 -#define TXCFG_IFG011 0x3000000 - -#define RXCFG_READ_MASK 0x3F - -#define RXCFG_APM 0x2 -#define RXCFG_AM 0x4 -#define RXCFG_AB 0x8 -#define RXCFG_MAX_DMA_UNLIMITED 0x700 -#define RXCFG_EARLY_OFF_V2 0x800 -#define RXCFG_FTH_NONE 0xE000 -#define RXCFG_MULTI_ENABLE 0x4000 -#define RXCFG_128INT_ENABLE 0x8000 - -#define CFG1_SPEED_DOWN 0x10 - -#define CFG2_CLOCK_REQUEST_ENABLE 0x80 - -#define CFG3_BEACON_ENABLE 0x1 -#define CFG3_READY_TO_L23 0x2 - -#define CFG5_ASPM_ENABLE 0x1 -#define CFG5_SPI_ENABLE 0x8 - -#define PHY_LINK_STATUS 0x2 - -#define PHY_FLAG 0x80000000 -#define PHY_REG_BMCR 0x00 -#define PHY_REG_ANAR 0x4 -#define PHY_REG_GBCR 0x9 - -#define CSI_FLAG 0x80000000 -#define CSI_BYTE_ENABLE 0xF000 -#define CSI_FUNC_NIC 0x20000 -#define CSI_FUNC_NIC2 0x10000 - -#define CSI_ACCESS_1 0x17000000 -#define CSI_ACCESS_2 0x27000000 - -#define EPHY_FLAG 0x80000000 - -#define ERI_FLAG 0x80000000 -#define ERI_MASK_0001 0x1000 -#define ERI_MASK_0011 0x3000 -#define ERI_MASK_0100 0x4000 -#define ERI_MASK_0101 0x5000 -#define ERI_MASK_1111 0xF000 - -#define ERI_EXGMAC 0x0 - -#define OCP_FLAG 0x80000000 -#define OCP_STANDARD_PHY_BASE 0xa400 - -#define TXSTART_START 0x40 - -#define BMCR_RESET 0x8000 -#define BMCR_SPEED_0 0x2000 -#define BMCR_AUTO_NEGOTIATE 0x1000 -#define BMCR_RESTART_AUTO_NEGOTIATE 0x200 -#define BMCR_DUPLEX 0x100 -#define BMCR_SPEED_1 0x40 - -#define ADVERTISE_10_HALF 0x20 -#define ADVERTISE_10_FULL 0x40 -#define ADVERTISE_100_HALF 0x80 -#define ADVERTISE_100_FULL 0x100 -#define ADVERTISE_PAUSE_CAP 0x400 -#define ADVERTISE_PAUSE_ASYM 0x800 - -#define ADVERTISE_1000_HALF 0x100 -#define ADVERTISE_1000_FULL 0x200 - -#define DLLPR_PFM_ENABLE 0x40 -#define DLLPR_TX_10M_PS_ENABLE 0x80 - -#define MCU_LINK_LIST_READY 0x2 -#define MCU_RX_EMPTY 0x10 -#define MCU_TX_EMPTY 0x20 -#define MCU_NOW_IS_OOB 0x80 - -#define MTPS_JUMBO 0x3F - -#define MISC_RXDV_GATE_ENABLE 0x80000 -#define MISC_PWM_ENABLE 0x400000 - -#define MISC2_PFM_D3COLD_ENABLE 0x40 - -#define PHYSTATUS_FULLDUP 0x01 -#define PHYSTATUS_1000MF 0x10 -#define PHYSTATUS_100M 0x08 -#define PHYSTATUS_10M 0x04 - -#define GPIO_ENABLE 0x1 - -#define DBG_FIX_NAK_2 0x8 -#define DBG_FIX_NAK_1 0x10 - -#define TX_BUFFER_SIZE 0x1FF8 -#define RX_BUFFER_SIZE 0x1FF8 // FIXME: this should be increased (0x3FFF) - -UNMAP_AFTER_INIT ErrorOr RTL8168NetworkAdapter::probe(PCI::DeviceIdentifier const& pci_device_identifier) -{ - if (pci_device_identifier.hardware_id().vendor_id != PCI::VendorID::Realtek) - return false; - if (pci_device_identifier.hardware_id().device_id != 0x8168) - return false; - return true; -} - -UNMAP_AFTER_INIT ErrorOr> RTL8168NetworkAdapter::create(PCI::DeviceIdentifier const& pci_device_identifier) -{ - u8 irq = pci_device_identifier.interrupt_line().value(); - auto interface_name = TRY(NetworkingManagement::generate_interface_name_from_pci_address(pci_device_identifier)); - auto registers_io_window = TRY(IOWindow::create_for_pci_device_bar(pci_device_identifier, PCI::HeaderType0BaseRegister::BAR0)); - return TRY(adopt_nonnull_ref_or_enomem(new (nothrow) RTL8168NetworkAdapter(interface_name.representable_view(), pci_device_identifier, irq, move(registers_io_window)))); -} - -bool RTL8168NetworkAdapter::determine_supported_version() const -{ - switch (m_version) { - case ChipVersion::Version1: - case ChipVersion::Version2: - case ChipVersion::Version3: - case ChipVersion::Version4: - case ChipVersion::Version5: - case ChipVersion::Version6: - return true; - case ChipVersion::Version7: - case ChipVersion::Version8: - case ChipVersion::Version9: - case ChipVersion::Version10: - case ChipVersion::Version11: - case ChipVersion::Version12: - case ChipVersion::Version13: - case ChipVersion::Version14: - return false; - case ChipVersion::Version15: - return true; - case ChipVersion::Version16: - return false; - case ChipVersion::Version17: - return true; - case ChipVersion::Version18: - case ChipVersion::Version19: - case ChipVersion::Version20: - case ChipVersion::Version21: - case ChipVersion::Version22: - case ChipVersion::Version23: - case ChipVersion::Version24: - case ChipVersion::Version25: - case ChipVersion::Version26: - case ChipVersion::Version27: - case ChipVersion::Version28: - case ChipVersion::Version29: - return false; - case ChipVersion::Version30: - return true; - default: - return false; - } -} - -UNMAP_AFTER_INIT RTL8168NetworkAdapter::RTL8168NetworkAdapter(StringView interface_name, PCI::DeviceIdentifier const& device_identifier, u8 irq, NonnullOwnPtr registers_io_window) - : NetworkAdapter(interface_name) - , PCI::Device(device_identifier) - , IRQHandler(irq) - , m_registers_io_window(move(registers_io_window)) - , m_rx_descriptors_region(MM.allocate_contiguous_kernel_region(Memory::page_round_up(sizeof(TXDescriptor) * (number_of_rx_descriptors + 1)).release_value_but_fixme_should_propagate_errors(), "RTL8168 RX"sv, Memory::Region::Access::ReadWrite).release_value()) - , m_tx_descriptors_region(MM.allocate_contiguous_kernel_region(Memory::page_round_up(sizeof(RXDescriptor) * (number_of_tx_descriptors + 1)).release_value_but_fixme_should_propagate_errors(), "RTL8168 TX"sv, Memory::Region::Access::ReadWrite).release_value()) -{ - dmesgln_pci(*this, "Found @ {}", device_identifier.address()); - dmesgln_pci(*this, "I/O port base: {}", m_registers_io_window); -} - -UNMAP_AFTER_INIT ErrorOr RTL8168NetworkAdapter::initialize(Badge) -{ - identify_chip_version(); - dmesgln_pci(*this, "Version detected - {} ({}{})", possible_device_name(), (u8)m_version, m_version_uncertain ? "?" : ""); - - if (!determine_supported_version()) { - dmesgln_pci(*this, "Aborting initialization! Support for your chip version ({}) is not implemented yet, please open a GH issue and include this message.", (u8)m_version); - return Error::from_errno(ENODEV); // Each ChipVersion requires a specific implementation of configure_phy and hardware_quirks - } - // set initial REG_RXCFG - auto rx_config = RXCFG_MAX_DMA_UNLIMITED; - if (m_version <= ChipVersion::Version3) { - rx_config |= RXCFG_FTH_NONE; - } else if (m_version <= ChipVersion::Version8 || (m_version >= ChipVersion::Version16 && m_version <= ChipVersion::Version19)) { - rx_config |= RXCFG_128INT_ENABLE | RXCFG_MULTI_ENABLE; - } else if (m_version >= ChipVersion::Version21) { - rx_config |= RXCFG_128INT_ENABLE | RXCFG_MULTI_ENABLE | RXCFG_EARLY_OFF_V2; - } else { - rx_config |= RXCFG_128INT_ENABLE; - } - out32(REG_RXCFG, rx_config); - - // disable interrupts - out16(REG_IMR, 0); - - // initialize hardware - if (m_version == ChipVersion::Version23 || m_version == ChipVersion::Version27 || m_version == ChipVersion::Version28) { - // disable CMAC - out8(REG_IBCR2, in8(REG_IBCR2) & ~1); - - while ((in32(REG_IBISR0) & 0x2) != 0) - Processor::wait_check(); - - out8(REG_IBISR0, in8(REG_IBISR0) | 0x20); - out8(REG_IBCR0, in8(REG_IBCR0) & ~1); - } - if (m_version >= ChipVersion::Version21) { - m_ocp_base_address = OCP_STANDARD_PHY_BASE; - - // enable RXDV gate - out32(REG_MISC, in32(REG_MISC) | MISC_RXDV_GATE_ENABLE); - - while ((in32(REG_TXCFG) & TXCFG_EMPTY) == 0) - Processor::wait_check(); - - while ((in32(REG_MCU) & (MCU_RX_EMPTY | MCU_TX_EMPTY)) == 0) - Processor::wait_check(); - - out8(REG_COMMAND, in8(REG_COMMAND) & ~(COMMAND_RX_ENABLE | COMMAND_TX_ENABLE)); - out8(REG_MCU, in8(REG_MCU) & ~MCU_NOW_IS_OOB); - - // vendor magic values ??? - auto data = ocp_in(0xe8de); - data &= ~(1 << 14); - ocp_out(0xe8de, data); - - while ((in32(REG_MCU) & MCU_LINK_LIST_READY) == 0) - Processor::wait_check(); - - // vendor magic values ??? - data = ocp_in(0xe8de); - data |= (1 << 15); - ocp_out(0xe8de, data); - - while ((in32(REG_MCU) & MCU_LINK_LIST_READY) == 0) - Processor::wait_check(); - } - - // clear interrupts - out16(REG_ISR, 0xffff); - - pci_commit(); - - // software reset - reset(); - - enable_bus_mastering(device_identifier()); - - read_mac_address(); - dmesgln_pci(*this, "MAC address: {}", mac_address().to_string()); - - // notify about driver start - if (m_version >= ChipVersion::Version11 && m_version <= ChipVersion::Version13) { - // if check_dash - // notify - TODO(); - } else if (m_version == ChipVersion::Version23 || m_version == ChipVersion::Version27 || m_version == ChipVersion::Version28) { - // if check_dash - // notify - TODO(); - } - - startup(); - return {}; -} - -void RTL8168NetworkAdapter::startup() -{ - // initialize descriptors - initialize_rx_descriptors(); - initialize_tx_descriptors(); - - // register irq - enable_irq(); - - // version specific phy configuration - configure_phy(); - pci_commit(); - - // disable interrupts - out16(REG_IMR, 0); - out16(REG_ISR, 0xffff); - pci_commit(); - - // send stop command - out8(REG_COMMAND, COMMAND_STOP); - - reset(); - - // software reset phy - phy_out(PHY_REG_BMCR, phy_in(PHY_REG_BMCR) | BMCR_RESET); - while ((phy_in(PHY_REG_BMCR) & BMCR_RESET) != 0) - Processor::wait_check(); - - set_phy_speed(); - - // set C+ command - auto cplus_command = in16(REG_CPLUS_COMMAND) | CPLUS_COMMAND_VERIFY_CHECKSUM | CPLUS_COMMAND_VLAN_STRIP; - out16(REG_CPLUS_COMMAND, cplus_command); - in16(REG_CPLUS_COMMAND); // C+ Command barrier - - if (m_version == ChipVersion::Version5 || m_version == ChipVersion::Version6) { - if (in8(REG_MACDBG) & 0x80) - out8(REG_GPIO, in8(REG_GPIO) | GPIO_ENABLE); - else - out8(REG_GPIO, in8(REG_GPIO) & ~GPIO_ENABLE); - } - - // power up phy - if (m_version >= ChipVersion::Version9 && m_version <= ChipVersion::Version15) { - out8(REG_PMCH, in8(REG_PMCH) | 0x80); - } else if (m_version >= ChipVersion::Version26) { - out8(REG_PMCH, in8(REG_PMCH) | 0xC0); - } else if (m_version >= ChipVersion::Version21 && m_version <= ChipVersion::Version23) { - out8(REG_PMCH, in8(REG_PMCH) | 0xC0); - // vendor magic values ??? - eri_update(0x1a8, ERI_MASK_1111, 0xfc000000, 0, ERI_EXGMAC); - } - - // wakeup phy (more vendor magic values) - phy_out(0x1F, 0); - if (m_version <= ChipVersion::Version13) { - phy_out(0x0E, 0); - } - phy_out(PHY_REG_BMCR, BMCR_AUTO_NEGOTIATE); // send known good phy write (acts as a phy barrier) - start_hardware(); - pci_commit(); - - // re-enable interrupts - auto enabled_interrupts = INT_RXOK | INT_RXERR | INT_TXOK | INT_TXERR | INT_RX_OVERFLOW | INT_LINK_CHANGE | INT_SYS_ERR; - if (m_version == ChipVersion::Version1) { - enabled_interrupts |= INT_RX_FIFO_OVERFLOW; - enabled_interrupts &= ~INT_RX_OVERFLOW; - } - out16(REG_IMR, enabled_interrupts); - pci_commit(); - - // update link status - m_link_up = (in8(REG_PHYSTATUS) & PHY_LINK_STATUS) != 0; -} - -void RTL8168NetworkAdapter::configure_phy() -{ - // this method sets a bunch of magic vendor values to the phy configuration registers based on the hardware version - switch (m_version) { - case ChipVersion::Version1: { - configure_phy_b_1(); - return; - } - case ChipVersion::Version2: - case ChipVersion::Version3: { - configure_phy_b_2(); - return; - } - case ChipVersion::Version4: - configure_phy_c_1(); - return; - case ChipVersion::Version5: - configure_phy_c_2(); - return; - case ChipVersion::Version6: - configure_phy_c_3(); - return; - case ChipVersion::Version7: - TODO(); - case ChipVersion::Version8: - TODO(); - case ChipVersion::Version9: - TODO(); - case ChipVersion::Version10: - TODO(); - case ChipVersion::Version11: - TODO(); - case ChipVersion::Version12: - TODO(); - case ChipVersion::Version13: - TODO(); - case ChipVersion::Version14: - TODO(); - case ChipVersion::Version15: { - configure_phy_e_2(); - return; - } - case ChipVersion::Version16: - TODO(); - case ChipVersion::Version17: { - configure_phy_e_2(); - return; - } - case ChipVersion::Version18: - TODO(); - case ChipVersion::Version19: - TODO(); - case ChipVersion::Version20: - TODO(); - case ChipVersion::Version21: - TODO(); - case ChipVersion::Version22: - TODO(); - case ChipVersion::Version23: - TODO(); - case ChipVersion::Version24: - TODO(); - case ChipVersion::Version25: - TODO(); - case ChipVersion::Version26: - TODO(); - case ChipVersion::Version27: - TODO(); - case ChipVersion::Version28: - TODO(); - case ChipVersion::Version29: { - configure_phy_h_1(); - return; - } - case ChipVersion::Version30: { - configure_phy_h_2(); - return; - } - default: - VERIFY_NOT_REACHED(); - } -} - -void RTL8168NetworkAdapter::configure_phy_b_1() -{ - static constexpr auto phy_registers = to_array({ - { 0x10, 0xf41b }, - { 0x1f, 0 }, - }); - - phy_out(0x1f, 0x1); - phy_out(0x16, 1 << 0); - - phy_out_batch(phy_registers); -} - -void RTL8168NetworkAdapter::configure_phy_b_2() -{ - static constexpr auto phy_registers = to_array({ - { 0x1f, 0x1 }, - { 0x10, 0xf41b }, - { 0x1f, 0 }, - }); - - phy_out_batch(phy_registers); -} - -void RTL8168NetworkAdapter::configure_phy_c_1() -{ - static constexpr auto phy_registers = to_array({ - { 0x1f, 0x0001 }, - { 0x12, 0x2300 }, - { 0x1f, 0x0002 }, - { 0x00, 0x88d4 }, - { 0x01, 0x82b1 }, - { 0x03, 0x7002 }, - { 0x08, 0x9e30 }, - { 0x09, 0x01f0 }, - { 0x0a, 0x5500 }, - { 0x0c, 0x00c8 }, - { 0x1f, 0x0003 }, - { 0x12, 0xc096 }, - { 0x16, 0x000a }, - { 0x1f, 0x0000 }, - { 0x1f, 0x0000 }, - { 0x09, 0x2000 }, - { 0x09, 0x0000 }, - }); - phy_out_batch(phy_registers); - - phy_update(0x14, 1 << 5, 0); - phy_update(0x0d, 1 << 5, 0); -} - -void RTL8168NetworkAdapter::configure_phy_c_2() -{ - static constexpr auto phy_registers = to_array({ - { 0x1f, 0x0001 }, - { 0x12, 0x2300 }, - { 0x03, 0x802f }, - { 0x02, 0x4f02 }, - { 0x01, 0x0409 }, - { 0x00, 0xf099 }, - { 0x04, 0x9800 }, - { 0x04, 0x9000 }, - { 0x1d, 0x3d98 }, - { 0x1f, 0x0002 }, - { 0x0c, 0x7eb8 }, - { 0x06, 0x0761 }, - { 0x1f, 0x0003 }, - { 0x16, 0x0f0a }, - { 0x1f, 0x0000 }, - }); - phy_out_batch(phy_registers); - - phy_update(0x16, 0x1, 0); - phy_update(0x14, 1 << 5, 0); - phy_update(0x0d, 1 << 5, 0); -} - -void RTL8168NetworkAdapter::configure_phy_c_3() -{ - static constexpr auto phy_registers = to_array({ - { 0x1f, 0x0001 }, - { 0x12, 0x2300 }, - { 0x1d, 0x3d98 }, - { 0x1f, 0x0002 }, - { 0x0c, 0x7eb8 }, - { 0x06, 0x5461 }, - { 0x1f, 0x0003 }, - { 0x16, 0x0f0a }, - { 0x1f, 0x0000 }, - }); - phy_out_batch(phy_registers); - - phy_update(0x16, 0x1, 0); - phy_update(0x14, 1 << 5, 0); - phy_update(0x0d, 1 << 5, 0); -} - -void RTL8168NetworkAdapter::configure_phy_e_2() -{ - // FIXME: linux's driver writes a firmware blob to the device at this point, is this needed? - - static constexpr auto phy_registers = to_array({ - // Enable delay cap - { 0x1f, 0x4 }, - { 0x1f, 0x7 }, - { 0x1e, 0xac }, - { 0x18, 0x6 }, - { 0x1f, 0x2 }, - { 0x1f, 0 }, - { 0x1f, 0 }, - - // Channel estimation fine tune - { 0x1f, 0x3 }, - { 0x9, 0xa20f }, - { 0x1f, 0 }, - { 0x1f, 0 }, - - // Green Setting - { 0x1f, 0x5 }, - { 0x5, 0x8b5b }, - { 0x6, 0x9222 }, - { 0x5, 0x8b6d }, - { 0x6, 0x8000 }, - { 0x5, 0x8b76 }, - { 0x6, 0x8000 }, - { 0x1f, 0 }, - }); - - phy_out_batch(phy_registers); - - // 4 corner performance improvement - phy_out(0x1f, 0x5); - phy_out(0x5, 0x8b80); - phy_update(0x17, 0x6, 0); - phy_out(0x1f, 0); - - // PHY auto speed down - phy_out(0x1f, 0x4); - phy_out(0x1f, 0x7); - phy_out(0x1e, 0x2d); - phy_update(0x18, 0x10, 0); - phy_out(0x1f, 0x2); - phy_out(0x1f, 0); - phy_update(0x14, 0x8000, 0); - - // Improve 10M EEE waveform - phy_out(0x1f, 0x5); - phy_out(0x5, 0x8b86); - phy_update(0x6, 0x1, 0); - phy_out(0x1f, 0); - - // Improve 2-pair detection performance - phy_out(0x1f, 0x5); - phy_out(0x5, 0x8b85); - phy_update(0x6, 0x4000, 0); - phy_out(0x1f, 0); - - // EEE Setting - eri_update(0x1b0, ERI_MASK_1111, 0, 0x3, ERI_EXGMAC); - phy_out(0x1f, 0x5); - phy_out(0x5, 0x8b85); - phy_update(0x6, 0, 0x2000); - phy_out(0x1f, 0x4); - phy_out(0x1f, 0x7); - phy_out(0x1e, 0x20); - phy_update(0x15, 0, 0x100); - phy_out(0x1f, 0x2); - phy_out(0x1f, 0); - phy_out(0xd, 0x7); - phy_out(0xe, 0x3c); - phy_out(0xd, 0x4007); - phy_out(0xe, 0); - phy_out(0xd, 0); - - // Green feature - phy_out(0x1f, 0x3); - phy_update(0x19, 0, 0x1); - phy_update(0x10, 0, 0x400); - phy_out(0x1f, 0); - - // Broken BIOS workaround: feed GigaMAC registers with MAC address. - rar_exgmac_set(); -} - -void RTL8168NetworkAdapter::configure_phy_h_1() -{ - // FIXME: linux's driver writes a firmware blob to the device at this point, is this needed? - - // CHN EST parameters adjust - giga master - phy_out(0x1f, 0x0a43); - phy_out(0x13, 0x809b); - phy_update(0x14, 0x8000, 0xf800); - phy_out(0x13, 0x80a2); - phy_update(0x14, 0x8000, 0xff00); - phy_out(0x13, 0x80a4); - phy_update(0x14, 0x8500, 0xff00); - phy_out(0x13, 0x809c); - phy_update(0x14, 0xbd00, 0xff00); - phy_out(0x1f, 0); - - // CHN EST parameters adjust - giga slave - phy_out(0x1f, 0x0a43); - phy_out(0x13, 0x80ad); - phy_update(0x14, 0x7000, 0xf800); - phy_out(0x13, 0x80b4); - phy_update(0x14, 0x5000, 0xff00); - phy_out(0x13, 0x80ac); - phy_update(0x14, 0x4000, 0xff00); - phy_out(0x1f, 0); - - // CHN EST parameters adjust - fnet - phy_out(0x1f, 0x0a43); - phy_out(0x13, 0x808e); - phy_update(0x14, 0x1200, 0xff00); - phy_out(0x13, 0x8090); - phy_update(0x14, 0xe500, 0xff00); - phy_out(0x13, 0x8092); - phy_update(0x14, 0x9f00, 0xff00); - phy_out(0x1f, 0); - - // enable R-tune & PGA-retune function - u16 dout_tapbin = 0; - phy_out(0x1f, 0x0a46); - auto data = phy_in(0x13); - data &= 3; - data <<= 2; - dout_tapbin |= data; - data = phy_in(0x12); - data &= 0xc000; - data >>= 14; - dout_tapbin |= data; - dout_tapbin = ~(dout_tapbin ^ 0x8); - dout_tapbin <<= 12; - dout_tapbin &= 0xf000; - phy_out(0x1f, 0x0a43); - phy_out(0x13, 0x827a); - phy_update(0x14, dout_tapbin, 0xf000); - phy_out(0x13, 0x827b); - phy_update(0x14, dout_tapbin, 0xf000); - phy_out(0x13, 0x827c); - phy_update(0x14, dout_tapbin, 0xf000); - phy_out(0x13, 0x827d); - phy_update(0x14, dout_tapbin, 0xf000); - - phy_out(0x1f, 0x0a43); - phy_out(0x13, 0x811); - phy_update(0x14, 0x800, 0); - phy_out(0x1f, 0x0a42); - phy_update(0x16, 0x2, 0); - phy_out(0x1f, 0); - - // enable GPHY 10M - phy_out(0x1f, 0x0a44); - phy_update(0x11, 0x800, 0); - phy_out(0x1f, 0); - - // SAR ADC performance - phy_out(0x1f, 0x0bca); - phy_update(0x17, 0x4000, 0x3000); - phy_out(0x1f, 0); - - phy_out(0x1f, 0x0a43); - phy_out(0x13, 0x803f); - phy_update(0x14, 0, 0x3000); - phy_out(0x13, 0x8047); - phy_update(0x14, 0, 0x3000); - phy_out(0x13, 0x804f); - phy_update(0x14, 0, 0x3000); - phy_out(0x13, 0x8057); - phy_update(0x14, 0, 0x3000); - phy_out(0x13, 0x805f); - phy_update(0x14, 0, 0x3000); - phy_out(0x13, 0x8067); - phy_update(0x14, 0, 0x3000); - phy_out(0x13, 0x806f); - phy_update(0x14, 0, 0x3000); - phy_out(0x1f, 0); - - // disable phy pfm mode - phy_out(0x1f, 0x0a44); - phy_update(0x11, 0, 0x80); - phy_out(0x1f, 0); - - // Check ALDPS bit, disable it if enabled - phy_out(0x1f, 0x0a43); - if (phy_in(0x10) & 0x4) - phy_update(0x10, 0, 0x4); - - phy_out(0x1f, 0); -} - -void RTL8168NetworkAdapter::configure_phy_h_2() -{ - // FIXME: linux's driver writes a firmware blob to the device at this point, is this needed? - - // CHIN EST parameter update - phy_out(0x1f, 0x0a43); - phy_out(0x13, 0x808a); - phy_update(0x14, 0x000a, 0x3f); - phy_out(0x1f, 0); - - // enable R-tune & PGA-retune function - phy_out(0x1f, 0x0a43); - phy_out(0x13, 0x811); - phy_update(0x14, 0x800, 0); - phy_out(0x1f, 0x0a42); - phy_update(0x16, 0x2, 0); - phy_out(0x1f, 0); - - // enable GPHY 10M - phy_out(0x1f, 0x0a44); - phy_update(0x11, 0x800, 0); - phy_out(0x1f, 0); - - ocp_out(0xdd02, 0x807d); - auto data = ocp_in(0xdd02); - u16 ioffset_p3 = ((data & 0x80) >> 7); - ioffset_p3 <<= 3; - - data = ocp_in(0xdd00); - ioffset_p3 |= ((data & (0xe000)) >> 13); - u16 ioffset_p2 = ((data & (0x1e00)) >> 9); - u16 ioffset_p1 = ((data & (0x1e0)) >> 5); - u16 ioffset_p0 = ((data & 0x10) >> 4); - ioffset_p0 <<= 3; - ioffset_p0 |= (data & (0x7)); - data = (ioffset_p3 << 12) | (ioffset_p2 << 8) | (ioffset_p1 << 4) | (ioffset_p0); - - if ((ioffset_p3 != 0x0f) || (ioffset_p2 != 0x0f) || (ioffset_p1 != 0x0f) || (ioffset_p0 != 0x0f)) { - phy_out(0x1f, 0x0bcf); - phy_out(0x16, data); - phy_out(0x1f, 0); - } - - // Modify rlen (TX LPF corner frequency) level - phy_out(0x1f, 0x0bcd); - data = phy_in(0x16); - data &= 0x000f; - u16 rlen = 0; - if (data > 3) - rlen = data - 3; - data = rlen | (rlen << 4) | (rlen << 8) | (rlen << 12); - phy_out(0x17, data); - phy_out(0x1f, 0x0bcd); - phy_out(0x1f, 0); - - // disable phy pfm mode - phy_out(0x1f, 0x0a44); - phy_update(0x11, 0, 0x80); - phy_out(0x1f, 0); - - // Check ALDPS bit, disable it if enabled - phy_out(0x1f, 0x0a43); - if (phy_in(0x10) & 0x4) - phy_update(0x10, 0, 0x4); - - phy_out(0x1f, 0); -} - -void RTL8168NetworkAdapter::rar_exgmac_set() -{ - auto mac = mac_address(); - - auto const w = to_array({ - (u16)(mac[0] | (mac[1] << 8)), - (u16)(mac[2] | (mac[3] << 8)), - (u16)(mac[4] | (mac[5] << 8)), - }); - - auto const exg_mac_registers = to_array({ - { 0xe0, ERI_MASK_1111, (u32)(w[0] | (w[1] << 16)) }, - { 0xe4, ERI_MASK_1111, (u32)w[2] }, - { 0xf0, ERI_MASK_1111, (u32)(w[0] << 16) }, - { 0xf4, ERI_MASK_1111, (u32)(w[1] | (w[2] << 16)) }, - }); - - exgmac_out_batch(exg_mac_registers); -} - -void RTL8168NetworkAdapter::start_hardware() -{ - // unlock config registers - out8(REG_CFG9346, CFG9346_UNLOCK); - - // configure the maximum transmit packet size - out16(REG_MTPS, MTPS_JUMBO); - - // configure the maximum receive packet size - out16(REG_RMS, RX_BUFFER_SIZE); - - auto cplus_command = in16(REG_CPLUS_COMMAND); - cplus_command |= CPLUS_COMMAND_PACKET_CONTROL_DISABLE; - // undocumented magic value??? - cplus_command |= 0x1; - out16(REG_CPLUS_COMMAND, cplus_command); - - // setup interrupt moderation, magic from vendor (Linux Driver uses 0x5151, *BSD Driver uses 0x5100, RTL Driver use 0x5f51???) - out16(REG_INT_MOD, 0x5151); - - // point to tx descriptors - out64(REG_TXADDR, m_tx_descriptors_region->physical_page(0)->paddr().get()); - - // point to rx descriptors - out64(REG_RXADDR, m_rx_descriptors_region->physical_page(0)->paddr().get()); - - // configure tx: use the maximum dma transfer size, default interframe gap time. - out32(REG_TXCFG, TXCFG_IFG011 | TXCFG_MAX_DMA_UNLIMITED); - - // version specific quirks and tweaks - hardware_quirks(); - - in8(REG_IMR); // known good read (acts as a barrier) - - // relock config registers - out8(REG_CFG9346, CFG9346_NONE); - - // enable rx/tx - out8(REG_COMMAND, COMMAND_RX_ENABLE | COMMAND_TX_ENABLE); - pci_commit(); - - // turn on all multicast - out32(REG_MAR0, 0xFFFFFFFF); - out32(REG_MAR4, 0xFFFFFFFF); - - // configure rx mode: accept physical (MAC) match, multicast, and broadcast - out32(REG_RXCFG, (in32(REG_RXCFG) & ~RXCFG_READ_MASK) | RXCFG_APM | RXCFG_AM | RXCFG_AB); - - // disable early-rx interrupts - out16(REG_MULTIINTR, in16(REG_MULTIINTR) & 0xF000); -} - -void RTL8168NetworkAdapter::hardware_quirks() -{ - switch (m_version) { - case ChipVersion::Version1: - hardware_quirks_b_1(); - return; - case ChipVersion::Version2: - case ChipVersion::Version3: - hardware_quirks_b_2(); - return; - case ChipVersion::Version4: - hardware_quirks_c_1(); - return; - case ChipVersion::Version5: - hardware_quirks_c_2(); - return; - case ChipVersion::Version6: - hardware_quirks_c_3(); - return; - case ChipVersion::Version7: - TODO(); - case ChipVersion::Version8: - TODO(); - case ChipVersion::Version9: - TODO(); - case ChipVersion::Version10: - TODO(); - case ChipVersion::Version11: - TODO(); - case ChipVersion::Version12: - TODO(); - case ChipVersion::Version13: - TODO(); - case ChipVersion::Version14: - TODO(); - case ChipVersion::Version15: - return; - case ChipVersion::Version16: - TODO(); - case ChipVersion::Version17: - hardware_quirks_e_2(); - return; - case ChipVersion::Version18: - TODO(); - case ChipVersion::Version19: - TODO(); - case ChipVersion::Version20: - TODO(); - case ChipVersion::Version21: - TODO(); - case ChipVersion::Version22: - TODO(); - case ChipVersion::Version23: - TODO(); - case ChipVersion::Version24: - TODO(); - case ChipVersion::Version25: - TODO(); - case ChipVersion::Version26: - TODO(); - case ChipVersion::Version27: - TODO(); - case ChipVersion::Version28: - TODO(); - case ChipVersion::Version29: - case ChipVersion::Version30: - hardware_quirks_h(); - return; - default: - VERIFY_NOT_REACHED(); - } -} - -void RTL8168NetworkAdapter::hardware_quirks_b_1() -{ - // disable checked reserved bits - out8(REG_CONFIG3, in8(REG_CONFIG3) & ~CFG3_BEACON_ENABLE); - constexpr u16 version1_cplus_quirks = CPLUS_COMMAND_ENABLE_BIST | CPLUS_COMMAND_MAC_DBGO_OE | CPLUS_COMMAND_FORCE_HALF_DUP | CPLUS_COMMAND_FORCE_RXFLOW_ENABLE | CPLUS_COMMAND_FORCE_TXFLOW_ENABLE | CPLUS_COMMAND_CXPL_DBG_SEL | CPLUS_COMMAND_ASF | CPLUS_COMMAND_PACKET_CONTROL_DISABLE | CPLUS_COMMAND_MAC_DBGO_SEL; - out16(REG_CPLUS_COMMAND, in16(REG_CPLUS_COMMAND) & ~version1_cplus_quirks); -} - -void RTL8168NetworkAdapter::hardware_quirks_b_2() -{ - hardware_quirks_b_1(); - - // configure the maximum transmit packet size (again) - out16(REG_MTPS, MTPS_JUMBO); - - // disable checked reserved bits - out8(REG_CONFIG4, in8(REG_CONFIG4) & ~1); -} - -void RTL8168NetworkAdapter::hardware_quirks_c_1() -{ - csi_enable(CSI_ACCESS_2); - - out8(REG_DBG, 0x06 | DBG_FIX_NAK_1 | DBG_FIX_NAK_2); - - static constexpr auto ephy_info = to_array({ - { 0x02, 0x0800, 0x1000 }, - { 0x03, 0, 0x0002 }, - { 0x06, 0x0080, 0x0000 }, - }); - extended_phy_initialize(ephy_info); - - out8(REG_CONFIG1, in8(REG_CONFIG1) | CFG1_SPEED_DOWN); - out8(REG_CONFIG3, in8(REG_CONFIG3) & ~CFG3_BEACON_ENABLE); -} - -void RTL8168NetworkAdapter::hardware_quirks_c_2() -{ - csi_enable(CSI_ACCESS_2); - - static constexpr auto ephy_info = to_array({ - { 0x01, 0, 0x1 }, - { 0x03, 0x0400, 0x0020 }, - }); - extended_phy_initialize(ephy_info); - - out8(REG_CONFIG1, in8(REG_CONFIG1) | CFG1_SPEED_DOWN); - out8(REG_CONFIG3, in8(REG_CONFIG3) | CFG3_BEACON_ENABLE); - - // FIXME: Disable PCIe clock request -} - -void RTL8168NetworkAdapter::hardware_quirks_c_3() -{ - csi_enable(CSI_ACCESS_2); - - out8(REG_CONFIG1, in8(REG_CONFIG1) | CFG1_SPEED_DOWN); - out8(REG_CONFIG3, in8(REG_CONFIG3) & ~CFG3_BEACON_ENABLE); - - // FIXME: Disable PCIe clock request -} - -void RTL8168NetworkAdapter::hardware_quirks_e_2() -{ - static constexpr auto ephy_info = to_array({ - { 0x9, 0, 0x80 }, - { 0x19, 0, 0x224 }, - }); - - csi_enable(CSI_ACCESS_1); - - extended_phy_initialize(ephy_info); - - // FIXME: MTU performance tweak - - eri_out(0xc0, ERI_MASK_0011, 0, ERI_EXGMAC); - eri_out(0xb8, ERI_MASK_0011, 0, ERI_EXGMAC); - eri_out(0xc8, ERI_MASK_1111, 0x100002, ERI_EXGMAC); - eri_out(0xe8, ERI_MASK_1111, 0x100006, ERI_EXGMAC); - eri_out(0xcc, ERI_MASK_1111, 0x50, ERI_EXGMAC); - eri_out(0xd0, ERI_MASK_1111, 0x7ff0060, ERI_EXGMAC); - eri_update(0x1b0, ERI_MASK_0001, 0x10, 0, ERI_EXGMAC); - eri_update(0xd4, ERI_MASK_0011, 0xc00, 0xff00, ERI_EXGMAC); - - // Set early TX - out8(REG_MTPS, 0x27); - - // FIXME: Disable PCIe clock request - - // enable tx auto fifo - out32(REG_TXCFG, in32(REG_TXCFG) | TXCFG_AUTO_FIFO); - - out8(REG_MCU, in8(REG_MCU) & ~MCU_NOW_IS_OOB); - - // Set EEE LED frequency - out8(REG_EEE_LED, in8(REG_EEE_LED) & ~0x7); - - out8(REG_DLLPR, in8(REG_DLLPR) | DLLPR_PFM_ENABLE); - out32(REG_MISC, in32(REG_MISC) | MISC_PWM_ENABLE); - out8(REG_CONFIG5, in8(REG_CONFIG5) & ~CFG5_SPI_ENABLE); -} - -void RTL8168NetworkAdapter::hardware_quirks_h() -{ - // disable aspm and clock request before accessing extended phy - out8(REG_CONFIG2, in8(REG_CONFIG2) & ~CFG2_CLOCK_REQUEST_ENABLE); - out8(REG_CONFIG5, in8(REG_CONFIG5) & ~CFG5_ASPM_ENABLE); - - // initialize extended phy - static constexpr auto ephy_info = to_array({ - { 0x1e, 0x800, 0x1 }, - { 0x1d, 0, 0x800 }, - { 0x5, 0xffff, 0x2089 }, - { 0x6, 0xffff, 0x5881 }, - { 0x4, 0xffff, 0x154a }, - { 0x1, 0xffff, 0x68b }, - }); - extended_phy_initialize(ephy_info); - - // enable tx auto fifo - out32(REG_TXCFG, in32(REG_TXCFG) | TXCFG_AUTO_FIFO); - - // vendor magic values ??? - eri_out(0xC8, ERI_MASK_0101, 0x80002, ERI_EXGMAC); - eri_out(0xCC, ERI_MASK_0001, 0x38, ERI_EXGMAC); - eri_out(0xD0, ERI_MASK_0001, 0x48, ERI_EXGMAC); - eri_out(0xE8, ERI_MASK_1111, 0x100006, ERI_EXGMAC); - - csi_enable(CSI_ACCESS_1); - - // vendor magic values ??? - eri_update(0xDC, ERI_MASK_0001, 0x0, 0x1, ERI_EXGMAC); - eri_update(0xDC, ERI_MASK_0001, 0x1, 0x0, ERI_EXGMAC); - eri_update(0xDC, ERI_MASK_1111, 0x10, 0x0, ERI_EXGMAC); - eri_update(0xD4, ERI_MASK_1111, 0x1F00, 0x0, ERI_EXGMAC); - eri_out(0x5F0, ERI_MASK_0011, 0x4F87, ERI_EXGMAC); - - // disable rxdv gate - out32(REG_MISC, in32(REG_MISC) & ~MISC_RXDV_GATE_ENABLE); - - // set early TX - out8(REG_MTPS, 0x27); - - // vendor magic values ??? - eri_out(0xC0, ERI_MASK_0011, 0, ERI_EXGMAC); - eri_out(0xB8, ERI_MASK_0011, 0, ERI_EXGMAC); - - // Set EEE LED frequency - out8(REG_EEE_LED, in8(REG_EEE_LED) & ~0x7); - - out8(REG_DLLPR, in8(REG_DLLPR) & ~DLLPR_PFM_ENABLE); - out8(REG_MISC2, in8(REG_MISC2) & ~MISC2_PFM_D3COLD_ENABLE); - out8(REG_DLLPR, in8(REG_DLLPR) & ~DLLPR_TX_10M_PS_ENABLE); - - // vendor magic values ??? - eri_update(0x1B0, ERI_MASK_0011, 0, 0x1000, ERI_EXGMAC); - - // disable l2l3 state - out8(REG_CONFIG3, in8(REG_CONFIG3) & ~CFG3_READY_TO_L23); - - // blackmagic code taken from linux's r8169 - phy_out(0x1F, 0x0C42); - auto rg_saw_count = (phy_in(0x13) & 0x3FFF); - phy_out(0x1F, 0); - if (rg_saw_count > 0) { - u16 sw_count_1ms_ini = 16000000 / rg_saw_count; - sw_count_1ms_ini &= 0x0fff; - u32 data = ocp_in(0xd412); - data &= ~0x0fff; - data |= sw_count_1ms_ini; - ocp_out(0xd412, data); - } - - u32 data = ocp_in(0xe056); - data &= ~0xf0; - data |= 0x70; - ocp_out(0xe056, data); - - data = ocp_in(0xe052); - data &= ~0x6000; - data |= 0x8008; - ocp_out(0xe052, data); - - data = ocp_in(0xe0d6); - data &= ~0x1ff; - data |= 0x17f; - ocp_out(0xe0d6, data); - - data = ocp_in(0xd420); - data &= ~0x0fff; - data |= 0x47f; - ocp_out(0xd420, data); - - ocp_out(0xe63e, 0x1); - ocp_out(0xe63e, 0); - ocp_out(0xc094, 0); - ocp_out(0xc09e, 0); -} - -void RTL8168NetworkAdapter::set_phy_speed() -{ - // wakeup phy - phy_out(0x1F, 0); - - // advertise all available features to get best connection possible - auto auto_negotiation_advertisement = phy_in(PHY_REG_ANAR); - auto_negotiation_advertisement |= ADVERTISE_10_HALF; // 10 mbit half duplex - auto_negotiation_advertisement |= ADVERTISE_10_FULL; // 10 mbit full duplex - auto_negotiation_advertisement |= ADVERTISE_100_HALF; // 100 mbit half duplex - auto_negotiation_advertisement |= ADVERTISE_100_FULL; // 100 mbit full duplex - auto_negotiation_advertisement |= ADVERTISE_PAUSE_CAP; // capable of pause flow control - auto_negotiation_advertisement |= ADVERTISE_PAUSE_ASYM; // capable of asymmetric pause flow control - phy_out(PHY_REG_ANAR, auto_negotiation_advertisement); - - auto gigabyte_control = phy_in(PHY_REG_GBCR); - gigabyte_control |= ADVERTISE_1000_HALF; // 1000 mbit half dulpex - gigabyte_control |= ADVERTISE_1000_FULL; // 1000 mbit full duplex - phy_out(PHY_REG_GBCR, gigabyte_control); - - // restart auto-negotiation with set advertisements - phy_out(PHY_REG_BMCR, BMCR_AUTO_NEGOTIATE | BMCR_RESTART_AUTO_NEGOTIATE); -} - -UNMAP_AFTER_INIT void RTL8168NetworkAdapter::initialize_rx_descriptors() -{ - auto* rx_descriptors = (RXDescriptor*)m_rx_descriptors_region->vaddr().as_ptr(); - for (size_t i = 0; i < number_of_rx_descriptors; ++i) { - auto& descriptor = rx_descriptors[i]; - auto region = MM.allocate_contiguous_kernel_region(Memory::page_round_up(RX_BUFFER_SIZE).release_value_but_fixme_should_propagate_errors(), "RTL8168 RX buffer"sv, Memory::Region::Access::ReadWrite).release_value(); - memset(region->vaddr().as_ptr(), 0, region->size()); // MM already zeros out newly allocated pages, but we do it again in case that ever changes - m_rx_buffers_regions.append(move(region)); - - descriptor.buffer_size = RX_BUFFER_SIZE; - descriptor.flags = RXDescriptor::Ownership; // let the NIC know it can use this descriptor - auto physical_address = m_rx_buffers_regions[i]->physical_page(0)->paddr().get(); - descriptor.buffer_address_low = physical_address & 0xFFFFFFFF; - descriptor.buffer_address_high = (u64)physical_address >> 32; // cast to prevent shift count >= with of type warnings in 32 bit systems - } - rx_descriptors[number_of_rx_descriptors - 1].flags = rx_descriptors[number_of_rx_descriptors - 1].flags | RXDescriptor::EndOfRing; -} - -UNMAP_AFTER_INIT void RTL8168NetworkAdapter::initialize_tx_descriptors() -{ - auto* tx_descriptors = (TXDescriptor*)m_tx_descriptors_region->vaddr().as_ptr(); - for (size_t i = 0; i < number_of_tx_descriptors; ++i) { - auto& descriptor = tx_descriptors[i]; - auto region = MM.allocate_contiguous_kernel_region(Memory::page_round_up(TX_BUFFER_SIZE).release_value_but_fixme_should_propagate_errors(), "RTL8168 TX buffer"sv, Memory::Region::Access::ReadWrite).release_value(); - memset(region->vaddr().as_ptr(), 0, region->size()); // MM already zeros out newly allocated pages, but we do it again in case that ever changes - m_tx_buffers_regions.append(move(region)); - - descriptor.flags = TXDescriptor::FirstSegment | TXDescriptor::LastSegment; - auto physical_address = m_tx_buffers_regions[i]->physical_page(0)->paddr().get(); - descriptor.buffer_address_low = physical_address & 0xFFFFFFFF; - descriptor.buffer_address_high = (u64)physical_address >> 32; - } - tx_descriptors[number_of_tx_descriptors - 1].flags = tx_descriptors[number_of_tx_descriptors - 1].flags | TXDescriptor::EndOfRing; -} - -UNMAP_AFTER_INIT RTL8168NetworkAdapter::~RTL8168NetworkAdapter() = default; - -bool RTL8168NetworkAdapter::handle_irq(RegisterState const&) -{ - bool was_handled = false; - for (;;) { - int status = in16(REG_ISR); - out16(REG_ISR, status); - - m_entropy_source.add_random_event(status); - - dbgln_if(RTL8168_DEBUG, "RTL8168: handle_irq status={:#04x}", status); - - if ((status & (INT_RXOK | INT_RXERR | INT_TXOK | INT_TXERR | INT_RX_OVERFLOW | INT_LINK_CHANGE | INT_RX_FIFO_OVERFLOW | INT_SYS_ERR)) == 0) - break; - - was_handled = true; - if (status & INT_RXOK) { - dbgln_if(RTL8168_DEBUG, "RTL8168: RX ready"); - receive(); - } - if (status & INT_RXERR) { - dbgln_if(RTL8168_DEBUG, "RTL8168: RX error - invalid packet"); - } - if (status & INT_TXOK) { - dbgln_if(RTL8168_DEBUG, "RTL8168: TX complete"); - m_wait_queue.wake_one(); - } - if (status & INT_TXERR) { - dbgln_if(RTL8168_DEBUG, "RTL8168: TX error - invalid packet"); - } - if (status & INT_RX_OVERFLOW) { - dmesgln_pci(*this, "RX descriptor unavailable (packet lost)"); - receive(); - } - if (status & INT_LINK_CHANGE) { - m_link_up = (in8(REG_PHYSTATUS) & PHY_LINK_STATUS) != 0; - dmesgln_pci(*this, "Link status changed up={}", m_link_up); - } - if (status & INT_RX_FIFO_OVERFLOW) { - dmesgln_pci(*this, "RX FIFO overflow"); - receive(); - } - if (status & INT_SYS_ERR) { - dmesgln_pci(*this, "Fatal system error"); - } - } - return was_handled; -} - -void RTL8168NetworkAdapter::reset() -{ - out8(REG_COMMAND, COMMAND_RESET); - while ((in8(REG_COMMAND) & COMMAND_RESET) != 0) - Processor::wait_check(); -} - -void RTL8168NetworkAdapter::pci_commit() -{ - // read any register to commit previous PCI write - in8(REG_COMMAND); -} - -UNMAP_AFTER_INIT void RTL8168NetworkAdapter::read_mac_address() -{ - MACAddress mac {}; - for (int i = 0; i < 6; i++) - mac[i] = in8(REG_MAC + i); - set_mac_address(mac); -} - -void RTL8168NetworkAdapter::send_raw(ReadonlyBytes payload) -{ - dbgln_if(RTL8168_DEBUG, "RTL8168: send_raw length={}", payload.size()); - - if (payload.size() > TX_BUFFER_SIZE) { - dmesgln_pci(*this, "Packet was too big; discarding"); - return; - } - - auto* tx_descriptors = (TXDescriptor*)m_tx_descriptors_region->vaddr().as_ptr(); - auto& free_descriptor = tx_descriptors[m_tx_free_index]; - - if ((free_descriptor.flags & TXDescriptor::Ownership) != 0) { - dbgln_if(RTL8168_DEBUG, "RTL8168: No free TX buffers, sleeping until one is available"); - m_wait_queue.wait_forever("RTL8168NetworkAdapter"sv); - return send_raw(payload); - // if we woke up a TX descriptor is guaranteed to be available, so this should never recurse more than once - // but this can probably be done more cleanly - } - - dbgln_if(RTL8168_DEBUG, "RTL8168: Chose descriptor {}", m_tx_free_index); - memcpy(m_tx_buffers_regions[m_tx_free_index]->vaddr().as_ptr(), payload.data(), payload.size()); - - m_tx_free_index = (m_tx_free_index + 1) % number_of_tx_descriptors; - - free_descriptor.frame_length = payload.size() & 0x3FFF; - free_descriptor.flags = free_descriptor.flags | TXDescriptor::Ownership; - - out8(REG_TXSTART, TXSTART_START); // FIXME: this shouldn't be done so often, we should look into doing this using the watchdog timer -} - -void RTL8168NetworkAdapter::receive() -{ - auto* rx_descriptors = (RXDescriptor*)m_rx_descriptors_region->vaddr().as_ptr(); - for (u16 i = 0; i < number_of_rx_descriptors; ++i) { - auto descriptor_index = (m_rx_free_index + i) % number_of_rx_descriptors; - auto& descriptor = rx_descriptors[descriptor_index]; - - if ((descriptor.flags & RXDescriptor::Ownership) != 0) { - m_rx_free_index = descriptor_index; - break; - } - - u16 flags = descriptor.flags; - u16 length = descriptor.buffer_size & 0x3FFF; - - dbgln_if(RTL8168_DEBUG, "RTL8168: receive, flags={:#04x}, length={}, descriptor={}", flags, length, descriptor_index); - - if (length > RX_BUFFER_SIZE || (flags & RXDescriptor::ErrorSummary) != 0) { - dmesgln_pci(*this, "receive got bad packet, flags={:#04x}, length={}", flags, length); - } else if ((flags & RXDescriptor::FirstSegment) != 0 && (flags & RXDescriptor::LastSegment) == 0) { - VERIFY_NOT_REACHED(); - // Our maximum received packet size is smaller than the descriptor buffer size, so packets should never be segmented - // if this happens on a real NIC it might not respect that, and we will have to support packet segmentation - } else { - did_receive({ m_rx_buffers_regions[descriptor_index]->vaddr().as_ptr(), length }); - } - - descriptor.buffer_size = RX_BUFFER_SIZE; - flags = RXDescriptor::Ownership; - if (descriptor_index == number_of_rx_descriptors - 1) - flags |= RXDescriptor::EndOfRing; - descriptor.flags = flags; // let the NIC know it can use this descriptor again - } -} - -void RTL8168NetworkAdapter::out8(u16 address, u8 data) -{ - m_registers_io_window->write8(address, data); -} - -void RTL8168NetworkAdapter::out16(u16 address, u16 data) -{ - m_registers_io_window->write16(address, data); -} - -void RTL8168NetworkAdapter::out32(u16 address, u32 data) -{ - m_registers_io_window->write32(address, data); -} - -void RTL8168NetworkAdapter::out64(u16 address, u64 data) -{ - // ORDER MATTERS: Some NICs require the high part of the address to be written first - m_registers_io_window->write32(address + 4, (u32)(data >> 32)); - m_registers_io_window->write32(address, (u32)(data & 0xFFFFFFFF)); -} - -u8 RTL8168NetworkAdapter::in8(u16 address) -{ - return m_registers_io_window->read8(address); -} - -u16 RTL8168NetworkAdapter::in16(u16 address) -{ - return m_registers_io_window->read16(address); -} - -u32 RTL8168NetworkAdapter::in32(u16 address) -{ - return m_registers_io_window->read32(address); -} - -void RTL8168NetworkAdapter::phy_out(u8 address, u16 data) -{ - if (m_version == ChipVersion::Version11) { - TODO(); - } else if (m_version == ChipVersion::Version12 || m_version == ChipVersion::Version13) { - TODO(); - } else if (m_version >= ChipVersion::Version21) { - if (address == 0x1F) { - m_ocp_base_address = data ? data << 4 : OCP_STANDARD_PHY_BASE; - return; - } - - if (m_ocp_base_address != OCP_STANDARD_PHY_BASE) - address -= 0x10; - - ocp_phy_out(m_ocp_base_address + address * 2, data); - } else { - VERIFY((address & 0xE0) == 0); // register address is only 5 bit - out32(REG_PHYACCESS, PHY_FLAG | (address & 0x1F) << 16 | (data & 0xFFFF)); - while ((in32(REG_PHYACCESS) & PHY_FLAG) != 0) - Processor::wait_check(); - } -} - -u16 RTL8168NetworkAdapter::phy_in(u8 address) -{ - if (m_version == ChipVersion::Version11) { - TODO(); - } else if (m_version == ChipVersion::Version12 || m_version == ChipVersion::Version13) { - TODO(); - } else if (m_version >= ChipVersion::Version21) { - if (m_ocp_base_address != OCP_STANDARD_PHY_BASE) - address -= 0x10; - - return ocp_phy_in(m_ocp_base_address + address * 2); - } else { - VERIFY((address & 0xE0) == 0); // register address is only 5 bit - out32(REG_PHYACCESS, (address & 0x1F) << 16); - while ((in32(REG_PHYACCESS) & PHY_FLAG) == 0) - Processor::wait_check(); - return in32(REG_PHYACCESS) & 0xFFFF; - } -} - -void RTL8168NetworkAdapter::phy_update(u32 address, u32 set, u32 clear) -{ - auto value = phy_in(address); - phy_out(address, (value & ~clear) | set); -} - -void RTL8168NetworkAdapter::phy_out_batch(ReadonlySpan phy_registers) -{ - for (auto const& phy_register : phy_registers) { - phy_out(phy_register.address, phy_register.data); - } -} - -void RTL8168NetworkAdapter::extended_phy_out(u8 address, u16 data) -{ - VERIFY((address & 0xE0) == 0); // register address is only 5 bit - out32(REG_EPHYACCESS, EPHY_FLAG | (address & 0x1F) << 16 | (data & 0xFFFF)); - while ((in32(REG_EPHYACCESS) & EPHY_FLAG) != 0) - Processor::wait_check(); -} - -u16 RTL8168NetworkAdapter::extended_phy_in(u8 address) -{ - VERIFY((address & 0xE0) == 0); // register address is only 5 bit - out32(REG_EPHYACCESS, (address & 0x1F) << 16); - while ((in32(REG_EPHYACCESS) & EPHY_FLAG) == 0) - Processor::wait_check(); - return in32(REG_EPHYACCESS) & 0xFFFF; -} - -void RTL8168NetworkAdapter::extended_phy_initialize(ReadonlySpan ephy_info) -{ - for (auto const& info : ephy_info) { - auto updated_value = (extended_phy_in(info.offset) & ~info.clear) | info.set; - extended_phy_out(info.offset, updated_value); - } -} - -void RTL8168NetworkAdapter::eri_out(u32 address, u32 mask, u32 data, u32 type) -{ - out32(REG_ERI_DATA, data); - out32(REG_ERI_ADDR, ERI_FLAG | type | mask | address); - while ((in32(REG_ERI_ADDR) & ERI_FLAG) != 0) - Processor::wait_check(); -} - -u32 RTL8168NetworkAdapter::eri_in(u32 address, u32 type) -{ - out32(REG_ERI_ADDR, type | ERI_MASK_1111 | address); - while ((in32(REG_ERI_ADDR) & ERI_FLAG) == 0) - Processor::wait_check(); - return in32(REG_ERI_DATA); -} - -void RTL8168NetworkAdapter::eri_update(u32 address, u32 mask, u32 set, u32 clear, u32 type) -{ - auto value = eri_in(address, type); - eri_out(address, mask, (value & ~clear) | set, type); -} - -void RTL8168NetworkAdapter::exgmac_out_batch(ReadonlySpan exgmac_registers) -{ - for (auto const& exgmac_register : exgmac_registers) { - eri_out(exgmac_register.address, exgmac_register.mask, exgmac_register.value, ERI_EXGMAC); - } -} - -void RTL8168NetworkAdapter::csi_out(u32 address, u32 data) -{ - VERIFY(m_version >= ChipVersion::Version4); - out32(REG_CSI_DATA, data); - auto modifier = CSI_BYTE_ENABLE; - if (m_version == ChipVersion::Version20) { - modifier |= CSI_FUNC_NIC; - } else if (m_version == ChipVersion::Version26) { - modifier |= CSI_FUNC_NIC2; - } - out32(REG_CSI_ADDR, CSI_FLAG | (address & 0xFFF) | modifier); - while ((in32(REG_CSI_ADDR) & CSI_FLAG) != 0) - Processor::wait_check(); -} - -u32 RTL8168NetworkAdapter::csi_in(u32 address) -{ - VERIFY(m_version >= ChipVersion::Version4); - auto modifier = CSI_BYTE_ENABLE; - if (m_version == ChipVersion::Version20) { - modifier |= CSI_FUNC_NIC; - } else if (m_version == ChipVersion::Version26) { - modifier |= CSI_FUNC_NIC2; - } - out32(REG_CSI_ADDR, (address & 0xFFF) | modifier); - while ((in32(REG_CSI_ADDR) & CSI_FLAG) == 0) - Processor::wait_check(); - return in32(REG_CSI_DATA) & 0xFFFF; -} - -void RTL8168NetworkAdapter::csi_enable(u32 bits) -{ - auto csi = csi_in(0x70c) & 0x00ffffff; - csi_out(0x70c, csi | bits); -} - -void RTL8168NetworkAdapter::ocp_out(u32 address, u32 data) -{ - VERIFY((address & 0xFFFF0001) == 0); - out32(REG_OCP_DATA, OCP_FLAG | address << 15 | data); -} - -u32 RTL8168NetworkAdapter::ocp_in(u32 address) -{ - VERIFY((address & 0xFFFF0001) == 0); - out32(REG_OCP_DATA, address << 15); - return in32(REG_OCP_DATA); -} - -void RTL8168NetworkAdapter::ocp_phy_out(u32 address, u32 data) -{ - VERIFY((address & 0xFFFF0001) == 0); - out32(REG_GPHY_OCP, OCP_FLAG | (address << 15) | data); - while ((in32(REG_GPHY_OCP) & OCP_FLAG) != 0) - Processor::wait_check(); -} - -u16 RTL8168NetworkAdapter::ocp_phy_in(u32 address) -{ - VERIFY((address & 0xFFFF0001) == 0); - out32(REG_GPHY_OCP, address << 15); - while ((in32(REG_GPHY_OCP) & OCP_FLAG) == 0) - Processor::wait_check(); - return in32(REG_GPHY_OCP) & 0xFFFF; -} - -void RTL8168NetworkAdapter::identify_chip_version() -{ - auto transmit_config = in32(REG_TXCFG); - auto registers = transmit_config & 0x7c800000; - auto hw_version_id = transmit_config & 0x700000; - - m_version_uncertain = false; - - switch (registers) { - case 0x30000000: - m_version = ChipVersion::Version1; - break; - case 0x38000000: - if (hw_version_id == 00000) { - m_version = ChipVersion::Version2; - } else if (hw_version_id == 0x500000) { - m_version = ChipVersion::Version3; - } else { - m_version = ChipVersion::Version3; - m_version_uncertain = true; - } - break; - case 0x3C000000: - if (hw_version_id == 00000) { - m_version = ChipVersion::Version4; - } else if (hw_version_id == 0x200000) { - m_version = ChipVersion::Version5; - } else if (hw_version_id == 0x400000) { - m_version = ChipVersion::Version6; - } else { - m_version = ChipVersion::Version6; - m_version_uncertain = true; - } - break; - case 0x3C800000: - if (hw_version_id == 0x100000) { - m_version = ChipVersion::Version7; - } else if (hw_version_id == 0x300000) { - m_version = ChipVersion::Version8; - } else { - m_version = ChipVersion::Version8; - m_version_uncertain = true; - } - break; - case 0x28000000: - if (hw_version_id == 0x100000) { - m_version = ChipVersion::Version9; - } else if (hw_version_id == 0x300000) { - m_version = ChipVersion::Version10; - } else { - m_version = ChipVersion::Version10; - m_version_uncertain = true; - } - break; - case 0x28800000: - if (hw_version_id == 00000) { - m_version = ChipVersion::Version11; - } else if (hw_version_id == 0x200000) { - m_version = ChipVersion::Version12; - } else if (hw_version_id == 0x300000) { - m_version = ChipVersion::Version13; - } else { - m_version = ChipVersion::Version13; - m_version_uncertain = true; - } - break; - case 0x2C000000: - if (hw_version_id == 0x100000) { - m_version = ChipVersion::Version14; - } else if (hw_version_id == 0x200000) { - m_version = ChipVersion::Version15; - } else { - m_version = ChipVersion::Version15; - m_version_uncertain = true; - } - break; - case 0x2C800000: - if (hw_version_id == 00000) { - m_version = ChipVersion::Version16; - } else if (hw_version_id == 0x100000) { - m_version = ChipVersion::Version17; - } else { - m_version = ChipVersion::Version17; - m_version_uncertain = true; - } - break; - case 0x48000000: - if (hw_version_id == 00000) { - m_version = ChipVersion::Version18; - } else if (hw_version_id == 0x100000) { - m_version = ChipVersion::Version19; - } else { - m_version = ChipVersion::Version19; - m_version_uncertain = true; - } - break; - case 0x48800000: - if (hw_version_id == 00000) { - m_version = ChipVersion::Version20; - } else { - m_version = ChipVersion::Version20; - m_version_uncertain = true; - } - - break; - case 0x4C000000: - if (hw_version_id == 00000) { - m_version = ChipVersion::Version21; - } else if (hw_version_id == 0x100000) { - m_version = ChipVersion::Version22; - } else { - m_version = ChipVersion::Version22; - m_version_uncertain = true; - } - break; - case 0x50000000: - if (hw_version_id == 00000) { - m_version = ChipVersion::Version23; - } else if (hw_version_id == 0x100000) { - m_version = ChipVersion::Version27; - } else if (hw_version_id == 0x200000) { - m_version = ChipVersion::Version28; - } else { - m_version = ChipVersion::Version28; - m_version_uncertain = true; - } - break; - case 0x50800000: - if (hw_version_id == 00000) { - m_version = ChipVersion::Version24; - } else if (hw_version_id == 0x100000) { - m_version = ChipVersion::Version25; - } else { - m_version = ChipVersion::Version25; - m_version_uncertain = true; - } - break; - case 0x5C800000: - if (hw_version_id == 00000) { - m_version = ChipVersion::Version26; - } else { - m_version = ChipVersion::Version26; - m_version_uncertain = true; - } - break; - case 0x54000000: - if (hw_version_id == 00000) { - m_version = ChipVersion::Version29; - } else if (hw_version_id == 0x100000) { - m_version = ChipVersion::Version30; - } else { - m_version = ChipVersion::Version30; - m_version_uncertain = true; - } - break; - default: - dbgln_if(RTL8168_DEBUG, "Unable to determine device version: {:#04x}", registers); - m_version = ChipVersion::Unknown; - m_version_uncertain = true; - break; - } -} - -StringView RTL8168NetworkAdapter::possible_device_name() -{ - switch (m_version) { // We are following *BSD's versioning scheme, the comments note linux's versioning scheme, but they dont match up exactly - case ChipVersion::Version1: - case ChipVersion::Version2: - case ChipVersion::Version3: - return "RTL8168B/8111B"sv; // 11, 12, 17 - case ChipVersion::Version4: - case ChipVersion::Version5: - case ChipVersion::Version6: - return "RTL8168C/8111C"sv; // 19, 20, 21, 22 - case ChipVersion::Version7: - case ChipVersion::Version8: - return "RTL8168CP/8111CP"sv; // 18, 23, 24 - case ChipVersion::Version9: - case ChipVersion::Version10: - return "RTL8168D/8111D"sv; // 25, 26 - case ChipVersion::Version11: - case ChipVersion::Version12: - case ChipVersion::Version13: - return "RTL8168DP/8111DP"sv; // 27, 28, 31 - case ChipVersion::Version14: - case ChipVersion::Version15: - return "RTL8168E/8111E"sv; // 32, 33 - case ChipVersion::Version16: - case ChipVersion::Version17: - return "RTL8168E-VL/8111E-VL"sv; // 34 - case ChipVersion::Version18: - case ChipVersion::Version19: - return "RTL8168F/8111F"sv; // 35, 36 - case ChipVersion::Version20: - return "RTL8411"sv; // 38 - case ChipVersion::Version21: - case ChipVersion::Version22: - return "RTL8168G/8111G"sv; // 40, 41, 42 - case ChipVersion::Version23: - case ChipVersion::Version27: - case ChipVersion::Version28: - return "RTL8168EP/8111EP"sv; // 49, 50, 51 - case ChipVersion::Version24: - case ChipVersion::Version25: - return "RTL8168GU/8111GU"sv; // ??? - case ChipVersion::Version26: - return "RTL8411B"sv; // 44 - case ChipVersion::Version29: - case ChipVersion::Version30: - return "RTL8168H/8111H"sv; // 45, 46 - case ChipVersion::Unknown: - return "Unknown"sv; - } - VERIFY_NOT_REACHED(); -} - -bool RTL8168NetworkAdapter::link_full_duplex() -{ - u8 phystatus = in8(REG_PHYSTATUS); - return !!(phystatus & (PHYSTATUS_FULLDUP | PHYSTATUS_1000MF)); -} - -i32 RTL8168NetworkAdapter::link_speed() -{ - if (!link_up()) - return NetworkAdapter::LINKSPEED_INVALID; - - u8 phystatus = in8(REG_PHYSTATUS); - if (phystatus & PHYSTATUS_1000MF) - return 1000; - if (phystatus & PHYSTATUS_100M) - return 100; - if (phystatus & PHYSTATUS_10M) - return 10; - - return NetworkAdapter::LINKSPEED_INVALID; -} - -} diff --git a/Kernel/Net/Realtek/RTL8168NetworkAdapter.h b/Kernel/Net/Realtek/RTL8168NetworkAdapter.h deleted file mode 100644 index da8f1397ced..00000000000 --- a/Kernel/Net/Realtek/RTL8168NetworkAdapter.h +++ /dev/null @@ -1,224 +0,0 @@ -/* - * Copyright (c) 2021, Idan Horowitz - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include -#include -#include -#include - -namespace Kernel { - -// RTL8618 / RTL8111 Driver based on https://people.freebsd.org/~wpaul/RealTek/RTL8111B_8168B_Registers_DataSheet_1.0.pdf -class RTL8168NetworkAdapter final : public NetworkAdapter - , public PCI::Device - , public IRQHandler { -public: - static ErrorOr probe(PCI::DeviceIdentifier const&); - static ErrorOr> create(PCI::DeviceIdentifier const&); - virtual ErrorOr initialize(Badge) override; - - virtual ~RTL8168NetworkAdapter() override; - - virtual void send_raw(ReadonlyBytes) override; - virtual bool link_up() override { return m_link_up; } - virtual bool link_full_duplex() override; - virtual i32 link_speed() override; - - virtual StringView purpose() const override { return class_name(); } - virtual StringView device_name() const override { return class_name(); } - virtual Type adapter_type() const override { return Type::Ethernet; } - -private: - // FIXME: should this be increased? (maximum allowed here is 1024) - memory usage vs packet loss chance tradeoff - static constexpr size_t number_of_rx_descriptors = 64; - static constexpr size_t number_of_tx_descriptors = 16; - - RTL8168NetworkAdapter(StringView, PCI::DeviceIdentifier const&, u8 irq, NonnullOwnPtr registers_io_window); - - virtual bool handle_irq(RegisterState const&) override; - virtual StringView class_name() const override { return "RTL8168NetworkAdapter"sv; } - - bool determine_supported_version() const; - - struct [[gnu::packed]] TXDescriptor { - u16 volatile frame_length; // top 2 bits are reserved - u16 volatile flags; - u16 volatile vlan_tag; - u16 volatile vlan_flags; - u32 volatile buffer_address_low; - u32 volatile buffer_address_high; - - // flags bit field - static constexpr u16 Ownership = 0x8000u; - static constexpr u16 EndOfRing = 0x4000u; - static constexpr u16 FirstSegment = 0x2000u; - static constexpr u16 LastSegment = 0x1000u; - static constexpr u16 LargeSend = 0x800u; - }; - - static_assert(AssertSize()); - - struct [[gnu::packed]] RXDescriptor { - u16 volatile buffer_size; // top 2 bits are reserved - u16 volatile flags; - u16 volatile vlan_tag; - u16 volatile vlan_flags; - u32 volatile buffer_address_low; - u32 volatile buffer_address_high; - - // flags bit field - static constexpr u16 Ownership = 0x8000u; - static constexpr u16 EndOfRing = 0x4000u; - static constexpr u16 FirstSegment = 0x2000u; - static constexpr u16 LastSegment = 0x1000u; - static constexpr u16 MulticastPacket = 0x800u; - static constexpr u16 PhysicalPacket = 0x400u; - static constexpr u16 BroadcastPacket = 0x200u; - static constexpr u16 WatchdogTimerExpired = 0x40; - static constexpr u16 ErrorSummary = 0x20; - static constexpr u16 RuntPacket = 0x10; - static constexpr u16 CRCError = 0x8; - }; - - static_assert(AssertSize()); - - enum class ChipVersion : u8 { - Unknown = 0, - Version1 = 1, - Version2 = 2, - Version3 = 3, - Version4 = 4, - Version5 = 5, - Version6 = 6, - Version7 = 7, - Version8 = 8, - Version9 = 9, - Version10 = 10, - Version11 = 11, - Version12 = 12, - Version13 = 13, - Version14 = 14, - Version15 = 15, - Version16 = 16, - Version17 = 17, - Version18 = 18, - Version19 = 19, - Version20 = 20, - Version21 = 21, - Version22 = 22, - Version23 = 23, - Version24 = 24, - Version25 = 25, - Version26 = 26, - Version27 = 27, - Version28 = 28, - Version29 = 29, - Version30 = 30 - }; - - void identify_chip_version(); - StringView possible_device_name(); - - void reset(); - void pci_commit(); - void read_mac_address(); - void set_phy_speed(); - void start_hardware(); - void startup(); - - void configure_phy(); - void configure_phy_b_1(); - void configure_phy_b_2(); - void configure_phy_c_1(); - void configure_phy_c_2(); - void configure_phy_c_3(); - void configure_phy_e_2(); - void configure_phy_h_1(); - void configure_phy_h_2(); - - void rar_exgmac_set(); - - void hardware_quirks(); - void hardware_quirks_b_1(); - void hardware_quirks_b_2(); - void hardware_quirks_c_1(); - void hardware_quirks_c_2(); - void hardware_quirks_c_3(); - void hardware_quirks_e_2(); - void hardware_quirks_h(); - - void initialize_rx_descriptors(); - void initialize_tx_descriptors(); - - void receive(); - - void out8(u16 address, u8 data); - void out16(u16 address, u16 data); - void out32(u16 address, u32 data); - void out64(u16 address, u64 data); - u8 in8(u16 address); - u16 in16(u16 address); - u32 in32(u16 address); - - void phy_out(u8 address, u16 data); - u16 phy_in(u8 address); - void phy_update(u32 address, u32 set, u32 clear); - struct PhyRegister { - u16 address; - u16 data; - }; - void phy_out_batch(ReadonlySpan); - - void extended_phy_out(u8 address, u16 data); - u16 extended_phy_in(u8 address); - struct EPhyUpdate { - u32 offset; - u16 clear; - u16 set; - }; - void extended_phy_initialize(ReadonlySpan); - - void eri_out(u32 address, u32 mask, u32 data, u32 type); - u32 eri_in(u32 address, u32 type); - void eri_update(u32 address, u32 mask, u32 set, u32 clear, u32 type); - struct ExgMacRegister { - u16 address; - u16 mask; - u32 value; - }; - void exgmac_out_batch(ReadonlySpan); - - void csi_out(u32 address, u32 data); - u32 csi_in(u32 address); - void csi_enable(u32 bits); - - void ocp_out(u32 address, u32 data); - u32 ocp_in(u32 address); - - void ocp_phy_out(u32 address, u32 data); - u16 ocp_phy_in(u32 address); - - ChipVersion m_version { ChipVersion::Unknown }; - bool m_version_uncertain { true }; - NonnullOwnPtr m_registers_io_window; - u32 m_ocp_base_address { 0 }; - OwnPtr m_rx_descriptors_region; - Vector> m_rx_buffers_regions; - u16 m_rx_free_index { 0 }; - OwnPtr m_tx_descriptors_region; - Vector> m_tx_buffers_regions; - u16 m_tx_free_index { 0 }; - bool m_link_up { false }; - EntropySource m_entropy_source; - WaitQueue m_wait_queue; -}; -} diff --git a/Kernel/Net/Routing.cpp b/Kernel/Net/Routing.cpp deleted file mode 100644 index de07bcce25d..00000000000 --- a/Kernel/Net/Routing.cpp +++ /dev/null @@ -1,348 +0,0 @@ -/* - * Copyright (c) 2018-2021, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace Kernel { - -static Singleton, LockRank::None>> s_arp_table; -static Singleton> s_routing_table; - -class ARPTableBlocker final : public Thread::Blocker { -public: - ARPTableBlocker(IPv4Address ip_addr, Optional& addr); - - virtual StringView state_string() const override { return "Routing (ARP)"sv; } - virtual Type blocker_type() const override { return Type::Routing; } - virtual bool setup_blocker() override; - - virtual void will_unblock_immediately_without_blocking(UnblockImmediatelyReason) override; - - bool unblock_if_matching_ip_address(bool from_add_blocker, IPv4Address const& ip_address, MACAddress const& mac_address) - { - if (m_ip_address != ip_address) - return false; - - { - SpinlockLocker lock(m_lock); - if (m_did_unblock) - return false; - m_did_unblock = true; - m_mac_address = mac_address; - } - - if (!from_add_blocker) - unblock_from_blocker(); - return true; - } - - IPv4Address const& ip_address() const { return m_ip_address; } - -private: - IPv4Address const m_ip_address; - Optional& m_mac_address; - bool m_did_unblock { false }; -}; - -class ARPTableBlockerSet final : public Thread::BlockerSet { -public: - void unblock_blockers_waiting_for_ipv4_address(IPv4Address const& ipv4_address, MACAddress const& mac_address) - { - BlockerSet::unblock_all_blockers_whose_conditions_are_met([&](auto& b, void*, bool&) { - VERIFY(b.blocker_type() == Thread::Blocker::Type::Routing); - auto& blocker = static_cast(b); - return blocker.unblock_if_matching_ip_address(false, ipv4_address, mac_address); - }); - } - -protected: - virtual bool should_add_blocker(Thread::Blocker& b, void*) override - { - VERIFY(b.blocker_type() == Thread::Blocker::Type::Routing); - auto& blocker = static_cast(b); - auto maybe_mac_address = arp_table().with([&](auto const& table) -> auto { - return table.get(blocker.ip_address()); - }); - if (!maybe_mac_address.has_value()) - return true; - return !blocker.unblock_if_matching_ip_address(true, blocker.ip_address(), maybe_mac_address.value()); - } -}; - -static Singleton s_arp_table_blocker_set; - -ARPTableBlocker::ARPTableBlocker(IPv4Address ip_addr, Optional& addr) - : m_ip_address(ip_addr) - , m_mac_address(addr) -{ -} - -bool ARPTableBlocker::setup_blocker() -{ - return add_to_blocker_set(*s_arp_table_blocker_set); -} - -void ARPTableBlocker::will_unblock_immediately_without_blocking(UnblockImmediatelyReason) -{ - auto addr = arp_table().with([&](auto const& table) -> auto { - return table.get(ip_address()); - }); - - SpinlockLocker lock(m_lock); - if (!m_did_unblock) { - m_did_unblock = true; - m_mac_address = move(addr); - } -} - -SpinlockProtected, LockRank::None>& arp_table() -{ - return *s_arp_table; -} - -void update_arp_table(IPv4Address const& ip_addr, MACAddress const& addr, UpdateTable update) -{ - arp_table().with([&](auto& table) { - if (update == UpdateTable::Set) - table.set(ip_addr, addr); - if (update == UpdateTable::Delete) - table.remove(ip_addr); - }); - s_arp_table_blocker_set->unblock_blockers_waiting_for_ipv4_address(ip_addr, addr); - - if constexpr (ARP_DEBUG) { - arp_table().with([&](auto const& table) { - dmesgln("ARP table ({} entries):", table.size()); - for (auto& it : table) - dmesgln("{} :: {}", it.value.to_string(), it.key.to_string()); - }); - } -} - -SpinlockProtected& routing_table() -{ - return *s_routing_table; -} - -ErrorOr update_routing_table(IPv4Address const& destination, IPv4Address const& gateway, IPv4Address const& netmask, u16 flags, RefPtr adapter, UpdateTable update) -{ - dbgln_if(ROUTING_DEBUG, "update_routing_table {} {} {} {} {} {}", destination, gateway, netmask, flags, adapter, update == UpdateTable::Set ? "Set" : "Delete"); - - auto route_entry = adopt_ref_if_nonnull(new (nothrow) Route { destination, gateway, netmask, flags, adapter.release_nonnull() }); - if (!route_entry) - return ENOMEM; - - TRY(routing_table().with([&](auto& table) -> ErrorOr { - if (update == UpdateTable::Set) { - for (auto const& route : table) { - if (route == *route_entry) - return EEXIST; - } - table.append(*route_entry); - } - if (update == UpdateTable::Delete) { - for (auto& route : table) { - dbgln_if(ROUTING_DEBUG, "candidate: {} {} {} {} {}", route.destination, route.gateway, route.netmask, route.flags, route.adapter); - if (route.matches(*route_entry)) { - // FIXME: Remove all entries, not only the first one. - table.remove(route); - return {}; - } - } - return ESRCH; - } - return {}; - })); - - return {}; -} - -bool RoutingDecision::is_zero() const -{ - return adapter.is_null() || next_hop.is_zero(); -} - -static MACAddress multicast_ethernet_address(IPv4Address const& address) -{ - return MACAddress { 0x01, 0x00, 0x5e, (u8)(address[1] & 0x7f), address[2], address[3] }; -} - -RoutingDecision route_to(IPv4Address const& target, IPv4Address const& source, RefPtr const through, AllowBroadcast allow_broadcast, AllowUsingGateway allow_using_gateway) -{ - auto matches = [&](auto& adapter) { - if (!through) - return true; - - return through == adapter; - }; - auto if_matches = [&](auto& adapter, auto const& mac) -> RoutingDecision { - if (!matches(adapter)) - return { nullptr, {} }; - return { adapter, mac }; - }; - - if (target[0] == 0 && target[1] == 0 && target[2] == 0 && target[3] == 0) - return if_matches(*NetworkingManagement::the().loopback_adapter(), NetworkingManagement::the().loopback_adapter()->mac_address()); - if (target[0] == 127) - return if_matches(*NetworkingManagement::the().loopback_adapter(), NetworkingManagement::the().loopback_adapter()->mac_address()); - - auto target_addr = target.to_u32(); - auto source_addr = source.to_u32(); - - RefPtr local_adapter = nullptr; - RefPtr chosen_route = nullptr; - - NetworkingManagement::the().for_each([source_addr, &target_addr, &local_adapter, &matches, &through](NetworkAdapter& adapter) { - auto adapter_addr = adapter.ipv4_address().to_u32(); - auto adapter_mask = adapter.ipv4_netmask().to_u32(); - - if (target_addr == adapter_addr) { - local_adapter = NetworkingManagement::the().loopback_adapter(); - return; - } - - if (!adapter.link_up() || (adapter_addr == 0 && !through)) - return; - - if (source_addr != 0 && source_addr != adapter_addr) - return; - - if ((target_addr & adapter_mask) == (adapter_addr & adapter_mask) && matches(adapter)) - local_adapter = adapter; - }); - - u32 longest_prefix_match = 0; - routing_table().for_each([&target_addr, &matches, &longest_prefix_match, &chosen_route](auto& route) { - auto route_addr = route.destination.to_u32(); - auto route_mask = route.netmask.to_u32(); - - if (route_addr == 0 && matches(*route.adapter)) { - dbgln_if(ROUTING_DEBUG, "Resorting to default route found for adapter: {}", route.adapter->name()); - chosen_route = route; - } - - // We have a direct match and we can exit the routing table earlier. - if (target_addr == route_addr) { - dbgln_if(ROUTING_DEBUG, "Target address has a direct match in the routing table"); - chosen_route = route; - return; - } - - if ((target_addr & route_mask) == (route_addr & route_mask) && (route_addr != 0)) { - auto prefix = (target_addr & (route_addr & route_mask)); - - if (chosen_route && prefix == longest_prefix_match) { - chosen_route = (route.netmask.to_u32() > chosen_route->netmask.to_u32()) ? route : chosen_route; - dbgln_if(ROUTING_DEBUG, "Found a matching prefix match. Using longer netmask: {}", chosen_route->netmask); - } - - if (prefix > longest_prefix_match) { - dbgln_if(ROUTING_DEBUG, "Found a longer prefix match - route: {}, netmask: {}", route.destination.to_string(), route.netmask); - longest_prefix_match = prefix; - chosen_route = route; - } - } - }); - - if (local_adapter && target == local_adapter->ipv4_address()) - return { local_adapter, local_adapter->mac_address() }; - - if (!local_adapter && !chosen_route) { - dbgln_if(ROUTING_DEBUG, "Routing: Couldn't find a suitable adapter for route to {}", target); - return { nullptr, {} }; - } - - RefPtr adapter = nullptr; - IPv4Address next_hop_ip; - - if (local_adapter) { - dbgln_if(ROUTING_DEBUG, "Routing: Got adapter for route (direct): {} ({}/{}) for {}", - local_adapter->name(), - local_adapter->ipv4_address(), - local_adapter->ipv4_netmask(), - target); - - adapter = local_adapter; - next_hop_ip = target; - } else if (chosen_route && allow_using_gateway == AllowUsingGateway::Yes) { - dbgln_if(ROUTING_DEBUG, "Routing: Got adapter for route (using gateway {}): {} ({}/{}) for {}", - chosen_route->gateway, - chosen_route->adapter->name(), - chosen_route->adapter->ipv4_address(), - chosen_route->adapter->ipv4_netmask(), - target); - adapter = chosen_route->adapter; - next_hop_ip = chosen_route->gateway; - } else { - return { nullptr, {} }; - } - - // If it's a broadcast, we already know everything we need to know. - // FIXME: We should also deal with the case where `target_addr` is - // a broadcast to a subnet rather than a full broadcast. - if (target_addr == 0xffffffff && matches(adapter)) { - if (allow_broadcast == AllowBroadcast::Yes) - return { adapter, { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff } }; - return { nullptr, {} }; - } - - if (adapter == NetworkingManagement::the().loopback_adapter()) - return { adapter, adapter->mac_address() }; - - if ((target_addr & (IPv4Address { 240, 0, 0, 0 }.to_u32())) == IPv4Address { 224, 0, 0, 0 }.to_u32()) - return { adapter, multicast_ethernet_address(target) }; - - { - auto addr = arp_table().with([&](auto const& table) -> auto { - return table.get(next_hop_ip); - }); - if (addr.has_value()) { - dbgln_if(ARP_DEBUG, "Routing: Using cached ARP entry for {} ({})", next_hop_ip, addr.value().to_string()); - return { adapter, addr.value() }; - } - } - - dbgln_if(ARP_DEBUG, "Routing: Sending ARP request via adapter {} for IPv4 address {}", adapter->name(), next_hop_ip); - - ARPPacket request; - request.set_operation(ARPOperation::Request); - request.set_target_hardware_address({ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }); - request.set_target_protocol_address(next_hop_ip); - request.set_sender_hardware_address(adapter->mac_address()); - request.set_sender_protocol_address(adapter->ipv4_address()); - adapter->send({ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }, request); - - if (NetworkTask::is_current()) { - // FIXME: Waiting for the ARP response from inside the NetworkTask would - // deadlock, so let's hope that whoever called route_to() tries again in a bit. - dbgln_if(ARP_DEBUG, "Routing: Not waiting for ARP response from inside NetworkTask, sent ARP request using adapter {} for {}", adapter->name(), target); - return { nullptr, {} }; - } - - Optional addr; - if (!Thread::current()->block({}, next_hop_ip, addr).was_interrupted()) { - if (addr.has_value()) { - dbgln_if(ARP_DEBUG, "Routing: Got ARP response using adapter {} for {} ({})", - adapter->name(), - next_hop_ip, - addr.value().to_string()); - return { adapter, addr.value() }; - } - } - - dbgln_if(ROUTING_DEBUG, "Routing: Couldn't find route using adapter {} for {}", adapter->name(), target); - return { nullptr, {} }; -} - -} diff --git a/Kernel/Net/Routing.h b/Kernel/Net/Routing.h deleted file mode 100644 index 001f2fc6470..00000000000 --- a/Kernel/Net/Routing.h +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include - -namespace Kernel { - -struct Route final : public AtomicRefCounted { - Route(IPv4Address const& destination, IPv4Address const& gateway, IPv4Address const& netmask, u16 flags, NonnullRefPtr adapter) - : destination(destination) - , gateway(gateway) - , netmask(netmask) - , flags(flags) - , adapter(adapter) - { - } - - bool operator==(Route const& other) const - { - return destination == other.destination && netmask == other.netmask && flags == other.flags && adapter.ptr() == other.adapter.ptr(); - } - - bool matches(Route const& other) const - { - return destination == other.destination && (gateway == other.gateway || other.gateway.is_zero()) && netmask == other.netmask && flags == other.flags && adapter.ptr() == other.adapter.ptr(); - } - - IPv4Address const destination; - IPv4Address const gateway; - IPv4Address const netmask; - u16 const flags; - NonnullRefPtr const adapter; - - IntrusiveListNode> route_list_node {}; - using RouteList = IntrusiveList<&Route::route_list_node>; -}; - -struct RoutingDecision { - RefPtr adapter; - MACAddress next_hop; - - bool is_zero() const; -}; - -enum class UpdateTable { - Set, - Delete, -}; - -void update_arp_table(IPv4Address const&, MACAddress const&, UpdateTable update); -ErrorOr update_routing_table(IPv4Address const& destination, IPv4Address const& gateway, IPv4Address const& netmask, u16 flags, RefPtr const adapter, UpdateTable update); - -enum class AllowUsingGateway { - Yes, - No, -}; - -enum class AllowBroadcast { - Yes, - No, -}; - -RoutingDecision route_to(IPv4Address const& target, IPv4Address const& source, RefPtr const through = nullptr, AllowBroadcast = AllowBroadcast::No, AllowUsingGateway = AllowUsingGateway::Yes); - -SpinlockProtected, LockRank::None>& arp_table(); -SpinlockProtected& routing_table(); - -} diff --git a/Kernel/Net/Socket.cpp b/Kernel/Net/Socket.cpp deleted file mode 100644 index d9ba096c4f1..00000000000 --- a/Kernel/Net/Socket.cpp +++ /dev/null @@ -1,327 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace Kernel { - -ErrorOr> Socket::create(int domain, int type, int protocol) -{ - switch (domain) { - case AF_LOCAL: - return TRY(LocalSocket::try_create(type & SOCK_TYPE_MASK)); - case AF_INET: - return IPv4Socket::create(type & SOCK_TYPE_MASK, protocol); - default: - return EAFNOSUPPORT; - } -} - -Socket::Socket(int domain, int type, int protocol) - : m_domain(domain) - , m_type(type) - , m_protocol(protocol) -{ - set_origin(Process::current()); -} - -Socket::~Socket() = default; - -void Socket::set_setup_state(SetupState new_setup_state) -{ - dbgln_if(SOCKET_DEBUG, "Socket({}) setup state moving from {} to {}", this, to_string(m_setup_state), to_string(new_setup_state)); - m_setup_state = new_setup_state; - evaluate_block_conditions(); -} - -RefPtr Socket::accept() -{ - MutexLocker locker(mutex()); - if (m_pending.is_empty()) - return nullptr; - dbgln_if(SOCKET_DEBUG, "Socket({}) de-queueing connection", this); - auto client = m_pending.take_first(); - VERIFY(!client->is_connected()); - auto& process = Process::current(); - client->set_acceptor(process); - client->m_connected = true; - client->set_role(Role::Accepted); - if (!m_pending.is_empty()) - evaluate_block_conditions(); - return client; -} - -ErrorOr Socket::queue_connection_from(NonnullRefPtr peer) -{ - dbgln_if(SOCKET_DEBUG, "Socket({}) queueing connection", this); - MutexLocker locker(mutex()); - if (m_pending.size() >= m_backlog) - return set_so_error(ECONNREFUSED); - SOCKET_TRY(m_pending.try_append(move(peer))); - evaluate_block_conditions(); - return {}; -} - -ErrorOr Socket::setsockopt(int level, int option, Userspace user_value, socklen_t user_value_size) -{ - MutexLocker locker(mutex()); - - if (level != SOL_SOCKET) - return ENOPROTOOPT; - VERIFY(level == SOL_SOCKET); - switch (option) { - case SO_SNDTIMEO: - if (user_value_size != sizeof(timeval)) - return EINVAL; - m_send_timeout = TRY(copy_time_from_user(static_ptr_cast(user_value))); - return {}; - case SO_RCVTIMEO: - if (user_value_size != sizeof(timeval)) - return EINVAL; - m_receive_timeout = TRY(copy_time_from_user(static_ptr_cast(user_value))); - return {}; - case SO_BINDTODEVICE: { - if (user_value_size != IFNAMSIZ) - return EINVAL; - auto user_string = static_ptr_cast(user_value); - auto ifname = TRY(Process::get_syscall_name_string_fixed_buffer(user_string, user_value_size)); - auto device = NetworkingManagement::the().lookup_by_name(ifname.representable_view()); - if (!device) - return ENODEV; - m_bound_interface.with([&device](auto& bound_device) { - bound_device = move(device); - }); - return {}; - } - case SO_DEBUG: - // NOTE: This is supposed to toggle collection of debugging information on/off, we don't have any right now, so this is a no-op. - return {}; - case SO_KEEPALIVE: - // FIXME: Obviously, this is not a real keepalive. - return {}; - case SO_TIMESTAMP: - if (user_value_size != sizeof(int)) - return EINVAL; - m_timestamp = TRY(copy_typed_from_user(static_ptr_cast(user_value))); - if (m_timestamp != 0 && (domain() != AF_INET || type() == SOCK_STREAM)) { - // FIXME: Support SO_TIMESTAMP for more protocols? - m_timestamp = 0; - return ENOTSUP; - } - return {}; - case SO_DONTROUTE: { - if (user_value_size != sizeof(int)) - return EINVAL; - m_routing_disabled = TRY(copy_typed_from_user(static_ptr_cast(user_value))) != 0; - return {}; - } - case SO_REUSEADDR: - dbgln("FIXME: SO_REUSEADDR requested, but not implemented."); - return {}; - case SO_BROADCAST: { - if (user_value_size != sizeof(int)) - return EINVAL; - m_broadcast_allowed = TRY(copy_typed_from_user(static_ptr_cast(user_value))) != 0; - return {}; - } - default: - dbgln("setsockopt({}) at SOL_SOCKET not implemented.", option); - return ENOPROTOOPT; - } -} - -ErrorOr Socket::getsockopt(OpenFileDescription&, int level, int option, Userspace value, Userspace value_size) -{ - MutexLocker locker(mutex()); - - socklen_t size; - TRY(copy_from_user(&size, value_size.unsafe_userspace_ptr())); - - // FIXME: Add TCP_NODELAY, IPPROTO_TCP and IPPROTO_IP (used in OpenSSH) - if (level != SOL_SOCKET) { - // Not sure if this is the correct error code, but it's only temporary until other levels are implemented. - return ENOPROTOOPT; - } - - switch (option) { - case SO_SNDTIMEO: - if (size < sizeof(timeval)) - return EINVAL; - { - timeval tv = m_send_timeout.to_timeval(); - TRY(copy_to_user(static_ptr_cast(value), &tv)); - } - size = sizeof(timeval); - return copy_to_user(value_size, &size); - case SO_RCVTIMEO: - if (size < sizeof(timeval)) - return EINVAL; - { - timeval tv = m_receive_timeout.to_timeval(); - TRY(copy_to_user(static_ptr_cast(value), &tv)); - } - size = sizeof(timeval); - return copy_to_user(value_size, &size); - case SO_ERROR: { - if (size < sizeof(int)) - return EINVAL; - return so_error().with([&size, value, value_size](auto& error) -> ErrorOr { - int errno = 0; - if (error.has_value()) - errno = error.value(); - TRY(copy_to_user(static_ptr_cast(value), &errno)); - size = sizeof(int); - TRY(copy_to_user(value_size, &size)); - error = {}; - return {}; - }); - } - case SO_BINDTODEVICE: - if (size < IFNAMSIZ) - return EINVAL; - return m_bound_interface.with([&](auto& bound_device) -> ErrorOr { - if (bound_device) { - auto name = bound_device->name(); - auto length = name.length() + 1; - auto characters = name.characters_without_null_termination(); - TRY(copy_to_user(static_ptr_cast(value), characters, length)); - size = length; - return copy_to_user(value_size, &size); - } else { - size = 0; - TRY(copy_to_user(value_size, &size)); - // FIXME: This return value looks suspicious. - return EFAULT; - } - }); - case SO_TIMESTAMP: - if (size < sizeof(int)) - return EINVAL; - TRY(copy_to_user(static_ptr_cast(value), &m_timestamp)); - size = sizeof(int); - return copy_to_user(value_size, &size); - case SO_TYPE: - if (size < sizeof(int)) - return EINVAL; - TRY(copy_to_user(static_ptr_cast(value), &m_type)); - size = sizeof(int); - return copy_to_user(value_size, &size); - case SO_DEBUG: - // NOTE: This is supposed to toggle collection of debugging information on/off, we don't have any right now, so we just claim it's always off. - if (size < sizeof(int)) - return EINVAL; - TRY(memset_user(value.unsafe_userspace_ptr(), 0, sizeof(int))); - size = sizeof(int); - return copy_to_user(value_size, &size); - case SO_ACCEPTCONN: { - int accepting_connections = (m_role == Role::Listener) ? 1 : 0; - if (size < sizeof(accepting_connections)) - return EINVAL; - TRY(copy_to_user(static_ptr_cast(value), &accepting_connections)); - size = sizeof(accepting_connections); - return copy_to_user(value_size, &size); - } - case SO_DONTROUTE: { - int routing_disabled = m_routing_disabled ? 1 : 0; - if (size < sizeof(routing_disabled)) - return EINVAL; - TRY(copy_to_user(static_ptr_cast(value), &routing_disabled)); - size = sizeof(routing_disabled); - return copy_to_user(value_size, &size); - } - case SO_REUSEADDR: { - int reuse_address = 0; - if (size < sizeof(reuse_address)) - return EINVAL; - TRY(copy_to_user(static_ptr_cast(value), &reuse_address)); - size = sizeof(reuse_address); - return copy_to_user(value_size, &size); - } - case SO_BROADCAST: { - int broadcast_allowed = m_broadcast_allowed ? 1 : 0; - if (size < sizeof(broadcast_allowed)) - return EINVAL; - TRY(copy_to_user(static_ptr_cast(value), &broadcast_allowed)); - size = sizeof(broadcast_allowed); - return copy_to_user(value_size, &size); - } - default: - dbgln("getsockopt({}) at SOL_SOCKET not implemented.", option); - return ENOPROTOOPT; - } -} - -ErrorOr Socket::read(OpenFileDescription& description, u64, UserOrKernelBuffer& buffer, size_t size) -{ - if (is_shut_down_for_reading()) - return 0; - UnixDateTime t {}; - return recvfrom(description, buffer, size, 0, {}, 0, t, description.is_blocking()); -} - -ErrorOr Socket::write(OpenFileDescription& description, u64, UserOrKernelBuffer const& data, size_t size) -{ - if (is_shut_down_for_writing()) - return set_so_error(EPIPE); - return sendto(description, data, size, 0, {}, 0); -} - -ErrorOr Socket::shutdown(int how) -{ - MutexLocker locker(mutex()); - if (type() == SOCK_STREAM && !is_connected()) - return set_so_error(ENOTCONN); - if (m_role == Role::Listener) - return set_so_error(ENOTCONN); - if (!m_shut_down_for_writing && (how == SHUT_WR || how == SHUT_RDWR)) { - shut_down_for_writing(); - m_shut_down_for_writing = true; - } - if (!m_shut_down_for_reading && (how == SHUT_RD || how == SHUT_RDWR)) { - shut_down_for_reading(); - m_shut_down_for_reading = true; - } - return {}; -} - -ErrorOr Socket::stat() const -{ - struct stat st = {}; - st.st_mode = S_IFSOCK; - return st; -} - -void Socket::set_connected(bool connected) -{ - MutexLocker locker(mutex()); - if (m_connected == connected) - return; - m_connected = connected; - evaluate_block_conditions(); -} - -void Socket::set_origin(Process const& process) -{ - auto credentials = process.credentials(); - m_origin = { process.pid().value(), credentials->uid().value(), credentials->gid().value() }; -} - -void Socket::set_acceptor(Process const& process) -{ - auto credentials = process.credentials(); - m_acceptor = { process.pid().value(), credentials->uid().value(), credentials->gid().value() }; -} - -} diff --git a/Kernel/Net/Socket.h b/Kernel/Net/Socket.h deleted file mode 100644 index 01c154c221a..00000000000 --- a/Kernel/Net/Socket.h +++ /dev/null @@ -1,197 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include -#include -#include - -namespace Kernel { - -class OpenFileDescription; - -class Socket : public File { -public: - static ErrorOr> create(int domain, int type, int protocol); - virtual ~Socket() override; - - int domain() const { return m_domain; } - int type() const { return m_type; } - int protocol() const { return m_protocol; } - - bool is_shut_down_for_writing() const { return m_shut_down_for_writing; } - bool is_shut_down_for_reading() const { return m_shut_down_for_reading; } - - enum class SetupState { - Unstarted, // we haven't tried to set the socket up yet - InProgress, // we're in the process of setting things up - for TCP maybe we've sent a SYN packet - Completed, // the setup process is complete, but not necessarily successful - }; - - enum class Role : u8 { - None, - Listener, - Accepted, - Connected, - Connecting - }; - - static StringView to_string(SetupState setup_state) - { - switch (setup_state) { - case SetupState::Unstarted: - return "Unstarted"sv; - case SetupState::InProgress: - return "InProgress"sv; - case SetupState::Completed: - return "Completed"sv; - default: - return "None"sv; - } - } - - SetupState setup_state() const { return m_setup_state; } - void set_setup_state(SetupState setup_state); - - virtual Role role(OpenFileDescription const&) const { return m_role; } - - bool is_connected() const { return m_connected; } - void set_connected(bool); - - bool can_accept() const { return !m_pending.is_empty(); } - RefPtr accept(); - - ErrorOr shutdown(int how); - - virtual ErrorOr bind(Credentials const&, Userspace, socklen_t) = 0; - virtual ErrorOr connect(Credentials const&, OpenFileDescription&, Userspace, socklen_t) = 0; - virtual ErrorOr listen(size_t) = 0; - virtual void get_local_address(sockaddr*, socklen_t*) = 0; - virtual void get_peer_address(sockaddr*, socklen_t*) = 0; - virtual bool is_local() const { return false; } - virtual bool is_ipv4() const { return false; } - virtual ErrorOr sendto(OpenFileDescription&, UserOrKernelBuffer const&, size_t, int flags, Userspace, socklen_t) = 0; - virtual ErrorOr recvfrom(OpenFileDescription&, UserOrKernelBuffer&, size_t, int flags, Userspace, Userspace, UnixDateTime&, bool blocking) = 0; - - virtual ErrorOr setsockopt(int level, int option, Userspace, socklen_t); - virtual ErrorOr getsockopt(OpenFileDescription&, int level, int option, Userspace, Userspace); - - ProcessID origin_pid() const { return m_origin.pid; } - UserID origin_uid() const { return m_origin.uid; } - GroupID origin_gid() const { return m_origin.gid; } - ProcessID acceptor_pid() const { return m_acceptor.pid; } - UserID acceptor_uid() const { return m_acceptor.uid; } - GroupID acceptor_gid() const { return m_acceptor.gid; } - SpinlockProtected, LockRank::None> const& bound_interface() const { return m_bound_interface; } - - Mutex& mutex() { return m_mutex; } - - // ^File - virtual ErrorOr read(OpenFileDescription&, u64, UserOrKernelBuffer&, size_t) override final; - virtual ErrorOr write(OpenFileDescription&, u64, UserOrKernelBuffer const&, size_t) override final; - virtual ErrorOr stat() const override; - virtual ErrorOr> pseudo_path(OpenFileDescription const&) const override = 0; - - bool has_receive_timeout() const { return m_receive_timeout != Duration::zero(); } - Duration const& receive_timeout() const { return m_receive_timeout; } - - bool has_send_timeout() const { return m_send_timeout != Duration::zero(); } - Duration const& send_timeout() const { return m_send_timeout; } - - bool wants_timestamp() const { return m_timestamp; } - -protected: - Socket(int domain, int type, int protocol); - - ErrorOr queue_connection_from(NonnullRefPtr); - - size_t backlog() const { return m_backlog; } - void set_backlog(size_t backlog) { m_backlog = backlog; } - - virtual StringView class_name() const override { return "Socket"sv; } - - virtual void shut_down_for_reading() { } - virtual void shut_down_for_writing() { } - - Role m_role { Role::None }; - - SpinlockProtected, LockRank::None>& so_error() { return m_so_error; } - - Error set_so_error(ErrnoCode error_code) - { - m_so_error.with([&error_code](auto& so_error) { - so_error = error_code; - }); - return Error::from_errno(error_code); - } - - Error set_so_error(Error error) - { - m_so_error.with([&error](auto& so_error) { - so_error = static_cast(error.code()); - }); - return error; - } - - void clear_so_error() - { - m_so_error.with([](auto& so_error) { - so_error = {}; - }); - } - - void set_origin(Process const&); - void set_acceptor(Process const&); - - void set_role(Role role) { m_role = role; } - - ucred m_origin { 0, 0, 0 }; - ucred m_acceptor { 0, 0, 0 }; - bool m_routing_disabled { false }; - bool m_broadcast_allowed { false }; - -private: - virtual bool is_socket() const final { return true; } - - Mutex m_mutex { "Socket"sv }; - - int m_domain { 0 }; - int m_type { 0 }; - int m_protocol { 0 }; - size_t m_backlog { 0 }; - SetupState m_setup_state { SetupState::Unstarted }; - bool m_connected { false }; - bool m_shut_down_for_reading { false }; - bool m_shut_down_for_writing { false }; - - SpinlockProtected, LockRank::None> m_bound_interface; - - Duration m_receive_timeout {}; - Duration m_send_timeout {}; - int m_timestamp { 0 }; - - SpinlockProtected, LockRank::None> m_so_error; - - Vector> m_pending; -}; - -// This is a special variant of TRY() that also updates the socket's SO_ERROR field on error. -#define SOCKET_TRY(expression) \ - ({ \ - auto&& result = (expression); \ - if (result.is_error()) \ - return set_so_error(result.release_error()); \ - static_assert(!::AK::Detail::IsLvalueReference, \ - "Do not return a reference from a fallible expression"); \ - result.release_value(); \ - }) - -} diff --git a/Kernel/Net/TCP.h b/Kernel/Net/TCP.h deleted file mode 100644 index 646a779f248..00000000000 --- a/Kernel/Net/TCP.h +++ /dev/null @@ -1,157 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include - -namespace Kernel { - -struct TCPFlags { - enum : u16 { - FIN = 0x01, - SYN = 0x02, - RST = 0x04, - PSH = 0x08, - ACK = 0x10, - URG = 0x20 - }; -}; - -enum class TCPOptionKind : u8 { - End = 0, - Nop = 1, - MSS = 2, - WindowScale = 3, - SACKPermitted = 4, - SACK = 5, - Timestamp = 6, -}; - -class [[gnu::packed]] TCPOption { -public: - TCPOptionKind kind() const { return m_kind; } - u8 length() const { return m_length; } - -protected: - TCPOption(TCPOptionKind kind, u8 length) - : m_kind(kind) - , m_length(length) {}; - -private: - TCPOptionKind m_kind { TCPOptionKind::End }; - u8 m_length { sizeof(TCPOption) }; -}; - -class [[gnu::packed]] TCPOptionMSS : public TCPOption { -public: - TCPOptionMSS(u16 value) - : TCPOption(TCPOptionKind::MSS, sizeof(TCPOptionMSS)) - , m_value(value) - { - } - - u16 value() const { return m_value; } - -private: - NetworkOrdered m_value; -}; - -class [[gnu::packed]] TCPOptionWindowScale : public TCPOption { -public: - TCPOptionWindowScale(u8 value) - : TCPOption(TCPOptionKind::WindowScale, sizeof(TCPOptionWindowScale)) - , m_value(value) - { - } - - u8 value() const { return m_value; } - -private: - NetworkOrdered m_value; -}; - -static_assert(AssertSize()); - -class [[gnu::packed]] TCPPacket { -public: - TCPPacket() = default; - ~TCPPacket() = default; - - size_t header_size() const { return data_offset() * sizeof(u32); } - - u16 source_port() const { return m_source_port; } - void set_source_port(u16 port) { m_source_port = port; } - - u16 destination_port() const { return m_destination_port; } - void set_destination_port(u16 port) { m_destination_port = port; } - - u32 sequence_number() const { return m_sequence_number; } - void set_sequence_number(u32 number) { m_sequence_number = number; } - - u32 ack_number() const { return m_ack_number; } - void set_ack_number(u32 number) { m_ack_number = number; } - - u16 flags() const { return m_flags_and_data_offset & 0x1ff; } - void set_flags(u16 flags) { m_flags_and_data_offset = (m_flags_and_data_offset & ~0x1ff) | (flags & 0x1ff); } - - bool has_syn() const { return flags() & TCPFlags::SYN; } - bool has_ack() const { return flags() & TCPFlags::ACK; } - bool has_fin() const { return flags() & TCPFlags::FIN; } - bool has_rst() const { return flags() & TCPFlags::RST; } - - u8 data_offset() const { return (m_flags_and_data_offset & 0xf000) >> 12; } - void set_data_offset(u16 data_offset) { m_flags_and_data_offset = (m_flags_and_data_offset & ~0xf000) | data_offset << 12; } - - u16 window_size() const { return m_window_size; } - void set_window_size(u16 window_size) { m_window_size = window_size; } - - u16 checksum() const { return m_checksum; } - void set_checksum(u16 checksum) { m_checksum = checksum; } - - u16 urgent() const { return m_urgent; } - void set_urgent(u16 urgent) { m_urgent = urgent; } - - void const* payload() const { return ((u8 const*)this) + header_size(); } - void* payload() { return ((u8*)this) + header_size(); } - - template - void for_each_option(Callback callback) const - { - auto const* next_option = (u8 const*)this + sizeof(TCPPacket); - auto const* options_end = payload(); - while (next_option < options_end) { - if ((size_t)options_end - (size_t)next_option < sizeof(TCPOption)) - return; // Not enough space left for another option - auto const* option = (TCPOption const*)next_option; - if (option->kind() == TCPOptionKind::End) - return; - if (option->kind() == TCPOptionKind::Nop) { - next_option += 1; - continue; - } - if (option->length() < sizeof(TCPOption)) - return; // minimal option length - callback(*option); - next_option += option->length(); - } - } - -private: - NetworkOrdered m_source_port; - NetworkOrdered m_destination_port; - NetworkOrdered m_sequence_number; - NetworkOrdered m_ack_number; - - NetworkOrdered m_flags_and_data_offset; - NetworkOrdered m_window_size; - NetworkOrdered m_checksum; - NetworkOrdered m_urgent; -}; - -static_assert(AssertSize()); - -} diff --git a/Kernel/Net/TCPSocket.cpp b/Kernel/Net/TCPSocket.cpp deleted file mode 100644 index 9e76d29eef7..00000000000 --- a/Kernel/Net/TCPSocket.cpp +++ /dev/null @@ -1,784 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace Kernel { - -void TCPSocket::for_each(Function callback) -{ - sockets_by_tuple().for_each_shared([&](auto const& it) { - callback(*it.value); - }); -} - -ErrorOr TCPSocket::try_for_each(Function(TCPSocket const&)> callback) -{ - return sockets_by_tuple().with_shared([&](auto const& sockets) -> ErrorOr { - for (auto& it : sockets) - TRY(callback(*it.value)); - return {}; - }); -} - -bool TCPSocket::unref() const -{ - bool did_hit_zero = sockets_by_tuple().with_exclusive([&](auto& table) { - if (deref_base()) - return false; - table.remove(tuple()); - const_cast(*this).revoke_weak_ptrs(); - return true; - }); - if (did_hit_zero) { - const_cast(*this).will_be_destroyed(); - delete this; - } - return did_hit_zero; -} - -void TCPSocket::set_state(State new_state) -{ - dbgln_if(TCP_SOCKET_DEBUG, "TCPSocket({}) state moving from {} to {}", this, to_string(m_state), to_string(new_state)); - - auto was_disconnected = protocol_is_disconnected(); - auto previous_role = m_role; - - m_state = new_state; - - if (new_state == State::Established && m_direction == Direction::Outgoing) { - set_role(Role::Connected); - clear_so_error(); - } - - if (new_state == State::TimeWait) { - // Once we hit TimeWait, we are only holding the socket in case there - // are packets on the way which we wouldn't want a new socket to get hit - // with, so there's no point in keeping the receive buffer around. - drop_receive_buffer(); - - auto deadline = TimeManagement::the().current_time(CLOCK_MONOTONIC_COARSE) + maximum_segment_lifetime; - auto timer_was_added = TimerQueue::the().add_timer_without_id(*m_timer, CLOCK_MONOTONIC_COARSE, deadline, [&]() { - dbgln_if(TCP_SOCKET_DEBUG, "TCPSocket({}) TimeWait timer elpased", this); - if (m_state == State::TimeWait) { - m_state = State::Closed; - do_state_closed(); - } - }); - - if (!timer_was_added) [[unlikely]] { - dbgln_if(TCP_SOCKET_DEBUG, "TCPSocket({}) TimeWait timer deadline is in the past", this); - m_state = State::Closed; - new_state = State::Closed; - } - } - - if (new_state == State::Closed) - do_state_closed(); - - if (previous_role != m_role || was_disconnected != protocol_is_disconnected()) - evaluate_block_conditions(); -} - -void TCPSocket::do_state_closed() -{ - if (m_originator) - release_to_originator(); - - closing_sockets().with_exclusive([&](auto& table) { - table.remove(tuple()); - }); -} - -static Singleton>>> s_socket_closing; - -MutexProtected>>& TCPSocket::closing_sockets() -{ - return *s_socket_closing; -} - -static Singleton>> s_socket_tuples; - -MutexProtected>& TCPSocket::sockets_by_tuple() -{ - return *s_socket_tuples; -} - -RefPtr TCPSocket::from_tuple(IPv4SocketTuple const& tuple) -{ - return sockets_by_tuple().with_shared([&](auto const& table) -> RefPtr { - auto exact_match = table.get(tuple); - if (exact_match.has_value()) - return { *exact_match.value() }; - - auto address_tuple = IPv4SocketTuple(tuple.local_address(), tuple.local_port(), IPv4Address(), 0); - auto address_match = table.get(address_tuple); - if (address_match.has_value()) - return { *address_match.value() }; - - auto wildcard_tuple = IPv4SocketTuple(IPv4Address(), tuple.local_port(), IPv4Address(), 0); - auto wildcard_match = table.get(wildcard_tuple); - if (wildcard_match.has_value()) - return { *wildcard_match.value() }; - - return {}; - }); -} -ErrorOr> TCPSocket::try_create_client(IPv4Address const& new_local_address, u16 new_local_port, IPv4Address const& new_peer_address, u16 new_peer_port) -{ - auto tuple = IPv4SocketTuple(new_local_address, new_local_port, new_peer_address, new_peer_port); - return sockets_by_tuple().with_exclusive([&](auto& table) -> ErrorOr> { - if (table.contains(tuple)) - return EEXIST; - - auto receive_buffer = TRY(try_create_receive_buffer()); - auto client = TRY(TCPSocket::try_create(protocol(), move(receive_buffer))); - - client->set_setup_state(SetupState::InProgress); - client->set_local_address(new_local_address); - client->set_local_port(new_local_port); - client->set_peer_address(new_peer_address); - client->set_peer_port(new_peer_port); - client->set_bound(); - client->set_direction(Direction::Incoming); - client->set_originator(*this); - - m_pending_release_for_accept.set(tuple, client); - client->m_registered_socket_tuple = tuple; - table.set(tuple, client); - - return { move(client) }; - }); -} - -void TCPSocket::release_to_originator() -{ - VERIFY(!!m_originator); - m_originator.strong_ref()->release_for_accept(*this); - m_originator.clear(); -} - -void TCPSocket::release_for_accept(NonnullRefPtr socket) -{ - VERIFY(m_pending_release_for_accept.contains(socket->tuple())); - m_pending_release_for_accept.remove(socket->tuple()); - // FIXME: Should we observe this error somehow? - [[maybe_unused]] auto rc = queue_connection_from(move(socket)); -} - -TCPSocket::TCPSocket(int protocol, NonnullOwnPtr receive_buffer, NonnullOwnPtr scratch_buffer, NonnullRefPtr timer) - : IPv4Socket(SOCK_STREAM, protocol, move(receive_buffer), move(scratch_buffer)) - , m_last_ack_sent_time(TimeManagement::the().monotonic_time()) - , m_last_retransmit_time(TimeManagement::the().monotonic_time()) - , m_timer(timer) -{ -} - -TCPSocket::~TCPSocket() -{ - dequeue_for_retransmit(); - - dbgln_if(TCP_SOCKET_DEBUG, "~TCPSocket in state {}", to_string(state())); -} - -ErrorOr> TCPSocket::try_create(int protocol, NonnullOwnPtr receive_buffer) -{ - // Note: Scratch buffer is only used for SOCK_STREAM sockets. - auto scratch_buffer = TRY(KBuffer::try_create_with_size("TCPSocket: Scratch buffer"sv, 65536)); - auto timer = TRY(adopt_nonnull_ref_or_enomem(new (nothrow) Timer)); - return adopt_nonnull_ref_or_enomem(new (nothrow) TCPSocket(protocol, move(receive_buffer), move(scratch_buffer), timer)); -} - -ErrorOr TCPSocket::protocol_size(ReadonlyBytes raw_ipv4_packet) -{ - auto& ipv4_packet = *reinterpret_cast(raw_ipv4_packet.data()); - auto& tcp_packet = *static_cast(ipv4_packet.payload()); - return raw_ipv4_packet.size() - sizeof(IPv4Packet) - tcp_packet.header_size(); -} - -ErrorOr TCPSocket::protocol_receive(ReadonlyBytes raw_ipv4_packet, UserOrKernelBuffer& buffer, size_t buffer_size, [[maybe_unused]] int flags) -{ - auto& ipv4_packet = *reinterpret_cast(raw_ipv4_packet.data()); - auto& tcp_packet = *static_cast(ipv4_packet.payload()); - size_t payload_size = raw_ipv4_packet.size() - sizeof(IPv4Packet) - tcp_packet.header_size(); - dbgln_if(TCP_SOCKET_DEBUG, "payload_size {}, will it fit in {}?", payload_size, buffer_size); - VERIFY(buffer_size >= payload_size); - SOCKET_TRY(buffer.write(tcp_packet.payload(), payload_size)); - return payload_size; -} - -ErrorOr TCPSocket::protocol_send(UserOrKernelBuffer const& data, size_t data_length) -{ - auto adapter = bound_interface().with([](auto& bound_device) -> RefPtr { return bound_device; }); - RoutingDecision routing_decision = route_to(peer_address(), local_address(), adapter); - if (routing_decision.is_zero()) - return set_so_error(EHOSTUNREACH); - size_t mss = routing_decision.adapter->mtu() - sizeof(IPv4Packet) - sizeof(TCPPacket); - - if (!m_no_delay) { - // RFC 896 (Nagle’s algorithm): https://www.ietf.org/rfc/rfc0896 - // "The solution is to inhibit the sending of new TCP segments when - // new outgoing data arrives from the user if any previously - // transmitted data on the connection remains unacknowledged. This - // inhibition is to be unconditional; no timers, tests for size of - // data received, or other conditions are required." - auto has_unacked_data = m_unacked_packets.with_shared([&](auto const& packets) { return packets.size > 0; }); - if (has_unacked_data && data_length < mss) - return set_so_error(EAGAIN); - } - - data_length = min(data_length, mss); - TRY(send_tcp_packet(TCPFlags::PSH | TCPFlags::ACK, &data, data_length, &routing_decision)); - return data_length; -} - -ErrorOr TCPSocket::send_ack(bool allow_duplicate) -{ - if (!allow_duplicate && m_last_ack_number_sent == m_ack_number) - return {}; - return send_tcp_packet(TCPFlags::ACK); -} - -ErrorOr TCPSocket::send_tcp_packet(u16 flags, UserOrKernelBuffer const* payload, size_t payload_size, RoutingDecision* user_routing_decision) -{ - auto adapter = bound_interface().with([](auto& bound_device) -> RefPtr { return bound_device; }); - RoutingDecision routing_decision = user_routing_decision ? *user_routing_decision : route_to(peer_address(), local_address(), adapter); - if (routing_decision.is_zero()) - return set_so_error(EHOSTUNREACH); - - auto ipv4_payload_offset = routing_decision.adapter->ipv4_payload_offset(); - - bool const has_mss_option = flags & TCPFlags::SYN; - bool const has_window_scale_option = flags & TCPFlags::SYN; - size_t const options_size = (has_mss_option ? sizeof(TCPOptionMSS) : 0) + (has_window_scale_option ? sizeof(TCPOptionWindowScale) : 0); - size_t const tcp_header_size = sizeof(TCPPacket) + align_up_to(options_size, 4); - size_t const buffer_size = ipv4_payload_offset + tcp_header_size + payload_size; - auto packet = routing_decision.adapter->acquire_packet_buffer(buffer_size); - if (!packet) - return set_so_error(ENOMEM); - routing_decision.adapter->fill_in_ipv4_header(*packet, local_address(), - routing_decision.next_hop, peer_address(), IPv4Protocol::TCP, - buffer_size - ipv4_payload_offset, type_of_service(), ttl()); - memset(packet->buffer->data() + ipv4_payload_offset, 0, sizeof(TCPPacket)); - auto& tcp_packet = *(TCPPacket*)(packet->buffer->data() + ipv4_payload_offset); - VERIFY(local_port()); - tcp_packet.set_source_port(local_port()); - tcp_packet.set_destination_port(peer_port()); - auto window_size = available_space_in_receive_buffer(); - if ((flags & TCPFlags::SYN) == 0 && m_window_scaling_supported) - window_size >>= receive_window_scale(); - tcp_packet.set_window_size(min(window_size, NumericLimits::max())); - tcp_packet.set_sequence_number(m_sequence_number); - tcp_packet.set_data_offset(tcp_header_size / sizeof(u32)); - tcp_packet.set_flags(flags); - - if (payload) { - if (auto result = payload->read(tcp_packet.payload(), payload_size); result.is_error()) { - routing_decision.adapter->release_packet_buffer(*packet); - return set_so_error(result.release_error()); - } - } - - if (flags & TCPFlags::ACK) { - m_last_ack_number_sent = m_ack_number; - m_last_ack_sent_time = TimeManagement::the().monotonic_time(); - tcp_packet.set_ack_number(m_ack_number); - } - - if (flags & TCPFlags::SYN) { - ++m_sequence_number; - } else { - m_sequence_number += payload_size; - } - - u8* next_option = packet->buffer->data() + ipv4_payload_offset + sizeof(TCPPacket); - if (has_mss_option) { - u16 mss = routing_decision.adapter->mtu() - sizeof(IPv4Packet) - sizeof(TCPPacket); - TCPOptionMSS mss_option { mss }; - memcpy(next_option, &mss_option, sizeof(mss_option)); - next_option += sizeof(mss_option); - } - if (has_window_scale_option) { - TCPOptionWindowScale window_scale_option { receive_window_scale() }; - memcpy(next_option, &window_scale_option, sizeof(window_scale_option)); - next_option += sizeof(window_scale_option); - } - if ((options_size % 4) != 0) - *next_option = to_underlying(TCPOptionKind::End); - - tcp_packet.set_checksum(compute_tcp_checksum(local_address(), peer_address(), tcp_packet, payload_size)); - - bool expect_ack { tcp_packet.has_syn() || payload_size > 0 }; - if (expect_ack) { - bool append_failed { false }; - m_unacked_packets.with_exclusive([&](auto& unacked_packets) { - auto result = unacked_packets.packets.try_append({ m_sequence_number, packet, ipv4_payload_offset, *routing_decision.adapter }); - if (result.is_error()) { - dbgln("TCPSocket: Dropped outbound packet because try_append() failed"); - append_failed = true; - return; - } - unacked_packets.size += payload_size; - enqueue_for_retransmit(); - }); - if (append_failed) - return set_so_error(ENOMEM); - } - - m_packets_out++; - m_bytes_out += buffer_size; - routing_decision.adapter->send_packet(packet->bytes()); - if (!expect_ack) - routing_decision.adapter->release_packet_buffer(*packet); - - return {}; -} - -void TCPSocket::receive_tcp_packet(TCPPacket const& packet, u16 size) -{ - if (packet.has_ack()) { - u32 ack_number = packet.ack_number(); - - dbgln_if(TCP_SOCKET_DEBUG, "TCPSocket: receive_tcp_packet: {}", ack_number); - - int removed = 0; - m_unacked_packets.with_exclusive([&](auto& unacked_packets) { - while (!unacked_packets.packets.is_empty()) { - auto& packet = unacked_packets.packets.first(); - - dbgln_if(TCP_SOCKET_DEBUG, "TCPSocket: iterate: {}", packet.ack_number); - - if (packet.ack_number <= ack_number) { - auto old_adapter = packet.adapter.strong_ref(); - if (old_adapter) - old_adapter->release_packet_buffer(*packet.buffer); - TCPPacket& tcp_packet = *(TCPPacket*)(packet.buffer->buffer->data() + packet.ipv4_payload_offset); - if (m_send_window_size != tcp_packet.window_size()) { - m_send_window_size = tcp_packet.window_size() << m_send_window_scale; - } - auto payload_size = packet.buffer->buffer->data() + packet.buffer->buffer->size() - (u8*)tcp_packet.payload(); - unacked_packets.size -= payload_size; - evaluate_block_conditions(); - unacked_packets.packets.take_first(); - removed++; - } else { - break; - } - } - - if (unacked_packets.packets.is_empty()) { - m_retransmit_attempts = 0; - dequeue_for_retransmit(); - } - - dbgln_if(TCP_SOCKET_DEBUG, "TCPSocket: receive_tcp_packet acknowledged {} packets", removed); - }); - } - - m_packets_in++; - m_bytes_in += packet.header_size() + size; -} - -bool TCPSocket::should_delay_next_ack() const -{ - // FIXME: We don't know the MSS here so make a reasonable guess. - size_t const mss = 1500; - - // RFC 1122 says we should send an ACK for every two full-sized segments. - if (m_ack_number >= m_last_ack_number_sent + 2 * mss) - return false; - - // RFC 1122 says we should not delay ACKs for more than 500 milliseconds. - if (TimeManagement::the().monotonic_time(TimePrecision::Precise) >= m_last_ack_sent_time + Duration::from_milliseconds(500)) - return false; - - return true; -} - -NetworkOrdered TCPSocket::compute_tcp_checksum(IPv4Address const& source, IPv4Address const& destination, TCPPacket const& packet, u16 payload_size) -{ - union PseudoHeader { - struct [[gnu::packed]] { - IPv4Address source; - IPv4Address destination; - u8 zero; - u8 protocol; - NetworkOrdered payload_size; - } header; - u16 raw[6]; - }; - static_assert(sizeof(PseudoHeader) == 12); - - Checked packet_size = packet.header_size(); - packet_size += payload_size; - VERIFY(!packet_size.has_overflow()); - - PseudoHeader pseudo_header { .header = { source, destination, 0, (u8)IPv4Protocol::TCP, packet_size.value() } }; - - u32 checksum = 0; - auto* raw_pseudo_header = pseudo_header.raw; - for (size_t i = 0; i < sizeof(pseudo_header) / sizeof(u16); ++i) { - checksum += AK::convert_between_host_and_network_endian(raw_pseudo_header[i]); - if (checksum > 0xffff) - checksum = (checksum >> 16) + (checksum & 0xffff); - } - auto* raw_packet = bit_cast(&packet); - for (size_t i = 0; i < packet.header_size() / sizeof(u16); ++i) { - checksum += AK::convert_between_host_and_network_endian(raw_packet[i]); - if (checksum > 0xffff) - checksum = (checksum >> 16) + (checksum & 0xffff); - } - VERIFY(packet.data_offset() * 4 == packet.header_size()); - auto* raw_payload = bit_cast(packet.payload()); - for (size_t i = 0; i < payload_size / sizeof(u16); ++i) { - checksum += AK::convert_between_host_and_network_endian(raw_payload[i]); - if (checksum > 0xffff) - checksum = (checksum >> 16) + (checksum & 0xffff); - } - if (payload_size & 1) { - u16 expanded_byte = ((u8 const*)packet.payload())[payload_size - 1] << 8; - checksum += expanded_byte; - if (checksum > 0xffff) - checksum = (checksum >> 16) + (checksum & 0xffff); - } - return ~(checksum & 0xffff); -} - -ErrorOr TCPSocket::setsockopt(int level, int option, Userspace user_value, socklen_t user_value_size) -{ - if (level != IPPROTO_TCP) - return IPv4Socket::setsockopt(level, option, user_value, user_value_size); - - MutexLocker locker(mutex()); - - switch (option) { - case TCP_NODELAY: - if (user_value_size < sizeof(int)) - return EINVAL; - int value; - TRY(copy_from_user(&value, static_ptr_cast(user_value))); - if (value != 0 && value != 1) - return EINVAL; - m_no_delay = value; - return {}; - default: - dbgln("setsockopt({}) at IPPROTO_TCP not implemented.", option); - return ENOPROTOOPT; - } -} - -ErrorOr TCPSocket::getsockopt(OpenFileDescription& description, int level, int option, Userspace value, Userspace value_size) -{ - if (level != IPPROTO_TCP) - return IPv4Socket::getsockopt(description, level, option, value, value_size); - - MutexLocker locker(mutex()); - - socklen_t size; - TRY(copy_from_user(&size, value_size.unsafe_userspace_ptr())); - - switch (option) { - case TCP_NODELAY: { - int nodelay = m_no_delay ? 1 : 0; - if (size < sizeof(nodelay)) - return EINVAL; - TRY(copy_to_user(static_ptr_cast(value), &nodelay)); - size = sizeof(nodelay); - return copy_to_user(value_size, &size); - } - default: - dbgln("getsockopt({}) at IPPROTO_TCP not implemented.", option); - return ENOPROTOOPT; - } -} - -ErrorOr TCPSocket::protocol_bind() -{ - dbgln_if(TCP_SOCKET_DEBUG, "TCPSocket::protocol_bind(), local_port() is {}", local_port()); - // Check that we do have the address we're trying to bind to. - TRY(m_adapter.with([this](auto& adapter) -> ErrorOr { - if (has_specific_local_address() && !adapter) { - adapter = NetworkingManagement::the().from_ipv4_address(local_address()); - if (!adapter) - return set_so_error(EADDRNOTAVAIL); - } - return {}; - })); - - if (local_port() == 0) { - // Allocate an unused ephemeral port. - constexpr u16 first_ephemeral_port = 32768; - constexpr u16 last_ephemeral_port = 60999; - constexpr u16 ephemeral_port_range_size = last_ephemeral_port - first_ephemeral_port; - u16 first_scan_port = first_ephemeral_port + get_good_random() % ephemeral_port_range_size; - - return sockets_by_tuple().with_exclusive([&](auto& table) -> ErrorOr { - u16 port = first_scan_port; - while (true) { - IPv4SocketTuple proposed_tuple(local_address(), port, peer_address(), peer_port()); - - auto it = table.find(proposed_tuple); - if (it == table.end()) { - set_local_port(port); - m_registered_socket_tuple = proposed_tuple; - table.set(proposed_tuple, this); - dbgln_if(TCP_SOCKET_DEBUG, "...allocated port {}, tuple {}", port, proposed_tuple.to_string()); - return {}; - } - ++port; - if (port > last_ephemeral_port) - port = first_ephemeral_port; - if (port == first_scan_port) - break; - } - return set_so_error(EADDRINUSE); - }); - } else { - // Verify that the user-supplied port is not already used by someone else. - bool ok = sockets_by_tuple().with_exclusive([&](auto& table) -> bool { - if (table.contains(tuple())) - return false; - auto socket_tuple = tuple(); - m_registered_socket_tuple = socket_tuple; - table.set(socket_tuple, this); - return true; - }); - if (!ok) - return set_so_error(EADDRINUSE); - return {}; - } -} - -ErrorOr TCPSocket::protocol_listen() -{ - set_direction(Direction::Passive); - set_state(State::Listen); - set_setup_state(SetupState::Completed); - return {}; -} - -ErrorOr TCPSocket::protocol_connect(OpenFileDescription& description) -{ - MutexLocker locker(mutex()); - - auto routing_decision = route_to(peer_address(), local_address()); - if (routing_decision.is_zero()) - return set_so_error(EHOSTUNREACH); - if (!has_specific_local_address()) - set_local_address(routing_decision.adapter->ipv4_address()); - - TRY(ensure_bound()); - if (m_registered_socket_tuple.has_value() && m_registered_socket_tuple != tuple()) { - // If the socket was manually bound (using bind(2)) instead of implicitly using connect, - // it will already be registered in the TCPSocket sockets_by_tuple table, under the previous - // socket tuple. We replace the entry in the table to ensure it is also properly removed on - // socket deletion, to prevent a dangling reference. - TRY(sockets_by_tuple().with_exclusive([this](auto& table) -> ErrorOr { - auto removed = table.remove(*m_registered_socket_tuple); - VERIFY(removed); - if (table.contains(tuple())) - return set_so_error(EADDRINUSE); - table.set(tuple(), this); - return {}; - })); - m_registered_socket_tuple = tuple(); - } - - m_sequence_number = get_good_random(); - m_ack_number = 0; - - set_setup_state(SetupState::InProgress); - TRY(send_tcp_packet(TCPFlags::SYN)); - m_state = State::SynSent; - set_role(Role::Connecting); - m_direction = Direction::Outgoing; - - evaluate_block_conditions(); - - if (description.is_blocking()) { - locker.unlock(); - auto unblock_flags = Thread::FileBlocker::BlockFlags::None; - if (Thread::current()->block({}, description, unblock_flags).was_interrupted()) - return set_so_error(EINTR); - locker.lock(); - VERIFY(setup_state() == SetupState::Completed); - if (has_error()) { // TODO: check unblock_flags - set_role(Role::None); - if (error() == TCPSocket::Error::RetransmitTimeout) - return set_so_error(ETIMEDOUT); - else - return set_so_error(ECONNREFUSED); - } - return {}; - } - - return set_so_error(EINPROGRESS); -} - -bool TCPSocket::protocol_is_disconnected() const -{ - switch (m_state) { - case State::Closed: - case State::CloseWait: - case State::LastAck: - case State::FinWait1: - case State::FinWait2: - case State::Closing: - case State::TimeWait: - return true; - default: - return false; - } -} - -void TCPSocket::shut_down_for_writing() -{ - if (state() == State::Established) { - dbgln_if(TCP_SOCKET_DEBUG, " Sending FIN from Established and moving into FinWait1"); - (void)send_tcp_packet(TCPFlags::FIN | TCPFlags::ACK); - set_state(State::FinWait1); - } else { - dbgln(" Shutting down TCPSocket for writing but not moving to FinWait1 since state is {}", to_string(state())); - } -} - -ErrorOr TCPSocket::close() -{ - MutexLocker locker(mutex()); - auto result = IPv4Socket::close(); - if (state() == State::CloseWait) { - dbgln_if(TCP_SOCKET_DEBUG, " Sending FIN from CloseWait and moving into LastAck"); - [[maybe_unused]] auto rc = send_tcp_packet(TCPFlags::FIN | TCPFlags::ACK); - set_state(State::LastAck); - } - - if (state() != State::Closed && state() != State::Listen) - closing_sockets().with_exclusive([&](auto& table) { - table.set(tuple(), *this); - }); - return result; -} - -static Singleton> s_sockets_for_retransmit; - -MutexProtected& TCPSocket::sockets_for_retransmit() -{ - return *s_sockets_for_retransmit; -} - -void TCPSocket::enqueue_for_retransmit() -{ - sockets_for_retransmit().with_exclusive([&](auto& list) { - list.append(*this); - }); -} - -void TCPSocket::dequeue_for_retransmit() -{ - sockets_for_retransmit().with_exclusive([&](auto& list) { - list.remove(*this); - }); -} - -void TCPSocket::retransmit_packets() -{ - auto now = TimeManagement::the().monotonic_time(); - - // RFC6298 says we should have at least one second between retransmits. According to - // RFC1122 we must do exponential backoff - even for SYN packets. - i64 retransmit_interval = 1; - for (decltype(m_retransmit_attempts) i = 0; i < m_retransmit_attempts; i++) - retransmit_interval *= 2; - - if (m_last_retransmit_time > now - Duration::from_seconds(retransmit_interval)) - return; - - dbgln_if(TCP_SOCKET_DEBUG, "TCPSocket({}) handling retransmit", this); - - m_last_retransmit_time = now; - ++m_retransmit_attempts; - - if (m_retransmit_attempts > maximum_retransmits) { - set_state(TCPSocket::State::Closed); - set_error(TCPSocket::Error::RetransmitTimeout); - set_setup_state(Socket::SetupState::Completed); - return; - } - - auto adapter = bound_interface().with([](auto& bound_device) -> RefPtr { return bound_device; }); - auto routing_decision = route_to(peer_address(), local_address(), adapter); - if (routing_decision.is_zero()) - return; - - m_unacked_packets.with_exclusive([&](auto& unacked_packets) { - for (auto& packet : unacked_packets.packets) { - packet.tx_counter++; - - if constexpr (TCP_SOCKET_DEBUG) { - auto& tcp_packet = *(TCPPacket const*)(packet.buffer->buffer->data() + packet.ipv4_payload_offset); - dbgln("Sending TCP packet from {}:{} to {}:{} with ({}{}{}{}) seq_no={}, ack_no={}, tx_counter={}", - local_address(), local_port(), - peer_address(), peer_port(), - (tcp_packet.has_syn() ? "SYN " : ""), - (tcp_packet.has_ack() ? "ACK " : ""), - (tcp_packet.has_fin() ? "FIN " : ""), - (tcp_packet.has_rst() ? "RST " : ""), - tcp_packet.sequence_number(), - tcp_packet.ack_number(), - packet.tx_counter); - } - - size_t ipv4_payload_offset = routing_decision.adapter->ipv4_payload_offset(); - if (ipv4_payload_offset != packet.ipv4_payload_offset) { - // FIXME: Add support for this. This can happen if after a route change - // we ended up on another adapter which doesn't have the same layer 2 type - // like the previous adapter. - VERIFY_NOT_REACHED(); - } - - auto packet_buffer = packet.buffer->bytes(); - - routing_decision.adapter->fill_in_ipv4_header(*packet.buffer, - local_address(), routing_decision.next_hop, peer_address(), - IPv4Protocol::TCP, packet_buffer.size() - ipv4_payload_offset, type_of_service(), ttl()); - routing_decision.adapter->send_packet(packet_buffer); - m_packets_out++; - m_bytes_out += packet_buffer.size(); - } - }); -} - -bool TCPSocket::can_write(OpenFileDescription const& file_description, u64 size) const -{ - if (!IPv4Socket::can_write(file_description, size)) - return false; - - if (m_state == State::SynSent || m_state == State::SynReceived) - return false; - - if (!file_description.is_blocking()) - return true; - - return m_unacked_packets.with_shared([&](auto& unacked_packets) { - return unacked_packets.size + size <= m_send_window_size; - }); -} -} diff --git a/Kernel/Net/TCPSocket.h b/Kernel/Net/TCPSocket.h deleted file mode 100644 index ef8ddc87d9a..00000000000 --- a/Kernel/Net/TCPSocket.h +++ /dev/null @@ -1,268 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace Kernel { - -class TCPSocket final : public IPv4Socket { -public: - static void for_each(Function); - static ErrorOr try_for_each(Function(TCPSocket const&)>); - static ErrorOr> try_create(int protocol, NonnullOwnPtr receive_buffer); - virtual ~TCPSocket() override; - - virtual bool unref() const override; - - enum class Direction { - Unspecified, - Outgoing, - Incoming, - Passive, - }; - - static StringView to_string(Direction direction) - { - switch (direction) { - case Direction::Unspecified: - return "Unspecified"sv; - case Direction::Outgoing: - return "Outgoing"sv; - case Direction::Incoming: - return "Incoming"sv; - case Direction::Passive: - return "Passive"sv; - default: - return "None"sv; - } - } - - enum class State { - Closed, - Listen, - SynSent, - SynReceived, - Established, - CloseWait, - LastAck, - FinWait1, - FinWait2, - Closing, - TimeWait, - }; - - static StringView to_string(State state) - { - switch (state) { - case State::Closed: - return "Closed"sv; - case State::Listen: - return "Listen"sv; - case State::SynSent: - return "SynSent"sv; - case State::SynReceived: - return "SynReceived"sv; - case State::Established: - return "Established"sv; - case State::CloseWait: - return "CloseWait"sv; - case State::LastAck: - return "LastAck"sv; - case State::FinWait1: - return "FinWait1"sv; - case State::FinWait2: - return "FinWait2"sv; - case State::Closing: - return "Closing"sv; - case State::TimeWait: - return "TimeWait"sv; - default: - return "None"sv; - } - } - - enum class Error { - None, - FINDuringConnect, - RSTDuringConnect, - UnexpectedFlagsDuringConnect, - RetransmitTimeout, - }; - - static StringView to_string(Error error) - { - switch (error) { - case Error::None: - return "None"sv; - case Error::FINDuringConnect: - return "FINDuringConnect"sv; - case Error::RSTDuringConnect: - return "RSTDuringConnect"sv; - case Error::UnexpectedFlagsDuringConnect: - return "UnexpectedFlagsDuringConnect"sv; - default: - return "Invalid"sv; - } - } - - State state() const { return m_state; } - void set_state(State state); - - Direction direction() const { return m_direction; } - - bool has_error() const { return m_error != Error::None; } - Error error() const { return m_error; } - void set_error(Error error) { m_error = error; } - - void set_ack_number(u32 n) { m_ack_number = n; } - void set_sequence_number(u32 n) { m_sequence_number = n; } - u32 ack_number() const { return m_ack_number; } - u32 sequence_number() const { return m_sequence_number; } - u32 packets_in() const { return m_packets_in; } - u32 bytes_in() const { return m_bytes_in; } - u32 packets_out() const { return m_packets_out; } - u32 bytes_out() const { return m_bytes_out; } - - void set_send_window_scale(size_t scale) - { - m_window_scaling_supported = true; - m_send_window_scale = scale; - } - - // FIXME: Make this configurable? - static constexpr u32 maximum_duplicate_acks = 5; - void set_duplicate_acks(u32 acks) { m_duplicate_acks = acks; } - u32 duplicate_acks() const { return m_duplicate_acks; } - - ErrorOr send_ack(bool allow_duplicate = false); - ErrorOr send_tcp_packet(u16 flags, UserOrKernelBuffer const* = nullptr, size_t = 0, RoutingDecision* = nullptr); - void receive_tcp_packet(TCPPacket const&, u16 size); - - bool should_delay_next_ack() const; - - static MutexProtected>& sockets_by_tuple(); - static RefPtr from_tuple(IPv4SocketTuple const& tuple); - - static MutexProtected>>& closing_sockets(); - - ErrorOr> try_create_client(IPv4Address const& local_address, u16 local_port, IPv4Address const& peer_address, u16 peer_port); - void set_originator(TCPSocket& originator) { m_originator = originator; } - bool has_originator() { return !!m_originator; } - void release_to_originator(); - void release_for_accept(NonnullRefPtr); - - void retransmit_packets(); - - virtual ErrorOr close() override; - - virtual bool can_write(OpenFileDescription const&, u64) const override; - - static NetworkOrdered compute_tcp_checksum(IPv4Address const& source, IPv4Address const& destination, TCPPacket const&, u16 payload_size); - - virtual ErrorOr setsockopt(int level, int option, Userspace, socklen_t) override; - virtual ErrorOr getsockopt(OpenFileDescription&, int level, int option, Userspace, Userspace) override; - -protected: - void set_direction(Direction direction) { m_direction = direction; } - -private: - explicit TCPSocket(int protocol, NonnullOwnPtr receive_buffer, NonnullOwnPtr scratch_buffer, NonnullRefPtr timer); - virtual StringView class_name() const override { return "TCPSocket"sv; } - - virtual void shut_down_for_writing() override; - - virtual ErrorOr protocol_receive(ReadonlyBytes raw_ipv4_packet, UserOrKernelBuffer& buffer, size_t buffer_size, int flags) override; - virtual ErrorOr protocol_send(UserOrKernelBuffer const&, size_t) override; - virtual ErrorOr protocol_connect(OpenFileDescription&) override; - virtual ErrorOr protocol_size(ReadonlyBytes raw_ipv4_packet) override; - virtual bool protocol_is_disconnected() const override; - virtual ErrorOr protocol_bind() override; - virtual ErrorOr protocol_listen() override; - - void do_state_closed(); - - void enqueue_for_retransmit(); - void dequeue_for_retransmit(); - - static constexpr size_t receive_window_scale() - { - auto buffer_size_bit_length = AK::log2(receive_buffer_size) + 1; - if (buffer_size_bit_length < 16) - return 0; - return buffer_size_bit_length - 16; - } - - LockWeakPtr m_originator; - HashMap> m_pending_release_for_accept; - Direction m_direction { Direction::Unspecified }; - Error m_error { Error::None }; - SpinlockProtected, LockRank::None> m_adapter; - u32 m_sequence_number { 0 }; - u32 m_ack_number { 0 }; - State m_state { State::Closed }; - u32 m_packets_in { 0 }; - u32 m_bytes_in { 0 }; - u32 m_packets_out { 0 }; - u32 m_bytes_out { 0 }; - - struct OutgoingPacket { - u32 ack_number { 0 }; - RefPtr buffer; - size_t ipv4_payload_offset; - LockWeakPtr adapter; - int tx_counter { 0 }; - }; - - struct UnackedPackets { - SinglyLinkedList packets; - size_t size { 0 }; - }; - - MutexProtected m_unacked_packets; - - u32 m_duplicate_acks { 0 }; - - u32 m_last_ack_number_sent { 0 }; - MonotonicTime m_last_ack_sent_time; - - static constexpr Duration maximum_segment_lifetime = Duration::from_seconds(120); - - // FIXME: Make this configurable (sysctl) - static constexpr u32 maximum_retransmits = 5; - MonotonicTime m_last_retransmit_time; - u32 m_retransmit_attempts { 0 }; - - // Default to maximum window size. receive_tcp_packet() will update from the - // peer's advertised window size. - u32 m_send_window_size { 64 * KiB }; - bool m_window_scaling_supported { false }; - size_t m_send_window_scale { 0 }; - - bool m_no_delay { false }; - - IntrusiveListNode m_retransmit_list_node; - - Optional m_registered_socket_tuple; - - NonnullRefPtr m_timer; - -public: - using RetransmitList = IntrusiveList<&TCPSocket::m_retransmit_list_node>; - static MutexProtected& sockets_for_retransmit(); -}; - -} diff --git a/Kernel/Net/UDP.h b/Kernel/Net/UDP.h deleted file mode 100644 index eb57919b00e..00000000000 --- a/Kernel/Net/UDP.h +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include - -namespace Kernel { - -class [[gnu::packed]] UDPPacket { -public: - UDPPacket() = default; - ~UDPPacket() = default; - - u16 source_port() const { return m_source_port; } - void set_source_port(u16 port) { m_source_port = port; } - - u16 destination_port() const { return m_destination_port; } - void set_destination_port(u16 port) { m_destination_port = port; } - - u16 length() const { return m_length; } - void set_length(u16 length) { m_length = length; } - - u16 checksum() const { return m_checksum; } - void set_checksum(u16 checksum) { m_checksum = checksum; } - - void const* payload() const { return this + 1; } - void* payload() { return this + 1; } - -private: - NetworkOrdered m_source_port; - NetworkOrdered m_destination_port; - NetworkOrdered m_length; - NetworkOrdered m_checksum; -}; - -static_assert(sizeof(UDPPacket) == 8); - -} diff --git a/Kernel/Net/UDPSocket.cpp b/Kernel/Net/UDPSocket.cpp deleted file mode 100644 index 0a1c5789366..00000000000 --- a/Kernel/Net/UDPSocket.cpp +++ /dev/null @@ -1,155 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -namespace Kernel { - -void UDPSocket::for_each(Function callback) -{ - sockets_by_port().for_each_shared([&](auto const& socket) { - callback(*socket.value); - }); -} - -ErrorOr UDPSocket::try_for_each(Function(UDPSocket const&)> callback) -{ - return sockets_by_port().with_shared([&](auto const& sockets) -> ErrorOr { - for (auto& socket : sockets) - TRY(callback(*socket.value)); - return {}; - }); -} - -static Singleton>> s_map; - -MutexProtected>& UDPSocket::sockets_by_port() -{ - return *s_map; -} - -RefPtr UDPSocket::from_port(u16 port) -{ - return sockets_by_port().with_shared([&](auto const& table) -> RefPtr { - auto it = table.find(port); - if (it == table.end()) - return {}; - return (*it).value; - }); -} - -UDPSocket::UDPSocket(int protocol, NonnullOwnPtr receive_buffer) - : IPv4Socket(SOCK_DGRAM, protocol, move(receive_buffer), {}) -{ -} - -UDPSocket::~UDPSocket() -{ - sockets_by_port().with_exclusive([&](auto& table) { - table.remove(local_port()); - }); -} - -ErrorOr> UDPSocket::try_create(int protocol, NonnullOwnPtr receive_buffer) -{ - return adopt_nonnull_ref_or_enomem(new (nothrow) UDPSocket(protocol, move(receive_buffer))); -} - -ErrorOr UDPSocket::protocol_size(ReadonlyBytes raw_ipv4_packet) -{ - auto& ipv4_packet = *(IPv4Packet const*)(raw_ipv4_packet.data()); - auto& udp_packet = *static_cast(ipv4_packet.payload()); - return udp_packet.length() - sizeof(UDPPacket); -} - -ErrorOr UDPSocket::protocol_receive(ReadonlyBytes raw_ipv4_packet, UserOrKernelBuffer& buffer, size_t buffer_size, [[maybe_unused]] int flags) -{ - auto& ipv4_packet = *(IPv4Packet const*)(raw_ipv4_packet.data()); - auto& udp_packet = *static_cast(ipv4_packet.payload()); - VERIFY(udp_packet.length() >= sizeof(UDPPacket)); // FIXME: This should be rejected earlier. - size_t read_size = min(buffer_size, udp_packet.length() - sizeof(UDPPacket)); - SOCKET_TRY(buffer.write(udp_packet.payload(), read_size)); - return read_size; -} - -ErrorOr UDPSocket::protocol_send(UserOrKernelBuffer const& data, size_t data_length) -{ - auto adapter = bound_interface().with([](auto& bound_device) -> RefPtr { return bound_device; }); - auto allow_broadcast = m_broadcast_allowed ? AllowBroadcast::Yes : AllowBroadcast::No; - auto routing_decision = route_to(peer_address(), local_address(), adapter, allow_broadcast); - if (routing_decision.is_zero()) - return set_so_error(EHOSTUNREACH); - auto ipv4_payload_offset = routing_decision.adapter->ipv4_payload_offset(); - data_length = min(data_length, routing_decision.adapter->mtu() - ipv4_payload_offset - sizeof(UDPPacket)); - size_t const udp_buffer_size = sizeof(UDPPacket) + data_length; - auto packet = routing_decision.adapter->acquire_packet_buffer(ipv4_payload_offset + udp_buffer_size); - if (!packet) - return set_so_error(ENOMEM); - memset(packet->buffer->data() + ipv4_payload_offset, 0, sizeof(UDPPacket)); - auto& udp_packet = *reinterpret_cast(packet->buffer->data() + ipv4_payload_offset); - udp_packet.set_source_port(local_port()); - udp_packet.set_destination_port(peer_port()); - udp_packet.set_length(udp_buffer_size); - SOCKET_TRY(data.read(udp_packet.payload(), data_length)); - routing_decision.adapter->fill_in_ipv4_header(*packet, local_address(), routing_decision.next_hop, - peer_address(), IPv4Protocol::UDP, udp_buffer_size, type_of_service(), ttl()); - routing_decision.adapter->send_packet(packet->bytes()); - return data_length; -} - -ErrorOr UDPSocket::protocol_connect(OpenFileDescription&) -{ - TRY(ensure_bound()); - set_role(Role::Connected); - set_connected(true); - return {}; -} - -ErrorOr UDPSocket::protocol_bind() -{ - if (local_port() == 0) { - // Allocate an unused ephemeral port. - constexpr u16 first_ephemeral_port = 32768; - constexpr u16 last_ephemeral_port = 60999; - constexpr u16 ephemeral_port_range_size = last_ephemeral_port - first_ephemeral_port; - u16 first_scan_port = first_ephemeral_port + get_good_random() % ephemeral_port_range_size; - - return sockets_by_port().with_exclusive([&](auto& table) -> ErrorOr { - u16 port = first_scan_port; - while (true) { - auto it = table.find(port); - if (it == table.end()) { - set_local_port(port); - table.set(port, this); - return {}; - } - ++port; - if (port > last_ephemeral_port) - port = first_ephemeral_port; - if (port == first_scan_port) - break; - } - return set_so_error(EADDRINUSE); - }); - } else { - // Verify that the user-supplied port is not already used by someone else. - return sockets_by_port().with_exclusive([&](auto& table) -> ErrorOr { - if (table.contains(local_port())) - return set_so_error(EADDRINUSE); - table.set(local_port(), this); - return {}; - }); - } -} - -} diff --git a/Kernel/Net/UDPSocket.h b/Kernel/Net/UDPSocket.h deleted file mode 100644 index 32b5b05334c..00000000000 --- a/Kernel/Net/UDPSocket.h +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include - -namespace Kernel { - -class UDPSocket final : public IPv4Socket { -public: - static ErrorOr> try_create(int protocol, NonnullOwnPtr receive_buffer); - virtual ~UDPSocket() override; - - static RefPtr from_port(u16); - static void for_each(Function); - static ErrorOr try_for_each(Function(UDPSocket const&)>); - -private: - explicit UDPSocket(int protocol, NonnullOwnPtr receive_buffer); - virtual StringView class_name() const override { return "UDPSocket"sv; } - static MutexProtected>& sockets_by_port(); - - virtual ErrorOr protocol_receive(ReadonlyBytes raw_ipv4_packet, UserOrKernelBuffer& buffer, size_t buffer_size, int flags) override; - virtual ErrorOr protocol_send(UserOrKernelBuffer const&, size_t) override; - virtual ErrorOr protocol_size(ReadonlyBytes raw_ipv4_packet) override; - virtual ErrorOr protocol_connect(OpenFileDescription&) override; - virtual ErrorOr protocol_bind() override; -}; - -} diff --git a/Kernel/Net/VirtIO/VirtIONetworkAdapter.cpp b/Kernel/Net/VirtIO/VirtIONetworkAdapter.cpp deleted file mode 100644 index c42dd6d19db..00000000000 --- a/Kernel/Net/VirtIO/VirtIONetworkAdapter.cpp +++ /dev/null @@ -1,278 +0,0 @@ -/* - * Copyright (c) 2023, Kirill Nikolaev - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include - -namespace Kernel { - -namespace VirtIO { - -// https://docs.oasis-open.org/virtio/virtio/v1.2/csd01/virtio-v1.2-csd01.html - -static constexpr u64 VIRTIO_NET_F_CSUM = (1ull << 0); // Device handles packets with partial checksum. -static constexpr u64 VIRTIO_NET_F_GUEST_CSUM = (1ull << 1); // Driver handles packets with partial checksum. -static constexpr u64 VIRTIO_NET_F_CTRL_GUEST_OFFLOADS = (1ull << 2); // Control channel offloads reconfiguration support. -static constexpr u64 VIRTIO_NET_F_MTU = (1ull << 3); // Device maximum MTU reporting is supported. -static constexpr u64 VIRTIO_NET_F_MAC = (1ull << 5); // Device has given MAC address. -static constexpr u64 VIRTIO_NET_F_GUEST_TSO4 = (1ull << 7); // Driver can receive TSOv4. -static constexpr u64 VIRTIO_NET_F_GUEST_TSO6 = (1ull << 8); // Driver can receive TSOv6. -static constexpr u64 VIRTIO_NET_F_GUEST_ECN = (1ull << 9); // Driver can receive TSO with ECN. -static constexpr u64 VIRTIO_NET_F_GUEST_UFO = (1ull << 10); // Driver can receive UFO. -static constexpr u64 VIRTIO_NET_F_HOST_TSO4 = (1ull << 11); // Device can receive TSOv4. -static constexpr u64 VIRTIO_NET_F_HOST_TSO6 = (1ull << 12); // Device can receive TSOv6. -static constexpr u64 VIRTIO_NET_F_HOST_ECN = (1ull << 13); // Device can receive TSO with ECN. -static constexpr u64 VIRTIO_NET_F_HOST_UFO = (1ull << 14); // Device can receive UFO. -static constexpr u64 VIRTIO_NET_F_MRG_RXBUF = (1ull << 15); // Driver can merge receive buffers. -static constexpr u64 VIRTIO_NET_F_STATUS = (1ull << 16); // Configuration status field is available. -static constexpr u64 VIRTIO_NET_F_CTRL_VQ = (1ull << 17); // Control channel is available. -static constexpr u64 VIRTIO_NET_F_CTRL_RX = (1ull << 18); // Control channel RX mode support. -static constexpr u64 VIRTIO_NET_F_CTRL_VLAN = (1ull << 19); // Control channel VLAN filtering. -static constexpr u64 VIRTIO_NET_F_GUEST_ANNOUNCE = (1ull << 21); // Driver can send gratuitous packets. -static constexpr u64 VIRTIO_NET_F_MQ = (1ull << 22); // Device supports multiqueue with automatic receive steering. -static constexpr u64 VIRTIO_NET_F_CTRL_MAC_ADDR = (1ull << 23); // Set MAC address through control channel. -static constexpr u64 VIRTIO_NET_F_HOST_USO = (1ull << 56); // Device can receive USO packets. -static constexpr u64 VIRTIO_NET_F_HASH_REPORT = (1ull << 57); // Device can report per-packet hash value and a type of calculated hash. -static constexpr u64 VIRTIO_NET_F_GUEST_HDRLEN = (1ull << 59); // Driver can provide the exact hdr_len value. -static constexpr u64 VIRTIO_NET_F_RSS = (1ull << 60); // Device supports RSS with Toeplitz hash calculation -static constexpr u64 VIRTIO_NET_F_RSC_EXT = (1ull << 21); // Device can process duplicated ACKs and report number of coalesced segments and duplicated ACKs. -static constexpr u64 VIRTIO_NET_F_STANDBY = (1ull << 63); // Device may act as a standby for a primary device with the same MAC address. -static constexpr u64 VIRTIO_NET_F_SPEED_DUPLEX = (1ull << 63); // Device reports speed and duplex. - -static constexpr u16 VIRTIO_NET_S_LINK_UP = 1; -static constexpr u16 VIRTIO_NET_S_ANNOUNCE = 2; - -static constexpr u8 VIRTIO_NET_HDR_F_NEEDS_CSUM = 1; -static constexpr u8 VIRTIO_NET_HDR_F_DATA_VALID = 1; -static constexpr u8 VIRTIO_NET_HDR_F_RSC_INFO = 1; -static constexpr u8 VIRTIO_NET_HDR_GSO_NONE = 0; -static constexpr u8 VIRTIO_NET_HDR_GSO_TCPV4 = 1; -static constexpr u8 VIRTIO_NET_HDR_GSO_UDP = 3; -static constexpr u8 VIRTIO_NET_HDR_GSO_TCPV6 = 4; -static constexpr u8 VIRTIO_NET_HDR_GSO_UDP_L4 = 5; -static constexpr u8 VIRTIO_NET_HDR_GSO_ECN = 0x80; - -struct [[gnu::packed]] VirtIONetConfig { - u8 mac[6]; - LittleEndian status; - LittleEndian max_virtqueue_pairs; - LittleEndian mtu; - LittleEndian speed; - u8 duplex; - u8 rss_max_key_size; - LittleEndian rss_max_indirection_table_length; - LittleEndian supported_hash_types; -}; - -struct [[gnu::packed]] VirtIONetHdr { - u8 flags; - u8 gso_type; - LittleEndian hdr_len; - LittleEndian gso_size; - LittleEndian csum_start; - LittleEndian csum_offset; - LittleEndian num_buffers; - u8 frame[0]; -}; - -} - -using namespace VirtIO; - -static constexpr u16 RECEIVEQ = 0; -static constexpr u16 TRANSMITQ = 1; - -static constexpr size_t MAX_RX_FRAME_SIZE = 1514; // Non-jumbo Ethernet frame limit. -static constexpr size_t RX_BUFFER_SIZE = sizeof(VirtIONetHdr) * MAX_RX_FRAME_SIZE; -static constexpr u16 MAX_INFLIGHT_PACKETS = 128; - -UNMAP_AFTER_INIT ErrorOr VirtIONetworkAdapter::probe(PCI::DeviceIdentifier const& pci_device_identifier) -{ - if (pci_device_identifier.hardware_id().vendor_id != PCI::VendorID::VirtIO) - return false; - if (pci_device_identifier.hardware_id().device_id != PCI::DeviceID::VirtIONetAdapter) - return false; - return true; -} - -UNMAP_AFTER_INIT ErrorOr> VirtIONetworkAdapter::create(PCI::DeviceIdentifier const& pci_device_identifier) -{ - auto interface_name = TRY(NetworkingManagement::generate_interface_name_from_pci_address(pci_device_identifier)); - auto pci_transport_link = TRY(VirtIO::PCIeTransportLink::create(pci_device_identifier)); - return TRY(adopt_nonnull_ref_or_enomem(new (nothrow) VirtIONetworkAdapter(interface_name.representable_view(), move(pci_transport_link)))); -} - -UNMAP_AFTER_INIT VirtIONetworkAdapter::VirtIONetworkAdapter(StringView interface_name, NonnullOwnPtr pci_transport_link) - : VirtIO::Device(move(pci_transport_link)) - , NetworkAdapter(interface_name) -{ -} - -UNMAP_AFTER_INIT ErrorOr VirtIONetworkAdapter::initialize(Badge) -{ - m_rx_buffers = TRY(Memory::RingBuffer::try_create("VirtIONetworkAdapter Rx buffer"sv, RX_BUFFER_SIZE * MAX_INFLIGHT_PACKETS)); - m_tx_buffers = TRY(Memory::RingBuffer::try_create("VirtIONetworkAdapter Tx buffer"sv, RX_BUFFER_SIZE * MAX_INFLIGHT_PACKETS)); - - return initialize_virtio_resources(); -} - -UNMAP_AFTER_INIT ErrorOr VirtIONetworkAdapter::initialize_virtio_resources() -{ - dbgln_if(VIRTIO_DEBUG, "VirtIONetworkAdapter: initialize_virtio_resources"); - TRY(Device::initialize_virtio_resources()); - m_device_config = TRY(transport_entity().get_config(VirtIO::ConfigurationType::Device)); - - TRY(negotiate_features([&](u64 supported_features) { - u64 negotiated = 0; - if (is_feature_set(supported_features, VIRTIO_NET_F_STATUS)) - negotiated |= VIRTIO_NET_F_STATUS; - if (is_feature_set(supported_features, VIRTIO_NET_F_MAC)) - negotiated |= VIRTIO_NET_F_MAC; - if (is_feature_set(supported_features, VIRTIO_NET_F_SPEED_DUPLEX)) - negotiated |= VIRTIO_NET_F_SPEED_DUPLEX; - if (is_feature_set(supported_features, VIRTIO_NET_F_MTU)) - negotiated |= VIRTIO_NET_F_MTU; - return negotiated; - })); - - TRY(handle_device_config_change()); - TRY(setup_queues(2)); // receive & transmit - - finish_init(); - - { - // Supply receive buffers. - auto& rx_queue = get_queue(RECEIVEQ); - SpinlockLocker queue_lock(rx_queue.lock()); - VirtIO::QueueChain chain(rx_queue); - while (m_rx_buffers->available_bytes() > RX_BUFFER_SIZE) { - // We know that the RingBuffer will not wraparound in this loop. But it's still awkward. - auto buffer_start = MUST(m_rx_buffers->reserve_space(RX_BUFFER_SIZE)); - VERIFY(chain.add_buffer_to_chain(buffer_start, RX_BUFFER_SIZE, VirtIO::BufferType::DeviceWritable)); - supply_chain_and_notify(RECEIVEQ, chain); - } - } - - return {}; -} - -ErrorOr VirtIONetworkAdapter::handle_device_config_change() -{ - dbgln_if(VIRTIO_DEBUG, "VirtIONetworkAdapter: handle_device_config_change"); - transport_entity().read_config_atomic([&]() { - if (is_feature_accepted(VIRTIO_NET_F_MAC)) { - set_mac_address(MACAddress( - transport_entity().config_read8(*m_device_config, 0x0), - transport_entity().config_read8(*m_device_config, 0x1), - transport_entity().config_read8(*m_device_config, 0x2), - transport_entity().config_read8(*m_device_config, 0x3), - transport_entity().config_read8(*m_device_config, 0x4), - transport_entity().config_read8(*m_device_config, 0x5))); - } - if (is_feature_accepted(VIRTIO_NET_F_STATUS)) { - u16 status = transport_entity().config_read16(*m_device_config, offsetof(VirtIONetConfig, status)); - m_link_up = (status & VIRTIO_NET_S_LINK_UP) != 0; - } - if (is_feature_accepted(VIRTIO_NET_F_MTU)) { - u16 mtu = transport_entity().config_read16(*m_device_config, offsetof(VirtIONetConfig, mtu)); - set_mtu(mtu); - } - if (is_feature_accepted(VIRTIO_NET_F_SPEED_DUPLEX)) { - u32 speed = transport_entity().config_read32(*m_device_config, offsetof(VirtIONetConfig, speed)); - m_link_speed = speed; - u32 duplex = transport_entity().config_read32(*m_device_config, offsetof(VirtIONetConfig, duplex)); - m_link_duplex = duplex == 0x01; - } - }); - return {}; -} - -void VirtIONetworkAdapter::handle_queue_update(u16 queue_index) -{ - dbgln_if(VIRTIO_DEBUG, "VirtIONetworkAdapter: handle_queue_update {}", queue_index); - - if (queue_index == RECEIVEQ) { - // FIXME: Disable interrupts while receiving as recommended by the spec. - auto& queue = get_queue(RECEIVEQ); - SpinlockLocker queue_lock(queue.lock()); - size_t used; - VirtIO::QueueChain popped_chain = queue.pop_used_buffer_chain(used); - - while (!popped_chain.is_empty()) { - VERIFY(popped_chain.length() == 1); - popped_chain.for_each([&](PhysicalAddress addr, size_t length) { - size_t offset = addr.as_ptr() - m_rx_buffers->start_of_region().as_ptr(); - auto* message = reinterpret_cast(m_rx_buffers->vaddr().offset(offset).as_ptr()); - did_receive({ message->frame, length - sizeof(VirtIONetHdr) }); - }); - - supply_chain_and_notify(RECEIVEQ, popped_chain); - popped_chain = queue.pop_used_buffer_chain(used); - } - } else if (queue_index == TRANSMITQ) { - auto& queue = get_queue(TRANSMITQ); - SpinlockLocker queue_lock(queue.lock()); - SpinlockLocker ringbuffer_lock(m_tx_buffers->lock()); - - size_t used; - VirtIO::QueueChain popped_chain = queue.pop_used_buffer_chain(used); - do { - popped_chain.for_each([this](PhysicalAddress address, size_t length) { - m_tx_buffers->reclaim_space(address, length); - }); - popped_chain.release_buffer_slots_to_queue(); - popped_chain = queue.pop_used_buffer_chain(used); - } while (!popped_chain.is_empty()); - } else { - dmesgln("VirtIONetworkAdapter: unexpected update for queue {}", queue_index); - } -} - -static bool copy_data_to_chain(VirtIO::QueueChain& chain, Memory::RingBuffer& ring, u8 const* data, size_t length) -{ - UserOrKernelBuffer buf = UserOrKernelBuffer::for_kernel_buffer(const_cast(data)); - - size_t offset = 0; - while (offset < length) { - PhysicalAddress start_of_chunk; - size_t length_of_chunk; - VERIFY(ring.copy_data_in(buf, offset, length - offset, start_of_chunk, length_of_chunk)); - if (!chain.add_buffer_to_chain(start_of_chunk, length_of_chunk, VirtIO::BufferType::DeviceReadable)) { - // FIXME: Rewind the RingBuffer. - // We are leaving the RingBuffer in an inconsistent state, but interface doesn't allow to undo pushes :(. - return false; - } - offset += length_of_chunk; - } - return true; -} - -void VirtIONetworkAdapter::send_raw(ReadonlyBytes payload) -{ - dbgln_if(VIRTIO_DEBUG, "VirtIONetworkAdapter: send_raw length={}", payload.size()); - - auto& queue = get_queue(TRANSMITQ); - SpinlockLocker queue_lock(queue.lock()); - VirtIO::QueueChain chain(queue); - - SpinlockLocker ringbuffer_lock(m_tx_buffers->lock()); - if (m_tx_buffers->available_bytes() < sizeof(VirtIONetHdr) + payload.size()) { - // We can drop packets that don't fit to apply back pressure on eager senders. - dmesgln("VirtIONetworkAdapter: not enough space in the buffer. Dropping packet"); - return; - } - - // FIXME: Handle errors from pushing to the chain and rewind the RingBuffer. - VirtIONetHdr hdr {}; - VERIFY(copy_data_to_chain(chain, *m_tx_buffers, reinterpret_cast(&hdr), sizeof(hdr))); - VERIFY(copy_data_to_chain(chain, *m_tx_buffers, payload.data(), payload.size())); - - supply_chain_and_notify(TRANSMITQ, chain); -} - -} diff --git a/Kernel/Net/VirtIO/VirtIONetworkAdapter.h b/Kernel/Net/VirtIO/VirtIONetworkAdapter.h deleted file mode 100644 index e830c4c67b1..00000000000 --- a/Kernel/Net/VirtIO/VirtIONetworkAdapter.h +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright (c) 2023, Kirill Nikolaev - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include - -namespace Kernel { - -class VirtIONetworkAdapter - : public VirtIO::Device - , public NetworkAdapter { - -public: - static ErrorOr probe(PCI::DeviceIdentifier const&); - static ErrorOr> create(PCI::DeviceIdentifier const&); - virtual ~VirtIONetworkAdapter() override = default; - - // VirtIO::Device - virtual ErrorOr initialize_virtio_resources() override; - - // NetworkAdapter - virtual StringView class_name() const override { return "VirtIONetworkAdapter"sv; } - virtual Type adapter_type() const override { return Type::Ethernet; } - virtual ErrorOr initialize(Badge) override; - - virtual bool link_up() override { return m_link_up; } - virtual bool link_full_duplex() override { return m_link_duplex; } - virtual i32 link_speed() override { return m_link_speed; } - -private: - explicit VirtIONetworkAdapter(StringView interface_name, NonnullOwnPtr); - - // VirtIO::Device - virtual ErrorOr handle_device_config_change() override; - virtual void handle_queue_update(u16 queue_index) override; - - // NetworkAdapter - virtual void send_raw(ReadonlyBytes) override; - -private: - VirtIO::Configuration const* m_device_config { nullptr }; - - // FIXME: Make atomic as they are read without sync. - // Note that VirtIO::NetworkAdapter may also have the same defect. - bool m_link_up { false }; - i32 m_link_speed { LINKSPEED_INVALID }; - bool m_link_duplex { false }; - - OwnPtr m_rx_buffers; - OwnPtr m_tx_buffers; -}; - -} diff --git a/Kernel/Prekernel/CMakeLists.txt b/Kernel/Prekernel/CMakeLists.txt deleted file mode 100644 index 9b45caeb431..00000000000 --- a/Kernel/Prekernel/CMakeLists.txt +++ /dev/null @@ -1,55 +0,0 @@ -set(SOURCES - UBSanitizer.cpp - ../Library/MiniStdLib.cpp - boot.S - multiboot.S - init.cpp - ../../Userland/Libraries/LibELF/Relocation.cpp - ) - -if ("${SERENITY_ARCH}" STREQUAL "x86_64") - set(PREKERNEL_TARGET kernel_x86-64) -elseif("${SERENITY_ARCH}" STREQUAL "aarch64") - message(SEND_ERROR "Prekernel is not needed on aarch64 and should not be compiled!") -endif() - -set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -static") - -add_library(KernelObject OBJECT IMPORTED) - -set_property(TARGET KernelObject PROPERTY - IMPORTED_OBJECTS ${CMAKE_CURRENT_BINARY_DIR}/../Kernel.o -) - -add_executable(${PREKERNEL_TARGET} ${SOURCES} $) -add_dependencies(${PREKERNEL_TARGET} Kernel) -add_dependencies(${PREKERNEL_TARGET} install_libc_headers) -target_compile_options(${PREKERNEL_TARGET} PRIVATE -no-pie -fno-pic -fno-threadsafe-statics) - -target_link_options(${PREKERNEL_TARGET} PRIVATE LINKER:-T ${CMAKE_CURRENT_SOURCE_DIR}/linker.ld -nostdlib LINKER:--no-pie) -set_target_properties(${PREKERNEL_TARGET} PROPERTIES LINK_DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/linker.ld) - -target_link_libraries(${PREKERNEL_TARGET} PUBLIC GenericClangPlugin) -if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU") - target_link_libraries(${PREKERNEL_TARGET} PRIVATE gcc) -elseif(CMAKE_CXX_COMPILER_ID MATCHES "Clang$") - target_link_libraries(${PREKERNEL_TARGET} PRIVATE clang_rt.builtins) -endif() - -add_custom_command( - TARGET ${PREKERNEL_TARGET} POST_BUILD - COMMAND ${CMAKE_OBJCOPY} -O elf32-i386 ${CMAKE_CURRENT_BINARY_DIR}/${PREKERNEL_TARGET} ${CMAKE_CURRENT_BINARY_DIR}/../Kernel - BYPRODUCTS ${CMAKE_CURRENT_BINARY_DIR}/Prekernel -) - -install(FILES "${CMAKE_CURRENT_BINARY_DIR}/../Kernel" DESTINATION boot) - -# Remove options which the Prekernel environment doesn't support. -get_target_property(PREKERNEL_TARGET_OPTIONS ${PREKERNEL_TARGET} COMPILE_OPTIONS) -list(REMOVE_ITEM PREKERNEL_TARGET_OPTIONS "-fsanitize-coverage=trace-pc") -list(REMOVE_ITEM PREKERNEL_TARGET_OPTIONS "-fsanitize=kernel-address") -set_target_properties(${PREKERNEL_TARGET} PROPERTIES COMPILE_OPTIONS "${PREKERNEL_TARGET_OPTIONS}") - -if (ENABLE_KERNEL_ADDRESS_SANITIZER) - add_compile_definitions(KERNEL_ADDRESS_SANITIZER_ENABLED) -endif() diff --git a/Kernel/Prekernel/Prekernel.h b/Kernel/Prekernel/Prekernel.h deleted file mode 100644 index 7c655d53766..00000000000 --- a/Kernel/Prekernel/Prekernel.h +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright (c) 2021, Gunnar Beutner - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#ifdef __cplusplus -# include -# include -# include -#endif - -#define MAX_KERNEL_SIZE 0x4000000 -#define KERNEL_PD_SIZE 0x31000000 - -// FIXME: This should be using the define from Sections.h, but that currently is not possible -// and causes linker errors, because Sections.h includes BootInfo.h. -#define KERNEL_MAPPING_BASE 0x2000000000 - -#ifdef __cplusplus -namespace Kernel { - -# if ARCH(X86_64) -struct [[gnu::packed]] BootInfo { - u32 start_of_prekernel_image; - u32 end_of_prekernel_image; - u64 physical_to_virtual_offset; - u64 kernel_mapping_base; - u64 kernel_load_base; - u32 gdt64ptr; - u16 code64_sel; - u32 boot_pml4t; - u32 boot_pdpt; - u32 boot_pd0; - u32 boot_pd_kernel; - u64 boot_pd_kernel_pt1023; - u64 kernel_cmdline; - u32 multiboot_flags; - u64 multiboot_memory_map; - u32 multiboot_memory_map_count; - u64 multiboot_module_physical_ptr; - u32 multiboot_module_length; - u64 multiboot_framebuffer_addr; - u32 multiboot_framebuffer_pitch; - u32 multiboot_framebuffer_width; - u32 multiboot_framebuffer_height; - u8 multiboot_framebuffer_bpp; - u8 multiboot_framebuffer_type; -}; -# elif ARCH(AARCH64) -struct BootInfo { }; -# elif ARCH(RISCV64) -struct BootInfo { - FlatPtr mhartid; - PhysicalPtr fdt_phys_addr; -}; -# endif - -} -#endif diff --git a/Kernel/Prekernel/UBSanitizer.cpp b/Kernel/Prekernel/UBSanitizer.cpp deleted file mode 100644 index fe17ea85786..00000000000 --- a/Kernel/Prekernel/UBSanitizer.cpp +++ /dev/null @@ -1,150 +0,0 @@ -/* - * Copyright (c) 2021, Andreas Kling - * Copyright (c) 2020, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include - -using namespace AK::UBSanitizer; - -Atomic AK::UBSanitizer::g_ubsan_is_deadly { true }; - -extern "C" { - -static void print_location(SourceLocation const&) -{ -#if ARCH(X86_64) - asm volatile("cli; hlt"); -#else - for (;;) { } -#endif -} - -void __ubsan_handle_load_invalid_value(InvalidValueData const&, ValueHandle) __attribute__((used)); -void __ubsan_handle_load_invalid_value(InvalidValueData const& data, ValueHandle) -{ - print_location(data.location); -} - -void __ubsan_handle_nonnull_arg(NonnullArgData const&) __attribute__((used)); -void __ubsan_handle_nonnull_arg(NonnullArgData const& data) -{ - print_location(data.location); -} - -void __ubsan_handle_nullability_arg(NonnullArgData const&) __attribute__((used)); -void __ubsan_handle_nullability_arg(NonnullArgData const& data) -{ - print_location(data.location); -} - -void __ubsan_handle_nonnull_return_v1(NonnullReturnData const&, SourceLocation const&) __attribute__((used)); -void __ubsan_handle_nonnull_return_v1(NonnullReturnData const&, SourceLocation const& location) -{ - print_location(location); -} - -void __ubsan_handle_nullability_return_v1(NonnullReturnData const& data, SourceLocation const& location) __attribute__((used)); -void __ubsan_handle_nullability_return_v1(NonnullReturnData const&, SourceLocation const& location) -{ - print_location(location); -} - -void __ubsan_handle_vla_bound_not_positive(VLABoundData const&, ValueHandle) __attribute__((used)); -void __ubsan_handle_vla_bound_not_positive(VLABoundData const& data, ValueHandle) -{ - print_location(data.location); -} - -void __ubsan_handle_add_overflow(OverflowData const&, ValueHandle lhs, ValueHandle rhs) __attribute__((used)); -void __ubsan_handle_add_overflow(OverflowData const& data, ValueHandle, ValueHandle) -{ - print_location(data.location); -} - -void __ubsan_handle_sub_overflow(OverflowData const&, ValueHandle lhs, ValueHandle rhs) __attribute__((used)); -void __ubsan_handle_sub_overflow(OverflowData const& data, ValueHandle, ValueHandle) -{ - print_location(data.location); -} - -void __ubsan_handle_negate_overflow(OverflowData const&, ValueHandle) __attribute__((used)); -void __ubsan_handle_negate_overflow(OverflowData const& data, ValueHandle) -{ - print_location(data.location); -} - -void __ubsan_handle_mul_overflow(OverflowData const&, ValueHandle lhs, ValueHandle rhs) __attribute__((used)); -void __ubsan_handle_mul_overflow(OverflowData const& data, ValueHandle, ValueHandle) -{ - print_location(data.location); -} - -void __ubsan_handle_shift_out_of_bounds(ShiftOutOfBoundsData const&, ValueHandle lhs, ValueHandle rhs) __attribute__((used)); -void __ubsan_handle_shift_out_of_bounds(ShiftOutOfBoundsData const& data, ValueHandle, ValueHandle) -{ - print_location(data.location); -} - -void __ubsan_handle_divrem_overflow(OverflowData const&, ValueHandle lhs, ValueHandle rhs) __attribute__((used)); -void __ubsan_handle_divrem_overflow(OverflowData const& data, ValueHandle, ValueHandle) -{ - print_location(data.location); -} - -void __ubsan_handle_out_of_bounds(OutOfBoundsData const&, ValueHandle) __attribute__((used)); -void __ubsan_handle_out_of_bounds(OutOfBoundsData const& data, ValueHandle) -{ - print_location(data.location); -} - -void __ubsan_handle_type_mismatch_v1(TypeMismatchData const&, ValueHandle) __attribute__((used)); -void __ubsan_handle_type_mismatch_v1(TypeMismatchData const& data, ValueHandle) -{ - print_location(data.location); -} - -void __ubsan_handle_alignment_assumption(AlignmentAssumptionData const&, ValueHandle, ValueHandle, ValueHandle) __attribute__((used)); -void __ubsan_handle_alignment_assumption(AlignmentAssumptionData const& data, ValueHandle, ValueHandle, ValueHandle) -{ - print_location(data.location); -} - -void __ubsan_handle_builtin_unreachable(UnreachableData const&) __attribute__((used)); -void __ubsan_handle_builtin_unreachable(UnreachableData const& data) -{ - print_location(data.location); -} - -void __ubsan_handle_missing_return(UnreachableData const&) __attribute__((used)); -void __ubsan_handle_missing_return(UnreachableData const& data) -{ - print_location(data.location); -} - -void __ubsan_handle_implicit_conversion(ImplicitConversionData const&, ValueHandle, ValueHandle) __attribute__((used)); -void __ubsan_handle_implicit_conversion(ImplicitConversionData const& data, ValueHandle, ValueHandle) -{ - print_location(data.location); -} - -void __ubsan_handle_invalid_builtin(InvalidBuiltinData const) __attribute__((used)); -void __ubsan_handle_invalid_builtin(InvalidBuiltinData const data) -{ - print_location(data.location); -} - -void __ubsan_handle_pointer_overflow(PointerOverflowData const&, ValueHandle, ValueHandle) __attribute__((used)); -void __ubsan_handle_pointer_overflow(PointerOverflowData const& data, ValueHandle, ValueHandle) -{ - print_location(data.location); -} - -void __ubsan_handle_function_type_mismatch(FunctionTypeMismatchData const&, ValueHandle) __attribute__((used)); -void __ubsan_handle_function_type_mismatch(FunctionTypeMismatchData const& data, ValueHandle) -{ - print_location(data.location); -} -} diff --git a/Kernel/Prekernel/boot.S b/Kernel/Prekernel/boot.S deleted file mode 100644 index 75eed5c1d3d..00000000000 --- a/Kernel/Prekernel/boot.S +++ /dev/null @@ -1,654 +0,0 @@ -#include -#include - -.code32 - -.section .stack, "aw", @nobits -stack_bottom: -.skip 32768 -stack_top: - -.global kernel_cmdline -kernel_cmdline: -.skip 4096 - -.section .page_tables, "aw", @nobits -.align 4096 -#if ARCH(X86_64) -.global boot_pml4t -boot_pml4t: -.skip 4096 -#endif -.global boot_pdpt -boot_pdpt: -.skip 4096 -.global boot_pd0 -boot_pd0: -.skip (4096 * 4) -.global boot_pd0_pts -boot_pd0_pts: -.skip 4096 * (MAX_KERNEL_SIZE >> 21) -.global boot_pd_kernel -boot_pd_kernel: -.skip 4096 -.global boot_pd_kernel_pt0 -boot_pd_kernel_pt0: -.skip 4096 -.global boot_pd_kernel_image_pts -boot_pd_kernel_image_pts: -.skip 4096 * (MAX_KERNEL_SIZE >> 21) -.global boot_pd_kernel_pt1023 -boot_pd_kernel_pt1023: -.skip 4096 - -.section .text - -.global start -.type start, @function - -.extern init -.type init, @function - -.global reload_cr3 -.type reload_cr3, @function - -.extern multiboot_info_ptr -.type multiboot_info_ptr, @object - -/* - construct the following (64-bit PML4T) page table layout: - (the PML4T part is not used for 32-bit x86) - -pml4t: - - 0: pdpt (0-512GB) - -pdpt - - 0: boot_pd0 (0-1GB) - 1: boot_pd0 + 4096 (1-2GB) - 2: boot_pd0 + 4096 * 2 (2-3GB) - 3: boot_pd0 + 4096 * 3 (3-4GB) - -boot_pd0 : 512 PDEs - - boot_pd0_pts (0MB - MAX_KERNEL_SIZE) (id 512 4KB pages) - -the page tables each contain 512 PTEs that map individual 4KB pages - -*/ - -gdt64: - .quad 0 -gdt64code: - .quad (1<<43) | (1<<44) | (1<<47) | (1<<53) /* executable, code segment, present, 64-bit */ -.global gdt64ptr -gdt64ptr: - .short . - gdt64 - 1 - .quad gdt64 - -.set code64_sel_value, gdt64code - gdt64 - -.global code64_sel -code64_sel: -.short code64_sel_value - -start: - jmp real_start - -/* - param 1: pointer to C string - returns: Length of string (including null byte) -*/ -print_no_halt: - pushl %ebp - movl %esp, %ebp - - pushl %esi - pushl %ecx - - movl 8(%ebp), %esi - - mov $0xb8000, %ecx /* VRAM address. */ - mov $0x07, %ah /* grey-on-black text. */ - -.Lprint_str_loop: - lodsb /* Loads a byte from address at %esi into %al and increments %esi. */ - - test %al, %al - jz .Lprint_str_end - - movw %ax, (%ecx) - add $2, %ecx - - jmp .Lprint_str_loop -.Lprint_str_end: - - mov %esi, %eax - sub 8(%ebp), %eax - - popl %ecx - popl %esi - - movl %ebp, %esp - popl %ebp - ret - - - -/* - this function assumes that paging is disabled (or everything is mapped 1:1) - param 1: pointer to string ended with null terminator (C string) -*/ -print_and_halt: - -/* from now on, we don't really care about booting because we are missing required CPU features such as PAE or long mode. - the flow from now is like so: - 1. Copy all necessary parts to low memory section in RAM - 2. Jump to that section - 3. In that section we do: - a. exit protected mode to pure 16 bit real mode - b. load the " is not supported" String, call the BIOS print to screen service - c. halt -*/ - -.equ COPIED_STRING_LOCATION, 0x400 -.equ GDT_REAL_MODE_LOCATION, 0x45000 -.equ EXITING_PROTECTED_MODE_CODE_LOCATION, 0x10000 -.equ REAL_MODE_CODE, 0x500 -.equ PROTECTED_MODE_16_BIT_CODE, 0x600 - movl %esp, %ebp - movl 4(%ebp), %esi - - /* Print string using non-destructive methods */ - pushl %esi - call print_no_halt - addl $4, %esp - - /* print_no_halt returns the string length (including null byte) in eax. */ - mov %eax, %ecx - movw %cx, (COPIED_STRING_LOCATION) /* Store string length for later use. */ - - /* Copy string into lower memory */ - mov 4(%ebp), %esi - mov $COPIED_STRING_LOCATION + 2, %edi - rep movsb - - /* Copy gdt_table_real_mode to low memory section */ - movl $gdt_table_real_mode, %eax - movl $gdt_table_real_mode_end, %ebx - - movl %ebx, %ecx - sub %eax, %ecx - mov %eax, %esi /* source address of the code */ - mov $GDT_REAL_MODE_LOCATION, %edi /* destination address of the code */ - rep movsb - - /* Copy protected_mode_16_bit to real_mode to low memory section */ - movl $protected_mode_16_bit, %eax - movl $real_mode, %ebx - - movl %ebx, %ecx - sub %eax, %ecx - mov %eax, %esi /* source address of the code */ - mov $PROTECTED_MODE_16_BIT_CODE, %edi /* destination address of the code */ - rep movsb - - /* Copy real_mode to end_of_print_and_halt_function to low memory section */ - movl $real_mode, %eax - movl $end_of_print_and_halt_function, %ebx - - movl %ebx, %ecx - sub %eax, %ecx - mov %eax, %esi /* source address of the code */ - mov $REAL_MODE_CODE, %edi /* destination address of the code */ - rep movsb - - - /* Copy all opcodes from exiting_real_mode label to protected_mode_16_bit label to low memory RAM */ - movl $exiting_real_mode, %eax - movl $protected_mode_16_bit, %ebx - - movl %ebx, %ecx - sub %eax, %ecx - mov %eax, %esi /* source address of the code */ - mov $EXITING_PROTECTED_MODE_CODE_LOCATION, %edi /* destination address of the code */ - pushl %edi - rep movsb - popl %edi - pushl %edi - ret - -gdt_table_real_mode: - .quad 0 /* Empty entry */ - - .short 0xffff - .short 0 - .byte 0 - .byte 0b10011010 - .byte 0b00001111 - .byte 0x0 - - .short 0xffff - .short 0 - .byte 0 - .byte 0b10010010 - .byte 0b00001111 - .byte 0x0 -gdt_table_real_mode_end: - -no_long_mode_string: - .asciz "Your computer does not support long mode (64-bit mode). Halting!" - -no_pae_string: - .asciz "Your computer does not support PAE. Halting!" - -pentium_m_forcepae_string: - .asciz "Intel Pentium M detected. Assuming present but unadvertised PAE support." - -kernel_image_too_big_string: - .asciz "Error: Kernel Image too big for memory slot. Halting!" - -/* - This part is completely standalone - it doesn't involve any location from this - near code. It uses arbitrary locations in the low memory section of the RAM. - We don't really worry about where are these locations, because we only want to quickly - print a string and halt. -*/ -.code32 -exiting_real_mode: - - /* Build IDT pointer and load it */ - mov $0x50000, %eax - pushl %eax - movl $0x3ff, 0(%eax) - add $2, %eax - movl $0, 0(%eax) - popl %eax - lidt (%eax) - - /* Build GDT pointer and load it */ - mov $0x40000, %eax - pushl %eax - movl $32, 0(%eax) - add $2, %eax - movl $GDT_REAL_MODE_LOCATION, 0(%eax) - popl %eax - lgdt (%eax) - - /* far jump to protected_mode_16_bit in 0x5000 */ - pushw $8 - push $PROTECTED_MODE_16_BIT_CODE - lret - hlt - -.code16 -protected_mode_16_bit: - xor %eax, %eax - movl $0x10, %eax - movw %ax, %ds - and $0xFE, %al /* switch to pure real mode */ - mov %eax, %cr0 - mov $0x10, %eax - movl %eax, %cr0 - - pushw $0 - push $REAL_MODE_CODE - lret - hlt - -real_mode: - movw $0x7000, %ax - movl $0x0000, %esp - movw %ax, %ss - - xor %ax, %ax - movw %ax, %ds - movw %ax, %es - movw %ax, %fs - movw %ax, %gs - - mov $0x3, %ax - int $0x10 - - movb $0x13, %ah - movb $0x0, %bh - movb $0xf, %bl - movw (COPIED_STRING_LOCATION), %cx - movw $0, %dx - movw $COPIED_STRING_LOCATION + 2, %bp - int $0x10 - - movl $0xdeadcafe, %ebx - cli - hlt -end_of_print_and_halt_function: - -.code32 -real_start: - cli - cld - mov $end_of_prekernel_image, %esi - cmp $MAX_KERNEL_SIZE, %esi - jbe kernel_not_too_large - - movl $kernel_image_too_big_string, %esi - pushl %esi - call print_and_halt - /* We should not return, but just in case, halt */ - hlt - -kernel_not_too_large: - /* test for PAE presence, save the most important registers from corruption */ - pushl %eax - pushl %edx - pushl %ebx - - movl $0x1, %eax /* PAE presence is in CPUID input 0x1 */ - cpuid - testl $(1 << 6), %edx /* Test if the PAE-bit, which is bit 6, is set in the edx register. */ - jnz pae_supported /* If the bit is not set, there is no PAE capability. */ - - /* We might have a Pentium M, which supports PAE but doesn't advertise it. */ - call is_pentium_m_with_hidden_pae - test %eax, %eax - jz .Lskip_forcepae - - /* Print a warning message, but continue. */ - pushl $pentium_m_forcepae_string - call print_no_halt - subl $4, %esp - jmp pae_supported -.Lskip_forcepae: - - /* Since there is no PAE capability, halt with an error message */ - movl $no_pae_string, %esi - pushl %esi - call print_and_halt - /* We should not return, but just in case, halt */ - hlt - -pae_supported: - movl $0x80000001, %eax - cpuid - testl $(1 << 29), %edx /* Test if the LM-bit, which is bit 29, is set in the edx register. */ - jnz long_mode_supported /* If LM-bit is not enabled, there is no long mode. */ - - /* Since there is no long mode, halt with an error message */ - movl $no_long_mode_string, %esi - pushl %esi - call print_and_halt - /* We should not return, but just in case, halt */ - hlt - - -/* If both PAE and long mode is supported, continue with booting the system */ - -long_mode_supported: - /* restore the pushed registers and continue with booting */ - popl %ebx - popl %edx - popl %eax - - /* We don't know where the bootloader might have put the command line. - * It might be at an inconvenient location that we're not about to map, - * so let's just copy it to a convenient location while we have the whole - * memory space identity-mapped anyway. :^) - */ - - movl %ebx, %esi - addl $16, %esi - movl (%esi), %esi - movl $1024, %ecx - movl $kernel_cmdline, %edi - rep movsl - - /* clear pml4t */ - movl $boot_pml4t, %edi - movl $1024, %ecx - xorl %eax, %eax - rep stosl - - /* set up pml4t[0] */ - movl $boot_pml4t, %edi - movl $boot_pdpt, 0(%edi) - /* R/W + Present */ - orl $0x3, 0(%edi) - - /* clear pdpt */ - movl $boot_pdpt, %edi - movl $1024, %ecx - xorl %eax, %eax - rep stosl - - /* set up pdpt[0]..pdpt[3] */ - movl $boot_pdpt, %edi - movl $(boot_pd0 + 3), 0(%edi) - movl $(boot_pd0 + 4096 + 3), 8(%edi) - movl $(boot_pd0 + 4096 * 2 + 3), 16(%edi) - movl $(boot_pd0 + 4096 * 3 + 3), 24(%edi) - - /* clear pd0 */ - movl $boot_pd0, %edi - movl $4096, %ecx - xorl %eax, %eax - rep stosl - - /* clear pd0's PTs */ - movl $boot_pd0_pts, %edi - movl $(1024 * (MAX_KERNEL_SIZE >> 21)), %ecx - xorl %eax, %eax - rep stosl - - /* add boot_pd0_pts to boot_pd0 */ - movl $(MAX_KERNEL_SIZE >> 21), %ecx - movl $boot_pd0, %edi - movl $boot_pd0_pts, %eax - -1: - movl %eax, 0(%edi) - /* R/W + Present */ - orl $0x3, 0(%edi) - addl $8, %edi - addl $4096, %eax - loop 1b - - /* identity map the 0MB to MAX_KERNEL_SIZE range */ - movl $(512 * (MAX_KERNEL_SIZE >> 21)), %ecx - movl $boot_pd0_pts, %edi - xorl %eax, %eax - -1: - movl %eax, 0(%edi) - /* R/W + Present */ - orl $0x3, 0(%edi) - addl $8, %edi - addl $4096, %eax - loop 1b - - /* Map the rest with 2MiB pages */ - /* add boot_pd0_pts to boot_pd0 */ - movl $(2048 - (MAX_KERNEL_SIZE >> 21)), %ecx - movl $(boot_pd0 + (MAX_KERNEL_SIZE >> 21) * 8), %edi - /* R/W + Present + 2 MiB */ - movl $(MAX_KERNEL_SIZE | 0x83), %eax - -1: - movl %eax, 0(%edi) - addl $8, %edi - addl $(1 << 21), %eax - loop 1b - - /* point CR3 to PML4T */ - movl $boot_pml4t, %eax - - movl %eax, %cr3 - - /* enable PAE + PSE */ - movl %cr4, %eax - orl $0x60, %eax - movl %eax, %cr4 - -1: - /* Enter Long-mode! ref(https://wiki.osdev.org/Setting_Up_Long_Mode)*/ - mov $0xC0000080, %ecx /* Set the C-register to 0xC0000080, which is the EFER MSR.*/ - rdmsr /* Read from the model-specific register.*/ - or $(1 << 8), %eax /* Set the LM-bit which is the 9th bit (bit 8).*/ - wrmsr /* Write to the model-specific register.*/ - - /* enable PG */ - movl %cr0, %eax - orl $0x80000000, %eax - movl %eax, %cr0 - - /* set up stack */ - mov $stack_top, %esp - and $-16, %esp - - /* Now we are in 32-bit compatibility mode, We still need to load a 64-bit GDT */ - mov $gdt64ptr, %eax - lgdt (%eax) - ljmpl $code64_sel_value, $1f - -.code64 -1: - movl %ebx, %ebx - movq %rbx, multiboot_info_ptr - - mov $0, %ax - mov %ax, %ss - mov %ax, %ds - mov %ax, %es - mov %ax, %fs - mov %ax, %gs - - call reload_cr3 - call init - - cli -loop: - hlt - jmp loop - -reload_cr3: - pushq %rax - mov %cr3, %rax - mov %rax, %cr3 - popq %rax - ret - - -.code32 - -/* Determines if the CPU is made by Intel */ -is_intel_cpu: - pushl %ebp - movl %esp, %ebp - - pushl %ebx - - xorl %eax, %eax - cpuid - - xorl %eax, %eax - cmpl $0x756e6547, %ebx /* "Genu" */ - jnz .Lis_intel_cpu_end - cmpl $0x49656e69, %edx /* "ineI" */ - jnz .Lis_intel_cpu_end - cmpl $0x6c65746e, %ecx /* "ntel" */ - jnz .Lis_intel_cpu_end - - movl $1, %eax - -.Lis_intel_cpu_end: - popl %ebx - - movl %ebp, %esp - popl %ebp - - ret - -/* Fetches the CPU family (eax) and model (edx) */ -get_cpu_model_family: - pushl %ebp - movl %esp, %ebp - - pushl %ebx - - movl $0x1, %eax - cpuid - movl %eax, %ebx - movl %eax, %ecx - movl %eax, %edx - - /* Bit 8 - 11: Processor Family */ - shrl $8, %eax - andl $0xf, %eax - - /* Bit 4 - 7: Processor Model */ - shrl $4, %edx - andl $0xf, %edx - - /* Bit 16 - 19: Extended Model ID */ - /* (only applies if Family is 6 or 15) */ - cmpl $6, %eax - jz .Ldo_ext_model - cmpl $15, %eax - jz .Ldo_ext_model - jmp .Lskip_ext_model -.Ldo_ext_model: - shrl $16, %ebx - andl $0xf, %ebx - shll $4, %ebx - addl %ebx, %edx -.Lskip_ext_model: - - /* Bit 20 - 27: Extended Family */ - /* (only applies if Family is 15) */ - cmpl $15, %eax - jnz .Lskip_ext_family - shrl $20, %ecx - andl $0xff, %ecx - addl %ecx, %eax -.Lskip_ext_family: - - popl %ebx - - movl %ebp, %esp - popl %ebp - - ret - -/* Determines if the CPU is an Intel Pentium M with hidden PAE flag. */ -is_pentium_m_with_hidden_pae: - pushl %ebp - movl %esp, %ebp - - /* Check the manufacturer string. */ - call is_intel_cpu - testl %eax, %eax - jz .Lis_pentium_m_end - - /* Check the processor model. */ - call get_cpu_model_family - movl %eax, %ecx /* Free up eax for the return value. */ - xorl %eax, %eax - - cmpl $6, %ecx /* Family 6: Big Cores */ - jnz .Lis_pentium_m_end - - cmpl $9, %edx /* Model 9: Pentium M (Banias) */ - jz .Lpass_model_check - cmpl $13, %edx /* Model 13: Pentium M (Dothan) */ - jz .Lpass_model_check - jmp .Lis_pentium_m_end -.Lpass_model_check: - - /* We are a Pentium M. */ - movl $1, %eax - -.Lis_pentium_m_end: - movl %ebp, %esp - popl %ebp - - ret diff --git a/Kernel/Prekernel/init.cpp b/Kernel/Prekernel/init.cpp deleted file mode 100644 index 93b74aaf32f..00000000000 --- a/Kernel/Prekernel/init.cpp +++ /dev/null @@ -1,303 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * Copyright (c) 2021, Gunnar Beutner - * Copyright (c) 2021, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include -#include -#include - -#if ARCH(X86_64) -# include -# include -#endif - -// Defined in the linker script -extern uintptr_t __stack_chk_guard; -uintptr_t __stack_chk_guard __attribute__((used)); -extern "C" [[noreturn]] void __stack_chk_fail(); - -extern "C" u8 start_of_prekernel_image[]; -extern "C" u8 end_of_prekernel_image[]; - -extern "C" u8 _binary_Kernel_standalone_start[]; -extern "C" u8 end_of_prekernel_image_after_kernel_image[]; - -extern "C" u8 gdt64ptr[]; -extern "C" u16 code64_sel; -extern "C" u64 boot_pml4t[512]; -extern "C" u64 boot_pdpt[512]; -extern "C" u64 boot_pd0[512]; -extern "C" u64 boot_pd0_pts[512 * (MAX_KERNEL_SIZE >> 21 & 0x1ff)]; -extern "C" u64 boot_pd_kernel[512]; -extern "C" u64 boot_pd_kernel_pt0[512]; -extern "C" u64 boot_pd_kernel_image_pts[512 * (MAX_KERNEL_SIZE >> 21 & 0x1ff)]; -extern "C" u64 boot_pd_kernel_pt1023[512]; -extern "C" char const kernel_cmdline[4096]; - -extern "C" void reload_cr3(); - -extern "C" { -multiboot_info_t* multiboot_info_ptr; -} - -[[noreturn]] static void halt() -{ - asm volatile("hlt"); - __builtin_unreachable(); -} - -void __stack_chk_fail() -{ - halt(); -} - -void __assertion_failed(char const*, char const*, unsigned int, char const*) -{ - halt(); -} - -namespace Kernel { - -// boot.S expects these functions to exactly have the following signatures. -// We declare them here to ensure their signatures don't accidentally change. -extern "C" [[noreturn]] void init(); - -// SerenityOS Pre-Kernel Environment C++ entry point :^) -// -// This is where C++ execution begins, after boot.S transfers control here. -// - -u64 generate_secure_seed(); - -static void memmove_virt(void* dest_virt, FlatPtr dest_phys, void* src, size_t n) -{ - if (dest_phys < (FlatPtr)src) { - u8* pd = (u8*)dest_virt; - u8 const* ps = (u8 const*)src; - for (; n--;) - *pd++ = *ps++; - return; - } - - u8* pd = (u8*)dest_virt; - u8 const* ps = (u8 const*)src; - for (pd += n, ps += n; n--;) - *--pd = *--ps; -} - -extern "C" [[noreturn]] void init() -{ - u32 initrd_module_start = 0; - u32 initrd_module_end = 0; - if (multiboot_info_ptr->mods_count > 0) { - // We only consider the first specified multiboot module, and ignore - // the rest of the modules. - multiboot_module_entry_t* initrd_module = (multiboot_module_entry_t*)(FlatPtr)multiboot_info_ptr->mods_addr; - if (initrd_module->start > initrd_module->end) - halt(); - - initrd_module_start = initrd_module->start; - initrd_module_end = initrd_module->end; - } - - u8* kernel_image = _binary_Kernel_standalone_start; - // copy the ELF header and program headers because we might end up overwriting them - Elf_Ehdr kernel_elf_header = *(Elf_Ehdr*)kernel_image; - Elf_Phdr kernel_program_headers[16]; - if (kernel_elf_header.e_phnum > array_size(kernel_program_headers)) - halt(); - __builtin_memcpy(kernel_program_headers, kernel_image + kernel_elf_header.e_phoff, sizeof(Elf_Phdr) * kernel_elf_header.e_phnum); - - FlatPtr kernel_physical_base = (FlatPtr)0x200000; - FlatPtr default_kernel_load_base = KERNEL_MAPPING_BASE + kernel_physical_base; - - FlatPtr kernel_load_base = default_kernel_load_base; - - if (__builtin_strstr(kernel_cmdline, "disable_kaslr") == nullptr) { - FlatPtr maximum_offset = (FlatPtr)KERNEL_PD_SIZE - MAX_KERNEL_SIZE - 2 * MiB; // The first 2 MiB are used for mapping the pre-kernel -#ifdef KERNEL_ADDRESS_SANITIZER_ENABLED - // To allow for easy mapping between the kernel virtual addresses and KASAN shadow memory, - // we map shadow memory at the very end of the virtual range, so that we can index into it - // using just an offset. To ensure this range is free when needed, we restrict the possible - // KASLR range when KASAN is enabled to make sure we don't use the end of the virtual range. - maximum_offset -= ceil_div(maximum_offset, 9ul); -#endif - kernel_load_base += (generate_secure_seed() % maximum_offset); - kernel_load_base &= ~(2 * MiB - 1); - } - - FlatPtr kernel_load_end = 0; - for (size_t i = 0; i < kernel_elf_header.e_phnum; i++) { - auto& kernel_program_header = kernel_program_headers[i]; - if (kernel_program_header.p_type != PT_LOAD) - continue; - auto start = kernel_load_base + kernel_program_header.p_vaddr; - auto end = start + kernel_program_header.p_memsz; - if (start < (FlatPtr)end_of_prekernel_image) - halt(); - if (kernel_physical_base + kernel_program_header.p_paddr < (FlatPtr)end_of_prekernel_image) - halt(); - if (end > kernel_load_end) - kernel_load_end = end; - } - - // align to 1GB - FlatPtr kernel_mapping_base = kernel_load_base & ~(FlatPtr)0x3fffffff; - - VERIFY(kernel_load_base % 0x1000 == 0); - VERIFY(kernel_load_base >= kernel_mapping_base + kernel_physical_base); - - int pdpt_flags = 0x3; - - boot_pdpt[(kernel_mapping_base >> 30) & 0x1ffu] = (FlatPtr)boot_pd_kernel | pdpt_flags; - - boot_pd_kernel[0] = (FlatPtr)boot_pd_kernel_pt0 | 0x3; - - for (FlatPtr vaddr = kernel_load_base; vaddr <= kernel_load_end; vaddr += PAGE_SIZE * 512) - boot_pd_kernel[(vaddr - kernel_mapping_base) >> 21] = (FlatPtr)(&boot_pd_kernel_image_pts[(vaddr - kernel_load_base) >> 12]) | 0x3; - - __builtin_memset(boot_pd_kernel_pt0, 0, sizeof(boot_pd_kernel_pt0)); - - VERIFY((size_t)end_of_prekernel_image < array_size(boot_pd_kernel_pt0) * PAGE_SIZE); - - /* pseudo-identity map 0M - end_of_prekernel_image */ - for (size_t i = 0; i < (FlatPtr)end_of_prekernel_image / PAGE_SIZE; i++) - boot_pd_kernel_pt0[i] = i * PAGE_SIZE | 0x3; - - __builtin_memset(boot_pd_kernel_image_pts, 0, sizeof(boot_pd_kernel_image_pts)); - - for (size_t i = 0; i < kernel_elf_header.e_phnum; i++) { - auto& kernel_program_header = kernel_program_headers[i]; - if (kernel_program_header.p_type != PT_LOAD) - continue; - for (FlatPtr offset = 0; offset < kernel_program_header.p_memsz; offset += PAGE_SIZE) { - auto pte_index = ((kernel_load_base & 0x1fffff) + kernel_program_header.p_vaddr + offset) >> 12; - boot_pd_kernel_image_pts[pte_index] = (kernel_physical_base + kernel_program_header.p_paddr + offset) | 0x3; - } - } - - boot_pd_kernel[511] = (FlatPtr)boot_pd_kernel_pt1023 | 0x3; - - // Fill-in multiboot-related info before loading kernel as to avoid accidentally - // overwriting mbi end as to avoid to check whether it's mapped after reloading page tables. - BootInfo info {}; - - auto adjust_by_mapping_base = [kernel_mapping_base](auto ptr) { - return (decltype(ptr))((FlatPtr)ptr + kernel_mapping_base); - }; - - info.multiboot_flags = multiboot_info_ptr->flags; - info.multiboot_memory_map = adjust_by_mapping_base((FlatPtr)multiboot_info_ptr->mmap_addr); - info.multiboot_memory_map_count = multiboot_info_ptr->mmap_length / sizeof(multiboot_memory_map_t); - - if (initrd_module_start != 0 && initrd_module_end != 0) { - info.multiboot_module_physical_ptr = initrd_module_start; - info.multiboot_module_length = initrd_module_end - initrd_module_start; - } - - if ((multiboot_info_ptr->flags & MULTIBOOT_INFO_FRAMEBUFFER_INFO) != 0) { - info.multiboot_framebuffer_addr = multiboot_info_ptr->framebuffer_addr; - info.multiboot_framebuffer_pitch = multiboot_info_ptr->framebuffer_pitch; - info.multiboot_framebuffer_width = multiboot_info_ptr->framebuffer_width; - info.multiboot_framebuffer_height = multiboot_info_ptr->framebuffer_height; - info.multiboot_framebuffer_bpp = multiboot_info_ptr->framebuffer_bpp; - info.multiboot_framebuffer_type = multiboot_info_ptr->framebuffer_type; - } - - reload_cr3(); - - int backwards = kernel_physical_base >= (FlatPtr)kernel_image; - - for (ssize_t i = 0; i < kernel_elf_header.e_phnum; i++) { - auto& kernel_program_header = kernel_program_headers[backwards ? kernel_elf_header.e_phnum - 1 - i : i]; - if (kernel_program_header.p_type != PT_LOAD) - continue; - memmove_virt((u8*)kernel_load_base + kernel_program_header.p_vaddr, - kernel_physical_base + kernel_program_header.p_vaddr, - kernel_image + kernel_program_header.p_offset, kernel_program_header.p_filesz); - } - - for (ssize_t i = kernel_elf_header.e_phnum - 1; i >= 0; i--) { - auto& kernel_program_header = kernel_program_headers[i]; - if (kernel_program_header.p_type != PT_LOAD) - continue; - __builtin_memset((u8*)kernel_load_base + kernel_program_header.p_vaddr + kernel_program_header.p_filesz, 0, kernel_program_header.p_memsz - kernel_program_header.p_filesz); - } - - info.start_of_prekernel_image = (PhysicalPtr)start_of_prekernel_image; - info.end_of_prekernel_image = (PhysicalPtr)end_of_prekernel_image; - info.physical_to_virtual_offset = kernel_load_base - kernel_physical_base; - info.kernel_mapping_base = kernel_mapping_base; - info.kernel_load_base = kernel_load_base; -#if ARCH(X86_64) - info.gdt64ptr = (PhysicalPtr)gdt64ptr; - info.code64_sel = code64_sel; - info.boot_pml4t = (PhysicalPtr)boot_pml4t; -#endif - info.boot_pdpt = (PhysicalPtr)boot_pdpt; - info.boot_pd0 = (PhysicalPtr)boot_pd0; - info.boot_pd_kernel = (PhysicalPtr)boot_pd_kernel; - info.boot_pd_kernel_pt1023 = (FlatPtr)adjust_by_mapping_base(boot_pd_kernel_pt1023); - info.kernel_cmdline = (FlatPtr)adjust_by_mapping_base(kernel_cmdline); - - asm( - "mov %0, %%rax\n" - "add %%rax, %%rsp" ::"g"(kernel_mapping_base) - : "ax"); - - // unmap the 0-1MB region - for (size_t i = 0; i < 256; i++) - boot_pd0_pts[i] = 0; - - // unmap the end_of_prekernel_image - MAX_KERNEL_SIZE region - for (FlatPtr vaddr = (FlatPtr)end_of_prekernel_image; vaddr < MAX_KERNEL_SIZE; vaddr += PAGE_SIZE) - boot_pd0_pts[vaddr >> 12] = 0; - - reload_cr3(); - - ELF::perform_relative_relocations(kernel_load_base); - - void (*entry)(BootInfo const&) = (void (*)(BootInfo const&))(kernel_load_base + kernel_elf_header.e_entry); - entry(*adjust_by_mapping_base(&info)); - - __builtin_unreachable(); -} - -u64 generate_secure_seed() -{ - u32 seed = 0xFEEBDAED; - -#if ARCH(X86_64) - CPUID processor_info(0x1); - if (processor_info.edx() & (1 << 4)) // TSC - seed ^= read_tsc(); - - if (processor_info.ecx() & (1 << 30)) // RDRAND - seed ^= read_rdrand(); - - CPUID extended_features(0x7); - if (extended_features.ebx() & (1 << 18)) // RDSEED - seed ^= read_rdseed(); -#else -# warning No native randomness source available for this architecture -#endif - - seed ^= multiboot_info_ptr->mods_addr; - seed ^= multiboot_info_ptr->framebuffer_addr; - - return seed; -} - -// Define some Itanium C++ ABI methods to stop the linker from complaining. -// If we actually call these something has gone horribly wrong -void* __dso_handle __attribute__((visibility("hidden"))); - -} diff --git a/Kernel/Prekernel/linker.ld b/Kernel/Prekernel/linker.ld deleted file mode 100644 index 32cf31436e5..00000000000 --- a/Kernel/Prekernel/linker.ld +++ /dev/null @@ -1,56 +0,0 @@ -ENTRY(start) - -PHDRS -{ - boot_text PT_LOAD ; - text PT_LOAD ; - data PT_LOAD ; - bss PT_LOAD ; -} - -SECTIONS -{ - . = 0x00100000; - - start_of_prekernel_image = .; - - .boot_text ALIGN(4K) : AT (ADDR(.boot_text)) - { - KEEP(*(.multiboot)) - } :boot_text - - .text ALIGN(4K) : AT (ADDR(.text)) - { - start_of_prekernel_text = .; - *(.text*) - } :text - - .rodata ALIGN(4K) : AT (ADDR(.rodata)) - { - *(.rodata*) - } :data - - .data ALIGN(4K) : AT (ADDR(.data)) - { - *(.data*) - } :data - - .bss ALIGN(4K) (NOLOAD) : AT (ADDR(.bss)) - { - *(COMMON) - *(.bss) - *(.stack) - *(.page_tables) - } :bss - - end_of_prekernel_image = .; - - .Kernel_image ALIGN(4K) : AT (ADDR(.Kernel_image)) - { - _binary_Kernel_standalone_start = .; - KEEP(*(.Kernel_image)) - } - - end_of_prekernel_image_after_kernel_image = .; - -} diff --git a/Kernel/Prekernel/multiboot.S b/Kernel/Prekernel/multiboot.S deleted file mode 100644 index 7d3f1d747d5..00000000000 --- a/Kernel/Prekernel/multiboot.S +++ /dev/null @@ -1,28 +0,0 @@ -.code32 -.set MULTIBOOT_MAGIC, 0x1badb002 -.set MULTIBOOT_PAGE_ALIGN, 0x1 -.set MULTIBOOT_MEMORY_INFO, 0x2 -.set MULTIBOOT_VIDEO_MODE, 0x4 -.set multiboot_flags, MULTIBOOT_PAGE_ALIGN | MULTIBOOT_MEMORY_INFO | MULTIBOOT_VIDEO_MODE -.set multiboot_checksum, -(MULTIBOOT_MAGIC + multiboot_flags) - -.section .multiboot, "a" -.align 4 - -.long MULTIBOOT_MAGIC -.long multiboot_flags -.long multiboot_checksum - - -/* for MULTIBOOT_AOUT_KLUDGE, just a dummy */ -.long 0x00000000 /* header_addr */ -.long 0x00000000 /* load_addr */ -.long 0x00000000 /* load_end_addr */ -.long 0x00000000 /* bss_end_addr */ -.long 0x00000000 /* entry_addr */ - -/* for MULTIBOOT_VIDEO_MODE */ -.long 0x00000000 /* mode_type */ -.long 1280 /* width */ -.long 1024 /* height */ -.long 32 /* depth */ diff --git a/Kernel/SanCov.cpp b/Kernel/SanCov.cpp deleted file mode 100644 index 43bfdb57ad2..00000000000 --- a/Kernel/SanCov.cpp +++ /dev/null @@ -1,80 +0,0 @@ -/* - * Copyright (c) 2021, Patrick Meyer - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include -#include - -extern SetOnce g_not_in_early_boot; - -#ifdef ENABLE_KERNEL_COVERAGE_COLLECTION_DEBUG -// Set kcov_emergency_off=true before making calls from __sanitizer_cov_trace_pc to coverage -// instrumented code, in order to prevent an infinite recursion. -// Any code reachable from the non-failure path in __sanitizer_cov_trace_pc must not be -// coverage instrumented. However, once a fatal error was detected, crash_and_burn will use -// a bunch of extra code to print useful debugging information. It would be wasteful not to -// instrument all of that code, so kcov_emergency_off=true can be used to bail out from -// recursive __sanitizer_cov_trace_pc calls while inside crash_and_burn. -bool kcov_emergency_off { false }; -static void crash_and_burn(Thread* thread) -{ - kcov_emergency_off = true; - thread->print_backtrace(); - PANIC("KCOV is b0rked."); - VERIFY_NOT_REACHED(); -} -#endif - -// Set ENABLE_KERNEL_COVERAGE_COLLECTION=ON via cmake, to inject this function on every program edge. -// Note: This function is only used by fuzzing builds. When in use, it becomes an ultra hot code path. -// See https://clang.llvm.org/docs/SanitizerCoverage.html#edge-coverage -extern "C" void __sanitizer_cov_trace_pc(void); -extern "C" void __sanitizer_cov_trace_pc(void) -{ - if (!g_not_in_early_boot.was_set()) [[unlikely]] - return; - - auto* thread = Processor::current_thread(); - -#ifdef ENABLE_KERNEL_COVERAGE_COLLECTION_DEBUG - if (kcov_emergency_off) [[unlikely]] - return; - - // Use are_interrupts_enabled() as a proxy to check we are not currently in an interrupt. - // current_in_irq() will only start returning true, once it incremented m_in_irq, which it - // doesn't do right away. This results in a short interval where we are in an interrupt, - // but the check will not tell us so. In that case, we would incorrectly identify the - // interrupt as __sanitizer_cov_trace_pc recursion here: - if (thread->m_kcov_recursion_hint && Processor::are_interrupts_enabled()) [[unlikely]] { - kcov_emergency_off = true; - dbgln("KCOV Error: __sanitizer_cov_trace_pc causes recursion. If possible, modify " - "__sanitizer_cov_trace_pc to not make the call which transitively caused the recursion. " - "Alternatively either mark the caller of the second __sanitizer_cov_trace_pc with " - "NO_SANITIZE_COVERAGE, or add that callers .cpp file to KCOV_EXCLUDED_SOURCES."); - crash_and_burn(thread); - VERIFY_NOT_REACHED(); - } - TemporaryChange kcov_recursion_hint { thread->m_kcov_recursion_hint, true }; -#endif - - auto* kcov_instance = thread->process().kcov_instance(); - if (kcov_instance == nullptr || !thread->m_kcov_enabled) [[likely]] - return; // KCOV device hasn't been opened yet or thread is not traced - - if (Processor::current_in_irq()) [[unlikely]] { - // Do not collect coverage caused by interrupts. We want the collected coverage to be a function - // of the syscalls executed by the fuzzer. Interrupts can occur more or less randomly. Fuzzers - // uses coverage to identify function call sequences, which triggered new code paths. If the - // coverage is noisy, the fuzzer will waste time on unintersting sequences. - return; - } - - kcov_instance->buffer_add_pc((u64)__builtin_return_address(0)); - return; -} diff --git a/Kernel/Sections.h b/Kernel/Sections.h deleted file mode 100644 index 12c759c5d33..00000000000 --- a/Kernel/Sections.h +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright (c) 2018-2021, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#ifdef __cplusplus -# include -# include -#endif - -#define READONLY_AFTER_INIT __attribute__((section(".ro_after_init"))) -#define UNMAP_AFTER_INIT NEVER_INLINE __attribute__((section(".unmap_after_init"))) - -#define KERNEL_MAPPING_BASE 0x2000000000 - -#define KERNEL_PD_END (kernel_mapping_base + KERNEL_PD_SIZE) -#define KERNEL_PT1024_OFFSET 0x3FE00000 -#define KERNEL_PT1024_BASE (kernel_mapping_base + KERNEL_PT1024_OFFSET) - -#define KERNEL_MAX_CPU_COUNT 64 -#define KERNEL_QUICKMAP_PT_PER_CPU_BASE (KERNEL_PT1024_BASE + (1 * KERNEL_MAX_CPU_COUNT * PAGE_SIZE)) -#define KERNEL_QUICKMAP_PD_PER_CPU_BASE (KERNEL_PT1024_BASE + (2 * KERNEL_MAX_CPU_COUNT * PAGE_SIZE)) -#define KERNEL_QUICKMAP_PER_CPU_BASE (KERNEL_PT1024_BASE + (3 * KERNEL_MAX_CPU_COUNT * PAGE_SIZE)) - -#define USER_RANGE_BASE 0x10000 -#define USER_RANGE_CEILING (kernel_mapping_base - 0x2000000) diff --git a/Kernel/Security/AddressSanitizer.cpp b/Kernel/Security/AddressSanitizer.cpp deleted file mode 100644 index 7352f4eca20..00000000000 --- a/Kernel/Security/AddressSanitizer.cpp +++ /dev/null @@ -1,431 +0,0 @@ -/* - * Copyright (c) 2021, Brian Gianforcaro - * Copyright (c) 2023, Idan Horowitz - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include - -#include -#include -#include -#include -#include - -static constexpr size_t kasan_shadow_scale_offset = 3; // We map each 8 real bytes to 1 shadow byte -static constexpr size_t kasan_shadow_scale = 1 << kasan_shadow_scale_offset; -static constexpr size_t kasan_shadow_mask = kasan_shadow_scale - 1; - -// Defined in clang -static constexpr size_t kasan_alloca_redzone_size = 32; - -namespace Kernel::AddressSanitizer { - -enum class AccessType { - Load, - Store -}; - -static constexpr StringView to_string(AccessType shadow_type) -{ - switch (shadow_type) { - case AccessType::Load: - return "Load"sv; - case AccessType::Store: - return "Store"sv; - default: - return "Unknown"sv; - } -} - -static constexpr StringView to_string(ShadowType shadow_type) -{ - switch (shadow_type) { - case ShadowType::Unpoisoned8Bytes: - return "8 Bytes Unpoisoned"sv; - case ShadowType::Unpoisoned1Byte: - return "1 Byte Unpoisoned | 7 Bytes Poisoned"sv; - case ShadowType::Unpoisoned2Bytes: - return "2 Bytes Unpoisoned | 6 Bytes Poisoned"sv; - case ShadowType::Unpoisoned3Bytes: - return "3 Bytes Unpoisoned | 5 Bytes Poisoned"sv; - case ShadowType::Unpoisoned4Bytes: - return "4 Bytes Unpoisoned | 4 Bytes Poisoned"sv; - case ShadowType::Unpoisoned5Bytes: - return "5 Bytes Unpoisoned | 3 Bytes Poisoned"sv; - case ShadowType::Unpoisoned6Bytes: - return "6 Bytes Unpoisoned | 2 Bytes Poisoned"sv; - case ShadowType::Unpoisoned7Bytes: - return "7 Bytes Unpoisoned | 1 Byte Poisoned"sv; - case ShadowType::StackLeft: - return "Stack Left Redzone"sv; - case ShadowType::StackMiddle: - return "Stack Middle Redzone"sv; - case ShadowType::StackRight: - return "Stack Right Redzone"sv; - case ShadowType::UseAfterReturn: - return "Use After Return"sv; - case ShadowType::UseAfterScope: - return "Use After Scope"sv; - case ShadowType::Generic: - return "Generic Redzone"sv; - case ShadowType::Malloc: - return "Malloc Redzone"sv; - case ShadowType::Free: - return "Freed Region"sv; - default: - return "Unknown"sv; - } -} - -Atomic g_kasan_is_deadly { true }; - -static void print_violation(FlatPtr address, size_t size, AccessType access_type, ShadowType shadow_type, void* return_address) -{ - critical_dmesgln("KASAN: Invalid {}-byte {} access to {}, which is marked as '{}' [at {:p}]", size, to_string(access_type), VirtualAddress(address), to_string(shadow_type), return_address); - dump_backtrace(g_kasan_is_deadly ? PrintToScreen::Yes : PrintToScreen::No); - if (g_kasan_is_deadly) { - critical_dmesgln("KASAN is configured to be deadly, halting the system."); - Processor::halt(); - } -} - -static FlatPtr kasan_shadow_base; -static FlatPtr kasan_shadow_offset; -static bool kasan_initialized = false; - -void init(FlatPtr shadow_base) -{ - kasan_shadow_base = shadow_base; - kasan_shadow_offset = shadow_base - (kernel_mapping_base >> kasan_shadow_scale_offset); - kasan_initialized = true; -} - -static inline ShadowType* va_to_shadow(FlatPtr address) -{ - return (ShadowType*)((address >> kasan_shadow_scale_offset) + kasan_shadow_offset); -} - -void fill_shadow(FlatPtr address, size_t size, ShadowType type) -{ - if (!kasan_initialized) [[unlikely]] - return; - VERIFY((address % kasan_shadow_scale) == 0); - VERIFY((size % kasan_shadow_scale) == 0); - auto* shadow = va_to_shadow(address); - auto shadow_size = size >> kasan_shadow_scale_offset; - memset(shadow, to_underlying(type), shadow_size); -} - -void mark_region(FlatPtr address, size_t valid_size, size_t total_size, ShadowType type) -{ - if (!kasan_initialized) [[unlikely]] - return; - VERIFY((address % kasan_shadow_scale) == 0); - VERIFY((total_size % kasan_shadow_scale) == 0); - auto* shadow = va_to_shadow(address); - auto valid_shadow_size = valid_size >> kasan_shadow_scale_offset; - memset(shadow, to_underlying(ShadowType::Unpoisoned8Bytes), valid_shadow_size); - auto unaligned_size = valid_size & kasan_shadow_mask; - if (unaligned_size) - *(shadow + valid_shadow_size) = static_cast(unaligned_size); - auto poisoned_shadow_size = (total_size - round_up_to_power_of_two(valid_size, kasan_shadow_scale)) >> kasan_shadow_scale_offset; - memset(shadow + valid_shadow_size + (unaligned_size != 0), to_underlying(type), poisoned_shadow_size); -} - -static bool shadow_va_check_1b(FlatPtr address, ShadowType& shadow_type) -{ - auto const shadow = *va_to_shadow(address); - i8 const minimal_valid_shadow = (address & kasan_shadow_mask) + 1; - if (shadow == ShadowType::Unpoisoned8Bytes || (minimal_valid_shadow <= static_cast(shadow))) [[likely]] - return true; - shadow_type = shadow; - return false; -} - -static bool shadow_va_check_2b(FlatPtr address, ShadowType& shadow_type) -{ - // Check for unaligned access - if ((address >> kasan_shadow_scale_offset) != (address + 1) >> kasan_shadow_scale_offset) [[unlikely]] - return shadow_va_check_1b(address, shadow_type) && shadow_va_check_1b(address + 1, shadow_type); - - auto const shadow = *va_to_shadow(address); - i8 const minimal_valid_shadow = ((address + 1) & kasan_shadow_mask) + 1; - if (shadow == ShadowType::Unpoisoned8Bytes || (minimal_valid_shadow <= static_cast(shadow))) [[likely]] - return true; - shadow_type = shadow; - return false; -} - -static bool shadow_va_check_4b(FlatPtr address, ShadowType& shadow_type) -{ - // Check for unaligned access - if ((address >> kasan_shadow_scale_offset) != (address + 3) >> kasan_shadow_scale_offset) [[unlikely]] - return shadow_va_check_2b(address, shadow_type) && shadow_va_check_2b(address + 2, shadow_type); - - auto const shadow = *va_to_shadow(address); - i8 const minimal_valid_shadow = ((address + 3) & kasan_shadow_mask) + 1; - if (shadow == ShadowType::Unpoisoned8Bytes || (minimal_valid_shadow <= static_cast(shadow))) [[likely]] - return true; - shadow_type = shadow; - return false; -} - -static bool shadow_va_check_8b(FlatPtr address, ShadowType& shadow_type) -{ - // Check for unaligned access - if ((address >> kasan_shadow_scale_offset) != (address + 7) >> kasan_shadow_scale_offset) [[unlikely]] - return shadow_va_check_4b(address, shadow_type) && shadow_va_check_4b(address + 4, shadow_type); - - auto const shadow = *va_to_shadow(address); - i8 const minimal_valid_shadow = ((address + 7) & kasan_shadow_mask) + 1; - if (shadow == ShadowType::Unpoisoned8Bytes || (minimal_valid_shadow <= static_cast(shadow))) [[likely]] - return true; - shadow_type = shadow; - return false; -} - -static bool shadow_va_check_Nb(FlatPtr address, size_t n, ShadowType& shadow_type) -{ - while ((address % 8) && (n > 0)) { - if (!shadow_va_check_1b(address, shadow_type)) [[unlikely]] - return false; - address++; - n--; - } - while (n >= 8) { - if (!shadow_va_check_8b(address, shadow_type)) - return false; - address += 8; - n -= 8; - } - while (n > 0) { - if (!shadow_va_check_1b(address, shadow_type)) [[unlikely]] - return false; - address++; - n--; - } - return true; -} - -static void shadow_va_check(FlatPtr address, size_t size, AccessType access_type, void* return_address) -{ - if (size == 0) [[unlikely]] - return; - if (!kasan_initialized) [[unlikely]] - return; - if (address < kernel_mapping_base || address >= kasan_shadow_base) [[unlikely]] - return; - - bool valid = false; - ShadowType shadow_type = ShadowType::Unpoisoned8Bytes; - switch (size) { - case 1: - valid = shadow_va_check_1b(address, shadow_type); - break; - case 2: - valid = shadow_va_check_2b(address, shadow_type); - break; - case 4: - valid = shadow_va_check_4b(address, shadow_type); - break; - case 8: - valid = shadow_va_check_8b(address, shadow_type); - break; - default: - valid = shadow_va_check_Nb(address, size, shadow_type); - break; - } - - if (valid) [[likely]] - return; - - print_violation(address, size, access_type, shadow_type, return_address); -} - -} - -using namespace Kernel; -using namespace Kernel::AddressSanitizer; - -extern "C" { - -// Define a macro to easily declare the KASAN load and store callbacks for -// the various sizes of data type. -// -#define ADDRESS_SANITIZER_LOAD_STORE(size) \ - void __asan_load##size(FlatPtr); \ - void __asan_load##size(FlatPtr address) \ - { \ - shadow_va_check(address, size, AccessType::Load, __builtin_return_address(0)); \ - } \ - void __asan_load##size##_noabort(FlatPtr); \ - void __asan_load##size##_noabort(FlatPtr address) \ - { \ - shadow_va_check(address, size, AccessType::Load, __builtin_return_address(0)); \ - } \ - void __asan_store##size(FlatPtr); \ - void __asan_store##size(FlatPtr address) \ - { \ - shadow_va_check(address, size, AccessType::Store, __builtin_return_address(0)); \ - } \ - void __asan_store##size##_noabort(FlatPtr); \ - void __asan_store##size##_noabort(FlatPtr address) \ - { \ - shadow_va_check(address, size, AccessType::Store, __builtin_return_address(0)); \ - } \ - void __asan_report_load##size(FlatPtr); \ - void __asan_report_load##size(FlatPtr address) \ - { \ - print_violation(address, size, AccessType::Load, ShadowType::Generic, __builtin_return_address(0)); \ - } \ - void __asan_report_load##size##_noabort(FlatPtr); \ - void __asan_report_load##size##_noabort(FlatPtr address) \ - { \ - print_violation(address, size, AccessType::Load, ShadowType::Generic, __builtin_return_address(0)); \ - } \ - void __asan_report_store##size(FlatPtr); \ - void __asan_report_store##size(FlatPtr address) \ - { \ - print_violation(address, size, AccessType::Store, ShadowType::Generic, __builtin_return_address(0)); \ - } \ - void __asan_report_store##size##_noabort(FlatPtr); \ - void __asan_report_store##size##_noabort(FlatPtr address) \ - { \ - print_violation(address, size, AccessType::Store, ShadowType::Generic, __builtin_return_address(0)); \ - } - -ADDRESS_SANITIZER_LOAD_STORE(1); -ADDRESS_SANITIZER_LOAD_STORE(2); -ADDRESS_SANITIZER_LOAD_STORE(4); -ADDRESS_SANITIZER_LOAD_STORE(8); -ADDRESS_SANITIZER_LOAD_STORE(16); - -#undef ADDRESS_SANITIZER_LOAD_STORE - -void __asan_loadN(FlatPtr, size_t); -void __asan_loadN(FlatPtr address, size_t size) -{ - shadow_va_check(address, size, AccessType::Load, __builtin_return_address(0)); -} - -void __asan_loadN_noabort(FlatPtr, size_t); -void __asan_loadN_noabort(FlatPtr address, size_t size) -{ - shadow_va_check(address, size, AccessType::Load, __builtin_return_address(0)); -} - -void __asan_storeN(FlatPtr, size_t); -void __asan_storeN(FlatPtr address, size_t size) -{ - shadow_va_check(address, size, AccessType::Store, __builtin_return_address(0)); -} - -void __asan_storeN_noabort(FlatPtr, size_t); -void __asan_storeN_noabort(FlatPtr address, size_t size) -{ - shadow_va_check(address, size, AccessType::Store, __builtin_return_address(0)); -} - -void __asan_report_load_n(FlatPtr, size_t); -void __asan_report_load_n(FlatPtr address, size_t size) -{ - print_violation(address, size, AccessType::Load, ShadowType::Generic, __builtin_return_address(0)); -} - -void __asan_report_load_n_noabort(FlatPtr, size_t); -void __asan_report_load_n_noabort(FlatPtr address, size_t size) -{ - print_violation(address, size, AccessType::Load, ShadowType::Generic, __builtin_return_address(0)); -} - -void __asan_report_store_n(FlatPtr, size_t); -void __asan_report_store_n(FlatPtr address, size_t size) -{ - print_violation(address, size, AccessType::Store, ShadowType::Generic, __builtin_return_address(0)); -} - -void __asan_report_store_n_noabort(FlatPtr, size_t); -void __asan_report_store_n_noabort(FlatPtr address, size_t size) -{ - print_violation(address, size, AccessType::Store, ShadowType::Generic, __builtin_return_address(0)); -} - -// As defined in the compiler -struct __asan_global_source_location { - char const* filename; - int line_number; - int column_number; -}; -struct __asan_global { - uintptr_t address; - size_t valid_size; - size_t total_size; - char const* name; - char const* module_name; - size_t has_dynamic_init; - struct __asan_global_source_location* location; - size_t odr_indicator; -}; - -void __asan_register_globals(struct __asan_global*, size_t); -void __asan_register_globals(struct __asan_global* globals, size_t count) -{ - for (auto i = 0u; i < count; ++i) - mark_region(globals[i].address, globals[i].valid_size, globals[i].total_size, ShadowType::Generic); -} - -void __asan_unregister_globals(struct __asan_global*, size_t); -void __asan_unregister_globals(struct __asan_global* globals, size_t count) -{ - for (auto i = 0u; i < count; ++i) - mark_region(globals[i].address, globals[i].total_size, globals[i].total_size, ShadowType::Unpoisoned8Bytes); -} - -void __asan_alloca_poison(FlatPtr, size_t); -void __asan_alloca_poison(FlatPtr address, size_t size) -{ - VERIFY(address % kasan_alloca_redzone_size == 0); - auto rounded_size = round_up_to_power_of_two(size, kasan_alloca_redzone_size); - fill_shadow(address - kasan_alloca_redzone_size, kasan_alloca_redzone_size, ShadowType::StackLeft); - mark_region(address, size, rounded_size, Kernel::AddressSanitizer::ShadowType::StackMiddle); - fill_shadow(address + rounded_size, kasan_alloca_redzone_size, Kernel::AddressSanitizer::ShadowType::StackRight); -} - -void __asan_allocas_unpoison(FlatPtr, size_t); -void __asan_allocas_unpoison(FlatPtr start, size_t end) -{ - VERIFY(start >= end); - auto size = end - start; - VERIFY(size % kasan_shadow_scale == 0); - fill_shadow(start, size, Kernel::AddressSanitizer::ShadowType::Unpoisoned8Bytes); -} - -void __asan_poison_stack_memory(FlatPtr, size_t); -void __asan_poison_stack_memory(FlatPtr address, size_t size) -{ - fill_shadow(address, round_up_to_power_of_two(size, kasan_shadow_scale), Kernel::AddressSanitizer::ShadowType::UseAfterScope); -} - -void __asan_unpoison_stack_memory(FlatPtr, size_t); -void __asan_unpoison_stack_memory(FlatPtr address, size_t size) -{ - fill_shadow(address, round_up_to_power_of_two(size, kasan_shadow_scale), Kernel::AddressSanitizer::ShadowType::Unpoisoned8Bytes); -} - -void __asan_handle_no_return(void); -void __asan_handle_no_return(void) -{ -} - -void __asan_before_dynamic_init(char const*); -void __asan_before_dynamic_init(char const*) -{ -} - -void __asan_after_dynamic_init(); -void __asan_after_dynamic_init() -{ -} -} diff --git a/Kernel/Security/AddressSanitizer.h b/Kernel/Security/AddressSanitizer.h deleted file mode 100644 index cc4dfe177c9..00000000000 --- a/Kernel/Security/AddressSanitizer.h +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright (c) 2021, Brian Gianforcaro - * Copyright (c) 2023, Idan Horowitz - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include - -namespace Kernel::AddressSanitizer { - -extern Atomic g_kasan_is_deadly; - -enum class ShadowType : u8 { - Unpoisoned8Bytes = 0, - Unpoisoned1Byte = 1, - Unpoisoned2Bytes = 2, - Unpoisoned3Bytes = 3, - Unpoisoned4Bytes = 4, - Unpoisoned5Bytes = 5, - Unpoisoned6Bytes = 6, - Unpoisoned7Bytes = 7, - StackLeft = 0xF1, - StackMiddle = 0xF2, - StackRight = 0xF3, - UseAfterReturn = 0xF5, - UseAfterScope = 0xF8, - Generic = 0xFA, - Malloc = 0xFB, - Free = 0xFC, -}; - -void init(FlatPtr shadow_base); -void fill_shadow(FlatPtr address, size_t size, ShadowType type); -void mark_region(FlatPtr address, size_t valid_size, size_t total_size, ShadowType type); - -} diff --git a/Kernel/Security/Credentials.cpp b/Kernel/Security/Credentials.cpp deleted file mode 100644 index fa649909df2..00000000000 --- a/Kernel/Security/Credentials.cpp +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright (c) 2022, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include - -namespace Kernel { - -ErrorOr> Credentials::create(UserID uid, GroupID gid, UserID euid, GroupID egid, UserID suid, GroupID sgid, ReadonlySpan extra_gids, SessionID sid, ProcessGroupID pgid) -{ - auto extra_gids_array = TRY(FixedArray::create(extra_gids)); - return adopt_nonnull_ref_or_enomem(new (nothrow) Credentials(uid, gid, euid, egid, suid, sgid, move(extra_gids_array), sid, pgid)); -} - -Credentials::Credentials(UserID uid, GroupID gid, UserID euid, GroupID egid, UserID suid, GroupID sgid, FixedArray extra_gids, SessionID sid, ProcessGroupID pgid) - : m_uid(uid) - , m_gid(gid) - , m_euid(euid) - , m_egid(egid) - , m_suid(suid) - , m_sgid(sgid) - , m_extra_gids(move(extra_gids)) - , m_sid(sid) - , m_pgid(pgid) -{ -} - -Credentials::~Credentials() = default; - -bool Credentials::in_group(Kernel::GroupID gid) const -{ - return m_gid == gid || m_extra_gids.contains_slow(gid); -} - -} diff --git a/Kernel/Security/Credentials.h b/Kernel/Security/Credentials.h deleted file mode 100644 index 7f3f33d5f78..00000000000 --- a/Kernel/Security/Credentials.h +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright (c) 2022, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include - -namespace Kernel { - -class Credentials final : public AtomicRefCounted { -public: - static ErrorOr> create(UserID uid, GroupID gid, UserID euid, GroupID egid, UserID suid, GroupID sgid, ReadonlySpan extra_gids, SessionID sid, ProcessGroupID pgid); - ~Credentials(); - - bool is_superuser() const { return euid() == 0; } - - UserID euid() const { return m_euid; } - GroupID egid() const { return m_egid; } - UserID uid() const { return m_uid; } - GroupID gid() const { return m_gid; } - UserID suid() const { return m_suid; } - GroupID sgid() const { return m_sgid; } - ReadonlySpan extra_gids() const { return m_extra_gids.span(); } - SessionID sid() const { return m_sid; } - ProcessGroupID pgid() const { return m_pgid; } - - bool in_group(GroupID) const; - -private: - Credentials(UserID uid, GroupID gid, UserID euid, GroupID egid, UserID suid, GroupID sgid, FixedArray extra_gids, SessionID sid, ProcessGroupID pgid); - - UserID m_uid; - GroupID m_gid; - UserID m_euid; - GroupID m_egid; - UserID m_suid; - GroupID m_sgid; - FixedArray m_extra_gids; - SessionID m_sid; - ProcessGroupID m_pgid; -}; - -} diff --git a/Kernel/Security/ExecutionMode.h b/Kernel/Security/ExecutionMode.h deleted file mode 100644 index b84e7e66e06..00000000000 --- a/Kernel/Security/ExecutionMode.h +++ /dev/null @@ -1,18 +0,0 @@ -/* - * Copyright (c) 2021, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include - -namespace Kernel { - -enum class ExecutionMode : u8 { - Kernel = 0, - User, -}; - -} diff --git a/Kernel/Security/Jail.cpp b/Kernel/Security/Jail.cpp deleted file mode 100644 index 284f74cac32..00000000000 --- a/Kernel/Security/Jail.cpp +++ /dev/null @@ -1,87 +0,0 @@ -/* - * Copyright (c) 2022-2023, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include - -namespace Kernel { - -static Atomic s_jail_id; -static Singleton> s_all_instances {}; - -static JailIndex generate_jail_id() -{ - return s_jail_id.fetch_add(1); -} - -RefPtr Jail::process_list() -{ - return m_process_list; -} - -ErrorOr> Jail::create(NonnullOwnPtr name, unsigned flags) -{ - RefPtr jail_process_list; - if (flags & static_cast(JailIsolationFlags::PIDIsolation)) - jail_process_list = TRY(ProcessList::create()); - - return s_all_instances->with([&](auto& list) -> ErrorOr> { - auto jail = TRY(adopt_nonnull_ref_or_enomem(new (nothrow) Jail(move(name), generate_jail_id(), jail_process_list))); - list.append(jail); - return jail; - }); -} - -ErrorOr Jail::for_each_when_process_is_not_jailed(Function(Jail const&)> callback) -{ - return Process::current().jail().with([&](auto const& my_jail) -> ErrorOr { - // Note: If we are in a jail, don't reveal anything about the outside world, - // not even the fact that we are in which jail... - if (my_jail) - return {}; - return s_all_instances->with([&](auto& list) -> ErrorOr { - for (auto& jail : list) { - TRY(callback(jail)); - } - return {}; - }); - }); -} - -RefPtr Jail::find_by_index(JailIndex index) -{ - return s_all_instances->with([&](auto& list) -> RefPtr { - for (auto& jail : list) { - if (jail.index() == index) - return jail; - } - return {}; - }); -} - -Jail::Jail(NonnullOwnPtr name, JailIndex index, RefPtr process_list) - : m_name(move(name)) - , m_index(index) - , m_process_list(process_list) -{ -} - -void Jail::detach(Badge) -{ - VERIFY(ref_count() > 0); - m_attach_count.with([&](auto& my_attach_count) { - VERIFY(my_attach_count > 0); - my_attach_count--; - if (my_attach_count == 0) { - m_list_node.remove(); - } - }); -} - -} diff --git a/Kernel/Security/Jail.h b/Kernel/Security/Jail.h deleted file mode 100644 index d5c67341200..00000000000 --- a/Kernel/Security/Jail.h +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright (c) 2022-2023, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace Kernel { - -class ProcessList; - -AK_TYPEDEF_DISTINCT_ORDERED_ID(u64, JailIndex); - -class Jail : public AtomicRefCounted { - -public: - RefPtr process_list(); - - static RefPtr find_by_index(JailIndex); - static ErrorOr> create(NonnullOwnPtr name, unsigned flags); - static ErrorOr for_each_when_process_is_not_jailed(Function(Jail const&)> callback); - - StringView name() const { return m_name->view(); } - JailIndex index() const { return m_index; } - - void detach(Badge); - SpinlockProtected& attach_count() { return m_attach_count; } - -private: - Jail(NonnullOwnPtr, JailIndex, RefPtr); - - NonnullOwnPtr m_name; - JailIndex const m_index; - - IntrusiveListNode> m_list_node; - -public: - using List = IntrusiveListRelaxedConst<&Jail::m_list_node>; - -private: - RefPtr const m_process_list; - - SpinlockProtected m_attach_count { 0 }; -}; - -} diff --git a/Kernel/Security/Random.cpp b/Kernel/Security/Random.cpp deleted file mode 100644 index d4b02c27c40..00000000000 --- a/Kernel/Security/Random.cpp +++ /dev/null @@ -1,185 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * Copyright (c) 2020, Peter Elliott - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#if ARCH(X86_64) -# include -# include -#elif ARCH(AARCH64) -# include -#endif -#include -#include -#include -#include - -namespace Kernel { - -static Singleton s_the; -static Atomic s_next_random_value = 1; - -KernelRng& KernelRng::the() -{ - return *s_the; -} - -UNMAP_AFTER_INIT KernelRng::KernelRng() -{ -#if ARCH(X86_64) - if (Processor::current().has_feature(CPUFeature::RDSEED)) { - dmesgln("KernelRng: Using RDSEED as entropy source"); - - for (size_t i = 0; i < pool_count * reseed_threshold; ++i) { - add_random_event(Kernel::read_rdseed(), i % 32); - } - } else if (Processor::current().has_feature(CPUFeature::RDRAND)) { - dmesgln("KernelRng: Using RDRAND as entropy source"); - - for (size_t i = 0; i < pool_count * reseed_threshold; ++i) { - add_random_event(Kernel::read_rdrand(), i % 32); - } - } else if (TimeManagement::the().can_query_precise_time()) { - // Add HPET as entropy source if we don't have anything better. - dmesgln("KernelRng: Using HPET as entropy source"); - - for (size_t i = 0; i < pool_count * reseed_threshold; ++i) { - u64 hpet_time = HPET::the().read_main_counter_unsafe(); - add_random_event(hpet_time, i % 32); - } - } else { - // Fallback to RTC - dmesgln("KernelRng: Using RTC as entropy source (bad!)"); - auto current_time = static_cast(RTC::now()); - for (size_t i = 0; i < pool_count * reseed_threshold; ++i) { - add_random_event(current_time, i % 32); - current_time *= 0x574au; - current_time += 0x40b2u; - } - } -#elif ARCH(AARCH64) - if (Processor::current().has_feature(CPUFeature::RNG)) { - dmesgln("KernelRng: Using RNDRRS as entropy source"); - for (size_t i = 0; i < pool_count * reseed_threshold; ++i) { - add_random_event(Aarch64::Asm::read_rndrrs(), i % 32); - } - } else { - // Fallback to TimeManagement as entropy - dmesgln("KernelRng: Using bad entropy source TimeManagement"); - auto current_time = static_cast(TimeManagement::now().milliseconds_since_epoch()); - for (size_t i = 0; i < pool_count * reseed_threshold; ++i) { - add_random_event(current_time, i % 32); - current_time *= 0x574au; - current_time += 0x40b2u; - } - } -#elif ARCH(RISCV64) - // Fallback to TimeManagement as entropy - dmesgln("KernelRng: Using bad entropy source TimeManagement"); - auto current_time = static_cast(TimeManagement::now().milliseconds_since_epoch()); - for (size_t i = 0; i < pool_count * reseed_threshold; ++i) { - add_random_event(current_time, i % 32); - current_time *= 0x574au; - current_time += 0x40b2u; - } -#else - dmesgln("KernelRng: No entropy source available!"); -#endif -} - -void KernelRng::wait_for_entropy() -{ - SpinlockLocker lock(get_lock()); - if (!is_ready()) { - dbgln("Entropy starvation..."); - m_seed_queue.wait_forever("KernelRng"sv); - } -} - -void KernelRng::wake_if_ready() -{ - VERIFY(get_lock().is_locked()); - if (is_ready()) { - m_seed_queue.wake_all(); - } -} - -size_t EntropySource::next_source { static_cast(EntropySource::Static::MaxHardcodedSourceIndex) }; - -static void do_get_fast_random_bytes(Bytes buffer) -{ - - union { - u8 bytes[4]; - u32 value; - } u; - size_t offset = 4; - for (size_t i = 0; i < buffer.size(); ++i) { - if (offset >= 4) { - auto current_next = s_next_random_value.load(); - for (;;) { - auto new_next = current_next * 1103515245 + 12345; - if (s_next_random_value.compare_exchange_strong(current_next, new_next)) { - u.value = new_next; - break; - } - } - offset = 0; - } - buffer[i] = u.bytes[offset++]; - } -} - -bool get_good_random_bytes(Bytes buffer, bool allow_wait, bool fallback_to_fast) -{ - bool result = false; - auto& kernel_rng = KernelRng::the(); - // FIXME: What if interrupts are disabled because we're in an interrupt? - bool can_wait = Processor::are_interrupts_enabled(); - if (!can_wait && allow_wait) { - // If we can't wait but the caller would be ok with it, then we - // need to definitely fallback to *something*, even if it's less - // secure... - fallback_to_fast = true; - } - if (can_wait && allow_wait) { - for (;;) { - { - if (kernel_rng.get_random_bytes(buffer)) { - result = true; - break; - } - } - kernel_rng.wait_for_entropy(); - } - } else { - // We can't wait/block here, or we are not allowed to block/wait - if (kernel_rng.get_random_bytes(buffer)) { - result = true; - } else if (fallback_to_fast) { - // If interrupts are disabled - do_get_fast_random_bytes(buffer); - result = true; - } - } - - // NOTE: The only case where this function should ever return false and - // not actually return random data is if fallback_to_fast == false and - // allow_wait == false and interrupts are enabled! - VERIFY(result || !fallback_to_fast); - return result; -} - -void get_fast_random_bytes(Bytes buffer) -{ - // Try to get good randomness, but don't block if we can't right now - // and allow falling back to fast randomness - auto result = get_good_random_bytes(buffer, false, true); - VERIFY(result); -} - -} diff --git a/Kernel/Security/Random.h b/Kernel/Security/Random.h deleted file mode 100644 index 97d7b2eab69..00000000000 --- a/Kernel/Security/Random.h +++ /dev/null @@ -1,203 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * Copyright (c) 2020, Peter Elliott - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace Kernel { - -template -class FortunaPRNG { -public: - constexpr static size_t pool_count = 32; - constexpr static size_t reseed_threshold = 16; - - using CipherType = CipherT; - using BlockType = typename CipherT::BlockType; - using HashType = HashT; - using DigestType = typename HashT::DigestType; - - // FIXME: Do something other than VERIFY()'ing in case of OOM. - FortunaPRNG() - : m_counter(ByteBuffer::create_zeroed(BlockType::block_size()).release_value_but_fixme_should_propagate_errors()) - { - } - - bool get_random_bytes(Bytes buffer) - { - SpinlockLocker lock(m_lock); - if (!is_ready()) - return false; - if (m_p0_len >= reseed_threshold) { - this->reseed(); - } - - VERIFY(is_seeded()); - - // FIXME: More than 2^20 bytes cannot be generated without refreshing the key. - VERIFY(buffer.size() < (1 << 20)); - - typename CipherType::CTRMode cipher(m_key, KeySize, Crypto::Cipher::Intent::Encryption); - - auto counter_span = m_counter.bytes(); - cipher.key_stream(buffer, counter_span, &counter_span); - - // Extract a new key from the prng stream. - Bytes key_span = m_key.bytes(); - cipher.key_stream(key_span, counter_span, &counter_span); - return true; - } - - template - void add_random_event(T const& event_data, size_t pool) - { - pool %= pool_count; - if (pool == 0) { - m_p0_len++; - } - m_pools[pool].update(reinterpret_cast(&event_data), sizeof(T)); - } - - [[nodiscard]] bool is_seeded() const - { - return m_reseed_number > 0; - } - - [[nodiscard]] bool is_ready() const - { - VERIFY(m_lock.is_locked()); - return is_seeded() || m_p0_len >= reseed_threshold; - } - - Spinlock& get_lock() { return m_lock; } - -private: - void reseed() - { - HashType new_key; - new_key.update(m_key); - for (size_t i = 0; i < pool_count; ++i) { - if (m_reseed_number % (1u << i) == 0) { - DigestType digest = m_pools[i].digest(); - new_key.update(digest.immutable_data(), digest.data_length()); - } - } - DigestType digest = new_key.digest(); - if (m_key.size() == digest.data_length()) { - // Avoid reallocating, just overwrite the key. - m_key.overwrite(0, digest.immutable_data(), digest.data_length()); - } else { - auto buffer_result = ByteBuffer::copy(digest.immutable_data(), digest.data_length()); - // If there's no memory left to copy this into, bail out. - if (buffer_result.is_error()) - return; - - m_key = buffer_result.release_value(); - } - - m_reseed_number++; - m_p0_len = 0; - } - - ByteBuffer m_counter; - size_t m_reseed_number { 0 }; - size_t m_p0_len { 0 }; - ByteBuffer m_key; - HashType m_pools[pool_count]; - Spinlock m_lock {}; -}; - -class KernelRng : public FortunaPRNG { - -public: - KernelRng(); - static KernelRng& the(); - - void wait_for_entropy(); - - void wake_if_ready(); - -private: - WaitQueue m_seed_queue; -}; - -class EntropySource { - template - struct Event { - u64 timestamp; - size_t source; - T event_data; - }; - -public: - enum class Static : size_t { - Interrupts, - MaxHardcodedSourceIndex, - }; - - EntropySource() - : m_source(next_source++) - { - } - - EntropySource(Static hardcoded_source) - : m_source(static_cast(hardcoded_source)) - { - } - - template - void add_random_event(T const& event_data) - { - auto& kernel_rng = KernelRng::the(); - SpinlockLocker lock(kernel_rng.get_lock()); - // We don't lock this because on the off chance a pool is corrupted, entropy isn't lost. - Event event = { Processor::read_cpu_counter(), m_source, event_data }; - kernel_rng.add_random_event(event, m_pool); - m_pool++; - kernel_rng.wake_if_ready(); - } - -private: - static size_t next_source; - size_t m_pool { 0 }; - size_t m_source; -}; - -// NOTE: These API's are primarily about expressing intent/needs in the calling code. -// The only difference is that get_fast_random is guaranteed not to block. - -void get_fast_random_bytes(Bytes); -bool get_good_random_bytes(Bytes bytes, bool allow_wait = true, bool fallback_to_fast = true); - -template -inline T get_fast_random() -{ - T value; - Bytes bytes { reinterpret_cast(&value), sizeof(T) }; - get_fast_random_bytes(bytes); - return value; -} - -template -inline T get_good_random() -{ - T value; - Bytes bytes { reinterpret_cast(&value), sizeof(T) }; - get_good_random_bytes(bytes); - return value; -} - -} diff --git a/Kernel/Security/Random/VirtIO/RNG.cpp b/Kernel/Security/Random/VirtIO/RNG.cpp deleted file mode 100644 index 066f03bd4cc..00000000000 --- a/Kernel/Security/Random/VirtIO/RNG.cpp +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Copyright (c) 2021, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include - -namespace Kernel::VirtIO { - -UNMAP_AFTER_INIT NonnullLockRefPtr RNG::must_create_for_pci_instance(PCI::DeviceIdentifier const& device_identifier) -{ - auto pci_transport_link = MUST(PCIeTransportLink::create(device_identifier)); - return adopt_lock_ref_if_nonnull(new RNG(move(pci_transport_link))).release_nonnull(); -} - -UNMAP_AFTER_INIT ErrorOr RNG::initialize_virtio_resources() -{ - TRY(Device::initialize_virtio_resources()); - TRY(negotiate_features([&](auto) { - return 0; - })); - TRY(setup_queues(1)); - finish_init(); - m_entropy_buffer = TRY(MM.allocate_contiguous_kernel_region(PAGE_SIZE, "VirtIO::RNG"sv, Memory::Region::Access::ReadWrite)); - memset(m_entropy_buffer->vaddr().as_ptr(), 0, m_entropy_buffer->size()); - request_entropy_from_host(); - return {}; -} - -UNMAP_AFTER_INIT RNG::RNG(NonnullOwnPtr transport_entity) - : VirtIO::Device(move(transport_entity)) -{ -} - -ErrorOr RNG::handle_device_config_change() -{ - return Error::from_errno(EIO); // Device has no config -} - -void RNG::handle_queue_update(u16 queue_index) -{ - VERIFY(queue_index == REQUESTQ); - size_t available_entropy = 0, used; - auto& queue = get_queue(REQUESTQ); - { - SpinlockLocker lock(queue.lock()); - auto chain = queue.pop_used_buffer_chain(used); - if (chain.is_empty()) - return; - VERIFY(chain.length() == 1); - chain.for_each([&available_entropy](PhysicalAddress, size_t length) { - available_entropy = length; - }); - chain.release_buffer_slots_to_queue(); - } - dbgln_if(VIRTIO_DEBUG, "VirtIO::RNG: received {} bytes of entropy!", available_entropy); - for (auto i = 0u; i < available_entropy; i++) { - m_entropy_source.add_random_event(m_entropy_buffer->vaddr().as_ptr()[i]); - } - // TODO: When should we get some more entropy? -} - -void RNG::request_entropy_from_host() -{ - auto& queue = get_queue(REQUESTQ); - SpinlockLocker lock(queue.lock()); - QueueChain chain(queue); - chain.add_buffer_to_chain(m_entropy_buffer->physical_page(0)->paddr(), PAGE_SIZE, BufferType::DeviceWritable); - supply_chain_and_notify(REQUESTQ, chain); -} - -} diff --git a/Kernel/Security/Random/VirtIO/RNG.h b/Kernel/Security/Random/VirtIO/RNG.h deleted file mode 100644 index 55b26b14368..00000000000 --- a/Kernel/Security/Random/VirtIO/RNG.h +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright (c) 2021, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include - -namespace Kernel::VirtIO { - -#define REQUESTQ 0 - -class RNG final - : public AtomicRefCounted - , public VirtIO::Device { -public: - static NonnullLockRefPtr must_create_for_pci_instance(PCI::DeviceIdentifier const&); - virtual ~RNG() override = default; - - virtual ErrorOr initialize_virtio_resources() override; - -private: - virtual StringView class_name() const override { return "VirtIORNG"sv; } - explicit RNG(NonnullOwnPtr); - virtual ErrorOr handle_device_config_change() override; - virtual void handle_queue_update(u16 queue_index) override; - void request_entropy_from_host(); - - OwnPtr m_entropy_buffer; - EntropySource m_entropy_source; -}; - -} diff --git a/Kernel/Security/UBSanitizer.cpp b/Kernel/Security/UBSanitizer.cpp deleted file mode 100644 index 72be1185e0d..00000000000 --- a/Kernel/Security/UBSanitizer.cpp +++ /dev/null @@ -1,218 +0,0 @@ -/* - * Copyright (c) 2021, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include - -using namespace Kernel; -using namespace AK::UBSanitizer; - -Atomic AK::UBSanitizer::g_ubsan_is_deadly { true }; - -extern "C" { - -static void print_location(SourceLocation const& location) -{ - if (!location.filename()) - critical_dmesgln("KUBSAN: in unknown file"); - else - critical_dmesgln("KUBSAN: at {}, line {}, column: {}", location.filename(), location.line(), location.column()); - dump_backtrace(g_ubsan_is_deadly ? PrintToScreen::Yes : PrintToScreen::No); - if (g_ubsan_is_deadly) { - critical_dmesgln("UB is configured to be deadly, halting the system."); - Processor::halt(); - } -} - -void __ubsan_handle_load_invalid_value(InvalidValueData const&, ValueHandle) __attribute__((used)); -void __ubsan_handle_load_invalid_value(InvalidValueData const& data, ValueHandle) -{ - critical_dmesgln("KUBSAN: load-invalid-value: {} ({}-bit)", data.type.name(), data.type.bit_width()); - print_location(data.location); -} - -void __ubsan_handle_nonnull_arg(NonnullArgData const&) __attribute__((used)); -void __ubsan_handle_nonnull_arg(NonnullArgData const& data) -{ - critical_dmesgln("KUBSAN: null pointer passed as argument {}, which is declared to never be null", data.argument_index); - print_location(data.location); -} - -void __ubsan_handle_nullability_arg(NonnullArgData const&) __attribute__((used)); -void __ubsan_handle_nullability_arg(NonnullArgData const& data) -{ - critical_dmesgln("KUBSAN: null pointer passed as argument {}, which is declared to never be null", data.argument_index); - print_location(data.location); -} - -void __ubsan_handle_nonnull_return_v1(NonnullReturnData const&, SourceLocation const&) __attribute__((used)); -void __ubsan_handle_nonnull_return_v1(NonnullReturnData const&, SourceLocation const& location) -{ - critical_dmesgln("KUBSAN: null pointer return from function declared to never return null"); - print_location(location); -} - -void __ubsan_handle_nullability_return_v1(NonnullReturnData const& data, SourceLocation const& location) __attribute__((used)); -void __ubsan_handle_nullability_return_v1(NonnullReturnData const&, SourceLocation const& location) -{ - critical_dmesgln("KUBSAN: null pointer return from function declared to never return null"); - print_location(location); -} - -void __ubsan_handle_vla_bound_not_positive(VLABoundData const&, ValueHandle) __attribute__((used)); -void __ubsan_handle_vla_bound_not_positive(VLABoundData const& data, ValueHandle) -{ - critical_dmesgln("KUBSAN: VLA bound not positive {} ({}-bit)", data.type.name(), data.type.bit_width()); - print_location(data.location); -} - -void __ubsan_handle_add_overflow(OverflowData const&, ValueHandle lhs, ValueHandle rhs) __attribute__((used)); -void __ubsan_handle_add_overflow(OverflowData const& data, ValueHandle, ValueHandle) -{ - critical_dmesgln("KUBSAN: addition overflow, {} ({}-bit)", data.type.name(), data.type.bit_width()); - - print_location(data.location); -} - -void __ubsan_handle_sub_overflow(OverflowData const&, ValueHandle lhs, ValueHandle rhs) __attribute__((used)); -void __ubsan_handle_sub_overflow(OverflowData const& data, ValueHandle, ValueHandle) -{ - critical_dmesgln("KUBSAN: subtraction overflow, {} ({}-bit)", data.type.name(), data.type.bit_width()); - - print_location(data.location); -} - -void __ubsan_handle_negate_overflow(OverflowData const&, ValueHandle) __attribute__((used)); -void __ubsan_handle_negate_overflow(OverflowData const& data, ValueHandle) -{ - critical_dmesgln("KUBSAN: negation overflow, {} ({}-bit)", data.type.name(), data.type.bit_width()); - - print_location(data.location); -} - -void __ubsan_handle_mul_overflow(OverflowData const&, ValueHandle lhs, ValueHandle rhs) __attribute__((used)); -void __ubsan_handle_mul_overflow(OverflowData const& data, ValueHandle, ValueHandle) -{ - critical_dmesgln("KUBSAN: multiplication overflow, {} ({}-bit)", data.type.name(), data.type.bit_width()); - print_location(data.location); -} - -void __ubsan_handle_shift_out_of_bounds(ShiftOutOfBoundsData const&, ValueHandle lhs, ValueHandle rhs) __attribute__((used)); -void __ubsan_handle_shift_out_of_bounds(ShiftOutOfBoundsData const& data, ValueHandle, ValueHandle) -{ - critical_dmesgln("KUBSAN: shift out of bounds, {} ({}-bit) shifted by {} ({}-bit)", data.lhs_type.name(), data.lhs_type.bit_width(), data.rhs_type.name(), data.rhs_type.bit_width()); - print_location(data.location); -} - -void __ubsan_handle_divrem_overflow(OverflowData const&, ValueHandle lhs, ValueHandle rhs) __attribute__((used)); -void __ubsan_handle_divrem_overflow(OverflowData const& data, ValueHandle, ValueHandle) -{ - critical_dmesgln("KUBSAN: divrem overflow, {} ({}-bit)", data.type.name(), data.type.bit_width()); - print_location(data.location); -} - -void __ubsan_handle_out_of_bounds(OutOfBoundsData const&, ValueHandle) __attribute__((used)); -void __ubsan_handle_out_of_bounds(OutOfBoundsData const& data, ValueHandle) -{ - critical_dmesgln("KUBSAN: out of bounds access into array of {} ({}-bit), index type {} ({}-bit)", data.array_type.name(), data.array_type.bit_width(), data.index_type.name(), data.index_type.bit_width()); - print_location(data.location); -} - -void __ubsan_handle_type_mismatch_v1(TypeMismatchData const&, ValueHandle) __attribute__((used)); -void __ubsan_handle_type_mismatch_v1(TypeMismatchData const& data, ValueHandle ptr) -{ - constexpr StringView kinds[] = { - "load of"sv, - "store to"sv, - "reference binding to"sv, - "member access within"sv, - "member call on"sv, - "constructor call on"sv, - "downcast of"sv, - "downcast of"sv, - "upcast of"sv, - "cast to virtual base of"sv, - "_Nonnull binding to"sv, - "dynamic operation on"sv - }; - - FlatPtr alignment = (FlatPtr)1 << data.log_alignment; - auto kind = kinds[data.type_check_kind]; - - if (!ptr) - critical_dmesgln("KUBSAN: {} null pointer of type {}", kind, data.type.name()); - else if ((FlatPtr)ptr & (alignment - 1)) - critical_dmesgln("KUBSAN: {} misaligned address {:p} of type {}", kind, ptr, data.type.name()); - else - critical_dmesgln("KUBSAN: {} address {:p} with insufficient space for type {}", kind, ptr, data.type.name()); - - print_location(data.location); -} - -void __ubsan_handle_alignment_assumption(AlignmentAssumptionData const&, ValueHandle, ValueHandle, ValueHandle) __attribute__((used)); -void __ubsan_handle_alignment_assumption(AlignmentAssumptionData const& data, ValueHandle pointer, ValueHandle alignment, ValueHandle offset) -{ - if (offset) - critical_dmesgln("KUBSAN: assumption of {:p} byte alignment (with offset of {:p} byte) for pointer {:p} of type {} failed", alignment, offset, pointer, data.type.name()); - else - critical_dmesgln("KUBSAN: assumption of {:p} byte alignment for pointer {:p} of type {} failed", alignment, pointer, data.type.name()); - - print_location(data.location); -} - -void __ubsan_handle_builtin_unreachable(UnreachableData const&) __attribute__((used)); -void __ubsan_handle_builtin_unreachable(UnreachableData const& data) -{ - critical_dmesgln("KUBSAN: execution reached an unreachable program point"); - print_location(data.location); -} - -void __ubsan_handle_missing_return(UnreachableData const&) __attribute__((used)); -void __ubsan_handle_missing_return(UnreachableData const& data) -{ - critical_dmesgln("KUBSAN: execution reached the end of a value-returning function without returning a value"); - print_location(data.location); -} - -void __ubsan_handle_implicit_conversion(ImplicitConversionData const&, ValueHandle, ValueHandle) __attribute__((used)); -void __ubsan_handle_implicit_conversion(ImplicitConversionData const& data, ValueHandle, ValueHandle) -{ - char const* src_signed = data.from_type.is_signed() ? "" : "un"; - char const* dst_signed = data.to_type.is_signed() ? "" : "un"; - critical_dmesgln("KUBSAN: implicit conversion from type {} ({}-bit, {}signed) to type {} ({}-bit, {}signed)", data.from_type.name(), data.from_type.bit_width(), src_signed, data.to_type.name(), data.to_type.bit_width(), dst_signed); - print_location(data.location); -} - -void __ubsan_handle_invalid_builtin(InvalidBuiltinData const) __attribute__((used)); -void __ubsan_handle_invalid_builtin(InvalidBuiltinData const data) -{ - critical_dmesgln("KUBSAN: passing invalid argument"); - print_location(data.location); -} - -void __ubsan_handle_pointer_overflow(PointerOverflowData const&, ValueHandle, ValueHandle) __attribute__((used)); -void __ubsan_handle_pointer_overflow(PointerOverflowData const& data, ValueHandle base, ValueHandle result) -{ - if (base == 0 && result == 0) - critical_dmesgln("KUBSAN: applied zero offset to nullptr"); - else if (base == 0 && result != 0) - critical_dmesgln("KUBSAN: applied non-zero offset {:p} to nullptr", result); - else if (base != 0 && result == 0) - critical_dmesgln("KUBSAN: applying non-zero offset to non-null pointer {:p} produced null pointer", base); - else - critical_dmesgln("KUBSAN: addition of unsigned offset to {:p} overflowed to {:p}", base, result); - print_location(data.location); -} - -void __ubsan_handle_function_type_mismatch(FunctionTypeMismatchData const&) __attribute__((used)); -void __ubsan_handle_function_type_mismatch(FunctionTypeMismatchData const& data) -{ - critical_dmesgln("KUBSAN: call to function through pointer to incorrect function type {}", data.type.name()); - print_location(data.location); -} -} diff --git a/Kernel/Syscalls/SyscallHandler.cpp b/Kernel/Syscalls/SyscallHandler.cpp deleted file mode 100644 index 408c38ac1d0..00000000000 --- a/Kernel/Syscalls/SyscallHandler.cpp +++ /dev/null @@ -1,203 +0,0 @@ -/* - * Copyright (c) 2018-2021, Andreas Kling - * Copyright (c) 2022, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace Kernel { - -extern bool g_in_system_shutdown; - -namespace Syscall { - -using Handler = auto (Process::*)(FlatPtr, FlatPtr, FlatPtr, FlatPtr) -> ErrorOr; -using HandlerWithRegisterState = auto (Process::*)(RegisterState&) -> ErrorOr; - -struct HandlerMetadata { - Handler handler; - NeedsBigProcessLock needs_lock; -}; - -#define __ENUMERATE_SYSCALL(sys_call, needs_lock) { bit_cast(&Process::sys$##sys_call), needs_lock }, -static HandlerMetadata const s_syscall_table[] = { - ENUMERATE_SYSCALLS(__ENUMERATE_SYSCALL) -}; -#undef __ENUMERATE_SYSCALL - -ErrorOr handle(RegisterState& regs, FlatPtr function, FlatPtr arg1, FlatPtr arg2, FlatPtr arg3, FlatPtr arg4) -{ - VERIFY_INTERRUPTS_ENABLED(); - auto* current_thread = Thread::current(); - auto& process = current_thread->process(); - current_thread->did_syscall(); - - PerformanceManager::add_syscall_event(*current_thread, regs); - - if (g_in_system_shutdown) - return ENOSYS; - - if (function >= Function::__Count) { - dbgln("Unknown syscall {} requested ({:p}, {:p}, {:p}, {:p})", function, arg1, arg2, arg3, arg4); - return ENOSYS; - } - - auto const syscall_metadata = s_syscall_table[function]; - if (syscall_metadata.handler == nullptr) { - dbgln("Null syscall {} requested, you probably need to rebuild this program!", function); - return ENOSYS; - } - - MutexLocker mutex_locker; - auto const needs_big_lock = syscall_metadata.needs_lock == NeedsBigProcessLock::Yes; - if (needs_big_lock) { - mutex_locker.attach_and_lock(process.big_lock()); - }; - - if (function == SC_exit || function == SC_exit_thread) { - // These syscalls need special handling since they never return to the caller. - // In these cases the process big lock will get released on the exit of the thread. - - if (auto* tracer = process.tracer(); tracer && tracer->is_tracing_syscalls()) { - regs.set_return_reg(0); - tracer->set_trace_syscalls(false); - process.tracer_trap(*current_thread, regs); // this triggers SIGTRAP and stops the thread! - } - - switch (function) { - case SC_exit: - process.sys$exit(arg1); - case SC_exit_thread: - process.sys$exit_thread(arg1, arg2, arg3); - default: - VERIFY_NOT_REACHED(); - } - } - - ErrorOr result { FlatPtr(nullptr) }; - if (function == SC_fork || function == SC_sigreturn) { - // These syscalls want the RegisterState& rather than individual parameters. - auto handler = bit_cast(syscall_metadata.handler); - result = (process.*(handler))(regs); - } else { - result = (process.*(syscall_metadata.handler))(arg1, arg2, arg3, arg4); - } - - return result; -} - -} - -extern "C" NEVER_INLINE void syscall_handler(TrapFrame* trap); -NEVER_INLINE void syscall_handler(TrapFrame* trap) -{ -#if ARCH(X86_64) - // Make sure SMAP protection is enabled on syscall entry. - clac(); -#elif ARCH(AARCH64) - // FIXME: Implement the security mechanism for aarch64 -#elif ARCH(RISCV64) - // FIXME: Implement the security mechanism for riscv64 -#else -# error Unknown architecture -#endif - - auto& regs = *trap->regs; - auto* current_thread = Thread::current(); - VERIFY(current_thread->previous_mode() == ExecutionMode::User); - auto& process = current_thread->process(); - if (process.is_dying()) { - // It's possible this thread is just about to make a syscall while another is - // is killing our process. - current_thread->die_if_needed(); - return; - } - - if (auto* tracer = process.tracer(); tracer && tracer->is_tracing_syscalls()) { - tracer->set_trace_syscalls(false); - process.tracer_trap(*current_thread, regs); // this triggers SIGTRAP and stops the thread! - } - - current_thread->yield_if_should_be_stopped(); - -#if ARCH(X86_64) - // Apply a random offset in the range 0-255 to the stack pointer, - // to make kernel stacks a bit less deterministic. - u32 lsw; - u32 msw; - read_tsc(lsw, msw); - - auto* ptr = (char*)__builtin_alloca(lsw & 0xff); - asm volatile("" - : "=m"(*ptr)); - - constexpr FlatPtr iopl_mask = 3u << 12; - - FlatPtr flags = regs.flags(); - if ((flags & (iopl_mask)) != 0) { - PANIC("Syscall from process with IOPL != 0"); - } -#elif ARCH(AARCH64) - // FIXME: Implement the security mechanism for aarch64 -#elif ARCH(RISCV64) - // FIXME: Implement the security mechanism for riscv64 -#else -# error Unknown architecture -#endif - - Memory::MemoryManager::validate_syscall_preconditions(process, regs); - - FlatPtr function; - FlatPtr arg1; - FlatPtr arg2; - FlatPtr arg3; - FlatPtr arg4; - regs.capture_syscall_params(function, arg1, arg2, arg3, arg4); - - auto result = Syscall::handle(regs, function, arg1, arg2, arg3, arg4); - - if (result.is_error()) { - regs.set_return_reg(-result.error().code()); - } else { - regs.set_return_reg(result.value()); - } - - if (auto* tracer = process.tracer(); tracer && tracer->is_tracing_syscalls()) { - tracer->set_trace_syscalls(false); - process.tracer_trap(*current_thread, regs); // this triggers SIGTRAP and stops the thread! - } - - current_thread->yield_if_should_be_stopped(); - - current_thread->check_dispatch_pending_signal(); - - // If the previous mode somehow changed something is seriously messed up... - VERIFY(current_thread->previous_mode() == ExecutionMode::User); - - // Check if we're supposed to return to userspace or just die. - current_thread->die_if_needed(); - - // Crash any processes which have committed a promise violation during syscall handling. - if (result.is_error() && result.error().code() == EPROMISEVIOLATION) { - VERIFY(current_thread->is_promise_violation_pending()); - current_thread->set_promise_violation_pending(false); - process.crash(SIGABRT, {}); - } else { - VERIFY(!current_thread->is_promise_violation_pending()); - } - - VERIFY(!g_scheduler_lock.is_locked_by_current_processor()); -} - -} diff --git a/Kernel/Syscalls/alarm.cpp b/Kernel/Syscalls/alarm.cpp deleted file mode 100644 index db0b1e6192e..00000000000 --- a/Kernel/Syscalls/alarm.cpp +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include - -namespace Kernel { - -ErrorOr Process::sys$alarm(unsigned seconds) -{ - VERIFY_NO_PROCESS_BIG_LOCK(this); - TRY(require_promise(Pledge::stdio)); - return m_alarm_timer.with([&](auto& timer) -> ErrorOr { - unsigned previous_alarm_remaining = 0; - if (timer) { - bool was_in_use = false; - if (TimerQueue::the().cancel_timer(*timer, &was_in_use)) { - // The timer hasn't fired. Round up the remaining time (if any) - Duration remaining = timer->remaining() + Duration::from_nanoseconds(999'999'999); - previous_alarm_remaining = remaining.to_truncated_seconds(); - } - // We had an existing alarm, must return a non-zero value here! - if (was_in_use && previous_alarm_remaining == 0) - previous_alarm_remaining = 1; - } - - if (seconds > 0) { - auto deadline = TimeManagement::the().current_time(CLOCK_REALTIME_COARSE); - deadline = deadline + Duration::from_seconds(seconds); - if (!timer) { - timer = TRY(adopt_nonnull_ref_or_enomem(new (nothrow) Timer)); - } - auto timer_was_added = TimerQueue::the().add_timer_without_id(*timer, CLOCK_REALTIME_COARSE, deadline, [this]() { - MUST(send_signal(SIGALRM, nullptr)); - }); - if (!timer_was_added) - return ENOMEM; - } - return previous_alarm_remaining; - }); -} - -} diff --git a/Kernel/Syscalls/anon_create.cpp b/Kernel/Syscalls/anon_create.cpp deleted file mode 100644 index 31c1a91da08..00000000000 --- a/Kernel/Syscalls/anon_create.cpp +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright (c) 2021, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include - -namespace Kernel { - -ErrorOr Process::sys$anon_create(size_t size, int options) -{ - VERIFY_NO_PROCESS_BIG_LOCK(this); - TRY(require_promise(Pledge::stdio)); - - if (!size) - return EINVAL; - - if (size % PAGE_SIZE) - return EINVAL; - - if (size > NumericLimits::max()) - return EINVAL; - - auto vmobject = TRY(Memory::AnonymousVMObject::try_create_purgeable_with_size(size, AllocationStrategy::AllocateNow)); - auto anon_file = TRY(AnonymousFile::try_create(move(vmobject))); - auto description = TRY(OpenFileDescription::try_create(move(anon_file))); - - description->set_writable(true); - description->set_readable(true); - - u32 fd_flags = 0; - if (options & O_CLOEXEC) - fd_flags |= FD_CLOEXEC; - - return m_fds.with_exclusive([&](auto& fds) -> ErrorOr { - auto new_fd = TRY(fds.allocate()); - fds[new_fd.fd].set(description, fd_flags); - return new_fd.fd; - }); -} - -} diff --git a/Kernel/Syscalls/chdir.cpp b/Kernel/Syscalls/chdir.cpp deleted file mode 100644 index 3cec0525802..00000000000 --- a/Kernel/Syscalls/chdir.cpp +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include - -namespace Kernel { - -ErrorOr Process::sys$chdir(Userspace user_path, size_t path_length) -{ - VERIFY_NO_PROCESS_BIG_LOCK(this); - TRY(require_promise(Pledge::rpath)); - auto path = TRY(get_syscall_path_argument(user_path, path_length)); - - RefPtr new_directory = TRY(VirtualFileSystem::the().open_directory(credentials(), path->view(), current_directory())); - m_current_directory.with([&](auto& current_directory) { - // NOTE: We use swap() here to avoid manipulating the ref counts while holding the lock. - swap(current_directory, new_directory); - }); - return 0; -} - -ErrorOr Process::sys$fchdir(int fd) -{ - VERIFY_NO_PROCESS_BIG_LOCK(this); - TRY(require_promise(Pledge::stdio)); - auto description = TRY(open_file_description(fd)); - if (!description->is_directory()) - return ENOTDIR; - if (!description->metadata().may_execute(credentials())) - return EACCES; - m_current_directory.with([&](auto& current_directory) { - current_directory = description->custody(); - }); - return 0; -} - -ErrorOr Process::sys$getcwd(Userspace buffer, size_t size) -{ - VERIFY_NO_PROCESS_BIG_LOCK(this); - TRY(require_promise(Pledge::rpath)); - - if (size > NumericLimits::max()) - return EINVAL; - - auto path = TRY(current_directory()->try_serialize_absolute_path()); - size_t ideal_size = path->length() + 1; - auto size_to_copy = min(ideal_size, size); - TRY(copy_to_user(buffer, path->characters(), size_to_copy)); - // Note: we return the whole size here, not the copied size. - return ideal_size; -} - -} diff --git a/Kernel/Syscalls/chmod.cpp b/Kernel/Syscalls/chmod.cpp deleted file mode 100644 index 2105aea6580..00000000000 --- a/Kernel/Syscalls/chmod.cpp +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * Copyright (c) 2022, Daniel Bertalan - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include - -namespace Kernel { - -ErrorOr Process::sys$chmod(Userspace user_params) -{ - VERIFY_NO_PROCESS_BIG_LOCK(this); - TRY(require_promise(Pledge::fattr)); - auto params = TRY(copy_typed_from_user(user_params)); - auto path = TRY(get_syscall_path_argument(params.path)); - CustodyBase base(params.dirfd, path->view()); - TRY(VirtualFileSystem::the().chmod(credentials(), path->view(), params.mode, base, params.follow_symlinks ? 0 : O_NOFOLLOW_NOERROR)); - return 0; -} - -ErrorOr Process::sys$fchmod(int fd, mode_t mode) -{ - VERIFY_NO_PROCESS_BIG_LOCK(this); - TRY(require_promise(Pledge::fattr)); - auto description = TRY(open_file_description(fd)); - TRY(description->chmod(credentials(), mode)); - return 0; -} - -} diff --git a/Kernel/Syscalls/chown.cpp b/Kernel/Syscalls/chown.cpp deleted file mode 100644 index 563390a0765..00000000000 --- a/Kernel/Syscalls/chown.cpp +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include - -namespace Kernel { - -ErrorOr Process::sys$fchown(int fd, UserID uid, GroupID gid) -{ - VERIFY_NO_PROCESS_BIG_LOCK(this); - TRY(require_promise(Pledge::chown)); - auto description = TRY(open_file_description(fd)); - TRY(description->chown(credentials(), uid, gid)); - return 0; -} - -ErrorOr Process::sys$chown(Userspace user_params) -{ - VERIFY_NO_PROCESS_BIG_LOCK(this); - TRY(require_promise(Pledge::chown)); - auto params = TRY(copy_typed_from_user(user_params)); - auto path = TRY(get_syscall_path_argument(params.path)); - CustodyBase base(params.dirfd, path->view()); - TRY(VirtualFileSystem::the().chown(credentials(), path->view(), params.uid, params.gid, base, params.follow_symlinks ? 0 : O_NOFOLLOW_NOERROR)); - return 0; -} - -} diff --git a/Kernel/Syscalls/clock.cpp b/Kernel/Syscalls/clock.cpp deleted file mode 100644 index d85b111b07d..00000000000 --- a/Kernel/Syscalls/clock.cpp +++ /dev/null @@ -1,131 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include - -namespace Kernel { - -ErrorOr Process::sys$map_time_page() -{ - VERIFY_NO_PROCESS_BIG_LOCK(this); - TRY(require_promise(Pledge::stdio)); - - auto& vmobject = TimeManagement::the().time_page_vmobject(); - - return address_space().with([&](auto& space) -> ErrorOr { - auto* region = TRY(space->allocate_region_with_vmobject(Memory::RandomizeVirtualAddress::Yes, {}, PAGE_SIZE, PAGE_SIZE, vmobject, 0, "Kernel time page"sv, PROT_READ, true)); - return region->vaddr().get(); - }); -} - -ErrorOr Process::sys$clock_gettime(clockid_t clock_id, Userspace user_ts) -{ - VERIFY_NO_PROCESS_BIG_LOCK(this); - TRY(require_promise(Pledge::stdio)); - - TRY(TimeManagement::validate_clock_id(clock_id)); - - auto ts = TimeManagement::the().current_time(clock_id).to_timespec(); - TRY(copy_to_user(user_ts, &ts)); - return 0; -} - -ErrorOr Process::sys$clock_settime(clockid_t clock_id, Userspace user_ts) -{ - VERIFY_NO_PROCESS_BIG_LOCK(this); - TRY(require_promise(Pledge::settime)); - - auto credentials = this->credentials(); - if (!credentials->is_superuser()) - return EPERM; - - auto time = UnixDateTime::epoch() + TRY(copy_time_from_user(user_ts)); - - switch (clock_id) { - case CLOCK_REALTIME: - TimeManagement::the().set_epoch_time(time); - break; - default: - return EINVAL; - } - return 0; -} - -ErrorOr Process::sys$clock_nanosleep(Userspace user_params) -{ - VERIFY_NO_PROCESS_BIG_LOCK(this); - TRY(require_promise(Pledge::stdio)); - auto params = TRY(copy_typed_from_user(user_params)); - - auto requested_sleep = TRY(copy_time_from_user(params.requested_sleep)); - - bool is_absolute; - switch (params.flags) { - case 0: - is_absolute = false; - break; - case TIMER_ABSTIME: - is_absolute = true; - break; - default: - return EINVAL; - } - - TRY(TimeManagement::validate_clock_id(params.clock_id)); - - bool was_interrupted; - if (is_absolute) { - was_interrupted = Thread::current()->sleep_until(params.clock_id, requested_sleep).was_interrupted(); - } else { - Duration remaining_sleep; - was_interrupted = Thread::current()->sleep(params.clock_id, requested_sleep, &remaining_sleep).was_interrupted(); - timespec remaining_sleep_ts = remaining_sleep.to_timespec(); - if (was_interrupted && params.remaining_sleep) { - TRY(copy_to_user(params.remaining_sleep, &remaining_sleep_ts)); - } - } - if (was_interrupted) - return EINTR; - return 0; -} - -ErrorOr Process::sys$clock_getres(Userspace user_params) -{ - VERIFY_NO_PROCESS_BIG_LOCK(this); - auto params = TRY(copy_typed_from_user(user_params)); - - TRY(TimeManagement::validate_clock_id(params.clock_id)); - - auto ts = TimeManagement::the().clock_resolution().to_timespec(); - TRY(copy_to_user(params.result, &ts)); - return 0; -} - -ErrorOr Process::sys$adjtime(Userspace user_delta, Userspace user_old_delta) -{ - VERIFY_NO_PROCESS_BIG_LOCK(this); - if (user_old_delta) { - auto old_delta_duration = TimeManagement::the().remaining_epoch_time_adjustment(); - auto old_delta = old_delta_duration.to_timeval(); - TRY(copy_to_user(user_old_delta, &old_delta)); - } - - if (user_delta) { - TRY(require_promise(Pledge::settime)); - auto credentials = this->credentials(); - if (!credentials->is_superuser()) - return EPERM; - auto delta = TRY(copy_time_from_user(user_delta)); - - TimeManagement::the().set_remaining_epoch_time_adjustment(delta); - } - - return 0; -} - -} diff --git a/Kernel/Syscalls/debug.cpp b/Kernel/Syscalls/debug.cpp deleted file mode 100644 index 5da74a8329f..00000000000 --- a/Kernel/Syscalls/debug.cpp +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include - -namespace Kernel { - -ErrorOr Process::sys$dump_backtrace() -{ - VERIFY_NO_PROCESS_BIG_LOCK(this); - dump_backtrace(); - return 0; -} - -ErrorOr Process::sys$dbgputstr(Userspace characters, size_t size) -{ - VERIFY_NO_PROCESS_BIG_LOCK(this); - if (size == 0) - return 0; - - if (size <= 1024) { - char buffer[1024]; - TRY(copy_from_user(buffer, characters, size)); - dbgputstr(buffer, size); - return size; - } - - auto string = TRY(try_copy_kstring_from_user(characters, size)); - dbgputstr(string->view()); - return string->length(); -} - -} diff --git a/Kernel/Syscalls/disown.cpp b/Kernel/Syscalls/disown.cpp deleted file mode 100644 index d68bdb51e59..00000000000 --- a/Kernel/Syscalls/disown.cpp +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright (c) 2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include - -namespace Kernel { - -ErrorOr Process::sys$disown(ProcessID pid) -{ - VERIFY_NO_PROCESS_BIG_LOCK(this); - TRY(require_promise(Pledge::proc)); - auto process = Process::from_pid_in_same_jail(pid); - if (!process) - return ESRCH; - TRY(process->with_mutable_protected_data([this](auto& protected_data) -> ErrorOr { - if (protected_data.ppid != this->pid()) - return ECHILD; - protected_data.ppid = 0; - return {}; - })); - process->disowned_by_waiter(*this); - return 0; -} -} diff --git a/Kernel/Syscalls/dup2.cpp b/Kernel/Syscalls/dup2.cpp deleted file mode 100644 index dab016e6a4d..00000000000 --- a/Kernel/Syscalls/dup2.cpp +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include - -namespace Kernel { - -ErrorOr Process::sys$dup2(int old_fd, int new_fd) -{ - VERIFY_NO_PROCESS_BIG_LOCK(this); - TRY(require_promise(Pledge::stdio)); - return m_fds.with_exclusive([&](auto& fds) -> ErrorOr { - auto description = TRY(fds.open_file_description(old_fd)); - if (old_fd == new_fd) - return new_fd; - if (new_fd < 0 || static_cast(new_fd) >= OpenFileDescriptions::max_open()) - return EINVAL; - if (!fds.m_fds_metadatas[new_fd].is_allocated()) - fds.m_fds_metadatas[new_fd].allocate(); - fds[new_fd].set(move(description)); - return new_fd; - }); -} - -} diff --git a/Kernel/Syscalls/emuctl.cpp b/Kernel/Syscalls/emuctl.cpp deleted file mode 100644 index 1599fa60bb3..00000000000 --- a/Kernel/Syscalls/emuctl.cpp +++ /dev/null @@ -1,17 +0,0 @@ -/* - * Copyright (c) 2021, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include - -namespace Kernel { - -ErrorOr Process::sys$emuctl() -{ - VERIFY_NO_PROCESS_BIG_LOCK(this); - return ENOSYS; -} - -} diff --git a/Kernel/Syscalls/execve.cpp b/Kernel/Syscalls/execve.cpp deleted file mode 100644 index 140a49337d6..00000000000 --- a/Kernel/Syscalls/execve.cpp +++ /dev/null @@ -1,955 +0,0 @@ -/* - * Copyright (c) 2018-2021, Andreas Kling - * Copyright (c) 2022, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace Kernel { - -extern Memory::Region* g_signal_trampoline_region; - -struct LoadResult { - FlatPtr load_base { 0 }; - FlatPtr entry_eip { 0 }; - size_t size { 0 }; - LockWeakPtr stack_region; -}; - -static constexpr size_t auxiliary_vector_size = 15; -static Array generate_auxiliary_vector(FlatPtr load_base, FlatPtr entry_eip, UserID uid, UserID euid, GroupID gid, GroupID egid, StringView executable_path, Optional const& main_program_fd_allocation); - -static bool validate_stack_size(Vector> const& arguments, Vector>& environment, Array const& auxiliary) -{ - size_t total_arguments_size = 0; - size_t total_environment_size = 0; - size_t total_auxiliary_size = 0; - - for (auto const& a : arguments) - total_arguments_size += a->length() + 1; - for (auto const& e : environment) - total_environment_size += e->length() + 1; - for (auto const& v : auxiliary) { - if (!v.optional_string.is_empty()) - total_auxiliary_size += round_up_to_power_of_two(v.optional_string.length() + 1, sizeof(FlatPtr)); - - if (v.auxv.a_type == ELF::AuxiliaryValue::Random) - total_auxiliary_size += round_up_to_power_of_two(16, sizeof(FlatPtr)); - } - - total_arguments_size += sizeof(char*) * (arguments.size() + 1); - total_environment_size += sizeof(char*) * (environment.size() + 1); - total_auxiliary_size += sizeof(auxv_t) * auxiliary.size(); - - if (total_arguments_size > Process::max_arguments_size) - return false; - - if (total_environment_size > Process::max_environment_size) - return false; - - if (total_auxiliary_size > Process::max_auxiliary_size) - return false; - - return true; -} - -static ErrorOr make_userspace_context_for_main_thread([[maybe_unused]] ThreadRegisters& regs, Memory::Region& region, Vector> const& arguments, - Vector> const& environment, Array auxiliary_values) -{ - FlatPtr new_sp = region.range().end().get(); - - // Add some bits of randomness to the user stack pointer. - new_sp -= round_up_to_power_of_two(get_fast_random() % 4096, 16); - - auto push_on_new_stack = [&new_sp](FlatPtr value) { - new_sp -= sizeof(FlatPtr); - Userspace stack_ptr = new_sp; - auto result = copy_to_user(stack_ptr, &value); - VERIFY(!result.is_error()); - }; - - auto push_aux_value_on_new_stack = [&new_sp](auxv_t value) { - new_sp -= sizeof(auxv_t); - Userspace stack_ptr = new_sp; - auto result = copy_to_user(stack_ptr, &value); - VERIFY(!result.is_error()); - }; - - auto push_string_on_new_stack = [&new_sp](StringView string) { - new_sp -= round_up_to_power_of_two(string.length() + 1, sizeof(FlatPtr)); - Userspace stack_ptr = new_sp; - auto result = copy_to_user(stack_ptr, string.characters_without_null_termination(), string.length() + 1); - VERIFY(!result.is_error()); - }; - - Vector argv_entries; - for (auto const& argument : arguments) { - push_string_on_new_stack(argument->view()); - TRY(argv_entries.try_append(new_sp)); - } - - Vector env_entries; - for (auto const& variable : environment) { - push_string_on_new_stack(variable->view()); - TRY(env_entries.try_append(new_sp)); - } - - for (auto& value : auxiliary_values) { - if (!value.optional_string.is_empty()) { - push_string_on_new_stack(value.optional_string); - value.auxv.a_un.a_ptr = (void*)new_sp; - } - if (value.auxv.a_type == ELF::AuxiliaryValue::Random) { - u8 random_bytes[16] {}; - get_fast_random_bytes({ random_bytes, sizeof(random_bytes) }); - push_string_on_new_stack({ random_bytes, sizeof(random_bytes) }); - value.auxv.a_un.a_ptr = (void*)new_sp; - } - } - - for (ssize_t i = auxiliary_values.size() - 1; i >= 0; --i) { - auto& value = auxiliary_values[i]; - push_aux_value_on_new_stack(value.auxv); - } - - push_on_new_stack(0); - for (ssize_t i = env_entries.size() - 1; i >= 0; --i) - push_on_new_stack(env_entries[i]); - FlatPtr envp = new_sp; - - push_on_new_stack(0); - for (ssize_t i = argv_entries.size() - 1; i >= 0; --i) - push_on_new_stack(argv_entries[i]); - FlatPtr argv = new_sp; - - // NOTE: The stack needs to be 16-byte aligned. - new_sp -= new_sp % 16; - -#if ARCH(X86_64) - regs.rdi = argv_entries.size(); - regs.rsi = argv; - regs.rdx = envp; -#elif ARCH(AARCH64) - regs.x[0] = argv_entries.size(); - regs.x[1] = argv; - regs.x[2] = envp; -#elif ARCH(RISCV64) - regs.x[9] = argv_entries.size(); - regs.x[10] = argv; - regs.x[11] = envp; -#else -# error Unknown architecture -#endif - - VERIFY(new_sp % 16 == 0); - - // FIXME: The way we're setting up the stack and passing arguments to the entry point isn't ABI-compliant - return new_sp; -} - -struct RequiredLoadRange { - FlatPtr start { 0 }; - FlatPtr end { 0 }; -}; - -static ErrorOr get_required_load_range(OpenFileDescription& program_description) -{ - auto& inode = *(program_description.inode()); - auto vmobject = TRY(Memory::SharedInodeVMObject::try_create_with_inode(inode)); - - size_t executable_size = inode.size(); - size_t rounded_executable_size = TRY(Memory::page_round_up(executable_size)); - auto region = TRY(MM.allocate_kernel_region_with_vmobject(*vmobject, rounded_executable_size, "ELF memory range calculation"sv, Memory::Region::Access::Read)); - auto elf_image = ELF::Image(region->vaddr().as_ptr(), executable_size); - if (!elf_image.is_valid()) { - return EINVAL; - } - - RequiredLoadRange range {}; - elf_image.for_each_program_header([&range](auto const& pheader) { - if (pheader.type() != PT_LOAD) - return; - - auto region_start = (FlatPtr)pheader.vaddr().as_ptr(); - auto region_end = region_start + pheader.size_in_memory(); - if (range.start == 0 || region_start < range.start) - range.start = region_start; - if (range.end == 0 || region_end > range.end) - range.end = region_end; - }); - - // If there's nothing to load, there's nothing to execute - if (range.start == range.end) - return EINVAL; - - VERIFY(range.end > range.start); - return range; -} - -static ErrorOr get_load_offset(Elf_Ehdr const& main_program_header, OpenFileDescription& main_program_description, OpenFileDescription* interpreter_description) -{ - constexpr FlatPtr load_range_start = 0x08000000; - constexpr FlatPtr load_range_size = 65536 * PAGE_SIZE; // 2**16 * PAGE_SIZE = 256MB - constexpr FlatPtr minimum_load_offset_randomization_size = 10 * MiB; - - auto random_load_offset_in_range([](auto start, auto size) { - return Memory::page_round_down(start + get_good_random() % size); - }); - - if (main_program_header.e_type == ET_DYN) { - return random_load_offset_in_range(load_range_start, load_range_size); - } - - if (main_program_header.e_type != ET_EXEC) - return EINVAL; - - auto main_program_load_range = TRY(get_required_load_range(main_program_description)); - - RequiredLoadRange selected_range {}; - - if (interpreter_description) { - auto interpreter_load_range = TRY(get_required_load_range(*interpreter_description)); - - auto interpreter_size_in_memory = interpreter_load_range.end - interpreter_load_range.start; - auto interpreter_load_range_end = load_range_start + load_range_size - interpreter_size_in_memory; - - // No intersection - if (main_program_load_range.end < load_range_start || main_program_load_range.start > interpreter_load_range_end) - return random_load_offset_in_range(load_range_start, load_range_size); - - RequiredLoadRange first_available_part = { load_range_start, main_program_load_range.start }; - RequiredLoadRange second_available_part = { main_program_load_range.end, interpreter_load_range_end }; - - // Select larger part - if (first_available_part.end - first_available_part.start > second_available_part.end - second_available_part.start) - selected_range = first_available_part; - else - selected_range = second_available_part; - } else - selected_range = main_program_load_range; - - // If main program is too big and leaves us without enough space for adequate loader randomization - if (selected_range.end - selected_range.start < minimum_load_offset_randomization_size) - return E2BIG; - - return random_load_offset_in_range(selected_range.start, selected_range.end - selected_range.start); -} - -enum class ShouldAllowSyscalls { - No, - Yes, -}; - -static ErrorOr load_elf_object(Memory::AddressSpace& new_space, OpenFileDescription& object_description, - FlatPtr load_offset, ShouldAllowSyscalls should_allow_syscalls, Optional minimum_stack_size = {}) -{ - auto& inode = *(object_description.inode()); - auto vmobject = TRY(Memory::SharedInodeVMObject::try_create_with_inode(inode)); - - if (vmobject->writable_mappings()) { - dbgln("Refusing to execute a write-mapped program"); - return ETXTBSY; - } - - size_t executable_size = inode.size(); - size_t rounded_executable_size = TRY(Memory::page_round_up(executable_size)); - - auto executable_region = TRY(MM.allocate_kernel_region_with_vmobject(*vmobject, rounded_executable_size, "ELF loading"sv, Memory::Region::Access::Read)); - auto elf_image = ELF::Image(executable_region->vaddr().as_ptr(), executable_size); - - if (!elf_image.is_valid()) - return ENOEXEC; - - FlatPtr load_base_address = 0; - size_t stack_size = Thread::default_userspace_stack_size; - - if (minimum_stack_size.has_value() && minimum_stack_size.value() > stack_size) - stack_size = minimum_stack_size.value(); - - auto elf_name = TRY(object_description.pseudo_path()); - VERIFY(!Processor::in_critical()); - - Memory::MemoryManager::enter_address_space(new_space); - - auto load_writable_section = [&](auto& program_header) -> ErrorOr { - // Writable section: create a copy in memory. - VERIFY(program_header.alignment() % PAGE_SIZE == 0); - - if (!elf_image.is_within_image(program_header.raw_data(), program_header.size_in_image())) { - dbgln("Shenanigans! Writable ELF PT_LOAD header sneaks outside of executable."); - return ENOEXEC; - } - - int prot = 0; - if (program_header.is_readable()) - prot |= PROT_READ; - if (program_header.is_writable()) - prot |= PROT_WRITE; - auto region_name = TRY(KString::formatted("{} (data-{}{})", elf_name, program_header.is_readable() ? "r" : "", program_header.is_writable() ? "w" : "")); - - auto range_base = VirtualAddress { Memory::page_round_down(program_header.vaddr().offset(load_offset).get()) }; - size_t rounded_range_end = TRY(Memory::page_round_up(program_header.vaddr().offset(load_offset).offset(program_header.size_in_memory()).get())); - auto range_end = VirtualAddress { rounded_range_end }; - - auto region = TRY(new_space.allocate_region(Memory::RandomizeVirtualAddress::Yes, range_base, range_end.get() - range_base.get(), PAGE_SIZE, region_name->view(), prot, AllocationStrategy::Reserve)); - - // It's not always the case with PIE executables (and very well shouldn't be) that the - // virtual address in the program header matches the one we end up giving the process. - // In order to copy the data image correctly into memory, we need to copy the data starting at - // the right initial page offset into the pages allocated for the elf_alloc-XX section. - // FIXME: There's an opportunity to munmap, or at least mprotect, the padding space between - // the .text and .data PT_LOAD sections of the executable. - // Accessing it would definitely be a bug. - auto page_offset = program_header.vaddr(); - page_offset.mask(~PAGE_MASK); - TRY(copy_to_user((u8*)region->vaddr().as_ptr() + page_offset.get(), program_header.raw_data(), program_header.size_in_image())); - return {}; - }; - - auto load_section = [&](auto& program_header) -> ErrorOr { - if (program_header.size_in_memory() == 0) - return {}; - - if (program_header.is_writable()) - return load_writable_section(program_header); - - // Non-writable section: map the executable itself in memory. - VERIFY(program_header.alignment() % PAGE_SIZE == 0); - int prot = 0; - if (program_header.is_readable()) - prot |= PROT_READ; - if (program_header.is_writable()) - prot |= PROT_WRITE; - if (program_header.is_executable()) - prot |= PROT_EXEC; - - auto range_base = VirtualAddress { Memory::page_round_down(program_header.vaddr().offset(load_offset).get()) }; - size_t rounded_range_end = TRY(Memory::page_round_up(program_header.vaddr().offset(load_offset).offset(program_header.size_in_memory()).get())); - auto range_end = VirtualAddress { rounded_range_end }; - auto region = TRY(new_space.allocate_region_with_vmobject(Memory::RandomizeVirtualAddress::Yes, range_base, range_end.get() - range_base.get(), program_header.alignment(), *vmobject, program_header.offset(), elf_name->view(), prot, true)); - if (program_header.is_executable()) - region->set_initially_loaded_executable_segment(); - - if (should_allow_syscalls == ShouldAllowSyscalls::Yes) - region->set_syscall_region(true); - if (program_header.offset() == 0) - load_base_address = (FlatPtr)region->vaddr().as_ptr(); - return {}; - }; - - auto load_elf_program_header = [&](auto& program_header) -> ErrorOr { - if (program_header.type() == PT_LOAD) - return load_section(program_header); - - // NOTE: We ignore other program header types. - return {}; - }; - - TRY([&] { - ErrorOr result; - elf_image.for_each_program_header([&](ELF::Image::ProgramHeader const& program_header) { - result = load_elf_program_header(program_header); - return result.is_error() ? IterationDecision::Break : IterationDecision::Continue; - }); - return result; - }()); - - if (!elf_image.entry().offset(load_offset).get()) { - dbgln("do_exec: Failure loading program, entry pointer is invalid! {})", elf_image.entry().offset(load_offset)); - return ENOEXEC; - } - - auto* stack_region = TRY(new_space.allocate_region(Memory::RandomizeVirtualAddress::Yes, {}, stack_size, PAGE_SIZE, "Stack (Main thread)"sv, PROT_READ | PROT_WRITE, AllocationStrategy::Reserve)); - stack_region->set_stack(true); - - return LoadResult { - load_base_address, - elf_image.entry().offset(load_offset).get(), - executable_size, - TRY(stack_region->try_make_weak_ptr()) - }; -} - -ErrorOr -Process::load(Memory::AddressSpace& new_space, NonnullRefPtr main_program_description, - RefPtr interpreter_description, Elf_Ehdr const& main_program_header, Optional minimum_stack_size) -{ - auto load_offset = TRY(get_load_offset(main_program_header, main_program_description, interpreter_description)); - - if (interpreter_description.is_null()) - return TRY(load_elf_object(new_space, main_program_description, load_offset, ShouldAllowSyscalls::No, minimum_stack_size)); - - return TRY(load_elf_object(new_space, *interpreter_description, load_offset, ShouldAllowSyscalls::Yes, minimum_stack_size)); -} - -void Process::clear_signal_handlers_for_exec() -{ - // Comments are as they are presented in the POSIX specification, but slightly out of order. - for (size_t signal = 0; signal < m_signal_action_data.size(); signal++) { - // Except for SIGCHLD, signals set to be ignored by the calling process image shall be set to be ignored by the new process image. - // If the SIGCHLD signal is set to be ignored by the calling process image, it is unspecified whether the SIGCHLD signal is set - // to be ignored or to the default action in the new process image. - if (signal != SIGCHLD && m_signal_action_data[signal].handler_or_sigaction.get() == reinterpret_cast(SIG_IGN)) { - m_signal_action_data[signal] = {}; - m_signal_action_data[signal].handler_or_sigaction.set(reinterpret_cast(SIG_IGN)); - continue; - } - - // Signals set to the default action in the calling process image shall be set to the default action in the new process image. - // Signals set to be caught by the calling process image shall be set to the default action in the new process image. - m_signal_action_data[signal] = {}; - } -} - -ErrorOr Process::do_exec(NonnullRefPtr main_program_description, Vector> arguments, Vector> environment, - RefPtr interpreter_description, Thread*& new_main_thread, InterruptsState& previous_interrupts_state, Elf_Ehdr const& main_program_header, Optional minimum_stack_size) -{ - VERIFY(is_user_process()); - VERIFY(!Processor::in_critical()); - auto main_program_metadata = main_program_description->metadata(); - // NOTE: Don't allow running SUID binaries at all if we are in a jail. - TRY(Process::current().jail().with([&](auto const& my_jail) -> ErrorOr { - if (my_jail && (main_program_metadata.is_setuid() || main_program_metadata.is_setgid())) { - return Error::from_errno(EPERM); - } - return {}; - })); - - // Although we *could* handle a pseudo_path here, trying to execute something that doesn't have - // a custody (e.g. BlockDevice or RandomDevice) is pretty suspicious anyway. - auto path = TRY(main_program_description->original_absolute_path()); - - dbgln_if(EXEC_DEBUG, "do_exec: {}", path); - - auto last_part = path->view().find_last_split_view('/'); - - auto allocated_space = TRY(Memory::AddressSpace::try_create(*this, nullptr)); - OwnPtr old_space; - auto& new_space = m_space.with([&](auto& space) -> Memory::AddressSpace& { - old_space = move(space); - space = move(allocated_space); - return *space; - }); - ArmedScopeGuard space_guard([&]() { - // If we failed at any point from now on we have to revert back to the old address space - m_space.with([&](auto& space) { - space = old_space.release_nonnull(); - }); - Memory::MemoryManager::enter_process_address_space(*this); - }); - - auto load_result = TRY(load(new_space, main_program_description, interpreter_description, main_program_header, minimum_stack_size)); - - // NOTE: We don't need the interpreter executable description after this point. - // We destroy it here to prevent it from getting destroyed when we return from this function. - // That's important because when we're returning from this function, we're in a very delicate - // state where we can't block (e.g by trying to acquire a mutex in description teardown.) - bool has_interpreter = interpreter_description; - interpreter_description = nullptr; - - auto* signal_trampoline_region = TRY(new_space.allocate_region_with_vmobject(Memory::RandomizeVirtualAddress::Yes, {}, PAGE_SIZE, PAGE_SIZE, g_signal_trampoline_region->vmobject(), 0, "Signal trampoline"sv, PROT_READ | PROT_EXEC, true)); - signal_trampoline_region->set_syscall_region(true); - - // (For dynamically linked executable) Allocate an FD for passing the main executable to the dynamic loader. - Optional main_program_fd_allocation; - if (has_interpreter) - main_program_fd_allocation = TRY(allocate_fd()); - - auto old_credentials = this->credentials(); - auto new_credentials = old_credentials; - auto old_process_attached_jail = m_attached_jail.with([&](auto& jail) -> RefPtr { return jail; }); - auto old_scoped_list = m_jail_process_list.with([&](auto& list) -> RefPtr { return list; }); - - bool executable_is_setid = false; - - if (!(main_program_description->custody()->mount_flags() & MS_NOSUID)) { - auto new_euid = old_credentials->euid(); - auto new_egid = old_credentials->egid(); - auto new_suid = old_credentials->suid(); - auto new_sgid = old_credentials->sgid(); - - if (main_program_metadata.is_setuid()) { - executable_is_setid = true; - new_euid = main_program_metadata.uid; - new_suid = main_program_metadata.uid; - } - if (main_program_metadata.is_setgid()) { - executable_is_setid = true; - new_egid = main_program_metadata.gid; - new_sgid = main_program_metadata.gid; - } - - if (executable_is_setid) { - new_credentials = TRY(Credentials::create( - old_credentials->uid(), - old_credentials->gid(), - new_euid, - new_egid, - new_suid, - new_sgid, - old_credentials->extra_gids(), - old_credentials->sid(), - old_credentials->pgid())); - } - } - - // We commit to the new executable at this point. There is no turning back! - space_guard.disarm(); - - // Prevent other processes from attaching to us with ptrace while we're doing this. - MutexLocker ptrace_locker(ptrace_lock()); - - // Disable profiling temporarily in case it's running on this process. - auto was_profiling = m_profiling; - TemporaryChange profiling_disabler(m_profiling, false); - - kill_threads_except_self(); - - with_mutable_protected_data([&](auto& protected_data) { - protected_data.credentials = move(new_credentials); - protected_data.dumpable = !executable_is_setid; - protected_data.executable_is_setid = executable_is_setid; - }); - - m_executable.with([&](auto& executable) { executable = main_program_description->custody(); }); - m_arguments = move(arguments); - m_attached_jail.with([&](auto& jail) { - jail = old_process_attached_jail; - }); - - m_jail_process_list.with([&](auto& list) { - list = old_scoped_list; - }); - - m_environment = move(environment); - - TRY(m_unveil_data.with([&](auto& unveil_data) -> ErrorOr { - TRY(m_exec_unveil_data.with([&](auto& exec_unveil_data) -> ErrorOr { - // Note: If we have exec unveil data being waiting to be dispatched - // to the current execve'd program, then we apply the unveil data and - // ensure it is locked in the new program. - if (exec_unveil_data.state == VeilState::Dropped) { - unveil_data.state = VeilState::LockedInherited; - exec_unveil_data.state = VeilState::None; - unveil_data.paths = TRY(exec_unveil_data.paths.deep_copy()); - } else { - unveil_data.state = VeilState::None; - exec_unveil_data.state = VeilState::None; - unveil_data.paths.clear(); - unveil_data.paths.set_metadata({ TRY(KString::try_create("/"sv)), UnveilAccess::None, false }); - } - exec_unveil_data.paths.clear(); - exec_unveil_data.paths.set_metadata({ TRY(KString::try_create("/"sv)), UnveilAccess::None, false }); - return {}; - })); - return {}; - })); - - m_coredump_properties.for_each([](auto& property) { - property = {}; - }); - - auto* current_thread = Thread::current(); - current_thread->reset_signals_for_exec(); - - clear_signal_handlers_for_exec(); - - clear_futex_queues_on_exec(); - - m_fds.with_exclusive([&](auto& fds) { - fds.change_each([&](auto& file_description_metadata) { - if (file_description_metadata.is_valid() && file_description_metadata.flags() & FD_CLOEXEC) - file_description_metadata = {}; - }); - }); - - if (main_program_fd_allocation.has_value()) { - main_program_description->set_readable(true); - m_fds.with_exclusive([&](auto& fds) { fds[main_program_fd_allocation->fd].set(move(main_program_description), FD_CLOEXEC); }); - } - - new_main_thread = nullptr; - if (¤t_thread->process() == this) { - new_main_thread = current_thread; - } else { - for_each_thread([&](auto& thread) { - new_main_thread = &thread; - return IterationDecision::Break; - }); - } - VERIFY(new_main_thread); - - auto credentials = this->credentials(); - auto auxv = generate_auxiliary_vector(load_result.load_base, load_result.entry_eip, credentials->uid(), credentials->euid(), credentials->gid(), credentials->egid(), path->view(), main_program_fd_allocation); - - // FIXME: How much stack space does process startup need? - if (!validate_stack_size(m_arguments, m_environment, auxv)) - return E2BIG; - - // NOTE: We create the new stack before disabling interrupts since it will zero-fault - // and we don't want to deal with faults after this point. - auto new_userspace_sp = TRY(make_userspace_context_for_main_thread(new_main_thread->regs(), *load_result.stack_region.unsafe_ptr(), m_arguments, m_environment, move(auxv))); - - // NOTE: The Process and its first thread share the same name. - set_name(last_part); - new_main_thread->set_name(last_part); - - if (wait_for_tracer_at_next_execve()) { - // Make sure we release the ptrace lock here or the tracer will block forever. - ptrace_locker.unlock(); - Thread::current()->send_urgent_signal_to_self(SIGSTOP); - } else { - // Unlock regardless before disabling interrupts. - // Ensure we always unlock after checking ptrace status to avoid TOCTOU ptrace issues - ptrace_locker.unlock(); - } - - // We enter a critical section here because we don't want to get interrupted between do_exec() - // and Processor::assume_context() or the next context switch. - // If we used an InterruptDisabler that calls enable_interrupts() on exit, we might timer tick'd too soon in exec(). - Processor::enter_critical(); - previous_interrupts_state = Processor::interrupts_state(); - Processor::disable_interrupts(); - - // NOTE: Be careful to not trigger any page faults below! - - with_mutable_protected_data([&](auto& protected_data) { - protected_data.promises = protected_data.execpromises; - protected_data.has_promises = protected_data.has_execpromises; - - protected_data.execpromises = 0; - protected_data.has_execpromises = false; - - protected_data.signal_trampoline = signal_trampoline_region->vaddr(); - - // FIXME: PID/TID ISSUE - protected_data.pid = new_main_thread->tid().value(); - }); - - new_main_thread->reset_fpu_state(); - - auto& regs = new_main_thread->m_regs; - address_space().with([&](auto& space) { - regs.set_exec_state(load_result.entry_eip, new_userspace_sp, *space); - }); - - { - TemporaryChange profiling_disabler(m_profiling, was_profiling); - PerformanceManager::add_process_exec_event(*this); - } - - u32 lock_count_to_restore; - [[maybe_unused]] auto rc = big_lock().force_unlock_exclusive_if_locked(lock_count_to_restore); - VERIFY_INTERRUPTS_DISABLED(); - VERIFY(Processor::in_critical()); - return {}; -} - -static Array generate_auxiliary_vector(FlatPtr load_base, FlatPtr entry_eip, UserID uid, UserID euid, GroupID gid, GroupID egid, StringView executable_path, Optional const& main_program_fd_allocation) -{ - return { { - // PHDR/EXECFD - // PH* - { ELF::AuxiliaryValue::PageSize, PAGE_SIZE }, - { ELF::AuxiliaryValue::BaseAddress, (void*)load_base }, - - { ELF::AuxiliaryValue::Entry, (void*)entry_eip }, - // NOTELF - { ELF::AuxiliaryValue::Uid, (long)uid.value() }, - { ELF::AuxiliaryValue::EUid, (long)euid.value() }, - { ELF::AuxiliaryValue::Gid, (long)gid.value() }, - { ELF::AuxiliaryValue::EGid, (long)egid.value() }, - - { ELF::AuxiliaryValue::Platform, Processor::platform_string() }, - // FIXME: This is platform specific -#if ARCH(X86_64) - { ELF::AuxiliaryValue::HwCap, (long)CPUID(1).edx() }, -#elif ARCH(AARCH64) - { ELF::AuxiliaryValue::HwCap, (long)0 }, -#elif ARCH(RISCV64) - { ELF::AuxiliaryValue::HwCap, (long)0 }, // TODO -#else -# error "Unknown architecture" -#endif - - { ELF::AuxiliaryValue::ClockTick, (long)TimeManagement::the().ticks_per_second() }, - - // FIXME: Also take into account things like extended filesystem permissions? That's what linux does... - { ELF::AuxiliaryValue::Secure, ((uid != euid) || (gid != egid)) ? 1 : 0 }, - - { ELF::AuxiliaryValue::Random, nullptr }, - - { ELF::AuxiliaryValue::ExecFilename, executable_path }, - - main_program_fd_allocation.has_value() ? ELF::AuxiliaryValue { ELF::AuxiliaryValue::ExecFileDescriptor, main_program_fd_allocation->fd } : ELF::AuxiliaryValue { ELF::AuxiliaryValue::Ignore, 0L }, - - { ELF::AuxiliaryValue::Null, 0L }, - } }; -} - -static ErrorOr>> find_shebang_interpreter_for_executable(char const first_page[], size_t nread) -{ - int word_start = 2; - size_t word_length = 0; - if (nread > 2 && first_page[0] == '#' && first_page[1] == '!') { - Vector> interpreter_words; - - for (size_t i = 2; i < nread; ++i) { - if (first_page[i] == '\n') { - break; - } - - if (first_page[i] != ' ') { - ++word_length; - } - - if (first_page[i] == ' ') { - if (word_length > 0) { - auto word = TRY(KString::try_create(StringView { &first_page[word_start], word_length })); - TRY(interpreter_words.try_append(move(word))); - } - word_length = 0; - word_start = i + 1; - } - } - - if (word_length > 0) { - auto word = TRY(KString::try_create(StringView { &first_page[word_start], word_length })); - TRY(interpreter_words.try_append(move(word))); - } - - if (!interpreter_words.is_empty()) - return interpreter_words; - } - - return ENOEXEC; -} - -ErrorOr> Process::find_elf_interpreter_for_executable(StringView path, Elf_Ehdr const& main_executable_header, size_t main_executable_header_size, size_t file_size, Optional& minimum_stack_size) -{ - // Not using ErrorOr here because we'll want to do the same thing in userspace in the RTLD - StringBuilder interpreter_path_builder; - Optional main_executable_requested_stack_size {}; - if (!TRY(ELF::validate_program_headers(main_executable_header, file_size, { &main_executable_header, main_executable_header_size }, &interpreter_path_builder, &main_executable_requested_stack_size))) { - dbgln("exec({}): File has invalid ELF Program headers", path); - return ENOEXEC; - } - auto interpreter_path = interpreter_path_builder.string_view(); - if (main_executable_requested_stack_size.has_value() && (!minimum_stack_size.has_value() || *minimum_stack_size < *main_executable_requested_stack_size)) - minimum_stack_size = main_executable_requested_stack_size; - - if (!interpreter_path.is_empty()) { - dbgln_if(EXEC_DEBUG, "exec({}): Using program interpreter {}", path, interpreter_path); - auto interpreter_description = TRY(VirtualFileSystem::the().open(credentials(), interpreter_path, O_EXEC, 0, current_directory())); - auto interp_metadata = interpreter_description->metadata(); - - VERIFY(interpreter_description->inode()); - - // Validate the program interpreter as a valid elf binary. - // If your program interpreter is a #! file or something, it's time to stop playing games :) - if (interp_metadata.size < (int)sizeof(Elf_Ehdr)) - return ENOEXEC; - - char first_page[PAGE_SIZE] = {}; - auto first_page_buffer = UserOrKernelBuffer::for_kernel_buffer((u8*)&first_page); - auto nread = TRY(interpreter_description->read(first_page_buffer, sizeof(first_page))); - - if (nread < sizeof(Elf_Ehdr)) - return ENOEXEC; - - auto* elf_header = (Elf_Ehdr*)first_page; - if (!ELF::validate_elf_header(*elf_header, interp_metadata.size)) { - dbgln("exec({}): Interpreter ({}) has invalid ELF header", path, interpreter_path); - return ENOEXEC; - } - - // Not using ErrorOr here because we'll want to do the same thing in userspace in the RTLD - StringBuilder interpreter_interpreter_path_builder; - Optional interpreter_requested_stack_size {}; - if (!TRY(ELF::validate_program_headers(*elf_header, interp_metadata.size, { first_page, nread }, &interpreter_interpreter_path_builder, &interpreter_requested_stack_size))) { - dbgln("exec({}): Interpreter ({}) has invalid ELF Program headers", path, interpreter_path); - return ENOEXEC; - } - auto interpreter_interpreter_path = interpreter_interpreter_path_builder.string_view(); - if (interpreter_requested_stack_size.has_value() && (!minimum_stack_size.has_value() || *minimum_stack_size < *interpreter_requested_stack_size)) - minimum_stack_size = interpreter_requested_stack_size; - - if (!interpreter_interpreter_path.is_empty()) { - dbgln("exec({}): Interpreter ({}) has its own interpreter ({})! No thank you!", path, interpreter_path, interpreter_interpreter_path); - return ELOOP; - } - - return interpreter_description; - } - - if (main_executable_header.e_type == ET_REL) { - // We can't exec an ET_REL, that's just an object file from the compiler - return ENOEXEC; - } - if (main_executable_header.e_type == ET_DYN) { - // If it's ET_DYN with no PT_INTERP, then it's a dynamic executable responsible - // for its own relocation (i.e. it's /usr/lib/Loader.so) - return nullptr; - } - - // No interpreter, but, path refers to a valid elf image - return nullptr; -} - -ErrorOr Process::exec(NonnullOwnPtr path, Vector> arguments, Vector> environment, Thread*& new_main_thread, InterruptsState& previous_interrupts_state, int recursion_depth) -{ - if (recursion_depth > 2) { - dbgln("exec({}): SHENANIGANS! recursed too far trying to find #! interpreter", path); - return ELOOP; - } - - // Open the file to check what kind of binary format it is - // Currently supported formats: - // - #! interpreted file - // - ELF32 - // * ET_EXEC binary that just gets loaded - // * ET_DYN binary that requires a program interpreter - // - auto description = TRY(VirtualFileSystem::the().open(credentials(), path->view(), O_EXEC, 0, current_directory())); - auto metadata = description->metadata(); - - if (!metadata.is_regular_file()) - return EACCES; - - // Always gonna need at least 3 bytes. these are for #!X - if (metadata.size < 3) - return ENOEXEC; - - VERIFY(description->inode()); - - // Read the first page of the program into memory so we can validate the binfmt of it - char first_page[PAGE_SIZE]; - auto first_page_buffer = UserOrKernelBuffer::for_kernel_buffer((u8*)&first_page); - auto nread = TRY(description->read(first_page_buffer, sizeof(first_page))); - - // 1) #! interpreted file - auto shebang_result = find_shebang_interpreter_for_executable(first_page, nread); - if (!shebang_result.is_error()) { - auto shebang_words = shebang_result.release_value(); - auto shebang_path = TRY(shebang_words.first()->try_clone()); - arguments[0] = move(path); - TRY(arguments.try_prepend(move(shebang_words))); - return exec(move(shebang_path), move(arguments), move(environment), new_main_thread, previous_interrupts_state, ++recursion_depth); - } - - // #2) ELF32 for i386 - - if (nread < sizeof(Elf_Ehdr)) - return ENOEXEC; - auto const* main_program_header = (Elf_Ehdr*)first_page; - - if (!ELF::validate_elf_header(*main_program_header, metadata.size)) { - dbgln("exec({}): File has invalid ELF header", path); - return ENOEXEC; - } - - Optional minimum_stack_size {}; - auto interpreter_description = TRY(find_elf_interpreter_for_executable(path->view(), *main_program_header, nread, metadata.size, minimum_stack_size)); - return do_exec(move(description), move(arguments), move(environment), move(interpreter_description), new_main_thread, previous_interrupts_state, *main_program_header, minimum_stack_size); -} - -ErrorOr Process::sys$execve(Userspace user_params) -{ - VERIFY_PROCESS_BIG_LOCK_ACQUIRED(this); - TRY(require_promise(Pledge::exec)); - - Thread* new_main_thread = nullptr; - InterruptsState previous_interrupts_state = InterruptsState::Enabled; - - // NOTE: Be extremely careful with allocating any kernel memory in this function. - // On success, the kernel stack will be lost. - // The explicit block scope below is specifically placed to minimize the number - // of stack locals in this function. - { - auto params = TRY(copy_typed_from_user(user_params)); - - if (params.arguments.length > ARG_MAX || params.environment.length > ARG_MAX) - return E2BIG; - - // NOTE: The caller is expected to always pass at least one argument by convention, - // the program path that was passed as params.path. - if (params.arguments.length == 0) - return EINVAL; - - auto path = TRY(get_syscall_path_argument(params.path)); - - auto copy_user_strings = [](auto const& list, auto& output) -> ErrorOr { - if (!list.length) - return {}; - Checked size = sizeof(*list.strings); - size *= list.length; - if (size.has_overflow()) - return EOVERFLOW; - Vector strings; - TRY(strings.try_resize(list.length)); - TRY(copy_from_user(strings.data(), list.strings, size.value())); - for (size_t i = 0; i < list.length; ++i) { - auto string = TRY(try_copy_kstring_from_user(strings[i])); - TRY(output.try_append(move(string))); - } - return {}; - }; - - Vector> arguments; - TRY(copy_user_strings(params.arguments, arguments)); - - Vector> environment; - TRY(copy_user_strings(params.environment, environment)); - - TRY(exec(move(path), move(arguments), move(environment), new_main_thread, previous_interrupts_state)); - } - - // NOTE: If we're here, the exec has succeeded and we've got a new executable image! - // We will not return normally from this function. Instead, the next time we - // get scheduled, it'll be at the entry point of the new executable. - - VERIFY_INTERRUPTS_DISABLED(); - VERIFY(Processor::in_critical()); - - auto* current_thread = Thread::current(); - if (current_thread == new_main_thread) { - // We need to enter the scheduler lock before changing the state - // and it will be released after the context switch into that - // thread. We should also still be in our critical section - VERIFY(!g_scheduler_lock.is_locked_by_current_processor()); - VERIFY(Processor::in_critical() == 1); - g_scheduler_lock.lock(); - current_thread->set_state(Thread::State::Running); - Processor::assume_context(*current_thread, previous_interrupts_state); - VERIFY_NOT_REACHED(); - } - - // NOTE: This code path is taken in the non-syscall case, i.e when the kernel spawns - // a userspace process directly (such as /bin/SystemServer on startup) - - Processor::restore_interrupts_state(previous_interrupts_state); - Processor::leave_critical(); - return 0; -} - -} diff --git a/Kernel/Syscalls/exit.cpp b/Kernel/Syscalls/exit.cpp deleted file mode 100644 index 9911d5bd5b6..00000000000 --- a/Kernel/Syscalls/exit.cpp +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include - -namespace Kernel { - -void Process::sys$exit(int status) -{ - // FIXME: We have callers from kernel which don't acquire the big process lock. - if (Thread::current()->previous_mode() == ExecutionMode::User) { - VERIFY_PROCESS_BIG_LOCK_ACQUIRED(this); - } - - with_mutable_protected_data([status](auto& protected_data) { - protected_data.termination_status = status; - protected_data.termination_signal = 0; - }); - - auto* current_thread = Thread::current(); - current_thread->set_profiling_suppressed(); - PerformanceManager::add_thread_exit_event(*current_thread); - - die(); - current_thread->die_if_needed(); - VERIFY_NOT_REACHED(); -} - -} diff --git a/Kernel/Syscalls/faccessat.cpp b/Kernel/Syscalls/faccessat.cpp deleted file mode 100644 index 023e22b80d5..00000000000 --- a/Kernel/Syscalls/faccessat.cpp +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include - -namespace Kernel { - -ErrorOr Process::sys$faccessat(Userspace user_params) -{ - VERIFY_NO_PROCESS_BIG_LOCK(this); - TRY(require_promise(Pledge::rpath)); - auto params = TRY(copy_typed_from_user(user_params)); - auto pathname = TRY(get_syscall_path_argument(params.pathname)); - - if ((params.flags & ~(AT_SYMLINK_NOFOLLOW | AT_EACCESS)) != 0) - return EINVAL; - - auto flags = AccessFlags::None; - if (params.flags & AT_SYMLINK_NOFOLLOW) - flags |= AccessFlags::DoNotFollowSymlinks; - if (params.flags & AT_EACCESS) - flags |= AccessFlags::EffectiveAccess; - - CustodyBase base(params.dirfd, pathname->view()); - TRY(VirtualFileSystem::the().access(credentials(), pathname->view(), params.mode, base, flags)); - return 0; -} - -} diff --git a/Kernel/Syscalls/fallocate.cpp b/Kernel/Syscalls/fallocate.cpp deleted file mode 100644 index 1251b8b2b66..00000000000 --- a/Kernel/Syscalls/fallocate.cpp +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright (c) 2022, Leon Albrecht - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include - -namespace Kernel { - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/posix_fallocate.html -ErrorOr Process::sys$posix_fallocate(int fd, off_t offset, off_t length) -{ - VERIFY_NO_PROCESS_BIG_LOCK(this); - TRY(require_promise(Pledge::stdio)); - - // [EINVAL] The len argument is less than zero, or the offset argument is less than zero, or the underlying file system does not support this operation. - if (offset < 0) - return EINVAL; - if (length <= 0) - return EINVAL; - - Checked checked_size { length }; - checked_size += offset; - // FIXME: Return EFBIG if offset+length > FileSizeMax - if (checked_size.has_overflow()) - return EFBIG; - - auto description = TRY(open_file_description(fd)); - - // [EBADF] The fd argument references a file that was opened without write permission. - if (!description->is_writable()) - return EBADF; - - // [ESPIPE] The fd argument is associated with a pipe or FIFO. - if (description->is_fifo()) - return ESPIPE; - - // [ENODEV] The fd argument does not refer to a regular file. - if (!description->file().is_regular_file()) - return ENODEV; - - VERIFY(description->file().is_inode()); - - auto& file = static_cast(description->file()); - if (file.inode().size() >= checked_size.value()) - return 0; - - // Note: truncate essentially calls resize in the inodes implementation - // while resize is not a standard member of an inode, so we just call - // truncate instead - TRY(file.inode().truncate(checked_size.value())); - - // FIXME: EINTR: A signal was caught during execution. - return 0; -} - -} diff --git a/Kernel/Syscalls/fcntl.cpp b/Kernel/Syscalls/fcntl.cpp deleted file mode 100644 index 528ba244893..00000000000 --- a/Kernel/Syscalls/fcntl.cpp +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include - -namespace Kernel { - -ErrorOr Process::sys$fcntl(int fd, int cmd, uintptr_t arg) -{ - VERIFY_NO_PROCESS_BIG_LOCK(this); - TRY(require_promise(Pledge::stdio)); - dbgln_if(IO_DEBUG, "sys$fcntl: fd={}, cmd={}, arg={}", fd, cmd, arg); - auto description = TRY(open_file_description(fd)); - // NOTE: The FD flags are not shared between OpenFileDescription objects. - // This means that dup() doesn't copy the FD_CLOEXEC flag! - switch (cmd) { - case F_DUPFD_CLOEXEC: - case F_DUPFD: { - int arg_fd = (int)arg; - if (arg_fd < 0) - return EINVAL; - return m_fds.with_exclusive([&](auto& fds) -> ErrorOr { - auto fd_allocation = TRY(fds.allocate(arg_fd)); - fds[fd_allocation.fd].set(*description, (cmd == F_DUPFD_CLOEXEC) ? FD_CLOEXEC : 0); - return fd_allocation.fd; - }); - } - case F_GETFD: - return m_fds.with_exclusive([fd](auto& fds) { return fds[fd].flags(); }); - case F_SETFD: - m_fds.with_exclusive([fd, arg](auto& fds) { fds[fd].set_flags(arg); }); - break; - case F_GETFL: - return description->file_flags(); - case F_SETFL: - description->set_file_flags(arg); - break; - case F_ISTTY: - return description->is_tty(); - case F_GETLK: - TRY(description->get_flock(Userspace(arg))); - return 0; - case F_SETLK: - TRY(description->apply_flock(Process::current(), Userspace(arg), ShouldBlock::No)); - return 0; - case F_SETLKW: - TRY(description->apply_flock(Process::current(), Userspace(arg), ShouldBlock::Yes)); - return 0; - default: - return EINVAL; - } - return 0; -} - -} diff --git a/Kernel/Syscalls/fork.cpp b/Kernel/Syscalls/fork.cpp deleted file mode 100644 index e7e605cbdfe..00000000000 --- a/Kernel/Syscalls/fork.cpp +++ /dev/null @@ -1,195 +0,0 @@ -/* - * Copyright (c) 2018-2023, Andreas Kling - * Copyright (c) 2023, Idan Horowitz - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include -#include -#include - -namespace Kernel { - -ErrorOr Process::sys$fork(RegisterState& regs) -{ - VERIFY_NO_PROCESS_BIG_LOCK(this); - TRY(require_promise(Pledge::proc)); - - auto credentials = this->credentials(); - auto child_and_first_thread = TRY(Process::create_with_forked_name(credentials->uid(), credentials->gid(), pid(), m_is_kernel_process, current_directory(), executable(), tty(), this)); - auto& child = child_and_first_thread.process; - auto& child_first_thread = child_and_first_thread.first_thread; - - ArmedScopeGuard thread_finalizer_guard = [&child_first_thread]() { - SpinlockLocker lock(g_scheduler_lock); - child_first_thread->detach(); - child_first_thread->set_state(Thread::State::Dying); - }; - - // NOTE: All user processes have a leaked ref on them. It's balanced by Thread::WaitBlockerSet::finalize(). - child->ref(); - - TRY(m_unveil_data.with([&](auto& parent_unveil_data) -> ErrorOr { - return child->m_unveil_data.with([&](auto& child_unveil_data) -> ErrorOr { - child_unveil_data.state = parent_unveil_data.state; - child_unveil_data.paths = TRY(parent_unveil_data.paths.deep_copy()); - return {}; - }); - })); - - TRY(m_exec_unveil_data.with([&](auto& parent_exec_unveil_data) -> ErrorOr { - return child->m_exec_unveil_data.with([&](auto& child_exec_unveil_data) -> ErrorOr { - child_exec_unveil_data.state = parent_exec_unveil_data.state; - child_exec_unveil_data.paths = TRY(parent_exec_unveil_data.paths.deep_copy()); - return {}; - }); - })); - - // Note: We take the spinlock of Process::all_instances list because we need - // to ensure that when we take the jail spinlock of two processes that we don't - // run into a deadlock situation because both processes compete over each other Jail's - // spinlock. Such pattern of taking 3 spinlocks in the same order happens in - // Process::for_each* methods. - TRY(Process::all_instances().with([&](auto const&) -> ErrorOr { - TRY(m_attached_jail.with([&](auto& parent_jail) -> ErrorOr { - return child->m_attached_jail.with([&](auto& child_jail) -> ErrorOr { - child_jail = parent_jail; - if (child_jail) { - child_jail->attach_count().with([&](auto& attach_count) { - attach_count++; - }); - } - return {}; - }); - })); - return {}; - })); - - ArmedScopeGuard remove_from_jail_process_list = [&]() { - m_jail_process_list.with([&](auto& list_ptr) { - if (list_ptr) { - list_ptr->attached_processes().with([&](auto& list) { - list.remove(*child); - }); - } - }); - }; - m_jail_process_list.with([&](auto& list_ptr) { - if (list_ptr) { - child->m_jail_process_list.with([&](auto& child_list_ptr) { - child_list_ptr = list_ptr; - }); - list_ptr->attached_processes().with([&](auto& list) { - list.append(child); - }); - } - }); - - TRY(child->m_fds.with_exclusive([&](auto& child_fds) { - return m_fds.with_exclusive([&](auto& parent_fds) { - return child_fds.try_clone(parent_fds); - }); - })); - - with_protected_data([&](auto& my_protected_data) { - child->with_mutable_protected_data([&](auto& child_protected_data) { - child_protected_data.promises = my_protected_data.promises; - child_protected_data.execpromises = my_protected_data.execpromises; - child_protected_data.has_promises = my_protected_data.has_promises; - child_protected_data.has_execpromises = my_protected_data.has_execpromises; - child_protected_data.credentials = my_protected_data.credentials; - child_protected_data.umask = my_protected_data.umask; - child_protected_data.signal_trampoline = my_protected_data.signal_trampoline; - child_protected_data.dumpable = my_protected_data.dumpable; - child_protected_data.process_group = my_protected_data.process_group; - }); - }); - - dbgln_if(FORK_DEBUG, "fork: child={}", child); - - // A child created via fork(2) inherits a copy of its parent's signal mask - child_first_thread->update_signal_mask(Thread::current()->signal_mask()); - - // A child process created via fork(2) inherits a copy of its parent's alternate signal stack settings. - child_first_thread->m_alternative_signal_stack = Thread::current()->m_alternative_signal_stack; - - auto& child_regs = child_first_thread->m_regs; -#if ARCH(X86_64) - child_regs.rax = 0; // fork() returns 0 in the child :^) - child_regs.rbx = regs.rbx; - child_regs.rcx = regs.rcx; - child_regs.rdx = regs.rdx; - child_regs.rbp = regs.rbp; - child_regs.rsp = regs.userspace_rsp; - child_regs.rsi = regs.rsi; - child_regs.rdi = regs.rdi; - child_regs.r8 = regs.r8; - child_regs.r9 = regs.r9; - child_regs.r10 = regs.r10; - child_regs.r11 = regs.r11; - child_regs.r12 = regs.r12; - child_regs.r13 = regs.r13; - child_regs.r14 = regs.r14; - child_regs.r15 = regs.r15; - child_regs.rflags = regs.rflags; - child_regs.rip = regs.rip; - child_regs.cs = regs.cs; - - dbgln_if(FORK_DEBUG, "fork: child will begin executing at {:#04x}:{:p} with stack {:p}, kstack {:p}", - child_regs.cs, child_regs.rip, child_regs.rsp, child_regs.rsp0); -#elif ARCH(AARCH64) - child_regs.x[0] = 0; // fork() returns 0 in the child :^) - for (size_t i = 1; i < array_size(child_regs.x); ++i) - child_regs.x[i] = regs.x[i]; - child_regs.spsr_el1 = regs.spsr_el1; - child_regs.elr_el1 = regs.elr_el1; - child_regs.sp_el0 = regs.sp_el0; - child_regs.tpidr_el0 = regs.tpidr_el0; -#elif ARCH(RISCV64) - for (size_t i = 0; i < array_size(child_regs.x); ++i) - child_regs.x[i] = regs.x[i]; - child_regs.x[9] = 0; // fork() returns 0 in the child :^) - child_regs.sstatus = regs.sstatus; - child_regs.pc = regs.sepc; - dbgln_if(FORK_DEBUG, "fork: child will begin executing at {:p} with stack {:p}, kstack {:p}", - child_regs.pc, child_regs.sp(), child_regs.kernel_sp); -#else -# error Unknown architecture -#endif - - TRY(address_space().with([&](auto& parent_space) { - return child->address_space().with([&](auto& child_space) -> ErrorOr { - if (parent_space->enforces_syscall_regions()) - child_space->set_enforces_syscall_regions(); - for (auto& region : parent_space->region_tree().regions()) { - dbgln_if(FORK_DEBUG, "fork: cloning Region '{}' @ {}", region.name(), region.vaddr()); - auto region_clone = TRY(region.try_clone()); - TRY(region_clone->map(child_space->page_directory(), Memory::ShouldFlushTLB::No)); - TRY(child_space->region_tree().place_specifically(*region_clone, region.range())); - (void)region_clone.leak_ptr(); - } - return {}; - }); - })); - - thread_finalizer_guard.disarm(); - remove_from_jail_process_list.disarm(); - - Process::register_new(*child); - - PerformanceManager::add_process_created_event(*child); - - SpinlockLocker lock(g_scheduler_lock); - child_first_thread->set_affinity(Thread::current()->affinity()); - child_first_thread->set_state(Thread::State::Runnable); - - auto child_pid = child->pid().value(); - - return child_pid; -} -} diff --git a/Kernel/Syscalls/fsync.cpp b/Kernel/Syscalls/fsync.cpp deleted file mode 100644 index 4b7cd257361..00000000000 --- a/Kernel/Syscalls/fsync.cpp +++ /dev/null @@ -1,20 +0,0 @@ -/* - * Copyright (c) 2021, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include - -namespace Kernel { - -ErrorOr Process::sys$fsync(int fd) -{ - VERIFY_NO_PROCESS_BIG_LOCK(this); - TRY(require_promise(Pledge::stdio)); - auto description = TRY(open_file_description(fd)); - TRY(description->sync()); - return 0; -} - -} diff --git a/Kernel/Syscalls/ftruncate.cpp b/Kernel/Syscalls/ftruncate.cpp deleted file mode 100644 index a1791f545ca..00000000000 --- a/Kernel/Syscalls/ftruncate.cpp +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include - -namespace Kernel { - -ErrorOr Process::sys$ftruncate(int fd, off_t length) -{ - VERIFY_NO_PROCESS_BIG_LOCK(this); - TRY(require_promise(Pledge::stdio)); - if (length < 0) - return EINVAL; - auto description = TRY(open_file_description(fd)); - if (!description->is_writable()) - return EBADF; - TRY(description->truncate(static_cast(length))); - return 0; -} - -} diff --git a/Kernel/Syscalls/futex.cpp b/Kernel/Syscalls/futex.cpp deleted file mode 100644 index c6ee2c88c49..00000000000 --- a/Kernel/Syscalls/futex.cpp +++ /dev/null @@ -1,331 +0,0 @@ -/* - * Copyright (c) 2018-2021, Andreas Kling - * Copyright (c) 2022, Idan Horowitz - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include - -namespace Kernel { - -static Singleton>, LockRank::None>> s_global_futex_queues; - -void Process::clear_futex_queues_on_exec() -{ - s_global_futex_queues->with([this](auto& queues) { - auto const* address_space = this->address_space().with([](auto& space) { return space.ptr(); }); - queues.remove_all_matching([address_space](auto& futex_key, auto& futex_queue) { - if ((futex_key.raw.offset & futex_key_private_flag) == 0) - return false; - if (futex_key.private_.address_space != address_space) - return false; - bool did_wake_all; - futex_queue->wake_all(did_wake_all); - VERIFY(did_wake_all); // No one should be left behind... - return true; - }); - }); -} - -ErrorOr Process::get_futex_key(FlatPtr user_address, bool shared) -{ - if (user_address & 0b11) // user_address points to a u32, so must be 4byte aligned - return EINVAL; - - auto range = Memory::VirtualRange { VirtualAddress(user_address), sizeof(u32) }; - - if (!Kernel::Memory::is_user_range(range)) - return EFAULT; - - if (!shared) { // If this is thread-shared, we can skip searching the matching region - return GlobalFutexKey { - .private_ = { - .address_space = this->address_space().with([](auto& space) { return space.ptr(); }), - .user_address = user_address | futex_key_private_flag, - } - }; - } - - return address_space().with([&](auto& space) -> ErrorOr { - auto* matching_region = space->find_region_containing(range); - if (!matching_region) - return EFAULT; - - // The user wants to share this futex, but if the address doesn't point to a shared resource, there's not - // much sharing to be done, so let's mark this as private - if (!matching_region->is_shared()) { - return GlobalFutexKey { - .private_ = { - .address_space = space.ptr(), - .user_address = user_address | futex_key_private_flag, - } - }; - } - - // This address is backed by a shared VMObject, if it's an AnonymousVMObject, it can be shared between processes - // via forking, and shared regions that are cloned during a fork retain their original AnonymousVMObject. - // On the other hand, if it's a SharedInodeVMObject, it can be shared by two processes mapping the same file as - // MAP_SHARED, but since they are deduplicated based on the inode, in all cases the VMObject pointer should be - // a unique global identifier. - // NOTE: This assumes that a program will not unmap the only region keeping the vmobject alive while waiting on it, - // if it does, it will get stuck waiting forever until interrupted by a signal, but since that use case is defined as - // a programmer error, we are fine with it. - - auto const& vmobject = matching_region->vmobject(); - if (vmobject.is_inode()) - VERIFY(vmobject.is_shared_inode()); - - return GlobalFutexKey { - .shared = { - .vmobject = &vmobject, - .offset = matching_region->offset_in_vmobject_from_vaddr(range.base()) } - }; - }); -} - -ErrorOr Process::sys$futex(Userspace user_params) -{ - VERIFY_PROCESS_BIG_LOCK_ACQUIRED(this); - auto params = TRY(copy_typed_from_user(user_params)); - - Thread::BlockTimeout timeout; - u32 cmd = params.futex_op & FUTEX_CMD_MASK; - - bool use_realtime_clock = (params.futex_op & FUTEX_CLOCK_REALTIME) != 0; - if (use_realtime_clock && cmd != FUTEX_WAIT && cmd != FUTEX_WAIT_BITSET) { - return ENOSYS; - } - - bool shared = (params.futex_op & FUTEX_PRIVATE_FLAG) == 0; - - switch (cmd) { - case FUTEX_WAIT: - case FUTEX_WAIT_BITSET: - case FUTEX_REQUEUE: - case FUTEX_CMP_REQUEUE: { - if (params.timeout) { - auto timeout_time = TRY(copy_time_from_user(params.timeout)); - bool is_absolute = cmd != FUTEX_WAIT; - clockid_t clock_id = use_realtime_clock ? CLOCK_REALTIME_COARSE : CLOCK_MONOTONIC_COARSE; - timeout = Thread::BlockTimeout(is_absolute, &timeout_time, nullptr, clock_id); - } - if (cmd == FUTEX_WAIT_BITSET && params.val3 == FUTEX_BITSET_MATCH_ANY) - cmd = FUTEX_WAIT; - break; - case FUTEX_WAKE_BITSET: - if (params.val3 == FUTEX_BITSET_MATCH_ANY) - cmd = FUTEX_WAKE; - break; - } - } - - auto find_futex_queue = [&](GlobalFutexKey futex_key, bool create_if_not_found, bool* did_create = nullptr) -> ErrorOr> { - VERIFY(!create_if_not_found || did_create != nullptr); - return s_global_futex_queues->with([&](auto& queues) -> ErrorOr> { - auto it = queues.find(futex_key); - if (it != queues.end()) - return it->value; - if (!create_if_not_found) - return nullptr; - *did_create = true; - auto futex_queue = TRY(adopt_nonnull_lock_ref_or_enomem(new (nothrow) FutexQueue)); - auto result = TRY(queues.try_set(futex_key, futex_queue)); - VERIFY(result == AK::HashSetResult::InsertedNewEntry); - return futex_queue; - }); - }; - - auto remove_futex_queue = [&](GlobalFutexKey futex_key) { - return s_global_futex_queues->with([&](auto& queues) { - auto it = queues.find(futex_key); - if (it == queues.end()) - return; - if (it->value->try_remove()) - queues.remove(it); - }); - }; - - auto do_wake = [&](FlatPtr user_address, u32 count, Optional const& bitmask) -> ErrorOr { - if (count == 0) - return 0; - auto futex_key = TRY(get_futex_key(user_address, shared)); - auto futex_queue = TRY(find_futex_queue(futex_key, false)); - if (!futex_queue) - return 0; - bool is_empty; - u32 woke_count = futex_queue->wake_n(count, bitmask, is_empty); - if (is_empty) { - // If there are no more waiters, we want to get rid of the futex! - remove_futex_queue(futex_key); - } - return (int)woke_count; - }; - - auto user_address = FlatPtr(params.userspace_address); - auto user_address2 = FlatPtr(params.userspace_address2); - - auto do_wait = [&](u32 bitset) -> ErrorOr { - bool did_create; - LockRefPtr futex_queue; - auto futex_key = TRY(get_futex_key(user_address, shared)); - do { - auto user_value = user_atomic_load_relaxed(params.userspace_address); - if (!user_value.has_value()) - return EFAULT; - if (user_value.value() != params.val) { - dbgln_if(FUTEX_DEBUG, "futex wait: EAGAIN. user value: {:p} @ {:p} != val: {}", user_value.value(), params.userspace_address, params.val); - return EAGAIN; - } - atomic_thread_fence(AK::MemoryOrder::memory_order_acquire); - - did_create = false; - futex_queue = TRY(find_futex_queue(futex_key, true, &did_create)); - VERIFY(futex_queue); - // We need to try again if we didn't create this queue and the existing queue - // was removed before we were able to queue an imminent wait. - } while (!did_create && !futex_queue->queue_imminent_wait()); - - // We must not hold the lock before blocking. But we have a reference - // to the FutexQueue so that we can keep it alive. - - Thread::BlockResult block_result = futex_queue->wait_on(timeout, bitset); - - if (futex_queue->is_empty_and_no_imminent_waits()) { - // If there are no more waiters, we want to get rid of the futex! - remove_futex_queue(futex_key); - } - if (block_result == Thread::BlockResult::InterruptedByTimeout) { - return ETIMEDOUT; - } - return 0; - }; - - auto do_requeue = [&](Optional val3) -> ErrorOr { - auto user_value = user_atomic_load_relaxed(params.userspace_address); - if (!user_value.has_value()) - return EFAULT; - if (val3.has_value() && val3.value() != user_value.value()) - return EAGAIN; - atomic_thread_fence(AK::MemoryOrder::memory_order_acquire); - - auto futex_key = TRY(get_futex_key(user_address, shared)); - auto futex_queue = TRY(find_futex_queue(futex_key, false)); - if (!futex_queue) - return 0; - - LockRefPtr target_futex_queue; - bool is_empty = false; - bool is_target_empty = false; - auto futex_key2 = TRY(get_futex_key(user_address2, shared)); - auto woken_or_requeued = TRY(futex_queue->wake_n_requeue( - params.val, [&]() -> ErrorOr { - // NOTE: futex_queue's lock is being held while this callback is called - // The reason we're doing this in a callback is that we don't want to always - // create a target queue, only if we actually have anything to move to it! - target_futex_queue = TRY(find_futex_queue(futex_key2, true)); - return target_futex_queue.ptr(); - }, - params.val2, is_empty, is_target_empty)); - if (is_empty) - remove_futex_queue(futex_key); - if (is_target_empty && target_futex_queue) - remove_futex_queue(futex_key2); - return woken_or_requeued; - }; - - switch (cmd) { - case FUTEX_WAIT: - return do_wait(0); - - case FUTEX_WAKE: - return TRY(do_wake(user_address, params.val, {})); - - case FUTEX_WAKE_OP: { - Optional oldval; - u32 op_arg = _FUTEX_OP_ARG(params.val3); - auto op = _FUTEX_OP(params.val3); - if (op & FUTEX_OP_ARG_SHIFT) { - op_arg = 1 << op_arg; - op &= FUTEX_OP_ARG_SHIFT; - } - atomic_thread_fence(AK::MemoryOrder::memory_order_release); - switch (op) { - case FUTEX_OP_SET: - oldval = user_atomic_exchange_relaxed(params.userspace_address2, op_arg); - break; - case FUTEX_OP_ADD: - oldval = user_atomic_fetch_add_relaxed(params.userspace_address2, op_arg); - break; - case FUTEX_OP_OR: - oldval = user_atomic_fetch_or_relaxed(params.userspace_address2, op_arg); - break; - case FUTEX_OP_ANDN: - oldval = user_atomic_fetch_and_not_relaxed(params.userspace_address2, op_arg); - break; - case FUTEX_OP_XOR: - oldval = user_atomic_fetch_xor_relaxed(params.userspace_address2, op_arg); - break; - default: - return EINVAL; - } - if (!oldval.has_value()) - return EFAULT; - atomic_thread_fence(AK::MemoryOrder::memory_order_acquire); - auto result = TRY(do_wake(user_address, params.val, {})); - if (params.val2 > 0) { - bool compare_result; - switch (_FUTEX_CMP(params.val3)) { - case FUTEX_OP_CMP_EQ: - compare_result = (oldval.value() == _FUTEX_CMP_ARG(params.val3)); - break; - case FUTEX_OP_CMP_NE: - compare_result = (oldval.value() != _FUTEX_CMP_ARG(params.val3)); - break; - case FUTEX_OP_CMP_LT: - compare_result = (oldval.value() < _FUTEX_CMP_ARG(params.val3)); - break; - case FUTEX_OP_CMP_LE: - compare_result = (oldval.value() <= _FUTEX_CMP_ARG(params.val3)); - break; - case FUTEX_OP_CMP_GT: - compare_result = (oldval.value() > _FUTEX_CMP_ARG(params.val3)); - break; - case FUTEX_OP_CMP_GE: - compare_result = (oldval.value() >= _FUTEX_CMP_ARG(params.val3)); - break; - default: - return EINVAL; - } - if (compare_result) - result += TRY(do_wake(user_address2, params.val2, {})); - } - return result; - } - - case FUTEX_REQUEUE: - return do_requeue({}); - - case FUTEX_CMP_REQUEUE: - return do_requeue(params.val3); - - case FUTEX_WAIT_BITSET: - VERIFY(params.val3 != FUTEX_BITSET_MATCH_ANY); // we should have turned it into FUTEX_WAIT - if (params.val3 == 0) - return EINVAL; - return do_wait(params.val3); - - case FUTEX_WAKE_BITSET: - VERIFY(params.val3 != FUTEX_BITSET_MATCH_ANY); // we should have turned it into FUTEX_WAKE - if (params.val3 == 0) - return EINVAL; - return TRY(do_wake(user_address, params.val, params.val3)); - } - return ENOSYS; -} - -} diff --git a/Kernel/Syscalls/get_dir_entries.cpp b/Kernel/Syscalls/get_dir_entries.cpp deleted file mode 100644 index 680061268e6..00000000000 --- a/Kernel/Syscalls/get_dir_entries.cpp +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include - -namespace Kernel { - -ErrorOr Process::sys$get_dir_entries(int fd, Userspace user_buffer, size_t user_size) -{ - VERIFY_NO_PROCESS_BIG_LOCK(this); - TRY(require_promise(Pledge::stdio)); - if (user_size > NumericLimits::max()) - return EINVAL; - auto description = TRY(open_file_description(fd)); - auto buffer = TRY(UserOrKernelBuffer::for_user_buffer(user_buffer, static_cast(user_size))); - auto count = TRY(description->get_dir_entries(buffer, user_size)); - return count; -} - -} diff --git a/Kernel/Syscalls/get_stack_bounds.cpp b/Kernel/Syscalls/get_stack_bounds.cpp deleted file mode 100644 index c8dcb370e3b..00000000000 --- a/Kernel/Syscalls/get_stack_bounds.cpp +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright (c) 2018-2021, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include - -namespace Kernel { - -ErrorOr Process::sys$get_stack_bounds(Userspace user_stack_base, Userspace user_stack_size) -{ - VERIFY_NO_PROCESS_BIG_LOCK(this); - auto& regs = Thread::current()->get_register_dump_from_stack(); - FlatPtr stack_pointer = regs.userspace_sp(); - return address_space().with([&](auto& space) -> ErrorOr { - auto* stack_region = space->find_region_containing(Memory::VirtualRange { VirtualAddress(stack_pointer), 1 }); - - // The syscall handler should have killed us if we had an invalid stack pointer. - VERIFY(stack_region); - - FlatPtr stack_base = stack_region->range().base().get(); - size_t stack_size = stack_region->size(); - TRY(copy_to_user(user_stack_base, &stack_base)); - TRY(copy_to_user(user_stack_size, &stack_size)); - return 0; - }); -} - -} diff --git a/Kernel/Syscalls/getrandom.cpp b/Kernel/Syscalls/getrandom.cpp deleted file mode 100644 index e249791d8dd..00000000000 --- a/Kernel/Syscalls/getrandom.cpp +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include - -namespace Kernel { - -// We don't use the flag yet, but we could use it for distinguishing -// random source like Linux, unlike the OpenBSD equivalent. However, if we -// do, we should be able of the caveats that Linux has dealt with. -ErrorOr Process::sys$getrandom(Userspace buffer, size_t buffer_size, [[maybe_unused]] unsigned flags) -{ - VERIFY_NO_PROCESS_BIG_LOCK(this); - TRY(require_promise(Pledge::stdio)); - if (buffer_size > NumericLimits::max()) - return EINVAL; - - auto data_buffer = TRY(UserOrKernelBuffer::for_user_buffer(buffer, buffer_size)); - - return TRY(data_buffer.write_buffered<1024>(buffer_size, [&](Bytes bytes) { - get_good_random_bytes(bytes); - return bytes.size(); - })); -} - -} diff --git a/Kernel/Syscalls/getuid.cpp b/Kernel/Syscalls/getuid.cpp deleted file mode 100644 index 3abdc1d76ed..00000000000 --- a/Kernel/Syscalls/getuid.cpp +++ /dev/null @@ -1,90 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include - -namespace Kernel { - -ErrorOr Process::sys$getuid() -{ - VERIFY_NO_PROCESS_BIG_LOCK(this); - TRY(require_promise(Pledge::stdio)); - auto credentials = this->credentials(); - return credentials->uid().value(); -} - -ErrorOr Process::sys$getgid() -{ - VERIFY_NO_PROCESS_BIG_LOCK(this); - TRY(require_promise(Pledge::stdio)); - auto credentials = this->credentials(); - return credentials->gid().value(); -} - -ErrorOr Process::sys$geteuid() -{ - VERIFY_NO_PROCESS_BIG_LOCK(this); - TRY(require_promise(Pledge::stdio)); - auto credentials = this->credentials(); - return credentials->euid().value(); -} - -ErrorOr Process::sys$getegid() -{ - VERIFY_NO_PROCESS_BIG_LOCK(this); - TRY(require_promise(Pledge::stdio)); - auto credentials = this->credentials(); - return credentials->egid().value(); -} - -ErrorOr Process::sys$getresuid(Userspace user_ruid, Userspace user_euid, Userspace user_suid) -{ - VERIFY_NO_PROCESS_BIG_LOCK(this); - TRY(require_promise(Pledge::stdio)); - - auto credentials = this->credentials(); - auto uid = credentials->uid(); - auto euid = credentials->euid(); - auto suid = credentials->suid(); - - TRY(copy_to_user(user_ruid, &uid)); - TRY(copy_to_user(user_euid, &euid)); - TRY(copy_to_user(user_suid, &suid)); - return 0; -} - -ErrorOr Process::sys$getresgid(Userspace user_rgid, Userspace user_egid, Userspace user_sgid) -{ - VERIFY_NO_PROCESS_BIG_LOCK(this); - TRY(require_promise(Pledge::stdio)); - - auto credentials = this->credentials(); - auto gid = credentials->gid(); - auto egid = credentials->egid(); - auto sgid = credentials->sgid(); - - TRY(copy_to_user(user_rgid, &gid)); - TRY(copy_to_user(user_egid, &egid)); - TRY(copy_to_user(user_sgid, &sgid)); - return 0; -} - -ErrorOr Process::sys$getgroups(size_t count, Userspace user_gids) -{ - VERIFY_NO_PROCESS_BIG_LOCK(this); - TRY(require_promise(Pledge::stdio)); - - auto credentials = this->credentials(); - - if (!count) - return credentials->extra_gids().size(); - if (count != credentials->extra_gids().size()) - return EINVAL; - TRY(copy_n_to_user(user_gids, credentials->extra_gids().data(), count)); - return 0; -} - -} diff --git a/Kernel/Syscalls/hostname.cpp b/Kernel/Syscalls/hostname.cpp deleted file mode 100644 index c1603d45007..00000000000 --- a/Kernel/Syscalls/hostname.cpp +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include - -namespace Kernel { - -ErrorOr Process::sys$gethostname(Userspace buffer, size_t size) -{ - VERIFY_NO_PROCESS_BIG_LOCK(this); - TRY(require_promise(Pledge::stdio)); - if (size > NumericLimits::max()) - return EINVAL; - return hostname().with_shared([&](auto const& name) -> ErrorOr { - // NOTE: To be able to copy a null-terminated string, we need at most - // 65 characters to store and copy and not 64 here, to store the whole - // hostname string + null terminator. - FixedStringBuffer current_hostname {}; - current_hostname.store_characters(name.representable_view()); - auto name_view = current_hostname.representable_view(); - if (size < (name_view.length() + 1)) - return ENAMETOOLONG; - TRY(copy_to_user(buffer, name_view.characters_without_null_termination(), name_view.length() + 1)); - return 0; - }); -} - -ErrorOr Process::sys$sethostname(Userspace buffer, size_t length) -{ - VERIFY_NO_PROCESS_BIG_LOCK(this); - TRY(require_no_promises()); - - auto credentials = this->credentials(); - if (!credentials->is_superuser()) - return EPERM; - auto new_hostname = TRY(get_syscall_name_string_fixed_buffer(buffer, length)); - hostname().with_exclusive([&](auto& name) { - name.store_characters(new_hostname.representable_view()); - }); - return 0; -} - -} diff --git a/Kernel/Syscalls/inode_watcher.cpp b/Kernel/Syscalls/inode_watcher.cpp deleted file mode 100644 index 926868b2e21..00000000000 --- a/Kernel/Syscalls/inode_watcher.cpp +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * Copyright (c) 2021, sin-ack - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include -#include - -namespace Kernel { - -ErrorOr Process::sys$create_inode_watcher(u32 flags) -{ - VERIFY_NO_PROCESS_BIG_LOCK(this); - TRY(require_promise(Pledge::rpath)); - - auto watcher = TRY(InodeWatcher::try_create()); - auto description = TRY(OpenFileDescription::try_create(move(watcher))); - - description->set_readable(true); - if (flags & static_cast(InodeWatcherFlags::Nonblock)) - description->set_blocking(false); - - return m_fds.with_exclusive([&](auto& fds) -> ErrorOr { - auto fd_allocation = TRY(fds.allocate()); - fds[fd_allocation.fd].set(move(description)); - - if (flags & static_cast(InodeWatcherFlags::CloseOnExec)) - fds[fd_allocation.fd].set_flags(fds[fd_allocation.fd].flags() | FD_CLOEXEC); - - return fd_allocation.fd; - }); -} - -ErrorOr Process::sys$inode_watcher_add_watch(Userspace user_params) -{ - VERIFY_NO_PROCESS_BIG_LOCK(this); - TRY(require_promise(Pledge::rpath)); - auto params = TRY(copy_typed_from_user(user_params)); - - auto description = TRY(open_file_description(params.fd)); - if (!description->is_inode_watcher()) - return EBADF; - auto* inode_watcher = description->inode_watcher(); - auto path = TRY(get_syscall_path_argument(params.user_path)); - auto custody = TRY(VirtualFileSystem::the().resolve_path(credentials(), path->view(), current_directory())); - if (!custody->inode().fs().supports_watchers()) - return ENOTSUP; - - return TRY(inode_watcher->register_inode(custody->inode(), params.event_mask)); -} - -ErrorOr Process::sys$inode_watcher_remove_watch(int fd, int wd) -{ - VERIFY_NO_PROCESS_BIG_LOCK(this); - auto description = TRY(open_file_description(fd)); - if (!description->is_inode_watcher()) - return EBADF; - TRY(description->inode_watcher()->unregister_by_wd(wd)); - return 0; -} - -} diff --git a/Kernel/Syscalls/ioctl.cpp b/Kernel/Syscalls/ioctl.cpp deleted file mode 100644 index b793cff3648..00000000000 --- a/Kernel/Syscalls/ioctl.cpp +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include - -namespace Kernel { - -ErrorOr Process::sys$ioctl(int fd, unsigned request, FlatPtr arg) -{ - VERIFY_NO_PROCESS_BIG_LOCK(this); - auto description = TRY(open_file_description(fd)); - if (request == FIONBIO) { - description->set_blocking(TRY(copy_typed_from_user(Userspace(arg))) == 0); - return 0; - } - if (request == FIOCLEX) { - m_fds.with_exclusive([&](auto& fds) { - fds[fd].set_flags(fds[fd].flags() | FD_CLOEXEC); - }); - return 0; - } - if (request == FIONCLEX) { - m_fds.with_exclusive([&](auto& fds) { - fds[fd].set_flags(fds[fd].flags() & ~FD_CLOEXEC); - }); - return 0; - } - TRY(description->file().ioctl(*description, request, arg)); - return 0; -} - -} diff --git a/Kernel/Syscalls/jail.cpp b/Kernel/Syscalls/jail.cpp deleted file mode 100644 index 7ed1c167ecd..00000000000 --- a/Kernel/Syscalls/jail.cpp +++ /dev/null @@ -1,85 +0,0 @@ -/* - * Copyright (c) 2022, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include - -namespace Kernel { - -constexpr size_t jail_name_max_size = 50; - -ErrorOr Process::sys$jail_create(Userspace user_params) -{ - VERIFY_NO_PROCESS_BIG_LOCK(this); - TRY(require_promise(Pledge::jail)); - - auto params = TRY(copy_typed_from_user(user_params)); - auto jail_name = TRY(get_syscall_path_argument(params.name)); - if (jail_name->length() > jail_name_max_size) - return ENAMETOOLONG; - - params.index = TRY(m_attached_jail.with([&](auto& my_jail) -> ErrorOr { - // Note: If we are already in a jail, don't let the process to be able to create other jails - // even if it will not be able to join them later on. The reason for this is to prevent as much as possible - // any info leak about the "outside world" jail metadata. - if (my_jail) - return Error::from_errno(EPERM); - auto jail = TRY(Jail::create(move(jail_name), static_cast(params.flags))); - return jail->index().value(); - })); - // Note: We do the copy_to_user outside of the m_attached_jail Spinlock locked scope because - // we rely on page faults to work properly. - TRY(copy_to_user(user_params, ¶ms)); - return 0; -} - -ErrorOr Process::sys$jail_attach(Userspace user_params) -{ - VERIFY_NO_PROCESS_BIG_LOCK(this); - TRY(require_promise(Pledge::jail)); - - // NOTE: Because the user might run a binary that is using this syscall and - // that binary was marked as SUID, then the user might be unaware of the - // fact that while no new setuid binaries might be executed, he is already - // running within such binary so for the sake of completeness and preventing - // naive sense of being secure, we should block that. - TRY(with_protected_data([&](auto& protected_data) -> ErrorOr { - if (protected_data.executable_is_setid) - return EPERM; - return {}; - })); - - auto params = TRY(copy_typed_from_user(user_params)); - return m_attached_jail.with([&](auto& my_jail) -> ErrorOr { - // Note: If we are already in a jail, don't let the process escape it even if - // it knows there are other jails. - // Note: To ensure the process doesn't try to maliciously enumerate all jails - // in the system, just return EPERM before doing anything else. - if (my_jail) - return EPERM; - auto jail = Jail::find_by_index(static_cast(params.index)); - if (!jail) - return EINVAL; - my_jail = *jail; - my_jail->attach_count().with([&](auto& attach_count) { - attach_count++; - }); - m_jail_process_list.with([&](auto& list_ptr) { - list_ptr = my_jail->process_list(); - if (list_ptr) { - list_ptr->attached_processes().with([&](auto& list) { - list.append(*this); - }); - } - }); - return 0; - }); -} - -} diff --git a/Kernel/Syscalls/keymap.cpp b/Kernel/Syscalls/keymap.cpp deleted file mode 100644 index 63a594f3c48..00000000000 --- a/Kernel/Syscalls/keymap.cpp +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include - -namespace Kernel { - -constexpr size_t map_name_max_size = 50; - -ErrorOr Process::sys$setkeymap(Userspace user_params) -{ - VERIFY_NO_PROCESS_BIG_LOCK(this); - TRY(require_promise(Pledge::setkeymap)); - - auto credentials = this->credentials(); - if (!credentials->is_superuser()) - return EPERM; - - auto params = TRY(copy_typed_from_user(user_params)); - - Keyboard::CharacterMapData character_map_data; - - TRY(copy_n_from_user(character_map_data.map, params.map, CHAR_MAP_SIZE)); - TRY(copy_n_from_user(character_map_data.shift_map, params.shift_map, CHAR_MAP_SIZE)); - TRY(copy_n_from_user(character_map_data.alt_map, params.alt_map, CHAR_MAP_SIZE)); - TRY(copy_n_from_user(character_map_data.altgr_map, params.altgr_map, CHAR_MAP_SIZE)); - TRY(copy_n_from_user(character_map_data.shift_altgr_map, params.shift_altgr_map, CHAR_MAP_SIZE)); - - auto map_name = TRY(get_syscall_path_argument(params.map_name)); - if (map_name->length() > map_name_max_size) - return ENAMETOOLONG; - - HIDManagement::the().set_maps(move(map_name), character_map_data); - return 0; -} - -ErrorOr Process::sys$getkeymap(Userspace user_params) -{ - VERIFY_NO_PROCESS_BIG_LOCK(this); - TRY(require_promise(Pledge::getkeymap)); - auto params = TRY(copy_typed_from_user(user_params)); - - return HIDManagement::the().keymap_data().with([&](auto const& keymap_data) -> ErrorOr { - if (params.map_name.size < keymap_data.character_map_name->length()) - return ENAMETOOLONG; - TRY(copy_to_user(params.map_name.data, keymap_data.character_map_name->characters(), keymap_data.character_map_name->length())); - - auto const& character_maps = keymap_data.character_map; - TRY(copy_n_to_user(params.map, character_maps.map, CHAR_MAP_SIZE)); - TRY(copy_n_to_user(params.shift_map, character_maps.shift_map, CHAR_MAP_SIZE)); - TRY(copy_n_to_user(params.alt_map, character_maps.alt_map, CHAR_MAP_SIZE)); - TRY(copy_n_to_user(params.altgr_map, character_maps.altgr_map, CHAR_MAP_SIZE)); - TRY(copy_n_to_user(params.shift_altgr_map, character_maps.shift_altgr_map, CHAR_MAP_SIZE)); - return 0; - }); -} - -} diff --git a/Kernel/Syscalls/kill.cpp b/Kernel/Syscalls/kill.cpp deleted file mode 100644 index 84491f6a663..00000000000 --- a/Kernel/Syscalls/kill.cpp +++ /dev/null @@ -1,148 +0,0 @@ -/* - * Copyright (c) 2018-2023, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include - -namespace Kernel { - -ErrorOr Process::do_kill(Process& process, int signal) -{ - // FIXME: Should setuid processes have some special treatment here? - auto credentials = this->credentials(); - auto kill_process_credentials = process.credentials(); - - bool can_send_signal = credentials->is_superuser() - || credentials->euid() == kill_process_credentials->uid() - || credentials->uid() == kill_process_credentials->uid() - || (signal == SIGCONT && credentials->pgid() == kill_process_credentials->pgid()); - if (!can_send_signal) - return EPERM; - if (process.is_kernel_process()) { - process.name().with([&](auto& process_name) { - dbgln("Attempted to send signal {} to kernel process {} ({})", signal, process_name.representable_view(), process.pid()); - }); - return EPERM; - } - if (signal != 0) - return process.send_signal(signal, this); - return {}; -} - -ErrorOr Process::do_killpg(ProcessGroupID pgrp, int signal) -{ - VERIFY(pgrp >= 0); - - // Send the signal to all processes in the given group. - if (pgrp == 0) { - // Send the signal to our own pgrp. - pgrp = pgid(); - } - - bool group_was_empty = true; - bool any_succeeded = false; - ErrorOr error; - - TRY(Process::current().for_each_in_pgrp_in_same_jail(pgrp, [&](auto& process) -> ErrorOr { - group_was_empty = false; - - ErrorOr res = do_kill(process, signal); - if (!res.is_error()) - any_succeeded = true; - else - error = move(res); - return {}; - })); - - if (group_was_empty) - return ESRCH; - if (any_succeeded) - return {}; - return error; -} - -ErrorOr Process::do_killall(int signal) -{ - bool any_succeeded = false; - ErrorOr error; - - // Send the signal to all processes we have access to for. - TRY(Process::for_each_in_same_jail([&](auto& process) -> ErrorOr { - ErrorOr res; - if (process.pid() == pid()) - res = do_killself(signal); - else - res = do_kill(process, signal); - - if (!res.is_error()) - any_succeeded = true; - else - error = move(res); - return {}; - })); - - if (any_succeeded) - return {}; - return error; -} - -ErrorOr Process::do_killself(int signal) -{ - if (signal == 0) - return {}; - - auto* current_thread = Thread::current(); - if (!current_thread->should_ignore_signal(signal)) - current_thread->send_signal(signal, this); - - return {}; -} - -ErrorOr Process::sys$kill(pid_t pid_or_pgid, int signal) -{ - VERIFY_NO_PROCESS_BIG_LOCK(this); - if (pid_or_pgid == pid().value()) - TRY(require_promise(Pledge::stdio)); - else - TRY(require_promise(Pledge::proc)); - - if (signal < 0 || signal >= 32) - return EINVAL; - if (pid_or_pgid < -1) { - if (pid_or_pgid == NumericLimits::min()) - return EINVAL; - TRY(do_killpg(-pid_or_pgid, signal)); - return 0; - } - if (pid_or_pgid == -1) { - TRY(do_killall(signal)); - return 0; - } - if (pid_or_pgid == pid().value()) { - TRY(do_killself(signal)); - return 0; - } - VERIFY(pid_or_pgid >= 0); - auto peer = Process::from_pid_in_same_jail(pid_or_pgid); - if (!peer) - return ESRCH; - TRY(do_kill(*peer, signal)); - return 0; -} - -ErrorOr Process::sys$killpg(pid_t pgrp, int signum) -{ - VERIFY_NO_PROCESS_BIG_LOCK(this); - TRY(require_promise(Pledge::proc)); - if (signum < 1 || signum >= 32) - return EINVAL; - if (pgrp < 0) - return EINVAL; - - TRY(do_killpg(pgrp, signum)); - return 0; -} - -} diff --git a/Kernel/Syscalls/link.cpp b/Kernel/Syscalls/link.cpp deleted file mode 100644 index 1c206aa54e7..00000000000 --- a/Kernel/Syscalls/link.cpp +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include - -namespace Kernel { - -ErrorOr Process::sys$link(Userspace user_params) -{ - VERIFY_NO_PROCESS_BIG_LOCK(this); - TRY(require_promise(Pledge::cpath)); - auto params = TRY(copy_typed_from_user(user_params)); - auto old_path = TRY(try_copy_kstring_from_user(params.old_path)); - auto new_path = TRY(try_copy_kstring_from_user(params.new_path)); - - TRY(VirtualFileSystem::the().link(credentials(), old_path->view(), new_path->view(), current_directory())); - return 0; -} - -ErrorOr Process::sys$symlink(Userspace user_params) -{ - VERIFY_NO_PROCESS_BIG_LOCK(this); - TRY(require_promise(Pledge::cpath)); - auto params = TRY(copy_typed_from_user(user_params)); - - auto target = TRY(get_syscall_path_argument(params.target)); - auto linkpath = TRY(get_syscall_path_argument(params.linkpath)); - CustodyBase base(params.dirfd, target->view()); - TRY(VirtualFileSystem::the().symlink(credentials(), target->view(), linkpath->view(), base)); - return 0; -} - -} diff --git a/Kernel/Syscalls/lseek.cpp b/Kernel/Syscalls/lseek.cpp deleted file mode 100644 index cc4120e38d7..00000000000 --- a/Kernel/Syscalls/lseek.cpp +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include - -namespace Kernel { - -ErrorOr Process::sys$lseek(int fd, Userspace userspace_offset, int whence) -{ - VERIFY_NO_PROCESS_BIG_LOCK(this); - TRY(require_promise(Pledge::stdio)); - auto description = TRY(open_file_description(fd)); - off_t offset; - TRY(copy_from_user(&offset, userspace_offset)); - auto seek_result = TRY(description->seek(offset, whence)); - TRY(copy_to_user(userspace_offset, &seek_result)); - return 0; -} - -} diff --git a/Kernel/Syscalls/mkdir.cpp b/Kernel/Syscalls/mkdir.cpp deleted file mode 100644 index aef932e5e41..00000000000 --- a/Kernel/Syscalls/mkdir.cpp +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include - -namespace Kernel { - -ErrorOr Process::sys$mkdir(int dirfd, Userspace user_path, size_t path_length, mode_t mode) -{ - VERIFY_NO_PROCESS_BIG_LOCK(this); - TRY(require_promise(Pledge::cpath)); - auto path = TRY(get_syscall_path_argument(user_path, path_length)); - - CustodyBase base(dirfd, path->view()); - TRY(VirtualFileSystem::the().mkdir(credentials(), path->view(), mode & ~umask(), base)); - return 0; -} -} diff --git a/Kernel/Syscalls/mknod.cpp b/Kernel/Syscalls/mknod.cpp deleted file mode 100644 index 353fbe6cc29..00000000000 --- a/Kernel/Syscalls/mknod.cpp +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include - -namespace Kernel { - -ErrorOr Process::sys$mknod(Userspace user_params) -{ - VERIFY_NO_PROCESS_BIG_LOCK(this); - TRY(require_promise(Pledge::dpath)); - auto params = TRY(copy_typed_from_user(user_params)); - - auto credentials = this->credentials(); - if (!credentials->is_superuser() && !is_regular_file(params.mode) && !is_fifo(params.mode) && !is_socket(params.mode)) - return EPERM; - auto path = TRY(get_syscall_path_argument(params.path)); - - CustodyBase base(params.dirfd, path->view()); - TRY(VirtualFileSystem::the().mknod(credentials, path->view(), params.mode & ~umask(), params.dev, base)); - return 0; -} - -} diff --git a/Kernel/Syscalls/mmap.cpp b/Kernel/Syscalls/mmap.cpp deleted file mode 100644 index d9b52189679..00000000000 --- a/Kernel/Syscalls/mmap.cpp +++ /dev/null @@ -1,558 +0,0 @@ -/* - * Copyright (c) 2018-2021, Andreas Kling - * Copyright (c) 2021, Leon Albrecht - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#if ARCH(X86_64) -# include -#endif - -namespace Kernel { - -ErrorOr Process::validate_mmap_prot(int prot, bool map_stack, bool map_anonymous, Memory::Region const* region) const -{ - bool make_writable = prot & PROT_WRITE; - bool make_executable = prot & PROT_EXEC; - - if (map_anonymous && make_executable && !(executable()->mount_flags() & MS_AXALLOWED)) - return EINVAL; - - if (map_stack && make_executable) - return EINVAL; - - if (executable()->mount_flags() & MS_WXALLOWED) - return {}; - - if (make_writable && make_executable) - return EINVAL; - - if (region) { - if (make_writable && region->has_been_executable()) - return EINVAL; - - if (make_executable && region->has_been_writable() && should_reject_transition_to_executable_from_writable_prot()) - return EINVAL; - } - - return {}; -} - -ErrorOr Process::validate_inode_mmap_prot(int prot, bool readable_description, bool description_writable, bool map_shared) const -{ - if ((prot & PROT_READ) && !readable_description) - return EACCES; - - if (map_shared) { - // FIXME: What about readonly filesystem mounts? We cannot make a - // decision here without knowing the mount flags, so we would need to - // keep a Custody or something from mmap time. - if ((prot & PROT_WRITE) && !description_writable) - return EACCES; - } - return {}; -} - -ErrorOr Process::sys$mmap(Userspace user_params) -{ - VERIFY_NO_PROCESS_BIG_LOCK(this); - TRY(require_promise(Pledge::stdio)); - auto params = TRY(copy_typed_from_user(user_params)); - - auto addr = (FlatPtr)params.addr; - auto size = params.size; - auto alignment = params.alignment ? params.alignment : PAGE_SIZE; - auto prot = params.prot; - auto flags = params.flags; - auto fd = params.fd; - auto offset = params.offset; - - if (prot & PROT_EXEC) { - TRY(require_promise(Pledge::prot_exec)); - } - - if (flags & MAP_FIXED || flags & MAP_FIXED_NOREPLACE) { - TRY(require_promise(Pledge::map_fixed)); - } - - if (alignment & ~PAGE_MASK) - return EINVAL; - - size_t rounded_size = TRY(Memory::page_round_up(size)); - if (!Memory::is_user_range(VirtualAddress(addr), rounded_size)) - return EFAULT; - - OwnPtr name; - if (params.name.characters) { - if (params.name.length > PATH_MAX) - return ENAMETOOLONG; - name = TRY(try_copy_kstring_from_user(params.name)); - } - - if (size == 0) - return EINVAL; - if ((FlatPtr)addr & ~PAGE_MASK) - return EINVAL; - - bool map_shared = flags & MAP_SHARED; - bool map_anonymous = flags & MAP_ANONYMOUS; - bool map_private = flags & MAP_PRIVATE; - bool map_stack = flags & MAP_STACK; - bool map_fixed = flags & MAP_FIXED; - bool map_noreserve = flags & MAP_NORESERVE; - bool map_randomized = flags & MAP_RANDOMIZED; - bool map_fixed_noreplace = flags & MAP_FIXED_NOREPLACE; - - if (map_shared && map_private) - return EINVAL; - - if (!map_shared && !map_private) - return EINVAL; - - if ((map_fixed || map_fixed_noreplace) && map_randomized) - return EINVAL; - - TRY(validate_mmap_prot(prot, map_stack, map_anonymous)); - - if (map_stack && (!map_private || !map_anonymous)) - return EINVAL; - - Memory::VirtualRange requested_range { VirtualAddress { addr }, rounded_size }; - if (addr && !(map_fixed || map_fixed_noreplace)) { - // If there's an address but MAP_FIXED wasn't specified, the address is just a hint. - requested_range = { {}, rounded_size }; - } - - Memory::Region* region = nullptr; - - RefPtr description; - LockRefPtr vmobject; - u64 used_offset = 0; - - if (map_anonymous) { - auto strategy = map_noreserve ? AllocationStrategy::None : AllocationStrategy::Reserve; - - if (flags & MAP_PURGEABLE) { - vmobject = TRY(Memory::AnonymousVMObject::try_create_purgeable_with_size(rounded_size, strategy)); - } else { - vmobject = TRY(Memory::AnonymousVMObject::try_create_with_size(rounded_size, strategy)); - } - } else { - if (offset < 0) - return EINVAL; - used_offset = static_cast(offset); - if (static_cast(offset) & ~PAGE_MASK) - return EINVAL; - description = TRY(open_file_description(fd)); - if (description->is_directory()) - return ENODEV; - // Require read access even when read protection is not requested. - if (!description->is_readable()) - return EACCES; - if (map_shared) { - if ((prot & PROT_WRITE) && !description->is_writable()) - return EACCES; - } - if (description->inode()) - TRY(validate_inode_mmap_prot(prot, description->is_readable(), description->is_writable(), map_shared)); - - vmobject = TRY(description->vmobject_for_mmap(*this, requested_range, used_offset, map_shared)); - } - - return address_space().with([&](auto& space) -> ErrorOr { - // If MAP_FIXED is specified, existing mappings that intersect the requested range are removed. - if (map_fixed) - TRY(space->unmap_mmap_range(VirtualAddress(addr), size)); - - region = TRY(space->allocate_region_with_vmobject( - map_randomized ? Memory::RandomizeVirtualAddress::Yes : Memory::RandomizeVirtualAddress::No, - requested_range.base(), - requested_range.size(), - alignment, - vmobject.release_nonnull(), - used_offset, - {}, - prot, - map_shared)); - - if (!region) - return ENOMEM; - - if (description) - region->set_mmap(true, description->is_readable(), description->is_writable()); - else - region->set_mmap(true, false, false); - - if (map_shared) - region->set_shared(true); - if (map_stack) - region->set_stack(true); - if (name) - region->set_name(move(name)); - - PerformanceManager::add_mmap_perf_event(*this, *region); - - return region->vaddr().get(); - }); -} - -ErrorOr Process::sys$mprotect(Userspace addr, size_t size, int prot) -{ - VERIFY_NO_PROCESS_BIG_LOCK(this); - TRY(require_promise(Pledge::stdio)); - - if (prot & PROT_EXEC) { - TRY(require_promise(Pledge::prot_exec)); - } - - auto range_to_mprotect = TRY(Memory::expand_range_to_page_boundaries(addr.ptr(), size)); - if (!range_to_mprotect.size()) - return EINVAL; - - if (!is_user_range(range_to_mprotect)) - return EFAULT; - - return address_space().with([&](auto& space) -> ErrorOr { - if (auto* whole_region = space->find_region_from_range(range_to_mprotect)) { - if (!whole_region->is_mmap()) - return EPERM; - if (whole_region->is_immutable()) - return EPERM; - TRY(validate_mmap_prot(prot, whole_region->is_stack(), whole_region->vmobject().is_anonymous(), whole_region)); - if (whole_region->access() == Memory::prot_to_region_access_flags(prot)) - return 0; - if (whole_region->vmobject().is_inode()) - TRY(validate_inode_mmap_prot(prot, whole_region->mmapped_from_readable(), whole_region->mmapped_from_writable(), whole_region->is_shared())); - whole_region->set_readable(prot & PROT_READ); - whole_region->set_writable(prot & PROT_WRITE); - whole_region->set_executable(prot & PROT_EXEC); - - whole_region->remap(); - return 0; - } - - // Check if we can carve out the desired range from an existing region - if (auto* old_region = space->find_region_containing(range_to_mprotect)) { - if (!old_region->is_mmap()) - return EPERM; - if (old_region->is_immutable()) - return EPERM; - TRY(validate_mmap_prot(prot, old_region->is_stack(), old_region->vmobject().is_anonymous(), old_region)); - if (old_region->access() == Memory::prot_to_region_access_flags(prot)) - return 0; - if (old_region->vmobject().is_inode()) - TRY(validate_inode_mmap_prot(prot, old_region->mmapped_from_readable(), old_region->mmapped_from_writable(), old_region->is_shared())); - - // Remove the old region from our regions tree, since were going to add another region - // with the exact same start address. - auto region = space->take_region(*old_region); - region->unmap(); - - // This vector is the region(s) adjacent to our range. - // We need to allocate a new region for the range we wanted to change permission bits on. - auto adjacent_regions = TRY(space->try_split_region_around_range(*region, range_to_mprotect)); - - size_t new_range_offset_in_vmobject = region->offset_in_vmobject() + (range_to_mprotect.base().get() - region->range().base().get()); - auto* new_region = TRY(space->try_allocate_split_region(*region, range_to_mprotect, new_range_offset_in_vmobject)); - new_region->set_readable(prot & PROT_READ); - new_region->set_writable(prot & PROT_WRITE); - new_region->set_executable(prot & PROT_EXEC); - - // Map the new regions using our page directory (they were just allocated and don't have one). - for (auto* adjacent_region : adjacent_regions) { - TRY(adjacent_region->map(space->page_directory())); - } - TRY(new_region->map(space->page_directory())); - return 0; - } - - if (auto const& regions = TRY(space->find_regions_intersecting(range_to_mprotect)); regions.size()) { - size_t full_size_found = 0; - // Check that all intersecting regions are compatible. - for (auto const* region : regions) { - if (!region->is_mmap()) - return EPERM; - if (region->is_immutable()) - return EPERM; - TRY(validate_mmap_prot(prot, region->is_stack(), region->vmobject().is_anonymous(), region)); - if (region->vmobject().is_inode()) - TRY(validate_inode_mmap_prot(prot, region->mmapped_from_readable(), region->mmapped_from_writable(), region->is_shared())); - - full_size_found += region->range().intersect(range_to_mprotect).size(); - } - - if (full_size_found != range_to_mprotect.size()) - return ENOMEM; - - // Finally, iterate over each region, either updating its access flags if the range covers it wholly, - // or carving out a new subregion with the appropriate access flags set. - for (auto* old_region : regions) { - if (old_region->access() == Memory::prot_to_region_access_flags(prot)) - continue; - - auto const intersection_to_mprotect = range_to_mprotect.intersect(old_region->range()); - // If the region is completely covered by range, simply update the access flags - if (intersection_to_mprotect == old_region->range()) { - old_region->set_readable(prot & PROT_READ); - old_region->set_writable(prot & PROT_WRITE); - old_region->set_executable(prot & PROT_EXEC); - - old_region->remap(); - continue; - } - // Remove the old region from our regions tree, since were going to add another region - // with the exact same start address. - auto region = space->take_region(*old_region); - region->unmap(); - - // This vector is the region(s) adjacent to our range. - // We need to allocate a new region for the range we wanted to change permission bits on. - auto adjacent_regions = TRY(space->try_split_region_around_range(*old_region, intersection_to_mprotect)); - - // Since the range is not contained in a single region, it can only partially cover its starting and ending region, - // therefore carving out a chunk from the region will always produce a single extra region, and not two. - VERIFY(adjacent_regions.size() == 1); - - size_t new_range_offset_in_vmobject = old_region->offset_in_vmobject() + (intersection_to_mprotect.base().get() - old_region->range().base().get()); - auto* new_region = TRY(space->try_allocate_split_region(*region, intersection_to_mprotect, new_range_offset_in_vmobject)); - - new_region->set_readable(prot & PROT_READ); - new_region->set_writable(prot & PROT_WRITE); - new_region->set_executable(prot & PROT_EXEC); - - // Map the new region using our page directory (they were just allocated and don't have one) if any. - if (adjacent_regions.size()) - TRY(adjacent_regions[0]->map(space->page_directory())); - - TRY(new_region->map(space->page_directory())); - } - - return 0; - } - - return EINVAL; - }); -} - -ErrorOr Process::sys$madvise(Userspace address, size_t size, int advice) -{ - VERIFY_NO_PROCESS_BIG_LOCK(this); - TRY(require_promise(Pledge::stdio)); - - auto range_to_madvise = TRY(Memory::expand_range_to_page_boundaries(address.ptr(), size)); - - if (!range_to_madvise.size()) - return EINVAL; - - if (!is_user_range(range_to_madvise)) - return EFAULT; - - return address_space().with([&](auto& space) -> ErrorOr { - auto* region = space->find_region_from_range(range_to_madvise); - if (!region) - return EINVAL; - if (!region->is_mmap()) - return EPERM; - if (region->is_immutable()) - return EPERM; - if (advice == MADV_SET_VOLATILE || advice == MADV_SET_NONVOLATILE) { - if (!region->vmobject().is_anonymous()) - return EINVAL; - auto& vmobject = static_cast(region->vmobject()); - if (!vmobject.is_purgeable()) - return EINVAL; - bool was_purged = false; - TRY(vmobject.set_volatile(advice == MADV_SET_VOLATILE, was_purged)); - return was_purged ? 1 : 0; - } - return EINVAL; - }); -} - -ErrorOr Process::sys$set_mmap_name(Userspace user_params) -{ - VERIFY_NO_PROCESS_BIG_LOCK(this); - TRY(require_promise(Pledge::stdio)); - auto params = TRY(copy_typed_from_user(user_params)); - - if (params.name.length > PATH_MAX) - return ENAMETOOLONG; - - auto name = TRY(try_copy_kstring_from_user(params.name)); - auto range = TRY(Memory::expand_range_to_page_boundaries((FlatPtr)params.addr, params.size)); - - return address_space().with([&](auto& space) -> ErrorOr { - auto* region = space->find_region_from_range(range); - if (!region) - return EINVAL; - if (!region->is_mmap()) - return EPERM; - - if (region->is_immutable()) - return EPERM; - - region->set_name(move(name)); - PerformanceManager::add_mmap_perf_event(*this, *region); - - return 0; - }); -} - -ErrorOr Process::sys$munmap(Userspace addr, size_t size) -{ - VERIFY_NO_PROCESS_BIG_LOCK(this); - TRY(require_promise(Pledge::stdio)); - TRY(address_space().with([&](auto& space) { - return space->unmap_mmap_range(addr.vaddr(), size); - })); - return 0; -} - -ErrorOr Process::sys$mremap(Userspace user_params) -{ - VERIFY_NO_PROCESS_BIG_LOCK(this); - TRY(require_promise(Pledge::stdio)); - auto params = TRY(copy_typed_from_user(user_params)); - - auto old_range = TRY(Memory::expand_range_to_page_boundaries((FlatPtr)params.old_address, params.old_size)); - - return address_space().with([&](auto& space) -> ErrorOr { - auto* old_region = space->find_region_from_range(old_range); - if (!old_region) - return EINVAL; - - if (!old_region->is_mmap()) - return EPERM; - - if (old_region->is_immutable()) - return EPERM; - - if (old_region->vmobject().is_shared_inode() && params.flags & MAP_PRIVATE && !(params.flags & (MAP_ANONYMOUS | MAP_NORESERVE))) { - auto range = old_region->range(); - auto old_prot = region_access_flags_to_prot(old_region->access()); - auto old_offset = old_region->offset_in_vmobject(); - NonnullLockRefPtr inode = static_cast(old_region->vmobject()).inode(); - - auto new_vmobject = TRY(Memory::PrivateInodeVMObject::try_create_with_inode(inode)); - auto old_name = old_region->take_name(); - - bool old_region_was_mmapped_from_readable = old_region->mmapped_from_readable(); - bool old_region_was_mmapped_from_writable = old_region->mmapped_from_writable(); - - old_region->unmap(); - space->deallocate_region(*old_region); - - auto* new_region = TRY(space->allocate_region_with_vmobject(range, move(new_vmobject), old_offset, old_name->view(), old_prot, false)); - new_region->set_mmap(true, old_region_was_mmapped_from_readable, old_region_was_mmapped_from_writable); - return new_region->vaddr().get(); - } - - dbgln("sys$mremap: Unimplemented remap request (flags={})", params.flags); - return ENOTIMPL; - }); -} - -ErrorOr Process::sys$annotate_mapping(Userspace address, int flags) -{ - VERIFY_NO_PROCESS_BIG_LOCK(this); - if (flags == to_underlying(VirtualMemoryRangeFlags::None)) - return EINVAL; - - if (!address) - return EINVAL; - - if (!Memory::is_user_address(address.vaddr())) - return EFAULT; - - return address_space().with([&](auto& space) -> ErrorOr { - if (space->enforces_syscall_regions() && (flags & to_underlying(VirtualMemoryRangeFlags::SyscallCode))) - return EPERM; - - auto* region = space->find_region_containing(Memory::VirtualRange { address.vaddr(), 1 }); - if (!region) - return EINVAL; - - if (!region->is_mmap() && !region->is_initially_loaded_executable_segment()) - return EINVAL; - if (region->is_immutable()) - return EPERM; - - if (flags & to_underlying(VirtualMemoryRangeFlags::SyscallCode)) - region->set_syscall_region(true); - if (flags & to_underlying(VirtualMemoryRangeFlags::Immutable)) - region->set_immutable(); - return 0; - }); -} - -ErrorOr Process::sys$msync(Userspace address, size_t size, int flags) -{ - VERIFY_NO_PROCESS_BIG_LOCK(this); - if ((flags & (MS_SYNC | MS_ASYNC | MS_INVALIDATE)) != flags) - return EINVAL; - - bool is_async = (flags & MS_ASYNC) == MS_ASYNC; - bool is_sync = (flags & MS_SYNC) == MS_SYNC; - if (is_sync == is_async) - return EINVAL; - - if (address.ptr() % PAGE_SIZE != 0) - return EINVAL; - - // Note: This is not specified - auto rounded_size = TRY(Memory::page_round_up(size)); - - return address_space().with([&](auto& space) -> ErrorOr { - auto regions = TRY(space->find_regions_intersecting(Memory::VirtualRange { address.vaddr(), rounded_size })); - // All regions from address up to address+size shall be mapped - if (regions.is_empty()) - return ENOMEM; - - size_t total_intersection_size = 0; - Memory::VirtualRange range_to_sync { address.vaddr(), rounded_size }; - for (auto const* region : regions) { - // Region was not mapped - if (!region->is_mmap()) - return ENOMEM; - total_intersection_size += region->range().intersect(range_to_sync).size(); - } - // Part of the indicated range was not mapped - if (total_intersection_size != size) - return ENOMEM; - - for (auto* region : regions) { - auto& vmobject = region->vmobject(); - if (!vmobject.is_shared_inode()) - continue; - - off_t offset = region->offset_in_vmobject() + address.ptr() - region->range().base().get(); - - auto& inode_vmobject = static_cast(vmobject); - // FIXME: If multiple regions belong to the same vmobject we might want to coalesce these writes - // FIXME: Handle MS_ASYNC - TRY(inode_vmobject.sync(offset / PAGE_SIZE, rounded_size / PAGE_SIZE)); - // FIXME: Handle MS_INVALIDATE - } - return 0; - }); -} - -} diff --git a/Kernel/Syscalls/mount.cpp b/Kernel/Syscalls/mount.cpp deleted file mode 100644 index 58586c9e0cd..00000000000 --- a/Kernel/Syscalls/mount.cpp +++ /dev/null @@ -1,127 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * Copyright (c) 2022, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include - -namespace Kernel { - -ErrorOr Process::sys$fsopen(Userspace user_params) -{ - VERIFY_NO_PROCESS_BIG_LOCK(this); - TRY(require_promise(Pledge::mount)); - auto credentials = this->credentials(); - if (!credentials->is_superuser()) - return Error::from_errno(EPERM); - auto params = TRY(copy_typed_from_user(user_params)); - // NOTE: 16 characters should be enough for any fstype today and in the future. - auto fs_type_string = TRY(get_syscall_name_string_fixed_buffer<16>(params.fs_type)); - - // NOTE: If some userspace program uses MS_REMOUNT, return EINVAL to indicate that we never want this - // flag to appear in the mount table... - if (params.flags & MS_REMOUNT || params.flags & MS_BIND) - return Error::from_errno(EINVAL); - - auto const* fs_type_initializer = TRY(VirtualFileSystem::find_filesystem_type_initializer(fs_type_string.representable_view())); - VERIFY(fs_type_initializer); - auto mount_file = TRY(MountFile::create(*fs_type_initializer, params.flags)); - auto description = TRY(OpenFileDescription::try_create(move(mount_file))); - return m_fds.with_exclusive([&](auto& fds) -> ErrorOr { - auto new_fd = TRY(fds.allocate()); - fds[new_fd.fd].set(move(description), FD_CLOEXEC); - return new_fd.fd; - }); -} - -ErrorOr Process::sys$fsmount(Userspace user_params) -{ - VERIFY_NO_PROCESS_BIG_LOCK(this); - TRY(require_promise(Pledge::mount)); - auto credentials = this->credentials(); - if (!credentials->is_superuser()) - return Error::from_errno(EPERM); - - auto params = TRY(copy_typed_from_user(user_params)); - auto mount_description = TRY(open_file_description(params.mount_fd)); - if (!mount_description->is_mount_file()) - return Error::from_errno(EINVAL); - - RefPtr source_description = TRY(open_file_description_ignoring_negative(params.source_fd)); - auto target = TRY(try_copy_kstring_from_user(params.target)); - auto target_custody = TRY(VirtualFileSystem::the().resolve_path(credentials, target->view(), current_directory())); - auto flags = mount_description->mount_file()->mount_flags(); - TRY(VirtualFileSystem::the().mount(*mount_description->mount_file(), source_description.ptr(), target_custody, flags)); - return 0; -} - -ErrorOr Process::sys$remount(Userspace user_params) -{ - VERIFY_NO_PROCESS_BIG_LOCK(this); - TRY(require_promise(Pledge::mount)); - auto credentials = this->credentials(); - if (!credentials->is_superuser()) - return EPERM; - - auto params = TRY(copy_typed_from_user(user_params)); - if (params.flags & MS_REMOUNT) - return EINVAL; - if (params.flags & MS_BIND) - return EINVAL; - - auto target = TRY(try_copy_kstring_from_user(params.target)); - auto target_custody = TRY(VirtualFileSystem::the().resolve_path(credentials, target->view(), current_directory())); - TRY(VirtualFileSystem::the().remount(target_custody, params.flags)); - return 0; -} - -ErrorOr Process::sys$bindmount(Userspace user_params) -{ - VERIFY_NO_PROCESS_BIG_LOCK(this); - TRY(require_promise(Pledge::mount)); - auto credentials = this->credentials(); - if (!credentials->is_superuser()) - return EPERM; - - auto params = TRY(copy_typed_from_user(user_params)); - if (params.flags & MS_REMOUNT) - return EINVAL; - if (params.flags & MS_BIND) - return EINVAL; - - auto source_fd = params.source_fd; - auto target = TRY(try_copy_kstring_from_user(params.target)); - auto target_custody = TRY(VirtualFileSystem::the().resolve_path(credentials, target->view(), current_directory())); - - auto description = TRY(open_file_description(source_fd)); - if (!description->custody()) { - // NOTE: We only support bind-mounting inodes, not arbitrary files. - return ENODEV; - } - - TRY(VirtualFileSystem::the().bind_mount(*description->custody(), target_custody, params.flags)); - return 0; -} - -ErrorOr Process::sys$umount(Userspace user_mountpoint, size_t mountpoint_length) -{ - VERIFY_NO_PROCESS_BIG_LOCK(this); - auto credentials = this->credentials(); - if (!credentials->is_superuser()) - return EPERM; - - TRY(require_promise(Pledge::mount)); - - auto mountpoint = TRY(get_syscall_path_argument(user_mountpoint, mountpoint_length)); - auto custody = TRY(VirtualFileSystem::the().resolve_path(credentials, mountpoint->view(), current_directory())); - TRY(VirtualFileSystem::the().unmount(*custody)); - return 0; -} - -} diff --git a/Kernel/Syscalls/open.cpp b/Kernel/Syscalls/open.cpp deleted file mode 100644 index 644808f93cd..00000000000 --- a/Kernel/Syscalls/open.cpp +++ /dev/null @@ -1,83 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include -#include -#include - -namespace Kernel { - -ErrorOr Process::open_impl(Userspace user_params) -{ - VERIFY_NO_PROCESS_BIG_LOCK(this); - auto params = TRY(copy_typed_from_user(user_params)); - - int dirfd = params.dirfd; - int options = params.options; - u16 mode = params.mode; - - if (options & O_NOFOLLOW_NOERROR) - return EINVAL; - - if (options & O_UNLINK_INTERNAL) - return EINVAL; - - auto path = TRY(get_syscall_path_argument(params.path)); - - // Disable checking open pledges when building userspace with coverage - // so that all processes can write out coverage data even with pledges - bool skip_pledge_verification = false; - -#ifdef SKIP_PATH_VALIDATION_FOR_COVERAGE_INSTRUMENTATION - if (KLexicalPath::basename(path->view()).ends_with(".profraw"sv)) - skip_pledge_verification = true; -#endif - if (!skip_pledge_verification) { - if (options & O_WRONLY) - TRY(require_promise(Pledge::wpath)); - else if (options & O_RDONLY) - TRY(require_promise(Pledge::rpath)); - - if (options & O_CREAT) - TRY(require_promise(Pledge::cpath)); - } - - // Ignore everything except permission bits. - mode &= 0777; - - dbgln_if(IO_DEBUG, "sys$open(dirfd={}, path='{}', options={}, mode={})", dirfd, path->view(), options, mode); - - auto fd_allocation = TRY(allocate_fd()); - CustodyBase base(dirfd, path->view()); - auto description = TRY(VirtualFileSystem::the().open(credentials(), path->view(), options, mode & ~umask(), base)); - - if (description->inode() && description->inode()->bound_socket()) - return ENXIO; - - return m_fds.with_exclusive([&](auto& fds) -> ErrorOr { - u32 fd_flags = (options & O_CLOEXEC) ? FD_CLOEXEC : 0; - fds[fd_allocation.fd].set(move(description), fd_flags); - return fd_allocation.fd; - }); -} - -ErrorOr Process::close_impl(int fd) -{ - VERIFY_NO_PROCESS_BIG_LOCK(this); - TRY(require_promise(Pledge::stdio)); - auto description = TRY(open_file_description(fd)); - auto result = description->close(); - m_fds.with_exclusive([fd](auto& fds) { fds[fd] = {}; }); - if (result.is_error()) - return result.release_error(); - return 0; -} - -} diff --git a/Kernel/Syscalls/perf_event.cpp b/Kernel/Syscalls/perf_event.cpp deleted file mode 100644 index a359c8e885d..00000000000 --- a/Kernel/Syscalls/perf_event.cpp +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright (c) 2018-2021, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include - -namespace Kernel { - -ErrorOr Process::sys$perf_event(int type, FlatPtr arg1, FlatPtr arg2) -{ - VERIFY_PROCESS_BIG_LOCK_ACQUIRED(this); - auto* events_buffer = current_perf_events_buffer(); - if (!events_buffer) - return 0; - TRY(events_buffer->append(type, arg1, arg2, {})); - return 0; -} - -ErrorOr Process::sys$perf_register_string(Userspace user_string, size_t user_string_length) -{ - VERIFY_PROCESS_BIG_LOCK_ACQUIRED(this); - auto* events_buffer = current_perf_events_buffer(); - if (!events_buffer) - return 0; - - auto string = TRY(try_copy_kstring_from_user(user_string, user_string_length)); - return events_buffer->register_string(move(string)); -} - -} diff --git a/Kernel/Syscalls/pipe.cpp b/Kernel/Syscalls/pipe.cpp deleted file mode 100644 index f5868f6770c..00000000000 --- a/Kernel/Syscalls/pipe.cpp +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include - -namespace Kernel { - -ErrorOr Process::sys$pipe(Userspace pipefd, int flags) -{ - VERIFY_NO_PROCESS_BIG_LOCK(this); - TRY(require_promise(Pledge::stdio)); - - // Reject flags other than O_CLOEXEC, O_NONBLOCK - if ((flags & (O_CLOEXEC | O_NONBLOCK)) != flags) - return EINVAL; - - u32 fd_flags = (flags & O_CLOEXEC) ? FD_CLOEXEC : 0; - auto credentials = this->credentials(); - auto fifo = TRY(FIFO::try_create(credentials->uid())); - - auto reader_description = TRY(fifo->open_direction(FIFO::Direction::Reader)); - auto writer_description = TRY(fifo->open_direction(FIFO::Direction::Writer)); - - reader_description->set_readable(true); - writer_description->set_writable(true); - if (flags & O_NONBLOCK) { - reader_description->set_blocking(false); - writer_description->set_blocking(false); - } - - TRY(m_fds.with_exclusive([&](auto& fds) -> ErrorOr { - auto reader_fd_allocation = TRY(fds.allocate()); - auto writer_fd_allocation = TRY(fds.allocate()); - - fds[reader_fd_allocation.fd].set(move(reader_description), fd_flags); - fds[writer_fd_allocation.fd].set(move(writer_description), fd_flags); - - int fds_for_userspace[2] = { - reader_fd_allocation.fd, - writer_fd_allocation.fd, - }; - if (copy_n_to_user(pipefd, fds_for_userspace, 2).is_error()) { - // Avoid leaking both file descriptors on error. - fds[reader_fd_allocation.fd] = {}; - fds[writer_fd_allocation.fd] = {}; - return EFAULT; - } - return {}; - })); - return 0; -} - -} diff --git a/Kernel/Syscalls/pledge.cpp b/Kernel/Syscalls/pledge.cpp deleted file mode 100644 index 93b7084ba7b..00000000000 --- a/Kernel/Syscalls/pledge.cpp +++ /dev/null @@ -1,96 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include - -namespace Kernel { - -ErrorOr Process::sys$pledge(Userspace user_params) -{ - VERIFY_NO_PROCESS_BIG_LOCK(this); - auto params = TRY(copy_typed_from_user(user_params)); - - FixedStringBuffer promises {}; - bool promises_provided { false }; - FixedStringBuffer execpromises {}; - bool execpromises_provided { false }; - - if (params.promises.characters) { - promises_provided = true; - promises = TRY(get_syscall_string_fixed_buffer(params.promises)); - } - - if (params.execpromises.characters) { - execpromises_provided = true; - execpromises = TRY(get_syscall_string_fixed_buffer(params.execpromises)); - } - - auto parse_pledge = [&](auto pledge_spec, u32& mask) { - auto found_invalid_pledge = true; - pledge_spec.for_each_split_view(' ', SplitBehavior::Nothing, [&mask, &found_invalid_pledge](auto const& part) { -#define __ENUMERATE_PLEDGE_PROMISE(x) \ - if (part == #x##sv) { \ - mask |= (1u << (u32)Pledge::x); \ - return; \ - } - ENUMERATE_PLEDGE_PROMISES -#undef __ENUMERATE_PLEDGE_PROMISE - found_invalid_pledge = false; - }); - return found_invalid_pledge; - }; - - u32 new_promises = 0; - if (promises_provided) { - if (!parse_pledge(promises.representable_view(), new_promises)) - return EINVAL; - } - - u32 new_execpromises = 0; - if (execpromises_provided) { - if (!parse_pledge(execpromises.representable_view(), new_execpromises)) - return EINVAL; - } - - return with_mutable_protected_data([&](auto& protected_data) -> ErrorOr { - if (promises_provided) { - if (protected_data.has_promises && (new_promises & ~protected_data.promises)) { - if (!(protected_data.promises & (1u << (u32)Pledge::no_error))) - return EPERM; - new_promises &= protected_data.promises; - } - } - - if (execpromises_provided) { - if (protected_data.has_execpromises && (new_execpromises & ~protected_data.execpromises)) { - if (!(protected_data.promises & (1u << (u32)Pledge::no_error))) - return EPERM; - new_execpromises &= protected_data.execpromises; - } - } - - // Only apply promises after all validation has occurred, this ensures - // we don't introduce logic bugs like applying the promises, and then - // erroring out when parsing the exec promises later. Such bugs silently - // leave the caller in an unexpected state. - - if (promises_provided) { - protected_data.has_promises = true; - protected_data.promises = new_promises; - } - - if (execpromises_provided) { - protected_data.has_execpromises = true; - protected_data.execpromises = new_execpromises; - } - - return 0; - }); -} - -} diff --git a/Kernel/Syscalls/poll.cpp b/Kernel/Syscalls/poll.cpp deleted file mode 100644 index 2ed0ac1d63f..00000000000 --- a/Kernel/Syscalls/poll.cpp +++ /dev/null @@ -1,139 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include -#include - -namespace Kernel { - -using BlockFlags = Thread::FileBlocker::BlockFlags; - -ErrorOr Process::sys$poll(Userspace user_params) -{ - VERIFY_NO_PROCESS_BIG_LOCK(this); - TRY(require_promise(Pledge::stdio)); - - auto params = TRY(copy_typed_from_user(user_params)); - - if (params.nfds >= OpenFileDescriptions::max_open()) - return ENOBUFS; - - Thread::BlockTimeout timeout; - if (params.timeout) { - auto timeout_time = TRY(copy_time_from_user(params.timeout)); - timeout = Thread::BlockTimeout(false, &timeout_time); - } - - sigset_t sigmask = {}; - if (params.sigmask) - TRY(copy_from_user(&sigmask, params.sigmask)); - - Vector fds_copy; - if (params.nfds > 0) { - Checked nfds_checked = sizeof(pollfd); - nfds_checked *= params.nfds; - if (nfds_checked.has_overflow()) - return EFAULT; - TRY(fds_copy.try_resize(params.nfds)); - TRY(copy_from_user(fds_copy.data(), ¶ms.fds[0], nfds_checked.value())); - } - - Thread::SelectBlocker::FDVector fds_info; - TRY(fds_info.try_ensure_capacity(params.nfds)); - - TRY(m_fds.with_shared([&](auto& fds) -> ErrorOr { - for (size_t i = 0; i < params.nfds; i++) { - auto& pfd = fds_copy[i]; - RefPtr description; - auto description_or_error = fds.open_file_description(pfd.fd); - if (!description_or_error.is_error()) - description = description_or_error.release_value(); - BlockFlags block_flags = BlockFlags::WriteError | BlockFlags::WriteHangUp; // always want POLLERR, POLLHUP, POLLNVAL - if (pfd.events & POLLIN) - block_flags |= BlockFlags::Read; - if (pfd.events & POLLOUT) - block_flags |= BlockFlags::Write; - if (pfd.events & POLLPRI) - block_flags |= BlockFlags::ReadPriority; - if (pfd.events & POLLWRBAND) - block_flags |= BlockFlags::WritePriority; - if (pfd.events & POLLRDHUP) - block_flags |= BlockFlags::ReadHangUp; - fds_info.unchecked_append({ move(description), block_flags }); - } - return {}; - })); - - auto* current_thread = Thread::current(); - - u32 previous_signal_mask = 0; - if (params.sigmask) - previous_signal_mask = current_thread->update_signal_mask(sigmask); - ScopeGuard rollback_signal_mask([&]() { - if (params.sigmask) - current_thread->update_signal_mask(previous_signal_mask); - }); - - if constexpr (IO_DEBUG || POLL_SELECT_DEBUG) - dbgln("polling on {} fds, timeout={}", fds_info.size(), params.timeout); - - if (current_thread->block(timeout, fds_info).was_interrupted()) - return EINTR; - - int fds_with_revents = 0; - - for (unsigned i = 0; i < params.nfds; ++i) { - auto& pfd = fds_copy[i]; - auto& fds_entry = fds_info[i]; - - pfd.revents = 0; - if (fds_entry.unblocked_flags == BlockFlags::None) - continue; - - if (has_flag(fds_entry.unblocked_flags, BlockFlags::WriteHangUp)) - pfd.revents |= POLLHUP; - if (has_flag(fds_entry.unblocked_flags, BlockFlags::WriteError) || !fds_entry.description) { - if (has_flag(fds_entry.unblocked_flags, BlockFlags::WriteError)) - pfd.revents |= POLLERR; - if (!fds_entry.description) - pfd.revents |= POLLNVAL; - } else { - if (has_flag(fds_entry.unblocked_flags, BlockFlags::Read)) { - VERIFY(pfd.events & POLLIN); - pfd.revents |= POLLIN; - } - if (has_flag(fds_entry.unblocked_flags, BlockFlags::ReadPriority)) { - VERIFY(pfd.events & POLLPRI); - pfd.revents |= POLLPRI; - } - if (!has_flag(fds_entry.unblocked_flags, BlockFlags::WriteHangUp) && has_flag(fds_entry.unblocked_flags, BlockFlags::Write)) { - VERIFY(pfd.events & POLLOUT); - pfd.revents |= POLLOUT; - } - if (has_flag(fds_entry.unblocked_flags, BlockFlags::WritePriority)) { - VERIFY(pfd.events & POLLWRBAND); - pfd.revents |= POLLWRBAND; - } - if (has_flag(fds_entry.unblocked_flags, BlockFlags::ReadHangUp)) { - VERIFY(pfd.events & POLLRDHUP); - pfd.revents |= POLLRDHUP; - } - } - if (pfd.revents) - fds_with_revents++; - } - - if (params.nfds > 0) - TRY(copy_n_to_user(¶ms.fds[0], fds_copy.data(), params.nfds)); - - return fds_with_revents; -} - -} diff --git a/Kernel/Syscalls/prctl.cpp b/Kernel/Syscalls/prctl.cpp deleted file mode 100644 index 0a4ddc93a47..00000000000 --- a/Kernel/Syscalls/prctl.cpp +++ /dev/null @@ -1,108 +0,0 @@ -/* - * Copyright (c) 2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include - -namespace Kernel { - -ErrorOr Process::sys$prctl(int option, FlatPtr arg1, FlatPtr arg2, FlatPtr arg3) -{ - VERIFY_NO_PROCESS_BIG_LOCK(this); - return with_mutable_protected_data([&](auto& protected_data) -> ErrorOr { - switch (option) { - case PR_GET_DUMPABLE: - return protected_data.dumpable; - case PR_SET_DUMPABLE: - if (arg1 != 0 && arg1 != 1) - return EINVAL; - protected_data.dumpable = arg1; - return 0; - case PR_GET_NO_NEW_SYSCALL_REGION_ANNOTATIONS: - return address_space().with([&](auto& space) -> ErrorOr { - return space->enforces_syscall_regions(); - }); - case PR_SET_NO_NEW_SYSCALL_REGION_ANNOTATIONS: { - if (arg1 != 0) - return EINVAL; - return address_space().with([&](auto& space) -> ErrorOr { - space->set_enforces_syscall_regions(); - return 0; - }); - return 0; - } - case PR_SET_COREDUMP_METADATA_VALUE: { - auto params = TRY(copy_typed_from_user(arg1)); - if (params.key.length == 0 || params.key.length > 16 * KiB) - return EINVAL; - if (params.value.length > 16 * KiB) - return EINVAL; - auto key = TRY(try_copy_kstring_from_user(params.key)); - auto value = TRY(try_copy_kstring_from_user(params.value)); - TRY(set_coredump_property(move(key), move(value))); - return 0; - } - case PR_SET_PROCESS_NAME: { - TRY(require_promise(Pledge::proc)); - Userspace buffer = arg1; - size_t buffer_size = static_cast(arg2); - Process::Name process_name {}; - TRY(try_copy_name_from_user_into_fixed_string_buffer(buffer, process_name, buffer_size)); - // NOTE: Reject empty and whitespace-only names, as they only confuse users. - if (process_name.representable_view().is_whitespace()) - return EINVAL; - set_name(process_name.representable_view()); - return 0; - } - case PR_GET_PROCESS_NAME: { - TRY(require_promise(Pledge::stdio)); - Userspace buffer = arg1; - size_t buffer_size = static_cast(arg2); - TRY(m_name.with([&buffer, buffer_size](auto& name) -> ErrorOr { - VERIFY(!name.representable_view().is_null()); - return copy_fixed_string_buffer_including_null_char_to_user(buffer, buffer_size, name); - })); - return 0; - } - case PR_SET_THREAD_NAME: { - TRY(require_promise(Pledge::stdio)); - int thread_id = static_cast(arg1); - Userspace buffer = arg2; - size_t buffer_size = static_cast(arg3); - - Thread::Name thread_name {}; - TRY(try_copy_name_from_user_into_fixed_string_buffer(buffer, thread_name, buffer_size)); - auto thread = TRY(get_thread_from_thread_list(thread_id)); - thread->set_name(thread_name.representable_view()); - return 0; - } - case PR_GET_THREAD_NAME: { - TRY(require_promise(Pledge::thread)); - int thread_id = static_cast(arg1); - Userspace buffer = arg2; - size_t buffer_size = static_cast(arg3); - auto thread = TRY(get_thread_from_thread_list(thread_id)); - - TRY(thread->name().with([&](auto& thread_name) -> ErrorOr { - VERIFY(!thread_name.representable_view().is_null()); - return copy_fixed_string_buffer_including_null_char_to_user(buffer, buffer_size, thread_name); - })); - return 0; - } - case PR_SET_NO_TRANSITION_TO_EXECUTABLE_FROM_WRITABLE_PROT: { - TRY(require_promise(Pledge::prot_exec)); - with_mutable_protected_data([](auto& protected_data) { - return protected_data.reject_transition_to_executable_from_writable_prot.set(); - }); - return 0; - } - } - - return EINVAL; - }); -} - -} diff --git a/Kernel/Syscalls/process.cpp b/Kernel/Syscalls/process.cpp deleted file mode 100644 index 63895026015..00000000000 --- a/Kernel/Syscalls/process.cpp +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include - -namespace Kernel { - -ErrorOr Process::sys$getpid() -{ - VERIFY_NO_PROCESS_BIG_LOCK(this); - TRY(require_promise(Pledge::stdio)); - return pid().value(); -} - -ErrorOr Process::sys$getppid() -{ - VERIFY_NO_PROCESS_BIG_LOCK(this); - TRY(require_promise(Pledge::stdio)); - return ppid().value(); -} - -} diff --git a/Kernel/Syscalls/profiled_syscalls.cpp b/Kernel/Syscalls/profiled_syscalls.cpp deleted file mode 100644 index 1e6b411f61c..00000000000 --- a/Kernel/Syscalls/profiled_syscalls.cpp +++ /dev/null @@ -1,274 +0,0 @@ -/* - * Copyright (c) 2022-2023, Jakub Berkop - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include - -namespace Kernel { - -ErrorOr> open_readable_file_description(auto& fds, int fd); - -static ErrorOr get_path_index(auto& fds, int fd, PerformanceEventBuffer* event_buffer) -{ - auto description = TRY(open_readable_file_description(fds, fd)); - - if (auto path = description->original_absolute_path(); !path.is_error()) { - return TRY(event_buffer->register_string(move(path.value()))); - } else if (auto pseudo_path = description->pseudo_path(); !pseudo_path.is_error()) { - return TRY(event_buffer->register_string(move(pseudo_path.value()))); - } else { - auto invalid_path_string = TRY(KString::try_create(""sv)); // TODO: Performance, unecessary allocations. - return TRY(event_buffer->register_string(move(invalid_path_string))); - } -} - -// FIXME: Following functions are very similar, and could be refactored into a more generic solution. -// However, it's not clear how to do it in a way that would be easy to read and understand. -ErrorOr Process::sys$open(Userspace user_params) -{ - Optional start_timestamp = {}; - - // We have to check whether profiling is enabled at before going into the syscall implementation - // so that we can measure the time it took to execute the syscall. - // This approach ensures that we don't have a race condition in case profiling was enabled during - // the execution of the syscall. - // If profiling is disabled at the beginning, we don't want to call TimeManagement::the().monotonic_time() - // because of the overhead it would introduce for every syscall. - bool const profiling_enabled_at_entry = !Thread::current()->is_profiling_suppressed(); - if (profiling_enabled_at_entry) { - start_timestamp = TimeManagement::the().monotonic_time(TimePrecision::Precise); - } - - auto const params = TRY(copy_typed_from_user(user_params)); - auto result = open_impl(user_params); - - if (!profiling_enabled_at_entry || Thread::current()->is_profiling_suppressed()) - return result; - - auto* event_buffer = current_perf_events_buffer(); - if (event_buffer == nullptr) - return result; - - auto const end_timestamp = TimeManagement::the().monotonic_time(TimePrecision::Precise); - auto const duration = end_timestamp - start_timestamp.value(); - - FilesystemEvent data; - data.type = FilesystemEventType::Open; - data.durationNs = static_cast(duration.to_nanoseconds()); - - if (result.is_error()) { - data.result.is_error = true; - data.result.value = result.error().code(); - } else { - data.result.is_error = false; - data.result.value = 0; - } - - auto path = get_syscall_path_argument(params.path); - if (!path.is_error()) { - auto value = event_buffer->register_string(move(path.value())); - data.data.open.filename_index = value.value(); - } - - data.data.open.dirfd = params.dirfd; - data.data.open.options = params.options; - data.data.open.mode = params.mode; - - (void)event_buffer->append(PERF_EVENT_FILESYSTEM, 0, 0, {}, Thread::current(), data); - - return result; -} - -ErrorOr Process::sys$close(int fd) -{ - Optional start_timestamp = {}; - - bool const profiling_enabled_at_entry = !Thread::current()->is_profiling_suppressed(); - if (profiling_enabled_at_entry) { - start_timestamp = TimeManagement::the().monotonic_time(TimePrecision::Precise); - } - - auto result = close_impl(fd); - if (Thread::current()->is_profiling_suppressed()) - return result; - - if (!profiling_enabled_at_entry || Thread::current()->is_profiling_suppressed()) - return result; - - auto* event_buffer = current_perf_events_buffer(); - if (event_buffer == nullptr) - return result; - - auto const end_timestamp = TimeManagement::the().monotonic_time(TimePrecision::Precise); - auto const duration = end_timestamp - start_timestamp.value(); - - FilesystemEvent data; - data.type = FilesystemEventType::Close; - data.durationNs = static_cast(duration.to_nanoseconds()); - data.data.close.fd = fd; - - if (result.is_error()) { - data.result.is_error = true; - data.result.value = result.error().code(); - } else { - data.result.is_error = false; - data.result.value = 0; - } - - auto maybe_path_index = get_path_index(fds(), fd, event_buffer); - if (maybe_path_index.is_error()) - return result; - - data.data.close.filename_index = maybe_path_index.value(); - - (void)event_buffer->append(PERF_EVENT_FILESYSTEM, 0, 0, {}, Thread::current(), data); - - return result; -} - -ErrorOr Process::sys$readv(int fd, Userspace iov, int iov_count) -{ - Optional start_timestamp = {}; - - bool const profiling_enabled_at_entry = !Thread::current()->is_profiling_suppressed(); - if (profiling_enabled_at_entry) { - start_timestamp = TimeManagement::the().monotonic_time(TimePrecision::Precise); - } - - auto result = readv_impl(fd, iov, iov_count); - - if (!profiling_enabled_at_entry || Thread::current()->is_profiling_suppressed()) - return result; - - if (Thread::current()->is_profiling_suppressed()) - return result; - - auto* event_buffer = current_perf_events_buffer(); - if (event_buffer == nullptr) - return result; - - auto const end_timestamp = TimeManagement::the().monotonic_time(TimePrecision::Precise); - auto const duration = end_timestamp - start_timestamp.value(); - - FilesystemEvent data; - data.type = FilesystemEventType::Readv; - data.durationNs = static_cast(duration.to_nanoseconds()); - data.data.readv.fd = fd; - - if (result.is_error()) { - data.result.is_error = true; - data.result.value = result.error().code(); - } else { - data.result.is_error = false; - data.result.value = 0; - } - - auto maybe_path_index = get_path_index(fds(), fd, event_buffer); - if (maybe_path_index.is_error()) - return result; - - data.data.readv.filename_index = maybe_path_index.value(); - - (void)event_buffer->append(PERF_EVENT_FILESYSTEM, 0, 0, {}, Thread::current(), data); - - return result; -} - -ErrorOr Process::sys$read(int fd, Userspace buffer, size_t size) -{ - Optional start_timestamp = {}; - - bool const profiling_enabled_at_entry = !Thread::current()->is_profiling_suppressed(); - if (profiling_enabled_at_entry) { - start_timestamp = TimeManagement::the().monotonic_time(TimePrecision::Precise); - } - - auto result = read_impl(fd, buffer, size); - - if (!profiling_enabled_at_entry || Thread::current()->is_profiling_suppressed()) - return result; - - auto* event_buffer = current_perf_events_buffer(); - if (event_buffer == nullptr) - return result; - - auto const end_timestamp = TimeManagement::the().monotonic_time(TimePrecision::Precise); - auto const duration = end_timestamp - start_timestamp.value(); - - FilesystemEvent data; - data.type = FilesystemEventType::Read; - data.durationNs = static_cast(duration.to_nanoseconds()); - data.data.read.fd = fd; - - if (result.is_error()) { - data.result.is_error = true; - data.result.value = result.error().code(); - } else { - data.result.is_error = false; - data.result.value = 0; - } - - auto maybe_path_index = get_path_index(fds(), fd, event_buffer); - if (maybe_path_index.is_error()) - return result; - - data.data.read.filename_index = maybe_path_index.value(); - - (void)event_buffer->append(PERF_EVENT_FILESYSTEM, 0, 0, {}, Thread::current(), data); - - return result; -} - -ErrorOr Process::sys$pread(int fd, Userspace buffer, size_t size, off_t userspace_offset) -{ - Optional start_timestamp = {}; - - bool const profiling_enabled_at_entry = !Thread::current()->is_profiling_suppressed(); - if (profiling_enabled_at_entry) { - start_timestamp = TimeManagement::the().monotonic_time(TimePrecision::Precise); - } - - auto result = pread_impl(fd, buffer, size, userspace_offset); - - if (!profiling_enabled_at_entry || Thread::current()->is_profiling_suppressed()) - return result; - - auto* event_buffer = current_perf_events_buffer(); - if (event_buffer == nullptr) - return result; - - auto const end_timestamp = TimeManagement::the().monotonic_time(TimePrecision::Precise); - auto const duration = end_timestamp - start_timestamp.value(); - - FilesystemEvent data; - data.type = FilesystemEventType::Pread; - data.durationNs = static_cast(duration.to_nanoseconds()); - data.data.pread.fd = fd; - data.data.pread.buffer_ptr = buffer.ptr(); - data.data.pread.size = size; - data.data.pread.offset = userspace_offset; - - if (result.is_error()) { - data.result.is_error = true; - data.result.value = result.error().code(); - } else { - data.result.is_error = false; - data.result.value = 0; - } - - auto maybe_path_index = get_path_index(fds(), fd, event_buffer); - if (maybe_path_index.is_error()) - return result; - - data.data.pread.filename_index = maybe_path_index.value(); - - (void)event_buffer->append(PERF_EVENT_FILESYSTEM, 0, 0, {}, Thread::current(), data); - - return result; -} - -} diff --git a/Kernel/Syscalls/profiling.cpp b/Kernel/Syscalls/profiling.cpp deleted file mode 100644 index f6e0c999a66..00000000000 --- a/Kernel/Syscalls/profiling.cpp +++ /dev/null @@ -1,153 +0,0 @@ -/* - * Copyright (c) 2018-2021, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include - -namespace Kernel { - -bool g_profiling_all_threads; -PerformanceEventBuffer* g_global_perf_events; -u64 g_profiling_event_mask; - -ErrorOr Process::sys$profiling_enable(pid_t pid, u64 event_mask) -{ - VERIFY_PROCESS_BIG_LOCK_ACQUIRED(this); - TRY(require_no_promises()); - - return profiling_enable(pid, event_mask); -} - -// NOTE: This second entrypoint exists to allow the kernel to invoke the syscall to enable boot profiling. -ErrorOr Process::profiling_enable(pid_t pid, u64 event_mask) -{ - VERIFY_PROCESS_BIG_LOCK_ACQUIRED(this); - - if (pid == -1) { - auto credentials = this->credentials(); - if (!credentials->is_superuser()) - return EPERM; - ScopedCritical critical; - g_profiling_event_mask = PERF_EVENT_PROCESS_CREATE | PERF_EVENT_THREAD_CREATE | PERF_EVENT_MMAP; - if (g_global_perf_events) { - g_global_perf_events->clear(); - } else { - g_global_perf_events = PerformanceEventBuffer::try_create_with_size(32 * MiB).leak_ptr(); - if (!g_global_perf_events) { - g_profiling_event_mask = 0; - return ENOMEM; - } - } - - SpinlockLocker lock(g_profiling_lock); - if (!TimeManagement::the().enable_profile_timer()) - return ENOTSUP; - g_profiling_all_threads = true; - PerformanceManager::add_process_created_event(*Scheduler::colonel()); - TRY(Process::for_each_in_same_jail([](auto& process) -> ErrorOr { - PerformanceManager::add_process_created_event(process); - return {}; - })); - g_profiling_event_mask = event_mask; - return 0; - } - - auto process = Process::from_pid_in_same_jail(pid); - if (!process) - return ESRCH; - if (process->is_dead()) - return ESRCH; - auto credentials = this->credentials(); - auto profile_process_credentials = process->credentials(); - if (!credentials->is_superuser() && profile_process_credentials->uid() != credentials->euid()) - return EPERM; - SpinlockLocker lock(g_profiling_lock); - g_profiling_event_mask = PERF_EVENT_PROCESS_CREATE | PERF_EVENT_THREAD_CREATE | PERF_EVENT_MMAP; - process->set_profiling(true); - if (!process->create_perf_events_buffer_if_needed()) { - process->set_profiling(false); - return ENOMEM; - } - g_profiling_event_mask = event_mask; - if (!TimeManagement::the().enable_profile_timer()) { - process->set_profiling(false); - return ENOTSUP; - } - return 0; -} - -ErrorOr Process::sys$profiling_disable(pid_t pid) -{ - VERIFY_PROCESS_BIG_LOCK_ACQUIRED(this); - TRY(require_no_promises()); - - if (pid == -1) { - auto credentials = this->credentials(); - if (!credentials->is_superuser()) - return EPERM; - ScopedCritical critical; - if (!TimeManagement::the().disable_profile_timer()) - return ENOTSUP; - g_profiling_all_threads = false; - return 0; - } - - auto process = Process::from_pid_in_same_jail(pid); - if (!process) - return ESRCH; - auto credentials = this->credentials(); - auto profile_process_credentials = process->credentials(); - if (!credentials->is_superuser() && profile_process_credentials->uid() != credentials->euid()) - return EPERM; - SpinlockLocker lock(g_profiling_lock); - if (!process->is_profiling()) - return EINVAL; - // FIXME: If we enabled the profile timer and it's not supported, how do we disable it now? - if (!TimeManagement::the().disable_profile_timer()) - return ENOTSUP; - process->set_profiling(false); - return 0; -} - -ErrorOr Process::sys$profiling_free_buffer(pid_t pid) -{ - VERIFY_PROCESS_BIG_LOCK_ACQUIRED(this); - TRY(require_no_promises()); - - if (pid == -1) { - auto credentials = this->credentials(); - if (!credentials->is_superuser()) - return EPERM; - - OwnPtr perf_events; - - { - ScopedCritical critical; - - perf_events = adopt_own_if_nonnull(g_global_perf_events); - g_global_perf_events = nullptr; - } - - return 0; - } - - auto process = Process::from_pid_in_same_jail(pid); - if (!process) - return ESRCH; - auto credentials = this->credentials(); - auto profile_process_credentials = process->credentials(); - if (!credentials->is_superuser() && profile_process_credentials->uid() != credentials->euid()) - return EPERM; - SpinlockLocker lock(g_profiling_lock); - if (process->is_profiling()) - return EINVAL; - process->delete_perf_events_buffer(); - return 0; -} -} diff --git a/Kernel/Syscalls/ptrace.cpp b/Kernel/Syscalls/ptrace.cpp deleted file mode 100644 index 6009a607e75..00000000000 --- a/Kernel/Syscalls/ptrace.cpp +++ /dev/null @@ -1,305 +0,0 @@ -/* - * Copyright (c) 2020, Itamar S. - * Copyright (c) 2020-2021, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -namespace Kernel { - -static ErrorOr handle_ptrace(Kernel::Syscall::SC_ptrace_params const& params, Process& caller) -{ - if (params.request == PT_TRACE_ME) { - if (Process::current().tracer()) - return EBUSY; - - caller.set_wait_for_tracer_at_next_execve(true); - return 0; - } - - // FIXME: PID/TID BUG - // This bug allows to request PT_ATTACH (or anything else) the same process, as - // long it is not the main thread. Alternatively, if this is desired, then the - // bug is that this prevents PT_ATTACH to the main thread from another thread. - if (params.tid == caller.pid().value()) - return EINVAL; - - auto peer = Thread::from_tid_in_same_jail(params.tid); - if (!peer) - return ESRCH; - - MutexLocker ptrace_locker(peer->process().ptrace_lock()); - SpinlockLocker scheduler_lock(g_scheduler_lock); - - auto peer_credentials = peer->process().credentials(); - auto caller_credentials = caller.credentials(); - if (!caller_credentials->is_superuser() && ((peer_credentials->uid() != caller_credentials->euid()) || (peer_credentials->uid() != peer_credentials->euid()))) // Disallow tracing setuid processes - return EACCES; - - if (!peer->process().is_dumpable()) - return EACCES; - - auto& peer_process = peer->process(); - if (params.request == PT_ATTACH) { - if (peer_process.tracer()) { - return EBUSY; - } - TRY(peer_process.start_tracing_from(caller.pid())); - SpinlockLocker lock(peer->get_lock()); - if (peer->state() == Thread::State::Stopped) { - peer_process.tracer()->set_regs(peer->get_register_dump_from_stack()); - } else { - peer->send_signal(SIGSTOP, &caller); - } - return 0; - } - - auto* tracer = peer_process.tracer(); - - if (!tracer) - return EPERM; - - if (tracer->tracer_pid() != caller.pid()) - return EBUSY; - - if (peer->state() == Thread::State::Running) - return EBUSY; - - scheduler_lock.unlock(); - - switch (params.request) { - case PT_CONTINUE: - peer->send_signal(SIGCONT, &caller); - break; - - case PT_DETACH: - peer_process.stop_tracing(); - peer->send_signal(SIGCONT, &caller); - break; - - case PT_SYSCALL: - tracer->set_trace_syscalls(true); - peer->send_signal(SIGCONT, &caller); - break; - - case PT_GETREGS: { - if (!tracer->has_regs()) - return EINVAL; - auto* regs = reinterpret_cast(params.addr); - TRY(copy_to_user(regs, &tracer->regs())); - break; - } - - case PT_SETREGS: { - if (!tracer->has_regs()) - return EINVAL; - - PtraceRegisters regs {}; - TRY(copy_from_user(®s, (PtraceRegisters const*)params.addr)); - - auto& peer_saved_registers = peer->get_register_dump_from_stack(); - // Verify that the saved registers are in usermode context - if (peer_saved_registers.previous_mode() != ExecutionMode::User) - return EFAULT; - - tracer->set_regs(regs); - copy_ptrace_registers_into_kernel_registers(peer_saved_registers, regs); - break; - } - - case PT_PEEK: { - auto data = TRY(peer->process().peek_user_data(Userspace { (FlatPtr)params.addr })); - TRY(copy_to_user((FlatPtr*)params.data, &data)); - break; - } - - case PT_POKE: - TRY(peer->process().poke_user_data(Userspace { (FlatPtr)params.addr }, params.data)); - return 0; - - case PT_PEEKBUF: { - Kernel::Syscall::SC_ptrace_buf_params buf_params {}; - TRY(copy_from_user(&buf_params, reinterpret_cast(params.data))); - // This is a comparatively large allocation on the Kernel stack. - // However, we know that we're close to the root of the call stack, and the following calls shouldn't go too deep. - Array buf; - FlatPtr tracee_ptr = (FlatPtr)params.addr; - while (buf_params.buf.size > 0) { - size_t copy_this_iteration = min(buf.size(), buf_params.buf.size); - TRY(peer->process().peek_user_data(buf.span().slice(0, copy_this_iteration), Userspace { tracee_ptr })); - TRY(copy_to_user((void*)buf_params.buf.data, buf.data(), copy_this_iteration)); - tracee_ptr += copy_this_iteration; - buf_params.buf.data += copy_this_iteration; - buf_params.buf.size -= copy_this_iteration; - } - break; - } - - case PT_PEEKDEBUG: { - auto data = TRY(peer->peek_debug_register(reinterpret_cast(params.addr))); - TRY(copy_to_user((FlatPtr*)params.data, &data)); - break; - } - case PT_POKEDEBUG: - TRY(peer->poke_debug_register(reinterpret_cast(params.addr), params.data)); - return 0; - default: - return EINVAL; - } - - return 0; -} - -ErrorOr Process::sys$ptrace(Userspace user_params) -{ - VERIFY_PROCESS_BIG_LOCK_ACQUIRED(this); - TRY(require_promise(Pledge::ptrace)); - auto params = TRY(copy_typed_from_user(user_params)); - - return handle_ptrace(params, *this); -} - -/** - * "Does this process have a thread that is currently being traced by the provided process?" - */ -bool Process::has_tracee_thread(ProcessID tracer_pid) -{ - if (auto const* tracer = this->tracer()) - return tracer->tracer_pid() == tracer_pid; - return false; -} - -ErrorOr Process::peek_user_data(Userspace address) -{ - // This function can be called from the context of another - // process that called PT_PEEK - ScopedAddressSpaceSwitcher switcher(*this); - return TRY(copy_typed_from_user(address)); -} - -ErrorOr Process::peek_user_data(Span destination, Userspace address) -{ - // This function can be called from the context of another - // process that called PT_PEEKBUF - ScopedAddressSpaceSwitcher switcher(*this); - TRY(copy_from_user(destination.data(), address, destination.size())); - return {}; -} - -ErrorOr Process::poke_user_data(Userspace address, FlatPtr data) -{ - Memory::VirtualRange range = { address.vaddr(), sizeof(FlatPtr) }; - - return address_space().with([&](auto& space) -> ErrorOr { - auto* region = space->find_region_containing(range); - if (!region) - return EFAULT; - ScopedAddressSpaceSwitcher switcher(*this); - if (region->is_shared()) { - // If the region is shared, we change its vmobject to a PrivateInodeVMObject - // to prevent the write operation from changing any shared inode data - VERIFY(region->vmobject().is_shared_inode()); - auto vmobject = TRY(Memory::PrivateInodeVMObject::try_create_with_inode(static_cast(region->vmobject()).inode())); - region->set_vmobject(move(vmobject)); - region->set_shared(false); - } - bool const was_writable = region->is_writable(); - if (!was_writable) { - region->set_writable(true); - region->remap(); - } - ScopeGuard rollback([&]() { - if (!was_writable) { - region->set_writable(false); - region->remap(); - } - }); - - return copy_to_user(address, &data); - }); -} - -ErrorOr Thread::peek_debug_register(u32 register_index) -{ -#if ARCH(X86_64) - FlatPtr data; - switch (register_index) { - case 0: - data = m_debug_register_state.dr0; - break; - case 1: - data = m_debug_register_state.dr1; - break; - case 2: - data = m_debug_register_state.dr2; - break; - case 3: - data = m_debug_register_state.dr3; - break; - case 6: - data = m_debug_register_state.dr6; - break; - case 7: - data = m_debug_register_state.dr7; - break; - default: - return EINVAL; - } - return data; -#elif ARCH(AARCH64) - (void)register_index; - TODO_AARCH64(); -#elif ARCH(RISCV64) - (void)register_index; - TODO_RISCV64(); -#else -# error "Unknown architecture" -#endif -} - -ErrorOr Thread::poke_debug_register(u32 register_index, FlatPtr data) -{ -#if ARCH(X86_64) - switch (register_index) { - case 0: - m_debug_register_state.dr0 = data; - break; - case 1: - m_debug_register_state.dr1 = data; - break; - case 2: - m_debug_register_state.dr2 = data; - break; - case 3: - m_debug_register_state.dr3 = data; - break; - case 7: - m_debug_register_state.dr7 = data; - break; - default: - return EINVAL; - } - return {}; -#elif ARCH(AARCH64) - (void)register_index; - (void)data; - TODO_AARCH64(); -#elif ARCH(RISCV64) - (void)register_index; - (void)data; - TODO_RISCV64(); -#else -# error "Unknown architecture" -#endif -} - -} diff --git a/Kernel/Syscalls/purge.cpp b/Kernel/Syscalls/purge.cpp deleted file mode 100644 index 6659e2655f9..00000000000 --- a/Kernel/Syscalls/purge.cpp +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include - -namespace Kernel { - -ErrorOr Process::sys$purge(int mode) -{ - VERIFY_PROCESS_BIG_LOCK_ACQUIRED(this); - TRY(require_no_promises()); - auto credentials = this->credentials(); - if (!credentials->is_superuser()) - return EPERM; - size_t purged_page_count = 0; - if (mode & PURGE_ALL_VOLATILE) { - Vector> vmobjects; - { - ErrorOr result; - Memory::MemoryManager::for_each_vmobject([&](auto& vmobject) { - if (vmobject.is_anonymous()) { - // In the event that the append fails, only attempt to continue - // the purge if we have already appended something successfully. - if (auto append_result = vmobjects.try_append(static_cast(vmobject)); append_result.is_error() && vmobjects.is_empty()) { - result = append_result.release_error(); - return IterationDecision::Break; - } - } - return IterationDecision::Continue; - }); - - if (result.is_error()) - return result.release_error(); - } - for (auto& vmobject : vmobjects) { - purged_page_count += vmobject->purge(); - } - } - if (mode & PURGE_ALL_CLEAN_INODE) { - Vector> vmobjects; - { - ErrorOr result; - Memory::MemoryManager::for_each_vmobject([&](auto& vmobject) { - if (vmobject.is_inode()) { - // In the event that the append fails, only attempt to continue - // the purge if we have already appended something successfully. - if (auto append_result = vmobjects.try_append(static_cast(vmobject)); append_result.is_error() && vmobjects.is_empty()) { - result = append_result.release_error(); - return IterationDecision::Break; - } - } - return IterationDecision::Continue; - }); - - if (result.is_error()) - return result.release_error(); - } - for (auto& vmobject : vmobjects) { - purged_page_count += vmobject->release_all_clean_pages(); - } - } - return purged_page_count; -} - -} diff --git a/Kernel/Syscalls/read.cpp b/Kernel/Syscalls/read.cpp deleted file mode 100644 index de602c9d90c..00000000000 --- a/Kernel/Syscalls/read.cpp +++ /dev/null @@ -1,109 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include - -namespace Kernel { - -using BlockFlags = Thread::FileBlocker::BlockFlags; - -ErrorOr> open_readable_file_description(auto& fds, int fd) -{ - auto description = TRY(fds.with_shared([&](auto& fds) { return fds.open_file_description(fd); })); - if (!description->is_readable()) - return EBADF; - if (description->is_directory()) - return EISDIR; - return description; -} - -static ErrorOr check_blocked_read(OpenFileDescription* description) -{ - if (description->is_blocking()) { - if (!description->can_read()) { - auto unblock_flags = BlockFlags::None; - if (Thread::current()->block({}, *description, unblock_flags).was_interrupted()) - return EINTR; - if (!has_flag(unblock_flags, BlockFlags::Read)) - return EAGAIN; - // TODO: handle exceptions in unblock_flags - } - } - return {}; -} - -ErrorOr Process::readv_impl(int fd, Userspace iov, int iov_count) -{ - VERIFY_PROCESS_BIG_LOCK_ACQUIRED(this); - TRY(require_promise(Pledge::stdio)); - if (iov_count < 0) - return EINVAL; - - if (iov_count > IOV_MAX) - return EFAULT; - - u64 total_length = 0; - Vector vecs; - TRY(vecs.try_resize(iov_count)); - TRY(copy_n_from_user(vecs.data(), iov, iov_count)); - for (auto& vec : vecs) { - total_length += vec.iov_len; - if (total_length > NumericLimits::max()) - return EINVAL; - } - - auto description = TRY(open_readable_file_description(fds(), fd)); - - int nread = 0; - for (auto& vec : vecs) { - TRY(check_blocked_read(description)); - auto buffer = TRY(UserOrKernelBuffer::for_user_buffer((u8*)vec.iov_base, vec.iov_len)); - auto nread_here = TRY(description->read(buffer, vec.iov_len)); - nread += nread_here; - } - - return nread; -} - -ErrorOr Process::read_impl(int fd, Userspace buffer, size_t size) -{ - VERIFY_PROCESS_BIG_LOCK_ACQUIRED(this); - TRY(require_promise(Pledge::stdio)); - if (size == 0) - return 0; - if (size > NumericLimits::max()) - return EINVAL; - dbgln_if(IO_DEBUG, "sys$read({}, {}, {})", fd, buffer.ptr(), size); - auto description = TRY(open_readable_file_description(fds(), fd)); - - TRY(check_blocked_read(description)); - auto user_buffer = TRY(UserOrKernelBuffer::for_user_buffer(buffer, size)); - return TRY(description->read(user_buffer, size)); -} - -ErrorOr Process::pread_impl(int fd, Userspace buffer, size_t size, off_t offset) -{ - VERIFY_PROCESS_BIG_LOCK_ACQUIRED(this); - TRY(require_promise(Pledge::stdio)); - if (size == 0) - return 0; - if (size > NumericLimits::max()) - return EINVAL; - if (offset < 0) - return EINVAL; - dbgln_if(IO_DEBUG, "sys$pread({}, {}, {}, {})", fd, buffer.ptr(), size, offset); - auto description = TRY(open_readable_file_description(fds(), fd)); - if (!description->file().is_seekable()) - return EINVAL; - TRY(check_blocked_read(description)); - auto user_buffer = TRY(UserOrKernelBuffer::for_user_buffer(buffer, size)); - return TRY(description->read(user_buffer, offset, size)); -} - -} diff --git a/Kernel/Syscalls/readlink.cpp b/Kernel/Syscalls/readlink.cpp deleted file mode 100644 index 96450f47ccc..00000000000 --- a/Kernel/Syscalls/readlink.cpp +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include - -namespace Kernel { - -ErrorOr Process::sys$readlink(Userspace user_params) -{ - VERIFY_NO_PROCESS_BIG_LOCK(this); - TRY(require_promise(Pledge::rpath)); - auto params = TRY(copy_typed_from_user(user_params)); - - auto path = TRY(get_syscall_path_argument(params.path)); - auto description = TRY(VirtualFileSystem::the().open(credentials(), path->view(), O_RDONLY | O_NOFOLLOW_NOERROR, 0, TRY(custody_for_dirfd(params.dirfd)))); - - if (!description->metadata().is_symlink()) - return EINVAL; - - // Make sure that our assumptions about the path length hold up. - // Note that this doesn't mean that the reported size can be trusted, some inodes just report zero. - VERIFY(description->inode()->size() <= MAXPATHLEN); - - Array link_target; - auto read_bytes = TRY(description->inode()->read_until_filled_or_end(0, link_target.size(), UserOrKernelBuffer::for_kernel_buffer(link_target.data()), description)); - auto size_to_copy = min(read_bytes, params.buffer.size); - TRY(copy_to_user(params.buffer.data, link_target.data(), size_to_copy)); - // Note: we return the whole size here, not the copied size. - return read_bytes; -} - -} diff --git a/Kernel/Syscalls/realpath.cpp b/Kernel/Syscalls/realpath.cpp deleted file mode 100644 index cc566030d0c..00000000000 --- a/Kernel/Syscalls/realpath.cpp +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include - -namespace Kernel { - -ErrorOr Process::sys$realpath(Userspace user_params) -{ - VERIFY_NO_PROCESS_BIG_LOCK(this); - TRY(require_promise(Pledge::rpath)); - auto params = TRY(copy_typed_from_user(user_params)); - - auto path = TRY(get_syscall_path_argument(params.path)); - auto custody = TRY(VirtualFileSystem::the().resolve_path(credentials(), path->view(), current_directory())); - auto absolute_path = TRY(custody->try_serialize_absolute_path()); - - size_t ideal_size = absolute_path->length() + 1; - auto size_to_copy = min(ideal_size, params.buffer.size); - TRY(copy_to_user(params.buffer.data, absolute_path->characters(), size_to_copy)); - // Note: we return the whole size here, not the copied size. - return ideal_size; -} - -} diff --git a/Kernel/Syscalls/rename.cpp b/Kernel/Syscalls/rename.cpp deleted file mode 100644 index e9426b80375..00000000000 --- a/Kernel/Syscalls/rename.cpp +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include - -namespace Kernel { - -ErrorOr Process::sys$rename(Userspace user_params) -{ - VERIFY_NO_PROCESS_BIG_LOCK(this); - TRY(require_promise(Pledge::cpath)); - auto params = TRY(copy_typed_from_user(user_params)); - auto old_path = TRY(get_syscall_path_argument(params.old_path)); - auto new_path = TRY(get_syscall_path_argument(params.new_path)); - CustodyBase old_base(params.olddirfd, old_path->view()); - CustodyBase new_base(params.newdirfd, new_path->view()); - TRY(VirtualFileSystem::the().rename(credentials(), old_base, old_path->view(), new_base, new_path->view())); - return 0; -} - -} diff --git a/Kernel/Syscalls/resource.cpp b/Kernel/Syscalls/resource.cpp deleted file mode 100644 index 1e3f3c4fa3e..00000000000 --- a/Kernel/Syscalls/resource.cpp +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright (c) 2022, Lucas Chollet - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include - -namespace Kernel { - -ErrorOr Process::sys$getrusage(int who, Userspace user_usage) -{ - VERIFY_NO_PROCESS_BIG_LOCK(this); - - rusage usage {}; - - auto const ticks_per_second = TimeManagement::the().ticks_per_second(); - - switch (who) { - case RUSAGE_SELF: - usage.ru_utime = Duration::from_ticks(m_ticks_in_user, ticks_per_second).to_timeval(); - usage.ru_stime = Duration::from_ticks(m_ticks_in_kernel, ticks_per_second).to_timeval(); - break; - case RUSAGE_CHILDREN: - usage.ru_utime = Duration::from_ticks(m_ticks_in_user_for_dead_children, ticks_per_second).to_timeval(); - usage.ru_stime = Duration::from_ticks(m_ticks_in_kernel_for_dead_children, ticks_per_second).to_timeval(); - break; - default: - return EINVAL; - } - - TRY(copy_to_user(user_usage, &usage)); - - return 0; -} - -} diff --git a/Kernel/Syscalls/rmdir.cpp b/Kernel/Syscalls/rmdir.cpp deleted file mode 100644 index adbc35c0d95..00000000000 --- a/Kernel/Syscalls/rmdir.cpp +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include - -namespace Kernel { - -ErrorOr Process::sys$rmdir(Userspace user_path, size_t path_length) -{ - VERIFY_NO_PROCESS_BIG_LOCK(this); - TRY(require_promise(Pledge::cpath)); - auto path = TRY(get_syscall_path_argument(user_path, path_length)); - TRY(VirtualFileSystem::the().rmdir(credentials(), path->view(), current_directory())); - return 0; -} - -} diff --git a/Kernel/Syscalls/sched.cpp b/Kernel/Syscalls/sched.cpp deleted file mode 100644 index 62cc1b7b26d..00000000000 --- a/Kernel/Syscalls/sched.cpp +++ /dev/null @@ -1,117 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * Copyright (c) 2022, kleines Filmröllchen - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include - -namespace Kernel { - -ErrorOr Process::sys$yield() -{ - VERIFY_NO_PROCESS_BIG_LOCK(this); - TRY(require_promise(Pledge::stdio)); - Thread::current()->yield_without_releasing_big_lock(); - return 0; -} - -ErrorOr> Process::get_thread_from_pid_or_tid(pid_t pid_or_tid, Syscall::SchedulerParametersMode mode) -{ - VERIFY(g_scheduler_lock.is_locked_by_current_processor()); - Thread* peer; - switch (mode) { - case Syscall::SchedulerParametersMode::Thread: { - peer = Thread::current(); - if (pid_or_tid != 0) - peer = Thread::from_tid_in_same_jail(pid_or_tid); - - // Only superuser can access other processes' threads. - if (!credentials()->is_superuser() && peer && &peer->process() != this) - return EPERM; - - break; - } - case Syscall::SchedulerParametersMode::Process: { - auto* searched_process = this; - if (pid_or_tid != 0) - searched_process = Process::from_pid_in_same_jail(pid_or_tid); - - if (searched_process == nullptr) - return ESRCH; - auto pid = searched_process->pid().value(); - // Main thread has tid == pid - this->thread_list().for_each([&](auto& thread) { - if (thread.tid().value() == pid) - peer = &thread; - }); - break; - } - default: - VERIFY_NOT_REACHED(); - } - if (!peer) - return ESRCH; - return NonnullRefPtr { *peer }; -} - -ErrorOr Process::sys$scheduler_set_parameters(Userspace user_param) -{ - VERIFY_NO_PROCESS_BIG_LOCK(this); - TRY(require_promise(Pledge::proc)); - auto parameters = TRY(copy_typed_from_user(user_param)); - - if (parameters.parameters.sched_priority < THREAD_PRIORITY_MIN || parameters.parameters.sched_priority > THREAD_PRIORITY_MAX) - return EINVAL; - - SpinlockLocker lock(g_scheduler_lock); - auto peer = TRY(get_thread_from_pid_or_tid(parameters.pid_or_tid, parameters.mode)); - - auto credentials = this->credentials(); - auto peer_credentials = peer->process().credentials(); - if (!credentials->is_superuser() && credentials->euid() != peer_credentials->uid() && credentials->uid() != peer_credentials->uid()) - return EPERM; - - peer->set_priority((u32)parameters.parameters.sched_priority); - // POSIX says that process scheduling parameters have precedence over thread scheduling parameters. - // We don't track them separately, so overwrite the thread scheduling settings manually for now. - if (parameters.mode == Syscall::SchedulerParametersMode::Process) { - peer->process().for_each_thread([&](auto& thread) { - thread.set_priority((u32)parameters.parameters.sched_priority); - }); - } - - return 0; -} - -ErrorOr Process::sys$scheduler_get_parameters(Userspace user_param) -{ - VERIFY_NO_PROCESS_BIG_LOCK(this); - TRY(require_promise(Pledge::proc)); - - Syscall::SC_scheduler_parameters_params parameters; - TRY(copy_from_user(¶meters, user_param)); - - int priority; - { - SpinlockLocker lock(g_scheduler_lock); - auto peer = TRY(get_thread_from_pid_or_tid(parameters.pid_or_tid, parameters.mode)); - - auto credentials = this->credentials(); - auto peer_credentials = peer->process().credentials(); - if (!credentials->is_superuser() && credentials->euid() != peer_credentials->uid() && credentials->uid() != peer_credentials->uid()) - return EPERM; - - priority = (int)peer->priority(); - } - - parameters.parameters.sched_priority = priority; - - TRY(copy_to_user(user_param, ¶meters)); - return 0; -} - -} diff --git a/Kernel/Syscalls/sendfd.cpp b/Kernel/Syscalls/sendfd.cpp deleted file mode 100644 index 78e71f8eb85..00000000000 --- a/Kernel/Syscalls/sendfd.cpp +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include - -namespace Kernel { - -ErrorOr Process::sys$sendfd(int sockfd, int fd) -{ - VERIFY_NO_PROCESS_BIG_LOCK(this); - TRY(require_promise(Pledge::sendfd)); - auto socket_description = TRY(open_file_description(sockfd)); - if (!socket_description->is_socket()) - return ENOTSOCK; - auto& socket = *socket_description->socket(); - if (!socket.is_local()) - return EAFNOSUPPORT; - if (!socket.is_connected()) - return ENOTCONN; - - auto passing_description = TRY(open_file_description(fd)); - auto& local_socket = static_cast(socket); - TRY(local_socket.sendfd(*socket_description, move(passing_description))); - return 0; -} - -ErrorOr Process::sys$recvfd(int sockfd, int options) -{ - VERIFY_NO_PROCESS_BIG_LOCK(this); - TRY(require_promise(Pledge::recvfd)); - auto socket_description = TRY(open_file_description(sockfd)); - if (!socket_description->is_socket()) - return ENOTSOCK; - auto& socket = *socket_description->socket(); - if (!socket.is_local()) - return EAFNOSUPPORT; - - auto fd_allocation = TRY(m_fds.with_exclusive([](auto& fds) { return fds.allocate(); })); - - auto& local_socket = static_cast(socket); - auto received_description = TRY(local_socket.recvfd(*socket_description)); - - u32 fd_flags = 0; - if (options & O_CLOEXEC) - fd_flags |= FD_CLOEXEC; - - m_fds.with_exclusive([&](auto& fds) { fds[fd_allocation.fd].set(move(received_description), fd_flags); }); - return fd_allocation.fd; -} - -} diff --git a/Kernel/Syscalls/setpgid.cpp b/Kernel/Syscalls/setpgid.cpp deleted file mode 100644 index 0517d5d44b2..00000000000 --- a/Kernel/Syscalls/setpgid.cpp +++ /dev/null @@ -1,173 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include - -namespace Kernel { - -ErrorOr Process::sys$getsid(pid_t pid) -{ - VERIFY_NO_PROCESS_BIG_LOCK(this); - TRY(require_promise(Pledge::stdio)); - if (pid == 0 || pid == this->pid()) - return sid().value(); - auto peer = Process::from_pid_in_same_jail(pid); - if (!peer) - return ESRCH; - auto peer_sid = peer->sid(); - if (sid() != peer_sid) - return EPERM; - return peer_sid.value(); -} - -ErrorOr Process::sys$setsid() -{ - VERIFY_NO_PROCESS_BIG_LOCK(this); - TRY(require_promise(Pledge::proc)); - - // NOTE: ProcessGroup::create_if_unused_pgid() will fail with EPERM - // if a process group with the same PGID already exists. - auto process_group = TRY(ProcessGroup::create_if_unused_pgid(ProcessGroupID(pid().value()))); - - auto new_sid = SessionID(pid().value()); - auto credentials = this->credentials(); - auto new_credentials = TRY(Credentials::create( - credentials->uid(), - credentials->gid(), - credentials->euid(), - credentials->egid(), - credentials->suid(), - credentials->sgid(), - credentials->extra_gids(), - new_sid, - credentials->pgid())); - - with_mutable_protected_data([&](auto& protected_data) { - protected_data.tty = nullptr; - protected_data.process_group = move(process_group); - protected_data.credentials = move(new_credentials); - }); - return new_sid.value(); -} - -ErrorOr Process::sys$getpgid(pid_t pid) -{ - VERIFY_NO_PROCESS_BIG_LOCK(this); - TRY(require_promise(Pledge::stdio)); - if (pid == 0) - return pgid().value(); - auto process = Process::from_pid_in_same_jail(pid); - if (!process) - return ESRCH; - return process->pgid().value(); -} - -ErrorOr Process::sys$getpgrp() -{ - VERIFY_NO_PROCESS_BIG_LOCK(this); - TRY(require_promise(Pledge::stdio)); - return pgid().value(); -} - -SessionID Process::get_sid_from_pgid(ProcessGroupID pgid) -{ - // FIXME: This xor sys$setsid() uses the wrong locking mechanism. - - SessionID sid { -1 }; - MUST(Process::current().for_each_in_pgrp_in_same_jail(pgid, [&](auto& process) -> ErrorOr { - sid = process.sid(); - return {}; - })); - - return sid; -} - -ErrorOr Process::sys$setpgid(pid_t specified_pid, pid_t specified_pgid) -{ - VERIFY_NO_PROCESS_BIG_LOCK(this); - TRY(require_promise(Pledge::proc)); - ProcessID pid = specified_pid ? ProcessID(specified_pid) : this->pid(); - if (specified_pgid < 0) { - // The value of the pgid argument is less than 0, or is not a value supported by the implementation. - return EINVAL; - } - auto process = Process::from_pid_in_same_jail(pid); - if (!process) - return ESRCH; - if (process != this && process->ppid() != this->pid()) { - // The value of the pid argument does not match the process ID - // of the calling process or of a child process of the calling process. - return ESRCH; - } - if (process->is_session_leader()) { - // The process indicated by the pid argument is a session leader. - return EPERM; - } - if (process->ppid() == this->pid() && process->sid() != sid()) { - // The value of the pid argument matches the process ID of a child - // process of the calling process and the child process is not in - // the same session as the calling process. - return EPERM; - } - - ProcessGroupID new_pgid = specified_pgid ? ProcessGroupID(specified_pgid) : process->pid().value(); - SessionID current_sid = sid(); - SessionID new_sid = get_sid_from_pgid(new_pgid); - if (new_sid != -1 && current_sid != new_sid) { - // Can't move a process between sessions. - return EPERM; - } - if (new_sid == -1 && new_pgid != process->pid().value()) { - // The value of the pgid argument is valid, but is not - // the calling pid, and is not an existing process group. - return EPERM; - } - // FIXME: There are more EPERM conditions to check for here.. - auto process_group = TRY(ProcessGroup::find_or_create(new_pgid)); - return process->with_mutable_protected_data([&process, &process_group, new_pgid](auto& protected_data) -> ErrorOr { - auto credentials = process->credentials(); - - auto new_credentials = TRY(Credentials::create( - credentials->uid(), - credentials->gid(), - credentials->euid(), - credentials->egid(), - credentials->suid(), - credentials->sgid(), - credentials->extra_gids(), - credentials->sid(), - new_pgid)); - - protected_data.credentials = move(new_credentials); - protected_data.process_group = move(process_group); - return 0; - }); -} - -ErrorOr Process::sys$get_root_session_id(pid_t force_sid) -{ - TRY(require_promise(Pledge::stdio)); - pid_t sid = (force_sid == -1) ? this->sid().value() : force_sid; - if (sid == 0) - return 0; - while (true) { - auto sid_process = Process::from_pid_in_same_jail(sid); - if (!sid_process) - return ESRCH; - auto parent_pid = sid_process->ppid().value(); - auto parent_process = Process::from_pid_in_same_jail(parent_pid); - if (!parent_process) - return ESRCH; - pid_t parent_sid = parent_process->sid().value(); - if (parent_sid == 0) - break; - sid = parent_sid; - } - return sid; -} - -} diff --git a/Kernel/Syscalls/setuid.cpp b/Kernel/Syscalls/setuid.cpp deleted file mode 100644 index 34d65619508..00000000000 --- a/Kernel/Syscalls/setuid.cpp +++ /dev/null @@ -1,352 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include - -namespace Kernel { - -ErrorOr Process::sys$seteuid(UserID new_euid) -{ - VERIFY_NO_PROCESS_BIG_LOCK(this); - TRY(require_promise(Pledge::id)); - - if (new_euid == (uid_t)-1) - return EINVAL; - - return with_mutable_protected_data([&](auto& protected_data) -> ErrorOr { - auto credentials = this->credentials(); - - if (new_euid != credentials->uid() && new_euid != credentials->suid() && !credentials->is_superuser()) - return EPERM; - - auto new_credentials = TRY(Credentials::create( - credentials->uid(), - credentials->gid(), - new_euid, - credentials->egid(), - credentials->suid(), - credentials->sgid(), - credentials->extra_gids(), - credentials->sid(), - credentials->pgid())); - - if (credentials->euid() != new_euid) - protected_data.dumpable = false; - - protected_data.credentials = move(new_credentials); - return 0; - }); -} - -ErrorOr Process::sys$setegid(GroupID new_egid) -{ - VERIFY_NO_PROCESS_BIG_LOCK(this); - TRY(require_promise(Pledge::id)); - - if (new_egid == (uid_t)-1) - return EINVAL; - - return with_mutable_protected_data([&](auto& protected_data) -> ErrorOr { - auto credentials = this->credentials(); - - if (new_egid != credentials->gid() && new_egid != credentials->sgid() && !credentials->is_superuser()) - return EPERM; - - auto new_credentials = TRY(Credentials::create( - credentials->uid(), - credentials->gid(), - credentials->euid(), - new_egid, - credentials->suid(), - credentials->sgid(), - credentials->extra_gids(), - credentials->sid(), - credentials->pgid())); - - if (credentials->egid() != new_egid) - protected_data.dumpable = false; - - protected_data.credentials = move(new_credentials); - return 0; - }); -} - -ErrorOr Process::sys$setuid(UserID new_uid) -{ - VERIFY_NO_PROCESS_BIG_LOCK(this); - TRY(require_promise(Pledge::id)); - - if (new_uid == (uid_t)-1) - return EINVAL; - - return with_mutable_protected_data([&](auto& protected_data) -> ErrorOr { - auto credentials = this->credentials(); - - if (new_uid != credentials->uid() && new_uid != credentials->euid() && !credentials->is_superuser()) - return EPERM; - - auto new_credentials = TRY(Credentials::create( - new_uid, - credentials->gid(), - new_uid, - credentials->egid(), - new_uid, - credentials->sgid(), - credentials->extra_gids(), - credentials->sid(), - credentials->pgid())); - - if (credentials->euid() != new_uid) - protected_data.dumpable = false; - - protected_data.credentials = move(new_credentials); - return 0; - }); -} - -ErrorOr Process::sys$setgid(GroupID new_gid) -{ - VERIFY_NO_PROCESS_BIG_LOCK(this); - TRY(require_promise(Pledge::id)); - - if (new_gid == (uid_t)-1) - return EINVAL; - - return with_mutable_protected_data([&](auto& protected_data) -> ErrorOr { - auto credentials = this->credentials(); - - if (new_gid != credentials->gid() && new_gid != credentials->egid() && !credentials->is_superuser()) - return EPERM; - - auto new_credentials = TRY(Credentials::create( - credentials->uid(), - new_gid, - credentials->euid(), - new_gid, - credentials->suid(), - new_gid, - credentials->extra_gids(), - credentials->sid(), - credentials->pgid())); - - if (credentials->egid() != new_gid) - protected_data.dumpable = false; - - protected_data.credentials = move(new_credentials); - return 0; - }); -} - -ErrorOr Process::sys$setreuid(UserID new_ruid, UserID new_euid) -{ - VERIFY_NO_PROCESS_BIG_LOCK(this); - TRY(require_promise(Pledge::id)); - - return with_mutable_protected_data([&](auto& protected_data) -> ErrorOr { - auto credentials = this->credentials(); - - if (new_ruid == (uid_t)-1) - new_ruid = credentials->uid(); - if (new_euid == (uid_t)-1) - new_euid = credentials->euid(); - - auto ok = [&credentials](UserID id) { return id == credentials->uid() || id == credentials->euid() || id == credentials->suid(); }; - if (!ok(new_ruid) || !ok(new_euid)) - return EPERM; - - if (new_ruid < (uid_t)-1 || new_euid < (uid_t)-1) - return EINVAL; - - auto new_credentials = TRY(Credentials::create( - new_ruid, - credentials->gid(), - new_euid, - credentials->egid(), - credentials->suid(), - credentials->sgid(), - credentials->extra_gids(), - credentials->sid(), - credentials->pgid())); - - if (credentials->euid() != new_euid) - protected_data.dumpable = false; - - protected_data.credentials = move(new_credentials); - return 0; - }); -} - -ErrorOr Process::sys$setresuid(UserID new_ruid, UserID new_euid, UserID new_suid) -{ - VERIFY_NO_PROCESS_BIG_LOCK(this); - TRY(require_promise(Pledge::id)); - - return with_mutable_protected_data([&](auto& protected_data) -> ErrorOr { - auto credentials = this->credentials(); - - if (new_ruid == (uid_t)-1) - new_ruid = credentials->uid(); - if (new_euid == (uid_t)-1) - new_euid = credentials->euid(); - if (new_suid == (uid_t)-1) - new_suid = credentials->suid(); - - auto ok = [&credentials](UserID id) { return id == credentials->uid() || id == credentials->euid() || id == credentials->suid(); }; - if ((!ok(new_ruid) || !ok(new_euid) || !ok(new_suid)) && !credentials->is_superuser()) - return EPERM; - - auto new_credentials = TRY(Credentials::create( - new_ruid, - credentials->gid(), - new_euid, - credentials->egid(), - new_suid, - credentials->sgid(), - credentials->extra_gids(), - credentials->sid(), - credentials->pgid())); - - if (credentials->euid() != new_euid) - protected_data.dumpable = false; - - protected_data.credentials = move(new_credentials); - return 0; - }); -} - -ErrorOr Process::sys$setregid(GroupID new_rgid, GroupID new_egid) -{ - VERIFY_NO_PROCESS_BIG_LOCK(this); - TRY(require_promise(Pledge::id)); - - return with_mutable_protected_data([&](auto& protected_data) -> ErrorOr { - auto credentials = this->credentials(); - - if (new_rgid == (gid_t)-1) - new_rgid = credentials->gid(); - if (new_egid == (gid_t)-1) - new_egid = credentials->egid(); - - auto ok = [&credentials](GroupID id) { return id == credentials->gid() || id == credentials->egid() || id == credentials->sgid(); }; - if (!ok(new_rgid) || !ok(new_egid)) - return EPERM; - - auto new_credentials = TRY(Credentials::create( - credentials->uid(), - new_rgid, - credentials->euid(), - new_egid, - credentials->suid(), - credentials->sgid(), - credentials->extra_gids(), - credentials->sid(), - credentials->pgid())); - - if (credentials->egid() != new_egid) - protected_data.dumpable = false; - - protected_data.credentials = move(new_credentials); - return 0; - }); -} - -ErrorOr Process::sys$setresgid(GroupID new_rgid, GroupID new_egid, GroupID new_sgid) -{ - VERIFY_NO_PROCESS_BIG_LOCK(this); - TRY(require_promise(Pledge::id)); - - return with_mutable_protected_data([&](auto& protected_data) -> ErrorOr { - auto credentials = this->credentials(); - - if (new_rgid == (gid_t)-1) - new_rgid = credentials->gid(); - if (new_egid == (gid_t)-1) - new_egid = credentials->egid(); - if (new_sgid == (gid_t)-1) - new_sgid = credentials->sgid(); - - auto ok = [&credentials](GroupID id) { return id == credentials->gid() || id == credentials->egid() || id == credentials->sgid(); }; - if ((!ok(new_rgid) || !ok(new_egid) || !ok(new_sgid)) && !credentials->is_superuser()) - return EPERM; - - auto new_credentials = TRY(Credentials::create( - credentials->uid(), - new_rgid, - credentials->euid(), - new_egid, - credentials->suid(), - new_sgid, - credentials->extra_gids(), - credentials->sid(), - credentials->pgid())); - - if (credentials->egid() != new_egid) - protected_data.dumpable = false; - - protected_data.credentials = move(new_credentials); - return 0; - }); -} - -ErrorOr Process::sys$setgroups(size_t count, Userspace user_gids) -{ - VERIFY_NO_PROCESS_BIG_LOCK(this); - TRY(require_promise(Pledge::id)); - - if (count > NGROUPS_MAX) - return EINVAL; - - return with_mutable_protected_data([&](auto& protected_data) -> ErrorOr { - auto credentials = this->credentials(); - - if (!credentials->is_superuser()) - return EPERM; - - if (!count) { - protected_data.credentials = TRY(Credentials::create( - credentials->uid(), - credentials->gid(), - credentials->euid(), - credentials->egid(), - credentials->suid(), - credentials->sgid(), - {}, - credentials->sid(), - credentials->pgid())); - return 0; - } - - Vector new_extra_gids; - TRY(new_extra_gids.try_resize(count)); - TRY(copy_n_from_user(new_extra_gids.data(), user_gids, count)); - - HashTable unique_extra_gids; - for (auto& extra_gid : new_extra_gids) { - if (extra_gid != credentials->gid()) - TRY(unique_extra_gids.try_set(extra_gid)); - } - - new_extra_gids.clear_with_capacity(); - for (auto extra_gid : unique_extra_gids) { - TRY(new_extra_gids.try_append(extra_gid)); - } - - protected_data.credentials = TRY(Credentials::create( - credentials->uid(), - credentials->gid(), - credentials->euid(), - credentials->egid(), - credentials->suid(), - credentials->sgid(), - new_extra_gids.span(), - credentials->sid(), - credentials->pgid())); - return 0; - }); -} - -} diff --git a/Kernel/Syscalls/sigaction.cpp b/Kernel/Syscalls/sigaction.cpp deleted file mode 100644 index 23fd8b3bf0f..00000000000 --- a/Kernel/Syscalls/sigaction.cpp +++ /dev/null @@ -1,350 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * Copyright (c) 2021, Idan Horowitz - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include - -namespace Kernel { - -ErrorOr Process::sys$sigprocmask(int how, Userspace set, Userspace old_set) -{ - VERIFY_NO_PROCESS_BIG_LOCK(this); - TRY(require_promise(Pledge::sigaction)); - auto* current_thread = Thread::current(); - u32 previous_signal_mask; - if (set) { - auto set_value = TRY(copy_typed_from_user(set)); - switch (how) { - case SIG_BLOCK: - previous_signal_mask = current_thread->signal_mask_block(set_value, true); - break; - case SIG_UNBLOCK: - previous_signal_mask = current_thread->signal_mask_block(set_value, false); - break; - case SIG_SETMASK: - previous_signal_mask = current_thread->update_signal_mask(set_value); - break; - default: - return EINVAL; - } - } else { - previous_signal_mask = current_thread->signal_mask(); - } - if (old_set) { - TRY(copy_to_user(old_set, &previous_signal_mask)); - } - return 0; -} - -ErrorOr Process::sys$sigpending(Userspace set) -{ - VERIFY_NO_PROCESS_BIG_LOCK(this); - TRY(require_promise(Pledge::stdio)); - auto pending_signals = Thread::current()->pending_signals(); - TRY(copy_to_user(set, &pending_signals)); - return 0; -} - -ErrorOr Process::sys$sigaction(int signum, Userspace user_act, Userspace user_old_act) -{ - VERIFY_PROCESS_BIG_LOCK_ACQUIRED(this); - TRY(require_promise(Pledge::sigaction)); - if (signum < 1 || signum >= NSIG || signum == SIGKILL || signum == SIGSTOP) - return EINVAL; - - InterruptDisabler disabler; // FIXME: This should use a narrower lock. Maybe a way to ignore signals temporarily? - auto& action = m_signal_action_data[signum]; - if (user_old_act) { - sigaction old_act {}; - old_act.sa_flags = action.flags; - old_act.sa_sigaction = reinterpret_cast(action.handler_or_sigaction.as_ptr()); - old_act.sa_mask = action.mask; - TRY(copy_to_user(user_old_act, &old_act)); - } - if (user_act) { - auto act = TRY(copy_typed_from_user(user_act)); - action.mask = act.sa_mask; - action.flags = act.sa_flags; - action.handler_or_sigaction = VirtualAddress { reinterpret_cast(act.sa_sigaction) }; - } - return 0; -} - -ErrorOr Process::sys$sigreturn(RegisterState& registers) -{ - VERIFY_NO_PROCESS_BIG_LOCK(this); - TRY(require_promise(Pledge::stdio)); - SmapDisabler disabler; - - // Here, we restore the state pushed by dispatch signal and asm_signal_trampoline. - auto stack_ptr = registers.userspace_sp(); - - // Stack state (created by the signal trampoline): - // saved_ax, ucontext, signal_info, fpu_state?. - - // The FPU state is at the top here, pop it off and restore it. - // FIXME: The stack alignment is off by 8 bytes here, figure this out and remove this excessively aligned object. - alignas(alignof(FPUState) * 2) FPUState data {}; - TRY(copy_from_user(&data, bit_cast(stack_ptr))); - Thread::current()->fpu_state() = data; - stack_ptr += sizeof(FPUState); - - stack_ptr += sizeof(siginfo); // We don't need this here. - - auto ucontext = TRY(copy_typed_from_user<__ucontext>(stack_ptr)); - stack_ptr += sizeof(__ucontext); - - auto saved_ax = TRY(copy_typed_from_user(stack_ptr)); - - Thread::current()->m_signal_mask = ucontext.uc_sigmask; - Thread::current()->m_currently_handled_signal = 0; -#if ARCH(X86_64) - auto sp = registers.rsp; -#endif - - copy_ptrace_registers_into_kernel_registers(registers, static_cast(ucontext.uc_mcontext)); - -#if ARCH(X86_64) - registers.set_userspace_sp(registers.rsp); - registers.rsp = sp; -#endif - - return saved_ax; -} - -ErrorOr Process::remap_range_as_stack(FlatPtr address, size_t size) -{ - // FIXME: This duplicates a lot of logic from sys$mprotect, this should be abstracted out somehow - // NOTE: We shrink the given range to page boundaries (instead of expanding it), as sigaltstack's manpage suggests - // using malloc() to allocate the stack region, and many heap implementations (including ours) store heap chunk - // metadata in memory just before the vended pointer, which we would end up zeroing. - auto range_to_remap = TRY(Memory::shrink_range_to_page_boundaries(address, size)); - if (!range_to_remap.size()) - return EINVAL; - - if (!is_user_range(range_to_remap)) - return EFAULT; - - return address_space().with([&](auto& space) -> ErrorOr { - if (auto* whole_region = space->find_region_from_range(range_to_remap)) { - if (!whole_region->is_mmap()) - return EPERM; - if (!whole_region->vmobject().is_anonymous() || whole_region->is_shared()) - return EINVAL; - whole_region->unsafe_clear_access(); - whole_region->set_readable(true); - whole_region->set_writable(true); - whole_region->set_stack(true); - whole_region->set_syscall_region(false); - whole_region->clear_to_zero(); - whole_region->remap(); - - return range_to_remap; - } - - if (auto* old_region = space->find_region_containing(range_to_remap)) { - if (!old_region->is_mmap()) - return EPERM; - if (!old_region->vmobject().is_anonymous() || old_region->is_shared()) - return EINVAL; - - // Remove the old region from our regions tree, since were going to add another region - // with the exact same start address. - auto region = space->take_region(*old_region); - region->unmap(); - - // This vector is the region(s) adjacent to our range. - // We need to allocate a new region for the range we wanted to change permission bits on. - auto adjacent_regions = TRY(space->try_split_region_around_range(*region, range_to_remap)); - - size_t new_range_offset_in_vmobject = region->offset_in_vmobject() + (range_to_remap.base().get() - region->range().base().get()); - auto* new_region = TRY(space->try_allocate_split_region(*region, range_to_remap, new_range_offset_in_vmobject)); - new_region->unsafe_clear_access(); - new_region->set_readable(true); - new_region->set_writable(true); - new_region->set_stack(true); - new_region->set_syscall_region(false); - new_region->clear_to_zero(); - - // Map the new regions using our page directory (they were just allocated and don't have one). - for (auto* adjacent_region : adjacent_regions) { - TRY(adjacent_region->map(space->page_directory())); - } - TRY(new_region->map(space->page_directory())); - - return range_to_remap; - } - - if (auto const& regions = TRY(space->find_regions_intersecting(range_to_remap)); regions.size()) { - size_t full_size_found = 0; - // Check that all intersecting regions are compatible. - for (auto const* region : regions) { - if (!region->is_mmap()) - return EPERM; - if (!region->vmobject().is_anonymous() || region->is_shared()) - return EINVAL; - full_size_found += region->range().intersect(range_to_remap).size(); - } - - if (full_size_found != range_to_remap.size()) - return ENOMEM; - - // Finally, iterate over each region, either updating its access flags if the range covers it wholly, - // or carving out a new subregion with the appropriate access flags set. - for (auto* old_region : regions) { - auto const intersection_to_remap = range_to_remap.intersect(old_region->range()); - // If the region is completely covered by range, simply update the access flags - if (intersection_to_remap == old_region->range()) { - old_region->unsafe_clear_access(); - old_region->set_readable(true); - old_region->set_writable(true); - old_region->set_stack(true); - old_region->set_syscall_region(false); - old_region->clear_to_zero(); - old_region->remap(); - continue; - } - // Remove the old region from our regions tree, since were going to add another region - // with the exact same start address. - auto region = space->take_region(*old_region); - region->unmap(); - - // This vector is the region(s) adjacent to our range. - // We need to allocate a new region for the range we wanted to change permission bits on. - auto adjacent_regions = TRY(space->try_split_region_around_range(*old_region, intersection_to_remap)); - - // Since the range is not contained in a single region, it can only partially cover its starting and ending region, - // therefore carving out a chunk from the region will always produce a single extra region, and not two. - VERIFY(adjacent_regions.size() == 1); - - size_t new_range_offset_in_vmobject = old_region->offset_in_vmobject() + (intersection_to_remap.base().get() - old_region->range().base().get()); - auto* new_region = TRY(space->try_allocate_split_region(*region, intersection_to_remap, new_range_offset_in_vmobject)); - - new_region->unsafe_clear_access(); - new_region->set_readable(true); - new_region->set_writable(true); - new_region->set_stack(true); - new_region->set_syscall_region(false); - new_region->clear_to_zero(); - - // Map the new region using our page directory (they were just allocated and don't have one) if any. - TRY(adjacent_regions[0]->map(space->page_directory())); - - TRY(new_region->map(space->page_directory())); - } - - return range_to_remap; - } - - return EINVAL; - }); -} - -ErrorOr Process::sys$sigaltstack(Userspace user_ss, Userspace user_old_ss) -{ - VERIFY_PROCESS_BIG_LOCK_ACQUIRED(this); - TRY(require_promise(Pledge::sigaction)); - - if (user_old_ss) { - stack_t old_ss_value {}; - if (Thread::current()->m_alternative_signal_stack.has_value()) { - old_ss_value.ss_sp = Thread::current()->m_alternative_signal_stack->base().as_ptr(); - old_ss_value.ss_size = Thread::current()->m_alternative_signal_stack->size(); - if (Thread::current()->is_in_alternative_signal_stack()) - old_ss_value.ss_flags = SS_ONSTACK; - else - old_ss_value.ss_flags = 0; - } else { - old_ss_value.ss_flags = SS_DISABLE; - } - TRY(copy_to_user(user_old_ss, &old_ss_value)); - } - - if (user_ss) { - auto ss = TRY(copy_typed_from_user(user_ss)); - - if (Thread::current()->is_in_alternative_signal_stack()) - return EPERM; - - if (ss.ss_flags == SS_DISABLE) { - Thread::current()->m_alternative_signal_stack.clear(); - } else if (ss.ss_flags == 0) { - if (ss.ss_size <= MINSIGSTKSZ) - return ENOMEM; - if (Checked::addition_would_overflow((FlatPtr)ss.ss_sp, ss.ss_size)) - return ENOMEM; - - // In order to preserve compatibility with our MAP_STACK, W^X and syscall region - // protections, sigaltstack ranges are carved out of their regions, zeroed, and - // turned into read/writable MAP_STACK-enabled regions. - // This is inspired by OpenBSD's solution: https://man.openbsd.org/sigaltstack.2 - Thread::current()->m_alternative_signal_stack = TRY(remap_range_as_stack((FlatPtr)ss.ss_sp, ss.ss_size)); - } else { - return EINVAL; - } - } - - return 0; -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/sigtimedwait.html -ErrorOr Process::sys$sigtimedwait(Userspace set, Userspace info, Userspace timeout) -{ - VERIFY_NO_PROCESS_BIG_LOCK(this); - TRY(require_promise(Pledge::sigaction)); - - sigset_t set_value; - TRY(copy_from_user(&set_value, set)); - - Thread::BlockTimeout block_timeout = {}; - if (timeout) { - auto timeout_time = TRY(copy_time_from_user(timeout)); - block_timeout = Thread::BlockTimeout(false, &timeout_time); - } - - siginfo_t info_value = {}; - auto block_result = Thread::current()->block(block_timeout, set_value, info_value); - if (block_result.was_interrupted()) - return EINTR; - // We check for an unset signal instead of directly checking for a timeout interruption - // in order to allow polling the pending signals by setting the timeout to 0. - if (info_value.si_signo == SIGINVAL) { - VERIFY(block_result == Thread::BlockResult::InterruptedByTimeout); - return EAGAIN; - } - - if (info) - TRY(copy_to_user(info, &info_value)); - return info_value.si_signo; -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/sigsuspend.html -ErrorOr Process::sys$sigsuspend(Userspace mask) -{ - VERIFY_NO_PROCESS_BIG_LOCK(this); - - auto sigmask = TRY(copy_typed_from_user(mask)); - - auto* current_thread = Thread::current(); - - u32 previous_signal_mask = current_thread->update_signal_mask(sigmask); - ScopeGuard rollback_signal_mask([&]() { - current_thread->update_signal_mask(previous_signal_mask); - }); - - // TODO: Ensure that/check if we never return if the action is to terminate the process. - // TODO: Ensure that/check if we only return after an eventual signal-catching function returns. - Thread::BlockTimeout timeout = {}; - siginfo_t siginfo = {}; - if (current_thread->block(timeout, ~sigmask, siginfo).was_interrupted()) - return EINTR; - - return 0; -} - -} diff --git a/Kernel/Syscalls/socket.cpp b/Kernel/Syscalls/socket.cpp deleted file mode 100644 index d363de419ce..00000000000 --- a/Kernel/Syscalls/socket.cpp +++ /dev/null @@ -1,448 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include - -namespace Kernel { - -#define REQUIRE_PROMISE_FOR_SOCKET_DOMAIN(domain) \ - do { \ - if (domain == AF_INET) \ - TRY(require_promise(Pledge::inet)); \ - else if (domain == AF_LOCAL) \ - TRY(require_promise(Pledge::unix)); \ - } while (0) - -static void setup_socket_fd(Process::OpenFileDescriptions& fds, int fd, NonnullRefPtr description, int type) -{ - description->set_readable(true); - description->set_writable(true); - unsigned flags = 0; - if (type & SOCK_CLOEXEC) - flags |= FD_CLOEXEC; - if (type & SOCK_NONBLOCK) - description->set_blocking(false); - fds[fd].set(*description, flags); -} - -ErrorOr Process::sys$socket(int domain, int type, int protocol) -{ - VERIFY_NO_PROCESS_BIG_LOCK(this); - REQUIRE_PROMISE_FOR_SOCKET_DOMAIN(domain); - - auto credentials = this->credentials(); - if ((type & SOCK_TYPE_MASK) == SOCK_RAW && !credentials->is_superuser()) - return EACCES; - - return m_fds.with_exclusive([&](auto& fds) -> ErrorOr { - auto fd_allocation = TRY(fds.allocate()); - auto socket = TRY(Socket::create(domain, type, protocol)); - auto description = TRY(OpenFileDescription::try_create(socket)); - setup_socket_fd(fds, fd_allocation.fd, move(description), type); - return fd_allocation.fd; - }); -} - -ErrorOr Process::sys$bind(int sockfd, Userspace address, socklen_t address_length) -{ - VERIFY_NO_PROCESS_BIG_LOCK(this); - auto description = TRY(open_file_description(sockfd)); - if (!description->is_socket()) - return ENOTSOCK; - auto& socket = *description->socket(); - REQUIRE_PROMISE_FOR_SOCKET_DOMAIN(socket.domain()); - TRY(socket.bind(credentials(), address, address_length)); - return 0; -} - -ErrorOr Process::sys$listen(int sockfd, int backlog) -{ - VERIFY_NO_PROCESS_BIG_LOCK(this); - // As per POSIX, the behavior of listen() with a backlog value of 0 is implementation defined: - // "A backlog argument of 0 may allow the socket to accept connections, in which case the length of the listen queue may be set to an implementation-defined minimum value." - // Since creating a socket that can't accept any connections seems relatively useless, and as other platforms (Linux, FreeBSD, etc) chose to support accepting connections - // with this backlog value, support it as well by normalizing it to 1. - // Also, as per POSIX, the behaviour of a negative backlog value is equivalent to a backlog value of 0: - // "If listen() is called with a backlog argument value that is less than 0, the function behaves as if it had been called with a backlog argument value of 0." - if (backlog <= 0) - backlog = 1; - auto description = TRY(open_file_description(sockfd)); - if (!description->is_socket()) - return ENOTSOCK; - auto& socket = *description->socket(); - REQUIRE_PROMISE_FOR_SOCKET_DOMAIN(socket.domain()); - if (socket.is_connected()) - return EINVAL; - TRY(socket.listen(backlog)); - return 0; -} - -ErrorOr Process::sys$accept4(Userspace user_params) -{ - VERIFY_NO_PROCESS_BIG_LOCK(this); - TRY(require_promise(Pledge::accept)); - auto params = TRY(copy_typed_from_user(user_params)); - - int accepting_socket_fd = params.sockfd; - Userspace user_address((FlatPtr)params.addr); - Userspace user_address_size((FlatPtr)params.addrlen); - int flags = params.flags; - - socklen_t address_size = 0; - if (user_address) - TRY(copy_from_user(&address_size, static_ptr_cast(user_address_size))); - - ScopedDescriptionAllocation fd_allocation; - RefPtr accepting_socket_description; - - TRY(m_fds.with_exclusive([&](auto& fds) -> ErrorOr { - fd_allocation = TRY(fds.allocate()); - accepting_socket_description = TRY(fds.open_file_description(accepting_socket_fd)); - return {}; - })); - if (!accepting_socket_description->is_socket()) - return ENOTSOCK; - auto& socket = *accepting_socket_description->socket(); - - LockRefPtr accepted_socket; - for (;;) { - accepted_socket = socket.accept(); - if (accepted_socket) - break; - if (!accepting_socket_description->is_blocking()) - return EAGAIN; - auto unblock_flags = Thread::FileBlocker::BlockFlags::None; - if (Thread::current()->block({}, *accepting_socket_description, unblock_flags).was_interrupted()) - return EINTR; - } - - if (user_address) { - sockaddr_un address_buffer {}; - address_size = min(sizeof(sockaddr_un), static_cast(address_size)); - accepted_socket->get_peer_address((sockaddr*)&address_buffer, &address_size); - TRY(copy_to_user(user_address, &address_buffer, address_size)); - TRY(copy_to_user(user_address_size, &address_size)); - } - - auto accepted_socket_description = TRY(OpenFileDescription::try_create(*accepted_socket)); - - accepted_socket_description->set_readable(true); - accepted_socket_description->set_writable(true); - if (flags & SOCK_NONBLOCK) - accepted_socket_description->set_blocking(false); - int fd_flags = 0; - if (flags & SOCK_CLOEXEC) - fd_flags |= FD_CLOEXEC; - - TRY(m_fds.with_exclusive([&](auto& fds) -> ErrorOr { - fds[fd_allocation.fd].set(move(accepted_socket_description), fd_flags); - return {}; - })); - - // NOTE: Moving this state to Completed is what causes connect() to unblock on the client side. - accepted_socket->set_setup_state(Socket::SetupState::Completed); - return fd_allocation.fd; -} - -ErrorOr Process::sys$connect(int sockfd, Userspace user_address, socklen_t user_address_size) -{ - VERIFY_NO_PROCESS_BIG_LOCK(this); - - auto description = TRY(open_file_description(sockfd)); - if (!description->is_socket()) - return ENOTSOCK; - auto& socket = *description->socket(); - REQUIRE_PROMISE_FOR_SOCKET_DOMAIN(socket.domain()); - TRY(socket.connect(credentials(), *description, user_address, user_address_size)); - return 0; -} - -ErrorOr Process::sys$shutdown(int sockfd, int how) -{ - VERIFY_NO_PROCESS_BIG_LOCK(this); - TRY(require_promise(Pledge::stdio)); - if (how != SHUT_RD && how != SHUT_WR && how != SHUT_RDWR) - return EINVAL; - auto description = TRY(open_file_description(sockfd)); - if (!description->is_socket()) - return ENOTSOCK; - auto& socket = *description->socket(); - REQUIRE_PROMISE_FOR_SOCKET_DOMAIN(socket.domain()); - TRY(socket.shutdown(how)); - return 0; -} - -ErrorOr Process::sys$sendmsg(int sockfd, Userspace user_msg, int flags) -{ - VERIFY_PROCESS_BIG_LOCK_ACQUIRED(this); - TRY(require_promise(Pledge::stdio)); - auto msg = TRY(copy_typed_from_user(user_msg)); - - if (msg.msg_iovlen != 1) - return ENOTSUP; // FIXME: Support this :) - Vector iovs; - TRY(iovs.try_resize(msg.msg_iovlen)); - TRY(copy_n_from_user(iovs.data(), msg.msg_iov, msg.msg_iovlen)); - if (iovs[0].iov_len > NumericLimits::max()) - return EINVAL; - - Userspace user_addr((FlatPtr)msg.msg_name); - socklen_t addr_length = msg.msg_namelen; - - auto description = TRY(open_file_description(sockfd)); - if (!description->is_socket()) - return ENOTSOCK; - - auto& socket = *description->socket(); - if (socket.is_shut_down_for_writing()) { - if ((flags & MSG_NOSIGNAL) == 0) - Thread::current()->send_signal(SIGPIPE, &Process::current()); - return EPIPE; - } - - if (msg.msg_controllen > 0) { - // Handle command messages. - auto cmsg_buffer = TRY(ByteBuffer::create_uninitialized(msg.msg_controllen)); - TRY(copy_from_user(cmsg_buffer.data(), msg.msg_control, msg.msg_controllen)); - msg.msg_control = cmsg_buffer.data(); - for (struct cmsghdr* cmsg = CMSG_FIRSTHDR(&msg); cmsg != nullptr; cmsg = CMSG_NXTHDR(&msg, cmsg)) { - if (socket.is_local() && cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS) { - auto& local_socket = static_cast(socket); - int* fds = (int*)CMSG_DATA(cmsg); - size_t nfds = (cmsg->cmsg_len - CMSG_ALIGN(sizeof(struct cmsghdr))) / sizeof(int); - for (size_t i = 0; i < nfds; ++i) { - TRY(local_socket.sendfd(*description, TRY(open_file_description(fds[i])))); - } - } - } - } - - auto data_buffer = TRY(UserOrKernelBuffer::for_user_buffer((u8*)iovs[0].iov_base, iovs[0].iov_len)); - - while (true) { - while (!description->can_write()) { - if (!description->is_blocking()) { - return EAGAIN; - } - - auto unblock_flags = Thread::FileBlocker::BlockFlags::None; - if (Thread::current()->block({}, *description, unblock_flags).was_interrupted()) { - return EINTR; - } - // TODO: handle exceptions in unblock_flags - } - - auto bytes_sent_or_error = socket.sendto(*description, data_buffer, iovs[0].iov_len, flags, user_addr, addr_length); - if (bytes_sent_or_error.is_error()) { - if ((flags & MSG_NOSIGNAL) == 0 && bytes_sent_or_error.error().code() == EPIPE) - Thread::current()->send_signal(SIGPIPE, &Process::current()); - return bytes_sent_or_error.release_error(); - } - - auto bytes_sent = bytes_sent_or_error.release_value(); - if (bytes_sent > 0) - return bytes_sent; - } -} - -ErrorOr Process::sys$recvmsg(int sockfd, Userspace user_msg, int flags) -{ - VERIFY_PROCESS_BIG_LOCK_ACQUIRED(this); - TRY(require_promise(Pledge::stdio)); - - struct msghdr msg; - TRY(copy_from_user(&msg, user_msg)); - - if (msg.msg_iovlen != 1) - return ENOTSUP; // FIXME: Support this :) - Vector iovs; - TRY(iovs.try_resize(msg.msg_iovlen)); - TRY(copy_n_from_user(iovs.data(), msg.msg_iov, msg.msg_iovlen)); - - Userspace user_addr((FlatPtr)msg.msg_name); - Userspace user_addr_length(msg.msg_name ? (FlatPtr)&user_msg.unsafe_userspace_ptr()->msg_namelen : 0); - - auto description = TRY(open_file_description(sockfd)); - if (!description->is_socket()) - return ENOTSOCK; - auto& socket = *description->socket(); - - if (socket.is_shut_down_for_reading()) - return 0; - - auto data_buffer = TRY(UserOrKernelBuffer::for_user_buffer((u8*)iovs[0].iov_base, iovs[0].iov_len)); - UnixDateTime timestamp {}; - bool blocking = (flags & MSG_DONTWAIT) ? false : description->is_blocking(); - auto result = socket.recvfrom(*description, data_buffer, iovs[0].iov_len, flags, user_addr, user_addr_length, timestamp, blocking); - - if (result.is_error()) - return result.release_error(); - - int msg_flags = 0; - - if (result.value() > iovs[0].iov_len) { - VERIFY(socket.type() != SOCK_STREAM); - msg_flags |= MSG_TRUNC; - } - - socklen_t current_cmsg_len = 0; - auto try_add_cmsg = [&](int level, int type, void const* data, socklen_t len) -> ErrorOr { - if (current_cmsg_len + len > msg.msg_controllen) { - msg_flags |= MSG_CTRUNC; - return false; - } - - cmsghdr cmsg = { (socklen_t)CMSG_LEN(len), level, type }; - cmsghdr* target = (cmsghdr*)(((char*)msg.msg_control) + current_cmsg_len); - TRY(copy_to_user(target, &cmsg)); - TRY(copy_to_user(CMSG_DATA(target), data, len)); - current_cmsg_len += CMSG_ALIGN(cmsg.cmsg_len); - return true; - }; - - if (socket.wants_timestamp()) { - timeval time = timestamp.to_timeval(); - TRY(try_add_cmsg(SOL_SOCKET, SCM_TIMESTAMP, &time, sizeof(time))); - } - - int space_for_fds = (msg.msg_controllen - current_cmsg_len - sizeof(struct cmsghdr)) / sizeof(int); - if (space_for_fds > 0 && socket.is_local()) { - auto& local_socket = static_cast(socket); - auto descriptions = TRY(local_socket.recvfds(description, space_for_fds)); - Vector fdnums; - for (auto& description : descriptions) { - auto fd_allocation = TRY(m_fds.with_exclusive([](auto& fds) { return fds.allocate(); })); - m_fds.with_exclusive([&](auto& fds) { fds[fd_allocation.fd].set(*description, 0); }); - fdnums.append(fd_allocation.fd); - } - if (!fdnums.is_empty()) - TRY(try_add_cmsg(SOL_SOCKET, SCM_RIGHTS, fdnums.data(), fdnums.size() * sizeof(int))); - } - - TRY(copy_to_user(&user_msg.unsafe_userspace_ptr()->msg_controllen, ¤t_cmsg_len)); - - TRY(copy_to_user(&user_msg.unsafe_userspace_ptr()->msg_flags, &msg_flags)); - return result.value(); -} - -template -ErrorOr Process::get_sock_or_peer_name(Params const& params) -{ - socklen_t addrlen_value; - TRY(copy_from_user(&addrlen_value, params.addrlen, sizeof(socklen_t))); - - if (addrlen_value <= 0) - return EINVAL; - - auto description = TRY(open_file_description(params.sockfd)); - if (!description->is_socket()) - return ENOTSOCK; - - auto& socket = *description->socket(); - REQUIRE_PROMISE_FOR_SOCKET_DOMAIN(socket.domain()); - - sockaddr_un address_buffer {}; - addrlen_value = min(sizeof(sockaddr_un), static_cast(addrlen_value)); - if constexpr (sock_or_peer_name == SockOrPeerName::SockName) - socket.get_local_address((sockaddr*)&address_buffer, &addrlen_value); - else - socket.get_peer_address((sockaddr*)&address_buffer, &addrlen_value); - TRY(copy_to_user(params.addr, &address_buffer, addrlen_value)); - return copy_to_user(params.addrlen, &addrlen_value); -} - -ErrorOr Process::sys$getsockname(Userspace user_params) -{ - VERIFY_NO_PROCESS_BIG_LOCK(this); - auto params = TRY(copy_typed_from_user(user_params)); - TRY(get_sock_or_peer_name(params)); - return 0; -} - -ErrorOr Process::sys$getpeername(Userspace user_params) -{ - VERIFY_NO_PROCESS_BIG_LOCK(this); - auto params = TRY(copy_typed_from_user(user_params)); - TRY(get_sock_or_peer_name(params)); - return 0; -} - -ErrorOr Process::sys$getsockopt(Userspace user_params) -{ - VERIFY_NO_PROCESS_BIG_LOCK(this); - auto params = TRY(copy_typed_from_user(user_params)); - - int sockfd = params.sockfd; - int level = params.level; - int option = params.option; - Userspace user_value((FlatPtr)params.value); - Userspace user_value_size((FlatPtr)params.value_size); - - socklen_t value_size; - TRY(copy_from_user(&value_size, params.value_size, sizeof(socklen_t))); - - auto description = TRY(open_file_description(sockfd)); - if (!description->is_socket()) - return ENOTSOCK; - auto& socket = *description->socket(); - REQUIRE_PROMISE_FOR_SOCKET_DOMAIN(socket.domain()); - TRY(socket.getsockopt(*description, level, option, user_value, user_value_size)); - return 0; -} - -ErrorOr Process::sys$setsockopt(Userspace user_params) -{ - VERIFY_NO_PROCESS_BIG_LOCK(this); - auto params = TRY(copy_typed_from_user(user_params)); - - Userspace user_value((FlatPtr)params.value); - auto description = TRY(open_file_description(params.sockfd)); - if (!description->is_socket()) - return ENOTSOCK; - auto& socket = *description->socket(); - REQUIRE_PROMISE_FOR_SOCKET_DOMAIN(socket.domain()); - TRY(socket.setsockopt(params.level, params.option, user_value, params.value_size)); - return 0; -} - -ErrorOr Process::sys$socketpair(Userspace user_params) -{ - VERIFY_NO_PROCESS_BIG_LOCK(this); - auto params = TRY(copy_typed_from_user(user_params)); - - if (params.domain != AF_LOCAL) - return EINVAL; - - if (params.protocol != 0 && params.protocol != PF_LOCAL) - return EINVAL; - - auto pair = TRY(LocalSocket::try_create_connected_pair(params.type & SOCK_TYPE_MASK)); - - return m_fds.with_exclusive([&](auto& fds) -> ErrorOr { - auto fd_allocation0 = TRY(fds.allocate()); - auto fd_allocation1 = TRY(fds.allocate()); - - int allocated_fds[2]; - allocated_fds[0] = fd_allocation0.fd; - allocated_fds[1] = fd_allocation1.fd; - setup_socket_fd(fds, allocated_fds[0], pair.description0, params.type); - setup_socket_fd(fds, allocated_fds[1], pair.description1, params.type); - - if (copy_n_to_user(params.sv, allocated_fds, 2).is_error()) { - // Avoid leaking both file descriptors on error. - fds[allocated_fds[0]] = {}; - fds[allocated_fds[1]] = {}; - return EFAULT; - } - return 0; - }); -} - -} diff --git a/Kernel/Syscalls/stat.cpp b/Kernel/Syscalls/stat.cpp deleted file mode 100644 index 800e0899e34..00000000000 --- a/Kernel/Syscalls/stat.cpp +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include - -namespace Kernel { - -ErrorOr Process::sys$fstat(int fd, Userspace user_statbuf) -{ - VERIFY_NO_PROCESS_BIG_LOCK(this); - TRY(require_promise(Pledge::stdio)); - auto description = TRY(open_file_description(fd)); - auto buffer = TRY(description->stat()); - TRY(copy_to_user(user_statbuf, &buffer)); - return 0; -} - -ErrorOr Process::sys$stat(Userspace user_params) -{ - VERIFY_NO_PROCESS_BIG_LOCK(this); - TRY(require_promise(Pledge::rpath)); - auto params = TRY(copy_typed_from_user(user_params)); - - auto path = TRY(get_syscall_path_argument(params.path)); - - CustodyBase base(params.dirfd, path->view()); - auto metadata = TRY(VirtualFileSystem::the().lookup_metadata(credentials(), path->view(), base, params.follow_symlinks ? 0 : O_NOFOLLOW_NOERROR)); - auto statbuf = TRY(metadata.stat()); - TRY(copy_to_user(params.statbuf, &statbuf)); - return 0; -} - -} diff --git a/Kernel/Syscalls/statvfs.cpp b/Kernel/Syscalls/statvfs.cpp deleted file mode 100644 index 03bbb3c282a..00000000000 --- a/Kernel/Syscalls/statvfs.cpp +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Copyright (c) 2021, Justin Mietzner - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include - -namespace Kernel { - -ErrorOr Process::do_statvfs(FileSystem const& fs, Custody const* custody, statvfs* buf) -{ - statvfs kernelbuf = {}; - - kernelbuf.f_bsize = static_cast(fs.logical_block_size()); - kernelbuf.f_frsize = fs.fragment_size(); - kernelbuf.f_blocks = fs.total_block_count(); - kernelbuf.f_bfree = fs.free_block_count(); - - // FIXME: Implement "available blocks" into Filesystem - kernelbuf.f_bavail = fs.free_block_count(); - - kernelbuf.f_files = fs.total_inode_count(); - kernelbuf.f_ffree = fs.free_inode_count(); - kernelbuf.f_favail = fs.free_inode_count(); // FIXME: same as f_bavail - - kernelbuf.f_fsid = fs.fsid().value(); - - kernelbuf.f_namemax = 255; - - (void)fs.class_name().copy_characters_to_buffer(kernelbuf.f_basetype, FSTYPSZ); - - if (custody) - kernelbuf.f_flag = custody->mount_flags(); - - TRY(copy_to_user(buf, &kernelbuf)); - return 0; -} - -ErrorOr Process::sys$statvfs(Userspace user_params) -{ - VERIFY_NO_PROCESS_BIG_LOCK(this); - TRY(require_promise(Pledge::rpath)); - auto params = TRY(copy_typed_from_user(user_params)); - - auto path = TRY(get_syscall_path_argument(params.path)); - - auto custody = TRY(VirtualFileSystem::the().resolve_path(credentials(), path->view(), current_directory(), nullptr, 0)); - auto& inode = custody->inode(); - auto const& fs = inode.fs(); - - return do_statvfs(fs, custody, params.buf); -} - -ErrorOr Process::sys$fstatvfs(int fd, statvfs* buf) -{ - VERIFY_NO_PROCESS_BIG_LOCK(this); - TRY(require_promise(Pledge::stdio)); - - auto description = TRY(open_file_description(fd)); - auto const* inode = description->inode(); - if (inode == nullptr) - return ENOENT; - - // FIXME: The custody that we pass in might be outdated. However, this only affects the mount flags. - return do_statvfs(inode->fs(), description->custody(), buf); -} - -} diff --git a/Kernel/Syscalls/sync.cpp b/Kernel/Syscalls/sync.cpp deleted file mode 100644 index 260fa6c4cf5..00000000000 --- a/Kernel/Syscalls/sync.cpp +++ /dev/null @@ -1,20 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include - -namespace Kernel { - -ErrorOr Process::sys$sync() -{ - VERIFY_NO_PROCESS_BIG_LOCK(this); - TRY(require_promise(Pledge::stdio)); - VirtualFileSystem::sync(); - return 0; -} - -} diff --git a/Kernel/Syscalls/sysconf.cpp b/Kernel/Syscalls/sysconf.cpp deleted file mode 100644 index cda8e3dd404..00000000000 --- a/Kernel/Syscalls/sysconf.cpp +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include - -namespace Kernel { - -ErrorOr Process::sys$sysconf(int name) -{ - VERIFY_NO_PROCESS_BIG_LOCK(this); - switch (name) { - case _SC_MONOTONIC_CLOCK: - return 1; - case _SC_NPROCESSORS_CONF: - case _SC_NPROCESSORS_ONLN: - return Processor::count(); - case _SC_OPEN_MAX: - return OpenFileDescriptions::max_open(); - case _SC_PAGESIZE: - return PAGE_SIZE; - case _SC_HOST_NAME_MAX: - return HOST_NAME_MAX; - case _SC_TTY_NAME_MAX: - return TTY_NAME_MAX; - case _SC_GETPW_R_SIZE_MAX: - case _SC_GETGR_R_SIZE_MAX: - return 4096; // idk - case _SC_CLK_TCK: - return TimeManagement::the().ticks_per_second(); - case _SC_SYMLOOP_MAX: - return Kernel::VirtualFileSystem::symlink_recursion_limit; - case _SC_ARG_MAX: - return Process::max_arguments_size; - case _SC_IOV_MAX: - return IOV_MAX; - case _SC_PHYS_PAGES: - return MM.get_system_memory_info().physical_pages; - default: - return EINVAL; - } -} - -} diff --git a/Kernel/Syscalls/thread.cpp b/Kernel/Syscalls/thread.cpp deleted file mode 100644 index 779c3782fd8..00000000000 --- a/Kernel/Syscalls/thread.cpp +++ /dev/null @@ -1,202 +0,0 @@ -/* - * Copyright (c) 2018-2021, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include - -namespace Kernel { - -ErrorOr Process::sys$create_thread(void* (*entry)(void*), Userspace user_params) -{ - VERIFY_NO_PROCESS_BIG_LOCK(this); - TRY(require_promise(Pledge::thread)); - auto params = TRY(copy_typed_from_user(user_params)); - - unsigned detach_state = params.detach_state; - int schedule_priority = params.schedule_priority; - unsigned stack_size = params.stack_size; - - auto user_sp = Checked((FlatPtr)params.stack_location); - user_sp += stack_size; - if (user_sp.has_overflow()) - return EOVERFLOW; - - TRY(address_space().with([&](auto& space) -> ErrorOr { - if (!MM.validate_user_stack(*space, VirtualAddress(user_sp.value() - 4))) - return EFAULT; - return {}; - })); - - // FIXME: return EAGAIN if Thread::all_threads().size() is greater than PTHREAD_THREADS_MAX - - int requested_thread_priority = schedule_priority; - if (requested_thread_priority < THREAD_PRIORITY_MIN || requested_thread_priority > THREAD_PRIORITY_MAX) - return EINVAL; - - bool is_thread_joinable = (0 == detach_state); - - // FIXME: Do something with guard pages? - - auto thread = TRY(Thread::create(*this)); - - // We know this thread is not the main_thread, - // So give it a unique name until the user calls $prctl with the PR_SET_THREAD_NAME option on it - auto new_thread_name = TRY(name().with([&](auto& process_name) { - return KString::formatted("{} [{}]", process_name.representable_view(), thread->tid().value()); - })); - thread->set_name(new_thread_name->view()); - - if (!is_thread_joinable) - thread->detach(); - - auto& regs = thread->regs(); - regs.set_ip((FlatPtr)entry); - regs.set_sp(user_sp.value()); - -#if ARCH(X86_64) - regs.set_flags(0x0202); - regs.cr3 = address_space().with([](auto& space) { return space->page_directory().cr3(); }); - - // Set up the argument registers expected by pthread_create_helper. - regs.rdi = (FlatPtr)params.entry; - regs.rsi = (FlatPtr)params.entry_argument; - regs.rdx = (FlatPtr)params.stack_location; - regs.rcx = (FlatPtr)params.stack_size; - - thread->arch_specific_data().fs_base = bit_cast(params.tls_pointer); -#elif ARCH(AARCH64) - regs.ttbr0_el1 = address_space().with([](auto& space) { return space->page_directory().ttbr0(); }); - - // Set up the argument registers expected by pthread_create_helper. - regs.x[0] = (FlatPtr)params.entry; - regs.x[1] = (FlatPtr)params.entry_argument; - regs.x[2] = (FlatPtr)params.stack_location; - regs.x[3] = (FlatPtr)params.stack_size; - - regs.tpidr_el0 = bit_cast(params.tls_pointer); -#elif ARCH(RISCV64) - regs.satp = address_space().with([](auto& space) { return space->page_directory().satp(); }); - - // Set up the argument registers expected by pthread_create_helper. - regs.x[9] = (FlatPtr)params.entry; - regs.x[10] = (FlatPtr)params.entry_argument; - regs.x[11] = (FlatPtr)params.stack_location; - regs.x[12] = (FlatPtr)params.stack_size; - - regs.x[3] = bit_cast(params.tls_pointer); -#else -# error Unknown architecture -#endif - - PerformanceManager::add_thread_created_event(*thread); - - SpinlockLocker lock(g_scheduler_lock); - thread->set_priority(requested_thread_priority); - thread->set_state(Thread::State::Runnable); - return thread->tid().value(); -} - -void Process::sys$exit_thread(Userspace exit_value, Userspace stack_location, size_t stack_size) -{ - VERIFY_PROCESS_BIG_LOCK_ACQUIRED(this); - - auto result = require_promise(Pledge::thread); - if (result.is_error()) { - // Crash now, as we will never reach back to the syscall handler. - crash(SIGABRT, {}); - } - - if (this->thread_count() == 1) { - // If this is the last thread, instead kill the process. - this->sys$exit(0); - } - - auto* current_thread = Thread::current(); - current_thread->set_profiling_suppressed(); - PerformanceManager::add_thread_exit_event(*current_thread); - - if (stack_location) { - auto unmap_result = address_space().with([&](auto& space) { - return space->unmap_mmap_range(stack_location.vaddr(), stack_size); - }); - if (unmap_result.is_error()) - dbgln("Failed to unmap thread stack, terminating thread anyway. Error code: {}", unmap_result.error()); - } - - current_thread->exit(reinterpret_cast(exit_value.ptr())); - VERIFY_NOT_REACHED(); -} - -ErrorOr Process::sys$detach_thread(pid_t tid) -{ - VERIFY_NO_PROCESS_BIG_LOCK(this); - TRY(require_promise(Pledge::thread)); - auto thread = TRY(get_thread_from_thread_list(tid)); - if (!thread->is_joinable()) - return EINVAL; - - thread->detach(); - return 0; -} - -ErrorOr Process::sys$join_thread(pid_t tid, Userspace exit_value) -{ - VERIFY_NO_PROCESS_BIG_LOCK(this); - TRY(require_promise(Pledge::thread)); - - auto thread = TRY(get_thread_from_thread_list(tid)); - auto* current_thread = Thread::current(); - if (thread == current_thread) - return EDEADLK; - - void* joinee_exit_value = nullptr; - - // NOTE: pthread_join() cannot be interrupted by signals. Only by death. - for (;;) { - ErrorOr try_join_result; - auto result = current_thread->block({}, *thread, try_join_result, joinee_exit_value); - if (result == Thread::BlockResult::NotBlocked) { - if (try_join_result.is_error()) - return try_join_result.release_error(); - break; - } - if (result == Thread::BlockResult::InterruptedByDeath) - break; - dbgln("join_thread: retrying"); - } - - if (exit_value) - TRY(copy_to_user(exit_value, &joinee_exit_value)); - - return 0; -} - -ErrorOr Process::sys$kill_thread(pid_t tid, int signal) -{ - VERIFY_NO_PROCESS_BIG_LOCK(this); - TRY(require_promise(Pledge::thread)); - - if (signal < 0 || signal >= NSIG) - return EINVAL; - - auto thread = TRY(get_thread_from_thread_list(tid)); - if (signal != 0) - thread->send_signal(signal, &Process::current()); - - return 0; -} - -ErrorOr Process::sys$gettid() -{ - VERIFY_NO_PROCESS_BIG_LOCK(this); - TRY(require_promise(Pledge::stdio)); - return Thread::current()->tid().value(); -} - -} diff --git a/Kernel/Syscalls/times.cpp b/Kernel/Syscalls/times.cpp deleted file mode 100644 index 9c0d714a6dc..00000000000 --- a/Kernel/Syscalls/times.cpp +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include - -namespace Kernel { - -ErrorOr Process::sys$times(Userspace user_times) -{ - VERIFY_NO_PROCESS_BIG_LOCK(this); - TRY(require_promise(Pledge::stdio)); - - // There's no lock here, as it seems harmless to report intermediate values - // as long as each individual counter is intact. - tms times = {}; - times.tms_utime = m_ticks_in_user; - times.tms_stime = m_ticks_in_kernel; - times.tms_cutime = m_ticks_in_user_for_dead_children; - times.tms_cstime = m_ticks_in_kernel_for_dead_children; - - TRY(copy_to_user(user_times, ×)); - return TimeManagement::the().uptime_ms() & 0x7fffffff; -} - -} diff --git a/Kernel/Syscalls/umask.cpp b/Kernel/Syscalls/umask.cpp deleted file mode 100644 index 77f41d3829e..00000000000 --- a/Kernel/Syscalls/umask.cpp +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include - -namespace Kernel { - -ErrorOr Process::sys$umask(mode_t mask) -{ - VERIFY_NO_PROCESS_BIG_LOCK(this); - TRY(require_promise(Pledge::stdio)); - return with_mutable_protected_data([&](auto& protected_data) -> ErrorOr { - auto old_mask = protected_data.umask; - protected_data.umask = mask & 0777; - return old_mask; - }); -} - -} diff --git a/Kernel/Syscalls/uname.cpp b/Kernel/Syscalls/uname.cpp deleted file mode 100644 index ba13dedaeb6..00000000000 --- a/Kernel/Syscalls/uname.cpp +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * Copyright (c) 2022, kleines Filmröllchen - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include - -namespace Kernel { - -#if ARCH(X86_64) -# define UNAME_MACHINE "x86_64" -#elif ARCH(AARCH64) -# define UNAME_MACHINE "AArch64" -#elif ARCH(RISCV64) -# define UNAME_MACHINE "riscv64" -#else -# error Unknown architecture -#endif - -KString* g_version_string { nullptr }; - -ErrorOr Process::sys$uname(Userspace user_buf) -{ - VERIFY_NO_PROCESS_BIG_LOCK(this); - TRY(require_promise(Pledge::stdio)); - - utsname buf { - "SerenityOS", - {}, // Hostname, filled in below. - {}, // "Release" (1.0-dev), filled in below. - {}, // "Revision" (git commit hash), filled in below. - UNAME_MACHINE - }; - - VERIFY(g_version_string); - AK::TypedTransfer::copy(reinterpret_cast(buf.release), g_version_string->bytes().data(), min(g_version_string->length(), UTSNAME_ENTRY_LEN - 1)); - - AK::TypedTransfer::copy(reinterpret_cast(buf.version), SERENITY_VERSION.bytes().data(), min(SERENITY_VERSION.length(), UTSNAME_ENTRY_LEN - 1)); - - hostname().with_shared([&](auto const& name) { - auto name_length = name.representable_view().length(); - VERIFY(name_length <= (UTSNAME_ENTRY_LEN - 1)); - AK::TypedTransfer::copy(reinterpret_cast(buf.nodename), name.representable_view().characters_without_null_termination(), name_length); - buf.nodename[name_length] = '\0'; - }); - - TRY(copy_to_user(user_buf, &buf)); - return 0; -} - -} diff --git a/Kernel/Syscalls/unlink.cpp b/Kernel/Syscalls/unlink.cpp deleted file mode 100644 index 00851918d9f..00000000000 --- a/Kernel/Syscalls/unlink.cpp +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include - -namespace Kernel { - -ErrorOr Process::sys$unlink(int dirfd, Userspace user_path, size_t path_length, int flags) -{ - VERIFY_NO_PROCESS_BIG_LOCK(this); - TRY(require_promise(Pledge::cpath)); - auto path = TRY(get_syscall_path_argument(user_path, path_length)); - - if (flags & ~AT_REMOVEDIR) - return Error::from_errno(EINVAL); - - CustodyBase base(dirfd, path->view()); - - if (flags & AT_REMOVEDIR) - TRY(VirtualFileSystem::the().rmdir(credentials(), path->view(), base)); - else - TRY(VirtualFileSystem::the().unlink(credentials(), path->view(), base)); - return 0; -} - -} diff --git a/Kernel/Syscalls/unveil.cpp b/Kernel/Syscalls/unveil.cpp deleted file mode 100644 index 0a36981a798..00000000000 --- a/Kernel/Syscalls/unveil.cpp +++ /dev/null @@ -1,173 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * Copyright (c) 2021, Max Wipfli - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include -#include -#include - -namespace Kernel { - -static void update_intermediate_node_permissions(UnveilNode& root_node, UnveilAccess new_permissions) -{ - for (auto& entry : root_node.children()) { - auto& node = static_cast(*entry.value); - if (node.was_explicitly_unveiled()) - continue; - node.metadata_value().permissions = new_permissions; - update_intermediate_node_permissions(node, new_permissions); - } -} - -static ErrorOr update_unveil_data(Process::UnveilData& locked_unveil_data, StringView unveiled_path, UnveilAccess new_permissions) -{ - auto path_parts = KLexicalPath::parts(unveiled_path); - auto it = path_parts.begin(); - // Note: For the sake of completence, we check if the locked state was inherited - // by an execve'd sequence. If that is the case, just silently ignore this. - if (locked_unveil_data.state == VeilState::LockedInherited) - return {}; - // NOTE: We have to check again, since the veil may have been locked by another thread - // while we were parsing the arguments. - if (locked_unveil_data.state == VeilState::Locked) - return EPERM; - - auto& matching_node = locked_unveil_data.paths.traverse_until_last_accessible_node(it, path_parts.end()); - if (it.is_end()) { - // If the path has already been explicitly unveiled, do not allow elevating its permissions. - if (matching_node.was_explicitly_unveiled()) { - if (new_permissions & ~matching_node.permissions()) - return EPERM; - } - - // It is possible that nodes that are "grandchildren" of the matching node have already been unveiled. - // This means that there may be intermediate nodes between this one and the unveiled "grandchildren" - // that inherited the current node's previous permissions. Those nodes now need their permissions - // updated to match the current node. - if (matching_node.permissions() != new_permissions) - update_intermediate_node_permissions(matching_node, new_permissions); - - matching_node.metadata_value().explicitly_unveiled = true; - matching_node.metadata_value().permissions = new_permissions; - locked_unveil_data.state = VeilState::Dropped; - return {}; - } - - auto new_unveiled_path = TRY(KString::try_create(unveiled_path)); - TRY(matching_node.insert( - it, - path_parts.end(), - { move(new_unveiled_path), new_permissions, true }, - [](auto& parent, auto& it) -> ErrorOr> { - auto path = TRY(KString::formatted("{}/{}", parent.path(), *it)); - return UnveilMetadata(move(path), parent.permissions(), false); - })); - - VERIFY(locked_unveil_data.state != VeilState::Locked); - locked_unveil_data.state = VeilState::Dropped; - return {}; -} - -ErrorOr Process::sys$unveil(Userspace user_params) -{ - VERIFY_NO_PROCESS_BIG_LOCK(this); - auto params = TRY(copy_typed_from_user(user_params)); - - if (!params.path.characters && !params.permissions.characters) { - m_unveil_data.with([&](auto& unveil_data) { unveil_data.state = VeilState::Locked; }); - return 0; - } - - if (!((params.flags & to_underlying(UnveilFlags::CurrentProgram)) || (params.flags & to_underlying(UnveilFlags::AfterExec)))) - return EINVAL; - - // Note: If we inherited a locked state, then silently ignore the unveil request, - // and let the user program potentially deal with an ENOENT error later on. - if ((params.flags & static_cast(UnveilFlags::CurrentProgram)) && veil_state() == VeilState::LockedInherited) - return 0; - - // Note: We only lock the unveil state for current program, while allowing adding - // indefinitely unveil data before doing the actual exec(). - if ((params.flags & static_cast(UnveilFlags::CurrentProgram)) && veil_state() == VeilState::Locked) - return EPERM; - - if (!params.path.characters || !params.permissions.characters) - return EINVAL; - - auto path = TRY(get_syscall_path_argument(params.path)); - - if (path->is_empty() || !path->view().starts_with('/')) - return EINVAL; - - auto permissions = TRY(get_syscall_string_fixed_buffer<5>(params.permissions)); - - // Let's work out permissions first... - unsigned new_permissions = 0; - for (char const permission : permissions.representable_view()) { - switch (permission) { - case 'r': - new_permissions |= UnveilAccess::Read; - break; - case 'w': - new_permissions |= UnveilAccess::Write; - break; - case 'x': - new_permissions |= UnveilAccess::Execute; - break; - case 'c': - new_permissions |= UnveilAccess::CreateOrRemove; - break; - case 'b': - new_permissions |= UnveilAccess::Browse; - break; - default: - return EINVAL; - } - } - - // Now, let's try and resolve the path and obtain custody of the inode on the disk, and if not, bail out with - // the error from resolve_path_without_veil() - // However, if the user specified unveil() with "c" permissions, we don't set errno if ENOENT is encountered, - // because they most likely intend the program to create the file for them later on. - // If this case is encountered, the parent node of the path is returned and the custody of that inode is used instead. - RefPtr parent_custody; // Parent inode in case of ENOENT - OwnPtr new_unveiled_path; - auto custody_or_error = VirtualFileSystem::the().resolve_path_without_veil(credentials(), path->view(), VirtualFileSystem::the().root_custody(), &parent_custody); - if (!custody_or_error.is_error()) { - new_unveiled_path = TRY(custody_or_error.value()->try_serialize_absolute_path()); - } else if (custody_or_error.error().code() == ENOENT && parent_custody && (new_permissions & UnveilAccess::CreateOrRemove)) { - auto parent_custody_path = TRY(parent_custody->try_serialize_absolute_path()); - new_unveiled_path = TRY(KLexicalPath::try_join(parent_custody_path->view(), KLexicalPath::basename(path->view()))); - } else { - // FIXME Should this be EINVAL? - return custody_or_error.release_error(); - } - - if (params.flags & static_cast(UnveilFlags::CurrentProgram)) { - TRY(unveil_data().with([&](auto& data) -> ErrorOr { - TRY(update_unveil_data(data, new_unveiled_path->view(), static_cast(new_permissions))); - return {}; - })); - } - - if (params.flags & static_cast(UnveilFlags::AfterExec)) { - TRY(exec_unveil_data().with([&](auto& data) -> ErrorOr { - // Note: The only valid way to get into this state is by using unveil before doing - // an actual exec with the UnveilFlags::AfterExec flag. Then this state is applied on - // the actual new program unveil data, and never on the m_exec_unveil_data. - VERIFY(data.state != VeilState::LockedInherited); - TRY(update_unveil_data(data, new_unveiled_path->view(), static_cast(new_permissions))); - return {}; - })); - } - return 0; -} - -} diff --git a/Kernel/Syscalls/utime.cpp b/Kernel/Syscalls/utime.cpp deleted file mode 100644 index 0b9da8f3006..00000000000 --- a/Kernel/Syscalls/utime.cpp +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include - -namespace Kernel { - -ErrorOr Process::sys$utime(Userspace user_path, size_t path_length, Userspace user_buf) -{ - VERIFY_NO_PROCESS_BIG_LOCK(this); - TRY(require_promise(Pledge::fattr)); - auto path = TRY(get_syscall_path_argument(user_path, path_length)); - utimbuf buf; - if (user_buf) { - TRY(copy_from_user(&buf, user_buf)); - } else { - auto now = kgettimeofday().truncated_seconds_since_epoch(); - // Not a bug! - buf = { now, now }; - } - TRY(VirtualFileSystem::the().utime(credentials(), path->view(), current_directory(), buf.actime, buf.modtime)); - return 0; -} - -} diff --git a/Kernel/Syscalls/utimensat.cpp b/Kernel/Syscalls/utimensat.cpp deleted file mode 100644 index d34bd3aca61..00000000000 --- a/Kernel/Syscalls/utimensat.cpp +++ /dev/null @@ -1,81 +0,0 @@ -/* - * Copyright (c) 2022, Ariel Don - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include - -namespace Kernel { - -ErrorOr Process::sys$futimens(Userspace user_params) -{ - VERIFY_NO_PROCESS_BIG_LOCK(this); - TRY(require_promise(Pledge::fattr)); - - auto params = TRY(copy_typed_from_user(user_params)); - auto now = kgettimeofday().to_timespec(); - - timespec times[2]; - if (params.times) { - TRY(copy_from_user(times, params.times, sizeof(times))); - if (times[0].tv_nsec == UTIME_NOW) - times[0] = now; - if (times[1].tv_nsec == UTIME_NOW) - times[1] = now; - } else { - // According to POSIX, both access and modification times are set to - // the current time given a nullptr. - times[0] = now; - times[1] = now; - } - - auto description = TRY(open_file_description(params.fd)); - if (!description->inode()) - return EBADF; - if (!description->custody()) - return EBADF; - - auto& atime = times[0]; - auto& mtime = times[1]; - TRY(VirtualFileSystem::the().do_utimens(credentials(), *description->custody(), atime, mtime)); - return 0; -} - -ErrorOr Process::sys$utimensat(Userspace user_params) -{ - VERIFY_NO_PROCESS_BIG_LOCK(this); - TRY(require_promise(Pledge::fattr)); - - auto params = TRY(copy_typed_from_user(user_params)); - auto now = kgettimeofday().to_timespec(); - int follow_symlink = params.flag & AT_SYMLINK_NOFOLLOW ? O_NOFOLLOW_NOERROR : 0; - - timespec times[2]; - if (params.times) { - TRY(copy_from_user(times, params.times, sizeof(times))); - if (times[0].tv_nsec == UTIME_NOW) - times[0] = now; - if (times[1].tv_nsec == UTIME_NOW) - times[1] = now; - } else { - // According to POSIX, both access and modification times are set to - // the current time given a nullptr. - times[0] = now; - times[1] = now; - } - - auto path = TRY(get_syscall_path_argument(params.path)); - auto& atime = times[0]; - auto& mtime = times[1]; - - CustodyBase base(params.dirfd, path->view()); - TRY(VirtualFileSystem::the().utimensat(credentials(), path->view(), base, atime, mtime, follow_symlink)); - return 0; -} - -} diff --git a/Kernel/Syscalls/waitid.cpp b/Kernel/Syscalls/waitid.cpp deleted file mode 100644 index 62bf0fe7649..00000000000 --- a/Kernel/Syscalls/waitid.cpp +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include - -namespace Kernel { - -ErrorOr Process::do_waitid(Variant, NonnullRefPtr> waitee, int options) -{ - ErrorOr result = siginfo_t {}; - if (Thread::current()->block({}, options, move(waitee), result).was_interrupted()) - return EINTR; - VERIFY(!result.is_error() || (options & WNOHANG)); - return result; -} - -ErrorOr Process::sys$waitid(Userspace user_params) -{ - VERIFY_PROCESS_BIG_LOCK_ACQUIRED(this); - TRY(require_promise(Pledge::proc)); - auto params = TRY(copy_typed_from_user(user_params)); - - Variant, NonnullRefPtr> waitee; - switch (params.idtype) { - case P_ALL: - break; - case P_PID: { - auto waitee_process = Process::from_pid_in_same_jail(params.id); - if (!waitee_process) - return ECHILD; - bool waitee_is_child = waitee_process->ppid() == Process::current().pid(); - bool waitee_is_our_tracee = waitee_process->has_tracee_thread(Process::current().pid()); - if (!waitee_is_child && !waitee_is_our_tracee) - return ECHILD; - waitee = waitee_process.release_nonnull(); - break; - } - case P_PGID: { - auto waitee_group = ProcessGroup::from_pgid(params.id); - if (!waitee_group) { - return ECHILD; - } - waitee = waitee_group.release_nonnull(); - break; - } - default: - return EINVAL; - } - - dbgln_if(PROCESS_DEBUG, "sys$waitid({}, {}, {}, {})", params.idtype, params.id, params.infop, params.options); - - auto siginfo = TRY(do_waitid(move(waitee), params.options)); - TRY(copy_to_user(params.infop, &siginfo)); - return 0; -} - -} diff --git a/Kernel/Syscalls/write.cpp b/Kernel/Syscalls/write.cpp deleted file mode 100644 index fd59d696ccc..00000000000 --- a/Kernel/Syscalls/write.cpp +++ /dev/null @@ -1,116 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include - -namespace Kernel { - -ErrorOr Process::sys$pwritev(int fd, Userspace iov, int iov_count, off_t base_offset) -{ - VERIFY_PROCESS_BIG_LOCK_ACQUIRED(this); - TRY(require_promise(Pledge::stdio)); - if (iov_count < 0) - return EINVAL; - - if (iov_count > IOV_MAX) - return EFAULT; - - u64 total_length = 0; - Vector vecs; - TRY(vecs.try_resize(iov_count)); - TRY(copy_n_from_user(vecs.data(), iov, iov_count)); - for (auto& vec : vecs) { - total_length += vec.iov_len; - if (total_length > NumericLimits::max()) - return EINVAL; - } - - auto description = TRY(open_file_description(fd)); - if (!description->is_writable()) - return EBADF; - // NOTE: Negative offset means "operate like writev" which seeks the file. - if (base_offset >= 0 && !description->file().is_seekable()) - return EINVAL; - - int nwritten = 0; - off_t current_offset = base_offset; - for (auto& vec : vecs) { - auto buffer = TRY(UserOrKernelBuffer::for_user_buffer((u8*)vec.iov_base, vec.iov_len)); - auto result = do_write(*description, buffer, vec.iov_len, base_offset >= 0 ? current_offset : Optional {}); - if (result.is_error()) { - if (nwritten == 0) - return result.release_error(); - return nwritten; - } - nwritten += result.value(); - current_offset += result.value(); - } - - return nwritten; -} - -ErrorOr Process::do_write(OpenFileDescription& description, UserOrKernelBuffer const& data, size_t data_size, Optional offset) -{ - size_t total_nwritten = 0; - - if (description.should_append() && description.file().is_seekable()) { - TRY(description.seek(0, SEEK_END)); - } - - while (total_nwritten < data_size) { - while (!description.can_write()) { - if (!description.is_blocking()) { - if (total_nwritten > 0) - return total_nwritten; - return EAGAIN; - } - auto unblock_flags = Thread::FileBlocker::BlockFlags::None; - if (Thread::current()->block({}, description, unblock_flags).was_interrupted()) { - if (total_nwritten == 0) - return EINTR; - } - // TODO: handle exceptions in unblock_flags - } - auto nwritten_or_error = offset.has_value() - ? description.write(offset.value() + total_nwritten, data.offset(total_nwritten), data_size - total_nwritten) - : description.write(data.offset(total_nwritten), data_size - total_nwritten); - if (nwritten_or_error.is_error()) { - if (total_nwritten > 0) - return total_nwritten; - if (nwritten_or_error.error().code() == EAGAIN) - continue; - if (nwritten_or_error.error().code() == EPIPE) - Thread::current()->send_signal(SIGPIPE, &Process::current()); - return nwritten_or_error.release_error(); - } - VERIFY(nwritten_or_error.value() > 0); - total_nwritten += nwritten_or_error.value(); - } - return total_nwritten; -} - -ErrorOr Process::sys$write(int fd, Userspace data, size_t size) -{ - VERIFY_PROCESS_BIG_LOCK_ACQUIRED(this); - TRY(require_promise(Pledge::stdio)); - if (size == 0) - return 0; - if (size > NumericLimits::max()) - return EINVAL; - - dbgln_if(IO_DEBUG, "sys$write({}, {}, {})", fd, data.ptr(), size); - auto description = TRY(open_file_description(fd)); - if (!description->is_writable()) - return EBADF; - - auto buffer = TRY(UserOrKernelBuffer::for_user_buffer(data, static_cast(size))); - return do_write(*description, buffer, size); -} - -} diff --git a/Kernel/Tasks/AtomicEdgeAction.h b/Kernel/Tasks/AtomicEdgeAction.h deleted file mode 100644 index c80adcaa1da..00000000000 --- a/Kernel/Tasks/AtomicEdgeAction.h +++ /dev/null @@ -1,87 +0,0 @@ -/* - * Copyright (c) 2021, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include - -namespace Kernel { - -template -class AtomicEdgeAction { -public: - template - bool ref(FirstRefAction first_ref_action) - { - AtomicRefCountType expected = 0; - AtomicRefCountType desired = (1 << 1) | 1; - // Least significant bit indicates we're busy protecting/unprotecting - for (;;) { - if (m_atomic_ref_count.compare_exchange_strong(expected, desired, AK::memory_order_relaxed)) - break; - - Processor::wait_check(); - - expected &= ~1; - desired = expected + (1 << 1); - VERIFY(desired > expected); - if (expected == 0) - desired |= 1; - } - - atomic_thread_fence(AK::memory_order_acquire); - - if (expected == 0) { - first_ref_action(); - - // drop the busy flag - m_atomic_ref_count.store(desired & ~1, AK::memory_order_release); - return true; - } - return false; - } - - template - bool unref(LastRefAction last_ref_action) - { - AtomicRefCountType expected = 1 << 1; - AtomicRefCountType desired = (1 << 1) | 1; - // Least significant bit indicates we're busy protecting/unprotecting - for (;;) { - if (m_atomic_ref_count.compare_exchange_strong(expected, desired, AK::memory_order_relaxed)) - break; - - Processor::wait_check(); - - expected &= ~1; - VERIFY(expected != 0); // Someone should always have at least one reference - - if (expected == 1 << 1) { - desired = (1 << 1) | 1; - } else { - desired = expected - (1 << 1); - VERIFY(desired < expected); - } - } - - AK::atomic_thread_fence(AK::memory_order_release); - - if (expected == 1 << 1) { - last_ref_action(); - - // drop the busy flag and release reference - m_atomic_ref_count.store(0, AK::memory_order_release); - return true; - } - return false; - } - -private: - Atomic m_atomic_ref_count { 0 }; -}; - -} diff --git a/Kernel/Tasks/Coredump.cpp b/Kernel/Tasks/Coredump.cpp deleted file mode 100644 index 401f8064a6e..00000000000 --- a/Kernel/Tasks/Coredump.cpp +++ /dev/null @@ -1,403 +0,0 @@ -/* - * Copyright (c) 2019-2020, Jesse Buhagiar - * Copyright (c) 2020, Itamar S. - * Copyright (c) 2020-2021, Linus Groh - * Copyright (c) 2021, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define INCLUDE_USERSPACE_HEAP_MEMORY_IN_COREDUMPS 0 - -static Singleton, LockRank::None>> s_coredump_directory_path; - -namespace Kernel { - -SpinlockProtected, LockRank::None>& Coredump::directory_path() -{ - return s_coredump_directory_path; -} - -bool Coredump::FlatRegionData::looks_like_userspace_heap_region() const -{ - return name().starts_with("LibJS:"sv) || name().starts_with("malloc:"sv); -} - -bool Coredump::FlatRegionData::is_consistent_with_region(Memory::Region const& region) const -{ - if (m_access != region.access()) - return false; - - if (m_page_count != region.page_count() || m_size != region.size()) - return false; - - if (m_vaddr != region.vaddr()) - return false; - - return true; -} - -ErrorOr> Coredump::try_create(NonnullRefPtr process, StringView output_path) -{ - if (!process->is_dumpable()) { - dbgln("Refusing to generate coredump for non-dumpable process {}", process->pid().value()); - return EPERM; - } - - Vector regions; - size_t number_of_regions = process->address_space().with([](auto& space) { - return space->region_tree().regions().size(); - }); - TRY(regions.try_ensure_capacity(number_of_regions)); - TRY(process->address_space().with([&](auto& space) -> ErrorOr { - for (auto& region : space->region_tree().regions()) - TRY(regions.try_empend(region, TRY(KString::try_create(region.name())))); - return {}; - })); - - auto description = TRY(try_create_target_file(process, output_path)); - return adopt_nonnull_own_or_enomem(new (nothrow) Coredump(move(process), move(description), move(regions))); -} - -Coredump::Coredump(NonnullRefPtr process, NonnullRefPtr description, Vector regions) - : m_process(move(process)) - , m_description(move(description)) - , m_regions(move(regions)) -{ - m_num_program_headers = 0; - for (auto& region : m_regions) { -#if !INCLUDE_USERSPACE_HEAP_MEMORY_IN_COREDUMPS - if (region.looks_like_userspace_heap_region()) - continue; -#endif - - if (region.access() == Memory::Region::Access::None) - continue; - ++m_num_program_headers; - } - ++m_num_program_headers; // +1 for NOTE segment -} - -ErrorOr> Coredump::try_create_target_file(Process const& process, StringView output_path) -{ - auto output_directory = KLexicalPath::dirname(output_path); - auto dump_directory = TRY(VirtualFileSystem::the().open_directory(Process::current().credentials(), output_directory, VirtualFileSystem::the().root_custody())); - auto dump_directory_metadata = dump_directory->inode().metadata(); - if (dump_directory_metadata.uid != 0 || dump_directory_metadata.gid != 0 || dump_directory_metadata.mode != 040777) { - dbgln("Refusing to put coredump in sketchy directory '{}'", output_directory); - return EINVAL; - } - - auto process_credentials = process.credentials(); - return TRY(VirtualFileSystem::the().open( - Process::current().credentials(), - KLexicalPath::basename(output_path), - O_CREAT | O_WRONLY | O_EXCL, - S_IFREG, // We will enable reading from userspace when we finish generating the coredump file - dump_directory, - UidAndGid { process_credentials->uid(), process_credentials->gid() })); -} - -ErrorOr Coredump::write_elf_header() -{ - Elf_Ehdr elf_file_header; - elf_file_header.e_ident[EI_MAG0] = 0x7f; - elf_file_header.e_ident[EI_MAG1] = 'E'; - elf_file_header.e_ident[EI_MAG2] = 'L'; - elf_file_header.e_ident[EI_MAG3] = 'F'; -#if ARCH(X86_64) || ARCH(AARCH64) || ARCH(RISCV64) - elf_file_header.e_ident[EI_CLASS] = ELFCLASS64; -#else -# error Unknown architecture -#endif - elf_file_header.e_ident[EI_DATA] = ELFDATA2LSB; - elf_file_header.e_ident[EI_VERSION] = EV_CURRENT; - elf_file_header.e_ident[EI_OSABI] = 0; // ELFOSABI_NONE - elf_file_header.e_ident[EI_ABIVERSION] = 0; - elf_file_header.e_ident[EI_PAD + 1] = 0; - elf_file_header.e_ident[EI_PAD + 2] = 0; - elf_file_header.e_ident[EI_PAD + 3] = 0; - elf_file_header.e_ident[EI_PAD + 4] = 0; - elf_file_header.e_ident[EI_PAD + 5] = 0; - elf_file_header.e_ident[EI_PAD + 6] = 0; - elf_file_header.e_type = ET_CORE; -#if ARCH(X86_64) - elf_file_header.e_machine = EM_X86_64; -#elif ARCH(AARCH64) - elf_file_header.e_machine = EM_AARCH64; -#elif ARCH(RISCV64) - elf_file_header.e_machine = EM_RISCV; -#else -# error Unknown architecture -#endif - elf_file_header.e_version = 1; - elf_file_header.e_entry = 0; - elf_file_header.e_phoff = sizeof(Elf_Ehdr); - elf_file_header.e_shoff = 0; - elf_file_header.e_flags = 0; - elf_file_header.e_ehsize = sizeof(Elf_Ehdr); - elf_file_header.e_shentsize = sizeof(Elf_Shdr); - elf_file_header.e_phentsize = sizeof(Elf_Phdr); - elf_file_header.e_phnum = m_num_program_headers; - elf_file_header.e_shnum = 0; - elf_file_header.e_shstrndx = SHN_UNDEF; - - TRY(m_description->write(UserOrKernelBuffer::for_kernel_buffer(reinterpret_cast(&elf_file_header)), sizeof(Elf_Ehdr))); - - return {}; -} - -ErrorOr Coredump::write_program_headers(size_t notes_size) -{ - size_t offset = sizeof(Elf_Ehdr) + m_num_program_headers * sizeof(Elf_Phdr); - for (auto& region : m_regions) { -#if !INCLUDE_USERSPACE_HEAP_MEMORY_IN_COREDUMPS - if (region.looks_like_userspace_heap_region()) - continue; -#endif - - if (region.access() == Memory::Region::Access::None) - continue; - - Elf_Phdr phdr {}; - - phdr.p_type = PT_LOAD; - phdr.p_offset = offset; - phdr.p_vaddr = region.vaddr().get(); - phdr.p_paddr = 0; - - phdr.p_filesz = region.page_count() * PAGE_SIZE; - phdr.p_memsz = region.page_count() * PAGE_SIZE; - phdr.p_align = 0; - - phdr.p_flags = region.is_readable() ? PF_R : 0; - if (region.is_writable()) - phdr.p_flags |= PF_W; - if (region.is_executable()) - phdr.p_flags |= PF_X; - - offset += phdr.p_filesz; - - [[maybe_unused]] auto rc = m_description->write(UserOrKernelBuffer::for_kernel_buffer(reinterpret_cast(&phdr)), sizeof(Elf_Phdr)); - } - - Elf_Phdr notes_pheader {}; - notes_pheader.p_type = PT_NOTE; - notes_pheader.p_offset = offset; - notes_pheader.p_vaddr = 0; - notes_pheader.p_paddr = 0; - notes_pheader.p_filesz = notes_size; - notes_pheader.p_memsz = notes_size; - notes_pheader.p_align = 0; - notes_pheader.p_flags = 0; - - TRY(m_description->write(UserOrKernelBuffer::for_kernel_buffer(reinterpret_cast(¬es_pheader)), sizeof(Elf_Phdr))); - - return {}; -} - -ErrorOr Coredump::write_regions() -{ - u8 zero_buffer[PAGE_SIZE] = {}; - - for (auto& region : m_regions) { - VERIFY(!region.is_kernel()); - -#if !INCLUDE_USERSPACE_HEAP_MEMORY_IN_COREDUMPS - if (region.looks_like_userspace_heap_region()) - continue; -#endif - - if (region.access() == Memory::Region::Access::None) - continue; - - auto buffer = TRY(KBuffer::try_create_with_size("Coredump Region Copy Buffer"sv, region.page_count() * PAGE_SIZE)); - - TRY(m_process->address_space().with([&](auto& space) -> ErrorOr { - auto* real_region = space->region_tree().regions().find(region.vaddr().get()); - - if (!real_region) { - dmesgln("Coredump::write_regions: Failed to find matching region in the process"); - return Error::from_errno(EFAULT); - } - - if (!region.is_consistent_with_region(*real_region)) { - dmesgln("Coredump::write_regions: Found region does not match stored metadata"); - return Error::from_errno(EINVAL); - } - - // If we crashed in the middle of mapping in Regions, they do not have a page directory yet, and will crash on a remap() call - if (!real_region->is_mapped()) - return {}; - - real_region->set_readable(true); - real_region->remap(); - - for (size_t i = 0; i < region.page_count(); i++) { - auto page = real_region->physical_page(i); - auto src_buffer = [&]() -> ErrorOr { - if (page) - return UserOrKernelBuffer::for_user_buffer(reinterpret_cast((region.vaddr().as_ptr() + (i * PAGE_SIZE))), PAGE_SIZE); - // If the current page is not backed by a physical page, we zero it in the coredump file. - return UserOrKernelBuffer::for_kernel_buffer(zero_buffer); - }(); - TRY(src_buffer.value().read(buffer->bytes().slice(i * PAGE_SIZE, PAGE_SIZE))); - } - - return {}; - })); - - TRY(m_description->write(UserOrKernelBuffer::for_kernel_buffer(buffer->data()), buffer->size())); - } - - return {}; -} - -ErrorOr Coredump::write_notes_segment(ReadonlyBytes notes_segment) -{ - TRY(m_description->write(UserOrKernelBuffer::for_kernel_buffer(const_cast(notes_segment.data())), notes_segment.size())); - return {}; -} - -ErrorOr Coredump::create_notes_process_data(auto& builder) const -{ - ELF::Core::ProcessInfo info {}; - info.header.type = ELF::Core::NotesEntryHeader::Type::ProcessInfo; - TRY(builder.append_bytes(ReadonlyBytes { (void*)&info, sizeof(info) })); - - { - auto process_obj = TRY(JsonObjectSerializer<>::try_create(builder)); - TRY(process_obj.add("pid"sv, m_process->pid().value())); - TRY(process_obj.add("termination_signal"sv, m_process->termination_signal())); - TRY(process_obj.add("executable_path"sv, m_process->executable() ? TRY(m_process->executable()->try_serialize_absolute_path())->view() : ""sv)); - - { - auto arguments_array = TRY(process_obj.add_array("arguments"sv)); - for (auto const& argument : m_process->arguments()) - TRY(arguments_array.add(argument->view())); - TRY(arguments_array.finish()); - } - - { - auto environment_array = TRY(process_obj.add_array("environment"sv)); - for (auto const& variable : m_process->environment()) - TRY(environment_array.add(variable->view())); - TRY(environment_array.finish()); - } - - TRY(process_obj.finish()); - } - - TRY(builder.append('\0')); - return {}; -} - -ErrorOr Coredump::create_notes_threads_data(auto& builder) const -{ - for (auto const& thread : m_process->threads_for_coredump({})) { - ELF::Core::ThreadInfo info {}; - info.header.type = ELF::Core::NotesEntryHeader::Type::ThreadInfo; - info.tid = thread->tid().value(); - - if (thread->current_trap()) - copy_kernel_registers_into_ptrace_registers(info.regs, thread->get_register_dump_from_stack()); - - TRY(builder.append_bytes(ReadonlyBytes { &info, sizeof(info) })); - } - return {}; -} - -ErrorOr Coredump::create_notes_regions_data(auto& builder) const -{ - size_t region_index = 0; - for (auto const& region : m_regions) { -#if !INCLUDE_USERSPACE_HEAP_MEMORY_IN_COREDUMPS - if (region.looks_like_userspace_heap_region()) - continue; -#endif - - if (region.access() == Memory::Region::Access::None) - continue; - - ELF::Core::MemoryRegionInfo info {}; - info.header.type = ELF::Core::NotesEntryHeader::Type::MemoryRegionInfo; - - info.region_start = region.vaddr().get(); - info.region_end = region.vaddr().offset(region.size()).get(); - info.program_header_index = region_index++; - - TRY(builder.append_bytes(ReadonlyBytes { (void*)&info, sizeof(info) })); - - // NOTE: The region name *is* null-terminated, so the following is ok: - auto name = region.name(); - if (name.is_empty()) - TRY(builder.append('\0')); - else - TRY(builder.append(name.characters_without_null_termination(), name.length() + 1)); - } - - return {}; -} - -ErrorOr Coredump::create_notes_metadata_data(auto& builder) const -{ - ELF::Core::Metadata metadata {}; - metadata.header.type = ELF::Core::NotesEntryHeader::Type::Metadata; - TRY(builder.append_bytes(ReadonlyBytes { (void*)&metadata, sizeof(metadata) })); - - { - auto metadata_obj = TRY(JsonObjectSerializer<>::try_create(builder)); - TRY(m_process->for_each_coredump_property([&](auto& key, auto& value) -> ErrorOr { - TRY(metadata_obj.add(key.view(), value.view())); - return {}; - })); - TRY(metadata_obj.finish()); - } - TRY(builder.append('\0')); - return {}; -} - -ErrorOr Coredump::create_notes_segment_data(auto& builder) const -{ - TRY(create_notes_process_data(builder)); - TRY(create_notes_threads_data(builder)); - TRY(create_notes_regions_data(builder)); - TRY(create_notes_metadata_data(builder)); - - ELF::Core::NotesEntryHeader null_entry {}; - null_entry.type = ELF::Core::NotesEntryHeader::Type::Null; - TRY(builder.append(ReadonlyBytes { &null_entry, sizeof(null_entry) })); - - return {}; -} - -ErrorOr Coredump::write() -{ - ScopedAddressSpaceSwitcher switcher(m_process); - - auto builder = TRY(KBufferBuilder::try_create()); - TRY(create_notes_segment_data(builder)); - TRY(write_elf_header()); - TRY(write_program_headers(builder.bytes().size())); - TRY(write_regions()); - TRY(write_notes_segment(builder.bytes())); - - return m_description->chmod(Process::current().credentials(), 0600); // Make coredump file read/writable -} - -} diff --git a/Kernel/Tasks/Coredump.h b/Kernel/Tasks/Coredump.h deleted file mode 100644 index dbe5968ef8b..00000000000 --- a/Kernel/Tasks/Coredump.h +++ /dev/null @@ -1,88 +0,0 @@ -/* - * Copyright (c) 2019-2020, Jesse Buhagiar - * Copyright (c) 2020, Itamar S. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include -#include - -namespace Kernel { - -class Coredump { -public: - static ErrorOr> try_create(NonnullRefPtr, StringView output_path); - static SpinlockProtected, LockRank::None>& directory_path(); - - ~Coredump() = default; - ErrorOr write(); - -private: - class FlatRegionData { - public: - explicit FlatRegionData(Memory::Region const& region, NonnullOwnPtr name) - : m_access(region.access()) - , m_is_executable(region.is_executable()) - , m_is_kernel(region.is_kernel()) - , m_is_readable(region.is_readable()) - , m_is_writable(region.is_writable()) - , m_name(move(name)) - , m_page_count(region.page_count()) - , m_size(region.size()) - , m_vaddr(region.vaddr()) - { - } - - auto access() const { return m_access; } - auto name() const { return m_name->view(); } - auto is_executable() const { return m_is_executable; } - auto is_kernel() const { return m_is_kernel; } - auto is_readable() const { return m_is_readable; } - auto is_writable() const { return m_is_writable; } - auto page_count() const { return m_page_count; } - auto size() const { return m_size; } - auto vaddr() const { return m_vaddr; } - - bool looks_like_userspace_heap_region() const; - bool is_consistent_with_region(Memory::Region const& region) const; - - private: - Memory::Region::Access m_access; - bool m_is_executable; - bool m_is_kernel; - bool m_is_readable; - bool m_is_writable; - NonnullOwnPtr m_name; - size_t m_page_count; - size_t m_size; - VirtualAddress m_vaddr; - }; - - Coredump(NonnullRefPtr, NonnullRefPtr, Vector); - static ErrorOr> try_create_target_file(Process const&, StringView output_path); - - ErrorOr write_elf_header(); - ErrorOr write_program_headers(size_t notes_size); - ErrorOr write_regions(); - ErrorOr write_notes_segment(ReadonlyBytes); - - ErrorOr create_notes_segment_data(auto&) const; - ErrorOr create_notes_process_data(auto&) const; - ErrorOr create_notes_threads_data(auto&) const; - ErrorOr create_notes_regions_data(auto&) const; - ErrorOr create_notes_metadata_data(auto&) const; - - NonnullRefPtr const m_process; - NonnullRefPtr const m_description; - size_t m_num_program_headers { 0 }; - Vector m_regions; -}; - -} diff --git a/Kernel/Tasks/CrashHandler.cpp b/Kernel/Tasks/CrashHandler.cpp deleted file mode 100644 index c4ad36f9534..00000000000 --- a/Kernel/Tasks/CrashHandler.cpp +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright (c) 2018-2021, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include - -#include -#include -#include - -namespace Kernel { - -void handle_crash(Kernel::RegisterState const& regs, char const* description, int signal, bool out_of_memory) -{ - auto* current_thread = Thread::current(); - if (!current_thread) { - VERIFY(regs.previous_mode() == ExecutionMode::Kernel); - - dbgln("CRASH: CPU #{} {} in kernel", Processor::current_id(), description); - - dump_registers(regs); - if (Memory::MemoryManager::is_initialized()) - MM.dump_kernel_regions(); - - PANIC("Crash in kernel with !Thread::current()"); - } - - auto crashed_in_kernel = regs.previous_mode() == ExecutionMode::Kernel; - if (!crashed_in_kernel && current_thread->has_signal_handler(signal) && !current_thread->should_ignore_signal(signal) && !current_thread->is_signal_masked(signal)) { - current_thread->send_urgent_signal_to_self(signal); - return; - } - - auto& process = current_thread->process(); - - // If a process crashed while inspecting another process, - // make sure we switch back to the right page tables. - Memory::MemoryManager::enter_process_address_space(process); - - dmesgln("CRASH: CPU #{} {} in {}", Processor::current_id(), description, regs.previous_mode() == ExecutionMode::Kernel ? "kernel"sv : "userspace"sv); - dump_registers(regs); - - if (crashed_in_kernel) { - process.address_space().with([&](auto& space) { space->dump_regions(); }); - PANIC("Crash in kernel"); - } - - process.crash(signal, { regs }, out_of_memory); -} - -} diff --git a/Kernel/Tasks/FinalizerTask.cpp b/Kernel/Tasks/FinalizerTask.cpp deleted file mode 100644 index 9586a9b4ea4..00000000000 --- a/Kernel/Tasks/FinalizerTask.cpp +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright (c) 2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include - -namespace Kernel { - -static constexpr StringView finalizer_task_name = "Finalizer Task"sv; - -static void finalizer_task(void*) -{ - Thread::current()->set_priority(THREAD_PRIORITY_LOW); - while (!Process::current().is_dying()) { - // The order of this if-else is important: We want to continue trying to finalize the threads in case - // Thread::finalize_dying_threads set g_finalizer_has_work back to true due to OOM conditions - if (g_finalizer_has_work.exchange(false, AK::MemoryOrder::memory_order_acq_rel) == true) - Thread::finalize_dying_threads(); - else - g_finalizer_wait_queue->wait_forever(finalizer_task_name); - } - Process::current().sys$exit(0); - VERIFY_NOT_REACHED(); -} - -UNMAP_AFTER_INIT void FinalizerTask::spawn() -{ - auto [_, finalizer_thread] = MUST(Process::create_kernel_process(finalizer_task_name, finalizer_task, nullptr)); - g_finalizer = move(finalizer_thread); -} - -} diff --git a/Kernel/Tasks/FinalizerTask.h b/Kernel/Tasks/FinalizerTask.h deleted file mode 100644 index 8aac0c95d10..00000000000 --- a/Kernel/Tasks/FinalizerTask.h +++ /dev/null @@ -1,14 +0,0 @@ -/* - * Copyright (c) 2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -namespace Kernel { -class FinalizerTask { -public: - static void spawn(); -}; -} diff --git a/Kernel/Tasks/FutexQueue.cpp b/Kernel/Tasks/FutexQueue.cpp deleted file mode 100644 index 4c9f9f2df6d..00000000000 --- a/Kernel/Tasks/FutexQueue.cpp +++ /dev/null @@ -1,160 +0,0 @@ -/* - * Copyright (c) 2020, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include - -namespace Kernel { - -FutexQueue::FutexQueue() = default; -FutexQueue::~FutexQueue() = default; - -bool FutexQueue::should_add_blocker(Thread::Blocker& b, void*) -{ - VERIFY(m_lock.is_locked()); - VERIFY(b.blocker_type() == Thread::Blocker::Type::Futex); - - VERIFY(m_imminent_waits > 0); - m_imminent_waits--; - - if (m_was_removed) { - dbgln_if(FUTEXQUEUE_DEBUG, "FutexQueue @ {}: should not block thread {}: was removed", this, b.thread()); - return false; - } - dbgln_if(FUTEXQUEUE_DEBUG, "FutexQueue @ {}: should block thread {}", this, b.thread()); - - return true; -} - -ErrorOr FutexQueue::wake_n_requeue(u32 wake_count, Function()> const& get_target_queue, u32 requeue_count, bool& is_empty, bool& is_empty_target) -{ - is_empty_target = false; - SpinlockLocker lock(m_lock); - - dbgln_if(FUTEXQUEUE_DEBUG, "FutexQueue @ {}: wake_n_requeue({}, {})", this, wake_count, requeue_count); - - u32 did_wake = 0, did_requeue = 0; - unblock_all_blockers_whose_conditions_are_met_locked([&](Thread::Blocker& b, void*, bool& stop_iterating) { - VERIFY(b.blocker_type() == Thread::Blocker::Type::Futex); - auto& blocker = static_cast(b); - - dbgln_if(FUTEXQUEUE_DEBUG, "FutexQueue @ {}: wake_n_requeue unblocking {}", this, blocker.thread()); - VERIFY(did_wake < wake_count); - if (blocker.unblock()) { - if (++did_wake >= wake_count) - stop_iterating = true; - return true; - } - return false; - }); - is_empty = is_empty_and_no_imminent_waits_locked(); - if (requeue_count > 0) { - auto blockers_to_requeue = do_take_blockers(requeue_count); - if (!blockers_to_requeue.is_empty()) { - if (auto* target_futex_queue = TRY(get_target_queue())) { - dbgln_if(FUTEXQUEUE_DEBUG, "FutexQueue @ {}: wake_n_requeue requeueing {} blockers to {}", this, blockers_to_requeue.size(), target_futex_queue); - - // While still holding m_lock, notify each blocker - for (auto& info : blockers_to_requeue) { - VERIFY(info.blocker->blocker_type() == Thread::Blocker::Type::Futex); - auto& blocker = *static_cast(info.blocker); - blocker.begin_requeue(); - } - - lock.unlock(); - did_requeue = blockers_to_requeue.size(); - - SpinlockLocker target_lock(target_futex_queue->m_lock); - // Now that we have the lock of the target, append the blockers - // and notify them that they completed the move - for (auto& info : blockers_to_requeue) { - VERIFY(info.blocker->blocker_type() == Thread::Blocker::Type::Futex); - auto& blocker = *static_cast(info.blocker); - blocker.finish_requeue(*target_futex_queue); - } - target_futex_queue->do_append_blockers(move(blockers_to_requeue)); - is_empty_target = target_futex_queue->is_empty_and_no_imminent_waits_locked(); - } else { - dbgln_if(FUTEXQUEUE_DEBUG, "FutexQueue @ {}: wake_n_requeue could not get target queue to requeue {} blockers", this, blockers_to_requeue.size()); - do_append_blockers(move(blockers_to_requeue)); - } - } - } - return did_wake + did_requeue; -} - -u32 FutexQueue::wake_n(u32 wake_count, Optional const& bitset, bool& is_empty) -{ - if (wake_count == 0) { - is_empty = false; - return 0; // should we assert instead? - } - SpinlockLocker lock(m_lock); - dbgln_if(FUTEXQUEUE_DEBUG, "FutexQueue @ {}: wake_n({})", this, wake_count); - u32 did_wake = 0; - unblock_all_blockers_whose_conditions_are_met_locked([&](Thread::Blocker& b, void*, bool& stop_iterating) { - VERIFY(b.blocker_type() == Thread::Blocker::Type::Futex); - auto& blocker = static_cast(b); - - dbgln_if(FUTEXQUEUE_DEBUG, "FutexQueue @ {}: wake_n unblocking {}", this, blocker.thread()); - VERIFY(did_wake < wake_count); - if (bitset.has_value() ? blocker.unblock_bitset(bitset.value()) : blocker.unblock()) { - if (++did_wake >= wake_count) - stop_iterating = true; - return true; - } - return false; - }); - is_empty = is_empty_and_no_imminent_waits_locked(); - return did_wake; -} - -u32 FutexQueue::wake_all(bool& is_empty) -{ - SpinlockLocker lock(m_lock); - dbgln_if(FUTEXQUEUE_DEBUG, "FutexQueue @ {}: wake_all", this); - u32 did_wake = 0; - unblock_all_blockers_whose_conditions_are_met_locked([&](Thread::Blocker& b, void*, bool&) { - VERIFY(b.blocker_type() == Thread::Blocker::Type::Futex); - auto& blocker = static_cast(b); - dbgln_if(FUTEXQUEUE_DEBUG, "FutexQueue @ {}: wake_all unblocking {}", this, blocker.thread()); - if (blocker.unblock(true)) { - did_wake++; - return true; - } - return false; - }); - is_empty = is_empty_and_no_imminent_waits_locked(); - return did_wake; -} - -bool FutexQueue::is_empty_and_no_imminent_waits_locked() -{ - return m_imminent_waits == 0 && is_empty_locked(); -} - -bool FutexQueue::queue_imminent_wait() -{ - SpinlockLocker lock(m_lock); - if (m_was_removed) - return false; - m_imminent_waits++; - return true; -} - -bool FutexQueue::try_remove() -{ - SpinlockLocker lock(m_lock); - if (m_was_removed) - return false; - if (!is_empty_and_no_imminent_waits_locked()) - return false; - m_was_removed = true; - return true; -} - -} diff --git a/Kernel/Tasks/FutexQueue.h b/Kernel/Tasks/FutexQueue.h deleted file mode 100644 index 3b53c25ee3b..00000000000 --- a/Kernel/Tasks/FutexQueue.h +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright (c) 2020, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include - -namespace Kernel { - -class FutexQueue final - : public AtomicRefCounted - , public Thread::BlockerSet { -public: - FutexQueue(); - virtual ~FutexQueue(); - - ErrorOr wake_n_requeue(u32, Function()> const&, u32, bool&, bool&); - u32 wake_n(u32, Optional const&, bool&); - u32 wake_all(bool&); - - template - Thread::BlockResult wait_on(Thread::BlockTimeout const& timeout, Args&&... args) - { - return Thread::current()->block(timeout, *this, forward(args)...); - } - - bool queue_imminent_wait(); - bool try_remove(); - - bool is_empty_and_no_imminent_waits() - { - SpinlockLocker lock(m_lock); - return is_empty_and_no_imminent_waits_locked(); - } - bool is_empty_and_no_imminent_waits_locked(); - -protected: - virtual bool should_add_blocker(Thread::Blocker& b, void*) override; - -private: - size_t m_imminent_waits { 1 }; // We only create this object if we're going to be waiting, so start out with 1 - bool m_was_removed { false }; -}; - -} diff --git a/Kernel/Tasks/PerformanceEventBuffer.cpp b/Kernel/Tasks/PerformanceEventBuffer.cpp deleted file mode 100644 index a1f737e120d..00000000000 --- a/Kernel/Tasks/PerformanceEventBuffer.cpp +++ /dev/null @@ -1,430 +0,0 @@ -/* - * Copyright (c) 2020-2021, Andreas Kling - * Copyright (c) 2023, Jakub Berkop - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace Kernel { - -PerformanceEventBuffer::PerformanceEventBuffer(NonnullOwnPtr buffer) - : m_buffer(move(buffer)) -{ -} - -NEVER_INLINE ErrorOr PerformanceEventBuffer::append(int type, FlatPtr arg1, FlatPtr arg2, StringView arg3, Thread* current_thread, FilesystemEvent filesystem_event) -{ - FlatPtr base_pointer = (FlatPtr)__builtin_frame_address(0); - return append_with_ip_and_bp(current_thread->pid(), current_thread->tid(), 0, base_pointer, type, 0, arg1, arg2, arg3, filesystem_event); -} - -static Vector raw_backtrace(FlatPtr frame_pointer, FlatPtr pc) -{ - Vector backtrace; - if (pc != 0) - backtrace.unchecked_append(pc); - - // NOTE: The stack should always have kernel frames first, followed by userspace frames. - // If a userspace frame points back into kernel memory, something is afoot. - bool is_walking_userspace_stack = false; - - MUST(AK::unwind_stack_from_frame_pointer( - frame_pointer, - [&is_walking_userspace_stack](FlatPtr address) -> ErrorOr { - if (!Memory::is_user_address(VirtualAddress { address })) { - if (is_walking_userspace_stack) { - dbgln("SHENANIGANS! Userspace stack points back into kernel memory"); - return EFAULT; - } - } else { - is_walking_userspace_stack = true; - } - - FlatPtr value; - - if (Memory::is_user_range(VirtualAddress { address }, sizeof(FlatPtr))) { - TRY(copy_from_user(&value, bit_cast(address))); - } else { - void* fault_at; - if (!safe_memcpy(&value, bit_cast(address), sizeof(FlatPtr), fault_at)) - return EFAULT; - } - - return value; - }, - [&backtrace](AK::StackFrame stack_frame) -> ErrorOr { - backtrace.unchecked_append(stack_frame.return_address); - if (backtrace.size() >= PerformanceEvent::max_stack_frame_count) - return IterationDecision::Break; - - return IterationDecision::Continue; - })); - - return backtrace; -} - -ErrorOr PerformanceEventBuffer::append_with_ip_and_bp(ProcessID pid, ThreadID tid, RegisterState const& regs, - int type, u32 lost_samples, FlatPtr arg1, FlatPtr arg2, StringView arg3, FilesystemEvent filesystem_event) -{ - return append_with_ip_and_bp(pid, tid, regs.ip(), regs.bp(), type, lost_samples, arg1, arg2, arg3, filesystem_event); -} - -ErrorOr PerformanceEventBuffer::append_with_ip_and_bp(ProcessID pid, ThreadID tid, - FlatPtr ip, FlatPtr bp, int type, u32 lost_samples, FlatPtr arg1, FlatPtr arg2, StringView arg3, FilesystemEvent filesystem_event) -{ - if (count() >= capacity()) - return ENOBUFS; - - if ((g_profiling_event_mask & type) == 0) - return EINVAL; - - auto* current_thread = Thread::current(); - u32 enter_count = 0; - if (current_thread) - enter_count = current_thread->enter_profiler(); - ScopeGuard leave_profiler([&] { - if (current_thread) - current_thread->leave_profiler(); - }); - if (enter_count > 0) - return EINVAL; - - PerformanceEvent event; - event.type = type; - event.lost_samples = lost_samples; - - switch (type) { - case PERF_EVENT_SAMPLE: - break; - case PERF_EVENT_MALLOC: - event.data.malloc.size = arg1; - event.data.malloc.ptr = arg2; - break; - case PERF_EVENT_FREE: - event.data.free.ptr = arg1; - break; - case PERF_EVENT_MMAP: - event.data.mmap.ptr = arg1; - event.data.mmap.size = arg2; - memset(event.data.mmap.name, 0, sizeof(event.data.mmap.name)); - if (!arg3.is_empty()) - memcpy(event.data.mmap.name, arg3.characters_without_null_termination(), min(arg3.length(), sizeof(event.data.mmap.name) - 1)); - break; - case PERF_EVENT_MUNMAP: - event.data.munmap.ptr = arg1; - event.data.munmap.size = arg2; - break; - case PERF_EVENT_PROCESS_CREATE: - event.data.process_create.parent_pid = arg1; - memset(event.data.process_create.executable, 0, sizeof(event.data.process_create.executable)); - if (!arg3.is_empty()) { - memcpy(event.data.process_create.executable, arg3.characters_without_null_termination(), - min(arg3.length(), sizeof(event.data.process_create.executable) - 1)); - } - break; - case PERF_EVENT_PROCESS_EXEC: - memset(event.data.process_exec.executable, 0, sizeof(event.data.process_exec.executable)); - if (!arg3.is_empty()) { - memcpy(event.data.process_exec.executable, arg3.characters_without_null_termination(), - min(arg3.length(), sizeof(event.data.process_exec.executable) - 1)); - } - break; - case PERF_EVENT_PROCESS_EXIT: - break; - case PERF_EVENT_THREAD_CREATE: - event.data.thread_create.parent_tid = arg1; - break; - case PERF_EVENT_THREAD_EXIT: - break; - case PERF_EVENT_CONTEXT_SWITCH: - event.data.context_switch.next_pid = arg1; - event.data.context_switch.next_tid = arg2; - break; - case PERF_EVENT_KMALLOC: - event.data.kmalloc.size = arg1; - event.data.kmalloc.ptr = arg2; - break; - case PERF_EVENT_KFREE: - event.data.kfree.size = arg1; - event.data.kfree.ptr = arg2; - break; - case PERF_EVENT_PAGE_FAULT: - break; - case PERF_EVENT_SYSCALL: - break; - case PERF_EVENT_SIGNPOST: - event.data.signpost.arg1 = arg1; - event.data.signpost.arg2 = arg2; - break; - case PERF_EVENT_FILESYSTEM: - event.data.filesystem = filesystem_event; - break; - default: - return EINVAL; - } - - auto backtrace = raw_backtrace(bp, ip); - event.stack_size = min(sizeof(event.stack) / sizeof(FlatPtr), static_cast(backtrace.size())); - memcpy(event.stack, backtrace.data(), event.stack_size * sizeof(FlatPtr)); - - event.pid = pid.value(); - event.tid = tid.value(); - event.timestamp = TimeManagement::the().uptime_ms(); - at(m_count++) = event; - return {}; -} - -PerformanceEvent& PerformanceEventBuffer::at(size_t index) -{ - VERIFY(index < capacity()); - auto* events = reinterpret_cast(m_buffer->data()); - return events[index]; -} - -template -ErrorOr PerformanceEventBuffer::to_json_impl(Serializer& object) const -{ - { - auto strings_object = TRY(object.add_array("strings"sv)); - Vector strings_sorted_by_index; - - TRY(m_strings.with([&](auto& strings) -> ErrorOr { - TRY(strings_sorted_by_index.try_resize(strings.size())); - - for (auto& entry : strings) { - strings_sorted_by_index[entry.value] = const_cast(entry.key.ptr()); - } - - for (size_t i = 0; i < strings.size(); i++) { - TRY(strings_object.add(strings_sorted_by_index[i]->view())); - } - - return {}; - })); - - TRY(strings_object.finish()); - } - - auto current_process_credentials = Process::current().credentials(); - bool show_kernel_addresses = current_process_credentials->is_superuser(); - auto array = TRY(object.add_array("events"sv)); - bool seen_first_sample = false; - for (size_t i = 0; i < m_count; ++i) { - auto const& event = at(i); - - if (!show_kernel_addresses) { - if (event.type == PERF_EVENT_KMALLOC || event.type == PERF_EVENT_KFREE) - continue; - } - - auto event_object = TRY(array.add_object()); - switch (event.type) { - case PERF_EVENT_SAMPLE: - TRY(event_object.add("type"sv, "sample")); - break; - case PERF_EVENT_MALLOC: - TRY(event_object.add("type"sv, "malloc")); - TRY(event_object.add("ptr"sv, static_cast(event.data.malloc.ptr))); - TRY(event_object.add("size"sv, static_cast(event.data.malloc.size))); - break; - case PERF_EVENT_FREE: - TRY(event_object.add("type"sv, "free")); - TRY(event_object.add("ptr"sv, static_cast(event.data.free.ptr))); - break; - case PERF_EVENT_MMAP: - TRY(event_object.add("type"sv, "mmap")); - TRY(event_object.add("ptr"sv, static_cast(event.data.mmap.ptr))); - TRY(event_object.add("size"sv, static_cast(event.data.mmap.size))); - TRY(event_object.add("name"sv, event.data.mmap.name)); - break; - case PERF_EVENT_MUNMAP: - TRY(event_object.add("type"sv, "munmap")); - TRY(event_object.add("ptr"sv, static_cast(event.data.munmap.ptr))); - TRY(event_object.add("size"sv, static_cast(event.data.munmap.size))); - break; - case PERF_EVENT_PROCESS_CREATE: - TRY(event_object.add("type"sv, "process_create")); - TRY(event_object.add("parent_pid"sv, static_cast(event.data.process_create.parent_pid))); - TRY(event_object.add("executable"sv, event.data.process_create.executable)); - break; - case PERF_EVENT_PROCESS_EXEC: - TRY(event_object.add("type"sv, "process_exec")); - TRY(event_object.add("executable"sv, event.data.process_exec.executable)); - break; - case PERF_EVENT_PROCESS_EXIT: - TRY(event_object.add("type"sv, "process_exit")); - break; - case PERF_EVENT_THREAD_CREATE: - TRY(event_object.add("type"sv, "thread_create")); - TRY(event_object.add("parent_tid"sv, static_cast(event.data.thread_create.parent_tid))); - break; - case PERF_EVENT_THREAD_EXIT: - TRY(event_object.add("type"sv, "thread_exit")); - break; - case PERF_EVENT_CONTEXT_SWITCH: - TRY(event_object.add("type"sv, "context_switch")); - TRY(event_object.add("next_pid"sv, static_cast(event.data.context_switch.next_pid))); - TRY(event_object.add("next_tid"sv, static_cast(event.data.context_switch.next_tid))); - break; - case PERF_EVENT_KMALLOC: - TRY(event_object.add("type"sv, "kmalloc")); - TRY(event_object.add("ptr"sv, static_cast(event.data.kmalloc.ptr))); - TRY(event_object.add("size"sv, static_cast(event.data.kmalloc.size))); - break; - case PERF_EVENT_KFREE: - TRY(event_object.add("type"sv, "kfree")); - TRY(event_object.add("ptr"sv, static_cast(event.data.kfree.ptr))); - TRY(event_object.add("size"sv, static_cast(event.data.kfree.size))); - break; - case PERF_EVENT_PAGE_FAULT: - TRY(event_object.add("type"sv, "page_fault")); - break; - case PERF_EVENT_SYSCALL: - TRY(event_object.add("type"sv, "syscall")); - break; - case PERF_EVENT_SIGNPOST: - TRY(event_object.add("type"sv, "signpost"sv)); - TRY(event_object.add("arg1"sv, event.data.signpost.arg1)); - TRY(event_object.add("arg2"sv, event.data.signpost.arg2)); - break; - case PERF_EVENT_FILESYSTEM: - TRY(event_object.add("type"sv, "filesystem"sv)); - TRY(event_object.add("durationNs"sv, event.data.filesystem.durationNs)); - switch (event.data.filesystem.type) { - case FilesystemEventType::Open: { - auto const& open = event.data.filesystem.data.open; - TRY(event_object.add("fs_event_type"sv, "open"sv)); - TRY(event_object.add("dirfd"sv, open.dirfd)); - TRY(event_object.add("filename_index"sv, open.filename_index)); - TRY(event_object.add("options"sv, open.options)); - TRY(event_object.add("mode"sv, open.mode)); - break; - } - case FilesystemEventType::Close: { - auto const& close = event.data.filesystem.data.close; - TRY(event_object.add("fs_event_type"sv, "close"sv)); - TRY(event_object.add("fd"sv, close.fd)); - TRY(event_object.add("filename_index"sv, close.filename_index)); - break; - } - case FilesystemEventType::Readv: { - auto const& readv = event.data.filesystem.data.readv; - TRY(event_object.add("fs_event_type"sv, "readv"sv)); - TRY(event_object.add("fd"sv, readv.fd)); - TRY(event_object.add("filename_index"sv, readv.filename_index)); - break; - } - case FilesystemEventType::Read: { - auto const& read = event.data.filesystem.data.read; - TRY(event_object.add("fs_event_type"sv, "read"sv)); - TRY(event_object.add("fd"sv, read.fd)); - TRY(event_object.add("filename_index"sv, read.filename_index)); - break; - } - case FilesystemEventType::Pread: { - auto const& pread = event.data.filesystem.data.pread; - TRY(event_object.add("fs_event_type"sv, "pread"sv)); - TRY(event_object.add("fd"sv, pread.fd)); - TRY(event_object.add("filename_index"sv, pread.filename_index)); - TRY(event_object.add("buffer_ptr"sv, pread.buffer_ptr)); - TRY(event_object.add("size"sv, pread.size)); - TRY(event_object.add("offset"sv, pread.offset)); - break; - } - } - break; - } - TRY(event_object.add("pid"sv, event.pid)); - TRY(event_object.add("tid"sv, event.tid)); - TRY(event_object.add("timestamp"sv, event.timestamp)); - TRY(event_object.add("lost_samples"sv, seen_first_sample ? event.lost_samples : 0)); - if (event.type == PERF_EVENT_SAMPLE) - seen_first_sample = true; - auto stack_array = TRY(event_object.add_array("stack"sv)); - for (size_t j = 0; j < event.stack_size; ++j) { - auto address = event.stack[j]; - if (!show_kernel_addresses && !Memory::is_user_address(VirtualAddress { address })) - address = 0xdeadc0de; - TRY(stack_array.add(address)); - } - TRY(stack_array.finish()); - TRY(event_object.finish()); - } - TRY(array.finish()); - TRY(object.finish()); - return {}; -} - -ErrorOr PerformanceEventBuffer::to_json(KBufferBuilder& builder) const -{ - auto object = TRY(JsonObjectSerializer<>::try_create(builder)); - return to_json_impl(object); -} - -OwnPtr PerformanceEventBuffer::try_create_with_size(size_t buffer_size) -{ - auto buffer_or_error = KBuffer::try_create_with_size("Performance events"sv, buffer_size, Memory::Region::Access::ReadWrite, AllocationStrategy::AllocateNow); - if (buffer_or_error.is_error()) - return {}; - return adopt_own_if_nonnull(new (nothrow) PerformanceEventBuffer(buffer_or_error.release_value())); -} - -ErrorOr PerformanceEventBuffer::add_process(Process const& process, ProcessEventType event_type) -{ - OwnPtr executable; - if (process.executable()) { - executable = TRY(process.executable()->try_serialize_absolute_path()); - } else { - executable = TRY(process.name().with([&](auto& process_name) { - return KString::formatted("<{}>", process_name.representable_view()); - })); - } - - TRY(append_with_ip_and_bp(process.pid(), 0, 0, 0, - event_type == ProcessEventType::Create ? PERF_EVENT_PROCESS_CREATE : PERF_EVENT_PROCESS_EXEC, - 0, process.pid().value(), 0, executable->view())); - - ErrorOr result; - process.for_each_thread([&](auto& thread) { - result = append_with_ip_and_bp(process.pid(), thread.tid().value(), - 0, 0, PERF_EVENT_THREAD_CREATE, 0, 0, 0, {}); - return result.is_error() ? IterationDecision::Break : IterationDecision::Continue; - }); - TRY(result); - - return process.address_space().with([&](auto& space) -> ErrorOr { - for (auto const& region : space->region_tree().regions()) { - TRY(append_with_ip_and_bp(process.pid(), 0, - 0, 0, PERF_EVENT_MMAP, 0, region.range().base().get(), region.range().size(), region.name())); - } - return {}; - }); -} - -ErrorOr PerformanceEventBuffer::register_string(NonnullOwnPtr string) -{ - return m_strings.with([&](auto& m_strings) -> ErrorOr { - auto it = m_strings.find(string); - if (it != m_strings.end()) { - return it->value; - } - - auto new_index = m_strings.size(); - TRY(m_strings.try_set(move(string), move(new_index))); - return new_index; - }); -} - -} diff --git a/Kernel/Tasks/PerformanceEventBuffer.h b/Kernel/Tasks/PerformanceEventBuffer.h deleted file mode 100644 index e7fb5962f56..00000000000 --- a/Kernel/Tasks/PerformanceEventBuffer.h +++ /dev/null @@ -1,216 +0,0 @@ -/* - * Copyright (c) 2020, Andreas Kling - * Copyright (c) 2023, Jakub Berkop - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include - -namespace Kernel { - -class KBufferBuilder; -struct RegisterState; - -struct [[gnu::packed]] MallocPerformanceEvent { - size_t size; - FlatPtr ptr; -}; - -struct [[gnu::packed]] FreePerformanceEvent { - size_t size; - FlatPtr ptr; -}; - -struct [[gnu::packed]] MmapPerformanceEvent { - size_t size; - FlatPtr ptr; - char name[64]; -}; - -struct [[gnu::packed]] MunmapPerformanceEvent { - size_t size; - FlatPtr ptr; -}; - -struct [[gnu::packed]] ProcessCreatePerformanceEvent { - pid_t parent_pid; - char executable[64]; -}; - -struct [[gnu::packed]] ProcessExecPerformanceEvent { - char executable[64]; -}; - -struct [[gnu::packed]] ThreadCreatePerformanceEvent { - pid_t parent_tid; -}; - -struct [[gnu::packed]] ContextSwitchPerformanceEvent { - pid_t next_pid; - u32 next_tid; -}; - -struct [[gnu::packed]] KMallocPerformanceEvent { - size_t size; - FlatPtr ptr; -}; - -struct [[gnu::packed]] KFreePerformanceEvent { - size_t size; - FlatPtr ptr; -}; - -struct [[gnu::packed]] SignpostPerformanceEvent { - FlatPtr arg1; - FlatPtr arg2; -}; - -struct [[gnu::packed]] ReadPerformanceEvent { - int fd; - size_t size; - size_t filename_index; - size_t start_timestamp; - bool success; -}; - -enum class FilesystemEventType : u8 { - Open, - Close, - Readv, - Read, - Pread -}; - -struct [[gnu::packed]] OpenEventData { - int dirfd; - size_t filename_index; - int options; - u64 mode; -}; - -struct [[gnu::packed]] CloseEventData { - int fd; - size_t filename_index; -}; - -struct [[gnu::packed]] ReadvEventData { - int fd; - size_t filename_index; - // struct iovec* iov; // TODO: Implement - // int iov_count; // TODO: Implement -}; - -struct [[gnu::packed]] ReadEventData { - int fd; - size_t filename_index; -}; - -struct [[gnu::packed]] PreadEventData { - int fd; - size_t filename_index; - FlatPtr buffer_ptr; - size_t size; - off_t offset; -}; - -// FIXME: This is a hack to make the compiler pack this struct correctly. -struct [[gnu::packed]] PackedErrorOr { - bool is_error; - FlatPtr value; -}; - -struct [[gnu::packed]] FilesystemEvent { - FilesystemEventType type; - u64 durationNs; - PackedErrorOr result; - - union { - OpenEventData open; - CloseEventData close; - ReadvEventData readv; - ReadEventData read; - PreadEventData pread; - } data; -}; - -struct [[gnu::packed]] PerformanceEvent { - u32 type { 0 }; - u8 stack_size { 0 }; - u32 pid { 0 }; - u32 tid { 0 }; - u64 timestamp; - u32 lost_samples; - union { - MallocPerformanceEvent malloc; - FreePerformanceEvent free; - MmapPerformanceEvent mmap; - MunmapPerformanceEvent munmap; - ProcessCreatePerformanceEvent process_create; - ProcessExecPerformanceEvent process_exec; - ThreadCreatePerformanceEvent thread_create; - ContextSwitchPerformanceEvent context_switch; - KMallocPerformanceEvent kmalloc; - KFreePerformanceEvent kfree; - SignpostPerformanceEvent signpost; - FilesystemEvent filesystem; - } data; - static constexpr size_t max_stack_frame_count = 64; - FlatPtr stack[max_stack_frame_count]; -}; - -enum class ProcessEventType { - Create, - Exec -}; - -class PerformanceEventBuffer { -public: - static OwnPtr try_create_with_size(size_t buffer_size); - - ErrorOr append(int type, FlatPtr arg1, FlatPtr arg2, StringView arg3, Thread* current_thread = Thread::current(), FilesystemEvent filesystem_event = {}); - ErrorOr append_with_ip_and_bp(ProcessID pid, ThreadID tid, FlatPtr eip, FlatPtr ebp, - int type, u32 lost_samples, FlatPtr arg1, FlatPtr arg2, StringView arg3, FilesystemEvent filesystem_event = {}); - ErrorOr append_with_ip_and_bp(ProcessID pid, ThreadID tid, RegisterState const& regs, - int type, u32 lost_samples, FlatPtr arg1, FlatPtr arg2, StringView arg3, FilesystemEvent filesystem_event = {}); - - void clear() - { - m_count = 0; - } - - size_t capacity() const { return m_buffer->size() / sizeof(PerformanceEvent); } - size_t count() const { return m_count; } - PerformanceEvent const& at(size_t index) const - { - return const_cast(*this).at(index); - } - - ErrorOr to_json(KBufferBuilder&) const; - - ErrorOr add_process(Process const&, ProcessEventType event_type); - - ErrorOr register_string(NonnullOwnPtr); - -private: - explicit PerformanceEventBuffer(NonnullOwnPtr); - - template - ErrorOr to_json_impl(Serializer&) const; - - PerformanceEvent& at(size_t index); - - size_t m_count { 0 }; - NonnullOwnPtr m_buffer; - - SpinlockProtected, size_t>, LockRank::None> m_strings; -}; - -extern bool g_profiling_all_threads; -extern PerformanceEventBuffer* g_global_perf_events; -extern u64 g_profiling_event_mask; - -} diff --git a/Kernel/Tasks/PerformanceManager.h b/Kernel/Tasks/PerformanceManager.h deleted file mode 100644 index ce30a206a82..00000000000 --- a/Kernel/Tasks/PerformanceManager.h +++ /dev/null @@ -1,151 +0,0 @@ -/* - * Copyright (c) 2021, Brian Gianforcaro - * Copyright (c) 2023, Jakub Berkop - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include - -namespace Kernel { - -class PerformanceManager { -public: - static void add_process_created_event(Process& process) - { - if (g_profiling_all_threads) { - VERIFY(g_global_perf_events); - (void)g_global_perf_events->add_process(process, ProcessEventType::Create); - } - } - - static void add_process_exec_event(Process& process) - { - if (auto* event_buffer = process.current_perf_events_buffer()) { - (void)event_buffer->add_process(process, ProcessEventType::Exec); - } - } - - static void add_process_exit_event(Process& process) - { - if (g_profiling_all_threads) { - VERIFY(g_global_perf_events); - [[maybe_unused]] auto rc = g_global_perf_events->append_with_ip_and_bp( - process.pid(), 0, 0, 0, PERF_EVENT_PROCESS_EXIT, 0, 0, 0, {}); - } - } - - static void add_thread_created_event(Thread& thread) - { - if (thread.is_profiling_suppressed()) - return; - if (auto* event_buffer = thread.process().current_perf_events_buffer()) { - [[maybe_unused]] auto rc = event_buffer->append(PERF_EVENT_THREAD_CREATE, thread.tid().value(), 0, {}, &thread); - } - } - - static void add_thread_exit_event(Thread& thread) - { - // As an exception this doesn't check whether profiling is suppressed for - // the thread so we can record the thread_exit event anyway. - if (auto* event_buffer = thread.process().current_perf_events_buffer()) { - [[maybe_unused]] auto rc = event_buffer->append(PERF_EVENT_THREAD_EXIT, thread.tid().value(), 0, {}, &thread); - } - } - - static void add_cpu_sample_event(Thread& current_thread, RegisterState const& regs, u32 lost_time) - { - if (current_thread.is_profiling_suppressed()) - return; - if (auto* event_buffer = current_thread.process().current_perf_events_buffer()) { - [[maybe_unused]] auto rc = event_buffer->append_with_ip_and_bp( - current_thread.pid(), current_thread.tid(), regs, PERF_EVENT_SAMPLE, lost_time, 0, 0, {}); - } - } - - static void add_mmap_perf_event(Process& current_process, Memory::Region const& region) - { - if (auto* event_buffer = current_process.current_perf_events_buffer()) { - [[maybe_unused]] auto res = event_buffer->append(PERF_EVENT_MMAP, region.vaddr().get(), region.size(), region.name()); - } - } - - static void add_unmap_perf_event(Process& current_process, Memory::VirtualRange const& region) - { - if (auto* event_buffer = current_process.current_perf_events_buffer()) { - [[maybe_unused]] auto res = event_buffer->append(PERF_EVENT_MUNMAP, region.base().get(), region.size(), {}); - } - } - - static void add_context_switch_perf_event(Thread& current_thread, Thread& next_thread) - { - if (current_thread.is_profiling_suppressed()) - return; - if (auto* event_buffer = current_thread.process().current_perf_events_buffer()) { - [[maybe_unused]] auto res = event_buffer->append(PERF_EVENT_CONTEXT_SWITCH, next_thread.pid().value(), next_thread.tid().value(), {}); - } - } - - static void add_kmalloc_perf_event(Thread& current_thread, size_t size, FlatPtr ptr) - { - if (current_thread.is_profiling_suppressed()) - return; - if (auto* event_buffer = current_thread.process().current_perf_events_buffer()) { - [[maybe_unused]] auto res = event_buffer->append(PERF_EVENT_KMALLOC, size, ptr, {}); - } - } - - static void add_kfree_perf_event(Thread& current_thread, size_t size, FlatPtr ptr) - { - if (current_thread.is_profiling_suppressed()) - return; - if (auto* event_buffer = current_thread.process().current_perf_events_buffer()) { - [[maybe_unused]] auto res = event_buffer->append(PERF_EVENT_KFREE, size, ptr, {}); - } - } - - static void add_page_fault_event(Thread& thread, RegisterState const& regs) - { - if (thread.is_profiling_suppressed()) - return; - if (auto* event_buffer = thread.process().current_perf_events_buffer()) { - [[maybe_unused]] auto rc = event_buffer->append_with_ip_and_bp( - thread.pid(), thread.tid(), regs, PERF_EVENT_PAGE_FAULT, 0, 0, 0, {}); - } - } - - static void add_syscall_event(Thread& thread, RegisterState const& regs) - { - if (thread.is_profiling_suppressed()) - return; - if (auto* event_buffer = thread.process().current_perf_events_buffer()) { - [[maybe_unused]] auto rc = event_buffer->append_with_ip_and_bp( - thread.pid(), thread.tid(), regs, PERF_EVENT_SYSCALL, 0, 0, 0, {}); - } - } - - static void timer_tick(RegisterState const& regs) - { - static UnixDateTime last_wakeup; - auto now = kgettimeofday(); - constexpr auto ideal_interval = Duration::from_microseconds(1000'000 / OPTIMAL_PROFILE_TICKS_PER_SECOND_RATE); - auto expected_wakeup = last_wakeup + ideal_interval; - auto delay = (now > expected_wakeup) ? now - expected_wakeup : Duration::from_microseconds(0); - last_wakeup = now; - auto* current_thread = Thread::current(); - // FIXME: We currently don't collect samples while idle. - // That will be an interesting mode to add in the future. :^) - if (!current_thread || current_thread == Processor::idle_thread()) - return; - - auto lost_samples = delay.to_microseconds() / ideal_interval.to_microseconds(); - PerformanceManager::add_cpu_sample_event(*current_thread, regs, lost_samples); - } -}; - -} diff --git a/Kernel/Tasks/PowerStateSwitchTask.cpp b/Kernel/Tasks/PowerStateSwitchTask.cpp deleted file mode 100644 index 8fc66a72a56..00000000000 --- a/Kernel/Tasks/PowerStateSwitchTask.cpp +++ /dev/null @@ -1,195 +0,0 @@ -/* - * Copyright (c) 2023, kleines Filmröllchen - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#if ARCH(X86_64) -# include -# include -#elif ARCH(AARCH64) -# include -#endif -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace Kernel { - -Thread* g_power_state_switch_task; -bool g_in_system_shutdown { false }; - -void PowerStateSwitchTask::power_state_switch_task(void* raw_entry_data) -{ - Thread::current()->set_priority(THREAD_PRIORITY_HIGH); - auto entry_data = bit_cast(raw_entry_data); - switch (entry_data) { - case PowerStateCommand::Shutdown: - MUST(PowerStateSwitchTask::perform_shutdown(DoReboot::No)); - break; - case PowerStateCommand::Reboot: - MUST(PowerStateSwitchTask::perform_shutdown(DoReboot::Yes)); - break; - default: - PANIC("Unknown power state command: {}", to_underlying(entry_data)); - } - - // Although common, the system may not halt through this task. - // Clear the power state switch task so that it can be spawned again. - g_power_state_switch_task = nullptr; -} - -void PowerStateSwitchTask::spawn(PowerStateCommand command) -{ - VERIFY(g_power_state_switch_task == nullptr); - auto [_, power_state_switch_task_thread] = MUST(Process::create_kernel_process( - "Power State Switch Task"sv, power_state_switch_task, bit_cast(command))); - g_power_state_switch_task = move(power_state_switch_task_thread); -} - -ErrorOr PowerStateSwitchTask::perform_shutdown(PowerStateSwitchTask::DoReboot do_reboot) -{ - // We assume that by this point userland has tried as much as possible to shut down everything in an orderly fashion. - // Therefore, we force kill remaining processes, including Kernel processes, except the finalizer and ourselves. - dbgln("Killing remaining processes..."); - Optional finalizer_process; - Process::all_instances().for_each([&](Process& process) { - if (process.pid() == g_finalizer->process().pid()) - finalizer_process = process; - }); - VERIFY(finalizer_process.has_value()); - - // Allow init process and finalizer task to be killed. - g_in_system_shutdown = true; - - // Make sure to kill all user processes first, otherwise we might get weird hangups. - TRY(kill_all_user_processes()); - - size_t alive_process_count = 0; - Process::all_instances().for_each([&](Process& process) { - if (!process.is_kernel_process() && !process.is_dead()) - alive_process_count++; - }); - // Don't panic here (since we may panic in a bit anyways) but report the probable cause of an unclean shutdown. - if (alive_process_count != 0) - dbgln("We're not the last process alive; proper shutdown may fail!"); - - ConsoleManagement::the().switch_to_debug(); - - dbgln("Locking all file systems..."); - FileSystem::lock_all(); - FileSystem::sync(); - - dbgln("Unmounting all file systems..."); - - auto unmount_was_successful = true; - while (unmount_was_successful) { - unmount_was_successful = false; - Vector mounts; - TRY(VirtualFileSystem::the().for_each_mount([&](auto const& mount) -> ErrorOr { - TRY(mounts.try_append(const_cast(mount))); - return {}; - })); - if (mounts.is_empty()) - break; - auto const remaining_mounts = mounts.size(); - - while (!mounts.is_empty()) { - auto& mount = mounts.take_last(); - TRY(mount.guest_fs().flush_writes()); - - auto mount_path = TRY(mount.absolute_path()); - auto& mount_inode = mount.guest(); - auto const result = VirtualFileSystem::the().unmount(mount_inode, mount_path->view()); - if (result.is_error()) { - dbgln("Error during unmount of {}: {}", mount_path, result.error()); - // FIXME: For unknown reasons the root FS stays busy even after everything else has shut down and was unmounted. - // Until we find the underlying issue, allow an unclean shutdown here. - if (remaining_mounts <= 1) - dbgln("BUG! One mount remaining; the root file system may not be unmountable at all. Shutting down anyways."); - } else { - unmount_was_successful = true; - } - } - } - - // NOTE: We don't really need to kill kernel processes, because in contrast - // to user processes, kernel processes will simply not make syscalls - // or do some other unexpected behavior. - // Therefore, we just lock the scheduler big lock to ensure nothing happens - // beyond this point forward. - SpinlockLocker lock(g_scheduler_lock); - - if (do_reboot == DoReboot::Yes) { - dbgln("Attempting system reboot..."); - dbgln("attempting reboot via ACPI"); - if (ACPI::is_enabled()) - ACPI::Parser::the()->try_acpi_reboot(); - arch_specific_reboot(); - - dmesgln("Reboot can't be completed. It's safe to turn off the computer!"); - Processor::halt(); - VERIFY_NOT_REACHED(); - } - VERIFY(do_reboot == DoReboot::No); - - dbgln("Attempting system shutdown..."); - arch_specific_poweroff(); - dmesgln("Shutdown can't be completed. It's safe to turn off the computer!"); - Processor::halt(); - VERIFY_NOT_REACHED(); -} - -ErrorOr PowerStateSwitchTask::kill_all_user_processes() -{ - { - SpinlockLocker lock(g_scheduler_lock); - Process::all_instances().for_each([&](Process& process) { - if (!process.is_kernel_process()) - process.die(); - }); - } - - // Although we *could* finalize processes ourselves (g_in_system_shutdown allows this), - // we're nice citizens and let the finalizer task perform final duties before we kill it. - Scheduler::notify_finalizer(); - int alive_process_count = 1; - MonotonicTime last_status_time = TimeManagement::the().monotonic_time(); - while (alive_process_count > 0) { - Scheduler::yield(); - alive_process_count = 0; - Process::all_instances().for_each([&](Process& process) { - if (!process.is_kernel_process() && !process.is_dead()) - alive_process_count++; - }); - - if (TimeManagement::the().monotonic_time() - last_status_time > Duration::from_seconds(2)) { - last_status_time = TimeManagement::the().monotonic_time(); - dmesgln("Waiting on {} processes to exit...", alive_process_count); - - if constexpr (PROCESS_DEBUG) { - Process::all_instances().for_each_const([&](Process const& process) { - if (!process.is_kernel_process() && !process.is_dead()) { - dbgln("Process (user) {:2} dead={} dying={} ({})", - process.pid(), process.is_dead(), process.is_dying(), - process.name().with([](auto& name) { return name.representable_view(); })); - } - }); - } - } - } - - return {}; -} - -} diff --git a/Kernel/Tasks/PowerStateSwitchTask.h b/Kernel/Tasks/PowerStateSwitchTask.h deleted file mode 100644 index 834bfac0b02..00000000000 --- a/Kernel/Tasks/PowerStateSwitchTask.h +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright (c) 2023, kleines Filmröllchen - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include - -namespace Kernel { - -enum class PowerStateCommand : uintptr_t { - Shutdown, - Reboot, -}; -// We will pass the power state command to the task in place of a void* as to avoid the complications of raw allocations. -static_assert(sizeof(PowerStateCommand) == sizeof(void*)); - -extern bool g_in_system_shutdown; - -class PowerStateSwitchTask { -public: - static void shutdown() { spawn(PowerStateCommand::Shutdown); } - static void reboot() { spawn(PowerStateCommand::Reboot); } - -private: - static void spawn(PowerStateCommand); - - static void power_state_switch_task(void* raw_entry_data); - - static ErrorOr kill_all_user_processes(); - enum class DoReboot { - No, - Yes, - }; - static ErrorOr perform_shutdown(DoReboot); -}; - -} diff --git a/Kernel/Tasks/Process.cpp b/Kernel/Tasks/Process.cpp deleted file mode 100644 index 4e87124ca68..00000000000 --- a/Kernel/Tasks/Process.cpp +++ /dev/null @@ -1,1227 +0,0 @@ -/* - * Copyright (c) 2018-2023, Andreas Kling - * Copyright (c) 2023, Timon Kruiper - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace Kernel { - -static void create_signal_trampoline(); - -extern ProcessID g_init_pid; -extern bool g_in_system_shutdown; -extern KString* g_version_string; - -RecursiveSpinlock g_profiling_lock {}; -static Atomic next_pid; -static Singleton> s_all_instances; -READONLY_AFTER_INIT Memory::Region* g_signal_trampoline_region; - -static Singleton>> s_hostname; - -MutexProtected>& hostname() -{ - return *s_hostname; -} - -SpinlockProtected& Process::all_instances() -{ - return *s_all_instances; -} - -ErrorOr Process::for_each_in_same_jail(Function(Process&)> callback) -{ - return Process::current().m_jail_process_list.with([&](auto const& list_ptr) -> ErrorOr { - ErrorOr result {}; - if (list_ptr) { - list_ptr->attached_processes().with([&](auto const& list) { - for (auto& process : list) { - result = callback(process); - if (result.is_error()) - break; - } - }); - return result; - } - all_instances().with([&](auto const& list) { - for (auto& process : list) { - result = callback(process); - if (result.is_error()) - break; - } - }); - return result; - }); -} - -ErrorOr Process::for_each_child_in_same_jail(Function(Process&)> callback) -{ - ProcessID my_pid = pid(); - return m_jail_process_list.with([&](auto const& list_ptr) -> ErrorOr { - ErrorOr result {}; - if (list_ptr) { - list_ptr->attached_processes().with([&](auto const& list) { - for (auto& process : list) { - if (process.ppid() == my_pid || process.has_tracee_thread(pid())) - result = callback(process); - if (result.is_error()) - break; - } - }); - return result; - } - all_instances().with([&](auto const& list) { - for (auto& process : list) { - if (process.ppid() == my_pid || process.has_tracee_thread(pid())) - result = callback(process); - if (result.is_error()) - break; - } - }); - return result; - }); -} - -ErrorOr Process::for_each_in_pgrp_in_same_jail(ProcessGroupID pgid, Function(Process&)> callback) -{ - return m_jail_process_list.with([&](auto const& list_ptr) -> ErrorOr { - ErrorOr result {}; - if (list_ptr) { - list_ptr->attached_processes().with([&](auto const& list) { - for (auto& process : list) { - if (!process.is_dead() && process.pgid() == pgid) - result = callback(process); - if (result.is_error()) - break; - } - }); - return result; - } - all_instances().with([&](auto const& list) { - for (auto& process : list) { - if (!process.is_dead() && process.pgid() == pgid) - result = callback(process); - if (result.is_error()) - break; - } - }); - return result; - }); -} - -ProcessID Process::allocate_pid() -{ - // Overflow is UB, and negative PIDs wreck havoc. - // TODO: Handle PID overflow - // For example: Use an Atomic, mask the most significant bit, - // retry if PID is already taken as a PID, taken as a TID, - // takes as a PGID, taken as a SID, or zero. - return next_pid.fetch_add(1, AK::MemoryOrder::memory_order_acq_rel); -} - -UNMAP_AFTER_INIT void Process::initialize() -{ - next_pid.store(0, AK::MemoryOrder::memory_order_release); - - // Note: This is called before scheduling is initialized, and before APs are booted. - // So we can "safely" bypass the lock here. - reinterpret_cast&>(hostname()).store_characters("courage"sv); - // NOTE: Just allocate the kernel version string here so we never have to worry - // about OOM conditions in the uname syscall. - g_version_string = MUST(KString::formatted("{}.{}-dev", SERENITY_MAJOR_REVISION, SERENITY_MINOR_REVISION)).leak_ptr(); - - create_signal_trampoline(); -} - -void Process::kill_threads_except_self() -{ - InterruptDisabler disabler; - - if (thread_count() <= 1) - return; - - auto* current_thread = Thread::current(); - for_each_thread([&](Thread& thread) { - if (&thread == current_thread) - return; - - if (auto state = thread.state(); state == Thread::State::Dead - || state == Thread::State::Dying) - return; - - // We need to detach this thread in case it hasn't been joined - thread.detach(); - thread.set_should_die(); - }); - - u32 dropped_lock_count = 0; - if (big_lock().force_unlock_exclusive_if_locked(dropped_lock_count) != LockMode::Unlocked) - dbgln("Process {} big lock had {} locks", *this, dropped_lock_count); -} - -void Process::kill_all_threads() -{ - for_each_thread([&](Thread& thread) { - // We need to detach this thread in case it hasn't been joined - thread.detach(); - thread.set_should_die(); - }); -} - -void Process::register_new(Process& process) -{ - // Note: this is essentially the same like process->ref() - NonnullRefPtr const new_process = process; - all_instances().with([&](auto& list) { - list.prepend(process); - }); -} - -ErrorOr Process::create_user_process(StringView path, UserID uid, GroupID gid, Vector> arguments, Vector> environment, RefPtr tty) -{ - auto parts = path.split_view('/'); - if (arguments.is_empty()) { - auto last_part = TRY(KString::try_create(parts.last())); - TRY(arguments.try_append(move(last_part))); - } - - auto path_string = TRY(KString::try_create(path)); - auto [process, first_thread] = TRY(Process::create(parts.last(), uid, gid, ProcessID(0), false, VirtualFileSystem::the().root_custody(), nullptr, tty)); - - TRY(process->m_fds.with_exclusive([&](auto& fds) -> ErrorOr { - TRY(fds.try_resize(Process::OpenFileDescriptions::max_open())); - - auto& device_to_use_as_tty = tty ? (CharacterDevice&)*tty : DeviceManagement::the().null_device(); - auto description = TRY(device_to_use_as_tty.open(O_RDWR)); - auto setup_description = [&](int fd) { - fds.m_fds_metadatas[fd].allocate(); - fds[fd].set(*description); - }; - setup_description(0); - setup_description(1); - setup_description(2); - - return {}; - })); - - Thread* new_main_thread = nullptr; - InterruptsState previous_interrupts_state = InterruptsState::Enabled; - TRY(process->exec(move(path_string), move(arguments), move(environment), new_main_thread, previous_interrupts_state)); - - register_new(*process); - - // NOTE: All user processes have a leaked ref on them. It's balanced by Thread::WaitBlockerSet::finalize(). - process->ref(); - - { - SpinlockLocker lock(g_scheduler_lock); - new_main_thread->set_state(Thread::State::Runnable); - } - - return ProcessAndFirstThread { move(process), move(first_thread) }; -} - -ErrorOr Process::create_kernel_process(StringView name, void (*entry)(void*), void* entry_data, u32 affinity, RegisterProcess do_register) -{ - auto process_and_first_thread = TRY(Process::create(name, UserID(0), GroupID(0), ProcessID(0), true)); - auto& process = *process_and_first_thread.process; - auto& thread = *process_and_first_thread.first_thread; - - thread.regs().set_entry_function((FlatPtr)entry, (FlatPtr)entry_data); - - if (do_register == RegisterProcess::Yes) - register_new(process); - - SpinlockLocker lock(g_scheduler_lock); - thread.set_affinity(affinity); - thread.set_state(Thread::State::Runnable); - return process_and_first_thread; -} - -void Process::protect_data() -{ - m_protected_data_refs.unref([&]() { - MM.set_page_writable_direct(VirtualAddress { &this->m_protected_values_do_not_access_directly }, false); - }); -} - -void Process::unprotect_data() -{ - m_protected_data_refs.ref([&]() { - MM.set_page_writable_direct(VirtualAddress { &this->m_protected_values_do_not_access_directly }, true); - }); -} - -ErrorOr Process::create_with_forked_name(UserID uid, GroupID gid, ProcessID ppid, bool is_kernel_process, RefPtr current_directory, RefPtr executable, RefPtr tty, Process* fork_parent) -{ - Process::Name name {}; - Process::current().name().with([&name](auto& process_name) { - name.store_characters(process_name.representable_view()); - }); - return TRY(Process::create(name.representable_view(), uid, gid, ppid, is_kernel_process, current_directory, executable, tty, fork_parent)); -} - -ErrorOr Process::create(StringView name, UserID uid, GroupID gid, ProcessID ppid, bool is_kernel_process, RefPtr current_directory, RefPtr executable, RefPtr tty, Process* fork_parent) -{ - auto unveil_tree = UnveilNode { TRY(KString::try_create("/"sv)), UnveilMetadata(TRY(KString::try_create("/"sv))) }; - auto exec_unveil_tree = UnveilNode { TRY(KString::try_create("/"sv)), UnveilMetadata(TRY(KString::try_create("/"sv))) }; - auto credentials = TRY(Credentials::create(uid, gid, uid, gid, uid, gid, {}, fork_parent ? fork_parent->sid() : 0, fork_parent ? fork_parent->pgid() : 0)); - - auto process = TRY(adopt_nonnull_ref_or_enomem(new (nothrow) Process(name, move(credentials), ppid, is_kernel_process, move(current_directory), move(executable), tty, move(unveil_tree), move(exec_unveil_tree), kgettimeofday()))); - - OwnPtr new_address_space; - if (fork_parent) { - TRY(fork_parent->address_space().with([&](auto& parent_address_space) -> ErrorOr { - new_address_space = TRY(Memory::AddressSpace::try_create(*process, parent_address_space.ptr())); - return {}; - })); - } else { - new_address_space = TRY(Memory::AddressSpace::try_create(*process, nullptr)); - } - - auto first_thread = TRY(process->attach_resources(new_address_space.release_nonnull(), fork_parent)); - - return ProcessAndFirstThread { move(process), move(first_thread) }; -} - -Process::Process(StringView name, NonnullRefPtr credentials, ProcessID ppid, bool is_kernel_process, RefPtr current_directory, RefPtr executable, RefPtr tty, UnveilNode unveil_tree, UnveilNode exec_unveil_tree, UnixDateTime creation_time) - : m_is_kernel_process(is_kernel_process) - , m_executable(move(executable)) - , m_current_directory(move(current_directory)) - , m_creation_time(creation_time) - , m_unveil_data(move(unveil_tree)) - , m_exec_unveil_data(move(exec_unveil_tree)) - , m_wait_blocker_set(*this) -{ - set_name(name); - // Ensure that we protect the process data when exiting the constructor. - with_mutable_protected_data([&](auto& protected_data) { - protected_data.pid = allocate_pid(); - protected_data.ppid = ppid; - protected_data.credentials = move(credentials); - protected_data.tty = move(tty); - }); - - if constexpr (PROCESS_DEBUG) { - this->name().with([&](auto& process_name) { - dbgln("Created new process {}({})", process_name.representable_view(), this->pid().value()); - }); - } -} - -ErrorOr> Process::attach_resources(NonnullOwnPtr&& preallocated_space, Process* fork_parent) -{ - m_space.with([&](auto& space) { - space = move(preallocated_space); - }); - - auto create_first_thread = [&] { - if (fork_parent) { - // NOTE: fork() doesn't clone all threads; the thread that called fork() becomes the only thread in the new process. - return Thread::current()->clone(*this); - } - // NOTE: This non-forked code path is only taken when the kernel creates a process "manually" (at boot.) - return Thread::create(*this); - }; - - auto first_thread = TRY(create_first_thread()); - - if (!fork_parent) { - // FIXME: Figure out if this is really necessary. - first_thread->detach(); - } - - // This is not actually explicitly verified by any official documentation, - // but it's not listed anywhere as being cleared, and rsync expects it to work like this. - if (fork_parent) - m_signal_action_data = fork_parent->m_signal_action_data; - - return first_thread; -} - -Process::~Process() -{ - unprotect_data(); - - VERIFY(thread_count() == 0); // all threads should have been finalized - - PerformanceManager::add_process_exit_event(*this); -} - -// Make sure the compiler doesn't "optimize away" this function: -extern void signal_trampoline_dummy() __attribute__((used)); -void signal_trampoline_dummy() -{ -#if ARCH(X86_64) - // The trampoline preserves the current rax, pushes the signal code and - // then calls the signal handler. We do this because, when interrupting a - // blocking syscall, that syscall may return some special error code in eax; - // This error code would likely be overwritten by the signal handler, so it's - // necessary to preserve it here. - constexpr static auto offset_to_first_register_slot = sizeof(__ucontext) + sizeof(siginfo) + sizeof(FPUState) + 3 * sizeof(FlatPtr); - asm( - ".intel_syntax noprefix\n" - ".globl asm_signal_trampoline\n" - "asm_signal_trampoline:\n" - // stack state: 0, ucontext, signal_info (alignment = 16), fpu_state (alignment = 16), ucontext*, siginfo*, signal, handler - - // Pop the handler into rcx - "pop rcx\n" // save handler - // we have to save rax 'cause it might be the return value from a syscall - "mov [rsp+%P1], rax\n" - // pop signal number into rdi (first param) - "pop rdi\n" - // pop siginfo* into rsi (second param) - "pop rsi\n" - // pop ucontext* into rdx (third param) - "pop rdx\n" - // Note that the stack is currently aligned to 16 bytes as we popped the extra entries above. - // call the signal handler - "call rcx\n" - // Current stack state is just saved_rax, ucontext, signal_info, fpu_state. - // syscall SC_sigreturn - "mov rax, %P0\n" - "syscall\n" - ".globl asm_signal_trampoline_end\n" - "asm_signal_trampoline_end:\n" - ".att_syntax" - : - : "i"(Syscall::SC_sigreturn), - "i"(offset_to_first_register_slot)); -#elif ARCH(AARCH64) - constexpr static auto offset_to_first_register_slot = align_up_to(sizeof(__ucontext) + sizeof(siginfo) + sizeof(FPUState) + 3 * sizeof(FlatPtr), 16); - asm( - ".global asm_signal_trampoline\n" - "asm_signal_trampoline:\n" - // stack state: 0, ucontext, signal_info (alignment = 16), fpu_state (alignment = 16), ucontext*, siginfo*, signal, handler - - // Load the handler address into x3. - "ldr x3, [sp, #0]\n" - // Store x0 (return value from a syscall) into the register slot, such that we can return the correct value in sys$sigreturn. - "str x0, [sp, %[offset_to_first_register_slot]]\n" - // Load the signal number into the first argument. - "ldr x0, [sp, #8]\n" - // Load a pointer to the signal_info structure into the second argument. - "ldr x1, [sp, #16]\n" - // Load a pointer to the ucontext into the third argument. - "ldr x2, [sp, #24]\n" - // Pop the values off the stack. - "add sp, sp, 32\n" - // Call the signal handler. - "blr x3\n" - - // Call sys$sigreturn. - "mov x8, %[sigreturn_syscall_number]\n" - "svc #0\n" - // We should never return, so trap if we do return. - "brk #0\n" - "\n" - ".global asm_signal_trampoline_end\n" - "asm_signal_trampoline_end: \n" ::[sigreturn_syscall_number] "i"(Syscall::SC_sigreturn), - [offset_to_first_register_slot] "i"(offset_to_first_register_slot)); -#elif ARCH(RISCV64) - constexpr static auto offset_to_a0_slot = sizeof(__ucontext) + sizeof(siginfo) + sizeof(FPUState) + 4 * sizeof(FlatPtr); - asm( - ".global asm_signal_trampoline\n" - "asm_signal_trampoline:\n" - // stack state: 0, ucontext, signal_info (alignment = 16), fpu_state (alignment = 16), ucontext*, siginfo*, signal, handler - - // Load the handler address into t0. - "ld t0, 0(sp)\n" - // Store a0 (return value from a syscall) into the register slot, such that we can return the correct value in sys$sigreturn. - "sd a0, %[offset_to_first_register_slot](sp)\n" - // Load the signal number into the first argument. - "ld a0, 8(sp)\n" - // Load a pointer to the signal_info structure into the second argument. - "ld a1, 16(sp)\n" - // Load a pointer to the ucontext into the third argument. - "ld a2, 24(sp)\n" - // Pop the values off the stack. - "addi sp, sp, 32\n" - // Call the signal handler. - "jalr t0\n" - - // Call sys$sigreturn. - "li a7, %[sigreturn_syscall_number]\n" - "ecall\n" - // We should never return, so trap if we do return. - "unimp\n" - - "\n" - ".global asm_signal_trampoline_end\n" - "asm_signal_trampoline_end: \n" ::[sigreturn_syscall_number] "i"(Syscall::SC_sigreturn), - [offset_to_first_register_slot] "i"(offset_to_a0_slot)); -#else -# error Unknown architecture -#endif -} - -extern "C" char const asm_signal_trampoline[]; -extern "C" char const asm_signal_trampoline_end[]; - -void create_signal_trampoline() -{ - // NOTE: We leak this region. - g_signal_trampoline_region = MM.allocate_kernel_region(PAGE_SIZE, "Signal trampolines"sv, Memory::Region::Access::ReadWrite).release_value().leak_ptr(); - g_signal_trampoline_region->set_syscall_region(true); - - size_t trampoline_size = asm_signal_trampoline_end - asm_signal_trampoline; - - u8* code_ptr = (u8*)g_signal_trampoline_region->vaddr().as_ptr(); - memcpy(code_ptr, asm_signal_trampoline, trampoline_size); - - g_signal_trampoline_region->set_writable(false); - g_signal_trampoline_region->remap(); -} - -void Process::crash(int signal, Optional regs, bool out_of_memory) -{ - VERIFY(!is_dead()); - VERIFY(&Process::current() == this); - - auto ip = regs.has_value() ? regs->ip() : 0; - - if (out_of_memory) { - dbgln("\033[31;1mOut of memory\033[m, killing: {}", *this); - } else { - if (ip >= kernel_load_base && g_kernel_symbols_available.was_set()) { - auto const* symbol = symbolicate_kernel_address(ip); - dbgln("\033[31;1m{:p} {} +{}\033[0m\n", ip, (symbol ? symbol->name : "(k?)"), (symbol ? ip - symbol->address : 0)); - } else { - dbgln("\033[31;1m{:p} (?)\033[0m\n", ip); - } -#if ARCH(X86_64) - constexpr bool userspace_backtrace = false; -#elif ARCH(AARCH64) - constexpr bool userspace_backtrace = true; -#elif ARCH(RISCV64) - constexpr bool userspace_backtrace = true; -#else -# error "Unknown architecture" -#endif - if constexpr (userspace_backtrace) { - dbgln("Userspace backtrace:"); - auto bp = regs.has_value() ? regs->bp() : 0; - dump_backtrace_from_base_pointer(bp); - } - - dbgln("Kernel backtrace:"); - dump_backtrace(); - } - with_mutable_protected_data([&](auto& protected_data) { - protected_data.termination_signal = signal; - }); - set_should_generate_coredump(!out_of_memory); - if constexpr (DUMP_REGIONS_ON_CRASH) { - address_space().with([](auto& space) { space->dump_regions(); }); - } - VERIFY(is_user_process()); - die(); - // We can not return from here, as there is nowhere - // to unwind to, so die right away. - Thread::current()->die_if_needed(); - VERIFY_NOT_REACHED(); -} - -#ifdef ENABLE_KERNEL_COVERAGE_COLLECTION -bool Process::is_kcov_busy() -{ - bool is_busy = false; - Kernel::Process::current().for_each_thread([&](auto& thread) { - if (thread.m_kcov_enabled) { - is_busy = true; - return IterationDecision::Break; - } - return IterationDecision::Continue; - }); - return is_busy; -} -#endif - -RefPtr Process::from_pid_in_same_jail(ProcessID pid) -{ - return Process::current().m_jail_process_list.with([&](auto const& list_ptr) -> RefPtr { - if (list_ptr) { - return list_ptr->attached_processes().with([&](auto const& list) -> RefPtr { - for (auto& process : list) { - if (process.pid() == pid) { - return process; - } - } - return {}; - }); - } - return all_instances().with([&](auto const& list) -> RefPtr { - for (auto& process : list) { - if (process.pid() == pid) { - return process; - } - } - return {}; - }); - }); -} - -RefPtr Process::from_pid_ignoring_jails(ProcessID pid) -{ - return all_instances().with([&](auto const& list) -> RefPtr { - for (auto const& process : list) { - if (process.pid() == pid) - return &process; - } - return {}; - }); -} - -Process::OpenFileDescriptionAndFlags const* Process::OpenFileDescriptions::get_if_valid(size_t i) const -{ - if (m_fds_metadatas.size() <= i) - return nullptr; - - if (auto const& metadata = m_fds_metadatas[i]; metadata.is_valid()) - return &metadata; - - return nullptr; -} -Process::OpenFileDescriptionAndFlags* Process::OpenFileDescriptions::get_if_valid(size_t i) -{ - if (m_fds_metadatas.size() <= i) - return nullptr; - - if (auto& metadata = m_fds_metadatas[i]; metadata.is_valid()) - return &metadata; - - return nullptr; -} - -Process::OpenFileDescriptionAndFlags const& Process::OpenFileDescriptions::at(size_t i) const -{ - VERIFY(m_fds_metadatas[i].is_allocated()); - return m_fds_metadatas[i]; -} - -Process::OpenFileDescriptionAndFlags& Process::OpenFileDescriptions::at(size_t i) -{ - VERIFY(m_fds_metadatas[i].is_allocated()); - return m_fds_metadatas[i]; -} - -ErrorOr> Process::OpenFileDescriptions::open_file_description(int fd) const -{ - if (fd < 0) - return EBADF; - if (static_cast(fd) >= m_fds_metadatas.size()) - return EBADF; - RefPtr description = m_fds_metadatas[fd].description(); - if (!description) - return EBADF; - return description.release_nonnull(); -} - -void Process::OpenFileDescriptions::enumerate(Function callback) const -{ - for (auto const& file_description_metadata : m_fds_metadatas) { - callback(file_description_metadata); - } -} - -ErrorOr Process::OpenFileDescriptions::try_enumerate(Function(OpenFileDescriptionAndFlags const&)> callback) const -{ - for (auto const& file_description_metadata : m_fds_metadatas) { - TRY(callback(file_description_metadata)); - } - return {}; -} - -void Process::OpenFileDescriptions::change_each(Function callback) -{ - for (auto& file_description_metadata : m_fds_metadatas) { - callback(file_description_metadata); - } -} - -size_t Process::OpenFileDescriptions::open_count() const -{ - size_t count = 0; - enumerate([&](auto& file_description_metadata) { - if (file_description_metadata.is_valid()) - ++count; - }); - return count; -} - -ErrorOr> Process::get_thread_from_thread_list(pid_t tid) -{ - if (tid < 0) - return ESRCH; - return m_thread_list.with([tid](auto& list) -> ErrorOr> { - for (auto& thread : list) { - if (thread.tid() == tid) - return thread; - } - return ESRCH; - }); -} - -ErrorOr Process::OpenFileDescriptions::allocate(int first_candidate_fd) -{ - for (size_t i = first_candidate_fd; i < max_open(); ++i) { - if (!m_fds_metadatas[i].is_allocated()) { - m_fds_metadatas[i].allocate(); - return Process::ScopedDescriptionAllocation { static_cast(i), &m_fds_metadatas[i] }; - } - } - return EMFILE; -} - -UnixDateTime kgettimeofday() -{ - return TimeManagement::now(); -} - -siginfo_t Process::wait_info() const -{ - auto credentials = this->credentials(); - siginfo_t siginfo {}; - siginfo.si_signo = SIGCHLD; - siginfo.si_pid = pid().value(); - siginfo.si_uid = credentials->uid().value(); - - with_protected_data([&](auto& protected_data) { - if (protected_data.termination_signal != 0) { - siginfo.si_status = protected_data.termination_signal; - siginfo.si_code = CLD_KILLED; - } else { - siginfo.si_status = protected_data.termination_status; - siginfo.si_code = CLD_EXITED; - } - }); - return siginfo; -} - -NonnullRefPtr Process::current_directory() -{ - return m_current_directory.with([&](auto& current_directory) -> NonnullRefPtr { - if (!current_directory) - current_directory = VirtualFileSystem::the().root_custody(); - return *current_directory; - }); -} - -ErrorOr> Process::get_syscall_path_argument(Userspace user_path, size_t path_length) -{ - if (path_length == 0) - return EINVAL; - if (path_length > PATH_MAX) - return ENAMETOOLONG; - return try_copy_kstring_from_user(user_path, path_length); -} - -ErrorOr> Process::get_syscall_path_argument(Syscall::StringArgument const& path) -{ - Userspace path_characters((FlatPtr)path.characters); - return get_syscall_path_argument(path_characters, path.length); -} - -ErrorOr Process::dump_core() -{ - VERIFY(is_dumpable()); - VERIFY(should_generate_coredump()); - dbgln("Generating coredump for pid: {}", pid().value()); - auto coredump_directory_path = TRY(Coredump::directory_path().with([&](auto& coredump_directory_path) -> ErrorOr> { - if (coredump_directory_path) - return KString::try_create(coredump_directory_path->view()); - return KString::try_create(""sv); - })); - if (coredump_directory_path->view() == ""sv) { - dbgln("Generating coredump for pid {} failed because coredump directory was not set.", pid().value()); - return {}; - } - auto coredump_path = TRY(name().with([&](auto& process_name) { - return KString::formatted("{}/{}_{}_{}", coredump_directory_path->view(), process_name.representable_view(), pid().value(), kgettimeofday().seconds_since_epoch()); - })); - auto coredump = TRY(Coredump::try_create(*this, coredump_path->view())); - return coredump->write(); -} - -ErrorOr Process::dump_perfcore() -{ - VERIFY(is_dumpable()); - VERIFY(m_perf_event_buffer); - dbgln("Generating perfcore for pid: {}", pid().value()); - - // Try to generate a filename which isn't already used. - auto base_filename = TRY(name().with([&](auto& process_name) { - return KString::formatted("{}_{}", process_name.representable_view(), pid().value()); - })); - auto perfcore_filename = TRY(KString::formatted("{}.profile", base_filename)); - RefPtr description; - auto credentials = this->credentials(); - for (size_t attempt = 1; attempt <= 10; ++attempt) { - auto description_or_error = VirtualFileSystem::the().open(*this, credentials, perfcore_filename->view(), O_CREAT | O_EXCL, 0400, current_directory(), UidAndGid { 0, 0 }); - if (!description_or_error.is_error()) { - description = description_or_error.release_value(); - break; - } - perfcore_filename = TRY(KString::formatted("{}.{}.profile", base_filename, attempt)); - } - if (!description) { - dbgln("Failed to generate perfcore for pid {}: Could not generate filename for the perfcore file.", pid().value()); - return EEXIST; - } - - auto builder = TRY(KBufferBuilder::try_create()); - TRY(m_perf_event_buffer->to_json(builder)); - - auto json = builder.build(); - if (!json) { - dbgln("Failed to generate perfcore for pid {}: Could not allocate buffer.", pid().value()); - return ENOMEM; - } - auto json_buffer = UserOrKernelBuffer::for_kernel_buffer(json->data()); - TRY(description->write(json_buffer, json->size())); - - dbgln("Wrote perfcore for pid {} to {}", pid().value(), perfcore_filename); - return {}; -} - -void Process::finalize() -{ - if (!g_in_system_shutdown) - VERIFY(Thread::current() == g_finalizer); - - dbgln_if(PROCESS_DEBUG, "Finalizing process {}", *this); - - if (veil_state() == VeilState::Dropped) { - name().with([&](auto& process_name) { - dbgln("\x1b[01;31mProcess '{}' exited with the veil left open\x1b[0m", process_name.representable_view()); - }); - } - - if (g_init_pid != 0 && pid() == g_init_pid) { - if (g_in_system_shutdown) - dbgln("Init process quitting for shutdown."); - else - PANIC("Init process quit unexpectedly. Exit code: {}", termination_status()); - } - - if (is_dumpable()) { - if (m_should_generate_coredump) { - auto result = dump_core(); - if (result.is_error()) { - dmesgln("Failed to write coredump for pid {}: {}", pid(), result.error()); - } - } - if (m_perf_event_buffer) { - auto result = dump_perfcore(); - if (result.is_error()) - dmesgln("Failed to write perfcore for pid {}: {}", pid(), result.error()); - TimeManagement::the().disable_profile_timer(); - } - } - - m_threads_for_coredump.clear(); - - m_alarm_timer.with([&](auto& timer) { - if (timer) - TimerQueue::the().cancel_timer(timer.release_nonnull()); - }); - m_fds.with_exclusive([](auto& fds) { fds.clear(); }); - with_mutable_protected_data([&](auto& protected_data) { protected_data.tty = nullptr; }); - m_executable.with([](auto& executable) { executable = nullptr; }); - m_attached_jail.with([](auto& jail) { - if (jail) - jail->detach({}); - jail = nullptr; - }); - m_arguments.clear(); - m_environment.clear(); - - m_state.store(State::Dead, AK::MemoryOrder::memory_order_release); - - { - if (auto parent_process = Process::from_pid_ignoring_jails(ppid())) { - if (parent_process->is_user_process() && (parent_process->m_signal_action_data[SIGCHLD].flags & SA_NOCLDWAIT) != SA_NOCLDWAIT) - (void)parent_process->send_signal(SIGCHLD, this); - } - } - - if (!!ppid()) { - if (auto parent = Process::from_pid_ignoring_jails(ppid())) { - parent->m_ticks_in_user_for_dead_children += m_ticks_in_user + m_ticks_in_user_for_dead_children; - parent->m_ticks_in_kernel_for_dead_children += m_ticks_in_kernel + m_ticks_in_kernel_for_dead_children; - } - } - - unblock_waiters(Thread::WaitBlocker::UnblockFlags::Terminated); - - m_space.with([](auto& space) { space->remove_all_regions({}); }); - - VERIFY(ref_count() > 0); - // WaitBlockerSet::finalize will be in charge of dropping the last - // reference if there are still waiters around, or whenever the last - // waitable states are consumed. Unless there is no parent around - // anymore, in which case we'll just drop it right away. - m_wait_blocker_set.finalize(); -} - -void Process::disowned_by_waiter(Process& process) -{ - m_wait_blocker_set.disowned_by_waiter(process); -} - -void Process::unblock_waiters(Thread::WaitBlocker::UnblockFlags flags, u8 signal) -{ - RefPtr waiter_process; - if (auto* my_tracer = tracer()) - waiter_process = Process::from_pid_ignoring_jails(my_tracer->tracer_pid()); - else - waiter_process = Process::from_pid_ignoring_jails(ppid()); - - if (waiter_process) - waiter_process->m_wait_blocker_set.unblock(*this, flags, signal); -} - -void Process::remove_from_secondary_lists() -{ - m_jail_process_list.with([this](auto& list_ptr) { - if (list_ptr) { - list_ptr->attached_processes().with([&](auto& list) { - list.remove(*this); - }); - } - }); -} - -void Process::die() -{ - auto expected = State::Running; - if (!m_state.compare_exchange_strong(expected, State::Dying, AK::memory_order_acquire)) { - // It's possible that another thread calls this at almost the same time - // as we can't always instantly kill other threads (they may be blocked) - // So if we already were called then other threads should stop running - // momentarily and we only really need to service the first thread - return; - } - - // Let go of the TTY, otherwise a slave PTY may keep the master PTY from - // getting an EOF when the last process using the slave PTY dies. - // If the master PTY owner relies on an EOF to know when to wait() on a - // slave owner, we have to allow the PTY pair to be torn down. - with_mutable_protected_data([&](auto& protected_data) { protected_data.tty = nullptr; }); - - VERIFY(m_threads_for_coredump.is_empty()); - for_each_thread([&](auto& thread) { - auto result = m_threads_for_coredump.try_append(thread); - if (result.is_error()) - dbgln("Failed to add thread {} to coredump due to OOM", thread.tid()); - }); - - all_instances().with([&](auto const& list) { - for (auto it = list.begin(); it != list.end();) { - auto& process = *it; - ++it; - if (process.has_tracee_thread(pid())) { - if constexpr (PROCESS_DEBUG) { - process.name().with([&](auto& process_name) { - name().with([&](auto& name) { - dbgln("Process {} ({}) is attached by {} ({}) which will exit", process_name.representable_view(), process.pid(), name.representable_view(), pid()); - }); - }); - } - process.stop_tracing(); - auto err = process.send_signal(SIGSTOP, this); - if (err.is_error()) { - process.name().with([&](auto& process_name) { - dbgln("Failed to send the SIGSTOP signal to {} ({})", process_name.representable_view(), process.pid()); - }); - } - } - } - }); - - kill_all_threads(); -} - -void Process::terminate_due_to_signal(u8 signal) -{ - VERIFY_INTERRUPTS_DISABLED(); - VERIFY(signal < NSIG); - VERIFY(&Process::current() == this); - dbgln("Terminating {} due to signal {}", *this, signal); - with_mutable_protected_data([&](auto& protected_data) { - protected_data.termination_status = 0; - protected_data.termination_signal = signal; - }); - die(); -} - -ErrorOr Process::send_signal(u8 signal, Process* sender) -{ - VERIFY(is_user_process()); - // Try to send it to the "obvious" main thread: - auto receiver_thread = Thread::from_tid_in_same_jail(pid().value()); - // If the main thread has died, there may still be other threads: - if (!receiver_thread) { - // The first one should be good enough. - // Neither kill(2) nor kill(3) specify any selection procedure. - for_each_thread([&receiver_thread](Thread& thread) -> IterationDecision { - receiver_thread = &thread; - return IterationDecision::Break; - }); - } - if (receiver_thread) { - receiver_thread->send_signal(signal, sender); - return {}; - } - return ESRCH; -} - -ErrorOr> Process::create_kernel_thread(void (*entry)(void*), void* entry_data, u32 priority, StringView name, u32 affinity, bool joinable) -{ - VERIFY((priority >= THREAD_PRIORITY_MIN) && (priority <= THREAD_PRIORITY_MAX)); - - // FIXME: Do something with guard pages? - - auto thread = TRY(Thread::create(*this)); - thread->set_name(name); - thread->set_affinity(affinity); - thread->set_priority(priority); - if (!joinable) - thread->detach(); - - auto& regs = thread->regs(); - regs.set_ip((FlatPtr)entry); - regs.set_sp((FlatPtr)entry_data); // entry function argument is expected to be in the SP register - - SpinlockLocker lock(g_scheduler_lock); - thread->set_state(Thread::State::Runnable); - return thread; -} - -void Process::OpenFileDescriptionAndFlags::clear() -{ - m_description = nullptr; - m_flags = 0; -} - -void Process::OpenFileDescriptionAndFlags::set(NonnullRefPtr description, u32 flags) -{ - m_description = move(description); - m_flags = flags; -} - -RefPtr Process::tty() -{ - return with_protected_data([&](auto& protected_data) { return protected_data.tty; }); -} - -RefPtr Process::tty() const -{ - return with_protected_data([&](auto& protected_data) { return protected_data.tty; }); -} - -void Process::set_tty(RefPtr new_tty) -{ - with_mutable_protected_data([&](auto& protected_data) { protected_data.tty = move(new_tty); }); -} - -ErrorOr Process::start_tracing_from(ProcessID tracer) -{ - m_tracer = TRY(ThreadTracer::try_create(tracer)); - return {}; -} - -void Process::stop_tracing() -{ - m_tracer = nullptr; -} - -void Process::tracer_trap(Thread& thread, RegisterState const& regs) -{ - VERIFY(m_tracer.ptr()); - m_tracer->set_regs(regs); - thread.send_urgent_signal_to_self(SIGTRAP); -} - -bool Process::create_perf_events_buffer_if_needed() -{ - if (m_perf_event_buffer) - return true; - m_perf_event_buffer = PerformanceEventBuffer::try_create_with_size(4 * MiB); - if (!m_perf_event_buffer) - return false; - return !m_perf_event_buffer->add_process(*this, ProcessEventType::Create).is_error(); -} - -void Process::delete_perf_events_buffer() -{ - if (m_perf_event_buffer) - m_perf_event_buffer = nullptr; -} - -bool Process::remove_thread(Thread& thread) -{ - u32 thread_count_before = 0; - thread_list().with([&](auto& thread_list) { - thread_list.remove(thread); - with_mutable_protected_data([&](auto& protected_data) { - thread_count_before = protected_data.thread_count.fetch_sub(1, AK::MemoryOrder::memory_order_acq_rel); - VERIFY(thread_count_before != 0); - }); - }); - return thread_count_before == 1; -} - -bool Process::add_thread(Thread& thread) -{ - bool is_first = false; - thread_list().with([&](auto& thread_list) { - thread_list.append(thread); - with_mutable_protected_data([&](auto& protected_data) { - is_first = protected_data.thread_count.fetch_add(1, AK::MemoryOrder::memory_order_relaxed) == 0; - }); - }); - return is_first; -} - -ErrorOr Process::set_coredump_property(NonnullOwnPtr key, NonnullOwnPtr value) -{ - return m_coredump_properties.with([&](auto& coredump_properties) -> ErrorOr { - // Write it into the first available property slot. - for (auto& slot : coredump_properties) { - if (slot.key) - continue; - slot.key = move(key); - slot.value = move(value); - return {}; - } - - return ENOBUFS; - }); -} - -ErrorOr Process::try_set_coredump_property(StringView key, StringView value) -{ - auto key_kstring = TRY(KString::try_create(key)); - auto value_kstring = TRY(KString::try_create(value)); - return set_coredump_property(move(key_kstring), move(value_kstring)); -} - -static constexpr StringView to_string(Pledge promise) -{ -#define __ENUMERATE_PLEDGE_PROMISE(x) \ - case Pledge::x: \ - return #x##sv; - switch (promise) { - ENUMERATE_PLEDGE_PROMISES - } -#undef __ENUMERATE_PLEDGE_PROMISE - VERIFY_NOT_REACHED(); -} - -ErrorOr Process::require_no_promises() const -{ - if (!has_promises()) - return {}; - dbgln("Has made a promise"); - Thread::current()->set_promise_violation_pending(true); - return EPROMISEVIOLATION; -} - -ErrorOr Process::require_promise(Pledge promise) -{ - if (!has_promises()) - return {}; - - if (has_promised(promise)) - return {}; - - dbgln("\033[31;1mProcess has not pledged '{}'\033[0m", to_string(promise)); - Thread::current()->set_promise_violation_pending(true); - (void)try_set_coredump_property("pledge_violation"sv, to_string(promise)); - return EPROMISEVIOLATION; -} - -NonnullRefPtr Process::credentials() const -{ - return with_protected_data([&](auto& protected_data) -> NonnullRefPtr { - return *protected_data.credentials; - }); -} - -RefPtr Process::executable() -{ - return m_executable.with([](auto& executable) { return executable; }); -} - -RefPtr Process::executable() const -{ - return m_executable.with([](auto& executable) { return executable; }); -} - -ErrorOr> Process::custody_for_dirfd(Badge, int dirfd) -{ - return custody_for_dirfd(dirfd); -} - -ErrorOr> Process::custody_for_dirfd(int dirfd) -{ - if (dirfd == AT_FDCWD) - return current_directory(); - auto description = TRY(open_file_description(dirfd)); - if (!description->custody()) - return EINVAL; - if (!description->is_directory()) - return ENOTDIR; - return *description->custody(); -} - -SpinlockProtected const& Process::name() const -{ - return m_name; -} - -void Process::set_name(StringView name) -{ - m_name.with([name](auto& process_name) { - process_name.store_characters(name); - }); -} - -} diff --git a/Kernel/Tasks/Process.h b/Kernel/Tasks/Process.h deleted file mode 100644 index bdee0a7438d..00000000000 --- a/Kernel/Tasks/Process.h +++ /dev/null @@ -1,1129 +0,0 @@ -/* - * Copyright (c) 2018-2023, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#ifdef ENABLE_KERNEL_COVERAGE_COLLECTION -# include -#endif -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace Kernel { - -MutexProtected>& hostname(); -UnixDateTime kgettimeofday(); - -#define ENUMERATE_PLEDGE_PROMISES \ - __ENUMERATE_PLEDGE_PROMISE(stdio) \ - __ENUMERATE_PLEDGE_PROMISE(rpath) \ - __ENUMERATE_PLEDGE_PROMISE(wpath) \ - __ENUMERATE_PLEDGE_PROMISE(cpath) \ - __ENUMERATE_PLEDGE_PROMISE(dpath) \ - __ENUMERATE_PLEDGE_PROMISE(inet) \ - __ENUMERATE_PLEDGE_PROMISE(id) \ - __ENUMERATE_PLEDGE_PROMISE(proc) \ - __ENUMERATE_PLEDGE_PROMISE(ptrace) \ - __ENUMERATE_PLEDGE_PROMISE(exec) \ - __ENUMERATE_PLEDGE_PROMISE(unix) \ - __ENUMERATE_PLEDGE_PROMISE(recvfd) \ - __ENUMERATE_PLEDGE_PROMISE(sendfd) \ - __ENUMERATE_PLEDGE_PROMISE(fattr) \ - __ENUMERATE_PLEDGE_PROMISE(tty) \ - __ENUMERATE_PLEDGE_PROMISE(chown) \ - __ENUMERATE_PLEDGE_PROMISE(thread) \ - __ENUMERATE_PLEDGE_PROMISE(video) \ - __ENUMERATE_PLEDGE_PROMISE(accept) \ - __ENUMERATE_PLEDGE_PROMISE(settime) \ - __ENUMERATE_PLEDGE_PROMISE(sigaction) \ - __ENUMERATE_PLEDGE_PROMISE(setkeymap) \ - __ENUMERATE_PLEDGE_PROMISE(prot_exec) \ - __ENUMERATE_PLEDGE_PROMISE(map_fixed) \ - __ENUMERATE_PLEDGE_PROMISE(getkeymap) \ - __ENUMERATE_PLEDGE_PROMISE(jail) \ - __ENUMERATE_PLEDGE_PROMISE(mount) \ - __ENUMERATE_PLEDGE_PROMISE(no_error) - -#define __ENUMERATE_PLEDGE_PROMISE(x) sizeof(#x) + 1 + -// NOTE: We truncate the last space from the string as it's not needed (with 0 - 1). -constexpr static unsigned all_promises_strings_length_with_spaces = ENUMERATE_PLEDGE_PROMISES 0 - 1; -#undef __ENUMERATE_PLEDGE_PROMISE - -// NOTE: This is a sanity check because length of more than 1024 characters -// is not reasonable. -static_assert(all_promises_strings_length_with_spaces <= 1024); - -enum class Pledge : u32 { -#define __ENUMERATE_PLEDGE_PROMISE(x) x, - ENUMERATE_PLEDGE_PROMISES -#undef __ENUMERATE_PLEDGE_PROMISE -}; - -enum class VeilState { - None, - Dropped, - Locked, - LockedInherited, -}; - -static constexpr FlatPtr futex_key_private_flag = 0b1; -union GlobalFutexKey { - struct { - Memory::VMObject const* vmobject; - FlatPtr offset; - } shared; - struct { - Memory::AddressSpace const* address_space; - FlatPtr user_address; - } private_; - struct { - FlatPtr parent; - FlatPtr offset; - } raw; -}; -static_assert(sizeof(GlobalFutexKey) == (sizeof(FlatPtr) * 2)); - -struct LoadResult; - -class ProcessList; - -class Process final - : public ListedRefCounted - , public LockWeakable { - - class ProtectedValues { - public: - ProcessID pid { 0 }; - ProcessID ppid { 0 }; - // FIXME: This should be a NonnullRefPtr - RefPtr credentials; - RefPtr process_group; - RefPtr tty; - bool dumpable { false }; - bool executable_is_setid { false }; - bool has_promises { false }; - u32 promises { 0 }; - bool has_execpromises { false }; - u32 execpromises { 0 }; - mode_t umask { 022 }; - VirtualAddress signal_trampoline; - Atomic thread_count { 0 }; - u8 termination_status { 0 }; - u8 termination_signal { 0 }; - SetOnce reject_transition_to_executable_from_writable_prot; - }; - -public: - AK_MAKE_NONCOPYABLE(Process); - AK_MAKE_NONMOVABLE(Process); - - MAKE_ALIGNED_ALLOCATED(Process, PAGE_SIZE); - - friend class Thread; - friend class Coredump; - - auto with_protected_data(auto&& callback) const - { - SpinlockLocker locker(m_protected_data_lock); - return callback(m_protected_values_do_not_access_directly); - } - - auto with_mutable_protected_data(auto&& callback) - { - SpinlockLocker locker(m_protected_data_lock); - unprotect_data(); - auto guard = ScopeGuard([&] { protect_data(); }); - return callback(m_protected_values_do_not_access_directly); - } - - enum class State : u8 { - Running = 0, - Dying, - Dead - }; - -public: - static Process& current() - { - auto* current_thread = Processor::current_thread(); - VERIFY(current_thread); - return current_thread->process(); - } - - static bool has_current() - { - return Processor::current_thread() != nullptr; - } - - template - static void kernel_process_trampoline(void* data) - { - EntryFunction* func = reinterpret_cast(data); - (*func)(); - delete func; - } - - enum class RegisterProcess { - No, - Yes - }; - - struct ProcessAndFirstThread { - NonnullRefPtr process; - NonnullRefPtr first_thread; - }; - - template - static ErrorOr create_kernel_process(StringView name, EntryFunction entry, u32 affinity = THREAD_AFFINITY_DEFAULT, RegisterProcess do_register = RegisterProcess::Yes) - { - auto* entry_func = new EntryFunction(move(entry)); - return create_kernel_process(name, &Process::kernel_process_trampoline, entry_func, affinity, do_register); - } - - static ErrorOr create_kernel_process(StringView name, void (*entry)(void*), void* entry_data = nullptr, u32 affinity = THREAD_AFFINITY_DEFAULT, RegisterProcess do_register = RegisterProcess::Yes); - static ErrorOr create_user_process(StringView path, UserID, GroupID, Vector> arguments, Vector> environment, RefPtr); - static void register_new(Process&); - - ~Process(); - - virtual void remove_from_secondary_lists(); - - ErrorOr> create_kernel_thread(void (*entry)(void*), void* entry_data, u32 priority, StringView name, u32 affinity = THREAD_AFFINITY_DEFAULT, bool joinable = true); - - bool is_profiling() const { return m_profiling; } - void set_profiling(bool profiling) { m_profiling = profiling; } - -#ifdef ENABLE_KERNEL_COVERAGE_COLLECTION - NO_SANITIZE_COVERAGE KCOVInstance* kcov_instance() - { - return m_kcov_instance; - } - void set_kcov_instance(KCOVInstance* kcov_instance) { m_kcov_instance = kcov_instance; } - static bool is_kcov_busy(); -#endif - - bool should_generate_coredump() const - { - return m_should_generate_coredump; - } - void set_should_generate_coredump(bool b) { m_should_generate_coredump = b; } - - bool is_dying() const { return m_state.load(AK::MemoryOrder::memory_order_acquire) != State::Running; } - bool is_dead() const { return m_state.load(AK::MemoryOrder::memory_order_acquire) == State::Dead; } - - bool is_stopped() const { return m_is_stopped; } - bool set_stopped(bool stopped) { return m_is_stopped.exchange(stopped); } - - bool is_kernel_process() const { return m_is_kernel_process; } - bool is_user_process() const { return !m_is_kernel_process; } - - static RefPtr from_pid_in_same_jail(ProcessID); - static RefPtr from_pid_ignoring_jails(ProcessID); - static SessionID get_sid_from_pgid(ProcessGroupID pgid); - - using Name = FixedStringBuffer<32>; - SpinlockProtected const& name() const; - void set_name(StringView); - - ProcessID pid() const - { - return with_protected_data([](auto& protected_data) { return protected_data.pid; }); - } - SessionID sid() const { return credentials()->sid(); } - bool is_session_leader() const { return sid().value() == pid().value(); } - ProcessGroupID pgid() const - { - return with_protected_data([](auto& protected_data) { return protected_data.process_group ? protected_data.process_group->pgid() : 0; }); - } - bool is_group_leader() const { return pgid().value() == pid().value(); } - ProcessID ppid() const - { - return with_protected_data([](auto& protected_data) { return protected_data.ppid; }); - } - - SpinlockProtected, LockRank::Process> const& jail() { return m_attached_jail; } - - bool is_currently_in_jail() const - { - return m_attached_jail.with([&](auto& jail) -> bool { return !jail.is_null(); }); - } - - NonnullRefPtr credentials() const; - - bool is_dumpable() const - { - return with_protected_data([](auto& protected_data) { return protected_data.dumpable; }); - } - - mode_t umask() const - { - return with_protected_data([](auto& protected_data) { return protected_data.umask; }); - } - - // Breakable iteration functions - template Callback> - static void for_each_ignoring_jails(Callback); - - static ErrorOr for_each_in_same_jail(Function(Process&)>); - ErrorOr for_each_in_pgrp_in_same_jail(ProcessGroupID, Function(Process&)>); - ErrorOr for_each_child_in_same_jail(Function(Process&)>); - - template Callback> - IterationDecision for_each_thread(Callback); - template Callback> - IterationDecision for_each_thread(Callback callback) const; - ErrorOr try_for_each_thread(Function(Thread const&)>) const; - - // Non-breakable iteration functions - template Callback> - static void for_each_ignoring_jails(Callback); - - template Callback> - IterationDecision for_each_thread(Callback); - template Callback> - IterationDecision for_each_thread(Callback callback) const; - - void die(); - void finalize(); - - ThreadTracer* tracer() { return m_tracer.ptr(); } - bool is_traced() const { return !!m_tracer; } - ErrorOr start_tracing_from(ProcessID tracer); - void stop_tracing(); - void tracer_trap(Thread&, RegisterState const&); - - ErrorOr sys$emuctl(); - ErrorOr sys$yield(); - ErrorOr sys$sync(); - ErrorOr sys$beep(int tone); - ErrorOr sys$create_inode_watcher(u32 flags); - ErrorOr sys$inode_watcher_add_watch(Userspace user_params); - ErrorOr sys$inode_watcher_remove_watch(int fd, int wd); - ErrorOr sys$dbgputstr(Userspace, size_t); - ErrorOr sys$dump_backtrace(); - ErrorOr sys$gettid(); - ErrorOr sys$setsid(); - ErrorOr sys$getsid(pid_t); - ErrorOr sys$setpgid(pid_t pid, pid_t pgid); - ErrorOr sys$getpgrp(); - ErrorOr sys$getpgid(pid_t); - ErrorOr sys$getuid(); - ErrorOr sys$getgid(); - ErrorOr sys$geteuid(); - ErrorOr sys$getegid(); - ErrorOr sys$getpid(); - ErrorOr sys$getppid(); - ErrorOr sys$getresuid(Userspace, Userspace, Userspace); - ErrorOr sys$getresgid(Userspace, Userspace, Userspace); - ErrorOr sys$getrusage(int, Userspace); - ErrorOr sys$umask(mode_t); - ErrorOr sys$open(Userspace); - ErrorOr sys$close(int fd); - ErrorOr sys$read(int fd, Userspace, size_t); - ErrorOr sys$pread(int fd, Userspace, size_t, off_t); - ErrorOr sys$readv(int fd, Userspace iov, int iov_count); - ErrorOr sys$write(int fd, Userspace, size_t); - ErrorOr sys$pwritev(int fd, Userspace iov, int iov_count, off_t); - ErrorOr sys$fstat(int fd, Userspace); - ErrorOr sys$stat(Userspace); - ErrorOr sys$annotate_mapping(Userspace, int flags); - ErrorOr sys$lseek(int fd, Userspace, int whence); - ErrorOr sys$ftruncate(int fd, off_t); - ErrorOr sys$futimens(Userspace); - ErrorOr sys$posix_fallocate(int fd, off_t, off_t); - ErrorOr sys$kill(pid_t pid_or_pgid, int sig); - [[noreturn]] void sys$exit(int status); - ErrorOr sys$sigreturn(RegisterState& registers); - ErrorOr sys$waitid(Userspace); - ErrorOr sys$mmap(Userspace); - ErrorOr sys$mremap(Userspace); - ErrorOr sys$munmap(Userspace, size_t); - ErrorOr sys$set_mmap_name(Userspace); - ErrorOr sys$mprotect(Userspace, size_t, int prot); - ErrorOr sys$madvise(Userspace, size_t, int advice); - ErrorOr sys$msync(Userspace, size_t, int flags); - ErrorOr sys$purge(int mode); - ErrorOr sys$poll(Userspace); - ErrorOr sys$get_dir_entries(int fd, Userspace, size_t); - ErrorOr sys$getcwd(Userspace, size_t); - ErrorOr sys$chdir(Userspace, size_t); - ErrorOr sys$fchdir(int fd); - ErrorOr sys$adjtime(Userspace, Userspace); - ErrorOr sys$clock_gettime(clockid_t, Userspace); - ErrorOr sys$clock_settime(clockid_t, Userspace); - ErrorOr sys$clock_nanosleep(Userspace); - ErrorOr sys$clock_getres(Userspace); - ErrorOr sys$gethostname(Userspace, size_t); - ErrorOr sys$sethostname(Userspace, size_t); - ErrorOr sys$uname(Userspace); - ErrorOr sys$readlink(Userspace); - ErrorOr sys$fork(RegisterState&); - ErrorOr sys$execve(Userspace); - ErrorOr sys$dup2(int old_fd, int new_fd); - ErrorOr sys$sigaction(int signum, Userspace act, Userspace old_act); - ErrorOr sys$sigaltstack(Userspace ss, Userspace old_ss); - ErrorOr sys$sigprocmask(int how, Userspace set, Userspace old_set); - ErrorOr sys$sigpending(Userspace); - ErrorOr sys$sigsuspend(Userspace); - ErrorOr sys$sigtimedwait(Userspace, Userspace, Userspace); - ErrorOr sys$getgroups(size_t, Userspace); - ErrorOr sys$setgroups(size_t, Userspace); - ErrorOr sys$pipe(Userspace, int flags); - ErrorOr sys$killpg(pid_t pgrp, int sig); - ErrorOr sys$seteuid(UserID); - ErrorOr sys$setegid(GroupID); - ErrorOr sys$setuid(UserID); - ErrorOr sys$setgid(GroupID); - ErrorOr sys$setreuid(UserID, UserID); - ErrorOr sys$setresuid(UserID, UserID, UserID); - ErrorOr sys$setregid(GroupID, GroupID); - ErrorOr sys$setresgid(GroupID, GroupID, GroupID); - ErrorOr sys$alarm(unsigned seconds); - ErrorOr sys$faccessat(Userspace); - ErrorOr sys$fcntl(int fd, int cmd, uintptr_t extra_arg); - ErrorOr sys$ioctl(int fd, unsigned request, FlatPtr arg); - ErrorOr sys$mkdir(int dirfd, Userspace pathname, size_t path_length, mode_t mode); - ErrorOr sys$times(Userspace); - ErrorOr sys$utime(Userspace pathname, size_t path_length, Userspace); - ErrorOr sys$utimensat(Userspace); - ErrorOr sys$link(Userspace); - ErrorOr sys$unlink(int dirfd, Userspace pathname, size_t path_length, int flags); - ErrorOr sys$symlink(Userspace); - ErrorOr sys$rmdir(Userspace pathname, size_t path_length); - ErrorOr sys$fsmount(Userspace); - ErrorOr sys$fsopen(Userspace); - ErrorOr sys$umount(Userspace mountpoint, size_t mountpoint_length); - ErrorOr sys$chmod(Userspace); - ErrorOr sys$fchmod(int fd, mode_t); - ErrorOr sys$chown(Userspace); - ErrorOr sys$fchown(int fd, UserID, GroupID); - ErrorOr sys$fsync(int fd); - ErrorOr sys$socket(int domain, int type, int protocol); - ErrorOr sys$bind(int sockfd, Userspace addr, socklen_t); - ErrorOr sys$listen(int sockfd, int backlog); - ErrorOr sys$accept4(Userspace); - ErrorOr sys$connect(int sockfd, Userspace, socklen_t); - ErrorOr sys$shutdown(int sockfd, int how); - ErrorOr sys$sendmsg(int sockfd, Userspace, int flags); - ErrorOr sys$recvmsg(int sockfd, Userspace, int flags); - ErrorOr sys$getsockopt(Userspace); - ErrorOr sys$setsockopt(Userspace); - ErrorOr sys$getsockname(Userspace); - ErrorOr sys$getpeername(Userspace); - ErrorOr sys$socketpair(Userspace); - ErrorOr sys$scheduler_set_parameters(Userspace); - ErrorOr sys$scheduler_get_parameters(Userspace); - ErrorOr sys$create_thread(void* (*)(void*), Userspace); - [[noreturn]] void sys$exit_thread(Userspace, Userspace, size_t); - ErrorOr sys$join_thread(pid_t tid, Userspace exit_value); - ErrorOr sys$detach_thread(pid_t tid); - ErrorOr sys$kill_thread(pid_t tid, int signal); - ErrorOr sys$rename(Userspace); - ErrorOr sys$mknod(Userspace); - ErrorOr sys$realpath(Userspace); - ErrorOr sys$getrandom(Userspace, size_t, unsigned int); - ErrorOr sys$getkeymap(Userspace); - ErrorOr sys$setkeymap(Userspace); - ErrorOr sys$profiling_enable(pid_t, u64); - ErrorOr profiling_enable(pid_t, u64 event_mask); - ErrorOr sys$profiling_disable(pid_t); - ErrorOr sys$profiling_free_buffer(pid_t); - ErrorOr sys$futex(Userspace); - ErrorOr sys$pledge(Userspace); - ErrorOr sys$unveil(Userspace); - ErrorOr sys$perf_event(int type, FlatPtr arg1, FlatPtr arg2); - ErrorOr sys$perf_register_string(Userspace, size_t); - ErrorOr sys$get_stack_bounds(Userspace stack_base, Userspace stack_size); - ErrorOr sys$ptrace(Userspace); - ErrorOr sys$sendfd(int sockfd, int fd); - ErrorOr sys$recvfd(int sockfd, int options); - ErrorOr sys$sysconf(int name); - ErrorOr sys$disown(ProcessID); - ErrorOr sys$prctl(int option, FlatPtr arg1, FlatPtr arg2, FlatPtr arg3); - ErrorOr sys$anon_create(size_t, int options); - ErrorOr sys$statvfs(Userspace user_params); - ErrorOr sys$fstatvfs(int fd, statvfs* buf); - ErrorOr sys$map_time_page(); - ErrorOr sys$jail_create(Userspace user_params); - ErrorOr sys$jail_attach(Userspace user_params); - ErrorOr sys$get_root_session_id(pid_t force_sid); - ErrorOr sys$remount(Userspace user_params); - ErrorOr sys$bindmount(Userspace user_params); - ErrorOr sys$archctl(int option, FlatPtr arg1); - - enum SockOrPeerName { - SockName, - PeerName, - }; - template - ErrorOr get_sock_or_peer_name(Params const&); - - static void initialize(); - - [[noreturn]] void crash(int signal, Optional regs, bool out_of_memory = false); - [[nodiscard]] siginfo_t wait_info() const; - - RefPtr tty(); - RefPtr tty() const; - void set_tty(RefPtr); - - clock_t m_ticks_in_user { 0 }; - clock_t m_ticks_in_kernel { 0 }; - clock_t m_ticks_in_user_for_dead_children { 0 }; - clock_t m_ticks_in_kernel_for_dead_children { 0 }; - - NonnullRefPtr current_directory(); - RefPtr executable(); - RefPtr executable() const; - - UnixDateTime creation_time() const { return m_creation_time; } - - static constexpr size_t max_arguments_size = Thread::default_userspace_stack_size / 8; - static constexpr size_t max_environment_size = Thread::default_userspace_stack_size / 8; - static constexpr size_t max_auxiliary_size = Thread::default_userspace_stack_size / 8; - Vector> const& arguments() const { return m_arguments; } - Vector> const& environment() const { return m_environment; } - - ErrorOr exec(NonnullOwnPtr path, Vector> arguments, Vector> environment, Thread*& new_main_thread, InterruptsState& previous_interrupts_state, int recursion_depth = 0); - - ErrorOr load(Memory::AddressSpace& new_space, NonnullRefPtr main_program_description, RefPtr interpreter_description, Elf_Ehdr const& main_program_header, Optional minimum_stack_size = {}); - - void terminate_due_to_signal(u8 signal); - ErrorOr send_signal(u8 signal, Process* sender); - - u8 termination_signal() const - { - return with_protected_data([](auto& protected_data) -> u8 { - return protected_data.termination_signal; - }); - } - u8 termination_status() const - { - return with_protected_data([](auto& protected_data) { return protected_data.termination_status; }); - } - - u16 thread_count() const - { - return with_protected_data([](auto& protected_data) { - return protected_data.thread_count.load(AK::MemoryOrder::memory_order_relaxed); - }); - } - - Mutex& big_lock() { return m_big_lock; } - Mutex& ptrace_lock() { return m_ptrace_lock; } - - bool has_promises() const - { - return with_protected_data([](auto& protected_data) { return protected_data.has_promises; }); - } - bool has_promised(Pledge pledge) const - { - return with_protected_data([&](auto& protected_data) { - return (protected_data.promises & (1U << (u32)pledge)) != 0; - }); - } - - VeilState veil_state() const - { - return m_unveil_data.with([&](auto const& unveil_data) { return unveil_data.state; }); - } - - struct UnveilData { - explicit UnveilData(UnveilNode&& p) - : paths(move(p)) - { - } - VeilState state { VeilState::None }; - UnveilNode paths; - }; - - auto& unveil_data() { return m_unveil_data; } - auto const& unveil_data() const { return m_unveil_data; } - - auto& exec_unveil_data() { return m_exec_unveil_data; } - auto const& exec_unveil_data() const { return m_exec_unveil_data; } - - bool wait_for_tracer_at_next_execve() const - { - return m_wait_for_tracer_at_next_execve; - } - void set_wait_for_tracer_at_next_execve(bool val) - { - m_wait_for_tracer_at_next_execve = val; - } - - ErrorOr peek_user_data(Span destination, Userspace address); - ErrorOr peek_user_data(Userspace address); - ErrorOr poke_user_data(Userspace address, FlatPtr data); - - void disowned_by_waiter(Process& process); - void unblock_waiters(Thread::WaitBlocker::UnblockFlags, u8 signal = 0); - Thread::WaitBlockerSet& wait_blocker_set() { return m_wait_blocker_set; } - - template - ErrorOr for_each_coredump_property(Callback callback) const - { - return m_coredump_properties.with([&](auto const& coredump_properties) -> ErrorOr { - for (auto const& property : coredump_properties) { - if (property.key && property.value) - TRY(callback(*property.key, *property.value)); - } - - return {}; - }); - } - - ErrorOr set_coredump_property(NonnullOwnPtr key, NonnullOwnPtr value); - ErrorOr try_set_coredump_property(StringView key, StringView value); - - Vector> const& threads_for_coredump(Badge) const { return m_threads_for_coredump; } - - PerformanceEventBuffer* perf_events() { return m_perf_event_buffer; } - PerformanceEventBuffer const* perf_events() const { return m_perf_event_buffer; } - - SpinlockProtected, LockRank::None>& address_space() { return m_space; } - SpinlockProtected, LockRank::None> const& address_space() const { return m_space; } - - VirtualAddress signal_trampoline() const - { - return with_protected_data([](auto& protected_data) { return protected_data.signal_trampoline; }); - } - - ErrorOr require_promise(Pledge); - ErrorOr require_no_promises() const; - - bool should_reject_transition_to_executable_from_writable_prot() const - { - return with_protected_data([](auto& protected_data) { - return protected_data.reject_transition_to_executable_from_writable_prot.was_set(); - }); - } - - ErrorOr validate_mmap_prot(int prot, bool map_stack, bool map_anonymous, Memory::Region const* region = nullptr) const; - ErrorOr validate_inode_mmap_prot(int prot, bool description_readable, bool description_writable, bool map_shared) const; - - template - static ErrorOr> get_syscall_string_fixed_buffer(Syscall::StringArgument const& argument) - { - // NOTE: If the string is too much big for the FixedStringBuffer, - // we return E2BIG error here. - FixedStringBuffer buffer; - TRY(try_copy_string_from_user_into_fixed_string_buffer(reinterpret_cast(argument.characters), buffer, argument.length)); - return buffer; - } - - template - static ErrorOr> get_syscall_name_string_fixed_buffer(Userspace user_buffer, size_t user_length = Size) - { - // NOTE: If the string is too much big for the FixedStringBuffer, - // we return E2BIG error here. - FixedStringBuffer buffer; - TRY(try_copy_string_from_user_into_fixed_string_buffer(user_buffer, buffer, user_length)); - return buffer; - } - - template - static ErrorOr> get_syscall_name_string_fixed_buffer(Syscall::StringArgument const& argument) - { - // NOTE: If the string is too much big for the FixedStringBuffer, - // we return ENAMETOOLONG error here. - FixedStringBuffer buffer; - TRY(try_copy_name_from_user_into_fixed_string_buffer(reinterpret_cast(argument.characters), buffer, argument.length)); - return buffer; - } - -private: - friend class MemoryManager; - friend class Scheduler; - friend class Region; - friend class PerformanceManager; - - bool add_thread(Thread&); - bool remove_thread(Thread&); - - Process(StringView name, NonnullRefPtr, ProcessID ppid, bool is_kernel_process, RefPtr current_directory, RefPtr executable, RefPtr tty, UnveilNode unveil_tree, UnveilNode exec_unveil_tree, UnixDateTime creation_time); - static ErrorOr create_with_forked_name(UserID, GroupID, ProcessID ppid, bool is_kernel_process, RefPtr current_directory = nullptr, RefPtr executable = nullptr, RefPtr = nullptr, Process* fork_parent = nullptr); - static ErrorOr create(StringView name, UserID, GroupID, ProcessID ppid, bool is_kernel_process, RefPtr current_directory = nullptr, RefPtr executable = nullptr, RefPtr = nullptr, Process* fork_parent = nullptr); - ErrorOr> attach_resources(NonnullOwnPtr&&, Process* fork_parent); - static ProcessID allocate_pid(); - - void kill_threads_except_self(); - void kill_all_threads(); - ErrorOr dump_core(); - ErrorOr dump_perfcore(); - bool create_perf_events_buffer_if_needed(); - void delete_perf_events_buffer(); - - ErrorOr do_exec(NonnullRefPtr main_program_description, Vector> arguments, Vector> environment, RefPtr interpreter_description, Thread*& new_main_thread, InterruptsState& previous_interrupts_state, Elf_Ehdr const& main_program_header, Optional minimum_stack_size = {}); - ErrorOr do_write(OpenFileDescription&, UserOrKernelBuffer const&, size_t, Optional = {}); - - ErrorOr do_statvfs(FileSystem const& path, Custody const*, statvfs* buf); - - ErrorOr> find_elf_interpreter_for_executable(StringView path, Elf_Ehdr const& main_executable_header, size_t main_executable_header_size, size_t file_size, Optional& minimum_stack_size); - - ErrorOr do_kill(Process&, int signal); - ErrorOr do_killpg(ProcessGroupID pgrp, int signal); - ErrorOr do_killall(int signal); - ErrorOr do_killself(int signal); - - ErrorOr do_waitid(Variant, NonnullRefPtr> waitee, int options); - - static ErrorOr> get_syscall_path_argument(Userspace user_path, size_t path_length); - static ErrorOr> get_syscall_path_argument(Syscall::StringArgument const&); - - bool has_tracee_thread(ProcessID tracer_pid); - - void clear_signal_handlers_for_exec(); - void clear_futex_queues_on_exec(); - - ErrorOr get_futex_key(FlatPtr user_address, bool shared); - - ErrorOr remap_range_as_stack(FlatPtr address, size_t size); - - ErrorOr open_impl(Userspace); - ErrorOr close_impl(int fd); - ErrorOr read_impl(int fd, Userspace buffer, size_t size); - ErrorOr pread_impl(int fd, Userspace, size_t, off_t); - ErrorOr readv_impl(int fd, Userspace iov, int iov_count); - -public: - ErrorOr traverse_as_directory(FileSystemID, Function(FileSystem::DirectoryEntryView const&)> callback) const; - ErrorOr> lookup_as_directory(ProcFS&, StringView name) const; - ErrorOr procfs_get_fds_stats(KBufferBuilder& builder) const; - ErrorOr procfs_get_perf_events(KBufferBuilder& builder) const; - ErrorOr procfs_get_unveil_stats(KBufferBuilder& builder) const; - ErrorOr procfs_get_pledge_stats(KBufferBuilder& builder) const; - ErrorOr procfs_get_virtual_memory_stats(KBufferBuilder& builder) const; - ErrorOr procfs_get_binary_link(KBufferBuilder& builder) const; - ErrorOr procfs_get_current_work_directory_link(KBufferBuilder& builder) const; - ErrorOr procfs_get_command_line(KBufferBuilder& builder) const; - mode_t binary_link_required_mode() const; - ErrorOr procfs_get_thread_stack(ThreadID thread_id, KBufferBuilder& builder) const; - ErrorOr traverse_stacks_directory(FileSystemID, Function(FileSystem::DirectoryEntryView const&)> callback) const; - ErrorOr> lookup_stacks_directory(ProcFS&, StringView name) const; - ErrorOr procfs_get_file_description_link(unsigned fd, KBufferBuilder& builder) const; - ErrorOr traverse_file_descriptions_directory(FileSystemID, Function(FileSystem::DirectoryEntryView const&)> callback) const; - ErrorOr> lookup_file_descriptions_directory(ProcFS&, StringView name) const; - ErrorOr> lookup_children_directory(ProcFS&, StringView name) const; - ErrorOr traverse_children_directory(FileSystemID, Function(FileSystem::DirectoryEntryView const&)> callback) const; - ErrorOr procfs_get_child_process_link(ProcessID child_pid, KBufferBuilder& builder) const; - -private: - inline PerformanceEventBuffer* current_perf_events_buffer() - { - if (g_profiling_all_threads) - return g_global_perf_events; - if (m_profiling) - return m_perf_event_buffer.ptr(); - return nullptr; - } - - SpinlockProtected m_name; - - SpinlockProtected, LockRank::None> m_space; - - RecursiveSpinlock mutable m_protected_data_lock; - AtomicEdgeAction m_protected_data_refs; - void protect_data(); - void unprotect_data(); - - OwnPtr m_tracer; - -public: - class OpenFileDescriptionAndFlags { - public: - bool is_valid() const { return !m_description.is_null(); } - bool is_allocated() const { return m_is_allocated; } - void allocate() - { - VERIFY(!m_is_allocated); - VERIFY(!is_valid()); - m_is_allocated = true; - } - void deallocate() - { - VERIFY(m_is_allocated); - VERIFY(!is_valid()); - m_is_allocated = false; - } - - OpenFileDescription* description() { return m_description; } - OpenFileDescription const* description() const { return m_description; } - u32 flags() const { return m_flags; } - void set_flags(u32 flags) { m_flags = flags; } - - void clear(); - void set(NonnullRefPtr, u32 flags = 0); - - private: - RefPtr m_description; - bool m_is_allocated { false }; - u32 m_flags { 0 }; - }; - - class ScopedDescriptionAllocation; - class OpenFileDescriptions { - AK_MAKE_NONCOPYABLE(OpenFileDescriptions); - AK_MAKE_NONMOVABLE(OpenFileDescriptions); - friend class Process; - - public: - OpenFileDescriptions() { } - ALWAYS_INLINE OpenFileDescriptionAndFlags const& operator[](size_t i) const { return at(i); } - ALWAYS_INLINE OpenFileDescriptionAndFlags& operator[](size_t i) { return at(i); } - - ErrorOr try_clone(Kernel::Process::OpenFileDescriptions const& other) - { - TRY(try_resize(other.m_fds_metadatas.size())); - - for (size_t i = 0; i < other.m_fds_metadatas.size(); ++i) { - m_fds_metadatas[i] = other.m_fds_metadatas[i]; - } - return {}; - } - - OpenFileDescriptionAndFlags const& at(size_t i) const; - OpenFileDescriptionAndFlags& at(size_t i); - - OpenFileDescriptionAndFlags const* get_if_valid(size_t i) const; - OpenFileDescriptionAndFlags* get_if_valid(size_t i); - - void enumerate(Function) const; - ErrorOr try_enumerate(Function(OpenFileDescriptionAndFlags const&)>) const; - void change_each(Function); - - ErrorOr allocate(int first_candidate_fd = 0); - size_t open_count() const; - - ErrorOr try_resize(size_t size) { return m_fds_metadatas.try_resize(size); } - - static constexpr size_t max_open() - { - return s_max_open_file_descriptors; - } - - void clear() - { - m_fds_metadatas.clear(); - } - - ErrorOr> open_file_description(int fd) const; - - private: - static constexpr size_t s_max_open_file_descriptors { FD_SETSIZE }; - Vector m_fds_metadatas; - }; - - class ScopedDescriptionAllocation { - AK_MAKE_NONCOPYABLE(ScopedDescriptionAllocation); - - public: - ScopedDescriptionAllocation() = default; - ScopedDescriptionAllocation(int tracked_fd, OpenFileDescriptionAndFlags* description) - : fd(tracked_fd) - , m_description(description) - { - } - - ScopedDescriptionAllocation(ScopedDescriptionAllocation&& other) - : fd(other.fd) - { - // Take over the responsibility of tracking to deallocation. - swap(m_description, other.m_description); - } - - ScopedDescriptionAllocation& operator=(ScopedDescriptionAllocation&& other) - { - if (this != &other) { - m_description = exchange(other.m_description, nullptr); - fd = exchange(other.fd, -1); - } - return *this; - } - - ~ScopedDescriptionAllocation() - { - if (m_description && m_description->is_allocated() && !m_description->is_valid()) { - m_description->deallocate(); - } - } - - int fd { -1 }; - - private: - OpenFileDescriptionAndFlags* m_description { nullptr }; - }; - - MutexProtected& fds() { return m_fds; } - MutexProtected const& fds() const { return m_fds; } - - ErrorOr> open_file_description(int fd) - { - return m_fds.with_shared([fd](auto& fds) { return fds.open_file_description(fd); }); - } - - ErrorOr> open_file_description_ignoring_negative(int fd) - { - if (fd < 0) - return nullptr; - return open_file_description(fd); - } - - ErrorOr> open_file_description(int fd) const - { - return m_fds.with_shared([fd](auto& fds) { return fds.open_file_description(fd); }); - } - - ErrorOr> open_file_description_ignoring_negative(int fd) const - { - if (fd < 0) - return nullptr; - return open_file_description(fd); - } - - ErrorOr allocate_fd() - { - return m_fds.with_exclusive([](auto& fds) { return fds.allocate(); }); - } - - ErrorOr> custody_for_dirfd(Badge, int dirfd); - -private: - ErrorOr> custody_for_dirfd(int dirfd); - - SpinlockProtected& thread_list() { return m_thread_list; } - SpinlockProtected const& thread_list() const { return m_thread_list; } - - ErrorOr> get_thread_from_pid_or_tid(pid_t pid_or_tid, Syscall::SchedulerParametersMode mode); - ErrorOr> get_thread_from_thread_list(pid_t tid); - - SpinlockProtected m_thread_list {}; - - MutexProtected m_fds; - - bool const m_is_kernel_process; - Atomic m_state { State::Running }; - bool m_profiling { false }; - Atomic m_is_stopped { false }; - bool m_should_generate_coredump { false }; - -#ifdef ENABLE_KERNEL_COVERAGE_COLLECTION - KCOVInstance* m_kcov_instance { nullptr }; -#endif - - SpinlockProtected, LockRank::None> m_executable; - - SpinlockProtected, LockRank::None> m_current_directory; - - UnixDateTime const m_creation_time; - - Vector> m_arguments; - Vector> m_environment; - - IntrusiveListNode m_jail_process_list_node; - IntrusiveListNode m_all_processes_list_node; - -public: - using AllProcessesList = IntrusiveListRelaxedConst<&Process::m_all_processes_list_node>; - using JailProcessList = IntrusiveListRelaxedConst<&Process::m_jail_process_list_node>; - -private: - SpinlockProtected, LockRank::None> m_jail_process_list; - SpinlockProtected, LockRank::Process> m_attached_jail {}; - - Mutex m_big_lock { "Process"sv, Mutex::MutexBehavior::BigLock }; - Mutex m_ptrace_lock { "ptrace"sv }; - - SpinlockProtected, LockRank::None> m_alarm_timer; - - SpinlockProtected m_unveil_data; - SpinlockProtected m_exec_unveil_data; - - OwnPtr m_perf_event_buffer; - - // This member is used in the implementation of ptrace's PT_TRACEME flag. - // If it is set to true, the process will stop at the next execve syscall - // and wait for a tracer to attach. - bool m_wait_for_tracer_at_next_execve { false }; - - Thread::WaitBlockerSet m_wait_blocker_set; - - struct CoredumpProperty { - OwnPtr key; - OwnPtr value; - }; - - SpinlockProtected, LockRank::None> m_coredump_properties {}; - Vector> m_threads_for_coredump; - - struct SignalActionData { - VirtualAddress handler_or_sigaction; - int flags { 0 }; - u32 mask { 0 }; - }; - Array m_signal_action_data; - - static_assert(sizeof(ProtectedValues) < (PAGE_SIZE)); - alignas(4096) ProtectedValues m_protected_values_do_not_access_directly; - u8 m_protected_values_padding[PAGE_SIZE - sizeof(ProtectedValues)]; - -public: - static SpinlockProtected& all_instances(); -}; - -class ProcessList : public RefCounted { -public: - static ErrorOr> create(); - SpinlockProtected& attached_processes() { return m_attached_processes; } - SpinlockProtected const& attached_processes() const { return m_attached_processes; } - -private: - ProcessList() = default; - SpinlockProtected m_attached_processes; -}; - -// Note: Process object should be 2 pages of 4096 bytes each. -// It's not expected that the Process object will expand further because the first -// page is used for all unprotected values (which should be plenty of space for them). -// The second page is being used exclusively for write-protected values. -static_assert(AssertSize()); - -extern RecursiveSpinlock g_profiling_lock; - -template Callback> -inline IterationDecision Process::for_each_thread(Callback callback) -{ - return thread_list().with([&](auto& thread_list) -> IterationDecision { - for (auto& thread : thread_list) { - IterationDecision decision = callback(thread); - if (decision != IterationDecision::Continue) - return decision; - } - return IterationDecision::Continue; - }); -} - -template Callback> -inline void Process::for_each_ignoring_jails(Callback callback) -{ - Process::all_instances().with([&](auto const& list) { - for (auto it = list.begin(); it != list.end();) { - auto& process = *it; - ++it; - if (callback(process) == IterationDecision::Break) - break; - } - }); -} - -template Callback> -inline IterationDecision Process::for_each_thread(Callback callback) const -{ - return thread_list().with([&](auto& thread_list) -> IterationDecision { - for (auto& thread : thread_list) { - IterationDecision decision = callback(thread); - if (decision != IterationDecision::Continue) - return decision; - } - return IterationDecision::Continue; - }); -} - -template Callback> -inline IterationDecision Process::for_each_thread(Callback callback) const -{ - thread_list().with([&](auto& thread_list) { - for (auto& thread : thread_list) - callback(thread); - }); - return IterationDecision::Continue; -} - -inline ErrorOr Process::try_for_each_thread(Function(Thread const&)> callback) const -{ - return thread_list().with([&](auto& thread_list) -> ErrorOr { - for (auto& thread : thread_list) - TRY(callback(thread)); - return {}; - }); -} - -template Callback> -inline IterationDecision Process::for_each_thread(Callback callback) -{ - thread_list().with([&](auto& thread_list) { - for (auto& thread : thread_list) - callback(thread); - }); - return IterationDecision::Continue; -} - -inline ProcessID Thread::pid() const -{ - return m_process->pid(); -} - -} - -#define VERIFY_PROCESS_BIG_LOCK_ACQUIRED(process) \ - VERIFY(process->big_lock().is_exclusively_locked_by_current_thread()) - -#define VERIFY_NO_PROCESS_BIG_LOCK(process) \ - VERIFY(!process->big_lock().is_exclusively_locked_by_current_thread()) - -inline ErrorOr> try_copy_kstring_from_user(Kernel::Syscall::StringArgument const& string) -{ - Userspace characters((FlatPtr)string.characters); - return try_copy_kstring_from_user(characters, string.length); -} - -template<> -struct AK::Formatter : AK::Formatter { - ErrorOr format(FormatBuilder& builder, Kernel::Process const& value) - { - return value.name().with([&](auto& process_name) { - return AK::Formatter::format(builder, "{}({})"sv, process_name.representable_view(), value.pid().value()); - }); - } -}; - -namespace AK { -template<> -struct Traits : public DefaultTraits { - static unsigned hash(Kernel::GlobalFutexKey const& futex_key) { return pair_int_hash(ptr_hash(futex_key.raw.parent), ptr_hash(futex_key.raw.offset)); } - static bool equals(Kernel::GlobalFutexKey const& a, Kernel::GlobalFutexKey const& b) { return a.raw.parent == b.raw.parent && a.raw.offset == b.raw.offset; } -}; -}; diff --git a/Kernel/Tasks/ProcessGroup.cpp b/Kernel/Tasks/ProcessGroup.cpp deleted file mode 100644 index 79a9cbb27f4..00000000000 --- a/Kernel/Tasks/ProcessGroup.cpp +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright (c) 2020, the SerenityOS developers. - * Copyright (c) 2021-2023, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include - -namespace Kernel { - -static Singleton> s_all_instances; - -SpinlockProtected& ProcessGroup::all_instances() -{ - return s_all_instances; -} - -ProcessGroup::~ProcessGroup() = default; - -ErrorOr> ProcessGroup::create_if_unused_pgid(ProcessGroupID pgid) -{ - return all_instances().with([&](auto& all_instances) -> ErrorOr> { - for (auto& process_group : all_instances) { - if (process_group.pgid() == pgid) - return EPERM; - } - auto process_group = TRY(adopt_nonnull_ref_or_enomem(new (nothrow) ProcessGroup(pgid))); - all_instances.prepend(*process_group); - return process_group; - }); -} - -ErrorOr> ProcessGroup::find_or_create(ProcessGroupID pgid) -{ - return all_instances().with([&](auto& all_instances) -> ErrorOr> { - for (auto& group : all_instances) { - if (group.pgid() == pgid) - return group; - } - auto process_group = TRY(adopt_nonnull_ref_or_enomem(new (nothrow) ProcessGroup(pgid))); - all_instances.prepend(*process_group); - return process_group; - }); -} - -RefPtr ProcessGroup::from_pgid(ProcessGroupID pgid) -{ - return all_instances().with([&](auto& groups) -> RefPtr { - for (auto& group : groups) { - if (group.pgid() == pgid) - return &group; - } - return nullptr; - }); -} - -} diff --git a/Kernel/Tasks/ProcessGroup.h b/Kernel/Tasks/ProcessGroup.h deleted file mode 100644 index 2f74ff8d79f..00000000000 --- a/Kernel/Tasks/ProcessGroup.h +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright (c) 2020-2023, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include -#include -#include - -namespace Kernel { - -class ProcessGroup - : public ListedRefCounted - , public LockWeakable { - - AK_MAKE_NONMOVABLE(ProcessGroup); - AK_MAKE_NONCOPYABLE(ProcessGroup); - -public: - ~ProcessGroup(); - - static ErrorOr> create_if_unused_pgid(ProcessGroupID); - static ErrorOr> find_or_create(ProcessGroupID); - static RefPtr from_pgid(ProcessGroupID); - - ProcessGroupID const& pgid() const { return m_pgid; } - -private: - ProcessGroup(ProcessGroupID pgid) - : m_pgid(pgid) - { - } - - ProcessGroupID m_pgid; - - mutable IntrusiveListNode m_list_node; - -public: - using AllInstancesList = IntrusiveList<&ProcessGroup::m_list_node>; - static SpinlockProtected& all_instances(); -}; - -} diff --git a/Kernel/Tasks/ProcessList.cpp b/Kernel/Tasks/ProcessList.cpp deleted file mode 100644 index 6e5d765e68e..00000000000 --- a/Kernel/Tasks/ProcessList.cpp +++ /dev/null @@ -1,16 +0,0 @@ -/* - * Copyright (c) 2023, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include - -namespace Kernel { - -ErrorOr> ProcessList::create() -{ - return adopt_nonnull_ref_or_enomem(new (nothrow) ProcessList()); -} - -} diff --git a/Kernel/Tasks/Scheduler.cpp b/Kernel/Tasks/Scheduler.cpp deleted file mode 100644 index 2fa7fb67935..00000000000 --- a/Kernel/Tasks/Scheduler.cpp +++ /dev/null @@ -1,561 +0,0 @@ -/* - * Copyright (c) 2018-2022, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace Kernel { - -RecursiveSpinlock g_scheduler_lock {}; - -static u32 time_slice_for(Thread const& thread) -{ - // One time slice unit == 4ms (assuming 250 ticks/second) - if (thread.is_idle_thread()) - return 1; - return 2; -} - -READONLY_AFTER_INIT Thread* g_finalizer; -READONLY_AFTER_INIT WaitQueue* g_finalizer_wait_queue; -Atomic g_finalizer_has_work { false }; -READONLY_AFTER_INIT static Process* s_colonel_process; - -struct ThreadReadyQueue { - IntrusiveList<&Thread::m_ready_queue_node> thread_list; -}; - -struct ThreadReadyQueues { - u32 mask {}; - static constexpr size_t count = sizeof(mask) * 8; - Array queues; -}; - -static Singleton> g_ready_queues; - -static SpinlockProtected g_total_time_scheduled {}; - -static void dump_thread_list(bool = false); - -static inline u32 thread_priority_to_priority_index(u32 thread_priority) -{ - // Converts the priority in the range of THREAD_PRIORITY_MIN...THREAD_PRIORITY_MAX - // to a index into g_ready_queues where 0 is the highest priority bucket - VERIFY(thread_priority >= THREAD_PRIORITY_MIN && thread_priority <= THREAD_PRIORITY_MAX); - constexpr u32 thread_priority_count = THREAD_PRIORITY_MAX - THREAD_PRIORITY_MIN + 1; - static_assert(thread_priority_count > 0); - auto priority_bucket = ((thread_priority_count - (thread_priority - THREAD_PRIORITY_MIN)) / thread_priority_count) * (ThreadReadyQueues::count - 1); - VERIFY(priority_bucket < ThreadReadyQueues::count); - return priority_bucket; -} - -Thread& Scheduler::pull_next_runnable_thread() -{ - auto affinity_mask = 1u << Processor::current_id(); - - return g_ready_queues->with([&](auto& ready_queues) -> Thread& { - auto priority_mask = ready_queues.mask; - while (priority_mask != 0) { - auto priority = bit_scan_forward(priority_mask); - VERIFY(priority > 0); - auto& ready_queue = ready_queues.queues[--priority]; - for (auto& thread : ready_queue.thread_list) { - VERIFY(thread.m_runnable_priority == (int)priority); - if (thread.is_active()) - continue; - if (!(thread.affinity() & affinity_mask)) - continue; - thread.m_runnable_priority = -1; - ready_queue.thread_list.remove(thread); - if (ready_queue.thread_list.is_empty()) - ready_queues.mask &= ~(1u << priority); - // Mark it as active because we are using this thread. This is similar - // to comparing it with Processor::current_thread, but when there are - // multiple processors there's no easy way to check whether the thread - // is actually still needed. This prevents accidental finalization when - // a thread is no longer in Running state, but running on another core. - - // We need to mark it active here so that this thread won't be - // scheduled on another core if it were to be queued before actually - // switching to it. - // FIXME: Figure out a better way maybe? - thread.set_active(true); - return thread; - } - priority_mask &= ~(1u << priority); - } - - auto* idle_thread = Processor::idle_thread(); - idle_thread->set_active(true); - return *idle_thread; - }); -} - -Thread* Scheduler::peek_next_runnable_thread() -{ - auto affinity_mask = 1u << Processor::current_id(); - - return g_ready_queues->with([&](auto& ready_queues) -> Thread* { - auto priority_mask = ready_queues.mask; - while (priority_mask != 0) { - auto priority = bit_scan_forward(priority_mask); - VERIFY(priority > 0); - auto& ready_queue = ready_queues.queues[--priority]; - for (auto& thread : ready_queue.thread_list) { - VERIFY(thread.m_runnable_priority == (int)priority); - if (thread.is_active()) - continue; - if (!(thread.affinity() & affinity_mask)) - continue; - return &thread; - } - priority_mask &= ~(1u << priority); - } - - // Unlike in pull_next_runnable_thread() we don't want to fall back to - // the idle thread. We just want to see if we have any other thread ready - // to be scheduled. - return nullptr; - }); -} - -bool Scheduler::dequeue_runnable_thread(Thread& thread, bool check_affinity) -{ - if (thread.is_idle_thread()) - return true; - - return g_ready_queues->with([&](auto& ready_queues) { - auto priority = thread.m_runnable_priority; - if (priority < 0) { - VERIFY(!thread.m_ready_queue_node.is_in_list()); - return false; - } - - if (check_affinity && !(thread.affinity() & (1 << Processor::current_id()))) - return false; - - VERIFY(ready_queues.mask & (1u << priority)); - auto& ready_queue = ready_queues.queues[priority]; - thread.m_runnable_priority = -1; - ready_queue.thread_list.remove(thread); - if (ready_queue.thread_list.is_empty()) - ready_queues.mask &= ~(1u << priority); - return true; - }); -} - -void Scheduler::enqueue_runnable_thread(Thread& thread) -{ - VERIFY(g_scheduler_lock.is_locked_by_current_processor()); - if (thread.is_idle_thread()) - return; - auto priority = thread_priority_to_priority_index(thread.priority()); - - g_ready_queues->with([&](auto& ready_queues) { - VERIFY(thread.m_runnable_priority < 0); - thread.m_runnable_priority = (int)priority; - VERIFY(!thread.m_ready_queue_node.is_in_list()); - auto& ready_queue = ready_queues.queues[priority]; - bool was_empty = ready_queue.thread_list.is_empty(); - ready_queue.thread_list.append(thread); - if (was_empty) - ready_queues.mask |= (1u << priority); - }); -} - -UNMAP_AFTER_INIT void Scheduler::start() -{ - VERIFY_INTERRUPTS_DISABLED(); - - // We need to acquire our scheduler lock, which will be released - // by the idle thread once control transferred there - g_scheduler_lock.lock(); - - auto& processor = Processor::current(); - VERIFY(processor.is_initialized()); - auto& idle_thread = *Processor::idle_thread(); - VERIFY(processor.current_thread() == &idle_thread); - idle_thread.set_ticks_left(time_slice_for(idle_thread)); - idle_thread.did_schedule(); - idle_thread.set_initialized(true); - processor.init_context(idle_thread, false); - idle_thread.set_state(Thread::State::Running); - VERIFY(idle_thread.affinity() == (1u << processor.id())); - processor.initialize_context_switching(idle_thread); - VERIFY_NOT_REACHED(); -} - -void Scheduler::pick_next() -{ - VERIFY_INTERRUPTS_DISABLED(); - - // Set the in_scheduler flag before acquiring the spinlock. This - // prevents a recursive call into Scheduler::invoke_async upon - // leaving the scheduler lock. - ScopedCritical critical; - Processor::set_current_in_scheduler(true); - ScopeGuard guard( - []() { - // We may be on a different processor after we got switched - // back to this thread! - VERIFY(Processor::current_in_scheduler()); - Processor::set_current_in_scheduler(false); - }); - - SpinlockLocker lock(g_scheduler_lock); - - if constexpr (SCHEDULER_RUNNABLE_DEBUG) { - dump_thread_list(); - } - - auto& thread_to_schedule = pull_next_runnable_thread(); - if constexpr (SCHEDULER_DEBUG) { - dbgln("Scheduler[{}]: Switch to {} @ {:p}", - Processor::current_id(), - thread_to_schedule, - thread_to_schedule.regs().ip()); - } - - // We need to leave our first critical section before switching context, - // but since we're still holding the scheduler lock we're still in a critical section - critical.leave(); - - thread_to_schedule.set_ticks_left(time_slice_for(thread_to_schedule)); - context_switch(&thread_to_schedule); -} - -void Scheduler::yield() -{ - InterruptDisabler disabler; - - auto const* current_thread = Thread::current(); - dbgln_if(SCHEDULER_DEBUG, "Scheduler[{}]: yielding thread {} in_irq={}", Processor::current_id(), *current_thread, Processor::current_in_irq()); - VERIFY(current_thread != nullptr); - if (Processor::current_in_irq() || Processor::in_critical()) { - // If we're handling an IRQ we can't switch context, or we're in - // a critical section where we don't want to switch contexts, then - // delay until exiting the trap or critical section - Processor::current().invoke_scheduler_async(); - return; - } - - Scheduler::pick_next(); -} - -void Scheduler::context_switch(Thread* thread) -{ - thread->did_schedule(); - - auto* from_thread = Thread::current(); - VERIFY(from_thread); - - if (from_thread == thread) - return; - - // If the last process hasn't blocked (still marked as running), - // mark it as runnable for the next round, unless it's supposed - // to be stopped, in which case just mark it as such. - if (from_thread->state() == Thread::State::Running) { - if (from_thread->should_be_stopped()) - from_thread->set_state(Thread::State::Stopped); - else - from_thread->set_state(Thread::State::Runnable); - } - -#ifdef LOG_EVERY_CONTEXT_SWITCH - auto const msg = "Scheduler[{}]: {} -> {} [prio={}] {:p}"; - - dbgln(msg, - Processor::current_id(), from_thread->tid().value(), - thread->tid().value(), thread->priority(), thread->regs().ip()); -#endif - - auto& proc = Processor::current(); - if (!thread->is_initialized()) { - proc.init_context(*thread, false); - thread->set_initialized(true); - } - thread->set_state(Thread::State::Running); - - PerformanceManager::add_context_switch_perf_event(*from_thread, *thread); - - proc.switch_context(from_thread, thread); - - // NOTE: from_thread at this point reflects the thread we were - // switched from, and thread reflects Thread::current() - enter_current(*from_thread); - VERIFY(thread == Thread::current()); - - { - SpinlockLocker lock(thread->get_lock()); - thread->dispatch_one_pending_signal(); - } -} - -void Scheduler::enter_current(Thread& prev_thread) -{ - VERIFY(g_scheduler_lock.is_locked_by_current_processor()); - - // We already recorded the scheduled time when entering the trap, so this merely accounts for the kernel time since then - auto scheduler_time = TimeManagement::scheduler_current_time(); - prev_thread.update_time_scheduled(scheduler_time, true, true); - auto* current_thread = Thread::current(); - current_thread->update_time_scheduled(scheduler_time, true, false); - - // NOTE: When doing an exec(), we will context switch from and to the same thread! - // In that case, we must not mark the previous thread as inactive. - if (&prev_thread != current_thread) - prev_thread.set_active(false); - - if (prev_thread.state() == Thread::State::Dying) { - // If the thread we switched from is marked as dying, then notify - // the finalizer. Note that as soon as we leave the scheduler lock - // the finalizer may free from_thread! - notify_finalizer(); - } -} - -void Scheduler::leave_on_first_switch(InterruptsState previous_interrupts_state) -{ - // This is called when a thread is switched into for the first time. - // At this point, enter_current has already be called, but because - // Scheduler::context_switch is not in the call stack we need to - // clean up and release locks manually here - g_scheduler_lock.unlock(previous_interrupts_state); - - VERIFY(Processor::current_in_scheduler()); - Processor::set_current_in_scheduler(false); -} - -void Scheduler::prepare_after_exec() -{ - // This is called after exec() when doing a context "switch" into - // the new process. This is called from Processor::assume_context - VERIFY(g_scheduler_lock.is_locked_by_current_processor()); - - VERIFY(!Processor::current_in_scheduler()); - Processor::set_current_in_scheduler(true); -} - -void Scheduler::prepare_for_idle_loop() -{ - // This is called when the CPU finished setting up the idle loop - // and is about to run it. We need to acquire the scheduler lock - VERIFY(!g_scheduler_lock.is_locked_by_current_processor()); - g_scheduler_lock.lock(); - - VERIFY(!Processor::current_in_scheduler()); - Processor::set_current_in_scheduler(true); -} - -Process* Scheduler::colonel() -{ - VERIFY(s_colonel_process); - return s_colonel_process; -} - -UNMAP_AFTER_INIT void Scheduler::initialize() -{ - VERIFY(Processor::is_initialized()); // sanity check - VERIFY(TimeManagement::is_initialized()); - - g_finalizer_wait_queue = new WaitQueue; - - g_finalizer_has_work.store(false, AK::MemoryOrder::memory_order_release); - auto [colonel_process, idle_thread] = MUST(Process::create_kernel_process("colonel"sv, idle_loop, nullptr, 1, Process::RegisterProcess::No)); - s_colonel_process = &colonel_process.leak_ref(); - idle_thread->set_priority(THREAD_PRIORITY_MIN); - idle_thread->set_name("Idle Task #0"sv); - - set_idle_thread(idle_thread); -} - -UNMAP_AFTER_INIT void Scheduler::set_idle_thread(Thread* idle_thread) -{ - idle_thread->set_idle_thread(); - Processor::current().set_idle_thread(*idle_thread); - Processor::set_current_thread(*idle_thread); -} - -UNMAP_AFTER_INIT Thread* Scheduler::create_ap_idle_thread(u32 cpu) -{ - VERIFY(cpu != 0); - // This function is called on the bsp, but creates an idle thread for another AP - VERIFY(Processor::is_bootstrap_processor()); - - VERIFY(s_colonel_process); - Thread* idle_thread = MUST(s_colonel_process->create_kernel_thread(idle_loop, nullptr, THREAD_PRIORITY_MIN, MUST(KString::formatted("idle thread #{}", cpu))->view(), 1 << cpu, false)); - VERIFY(idle_thread); - return idle_thread; -} - -void Scheduler::add_time_scheduled(u64 time_to_add, bool is_kernel) -{ - g_total_time_scheduled.with([&](auto& total_time_scheduled) { - total_time_scheduled.total += time_to_add; - if (is_kernel) - total_time_scheduled.total_kernel += time_to_add; - }); -} - -void Scheduler::timer_tick(RegisterState const& regs) -{ - VERIFY_INTERRUPTS_DISABLED(); - VERIFY(Processor::current_in_irq()); - - auto* current_thread = Processor::current_thread(); - if (!current_thread) - return; - - // Sanity checks - VERIFY(current_thread->current_trap()); - VERIFY(current_thread->current_trap()->regs == ®s); - - if (current_thread->process().is_kernel_process()) { - // Because the previous mode when entering/exiting kernel threads never changes - // we never update the time scheduled. So we need to update it manually on the - // timer interrupt - current_thread->update_time_scheduled(TimeManagement::scheduler_current_time(), true, false); - } - - if (current_thread->previous_mode() == ExecutionMode::User && current_thread->should_die() && !current_thread->is_blocked()) { - SpinlockLocker scheduler_lock(g_scheduler_lock); - dbgln_if(SCHEDULER_DEBUG, "Scheduler[{}]: Terminating user mode thread {}", Processor::current_id(), *current_thread); - current_thread->set_state(Thread::State::Dying); - Processor::current().invoke_scheduler_async(); - return; - } - - if (current_thread->tick()) - return; - - if (!current_thread->is_idle_thread() && !peek_next_runnable_thread()) { - // If no other thread is ready to be scheduled we don't need to - // switch to the idle thread. Just give the current thread another - // time slice and let it run! - current_thread->set_ticks_left(time_slice_for(*current_thread)); - current_thread->did_schedule(); - dbgln_if(SCHEDULER_DEBUG, "Scheduler[{}]: No other threads ready, give {} another timeslice", Processor::current_id(), *current_thread); - return; - } - - VERIFY_INTERRUPTS_DISABLED(); - VERIFY(Processor::current_in_irq()); - Processor::current().invoke_scheduler_async(); -} - -void Scheduler::invoke_async() -{ - VERIFY_INTERRUPTS_DISABLED(); - VERIFY(!Processor::current_in_irq()); - - // Since this function is called when leaving critical sections (such - // as a Spinlock), we need to check if we're not already doing this - // to prevent recursion - if (!Processor::current_in_scheduler()) - pick_next(); -} - -void Scheduler::notify_finalizer() -{ - if (!g_finalizer_has_work.exchange(true, AK::MemoryOrder::memory_order_acq_rel)) - g_finalizer_wait_queue->wake_all(); -} - -void Scheduler::idle_loop(void*) -{ - auto& proc = Processor::current(); - dbgln("Scheduler[{}]: idle loop running", proc.id()); - VERIFY(Processor::are_interrupts_enabled()); - - for (;;) { - proc.idle_begin(); - proc.wait_for_interrupt(); - proc.idle_end(); - VERIFY_INTERRUPTS_ENABLED(); - yield(); - } -} - -void Scheduler::dump_scheduler_state(bool with_stack_traces) -{ - dump_thread_list(with_stack_traces); -} - -bool Scheduler::is_initialized() -{ - // The scheduler is initialized iff the idle thread exists - return Processor::idle_thread() != nullptr; -} - -TotalTimeScheduled Scheduler::get_total_time_scheduled() -{ - return g_total_time_scheduled.with([&](auto& total_time_scheduled) { return total_time_scheduled; }); -} - -void dump_thread_list(bool with_stack_traces) -{ - dbgln("Scheduler thread list for processor {}:", Processor::current_id()); - - auto get_eip = [](Thread& thread) -> u32 { - if (!thread.current_trap()) - return thread.regs().ip(); - return thread.get_register_dump_from_stack().ip(); - }; - - Thread::for_each_ignoring_jails([&](Thread& thread) { - auto color = thread.process().is_kernel_process() ? "\x1b[34;1m"sv : "\x1b[33;1m"sv; - switch (thread.state()) { - case Thread::State::Dying: - dmesgln(" {}{:30}\x1b[0m @ {:08x} is {:14} (Finalizable: {}, nsched: {})", - color, - thread, - get_eip(thread), - thread.state_string(), - thread.is_finalizable(), - thread.times_scheduled()); - break; - default: - dmesgln(" {}{:30}\x1b[0m @ {:08x} is {:14} (Pr:{:2}, nsched: {})", - color, - thread, - get_eip(thread), - thread.state_string(), - thread.priority(), - thread.times_scheduled()); - break; - } - if (thread.state() == Thread::State::Blocked && thread.blocking_mutex()) { - dmesgln(" Blocking on Mutex {:#x} ({})", thread.blocking_mutex(), thread.blocking_mutex()->name()); - } - if (thread.state() == Thread::State::Blocked && thread.blocker()) { - dmesgln(" Blocking on Blocker {:#x}", thread.blocker()); - } -#if LOCK_DEBUG - thread.for_each_held_lock([](auto const& entry) { - dmesgln(" Holding lock {:#x} ({}) at {}", entry.lock, entry.lock->name(), entry.lock_location); - }); -#endif - if (with_stack_traces) { - thread.print_backtrace(); - } - return IterationDecision::Continue; - }); -} - -} diff --git a/Kernel/Tasks/Scheduler.h b/Kernel/Tasks/Scheduler.h deleted file mode 100644 index ae7b6a5783e..00000000000 --- a/Kernel/Tasks/Scheduler.h +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include -#include -#include -#include - -namespace Kernel { - -struct RegisterState; - -extern Thread* g_finalizer; -extern WaitQueue* g_finalizer_wait_queue; -extern Atomic g_finalizer_has_work; -extern RecursiveSpinlock g_scheduler_lock; - -struct TotalTimeScheduled { - u64 total { 0 }; - u64 total_kernel { 0 }; -}; - -class Scheduler { -public: - static void initialize(); - static Thread* create_ap_idle_thread(u32 cpu); - static void set_idle_thread(Thread* idle_thread); - static void timer_tick(RegisterState const&); - [[noreturn]] static void start(); - static void pick_next(); - static void yield(); - static void context_switch(Thread*); - static void enter_current(Thread& prev_thread); - static void leave_on_first_switch(InterruptsState); - static void prepare_after_exec(); - static void prepare_for_idle_loop(); - static Process* colonel(); - static void idle_loop(void*); - static void invoke_async(); - static void notify_finalizer(); - static Thread& pull_next_runnable_thread(); - static Thread* peek_next_runnable_thread(); - static bool dequeue_runnable_thread(Thread&, bool = false); - static void enqueue_runnable_thread(Thread&); - static void dump_scheduler_state(bool = false); - static bool is_initialized(); - static TotalTimeScheduled get_total_time_scheduled(); - static void add_time_scheduled(u64, bool); -}; - -} diff --git a/Kernel/Tasks/SyncTask.cpp b/Kernel/Tasks/SyncTask.cpp deleted file mode 100644 index 9e7b87d6ffe..00000000000 --- a/Kernel/Tasks/SyncTask.cpp +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright (c) 2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include - -namespace Kernel { - -UNMAP_AFTER_INIT void SyncTask::spawn() -{ - MUST(Process::create_kernel_process("VFS Sync Task"sv, [] { - dbgln("VFS SyncTask is running"); - while (!Process::current().is_dying()) { - VirtualFileSystem::sync(); - (void)Thread::current()->sleep(Duration::from_seconds(1)); - } - Process::current().sys$exit(0); - VERIFY_NOT_REACHED(); - })); -} - -} diff --git a/Kernel/Tasks/SyncTask.h b/Kernel/Tasks/SyncTask.h deleted file mode 100644 index 9602168be1a..00000000000 --- a/Kernel/Tasks/SyncTask.h +++ /dev/null @@ -1,14 +0,0 @@ -/* - * Copyright (c) 2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -namespace Kernel { -class SyncTask { -public: - static void spawn(); -}; -} diff --git a/Kernel/Tasks/Thread.cpp b/Kernel/Tasks/Thread.cpp deleted file mode 100644 index 133b0933639..00000000000 --- a/Kernel/Tasks/Thread.cpp +++ /dev/null @@ -1,1455 +0,0 @@ -/* - * Copyright (c) 2018-2021, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace Kernel { - -static Singleton> s_list; - -SpinlockProtected& Thread::all_instances() -{ - return *s_list; -} - -ErrorOr> Thread::create(NonnullRefPtr process) -{ - auto kernel_stack_region = TRY(MM.allocate_kernel_region(default_kernel_stack_size, {}, Memory::Region::Access::ReadWrite, AllocationStrategy::AllocateNow)); - kernel_stack_region->set_stack(true); - - auto block_timer = TRY(try_make_ref_counted()); - - return adopt_nonnull_ref_or_enomem(new (nothrow) Thread(move(process), move(kernel_stack_region), move(block_timer))); -} - -Thread::Thread(NonnullRefPtr process, NonnullOwnPtr kernel_stack_region, NonnullRefPtr block_timer) - : m_process(move(process)) - , m_kernel_stack_region(move(kernel_stack_region)) - , m_block_timer(move(block_timer)) -{ - m_process->name().with([this](auto& process_name) { - set_name(process_name.representable_view()); - }); - - bool is_first_thread = m_process->add_thread(*this); - if (is_first_thread) { - // First thread gets TID == PID - m_tid = m_process->pid().value(); - } else { - m_tid = Process::allocate_pid().value(); - } - - // FIXME: Handle KString allocation failure. - m_kernel_stack_region->set_name(MUST(KString::formatted("Kernel stack (thread {})", m_tid.value()))); - - Thread::all_instances().with([&](auto& list) { - list.append(*this); - }); - - if constexpr (THREAD_DEBUG) { - m_process->name().with([&](auto& process_name) { - dbgln("Created new thread {}({}:{})", process_name.representable_view(), m_process->pid().value(), m_tid.value()); - }); - } - - reset_fpu_state(); - - m_kernel_stack_base = m_kernel_stack_region->vaddr().get(); - m_kernel_stack_top = m_kernel_stack_region->vaddr().offset(default_kernel_stack_size).get() & ~(FlatPtr)0x7u; - - m_process->address_space().with([&](auto& space) { - m_regs.set_initial_state(m_process->is_kernel_process(), *space, m_kernel_stack_top); - }); - - // We need to add another reference if we could successfully create - // all the resources needed for this thread. The reason for this is that - // we don't want to delete this thread after dropping the reference, - // it may still be running or scheduled to be run. - // The finalizer is responsible for dropping this reference once this - // thread is ready to be cleaned up. - ref(); -} - -Thread::~Thread() -{ - VERIFY(!m_process_thread_list_node.is_in_list()); - - // We shouldn't be queued - VERIFY(m_runnable_priority < 0); -} - -Thread::BlockResult Thread::block_impl(BlockTimeout const& timeout, Blocker& blocker) -{ - VERIFY(!Processor::current_in_irq()); - VERIFY(this == Thread::current()); - ScopedCritical critical; - - SpinlockLocker scheduler_lock(g_scheduler_lock); - - SpinlockLocker block_lock(m_block_lock); - // We need to hold m_block_lock so that nobody can unblock a blocker as soon - // as it is constructed and registered elsewhere - - ScopeGuard finalize_guard([&] { - blocker.finalize(); - }); - - if (!blocker.setup_blocker()) { - blocker.will_unblock_immediately_without_blocking(Blocker::UnblockImmediatelyReason::UnblockConditionAlreadyMet); - return BlockResult::NotBlocked; - } - - // Relaxed semantics are fine for timeout_unblocked because we - // synchronize on the spin locks already. - Atomic timeout_unblocked(false); - bool timer_was_added = false; - - switch (state()) { - case Thread::State::Stopped: - // It's possible that we were requested to be stopped! - break; - case Thread::State::Running: - VERIFY(m_blocker == nullptr); - break; - default: - VERIFY_NOT_REACHED(); - } - - m_blocker = &blocker; - - if (auto& block_timeout = blocker.override_timeout(timeout); !block_timeout.is_infinite()) { - // Process::kill_all_threads may be called at any time, which will mark all - // threads to die. In that case - timer_was_added = TimerQueue::the().add_timer_without_id(*m_block_timer, block_timeout.clock_id(), block_timeout.absolute_time(), [&]() { - VERIFY(!Processor::current_in_irq()); - VERIFY(!g_scheduler_lock.is_locked_by_current_processor()); - VERIFY(!m_block_lock.is_locked_by_current_processor()); - // NOTE: this may execute on the same or any other processor! - SpinlockLocker scheduler_lock(g_scheduler_lock); - SpinlockLocker block_lock(m_block_lock); - if (m_blocker && !timeout_unblocked.exchange(true)) - unblock(); - }); - if (!timer_was_added) { - // Timeout is already in the past - blocker.will_unblock_immediately_without_blocking(Blocker::UnblockImmediatelyReason::TimeoutInThePast); - m_blocker = nullptr; - return BlockResult::InterruptedByTimeout; - } - } - - blocker.begin_blocking({}); - - set_state(Thread::State::Blocked); - - block_lock.unlock(); - scheduler_lock.unlock(); - - dbgln_if(THREAD_DEBUG, "Thread {} blocking on {} ({}) -->", *this, &blocker, blocker.state_string()); - bool did_timeout = false; - u32 lock_count_to_restore = 0; - auto previous_locked = unlock_process_if_locked(lock_count_to_restore); - for (;;) { - // Yield to the scheduler, and wait for us to resume unblocked. - VERIFY(!g_scheduler_lock.is_locked_by_current_processor()); - VERIFY(Processor::in_critical()); - yield_without_releasing_big_lock(); - VERIFY(Processor::in_critical()); - - SpinlockLocker block_lock2(m_block_lock); - if (m_blocker && !m_blocker->can_be_interrupted() && !m_should_die) { - block_lock2.unlock(); - dbgln("Thread should not be unblocking, current state: {}", state_string()); - set_state(Thread::State::Blocked); - continue; - } - // Prevent the timeout from unblocking this thread if it happens to - // be in the process of firing already - did_timeout |= timeout_unblocked.exchange(true); - if (m_blocker) { - // Remove ourselves... - VERIFY(m_blocker == &blocker); - m_blocker = nullptr; - } - dbgln_if(THREAD_DEBUG, "<-- Thread {} unblocked from {} ({})", *this, &blocker, blocker.state_string()); - break; - } - - // Notify the blocker that we are no longer blocking. It may need - // to clean up now while we're still holding m_lock - auto result = blocker.end_blocking({}, did_timeout); // calls was_unblocked internally - - if (timer_was_added && !did_timeout) { - // Cancel the timer while not holding any locks. This allows - // the timer function to complete before we remove it - // (e.g. if it's on another processor) - TimerQueue::the().cancel_timer(*m_block_timer); - } - if (previous_locked != LockMode::Unlocked) { - // NOTE: This may trigger another call to Thread::block(). - relock_process(previous_locked, lock_count_to_restore); - } - return result; -} - -void Thread::block(Kernel::Mutex& lock, SpinlockLocker>& lock_lock, u32 lock_count) -{ - VERIFY(!Processor::current_in_irq()); - VERIFY(this == Thread::current()); - ScopedCritical critical; - - SpinlockLocker scheduler_lock(g_scheduler_lock); - SpinlockLocker block_lock(m_block_lock); - - switch (state()) { - case Thread::State::Stopped: - // It's possible that we were requested to be stopped! - break; - case Thread::State::Running: - VERIFY(m_blocker == nullptr); - break; - default: - dbgln("Error: Attempting to block with invalid thread state - {}", state_string()); - VERIFY_NOT_REACHED(); - } - - // If we're blocking on the big-lock we may actually be in the process - // of unblocking from another lock. If that's the case m_blocking_mutex - // is already set - auto& big_lock = process().big_lock(); - VERIFY((&lock == &big_lock && m_blocking_mutex != &big_lock) || !m_blocking_mutex); - - auto* previous_blocking_mutex = m_blocking_mutex; - m_blocking_mutex = &lock; - m_lock_requested_count = lock_count; - - set_state(Thread::State::Blocked); - - block_lock.unlock(); - scheduler_lock.unlock(); - - lock_lock.unlock(); - - dbgln_if(THREAD_DEBUG, "Thread {} blocking on Mutex {}", *this, &lock); - - for (;;) { - // Yield to the scheduler, and wait for us to resume unblocked. - VERIFY(!g_scheduler_lock.is_locked_by_current_processor()); - VERIFY(Processor::in_critical()); - if (&lock != &big_lock && big_lock.is_exclusively_locked_by_current_thread()) { - // We're locking another lock and already hold the big lock... - // We need to release the big lock - yield_and_release_relock_big_lock(); - } else { - // By the time we've reached this another thread might have - // marked us as holding the big lock, so this call must not - // verify that we're not holding it. - yield_without_releasing_big_lock(VerifyLockNotHeld::No); - } - VERIFY(Processor::in_critical()); - - SpinlockLocker block_lock2(m_block_lock); - VERIFY(!m_blocking_mutex); - m_blocking_mutex = previous_blocking_mutex; - break; - } - - lock_lock.lock(); -} - -u32 Thread::unblock_from_mutex(Kernel::Mutex& mutex) -{ - SpinlockLocker scheduler_lock(g_scheduler_lock); - SpinlockLocker block_lock(m_block_lock); - - VERIFY(!Processor::current_in_irq()); - VERIFY(m_blocking_mutex == &mutex); - - dbgln_if(THREAD_DEBUG, "Thread {} unblocked from Mutex {}", *this, &mutex); - - auto requested_count = m_lock_requested_count; - - m_blocking_mutex = nullptr; - if (Thread::current() == this) { - set_state(Thread::State::Running); - return requested_count; - } - VERIFY(m_state != Thread::State::Runnable && m_state != Thread::State::Running); - set_state(Thread::State::Runnable); - return requested_count; -} - -void Thread::unblock_from_blocker(Blocker& blocker) -{ - auto do_unblock = [&]() { - SpinlockLocker scheduler_lock(g_scheduler_lock); - SpinlockLocker block_lock(m_block_lock); - if (m_blocker != &blocker) - return; - VERIFY(!is_stopped()); - unblock(); - }; - if (Processor::current_in_irq() != 0) { - Processor::deferred_call_queue([do_unblock = move(do_unblock), self = try_make_weak_ptr().release_value_but_fixme_should_propagate_errors()]() { - if (auto this_thread = self.strong_ref()) - do_unblock(); - }); - } else { - do_unblock(); - } -} - -void Thread::unblock(u8 signal) -{ - VERIFY(!Processor::current_in_irq()); - VERIFY(g_scheduler_lock.is_locked_by_current_processor()); - VERIFY(m_block_lock.is_locked_by_current_processor()); - if (m_state != Thread::State::Blocked) - return; - if (m_blocking_mutex) - return; - VERIFY(m_blocker); - if (signal != 0) { - if (is_handling_page_fault()) { - // Don't let signals unblock threads that are blocked inside a page fault handler. - // This prevents threads from EINTR'ing the inode read in an inode page fault. - // FIXME: There's probably a better way to solve this. - return; - } - if (!m_blocker->can_be_interrupted() && !m_should_die) - return; - m_blocker->set_interrupted_by_signal(signal); - } - m_blocker = nullptr; - if (Thread::current() == this) { - set_state(Thread::State::Running); - return; - } - VERIFY(m_state != Thread::State::Runnable && m_state != Thread::State::Running); - set_state(Thread::State::Runnable); -} - -void Thread::set_should_die() -{ - if (m_should_die) { - dbgln("{} Should already die", *this); - return; - } - ScopedCritical critical; - - // Remember that we should die instead of returning to - // the userspace. - SpinlockLocker lock(g_scheduler_lock); - m_should_die = true; - - // NOTE: Even the current thread can technically be in "Stopped" - // state! This is the case when another thread sent a SIGSTOP to - // it while it was running and it calls e.g. exit() before - // the scheduler gets involved again. - if (is_stopped()) { - // If we were stopped, we need to briefly resume so that - // the kernel stacks can clean up. We won't ever return back - // to user mode, though - VERIFY(!process().is_stopped()); - resume_from_stopped(); - } - if (is_blocked()) { - SpinlockLocker block_lock(m_block_lock); - if (m_blocker) { - // We're blocked in the kernel. - m_blocker->set_interrupted_by_death(); - unblock(); - } - } -} - -void Thread::die_if_needed() -{ - VERIFY(Thread::current() == this); - - if (!m_should_die) - return; - - u32 unlock_count; - [[maybe_unused]] auto rc = unlock_process_if_locked(unlock_count); - - dbgln_if(THREAD_DEBUG, "Thread {} is dying", *this); - - { - SpinlockLocker lock(g_scheduler_lock); - // It's possible that we don't reach the code after this block if the - // scheduler is invoked and FinalizerTask cleans up this thread, however - // that doesn't matter because we're trying to invoke the scheduler anyway - set_state(Thread::State::Dying); - } - - ScopedCritical critical; - - // Flag a context switch. Because we're in a critical section, - // Scheduler::yield will actually only mark a pending context switch - // Simply leaving the critical section would not necessarily trigger - // a switch. - Scheduler::yield(); - - // Now leave the critical section so that we can also trigger the - // actual context switch - Processor::clear_critical(); - dbgln("die_if_needed returned from clear_critical!!! in irq: {}", Processor::current_in_irq()); - // We should never get here, but the scoped scheduler lock - // will be released by Scheduler::context_switch again - VERIFY_NOT_REACHED(); -} - -void Thread::exit(void* exit_value) -{ - VERIFY(Thread::current() == this); - m_join_blocker_set.thread_did_exit(exit_value); - set_should_die(); - u32 unlock_count; - [[maybe_unused]] auto rc = unlock_process_if_locked(unlock_count); - die_if_needed(); -} - -void Thread::yield_without_releasing_big_lock(VerifyLockNotHeld verify_lock_not_held) -{ - VERIFY(!g_scheduler_lock.is_locked_by_current_processor()); - VERIFY(verify_lock_not_held == VerifyLockNotHeld::No || !process().big_lock().is_exclusively_locked_by_current_thread()); - // Disable interrupts here. This ensures we don't accidentally switch contexts twice - InterruptDisabler disable; - Scheduler::yield(); // flag a switch - u32 prev_critical = Processor::clear_critical(); - // NOTE: We may be on a different CPU now! - Processor::restore_critical(prev_critical); -} - -void Thread::yield_and_release_relock_big_lock() -{ - VERIFY(!g_scheduler_lock.is_locked_by_current_processor()); - // Disable interrupts here. This ensures we don't accidentally switch contexts twice - InterruptDisabler disable; - Scheduler::yield(); // flag a switch - u32 lock_count_to_restore = 0; - auto previous_locked = unlock_process_if_locked(lock_count_to_restore); - // NOTE: Even though we call Scheduler::yield here, unless we happen - // to be outside of a critical section, the yield will be postponed - // until leaving it in relock_process. - relock_process(previous_locked, lock_count_to_restore); -} - -LockMode Thread::unlock_process_if_locked(u32& lock_count_to_restore) -{ - return process().big_lock().force_unlock_exclusive_if_locked(lock_count_to_restore); -} - -void Thread::relock_process(LockMode previous_locked, u32 lock_count_to_restore) -{ - // Clearing the critical section may trigger the context switch - // flagged by calling Scheduler::yield above. - // We have to do it this way because we intentionally - // leave the critical section here to be able to switch contexts. - u32 prev_critical = Processor::clear_critical(); - - // CONTEXT SWITCH HAPPENS HERE! - - // NOTE: We may be on a different CPU now! - Processor::restore_critical(prev_critical); - - if (previous_locked != LockMode::Unlocked) { - // We've unblocked, relock the process if needed and carry on. - process().big_lock().restore_exclusive_lock(lock_count_to_restore); - } -} - -// NOLINTNEXTLINE(readability-make-member-function-const) False positive; We call block which is not const -auto Thread::sleep(clockid_t clock_id, Duration const& duration, Duration* remaining_time) -> BlockResult -{ - VERIFY(state() == Thread::State::Running); - return Thread::current()->block({}, Thread::BlockTimeout(false, &duration, nullptr, clock_id), remaining_time); -} - -// NOLINTNEXTLINE(readability-make-member-function-const) False positive; We call block which is not const -auto Thread::sleep_until(clockid_t clock_id, Duration const& deadline) -> BlockResult -{ - VERIFY(state() == Thread::State::Running); - return Thread::current()->block({}, Thread::BlockTimeout(true, &deadline, nullptr, clock_id)); -} - -StringView Thread::state_string() const -{ - switch (state()) { - case Thread::State::Invalid: - return "Invalid"sv; - case Thread::State::Runnable: - return "Runnable"sv; - case Thread::State::Running: - return "Running"sv; - case Thread::State::Dying: - return "Dying"sv; - case Thread::State::Dead: - return "Dead"sv; - case Thread::State::Stopped: - return "Stopped"sv; - case Thread::State::Blocked: { - SpinlockLocker block_lock(m_block_lock); - if (m_blocking_mutex) - return "Mutex"sv; - if (m_blocker) - return m_blocker->state_string(); - VERIFY_NOT_REACHED(); - } - } - PANIC("Thread::state_string(): Invalid state: {}", (int)state()); -} - -void Thread::finalize() -{ - if (!g_in_system_shutdown) - VERIFY(Thread::current() == g_finalizer); - VERIFY(Thread::current() != this); - -#if LOCK_DEBUG - VERIFY(!m_lock.is_locked_by_current_processor()); - if (lock_count() > 0) { - dbgln("Thread {} leaking {} Locks!", *this, lock_count()); - SpinlockLocker list_lock(m_holding_locks_lock); - for (auto& info : m_holding_locks_list) { - auto const& location = info.lock_location; - dbgln(" - Mutex: \"{}\" @ {} locked in function \"{}\" at \"{}:{}\" with a count of: {}", info.lock->name(), info.lock, location.function_name(), location.filename(), location.line_number(), info.count); - } - VERIFY_NOT_REACHED(); - } -#endif - - { - SpinlockLocker lock(g_scheduler_lock); - dbgln_if(THREAD_DEBUG, "Finalizing thread {}", *this); - set_state(Thread::State::Dead); - m_join_blocker_set.thread_finalizing(); - } - - if (m_dump_backtrace_on_finalization) { - print_backtrace(); - } - - drop_thread_count(); -} - -void Thread::drop_thread_count() -{ - bool is_last = process().remove_thread(*this); - if (is_last) - process().finalize(); -} - -void Thread::finalize_dying_threads() -{ - VERIFY(Thread::current() == g_finalizer); - Vector dying_threads; - { - SpinlockLocker lock(g_scheduler_lock); - for_each_in_state_ignoring_jails(Thread::State::Dying, [&](Thread& thread) { - if (!thread.is_finalizable()) - return; - auto result = dying_threads.try_append(&thread); - // We ignore allocation failures above the first 32 guaranteed thread slots, and - // just flag our future-selves to finalize these threads at a later point - if (result.is_error()) - g_finalizer_has_work.store(true, AK::MemoryOrder::memory_order_release); - }); - } - for (auto* thread : dying_threads) { - RefPtr const process = thread->process(); - dbgln_if(PROCESS_DEBUG, "Before finalization, {} has {} refs and its process has {}", - *thread, thread->ref_count(), thread->process().ref_count()); - thread->finalize(); - dbgln_if(PROCESS_DEBUG, "After finalization, {} has {} refs and its process has {}", - *thread, thread->ref_count(), thread->process().ref_count()); - // This thread will never execute again, drop the running reference - // NOTE: This may not necessarily drop the last reference if anything - // else is still holding onto this thread! - thread->unref(); - } -} - -void Thread::update_time_scheduled(u64 current_scheduler_time, bool is_kernel, bool no_longer_running) -{ - if (m_last_time_scheduled.has_value()) { - u64 delta; - if (current_scheduler_time >= m_last_time_scheduled.value()) - delta = current_scheduler_time - m_last_time_scheduled.value(); - else - delta = m_last_time_scheduled.value() - current_scheduler_time; // the unlikely event that the clock wrapped - if (delta != 0) { - // Add it to the global total *before* updating the thread's value! - Scheduler::add_time_scheduled(delta, is_kernel); - - auto& total_time = is_kernel ? m_total_time_scheduled_kernel : m_total_time_scheduled_user; - total_time.fetch_add(delta, AK::memory_order_relaxed); - } - } - if (no_longer_running) - m_last_time_scheduled = {}; - else - m_last_time_scheduled = current_scheduler_time; -} - -bool Thread::tick() -{ - if (previous_mode() == ExecutionMode::Kernel) { - ++m_process->m_ticks_in_kernel; - ++m_ticks_in_kernel; - } else { - ++m_process->m_ticks_in_user; - ++m_ticks_in_user; - } - --m_ticks_left; - return m_ticks_left != 0; -} - -void Thread::check_dispatch_pending_signal() -{ - auto result = DispatchSignalResult::Continue; - { - SpinlockLocker scheduler_lock(g_scheduler_lock); - if (pending_signals_for_state() != 0) { - result = dispatch_one_pending_signal(); - } - } - - if (result == DispatchSignalResult::Yield) { - yield_without_releasing_big_lock(); - } -} - -u32 Thread::pending_signals() const -{ - SpinlockLocker lock(g_scheduler_lock); - return pending_signals_for_state(); -} - -u32 Thread::pending_signals_for_state() const -{ - VERIFY(g_scheduler_lock.is_locked_by_current_processor()); - constexpr u32 stopped_signal_mask = (1 << (SIGCONT - 1)) | (1 << (SIGKILL - 1)) | (1 << (SIGTRAP - 1)); - if (is_handling_page_fault()) - return 0; - return m_state != State::Stopped ? m_pending_signals : m_pending_signals & stopped_signal_mask; -} - -void Thread::send_signal(u8 signal, [[maybe_unused]] Process* sender) -{ - VERIFY(signal < NSIG); - VERIFY(process().is_user_process()); - SpinlockLocker scheduler_lock(g_scheduler_lock); - - // FIXME: Figure out what to do for masked signals. Should we also ignore them here? - if (should_ignore_signal(signal)) { - dbgln_if(SIGNAL_DEBUG, "Signal {} was ignored by {}", signal, process()); - return; - } - - if constexpr (SIGNAL_DEBUG) { - if (sender) - dbgln("Signal: {} sent {} to {}", *sender, signal, process()); - else - dbgln("Signal: Kernel send {} to {}", signal, process()); - } - - m_pending_signals |= 1 << (signal - 1); - m_signal_senders[signal] = sender ? sender->pid() : pid(); - m_have_any_unmasked_pending_signals.store((pending_signals_for_state() & ~m_signal_mask) != 0, AK::memory_order_release); - m_signal_blocker_set.unblock_all_blockers_whose_conditions_are_met(); - - if (!has_unmasked_pending_signals()) - return; - - if (m_state == Thread::State::Stopped) { - if (pending_signals_for_state() != 0) { - dbgln_if(SIGNAL_DEBUG, "Signal: Resuming stopped {} to deliver signal {}", *this, signal); - resume_from_stopped(); - } - } else { - SpinlockLocker block_lock(m_block_lock); - dbgln_if(SIGNAL_DEBUG, "Signal: Unblocking {} to deliver signal {}", *this, signal); - unblock(signal); - } -} - -u32 Thread::update_signal_mask(u32 signal_mask) -{ - SpinlockLocker lock(g_scheduler_lock); - auto previous_signal_mask = m_signal_mask; - m_signal_mask = signal_mask; - m_have_any_unmasked_pending_signals.store((pending_signals_for_state() & ~m_signal_mask) != 0, AK::memory_order_release); - return previous_signal_mask; -} - -u32 Thread::signal_mask() const -{ - SpinlockLocker lock(g_scheduler_lock); - return m_signal_mask; -} - -u32 Thread::signal_mask_block(sigset_t signal_set, bool block) -{ - SpinlockLocker lock(g_scheduler_lock); - auto previous_signal_mask = m_signal_mask; - if (block) - m_signal_mask |= signal_set; - else - m_signal_mask &= ~signal_set; - m_have_any_unmasked_pending_signals.store((pending_signals_for_state() & ~m_signal_mask) != 0, AK::memory_order_release); - return previous_signal_mask; -} - -void Thread::reset_signals_for_exec() -{ - SpinlockLocker lock(g_scheduler_lock); - // The signal mask is preserved across execve(2). - // The pending signal set is preserved across an execve(2). - m_have_any_unmasked_pending_signals.store(false, AK::memory_order_release); - m_signal_action_masks.fill({}); - // A successful call to execve(2) removes any existing alternate signal stack - m_alternative_signal_stack.clear(); -} - -// Certain exceptions, such as SIGSEGV and SIGILL, put a -// thread into a state where the signal handler must be -// invoked immediately, otherwise it will continue to fault. -// This function should be used in an exception handler to -// ensure that when the thread resumes, it's executing in -// the appropriate signal handler. -void Thread::send_urgent_signal_to_self(u8 signal) -{ - VERIFY(Thread::current() == this); - DispatchSignalResult result; - { - SpinlockLocker lock(g_scheduler_lock); - result = dispatch_signal(signal); - } - if (result == DispatchSignalResult::Terminate) { - Thread::current()->die_if_needed(); - VERIFY_NOT_REACHED(); // dispatch_signal will request termination of the thread, so the above call should never return - } - if (result == DispatchSignalResult::Yield) - yield_and_release_relock_big_lock(); -} - -DispatchSignalResult Thread::dispatch_one_pending_signal() -{ - VERIFY(g_scheduler_lock.is_locked_by_current_processor()); - u32 signal_candidates = pending_signals_for_state() & ~m_signal_mask; - if (signal_candidates == 0) - return DispatchSignalResult::Continue; - - u8 signal = 1; - for (; signal < NSIG; ++signal) { - if ((signal_candidates & (1 << (signal - 1))) != 0) { - break; - } - } - return dispatch_signal(signal); -} - -DispatchSignalResult Thread::try_dispatch_one_pending_signal(u8 signal) -{ - VERIFY(signal != 0); - SpinlockLocker scheduler_lock(g_scheduler_lock); - u32 signal_candidates = pending_signals_for_state() & ~m_signal_mask; - if ((signal_candidates & (1 << (signal - 1))) == 0) - return DispatchSignalResult::Continue; - return dispatch_signal(signal); -} - -enum class DefaultSignalAction { - Terminate, - Ignore, - DumpCore, - Stop, - Continue, -}; - -static DefaultSignalAction default_signal_action(u8 signal) -{ - VERIFY(signal && signal < NSIG); - - switch (signal) { - case SIGHUP: - case SIGINT: - case SIGKILL: - case SIGPIPE: - case SIGALRM: - case SIGUSR1: - case SIGUSR2: - case SIGVTALRM: - case SIGSTKFLT: - case SIGIO: - case SIGPROF: - case SIGTERM: - case SIGCANCEL: - return DefaultSignalAction::Terminate; - case SIGCHLD: - case SIGURG: - case SIGWINCH: - case SIGINFO: - return DefaultSignalAction::Ignore; - case SIGQUIT: - case SIGILL: - case SIGTRAP: - case SIGABRT: - case SIGBUS: - case SIGFPE: - case SIGSEGV: - case SIGXCPU: - case SIGXFSZ: - case SIGSYS: - return DefaultSignalAction::DumpCore; - case SIGCONT: - return DefaultSignalAction::Continue; - case SIGSTOP: - case SIGTSTP: - case SIGTTIN: - case SIGTTOU: - return DefaultSignalAction::Stop; - default: - VERIFY_NOT_REACHED(); - } -} - -bool Thread::should_ignore_signal(u8 signal) const -{ - VERIFY(signal < NSIG); - auto const& action = m_process->m_signal_action_data[signal]; - if (action.handler_or_sigaction.is_null()) - return default_signal_action(signal) == DefaultSignalAction::Ignore; - return ((sighandler_t)action.handler_or_sigaction.get() == SIG_IGN); -} - -bool Thread::has_signal_handler(u8 signal) const -{ - VERIFY(signal < NSIG); - auto const& action = m_process->m_signal_action_data[signal]; - return !action.handler_or_sigaction.is_null(); -} - -bool Thread::is_signal_masked(u8 signal) const -{ - VERIFY(signal < NSIG); - return (1 << (signal - 1)) & m_signal_mask; -} - -bool Thread::is_in_alternative_signal_stack() const -{ - auto sp = get_register_dump_from_stack().userspace_sp(); - if (!m_alternative_signal_stack.has_value()) - return false; - return m_alternative_signal_stack->contains(VirtualAddress(sp)); -} - -static ErrorOr push_value_on_user_stack(FlatPtr& stack, FlatPtr data) -{ - stack -= sizeof(FlatPtr); - return copy_to_user((FlatPtr*)stack, &data); -} - -template -static ErrorOr copy_value_on_user_stack(FlatPtr& stack, T const& data) -{ - stack -= sizeof(data); - return copy_to_user((RemoveCVReference*)stack, &data); -} - -void Thread::resume_from_stopped() -{ - VERIFY(is_stopped()); - VERIFY(m_stop_state != State::Invalid); - VERIFY(g_scheduler_lock.is_locked_by_current_processor()); - if (m_stop_state == Thread::State::Blocked) { - SpinlockLocker block_lock(m_block_lock); - if (m_blocker || m_blocking_mutex) { - // Hasn't been unblocked yet - set_state(Thread::State::Blocked, 0); - } else { - // Was unblocked while stopped - set_state(Thread::State::Runnable); - } - } else { - set_state(m_stop_state, 0); - } -} - -DispatchSignalResult Thread::dispatch_signal(u8 signal) -{ - VERIFY_INTERRUPTS_DISABLED(); - VERIFY(g_scheduler_lock.is_locked_by_current_processor()); - VERIFY(signal > 0 && signal <= NSIG); - VERIFY(process().is_user_process()); - VERIFY(this == Thread::current()); - - dbgln_if(SIGNAL_DEBUG, "Dispatch signal {} to {}, state: {}", signal, *this, state_string()); - - if (m_state == Thread::State::Invalid || !is_initialized()) { - // Thread has barely been created, we need to wait until it is - // at least in Runnable state and is_initialized() returns true, - // which indicates that it is fully set up an we actually have - // a register state on the stack that we can modify - return DispatchSignalResult::Deferred; - } - - auto& action = m_process->m_signal_action_data[signal]; - auto sender_pid = m_signal_senders[signal]; - auto sender = Process::from_pid_ignoring_jails(sender_pid); - - if (!current_trap() && !action.handler_or_sigaction.is_null()) { - // We're trying dispatch a handled signal to a user process that was scheduled - // after a yielding/blocking kernel thread, we don't have a register capture of - // the thread, so just defer processing the signal to later. - return DispatchSignalResult::Deferred; - } - - // Mark this signal as handled. - m_pending_signals &= ~(1 << (signal - 1)); - m_have_any_unmasked_pending_signals.store((m_pending_signals & ~m_signal_mask) != 0, AK::memory_order_release); - - auto& process = this->process(); - auto* tracer = process.tracer(); - if (signal == SIGSTOP || (tracer && default_signal_action(signal) == DefaultSignalAction::DumpCore)) { - dbgln_if(SIGNAL_DEBUG, "Signal {} stopping this thread", signal); - if (tracer) - tracer->set_regs(get_register_dump_from_stack()); - set_state(Thread::State::Stopped, signal); - return DispatchSignalResult::Yield; - } - - if (signal == SIGCONT) { - dbgln_if(SIGNAL_DEBUG, "signal: SIGCONT resuming {}", *this); - } else { - if (tracer) { - // when a thread is traced, it should be stopped whenever it receives a signal - // the tracer is notified of this by using waitpid() - // only "pending signals" from the tracer are sent to the tracee - if (!tracer->has_pending_signal(signal)) { - dbgln_if(SIGNAL_DEBUG, "signal: {} stopping {} for tracer", signal, *this); - set_state(Thread::State::Stopped, signal); - return DispatchSignalResult::Yield; - } - tracer->unset_signal(signal); - } - } - - auto handler_vaddr = action.handler_or_sigaction; - if (handler_vaddr.is_null()) { - switch (default_signal_action(signal)) { - case DefaultSignalAction::Stop: - set_state(Thread::State::Stopped, signal); - return DispatchSignalResult::Yield; - case DefaultSignalAction::DumpCore: - process.set_should_generate_coredump(true); - process.for_each_thread([](auto& thread) { - thread.set_dump_backtrace_on_finalization(); - }); - [[fallthrough]]; - case DefaultSignalAction::Terminate: - m_process->terminate_due_to_signal(signal); - return DispatchSignalResult::Terminate; - case DefaultSignalAction::Ignore: - VERIFY_NOT_REACHED(); - case DefaultSignalAction::Continue: - return DispatchSignalResult::Continue; - } - VERIFY_NOT_REACHED(); - } - - if ((sighandler_t)handler_vaddr.as_ptr() == SIG_IGN) { - dbgln_if(SIGNAL_DEBUG, "Ignored signal {}", signal); - return DispatchSignalResult::Continue; - } - - ScopedAddressSpaceSwitcher switcher(m_process); - - m_currently_handled_signal = signal; - - u32 old_signal_mask = m_signal_mask; - u32 new_signal_mask = m_signal_action_masks[signal].value_or(action.mask); - if ((action.flags & SA_NODEFER) == SA_NODEFER) - new_signal_mask &= ~(1 << (signal - 1)); - else - new_signal_mask |= 1 << (signal - 1); - - m_signal_mask |= new_signal_mask; - m_have_any_unmasked_pending_signals.store((m_pending_signals & ~m_signal_mask) != 0, AK::memory_order_release); - - bool use_alternative_stack = ((action.flags & SA_ONSTACK) != 0) && m_alternative_signal_stack.has_value() && !is_in_alternative_signal_stack(); - - auto setup_stack = [&](RegisterState& state) -> ErrorOr { - FlatPtr stack; - if (use_alternative_stack) - stack = m_alternative_signal_stack->end().get(); - else - stack = state.userspace_sp(); - - dbgln_if(SIGNAL_DEBUG, "Setting up user stack to return to IP {:p}, SP {:p}", state.ip(), state.userspace_sp()); - - __ucontext ucontext { - .uc_link = nullptr, - .uc_sigmask = old_signal_mask, - .uc_stack = { - .ss_sp = bit_cast(stack), - .ss_flags = action.flags & SA_ONSTACK, - .ss_size = use_alternative_stack ? m_alternative_signal_stack->size() : 0, - }, - .uc_mcontext = {}, - }; - copy_kernel_registers_into_ptrace_registers(static_cast(ucontext.uc_mcontext), state); - - auto fill_signal_info_for_signal = [&](siginfo& signal_info) { - if (signal == SIGCHLD) { - if (!sender) { - signal_info.si_code = CLD_EXITED; - return; - } - auto const* thread = sender->thread_list().with([](auto& list) { return list.is_empty() ? nullptr : list.first(); }); - if (!thread) { - signal_info.si_code = CLD_EXITED; - return; - } - - switch (thread->m_state) { - case State::Dead: - if (sender->should_generate_coredump() && sender->is_dumpable()) { - signal_info.si_code = CLD_DUMPED; - signal_info.si_status = sender->termination_signal(); - return; - } - [[fallthrough]]; - case State::Dying: - if (sender->termination_signal() == 0) { - signal_info.si_code = CLD_EXITED; - signal_info.si_status = sender->termination_status(); - return; - } - signal_info.si_code = CLD_KILLED; - signal_info.si_status = sender->termination_signal(); - return; - case State::Runnable: - case State::Running: - case State::Blocked: - signal_info.si_code = CLD_CONTINUED; - return; - case State::Stopped: - signal_info.si_code = CLD_STOPPED; - return; - case State::Invalid: - // Something is wrong, but we're just an observer. - break; - } - } - - signal_info.si_code = SI_NOINFO; - }; - - siginfo signal_info { - .si_signo = signal, - // Filled in below by fill_signal_info_for_signal. - .si_code = 0, - // Set for SI_TIMER, we don't have the data here. - .si_errno = 0, - .si_pid = sender_pid.value(), - .si_uid = sender ? sender->credentials()->uid().value() : 0, - // Set for SIGILL, SIGFPE, SIGSEGV and SIGBUS - // FIXME: We don't generate these signals in a way that can be handled. - .si_addr = 0, - // Set for SIGCHLD. - .si_status = 0, - // Set for SIGPOLL, we don't have SIGPOLL. - .si_band = 0, - // Set for SI_QUEUE, SI_TIMER, SI_ASYNCIO and SI_MESGQ - // We do not generate any of these. - .si_value = { - .sival_int = 0, - }, - }; - - if (action.flags & SA_SIGINFO) - fill_signal_info_for_signal(signal_info); - - // Align the stack to 16 bytes. - // Note that we push some elements on to the stack before the return address, - // so we need to account for this here. - constexpr static FlatPtr elements_pushed_on_stack_before_handler_address = 1; // one slot for a saved register - FlatPtr const extra_bytes_pushed_on_stack_before_handler_address = sizeof(ucontext) + sizeof(signal_info); - FlatPtr stack_alignment = (stack - elements_pushed_on_stack_before_handler_address * sizeof(FlatPtr) + extra_bytes_pushed_on_stack_before_handler_address) % 16; - stack -= stack_alignment; - -#if ARCH(X86_64) - // Also note that we have to skip the thread red-zone (if needed), so do that here. - constexpr static FlatPtr thread_red_zone_size = 128; - stack -= thread_red_zone_size; -#endif - - auto start_of_stack = stack; - - TRY(push_value_on_user_stack(stack, 0)); // syscall return value slot - - TRY(copy_value_on_user_stack(stack, ucontext)); - auto pointer_to_ucontext = stack; - - TRY(copy_value_on_user_stack(stack, signal_info)); - auto pointer_to_signal_info = stack; - - // Make sure we actually pushed as many elements as we claimed to have pushed. - if (start_of_stack - stack != elements_pushed_on_stack_before_handler_address * sizeof(FlatPtr) + extra_bytes_pushed_on_stack_before_handler_address) { - PANIC("Stack in invalid state after signal trampoline, expected {:x} but got {:x}", - start_of_stack - elements_pushed_on_stack_before_handler_address * sizeof(FlatPtr) - extra_bytes_pushed_on_stack_before_handler_address, stack); - } - - VERIFY(stack % 16 == 0); - - // Save the FPU/SSE state - TRY(copy_value_on_user_stack(stack, fpu_state())); - - TRY(push_value_on_user_stack(stack, pointer_to_ucontext)); - TRY(push_value_on_user_stack(stack, pointer_to_signal_info)); - TRY(push_value_on_user_stack(stack, signal)); - - TRY(push_value_on_user_stack(stack, handler_vaddr.get())); - - // We write back the adjusted stack value into the register state. - // We have to do this because we can't just pass around a reference to a packed field, as it's UB. - state.set_userspace_sp(stack); - - return {}; - }; - - // We now place the thread state on the userspace stack. - // Note that we use a RegisterState. - // Conversely, when the thread isn't blocking the RegisterState may not be - // valid (fork, exec etc) but the tss will, so we use that instead. - auto& regs = get_register_dump_from_stack(); - - auto result = setup_stack(regs); - if (result.is_error()) { - dbgln("Invalid stack pointer: {}", regs.userspace_sp()); - process.set_should_generate_coredump(true); - process.for_each_thread([](auto& thread) { - thread.set_dump_backtrace_on_finalization(); - }); - m_process->terminate_due_to_signal(signal); - return DispatchSignalResult::Terminate; - } - - auto signal_trampoline_addr = process.signal_trampoline().get(); - regs.set_ip(signal_trampoline_addr); - -#if ARCH(X86_64) - // Userspace flags might be invalid for function entry, according to SYSV ABI (section 3.2.1). - // Set them to a known-good value to avoid weird handler misbehavior. - // Only IF (and the reserved bit 1) are set. - regs.set_flags(2 | (regs.rflags & ~safe_eflags_mask)); -#endif - - dbgln_if(SIGNAL_DEBUG, "Thread in state '{}' has been primed with signal handler {:p} to deliver {}", state_string(), m_regs.ip(), signal); - - return DispatchSignalResult::Continue; -} - -RegisterState& Thread::get_register_dump_from_stack() -{ - auto* trap = current_trap(); - - // We should *always* have a trap. If we don't we're probably a kernel - // thread that hasn't been preempted. If we want to support this, we - // need to capture the registers probably into m_regs and return it - VERIFY(trap); - - while (trap) { - if (!trap->next_trap) - break; - trap = trap->next_trap; - } - return *trap->regs; -} - -ErrorOr> Thread::clone(NonnullRefPtr process) -{ - auto clone = TRY(Thread::create(move(process))); - m_signal_action_masks.span().copy_to(clone->m_signal_action_masks); - clone->m_signal_mask = m_signal_mask; - clone->m_fpu_state = m_fpu_state; - clone->m_arch_specific_data = m_arch_specific_data; - return clone; -} - -void Thread::set_state(State new_state, u8 stop_signal) -{ - State previous_state; - VERIFY(g_scheduler_lock.is_locked_by_current_processor()); - if (new_state == m_state) - return; - - { - previous_state = m_state; - if (previous_state == Thread::State::Invalid) { - // If we were *just* created, we may have already pending signals - if (has_unmasked_pending_signals()) { - dbgln_if(THREAD_DEBUG, "Dispatch pending signals to new thread {}", *this); - dispatch_one_pending_signal(); - } - } - - m_state = new_state; - dbgln_if(THREAD_DEBUG, "Set thread {} state to {}", *this, state_string()); - } - - if (previous_state == Thread::State::Runnable) { - Scheduler::dequeue_runnable_thread(*this); - } else if (previous_state == Thread::State::Stopped) { - m_stop_state = State::Invalid; - auto& process = this->process(); - if (process.set_stopped(false)) { - process.for_each_thread([&](auto& thread) { - if (&thread == this) - return; - if (!thread.is_stopped()) - return; - dbgln_if(THREAD_DEBUG, "Resuming peer thread {}", thread); - thread.resume_from_stopped(); - }); - process.unblock_waiters(Thread::WaitBlocker::UnblockFlags::Continued); - // Tell the parent process (if any) about this change. - if (auto parent = Process::from_pid_ignoring_jails(process.ppid())) { - [[maybe_unused]] auto result = parent->send_signal(SIGCHLD, &process); - } - } - } - - if (m_state == Thread::State::Runnable) { - Scheduler::enqueue_runnable_thread(*this); - Processor::smp_wake_n_idle_processors(1); - } else if (m_state == Thread::State::Stopped) { - // We don't want to restore to Running state, only Runnable! - m_stop_state = previous_state != Thread::State::Running ? previous_state : Thread::State::Runnable; - auto& process = this->process(); - if (!process.set_stopped(true)) { - // Note that we don't explicitly stop peer threads, we let them stop on their own the next time they - // enter/exit a syscall, or once their current time slice runs out. - process.unblock_waiters(Thread::WaitBlocker::UnblockFlags::Stopped, stop_signal); - // Tell the parent process (if any) about this change. - if (auto parent = Process::from_pid_ignoring_jails(process.ppid())) { - [[maybe_unused]] auto result = parent->send_signal(SIGCHLD, &process); - } - } - } else if (m_state == Thread::State::Dying) { - VERIFY(previous_state != Thread::State::Blocked); - if (this != Thread::current() && is_finalizable()) { - // Some other thread set this thread to Dying, notify the - // finalizer right away as it can be cleaned up now - Scheduler::notify_finalizer(); - } - } -} - -struct RecognizedSymbol { - FlatPtr address; - KernelSymbol const* symbol { nullptr }; -}; - -static ErrorOr symbolicate(RecognizedSymbol const& symbol, Process& process, StringBuilder& builder) -{ - if (symbol.address == 0) - return false; - - auto credentials = process.credentials(); - bool mask_kernel_addresses = !credentials->is_superuser(); - if (!symbol.symbol) { - if (!Memory::is_user_address(VirtualAddress(symbol.address))) { - TRY(builder.try_append("0xdeadc0de\n"sv)); - } else { - TRY(process.address_space().with([&](auto& space) -> ErrorOr { - if (auto* region = space->find_region_containing({ VirtualAddress(symbol.address), sizeof(FlatPtr) })) { - size_t offset = symbol.address - region->vaddr().get(); - if (auto region_name = region->name(); !region_name.is_null() && !region_name.is_empty()) - TRY(builder.try_appendff("{:p} {} + {:#x}\n", (void*)symbol.address, region_name, offset)); - else - TRY(builder.try_appendff("{:p} {:p} + {:#x}\n", (void*)symbol.address, region->vaddr().as_ptr(), offset)); - } else { - TRY(builder.try_appendff("{:p}\n", symbol.address)); - } - return {}; - })); - } - return true; - } - unsigned offset = symbol.address - symbol.symbol->address; - if (symbol.symbol->address == g_highest_kernel_symbol_address && offset > 4096) - TRY(builder.try_appendff("{:p}\n", (void*)(mask_kernel_addresses ? 0xdeadc0de : symbol.address))); - else - TRY(builder.try_appendff("{:p} {} + {:#x}\n", (void*)(mask_kernel_addresses ? 0xdeadc0de : symbol.address), symbol.symbol->name, offset)); - return true; -} - -ErrorOr> Thread::backtrace() -{ - Vector recognized_symbols; - - auto& process = const_cast(this->process()); - auto stack_trace = TRY(Processor::capture_stack_trace(*this)); - VERIFY(!g_scheduler_lock.is_locked_by_current_processor()); - ScopedAddressSpaceSwitcher switcher(process); - for (auto& frame : stack_trace) { - if (Memory::is_user_range(VirtualAddress(frame), sizeof(FlatPtr) * 2)) { - TRY(recognized_symbols.try_append({ frame })); - } else { - TRY(recognized_symbols.try_append({ frame, symbolicate_kernel_address(frame) })); - } - } - - StringBuilder builder; - for (auto& symbol : recognized_symbols) { - if (!TRY(symbolicate(symbol, process, builder))) - break; - } - return KString::try_create(builder.string_view()); -} - -void Thread::print_backtrace() -{ - auto trace_or_error = this->backtrace(); - if (!trace_or_error.is_error()) { - auto trace = trace_or_error.release_value(); - dbgln("Backtrace:"); - kernelputstr(trace->characters(), trace->length()); - } -} - -RefPtr Thread::from_tid_in_same_jail(ThreadID tid) -{ - return Thread::all_instances().with([&](auto& list) -> RefPtr { - for (Thread& thread : list) { - if (thread.tid() == tid) { - return Process::current().jail().with([&thread](auto const& my_jail) -> RefPtr { - return thread.process().jail().with([&thread, my_jail](auto const& other_thread_process_jail) -> RefPtr { - if (my_jail && my_jail.ptr() != other_thread_process_jail.ptr()) - return nullptr; - return thread; - }); - }); - } - } - return nullptr; - }); -} - -RefPtr Thread::from_tid_ignoring_jails(ThreadID tid) -{ - return Thread::all_instances().with([&](auto& list) -> RefPtr { - for (Thread& thread : list) { - if (thread.tid() == tid) - return thread; - } - return nullptr; - }); -} - -void Thread::reset_fpu_state() -{ - memcpy(&m_fpu_state, &Processor::clean_fpu_state(), sizeof(FPUState)); -} - -bool Thread::should_be_stopped() const -{ - return process().is_stopped(); -} - -void Thread::track_lock_acquire(LockRank rank) -{ - // Nothing to do for locks without a rank. - if (rank == LockRank::None) - return; - - if (m_lock_rank_mask != LockRank::None) { - // Verify we are only attempting to take a lock of a higher rank. - VERIFY(m_lock_rank_mask > rank); - } - - m_lock_rank_mask |= rank; -} - -void Thread::track_lock_release(LockRank rank) -{ - // Nothing to do for locks without a rank. - if (rank == LockRank::None) - return; - - // The rank value from the caller should only contain a single bit, otherwise - // we are disabling the tracking for multiple locks at once which will corrupt - // the lock tracking mask, and we will assert somewhere else. - auto rank_is_a_single_bit = [](auto rank_enum) -> bool { - auto rank = to_underlying(rank_enum); - auto rank_without_least_significant_bit = rank - 1; - return (rank & rank_without_least_significant_bit) == 0; - }; - - // We can't release locks out of order, as that would violate the ranking. - // This is validated by toggling the least significant bit of the mask, and - // then bit wise or-ing the rank we are trying to release with the resulting - // mask. If the rank we are releasing is truly the highest rank then the mask - // we get back will be equal to the current mask stored on the thread. - auto rank_is_in_order = [](auto mask_enum, auto rank_enum) -> bool { - auto mask = to_underlying(mask_enum); - auto rank = to_underlying(rank_enum); - auto mask_without_least_significant_bit = mask - 1; - return ((mask & mask_without_least_significant_bit) | rank) == mask; - }; - - VERIFY(has_flag(m_lock_rank_mask, rank)); - VERIFY(rank_is_a_single_bit(rank)); - VERIFY(rank_is_in_order(m_lock_rank_mask, rank)); - - m_lock_rank_mask ^= rank; -} - -void Thread::set_name(StringView name) -{ - m_name.with([name](auto& thread_name) { - thread_name.store_characters(name); - }); -} - -} - -ErrorOr AK::Formatter::format(FormatBuilder& builder, Kernel::Thread const& value) -{ - return value.process().name().with([&](auto& thread_name) { - return AK::Formatter::format( - builder, - "{}({}:{})"sv, thread_name.representable_view(), value.pid().value(), value.tid().value()); - }); -} diff --git a/Kernel/Tasks/Thread.h b/Kernel/Tasks/Thread.h deleted file mode 100644 index addf4bf36dc..00000000000 --- a/Kernel/Tasks/Thread.h +++ /dev/null @@ -1,1351 +0,0 @@ -/* - * Copyright (c) 2018-2021, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace Kernel { - -class Timer; - -enum class DispatchSignalResult { - Deferred = 0, - Yield, - Terminate, - Continue -}; - -struct ThreadSpecificData { - ThreadSpecificData* self; -}; - -#define THREAD_AFFINITY_DEFAULT 0xffffffff - -class Thread - : public ListedRefCounted - , public LockWeakable { - AK_MAKE_NONCOPYABLE(Thread); - AK_MAKE_NONMOVABLE(Thread); - - friend class Mutex; - friend class Process; - friend class Scheduler; - friend struct ThreadReadyQueue; - -public: - static Thread* current() - { - return Processor::current_thread(); - } - - static ErrorOr> create(NonnullRefPtr); - ~Thread(); - - static RefPtr from_tid_ignoring_jails(ThreadID); - static RefPtr from_tid_in_same_jail(ThreadID); - static void finalize_dying_threads(); - - ThreadID tid() const { return m_tid; } - ProcessID pid() const; - - void set_priority(u32 p) { m_priority = p; } - u32 priority() const { return m_priority; } - - void detach() - { - SpinlockLocker lock(m_lock); - m_is_joinable = false; - } - - [[nodiscard]] bool is_joinable() const - { - SpinlockLocker lock(m_lock); - return m_is_joinable; - } - - NO_SANITIZE_COVERAGE Process& process() { return m_process; } - NO_SANITIZE_COVERAGE Process const& process() const { return m_process; } - - using Name = FixedStringBuffer<64>; - SpinlockProtected const& name() const - { - return m_name; - } - - void set_name(StringView); - - void finalize(); - - enum class State : u8 { - Invalid = 0, - Runnable, - Running, - Dying, - Dead, - Stopped, - Blocked, - }; - - class [[nodiscard]] BlockResult { - public: - enum Type { - WokeNormally, - NotBlocked, - InterruptedBySignal, - InterruptedByDeath, - InterruptedByTimeout, - }; - - BlockResult() = delete; - - BlockResult(Type type) - : m_type(type) - { - } - - bool operator==(Type type) const - { - return m_type == type; - } - bool operator!=(Type type) const - { - return m_type != type; - } - - [[nodiscard]] bool was_interrupted() const - { - switch (m_type) { - case InterruptedBySignal: - case InterruptedByDeath: - return true; - default: - return false; - } - } - - private: - Type m_type; - }; - - class BlockTimeout { - public: - BlockTimeout() - : m_infinite(true) - { - } - explicit BlockTimeout(bool is_absolute, Duration const* time, Duration const* start_time = nullptr, clockid_t clock_id = CLOCK_MONOTONIC_COARSE); - - Duration const& absolute_time() const { return m_time; } - Duration const* start_time() const { return !m_infinite ? &m_start_time : nullptr; } - clockid_t clock_id() const { return m_clock_id; } - bool is_infinite() const { return m_infinite; } - - private: - Duration m_time {}; - Duration m_start_time {}; - clockid_t m_clock_id { CLOCK_MONOTONIC_COARSE }; - bool m_infinite { false }; - }; - - class BlockerSet; - - class Blocker { - AK_MAKE_NONMOVABLE(Blocker); - AK_MAKE_NONCOPYABLE(Blocker); - - public: - enum class Type { - Unknown = 0, - File, - Futex, - Plan9FS, - Join, - Queue, - Routing, - Sleep, - Signal, - Wait, - Flock - }; - virtual ~Blocker(); - virtual StringView state_string() const = 0; - virtual Type blocker_type() const = 0; - virtual BlockTimeout const& override_timeout(BlockTimeout const& timeout) { return timeout; } - virtual bool can_be_interrupted() const { return true; } - virtual bool setup_blocker(); - virtual void finalize(); - - Thread& thread() { return m_thread; } - - enum class UnblockImmediatelyReason { - UnblockConditionAlreadyMet, - TimeoutInThePast, - }; - - virtual void will_unblock_immediately_without_blocking(UnblockImmediatelyReason) = 0; - - virtual void was_unblocked(bool did_timeout) - { - if (did_timeout) { - SpinlockLocker lock(m_lock); - m_did_timeout = true; - } - } - void set_interrupted_by_death() - { - SpinlockLocker lock(m_lock); - do_set_interrupted_by_death(); - } - void set_interrupted_by_signal(u8 signal) - { - SpinlockLocker lock(m_lock); - do_set_interrupted_by_signal(signal); - } - u8 was_interrupted_by_signal() const - { - SpinlockLocker lock(m_lock); - return do_get_interrupted_by_signal(); - } - virtual Thread::BlockResult block_result() - { - SpinlockLocker lock(m_lock); - if (m_was_interrupted_by_death) - return Thread::BlockResult::InterruptedByDeath; - if (m_was_interrupted_by_signal != 0) - return Thread::BlockResult::InterruptedBySignal; - if (m_did_timeout) - return Thread::BlockResult::InterruptedByTimeout; - return Thread::BlockResult::WokeNormally; - } - - void begin_blocking(Badge); - BlockResult end_blocking(Badge, bool); - - protected: - Blocker() - : m_thread(*Thread::current()) - { - } - - void do_set_interrupted_by_death() - { - m_was_interrupted_by_death = true; - } - void do_set_interrupted_by_signal(u8 signal) - { - VERIFY(signal != 0); - m_was_interrupted_by_signal = signal; - } - void do_clear_interrupted_by_signal() - { - m_was_interrupted_by_signal = 0; - } - u8 do_get_interrupted_by_signal() const - { - return m_was_interrupted_by_signal; - } - [[nodiscard]] bool was_interrupted() const - { - return m_was_interrupted_by_death || m_was_interrupted_by_signal != 0; - } - void unblock_from_blocker() - { - { - SpinlockLocker lock(m_lock); - if (!m_is_blocking) - return; - m_is_blocking = false; - } - - m_thread->unblock_from_blocker(*this); - } - - bool add_to_blocker_set(BlockerSet&, void* = nullptr); - void set_blocker_set_raw_locked(BlockerSet* blocker_set) { m_blocker_set = blocker_set; } - - // FIXME: Figure out whether this can be Thread. - mutable RecursiveSpinlock m_lock {}; - - private: - BlockerSet* m_blocker_set { nullptr }; - NonnullRefPtr const m_thread; - u8 m_was_interrupted_by_signal { 0 }; - bool m_is_blocking { false }; - bool m_was_interrupted_by_death { false }; - bool m_did_timeout { false }; - }; - - class BlockerSet { - AK_MAKE_NONCOPYABLE(BlockerSet); - AK_MAKE_NONMOVABLE(BlockerSet); - - public: - BlockerSet() = default; - - virtual ~BlockerSet() - { - VERIFY(!m_lock.is_locked()); - VERIFY(m_blockers.is_empty()); - } - - bool add_blocker(Blocker& blocker, void* data) - { - SpinlockLocker lock(m_lock); - if (!should_add_blocker(blocker, data)) - return false; - m_blockers.append({ &blocker, data }); - return true; - } - - void remove_blocker(Blocker& blocker) - { - SpinlockLocker lock(m_lock); - // NOTE: it's possible that the blocker is no longer present - m_blockers.remove_all_matching([&](auto& info) { - return info.blocker == &blocker; - }); - } - - bool is_empty() const - { - SpinlockLocker lock(m_lock); - return is_empty_locked(); - } - - protected: - template - bool unblock_all_blockers_whose_conditions_are_met(Callback try_to_unblock_one) - { - SpinlockLocker lock(m_lock); - return unblock_all_blockers_whose_conditions_are_met_locked(try_to_unblock_one); - } - - template - bool unblock_all_blockers_whose_conditions_are_met_locked(Callback try_to_unblock_one) - { - VERIFY(m_lock.is_locked()); - bool stop_iterating = false; - bool did_unblock_any = false; - for (size_t i = 0; i < m_blockers.size() && !stop_iterating;) { - auto& info = m_blockers[i]; - if (bool did_unblock = try_to_unblock_one(*info.blocker, info.data, stop_iterating)) { - m_blockers.remove(i); - did_unblock_any = true; - continue; - } - - i++; - } - return did_unblock_any; - } - - bool is_empty_locked() const - { - VERIFY(m_lock.is_locked()); - return m_blockers.is_empty(); - } - - virtual bool should_add_blocker(Blocker&, void*) { return true; } - - struct BlockerInfo { - Blocker* blocker; - void* data; - }; - - Vector do_take_blockers(size_t count) - { - if (m_blockers.size() <= count) - return move(m_blockers); - - size_t move_count = (count <= m_blockers.size()) ? count : m_blockers.size(); - VERIFY(move_count > 0); - - Vector taken_blockers; - taken_blockers.ensure_capacity(move_count); - for (size_t i = 0; i < move_count; i++) - taken_blockers.append(m_blockers.take(i)); - m_blockers.remove(0, move_count); - return taken_blockers; - } - - void do_append_blockers(Vector&& blockers_to_append) - { - if (blockers_to_append.is_empty()) - return; - if (m_blockers.is_empty()) { - m_blockers = move(blockers_to_append); - return; - } - m_blockers.ensure_capacity(m_blockers.size() + blockers_to_append.size()); - for (size_t i = 0; i < blockers_to_append.size(); i++) - m_blockers.append(blockers_to_append.take(i)); - blockers_to_append.clear(); - } - - // FIXME: Check whether this can be Thread. - mutable Spinlock m_lock {}; - - private: - Vector m_blockers; - }; - - friend class JoinBlocker; - class JoinBlocker final : public Blocker { - public: - explicit JoinBlocker(Thread& joinee, ErrorOr& try_join_result, void*& joinee_exit_value); - virtual Type blocker_type() const override { return Type::Join; } - virtual StringView state_string() const override { return "Joining"sv; } - virtual bool can_be_interrupted() const override { return false; } - virtual void will_unblock_immediately_without_blocking(UnblockImmediatelyReason) override; - - virtual bool setup_blocker() override; - - bool unblock(void*, bool); - - private: - NonnullRefPtr const m_joinee; - void*& m_joinee_exit_value; - ErrorOr& m_try_join_result; - bool m_did_unblock { false }; - }; - - class WaitQueueBlocker final : public Blocker { - public: - explicit WaitQueueBlocker(WaitQueue&, StringView block_reason = {}); - virtual ~WaitQueueBlocker(); - - virtual Type blocker_type() const override { return Type::Queue; } - virtual StringView state_string() const override { return m_block_reason.is_null() ? m_block_reason : "Queue"sv; } - virtual void will_unblock_immediately_without_blocking(UnblockImmediatelyReason) override { } - virtual bool setup_blocker() override; - - bool unblock(); - - protected: - WaitQueue& m_wait_queue; - StringView m_block_reason; - bool m_did_unblock { false }; - }; - - class FutexBlocker final : public Blocker { - public: - explicit FutexBlocker(FutexQueue&, u32); - virtual ~FutexBlocker(); - - virtual Type blocker_type() const override { return Type::Futex; } - virtual StringView state_string() const override { return "Futex"sv; } - virtual void will_unblock_immediately_without_blocking(UnblockImmediatelyReason) override { } - virtual bool setup_blocker() override; - - u32 bitset() const { return m_bitset; } - - void begin_requeue() - { - // We need to hold the lock until we moved it over - m_previous_interrupts_state = m_lock.lock(); - } - void finish_requeue(FutexQueue&); - - bool unblock_bitset(u32 bitset); - bool unblock(bool force = false); - - protected: - FutexQueue& m_futex_queue; - u32 m_bitset { 0 }; - InterruptsState m_previous_interrupts_state { InterruptsState::Disabled }; - bool m_did_unblock { false }; - }; - - class FileBlocker : public Blocker { - public: - enum class BlockFlags : u16 { - None = 0, - - Read = 1 << 0, - Write = 1 << 1, - ReadPriority = 1 << 2, - WritePriority = 1 << 3, - - Accept = 1 << 4, - Connect = 1 << 5, - SocketFlags = Accept | Connect, - - WriteError = 1 << 6, - WriteHangUp = 1 << 7, - ReadHangUp = 1 << 8, - Exception = WriteError | WriteHangUp | ReadHangUp, - }; - - virtual Type blocker_type() const override { return Type::File; } - - virtual bool unblock_if_conditions_are_met(bool, void*) = 0; - }; - - class OpenFileDescriptionBlocker : public FileBlocker { - public: - OpenFileDescription const& blocked_description() const; - - virtual bool unblock_if_conditions_are_met(bool, void*) override; - virtual void will_unblock_immediately_without_blocking(UnblockImmediatelyReason) override; - virtual bool setup_blocker() override; - - protected: - explicit OpenFileDescriptionBlocker(OpenFileDescription&, BlockFlags, BlockFlags&); - - private: - NonnullRefPtr m_blocked_description; - BlockFlags const m_flags; - BlockFlags& m_unblocked_flags; - bool m_did_unblock { false }; - }; - - class AcceptBlocker final : public OpenFileDescriptionBlocker { - public: - explicit AcceptBlocker(OpenFileDescription&, BlockFlags&); - virtual StringView state_string() const override { return "Accepting"sv; } - }; - - class ConnectBlocker final : public OpenFileDescriptionBlocker { - public: - explicit ConnectBlocker(OpenFileDescription&, BlockFlags&); - virtual StringView state_string() const override { return "Connecting"sv; } - }; - - class WriteBlocker final : public OpenFileDescriptionBlocker { - public: - explicit WriteBlocker(OpenFileDescription&, BlockFlags&); - virtual StringView state_string() const override { return "Writing"sv; } - virtual BlockTimeout const& override_timeout(BlockTimeout const&) override; - - private: - BlockTimeout m_timeout; - }; - - class ReadBlocker final : public OpenFileDescriptionBlocker { - public: - explicit ReadBlocker(OpenFileDescription&, BlockFlags&); - virtual StringView state_string() const override { return "Reading"sv; } - virtual BlockTimeout const& override_timeout(BlockTimeout const&) override; - - private: - BlockTimeout m_timeout; - }; - - class SleepBlocker final : public Blocker { - public: - explicit SleepBlocker(BlockTimeout const&, Duration* = nullptr); - virtual StringView state_string() const override { return "Sleeping"sv; } - virtual Type blocker_type() const override { return Type::Sleep; } - virtual BlockTimeout const& override_timeout(BlockTimeout const&) override; - virtual void will_unblock_immediately_without_blocking(UnblockImmediatelyReason) override; - virtual void was_unblocked(bool) override; - virtual Thread::BlockResult block_result() override; - - private: - void calculate_remaining(); - - BlockTimeout m_deadline; - Duration* m_remaining; - }; - - class SelectBlocker final : public FileBlocker { - public: - struct FDInfo { - RefPtr description; - BlockFlags block_flags { BlockFlags::None }; - BlockFlags unblocked_flags { BlockFlags::None }; - }; - - using FDVector = Vector; - explicit SelectBlocker(FDVector&); - virtual ~SelectBlocker(); - - virtual bool unblock_if_conditions_are_met(bool, void*) override; - virtual void will_unblock_immediately_without_blocking(UnblockImmediatelyReason) override; - virtual void was_unblocked(bool) override; - virtual StringView state_string() const override { return "Selecting"sv; } - virtual bool setup_blocker() override; - virtual void finalize() override; - - private: - size_t collect_unblocked_flags(); - - FDVector& m_fds; - bool m_did_unblock { false }; - }; - - class SignalBlocker final : public Blocker { - public: - explicit SignalBlocker(sigset_t pending_set, siginfo_t& result); - virtual StringView state_string() const override { return "Pending Signal"sv; } - virtual Type blocker_type() const override { return Type::Signal; } - void will_unblock_immediately_without_blocking(UnblockImmediatelyReason) override; - virtual bool setup_blocker() override; - bool check_pending_signals(bool from_add_blocker); - - private: - sigset_t m_pending_set { 0 }; - siginfo_t& m_result; - bool m_did_unblock { false }; - }; - - class SignalBlockerSet final : public BlockerSet { - public: - void unblock_all_blockers_whose_conditions_are_met() - { - BlockerSet::unblock_all_blockers_whose_conditions_are_met([&](auto& b, void*, bool&) { - VERIFY(b.blocker_type() == Blocker::Type::Signal); - auto& blocker = static_cast(b); - return blocker.check_pending_signals(false); - }); - } - - private: - bool should_add_blocker(Blocker& b, void*) override - { - VERIFY(b.blocker_type() == Blocker::Type::Signal); - auto& blocker = static_cast(b); - return !blocker.check_pending_signals(true); - } - }; - - class WaitBlocker final : public Blocker { - public: - enum class UnblockFlags { - Terminated, - Stopped, - Continued, - Disowned - }; - - WaitBlocker(int wait_options, Variant, NonnullRefPtr> waitee, ErrorOr& result); - virtual StringView state_string() const override { return "Waiting"sv; } - virtual Type blocker_type() const override { return Type::Wait; } - virtual void will_unblock_immediately_without_blocking(UnblockImmediatelyReason) override; - virtual void was_unblocked(bool) override; - virtual bool setup_blocker() override; - - bool unblock(Process& process, UnblockFlags flags, u8 signal, bool from_add_blocker); - bool is_wait() const { return (m_wait_options & WNOWAIT) != WNOWAIT; } - - private: - void do_was_disowned(); - void do_set_result(siginfo_t const&); - - int const m_wait_options; - ErrorOr& m_result; - Variant, NonnullRefPtr> const m_waitee; - bool m_did_unblock { false }; - bool m_got_sigchild { false }; - }; - - class WaitBlockerSet final : public BlockerSet { - friend class WaitBlocker; - - public: - explicit WaitBlockerSet(Process& process) - : m_process(process) - { - } - - void disowned_by_waiter(Process&); - bool unblock(Process&, WaitBlocker::UnblockFlags, u8); - void try_unblock(WaitBlocker&); - void finalize(); - - protected: - virtual bool should_add_blocker(Blocker&, void*) override; - - private: - struct ProcessBlockInfo { - NonnullRefPtr const process; - WaitBlocker::UnblockFlags flags; - u8 signal; - bool was_waited { false }; - - explicit ProcessBlockInfo(NonnullRefPtr&&, WaitBlocker::UnblockFlags, u8); - ~ProcessBlockInfo(); - }; - - Process& m_process; - Vector m_processes; - bool m_finalized { false }; - }; - - class FlockBlocker final : public Blocker { - public: - FlockBlocker(NonnullRefPtr, flock const&); - virtual StringView state_string() const override { return "Locking File"sv; } - virtual Type blocker_type() const override { return Type::Flock; } - virtual void will_unblock_immediately_without_blocking(UnblockImmediatelyReason) override; - virtual bool setup_blocker() override; - bool try_unblock(bool from_add_blocker); - - private: - NonnullRefPtr m_inode; - flock const& m_flock; - bool m_did_unblock { false }; - }; - - class FlockBlockerSet final : public BlockerSet { - public: - void unblock_all_blockers_whose_conditions_are_met() - { - BlockerSet::unblock_all_blockers_whose_conditions_are_met([&](auto& b, void*, bool&) { - VERIFY(b.blocker_type() == Blocker::Type::Flock); - auto& blocker = static_cast(b); - return blocker.try_unblock(false); - }); - } - - private: - bool should_add_blocker(Blocker& b, void*) override - { - VERIFY(b.blocker_type() == Blocker::Type::Flock); - auto& blocker = static_cast(b); - return !blocker.try_unblock(true); - } - }; - - template - ErrorOr try_join(AddBlockerHandler add_blocker) - { - if (Thread::current() == this) - return EDEADLK; - - SpinlockLocker lock(m_lock); - - // Joining dead threads is allowed for two main reasons: - // - Thread join behavior should not be racy when a thread is joined and exiting at roughly the same time. - // This is common behavior when threads are given a signal to end (meaning they are going to exit ASAP) and then joined. - // - POSIX requires that exited threads are joinable (at least, there is no language in the specification forbidding it). - if (!m_is_joinable || state() == Thread::State::Invalid) - return EINVAL; - - add_blocker(); - - // From this point on the thread is no longer joinable by anyone - // else. It also means that if the join is timed, it becomes - // detached when a timeout happens. - m_is_joinable = false; - return {}; - } - - void did_schedule() { ++m_times_scheduled; } - u32 times_scheduled() const { return m_times_scheduled; } - - void resume_from_stopped(); - - [[nodiscard]] bool should_be_stopped() const; - [[nodiscard]] bool is_stopped() const { return m_state == Thread::State::Stopped; } - [[nodiscard]] bool is_blocked() const { return m_state == Thread::State::Blocked; } - - u32 cpu() const { return m_cpu.load(AK::MemoryOrder::memory_order_consume); } - void set_cpu(u32 cpu) { m_cpu.store(cpu, AK::MemoryOrder::memory_order_release); } - u32 affinity() const { return m_cpu_affinity; } - void set_affinity(u32 affinity) { m_cpu_affinity = affinity; } - - RegisterState& get_register_dump_from_stack(); - RegisterState const& get_register_dump_from_stack() const { return const_cast(this)->get_register_dump_from_stack(); } - - DebugRegisterState& debug_register_state() { return m_debug_register_state; } - DebugRegisterState const& debug_register_state() const { return m_debug_register_state; } - - ThreadRegisters& regs() { return m_regs; } - ThreadRegisters const& regs() const { return m_regs; } - - State state() const { return m_state; } - StringView state_string() const; - - ArchSpecificThreadData& arch_specific_data() { return m_arch_specific_data; } - ArchSpecificThreadData const& arch_specific_data() const { return m_arch_specific_data; } - - ALWAYS_INLINE void yield_if_should_be_stopped() - { - // A thread may continue to execute in user land until the next timer - // tick or until entering the next system call/exiting the current one. - if (!is_stopped() && should_be_stopped()) { - SpinlockLocker scheduler_lock(g_scheduler_lock); - set_state(State::Stopped); - } - // If we're stopped, we need to yield to someone else. - SpinlockLocker lock(m_lock); - while (is_stopped()) { - lock.unlock(); - // We shouldn't be holding the big lock here - yield_without_releasing_big_lock(); - lock.lock(); - } - } - - void block(Kernel::Mutex&, SpinlockLocker>&, u32); - - template - BlockResult block(BlockTimeout const& timeout, Args&&... args) - { - BlockerType blocker(forward(args)...); - return block_impl(timeout, blocker); - } - - u32 unblock_from_mutex(Kernel::Mutex&); - void unblock_from_blocker(Blocker&); - void unblock(u8 signal = 0); - - template - Thread::BlockResult wait_on(WaitQueue& wait_queue, Thread::BlockTimeout const& timeout, Args&&... args) - { - VERIFY(this == Thread::current()); - return block(timeout, wait_queue, forward(args)...); - } - - BlockResult sleep(clockid_t, Duration const&, Duration* = nullptr); - BlockResult sleep(Duration const& duration, Duration* remaining_time = nullptr) - { - return sleep(CLOCK_MONOTONIC_COARSE, duration, remaining_time); - } - BlockResult sleep_until(clockid_t, Duration const&); - BlockResult sleep_until(Duration const& duration) - { - return sleep_until(CLOCK_MONOTONIC_COARSE, duration); - } - - // Tell this thread to unblock if needed, - // gracefully unwind the stack and die. - void set_should_die(); - [[nodiscard]] bool should_die() const { return m_should_die; } - void die_if_needed(); - - void exit(void* = nullptr); - - void update_time_scheduled(u64, bool, bool); - bool tick(); - void set_ticks_left(u32 t) { m_ticks_left = t; } - u32 ticks_left() const { return m_ticks_left; } - - FlatPtr kernel_stack_base() const { return m_kernel_stack_base; } - FlatPtr kernel_stack_top() const { return m_kernel_stack_top; } - - void set_state(State, u8 = 0); - - [[nodiscard]] bool is_initialized() const { return m_initialized; } - void set_initialized(bool initialized) { m_initialized = initialized; } - - void send_urgent_signal_to_self(u8 signal); - void send_signal(u8 signal, Process* sender); - - u32 update_signal_mask(u32 signal_mask); - u32 signal_mask_block(sigset_t signal_set, bool block); - u32 signal_mask() const; - void reset_signals_for_exec(); - - ErrorOr peek_debug_register(u32 register_index); - ErrorOr poke_debug_register(u32 register_index, FlatPtr data); - - void set_dump_backtrace_on_finalization() { m_dump_backtrace_on_finalization = true; } - - DispatchSignalResult dispatch_one_pending_signal(); - DispatchSignalResult try_dispatch_one_pending_signal(u8 signal); - DispatchSignalResult dispatch_signal(u8 signal); - void check_dispatch_pending_signal(); - [[nodiscard]] bool has_unmasked_pending_signals() const { return m_have_any_unmasked_pending_signals.load(AK::memory_order_consume); } - [[nodiscard]] bool should_ignore_signal(u8 signal) const; - [[nodiscard]] bool has_signal_handler(u8 signal) const; - [[nodiscard]] bool is_signal_masked(u8 signal) const; - u32 pending_signals() const; - u32 pending_signals_for_state() const; - - [[nodiscard]] bool is_in_alternative_signal_stack() const; - - FPUState& fpu_state() { return m_fpu_state; } - - unsigned syscall_count() const { return m_syscall_count; } - void did_syscall() { ++m_syscall_count; } - unsigned inode_faults() const { return m_inode_faults; } - void did_inode_fault() { ++m_inode_faults; } - unsigned zero_faults() const { return m_zero_faults; } - void did_zero_fault() { ++m_zero_faults; } - unsigned cow_faults() const { return m_cow_faults; } - void did_cow_fault() { ++m_cow_faults; } - - u64 file_read_bytes() const { return m_file_read_bytes; } - u64 file_write_bytes() const { return m_file_write_bytes; } - - void did_file_read(u64 bytes) - { - m_file_read_bytes += bytes; - } - - void did_file_write(u64 bytes) - { - m_file_write_bytes += bytes; - } - - u64 unix_socket_read_bytes() const { return m_unix_socket_read_bytes; } - u64 unix_socket_write_bytes() const { return m_unix_socket_write_bytes; } - - void did_unix_socket_read(u64 bytes) - { - m_unix_socket_read_bytes += bytes; - } - - void did_unix_socket_write(u64 bytes) - { - m_unix_socket_write_bytes += bytes; - } - - u64 ipv4_socket_read_bytes() const { return m_ipv4_socket_read_bytes; } - u64 ipv4_socket_write_bytes() const { return m_ipv4_socket_write_bytes; } - - void did_ipv4_socket_read(u64 bytes) - { - m_ipv4_socket_read_bytes += bytes; - } - - void did_ipv4_socket_write(u64 bytes) - { - m_ipv4_socket_write_bytes += bytes; - } - - void set_active(bool active) { m_is_active = active; } - - u32 saved_critical() const { return m_saved_critical; } - void save_critical(u32 critical) { m_saved_critical = critical; } - - void track_lock_acquire(LockRank rank); - void track_lock_release(LockRank rank); - - [[nodiscard]] bool is_active() const { return m_is_active; } - - [[nodiscard]] bool is_finalizable() const - { - // We can't finalize as long as this thread is still running - // Note that checking for Running state here isn't sufficient - // as the thread may not be in Running state but switching out. - // m_is_active is set to false once the context switch is - // complete and the thread is not executing on any processor. - if (m_is_active.load(AK::memory_order_acquire)) - return false; - // We can't finalize until the thread is either detached or - // a join has started. We can't make m_is_joinable atomic - // because that would introduce a race in try_join. - SpinlockLocker lock(m_lock); - return !m_is_joinable; - } - - ErrorOr> clone(NonnullRefPtr); - - template Callback> - static IterationDecision for_each_in_state_ignoring_jails(State, Callback); - template Callback> - static IterationDecision for_each_ignoring_jails(Callback); - - template Callback> - static IterationDecision for_each_in_state_ignoring_jails(State, Callback); - template Callback> - static IterationDecision for_each_ignoring_jails(Callback); - - static constexpr u32 default_kernel_stack_size = 65536; - static constexpr u32 default_userspace_stack_size = 1 * MiB; - - u64 time_in_user() const { return m_total_time_scheduled_user.load(AK::MemoryOrder::memory_order_relaxed); } - u64 time_in_kernel() const { return m_total_time_scheduled_kernel.load(AK::MemoryOrder::memory_order_relaxed); } - - ExecutionMode previous_mode() const { return m_previous_mode; } - bool set_previous_mode(ExecutionMode mode) - { - if (m_previous_mode == mode) - return false; - m_previous_mode = mode; - return true; - } - - TrapFrame*& current_trap() { return m_current_trap; } - TrapFrame const* const& current_trap() const { return m_current_trap; } - - RecursiveSpinlock& get_lock() const { return m_lock; } - -#if LOCK_DEBUG - void holding_lock(Mutex& lock, int refs_delta, LockLocation const& location) - { - VERIFY(refs_delta != 0); - m_holding_locks.fetch_add(refs_delta, AK::MemoryOrder::memory_order_relaxed); - SpinlockLocker list_lock(m_holding_locks_lock); - if (refs_delta > 0) { - bool have_existing = false; - for (size_t i = 0; i < m_holding_locks_list.size(); i++) { - auto& info = m_holding_locks_list[i]; - if (info.lock == &lock) { - have_existing = true; - info.count += refs_delta; - break; - } - } - if (!have_existing) - m_holding_locks_list.append({ &lock, location, 1 }); - } else { - VERIFY(refs_delta < 0); - bool found = false; - for (size_t i = 0; i < m_holding_locks_list.size(); i++) { - auto& info = m_holding_locks_list[i]; - if (info.lock == &lock) { - VERIFY(info.count >= (unsigned)-refs_delta); - info.count -= (unsigned)-refs_delta; - if (info.count == 0) - m_holding_locks_list.remove(i); - found = true; - break; - } - } - VERIFY(found); - } - } - u32 lock_count() const - { - return m_holding_locks.load(AK::MemoryOrder::memory_order_relaxed); - } -#endif - - bool is_handling_page_fault() const - { - return m_handling_page_fault; - } - void set_handling_page_fault(bool b) { m_handling_page_fault = b; } - void set_idle_thread() { m_is_idle_thread = true; } - bool is_idle_thread() const { return m_is_idle_thread; } - - void set_crashing() { m_is_crashing = true; } - [[nodiscard]] bool is_crashing() const { return m_is_crashing; } - - ALWAYS_INLINE u32 enter_profiler() - { - return m_nested_profiler_calls.fetch_add(1, AK::MemoryOrder::memory_order_acq_rel); - } - - ALWAYS_INLINE u32 leave_profiler() - { - return m_nested_profiler_calls.fetch_sub(1, AK::MemoryOrder::memory_order_acquire); - } - - bool is_profiling_suppressed() const { return m_is_profiling_suppressed; } - void set_profiling_suppressed() { m_is_profiling_suppressed = true; } - - bool is_promise_violation_pending() const { return m_is_promise_violation_pending; } - void set_promise_violation_pending(bool value) { m_is_promise_violation_pending = value; } - - bool is_allocation_enabled() const { return m_allocation_enabled; } - void set_allocation_enabled(bool value) { m_allocation_enabled = value; } - - ErrorOr> backtrace(); - void print_backtrace(); - - Blocker const* blocker() const { return m_blocker; } - Kernel::Mutex const* blocking_mutex() const { return m_blocking_mutex; } - -#if LOCK_DEBUG - struct HoldingLockInfo { - Mutex* lock; - LockLocation lock_location; - unsigned count; - }; - - template Callback> - void for_each_held_lock(Callback); - template Callback> - void for_each_held_lock(Callback); -#endif - -private: - Thread(NonnullRefPtr, NonnullOwnPtr, NonnullRefPtr); - - BlockResult block_impl(BlockTimeout const&, Blocker&); - - IntrusiveListNode m_process_thread_list_node; - int m_runnable_priority { -1 }; - - friend class WaitQueue; - - class JoinBlockerSet final : public BlockerSet { - public: - void thread_did_exit(void* exit_value) - { - SpinlockLocker lock(m_lock); - VERIFY(!m_thread_did_exit); - m_thread_did_exit = true; - m_exit_value.store(exit_value, AK::MemoryOrder::memory_order_release); - do_unblock_joiner(); - } - void thread_finalizing() - { - SpinlockLocker lock(m_lock); - do_unblock_joiner(); - } - void* exit_value() const - { - VERIFY(m_thread_did_exit); - return m_exit_value.load(AK::MemoryOrder::memory_order_acquire); - } - - void try_unblock(JoinBlocker& blocker) - { - SpinlockLocker lock(m_lock); - if (m_thread_did_exit) - blocker.unblock(exit_value(), false); - } - - protected: - virtual bool should_add_blocker(Blocker& b, void*) override - { - VERIFY(b.blocker_type() == Blocker::Type::Join); - auto& blocker = static_cast(b); - - // NOTE: m_lock is held already! - if (m_thread_did_exit) { - blocker.unblock(exit_value(), true); - return false; - } - return true; - } - - private: - void do_unblock_joiner() - { - unblock_all_blockers_whose_conditions_are_met_locked([&](Blocker& b, void*, bool&) { - VERIFY(b.blocker_type() == Blocker::Type::Join); - auto& blocker = static_cast(b); - return blocker.unblock(exit_value(), false); - }); - } - - Atomic m_exit_value { nullptr }; - bool m_thread_did_exit { false }; - }; - - LockMode unlock_process_if_locked(u32&); - void relock_process(LockMode, u32); - void reset_fpu_state(); - - mutable RecursiveSpinlock m_lock {}; - mutable RecursiveSpinlock m_block_lock {}; - NonnullRefPtr const m_process; - ThreadID m_tid { -1 }; - ThreadRegisters m_regs {}; - DebugRegisterState m_debug_register_state {}; - TrapFrame* m_current_trap { nullptr }; - u32 m_saved_critical { 1 }; - IntrusiveListNode m_ready_queue_node; - Atomic m_cpu { 0 }; - u32 m_cpu_affinity { THREAD_AFFINITY_DEFAULT }; - Optional m_last_time_scheduled; - Atomic m_total_time_scheduled_user { 0 }; - Atomic m_total_time_scheduled_kernel { 0 }; - u32 m_ticks_left { 0 }; - u32 m_times_scheduled { 0 }; - u32 m_ticks_in_user { 0 }; - u32 m_ticks_in_kernel { 0 }; - u32 m_pending_signals { 0 }; - u8 m_currently_handled_signal { 0 }; - u32 m_signal_mask { 0 }; - Optional m_alternative_signal_stack; - SignalBlockerSet m_signal_blocker_set; - FlatPtr m_kernel_stack_base { 0 }; - FlatPtr m_kernel_stack_top { 0 }; - NonnullOwnPtr m_kernel_stack_region; - Array, NSIG> m_signal_action_masks; - Array m_signal_senders; - Blocker* m_blocker { nullptr }; - Kernel::Mutex* m_blocking_mutex { nullptr }; - u32 m_lock_requested_count { 0 }; - IntrusiveListNode m_blocked_threads_list_node; - LockRank m_lock_rank_mask {}; - bool m_allocation_enabled { true }; - ArchSpecificThreadData m_arch_specific_data; - - // FIXME: remove this after annihilating Process::m_big_lock - IntrusiveListNode m_big_lock_blocked_threads_list_node; - -#if LOCK_DEBUG - Atomic m_holding_locks { 0 }; - Spinlock m_holding_locks_lock {}; - Vector m_holding_locks_list; -#endif - - JoinBlockerSet m_join_blocker_set; - Atomic m_is_active { false }; - bool m_is_joinable { true }; - bool m_handling_page_fault { false }; - ExecutionMode m_previous_mode { ExecutionMode::Kernel }; // We always start out in kernel mode - - unsigned m_syscall_count { 0 }; - unsigned m_inode_faults { 0 }; - unsigned m_zero_faults { 0 }; - unsigned m_cow_faults { 0 }; - - u64 m_file_read_bytes { 0 }; - u64 m_file_write_bytes { 0 }; - - u64 m_unix_socket_read_bytes { 0 }; - u64 m_unix_socket_write_bytes { 0 }; - - u64 m_ipv4_socket_read_bytes { 0 }; - u64 m_ipv4_socket_write_bytes { 0 }; - - FPUState m_fpu_state {}; - State m_state { Thread::State::Invalid }; - SpinlockProtected m_name; - u32 m_priority { THREAD_PRIORITY_NORMAL }; - - State m_stop_state { Thread::State::Invalid }; - - bool m_dump_backtrace_on_finalization { false }; - bool m_should_die { false }; - bool m_initialized { false }; - bool m_is_idle_thread { false }; - bool m_is_crashing { false }; - bool m_is_promise_violation_pending { false }; - Atomic m_have_any_unmasked_pending_signals { false }; - Atomic m_nested_profiler_calls { 0 }; - - NonnullRefPtr const m_block_timer; - - bool m_is_profiling_suppressed { false }; - - void yield_and_release_relock_big_lock(); - - enum class VerifyLockNotHeld { - Yes, - No - }; - - void yield_without_releasing_big_lock(VerifyLockNotHeld verify_lock_not_held = VerifyLockNotHeld::Yes); - void drop_thread_count(); - - mutable IntrusiveListNode m_global_thread_list_node; - -public: - using ListInProcess = IntrusiveList<&Thread::m_process_thread_list_node>; - using GlobalList = IntrusiveList<&Thread::m_global_thread_list_node>; - - static SpinlockProtected& all_instances(); - -#ifdef ENABLE_KERNEL_COVERAGE_COLLECTION - // Used by __sanitizer_cov_trace_pc to identify traced threads. - bool m_kcov_enabled { false }; -# ifdef ENABLE_KERNEL_COVERAGE_COLLECTION_DEBUG - // Used by __sanitizer_cov_trace_pc to detect an infinite recursion. - bool m_kcov_recursion_hint { false }; -# endif -#endif -}; - -AK_ENUM_BITWISE_OPERATORS(Thread::FileBlocker::BlockFlags); - -template Callback> -inline IterationDecision Thread::for_each_ignoring_jails(Callback callback) -{ - return Thread::all_instances().with([&](auto& list) -> IterationDecision { - for (auto& thread : list) { - IterationDecision decision = callback(thread); - if (decision != IterationDecision::Continue) - return decision; - } - return IterationDecision::Continue; - }); -} - -template Callback> -inline IterationDecision Thread::for_each_in_state_ignoring_jails(State state, Callback callback) -{ - return Thread::all_instances().with([&](auto& list) -> IterationDecision { - for (auto& thread : list) { - if (thread.state() != state) - continue; - IterationDecision decision = callback(thread); - if (decision != IterationDecision::Continue) - return decision; - } - return IterationDecision::Continue; - }); -} - -template Callback> -inline IterationDecision Thread::for_each_ignoring_jails(Callback callback) -{ - return Thread::all_instances().with([&](auto& list) { - for (auto& thread : list) { - if (callback(thread) == IterationDecision::Break) - return IterationDecision::Break; - } - return IterationDecision::Continue; - }); -} - -template Callback> -inline IterationDecision Thread::for_each_in_state_ignoring_jails(State state, Callback callback) -{ - return for_each_in_state_ignoring_jails(state, [&](auto& thread) { - callback(thread); - return IterationDecision::Continue; - }); -} - -#if LOCK_DEBUG -template Callback> -inline void Thread::for_each_held_lock(Callback callback) -{ - SpinlockLocker list_lock(m_holding_locks_lock); - - for (auto const& lock_info : m_holding_locks_list) { - if (callback(lock_info) == IterationDecision::Break) - break; - } -} - -template Callback> -inline void Thread::for_each_held_lock(Callback callback) -{ - for_each_held_lock([&](auto const& lock_info) { - callback(lock_info); - return IterationDecision::Continue; - }); -} -#endif - -} - -template<> -struct AK::Formatter : AK::Formatter { - ErrorOr format(FormatBuilder&, Kernel::Thread const&); -}; diff --git a/Kernel/Tasks/ThreadBlockers.cpp b/Kernel/Tasks/ThreadBlockers.cpp deleted file mode 100644 index e9472618009..00000000000 --- a/Kernel/Tasks/ThreadBlockers.cpp +++ /dev/null @@ -1,858 +0,0 @@ -/* - * Copyright (c) 2020, the SerenityOS developers. - * Copyright (c) 2022, Idan Horowitz - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include -#include -#include - -namespace Kernel { - -Thread::BlockTimeout::BlockTimeout(bool is_absolute, Duration const* time, Duration const* start_time, clockid_t clock_id) - : m_clock_id(clock_id) - , m_infinite(!time) -{ - if (m_infinite) - return; - if (*time > Duration::zero()) - m_time = *time; - m_start_time = start_time ? *start_time : TimeManagement::the().current_time(clock_id); - if (!is_absolute) - m_time += m_start_time; -} - -bool Thread::Blocker::add_to_blocker_set(Thread::BlockerSet& blocker_set, void* data) -{ - VERIFY(!m_blocker_set); - if (blocker_set.add_blocker(*this, data)) { - m_blocker_set = &blocker_set; - return true; - } - return false; -} - -Thread::Blocker::~Blocker() = default; - -void Thread::Blocker::finalize() -{ - if (m_blocker_set) - m_blocker_set->remove_blocker(*this); -} - -bool Thread::Blocker::setup_blocker() -{ - return true; -} - -void Thread::Blocker::begin_blocking(Badge) -{ - SpinlockLocker lock(m_lock); - VERIFY(!m_is_blocking); - m_is_blocking = true; -} - -auto Thread::Blocker::end_blocking(Badge, bool did_timeout) -> BlockResult -{ - SpinlockLocker lock(m_lock); - // if m_is_blocking is false here, some thread forced to - // unblock us when we get here. This is only called from the - // thread that was blocked. - VERIFY(Thread::current() == m_thread); - m_is_blocking = false; - - was_unblocked(did_timeout); - return block_result(); -} - -Thread::JoinBlocker::JoinBlocker(Thread& joinee, ErrorOr& try_join_result, void*& joinee_exit_value) - : m_joinee(joinee) - , m_joinee_exit_value(joinee_exit_value) - , m_try_join_result(try_join_result) -{ -} - -bool Thread::JoinBlocker::setup_blocker() -{ - // We need to hold our lock to avoid a race where try_join succeeds - // but the joinee is joining immediately - SpinlockLocker lock(m_lock); - bool should_block = true; - m_try_join_result = m_joinee->try_join([&]() { - if (!add_to_blocker_set(m_joinee->m_join_blocker_set)) - should_block = false; - }); - if (m_try_join_result.is_error()) - return false; - return should_block; -} - -void Thread::JoinBlocker::will_unblock_immediately_without_blocking(UnblockImmediatelyReason reason) -{ - // If we should have blocked but got here it must have been that the - // timeout was already in the past. So we need to ask the BlockerSet - // to supply us the information. We cannot hold the lock as unblock - // could be called by the BlockerSet at any time! - if (reason == UnblockImmediatelyReason::TimeoutInThePast) { - m_joinee->m_join_blocker_set.try_unblock(*this); - } -} - -bool Thread::JoinBlocker::unblock(void* value, bool from_add_blocker) -{ - { - SpinlockLocker lock(m_lock); - if (m_did_unblock) - return false; - m_did_unblock = true; - m_joinee_exit_value = value; - do_set_interrupted_by_death(); - } - - if (!from_add_blocker) - unblock_from_blocker(); - return true; -} - -Thread::WaitQueueBlocker::WaitQueueBlocker(WaitQueue& wait_queue, StringView block_reason) - : m_wait_queue(wait_queue) - , m_block_reason(block_reason) -{ -} - -bool Thread::WaitQueueBlocker::setup_blocker() -{ - return add_to_blocker_set(m_wait_queue); -} - -Thread::WaitQueueBlocker::~WaitQueueBlocker() = default; - -bool Thread::WaitQueueBlocker::unblock() -{ - { - SpinlockLocker lock(m_lock); - if (m_did_unblock) - return false; - m_did_unblock = true; - } - - unblock_from_blocker(); - return true; -} - -Thread::FutexBlocker::FutexBlocker(FutexQueue& futex_queue, u32 bitset) - : m_futex_queue(futex_queue) - , m_bitset(bitset) -{ -} - -bool Thread::FutexBlocker::setup_blocker() -{ - return add_to_blocker_set(m_futex_queue); -} - -Thread::FutexBlocker::~FutexBlocker() = default; - -void Thread::FutexBlocker::finish_requeue(FutexQueue& futex_queue) -{ - VERIFY(m_lock.is_locked_by_current_processor()); - set_blocker_set_raw_locked(&futex_queue); - // We can now release the lock - m_lock.unlock(m_previous_interrupts_state); -} - -bool Thread::FutexBlocker::unblock_bitset(u32 bitset) -{ - { - SpinlockLocker lock(m_lock); - if (m_did_unblock || (bitset != FUTEX_BITSET_MATCH_ANY && (m_bitset & bitset) == 0)) - return false; - - m_did_unblock = true; - } - - unblock_from_blocker(); - return true; -} - -bool Thread::FutexBlocker::unblock(bool force) -{ - { - SpinlockLocker lock(m_lock); - if (m_did_unblock) - return force; - m_did_unblock = true; - } - - unblock_from_blocker(); - return true; -} - -Thread::OpenFileDescriptionBlocker::OpenFileDescriptionBlocker(OpenFileDescription& description, BlockFlags flags, BlockFlags& unblocked_flags) - : m_blocked_description(description) - , m_flags(flags) - , m_unblocked_flags(unblocked_flags) -{ -} - -bool Thread::OpenFileDescriptionBlocker::setup_blocker() -{ - m_unblocked_flags = BlockFlags::None; - return add_to_blocker_set(m_blocked_description->blocker_set()); -} - -bool Thread::OpenFileDescriptionBlocker::unblock_if_conditions_are_met(bool from_add_blocker, void*) -{ - auto unblock_flags = m_blocked_description->should_unblock(m_flags); - if (unblock_flags == BlockFlags::None) - return false; - - { - SpinlockLocker lock(m_lock); - if (m_did_unblock) - return false; - m_did_unblock = true; - m_unblocked_flags = unblock_flags; - } - - if (!from_add_blocker) - unblock_from_blocker(); - return true; -} - -void Thread::OpenFileDescriptionBlocker::will_unblock_immediately_without_blocking(UnblockImmediatelyReason reason) -{ - if (reason == UnblockImmediatelyReason::UnblockConditionAlreadyMet) - return; - - // If we should have blocked but got here it must have been that the - // timeout was already in the past. So we need to ask the BlockerSet - // to supply us the information. We cannot hold the lock as unblock - // could be called by the BlockerSet at any time! - VERIFY(reason == UnblockImmediatelyReason::TimeoutInThePast); - - // Just call unblock_if_conditions_are_met here because we will query the file description - // for the data and don't need any input from the FileBlockerSet. - // However, it's possible that if timeout_in_past is true then FileBlockerSet - // may call us at any given time, so our call to unblock here may fail. - // Either way, unblock will be called at least once, which provides - // all the data we need. - unblock_if_conditions_are_met(false, nullptr); -} - -OpenFileDescription const& Thread::OpenFileDescriptionBlocker::blocked_description() const -{ - return m_blocked_description; -} - -Thread::AcceptBlocker::AcceptBlocker(OpenFileDescription& description, BlockFlags& unblocked_flags) - : OpenFileDescriptionBlocker(description, BlockFlags::Accept | BlockFlags::Exception, unblocked_flags) -{ -} - -Thread::ConnectBlocker::ConnectBlocker(OpenFileDescription& description, BlockFlags& unblocked_flags) - : OpenFileDescriptionBlocker(description, BlockFlags::Connect | BlockFlags::Exception, unblocked_flags) -{ -} - -Thread::WriteBlocker::WriteBlocker(OpenFileDescription& description, BlockFlags& unblocked_flags) - : OpenFileDescriptionBlocker(description, BlockFlags::Write | BlockFlags::Exception, unblocked_flags) -{ -} - -auto Thread::WriteBlocker::override_timeout(BlockTimeout const& timeout) -> BlockTimeout const& -{ - auto const& description = blocked_description(); - if (description.is_socket()) { - auto const& socket = *description.socket(); - if (socket.has_send_timeout()) { - Duration send_timeout = socket.send_timeout(); - m_timeout = BlockTimeout(false, &send_timeout, timeout.start_time(), timeout.clock_id()); - if (timeout.is_infinite() || (!m_timeout.is_infinite() && m_timeout.absolute_time() < timeout.absolute_time())) - return m_timeout; - } - } - return timeout; -} - -Thread::ReadBlocker::ReadBlocker(OpenFileDescription& description, BlockFlags& unblocked_flags) - : OpenFileDescriptionBlocker(description, BlockFlags::Read | BlockFlags::Exception, unblocked_flags) -{ -} - -auto Thread::ReadBlocker::override_timeout(BlockTimeout const& timeout) -> BlockTimeout const& -{ - auto const& description = blocked_description(); - if (description.is_socket()) { - auto const& socket = *description.socket(); - if (socket.has_receive_timeout()) { - Duration receive_timeout = socket.receive_timeout(); - m_timeout = BlockTimeout(false, &receive_timeout, timeout.start_time(), timeout.clock_id()); - if (timeout.is_infinite() || (!m_timeout.is_infinite() && m_timeout.absolute_time() < timeout.absolute_time())) - return m_timeout; - } - } - return timeout; -} - -Thread::SleepBlocker::SleepBlocker(BlockTimeout const& deadline, Duration* remaining) - : m_deadline(deadline) - , m_remaining(remaining) -{ -} - -auto Thread::SleepBlocker::override_timeout(BlockTimeout const& timeout) -> BlockTimeout const& -{ - VERIFY(timeout.is_infinite()); // A timeout should not be provided - // To simplify things only use the sleep deadline. - return m_deadline; -} - -void Thread::SleepBlocker::will_unblock_immediately_without_blocking(UnblockImmediatelyReason reason) -{ - // SleepBlocker::should_block should always return true, so timeout - // in the past is the only valid case when this function is called - VERIFY(reason == UnblockImmediatelyReason::TimeoutInThePast); - calculate_remaining(); -} - -void Thread::SleepBlocker::was_unblocked(bool did_timeout) -{ - Blocker::was_unblocked(did_timeout); - - calculate_remaining(); -} - -void Thread::SleepBlocker::calculate_remaining() -{ - if (!m_remaining) - return; - auto time_now = TimeManagement::the().current_time(m_deadline.clock_id()); - if (time_now < m_deadline.absolute_time()) - *m_remaining = m_deadline.absolute_time() - time_now; - else - *m_remaining = {}; -} - -Thread::BlockResult Thread::SleepBlocker::block_result() -{ - auto result = Blocker::block_result(); - if (result == Thread::BlockResult::InterruptedByTimeout) - return Thread::BlockResult::WokeNormally; - return result; -} - -Thread::SelectBlocker::SelectBlocker(FDVector& fds) - : m_fds(fds) -{ -} - -bool Thread::SelectBlocker::setup_blocker() -{ - bool should_block = true; - for (auto& fd_entry : m_fds) { - fd_entry.unblocked_flags = FileBlocker::BlockFlags::None; - - if (!should_block) - continue; - if (!fd_entry.description) { - should_block = false; - continue; - } - if (!fd_entry.description->blocker_set().add_blocker(*this, &fd_entry)) - should_block = false; - } - return should_block; -} - -Thread::SelectBlocker::~SelectBlocker() = default; - -void Thread::SelectBlocker::finalize() -{ - Thread::FileBlocker::finalize(); - for (auto& fd_entry : m_fds) { - if (fd_entry.description) - fd_entry.description->blocker_set().remove_blocker(*this); - } -} - -void Thread::SelectBlocker::will_unblock_immediately_without_blocking(UnblockImmediatelyReason reason) -{ - SpinlockLocker lock(m_lock); - if (m_did_unblock) - return; - m_did_unblock = true; - if (reason == UnblockImmediatelyReason::UnblockConditionAlreadyMet) { - auto count = collect_unblocked_flags(); - VERIFY(count > 0); - } -} - -bool Thread::SelectBlocker::unblock_if_conditions_are_met(bool from_add_blocker, void* data) -{ - VERIFY(data); // data is a pointer to an entry in the m_fds vector - auto& fd_info = *static_cast(data); - - { - SpinlockLocker lock(m_lock); - if (m_did_unblock) - return false; - - VERIFY(fd_info.description); - auto unblock_flags = fd_info.description->should_unblock(fd_info.block_flags); - if (unblock_flags == BlockFlags::None) - return false; - - m_did_unblock = true; - - // We need to store unblock_flags here, otherwise someone else - // affecting this file descriptor could change the information - // between now and when was_unblocked is called! - fd_info.unblocked_flags = unblock_flags; - } - - // Only do this once for the first one - if (!from_add_blocker) - unblock_from_blocker(); - return true; -} - -size_t Thread::SelectBlocker::collect_unblocked_flags() -{ - size_t count = 0; - for (auto& fd_entry : m_fds) { - VERIFY(fd_entry.block_flags != FileBlocker::BlockFlags::None); - - if (!fd_entry.description) { - count++; - continue; - } - - // unblock will have set at least the first descriptor's unblock - // flags that triggered the unblock. Make sure we don't discard that - // information as it may have changed by now! - if (fd_entry.unblocked_flags == FileBlocker::BlockFlags::None) - fd_entry.unblocked_flags = fd_entry.description->should_unblock(fd_entry.block_flags); - - if (fd_entry.unblocked_flags != FileBlocker::BlockFlags::None) - count++; - } - return count; -} - -void Thread::SelectBlocker::was_unblocked(bool did_timeout) -{ - Blocker::was_unblocked(did_timeout); - if (!did_timeout && !was_interrupted()) { - { - SpinlockLocker lock(m_lock); - VERIFY(m_did_unblock); - } - size_t count = collect_unblocked_flags(); - // If we were blocked and didn't time out, we should have at least one unblocked fd! - VERIFY(count > 0); - } -} - -Thread::SignalBlocker::SignalBlocker(sigset_t pending_set, siginfo_t& result) - : m_pending_set(pending_set) - , m_result(result) -{ -} - -void Thread::SignalBlocker::will_unblock_immediately_without_blocking(UnblockImmediatelyReason unblock_immediately_reason) -{ - if (unblock_immediately_reason != UnblockImmediatelyReason::TimeoutInThePast) - return; - // If the specified timeout is 0 the caller is simply trying to poll once for pending signals, - // so simply calling check_pending_signals should populate the requested information. - check_pending_signals(false); -} - -bool Thread::SignalBlocker::setup_blocker() -{ - return add_to_blocker_set(thread().m_signal_blocker_set); -} - -bool Thread::SignalBlocker::check_pending_signals(bool from_add_blocker) -{ - { - SpinlockLocker lock(m_lock); - if (m_did_unblock) - return false; - - auto pending_signals = thread().pending_signals() & m_pending_set; - - // Also unblock if we have just "handled" that signal and are in the procecss - // of running their signal handler (i.e. we just unmarked the signal as pending). - if (thread().m_currently_handled_signal) - pending_signals |= (1 << (thread().m_currently_handled_signal - 1)) & m_pending_set; - - auto matching_pending_signal = bit_scan_forward(pending_signals); - - if (matching_pending_signal == 0) - return false; - - m_did_unblock = true; - m_result = {}; - m_result.si_signo = matching_pending_signal; - m_result.si_code = 0; // FIXME: How can we determine this? - } - - if (!from_add_blocker) - unblock_from_blocker(); - return true; -} - -Thread::WaitBlockerSet::ProcessBlockInfo::ProcessBlockInfo(NonnullRefPtr&& process, WaitBlocker::UnblockFlags flags, u8 signal) - : process(move(process)) - , flags(flags) - , signal(signal) -{ -} - -Thread::WaitBlockerSet::ProcessBlockInfo::~ProcessBlockInfo() = default; - -void Thread::WaitBlockerSet::try_unblock(Thread::WaitBlocker& blocker) -{ - SpinlockLocker lock(m_lock); - // We if we have any processes pending - for (size_t i = 0; i < m_processes.size(); i++) { - auto& info = m_processes[i]; - // We need to call unblock as if we were called from add_blocker - // so that we don't trigger a context switch by yielding! - if (info.was_waited && blocker.is_wait()) - continue; // This state was already waited on, do not unblock - if (blocker.unblock(info.process, info.flags, info.signal, true)) { - if (blocker.is_wait()) { - if (info.flags == Thread::WaitBlocker::UnblockFlags::Terminated) { - m_processes.remove(i); - dbgln_if(WAITBLOCK_DEBUG, "WaitBlockerSet[{}] terminated, remove {}", m_process, *info.process); - } else { - dbgln_if(WAITBLOCK_DEBUG, "WaitBlockerSet[{}] terminated, mark as waited {}", m_process, *info.process); - info.was_waited = true; - } - } - break; - } - } -} - -void Thread::WaitBlockerSet::disowned_by_waiter(Process& process) -{ - SpinlockLocker lock(m_lock); - if (m_finalized) - return; - for (size_t i = 0; i < m_processes.size();) { - auto& info = m_processes[i]; - if (info.process == &process) { - unblock_all_blockers_whose_conditions_are_met_locked([&](Blocker& b, void*, bool&) { - VERIFY(b.blocker_type() == Blocker::Type::Wait); - auto& blocker = static_cast(b); - bool did_unblock = blocker.unblock(info.process, WaitBlocker::UnblockFlags::Disowned, 0, false); - VERIFY(did_unblock); // disowning must unblock everyone - return true; - }); - dbgln_if(WAITBLOCK_DEBUG, "WaitBlockerSet[{}] disowned {}", m_process, *info.process); - m_processes.remove(i); - continue; - } - - i++; - } -} - -bool Thread::WaitBlockerSet::unblock(Process& process, WaitBlocker::UnblockFlags flags, u8 signal) -{ - VERIFY(flags != WaitBlocker::UnblockFlags::Disowned); - - bool did_unblock_any = false; - bool did_wait = false; - bool was_waited_already = false; - - SpinlockLocker lock(m_lock); - if (m_finalized) - return false; - if (flags != WaitBlocker::UnblockFlags::Terminated) { - // First check if this state was already waited on - for (auto& info : m_processes) { - if (info.process == &process) { - was_waited_already = info.was_waited; - break; - } - } - } - - unblock_all_blockers_whose_conditions_are_met_locked([&](Blocker& b, void*, bool&) { - VERIFY(b.blocker_type() == Blocker::Type::Wait); - auto& blocker = static_cast(b); - if (was_waited_already && blocker.is_wait()) - return false; // This state was already waited on, do not unblock - if (blocker.unblock(process, flags, signal, false)) { - did_wait |= blocker.is_wait(); // anyone requesting a wait - did_unblock_any = true; - return true; - } - return false; - }); - - // If no one has waited (yet), or this wasn't a wait, or if it's anything other than - // UnblockFlags::Terminated then add it to your list - if (!did_unblock_any || !did_wait || flags != WaitBlocker::UnblockFlags::Terminated) { - bool updated_existing = false; - for (auto& info : m_processes) { - if (info.process == &process) { - VERIFY(info.flags != WaitBlocker::UnblockFlags::Terminated); - info.flags = flags; - info.signal = signal; - info.was_waited = did_wait; - dbgln_if(WAITBLOCK_DEBUG, "WaitBlockerSet[{}] update {} flags={}, waited={}", m_process, process, (int)flags, info.was_waited); - updated_existing = true; - break; - } - } - if (!updated_existing) { - dbgln_if(WAITBLOCK_DEBUG, "WaitBlockerSet[{}] add {} flags: {}", m_process, process, (int)flags); - m_processes.append(ProcessBlockInfo(process, flags, signal)); - } - } - return did_unblock_any; -} - -bool Thread::WaitBlockerSet::should_add_blocker(Blocker& b, void*) -{ - // NOTE: m_lock is held already! - if (m_finalized) - return false; - VERIFY(b.blocker_type() == Blocker::Type::Wait); - auto& blocker = static_cast(b); - // See if we can match any process immediately - for (size_t i = 0; i < m_processes.size(); i++) { - auto& info = m_processes[i]; - if (blocker.unblock(info.process, info.flags, info.signal, true)) { - // Only remove the entry if UnblockFlags::Terminated - if (info.flags == Thread::WaitBlocker::UnblockFlags::Terminated && blocker.is_wait()) - m_processes.remove(i); - return false; - } - } - return true; -} - -void Thread::WaitBlockerSet::finalize() -{ - SpinlockLocker lock(m_lock); - VERIFY(!m_finalized); - m_finalized = true; - - // Clear the list of threads here so we can drop the references to them - m_processes.clear(); - - // NOTE: Kernel processes don't have a leaked ref on them. - if (!m_process.is_kernel_process()) { - // No more waiters, drop the last reference immediately. This may - // cause us to be destructed ourselves! - VERIFY(m_process.ref_count() > 0); - m_process.unref(); - } -} - -Thread::WaitBlocker::WaitBlocker(int wait_options, Variant, NonnullRefPtr> waitee, ErrorOr& result) - : m_wait_options(wait_options) - , m_result(result) - , m_waitee(move(waitee)) -{ -} - -bool Thread::WaitBlocker::setup_blocker() -{ - if (m_wait_options & WNOHANG) - return false; - return add_to_blocker_set(Process::current().wait_blocker_set()); -} - -void Thread::WaitBlocker::will_unblock_immediately_without_blocking(UnblockImmediatelyReason) -{ - Process::current().wait_blocker_set().try_unblock(*this); -} - -void Thread::WaitBlocker::was_unblocked(bool) -{ - bool got_sigchld, try_unblock; - { - SpinlockLocker lock(m_lock); - try_unblock = !m_did_unblock; - got_sigchld = m_got_sigchild; - } - - if (try_unblock) - Process::current().wait_blocker_set().try_unblock(*this); - - // If we were interrupted by SIGCHLD (which gets special handling - // here) we're not going to return with EINTR. But we're going to - // deliver SIGCHLD (only) here. - auto* current_thread = Thread::current(); - if (got_sigchld && current_thread->state() != State::Stopped) - current_thread->try_dispatch_one_pending_signal(SIGCHLD); -} - -void Thread::WaitBlocker::do_was_disowned() -{ - VERIFY(!m_did_unblock); - m_did_unblock = true; - m_result = ECHILD; -} - -void Thread::WaitBlocker::do_set_result(siginfo_t const& result) -{ - VERIFY(!m_did_unblock); - m_did_unblock = true; - m_result = result; - - if (do_get_interrupted_by_signal() == SIGCHLD) { - // This makes it so that wait() will return normally despite the - // fact that SIGCHLD was delivered. Calling do_clear_interrupted_by_signal - // will disable dispatching signals in Thread::block and prevent - // it from returning with EINTR. We will then manually dispatch - // SIGCHLD (and only SIGCHLD) in was_unblocked. - m_got_sigchild = true; - do_clear_interrupted_by_signal(); - } -} - -bool Thread::WaitBlocker::unblock(Process& process, UnblockFlags flags, u8 signal, bool from_add_blocker) -{ - VERIFY(flags != UnblockFlags::Terminated || signal == 0); // signal argument should be ignored for Terminated - - bool do_not_unblock = m_waitee.visit( - [&](NonnullRefPtr const& waitee_process) { - return &process != waitee_process; - }, - [&](NonnullRefPtr const& waitee_process_group) { - return waitee_process_group->pgid() != process.pgid(); - }, - [&](Empty const&) { - // Generic waiter won't be unblocked by disown - return flags == UnblockFlags::Disowned; - }); - - if (do_not_unblock) - return false; - - switch (flags) { - case UnblockFlags::Terminated: - if (!(m_wait_options & WEXITED)) - return false; - break; - case UnblockFlags::Stopped: - if (!(m_wait_options & WSTOPPED)) - return false; - if (!(m_wait_options & WUNTRACED) && !process.is_traced()) - return false; - break; - case UnblockFlags::Continued: - if (!(m_wait_options & WCONTINUED)) - return false; - if (!(m_wait_options & WUNTRACED) && !process.is_traced()) - return false; - break; - case UnblockFlags::Disowned: - SpinlockLocker lock(m_lock); - // Disowning must unblock anyone waiting for this process explicitly - if (!m_did_unblock) - do_was_disowned(); - return true; - } - - if (flags == UnblockFlags::Terminated) { - VERIFY(process.is_dead()); - - SpinlockLocker lock(m_lock); - if (m_did_unblock) - return false; - // Up until this point, this function may have been called - // more than once! - do_set_result(process.wait_info()); - } else { - siginfo_t siginfo {}; - { - SpinlockLocker lock(g_scheduler_lock); - auto credentials = process.credentials(); - // We need to gather the information before we release the scheduler lock! - siginfo.si_signo = SIGCHLD; - siginfo.si_pid = process.pid().value(); - siginfo.si_uid = credentials->uid().value(); - siginfo.si_status = signal; - - switch (flags) { - case UnblockFlags::Terminated: - case UnblockFlags::Disowned: - VERIFY_NOT_REACHED(); - case UnblockFlags::Stopped: - siginfo.si_code = CLD_STOPPED; - break; - case UnblockFlags::Continued: - siginfo.si_code = CLD_CONTINUED; - break; - } - } - - SpinlockLocker lock(m_lock); - if (m_did_unblock) - return false; - // Up until this point, this function may have been called - // more than once! - do_set_result(siginfo); - } - - if (!from_add_blocker) { - // Only call unblock if we weren't called from within add_to_blocker_set! - VERIFY(flags != UnblockFlags::Disowned); - unblock_from_blocker(); - } - // Because this may be called from add_blocker, in which case we should - // not be actually trying to unblock the thread (because it hasn't actually - // been blocked yet), we need to return true anyway - return true; -} - -Thread::FlockBlocker::FlockBlocker(NonnullRefPtr inode, flock const& flock) - : m_inode(move(inode)) - , m_flock(flock) -{ -} - -void Thread::FlockBlocker::will_unblock_immediately_without_blocking(UnblockImmediatelyReason reason) -{ - VERIFY(reason == UnblockImmediatelyReason::UnblockConditionAlreadyMet); -} - -bool Thread::FlockBlocker::setup_blocker() -{ - return add_to_blocker_set(m_inode->flock_blocker_set()); -} - -bool Thread::FlockBlocker::try_unblock(bool from_add_blocker) -{ - if (!m_inode->can_apply_flock(m_flock)) - return false; - - { - SpinlockLocker lock(m_lock); - if (m_did_unblock) - return false; - m_did_unblock = true; - } - - if (!from_add_blocker) - unblock_from_blocker(); - return true; -} - -} diff --git a/Kernel/Tasks/ThreadTracer.cpp b/Kernel/Tasks/ThreadTracer.cpp deleted file mode 100644 index fab074982d2..00000000000 --- a/Kernel/Tasks/ThreadTracer.cpp +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include - -namespace Kernel { - -ThreadTracer::ThreadTracer(ProcessID tracer_pid) - : m_tracer_pid(tracer_pid) -{ -} - -void ThreadTracer::set_regs(RegisterState const& regs) -{ - PtraceRegisters r {}; - copy_kernel_registers_into_ptrace_registers(r, regs); - m_regs = r; -} - -} diff --git a/Kernel/Tasks/ThreadTracer.h b/Kernel/Tasks/ThreadTracer.h deleted file mode 100644 index f5a138c332c..00000000000 --- a/Kernel/Tasks/ThreadTracer.h +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include - -namespace Kernel { - -class ThreadTracer { -public: - static ErrorOr> try_create(ProcessID tracer) { return adopt_nonnull_own_or_enomem(new (nothrow) ThreadTracer(tracer)); } - - ProcessID tracer_pid() const { return m_tracer_pid; } - bool has_pending_signal(u32 signal) const { return (m_pending_signals & (1 << (signal - 1))) != 0; } - void set_signal(u32 signal) { m_pending_signals |= (1 << (signal - 1)); } - void unset_signal(u32 signal) { m_pending_signals &= ~(1 << (signal - 1)); } - - bool is_tracing_syscalls() const { return m_trace_syscalls; } - void set_trace_syscalls(bool val) { m_trace_syscalls = val; } - - void set_regs(RegisterState const& regs); - void set_regs(PtraceRegisters const& regs) { m_regs = regs; } - bool has_regs() const { return m_regs.has_value(); } - PtraceRegisters const& regs() const - { - VERIFY(m_regs.has_value()); - return m_regs.value(); - } - -private: - explicit ThreadTracer(ProcessID); - - ProcessID m_tracer_pid { -1 }; - - // This is a bitmap for signals that are sent from the tracer to the tracee - // TODO: Since we do not currently support sending signals - // to the tracee via PT_CONTINUE, this bitmap is always zeroed - u32 m_pending_signals { 0 }; - - bool m_trace_syscalls { false }; - Optional m_regs; -}; - -} diff --git a/Kernel/Tasks/WaitQueue.cpp b/Kernel/Tasks/WaitQueue.cpp deleted file mode 100644 index 4245712c787..00000000000 --- a/Kernel/Tasks/WaitQueue.cpp +++ /dev/null @@ -1,96 +0,0 @@ -/* - * Copyright (c) 2020, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include - -namespace Kernel { - -bool WaitQueue::should_add_blocker(Thread::Blocker& b, void*) -{ - VERIFY(m_lock.is_locked()); - VERIFY(b.blocker_type() == Thread::Blocker::Type::Queue); - if (m_wake_requested) { - m_wake_requested = false; - dbgln_if(WAITQUEUE_DEBUG, "WaitQueue @ {}: do not block thread {}", this, b.thread()); - return false; - } - dbgln_if(WAITQUEUE_DEBUG, "WaitQueue @ {}: should block thread {}", this, b.thread()); - return true; -} - -u32 WaitQueue::wake_one() -{ - u32 did_wake = 0; - SpinlockLocker lock(m_lock); - dbgln_if(WAITQUEUE_DEBUG, "WaitQueue @ {}: wake_one", this); - bool did_unblock_one = unblock_all_blockers_whose_conditions_are_met_locked([&](Thread::Blocker& b, void*, bool& stop_iterating) { - VERIFY(b.blocker_type() == Thread::Blocker::Type::Queue); - auto& blocker = static_cast(b); - dbgln_if(WAITQUEUE_DEBUG, "WaitQueue @ {}: wake_one unblocking {}", this, blocker.thread()); - if (blocker.unblock()) { - stop_iterating = true; - did_wake = 1; - return true; - } - return false; - }); - m_wake_requested = !did_unblock_one; - dbgln_if(WAITQUEUE_DEBUG, "WaitQueue @ {}: wake_one woke {} threads", this, did_wake); - return did_wake; -} - -u32 WaitQueue::wake_n(u32 wake_count) -{ - if (wake_count == 0) - return 0; // should we assert instead? - SpinlockLocker lock(m_lock); - dbgln_if(WAITQUEUE_DEBUG, "WaitQueue @ {}: wake_n({})", this, wake_count); - u32 did_wake = 0; - - bool did_unblock_some = unblock_all_blockers_whose_conditions_are_met_locked([&](Thread::Blocker& b, void*, bool& stop_iterating) { - VERIFY(b.blocker_type() == Thread::Blocker::Type::Queue); - auto& blocker = static_cast(b); - dbgln_if(WAITQUEUE_DEBUG, "WaitQueue @ {}: wake_n unblocking {}", this, blocker.thread()); - VERIFY(did_wake < wake_count); - if (blocker.unblock()) { - if (++did_wake >= wake_count) - stop_iterating = true; - return true; - } - return false; - }); - m_wake_requested = !did_unblock_some; - dbgln_if(WAITQUEUE_DEBUG, "WaitQueue @ {}: wake_n({}) woke {} threads", this, wake_count, did_wake); - return did_wake; -} - -u32 WaitQueue::wake_all() -{ - SpinlockLocker lock(m_lock); - - dbgln_if(WAITQUEUE_DEBUG, "WaitQueue @ {}: wake_all", this); - u32 did_wake = 0; - - bool did_unblock_any = unblock_all_blockers_whose_conditions_are_met_locked([&](Thread::Blocker& b, void*, bool&) { - VERIFY(b.blocker_type() == Thread::Blocker::Type::Queue); - auto& blocker = static_cast(b); - - dbgln_if(WAITQUEUE_DEBUG, "WaitQueue @ {}: wake_all unblocking {}", this, blocker.thread()); - - if (blocker.unblock()) { - did_wake++; - return true; - } - return false; - }); - m_wake_requested = !did_unblock_any; - dbgln_if(WAITQUEUE_DEBUG, "WaitQueue @ {}: wake_all woke {} threads", this, did_wake); - return did_wake; -} - -} diff --git a/Kernel/Tasks/WaitQueue.h b/Kernel/Tasks/WaitQueue.h deleted file mode 100644 index a5e60d62aa5..00000000000 --- a/Kernel/Tasks/WaitQueue.h +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright (c) 2020, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include - -namespace Kernel { - -class WaitQueue final : public Thread::BlockerSet { -public: - u32 wake_one(); - u32 wake_n(u32 wake_count); - u32 wake_all(); - - template - Thread::BlockResult wait_on(Thread::BlockTimeout const& timeout, Args&&... args) - { - return Thread::current()->block(timeout, *this, forward(args)...); - } - - template - void wait_forever(Args&&... args) - { - (void)Thread::current()->block({}, *this, forward(args)...); - } - -protected: - virtual bool should_add_blocker(Thread::Blocker& b, void*) override; - -private: - bool m_wake_requested { false }; -}; - -} diff --git a/Kernel/Tasks/WorkQueue.cpp b/Kernel/Tasks/WorkQueue.cpp deleted file mode 100644 index bf88875e1c5..00000000000 --- a/Kernel/Tasks/WorkQueue.cpp +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright (c) 2021, the SerenityOS developers. - * Copyright (c) 2021, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include - -namespace Kernel { - -WorkQueue* g_io_work; -WorkQueue* g_ata_work; - -UNMAP_AFTER_INIT void WorkQueue::initialize() -{ - g_io_work = new WorkQueue("IO WorkQueue Task"sv); - g_ata_work = new WorkQueue("ATA WorkQueue Task"sv); -} - -UNMAP_AFTER_INIT WorkQueue::WorkQueue(StringView name) -{ - auto [_, thread] = Process::create_kernel_process(name, [this] { - while (!Process::current().is_dying()) { - WorkItem* item; - bool have_more; - m_items.with([&](auto& items) { - item = items.take_first(); - have_more = !items.is_empty(); - }); - if (item) { - item->function(); - delete item; - - if (have_more) - continue; - } - [[maybe_unused]] auto result = m_wait_queue.wait_on({}); - } - Process::current().sys$exit(0); - VERIFY_NOT_REACHED(); - }).release_value_but_fixme_should_propagate_errors(); - m_thread = move(thread); -} - -void WorkQueue::do_queue(WorkItem& item) -{ - m_items.with([&](auto& items) { - items.append(item); - }); - m_wait_queue.wake_one(); -} - -} diff --git a/Kernel/Tasks/WorkQueue.h b/Kernel/Tasks/WorkQueue.h deleted file mode 100644 index a28166b9743..00000000000 --- a/Kernel/Tasks/WorkQueue.h +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Copyright (c) 2021, the SerenityOS developers. - * Copyright (c) 2021, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include - -namespace Kernel { - -extern WorkQueue* g_io_work; -extern WorkQueue* g_ata_work; - -class WorkQueue { - AK_MAKE_NONCOPYABLE(WorkQueue); - AK_MAKE_NONMOVABLE(WorkQueue); - -public: - static void initialize(); - - ErrorOr try_queue(void (*function)(void*), void* data = nullptr, void (*free_data)(void*) = nullptr) - { - auto item = new (nothrow) WorkItem; // TODO: use a pool - if (!item) - return Error::from_errno(ENOMEM); - item->function = [function, data, free_data] { - function(data); - if (free_data) - free_data(data); - }; - do_queue(*item); - return {}; - } - - template - ErrorOr try_queue(Function function) - { - auto item = new (nothrow) WorkItem; // TODO: use a pool - if (!item) - return Error::from_errno(ENOMEM); - item->function = Function(function); - do_queue(*item); - return {}; - } - -private: - explicit WorkQueue(StringView); - - struct WorkItem { - public: - IntrusiveListNode m_node; - Function function; - }; - - void do_queue(WorkItem&); - - RefPtr m_thread; - WaitQueue m_wait_queue; - SpinlockProtected, LockRank::None> m_items {}; -}; - -} diff --git a/Kernel/Time/HardwareTimer.h b/Kernel/Time/HardwareTimer.h deleted file mode 100644 index a1463d2589e..00000000000 --- a/Kernel/Time/HardwareTimer.h +++ /dev/null @@ -1,168 +0,0 @@ -/* - * Copyright (c) 2020, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include - -namespace Kernel { - -enum class HardwareTimerType { -#if ARCH(X86_64) - i8253 = 0x1, /* PIT */ - RTC = 0x2, /* Real Time Clock */ - HighPrecisionEventTimer = 0x3, /* also known as IA-PC HPET */ - LocalAPICTimer = 0x4, /* Local APIC */ -#elif ARCH(AARCH64) - RPiTimer = 0x5, -#elif ARCH(RISCV64) - RISCVTimer = 0x6, -#else -# error Unknown architecture -#endif -}; - -template -class HardwareTimer; - -class HardwareTimerBase : public AtomicRefCounted { -public: - virtual ~HardwareTimerBase() = default; - - // We need to create a virtual will_be_destroyed here because we derive - // from RefCounted here, which means that RefCounted<> - // will only call will_be_destroyed if we define it here. The derived - // classes then should forward this to e.g. GenericInterruptHandler. - virtual void will_be_destroyed() = 0; - - virtual StringView model() const = 0; - virtual HardwareTimerType timer_type() const = 0; - virtual Function set_callback(Function) = 0; - - virtual bool is_periodic() const = 0; - virtual bool is_periodic_capable() const = 0; - virtual void set_periodic() = 0; - virtual void set_non_periodic() = 0; - virtual void disable() = 0; - virtual bool can_query_raw() const { return false; } - virtual u64 current_raw() const { return 0; } - virtual u64 raw_to_ns(u64) const { return 0; } - - virtual size_t ticks_per_second() const = 0; - - virtual void reset_to_default_ticks_per_second() = 0; - virtual bool try_to_set_frequency(size_t frequency) = 0; - virtual bool is_capable_of_frequency(size_t frequency) const = 0; - virtual size_t calculate_nearest_possible_frequency(size_t frequency) const = 0; -}; - -template<> -class HardwareTimer - : public HardwareTimerBase - , public IRQHandler { -public: - virtual void will_be_destroyed() override - { - IRQHandler::will_be_destroyed(); - } - - virtual StringView purpose() const override - { - if (TimeManagement::the().is_system_timer(*this)) - return "System Timer"sv; - return model(); - } - - virtual Function set_callback(Function callback) override - { - disable_irq(); - auto previous_callback = move(m_callback); - m_callback = move(callback); - enable_irq(); - return previous_callback; - } - - virtual size_t ticks_per_second() const override { return m_frequency; } - -protected: - HardwareTimer(u8 irq_number, Function callback = nullptr) - : IRQHandler(irq_number) - , m_callback(move(callback)) - { - } - - virtual bool handle_irq(RegisterState const& regs) override - { - // Note: if we have an IRQ on this line, it's going to be the timer always - if (m_callback) { - m_callback(regs); - return true; - } - return false; - } - - u64 m_frequency { OPTIMAL_TICKS_PER_SECOND_RATE }; - -private: - Function m_callback; -}; - -template<> -class HardwareTimer - : public HardwareTimerBase - , public GenericInterruptHandler { -public: - virtual void will_be_destroyed() override - { - GenericInterruptHandler::will_be_destroyed(); - } - - virtual StringView purpose() const override - { - return model(); - } - - virtual Function set_callback(Function callback) override - { - auto previous_callback = move(m_callback); - m_callback = move(callback); - return previous_callback; - } - - virtual size_t sharing_devices_count() const override { return 0; } - virtual bool is_shared_handler() const override { return false; } - virtual HandlerType type() const override { return HandlerType::IRQHandler; } - virtual StringView controller() const override { return {}; } - virtual bool eoi() override; - virtual size_t ticks_per_second() const override { return m_frequency; } - -protected: - HardwareTimer(u8 irq_number, Function callback = nullptr) - : GenericInterruptHandler(irq_number) - , m_callback(move(callback)) - { - } - - virtual bool handle_interrupt(RegisterState const& regs) override - { - // Note: if we have an IRQ on this line, it's going to be the timer always - if (m_callback) { - m_callback(regs); - return true; - } - return false; - } - - u64 m_frequency { OPTIMAL_TICKS_PER_SECOND_RATE }; - -private: - Function m_callback; -}; - -} diff --git a/Kernel/Time/TimeManagement.cpp b/Kernel/Time/TimeManagement.cpp deleted file mode 100644 index f9fdbe9131d..00000000000 --- a/Kernel/Time/TimeManagement.cpp +++ /dev/null @@ -1,596 +0,0 @@ -/* - * Copyright (c) 2020, Liav A. - * Copyright (c) 2022, Timon Kruiper - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#if ARCH(X86_64) -# include -# include -# include -# include -# include -# include -# include -#elif ARCH(AARCH64) -# include -#elif ARCH(RISCV64) -# include -#else -# error Unknown architecture -#endif - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace Kernel { - -static Singleton s_the; - -bool TimeManagement::is_initialized() -{ - return s_the.is_initialized(); -} - -TimeManagement& TimeManagement::the() -{ - return *s_the; -} - -// The s_scheduler_specific_current_time function provides a current time for scheduling purposes, -// which may not necessarily relate to wall time -static u64 (*s_scheduler_current_time)(); - -static u64 current_time_monotonic() -{ - // We always need a precise timestamp here, we cannot rely on a coarse timestamp - return (u64)TimeManagement::the().monotonic_time(TimePrecision::Precise).nanoseconds(); -} - -u64 TimeManagement::scheduler_current_time() -{ - VERIFY(s_scheduler_current_time); - return s_scheduler_current_time(); -} - -ErrorOr TimeManagement::validate_clock_id(clockid_t clock_id) -{ - switch (clock_id) { - case CLOCK_MONOTONIC: - case CLOCK_MONOTONIC_COARSE: - case CLOCK_MONOTONIC_RAW: - case CLOCK_REALTIME: - case CLOCK_REALTIME_COARSE: - return {}; - default: - return EINVAL; - }; -} - -Duration TimeManagement::current_time(clockid_t clock_id) const -{ - switch (clock_id) { - case CLOCK_MONOTONIC: - return monotonic_time(TimePrecision::Precise).time_since_start({}); - case CLOCK_MONOTONIC_COARSE: - return monotonic_time(TimePrecision::Coarse).time_since_start({}); - case CLOCK_MONOTONIC_RAW: - return monotonic_time_raw().time_since_start({}); - case CLOCK_REALTIME: - return epoch_time(TimePrecision::Precise).offset_to_epoch(); - case CLOCK_REALTIME_COARSE: - return epoch_time(TimePrecision::Coarse).offset_to_epoch(); - default: - // Syscall entrypoint is missing a is_valid_clock_id(..) check? - VERIFY_NOT_REACHED(); - } -} - -bool TimeManagement::is_system_timer(HardwareTimerBase const& timer) const -{ - return &timer == m_system_timer.ptr(); -} - -void TimeManagement::set_epoch_time(UnixDateTime ts) -{ - // FIXME: The interrupt disabler intends to enforce atomic update of epoch time and remaining adjustment, - // but that sort of assumption is known to break on SMP. - InterruptDisabler disabler; - m_epoch_time = ts; - m_remaining_epoch_time_adjustment = {}; -} - -MonotonicTime TimeManagement::monotonic_time(TimePrecision precision) const -{ - // This is the time when last updated by an interrupt. - u64 seconds; - u32 ticks; - - bool do_query = precision == TimePrecision::Precise && m_can_query_precise_time.was_set(); - - u32 update_iteration; - do { - update_iteration = m_update1.load(AK::MemoryOrder::memory_order_acquire); - seconds = m_seconds_since_boot; - ticks = m_ticks_this_second; - - if (do_query) { -#if ARCH(X86_64) - // We may have to do this over again if the timer interrupt fires - // while we're trying to query the information. In that case, our - // seconds and ticks became invalid, producing an incorrect time. - // Be sure to not modify m_seconds_since_boot and m_ticks_this_second - // because this may only be modified by the interrupt handler - HPET::the().update_time(seconds, ticks, true); -#elif ARCH(AARCH64) - // FIXME: Get rid of these horrible casts - const_cast(static_cast(m_system_timer.ptr()))->update_time(seconds, ticks, true); -#elif ARCH(RISCV64) - TODO_RISCV64(); -#else -# error Unknown architecture -#endif - } - } while (update_iteration != m_update2.load(AK::MemoryOrder::memory_order_acquire)); - - VERIFY(m_time_ticks_per_second > 0); - VERIFY(ticks < m_time_ticks_per_second); - u64 ns = ((u64)ticks * 1000000000ull) / m_time_ticks_per_second; - VERIFY(ns < 1000000000ull); - return MonotonicTime::from_hardware_time({}, seconds, ns); -} - -UnixDateTime TimeManagement::epoch_time(TimePrecision) const -{ - // TODO: Take into account precision - UnixDateTime time; - u32 update_iteration; - do { - update_iteration = m_update1.load(AK::MemoryOrder::memory_order_acquire); - time = m_epoch_time; - } while (update_iteration != m_update2.load(AK::MemoryOrder::memory_order_acquire)); - return time; -} - -u64 TimeManagement::uptime_ms() const -{ - auto mtime = monotonic_time().time_since_start({}).to_timespec(); - // This overflows after 292 million years of uptime. - // Since this is only used for performance timestamps and sys$times, that's probably enough. - u64 ms = mtime.tv_sec * 1000ull; - ms += mtime.tv_nsec / 1000000; - return ms; -} - -UNMAP_AFTER_INIT void TimeManagement::initialize([[maybe_unused]] u32 cpu) -{ - // Note: We must disable interrupts, because the timers interrupt might fire before - // the TimeManagement class is completely initialized. - InterruptDisabler disabler; - -#if ARCH(X86_64) - if (cpu == 0) { - VERIFY(!s_the.is_initialized()); - s_the.ensure_instance(); - - if (APIC::initialized()) { - // Initialize the APIC timers after the other timers as the - // initialization needs to briefly enable interrupts, which then - // would trigger a deadlock trying to get the s_the instance while - // creating it. - if (auto* apic_timer = APIC::the().initialize_timers(*s_the->m_system_timer)) { - dmesgln("Duration: Using APIC timer as system timer"); - s_the->set_system_timer(*apic_timer); - } - } - } else { - VERIFY(s_the.is_initialized()); - if (auto* apic_timer = APIC::the().get_timer()) { - dmesgln("Duration: Enable APIC timer on CPU #{}", cpu); - apic_timer->enable_local_timer(); - } - } -#elif ARCH(AARCH64) - if (cpu == 0) { - VERIFY(!s_the.is_initialized()); - s_the.ensure_instance(); - } -#elif ARCH(RISCV64) - if (cpu == 0) { - VERIFY(!s_the.is_initialized()); - s_the.ensure_instance(); - } -#else -# error Unknown architecture -#endif - auto* possible_arch_specific_current_time_function = optional_current_time(); - if (possible_arch_specific_current_time_function) - s_scheduler_current_time = possible_arch_specific_current_time_function; - else - s_scheduler_current_time = current_time_monotonic; -} - -void TimeManagement::set_system_timer(HardwareTimerBase& timer) -{ - VERIFY(Processor::is_bootstrap_processor()); // This should only be called on the BSP! - auto original_callback = m_system_timer->set_callback(nullptr); - m_system_timer->disable(); - timer.set_callback(move(original_callback)); - m_system_timer = timer; -} - -time_t TimeManagement::ticks_per_second() const -{ - return m_time_keeper_timer->ticks_per_second(); -} - -UnixDateTime TimeManagement::boot_time() -{ -#if ARCH(X86_64) - return RTC::boot_time(); -#elif ARCH(AARCH64) || ARCH(RISCV64) - // FIXME: Return correct boot time - return UnixDateTime::epoch(); -#else -# error Unknown architecture -#endif -} - -Duration TimeManagement::clock_resolution() const -{ - long nanoseconds_per_tick = 1'000'000'000 / m_time_keeper_timer->ticks_per_second(); - return Duration::from_nanoseconds(nanoseconds_per_tick); -} - -UNMAP_AFTER_INIT TimeManagement::TimeManagement() - : m_time_page_region(MM.allocate_kernel_region(PAGE_SIZE, "Duration page"sv, Memory::Region::Access::ReadWrite, AllocationStrategy::AllocateNow).release_value_but_fixme_should_propagate_errors()) -{ -#if ARCH(X86_64) - bool probe_non_legacy_hardware_timers = !(kernel_command_line().is_legacy_time_enabled()); - if (ACPI::is_enabled()) { - if (!ACPI::Parser::the()->x86_specific_flags().cmos_rtc_not_present) { - RTC::initialize(); - m_epoch_time += boot_time().offset_to_epoch(); - } else { - dmesgln("ACPI: RTC CMOS Not present"); - } - } else { - // We just assume that we can access RTC CMOS, if ACPI isn't usable. - RTC::initialize(); - m_epoch_time += boot_time().offset_to_epoch(); - } - if (probe_non_legacy_hardware_timers) { - if (!probe_and_set_x86_non_legacy_hardware_timers()) - if (!probe_and_set_x86_legacy_hardware_timers()) - VERIFY_NOT_REACHED(); - } else if (!probe_and_set_x86_legacy_hardware_timers()) { - VERIFY_NOT_REACHED(); - } -#elif ARCH(AARCH64) - probe_and_set_aarch64_hardware_timers(); -#elif ARCH(RISCV64) - probe_and_set_riscv64_hardware_timers(); -#else -# error Unknown architecture -#endif -} - -UnixDateTime TimeManagement::now() -{ - return s_the.ptr()->epoch_time(); -} - -UNMAP_AFTER_INIT Vector TimeManagement::scan_and_initialize_periodic_timers() -{ - bool should_enable = is_hpet_periodic_mode_allowed(); - dbgln("Duration: Scanning for periodic timers"); - Vector timers; - for (auto& hardware_timer : m_hardware_timers) { - if (hardware_timer->is_periodic_capable()) { - timers.append(hardware_timer); - if (should_enable) - hardware_timer->set_periodic(); - } - } - return timers; -} - -UNMAP_AFTER_INIT Vector TimeManagement::scan_for_non_periodic_timers() -{ - dbgln("Duration: Scanning for non-periodic timers"); - Vector timers; - for (auto& hardware_timer : m_hardware_timers) { - if (!hardware_timer->is_periodic_capable()) - timers.append(hardware_timer); - } - return timers; -} - -bool TimeManagement::is_hpet_periodic_mode_allowed() -{ - switch (kernel_command_line().hpet_mode()) { - case HPETMode::Periodic: - return true; - case HPETMode::NonPeriodic: - return false; - default: - VERIFY_NOT_REACHED(); - } -} - -#if ARCH(X86_64) -UNMAP_AFTER_INIT bool TimeManagement::probe_and_set_x86_non_legacy_hardware_timers() -{ - if (!ACPI::is_enabled()) - return false; - if (!HPET::test_and_initialize()) - return false; - if (!HPET::the().comparators().size()) { - dbgln("HPET initialization aborted."); - return false; - } - dbgln("HPET: Setting appropriate functions to timers."); - - for (auto& hpet_comparator : HPET::the().comparators()) - m_hardware_timers.append(hpet_comparator); - - auto periodic_timers = scan_and_initialize_periodic_timers(); - auto non_periodic_timers = scan_for_non_periodic_timers(); - - if (is_hpet_periodic_mode_allowed()) - VERIFY(!periodic_timers.is_empty()); - - VERIFY(periodic_timers.size() + non_periodic_timers.size() > 0); - - size_t taken_periodic_timers_count = 0; - size_t taken_non_periodic_timers_count = 0; - - if (periodic_timers.size() > taken_periodic_timers_count) { - m_system_timer = periodic_timers[taken_periodic_timers_count]; - taken_periodic_timers_count += 1; - } else if (non_periodic_timers.size() > taken_non_periodic_timers_count) { - m_system_timer = non_periodic_timers[taken_non_periodic_timers_count]; - taken_non_periodic_timers_count += 1; - } - - m_system_timer->set_callback([this](RegisterState const& regs) { - // Update the time. We don't really care too much about the - // frequency of the interrupt because we'll query the main - // counter to get an accurate time. - if (Processor::is_bootstrap_processor()) { - // TODO: Have the other CPUs call system_timer_tick directly - increment_time_since_boot_hpet(); - } - - system_timer_tick(regs); - }); - - // Use the HPET main counter frequency for time purposes. This is likely - // a much higher frequency than the interrupt itself and allows us to - // keep a more accurate time - m_can_query_precise_time.set(); - m_time_ticks_per_second = HPET::the().frequency(); - - m_system_timer->try_to_set_frequency(m_system_timer->calculate_nearest_possible_frequency(OPTIMAL_TICKS_PER_SECOND_RATE)); - - // We don't need an interrupt for time keeping purposes because we - // can query the timer. - m_time_keeper_timer = m_system_timer; - - if (periodic_timers.size() > taken_periodic_timers_count) { - m_profile_timer = periodic_timers[taken_periodic_timers_count]; - taken_periodic_timers_count += 1; - } else if (non_periodic_timers.size() > taken_non_periodic_timers_count) { - m_profile_timer = non_periodic_timers[taken_non_periodic_timers_count]; - taken_non_periodic_timers_count += 1; - } - - if (m_profile_timer) { - m_profile_timer->set_callback(PerformanceManager::timer_tick); - m_profile_timer->try_to_set_frequency(m_profile_timer->calculate_nearest_possible_frequency(1)); - } - - return true; -} - -UNMAP_AFTER_INIT bool TimeManagement::probe_and_set_x86_legacy_hardware_timers() -{ - if (ACPI::is_enabled()) { - if (ACPI::Parser::the()->x86_specific_flags().cmos_rtc_not_present) { - dbgln("ACPI: CMOS RTC Not Present"); - return false; - } else { - dbgln("ACPI: CMOS RTC Present"); - } - } - - m_hardware_timers.append(PIT::initialize(TimeManagement::update_time)); - m_hardware_timers.append(RealTimeClock::create(TimeManagement::system_timer_tick)); - m_time_keeper_timer = m_hardware_timers[0]; - m_system_timer = m_hardware_timers[1]; - - // The timer is only as accurate as the interrupts... - m_time_ticks_per_second = m_time_keeper_timer->ticks_per_second(); - return true; -} - -void TimeManagement::update_time(RegisterState const&) -{ - TimeManagement::the().increment_time_since_boot(); -} - -void TimeManagement::increment_time_since_boot_hpet() -{ - VERIFY(!m_time_keeper_timer.is_null()); - VERIFY(m_time_keeper_timer->timer_type() == HardwareTimerType::HighPrecisionEventTimer); - - // NOTE: m_seconds_since_boot and m_ticks_this_second are only ever - // updated here! So we can safely read that information, query the clock, - // and when we're all done we can update the information. This reduces - // contention when other processors attempt to read the clock. - auto seconds_since_boot = m_seconds_since_boot; - auto ticks_this_second = m_ticks_this_second; - auto delta_ns = HPET::the().update_time(seconds_since_boot, ticks_this_second, false); - - // Now that we have a precise time, go update it as quickly as we can - u32 update_iteration = m_update2.fetch_add(1, AK::MemoryOrder::memory_order_acquire); - m_seconds_since_boot = seconds_since_boot; - m_ticks_this_second = ticks_this_second; - // TODO: Apply m_remaining_epoch_time_adjustment - timespec time_adjustment = { (time_t)(delta_ns / 1000000000), (long)(delta_ns % 1000000000) }; - m_epoch_time += Duration::from_timespec(time_adjustment); - - m_update1.store(update_iteration + 1, AK::MemoryOrder::memory_order_release); - - update_time_page(); -} -#elif ARCH(AARCH64) -UNMAP_AFTER_INIT bool TimeManagement::probe_and_set_aarch64_hardware_timers() -{ - m_hardware_timers.append(RPi::Timer::initialize()); - m_system_timer = m_hardware_timers[0]; - m_time_ticks_per_second = m_system_timer->ticks_per_second(); - - m_system_timer->set_callback([this](RegisterState const& regs) { - auto seconds_since_boot = m_seconds_since_boot; - auto ticks_this_second = m_ticks_this_second; - auto delta_ns = static_cast(m_system_timer.ptr())->update_time(seconds_since_boot, ticks_this_second, false); - - u32 update_iteration = m_update2.fetch_add(1, AK::MemoryOrder::memory_order_acquire); - m_seconds_since_boot = seconds_since_boot; - m_ticks_this_second = ticks_this_second; - - m_epoch_time += Duration::from_nanoseconds(delta_ns); - - m_update1.store(update_iteration + 1, AK::MemoryOrder::memory_order_release); - - update_time_page(); - - system_timer_tick(regs); - }); - - m_time_keeper_timer = m_system_timer; - - return true; -} -#elif ARCH(RISCV64) -UNMAP_AFTER_INIT bool TimeManagement::probe_and_set_riscv64_hardware_timers() -{ - m_hardware_timers.append(RISCV64::Timer::initialize()); - m_system_timer = m_hardware_timers[0]; - m_time_ticks_per_second = m_system_timer->ticks_per_second(); - - m_system_timer->set_callback([this](RegisterState const& regs) { - auto seconds_since_boot = m_seconds_since_boot; - auto ticks_this_second = m_ticks_this_second; - auto delta_ns = static_cast(m_system_timer.ptr())->update_time(seconds_since_boot, ticks_this_second, false); - - u32 update_iteration = m_update2.fetch_add(1, AK::MemoryOrder::memory_order_acquire); - m_seconds_since_boot = seconds_since_boot; - m_ticks_this_second = ticks_this_second; - - m_epoch_time += Duration::from_nanoseconds(delta_ns); - - m_update1.store(update_iteration + 1, AK::MemoryOrder::memory_order_release); - - update_time_page(); - - system_timer_tick(regs); - }); - - m_time_keeper_timer = m_system_timer; - - return true; -} -#else -# error Unknown architecture -#endif - -void TimeManagement::increment_time_since_boot() -{ - VERIFY(!m_time_keeper_timer.is_null()); - - // Compute time adjustment for adjtime. Let the clock run up to 1% fast or slow. - // That way, adjtime can adjust up to 36 seconds per hour, without time getting very jumpy. - // Once we have a smarter NTP service that also adjusts the frequency instead of just slewing time, maybe we can lower this. - long nanos_per_tick = 1'000'000'000 / m_time_keeper_timer->ticks_per_second(); - time_t max_slew_nanos = nanos_per_tick / 100; - - u32 update_iteration = m_update2.fetch_add(1, AK::MemoryOrder::memory_order_acquire); - - auto slew_nanos = Duration::from_nanoseconds( - clamp(m_remaining_epoch_time_adjustment.to_nanoseconds(), -max_slew_nanos, max_slew_nanos)); - m_remaining_epoch_time_adjustment -= slew_nanos; - - m_epoch_time += Duration::from_nanoseconds(nanos_per_tick + slew_nanos.to_nanoseconds()); - - if (++m_ticks_this_second >= m_time_keeper_timer->ticks_per_second()) { - // FIXME: Synchronize with other clock somehow to prevent drifting apart. - ++m_seconds_since_boot; - m_ticks_this_second = 0; - } - - m_update1.store(update_iteration + 1, AK::MemoryOrder::memory_order_release); - - update_time_page(); -} - -void TimeManagement::system_timer_tick(RegisterState const& regs) -{ - if (Processor::current_in_irq() <= 1) { - // Don't expire timers while handling IRQs - TimerQueue::the().fire(); - } - Scheduler::timer_tick(regs); -} - -bool TimeManagement::enable_profile_timer() -{ - if (!m_profile_timer) - return false; - if (m_profile_enable_count.fetch_add(1) == 0) - return m_profile_timer->try_to_set_frequency(m_profile_timer->calculate_nearest_possible_frequency(OPTIMAL_PROFILE_TICKS_PER_SECOND_RATE)); - return true; -} - -bool TimeManagement::disable_profile_timer() -{ - if (!m_profile_timer) - return false; - if (m_profile_enable_count.fetch_sub(1) == 1) - return m_profile_timer->try_to_set_frequency(m_profile_timer->calculate_nearest_possible_frequency(1)); - return true; -} - -void TimeManagement::update_time_page() -{ - auto& page = time_page(); - u32 update_iteration = AK::atomic_fetch_add(&page.update2, 1u, AK::MemoryOrder::memory_order_acquire); - page.clocks[CLOCK_REALTIME_COARSE] = m_epoch_time.to_timespec(); - page.clocks[CLOCK_MONOTONIC_COARSE] = monotonic_time(TimePrecision::Coarse).time_since_start({}).to_timespec(); - AK::atomic_store(&page.update1, update_iteration + 1u, AK::MemoryOrder::memory_order_release); -} - -TimePage& TimeManagement::time_page() -{ - return *static_cast((void*)m_time_page_region->vaddr().as_ptr()); -} - -Memory::VMObject& TimeManagement::time_page_vmobject() -{ - return m_time_page_region->vmobject(); -} - -} diff --git a/Kernel/Time/TimeManagement.h b/Kernel/Time/TimeManagement.h deleted file mode 100644 index 26d34793db3..00000000000 --- a/Kernel/Time/TimeManagement.h +++ /dev/null @@ -1,124 +0,0 @@ -/* - * Copyright (c) 2020, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace Kernel { - -#define OPTIMAL_TICKS_PER_SECOND_RATE 250 -#define OPTIMAL_PROFILE_TICKS_PER_SECOND_RATE 1000 - -class HardwareTimerBase; - -enum class TimePrecision { - Coarse = 0, - Precise -}; - -class TimeManagement { - -public: - TimeManagement(); - static void initialize(u32 cpu); - static bool is_initialized(); - static TimeManagement& the(); - - static u64 scheduler_current_time(); - - static ErrorOr validate_clock_id(clockid_t); - // This API cannot distinguish returned time types; prefer the clock-specific functions instead. - Duration current_time(clockid_t) const; - MonotonicTime monotonic_time(TimePrecision = TimePrecision::Coarse) const; - MonotonicTime monotonic_time_raw() const - { - // TODO: implement - return monotonic_time(TimePrecision::Precise); - } - UnixDateTime epoch_time(TimePrecision = TimePrecision::Precise) const; - void set_epoch_time(UnixDateTime); - time_t ticks_per_second() const; - static UnixDateTime boot_time(); - Duration clock_resolution() const; - - bool is_system_timer(HardwareTimerBase const&) const; - - void increment_time_since_boot(); - - static bool is_hpet_periodic_mode_allowed(); - - bool enable_profile_timer(); - bool disable_profile_timer(); - - u64 uptime_ms() const; - static UnixDateTime now(); - - // FIXME: Most likely broken, because it does not check m_update[12] for in-progress updates. - Duration remaining_epoch_time_adjustment() const { return m_remaining_epoch_time_adjustment; } - // FIXME: Most likely broken, because it does not check m_update[12] for in-progress updates. - void set_remaining_epoch_time_adjustment(Duration adjustment) { m_remaining_epoch_time_adjustment = adjustment; } - - bool can_query_precise_time() const { return m_can_query_precise_time.was_set(); } - - Memory::VMObject& time_page_vmobject(); - -private: - TimePage& time_page(); - void update_time_page(); - -#if ARCH(X86_64) - bool probe_and_set_x86_legacy_hardware_timers(); - bool probe_and_set_x86_non_legacy_hardware_timers(); - void increment_time_since_boot_hpet(); - static void update_time(RegisterState const&); -#elif ARCH(AARCH64) - bool probe_and_set_aarch64_hardware_timers(); -#elif ARCH(RISCV64) - bool probe_and_set_riscv64_hardware_timers(); -#else -# error Unknown architecture -#endif - Vector scan_and_initialize_periodic_timers(); - Vector scan_for_non_periodic_timers(); - Vector> m_hardware_timers; - void set_system_timer(HardwareTimerBase&); - static void system_timer_tick(RegisterState const&); - - // Variables between m_update1 and m_update2 are synchronized - // FIXME: Replace m_update1 and m_update2 with a SpinlockLocker - Atomic m_update1 { 0 }; - u32 m_ticks_this_second { 0 }; - u64 m_seconds_since_boot { 0 }; - UnixDateTime m_epoch_time {}; - Duration m_remaining_epoch_time_adjustment {}; - Atomic m_update2 { 0 }; - - u32 m_time_ticks_per_second { 0 }; // may be different from interrupts/second (e.g. hpet) - SetOnce m_can_query_precise_time; - bool m_updating_time { false }; // may only be accessed from the BSP! - - LockRefPtr m_system_timer; - LockRefPtr m_time_keeper_timer; - - Atomic m_profile_enable_count { 0 }; - LockRefPtr m_profile_timer; - - NonnullOwnPtr m_time_page_region; -}; - -} diff --git a/Kernel/Time/TimerQueue.cpp b/Kernel/Time/TimerQueue.cpp deleted file mode 100644 index 89546b0c079..00000000000 --- a/Kernel/Time/TimerQueue.cpp +++ /dev/null @@ -1,232 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include -#include - -namespace Kernel { - -static Singleton s_the; -static Spinlock g_timerqueue_lock {}; - -Duration Timer::remaining() const -{ - return m_remaining; -} - -Duration Timer::now(bool is_firing) const -{ - // NOTE: If is_firing is true then TimePrecision::Precise isn't really useful here. - // We already have a quite precise time stamp because we just updated the time in the - // interrupt handler. In those cases, just use coarse timestamps. - auto clock_id = m_clock_id; - if (is_firing) { - switch (clock_id) { - case CLOCK_MONOTONIC: - clock_id = CLOCK_MONOTONIC_COARSE; - break; - case CLOCK_MONOTONIC_RAW: - // TODO: use a special CLOCK_MONOTONIC_RAW_COARSE like mechanism here - break; - case CLOCK_REALTIME: - clock_id = CLOCK_REALTIME_COARSE; - break; - default: - break; - } - } - return TimeManagement::the().current_time(clock_id); -} - -TimerQueue& TimerQueue::the() -{ - return *s_the; -} - -UNMAP_AFTER_INIT TimerQueue::TimerQueue() -{ - m_ticks_per_second = TimeManagement::the().ticks_per_second(); -} - -bool TimerQueue::add_timer_without_id(NonnullRefPtr timer, clockid_t clock_id, Duration const& deadline, Function&& callback) -{ - if (deadline <= TimeManagement::the().current_time(clock_id)) - return false; - - // Because timer handlers can execute on any processor and there is - // a race between executing a timer handler and cancel_timer() this - // *must* be a RefPtr. Otherwise, calling cancel_timer() could - // inadvertently cancel another timer that has been created between - // returning from the timer handler and a call to cancel_timer(). - timer->setup(clock_id, deadline, move(callback)); - - SpinlockLocker lock(g_timerqueue_lock); - timer->m_id = 0; // Don't generate a timer id - add_timer_locked(move(timer)); - return true; -} - -TimerId TimerQueue::add_timer(NonnullRefPtr&& timer) -{ - SpinlockLocker lock(g_timerqueue_lock); - - timer->m_id = ++m_timer_id_count; - VERIFY(timer->m_id != 0); // wrapped - auto id = timer->m_id; - add_timer_locked(move(timer)); - return id; -} - -void TimerQueue::add_timer_locked(NonnullRefPtr timer) -{ - Duration timer_expiration = timer->m_expires; - - timer->clear_cancelled(); - timer->clear_callback_finished(); - timer->set_in_use(); - - auto& queue = queue_for_timer(*timer); - if (queue.list.is_empty()) { - queue.list.append(timer.leak_ref()); - queue.next_timer_due = timer_expiration; - } else { - Timer* following_timer = nullptr; - for (auto& t : queue.list) { - if (t.m_expires > timer_expiration) { - following_timer = &t; - break; - } - } - if (following_timer) { - bool next_timer_needs_update = queue.list.first() == following_timer; - queue.list.insert_before(*following_timer, timer.leak_ref()); - if (next_timer_needs_update) - queue.next_timer_due = timer_expiration; - } else { - queue.list.append(timer.leak_ref()); - } - } -} - -bool TimerQueue::cancel_timer(Timer& timer, bool* was_in_use) -{ - bool in_use = timer.is_in_use(); - if (was_in_use) - *was_in_use = in_use; - - // If the timer isn't in use, the cancellation is a no-op. - if (!in_use) { - VERIFY(!timer.m_list_node.is_in_list()); - return false; - } - - bool did_already_run = timer.set_cancelled(); - auto& timer_queue = queue_for_timer(timer); - if (!did_already_run) { - timer.clear_in_use(); - - SpinlockLocker lock(g_timerqueue_lock); - if (timer_queue.list.contains(timer)) { - // The timer has not fired, remove it - VERIFY(timer.ref_count() > 1); - remove_timer_locked(timer_queue, timer); - return true; - } - - // The timer was queued to execute but hasn't had a chance - // to run. In this case, it should still be in m_timers_executing - // and we don't need to spin. It still holds a reference - // that will be dropped when it does get a chance to run, - // but since we called set_cancelled it will only drop its reference - VERIFY(m_timers_executing.contains(timer)); - m_timers_executing.remove(timer); - return true; - } - - // At this point the deferred call is queued and is being executed - // on another processor. We need to wait until it's complete! - while (!timer.is_callback_finished()) - Processor::wait_check(); - - return false; -} - -void TimerQueue::remove_timer_locked(Queue& queue, Timer& timer) -{ - bool was_next_timer = (queue.list.first() == &timer); - queue.list.remove(timer); - auto now = timer.now(false); - if (timer.m_expires > now) - timer.m_remaining = timer.m_expires - now; - - if (was_next_timer) - update_next_timer_due(queue); - // Whenever we remove a timer that was still queued (but hasn't been - // fired) we added a reference to it. So, when removing it from the - // queue we need to drop that reference. - timer.unref(); -} - -void TimerQueue::fire() -{ - SpinlockLocker lock(g_timerqueue_lock); - - auto fire_timers = [&](Queue& queue) { - auto* timer = queue.list.first(); - VERIFY(timer); - VERIFY(queue.next_timer_due == timer->m_expires); - - while (timer && timer->now(true) > timer->m_expires) { - queue.list.remove(*timer); - - m_timers_executing.append(*timer); - - update_next_timer_due(queue); - - lock.unlock(); - - // Defer executing the timer outside of the irq handler - Processor::deferred_call_queue([this, timer]() { - // Check if we were cancelled in between being triggered - // by the timer irq handler and now. If so, just drop - // our reference and don't execute the callback. - if (!timer->set_cancelled()) { - timer->m_callback(); - SpinlockLocker lock(g_timerqueue_lock); - m_timers_executing.remove(*timer); - } - timer->clear_in_use(); - timer->set_callback_finished(); - // Drop the reference we added when queueing the timer - timer->unref(); - }); - - lock.lock(); - timer = queue.list.first(); - } - }; - - if (!m_timer_queue_monotonic.list.is_empty()) - fire_timers(m_timer_queue_monotonic); - if (!m_timer_queue_realtime.list.is_empty()) - fire_timers(m_timer_queue_realtime); -} - -void TimerQueue::update_next_timer_due(Queue& queue) -{ - VERIFY(g_timerqueue_lock.is_locked()); - - if (auto* next_timer = queue.list.first()) - queue.next_timer_due = next_timer->m_expires; - else - queue.next_timer_due = {}; -} - -} diff --git a/Kernel/Time/TimerQueue.h b/Kernel/Time/TimerQueue.h deleted file mode 100644 index 84961d5ba32..00000000000 --- a/Kernel/Time/TimerQueue.h +++ /dev/null @@ -1,126 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include -#include -#include - -namespace Kernel { - -AK_TYPEDEF_DISTINCT_ORDERED_ID(u64, TimerId); - -class Timer final : public AtomicRefCounted { - friend class TimerQueue; - -public: - void setup(clockid_t clock_id, Duration expires, Function&& callback) - { - VERIFY(!is_queued()); - m_clock_id = clock_id; - m_expires = expires; - m_callback = move(callback); - } - - ~Timer() - { - VERIFY(!is_queued()); - } - - Duration remaining() const; - -private: - TimerId m_id; - clockid_t m_clock_id; - Duration m_expires; - Duration m_remaining {}; - Function m_callback; - Atomic m_cancelled { false }; - Atomic m_callback_finished { false }; - Atomic m_in_use { false }; - - bool operator<(Timer const& rhs) const - { - return m_expires < rhs.m_expires; - } - bool operator>(Timer const& rhs) const - { - return m_expires > rhs.m_expires; - } - bool operator==(Timer const& rhs) const - { - return m_id == rhs.m_id; - } - - void clear_cancelled() { return m_cancelled.store(false, AK::memory_order_release); } - bool set_cancelled() { return m_cancelled.exchange(true, AK::memory_order_acq_rel); } - - bool is_in_use() { return m_in_use.load(AK::memory_order_acquire); } - void set_in_use() { m_in_use.store(true, AK::memory_order_release); } - void clear_in_use() { return m_in_use.store(false, AK::memory_order_release); } - - bool is_callback_finished() const { return m_callback_finished.load(AK::memory_order_acquire); } - void clear_callback_finished() { m_callback_finished.store(false, AK::memory_order_release); } - void set_callback_finished() { m_callback_finished.store(true, AK::memory_order_release); } - - Duration now(bool) const; - - bool is_queued() const { return m_list_node.is_in_list(); } - -public: - IntrusiveListNode m_list_node; - using List = IntrusiveList<&Timer::m_list_node>; -}; - -class TimerQueue { - friend class Timer; - -public: - TimerQueue(); - static TimerQueue& the(); - - TimerId add_timer(NonnullRefPtr&&); - bool add_timer_without_id(NonnullRefPtr, clockid_t, Duration const&, Function&&); - bool cancel_timer(Timer& timer, bool* was_in_use = nullptr); - void fire(); - -private: - struct Queue { - Timer::List list; - Duration next_timer_due {}; - }; - void remove_timer_locked(Queue&, Timer&); - void update_next_timer_due(Queue&); - void add_timer_locked(NonnullRefPtr); - - Queue& queue_for_timer(Timer& timer) - { - switch (timer.m_clock_id) { - case CLOCK_MONOTONIC: - case CLOCK_MONOTONIC_COARSE: - case CLOCK_MONOTONIC_RAW: - return m_timer_queue_monotonic; - case CLOCK_REALTIME: - case CLOCK_REALTIME_COARSE: - return m_timer_queue_realtime; - default: - VERIFY_NOT_REACHED(); - } - } - - u64 m_timer_id_count { 0 }; - u64 m_ticks_per_second { 0 }; - Queue m_timer_queue_monotonic; - Queue m_timer_queue_realtime; - Timer::List m_timers_executing; -}; - -} diff --git a/Kernel/UnixTypes.h b/Kernel/UnixTypes.h deleted file mode 100644 index 81e2694bf20..00000000000 --- a/Kernel/UnixTypes.h +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright (c) 2018-2021, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include diff --git a/Kernel/embedmap.sh b/Kernel/embedmap.sh deleted file mode 100644 index 58e33e8a1fa..00000000000 --- a/Kernel/embedmap.sh +++ /dev/null @@ -1,6 +0,0 @@ -#!/bin/sh -tmp=$(mktemp) -(cat kernel.map; printf '%b' '\0') > "$tmp" -OBJCOPY="${OBJCOPY:-objcopy}" -"$OBJCOPY" --update-section .ksyms="$tmp" Kernel -rm -f "$tmp" diff --git a/Kernel/generate-version-file.sh b/Kernel/generate-version-file.sh deleted file mode 100755 index 98d02b7ae54..00000000000 --- a/Kernel/generate-version-file.sh +++ /dev/null @@ -1,37 +0,0 @@ -#!/bin/sh - -set -e - -OUTPUT_FILE=$1 - -if command -v git >/dev/null; then - if git status >/dev/null 2>&1; then - GIT_HASH=$( (git log --pretty=format:'%h' -n 1 | cut -c1-7) || true ) - # There is at least one modified file as reported by git. - if git status --porcelain=v2 | head | grep -Ei '^1' >/dev/null; then - GIT_HASH="${GIT_HASH}-modified" - fi - else - GIT_HASH=unknown - fi -else - GIT_HASH=unknown -fi - - -cat << EOF > "$OUTPUT_FILE" -/* - * Automatically generated by Kernel/generate-version-file.sh - */ - -#pragma once -#include - -namespace Kernel { - -constexpr unsigned SERENITY_MAJOR_REVISION = 1; -constexpr unsigned SERENITY_MINOR_REVISION = 0; -constexpr StringView SERENITY_VERSION = "${GIT_HASH}"sv; - -} -EOF diff --git a/Kernel/kprintf.cpp b/Kernel/kprintf.cpp deleted file mode 100644 index 233a2edd974..00000000000 --- a/Kernel/kprintf.cpp +++ /dev/null @@ -1,153 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#if ARCH(X86_64) -# include -#endif -#include -#include -#include -#include -#include -#include -#include -#include - -namespace Kernel { -extern Atomic g_boot_console; -} - -static bool s_serial_debug_enabled; -// A recursive spinlock allows us to keep writing in the case where a -// page fault happens in the middle of a dbgln(), etc -static RecursiveSpinlock s_log_lock {}; - -void set_serial_debug_enabled(bool desired_state) -{ - s_serial_debug_enabled = desired_state; -} - -bool is_serial_debug_enabled() -{ - return s_serial_debug_enabled; -} - -static void serial_putch(char ch) -{ - if (PCISerialDevice::is_available()) - return PCISerialDevice::the().put_char(ch); - - debug_output(ch); -} - -static void critical_console_out(char ch) -{ - if (s_serial_debug_enabled) - serial_putch(ch); - -#if ARCH(X86_64) - // No need to output things to the real ConsoleDevice as no one is likely - // to read it (because we are in a fatal situation, so only print things and halt) - bochs_debug_output(ch); -#endif - - // We emit chars directly to the string. this is necessary in few cases, - // especially when we want to avoid any memory allocations... - if (GraphicsManagement::is_initialized() && GraphicsManagement::the().console()) { - GraphicsManagement::the().console()->write(ch, true); - } else if (auto* boot_console = g_boot_console.load()) { - boot_console->write(ch, true); - } -} - -static void console_out(char ch) -{ - if (DeviceManagement::the().is_console_device_attached()) { - DeviceManagement::the().console_device().put_char(ch); - } else { - if (s_serial_debug_enabled) - serial_putch(ch); - -#if ARCH(X86_64) - bochs_debug_output(ch); -#endif - } - if (ConsoleManagement::is_initialized()) { - ConsoleManagement::the().debug_tty()->emit_char(ch); - } else if (auto* boot_console = g_boot_console.load()) { - boot_console->write(ch, true); - } -} - -// Declare it, so that the symbol is exported, because libstdc++ uses it. -// However, *only* libstdc++ uses it, and none of the rest of the Kernel. -extern "C" int sprintf(char* buffer, char const* fmt, ...); - -int sprintf(char*, char const*, ...) -{ - VERIFY_NOT_REACHED(); -} - -static inline void internal_dbgputch(char ch) -{ - if (s_serial_debug_enabled) - serial_putch(ch); -#if ARCH(X86_64) - bochs_debug_output(ch); -#endif -} - -extern "C" void dbgputchar(char ch) -{ - internal_dbgputch(ch); -} - -extern "C" void dbgputstr(char const* characters, size_t length) -{ - if (!characters) - return; - SpinlockLocker lock(s_log_lock); - for (size_t i = 0; i < length; ++i) - internal_dbgputch(characters[i]); -} - -void dbgputstr(StringView view) -{ - ::dbgputstr(view.characters_without_null_termination(), view.length()); -} - -extern "C" void kernelputstr(char const* characters, size_t length) -{ - if (!characters) - return; - SpinlockLocker lock(s_log_lock); - for (size_t i = 0; i < length; ++i) - console_out(characters[i]); -} - -extern "C" void kernelcriticalputstr(char const* characters, size_t length) -{ - if (!characters) - return; - SpinlockLocker lock(s_log_lock); - for (size_t i = 0; i < length; ++i) - critical_console_out(characters[i]); -} - -extern "C" void kernelearlyputstr(char const* characters, size_t length) -{ - if (!characters) - return; - // NOTE: We do not lock the log lock here, as this function is called before this or any other processor was initialized, meaning: - // A) The $gs base was not setup yet, so we cannot enter into critical sections, and as a result we cannot use SpinLocks - // B) No other processors may try to print at the same time anyway - for (size_t i = 0; i < length; ++i) - internal_dbgputch(characters[i]); -} diff --git a/Kernel/kstdio.h b/Kernel/kstdio.h deleted file mode 100644 index 03f6d65048e..00000000000 --- a/Kernel/kstdio.h +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include - -extern "C" { -void dbgputstr(char const*, size_t); -void kernelputstr(char const*, size_t); -void kernelcriticalputstr(char const*, size_t); -void dbgputchar(char); -void kernelearlyputstr(char const*, size_t); -void set_serial_debug_enabled(bool desired_state); -bool is_serial_debug_enabled(); -} - -void dbgputstr(StringView view); diff --git a/Kernel/mkmap.sh b/Kernel/mkmap.sh deleted file mode 100644 index 54002594681..00000000000 --- a/Kernel/mkmap.sh +++ /dev/null @@ -1,7 +0,0 @@ -#!/bin/sh -tmp=$(mktemp) -NM="${NM:-nm}" -"$NM" -C -n Kernel | grep -vE \\.Lubsan_data | awk '{ if ($2 != "a") print; }' | uniq > "$tmp" -printf "%08x\n" "$(wc -l "$tmp" | awk '{print $1}')" > kernel.map -cat "$tmp" >> kernel.map -rm -f "$tmp" diff --git a/Meta/Lagom/CMakeLists.txt b/Meta/Lagom/CMakeLists.txt index 74a6cec760f..ffc87e02168 100644 --- a/Meta/Lagom/CMakeLists.txt +++ b/Meta/Lagom/CMakeLists.txt @@ -444,10 +444,8 @@ if (BUILD_LAGOM) Archive Audio Compress - Crypto Cpp - DeviceTree - DNS + Crypto Diff Gemini Gfx @@ -455,14 +453,12 @@ if (BUILD_LAGOM) GLSL GPU HTTP - IMAP ImageDecoderClient IPC JIT JS Line Locale - Manual Markdown PDF Protocol @@ -501,48 +497,18 @@ if (BUILD_LAGOM) add_serenity_subdirectory("Userland/Libraries/Lib${lib}") endforeach() - # GUI - set(LIBGUI_SOURCES - GML/Lexer.cpp - GML/Parser.cpp - GML/SyntaxHighlighter.cpp - Icon.cpp - Model.cpp - ModelIndex.cpp - ) - list(TRANSFORM LIBGUI_SOURCES PREPEND "${SERENITY_PROJECT_ROOT}/Userland/Libraries/LibGUI/") - lagom_lib(LibGUI gui - SOURCES ${LIBGUI_SOURCES} - LIBS LibCore LibGfx LibSyntax) - # FIXME: How about we don't include Kernel/API from random high-level libraries? install(FILES ${SERENITY_PROJECT_ROOT}/Kernel/API/KeyCode.h DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/Kernel/API") target_link_libraries(LibLocale PRIVATE LibTimeZone) - add_serenity_subdirectory(Userland/Shell) - if (NOT ENABLE_FUZZERS AND NOT ENABLE_COMPILER_EXPLORER_BUILD AND NOT ANDROID AND NOT IOS) - # Lagom Services - add_serenity_subdirectory(Userland/Services) - # Lagom Utilities lagom_utility(abench SOURCES ../../Userland/Utilities/abench.cpp LIBS LibMain LibFileSystem LibAudio) lagom_utility(aconv SOURCES ../../Userland/Utilities/aconv.cpp LIBS LibMain LibFileSystem LibAudio) - if (NOT EMSCRIPTEN) - lagom_utility(adjtime SOURCES ../../Userland/Utilities/adjtime.cpp LIBS LibMain) - endif() - lagom_utility(animation SOURCES ../../Userland/Utilities/animation.cpp LIBS LibGfx LibMain) - lagom_utility(base64 SOURCES ../../Userland/Utilities/base64.cpp LIBS LibMain) - if (NOT EMSCRIPTEN) - lagom_utility(disasm SOURCES ../../Userland/Utilities/disasm.cpp LIBS LibELF LibX86 LibMain) - lagom_utility(file SOURCES ../../Userland/Utilities/file.cpp LIBS LibAudio LibArchive LibCompress LibELF LibGfx LibIPC LibMain) - endif() - - lagom_utility(gml-format SOURCES ../../Userland/Utilities/gml-format.cpp LIBS LibGUI LibMain) lagom_utility(gzip SOURCES ../../Userland/Utilities/gzip.cpp LIBS LibCompress LibMain) # Work around bug in JetBrains distributed CMake 3.27.2 where this causes infinite recursion in @@ -577,12 +543,6 @@ if (BUILD_LAGOM) endif() lagom_utility(lzcat SOURCES ../../Userland/Utilities/lzcat.cpp LIBS LibCompress LibMain) - lagom_utility(markdown-check SOURCES ../../Userland/Utilities/markdown-check.cpp LIBS LibFileSystem LibMarkdown LibMain LibManual LibURL) - lagom_utility(mkfs.fat SOURCES ../../Userland/Utilities/mkfs.fat.cpp LIBS LibFileSystem LibMain) - - if (NOT EMSCRIPTEN) - lagom_utility(ntpquery SOURCES ../../Userland/Utilities/ntpquery.cpp LIBS LibMain) - endif() lagom_utility(pdf SOURCES ../../Userland/Utilities/pdf.cpp LIBS LibGfx LibPDF LibMain) lagom_utility(sql SOURCES ../../Userland/Utilities/sql.cpp LIBS LibFileSystem LibIPC LibLine LibMain LibSQL) @@ -609,7 +569,6 @@ if (BUILD_LAGOM) lagom_utility(wasm SOURCES ../../Userland/Utilities/wasm.cpp LIBS LibFileSystem LibWasm LibLine LibMain LibJS) lagom_utility(xml SOURCES ../../Userland/Utilities/xml.cpp LIBS LibFileSystem LibMain LibXML LibURL) lagom_utility(xzcat SOURCES ../../Userland/Utilities/xzcat.cpp LIBS LibCompress LibMain) - lagom_utility(fdtdump SOURCES ../../Userland/Utilities/fdtdump.cpp LIBS LibDeviceTree LibMain) enable_testing() # LibTest @@ -637,7 +596,6 @@ if (BUILD_LAGOM) LibCompress LibGL LibGfx - LibIMAP LibLocale LibMarkdown LibPDF @@ -654,9 +612,6 @@ if (BUILD_LAGOM) list(APPEND TEST_DIRECTORIES LibWeb) list(APPEND TEST_DIRECTORIES LibWebView) endif() - if (LINUX) - list(APPEND TEST_DIRECTORIES LibELF) - endif() if (ENABLE_CLANG_PLUGINS AND CMAKE_CXX_COMPILER_ID MATCHES "Clang$") list(APPEND TEST_DIRECTORIES ClangPlugins) endif() @@ -712,17 +667,6 @@ if (BUILD_LAGOM) lagom_test(../../Tests/LibJS/test-invalid-unicode-js.cpp LIBS LibJS) lagom_test(../../Tests/LibJS/test-value-js.cpp LIBS LibJS) - # Spreadsheet - add_executable(test-spreadsheet - ../../Tests/Spreadsheet/test-spreadsheet.cpp - ../../Userland/Libraries/LibTest/JavaScriptTestRunnerMain.cpp) - target_link_libraries(test-spreadsheet AK LibCore LibFileSystem LibTest LibJS) - add_test( - NAME Spreadsheet - COMMAND test-spreadsheet --show-progress=false - ) - set_tests_properties(Spreadsheet PROPERTIES ENVIRONMENT SERENITY_SOURCE_DIR=${SERENITY_PROJECT_ROOT}) - # test-wasm add_executable(test-wasm ../../Tests/LibWasm/test-wasm.cpp @@ -737,23 +681,6 @@ if (BUILD_LAGOM) ENVIRONMENT SERENITY_SOURCE_DIR=${SERENITY_PROJECT_ROOT} ) - # Tests that are not LibTest based - # Shell - file(GLOB SHELL_TESTS CONFIGURE_DEPENDS "../../Userland/Shell/Tests/*.sh") - foreach(TEST_PATH ${SHELL_TESTS}) - get_filename_component(TEST_NAME ${TEST_PATH} NAME_WE) - add_test( - NAME "Shell-${TEST_NAME}" - COMMAND Shell --skip-shellrc "${TEST_PATH}" - WORKING_DIRECTORY ${SERENITY_PROJECT_ROOT}/Userland/Shell/Tests - ) - set_tests_properties("Shell-${TEST_NAME}" PROPERTIES - TIMEOUT 10 - FAIL_REGULAR_EXPRESSION "FAIL" - PASS_REGULAR_EXPRESSION "PASS" - ) - endforeach() - # FIXME: When we are using CMake >= 3.21, the library installations can be replaced with RUNTIME_DEPENDENCIES. # https://cmake.org/cmake/help/latest/command/install.html include(get_linked_lagom_libraries.cmake) diff --git a/Meta/Screenshots/screenshot-000ccc0.png b/Meta/Screenshots/screenshot-000ccc0.png deleted file mode 100644 index 7f1b359141b..00000000000 Binary files a/Meta/Screenshots/screenshot-000ccc0.png and /dev/null differ diff --git a/Meta/Screenshots/screenshot-0f85753.png b/Meta/Screenshots/screenshot-0f85753.png deleted file mode 100644 index a289bb3f645..00000000000 Binary files a/Meta/Screenshots/screenshot-0f85753.png and /dev/null differ diff --git a/Meta/Screenshots/screenshot-1133aca.png b/Meta/Screenshots/screenshot-1133aca.png deleted file mode 100644 index 91c41f1015e..00000000000 Binary files a/Meta/Screenshots/screenshot-1133aca.png and /dev/null differ diff --git a/Meta/Screenshots/screenshot-191112e.png b/Meta/Screenshots/screenshot-191112e.png deleted file mode 100644 index 391c92be04e..00000000000 Binary files a/Meta/Screenshots/screenshot-191112e.png and /dev/null differ diff --git a/Meta/Screenshots/screenshot-23263aa.png b/Meta/Screenshots/screenshot-23263aa.png deleted file mode 100644 index 44ba8948a53..00000000000 Binary files a/Meta/Screenshots/screenshot-23263aa.png and /dev/null differ diff --git a/Meta/Screenshots/screenshot-8ea4375.png b/Meta/Screenshots/screenshot-8ea4375.png deleted file mode 100644 index 705048c64ed..00000000000 Binary files a/Meta/Screenshots/screenshot-8ea4375.png and /dev/null differ diff --git a/Meta/Screenshots/screenshot-9145a72.png b/Meta/Screenshots/screenshot-9145a72.png deleted file mode 100644 index d1c01256c67..00000000000 Binary files a/Meta/Screenshots/screenshot-9145a72.png and /dev/null differ diff --git a/Meta/Screenshots/screenshot-b36968c.png b/Meta/Screenshots/screenshot-b36968c.png deleted file mode 100644 index 56858e246ee..00000000000 Binary files a/Meta/Screenshots/screenshot-b36968c.png and /dev/null differ diff --git a/Meta/Screenshots/screenshot-b5521e1.png b/Meta/Screenshots/screenshot-b5521e1.png deleted file mode 100644 index 1920763f426..00000000000 Binary files a/Meta/Screenshots/screenshot-b5521e1.png and /dev/null differ diff --git a/Meta/Screenshots/screenshot-c03b788.png b/Meta/Screenshots/screenshot-c03b788.png deleted file mode 100644 index 282c25ae27a..00000000000 Binary files a/Meta/Screenshots/screenshot-c03b788.png and /dev/null differ diff --git a/Meta/Screenshots/screenshot-cdb82f6.png b/Meta/Screenshots/screenshot-cdb82f6.png deleted file mode 100644 index aa0d0f23b34..00000000000 Binary files a/Meta/Screenshots/screenshot-cdb82f6.png and /dev/null differ diff --git a/Meta/Screenshots/screenshot-d727005.png b/Meta/Screenshots/screenshot-d727005.png deleted file mode 100644 index b1907f210a9..00000000000 Binary files a/Meta/Screenshots/screenshot-d727005.png and /dev/null differ diff --git a/Meta/Screenshots/screenshot-ef9fbef.png b/Meta/Screenshots/screenshot-ef9fbef.png deleted file mode 100644 index 8d86ea38036..00000000000 Binary files a/Meta/Screenshots/screenshot-ef9fbef.png and /dev/null differ diff --git a/Meta/Screenshots/screenshot-f8fc892.png b/Meta/Screenshots/screenshot-f8fc892.png deleted file mode 100644 index a14c21b7aee..00000000000 Binary files a/Meta/Screenshots/screenshot-f8fc892.png and /dev/null differ diff --git a/Meta/Websites/man.serenityos.org/add-anchors.lua b/Meta/Websites/man.serenityos.org/add-anchors.lua deleted file mode 100644 index 18e7f8a0466..00000000000 --- a/Meta/Websites/man.serenityos.org/add-anchors.lua +++ /dev/null @@ -1,14 +0,0 @@ -function Header(header) - local level = header.level - local identifier = header.identifier - local anchor = pandoc.RawInline('html', '#') - - -- Create a list of inline elements containing the anchor and header content - local new_content = pandoc.List({anchor}) - for _, elem in ipairs(header.content) do - new_content:insert(elem) - end - - return pandoc.Header(level, new_content, identifier) - end - \ No newline at end of file diff --git a/Meta/Websites/man.serenityos.org/banner-preamble.inc b/Meta/Websites/man.serenityos.org/banner-preamble.inc deleted file mode 100644 index c727b209ba2..00000000000 --- a/Meta/Websites/man.serenityos.org/banner-preamble.inc +++ /dev/null @@ -1 +0,0 @@ - diff --git a/Meta/Websites/man.serenityos.org/banner.png b/Meta/Websites/man.serenityos.org/banner.png deleted file mode 100644 index 872cf0eee59..00000000000 Binary files a/Meta/Websites/man.serenityos.org/banner.png and /dev/null differ diff --git a/Meta/Websites/man.serenityos.org/cant-run-application.md b/Meta/Websites/man.serenityos.org/cant-run-application.md deleted file mode 100644 index f54d80aae8b..00000000000 --- a/Meta/Websites/man.serenityos.org/cant-run-application.md +++ /dev/null @@ -1,5 +0,0 @@ -# Sorry :^( - -This is a link to open the application directly, but that only works in SerenityOS. - -[Go back to main page](index.html) diff --git a/Meta/Websites/man.serenityos.org/index.md b/Meta/Websites/man.serenityos.org/index.md deleted file mode 100644 index d554b2576d6..00000000000 --- a/Meta/Websites/man.serenityos.org/index.md +++ /dev/null @@ -1,8 +0,0 @@ -- [Section 1: User Programs (applets, applications, utilities, etc.)](man1/index.html) -- [Section 2: System Calls (getuid, mount, pledge, sendfd, etc.)](man2/index.html) -- [Section 3: Library Functions (basename, isatty, POSIX functions, etc.)](man3/index.html) -- [Section 4: Special Files (audio, mem, etc.)](man4/index.html) -- [Section 5: File Formats (getopt convention, Shell, SystemServer)](man5/index.html) -- [Section 6: Games (2048, Chess, FlappyBug, etc.)](man6/index.html) -- [Section 7: Miscellanea (mitigations, sys filesystem, SystemServer, etc.)](man7/index.html) -- [Section 8: Sysadmin Tools (dmesg, pls, sysctl, useradd, etc.)](man8/index.html) diff --git a/Meta/Websites/serenityos.org/banner.png b/Meta/Websites/serenityos.org/banner.png deleted file mode 100644 index 872cf0eee59..00000000000 Binary files a/Meta/Websites/serenityos.org/banner.png and /dev/null differ diff --git a/Meta/Websites/serenityos.org/banner2.png b/Meta/Websites/serenityos.org/banner2.png deleted file mode 100644 index 9f49c664fe7..00000000000 Binary files a/Meta/Websites/serenityos.org/banner2.png and /dev/null differ diff --git a/Meta/Websites/serenityos.org/bounty/index.html b/Meta/Websites/serenityos.org/bounty/index.html deleted file mode 100644 index 27b102ba636..00000000000 --- a/Meta/Websites/serenityos.org/bounty/index.html +++ /dev/null @@ -1,66 +0,0 @@ - - - -SerenityOS bug bounty program - - - -

SerenityOS bug bounty program :^)

-

- Like any respectable software project, SerenityOS - also runs a bug bounty program. - I don't have a huge budget, but I want to reward good honest work. -

-

- I will pay $50 USD for exploitable bugs in these categories: -

-
    -
  • Remote code execution.
  • -
  • Local privilege escalation.
  • -
  • Arbitrary code execution in the Browser when loading a remote web page.
  • -
-

Rules

-
    -
  • No rewards for bugs you caused yourself.
  • -
  • The PoC exploit needs to work against the master branch at the time of claim.
  • -
  • Max 3 bounties per person.
  • -
  • No duplicates. If a bug is already reported, only the earliest reporter may claim the reward. This includes bugs found by continuous fuzzing systems.
  • -
  • No rewards for bugs that require unlikely user interaction or social engineering.
  • -
  • Remote bugs must be exploitable with an unmodified "default setup" of SerenityOS. Bugs in programs that are not started by default don't qualify.
  • -
  • The PoC exploit needs to work on a QEMU-emulated CPU that supports SMAP, SMEP, UMIP, NX, WP, and TSD natively.
  • -
  • SerenityOS always runs with assertions enabled, so you'll need to find a way around them.
  • -
-

- To claim a reward, get in touch with me either on the SerenityOS Discord (awesomekling#1985) or via kling@serenityos.org. (And even if you are not interested in the reward, I'd still like to hear about any exploits!) -

-

Past exploits:

-
    -
  • 2021-03-04: Iliad used a VLA stack overflow in the TCP implementation to smash a nearby kernel stack and become root. (Writeup and exploit)
  • -
  • 2021-02-18: cees-elzinga combined a ptrace race condition with an ASLR bypass to modify /etc/passwd and become root. (Bug report and exploit)
  • -
  • 2021-02-11: vakzz wrote the first-ever full chain exploit, stringing together a LibJS bug and a kernel bug to create a web page that got root access when viewed in our browser. (Writeup and exploit)
  • -
  • 2020-12-22: ALLES! CTF found a kernel LPE due to missing EFLAGS validation in ptrace(). (Writeup and exploit)
  • -
  • 2020-12-20: yyyyyyy found a kernel LPE due to a race condition between execve() and ptrace(). (Writeup and exploit)
  • -
  • 2020-03-30: \0 claimed $5 for reporting that the documentation neglects to mention that the default anon user can use su to become root by default. Donated to "Kiwis for Kiwi" charity as per \0's request. Fixed with this commit.
  • -
  • 2019-12-30: Fire30 found a kernel LPE due to bad userspace pointer validation. (Writeup and exploit)
  • -
  • 2019-12-29: braindead found a kernel LPE due to a TOCTOU bug in clock_nanosleep(). (Writeup and exploit)
  • - -
- - diff --git a/Meta/Websites/serenityos.org/bounty/kiwis4kiwi.png b/Meta/Websites/serenityos.org/bounty/kiwis4kiwi.png deleted file mode 100644 index b57dd8bd727..00000000000 Binary files a/Meta/Websites/serenityos.org/bounty/kiwis4kiwi.png and /dev/null differ diff --git a/Meta/Websites/serenityos.org/buggie.png b/Meta/Websites/serenityos.org/buggie.png deleted file mode 100644 index 1602318a470..00000000000 Binary files a/Meta/Websites/serenityos.org/buggie.png and /dev/null differ diff --git a/Meta/Websites/serenityos.org/faq/index.html b/Meta/Websites/serenityos.org/faq/index.html deleted file mode 100644 index 30801480f40..00000000000 --- a/Meta/Websites/serenityos.org/faq/index.html +++ /dev/null @@ -1,9 +0,0 @@ - - - - SerenityOS FAQ - - - SerenityOS FAQ - - diff --git a/Meta/Websites/serenityos.org/favicon.ico b/Meta/Websites/serenityos.org/favicon.ico deleted file mode 100644 index 0f7ce747ab2..00000000000 Binary files a/Meta/Websites/serenityos.org/favicon.ico and /dev/null differ diff --git a/Meta/Websites/serenityos.org/github-sponsors/index.html b/Meta/Websites/serenityos.org/github-sponsors/index.html deleted file mode 100644 index 3b496816db8..00000000000 --- a/Meta/Websites/serenityos.org/github-sponsors/index.html +++ /dev/null @@ -1,38 +0,0 @@ - - - Sponsor SerenityOS on GitHub! - - - -
-

Sponsor SerenityOS on GitHub!

-
-
-

Hello friends!

-

As of October 19 2019, I am in the GitHub Sponsors program!

-

This means you can support my work with a monthly donation via their platform.

-

There are six tiers: $1, $5, $10, $20, $50 and $100.

-

Sponsorships are (optionally) visible on your GitHub user card.

-

I will remain on Patreon as well, with the same tiers.

-

Thank you for checking it out :^)

-

Andreas - ( - GitHub | - YouTube | - Twitter | - Patreon | - PayPal ) -

-
- - diff --git a/Meta/Websites/serenityos.org/github-sponsors/mona.png b/Meta/Websites/serenityos.org/github-sponsors/mona.png deleted file mode 100644 index 4cb161288a5..00000000000 Binary files a/Meta/Websites/serenityos.org/github-sponsors/mona.png and /dev/null differ diff --git a/Meta/Websites/serenityos.org/happy/1st/2018-10-10.png b/Meta/Websites/serenityos.org/happy/1st/2018-10-10.png deleted file mode 100644 index 4cbf8a24a00..00000000000 Binary files a/Meta/Websites/serenityos.org/happy/1st/2018-10-10.png and /dev/null differ diff --git a/Meta/Websites/serenityos.org/happy/1st/2018-11-08.png b/Meta/Websites/serenityos.org/happy/1st/2018-11-08.png deleted file mode 100644 index c6c6ead564d..00000000000 Binary files a/Meta/Websites/serenityos.org/happy/1st/2018-11-08.png and /dev/null differ diff --git a/Meta/Websites/serenityos.org/happy/1st/2019-01-09.png b/Meta/Websites/serenityos.org/happy/1st/2019-01-09.png deleted file mode 100644 index c3a0ba1055a..00000000000 Binary files a/Meta/Websites/serenityos.org/happy/1st/2019-01-09.png and /dev/null differ diff --git a/Meta/Websites/serenityos.org/happy/1st/2019-01-10.png b/Meta/Websites/serenityos.org/happy/1st/2019-01-10.png deleted file mode 100644 index e592275868e..00000000000 Binary files a/Meta/Websites/serenityos.org/happy/1st/2019-01-10.png and /dev/null differ diff --git a/Meta/Websites/serenityos.org/happy/1st/2019-01-21.png b/Meta/Websites/serenityos.org/happy/1st/2019-01-21.png deleted file mode 100644 index e1eca73e3aa..00000000000 Binary files a/Meta/Websites/serenityos.org/happy/1st/2019-01-21.png and /dev/null differ diff --git a/Meta/Websites/serenityos.org/happy/1st/2019-01-29.png b/Meta/Websites/serenityos.org/happy/1st/2019-01-29.png deleted file mode 100644 index 987f4307f9e..00000000000 Binary files a/Meta/Websites/serenityos.org/happy/1st/2019-01-29.png and /dev/null differ diff --git a/Meta/Websites/serenityos.org/happy/1st/2019-02-03.png b/Meta/Websites/serenityos.org/happy/1st/2019-02-03.png deleted file mode 100644 index 23d948b1c57..00000000000 Binary files a/Meta/Websites/serenityos.org/happy/1st/2019-02-03.png and /dev/null differ diff --git a/Meta/Websites/serenityos.org/happy/1st/2019-02-08.png b/Meta/Websites/serenityos.org/happy/1st/2019-02-08.png deleted file mode 100644 index f3c7099faec..00000000000 Binary files a/Meta/Websites/serenityos.org/happy/1st/2019-02-08.png and /dev/null differ diff --git a/Meta/Websites/serenityos.org/happy/1st/2019-02-11.png b/Meta/Websites/serenityos.org/happy/1st/2019-02-11.png deleted file mode 100644 index 0627c524d42..00000000000 Binary files a/Meta/Websites/serenityos.org/happy/1st/2019-02-11.png and /dev/null differ diff --git a/Meta/Websites/serenityos.org/happy/1st/2019-02-28.png b/Meta/Websites/serenityos.org/happy/1st/2019-02-28.png deleted file mode 100644 index 06dcc3f2837..00000000000 Binary files a/Meta/Websites/serenityos.org/happy/1st/2019-02-28.png and /dev/null differ diff --git a/Meta/Websites/serenityos.org/happy/1st/2019-03-04.png b/Meta/Websites/serenityos.org/happy/1st/2019-03-04.png deleted file mode 100644 index 5bd9b8f89c3..00000000000 Binary files a/Meta/Websites/serenityos.org/happy/1st/2019-03-04.png and /dev/null differ diff --git a/Meta/Websites/serenityos.org/happy/1st/2019-03-07.png b/Meta/Websites/serenityos.org/happy/1st/2019-03-07.png deleted file mode 100644 index 8ad0fa76a61..00000000000 Binary files a/Meta/Websites/serenityos.org/happy/1st/2019-03-07.png and /dev/null differ diff --git a/Meta/Websites/serenityos.org/happy/1st/2019-03-12.png b/Meta/Websites/serenityos.org/happy/1st/2019-03-12.png deleted file mode 100644 index 404b4a5a143..00000000000 Binary files a/Meta/Websites/serenityos.org/happy/1st/2019-03-12.png and /dev/null differ diff --git a/Meta/Websites/serenityos.org/happy/1st/2019-03-15.png b/Meta/Websites/serenityos.org/happy/1st/2019-03-15.png deleted file mode 100644 index fd670ca4295..00000000000 Binary files a/Meta/Websites/serenityos.org/happy/1st/2019-03-15.png and /dev/null differ diff --git a/Meta/Websites/serenityos.org/happy/1st/2019-03-29.png b/Meta/Websites/serenityos.org/happy/1st/2019-03-29.png deleted file mode 100644 index 41f31c52a23..00000000000 Binary files a/Meta/Websites/serenityos.org/happy/1st/2019-03-29.png and /dev/null differ diff --git a/Meta/Websites/serenityos.org/happy/1st/2019-04-11.png b/Meta/Websites/serenityos.org/happy/1st/2019-04-11.png deleted file mode 100644 index 9b8e9a3cf27..00000000000 Binary files a/Meta/Websites/serenityos.org/happy/1st/2019-04-11.png and /dev/null differ diff --git a/Meta/Websites/serenityos.org/happy/1st/2019-04-14.png b/Meta/Websites/serenityos.org/happy/1st/2019-04-14.png deleted file mode 100644 index 5b169915939..00000000000 Binary files a/Meta/Websites/serenityos.org/happy/1st/2019-04-14.png and /dev/null differ diff --git a/Meta/Websites/serenityos.org/happy/1st/2019-04-20.png b/Meta/Websites/serenityos.org/happy/1st/2019-04-20.png deleted file mode 100644 index 65e6a9d89b6..00000000000 Binary files a/Meta/Websites/serenityos.org/happy/1st/2019-04-20.png and /dev/null differ diff --git a/Meta/Websites/serenityos.org/happy/1st/2019-04-22.png b/Meta/Websites/serenityos.org/happy/1st/2019-04-22.png deleted file mode 100644 index 5da859b3c7c..00000000000 Binary files a/Meta/Websites/serenityos.org/happy/1st/2019-04-22.png and /dev/null differ diff --git a/Meta/Websites/serenityos.org/happy/1st/2019-04-29.png b/Meta/Websites/serenityos.org/happy/1st/2019-04-29.png deleted file mode 100644 index 04e2ecff02d..00000000000 Binary files a/Meta/Websites/serenityos.org/happy/1st/2019-04-29.png and /dev/null differ diff --git a/Meta/Websites/serenityos.org/happy/1st/2019-06-14.png b/Meta/Websites/serenityos.org/happy/1st/2019-06-14.png deleted file mode 100644 index 89f6f88a8c2..00000000000 Binary files a/Meta/Websites/serenityos.org/happy/1st/2019-06-14.png and /dev/null differ diff --git a/Meta/Websites/serenityos.org/happy/1st/2019-07-14.png b/Meta/Websites/serenityos.org/happy/1st/2019-07-14.png deleted file mode 100644 index e362743a568..00000000000 Binary files a/Meta/Websites/serenityos.org/happy/1st/2019-07-14.png and /dev/null differ diff --git a/Meta/Websites/serenityos.org/happy/1st/2019-08-27.png b/Meta/Websites/serenityos.org/happy/1st/2019-08-27.png deleted file mode 100644 index 574119dd581..00000000000 Binary files a/Meta/Websites/serenityos.org/happy/1st/2019-08-27.png and /dev/null differ diff --git a/Meta/Websites/serenityos.org/happy/1st/2019-09-09.png b/Meta/Websites/serenityos.org/happy/1st/2019-09-09.png deleted file mode 100644 index 0a4aed5c3f1..00000000000 Binary files a/Meta/Websites/serenityos.org/happy/1st/2019-09-09.png and /dev/null differ diff --git a/Meta/Websites/serenityos.org/happy/1st/2019-09-29.png b/Meta/Websites/serenityos.org/happy/1st/2019-09-29.png deleted file mode 100644 index b4ed7a724bc..00000000000 Binary files a/Meta/Websites/serenityos.org/happy/1st/2019-09-29.png and /dev/null differ diff --git a/Meta/Websites/serenityos.org/happy/1st/2019-10-06.png b/Meta/Websites/serenityos.org/happy/1st/2019-10-06.png deleted file mode 100644 index 7e95fbf4f53..00000000000 Binary files a/Meta/Websites/serenityos.org/happy/1st/2019-10-06.png and /dev/null differ diff --git a/Meta/Websites/serenityos.org/happy/1st/2019-10-10.png b/Meta/Websites/serenityos.org/happy/1st/2019-10-10.png deleted file mode 100644 index 1984023c50b..00000000000 Binary files a/Meta/Websites/serenityos.org/happy/1st/2019-10-10.png and /dev/null differ diff --git a/Meta/Websites/serenityos.org/happy/1st/index.html b/Meta/Websites/serenityos.org/happy/1st/index.html deleted file mode 100644 index 6e6db2b6243..00000000000 --- a/Meta/Websites/serenityos.org/happy/1st/index.html +++ /dev/null @@ -1,217 +0,0 @@ - -SerenityOS: From zero to HTML in a year - -

SerenityOS: From zero to HTML in a year

-

- Hello friends! -

-

- The Serenity operating system turns 1 year old today. I'm counting - from the first commit in the - git repository, - on October 10, 2018. - Parts of the code had been around for a while before that, so this - first commit was really about putting everything I was tinkering - with into a shared repo. -

-

Anyways... we have to start somewhere!

-

- NOTE: This web page is very light on HTML/CSS features so that it - can be rendered by Serenity's own Browser! -

-

- In the beginning there were four components: -

-
    -
  • The AK utility library
  • -
  • An ELF executable loader
  • -
  • An ext2 filesystem parser
  • -
  • A simple GUI toolkit on top of SDL
  • -
-

- These were just some little C++ projects I had been tinkering with - after installing Slackware 11 on an old PC to do some hacking. - None of them were very mature, but it was fun, and I was in a place - where I needed to do some programming to find myself again. -

-

- Here's how the ext2 filesystem parser looked like when running: -

-
-    ::>cd /home/andreas
-    ::>ls -l
-    [VFS] ls ///home/andreas -> ext2fs 01:00000017
-    01:00000017 drwxr-xr-x        1024 2018-10-08 13:47:04 ./
-    01:00000016 drwxr-xr-x        1024 2018-10-02 23:43:49 ../
-    01:00000018 -rw-r--r--          18 2018-10-02 23:44:09 file1
-    01:00000019 -rw-r--r--          18 2018-10-02 23:44:14 file2
-    ::>
-    
-
-

2018-10-10: The first ever "screenshot" of what became Serenity:

- -
-

- I imported a little x86 kernel I had been working on earlier in - the year. It was a piece of crap, but it was a place to start. - I originally had some weird idea that kernels should be written - in C, so I had tried to write in C, but I thankfully realized very - quickly that I was being silly. :^) -

-
-

2018-11-08: Serenity running inside a virtual machine:

- -
-

- I kept working on the GUI toolkit on my Linux system, while also - developing the standalone system. This is how far I got with the - GUI while it was still sitting on top of SDL on Linux: -

-
-

2019-01-09: The "Widgets" test program in an SDL framebuffer:

- -
-

- I then decided it was time to get this running on top of Serenity's - own kernel. A day later, I saw this running in a VM for the first time. - It was the most beautiful thing I had ever seen. -

-
-

2019-01-10: Serenity booting into a GUI for the first time:

- -
-

- I started working on a userspace API for doing GUI stuff. The original - version was a monstrosity with the window server running in the kernel - and processes making syscalls to create windows, etc. -

-
-

2019-01-21: Seeing how many "guitest" processes I can spawn:

- -
-
-

2019-02-03: Added FontEditor and Clock, ported GNU bc:

- -
- -
-

2019-02-08: Wallpaper support, a "top" program, and more:

- -
- -
-

2019-02-11: First implementation of menus! And a very early FileManager:

- -
- -
-

2019-02-28: Early version of ProcessManager (today called SystemMonitor):

- -
- -

- Somewhere around here I figured it would be cool to add networking support, - so I started building a network stack. -

- -
-

2019-03-12: My host machine is pinging me!

- -
- -
-

2019-03-15: The first thing I did when TCP kinda worked was build an IRC client:

- -
- -
-

2019-03-29: Using FileManager to drive development of tree and icon views:

- -
- -
-

2019-04-11: First screenshot of VisualBuilder, a Visual Basic inspired GUI design tool:

- -
- -
-

2019-04-20: I made a Snake game. You gotta have snake!

- -
- -
-

2019-04-22: First semi-successful GCC port. It can only compile small C programs:

- -
- -
-

2019-06-14: Started building PaintBrush, a simple painting application:

- -
- -
-

2019-07-14: After the system got basic sound support, I built Piano, a desktop synthesizer:

- -
- -
-

2019-08-27: New menu look inspired by Microsoft circa 2002:

- -
- -
-

2019-09-09: Ladies and gentlemen, we've got DOOM:

- -
- -
-

2019-09-29: Working on basic CSS support in LibHTML:

- -
- -
-

2019-10-10 Viewing this webpage in Serenity's Browser!

- -
-

- And with that, this little screenshot tour of the first year of Serenity is over. -

-

- If you would like to see more, I've also been making regular monthly update videos - throughout the year: -

- -

- To all the boys and girls who have helped out in the last year, with code, bug reports, - docs, commenting on videos, e-mailing, hanging out on IRC, retweeting, telling your friends, etc, - thank you! I'm so grateful for all the love this project has been getting. -

-

- And also, a huge thank you! to everyone who has supported me via - Patreon - and other donation methods! It's my dream to turn this into a full time show some day, - and you're a huge part of keeping that dream alive. :) -

-

- Let's see what we can do in year 2! -

-

- Andreas Kling, 2019-10-10 -

-

- GitHub | - YouTube | - Twitter | - Patreon | - PayPal -

- - diff --git a/Meta/Websites/serenityos.org/happy/1st/thumb.2018-10-10.png b/Meta/Websites/serenityos.org/happy/1st/thumb.2018-10-10.png deleted file mode 100644 index 6627c243aca..00000000000 Binary files a/Meta/Websites/serenityos.org/happy/1st/thumb.2018-10-10.png and /dev/null differ diff --git a/Meta/Websites/serenityos.org/happy/1st/thumb.2018-11-08.png b/Meta/Websites/serenityos.org/happy/1st/thumb.2018-11-08.png deleted file mode 100644 index 3a8ad5f9b7d..00000000000 Binary files a/Meta/Websites/serenityos.org/happy/1st/thumb.2018-11-08.png and /dev/null differ diff --git a/Meta/Websites/serenityos.org/happy/1st/thumb.2019-01-09.png b/Meta/Websites/serenityos.org/happy/1st/thumb.2019-01-09.png deleted file mode 100644 index 5001649b57c..00000000000 Binary files a/Meta/Websites/serenityos.org/happy/1st/thumb.2019-01-09.png and /dev/null differ diff --git a/Meta/Websites/serenityos.org/happy/1st/thumb.2019-01-10.png b/Meta/Websites/serenityos.org/happy/1st/thumb.2019-01-10.png deleted file mode 100644 index bb6605432a4..00000000000 Binary files a/Meta/Websites/serenityos.org/happy/1st/thumb.2019-01-10.png and /dev/null differ diff --git a/Meta/Websites/serenityos.org/happy/1st/thumb.2019-01-21.png b/Meta/Websites/serenityos.org/happy/1st/thumb.2019-01-21.png deleted file mode 100644 index 572f6e6b3c3..00000000000 Binary files a/Meta/Websites/serenityos.org/happy/1st/thumb.2019-01-21.png and /dev/null differ diff --git a/Meta/Websites/serenityos.org/happy/1st/thumb.2019-01-29.png b/Meta/Websites/serenityos.org/happy/1st/thumb.2019-01-29.png deleted file mode 100644 index 6e6b9eba8f3..00000000000 Binary files a/Meta/Websites/serenityos.org/happy/1st/thumb.2019-01-29.png and /dev/null differ diff --git a/Meta/Websites/serenityos.org/happy/1st/thumb.2019-02-03.png b/Meta/Websites/serenityos.org/happy/1st/thumb.2019-02-03.png deleted file mode 100644 index 3d71658b7f3..00000000000 Binary files a/Meta/Websites/serenityos.org/happy/1st/thumb.2019-02-03.png and /dev/null differ diff --git a/Meta/Websites/serenityos.org/happy/1st/thumb.2019-02-08.png b/Meta/Websites/serenityos.org/happy/1st/thumb.2019-02-08.png deleted file mode 100644 index abb08826f4c..00000000000 Binary files a/Meta/Websites/serenityos.org/happy/1st/thumb.2019-02-08.png and /dev/null differ diff --git a/Meta/Websites/serenityos.org/happy/1st/thumb.2019-02-11.png b/Meta/Websites/serenityos.org/happy/1st/thumb.2019-02-11.png deleted file mode 100644 index 64ee82d65d2..00000000000 Binary files a/Meta/Websites/serenityos.org/happy/1st/thumb.2019-02-11.png and /dev/null differ diff --git a/Meta/Websites/serenityos.org/happy/1st/thumb.2019-02-28.png b/Meta/Websites/serenityos.org/happy/1st/thumb.2019-02-28.png deleted file mode 100644 index ed81ed34f95..00000000000 Binary files a/Meta/Websites/serenityos.org/happy/1st/thumb.2019-02-28.png and /dev/null differ diff --git a/Meta/Websites/serenityos.org/happy/1st/thumb.2019-03-04.png b/Meta/Websites/serenityos.org/happy/1st/thumb.2019-03-04.png deleted file mode 100644 index 47f9ec4756c..00000000000 Binary files a/Meta/Websites/serenityos.org/happy/1st/thumb.2019-03-04.png and /dev/null differ diff --git a/Meta/Websites/serenityos.org/happy/1st/thumb.2019-03-07.png b/Meta/Websites/serenityos.org/happy/1st/thumb.2019-03-07.png deleted file mode 100644 index 65a4b895869..00000000000 Binary files a/Meta/Websites/serenityos.org/happy/1st/thumb.2019-03-07.png and /dev/null differ diff --git a/Meta/Websites/serenityos.org/happy/1st/thumb.2019-03-12.png b/Meta/Websites/serenityos.org/happy/1st/thumb.2019-03-12.png deleted file mode 100644 index a96b1c009ba..00000000000 Binary files a/Meta/Websites/serenityos.org/happy/1st/thumb.2019-03-12.png and /dev/null differ diff --git a/Meta/Websites/serenityos.org/happy/1st/thumb.2019-03-15.png b/Meta/Websites/serenityos.org/happy/1st/thumb.2019-03-15.png deleted file mode 100644 index 3c1e6b0ea2b..00000000000 Binary files a/Meta/Websites/serenityos.org/happy/1st/thumb.2019-03-15.png and /dev/null differ diff --git a/Meta/Websites/serenityos.org/happy/1st/thumb.2019-03-29.png b/Meta/Websites/serenityos.org/happy/1st/thumb.2019-03-29.png deleted file mode 100644 index 8e364a10894..00000000000 Binary files a/Meta/Websites/serenityos.org/happy/1st/thumb.2019-03-29.png and /dev/null differ diff --git a/Meta/Websites/serenityos.org/happy/1st/thumb.2019-04-11.png b/Meta/Websites/serenityos.org/happy/1st/thumb.2019-04-11.png deleted file mode 100644 index 178d5be4b50..00000000000 Binary files a/Meta/Websites/serenityos.org/happy/1st/thumb.2019-04-11.png and /dev/null differ diff --git a/Meta/Websites/serenityos.org/happy/1st/thumb.2019-04-14.png b/Meta/Websites/serenityos.org/happy/1st/thumb.2019-04-14.png deleted file mode 100644 index 729a9ce51f8..00000000000 Binary files a/Meta/Websites/serenityos.org/happy/1st/thumb.2019-04-14.png and /dev/null differ diff --git a/Meta/Websites/serenityos.org/happy/1st/thumb.2019-04-20.png b/Meta/Websites/serenityos.org/happy/1st/thumb.2019-04-20.png deleted file mode 100644 index 7e6f401f8f5..00000000000 Binary files a/Meta/Websites/serenityos.org/happy/1st/thumb.2019-04-20.png and /dev/null differ diff --git a/Meta/Websites/serenityos.org/happy/1st/thumb.2019-04-22.png b/Meta/Websites/serenityos.org/happy/1st/thumb.2019-04-22.png deleted file mode 100644 index a74331d8b4d..00000000000 Binary files a/Meta/Websites/serenityos.org/happy/1st/thumb.2019-04-22.png and /dev/null differ diff --git a/Meta/Websites/serenityos.org/happy/1st/thumb.2019-04-29.png b/Meta/Websites/serenityos.org/happy/1st/thumb.2019-04-29.png deleted file mode 100644 index 6fb6fa484e6..00000000000 Binary files a/Meta/Websites/serenityos.org/happy/1st/thumb.2019-04-29.png and /dev/null differ diff --git a/Meta/Websites/serenityos.org/happy/1st/thumb.2019-06-14.png b/Meta/Websites/serenityos.org/happy/1st/thumb.2019-06-14.png deleted file mode 100644 index bf40d695028..00000000000 Binary files a/Meta/Websites/serenityos.org/happy/1st/thumb.2019-06-14.png and /dev/null differ diff --git a/Meta/Websites/serenityos.org/happy/1st/thumb.2019-07-14.png b/Meta/Websites/serenityos.org/happy/1st/thumb.2019-07-14.png deleted file mode 100644 index b415a922105..00000000000 Binary files a/Meta/Websites/serenityos.org/happy/1st/thumb.2019-07-14.png and /dev/null differ diff --git a/Meta/Websites/serenityos.org/happy/1st/thumb.2019-08-27.png b/Meta/Websites/serenityos.org/happy/1st/thumb.2019-08-27.png deleted file mode 100644 index be6eda09cd3..00000000000 Binary files a/Meta/Websites/serenityos.org/happy/1st/thumb.2019-08-27.png and /dev/null differ diff --git a/Meta/Websites/serenityos.org/happy/1st/thumb.2019-09-09.png b/Meta/Websites/serenityos.org/happy/1st/thumb.2019-09-09.png deleted file mode 100644 index 4391ffb4df5..00000000000 Binary files a/Meta/Websites/serenityos.org/happy/1st/thumb.2019-09-09.png and /dev/null differ diff --git a/Meta/Websites/serenityos.org/happy/1st/thumb.2019-09-29.png b/Meta/Websites/serenityos.org/happy/1st/thumb.2019-09-29.png deleted file mode 100644 index a1e760235f0..00000000000 Binary files a/Meta/Websites/serenityos.org/happy/1st/thumb.2019-09-29.png and /dev/null differ diff --git a/Meta/Websites/serenityos.org/happy/1st/thumb.2019-10-06.png b/Meta/Websites/serenityos.org/happy/1st/thumb.2019-10-06.png deleted file mode 100644 index 1e5131c3507..00000000000 Binary files a/Meta/Websites/serenityos.org/happy/1st/thumb.2019-10-06.png and /dev/null differ diff --git a/Meta/Websites/serenityos.org/happy/1st/thumb.2019-10-10.png b/Meta/Websites/serenityos.org/happy/1st/thumb.2019-10-10.png deleted file mode 100644 index 91745429a28..00000000000 Binary files a/Meta/Websites/serenityos.org/happy/1st/thumb.2019-10-10.png and /dev/null differ diff --git a/Meta/Websites/serenityos.org/happy/2nd/2018-10-10.png b/Meta/Websites/serenityos.org/happy/2nd/2018-10-10.png deleted file mode 100644 index 4cbf8a24a00..00000000000 Binary files a/Meta/Websites/serenityos.org/happy/2nd/2018-10-10.png and /dev/null differ diff --git a/Meta/Websites/serenityos.org/happy/2nd/2018-11-08.png b/Meta/Websites/serenityos.org/happy/2nd/2018-11-08.png deleted file mode 100644 index c6c6ead564d..00000000000 Binary files a/Meta/Websites/serenityos.org/happy/2nd/2018-11-08.png and /dev/null differ diff --git a/Meta/Websites/serenityos.org/happy/2nd/2019-01-09.png b/Meta/Websites/serenityos.org/happy/2nd/2019-01-09.png deleted file mode 100644 index c3a0ba1055a..00000000000 Binary files a/Meta/Websites/serenityos.org/happy/2nd/2019-01-09.png and /dev/null differ diff --git a/Meta/Websites/serenityos.org/happy/2nd/2019-01-10.png b/Meta/Websites/serenityos.org/happy/2nd/2019-01-10.png deleted file mode 100644 index e592275868e..00000000000 Binary files a/Meta/Websites/serenityos.org/happy/2nd/2019-01-10.png and /dev/null differ diff --git a/Meta/Websites/serenityos.org/happy/2nd/2019-01-21.png b/Meta/Websites/serenityos.org/happy/2nd/2019-01-21.png deleted file mode 100644 index e1eca73e3aa..00000000000 Binary files a/Meta/Websites/serenityos.org/happy/2nd/2019-01-21.png and /dev/null differ diff --git a/Meta/Websites/serenityos.org/happy/2nd/2019-01-29.png b/Meta/Websites/serenityos.org/happy/2nd/2019-01-29.png deleted file mode 100644 index 987f4307f9e..00000000000 Binary files a/Meta/Websites/serenityos.org/happy/2nd/2019-01-29.png and /dev/null differ diff --git a/Meta/Websites/serenityos.org/happy/2nd/2019-02-03.png b/Meta/Websites/serenityos.org/happy/2nd/2019-02-03.png deleted file mode 100644 index 23d948b1c57..00000000000 Binary files a/Meta/Websites/serenityos.org/happy/2nd/2019-02-03.png and /dev/null differ diff --git a/Meta/Websites/serenityos.org/happy/2nd/2019-02-08.png b/Meta/Websites/serenityos.org/happy/2nd/2019-02-08.png deleted file mode 100644 index f3c7099faec..00000000000 Binary files a/Meta/Websites/serenityos.org/happy/2nd/2019-02-08.png and /dev/null differ diff --git a/Meta/Websites/serenityos.org/happy/2nd/2019-02-11.png b/Meta/Websites/serenityos.org/happy/2nd/2019-02-11.png deleted file mode 100644 index 0627c524d42..00000000000 Binary files a/Meta/Websites/serenityos.org/happy/2nd/2019-02-11.png and /dev/null differ diff --git a/Meta/Websites/serenityos.org/happy/2nd/2019-02-28.png b/Meta/Websites/serenityos.org/happy/2nd/2019-02-28.png deleted file mode 100644 index 06dcc3f2837..00000000000 Binary files a/Meta/Websites/serenityos.org/happy/2nd/2019-02-28.png and /dev/null differ diff --git a/Meta/Websites/serenityos.org/happy/2nd/2019-03-04.png b/Meta/Websites/serenityos.org/happy/2nd/2019-03-04.png deleted file mode 100644 index 5bd9b8f89c3..00000000000 Binary files a/Meta/Websites/serenityos.org/happy/2nd/2019-03-04.png and /dev/null differ diff --git a/Meta/Websites/serenityos.org/happy/2nd/2019-03-07.png b/Meta/Websites/serenityos.org/happy/2nd/2019-03-07.png deleted file mode 100644 index 8ad0fa76a61..00000000000 Binary files a/Meta/Websites/serenityos.org/happy/2nd/2019-03-07.png and /dev/null differ diff --git a/Meta/Websites/serenityos.org/happy/2nd/2019-03-12.png b/Meta/Websites/serenityos.org/happy/2nd/2019-03-12.png deleted file mode 100644 index 404b4a5a143..00000000000 Binary files a/Meta/Websites/serenityos.org/happy/2nd/2019-03-12.png and /dev/null differ diff --git a/Meta/Websites/serenityos.org/happy/2nd/2019-03-15.png b/Meta/Websites/serenityos.org/happy/2nd/2019-03-15.png deleted file mode 100644 index fd670ca4295..00000000000 Binary files a/Meta/Websites/serenityos.org/happy/2nd/2019-03-15.png and /dev/null differ diff --git a/Meta/Websites/serenityos.org/happy/2nd/2019-03-29.png b/Meta/Websites/serenityos.org/happy/2nd/2019-03-29.png deleted file mode 100644 index 41f31c52a23..00000000000 Binary files a/Meta/Websites/serenityos.org/happy/2nd/2019-03-29.png and /dev/null differ diff --git a/Meta/Websites/serenityos.org/happy/2nd/2019-04-11.png b/Meta/Websites/serenityos.org/happy/2nd/2019-04-11.png deleted file mode 100644 index 9b8e9a3cf27..00000000000 Binary files a/Meta/Websites/serenityos.org/happy/2nd/2019-04-11.png and /dev/null differ diff --git a/Meta/Websites/serenityos.org/happy/2nd/2019-04-14.png b/Meta/Websites/serenityos.org/happy/2nd/2019-04-14.png deleted file mode 100644 index 5b169915939..00000000000 Binary files a/Meta/Websites/serenityos.org/happy/2nd/2019-04-14.png and /dev/null differ diff --git a/Meta/Websites/serenityos.org/happy/2nd/2019-04-20.png b/Meta/Websites/serenityos.org/happy/2nd/2019-04-20.png deleted file mode 100644 index 65e6a9d89b6..00000000000 Binary files a/Meta/Websites/serenityos.org/happy/2nd/2019-04-20.png and /dev/null differ diff --git a/Meta/Websites/serenityos.org/happy/2nd/2019-04-22.png b/Meta/Websites/serenityos.org/happy/2nd/2019-04-22.png deleted file mode 100644 index 5da859b3c7c..00000000000 Binary files a/Meta/Websites/serenityos.org/happy/2nd/2019-04-22.png and /dev/null differ diff --git a/Meta/Websites/serenityos.org/happy/2nd/2019-04-29.png b/Meta/Websites/serenityos.org/happy/2nd/2019-04-29.png deleted file mode 100644 index 04e2ecff02d..00000000000 Binary files a/Meta/Websites/serenityos.org/happy/2nd/2019-04-29.png and /dev/null differ diff --git a/Meta/Websites/serenityos.org/happy/2nd/2019-06-14.png b/Meta/Websites/serenityos.org/happy/2nd/2019-06-14.png deleted file mode 100644 index 89f6f88a8c2..00000000000 Binary files a/Meta/Websites/serenityos.org/happy/2nd/2019-06-14.png and /dev/null differ diff --git a/Meta/Websites/serenityos.org/happy/2nd/2019-07-14.png b/Meta/Websites/serenityos.org/happy/2nd/2019-07-14.png deleted file mode 100644 index e362743a568..00000000000 Binary files a/Meta/Websites/serenityos.org/happy/2nd/2019-07-14.png and /dev/null differ diff --git a/Meta/Websites/serenityos.org/happy/2nd/2019-08-27.png b/Meta/Websites/serenityos.org/happy/2nd/2019-08-27.png deleted file mode 100644 index 574119dd581..00000000000 Binary files a/Meta/Websites/serenityos.org/happy/2nd/2019-08-27.png and /dev/null differ diff --git a/Meta/Websites/serenityos.org/happy/2nd/2019-09-09.png b/Meta/Websites/serenityos.org/happy/2nd/2019-09-09.png deleted file mode 100644 index 0a4aed5c3f1..00000000000 Binary files a/Meta/Websites/serenityos.org/happy/2nd/2019-09-09.png and /dev/null differ diff --git a/Meta/Websites/serenityos.org/happy/2nd/2019-09-29.png b/Meta/Websites/serenityos.org/happy/2nd/2019-09-29.png deleted file mode 100644 index b4ed7a724bc..00000000000 Binary files a/Meta/Websites/serenityos.org/happy/2nd/2019-09-29.png and /dev/null differ diff --git a/Meta/Websites/serenityos.org/happy/2nd/2019-10-06.png b/Meta/Websites/serenityos.org/happy/2nd/2019-10-06.png deleted file mode 100644 index 7e95fbf4f53..00000000000 Binary files a/Meta/Websites/serenityos.org/happy/2nd/2019-10-06.png and /dev/null differ diff --git a/Meta/Websites/serenityos.org/happy/2nd/2019-10-10.png b/Meta/Websites/serenityos.org/happy/2nd/2019-10-10.png deleted file mode 100644 index 1984023c50b..00000000000 Binary files a/Meta/Websites/serenityos.org/happy/2nd/2019-10-10.png and /dev/null differ diff --git a/Meta/Websites/serenityos.org/happy/2nd/2019-10-31.png b/Meta/Websites/serenityos.org/happy/2nd/2019-10-31.png deleted file mode 100644 index f4768dbfafa..00000000000 Binary files a/Meta/Websites/serenityos.org/happy/2nd/2019-10-31.png and /dev/null differ diff --git a/Meta/Websites/serenityos.org/happy/2nd/2019-11-30.png b/Meta/Websites/serenityos.org/happy/2nd/2019-11-30.png deleted file mode 100644 index 164d5eb65e4..00000000000 Binary files a/Meta/Websites/serenityos.org/happy/2nd/2019-11-30.png and /dev/null differ diff --git a/Meta/Websites/serenityos.org/happy/2nd/2020-01-31.png b/Meta/Websites/serenityos.org/happy/2nd/2020-01-31.png deleted file mode 100644 index 903c5fc04fd..00000000000 Binary files a/Meta/Websites/serenityos.org/happy/2nd/2020-01-31.png and /dev/null differ diff --git a/Meta/Websites/serenityos.org/happy/2nd/2020-02-22.png b/Meta/Websites/serenityos.org/happy/2nd/2020-02-22.png deleted file mode 100644 index e8227a0b666..00000000000 Binary files a/Meta/Websites/serenityos.org/happy/2nd/2020-02-22.png and /dev/null differ diff --git a/Meta/Websites/serenityos.org/happy/2nd/2020-02-29.png b/Meta/Websites/serenityos.org/happy/2nd/2020-02-29.png deleted file mode 100644 index 05ca6383902..00000000000 Binary files a/Meta/Websites/serenityos.org/happy/2nd/2020-02-29.png and /dev/null differ diff --git a/Meta/Websites/serenityos.org/happy/2nd/2020-03-31.png b/Meta/Websites/serenityos.org/happy/2nd/2020-03-31.png deleted file mode 100644 index 5113be9ffb2..00000000000 Binary files a/Meta/Websites/serenityos.org/happy/2nd/2020-03-31.png and /dev/null differ diff --git a/Meta/Websites/serenityos.org/happy/2nd/2020-05-30.png b/Meta/Websites/serenityos.org/happy/2nd/2020-05-30.png deleted file mode 100644 index 872edceaef7..00000000000 Binary files a/Meta/Websites/serenityos.org/happy/2nd/2020-05-30.png and /dev/null differ diff --git a/Meta/Websites/serenityos.org/happy/2nd/2020-06-22.png b/Meta/Websites/serenityos.org/happy/2nd/2020-06-22.png deleted file mode 100644 index 58dbda82fff..00000000000 Binary files a/Meta/Websites/serenityos.org/happy/2nd/2020-06-22.png and /dev/null differ diff --git a/Meta/Websites/serenityos.org/happy/2nd/2020-06-30.png b/Meta/Websites/serenityos.org/happy/2nd/2020-06-30.png deleted file mode 100644 index 67baaf52b97..00000000000 Binary files a/Meta/Websites/serenityos.org/happy/2nd/2020-06-30.png and /dev/null differ diff --git a/Meta/Websites/serenityos.org/happy/2nd/2020-07-27.png b/Meta/Websites/serenityos.org/happy/2nd/2020-07-27.png deleted file mode 100644 index b448f457018..00000000000 Binary files a/Meta/Websites/serenityos.org/happy/2nd/2020-07-27.png and /dev/null differ diff --git a/Meta/Websites/serenityos.org/happy/2nd/2020-08-30.png b/Meta/Websites/serenityos.org/happy/2nd/2020-08-30.png deleted file mode 100644 index 5c3d688cd99..00000000000 Binary files a/Meta/Websites/serenityos.org/happy/2nd/2020-08-30.png and /dev/null differ diff --git a/Meta/Websites/serenityos.org/happy/2nd/2020-09-22.png b/Meta/Websites/serenityos.org/happy/2nd/2020-09-22.png deleted file mode 100644 index 504042bc02d..00000000000 Binary files a/Meta/Websites/serenityos.org/happy/2nd/2020-09-22.png and /dev/null differ diff --git a/Meta/Websites/serenityos.org/happy/2nd/2020-09-29.png b/Meta/Websites/serenityos.org/happy/2nd/2020-09-29.png deleted file mode 100644 index b2137d4fa03..00000000000 Binary files a/Meta/Websites/serenityos.org/happy/2nd/2020-09-29.png and /dev/null differ diff --git a/Meta/Websites/serenityos.org/happy/2nd/2020-10-03.png b/Meta/Websites/serenityos.org/happy/2nd/2020-10-03.png deleted file mode 100644 index bd4e8979c3e..00000000000 Binary files a/Meta/Websites/serenityos.org/happy/2nd/2020-10-03.png and /dev/null differ diff --git a/Meta/Websites/serenityos.org/happy/2nd/2020-10-10.png b/Meta/Websites/serenityos.org/happy/2nd/2020-10-10.png deleted file mode 100644 index 07d770886d8..00000000000 Binary files a/Meta/Websites/serenityos.org/happy/2nd/2020-10-10.png and /dev/null differ diff --git a/Meta/Websites/serenityos.org/happy/2nd/buggie2.png b/Meta/Websites/serenityos.org/happy/2nd/buggie2.png deleted file mode 100644 index e3b0b67520c..00000000000 Binary files a/Meta/Websites/serenityos.org/happy/2nd/buggie2.png and /dev/null differ diff --git a/Meta/Websites/serenityos.org/happy/2nd/getbrowser.gif b/Meta/Websites/serenityos.org/happy/2nd/getbrowser.gif deleted file mode 100644 index c677dd60962..00000000000 Binary files a/Meta/Websites/serenityos.org/happy/2nd/getbrowser.gif and /dev/null differ diff --git a/Meta/Websites/serenityos.org/happy/2nd/hxp.png b/Meta/Websites/serenityos.org/happy/2nd/hxp.png deleted file mode 100644 index 3c82d032c9a..00000000000 Binary files a/Meta/Websites/serenityos.org/happy/2nd/hxp.png and /dev/null differ diff --git a/Meta/Websites/serenityos.org/happy/2nd/index.html b/Meta/Websites/serenityos.org/happy/2nd/index.html deleted file mode 100644 index 52ea30e6ae3..00000000000 --- a/Meta/Websites/serenityos.org/happy/2nd/index.html +++ /dev/null @@ -1,458 +0,0 @@ - - - - SerenityOS: The second year - - - -

SerenityOS: The second year

- -
-
- Best viewed in 1024x768 with the SerenityOS Browser!
- (jk it works everywhere) -
-

- Hello friends! -

-

- Today is the second birthday of SerenityOS, - counting from the - first commit - in the git repository, - on October 10, 2018. -

-

- Previous birthdays: 1st. -

-

- NOTE: Since the HTML and CSS support has improved over the last - year, this page uses a bit more of them to showcase SerenityOS browser - support. :^) -

- -

Introduction to SerenityOS

- -

- SerenityOS is a from-scratch desktop operating system that combines - a Unix-like core with the look&feel of 1990s productivity software. - It's written in modern C++ and goes all the way from kernel to web browser. - The project aims to build everything in-house instead of relying on - third-party libraries. -

- -

- I started building this system after - finishing a 3-month rehabilitation program for drug addiction - in 2018. I found myself with a lot of time and nothing to spend it on. - So I began building something I'd always wanted to build: my very own dream OS. -

- -

- Parts of my development work is presented in screencast format on - my YouTube channel. - I also post monthly update videos showcasing new features there. -

- -

Snapshots from year 2 of development

- -

- Anyways, let's continue looking at screenshots where we left off - in last year's birthday celebration! - - We had just gotten the browser strong enough to display the first - birthday celebration page.. -

- -
-
2019-10-31: :hover selectors in the Browser
-
- -

- I remember first seeing links change color when hovered back - in the IE3 days, and thinking it was the coolest thing ever. -

-
-
- -
-
2019-11-04: Ladies and gentlemen, we've got Quake!
-
- -

- Jesse ported Quake to Serenity. We didn't have sound at - first but that was fixed later. Still, it was very exciting! -

-
-
- - -
-
2019-11-30: Playing with "find in files" in the HackStudio IDE
-
- -

- I decided to start building an IDE for C++ development. - Since SerenityOS is a programmer's OS, a capable development - environment is a must-have. -

-
-
- -
-
2019-12-08: Ported the nesalizer NES emulator (with sound support, too!)
-
- -

- Getting a NES emulator running helped drive improvements for - threading and timing APIs in the kernel. - Check out the video to see me die before even reaching the 1st palace.. -

-
-
- -
-
2019-12-30: SerenityOS was hacked in a 36c3 CTF
-
- -

- Someone put together a CTF (Capture The Flag) challenge at the - 36c3 computer security conference. I learned about this when - two exploits showed up (complete with write-ups!) on GitHub: - - one from Fire30 and another one from braindead. -

- This caught me by total surprise and I was a little bit disappointed - at how easy it was to break into the system. This event sent me down - a deep rabbit hole of learning about system security and applying - everything I learned to secure Serenity. I wanted to make sure that - the next CTF presented more of a challenge! -

-
-
- -
-
2020-01-12: I ported the game VVVVVVV to Serenity
-
- -

- After the classic platformer VVVVVV was released as open-source, - I wanted to get it running on Serenity. It was pretty easy - to get it going since it used SDL and we already had a decent SDL port. - The game was open sourced on Jan 10th and I had it running 2 days later. -

-
-
- -
-
2020-01-31: pledge() and unveil() all the things
-
- -

- Outside of enabling every protection mechanism the x86 has to offer, - there was also a fair amount of architectural Unix-level security - work. One of the most important pieces was adopting two OpenBSD-like - system calls: pledge() and unveil(). -

- I wrote about Serenity's pledge and unveil implementation on my blog. -

-
-
- -
-
2020-02-22: Kernel symbols in the Profiler
-
- -

- The system's built-in time profiler became a lot more helpful when - we added symbolication of kernel stack frames. You can tell them apart - from userspace frames by the little red icon in the graph! -

-
-
- -
-
2020-02-29: A simple built-in web server
-
- -

- For my own birthday (February 9th), I celebrated by building a simple - HTTP server for Serenity. I always loved the simple HTTP server that - comes with Python, and I wanted something similar for Serenity. -

-
-
- -
-
2020-03-31: The JavaScript explosion
-
- -

- People kept asking me if the web browser would ever support JavaScript. - I honestly felt a bit intimidated when starting this project but I just - did it anyway, and a whole bunch of people very quickly joined up to - help out. -

- I started by implementing a simple AST interpreter, - and building some test AST's by hand. Then Stephan added a parser, and Sergey made a REPL, - and then things really took off, with Linus and Matt going especially deep. -

-
-
- -
-
2020-05-28: Changing the LICENSE (a tiny bit)
-
- -

- To celebrate the project reaching 10'000 commits, I updated the main license file - to assign copyright to "the SerenityOS developers" instead of just myself. -

-
-
- -
-
2020-05-30: Accessing more of the web with TLS (and HTTPS)
-
- -

- Browser development was moving forward with the LibJS engine allowing - more and more complex things. But we were still limited to visiting unencrypted - websites served over HTTP. -

- In comes LibTLS and LibCrypto! Thanks to some amazing work by Ali, assisted by - Itamar and DexesTTP, we can now talk to HTTPS websites as well. The whole world - opens up! -

- I was super excited when I could get Google loading for the first time! -

-
-
- -
-
2020-06-22: Our very own JPEG decoder
-
- -

- Devashish implemented a JPEG decoder for LibGfx and suddenly we had access - to a world of photos! -

-
-
- -
-
2020-06-30: Working on ACID2 compliance test fixes
-
- -

- I spent a whole bunch of time improving standards compliance in the - LibWeb engine's CSS implementation. - I've been using the classic ACID2 test - to drive some of the work. As you can see from the picture, a lot of work - still remains! -

-
-
- -
-
2020-07-27: Catching memory errors with a userspace emulator
-
- -

- One tool I've missed ever since starting the SerenityOS project is the amazing - Valgrind. -

- I finally decided to do something about it, and I was curious how such a thing might - be implemented, so I set out to create a workalike of my own. -

- I've hacked on x86 emulators in the past, so I decided to bring my knowledge in that - area into Serenity and started building UserspaceEmulator (or UE for short), a program - that implements a ring-3-only x86 CPU and intercepts system calls so we can instrument them. -

- Within a couple of weeks of hacking, I could run compiled programs without changing - anything about them, and catch memory leaks just like Valgrind would! -

-
-
- -
-
2020-08-30: More desktop games! (Chess and 2048)
-
- -

- I've always been a fan of desktop games, ever since the days of the - Microsoft Entertainment Pack. -

- Here we were joined by two great games, a 2048 clone by Ali & Sergey, - and a Chess game by Peter. The chess engine is still pleasantly stupid, - so I can even beat it myself! -

- Also of note: the awesome icons in the system menu! Many of them were - drawn by thankyouverycool (so thank you, thankyouverycool, very cool!) -

- Even more: the "About" box was redesigned by Nick, and Buggie the system - mascot was drawn by Simon! These things happened much earlier but I'm - mentioning them here since they are visible in the screenshot. -

-
-
- -
-
2020-09-22: Git integration in the HackStudio IDE
-
- -

- Itamar started adding support for Git integration (diffing, staging, commits) - to the HackStudio IDE. -

- Note that this is an optional feature that is only available if you have the - git port installed. HackStudio is the only program in the system - that makes use of ports since we don't have our own implementations of - git, a compiler, or a linker... yet. :^) -

-
-
- -
-
2020-09-29: Playing with the Spreadsheet application
-
- -

- The Spreadsheet app was started by Ali and is powered internally by the - LibJS JavaScript engine. Cell expressions are evaluated as JS. - It feels really neat so far, although we're still figuring out - how it should work. -

- It even has built-in documentation, which is generated from the JavaScript - source code! -

-
-
- -
-
2020-10-03: Copying images between applications!
-
- -

- While we've had a system clipboard for a long time, it only recently - became possible to copy anything other than text between apps. -

- Here I had just added an image context menu in the Browser so you can - copy images from the web directly to the clipboard! -

- Also on display: the PixelPaint app (formerly PaintBrush), which became - a layer-based image editor in the last year. -

-
-
- -
-
2020-10-10: Making this very webpage!
-
- -

- I had to fix some CSS bugs in the LibWeb engine while putting together - this page. I have also discovered a whole bunch of things that need - fixing. But this is where we are right now! -

- Happy 2nd birthday SerenityOS! I'm so proud of you, and of all the people who have worked on you! -

-
-
- -

Conclusion

- -

- And with that, this little tour of the second year of Serenity is over! -

-

- If you would like to see more, I've continued the tradition of making a monthly - round-up video at the end of each month. You can find them in - a special playlist - on my YouTube channel. -

- -

Thanks

- -

- To all the lovely people have helped out in the last year, with code, bug reports, - man pages, commenting/liking/sharing my videos, sending letters, chilling on IRC, - retweeting my shower thoughts, telling your friends, etc, thank you! - I'm eternally grateful for all the love and support this project gets. -

-

- And also, a huge thank you! to everyone who has supported me via - GitHub Sponsors, - Patreon, - and PayPal. - - I hope to be able to do this full time some day, and your continued support - is keeping the dream alive! -

-

- All right, let's keep pushing forward into year 3! -

-

- Andreas Kling, 2020-10-10 -

-

- GitHub | - YouTube | - Twitter | - Patreon | - PayPal -

- - - diff --git a/Meta/Websites/serenityos.org/happy/2nd/thumb.2018-10-10.png b/Meta/Websites/serenityos.org/happy/2nd/thumb.2018-10-10.png deleted file mode 100644 index 6627c243aca..00000000000 Binary files a/Meta/Websites/serenityos.org/happy/2nd/thumb.2018-10-10.png and /dev/null differ diff --git a/Meta/Websites/serenityos.org/happy/2nd/thumb.2018-11-08.png b/Meta/Websites/serenityos.org/happy/2nd/thumb.2018-11-08.png deleted file mode 100644 index 3a8ad5f9b7d..00000000000 Binary files a/Meta/Websites/serenityos.org/happy/2nd/thumb.2018-11-08.png and /dev/null differ diff --git a/Meta/Websites/serenityos.org/happy/2nd/thumb.2019-01-09.png b/Meta/Websites/serenityos.org/happy/2nd/thumb.2019-01-09.png deleted file mode 100644 index 5001649b57c..00000000000 Binary files a/Meta/Websites/serenityos.org/happy/2nd/thumb.2019-01-09.png and /dev/null differ diff --git a/Meta/Websites/serenityos.org/happy/2nd/thumb.2019-01-10.png b/Meta/Websites/serenityos.org/happy/2nd/thumb.2019-01-10.png deleted file mode 100644 index bb6605432a4..00000000000 Binary files a/Meta/Websites/serenityos.org/happy/2nd/thumb.2019-01-10.png and /dev/null differ diff --git a/Meta/Websites/serenityos.org/happy/2nd/thumb.2019-01-21.png b/Meta/Websites/serenityos.org/happy/2nd/thumb.2019-01-21.png deleted file mode 100644 index 572f6e6b3c3..00000000000 Binary files a/Meta/Websites/serenityos.org/happy/2nd/thumb.2019-01-21.png and /dev/null differ diff --git a/Meta/Websites/serenityos.org/happy/2nd/thumb.2019-01-29.png b/Meta/Websites/serenityos.org/happy/2nd/thumb.2019-01-29.png deleted file mode 100644 index 6e6b9eba8f3..00000000000 Binary files a/Meta/Websites/serenityos.org/happy/2nd/thumb.2019-01-29.png and /dev/null differ diff --git a/Meta/Websites/serenityos.org/happy/2nd/thumb.2019-02-03.png b/Meta/Websites/serenityos.org/happy/2nd/thumb.2019-02-03.png deleted file mode 100644 index 3d71658b7f3..00000000000 Binary files a/Meta/Websites/serenityos.org/happy/2nd/thumb.2019-02-03.png and /dev/null differ diff --git a/Meta/Websites/serenityos.org/happy/2nd/thumb.2019-02-08.png b/Meta/Websites/serenityos.org/happy/2nd/thumb.2019-02-08.png deleted file mode 100644 index abb08826f4c..00000000000 Binary files a/Meta/Websites/serenityos.org/happy/2nd/thumb.2019-02-08.png and /dev/null differ diff --git a/Meta/Websites/serenityos.org/happy/2nd/thumb.2019-02-11.png b/Meta/Websites/serenityos.org/happy/2nd/thumb.2019-02-11.png deleted file mode 100644 index 64ee82d65d2..00000000000 Binary files a/Meta/Websites/serenityos.org/happy/2nd/thumb.2019-02-11.png and /dev/null differ diff --git a/Meta/Websites/serenityos.org/happy/2nd/thumb.2019-02-28.png b/Meta/Websites/serenityos.org/happy/2nd/thumb.2019-02-28.png deleted file mode 100644 index ed81ed34f95..00000000000 Binary files a/Meta/Websites/serenityos.org/happy/2nd/thumb.2019-02-28.png and /dev/null differ diff --git a/Meta/Websites/serenityos.org/happy/2nd/thumb.2019-03-04.png b/Meta/Websites/serenityos.org/happy/2nd/thumb.2019-03-04.png deleted file mode 100644 index 47f9ec4756c..00000000000 Binary files a/Meta/Websites/serenityos.org/happy/2nd/thumb.2019-03-04.png and /dev/null differ diff --git a/Meta/Websites/serenityos.org/happy/2nd/thumb.2019-03-07.png b/Meta/Websites/serenityos.org/happy/2nd/thumb.2019-03-07.png deleted file mode 100644 index 65a4b895869..00000000000 Binary files a/Meta/Websites/serenityos.org/happy/2nd/thumb.2019-03-07.png and /dev/null differ diff --git a/Meta/Websites/serenityos.org/happy/2nd/thumb.2019-03-12.png b/Meta/Websites/serenityos.org/happy/2nd/thumb.2019-03-12.png deleted file mode 100644 index a96b1c009ba..00000000000 Binary files a/Meta/Websites/serenityos.org/happy/2nd/thumb.2019-03-12.png and /dev/null differ diff --git a/Meta/Websites/serenityos.org/happy/2nd/thumb.2019-03-15.png b/Meta/Websites/serenityos.org/happy/2nd/thumb.2019-03-15.png deleted file mode 100644 index 3c1e6b0ea2b..00000000000 Binary files a/Meta/Websites/serenityos.org/happy/2nd/thumb.2019-03-15.png and /dev/null differ diff --git a/Meta/Websites/serenityos.org/happy/2nd/thumb.2019-03-29.png b/Meta/Websites/serenityos.org/happy/2nd/thumb.2019-03-29.png deleted file mode 100644 index 8e364a10894..00000000000 Binary files a/Meta/Websites/serenityos.org/happy/2nd/thumb.2019-03-29.png and /dev/null differ diff --git a/Meta/Websites/serenityos.org/happy/2nd/thumb.2019-04-11.png b/Meta/Websites/serenityos.org/happy/2nd/thumb.2019-04-11.png deleted file mode 100644 index 178d5be4b50..00000000000 Binary files a/Meta/Websites/serenityos.org/happy/2nd/thumb.2019-04-11.png and /dev/null differ diff --git a/Meta/Websites/serenityos.org/happy/2nd/thumb.2019-04-14.png b/Meta/Websites/serenityos.org/happy/2nd/thumb.2019-04-14.png deleted file mode 100644 index 729a9ce51f8..00000000000 Binary files a/Meta/Websites/serenityos.org/happy/2nd/thumb.2019-04-14.png and /dev/null differ diff --git a/Meta/Websites/serenityos.org/happy/2nd/thumb.2019-04-20.png b/Meta/Websites/serenityos.org/happy/2nd/thumb.2019-04-20.png deleted file mode 100644 index 7e6f401f8f5..00000000000 Binary files a/Meta/Websites/serenityos.org/happy/2nd/thumb.2019-04-20.png and /dev/null differ diff --git a/Meta/Websites/serenityos.org/happy/2nd/thumb.2019-04-22.png b/Meta/Websites/serenityos.org/happy/2nd/thumb.2019-04-22.png deleted file mode 100644 index a74331d8b4d..00000000000 Binary files a/Meta/Websites/serenityos.org/happy/2nd/thumb.2019-04-22.png and /dev/null differ diff --git a/Meta/Websites/serenityos.org/happy/2nd/thumb.2019-04-29.png b/Meta/Websites/serenityos.org/happy/2nd/thumb.2019-04-29.png deleted file mode 100644 index 6fb6fa484e6..00000000000 Binary files a/Meta/Websites/serenityos.org/happy/2nd/thumb.2019-04-29.png and /dev/null differ diff --git a/Meta/Websites/serenityos.org/happy/2nd/thumb.2019-06-14.png b/Meta/Websites/serenityos.org/happy/2nd/thumb.2019-06-14.png deleted file mode 100644 index bf40d695028..00000000000 Binary files a/Meta/Websites/serenityos.org/happy/2nd/thumb.2019-06-14.png and /dev/null differ diff --git a/Meta/Websites/serenityos.org/happy/2nd/thumb.2019-07-14.png b/Meta/Websites/serenityos.org/happy/2nd/thumb.2019-07-14.png deleted file mode 100644 index b415a922105..00000000000 Binary files a/Meta/Websites/serenityos.org/happy/2nd/thumb.2019-07-14.png and /dev/null differ diff --git a/Meta/Websites/serenityos.org/happy/2nd/thumb.2019-08-27.png b/Meta/Websites/serenityos.org/happy/2nd/thumb.2019-08-27.png deleted file mode 100644 index be6eda09cd3..00000000000 Binary files a/Meta/Websites/serenityos.org/happy/2nd/thumb.2019-08-27.png and /dev/null differ diff --git a/Meta/Websites/serenityos.org/happy/2nd/thumb.2019-09-09.png b/Meta/Websites/serenityos.org/happy/2nd/thumb.2019-09-09.png deleted file mode 100644 index 4391ffb4df5..00000000000 Binary files a/Meta/Websites/serenityos.org/happy/2nd/thumb.2019-09-09.png and /dev/null differ diff --git a/Meta/Websites/serenityos.org/happy/2nd/thumb.2019-09-29.png b/Meta/Websites/serenityos.org/happy/2nd/thumb.2019-09-29.png deleted file mode 100644 index a1e760235f0..00000000000 Binary files a/Meta/Websites/serenityos.org/happy/2nd/thumb.2019-09-29.png and /dev/null differ diff --git a/Meta/Websites/serenityos.org/happy/2nd/thumb.2019-10-06.png b/Meta/Websites/serenityos.org/happy/2nd/thumb.2019-10-06.png deleted file mode 100644 index 1e5131c3507..00000000000 Binary files a/Meta/Websites/serenityos.org/happy/2nd/thumb.2019-10-06.png and /dev/null differ diff --git a/Meta/Websites/serenityos.org/happy/2nd/thumb.2019-10-10.png b/Meta/Websites/serenityos.org/happy/2nd/thumb.2019-10-10.png deleted file mode 100644 index 91745429a28..00000000000 Binary files a/Meta/Websites/serenityos.org/happy/2nd/thumb.2019-10-10.png and /dev/null differ diff --git a/Meta/Websites/serenityos.org/happy/2nd/thumb.2019-10-31.png b/Meta/Websites/serenityos.org/happy/2nd/thumb.2019-10-31.png deleted file mode 100644 index 4637daf37b8..00000000000 Binary files a/Meta/Websites/serenityos.org/happy/2nd/thumb.2019-10-31.png and /dev/null differ diff --git a/Meta/Websites/serenityos.org/happy/2nd/thumb.2019-11-30.png b/Meta/Websites/serenityos.org/happy/2nd/thumb.2019-11-30.png deleted file mode 100644 index 3076eebd48e..00000000000 Binary files a/Meta/Websites/serenityos.org/happy/2nd/thumb.2019-11-30.png and /dev/null differ diff --git a/Meta/Websites/serenityos.org/happy/2nd/thumb.2020-01-31.png b/Meta/Websites/serenityos.org/happy/2nd/thumb.2020-01-31.png deleted file mode 100644 index fe7424f87ce..00000000000 Binary files a/Meta/Websites/serenityos.org/happy/2nd/thumb.2020-01-31.png and /dev/null differ diff --git a/Meta/Websites/serenityos.org/happy/2nd/thumb.2020-02-22.png b/Meta/Websites/serenityos.org/happy/2nd/thumb.2020-02-22.png deleted file mode 100644 index b7bc810e4dd..00000000000 Binary files a/Meta/Websites/serenityos.org/happy/2nd/thumb.2020-02-22.png and /dev/null differ diff --git a/Meta/Websites/serenityos.org/happy/2nd/thumb.2020-02-29.png b/Meta/Websites/serenityos.org/happy/2nd/thumb.2020-02-29.png deleted file mode 100644 index 13de4708c75..00000000000 Binary files a/Meta/Websites/serenityos.org/happy/2nd/thumb.2020-02-29.png and /dev/null differ diff --git a/Meta/Websites/serenityos.org/happy/2nd/thumb.2020-03-31.png b/Meta/Websites/serenityos.org/happy/2nd/thumb.2020-03-31.png deleted file mode 100644 index 044f6523598..00000000000 Binary files a/Meta/Websites/serenityos.org/happy/2nd/thumb.2020-03-31.png and /dev/null differ diff --git a/Meta/Websites/serenityos.org/happy/2nd/thumb.2020-05-30.png b/Meta/Websites/serenityos.org/happy/2nd/thumb.2020-05-30.png deleted file mode 100644 index 83188b48cbe..00000000000 Binary files a/Meta/Websites/serenityos.org/happy/2nd/thumb.2020-05-30.png and /dev/null differ diff --git a/Meta/Websites/serenityos.org/happy/2nd/thumb.2020-06-22.png b/Meta/Websites/serenityos.org/happy/2nd/thumb.2020-06-22.png deleted file mode 100644 index 644f58df810..00000000000 Binary files a/Meta/Websites/serenityos.org/happy/2nd/thumb.2020-06-22.png and /dev/null differ diff --git a/Meta/Websites/serenityos.org/happy/2nd/thumb.2020-06-30.png b/Meta/Websites/serenityos.org/happy/2nd/thumb.2020-06-30.png deleted file mode 100644 index 7ad1b96c5a1..00000000000 Binary files a/Meta/Websites/serenityos.org/happy/2nd/thumb.2020-06-30.png and /dev/null differ diff --git a/Meta/Websites/serenityos.org/happy/2nd/thumb.2020-07-27.png b/Meta/Websites/serenityos.org/happy/2nd/thumb.2020-07-27.png deleted file mode 100644 index 2badd7dfad1..00000000000 Binary files a/Meta/Websites/serenityos.org/happy/2nd/thumb.2020-07-27.png and /dev/null differ diff --git a/Meta/Websites/serenityos.org/happy/2nd/thumb.2020-08-30.png b/Meta/Websites/serenityos.org/happy/2nd/thumb.2020-08-30.png deleted file mode 100644 index ac8b7eadc97..00000000000 Binary files a/Meta/Websites/serenityos.org/happy/2nd/thumb.2020-08-30.png and /dev/null differ diff --git a/Meta/Websites/serenityos.org/happy/2nd/thumb.2020-09-22.png b/Meta/Websites/serenityos.org/happy/2nd/thumb.2020-09-22.png deleted file mode 100644 index 764601c7fb3..00000000000 Binary files a/Meta/Websites/serenityos.org/happy/2nd/thumb.2020-09-22.png and /dev/null differ diff --git a/Meta/Websites/serenityos.org/happy/2nd/thumb.2020-09-29.png b/Meta/Websites/serenityos.org/happy/2nd/thumb.2020-09-29.png deleted file mode 100644 index 7049596df16..00000000000 Binary files a/Meta/Websites/serenityos.org/happy/2nd/thumb.2020-09-29.png and /dev/null differ diff --git a/Meta/Websites/serenityos.org/happy/2nd/thumb.2020-10-03.png b/Meta/Websites/serenityos.org/happy/2nd/thumb.2020-10-03.png deleted file mode 100644 index 9249ceaaf44..00000000000 Binary files a/Meta/Websites/serenityos.org/happy/2nd/thumb.2020-10-03.png and /dev/null differ diff --git a/Meta/Websites/serenityos.org/happy/2nd/thumb.2020-10-10.png b/Meta/Websites/serenityos.org/happy/2nd/thumb.2020-10-10.png deleted file mode 100644 index 6e54f673578..00000000000 Binary files a/Meta/Websites/serenityos.org/happy/2nd/thumb.2020-10-10.png and /dev/null differ diff --git a/Meta/Websites/serenityos.org/happy/3rd/2020-12-06.png b/Meta/Websites/serenityos.org/happy/3rd/2020-12-06.png deleted file mode 100644 index 78e06d3c9fc..00000000000 Binary files a/Meta/Websites/serenityos.org/happy/3rd/2020-12-06.png and /dev/null differ diff --git a/Meta/Websites/serenityos.org/happy/3rd/2020-12-17.png b/Meta/Websites/serenityos.org/happy/3rd/2020-12-17.png deleted file mode 100644 index b0be305a633..00000000000 Binary files a/Meta/Websites/serenityos.org/happy/3rd/2020-12-17.png and /dev/null differ diff --git a/Meta/Websites/serenityos.org/happy/3rd/2020-12-28.png b/Meta/Websites/serenityos.org/happy/3rd/2020-12-28.png deleted file mode 100644 index d67534af1d6..00000000000 Binary files a/Meta/Websites/serenityos.org/happy/3rd/2020-12-28.png and /dev/null differ diff --git a/Meta/Websites/serenityos.org/happy/3rd/2021-01-06.png b/Meta/Websites/serenityos.org/happy/3rd/2021-01-06.png deleted file mode 100644 index 0d782863d3d..00000000000 Binary files a/Meta/Websites/serenityos.org/happy/3rd/2021-01-06.png and /dev/null differ diff --git a/Meta/Websites/serenityos.org/happy/3rd/2021-01-08.png b/Meta/Websites/serenityos.org/happy/3rd/2021-01-08.png deleted file mode 100644 index 4ea88a18e1e..00000000000 Binary files a/Meta/Websites/serenityos.org/happy/3rd/2021-01-08.png and /dev/null differ diff --git a/Meta/Websites/serenityos.org/happy/3rd/2021-01-22.png b/Meta/Websites/serenityos.org/happy/3rd/2021-01-22.png deleted file mode 100644 index a0c47011431..00000000000 Binary files a/Meta/Websites/serenityos.org/happy/3rd/2021-01-22.png and /dev/null differ diff --git a/Meta/Websites/serenityos.org/happy/3rd/2021-02-16.png b/Meta/Websites/serenityos.org/happy/3rd/2021-02-16.png deleted file mode 100644 index 88e6dfe9fe1..00000000000 Binary files a/Meta/Websites/serenityos.org/happy/3rd/2021-02-16.png and /dev/null differ diff --git a/Meta/Websites/serenityos.org/happy/3rd/2021-02-18.png b/Meta/Websites/serenityos.org/happy/3rd/2021-02-18.png deleted file mode 100644 index 43cfe119098..00000000000 Binary files a/Meta/Websites/serenityos.org/happy/3rd/2021-02-18.png and /dev/null differ diff --git a/Meta/Websites/serenityos.org/happy/3rd/2021-02-20.png b/Meta/Websites/serenityos.org/happy/3rd/2021-02-20.png deleted file mode 100644 index 5c1377d1895..00000000000 Binary files a/Meta/Websites/serenityos.org/happy/3rd/2021-02-20.png and /dev/null differ diff --git a/Meta/Websites/serenityos.org/happy/3rd/2021-03-06.png b/Meta/Websites/serenityos.org/happy/3rd/2021-03-06.png deleted file mode 100644 index 3c03cf6e579..00000000000 Binary files a/Meta/Websites/serenityos.org/happy/3rd/2021-03-06.png and /dev/null differ diff --git a/Meta/Websites/serenityos.org/happy/3rd/2021-03-25.png b/Meta/Websites/serenityos.org/happy/3rd/2021-03-25.png deleted file mode 100644 index 16f91ce410b..00000000000 Binary files a/Meta/Websites/serenityos.org/happy/3rd/2021-03-25.png and /dev/null differ diff --git a/Meta/Websites/serenityos.org/happy/3rd/2021-03-29.png b/Meta/Websites/serenityos.org/happy/3rd/2021-03-29.png deleted file mode 100644 index 0243355a55a..00000000000 Binary files a/Meta/Websites/serenityos.org/happy/3rd/2021-03-29.png and /dev/null differ diff --git a/Meta/Websites/serenityos.org/happy/3rd/2021-04-03.png b/Meta/Websites/serenityos.org/happy/3rd/2021-04-03.png deleted file mode 100644 index a1f30d4a72d..00000000000 Binary files a/Meta/Websites/serenityos.org/happy/3rd/2021-04-03.png and /dev/null differ diff --git a/Meta/Websites/serenityos.org/happy/3rd/2021-04-10.png b/Meta/Websites/serenityos.org/happy/3rd/2021-04-10.png deleted file mode 100644 index 151dc6e6bf4..00000000000 Binary files a/Meta/Websites/serenityos.org/happy/3rd/2021-04-10.png and /dev/null differ diff --git a/Meta/Websites/serenityos.org/happy/3rd/2021-05-16-2.png b/Meta/Websites/serenityos.org/happy/3rd/2021-05-16-2.png deleted file mode 100644 index b80fd050f30..00000000000 Binary files a/Meta/Websites/serenityos.org/happy/3rd/2021-05-16-2.png and /dev/null differ diff --git a/Meta/Websites/serenityos.org/happy/3rd/2021-05-16.png b/Meta/Websites/serenityos.org/happy/3rd/2021-05-16.png deleted file mode 100644 index 9c2d7a6d0d8..00000000000 Binary files a/Meta/Websites/serenityos.org/happy/3rd/2021-05-16.png and /dev/null differ diff --git a/Meta/Websites/serenityos.org/happy/3rd/2021-05-20.png b/Meta/Websites/serenityos.org/happy/3rd/2021-05-20.png deleted file mode 100644 index 2c53c203623..00000000000 Binary files a/Meta/Websites/serenityos.org/happy/3rd/2021-05-20.png and /dev/null differ diff --git a/Meta/Websites/serenityos.org/happy/3rd/2021-05-22.png b/Meta/Websites/serenityos.org/happy/3rd/2021-05-22.png deleted file mode 100644 index 02e5dc9f008..00000000000 Binary files a/Meta/Websites/serenityos.org/happy/3rd/2021-05-22.png and /dev/null differ diff --git a/Meta/Websites/serenityos.org/happy/3rd/2021-07-20.png b/Meta/Websites/serenityos.org/happy/3rd/2021-07-20.png deleted file mode 100644 index 88a48687cef..00000000000 Binary files a/Meta/Websites/serenityos.org/happy/3rd/2021-07-20.png and /dev/null differ diff --git a/Meta/Websites/serenityos.org/happy/3rd/2021-07-26.png b/Meta/Websites/serenityos.org/happy/3rd/2021-07-26.png deleted file mode 100644 index 54730dc4e5d..00000000000 Binary files a/Meta/Websites/serenityos.org/happy/3rd/2021-07-26.png and /dev/null differ diff --git a/Meta/Websites/serenityos.org/happy/3rd/2021-08-02.png b/Meta/Websites/serenityos.org/happy/3rd/2021-08-02.png deleted file mode 100644 index 3572653d238..00000000000 Binary files a/Meta/Websites/serenityos.org/happy/3rd/2021-08-02.png and /dev/null differ diff --git a/Meta/Websites/serenityos.org/happy/3rd/2021-08-10.png b/Meta/Websites/serenityos.org/happy/3rd/2021-08-10.png deleted file mode 100644 index 819808f7910..00000000000 Binary files a/Meta/Websites/serenityos.org/happy/3rd/2021-08-10.png and /dev/null differ diff --git a/Meta/Websites/serenityos.org/happy/3rd/2021-08-29.png b/Meta/Websites/serenityos.org/happy/3rd/2021-08-29.png deleted file mode 100644 index b118a690151..00000000000 Binary files a/Meta/Websites/serenityos.org/happy/3rd/2021-08-29.png and /dev/null differ diff --git a/Meta/Websites/serenityos.org/happy/3rd/2021-09-12.png b/Meta/Websites/serenityos.org/happy/3rd/2021-09-12.png deleted file mode 100644 index 2d77a0acb6f..00000000000 Binary files a/Meta/Websites/serenityos.org/happy/3rd/2021-09-12.png and /dev/null differ diff --git a/Meta/Websites/serenityos.org/happy/3rd/2021-09-19.png b/Meta/Websites/serenityos.org/happy/3rd/2021-09-19.png deleted file mode 100644 index 5732926d5ba..00000000000 Binary files a/Meta/Websites/serenityos.org/happy/3rd/2021-09-19.png and /dev/null differ diff --git a/Meta/Websites/serenityos.org/happy/3rd/2021-10-02.png b/Meta/Websites/serenityos.org/happy/3rd/2021-10-02.png deleted file mode 100644 index 881c06a1572..00000000000 Binary files a/Meta/Websites/serenityos.org/happy/3rd/2021-10-02.png and /dev/null differ diff --git a/Meta/Websites/serenityos.org/happy/3rd/arstechnica.png b/Meta/Websites/serenityos.org/happy/3rd/arstechnica.png deleted file mode 100644 index 5a1ed371dd3..00000000000 Binary files a/Meta/Websites/serenityos.org/happy/3rd/arstechnica.png and /dev/null differ diff --git a/Meta/Websites/serenityos.org/happy/3rd/bgianf.jpg b/Meta/Websites/serenityos.org/happy/3rd/bgianf.jpg deleted file mode 100644 index 23927fb9f1b..00000000000 Binary files a/Meta/Websites/serenityos.org/happy/3rd/bgianf.jpg and /dev/null differ diff --git a/Meta/Websites/serenityos.org/happy/3rd/idanho.jpg b/Meta/Websites/serenityos.org/happy/3rd/idanho.jpg deleted file mode 100644 index effe39db4b8..00000000000 Binary files a/Meta/Websites/serenityos.org/happy/3rd/idanho.jpg and /dev/null differ diff --git a/Meta/Websites/serenityos.org/happy/3rd/index.html b/Meta/Websites/serenityos.org/happy/3rd/index.html deleted file mode 100644 index b0bb90f32b4..00000000000 --- a/Meta/Websites/serenityos.org/happy/3rd/index.html +++ /dev/null @@ -1,622 +0,0 @@ - - - - SerenityOS: Year 3 in review - - - -
-

SerenityOS: Year 3 in review

-
-
- - -

Hello friends! :^) - -

Today we celebrate the third birthday of SerenityOS, counting from the first commit in the - git repository, on October 10, 2018. - -

Previous birthdays: 1st, 2nd. - -

What follows is a list of interesting events from the past year, mixed with random development - screenshots and also reflections from other developers in the SerenityOS community. - -

-

Introduction to SerenityOS

- -

SerenityOS is a from-scratch desktop operating system that combines a Unix-like core - with the look&feel of 1990s productivity software. It's written in modern C++ and - goes all the way from kernel to web browser. The project aims to build everything in-house - instead of relying on third-party libraries. - -

I started building this system after - finishing a 3-month rehabilitation program for drug addiction - in 2018. I found myself with a lot of time and nothing to spend it on. So I began - building something I'd always wanted to build: my very own dream OS. - -

Parts of my development work is presented in screencast format on - my YouTube channel. - I also post monthly update videos showcasing new features there. -

- -
-

2020-12-06: Working on Reddit support in LibWeb

- -

Building a browser takes time, and there's a lot of unglamorous - work like figuring out why things don't align right. Fortunately it's - also really fun! - -

-

- -
-

2020-12-20: Interview on CppCast

- -

I went on the CppCast podcast with Jason Turner - and Rob Irving to talk about SerenityOS. - -

It was my first time doing an interview and I was really nervous about it, - but it turned out very okay! - -

-
- -
-

2020-12-20: The 2020 HXP CTF

-

- SerenityOS was once again featured in the HXP CTF. - After being in their 2019 CTF, we spent a whole bunch of time beefing up system security, - and it definitely helped: This time, only 1 team was able to find an exploit, - compared to 6 teams in the previous CTF! -

- Write-ups & exploits from the event: -

-
- -
-

2021-01-06: Reading "Hackles" on SerenityOS

- -

I was very happy to get the classic Unix geek webcomic - Hackles working in Browser. - -

-

- - - -
-

2021-02-11: vakzz's full chain exploit

-

William Bowling (vakzz) released - the first ever full chain exploit for SerenityOS, combining a browser bug and - a kernel bug to get remote root access via opening a web page! - -

Check out vakzz's excellent write-up - for a step-by-step walthrough. - -

- -
-

2021-02-13: SerenityOS developer interview: Linus Groh

- -

I wanted to introduce my YouTube audience to more of the SerenityOS - developer community, and Linus became the first guest in my developer - interview series! - -

It was really nice to shine a light on someone else doing great work on the project. - -

-
- -
-

- Developer reflections: Linus Groh - -

- -

One of my favorite aspects of the past year of SerenityOS development - is the overall progress on the browser! There's still a ton of work to - do, but we're starting to get more and more websites into a recognizable - shape - compared to a year ago, the number of blank pages and crashes - on load is reduced considerably. - -

It's also one of the most collaborative subsystems: everything from - improving spec compliance in our JavaScript engine and adding some - basic optimizations to implementing countless Web APIs, and continuous - work on CSS and DOM has been a team effort. It's great to see everyone - get comfortable, explore, and eventually become experts in their - favorite topics of browser and JS engine development! - -

It's been so much fun building all these things together, and I'm - excited to see how far we can get in another year :^) -

- - -
-

2021-03-06: Classic game "port": Diablo

- -

DevilutionX is a reverse engineered "port" of the classic game Diablo. - I ported it to SerenityOS and captured the process in a video. - To date, this is my most viewed video and thousands of people discovered - the project through this video. -

- -

I also finally beat the game! - -

-

- -
-

2021-04-01: A new direction for the project

- -

On April 1st, I posted a video announcing a new visual and spiritual direction - for the SerenityOS project. Most people got the joke :^) - -

-
- -
-

- 2021-04-10: Opening a SerenityOS Discord server - -

- -

We decided to try out Discord after seeing how it was used to great effect - in the Zig language community. - -

It's been a huge success! While our IRC channel peaked at about 170 users, - we've got well over 4000 members on Discord, and it's helped us reach new - levels of collaboration that were simply not possible with IRC. - -

It has also spawned an extremely nerdy culture of yak-related memes. - -

-

- -
-

2021-04-18: Interviewed on "Systems with JT"

- -

Programming language wizard JT invited me for an live interview - about SerenityOS and everything around it. It was my first live interview, and I was kinda nervous - but I think it went well! - -

JT also did a heartwarming video review of SerenityOS back around Christmas. - -

-
- -
-

2021-04-26: More project maintainers

- -

In the interview with JT, one of the things that came up was my own - scalability as a project maintainer. Up until this point I had been doing - all the PR review and merging myself. - -

After talking about it with JT, I realized that I needed to ask for - some help from a handful of trusted contributors. It was scary to give up - a bit of control, but in retrospect it's one of the best decisions I've made. :^) - -

At the time of writing, we now have five maintainers in addition to myself (in alphabetical order): -

- -

They each bring their own expertise and passion to the project, and they've been doing a great job - at keeping the project moving forward while growing. -

- -
-

2021-05-16: Some GUI face-lifts

- -

Sometimes I like to pick out a part of the GUI that is particularly weak - and spend some time on improving it. Here I was working on the PixelPaint - application, and also the system shutdown dialog. - -

-

-

- -
-

2021-05-27: Linus gets on GitHub Sponsors

- -

Linus becomes the second person to accept sponsorships - for his SerenityOS work. More people getting sponsored to work on SerenityOS is super cool! -

- -
-

2021-05-28: I quit my job to work on SerenityOS full time!

-

As of May of 2021, I'm receiving enough in donations to be able to support - myself while working full-time on SerenityOS! - - I wrote a blog post about it here and people were very - supportive - around - the - web. - -

I'm extremely grateful for all the support, and it's super exciting to be - able to focus on this full time! Massive thanks to everyone who has supported - me over the years! If you would like to help me out as well, check out - the links at the bottom of this page. -

- -
-

2021-06-12: Interview on Zig SHOWTIME!

- -

I was a guest on the Zig SHOWTIME variety show - from the Zig language community. The theme was - "tech, taste and soul" and the interview lasted almost 3 hours. Exhausting but fun! - -

-
- -
-

2021-06-30: 64-bit mode activated!

- -

Up until this point, SerenityOS was a 32-bit x86-only system. Then came x86_64, - much thanks to the hard work of Gunnar Beutner - who decided that the port was going to happen, and then didn't stop until it was up and running! - -

-

- -
-

- Developer reflections: Brian Gianforcaro - -

- -

The past year of Serenity development has been super exciting! One of my favorite things - to happen was the bring up of the x86_64 Kernel. Andreas started making baby steps in Feb 2021, - followed by others contributing additional fixes, until around Jun 2021 when - Gunnar Beutner started contributing tons - of patches and with the help of many others got the system booting and running on x86_64. - In my mind this was a significant symbolic step for the project and the community, onboarding - another architecture makes the system a bit more real in my mind. - -

From the community perspective I found it very inspiring how Gunnar just took the lead and - started fixing issues left and right. The community saw the momentum and started working - on fixes as well, and everyone together got the system running. - -

I wish Andreas, the SerenityOS project and community, continued success and here's hoping - for another fruitful year of fun and progress. With the - nascent aarch64 port under way by - Nico Weber, and the countless other exciting things - folks are working on, I'm excited to see what the next year has in store! :^) -

- - -
-

2021-07-08: SerenityOS Office Hours

- -

After an interesting back & forth "discussion" with my YouTube audience - that started with the question "Am I losing touch with the audience?", - I decided to put some serious effort into connecting with the audience. - -

After some experimentation, I finally arrived at the SerenityOS Office Hours - format. This is a weekly Q&A livestream that I do every Friday at 4pm Swedish Time. - People are invited to ask any technical or non-technical question about SerenityOS - and we dig into whatever topics come up. It has been well-received and I've really - enjoyed being able to answer questions interactively! - -

Check out my stream archive - on YouTube. (And come say hi when I'm live some time!) - -

- -
-

2021-07-08: A world map of SerenityOS hackers

- -

Linus created a collaborative map - of SerenityOS developers & users around the world. - -

-

- -
-

2021-07-20: TrueType renderer improvements

- -

While I'm a big fan of bitmap fonts personally, I did spend some time working - on our TrueType renderer, fixing up things like vertical alignment and glyph sizes. - -

I also did some work to support the Microsoft Tahoma - and JetBrains Mono typefaces, - seen in this screenshot! - -

-

- -
-

2021-07-26: Building a "Settings" app

- -

Until this point, all the various settings dialogs were scattered - around the system menu. I decided it was time to collect them in a - simple Settings application instead. I think it turned out quite nice! - -

-

- -
-

2021-07-26: SerenityOS developer interview: Ali Mohammadpur

- -

I did another developer interview video! This time with Ali, - who is behind many of the subsystems in Serenity (including TLS, - line editing, the spreadsheet, and more!) - -

-
- -
-

2021-08-10: Working on multi-core stability

- -

Multi-core support is still immature in SerenityOS, but we have been making some - strides forward in this area. In this screenshot, I'm successfully running Quake II - using 2 CPU's simultaneously. - -

-

- -
-

2021-08-18: ArsTechnica reviews SerenityOS

-

In mid-August, ArsTechnica ran a feature article on SerenityOS. - This came out of nowhere and was a lot of fun! -

-

- -
-

2021-08-29: Showing SerenityOS to my nephew

- -

My nephew called me on Skype while I was hacking on something, and I asked - if he wanted a tour of the operating system. He said yes, and I got this sweet - screenshot of him excitedly seeing me beat our Breakout game! - -

-

- -
-

2021-09-12: 500 contributors on GitHub!

- -

It's wild how many people have contributed - to the project at this point! - -

-

- -
-

2021-09-18: Linus Groh interviewed on CppCast

- -

It's been so cool to see Linus's journey with SerenityOS, - from not knowing C++ at all 18 months ago, to being interviewed on a major C++ podcast. - -

-
- -
-

2021-09-19: Reading the HTML spec

- -

It's a pretty cool milestone when your browser engine is strong enough - to download and display the HTML spec itself. - -

-

- -
-

- Developer reflections: Idan Horowitz - -

- -

One of the main subprojects in LibJS that was being worked on in 2021 was support for - the stage 3 Temporal proposal, - which aims to replace the old and awkward Date API - with a more modern, unified and fully-featured interface. - -

As a result of the efforts of many contributors (with some of the most notable ones - being Linus Groh - and Luke Wilde) Serenity's - LibJS contains the most fleshed out Temporal implementation out of all the popular Javascript engines. - -

- -
-

2021-10-02: Browser performance work

- -

Lately I've been doing a ton of work on browser performance, trying to - bring it to a point where it can display complex pages in a somewhat reasonable - time. - -

Here I am using Profiler to examine what appears to be memory allocation - performance in our regular expression engine. - -

The profiling system has matured quite a bit during the last year. It now - has the ability to capture full-system profiles, and we've got more visualizations - to aid in performance analysis. :^) - -

-

- -
-

Monthly update videos

- -

The tradition of the monthly SerenityOS update video is alive and well, - ever since my first-ever update video in March 2019. - -

Something new this year is that for the last couple of videos, I've been - joined by Linus in the videos. The sheer amount of things happening month-to-month - was getting hard to cover by myself, and it's great to share the stage with - someone else who cares deeply about the project as well. - -

- -

Check out the playlist on YouTube - for the full archive! -

-
- -
-

Thanks

- -

To all the awesome people who have participated in the last year, writing code, - bug reports, documentation, commenting/liking/sharing my videos, sending letters, - chilling on Discord, coming to the Office Hours livestreams, telling your friends, - etc, thank you all! - -

I'm unbelievably grateful for all the love and support this project receives! - -

And also, a huge thank you! to everyone who has supported me via - GitHub Sponsors, - Patreon, - and PayPal. Thanks to you, I'm able - to do this full time and I'm excited to see where we can push this project! - -

All right, let's keep moving forward into year number 4! - -

Andreas Kling, 2021-10-10 -
GitHub | - YouTube | - Twitter | - Patreon | - PayPal | - Store - -

-

- - - diff --git a/Meta/Websites/serenityos.org/happy/3rd/linusg.png b/Meta/Websites/serenityos.org/happy/3rd/linusg.png deleted file mode 100644 index 12f30e82794..00000000000 Binary files a/Meta/Websites/serenityos.org/happy/3rd/linusg.png and /dev/null differ diff --git a/Meta/Websites/serenityos.org/happy/3rd/usermap.png b/Meta/Websites/serenityos.org/happy/3rd/usermap.png deleted file mode 100644 index be52c6283f0..00000000000 Binary files a/Meta/Websites/serenityos.org/happy/3rd/usermap.png and /dev/null differ diff --git a/Meta/Websites/serenityos.org/happy/3rd/x86_64.png b/Meta/Websites/serenityos.org/happy/3rd/x86_64.png deleted file mode 100644 index 1456ede5d1e..00000000000 Binary files a/Meta/Websites/serenityos.org/happy/3rd/x86_64.png and /dev/null differ diff --git a/Meta/Websites/serenityos.org/happy/3rd/yakbait.png b/Meta/Websites/serenityos.org/happy/3rd/yakbait.png deleted file mode 100644 index b519c8045d4..00000000000 Binary files a/Meta/Websites/serenityos.org/happy/3rd/yakbait.png and /dev/null differ diff --git a/Meta/Websites/serenityos.org/happy/3rd/yakoverflow.png b/Meta/Websites/serenityos.org/happy/3rd/yakoverflow.png deleted file mode 100644 index b5e2cb4fc9e..00000000000 Binary files a/Meta/Websites/serenityos.org/happy/3rd/yakoverflow.png and /dev/null differ diff --git a/Meta/Websites/serenityos.org/happy/3rd/yakstack.png b/Meta/Websites/serenityos.org/happy/3rd/yakstack.png deleted file mode 100644 index 45dee006280..00000000000 Binary files a/Meta/Websites/serenityos.org/happy/3rd/yakstack.png and /dev/null differ diff --git a/Meta/Websites/serenityos.org/index.html b/Meta/Websites/serenityos.org/index.html deleted file mode 100644 index d45433d2982..00000000000 --- a/Meta/Websites/serenityos.org/index.html +++ /dev/null @@ -1,63 +0,0 @@ - - - - SerenityOS - - - -SerenityOS -

SerenityOS

-A graphical Unix-like operating system for desktop computers! - -

SerenityOS is a love letter to '90s user interfaces with a custom Unix-like core. It flatters with sincerity by stealing beautiful ideas from various other systems.

- -

Roughly speaking, the goal is a marriage between the aesthetic of late-1990s productivity software and the power-user accessibility of late-2000s *nix.

- -

This is a system by us, for us, based on the things we like.

- -

Project:

- - -

Sponsoring developers:

- - - -

Other links:

- - -

Screenshot:

- - - - - diff --git a/Meta/analyze-qemu-coverage.sh b/Meta/analyze-qemu-coverage.sh deleted file mode 100755 index 723a5f7f53c..00000000000 --- a/Meta/analyze-qemu-coverage.sh +++ /dev/null @@ -1,75 +0,0 @@ -#!/usr/bin/env bash - -set -eo pipefail - -SCRIPT_DIR="$(dirname "${0}")" -SERENITY_ROOT="$(realpath "${SCRIPT_DIR}"/..)" - -if [ -z "$SERENITY_ARCH" ]; then - SERENITY_ARCH="x86_64" -fi - -toolchain_suffix= -if [ "$SERENITY_TOOLCHAIN" = "Clang" ]; then - toolchain_suffix="clang" -fi - -BUILD_DIR="${SERENITY_ROOT}/Build/${SERENITY_ARCH}${toolchain_suffix}" -TEMP_PROFDATA="$BUILD_DIR/tmp_profile_data" - -mkdir -p "$TEMP_PROFDATA" -mkdir -p "$BUILD_DIR/mnt" - -[ -z "${ELEVATE+x}" ] && ELEVATE="sudo" -echo "ELEVATE is $ELEVATE" - -echo "[ELEVATED] Mounting _disk_image into $BUILD_DIR/mnt, this requires elevated privileges..." -"$ELEVATE" mount "$BUILD_DIR/_disk_image" "$BUILD_DIR/mnt" -echo "[ELEVATED] Copying profile data files out of $BUILD_DIR/mnt, this requires elevated privileges..." -"$ELEVATE" cp -r "$BUILD_DIR/mnt/home/anon/profiles" "$TEMP_PROFDATA/" -echo "[ELEVATED] Unmounting _disk_image, this requires elevated privileges..." -"$ELEVATE" umount "$BUILD_DIR/mnt" -echo "[ELEVATED] Making sure profile data files are owned by the current user, this requires elevated privileges..." -"$ELEVATE" chown -R "$(id -u)":"$(id -g)" "$TEMP_PROFDATA/" - - -echo "Moving profile data into $TEMP_PROFDATA directly..." -mv "$TEMP_PROFDATA"/profiles/* "$TEMP_PROFDATA/" - -echo "Discovering all binaries and shared libraries in $BUILD_DIR/Root" -# shellcheck disable=SC2156 # The recommended fix on the Shellcheck github page for this warning causes the script to not find any files at all -mapfile -d '\n' all_binaries < <(find "$BUILD_DIR"/Root -type f -exec sh -c "file {} | grep -vi relocatable | grep -Eiq ': elf (32|64)-bit'" \; -print | grep -Ev '(usr\/Tests|usr\/local|boot\/|Loader.so)') - -# FIXME: Come up with our own coverage prep script instead of using llvm's -COVERAGE_PREPARE="$BUILD_DIR/prepare-code-coverage-artifact.py" -if [ ! -f "$COVERAGE_PREPARE" ]; then - # Download coverage prep script from github - LLVM_14_RELEASE_HASH=329fda39c507e8740978d10458451dcdb21563be - SHA256_SUM=2cf1019d1df9a10c87234e0ec9c984dbb97d5543688b7f4a7387cb377ced7f21 - URL=https://raw.githubusercontent.com/llvm/llvm-project/${LLVM_14_RELEASE_HASH}/llvm/utils/prepare-code-coverage-artifact.py - - echo "Downloading prepare-code-coverage-artifact.py from ${URL}" - wget "$URL" -P "$BUILD_DIR" - - # Verify hash matches for integrity - echo "Expecting sha256sum: $SHA256_SUM" - CALC_SUM="$(sha256sum "${COVERAGE_PREPARE}" | cut -f1 -d' ')" - echo "sha256sum($COVERAGE_PREPARE) = '$CALC_SUM'" - if [ "$CALC_SUM" != "$SHA256_SUM" ]; then - # remove downloaded file to re-download on next run - rm -f "$COVERAGE_PREPARE" - echo "sha256sums mismatching, removed erroneous download. Please re-try." - exit 1 - fi -fi - -CLANG_BINDIR="${SERENITY_ROOT}/Toolchain/Local/clang/bin" - -# shellcheck disable=SC2128,SC2086 # all_binaries variable needs expanded to space separated string, not newline separated string -python3 "$COVERAGE_PREPARE" \ - --unified-report \ - -C "${SERENITY_ROOT}" \ - "$CLANG_BINDIR/llvm-profdata" "$CLANG_BINDIR/llvm-cov" \ - "$TEMP_PROFDATA/" \ - "$BUILD_DIR/reports/" \ - $all_binaries diff --git a/Meta/bochsrc b/Meta/bochsrc deleted file mode 100644 index 98480225bde..00000000000 --- a/Meta/bochsrc +++ /dev/null @@ -1,55 +0,0 @@ -# configuration file generated by Bochs -config_interface: textconfig -display_library: x -memory: host=1024, guest=1024 -romimage: file="$BXSHARE/BIOS-bochs-latest", address=0x00000000, options=none -vgaromimage: file="$BXSHARE/VGABIOS-lgpl-latest" -boot: disk -#floppy_bootsig_check: disabled=0 -#floppya: type=1_44, 1_44=".floppy-image", status=inserted, write_protected=0 -# no floppyb -ata0: enabled=true, ioaddr1=0x1f0, ioaddr2=0x3f0, irq=14 -ata0-master: type=disk, path="grub_disk_image", mode=flat, cylinders=0, heads=0, spt=0, model="Generic 1234", biosdetect=auto, translation=auto -ata0-slave: type=none -ata1: enabled=true, ioaddr1=0x170, ioaddr2=0x370, irq=15 -ata1-master: type=none -ata1-slave: type=none -ata2: enabled=false -ata3: enabled=false -optromimage1: file=none -optromimage2: file=none -optromimage3: file=none -optromimage4: file=none -optramimage1: file=none -optramimage2: file=none -optramimage3: file=none -optramimage4: file=none -pci: enabled=1, chipset=i440fx, slot1=pcivga -vga: extension=vbe, update_freq=60, realtime=0 -cpu: count=1, ips=4000000, model=bx_generic, reset_on_triple_fault=1, cpuid_limit_winnt=0, ignore_bad_msrs=1, mwait_is_nop=0 -cpuid: level=6, stepping=3, model=3, family=6, vendor_string="GenuineIntel", brand_string=" Intel(R) Pentium(R) 4 CPU " -cpuid: mmx=true, apic=xapic, simd=sse3, sse4a=false, misaligned_sse=false, sep=true -cpuid: movbe=false, adx=false, aes=false, sha=false, xsave=false, xsaveopt=false, smep=true -cpuid: smap=true, mwait=true -print_timestamps: enabled=0 -port_e9_hack: enabled=1 -private_colormap: enabled=0 -clock: sync=realtime, time0=local, rtc_sync=1 -#clock: sync=none, time0=local, rtc_sync=1 -# no cmosimage -log: - -logprefix: %t%e%d -debug: action=ignore -info: action=report -error: action=report -panic: action=ask -keyboard: type=mf, serial_delay=250, paste_delay=100000, user_shortcut=none -mouse: type=ps2, enabled=false, toggle=ctrl+alt -speaker: enabled=true, mode=system -parport1: enabled=true, file=none -parport2: enabled=false -com1: enabled=true, mode=null -com2: enabled=false -com3: enabled=false -com4: enabled=false -plugin_ctrl: e1000=1 diff --git a/Meta/build-image-extlinux.sh b/Meta/build-image-extlinux.sh deleted file mode 100755 index f6af37224bc..00000000000 --- a/Meta/build-image-extlinux.sh +++ /dev/null @@ -1,102 +0,0 @@ -#!/usr/bin/env bash - -set -e - -script_path=$(cd -P -- "$(dirname -- "$0")" && pwd -P) - -. "${script_path}/shell_include.sh" - -if [ "$(id -u)" != 0 ]; then - set +e - ${SUDO} -- "${SHELL}" -c "\"$0\" $* || exit 42" - case $? in - 1) - die "this script needs to run as root" - ;; - 42) - exit 1 - ;; - *) - exit 0 - ;; - esac -else - : "${SUDO_UID:=0}" "${SUDO_GID:=0}" -fi - -for dir in "/usr/lib/syslinux/bios" "/usr/lib/syslinux" "/usr/share/syslinux"; do - if [ -d $dir ]; then - syslinux_dir=$dir - break - fi -done -if [ -z $syslinux_dir ]; then - echo "can't find syslinux directory" - exit 1 -fi - -DISK_SIZE=$(($(disk_usage "$SERENITY_SOURCE_DIR/Base") + $(disk_usage Root) + 300)) - -echo "setting up disk image..." -dd if=/dev/zero of=extlinux_disk_image bs=1M count="${DISK_SIZE:-800}" status=none || die "couldn't create disk image" -chown "$SUDO_UID":"$SUDO_GID" extlinux_disk_image || die "couldn't adjust permissions on disk image" -echo "done" - -printf "creating loopback device... " -dev=$(losetup --find --partscan --show extlinux_disk_image) -if [ -z "$dev" ]; then - die "couldn't mount loopback device" -fi -echo "loopback device is at ${dev}" - -cleanup() { - if [ -d mnt ]; then - printf "unmounting filesystem... " - umount mnt || ( sleep 1 && sync && umount mnt ) - rmdir mnt - echo "done" - fi - - if [ -e "${dev}" ]; then - printf "cleaning up loopback device... " - losetup -d "${dev}" - echo "done" - fi -} -trap cleanup EXIT - -printf "creating partition table... " -parted -s "${dev}" mklabel msdos mkpart primary ext2 32k 100% -a minimal set 1 boot on || die "couldn't partition disk" -partition_number="p1" -echo "done" - -printf "destroying old filesystem... " -dd if=/dev/zero of="${dev}${partition_number}" bs=1M count=1 status=none || die "couldn't destroy old filesystem" -echo "done" - -printf "creating new filesystem... " -mke2fs -q -I 128 "${dev}${partition_number}" || die "couldn't create filesystem" -echo "done" - -printf "mounting filesystem... " -mkdir -p mnt -mount "${dev}${partition_number}" mnt/ || die "couldn't mount filesystem" -echo "done" - -"$script_path/build-root-filesystem.sh" - -if [ -z "$1" ]; then - extlinux_cfg="$SERENITY_SOURCE_DIR"/Meta/extlinux.conf -else - extlinux_cfg=$1 -fi - -echo "installing extlinux with $extlinux_cfg..." -mkdir -p mnt/boot/extlinux -extlinux --install mnt/boot/extlinux -cp "$extlinux_cfg" mnt/boot/extlinux/extlinux.conf -for module in mboot.c32 menu.c32 libutil.c32 libcom32.c32; do - cp $syslinux_dir/$module mnt/boot/extlinux/ -done -dd bs=440 count=1 conv=notrunc if=$syslinux_dir/mbr.bin of="$dev" -echo "done" diff --git a/Meta/build-image-grub.sh b/Meta/build-image-grub.sh deleted file mode 100755 index 89e8d0ab070..00000000000 --- a/Meta/build-image-grub.sh +++ /dev/null @@ -1,120 +0,0 @@ -#!/usr/bin/env bash - -set -e - -script_path=$(cd -P -- "$(dirname -- "$0")" && pwd -P) - -. "${script_path}/shell_include.sh" - -if [ "$(id -u)" != 0 ]; then - set +e - ${SUDO} -- "${SHELL}" -c "\"$0\" $* || exit 42" - case $? in - 1) - die "this script needs to run as root" - ;; - 42) - exit 1 - ;; - *) - exit 0 - ;; - esac -else - : "${SUDO_UID:=0}" "${SUDO_GID:=0}" -fi - -grub=$(command -v grub-install 2>/dev/null) || true -if [ -z "$grub" ]; then - grub=$(command -v grub2-install 2>/dev/null) || true -fi -if [ -z "$grub" ]; then - echo "can't find a grub-install or grub2-install binary, oh no" - exit 1 -fi -echo "using grub-install at ${grub}" - -DISK_SIZE=$(($(disk_usage "$SERENITY_SOURCE_DIR/Base") + $(disk_usage Root) + 300)) - -echo "setting up disk image..." -if [ "$1" = "ebr" ]; then - DISK_SIZE= -fi -dd if=/dev/zero of=grub_disk_image bs=1M count="${DISK_SIZE:-800}" status=none || die "couldn't create disk image" -chown "$SUDO_UID":"$SUDO_GID" grub_disk_image || die "couldn't adjust permissions on disk image" -echo "done" - -printf "creating loopback device... " -dev=$(losetup --find --partscan --show grub_disk_image) -if [ -z "$dev" ]; then - die "couldn't mount loopback device" -fi -echo "loopback device is at ${dev}" - -cleanup() { - if [ -d mnt ]; then - printf "unmounting filesystem... " - umount mnt || ( sleep 1 && sync && umount mnt ) - rmdir mnt - echo "done" - fi - - if [ -e "${dev}" ]; then - printf "cleaning up loopback device... " - losetup -d "${dev}" - echo "done" - fi -} -trap cleanup EXIT - -printf "creating partition table... " -if [ "$1" = "mbr" ]; then - parted -s "${dev}" mklabel msdos mkpart primary ext2 1MiB 100% -a minimal set 1 boot on || die "couldn't partition disk" - partition_number="p1" - partition_scheme="mbr" -elif [ "$1" = "gpt" ]; then - parted -s "${dev}" mklabel gpt mkpart BIOSBOOT ext3 1MiB 8MiB mkpart OS ext2 8MiB 290MiB set 1 bios_grub || die "couldn't partition disk" - partition_number="p2" - partition_scheme="gpt" -elif [ "$1" = "ebr" ]; then - parted -s "${dev}" mklabel msdos mkpart primary 32k 200MiB mkpart primary 200MiB 201MiB mkpart primary 201MiB 202MiB mkpart extended 250MiB 739MiB mkpart logical 372MiB 739MiB -a minimal set 1 boot on || die "couldn't partition disk" - partition_number="p5" - partition_scheme="ebr" -else - parted -s "${dev}" mklabel msdos mkpart primary ext2 1MiB 100% -a minimal set 1 boot on || die "couldn't partition disk" - partition_number="p1" - partition_scheme="mbr" -fi - -echo "done" - -printf "destroying old filesystem... " -dd if=/dev/zero of="${dev}${partition_number}" bs=1M count=1 status=none || die "couldn't destroy old filesystem" -echo "done" - -printf "creating new filesystem... " -mke2fs -q -I 128 "${dev}${partition_number}" || die "couldn't create filesystem" -echo "done" - -printf "mounting filesystem... " -mkdir -p mnt -mount "${dev}${partition_number}" mnt/ || die "couldn't mount filesystem" -echo "done" - -"$script_path/build-root-filesystem.sh" - -if [ -z "$2" ]; then - grub_cfg="$SERENITY_SOURCE_DIR"/Meta/grub-"${partition_scheme}".cfg -else - grub_cfg=$2 -fi - -echo "installing grub using $grub with $grub_cfg..." -$grub --boot-directory=mnt/boot --target=i386-pc --modules="ext2 part_msdos" "${dev}" - -if [ -d mnt/boot/grub2 ]; then - cp "$grub_cfg" mnt/boot/grub2/grub.cfg -else - cp "$grub_cfg" mnt/boot/grub/grub.cfg -fi -echo "done" diff --git a/Meta/build-image-limine.sh b/Meta/build-image-limine.sh deleted file mode 100755 index 10246c30fbd..00000000000 --- a/Meta/build-image-limine.sh +++ /dev/null @@ -1,98 +0,0 @@ -#!/usr/bin/env bash - -set -e - -script_path=$(cd -P -- "$(dirname -- "$0")" && pwd -P) - -. "${script_path}/shell_include.sh" - -if [ ! -d "limine" ]; then - echo "limine not found, the script will now build it" - git clone --depth 1 --branch v2.78.2 --single-branch https://github.com/limine-bootloader/limine - cd limine - ./autogen.sh - make all - cd .. -fi - -if [ "$(id -u)" != 0 ]; then - set +e - ${SUDO} -- "${SHELL}" -c "\"$0\" $* || exit 42" - case $? in - 1) - die "this script needs to run as root" - ;; - 42) - exit 1 - ;; - *) - exit 0 - ;; - esac -else - : "${SUDO_UID:=0}" "${SUDO_GID:=0}" -fi - -DISK_SIZE=$(($(disk_usage "$SERENITY_SOURCE_DIR/Base") + $(disk_usage Root) + 300)) - -echo "setting up disk image..." -dd if=/dev/zero of=limine_disk_image bs=1M count="${DISK_SIZE:-800}" status=none || die "couldn't create disk image" -chown "$SUDO_UID":"$SUDO_GID" limine_disk_image || die "couldn't adjust permissions on disk image" -echo "done" - -printf "creating loopback device... " -dev=$(losetup --find --partscan --show limine_disk_image) -if [ -z "$dev" ]; then - die "couldn't mount loopback device" -fi -echo "loopback device is at ${dev}" - -cleanup() { - if [ -d mnt ]; then - printf "unmounting root partition... " - umount -R mnt || ( sleep 1 && sync && umount -R mnt ) - rmdir mnt - echo "done" - fi - - if [ -d esp ]; then - printf "unmounting efi partition... " - umount -R esp || ( sleep 1 && sync && umount -R esp ) - rmdir esp - echo "done" - fi - - if [ -e "${dev}" ]; then - printf "cleaning up loopback device... " - losetup -d "${dev}" - echo "done" - fi -} - -trap cleanup EXIT - -printf "creating partition table... " -parted -s "${dev}" mklabel gpt mkpart EFI fat32 1MiB 10MiB mkpart ROOT ext2 10MiB 100% set 1 esp on || die "couldn't partition disk" -echo "done" - -printf "creating new filesystems... " -mkfs.vfat -F 32 "${dev}p1" || die "couldn't create efi filesystem" -mke2fs -q -I 128 "${dev}p2" || die "couldn't create root filesystem" -echo "done" - -printf "mounting filesystems... " -mkdir -p esp -mount "${dev}p1" esp || die "couldn't mount efi filesystem" -mkdir -p mnt -mount "${dev}p2" mnt || die "couldn't mount root filesystem" -echo "done" - -"$script_path/build-root-filesystem.sh" - -echo "installing limine" -mkdir -p esp/EFI/BOOT -cp limine/bin/limine.sys esp -cp limine/bin/BOOTX64.EFI esp/EFI/BOOT -cp "$SERENITY_SOURCE_DIR"/Meta/limine.cfg esp -limine/bin/limine-install "${dev}" -echo "done" diff --git a/Meta/build-image-qemu.sh b/Meta/build-image-qemu.sh deleted file mode 100755 index 16b3310760b..00000000000 --- a/Meta/build-image-qemu.sh +++ /dev/null @@ -1,187 +0,0 @@ -#!/usr/bin/env bash -set -e - -SCRIPT_DIR="$(dirname "${0}")" - -. "${SCRIPT_DIR}/shell_include.sh" - -USE_FUSE2FS=0 - -if [ "$(id -u)" != 0 ]; then - if [ -x "$FUSE2FS_PATH" ] && $FUSE2FS_PATH --help 2>&1 |grep fakeroot > /dev/null; then - USE_FUSE2FS=1 - else - set +e - ${SUDO} -- "${SHELL}" -c "\"$0\" $* || exit 42" - case $? in - 1) - die "this script needs to run as root" - ;; - 42) - exit 1 - ;; - *) - exit 0 - ;; - esac - fi -else - : "${SUDO_UID:=0}" "${SUDO_GID:=0}" -fi - -# Prepend the toolchain qemu directory so we pick up QEMU from there -PATH="$SCRIPT_DIR/../Toolchain/Local/qemu/bin:$PATH" - -INODE_SIZE=128 -INODE_COUNT=$(($(inode_usage "$SERENITY_SOURCE_DIR/Base") + $(inode_usage Root))) -INODE_COUNT=$((INODE_COUNT + 2000)) # Some additional inodes for toolchain files, could probably also be calculated -DISK_SIZE_BYTES=$((($(disk_usage "$SERENITY_SOURCE_DIR/Base") + $(disk_usage Root) ) * 1024 * 1024)) -DISK_SIZE_BYTES=$((DISK_SIZE_BYTES + (INODE_COUNT * INODE_SIZE))) - -if [ -z "$SERENITY_DISK_SIZE_BYTES" ]; then - # Try to use heuristics to guess a good disk size and inode count. - # The disk must notably fit: - # * Data blocks (for both files and directories), - # * Indirect/doubly indirect/triply indirect blocks, - # * Inodes and block bitmaps for each block group, - # * Plenty of extra free space and free inodes. - DISK_SIZE_BYTES=$((DISK_SIZE_BYTES * 2)) - INODE_COUNT=$((INODE_COUNT * 7)) -else - if [ "$DISK_SIZE_BYTES" -gt "$SERENITY_DISK_SIZE_BYTES" ]; then - die "SERENITY_DISK_SIZE_BYTES is set to $SERENITY_DISK_SIZE_BYTES but required disk size is $DISK_SIZE_BYTES bytes" - fi - DISK_SIZE_BYTES="$SERENITY_DISK_SIZE_BYTES" -fi - -if [ -n "$SERENITY_INODE_COUNT" ]; then - if [ "$INODE_COUNT" -gt "$SERENITY_INODE_COUNT" ]; then - die "SERENITY_INODE_COUNT is set to $SERENITY_INODE_COUNT but required inode count is roughly $INODE_COUNT" - fi - INODE_COUNT="$SERENITY_INODE_COUNT" -fi - -nearest_power_of_2() { - local n=$1 - local p=1 - while [ $p -lt "$n" ]; do - p=$((p*2)) - done - echo $p -} -if [ "$SERENITY_ARCH" = "aarch64" ] || { [ -n "$SERENITY_USE_SDCARD" ] && [ "$SERENITY_USE_SDCARD" -eq 1 ]; }; then - # SD cards must have a size that is a power of 2. The Aarch64 port loads from an SD card. - DISK_SIZE_BYTES=$(nearest_power_of_2 "$DISK_SIZE_BYTES") -fi - -USE_EXISTING=0 - -if [ -f _disk_image ]; then - USE_EXISTING=1 - - echo "checking existing image" - result=0 - "$E2FSCK_PATH" -f -y _disk_image || result=$? - if [ $result -ge 4 ]; then - rm -f _disk_image - USE_EXISTING=0 - echo "failed, not using existing image" - else - echo "done" - fi -fi - -if [ $USE_EXISTING -eq 1 ]; then - OLD_DISK_SIZE_BYTES=$(wc -c < _disk_image) - if [ "$DISK_SIZE_BYTES" -gt "$OLD_DISK_SIZE_BYTES" ]; then - echo "resizing disk image..." - qemu-img resize -f raw _disk_image "$DISK_SIZE_BYTES" || die "could not resize disk image" - if ! "$RESIZE2FS_PATH" _disk_image; then - rm -f _disk_image - USE_EXISTING=0 - echo "failed, not using existing image" - fi - echo "done" - fi -fi - -if [ $USE_EXISTING -ne 1 ]; then - printf "setting up disk image... " - qemu-img create -q -f raw _disk_image "$DISK_SIZE_BYTES" || die "could not create disk image" - chown "$SUDO_UID":"$SUDO_GID" _disk_image || die "could not adjust permissions on disk image" - echo "done" - - printf "creating new filesystem... " - if [ "$(uname -s)" = "OpenBSD" ]; then - VND=$(vnconfig _disk_image) - (echo "e 0"; echo 83; echo n; echo 0; echo "*"; echo "quit") | fdisk -e "$VND" - newfs_ext2fs -D "${INODE_SIZE}" -n "${INODE_COUNT}" "/dev/r${VND}i" || die "could not create filesystem" - else - "${MKE2FS_PATH}" -q -I "${INODE_SIZE}" -N "${INODE_COUNT}" _disk_image || die "could not create filesystem" - fi - echo "done" -fi - -printf "mounting filesystem... " -mkdir -p mnt -use_genext2fs=0 -if [ $USE_FUSE2FS -eq 1 ]; then - mount_cmd="$FUSE2FS_PATH _disk_image mnt/ -o fakeroot,rw" -elif [ "$(uname -s)" = "Darwin" ]; then - mount_cmd="fuse-ext2 _disk_image mnt -o rw+,allow_other,uid=501,gid=20" -elif [ "$(uname -s)" = "OpenBSD" ]; then - VND=$(vnconfig _disk_image) - mount_cmd="mount -t ext2fs "/dev/${VND}i" mnt/" -elif [ "$(uname -s)" = "FreeBSD" ]; then - MD=$(mdconfig _disk_image) - mount_cmd="fuse-ext2 -o rw+,direct_io "/dev/${MD}" mnt/" -else - mount_cmd="mount _disk_image mnt/" -fi -if ! eval "$mount_cmd"; then - if command -v genext2fs 1>/dev/null ; then - echo "mount failed but genext2fs exists, use it instead" - use_genext2fs=1 - else - die "could not mount filesystem and genext2fs is missing" - fi -else - echo "done" -fi - -cleanup() { - if [ -d mnt ]; then - if [ $use_genext2fs = 0 ] ; then - printf "unmounting filesystem... " - if [ $USE_FUSE2FS -eq 1 ]; then - fusermount -u mnt || (sleep 1 && sync && fusermount -u mnt) - else - umount mnt || ( sleep 1 && sync && umount mnt ) - fi - rmdir mnt - else - rm -rf mnt - fi - - if [ "$(uname -s)" = "OpenBSD" ]; then - vnconfig -u "$VND" - elif [ "$(uname -s)" = "FreeBSD" ]; then - mdconfig -d -u "$MD" - fi - echo "done" - fi -} -trap cleanup EXIT - -script_path=$(cd -P -- "$(dirname -- "$0")" && pwd -P) -"$script_path/build-root-filesystem.sh" - -if [ $use_genext2fs = 1 ]; then - # regenerate new image, since genext2fs is unable to reuse the previously written image. - # genext2fs is very slow in generating big images, so I use a smaller image here. size can be updated - # if it's not enough. - # not using "-I $INODE_SIZE" since it hangs. Serenity handles whatever default this uses instead. - genext2fs -B 4096 -b $((DISK_SIZE_BYTES / 4096)) -N "${INODE_COUNT}" -d mnt _disk_image || die "try increasing image size (genext2fs -b)" - # if using docker with shared mount, file is created as root, so make it writable for users - chmod 0666 _disk_image -fi diff --git a/Meta/build-manpages-website.sh b/Meta/build-manpages-website.sh deleted file mode 100755 index 6d2dd95709d..00000000000 --- a/Meta/build-manpages-website.sh +++ /dev/null @@ -1,122 +0,0 @@ -#!/bin/bash -# shellcheck disable=SC1004 # literal backslash+linefeed is intended - -set -e - -script_path=$(cd -P -- "$(dirname -- "$0")" && pwd -P) -cd "${script_path}/.." - -export LC_ALL=C # Make the directory order reproducible -export MAN_DIR=Base/usr/share/man/ - -if [[ -e output ]]; then - echo "Directory 'output/' already exists. Delete it first." - exit 1 -fi - -# Use case-insensitive sorting, which will lead to more intuitive index pages. -SORT="sort -f" - -# Prepare output directories -for d in "${MAN_DIR}"*/; do - dir_name=$(basename "$d") - section="${dir_name/man}" - mkdir -p "output/${dir_name}" -done - -# Convert markdown to html - -# If you're here because your local results are different from the website: -# Check that your pandoc version matches the pandoc-version specified in manpages.yaml. - -for md_file in $(find "${MAN_DIR}" -iname '*.md' | ${SORT}); do - relative_path="$(realpath --relative-to="${MAN_DIR}" "${md_file}")" - section="${relative_path%%/*}" - section_number="${section#man}" - filename="${relative_path#*/}" - name="${filename%.md}" - output_file="output/${section}/${name}.html" - - echo "Generating $md_file -> $output_file" - mkdir -p "$(dirname "${output_file}")" - pandoc -f gfm -t html5 -s \ - -B Meta/Websites/man.serenityos.org/banner-preamble.inc \ - --lua-filter=Meta/convert-markdown-links.lua \ - --lua-filter=Meta/Websites/man.serenityos.org/add-anchors.lua \ - --metadata title="${name}(${section_number}) - SerenityOS man pages" \ - -o "${output_file}" \ - "${md_file}" & -done - -# Wait for all pandoc executions to finish so that man page indices are always correct. -# shellcheck disable=SC2046 # Word splitting is intentional here -wait $(jobs -p) - -# Generate man page listings through pandoc -for section_directory in output/*/; do - section=$(basename "${section_directory}") - section_number="${section#man}" - case "${section_number}" in - 1) section_title="User Programs";; - 2) section_title="System Calls";; - 3) section_title="Library Functions";; - 4) section_title="Special Files";; - 5) section_title="File Formats";; - 6) section_title="Games";; - 7) section_title="Miscellanea";; - 8) section_title="Sysadmin Tools";; - *) section_title="SerenityOS man pages"; echo "WARNING: Unrecognized section ${section_number}?!";; - esac - output="output/${section}/index.html" - - echo "Generating section ${section_number} index -> ${output}" - pandoc -f gfm -t html5 -s \ - -B Meta/Websites/man.serenityos.org/banner-preamble.inc \ - --metadata title="Section ${section_number} - ${section_title}" \ - -o "${output}" \ - <( - for f in $(find "${section_directory}" -iname '*.html' | ${SORT}); do - filename=$(realpath --relative-to="${section_directory}" "$f") - if [[ "$filename" == "index.html" ]]; then - continue - fi - filename_no_extension="${filename%.html}" - # Generate indentation by subdirectory count: Replace each slash by the two Markdown indentation spaces, then remove all non-space characters. - indentation=$(echo "${filename_no_extension}" | sed -e 's/ //g' -e 's/\// /g' -e 's/[^ ]//g') - name="$(basename "${filename}")" - name="${name%.html}" - echo "${indentation}- [${name}](${filename})" - done - ) & -done - -# Generate main landing page listings through pandoc -echo 'Generating main pages' -pandoc -f gfm -t html5 -s \ - -B Meta/Websites/man.serenityos.org/banner-preamble.inc \ - --metadata title="SerenityOS man pages" \ - -o output/index.html \ - Meta/Websites/man.serenityos.org/index.md & -pandoc -f gfm -t html5 -s \ - -B Meta/Websites/man.serenityos.org/banner-preamble.inc \ - --metadata title="Can't run applications" \ - -o output/cant-run-application.html \ - Meta/Websites/man.serenityos.org/cant-run-application.md & - -# Copy pre-made files -echo 'Copying images' -rsync -a Meta/Websites/man.serenityos.org/banner.png output/ & -rsync -a Base/usr/share/man/man7/LibDSP_classes.svg output/ & -find Base/usr/share/man/ -iname '*.png' -exec rsync -a {} output/ \; & - -# Copy icons -mkdir output/icons - -while read -r p; do - rsync -a --relative Base/res/icons/./"$p" output/icons/ -done < icons.txt - -rm icons.txt - -# shellcheck disable=SC2046 # Word splitting is intentional here -wait $(jobs -p) diff --git a/Meta/build-native-partition.sh b/Meta/build-native-partition.sh deleted file mode 100755 index 2cc62f5dae6..00000000000 --- a/Meta/build-native-partition.sh +++ /dev/null @@ -1,56 +0,0 @@ -#!/usr/bin/env bash - -set -e - -script_path=$(cd -P -- "$(dirname -- "$0")" && pwd -P) - -. "${script_path}/shell_include.sh" - -cleanup() { - if [ -d mnt ]; then - umount mnt || ( sleep 1 && sync && umount mnt ) - rmdir mnt - echo "done" - fi -} - -if [ "$(id -u)" != 0 ]; then - set +e - ${SUDO} -- "${SHELL}" -c "\"$0\" $* || exit 42" - case $? in - 1) - die "this script needs to run as root" - ;; - 42) - exit 1 - ;; - *) - exit 0 - ;; - esac -else - : "${SUDO_UID:=0}" "${SUDO_GID:=0}" -fi - -if [ -z "$SERENITY_TARGET_INSTALL_PARTITION" ]; then - die "SERENITY_TARGET_INSTALL_PARTITION environment variable was not set!" -fi - -printf "verifying partition %s is already ext2... " "$SERENITY_TARGET_INSTALL_PARTITION" -if file -sL "$SERENITY_TARGET_INSTALL_PARTITION" 2>&1 | grep "ext2" > /dev/null; then - echo "done" -else - die "$SERENITY_TARGET_INSTALL_PARTITION is not an ext2 partition!" -fi - -trap cleanup EXIT - -printf "mounting filesystem on device %s... " "$SERENITY_TARGET_INSTALL_PARTITION" -mkdir -p mnt -if ! eval "mount $SERENITY_TARGET_INSTALL_PARTITION mnt/"; then - die "could not mount existing ext2 filesystem on $SERENITY_TARGET_INSTALL_PARTITION" -else - echo "done" -fi - -"$script_path/build-root-filesystem.sh" diff --git a/Meta/build-root-filesystem.sh b/Meta/build-root-filesystem.sh deleted file mode 100755 index 2c824f6d252..00000000000 --- a/Meta/build-root-filesystem.sh +++ /dev/null @@ -1,224 +0,0 @@ -#!/bin/sh - -set -e - -wheel_gid=1 -phys_gid=3 -utmp_gid=5 -window_uid=13 -window_gid=13 - -die() { - echo "die: $*" - exit 1 -} - -[ -z "$SERENITY_SOURCE_DIR" ] && die "SERENITY_SOURCE_DIR is not set" -[ -d "$SERENITY_SOURCE_DIR/Base" ] || die "$SERENITY_SOURCE_DIR/Base doesn't exist" - -umask 0022 - -printf "installing base system... " -if ! command -v rsync >/dev/null; then - die "Please install rsync." -fi - -if rsync --chown 2>&1 | grep "missing argument" >/dev/null; then - rsync -aH --chown=0:0 --inplace --update "$SERENITY_SOURCE_DIR"/Base/ mnt/ - rsync -aH --chown=0:0 --inplace --update Root/ mnt/ -else - rsync -aH --inplace --update "$SERENITY_SOURCE_DIR"/Base/ mnt/ - rsync -aH --inplace --update Root/ mnt/ - chown -R 0:0 mnt/ -fi - -SERENITY_ARCH="${SERENITY_ARCH:-x86_64}" - -if [ "$SERENITY_TOOLCHAIN" = "Clang" ]; then - TOOLCHAIN_DIR="$SERENITY_SOURCE_DIR"/Toolchain/Local/clang/ - rsync -aH --update -t "$TOOLCHAIN_DIR"/lib/"$SERENITY_ARCH"-pc-serenity/* mnt/usr/lib - mkdir -p mnt/usr/include/"$SERENITY_ARCH"-pc-serenity - rsync -aH --update -t -r "$TOOLCHAIN_DIR"/include/c++ mnt/usr/include - rsync -aH --update -t -r "$TOOLCHAIN_DIR"/include/"$SERENITY_ARCH"-pc-serenity/c++ mnt/usr/include/"$SERENITY_ARCH"-pc-serenity -else - rsync -aH --update -t -r "$SERENITY_SOURCE_DIR"/Toolchain/Local/"$SERENITY_ARCH"/"$SERENITY_ARCH"-pc-serenity/lib/* mnt/usr/lib - rsync -aH --update -t -r "$SERENITY_SOURCE_DIR"/Toolchain/Local/"$SERENITY_ARCH"/"$SERENITY_ARCH"-pc-serenity/include/c++ mnt/usr/include -fi - -# If umask was 027 or similar when the repo was cloned, -# file permissions in Base/ are too restrictive. Restore -# the permissions needed in the image. -chmod -R g+rX,o+rX "$SERENITY_SOURCE_DIR"/Base/* mnt/ - -chmod 660 mnt/etc/WindowServer.ini -chown $window_uid:$window_gid mnt/etc/WindowServer.ini -echo "/bin/sh" > mnt/etc/shells - -if [ -f mnt/bin/su ]; then - chown 0:$wheel_gid mnt/bin/su - chmod 4750 mnt/bin/su -fi -if [ -f mnt/bin/passwd ]; then - chown 0:$wheel_gid mnt/bin/passwd - chmod 4755 mnt/bin/passwd -fi -if [ -f mnt/bin/ping ]; then - chown 0:$wheel_gid mnt/bin/ping - chmod 4755 mnt/bin/ping -fi -if [ -f mnt/bin/traceroute ]; then - chown 0:$wheel_gid mnt/bin/traceroute - chmod 4755 mnt/bin/traceroute -fi -if [ -f mnt/bin/keymap ]; then - chown 0:$phys_gid mnt/bin/keymap - chmod 4750 mnt/bin/keymap -fi -if [ -f mnt/bin/shutdown ]; then - chown 0:$phys_gid mnt/bin/shutdown - chmod 4750 mnt/bin/shutdown -fi -if [ -f mnt/bin/reboot ]; then - chown 0:$phys_gid mnt/bin/reboot - chmod 4750 mnt/bin/reboot -fi -if [ -f mnt/bin/pls ]; then - chown 0:$wheel_gid mnt/bin/pls - chmod 4750 mnt/bin/pls -fi -if [ -f mnt/bin/Escalator ]; then - chown 0:$wheel_gid mnt/bin/Escalator - chmod 4750 mnt/bin/Escalator -fi -if [ -f mnt/bin/utmpupdate ]; then - chown 0:$utmp_gid mnt/bin/utmpupdate - chmod 2755 mnt/bin/utmpupdate -fi -if [ -f mnt/bin/timezone ]; then - chown 0:$phys_gid mnt/bin/timezone - chmod 4750 mnt/bin/timezone -fi -if [ -f mnt/usr/Tests/Kernel/TestExt2FS ]; then - chown 0:0 mnt/usr/Tests/Kernel/TestExt2FS - chmod 4755 mnt/usr/Tests/Kernel/TestExt2FS -fi -if [ -f mnt/usr/Tests/Kernel/TestMemoryDeviceMmap ]; then - chown 0:0 mnt/usr/Tests/Kernel/TestMemoryDeviceMmap - chmod 4755 mnt/usr/Tests/Kernel/TestMemoryDeviceMmap -fi -if [ -f mnt/usr/Tests/Kernel/TestProcFSWrite ]; then - chown 0:0 mnt/usr/Tests/Kernel/TestProcFSWrite - chmod 4755 mnt/usr/Tests/Kernel/TestProcFSWrite -fi -if [ -f mnt/usr/Tests/Kernel/TestLoopDevice ]; then - chown 0:0 mnt/usr/Tests/Kernel/TestLoopDevice - chmod 4755 mnt/usr/Tests/Kernel/TestLoopDevice -fi - -if [ -f mnt/res/kernel.map ]; then - chmod 0400 mnt/res/kernel.map -fi - -if [ -f mnt/boot/Kernel ]; then - chmod 0400 mnt/boot/Kernel -fi - -if [ -f mnt/boot/Kernel.debug ]; then - chmod 0400 mnt/boot/Kernel.debug -fi - -if [ -f mnt/bin/network-settings ]; then - chown 0:0 mnt/bin/network-settings - chmod 500 mnt/bin/network-settings -fi - -chmod 600 mnt/etc/shadow -chmod 755 mnt/res/devel/templates/*.postcreate -echo "done" - -printf "creating initial filesystem structure... " -for dir in bin etc proc mnt tmp boot mod var/run usr/local usr/bin; do - mkdir -p mnt/$dir -done -chmod 700 mnt/boot -chmod 700 mnt/mod -chmod 1777 mnt/tmp -echo "done" - -printf "creating utmp file... " -echo "{}" > mnt/var/run/utmp -chown 0:$utmp_gid mnt/var/run/utmp -chmod 664 mnt/var/run/utmp -echo "done" - -printf "setting up device nodes folder... " -mkdir -p mnt/dev -echo "done" - -printf "setting up sysfs folder... " -mkdir -p mnt/sys -echo "done" - -printf "installing users... " -mkdir -p mnt/root -mkdir -p mnt/home/anon -mkdir -p mnt/home/anon/Desktop -mkdir -p mnt/home/anon/Downloads -mkdir -p mnt/home/anon/Music -mkdir -p mnt/home/anon/Pictures -mkdir -p mnt/home/nona -# FIXME: Handle these test copies using CMake install rules -rm -fr mnt/home/anon/Tests/js-tests mnt/home/anon/Tests/cpp-tests -mkdir -p mnt/home/anon/Tests/cpp-tests/ -cp "$SERENITY_SOURCE_DIR"/README.md mnt/home/anon/ -cp -r "$SERENITY_SOURCE_DIR"/Userland/Libraries/LibJS/Tests mnt/home/anon/Tests/js-tests -cp -r "$SERENITY_SOURCE_DIR"/Userland/Libraries/LibCodeComprehension/Cpp/Tests mnt/home/anon/Tests/cpp-tests/comprehension -cp -r "$SERENITY_SOURCE_DIR"/Userland/Libraries/LibCpp/Tests/parser mnt/home/anon/Tests/cpp-tests/parser -cp -r "$SERENITY_SOURCE_DIR"/Userland/Libraries/LibCpp/Tests/preprocessor mnt/home/anon/Tests/cpp-tests/preprocessor -cp -r "$SERENITY_SOURCE_DIR"/Userland/Libraries/LibJS/Tests/test-common.js mnt/home/anon/Tests/wasm-tests -cp -r "$SERENITY_SOURCE_DIR"/Userland/Applications/Spreadsheet/Tests mnt/home/anon/Tests/spreadsheet-tests - -if [ -n "$SERENITY_COPY_SOURCE" ] ; then - printf "\ncopying Serenity's source... " - rm -fr mnt/home/anon/Source/serenity - mkdir -p mnt/home/anon/Source/serenity - git clone --depth=1 file://"$SERENITY_SOURCE_DIR" mnt/home/anon/Source/serenity - rm -fr mnt/home/anon/Source/serenity/.git -fi - -chmod 700 mnt/root -chmod 700 mnt/home/anon -chmod 700 mnt/home/nona -chown -R 100:100 mnt/home/anon -chown -R 200:100 mnt/home/nona -echo "done" - -printf "adding some desktop icons... " -ln -sf /bin/Browser mnt/home/anon/Desktop/Ladybird -ln -sf /bin/TextEditor mnt/home/anon/Desktop/Text\ Editor -ln -sf /bin/Help mnt/home/anon/Desktop/ -ln -sf /home/anon mnt/home/anon/Desktop/Home -chown -R 100:100 mnt/home/anon/Desktop -echo "done" - -printf "installing shortcuts... " -ln -sf /bin/PackageManager mnt/bin/pkg -ln -sf Shell mnt/bin/sh -ln -sf test mnt/bin/[ -ln -sf less mnt/bin/more -ln -sf /bin/env mnt/usr/bin/env -ln -sf /bin/SystemServer mnt/init -echo "done" - -printf "installing 'checksum' variants... " -ln -sf checksum mnt/bin/b2sum -ln -sf checksum mnt/bin/md5sum -ln -sf checksum mnt/bin/sha1sum -ln -sf checksum mnt/bin/sha256sum -ln -sf checksum mnt/bin/sha512sum -echo "done" - -# Run local sync script, if it exists -if [ -f "${SERENITY_SOURCE_DIR}/sync-local.sh" ]; then - sh "${SERENITY_SOURCE_DIR}/sync-local.sh" -fi diff --git a/Meta/convert-markdown-links.lua b/Meta/convert-markdown-links.lua deleted file mode 100644 index eb1d55341e0..00000000000 --- a/Meta/convert-markdown-links.lua +++ /dev/null @@ -1,22 +0,0 @@ -function Link(el) - el.target = string.gsub(el.target, "file:///bin/.*", "/cant-run-application.html") - el.target = string.gsub(el.target, "help://man/([^/]*)/(.*)", "/man%1/%2.html") - return el -end - -function Image(el) - -- Images that are not icons are always copied to the website root. - if el.src:find("^/res/icons/") == nil then - el.src = "/" .. el.src - return el - end - - local pattern = "/res/icons/(.*)" - local image = string.gsub(el.src, pattern, "%1") - - el.src = "/icons/" .. image - file = io.open("icons.txt", "a+") - file:write(image .. "\n") - file:close() - return el -end diff --git a/Meta/debug-kernel.sh b/Meta/debug-kernel.sh deleted file mode 100755 index 67a38cf5c6d..00000000000 --- a/Meta/debug-kernel.sh +++ /dev/null @@ -1,76 +0,0 @@ -#!/usr/bin/env bash - -SCRIPT_DIR="$(dirname "${0}")" - -if [ -z "$SERENITY_ARCH" ]; then - SERENITY_ARCH="x86_64" -fi - -# Set this environment variable to override the default debugger. -# -if [ -z "$SERENITY_KERNEL_DEBUGGER" ]; then - # Prepend the toolchain's GDB bin directory so we pick up GDB from there - PATH="$SCRIPT_DIR/../Toolchain/Local/$SERENITY_ARCH-gdb/bin:$PATH" - # GDB used to be installed directly inside the toolchain bin directory - PATH="$SCRIPT_DIR/../Toolchain/Local/$SERENITY_ARCH/bin:$PATH" - - if command -v "$SERENITY_ARCH-pc-serenity-gdb" >/dev/null; then - SERENITY_KERNEL_DEBUGGER="$SERENITY_ARCH-pc-serenity-gdb" - elif command -v "$SERENITY_ARCH-elf-gdb" >/dev/null; then - SERENITY_KERNEL_DEBUGGER="$SERENITY_ARCH-elf-gdb" - elif command -v gdb >/dev/null && gdb -ex 'set architecture' -ex 'quit' 2>&1 | grep "${SERENITY_ARCH//_/-}"; then - SERENITY_KERNEL_DEBUGGER="gdb" - else - echo "Error: No suitable GDB installation found." >&2 - echo "Please install $SERENITY_ARCH-elf-gdb or build it with Toolchain/BuildGDB.sh $SERENITY_ARCH" >&2 - # Prevent tmux from dying instantly by waiting for user input - read -rp "Press Enter to exit" - exit 1 - fi -fi - -toolchain_suffix= -if [ "$SERENITY_TOOLCHAIN" = "Clang" ]; then - toolchain_suffix="clang" -fi - -# The QEMU -s option (enabled by default in ./run) sets up a debugger -# remote on localhost:1234. So point our debugger there, and inform -# the debugger which binary to load symbols, etc from. -# -if [ "$SERENITY_ARCH" = "x86_64" ]; then - gdb_arch=i386:x86-64 - prekernel_image=Prekernel64 - kernel_base=0x2000200000 -elif [ "$SERENITY_ARCH" = "aarch64" ]; then - gdb_arch=aarch64:armv8-r - prekernel_image=Prekernel - kernel_base=0x0 -elif [ "$SERENITY_ARCH" = "riscv64" ]; then - gdb_arch=riscv:rv64 - prekernel_image=Prekernel - kernel_base=0x0 -fi - -# FIXME: This doesn't work when running QEMU inside the WSL2 VM -if command -v wslpath >/dev/null; then - gdb_host=$(powershell.exe "(Test-Connection -ComputerName (hostname) -Count 1).IPV4Address.IPAddressToString" | tr -d '\r\n') -else - gdb_host=${SERENITY_HOST_IP:-127.0.0.1} -fi - - -exec $SERENITY_KERNEL_DEBUGGER \ - -ex "file $SCRIPT_DIR/../Build/${SERENITY_ARCH:-x86_64}$toolchain_suffix/Kernel/Prekernel/$prekernel_image" \ - -ex "set confirm off" \ - -ex "directory $SCRIPT_DIR/../Build/${SERENITY_ARCH:-x86_64}$toolchain_suffix/" \ - -ex "add-symbol-file $SCRIPT_DIR/../Build/${SERENITY_ARCH:-x86_64}$toolchain_suffix/Kernel/Kernel_shared_object -o $kernel_base" \ - -ex "set confirm on" \ - -ex "set arch $gdb_arch" \ - -ex "set print frame-arguments none" \ - -ex "set print asm-demangle on" \ - -ex "target remote ${gdb_host}:1234" \ - -ex "source $SCRIPT_DIR/serenity_gdb.py" \ - -ex "layout asm" \ - -ex "fs next" \ - "$@" diff --git a/Meta/export-argsparser-manpages.sh b/Meta/export-argsparser-manpages.sh deleted file mode 100755 index ea587a9c8bb..00000000000 --- a/Meta/export-argsparser-manpages.sh +++ /dev/null @@ -1,91 +0,0 @@ -#!/bin/sh - -set -e - -script_path=$(cd -P -- "$(dirname -- "$0")" && pwd -P) -cd "${script_path}/.." - -if ! command -v tar >/dev/null 2>&1 ; then - echo "Please install tar!" - exit 1 -fi - -if [ "$#" = "0" ]; then - VERIFY_GIT_STATE=n -elif [ "$#" = "1" ] && [ "$1" = "--verify-git-state" ]; then - VERIFY_GIT_STATE=y -else - echo "USAGE: $0 [--verify-git-state]" - echo "This script runs Serenity and exports a set of manpages through ArgsParser," - echo "and places them in Base/usr/share/man/." - echo "If --verify-git-state is given, this script verifies that this does not modify" - echo "the git state, i.e. that all exported manpages already were in the repository" - echo "with the exact same content." - exit 1 -fi - -echo "This script assumes passwordless sudo." -sudo true - -if [ -z "$BUILD_DIR" ]; then - if [ -z "$SERENITY_ARCH" ]; then - export SERENITY_ARCH="x86_64" - echo "SERENITY_ARCH not given. Assuming ${SERENITY_ARCH}." - fi - BUILD_DIR=Build/"$SERENITY_ARCH" - echo "BUILD_DIR not given. Assuming ${BUILD_DIR}." -fi - -if [ -e fsmount ]; then - echo "Directory 'fsmount' already exists." - echo "Manual cleanup needed. You might also need to unmount first." - exit 1 -fi - -if ! [ -d Base/usr/share/man/ ]; then - echo "Base/usr/share/man/ does not exist. How did that happen?! o.O" - exit 1 -fi - -echo "Using 'ninja run' to generate manpages ..." -export SERENITY_RUN="ci" -export SERENITY_KERNEL_CMDLINE="graphics_subsystem_mode=off panic=shutdown system_mode=generate-manpages" -# The 'sed' gets rid of the clear-screen escape sequence. -ninja -C "$BUILD_DIR" -- run | sed -re 's,''c,,' -echo - -mkdir fsmount -sudo mount -t ext2 -o loop,rw "$BUILD_DIR"/_disk_image fsmount - -if sudo test -f "fsmount/root/generate_manpages_error.log"; then - echo ":^( Generating manpages failed, error log:" - sudo cat fsmount/root/generate_manpages_error.log - - sudo umount fsmount - rmdir fsmount - - exit 1 -fi - -echo "Extracting generated manpages ..." -# 'cp' would create the new files as root, but we don't want that. -sudo tar -C fsmount/root/generated_manpages --create . | tar -C Base/usr/share/man/ --extract -sudo umount fsmount -rmdir fsmount - -echo "Successfully (re-)generated manpages in Base/usr/share/man/" - -if [ "$VERIFY_GIT_STATE" = "y" ]; then - echo "Verifying git state ..." - if [ "" != "$(git clean -n Base/usr/share/man)" ] || ! git diff --quiet Base/usr/share/man; then - echo "Failed: There are missing and/or outdated manpages." - echo "$ git status Base/usr/share/man" - git status Base/usr/share/man - echo "$ git diff Base/usr/share/man" - git diff Base/usr/share/man - echo "You may need to run ./Meta/export-argsparser-manpages.sh on your system and commit/squash the resulting changes." - exit 1 - else - echo "Verified: No missing or outdated manpages. Great!" - fi -fi diff --git a/Meta/extlinux.conf b/Meta/extlinux.conf deleted file mode 100644 index 855a5004957..00000000000 --- a/Meta/extlinux.conf +++ /dev/null @@ -1,26 +0,0 @@ -UI menu.c32 -PROMPT 0 - -MENU TITLE SerenityOS Boot menu -TIMEOUT 10 -DEFAULT SerenityOS - -LABEL SerenityOS - MENU LABEL SerenityOS - KERNEL mboot.c32 - APPEND ../Prekernel root=/dev/hda1 --- ../Kernel - -LABEL SerenityOSText - MENU LABEL SerenityOS (text mode) - KERNEL mboot.c32 - APPEND ../Prekernel root=/dev/hda1 graphics_subsystem_mode=off --- ../Kernel - -LABEL SerenityOSNoACPI - MENU LABEL SerenityOS (No ACPI) - KERNEL mboot.c32 - APPEND ../Prekernel root=/dev/hda1 acpi=off --- ../Kernel - -LABEL SerenityOSSerialDebug - MENU LABEL SerenityOS (with serial debug) - KERNEL mboot.c32 - APPEND ../Prekernel root=/dev/hda1 serial_debug --- ../Kernel diff --git a/Meta/grub-ebr.cfg b/Meta/grub-ebr.cfg deleted file mode 100644 index e1066302f1f..00000000000 --- a/Meta/grub-ebr.cfg +++ /dev/null @@ -1,22 +0,0 @@ -timeout=1 - -menuentry 'SerenityOS (normal)' { - root=hd0,5 - multiboot /boot/Kernel root="lun0:0:0;part3" -} - -menuentry 'SerenityOS (text mode)' { - root=hd0,5 - multiboot /boot/Kernel graphics_subsystem_mode=off root="lun0:0:0;part3" -} - -menuentry 'SerenityOS (No ACPI)' { - root=hd0,5 - multiboot /boot/Kernel root="lun0:0:0;part3" acpi=off -} - -menuentry 'SerenityOS (with serial debug)' { - root=hd0,5 - multiboot /boot/Kernel serial_debug root="lun0:0:0;part3" -} - diff --git a/Meta/grub-gpt.cfg b/Meta/grub-gpt.cfg deleted file mode 100644 index 9b83bc8175b..00000000000 --- a/Meta/grub-gpt.cfg +++ /dev/null @@ -1,21 +0,0 @@ -timeout=1 - -menuentry 'SerenityOS (normal)' { - root=hd0,2 - multiboot /boot/Kernel root="lun0:0:0;part1" -} - -menuentry 'SerenityOS (text mode)' { - root=hd0,2 - multiboot /boot/Kernel graphics_subsystem_mode=off root="lun0:0:0;part1" -} - -menuentry 'SerenityOS (No ACPI)' { - root=hd0,2 - multiboot /boot/Kernel root="lun0:0:0;part1" acpi=off -} - -menuentry 'SerenityOS (with serial debug)' { - root=hd0,2 - multiboot /boot/Kernel serial_debug root="lun0:0:0;part1" -} diff --git a/Meta/grub-mbr.cfg b/Meta/grub-mbr.cfg deleted file mode 100644 index b8af9642c31..00000000000 --- a/Meta/grub-mbr.cfg +++ /dev/null @@ -1,21 +0,0 @@ -timeout=1 - -menuentry 'SerenityOS (normal)' { - root=hd0,1 - multiboot /boot/Kernel root="lun0:0:0;part0" -} - -menuentry 'SerenityOS (text mode)' { - root=hd0,1 - multiboot /boot/Kernel graphics_subsystem_mode=off root="lun0:0:0;part0" -} - -menuentry 'SerenityOS (No ACPI)' { - root=hd0,1 - multiboot /boot/Kernel root="lun0:0:0;part0" acpi=off -} - -menuentry 'SerenityOS (with serial debug)' { - root=hd0,1 - multiboot /boot/Kernel serial_debug root="lun0:0:0;part0" -} diff --git a/Meta/limine.cfg b/Meta/limine.cfg deleted file mode 100644 index bca48838feb..00000000000 --- a/Meta/limine.cfg +++ /dev/null @@ -1,25 +0,0 @@ -TIMEOUT=3 - -:SerenityOS (normal) -PROTOCOL=multiboot1 -CMDLINE=root=/dev/hda2 -KERNEL_PATH=boot://2/boot/Prekernel -MODULE_PATH=boot://2/boot/Kernel - -:SerenityOS (text mode) -PROTOCOL=multiboot1 -CMDLINE=graphics_subsystem_mode=off root=/dev/hda2 -KERNEL_PATH=boot://2/boot/Prekernel -MODULE_PATH=boot://2/boot/Kernel - -:SerenityOS (no ACPI) -PROTOCOL=multiboot1 -CMDLINE=root=/dev/hda2 acpi=off -KERNEL_PATH=boot://2/boot/Prekernel -MODULE_PATH=boot://2/boot/Kernel - -:SerenityOS (with serial output) -PROTOCOL=multiboot1 -CMDLINE=root=/dev/hda2 -KERNEL_PATH=boot://2/boot/Prekernel -MODULE_PATH=boot://2/boot/Kernel diff --git a/Meta/lint-ci.sh b/Meta/lint-ci.sh index 3710af4374a..6ef8e7b919c 100755 --- a/Meta/lint-ci.sh +++ b/Meta/lint-ci.sh @@ -5,12 +5,6 @@ set -e script_path=$(cd -P -- "$(dirname -- "$0")" && pwd -P) cd "${script_path}/.." || exit 1 -ports=true -if [ "$1" == "--no-ports" ]; then - ports=false - shift -fi - RED='\033[0;31m' GREEN='\033[0;32m' NC='\033[0m' # No Color @@ -23,14 +17,11 @@ for cmd in \ Meta/check-ak-test-files.sh \ Meta/check-debug-flags.sh \ Meta/check-emoji.py \ - Meta/check-markdown.sh \ Meta/check-newlines-at-eof.py \ Meta/check-png-sizes.sh \ Meta/check-style.py \ Meta/lint-executable-resources.sh \ - Meta/lint-gml-format.sh \ Meta/lint-gn.sh \ - Meta/lint-keymaps.py \ Meta/lint-prettier.sh \ Meta/lint-python.sh \ Meta/lint-shell-scripts.sh; do @@ -60,19 +51,4 @@ else ((FAILURES+=1)) fi -# lint-ports.py is handled separately as it scans all Ports/ all the time. -# This is fine when running lint-ci.sh from the PR validation workflow. -# However when running from the pre-commit workflow it takes an excessive -# amount of time. This condition allows the pre-commit program to detect -# when Ports/ files have changed and only invoke lint-ports.py when needed. -# -if [ "$ports" = true ]; then - if Meta/lint-ports.py; then - echo -e "[${GREEN}OK${NC}]: Meta/lint-ports.py" - else - echo -e "[${RED}FAIL${NC}]: Meta/lint-ports.py" - ((FAILURES+=1)) - fi -fi - exit "${FAILURES}" diff --git a/Meta/lint-executable-resources.sh b/Meta/lint-executable-resources.sh index a7069c3f10a..46d9f1aef71 100755 --- a/Meta/lint-executable-resources.sh +++ b/Meta/lint-executable-resources.sh @@ -7,9 +7,9 @@ cd "$script_path/.." if [ "$(uname -s)" = "Darwin" ]; then # MacOS's find does not support '-executable' OR '-perm /mode'. - BAD_FILES=$(find Base/etc/ Base/res/ Base/www/ -type f -perm +111) + BAD_FILES=$(find Base/res/ -type f -perm +111) else - BAD_FILES=$(find Base/etc/ Base/res/ Base/www/ -type f -executable) + BAD_FILES=$(find Base/res/ -type f -executable) fi if [ -n "${BAD_FILES}" ] diff --git a/Meta/lint-gml-format.sh b/Meta/lint-gml-format.sh deleted file mode 100755 index 3106977fa37..00000000000 --- a/Meta/lint-gml-format.sh +++ /dev/null @@ -1,37 +0,0 @@ -#!/usr/bin/env bash - -set -e - -trap 'git diff --exit-code' EXIT - -script_path=$(cd -P -- "$(dirname -- "$0")" && pwd -P) -cd "${script_path}/.." || exit 1 - -if [ -z "${GML_FORMAT:-}" ] ; then - if ! [ -d Build/lagom/ ] ; then - echo "Directory Build/lagom/ does not exist. Skipping GML formatting." - exit 0 - fi - if ! [ -r Build/lagom/bin/gml-format ] ; then - echo "Lagom executable gml-format was not built. Skipping GML formatting." - echo "To enable this check, you may need to run './Meta/serenity.sh build lagom' first." - exit 0 - fi - GML_FORMAT="Build/lagom/bin/gml-format" -fi - -if [ "$#" -gt "0" ] ; then - # We're in the middle of a pre-commit run, so we should only check the files that have - # actually changed. The reason is that "git ls-files | grep" on the entire repo takes - # about 100ms. That is perfectly fine during a CI run, but becomes noticeable during a - # pre-commit hook. It is unnecessary to check the entire repository on every single - # commit, so we save some time here. - for file in "$@"; do - if [[ "${file}" =~ \.gml ]]; then - echo "$file" - fi - done -else - find AK Base Documentation Kernel Meta Ports Tests Userland -type f -name '*.gml' -print -fi \ -| xargs -r "${GML_FORMAT}" -i diff --git a/Meta/lint-keymaps.py b/Meta/lint-keymaps.py deleted file mode 100755 index 162db782767..00000000000 --- a/Meta/lint-keymaps.py +++ /dev/null @@ -1,157 +0,0 @@ -#!/usr/bin/env python3 - -import json -import os -import sys - -PERMITTED_MAPS = ['map', 'shift_map', 'alt_map', 'altgr_map', 'shift_altgr_map'] -REQUIRED_MAPS = ['map', 'shift_map', 'alt_map'] -# See Userland/Libraries/LibKeyboard/CharacterMapFile.cpp -# and Userland/Libraries/LibKeyboard/CharacterMap.cpp. -GOOD_MAP_LENGTHS = {90, 128} - - -def report(filename, problem): - """Print a lint problem to stdout. - - Args: - filename (str): keymap filename - problem (str): problem message - """ - print('{}: {}'.format(filename, problem)) - - -def validate_single_map(filename, mapname, values): - """Validate a key map. - - Args: - filename (str): keymap filename - mapname (str): map name (altgr_map, alt_map, shift_altgr_map) - values (list): key values - - Returns: - bool: key map is valid - """ - - all_good = True - - if not isinstance(values, list): - report(filename, '"{}" is not an array'.format(mapname)) - return False # Cannot continue other checks - - if not any(values): - report(filename, 'no values set in {}'.format(mapname)) - all_good = False - - for i, c in enumerate(values): - if len(c) > 1: - report(filename, 'more than one character ("{}") for charmap index {} of {}'.format(c, i, mapname)) - all_good = False - - if len(values) == 0: - report(filename, 'map {} is empty.'.format(mapname)) - all_good = False - - if len(values) not in GOOD_MAP_LENGTHS: - report(filename, 'length {} of map {} is suspicious. Off-by-one?'.format(len(values), mapname)) - all_good = False - - return all_good - - -def validate_fullmap(filename, fullmap): - """Validate a full key map for all map names (including maps for key modifiers). - - Args: - filename (str): keymap filename - fullmap (dict): key mappings - - Returns: - bool: keymap file contains valid key mappings - """ - - all_good = True - - if not isinstance(fullmap, dict): - report(filename, 'is not an object') - return False # Cannot continue other checks - - for name, map_ in fullmap.items(): - if name not in PERMITTED_MAPS: - report(filename, 'contains unknown entry {}'.format(name)) - all_good = False - - all_good &= validate_single_map(filename, name, map_) - - for name in REQUIRED_MAPS: - if name not in fullmap: - report(filename, 'map {} is missing'.format(name)) - all_good = False - - if 'altgr_map' in fullmap and 'alt_map' in fullmap and fullmap['altgr_map'] == fullmap['alt_map']: - report(filename, 'altgr_map is identical to alt_map. Remove altgr_map for the same effect.') - report(filename, '(Or add new characters!)') - all_good = False - - if 'shift_altgr_map' in fullmap and 'alt_map' in fullmap and fullmap['shift_altgr_map'] == fullmap['alt_map']: - report(filename, 'shift_altgr_map is identical to alt_map. Remove shift_altgr_map for the same effect.') - report(filename, '(Or add new characters!)') - all_good = False - - return all_good - - -def run_with(filenames): - """Check list of keymap files for errors. - - Args: - filenames (list): keymap files to check - - Returns: - bool: All keymap files are valid - """ - - passed = 0 - for filename in filenames: - with open(filename, 'r') as fp: - fullmap = json.load(fp) - if validate_fullmap(filename, fullmap): - passed += 1 - - print('{} out of {} keymaps passed.'.format(passed, len(filenames))) - return passed == len(filenames) - - -def list_files_here(): - """Retrieve a list of all '.json' files in the working directory. - - Returns: - list: JSON filenames - """ - - filelist = [] - for filename in os.listdir(): - if filename.endswith('.json'): - filelist.append(filename) - else: - report(filename, 'weird filename (ignored)') - # Files are in "filesystem" order. Sort them for slightly more - # aesthetically pleasing output. - filelist.sort() - return filelist - - -def run_here(): - """Check all keymap files in the working directory for errors. - - Returns: - bool: All keymap files are valid - """ - - return run_with(list_files_here()) - - -if __name__ == '__main__': - os.chdir(os.path.dirname(__file__) + "/../Base/res/keymaps/") - if not run_here(): - sys.exit(1) diff --git a/Meta/lint-ports.py b/Meta/lint-ports.py deleted file mode 100755 index 4fb39e2eff7..00000000000 --- a/Meta/lint-ports.py +++ /dev/null @@ -1,449 +0,0 @@ -#!/usr/bin/env python3 - -import os -import re -import stat -import sys -import subprocess -from pathlib import Path -from tempfile import NamedTemporaryFile - -# Matches e.g. "| [`bash`](bash/) | GNU Bash | 5.0 | https://www.gnu.org/software/bash/ |" -# and captures "bash" in group 1, "bash/" in group 2, "" in group 3, "GNU Bash" in group 4, "5.0" in group 5 -# and "https://www.gnu.org/software/bash/" in group 6. -PORT_TABLE_REGEX = re.compile( - r'^\| \[`([^`]+)`\]\(([^\)]+)\)([^\|]+) \| ([^\|]+) \| ([^\|]+?) \| ([^\|]+) \|+$', re.MULTILINE -) - -# Matches non-abbreviated git hashes -GIT_HASH_REGEX = re.compile(r'^[0-9a-f]{40}$') - -PORT_TABLE_FILE = 'AvailablePorts.md' -IGNORE_FILES = { - '.gitignore', - '.port_include.sh', - '.strip_env.sh', - PORT_TABLE_FILE, - 'build_all.sh', - 'build_installed.sh', - 'README.md', - '.hosted_defs.sh' -} - -# Matches port names in Ports/foo/ReadMe.md -PORT_NAME_REGEX = re.compile(r'([ .()[\]{}\w-]+)\.patch') -REQUIRE_GIT_PATCHES = True -GIT_PATCH_SUBJECT_RE = re.compile(r'Subject: (.*)\n') - - -def read_port_table(filename): - """Open a file and find all PORT_TABLE_REGEX matches. - - Args: - filename (str): filename - - Returns: - set: all PORT_TABLE_REGEX matches - """ - ports = {} - with open(filename, 'r') as fp: - matches = PORT_TABLE_REGEX.findall(fp.read()) - for match in matches: - line_len = sum([len(part) for part in match]) - ports[match[0]] = { - "dir_ref": match[1], - "name": match[2].strip(), - "version": match[4].strip(), - "url": match[5].strip(), - "line_len": line_len - } - return ports - - -def read_port_dirs(): - """Check Ports directory for unexpected files and check each port has an executable package.sh file. - - Returns: - list: all ports (set), no errors encountered (bool) - """ - - ports = {} - all_good = True - for entry in os.listdir(): - if entry in IGNORE_FILES: - continue - if not os.path.isdir(entry): - print(f"Ports/{entry} is neither a port (not a directory) nor an ignored file?!") - all_good = False - continue - if os.listdir(entry) == []: - continue - if not os.path.exists(entry + '/package.sh'): - print(f"Ports/{entry}/ is missing its package.sh?!") - all_good = False - continue - if not os.stat(entry + '/package.sh')[stat.ST_MODE] & stat.S_IXUSR: - print(f"Ports/{entry}/package.sh is not executable?!") - all_good = False - ports[entry] = get_port_properties(entry) - - return ports, all_good - - -PORT_PROPERTIES = ('port', 'version', 'files') - - -def resolve_script_values(value: str, props: dict) -> str: - """Resolve all ${...} values in a string. - - Args: - value (str): string to resolve - props (dict): dict of properties to resolve from - - Returns: - str: resolved string - """ - for match in re.finditer(r'\$\{([^}]+)\}', value): - key = match.group(1) - if key in props: - value = value.replace(match.group(0), props[key]) - return value - - -def get_script_props(dir: str, script_name: str, props: dict, depth: int = 0, max_depth: int = 10) -> dict: - """Parse a script file and return a dict of properties. - - Args: - dir (str): root directory of script - script_name (str): name of script to parse - props (dict): dict of properties to resolve from - depth (int): current depth of recursion - max_depth (int): maximum depth of recursion - - Returns: - dict: dict of properties - """ - if depth > max_depth: - print(f"Maximum recursion depth exceeded while parsing {dir}/{script_name}") - return props - - buffer: str = "" - for line in open(f"{dir}/{script_name}", 'r'): - # Ignore comments (search in reverse to ignore # in strings) - if line.rfind("#") > min(line.rfind('"'), line.rfind("'"), 0): - line = line[0:line.rfind("#")] - - line = line.rstrip() - buffer += line - - if "=" in buffer: - [key, value] = buffer.split("=", 1) - - if (key.startswith(" ") or key.isspace()): - buffer = "" - continue - - if (value.startswith(('"', "'"))): - if (value.endswith(value[0]) and len(value) > 1): - value = value[1:-1] - else: - buffer += "\n" - continue - - props[key] = resolve_script_values(value, props) - buffer = "" - elif buffer.startswith('source'): - resolved_path = resolve_script_values(buffer, props).split(' ', 1)[1] - props = get_script_props(dir, resolved_path, props, depth + 1, max_depth) - buffer = "" - else: - buffer = "" - return props - - -def get_port_properties(port): - """Retrieves common port properties from its package.sh file. - - Returns: - dict: keys are values from PORT_PROPERTIES, values are from the package.sh file - """ - props = get_script_props(port, 'package.sh', {}) - props = {prop: props[prop] if prop in props else '' for prop in PORT_PROPERTIES} - return props - - -def check_package_files(ports): - """Check port package.sh file for required properties. - - Args: - ports (list): List of all ports to check - - Returns: - bool: no errors encountered - """ - - all_good = True - for port in ports.keys(): - package_file = f"{port}/package.sh" - if not os.path.exists(package_file): - continue - props = ports[port] - - if props['port'] != port: - print(f"Ports/{port} should use '{port}' for 'port' but is using '{props['port']}' instead") - all_good = False - - for prop in PORT_PROPERTIES: - if props[prop] == '': - print(f"Ports/{port} is missing required property '{prop}'") - all_good = False - - return all_good - - -def get_and_check_port_patch_list(ports): - """Checks all port patches and returns the port list/properties - - Args: - ports (list): List of all ports to check - - Returns: - all_good (bool): No errors encountered - all_properties (dict): Mapping of port to port properties - """ - all_port_properties = {} - all_good = True - - for port in ports: - patches_directory = f"{port}/patches" - - if not os.path.exists(patches_directory): - continue - - if not os.path.isdir(patches_directory): - print(f"Ports/{port}/patches exists, but is not a directory. This is not right!") - all_good = False - continue - - patches_path = Path(patches_directory) - patches_readme_path = patches_path / "ReadMe.md" - patch_files = set(patches_path.glob("*.patch")) - non_patch_files = set(patches_path.glob("*")) - patch_files - {patches_readme_path} - - port_properties = { - "patches_path": patches_path, - "patches_readme_path": patches_readme_path, - "patch_files": patch_files, - "non_patch_files": non_patch_files - } - all_port_properties[port] = port_properties - - if len(non_patch_files) != 0: - print(f"Ports/{port}/patches contains the following non-patch files:", - ', '.join(x.name for x in non_patch_files)) - all_good = False - - return all_good, all_port_properties - - -def check_descriptions_for_port_patches(patches): - """Ensure that ports containing patches have them documented. - - Args: - patches (dict): Dictionary mapping ports to all their patches - - Returns: - bool: no errors encountered - """ - - all_good = True - for port, properties in patches.items(): - patches_readme_path = properties["patches_readme_path"] - patch_files = properties["patch_files"] - - readme_file_exists = patches_readme_path.exists() - if len(patch_files) == 0: - print(f"Ports/{port}/patches exists, but contains no patches", end="") - if readme_file_exists: - print(", yet it contains a ReadMe.md") - else: - print() - all_good = False - continue - - if not readme_file_exists: - print(f"Ports/{port}/patches contains patches but no ReadMe.md describing them") - all_good = False - continue - - with open(str(patches_readme_path), 'r', encoding='utf-8') as f: - readme_contents = [] - for line in f: - if not line.startswith('#'): - continue - match = PORT_NAME_REGEX.search(line) - if match: - readme_contents.append(match.group(1)) - - patch_names = set(Path(x).stem for x in patch_files) - - for patch_name in patch_names: - if patch_name not in readme_contents: - print(f"Ports/{port}/patches/{patch_name}.patch does not appear to be described in" - " the corresponding ReadMe.md") - all_good = False - - for patch_name in readme_contents: - if patch_name not in patch_names: - print(f"Ports/{port}/patches/{patch_name}.patch is described in ReadMe.md, " - "but does not actually exist") - all_good = False - - return all_good - - -def try_parse_git_patch(path_to_patch): - with open(path_to_patch, 'rb') as f: - contents_of_patch = f.read() - - with NamedTemporaryFile('r+b') as message_file: - res = subprocess.run( - f"git mailinfo {message_file.name} /dev/null", - shell=True, - capture_output=True, - input=contents_of_patch) - - if res.returncode != 0: - return None - - message = message_file.read().decode('utf-8') - subject = GIT_PATCH_SUBJECT_RE.search(res.stdout.decode("utf-8")) - if subject: - message = subject.group(1) + "\n" + message - - return message - - -def check_patches_are_git_patches(patches): - """Ensure that all patches are patches made by (or compatible with) `git format-patch`. - - Args: - patches (dict): Dictionary mapping ports to all their patches - - Returns: - bool: no errors encountered - """ - - all_good = True - - for port, properties in patches.items(): - for patch_path in properties["patch_files"]: - result = try_parse_git_patch(patch_path) - if not result: - print(f"Ports/{port}/patches: {patch_path.stem} does not appear to be a valid " - "git patch.") - all_good = False - continue - return all_good - - -def check_available_ports(from_table, ports): - """Check AvailablePorts.md for correct properties. - - Args: - from_table (dict): Ports table from AvailablePorts.md - ports (dict): Dictionary with port properties from package.sh - - Returns: - bool: no errors encountered - """ - - all_good = True - - previous_line_len = None - - for port in from_table.keys(): - if previous_line_len is None: - previous_line_len = from_table[port]["line_len"] - if previous_line_len != from_table[port]["line_len"]: - print(f"Table row for port {port} is improperly aligned with other rows.") - all_good = False - else: - previous_line_len = from_table[port]["line_len"] - - actual_ref = from_table[port]["dir_ref"] - expected_ref = f"{port}/" - if actual_ref != expected_ref: - print(( - f'Directory link target in AvailablePorts.md for port {port} is ' - f'incorrect, expected "{expected_ref}", found "{actual_ref}"' - )) - all_good = False - - actual_version = from_table[port]["version"] - expected_version = ports[port]["version"] - if GIT_HASH_REGEX.match(expected_version): - expected_version = expected_version[0:7] - if expected_version == "git": - expected_version = "" - if actual_version != expected_version: - print(( - f'Version in AvailablePorts.md for port {port} is incorrect, ' - f'expected "{expected_version}", found "{actual_version}"' - )) - all_good = False - - return all_good - - -def run(): - """Check Ports directory and package files for errors.""" - - from_table = read_port_table(PORT_TABLE_FILE) - ports, all_good = read_port_dirs() - - from_table_set = set(from_table.keys()) - ports_set = set(ports.keys()) - - if list(from_table.keys()) != sorted(from_table.keys(), key=str.lower): - all_good = False - print('AvailablePorts.md is not in the correct order, please ensure that all ports are sorted as follows:') - for port in sorted(from_table.keys(), key=str.lower): - print(f" {port}") - - if from_table_set - ports_set: - all_good = False - print('AvailablePorts.md lists ports that do not appear in the file system:') - for port in sorted(from_table_set - ports_set): - print(f" {port}") - - if ports_set - from_table_set: - all_good = False - print('AvailablePorts.md is missing the following ports:') - for port in sorted(ports_set - from_table_set): - print(f" {port}") - - if not check_package_files(ports): - all_good = False - - if not check_available_ports(from_table, ports): - all_good = False - - patch_list_good, port_properties = get_and_check_port_patch_list(ports.keys()) - all_good = all_good and patch_list_good - - if not check_descriptions_for_port_patches(port_properties): - all_good = False - - if REQUIRE_GIT_PATCHES and not check_patches_are_git_patches(port_properties): - all_good = False - - if not all_good: - sys.exit(1) - - print('No issues found.') - - -if __name__ == '__main__': - os.chdir(f"{os.path.dirname(__file__)}/../Ports") - run() diff --git a/Meta/new-project.sh b/Meta/new-project.sh deleted file mode 100755 index be60745732c..00000000000 --- a/Meta/new-project.sh +++ /dev/null @@ -1,100 +0,0 @@ -#!/usr/bin/env bash - -set -euo pipefail - -script_path="$(cd -P -- "$(dirname -- "$0")" && pwd -P)" -cd "${script_path}/.." - -script_name="$(basename "$0")" - -function list_templates() { - echo "Available templates:" - for file in ./Base/res/devel/templates/*.ini; do - printf ' %s - ' "$(basename "${file%%.ini}")" - awk -F "=" '/Description/ { print $2 }' "$file" - done -} - -function usage() { - local return_code=${1:-0} - - cat < serenity.files diff --git a/Meta/run.py b/Meta/run.py deleted file mode 100755 index ab7b026e7c1..00000000000 --- a/Meta/run.py +++ /dev/null @@ -1,949 +0,0 @@ -#!/usr/bin/env python3 - -# Copyright (c) 2018-2023, the SerenityOS developers. -# Copyright (c) 2023, kleines Filmröllchen -# -# SPDX-License-Identifier: BSD-2-Clause - -from __future__ import annotations - -import os -import re -import sys -from dataclasses import dataclass, field -from enum import Enum, unique -from itertools import chain, repeat -from os import access, environ -from pathlib import Path -from shutil import which -from subprocess import run -from typing import Any, Callable, Literal -import shlex - -QEMU_MINIMUM_REQUIRED_MAJOR_VERSION = 6 -QEMU_MINIMUM_REQUIRED_MINOR_VERSION = 2 - - -class RunError(Exception): - pass - - -@unique -class Arch(Enum): - """SerenityOS architecture, not host architecture.""" - - Aarch64 = "aarch64" - RISCV64 = "riscv64" - x86_64 = "x86_64" - - -@unique -class QEMUKind(Enum): - """VM distinctions determining which hardware acceleration technology *might* be used.""" - - # Linux and anything that may or may not have KVM, including WSL with Linux QEMU. - Other = "kvm" - # WSL with native Windows QEMU (and possibly WHPX). - NativeWindows = "whpx" - # MacOS with HVF. - MacOS = "hvf" - # Serenity on Serenity with ported QEMU - SerenityOS = "tcg" - - -@unique -class MachineType(Enum): - Default = "" - QEMUTap = "qtap" - QEMUWithoutNetwork = "qn" - QEMUExtLinux = "qextlinux" - QEMU35 = "q35" - QEMU35Grub = "q35grub" - QEMUGrub = "qgrub" - CI = "ci" - Limine = "limine" - Bochs = "b" - MicroVM = "microvm" - ISAPC = "isapc" - - def uses_grub(self) -> bool: - return self in [MachineType.QEMU35Grub, MachineType.QEMUGrub] - - def is_q35(self) -> bool: - return self in [MachineType.QEMU35Grub, MachineType.QEMU35] - - def supports_pc_speaker(self) -> bool: - """Whether the pcspk-audiodev option is allowed for this machine type.""" - return self in [ - MachineType.Default, - MachineType.QEMUTap, - MachineType.QEMUWithoutNetwork, - MachineType.QEMUExtLinux, - MachineType.QEMU35, - MachineType.QEMU35Grub, - MachineType.QEMUGrub, - MachineType.Limine, - ] - - -def arguments_generator(prefix: str) -> Any: - """ - Construct an argument generator that returns some prefix and the member value(s) if the member value - is not None, or returns an empty list otherwise. - The member value is the return value of the function decorated with this decorator. - If a default is provided, in this case we return [prefix, default] instead. - Many of our configurable QEMU arguments work like this. - """ - - def decorate_function(member_accessor: Callable[[Configuration], str | list[str]]): - def generate_arguments(self: Configuration) -> list[str]: - member_value = member_accessor(self) - if member_value is not None: - if type(member_value) is list: - # apply the prefix to every element of the list - return list(chain(*zip(repeat(prefix), member_value))) - # NOTE: the typechecker gets confused and can't figure out that - # type(member_value) is *always* str here. - elif type(member_value) is str: - return [prefix, member_value] - return [] - - return generate_arguments - - return decorate_function - - -@dataclass -class Configuration: - """Run configuration, populated from command-line or environment variable data.""" - - # ## Programs and environmental configuration - virtualization_support: bool = False - bochs_binary: Path = Path("bochs") - qemu_binary: Path | None = None - qemu_kind: QEMUKind | None = None - kvm_usable: bool | None = None - architecture: Arch | None = None - serenity_src: Path | None = None - - # ## High-level run configuration - machine_type: MachineType = MachineType.Default - enable_gdb: bool = False - enable_gl: bool = False - # FIXME: Replace these three flags by a boot drive enum, see FIXME for boot_drive below. - nvme_enable: bool = True - sd_enable: bool = False - usb_boot_enable: bool = False - virtio_block_enable: bool = False - screen_count: int = 1 - host_ip: str = "127.0.0.1" - ethernet_device_type: str = "e1000" - disk_image: Path = Path("_disk_image") - - # ## Low-level QEMU configuration - # QEMU -append - kernel_cmdline: list[str] = field(default_factory=lambda: ["hello"]) - # QEMU -m - ram_size: str | None = "2G" - # QEMU -cpu - qemu_cpu: str | None = "max" - # QEMU -smp - cpu_count: int | None = 2 - # QEMU -machine - qemu_machine: str | None = None - # QEMU -usb - enable_usb: bool = False - # QEMU -audiodev - audio_backend: str | None = "none,id=snd0" - # Each is a QEMU -device related to audio. - audio_devices: list[str] = field(default_factory=list) - # QEMU -vga - vga_type: str | None = "none" - # QEMU -display; if None, will omit the option and let QEMU figure out which backend to use on its own. - display_backend: str | None = None - # A QEMU -device for the graphics card. - display_device: str | None = "VGA,vgamem_mb=64" - # QEMU -netdev - network_backend: str | None = None - # A QEMU -device for networking. - # Note that often, there are other network devices in the generic device list, added by specific machine types. - network_default_device: str | None = None - # QEMU -drive - # FIXME: Make an enum for the various boot drive options to handle boot drive selection more cleanly. - boot_drive: str | None = None - # Each is a QEMU -chardev - character_devices: list[str] = field(default_factory=list) - # Each is a QEMU -device - devices: list[str] = field(default_factory=list) - - # ## Argument lists and methods generating them - - # Argument list pertaining to Kernel and Prekernel image(s) - kernel_and_initrd_arguments: list[str] = field(default_factory=list) - # Argument list provided by the user for performing packet logging - packet_logging_arguments: list[str] = field(default_factory=list) - # Various arguments relating to SPICE setup - spice_arguments: list[str] = field(default_factory=list) - # Arbitrary extra arguments - extra_arguments: list[str] = field(default_factory=list) - - @property - @arguments_generator(prefix="-accel") - def accelerator_arguments(self) -> str | None: - return self.qemu_kind.value if self.virtualization_support and (self.qemu_kind is not None) else "tcg" - - @property - def kernel_cmdline_arguments(self) -> list[str]: - return ["-append", " ".join(self.kernel_cmdline)] if len(self.kernel_cmdline) != 0 else [] - - @property - @arguments_generator(prefix="-m") - def ram_arguments(self) -> str | None: - return self.ram_size - - @property - @arguments_generator(prefix="-cpu") - def cpu_arguments(self) -> str | None: - return self.qemu_cpu - - @property - @arguments_generator(prefix="-smp") - def smp_arguments(self) -> str | None: - return str(self.cpu_count) if self.cpu_count is not None else None - - @property - @arguments_generator(prefix="-machine") - def machine_arguments(self) -> str | None: - return self.qemu_machine - - @property - def usb_arguments(self) -> list[str]: - return ["-usb"] if self.enable_usb else [] - - @property - @arguments_generator(prefix="-audiodev") - def audio_backend_arguments(self) -> str | None: - return self.audio_backend - - @property - @arguments_generator(prefix="-device") - def audio_devices_arguments(self) -> list[str] | None: - return self.audio_devices - - @property - @arguments_generator(prefix="-vga") - def vga_arguments(self) -> str | None: - return self.vga_type - - @property - @arguments_generator(prefix="-display") - def display_backend_arguments(self) -> str | None: - return self.display_backend - - @property - @arguments_generator(prefix="-device") - def display_device_arguments(self) -> str | None: - return self.display_device - - @property - def network_backend_arguments(self) -> list[str]: - return ["-netdev", self.network_backend] if self.network_backend is not None else ["-nic", "none"] - - @property - @arguments_generator(prefix="-device") - def network_default_arguments(self) -> str | None: - return self.network_default_device - - @property - @arguments_generator(prefix="-drive") - def boot_drive_arguments(self) -> str | None: - return self.boot_drive - - @property - @arguments_generator(prefix="-chardev") - def character_device_arguments(self) -> list[str]: - return self.character_devices - - @property - @arguments_generator(prefix="-device") - def device_arguments(self) -> list[str]: - return self.devices - - def add_device(self, device: str): - self.devices.append(device) - - def add_devices(self, devices: list[str]): - self.devices.extend(devices) - - -def kvm_usable() -> bool: - return access("/dev/kvm", os.R_OK | os.W_OK) - - -def determine_qemu_kind() -> QEMUKind: - if which("wslpath") is not None and environ.get("SERENITY_NATIVE_WINDOWS_QEMU", "1") == "1": - # Assume native Windows QEMU for now, - # we might discard that assuption later when we properly - # look for the binary. - return QEMUKind.NativeWindows - if sys.platform == "darwin": - return QEMUKind.MacOS - if os.uname().sysname == "SerenityOS": - return QEMUKind.SerenityOS - return QEMUKind.Other - - -def determine_serenity_arch() -> Arch: - arch = environ.get("SERENITY_ARCH") - if arch == "aarch64": - return Arch.Aarch64 - if arch == "riscv64": - return Arch.RISCV64 - if arch == "x86_64": - return Arch.x86_64 - raise RunError("Please specify a valid SerenityOS architecture") - - -def determine_machine_type() -> MachineType: - provided_machine_type = environ.get("SERENITY_RUN") - if provided_machine_type is not None: - try: - value = MachineType(provided_machine_type) - except ValueError: - raise RunError(f"{provided_machine_type} is not a valid SerenityOS machine type") - return value - return MachineType.Default - - -def detect_bochs() -> Path: - return Path(environ.get("SERENITY_BOCHS_BIN", "bochs")) - - -def detect_ram_size() -> str | None: - return environ.get("SERENITY_RAM_SIZE", "2G") - - -def set_up_qemu_binary(config: Configuration): - qemu_binary_basename: str | None = None - if "SERENITY_QEMU_BIN" in environ: - qemu_binary_basename = environ.get("SERENITY_QEMU_BIN") - else: - if config.architecture == Arch.Aarch64: - qemu_binary_basename = "qemu-system-aarch64" - elif config.architecture == Arch.RISCV64: - qemu_binary_basename = "qemu-system-riscv64" - elif config.architecture == Arch.x86_64: - qemu_binary_basename = "qemu-system-x86_64" - if qemu_binary_basename is None: - raise RunError("QEMU binary could not be determined") - - # Try finding native Windows QEMU first - if config.qemu_kind == QEMUKind.NativeWindows: - # FIXME: Consider using the wslwinreg module instead to access the registry more conveniently. - # Some Windows systems don't have reg.exe's directory on the PATH by default. - environ["PATH"] = environ["PATH"] + ":/mnt/c/Windows/System32" - try: - qemu_install_dir_result = run( - ["reg.exe", "query", r"HKLM\Software\QEMU", "/v", "Install_Dir", "/t", "REG_SZ"], - capture_output=True, - ) - if qemu_install_dir_result.returncode == 0: - registry_regex = re.compile(rb"Install_Dir\s+REG_SZ\s+(.*)$", flags=re.MULTILINE) - qemu_install_dir_match = registry_regex.search(qemu_install_dir_result.stdout) - if qemu_install_dir_match is not None: - # If Windows prints non-ASCII characters, those will most likely not be UTF-8. - # Therefore, don't decode sooner. Also, remove trailing '\r' - qemu_install_dir = Path(qemu_install_dir_match.group(1).decode("utf-8").strip()) - config.qemu_binary = Path( - run( - ["wslpath", "--", Path(qemu_install_dir, qemu_binary_basename)], - encoding="utf-8", - capture_output=True, - ).stdout.strip() - ).with_suffix(".exe") - # No native Windows QEMU, reconfigure to Linux QEMU without KVM - else: - config.virtualization_support = False - config.qemu_kind = QEMUKind.Other - except Exception: - # reg.exe not found; errors in reg.exe itself do not throw an error. - config.qemu_kind = QEMUKind.Other - - if config.qemu_binary is None: - # Set up full path for the binary if possible (otherwise trust system PATH) - local_qemu_bin = Path(str(config.serenity_src), "Toolchain/Local/qemu/bin/", qemu_binary_basename) - old_local_qemu_bin = Path(str(config.serenity_src), "Toolchain/Local/x86_64/bin/", qemu_binary_basename) - if local_qemu_bin.exists(): - config.qemu_binary = local_qemu_bin - elif old_local_qemu_bin.exists(): - config.qemu_binary = old_local_qemu_bin - else: - config.qemu_binary = Path(qemu_binary_basename) - - -def check_qemu_version(config: Configuration): - if config.qemu_binary is None: - raise RunError( - f"Please install QEMU version {QEMU_MINIMUM_REQUIRED_MAJOR_VERSION}.{QEMU_MINIMUM_REQUIRED_MINOR_VERSION} or newer or use the Toolchain/BuildQemu.sh script." # noqa: E501 - ) - version_information = run([config.qemu_binary, "-version"], capture_output=True, encoding="utf-8").stdout - qemu_version_regex = re.compile(r"QEMU emulator version ([1-9][0-9]*|0)\.([1-9][0-9]*|0)") - version_groups = qemu_version_regex.search(version_information) - if version_groups is None: - raise RunError(f'QEMU seems to be defective, its version information is "{version_information}"') - major = int(version_groups.group(1)) - minor = int(version_groups.group(2)) - - if major < QEMU_MINIMUM_REQUIRED_MAJOR_VERSION or ( - major == QEMU_MINIMUM_REQUIRED_MAJOR_VERSION and minor < QEMU_MINIMUM_REQUIRED_MINOR_VERSION - ): - raise RunError( - f"Required QEMU >= {QEMU_MINIMUM_REQUIRED_MAJOR_VERSION}.{QEMU_MINIMUM_REQUIRED_MINOR_VERSION}!\ - Found {major}.{minor}. Please install a newer version of QEMU or use the Toolchain/BuildQemu.sh script." - ) - - -def set_up_virtualization_support(config: Configuration): - provided_virtualization_enable = environ.get("SERENITY_VIRTUALIZATION_SUPPORT") - # The user config always forces the platform-appropriate virtualizer to be used, - # even if we couldn't detect it otherwise; this is intended behavior. - if provided_virtualization_enable is not None: - config.virtualization_support = provided_virtualization_enable == "1" - elif config.architecture == Arch.x86_64 and os.uname().machine == Arch.x86_64.value: - # FIXME: Can RISC-V use hardware acceleration? - config.virtualization_support = (config.qemu_kind in [QEMUKind.NativeWindows, QEMUKind.MacOS] - or kvm_usable()) - - if config.virtualization_support: - available_accelerators = run( - [str(config.qemu_binary), "-accel", "help"], - capture_output=True, - ).stdout - # Check if HVF is actually available if we're on MacOS - if config.qemu_kind == QEMUKind.MacOS and (b"hvf" not in available_accelerators): - config.virtualization_support = False - - -def set_up_basic_kernel_cmdline(config: Configuration): - provided_cmdline = environ.get("SERENITY_KERNEL_CMDLINE") - if provided_cmdline is not None: - # Split environment variable at spaces, since we don't pass arguments like shell scripts do. - config.kernel_cmdline.extend(provided_cmdline.split(sep=None)) - - # Handle system-specific arguments now, boot type specific arguments are handled later. - if config.qemu_kind == QEMUKind.NativeWindows: - config.kernel_cmdline.append("disable_virtio") - - -def set_up_disk_image_path(config: Configuration): - provided_disk_image = environ.get("SERENITY_DISK_IMAGE") - if provided_disk_image is not None: - config.disk_image = Path(provided_disk_image) - else: - if config.machine_type.uses_grub(): - config.disk_image = Path("grub_disk_image") - elif config.machine_type == MachineType.Limine: - config.disk_image = Path("limine_disk_image") - elif config.machine_type == MachineType.QEMUExtLinux: - config.disk_image = Path("extlinux_disk_image") - - if config.qemu_kind == QEMUKind.NativeWindows: - config.disk_image = Path( - run(["wslpath", "-w", config.disk_image], capture_output=True, encoding="utf-8").stdout.strip() - ) - - -def set_up_cpu(config: Configuration): - if config.qemu_kind == QEMUKind.NativeWindows: - config.qemu_cpu = "max,vmx=off" - else: - provided_cpu = environ.get("SERENITY_QEMU_CPU") - if provided_cpu is not None: - config.qemu_cpu = provided_cpu - - -def set_up_cpu_count(config: Configuration): - if config.architecture != Arch.x86_64: - return - - provided_cpu_count = environ.get("SERENITY_CPUS") - if provided_cpu_count is not None: - try: - config.cpu_count = int(provided_cpu_count) - except ValueError: - raise RunError(f"Non-integer CPU count {provided_cpu_count}") - - if config.cpu_count is not None and config.qemu_cpu is not None and config.cpu_count <= 8: - # -x2apic is not a flag, but disables x2APIC for easier testing on lower CPU counts. - config.qemu_cpu += ",-x2apic" - - -def set_up_spice(config: Configuration): - if environ.get("SERENITY_SPICE") == "1": - chardev_info = run( - [str(config.qemu_binary), "-chardev", "help"], - capture_output=True, - encoding="utf-8", - ).stdout.lower() - if "qemu-vdagent" in chardev_info: - config.spice_arguments = [ - "-chardev", - "qemu-vdagent,clipboard=on,mouse=off,id=vdagent,name=vdagent", - ] - elif "spicevmc" in chardev_info: - config.spice_arguments = ["-chardev", "spicevmc,id=vdagent,name=vdagent"] - else: - raise RunError("No compatible SPICE character device was found") - - if "spice" in chardev_info: - config.spice_arguments.extend(["-spice", "port=5930,agent-mouse=off,disable-ticketing=on"]) - if "spice" in chardev_info or "vdagent" in chardev_info: - config.spice_arguments.extend(["-device", "virtserialport,chardev=vdagent,nr=1"]) - - -def set_up_audio_backend(config: Configuration): - if config.qemu_kind == QEMUKind.MacOS: - config.audio_backend = "coreaudio" - elif config.qemu_kind == QEMUKind.NativeWindows: - config.audio_backend = "dsound,timer-period=2000" - elif config.machine_type != MachineType.CI: - # FIXME: Use "-audiodev help" once that contains information on all our supported versions, - # "-audio-help" is marked as deprecated. - qemu_audio_help = run( - [str(config.qemu_binary), "-audio-help"], - capture_output=True, - encoding="utf-8", - ).stdout - if qemu_audio_help == "": - qemu_audio_help = run( - [str(config.qemu_binary), "-audiodev", "help"], - capture_output=True, - encoding="utf-8", - ).stdout - if "sdl" in qemu_audio_help: - config.audio_backend = "sdl" - elif "pa" in qemu_audio_help: - config.audio_backend = "pa,timer-period=2000" - - if config.audio_backend is not None: - config.audio_backend += ",id=snd0" - - -def set_up_audio_hardware(config: Configuration): - provided_audio_hardware = environ.get("SERENITY_AUDIO_HARDWARE", "intelhda") - if provided_audio_hardware == "ac97": - config.audio_devices = ["ac97,audiodev=snd0"] - elif provided_audio_hardware == "intelhda": - config.audio_devices = ["ich9-intel-hda", "hda-output,audiodev=snd0"] - else: - raise RunError(f"Unknown audio hardware {provided_audio_hardware}. Supported values: ac97, intelhda") - - if config.machine_type.supports_pc_speaker() and config.architecture == Arch.x86_64: - config.extra_arguments.extend(["-machine", "pcspk-audiodev=snd0"]) - - -def has_virgl() -> bool: - try: - ldconfig_result = run(["ldconfig", "-p"], capture_output=True, encoding="utf-8").stdout.lower() - return "virglrenderer" in ldconfig_result - except FileNotFoundError: - print("Warning: ldconfig not found in PATH, assuming virgl support to not be present.") - return False - - -def set_up_screens(config: Configuration): - provided_screen_count_unparsed = environ.get("SERENITY_SCREENS", "1") - try: - config.screen_count = int(provided_screen_count_unparsed) - except ValueError: - raise RunError(f"Invalid screen count {provided_screen_count_unparsed}") - - provided_display_backend = environ.get("SERENITY_QEMU_DISPLAY_BACKEND") - if provided_display_backend is not None: - config.display_backend = provided_display_backend - else: - qemu_display_info = run( - [str(config.qemu_binary), "-display", "help"], - capture_output=True, - encoding="utf-8", - ).stdout.lower() - if len(config.spice_arguments) != 0: - config.display_backend = "spice-app" - elif config.qemu_kind == QEMUKind.NativeWindows: - # QEMU for windows does not like gl=on, so detect if we are building in wsl, and if so, disable it - # Also, when using the GTK backend we run into this problem: - # https://github.com/SerenityOS/serenity/issues/7657 - config.display_backend = "sdl,gl=off" - elif config.screen_count > 1 and "sdl" in qemu_display_info: - config.display_backend = "sdl,gl=off" - elif "sdl" in qemu_display_info and has_virgl(): - config.display_backend = "sdl,gl=on" - elif "cocoa" in qemu_display_info: - config.display_backend = "cocoa,gl=off" - elif config.qemu_kind == QEMUKind.SerenityOS: - config.display_backend = "sdl,gl=off" - else: - config.display_backend = "gtk,gl=off" - - -def set_up_display_device(config: Configuration): - config.enable_gl = environ.get("SERENITY_GL") == "1" - provided_display_device = environ.get("SERENITY_QEMU_DISPLAY_DEVICE") - if provided_display_device is not None: - config.display_device = provided_display_device - elif config.enable_gl: - # QEMU appears to not support the GL backend for VirtIO GPU variant on macOS. - if config.qemu_kind == QEMUKind.MacOS: - raise RunError("SERENITY_GL is not supported since there's no GL backend on macOS") - elif config.screen_count > 1: - raise RunError("SERENITY_GL and multi-monitor support cannot be set up simultaneously") - config.display_device = "virtio-vga-gl" - - elif config.screen_count > 1: - # QEMU appears to not support the virtio-vga VirtIO GPU variant on macOS. - # To ensure we can still boot on macOS with VirtIO GPU, use the virtio-gpu-pci - # variant, which lacks any VGA compatibility (which is not relevant for us anyway). - if config.qemu_kind == QEMUKind.MacOS: - config.display_device = f"virtio-gpu-pci,max_outputs={config.screen_count}" - else: - config.display_device = f"virtio-vga,max_outputs={config.screen_count}" - - # QEMU appears to always relay absolute mouse coordinates relative to the screen that the mouse is - # pointed to, without any way for us to know what screen it was. So, when dealing with multiple - # displays force using relative coordinates only. - config.kernel_cmdline.append("vmmouse=off") - - -def set_up_boot_drive(config: Configuration): - provided_nvme_enable = environ.get("SERENITY_NVME_ENABLE") - if provided_nvme_enable is not None: - config.nvme_enable = provided_nvme_enable == "1" - provided_sdcard_enable = environ.get("SERENITY_USE_SDCARD") - if provided_sdcard_enable is not None: - config.sd_enable = provided_sdcard_enable == "1" - provided_usb_boot_enable = environ.get("SERENITY_USE_USBDRIVE") - if provided_usb_boot_enable is not None: - config.usb_boot_enable = provided_usb_boot_enable == "1" - provided_virtio_block_enable = environ.get("SERENITY_USE_VIRTIOBLOCK") - if provided_virtio_block_enable is not None: - config.virtio_block_enable = provided_virtio_block_enable == "1" - - if config.machine_type in [MachineType.MicroVM, MachineType.ISAPC]: - if config.nvme_enable: - print("Warning: NVMe does not work under MicroVM/ISA PC, automatically disabling it.") - config.nvme_enable = False - - if config.architecture == Arch.Aarch64: - config.boot_drive = f"file={config.disk_image},if=sd,format=raw,id=disk" - elif config.nvme_enable: - config.boot_drive = f"file={config.disk_image},format=raw,index=0,media=disk,if=none,id=disk" - config.add_devices( - [ - "i82801b11-bridge,id=bridge4", - "nvme,serial=deadbeef,drive=disk,bus=bridge4,logical_block_size=4096,physical_block_size=4096", - ] - ) - config.kernel_cmdline.append("root=nvme0:1:0") - elif config.sd_enable: - config.boot_drive = f"id=sd-boot-drive,if=none,format=raw,file={config.disk_image}" - config.add_devices(["sdhci-pci", "sd-card,drive=sd-boot-drive"]) - config.kernel_cmdline.append("root=sd2:0:0") - elif config.usb_boot_enable: - config.boot_drive = f"if=none,id=usbstick,format=raw,file={config.disk_image}" - config.add_device("usb-storage,drive=usbstick") - # FIXME: Find a better way to address the usb drive - config.kernel_cmdline.append("root=block3:0") - elif config.virtio_block_enable: - config.boot_drive = f"if=none,id=virtio-root,format=raw,file={config.disk_image}" - config.add_device("virtio-blk-pci,drive=virtio-root") - config.kernel_cmdline.append("root=lun3:0:0") - else: - config.boot_drive = f"file={config.disk_image},format=raw,index=0,media=disk,id=disk" - - -def determine_host_address() -> str: - return environ.get("SERENITY_HOST_IP", "127.0.0.1") - - -def set_up_gdb(config: Configuration): - config.enable_gdb = environ.get("SERENITY_DISABLE_GDB_SOCKET") != "1" - if config.qemu_kind == QEMUKind.NativeWindows or ( - config.virtualization_support and config.qemu_kind == QEMUKind.MacOS - ): - config.enable_gdb = False - - if config.enable_gdb: - config.extra_arguments.extend(["-gdb", f"tcp:{config.host_ip}:1234"]) - - -def set_up_network_hardware(config: Configuration): - config.packet_logging_arguments = (environ.get("SERENITY_PACKET_LOGGING_ARG", "")).split() - - provided_ethernet_device_type = environ.get("SERENITY_ETHERNET_DEVICE_TYPE") - if provided_ethernet_device_type is not None: - config.ethernet_device_type = provided_ethernet_device_type - - if config.architecture == Arch.Aarch64: - config.network_backend = None - config.network_default_device = None - else: - config.network_backend = f"user,id=breh,hostfwd=tcp:{config.host_ip}:8888-10.0.2.15:8888,\ -hostfwd=tcp:{config.host_ip}:8823-10.0.2.15:23,\ -hostfwd=tcp:{config.host_ip}:8000-10.0.2.15:8000,\ -hostfwd=tcp:{config.host_ip}:2222-10.0.2.15:22" - config.network_default_device = f"{config.ethernet_device_type},netdev=breh" - - -def set_up_kernel(config: Configuration): - if config.architecture == Arch.Aarch64: - config.kernel_and_initrd_arguments = ["-kernel", "Kernel/Kernel"] - elif config.architecture == Arch.RISCV64: - config.kernel_and_initrd_arguments = ["-kernel", "Kernel/Kernel.bin"] - elif config.architecture == Arch.x86_64: - config.kernel_and_initrd_arguments = ["-kernel", "Kernel/Kernel"] - - -def set_up_machine_devices(config: Configuration): - # TODO: Maybe disable SPICE everwhere except the default machine? - - if config.qemu_kind != QEMUKind.NativeWindows: - config.extra_arguments.extend(["-qmp", "unix:qmp-sock,server,nowait"]) - - config.extra_arguments.extend(["-name", "SerenityOS", "-d", "guest_errors"]) - - # Architecture specifics. - if config.architecture == Arch.Aarch64: - config.qemu_machine = "raspi3b" - config.cpu_count = None - config.ram_size = "1G" # The raspi3b machine only accepts 1G as a valid RAM size. - config.vga_type = None - config.display_device = None - if config.machine_type != MachineType.CI: - # FIXME: Windows QEMU crashes when we set the same display as usual here. - config.display_backend = None - config.audio_devices = [] - config.extra_arguments.extend(["-serial", "stdio"]) - config.qemu_cpu = None - return - - elif config.architecture == Arch.RISCV64: - config.qemu_machine = "virt" - config.cpu_count = None - config.audio_devices = [] - config.extra_arguments.extend(["-serial", "stdio"]) - config.kernel_cmdline.extend(["serial_debug", "nvme_poll"]) - config.qemu_cpu = None - config.add_devices( - [ - "virtio-keyboard", - "virtio-tablet", - ] - ) - return - - # Machine specific base setups - if config.machine_type in [MachineType.QEMU35Grub, MachineType.QEMU35]: - config.qemu_machine = "q35" - config.vga_type = None - # We set up our own custom display devices. - config.display_device = None - config.add_devices( - [ - "isa-debugcon,chardev=stdout", - "vmware-svga", - "ich9-usb-ehci1,bus=pcie.0,multifunction=on,addr=0x05.3,multifunction=on,id=ehci1", - "ich9-usb-uhci1,bus=pcie.0,multifunction=on,addr=0x05.0,masterbus=ehci1.0,firstport=0", - "ich9-usb-uhci2,bus=pcie.0,multifunction=on,addr=0x05.1,masterbus=ehci1.0,firstport=2", - "ich9-usb-uhci3,bus=pcie.0,multifunction=on,addr=0x05.2,masterbus=ehci1.0,firstport=4", - "ich9-usb-ehci2,bus=pcie.0,multifunction=on,addr=0x07.3,multifunction=on,id=ehci2", - "ich9-usb-uhci4,bus=pcie.0,multifunction=on,addr=0x07.0,masterbus=ehci2.0,firstport=0", - "ich9-usb-uhci5,bus=pcie.0,multifunction=on,addr=0x07.1,masterbus=ehci2.0,firstport=2", - "ich9-usb-uhci6,bus=pcie.0,multifunction=on,addr=0x07.2,masterbus=ehci2.0,firstport=4", - "pcie-root-port,port=0x10,chassis=1,id=pcie.1,bus=pcie.0,multifunction=on,addr=0x6", - "pcie-root-port,port=0x11,chassis=2,id=pcie.2,bus=pcie.0,addr=0x6.0x1", - "pcie-root-port,port=0x12,chassis=3,id=pcie.3,bus=pcie.0,addr=0x6.0x2", - "pcie-root-port,port=0x13,chassis=4,id=pcie.4,bus=pcie.0,addr=0x6.0x3", - "pcie-root-port,port=0x14,chassis=5,id=pcie.5,bus=pcie.0,addr=0x6.0x4", - "pcie-root-port,port=0x15,chassis=6,id=pcie.6,bus=pcie.0,addr=0x6.0x5", - "pcie-root-port,port=0x16,chassis=7,id=pcie.7,bus=pcie.0,addr=0x6.0x6", - "pcie-root-port,port=0x17,chassis=8,id=pcie.8,bus=pcie.0,addr=0x6.0x7", - "ich9-intel-hda,bus=pcie.2,addr=0x03.0x0", - "bochs-display", - "nec-usb-xhci,bus=pcie.2,addr=0x11.0x0", - "pci-bridge,chassis_nr=1,id=bridge1,bus=pcie.4,addr=0x3.0x0", - "sdhci-pci,bus=bridge1,addr=0x1.0x0", - ] - ) - config.character_devices.append("stdio,id=stdout,mux=on") - config.enable_usb = True - elif config.machine_type in [MachineType.MicroVM, MachineType.ISAPC]: - config.character_devices.append("stdio,id=stdout,mux=on") - config.qemu_cpu = "qemu64" - config.cpu_count = None - config.display_device = None - config.network_default_device = None - config.audio_devices = [] - config.add_devices(["isa-debugcon,chardev=stdout", "isa-vga", "ne2k_isa,netdev=breh"]) - - if config.machine_type == MachineType.MicroVM: - config.qemu_machine = "microvm,pit=on,rtc=on,pic=on" - config.add_devices(["isa-ide", "ide-hd,drive=disk", "i8042"]) - else: # ISAPC - config.qemu_machine = "isapc" - - elif config.machine_type == MachineType.CI: - config.display_backend = "none" - config.audio_backend = None - config.audio_devices = [] - config.extra_arguments.extend(["-serial", "stdio", "-no-reboot", "-monitor", "none"]) - config.spice_arguments = [] - if config.architecture == Arch.Aarch64: - config.extra_arguments.extend(["-serial", "file:debug.log"]) - else: - config.add_device("ich9-ahci") - config.extra_arguments.extend(["-debugcon", "file:debug.log"]) - - else: - # Default machine - config.network_default_device = f"{config.network_default_device},bus=bridge1" - config.add_devices( - [ - "virtio-serial,max_ports=2", - "virtconsole,chardev=stdout", - "isa-debugcon,chardev=stdout", - "virtio-rng-pci", - "pci-bridge,chassis_nr=1,id=bridge1", - "i82801b11-bridge,bus=bridge1,id=bridge2", - "sdhci-pci,bus=bridge2", - "i82801b11-bridge,id=bridge3", - "sdhci-pci,bus=bridge3", - "ich9-ahci,bus=bridge3", - ] - ) - config.character_devices.append("stdio,id=stdout,mux=on") - config.enable_usb = True - - # Modifications for machine types that are *mostly* like the default, - # but not entirely (especially in terms of networking). - if config.machine_type in [MachineType.QEMUWithoutNetwork, MachineType.QEMU35Grub]: - config.network_backend = None - config.network_default_device = config.ethernet_device_type - config.packet_logging_arguments = [] - elif config.machine_type == MachineType.QEMUTap: - config.network_backend = "tap,ifname=tap0,id=br0" - config.network_default_device = f"{config.ethernet_device_type},netdev=br0" - elif config.machine_type in [MachineType.QEMUGrub, MachineType.QEMUExtLinux]: - config.kernel_cmdline = [] - - -def assemble_arguments(config: Configuration) -> list[str | Path]: - if config.machine_type == MachineType.Bochs: - boch_src = Path(config.serenity_src or ".", "Meta/bochsrc") - return [config.bochs_binary, "-q", "-f", boch_src] - - passed_qemu_args = shlex.split(environ.get("SERENITY_EXTRA_QEMU_ARGS", "")) - - return [ - config.qemu_binary or "", - # Deviate from standard order here: - # The device list contains PCI bridges which must be available for other devices. - *config.device_arguments, - *config.kernel_and_initrd_arguments, - *config.packet_logging_arguments, - *config.spice_arguments, - *config.extra_arguments, - *config.accelerator_arguments, - *config.kernel_cmdline_arguments, - *config.ram_arguments, - *config.cpu_arguments, - *config.smp_arguments, - *config.machine_arguments, - *config.usb_arguments, - *config.audio_backend_arguments, - *config.audio_devices_arguments, - *config.vga_arguments, - *config.display_backend_arguments, - *config.display_device_arguments, - *config.network_backend_arguments, - *config.network_default_arguments, - *config.boot_drive_arguments, - *config.character_device_arguments, - *passed_qemu_args, - ] - - -class TapController: - """Context manager for setting up and tearing down a tap device when QEMU is run with tap networking.""" - - def __init__(self, machine_type: MachineType): - self.should_enable_tap = machine_type == MachineType.QEMUTap - - def __enter__(self) -> None: - if self.should_enable_tap: - run(["sudo", "ip", "tuntap", "del", "dev", "tap0", "mode", "tap"]) - user = os.getuid() - run(["sudo", "ip", "tuntap", "add", "dev", "tap0", "mode", "tap", "user", str(user)]) - - def __exit__(self, exc_type: type | None, exc_value: Any | None, traceback: Any | None) -> Literal[False]: - if self.should_enable_tap: - run(["sudo", "ip", "tuntap", "del", "dev", "tap0", "mode", "tap"]) - # Re-raise exceptions in any case. - return False - - -def configure_and_run(): - config = Configuration() - config.kvm_usable = kvm_usable() - config.qemu_kind = determine_qemu_kind() - config.architecture = determine_serenity_arch() - config.machine_type = determine_machine_type() - config.bochs_binary = detect_bochs() - config.ram_size = detect_ram_size() - config.host_ip = determine_host_address() - - serenity_src = environ.get("SERENITY_SOURCE_DIR") - if serenity_src is None: - raise RunError("SERENITY_SOURCE_DIR not set or empty") - config.serenity_src = Path(serenity_src) - - set_up_qemu_binary(config) - check_qemu_version(config) - set_up_virtualization_support(config) - set_up_basic_kernel_cmdline(config) - set_up_disk_image_path(config) - set_up_cpu(config) - set_up_cpu_count(config) - set_up_spice(config) - set_up_audio_backend(config) - set_up_audio_hardware(config) - set_up_screens(config) - set_up_display_device(config) - set_up_boot_drive(config) - set_up_gdb(config) - set_up_network_hardware(config) - set_up_kernel(config) - set_up_machine_devices(config) - - arguments = assemble_arguments(config) - - build_directory = environ.get("SERENITY_BUILD", ".") - os.chdir(build_directory) - - with TapController(config.machine_type): - run(arguments) - - -def main(): - try: - configure_and_run() - except KeyboardInterrupt: - pass - except RunError as e: - print(f"Error: {e}") - except Exception as e: - print(f"Unknown error: {e}") - print("This is likely a bug, consider filing a bug report.") - - -if __name__ == "__main__": - main() diff --git a/Meta/run.sh b/Meta/run.sh deleted file mode 100755 index 62863a4a544..00000000000 --- a/Meta/run.sh +++ /dev/null @@ -1,13 +0,0 @@ -#!/bin/sh - -set -e - -SCRIPT_DIR="$(dirname "${0}")" - -# https://www.shellcheck.net/wiki/SC1090 No need to shellcheck private config. -# shellcheck source=/dev/null -[ -x "$SCRIPT_DIR/../run-local.sh" ] && . "$SCRIPT_DIR/../run-local.sh" - -#export SERENITY_PACKET_LOGGING_ARG="-object filter-dump,id=hue,netdev=breh,file=e1000.pcap" - -"$SCRIPT_DIR/run.py" diff --git a/Meta/serenity_gdb.py b/Meta/serenity_gdb.py deleted file mode 100644 index 9be85ec2465..00000000000 --- a/Meta/serenity_gdb.py +++ /dev/null @@ -1,498 +0,0 @@ -# Copyright (c) 2021, Gunnar Beutner -# -# SPDX-License-Identifier: BSD-2-Clause - -import gdb -import gdb.types -import re - - -def handler_class_for_type(type, re=re.compile('^([^<]+)(<.*>)?$')): - typename = str(type.tag) - - match = re.match(typename) - if not match: - return UnhandledType - - klass = match.group(1) - - if klass == 'AK::Array': - return AKArray - elif klass == 'AK::Atomic': - return AKAtomic - elif klass == 'AK::DistinctNumeric': - return AKDistinctNumeric - elif klass == 'AK::FixedArray': - return AKFixedArrayPrinter - elif klass == 'AK::HashMap': - return AKHashMapPrettyPrinter - elif klass == 'AK::RefCounted': - return AKRefCounted - elif klass == 'AK::RefPtr': - return AKRefPtr - elif klass == 'AK::OwnPtr': - return AKOwnPtr - elif klass == 'AK::NonnullRefPtr': - return AKRefPtr - elif klass == 'AK::SinglyLinkedList': - return AKSinglyLinkedList - elif klass == 'AK::String': - return AKString - elif klass == 'AK::ByteString': - return AKDeprecatedString - elif klass == 'AK::StringView': - return AKStringView - elif klass == 'AK::StringImpl': - return AKStringImpl - elif klass == 'AK::Variant': - return AKVariant - elif klass == 'AK::Optional': - return AKOptional - elif klass == 'AK::Vector': - return AKVector - elif klass == 'VirtualAddress': - return VirtualAddress - else: - return UnhandledType - - -class UnhandledType: - @classmethod - def prettyprint_type(cls, type): - return type.name - - -class AKAtomic: - def __init__(self, val): - self.val = val - - def to_string(self): - return self.val["m_value"] - - @classmethod - def prettyprint_type(cls, type): - contained_type = type.template_argument(0) - return f'AK::Atomic<{handler_class_for_type(contained_type).prettyprint_type(contained_type)}>' - - -class AKDistinctNumeric: - def __init__(self, val): - self.val = val - - def to_string(self): - return self.val["m_value"] - - @classmethod - def prettyprint_type(cls, type): - actual_name = type.template_argument(1) - parts = actual_name.name.split("::") - unqualified_name = re.sub(r'__(\w+)_tag', r'\1', actual_name.name) - if unqualified_name != actual_name.name: - qualified_name = '::'.join(parts[:-2] + [unqualified_name]) - return qualified_name - # If the tag is malformed, just print DistinctNumeric - contained_type = type.template_argument(0) - return f'AK::DistinctNumeric<{handler_class_for_type(contained_type).prettyprint_type(contained_type)}>' - - -class AKFixedArrayPrinter: - def __init__(self, val): - self.val = val - - def get_storage(self): - storage = self.val["m_storage"] - - return None if int(storage) == 0 else storage - - def to_string(self): - storage = self.get_storage() - if storage is not None: - size = storage["size"] - else: - size = 0 - - return f'{AKFixedArrayPrinter.prettyprint_type(self.val.type)} of size {size}' - - def children(self): - storage = self.get_storage() - - if storage is None: - return [] - else: - size = storage["size"] - elements = storage["elements"] - - # Very arbitrary limit, just to catch UAF'd and garbage vector values with a silly number of elements - if size > 373373: - return [] - - return [(f"[{i}]", elements[i]) for i in range(size)] - - @classmethod - def prettyprint_type(cls, type): - template_type = type.template_argument(0) - return f'AK::FixedArray<{handler_class_for_type(template_type).prettyprint_type(template_type)}>' - - -class AKRefCounted: - def __init__(self, val): - self.val = val - - def to_string(self): - return self.val["m_ref_count"] - - @classmethod - def prettyprint_type(cls, type): - contained_type = type.template_argument(0) - return f'AK::RefCounted<{handler_class_for_type(contained_type).prettyprint_type(contained_type)}>' - - -class AKString: - def __init__(self, val): - self.val = val - - def to_string(self): - # Using the internal structure directly is quite convoluted here because of the packing optimizations - # of AK::String (could be a short string, a substring, or a normal string). - # This workaround was described in the gdb bugzilla on a discussion of supporting direct method calls - # on values: https://sourceware.org/bugzilla/show_bug.cgi?id=13326 - gdb.set_convenience_variable('_tmp', self.val.reference_value()) - string_view = gdb.parse_and_eval('$_tmp.bytes_as_string_view()') - return AKStringView(string_view).to_string() - - @classmethod - def prettyprint_type(cls, type): - return 'AK::String' - - -class AKDeprecatedString: - def __init__(self, val): - self.val = val - - def to_string(self): - if int(self.val["m_impl"]["m_ptr"]) == 0: - return '""' - else: - impl = AKRefPtr(self.val["m_impl"]).get_pointee().dereference() - return AKStringImpl(impl).to_string() - - @classmethod - def prettyprint_type(cls, type): - return 'AK::ByteString' - - -class AKStringView: - def __init__(self, val): - self.val = val - - def to_string(self): - if int(self.val["m_length"]) == 0: - return '""' - else: - return self.val["m_characters"].string(length=self.val["m_length"]) - - @classmethod - def prettyprint_type(cls, type): - return 'AK::StringView' - - -def get_field_unalloced(val, member, type): - # Trying to access a variable-length field seems to fail with - # Python Exception value requires 4294967296 bytes, which is more than max-value-size - # This works around that issue. - return gdb.parse_and_eval(f"*({type}*)(({val.type.name}*){int(val.address)})->{member}") - - -class AKStringImpl: - def __init__(self, val): - self.val = val - - def to_string(self): - if int(self.val["m_length"]) == 0: - return '""' - else: - return self.val["m_inline_buffer"].string(length=self.val["m_length"]) - - @classmethod - def prettyprint_type(cls, type): - return 'AK::StringImpl' - - -class AKOwnPtr: - def __init__(self, val): - self.val = val - - def to_string(self): - return AKOwnPtr.prettyprint_type(self.val.type) - - def children(self): - return [('*', self.val["m_ptr"])] - - @classmethod - def prettyprint_type(cls, type): - contained_type = type.template_argument(0) - return f'AK::OwnPtr<{handler_class_for_type(contained_type).prettyprint_type(contained_type)}>' - - -class AKRefPtr: - def __init__(self, val): - self.val = val - - def to_string(self): - return AKRefPtr.prettyprint_type(self.val.type) - - def get_pointee(self): - inner_type = self.val.type.template_argument(0) - inner_type_ptr = inner_type.pointer() - return self.val["m_ptr"].cast(inner_type_ptr) - - def children(self): - return [('*', self.get_pointee())] - - @classmethod - def prettyprint_type(cls, type): - contained_type = type.template_argument(0) - return f'AK::RefPtr<{handler_class_for_type(contained_type).prettyprint_type(contained_type)}>' - - -class AKVariant: - def __init__(self, val): - self.val = val - self.index = int(self.val["m_index"]) - self.contained_types = self.resolve_types(self.val.type) - - def to_string(self): - return AKVariant.prettyprint_type(self.val.type) - - def children(self): - data = self.val["m_data"] - ty = self.contained_types[self.index] - return [(ty.name, data.cast(ty.pointer()).referenced_value())] - - @classmethod - def resolve_types(cls, ty): - contained_types = [] - type_resolved = ty.strip_typedefs() - index = 0 - while True: - try: - arg = type_resolved.template_argument(index) - index += 1 - contained_types.append(arg) - except RuntimeError: - break - return contained_types - - @classmethod - def prettyprint_type(cls, ty): - names = ", ".join(handler_class_for_type(t).prettyprint_type(t) for t in AKVariant.resolve_types(ty)) - return f'AK::Variant<{names}>' - - -class AKOptional: - def __init__(self, val): - self.val = val - self.has_value = bool(self.val["m_has_value"]) - self.contained_type = self.val.type.strip_typedefs().template_argument(0) - - def to_string(self): - return AKOptional.prettyprint_type(self.val.type) - - def children(self): - if self.has_value: - data = self.val["m_storage"] - return [(self.contained_type.name, data.cast(self.contained_type.pointer()).referenced_value())] - return [("OptionalNone", "{}")] - - @classmethod - def prettyprint_type(cls, type): - template_type = type.template_argument(0) - return f'AK::Optional<{template_type}>' - - -class AKVector: - def __init__(self, val): - self.val = val - - def to_string(self): - return f'{AKVector.prettyprint_type(self.val.type)} of len {int(self.val["m_size"])}' - - def children(self): - vec_len = int(self.val["m_size"]) - - if vec_len == 0: - return [] - - outline_buf = self.val["m_outline_buffer"] - - inner_type_ptr = self.val.type.template_argument(0).pointer() - - if int(outline_buf) != 0: - elements = outline_buf.cast(inner_type_ptr) - else: - elements = get_field_unalloced(self.val, "m_inline_buffer_storage", inner_type_ptr) - - # Very arbitrary limit, just to catch UAF'd and garbage vector values with a silly number of elements - if vec_len > 373373: - return [] - - return [(f"[{i}]", elements[i]) for i in range(vec_len)] - - @classmethod - def prettyprint_type(cls, type): - template_type = type.template_argument(0) - return f'AK::Vector<{handler_class_for_type(template_type).prettyprint_type(template_type)}>' - - -class AKArray: - def __init__(self, val): - self.val = val - self.storage_type = self.val.type.template_argument(0) - self.array_size = self.val.type.template_argument(1) - - def to_string(self): - return AKArray.prettyprint_type(self.val.type) - - def children(self): - data_array = self.val["__data"] - storage_type_ptr = self.storage_type.pointer() - elements = data_array.cast(storage_type_ptr) - - return [(f"[{i}]", elements[i]) for i in range(self.array_size)] - - @classmethod - def prettyprint_type(cls, type): - template_type = type.template_argument(0) - template_size = type.template_argument(1) - return f'AK::Array<{template_type}, {template_size}>' - - -class AKHashMapPrettyPrinter: - def __init__(self, val): - self.val = val - - @staticmethod - def _iter_hashtable(val, cb): - entry_type_ptr = val.type.template_argument(0).pointer() - buckets = val["m_buckets"] - for i in range(0, val["m_capacity"]): - bucket = buckets[i] - # if state == Used - if bucket["state"] & 0xf0 == 0x10: - cb(bucket["storage"].cast(entry_type_ptr)) - - @staticmethod - def _iter_hashmap(val, cb): - table = val["m_table"] - AKHashMapPrettyPrinter._iter_hashtable(table, lambda entry: cb(entry["key"], entry["value"])) - - def to_string(self): - return AKHashMapPrettyPrinter.prettyprint_type(self.val.type) - - def children(self): - elements = [] - - def cb(key, value): - nonlocal elements - elements.append((f"[{key}]", value)) - - AKHashMapPrettyPrinter._iter_hashmap(self.val, cb) - return elements - - @classmethod - def prettyprint_type(cls, type): - template_types = list(type.template_argument(i) for i in (0, 1)) - key, value = list(handler_class_for_type(t).prettyprint_type(t) for t in template_types) - return f'AK::HashMap<{key}, {value}>' - - -class AKSinglyLinkedList: - def __init__(self, val): - self.val = val - - def to_string(self): - return AKSinglyLinkedList.prettyprint_type(self.val.type) - - def children(self): - elements = [] - - node = self.val["m_head"] - while node != 0: - elements.append(node["value"]) - node = node["next"] - - return [(f"[{i}]", elements[i]) for i in range(len(elements))] - - @classmethod - def prettyprint_type(cls, type): - template_type = type.template_argument(0) - return f'AK::SinglyLinkedList<{handler_class_for_type(template_type).prettyprint_type(template_type)}>' - - -class VirtualAddress: - def __init__(self, val): - self.val = val - - def to_string(self): - return self.val["m_address"] - - @classmethod - def prettyprint_type(cls, type): - return 'VirtualAddress' - - -class SerenityPrettyPrinterLocator(gdb.printing.PrettyPrinter): - def __init__(self): - super(SerenityPrettyPrinterLocator, self).__init__("serenity_pretty_printers", []) - - def __call__(self, val): - type = gdb.types.get_basic_type(val.type) - handler = handler_class_for_type(type) - if handler is UnhandledType: - return None - return handler(val) - - -gdb.printing.register_pretty_printer(None, SerenityPrettyPrinterLocator(), replace=True) - - -class FindThreadCmd(gdb.Command): - """ - Find SerenityOS thread for the specified TID. - find_thread TID - """ - - def __init__(self): - super(FindThreadCmd, self).__init__( - "find_thread", gdb.COMMAND_USER - ) - - def _find_thread(self, tid): - threads = gdb.parse_and_eval("Kernel::Thread::g_tid_map") - thread = None - - def cb(key, value): - nonlocal thread - if int(key["m_value"]) == tid: - thread = value - - AKHashMapPrettyPrinter._iter_hashmap(threads, cb) - return thread - - def complete(self, text, word): - return gdb.COMPLETE_SYMBOL - - def invoke(self, args, from_tty): - argv = gdb.string_to_argv(args) - if len(argv) == 0: - gdb.write("Argument required (TID).\n") - return - tid = int(argv[0]) - thread = self._find_thread(tid) - if not thread: - gdb.write(f"No thread with TID {tid} found.\n") - else: - gdb.write(f"{thread}\n") - - -FindThreadCmd() diff --git a/Meta/toot-commits.js b/Meta/toot-commits.js deleted file mode 100644 index 0db3060ce5c..00000000000 --- a/Meta/toot-commits.js +++ /dev/null @@ -1,34 +0,0 @@ -const fs = require("fs"); -const Mastodon = require("mastodon"); -const { ACCESS_TOKEN } = process.env; -const tootLength = 500; -// Mastodon always considers links to be 23 chars, see https://docs.joinmastodon.org/user/posting/#links -const mastodonLinkLength = 23; - -const mastodon = new Mastodon({ - access_token: ACCESS_TOKEN, - timeout_ms: 60 * 1000, - api_url: "https://serenityos.social/api/v1/", -}); - -(async () => { - const githubEvent = JSON.parse(fs.readFileSync(0).toString()); - const toots = []; - for (const commit of githubEvent["commits"]) { - const authorLine = `Author: ${commit["author"]["name"]}`; - const maxMessageLength = tootLength - authorLine.length - mastodonLinkLength - 2; // -2 for newlines - const commitMessage = - commit["message"].length > maxMessageLength - ? commit["message"].substring(0, maxMessageLength - 2) + "…" // Ellipsis counts as 2 characters - : commit["message"]; - - toots.push(`${commitMessage}\n${authorLine}\n${commit["url"]}`); - } - for (const toot of toots) { - try { - await mastodon.post("statuses", { status: toot, visibility: "unlisted" }); - } catch (e) { - console.error("Failed to post a toot!", e.message); - } - } -})(); diff --git a/Meta/tweet-commits.js b/Meta/tweet-commits.js deleted file mode 100644 index cb601f7f9b1..00000000000 --- a/Meta/tweet-commits.js +++ /dev/null @@ -1,35 +0,0 @@ -const fs = require("fs"); -const Twit = require("twit"); -const { CONSUMER_KEY, CONSUMER_SECRET, ACCESS_TOKEN, ACCESS_TOKEN_SECRET } = process.env; -const tweetLength = 280; -// Twitter always considers t.co links to be 23 chars, see https://help.twitter.com/en/using-twitter/how-to-tweet-a-link -const twitterLinkLength = 23; - -const T = new Twit({ - consumer_key: CONSUMER_KEY, - consumer_secret: CONSUMER_SECRET, - access_token: ACCESS_TOKEN, - access_token_secret: ACCESS_TOKEN_SECRET, -}); - -(async () => { - const githubEvent = JSON.parse(fs.readFileSync(0).toString()); - const tweets = []; - for (const commit of githubEvent["commits"]) { - const authorLine = `Author: ${commit["author"]["name"]}`; - const maxMessageLength = tweetLength - authorLine.length - twitterLinkLength - 2; // -2 for newlines - const commitMessage = - commit["message"].length > maxMessageLength - ? commit["message"].substring(0, maxMessageLength - 2) + "…" // Ellipsis counts as 2 characters - : commit["message"]; - - tweets.push(`${commitMessage}\n${authorLine}\n${commit["url"]}`); - } - for (const tweet of tweets) { - try { - await T.post("statuses/update", { status: tweet }); - } catch (e) { - console.error("Failed to post a tweet!", e.message); - } - } -})(); diff --git a/Ports/.gitignore b/Ports/.gitignore deleted file mode 100644 index 937271b4091..00000000000 --- a/Ports/.gitignore +++ /dev/null @@ -1,6 +0,0 @@ -*/* -!*/package.sh -!*/patches -!*/patches/* -!*/.gitignore -!*/README.md diff --git a/Ports/.hosted_defs.sh b/Ports/.hosted_defs.sh deleted file mode 100644 index abb0622bcea..00000000000 --- a/Ports/.hosted_defs.sh +++ /dev/null @@ -1,62 +0,0 @@ -#!/usr/bin/env bash - -SCRIPT="$(realpath $(dirname "${BASH_SOURCE[0]}"))" - -export SERENITY_ARCH="${SERENITY_ARCH:-x86_64}" -export SERENITY_TOOLCHAIN="${SERENITY_TOOLCHAIN:-GNU}" - -if [ -z "${HOST_CC:=}" ]; then - export HOST_CC="${CC:=cc}" - export HOST_CXX="${CXX:=c++}" - export HOST_LD="${LD:=ld}" - export HOST_AR="${AR:=ar}" - export HOST_RANLIB="${RANLIB:=ranlib}" - export HOST_PATH="${PATH:=}" - export HOST_READELF="${READELF:=readelf}" - export HOST_OBJCOPY="${OBJCOPY:=objcopy}" - export HOST_OBJDUMP="${OBJDUMP:=objdump}" - export HOST_STRIP="${STRIP:=strip}" - export HOST_CXXFILT="${CXXFILT:=c++filt}" - export HOST_PKG_CONFIG_DIR="${PKG_CONFIG_DIR:=}" - export HOST_PKG_CONFIG_SYSROOT_DIR="${PKG_CONFIG_SYSROOT_DIR:=}" - export HOST_PKG_CONFIG_LIBDIR="${PKG_CONFIG_LIBDIR:=}" -fi - -export SERENITY_SOURCE_DIR="$(realpath "${SCRIPT}/../")" - -if [ "$SERENITY_TOOLCHAIN" = "Clang" ]; then - export SERENITY_BUILD_DIR="${SERENITY_SOURCE_DIR}/Build/${SERENITY_ARCH}clang" - export SERENITY_TOOLCHAIN_BINDIR="${SERENITY_SOURCE_DIR}/Toolchain/Local/clang/bin" - export CC="${SERENITY_ARCH}-pc-serenity-clang" - export CXX="${SERENITY_ARCH}-pc-serenity-clang++" - export LD="${SERENITY_TOOLCHAIN_BINDIR}/ld.lld" - export AR="llvm-ar" - export RANLIB="llvm-ranlib" - export READELF="llvm-readelf" - export OBJCOPY="llvm-objcopy" - export OBJDUMP="llvm-objdump" - export STRIP="llvm-strip" - export CXXFILT="llvm-cxxfilt" -else - export SERENITY_BUILD_DIR="${SERENITY_SOURCE_DIR}/Build/${SERENITY_ARCH}" - export SERENITY_TOOLCHAIN_BINDIR="${SERENITY_SOURCE_DIR}/Toolchain/Local/${SERENITY_ARCH}/bin" - export CC="${SERENITY_ARCH}-pc-serenity-gcc" - export CXX="${SERENITY_ARCH}-pc-serenity-g++" - export LD="${SERENITY_TOOLCHAIN_BINDIR}/${SERENITY_ARCH}-pc-serenity-ld" - export AR="${SERENITY_ARCH}-pc-serenity-ar" - export RANLIB="${SERENITY_ARCH}-pc-serenity-ranlib" - export READELF="${SERENITY_ARCH}-pc-serenity-readelf" - export OBJCOPY="${SERENITY_ARCH}-pc-serenity-objcopy" - export OBJDUMP="${SERENITY_ARCH}-pc-serenity-objdump" - export STRIP="${SERENITY_ARCH}-pc-serenity-strip" - export CXXFILT="${SERENITY_ARCH}-pc-serenity-c++filt" -fi - -export PATH="${SERENITY_TOOLCHAIN_BINDIR}:${SERENITY_SOURCE_DIR}/Toolchain/Local/cmake/bin:${HOST_PATH}" - -export PKG_CONFIG_DIR="" -export PKG_CONFIG_SYSROOT_DIR="${SERENITY_BUILD_DIR}/Root" -export PKG_CONFIG_LIBDIR="${PKG_CONFIG_SYSROOT_DIR}/usr/local/lib/pkgconfig" - -export SERENITY_INSTALL_ROOT="${SERENITY_BUILD_DIR}/Root" -export SERENITY_PORT_DIRS="${SERENITY_PORT_DIRS:+${SERENITY_PORT_DIRS}:}${SERENITY_SOURCE_DIR}/Ports" diff --git a/Ports/.port_include.sh b/Ports/.port_include.sh deleted file mode 100755 index 11e0c459edc..00000000000 --- a/Ports/.port_include.sh +++ /dev/null @@ -1,896 +0,0 @@ -#!/usr/bin/env bash -set -eu - -SCRIPT="$(realpath $(dirname "${BASH_SOURCE[0]}"))" - -if [ -z "${SERENITY_STRIPPED_ENV:-}" ]; then - exec "${SCRIPT}/.strip_env.sh" "${@}" -fi -unset SERENITY_STRIPPED_ENV - -export MAKEJOBS="${MAKEJOBS:-$(nproc)}" -export CMAKE_BUILD_PARALLEL_LEVEL="$MAKEJOBS" - -buildstep() { - local buildstep_name=$1 - shift - if [ "$#" -eq '0' ]; then - "${buildstep_name}" - else - "$@" - fi 2>&1 | sed $'s|^|\x1b[34m['"${port}/${buildstep_name}"$']\x1b[39m |' - local return_code=${PIPESTATUS[0]} - if [ ${return_code} != 0 ]; then - echo -e "\x1b[1;31mError in step ${port}/${buildstep_name} (status=${return_code})\x1b[0m" - fi - return ${return_code} -} - -buildstep_intro() { - echo -e "\x1b[1;32m=> $@\x1b[0m" -} - -target_env() { - if [ -f "${SCRIPT}/.hosted_defs.sh" ]; then - . "${SCRIPT}/.hosted_defs.sh" - elif [ "$(uname -s)" = "SerenityOS" ]; then - export SERENITY_ARCH="$(uname -m)" - export SERENITY_INSTALL_ROOT="" - else - >&2 echo "Error: .hosted_defs.sh is missing and we are not running on Serenity." - exit 1 - fi -} - -target_env - -DESTDIR="${SERENITY_INSTALL_ROOT}" - -enable_ccache() { - if [ "${USE_CCACHE:-true}" = "true" ] && command -v ccache &>/dev/null; then - ccache_tooldir="${SERENITY_BUILD_DIR}/ccache" - mkdir -p "$ccache_tooldir" - for tool in cc clang gcc c++ clang++ g++; do - name="${SERENITY_ARCH}-pc-serenity-${tool}" - if ! command -v "${name}" >/dev/null; then - continue - fi - ln -sf "$(command -v ccache)" "${ccache_tooldir}/${name}" - done - export PATH="${ccache_tooldir}:$PATH" - fi -} - -enable_ccache - -host_env() { - export CC="${HOST_CC}" - export CXX="${HOST_CXX}" - export LD="${HOST_LD}" - export AR="${HOST_AR}" - export RANLIB="${HOST_RANLIB}" - export PATH="${HOST_PATH}" - export READELF="${HOST_READELF}" - export OBJCOPY="${HOST_OBJCOPY}" - export STRIP="${HOST_STRIP}" - export CXXFILT="${HOST_CXXFILT}" - export PKG_CONFIG_DIR="${HOST_PKG_CONFIG_DIR}" - export PKG_CONFIG_SYSROOT_DIR="${HOST_PKG_CONFIG_SYSROOT_DIR}" - export PKG_CONFIG_LIBDIR="${HOST_PKG_CONFIG_LIBDIR}" - enable_ccache -} - -installedpackagesdb="${DESTDIR}/usr/Ports/installed.db" - -makeopts=("-j${MAKEJOBS}") -installopts=() -configscript=configure -configopts=() -useconfigure=false -config_sub_paths=("config.sub") -config_guess_paths=("config.guess") -use_fresh_config_sub=false -use_fresh_config_guess=false -depends=() -patchlevel=1 -launcher_name= -launcher_category= -launcher_command= -launcher_workdir= -launcher_run_in_terminal=false -icon_file= - -. "$@" -shift - -: "${workdir:=$port-$version}" - -PORT_META_DIR="$(pwd)" -if [[ -z ${SERENITY_BUILD_DIR:-} ]]; then - PORT_BUILD_DIR="${PORT_META_DIR}" -else - PORT_BUILD_DIR="${SERENITY_BUILD_DIR}/Ports/${port}" -fi - -mkdir -p "${PORT_BUILD_DIR}" -cd "${PORT_BUILD_DIR}" - -# 1 = url -# 2 = sha256sum -FILES_SIMPLE_PATTERN='^(https?:\/\/.+)#([0-9a-f]{64})$' - -# 1 = repository -# 2 = revision -FILES_GIT_PATTERN='^git\+(.+)#(.+)$' - -cleanup_git() { - echo "WARNING: Reverting changes to $workdir as we are in dev mode!" - run git clean -xffd >/dev/null 2>&1 -} - -# Make sure to clean up the git repository of the port afterwards. -if [ -n "${IN_SERENITY_PORT_DEV:-}" ]; then - echo "WARNING: All changes to the workdir in the current state (inside ./package.sh dev) are temporary!" - echo " They will be reverted once the command exits!" - trap "run cleanup_git" EXIT -fi - -run_nocd() { - echo "+ $@ (nocd)" >&2 - ("$@") -} - -run() { - echo "+ $@" - (cd "$workdir" && "$@") -} - -run_replace_in_file() { - if [ "$(uname -s)" = "SerenityOS" ]; then - run sed -i "$1" $2 - else - run perl -p -i -e "$1" $2 - fi -} - -sed_in_place() { - if [ "$(uname -s)" = "Darwin" ]; then - sed -i '' "${@}" - else - sed -i "${@}" - fi -} - -get_new_config_sub() { - config_sub="${1:-config.sub}" - if [ ! -f "$workdir/$config_sub" ]; then - >&2 echo "Error: Downloaded $config_sub does not replace an existing file!" - exit 1 - fi - if ! run grep -q serenity "$config_sub"; then - run do_download_file "https://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.sub" "${config_sub}" false - fi -} - -get_new_config_guess() { - config_guess="${1:-config.guess}" - if [ ! -f "$workdir/$config_guess" ]; then - >&2 echo "Error: Downloaded $config_guess does not replace an existing file!" - exit 1 - fi - if ! run grep -q SerenityOS "$config_guess"; then - run do_download_file "https://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.guess" "${config_guess}" false - fi -} - -ensure_new_config_sub() { - for path in "${config_sub_paths[@]}"; do - get_new_config_sub "${path}" - done -} - -ensure_new_config_guess() { - for path in "${config_guess_paths[@]}"; do - get_new_config_guess "${path}" - done -} - -ensure_build() { - # Sanity check. - if [ ! -f "${DESTDIR}/usr/lib/libc.so" ]; then - echo "libc.so could not be found. This likely means that SerenityOS:" - echo "- has not been built and/or installed yet" - echo "- has been installed in an unexpected location" - echo "The currently configured build directory is ${SERENITY_BUILD_DIR}. Resolve this issue and try again." - exit 1 - fi -} - -install_main_icon() { - if [ -n "$icon_file" ] && [ -n "$launcher_command" ]; then - local launcher_binary="${launcher_command%% *}" - install_icon "$icon_file" "${launcher_binary}" - fi -} - -install_icon() { - if [ "$#" -lt 2 ]; then - echo "Syntax: install_icon " - exit 1 - fi - local icon="$1" - local launcher="$2" - - command -v convert >/dev/null || true - local convert_exists=$? - command -v identify >/dev/null || true - local identify_exists=$? - - if [ "${convert_exists}" != "0" ] || [ "${identify_exists}" != 0 ]; then - echo 'Unable to install icon: missing convert or identify, did you install ImageMagick?' - return - fi - - for icon_size in "16x16" "32x32"; do - index=$(run identify -format '%p;%wx%h\n' "$icon" | grep "$icon_size" | cut -d";" -f1 | head -n1) - if [ -n "$index" ]; then - run convert "${icon}[${index}]" "app-${icon_size}.png" - else - run convert "$icon[0]" -resize $icon_size "app-${icon_size}.png" - fi - done - run $OBJCOPY --add-section serenity_icon_s="app-16x16.png" "${DESTDIR}${launcher}" - run $OBJCOPY --add-section serenity_icon_m="app-32x32.png" "${DESTDIR}${launcher}" -} - -install_main_launcher() { - if [ -n "$launcher_name" ] && [ -n "$launcher_category" ] && [ -n "$launcher_command" ]; then - install_launcher "$launcher_name" "$launcher_category" "$launcher_command" "$launcher_workdir" - fi -} - -install_launcher() { - if [ "$#" -lt 4 ]; then - echo "Syntax: install_launcher " - exit 1 - fi - local launcher_name="$1" - local launcher_category="$2" - local launcher_command="$3" - local launcher_workdir="$4" - local launcher_filename="${launcher_name,,}" - launcher_filename="${launcher_filename// /}" - local icon_override="" - case "$launcher_command" in - *\ *) - mkdir -p $DESTDIR/usr/local/libexec - launcher_executable="/usr/local/libexec/$launcher_filename" - cat >"$DESTDIR/$launcher_executable" < -)"sv)); - TRY(main_element.serialize(builder)); - TRY(builder.try_append(""sv)); - return builder.to_byte_string(); -} diff --git a/Userland/Applications/Presenter/Presentation.h b/Userland/Applications/Presenter/Presentation.h deleted file mode 100644 index 91fbca84233..00000000000 --- a/Userland/Applications/Presenter/Presentation.h +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright (c) 2022, kleines Filmröllchen - * Copyright (c) 2023, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include "Slide.h" -#include -#include -#include -#include -#include - -static constexpr int const PRESENTATION_FORMAT_VERSION = 1; - -// In-memory representation of the presentation stored in a file. -// This class also contains all the parser code for loading .presenter files. -class Presentation { -public: - ~Presentation() = default; - - static ErrorOr> load_from_file(StringView file_name); - - StringView title() const; - StringView author() const; - Gfx::IntSize normative_size() const { return m_normative_size; } - - Slide const& current_slide() const { return m_slides[m_current_slide.value()]; } - unsigned current_slide_number() const { return m_current_slide.value(); } - unsigned current_frame_in_slide_number() const { return m_current_frame_in_slide.value(); } - - bool has_next_frame() const; - bool has_previous_frame() const; - void next_frame(); - void previous_frame(); - void go_to_first_slide(); - - ErrorOr render(); - -private: - static HashMap parse_metadata(JsonObject const& metadata_object); - static ErrorOr parse_presentation_size(JsonObject const& metadata_object); - - Presentation(Gfx::IntSize normative_size, HashMap metadata); - static NonnullOwnPtr construct(Gfx::IntSize normative_size, HashMap metadata); - - void append_slide(Slide slide); - - Vector m_slides {}; - // This is not a pixel size, but an abstract size used by the slide objects for relative positioning. - Gfx::IntSize m_normative_size; - HashMap m_metadata; - - Checked m_current_slide { 0 }; - Checked m_current_frame_in_slide { 0 }; -}; diff --git a/Userland/Applications/Presenter/PresenterWidget.cpp b/Userland/Applications/Presenter/PresenterWidget.cpp deleted file mode 100644 index 2058f301819..00000000000 --- a/Userland/Applications/Presenter/PresenterWidget.cpp +++ /dev/null @@ -1,226 +0,0 @@ -/* - * Copyright (c) 2022, kleines Filmröllchen - * Copyright (c) 2023, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "PresenterWidget.h" -#include "Presentation.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -PresenterWidget::PresenterWidget() -{ - set_min_size(200, 120); - set_fill_with_background_color(true); - m_web_view = add(); - m_web_view->set_frame_style(Gfx::FrameStyle::NoFrame); - m_web_view->set_scrollbars_enabled(false); - m_web_view->set_focus_policy(GUI::FocusPolicy::NoFocus); - m_web_view->set_content_scales_to_viewport(true); -} - -void PresenterWidget::resize_event(GUI::ResizeEvent& event) -{ - Widget::resize_event(event); - - if (!m_current_presentation) - return; - - auto normative_size = m_current_presentation->normative_size().to_type(); - float widget_ratio = static_cast(event.size().width()) / static_cast(event.size().height()); - float wh_ratio = normative_size.width() / normative_size.height(); - - Gfx::IntRect rect; - if (widget_ratio >= wh_ratio) { - rect.set_width(static_cast(ceilf(static_cast(event.size().height()) * wh_ratio))); - rect.set_height(event.size().height()); - } else { - float hw_ratio = normative_size.height() / normative_size.width(); - rect.set_width(event.size().width()); - rect.set_height(static_cast(ceilf(static_cast(event.size().width()) * hw_ratio))); - } - m_web_view->set_relative_rect(rect.centered_within(this->rect())); -} - -ErrorOr PresenterWidget::initialize_menubar() -{ - auto* window = this->window(); - // Set up the menu bar. - auto file_menu = window->add_menu("&File"_string); - auto open_action = GUI::CommonActions::make_open_action([this](auto&) { - FileSystemAccessClient::OpenFileOptions options { - .allowed_file_types = { { GUI::FileTypeFilter { "Presentation Files", { { "presenter" } } }, GUI::FileTypeFilter::all_files() } }, - }; - auto response = FileSystemAccessClient::Client::the().open_file(this->window(), options); - if (response.is_error()) - return; - this->set_file(response.value().filename()); - }); - file_menu->add_action(open_action); - file_menu->add_separator(); - file_menu->add_action(GUI::CommonActions::make_quit_action([](auto&) { - GUI::Application::the()->quit(); - })); - - auto presentation_menu = window->add_menu("&Presentation"_string); - m_next_slide_action = GUI::Action::create("&Next", { KeyCode::Key_Right }, TRY(Gfx::Bitmap::load_from_file("/res/icons/16x16/go-forward.png"sv)), [this](auto&) { - if (m_current_presentation) { - m_current_presentation->next_frame(); - update_web_view(); - update_slides_actions(); - } - }); - m_previous_slide_action = GUI::Action::create("&Previous", { KeyCode::Key_Left }, TRY(Gfx::Bitmap::load_from_file("/res/icons/16x16/go-back.png"sv)), [this](auto&) { - if (m_current_presentation) { - m_current_presentation->previous_frame(); - update_web_view(); - update_slides_actions(); - } - }); - m_present_from_first_slide_action = GUI::Action::create("Present from First &Slide", { KeyCode::Key_F5 }, TRY(Gfx::Bitmap::load_from_file("/res/icons/16x16/play.png"sv)), [this](auto&) { - if (m_current_presentation) { - m_current_presentation->go_to_first_slide(); - update_web_view(); - } - this->window()->set_fullscreen(true); - }); - - presentation_menu->add_action(*m_next_slide_action); - presentation_menu->add_action(*m_previous_slide_action); - presentation_menu->add_action(*m_present_from_first_slide_action); - - auto view_menu = window->add_menu("&View"_string); - m_full_screen_action = GUI::Action::create("Toggle &Full Screen", { KeyModifier::Mod_Shift, KeyCode::Key_F5 }, { KeyCode::Key_F11 }, TRY(Gfx::Bitmap::load_from_file("/res/icons/16x16/fullscreen.png"sv)), [this](auto&) { - auto* window = this->window(); - window->set_fullscreen(!window->is_fullscreen()); - }); - m_resize_to_fit_content_action = GUI::Action::create("Resize to Fit &Content", { KeyModifier::Mod_Alt, KeyCode::Key_C }, TRY(Gfx::Bitmap::load_from_file("/res/icons/16x16/scale.png"sv)), [this](auto&) { - if (m_current_presentation) { - auto presentation_size = m_current_presentation->normative_size(); - auto* window = this->window(); - - window->resize(window->size().match_aspect_ratio(presentation_size.aspect_ratio(), Orientation::Horizontal)); - } - }); - - view_menu->add_action(*m_full_screen_action); - view_menu->add_action(*m_resize_to_fit_content_action); - - update_slides_actions(); - - auto help_menu = window->add_menu("&Help"_string); - help_menu->add_action(GUI::CommonActions::make_help_action([](auto&) { - Desktop::Launcher::open(URL::create_with_file_scheme("/usr/share/man/man1/Applications/Presenter.md"), "/bin/Help"); - })); - help_menu->add_action(GUI::CommonActions::make_about_action("Presenter"_string, GUI::Icon::default_icon("app-presenter"sv))); - - return {}; -} - -void PresenterWidget::update_web_view() -{ - m_web_view->run_javascript(ByteString::formatted("goto({}, {})", m_current_presentation->current_slide_number(), m_current_presentation->current_frame_in_slide_number())); -} - -void PresenterWidget::update_slides_actions() -{ - if (m_current_presentation) { - m_next_slide_action->set_enabled(m_current_presentation->has_next_frame()); - m_previous_slide_action->set_enabled(m_current_presentation->has_previous_frame()); - m_full_screen_action->set_enabled(true); - m_present_from_first_slide_action->set_enabled(true); - } else { - m_next_slide_action->set_enabled(false); - m_previous_slide_action->set_enabled(false); - m_full_screen_action->set_enabled(false); - m_present_from_first_slide_action->set_enabled(false); - } -} - -void PresenterWidget::set_file(StringView file_name) -{ - auto presentation = Presentation::load_from_file(file_name); - if (presentation.is_error()) { - GUI::MessageBox::show_error(window(), ByteString::formatted("The presentation \"{}\" could not be loaded.\n{}", file_name, presentation.error())); - } else { - m_current_presentation = presentation.release_value(); - window()->set_title(ByteString::formatted(title_template, m_current_presentation->title(), m_current_presentation->author())); - set_min_size(m_current_presentation->normative_size()); - m_web_view->load_html(MUST(m_current_presentation->render())); - update_slides_actions(); - } -} - -void PresenterWidget::keydown_event(GUI::KeyEvent& event) -{ - if (event.key() == Key_Escape && window()->is_fullscreen()) - window()->set_fullscreen(false); - - // Alternate shortcuts for forward and backward - switch (event.key()) { - case Key_Down: - case Key_PageDown: - case Key_Space: - case Key_N: - case Key_Return: - m_next_slide_action->activate(); - event.accept(); - break; - case Key_Up: - case Key_Backspace: - case Key_PageUp: - case Key_P: - m_previous_slide_action->activate(); - event.accept(); - break; - default: - event.ignore(); - break; - } -} - -void PresenterWidget::paint_event(GUI::PaintEvent& event) -{ - GUI::Painter painter(*this); - painter.clear_rect(event.rect(), Gfx::Color::Black); -} - -void PresenterWidget::second_paint_event(GUI::PaintEvent& event) -{ - if (!m_current_presentation) - return; - GUI::Painter painter(*this); - painter.add_clip_rect(event.rect()); - painter.draw_text(m_web_view->relative_rect(), m_current_presentation->current_slide().title(), Gfx::TextAlignment::BottomCenter); -} - -void PresenterWidget::drag_enter_event(GUI::DragEvent& event) -{ - auto const& mime_types = event.mime_types(); - if (mime_types.contains_slow("text/uri-list"sv)) - event.accept(); -} - -void PresenterWidget::drop_event(GUI::DropEvent& event) -{ - event.accept(); - - if (event.mime_data().has_urls()) { - auto urls = event.mime_data().urls(); - if (urls.is_empty()) - return; - - window()->move_to_front(); - set_file(urls.first().serialize_path()); - } -} diff --git a/Userland/Applications/Presenter/PresenterWidget.h b/Userland/Applications/Presenter/PresenterWidget.h deleted file mode 100644 index b23795685ea..00000000000 --- a/Userland/Applications/Presenter/PresenterWidget.h +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright (c) 2022, kleines Filmröllchen - * Copyright (c) 2023, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include "Presentation.h" -#include -#include -#include -#include -#include - -// Title, Author -constexpr StringView const title_template = "{} ({}) — Presenter"sv; - -class PresenterWidget : public GUI::Widget { - C_OBJECT(PresenterWidget); - -public: - PresenterWidget(); - ErrorOr initialize_menubar(); - - virtual ~PresenterWidget() override = default; - - // Errors that happen here are directly displayed to the user. - void set_file(StringView file_name); - -protected: - virtual void paint_event(GUI::PaintEvent&) override; - virtual void second_paint_event(GUI::PaintEvent&) override; - virtual void keydown_event(GUI::KeyEvent&) override; - virtual void drag_enter_event(GUI::DragEvent&) override; - virtual void drop_event(GUI::DropEvent&) override; - virtual void resize_event(GUI::ResizeEvent&) override; - -private: - void update_web_view(); - void update_slides_actions(); - - RefPtr m_web_view; - - OwnPtr m_current_presentation; - RefPtr m_next_slide_action; - RefPtr m_previous_slide_action; - RefPtr m_present_from_first_slide_action; - - RefPtr m_full_screen_action; - RefPtr m_resize_to_fit_content_action; -}; diff --git a/Userland/Applications/Presenter/Slide.cpp b/Userland/Applications/Presenter/Slide.cpp deleted file mode 100644 index db6684efd97..00000000000 --- a/Userland/Applications/Presenter/Slide.cpp +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright (c) 2022, kleines Filmröllchen - * Copyright (c) 2023, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "Slide.h" -#include "Presentation.h" -#include - -Slide::Slide(unsigned frame_count, Vector> slide_objects, ByteString title) - : m_frame_count(move(frame_count)) - , m_slide_objects(move(slide_objects)) - , m_title(move(title)) -{ -} - -ErrorOr Slide::parse_slide(JsonObject const& slide_json, unsigned slide_index) -{ - // FIXME: Use the text with the "title" role for a title, if there is no title given. - auto title = slide_json.get_byte_string("title"sv).value_or("Untitled slide"); - auto frame_count = slide_json.get_u32("frame_count"sv).value_or(1); - auto maybe_slide_objects = slide_json.get_array("objects"sv); - if (!maybe_slide_objects.has_value()) - return Error::from_string_view("Slide objects must be an array"sv); - - auto const& json_slide_objects = maybe_slide_objects.value(); - Vector> slide_objects; - for (auto const& maybe_slide_object_json : json_slide_objects.values()) { - if (!maybe_slide_object_json.is_object()) - return Error::from_string_view("Slides must be objects"sv); - auto const& slide_object_json = maybe_slide_object_json.as_object(); - - auto slide_object = TRY(SlideObject::parse_slide_object(slide_object_json, slide_index)); - slide_objects.append(move(slide_object)); - } - - return Slide { frame_count, move(slide_objects), title }; -} - -ErrorOr Slide::render(Presentation const& presentation) const -{ - HTMLElement wrapper; - wrapper.tag_name = "div"sv; - for (auto const& object : m_slide_objects) - TRY(wrapper.children.try_append(TRY(object->render(presentation)))); - return wrapper; -} diff --git a/Userland/Applications/Presenter/Slide.h b/Userland/Applications/Presenter/Slide.h deleted file mode 100644 index 3baf5a420c2..00000000000 --- a/Userland/Applications/Presenter/Slide.h +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright (c) 2022, kleines Filmröllchen - * Copyright (c) 2023, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include "SlideObject.h" -#include -#include - -// A single slide of a presentation. -class Slide final { -public: - static ErrorOr parse_slide(JsonObject const& slide_json, unsigned slide_index); - - unsigned frame_count() const { return m_frame_count; } - StringView title() const { return m_title; } - - ErrorOr render(Presentation const&) const; - -private: - Slide(unsigned frame_count, Vector> slide_objects, ByteString title); - - unsigned m_frame_count; - Vector> m_slide_objects; - ByteString m_title; -}; diff --git a/Userland/Applications/Presenter/SlideObject.cpp b/Userland/Applications/Presenter/SlideObject.cpp deleted file mode 100644 index a3cb7afd909..00000000000 --- a/Userland/Applications/Presenter/SlideObject.cpp +++ /dev/null @@ -1,157 +0,0 @@ -/* - * Copyright (c) 2022, kleines Filmröllchen - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "SlideObject.h" -#include "Presentation.h" -#include -#include -#include -#include -#include - -static ByteString to_css_length(float design_value, Presentation const& presentation) -{ - float length_in_vw = design_value / static_cast(presentation.normative_size().width()) * 100.0f; - return ByteString::formatted("{}vw", length_in_vw); -} - -ErrorOr> SlideObject::parse_slide_object(JsonObject const& slide_object_json, unsigned slide_index) -{ - auto frame = slide_object_json.get_u32("frame"sv).value_or(0); - auto maybe_type = slide_object_json.get_byte_string("type"sv); - if (!maybe_type.has_value()) - return Error::from_string_view("Slide object must have a type"sv); - - auto type = maybe_type.value(); - RefPtr object; - if (type == "text"sv) - object = TRY(try_make_ref_counted(Index { slide_index, frame })); - else if (type == "image"sv) - object = TRY(try_make_ref_counted(Index { slide_index, frame })); - else - return Error::from_string_view("Unsupported slide object type"sv); - - slide_object_json.for_each_member([&](auto const& key, auto const& value) { - object->set_property(key, value); - }); - - return object.release_nonnull(); -} - -void SlideObject::set_property(StringView name, JsonValue value) -{ - if (name == "rect"sv) - m_rect = GUI::PropertyDeserializer {}(value).release_value_but_fixme_should_propagate_errors(); - m_properties.set(name, move(value)); -} - -void GraphicsObject::set_property(StringView name, JsonValue value) -{ - if (name == "color"sv) { - if (auto color = Gfx::Color::from_string(value.as_string()); color.has_value()) { - m_color = color.release_value(); - } - } - SlideObject::set_property(name, move(value)); -} - -void Text::set_property(StringView name, JsonValue value) -{ - if (name == "text"sv) { - m_text = value.as_string(); - } else if (name == "font"sv) { - m_font_family = value.as_string(); - } else if (name == "font-weight"sv) { - m_font_weight = Gfx::name_to_weight(value.as_string()); - } else if (name == "font-size"sv) { - m_font_size_in_pt = value.get_float_with_precision_loss().value(); - } else if (name == "text-alignment"sv) { - m_text_align = value.as_string(); - } - GraphicsObject::set_property(name, move(value)); -} - -void Image::set_property(StringView name, JsonValue value) -{ - if (name == "path"sv) { - m_src = value.as_string(); - } else if (name == "scaling-mode"sv) { - if (value.as_string() == "nearest-neighbor"sv) - m_image_rendering = "crisp-edges"sv; - else if (value.as_string() == "smooth-pixels"sv) - m_image_rendering = "pixelated"sv; - } - SlideObject::set_property(name, move(value)); -} - -ErrorOr Text::render(Presentation const& presentation) const -{ - HTMLElement div; - div.tag_name = "div"sv; - TRY(div.attributes.try_set("class"sv, ByteString::formatted("frame slide{}-frame{}", m_slide_index, m_frame_index))); - div.style.set("color"sv, m_color.to_byte_string()); - div.style.set("font-family"sv, ByteString::formatted("'{}'", m_font_family)); - div.style.set("font-size"sv, to_css_length(m_font_size_in_pt * 1.33333333f, presentation)); - div.style.set("font-weight"sv, ByteString::number(m_font_weight)); - div.style.set("text-align"sv, m_text_align); - div.style.set("white-space"sv, "pre-wrap"sv); - div.style.set("width"sv, to_css_length(m_rect.width(), presentation)); - div.style.set("height"sv, to_css_length(m_rect.height(), presentation)); - div.style.set("position"sv, "absolute"sv); - div.style.set("left"sv, to_css_length(m_rect.left(), presentation)); - div.style.set("top"sv, to_css_length(m_rect.top(), presentation)); - div.inner_text = m_text; - return div; -} - -ErrorOr Image::render(Presentation const& presentation) const -{ - HTMLElement img; - img.tag_name = "img"sv; - img.attributes.set("src"sv, URL::create_with_file_scheme(m_src).to_byte_string()); - img.style.set("image-rendering"sv, m_image_rendering); - if (m_rect.width() > m_rect.height()) - img.style.set("height"sv, "100%"sv); - else - img.style.set("width"sv, "100%"sv); - - HTMLElement image_wrapper; - image_wrapper.tag_name = "div"sv; - TRY(image_wrapper.attributes.try_set("class"sv, ByteString::formatted("frame slide{}-frame{}", m_slide_index, m_frame_index))); - image_wrapper.children.append(move(img)); - image_wrapper.style.set("position"sv, "absolute"sv); - image_wrapper.style.set("left"sv, to_css_length(m_rect.left(), presentation)); - image_wrapper.style.set("top"sv, to_css_length(m_rect.top(), presentation)); - image_wrapper.style.set("width"sv, to_css_length(m_rect.width(), presentation)); - image_wrapper.style.set("height"sv, to_css_length(m_rect.height(), presentation)); - image_wrapper.style.set("text-align"sv, "center"sv); - return image_wrapper; -} - -ErrorOr HTMLElement::serialize(StringBuilder& builder) const -{ - TRY(builder.try_appendff("<{}", tag_name)); - for (auto const& [key, value] : attributes) { - // FIXME: Escape the value string as necessary. - TRY(builder.try_appendff(" {}='{}'", key, value)); - } - if (!style.is_empty()) { - TRY(builder.try_append(" style=\""sv)); - for (auto const& [key, value] : style) { - // FIXME: Escape the value string as necessary. - TRY(builder.try_appendff(" {}: {};", key, value)); - } - TRY(builder.try_append("\""sv)); - } - TRY(builder.try_append(">"sv)); - if (!inner_text.is_empty()) - TRY(builder.try_append(inner_text)); - for (auto const& child : children) { - TRY(child.serialize(builder)); - } - TRY(builder.try_appendff("", tag_name)); - return {}; -} diff --git a/Userland/Applications/Presenter/SlideObject.h b/Userland/Applications/Presenter/SlideObject.h deleted file mode 100644 index cc5c152bea7..00000000000 --- a/Userland/Applications/Presenter/SlideObject.h +++ /dev/null @@ -1,102 +0,0 @@ -/* - * Copyright (c) 2022, kleines Filmröllchen - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include - -class Presentation; - -struct HTMLElement { - StringView tag_name; - HashMap attributes; - HashMap style; - ByteString inner_text; - Vector children; - - ErrorOr serialize(StringBuilder&) const; -}; -struct Index { - unsigned slide; - unsigned frame; -}; - -// Anything that can be on a slide. -class SlideObject : public RefCounted { -public: - virtual ~SlideObject() = default; - static ErrorOr> parse_slide_object(JsonObject const& slide_object_json, unsigned slide_index); - virtual ErrorOr render(Presentation const&) const = 0; - -protected: - SlideObject(Index index) - : m_frame_index(index.frame) - , m_slide_index(index.slide) - { - } - - virtual void set_property(StringView name, JsonValue); - - unsigned m_frame_index; - unsigned m_slide_index; - HashMap m_properties; - Gfx::IntRect m_rect; -}; - -// Objects with a foreground color. -class GraphicsObject : public SlideObject { -public: - virtual ~GraphicsObject() = default; - -protected: - GraphicsObject(Index index) - : SlideObject(index) - { - } - virtual void set_property(StringView name, JsonValue) override; - - // FIXME: Change the default color based on the color scheme - Gfx::Color m_color { Gfx::Color::Black }; -}; - -class Text final : public GraphicsObject { -public: - Text(Index index) - : GraphicsObject(index) - { - } - virtual ~Text() = default; - -private: - virtual ErrorOr render(Presentation const&) const override; - virtual void set_property(StringView name, JsonValue) override; - - ByteString m_text; - ByteString m_font_family; - ByteString m_text_align; - float m_font_size_in_pt { 18 }; - unsigned m_font_weight { Gfx::FontWeight::Regular }; -}; - -class Image final : public SlideObject { -public: - Image(Index index) - : SlideObject(index) - { - } - virtual ~Image() = default; - -private: - ByteString m_src; - StringView m_image_rendering; - - virtual ErrorOr render(Presentation const&) const override; - virtual void set_property(StringView name, JsonValue) override; -}; diff --git a/Userland/Applications/Presenter/main.cpp b/Userland/Applications/Presenter/main.cpp deleted file mode 100644 index 228d956e251..00000000000 --- a/Userland/Applications/Presenter/main.cpp +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright (c) 2022, kleines Filmröllchen - * Copyright (c) 2023, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "PresenterWidget.h" -#include -#include -#include -#include -#include -#include -#include - -ErrorOr serenity_main(Main::Arguments arguments) -{ - // rpath is required to load .presenter files, unix, sendfd and recvfd are required to talk to WindowServer and WebContent. - TRY(Core::System::pledge("stdio rpath unix sendfd recvfd")); - - ByteString file_to_load; - Core::ArgsParser argument_parser; - argument_parser.add_positional_argument(file_to_load, "Presentation to load", "file", Core::ArgsParser::Required::No); - argument_parser.parse(arguments); - - auto app = TRY(GUI::Application::create(arguments)); - - TRY(Desktop::Launcher::add_allowed_handler_with_only_specific_urls("/bin/Help", { URL::create_with_file_scheme("/usr/share/man/man1/Applications/Presenter.md") })); - TRY(Desktop::Launcher::seal_allowlist()); - - auto window = GUI::Window::construct(); - window->set_title("Presenter"); - window->set_icon(GUI::Icon::default_icon("app-presenter"sv).bitmap_for_size(16)); - window->restore_size_and_position("Presenter"sv, "Window"sv, { { 640, 400 } }); - window->save_size_and_position_on_close("Presenter"sv, "Window"sv); - auto main_widget = window->set_main_widget(); - TRY(main_widget->initialize_menubar()); - window->show(); - - if (!file_to_load.is_empty()) - main_widget->set_file(file_to_load); - - return app->exec(); -} diff --git a/Userland/Applications/Run/CMakeLists.txt b/Userland/Applications/Run/CMakeLists.txt deleted file mode 100644 index 46af0a3f2a6..00000000000 --- a/Userland/Applications/Run/CMakeLists.txt +++ /dev/null @@ -1,16 +0,0 @@ -serenity_component( - Run - REQUIRED - TARGETS Run -) - -compile_gml(Run.gml RunGML.cpp) - -set(SOURCES - main.cpp - RunWindow.cpp - RunGML.cpp -) - -serenity_app(Run ICON app-run) -target_link_libraries(Run PRIVATE LibCore LibFileSystem LibDesktop LibGfx LibGUI LibMain LibURL) diff --git a/Userland/Applications/Run/MainWidget.h b/Userland/Applications/Run/MainWidget.h deleted file mode 100644 index f82a34953be..00000000000 --- a/Userland/Applications/Run/MainWidget.h +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Copyright (c) 2023, kleines Filmröllchen - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include - -namespace Run { - -class MainWidget : public GUI::Widget { - C_OBJECT(MainWidget) -public: - MainWidget() = default; - static ErrorOr> try_create(); - - virtual ~MainWidget() override = default; -}; - -} diff --git a/Userland/Applications/Run/Run.gml b/Userland/Applications/Run/Run.gml deleted file mode 100644 index 768e2f21e9a..00000000000 --- a/Userland/Applications/Run/Run.gml +++ /dev/null @@ -1,61 +0,0 @@ -@Run::MainWidget { - fill_with_background_color: true - layout: @GUI::VerticalBoxLayout { - margins: [4] - } - - @GUI::Widget { - max_height: 32 - layout: @GUI::HorizontalBoxLayout { - spacing: 16 - } - - @GUI::ImageWidget { - name: "icon" - } - - @GUI::Label { - name: "info" - text: "Type the name of a program, folder, document,\nor website, and SerenityOS will open it for you." - text_alignment: "CenterLeft" - } - } - - @GUI::Widget { - layout: @GUI::HorizontalBoxLayout { - margins: [4] - } - - @GUI::Label { - text: "Open:" - fixed_width: 50 - text_alignment: "CenterLeft" - } - - @GUI::ComboBox { - name: "path" - } - } - - @GUI::Widget { - layout: @GUI::HorizontalBoxLayout {} - fixed_height: 22 - - @GUI::Layout::Spacer {} - - @GUI::DialogButton { - name: "ok_button" - text: "OK" - } - - @GUI::DialogButton { - name: "cancel_button" - text: "Cancel" - } - - @GUI::DialogButton { - name: "browse_button" - text: "Browse..." - } - } -} diff --git a/Userland/Applications/Run/RunWindow.cpp b/Userland/Applications/Run/RunWindow.cpp deleted file mode 100644 index c918567b0ab..00000000000 --- a/Userland/Applications/Run/RunWindow.cpp +++ /dev/null @@ -1,206 +0,0 @@ -/* - * Copyright (c) 2021, Nick Vella - * Copyright (c) 2022, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "RunWindow.h" -#include "MainWidget.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace Run { - -RunWindow::RunWindow() - : m_path_history() - , m_path_history_model(GUI::ItemListModel::create(m_path_history)) -{ - // FIXME: Handle failure to load history somehow. - (void)load_history(); - - auto app_icon = GUI::Icon::default_icon("app-run"sv); - - set_title("Run"); - set_icon(app_icon.bitmap_for_size(16)); - resize(345, 100); - set_resizable(false); - set_minimizable(false); - - auto main_widget = MUST(Run::MainWidget::try_create()); - set_main_widget(main_widget); - - m_icon_image_widget = *main_widget->find_descendant_of_type_named("icon"); - m_icon_image_widget->set_bitmap(app_icon.bitmap_for_size(32)); - - m_path_combo_box = *main_widget->find_descendant_of_type_named("path"); - m_path_combo_box->set_model(m_path_history_model); - if (!m_path_history.is_empty()) - m_path_combo_box->set_selected_index(0); - - m_ok_button = *main_widget->find_descendant_of_type_named("ok_button"); - m_ok_button->on_click = [this](auto) { - do_run(); - }; - m_ok_button->set_default(true); - - m_cancel_button = *main_widget->find_descendant_of_type_named("cancel_button"); - m_cancel_button->on_click = [this](auto) { - close(); - }; - - m_browse_button = *main_widget->find_descendant_of_type_named("browse_button"); - m_browse_button->on_click = [this](auto) { - Optional path = GUI::FilePicker::get_open_filepath(this, {}, Core::StandardPaths::home_directory(), false, GUI::Dialog::ScreenPosition::Center); - if (path.has_value()) - m_path_combo_box->set_text(path.value().view()); - }; -} - -void RunWindow::event(Core::Event& event) -{ - if (event.type() == GUI::Event::KeyDown) { - auto& key_event = static_cast(event); - if (key_event.key() == Key_Escape) { - // Escape key pressed, close dialog - close(); - return; - } else if ((key_event.key() == Key_Up || key_event.key() == Key_Down) && m_path_history.is_empty()) { - return; - } - } - - Window::event(event); -} - -void RunWindow::do_run() -{ - auto run_input = m_path_combo_box->text().trim_whitespace(); - - hide(); - - if (run_via_launch(run_input) || run_as_command(run_input)) { - close(); - return; - } - - GUI::MessageBox::show_error(this, "Failed to run. Please check your command, path, or address, and try again."sv); - - show(); -} - -bool RunWindow::run_as_command(ByteString const& run_input) -{ - // TODO: Query and use the user's preferred shell. - auto maybe_child_pid = Core::Process::spawn("/bin/Shell"sv, Array { "-c", run_input.characters() }, {}, Core::Process::KeepAsChild::Yes); - if (maybe_child_pid.is_error()) - return false; - - pid_t child_pid = maybe_child_pid.release_value(); - - // The child shell was able to start. Let's save it to the history immediately so users can see it as the first entry the next time they run this program. - prepend_history(run_input); - // FIXME: Handle failure to save history somehow. - (void)save_history(); - - int status; - if (waitpid(child_pid, &status, 0) < 0) - return false; - - int child_error = WEXITSTATUS(status); - dbgln("Child shell exited with code {}", child_error); - - // 127 is typically the shell indicating command not found. 126 for all other errors. - if (child_error == 126 || child_error == 127) { - // There's an opportunity to remove the history entry here since it failed during its runtime, but other implementations (e.g. Windows 11) don't bother removing the entry. - // This makes sense, especially for cases where a user is debugging a failing program. - return false; - } - - dbgln("Ran via command shell."); - - return true; -} - -bool RunWindow::run_via_launch(ByteString const& run_input) -{ - auto url = URL::create_with_url_or_path(run_input); - - if (url.scheme() == "file") { - auto file_path = url.serialize_path(); - auto real_path_or_error = FileSystem::real_path(file_path); - if (real_path_or_error.is_error()) { - warnln("Failed to launch '{}': {}", file_path, real_path_or_error.error()); - return false; - } - url = URL::create_with_url_or_path(real_path_or_error.release_value()); - } - - if (!Desktop::Launcher::open(url)) { - warnln("Failed to launch '{}'", url); - return false; - } - - prepend_history(run_input); - // FIXME: Handle failure to save history somehow. - (void)save_history(); - - dbgln("Ran via URL launch."); - - return true; -} - -ByteString RunWindow::history_file_path() -{ - return LexicalPath::canonicalized_path(ByteString::formatted("{}/{}", Core::StandardPaths::config_directory(), "RunHistory.txt")); -} - -ErrorOr RunWindow::load_history() -{ - m_path_history.clear(); - auto file = TRY(Core::File::open(history_file_path(), Core::File::OpenMode::Read)); - auto buffered_file = TRY(Core::InputBufferedFile::create(move(file))); - Array line_buffer; - - while (!buffered_file->is_eof()) { - StringView line = TRY(buffered_file->read_line(line_buffer)); - if (!line.is_empty() && !line.is_whitespace()) - m_path_history.append(line); - } - return {}; -} - -void RunWindow::prepend_history(ByteString const& input) -{ - m_path_history.remove_all_matching([&](ByteString const& entry) { return input == entry; }); - m_path_history.prepend(input); -} - -ErrorOr RunWindow::save_history() -{ - auto file = TRY(Core::File::open(history_file_path(), Core::File::OpenMode::Write)); - - // Write the first 25 items of history - for (int i = 0; i < min(static_cast(m_path_history.size()), 25); i++) - TRY(file->write_until_depleted(ByteString::formatted("{}\n", m_path_history[i]))); - - return {}; -} - -} diff --git a/Userland/Applications/Run/RunWindow.h b/Userland/Applications/Run/RunWindow.h deleted file mode 100644 index 223d387181f..00000000000 --- a/Userland/Applications/Run/RunWindow.h +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright (c) 2021, Nick Vella - * Copyright (c) 2022, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include - -namespace Run { - -class RunWindow final : public GUI::Window { - C_OBJECT(RunWindow) -public: - virtual ~RunWindow() override = default; - - virtual void event(Core::Event&) override; - -private: - RunWindow(); - - void do_run(); - bool run_as_command(ByteString const& run_input); - bool run_via_launch(ByteString const& run_input); - - ByteString history_file_path(); - ErrorOr load_history(); - ErrorOr save_history(); - void prepend_history(ByteString const& input); - - Vector m_path_history; - NonnullRefPtr> m_path_history_model; - - RefPtr m_icon_image_widget; - RefPtr m_ok_button; - RefPtr m_cancel_button; - RefPtr m_browse_button; - RefPtr m_path_combo_box; -}; - -} diff --git a/Userland/Applications/Run/main.cpp b/Userland/Applications/Run/main.cpp deleted file mode 100644 index cf0cae0cf1d..00000000000 --- a/Userland/Applications/Run/main.cpp +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright (c) 2021, Nick Vella - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "RunWindow.h" -#include -#include -#include -#include - -ErrorOr serenity_main(Main::Arguments arguments) -{ - TRY(Core::System::pledge("stdio recvfd sendfd thread cpath rpath wpath unix proc exec")); - - auto app = TRY(GUI::Application::create(arguments)); - auto window = TRY(Run::RunWindow::try_create()); - - constexpr int margin = 16; - window->move_to(margin, GUI::Desktop::the().rect().bottom() - 1 - GUI::Desktop::the().taskbar_height() - margin - window->height()); - window->show(); - - return app->exec(); -} diff --git a/Userland/Applications/Screenshot/CMakeLists.txt b/Userland/Applications/Screenshot/CMakeLists.txt deleted file mode 100644 index bf066a9eb61..00000000000 --- a/Userland/Applications/Screenshot/CMakeLists.txt +++ /dev/null @@ -1,16 +0,0 @@ -serenity_component( - Screenshot - RECOMMENDED - TARGETS Screenshot -) - -compile_gml(Screenshot.gml ScreenshotGML.cpp) - -set(SOURCES - ScreenshotGML.cpp - MainWindow.cpp - main.cpp -) - -serenity_app(Screenshot ICON app-screenshot) -target_link_libraries(Screenshot PRIVATE LibCore LibConfig LibGfx LibGUI LibMain) diff --git a/Userland/Applications/Screenshot/MainWidget.h b/Userland/Applications/Screenshot/MainWidget.h deleted file mode 100644 index 4ec71e917f0..00000000000 --- a/Userland/Applications/Screenshot/MainWidget.h +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Copyright (c) 2024, circl - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include - -namespace Screenshot { - -class MainWidget : public GUI::Widget { - C_OBJECT(MainWidget) -public: - MainWidget() = default; - static ErrorOr> try_create(); - - virtual ~MainWidget() override = default; -}; - -} diff --git a/Userland/Applications/Screenshot/MainWindow.cpp b/Userland/Applications/Screenshot/MainWindow.cpp deleted file mode 100644 index e11a43de393..00000000000 --- a/Userland/Applications/Screenshot/MainWindow.cpp +++ /dev/null @@ -1,81 +0,0 @@ -/* - * Copyright (c) 2024, circl - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "MainWindow.h" -#include "MainWidget.h" -#include -#include -#include -#include -#include - -namespace Screenshot { - -MainWindow::MainWindow() -{ - auto app_icon = GUI::Icon::default_icon("app-screenshot"sv); - - set_title("Screenshot"); - set_icon(app_icon.bitmap_for_size(16)); - resize(300, 150); - set_resizable(false); - set_minimizable(false); - - auto main_widget = MUST(MainWidget::try_create()); - set_main_widget(main_widget); - - m_ok_button = *main_widget->find_descendant_of_type_named("ok_button"); - m_ok_button->set_default(true); - m_ok_button->on_click = [this](auto) { - take_screenshot(); - }; - - m_cancel_button = *main_widget->find_descendant_of_type_named("cancel_button"); - m_cancel_button->on_click = [this](auto) { - close(); - }; - - m_browse = *main_widget->find_descendant_of_type_named("browse"); - m_browse->set_icon(Gfx::Bitmap::load_from_file("/res/icons/16x16/open.png"sv).release_value_but_fixme_should_propagate_errors()); - m_browse->on_click = [this](auto) { - auto filepath = GUI::FilePicker::get_open_filepath(this, "Save screenshot to...", m_destination->text(), true); - - if (filepath.has_value()) { - Config::write_string("Screenshot"sv, "General"sv, "SavePath"sv, filepath.value()); - m_destination->set_text(filepath.value()); - m_destination->repaint(); - } - }; - - m_selected_area = *main_widget->find_descendant_of_type_named("selected_area"); - - m_edit_in_pixel_paint = *main_widget->find_descendant_of_type_named("edit_in_pixel_paint"); - m_edit_in_pixel_paint->on_checked = [this](bool is_checked) { - m_browse->set_enabled(!is_checked); - m_destination->set_enabled(!is_checked); - }; - - m_destination = *main_widget->find_descendant_of_type_named("destination"); - m_destination->set_text(Config::read_string("Screenshot"sv, "General"sv, "SavePath"sv, Core::StandardPaths::pictures_directory())); -} - -void MainWindow::take_screenshot() -{ - close(); - - Vector arguments; - - if (m_selected_area->is_checked()) - arguments.append("-r"sv); - - if (m_edit_in_pixel_paint->is_checked()) - arguments.append("-e"sv); - - // FIXME: Place common screenshot code into library and use that - MUST(Core::Process::spawn("/bin/shot"sv, arguments, m_destination->text(), Core::Process::KeepAsChild::No)); -} - -} diff --git a/Userland/Applications/Screenshot/MainWindow.h b/Userland/Applications/Screenshot/MainWindow.h deleted file mode 100644 index f8694450636..00000000000 --- a/Userland/Applications/Screenshot/MainWindow.h +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright (c) 2024, circl - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include - -namespace Screenshot { - -class MainWindow final : public GUI::Window { - C_OBJECT(MainWindow) -public: - virtual ~MainWindow() override = default; - -private: - MainWindow(); - - void take_screenshot(); - - RefPtr m_ok_button; - RefPtr m_cancel_button; - RefPtr m_browse; - RefPtr m_selected_area; - RefPtr m_edit_in_pixel_paint; - RefPtr m_destination; -}; - -} diff --git a/Userland/Applications/Screenshot/Screenshot.gml b/Userland/Applications/Screenshot/Screenshot.gml deleted file mode 100644 index 5ebf0b7ad27..00000000000 --- a/Userland/Applications/Screenshot/Screenshot.gml +++ /dev/null @@ -1,80 +0,0 @@ -@Screenshot::MainWidget { - fill_with_background_color: true - layout: @GUI::HorizontalBoxLayout { - margins: [6, 4, 4, 4] - spacing: 6 - } - - @GUI::Widget { - max_width: 32 - layout: @GUI::VerticalBoxLayout {} - - @GUI::ImageWidget { - bitmap: "/res/icons/32x32/app-screenshot.png" - } - } - - @GUI::Widget { - layout: @GUI::VerticalBoxLayout {} - - @GUI::GroupBox { - title: "Take screenshot of:" - layout: @GUI::VerticalBoxLayout { - margins: [4] - spacing: 0 - } - - @GUI::RadioButton { - name: "whole_desktop" - text: "Whole desktop" - checked: true - } - - @GUI::RadioButton { - name: "selected_area" - text: "Selected area" - } - } - - @GUI::Widget { - layout: @GUI::VerticalBoxLayout { - margins: [0, 0, 0, 8] - } - - @GUI::CheckBox { - name: "edit_in_pixel_paint" - text: "Edit in Pixel Paint" - } - } - - @GUI::Widget { - layout: @GUI::HorizontalBoxLayout {} - - @GUI::TextBox { - name: "destination" - mode: "DisplayOnly" - } - - @GUI::Button { - name: "browse" - max_width: 22 - } - } - - @GUI::Widget { - layout: @GUI::HorizontalBoxLayout {} - - @GUI::Layout::Spacer {} - - @GUI::DialogButton { - name: "ok_button" - text: "OK" - } - - @GUI::DialogButton { - name: "cancel_button" - text: "Cancel" - } - } - } -} diff --git a/Userland/Applications/Screenshot/main.cpp b/Userland/Applications/Screenshot/main.cpp deleted file mode 100644 index bc1e06bd249..00000000000 --- a/Userland/Applications/Screenshot/main.cpp +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright (c) 2024, circl - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "MainWindow.h" -#include -#include -#include -#include - -ErrorOr serenity_main(Main::Arguments arguments) -{ - TRY(Core::System::pledge("stdio recvfd sendfd thread cpath rpath wpath unix proc exec")); - - auto app = TRY(GUI::Application::create(arguments)); - app->set_config_domain("Screenshot"_string); - - auto window = TRY(Screenshot::MainWindow::try_create()); - - window->show(); - - return app->exec(); -} diff --git a/Userland/Applications/Settings/CMakeLists.txt b/Userland/Applications/Settings/CMakeLists.txt deleted file mode 100644 index aa12d8a7574..00000000000 --- a/Userland/Applications/Settings/CMakeLists.txt +++ /dev/null @@ -1,12 +0,0 @@ -serenity_component( - Settings - REQUIRED - TARGETS Settings -) - -set(SOURCES - main.cpp -) - -serenity_app(Settings ICON app-settings) -target_link_libraries(Settings PRIVATE LibCore LibGfx LibGUI LibDesktop LibMain) diff --git a/Userland/Applications/Settings/main.cpp b/Userland/Applications/Settings/main.cpp deleted file mode 100644 index b576cc9aafe..00000000000 --- a/Userland/Applications/Settings/main.cpp +++ /dev/null @@ -1,148 +0,0 @@ -/* - * Copyright (c) 2021, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -// Modeled after PlaylistWidget. -enum class SettingsAppsModelCustomRole { - _DONOTUSE = (int)GUI::ModelRole::Custom, - RequiresRoot -}; - -class SettingsAppsModel final : public GUI::Model { -public: - SettingsAppsModel() - { - Desktop::AppFile::for_each([&](Desktop::AppFile& app_file) { - if (app_file.category() != "Settings") - return; - m_apps.append(app_file); - }); - quick_sort(m_apps, [](auto& a, auto& b) { return a->name() < b->name(); }); - } - virtual int row_count(GUI::ModelIndex const&) const override { return m_apps.size(); } - virtual int column_count(GUI::ModelIndex const&) const override { return 1; } - - virtual GUI::ModelIndex index(int row, int column, GUI::ModelIndex const&) const override - { - if (row < 0 || row >= (int)m_apps.size()) - return {}; - return create_index(row, column, &m_apps[row]); - } - - virtual GUI::Variant data(GUI::ModelIndex const& index, GUI::ModelRole role) const override - { - auto& app = m_apps[index.row()]; - if (role == GUI::ModelRole::Icon) - return app->icon(); - - if (role == GUI::ModelRole::Display) { - ByteString name; - - if (app->name().ends_with(" Settings"sv)) - name = app->name().substring(0, app->name().length() - " Settings"sv.length()); - else - name = app->name(); - - return name; - } - - if (role == GUI::ModelRole::Custom) - return app->executable(); - - if (role == static_cast(SettingsAppsModelCustomRole::RequiresRoot)) - return app->requires_root(); - - return {}; - } - -private: - Vector> m_apps; -}; - -ErrorOr serenity_main(Main::Arguments arguments) -{ - TRY(Core::System::pledge("stdio thread recvfd sendfd rpath cpath wpath unix proc exec")); - - auto app = TRY(GUI::Application::create(arguments)); - - TRY(Core::System::pledge("stdio thread recvfd sendfd rpath cpath wpath proc exec")); - - auto app_icon = GUI::Icon::default_icon("app-settings"sv); - - auto window = GUI::Window::construct(); - window->set_title("Settings"); - window->resize(420, 265); - - auto file_menu = window->add_menu("&File"_string); - file_menu->add_action(GUI::CommonActions::make_quit_action([&](auto&) { - app->quit(); - })); - - auto view_menu = window->add_menu("&View"_string); - view_menu->add_action(GUI::CommonActions::make_fullscreen_action([&](auto&) { - window->set_fullscreen(!window->is_fullscreen()); - })); - - auto help_menu = window->add_menu("&Help"_string); - help_menu->add_action(GUI::CommonActions::make_command_palette_action(window)); - help_menu->add_action(GUI::CommonActions::make_about_action("Settings"_string, app_icon, window)); - - auto main_widget = window->set_main_widget(); - main_widget->set_fill_with_background_color(true); - main_widget->set_layout(); - - auto& icon_view = main_widget->add(); - icon_view.set_should_hide_unnecessary_scrollbars(true); - auto model = adopt_ref(*new SettingsAppsModel); - icon_view.set_model(*model); - - icon_view.on_activation = [&](GUI::ModelIndex const& index) { - auto executable = model->data(index, GUI::ModelRole::Custom).as_string(); - auto requires_root = model->data(index, static_cast(SettingsAppsModelCustomRole::RequiresRoot)).as_bool(); - - auto launch_origin_rect = icon_view.to_widget_rect(icon_view.content_rect(index)).translated(icon_view.screen_relative_rect().location()); - setenv("__libgui_launch_origin_rect", ByteString::formatted("{},{},{},{}", launch_origin_rect.x(), launch_origin_rect.y(), launch_origin_rect.width(), launch_origin_rect.height()).characters(), 1); - - if (requires_root) - GUI::Process::spawn_or_show_error(window, "/bin/Escalator"sv, Array { executable }); - else - GUI::Process::spawn_or_show_error(window, executable); - }; - - auto& statusbar = main_widget->add(); - - icon_view.on_selection_change = [&] { - auto index = icon_view.selection().first(); - if (!index.is_valid()) { - statusbar.set_text({}); - return; - } - - auto& app = *(NonnullRefPtr*)index.internal_data(); - statusbar.set_text(String::from_byte_string(app->description()).release_value_but_fixme_should_propagate_errors()); - }; - - window->set_icon(app_icon.bitmap_for_size(16)); - - window->show(); - return app->exec(); -} diff --git a/Userland/Applications/SoundPlayer/AlbumCoverVisualizationWidget.cpp b/Userland/Applications/SoundPlayer/AlbumCoverVisualizationWidget.cpp deleted file mode 100644 index 5c56a5bdaa3..00000000000 --- a/Userland/Applications/SoundPlayer/AlbumCoverVisualizationWidget.cpp +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Copyright (c) 2021, Cesar Torres - * Copyright (c) 2022, the SerenityOS developers. - * Copyright (c) 2022, Nícolas F. R. A. Prado - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "AlbumCoverVisualizationWidget.h" -#include -#include -#include -#include - -AlbumCoverVisualizationWidget::AlbumCoverVisualizationWidget(Function()> get_file_cover_from_player) - : m_get_file_cover_from_player(move(get_file_cover_from_player)) -{ -} - -void AlbumCoverVisualizationWidget::paint_event(GUI::PaintEvent& event) -{ - Frame::paint_event(event); - GUI::Painter painter(*this); - - auto const& cover = m_file_cover ? m_file_cover : m_album_cover; - if (cover) { - auto album_cover_rect = cover->rect(); - - auto height_ratio = frame_inner_rect().height() / (float)album_cover_rect.height(); - auto width_ratio = frame_inner_rect().width() / (float)album_cover_rect.width(); - auto scale = min(height_ratio, width_ratio); - - Gfx::IntRect fitted_rect = { 0, 0, (int)(album_cover_rect.width() * scale), (int)(album_cover_rect.height() * scale) }; - fitted_rect.center_within(frame_inner_rect()); - - painter.draw_scaled_bitmap(fitted_rect, *cover, cover->rect(), 1.0f); - } else { - if (!m_serenity_bg) - m_serenity_bg = Gfx::Bitmap::load_from_file("/res/wallpapers/sunset-retro.png"sv).release_value_but_fixme_should_propagate_errors(); - painter.draw_scaled_bitmap(frame_inner_rect(), *m_serenity_bg, m_serenity_bg->rect(), 1.0f); - } -} - -ErrorOr> AlbumCoverVisualizationWidget::get_album_cover(StringView const filename) -{ - auto directory = LexicalPath::dirname(filename); - - static constexpr auto possible_cover_filenames = Array { "cover.png"sv, "cover.jpg"sv }; - for (auto& it : possible_cover_filenames) { - LexicalPath cover_path = LexicalPath::join(directory, it); - if (FileSystem::exists(cover_path.string())) - return Gfx::Bitmap::load_from_file(cover_path.string()); - } - - return Error::from_string_literal("No cover file found"); -} - -void AlbumCoverVisualizationWidget::start_new_file(StringView filename) -{ - if (m_get_file_cover_from_player) - m_file_cover = m_get_file_cover_from_player(); - - if (m_file_cover) - return; - - auto album_cover_or_error = get_album_cover(filename); - if (album_cover_or_error.is_error()) - m_album_cover = nullptr; - else - m_album_cover = album_cover_or_error.value(); -} diff --git a/Userland/Applications/SoundPlayer/AlbumCoverVisualizationWidget.h b/Userland/Applications/SoundPlayer/AlbumCoverVisualizationWidget.h deleted file mode 100644 index fb30e1499fa..00000000000 --- a/Userland/Applications/SoundPlayer/AlbumCoverVisualizationWidget.h +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright (c) 2021, Cesar Torres - * Copyright (c) 2022, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include "VisualizationWidget.h" -#include - -class AlbumCoverVisualizationWidget final : public VisualizationWidget { - C_OBJECT(AlbumCoverVisualizationWidget) - -public: - AlbumCoverVisualizationWidget(Function()> get_file_cover_from_player); - ~AlbumCoverVisualizationWidget() override = default; - void start_new_file(StringView) override; - -private: - void render(GUI::PaintEvent&, FixedArray const&) override { } - void paint_event(GUI::PaintEvent&) override; - AlbumCoverVisualizationWidget() = default; - ErrorOr> get_album_cover(StringView const filename); - - Function()> m_get_file_cover_from_player; - - RefPtr m_serenity_bg; - RefPtr m_album_cover; - RefPtr m_file_cover; -}; diff --git a/Userland/Applications/SoundPlayer/BarsVisualizationWidget.cpp b/Userland/Applications/SoundPlayer/BarsVisualizationWidget.cpp deleted file mode 100644 index 8779cd48eb9..00000000000 --- a/Userland/Applications/SoundPlayer/BarsVisualizationWidget.cpp +++ /dev/null @@ -1,115 +0,0 @@ -/* - * Copyright (c) 2021, Cesar Torres - * Copyright (c) 2022, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "BarsVisualizationWidget.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include - -void BarsVisualizationWidget::render(GUI::PaintEvent& event, FixedArray const& samples) -{ - GUI::Frame::paint_event(event); - GUI::Painter painter(*this); - - painter.add_clip_rect(event.rect()); - painter.fill_rect(frame_inner_rect(), Color::Black); - - // First half of data is from previous iteration, second half is from now. - // This gives us fully overlapping windows, which result in more accurate and visually appealing STFT. - for (size_t i = 0; i < fft_size / 2; i++) - m_fft_samples[i] = m_previous_samples[i] * m_fft_window[i]; - for (size_t i = 0; i < fft_size / 2; i++) - m_fft_samples[i + fft_size / 2] = samples[i] * m_fft_window[i + fft_size / 2]; - - AK::TypedTransfer::copy(m_previous_samples.data(), samples.data(), samples.size()); - - DSP::fft(m_fft_samples.span(), false); - - Array groups {}; - - if (m_logarithmic_spectrum) { - auto const log_bar_size = static_cast(bar_count) / AK::log2(fft_size); - - for (size_t i = 0; i < bar_count; ++i) { - auto const bar_start = i == 0 ? 0 : static_cast(floor(AK::pow(2.f, static_cast(i) / log_bar_size))); - auto const bar_end = clamp(static_cast(floor(AK::pow(2.f, static_cast(i + 1) / log_bar_size))), bar_start + 1, cutoff); - auto const values_in_bar = bar_end - bar_start; - - for (size_t sample_index = bar_start; sample_index < bar_start + values_in_bar; sample_index++) { - float const magnitude = m_fft_samples[sample_index].magnitude(); - groups[i] += magnitude; - } - groups[i] /= static_cast(values_in_bar); - } - } else { - static constexpr size_t values_per_bar = (fft_size / 2) / bar_count; - for (size_t i = 0; i < fft_size / 2; i += values_per_bar) { - float const magnitude = m_fft_samples[i].magnitude(); - groups[i / values_per_bar] = magnitude; - for (size_t j = 0; j < values_per_bar; j++) { - float const magnitude = m_fft_samples[i + j].magnitude(); - groups[i / values_per_bar] += magnitude; - } - groups[i / values_per_bar] /= values_per_bar; - } - } - - float const max_peak_value = AK::sqrt(static_cast(fft_size * 2)); - for (size_t i = 0; i < bar_count; i++) { - groups[i] = AK::log(groups[i] + 1) / AK::log(max_peak_value); - if (m_adjust_frequencies) - groups[i] *= 1 + 2.0f * (static_cast(i) - bar_count / 3.0f) / static_cast(bar_count); - } - - int const horizontal_margin = 30; - int const top_vertical_margin = 15; - int const pixels_inbetween_groups = frame_inner_rect().width() > 350 ? 5 : 2; - int const pixel_per_group_width = (frame_inner_rect().width() - horizontal_margin * 2 - pixels_inbetween_groups * (bar_count - 1)) / bar_count; - int const max_height = AK::max(0, frame_inner_rect().height() - top_vertical_margin); - int current_xpos = horizontal_margin; - for (size_t g = 0; g < bar_count; g++) { - m_gfx_falling_bars[g] = AK::min(clamp(max_height - static_cast(groups[g] * static_cast(max_height) * 0.8f), 0, max_height), m_gfx_falling_bars[g]); - painter.fill_rect(Gfx::Rect(current_xpos, max_height - static_cast(groups[g] * static_cast(max_height) * 0.8f), pixel_per_group_width, static_cast(groups[g] * max_height * 0.8f)), Gfx::Color::from_rgb(0x95d437)); - painter.fill_rect(Gfx::Rect(current_xpos, m_gfx_falling_bars[g], pixel_per_group_width, 2), Gfx::Color::White); - current_xpos += pixel_per_group_width + pixels_inbetween_groups; - m_gfx_falling_bars[g] += 3; - } -} - -BarsVisualizationWidget::BarsVisualizationWidget() - : m_is_using_last(false) - , m_adjust_frequencies(true) - , m_logarithmic_spectrum(true) -{ - m_context_menu = GUI::Menu::construct(); - auto frequency_energy_action = GUI::Action::create_checkable("Adjust Frequency Energy", [&](GUI::Action& action) { - m_adjust_frequencies = action.is_checked(); - }); - frequency_energy_action->set_checked(true); - m_context_menu->add_action(frequency_energy_action); - auto logarithmic_spectrum_action = GUI::Action::create_checkable("Scale Spectrum Logarithmically", [&](GUI::Action& action) { - m_logarithmic_spectrum = action.is_checked(); - }); - logarithmic_spectrum_action->set_checked(true); - m_context_menu->add_action(logarithmic_spectrum_action); - - m_fft_window = DSP::Window::hann(); - - // As we use full-overlapping windows, the passed-in data is only half the size of one FFT operation. - MUST(set_render_sample_count(fft_size / 2)); -} - -void BarsVisualizationWidget::context_menu_event(GUI::ContextMenuEvent& event) -{ - m_context_menu->popup(event.screen_position()); -} diff --git a/Userland/Applications/SoundPlayer/BarsVisualizationWidget.h b/Userland/Applications/SoundPlayer/BarsVisualizationWidget.h deleted file mode 100644 index 363a51e4d64..00000000000 --- a/Userland/Applications/SoundPlayer/BarsVisualizationWidget.h +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright (c) 2021, Cesar Torres - * Copyright (c) 2022, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include "VisualizationWidget.h" -#include -#include -#include -#include - -class BarsVisualizationWidget final : public VisualizationWidget { - C_OBJECT(BarsVisualizationWidget) - -public: - ~BarsVisualizationWidget() override = default; - -private: - BarsVisualizationWidget(); - - void render(GUI::PaintEvent&, FixedArray const&) override; - void context_menu_event(GUI::ContextMenuEvent& event) override; - - static constexpr size_t fft_size = 512; - static constexpr size_t bar_count = 64; - // Things become weird near the Nyquist limit. Just don't use that FFT data. - static constexpr size_t cutoff = fft_size - 32; - - Array, fft_size> m_fft_samples {}; - Array m_fft_window {}; - Array m_previous_samples {}; - Array m_gfx_falling_bars {}; - bool m_is_using_last; - bool m_adjust_frequencies; - bool m_logarithmic_spectrum; - RefPtr m_context_menu; -}; diff --git a/Userland/Applications/SoundPlayer/CMakeLists.txt b/Userland/Applications/SoundPlayer/CMakeLists.txt deleted file mode 100644 index b2cf2edbd27..00000000000 --- a/Userland/Applications/SoundPlayer/CMakeLists.txt +++ /dev/null @@ -1,22 +0,0 @@ -serenity_component( - SoundPlayer - RECOMMENDED - TARGETS SoundPlayer - DEPENDS AudioServer -) - -set(SOURCES - AlbumCoverVisualizationWidget.cpp - BarsVisualizationWidget.cpp - M3UParser.cpp - PlaybackManager.cpp - Player.cpp - Playlist.cpp - PlaylistWidget.cpp - SampleWidget.cpp - SoundPlayerWidget.cpp - main.cpp -) - -serenity_app(SoundPlayer ICON app-sound-player) -target_link_libraries(SoundPlayer PRIVATE LibAudio LibConfig LibCore LibFileSystem LibDSP LibGfx LibGUI LibIPC LibMain LibThreading LibImageDecoderClient LibURL) diff --git a/Userland/Applications/SoundPlayer/M3UParser.cpp b/Userland/Applications/SoundPlayer/M3UParser.cpp deleted file mode 100644 index 33df658fa62..00000000000 --- a/Userland/Applications/SoundPlayer/M3UParser.cpp +++ /dev/null @@ -1,94 +0,0 @@ -/* - * Copyright (c) 2021, Cesar Torres - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "M3UParser.h" -#include -#include -#include -#include -#include - -M3UParser::M3UParser() -{ -} - -NonnullOwnPtr M3UParser::from_file(StringView path) -{ - auto file_result = Core::File::open(path, Core::File::OpenMode::Read).release_value_but_fixme_should_propagate_errors(); - auto contents = file_result->read_until_eof().release_value_but_fixme_should_propagate_errors(); - auto use_utf8 = path.ends_with(".m3u8"sv, CaseSensitivity::CaseInsensitive); - return from_memory(ByteString { contents, NoChomp }, use_utf8); -} - -NonnullOwnPtr M3UParser::from_memory(ByteString const& m3u_contents, bool utf8) -{ - auto parser = make(); - VERIFY(!m3u_contents.is_empty() && !m3u_contents.is_whitespace()); - parser->m_m3u_raw_data = m3u_contents; - parser->m_use_utf8 = utf8; - return parser; -} - -NonnullOwnPtr> M3UParser::parse(bool include_extended_info) -{ - auto vec = make>(); - - if (m_use_utf8) { - // TODO: Implement M3U8 parsing - TODO(); - return vec; - } - - auto lines = m_m3u_raw_data.split_view('\n'); - - bool has_extended_info_tag = include_extended_info && (lines[0] == "#EXTM3U"); - - M3UExtendedInfo metadata_for_next_file {}; - for (auto& line : lines) { - line = line.trim_whitespace(); - - if (!has_extended_info_tag || !line.starts_with('#')) { - vec->append({ line, metadata_for_next_file }); - metadata_for_next_file = {}; - continue; - } - - auto tag = [&line](StringView tag_name) -> Optional { - if (line.starts_with(tag_name)) { - auto value = line.substring_view(tag_name.length()); - VERIFY(!value.is_empty()); - return value; - } - return {}; - }; - - if (auto ext_inf = tag("#EXTINF:"sv); ext_inf.has_value()) { - auto separator = ext_inf.value().find(','); - VERIFY(separator.has_value()); - auto seconds = ext_inf.value().substring_view(0, separator.value()); - VERIFY(!seconds.is_whitespace() && !seconds.is_null() && !seconds.is_empty()); - metadata_for_next_file.track_length_in_seconds = seconds.to_number(); - auto display_name = ext_inf.value().substring_view(seconds.length() + 1); - VERIFY(!display_name.is_empty() && !display_name.is_null() && !display_name.is_empty()); - metadata_for_next_file.track_display_title = display_name; - // TODO: support the alternative, non-standard #EXTINF value of a key=value dictionary - continue; - } - if (auto playlist = tag("#PLAYLIST:"sv); playlist.has_value()) - m_parsed_playlist_title = move(playlist.value()); - else if (auto ext_grp = tag("#EXTGRP:"sv); ext_grp.has_value()) - metadata_for_next_file.group_name = move(ext_grp.value()); - else if (auto ext_alb = tag("#EXTALB:"sv); ext_alb.has_value()) - metadata_for_next_file.album_title = move(ext_alb.value()); - else if (auto ext_art = tag("#EXTART:"sv); ext_art.has_value()) - metadata_for_next_file.album_artist = move(ext_art.value()); - else if (auto ext_genre = tag("#EXTGENRE:"sv); ext_genre.has_value()) - metadata_for_next_file.album_genre = move(ext_genre.value()); - // TODO: Support M3A files (M3U files with embedded mp3 files) - } - - return vec; -} diff --git a/Userland/Applications/SoundPlayer/M3UParser.h b/Userland/Applications/SoundPlayer/M3UParser.h deleted file mode 100644 index 4bfc8381121..00000000000 --- a/Userland/Applications/SoundPlayer/M3UParser.h +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright (c) 2021, Cesar Torres - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include - -// Extended M3U fields (de facto standard) -struct M3UExtendedInfo { - Optional track_length_in_seconds; - Optional track_display_title; - Optional group_name; - Optional album_title; - Optional album_artist; - Optional album_genre; - Optional file_size_in_bytes; - Optional embedded_mp3; - Optional cover_path; -}; - -struct M3UEntry { - ByteString path; - Optional extended_info; -}; - -class M3UParser { -public: - static NonnullOwnPtr from_file(StringView path); - static NonnullOwnPtr from_memory(ByteString const& m3u_contents, bool utf8); - - NonnullOwnPtr> parse(bool include_extended_info); - - Optional& get_playlist_title_metadata() { return m_parsed_playlist_title; } - - M3UParser(); - -private: - ByteString m_m3u_raw_data; - ByteString m_playlist_path; - bool m_use_utf8; - Optional m_parsed_playlist_title; -}; diff --git a/Userland/Applications/SoundPlayer/PlaybackManager.cpp b/Userland/Applications/SoundPlayer/PlaybackManager.cpp deleted file mode 100644 index df441b27617..00000000000 --- a/Userland/Applications/SoundPlayer/PlaybackManager.cpp +++ /dev/null @@ -1,124 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * Copyright (c) 2022, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "PlaybackManager.h" - -PlaybackManager::PlaybackManager(NonnullRefPtr connection) - : m_connection(connection) -{ - // FIXME: The buffer enqueuing should happen on a wholly independent second thread. - m_timer = Core::Timer::create_repeating(PlaybackManager::update_rate_ms, [&]() { - if (!m_loader) - return; - next_buffer(); - }); -} - -void PlaybackManager::set_loader(NonnullRefPtr&& loader) -{ - stop(); - m_loader = loader; - if (m_loader) { - m_connection->set_self_sample_rate(loader->sample_rate()); - m_total_length = m_loader->total_samples() / static_cast(m_loader->sample_rate()); - m_samples_to_load_per_buffer = PlaybackManager::buffer_size_ms / 1000.0f * m_loader->sample_rate(); - m_timer->start(); - } else { - m_timer->stop(); - } -} - -void PlaybackManager::stop() -{ - set_paused(true); - m_connection->clear_client_buffer(); - m_connection->async_clear_buffer(); - - if (m_loader) - (void)m_loader->reset(); -} - -void PlaybackManager::play() -{ - set_paused(false); -} - -void PlaybackManager::loop(bool loop) -{ - m_loop = loop; -} - -void PlaybackManager::seek(int const position) -{ - if (!m_loader) - return; - - bool paused_state = m_paused; - set_paused(true); - - [[maybe_unused]] auto result = m_loader->seek(position); - - m_connection->clear_client_buffer(); - m_connection->async_clear_buffer(); - if (!paused_state) - set_paused(false); -} - -void PlaybackManager::pause() -{ - set_paused(true); -} - -void PlaybackManager::set_paused(bool paused) -{ - m_paused = paused; - if (m_paused) - m_connection->async_pause_playback(); - else - m_connection->async_start_playback(); -} - -bool PlaybackManager::toggle_pause() -{ - if (m_paused) { - play(); - } else { - pause(); - } - return m_paused; -} - -void PlaybackManager::next_buffer() -{ - if (on_update) - on_update(); - - if (m_paused) - return; - - while (m_connection->remaining_samples() < m_samples_to_load_per_buffer * always_enqueued_buffer_count) { - bool all_samples_loaded = (m_loader->loaded_samples() >= m_loader->total_samples()); - bool audio_server_done = (m_connection->remaining_samples() == 0); - - if (all_samples_loaded && audio_server_done) { - stop(); - if (on_finished_playing) - on_finished_playing(); - return; - } - - auto buffer_or_error = m_loader->get_more_samples(m_samples_to_load_per_buffer); - if (buffer_or_error.is_error()) { - // FIXME: These errors should be shown to the user instead of being logged and then ignored - dbgln("Error while loading samples: {}", buffer_or_error.error().description); - return; - } - auto buffer = buffer_or_error.release_value(); - m_current_buffer.swap(buffer); - MUST(m_connection->async_enqueue(m_current_buffer)); - } -} diff --git a/Userland/Applications/SoundPlayer/PlaybackManager.h b/Userland/Applications/SoundPlayer/PlaybackManager.h deleted file mode 100644 index 39da487c232..00000000000 --- a/Userland/Applications/SoundPlayer/PlaybackManager.h +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * Copyright (c) 2022, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include -#include -#include -#include - -class PlaybackManager final { -public: - PlaybackManager(NonnullRefPtr); - ~PlaybackManager() = default; - - void play(); - void stop(); - void pause(); - void seek(int const position); - void loop(bool); - bool toggle_pause(); - void set_loader(NonnullRefPtr&&); - RefPtr loader() const { return m_loader; } - - bool is_paused() const { return m_paused; } - float total_length() const { return m_total_length; } - FixedArray const& current_buffer() const { return m_current_buffer; } - - NonnullRefPtr connection() const { return m_connection; } - - Function on_update; - Function on_finished_playing; - -private: - // Number of buffers we want to always keep enqueued. - static constexpr size_t always_enqueued_buffer_count = 5; - - void next_buffer(); - void set_paused(bool); - - bool m_paused { true }; - bool m_loop = { false }; - float m_total_length { 0 }; - size_t m_samples_to_load_per_buffer { 0 }; - RefPtr m_loader { nullptr }; - NonnullRefPtr m_connection; - FixedArray m_current_buffer; - RefPtr m_timer; - - // Controls the GUI update rate. A smaller value makes the visualizations nicer. - static constexpr u32 update_rate_ms = 50; - // Number of milliseconds of audio data contained in each audio buffer - static constexpr u32 buffer_size_ms = 100; -}; diff --git a/Userland/Applications/SoundPlayer/Player.cpp b/Userland/Applications/SoundPlayer/Player.cpp deleted file mode 100644 index 1017de350e6..00000000000 --- a/Userland/Applications/SoundPlayer/Player.cpp +++ /dev/null @@ -1,159 +0,0 @@ -/* - * Copyright (c) 2021, Cesar Torres - * Copyright (c) 2021, Leandro A. F. Pereira - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "Player.h" -#include -#include - -Player::Player(Audio::ConnectionToServer& audio_client_connection) - : m_audio_client_connection(audio_client_connection) - , m_playback_manager(audio_client_connection) -{ - m_playback_manager.on_update = [&]() { - auto samples_played = m_playback_manager.loader()->loaded_samples(); - auto sample_rate = m_playback_manager.loader()->sample_rate(); - - auto played_seconds = samples_played / sample_rate; - time_elapsed(played_seconds); - if (play_state() == PlayState::Playing) - sound_buffer_played(m_playback_manager.current_buffer(), sample_rate, samples_played); - }; - m_playback_manager.on_finished_playing = [&]() { - set_play_state(PlayState::Stopped); - - switch (loop_mode()) { - case LoopMode::File: - play_file_path(loaded_filename()); - return; - case LoopMode::Playlist: - play_file_path(m_playlist.next()); - return; - case LoopMode::None: - return; - } - }; -} - -void Player::play_file_path(ByteString const& path) -{ - if (!FileSystem::exists(path)) { - audio_load_error(path, "File does not exist"sv); - return; - } - - if (is_playlist(path)) { - playlist_loaded(path, m_playlist.load(path)); - return; - } - - auto maybe_loader = Audio::Loader::create(path); - if (maybe_loader.is_error()) { - audio_load_error(path, maybe_loader.error().description); - return; - } - auto loader = maybe_loader.value(); - - m_loaded_filename = path; - - total_samples_changed(loader->total_samples()); - m_playback_manager.set_loader(move(loader)); - file_name_changed(path); - - play(); -} - -bool Player::is_playlist(ByteString const& path) -{ - return (path.ends_with(".m3u"sv, AK::CaseSensitivity::CaseInsensitive) - || path.ends_with(".m3u8"sv, AK::CaseSensitivity::CaseInsensitive)); -} - -void Player::set_play_state(PlayState state) -{ - if (m_play_state != state) { - m_play_state = state; - play_state_changed(state); - } -} - -void Player::set_loop_mode(LoopMode mode) -{ - if (m_loop_mode != mode) { - m_loop_mode = mode; - m_playlist.set_looping(mode == LoopMode::Playlist); - loop_mode_changed(mode); - } -} - -void Player::set_volume(double volume) -{ - m_volume = clamp(volume, 0, 1.5); - m_audio_client_connection.set_self_volume(m_volume); - volume_changed(m_volume); -} - -void Player::set_mute(bool muted) -{ - if (m_muted != muted) { - m_muted = muted; - m_audio_client_connection.set_self_muted(muted); - mute_changed(muted); - } -} - -void Player::set_shuffle_mode(ShuffleMode mode) -{ - if (m_shuffle_mode != mode) { - m_shuffle_mode = mode; - m_playlist.set_shuffling(mode == ShuffleMode::Shuffling); - shuffle_mode_changed(mode); - } -} - -void Player::play() -{ - m_playback_manager.play(); - set_play_state(PlayState::Playing); -} - -void Player::pause() -{ - m_playback_manager.pause(); - set_play_state(PlayState::Paused); -} - -void Player::toggle_pause() -{ - bool paused = m_playback_manager.toggle_pause(); - set_play_state(paused ? PlayState::Paused : PlayState::Playing); -} - -void Player::stop() -{ - m_playback_manager.stop(); - set_play_state(PlayState::Stopped); -} - -void Player::mute() -{ - set_mute(true); -} - -void Player::toggle_mute() -{ - set_mute(!m_muted); -} - -void Player::seek(int sample) -{ - m_playback_manager.seek(sample); -} - -Vector const& Player::pictures() const -{ - return m_playback_manager.loader()->pictures(); -} diff --git a/Userland/Applications/SoundPlayer/Player.h b/Userland/Applications/SoundPlayer/Player.h deleted file mode 100644 index f3cab748edf..00000000000 --- a/Userland/Applications/SoundPlayer/Player.h +++ /dev/null @@ -1,104 +0,0 @@ -/* - * Copyright (c) 2021, Cesar Torres - * Copyright (c) 2022, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include "PlaybackManager.h" -#include "Playlist.h" -#include "PlaylistWidget.h" -#include -#include -#include - -class Player { -public: - enum class PlayState { - NoFileLoaded, - Paused, - Stopped, - Playing, - }; - enum class LoopMode { - None, - File, - Playlist, - }; - enum class ShuffleMode { - None, - Shuffling, - }; - - explicit Player(Audio::ConnectionToServer& audio_client_connection); - virtual ~Player() = default; - - void play_file_path(ByteString const& path); - bool is_playlist(ByteString const& path); - - Playlist& playlist() { return m_playlist; } - PlaybackManager const& playback_manager() const { return m_playback_manager; } - ByteString const& loaded_filename() const { return m_loaded_filename; } - - PlayState play_state() const { return m_play_state; } - void set_play_state(PlayState); - - LoopMode loop_mode() const { return m_loop_mode; } - void set_loop_mode(LoopMode); - - ShuffleMode shuffle_mode() const { return m_shuffle_mode; } - void set_shuffle_mode(ShuffleMode); - - double volume() const { return m_volume; } - void set_volume(double value); - - bool is_muted() const { return m_muted; } - void set_mute(bool); - - void play(); - void pause(); - void toggle_pause(); - void stop(); - void seek(int sample); - void mute(); - void toggle_mute(); - - virtual void play_state_changed(PlayState) = 0; - virtual void loop_mode_changed(LoopMode) = 0; - virtual void time_elapsed(int) = 0; - virtual void file_name_changed(StringView) = 0; - virtual void playlist_loaded(StringView, bool) = 0; - virtual void audio_load_error(StringView, StringView) = 0; - virtual void shuffle_mode_changed(ShuffleMode) = 0; - virtual void volume_changed(double) = 0; - virtual void mute_changed(bool) = 0; - virtual void total_samples_changed(int) = 0; - virtual void sound_buffer_played(FixedArray const&, [[maybe_unused]] int sample_rate, [[maybe_unused]] int samples_played) = 0; - - Vector const& pictures() const; - -protected: - void done_initializing() - { - set_play_state(PlayState::NoFileLoaded); - set_loop_mode(LoopMode::None); - time_elapsed(0); - set_volume(1.); - set_mute(false); - } - -private: - Playlist m_playlist; - PlayState m_play_state { PlayState::NoFileLoaded }; - LoopMode m_loop_mode { LoopMode::None }; - ShuffleMode m_shuffle_mode { ShuffleMode::None }; - - Audio::ConnectionToServer& m_audio_client_connection; - PlaybackManager m_playback_manager; - - ByteString m_loaded_filename; - double m_volume { 0 }; - bool m_muted { false }; -}; diff --git a/Userland/Applications/SoundPlayer/Playlist.cpp b/Userland/Applications/SoundPlayer/Playlist.cpp deleted file mode 100644 index d245ddc8ba2..00000000000 --- a/Userland/Applications/SoundPlayer/Playlist.cpp +++ /dev/null @@ -1,111 +0,0 @@ -/* - * Copyright (c) 2021, Cesar Torres - * Copyright (c) 2021, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "Playlist.h" - -#include -#include -#include -#include -#include - -bool Playlist::load(StringView path) -{ - auto parser = M3UParser::from_file(path); - auto items = parser->parse(true); - - if (items->size() <= 0) - return false; - - try_fill_missing_info(*items, path); - for (auto& item : *items) - m_model->items().append(item); - m_model->invalidate(); - - return true; -} - -void Playlist::try_fill_missing_info(Vector& entries, StringView path) -{ - LexicalPath playlist_path(path); - Vector to_delete; - - for (auto& entry : entries) { - if (!LexicalPath { entry.path }.is_absolute()) - entry.path = ByteString::formatted("{}/{}", playlist_path.dirname(), entry.path); - - if (!entry.extended_info->file_size_in_bytes.has_value()) { - auto size = FileSystem::size_from_stat(entry.path); - if (size.is_error()) - continue; - entry.extended_info->file_size_in_bytes = size.value(); - } else if (!FileSystem::exists(entry.path)) { - to_delete.append(&entry); - continue; - } - - if (!entry.extended_info->track_display_title.has_value()) - entry.extended_info->track_display_title = LexicalPath::title(entry.path); - - if (!entry.extended_info->track_length_in_seconds.has_value()) { - // TODO: Implement embedded metadata extractor for other audio formats - if (auto reader = Audio::Loader::create(entry.path); !reader.is_error()) - entry.extended_info->track_length_in_seconds = reader.value()->total_samples() / reader.value()->sample_rate(); - } - - // TODO: Implement a metadata parser for the uncomfortably numerous popular embedded metadata formats - } - - for (auto& entry : to_delete) - entries.remove_first_matching([&](M3UEntry& e) { return &e == entry; }); -} - -StringView Playlist::next() -{ - if (m_next_index_to_play >= size()) { - if (!looping()) - return {}; - m_next_index_to_play = 0; - } - - auto next = m_model->items().at(m_next_index_to_play).path; - if (!shuffling()) { - m_next_index_to_play++; - return next; - } - - // Try a few times getting an item to play that has not been - // recently played. But do not try too hard, as we don't want - // to wait forever. - int shuffle_try; - int const max_times_to_try = min(4, size()); - for (shuffle_try = 0; shuffle_try < max_times_to_try; shuffle_try++) { - if (!m_previously_played_paths.maybe_contains(next)) - break; - - m_next_index_to_play = get_random_uniform(size()); - next = m_model->items().at(m_next_index_to_play).path; - } - if (shuffle_try == max_times_to_try) { - // If we tried too much, maybe it's time to try resetting - // the bloom filter and start over. - m_previously_played_paths.reset(); - } - - m_previously_played_paths.add(next); - return next; -} - -StringView Playlist::previous() -{ - m_next_index_to_play--; - if (m_next_index_to_play < 0) { - m_next_index_to_play = 0; - return {}; - } - return m_model->items().at(m_next_index_to_play).path; -} diff --git a/Userland/Applications/SoundPlayer/Playlist.h b/Userland/Applications/SoundPlayer/Playlist.h deleted file mode 100644 index c0c0344a763..00000000000 --- a/Userland/Applications/SoundPlayer/Playlist.h +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright (c) 2021, the SerenityOS developers. - * Copyright (c) 2021, Leandro A. F. Pereira - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include "M3UParser.h" -#include "PlaylistWidget.h" -#include -#include -#include - -class Playlist { -public: - Playlist() - : m_model(adopt_ref(*new PlaylistModel())) - { - } - - bool load(StringView); - - RefPtr model() { return m_model; } - int size() { return m_model->items().size(); } - - StringView next(); - StringView previous(); - - void set_looping(bool looping) { m_looping = looping; } - bool looping() const { return m_looping; } - - void set_shuffling(bool shuffling) { m_shuffling = shuffling; } - bool shuffling() const { return m_shuffling; } - -private: - // This naïve bloom filter is used in the shuffling algorithm to - // provide random uniformity, avoiding playing items that were recently - // played. - class BloomFilter { - public: - void reset() { m_bitmap = 0; } - void add(StringView const key) { m_bitmap |= mask_for_key(key); } - bool maybe_contains(StringView const key) const - { - auto mask = mask_for_key(key); - return (m_bitmap & mask) == mask; - } - - private: - u64 mask_for_key(StringView key) const - { - auto key_chars = key.characters_without_null_termination(); - auto hash1 = string_hash(key_chars, key.length(), 0xdeadbeef); - auto hash2 = string_hash(key_chars, key.length(), 0xbebacafe); - return 1ULL << (hash1 & 63) | 1ULL << (hash2 & 63); - } - u64 m_bitmap { 0 }; - }; - - void try_fill_missing_info(Vector&, StringView); - - RefPtr m_model; - bool m_looping { false }; - bool m_shuffling { false }; - - BloomFilter m_previously_played_paths; - int m_next_index_to_play { 0 }; -}; diff --git a/Userland/Applications/SoundPlayer/PlaylistWidget.cpp b/Userland/Applications/SoundPlayer/PlaylistWidget.cpp deleted file mode 100644 index 8b5fc1157bf..00000000000 --- a/Userland/Applications/SoundPlayer/PlaylistWidget.cpp +++ /dev/null @@ -1,88 +0,0 @@ -/* - * Copyright (c) 2021, Cesar Torres - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "PlaylistWidget.h" -#include "Player.h" -#include -#include -#include -#include -#include -#include - -PlaylistWidget::PlaylistWidget() -{ - set_layout(); - m_table_view = add(); - m_table_view->set_selection_mode(GUI::AbstractView::SelectionMode::SingleSelection); - m_table_view->set_selection_behavior(GUI::AbstractView::SelectionBehavior::SelectRows); - m_table_view->set_highlight_selected_rows(true); - m_table_view->on_doubleclick = [&](Gfx::Point const& point) { - auto player = dynamic_cast(window()->main_widget()); - auto index = m_table_view->index_at_event_position(point); - if (!index.is_valid()) - return; - player->play_file_path(m_table_view->model()->data(index, static_cast(PlaylistModelCustomRole::FilePath)).as_string()); - }; -} - -GUI::Variant PlaylistModel::data(GUI::ModelIndex const& index, GUI::ModelRole role) const -{ - if (role == GUI::ModelRole::TextAlignment) - return "CenterLeft"; - if (role == GUI::ModelRole::Display) { - switch (index.column()) { - case Column::Title: - return m_playlist_items[index.row()].extended_info->track_display_title.value_or(LexicalPath::title(m_playlist_items[index.row()].path)); - case Column::Duration: - return human_readable_digital_time(m_playlist_items[index.row()].extended_info->track_length_in_seconds.value_or(0)); - case Column::Group: - return m_playlist_items[index.row()].extended_info->group_name.value_or(""); - case Column::Album: - return m_playlist_items[index.row()].extended_info->album_title.value_or(""); - case Column::Artist: - return m_playlist_items[index.row()].extended_info->album_artist.value_or(""); - case Column::Filesize: - return human_readable_size(m_playlist_items[index.row()].extended_info->file_size_in_bytes.value_or(0)); - } - } - if (role == GUI::ModelRole::Sort) - return data(index, GUI::ModelRole::Display); - if (role == static_cast(PlaylistModelCustomRole::FilePath)) // path - return m_playlist_items[index.row()].path; - - return {}; -} - -ErrorOr PlaylistModel::column_name(int column) const -{ - switch (column) { - case Column::Title: - return "Title"_string; - case Column::Duration: - return "Duration"_string; - case Column::Group: - return "Group"_string; - case Column::Album: - return "Album"_string; - case Column::Artist: - return "Artist"_string; - case Column::Filesize: - return "Filesize"_string; - } - VERIFY_NOT_REACHED(); -} - -PlaylistTableView::PlaylistTableView() = default; - -void PlaylistTableView::doubleclick_event(GUI::MouseEvent& event) -{ - AbstractView::doubleclick_event(event); - if (event.button() == GUI::Primary) { - if (on_doubleclick) - on_doubleclick(event.position()); - } -} diff --git a/Userland/Applications/SoundPlayer/PlaylistWidget.h b/Userland/Applications/SoundPlayer/PlaylistWidget.h deleted file mode 100644 index d10d922f99c..00000000000 --- a/Userland/Applications/SoundPlayer/PlaylistWidget.h +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright (c) 2021, Cesar Torres - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include "M3UParser.h" -#include -#include -#include -#include - -enum class PlaylistModelCustomRole { - _DONOTUSE = (int)GUI::ModelRole::Custom, - FilePath -}; - -class PlaylistModel : public GUI::Model { -public: - enum Column { - Title, - Duration, - Group, - Album, - Artist, - Filesize, - __Count - }; - - ~PlaylistModel() override = default; - - int row_count(GUI::ModelIndex const&) const override { return m_playlist_items.size(); } - int column_count(GUI::ModelIndex const&) const override { return 6; } - GUI::Variant data(GUI::ModelIndex const&, GUI::ModelRole) const override; - ErrorOr column_name(int column) const override; - Vector& items() { return m_playlist_items; } - -private: - Vector m_playlist_items; -}; - -class PlaylistTableView : public GUI::TableView { - C_OBJECT(PlaylistTableView) -public: - void doubleclick_event(GUI::MouseEvent& event) override; - - Function const&)> on_doubleclick; - -private: - PlaylistTableView(); -}; - -class PlaylistWidget : public GUI::Widget { - C_OBJECT(PlaylistWidget) -public: - void set_data_model(RefPtr model) - { - m_table_view->set_model(model); - m_table_view->update(); - } - -private: - PlaylistWidget(); - - RefPtr m_table_view; -}; diff --git a/Userland/Applications/SoundPlayer/SampleWidget.cpp b/Userland/Applications/SoundPlayer/SampleWidget.cpp deleted file mode 100644 index 6b8b8dad90c..00000000000 --- a/Userland/Applications/SoundPlayer/SampleWidget.cpp +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * Copyright (c) 2022, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "SampleWidget.h" -#include - -SampleWidget::SampleWidget() -{ - MUST(set_render_sample_count(512)); -} - -void SampleWidget::render(GUI::PaintEvent& event, FixedArray const& samples) -{ - GUI::Frame::paint_event(event); - GUI::Painter painter(*this); - - painter.add_clip_rect(event.rect()); - painter.fill_rect(frame_inner_rect(), Color::Black); - - int x_offset = frame_inner_rect().x(); - int x = x_offset; - int y_offset = frame_inner_rect().center().y(); - - if (samples.size() > 0) { - float samples_per_pixel = samples.size() / static_cast(frame_inner_rect().width()); - float sample_index = 0; - - while (sample_index < samples.size()) { - float sample = AK::abs(samples[sample_index]); - for (size_t i = 1; i < static_cast(samples_per_pixel + 0.5f); i++) - sample = max(sample, AK::abs(samples[sample_index])); - - Gfx::IntPoint min_point = { x, y_offset + static_cast(-sample * frame_inner_rect().height() / 2) }; - Gfx::IntPoint max_point = { x, y_offset + static_cast(sample * frame_inner_rect().height() / 2) }; - painter.draw_line(min_point, max_point, Color::Green); - sample_index += samples_per_pixel; - x++; - } - } else { - painter.draw_line({ x, y_offset }, { frame_inner_rect().width(), y_offset }, Color::Green); - } -} diff --git a/Userland/Applications/SoundPlayer/SampleWidget.h b/Userland/Applications/SoundPlayer/SampleWidget.h deleted file mode 100644 index 1fe4675983f..00000000000 --- a/Userland/Applications/SoundPlayer/SampleWidget.h +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * Copyright (c) 2022, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include "VisualizationWidget.h" -#include - -class SampleWidget final : public VisualizationWidget { - C_OBJECT(SampleWidget) -public: - virtual ~SampleWidget() override = default; - -private: - SampleWidget(); - virtual void render(GUI::PaintEvent&, FixedArray const& samples) override; -}; diff --git a/Userland/Applications/SoundPlayer/SoundPlayerWidget.cpp b/Userland/Applications/SoundPlayer/SoundPlayerWidget.cpp deleted file mode 100644 index 598322c4c5b..00000000000 --- a/Userland/Applications/SoundPlayer/SoundPlayerWidget.cpp +++ /dev/null @@ -1,301 +0,0 @@ -/* - * Copyright (c) 2021, Cesar Torres - * Copyright (c) 2021, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "SoundPlayerWidget.h" -#include "AlbumCoverVisualizationWidget.h" -#include "BarsVisualizationWidget.h" -#include "M3UParser.h" -#include "PlaybackManager.h" -#include "SampleWidget.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -SoundPlayerWidget::SoundPlayerWidget(GUI::Window& window, Audio::ConnectionToServer& connection, ImageDecoderClient::Client& image_decoder_client) - : Player(connection) - , m_window(window) - , m_image_decoder_client(image_decoder_client) -{ - window.resize(455, 350); - window.set_resizable(true); - set_fill_with_background_color(true); - - set_layout(); - m_splitter = add(); - m_player_view = m_splitter->add(); - - m_playlist_widget = PlaylistWidget::construct(); - m_playlist_widget->set_data_model(playlist().model()); - m_playlist_widget->set_preferred_width(150); - - m_player_view->set_layout(); - - m_play_icon = Gfx::Bitmap::load_from_file("/res/icons/16x16/play.png"sv).release_value_but_fixme_should_propagate_errors(); - m_pause_icon = Gfx::Bitmap::load_from_file("/res/icons/16x16/pause.png"sv).release_value_but_fixme_should_propagate_errors(); - m_stop_icon = Gfx::Bitmap::load_from_file("/res/icons/16x16/stop.png"sv).release_value_but_fixme_should_propagate_errors(); - m_back_icon = Gfx::Bitmap::load_from_file("/res/icons/16x16/go-back.png"sv).release_value_but_fixme_should_propagate_errors(); - m_next_icon = Gfx::Bitmap::load_from_file("/res/icons/16x16/go-forward.png"sv).release_value_but_fixme_should_propagate_errors(); - m_volume_icon = Gfx::Bitmap::load_from_file("/res/icons/16x16/audio-volume-medium.png"sv).release_value_but_fixme_should_propagate_errors(); - m_muted_icon = Gfx::Bitmap::load_from_file("/res/icons/16x16/audio-volume-muted.png"sv).release_value_but_fixme_should_propagate_errors(); - - auto visualization = Config::read_string("SoundPlayer"sv, "Preferences"sv, "Visualization"sv, "bars"sv); - if (visualization == "samples") { - m_visualization = m_player_view->add(); - } else if (visualization == "album_cover") { - m_visualization = m_player_view->add([this]() { - return get_image_from_music_file(); - }); - } else { - m_visualization = m_player_view->add(); - } - - m_playback_progress_slider = m_player_view->add(); - m_playback_progress_slider->set_fixed_height(20); - m_playback_progress_slider->set_jump_to_cursor(true); - m_playback_progress_slider->set_min(0); - m_playback_progress_slider->on_change = [&](int value) { - if (!m_playback_progress_slider->knob_dragging()) - seek(value); - }; - m_playback_progress_slider->on_drag_end = [&]() { - seek(m_playback_progress_slider->value()); - }; - - auto& toolbar_container = m_player_view->add(); - auto& menubar = toolbar_container.add(); - - m_play_action = GUI::Action::create("Play", { Key_Space }, m_play_icon, [&](auto&) { - toggle_pause(); - }); - m_play_action->set_enabled(false); - menubar.add_action(*m_play_action); - - m_stop_action = GUI::Action::create("Stop", { Key_S }, m_stop_icon, [&](auto&) { - stop(); - }); - m_stop_action->set_enabled(false); - menubar.add_action(*m_stop_action); - - menubar.add_separator(); - - m_timestamp_label = menubar.add(); - m_timestamp_label->set_fixed_width(110); - - // Filler label - menubar.add(); - - m_back_action = GUI::Action::create("Back", m_back_icon, [&](auto&) { - play_file_path(playlist().previous()); - }); - m_back_action->set_enabled(false); - menubar.add_action(*m_back_action); - - m_next_action = GUI::Action::create("Next", m_next_icon, [&](auto&) { - play_file_path(playlist().next()); - }); - m_next_action->set_enabled(false); - menubar.add_action(*m_next_action); - - menubar.add_separator(); - - m_mute_action = GUI::Action::create("Mute", { Key_M }, m_volume_icon, [&](auto&) { - toggle_mute(); - }); - m_mute_action->set_enabled(true); - menubar.add_action(*m_mute_action); - - m_volume_label = &menubar.add(); - m_volume_label->set_fixed_width(30); - - m_volume_slider = &menubar.add(); - m_volume_slider->set_fixed_width(95); - m_volume_slider->set_min(0); - m_volume_slider->set_max(150); - m_volume_slider->set_value(100); - - m_volume_slider->on_change = [&](int value) { - double volume = m_nonlinear_volume_slider ? (double)(value * value) / (100 * 100) : value / 100.; - set_volume(volume); - }; - - set_nonlinear_volume_slider(false); - - done_initializing(); -} - -void SoundPlayerWidget::set_nonlinear_volume_slider(bool nonlinear) -{ - m_nonlinear_volume_slider = nonlinear; -} - -void SoundPlayerWidget::drag_enter_event(GUI::DragEvent& event) -{ - auto const& mime_types = event.mime_types(); - if (mime_types.contains_slow("text/uri-list"sv)) - event.accept(); -} - -void SoundPlayerWidget::drop_event(GUI::DropEvent& event) -{ - event.accept(); - - if (event.mime_data().has_urls()) { - auto urls = event.mime_data().urls(); - if (urls.is_empty()) - return; - window()->move_to_front(); - // FIXME: Add all paths from drop event to the playlist - play_file_path(urls.first().serialize_path()); - } -} - -void SoundPlayerWidget::keydown_event(GUI::KeyEvent& event) -{ - if (event.key() == Key_Up) - m_volume_slider->increase_slider_by_page_steps(1); - - if (event.key() == Key_Down) - m_volume_slider->decrease_slider_by_page_steps(1); - - GUI::Widget::keydown_event(event); -} - -void SoundPlayerWidget::set_playlist_visible(bool visible) -{ - if (!visible) { - m_playlist_widget->remove_from_parent(); - m_player_view->set_max_width(window()->width()); - } else if (!m_playlist_widget->parent()) { - m_player_view->parent_widget()->add_child(*m_playlist_widget); - } -} - -RefPtr SoundPlayerWidget::get_image_from_music_file() -{ - auto const& pictures = this->pictures(); - if (pictures.is_empty()) - return {}; - - // FIXME: We randomly select the first picture available for the track, - // We might want to hardcode or let the user set a preference. - // FIXME: Refactor image decoding to be more async-aware, and don't await this promise - auto decoded_image_or_error = m_image_decoder_client.decode_image(pictures[0].data, {}, {})->await(); - if (decoded_image_or_error.is_error()) - return {}; - - auto const decoded_image = decoded_image_or_error.release_value(); - return decoded_image.frames[0].bitmap; -} - -void SoundPlayerWidget::play_state_changed(Player::PlayState state) -{ - sync_previous_next_actions(); - - m_play_action->set_enabled(state != PlayState::NoFileLoaded); - m_play_action->set_icon(state == PlayState::Playing ? m_pause_icon : m_play_icon); - m_play_action->set_text(state == PlayState::Playing ? "Pause"sv : "Play"sv); - - m_stop_action->set_enabled(state != PlayState::Stopped && state != PlayState::NoFileLoaded); - - m_playback_progress_slider->set_enabled(state != PlayState::NoFileLoaded); - if (state == PlayState::Stopped) { - m_playback_progress_slider->set_value(m_playback_progress_slider->min(), GUI::AllowCallback::No); - m_visualization->reset_buffer(); - } -} - -void SoundPlayerWidget::loop_mode_changed(Player::LoopMode) -{ -} - -void SoundPlayerWidget::mute_changed(bool muted) -{ - m_mute_action->set_text(muted ? "Unmute"sv : "Mute"sv); - m_mute_action->set_icon(muted ? m_muted_icon : m_volume_icon); - m_volume_slider->set_enabled(!muted); -} - -void SoundPlayerWidget::sync_previous_next_actions() -{ - m_back_action->set_enabled(playlist().size() > 1 && !playlist().shuffling()); - m_next_action->set_enabled(playlist().size() > 1); -} - -void SoundPlayerWidget::shuffle_mode_changed(Player::ShuffleMode) -{ - sync_previous_next_actions(); -} - -void SoundPlayerWidget::time_elapsed(int seconds) -{ - m_timestamp_label->set_text(String::formatted("Elapsed: {}", human_readable_digital_time(seconds)).release_value_but_fixme_should_propagate_errors()); -} - -void SoundPlayerWidget::file_name_changed(StringView name) -{ - m_visualization->start_new_file(name); - ByteString title = name; - if (playback_manager().loader()) { - auto const& metadata = playback_manager().loader()->metadata(); - if (auto artists_or_error = metadata.all_artists(" / "_string); - !artists_or_error.is_error() && artists_or_error.value().has_value() && metadata.title.has_value()) { - title = ByteString::formatted("{} – {}", metadata.title.value(), artists_or_error.release_value().release_value()); - } else if (metadata.title.has_value()) { - title = metadata.title.value().to_byte_string(); - } - } - m_window.set_title(ByteString::formatted("{} — Sound Player", title)); -} - -void SoundPlayerWidget::total_samples_changed(int total_samples) -{ - m_playback_progress_slider->set_max(total_samples); - m_playback_progress_slider->set_page_step(total_samples / 10); -} - -void SoundPlayerWidget::sound_buffer_played(FixedArray const& buffer, int sample_rate, int samples_played) -{ - m_visualization->set_buffer(buffer); - m_visualization->set_samplerate(sample_rate); - // If the user is currently dragging the slider, don't interfere. - if (!m_playback_progress_slider->knob_dragging()) - m_playback_progress_slider->set_value(samples_played, GUI::AllowCallback::No); -} - -void SoundPlayerWidget::volume_changed(double volume) -{ - m_volume_label->set_text(String::formatted("{}%", static_cast(volume * 100)).release_value_but_fixme_should_propagate_errors()); -} - -void SoundPlayerWidget::playlist_loaded(StringView path, bool loaded) -{ - if (!loaded) { - GUI::MessageBox::show(&m_window, ByteString::formatted("Could not load playlist at \"{}\".", path), "Error opening playlist"sv, GUI::MessageBox::Type::Error); - return; - } - set_playlist_visible(true); - play_file_path(playlist().next()); -} - -void SoundPlayerWidget::audio_load_error(StringView path, StringView error_string) -{ - GUI::MessageBox::show(&m_window, ByteString::formatted("Failed to load audio file: {} ({})", path, error_string.is_null() ? "Unknown error"sv : error_string), - "Filetype error"sv, GUI::MessageBox::Type::Error); -} diff --git a/Userland/Applications/SoundPlayer/SoundPlayerWidget.h b/Userland/Applications/SoundPlayer/SoundPlayerWidget.h deleted file mode 100644 index 43ed95f67a3..00000000000 --- a/Userland/Applications/SoundPlayer/SoundPlayerWidget.h +++ /dev/null @@ -1,93 +0,0 @@ -/* - * Copyright (c) 2021, Cesar Torres - * Copyright (c) 2021, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include "PlaybackManager.h" -#include "Player.h" -#include "VisualizationWidget.h" -#include -#include -#include -#include -#include -#include -#include -#include - -class SoundPlayerWidget final : public GUI::Widget - , public Player { - C_OBJECT(SoundPlayerWidget) - -public: - void set_nonlinear_volume_slider(bool nonlinear); - void set_playlist_visible(bool visible); - RefPtr get_image_from_music_file(); - - template - void set_visualization(Args... args) - { - m_visualization->remove_from_parent(); - update(); - auto new_visualization = T::construct(move(args)...); - m_player_view->insert_child_before(new_visualization, *static_cast(m_playback_progress_slider.ptr())); - m_visualization = new_visualization; - if (!loaded_filename().is_empty()) - m_visualization->start_new_file(loaded_filename()); - } - - virtual void play_state_changed(PlayState) override; - virtual void loop_mode_changed(LoopMode) override; - virtual void shuffle_mode_changed(ShuffleMode) override; - virtual void time_elapsed(int) override; - virtual void file_name_changed(StringView) override; - virtual void playlist_loaded(StringView, bool) override; - virtual void audio_load_error(StringView path, StringView error_reason) override; - virtual void volume_changed(double) override; - virtual void mute_changed(bool) override; - virtual void total_samples_changed(int) override; - virtual void sound_buffer_played(FixedArray const&, int sample_rate, int samples_played) override; - -protected: - void keydown_event(GUI::KeyEvent&) override; - -private: - SoundPlayerWidget(GUI::Window&, Audio::ConnectionToServer&, ImageDecoderClient::Client&); - - void sync_previous_next_actions(); - - void drag_enter_event(GUI::DragEvent& event) override; - void drop_event(GUI::DropEvent& event) override; - GUI::Window& m_window; - ImageDecoderClient::Client& m_image_decoder_client; - - RefPtr m_splitter; - RefPtr m_player_view; - RefPtr m_playlist_widget; - RefPtr m_visualization; - - RefPtr m_play_icon; - RefPtr m_pause_icon; - RefPtr m_stop_icon; - RefPtr m_back_icon; - RefPtr m_next_icon; - RefPtr m_volume_icon; - RefPtr m_muted_icon; - - RefPtr m_play_action; - RefPtr m_stop_action; - RefPtr m_back_action; - RefPtr m_next_action; - RefPtr m_mute_action; - - RefPtr m_playback_progress_slider; - RefPtr m_volume_label; - RefPtr m_volume_slider; - RefPtr m_timestamp_label; - - bool m_nonlinear_volume_slider; -}; diff --git a/Userland/Applications/SoundPlayer/VisualizationWidget.h b/Userland/Applications/SoundPlayer/VisualizationWidget.h deleted file mode 100644 index b908ee883b8..00000000000 --- a/Userland/Applications/SoundPlayer/VisualizationWidget.h +++ /dev/null @@ -1,102 +0,0 @@ -/* - * Copyright (c) 2021, Cesar Torres - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include -#include - -class VisualizationWidget : public GUI::Frame { - C_OBJECT_ABSTRACT(VisualizationWidget) - -public: - virtual void render(GUI::PaintEvent&, FixedArray const& samples) = 0; - - void set_buffer(FixedArray const& buffer) - { - if (buffer.is_empty()) - return; - - if (m_sample_buffer.size() != buffer.size()) - m_sample_buffer.resize(buffer.size()); - - for (size_t i = 0; i < buffer.size(); i++) - m_sample_buffer.data()[i] = (buffer[i].left + buffer[i].right) / 2.f; - - m_frame_count = 0; - } - - void reset_buffer() - { - m_sample_buffer.clear(); - m_render_buffer.fill_with(.0f); - m_frame_count = 0; - } - - virtual void set_samplerate(int samplerate) - { - m_samplerate = samplerate; - } - - virtual void paint_event(GUI::PaintEvent& event) override - { - if (m_sample_buffer.size() == 0) { - Frame::paint_event(event); - GUI::Painter painter(*this); - painter.add_clip_rect(event.rect()); - painter.fill_rect(frame_inner_rect(), Color::Black); - return; - } - - if (m_render_buffer.size() == 0) - return; - - size_t buffer_position = (m_frame_count * REFRESH_TIME_MILLISECONDS) * m_samplerate / 1000; - if (buffer_position + m_render_buffer.size() >= m_sample_buffer.size()) - buffer_position = m_sample_buffer.size() - m_render_buffer.size(); - - AK::TypedTransfer::copy(m_render_buffer.data(), m_sample_buffer.span().slice(buffer_position).data(), m_render_buffer.size()); - - render(event, m_render_buffer); - } - - virtual void timer_event(Core::TimerEvent&) override - { - update(); - m_frame_count++; - } - - size_t frame_count() const { return m_frame_count; } - - ErrorOr set_render_sample_count(size_t count) - { - auto new_buffer = TRY(FixedArray::create(count)); - m_render_buffer.swap(new_buffer); - return {}; - } - virtual void start_new_file(StringView) { } - -protected: - int m_samplerate; - size_t m_frame_count; - Vector m_sample_buffer; - FixedArray m_render_buffer; - - static constexpr size_t REFRESH_TIME_MILLISECONDS = 30; - - VisualizationWidget() - : m_samplerate(-1) - , m_frame_count(0) - { - start_timer(REFRESH_TIME_MILLISECONDS); - } - - virtual ~VisualizationWidget() = default; -}; diff --git a/Userland/Applications/SoundPlayer/main.cpp b/Userland/Applications/SoundPlayer/main.cpp deleted file mode 100644 index c04eb5de5e3..00000000000 --- a/Userland/Applications/SoundPlayer/main.cpp +++ /dev/null @@ -1,170 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * Copyright (c) 2021-2023, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "AlbumCoverVisualizationWidget.h" -#include "BarsVisualizationWidget.h" -#include "Player.h" -#include "SampleWidget.h" -#include "SoundPlayerWidget.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -ErrorOr serenity_main(Main::Arguments arguments) -{ - TRY(Core::System::pledge("stdio recvfd sendfd rpath thread unix proc")); - - StringView file_path; - - Core::ArgsParser args_parser; - args_parser.add_positional_argument(file_path, "Path to audio file to play", "file", Core::ArgsParser::Required::No); - args_parser.parse(arguments); - - auto app = TRY(GUI::Application::create(arguments)); - auto audio_client = TRY(Audio::ConnectionToServer::try_create()); - auto decoder_client = TRY(ImageDecoderClient::Client::try_create()); - - Config::pledge_domains({ "SoundPlayer", "FileManager" }); - app->set_config_domain("SoundPlayer"_string); - - auto app_icon = GUI::Icon::default_icon("app-sound-player"sv); - - auto window = GUI::Window::construct(); - window->set_title("Sound Player"); - window->set_icon(app_icon.bitmap_for_size(16)); - - // start in advanced view by default - Player* player = window->set_main_widget(window, audio_client, decoder_client); - - if (!file_path.is_empty()) { - player->play_file_path(file_path); - if (player->is_playlist(file_path)) - player->set_loop_mode(Player::LoopMode::Playlist); - } - - auto file_menu = window->add_menu("&File"_string); - file_menu->add_action(GUI::CommonActions::make_open_action([&](auto&) { - Optional path = GUI::FilePicker::get_open_filepath(window); - if (path.has_value()) { - player->play_file_path(path.value()); - } - })); - - file_menu->add_separator(); - file_menu->add_action(GUI::CommonActions::make_quit_action([&](auto&) { - app->quit(); - })); - - auto playback_menu = window->add_menu("&Playback"_string); - GUI::ActionGroup loop_actions; - loop_actions.set_exclusive(true); - auto loop_none = GUI::Action::create_checkable("&No Loop", { Mod_Ctrl, Key_N }, [&](auto&) { - player->set_loop_mode(Player::LoopMode::None); - }); - loop_actions.add_action(loop_none); - playback_menu->add_action(loop_none); - - auto loop_file = GUI::Action::create_checkable("Loop &File", { Mod_Ctrl, Key_F }, [&](auto&) { - player->set_loop_mode(Player::LoopMode::File); - }); - loop_actions.add_action(loop_file); - playback_menu->add_action(loop_file); - - auto loop_playlist = GUI::Action::create_checkable("Loop &Playlist", { Mod_Ctrl, Key_P }, [&](auto&) { - player->set_loop_mode(Player::LoopMode::Playlist); - }); - loop_actions.add_action(loop_playlist); - playback_menu->add_action(loop_playlist); - - auto linear_volume_slider = GUI::Action::create_checkable("&Nonlinear Volume Slider", [&](auto& action) { - static_cast(player)->set_nonlinear_volume_slider(action.is_checked()); - }); - playback_menu->add_separator(); - playback_menu->add_action(linear_volume_slider); - playback_menu->add_separator(); - - auto playlist_toggle = GUI::Action::create_checkable("&Show Playlist", [&](auto& action) { - static_cast(player)->set_playlist_visible(action.is_checked()); - }); - if (player->loop_mode() == Player::LoopMode::Playlist) { - playlist_toggle->set_checked(true); - loop_playlist->set_checked(true); - } else { - loop_none->set_checked(true); - } - playback_menu->add_action(playlist_toggle); - - auto shuffle_mode = GUI::Action::create_checkable("S&huffle Playlist", [&](auto& action) { - if (action.is_checked()) - player->set_shuffle_mode(Player::ShuffleMode::Shuffling); - else - player->set_shuffle_mode(Player::ShuffleMode::None); - }); - playback_menu->add_action(shuffle_mode); - - auto visualization_menu = window->add_menu("&Visualization"_string); - GUI::ActionGroup visualization_actions; - visualization_actions.set_exclusive(true); - - auto set_selected_visualization_in_config = [](StringView name) { - Config::write_string("SoundPlayer"sv, "Preferences"sv, "Visualization"sv, name); - }; - - auto bars = GUI::Action::create_checkable("&Bars", [&](auto&) { - static_cast(player)->set_visualization(); - set_selected_visualization_in_config("bars"sv); - }); - visualization_menu->add_action(bars); - visualization_actions.add_action(bars); - - auto samples = GUI::Action::create_checkable("&Samples", [&](auto&) { - static_cast(player)->set_visualization(); - set_selected_visualization_in_config("samples"sv); - }); - visualization_menu->add_action(samples); - visualization_actions.add_action(samples); - - auto album_cover_visualization = GUI::Action::create_checkable("&Album Cover", [&](auto&) { - auto* view = static_cast(player); - view->set_visualization([&view]() { - return view->get_image_from_music_file(); - }); - - set_selected_visualization_in_config("album_cover"sv); - }); - visualization_menu->add_action(album_cover_visualization); - visualization_actions.add_action(album_cover_visualization); - - auto selected_visualization_widget = bars; - auto visualization = Config::read_string("SoundPlayer"sv, "Preferences"sv, "Visualization"sv, "bars"sv); - - if (visualization == "samples") - selected_visualization_widget = samples; - else if (visualization == "album_cover") - selected_visualization_widget = album_cover_visualization; - - selected_visualization_widget->set_checked(true); - - auto help_menu = window->add_menu("&Help"_string); - help_menu->add_action(GUI::CommonActions::make_command_palette_action(window)); - help_menu->add_action(GUI::CommonActions::make_about_action("Sound Player"_string, app_icon, window)); - - window->show(); - return app->exec(); -} diff --git a/Userland/Applications/SpaceAnalyzer/CMakeLists.txt b/Userland/Applications/SpaceAnalyzer/CMakeLists.txt deleted file mode 100644 index 4be9dd2eb9e..00000000000 --- a/Userland/Applications/SpaceAnalyzer/CMakeLists.txt +++ /dev/null @@ -1,17 +0,0 @@ -serenity_component( - SpaceAnalyzer - TARGETS SpaceAnalyzer -) - -compile_gml(SpaceAnalyzer.gml SpaceAnalyzerGML.cpp) - -set(SOURCES - ProgressWindow.cpp - SpaceAnalyzerGML.cpp - Tree.cpp - TreeMapWidget.cpp - main.cpp -) - -serenity_app(SpaceAnalyzer ICON app-space-analyzer) -target_link_libraries(SpaceAnalyzer PRIVATE LibCore LibDesktop LibFileSystem LibGfx LibGUI LibIPC LibMain LibURL) diff --git a/Userland/Applications/SpaceAnalyzer/MainWidget.h b/Userland/Applications/SpaceAnalyzer/MainWidget.h deleted file mode 100644 index 25fd3a836f4..00000000000 --- a/Userland/Applications/SpaceAnalyzer/MainWidget.h +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright (c) 2023, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include - -namespace SpaceAnalyzer { - -class MainWidget final : public GUI::Widget { - C_OBJECT(MainWidget) -public: - static ErrorOr> try_create(); - virtual ~MainWidget() override = default; - -private: - MainWidget() = default; -}; - -} diff --git a/Userland/Applications/SpaceAnalyzer/ProgressWindow.cpp b/Userland/Applications/SpaceAnalyzer/ProgressWindow.cpp deleted file mode 100644 index a97ad229d96..00000000000 --- a/Userland/Applications/SpaceAnalyzer/ProgressWindow.cpp +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright (c) 2021-2022, the SerenityOS developers. - * Copyright (c) 2023, Sam Atkins - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "ProgressWindow.h" -#include -#include -#include - -ErrorOr> ProgressWindow::try_create(StringView title, Window* parent) -{ - auto window = TRY(adopt_nonnull_ref_or_enomem(new (nothrow) ProgressWindow(title, parent))); - - auto main_widget = window->set_main_widget(); - main_widget->set_fill_with_background_color(true); - main_widget->set_layout(); - - auto& label = main_widget->add("Analyzing storage space..."_string); - label.set_fixed_height(22); - - window->m_progress_label = main_widget->add(); - window->m_progress_label->set_fixed_height(22); - - window->update_progress_label(0); - return window; -} - -ProgressWindow::ProgressWindow(StringView title, GUI::Window* parent) - : GUI::Window(parent) -{ - set_title(title); - set_resizable(false); - set_closeable(false); - resize(240, 50); - center_on_screen(); -} - -ProgressWindow::~ProgressWindow() = default; - -void ProgressWindow::update_progress_label(size_t files_encountered_count) -{ - m_progress_label->set_text(String::formatted("{} files...", files_encountered_count).release_value_but_fixme_should_propagate_errors()); - // FIXME: Why is this necessary to make the window repaint? - Core::EventLoop::current().pump(Core::EventLoop::WaitMode::PollForEvents); -} diff --git a/Userland/Applications/SpaceAnalyzer/ProgressWindow.h b/Userland/Applications/SpaceAnalyzer/ProgressWindow.h deleted file mode 100644 index c7e2e1c7606..00000000000 --- a/Userland/Applications/SpaceAnalyzer/ProgressWindow.h +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright (c) 2023, Sam Atkins - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include - -class ProgressWindow final : public GUI::Window { - C_OBJECT_ABSTRACT(ProgressWindow) -public: - static ErrorOr> try_create(StringView title, GUI::Window* parent = nullptr); - virtual ~ProgressWindow() override; - - void update_progress_label(size_t files_encountered_count); - -private: - ProgressWindow(StringView title, GUI::Window* parent = nullptr); - - RefPtr m_progress_label; -}; diff --git a/Userland/Applications/SpaceAnalyzer/SpaceAnalyzer.gml b/Userland/Applications/SpaceAnalyzer/SpaceAnalyzer.gml deleted file mode 100644 index adc40d06f67..00000000000 --- a/Userland/Applications/SpaceAnalyzer/SpaceAnalyzer.gml +++ /dev/null @@ -1,20 +0,0 @@ -@SpaceAnalyzer::MainWidget { - layout: @GUI::VerticalBoxLayout { - spacing: 0 - } - - @GUI::ToolbarContainer { - @GUI::Breadcrumbbar { - fixed_height: 25 - name: "breadcrumbbar" - } - } - - @SpaceAnalyzer::TreeMapWidget { - name: "tree_map" - } - - @GUI::Statusbar { - name: "statusbar" - } -} diff --git a/Userland/Applications/SpaceAnalyzer/Tree.cpp b/Userland/Applications/SpaceAnalyzer/Tree.cpp deleted file mode 100644 index 2e010f7f0fb..00000000000 --- a/Userland/Applications/SpaceAnalyzer/Tree.cpp +++ /dev/null @@ -1,148 +0,0 @@ -/* - * Copyright (c) 2022, the SerenityOS developers. - * Copyright (c) 2023, Sam Atkins - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "Tree.h" -#include -#include -#include -#include -#include - -#include -#include - -static constexpr size_t FILES_ENCOUNTERED_UPDATE_STEP_SIZE = 25; - -long long int TreeNode::update_totals() -{ - long long int result = 0; - if (m_children) { - for (auto& child : *m_children) { - result += child.update_totals(); - } - m_area = result; - } else { - result = m_area; - } - return result; -} - -void TreeNode::sort_children_by_area() const -{ - if (m_children) { - Vector* children = const_cast*>(m_children.ptr()); - quick_sort(*children, [](auto& a, auto& b) { return b.m_area < a.m_area; }); - } -} - -struct QueueEntry { - QueueEntry(ByteString path, TreeNode* node) - : path(move(path)) - , node(node) {}; - ByteString path; - TreeNode* node { nullptr }; -}; - -static MountInfo* find_mount_for_path(ByteString path, Vector& mounts) -{ - MountInfo* result = nullptr; - size_t length = 0; - for (auto& mount_info : mounts) { - ByteString& mount_point = mount_info.mount_point; - if (path.starts_with(mount_point)) { - if (!result || mount_point.length() > length) { - result = &mount_info; - length = mount_point.length(); - } - } - } - return result; -} - -HashMap TreeNode::populate_filesize_tree(Vector& mounts, Function on_progress) -{ - VERIFY(!m_name.ends_with('/')); - - Queue queue; - queue.enqueue(QueueEntry(m_name, this)); - size_t files_encountered_count = 0; - HashMap error_accumulator; - - auto log_error = [&](Error& error) { - auto error_code = error.code(); - int error_sum = error_accumulator.get(error_code).value_or(0); - error_accumulator.set(error_code, error_sum + 1); - }; - - StringBuilder builder = StringBuilder(); - builder.append(m_name); - builder.append('/'); - MountInfo* root_mount_info = find_mount_for_path(builder.to_byte_string(), mounts); - if (!root_mount_info) { - return error_accumulator; - } - while (!queue.is_empty()) { - QueueEntry queue_entry = queue.dequeue(); - - builder.clear(); - builder.append(queue_entry.path); - builder.append('/'); - - MountInfo* mount_info = find_mount_for_path(builder.to_byte_string(), mounts); - if (!mount_info || (mount_info != root_mount_info && mount_info->source != root_mount_info->source)) { - continue; - } - - auto directory_or_error = Core::Directory::create(builder.string_view(), Core::Directory::CreateDirectories::No); - if (directory_or_error.is_error()) { - log_error(directory_or_error.error()); - } else { - auto directory = directory_or_error.release_value(); - queue_entry.node->m_children = make>(); - - auto iteration_result = Core::Directory::for_each_entry(builder.string_view(), Core::DirIterator::SkipParentAndBaseDir, [&](auto& entry, auto&) -> ErrorOr { - TRY(queue_entry.node->m_children->try_append(TreeNode(entry.name))); - return IterationDecision::Continue; - }); - if (iteration_result.is_error()) - log_error(iteration_result.error()); - - for (auto& child : *queue_entry.node->m_children) { - files_encountered_count += 1; - if (!(files_encountered_count % FILES_ENCOUNTERED_UPDATE_STEP_SIZE)) - on_progress(files_encountered_count); - - builder.append(child.m_name); - auto st_or_error = directory.stat(child.m_name, AT_SYMLINK_NOFOLLOW); - if (st_or_error.is_error()) { - log_error(st_or_error.error()); - } else { - auto st = st_or_error.release_value(); - if (S_ISDIR(st.st_mode)) { - queue.enqueue(QueueEntry(builder.to_byte_string(), &child)); - } else { - child.m_area = st.st_size; - } - } - builder.trim(child.m_name.length()); - } - } - } - - update_totals(); - return error_accumulator; -} - -Optional TreeNode::child_with_name(ByteString name) const -{ - for (auto& child : *m_children) { - if (child.name() == name) - return child; - } - - return {}; -} diff --git a/Userland/Applications/SpaceAnalyzer/Tree.h b/Userland/Applications/SpaceAnalyzer/Tree.h deleted file mode 100644 index 34e0913a782..00000000000 --- a/Userland/Applications/SpaceAnalyzer/Tree.h +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright (c) 2022, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include - -struct MountInfo { - ByteString mount_point; - ByteString source; -}; - -class TreeNode final { -public: - TreeNode(ByteString name) - : m_name(move(name)) {}; - - ByteString name() const { return m_name; } - i64 area() const { return m_area; } - size_t num_children() const - { - if (m_children) { - return m_children->size(); - } - return 0; - } - TreeNode const& child_at(size_t i) const { return m_children->at(i); } - Optional child_with_name(ByteString name) const; - void sort_children_by_area() const; - HashMap populate_filesize_tree(Vector& mounts, Function on_progress); - -private: - long long int update_totals(); - - ByteString m_name; - i64 m_area { 0 }; - OwnPtr> m_children; -}; - -class Tree { -public: - static ErrorOr> create(ByteString root_name) - { - return adopt_nonnull_own_or_enomem(new (nothrow) Tree(move(root_name))); - } - ~Tree() {}; - - TreeNode& root() - { - return m_root; - } - -private: - Tree(ByteString root_name) - : m_root(move(root_name)) {}; - TreeNode m_root; -}; diff --git a/Userland/Applications/SpaceAnalyzer/TreeMapWidget.cpp b/Userland/Applications/SpaceAnalyzer/TreeMapWidget.cpp deleted file mode 100644 index f3939084912..00000000000 --- a/Userland/Applications/SpaceAnalyzer/TreeMapWidget.cpp +++ /dev/null @@ -1,471 +0,0 @@ -/* - * Copyright (c) 2021-2022, the SerenityOS developers. - * Copyright (c) 2022-2023, Sam Atkins - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "TreeMapWidget.h" -#include "ProgressWindow.h" -#include "Tree.h" -#include -#include -#include -#include -#include -#include -#include -#include - -namespace SpaceAnalyzer { - -static constexpr Array colors = { - Color(253, 231, 37), - Color(148, 216, 64), - Color(60, 188, 117), - Color(31, 150, 139), - Color(45, 112, 142), - Color(63, 71, 136), - Color(85, 121, 104), -}; - -static float get_normalized_aspect_ratio(float a, float b) -{ - if (a < b) { - return a / b; - } else { - return b / a; - } -} - -static bool node_is_leaf(TreeNode const& node) -{ - return node.num_children() == 0; -} - -bool TreeMapWidget::rect_can_contain_label(Gfx::IntRect const& rect) const -{ - return rect.height() >= font().presentation_size() && rect.width() > 20; -} - -void TreeMapWidget::paint_cell_frame(GUI::Painter& painter, TreeNode const& node, Gfx::IntRect const& cell_rect, Gfx::IntRect const& inner_rect, int depth, HasLabel has_label) const -{ - if (cell_rect.width() <= 2 || cell_rect.height() <= 2) { - painter.fill_rect(cell_rect, Color::Black); - return; - } - Gfx::IntRect remainder = cell_rect; - - Color color = colors[depth % (sizeof(colors) / sizeof(colors[0]))]; - if (m_selected_node_cache == &node) { - color = color.darkened(0.8f); - } - - // Draw borders. - painter.fill_rect(remainder.take_from_right(1), Color::Black); - painter.fill_rect(remainder.take_from_bottom(1), Color::Black); - // Draw highlights. - painter.fill_rect(remainder.take_from_right(1), color.darkened()); - painter.fill_rect(remainder.take_from_bottom(1), color.darkened()); - painter.fill_rect(remainder.take_from_top(1), color.lightened()); - painter.fill_rect(remainder.take_from_left(1), color.lightened()); - - // Paint the background. - if (inner_rect.is_empty()) { - painter.fill_rect(remainder, color); - } else { - // Draw black edges above and to the left of the inner_rect. - Gfx::IntRect border_rect = inner_rect.inflated(2, 2); - Gfx::IntRect hammer_rect = border_rect; - hammer_rect.set_width(hammer_rect.width() - 1); - hammer_rect.set_height(hammer_rect.height() - 1); - painter.fill_rect(border_rect.take_from_top(1), Color::Black); - painter.fill_rect(border_rect.take_from_left(1), Color::Black); - for (auto& shard : remainder.shatter(hammer_rect)) { - painter.fill_rect(shard, color); - } - } - - // Paint text. - if (has_label == HasLabel::Yes) { - Gfx::IntRect text_rect = remainder; - text_rect.shrink(4, 4); - painter.clear_clip_rect(); - painter.add_clip_rect(text_rect); - if (node_is_leaf(node)) { - painter.draw_text(text_rect, node.name(), font(), Gfx::TextAlignment::TopLeft, Color::Black); - text_rect.take_from_top(font().presentation_size() + 1); - painter.draw_text(text_rect, human_readable_size(node.area()), font(), Gfx::TextAlignment::TopLeft, Color::Black); - } else { - painter.draw_text(text_rect, ByteString::formatted("{} - {}", node.name(), human_readable_size(node.area())), font(), Gfx::TextAlignment::TopLeft, Color::Black); - } - painter.clear_clip_rect(); - } -} - -template -void TreeMapWidget::lay_out_children(TreeNode const& node, Gfx::IntRect const& rect, int depth, Function callback) -{ - if (node.num_children() == 0) { - return; - } - - // Check if the children are sorted yet, if not do that now. - for (size_t k = 0; k < node.num_children() - 1; k++) { - if (node.child_at(k).area() < node.child_at(k + 1).area()) { - node.sort_children_by_area(); - break; - } - } - - i64 total_area = node.area(); - Gfx::IntRect canvas = rect; - bool remaining_nodes_are_too_small = false; - for (size_t i = 0; !remaining_nodes_are_too_small && i < node.num_children(); i++) { - i64 const i_node_area = node.child_at(i).area(); - if (i_node_area == 0) - break; - - size_t const long_side_size = max(canvas.width(), canvas.height()); - size_t const short_side_size = min(canvas.width(), canvas.height()); - - size_t row_or_column_size = long_side_size * i_node_area / total_area; - i64 node_area_sum = i_node_area; - size_t k = i + 1; - - // Try to add nodes to this row or column so long as the worst aspect ratio of - // the new set of nodes is better than the worst aspect ratio of the current set. - { - float best_worst_aspect_ratio_so_far = get_normalized_aspect_ratio(row_or_column_size, short_side_size); - for (; k < node.num_children(); k++) { - // Do a preliminary calculation of the worst aspect ratio of the nodes at index i and k - // if that aspect ratio is better than the 'best_worst_aspect_ratio_so_far' we keep it, - // otherwise it is discarded. - i64 k_node_area = node.child_at(k).area(); - if (k_node_area == 0) { - break; - } - i64 new_node_area_sum = node_area_sum + k_node_area; - size_t new_row_or_column_size = long_side_size * new_node_area_sum / total_area; - size_t i_node_size = short_side_size * i_node_area / new_node_area_sum; - size_t k_node_size = short_side_size * k_node_area / new_node_area_sum; - float i_node_aspect_ratio = get_normalized_aspect_ratio(new_row_or_column_size, i_node_size); - float k_node_aspect_ratio = get_normalized_aspect_ratio(new_row_or_column_size, k_node_size); - float new_worst_aspect_ratio = min(i_node_aspect_ratio, k_node_aspect_ratio); - if (new_worst_aspect_ratio < best_worst_aspect_ratio_so_far) { - break; - } - best_worst_aspect_ratio_so_far = new_worst_aspect_ratio; - node_area_sum = new_node_area_sum; - row_or_column_size = new_row_or_column_size; - } - } - - // Paint the elements from 'i' up to and including 'k-1'. - { - size_t const fixed_side_size = row_or_column_size; - i64 placement_area = node_area_sum; - size_t main_dim = short_side_size; - - // Lay out nodes in a row or column. - Orientation orientation = canvas.width() > canvas.height() ? Orientation::Horizontal : Orientation::Vertical; - Gfx::IntRect layout_rect = canvas; - layout_rect.set_primary_size_for_orientation(orientation, fixed_side_size); - for (size_t q = i; q < k; q++) { - auto& child = node.child_at(q); - size_t node_size = main_dim * child.area() / placement_area; - Gfx::IntRect cell_rect = layout_rect; - cell_rect.set_secondary_size_for_orientation(orientation, node_size); - Gfx::IntRect inner_rect; - HasLabel has_label = HasLabel::No; - if (child.num_children() != 0 && rect.height() >= 8 && rect.width() >= 8) { - inner_rect = cell_rect; - inner_rect.shrink(4, 4); // border and shading - if (rect_can_contain_label(inner_rect)) { - int const margin = 5; - has_label = HasLabel::Yes; - inner_rect.set_y(inner_rect.y() + font().presentation_size() + margin); - inner_rect.set_height(inner_rect.height() - (font().presentation_size() + margin * 2)); - inner_rect.set_x(inner_rect.x() + margin); - inner_rect.set_width(inner_rect.width() - margin * 2); - } - } else if (rect_can_contain_label(cell_rect)) { - has_label = HasLabel::Yes; - } - callback(child, q, cell_rect, inner_rect, depth, has_label, IsRemainder::No); - if (cell_rect.width() * cell_rect.height() < 16) { - remaining_nodes_are_too_small = true; - } else if (!inner_rect.is_empty()) { - lay_out_children(child, inner_rect, depth + 1, callback); - } - layout_rect.set_secondary_offset_for_orientation(orientation, layout_rect.secondary_offset_for_orientation(orientation) + node_size); - main_dim -= node_size; - placement_area -= child.area(); - } - canvas.set_primary_offset_for_orientation(orientation, canvas.primary_offset_for_orientation(orientation) + fixed_side_size); - canvas.set_primary_size_for_orientation(orientation, canvas.primary_size_for_orientation(orientation) - fixed_side_size); - } - - // Consume nodes that were added to this row or column. - i = k - 1; - total_area -= node_area_sum; - } - - // If not the entire canvas was filled with nodes, fill the remaining area with a dither pattern. - if (!canvas.is_empty()) { - callback(node, 0, canvas, Gfx::IntRect(), depth, HasLabel::No, IsRemainder::Yes); - } -} - -TreeNode const* TreeMapWidget::path_node(size_t n) const -{ - if (!m_tree.ptr()) - return nullptr; - TreeNode const* iter = &m_tree->root(); - size_t path_index = 0; - while (iter && path_index < m_path_segments.size() && path_index < n) { - auto child_name = m_path_segments[path_index]; - auto maybe_child = iter->child_with_name(child_name); - if (!maybe_child.has_value()) - return nullptr; - iter = &maybe_child.release_value(); - path_index++; - } - return iter; -} - -void TreeMapWidget::paint_event(GUI::PaintEvent& event) -{ - GUI::Frame::paint_event(event); - GUI::Painter painter(*this); - - m_selected_node_cache = path_node(m_path_segments.size()); - - TreeNode const* node = path_node(m_viewpoint); - if (!node) { - painter.fill_rect(frame_inner_rect(), Color::MidGray); - } else if (node_is_leaf(*node)) { - paint_cell_frame(painter, *node, frame_inner_rect(), Gfx::IntRect(), m_viewpoint - 1, HasLabel::Yes); - } else { - lay_out_children(*node, frame_inner_rect(), m_viewpoint, [&](TreeNode const& node, int, Gfx::IntRect const& rect, Gfx::IntRect const& inner_rect, int depth, HasLabel has_label, IsRemainder remainder) { - if (remainder == IsRemainder::No) { - paint_cell_frame(painter, node, rect, inner_rect, depth, has_label); - } else { - Color color = colors[depth % (sizeof(colors) / sizeof(colors[0]))]; - Gfx::IntRect dither_rect = rect; - painter.fill_rect(dither_rect.take_from_right(1), Color::Black); - painter.fill_rect(dither_rect.take_from_bottom(1), Color::Black); - painter.fill_rect_with_dither_pattern(dither_rect, color, Color::Black); - } - }); - } -} - -Vector TreeMapWidget::path_to_position(Gfx::IntPoint position) -{ - TreeNode const* node = path_node(m_viewpoint); - if (!node) { - return {}; - } - Vector path; - lay_out_children(*node, frame_inner_rect(), m_viewpoint, [&](TreeNode const& node, int, Gfx::IntRect const& rect, Gfx::IntRect const&, int, HasLabel, IsRemainder is_remainder) { - if (is_remainder == IsRemainder::No && rect.contains(position)) { - path.append(node.name()); - } - }); - return path; -} - -void TreeMapWidget::mousemove_event(GUI::MouseEvent& event) -{ - auto* node = path_node(m_viewpoint); - if (!node) { - set_tooltip({}); - return; - } - - auto* hovered_node = node; - lay_out_children(*node, frame_inner_rect(), m_viewpoint, [&](TreeNode const&, int index, Gfx::IntRect const& rect, Gfx::IntRect const&, int, HasLabel, IsRemainder is_remainder) { - if (is_remainder == IsRemainder::No && rect.contains(event.position())) { - hovered_node = &hovered_node->child_at(index); - } - }); - - set_tooltip(MUST(String::formatted("{}\n{}", hovered_node->name(), human_readable_size(hovered_node->area())))); -} - -void TreeMapWidget::mousedown_event(GUI::MouseEvent& event) -{ - TreeNode const* node = path_node(m_viewpoint); - if (node && !node_is_leaf(*node)) { - auto path = path_to_position(event.position()); - if (!path.is_empty()) { - m_path_segments.shrink(m_viewpoint); - m_path_segments.extend(path); - update(); - } - } -} - -void TreeMapWidget::doubleclick_event(GUI::MouseEvent& event) -{ - if (event.button() != GUI::MouseButton::Primary) - return; - TreeNode const* node = path_node(m_viewpoint); - if (node && !node_is_leaf(*node)) { - auto path = path_to_position(event.position()); - m_path_segments.shrink(m_viewpoint); - m_path_segments.extend(path); - m_viewpoint = m_path_segments.size(); - if (on_path_change) { - on_path_change(); - } - update(); - } -} - -void TreeMapWidget::keydown_event(GUI::KeyEvent& event) -{ - if (event.key() == KeyCode::Key_Left) - set_viewpoint(m_viewpoint == 0 ? m_path_segments.size() : m_viewpoint - 1); - else if (event.key() == KeyCode::Key_Right) - set_viewpoint(m_viewpoint == m_path_segments.size() ? 0 : m_viewpoint + 1); - else - event.ignore(); -} - -void TreeMapWidget::mousewheel_event(GUI::MouseEvent& event) -{ - int delta = event.wheel_raw_delta_y(); - if (delta > 0) { - size_t step_back = delta; - if (step_back > m_viewpoint) - step_back = m_viewpoint; - set_viewpoint(m_viewpoint - step_back); - } else { - size_t step_up = -delta; - set_viewpoint(m_viewpoint + step_up); - } -} - -void TreeMapWidget::context_menu_event(GUI::ContextMenuEvent& context_menu_event) -{ - if (on_context_menu_request) - on_context_menu_request(context_menu_event); -} - -void TreeMapWidget::recalculate_path_for_new_tree() -{ - TreeNode const* current = &m_tree->root(); - size_t new_path_length = 0; - for (auto& segment : m_path_segments) { - auto maybe_child = current->child_with_name(segment); - if (!maybe_child.has_value()) - break; - new_path_length++; - current = &maybe_child.release_value(); - } - m_path_segments.shrink(new_path_length); - if (new_path_length < m_viewpoint) - m_viewpoint = new_path_length - 1; -} - -static ErrorOr fill_mounts(Vector& output) -{ - // Output info about currently mounted filesystems. - auto file = TRY(Core::File::open("/sys/kernel/df"sv, Core::File::OpenMode::Read)); - - auto content = TRY(file->read_until_eof()); - auto json = TRY(JsonValue::from_string(content)); - - TRY(json.as_array().try_for_each([&output](JsonValue const& value) -> ErrorOr { - auto& filesystem_object = value.as_object(); - MountInfo mount_info; - mount_info.mount_point = filesystem_object.get_byte_string("mount_point"sv).value_or({}); - mount_info.source = filesystem_object.get_byte_string("source"sv).value_or("none"); - TRY(output.try_append(mount_info)); - return {}; - })); - - return {}; -} - -ErrorOr TreeMapWidget::analyze(GUI::Statusbar& statusbar) -{ - statusbar.set_text({}); - auto progress_window = TRY(ProgressWindow::try_create("Space Analyzer"sv)); - progress_window->show(); - - // Build an in-memory tree mirroring the filesystem and for each node - // calculate the sum of the file size for all its descendants. - auto tree = TRY(Tree::create("")); - Vector mounts; - TRY(fill_mounts(mounts)); - auto errors = tree->root().populate_filesize_tree(mounts, [&](size_t processed_file_count) { - progress_window->update_progress_label(processed_file_count); - }); - - progress_window->close(); - - // Display an error summary in the statusbar. - if (!errors.is_empty()) { - StringBuilder builder; - bool first = true; - builder.append("Some directories were not analyzed: "sv); - for (auto& key : errors.keys()) { - if (!first) { - builder.append(", "sv); - } - auto const* error = strerror(key); - builder.append({ error, strlen(error) }); - builder.append(" ("sv); - int value = errors.get(key).value(); - builder.append(ByteString::number(value)); - if (value == 1) { - builder.append(" time"sv); - } else { - builder.append(" times"sv); - } - builder.append(')'); - first = false; - } - statusbar.set_text(TRY(builder.to_string())); - } else { - statusbar.set_text("No errors"_string); - } - - m_tree = move(tree); - recalculate_path_for_new_tree(); - - if (on_path_change) { - on_path_change(); - } - update(); - - return {}; -} - -void TreeMapWidget::set_viewpoint(size_t viewpoint) -{ - if (m_viewpoint == viewpoint) - return; - if (viewpoint > m_path_segments.size()) - viewpoint = m_path_segments.size(); - m_viewpoint = viewpoint; - if (on_path_change) { - on_path_change(); - } - update(); -} - -size_t TreeMapWidget::path_size() const -{ - return m_path_segments.size() + 1; -} - -size_t TreeMapWidget::viewpoint() const -{ - return m_viewpoint; -} - -} diff --git a/Userland/Applications/SpaceAnalyzer/TreeMapWidget.h b/Userland/Applications/SpaceAnalyzer/TreeMapWidget.h deleted file mode 100644 index 6131e839264..00000000000 --- a/Userland/Applications/SpaceAnalyzer/TreeMapWidget.h +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright (c) 2021-2022, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include "Tree.h" -#include -#include -#include - -namespace SpaceAnalyzer { - -class TreeMapWidget final : public GUI::Frame { - C_OBJECT(TreeMapWidget) - -public: - virtual ~TreeMapWidget() override = default; - Function on_path_change; - Function on_context_menu_request; - size_t path_size() const; - TreeNode const* path_node(size_t n) const; - size_t viewpoint() const; - void set_viewpoint(size_t); - ErrorOr analyze(GUI::Statusbar&); - -private: - TreeMapWidget() = default; - virtual void paint_event(GUI::PaintEvent&) override; - virtual void mousemove_event(GUI::MouseEvent&) override; - virtual void mousedown_event(GUI::MouseEvent&) override; - virtual void doubleclick_event(GUI::MouseEvent&) override; - virtual void mousewheel_event(GUI::MouseEvent&) override; - virtual void context_menu_event(GUI::ContextMenuEvent&) override; - virtual void keydown_event(GUI::KeyEvent&) override; - - bool rect_can_contain_label(Gfx::IntRect const& rect) const; - - enum class HasLabel { - Yes, - No - }; - enum class IsRemainder { - Yes, - No - }; - - template - void lay_out_children(TreeNode const&, Gfx::IntRect const&, int depth, Function); - void paint_cell_frame(GUI::Painter&, TreeNode const&, Gfx::IntRect const&, Gfx::IntRect const&, int depth, HasLabel has_label) const; - Vector path_to_position(Gfx::IntPoint); - void recalculate_path_for_new_tree(); - - OwnPtr m_tree; - Vector m_path_segments; - size_t m_viewpoint { 0 }; // Current position within m_path_segments. - void const* m_selected_node_cache; -}; - -} diff --git a/Userland/Applications/SpaceAnalyzer/main.cpp b/Userland/Applications/SpaceAnalyzer/main.cpp deleted file mode 100644 index 0b59c13a331..00000000000 --- a/Userland/Applications/SpaceAnalyzer/main.cpp +++ /dev/null @@ -1,197 +0,0 @@ -/* - * Copyright (c) 2021-2022, the SerenityOS developers. - * Copyright (c) 2023, Sam Atkins - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "MainWidget.h" -#include "Tree.h" -#include "TreeMapWidget.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -static auto const APP_NAME = "Space Analyzer"_string; - -static ByteString get_absolute_path_to_selected_node(SpaceAnalyzer::TreeMapWidget const& tree_map_widget, bool include_last_node = true) -{ - StringBuilder path_builder; - for (size_t k = 0; k < tree_map_widget.path_size() - (include_last_node ? 0 : 1); k++) { - if (k != 0) { - path_builder.append('/'); - } - TreeNode const* node = tree_map_widget.path_node(k); - path_builder.append(node->name()); - } - return path_builder.to_byte_string(); -} - -ErrorOr serenity_main(Main::Arguments arguments) -{ - auto app = TRY(GUI::Application::create(arguments)); - - // Configure application window. - auto app_icon = GUI::Icon::default_icon("app-space-analyzer"sv); - auto window = GUI::Window::construct(); - window->set_title(APP_NAME.bytes_as_string_view()); - window->resize(640, 480); - window->set_icon(app_icon.bitmap_for_size(16)); - - // Load widgets. - auto main_widget = TRY(SpaceAnalyzer::MainWidget::try_create()); - window->set_main_widget(main_widget); - - auto& breadcrumbbar = *main_widget->find_descendant_of_type_named("breadcrumbbar"); - auto& tree_map_widget = *main_widget->find_descendant_of_type_named("tree_map"); - auto& statusbar = *main_widget->find_descendant_of_type_named("statusbar"); - GUI::Application::the()->on_action_enter = [&statusbar](GUI::Action& action) { - statusbar.set_override_text(action.status_tip()); - }; - GUI::Application::the()->on_action_leave = [&statusbar](GUI::Action&) { - statusbar.set_override_text({}); - }; - - tree_map_widget.set_focus(true); - - auto file_menu = window->add_menu("&File"_string); - file_menu->add_action(GUI::Action::create("&Analyze", { KeyCode::Key_F5 }, TRY(Gfx::Bitmap::load_from_file("/res/icons/16x16/reload.png"sv)), [&](auto&) { - // FIXME: Just modify the tree in memory instead of traversing the entire file system - if (auto result = tree_map_widget.analyze(statusbar); result.is_error()) { - GUI::MessageBox::show_error(window, ByteString::formatted("{}", result.error())); - } - })); - file_menu->add_separator(); - file_menu->add_action(GUI::CommonActions::make_quit_action([&](auto&) { - app->quit(); - })); - - auto view_menu = window->add_menu("&View"_string); - view_menu->add_action(GUI::CommonActions::make_fullscreen_action([&](auto&) { - window->set_fullscreen(!window->is_fullscreen()); - })); - - auto help_menu = window->add_menu("&Help"_string); - help_menu->add_action(GUI::CommonActions::make_command_palette_action(window)); - help_menu->add_action(GUI::CommonActions::make_about_action(APP_NAME, app_icon, window)); - - auto open_icon = TRY(Gfx::Bitmap::load_from_file("/res/icons/16x16/open.png"sv)); - // Configure the node's context menu. - auto open_action = GUI::Action::create("Open in File Manager", { Mod_Ctrl, Key_O }, open_icon, [&](auto&) { - auto path_string = get_absolute_path_to_selected_node(tree_map_widget); - if (path_string.is_empty()) - return; - - if (FileSystem::is_directory(path_string)) { - Desktop::Launcher::open(URL::create_with_file_scheme(path_string)); - return; - } - LexicalPath path { path_string }; - Desktop::Launcher::open(URL::create_with_file_scheme(path.dirname(), path.basename())); - }); - - auto copy_icon = TRY(Gfx::Bitmap::load_from_file("/res/icons/16x16/edit-copy.png"sv)); - auto copy_path_action = GUI::Action::create("Copy Path to Clipboard", { Mod_Ctrl, Key_C }, copy_icon, [&](auto&) { - GUI::Clipboard::the().set_plain_text(get_absolute_path_to_selected_node(tree_map_widget)); - }); - auto delete_action = GUI::CommonActions::make_delete_action([&](auto&) { - ByteString selected_node_path = get_absolute_path_to_selected_node(tree_map_widget); - bool try_again = true; - while (try_again) { - try_again = false; - - auto deletion_result = FileSystem::remove(selected_node_path, FileSystem::RecursionMode::Allowed); - if (deletion_result.is_error()) { - auto retry_message_result = GUI::MessageBox::show(window, - ByteString::formatted("Failed to delete \"{}\": {}. Retry?", - selected_node_path, - deletion_result.error()), - "Deletion failed"sv, - GUI::MessageBox::Type::Error, - GUI::MessageBox::InputType::YesNo); - if (retry_message_result == GUI::MessageBox::ExecResult::Yes) { - try_again = true; - } - } else { - GUI::MessageBox::show(window, - ByteString::formatted("Successfully deleted \"{}\".", selected_node_path), - "Deletion completed"sv, - GUI::MessageBox::Type::Information, - GUI::MessageBox::InputType::OK); - } - } - - if (auto result = tree_map_widget.analyze(statusbar); result.is_error()) { - GUI::MessageBox::show_error(window, ByteString::formatted("{}", result.error())); - } - }); - - auto context_menu = GUI::Menu::construct(); - context_menu->add_action(open_action); - context_menu->add_action(copy_path_action); - context_menu->add_action(delete_action); - - // Configure event handlers. - breadcrumbbar.on_segment_click = [&](size_t index) { - VERIFY(index < tree_map_widget.path_size()); - tree_map_widget.set_viewpoint(index); - }; - tree_map_widget.on_path_change = [&]() { - StringBuilder builder; - - breadcrumbbar.clear_segments(); - for (size_t k = 0; k < tree_map_widget.path_size(); k++) { - if (k == 0) { - if (tree_map_widget.viewpoint() == 0) - window->set_title("/ - SpaceAnalyzer"); - - breadcrumbbar.append_segment("/", GUI::FileIconProvider::icon_for_path("/"sv).bitmap_for_size(16), "/", "/"_string); - continue; - } - - TreeNode const* node = tree_map_widget.path_node(k); - - builder.append('/'); - builder.append(node->name()); - - // Sneakily set the window title here, while the StringBuilder holds the right amount of the path. - if (k == tree_map_widget.viewpoint()) - window->set_title(ByteString::formatted("{} - SpaceAnalyzer", builder.string_view())); - - breadcrumbbar.append_segment(node->name(), GUI::FileIconProvider::icon_for_path(builder.string_view()).bitmap_for_size(16), builder.string_view(), MUST(builder.to_string())); - } - breadcrumbbar.set_selected_segment(tree_map_widget.viewpoint()); - }; - tree_map_widget.on_context_menu_request = [&](const GUI::ContextMenuEvent& event) { - ByteString selected_node_path = get_absolute_path_to_selected_node(tree_map_widget); - if (selected_node_path.is_empty()) - return; - delete_action->set_enabled(FileSystem::can_delete_or_move(selected_node_path)); - if (FileSystem::is_directory(selected_node_path)) - open_action->set_text("Open in File Manager"); - else - open_action->set_text("Reveal in File Manager"); - - context_menu->popup(event.screen_position()); - }; - - // At startup automatically do an analysis of root. - TRY(tree_map_widget.analyze(statusbar)); - - window->show(); - return app->exec(); -} diff --git a/Userland/Applications/Spreadsheet/CMakeLists.txt b/Userland/Applications/Spreadsheet/CMakeLists.txt deleted file mode 100644 index 3613ce0d86e..00000000000 --- a/Userland/Applications/Spreadsheet/CMakeLists.txt +++ /dev/null @@ -1,50 +0,0 @@ -serenity_component( - Spreadsheet - TARGETS Spreadsheet - DEPENDS WebContent -) - -stringify_gml(CondFormatting.gml CondFormattingGML.h cond_fmt_gml) -stringify_gml(CondView.gml CondFormattingViewGML.h cond_fmt_view_gml) -stringify_gml(csv_import.gml CSVImportGML.h csv_import_gml) -stringify_gml(csv_export.gml CSVExportGML.h csv_export_gml) -stringify_gml(select_format_page.gml FormatSelectionPageGML.h select_format_page_gml) - -set(SOURCES - Cell.cpp - CellSyntaxHighlighter.cpp - CellType/Date.cpp - CellType/Format.cpp - CellType/Identity.cpp - CellType/Numeric.cpp - CellType/String.cpp - CellType/Type.cpp - CellTypeDialog.cpp - ExportDialog.cpp - HelpWindow.cpp - ImportDialog.cpp - JSIntegration.cpp - Readers/XSV.cpp - Spreadsheet.cpp - SpreadsheetModel.cpp - SpreadsheetView.cpp - SpreadsheetWidget.cpp - Workbook.cpp - main.cpp -) - -set(GENERATED_SOURCES - CSVExportGML.h - CSVImportGML.h - CondFormattingGML.h - CondFormattingViewGML.h - FormatSelectionPageGML.h -) - -serenity_app(Spreadsheet ICON app-spreadsheet) -target_link_libraries(Spreadsheet PRIVATE LibConfig LibCore LibFileSystem LibFileSystemAccessClient LibGfx LibGUI LibJS LibMain LibMarkdown LibSyntax LibWebView LibWeb LibURL) - -serenity_test(Writers/Test/TestXSVWriter.cpp Spreadsheet) - -serenity_test(Readers/Test/TestXSV.cpp Spreadsheet) -target_sources(TestXSV PRIVATE Readers/XSV.cpp) diff --git a/Userland/Applications/Spreadsheet/Cell.cpp b/Userland/Applications/Spreadsheet/Cell.cpp deleted file mode 100644 index 370f7a5cac6..00000000000 --- a/Userland/Applications/Spreadsheet/Cell.cpp +++ /dev/null @@ -1,201 +0,0 @@ -/* - * Copyright (c) 2020, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "Cell.h" -#include "Spreadsheet.h" -#include -#include -#include - -namespace Spreadsheet { - -void Cell::set_data(ByteString new_data) -{ - // If we are a formula, we do not save the beginning '=', if the new_data is "" we can simply change our kind - if (m_kind == Formula && m_data.is_empty() && new_data.is_empty()) { - m_kind = LiteralString; - return; - } - - if (m_data == new_data) - return; - - if (new_data.starts_with('=')) { - new_data = new_data.substring(1, new_data.length() - 1); - m_kind = Formula; - } else { - m_kind = LiteralString; - } - - m_data = move(new_data); - m_dirty = true; - m_evaluated_externally = false; -} - -void Cell::set_data(JS::Value new_data) -{ - m_dirty = true; - m_evaluated_externally = true; - - StringBuilder builder; - - builder.append(new_data.to_string_without_side_effects()); - m_data = builder.to_byte_string(); - - m_evaluated_data = move(new_data); -} - -void Cell::set_type(CellType const* type) -{ - m_type = type; -} - -void Cell::set_type(StringView name) -{ - auto* cell_type = CellType::get_by_name(name); - if (cell_type) { - return set_type(cell_type); - } - - VERIFY_NOT_REACHED(); -} - -void Cell::set_type_metadata(CellTypeMetadata const& metadata) -{ - m_type_metadata = metadata; -} - -void Cell::set_type_metadata(CellTypeMetadata&& metadata) -{ - m_type_metadata = move(metadata); -} - -CellType const& Cell::type() const -{ - if (m_type) - return *m_type; - - if (m_kind == LiteralString) { - if (m_data.to_number().has_value()) - return *CellType::get_by_name("Numeric"sv); - } - - return *CellType::get_by_name("Identity"sv); -} - -JS::ThrowCompletionOr Cell::typed_display() const -{ - return type().display(const_cast(*this), m_type_metadata); -} - -JS::ThrowCompletionOr Cell::typed_js_data() const -{ - return type().js_value(const_cast(*this), m_type_metadata); -} - -void Cell::update_data(Badge) -{ - TemporaryChange cell_change { m_sheet->current_evaluated_cell(), this }; - if (!m_dirty) - return; - - if (m_dirty) { - m_dirty = false; - if (m_kind == Formula) { - if (!m_evaluated_externally) { - auto value_or_error = m_sheet->evaluate(m_data, this); - if (value_or_error.is_error()) { - m_evaluated_data = JS::js_undefined(); - m_thrown_value = *value_or_error.release_error().release_value(); - } else { - m_evaluated_data = value_or_error.release_value(); - m_thrown_value = {}; - } - } - } - - for (auto& ref : m_referencing_cells) { - if (ref) { - ref->m_dirty = true; - ref->update(); - } - } - } - - m_evaluated_formats.background_color.clear(); - m_evaluated_formats.foreground_color.clear(); - if (m_thrown_value.is_empty()) { - for (auto& fmt : m_conditional_formats) { - if (!fmt.condition.is_empty()) { - auto value_or_error = m_sheet->evaluate(fmt.condition, this); - if (value_or_error.is_error()) { - m_thrown_value = *value_or_error.release_error().release_value(); - } else { - if (value_or_error.release_value().to_boolean()) { - if (fmt.background_color.has_value()) - m_evaluated_formats.background_color = fmt.background_color; - if (fmt.foreground_color.has_value()) - m_evaluated_formats.foreground_color = fmt.foreground_color; - } - } - } - } - } -} - -void Cell::update() -{ - m_sheet->update(*this); -} - -JS::Value Cell::js_data() -{ - if (m_dirty) - update(); - - if (m_kind == Formula) - return m_evaluated_data; - - auto& vm = m_sheet->vm(); - return JS::PrimitiveString::create(vm, m_data); -} - -ByteString Cell::source() const -{ - StringBuilder builder; - if (m_kind == Formula) - builder.append('='); - builder.append(m_data); - return builder.to_byte_string(); -} - -// FIXME: Find a better way to figure out dependencies -void Cell::reference_from(Cell* other) -{ - if (!other || other == this) - return; - - if (!m_referencing_cells.find_if([other](auto const& ptr) { return ptr.ptr() == other; }).is_end()) - return; - - m_referencing_cells.append(other->make_weak_ptr()); -} - -void Cell::copy_from(Cell const& other) -{ - m_dirty = true; - m_evaluated_externally = other.m_evaluated_externally; - m_data = other.m_data; - m_evaluated_data = other.m_evaluated_data; - m_kind = other.m_kind; - m_type = other.m_type; - m_type_metadata = other.m_type_metadata; - m_conditional_formats = other.m_conditional_formats; - m_evaluated_formats = other.m_evaluated_formats; - m_thrown_value = other.m_thrown_value; -} - -} diff --git a/Userland/Applications/Spreadsheet/Cell.h b/Userland/Applications/Spreadsheet/Cell.h deleted file mode 100644 index d7072f59529..00000000000 --- a/Userland/Applications/Spreadsheet/Cell.h +++ /dev/null @@ -1,135 +0,0 @@ -/* - * Copyright (c) 2020, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include "CellType/Type.h" -#include "ConditionalFormatting.h" -#include "Forward.h" -#include "JSIntegration.h" -#include "Position.h" -#include -#include -#include -#include - -namespace Spreadsheet { - -struct Cell : public Weakable { - enum Kind { - LiteralString, - Formula, - }; - - Cell(ByteString data, Position position, WeakPtr sheet) - : m_dirty(false) - , m_data(move(data)) - , m_kind(LiteralString) - , m_sheet(sheet) - , m_position(move(position)) - { - } - - Cell(ByteString source, JS::Value&& cell_value, Position position, WeakPtr sheet) - : m_dirty(false) - , m_data(move(source)) - , m_evaluated_data(move(cell_value)) - , m_kind(Formula) - , m_sheet(sheet) - , m_position(move(position)) - { - } - - void reference_from(Cell*); - - void set_data(ByteString new_data); - void set_data(JS::Value new_data); - bool dirty() const { return m_dirty; } - void clear_dirty() { m_dirty = false; } - - StringView name_for_javascript(Sheet const& sheet) const - { - if (!m_name_for_javascript.is_empty()) - return m_name_for_javascript; - - m_name_for_javascript = ByteString::formatted("cell {}", m_position.to_cell_identifier(sheet)); - return m_name_for_javascript; - } - - void set_thrown_value(JS::Value value) { m_thrown_value = value; } - Optional thrown_value() const - { - if (m_thrown_value.is_empty()) - return {}; - return m_thrown_value; - } - - ByteString const& data() const { return m_data; } - const JS::Value& evaluated_data() const { return m_evaluated_data; } - Kind kind() const { return m_kind; } - Vector> const& referencing_cells() const { return m_referencing_cells; } - - void set_type(StringView name); - void set_type(CellType const*); - void set_type_metadata(CellTypeMetadata const&); - void set_type_metadata(CellTypeMetadata&&); - - Position const& position() const { return m_position; } - void set_position(Position position, Badge) - { - if (position != m_position) { - m_dirty = true; - m_position = move(position); - } - } - - Format const& evaluated_formats() const { return m_evaluated_formats; } - Format& evaluated_formats() { return m_evaluated_formats; } - Vector const& conditional_formats() const { return m_conditional_formats; } - void set_conditional_formats(Vector&& fmts) - { - m_dirty = true; - m_conditional_formats = move(fmts); - } - - JS::ThrowCompletionOr typed_display() const; - JS::ThrowCompletionOr typed_js_data() const; - - CellType const& type() const; - CellTypeMetadata const& type_metadata() const { return m_type_metadata; } - CellTypeMetadata& type_metadata() { return m_type_metadata; } - - ByteString source() const; - - JS::Value js_data(); - - void update(); - void update_data(Badge); - - Sheet const& sheet() const { return *m_sheet; } - Sheet& sheet() { return *m_sheet; } - - void copy_from(Cell const&); - -private: - bool m_dirty { false }; - bool m_evaluated_externally { false }; - ByteString m_data; - JS::Value m_evaluated_data; - JS::Value m_thrown_value; - Kind m_kind { LiteralString }; - WeakPtr m_sheet; - Vector> m_referencing_cells; - CellType const* m_type { nullptr }; - CellTypeMetadata m_type_metadata; - Position m_position; - mutable ByteString m_name_for_javascript; - - Vector m_conditional_formats; - Format m_evaluated_formats; -}; - -} diff --git a/Userland/Applications/Spreadsheet/CellSyntaxHighlighter.cpp b/Userland/Applications/Spreadsheet/CellSyntaxHighlighter.cpp deleted file mode 100644 index 7a2e0f2cee4..00000000000 --- a/Userland/Applications/Spreadsheet/CellSyntaxHighlighter.cpp +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright (c) 2020-2022, the SerenityOS developers. - * Copyright (c) 2023, Matteo benetti - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "CellSyntaxHighlighter.h" -#include -#include -#include - -namespace Spreadsheet { - -void CellSyntaxHighlighter::rehighlight(Palette const& palette) -{ - m_client->clear_spans(); - - if (m_client->get_text().starts_with('=')) { - - JS::SyntaxHighlighter::rehighlight(palette); - - auto spans = m_client->spans(); - - // Highlight the '=' - spans.empend( - GUI::TextRange { { 0, 0 }, { 0, 1 } }, - Gfx::TextAttributes { - palette.syntax_keyword(), - Optional {}, - false, - }, - (u64)-1, - false); - - if (m_cell && m_cell->thrown_value().has_value()) { - if (auto value = m_cell->thrown_value().value(); value.is_object() && is(value.as_object())) { - auto& error = static_cast(value.as_object()); - auto& range = error.traceback().first().source_range(); - - spans.prepend({ - GUI::TextRange { { range.start.line - 1, range.start.column }, { range.end.line - 1, range.end.column } }, - Gfx::TextAttributes { - Color::Black, - Color::Red, - false, - }, - (u64)-1, - false, - }); - } - } - - m_client->do_set_spans(move(spans)); - } - - m_client->do_update(); -} -} diff --git a/Userland/Applications/Spreadsheet/CellSyntaxHighlighter.h b/Userland/Applications/Spreadsheet/CellSyntaxHighlighter.h deleted file mode 100644 index 265c28efbef..00000000000 --- a/Userland/Applications/Spreadsheet/CellSyntaxHighlighter.h +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright (c) 2020-2022, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include "Cell.h" -#include - -namespace Spreadsheet { - -class CellSyntaxHighlighter final : public JS::SyntaxHighlighter { -public: - CellSyntaxHighlighter() = default; - virtual ~CellSyntaxHighlighter() override = default; - - virtual void rehighlight(Palette const&) override; - void set_cell(Cell const* cell) { m_cell = cell; } - -private: - Cell const* m_cell { nullptr }; -}; - -} diff --git a/Userland/Applications/Spreadsheet/CellType/Date.cpp b/Userland/Applications/Spreadsheet/CellType/Date.cpp deleted file mode 100644 index d74898db339..00000000000 --- a/Userland/Applications/Spreadsheet/CellType/Date.cpp +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright (c) 2020-2022, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "Date.h" -#include "../Cell.h" -#include "../Spreadsheet.h" -#include -#include - -namespace Spreadsheet { - -DateCell::DateCell() - : CellType("Date"sv) -{ -} - -JS::ThrowCompletionOr DateCell::display(Cell& cell, CellTypeMetadata const& metadata) const -{ - return propagate_failure(cell, [&]() -> JS::ThrowCompletionOr { - auto& vm = cell.sheet().global_object().vm(); - auto timestamp = TRY(js_value(cell, metadata)); - auto string = Core::DateTime::from_timestamp(TRY(timestamp.to_i32(vm))).to_byte_string(metadata.format.is_empty() ? "%Y-%m-%d %H:%M:%S"sv : metadata.format.view()); - - if (metadata.length >= 0) - return string.substring(0, metadata.length); - - return string; - }); -} - -JS::ThrowCompletionOr DateCell::js_value(Cell& cell, CellTypeMetadata const&) const -{ - auto& vm = cell.sheet().global_object().vm(); - auto js_data = cell.js_data(); - auto value = TRY(js_data.to_double(vm)); - return JS::Value(value / 1000); // Turn it to seconds -} - -String DateCell::metadata_hint(MetadataName metadata) const -{ - if (metadata == MetadataName::Format) - return "Date format string as supported by `strftime'"_string; - return {}; -} - -} diff --git a/Userland/Applications/Spreadsheet/CellType/Date.h b/Userland/Applications/Spreadsheet/CellType/Date.h deleted file mode 100644 index 72ab9888238..00000000000 --- a/Userland/Applications/Spreadsheet/CellType/Date.h +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright (c) 2020-2022, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include "Numeric.h" -#include "Type.h" - -namespace Spreadsheet { - -class DateCell : public CellType { - -public: - DateCell(); - virtual ~DateCell() override = default; - virtual JS::ThrowCompletionOr display(Cell&, CellTypeMetadata const&) const override; - virtual JS::ThrowCompletionOr js_value(Cell&, CellTypeMetadata const&) const override; - virtual String metadata_hint(MetadataName) const override; -}; - -} diff --git a/Userland/Applications/Spreadsheet/CellType/Format.cpp b/Userland/Applications/Spreadsheet/CellType/Format.cpp deleted file mode 100644 index 431254d6f5f..00000000000 --- a/Userland/Applications/Spreadsheet/CellType/Format.cpp +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright (c) 2020, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "Format.h" -#include -#include -#include - -namespace Spreadsheet { - -template -struct SingleEntryListNext { - ALWAYS_INLINE T operator()(V value) const - { - return (T)value; - } -}; - -template typename NextArgument, typename CharType> -struct PrintfImpl : public PrintfImplementation::PrintfImpl { - ALWAYS_INLINE PrintfImpl(PutChFunc& putch, char*& bufptr, int const& nwritten) - : PrintfImplementation::PrintfImpl(putch, bufptr, nwritten) - { - } - - // Disallow pointer formats. - ALWAYS_INLINE int format_n(PrintfImplementation::ModifierState const&, ArgumentListRefT&) const - { - return 0; - } - ALWAYS_INLINE int format_s(PrintfImplementation::ModifierState const&, ArgumentListRefT&) const - { - return 0; - } -}; - -ByteString format_double(char const* format, double value) -{ - StringBuilder builder; - auto putch = [&](auto, auto ch) { builder.append(ch); }; - printf_internal(putch, nullptr, format, value); - - return builder.to_byte_string(); -} - -} diff --git a/Userland/Applications/Spreadsheet/CellType/Format.h b/Userland/Applications/Spreadsheet/CellType/Format.h deleted file mode 100644 index b9776a27e04..00000000000 --- a/Userland/Applications/Spreadsheet/CellType/Format.h +++ /dev/null @@ -1,15 +0,0 @@ -/* - * Copyright (c) 2020, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include - -namespace Spreadsheet { - -ByteString format_double(char const* format, double value); - -} diff --git a/Userland/Applications/Spreadsheet/CellType/Identity.cpp b/Userland/Applications/Spreadsheet/CellType/Identity.cpp deleted file mode 100644 index 1d7c9820525..00000000000 --- a/Userland/Applications/Spreadsheet/CellType/Identity.cpp +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright (c) 2020-2022, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "Identity.h" -#include "../Cell.h" -#include "../Spreadsheet.h" - -namespace Spreadsheet { - -IdentityCell::IdentityCell() - : CellType("Identity"sv) -{ -} - -JS::ThrowCompletionOr IdentityCell::display(Cell& cell, CellTypeMetadata const& metadata) const -{ - auto& vm = cell.sheet().global_object().vm(); - auto data = cell.js_data(); - if (!metadata.format.is_empty()) - data = TRY(cell.sheet().evaluate(metadata.format, &cell)); - - return data.to_byte_string(vm); -} - -JS::ThrowCompletionOr IdentityCell::js_value(Cell& cell, CellTypeMetadata const&) const -{ - return cell.js_data(); -} - -String IdentityCell::metadata_hint(MetadataName metadata) const -{ - if (metadata == MetadataName::Length) - return "Ignored"_string; - if (metadata == MetadataName::Format) - return "JavaScript expression, `value' refers to the cell's value"_string; - - return {}; -} - -} diff --git a/Userland/Applications/Spreadsheet/CellType/Identity.h b/Userland/Applications/Spreadsheet/CellType/Identity.h deleted file mode 100644 index fcfd3707c46..00000000000 --- a/Userland/Applications/Spreadsheet/CellType/Identity.h +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright (c) 2020-2022, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include "Type.h" - -namespace Spreadsheet { - -class IdentityCell : public CellType { - -public: - IdentityCell(); - virtual ~IdentityCell() override = default; - virtual JS::ThrowCompletionOr display(Cell&, CellTypeMetadata const&) const override; - virtual JS::ThrowCompletionOr js_value(Cell&, CellTypeMetadata const&) const override; - virtual String metadata_hint(MetadataName) const override; -}; - -} diff --git a/Userland/Applications/Spreadsheet/CellType/Numeric.cpp b/Userland/Applications/Spreadsheet/CellType/Numeric.cpp deleted file mode 100644 index ed85b9c8604..00000000000 --- a/Userland/Applications/Spreadsheet/CellType/Numeric.cpp +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright (c) 2020-2022, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "Numeric.h" -#include "../Cell.h" -#include "../Spreadsheet.h" -#include "Format.h" -#include -#include - -namespace Spreadsheet { - -NumericCell::NumericCell() - : CellType("Numeric"sv) -{ -} - -JS::ThrowCompletionOr NumericCell::display(Cell& cell, CellTypeMetadata const& metadata) const -{ - return propagate_failure(cell, [&]() -> JS::ThrowCompletionOr { - auto& vm = cell.sheet().global_object().vm(); - auto value = TRY(js_value(cell, metadata)); - ByteString string; - if (metadata.format.is_empty()) - string = TRY(value.to_byte_string(vm)); - else - string = format_double(metadata.format.characters(), TRY(value.to_double(vm))); - - if (metadata.length >= 0) - return string.substring(0, min(string.length(), metadata.length)); - - return string; - }); -} - -JS::ThrowCompletionOr NumericCell::js_value(Cell& cell, CellTypeMetadata const&) const -{ - return propagate_failure(cell, [&]() { - auto& vm = cell.sheet().global_object().vm(); - return cell.js_data().to_number(vm); - }); -} - -String NumericCell::metadata_hint(MetadataName metadata) const -{ - if (metadata == MetadataName::Format) - return "Format string as accepted by `printf', all numeric formats refer to the same value (the cell's value)"_string; - - return {}; -} - -} diff --git a/Userland/Applications/Spreadsheet/CellType/Numeric.h b/Userland/Applications/Spreadsheet/CellType/Numeric.h deleted file mode 100644 index c8480ea9d5c..00000000000 --- a/Userland/Applications/Spreadsheet/CellType/Numeric.h +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright (c) 2020-2022, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include "../Cell.h" -#include "Type.h" - -namespace Spreadsheet { - -template -static auto propagate_failure(Cell& cell, Callable&& steps) -{ - auto result_or_error = steps(); - if (result_or_error.is_error()) - cell.set_thrown_value(*result_or_error.throw_completion().value()); - - return result_or_error; -} - -class NumericCell : public CellType { - -public: - NumericCell(); - virtual ~NumericCell() override = default; - virtual JS::ThrowCompletionOr display(Cell&, CellTypeMetadata const&) const override; - virtual JS::ThrowCompletionOr js_value(Cell&, CellTypeMetadata const&) const override; - virtual String metadata_hint(MetadataName) const override; -}; - -} diff --git a/Userland/Applications/Spreadsheet/CellType/String.cpp b/Userland/Applications/Spreadsheet/CellType/String.cpp deleted file mode 100644 index c0a9ff67bf4..00000000000 --- a/Userland/Applications/Spreadsheet/CellType/String.cpp +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright (c) 2020-2022, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "String.h" -#include "../Cell.h" -#include "../Spreadsheet.h" - -namespace Spreadsheet { - -StringCell::StringCell() - : CellType("String"sv) -{ -} - -JS::ThrowCompletionOr StringCell::display(Cell& cell, CellTypeMetadata const& metadata) const -{ - auto& vm = cell.sheet().global_object().vm(); - auto string = TRY(cell.js_data().to_byte_string(vm)); - if (metadata.length >= 0) - return string.substring(0, metadata.length); - - return string; -} - -JS::ThrowCompletionOr StringCell::js_value(Cell& cell, CellTypeMetadata const& metadata) const -{ - auto& vm = cell.sheet().vm(); - auto string = TRY(display(cell, metadata)); - return JS::PrimitiveString::create(vm, string); -} - -String StringCell::metadata_hint(MetadataName metadata) const -{ - if (metadata == MetadataName::Format) - return "Ignored"_string; - return {}; -} - -} diff --git a/Userland/Applications/Spreadsheet/CellType/String.h b/Userland/Applications/Spreadsheet/CellType/String.h deleted file mode 100644 index 7512207b431..00000000000 --- a/Userland/Applications/Spreadsheet/CellType/String.h +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright (c) 2020-2022, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include "Type.h" - -namespace Spreadsheet { - -class StringCell : public CellType { - -public: - StringCell(); - virtual ~StringCell() override = default; - virtual JS::ThrowCompletionOr display(Cell&, CellTypeMetadata const&) const override; - virtual JS::ThrowCompletionOr js_value(Cell&, CellTypeMetadata const&) const override; - virtual String metadata_hint(MetadataName) const override; -}; - -} diff --git a/Userland/Applications/Spreadsheet/CellType/Type.cpp b/Userland/Applications/Spreadsheet/CellType/Type.cpp deleted file mode 100644 index 903b0bb3046..00000000000 --- a/Userland/Applications/Spreadsheet/CellType/Type.cpp +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright (c) 2020, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "Type.h" -#include "Date.h" -#include "Identity.h" -#include "Numeric.h" -#include "String.h" -#include -#include - -static HashMap s_cell_types; -static Spreadsheet::StringCell s_string_cell; -static Spreadsheet::NumericCell s_numeric_cell; -static Spreadsheet::IdentityCell s_identity_cell; -static Spreadsheet::DateCell s_date_cell; - -namespace Spreadsheet { - -CellType const* CellType::get_by_name(StringView name) -{ - return s_cell_types.get(name).value_or(nullptr); -} - -Vector CellType::names() -{ - Vector names; - for (auto& it : s_cell_types) - names.append(it.key); - return names; -} - -CellType::CellType(StringView name) - : m_name(name) -{ - VERIFY(!s_cell_types.contains(name)); - s_cell_types.set(name, this); -} - -} diff --git a/Userland/Applications/Spreadsheet/CellType/Type.h b/Userland/Applications/Spreadsheet/CellType/Type.h deleted file mode 100644 index deefb6f9d89..00000000000 --- a/Userland/Applications/Spreadsheet/CellType/Type.h +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright (c) 2020-2022, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include "../ConditionalFormatting.h" -#include "../Forward.h" -#include -#include -#include -#include -#include - -namespace Spreadsheet { - -struct CellTypeMetadata { - int length { -1 }; - ByteString format; - Gfx::TextAlignment alignment { Gfx::TextAlignment::CenterRight }; - Format static_format; -}; - -enum class MetadataName { - Length, - Format, - Alignment, - StaticFormat, -}; - -class CellType { -public: - static CellType const* get_by_name(StringView); - static Vector names(); - - virtual JS::ThrowCompletionOr display(Cell&, CellTypeMetadata const&) const = 0; - virtual JS::ThrowCompletionOr js_value(Cell&, CellTypeMetadata const&) const = 0; - virtual String metadata_hint(MetadataName) const { return {}; } - virtual ~CellType() = default; - - ByteString const& name() const { return m_name; } - -protected: - CellType(StringView name); - -private: - ByteString m_name; -}; - -} diff --git a/Userland/Applications/Spreadsheet/CellTypeDialog.cpp b/Userland/Applications/Spreadsheet/CellTypeDialog.cpp deleted file mode 100644 index 83b141858ba..00000000000 --- a/Userland/Applications/Spreadsheet/CellTypeDialog.cpp +++ /dev/null @@ -1,466 +0,0 @@ -/* - * Copyright (c) 2020, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "CellTypeDialog.h" -#include "Cell.h" -#include "Spreadsheet.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -REGISTER_WIDGET(Spreadsheet, ConditionsView); - -namespace Spreadsheet { - -CellTypeDialog::CellTypeDialog(Vector const& positions, Sheet& sheet, GUI::Window* parent) - : GUI::Dialog(parent) -{ - VERIFY(!positions.is_empty()); - - StringBuilder builder; - - if (positions.size() == 1) - builder.appendff("Format cell {}", positions.first().to_cell_identifier(sheet)); - else - builder.appendff("Format {} cells", positions.size()); - - set_title(builder.string_view()); - set_icon(parent->icon()); - resize(285, 360); - - auto main_widget = set_main_widget(); - main_widget->set_layout(4); - main_widget->set_fill_with_background_color(true); - - auto& tab_widget = main_widget->add(); - setup_tabs(tab_widget, positions, sheet); - - auto& buttonbox = main_widget->add(); - buttonbox.set_shrink_to_fit(true); - buttonbox.set_layout(GUI::Margins {}, 10); - buttonbox.add_spacer(); - auto& ok_button = buttonbox.add("OK"_string); - ok_button.set_fixed_width(80); - ok_button.on_click = [&](auto) { done(ExecResult::OK); }; -} - -Vector const g_horizontal_alignments { "Left", "Center", "Right" }; -Vector const g_vertical_alignments { "Top", "Center", "Bottom" }; -Vector g_types; - -constexpr static CellTypeDialog::VerticalAlignment vertical_alignment_from(Gfx::TextAlignment alignment) -{ - switch (alignment) { - case Gfx::TextAlignment::CenterRight: - case Gfx::TextAlignment::CenterLeft: - case Gfx::TextAlignment::Center: - return CellTypeDialog::VerticalAlignment::Center; - - case Gfx::TextAlignment::TopCenter: - case Gfx::TextAlignment::TopRight: - case Gfx::TextAlignment::TopLeft: - return CellTypeDialog::VerticalAlignment::Top; - - case Gfx::TextAlignment::BottomCenter: - case Gfx::TextAlignment::BottomLeft: - case Gfx::TextAlignment::BottomRight: - return CellTypeDialog::VerticalAlignment::Bottom; - } - - return CellTypeDialog::VerticalAlignment::Center; -} - -constexpr static CellTypeDialog::HorizontalAlignment horizontal_alignment_from(Gfx::TextAlignment alignment) -{ - switch (alignment) { - case Gfx::TextAlignment::BottomCenter: - case Gfx::TextAlignment::Center: - case Gfx::TextAlignment::TopCenter: - return CellTypeDialog::HorizontalAlignment::Center; - - case Gfx::TextAlignment::TopRight: - case Gfx::TextAlignment::CenterRight: - case Gfx::TextAlignment::BottomRight: - return CellTypeDialog::HorizontalAlignment::Right; - - case Gfx::TextAlignment::TopLeft: - case Gfx::TextAlignment::CenterLeft: - case Gfx::TextAlignment::BottomLeft: - return CellTypeDialog::HorizontalAlignment::Left; - } - - return CellTypeDialog::HorizontalAlignment::Right; -} - -void CellTypeDialog::setup_tabs(GUI::TabWidget& tabs, Vector const& positions, Sheet& sheet) -{ - g_types.clear(); - for (auto& type_name : CellType::names()) - g_types.append(type_name); - - Vector cells; - for (auto& position : positions) { - if (auto cell = sheet.at(position)) - cells.append(*cell); - } - - if (cells.size() == 1) { - auto& cell = cells.first(); - m_format = cell.type_metadata().format; - m_length = cell.type_metadata().length; - m_type = &cell.type(); - m_vertical_alignment = vertical_alignment_from(cell.type_metadata().alignment); - m_horizontal_alignment = horizontal_alignment_from(cell.type_metadata().alignment); - m_static_format = cell.type_metadata().static_format; - m_conditional_formats = cell.conditional_formats(); - } - - auto& type_tab = tabs.add_tab("Type"_string); - type_tab.set_layout(4); - { - auto& left_side = type_tab.add(); - left_side.set_layout(); - auto& right_side = type_tab.add(); - right_side.set_layout(); - right_side.set_fixed_width(170); - - auto& type_list = left_side.add(); - type_list.set_model(*GUI::ItemListModel::create(g_types)); - type_list.set_should_hide_unnecessary_scrollbars(true); - type_list.on_selection_change = [&] { - auto const& index = type_list.selection().first(); - if (!index.is_valid()) { - m_type = nullptr; - return; - } - - m_type = CellType::get_by_name(g_types.at(index.row())); - if (auto* editor = right_side.find_descendant_of_type_named("format_editor")) - editor->set_tooltip(m_type->metadata_hint(MetadataName::Format)); - }; - - { - auto& checkbox = right_side.add("Override max length"_string); - auto& spinbox = right_side.add(); - checkbox.set_checked(m_length != -1); - spinbox.set_min(0); - spinbox.set_enabled(m_length != -1); - if (m_length > -1) - spinbox.set_value(m_length); - - checkbox.on_checked = [&](auto checked) { - spinbox.set_enabled(checked); - if (!checked) { - m_length = -1; - spinbox.set_value(0); - } - }; - spinbox.on_change = [&](auto value) { - m_length = value; - }; - } - { - auto& checkbox = right_side.add("Override display format"_string); - auto& editor = right_side.add(); - checkbox.set_checked(!m_format.is_empty()); - editor.set_name("format_editor"); - editor.set_should_hide_unnecessary_scrollbars(true); - editor.set_enabled(!m_format.is_empty()); - editor.set_text(m_format); - - checkbox.on_checked = [&](auto checked) { - editor.set_enabled(checked); - if (!checked) - m_format = ByteString::empty(); - editor.set_text(m_format); - }; - editor.on_change = [&] { - m_format = editor.text(); - }; - } - } - - auto& alignment_tab = tabs.add_tab("Alignment"_string); - alignment_tab.set_layout(4); - { - // FIXME: Frame? - // Horizontal alignment - { - auto& horizontal_alignment_selection_container = alignment_tab.add(); - horizontal_alignment_selection_container.set_layout(GUI::Margins { 4, 0, 0 }); - horizontal_alignment_selection_container.set_fixed_height(22); - - auto& horizontal_alignment_label = horizontal_alignment_selection_container.add(); - horizontal_alignment_label.set_text_alignment(Gfx::TextAlignment::CenterLeft); - horizontal_alignment_label.set_text("Horizontal text alignment"_string); - - auto& horizontal_combobox = alignment_tab.add(); - horizontal_combobox.set_only_allow_values_from_model(true); - horizontal_combobox.set_model(*GUI::ItemListModel::create(g_horizontal_alignments)); - horizontal_combobox.set_selected_index((int)m_horizontal_alignment); - horizontal_combobox.on_change = [&](auto&, const GUI::ModelIndex& index) { - switch (index.row()) { - case 0: - m_horizontal_alignment = HorizontalAlignment::Left; - break; - case 1: - m_horizontal_alignment = HorizontalAlignment::Center; - break; - case 2: - m_horizontal_alignment = HorizontalAlignment::Right; - break; - default: - VERIFY_NOT_REACHED(); - } - }; - } - - // Vertical alignment - { - auto& vertical_alignment_container = alignment_tab.add(); - vertical_alignment_container.set_layout(GUI::Margins { 4, 0, 0 }); - vertical_alignment_container.set_fixed_height(22); - - auto& vertical_alignment_label = vertical_alignment_container.add(); - vertical_alignment_label.set_text_alignment(Gfx::TextAlignment::CenterLeft); - vertical_alignment_label.set_text("Vertical text alignment"_string); - - auto& vertical_combobox = alignment_tab.add(); - vertical_combobox.set_only_allow_values_from_model(true); - vertical_combobox.set_model(*GUI::ItemListModel::create(g_vertical_alignments)); - vertical_combobox.set_selected_index((int)m_vertical_alignment); - vertical_combobox.on_change = [&](auto&, const GUI::ModelIndex& index) { - switch (index.row()) { - case 0: - m_vertical_alignment = VerticalAlignment::Top; - break; - case 1: - m_vertical_alignment = VerticalAlignment::Center; - break; - case 2: - m_vertical_alignment = VerticalAlignment::Bottom; - break; - default: - VERIFY_NOT_REACHED(); - } - }; - } - } - - auto& colors_tab = tabs.add_tab("Color"_string); - colors_tab.set_layout(4); - { - // Static formatting - { - auto& static_formatting_container = colors_tab.add(); - static_formatting_container.set_layout(); - - // Foreground - { - // FIXME: Somehow allow unsetting these. - auto& foreground_container = static_formatting_container.add(); - foreground_container.set_layout(GUI::Margins { 4, 0, 0 }); - foreground_container.set_preferred_height(GUI::SpecialDimension::Fit); - - auto& foreground_label = foreground_container.add(); - foreground_label.set_text_alignment(Gfx::TextAlignment::CenterLeft); - foreground_label.set_text("Static foreground color"_string); - - auto& foreground_selector = foreground_container.add(); - if (m_static_format.foreground_color.has_value()) - foreground_selector.set_color(m_static_format.foreground_color.value()); - foreground_selector.on_change = [&]() { - m_static_format.foreground_color = foreground_selector.color(); - }; - } - - // Background - { - // FIXME: Somehow allow unsetting these. - auto& background_container = static_formatting_container.add(); - background_container.set_layout(GUI::Margins { 4, 0, 0 }); - background_container.set_preferred_height(GUI::SpecialDimension::Fit); - - auto& background_label = background_container.add(); - background_label.set_text_alignment(Gfx::TextAlignment::CenterLeft); - background_label.set_text("Static background color"_string); - - auto& background_selector = background_container.add(); - if (m_static_format.background_color.has_value()) - background_selector.set_color(m_static_format.background_color.value()); - background_selector.on_change = [&]() { - m_static_format.background_color = background_selector.color(); - }; - } - } - } - - auto& conditional_fmt_tab = tabs.add_tab("Conditional format"_string); - conditional_fmt_tab.load_from_gml(cond_fmt_gml).release_value_but_fixme_should_propagate_errors(); - { - auto& view = *conditional_fmt_tab.find_descendant_of_type_named("conditions_view"); - view.set_formats(&m_conditional_formats); - - auto& add_button = *conditional_fmt_tab.find_descendant_of_type_named("add_button"); - add_button.on_click = [&](auto) { - view.add_format(); - }; - - // FIXME: Disable this when empty. - auto& remove_button = *conditional_fmt_tab.find_descendant_of_type_named("remove_button"); - remove_button.on_click = [&](auto) { - view.remove_top(); - }; - } -} - -CellTypeMetadata CellTypeDialog::metadata() const -{ - CellTypeMetadata metadata; - metadata.format = m_format; - metadata.length = m_length; - metadata.static_format = m_static_format; - - switch (m_vertical_alignment) { - case VerticalAlignment::Top: - switch (m_horizontal_alignment) { - case HorizontalAlignment::Left: - metadata.alignment = Gfx::TextAlignment::TopLeft; - break; - case HorizontalAlignment::Center: - metadata.alignment = Gfx::TextAlignment::TopCenter; - break; - case HorizontalAlignment::Right: - metadata.alignment = Gfx::TextAlignment::TopRight; - break; - } - break; - case VerticalAlignment::Center: - switch (m_horizontal_alignment) { - case HorizontalAlignment::Left: - metadata.alignment = Gfx::TextAlignment::CenterLeft; - break; - case HorizontalAlignment::Center: - metadata.alignment = Gfx::TextAlignment::Center; - break; - case HorizontalAlignment::Right: - metadata.alignment = Gfx::TextAlignment::CenterRight; - break; - } - break; - case VerticalAlignment::Bottom: - switch (m_horizontal_alignment) { - case HorizontalAlignment::Left: - metadata.alignment = Gfx::TextAlignment::CenterLeft; // BottomLeft? - break; - case HorizontalAlignment::Center: - metadata.alignment = Gfx::TextAlignment::Center; - break; - case HorizontalAlignment::Right: - metadata.alignment = Gfx::TextAlignment::BottomRight; - break; - } - break; - } - - return metadata; -} - -ConditionView::ConditionView(ConditionalFormat& fmt) - : m_format(fmt) -{ - load_from_gml(cond_fmt_view_gml).release_value_but_fixme_should_propagate_errors(); - - auto& fg_input = *find_descendant_of_type_named("foreground_input"); - auto& bg_input = *find_descendant_of_type_named("background_input"); - auto& formula_editor = *find_descendant_of_type_named("formula_editor"); - - if (m_format.foreground_color.has_value()) - fg_input.set_color(m_format.foreground_color.value()); - - if (m_format.background_color.has_value()) - bg_input.set_color(m_format.background_color.value()); - - formula_editor.set_text(m_format.condition); - - // FIXME: Allow unsetting these. - fg_input.on_change = [&] { - m_format.foreground_color = fg_input.color(); - }; - - bg_input.on_change = [&] { - m_format.background_color = bg_input.color(); - }; - - formula_editor.set_syntax_highlighter(make()); - formula_editor.set_should_hide_unnecessary_scrollbars(true); - formula_editor.on_change = [&] { - m_format.condition = formula_editor.text(); - }; -} - -ConditionView::~ConditionView() -{ -} - -ConditionsView::ConditionsView() -{ - set_layout(6, 4); -} - -void ConditionsView::set_formats(Vector* formats) -{ - VERIFY(!m_formats); - - m_formats = formats; - - for (auto& entry : *m_formats) - m_widgets.append(add(entry)); -} - -void ConditionsView::add_format() -{ - VERIFY(m_formats); - - m_formats->empend(); - auto& last = m_formats->last(); - - m_widgets.append(add(last)); - - update(); -} - -void ConditionsView::remove_top() -{ - VERIFY(m_formats); - - if (m_formats->is_empty()) - return; - - m_formats->take_last(); - m_widgets.take_last()->remove_from_parent(); - update(); -} - -ConditionsView::~ConditionsView() -{ -} - -} diff --git a/Userland/Applications/Spreadsheet/CellTypeDialog.h b/Userland/Applications/Spreadsheet/CellTypeDialog.h deleted file mode 100644 index 8060924485a..00000000000 --- a/Userland/Applications/Spreadsheet/CellTypeDialog.h +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright (c) 2020, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include "CellType/Type.h" -#include "ConditionalFormatting.h" -#include "Forward.h" -#include - -namespace Spreadsheet { - -class CellTypeDialog : public GUI::Dialog { - C_OBJECT(CellTypeDialog); - -public: - CellTypeMetadata metadata() const; - CellType const* type() const { return m_type; } - Vector conditional_formats() { return m_conditional_formats; } - - enum class HorizontalAlignment : int { - Left = 0, - Center, - Right, - }; - enum class VerticalAlignment : int { - Top = 0, - Center, - Bottom, - }; - -private: - CellTypeDialog(Vector const&, Sheet&, GUI::Window* parent = nullptr); - void setup_tabs(GUI::TabWidget&, Vector const&, Sheet&); - - CellType const* m_type { nullptr }; - - int m_length { -1 }; - ByteString m_format; - HorizontalAlignment m_horizontal_alignment { HorizontalAlignment::Right }; - VerticalAlignment m_vertical_alignment { VerticalAlignment::Center }; - Format m_static_format; - Vector m_conditional_formats; -}; - -} diff --git a/Userland/Applications/Spreadsheet/CondFormatting.gml b/Userland/Applications/Spreadsheet/CondFormatting.gml deleted file mode 100644 index 59ca4f004e4..00000000000 --- a/Userland/Applications/Spreadsheet/CondFormatting.gml +++ /dev/null @@ -1,38 +0,0 @@ -@GUI::Widget { - name: "main" - fill_with_background_color: true - layout: @GUI::VerticalBoxLayout { - margins: [4] - spacing: 4 - } - - @GUI::ScrollableContainerWidget { - should_hide_unnecessary_scrollbars: true - content_widget: @Spreadsheet::ConditionsView { - name: "conditions_view" - } - } - - @GUI::Widget { - preferred_height: "fit" - layout: @GUI::HorizontalBoxLayout { - spacing: 10 - } - - @GUI::Layout::Spacer {} - - @GUI::Button { - name: "add_button" - text: "Add" - fixed_width: 70 - } - - @GUI::Button { - name: "remove_button" - text: "Remove" - fixed_width: 70 - } - - @GUI::Layout::Spacer {} - } -} diff --git a/Userland/Applications/Spreadsheet/CondView.gml b/Userland/Applications/Spreadsheet/CondView.gml deleted file mode 100644 index 41a0cdcc7f9..00000000000 --- a/Userland/Applications/Spreadsheet/CondView.gml +++ /dev/null @@ -1,49 +0,0 @@ -@GUI::Widget { - layout: @GUI::VerticalBoxLayout {} - preferred_height: "fit" - - @GUI::Widget { - preferred_height: "fit" - layout: @GUI::HorizontalBoxLayout {} - - @GUI::Label { - text: "if..." - fixed_width: 40 - } - - @GUI::TextEditor { - name: "formula_editor" - fixed_height: 32 - tooltip: "Use 'value' to refer to the current cell's value" - font_type: "FixedWidth" - } - } - - @GUI::Widget { - preferred_height: "fit" - layout: @GUI::HorizontalBoxLayout {} - - @GUI::Label { - text: "Foreground..." - fixed_width: 150 - } - - @GUI::ColorInput { - name: "foreground_input" - } - } - - @GUI::Widget { - preferred_height: "fit" - layout: @GUI::HorizontalBoxLayout {} - - @GUI::Label { - text: "Background..." - fixed_width: 150 - } - - @GUI::ColorInput { - name: "background_input" - } - } -} diff --git a/Userland/Applications/Spreadsheet/ConditionalFormatting.h b/Userland/Applications/Spreadsheet/ConditionalFormatting.h deleted file mode 100644 index 02ae9cd8554..00000000000 --- a/Userland/Applications/Spreadsheet/ConditionalFormatting.h +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright (c) 2020, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include "Forward.h" -#include -#include -#include - -namespace Spreadsheet { - -struct Format { - Optional foreground_color; - Optional background_color; -}; - -struct ConditionalFormat : public Format { - ByteString condition; -}; - -enum class FormatType { - Background = 0, - Foreground = 1 -}; - -class ConditionView : public GUI::Widget { - C_OBJECT(ConditionView) -public: - virtual ~ConditionView() override; - -private: - ConditionView(ConditionalFormat&); - - ConditionalFormat& m_format; -}; - -class ConditionsView : public GUI::Widget { - C_OBJECT(ConditionsView) -public: - virtual ~ConditionsView() override; - - void set_formats(Vector*); - - void add_format(); - void remove_top(); - -private: - ConditionsView(); - - Vector* m_formats { nullptr }; - Vector> m_widgets; -}; - -} diff --git a/Userland/Applications/Spreadsheet/ExportDialog.cpp b/Userland/Applications/Spreadsheet/ExportDialog.cpp deleted file mode 100644 index 66f566078b9..00000000000 --- a/Userland/Applications/Spreadsheet/ExportDialog.cpp +++ /dev/null @@ -1,247 +0,0 @@ -/* - * Copyright (c) 2020-2021, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "ExportDialog.h" -#include "Spreadsheet.h" -#include "Workbook.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -// This is defined in ImportDialog.cpp, we can't include it twice, since the generated symbol is exported. -extern StringView select_format_page_gml; - -namespace Spreadsheet { - -CSVExportDialogPage::CSVExportDialogPage(Sheet const& sheet) - : m_data(sheet.to_xsv()) -{ - m_headers.extend(m_data.take_first()); - - m_page = GUI::WizardPage::create( - "CSV Export Options"sv, - "Please select the options for the csv file you wish to export to"sv) - .release_value_but_fixme_should_propagate_errors(); - - m_page->body_widget().load_from_gml(csv_export_gml).release_value_but_fixme_should_propagate_errors(); - m_page->set_is_final_page(true); - - m_delimiter_comma_radio = m_page->body_widget().find_descendant_of_type_named("delimiter_comma_radio"); - m_delimiter_semicolon_radio = m_page->body_widget().find_descendant_of_type_named("delimiter_semicolon_radio"); - m_delimiter_tab_radio = m_page->body_widget().find_descendant_of_type_named("delimiter_tab_radio"); - m_delimiter_space_radio = m_page->body_widget().find_descendant_of_type_named("delimiter_space_radio"); - m_delimiter_other_radio = m_page->body_widget().find_descendant_of_type_named("delimiter_other_radio"); - m_delimiter_other_text_box = m_page->body_widget().find_descendant_of_type_named("delimiter_other_text_box"); - m_quote_single_radio = m_page->body_widget().find_descendant_of_type_named("quote_single_radio"); - m_quote_double_radio = m_page->body_widget().find_descendant_of_type_named("quote_double_radio"); - m_quote_other_radio = m_page->body_widget().find_descendant_of_type_named("quote_other_radio"); - m_quote_other_text_box = m_page->body_widget().find_descendant_of_type_named("quote_other_text_box"); - m_quote_escape_combo_box = m_page->body_widget().find_descendant_of_type_named("quote_escape_combo_box"); - m_export_header_check_box = m_page->body_widget().find_descendant_of_type_named("export_header_check_box"); - m_quote_all_fields_check_box = m_page->body_widget().find_descendant_of_type_named("quote_all_fields_check_box"); - m_data_preview_text_editor = m_page->body_widget().find_descendant_of_type_named("data_preview_text_editor"); - - m_data_preview_text_editor->set_should_hide_unnecessary_scrollbars(true); - - m_quote_escape_combo_box->set_model(GUI::ItemListModel::create(m_quote_escape_items)); - - // By default, use commas, double quotes with repeat, disable headers, and quote only the fields that require quoting. - m_delimiter_comma_radio->set_checked(true); - m_quote_double_radio->set_checked(true); - m_quote_escape_combo_box->set_selected_index(0); // Repeat - - m_delimiter_comma_radio->on_checked = [&](auto) { update_preview(); }; - m_delimiter_semicolon_radio->on_checked = [&](auto) { update_preview(); }; - m_delimiter_tab_radio->on_checked = [&](auto) { update_preview(); }; - m_delimiter_space_radio->on_checked = [&](auto) { update_preview(); }; - m_delimiter_other_radio->on_checked = [&](auto) { update_preview(); }; - m_delimiter_other_text_box->on_change = [&] { - if (m_delimiter_other_radio->is_checked()) - update_preview(); - }; - m_quote_single_radio->on_checked = [&](auto) { update_preview(); }; - m_quote_double_radio->on_checked = [&](auto) { update_preview(); }; - m_quote_other_radio->on_checked = [&](auto) { update_preview(); }; - m_quote_other_text_box->on_change = [&] { - if (m_quote_other_radio->is_checked()) - update_preview(); - }; - m_quote_escape_combo_box->on_change = [&](auto&, auto&) { update_preview(); }; - m_export_header_check_box->on_checked = [&](auto) { update_preview(); }; - m_quote_all_fields_check_box->on_checked = [&](auto) { update_preview(); }; - - update_preview(); -} - -auto CSVExportDialogPage::generate(Stream& stream, GenerationType type) -> ErrorOr -{ - auto delimiter = TRY([this]() -> ErrorOr { - if (m_delimiter_other_radio->is_checked()) { - if (m_delimiter_other_text_box->text().is_empty()) - return Error::from_string_literal("Delimiter unset"); - return m_delimiter_other_text_box->text(); - } - if (m_delimiter_comma_radio->is_checked()) - return ","; - if (m_delimiter_semicolon_radio->is_checked()) - return ";"; - if (m_delimiter_tab_radio->is_checked()) - return "\t"; - if (m_delimiter_space_radio->is_checked()) - return " "; - return Error::from_string_literal("Delimiter unset"); - }()); - - auto quote = TRY([this]() -> ErrorOr { - if (m_quote_other_radio->is_checked()) { - if (m_quote_other_text_box->text().is_empty()) - return Error::from_string_literal("Quote separator unset"); - return m_quote_other_text_box->text(); - } - if (m_quote_single_radio->is_checked()) - return "'"; - if (m_quote_double_radio->is_checked()) - return "\""; - return Error::from_string_literal("Quote separator unset"); - }()); - - auto quote_escape = [this]() { - auto index = m_quote_escape_combo_box->selected_index(); - if (index == 0) - return Writer::WriterTraits::Repeat; - if (index == 1) - return Writer::WriterTraits::Backslash; - VERIFY_NOT_REACHED(); - }(); - - auto should_export_headers = m_export_header_check_box->is_checked(); - auto should_quote_all_fields = m_quote_all_fields_check_box->is_checked(); - - Writer::WriterTraits traits { - move(delimiter), - move(quote), - quote_escape, - }; - - auto behaviors = Writer::default_behaviors(); - Vector empty_headers; - auto* headers = &empty_headers; - - if (should_export_headers) { - behaviors = behaviors | Writer::WriterBehavior::WriteHeaders; - headers = &m_headers; - } - - if (should_quote_all_fields) - behaviors = behaviors | Writer::WriterBehavior::QuoteAll; - - switch (type) { - case GenerationType::Normal: - TRY((Writer::XSV>::generate(stream, m_data, move(traits), *headers, behaviors))); - break; - case GenerationType::Preview: - TRY((Writer::XSV::generate_preview(stream, m_data, move(traits), *headers, behaviors))); - break; - default: - VERIFY_NOT_REACHED(); - } - - return {}; -} - -void CSVExportDialogPage::update_preview() -{ - auto maybe_error = [this]() -> ErrorOr { - AllocatingMemoryStream memory_stream; - TRY(generate(memory_stream, GenerationType::Preview)); - auto buffer = TRY(memory_stream.read_until_eof()); - m_data_preview_text_editor->set_text(StringView(buffer)); - m_data_preview_text_editor->update(); - return {}; - }(); - if (maybe_error.is_error()) - m_data_preview_text_editor->set_text(ByteString::formatted("Cannot update preview: {}", maybe_error.error())); -} - -ErrorOr ExportDialog::make_and_run_for(StringView mime, Core::File& file, ByteString filename, Workbook& workbook) -{ - auto wizard = TRY(GUI::WizardDialog::create(GUI::Application::the()->active_window())); - wizard->set_title("File Export Wizard"); - wizard->set_icon(GUI::Icon::default_icon("app-spreadsheet"sv).bitmap_for_size(16)); - - auto export_xsv = [&]() -> ErrorOr { - // FIXME: Prompt for the user to select a specific sheet to export - // For now, export the first sheet (if available) - if (!workbook.has_sheets()) - return Error::from_string_literal("The workbook has no sheets to export!"); - - CSVExportDialogPage page { workbook.sheets().first() }; - wizard->replace_page(page.page()); - if (wizard->exec() != GUI::Dialog::ExecResult::OK) - return Error::from_string_literal("CSV Export was cancelled"); - - TRY(page.generate(file, CSVExportDialogPage::GenerationType::Normal)); - return {}; - }; - - auto export_worksheet = [&]() -> ErrorOr { - JsonArray array; - for (auto& sheet : workbook.sheets()) - array.must_append(sheet->to_json()); - - auto file_content = array.to_byte_string(); - return file.write_until_depleted(file_content.bytes()); - }; - - if (mime == "text/csv") { - return export_xsv(); - } else if (mime == "application/x-sheets+json") { - return export_worksheet(); - } else { - auto page = TRY(GUI::WizardPage::create( - "Export File Format"sv, - TRY(String::formatted("Select the format you wish to export to '{}' as", LexicalPath::basename(filename))))); - - page->on_next_page = [] { return nullptr; }; - - TRY(page->body_widget().load_from_gml(select_format_page_gml)); - auto format_combo_box = page->body_widget().find_descendant_of_type_named("select_format_page_format_combo_box"); - - Vector supported_formats { - "CSV (text/csv)", - "Spreadsheet Worksheet", - }; - format_combo_box->set_model(GUI::ItemListModel::create(supported_formats)); - - wizard->push_page(page); - - if (wizard->exec() != GUI::Dialog::ExecResult::OK) - return Error::from_string_literal("Export was cancelled"); - - if (format_combo_box->selected_index() == 0) - return export_xsv(); - - if (format_combo_box->selected_index() == 1) - return export_worksheet(); - - VERIFY_NOT_REACHED(); - } -} - -}; diff --git a/Userland/Applications/Spreadsheet/ExportDialog.h b/Userland/Applications/Spreadsheet/ExportDialog.h deleted file mode 100644 index 56424a8c69d..00000000000 --- a/Userland/Applications/Spreadsheet/ExportDialog.h +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright (c) 2020-2021, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include "Writers/XSV.h" -#include -#include -#include -#include - -namespace Spreadsheet { - -class Sheet; -class Workbook; - -struct CSVExportDialogPage { - explicit CSVExportDialogPage(Sheet const&); - - NonnullRefPtr page() { return *m_page; } - - enum class GenerationType { - Normal, - Preview - }; - - ErrorOr generate(Stream&, GenerationType); - -protected: - void update_preview(); - -private: - Vector> m_data; - Vector m_headers; - RefPtr m_page; - RefPtr m_delimiter_comma_radio; - RefPtr m_delimiter_semicolon_radio; - RefPtr m_delimiter_tab_radio; - RefPtr m_delimiter_space_radio; - RefPtr m_delimiter_other_radio; - RefPtr m_delimiter_other_text_box; - RefPtr m_quote_single_radio; - RefPtr m_quote_double_radio; - RefPtr m_quote_other_radio; - RefPtr m_quote_other_text_box; - RefPtr m_quote_escape_combo_box; - RefPtr m_export_header_check_box; - RefPtr m_quote_all_fields_check_box; - RefPtr m_data_preview_text_editor; - Vector m_quote_escape_items { - // Note: Keep in sync with Writer::WriterTraits::QuoteEscape. - "Repeat", - "Backslash", - }; -}; - -struct ExportDialog { - static ErrorOr make_and_run_for(StringView mime, Core::File&, ByteString filename, Workbook&); -}; - -} diff --git a/Userland/Applications/Spreadsheet/Forward.h b/Userland/Applications/Spreadsheet/Forward.h deleted file mode 100644 index 5441055b298..00000000000 --- a/Userland/Applications/Spreadsheet/Forward.h +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright (c) 2020, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -namespace Spreadsheet { - -class ConditionView; -class Sheet; -class SheetGlobalObject; -class Workbook; -class WorkbookObject; -struct Cell; -struct ConditionalFormat; -struct Format; -struct Position; - -} diff --git a/Userland/Applications/Spreadsheet/HelpWindow.cpp b/Userland/Applications/Spreadsheet/HelpWindow.cpp deleted file mode 100644 index 6099b70dc6a..00000000000 --- a/Userland/Applications/Spreadsheet/HelpWindow.cpp +++ /dev/null @@ -1,206 +0,0 @@ -/* - * Copyright (c) 2020-2022, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "HelpWindow.h" -#include "SpreadsheetWidget.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace Spreadsheet { - -class HelpListModel final : public GUI::Model { -public: - static NonnullRefPtr create() { return adopt_ref(*new HelpListModel); } - - virtual ~HelpListModel() override = default; - - virtual int row_count(const GUI::ModelIndex& = GUI::ModelIndex()) const override { return m_keys.size(); } - virtual int column_count(const GUI::ModelIndex& = GUI::ModelIndex()) const override { return 1; } - - virtual GUI::Variant data(const GUI::ModelIndex& index, GUI::ModelRole role = GUI::ModelRole::Display) const override - { - if (role == GUI::ModelRole::Display) { - return key(index); - } - - return {}; - } - - ByteString key(const GUI::ModelIndex& index) const { return m_keys[index.row()]; } - - void set_from(JsonObject const& object) - { - m_keys.clear(); - object.for_each_member([this](auto& name, auto&) { - m_keys.append(name); - }); - AK::quick_sort(m_keys); - invalidate(); - } - -private: - HelpListModel() - { - } - - Vector m_keys; -}; - -RefPtr HelpWindow::s_the { nullptr }; - -HelpWindow::HelpWindow(GUI::Window* parent) - : GUI::Window(parent) -{ - resize(530, 365); - set_title("Spreadsheet Functions Help"); - set_icon(Gfx::Bitmap::load_from_file("/res/icons/16x16/app-help.png"sv).release_value_but_fixme_should_propagate_errors()); - set_window_mode(GUI::WindowMode::Modeless); - - auto widget = set_main_widget(); - widget->set_layout(); - widget->set_fill_with_background_color(true); - - auto& splitter = widget->add(); - auto& left_frame = splitter.add(); - left_frame.set_layout(); - // FIXME: Get rid of the magic number, dynamically calculate initial size based on left frame contents - left_frame.set_preferred_width(100); - m_listview = left_frame.add(); - m_listview->set_activates_on_selection(true); - m_listview->set_model(HelpListModel::create()); - - m_webview = splitter.add(); - m_webview->use_native_user_style_sheet(); - m_webview->on_link_click = [this](auto& url, auto&, auto&&) { - VERIFY(url.scheme() == "spreadsheet"); - if (url.host().template has() && url.host().template get() == "example"sv) { - auto example_path = url.serialize_path(); - auto entry = LexicalPath::basename(example_path); - auto doc_option = m_docs.get_object(entry); - if (!doc_option.has_value()) { - GUI::MessageBox::show_error(this, ByteString::formatted("No documentation entry found for '{}'", example_path)); - return; - } - auto& doc = doc_option.value(); - auto name = url.fragment().value_or(String {}); - - auto maybe_example_data = doc.get_object("example_data"sv); - if (!maybe_example_data.has_value()) { - GUI::MessageBox::show_error(this, ByteString::formatted("No example data found for '{}'", example_path)); - return; - } - auto& example_data = maybe_example_data.value(); - - if (!example_data.has_object(name)) { - GUI::MessageBox::show_error(this, ByteString::formatted("Example '{}' not found for '{}'", name, example_path)); - return; - } - auto& value = example_data.get_object(name).value(); - - auto window = GUI::Window::construct(this); - window->resize(size()); - window->set_icon(icon()); - window->set_title(ByteString::formatted("Spreadsheet Help - Example {} for {}", name, entry)); - window->on_close = [window = window.ptr()] { window->remove_from_parent(); }; - - auto widget = window->set_main_widget(window, Vector> {}, false); - auto sheet = Sheet::from_json(value, widget->workbook()); - if (!sheet) { - GUI::MessageBox::show_error(this, ByteString::formatted("Corrupted example '{}' in '{}'", name, example_path)); - return; - } - - widget->add_sheet(sheet.release_nonnull()); - window->show(); - } else if (url.host() == "doc"_string) { - auto entry = LexicalPath::basename(url.serialize_path()); - m_webview->load(URL::create_with_data("text/html"sv, render(entry))); - } else { - dbgln("Invalid spreadsheet action domain '{}'", url.serialized_host().release_value_but_fixme_should_propagate_errors()); - } - }; - - m_listview->on_activation = [this](auto& index) { - if (!m_webview) - return; - - auto key = static_cast(m_listview->model())->key(index); - m_webview->load(URL::create_with_data("text/html"sv, render(key))); - }; -} - -ByteString HelpWindow::render(StringView key) -{ - VERIFY(m_docs.has_object(key)); - auto& doc = m_docs.get_object(key).value(); - - auto name = doc.get_byte_string("name"sv).value_or({}); - auto argc = doc.get_u32("argc"sv).value_or(0); - VERIFY(doc.has_array("argnames"sv)); - auto& argnames = doc.get_array("argnames"sv).value(); - - auto docstring = doc.get_byte_string("doc"sv).value_or({}); - - StringBuilder markdown_builder; - - markdown_builder.append("# NAME\n`"sv); - markdown_builder.append(name); - markdown_builder.append("`\n\n"sv); - - markdown_builder.append("# ARGUMENTS\n"sv); - if (argc > 0) - markdown_builder.appendff("{} required argument(s):\n", argc); - else - markdown_builder.append("No required arguments.\n"sv); - - for (size_t i = 0; i < argc; ++i) - markdown_builder.appendff("- `{}`\n", argnames.at(i).as_string()); - - if (argc > 0) - markdown_builder.append("\n"sv); - - if ((size_t)argnames.size() > argc) { - auto opt_count = argnames.size() - argc; - markdown_builder.appendff("{} optional argument(s):\n", opt_count); - for (size_t i = argc; i < (size_t)argnames.size(); ++i) - markdown_builder.appendff("- `{}`\n", argnames.at(i).as_string()); - markdown_builder.append("\n"sv); - } - - markdown_builder.append("# DESCRIPTION\n"sv); - markdown_builder.append(docstring); - markdown_builder.append("\n\n"sv); - - if (doc.has("examples"sv)) { - auto examples = doc.get_object("examples"sv); - VERIFY(examples.has_value()); - markdown_builder.append("# EXAMPLES\n"sv); - examples->for_each_member([&](auto& text, auto& description_value) { - dbgln("```js\n{}\n```\n\n- {}\n", text, description_value.as_string()); - markdown_builder.appendff("```js\n{}\n```\n\n- {}\n", text, description_value.as_string()); - }); - } - - auto document = Markdown::Document::parse(markdown_builder.string_view()); - return document->render_to_html(); -} - -void HelpWindow::set_docs(JsonObject&& docs) -{ - m_docs = move(docs); - static_cast(m_listview->model())->set_from(m_docs); - m_listview->update(); -} -} diff --git a/Userland/Applications/Spreadsheet/HelpWindow.h b/Userland/Applications/Spreadsheet/HelpWindow.h deleted file mode 100644 index fe2b8765b09..00000000000 --- a/Userland/Applications/Spreadsheet/HelpWindow.h +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright (c) 2020-2022, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include - -namespace Spreadsheet { - -class HelpWindow : public GUI::Window { - C_OBJECT(HelpWindow); - -public: - static NonnullRefPtr the(GUI::Window* window) - { - if (s_the) - return *s_the; - - return *(s_the = adopt_ref(*new HelpWindow(window))); - } - - virtual ~HelpWindow() override = default; - - void set_docs(JsonObject&& docs); - -private: - static RefPtr s_the; - ByteString render(StringView key); - HelpWindow(GUI::Window* parent = nullptr); - - JsonObject m_docs; - RefPtr m_webview; - RefPtr m_listview; -}; - -} diff --git a/Userland/Applications/Spreadsheet/ImportDialog.cpp b/Userland/Applications/Spreadsheet/ImportDialog.cpp deleted file mode 100644 index bcf82d5122a..00000000000 --- a/Userland/Applications/Spreadsheet/ImportDialog.cpp +++ /dev/null @@ -1,279 +0,0 @@ -/* - * Copyright (c) 2020-2021, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "ImportDialog.h" -#include "Spreadsheet.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace Spreadsheet { - -CSVImportDialogPage::CSVImportDialogPage(StringView csv) - : m_csv(csv) -{ - m_page = GUI::WizardPage::create( - "CSV Import Options"sv, - "Please select the options for the csv file you wish to import"sv) - .release_value_but_fixme_should_propagate_errors(); - - m_page->body_widget().load_from_gml(csv_import_gml).release_value_but_fixme_should_propagate_errors(); - m_page->set_is_final_page(true); - - m_delimiter_comma_radio = m_page->body_widget().find_descendant_of_type_named("delimiter_comma_radio"); - m_delimiter_semicolon_radio = m_page->body_widget().find_descendant_of_type_named("delimiter_semicolon_radio"); - m_delimiter_tab_radio = m_page->body_widget().find_descendant_of_type_named("delimiter_tab_radio"); - m_delimiter_space_radio = m_page->body_widget().find_descendant_of_type_named("delimiter_space_radio"); - m_delimiter_other_radio = m_page->body_widget().find_descendant_of_type_named("delimiter_other_radio"); - m_delimiter_other_text_box = m_page->body_widget().find_descendant_of_type_named("delimiter_other_text_box"); - m_quote_single_radio = m_page->body_widget().find_descendant_of_type_named("quote_single_radio"); - m_quote_double_radio = m_page->body_widget().find_descendant_of_type_named("quote_double_radio"); - m_quote_other_radio = m_page->body_widget().find_descendant_of_type_named("quote_other_radio"); - m_quote_other_text_box = m_page->body_widget().find_descendant_of_type_named("quote_other_text_box"); - m_quote_escape_combo_box = m_page->body_widget().find_descendant_of_type_named("quote_escape_combo_box"); - m_read_header_check_box = m_page->body_widget().find_descendant_of_type_named("read_header_check_box"); - m_trim_leading_field_spaces_check_box = m_page->body_widget().find_descendant_of_type_named("trim_leading_field_spaces_check_box"); - m_trim_trailing_field_spaces_check_box = m_page->body_widget().find_descendant_of_type_named("trim_trailing_field_spaces_check_box"); - m_data_preview_table_view = m_page->body_widget().find_descendant_of_type_named("data_preview_table_view"); - m_data_preview_error_label = m_page->body_widget().find_descendant_of_type_named("data_preview_error_label"); - m_data_preview_widget = m_page->body_widget().find_descendant_of_type_named("data_preview_widget"); - - m_quote_escape_combo_box->set_model(GUI::ItemListModel::create(m_quote_escape_items)); - - // By default, use commas, double quotes with repeat, and disable headers. - m_delimiter_comma_radio->set_checked(true); - m_quote_double_radio->set_checked(true); - m_quote_escape_combo_box->set_selected_index(0); // Repeat - - m_delimiter_comma_radio->on_checked = [&](auto) { update_preview(); }; - m_delimiter_semicolon_radio->on_checked = [&](auto) { update_preview(); }; - m_delimiter_tab_radio->on_checked = [&](auto) { update_preview(); }; - m_delimiter_space_radio->on_checked = [&](auto) { update_preview(); }; - m_delimiter_other_radio->on_checked = [&](auto) { update_preview(); }; - m_delimiter_other_text_box->on_change = [&] { - if (m_delimiter_other_radio->is_checked()) - update_preview(); - }; - m_quote_single_radio->on_checked = [&](auto) { update_preview(); }; - m_quote_double_radio->on_checked = [&](auto) { update_preview(); }; - m_quote_other_radio->on_checked = [&](auto) { update_preview(); }; - m_quote_other_text_box->on_change = [&] { - if (m_quote_other_radio->is_checked()) - update_preview(); - }; - m_quote_escape_combo_box->on_change = [&](auto&, auto&) { update_preview(); }; - m_read_header_check_box->on_checked = [&](auto) { update_preview(); }; - m_trim_leading_field_spaces_check_box->on_checked = [&](auto) { update_preview(); }; - m_trim_trailing_field_spaces_check_box->on_checked = [&](auto) { update_preview(); }; - - update_preview(); -} - -auto CSVImportDialogPage::make_reader() -> Optional -{ - ByteString delimiter; - ByteString quote; - Reader::ParserTraits::QuoteEscape quote_escape; - - // Delimiter - if (m_delimiter_other_radio->is_checked()) - delimiter = m_delimiter_other_text_box->text(); - else if (m_delimiter_comma_radio->is_checked()) - delimiter = ","; - else if (m_delimiter_semicolon_radio->is_checked()) - delimiter = ";"; - else if (m_delimiter_tab_radio->is_checked()) - delimiter = "\t"; - else if (m_delimiter_space_radio->is_checked()) - delimiter = " "; - else - return {}; - - // Quote separator - if (m_quote_other_radio->is_checked()) - quote = m_quote_other_text_box->text(); - else if (m_quote_single_radio->is_checked()) - quote = "'"; - else if (m_quote_double_radio->is_checked()) - quote = "\""; - else - return {}; - - // Quote escape - auto index = m_quote_escape_combo_box->selected_index(); - if (index == 0) - quote_escape = Reader::ParserTraits::Repeat; - else if (index == 1) - quote_escape = Reader::ParserTraits::Backslash; - else - return {}; - - auto should_read_headers = m_read_header_check_box->is_checked(); - auto should_trim_leading = m_trim_leading_field_spaces_check_box->is_checked(); - auto should_trim_trailing = m_trim_trailing_field_spaces_check_box->is_checked(); - - if (quote.is_empty() || delimiter.is_empty()) - return {}; - - Reader::ParserTraits traits { - move(delimiter), - move(quote), - quote_escape, - }; - - auto behaviors = Reader::default_behaviors() | Reader::ParserBehavior::Lenient; - - if (should_read_headers) - behaviors = behaviors | Reader::ParserBehavior::ReadHeaders; - if (should_trim_leading) - behaviors = behaviors | Reader::ParserBehavior::TrimLeadingFieldSpaces; - if (should_trim_trailing) - behaviors = behaviors | Reader::ParserBehavior::TrimTrailingFieldSpaces; - - return Reader::XSV(m_csv, move(traits), behaviors); -} - -void CSVImportDialogPage::update_preview() - -{ - m_previously_made_reader = make_reader(); - if (!m_previously_made_reader.has_value()) { - m_data_preview_table_view->set_model(nullptr); - m_data_preview_error_label->set_text("Could not read the given file"_string); - m_data_preview_widget->set_active_widget(m_data_preview_error_label); - return; - } - - auto& reader = *m_previously_made_reader; - if (reader.has_error()) { - m_data_preview_table_view->set_model(nullptr); - m_data_preview_error_label->set_text(String::formatted("XSV parse error:\n{}", reader.error_string()).release_value_but_fixme_should_propagate_errors()); - m_data_preview_widget->set_active_widget(m_data_preview_error_label); - return; - } - - Vector headers; - for (auto const& header : reader.headers()) - headers.append(String::from_byte_string(header).release_value_but_fixme_should_propagate_errors()); - - m_data_preview_table_view->set_model( - GUI::ItemListModel>::create(reader, headers, min(8ul, reader.size()))); - m_data_preview_widget->set_active_widget(m_data_preview_table_view); - m_data_preview_table_view->update(); -} - -ErrorOr>, ByteString> ImportDialog::make_and_run_for(GUI::Window& parent, StringView mime, ByteString const& filename, Core::File& file, Workbook& workbook) -{ - auto wizard = GUI::WizardDialog::create(&parent).release_value_but_fixme_should_propagate_errors(); - wizard->set_title("File Import Wizard"); - wizard->set_icon(GUI::Icon::default_icon("app-spreadsheet"sv).bitmap_for_size(16)); - - auto import_xsv = [&]() -> ErrorOr>, ByteString> { - auto contents_or_error = file.read_until_eof(); - if (contents_or_error.is_error()) - return ByteString::formatted("{}", contents_or_error.release_error()); - CSVImportDialogPage page { contents_or_error.value() }; - wizard->replace_page(page.page()); - auto result = wizard->exec(); - - if (result == GUI::Dialog::ExecResult::OK) { - auto& reader = page.reader(); - - Vector> sheets; - - if (reader.has_value()) { - reader->parse(); - if (reader.value().has_error()) - return ByteString::formatted("CSV Import failed: {}", reader.value().error_string()); - - auto sheet = Sheet::from_xsv(reader.value(), workbook); - if (sheet) - sheets.append(sheet.release_nonnull()); - } - - return sheets; - } - - return ByteString { "CSV Import was cancelled" }; - }; - - auto import_worksheet = [&]() -> ErrorOr>, ByteString> { - auto contents_or_error = file.read_until_eof(); - if (contents_or_error.is_error()) - return ByteString::formatted("{}", contents_or_error.release_error()); - auto json_value_option = JsonParser(contents_or_error.release_value()).parse(); - if (json_value_option.is_error()) - return ByteString::formatted("Failed to parse {}", filename); - - auto& json_value = json_value_option.value(); - if (!json_value.is_array()) - return ByteString::formatted("Did not find a spreadsheet in {}", filename); - - Vector> sheets; - - auto& json_array = json_value.as_array(); - json_array.for_each([&](auto& sheet_json) { - if (!sheet_json.is_object()) - return IterationDecision::Continue; - - if (auto sheet = Sheet::from_json(sheet_json.as_object(), workbook)) - sheets.append(sheet.release_nonnull()); - - return IterationDecision::Continue; - }); - - return sheets; - }; - - if (mime == "text/csv") { - return import_xsv(); - } else if (mime == "application/x-sheets+json") { - return import_worksheet(); - } else { - auto page = GUI::WizardPage::create( - "Import File Format"sv, - ByteString::formatted("Select the format you wish to import '{}' as", LexicalPath::basename(filename))) - .release_value_but_fixme_should_propagate_errors(); - - page->on_next_page = [] { return nullptr; }; - - page->body_widget().load_from_gml(select_format_page_gml).release_value_but_fixme_should_propagate_errors(); - auto format_combo_box = page->body_widget().find_descendant_of_type_named("select_format_page_format_combo_box"); - - Vector supported_formats { - "CSV (text/csv)", - "Spreadsheet Worksheet", - }; - format_combo_box->set_model(GUI::ItemListModel::create(supported_formats)); - - wizard->push_page(page); - - if (wizard->exec() != GUI::Dialog::ExecResult::OK) - return ByteString { "Import was cancelled" }; - - if (format_combo_box->selected_index() == 0) - return import_xsv(); - - if (format_combo_box->selected_index() == 1) - return import_worksheet(); - - VERIFY_NOT_REACHED(); - } -} - -}; diff --git a/Userland/Applications/Spreadsheet/ImportDialog.h b/Userland/Applications/Spreadsheet/ImportDialog.h deleted file mode 100644 index 6d2286e09f5..00000000000 --- a/Userland/Applications/Spreadsheet/ImportDialog.h +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright (c) 2020-2021, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include "Readers/XSV.h" -#include -#include -#include - -namespace Spreadsheet { - -class Sheet; -class Workbook; - -struct CSVImportDialogPage { - explicit CSVImportDialogPage(StringView csv); - - NonnullRefPtr page() { return *m_page; } - Optional& reader() { return m_previously_made_reader; } - -protected: - void update_preview(); - Optional make_reader(); - -private: - StringView m_csv; - Optional m_previously_made_reader; - RefPtr m_page; - RefPtr m_delimiter_comma_radio; - RefPtr m_delimiter_semicolon_radio; - RefPtr m_delimiter_tab_radio; - RefPtr m_delimiter_space_radio; - RefPtr m_delimiter_other_radio; - RefPtr m_delimiter_other_text_box; - RefPtr m_quote_single_radio; - RefPtr m_quote_double_radio; - RefPtr m_quote_other_radio; - RefPtr m_quote_other_text_box; - RefPtr m_quote_escape_combo_box; - RefPtr m_read_header_check_box; - RefPtr m_trim_leading_field_spaces_check_box; - RefPtr m_trim_trailing_field_spaces_check_box; - RefPtr m_data_preview_table_view; - RefPtr m_data_preview_error_label; - RefPtr m_data_preview_widget; - Vector m_quote_escape_items { - // Note: Keep in sync with Reader::ParserTraits::QuoteEscape. - "Repeat", - "Backslash", - }; -}; - -struct ImportDialog { - static ErrorOr>, ByteString> make_and_run_for(GUI::Window& parent, StringView mime, ByteString const& filename, Core::File& file, Workbook&); -}; - -} diff --git a/Userland/Applications/Spreadsheet/JSIntegration.cpp b/Userland/Applications/Spreadsheet/JSIntegration.cpp deleted file mode 100644 index 0addf56a2be..00000000000 --- a/Userland/Applications/Spreadsheet/JSIntegration.cpp +++ /dev/null @@ -1,427 +0,0 @@ -/* - * Copyright (c) 2020-2022, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "JSIntegration.h" -#include "Spreadsheet.h" -#include "Workbook.h" -#include -#include -#include -#include -#include -#include - -namespace Spreadsheet { - -JS_DEFINE_ALLOCATOR(SheetGlobalObject); -JS_DEFINE_ALLOCATOR(WorkbookObject); - -Optional get_function_and_argument_index(StringView source) -{ - JS::Lexer lexer { source }; - // Track 's, and how many complete expressions are inside the parenthesized expression. - Vector state; - StringView last_name; - Vector names; - size_t open_parens_since_last_commit = 0; - size_t open_curlies_and_brackets_since_last_commit = 0; - bool previous_was_identifier = false; - auto token = lexer.next(); - while (token.type() != JS::TokenType::Eof) { - switch (token.type()) { - case JS::TokenType::Identifier: - previous_was_identifier = true; - last_name = token.value(); - break; - case JS::TokenType::ParenOpen: - if (!previous_was_identifier) { - open_parens_since_last_commit++; - break; - } - previous_was_identifier = false; - state.append(0); - names.append(last_name); - break; - case JS::TokenType::ParenClose: - previous_was_identifier = false; - if (open_parens_since_last_commit == 0) { - if (state.is_empty() || names.is_empty()) { - // JS Syntax error. - break; - } - state.take_last(); - names.take_last(); - break; - } - --open_parens_since_last_commit; - break; - case JS::TokenType::Comma: - previous_was_identifier = false; - if (open_parens_since_last_commit == 0 && open_curlies_and_brackets_since_last_commit == 0) { - if (!state.is_empty()) - state.last()++; - break; - } - break; - case JS::TokenType::BracketOpen: - previous_was_identifier = false; - open_curlies_and_brackets_since_last_commit++; - break; - case JS::TokenType::BracketClose: - previous_was_identifier = false; - if (open_curlies_and_brackets_since_last_commit > 0) - open_curlies_and_brackets_since_last_commit--; - break; - case JS::TokenType::CurlyOpen: - previous_was_identifier = false; - open_curlies_and_brackets_since_last_commit++; - break; - case JS::TokenType::CurlyClose: - previous_was_identifier = false; - if (open_curlies_and_brackets_since_last_commit > 0) - open_curlies_and_brackets_since_last_commit--; - break; - default: - previous_was_identifier = false; - break; - } - - token = lexer.next(); - } - if (!names.is_empty() && !state.is_empty()) - return FunctionAndArgumentIndex { names.last(), state.last() }; - return {}; -} - -SheetGlobalObject::SheetGlobalObject(JS::Realm& realm, Sheet& sheet) - : JS::GlobalObject(realm) - , m_sheet(sheet) -{ -} - -JS::ThrowCompletionOr SheetGlobalObject::internal_has_property(JS::PropertyKey const& name) const -{ - if (name.is_string()) { - if (name.as_string() == "value") - return true; - if (m_sheet.parse_cell_name(name.as_string()).has_value()) - return true; - } - return Object::internal_has_property(name); -} - -JS::ThrowCompletionOr SheetGlobalObject::internal_get(const JS::PropertyKey& property_name, JS::Value receiver, JS::CacheablePropertyMetadata*, PropertyLookupPhase) const -{ - if (property_name.is_string()) { - if (property_name.as_string() == "value") { - if (auto cell = m_sheet.current_evaluated_cell()) - return cell->js_data(); - - return JS::js_undefined(); - } - if (auto pos = m_sheet.parse_cell_name(property_name.as_string()); pos.has_value()) { - auto& cell = m_sheet.ensure(pos.value()); - cell.reference_from(m_sheet.current_evaluated_cell()); - return cell.typed_js_data(); - } - } - - return Base::internal_get(property_name, receiver); -} - -JS::ThrowCompletionOr SheetGlobalObject::internal_set(const JS::PropertyKey& property_name, JS::Value value, JS::Value receiver, JS::CacheablePropertyMetadata*) -{ - if (property_name.is_string()) { - if (auto pos = m_sheet.parse_cell_name(property_name.as_string()); pos.has_value()) { - auto& cell = m_sheet.ensure(pos.value()); - if (auto current = m_sheet.current_evaluated_cell()) - current->reference_from(&cell); - - cell.set_data(value); // FIXME: This produces un-savable state! - return true; - } - } - - return Base::internal_set(property_name, value, receiver); -} - -void SheetGlobalObject::initialize(JS::Realm& realm) -{ - Base::initialize(realm); - - u8 attr = JS::Attribute::Configurable | JS::Attribute::Writable | JS::Attribute::Enumerable; - define_native_function(realm, "get_real_cell_contents", get_real_cell_contents, 1, attr); - define_native_function(realm, "set_real_cell_contents", set_real_cell_contents, 2, attr); - define_native_function(realm, "parse_cell_name", parse_cell_name, 1, attr); - define_native_function(realm, "current_cell_position", current_cell_position, 0, attr); - define_native_function(realm, "column_arithmetic", column_arithmetic, 2, attr); - define_native_function(realm, "column_index", column_index, 1, attr); - define_native_function(realm, "get_column_bound", get_column_bound, 1, attr); - define_native_accessor(realm, "name", get_name, nullptr, attr); -} - -void SheetGlobalObject::visit_edges(Visitor& visitor) -{ - Base::visit_edges(visitor); - for (auto& it : m_sheet.cells()) { - if (auto opt_thrown_value = it.value->thrown_value(); opt_thrown_value.has_value()) - visitor.visit(*opt_thrown_value); - - visitor.visit(it.value->evaluated_data()); - } -} - -JS_DEFINE_NATIVE_FUNCTION(SheetGlobalObject::get_name) -{ - auto this_object = TRY(vm.this_value().to_object(vm)); - - if (!is(*this_object)) - return vm.throw_completion(JS::ErrorType::NotAnObjectOfType, "SheetGlobalObject"); - - auto& sheet_object = static_cast(*this_object); - return JS::PrimitiveString::create(vm, sheet_object.m_sheet.name()); -} - -JS_DEFINE_NATIVE_FUNCTION(SheetGlobalObject::get_real_cell_contents) -{ - auto this_object = TRY(vm.this_value().to_object(vm)); - - if (!is(*this_object)) - return vm.throw_completion(JS::ErrorType::NotAnObjectOfType, "SheetGlobalObject"); - - auto& sheet_object = static_cast(*this_object); - - if (vm.argument_count() != 1) - return vm.throw_completion("Expected exactly one argument to get_real_cell_contents()"sv); - - auto name_value = vm.argument(0); - if (!name_value.is_string()) - return vm.throw_completion("Expected a String argument to get_real_cell_contents()"sv); - auto position = sheet_object.m_sheet.parse_cell_name(name_value.as_string().byte_string()); - if (!position.has_value()) - return vm.throw_completion("Invalid cell name"sv); - - auto const* cell = sheet_object.m_sheet.at(position.value()); - if (!cell) - return JS::js_undefined(); - - if (cell->kind() == Spreadsheet::Cell::Kind::Formula) - return JS::PrimitiveString::create(vm, ByteString::formatted("={}", cell->data())); - - return JS::PrimitiveString::create(vm, cell->data()); -} - -JS_DEFINE_NATIVE_FUNCTION(SheetGlobalObject::set_real_cell_contents) -{ - auto this_object = TRY(vm.this_value().to_object(vm)); - - if (!is(*this_object)) - return vm.throw_completion(JS::ErrorType::NotAnObjectOfType, "SheetGlobalObject"); - - auto& sheet_object = static_cast(*this_object); - - if (vm.argument_count() != 2) - return vm.throw_completion("Expected exactly two arguments to set_real_cell_contents()"sv); - - auto name_value = vm.argument(0); - if (!name_value.is_string()) - return vm.throw_completion("Expected the first argument of set_real_cell_contents() to be a String"sv); - auto position = sheet_object.m_sheet.parse_cell_name(name_value.as_string().byte_string()); - if (!position.has_value()) - return vm.throw_completion("Invalid cell name"sv); - - auto new_contents_value = vm.argument(1); - if (!new_contents_value.is_string()) - return vm.throw_completion("Expected the second argument of set_real_cell_contents() to be a String"sv); - - auto& cell = sheet_object.m_sheet.ensure(position.value()); - auto new_contents = new_contents_value.as_string().byte_string(); - cell.set_data(new_contents); - return JS::js_null(); -} - -JS_DEFINE_NATIVE_FUNCTION(SheetGlobalObject::parse_cell_name) -{ - auto& realm = *vm.current_realm(); - - auto this_object = TRY(vm.this_value().to_object(vm)); - - if (!is(*this_object)) - return vm.throw_completion(JS::ErrorType::NotAnObjectOfType, "SheetGlobalObject"); - - auto& sheet_object = static_cast(*this_object); - - if (vm.argument_count() != 1) - return vm.throw_completion("Expected exactly one argument to parse_cell_name()"sv); - auto name_value = vm.argument(0); - if (!name_value.is_string()) - return vm.throw_completion("Expected a String argument to parse_cell_name()"sv); - auto position = sheet_object.m_sheet.parse_cell_name(name_value.as_string().byte_string()); - if (!position.has_value()) - return JS::js_undefined(); - - auto object = JS::Object::create(realm, realm.intrinsics().object_prototype()); - object->define_direct_property("column", JS::PrimitiveString::create(vm, sheet_object.m_sheet.column(position.value().column)), JS::default_attributes); - object->define_direct_property("row", JS::Value((unsigned)position.value().row), JS::default_attributes); - - return object; -} - -JS_DEFINE_NATIVE_FUNCTION(SheetGlobalObject::current_cell_position) -{ - auto& realm = *vm.current_realm(); - - if (vm.argument_count() != 0) - return vm.throw_completion("Expected no arguments to current_cell_position()"sv); - - auto this_object = TRY(vm.this_value().to_object(vm)); - - if (!is(*this_object)) - return vm.throw_completion(JS::ErrorType::NotAnObjectOfType, "SheetGlobalObject"); - - auto& sheet_object = static_cast(*this_object); - auto* current_cell = sheet_object.m_sheet.current_evaluated_cell(); - if (!current_cell) - return JS::js_null(); - - auto position = current_cell->position(); - - auto object = JS::Object::create(realm, realm.intrinsics().object_prototype()); - object->define_direct_property("column", JS::PrimitiveString::create(vm, sheet_object.m_sheet.column(position.column)), JS::default_attributes); - object->define_direct_property("row", JS::Value((unsigned)position.row), JS::default_attributes); - - return object; -} - -JS_DEFINE_NATIVE_FUNCTION(SheetGlobalObject::column_index) -{ - if (vm.argument_count() != 1) - return vm.throw_completion("Expected exactly one argument to column_index()"sv); - - auto column_name = vm.argument(0); - if (!column_name.is_string()) - return vm.throw_completion(JS::ErrorType::NotAnObjectOfType, "String"); - - auto column_name_str = column_name.as_string().byte_string(); - - auto this_object = TRY(vm.this_value().to_object(vm)); - - if (!is(*this_object)) - return vm.throw_completion(JS::ErrorType::NotAnObjectOfType, "SheetGlobalObject"); - - auto& sheet_object = static_cast(*this_object); - auto& sheet = sheet_object.m_sheet; - auto column_index = sheet.column_index(column_name_str); - if (!column_index.has_value()) - return vm.throw_completion(TRY_OR_THROW_OOM(vm, String::formatted("'{}' is not a valid column", column_name_str))); - - return JS::Value((i32)column_index.value()); -} - -JS_DEFINE_NATIVE_FUNCTION(SheetGlobalObject::column_arithmetic) -{ - if (vm.argument_count() != 2) - return vm.throw_completion("Expected exactly two arguments to column_arithmetic()"sv); - - auto column_name = vm.argument(0); - if (!column_name.is_string()) - return vm.throw_completion(JS::ErrorType::NotAnObjectOfType, "String"); - - auto column_name_str = column_name.as_string().byte_string(); - - auto offset = TRY(vm.argument(1).to_number(vm)); - auto offset_number = static_cast(offset.as_double()); - - auto this_object = TRY(vm.this_value().to_object(vm)); - - if (!is(*this_object)) - return vm.throw_completion(JS::ErrorType::NotAnObjectOfType, "SheetGlobalObject"); - - auto& sheet_object = static_cast(*this_object); - auto& sheet = sheet_object.m_sheet; - auto new_column = sheet.column_arithmetic(column_name_str, offset_number); - if (!new_column.has_value()) - return vm.throw_completion(TRY_OR_THROW_OOM(vm, String::formatted("'{}' is not a valid column", column_name_str))); - - return JS::PrimitiveString::create(vm, new_column.release_value()); -} - -JS_DEFINE_NATIVE_FUNCTION(SheetGlobalObject::get_column_bound) -{ - if (vm.argument_count() != 1) - return vm.throw_completion("Expected exactly one argument to get_column_bound()"sv); - - auto column_name = vm.argument(0); - if (!column_name.is_string()) - return vm.throw_completion(JS::ErrorType::NotAnObjectOfType, "String"); - - auto column_name_str = column_name.as_string().byte_string(); - auto this_object = TRY(vm.this_value().to_object(vm)); - - if (!is(*this_object)) - return vm.throw_completion(JS::ErrorType::NotAnObjectOfType, "SheetGlobalObject"); - - auto& sheet_object = static_cast(*this_object); - auto& sheet = sheet_object.m_sheet; - auto maybe_column_index = sheet.column_index(column_name_str); - if (!maybe_column_index.has_value()) - return vm.throw_completion(TRY_OR_THROW_OOM(vm, String::formatted("'{}' is not a valid column", column_name_str))); - - auto bounds = sheet.written_data_bounds(*maybe_column_index); - return JS::Value(bounds.row); -} - -WorkbookObject::WorkbookObject(JS::Realm& realm, Workbook& workbook) - : JS::Object(ConstructWithPrototypeTag::Tag, realm.intrinsics().object_prototype()) - , m_workbook(workbook) -{ -} - -void WorkbookObject::initialize(JS::Realm& realm) -{ - Base::initialize(realm); - define_native_function(realm, "sheet", sheet, 1, JS::default_attributes); -} - -void WorkbookObject::visit_edges(Visitor& visitor) -{ - Base::visit_edges(visitor); - for (auto& sheet : m_workbook.sheets()) - visitor.visit(&sheet->global_object()); -} - -JS_DEFINE_NATIVE_FUNCTION(WorkbookObject::sheet) -{ - if (vm.argument_count() != 1) - return vm.throw_completion("Expected exactly one argument to sheet()"sv); - auto name_value = vm.argument(0); - if (!name_value.is_string() && !name_value.is_number()) - return vm.throw_completion("Expected a String or Number argument to sheet()"sv); - - auto this_object = TRY(vm.this_value().to_object(vm)); - - if (!is(*this_object)) - return vm.throw_completion(JS::ErrorType::NotAnObjectOfType, "WorkbookObject"); - - auto& workbook_object = static_cast(*this_object); - auto& workbook = workbook_object.m_workbook; - - if (name_value.is_string()) { - auto name = name_value.as_string().byte_string(); - for (auto& sheet : workbook.sheets()) { - if (sheet->name() == name) - return JS::Value(&sheet->global_object()); - } - } else { - auto index = TRY(name_value.to_length(vm)); - if (index < workbook.sheets().size()) - return JS::Value(&workbook.sheets()[index]->global_object()); - } - - return JS::js_undefined(); -} - -} diff --git a/Userland/Applications/Spreadsheet/JSIntegration.h b/Userland/Applications/Spreadsheet/JSIntegration.h deleted file mode 100644 index 83f0c419367..00000000000 --- a/Userland/Applications/Spreadsheet/JSIntegration.h +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright (c) 2020-2022, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include "Forward.h" -#include -#include -#include - -namespace Spreadsheet { - -struct FunctionAndArgumentIndex { - ByteString function_name; - size_t argument_index { 0 }; -}; -Optional get_function_and_argument_index(StringView source); - -class SheetGlobalObject final : public JS::GlobalObject { - JS_OBJECT(SheetGlobalObject, JS::GlobalObject); - JS_DECLARE_ALLOCATOR(SheetGlobalObject); - -public: - SheetGlobalObject(JS::Realm&, Sheet&); - virtual void initialize(JS::Realm&) override; - virtual ~SheetGlobalObject() override = default; - - virtual JS::ThrowCompletionOr internal_has_property(JS::PropertyKey const& name) const override; - virtual JS::ThrowCompletionOr internal_get(JS::PropertyKey const&, JS::Value receiver, JS::CacheablePropertyMetadata*, PropertyLookupPhase) const override; - virtual JS::ThrowCompletionOr internal_set(JS::PropertyKey const&, JS::Value value, JS::Value receiver, JS::CacheablePropertyMetadata*) override; - - JS_DECLARE_NATIVE_FUNCTION(get_real_cell_contents); - JS_DECLARE_NATIVE_FUNCTION(set_real_cell_contents); - JS_DECLARE_NATIVE_FUNCTION(parse_cell_name); - JS_DECLARE_NATIVE_FUNCTION(current_cell_position); - JS_DECLARE_NATIVE_FUNCTION(column_index); - JS_DECLARE_NATIVE_FUNCTION(column_arithmetic); - JS_DECLARE_NATIVE_FUNCTION(get_column_bound); - JS_DECLARE_NATIVE_FUNCTION(get_name); - -private: - virtual void visit_edges(Visitor&) override; - Sheet& m_sheet; -}; - -class WorkbookObject final : public JS::Object { - JS_OBJECT(WorkbookObject, JS::Object); - JS_DECLARE_ALLOCATOR(WorkbookObject); - -public: - WorkbookObject(JS::Realm&, Workbook&); - - virtual ~WorkbookObject() override = default; - - virtual void initialize(JS::Realm&) override; - - JS_DECLARE_NATIVE_FUNCTION(sheet); - -private: - virtual void visit_edges(Visitor&) override; - Workbook& m_workbook; -}; - -} diff --git a/Userland/Applications/Spreadsheet/Position.h b/Userland/Applications/Spreadsheet/Position.h deleted file mode 100644 index 04558f4b838..00000000000 --- a/Userland/Applications/Spreadsheet/Position.h +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright (c) 2020, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include - -namespace Spreadsheet { - -class Sheet; - -struct Position { - Position() = default; - - Position(size_t column, size_t row) - : column(column) - , row(row) - , m_hash(pair_int_hash(column, row)) - { - } - - ALWAYS_INLINE u32 hash() const - { - if (m_hash == 0) - return m_hash = int_hash(column * 65537 + row); - - return m_hash; - } - - bool operator==(Position const& other) const - { - return row == other.row && column == other.column; - } - - ByteString to_cell_identifier(Sheet const& sheet) const; - URL::URL to_url(Sheet const& sheet) const; - - size_t column { 0 }; - size_t row { 0 }; - -private: - mutable u32 m_hash { 0 }; -}; - -} diff --git a/Userland/Applications/Spreadsheet/Readers/CSV.h b/Userland/Applications/Spreadsheet/Readers/CSV.h deleted file mode 100644 index 72283a72e03..00000000000 --- a/Userland/Applications/Spreadsheet/Readers/CSV.h +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright (c) 2020, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include "XSV.h" -#include -#include - -namespace Reader { - -class CSV : public XSV { -public: - CSV(StringView source, ParserBehavior behaviors = default_behaviors()) - : XSV(source, { ",", "\"", ParserTraits::Repeat }, behaviors) - { - } -}; - -} diff --git a/Userland/Applications/Spreadsheet/Readers/Test/TestXSV.cpp b/Userland/Applications/Spreadsheet/Readers/Test/TestXSV.cpp deleted file mode 100644 index b0c93e2ed51..00000000000 --- a/Userland/Applications/Spreadsheet/Readers/Test/TestXSV.cpp +++ /dev/null @@ -1,100 +0,0 @@ -/* - * Copyright (c) 2020, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include - -#include "../CSV.h" -#include "../XSV.h" -#include -#include - -TEST_CASE(should_parse_valid_data) -{ - { - auto data = R"~~~(Foo, Bar, Baz - 1, 2, 3 - 4, 5, 6 - """x", y"z, 9)~~~"sv; - auto csv = Reader::CSV { data, Reader::default_behaviors() | Reader::ParserBehavior::ReadHeaders | Reader::ParserBehavior::TrimLeadingFieldSpaces }; - csv.parse(); - EXPECT(!csv.has_error()); - - EXPECT_EQ(csv[0]["Foo"sv], "1"sv); - EXPECT_EQ(csv[2]["Foo"sv], "\"x"sv); - EXPECT_EQ(csv[2]["Bar"sv], "y\"z"sv); - } - - { - auto data = R"~~~(Foo, Bar, Baz - 1 , 2, 3 - 4, "5 " , 6 - """x", y"z, 9 )~~~"sv; - auto csv = Reader::CSV { data, Reader::default_behaviors() | Reader::ParserBehavior::ReadHeaders | Reader::ParserBehavior::TrimLeadingFieldSpaces | Reader::ParserBehavior::TrimTrailingFieldSpaces }; - csv.parse(); - EXPECT(!csv.has_error()); - - EXPECT_EQ(csv[0]["Foo"sv], "1"sv); - EXPECT_EQ(csv[1]["Bar"sv], "5 "sv); - EXPECT_EQ(csv[2]["Foo"sv], "\"x"sv); - EXPECT_EQ(csv[2]["Baz"sv], "9"sv); - } -} - -TEST_CASE(should_fail_nicely) -{ - { - auto data = R"~~~(Foo, Bar, Baz - x, y)~~~"sv; - auto csv = Reader::CSV { data, Reader::default_behaviors() | Reader::ParserBehavior::ReadHeaders | Reader::ParserBehavior::TrimLeadingFieldSpaces }; - csv.parse(); - EXPECT(csv.has_error()); - EXPECT_EQ(csv.error(), Reader::ReadError::NonConformingColumnCount); - } - - { - auto data = R"~~~(Foo, Bar, Baz - x, y, "z)~~~"sv; - auto csv = Reader::CSV { data, Reader::default_behaviors() | Reader::ParserBehavior::ReadHeaders | Reader::ParserBehavior::TrimLeadingFieldSpaces }; - csv.parse(); - EXPECT(csv.has_error()); - EXPECT_EQ(csv.error(), Reader::ReadError::QuoteFailure); - } -} - -TEST_CASE(should_iterate_rows) -{ - auto data = R"~~~(Foo, Bar, Baz - 1, 2, 3 - 4, 5, 6 - """x", y"z, 9)~~~"sv; - auto csv = Reader::CSV { data, Reader::default_behaviors() | Reader::ParserBehavior::ReadHeaders | Reader::ParserBehavior::TrimLeadingFieldSpaces }; - csv.parse(); - EXPECT(!csv.has_error()); - - bool ran = false; - for (auto row : csv) - ran = !row[0].is_empty(); - - EXPECT(ran); -} - -BENCHMARK_CASE(fairly_big_data) -{ - constexpr auto num_rows = 100000u; - constexpr auto line = "well,hello,friends,1,2,3,4,5,6,7,8,pizza,guacamole\n"sv; - auto buf = ByteBuffer::create_uninitialized((line.length() * num_rows) + 1).release_value(); - buf[buf.size() - 1] = '\0'; - - for (size_t row = 0; row <= num_rows; ++row) { - memcpy(buf.offset_pointer(row * line.length()), line.characters_without_null_termination(), line.length()); - } - - auto csv = Reader::CSV { StringView { buf.bytes() }, Reader::default_behaviors() | Reader::ParserBehavior::ReadHeaders }; - csv.parse(); - - EXPECT(!csv.has_error()); - EXPECT_EQ(csv.size(), num_rows); -} diff --git a/Userland/Applications/Spreadsheet/Readers/XSV.cpp b/Userland/Applications/Spreadsheet/Readers/XSV.cpp deleted file mode 100644 index 93128ec2bcf..00000000000 --- a/Userland/Applications/Spreadsheet/Readers/XSV.cpp +++ /dev/null @@ -1,307 +0,0 @@ -/* - * Copyright (c) 2020, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "XSV.h" -#include - -namespace Reader { - -ParserBehavior operator&(ParserBehavior left, ParserBehavior right) -{ - return static_cast(to_underlying(left) & to_underlying(right)); -} - -ParserBehavior operator|(ParserBehavior left, ParserBehavior right) -{ - return static_cast(to_underlying(left) | to_underlying(right)); -} - -void XSV::set_error(ReadError error) -{ - if (m_error == ReadError::None) - m_error = error; -} - -Vector XSV::headers() const -{ - Vector headers; - if (has_explicit_headers()) { - for (auto& field : m_names) - headers.append(field.is_string_view ? field.as_string_view : field.as_string.view()); - } else { - // No headers read, grab one of the rows and generate empty names - if (m_rows.is_empty()) - return headers; - - for ([[maybe_unused]] auto& field : m_rows.first()) - headers.append(ByteString::empty()); - } - - return headers; -} - -void XSV::parse_preview() -{ - reset(); - if ((m_behaviors & ParserBehavior::ReadHeaders) != ParserBehavior::None) - read_headers(); - - while (!has_error() && !m_lexer.is_eof()) { - if (m_rows.size() >= 10) - break; - m_rows.append(read_row()); - } -} - -void XSV::parse() -{ - reset(); - if ((m_behaviors & ParserBehavior::ReadHeaders) != ParserBehavior::None) - read_headers(); - - while (!has_error() && !m_lexer.is_eof()) - m_rows.append(read_row()); - - // Read and drop any extra lines at the end. - while (!m_lexer.is_eof()) { - if (!m_lexer.consume_specific("\r\n"sv) && !m_lexer.consume_specific('\n')) - break; - } - - if (!m_lexer.is_eof()) - set_error(ReadError::DataPastLogicalEnd); -} - -void XSV::read_headers() -{ - if (!m_names.is_empty()) { - set_error(ReadError::InternalError); - m_names.clear(); - } - - m_names = read_row(true); -} - -Vector XSV::read_row(bool header_row) -{ - Vector row; - bool first = true; - while (!(m_lexer.is_eof() || m_lexer.next_is('\n') || m_lexer.next_is("\r\n")) && (first || m_lexer.consume_specific(m_traits.separator.view()))) { - first = false; - row.append(read_one_field()); - } - - if (!m_lexer.is_eof()) { - auto crlf_ok = m_lexer.consume_specific("\r\n"sv); - if (!crlf_ok) { - auto lf_ok = m_lexer.consume_specific('\n'); - if (!lf_ok) - set_error(ReadError::DataPastLogicalEnd); - } - } - - auto is_lenient = (m_behaviors & ParserBehavior::Lenient) != ParserBehavior::None; - if (is_lenient) { - if (m_rows.is_empty()) - return row; - - auto& last_row = m_rows.last(); - if (row.size() < last_row.size()) { - if (!m_names.is_empty()) - row.resize(m_names.size()); - else - row.resize(last_row.size()); - } else if (row.size() > last_row.size()) { - auto new_size = row.size(); - for (auto& row : m_rows) - row.resize(new_size); - } - } else { - auto should_read_headers = (m_behaviors & ParserBehavior::ReadHeaders) != ParserBehavior::None; - if (!header_row && should_read_headers && row.size() != m_names.size()) - set_error(ReadError::NonConformingColumnCount); - else if (!header_row && !has_explicit_headers() && !m_rows.is_empty() && m_rows.first().size() != row.size()) - set_error(ReadError::NonConformingColumnCount); - } - - return row; -} - -XSV::Field XSV::read_one_field() -{ - if ((m_behaviors & ParserBehavior::TrimLeadingFieldSpaces) != ParserBehavior::None) - m_lexer.consume_while(is_any_of(" \t\v"sv)); - - bool is_quoted = false; - Field field; - if (m_lexer.next_is(m_traits.quote.view())) { - is_quoted = true; - field = read_one_quoted_field(); - } else { - field = read_one_unquoted_field(); - } - - if ((m_behaviors & ParserBehavior::TrimTrailingFieldSpaces) != ParserBehavior::None) { - m_lexer.consume_while(is_any_of(" \t\v"sv)); - - if (!is_quoted) { - // Also have to trim trailing spaces from unquoted fields. - StringView view; - if (field.is_string_view) - view = field.as_string_view; - else - view = field.as_string; - - if (!view.is_empty()) { - ssize_t i = view.length() - 1; - for (; i >= 0; --i) { - if (!view.substring_view(i, 1).is_one_of(" ", "\t", "\v")) - break; - } - view = view.substring_view(0, i + 1); - } - - if (field.is_string_view) - field.as_string_view = view; - else - field.as_string = field.as_string.substring(0, view.length()); - } - } - - return field; -} - -XSV::Field XSV::read_one_quoted_field() -{ - if (!m_lexer.consume_specific(m_traits.quote.view())) - set_error(ReadError::InternalError); - - size_t start = m_lexer.tell(), end = start; - bool is_copy = false; - StringBuilder builder; - auto allow_newlines = (m_behaviors & ParserBehavior::AllowNewlinesInFields) != ParserBehavior::None; - - for (; !m_lexer.is_eof();) { - char ch; - switch (m_traits.quote_escape) { - case ParserTraits::Backslash: - if (m_lexer.consume_specific('\\') && m_lexer.consume_specific(m_traits.quote.view())) { - // If there is an escaped quote, we have no choice but to make a copy. - if (!is_copy) { - is_copy = true; - builder.append(m_source.substring_view(start, end - start)); - } - builder.append(m_traits.quote); - end = m_lexer.tell(); - continue; - } - break; - case ParserTraits::Repeat: - if (m_lexer.consume_specific(m_traits.quote.view())) { - if (m_lexer.consume_specific(m_traits.quote.view())) { - // If there is an escaped quote, we have no choice but to make a copy. - if (!is_copy) { - is_copy = true; - builder.append(m_source.substring_view(start, end - start)); - } - builder.append(m_traits.quote); - end = m_lexer.tell(); - continue; - } - for (size_t i = 0; i < m_traits.quote.length(); ++i) - m_lexer.retreat(); - goto end; - } - break; - } - - if (m_lexer.next_is(m_traits.quote.view())) - goto end; - - if (!allow_newlines) { - if (m_lexer.next_is('\n') || m_lexer.next_is("\r\n")) - goto end; - } - - ch = m_lexer.consume(); - if (is_copy) - builder.append(ch); - end = m_lexer.tell(); - continue; - - end: - break; - } - - if (!m_lexer.consume_specific(m_traits.quote.view())) - set_error(ReadError::QuoteFailure); - - if (is_copy) - return { {}, builder.to_byte_string(), false }; - - return { m_source.substring_view(start, end - start), {}, true }; -} - -XSV::Field XSV::read_one_unquoted_field() -{ - size_t start = m_lexer.tell(), end = start; - bool allow_quote_in_field = (m_behaviors & ParserBehavior::QuoteOnlyInFieldStart) != ParserBehavior::None; - - for (; !m_lexer.is_eof();) { - if (m_lexer.next_is(m_traits.separator.view())) - break; - - if (m_lexer.next_is("\r\n") || m_lexer.next_is("\n")) - break; - - if (m_lexer.consume_specific(m_traits.quote.view())) { - if (!allow_quote_in_field) - set_error(ReadError::QuoteFailure); - end = m_lexer.tell(); - continue; - } - - m_lexer.consume(); - end = m_lexer.tell(); - } - - return { m_source.substring_view(start, end - start), {}, true }; -} - -StringView XSV::Row::operator[](StringView name) const -{ - VERIFY(!m_xsv.m_names.is_empty()); - auto it = m_xsv.m_names.find_if([&](auto const& entry) { return name == entry; }); - VERIFY(!it.is_end()); - - return (*this)[it.index()]; -} - -StringView XSV::Row::operator[](size_t column) const -{ - auto& field = m_xsv.m_rows[m_index][column]; - if (field.is_string_view) - return field.as_string_view; - return field.as_string; -} - -const XSV::Row XSV::operator[](size_t index) const -{ - return const_cast(*this)[index]; -} - -XSV::Row XSV::at(size_t index) const -{ - return this->operator[](index); -} - -XSV::Row XSV::operator[](size_t index) -{ - VERIFY(m_rows.size() > index); - return Row { *this, index }; -} - -} diff --git a/Userland/Applications/Spreadsheet/Readers/XSV.h b/Userland/Applications/Spreadsheet/Readers/XSV.h deleted file mode 100644 index 65e40c2f859..00000000000 --- a/Userland/Applications/Spreadsheet/Readers/XSV.h +++ /dev/null @@ -1,222 +0,0 @@ -/* - * Copyright (c) 2020-2022, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include - -namespace Reader { - -enum class ParserBehavior : u32 { - None = 0, - ReadHeaders = 1, - AllowNewlinesInFields = ReadHeaders << 1, - TrimLeadingFieldSpaces = ReadHeaders << 2, - TrimTrailingFieldSpaces = ReadHeaders << 3, - QuoteOnlyInFieldStart = ReadHeaders << 4, - Lenient = ReadHeaders << 5, // This is the typical "spreadsheet import" behavior - // Currently, it: - // - fills in missing fields with empty values - // - updates previous rows with extra columns -}; - -ParserBehavior operator&(ParserBehavior left, ParserBehavior right); -ParserBehavior operator|(ParserBehavior left, ParserBehavior right); - -struct ParserTraits { - ByteString separator; - ByteString quote { "\"" }; - enum QuoteEscape { - Repeat, - Backslash, - } quote_escape { Repeat }; -}; - -#define ENUMERATE_READ_ERRORS() \ - E(None, "No errors") \ - E(NonConformingColumnCount, "Header count does not match given column count") \ - E(QuoteFailure, "Quoting failure") \ - E(InternalError, "Internal error") \ - E(DataPastLogicalEnd, "Extra data past the logical end of the rows") - -enum class ReadError { -#define E(name, _) name, - ENUMERATE_READ_ERRORS() -#undef E -}; - -constexpr ParserBehavior default_behaviors() -{ - return ParserBehavior::QuoteOnlyInFieldStart; -} - -class XSV { -public: - XSV(StringView source, ParserTraits traits, ParserBehavior behaviors = default_behaviors()) - : m_source(source) - , m_lexer(m_source) - , m_traits(traits) - , m_behaviors(behaviors) - { - parse_preview(); - } - - virtual ~XSV() = default; - - void parse(); - bool has_error() const { return m_error != ReadError::None; } - ReadError error() const { return m_error; } - ByteString error_string() const - { - switch (m_error) { -#define E(x, y) \ - case ReadError::x: \ - return y; - - ENUMERATE_READ_ERRORS(); -#undef E - } - VERIFY_NOT_REACHED(); - } - - size_t size() const { return m_rows.size(); } - Vector headers() const; - [[nodiscard]] bool has_explicit_headers() const { return (static_cast(m_behaviors) & static_cast(ParserBehavior::ReadHeaders)) != 0; } - - class Row { - public: - explicit Row(XSV& xsv, size_t index) - : m_xsv(xsv) - , m_index(index) - { - } - - StringView operator[](StringView name) const; - StringView operator[](size_t column) const; - - template - StringView at(T column) const { return this->operator[](column); } - - size_t index() const { return m_index; } - size_t size() const { return m_xsv.headers().size(); } - - using ConstIterator = AK::SimpleIterator; - using Iterator = AK::SimpleIterator; - - constexpr ConstIterator begin() const { return ConstIterator::begin(*this); } - constexpr Iterator begin() { return Iterator::begin(*this); } - - constexpr ConstIterator end() const { return ConstIterator::end(*this); } - constexpr Iterator end() { return Iterator::end(*this); } - - private: - XSV& m_xsv; - size_t m_index { 0 }; - }; - - template - class RowIterator { - public: - explicit RowIterator(const XSV& xsv, size_t init_index = 0) - requires(const_) - : m_xsv(const_cast(xsv)) - , m_index(init_index) - { - } - - explicit RowIterator(XSV& xsv, size_t init_index = 0) - requires(!const_) - : m_xsv(xsv) - , m_index(init_index) - { - } - - Row operator*() const { return Row { m_xsv, m_index }; } - Row operator*() - requires(!const_) - { - return Row { m_xsv, m_index }; - } - - RowIterator& operator++() - { - ++m_index; - return *this; - } - - bool is_end() const { return m_index == m_xsv.m_rows.size(); } - bool operator==(RowIterator const& other) const - { - return m_index == other.m_index && &m_xsv == &other.m_xsv; - } - bool operator==(RowIterator const& other) const - { - return m_index == other.m_index && &m_xsv == &other.m_xsv; - } - - constexpr size_t index() const { return m_index; } - - private: - XSV& m_xsv; - size_t m_index { 0 }; - }; - - Row const operator[](size_t index) const; - Row operator[](size_t index); - - Row at(size_t index) const; - - auto begin() { return RowIterator(*this); } - auto end() { return RowIterator(*this, m_rows.size()); } - - auto begin() const { return RowIterator(*this); } - auto end() const { return RowIterator(*this, m_rows.size()); } - - using ConstIterator = RowIterator; - using Iterator = RowIterator; - -private: - struct Field { - StringView as_string_view; - ByteString as_string; // This member only used if the parser couldn't use the original source verbatim. - bool is_string_view { true }; - - bool operator==(StringView other) const - { - if (is_string_view) - return other == as_string_view; - return as_string == other; - } - }; - void set_error(ReadError error); - void parse_preview(); - void read_headers(); - void reset() - { - m_lexer = GenericLexer { m_source }; - m_rows.clear(); - m_names.clear(); - m_error = ReadError::None; - } - Vector read_row(bool header_row = false); - Field read_one_field(); - Field read_one_quoted_field(); - Field read_one_unquoted_field(); - - StringView m_source; - GenericLexer m_lexer; - ParserTraits m_traits; - ParserBehavior m_behaviors; - Vector m_names; - Vector> m_rows; - ReadError m_error { ReadError::None }; -}; - -} diff --git a/Userland/Applications/Spreadsheet/Spreadsheet.cpp b/Userland/Applications/Spreadsheet/Spreadsheet.cpp deleted file mode 100644 index 9bde4cd21d2..00000000000 --- a/Userland/Applications/Spreadsheet/Spreadsheet.cpp +++ /dev/null @@ -1,774 +0,0 @@ -/* - * Copyright (c) 2020-2022, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "Spreadsheet.h" -#include "JSIntegration.h" -#include "Workbook.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace Spreadsheet { - -Sheet::Sheet(StringView name, Workbook& workbook) - : Sheet(workbook) -{ - m_name = name; - - for (size_t i = 0; i < default_row_count; ++i) - add_row(); - - for (size_t i = 0; i < default_column_count; ++i) - add_column(); -} - -Sheet::Sheet(Workbook& workbook) - : m_workbook(workbook) - , m_vm(workbook.vm()) - , m_root_execution_context(JS::create_simple_execution_context(m_workbook.vm(), *this)) -{ - auto& vm = m_workbook.vm(); - JS::DeferGC defer_gc(vm.heap()); - auto& realm = *m_root_execution_context->realm; - m_global_object = static_cast(&realm.global_object()); - global_object().define_direct_property("workbook", m_workbook.workbook_object(), JS::default_attributes); - global_object().define_direct_property("thisSheet", &global_object(), JS::default_attributes); // Self-reference is unfortunate, but required. - - // Sadly, these have to be evaluated once per sheet. - constexpr auto runtime_file_path = "/res/js/Spreadsheet/runtime.js"sv; - auto file_or_error = Core::File::open(runtime_file_path, Core::File::OpenMode::Read); - if (!file_or_error.is_error()) { - auto buffer = file_or_error.value()->read_until_eof().release_value_but_fixme_should_propagate_errors(); - auto script_or_error = JS::Script::parse(buffer, realm, runtime_file_path); - if (script_or_error.is_error()) { - warnln("Spreadsheet: Failed to parse runtime code"); - for (auto& error : script_or_error.error()) { - // FIXME: This doesn't print hints anymore - warnln("SyntaxError: {}", error.to_byte_string()); - } - } else { - auto result = vm.bytecode_interpreter().run(script_or_error.value()); - if (result.is_error()) { - warnln("Spreadsheet: Failed to run runtime code:"); - auto thrown_value = *result.throw_completion().value(); - warn("Threw: {}", thrown_value.to_string_without_side_effects()); - if (thrown_value.is_object() && is(thrown_value.as_object())) { - auto& error = static_cast(thrown_value.as_object()); - warnln(" with message '{}'", error.get_without_side_effects(vm.names.message)); - dbgln("{}", error.stack_string(JS::CompactTraceback::Yes)); - } else { - warnln(); - } - } - } - } -} - -size_t Sheet::add_row() -{ - return m_rows++; -} - -static Optional convert_from_string(StringView str, unsigned base = 26, StringView map = {}) -{ - if (map.is_null()) - map = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"sv; - - VERIFY(base >= 2 && base <= map.length()); - - if (str.is_empty()) - return {}; - - size_t value = 0; - auto const len = str.length(); - for (auto i = 0u; i < len; i++) { - auto maybe_index = map.find(str[i]); - if (!maybe_index.has_value()) - return {}; - size_t digit_value = maybe_index.value(); - value += (digit_value + 1) * AK::pow(base, len - 1 - i); - } - - return value - 1; -} - -ByteString Sheet::add_column() -{ - auto next_column = ByteString::bijective_base_from(m_columns.size()); - m_columns.append(next_column); - return next_column; -} - -void Sheet::update() -{ - if (m_should_ignore_updates) { - m_update_requested = true; - return; - } - m_visited_cells_in_update.clear(); - Vector cells_copy; - - // Grab a copy as updates might insert cells into the table. - for (auto& it : m_cells) { - if (it.value->dirty()) { - cells_copy.append(*it.value); - m_workbook.set_dirty(true); - } - } - - for (auto& cell : cells_copy) - update(cell); - - m_visited_cells_in_update.clear(); -} - -void Sheet::update(Cell& cell) -{ - if (m_should_ignore_updates) { - m_update_requested = true; - return; - } - if (cell.dirty()) { - if (has_been_visited(&cell)) { - // This may be part of an cyclic reference chain, - // so just ignore it. - cell.clear_dirty(); - return; - } - m_visited_cells_in_update.set(&cell); - cell.update_data({}); - } -} - -JS::ThrowCompletionOr Sheet::evaluate(StringView source, Cell* on_behalf_of) -{ - TemporaryChange cell_change { m_current_cell_being_evaluated, on_behalf_of }; - auto name = on_behalf_of ? on_behalf_of->name_for_javascript(*this) : "cell "sv; - auto script_or_error = JS::Script::parse( - source, - realm(), - name); - - if (script_or_error.is_error()) - return vm().throw_completion(script_or_error.error().first().to_string()); - - return vm().bytecode_interpreter().run(script_or_error.value()); -} - -Cell* Sheet::at(StringView name) -{ - auto pos = parse_cell_name(name); - if (pos.has_value()) - return at(pos.value()); - - return nullptr; -} - -Cell* Sheet::at(Position const& position) -{ - auto it = m_cells.find(position); - - if (it == m_cells.end()) - return nullptr; - - return it->value; -} - -Optional Sheet::parse_cell_name(StringView name) const -{ - GenericLexer lexer(name); - auto col = lexer.consume_while(isalpha); - auto row = lexer.consume_while(isdigit); - - if (!lexer.is_eof() || row.is_empty() || col.is_empty()) - return {}; - - auto it = m_columns.find(col); - if (it == m_columns.end()) - return {}; - - return Position { it.index(), row.to_number().value() }; -} - -Optional Sheet::column_index(StringView column_name) const -{ - auto maybe_index = convert_from_string(column_name); - if (!maybe_index.has_value()) - return {}; - - auto index = maybe_index.value(); - if (m_columns.size() <= index || m_columns[index] != column_name) { - auto it = m_columns.find(column_name); - if (it == m_columns.end()) - return {}; - index = it.index(); - } - - return index; -} - -Optional Sheet::column_arithmetic(StringView column_name, int offset) -{ - auto maybe_index = column_index(column_name); - if (!maybe_index.has_value()) - return {}; - - if (offset < 0 && maybe_index.value() < (size_t)(0 - offset)) - return m_columns.first(); - - auto index = maybe_index.value() + offset; - if (m_columns.size() > index) - return m_columns[index]; - - for (size_t i = m_columns.size(); i <= index; ++i) - add_column(); - - return m_columns.last(); -} - -Cell* Sheet::from_url(const URL::URL& url) -{ - auto maybe_position = position_from_url(url); - if (!maybe_position.has_value()) - return nullptr; - - return at(maybe_position.value()); -} - -Optional Sheet::position_from_url(const URL::URL& url) const -{ - if (!url.is_valid()) { - dbgln("Invalid url: {}", url.to_byte_string()); - return {}; - } - - if (url.scheme() != "spreadsheet" || url.host() != "cell"_string) { - dbgln("Bad url: {}", url.to_byte_string()); - return {}; - } - - // FIXME: Figure out a way to do this cross-process. - VERIFY(url.serialize_path() == ByteString::formatted("/{}", getpid())); - - return parse_cell_name(url.fragment().value_or(String {})); -} - -Position Sheet::offset_relative_to(Position const& base, Position const& offset, Position const& offset_base) const -{ - if (offset.column >= m_columns.size()) { - dbgln("Column '{}' does not exist!", offset.column); - return base; - } - if (offset_base.column >= m_columns.size()) { - dbgln("Column '{}' does not exist!", offset_base.column); - return base; - } - if (base.column >= m_columns.size()) { - dbgln("Column '{}' does not exist!", base.column); - return offset; - } - - auto new_column = offset.column + base.column - offset_base.column; - auto new_row = offset.row + base.row - offset_base.row; - - return { new_column, new_row }; -} - -Vector Sheet::copy_cells(Vector from, Vector to, Optional resolve_relative_to, CopyOperation copy_operation) -{ - Vector cell_changes; - // Disallow misaligned copies. - if (to.size() > 1 && from.size() != to.size()) { - dbgln("Cannot copy {} cells to {} cells", from.size(), to.size()); - return cell_changes; - } - - Vector target_cells; - for (auto& position : from) - target_cells.append(resolve_relative_to.has_value() ? offset_relative_to(to.first(), position, resolve_relative_to.value()) : to.first()); - - auto copy_to = [&](auto& source_position, Position target_position) { - auto& target_cell = ensure(target_position); - auto* source_cell = at(source_position); - auto previous_data = target_cell.data(); - - if (!source_cell) { - target_cell.set_data(""); - cell_changes.append(CellChange(target_cell, previous_data)); - return; - } - - target_cell.copy_from(*source_cell); - cell_changes.append(CellChange(target_cell, previous_data)); - if (copy_operation == CopyOperation::Cut && !target_cells.contains_slow(source_position)) { - cell_changes.append(CellChange(*source_cell, source_cell->data())); - source_cell->set_data(""); - } - }; - - // Resolve each index as relative to the first index offset from the selection. - auto& target = to.first(); - - auto top_left_most_position_from = from.first(); - auto bottom_right_most_position_from = from.first(); - for (auto& position : from) { - if (position.row > bottom_right_most_position_from.row) - bottom_right_most_position_from = position; - else if (position.column > bottom_right_most_position_from.column) - bottom_right_most_position_from = position; - - if (position.row < top_left_most_position_from.row) - top_left_most_position_from = position; - else if (position.column < top_left_most_position_from.column) - top_left_most_position_from = position; - } - - Vector ordered_from; - auto current_column = top_left_most_position_from.column; - auto current_row = top_left_most_position_from.row; - for ([[maybe_unused]] auto& position : from) { - for (auto& position : from) - if (position.row == current_row && position.column == current_column) - ordered_from.append(position); - - if (current_column >= bottom_right_most_position_from.column) { - current_column = top_left_most_position_from.column; - current_row += 1; - } else { - current_column += 1; - } - } - - if (target.row > top_left_most_position_from.row || (target.row >= top_left_most_position_from.row && target.column > top_left_most_position_from.column)) - ordered_from.reverse(); - - for (auto& position : ordered_from) { - dbgln_if(COPY_DEBUG, "Paste from '{}' to '{}'", position.to_url(*this), target.to_url(*this)); - copy_to(position, resolve_relative_to.has_value() ? offset_relative_to(target, position, resolve_relative_to.value()) : target); - } - - return cell_changes; -} - -RefPtr Sheet::from_json(JsonObject const& object, Workbook& workbook) -{ - auto sheet = adopt_ref(*new Sheet(workbook)); - auto rows = object.get_u32("rows"sv).value_or(default_row_count); - auto columns = object.get_array("columns"sv); - auto name = object.get_byte_string("name"sv).value_or("Sheet"); - if (object.has("cells"sv) && !object.has_object("cells"sv)) - return {}; - - sheet->set_name(name); - - for (size_t i = 0; i < max(rows, (unsigned)Sheet::default_row_count); ++i) - sheet->add_row(); - - // FIXME: Better error checking. - if (columns.has_value()) { - columns->for_each([&](auto& value) { - sheet->m_columns.append(value.as_string()); - return IterationDecision::Continue; - }); - } - - if (sheet->m_columns.size() < default_column_count && sheet->columns_are_standard()) { - for (size_t i = sheet->m_columns.size(); i < default_column_count; ++i) - sheet->add_column(); - } - - auto json = sheet->global_object().get_without_side_effects("JSON"); - auto& parse_function = json.as_object().get_without_side_effects("parse").as_function(); - - auto read_format = [](auto& format, auto const& obj) { - if (auto value = obj.get_byte_string("foreground_color"sv); value.has_value()) - format.foreground_color = Color::from_string(*value); - if (auto value = obj.get_byte_string("background_color"sv); value.has_value()) - format.background_color = Color::from_string(*value); - }; - - if (auto cells = object.get_object("cells"sv); cells.has_value()) { - cells->for_each_member([&](auto& name, JsonValue const& value) { - auto position_option = sheet->parse_cell_name(name); - if (!position_option.has_value()) - return IterationDecision::Continue; - - auto position = position_option.value(); - auto& obj = value.as_object(); - auto kind = obj.get_byte_string("kind"sv).value_or("LiteralString") == "LiteralString" ? Cell::LiteralString : Cell::Formula; - - OwnPtr cell; - switch (kind) { - case Cell::LiteralString: - cell = make(obj.get_byte_string("value"sv).value_or({}), position, *sheet); - break; - case Cell::Formula: { - auto& vm = sheet->vm(); - auto value_or_error = JS::call(vm, parse_function, json, JS::PrimitiveString::create(vm, obj.get_byte_string("value"sv).value_or({}))); - if (value_or_error.is_error()) { - warnln("Failed to load previous value for cell {}, leaving as undefined", position.to_cell_identifier(sheet)); - value_or_error = JS::js_undefined(); - } - cell = make(obj.get_byte_string("source"sv).value_or({}), value_or_error.release_value(), position, *sheet); - break; - } - } - - auto type_name = obj.has("type"sv) ? obj.get_byte_string("type"sv).value_or({}) : "Numeric"; - cell->set_type(type_name); - - auto type_meta = obj.get_object("type_metadata"sv); - if (type_meta.has_value()) { - auto& meta_obj = type_meta.value(); - auto meta = cell->type_metadata(); - if (auto value = meta_obj.get_i32("length"sv); value.has_value()) - meta.length = value.value(); - if (auto value = meta_obj.get_byte_string("format"sv); value.has_value()) - meta.format = value.value(); - if (auto value = meta_obj.get_byte_string("alignment"sv); value.has_value()) { - auto alignment = Gfx::text_alignment_from_string(*value); - if (alignment.has_value()) - meta.alignment = alignment.value(); - } - read_format(meta.static_format, meta_obj); - - cell->set_type_metadata(move(meta)); - } - - auto conditional_formats = obj.get_array("conditional_formats"sv); - auto cformats = cell->conditional_formats(); - if (conditional_formats.has_value()) { - conditional_formats->for_each([&](auto const& fmt_val) { - if (!fmt_val.is_object()) - return IterationDecision::Continue; - - auto& fmt_obj = fmt_val.as_object(); - auto fmt_cond = fmt_obj.get_byte_string("condition"sv).value_or({}); - if (fmt_cond.is_empty()) - return IterationDecision::Continue; - - ConditionalFormat fmt; - fmt.condition = move(fmt_cond); - read_format(fmt, fmt_obj); - cformats.append(move(fmt)); - - return IterationDecision::Continue; - }); - cell->set_conditional_formats(move(cformats)); - } - - auto evaluated_format = obj.get_object("evaluated_formats"sv); - if (evaluated_format.has_value()) { - auto& evaluated_format_obj = evaluated_format.value(); - auto& evaluated_fmts = cell->evaluated_formats(); - - read_format(evaluated_fmts, evaluated_format_obj); - } - - sheet->m_cells.set(position, cell.release_nonnull()); - return IterationDecision::Continue; - }); - } - - return sheet; -} - -Position Sheet::written_data_bounds(Optional column_index) const -{ - Position bound; - for (auto const& entry : m_cells) { - if (entry.value->data().is_empty()) - continue; - if (column_index.has_value() && entry.key.column != *column_index) - continue; - if (entry.key.row >= bound.row) - bound.row = entry.key.row; - if (entry.key.column >= bound.column) - bound.column = entry.key.column; - } - - return bound; -} - -/// The sheet is allowed to have nonstandard column names -/// this checks whether all existing columns are 'standard' -/// (i.e. as generated by 'String::bijective_base_from()' -bool Sheet::columns_are_standard() const -{ - for (size_t i = 0; i < m_columns.size(); ++i) { - if (m_columns[i] != ByteString::bijective_base_from(i)) - return false; - } - - return true; -} - -JsonObject Sheet::to_json() const -{ - JsonObject object; - object.set("name", m_name); - - auto save_format = [](auto const& format, auto& obj) { - if (format.foreground_color.has_value()) - obj.set("foreground_color", format.foreground_color.value().to_byte_string()); - if (format.background_color.has_value()) - obj.set("background_color", format.background_color.value().to_byte_string()); - }; - - auto bottom_right = written_data_bounds(); - - if (!columns_are_standard()) { - auto columns = JsonArray(); - for (auto& column : m_columns) - columns.must_append(column); - object.set("columns", move(columns)); - } - object.set("rows", bottom_right.row + 1); - - JsonObject cells; - for (auto& it : m_cells) { - StringBuilder builder; - builder.append(column(it.key.column)); - builder.appendff("{}", it.key.row); - auto key = builder.to_byte_string(); - - JsonObject data; - data.set("kind", it.value->kind() == Cell::Kind::Formula ? "Formula" : "LiteralString"); - if (it.value->kind() == Cell::Formula) { - data.set("source", it.value->data()); - auto json = realm().global_object().get_without_side_effects("JSON"); - auto stringified_or_error = JS::call(vm(), json.as_object().get_without_side_effects("stringify").as_function(), json, it.value->evaluated_data()); - VERIFY(!stringified_or_error.is_error()); - data.set("value", stringified_or_error.release_value().to_string_without_side_effects().to_byte_string()); - } else { - data.set("value", it.value->data()); - } - - // Set type & meta - auto& type = it.value->type(); - auto& meta = it.value->type_metadata(); - data.set("type", type.name()); - - JsonObject metadata_object; - metadata_object.set("length", meta.length); - metadata_object.set("format", meta.format); - metadata_object.set("alignment", Gfx::to_string(meta.alignment)); - save_format(meta.static_format, metadata_object); - - data.set("type_metadata", move(metadata_object)); - - // Set conditional formats - JsonArray conditional_formats; - for (auto& fmt : it.value->conditional_formats()) { - JsonObject fmt_object; - fmt_object.set("condition", fmt.condition); - save_format(fmt, fmt_object); - - conditional_formats.must_append(move(fmt_object)); - } - - data.set("conditional_formats", move(conditional_formats)); - - auto& evaluated_formats = it.value->evaluated_formats(); - JsonObject evaluated_formats_obj; - - save_format(evaluated_formats, evaluated_formats_obj); - data.set("evaluated_formats", move(evaluated_formats_obj)); - - cells.set(key, move(data)); - } - object.set("cells", move(cells)); - - return object; -} - -Vector> Sheet::to_xsv() const -{ - Vector> data; - - auto bottom_right = written_data_bounds(); - - // First row = headers. - size_t column_count = m_columns.size(); - if (columns_are_standard()) { - column_count = bottom_right.column + 1; - Vector cols; - for (size_t i = 0; i < column_count; ++i) - cols.append(m_columns[i]); - data.append(move(cols)); - } else { - data.append(m_columns); - } - - for (size_t i = 0; i <= bottom_right.row; ++i) { - Vector row; - row.resize(column_count); - for (size_t j = 0; j < column_count; ++j) { - auto cell = at({ j, i }); - if (cell) { - auto result = cell->typed_display(); - if (result.has_value()) - row[j] = result.value(); - } - } - - data.append(move(row)); - } - - return data; -} - -RefPtr Sheet::from_xsv(Reader::XSV const& xsv, Workbook& workbook) -{ - auto cols = xsv.headers(); - auto rows = xsv.size(); - - auto sheet = adopt_ref(*new Sheet(workbook)); - if (xsv.has_explicit_headers()) { - sheet->m_columns = cols; - } else { - sheet->m_columns.ensure_capacity(cols.size()); - for (size_t i = 0; i < cols.size(); ++i) - sheet->m_columns.append(ByteString::bijective_base_from(i)); - } - for (size_t i = 0; i < max(rows, Sheet::default_row_count); ++i) - sheet->add_row(); - if (sheet->columns_are_standard()) { - for (size_t i = sheet->m_columns.size(); i < Sheet::default_column_count; ++i) - sheet->add_column(); - } - - for (auto row : xsv) { - for (size_t i = 0; i < cols.size(); ++i) { - auto str = row[i]; - if (str.is_empty()) - continue; - Position position { i, row.index() }; - auto cell = make(str, position, *sheet); - sheet->m_cells.set(position, move(cell)); - } - } - - return sheet; -} - -JsonObject Sheet::gather_documentation() const -{ - JsonObject object; - const JS::PropertyKey doc_name { "__documentation" }; - - auto add_docs_from = [&](auto& it, auto& global_object) { - auto value = global_object.get(it.key).release_value(); - if (!value.is_function() && !value.is_object()) - return; - - auto& value_object = value.is_object() ? value.as_object() : value.as_function(); - if (!value_object.has_own_property(doc_name).release_value()) - return; - - auto doc = value_object.get(doc_name).release_value(); - if (!doc.is_string()) - return; - - JsonParser parser(doc.to_string_without_side_effects()); - auto doc_object = parser.parse(); - - if (!doc_object.is_error()) - object.set(it.key.to_display_string(), doc_object.value()); - else - dbgln("Sheet::gather_documentation(): Failed to parse the documentation for '{}'!", it.key.to_display_string()); - }; - - for (auto& it : realm().global_object().shape().property_table()) - add_docs_from(it, realm().global_object()); - - for (auto& it : global_object().shape().property_table()) - add_docs_from(it, global_object()); - - m_cached_documentation = move(object); - return m_cached_documentation.value(); -} - -ByteString Sheet::generate_inline_documentation_for(StringView function, size_t argument_index) -{ - if (!m_cached_documentation.has_value()) - gather_documentation(); - - auto& docs = m_cached_documentation.value(); - auto entry = docs.get_object(function); - if (!entry.has_value()) - return ByteString::formatted("{}(...???{})", function, argument_index); - - auto& entry_object = entry.value(); - size_t argc = entry_object.get_integer("argc"sv).value_or(0); - auto argnames_value = entry_object.get_array("argnames"sv); - if (!argnames_value.has_value()) - return ByteString::formatted("{}(...{}???{})", function, argc, argument_index); - auto& argnames = argnames_value.value(); - StringBuilder builder; - builder.appendff("{}(", function); - for (size_t i = 0; i < (size_t)argnames.size(); ++i) { - if (i != 0 && i < (size_t)argnames.size()) - builder.append(", "sv); - if (i == argument_index) - builder.append('<'); - else if (i >= argc) - builder.append('['); - builder.append(argnames[i].as_string()); - if (i == argument_index) - builder.append('>'); - else if (i >= argc) - builder.append(']'); - } - - builder.append(')'); - return builder.to_byte_string(); -} - -ByteString Position::to_cell_identifier(Sheet const& sheet) const -{ - return ByteString::formatted("{}{}", sheet.column(column), row); -} - -URL::URL Position::to_url(Sheet const& sheet) const -{ - URL::URL url; - url.set_scheme("spreadsheet"_string); - url.set_host("cell"_string); - url.set_paths({ ByteString::number(getpid()) }); - url.set_fragment(String::from_byte_string(to_cell_identifier(sheet)).release_value()); - return url; -} - -CellChange::CellChange(Cell& cell, ByteString const& previous_data) - : m_cell(cell) - , m_previous_data(previous_data) -{ - m_new_data = cell.data(); -} - -CellChange::CellChange(Cell& cell, CellTypeMetadata const& previous_type_metadata) - : m_cell(cell) - , m_previous_type_metadata(previous_type_metadata) -{ - m_new_type_metadata = cell.type_metadata(); -} - -} diff --git a/Userland/Applications/Spreadsheet/Spreadsheet.h b/Userland/Applications/Spreadsheet/Spreadsheet.h deleted file mode 100644 index 20e441a939e..00000000000 --- a/Userland/Applications/Spreadsheet/Spreadsheet.h +++ /dev/null @@ -1,190 +0,0 @@ -/* - * Copyright (c) 2020-2022, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include "Cell.h" -#include "Forward.h" -#include "Readers/XSV.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace Spreadsheet { - -class CellChange { -public: - CellChange(Cell&, ByteString const&); - CellChange(Cell&, CellTypeMetadata const&); - - auto& cell() { return m_cell; } - auto& previous_data() { return m_previous_data; } - auto& new_data() { return m_new_data; } - auto& previous_type_metadata() { return m_previous_type_metadata; } - auto& new_type_metadata() { return m_new_type_metadata; } - -private: - Cell& m_cell; - ByteString m_previous_data; - ByteString m_new_data; - CellTypeMetadata m_previous_type_metadata; - CellTypeMetadata m_new_type_metadata; -}; - -class Sheet : public Core::EventReceiver { - C_OBJECT(Sheet); - -public: - constexpr static size_t default_row_count = 100; - constexpr static size_t default_column_count = 26; - - virtual ~Sheet() override = default; - - Optional parse_cell_name(StringView) const; - Optional column_index(StringView column_name) const; - Optional column_arithmetic(StringView column_name, int offset); - - Cell* from_url(const URL::URL&); - Cell const* from_url(const URL::URL& url) const { return const_cast(this)->from_url(url); } - Optional position_from_url(const URL::URL& url) const; - - /// Resolve 'offset' to an absolute position assuming 'base' is at 'offset_base'. - /// Effectively, "Walk the distance between 'offset' and 'offset_base' away from 'base'". - Position offset_relative_to(Position const& base, Position const& offset, Position const& offset_base) const; - - JsonObject to_json() const; - static RefPtr from_json(JsonObject const&, Workbook&); - - Vector> to_xsv() const; - static RefPtr from_xsv(Reader::XSV const&, Workbook&); - - ByteString const& name() const { return m_name; } - void set_name(StringView name) { m_name = name; } - - JsonObject gather_documentation() const; - - HashTable const& selected_cells() const { return m_selected_cells; } - HashTable& selected_cells() { return m_selected_cells; } - HashMap> const& cells() const { return m_cells; } - HashMap>& cells() { return m_cells; } - - Cell* at(Position const& position); - Cell const* at(Position const& position) const { return const_cast(this)->at(position); } - - Cell const* at(StringView name) const { return const_cast(this)->at(name); } - Cell* at(StringView); - - Cell const& ensure(Position const& position) const { return const_cast(this)->ensure(position); } - Cell& ensure(Position const& position) - { - if (auto cell = at(position)) - return *cell; - - m_cells.set(position, make(ByteString::empty(), position, *this)); - return *at(position); - } - - size_t add_row(); - ByteString add_column(); - - size_t row_count() const { return m_rows; } - size_t column_count() const { return m_columns.size(); } - Vector const& columns() const { return m_columns; } - ByteString const& column(size_t index) - { - for (size_t i = column_count(); i < index; ++i) - add_column(); - - VERIFY(column_count() > index); - return m_columns[index]; - } - ByteString const& column(size_t index) const - { - VERIFY(column_count() > index); - return m_columns[index]; - } - - void update(); - void update(Cell&); - void disable_updates() { m_should_ignore_updates = true; } - void enable_updates() - { - m_should_ignore_updates = false; - if (m_update_requested) { - m_update_requested = false; - update(); - } - } - - JS::ThrowCompletionOr evaluate(StringView, Cell* = nullptr); - SheetGlobalObject& global_object() const { return *m_global_object; } - - Cell*& current_evaluated_cell() { return m_current_cell_being_evaluated; } - bool has_been_visited(Cell* cell) const { return m_visited_cells_in_update.contains(cell); } - - Workbook const& workbook() const { return m_workbook; } - - enum class CopyOperation { - Copy, - Cut - }; - - Vector copy_cells(Vector from, Vector to, Optional resolve_relative_to = {}, CopyOperation copy_operation = CopyOperation::Copy); - - /// Gives the bottom-right corner of the smallest bounding box containing all the written data, optionally limited to the given column. - Position written_data_bounds(Optional column_index = {}) const; - - bool columns_are_standard() const; - - ByteString generate_inline_documentation_for(StringView function, size_t argument_index); - - JS::Realm& realm() const { return *m_root_execution_context->realm; } - JS::VM& vm() const { return realm().vm(); } - -private: - explicit Sheet(Workbook&); - explicit Sheet(StringView name, Workbook&); - - ByteString m_name; - Vector m_columns; - size_t m_rows { 0 }; - HashMap> m_cells; - HashTable m_selected_cells; - - Workbook& m_workbook; - mutable JS::GCPtr m_global_object; - - NonnullRefPtr m_vm; - NonnullOwnPtr m_root_execution_context; - - Cell* m_current_cell_being_evaluated { nullptr }; - - HashTable m_visited_cells_in_update; - bool m_should_ignore_updates { false }; - bool m_update_requested { false }; - mutable Optional m_cached_documentation; -}; - -} - -namespace AK { - -template<> -struct Traits : public DefaultTraits { - static constexpr bool is_trivial() { return false; } - static unsigned hash(Spreadsheet::Position const& p) - { - return p.hash(); - } -}; - -} diff --git a/Userland/Applications/Spreadsheet/SpreadsheetModel.cpp b/Userland/Applications/Spreadsheet/SpreadsheetModel.cpp deleted file mode 100644 index e4693e57d81..00000000000 --- a/Userland/Applications/Spreadsheet/SpreadsheetModel.cpp +++ /dev/null @@ -1,244 +0,0 @@ -/* - * Copyright (c) 2020-2022, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "SpreadsheetModel.h" -#include "ConditionalFormatting.h" -#include -#include -#include -#include - -namespace Spreadsheet { - -GUI::Variant SheetModel::data(const GUI::ModelIndex& index, GUI::ModelRole role) const -{ - if (!index.is_valid()) - return {}; - - if (role == GUI::ModelRole::Display) { - auto const* cell = m_sheet->at({ (size_t)index.column(), (size_t)index.row() }); - if (!cell) - return ByteString::empty(); - - Function to_byte_string_as_exception = [&](JS::Value value) { - auto& vm = cell->sheet().global_object().vm(); - StringBuilder builder; - builder.append("Error: "sv); - if (value.is_object()) { - auto& object = value.as_object(); - if (is(object)) { - auto message = object.get_without_side_effects("message"); - auto error = message.to_byte_string(vm); - if (error.is_throw_completion()) - builder.append(message.to_string_without_side_effects()); - else - builder.append(error.release_value()); - return builder.to_byte_string(); - } - } - auto error_message = value.to_byte_string(vm); - - if (error_message.is_throw_completion()) - return to_byte_string_as_exception(*error_message.release_error().value()); - - builder.append(error_message.release_value()); - return builder.to_byte_string(); - }; - - if (cell->kind() == Spreadsheet::Cell::Formula) { - if (auto opt_throw_value = cell->thrown_value(); opt_throw_value.has_value()) - return to_byte_string_as_exception(*opt_throw_value); - } - - auto display = cell->typed_display(); - if (display.is_error()) - return to_byte_string_as_exception(*display.release_error().value()); - - return display.release_value(); - } - - if (role == GUI::ModelRole::MimeData) - return Position { (size_t)index.column(), (size_t)index.row() }.to_url(m_sheet).to_byte_string(); - - if (role == GUI::ModelRole::TextAlignment) { - auto const* cell = m_sheet->at({ (size_t)index.column(), (size_t)index.row() }); - if (!cell) - return {}; - - return cell->type_metadata().alignment; - } - - if (role == GUI::ModelRole::ForegroundColor) { - auto const* cell = m_sheet->at({ (size_t)index.column(), (size_t)index.row() }); - if (!cell) - return {}; - - if (cell->kind() == Spreadsheet::Cell::Formula) { - if (cell->thrown_value().has_value()) - return Color(Color::Red); - } - - if (cell->evaluated_formats().foreground_color.has_value()) - return cell->evaluated_formats().foreground_color.value(); - - if (auto color = cell->type_metadata().static_format.foreground_color; color.has_value()) - return color.value(); - - return {}; - } - - if (role == GUI::ModelRole::BackgroundColor) { - auto const* cell = m_sheet->at({ (size_t)index.column(), (size_t)index.row() }); - if (!cell) - return {}; - - if (cell->evaluated_formats().background_color.has_value()) - return cell->evaluated_formats().background_color.value(); - - if (auto color = cell->type_metadata().static_format.background_color; color.has_value()) - return color.value(); - - return {}; - } - - if (to_underlying(role) == to_underlying(Role::Tooltip)) { - auto const* cell = m_sheet->at({ (size_t)index.column(), (size_t)index.row() }); - if (!cell || !cell->thrown_value().has_value()) - return {}; - - auto value = cell->thrown_value().value(); - if (!value.is_object()) - return {}; - - auto& object = value.as_object(); - if (!is(object)) - return {}; - - auto& error = static_cast(object); - auto const& trace = error.traceback(); - StringBuilder builder; - builder.appendff("{}\n", error.get_without_side_effects(object.vm().names.message).to_string_without_side_effects()); - for (auto const& frame : trace.in_reverse()) { - if (frame.source_range().filename().contains("runtime.js"sv)) { - if (frame.function_name == "") - builder.appendff(" in a builtin function at line {}, column {}\n", frame.source_range().start.line, frame.source_range().start.column); - else - builder.appendff(" while evaluating builtin '{}'\n", frame.function_name); - } else if (frame.source_range().filename().starts_with("cell "sv)) { - auto filename = frame.source_range().filename(); - builder.appendff(" in cell '{}', at line {}, column {}\n", filename.substring_view(5), frame.source_range().start.line, frame.source_range().start.column); - } - } - return builder.to_byte_string(); - } - - return {}; -} - -RefPtr SheetModel::mime_data(const GUI::ModelSelection& selection) const -{ - auto mime_data = GUI::Model::mime_data(selection); - - bool first = true; - const GUI::ModelIndex* cursor = nullptr; - const_cast(this)->for_each_view([&](const GUI::AbstractView& view) { - if (!first) - return; - cursor = &view.cursor_index(); - first = false; - }); - - VERIFY(cursor); - - Position cursor_position { (size_t)cursor->column(), (size_t)cursor->row() }; - auto mime_data_buffer = mime_data->data("text/x-spreadsheet-data"sv); - auto new_data = ByteString::formatted("{}\n{}", - cursor_position.to_url(m_sheet).to_byte_string(), - StringView(mime_data_buffer)); - mime_data->set_data("text/x-spreadsheet-data"_string, new_data.to_byte_buffer()); - - return mime_data; -} - -ErrorOr SheetModel::column_name(int index) const -{ - if (index < 0) - return String {}; - - return TRY(String::from_byte_string(m_sheet->column(index))); -} - -bool SheetModel::is_editable(const GUI::ModelIndex& index) const -{ - if (!index.is_valid()) - return false; - - return true; -} - -void SheetModel::set_data(const GUI::ModelIndex& index, const GUI::Variant& value) -{ - if (!index.is_valid()) - return; - - auto& cell = m_sheet->ensure({ (size_t)index.column(), (size_t)index.row() }); - auto previous_data = cell.data(); - cell.set_data(value.to_byte_string()); - if (on_cell_data_change) - on_cell_data_change(cell, previous_data); - did_update(UpdateFlag::DontInvalidateIndices); -} - -void SheetModel::update() -{ - m_sheet->update(); - did_update(UpdateFlag::DontInvalidateIndices | Model::UpdateFlag::DontResizeColumns); -} - -CellsUndoCommand::CellsUndoCommand(Vector cell_changes) -{ - m_cell_changes = cell_changes; -} - -CellsUndoCommand::CellsUndoCommand(Cell& cell, ByteString const& previous_data) -{ - m_cell_changes.append(CellChange(cell, previous_data)); -} - -void CellsUndoCommand::undo() -{ - for (size_t i = 0; i < m_cell_changes.size(); ++i) { - m_cell_changes[i].cell().set_data(m_cell_changes[i].previous_data()); - } -} - -void CellsUndoCommand::redo() -{ - for (size_t i = 0; i < m_cell_changes.size(); ++i) { - m_cell_changes[i].cell().set_data(m_cell_changes[i].new_data()); - } -} - -CellsUndoMetadataCommand::CellsUndoMetadataCommand(Vector cell_changes) -{ - m_cell_changes = move(cell_changes); -} - -void CellsUndoMetadataCommand::undo() -{ - for (size_t i = 0; i < m_cell_changes.size(); ++i) { - m_cell_changes[i].cell().set_type_metadata(m_cell_changes[i].previous_type_metadata()); - } -} - -void CellsUndoMetadataCommand::redo() -{ - for (size_t i = 0; i < m_cell_changes.size(); ++i) { - m_cell_changes[i].cell().set_type_metadata(m_cell_changes[i].new_type_metadata()); - } -} - -} diff --git a/Userland/Applications/Spreadsheet/SpreadsheetModel.h b/Userland/Applications/Spreadsheet/SpreadsheetModel.h deleted file mode 100644 index 49267320703..00000000000 --- a/Userland/Applications/Spreadsheet/SpreadsheetModel.h +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright (c) 2020-2022, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include "Spreadsheet.h" -#include - -namespace Spreadsheet { - -class SheetModel final : public GUI::Model { -public: - enum class Role : UnderlyingType { - _Custom = to_underlying(GUI::ModelRole::Custom), - Tooltip, - }; - - static NonnullRefPtr create(Sheet& sheet) { return adopt_ref(*new SheetModel(sheet)); } - virtual ~SheetModel() override = default; - - virtual int row_count(const GUI::ModelIndex& = GUI::ModelIndex()) const override { return m_sheet->row_count(); } - virtual int column_count(const GUI::ModelIndex& = GUI::ModelIndex()) const override { return m_sheet->column_count(); } - virtual ErrorOr column_name(int) const override; - virtual GUI::Variant data(const GUI::ModelIndex&, GUI::ModelRole) const override; - virtual RefPtr mime_data(const GUI::ModelSelection&) const override; - virtual bool is_editable(const GUI::ModelIndex&) const override; - virtual void set_data(const GUI::ModelIndex&, const GUI::Variant&) override; - virtual bool is_column_sortable(int) const override { return false; } - virtual StringView drag_data_type() const override { return "text/x-spreadsheet-data"sv; } - Sheet& sheet() { return *m_sheet; } - - void update(); - - Function on_cell_data_change; - Function)> on_cells_data_change; - -private: - explicit SheetModel(Sheet& sheet) - : m_sheet(sheet) - { - } - - NonnullRefPtr m_sheet; -}; - -class CellsUndoCommand : public GUI::Command { -public: - CellsUndoCommand(Cell&, ByteString const&); - CellsUndoCommand(Vector); - - virtual void undo() override; - virtual void redo() override; - -private: - Vector m_cell_changes; -}; - -class CellsUndoMetadataCommand : public GUI::Command { -public: - CellsUndoMetadataCommand(Vector); - - virtual void undo() override; - virtual void redo() override; - -private: - Vector m_cell_changes; -}; - -} diff --git a/Userland/Applications/Spreadsheet/SpreadsheetView.cpp b/Userland/Applications/Spreadsheet/SpreadsheetView.cpp deleted file mode 100644 index 0a052ca9d3a..00000000000 --- a/Userland/Applications/Spreadsheet/SpreadsheetView.cpp +++ /dev/null @@ -1,501 +0,0 @@ -/* - * Copyright (c) 2020-2022, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "SpreadsheetView.h" -#include "CellTypeDialog.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace Spreadsheet { - -void SpreadsheetView::EditingDelegate::set_value(GUI::Variant const& value, GUI::ModelEditingDelegate::SelectionBehavior selection_behavior) -{ - if (!value.is_valid()) { - StringModelEditingDelegate::set_value("", selection_behavior); - commit(); - return; - } - - if (m_has_set_initial_value) - return StringModelEditingDelegate::set_value(value, selection_behavior); - - m_has_set_initial_value = true; - auto const option = m_sheet.at({ (size_t)index().column(), (size_t)index().row() }); - if (option) - return StringModelEditingDelegate::set_value(option->source(), selection_behavior); - - StringModelEditingDelegate::set_value("", selection_behavior); -} - -void InfinitelyScrollableTableView::did_scroll() -{ - TableView::did_scroll(); - auto& vscrollbar = vertical_scrollbar(); - auto& hscrollbar = horizontal_scrollbar(); - if (!m_vertical_scroll_end_timer->is_active()) { - if (vscrollbar.is_visible() && vscrollbar.value() == vscrollbar.max()) { - m_vertical_scroll_end_timer->on_timeout = [&] { - if (on_reaching_vertical_end) - on_reaching_vertical_end(); - m_vertical_scroll_end_timer->stop(); - }; - m_vertical_scroll_end_timer->start(50); - } - } - if (!m_horizontal_scroll_end_timer->is_active()) { - if (hscrollbar.is_visible() && hscrollbar.value() == hscrollbar.max()) { - m_horizontal_scroll_end_timer->on_timeout = [&] { - if (on_reaching_horizontal_end) - on_reaching_horizontal_end(); - m_horizontal_scroll_end_timer->stop(); - }; - m_horizontal_scroll_end_timer->start(50); - } - } -} - -void InfinitelyScrollableTableView::mousemove_event(GUI::MouseEvent& event) -{ - if (auto model = this->model()) { - auto index = index_at_event_position(event.position()); - if (!index.is_valid()) - return TableView::mousemove_event(event); - - auto& sheet = static_cast(*model).sheet(); - sheet.disable_updates(); - ScopeGuard sheet_update_enabler { [&] { sheet.enable_updates(); } }; - - if (!is_dragging()) { - auto tooltip = model->data(index, static_cast(SheetModel::Role::Tooltip)); - if (tooltip.is_string()) { - set_tooltip(MUST(String::from_byte_string(tooltip.as_string()))); - show_or_hide_tooltip(); - } else { - set_tooltip({}); - } - } - - m_is_hovering_cut_zone = false; - m_is_hovering_extend_zone = false; - if (selection().size() > 0 && !m_is_dragging_for_select) { - // Get top-left and bottom-right most cells of selection - auto bottom_right_most_index = selection().first(); - auto top_left_most_index = selection().first(); - selection().for_each_index([&](auto& index) { - if (index.row() > bottom_right_most_index.row()) - bottom_right_most_index = index; - else if (index.column() > bottom_right_most_index.column()) - bottom_right_most_index = index; - if (index.row() < top_left_most_index.row()) - top_left_most_index = index; - else if (index.column() < top_left_most_index.column()) - top_left_most_index = index; - }); - - auto top_left_rect = content_rect_minus_scrollbars(top_left_most_index); - auto bottom_right_rect = content_rect_minus_scrollbars(bottom_right_most_index); - auto distance_tl = top_left_rect.center() - event.position(); - auto distance_br = bottom_right_rect.center() - event.position(); - auto is_over_top_line = false; - auto is_over_bottom_line = false; - auto is_over_left_line = false; - auto is_over_right_line = false; - - // If cursor is within the bounds of the selection - auto select_padding = 2; - if ((distance_br.y() >= -(bottom_right_rect.height() / 2 + select_padding)) && (distance_tl.y() <= (top_left_rect.height() / 2 + select_padding)) && (distance_br.x() >= -(bottom_right_rect.width() / 2 + select_padding)) && (distance_tl.x() <= (top_left_rect.width() / 2 + select_padding))) { - if (distance_tl.y() >= (top_left_rect.height() / 2 - select_padding)) - is_over_top_line = true; - else if (distance_br.y() <= -(bottom_right_rect.height() / 2 - select_padding)) - is_over_bottom_line = true; - - if (distance_tl.x() >= (top_left_rect.width() / 2 - select_padding)) - is_over_left_line = true; - else if (distance_br.x() <= -(bottom_right_rect.width() / 2 - select_padding)) - is_over_right_line = true; - } - - if (is_over_bottom_line && is_over_right_line) { - m_target_cell = bottom_right_most_index; - m_is_hovering_extend_zone = true; - } else if (is_over_top_line || is_over_bottom_line || is_over_left_line || is_over_right_line) { - m_target_cell = top_left_most_index; - m_is_hovering_cut_zone = true; - } - } - - if (m_is_hovering_cut_zone || m_is_dragging_for_cut) - set_override_cursor(Gfx::StandardCursor::Drag); - else if (m_is_hovering_extend_zone || m_is_dragging_for_extend) - set_override_cursor(Gfx::StandardCursor::Crosshair); - else - set_override_cursor(Gfx::StandardCursor::Arrow); - - auto holding_left_button = !!(event.buttons() & GUI::MouseButton::Primary); - if (m_is_dragging_for_cut) { - m_is_dragging_for_select = false; - if (holding_left_button) { - m_has_committed_to_cutting = true; - } - } else if (!m_is_dragging_for_select) { - if (!holding_left_button) { - m_starting_selection_index = index; - } else { - m_is_dragging_for_select = true; - m_might_drag = false; - } - } - - if (!m_has_committed_to_extending) { - if (m_is_dragging_for_extend && holding_left_button) { - m_has_committed_to_extending = true; - m_starting_selection_index = m_target_cell; - } - } - - if (holding_left_button && m_is_dragging_for_select && !m_has_committed_to_cutting) { - if (!m_starting_selection_index.is_valid()) - m_starting_selection_index = index; - - Vector new_selection; - for (auto i = min(m_starting_selection_index.row(), index.row()), imax = max(m_starting_selection_index.row(), index.row()); i <= imax; ++i) { - for (auto j = min(m_starting_selection_index.column(), index.column()), jmax = max(m_starting_selection_index.column(), index.column()); j <= jmax; ++j) { - auto index = model->index(i, j); - if (index.is_valid()) - new_selection.append(move(index)); - } - } - - if (!event.ctrl()) - selection().clear(); - - // Since the extend function has similarities to the select, then do - // a check within the selection process to see if extending. - if (m_has_committed_to_extending) { - if (index.row() == m_target_cell.row() || index.column() == m_target_cell.column()) - selection().add_all(new_selection); - else - // Prevent the target cell from being unselected in the cases - // when extending in a direction that is not in the same column or - // row as the same. (Extension can only be done linearly, not diagonally) - selection().add(m_target_cell); - } else { - selection().add_all(new_selection); - } - } - } - - TableView::mousemove_event(event); -} - -void InfinitelyScrollableTableView::mousedown_event(GUI::MouseEvent& event) -{ - // Override the mouse event so that the cell that is 'clicked' is not - // the one right beneath the cursor but instead the one that is referred to - // when m_is_hovering_cut_zone as it can be the case that the user is targeting - // a cell yet be outside of its bounding box due to the select_padding. - if (m_is_hovering_cut_zone || m_is_hovering_extend_zone) { - if (m_is_hovering_cut_zone) - m_is_dragging_for_cut = true; - else if (m_is_hovering_extend_zone) - m_is_dragging_for_extend = true; - auto rect = content_rect_minus_scrollbars(m_target_cell); - GUI::MouseEvent adjusted_event = { (GUI::Event::Type)event.type(), rect.center(), event.buttons(), event.button(), event.modifiers(), event.wheel_delta_x(), event.wheel_delta_y(), event.wheel_raw_delta_x(), event.wheel_raw_delta_y() }; - AbstractTableView::mousedown_event(adjusted_event); - } else { - AbstractTableView::mousedown_event(event); - if (event.button() == GUI::Primary) { - auto index = index_at_event_position(event.position()); - AbstractTableView::set_cursor(index, SelectionUpdate::Set); - } - } -} - -void InfinitelyScrollableTableView::mouseup_event(GUI::MouseEvent& event) -{ - // If done extending - if (m_has_committed_to_extending) { - Vector from; - Position position { (size_t)m_target_cell.column(), (size_t)m_target_cell.row() }; - from.append(position); - Vector cell_changes; - selection().for_each_index([&](auto& index) { - if (index == m_starting_selection_index) - return; - auto& sheet = static_cast(*this->model()).sheet(); - Vector to; - Position position { (size_t)index.column(), (size_t)index.row() }; - to.append(position); - auto cell_change = sheet.copy_cells(from, to); - cell_changes.extend(cell_change); - }); - if (static_cast(*this->model()).on_cells_data_change) - static_cast(*this->model()).on_cells_data_change(cell_changes); - update(); - } - - m_is_dragging_for_select = false; - m_is_dragging_for_cut = false; - m_is_dragging_for_extend = false; - m_has_committed_to_cutting = false; - m_has_committed_to_extending = false; - if (m_is_hovering_cut_zone || m_is_hovering_extend_zone) { - auto rect = content_rect_minus_scrollbars(m_target_cell); - GUI::MouseEvent adjusted_event = { (GUI::Event::Type)event.type(), rect.center(), event.buttons(), event.button(), event.modifiers(), event.wheel_delta_x(), event.wheel_delta_y(), event.wheel_raw_delta_x(), event.wheel_raw_delta_y() }; - TableView::mouseup_event(adjusted_event); - } else { - TableView::mouseup_event(event); - } -} - -void InfinitelyScrollableTableView::drop_event(GUI::DropEvent& event) -{ - m_is_dragging_for_cut = false; - set_override_cursor(Gfx::StandardCursor::Arrow); - if (!index_at_event_position(event.position()).is_valid()) - return; - - TableView::drop_event(event); - auto drop_index = index_at_event_position(event.position()); - if (selection().size() > 0) { - // Get top left index position of previous selection - auto top_left_most_index = selection().first(); - selection().for_each_index([&](auto& index) { - if (index.row() < top_left_most_index.row()) - top_left_most_index = index; - else if (index.column() < top_left_most_index.column()) - top_left_most_index = index; - }); - - // Compare with drag location - auto x_diff = drop_index.column() - top_left_most_index.column(); - auto y_diff = drop_index.row() - top_left_most_index.row(); - - // Set new selection - Vector new_selection; - for (auto& index : selection().indices()) { - auto new_index = model()->index(index.row() + y_diff, index.column() + x_diff); - new_selection.append(move(new_index)); - } - selection().clear(); - AbstractTableView::set_cursor(drop_index, SelectionUpdate::Set); - selection().add_all(new_selection); - } -} - -void SpreadsheetView::update_with_model() -{ - m_sheet_model->update(); - m_table_view->update(); -} - -SpreadsheetView::SpreadsheetView(Sheet& sheet) - : m_sheet(sheet) - , m_sheet_model(SheetModel::create(*m_sheet)) -{ - set_layout(2); - m_table_view = add(); - m_table_view->set_grid_style(GUI::TableView::GridStyle::Both); - m_table_view->set_selection_behavior(GUI::AbstractView::SelectionBehavior::SelectItems); - m_table_view->set_edit_triggers(GUI::AbstractView::EditTrigger::EditKeyPressed | GUI::AbstractView::AnyKeyPressed | GUI::AbstractView::DoubleClicked); - m_table_view->set_tab_key_navigation_enabled(true); - m_table_view->row_header().set_visible(true); - m_table_view->set_model(m_sheet_model); - m_table_view->on_reaching_vertical_end = [&]() { - for (size_t i = 0; i < 100; ++i) { - auto index = m_sheet->add_row(); - m_table_view->set_column_painting_delegate(index, make(*m_table_view)); - }; - update_with_model(); - }; - m_table_view->on_reaching_horizontal_end = [&]() { - for (size_t i = 0; i < 10; ++i) { - m_sheet->add_column(); - auto last_column_index = m_sheet->column_count() - 1; - m_table_view->set_column_width(last_column_index, 50); - m_table_view->set_default_column_width(last_column_index, 50); - m_table_view->set_column_header_alignment(last_column_index, Gfx::TextAlignment::Center); - m_table_view->set_column_painting_delegate(last_column_index, make(*m_table_view)); - } - update_with_model(); - }; - - set_focus_proxy(m_table_view); - - // FIXME: This is dumb. - for (size_t i = 0; i < m_sheet->column_count(); ++i) { - m_table_view->set_column_painting_delegate(i, make(*m_table_view)); - m_table_view->set_column_width(i, 50); - m_table_view->set_default_column_width(i, 50); - m_table_view->set_column_header_alignment(i, Gfx::TextAlignment::Center); - } - - m_table_view->set_alternating_row_colors(false); - m_table_view->set_highlight_selected_rows(false); - m_table_view->set_editable(true); - m_table_view->aid_create_editing_delegate = [this](auto&) { - auto delegate = make(*m_sheet); - delegate->on_cursor_key_pressed = [this](auto& event) { - m_table_view->stop_editing(); - m_table_view->dispatch_event(event); - }; - delegate->on_cell_focusout = [this](auto& index, auto& value) { - m_table_view->model()->set_data(index, value); - }; - return delegate; - }; - - m_table_view->on_selection_change = [&] { - m_sheet->selected_cells().clear(); - for (auto& index : m_table_view->selection().indices()) { - Position position { (size_t)index.column(), (size_t)index.row() }; - m_sheet->selected_cells().set(position); - } - - if (m_table_view->selection().is_empty() && on_selection_dropped) - return on_selection_dropped(); - - Vector selected_positions; - selected_positions.ensure_capacity(m_table_view->selection().size()); - for (auto& selection : m_table_view->selection().indices()) - selected_positions.empend((size_t)selection.column(), (size_t)selection.row()); - - if (on_selection_changed) { - on_selection_changed(move(selected_positions)); - update_with_model(); - }; - }; - - m_table_view->on_activation = [this](auto&) { - m_table_view->move_cursor(GUI::AbstractView::CursorMovement::Down, GUI::AbstractView::SelectionUpdate::Set); - }; - - m_table_view->on_context_menu_request = [&](const GUI::ModelIndex&, const GUI::ContextMenuEvent& event) { - // NOTE: We ignore the specific cell for now. - m_cell_range_context_menu->popup(event.screen_position()); - }; - - m_cell_range_context_menu = GUI::Menu::construct(); - m_cell_range_context_menu->add_action(GUI::Action::create("Format...", [this](auto&) { - Vector positions; - for (auto& index : m_table_view->selection().indices()) { - Position position { (size_t)index.column(), (size_t)index.row() }; - positions.append(move(position)); - } - - if (positions.is_empty()) { - auto& index = m_table_view->cursor_index(); - Position position { (size_t)index.column(), (size_t)index.row() }; - positions.append(move(position)); - } - - auto dialog = CellTypeDialog::construct(positions, *m_sheet, window()); - if (dialog->exec() == GUI::Dialog::ExecResult::OK) { - for (auto& position : positions) { - auto& cell = m_sheet->ensure(position); - cell.set_type(dialog->type()); - cell.set_type_metadata(dialog->metadata()); - cell.set_conditional_formats(dialog->conditional_formats()); - } - - m_table_view->update(); - } - })); - - m_table_view->on_drop = [&](const GUI::ModelIndex& index, const GUI::DropEvent& event) { - if (!index.is_valid()) - return; - - ScopeGuard update_after_drop { [this] { update(); } }; - - if (event.mime_data().has_format("text/x-spreadsheet-data"sv)) { - auto const& data = event.mime_data().data("text/x-spreadsheet-data"sv); - StringView urls { data.data(), data.size() }; - Vector source_positions, target_positions; - - for (auto& line : urls.lines(StringView::ConsiderCarriageReturn::No)) { - auto position = m_sheet->position_from_url(line); - if (position.has_value()) - source_positions.append(position.release_value()); - } - - // Drop always has a single target. - Position target { (size_t)index.column(), (size_t)index.row() }; - target_positions.append(move(target)); - - if (source_positions.is_empty()) - return; - - auto first_position = source_positions.take_first(); - auto cell_changes = m_sheet->copy_cells(move(source_positions), move(target_positions), first_position, Spreadsheet::Sheet::CopyOperation::Cut); - if (model()->on_cells_data_change) - model()->on_cells_data_change(cell_changes); - - return; - } - - if (event.mime_data().has_text()) { - auto& target_cell = m_sheet->ensure({ (size_t)index.column(), (size_t)index.row() }); - target_cell.set_data(event.text()); - return; - } - }; -} - -void SpreadsheetView::hide_event(GUI::HideEvent&) -{ - if (on_selection_dropped) - on_selection_dropped(); -} - -void SpreadsheetView::show_event(GUI::ShowEvent&) -{ - if (on_selection_changed && !m_table_view->selection().is_empty()) { - Vector selected_positions; - selected_positions.ensure_capacity(m_table_view->selection().size()); - for (auto& selection : m_table_view->selection().indices()) - selected_positions.empend((size_t)selection.column(), (size_t)selection.row()); - - on_selection_changed(move(selected_positions)); - } -} - -void SpreadsheetView::move_cursor(GUI::AbstractView::CursorMovement direction) -{ - m_table_view->move_cursor(direction, GUI::AbstractView::SelectionUpdate::Set); -} - -void SpreadsheetView::TableCellPainter::paint(GUI::Painter& painter, Gfx::IntRect const& rect, Gfx::Palette const& palette, const GUI::ModelIndex& index) -{ - // Draw a border. - // Undo the horizontal padding done by the table view... - auto cell_rect = rect.inflated(m_table_view.horizontal_padding() * 2, 0); - - if (auto bg = index.data(GUI::ModelRole::BackgroundColor); bg.is_color()) - painter.fill_rect(cell_rect, bg.as_color()); - - if (m_table_view.selection().contains(index)) { - Color fill_color = palette.selection(); - fill_color.set_alpha(80); - painter.fill_rect(cell_rect, fill_color); - } - - auto text_color = index.data(GUI::ModelRole::ForegroundColor).to_color(palette.color(m_table_view.foreground_role())); - auto data = index.data(); - auto text_alignment = index.data(GUI::ModelRole::TextAlignment).to_text_alignment(Gfx::TextAlignment::CenterRight); - painter.draw_text(rect, data.to_byte_string(), m_table_view.font_for_index(index), text_alignment, text_color, Gfx::TextElision::Right); -} - -} diff --git a/Userland/Applications/Spreadsheet/SpreadsheetView.h b/Userland/Applications/Spreadsheet/SpreadsheetView.h deleted file mode 100644 index a040416ba13..00000000000 --- a/Userland/Applications/Spreadsheet/SpreadsheetView.h +++ /dev/null @@ -1,170 +0,0 @@ -/* - * Copyright (c) 2020-2022, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include "Spreadsheet.h" -#include "SpreadsheetModel.h" -#include -#include -#include -#include -#include - -namespace Spreadsheet { - -class CellEditor final : public GUI::TextEditor { - C_OBJECT(CellEditor); - -public: - virtual ~CellEditor() = default; - - Function on_cursor_key_pressed; - -private: - CellEditor() - : TextEditor(TextEditor::Type::SingleLine) - { - } - - static bool is_navigation(const GUI::KeyEvent& event) - { - if (event.modifiers() == KeyModifier::Mod_Shift && event.key() == KeyCode::Key_Tab) - return true; - - if (event.modifiers()) - return false; - - switch (event.key()) { - case KeyCode::Key_Tab: - case KeyCode::Key_Return: - return true; - default: - return false; - } - } - - virtual void keydown_event(GUI::KeyEvent& event) override - { - if (is_navigation(event)) - on_cursor_key_pressed(event); - else - TextEditor::keydown_event(event); - } -}; - -class InfinitelyScrollableTableView : public GUI::TableView { - C_OBJECT(InfinitelyScrollableTableView) -public: - Function on_reaching_vertical_end; - Function on_reaching_horizontal_end; - -private: - InfinitelyScrollableTableView() - : m_horizontal_scroll_end_timer(Core::Timer::try_create().release_value_but_fixme_should_propagate_errors()) - , m_vertical_scroll_end_timer(Core::Timer::try_create().release_value_but_fixme_should_propagate_errors()) - { - } - virtual void did_scroll() override; - virtual void mousemove_event(GUI::MouseEvent&) override; - virtual void mousedown_event(GUI::MouseEvent&) override; - virtual void mouseup_event(GUI::MouseEvent&) override; - virtual void drop_event(GUI::DropEvent&) override; - - bool is_dragging() const { return m_is_dragging_for_cut || m_is_dragging_for_extend || m_is_dragging_for_select; } - - bool m_is_hovering_extend_zone { false }; - bool m_is_hovering_cut_zone { false }; - bool m_is_dragging_for_select { false }; - bool m_is_dragging_for_cut { false }; - bool m_is_dragging_for_extend { false }; - bool m_has_committed_to_cutting { false }; - bool m_has_committed_to_extending { false }; - GUI::ModelIndex m_starting_selection_index; - GUI::ModelIndex m_target_cell; - RefPtr m_horizontal_scroll_end_timer; - RefPtr m_vertical_scroll_end_timer; -}; - -class SpreadsheetView final : public GUI::Widget { - C_OBJECT(SpreadsheetView); - -public: - ~SpreadsheetView() = default; - - Sheet* sheet_if_available() { return m_sheet; } - - const GUI::ModelIndex* cursor() const - { - return &m_table_view->cursor_index(); - } - - Function&&)> on_selection_changed; - Function on_selection_dropped; - - void move_cursor(GUI::AbstractView::CursorMovement); - - NonnullRefPtr model() { return m_sheet_model; } - -private: - virtual void hide_event(GUI::HideEvent&) override; - virtual void show_event(GUI::ShowEvent&) override; - - void update_with_model(); - - SpreadsheetView(Sheet&); - - class EditingDelegate final : public GUI::StringModelEditingDelegate { - public: - EditingDelegate(Sheet const& sheet) - : m_sheet(sheet) - { - } - virtual void set_value(GUI::Variant const&, GUI::ModelEditingDelegate::SelectionBehavior) override; - - virtual RefPtr create_widget() override - { - auto textbox = CellEditor::construct(); - textbox->on_escape_pressed = [this] { - rollback(); - }; - textbox->on_cursor_key_pressed = [this](auto& event) { - commit(); - on_cursor_key_pressed(event); - }; - textbox->on_focusout = [this] { - on_cell_focusout(index(), value()); - }; - return textbox; - } - - Function on_cursor_key_pressed; - Function on_cell_focusout; - - private: - bool m_has_set_initial_value { false }; - Sheet const& m_sheet; - }; - - class TableCellPainter final : public GUI::TableCellPaintingDelegate { - public: - TableCellPainter(const GUI::TableView& view) - : m_table_view(view) - { - } - void paint(GUI::Painter&, Gfx::IntRect const&, Gfx::Palette const&, const GUI::ModelIndex&) override; - - private: - const GUI::TableView& m_table_view; - }; - - NonnullRefPtr m_sheet; - NonnullRefPtr m_sheet_model; - RefPtr m_table_view; - RefPtr m_cell_range_context_menu; -}; - -} diff --git a/Userland/Applications/Spreadsheet/SpreadsheetWidget.cpp b/Userland/Applications/Spreadsheet/SpreadsheetWidget.cpp deleted file mode 100644 index 8462dd857b6..00000000000 --- a/Userland/Applications/Spreadsheet/SpreadsheetWidget.cpp +++ /dev/null @@ -1,762 +0,0 @@ -/* - * Copyright (c) 2020-2022, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "SpreadsheetWidget.h" -#include "CellSyntaxHighlighter.h" -#include "HelpWindow.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace Spreadsheet { - -SpreadsheetWidget::SpreadsheetWidget(GUI::Window& parent_window, Vector>&& sheets, bool should_add_sheet_if_empty) - : m_workbook(make(move(sheets), parent_window)) -{ - set_fill_with_background_color(true); - set_layout(2); - - auto& toolbar_container = add(); - auto& toolbar = toolbar_container.add(); - - auto& container = add(); - - auto& top_bar = container.add(); - top_bar.set_layout(GUI::Margins {}, 1); - top_bar.set_preferred_height(26); - auto& current_cell_label = top_bar.add(); - current_cell_label.set_fixed_width(50); - - auto& help_button = top_bar.add(); - help_button.set_icon(Gfx::Bitmap::load_from_file("/res/icons/16x16/app-help.png"sv).release_value_but_fixme_should_propagate_errors()); - help_button.set_tooltip("Functions Help"_string); - help_button.set_fixed_size(20, 20); - help_button.on_click = [&](auto) { - if (!current_view()) { - GUI::MessageBox::show_error(window(), "Can only show function documentation/help when a worksheet exists and is open"sv); - } else if (auto* sheet_ptr = current_worksheet_if_available()) { - auto docs = sheet_ptr->gather_documentation(); - auto help_window = HelpWindow::the(window()); - help_window->set_docs(move(docs)); - help_window->show(); - } - }; - - auto& cell_value_editor = top_bar.add(GUI::TextEditor::Type::SingleLine); - cell_value_editor.set_font(Gfx::FontDatabase::default_fixed_width_font()); - cell_value_editor.set_scrollbars_enabled(false); - - cell_value_editor.on_return_pressed = [this]() { - current_view()->move_cursor(GUI::AbstractView::CursorMovement::Down); - }; - - cell_value_editor.set_syntax_highlighter(make()); - cell_value_editor.set_enabled(false); - current_cell_label.set_enabled(false); - - m_tab_widget = container.add(); - m_tab_widget->set_tab_position(TabPosition::Bottom); - - m_cell_value_editor = cell_value_editor; - m_current_cell_label = current_cell_label; - m_inline_documentation_window = GUI::Window::construct(window()); - m_inline_documentation_window->set_rect(m_cell_value_editor->rect().translated(0, m_cell_value_editor->height() + 7).inflated(6, 6)); - m_inline_documentation_window->set_window_type(GUI::WindowType::Tooltip); - m_inline_documentation_window->set_resizable(false); - auto inline_widget = m_inline_documentation_window->set_main_widget(); - inline_widget->set_fill_with_background_color(true); - inline_widget->set_layout(4); - inline_widget->set_frame_style(Gfx::FrameStyle::Plain); - m_inline_documentation_label = inline_widget->add(); - m_inline_documentation_label->set_fill_with_background_color(true); - m_inline_documentation_label->set_autosize(false); - m_inline_documentation_label->set_text_alignment(Gfx::TextAlignment::CenterLeft); - - if (!m_workbook->has_sheets() && should_add_sheet_if_empty) - m_workbook->add_sheet("Sheet 1"sv); - - m_tab_context_menu = GUI::Menu::construct(); - m_rename_action = GUI::CommonActions::make_rename_action([this](auto&) { - VERIFY(m_tab_context_menu_sheet_view); - - auto* sheet_ptr = m_tab_context_menu_sheet_view->sheet_if_available(); - VERIFY(sheet_ptr); // How did we get here without a sheet? - auto& sheet = *sheet_ptr; - String new_name = String::from_byte_string(sheet.name()).release_value_but_fixme_should_propagate_errors(); - if (GUI::InputBox::show(window(), new_name, {}, "Rename Sheet"sv, GUI::InputType::NonemptyText, "Name"sv) == GUI::Dialog::ExecResult::OK) { - sheet.set_name(new_name); - sheet.update(); - m_tab_widget->set_tab_title(static_cast(*m_tab_context_menu_sheet_view), new_name); - } - }); - m_tab_context_menu->add_action(*m_rename_action); - m_tab_context_menu->add_action(GUI::Action::create("Add New Sheet...", Gfx::Bitmap::load_from_file("/res/icons/16x16/new-tab.png"sv).release_value_but_fixme_should_propagate_errors(), [this](auto&) { - String name; - auto icon = Gfx::Bitmap::load_from_file("/res/icons/32x32/filetype-spreadsheet.png"sv).release_value_but_fixme_should_propagate_errors(); - if (GUI::InputBox::show(window(), name, "Enter a name:"sv, "New sheet"sv, GUI::InputType::NonemptyText, {}, move(icon)) == GUI::Dialog::ExecResult::OK) { - Vector> new_sheets; - new_sheets.append(m_workbook->add_sheet(name)); - setup_tabs(move(new_sheets)); - } - })); - - setup_tabs(m_workbook->sheets()); - - m_new_action = GUI::Action::create("Add New Sheet", Gfx::Bitmap::load_from_file("/res/icons/16x16/new-tab.png"sv).release_value_but_fixme_should_propagate_errors(), [&](auto&) { - add_sheet(); - }); - - m_open_action = GUI::CommonActions::make_open_action([&](auto&) { - if (!request_close()) - return; - - FileSystemAccessClient::OpenFileOptions options { - .allowed_file_types = Vector { - { "Spreadsheets", { { "sheets", "csv" } } }, - GUI::FileTypeFilter::all_files(), - }, - }; - auto response = FileSystemAccessClient::Client::the().open_file(window(), options); - if (response.is_error()) - return; - load_file(response.value().filename(), response.value().stream()); - }); - - m_import_action = GUI::Action::create("Import Sheets...", [&](auto&) { - FileSystemAccessClient::OpenFileOptions options { - .allowed_file_types = Vector { - { "Spreadsheets", { { "sheets", "csv" } } }, - GUI::FileTypeFilter::all_files(), - }, - }; - auto response = FileSystemAccessClient::Client::the().open_file(window(), options); - if (response.is_error()) - return; - - import_sheets(response.value().filename(), response.value().stream()); - }); - - m_save_action = GUI::CommonActions::make_save_action([&](auto&) { - if (current_filename().is_empty()) { - m_save_as_action->activate(); - return; - } - - auto response = FileSystemAccessClient::Client::the().request_file(window(), current_filename(), Core::File::OpenMode::Write); - if (response.is_error()) - return; - save(response.value().filename(), response.value().stream()); - }); - - m_save_as_action = GUI::CommonActions::make_save_as_action([&](auto&) { - ByteString name = "workbook"; - auto response = FileSystemAccessClient::Client::the().save_file(window(), name, "sheets"); - if (response.is_error()) - return; - save(response.value().filename(), response.value().stream()); - update_window_title(); - }); - - m_quit_action = GUI::CommonActions::make_quit_action([&](auto&) { - if (!request_close()) - return; - GUI::Application::the()->quit(0); - }); - - m_cut_action = GUI::CommonActions::make_cut_action([&](auto&) { clipboard_action(true); }, window()); - m_copy_action = GUI::CommonActions::make_copy_action([&](auto&) { clipboard_action(false); }, window()); - m_paste_action = GUI::CommonActions::make_paste_action([&](auto&) { - ScopeGuard update_after_paste { [&] { update(); } }; - - auto* worksheet_ptr = current_worksheet_if_available(); - if (!worksheet_ptr) { - GUI::MessageBox::show_error(window(), "There are no active worksheets"sv); - return; - } - auto& sheet = *worksheet_ptr; - auto& cells = sheet.selected_cells(); - VERIFY(!cells.is_empty()); - auto const& data = GUI::Clipboard::the().fetch_data_and_type(); - if (auto spreadsheet_data = data.metadata.get("text/x-spreadsheet-data"); spreadsheet_data.has_value()) { - Vector source_positions, target_positions; - auto lines = spreadsheet_data.value().split_view('\n'); - auto action = lines.take_first(); - - for (auto& line : lines) { - dbgln("Paste line '{}'", line); - auto position = sheet.position_from_url(line); - if (position.has_value()) - source_positions.append(position.release_value()); - } - - for (auto& position : sheet.selected_cells()) - target_positions.append(position); - - if (source_positions.is_empty()) - return; - - auto first_position = source_positions.take_first(); - auto cell_changes = sheet.copy_cells(move(source_positions), move(target_positions), first_position, action == "cut" ? Spreadsheet::Sheet::CopyOperation::Cut : Spreadsheet::Sheet::CopyOperation::Copy); - undo_stack().push(make(cell_changes)); - } else { - for (auto& cell : sheet.selected_cells()) - sheet.ensure(cell).set_data(StringView { data.data.data(), data.data.size() }); - update(); - } - }, - window()); - - m_insert_emoji_action = GUI::CommonActions::make_insert_emoji_action([&](auto&) { - auto emoji_input_dialog = GUI::EmojiInputDialog::construct(window()); - if (emoji_input_dialog->exec() != GUI::EmojiInputDialog::ExecResult::OK) - return; - - auto emoji_code_point = emoji_input_dialog->selected_emoji_text(); - - if (m_cell_value_editor->has_focus_within()) { - m_cell_value_editor->insert_at_cursor_or_replace_selection(emoji_code_point); - } - - auto* worksheet_ptr = current_worksheet_if_available(); - if (!worksheet_ptr) { - GUI::MessageBox::show_error(window(), "There are no active worksheets"sv); - return; - } - auto& sheet = *worksheet_ptr; - for (auto& cell : sheet.selected_cells()) - sheet.ensure(cell).set_data(emoji_code_point); - - update(); - }, - window()); - - m_undo_action = GUI::CommonActions::make_undo_action([&](auto&) { - undo(); - }); - - m_redo_action = GUI::CommonActions::make_redo_action([&](auto&) { - redo(); - }); - - m_undo_stack.on_state_change = [this] { - m_undo_action->set_enabled(m_undo_stack.can_undo()); - m_redo_action->set_enabled(m_undo_stack.can_redo()); - }; - - m_undo_action->set_enabled(false); - m_redo_action->set_enabled(false); - - m_change_background_color_action = GUI::Action::create( - "&Change Background Color", { Mod_Ctrl, Key_B }, Gfx::Bitmap::load_from_file("/res/icons/pixelpaint/bucket.png"sv).release_value_but_fixme_should_propagate_errors(), [&](auto&) { - change_cell_static_color_format(Spreadsheet::FormatType::Background); - }, - window()); - - m_change_foreground_color_action = GUI::Action::create( - "&Change Foreground Color", { Mod_Ctrl, Key_T }, Gfx::Bitmap::load_from_file("/res/icons/16x16/text-color.png"sv).release_value_but_fixme_should_propagate_errors(), [&](auto&) { - change_cell_static_color_format(Spreadsheet::FormatType::Foreground); - }, - window()); - - m_change_background_color_action->set_enabled(false); - m_change_foreground_color_action->set_enabled(false); - - m_functions_help_action = GUI::Action::create( - "&Functions Help", Gfx::Bitmap::load_from_file("/res/icons/16x16/app-help.png"sv).release_value_but_fixme_should_propagate_errors(), [&](auto&) { - if (auto* worksheet_ptr = current_worksheet_if_available()) { - auto docs = worksheet_ptr->gather_documentation(); - auto help_window = Spreadsheet::HelpWindow::the(window()); - help_window->set_docs(move(docs)); - help_window->show(); - } else { - GUI::MessageBox::show_error(window(), "Cannot prepare documentation/help without an active worksheet"sv); - } - }, - window()); - - m_search_action = GUI::CommonActions::make_command_palette_action(&parent_window); - - m_about_action = GUI::CommonActions::make_about_action("Spreadsheet"_string, GUI::Icon::default_icon("app-spreadsheet"sv), &parent_window); - - toolbar.add_action(*m_new_action); - toolbar.add_action(*m_open_action); - toolbar.add_action(*m_save_action); - toolbar.add_separator(); - toolbar.add_action(*m_cut_action); - toolbar.add_action(*m_copy_action); - toolbar.add_action(*m_paste_action); - toolbar.add_action(*m_undo_action); - toolbar.add_action(*m_redo_action); - toolbar.add_separator(); - toolbar.add_action(*m_change_background_color_action); - toolbar.add_action(*m_change_foreground_color_action); - - m_cut_action->set_enabled(false); - m_copy_action->set_enabled(false); - m_paste_action->set_enabled(false); - m_insert_emoji_action->set_enabled(false); - - m_tab_widget->on_change = [this](auto& selected_widget) { - // for keyboard shortcuts and command palette - m_tab_context_menu_sheet_view = static_cast(selected_widget); - }; - - m_tab_widget->on_context_menu_request = [&](auto& widget, auto& event) { - m_tab_context_menu_sheet_view = static_cast(widget); - m_tab_context_menu->popup(event.screen_position()); - }; - - m_tab_widget->on_double_click = [&](auto& widget) { - m_tab_context_menu_sheet_view = static_cast(widget); - m_rename_action->activate(); - }; -} - -void SpreadsheetWidget::resize_event(GUI::ResizeEvent& event) -{ - GUI::Widget::resize_event(event); - if (m_inline_documentation_window && m_cell_value_editor && window()) - m_inline_documentation_window->set_rect(m_cell_value_editor->screen_relative_rect().translated(0, m_cell_value_editor->height() + 7).inflated(6, 6)); -} - -void SpreadsheetWidget::clipboard_content_did_change(ByteString const& mime_type) -{ - if (auto* sheet = current_worksheet_if_available()) - m_paste_action->set_enabled(!sheet->selected_cells().is_empty() && mime_type.starts_with("text/"sv)); -} - -void SpreadsheetWidget::setup_tabs(Vector> new_sheets) -{ - for (auto& sheet : new_sheets) { - auto& new_view = m_tab_widget->add_tab(String::from_byte_string(sheet->name()).release_value_but_fixme_should_propagate_errors(), sheet); - new_view.model()->on_cell_data_change = [&](auto& cell, auto& previous_data) { - undo_stack().push(make(cell, previous_data)); - window()->set_modified(true); - }; - new_view.model()->on_cells_data_change = [&](Vector cell_changes) { - undo_stack().push(make(cell_changes)); - window()->set_modified(true); - }; - new_view.on_selection_changed = [&](Vector&& selection) { - auto* sheet_ptr = current_worksheet_if_available(); - // How did this even happen? - VERIFY(sheet_ptr); - auto& sheet = *sheet_ptr; - - VERIFY(!selection.is_empty()); - m_cut_action->set_enabled(true); - m_copy_action->set_enabled(true); - m_paste_action->set_enabled(GUI::Clipboard::the().fetch_mime_type().starts_with("text/"sv)); - m_insert_emoji_action->set_enabled(true); - m_current_cell_label->set_enabled(true); - m_cell_value_editor->set_enabled(true); - m_change_background_color_action->set_enabled(true); - m_change_foreground_color_action->set_enabled(true); - - if (selection.size() == 1) { - auto& position = selection.first(); - m_current_cell_label->set_text(String::from_byte_string(position.to_cell_identifier(sheet)).release_value_but_fixme_should_propagate_errors()); - - auto& cell = sheet.ensure(position); - m_cell_value_editor->on_change = nullptr; - m_cell_value_editor->set_text(cell.source()); - m_cell_value_editor->on_change = [&] { - auto text = m_cell_value_editor->text(); - // FIXME: Lines? - auto offset = m_cell_value_editor->cursor().column(); - try_generate_tip_for_input_expression(text, offset); - cell.set_data(move(text)); - sheet.update(); - update(); - }; - static_cast(const_cast(m_cell_value_editor->syntax_highlighter()))->set_cell(&cell); - return; - } - - // There are many cells selected, change all of them. - StringBuilder builder; - builder.appendff("<{}>", selection.size()); - m_current_cell_label->set_text(builder.to_string().release_value_but_fixme_should_propagate_errors()); - - Vector cells; - for (auto& position : selection) - cells.append(sheet.ensure(position)); - - auto& first_cell = cells.first(); - m_cell_value_editor->on_change = nullptr; - m_cell_value_editor->set_text(""sv); - m_should_change_selected_cells = false; - m_cell_value_editor->on_focusin = [this] { m_should_change_selected_cells = true; }; - m_cell_value_editor->on_focusout = [this] { m_should_change_selected_cells = false; }; - m_cell_value_editor->on_change = [cells = move(cells), this]() mutable { - if (m_should_change_selected_cells) { - auto* sheet_ptr = current_worksheet_if_available(); - if (!sheet_ptr) - return; - auto& sheet = *sheet_ptr; - auto text = m_cell_value_editor->text(); - // FIXME: Lines? - auto offset = m_cell_value_editor->cursor().column(); - try_generate_tip_for_input_expression(text, offset); - for (auto& cell : cells) - cell.set_data(text); - sheet.update(); - update(); - } - }; - static_cast(const_cast(m_cell_value_editor->syntax_highlighter()))->set_cell(&first_cell); - }; - new_view.on_selection_dropped = [&]() { - m_current_cell_label->set_enabled(false); - m_current_cell_label->set_text({}); - m_cell_value_editor->on_change = nullptr; - m_cell_value_editor->on_focusin = nullptr; - m_cell_value_editor->on_focusout = nullptr; - m_cell_value_editor->set_text({}); - m_cell_value_editor->set_enabled(false); - - m_cut_action->set_enabled(false); - m_copy_action->set_enabled(false); - m_paste_action->set_enabled(false); - m_insert_emoji_action->set_enabled(false); - - static_cast(const_cast(m_cell_value_editor->syntax_highlighter()))->set_cell(nullptr); - }; - } -} - -void SpreadsheetWidget::try_generate_tip_for_input_expression(StringView source, size_t cursor_offset) -{ - auto* sheet_ptr = current_view()->sheet_if_available(); - if (!sheet_ptr) - return; - - auto& sheet = *sheet_ptr; - - m_inline_documentation_window->set_rect(m_cell_value_editor->screen_relative_rect().translated(0, m_cell_value_editor->height() + 7).inflated(6, 6)); - if (!current_view() || !source.starts_with('=')) { - m_inline_documentation_window->hide(); - return; - } - cursor_offset = min(cursor_offset, source.length()); - auto maybe_function_and_argument = get_function_and_argument_index(source.substring_view(0, cursor_offset)); - if (!maybe_function_and_argument.has_value()) { - m_inline_documentation_window->hide(); - return; - } - - auto& [name, index] = maybe_function_and_argument.value(); - auto text = sheet.generate_inline_documentation_for(name, index); - if (text.is_empty()) { - m_inline_documentation_window->hide(); - } else { - m_inline_documentation_label->set_text(String::from_byte_string(text).release_value_but_fixme_should_propagate_errors()); - m_inline_documentation_window->show(); - } -} - -void SpreadsheetWidget::undo() -{ - if (!m_undo_stack.can_undo()) - return; - - m_undo_stack.undo(); - update(); -} - -void SpreadsheetWidget::redo() -{ - if (!m_undo_stack.can_redo()) - return; - - m_undo_stack.redo(); - update(); -} - -void SpreadsheetWidget::change_cell_static_color_format(Spreadsheet::FormatType format_type) -{ - VERIFY(current_worksheet_if_available()); - - auto preview_color_in_selected_cells = [this, format_type](Gfx::Color color) { - for (auto& position : current_worksheet_if_available()->selected_cells()) { - auto* cell = current_worksheet_if_available()->at(position); - auto previous_type_metadata = cell->type_metadata(); - if (format_type == Spreadsheet::FormatType::Background) - cell->type_metadata().static_format.background_color = color; - else - cell->type_metadata().static_format.foreground_color = color; - update(); - } - }; - auto apply_color_to_selected_cells = [this, format_type](Gfx::Color color) { - Vector cell_changes; - for (auto& position : current_worksheet_if_available()->selected_cells()) { - auto* cell = current_worksheet_if_available()->at(position); - auto previous_type_metadata = cell->type_metadata(); - if (format_type == Spreadsheet::FormatType::Background) - cell->type_metadata().static_format.background_color = color; - else - cell->type_metadata().static_format.foreground_color = color; - cell_changes.append(CellChange(*cell, previous_type_metadata)); - } - undo_stack().push(make(move(cell_changes))); - window()->set_modified(true); - }; - auto get_selection_color = [this, format_type](void) { - // FIXME: Not sure what to do if a selection of multiple cells has more than one color. - // For now we just grab the first one we see and pass that to GUI::ColorPicker - for (auto& position : current_worksheet_if_available()->selected_cells()) { - auto* cell = current_worksheet_if_available()->at(position); - auto previous_type_metadata = cell->type_metadata(); - if (format_type == Spreadsheet::FormatType::Background) - return cell->type_metadata().static_format.background_color.value_or(Color::White); - else - return cell->type_metadata().static_format.foreground_color.value_or(Color::White); - } - return Color(Color::White); - }; - - // FIXME: Hack, we want to restore the cell metadata to the actual state before computing the change - auto get_current_selection_metadata = [this](void) { - Vector cell_metadata; - for (auto& position : current_worksheet_if_available()->selected_cells()) { - auto* cell = current_worksheet_if_available()->at(position); - cell_metadata.append(cell->type_metadata()); - } - return cell_metadata; - }; - auto restore_current_selection_metadata = [this](Vector metadata) { - for (auto& position : current_worksheet_if_available()->selected_cells()) { - auto* cell = current_worksheet_if_available()->at(position); - cell->type_metadata() = metadata.take_first(); - } - }; - - auto dialog = GUI::ColorPicker::construct(get_selection_color(), window(), "Select Color"); - dialog->on_color_changed = [&preview_color_in_selected_cells](Gfx::Color color) { - preview_color_in_selected_cells(color); - }; - Vector preserved_state = get_current_selection_metadata(); - auto result = dialog->exec(); - restore_current_selection_metadata(preserved_state); - if (result == GUI::Dialog::ExecResult::OK) - apply_color_to_selected_cells(dialog->color()); -} - -void SpreadsheetWidget::save(ByteString const& filename, Core::File& file) -{ - auto result = m_workbook->write_to_file(filename, file); - if (result.is_error()) { - GUI::MessageBox::show_error(window(), ByteString::formatted("Cannot save file: {}", result.error())); - return; - } - undo_stack().set_current_unmodified(); - window()->set_modified(false); - GUI::Application::the()->set_most_recently_open_file(filename); -} - -void SpreadsheetWidget::load_file(ByteString const& filename, Core::File& file) -{ - auto result = m_workbook->open_file(filename, file); - if (result.is_error()) { - GUI::MessageBox::show_error(window(), result.error()); - if (!m_workbook->has_sheets()) { - add_sheet(); - } - return; - } - - m_cell_value_editor->on_change = nullptr; - m_current_cell_label->set_text({}); - m_should_change_selected_cells = false; - while (auto* widget = m_tab_widget->active_widget()) { - m_tab_widget->remove_tab(*widget); - } - - setup_tabs(m_workbook->sheets()); - update_window_title(); - GUI::Application::the()->set_most_recently_open_file(filename); -} - -void SpreadsheetWidget::import_sheets(ByteString const& filename, Core::File& file) -{ - auto result = m_workbook->import_file(filename, file); - if (result.is_error()) { - GUI::MessageBox::show_error(window(), result.error()); - return; - } - - if (!result.value()) - return; - - window()->set_modified(true); - - m_cell_value_editor->on_change = nullptr; - m_current_cell_label->set_text({}); - m_should_change_selected_cells = false; - while (auto* widget = m_tab_widget->active_widget()) { - m_tab_widget->remove_tab(*widget); - } - - setup_tabs(m_workbook->sheets()); - update_window_title(); -} - -bool SpreadsheetWidget::request_close() -{ - if (!undo_stack().is_current_modified()) - return true; - - auto result = GUI::MessageBox::ask_about_unsaved_changes(window(), current_filename()); - if (result == GUI::MessageBox::ExecResult::Yes) { - m_save_action->activate(); - return !m_workbook->dirty(); - } - - if (result == GUI::MessageBox::ExecResult::No) - return true; - - return false; -} - -void SpreadsheetWidget::add_sheet() -{ - StringBuilder name; - name.append("Sheet"sv); - name.appendff(" {}", m_workbook->sheets().size() + 1); - - Vector> new_sheets; - new_sheets.append(m_workbook->add_sheet(name.string_view())); - setup_tabs(move(new_sheets)); -} - -void SpreadsheetWidget::add_sheet(NonnullRefPtr&& sheet) -{ - VERIFY(m_workbook == &sheet->workbook()); - - Vector> new_sheets; - new_sheets.append(move(sheet)); - m_workbook->sheets().extend(new_sheets); - setup_tabs(new_sheets); -} - -void SpreadsheetWidget::update_window_title() -{ - StringBuilder builder; - if (current_filename().is_empty()) - builder.append("Untitled"sv); - else - builder.append(current_filename()); - builder.append("[*] - Spreadsheet"sv); - - window()->set_title(builder.to_byte_string()); -} - -void SpreadsheetWidget::clipboard_action(bool is_cut) -{ - /// text/x-spreadsheet-data: - /// - action: copy/cut - /// - currently selected cell - /// - selected cell+ - auto* worksheet_ptr = current_worksheet_if_available(); - if (!worksheet_ptr) { - GUI::MessageBox::show_error(window(), "There are no active worksheets"sv); - return; - } - auto& worksheet = *worksheet_ptr; - auto& cells = worksheet.selected_cells(); - VERIFY(!cells.is_empty()); - StringBuilder text_builder, url_builder; - url_builder.append(is_cut ? "cut\n"sv : "copy\n"sv); - bool first = true; - auto cursor = current_selection_cursor(); - if (cursor) { - Spreadsheet::Position position { (size_t)cursor->column(), (size_t)cursor->row() }; - url_builder.append(position.to_url(worksheet).to_byte_string()); - url_builder.append('\n'); - } - - for (auto& cell : cells) { - if (first && !cursor) { - url_builder.append(cell.to_url(worksheet).to_byte_string()); - url_builder.append('\n'); - } - - url_builder.append(cell.to_url(worksheet).to_byte_string()); - url_builder.append('\n'); - - auto cell_data = worksheet.at(cell); - if (!first) - text_builder.append('\t'); - if (cell_data) - text_builder.append(cell_data->data()); - first = false; - } - HashMap metadata; - metadata.set("text/x-spreadsheet-data", url_builder.to_byte_string()); - dbgln(url_builder.to_byte_string()); - - GUI::Clipboard::the().set_data(text_builder.string_view().bytes(), "text/plain", move(metadata)); -} - -ErrorOr SpreadsheetWidget::initialize_menubar(GUI::Window& window) -{ - auto file_menu = window.add_menu("&File"_string); - file_menu->add_action(*m_new_action); - file_menu->add_action(*m_open_action); - file_menu->add_action(*m_save_action); - file_menu->add_action(*m_save_as_action); - file_menu->add_separator(); - file_menu->add_action(*m_import_action); - file_menu->add_separator(); - file_menu->add_recent_files_list([&](auto& action) { - if (!request_close()) - return; - - auto response = FileSystemAccessClient::Client::the().request_file_read_only_approved(&window, action.text()); - if (response.is_error()) - return; - load_file(response.value().filename(), response.value().stream()); - }); - file_menu->add_action(*m_quit_action); - - auto edit_menu = window.add_menu("&Edit"_string); - edit_menu->add_action(*m_undo_action); - edit_menu->add_action(*m_redo_action); - edit_menu->add_separator(); - edit_menu->add_action(*m_cut_action); - edit_menu->add_action(*m_copy_action); - edit_menu->add_action(*m_paste_action); - edit_menu->add_action(*m_insert_emoji_action); - - auto view_menu = window.add_menu("&View"_string); - view_menu->add_action(GUI::CommonActions::make_fullscreen_action([&](auto&) { - window.set_fullscreen(!window.is_fullscreen()); - })); - - auto help_menu = window.add_menu("&Help"_string); - help_menu->add_action(*m_search_action); - help_menu->add_action(*m_functions_help_action); - help_menu->add_action(*m_about_action); - - return {}; -} - -} diff --git a/Userland/Applications/Spreadsheet/SpreadsheetWidget.h b/Userland/Applications/Spreadsheet/SpreadsheetWidget.h deleted file mode 100644 index bdffab14117..00000000000 --- a/Userland/Applications/Spreadsheet/SpreadsheetWidget.h +++ /dev/null @@ -1,105 +0,0 @@ -/* - * Copyright (c) 2020-2022, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include "SpreadsheetView.h" -#include "Workbook.h" -#include -#include -#include - -namespace Spreadsheet { - -class SpreadsheetWidget final - : public GUI::Widget - , public GUI::Clipboard::ClipboardClient { - C_OBJECT(SpreadsheetWidget); - -public: - virtual ~SpreadsheetWidget() override = default; - - void save(ByteString const& filename, Core::File&); - void load_file(ByteString const& filename, Core::File&); - void import_sheets(ByteString const& filename, Core::File&); - bool request_close(); - void add_sheet(); - void add_sheet(NonnullRefPtr&&); - - ByteString const& current_filename() const { return m_workbook->current_filename(); } - SpreadsheetView* current_view() { return static_cast(m_tab_widget->active_widget()); } - Sheet* current_worksheet_if_available() { return current_view() ? current_view()->sheet_if_available() : nullptr; } - void update_window_title(); - - Workbook& workbook() { return *m_workbook; } - Workbook const& workbook() const { return *m_workbook; } - - const GUI::ModelIndex* current_selection_cursor() - { - if (!current_view()) - return nullptr; - - return current_view()->cursor(); - } - - ErrorOr initialize_menubar(GUI::Window&); - - void undo(); - void redo(); - void change_cell_static_color_format(Spreadsheet::FormatType); - auto& undo_stack() { return m_undo_stack; } - -private: - // ^GUI::Widget - virtual void resize_event(GUI::ResizeEvent&) override; - - // ^GUI::Clipboard::ClipboardClient - virtual void clipboard_content_did_change(ByteString const& mime_type) override; - - explicit SpreadsheetWidget(GUI::Window& window, Vector>&& sheets = {}, bool should_add_sheet_if_empty = true); - - void setup_tabs(Vector> new_sheets); - - void try_generate_tip_for_input_expression(StringView source, size_t offset); - - RefPtr m_current_cell_label; - RefPtr m_cell_value_editor; - RefPtr m_inline_documentation_window; - RefPtr m_inline_documentation_label; - RefPtr m_tab_widget; - RefPtr m_tab_context_menu; - RefPtr m_tab_context_menu_sheet_view; - bool m_should_change_selected_cells { false }; - GUI::UndoStack m_undo_stack; - - OwnPtr m_workbook; - - void clipboard_action(bool is_cut); - RefPtr m_new_action; - RefPtr m_open_action; - RefPtr m_save_action; - RefPtr m_save_as_action; - RefPtr m_quit_action; - - RefPtr m_import_action; - - RefPtr m_cut_action; - RefPtr m_copy_action; - RefPtr m_paste_action; - RefPtr m_insert_emoji_action; - RefPtr m_undo_action; - RefPtr m_redo_action; - RefPtr m_change_background_color_action; - RefPtr m_change_foreground_color_action; - - RefPtr m_search_action; - RefPtr m_functions_help_action; - RefPtr m_about_action; - - RefPtr m_rename_action; -}; - -} diff --git a/Userland/Applications/Spreadsheet/Tests/basic.js b/Userland/Applications/Spreadsheet/Tests/basic.js deleted file mode 100644 index ff9cf791478..00000000000 --- a/Userland/Applications/Spreadsheet/Tests/basic.js +++ /dev/null @@ -1,302 +0,0 @@ -describe("Position", () => { - test("here", () => { - const workbook = createWorkbook(); - const sheet = createSheet(workbook, "Sheet 1"); - sheet.makeCurrent(); - - sheet.setCell("A", 0, "0"); - sheet.focusCell("A", 0); - - expect(here).toBeDefined(); - let position = here(); - expect(position).toBeDefined(); - - expect(position.column).toEqual("A"); - expect(position.name).toEqual("A0"); - expect(position.row).toEqual(0); - expect(position.sheet).toBe(sheet); - - expect(position.contents).toEqual("0"); - expect(position.value()).toEqual("0"); - expect(position.toString()).toEqual(""); - - position.contents = "=1 + 1"; - expect(position.contents).toEqual("=1 + 1"); - expect(position.value()).toEqual(2); - - expect(position.up().row).toEqual(0); - expect(position.down().row).toEqual(1); - expect(position.right().row).toEqual(0); - expect(position.left().row).toEqual(0); - - sheet.addColumn("B"); - expect(position.up().column).toEqual("A"); - expect(position.down().column).toEqual("A"); - expect(position.right().column).toEqual("B"); - expect(position.left().column).toEqual("A"); - }); - - test("Position.from_name", () => { - const workbook = createWorkbook(); - const sheet = createSheet(workbook, "Sheet 1"); - sheet.makeCurrent(); - - sheet.setCell("A", 0, "0"); - sheet.focusCell("A", 0); - - expect(Position.from_name).toBeDefined(); - let position = Position.from_name("A0"); - expect(position).toBeInstanceOf(Position); - - position = Position.from_name("A123"); - expect(position).toBeInstanceOf(Position); - }); -}); - -describe("Range", () => { - test("simple", () => { - const workbook = createWorkbook(); - const sheet = createSheet(workbook, "Sheet 1"); - sheet.makeCurrent(); - - sheet.setCell("A", 0, "0"); - sheet.setCell("A", 10, "0"); - sheet.setCell("B", 1, "0"); - sheet.focusCell("A", 0); - - expect(R).toBeDefined(); - let cellsVisited = 0; - R`A0:A10`.forEach(name => { - ++cellsVisited; - }); - expect(cellsVisited).toEqual(11); - - cellsVisited = 0; - R`A0:A10:1:2`.forEach(name => { - ++cellsVisited; - }); - expect(cellsVisited).toEqual(6); - }); - - test("multiple sheets", () => { - const workbook = createWorkbook(); - const sheet1 = createSheet(workbook, "Sheet 1"); - const sheet2 = createSheet(workbook, "Sheet 2"); - sheet1.makeCurrent(); - - sheet1.setCell("A", 0, "0"); - sheet1.focusCell("A", 0); - - sheet2.setCell("A", 0, "0"); - sheet2.setCell("A", 10, "0"); - sheet2.setCell("B", 1, "0"); - sheet2.focusCell("A", 0); - - expect(R).toBeDefined(); - let cellsVisited = 0; - R`sheet("Sheet 2"):A0:A10`.forEach(name => { - ++cellsVisited; - }); - expect(cellsVisited).toEqual(11); - }); - - test("Ranges", () => { - const workbook = createWorkbook(); - const sheet = createSheet(workbook, "Sheet 1"); - sheet.makeCurrent(); - - sheet.setCell("A", 0, "0"); - sheet.setCell("A", 10, "0"); - sheet.setCell("B", 1, "0"); - sheet.focusCell("A", 0); - - let cellsVisited = 0; - R`A0:A5`.union(R`A6:A10`).forEach(name => { - ++cellsVisited; - }); - expect(cellsVisited).toEqual(11); - }); - - test("Range#first", () => { - const workbook = createWorkbook(); - const sheet = createSheet(workbook, "Sheet 1"); - sheet.makeCurrent(); - - sheet.setCell("A", 0, "0"); - sheet.setCell("A", 1, "0"); - sheet.setCell("A", 2, "0"); - sheet.focusCell("A", 0); - expect(R`A0:A`.first().name).toEqual("A0"); - expect(R`A0:A25`.first().name).toEqual("A0"); - expect(R`A2:A25`.first().name).toEqual("A2"); - }); - - test("CommonRange#at", () => { - const workbook = createWorkbook(); - const sheet = createSheet(workbook, "Sheet 1"); - sheet.makeCurrent(); - let i = 0; - for (const col of ["A", "B"]) { - for (const row of [0, 1, 2]) { - sheet.setCell(col, row, Math.pow(i++, 2)); - } - } - - sheet.focusCell("A", 0); - expect(R`A0:A2`.at(2).name).toEqual("A2"); - expect(Ranges.from(R`A0:A2`, R`B0:B2`).at(5).name).toEqual("B2"); - }); - - test("CommonRange#toArray", () => { - const workbook = createWorkbook(); - const sheet = createSheet(workbook, "Sheet 1"); - sheet.makeCurrent(); - let i = 0; - for (const col of ["A", "B"]) { - for (const row of [0, 1, 2]) { - sheet.setCell(col, row, Math.pow(i++, 2)); - } - } - - sheet.focusCell("A", 0); - expect(R`A0:A2`.toArray().toString()).toEqual(",,"); - expect( - Ranges.from(R`A0:A2`, R`B0:B2`) - .toArray() - .toString() - ).toEqual(",,,,,"); - }); - - test("CommonRange#findIndex", () => { - makeSheet(); - const range = R`B`; - let idxs = []; - let values = []; - const idx = range.findIndex((val, idx) => { - idxs.push(idx); - values.push(val.value()); - return integer(val.value()) === 4; - }); - expect(idx).toEqual(1); - expect(values).toEqual(["1", "4"]); - expect(idxs).toEqual([0, 1]); - }); - - test("CommonRange#find", () => { - makeSheet(); - const range = R`B`; - let idxs = []; - let values = []; - const val = range.find((val, idx) => { - idxs.push(idx); - values.push(val.value()); - return integer(val.value()) === 4; - }); - expect(val.name).toEqual("B1"); - expect(values).toEqual(["1", "4"]); - expect(idxs).toEqual([0, 1]); - }); - - test("CommonRange#indexOf", () => { - makeSheet(); - const range = R`B`; - expect(range.indexOf("B1")).toEqual(1); - }); - - test("CommonRange#has", () => { - makeSheet(); - const range = R`B`; - expect(range.has("B1")).toEqual(true); - expect(range.has("B4")).toEqual(false); - }); -}); - -describe("SplitRange", () => { - makeSheet(); - test("Range#filter => SplitRange", () => { - const range = R`A0:B`.filter(c => c.value() % 2 === 1); - expect(range.toString()).toEqual('SplitRange.fromNames("A0", "A2", "B0", "B2")'); - expect(resolve(range)).toEqual(["1", "3", "1", "9"]); - expect(numericResolve(range)).toEqual([1, 3, 1, 9]); - expect(count(range)).toEqual(4); - }); - - test("Range#unique => SplitRange", () => { - makeSheet(); - - const origRange = R`A0:B`; - const uniqueRange = origRange.unique(); - expect(uniqueRange.toString()).toEqual( - 'SplitRange.fromNames("A0", "A1", "A2", "B1", "B2")' - ); - - const uniqueCount = count(uniqueRange); - // We expect that making a set (unique array) of the original range should equal the length of our unique range - expect(new Set(resolve(origRange)).size).toEqual(uniqueCount); - expect(uniqueCount).toEqual(5); - }); -}); - -describe("R function", () => { - makeSheet(); - - test("Check for correctness: R`A0:A`", () => { - const range = R`A0:A`; - expect(range.toString()).toEqual("R`A0:A`"); - expect(count(range)).toEqual(3); - expect(sum(range)).toEqual(6); - }); - test("Check for correctness: R`A`", () => { - const range = R`A`; - expect(range.toString()).toEqual("R`A0:A`"); - expect(count(range)).toEqual(3); - expect(sum(range)).toEqual(6); - }); - test("Check for correctness: R`A0:B`", () => { - const range = R`A0:B`; - expect(range.toString()).toEqual("R`A0:B`"); - expect(count(range)).toEqual(6); - expect(sum(range)).toEqual(20); - }); - test("Check for correctness: R`A0:B0`", () => { - const range = R`A0:B0`; - expect(range.toString()).toEqual("R`A0:B0`"); - expect(count(range)).toEqual(2); - expect(sum(range)).toEqual(2); - }); - test("Check for correctness: R`A0:B:2:1`", () => { - const range = R`A0:B2:1:2`; - expect(range.toString()).toEqual("R`A0:B2:1:2`"); - expect(range.toArray().toString()).toEqual( - ",,," - ); - expect(count(range)).toEqual(4); - expect(sum(range)).toEqual(14); - }); - test("R`B`", () => { - const range = R`B`; - expect(range.toString()).toEqual("R`B0:B`"); - expect(count(range)).toEqual(3); - expect(sum(range)).toEqual(14); - }); -}); - -/* - A B - +++ -0+1 1 -1+2 4 -2+3 9 -*/ - -function makeSheet() { - const workbook = createWorkbook(); - const sheet = createSheet(workbook, "Sheet 1"); - sheet.makeCurrent(); - for (const row of ["A", "B"]) { - for (let col of [0, 1, 2]) { - sheet.setCell(row, col++, row === "A" ? col : Math.pow(col, 2)); - } - } - sheet.focusCell("A", 0); -} diff --git a/Userland/Applications/Spreadsheet/Tests/free-functions.js b/Userland/Applications/Spreadsheet/Tests/free-functions.js deleted file mode 100644 index fb416a68fd8..00000000000 --- a/Userland/Applications/Spreadsheet/Tests/free-functions.js +++ /dev/null @@ -1,238 +0,0 @@ -describe("Basic functions", () => { - const workbook = createWorkbook(); - const sheet = createSheet(workbook, "Sheet 1"); - sheet.makeCurrent(); - - sheet.setCell("A", 0, "0"); - sheet.setCell("A", 1, "1"); - sheet.setCell("A", 2, "2"); - - test("select", () => { - expect(select).toBeDefined(); - expect(select(true, 1, 2)).toBe(1); - expect(select(false, 1, 2)).toBe(2); - }); - - test("choose", () => { - expect(choose).toBeDefined(); - expect(choose(0, 1, 2, 3)).toBe(1); - expect(choose(1, 1, 2, 3)).toBe(2); - expect(choose(3, 1, 2, 3)).toBeUndefined(); - expect(choose(-1, 1, 2, 3)).toBeUndefined(); - }); - - test("now", () => { - expect(now).toBeDefined(); - expect(now()).toBeInstanceOf(Date); - }); - - test("randRange", () => { - expect(randRange).toBeDefined(); - }); - - test("integer", () => { - expect(integer).toBeDefined(); - expect(integer("0")).toEqual(0); - expect(integer("32")).toEqual(32); - }); - - test("sheet", () => { - expect(globalThis.sheet).toBeDefined(); - expect(globalThis.sheet("Sheet 1")).toBe(sheet); - expect(globalThis.sheet("Not a sheet")).toBeUndefined(); - }); - - test("reduce", () => { - expect(reduce).toBeDefined(); - expect(reduce(acc => acc + 1, 0, [1, 2, 3, 4])).toEqual(4); - expect(reduce(acc => acc + 1, 0, [])).toEqual(0); - expect(reduce((acc, x) => acc + "|" + x.toString(), 0, R`A0:A2`)).toEqual("0|0|1|2"); - expect(reduce((acc, x) => acc + "|" + x.toString(), 0, R`A0:A0`)).toEqual("0|0"); - }); - - test("numericReduce", () => { - expect(numericReduce).toBeDefined(); - expect(numericReduce(acc => acc + 1, 0, [1, 2, 3, 4])).toEqual(4); - expect(numericReduce(acc => acc + 1, 0, [])).toEqual(0); - expect(numericReduce((acc, x) => acc + x, 19, R`A0:A2`)).toEqual(22); - expect(numericReduce(acc => acc + 1, 3, R`A0:A0`)).toEqual(4); - }); - - test("numericResolve", () => { - expect(numericResolve).toBeDefined(); - expect(numericResolve(["0", "1", "2"])).toEqual([0, 1, 2]); - expect(numericResolve([])).toEqual([]); - expect(numericResolve(R`A0:A2`)).toEqual([0, 1, 2]); - expect(numericResolve(R`A0:A0`)).toEqual([0]); - }); - - test("resolve", () => { - expect(resolve).toBeDefined(); - expect(resolve(["A", "B", "C"])).toEqual(["A", "B", "C"]); - expect(resolve([])).toEqual([]); - expect(resolve(R`A0:A2`)).toEqual(["0", "1", "2"]); - expect(resolve(R`A0:A0`)).toEqual(["0"]); - }); -}); - -describe("Statistics", () => { - const workbook = createWorkbook(); - const sheet = createSheet(workbook, "Sheet 1"); - sheet.makeCurrent(); - - for (let i = 0; i < 10; ++i) sheet.setCell("A", i, `${i}`); - for (let i = 0; i < 10; ++i) sheet.setCell("B", i, `${i * i}`); - - test("sum", () => { - expect(sum).toBeDefined(); - expect(sum(R`A0:A9`)).toEqual(45); - }); - - test("sumIf", () => { - expect(sumIf).toBeDefined(); - expect(sumIf(x => !Number.isNaN(x), R`A0:A10`)).toEqual(45); - }); - - test("count", () => { - expect(count).toBeDefined(); - expect(count(R`A0:A9`)).toEqual(10); - }); - - test("countIf", () => { - expect(countIf).toBeDefined(); - expect(countIf(x => x, R`A0:A10`)).toEqual(10); - }); - - test("average", () => { - expect(average).toBeDefined(); - expect(average(R`A0:A9`)).toEqual(4.5); - }); - - test("averageIf", () => { - expect(averageIf).toBeDefined(); - expect(averageIf(x => !Number.isNaN(x), R`A0:A10`)).toEqual(4.5); - }); - - test("minIf", () => { - expect(minIf).toBeDefined(); - expect(minIf(x => x > 25, R`B0:B9`)).toEqual(36); - }); - - test("min", () => { - expect(min).toBeDefined(); - expect(min(R`B0:B9`)).toEqual(0); - }); - - test("maxIf", () => { - expect(maxIf).toBeDefined(); - expect(maxIf(x => x > 25, R`B0:B9`)).toEqual(81); - }); - - test("max", () => { - expect(max).toBeDefined(); - expect(max(R`B0:B9`)).toEqual(81); - }); - - test("sumProductIf", () => { - expect(sumProductIf).toBeDefined(); - expect(sumProductIf((a, b) => b > 25, R`A0:A9`, R`B0:B9`)).toEqual(1800); - }); - - test("sumProduct", () => { - expect(sumProduct).toBeDefined(); - expect(sumProduct(R`A0:A9`, R`B0:B9`)).toEqual(2025); - }); - - test("median", () => { - expect(median).toBeDefined(); - expect(median(R`A0:A9`)).toEqual(4.5); - expect(median(R`A0:A2`)).toEqual(1); - }); - - test("variance", () => { - expect(variance).toBeDefined(); - expect(variance(R`A0:A0`)).toEqual(0); - expect(variance(R`A0:A9`)).toEqual(82.5); - }); - - test("mode", () => { - expect(mode).toBeDefined(); - expect(mode(R`A0:A0`.union(R`A0:A0`).union(R`A1:A9`))).toEqual(0); - }); - - test("stddev", () => { - expect(stddev).toBeDefined(); - expect(stddev(R`A0:A0`)).toEqual(0); - expect(stddev(R`A0:A9`)).toEqual(Math.sqrt(82.5)); - }); -}); - -describe("Lookup", () => { - const workbook = createWorkbook(); - const sheet = createSheet(workbook, "Sheet 1"); - sheet.makeCurrent(); - - for (let i = 0; i < 10; ++i) { - sheet.setCell("A", i, `${i}`); - sheet.setCell("B", i, `B${i}`); - } - - sheet.focusCell("A", 0); - - test("row", () => { - expect(row()).toEqual(0); - }); - - test("column", () => { - expect(column()).toEqual("A"); - }); - - test("lookup", () => { - expect(lookup).toBeDefined(); - // Note: String ordering. - expect(lookup("2", R`A0:A9`, R`B0:B9`)).toEqual("B2"); - expect(() => lookup("20", R`A0:A9`, R`B0:B9`)).toThrow(); - expect(lookup("80", R`A0:A9`, R`B0:B9`, undefined, "nextlargest")).toEqual("B9"); - }); - - test("reflookup", () => { - expect(reflookup).toBeDefined(); - // Note: String ordering. - expect(reflookup("2", R`A0:A9`, R`B0:B9`).name).toEqual("B2"); - expect(() => reflookup("20", R`A0:A9`, R`B0:B9`)).toThrow(); - expect(reflookup("80", R`A0:A9`, R`B0:B9`, undefined, "nextlargest").name).toEqual("B9"); - }); -}); - -describe("integer() function", () => { - test("undefined", () => { - expect(() => integer(undefined)).toThrow(Error); - }); - test("null", () => { - expect(() => integer(null)).toThrow(Error); - }); - test("NaN", () => { - expect(() => integer(NaN)).toThrow(Error); - }); - test("object", () => { - expect(() => integer({})).toThrow(Error); - }); - test("function", () => { - expect(() => integer(() => {})).toThrow(Error); - }); - test("try 1 as string", () => { - expect(integer("1")).toBe(1); - }); - test("try 1 as number", () => { - expect(integer(1)).toBe(1); - }); - test("try 1000000 as string", () => { - expect(integer("1000000")).toBe(1000000); - }); - test("try 1000000 as number", () => { - expect(integer(1000000)).toBe(1000000); - }); - test("don't just allow any strings", () => { - expect(() => integer("is this NaN yet?")).toThrow(); - }); -}); diff --git a/Userland/Applications/Spreadsheet/Tests/mock.test-common.js b/Userland/Applications/Spreadsheet/Tests/mock.test-common.js deleted file mode 100644 index 7a35cc4f9af..00000000000 --- a/Userland/Applications/Spreadsheet/Tests/mock.test-common.js +++ /dev/null @@ -1,192 +0,0 @@ -var thisSheet; -var workbook; - -var createWorkbook = () => { - return { - __sheets: new Map(), - sheet(nameOrIndex) { - if (typeof nameOrIndex !== "number") return this.__sheets.get(nameOrIndex); - for (const entry of this.__sheets) { - if (nameOrIndex === 0) return entry[1]; - nameOrIndex--; - } - return undefined; - }, - }; -}; - -function toBijectiveBase(number) { - const alpha = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; - number += 1; - let c = 0; - let x = 1; - while (number >= x) { - ++c; - number -= x; - x *= 26; - } - - let s = ""; - for (let i = 0; i < c; i++) { - s = alpha.charAt(number % 26) + s; - number = Math.floor(number / 26); - } - - return s; -} - -function __evaluate(expression, that) { - const object = Object.create(null); - for (const entry of that.__cells) { - const cell = JSON.parse(entry[0]); - object[`${cell[0]}${cell[1]}`] = entry[1][1]; - } - - const sheetObject = that; - let __value; - - // Warning: Dragons and fire ahead. - with (that.__workbook) { - with (object) { - with (sheetObject) { - __value = eval(expression); - } - } - } - return __value; -} - -class Sheet { - constructor(workbook) { - this.__cells = new Map(); - this.__columns = new Set(); - this.__workbook = workbook; - this.__currentCellPosition = undefined; - } - - get_real_cell_contents(name) { - const cell = this.parse_cell_name(name); - if (cell === undefined) throw new TypeError("Invalid cell name"); - return this.getCell(cell.column, cell.row)[0]; - } - - set_real_cell_contents(name, value) { - const cell = this.parse_cell_name(name); - if (cell === undefined) throw new TypeError("Invalid cell name"); - - this.setCell(cell.column, cell.row, value); - } - - parse_cell_name(name) { - const match = /^([a-zA-Z]+)(\d+)$/.exec(name); - if (!Array.isArray(match)) return undefined; - - return { - column: match[1], - row: +match[2], - }; - } - - current_cell_position() { - return this.__currentCellPosition; - } - - column_index(name) { - let i = 0; - for (const column of this.__columns) { - if (column === name) return i; - ++i; - } - } - - column_arithmetic(name, offset) { - if (offset < 0) { - const columns = this.getColumns(); - let index = columns.indexOf(name); - if (index === -1) throw new TypeError(`${name} is not a valid column name`); - - index += offset; - if (index < 0) return columns[0]; - return columns[index]; - } - - let found = false; - for (const column of this.__columns) { - if (!found) found = column === name; - if (found) { - if (offset === 0) return column; - offset--; - } - } - - if (!found) throw new TypeError(`${name} is not a valid column name`); - - let newName; - for (let i = 0; i < offset; ++i) { - newName = toBijectiveBase(this.__columns.size); - this.addColumn(newName); - } - return newName; - } - - get_column_bound(name) { - let bound = 0; - for (const entry of this.__cells) { - const [column, row] = JSON.parse(entry[0]); - if (column !== name) continue; - bound = Math.max(bound, row); - } - return bound; - } - - evaluate(currentColumn, currentRow, expression) { - const currentCellSave = this.__currentCellPosition; - this.__currentCellPosition = { column: currentColumn, row: currentRow }; - try { - return __evaluate(expression, this); - } finally { - this.__currentCellPosition = currentCellSave; - } - } - - addColumn(name) { - this.__columns.add(name); - } - - getColumns() { - return Array.from(this.__columns); - } - - setCell(column, row, source, value = undefined) { - this.addColumn(column); - source = `${source}`; - if (value === undefined) { - value = source; - if (value[0] === "=") value = this.evaluate(column, row, value.substr(1)); - } - - this.__cells.set(JSON.stringify([column, row]), [source, value]); - this[`${column}${row}`] = value; - } - - getCell(column, row) { - const data = this.__cells.get(JSON.stringify([column, row])); - if (data === undefined) return undefined; - return data; - } - - focusCell(column, row) { - this.__currentCellPosition = { column, row }; - } - - makeCurrent() { - thisSheet = this; - workbook = this.__workbook; - } -} - -var createSheet = (workbook, name) => { - const sheet = new Sheet(workbook); - workbook.__sheets.set(name, sheet); - return sheet; -}; diff --git a/Userland/Applications/Spreadsheet/Tests/test-harness.js b/Userland/Applications/Spreadsheet/Tests/test-harness.js deleted file mode 100644 index 283d76a0008..00000000000 --- a/Userland/Applications/Spreadsheet/Tests/test-harness.js +++ /dev/null @@ -1,46 +0,0 @@ -describe("Harness-defined functions", () => { - test("createWorkbook", () => { - expect(createWorkbook).toBeDefined(); - const workbook = createWorkbook(); - expect(workbook).toBeDefined(); - expect(workbook.sheet).toBeDefined(); - }); - test("createSheet", () => { - const workbook = createWorkbook(); - const sheet = createSheet(workbook, "foo"); - expect(sheet).toBeDefined(); - expect(sheet.get_real_cell_contents).toBeDefined(); - expect(sheet.set_real_cell_contents).toBeDefined(); - expect(sheet.parse_cell_name).toBeDefined(); - expect(sheet.current_cell_position).toBeDefined(); - expect(sheet.column_index).toBeDefined(); - expect(sheet.column_arithmetic).toBeDefined(); - expect(sheet.get_column_bound).toBeDefined(); - }); - test("Sheet mock behavior", () => { - const workbook = createWorkbook(); - const sheet = createSheet(workbook, "foo"); - sheet.setCell("A", 0, "10"); - expect(sheet.getCell("A", 0)).toEqual(["10", "10"]); - - sheet.setCell("A", 0, "=10"); - expect(sheet.getCell("A", 0)).toEqual(["=10", 10]); - - expect(sheet.getColumns()).toEqual(["A"]); - }); - test("Workbook mock behavior", () => { - const workbook = createWorkbook(); - const sheet = createSheet(workbook, "foo"); - expect(workbook.sheet("foo")).toBe(sheet); - expect(workbook.sheet(0)).toBe(sheet); - expect(workbook.sheet(1)).toBeUndefined(); - expect(workbook.sheet("bar")).toBeUndefined(); - }); - test("Referencing cells", () => { - const workbook = createWorkbook(); - const sheet = createSheet(workbook, "foo"); - sheet.setCell("A", 0, "42"); - sheet.setCell("A", 1, "=A0"); - expect(sheet.getCell("A", 1)).toEqual(["=A0", "42"]); - }); -}); diff --git a/Userland/Applications/Spreadsheet/Workbook.cpp b/Userland/Applications/Spreadsheet/Workbook.cpp deleted file mode 100644 index e34dab241b9..00000000000 --- a/Userland/Applications/Spreadsheet/Workbook.cpp +++ /dev/null @@ -1,88 +0,0 @@ -/* - * Copyright (c) 2020-2021, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "Workbook.h" -#include "ExportDialog.h" -#include "ImportDialog.h" -#include "JSIntegration.h" -#include -#include -#include -#include -#include -#include -#include -#include - -namespace Spreadsheet { - -Workbook::Workbook(Vector>&& sheets, GUI::Window& parent_window) - : m_sheets(move(sheets)) - , m_vm(JS::VM::create().release_value_but_fixme_should_propagate_errors()) - , m_root_execution_context(JS::create_simple_execution_context(m_vm)) - , m_main_execution_context(JS::ExecutionContext::create()) - , m_parent_window(parent_window) -{ - auto& realm = *m_root_execution_context->realm; - auto& vm = realm.vm(); - m_workbook_object = vm.heap().allocate(realm, realm, *this); - realm.global_object().define_direct_property("workbook", workbook_object(), JS::default_attributes); - - m_main_execution_context->this_value = &realm.global_object(); - m_main_execution_context->function_name = JS::PrimitiveString::create(vm, "(global execution context)"sv); - m_main_execution_context->lexical_environment = &realm.global_environment(); - m_main_execution_context->variable_environment = &realm.global_environment(); - m_main_execution_context->realm = &realm; - m_main_execution_context->is_strict_mode = true; - m_vm->push_execution_context(*m_main_execution_context); - m_vm->set_dynamic_imports_allowed(true); -} - -bool Workbook::set_filename(ByteString const& filename) -{ - if (m_current_filename == filename) - return false; - - m_current_filename = filename; - return true; -} - -ErrorOr Workbook::open_file(ByteString const& filename, Core::File& file) -{ - auto mime = Core::guess_mime_type_based_on_filename(filename); - - // Make an import dialog, we might need to import it. - m_sheets = TRY(ImportDialog::make_and_run_for(m_parent_window, mime, filename, file, *this)); - - set_filename(filename); - - return {}; -} - -ErrorOr Workbook::write_to_file(ByteString const& filename, Core::File& stream) -{ - auto mime = Core::guess_mime_type_based_on_filename(filename); - - // Make an export dialog, we might need to import it. - TRY(ExportDialog::make_and_run_for(mime, stream, filename, *this)); - - set_filename(filename); - set_dirty(false); - return {}; -} - -ErrorOr Workbook::import_file(ByteString const& filename, Core::File& file) -{ - auto mime = Core::guess_mime_type_based_on_filename(filename); - - auto sheets = TRY(ImportDialog::make_and_run_for(m_parent_window, mime, filename, file, *this)); - auto has_any_changes = !sheets.is_empty(); - m_sheets.extend(move(sheets)); - - return has_any_changes; -} - -} diff --git a/Userland/Applications/Spreadsheet/Workbook.h b/Userland/Applications/Spreadsheet/Workbook.h deleted file mode 100644 index bf2d4706fe2..00000000000 --- a/Userland/Applications/Spreadsheet/Workbook.h +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright (c) 2020, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include "Forward.h" -#include "Spreadsheet.h" - -namespace Spreadsheet { - -class Workbook { -public: - Workbook(Vector>&& sheets, GUI::Window& parent_window); - - ErrorOr open_file(ByteString const& filename, Core::File&); - ErrorOr write_to_file(ByteString const& filename, Core::File&); - - ErrorOr import_file(ByteString const& filename, Core::File&); - - ByteString const& current_filename() const { return m_current_filename; } - bool set_filename(ByteString const& filename); - bool dirty() { return m_dirty; } - void set_dirty(bool dirty) { m_dirty = dirty; } - - bool has_sheets() const { return !m_sheets.is_empty(); } - - Vector> const& sheets() const { return m_sheets; } - Vector> sheets() { return m_sheets; } - - Sheet& add_sheet(StringView name) - { - auto sheet = Sheet::construct(name, *this); - m_sheets.append(sheet); - return *sheet; - } - - WorkbookObject* workbook_object() { return m_workbook_object; } - JS::VM& vm() { return *m_vm; } - JS::VM const& vm() const { return *m_vm; } - -private: - Vector> m_sheets; - NonnullRefPtr m_vm; - NonnullOwnPtr m_root_execution_context; - - JS::GCPtr m_workbook_object; - NonnullOwnPtr m_main_execution_context; - GUI::Window& m_parent_window; - - ByteString m_current_filename; - bool m_dirty { false }; -}; - -} diff --git a/Userland/Applications/Spreadsheet/Writers/CSV.h b/Userland/Applications/Spreadsheet/Writers/CSV.h deleted file mode 100644 index 7fb5f4148d5..00000000000 --- a/Userland/Applications/Spreadsheet/Writers/CSV.h +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright (c) 2020, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include "XSV.h" -#include -#include - -namespace Writer { - -class CSV { -public: - template - static ErrorOr generate(Stream& output, ContainerType const& data, Vector headers = {}, WriterBehavior behaviors = default_behaviors()) - { - return XSV::generate(output, data, { ",", "\"", WriterTraits::Repeat }, move(headers), behaviors); - } - - template - static ErrorOr generate_preview(Stream& output, ContainerType const& data, Vector headers = {}, WriterBehavior behaviors = default_behaviors()) - { - return XSV::generate_preview(output, data, { ",", "\"", WriterTraits::Repeat }, move(headers), behaviors); - } -}; - -} diff --git a/Userland/Applications/Spreadsheet/Writers/Test/TestXSVWriter.cpp b/Userland/Applications/Spreadsheet/Writers/Test/TestXSVWriter.cpp deleted file mode 100644 index d57dc1768aa..00000000000 --- a/Userland/Applications/Spreadsheet/Writers/Test/TestXSVWriter.cpp +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright (c) 2020, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include - -#include "../CSV.h" -#include - -TEST_CASE(can_write) -{ - Vector> data = { - { 1, 2, 3 }, - { 4, 5, 6 }, - { 7, 8, 9 }, - }; - - AllocatingMemoryStream stream; - MUST(Writer::CSV::generate(stream, data)); - - auto expected_output = R"~(1,2,3 -4,5,6 -7,8,9 -)~"sv; - - auto buffer = MUST(stream.read_until_eof()); - EXPECT_EQ(StringView { buffer.bytes() }, expected_output); -} - -TEST_CASE(can_write_with_header) -{ - Vector> data = { - { 1, 2, 3 }, - { 4, 5, 6 }, - { 7, 8, 9 }, - }; - - AllocatingMemoryStream stream; - MUST(Writer::CSV::generate(stream, data, { "A"sv, "B\""sv, "C"sv })); - - auto expected_output = R"~(A,"B""",C -1,2,3 -4,5,6 -7,8,9 -)~"sv; - - auto buffer = MUST(stream.read_until_eof()); - EXPECT_EQ(StringView { buffer.bytes() }, expected_output); -} - -TEST_CASE(can_write_with_different_behaviors) -{ - Vector> data = { - { "Well", "Hello\"", "Friends" }, - { "We\"ll", "Hello,", " Friends" }, - }; - - AllocatingMemoryStream stream; - MUST(Writer::CSV::generate(stream, data, { "A"sv, "B\""sv, "C"sv }, Writer::WriterBehavior::QuoteOnlyInFieldStart | Writer::WriterBehavior::WriteHeaders)); - - auto expected_output = R"~(A,B",C -Well,Hello",Friends -We"ll,"Hello,", Friends -)~"sv; - - auto buffer = MUST(stream.read_until_eof()); - EXPECT_EQ(StringView { buffer.bytes() }, expected_output); -} diff --git a/Userland/Applications/Spreadsheet/Writers/XSV.h b/Userland/Applications/Spreadsheet/Writers/XSV.h deleted file mode 100644 index eb95b37ff42..00000000000 --- a/Userland/Applications/Spreadsheet/Writers/XSV.h +++ /dev/null @@ -1,176 +0,0 @@ -/* - * Copyright (c) 2020-2022, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include -#include -#include - -namespace Writer { - -enum class WriterBehavior : u32 { - None = 0, - WriteHeaders = 1, - AllowNewlinesInFields = WriteHeaders << 1, - QuoteOnlyInFieldStart = WriteHeaders << 2, - QuoteAll = WriteHeaders << 3, -}; -AK_ENUM_BITWISE_OPERATORS(WriterBehavior); - -struct WriterTraits { - ByteString separator; - ByteString quote { "\"" }; - enum QuoteEscape { - Repeat, - Backslash, - } quote_escape { Repeat }; -}; - -constexpr WriterBehavior default_behaviors() -{ - return WriterBehavior::None; -} - -template> -class XSV { -public: - static ErrorOr generate(Stream& output, ContainerType const& data, WriterTraits traits, HeaderType headers = {}, WriterBehavior behaviors = default_behaviors()) - { - auto writer = XSV(output, data, traits, headers, behaviors); - auto with_headers = has_flag(writer.m_behaviors, WriterBehavior::WriteHeaders); - if (with_headers) { - TRY(writer.write_row(writer.m_names)); - TRY(writer.m_output.write_until_depleted({ "\n", 1 })); - } - - for (auto&& row : writer.m_data) { - if (with_headers) { - if (row.size() != writer.m_names.size()) - return Error::from_string_literal("Header count does not match given column count"); - } - - TRY(writer.write_row(row)); - TRY(writer.m_output.write_until_depleted({ "\n", 1 })); - } - return {}; - } - - static ErrorOr generate_preview(Stream& output, ContainerType const& data, WriterTraits traits, HeaderType headers = {}, WriterBehavior behaviors = default_behaviors()) - { - auto writer = XSV(output, data, traits, headers, behaviors); - auto lines_written = 0; - constexpr auto max_preview_lines = 8; - - auto with_headers = has_flag(writer.m_behaviors, WriterBehavior::WriteHeaders); - if (with_headers) { - TRY(writer.write_row(writer.m_names)); - TRY(writer.m_output.write_until_depleted({ "\n", 1 })); - ++lines_written; - } - - for (auto&& row : writer.m_data) { - if (with_headers) { - if (row.size() != writer.m_names.size()) - return Error::from_string_literal("Header count does not match given column count"); - } - - TRY(writer.write_row(row)); - TRY(writer.m_output.write_until_depleted({ "\n", 1 })); - ++lines_written; - - if (lines_written >= max_preview_lines) - break; - } - return {}; - } - -private: - XSV(Stream& output, ContainerType const& data, WriterTraits traits, HeaderType headers = {}, WriterBehavior behaviors = default_behaviors()) - : m_data(data) - , m_traits(move(traits)) - , m_behaviors(behaviors) - , m_names(headers) - , m_output(output) - { - if (!headers.is_empty()) - m_behaviors = m_behaviors | WriterBehavior::WriteHeaders; - } - - template - ErrorOr write_row(T&& row) - { - bool first = true; - for (auto&& entry : row) { - if (!first) { - TRY(m_output.write_until_depleted(m_traits.separator.bytes())); - } - first = false; - TRY(write_entry(entry)); - } - return {}; - } - - template - ErrorOr write_entry(T&& entry) - { - auto string = ByteString::formatted("{}", FormatIfSupported(entry)); - - auto safe_to_write_normally = !has_flag(m_behaviors, WriterBehavior::QuoteAll) - && !string.contains('\n') - && !string.contains(m_traits.separator); - - if (safe_to_write_normally) { - if (has_flag(m_behaviors, WriterBehavior::QuoteOnlyInFieldStart)) - safe_to_write_normally = !string.starts_with(m_traits.quote); - else - safe_to_write_normally = !string.contains(m_traits.quote); - } - - if (safe_to_write_normally) { - if (!string.is_empty()) - TRY(m_output.write_until_depleted(string.bytes())); - return {}; - } - - TRY(m_output.write_until_depleted(m_traits.quote.bytes())); - - GenericLexer lexer(string); - while (!lexer.is_eof()) { - if (lexer.consume_specific(m_traits.quote.view())) { - switch (m_traits.quote_escape) { - case WriterTraits::Repeat: - TRY(m_output.write_until_depleted(m_traits.quote.bytes())); - TRY(m_output.write_until_depleted(m_traits.quote.bytes())); - break; - case WriterTraits::Backslash: - TRY(m_output.write_until_depleted({ "\\", 1 })); - TRY(m_output.write_until_depleted(m_traits.quote.bytes())); - break; - } - continue; - } - - auto ch = lexer.consume(); - TRY(m_output.write_until_depleted({ &ch, 1 })); - } - - TRY(m_output.write_until_depleted(m_traits.quote.bytes())); - return {}; - } - - ContainerType const& m_data; - WriterTraits m_traits; - WriterBehavior m_behaviors; - HeaderType m_names; - Stream& m_output; -}; - -} diff --git a/Userland/Applications/Spreadsheet/csv_export.gml b/Userland/Applications/Spreadsheet/csv_export.gml deleted file mode 100644 index 97a04e894f6..00000000000 --- a/Userland/Applications/Spreadsheet/csv_export.gml +++ /dev/null @@ -1,145 +0,0 @@ -@GUI::Widget { - layout: @GUI::VerticalBoxLayout { - margins: [20] - } - - @GUI::HorizontalSplitter { - @GUI::Widget { - name: "csv_options" - layout: @GUI::VerticalBoxLayout {} - - @GUI::Widget { - layout: @GUI::HorizontalBoxLayout {} - - @GUI::GroupBox { - title: "Delimiter" - layout: @GUI::VerticalBoxLayout { - margins: [10, 8, 8] - } - - @GUI::RadioButton { - name: "delimiter_comma_radio" - text: "Comma" - autosize: true - } - - @GUI::RadioButton { - name: "delimiter_semicolon_radio" - text: "Semicolon" - autosize: true - } - - @GUI::RadioButton { - name: "delimiter_tab_radio" - text: "Tab" - autosize: true - } - - @GUI::RadioButton { - name: "delimiter_space_radio" - text: "Space" - autosize: true - } - - @GUI::Widget { - fixed_height: 25 - layout: @GUI::HorizontalBoxLayout {} - - @GUI::RadioButton { - name: "delimiter_other_radio" - text: "Other: " - autosize: true - } - - @GUI::TextBox { - name: "delimiter_other_text_box" - text: "" - } - } - } - - @GUI::GroupBox { - title: "Quote" - layout: @GUI::VerticalBoxLayout { - margins: [10, 8, 8] - } - - @GUI::RadioButton { - name: "quote_single_radio" - text: "Single Quotes" - autosize: true - } - - @GUI::RadioButton { - name: "quote_double_radio" - text: "Double Quotes" - autosize: true - } - - @GUI::Widget { - fixed_height: 25 - layout: @GUI::HorizontalBoxLayout {} - - @GUI::RadioButton { - name: "quote_other_radio" - text: "Other: " - autosize: true - } - - @GUI::TextBox { - name: "quote_other_text_box" - text: "" - } - } - - @GUI::Layout::Spacer {} - - @GUI::Widget { - fixed_height: 25 - layout: @GUI::HorizontalBoxLayout {} - - @GUI::Label { - text: "Escape by " - autosize: true - } - - @GUI::ComboBox { - name: "quote_escape_combo_box" - model_only: true - } - } - - @GUI::Layout::Spacer {} - } - } - - @GUI::Widget { - fixed_height: 25 - layout: @GUI::HorizontalBoxLayout {} - - @GUI::CheckBox { - name: "export_header_check_box" - text: "Export with headers" - } - - @GUI::CheckBox { - name: "quote_all_fields_check_box" - text: "Quote all fields" - } - } - } - - @GUI::GroupBox { - title: "Data Preview" - fixed_width: 150 - layout: @GUI::VerticalBoxLayout { - margins: [10, 8, 8] - } - - @GUI::TextEditor { - name: "data_preview_text_editor" - mode: "ReadOnly" - } - } - } -} diff --git a/Userland/Applications/Spreadsheet/csv_import.gml b/Userland/Applications/Spreadsheet/csv_import.gml deleted file mode 100644 index e8403db919e..00000000000 --- a/Userland/Applications/Spreadsheet/csv_import.gml +++ /dev/null @@ -1,166 +0,0 @@ -@GUI::Widget { - layout: @GUI::VerticalBoxLayout { - margins: [20] - } - - @GUI::HorizontalSplitter { - @GUI::Widget { - name: "csv_options" - layout: @GUI::VerticalBoxLayout {} - - @GUI::Widget { - layout: @GUI::HorizontalBoxLayout {} - - @GUI::GroupBox { - title: "Delimiter" - layout: @GUI::VerticalBoxLayout { - margins: [10, 8, 8] - } - - @GUI::RadioButton { - name: "delimiter_comma_radio" - text: "Comma" - autosize: true - } - - @GUI::RadioButton { - name: "delimiter_semicolon_radio" - text: "Semicolon" - autosize: true - } - - @GUI::RadioButton { - name: "delimiter_tab_radio" - text: "Tab" - autosize: true - } - - @GUI::RadioButton { - name: "delimiter_space_radio" - text: "Space" - autosize: true - } - - @GUI::Widget { - fixed_height: 25 - layout: @GUI::HorizontalBoxLayout {} - - @GUI::RadioButton { - name: "delimiter_other_radio" - text: "Other: " - autosize: true - } - - @GUI::TextBox { - name: "delimiter_other_text_box" - text: "" - } - } - } - - @GUI::GroupBox { - title: "Quote" - layout: @GUI::VerticalBoxLayout { - margins: [10, 8, 8] - } - - @GUI::RadioButton { - name: "quote_single_radio" - text: "Single Quotes" - autosize: true - } - - @GUI::RadioButton { - name: "quote_double_radio" - text: "Double Quotes" - autosize: true - } - - @GUI::Widget { - fixed_height: 25 - layout: @GUI::HorizontalBoxLayout {} - - @GUI::RadioButton { - name: "quote_other_radio" - text: "Other: " - autosize: true - } - - @GUI::TextBox { - name: "quote_other_text_box" - text: "" - } - } - - @GUI::Layout::Spacer {} - - @GUI::Widget { - fixed_height: 25 - layout: @GUI::HorizontalBoxLayout {} - - @GUI::Label { - text: "Escape by " - autosize: true - } - - @GUI::ComboBox { - name: "quote_escape_combo_box" - model_only: true - } - } - - @GUI::Layout::Spacer {} - } - } - - @GUI::GroupBox { - title: "Trim Field Spaces" - fixed_height: 40 - layout: @GUI::VerticalBoxLayout { - margins: [4, 4, 0] - } - - @GUI::Widget { - layout: @GUI::HorizontalBoxLayout {} - - @GUI::CheckBox { - name: "trim_leading_field_spaces_check_box" - text: "Leading spaces" - } - - @GUI::CheckBox { - name: "trim_trailing_field_spaces_check_box" - text: "Trailing spaces" - } - } - } - - @GUI::CheckBox { - fixed_height: 15 - name: "read_header_check_box" - text: "Read a header row" - } - } - - @GUI::GroupBox { - title: "Data Preview" - fixed_width: 150 - layout: @GUI::VerticalBoxLayout { - margins: [10, 8, 8] - } - - @GUI::StackWidget { - name: "data_preview_widget" - - @GUI::TableView { - name: "data_preview_table_view" - } - - @GUI::Label { - name: "data_preview_error_label" - word_wrap: true - } - } - } - } -} diff --git a/Userland/Applications/Spreadsheet/main.cpp b/Userland/Applications/Spreadsheet/main.cpp deleted file mode 100644 index 9f69f9426f2..00000000000 --- a/Userland/Applications/Spreadsheet/main.cpp +++ /dev/null @@ -1,78 +0,0 @@ -/* - * Copyright (c) 2020-2022, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "HelpWindow.h" -#include "Spreadsheet.h" -#include "SpreadsheetWidget.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -ErrorOr serenity_main(Main::Arguments arguments) -{ - TRY(Core::System::pledge("stdio recvfd sendfd rpath fattr unix cpath wpath thread map_fixed")); - - auto app = TRY(GUI::Application::create(arguments)); - - StringView filename; - - Core::ArgsParser args_parser; - args_parser.add_positional_argument(filename, "File to read from", "file", Core::ArgsParser::Required::No); - - args_parser.parse(arguments); - - if (!filename.is_empty()) { - if (!FileSystem::exists(filename) || FileSystem::is_directory(filename)) { - warnln("File does not exist or is a directory: {}", filename); - return 1; - } - } - - Config::pledge_domain("Spreadsheet"); - app->set_config_domain("Spreadsheet"_string); - - TRY(Core::System::unveil("/tmp/session/%sid/portal/filesystemaccess", "rw")); - TRY(Core::System::unveil("/tmp/session/%sid/portal/webcontent", "rw")); - TRY(Core::System::unveil("/etc", "r")); - TRY(Core::System::unveil("/res", "r")); - TRY(Core::System::unveil(nullptr, nullptr)); - - auto app_icon = GUI::Icon::default_icon("app-spreadsheet"sv); - auto window = GUI::Window::construct(); - window->restore_size_and_position("Spreadsheet"sv, "Window"sv, { { 640, 480 } }); - window->save_size_and_position_on_close("Spreadsheet"sv, "Window"sv); - window->set_icon(app_icon.bitmap_for_size(16)); - - auto spreadsheet_widget = window->set_main_widget(*window, Vector> {}, filename.is_empty()); - - TRY(spreadsheet_widget->initialize_menubar(*window)); - spreadsheet_widget->update_window_title(); - - window->on_close_request = [&]() -> GUI::Window::CloseRequestDecision { - if (spreadsheet_widget->request_close()) - return GUI::Window::CloseRequestDecision::Close; - return GUI::Window::CloseRequestDecision::StayOpen; - }; - - window->show(); - - if (!filename.is_empty()) { - auto file = TRY(FileSystemAccessClient::Client::the().request_file_read_only_approved(window, filename)); - spreadsheet_widget->load_file(file.filename(), file.stream()); - } - - return app->exec(); -} diff --git a/Userland/Applications/Spreadsheet/select_format_page.gml b/Userland/Applications/Spreadsheet/select_format_page.gml deleted file mode 100644 index 389665b64a0..00000000000 --- a/Userland/Applications/Spreadsheet/select_format_page.gml +++ /dev/null @@ -1,29 +0,0 @@ -@GUI::Widget { - name: "select_format" - layout: @GUI::VerticalBoxLayout { - margins: [20] - } - - @GUI::Label { - text: "Please double-check the guessed file type\nor select the correct one below" - text_alignment: "TopLeft" - fixed_height: 32 - } - - @GUI::Widget { - fixed_height: 25 - layout: @GUI::HorizontalBoxLayout {} - - @GUI::Label { - text: "Type: " - autosize: true - } - - @GUI::ComboBox { - name: "select_format_page_format_combo_box" - model_only: true - } - } - - @GUI::Layout::Spacer {} -} diff --git a/Userland/Applications/SystemMonitor/CMakeLists.txt b/Userland/Applications/SystemMonitor/CMakeLists.txt deleted file mode 100644 index 36d135bc7c3..00000000000 --- a/Userland/Applications/SystemMonitor/CMakeLists.txt +++ /dev/null @@ -1,29 +0,0 @@ -serenity_component( - SystemMonitor - REQUIRED - TARGETS SystemMonitor Profiler -) - -stringify_gml(SystemMonitor.gml SystemMonitorGML.h system_monitor_gml) -stringify_gml(ProcessWindow.gml ProcessWindowGML.h process_window_gml) - -set(SOURCES - GraphWidget.cpp - main.cpp - MemoryStatsWidget.cpp - NetworkStatisticsWidget.cpp - ProcessFileDescriptorMapWidget.cpp - ProcessMemoryMapWidget.cpp - ProcessModel.cpp - ProcessUnveiledPathsWidget.cpp - ProcessStateWidget.cpp - ThreadStackWidget.cpp -) - -set(GENERATED_SOURCES - SystemMonitorGML.h - ProcessWindowGML.h -) - -serenity_app(SystemMonitor ICON app-system-monitor) -target_link_libraries(SystemMonitor PRIVATE LibConfig LibCore LibGfx LibGUI LibMain LibSymbolication LibThreading) diff --git a/Userland/Applications/SystemMonitor/GraphWidget.cpp b/Userland/Applications/SystemMonitor/GraphWidget.cpp deleted file mode 100644 index d0007f98716..00000000000 --- a/Userland/Applications/SystemMonitor/GraphWidget.cpp +++ /dev/null @@ -1,163 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * Copyright (c) 2022, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "GraphWidget.h" -#include -#include -#include -#include -#include -#include -#include - -REGISTER_WIDGET(SystemMonitor, GraphWidget) - -namespace SystemMonitor { - -GraphWidget::GraphWidget() -{ - REGISTER_BOOL_PROPERTY("stack_values", stack_values, set_stack_values); -} - -void GraphWidget::set_stack_values(bool stack_values) -{ - m_stack_values = stack_values; - update(); -} - -void GraphWidget::add_value(Vector&& value) -{ - m_values.enqueue(move(value)); - update(); -} - -void GraphWidget::paint_event(GUI::PaintEvent& event) -{ - auto const& system_palette = GUI::Application::the()->palette(); - - GUI::Frame::paint_event(event); - GUI::Painter painter(*this); - painter.add_clip_rect(event.rect()); - painter.add_clip_rect(frame_inner_rect()); - painter.fill_rect(event.rect(), palette().base()); - - auto inner_rect = frame_inner_rect(); - float scale = (float)inner_rect.height() / (float)m_max; - - if (!m_values.is_empty()) { - // Draw one set of values at a time - for (size_t k = 0; k < m_value_format.size(); k++) { - auto const& format = m_value_format[k]; - if (format.graph_color_role == ColorRole::Base) { - continue; - } - auto const& line_color = system_palette.color(format.graph_color_role); - auto const& background_color = line_color.with_alpha(0x7f); - m_calculated_points.clear_with_capacity(); - for (size_t i = 0; i < m_values.size(); i++) { - int x = inner_rect.right() - i * 2; - if (x < 0) - break; - auto const& current_values = m_values.at(m_values.size() - i - 1); - if (current_values.size() <= k) { - // Don't have a data point - m_calculated_points.append({ -1, -1 }); - continue; - } - float value = current_values[k]; - if (m_stack_values) { - for (size_t l = k + 1; l < current_values.size(); l++) - value += current_values[l]; - } - float scaled_value = value * scale; - Gfx::IntPoint current_point { x, inner_rect.bottom() - 1 - (int)scaled_value }; - m_calculated_points.append(current_point); - } - VERIFY(m_calculated_points.size() <= m_values.size()); - if (format.graph_color_role != ColorRole::Base) { - // Fill the background for the area we have values for - Gfx::Path path; - size_t points_in_path = 0; - bool started_path = false; - Gfx::IntPoint const* current_point = nullptr; - Gfx::IntPoint const* first_point = nullptr; - auto check_fill_area = [&]() { - if (!started_path) - return; - if (points_in_path > 1) { - VERIFY(current_point); - VERIFY(first_point); - path.line_to({ current_point->x() - 1, inner_rect.bottom() }); - path.line_to({ first_point->x() + 1, inner_rect.bottom() }); - path.close(); - painter.fill_path(path, background_color, Gfx::Painter::WindingRule::EvenOdd); - } else if (points_in_path == 1 && current_point) { - // Can't fill any area, we only have one data point. - // Just draw a vertical line as a "fill"... - painter.draw_line(*current_point, { current_point->x(), inner_rect.bottom() - 1 }, background_color); - } - path = {}; - points_in_path = 0; - first_point = nullptr; - started_path = false; - }; - for (size_t i = 0; i < m_calculated_points.size(); i++) { - current_point = &m_calculated_points[i]; - if (current_point->x() < 0) { - check_fill_area(); - continue; - } - if (!started_path) { - path.move_to({ current_point->x() + 1, current_point->y() }); - points_in_path = 1; - first_point = current_point; - started_path = true; - } else { - path.line_to({ current_point->x(), current_point->y() }); - points_in_path++; - } - } - check_fill_area(); - } - if (format.graph_color_role != ColorRole::Base) { - // Draw the line for the data points we have - Gfx::IntPoint const* previous_point = nullptr; - for (size_t i = 0; i < m_calculated_points.size(); i++) { - auto const& current_point = m_calculated_points[i]; - if (current_point.x() < 0) { - previous_point = nullptr; - continue; - } - if (previous_point) - painter.draw_line(*previous_point, current_point, line_color); - previous_point = ¤t_point; - } - } - } - } - - if (!m_values.is_empty() && !m_value_format.is_empty()) { - auto const& current_values = m_values.last(); - int y = 0; - for (size_t i = 0; i < min(m_value_format.size(), current_values.size()); i++) { - auto const& format = m_value_format[i]; - auto const& graph_color = system_palette.color(format.graph_color_role); - if (!format.text_formatter) - continue; - auto constrain_rect = inner_rect.shrunken(8, 8); - auto text_rect = constrain_rect.translated(0, y).intersected(constrain_rect); - text_rect.set_height(font().pixel_size_rounded_up()); - auto text = format.text_formatter(current_values[i]); - if (format.text_shadow_color != Color::Transparent) - painter.draw_text(text_rect.translated(1, 1), text, Gfx::TextAlignment::CenterRight, format.text_shadow_color); - painter.draw_text(text_rect, text, Gfx::TextAlignment::CenterRight, graph_color); - y += text_rect.height() + 4; - } - } -} - -} diff --git a/Userland/Applications/SystemMonitor/GraphWidget.h b/Userland/Applications/SystemMonitor/GraphWidget.h deleted file mode 100644 index 67eeef3a98b..00000000000 --- a/Userland/Applications/SystemMonitor/GraphWidget.h +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * Copyright (c) 2022, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include - -namespace SystemMonitor { - -class GraphWidget final : public GUI::Frame { - C_OBJECT(GraphWidget) -public: - virtual ~GraphWidget() override = default; - - void set_max(u64 max) { m_max = max; } - u64 max() const { return m_max; } - - void add_value(Vector&&); - - struct ValueFormat { - Gfx::ColorRole graph_color_role { Gfx::ColorRole::Base }; - Color text_shadow_color { Color::Transparent }; - Function text_formatter; - }; - void set_value_format(size_t index, ValueFormat&& format) - { - if (m_value_format.size() <= index) - m_value_format.resize(index + 1); - m_value_format[index] = move(format); - } - void set_stack_values(bool stack_values); - bool stack_values() const { return m_stack_values; } - -private: - explicit GraphWidget(); - - virtual void paint_event(GUI::PaintEvent&) override; - - u64 m_max { 100 }; - Vector m_value_format; - CircularQueue, 4000> m_values; - bool m_stack_values { false }; - - Vector m_calculated_points; -}; - -} diff --git a/Userland/Applications/SystemMonitor/MemoryStatsWidget.cpp b/Userland/Applications/SystemMonitor/MemoryStatsWidget.cpp deleted file mode 100644 index e5543b55f02..00000000000 --- a/Userland/Applications/SystemMonitor/MemoryStatsWidget.cpp +++ /dev/null @@ -1,143 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * Copyright (c) 2022, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "MemoryStatsWidget.h" -#include "GraphWidget.h" -#include -#include -#include -#include -#include -#include -#include -#include - -REGISTER_WIDGET(SystemMonitor, MemoryStatsWidget) - -namespace SystemMonitor { - -static MemoryStatsWidget* s_the; - -MemoryStatsWidget* MemoryStatsWidget::the() -{ - return s_the; -} - -MemoryStatsWidget::MemoryStatsWidget() - : MemoryStatsWidget(nullptr) -{ -} - -MemoryStatsWidget::MemoryStatsWidget(GraphWidget* graph) - : m_graph(graph) -{ - VERIFY(!s_the); - s_the = this; - - REGISTER_DEPRECATED_STRING_PROPERTY("memory_graph", graph_widget_name, set_graph_widget_via_name); - - set_fixed_height(110); - - set_layout(GUI::Margins { 8, 0, 0 }, 3); - - auto build_widgets_for_label = [this](String const& description) -> RefPtr { - auto& container = add(); - container.set_layout(); - container.set_fixed_size(275, 12); - auto& description_label = container.add(description); - description_label.set_font(Gfx::FontDatabase::default_font().bold_variant()); - description_label.set_text_alignment(Gfx::TextAlignment::CenterLeft); - auto& label = container.add(); - label.set_text_alignment(Gfx::TextAlignment::CenterRight); - return label; - }; - - m_physical_pages_label = build_widgets_for_label("Physical memory:"_string); - m_physical_pages_committed_label = build_widgets_for_label("Committed memory:"_string); - m_kmalloc_space_label = build_widgets_for_label("Kernel heap:"_string); - m_kmalloc_count_label = build_widgets_for_label("Calls kmalloc:"_string); - m_kfree_count_label = build_widgets_for_label("Calls kfree:"_string); - m_kmalloc_difference_label = build_widgets_for_label("Difference:"_string); - - refresh(); -} - -void MemoryStatsWidget::set_graph_widget(GraphWidget& graph) -{ - m_graph = &graph; -} - -void MemoryStatsWidget::set_graph_widget_via_name(ByteString name) -{ - m_graph_widget_name = move(name); - if (!m_graph_widget_name.is_empty()) { - // FIXME: We assume here that the graph widget is a sibling or descendant of a sibling. This prevents more complex hierarchies. - auto* maybe_graph = parent_widget()->find_descendant_of_type_named(m_graph_widget_name); - if (maybe_graph) { - m_graph = maybe_graph; - // Delete the stored graph name to signal that we found the widget - m_graph_widget_name = {}; - } else { - dbgln("MemoryStatsWidget: Couldn't find graph of name '{}', retrying later.", m_graph_widget_name); - } - } -} - -ByteString MemoryStatsWidget::graph_widget_name() -{ - if (m_graph) - return m_graph->name(); - return m_graph_widget_name; -} - -static inline u64 page_count_to_bytes(size_t count) -{ - return count * 4096; -} - -void MemoryStatsWidget::refresh() -{ - auto proc_memstat = Core::File::open("/sys/kernel/memstat"sv, Core::File::OpenMode::Read).release_value_but_fixme_should_propagate_errors(); - - auto file_contents = proc_memstat->read_until_eof().release_value_but_fixme_should_propagate_errors(); - auto json_result = JsonValue::from_string(file_contents).release_value_but_fixme_should_propagate_errors(); - auto const& json = json_result.as_object(); - - u32 kmalloc_allocated = json.get_u32("kmalloc_allocated"sv).value_or(0); - u32 kmalloc_available = json.get_u32("kmalloc_available"sv).value_or(0); - u64 physical_allocated = json.get_u64("physical_allocated"sv).value_or(0); - u64 physical_available = json.get_u64("physical_available"sv).value_or(0); - u64 physical_committed = json.get_u64("physical_committed"sv).value_or(0); - u64 physical_uncommitted = json.get_u64("physical_uncommitted"sv).value_or(0); - u32 kmalloc_call_count = json.get_u32("kmalloc_call_count"sv).value_or(0); - u32 kfree_call_count = json.get_u32("kfree_call_count"sv).value_or(0); - - u64 kmalloc_bytes_total = kmalloc_allocated + kmalloc_available; - u64 physical_pages_total = physical_allocated + physical_available; - - u64 physical_pages_in_use = physical_allocated; - u64 total_userphysical_and_swappable_pages = physical_allocated + physical_committed + physical_uncommitted; - - m_kmalloc_space_label->set_text(String::formatted("{}/{}", human_readable_size(kmalloc_allocated), human_readable_size(kmalloc_bytes_total)).release_value_but_fixme_should_propagate_errors()); - m_physical_pages_label->set_text(String::formatted("{}/{}", human_readable_size(page_count_to_bytes(physical_pages_in_use)), human_readable_size(page_count_to_bytes(physical_pages_total))).release_value_but_fixme_should_propagate_errors()); - m_physical_pages_committed_label->set_text(String::formatted("{}", human_readable_size(page_count_to_bytes(physical_committed))).release_value_but_fixme_should_propagate_errors()); - m_kmalloc_count_label->set_text(String::formatted("{}", kmalloc_call_count).release_value_but_fixme_should_propagate_errors()); - m_kfree_count_label->set_text(String::formatted("{}", kfree_call_count).release_value_but_fixme_should_propagate_errors()); - m_kmalloc_difference_label->set_text(String::formatted("{:+}", kmalloc_call_count - kfree_call_count).release_value_but_fixme_should_propagate_errors()); - - // Because the initialization order of us and the graph is unknown, we might get a couple of updates where the graph widget lookup fails. - // Therefore, we can retry indefinitely. (Should not be too much of a performance hit, as we don't update that often.) - if (!m_graph) - set_graph_widget_via_name(move(m_graph_widget_name)); - - if (m_graph) { - m_graph->set_max(page_count_to_bytes(total_userphysical_and_swappable_pages) + kmalloc_bytes_total); - m_graph->add_value({ page_count_to_bytes(physical_committed), page_count_to_bytes(physical_allocated), kmalloc_bytes_total }); - } -} - -} diff --git a/Userland/Applications/SystemMonitor/MemoryStatsWidget.h b/Userland/Applications/SystemMonitor/MemoryStatsWidget.h deleted file mode 100644 index 52d67f8ab3a..00000000000 --- a/Userland/Applications/SystemMonitor/MemoryStatsWidget.h +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * Copyright (c) 2022, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include - -namespace SystemMonitor { - -class GraphWidget; - -class MemoryStatsWidget final : public GUI::Widget { - C_OBJECT(MemoryStatsWidget) -public: - static MemoryStatsWidget* the(); - - virtual ~MemoryStatsWidget() override = default; - - void set_graph_widget(GraphWidget& graph); - - void set_graph_widget_via_name(ByteString name); - ByteString graph_widget_name(); - - void refresh(); - -private: - MemoryStatsWidget(GraphWidget* graph); - MemoryStatsWidget(); - - GraphWidget* m_graph; - // Is null if we have a valid graph - ByteString m_graph_widget_name {}; - RefPtr m_physical_pages_label; - RefPtr m_physical_pages_committed_label; - RefPtr m_kmalloc_space_label; - RefPtr m_kmalloc_count_label; - RefPtr m_kfree_count_label; - RefPtr m_kmalloc_difference_label; -}; - -} diff --git a/Userland/Applications/SystemMonitor/NetworkStatisticsWidget.cpp b/Userland/Applications/SystemMonitor/NetworkStatisticsWidget.cpp deleted file mode 100644 index 1c6f5d077e7..00000000000 --- a/Userland/Applications/SystemMonitor/NetworkStatisticsWidget.cpp +++ /dev/null @@ -1,145 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * Copyright (c) 2022, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "NetworkStatisticsWidget.h" -#include -#include -#include -#include -#include -#include -#include -#include - -REGISTER_WIDGET(SystemMonitor, NetworkStatisticsWidget) - -namespace SystemMonitor { - -NetworkStatisticsWidget::NetworkStatisticsWidget() -{ - on_first_show = [this](auto&) { - set_layout(4); - set_fill_with_background_color(true); - - m_network_connected_bitmap = Gfx::Bitmap::load_from_file("/res/icons/16x16/network-connected.png"sv).release_value_but_fixme_should_propagate_errors(); - m_network_disconnected_bitmap = Gfx::Bitmap::load_from_file("/res/icons/16x16/network-disconnected.png"sv).release_value_but_fixme_should_propagate_errors(); - - m_network_link_down_bitmap = Gfx::Bitmap::create(m_network_connected_bitmap->format(), m_network_connected_bitmap->size()).release_value_but_fixme_should_propagate_errors(); - { - Gfx::Painter painter(*m_network_link_down_bitmap); - painter.blit_filtered(Gfx::IntPoint {}, *m_network_connected_bitmap, m_network_connected_bitmap->rect(), [](Color color) { - return color.to_grayscale(); - }); - } - - auto& adapters_group_box = add("Adapters"sv); - adapters_group_box.set_layout(6); - adapters_group_box.set_fixed_height(120); - - m_adapter_table_view = adapters_group_box.add(); - - Vector net_adapters_fields; - net_adapters_fields.empend(String(), Gfx::TextAlignment::CenterLeft, - [this](JsonObject const& object) -> GUI::Variant { - if (!object.get_bool("link_up"sv).value_or(false)) - return *m_network_link_down_bitmap; - else - return object.get_byte_string("ipv4_address"sv).value_or("").is_empty() ? *m_network_disconnected_bitmap : *m_network_connected_bitmap; - }); - net_adapters_fields.empend("name", "Name"_string, Gfx::TextAlignment::CenterLeft); - net_adapters_fields.empend("class_name", "Class"_string, Gfx::TextAlignment::CenterLeft); - net_adapters_fields.empend("mac_address", "MAC"_string, Gfx::TextAlignment::CenterLeft); - net_adapters_fields.empend("Link status"_string, Gfx::TextAlignment::CenterLeft, - [](JsonObject const& object) -> ByteString { - if (!object.get_bool("link_up"sv).value_or(false)) - return "Down"; - - return ByteString::formatted("{} Mb/s {}-duplex", object.get_i32("link_speed"sv).value_or(0), - object.get_bool("link_full_duplex"sv).value_or(false) ? "full"sv : "half"sv); - }); - net_adapters_fields.empend("IPv4"_string, Gfx::TextAlignment::CenterLeft, - [](JsonObject const& object) -> ByteString { - return object.get_byte_string("ipv4_address"sv).value_or(""sv); - }); - net_adapters_fields.empend("packets_in", "Pkt In"_string, Gfx::TextAlignment::CenterRight); - net_adapters_fields.empend("packets_out", "Pkt Out"_string, Gfx::TextAlignment::CenterRight); - net_adapters_fields.empend("bytes_in", "Bytes In"_string, Gfx::TextAlignment::CenterRight); - net_adapters_fields.empend("bytes_out", "Bytes Out"_string, Gfx::TextAlignment::CenterRight); - net_adapters_fields.empend("packets_dropped", "Packets Dropped"_string, Gfx::TextAlignment::CenterRight); - m_adapter_model = GUI::JsonArrayModel::create("/sys/kernel/net/adapters", move(net_adapters_fields)); - m_adapter_table_view->set_model(MUST(GUI::SortingProxyModel::create(*m_adapter_model))); - m_adapter_context_menu = GUI::Menu::construct(); - m_adapter_context_menu->add_action(GUI::Action::create( - "Open in Network Settings...", MUST(Gfx::Bitmap::load_from_file("/res/icons/16x16/network.png"sv)), [this](GUI::Action&) { - m_adapter_table_view->selection().for_each_index([this](GUI::ModelIndex const& index) { - auto adapter_name = index.sibling_at_column(1).data().as_string(); - GUI::Process::spawn_or_show_error(window(), "/bin/NetworkSettings"sv, Array { adapter_name.characters() }); - }); - }, - this)); - m_adapter_table_view->on_context_menu_request = [this](GUI::ModelIndex const& index, GUI::ContextMenuEvent const& event) { - if (!index.is_valid()) { - return; - } - auto adapter_name = index.sibling_at_column(1).data().as_string(); - if (adapter_name == "loop") { - return; - } - m_adapter_context_menu->popup(event.screen_position()); - }; - - auto& tcp_sockets_group_box = add("TCP Sockets"sv); - tcp_sockets_group_box.set_layout(6); - - m_tcp_socket_table_view = tcp_sockets_group_box.add(); - - Vector net_tcp_fields; - net_tcp_fields.empend("peer_address", "Peer"_string, Gfx::TextAlignment::CenterLeft); - net_tcp_fields.empend("peer_port", "Port"_string, Gfx::TextAlignment::CenterRight); - net_tcp_fields.empend("local_address", "Local"_string, Gfx::TextAlignment::CenterLeft); - net_tcp_fields.empend("local_port", "Port"_string, Gfx::TextAlignment::CenterRight); - net_tcp_fields.empend("state", "State"_string, Gfx::TextAlignment::CenterLeft); - net_tcp_fields.empend("ack_number", "Ack#"_string, Gfx::TextAlignment::CenterRight); - net_tcp_fields.empend("sequence_number", "Seq#"_string, Gfx::TextAlignment::CenterRight); - net_tcp_fields.empend("packets_in", "Pkt In"_string, Gfx::TextAlignment::CenterRight); - net_tcp_fields.empend("packets_out", "Pkt Out"_string, Gfx::TextAlignment::CenterRight); - net_tcp_fields.empend("bytes_in", "Bytes In"_string, Gfx::TextAlignment::CenterRight); - net_tcp_fields.empend("bytes_out", "Bytes Out"_string, Gfx::TextAlignment::CenterRight); - m_tcp_socket_model = GUI::JsonArrayModel::create("/sys/kernel/net/tcp", move(net_tcp_fields)); - m_tcp_socket_table_view->set_model(MUST(GUI::SortingProxyModel::create(*m_tcp_socket_model))); - - auto& udp_sockets_group_box = add("UDP Sockets"sv); - udp_sockets_group_box.set_layout(6); - - m_udp_socket_table_view = udp_sockets_group_box.add(); - - Vector net_udp_fields; - net_udp_fields.empend("peer_address", "Peer"_string, Gfx::TextAlignment::CenterLeft); - net_udp_fields.empend("peer_port", "Port"_string, Gfx::TextAlignment::CenterRight); - net_udp_fields.empend("local_address", "Local"_string, Gfx::TextAlignment::CenterLeft); - net_udp_fields.empend("local_port", "Port"_string, Gfx::TextAlignment::CenterRight); - m_udp_socket_model = GUI::JsonArrayModel::create("/sys/kernel/net/udp", move(net_udp_fields)); - m_udp_socket_table_view->set_model(MUST(GUI::SortingProxyModel::create(*m_udp_socket_model))); - - m_update_timer = add( - 1000, [this] { - update_models(); - }); - m_update_timer->start(); - - update_models(); - }; -} - -void NetworkStatisticsWidget::update_models() -{ - m_adapter_model->update(); - m_tcp_socket_model->update(); - m_udp_socket_model->update(); -} - -} diff --git a/Userland/Applications/SystemMonitor/NetworkStatisticsWidget.h b/Userland/Applications/SystemMonitor/NetworkStatisticsWidget.h deleted file mode 100644 index f8e21e14858..00000000000 --- a/Userland/Applications/SystemMonitor/NetworkStatisticsWidget.h +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * Copyright (c) 2022, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include - -namespace SystemMonitor { - -class NetworkStatisticsWidget final : public GUI::LazyWidget { - C_OBJECT(NetworkStatisticsWidget) -public: - virtual ~NetworkStatisticsWidget() override = default; - -private: - NetworkStatisticsWidget(); - void update_models(); - - RefPtr m_adapter_table_view; - RefPtr m_adapter_context_menu; - RefPtr m_tcp_socket_table_view; - RefPtr m_udp_socket_table_view; - RefPtr m_adapter_model; - RefPtr m_tcp_socket_model; - RefPtr m_udp_socket_model; - RefPtr m_update_timer; - RefPtr m_network_connected_bitmap; - RefPtr m_network_disconnected_bitmap; - RefPtr m_network_link_down_bitmap; -}; - -} diff --git a/Userland/Applications/SystemMonitor/ProcessFileDescriptorMapWidget.cpp b/Userland/Applications/SystemMonitor/ProcessFileDescriptorMapWidget.cpp deleted file mode 100644 index 162d9b13d53..00000000000 --- a/Userland/Applications/SystemMonitor/ProcessFileDescriptorMapWidget.cpp +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * Copyright (c) 2022, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "ProcessFileDescriptorMapWidget.h" -#include -#include -#include -#include - -REGISTER_WIDGET(SystemMonitor, ProcessFileDescriptorMapWidget) - -namespace SystemMonitor { - -ErrorOr> ProcessFileDescriptorMapWidget::try_create() -{ - auto widget = TRY(adopt_nonnull_ref_or_enomem(new (nothrow) ProcessFileDescriptorMapWidget())); - widget->set_layout(4); - widget->m_table_view = widget->add(); - - Vector pid_fds_fields; - TRY(pid_fds_fields.try_empend("fd", "FD"_string, Gfx::TextAlignment::CenterRight)); - TRY(pid_fds_fields.try_empend("class", "Class"_string, Gfx::TextAlignment::CenterLeft)); - TRY(pid_fds_fields.try_empend("offset", "Offset"_string, Gfx::TextAlignment::CenterRight)); - TRY(pid_fds_fields.try_empend("absolute_path", "Path"_string, Gfx::TextAlignment::CenterLeft)); - TRY(pid_fds_fields.try_empend("Access"_string, Gfx::TextAlignment::CenterLeft, [](auto& object) { - return object.get_bool("seekable"sv).value_or(false) ? "Seekable" : "Sequential"; - })); - TRY(pid_fds_fields.try_empend("Blocking"_string, Gfx::TextAlignment::CenterLeft, [](auto& object) { - return object.get_bool("blocking"sv).value_or(false) ? "Blocking" : "Nonblocking"; - })); - TRY(pid_fds_fields.try_empend("On exec"_string, Gfx::TextAlignment::CenterLeft, [](auto& object) { - return object.get_bool("cloexec"sv).value_or(false) ? "Close" : "Keep"; - })); - TRY(pid_fds_fields.try_empend("Can read"_string, Gfx::TextAlignment::CenterLeft, [](auto& object) { - return object.get_bool("can_read"sv).value_or(false) ? "Yes" : "No"; - })); - TRY(pid_fds_fields.try_empend("Can write"_string, Gfx::TextAlignment::CenterLeft, [](auto& object) { - return object.get_bool("can_write"sv).value_or(false) ? "Yes" : "No"; - })); - - widget->m_model = GUI::JsonArrayModel::create({}, move(pid_fds_fields)); - widget->m_table_view->set_model(TRY(GUI::SortingProxyModel::create(*widget->m_model))); - - return widget; -} - -void ProcessFileDescriptorMapWidget::set_pid(pid_t pid) -{ - if (m_pid == pid) - return; - m_pid = pid; - m_model->set_json_path(ByteString::formatted("/proc/{}/fds", m_pid)); -} - -} diff --git a/Userland/Applications/SystemMonitor/ProcessFileDescriptorMapWidget.h b/Userland/Applications/SystemMonitor/ProcessFileDescriptorMapWidget.h deleted file mode 100644 index 7c7a2001295..00000000000 --- a/Userland/Applications/SystemMonitor/ProcessFileDescriptorMapWidget.h +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * Copyright (c) 2022, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include - -namespace SystemMonitor { - -class ProcessFileDescriptorMapWidget final : public GUI::Widget { - C_OBJECT_ABSTRACT(ProcessFileDescriptorMapWidget) - -public: - virtual ~ProcessFileDescriptorMapWidget() override = default; - - static ErrorOr> try_create(); - - void set_pid(pid_t); - -private: - ProcessFileDescriptorMapWidget() = default; - - RefPtr m_table_view; - RefPtr m_model; - pid_t m_pid { -1 }; -}; - -} diff --git a/Userland/Applications/SystemMonitor/ProcessMemoryMapWidget.cpp b/Userland/Applications/SystemMonitor/ProcessMemoryMapWidget.cpp deleted file mode 100644 index 1e4bd47addc..00000000000 --- a/Userland/Applications/SystemMonitor/ProcessMemoryMapWidget.cpp +++ /dev/null @@ -1,133 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * Copyright (c) 2022, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "ProcessMemoryMapWidget.h" -#include -#include -#include -#include -#include -#include -#include - -REGISTER_WIDGET(SystemMonitor, ProcessMemoryMapWidget) - -namespace SystemMonitor { - -class PagemapPaintingDelegate final : public GUI::TableCellPaintingDelegate { -public: - virtual ~PagemapPaintingDelegate() override = default; - - virtual void paint(GUI::Painter& painter, Gfx::IntRect const& a_rect, Gfx::Palette const&, const GUI::ModelIndex& index) override - { - auto rect = a_rect.shrunken(2, 2); - auto pagemap = index.data(GUI::ModelRole::Custom).to_byte_string(); - - float scale_factor = (float)pagemap.length() / (float)rect.width(); - - for (int i = 0; i < rect.width(); ++i) { - int x = rect.x() + i; - char c = pagemap[(float)i * scale_factor]; - Color color; - if (c == 'N') // Null (no page at all, typically an inode-backed page that hasn't been paged in.) - color = Color::White; - else if (c == 'Z') // Zero (globally shared zero page, typically an untouched anonymous page.) - color = Color::from_rgb(0xc0c0ff); - else if (c == 'P') // Physical (a resident page) - color = Color::Black; - else - VERIFY_NOT_REACHED(); - - painter.draw_line({ x, rect.top() }, { x, rect.bottom() - 1 }, color); - } - - painter.draw_rect(rect, Color::Black); - } -}; - -ErrorOr> ProcessMemoryMapWidget::try_create() -{ - auto widget = TRY(adopt_nonnull_ref_or_enomem(new (nothrow) ProcessMemoryMapWidget())); - widget->set_layout(4); - widget->m_table_view = widget->add(); - - Vector pid_vm_fields; - TRY(pid_vm_fields.try_empend( - "Address"_string, Gfx::TextAlignment::CenterLeft, - [](auto& object) { return ByteString::formatted("{:p}", object.get_u64("address"sv).value_or(0)); }, - [](auto& object) { return object.get_u64("address"sv).value_or(0); })); - TRY(pid_vm_fields.try_empend("size", "Size"_string, Gfx::TextAlignment::CenterRight)); - TRY(pid_vm_fields.try_empend("amount_resident", "Resident"_string, Gfx::TextAlignment::CenterRight)); - TRY(pid_vm_fields.try_empend("amount_dirty", "Dirty"_string, Gfx::TextAlignment::CenterRight)); - TRY(pid_vm_fields.try_empend("Access"_string, Gfx::TextAlignment::CenterLeft, [](auto& object) { - StringBuilder builder; - if (object.get_bool("readable"sv).value_or(false)) - builder.append('R'); - if (object.get_bool("writable"sv).value_or(false)) - builder.append('W'); - if (object.get_bool("executable"sv).value_or(false)) - builder.append('X'); - if (object.get_bool("shared"sv).value_or(false)) - builder.append('S'); - if (object.get_bool("syscall"sv).value_or(false)) - builder.append('C'); - if (object.get_bool("stack"sv).value_or(false)) - builder.append('T'); - return builder.to_byte_string(); - })); - TRY(pid_vm_fields.try_empend("VMObject type"_string, Gfx::TextAlignment::CenterLeft, [](auto& object) { - auto type = object.get_byte_string("vmobject"sv).value_or({}); - if (type.ends_with("VMObject"sv)) - type = type.substring(0, type.length() - 8); - return type; - })); - TRY(pid_vm_fields.try_empend("Purgeable"_string, Gfx::TextAlignment::CenterLeft, [](auto& object) { - if (object.get_bool("volatile"sv).value_or(false)) - return "Volatile"; - return "Non-volatile"; - })); - TRY(pid_vm_fields.try_empend( - "Page map"_string, Gfx::TextAlignment::CenterLeft, - [](auto&) { - return GUI::Variant(); - }, - [](auto&) { - return GUI::Variant(0); - }, - [](JsonObject const& object) { - auto pagemap = object.get_byte_string("pagemap"sv).value_or({}); - return pagemap; - })); - TRY(pid_vm_fields.try_empend("cow_pages", "# CoW"_string, Gfx::TextAlignment::CenterRight)); - TRY(pid_vm_fields.try_empend("name", "Name"_string, Gfx::TextAlignment::CenterLeft)); - widget->m_json_model = GUI::JsonArrayModel::create({}, move(pid_vm_fields)); - widget->m_table_view->set_model(TRY(GUI::SortingProxyModel::create(*widget->m_json_model))); - - widget->m_table_view->set_column_painting_delegate(7, TRY(try_make())); - - widget->m_table_view->set_key_column_and_sort_order(0, GUI::SortOrder::Ascending); - widget->m_timer = widget->add(1000, [widget] { widget->refresh(); }); - widget->m_timer->start(); - - return widget; -} - -void ProcessMemoryMapWidget::set_pid(pid_t pid) -{ - if (m_pid == pid) - return; - m_pid = pid; - m_json_model->set_json_path(ByteString::formatted("/proc/{}/vm", pid)); -} - -void ProcessMemoryMapWidget::refresh() -{ - if (m_pid != -1) - m_json_model->update(); -} - -} diff --git a/Userland/Applications/SystemMonitor/ProcessMemoryMapWidget.h b/Userland/Applications/SystemMonitor/ProcessMemoryMapWidget.h deleted file mode 100644 index 92c3759f84a..00000000000 --- a/Userland/Applications/SystemMonitor/ProcessMemoryMapWidget.h +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * Copyright (c) 2022, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include - -namespace SystemMonitor { - -class ProcessMemoryMapWidget final : public GUI::Widget { - C_OBJECT_ABSTRACT(ProcessMemoryMapWidget); - -public: - virtual ~ProcessMemoryMapWidget() override = default; - - static ErrorOr> try_create(); - - void set_pid(pid_t); - void refresh(); - -private: - ProcessMemoryMapWidget() = default; - - RefPtr m_table_view; - RefPtr m_json_model; - pid_t m_pid { -1 }; - RefPtr m_timer; -}; - -} diff --git a/Userland/Applications/SystemMonitor/ProcessModel.cpp b/Userland/Applications/SystemMonitor/ProcessModel.cpp deleted file mode 100644 index 8369c9b9662..00000000000 --- a/Userland/Applications/SystemMonitor/ProcessModel.cpp +++ /dev/null @@ -1,652 +0,0 @@ -/* - * Copyright (c) 2018-2021, Andreas Kling - * Copyright (c) 2022, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "ProcessModel.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -static ProcessModel* s_the; - -ProcessModel& ProcessModel::the() -{ - VERIFY(s_the); - return *s_the; -} - -ProcessModel::ProcessModel() -{ - VERIFY(!s_the); - s_the = this; - - auto file_or_error = Core::File::open("/sys/kernel/cpuinfo"sv, Core::File::OpenMode::Read); - if (!file_or_error.is_error()) { - auto buffer_or_error = file_or_error.value()->read_until_eof(); - if (!buffer_or_error.is_error()) { - auto json = JsonValue::from_string({ buffer_or_error.value() }); - auto cpuinfo_array = json.value().as_array(); - cpuinfo_array.for_each([&](auto& value) { - auto& cpu_object = value.as_object(); - auto cpu_id = cpu_object.get_u32("processor"sv).value(); - m_cpus.append(make(cpu_id)); - }); - } - } - - if (m_cpus.is_empty()) - m_cpus.append(make(0)); - - m_kernel_process_icon = GUI::Icon::default_icon("gear"sv); -} - -int ProcessModel::row_count(GUI::ModelIndex const& index) const -{ - if (!index.is_valid()) - return m_processes.size(); - // Anything in the second level (threads of processes) doesn't have children. - // This way, we don't get infinitely recursing main threads without having to handle that special case elsewhere. - if (index.parent().is_valid()) - return 0; - auto const& thread = *static_cast(index.internal_data()); - // Only the main thread has the other threads as its children. - // Also, if there's not more than one thread, we won't draw that. - if (thread.is_main_thread() && thread.current_state.process.threads.size() > 1) - return thread.current_state.process.threads.size() - 1; - return 0; -} - -int ProcessModel::column_count(GUI::ModelIndex const&) const -{ - return Column::__Count; -} - -ErrorOr ProcessModel::column_name(int column) const -{ - switch (column) { - case Column::Icon: - return String {}; - case Column::PID: - return "PID"_string; - case Column::TID: - return "TID"_string; - case Column::PPID: - return "PPID"_string; - case Column::PGID: - return "PGID"_string; - case Column::SID: - return "SID"_string; - case Column::State: - return "State"_string; - case Column::User: - return "User"_string; - case Column::Priority: - return "Pr"_string; - case Column::Virtual: - return "Virtual"_string; - case Column::Physical: - return "Physical"_string; - case Column::DirtyPrivate: - return "Private"_string; - case Column::CleanInode: - return "CleanI"_string; - case Column::PurgeableVolatile: - return "Purg:V"_string; - case Column::PurgeableNonvolatile: - return "Purg:N"_string; - case Column::CPU: - return "CPU"_string; - case Column::Processor: - return "Processor"_string; - case Column::Name: - return "Name"_string; - case Column::Syscalls: - return "Syscalls"_string; - case Column::InodeFaults: - return "F:Inode"_string; - case Column::ZeroFaults: - return "F:Zero"_string; - case Column::CowFaults: - return "F:CoW"_string; - case Column::IPv4SocketReadBytes: - return "IPv4 In"_string; - case Column::IPv4SocketWriteBytes: - return "IPv4 Out"_string; - case Column::UnixSocketReadBytes: - return "Unix In"_string; - case Column::UnixSocketWriteBytes: - return "Unix Out"_string; - case Column::FileReadBytes: - return "File In"_string; - case Column::FileWriteBytes: - return "File Out"_string; - case Column::Pledge: - return "Pledge"_string; - case Column::Veil: - return "Veil"_string; - case Column::Command: - return "Command"_string; - default: - VERIFY_NOT_REACHED(); - } -} - -GUI::Variant ProcessModel::data(GUI::ModelIndex const& index, GUI::ModelRole role) const -{ - VERIFY(is_within_range(index)); - - if (role == GUI::ModelRole::TextAlignment) { - switch (index.column()) { - case Column::Icon: - case Column::Name: - case Column::State: - case Column::User: - case Column::Pledge: - case Column::Veil: - case Column::Command: - return Gfx::TextAlignment::CenterLeft; - case Column::PID: - case Column::TID: - case Column::PPID: - case Column::PGID: - case Column::SID: - case Column::Priority: - case Column::Virtual: - case Column::Physical: - case Column::DirtyPrivate: - case Column::CleanInode: - case Column::PurgeableVolatile: - case Column::PurgeableNonvolatile: - case Column::CPU: - case Column::Processor: - case Column::Syscalls: - case Column::InodeFaults: - case Column::ZeroFaults: - case Column::CowFaults: - case Column::FileReadBytes: - case Column::FileWriteBytes: - case Column::UnixSocketReadBytes: - case Column::UnixSocketWriteBytes: - case Column::IPv4SocketReadBytes: - case Column::IPv4SocketWriteBytes: - return Gfx::TextAlignment::CenterRight; - default: - VERIFY_NOT_REACHED(); - } - } - - auto const& thread = *static_cast(index.internal_data()); - - if (role == GUI::ModelRole::Sort) { - switch (index.column()) { - case Column::Icon: - return 0; - case Column::PID: - return thread.current_state.pid; - case Column::TID: - return thread.current_state.tid; - case Column::PPID: - return thread.current_state.ppid; - case Column::PGID: - return thread.current_state.pgid; - case Column::SID: - return thread.current_state.sid; - case Column::State: - return thread.current_state.state; - case Column::User: - return thread.current_state.user; - case Column::Priority: - return thread.current_state.priority; - case Column::Virtual: - return (int)thread.current_state.amount_virtual; - case Column::Physical: - return (int)thread.current_state.amount_resident; - case Column::DirtyPrivate: - return (int)thread.current_state.amount_dirty_private; - case Column::CleanInode: - return (int)thread.current_state.amount_clean_inode; - case Column::PurgeableVolatile: - return (int)thread.current_state.amount_purgeable_volatile; - case Column::PurgeableNonvolatile: - return (int)thread.current_state.amount_purgeable_nonvolatile; - case Column::CPU: - return thread.current_state.cpu_percent; - case Column::Processor: - return thread.current_state.cpu; - case Column::Name: - return thread.current_state.name; - case Column::Command: - return thread.current_state.command.visit([](String const& cmdline) { return cmdline; }, [](auto const&) { return ""_string; }); - case Column::Syscalls: - return thread.current_state.syscall_count; - case Column::InodeFaults: - return thread.current_state.inode_faults; - case Column::ZeroFaults: - return thread.current_state.zero_faults; - case Column::CowFaults: - return thread.current_state.cow_faults; - case Column::IPv4SocketReadBytes: - return thread.current_state.ipv4_socket_read_bytes; - case Column::IPv4SocketWriteBytes: - return thread.current_state.ipv4_socket_write_bytes; - case Column::UnixSocketReadBytes: - return thread.current_state.unix_socket_read_bytes; - case Column::UnixSocketWriteBytes: - return thread.current_state.unix_socket_write_bytes; - case Column::FileReadBytes: - return thread.current_state.file_read_bytes; - case Column::FileWriteBytes: - return thread.current_state.file_write_bytes; - case Column::Pledge: - return thread.current_state.pledge; - case Column::Veil: - return thread.current_state.veil; - } - VERIFY_NOT_REACHED(); - } - - if (role == GUI::ModelRole::Display || role == DISPLAY_VERBOSE) { - switch (index.column()) { - case Column::Icon: - return icon_for(thread); - case Column::PID: - return thread.current_state.pid; - case Column::TID: - return thread.current_state.tid; - case Column::PPID: - return thread.current_state.ppid; - case Column::PGID: - return thread.current_state.pgid; - case Column::SID: - return thread.current_state.sid; - case Column::State: - return thread.current_state.state; - case Column::User: - return thread.current_state.user; - case Column::Priority: - return thread.current_state.priority; - case Column::Virtual: - return human_readable_size(thread.current_state.amount_virtual); - case Column::Physical: - return human_readable_size(thread.current_state.amount_resident); - case Column::DirtyPrivate: - return human_readable_size(thread.current_state.amount_dirty_private); - case Column::CleanInode: - return human_readable_size(thread.current_state.amount_clean_inode); - case Column::PurgeableVolatile: - return human_readable_size(thread.current_state.amount_purgeable_volatile); - case Column::PurgeableNonvolatile: - return human_readable_size(thread.current_state.amount_purgeable_nonvolatile); - case Column::CPU: - return ByteString::formatted("{:.2}", thread.current_state.cpu_percent); - case Column::Processor: - return thread.current_state.cpu; - case Column::Name: - if (thread.current_state.kernel) - return ByteString::formatted("{} (*)", thread.current_state.name); - return thread.current_state.name; - case Column::Command: - return thread.current_state.command.visit([](String const& cmdline) { return cmdline; }, [](auto const&) { return ""_string; }); - case Column::Syscalls: - return thread.current_state.syscall_count; - case Column::InodeFaults: - return thread.current_state.inode_faults; - case Column::ZeroFaults: - return thread.current_state.zero_faults; - case Column::CowFaults: - return thread.current_state.cow_faults; - case Column::IPv4SocketReadBytes: - if (role == DISPLAY_VERBOSE) - return human_readable_size_long(thread.current_state.ipv4_socket_read_bytes, UseThousandsSeparator::Yes); - return human_readable_size(thread.current_state.ipv4_socket_read_bytes, AK::HumanReadableBasedOn::Base2, UseThousandsSeparator::Yes); - case Column::IPv4SocketWriteBytes: - if (role == DISPLAY_VERBOSE) - return human_readable_size_long(thread.current_state.ipv4_socket_write_bytes, UseThousandsSeparator::Yes); - return human_readable_size(thread.current_state.ipv4_socket_write_bytes, AK::HumanReadableBasedOn::Base2, UseThousandsSeparator::Yes); - case Column::UnixSocketReadBytes: - if (role == DISPLAY_VERBOSE) - return human_readable_size_long(thread.current_state.unix_socket_read_bytes, UseThousandsSeparator::Yes); - return human_readable_size(thread.current_state.unix_socket_read_bytes, AK::HumanReadableBasedOn::Base2, UseThousandsSeparator::Yes); - case Column::UnixSocketWriteBytes: - if (role == DISPLAY_VERBOSE) - return human_readable_size_long(thread.current_state.unix_socket_write_bytes, UseThousandsSeparator::Yes); - return human_readable_size(thread.current_state.unix_socket_write_bytes, AK::HumanReadableBasedOn::Base2, UseThousandsSeparator::Yes); - case Column::FileReadBytes: - if (role == DISPLAY_VERBOSE) - return human_readable_size_long(thread.current_state.file_read_bytes, UseThousandsSeparator::Yes); - return human_readable_size(thread.current_state.file_read_bytes, AK::HumanReadableBasedOn::Base2, UseThousandsSeparator::Yes); - case Column::FileWriteBytes: - if (role == DISPLAY_VERBOSE) - return human_readable_size_long(thread.current_state.file_write_bytes, UseThousandsSeparator::Yes); - return human_readable_size(thread.current_state.file_write_bytes, AK::HumanReadableBasedOn::Base2, UseThousandsSeparator::Yes); - case Column::Pledge: - return thread.current_state.pledge; - case Column::Veil: - return thread.current_state.veil; - } - } - - if (role == GUI::ModelRole::Icon) - return icon_for(thread); - - if (role == GUI::ModelRole::IconOpacity) { - if (thread.current_state.uid != getuid()) - return 0.5f; - return {}; - } - - return {}; -} - -GUI::Icon ProcessModel::icon_for(Thread const& thread) const -{ - if (thread.current_state.kernel) - return m_kernel_process_icon; - return GUI::FileIconProvider::icon_for_executable(thread.current_state.executable); -} - -GUI::ModelIndex ProcessModel::index(int row, int column, GUI::ModelIndex const& parent) const -{ - if (row < 0 || column < 0) - return {}; - // Process index; we display the main thread here. - if (!parent.is_valid()) { - if (row >= static_cast(m_processes.size())) - return {}; - auto corresponding_thread = m_processes[row]->main_thread(); - if (!corresponding_thread.has_value()) - return {}; - return create_index(row, column, corresponding_thread.release_value().ptr()); - } - // Thread under process. - auto const& parent_thread = *static_cast(parent.internal_data()); - auto const& process = parent_thread.current_state.process; - // dbgln("Getting thread model index in process {} for col {} row {}", process.pid, column, row); - if (row >= static_cast(process.threads.size())) - return {}; - return create_index(row, column, &process.non_main_thread(row)); -} - -int ProcessModel::thread_model_row(Thread const& thread) const -{ - auto const& process = thread.current_state.process; - // A process's main thread uses the global process index. - if (process.pid == thread.current_state.pid) { - auto it = m_processes.find_if([&](auto& entry) { - return entry.ptr() == &process; - }); - if (it == m_processes.end()) - return 0; - return it.index(); - } - - return process.threads.find_first_index(thread).value_or(0); -} - -GUI::ModelIndex ProcessModel::parent_index(GUI::ModelIndex const& index) const -{ - if (!index.is_valid()) - return {}; - auto const& thread = *static_cast(index.internal_data()); - // There's no parent for the main thread. - if (thread.current_state.pid == thread.current_state.tid) - return {}; - // FIXME: We can't use first_matching here (not even a const version) because Optional cannot contain references. - auto const& parent = thread.current_state.process; - if (!parent.main_thread().has_value()) - return {}; - - auto process_index = [&]() -> size_t { - auto it = m_processes.find_if([&](auto& entry) { - return entry.ptr() == &parent; - }); - if (it == m_processes.end()) - return 0; - return it.index(); - }(); - return create_index(process_index, index.column(), parent.main_thread().value().ptr()); -} - -Vector ProcessModel::matches(StringView searching, unsigned flags, GUI::ModelIndex const&) -{ - Vector found_indices; - - for (auto const& thread : m_threads) { - if (string_matches(thread.value->current_state.name, searching, flags)) { - auto tid_row = thread_model_row(thread.value); - - found_indices.append(create_index(tid_row, Column::Name, reinterpret_cast(thread.value.ptr()))); - if (flags & FirstMatchOnly) - break; - } - } - - return found_indices; -} - -ErrorOr ProcessModel::read_command_line(pid_t pid) -{ - auto file = TRY(Core::File::open(TRY(String::formatted("/proc/{}/cmdline", pid)), Core::File::OpenMode::Read)); - auto data = TRY(file->read_until_eof()); - auto json = TRY(JsonValue::from_string(StringView { data.bytes() })); - auto array = json.as_array().values(); - return String::join(" "sv, array); -} - -ErrorOr ProcessModel::ensure_process_statistics_file() -{ - if (!m_process_statistics_file || !m_process_statistics_file->is_open()) - m_process_statistics_file = TRY(Core::File::open("/sys/kernel/processes"sv, Core::File::OpenMode::Read)); - - return {}; -} - -void ProcessModel::update() -{ - auto result = ensure_process_statistics_file(); - if (result.is_error()) { - dbgln("Process model couldn't be updated: {}", result.release_error()); - return; - } - - auto all_processes_or_error = Core::ProcessStatisticsReader::get_all(*m_process_statistics_file, true); - - auto previous_tid_count = m_threads.size(); - - HashTable live_tids; - u64 total_time_scheduled_diff = 0; - size_t process_count = 0; - if (!all_processes_or_error.is_error()) { - auto all_processes = all_processes_or_error.value(); - process_count = all_processes.processes.size(); - if (m_has_total_scheduled_time) - total_time_scheduled_diff = all_processes.total_time_scheduled - m_total_time_scheduled; - - m_total_time_scheduled = all_processes.total_time_scheduled; - m_total_time_scheduled_kernel = all_processes.total_time_scheduled_kernel; - m_has_total_scheduled_time = true; - - for (auto const& process : all_processes.processes) { - // Don't include the Idle Task in process statistics. - static constexpr pid_t IDLE_TASK_PID = 0; - if (process.pid == IDLE_TASK_PID) { - process_count--; - continue; - } - - NonnullOwnPtr* process_state = nullptr; - for (size_t i = 0; i < m_processes.size(); ++i) { - auto* other_process = &m_processes[i]; - if ((*other_process)->pid == process.pid) { - process_state = other_process; - break; - } - } - if (!process_state) { - m_processes.append(make()); - process_state = &m_processes.last(); - } - - auto add_thread_data = [&live_tids, this](int tid, Process& process_state, ThreadState state) { - auto thread_data = m_threads.ensure(tid, [&] { return make_ref_counted(process_state); }); - thread_data->previous_state = move(thread_data->current_state); - thread_data->current_state = move(state); - thread_data->read_command_line_if_necessary(); - - if (auto maybe_thread_index = process_state.threads.find_first_index(thread_data); maybe_thread_index.has_value()) { - process_state.threads[maybe_thread_index.value()] = thread_data; - } else { - process_state.threads.append(thread_data); - } - live_tids.set(tid); - }; - - (*process_state)->pid = process.pid; - if (!process.threads.is_empty()) { - for (auto& thread : process.threads) { - ThreadState state(**process_state); - state.tid = thread.tid; - state.pid = process.pid; - state.ppid = process.ppid; - state.pgid = process.pgid; - state.sid = process.sid; - state.time_user = thread.time_user; - state.time_kernel = thread.time_kernel; - state.kernel = process.kernel; - state.executable = process.executable; - state.name = thread.name; - state.uid = process.uid; - state.state = thread.state; - state.user = process.username; - state.pledge = process.pledge; - state.veil = process.veil; - state.cpu = thread.cpu; - state.priority = thread.priority; - state.amount_virtual = process.amount_virtual; - state.amount_resident = process.amount_resident; - state.amount_dirty_private = process.amount_dirty_private; - state.amount_clean_inode = process.amount_clean_inode; - state.amount_purgeable_volatile = process.amount_purgeable_volatile; - state.amount_purgeable_nonvolatile = process.amount_purgeable_nonvolatile; - state.syscall_count = thread.syscall_count; - state.inode_faults = thread.inode_faults; - state.zero_faults = thread.zero_faults; - state.cow_faults = thread.cow_faults; - state.unix_socket_read_bytes = thread.unix_socket_read_bytes; - state.unix_socket_write_bytes = thread.unix_socket_write_bytes; - state.ipv4_socket_read_bytes = thread.ipv4_socket_read_bytes; - state.ipv4_socket_write_bytes = thread.ipv4_socket_write_bytes; - state.file_read_bytes = thread.file_read_bytes; - state.file_write_bytes = thread.file_write_bytes; - state.cpu_percent = 0; - - add_thread_data(thread.tid, **process_state, move(state)); - } - } else { - // FIXME: If there are no threads left in a process this is an indication - // for a zombie process, so it should be handled differently - we add a mock thread - // just to simulate a process with a single thread. - // Find a way to untie the process representation from a main thread so we can - // just represent a zombie process without creating a mock thread. - ThreadState state(**process_state); - state.tid = process.pid; - state.pid = process.pid; - state.ppid = process.ppid; - state.pgid = process.pgid; - state.sid = process.sid; - state.kernel = process.kernel; - state.executable = process.executable; - state.name = process.name; - state.uid = process.uid; - state.state = "Zombie"; - state.user = process.username; - state.pledge = process.pledge; - state.veil = process.veil; - state.amount_virtual = process.amount_virtual; - state.amount_resident = process.amount_resident; - state.amount_dirty_private = process.amount_dirty_private; - state.amount_clean_inode = process.amount_clean_inode; - state.amount_purgeable_volatile = process.amount_purgeable_volatile; - state.amount_purgeable_nonvolatile = process.amount_purgeable_nonvolatile; - - add_thread_data(process.pid, **process_state, move(state)); - } - } - } - - for (auto& c : m_cpus) { - c->total_cpu_percent = 0.0; - c->total_cpu_percent_kernel = 0.0; - } - - Vector tids_to_remove; - for (auto& it : m_threads) { - if (!live_tids.contains(it.key)) { - tids_to_remove.append(it.key); - continue; - } - if (it.value->current_state.state == "Zombie") { - continue; - } - auto& thread = *it.value; - u64 time_scheduled_diff = (thread.current_state.time_user + thread.current_state.time_kernel) - - (thread.previous_state.time_user + thread.previous_state.time_kernel); - u64 time_scheduled_diff_kernel = thread.current_state.time_kernel - thread.previous_state.time_kernel; - thread.current_state.cpu_percent = total_time_scheduled_diff > 0 ? (float)((time_scheduled_diff * 1000) / total_time_scheduled_diff) / 10.0f : 0; - thread.current_state.cpu_percent_kernel = total_time_scheduled_diff > 0 ? (float)((time_scheduled_diff_kernel * 1000) / total_time_scheduled_diff) / 10.0f : 0; - if (it.value->current_state.pid != 0) { - auto& cpu_info = m_cpus[thread.current_state.cpu]; - cpu_info->total_cpu_percent += thread.current_state.cpu_percent; - cpu_info->total_cpu_percent_kernel += thread.current_state.cpu_percent_kernel; - } - } - - // FIXME: Also remove dead threads from processes - for (auto tid : tids_to_remove) { - m_threads.remove(tid); - for (size_t i = 0; i < m_processes.size(); ++i) { - auto& process = m_processes[i]; - process->threads.remove_all_matching([&](auto const& thread) { return thread->current_state.tid == tid; }); - if (process->threads.size() == 0) { - m_processes.remove(i); - --i; - } - } - } - - if (on_cpu_info_change) - on_cpu_info_change(m_cpus); - - if (on_state_update) - on_state_update(process_count, m_threads.size()); - - // FIXME: This is a rather hackish way of invalidating indices. - // It would be good if GUI::Model had a way to orchestrate removal/insertion while preserving indices. - did_update(previous_tid_count == m_threads.size() ? GUI::Model::UpdateFlag::DontInvalidateIndices : GUI::Model::UpdateFlag::InvalidateAllIndices); -} - -bool ProcessModel::is_default_column(int index) const -{ - switch (index) { - case Column::PID: - case Column::TID: - case Column::Name: - case Column::CPU: - case Column::User: - case Column::Virtual: - case Column::DirtyPrivate: - return true; - default: - return false; - } -} diff --git a/Userland/Applications/SystemMonitor/ProcessModel.h b/Userland/Applications/SystemMonitor/ProcessModel.h deleted file mode 100644 index f833fd9c517..00000000000 --- a/Userland/Applications/SystemMonitor/ProcessModel.h +++ /dev/null @@ -1,284 +0,0 @@ -/* - * Copyright (c) 2018-2021, Andreas Kling - * Copyright (c) 2022, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include -#include -#include -#include - -class GraphWidget; - -class ProcessModel final : public GUI::Model { -public: - enum Column { - Icon = 0, - Name, - PID, - TID, - CPU, - State, - User, - Virtual, - DirtyPrivate, - Pledge, - Physical, - CleanInode, - PurgeableVolatile, - PurgeableNonvolatile, - Veil, - Processor, - Priority, - PPID, - PGID, - SID, - Syscalls, - InodeFaults, - ZeroFaults, - CowFaults, - FileReadBytes, - FileWriteBytes, - UnixSocketReadBytes, - UnixSocketWriteBytes, - IPv4SocketReadBytes, - IPv4SocketWriteBytes, - Command, - __Count - }; - - static constexpr GUI::ModelRole DISPLAY_VERBOSE = static_cast(0x101); - - static ErrorOr read_command_line(pid_t pid); - - static ProcessModel& the(); - - static NonnullRefPtr create() { return adopt_ref(*new ProcessModel); } - virtual ~ProcessModel() override = default; - - virtual int tree_column() const override { return Column::Name; } - virtual int row_count(GUI::ModelIndex const&) const override; - virtual int column_count(GUI::ModelIndex const&) const override; - virtual ErrorOr column_name(int) const override; - virtual GUI::Variant data(GUI::ModelIndex const&, GUI::ModelRole) const override; - virtual GUI::ModelIndex index(int row, int column, GUI::ModelIndex const& parent = {}) const override; - virtual GUI::ModelIndex parent_index(GUI::ModelIndex const&) const override; - virtual bool is_searchable() const override { return true; } - virtual Vector matches(StringView, unsigned = MatchesFlag::AllMatching, GUI::ModelIndex const& = GUI::ModelIndex()) override; - virtual bool is_column_sortable(int column_index) const override { return column_index != Column::Icon; } - void update(); - bool is_default_column(int index) const; - - struct CpuInfo { - u32 id; - float total_cpu_percent { 0.0 }; - float total_cpu_percent_kernel { 0.0 }; - - explicit CpuInfo(u32 id) - : id(id) - { - } - }; - - Function> const&)> on_cpu_info_change; - Function on_state_update; - - Vector> const& cpus() const { return m_cpus; } - -private: - ProcessModel(); - - struct Process; - - enum class EmptyCommand : u8 { - NotInitialized, - PermissionError, - }; - - struct ThreadState { - pid_t tid { 0 }; - pid_t pid { 0 }; - pid_t ppid { 0 }; - pid_t pgid { 0 }; - pid_t sid { 0 }; - u64 time_user { 0 }; - u64 time_kernel { 0 }; - bool kernel { false }; - ByteString executable { "" }; - ByteString name { "" }; - Variant command { EmptyCommand::NotInitialized }; - uid_t uid { 0 }; - ByteString state { "" }; - ByteString user { "" }; - ByteString pledge { "" }; - ByteString veil { "" }; - u32 cpu { 0 }; - u32 priority { 0 }; - size_t amount_virtual { 0 }; - size_t amount_resident { 0 }; - size_t amount_dirty_private { 0 }; - size_t amount_clean_inode { 0 }; - size_t amount_purgeable_volatile { 0 }; - size_t amount_purgeable_nonvolatile { 0 }; - unsigned syscall_count { 0 }; - unsigned inode_faults { 0 }; - unsigned zero_faults { 0 }; - unsigned cow_faults { 0 }; - u64 unix_socket_read_bytes { 0 }; - u64 unix_socket_write_bytes { 0 }; - u64 ipv4_socket_read_bytes { 0 }; - u64 ipv4_socket_write_bytes { 0 }; - u64 file_read_bytes { 0 }; - u64 file_write_bytes { 0 }; - float cpu_percent { 0 }; - float cpu_percent_kernel { 0 }; - Process& process; - - ThreadState(Process& argument_process) - : process(argument_process) - { - } - ThreadState(ThreadState&& other) = default; - ThreadState& operator=(ThreadState&& other) - { - this->tid = other.tid; - this->pid = other.pid; - this->ppid = other.ppid; - this->pgid = other.pgid; - this->sid = other.sid; - this->time_user = other.time_user; - this->time_kernel = other.time_kernel; - this->kernel = other.kernel; - this->executable = other.executable; - this->name = other.name; - this->command = other.command; - this->uid = other.uid; - this->state = other.state; - this->user = other.user; - this->pledge = other.pledge; - this->veil = other.veil; - this->cpu = other.cpu; - this->priority = other.priority; - this->amount_virtual = other.amount_virtual; - this->amount_resident = other.amount_resident; - this->amount_dirty_private = other.amount_dirty_private; - this->amount_clean_inode = other.amount_clean_inode; - this->amount_purgeable_volatile = other.amount_purgeable_volatile; - this->amount_purgeable_nonvolatile = other.amount_purgeable_nonvolatile; - this->syscall_count = other.syscall_count; - this->inode_faults = other.inode_faults; - this->zero_faults = other.zero_faults; - this->cow_faults = other.cow_faults; - this->unix_socket_read_bytes = other.unix_socket_read_bytes; - this->unix_socket_write_bytes = other.unix_socket_write_bytes; - this->ipv4_socket_read_bytes = other.ipv4_socket_read_bytes; - this->ipv4_socket_write_bytes = other.ipv4_socket_write_bytes; - this->file_read_bytes = other.file_read_bytes; - this->file_write_bytes = other.file_write_bytes; - this->cpu_percent = other.cpu_percent; - this->cpu_percent_kernel = other.cpu_percent_kernel; - this->process = other.process; - - return *this; - } - ~ThreadState() = default; - }; - - struct Thread : public RefCounted { - ThreadState current_state; - ThreadState previous_state; - - Thread(Process& process) - : current_state(process) - , previous_state(process) - { - } - - bool operator==(Thread const& other) const - { - return current_state.tid == other.current_state.tid; - } - - bool is_main_thread() const - { - return current_state.tid == current_state.process.pid; - } - - void read_command_line_if_necessary() - { - // Most likely the previous state still has a command line which we can copy over. - // Or, reading the command line was not allowed, e.g. on a Kernel process. - if ((previous_state.command.has() && !current_state.command.has()) - || (previous_state.command.has() && previous_state.command.get() == EmptyCommand::PermissionError)) { - current_state.command = previous_state.command; - return; - } - - auto maybe_command_line = read_command_line(current_state.pid); - if (!maybe_command_line.is_error()) { - current_state.command = maybe_command_line.value(); - previous_state.command = maybe_command_line.release_value(); - } else if (maybe_command_line.error().code() == EPERM || maybe_command_line.error().code() == EACCES) { - current_state.command = EmptyCommand::PermissionError; - previous_state.command = EmptyCommand::PermissionError; - } - } - }; - - struct Process { - pid_t pid; - Vector> threads; - - bool operator==(Process const& other) const - { - return this->pid == other.pid; - } - - Optional> main_thread() const - { - return threads.first_matching([this](auto const thread) { return thread->current_state.tid == pid; }); - } - - // Return anything but the main thread; therefore, valid indices are anything up to threads.size()-1 exclusive. - Thread const& non_main_thread(size_t index) const - { - auto main_thread_index = -1; - for (size_t i = 0; i < threads.size(); ++i) { - if (threads[i]->is_main_thread()) { - main_thread_index = static_cast(i); - break; - } - } - VERIFY(main_thread_index >= 0); - // Shift all indices starting from the main thread's index upwards, so that the user doesn't have to worry about index discontinuities. - if (index >= static_cast(main_thread_index)) - return threads[index + 1]; - return threads[index]; - } - }; - - GUI::Icon icon_for(Thread const& thread) const; - - int thread_model_row(Thread const& thread) const; - - ErrorOr ensure_process_statistics_file(); - - OwnPtr m_process_statistics_file; - - // The thread list contains the same threads as the Process structs. - HashMap> m_threads; - Vector> m_processes; - Vector> m_cpus; - GUI::Icon m_kernel_process_icon; - u64 m_total_time_scheduled { 0 }; - u64 m_total_time_scheduled_kernel { 0 }; - bool m_has_total_scheduled_time { false }; -}; diff --git a/Userland/Applications/SystemMonitor/ProcessStateWidget.cpp b/Userland/Applications/SystemMonitor/ProcessStateWidget.cpp deleted file mode 100644 index fa057d5e96a..00000000000 --- a/Userland/Applications/SystemMonitor/ProcessStateWidget.cpp +++ /dev/null @@ -1,113 +0,0 @@ -/* - * Copyright (c) 2021, Andreas Kling - * Copyright (c) 2022, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "ProcessStateWidget.h" -#include "ProcessModel.h" -#include -#include -#include -#include -#include -#include -#include -#include - -REGISTER_WIDGET(SystemMonitor, ProcessStateWidget) - -namespace SystemMonitor { - -class ProcessStateModel final - : public GUI::Model - , public GUI::ModelClient { -public: - explicit ProcessStateModel(ProcessModel& target, pid_t pid) - : m_target(target) - , m_pid(pid) - { - m_target.register_client(*this); - refresh(); - } - - virtual ~ProcessStateModel() override - { - m_target.unregister_client(*this); - } - - virtual int row_count(const GUI::ModelIndex& = GUI::ModelIndex()) const override { return m_target.column_count({}); } - virtual int column_count(const GUI::ModelIndex& = GUI::ModelIndex()) const override { return 2; } - - virtual GUI::Variant data(const GUI::ModelIndex& index, GUI::ModelRole role = GUI::ModelRole::Display) const override - { - if (role == GUI::ModelRole::Display) { - if (index.column() == 0) { - if (index.row() == ProcessModel::Column::Icon) { - // NOTE: The icon column is nameless in ProcessModel, but we want it to have a name here. - return "Icon"; - } - return m_target.column_name(index.row()).release_value_but_fixme_should_propagate_errors(); - } - return m_target_index.sibling_at_column(index.row()).data(ProcessModel::DISPLAY_VERBOSE); - } - - if (role == GUI::ModelRole::Font) { - if (index.column() == 0) { - return Gfx::FontDatabase::default_font().bold_variant(); - } - } - - return {}; - } - - virtual void model_did_update([[maybe_unused]] unsigned flags) override - { - refresh(); - } - - void refresh() - { - m_target_index = {}; - for (int row = 0; row < m_target.row_count({}); ++row) { - auto index = m_target.index(row, ProcessModel::Column::PID); - if (index.data().to_i32() == m_pid) { - m_target_index = index; - break; - } - } - did_update(GUI::Model::UpdateFlag::DontInvalidateIndices); - } - - void set_pid(pid_t pid) - { - m_pid = pid; - refresh(); - } - pid_t pid() const { return m_pid; } - -private: - ProcessModel& m_target; - GUI::ModelIndex m_target_index; - pid_t m_pid { -1 }; -}; - -ErrorOr> ProcessStateWidget::try_create() -{ - auto widget = TRY(adopt_nonnull_ref_or_enomem(new (nothrow) ProcessStateWidget())); - widget->set_layout(4); - widget->m_table_view = widget->add(); - widget->m_table_view->set_model(TRY(try_make_ref_counted(ProcessModel::the(), 0))); - widget->m_table_view->column_header().set_visible(false); - widget->m_table_view->column_header().set_section_size(0, 90); - return widget; -} - -void ProcessStateWidget::set_pid(pid_t pid) -{ - static_cast(m_table_view->model())->set_pid(pid); - update(); -} - -} diff --git a/Userland/Applications/SystemMonitor/ProcessStateWidget.h b/Userland/Applications/SystemMonitor/ProcessStateWidget.h deleted file mode 100644 index 4bcabfc4545..00000000000 --- a/Userland/Applications/SystemMonitor/ProcessStateWidget.h +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright (c) 2021, Andreas Kling - * Copyright (c) 2022, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include - -namespace SystemMonitor { - -class ProcessStateWidget final : public GUI::Widget { - C_OBJECT_ABSTRACT(ProcessStateWidget); - -public: - virtual ~ProcessStateWidget() override = default; - - static ErrorOr> try_create(); - - void set_pid(pid_t); - -private: - ProcessStateWidget() = default; - - RefPtr m_table_view; -}; - -} diff --git a/Userland/Applications/SystemMonitor/ProcessUnveiledPathsWidget.cpp b/Userland/Applications/SystemMonitor/ProcessUnveiledPathsWidget.cpp deleted file mode 100644 index 8ffe69514a5..00000000000 --- a/Userland/Applications/SystemMonitor/ProcessUnveiledPathsWidget.cpp +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * Copyright (c) 2022, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "ProcessUnveiledPathsWidget.h" -#include -#include -#include -#include -#include - -REGISTER_WIDGET(SystemMonitor, ProcessUnveiledPathsWidget) - -namespace SystemMonitor { - -ErrorOr> ProcessUnveiledPathsWidget::try_create() -{ - auto widget = TRY(adopt_nonnull_ref_or_enomem(new (nothrow) ProcessUnveiledPathsWidget())); - widget->set_layout(4); - widget->m_table_view = widget->add(); - - Vector pid_unveil_fields; - TRY(pid_unveil_fields.try_empend("path", "Path"_string, Gfx::TextAlignment::CenterLeft)); - TRY(pid_unveil_fields.try_empend("permissions", "Permissions"_string, Gfx::TextAlignment::CenterLeft)); - - widget->m_model = GUI::JsonArrayModel::create({}, move(pid_unveil_fields)); - widget->m_table_view->set_model(TRY(GUI::SortingProxyModel::create(*widget->m_model))); - return widget; -} - -void ProcessUnveiledPathsWidget::set_pid(pid_t pid) -{ - if (m_pid == pid) - return; - m_pid = pid; - m_model->set_json_path(ByteString::formatted("/proc/{}/unveil", m_pid)); -} - -} diff --git a/Userland/Applications/SystemMonitor/ProcessUnveiledPathsWidget.h b/Userland/Applications/SystemMonitor/ProcessUnveiledPathsWidget.h deleted file mode 100644 index ebb2a76f71f..00000000000 --- a/Userland/Applications/SystemMonitor/ProcessUnveiledPathsWidget.h +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * Copyright (c) 2022, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include - -namespace SystemMonitor { - -class ProcessUnveiledPathsWidget final : public GUI::Widget { - C_OBJECT_ABSTRACT(ProcessUnveiledPathsWidget); - -public: - virtual ~ProcessUnveiledPathsWidget() override = default; - - static ErrorOr> try_create(); - - void set_pid(pid_t); - -private: - ProcessUnveiledPathsWidget() = default; - - RefPtr m_table_view; - RefPtr m_model; - pid_t m_pid { -1 }; -}; - -} diff --git a/Userland/Applications/SystemMonitor/ProcessWindow.gml b/Userland/Applications/SystemMonitor/ProcessWindow.gml deleted file mode 100644 index 5cd9e3d52af..00000000000 --- a/Userland/Applications/SystemMonitor/ProcessWindow.gml +++ /dev/null @@ -1,68 +0,0 @@ -@GUI::Widget { - fill_with_background_color: true - layout: @GUI::VerticalBoxLayout { - margins: [4] - } - - @GUI::Widget { - shrink_to_fit: true - layout: @GUI::HorizontalBoxLayout { - margins: [4] - spacing: 8 - } - - @GUI::ImageWidget { - name: "process_icon" - fixed_size: [32, 32] - } - - @GUI::Label { - name: "process_name" - font_weight: "Bold" - text_alignment: "CenterLeft" - text: "This is the process name." - preferred_width: "grow" - } - } - - @GUI::HorizontalSeparator { - fixed_height: 2 - } - - @GUI::StackWidget { - name: "widget_stack" - - @SystemMonitor::UnavailableProcessWidget { - name: "unavailable_process" - } - - @GUI::TabWidget { - name: "available_process" - - @SystemMonitor::ProcessStateWidget { - name: "process_state" - title: "State" - } - - @SystemMonitor::ProcessMemoryMapWidget { - name: "memory_map" - title: "Memory map" - } - - @SystemMonitor::ProcessFileDescriptorMapWidget { - name: "open_files" - title: "Open files" - } - - @SystemMonitor::ProcessUnveiledPathsWidget { - name: "unveiled_paths" - title: "Unveiled paths" - } - - @SystemMonitor::ThreadStackWidget { - name: "thread_stack" - title: "Stack" - } - } - } -} diff --git a/Userland/Applications/SystemMonitor/SystemMonitor.gml b/Userland/Applications/SystemMonitor/SystemMonitor.gml deleted file mode 100644 index cac1bb2b848..00000000000 --- a/Userland/Applications/SystemMonitor/SystemMonitor.gml +++ /dev/null @@ -1,91 +0,0 @@ -@GUI::Widget { - fill_with_background_color: true - layout: @GUI::VerticalBoxLayout {} - - // Add a tasteful separating line between the menu and the main UI. - @GUI::HorizontalSeparator { - fixed_height: 2 - } - - @GUI::Widget { - layout: @GUI::VerticalBoxLayout { - margins: [0, 4, 4] - } - - @GUI::TabWidget { - name: "main_tabs" - - @GUI::Widget { - title: "Processes" - name: "processes" - layout: @GUI::VerticalBoxLayout { - margins: [4] - spacing: 0 - } - - @GUI::TreeView { - name: "process_table" - column_headers_visible: true - should_fill_selected_rows: true - selection_behavior: "SelectRows" - } - } - - @GUI::Widget { - title: "Performance" - name: "performance" - background_role: "Button" - fill_with_background_color: true - layout: @GUI::VerticalBoxLayout { - margins: [4] - } - - @GUI::GroupBox { - title: "CPU usage" - name: "cpu_graph" - layout: @GUI::VerticalBoxLayout {} - } - - @GUI::GroupBox { - title: "Memory usage" - fixed_height: 120 - layout: @GUI::VerticalBoxLayout { - margins: [6] - } - - @SystemMonitor::GraphWidget { - stack_values: true - name: "memory_graph" - } - } - - @SystemMonitor::MemoryStatsWidget { - name: "memory_stats" - memory_graph: "memory_graph" - } - } - - @SystemMonitor::StorageTabWidget { - title: "Storage" - name: "storage" - layout: @GUI::VerticalBoxLayout { - margins: [4] - } - - @GUI::TableView { - name: "storage_table" - } - } - - @SystemMonitor::NetworkStatisticsWidget { - title: "Network" - name: "network" - } - } - } - - @GUI::Statusbar { - segment_count: 3 - name: "statusbar" - } -} diff --git a/Userland/Applications/SystemMonitor/ThreadStackWidget.cpp b/Userland/Applications/SystemMonitor/ThreadStackWidget.cpp deleted file mode 100644 index 9df72c865b9..00000000000 --- a/Userland/Applications/SystemMonitor/ThreadStackWidget.cpp +++ /dev/null @@ -1,140 +0,0 @@ -/* - * Copyright (c) 2018-2021, Andreas Kling - * Copyright (c) 2022, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "ThreadStackWidget.h" -#include -#include -#include -#include -#include -#include - -REGISTER_WIDGET(SystemMonitor, ThreadStackWidget) - -namespace SystemMonitor { - -class ThreadStackModel final : public GUI::Model { - - enum Column { - Address, - Object, - Symbol - }; - -public: - int column_count(GUI::ModelIndex const&) const override { return 3; } - int row_count(GUI::ModelIndex const&) const override { return m_symbols.size(); } - bool is_column_sortable(int) const override { return false; } - - ErrorOr column_name(int column) const override - { - switch (column) { - case Column::Address: - return "Address"_string; - case Column::Object: - return "Object"_string; - case Column::Symbol: - return "Symbol"_string; - default: - VERIFY_NOT_REACHED(); - } - } - - GUI::Variant data(GUI::ModelIndex const& model_index, GUI::ModelRole) const override - { - auto& symbol = m_symbols[model_index.row()]; - switch (model_index.column()) { - case Column::Address: - return ByteString::formatted("{:p}", symbol.address); - case Column::Object: - return symbol.object; - case Column::Symbol: - return symbol.name; - default: - VERIFY_NOT_REACHED(); - } - } - - void set_symbols(Vector const& symbols) - { - if (m_symbols == symbols) - return; - m_symbols = symbols; - invalidate(); - } - -private: - Vector m_symbols; -}; - -ErrorOr> ThreadStackWidget::try_create() -{ - auto widget = TRY(adopt_nonnull_ref_or_enomem(new (nothrow) ThreadStackWidget())); - widget->set_layout(4); - widget->m_stack_table = widget->add(); - widget->m_stack_table->set_model(TRY(try_make_ref_counted())); - return widget; -} - -void ThreadStackWidget::show_event(GUI::ShowEvent&) -{ - refresh(); - if (!m_timer) { - m_timer = add(1000, [this] { refresh(); }); - m_timer->start(); - } -} - -void ThreadStackWidget::hide_event(GUI::HideEvent&) -{ - m_timer = nullptr; -} - -void ThreadStackWidget::set_ids(pid_t pid, pid_t tid) -{ - if (m_pid == pid && m_tid == tid) - return; - m_pid = pid; - m_tid = tid; -} - -class CompletionEvent : public Core::CustomEvent { -public: - explicit CompletionEvent(Vector symbols) - : Core::CustomEvent(0) - , m_symbols(move(symbols)) - { - } - - Vector const& symbols() const { return m_symbols; } - -private: - Vector m_symbols; -}; - -void ThreadStackWidget::refresh() -{ - (void)Threading::BackgroundAction>::construct( - [pid = m_pid, tid = m_tid](auto&) { - return Symbolication::symbolicate_thread(pid, tid, Symbolication::IncludeSourcePosition::No); - }, - - [weak_this = make_weak_ptr()](auto result) -> ErrorOr { - if (!weak_this) - return {}; - Core::EventLoop::current().post_event(const_cast(*weak_this), make(move(result))); - return {}; - }); -} - -void ThreadStackWidget::custom_event(Core::CustomEvent& event) -{ - auto& completion_event = verify_cast(event); - verify_cast(m_stack_table->model())->set_symbols(completion_event.symbols()); -} - -} diff --git a/Userland/Applications/SystemMonitor/ThreadStackWidget.h b/Userland/Applications/SystemMonitor/ThreadStackWidget.h deleted file mode 100644 index d206ba22ae6..00000000000 --- a/Userland/Applications/SystemMonitor/ThreadStackWidget.h +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * Copyright (c) 2022, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include - -namespace SystemMonitor { - -class ThreadStackWidget final : public GUI::Widget { - C_OBJECT_ABSTRACT(ThreadStackWidget) -public: - virtual ~ThreadStackWidget() override = default; - - static ErrorOr> try_create(); - - void set_ids(pid_t pid, pid_t tid); - void refresh(); - -private: - ThreadStackWidget() = default; - - virtual void show_event(GUI::ShowEvent&) override; - virtual void hide_event(GUI::HideEvent&) override; - virtual void custom_event(Core::CustomEvent&) override; - - pid_t m_pid { -1 }; - pid_t m_tid { -1 }; - RefPtr m_stack_table; - RefPtr m_timer; -}; - -} diff --git a/Userland/Applications/SystemMonitor/main.cpp b/Userland/Applications/SystemMonitor/main.cpp deleted file mode 100644 index ee6a258fe35..00000000000 --- a/Userland/Applications/SystemMonitor/main.cpp +++ /dev/null @@ -1,638 +0,0 @@ -/* - * Copyright (c) 2018-2021, Andreas Kling - * Copyright (c) 2021, Undefine - * Copyright (c) 2022, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "GraphWidget.h" -#include "MemoryStatsWidget.h" -#include "NetworkStatisticsWidget.h" -#include "ProcessFileDescriptorMapWidget.h" -#include "ProcessMemoryMapWidget.h" -#include "ProcessModel.h" -#include "ProcessStateWidget.h" -#include "ProcessUnveiledPathsWidget.h" -#include "ThreadStackWidget.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -static ErrorOr> build_process_window(pid_t); -static ErrorOr build_performance_tab(GUI::Widget&); - -static RefPtr statusbar; - -namespace SystemMonitor { - -class ProgressbarPaintingDelegate final : public GUI::TableCellPaintingDelegate { -public: - virtual ~ProgressbarPaintingDelegate() override = default; - - virtual void paint(GUI::Painter& painter, Gfx::IntRect const& a_rect, Palette const& palette, GUI::ModelIndex const& index) override - { - auto rect = a_rect.shrunken(2, 2); - auto percentage = index.data(GUI::ModelRole::Custom).to_i32(); - - auto data = index.data(); - ByteString text; - if (data.is_string()) - text = data.as_string(); - Gfx::StylePainter::paint_progressbar(painter, rect, palette, 0, 100, percentage, text); - painter.draw_rect(rect, Color::Black); - } -}; - -class UnavailableProcessWidget final : public GUI::Frame { - C_OBJECT(UnavailableProcessWidget) -public: - virtual ~UnavailableProcessWidget() override = default; - - String const& text() const { return m_text; } - void set_text(String text) - { - m_text = move(text); - update(); - } - -private: - UnavailableProcessWidget() - { - REGISTER_STRING_PROPERTY("text", text, set_text); - } - - virtual void paint_event(GUI::PaintEvent& event) override - { - Frame::paint_event(event); - if (text().is_empty()) - return; - GUI::Painter painter(*this); - painter.add_clip_rect(event.rect()); - painter.draw_text(frame_inner_rect(), text(), Gfx::TextAlignment::Center, palette().window_text(), Gfx::TextElision::Right); - } - - String m_text; -}; - -class StorageTabWidget final : public GUI::LazyWidget { - C_OBJECT(StorageTabWidget) -public: - StorageTabWidget() - { - this->on_first_show = [](GUI::LazyWidget& self) { - auto& fs_table_view = *self.find_child_of_type_named("storage_table"); - - Vector df_fields; - df_fields.empend("mount_point", "Mount point"_string, Gfx::TextAlignment::CenterLeft); - df_fields.empend("class_name", "Class"_string, Gfx::TextAlignment::CenterLeft); - df_fields.empend("source", "Source"_string, Gfx::TextAlignment::CenterLeft); - df_fields.empend( - "Size"_string, Gfx::TextAlignment::CenterRight, - [](JsonObject const& object) { - StringBuilder size_builder; - size_builder.append(' '); - size_builder.append(human_readable_size(object.get_u64("total_block_count"sv).value_or(0) * object.get_u64("block_size"sv).value_or(0))); - size_builder.append(' '); - return size_builder.to_byte_string(); - }, - [](JsonObject const& object) { - return object.get_u64("total_block_count"sv).value_or(0) * object.get_u64("block_size"sv).value_or(0); - }, - [](JsonObject const& object) { - auto total_blocks = object.get_u64("total_block_count"sv).value_or(0); - if (total_blocks == 0) - return 0; - auto free_blocks = object.get_u64("free_block_count"sv).value_or(0); - auto used_blocks = total_blocks - free_blocks; - int percentage = (static_cast(used_blocks) / static_cast(total_blocks) * 100.0); - return percentage; - }); - df_fields.empend( - "Used"_string, Gfx::TextAlignment::CenterRight, - [](JsonObject const& object) { - auto total_blocks = object.get_u64("total_block_count"sv).value_or(0); - auto free_blocks = object.get_u64("free_block_count"sv).value_or(0); - auto used_blocks = total_blocks - free_blocks; - return human_readable_size(used_blocks * object.get_u64("block_size"sv).value_or(0)); }, - [](JsonObject const& object) { - auto total_blocks = object.get_u64("total_block_count"sv).value_or(0); - auto free_blocks = object.get_u64("free_block_count"sv).value_or(0); - auto used_blocks = total_blocks - free_blocks; - return used_blocks * object.get_u64("block_size"sv).value_or(0); - }); - df_fields.empend( - "Available"_string, Gfx::TextAlignment::CenterRight, - [](JsonObject const& object) { - return human_readable_size(object.get_u64("free_block_count"sv).value_or(0) * object.get_u64("block_size"sv).value_or(0)); - }, - [](JsonObject const& object) { - return object.get_u64("free_block_count"sv).value_or(0) * object.get_u64("block_size"sv).value_or(0); - }); - df_fields.empend("Access"_string, Gfx::TextAlignment::CenterLeft, [](JsonObject const& object) { - bool readonly = object.get_bool("readonly"sv).value_or(false); - int mount_flags = object.get_i32("mount_flags"sv).value_or(0); - return readonly || (mount_flags & MS_RDONLY) ? "Read-only" : "Read/Write"; - }); - df_fields.empend("Mount flags"_string, Gfx::TextAlignment::CenterLeft, [](JsonObject const& object) { - int mount_flags = object.get_i32("mount_flags"sv).value_or(0); - StringBuilder builder; - bool first = true; - auto check = [&](int flag, StringView name) { - if (!(mount_flags & flag)) - return; - if (!first) - builder.append(','); - builder.append(name); - first = false; - }; - check(MS_NODEV, "nodev"sv); - check(MS_NOEXEC, "noexec"sv); - check(MS_NOSUID, "nosuid"sv); - check(MS_BIND, "bind"sv); - check(MS_RDONLY, "ro"sv); - check(MS_WXALLOWED, "wxallowed"sv); - check(MS_AXALLOWED, "axallowed"sv); - check(MS_NOREGULAR, "noregular"sv); - if (builder.string_view().is_empty()) - return ByteString("defaults"); - return builder.to_byte_string(); - }); - df_fields.empend("free_block_count", "Free blocks"_string, Gfx::TextAlignment::CenterRight); - df_fields.empend("total_block_count", "Total blocks"_string, Gfx::TextAlignment::CenterRight); - df_fields.empend("free_inode_count", "Free inodes"_string, Gfx::TextAlignment::CenterRight); - df_fields.empend("total_inode_count", "Total inodes"_string, Gfx::TextAlignment::CenterRight); - df_fields.empend("block_size", "Block size"_string, Gfx::TextAlignment::CenterRight); - - fs_table_view.set_model(MUST(GUI::SortingProxyModel::create(GUI::JsonArrayModel::create("/sys/kernel/df", move(df_fields))))); - - fs_table_view.set_column_painting_delegate(3, make()); - - fs_table_view.model()->invalidate(); - }; - } -}; - -} - -REGISTER_WIDGET(SystemMonitor, StorageTabWidget) -REGISTER_WIDGET(SystemMonitor, UnavailableProcessWidget) - -static bool can_access_pid(pid_t pid) -{ - int rc = kill(pid, 0); - return rc == 0; -} - -ErrorOr serenity_main(Main::Arguments arguments) -{ - { - // Before we do anything else, boost our process priority to the maximum allowed. - // It's very frustrating when the system is bogged down under load and you just want - // System Monitor to work. - sched_param param { - .sched_priority = THREAD_PRIORITY_MAX, - }; - sched_setparam(0, ¶m); - } - - TRY(Core::System::pledge("stdio thread proc recvfd sendfd rpath exec unix")); - - auto app = TRY(GUI::Application::create(arguments)); - - Config::pledge_domain("SystemMonitor"); - - TRY(Core::System::unveil("/etc/passwd", "r")); - TRY(Core::System::unveil("/res", "r")); - TRY(Core::System::unveil("/proc", "r")); - TRY(Core::System::unveil("/sys/kernel", "r")); - TRY(Core::System::unveil("/dev", "r")); - TRY(Core::System::unveil("/bin", "r")); - TRY(Core::System::unveil("/bin/Escalator", "x")); - TRY(Core::System::unveil("/bin/NetworkSettings", "x")); - TRY(Core::System::unveil("/usr/lib", "r")); - - // This directory only exists if ports are installed - if (auto result = Core::System::unveil("/usr/local/bin", "r"); result.is_error() && result.error().code() != ENOENT) - return result.release_error(); - - if (auto result = Core::System::unveil("/usr/local/lib", "r"); result.is_error() && result.error().code() != ENOENT) - return result.release_error(); - - // This file is only accessible when running as root if it is available on the disk image. - // It might be possible to not have this file on the disk image, if the user decided to not - // include kernel symbols for debug purposes so don't fail if the error is ENOENT. - if (auto result = Core::System::unveil("/boot/Kernel.debug", "r"); result.is_error() && (result.error().code() != EACCES && result.error().code() != ENOENT)) - return result.release_error(); - - TRY(Core::System::unveil("/bin/Profiler", "rx")); - // HackStudio doesn't exist in the minimal build configuration. - if (auto result = Core::System::unveil("/bin/HackStudio", "rx"); result.is_error() && result.error().code() != ENOENT) - return result.release_error(); - TRY(Core::System::unveil(nullptr, nullptr)); - - StringView args_tab = "processes"sv; - Core::ArgsParser parser; - parser.add_option(args_tab, "Tab, one of 'processes', 'graphs', 'fs', 'hardware', or 'network'", "open-tab", 't', "tab"); - parser.parse(arguments); - StringView args_tab_view = args_tab; - - auto app_icon = TRY(GUI::Icon::try_create_default_icon("app-system-monitor"sv)); - - auto window = GUI::Window::construct(); - window->set_title("System Monitor"); - window->restore_size_and_position("SystemMonitor"sv, "Window"sv, { { 560, 430 } }); - window->save_size_and_position_on_close("SystemMonitor"sv, "Window"sv); - - auto main_widget = window->set_main_widget(); - TRY(main_widget->load_from_gml(system_monitor_gml)); - auto& tabwidget = *main_widget->find_descendant_of_type_named("main_tabs"); - statusbar = main_widget->find_descendant_of_type_named("statusbar"); - - auto& process_table_container = *tabwidget.find_descendant_of_type_named("processes"); - - auto process_model = ProcessModel::create(); - process_model->on_state_update = [&](int process_count, int thread_count) { - statusbar->set_text(0, String::formatted("Processes: {}", process_count).release_value_but_fixme_should_propagate_errors()); - statusbar->set_text(1, String::formatted("Threads: {}", thread_count).release_value_but_fixme_should_propagate_errors()); - }; - - auto& performance_widget = *tabwidget.find_descendant_of_type_named("performance"); - TRY(build_performance_tab(performance_widget)); - - auto& process_table_view = *process_table_container.find_child_of_type_named("process_table"); - process_table_view.set_model(TRY(GUI::SortingProxyModel::create(process_model))); - process_table_view.column_header().set_section_selectable(ProcessModel::Icon, false); - - for (auto column = 0; column < ProcessModel::Column::__Count; ++column) { - process_table_view.set_column_visible(column, - Config::read_bool("SystemMonitor"sv, "ProcessTableColumns"sv, TRY(process_model->column_name(column)), - process_model->is_default_column(column))); - } - - process_table_view.set_key_column_and_sort_order(ProcessModel::Column::CPU, GUI::SortOrder::Descending); - process_model->update(); - - i32 frequency = Config::read_i32("SystemMonitor"sv, "Monitor"sv, "Frequency"sv, 3); - if (frequency != 1 && frequency != 3 && frequency != 5) { - frequency = 3; - Config::write_i32("SystemMonitor"sv, "Monitor"sv, "Frequency"sv, frequency); - } - - auto update_stats = [&] { - // FIXME: remove the primitive re-toggling code once persistent model indices work. - auto toggled_indices = process_table_view.selection().indices(); - toggled_indices.remove_all_matching([&](auto const& index) { return !process_table_view.is_toggled(index); }); - process_model->update(); - if (!process_table_view.selection().is_empty()) - process_table_view.selection().for_each_index([&](auto& selection) { - if (toggled_indices.contains_slow(selection)) - process_table_view.expand_all_parents_of(selection); - }); - - if (auto* memory_stats_widget = SystemMonitor::MemoryStatsWidget::the()) - memory_stats_widget->refresh(); - }; - update_stats(); - auto& refresh_timer = window->add(frequency * 1000, move(update_stats)); - refresh_timer.start(); - - auto selected_id = [&](ProcessModel::Column column) -> pid_t { - if (process_table_view.selection().is_empty()) - return -1; - auto pid_index = process_table_view.model()->index(process_table_view.selection().first().row(), column, process_table_view.selection().first().parent()); - return pid_index.data().to_i32(); - }; - - auto selected_name = [&](ProcessModel::Column column) -> ByteString { - if (process_table_view.selection().is_empty()) - return {}; - auto pid_index = process_table_view.model()->index(process_table_view.selection().first().row(), column, process_table_view.selection().first().parent()); - return pid_index.data().to_byte_string(); - }; - - auto kill_action = GUI::Action::create( - "&Kill Process", { Mod_Ctrl, Key_K }, { Key_Delete }, TRY(Gfx::Bitmap::load_from_file("/res/icons/16x16/kill.png"sv)), [&](const GUI::Action&) { - pid_t pid = selected_id(ProcessModel::Column::PID); - if (pid == -1) - return; - auto rc = GUI::MessageBox::show(window, ByteString::formatted("Do you really want to kill \"{}\" (PID {})?", selected_name(ProcessModel::Column::Name), pid), "System Monitor"sv, GUI::MessageBox::Type::Question, GUI::MessageBox::InputType::YesNo); - if (rc == GUI::Dialog::ExecResult::Yes) - kill(pid, SIGKILL); - }, - &process_table_view); - - auto stop_action = GUI::Action::create( - "&Stop Process", { Mod_Ctrl, Key_S }, TRY(Gfx::Bitmap::load_from_file("/res/icons/16x16/stop-hand.png"sv)), [&](const GUI::Action&) { - pid_t pid = selected_id(ProcessModel::Column::PID); - if (pid == -1) - return; - auto rc = GUI::MessageBox::show(window, ByteString::formatted("Do you really want to stop \"{}\" (PID {})?", selected_name(ProcessModel::Column::Name), pid), "System Monitor"sv, GUI::MessageBox::Type::Question, GUI::MessageBox::InputType::YesNo); - if (rc == GUI::Dialog::ExecResult::Yes) - kill(pid, SIGSTOP); - }, - &process_table_view); - - auto continue_action = GUI::Action::create( - "&Continue Process", { Mod_Ctrl, Key_C }, TRY(Gfx::Bitmap::load_from_file("/res/icons/16x16/continue.png"sv)), [&](const GUI::Action&) { - pid_t pid = selected_id(ProcessModel::Column::PID); - if (pid != -1) - kill(pid, SIGCONT); - }, - &process_table_view); - - auto profile_action = GUI::Action::create( - "&Profile Process", { Mod_Ctrl, Key_P }, - TRY(Gfx::Bitmap::load_from_file("/res/icons/16x16/app-profiler.png"sv)), [&](auto&) { - pid_t pid = selected_id(ProcessModel::Column::PID); - if (pid == -1) - return; - auto pid_string = ByteString::number(pid); - GUI::Process::spawn_or_show_error(window, "/bin/Profiler"sv, Array { "--pid", pid_string.characters() }); - }, - &process_table_view); - - auto debug_action = GUI::Action::create( - "Debug in HackStudio", { Mod_Ctrl, Key_D }, TRY(Gfx::Bitmap::load_from_file("/res/icons/16x16/app-hack-studio.png"sv)), [&](const GUI::Action&) { - pid_t pid = selected_id(ProcessModel::Column::PID); - if (pid == -1) - return; - auto pid_string = ByteString::number(pid); - GUI::Process::spawn_or_show_error(window, "/bin/HackStudio"sv, Array { "--pid", pid_string.characters() }); - }, - &process_table_view); - - HashMap> process_windows; - - auto process_properties_action = GUI::CommonActions::make_properties_action( - [&](auto&) { - auto pid = selected_id(ProcessModel::Column::PID); - if (pid == -1) - return; - - RefPtr process_window; - auto it = process_windows.find(pid); - if (it == process_windows.end()) { - auto process_window_or_error = build_process_window(pid); - if (process_window_or_error.is_error()) - return; - process_window = process_window_or_error.release_value(); - process_window->on_close_request = [pid, &process_windows] { - process_windows.remove(pid); - return GUI::Window::CloseRequestDecision::Close; - }; - process_windows.set(pid, *process_window); - } else { - process_window = it->value; - } - process_window->show(); - process_window->move_to_front(); - }, - &process_table_view); - - auto file_menu = window->add_menu("&File"_string); - file_menu->add_action(GUI::CommonActions::make_quit_action([](auto&) { - GUI::Application::the()->quit(); - })); - - auto process_context_menu = GUI::Menu::construct(); - process_context_menu->add_action(kill_action); - process_context_menu->add_action(stop_action); - process_context_menu->add_action(continue_action); - process_context_menu->add_separator(); - process_context_menu->add_action(profile_action); - process_context_menu->add_action(debug_action); - process_context_menu->add_separator(); - process_context_menu->add_action(process_properties_action); - process_table_view.on_context_menu_request = [&]([[maybe_unused]] const GUI::ModelIndex& index, const GUI::ContextMenuEvent& event) { - if (index.is_valid()) - process_context_menu->popup(event.screen_position(), process_properties_action); - }; - - auto frequency_menu = window->add_menu("F&requency"_string); - GUI::ActionGroup frequency_action_group; - frequency_action_group.set_exclusive(true); - - auto make_frequency_action = [&](int seconds) -> ErrorOr { - auto action = GUI::Action::create_checkable(ByteString::formatted("&{} Sec", seconds), [&refresh_timer, seconds](auto&) { - Config::write_i32("SystemMonitor"sv, "Monitor"sv, "Frequency"sv, seconds); - refresh_timer.restart(seconds * 1000); - }); - action->set_status_tip(TRY(String::formatted("Refresh every {} seconds", seconds))); - action->set_checked(frequency == seconds); - frequency_action_group.add_action(*action); - frequency_menu->add_action(*action); - return {}; - }; - - TRY(make_frequency_action(1)); - TRY(make_frequency_action(3)); - TRY(make_frequency_action(5)); - - auto view_menu = window->add_menu("&View"_string); - view_menu->add_action(GUI::CommonActions::make_fullscreen_action([&](auto&) { - window->set_fullscreen(!window->is_fullscreen()); - })); - - auto help_menu = window->add_menu("&Help"_string); - help_menu->add_action(GUI::CommonActions::make_command_palette_action(window)); - help_menu->add_action(GUI::CommonActions::make_about_action("System Monitor"_string, app_icon, window)); - - process_table_view.on_activation = [&](auto&) { - if (process_properties_action->is_enabled()) - process_properties_action->activate(); - }; - - static pid_t last_selected_pid; - - process_table_view.on_selection_change = [&] { - pid_t pid = selected_id(ProcessModel::Column::PID); - if (pid == last_selected_pid || pid < 1) - return; - last_selected_pid = pid; - bool has_access = can_access_pid(pid); - kill_action->set_enabled(has_access); - stop_action->set_enabled(has_access); - continue_action->set_enabled(has_access); - profile_action->set_enabled(has_access); - debug_action->set_enabled(has_access); - process_properties_action->set_enabled(has_access); - }; - - app->on_action_enter = [](GUI::Action const& action) { - statusbar->set_override_text(action.status_tip()); - }; - app->on_action_leave = [](GUI::Action const&) { - statusbar->set_override_text({}); - }; - - window->show(); - window->set_icon(app_icon.bitmap_for_size(16)); - - if (args_tab_view == "processes") - tabwidget.set_active_widget(&process_table_container); - else if (args_tab_view == "graphs") - tabwidget.set_active_widget(&performance_widget); - else if (args_tab_view == "fs") - tabwidget.set_active_widget(tabwidget.find_descendant_of_type_named("storage")); - else if (args_tab_view == "network") - tabwidget.set_active_widget(tabwidget.find_descendant_of_type_named("network")); - - int exec = app->exec(); - - // When exiting the application, save the configuration of the columns - // to be loaded the next time the application is opened. - auto& process_table_header = process_table_view.column_header(); - for (auto column = 0; column < ProcessModel::Column::__Count; ++column) - Config::write_bool("SystemMonitor"sv, "ProcessTableColumns"sv, TRY(process_model->column_name(column)), process_table_header.is_section_visible(column)); - - return exec; -} - -ErrorOr> build_process_window(pid_t pid) -{ - auto window = GUI::Window::construct(); - window->resize(480, 360); - window->set_title(ByteString::formatted("PID {} - System Monitor", pid)); - - auto app_icon = TRY(GUI::Icon::try_create_default_icon("app-system-monitor"sv)); - window->set_icon(app_icon.bitmap_for_size(16)); - - auto main_widget = window->set_main_widget(); - TRY(main_widget->load_from_gml(process_window_gml)); - - GUI::ModelIndex process_index; - for (int row = 0; row < ProcessModel::the().row_count({}); ++row) { - auto index = ProcessModel::the().index(row, ProcessModel::Column::PID); - if (index.data().to_i32() == pid) { - process_index = index; - break; - } - } - - VERIFY(process_index.is_valid()); - if (auto icon_data = process_index.sibling_at_column(ProcessModel::Column::Icon).data(); icon_data.is_icon()) { - main_widget->find_descendant_of_type_named("process_icon")->set_bitmap(icon_data.as_icon().bitmap_for_size(32)); - } - - main_widget->find_descendant_of_type_named("process_name")->set_text(TRY(String::formatted("{} (PID {})", process_index.sibling_at_column(ProcessModel::Column::Name).data().to_byte_string(), pid))); - - main_widget->find_descendant_of_type_named("process_state")->set_pid(pid); - main_widget->find_descendant_of_type_named("open_files")->set_pid(pid); - main_widget->find_descendant_of_type_named("thread_stack")->set_ids(pid, pid); - main_widget->find_descendant_of_type_named("memory_map")->set_pid(pid); - main_widget->find_descendant_of_type_named("unveiled_paths")->set_pid(pid); - - auto& widget_stack = *main_widget->find_descendant_of_type_named("widget_stack"); - auto& unavailable_process_widget = *widget_stack.find_descendant_of_type_named("unavailable_process"); - unavailable_process_widget.set_text(TRY(String::formatted("Unable to access PID {}", pid))); - - if (can_access_pid(pid)) - widget_stack.set_active_widget(widget_stack.find_descendant_of_type_named("available_process")); - else - widget_stack.set_active_widget(&unavailable_process_widget); - - return window; -} - -ErrorOr build_performance_tab(GUI::Widget& graphs_container) -{ - auto& cpu_graph_group_box = *graphs_container.find_descendant_of_type_named("cpu_graph"); - - size_t cpu_graphs_per_row = min(4, ProcessModel::the().cpus().size()); - auto cpu_graph_rows = ceil_div(ProcessModel::the().cpus().size(), cpu_graphs_per_row); - cpu_graph_group_box.set_fixed_height(120u * cpu_graph_rows); - - Vector cpu_graphs; - for (auto row = 0u; row < cpu_graph_rows; ++row) { - auto& cpu_graph_row = cpu_graph_group_box.add(); - cpu_graph_row.set_layout(6); - cpu_graph_row.set_fixed_height(108); - for (auto i = 0u; i < cpu_graphs_per_row; ++i) { - auto& cpu_graph = cpu_graph_row.add(); - cpu_graph.set_max(100); - cpu_graph.set_value_format(0, { - .graph_color_role = ColorRole::SyntaxPreprocessorStatement, - .text_formatter = [](u64 value) { - return ByteString::formatted("Total: {}%", value); - }, - }); - cpu_graph.set_value_format(1, { - .graph_color_role = ColorRole::SyntaxPreprocessorValue, - .text_formatter = [](u64 value) { - return ByteString::formatted("Kernel: {}%", value); - }, - }); - cpu_graphs.append(cpu_graph); - } - } - ProcessModel::the().on_cpu_info_change = [cpu_graphs](Vector> const& cpus) mutable { - float sum_cpu = 0; - for (size_t i = 0; i < cpus.size(); ++i) { - cpu_graphs[i].add_value({ static_cast(cpus[i]->total_cpu_percent), static_cast(cpus[i]->total_cpu_percent_kernel) }); - sum_cpu += cpus[i]->total_cpu_percent; - } - float cpu_usage = sum_cpu / (float)cpus.size(); - statusbar->set_text(2, String::formatted("CPU usage: {}%", (int)roundf(cpu_usage)).release_value_but_fixme_should_propagate_errors()); - }; - - auto& memory_graph = *graphs_container.find_descendant_of_type_named("memory_graph"); - memory_graph.set_value_format(0, { - .graph_color_role = ColorRole::SyntaxComment, - .text_formatter = [](u64 bytes) { - return ByteString::formatted("Committed: {}", human_readable_size(bytes)); - }, - }); - memory_graph.set_value_format(1, { - .graph_color_role = ColorRole::SyntaxPreprocessorStatement, - .text_formatter = [](u64 bytes) { - return ByteString::formatted("Allocated: {}", human_readable_size(bytes)); - }, - }); - memory_graph.set_value_format(2, { - .graph_color_role = ColorRole::SyntaxPreprocessorValue, - .text_formatter = [](u64 bytes) { - return ByteString::formatted("Kernel heap: {}", human_readable_size(bytes)); - }, - }); - return {}; -} diff --git a/Userland/Applications/Terminal/CMakeLists.txt b/Userland/Applications/Terminal/CMakeLists.txt deleted file mode 100644 index f6884b73ca1..00000000000 --- a/Userland/Applications/Terminal/CMakeLists.txt +++ /dev/null @@ -1,12 +0,0 @@ -serenity_component( - Terminal - REQUIRED - TARGETS Terminal utmpupdate -) - -set(SOURCES - main.cpp -) - -serenity_app(Terminal ICON app-terminal) -target_link_libraries(Terminal PRIVATE LibConfig LibCore LibFileSystem LibDesktop LibGfx LibGUI LibVT LibMain LibURL) diff --git a/Userland/Applications/Terminal/main.cpp b/Userland/Applications/Terminal/main.cpp deleted file mode 100644 index 672e2d2f4f1..00000000000 --- a/Userland/Applications/Terminal/main.cpp +++ /dev/null @@ -1,477 +0,0 @@ -/* - * Copyright (c) 2018-2021, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -class TerminalChangeListener : public Config::Listener { -public: - TerminalChangeListener(VT::TerminalWidget& parent_terminal) - : m_parent_terminal(parent_terminal) - { - } - - virtual void config_bool_did_change(StringView domain, StringView group, StringView key, bool value) override - { - VERIFY(domain == "Terminal"); - - if (group == "Terminal") { - if (key == "ShowScrollBar") - m_parent_terminal.set_show_scrollbar(value); - else if (key == "ConfirmClose" && on_confirm_close_changed) - on_confirm_close_changed(value); - } else if (group == "Cursor" && key == "Blinking") { - m_parent_terminal.set_cursor_blinking(value); - } - } - - virtual void config_string_did_change(StringView domain, StringView group, StringView key, StringView value) override - { - VERIFY(domain == "Terminal"); - - if (group == "Window" && key == "Bell") { - auto bell_mode = VT::TerminalWidget::parse_bell(value).value_or(VT::TerminalWidget::BellMode::Visible); - m_parent_terminal.set_bell_mode(bell_mode); - } else if (group == "Text" && key == "Font") { - auto font = Gfx::FontDatabase::the().get_by_name(value); - if (font.is_null()) - font = Gfx::FontDatabase::default_fixed_width_font(); - m_parent_terminal.set_font_and_resize_to_fit(*font); - m_parent_terminal.apply_size_increments_to_window(*m_parent_terminal.window()); - m_parent_terminal.window()->resize(m_parent_terminal.size()); - } else if (group == "Cursor" && key == "Shape") { - auto cursor_shape = VT::TerminalWidget::parse_cursor_shape(value).value_or(VT::CursorShape::Block); - m_parent_terminal.set_cursor_shape(cursor_shape); - } else if (group == "Terminal" && key == "AutoMark") { - auto automark_mode = VT::TerminalWidget::parse_automark_mode(value).value_or(VT::TerminalWidget::AutoMarkMode::MarkInteractiveShellPrompt); - m_parent_terminal.set_auto_mark_mode(automark_mode); - } - } - - virtual void config_i32_did_change(StringView domain, StringView group, StringView key, i32 value) override - { - VERIFY(domain == "Terminal"); - - if (group == "Terminal" && key == "MaxHistorySize") { - m_parent_terminal.set_max_history_size(value); - } else if (group == "Window" && key == "Opacity") { - m_parent_terminal.set_opacity(value); - } - } - - Function on_confirm_close_changed; - -private: - VT::TerminalWidget& m_parent_terminal; -}; - -static ErrorOr utmp_update(StringView tty, pid_t pid, bool create) -{ - auto pid_string = TRY(String::number(pid)); - Array utmp_update_command { - "-f"sv, - "Terminal"sv, - "-p"sv, - pid_string.bytes_as_string_view(), - (create ? "-c"sv : "-d"sv), - tty, - }; - - auto utmpupdate_pid = TRY(Core::Process::spawn("/bin/utmpupdate"sv, utmp_update_command, {}, Core::Process::KeepAsChild::Yes)); - - Core::System::WaitPidResult status; - auto wait_successful = false; - while (!wait_successful) { - auto result = Core::System::waitpid(utmpupdate_pid, 0); - if (result.is_error() && result.error().code() != EINTR) { - return result.release_error(); - } else if (!result.is_error()) { - status = result.release_value(); - wait_successful = true; - } - } - - if (WIFEXITED(status.status) && WEXITSTATUS(status.status) != 0) - dbgln("Terminal: utmpupdate exited with status {}", WEXITSTATUS(status.status)); - else if (WIFSIGNALED(status.status)) - dbgln("Terminal: utmpupdate exited due to unhandled signal {}", WTERMSIG(status.status)); - - return {}; -} - -static ErrorOr run_command(StringView command, bool keep_open) -{ - auto shell = TRY(String::from_byte_string(TRY(Core::Account::self(Core::Account::Read::PasswdOnly)).shell())); - if (shell.is_empty()) - shell = "/bin/Shell"_string; - - Vector arguments; - arguments.append(shell); - if (!command.is_empty()) { - if (keep_open) - arguments.append("--keep-open"sv); - arguments.append("-c"sv); - arguments.append(command); - } - auto env = TRY(FixedArray::create({ "TERM=xterm"sv, "PAGER=more"sv, "PATH="sv DEFAULT_PATH_SV })); - TRY(Core::System::exec(shell, arguments, Core::System::SearchInPath::No, env.span())); - VERIFY_NOT_REACHED(); -} - -static ErrorOr> create_find_window(VT::TerminalWidget& terminal) -{ - auto window = GUI::Window::construct(&terminal); - window->set_window_mode(GUI::WindowMode::RenderAbove); - window->set_title("Find in Terminal"); - window->set_resizable(false); - window->resize(300, 90); - - auto main_widget = window->set_main_widget(); - main_widget->set_fill_with_background_color(true); - main_widget->set_background_role(ColorRole::Button); - main_widget->set_layout(4); - - auto& find = main_widget->add(); - find.set_layout(4); - find.set_fixed_height(30); - - auto& find_textbox = find.add(); - find_textbox.set_fixed_width(230); - find_textbox.set_focus(true); - if (terminal.has_selection()) - find_textbox.set_text(terminal.selected_text().replace("\n"sv, " "sv, ReplaceMode::All)); - auto& find_backwards = find.add(); - find_backwards.set_fixed_width(25); - find_backwards.set_icon(TRY(Gfx::Bitmap::load_from_file("/res/icons/16x16/upward-triangle.png"sv))); - auto& find_forwards = find.add(); - find_forwards.set_fixed_width(25); - find_forwards.set_icon(TRY(Gfx::Bitmap::load_from_file("/res/icons/16x16/downward-triangle.png"sv))); - - find_textbox.on_return_pressed = [&find_backwards] { - find_backwards.click(); - }; - - find_textbox.on_shift_return_pressed = [&find_forwards] { - find_forwards.click(); - }; - - auto& match_case = main_widget->add("Case sensitive"_string); - auto& wrap_around = main_widget->add("Wrap around"_string); - - find_backwards.on_click = [&terminal, &find_textbox, &match_case, &wrap_around](auto) { - auto needle = find_textbox.text(); - if (needle.is_empty()) { - return; - } - - auto found_range = terminal.find_previous(needle, terminal.normalized_selection().start(), match_case.is_checked(), wrap_around.is_checked()); - - if (found_range.is_valid()) { - terminal.scroll_to_row(found_range.start().row()); - terminal.set_selection(found_range); - } - }; - find_forwards.on_click = [&terminal, &find_textbox, &match_case, &wrap_around](auto) { - auto needle = find_textbox.text(); - if (needle.is_empty()) { - return; - } - - auto found_range = terminal.find_next(needle, terminal.normalized_selection().end(), match_case.is_checked(), wrap_around.is_checked()); - - if (found_range.is_valid()) { - terminal.scroll_to_row(found_range.start().row()); - terminal.set_selection(found_range); - } - }; - - return window; -} - -ErrorOr serenity_main(Main::Arguments arguments) -{ - TRY(Core::System::pledge("stdio tty rpath cpath wpath recvfd sendfd proc exec unix sigaction")); - - struct sigaction act; - act.sa_mask = 0; - // Do not trust that both function pointers overlap. - act.sa_sigaction = nullptr; - - act.sa_flags = SA_NOCLDWAIT; - act.sa_handler = SIG_IGN; - - TRY(Core::System::sigaction(SIGCHLD, &act, nullptr)); - - auto app = TRY(GUI::Application::create(arguments)); - - TRY(Core::System::pledge("stdio tty rpath cpath wpath recvfd sendfd proc exec unix")); - - Config::pledge_domain("Terminal"); - - StringView command_to_execute; - bool keep_open = false; - - Core::ArgsParser args_parser; - args_parser.add_option(command_to_execute, "Execute this command inside the terminal", nullptr, 'e', "command"); - args_parser.add_option(keep_open, "Keep the terminal open after the command has finished executing", nullptr, 'k'); - - args_parser.parse(arguments); - - if (keep_open && command_to_execute.is_empty()) { - warnln("Option -k can only be used in combination with -e."); - return 1; - } - - int ptm_fd; - pid_t shell_pid = forkpty(&ptm_fd, nullptr, nullptr, nullptr); - if (shell_pid < 0) - return Error::from_errno(errno); - - // We're the child process; run the startup command. - if (shell_pid == 0) { - if (!command_to_execute.is_empty()) - TRY(run_command(command_to_execute, keep_open)); - else - TRY(run_command(Config::read_string("Terminal"sv, "Startup"sv, "Command"sv, ""sv), false)); - VERIFY_NOT_REACHED(); - } - - auto ptsname = TRY(Core::System::ptsname(ptm_fd)); - TRY(utmp_update(ptsname, shell_pid, true)); - - auto app_icon = GUI::Icon::default_icon("app-terminal"sv); - - auto window = GUI::Window::construct(); - window->set_title("Terminal"); - window->set_obey_widget_min_size(false); - - auto terminal = window->set_main_widget(ptm_fd, true); - terminal->set_startup_process_id(shell_pid); - - terminal->on_command_exit = [&] { - app->quit(0); - }; - terminal->on_title_change = [&](auto title) { - window->set_title(title); - }; - terminal->on_terminal_size_change = [&](auto size) { - window->resize(size); - }; - terminal->apply_size_increments_to_window(*window); - window->set_icon(app_icon.bitmap_for_size(16)); - - Config::monitor_domain("Terminal"); - auto should_confirm_close = Config::read_bool("Terminal"sv, "Terminal"sv, "ConfirmClose"sv, true); - TerminalChangeListener listener { terminal }; - - auto bell = Config::read_string("Terminal"sv, "Window"sv, "Bell"sv, "Visible"sv); - if (bell == "AudibleBeep") { - terminal->set_bell_mode(VT::TerminalWidget::BellMode::AudibleBeep); - } else if (bell == "Disabled") { - terminal->set_bell_mode(VT::TerminalWidget::BellMode::Disabled); - } else { - terminal->set_bell_mode(VT::TerminalWidget::BellMode::Visible); - } - - auto automark = Config::read_string("Terminal"sv, "Terminal"sv, "AutoMark"sv, "MarkInteractiveShellPrompt"sv); - if (automark == "MarkNothing") { - terminal->set_auto_mark_mode(VT::TerminalWidget::AutoMarkMode::MarkNothing); - } else { - terminal->set_auto_mark_mode(VT::TerminalWidget::AutoMarkMode::MarkInteractiveShellPrompt); - } - - auto cursor_shape = VT::TerminalWidget::parse_cursor_shape(Config::read_string("Terminal"sv, "Cursor"sv, "Shape"sv, "Block"sv)).value_or(VT::CursorShape::Block); - terminal->set_cursor_shape(cursor_shape); - - auto cursor_blinking = Config::read_bool("Terminal"sv, "Cursor"sv, "Blinking"sv, true); - terminal->set_cursor_blinking(cursor_blinking); - - auto find_window = TRY(create_find_window(terminal)); - - auto new_opacity = Config::read_i32("Terminal"sv, "Window"sv, "Opacity"sv, 255); - terminal->set_opacity(new_opacity); - window->set_has_alpha_channel(new_opacity < 255); - - auto new_scrollback_size = Config::read_i32("Terminal"sv, "Terminal"sv, "MaxHistorySize"sv, terminal->max_history_size()); - terminal->set_max_history_size(new_scrollback_size); - - auto show_scroll_bar = Config::read_bool("Terminal"sv, "Terminal"sv, "ShowScrollBar"sv, true); - terminal->set_show_scrollbar(show_scroll_bar); - - auto open_settings_action = GUI::Action::create("Terminal &Settings", TRY(Gfx::Bitmap::load_from_file("/res/icons/16x16/settings.png"sv)), - [&](auto&) { - GUI::Process::spawn_or_show_error(window, "/bin/TerminalSettings"sv); - }); - - terminal->context_menu().add_separator(); - terminal->context_menu().add_action(open_settings_action); - - auto file_menu = window->add_menu("&File"_string); - file_menu->add_action(GUI::Action::create("Open New &Terminal", { Mod_Ctrl | Mod_Shift, Key_N }, TRY(Gfx::Bitmap::load_from_file("/res/icons/16x16/app-terminal.png"sv)), [&](auto&) { - GUI::Process::spawn_or_show_error(window, "/bin/Terminal"sv); - })); - - file_menu->add_action(open_settings_action); - file_menu->add_separator(); - - auto tty_has_foreground_process = [&] { - pid_t fg_pid = tcgetpgrp(ptm_fd); - return fg_pid != -1 && fg_pid != shell_pid; - }; - - auto shell_child_process_count = [&] { - int background_process_count = 0; - Core::Directory::for_each_entry(String::formatted("/proc/{}/children", shell_pid).release_value_but_fixme_should_propagate_errors(), Core::DirIterator::Flags::SkipParentAndBaseDir, [&](auto&, auto&) { - ++background_process_count; - return IterationDecision::Continue; - }).release_value_but_fixme_should_propagate_errors(); - return background_process_count; - }; - - auto check_terminal_quit = [&]() -> GUI::Dialog::ExecResult { - if (!should_confirm_close) - return GUI::MessageBox::ExecResult::OK; - Optional close_message; - auto title = "Running Process"sv; - if (tty_has_foreground_process()) { - close_message = "Close Terminal and kill its foreground process?"_string; - } else { - auto child_process_count = shell_child_process_count(); - if (child_process_count > 1) { - title = "Running Processes"sv; - close_message = String::formatted("Close Terminal and kill its {} background processes?", child_process_count).release_value_but_fixme_should_propagate_errors(); - } else if (child_process_count == 1) { - close_message = "Close Terminal and kill its background process?"_string; - } - } - if (close_message.has_value()) - return GUI::MessageBox::show(window, *close_message, title, GUI::MessageBox::Type::Warning, GUI::MessageBox::InputType::OKCancel); - return GUI::MessageBox::ExecResult::OK; - }; - - file_menu->add_action(GUI::CommonActions::make_quit_action([&](auto&) { - dbgln("Terminal: Quit menu activated!"); - if (check_terminal_quit() == GUI::MessageBox::ExecResult::OK) - GUI::Application::the()->quit(); - })); - - auto edit_menu = window->add_menu("&Edit"_string); - edit_menu->add_action(terminal->copy_action()); - edit_menu->add_action(terminal->paste_action()); - edit_menu->add_separator(); - edit_menu->add_action(GUI::Action::create("&Find...", { Mod_Ctrl | Mod_Shift, Key_F }, TRY(Gfx::Bitmap::load_from_file("/res/icons/16x16/find.png"sv)), - [&](auto&) { - find_window->show(); - find_window->move_to_front(); - })); - - auto view_menu = window->add_menu("&View"_string); - view_menu->add_action(GUI::CommonActions::make_fullscreen_action([&](auto&) { - window->set_fullscreen(!window->is_fullscreen()); - })); - view_menu->add_action(terminal->clear_including_history_action()); - view_menu->add_action(terminal->clear_to_previous_mark_action()); - - auto adjust_font_size = [&](float adjustment, Gfx::Font::AllowInexactSizeMatch preference) { - auto& font = terminal->font(); - auto new_size = max(5, font.presentation_size() + adjustment); - if (auto new_font = Gfx::FontDatabase::the().get(font.family(), new_size, font.weight(), font.width(), font.slope(), preference)) { - terminal->set_font_and_resize_to_fit(*new_font); - terminal->apply_size_increments_to_window(*window); - window->resize(terminal->size()); - } - }; - - view_menu->add_separator(); - view_menu->add_action(GUI::CommonActions::make_zoom_in_action([&](auto&) { - adjust_font_size(1, Gfx::Font::AllowInexactSizeMatch::Larger); - })); - view_menu->add_action(GUI::CommonActions::make_zoom_out_action([&](auto&) { - adjust_font_size(-1, Gfx::Font::AllowInexactSizeMatch::Smaller); - })); - - auto help_menu = window->add_menu("&Help"_string); - help_menu->add_action(GUI::CommonActions::make_command_palette_action(window)); - help_menu->add_action(GUI::CommonActions::make_help_action([](auto&) { - Desktop::Launcher::open(URL::create_with_file_scheme("/usr/share/man/man1/Applications/Terminal.md"), "/bin/Help"); - })); - help_menu->add_action(GUI::CommonActions::make_about_action("Terminal"_string, app_icon, window)); - - window->on_close_request = [&]() -> GUI::Window::CloseRequestDecision { - if (check_terminal_quit() == GUI::MessageBox::ExecResult::OK) - return GUI::Window::CloseRequestDecision::Close; - return GUI::Window::CloseRequestDecision::StayOpen; - }; - - window->on_input_preemption_change = [&](bool is_preempted) { - terminal->set_logical_focus(!is_preempted); - }; - - TRY(Core::System::unveil("/res", "r")); - TRY(Core::System::unveil("/bin", "r")); - TRY(Core::System::unveil("/proc", "r")); - TRY(Core::System::unveil("/bin/Terminal", "x")); - TRY(Core::System::unveil("/bin/TerminalSettings", "x")); - TRY(Core::System::unveil("/bin/utmpupdate", "x")); - TRY(Core::System::unveil("/etc/FileIconProvider.ini", "r")); - TRY(Core::System::unveil("/tmp/session/%sid/portal/launch", "rw")); - TRY(Core::System::unveil("/tmp/session/%sid/portal/config", "rw")); - TRY(Core::System::unveil(nullptr, nullptr)); - - auto modified_state_check_timer = Core::Timer::create_repeating(500, [&] { - window->set_modified(tty_has_foreground_process() || shell_child_process_count() > 0); - }); - - listener.on_confirm_close_changed = [&](bool confirm_close) { - if (confirm_close) { - modified_state_check_timer->start(); - } else { - modified_state_check_timer->stop(); - window->set_modified(false); - } - should_confirm_close = confirm_close; - }; - - window->show(); - if (should_confirm_close) - modified_state_check_timer->start(); - int result = app->exec(); - dbgln("Exiting terminal, updating utmp"); - TRY(utmp_update(ptsname, 0, false)); - return result; -} diff --git a/Userland/Applications/TerminalSettings/CMakeLists.txt b/Userland/Applications/TerminalSettings/CMakeLists.txt deleted file mode 100644 index b8dd06bf6fb..00000000000 --- a/Userland/Applications/TerminalSettings/CMakeLists.txt +++ /dev/null @@ -1,19 +0,0 @@ -serenity_component( - TerminalSettings - REQUIRED - TARGETS TerminalSettings -) - -compile_gml(TerminalSettingsMain.gml TerminalSettingsMainGML.cpp) -compile_gml(TerminalSettingsView.gml TerminalSettingsViewGML.cpp) - -set(SOURCES - TerminalSettingsMainGML.cpp - TerminalSettingsViewGML.cpp - MainWidget.cpp - ViewWidget.cpp - main.cpp -) - -serenity_app(TerminalSettings ICON app-terminal) -target_link_libraries(TerminalSettings PRIVATE LibCore LibGfx LibGUI LibConfig LibMain LibVT) diff --git a/Userland/Applications/TerminalSettings/MainWidget.cpp b/Userland/Applications/TerminalSettings/MainWidget.cpp deleted file mode 100644 index c4ba4865e93..00000000000 --- a/Userland/Applications/TerminalSettings/MainWidget.cpp +++ /dev/null @@ -1,129 +0,0 @@ -/* - * Copyright (c) 2018-2021, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "MainWidget.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace TerminalSettings { -ErrorOr> MainWidget::create() -{ - auto widget = TRY(MainWidget::try_create()); - TRY(widget->setup()); - return widget; -} - -ErrorOr MainWidget::setup() -{ - auto& beep_bell_radio = *find_descendant_of_type_named("beep_bell_radio"); - auto& visual_bell_radio = *find_descendant_of_type_named("visual_bell_radio"); - auto& no_bell_radio = *find_descendant_of_type_named("no_bell_radio"); - auto& automark_off_radio = *find_descendant_of_type_named("automark_off"); - auto& automark_on_interactive_prompt_radio = *find_descendant_of_type_named("automark_on_interactive_prompt"); - - m_bell_mode = VT::TerminalWidget::parse_bell(Config::read_string("Terminal"sv, "Window"sv, "Bell"sv)).value_or(VT::TerminalWidget::BellMode::Visible); - m_original_bell_mode = m_bell_mode; - - m_automark_mode = VT::TerminalWidget::parse_automark_mode(Config::read_string("Terminal"sv, "Terminal"sv, "AutoMark"sv)).value_or(VT::TerminalWidget::AutoMarkMode::MarkInteractiveShellPrompt); - m_original_automark_mode = m_automark_mode; - - switch (m_bell_mode) { - case VT::TerminalWidget::BellMode::Visible: - visual_bell_radio.set_checked(true, GUI::AllowCallback::No); - break; - case VT::TerminalWidget::BellMode::AudibleBeep: - beep_bell_radio.set_checked(true, GUI::AllowCallback::No); - break; - case VT::TerminalWidget::BellMode::Disabled: - no_bell_radio.set_checked(true, GUI::AllowCallback::No); - break; - } - - beep_bell_radio.on_checked = [this](bool) { - m_bell_mode = VT::TerminalWidget::BellMode::AudibleBeep; - Config::write_string("Terminal"sv, "Window"sv, "Bell"sv, VT::TerminalWidget::stringify_bell(m_bell_mode)); - set_modified(true); - }; - visual_bell_radio.on_checked = [this](bool) { - m_bell_mode = VT::TerminalWidget::BellMode::Visible; - Config::write_string("Terminal"sv, "Window"sv, "Bell"sv, VT::TerminalWidget::stringify_bell(m_bell_mode)); - set_modified(true); - }; - no_bell_radio.on_checked = [this](bool) { - m_bell_mode = VT::TerminalWidget::BellMode::Disabled; - Config::write_string("Terminal"sv, "Window"sv, "Bell"sv, VT::TerminalWidget::stringify_bell(m_bell_mode)); - set_modified(true); - }; - - switch (m_automark_mode) { - case VT::TerminalWidget::AutoMarkMode::MarkNothing: - automark_off_radio.set_checked(true, GUI::AllowCallback::No); - break; - case VT::TerminalWidget::AutoMarkMode::MarkInteractiveShellPrompt: - automark_on_interactive_prompt_radio.set_checked(true, GUI::AllowCallback::No); - break; - } - - automark_off_radio.on_checked = [this](bool) { - m_automark_mode = VT::TerminalWidget::AutoMarkMode::MarkNothing; - Config::write_string("Terminal"sv, "Terminal"sv, "AutoMark"sv, VT::TerminalWidget::stringify_automark_mode(m_automark_mode)); - set_modified(true); - }; - automark_on_interactive_prompt_radio.on_checked = [this](bool) { - m_automark_mode = VT::TerminalWidget::AutoMarkMode::MarkInteractiveShellPrompt; - Config::write_string("Terminal"sv, "Terminal"sv, "AutoMark"sv, VT::TerminalWidget::stringify_automark_mode(m_automark_mode)); - set_modified(true); - }; - - m_confirm_close = Config::read_bool("Terminal"sv, "Terminal"sv, "ConfirmClose"sv, true); - m_orignal_confirm_close = m_confirm_close; - auto& confirm_close_checkbox = *find_descendant_of_type_named("terminal_confirm_close"); - confirm_close_checkbox.on_checked = [&](bool confirm_close) { - m_confirm_close = confirm_close; - Config::write_bool("Terminal"sv, "Terminal"sv, "ConfirmClose"sv, confirm_close); - set_modified(true); - }; - confirm_close_checkbox.set_checked(m_confirm_close, GUI::AllowCallback::No); - return {}; -} - -void MainWidget::apply_settings() -{ - m_original_bell_mode = m_bell_mode; - m_orignal_confirm_close = m_confirm_close; - write_back_settings(); -} -void MainWidget::write_back_settings() const -{ - Config::write_bool("Terminal"sv, "Terminal"sv, "ConfirmClose"sv, m_orignal_confirm_close); - Config::write_string("Terminal"sv, "Window"sv, "Bell"sv, VT::TerminalWidget::stringify_bell(m_original_bell_mode)); - Config::write_string("Terminal"sv, "Terminal"sv, "AutoMark"sv, VT::TerminalWidget::stringify_automark_mode(m_automark_mode)); -} - -void MainWidget::cancel_settings() -{ - write_back_settings(); -} -} diff --git a/Userland/Applications/TerminalSettings/MainWidget.h b/Userland/Applications/TerminalSettings/MainWidget.h deleted file mode 100644 index f4957900736..00000000000 --- a/Userland/Applications/TerminalSettings/MainWidget.h +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright (c) 2018-2021, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include -#include - -namespace TerminalSettings { -class MainWidget final : public GUI::SettingsWindow::Tab { - C_OBJECT_ABSTRACT(TerminalSettingsMainWidget) -public: - static ErrorOr> create(); - - virtual void apply_settings() override; - virtual void cancel_settings() override; - -private: - static ErrorOr> try_create(); - MainWidget() = default; - ErrorOr setup(); - void write_back_settings() const; - - VT::TerminalWidget::BellMode m_bell_mode { VT::TerminalWidget::BellMode::Disabled }; - VT::TerminalWidget::AutoMarkMode m_automark_mode { VT::TerminalWidget::AutoMarkMode::MarkInteractiveShellPrompt }; - bool m_confirm_close { true }; - - VT::TerminalWidget::BellMode m_original_bell_mode; - VT::TerminalWidget::AutoMarkMode m_original_automark_mode; - bool m_orignal_confirm_close { true }; -}; -} diff --git a/Userland/Applications/TerminalSettings/TerminalSettingsMain.gml b/Userland/Applications/TerminalSettings/TerminalSettingsMain.gml deleted file mode 100644 index a15c9b73ba8..00000000000 --- a/Userland/Applications/TerminalSettings/TerminalSettingsMain.gml +++ /dev/null @@ -1,69 +0,0 @@ -@TerminalSettings::MainWidget { - fill_with_background_color: true - layout: @GUI::VerticalBoxLayout { - margins: [10] - spacing: 5 - } - - @GUI::GroupBox { - title: "Bell mode" - preferred_height: "fit" - layout: @GUI::VerticalBoxLayout { - margins: [8] - spacing: 8 - } - - @GUI::Widget { - preferred_height: "fit" - layout: @GUI::VerticalBoxLayout { - spacing: 4 - } - - @GUI::RadioButton { - name: "beep_bell_radio" - text: "Audible bell" - } - - @GUI::RadioButton { - name: "visual_bell_radio" - text: "Visible bell" - } - - @GUI::RadioButton { - name: "no_bell_radio" - text: "No bell" - } - } - } - - @GUI::GroupBox { - title: "Exit behavior" - preferred_height: "fit" - layout: @GUI::VerticalBoxLayout { - margins: [8] - } - - @GUI::CheckBox { - name: "terminal_confirm_close" - text: "Confirm exit when process is active" - } - } - - @GUI::GroupBox { - title: "Auto-mark behavior" - preferred_height: "fit" - layout: @GUI::VerticalBoxLayout { - margins: [8] - } - - @GUI::RadioButton { - name: "automark_off" - text: "Do not auto-mark" - } - - @GUI::RadioButton { - name: "automark_on_interactive_prompt" - text: "Auto-mark on interactive shell prompts" - } - } -} diff --git a/Userland/Applications/TerminalSettings/TerminalSettingsView.gml b/Userland/Applications/TerminalSettings/TerminalSettingsView.gml deleted file mode 100644 index 15f3772349a..00000000000 --- a/Userland/Applications/TerminalSettings/TerminalSettingsView.gml +++ /dev/null @@ -1,128 +0,0 @@ -@TerminalSettings::ViewWidget { - fill_with_background_color: true - layout: @GUI::VerticalBoxLayout { - margins: [10] - spacing: 5 - } - - @GUI::GroupBox { - title: "Terminal font" - preferred_height: "fit" - layout: @GUI::VerticalBoxLayout { - margins: [8] - spacing: 8 - } - - @GUI::CheckBox { - name: "terminal_font_defaulted" - text: "Use system default" - } - - @GUI::Widget { - preferred_height: "fit" - name: "terminal_font_selection" - layout: @GUI::HorizontalBoxLayout { - spacing: 6 - } - - @GUI::Label { - background_role: "Base" - frame_style: "SunkenContainer" - fill_with_background_color: true - name: "terminal_font_label" - } - - @GUI::Button { - text: "..." - name: "terminal_font_button" - fixed_width: 30 - } - } - } - - @GUI::GroupBox { - title: "Background opacity" - preferred_height: "fit" - layout: @GUI::VerticalBoxLayout { - margins: [8] - spacing: 8 - } - - @GUI::HorizontalOpacitySlider { - name: "background_opacity_slider" - min: 0 - max: 255 - orientation: "Horizontal" - } - } - - @GUI::Widget { - preferred_height: "shrink" - layout: @GUI::HorizontalBoxLayout {} - - @GUI::GroupBox { - title: "Cursor shape" - layout: @GUI::VerticalBoxLayout { - margins: [8] - } - - @GUI::RadioButton { - name: "terminal_cursor_block" - text: "Block" - } - - @GUI::RadioButton { - name: "terminal_cursor_underline" - text: "Underscore" - } - - @GUI::RadioButton { - name: "terminal_cursor_bar" - text: "Vertical bar" - } - } - - @GUI::GroupBox { - title: "Cursor behavior" - layout: @GUI::VerticalBoxLayout { - margins: [8] - } - - @GUI::CheckBox { - name: "terminal_cursor_blinking" - text: "Blink cursor" - } - } - } - - @GUI::GroupBox { - title: "Scrollback" - preferred_height: "fit" - layout: @GUI::VerticalBoxLayout { - margins: [8] - spacing: 8 - } - - @GUI::Widget { - preferred_height: "shrink" - layout: @GUI::HorizontalBoxLayout {} - - @GUI::SpinBox { - name: "history_size_spinbox" - min: 0 - max: 40960 - preferred_width: 100 - } - - @GUI::Label { - text: "lines" - autosize: true - } - } - - @GUI::CheckBox { - name: "terminal_show_scrollbar" - text: "Show terminal scrollbar" - } - } -} diff --git a/Userland/Applications/TerminalSettings/ViewWidget.cpp b/Userland/Applications/TerminalSettings/ViewWidget.cpp deleted file mode 100644 index c107bafce2f..00000000000 --- a/Userland/Applications/TerminalSettings/ViewWidget.cpp +++ /dev/null @@ -1,192 +0,0 @@ -/* - * Copyright (c) 2018-2021, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "ViewWidget.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace TerminalSettings { -ErrorOr> ViewWidget::create() -{ - auto widget = TRY(ViewWidget::try_create()); - TRY(widget->setup()); - return widget; -} - -ErrorOr ViewWidget::setup() -{ - auto& slider = *find_descendant_of_type_named("background_opacity_slider"); - m_opacity = Config::read_i32("Terminal"sv, "Window"sv, "Opacity"sv); - m_original_opacity = m_opacity; - slider.set_value(m_opacity); - slider.on_change = [this](int value) { - m_opacity = value; - Config::write_i32("Terminal"sv, "Window"sv, "Opacity"sv, static_cast(m_opacity)); - set_modified(true); - }; - - auto& font_button = *find_descendant_of_type_named("terminal_font_button"); - auto& font_text = *find_descendant_of_type_named("terminal_font_label"); - auto font_name = Config::read_string("Terminal"sv, "Text"sv, "Font"sv); - if (font_name.is_empty()) - m_font = Gfx::FontDatabase::the().default_fixed_width_font(); - else - m_font = Gfx::FontDatabase::the().get_by_name(font_name); - m_original_font = m_font; - font_text.set_text(m_font->human_readable_name()); - font_text.set_font(m_font); - font_button.on_click = [&](auto) { - auto picker = GUI::FontPicker::construct(window(), m_font.ptr(), true); - if (picker->exec() == GUI::Dialog::ExecResult::OK) { - m_font = picker->font(); - font_text.set_text(m_font->human_readable_name()); - font_text.set_font(m_font); - Config::write_string("Terminal"sv, "Text"sv, "Font"sv, m_font->qualified_name()); - set_modified(true); - } - }; - - auto& font_selection = *find_descendant_of_type_named("terminal_font_selection"); - auto& use_default_font_button = *find_descendant_of_type_named("terminal_font_defaulted"); - use_default_font_button.on_checked = [&, font_name](bool use_default_font) { - if (use_default_font) { - font_selection.set_enabled(false); - m_font = Gfx::FontDatabase::the().default_fixed_width_font(); - font_text.set_text(m_font->human_readable_name()); - font_text.set_font(m_font); - Config::write_string("Terminal"sv, "Text"sv, "Font"sv, m_font->qualified_name()); - } else { - font_selection.set_enabled(true); - m_font = font_name.is_empty() - ? Gfx::FontDatabase::the().default_fixed_width_font() - : Gfx::FontDatabase::the().get_by_name(font_name); - Config::write_string("Terminal"sv, "Text"sv, "Font"sv, m_font->qualified_name()); - } - set_modified(true); - }; - // The "use default font" setting is not stored itself - we automatically set it if the actually present font is the default, - // whether that was filled in by the above defaulting code or by the user. - use_default_font_button.set_checked(m_font == Gfx::FontDatabase::the().default_fixed_width_font(), GUI::AllowCallback::No); - font_selection.set_enabled(!use_default_font_button.is_checked()); - - auto& terminal_cursor_block = *find_descendant_of_type_named("terminal_cursor_block"); - auto& terminal_cursor_underline = *find_descendant_of_type_named("terminal_cursor_underline"); - auto& terminal_cursor_bar = *find_descendant_of_type_named("terminal_cursor_bar"); - - auto& terminal_cursor_blinking = *find_descendant_of_type_named("terminal_cursor_blinking"); - - m_cursor_shape = VT::TerminalWidget::parse_cursor_shape(Config::read_string("Terminal"sv, "Cursor"sv, "Shape"sv)).value_or(VT::CursorShape::Block); - m_original_cursor_shape = m_cursor_shape; - - m_cursor_is_blinking_set = Config::read_bool("Terminal"sv, "Cursor"sv, "Blinking"sv, true); - m_original_cursor_is_blinking_set = m_cursor_is_blinking_set; - - switch (m_cursor_shape) { - case VT::CursorShape::Underline: - terminal_cursor_underline.set_checked(true); - break; - case VT::CursorShape::Bar: - terminal_cursor_bar.set_checked(true); - break; - default: - terminal_cursor_block.set_checked(true); - } - - terminal_cursor_blinking.on_checked = [&](bool is_checked) { - set_modified(true); - m_cursor_is_blinking_set = is_checked; - Config::write_bool("Terminal"sv, "Cursor"sv, "Blinking"sv, is_checked); - }; - terminal_cursor_blinking.set_checked(Config::read_bool("Terminal"sv, "Cursor"sv, "Blinking"sv, true)); - - terminal_cursor_block.on_checked = [&](bool) { - set_modified(true); - m_cursor_shape = VT::CursorShape::Block; - Config::write_string("Terminal"sv, "Cursor"sv, "Shape"sv, "Block"sv); - }; - terminal_cursor_block.set_checked(Config::read_string("Terminal"sv, "Cursor"sv, "Shape"sv) == "Block"sv); - - terminal_cursor_underline.on_checked = [&](bool) { - set_modified(true); - m_cursor_shape = VT::CursorShape::Underline; - Config::write_string("Terminal"sv, "Cursor"sv, "Shape"sv, "Underline"sv); - }; - terminal_cursor_underline.set_checked(Config::read_string("Terminal"sv, "Cursor"sv, "Shape"sv) == "Underline"sv); - - terminal_cursor_bar.on_checked = [&](bool) { - set_modified(true); - m_cursor_shape = VT::CursorShape::Bar; - Config::write_string("Terminal"sv, "Cursor"sv, "Shape"sv, "Bar"sv); - }; - terminal_cursor_bar.set_checked(Config::read_string("Terminal"sv, "Cursor"sv, "Shape"sv) == "Bar"sv); - - m_max_history_size = Config::read_i32("Terminal"sv, "Terminal"sv, "MaxHistorySize"sv); - m_original_max_history_size = m_max_history_size; - auto& history_size_spinbox = *find_descendant_of_type_named("history_size_spinbox"); - history_size_spinbox.set_value(m_max_history_size, GUI::AllowCallback::No); - history_size_spinbox.on_change = [this](int value) { - m_max_history_size = value; - Config::write_i32("Terminal"sv, "Terminal"sv, "MaxHistorySize"sv, static_cast(m_max_history_size)); - set_modified(true); - }; - - m_show_scrollbar = Config::read_bool("Terminal"sv, "Terminal"sv, "ShowScrollBar"sv, true); - m_original_show_scrollbar = m_show_scrollbar; - auto& show_scrollbar_checkbox = *find_descendant_of_type_named("terminal_show_scrollbar"); - show_scrollbar_checkbox.on_checked = [&](bool show_scrollbar) { - m_show_scrollbar = show_scrollbar; - Config::write_bool("Terminal"sv, "Terminal"sv, "ShowScrollBar"sv, show_scrollbar); - set_modified(true); - }; - show_scrollbar_checkbox.set_checked(m_show_scrollbar, GUI::AllowCallback::No); - return {}; -} - -void ViewWidget::apply_settings() -{ - m_original_opacity = m_opacity; - m_original_font = m_font; - m_original_cursor_shape = m_cursor_shape; - m_original_cursor_is_blinking_set = m_cursor_is_blinking_set; - m_original_max_history_size = m_max_history_size; - m_original_show_scrollbar = m_show_scrollbar; - write_back_settings(); -} - -void ViewWidget::write_back_settings() const -{ - Config::write_i32("Terminal"sv, "Window"sv, "Opacity"sv, static_cast(m_original_opacity)); - Config::write_string("Terminal"sv, "Text"sv, "Font"sv, m_original_font->qualified_name()); - Config::write_string("Terminal"sv, "Cursor"sv, "Shape"sv, VT::TerminalWidget::stringify_cursor_shape(m_original_cursor_shape)); - Config::write_bool("Terminal"sv, "Cursor"sv, "Blinking"sv, m_original_cursor_is_blinking_set); - Config::write_i32("Terminal"sv, "Terminal"sv, "MaxHistorySize"sv, static_cast(m_original_max_history_size)); - Config::write_bool("Terminal"sv, "Terminal"sv, "ShowScrollBar"sv, m_original_show_scrollbar); -} - -void ViewWidget::cancel_settings() -{ - write_back_settings(); -} -} diff --git a/Userland/Applications/TerminalSettings/ViewWidget.h b/Userland/Applications/TerminalSettings/ViewWidget.h deleted file mode 100644 index e35845ba272..00000000000 --- a/Userland/Applications/TerminalSettings/ViewWidget.h +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright (c) 2018-2021, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include -#include - -namespace TerminalSettings { -class ViewWidget final : public GUI::SettingsWindow::Tab { - C_OBJECT_ABSTRACT(TerminalSettingsViewWidget) -public: - static ErrorOr> try_create(); - static ErrorOr> create(); - - virtual void apply_settings() override; - virtual void cancel_settings() override; - -private: - ViewWidget() = default; - ErrorOr setup(); - void write_back_settings() const; - - RefPtr m_font; - float m_opacity; - ByteString m_color_scheme; - VT::CursorShape m_cursor_shape { VT::CursorShape::Block }; - bool m_cursor_is_blinking_set { true }; - size_t m_max_history_size; - bool m_show_scrollbar { true }; - - RefPtr m_original_font; - float m_original_opacity; - ByteString m_original_color_scheme; - VT::CursorShape m_original_cursor_shape; - bool m_original_cursor_is_blinking_set; - size_t m_original_max_history_size; - bool m_original_show_scrollbar { true }; -}; -} diff --git a/Userland/Applications/TerminalSettings/main.cpp b/Userland/Applications/TerminalSettings/main.cpp deleted file mode 100644 index 38bc80a2dc2..00000000000 --- a/Userland/Applications/TerminalSettings/main.cpp +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright (c) 2021, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "MainWidget.h" -#include "ViewWidget.h" -#include -#include -#include -#include -#include -#include -#include - -ErrorOr serenity_main(Main::Arguments arguments) -{ - TRY(Core::System::pledge("stdio rpath recvfd sendfd unix")); - auto app = TRY(GUI::Application::create(arguments)); - Config::pledge_domain("Terminal"); - - StringView selected_tab; - Core::ArgsParser args_parser; - args_parser.add_option(selected_tab, "Tab, one of 'terminal' or 'view'", "open-tab", 't', "tab"); - args_parser.parse(arguments); - - TRY(Core::System::pledge("stdio rpath recvfd sendfd")); - TRY(Core::System::unveil("/res", "r")); - TRY(Core::System::unveil(nullptr, nullptr)); - - auto app_icon = GUI::Icon::default_icon("app-terminal"sv); - - auto window = TRY(GUI::SettingsWindow::create("Terminal Settings")); - window->set_icon(app_icon.bitmap_for_size(16)); - (void)TRY(window->add_tab(TRY(TerminalSettings::ViewWidget::create()), "View"_string, "view"sv)); - (void)TRY(window->add_tab(TRY(TerminalSettings::MainWidget::create()), "Terminal"_string, "terminal"sv)); - window->set_active_tab(selected_tab); - - window->show(); - return app->exec(); -} diff --git a/Userland/Applications/TextEditor/CMakeLists.txt b/Userland/Applications/TextEditor/CMakeLists.txt deleted file mode 100644 index fab90b141c7..00000000000 --- a/Userland/Applications/TextEditor/CMakeLists.txt +++ /dev/null @@ -1,21 +0,0 @@ -serenity_component( - TextEditor - RECOMMENDED - TARGETS TextEditor - DEPENDS ImageDecoder RequestServer WebContent FileSystemAccessServer -) - -stringify_gml(TextEditorWindow.gml TextEditorWindowGML.h text_editor_window_gml) - -set(SOURCES - FileArgument.cpp - MainWidget.cpp - main.cpp -) - -set(GENERATED_SOURCES - TextEditorWindowGML.h -) - -serenity_app(TextEditor ICON app-text-editor) -target_link_libraries(TextEditor PRIVATE LibCore LibWebView LibWeb LibMarkdown LibGfx LibGUI LibShell LibRegex LibDesktop LibCpp LibCMake LibJS LibSQL LibSyntax LibFileSystemAccessClient LibConfig LibMain LibURL) diff --git a/Userland/Applications/TextEditor/FileArgument.cpp b/Userland/Applications/TextEditor/FileArgument.cpp deleted file mode 100644 index 9594ac40c5d..00000000000 --- a/Userland/Applications/TextEditor/FileArgument.cpp +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright (c) 2021, ry755 - * Copyright (c) 2022, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "FileArgument.h" -#include - -namespace TextEditor { - -FileArgument::FileArgument(String file_argument) -{ - m_line = {}; - m_column = {}; - - // A file doesn't exist with the full specified name, maybe the user entered - // line/column coordinates? - Regex re("^(.+?)(?::([0-9]+))?(?::([0-9]+))?$"); - RegexResult result = match(file_argument, re, - PosixFlags::Global | PosixFlags::Multiline | PosixFlags::Ungreedy); - auto& groups = result.capture_group_matches.at(0); - - // Match 0 group 0: file name - // Match 0 group 1: line number - // Match 0 group 2: column number - if (groups.size() > 2) { - // Both a line and column number were specified. - auto filename = groups.at(0).view.to_string().release_value_but_fixme_should_propagate_errors(); - auto initial_line_number = groups.at(1).view.to_string().release_value_but_fixme_should_propagate_errors().to_number(); - auto initial_column_number = groups.at(2).view.to_string().release_value_but_fixme_should_propagate_errors().to_number(); - - m_filename = filename; - if (initial_line_number.has_value() && initial_line_number.value() > 0) - m_line = initial_line_number.value(); - if (initial_column_number.has_value()) - m_column = initial_column_number.value(); - } else if (groups.size() == 2) { - // Only a line number was specified. - auto filename = groups.at(0).view.to_string().release_value_but_fixme_should_propagate_errors(); - auto initial_line_number = groups.at(1).view.to_string().release_value_but_fixme_should_propagate_errors().to_number(); - - m_filename = filename; - if (initial_line_number.has_value() && initial_line_number.value() > 0) - m_line = initial_line_number.value(); - } else { - // A colon was found at the end of the file name but no values were found - // after it. - m_filename = groups.at(0).view.to_string().release_value_but_fixme_should_propagate_errors(); - } -} -} diff --git a/Userland/Applications/TextEditor/FileArgument.h b/Userland/Applications/TextEditor/FileArgument.h deleted file mode 100644 index 1646a6b7bba..00000000000 --- a/Userland/Applications/TextEditor/FileArgument.h +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright (c) 2021, ry755 - * Copyright (c) 2022, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include - -namespace TextEditor { - -class FileArgument final { -public: - explicit FileArgument(String); - ~FileArgument() = default; - - String filename() { return m_filename; } - Optional line() { return m_line; } - Optional column() { return m_column; } - -private: - String m_filename; - Optional m_line; - Optional m_column; -}; - -} diff --git a/Userland/Applications/TextEditor/MainWidget.cpp b/Userland/Applications/TextEditor/MainWidget.cpp deleted file mode 100644 index fae57c30aca..00000000000 --- a/Userland/Applications/TextEditor/MainWidget.cpp +++ /dev/null @@ -1,973 +0,0 @@ -/* - * Copyright (c) 2018-2021, Andreas Kling - * Copyright (c) 2022, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "MainWidget.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace TextEditor { - -MainWidget::MainWidget() -{ - load_from_gml(text_editor_window_gml).release_value_but_fixme_should_propagate_errors(); - - m_toolbar = *find_descendant_of_type_named("toolbar"); - m_toolbar_container = *find_descendant_of_type_named("toolbar_container"); - - m_editor = *find_descendant_of_type_named("editor"); - m_editor->set_ruler_visible(true); - m_editor->set_automatic_indentation_enabled(true); - if (m_editor->editing_engine()->is_regular()) - m_editor->set_editing_engine(make()); - else if (m_editor->editing_engine()->is_vim()) - m_editor->set_editing_engine(make()); - else - VERIFY_NOT_REACHED(); - - auto font_entry = Config::read_string("TextEditor"sv, "Text"sv, "Font"sv, "default"sv); - if (font_entry != "default") - m_editor->set_font(Gfx::FontDatabase::the().get_by_name(font_entry)); - - m_editor->on_change = Core::debounce(100, [this] { - update_preview(); - }); - - m_editor->on_modified_change = [this](bool modified) { - window()->set_modified(modified); - }; - - m_find_replace_widget = *find_descendant_of_type_named("find_replace_widget"); - m_find_widget = *find_descendant_of_type_named("find_widget"); - m_replace_widget = *find_descendant_of_type_named("replace_widget"); - - m_find_textbox = *find_descendant_of_type_named("find_textbox"); - m_find_textbox->set_placeholder("Find"sv); - - m_replace_textbox = *find_descendant_of_type_named("replace_textbox"); - m_replace_textbox->set_placeholder("Replace"sv); - - m_match_case_checkbox = *find_descendant_of_type_named("match_case_checkbox"); - m_match_case_checkbox->on_checked = [this](auto is_checked) { - m_match_case = is_checked; - }; - m_match_case_checkbox->set_checked(true); - - m_regex_checkbox = *find_descendant_of_type_named("regex_checkbox"); - m_regex_checkbox->on_checked = [this](auto is_checked) { - m_use_regex = is_checked; - }; - m_regex_checkbox->set_checked(false); - - m_wrap_around_checkbox = *find_descendant_of_type_named("wrap_around_checkbox"); - m_wrap_around_checkbox->on_checked = [this](auto is_checked) { - m_should_wrap = is_checked; - }; - m_wrap_around_checkbox->set_checked(true); - - m_find_next_action = GUI::Action::create("Find &Next", { Mod_Ctrl, Key_G }, Gfx::Bitmap::load_from_file("/res/icons/16x16/find-next.png"sv).release_value_but_fixme_should_propagate_errors(), [this](auto&) { - find_text(GUI::TextEditor::SearchDirection::Forward, ShowMessageIfNoResults::Yes); - }); - - m_find_previous_action = GUI::Action::create("Find Pr&evious", { Mod_Ctrl | Mod_Shift, Key_G }, Gfx::Bitmap::load_from_file("/res/icons/16x16/find-previous.png"sv).release_value_but_fixme_should_propagate_errors(), [this](auto&) { - find_text(GUI::TextEditor::SearchDirection::Backward, ShowMessageIfNoResults::Yes); - }); - - m_replace_action = GUI::Action::create("Rep&lace", { Mod_Ctrl, Key_F1 }, [&](auto&) { - auto needle = m_find_textbox->text(); - auto substitute = m_replace_textbox->text(); - if (needle.is_empty()) - return; - if (m_use_regex) - m_editor->document().update_regex_matches(needle); - - auto found_range = m_editor->document().find_next(needle, m_editor->normalized_selection().start(), m_should_wrap ? GUI::TextDocument::SearchShouldWrap::Yes : GUI::TextDocument::SearchShouldWrap::No, m_use_regex, m_match_case); - if (found_range.is_valid()) { - m_editor->set_selection(found_range); - m_editor->insert_at_cursor_or_replace_selection(substitute); - } else { - GUI::MessageBox::show(window(), - ByteString::formatted("Not found: \"{}\"", needle), - "Not found"sv, - GUI::MessageBox::Type::Information); - } - }); - - m_replace_all_action = GUI::Action::create("Replace &All", { Mod_Ctrl, Key_F2 }, [&](auto&) { - auto needle = m_find_textbox->text(); - auto substitute = m_replace_textbox->text(); - auto length_delta = substitute.length() - needle.length(); - if (needle.is_empty()) - return; - if (m_use_regex) - m_editor->document().update_regex_matches(needle); - - auto found_range = m_editor->document().find_next(needle, {}, GUI::TextDocument::SearchShouldWrap::No, m_use_regex, m_match_case); - if (found_range.is_valid()) { - while (found_range.is_valid()) { - m_editor->set_selection(found_range); - m_editor->insert_at_cursor_or_replace_selection(substitute); - auto next_start = GUI::TextPosition(found_range.end().line(), found_range.end().column() + length_delta); - found_range = m_editor->document().find_next(needle, next_start, GUI::TextDocument::SearchShouldWrap::No, m_use_regex, m_match_case); - } - } else { - GUI::MessageBox::show(window(), - ByteString::formatted("Not found: \"{}\"", needle), - "Not found"sv, - GUI::MessageBox::Type::Information); - } - }); - - m_find_previous_button = *find_descendant_of_type_named("find_previous_button"); - m_find_previous_button->set_action(*m_find_previous_action); - m_find_previous_button->set_icon(Gfx::Bitmap::load_from_file("/res/icons/16x16/find-previous.png"sv).release_value_but_fixme_should_propagate_errors()); - - m_find_next_button = *find_descendant_of_type_named("find_next_button"); - m_find_next_button->set_action(*m_find_next_action); - m_find_next_button->set_icon(Gfx::Bitmap::load_from_file("/res/icons/16x16/find-next.png"sv).release_value_but_fixme_should_propagate_errors()); - - m_find_textbox->on_return_pressed = [this] { - m_find_next_button->click(); - }; - - m_find_textbox->on_escape_pressed = [this] { - m_find_replace_widget->set_visible(false); - m_editor->set_focus(true); - m_editor->reset_search_results(); - }; - - m_find_textbox->on_change = [this] { - m_editor->reset_search_results(); - find_text(GUI::TextEditor::SearchDirection::Forward, ShowMessageIfNoResults::No); - }; - - m_replace_button = *find_descendant_of_type_named("replace_button"); - m_replace_button->set_action(*m_replace_action); - - m_replace_all_button = *find_descendant_of_type_named("replace_all_button"); - m_replace_all_button->set_action(*m_replace_all_action); - - m_replace_textbox->on_return_pressed = [this] { - m_replace_button->click(); - }; - - m_replace_textbox->on_escape_pressed = [this] { - m_find_replace_widget->set_visible(false); - m_editor->set_focus(true); - }; - - m_vim_emulation_setting_action = GUI::Action::create_checkable("&Vim Emulation", { Mod_Ctrl | Mod_Shift | Mod_Alt, Key_V }, [&](auto& action) { - if (action.is_checked()) - m_editor->set_editing_engine(make()); - else - m_editor->set_editing_engine(make()); - }); - m_vim_emulation_setting_action->set_checked(false); - - m_find_replace_action = GUI::Action::create("&Find/Replace...", { Mod_Ctrl | Mod_Shift, Key_F }, Gfx::Bitmap::load_from_file("/res/icons/16x16/find.png"sv).release_value_but_fixme_should_propagate_errors(), [this](auto&) { - m_find_replace_widget->set_visible(true); - m_find_widget->set_visible(true); - m_replace_widget->set_visible(true); - m_find_textbox->set_focus(true); - - if (m_editor->has_selection()) { - auto selected_text = m_editor->document().text_in_range(m_editor->normalized_selection()); - m_find_textbox->set_text(selected_text); - } - m_find_textbox->select_all(); - }); - - m_editor->add_custom_context_menu_action(*m_find_replace_action); - m_editor->add_custom_context_menu_action(*m_find_next_action); - m_editor->add_custom_context_menu_action(*m_find_previous_action); - - m_line_column_statusbar_menu = GUI::Menu::construct(); - m_syntax_statusbar_menu = GUI::Menu::construct(); - - m_statusbar = *find_descendant_of_type_named("statusbar"); - m_statusbar->segment(1).set_mode(GUI::Statusbar::Segment::Mode::Auto); - m_statusbar->segment(1).set_clickable(true); - m_statusbar->segment(1).set_menu(m_syntax_statusbar_menu); - m_statusbar->segment(2).set_mode(GUI::Statusbar::Segment::Mode::Fixed); - auto width = font().width("Ln 0,000 Col 000"sv) + font().max_glyph_width(); - m_statusbar->segment(2).set_fixed_width(width); - m_statusbar->segment(2).set_clickable(true); - m_statusbar->segment(2).set_menu(m_line_column_statusbar_menu); - - GUI::Application::the()->on_action_enter = [this](GUI::Action& action) { - m_statusbar->set_override_text(action.status_tip()); - }; - - GUI::Application::the()->on_action_leave = [this](GUI::Action&) { - m_statusbar->set_override_text({}); - }; - - m_editor->on_cursor_change = [this] { update_statusbar(); }; - m_editor->on_selection_change = [this] { update_statusbar(); }; - m_editor->on_highlighter_change = [this] { update_statusbar(); }; - - m_new_action = GUI::Action::create("&New", { Mod_Ctrl, Key_N }, Gfx::Bitmap::load_from_file("/res/icons/16x16/new.png"sv).release_value_but_fixme_should_propagate_errors(), [this](GUI::Action const&) { - if (editor().document().is_modified()) { - auto save_document_first_result = GUI::MessageBox::ask_about_unsaved_changes(window(), m_path, editor().document().undo_stack().last_unmodified_timestamp()); - if (save_document_first_result == GUI::Dialog::ExecResult::Yes) - m_save_action->activate(); - if (save_document_first_result != GUI::Dialog::ExecResult::No && editor().document().is_modified()) - return; - } - - m_editor->set_text(StringView()); - set_path({}); - update_title(); - }); - - m_open_action = GUI::CommonActions::make_open_action([this](auto&) { - if (editor().document().is_modified()) { - auto save_document_first_result = GUI::MessageBox::ask_about_unsaved_changes(window(), m_path, editor().document().undo_stack().last_unmodified_timestamp()); - if (save_document_first_result == GUI::Dialog::ExecResult::Yes) - m_save_action->activate(); - if (save_document_first_result != GUI::Dialog::ExecResult::No && editor().document().is_modified()) - return; - } - - auto response = FileSystemAccessClient::Client::the().open_file(window()); - if (response.is_error()) - return; - - if (auto result = read_file(response.value().filename(), response.value().stream()); result.is_error()) - GUI::MessageBox::show(window(), "Unable to open file.\n"sv, "Error"sv, GUI::MessageBox::Type::Error); - }); - - m_save_as_action = GUI::CommonActions::make_save_as_action([&](auto&) { - auto extension = m_extension; - if (extension.is_empty() && m_editor->syntax_highlighter()) - extension = Syntax::common_language_extension(m_editor->syntax_highlighter()->language()); - - auto response = FileSystemAccessClient::Client::the().save_file(window(), m_name, extension); - if (response.is_error()) - return; - - auto file = response.release_value(); - if (auto result = m_editor->write_to_file(file.stream()); result.is_error()) { - GUI::MessageBox::show(window(), "Unable to save file.\n"sv, "Error"sv, GUI::MessageBox::Type::Error); - return; - } - - set_path(file.filename()); - GUI::Application::the()->set_most_recently_open_file(file.filename()); - dbgln("Wrote document to {}", file.filename()); - }); - - m_save_action = GUI::CommonActions::make_save_action([&](auto&) { - if (m_path.is_empty()) { - m_save_as_action->activate(); - return; - } - auto response = FileSystemAccessClient::Client::the().request_file(window(), m_path, Core::File::OpenMode::Truncate | Core::File::OpenMode::Write); - if (response.is_error()) - return; - - if (auto result = m_editor->write_to_file(response.value().stream()); result.is_error()) { - GUI::MessageBox::show(window(), "Unable to save file.\n"sv, "Error"sv, GUI::MessageBox::Type::Error); - } - }); - - auto file_manager_icon = Gfx::Bitmap::load_from_file("/res/icons/16x16/app-file-manager.png"sv).release_value_but_fixme_should_propagate_errors(); - m_open_folder_action = GUI::Action::create("Reveal in File Manager", { Mod_Ctrl | Mod_Shift, Key_O }, file_manager_icon, [&](auto&) { - auto lexical_path = LexicalPath(m_path); - Desktop::Launcher::open(URL::create_with_file_scheme(lexical_path.dirname(), lexical_path.basename())); - }); - m_open_folder_action->set_enabled(!m_path.is_empty()); - m_open_folder_action->set_status_tip("Open the current file location in File Manager"_string); - - m_toolbar->add_action(*m_new_action); - m_toolbar->add_action(*m_open_action); - m_toolbar->add_action(*m_save_action); - - m_toolbar->add_separator(); - - m_toolbar->add_action(*m_open_folder_action); - - m_toolbar->add_separator(); - - m_toolbar->add_action(m_editor->cut_action()); - m_toolbar->add_action(m_editor->copy_action()); - m_toolbar->add_action(m_editor->paste_action()); - - m_toolbar->add_separator(); - - m_toolbar->add_action(m_editor->undo_action()); - m_toolbar->add_action(m_editor->redo_action()); -} - -WebView::OutOfProcessWebView& MainWidget::ensure_web_view() -{ - if (!m_page_view) { - auto& web_view_container = *find_descendant_of_type_named("web_view_container"); - m_page_view = web_view_container.add(); - m_page_view->on_link_hover = [this](auto& url) { - if (url.is_valid()) - m_statusbar->set_text(String::from_byte_string(url.to_byte_string()).release_value_but_fixme_should_propagate_errors()); - else - update_statusbar(); - }; - m_page_view->on_link_click = [&](auto& url, auto&, unsigned) { - if (!Desktop::Launcher::open(url)) { - GUI::MessageBox::show( - window(), - ByteString::formatted("The link to '{}' could not be opened.", url), - "Failed to open link"sv, - GUI::MessageBox::Type::Error); - } - }; - } - return *m_page_view; -} - -ErrorOr MainWidget::initialize_menubar(GUI::Window& window) -{ - auto file_menu = window.add_menu("&File"_string); - file_menu->add_action(*m_new_action); - file_menu->add_action(*m_open_action); - file_menu->add_action(*m_save_action); - file_menu->add_action(*m_save_as_action); - file_menu->add_separator(); - file_menu->add_action(*m_open_folder_action); - file_menu->add_separator(); - - file_menu->add_recent_files_list([&](auto& action) { - if (editor().document().is_modified()) { - auto save_document_first_result = GUI::MessageBox::ask_about_unsaved_changes(&window, m_path, editor().document().undo_stack().last_unmodified_timestamp()); - if (save_document_first_result == GUI::Dialog::ExecResult::Yes) - m_save_action->activate(); - if (save_document_first_result != GUI::Dialog::ExecResult::No && editor().document().is_modified()) - return; - } - - auto response = FileSystemAccessClient::Client::the().request_file_read_only_approved(&window, action.text()); - if (response.is_error()) - return; - - if (auto result = read_file(response.value().filename(), response.value().stream()); result.is_error()) - GUI::MessageBox::show(&window, "Unable to open file.\n"sv, "Error"sv, GUI::MessageBox::Type::Error); - }); - file_menu->add_action(GUI::CommonActions::make_quit_action([this](auto&) { - if (!request_close()) - return; - GUI::Application::the()->quit(); - })); - - auto edit_menu = window.add_menu("&Edit"_string); - edit_menu->add_action(m_editor->undo_action()); - edit_menu->add_action(m_editor->redo_action()); - edit_menu->add_separator(); - edit_menu->add_action(m_editor->cut_action()); - edit_menu->add_action(m_editor->copy_action()); - edit_menu->add_action(m_editor->paste_action()); - edit_menu->add_separator(); - edit_menu->add_action(m_editor->insert_emoji_action()); - edit_menu->add_action(*m_vim_emulation_setting_action); - edit_menu->add_separator(); - edit_menu->add_action(*m_find_replace_action); - edit_menu->add_action(*m_find_next_action); - edit_menu->add_action(*m_find_previous_action); - edit_menu->add_action(*m_replace_action); - edit_menu->add_action(*m_replace_all_action); - - m_no_preview_action = GUI::Action::create_checkable( - "&No Preview", [this](auto&) { - set_preview_mode(PreviewMode::None); - }); - - m_markdown_preview_action = GUI::Action::create_checkable( - "&Markdown Preview", [this](auto&) { - set_preview_mode(PreviewMode::Markdown); - }, - this); - - m_html_preview_action = GUI::Action::create_checkable( - "&HTML Preview", [this](auto&) { - set_preview_mode(PreviewMode::HTML); - }, - this); - - m_preview_actions.add_action(*m_no_preview_action); - m_preview_actions.add_action(*m_markdown_preview_action); - m_preview_actions.add_action(*m_html_preview_action); - m_preview_actions.set_exclusive(true); - - m_layout_toolbar_action = GUI::Action::create_checkable("&Toolbar", [&](auto& action) { - action.is_checked() ? m_toolbar_container->set_visible(true) : m_toolbar_container->set_visible(false); - Config::write_bool("TextEditor"sv, "Layout"sv, "ShowToolbar"sv, action.is_checked()); - }); - auto show_toolbar = Config::read_bool("TextEditor"sv, "Layout"sv, "ShowToolbar"sv, true); - m_layout_toolbar_action->set_checked(show_toolbar); - m_toolbar_container->set_visible(show_toolbar); - - m_layout_statusbar_action = GUI::Action::create_checkable("&Status Bar", [&](auto& action) { - action.is_checked() ? m_statusbar->set_visible(true) : m_statusbar->set_visible(false); - Config::write_bool("TextEditor"sv, "Layout"sv, "ShowStatusbar"sv, action.is_checked()); - update_statusbar(); - }); - auto show_statusbar = Config::read_bool("TextEditor"sv, "Layout"sv, "ShowStatusbar"sv, true); - m_layout_statusbar_action->set_checked(show_statusbar); - m_statusbar->set_visible(show_statusbar); - - m_layout_ruler_action = GUI::Action::create_checkable("&Ruler", [&](auto& action) { - action.is_checked() ? m_editor->set_ruler_visible(true) : m_editor->set_ruler_visible(false); - Config::write_bool("TextEditor"sv, "Layout"sv, "ShowRuler"sv, action.is_checked()); - }); - auto show_ruler = Config::read_bool("TextEditor"sv, "Layout"sv, "ShowRuler"sv, true); - m_layout_ruler_action->set_checked(show_ruler); - m_editor->set_ruler_visible(show_ruler); - - auto view_menu = window.add_menu("&View"_string); - auto layout_menu = view_menu->add_submenu("&Layout"_string); - layout_menu->add_action(*m_layout_toolbar_action); - layout_menu->add_action(*m_layout_statusbar_action); - layout_menu->add_action(*m_layout_ruler_action); - - view_menu->add_separator(); - - view_menu->add_action(GUI::Action::create("Change &Font...", TRY(Gfx::Bitmap::load_from_file("/res/icons/16x16/app-font-editor.png"sv)), - [&](auto&) { - auto picker = GUI::FontPicker::construct(&window, &m_editor->font(), false); - if (picker->exec() == GUI::Dialog::ExecResult::OK) { - dbgln("setting font {}", picker->font()->qualified_name()); - m_editor->set_font(picker->font()); - Config::write_string("TextEditor"sv, "Text"sv, "Font"sv, picker->font()->qualified_name()); - } - })); - - view_menu->add_separator(); - - m_wrapping_mode_actions.set_exclusive(true); - auto wrapping_mode_menu = view_menu->add_submenu("&Wrapping Mode"_string); - m_no_wrapping_action = GUI::Action::create_checkable("&No Wrapping", [&](auto&) { - m_editor->set_wrapping_mode(GUI::TextEditor::WrappingMode::NoWrap); - Config::write_string("TextEditor"sv, "View"sv, "WrappingMode"sv, "None"sv); - }); - m_wrap_anywhere_action = GUI::Action::create_checkable("Wrap &Anywhere", [&](auto&) { - m_editor->set_wrapping_mode(GUI::TextEditor::WrappingMode::WrapAnywhere); - Config::write_string("TextEditor"sv, "View"sv, "WrappingMode"sv, "Anywhere"sv); - }); - m_wrap_at_words_action = GUI::Action::create_checkable("Wrap at &Words", [&](auto&) { - m_editor->set_wrapping_mode(GUI::TextEditor::WrappingMode::WrapAtWords); - Config::write_string("TextEditor"sv, "View"sv, "WrappingMode"sv, "Words"sv); - }); - - m_wrapping_mode_actions.add_action(*m_no_wrapping_action); - m_wrapping_mode_actions.add_action(*m_wrap_anywhere_action); - m_wrapping_mode_actions.add_action(*m_wrap_at_words_action); - - wrapping_mode_menu->add_action(*m_no_wrapping_action); - wrapping_mode_menu->add_action(*m_wrap_anywhere_action); - wrapping_mode_menu->add_action(*m_wrap_at_words_action); - - auto word_wrap = Config::read_string("TextEditor"sv, "View"sv, "WrappingMode"sv, "Words"sv); - if (word_wrap == "None") { - m_no_wrapping_action->set_checked(true); - m_editor->set_wrapping_mode(GUI::TextEditor::WrappingMode::NoWrap); - } else if (word_wrap == "Anywhere") { - m_wrap_anywhere_action->set_checked(true); - m_editor->set_wrapping_mode(GUI::TextEditor::WrappingMode::WrapAnywhere); - } else { - m_wrap_at_words_action->set_checked(true); - m_editor->set_wrapping_mode(GUI::TextEditor::WrappingMode::WrapAtWords); - } - - m_soft_tab_width_actions.set_exclusive(true); - auto soft_tab_width_menu = view_menu->add_submenu("&Tab Width"_string); - m_soft_tab_1_width_action = GUI::Action::create_checkable("1", [&](auto&) { - m_editor->set_soft_tab_width(1); - }); - m_soft_tab_2_width_action = GUI::Action::create_checkable("2", [&](auto&) { - m_editor->set_soft_tab_width(2); - }); - m_soft_tab_4_width_action = GUI::Action::create_checkable("4", [&](auto&) { - m_editor->set_soft_tab_width(4); - }); - m_soft_tab_8_width_action = GUI::Action::create_checkable("8", [&](auto&) { - m_editor->set_soft_tab_width(8); - }); - m_soft_tab_16_width_action = GUI::Action::create_checkable("16", [&](auto&) { - m_editor->set_soft_tab_width(16); - }); - - m_soft_tab_width_actions.add_action(*m_soft_tab_1_width_action); - m_soft_tab_width_actions.add_action(*m_soft_tab_2_width_action); - m_soft_tab_width_actions.add_action(*m_soft_tab_4_width_action); - m_soft_tab_width_actions.add_action(*m_soft_tab_8_width_action); - m_soft_tab_width_actions.add_action(*m_soft_tab_16_width_action); - - soft_tab_width_menu->add_action(*m_soft_tab_1_width_action); - soft_tab_width_menu->add_action(*m_soft_tab_2_width_action); - soft_tab_width_menu->add_action(*m_soft_tab_4_width_action); - soft_tab_width_menu->add_action(*m_soft_tab_8_width_action); - soft_tab_width_menu->add_action(*m_soft_tab_16_width_action); - - m_soft_tab_4_width_action->set_checked(true); - - view_menu->add_separator(); - - m_visualize_trailing_whitespace_action = GUI::Action::create_checkable("T&railing Whitespace", [&](auto&) { - m_editor->set_visualize_trailing_whitespace(m_visualize_trailing_whitespace_action->is_checked()); - }); - m_visualize_leading_whitespace_action = GUI::Action::create_checkable("L&eading Whitespace", [&](auto&) { - m_editor->set_visualize_leading_whitespace(m_visualize_leading_whitespace_action->is_checked()); - }); - - m_visualize_trailing_whitespace_action->set_checked(true); - m_visualize_trailing_whitespace_action->set_status_tip("Visualize trailing whitespace"_string); - m_visualize_leading_whitespace_action->set_status_tip("Visualize leading whitespace"_string); - - view_menu->add_action(*m_visualize_trailing_whitespace_action); - view_menu->add_action(*m_visualize_leading_whitespace_action); - - m_cursor_line_highlighting_action = GUI::Action::create_checkable("L&ine Highlighting", [&](auto&) { - m_editor->set_cursor_line_highlighting(m_cursor_line_highlighting_action->is_checked()); - }); - - m_cursor_line_highlighting_action->set_checked(true); - m_cursor_line_highlighting_action->set_status_tip("Highlight the current line"_string); - - view_menu->add_action(*m_cursor_line_highlighting_action); - - m_relative_line_number_action = GUI::Action::create_checkable("R&elative Line Number", [&](auto& action) { - m_editor->set_relative_line_number(action.is_checked()); - Config::write_bool("TextEditor"sv, "View"sv, "RelativeLineNumber"sv, action.is_checked()); - }); - - auto show_relative_line_number = Config::read_bool("TextEditor"sv, "View"sv, "RelativeLineNumber"sv, false); - m_relative_line_number_action->set_checked(show_relative_line_number); - m_editor->set_relative_line_number(show_relative_line_number); - - m_relative_line_number_action->set_status_tip("Set relative line number"_string); - - view_menu->add_action(*m_relative_line_number_action); - - view_menu->add_separator(); - view_menu->add_action(*m_no_preview_action); - view_menu->add_action(*m_markdown_preview_action); - view_menu->add_action(*m_html_preview_action); - m_no_preview_action->set_checked(true); - view_menu->add_separator(); - - syntax_actions.set_exclusive(true); - - auto syntax_menu = view_menu->add_submenu("&Syntax"_string); - m_plain_text_highlight = GUI::Action::create_checkable("&Plain Text", [&](auto&) { - m_statusbar->set_text(1, "Plain Text"_string); - m_editor->set_syntax_highlighter({}); - m_editor->update(); - }); - m_plain_text_highlight->set_checked(true); - m_statusbar->set_text(1, "Plain Text"_string); - syntax_actions.add_action(*m_plain_text_highlight); - syntax_menu->add_action(*m_plain_text_highlight); - - m_cpp_highlight = GUI::Action::create_checkable("&C++", [&](auto&) { - m_editor->set_syntax_highlighter(make()); - m_editor->update(); - }); - syntax_actions.add_action(*m_cpp_highlight); - syntax_menu->add_action(*m_cpp_highlight); - - m_cmake_highlight = GUI::Action::create_checkable("C&Make", [&](auto&) { - m_editor->set_syntax_highlighter(make()); - m_editor->update(); - }); - syntax_actions.add_action(*m_cmake_highlight); - syntax_menu->add_action(*m_cmake_highlight); - - m_cmakecache_highlight = GUI::Action::create_checkable("CM&akeCache", [&](auto&) { - m_editor->set_syntax_highlighter(make()); - m_editor->update(); - }); - syntax_actions.add_action(*m_cmakecache_highlight); - syntax_menu->add_action(*m_cmakecache_highlight); - - m_js_highlight = GUI::Action::create_checkable("&JavaScript", [&](auto&) { - m_editor->set_syntax_highlighter(make()); - m_editor->update(); - }); - syntax_actions.add_action(*m_js_highlight); - syntax_menu->add_action(*m_js_highlight); - - m_css_highlight = GUI::Action::create_checkable("C&SS", [&](auto&) { - m_editor->set_syntax_highlighter(make()); - m_editor->update(); - }); - syntax_actions.add_action(*m_css_highlight); - syntax_menu->add_action(*m_css_highlight); - - m_html_highlight = GUI::Action::create_checkable("&HTML File", [&](auto&) { - m_editor->set_syntax_highlighter(make()); - m_editor->update(); - }); - syntax_actions.add_action(*m_html_highlight); - syntax_menu->add_action(*m_html_highlight); - - m_git_highlight = GUI::Action::create_checkable("Gi&t Commit", [&](auto&) { - m_editor->set_syntax_highlighter(make()); - m_editor->update(); - }); - syntax_actions.add_action(*m_git_highlight); - syntax_menu->add_action(*m_git_highlight); - - m_gml_highlight = GUI::Action::create_checkable("&GML", [&](auto&) { - m_editor->set_syntax_highlighter(make()); - m_editor->update(); - }); - syntax_actions.add_action(*m_gml_highlight); - syntax_menu->add_action(*m_gml_highlight); - - m_ini_highlight = GUI::Action::create_checkable("&INI File", [&](auto&) { - m_editor->set_syntax_highlighter(make()); - m_editor->update(); - }); - syntax_actions.add_action(*m_ini_highlight); - syntax_menu->add_action(*m_ini_highlight); - - m_markdown_highlight = GUI::Action::create_checkable("Ma&rkdown", [&](auto&) { - m_editor->set_syntax_highlighter(make()); - m_editor->update(); - }); - syntax_actions.add_action(*m_markdown_highlight); - syntax_menu->add_action(*m_markdown_highlight); - - m_shell_highlight = GUI::Action::create_checkable("Sh&ell File", [&](auto&) { - m_editor->set_syntax_highlighter(make()); - m_editor->update(); - }); - syntax_actions.add_action(*m_shell_highlight); - syntax_menu->add_action(*m_shell_highlight); - - m_sql_highlight = GUI::Action::create_checkable("S&QL File", [&](auto&) { - m_editor->set_syntax_highlighter(make()); - m_editor->update(); - }); - syntax_actions.add_action(*m_sql_highlight); - syntax_menu->add_action(*m_sql_highlight); - - view_menu->add_separator(); - view_menu->add_action(GUI::CommonActions::make_fullscreen_action([&](auto&) { - window.set_fullscreen(!window.is_fullscreen()); - })); - - auto help_menu = window.add_menu("&Help"_string); - help_menu->add_action(GUI::CommonActions::make_command_palette_action(&window)); - help_menu->add_action(GUI::CommonActions::make_help_action([](auto&) { - Desktop::Launcher::open(URL::create_with_file_scheme("/usr/share/man/man1/Applications/TextEditor.md"), "/bin/Help"); - })); - help_menu->add_action(GUI::CommonActions::make_about_action("Text Editor"_string, GUI::Icon::default_icon("app-text-editor"sv), &window)); - - auto wrapping_statusbar_menu = m_line_column_statusbar_menu->add_submenu("&Wrapping Mode"_string); - wrapping_statusbar_menu->add_action(*m_no_wrapping_action); - wrapping_statusbar_menu->add_action(*m_wrap_anywhere_action); - wrapping_statusbar_menu->add_action(*m_wrap_at_words_action); - - auto tab_width_statusbar_menu = m_line_column_statusbar_menu->add_submenu("&Tab Width"_string); - tab_width_statusbar_menu->add_action(*m_soft_tab_1_width_action); - tab_width_statusbar_menu->add_action(*m_soft_tab_2_width_action); - tab_width_statusbar_menu->add_action(*m_soft_tab_4_width_action); - tab_width_statusbar_menu->add_action(*m_soft_tab_8_width_action); - tab_width_statusbar_menu->add_action(*m_soft_tab_16_width_action); - - m_line_column_statusbar_menu->add_separator(); - m_line_column_statusbar_menu->add_action(*m_cursor_line_highlighting_action); - - m_syntax_statusbar_menu->add_action(*m_plain_text_highlight); - m_syntax_statusbar_menu->add_action(*m_cpp_highlight); - m_syntax_statusbar_menu->add_action(*m_cmake_highlight); - m_syntax_statusbar_menu->add_action(*m_cmakecache_highlight); - m_syntax_statusbar_menu->add_action(*m_css_highlight); - m_syntax_statusbar_menu->add_action(*m_git_highlight); - m_syntax_statusbar_menu->add_action(*m_gml_highlight); - m_syntax_statusbar_menu->add_action(*m_html_highlight); - m_syntax_statusbar_menu->add_action(*m_ini_highlight); - m_syntax_statusbar_menu->add_action(*m_js_highlight); - m_syntax_statusbar_menu->add_action(*m_markdown_highlight); - m_syntax_statusbar_menu->add_action(*m_shell_highlight); - m_syntax_statusbar_menu->add_action(*m_sql_highlight); - - return {}; -} - -void MainWidget::set_path(StringView path) -{ - if (path.is_empty()) { - m_path = {}; - m_name = {}; - m_extension = {}; - } else { - auto lexical_path = LexicalPath(path); - m_path = lexical_path.string(); - m_name = lexical_path.title(); - m_extension = lexical_path.extension(); - } - - if (m_extension == "c" || m_extension == "cc" || m_extension == "cxx" || m_extension == "cpp" || m_extension == "c++" - || m_extension == "h" || m_extension == "hh" || m_extension == "hxx" || m_extension == "hpp" || m_extension == "h++") { - m_cpp_highlight->activate(); - } else if (m_extension == "cmake" || (m_extension == "txt" && m_name == "CMakeLists")) { - m_cmake_highlight->activate(); - } else if (m_extension == "txt" && m_name == "CMakeCache") { - m_cmakecache_highlight->activate(); - } else if (m_extension == "js" || m_extension == "mjs" || m_extension == "json") { - m_js_highlight->activate(); - } else if (m_name == "COMMIT_EDITMSG") { - m_git_highlight->activate(); - } else if (m_extension == "gml") { - m_gml_highlight->activate(); - } else if (m_extension == "ini" || m_extension == "af") { - m_ini_highlight->activate(); - } else if (m_extension == "md") { - m_markdown_highlight->activate(); - } else if (m_extension == "sh" || m_extension == "bash") { - m_shell_highlight->activate(); - } else if (m_extension == "sql") { - m_sql_highlight->activate(); - } else if (m_extension == "html" || m_extension == "htm") { - m_html_highlight->activate(); - } else if (m_extension == "css") { - m_css_highlight->activate(); - } else { - m_plain_text_highlight->activate(); - } - - if (m_auto_detect_preview_mode) { - if (m_extension == "md") - set_preview_mode(PreviewMode::Markdown); - else if (m_extension == "html" || m_extension == "htm") - set_preview_mode(PreviewMode::HTML); - else - set_preview_mode(PreviewMode::None); - } - - m_open_folder_action->set_enabled(!path.is_empty()); - update_title(); -} - -void MainWidget::update_title() -{ - StringBuilder builder; - if (m_path.is_empty()) - builder.append("Untitled"sv); - else - builder.append(m_path); - builder.append("[*] - Text Editor"sv); - window()->set_title(builder.to_byte_string()); -} - -ErrorOr MainWidget::read_file(ByteString const& filename, Core::File& file) -{ - m_editor->set_text(TRY(file.read_until_eof())); - set_path(filename); - GUI::Application::the()->set_most_recently_open_file(filename); - m_editor->set_focus(true); - return {}; -} - -void MainWidget::open_nonexistent_file(ByteString const& path) -{ - m_editor->set_text({}); - set_path(path); - m_editor->set_focus(true); -} - -bool MainWidget::request_close() -{ - if (!editor().document().is_modified()) - return true; - auto result = GUI::MessageBox::ask_about_unsaved_changes(window(), m_path, editor().document().undo_stack().last_unmodified_timestamp()); - - if (result == GUI::MessageBox::ExecResult::Yes) { - m_save_action->activate(); - if (editor().document().is_modified()) - return false; - return true; - } - - if (result == GUI::MessageBox::ExecResult::No) - return true; - - return false; -} - -void MainWidget::drag_enter_event(GUI::DragEvent& event) -{ - auto const& mime_types = event.mime_types(); - if (mime_types.contains_slow("text/uri-list"sv)) - event.accept(); -} - -void MainWidget::drop_event(GUI::DropEvent& event) -{ - event.accept(); - window()->move_to_front(); - - if (event.mime_data().has_urls()) { - auto urls = event.mime_data().urls(); - if (urls.is_empty()) - return; - if (urls.size() > 1) { - GUI::MessageBox::show(window(), "TextEditor can only open one file at a time!"sv, "One at a time please!"sv, GUI::MessageBox::Type::Error); - return; - } - if (!request_close()) - return; - - auto response = FileSystemAccessClient::Client::the().request_file_read_only_approved(window(), urls.first().serialize_path()); - if (response.is_error()) - return; - if (auto result = read_file(response.value().filename(), response.value().stream()); result.is_error()) - GUI::MessageBox::show(window(), "Unable to open file.\n"sv, "Error"sv, GUI::MessageBox::Type::Error); - } -} - -void MainWidget::set_web_view_visible(bool visible) -{ - if (!visible && !m_page_view) - return; - ensure_web_view(); - auto& web_view_container = *find_descendant_of_type_named("web_view_container"); - web_view_container.set_visible(visible); -} - -void MainWidget::set_preview_mode(PreviewMode mode) -{ - if (m_preview_mode == mode) - return; - m_preview_mode = mode; - - if (m_preview_mode == PreviewMode::HTML) { - m_html_preview_action->set_checked(true); - set_web_view_visible(true); - update_html_preview(); - } else if (m_preview_mode == PreviewMode::Markdown) { - m_markdown_preview_action->set_checked(true); - set_web_view_visible(true); - update_markdown_preview(); - } else { - m_no_preview_action->set_checked(true); - set_web_view_visible(false); - } -} - -void MainWidget::update_preview() -{ - switch (m_preview_mode) { - case PreviewMode::Markdown: - update_markdown_preview(); - break; - case PreviewMode::HTML: - update_html_preview(); - break; - default: - break; - } -} - -void MainWidget::update_markdown_preview() -{ - auto document = Markdown::Document::parse(m_editor->text()); - if (document) { - auto html = document->render_to_html(); - auto current_scroll_pos = m_page_view->visible_content_rect(); - m_page_view->load_html(html); - m_page_view->scroll_into_view(current_scroll_pos, true, true); - } -} - -void MainWidget::update_html_preview() -{ - auto current_scroll_pos = m_page_view->visible_content_rect(); - m_page_view->load_html(m_editor->text()); - m_page_view->scroll_into_view(current_scroll_pos, true, true); -} - -void MainWidget::update_statusbar() -{ - if (!m_statusbar->is_visible()) - return; - - StringBuilder builder; - if (m_editor->has_selection()) { - ByteString selected_text = m_editor->selected_text(); - auto word_count = m_editor->number_of_selected_words(); - builder.appendff("{:'d} {} ({:'d} {}) selected", selected_text.length(), selected_text.length() == 1 ? "character" : "characters", word_count, word_count != 1 ? "words" : "word"); - } else { - ByteString text = m_editor->text(); - auto word_count = m_editor->number_of_words(); - builder.appendff("{:'d} {} ({:'d} {})", text.length(), text.length() == 1 ? "character" : "characters", word_count, word_count != 1 ? "words" : "word"); - } - m_statusbar->set_text(0, builder.to_string().release_value_but_fixme_should_propagate_errors()); - - if (m_editor && m_editor->syntax_highlighter()) { - auto language = m_editor->syntax_highlighter()->language(); - m_statusbar->set_text(1, String::from_utf8(Syntax::language_to_string(language)).release_value_but_fixme_should_propagate_errors()); - } - m_statusbar->set_text(2, String::formatted("Ln {:'d} Col {:'d}", m_editor->cursor().line() + 1, m_editor->cursor().column()).release_value_but_fixme_should_propagate_errors()); -} - -void MainWidget::find_text(GUI::TextEditor::SearchDirection direction, ShowMessageIfNoResults show_message) -{ - auto needle = m_find_textbox->text(); - if (needle.is_empty()) - return; - if (m_use_regex) - m_editor->document().update_regex_matches(needle); - - auto result = m_editor->find_text(needle, direction, - m_should_wrap ? GUI::TextDocument::SearchShouldWrap::Yes : GUI::TextDocument::SearchShouldWrap::No, - m_use_regex, m_match_case); - - if (!result.is_valid() && show_message == ShowMessageIfNoResults::Yes) { - GUI::MessageBox::show(window(), - ByteString::formatted("Not found: \"{}\"", needle), - "Not found"sv, - GUI::MessageBox::Type::Information); - } -} - -} diff --git a/Userland/Applications/TextEditor/MainWidget.h b/Userland/Applications/TextEditor/MainWidget.h deleted file mode 100644 index a8f29f558dd..00000000000 --- a/Userland/Applications/TextEditor/MainWidget.h +++ /dev/null @@ -1,153 +0,0 @@ -/* - * Copyright (c) 2018-2021, Andreas Kling - * Copyright (c) 2022, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace TextEditor { - -class MainWidget final : public GUI::Widget { - C_OBJECT(MainWidget); - -public: - virtual ~MainWidget() override = default; - ErrorOr read_file(ByteString const& filename, Core::File&); - void open_nonexistent_file(ByteString const& path); - bool request_close(); - - GUI::TextEditor& editor() { return *m_editor; } - - enum class PreviewMode { - None, - Markdown, - HTML, - }; - - void set_preview_mode(PreviewMode); - void set_auto_detect_preview_mode(bool value) { m_auto_detect_preview_mode = value; } - - void update_title(); - void update_statusbar(); - ErrorOr initialize_menubar(GUI::Window&); - -private: - MainWidget(); - void set_path(StringView); - void update_preview(); - void update_markdown_preview(); - void update_html_preview(); - - WebView::OutOfProcessWebView& ensure_web_view(); - void set_web_view_visible(bool); - - virtual void drag_enter_event(GUI::DragEvent&) override; - virtual void drop_event(GUI::DropEvent&) override; - - enum class ShowMessageIfNoResults { - Yes = 1, - No = 0 - }; - void find_text(GUI::TextEditor::SearchDirection, ShowMessageIfNoResults); - - RefPtr m_editor; - ByteString m_path; - ByteString m_name; - ByteString m_extension; - RefPtr m_new_action; - RefPtr m_open_action; - RefPtr m_save_action; - RefPtr m_save_as_action; - RefPtr m_open_folder_action; - RefPtr m_find_replace_action; - RefPtr m_vim_emulation_setting_action; - - RefPtr m_find_next_action; - RefPtr m_find_previous_action; - RefPtr m_replace_action; - RefPtr m_replace_all_action; - - RefPtr m_layout_toolbar_action; - RefPtr m_layout_statusbar_action; - RefPtr m_layout_ruler_action; - - GUI::ActionGroup m_preview_actions; - RefPtr m_no_preview_action; - RefPtr m_markdown_preview_action; - RefPtr m_html_preview_action; - - RefPtr m_toolbar; - RefPtr m_toolbar_container; - RefPtr m_statusbar; - RefPtr m_line_column_statusbar_menu; - RefPtr m_syntax_statusbar_menu; - - RefPtr m_find_textbox; - RefPtr m_replace_textbox; - RefPtr m_find_previous_button; - RefPtr m_find_next_button; - RefPtr m_replace_button; - RefPtr m_replace_all_button; - RefPtr m_find_replace_widget; - RefPtr m_find_widget; - RefPtr m_replace_widget; - RefPtr m_regex_checkbox; - RefPtr m_match_case_checkbox; - RefPtr m_wrap_around_checkbox; - - GUI::ActionGroup m_wrapping_mode_actions; - RefPtr m_no_wrapping_action; - RefPtr m_wrap_anywhere_action; - RefPtr m_wrap_at_words_action; - - RefPtr m_visualize_trailing_whitespace_action; - RefPtr m_visualize_leading_whitespace_action; - RefPtr m_cursor_line_highlighting_action; - RefPtr m_relative_line_number_action; - - GUI::ActionGroup m_soft_tab_width_actions; - RefPtr m_soft_tab_1_width_action; - RefPtr m_soft_tab_2_width_action; - RefPtr m_soft_tab_4_width_action; - RefPtr m_soft_tab_8_width_action; - RefPtr m_soft_tab_16_width_action; - - GUI::ActionGroup syntax_actions; - RefPtr m_plain_text_highlight; - RefPtr m_cmake_highlight; - RefPtr m_cmakecache_highlight; - RefPtr m_cpp_highlight; - RefPtr m_css_highlight; - RefPtr m_js_highlight; - RefPtr m_html_highlight; - RefPtr m_git_highlight; - RefPtr m_gml_highlight; - RefPtr m_ini_highlight; - RefPtr m_markdown_highlight; - RefPtr m_shell_highlight; - RefPtr m_sql_highlight; - - RefPtr m_page_view; - - bool m_auto_detect_preview_mode { false }; - bool m_use_regex { false }; - bool m_match_case { true }; - bool m_should_wrap { true }; - - PreviewMode m_preview_mode { PreviewMode::None }; -}; - -} diff --git a/Userland/Applications/TextEditor/TextEditorWindow.gml b/Userland/Applications/TextEditor/TextEditorWindow.gml deleted file mode 100644 index 1a1c3b1d85d..00000000000 --- a/Userland/Applications/TextEditor/TextEditorWindow.gml +++ /dev/null @@ -1,111 +0,0 @@ -@GUI::Widget { - name: "main" - fill_with_background_color: true - layout: @GUI::VerticalBoxLayout { - spacing: 2 - } - - @GUI::ToolbarContainer { - name: "toolbar_container" - - @GUI::Toolbar { - name: "toolbar" - } - } - - @GUI::HorizontalSplitter { - opportunistic_resizee: "First" - - @GUI::TextEditor { - name: "editor" - } - - @GUI::Widget { - name: "web_view_container" - visible: false - layout: @GUI::VerticalBoxLayout {} - } - } - - @GUI::GroupBox { - name: "find_replace_widget" - visible: false - fill_with_background_color: true - fixed_height: 56 - layout: @GUI::VerticalBoxLayout { - spacing: 2 - margins: [3] - } - - @GUI::Widget { - name: "find_widget" - fill_with_background_color: true - fixed_height: 22 - layout: @GUI::HorizontalBoxLayout { - spacing: 4 - } - - @GUI::Button { - name: "find_previous_button" - fixed_width: 38 - } - - @GUI::Button { - name: "find_next_button" - fixed_width: 38 - } - - @GUI::TextBox { - name: "find_textbox" - } - - @GUI::CheckBox { - name: "regex_checkbox" - text: "Use RegEx" - fixed_width: 80 - } - - @GUI::CheckBox { - name: "match_case_checkbox" - text: "Match case" - fixed_width: 85 - } - } - - @GUI::Widget { - name: "replace_widget" - fill_with_background_color: true - fixed_height: 22 - layout: @GUI::HorizontalBoxLayout { - spacing: 4 - } - - @GUI::Button { - name: "replace_button" - text: "Replace" - fixed_width: 80 - } - - @GUI::TextBox { - name: "replace_textbox" - } - - @GUI::Button { - name: "replace_all_button" - text: "Replace all" - fixed_width: 80 - } - - @GUI::CheckBox { - name: "wrap_around_checkbox" - text: "Wrap around" - fixed_width: 85 - } - } - } - - @GUI::Statusbar { - name: "statusbar" - segment_count: 3 - } -} diff --git a/Userland/Applications/TextEditor/main.cpp b/Userland/Applications/TextEditor/main.cpp deleted file mode 100644 index a57a08e5248..00000000000 --- a/Userland/Applications/TextEditor/main.cpp +++ /dev/null @@ -1,98 +0,0 @@ -/* - * Copyright (c) 2018-2021, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "FileArgument.h" -#include "MainWidget.h" -#include -#include -#include -#include -#include -#include -#include - -using namespace TextEditor; - -ErrorOr serenity_main(Main::Arguments arguments) -{ - TRY(Core::System::pledge("stdio recvfd sendfd thread rpath cpath wpath unix")); - - auto app = TRY(GUI::Application::create(arguments)); - - Config::pledge_domain("TextEditor"); - - app->set_config_domain("TextEditor"_string); - - auto preview_mode = "auto"sv; - StringView file_to_edit; - Core::ArgsParser parser; - parser.add_option(preview_mode, "Preview mode, one of 'none', 'html', 'markdown', 'auto'", "preview-mode", 'p', "mode"); - parser.add_positional_argument(file_to_edit, "File to edit, with optional starting line and column number", "file[:line[:column]]", Core::ArgsParser::Required::No); - parser.parse(arguments); - - TRY(Core::System::unveil("/res", "r")); - TRY(Core::System::unveil("/tmp/session/%sid/portal/launch", "rw")); - TRY(Core::System::unveil("/tmp/session/%sid/portal/webcontent", "rw")); - TRY(Core::System::unveil("/tmp/session/%sid/portal/filesystemaccess", "rw")); - TRY(Core::System::unveil(nullptr, nullptr)); - - auto app_icon = GUI::Icon::default_icon("app-text-editor"sv); - - auto window = GUI::Window::construct(); - window->restore_size_and_position("TextEditor"sv, "Window"sv, { { 640, 400 } }); - window->save_size_and_position_on_close("TextEditor"sv, "Window"sv); - - auto text_widget = window->set_main_widget(); - - text_widget->editor().set_focus(true); - - window->on_close_request = [&]() -> GUI::Window::CloseRequestDecision { - if (text_widget->request_close()) - return GUI::Window::CloseRequestDecision::Close; - return GUI::Window::CloseRequestDecision::StayOpen; - }; - - if (preview_mode == "auto") { - text_widget->set_auto_detect_preview_mode(true); - } else if (preview_mode == "markdown") { - text_widget->set_preview_mode(MainWidget::PreviewMode::Markdown); - } else if (preview_mode == "html") { - text_widget->set_preview_mode(MainWidget::PreviewMode::HTML); - } else if (preview_mode == "none") { - text_widget->set_preview_mode(MainWidget::PreviewMode::None); - } else { - warnln("Invalid mode '{}'", preview_mode); - return 1; - } - - TRY(text_widget->initialize_menubar(*window)); - text_widget->update_title(); - - window->show(); - window->set_icon(app_icon.bitmap_for_size(16)); - - if (!file_to_edit.is_empty()) { - auto filename = TRY(String::from_utf8(file_to_edit)); - FileArgument parsed_argument(filename); - - FileSystemAccessClient::Client::the().set_silence_errors(FileSystemAccessClient::ErrorFlag::NoEntries); - auto response = FileSystemAccessClient::Client::the().request_file_read_only_approved(window, parsed_argument.filename().to_byte_string()); - - if (response.is_error()) { - if (response.error().code() == ENOENT) - text_widget->open_nonexistent_file(parsed_argument.filename().to_byte_string()); - } else { - TRY(text_widget->read_file(response.value().filename(), response.value().stream())); - text_widget->editor().set_cursor_and_focus_line(parsed_argument.line().value_or(1) - 1, parsed_argument.column().value_or(0)); - } - - text_widget->update_title(); - FileSystemAccessClient::Client::the().set_silence_errors(FileSystemAccessClient::ErrorFlag::None); - } - text_widget->update_statusbar(); - - return app->exec(); -} diff --git a/Userland/Applications/ThemeEditor/AlignmentProperty.gml b/Userland/Applications/ThemeEditor/AlignmentProperty.gml deleted file mode 100644 index 235bb90a26a..00000000000 --- a/Userland/Applications/ThemeEditor/AlignmentProperty.gml +++ /dev/null @@ -1,18 +0,0 @@ -@GUI::Frame { - layout: @GUI::HorizontalBoxLayout { - spacing: 4 - } - preferred_height: "fit" - - @GUI::Label { - name: "name" - text: "Some alignment" - text_alignment: "CenterLeft" - fixed_width: 200 - } - - @GUI::ComboBox { - name: "combo_box" - model_only: true - } -} diff --git a/Userland/Applications/ThemeEditor/CMakeLists.txt b/Userland/Applications/ThemeEditor/CMakeLists.txt deleted file mode 100644 index acbcb9927fa..00000000000 --- a/Userland/Applications/ThemeEditor/CMakeLists.txt +++ /dev/null @@ -1,31 +0,0 @@ -serenity_component( - ThemeEditor - TARGETS ThemeEditor -) - -stringify_gml(ThemeEditor.gml ThemeEditorGML.h theme_editor_gml) -stringify_gml(AlignmentProperty.gml AlignmentPropertyGML.h alignment_property_gml) -stringify_gml(ColorProperty.gml ColorPropertyGML.h color_property_gml) -stringify_gml(FlagProperty.gml FlagPropertyGML.h flag_property_gml) -stringify_gml(MetricProperty.gml MetricPropertyGML.h metric_property_gml) -stringify_gml(PathProperty.gml PathPropertyGML.h path_property_gml) -stringify_gml(Previews/WindowPreview.gml WindowPreviewGML.h window_preview_gml) - -set(SOURCES - MainWidget.cpp - PreviewWidget.cpp - main.cpp -) - -set(GENERATED_SOURCES - AlignmentPropertyGML.h - ColorPropertyGML.h - FlagPropertyGML.h - MetricPropertyGML.h - PathPropertyGML.h - ThemeEditorGML.h - WindowPreviewGML.h -) - -serenity_app(ThemeEditor ICON app-theme-editor) -target_link_libraries(ThemeEditor PRIVATE LibConfig LibCore LibGfx LibGUI LibFileSystem LibFileSystemAccessClient LibIPC LibMain LibURL) diff --git a/Userland/Applications/ThemeEditor/ColorProperty.gml b/Userland/Applications/ThemeEditor/ColorProperty.gml deleted file mode 100644 index 975317bc184..00000000000 --- a/Userland/Applications/ThemeEditor/ColorProperty.gml +++ /dev/null @@ -1,17 +0,0 @@ -@GUI::Frame { - layout: @GUI::HorizontalBoxLayout { - spacing: 4 - } - preferred_height: "fit" - - @GUI::Label { - name: "name" - text: "Some color" - text_alignment: "CenterLeft" - fixed_width: 200 - } - - @GUI::ColorInput { - name: "color_input" - } -} diff --git a/Userland/Applications/ThemeEditor/FlagProperty.gml b/Userland/Applications/ThemeEditor/FlagProperty.gml deleted file mode 100644 index ab71d41dfe7..00000000000 --- a/Userland/Applications/ThemeEditor/FlagProperty.gml +++ /dev/null @@ -1,12 +0,0 @@ -@GUI::Frame { - layout: @GUI::HorizontalBoxLayout { - spacing: 4 - } - preferred_height: "fit" - - @GUI::CheckBox { - name: "checkbox" - text: "Some flag" - checkbox_position: "Right" - } -} diff --git a/Userland/Applications/ThemeEditor/MainWidget.cpp b/Userland/Applications/ThemeEditor/MainWidget.cpp deleted file mode 100644 index 1e83a57e21d..00000000000 --- a/Userland/Applications/ThemeEditor/MainWidget.cpp +++ /dev/null @@ -1,716 +0,0 @@ -/* - * Copyright (c) 2020, Andreas Kling - * Copyright (c) 2021-2022, networkException - * Copyright (c) 2021-2023, Sam Atkins - * Copyright (c) 2021, Antonio Di Stefano - * Copyright (c) 2022, Filiph Sandström - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "MainWidget.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace ThemeEditor { - -static PropertyTab const window_tab { - "Windows"sv, - { - { "General", - { { Gfx::FlagRole::IsDark }, - { Gfx::AlignmentRole::TitleAlignment }, - { Gfx::MetricRole::TitleHeight }, - { Gfx::MetricRole::TitleButtonWidth }, - { Gfx::MetricRole::TitleButtonHeight }, - { Gfx::PathRole::TitleButtonIcons }, - { Gfx::FlagRole::TitleButtonsIconOnly } } }, - - { "Border", - { { Gfx::MetricRole::BorderThickness }, - { Gfx::MetricRole::BorderRadius } } }, - - { "Active Window", - { { Gfx::ColorRole::ActiveWindowBorder1 }, - { Gfx::ColorRole::ActiveWindowBorder2 }, - { Gfx::ColorRole::ActiveWindowTitle }, - { Gfx::ColorRole::ActiveWindowTitleShadow }, - { Gfx::ColorRole::ActiveWindowTitleStripes }, - { Gfx::PathRole::ActiveWindowShadow } } }, - - { "Inactive Window", - { { Gfx::ColorRole::InactiveWindowBorder1 }, - { Gfx::ColorRole::InactiveWindowBorder2 }, - { Gfx::ColorRole::InactiveWindowTitle }, - { Gfx::ColorRole::InactiveWindowTitleShadow }, - { Gfx::ColorRole::InactiveWindowTitleStripes }, - { Gfx::PathRole::InactiveWindowShadow } } }, - - { "Highlighted Window", - { { Gfx::ColorRole::HighlightWindowBorder1 }, - { Gfx::ColorRole::HighlightWindowBorder2 }, - { Gfx::ColorRole::HighlightWindowTitle }, - { Gfx::ColorRole::HighlightWindowTitleShadow }, - { Gfx::ColorRole::HighlightWindowTitleStripes } } }, - - { "Moving Window", - { { Gfx::ColorRole::MovingWindowBorder1 }, - { Gfx::ColorRole::MovingWindowBorder2 }, - { Gfx::ColorRole::MovingWindowTitle }, - { Gfx::ColorRole::MovingWindowTitleShadow }, - { Gfx::ColorRole::MovingWindowTitleStripes } } }, - - { "Contents", - { { Gfx::ColorRole::Window }, - { Gfx::ColorRole::WindowText } } }, - - { "Desktop", - { { Gfx::ColorRole::DesktopBackground }, - { Gfx::PathRole::TaskbarShadow } } }, - } -}; - -static PropertyTab const widgets_tab { - "Widgets"sv, - { - { "General", - { { Gfx::ColorRole::Accent }, - { Gfx::ColorRole::Base }, - { Gfx::ColorRole::ThreedHighlight }, - { Gfx::ColorRole::ThreedShadow1 }, - { Gfx::ColorRole::ThreedShadow2 }, - { Gfx::ColorRole::HoverHighlight } } }, - - { "Text", - { { Gfx::ColorRole::BaseText }, - { Gfx::ColorRole::DisabledTextFront }, - { Gfx::ColorRole::DisabledTextBack }, - { Gfx::ColorRole::PlaceholderText } } }, - - { "Links", - { { Gfx::ColorRole::Link }, - { Gfx::ColorRole::ActiveLink }, - { Gfx::ColorRole::VisitedLink } } }, - - { "Buttons", - { { Gfx::ColorRole::Button }, - { Gfx::ColorRole::ButtonText } } }, - - { "Tooltips", - { { Gfx::ColorRole::Tooltip }, - { Gfx::ColorRole::TooltipText }, - { Gfx::PathRole::TooltipShadow } } }, - - { "Trays", - { { Gfx::ColorRole::Tray }, - { Gfx::ColorRole::TrayText } } }, - - { "Ruler", - { { Gfx::ColorRole::Ruler }, - { Gfx::ColorRole::RulerBorder }, - { Gfx::ColorRole::RulerActiveText }, - { Gfx::ColorRole::RulerInactiveText } } }, - - { "Gutter", - { { Gfx::ColorRole::Gutter }, - { Gfx::ColorRole::GutterBorder } } }, - - { "Rubber Band", - { { Gfx::ColorRole::RubberBandBorder }, - { Gfx::ColorRole::RubberBandFill } } }, - - { "Menus", - { { Gfx::ColorRole::MenuBase }, - { Gfx::ColorRole::MenuBaseText }, - { Gfx::ColorRole::MenuSelection }, - { Gfx::ColorRole::MenuSelectionText }, - { Gfx::ColorRole::MenuStripe }, - { Gfx::PathRole::MenuShadow } } }, - - { "Selection", - { { Gfx::ColorRole::FocusOutline }, - { Gfx::ColorRole::TextCursor }, - { Gfx::ColorRole::Selection }, - { Gfx::ColorRole::SelectionText }, - { Gfx::ColorRole::InactiveSelection }, - { Gfx::ColorRole::InactiveSelectionText }, - { Gfx::ColorRole::HighlightSearching }, - { Gfx::ColorRole::HighlightSearchingText } } }, - } -}; - -static PropertyTab const syntax_highlighting_tab { - "Syntax Highlighting"sv, - { - { "General", - { { Gfx::ColorRole::SyntaxComment }, - { Gfx::ColorRole::SyntaxControlKeyword }, - { Gfx::ColorRole::SyntaxIdentifier }, - { Gfx::ColorRole::SyntaxKeyword }, - { Gfx::ColorRole::SyntaxNumber }, - { Gfx::ColorRole::SyntaxOperator }, - { Gfx::ColorRole::SyntaxPreprocessorStatement }, - { Gfx::ColorRole::SyntaxPreprocessorValue }, - { Gfx::ColorRole::SyntaxPunctuation }, - { Gfx::ColorRole::SyntaxString }, - { Gfx::ColorRole::SyntaxType }, - { Gfx::ColorRole::SyntaxFunction }, - { Gfx::ColorRole::SyntaxVariable }, - { Gfx::ColorRole::SyntaxCustomType }, - { Gfx::ColorRole::SyntaxNamespace }, - { Gfx::ColorRole::SyntaxMember }, - { Gfx::ColorRole::SyntaxParameter } } }, - } -}; - -static PropertyTab const color_scheme_tab { - "Color Scheme"sv, - { - { "General", - { { Gfx::FlagRole::BoldTextAsBright }, - { Gfx::ColorRole::Black }, - { Gfx::ColorRole::Red }, - { Gfx::ColorRole::Green }, - { Gfx::ColorRole::Yellow }, - { Gfx::ColorRole::Blue }, - { Gfx::ColorRole::Magenta }, - { Gfx::ColorRole::ColorSchemeBackground }, - { Gfx::ColorRole::ColorSchemeForeground }, - { Gfx::ColorRole::Cyan }, - { Gfx::ColorRole::White }, - { Gfx::ColorRole::BrightBlack }, - { Gfx::ColorRole::BrightRed }, - { Gfx::ColorRole::BrightGreen }, - { Gfx::ColorRole::BrightYellow }, - { Gfx::ColorRole::BrightBlue }, - { Gfx::ColorRole::BrightMagenta }, - { Gfx::ColorRole::BrightCyan }, - { Gfx::ColorRole::BrightWhite } } }, - } -}; - -ErrorOr> MainWidget::try_create() -{ - auto alignment_model = TRY(AlignmentModel::try_create()); - - auto main_widget = TRY(adopt_nonnull_ref_or_enomem(new (nothrow) MainWidget(move(alignment_model)))); - - TRY(main_widget->load_from_gml(theme_editor_gml)); - main_widget->m_preview_widget = main_widget->find_descendant_of_type_named("preview_widget"); - main_widget->m_property_tabs = main_widget->find_descendant_of_type_named("property_tabs"); - main_widget->m_statusbar = main_widget->find_descendant_of_type_named("statusbar"); - - TRY(main_widget->add_property_tab(window_tab)); - TRY(main_widget->add_property_tab(widgets_tab)); - TRY(main_widget->add_property_tab(syntax_highlighting_tab)); - TRY(main_widget->add_property_tab(color_scheme_tab)); - - main_widget->build_override_controls(); - - return main_widget; -} - -MainWidget::MainWidget(NonnullRefPtr alignment_model) - : m_current_palette(GUI::Application::the()->palette()) - , m_alignment_model(move(alignment_model)) -{ - GUI::Application::the()->on_action_enter = [this](GUI::Action& action) { - m_statusbar->set_override_text(action.status_tip()); - }; - GUI::Application::the()->on_action_leave = [this](GUI::Action&) { - m_statusbar->set_override_text({}); - }; -} - -ErrorOr MainWidget::initialize_menubar(GUI::Window& window) -{ - auto file_menu = window.add_menu("&File"_string); - file_menu->add_action(GUI::CommonActions::make_open_action([&](auto&) { - if (request_close() == GUI::Window::CloseRequestDecision::StayOpen) - return; - FileSystemAccessClient::OpenFileOptions options { - .window_title = "Select Theme"sv, - .path = "/res/themes"sv, - .allowed_file_types = Vector { { "Theme Files", { { "ini" } } }, GUI::FileTypeFilter::all_files() }, - }; - auto response = FileSystemAccessClient::Client::the().open_file(&window, options); - if (response.is_error()) - return; - auto load_from_file_result = load_from_file(response.value().filename(), response.value().release_stream()); - if (load_from_file_result.is_error()) { - GUI::MessageBox::show_error(&window, ByteString::formatted("Can't open file named {}: {}", response.value().filename(), load_from_file_result.error())); - return; - } - })); - - m_save_action = GUI::CommonActions::make_save_action([&](auto&) { - if (m_path.has_value()) { - auto result = FileSystemAccessClient::Client::the().request_file(&window, *m_path, Core::File::OpenMode::ReadWrite | Core::File::OpenMode::Truncate); - if (result.is_error()) - return; - save_to_file(result.value().filename(), result.value().release_stream()); - } else { - auto result = FileSystemAccessClient::Client::the().save_file(&window, "Theme", "ini", Core::File::OpenMode::ReadWrite | Core::File::OpenMode::Truncate); - if (result.is_error()) - return; - save_to_file(result.value().filename(), result.value().release_stream()); - } - }); - file_menu->add_action(*m_save_action); - - file_menu->add_action(GUI::CommonActions::make_save_as_action([&](auto&) { - auto result = FileSystemAccessClient::Client::the().save_file(&window, "Theme", "ini", Core::File::OpenMode::ReadWrite | Core::File::OpenMode::Truncate); - if (result.is_error()) - return; - save_to_file(result.value().filename(), result.value().release_stream()); - })); - file_menu->add_separator(); - - file_menu->add_recent_files_list([&](auto& action) { - if (request_close() == GUI::Window::CloseRequestDecision::StayOpen) - return; - auto response = FileSystemAccessClient::Client::the().request_file_read_only_approved(&window, action.text()); - if (response.is_error()) - return; - auto load_from_file_result = load_from_file(response.value().filename(), response.value().release_stream()); - if (load_from_file_result.is_error()) { - GUI::MessageBox::show_error(&window, ByteString::formatted("Can't open file named {}: {}", response.value().filename(), load_from_file_result.error())); - return; - } - }); - - file_menu->add_action(GUI::CommonActions::make_quit_action([&](auto&) { - if (request_close() == GUI::Window::CloseRequestDecision::Close) - GUI::Application::the()->quit(); - })); - - auto view_menu = window.add_menu("&View"_string); - view_menu->add_action(GUI::CommonActions::make_fullscreen_action([&](auto&) { - window.set_fullscreen(!window.is_fullscreen()); - })); - - window.add_menu(GUI::CommonMenus::make_accessibility_menu(*m_preview_widget)); - - auto help_menu = window.add_menu("&Help"_string); - help_menu->add_action(GUI::CommonActions::make_command_palette_action(&window)); - help_menu->add_action(GUI::CommonActions::make_about_action("Theme Editor"_string, GUI::Icon::default_icon("app-theme-editor"sv), &window)); - - return {}; -} - -void MainWidget::update_title() -{ - window()->set_title(ByteString::formatted("{}[*] - Theme Editor", m_path.value_or("Untitled"))); -} - -GUI::Window::CloseRequestDecision MainWidget::request_close() -{ - if (!window()->is_modified()) - return GUI::Window::CloseRequestDecision::Close; - - auto result = GUI::MessageBox::ask_about_unsaved_changes(window(), m_path.value_or(""), m_last_modified_time); - if (result == GUI::MessageBox::ExecResult::Yes) { - m_save_action->activate(); - if (window()->is_modified()) - return GUI::Window::CloseRequestDecision::StayOpen; - return GUI::Window::CloseRequestDecision::Close; - } - - if (result == GUI::MessageBox::ExecResult::No) - return GUI::Window::CloseRequestDecision::Close; - - return GUI::Window::CloseRequestDecision::StayOpen; -} - -void MainWidget::set_path(ByteString path) -{ - m_path = path; - update_title(); -} - -void MainWidget::save_to_file(ByteString const& filename, NonnullOwnPtr file) -{ - auto theme = Core::ConfigFile::open(filename, move(file)).release_value_but_fixme_should_propagate_errors(); - -#define __ENUMERATE_ALIGNMENT_ROLE(role) theme->write_entry("Alignments", to_string(Gfx::AlignmentRole::role), to_string(m_current_palette.alignment(Gfx::AlignmentRole::role))); - ENUMERATE_ALIGNMENT_ROLES(__ENUMERATE_ALIGNMENT_ROLE) -#undef __ENUMERATE_ALIGNMENT_ROLE - -#define __ENUMERATE_COLOR_ROLE(role) theme->write_entry("Colors", to_string(Gfx::ColorRole::role), m_current_palette.color(Gfx::ColorRole::role).to_byte_string()); - ENUMERATE_COLOR_ROLES(__ENUMERATE_COLOR_ROLE) -#undef __ENUMERATE_COLOR_ROLE - -#define __ENUMERATE_FLAG_ROLE(role) theme->write_bool_entry("Flags", to_string(Gfx::FlagRole::role), m_current_palette.flag(Gfx::FlagRole::role)); - ENUMERATE_FLAG_ROLES(__ENUMERATE_FLAG_ROLE) -#undef __ENUMERATE_FLAG_ROLE - -#define __ENUMERATE_METRIC_ROLE(role) theme->write_num_entry("Metrics", to_string(Gfx::MetricRole::role), m_current_palette.metric(Gfx::MetricRole::role)); - ENUMERATE_METRIC_ROLES(__ENUMERATE_METRIC_ROLE) -#undef __ENUMERATE_METRIC_ROLE - -#define __ENUMERATE_PATH_ROLE(role) theme->write_entry("Paths", to_string(Gfx::PathRole::role), m_current_palette.path(Gfx::PathRole::role)); - ENUMERATE_PATH_ROLES(__ENUMERATE_PATH_ROLE) -#undef __ENUMERATE_PATH_ROLE - - auto sync_result = theme->sync(); - if (sync_result.is_error()) { - GUI::MessageBox::show_error(window(), ByteString::formatted("Failed to save theme file: {}", sync_result.error())); - } else { - m_last_modified_time = MonotonicTime::now(); - set_path(filename); - window()->set_modified(false); - GUI::Application::the()->set_most_recently_open_file(filename); - } -} - -ErrorOr MainWidget::encode() -{ - auto buffer = TRY(Core::AnonymousBuffer::create_with_size(sizeof(Gfx::SystemTheme))); - auto* data = buffer.data(); - -#define __ENUMERATE_ALIGNMENT_ROLE(role) \ - data->alignment[(int)Gfx::AlignmentRole::role] = m_current_palette.alignment(Gfx::AlignmentRole::role); - ENUMERATE_ALIGNMENT_ROLES(__ENUMERATE_ALIGNMENT_ROLE) -#undef __ENUMERATE_ALIGNMENT_ROLE - -#define __ENUMERATE_COLOR_ROLE(role) \ - data->color[(int)Gfx::ColorRole::role] = m_current_palette.color(Gfx::ColorRole::role).value(); - ENUMERATE_COLOR_ROLES(__ENUMERATE_COLOR_ROLE) -#undef __ENUMERATE_COLOR_ROLE - -#define __ENUMERATE_FLAG_ROLE(role) \ - data->flag[(int)Gfx::FlagRole::role] = m_current_palette.flag(Gfx::FlagRole::role); - ENUMERATE_FLAG_ROLES(__ENUMERATE_FLAG_ROLE) -#undef __ENUMERATE_FLAG_ROLE - -#define __ENUMERATE_METRIC_ROLE(role) \ - data->metric[(int)Gfx::MetricRole::role] = m_current_palette.metric(Gfx::MetricRole::role); - ENUMERATE_METRIC_ROLES(__ENUMERATE_METRIC_ROLE) -#undef __ENUMERATE_METRIC_ROLE - -#define ENCODE_PATH(role, allow_empty) \ - do { \ - auto path = m_current_palette.path(Gfx::PathRole::role); \ - char const* characters; \ - if (path.is_empty()) { \ - switch (Gfx::PathRole::role) { \ - case Gfx::PathRole::TitleButtonIcons: \ - characters = "/res/icons/16x16/"; \ - break; \ - default: \ - characters = allow_empty ? "" : "/res/"; \ - } \ - } \ - characters = path.characters(); \ - memcpy(data->path[(int)Gfx::PathRole::role], characters, min(strlen(characters) + 1, sizeof(data->path[(int)Gfx::PathRole::role]))); \ - data->path[(int)Gfx::PathRole::role][sizeof(data->path[(int)Gfx::PathRole::role]) - 1] = '\0'; \ - } while (0) - - ENCODE_PATH(TitleButtonIcons, false); - ENCODE_PATH(ActiveWindowShadow, true); - ENCODE_PATH(InactiveWindowShadow, true); - ENCODE_PATH(TaskbarShadow, true); - ENCODE_PATH(MenuShadow, true); - ENCODE_PATH(TooltipShadow, true); - - return buffer; -} - -void MainWidget::build_override_controls() -{ - auto* theme_override_controls = find_descendant_of_type_named("theme_override_controls"); - - m_theme_override_apply = theme_override_controls->find_child_of_type_named("apply_button"); - m_theme_override_reset = theme_override_controls->find_child_of_type_named("reset_button"); - - m_theme_override_apply->on_click = [&](auto) { - auto encoded = encode(); - if (encoded.is_error()) - return; - // Empty the color scheme path to signal that it exists only in memory. - m_current_palette.path(Gfx::PathRole::ColorScheme) = ""; - GUI::ConnectionToWindowServer::the().async_set_system_theme_override(encoded.value()); - }; - - m_theme_override_reset->on_click = [&](auto) { - GUI::ConnectionToWindowServer::the().async_clear_system_theme_override(); - }; - - GUI::Application::the()->on_theme_change = [&]() { - auto override_active = GUI::ConnectionToWindowServer::the().is_system_theme_overridden(); - m_theme_override_apply->set_enabled(!override_active && window()->is_modified()); - m_theme_override_reset->set_enabled(override_active); - }; -} - -ErrorOr MainWidget::add_property_tab(PropertyTab const& property_tab) -{ - auto& scrollable_container = m_property_tabs->add_tab(TRY(String::from_utf8(property_tab.title))); - scrollable_container.set_should_hide_unnecessary_scrollbars(true); - - auto properties_list = GUI::Widget::construct(); - scrollable_container.set_widget(properties_list); - properties_list->set_layout(GUI::Margins { 8 }, 12); - - for (auto const& group : property_tab.property_groups) { - NonnullRefPtr group_box = properties_list->add(group.title); - // 1px less on the left makes the text line up with the group title. - group_box->set_layout(GUI::Margins { 8, 8, 8, 7 }, 12); - group_box->set_preferred_height(GUI::SpecialDimension::Fit); - - for (auto const& property : group.properties) { - NonnullRefPtr row_widget = group_box->add(); - row_widget->set_fixed_height(22); - TRY(property.role.visit( - [&](Gfx::AlignmentRole role) -> ErrorOr { - TRY(row_widget->load_from_gml(alignment_property_gml)); - - auto& name_label = *row_widget->find_descendant_of_type_named("name"); - name_label.set_text(TRY(String::from_utf8(to_string(role)))); - - auto& alignment_picker = *row_widget->find_descendant_of_type_named("combo_box"); - alignment_picker.set_model(*m_alignment_model); - alignment_picker.on_change = [&, role](auto&, auto& index) { - set_alignment(role, index.data(GUI::ModelRole::Custom).to_text_alignment(Gfx::TextAlignment::CenterLeft)); - }; - alignment_picker.set_selected_index(m_alignment_model->index_of(m_current_palette.alignment(role)), GUI::AllowCallback::No); - - VERIFY(m_alignment_inputs[to_underlying(role)].is_null()); - m_alignment_inputs[to_underlying(role)] = alignment_picker; - return {}; - }, - [&](Gfx::ColorRole role) -> ErrorOr { - TRY(row_widget->load_from_gml(color_property_gml)); - - auto& name_label = *row_widget->find_descendant_of_type_named("name"); - name_label.set_text(TRY(String::from_utf8(to_string(role)))); - - auto& color_input = *row_widget->find_descendant_of_type_named("color_input"); - color_input.on_change = [&, role] { - set_color(role, color_input.color()); - }; - color_input.set_color(m_current_palette.color(role), GUI::AllowCallback::No); - - VERIFY(m_color_inputs[to_underlying(role)].is_null()); - m_color_inputs[to_underlying(role)] = color_input; - return {}; - }, - [&](Gfx::FlagRole role) -> ErrorOr { - TRY(row_widget->load_from_gml(flag_property_gml)); - - auto& checkbox = *row_widget->find_descendant_of_type_named("checkbox"); - checkbox.set_text(TRY(String::from_utf8(to_string(role)))); - checkbox.on_checked = [&, role](bool checked) { - set_flag(role, checked); - }; - checkbox.set_checked(m_current_palette.flag(role), GUI::AllowCallback::No); - - VERIFY(m_flag_inputs[to_underlying(role)].is_null()); - m_flag_inputs[to_underlying(role)] = checkbox; - return {}; - }, - [&](Gfx::MetricRole role) -> ErrorOr { - TRY(row_widget->load_from_gml(metric_property_gml)); - - auto& name_label = *row_widget->find_descendant_of_type_named("name"); - name_label.set_text(TRY(String::from_utf8(to_string(role)))); - - auto& spin_box = *row_widget->find_descendant_of_type_named("spin_box"); - spin_box.on_change = [&, role](int value) { - set_metric(role, value); - }; - spin_box.set_value(m_current_palette.metric(role), GUI::AllowCallback::No); - - VERIFY(m_metric_inputs[to_underlying(role)].is_null()); - m_metric_inputs[to_underlying(role)] = spin_box; - return {}; - }, - [&](Gfx::PathRole role) -> ErrorOr { - TRY(row_widget->load_from_gml(path_property_gml)); - - auto& name_label = *row_widget->find_descendant_of_type_named("name"); - name_label.set_text(TRY(String::from_utf8(to_string(role)))); - - auto& path_input = *row_widget->find_descendant_of_type_named("path_input"); - path_input.on_change = [&, role] { - set_path(role, path_input.text()); - }; - path_input.set_text(m_current_palette.path(role), GUI::AllowCallback::No); - - auto& path_picker_button = *row_widget->find_descendant_of_type_named("path_picker_button"); - auto picker_target = (role == Gfx::PathRole::TitleButtonIcons) ? PathPickerTarget::Folder : PathPickerTarget::File; - path_picker_button.on_click = [&, role, picker_target](auto) { - show_path_picker_dialog(to_string(role), path_input, picker_target); - }; - - VERIFY(m_path_inputs[to_underlying(role)].is_null()); - m_path_inputs[to_underlying(role)] = path_input; - return {}; - })); - } - } - - return {}; -} - -void MainWidget::set_alignment(Gfx::AlignmentRole role, Gfx::TextAlignment value) -{ - auto preview_palette = m_current_palette; - preview_palette.set_alignment(role, value); - set_palette(preview_palette); -} - -void MainWidget::set_color(Gfx::ColorRole role, Gfx::Color value) -{ - auto preview_palette = m_current_palette; - preview_palette.set_color(role, value); - set_palette(preview_palette); -} - -void MainWidget::set_flag(Gfx::FlagRole role, bool value) -{ - auto preview_palette = m_current_palette; - preview_palette.set_flag(role, value); - set_palette(preview_palette); -} - -void MainWidget::set_metric(Gfx::MetricRole role, int value) -{ - auto preview_palette = m_current_palette; - preview_palette.set_metric(role, value); - set_palette(preview_palette); -} - -void MainWidget::set_path(Gfx::PathRole role, ByteString value) -{ - auto preview_palette = m_current_palette; - preview_palette.set_path(role, value); - set_palette(preview_palette); -} - -void MainWidget::set_palette(Gfx::Palette palette) -{ - m_current_palette = move(palette); - m_preview_widget->set_preview_palette(m_current_palette); - m_theme_override_apply->set_enabled(true); - window()->set_modified(true); -} - -void MainWidget::show_path_picker_dialog(StringView property_display_name, GUI::TextBox& path_input, PathPickerTarget path_picker_target) -{ - bool open_folder = path_picker_target == PathPickerTarget::Folder; - auto window_title = ByteString::formatted(open_folder ? "Select {} folder"sv : "Select {} file"sv, property_display_name); - auto target_path = path_input.text(); - if (FileSystem::exists(target_path)) { - if (!FileSystem::is_directory(target_path)) - target_path = LexicalPath::dirname(target_path); - } else { - target_path = "/res/icons"; - } - auto result = GUI::FilePicker::get_open_filepath(window(), window_title, target_path, open_folder); - if (!result.has_value()) - return; - path_input.set_text(*result); -} - -ErrorOr MainWidget::load_from_file(ByteString const& filename, NonnullOwnPtr file) -{ - auto config_file = TRY(Core::ConfigFile::open(filename, move(file))); - auto theme = TRY(Gfx::load_system_theme(config_file)); - VERIFY(theme.is_valid()); - - auto new_palette = Gfx::Palette(Gfx::PaletteImpl::create_with_anonymous_buffer(theme)); - set_palette(move(new_palette)); - set_path(filename); - -#define __ENUMERATE_ALIGNMENT_ROLE(role) \ - if (auto alignment_input = m_alignment_inputs[to_underlying(Gfx::AlignmentRole::role)]) \ - alignment_input->set_selected_index(m_alignment_model->index_of(m_current_palette.alignment(Gfx::AlignmentRole::role)), GUI::AllowCallback::No); - ENUMERATE_ALIGNMENT_ROLES(__ENUMERATE_ALIGNMENT_ROLE) -#undef __ENUMERATE_ALIGNMENT_ROLE - -#define __ENUMERATE_COLOR_ROLE(role) \ - if (auto color_input = m_color_inputs[to_underlying(Gfx::ColorRole::role)]) \ - color_input->set_color(m_current_palette.color(Gfx::ColorRole::role), GUI::AllowCallback::No); - ENUMERATE_COLOR_ROLES(__ENUMERATE_COLOR_ROLE) -#undef __ENUMERATE_COLOR_ROLE - -#define __ENUMERATE_FLAG_ROLE(role) \ - if (auto flag_input = m_flag_inputs[to_underlying(Gfx::FlagRole::role)]) \ - flag_input->set_checked(m_current_palette.flag(Gfx::FlagRole::role), GUI::AllowCallback::No); - ENUMERATE_FLAG_ROLES(__ENUMERATE_FLAG_ROLE) -#undef __ENUMERATE_FLAG_ROLE - -#define __ENUMERATE_METRIC_ROLE(role) \ - if (auto metric_input = m_metric_inputs[to_underlying(Gfx::MetricRole::role)]) \ - metric_input->set_value(m_current_palette.metric(Gfx::MetricRole::role), GUI::AllowCallback::No); - ENUMERATE_METRIC_ROLES(__ENUMERATE_METRIC_ROLE) -#undef __ENUMERATE_METRIC_ROLE - -#define __ENUMERATE_PATH_ROLE(role) \ - if (auto path_input = m_path_inputs[to_underlying(Gfx::PathRole::role)]) \ - path_input->set_text(m_current_palette.path(Gfx::PathRole::role), GUI::AllowCallback::No); - ENUMERATE_PATH_ROLES(__ENUMERATE_PATH_ROLE) -#undef __ENUMERATE_PATH_ROLE - - m_last_modified_time = MonotonicTime::now(); - window()->set_modified(false); - GUI::Application::the()->set_most_recently_open_file(filename); - return {}; -} - -void MainWidget::drag_enter_event(GUI::DragEvent& event) -{ - auto const& mime_types = event.mime_types(); - if (mime_types.contains_slow("text/uri-list"sv)) - event.accept(); -} - -void MainWidget::drop_event(GUI::DropEvent& event) -{ - event.accept(); - window()->move_to_front(); - - if (event.mime_data().has_urls()) { - auto urls = event.mime_data().urls(); - if (urls.is_empty()) - return; - if (urls.size() > 1) { - GUI::MessageBox::show(window(), "ThemeEditor can only open one file at a time!"sv, "One at a time please!"sv, GUI::MessageBox::Type::Error); - return; - } - if (request_close() == GUI::Window::CloseRequestDecision::StayOpen) - return; - - auto response = FileSystemAccessClient::Client::the().request_file(window(), urls.first().serialize_path(), Core::File::OpenMode::Read); - if (response.is_error()) - return; - - auto load_from_file_result = load_from_file(response.value().filename(), response.value().release_stream()); - if (load_from_file_result.is_error()) - GUI::MessageBox::show_error(window(), ByteString::formatted("Can't open file named {}: {}", response.value().filename(), load_from_file_result.error())); - } -} - -} diff --git a/Userland/Applications/ThemeEditor/MainWidget.h b/Userland/Applications/ThemeEditor/MainWidget.h deleted file mode 100644 index 358ccd7ec16..00000000000 --- a/Userland/Applications/ThemeEditor/MainWidget.h +++ /dev/null @@ -1,141 +0,0 @@ -/* - * Copyright (c) 2022-2023, Sam Atkins - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include "PreviewWidget.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace ThemeEditor { - -class AlignmentModel final : public GUI::Model { -public: - static ErrorOr> try_create() - { - return adopt_nonnull_ref_or_enomem(new (nothrow) AlignmentModel()); - } - - virtual ~AlignmentModel() = default; - - virtual int row_count(GUI::ModelIndex const& = GUI::ModelIndex()) const override { return 3; } - virtual int column_count(GUI::ModelIndex const& = GUI::ModelIndex()) const override { return 2; } - - virtual GUI::Variant data(GUI::ModelIndex const& index, GUI::ModelRole role) const override - { - if (role == GUI::ModelRole::Display) - return m_alignments[index.row()].title; - if (role == GUI::ModelRole::Custom) - return m_alignments[index.row()].setting_value; - - return {}; - } - - size_t index_of(Gfx::TextAlignment alignment) const - { - auto match = m_alignments.find_if([&](auto& it) { return it.setting_value == alignment; }); - return match.index(); - } - -private: - AlignmentModel() = default; - - struct AlignmentValue { - ByteString title; - Gfx::TextAlignment setting_value; - }; - Vector m_alignments { - { "Center", Gfx::TextAlignment::Center }, - { "Left", Gfx::TextAlignment::CenterLeft }, - { "Right", Gfx::TextAlignment::CenterRight }, - }; -}; - -struct Property { - Variant role; -}; - -struct PropertyGroup { - ByteString title; - Vector properties; -}; - -struct PropertyTab { - StringView title; - Vector property_groups; -}; - -class MainWidget final : public GUI::Widget { - C_OBJECT_ABSTRACT(MainWidget); - -public: - static ErrorOr> try_create(); - virtual ~MainWidget() override = default; - - ErrorOr initialize_menubar(GUI::Window&); - GUI::Window::CloseRequestDecision request_close(); - void update_title(); - ErrorOr load_from_file(ByteString const& filename, NonnullOwnPtr file); - -private: - explicit MainWidget(NonnullRefPtr); - - virtual void drag_enter_event(GUI::DragEvent&) override; - virtual void drop_event(GUI::DropEvent&) override; - - void save_to_file(ByteString const& filename, NonnullOwnPtr file); - ErrorOr encode(); - void set_path(ByteString); - - void build_override_controls(); - - ErrorOr add_property_tab(PropertyTab const&); - void set_alignment(Gfx::AlignmentRole, Gfx::TextAlignment); - void set_color(Gfx::ColorRole, Gfx::Color); - void set_flag(Gfx::FlagRole, bool); - void set_metric(Gfx::MetricRole, int); - void set_path(Gfx::PathRole, ByteString); - - void set_palette(Gfx::Palette); - - enum class PathPickerTarget { - File, - Folder, - }; - void show_path_picker_dialog(StringView property_display_name, GUI::TextBox&, PathPickerTarget); - - RefPtr m_preview_widget; - RefPtr m_property_tabs; - RefPtr m_statusbar; - RefPtr m_save_action; - - RefPtr m_theme_override_apply; - RefPtr m_theme_override_reset; - - Optional m_path; - Gfx::Palette m_current_palette; - MonotonicTime m_last_modified_time { MonotonicTime::now() }; - - RefPtr m_alignment_model; - - Array, to_underlying(Gfx::AlignmentRole::__Count)> m_alignment_inputs; - Array, to_underlying(Gfx::ColorRole::__Count)> m_color_inputs; - Array, to_underlying(Gfx::FlagRole::__Count)> m_flag_inputs; - Array, to_underlying(Gfx::MetricRole::__Count)> m_metric_inputs; - Array, to_underlying(Gfx::PathRole::__Count)> m_path_inputs; - - OwnPtr m_preview_type_action_group; -}; - -} diff --git a/Userland/Applications/ThemeEditor/MetricProperty.gml b/Userland/Applications/ThemeEditor/MetricProperty.gml deleted file mode 100644 index 11c8e2c8a6d..00000000000 --- a/Userland/Applications/ThemeEditor/MetricProperty.gml +++ /dev/null @@ -1,17 +0,0 @@ -@GUI::Frame { - layout: @GUI::HorizontalBoxLayout { - spacing: 4 - } - preferred_height: "fit" - - @GUI::Label { - name: "name" - text: "Some metric" - text_alignment: "CenterLeft" - fixed_width: 200 - } - - @GUI::SpinBox { - name: "spin_box" - } -} diff --git a/Userland/Applications/ThemeEditor/PathProperty.gml b/Userland/Applications/ThemeEditor/PathProperty.gml deleted file mode 100644 index 4d5d0e3f565..00000000000 --- a/Userland/Applications/ThemeEditor/PathProperty.gml +++ /dev/null @@ -1,24 +0,0 @@ -@GUI::Frame { - layout: @GUI::HorizontalBoxLayout { - spacing: 4 - } - preferred_height: "fit" - - @GUI::Label { - name: "name" - text: "Some path" - text_alignment: "CenterLeft" - fixed_width: 200 - } - - @GUI::TextBox { - name: "path_input" - } - - @GUI::Button { - name: "path_picker_button" - fixed_width: 22 - icon: "/res/icons/16x16/open.png" - tooltip: "Choose..." - } -} diff --git a/Userland/Applications/ThemeEditor/PreviewWidget.cpp b/Userland/Applications/ThemeEditor/PreviewWidget.cpp deleted file mode 100644 index 977bb2f0a4b..00000000000 --- a/Userland/Applications/ThemeEditor/PreviewWidget.cpp +++ /dev/null @@ -1,152 +0,0 @@ -/* - * Copyright (c) 2020, Andreas Kling - * Copyright (c) 2021-2022, Sam Atkins - * Copyright (c) 2021, Antonio Di Stefano - * Copyright (c) 2022, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "PreviewWidget.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include - -REGISTER_WIDGET(ThemeEditor, PreviewWidget); - -namespace ThemeEditor { - -class MiniWidgetGallery final : public GUI::Widget { - C_OBJECT_ABSTRACT(MiniWidgetGallery); - -public: - static ErrorOr> try_create() - { - auto gallery = TRY(adopt_nonnull_ref_or_enomem(new (nothrow) MiniWidgetGallery())); - TRY(gallery->load_from_gml(window_preview_gml)); - - gallery->for_each_child_widget([](auto& child) { - child.set_focus_policy(GUI::FocusPolicy::NoFocus); - return IterationDecision::Continue; - }); - - return gallery; - } - - void set_preview_palette(Gfx::Palette& palette) - { - set_palette(palette); - Function recurse = [&](GUI::Widget& parent_widget) { - parent_widget.for_each_child_widget([&](auto& widget) { - widget.set_palette(palette); - recurse(widget); - return IterationDecision::Continue; - }); - }; - recurse(*this); - } - -private: - MiniWidgetGallery() - { - } -}; - -ErrorOr> PreviewWidget::try_create() -{ - auto preview_widget = TRY(adopt_nonnull_ref_or_enomem(new (nothrow) PreviewWidget())); - preview_widget->m_gallery = TRY(preview_widget->try_add()); - return preview_widget; -} - -PreviewWidget::PreviewWidget() - : GUI::AbstractThemePreview(GUI::Application::the()->palette()) -{ - set_greedy_for_hits(true); -} - -void PreviewWidget::palette_changed() -{ - m_gallery->set_preview_palette(preview_palette()); - update_preview_window_locations(); -} - -void PreviewWidget::set_color_filter(OwnPtr color_filter) -{ - m_color_filter = move(color_filter); - repaint(); -} - -void PreviewWidget::update_preview_window_locations() -{ - auto& palette = preview_palette(); - int window_title_height = palette.metric(Gfx::MetricRole::TitleHeight) - + palette.metric(Gfx::MetricRole::BorderThickness); - - constexpr int inactive_offset_x = -20; - int inactive_offset_y = -(window_title_height + 4); - constexpr int hightlight_offset_x = 140; - int hightlight_offset_y = window_title_height + 40; - - m_active_window_rect = Gfx::IntRect(0, 0, 320, 220); - m_inactive_window_rect = m_active_window_rect.translated(inactive_offset_x, inactive_offset_y); - m_highlight_window_rect = Gfx::IntRect(m_active_window_rect.location(), { 160, 70 }).translated(hightlight_offset_x, hightlight_offset_y); - - auto window_group = Array { - Window { m_active_window_rect }, - Window { m_inactive_window_rect }, - Window { m_highlight_window_rect }, - }; - - center_window_group_within(window_group, frame_inner_rect()); - - m_gallery->set_relative_rect(m_active_window_rect); -} - -void PreviewWidget::paint_preview(GUI::PaintEvent&) -{ - paint_window("Inactive window"sv, m_inactive_window_rect, Gfx::WindowTheme::WindowState::Inactive, active_window_icon()); - paint_window("Active window"sv, m_active_window_rect, Gfx::WindowTheme::WindowState::Active, inactive_window_icon()); -} - -void PreviewWidget::paint_hightlight_window() -{ - GUI::Painter painter(*this); - paint_window("Highlight window"sv, m_highlight_window_rect, Gfx::WindowTheme::WindowState::Highlighted, inactive_window_icon(), 1); - auto button_rect = Gfx::IntRect(0, 0, 80, 22).centered_within(m_highlight_window_rect); - Gfx::StylePainter::paint_button(painter, button_rect, preview_palette(), Gfx::ButtonStyle::Normal, false, false, false, true, false, false); - painter.draw_text(button_rect, ":^)"sv, Gfx::TextAlignment::Center, preview_palette().color(foreground_role()), Gfx::TextElision::Right, Gfx::TextWrapping::DontWrap); -} - -void PreviewWidget::second_paint_event(GUI::PaintEvent&) -{ - GUI::Painter painter(*this); - - paint_hightlight_window(); - - if (!m_color_filter) - return; - - auto target = painter.target(); - auto bitmap_clone_or_error = target->clone(); - if (bitmap_clone_or_error.is_error()) - return; - - auto clone = bitmap_clone_or_error.release_value(); - auto rect = target->rect(); - - m_color_filter->apply(*target, rect, *clone, rect); -} - -void PreviewWidget::resize_event(GUI::ResizeEvent&) -{ - update_preview_window_locations(); -} - -} diff --git a/Userland/Applications/ThemeEditor/PreviewWidget.h b/Userland/Applications/ThemeEditor/PreviewWidget.h deleted file mode 100644 index 392d01a4c23..00000000000 --- a/Userland/Applications/ThemeEditor/PreviewWidget.h +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright (c) 2020, Andreas Kling - * Copyright (c) 2021-2022, Sam Atkins - * Copyright (c) 2021, Antonio Di Stefano - * Copyright (c) 2022, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include -#include -#include - -namespace ThemeEditor { - -class MiniWidgetGallery; - -class PreviewWidget final - : public GUI::AbstractThemePreview - , public GUI::ColorFilterer { - C_OBJECT_ABSTRACT(PreviewWidget); - -public: - static ErrorOr> try_create(); - virtual ~PreviewWidget() override = default; - - virtual void set_color_filter(OwnPtr) override; - -private: - PreviewWidget(); - - virtual void paint_preview(GUI::PaintEvent&) override; - virtual void second_paint_event(GUI::PaintEvent&) override; - virtual void resize_event(GUI::ResizeEvent&) override; - virtual void palette_changed() override; - - void paint_hightlight_window(); - void update_preview_window_locations(); - - Gfx::IntRect m_active_window_rect; - Gfx::IntRect m_inactive_window_rect; - Gfx::IntRect m_highlight_window_rect; - - OwnPtr m_color_filter = nullptr; - RefPtr m_gallery; -}; - -} diff --git a/Userland/Applications/ThemeEditor/Previews/WindowPreview.gml b/Userland/Applications/ThemeEditor/Previews/WindowPreview.gml deleted file mode 100644 index 3f9a782f281..00000000000 --- a/Userland/Applications/ThemeEditor/Previews/WindowPreview.gml +++ /dev/null @@ -1,29 +0,0 @@ -@GUI::Frame { - layout: @GUI::VerticalBoxLayout {} - - @GUI::Widget { - layout: @GUI::VerticalBoxLayout { - margins: [8, 100, 8, 8] - } - - @GUI::Button { - text: "Button" - } - - @GUI::CheckBox { - text: "Check box" - } - - @GUI::RadioButton { - text: "Radio button" - } - - @GUI::TextEditor { - text: "Text editor\nwith multiple\nlines." - } - } - - @GUI::Statusbar { - text: "Status bar" - } -} diff --git a/Userland/Applications/ThemeEditor/ThemeEditor.gml b/Userland/Applications/ThemeEditor/ThemeEditor.gml deleted file mode 100644 index 0fcf4f10215..00000000000 --- a/Userland/Applications/ThemeEditor/ThemeEditor.gml +++ /dev/null @@ -1,52 +0,0 @@ -@GUI::Widget { - layout: @GUI::VerticalBoxLayout {} - fill_with_background_color: true - - @GUI::Widget { - layout: @GUI::VerticalBoxLayout { - margins: [0, 4, 4] - spacing: 6 - } - - @GUI::Widget { - layout: @GUI::HorizontalBoxLayout { - spacing: 4 - } - - @ThemeEditor::PreviewWidget { - name: "preview_widget" - } - - @GUI::TabWidget { - name: "property_tabs" - container_margins: [5] - } - } - - @GUI::Widget { - name: "theme_override_controls" - layout: @GUI::HorizontalBoxLayout { - spacing: 6 - } - preferred_height: "shrink" - - @GUI::Layout::Spacer {} - - @GUI::DialogButton { - name: "reset_button" - text: "Reset" - enabled: false - } - - @GUI::DialogButton { - name: "apply_button" - text: "Apply" - enabled: false - } - } - } - - @GUI::Statusbar { - name: "statusbar" - } -} diff --git a/Userland/Applications/ThemeEditor/main.cpp b/Userland/Applications/ThemeEditor/main.cpp deleted file mode 100644 index 70afa4e33b8..00000000000 --- a/Userland/Applications/ThemeEditor/main.cpp +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Copyright (c) 2020, Andreas Kling - * Copyright (c) 2021, networkException - * Copyright (c) 2021-2022, Sam Atkins - * Copyright (c) 2021, Antonio Di Stefano - * Copyright (c) 2022, Filiph Sandström - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "MainWidget.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -ErrorOr serenity_main(Main::Arguments arguments) -{ - TRY(Core::System::pledge("stdio recvfd sendfd thread rpath cpath wpath unix")); - - auto app = TRY(GUI::Application::create(arguments)); - - Config::pledge_domain("ThemeEditor"); - app->set_config_domain("ThemeEditor"_string); - - StringView file_to_edit; - - Core::ArgsParser parser; - parser.add_positional_argument(file_to_edit, "Theme file to edit", "file", Core::ArgsParser::Required::No); - parser.parse(arguments); - - IGNORE_USE_IN_ESCAPING_LAMBDA Optional path = {}; - - if (auto error_or_path = FileSystem::absolute_path(file_to_edit); !file_to_edit.is_empty() && !error_or_path.is_error()) - path = error_or_path.release_value(); - - TRY(Core::System::pledge("stdio recvfd sendfd thread rpath unix")); - TRY(Core::System::unveil("/tmp/session/%sid/portal/filesystemaccess", "rw")); - TRY(Core::System::unveil("/res", "r")); - TRY(Core::System::unveil(nullptr, nullptr)); - - auto app_icon = GUI::Icon::default_icon("app-theme-editor"sv); - IGNORE_USE_IN_ESCAPING_LAMBDA auto window = GUI::Window::construct(); - - IGNORE_USE_IN_ESCAPING_LAMBDA auto main_widget = TRY(ThemeEditor::MainWidget::try_create()); - window->set_main_widget(main_widget); - - if (path.has_value()) { - // Note: This is deferred to ensure that the window has already popped and any error dialog boxes would show up correctly. - app->event_loop().deferred_invoke( - [&window, &path, &main_widget]() { - auto response = FileSystemAccessClient::Client::the().request_file_read_only_approved(window, path.value()); - if (!response.is_error()) { - auto load_from_file_result = main_widget->load_from_file(response.value().filename(), response.value().release_stream()); - if (load_from_file_result.is_error()) - GUI::MessageBox::show_error(window, ByteString::formatted("Loading theme from file has failed: {}", load_from_file_result.error())); - } - }); - } - - TRY(main_widget->initialize_menubar(window)); - main_widget->update_title(); - - window->on_close_request = [&]() -> GUI::Window::CloseRequestDecision { - return main_widget->request_close(); - }; - - window->restore_size_and_position("ThemeEditor"sv, "Window"sv, { { 820, 520 } }); - window->save_size_and_position_on_close("ThemeEditor"sv, "Window"sv); - window->set_resizable(false); - window->show(); - window->set_icon(app_icon.bitmap_for_size(16)); - return app->exec(); -} diff --git a/Userland/Applications/VideoPlayer/CMakeLists.txt b/Userland/Applications/VideoPlayer/CMakeLists.txt deleted file mode 100644 index eb24ff91e1d..00000000000 --- a/Userland/Applications/VideoPlayer/CMakeLists.txt +++ /dev/null @@ -1,17 +0,0 @@ -serenity_component( - VideoPlayer - TARGETS VideoPlayer - DEPENDS AudioServer -) - -compile_gml(VideoPlayerWidget.gml VideoPlayerWidgetGML.cpp) - -set(SOURCES - main.cpp - VideoPlayerWidgetGML.cpp - VideoFrameWidget.cpp - VideoPlayerWidget.cpp -) - -serenity_app(VideoPlayer ICON app-video-player) -target_link_libraries(VideoPlayer PRIVATE LibVideo LibAudio LibConfig LibCore LibGfx LibGUI LibMain LibFileSystemAccessClient LibURL) diff --git a/Userland/Applications/VideoPlayer/VideoFrameWidget.cpp b/Userland/Applications/VideoPlayer/VideoFrameWidget.cpp deleted file mode 100644 index f219617b7b8..00000000000 --- a/Userland/Applications/VideoPlayer/VideoFrameWidget.cpp +++ /dev/null @@ -1,107 +0,0 @@ -/* - * Copyright (c) 2022, Gregory Bertilson - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include - -#include "VideoFrameWidget.h" - -namespace VideoPlayer { - -VideoFrameWidget::VideoFrameWidget() -{ - REGISTER_BOOL_PROPERTY("auto_resize", auto_resize, set_auto_resize); - set_auto_resize(true); -} - -void VideoFrameWidget::set_bitmap(Gfx::Bitmap const* bitmap) -{ - if (m_bitmap == bitmap) - return; - - m_bitmap = bitmap; - if (m_bitmap && m_auto_resize) - set_fixed_size(m_bitmap->size()); - - update(); -} - -void VideoFrameWidget::set_sizing_mode(VideoSizingMode value) -{ - if (value == m_sizing_mode) - return; - m_sizing_mode = value; - - update(); -} - -void VideoFrameWidget::set_auto_resize(bool value) -{ - m_auto_resize = value; - - if (m_bitmap) - set_fixed_size(m_bitmap->size()); -} - -void VideoFrameWidget::mousedown_event(GUI::MouseEvent&) -{ - if (on_click) - on_click(); -} - -void VideoFrameWidget::doubleclick_event(GUI::MouseEvent&) -{ - if (on_doubleclick) - on_doubleclick(); -} - -void VideoFrameWidget::paint_event(GUI::PaintEvent& event) -{ - Frame::paint_event(event); - - GUI::Painter painter(*this); - painter.add_clip_rect(event.rect()); - - painter.fill_rect(frame_inner_rect(), Gfx::Color::Black); - - if (!m_bitmap) - return; - - if (m_sizing_mode == VideoSizingMode::Stretch) { - painter.draw_scaled_bitmap(frame_inner_rect(), *m_bitmap, m_bitmap->rect(), 1.0f, Gfx::Painter::ScalingMode::BilinearBlend); - return; - } - - auto center = frame_inner_rect().center(); - - if (m_sizing_mode == VideoSizingMode::FullSize) { - painter.blit(center.translated(-m_bitmap->width() / 2, -m_bitmap->height() / 2), *m_bitmap, m_bitmap->rect()); - return; - } - - VERIFY(m_sizing_mode < VideoSizingMode::Sentinel); - - auto aspect_ratio = m_bitmap->width() / static_cast(m_bitmap->height()); - auto display_aspect_ratio = frame_inner_rect().width() / static_cast(frame_inner_rect().height()); - - Gfx::IntSize display_size; - if ((display_aspect_ratio > aspect_ratio) == (m_sizing_mode == VideoSizingMode::Fit)) { - display_size = { - (frame_inner_rect().height() * m_bitmap->width()) / m_bitmap->height(), - frame_inner_rect().height(), - }; - } else { - display_size = { - frame_inner_rect().width(), - (frame_inner_rect().width() * m_bitmap->height()) / m_bitmap->width(), - }; - } - - auto display_rect = Gfx::IntRect(center.translated(-display_size.width() / 2, -display_size.height() / 2), display_size); - painter.draw_scaled_bitmap(display_rect, *m_bitmap, m_bitmap->rect(), 1.0f, Gfx::Painter::ScalingMode::BilinearBlend); -} - -} diff --git a/Userland/Applications/VideoPlayer/VideoFrameWidget.h b/Userland/Applications/VideoPlayer/VideoFrameWidget.h deleted file mode 100644 index a1280fb306a..00000000000 --- a/Userland/Applications/VideoPlayer/VideoFrameWidget.h +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Copyright (c) 2022, Gregory Bertilson - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include - -namespace VideoPlayer { - -enum class VideoSizingMode : u8 { - Fit, - Fill, - Stretch, - FullSize, - Sentinel -}; - -class VideoFrameWidget final : public GUI::Frame { - C_OBJECT(VideoFrameWidget) -public: - virtual ~VideoFrameWidget() override = default; - - void set_bitmap(Gfx::Bitmap const*); - Gfx::Bitmap const* bitmap() const { return m_bitmap.ptr(); } - - void set_sizing_mode(VideoSizingMode value); - VideoSizingMode sizing_mode() const { return m_sizing_mode; } - - void set_auto_resize(bool value); - bool auto_resize() const { return m_auto_resize; } - - Function on_click; - Function on_doubleclick; - -protected: - explicit VideoFrameWidget(); - - virtual void mousedown_event(GUI::MouseEvent&) override; - virtual void doubleclick_event(GUI::MouseEvent&) override; - virtual void paint_event(GUI::PaintEvent&) override; - -private: - RefPtr m_bitmap; - VideoSizingMode m_sizing_mode { VideoSizingMode::Fit }; - bool m_auto_resize { false }; -}; - -constexpr StringView video_sizing_mode_name(VideoSizingMode mode) -{ - switch (mode) { - case VideoSizingMode::Fit: - return "Fit"sv; - break; - case VideoSizingMode::Fill: - return "Fill"sv; - break; - case VideoSizingMode::Stretch: - return "Stretch"sv; - break; - case VideoSizingMode::FullSize: - return "Full size"sv; - break; - default: - VERIFY_NOT_REACHED(); - } -} - -} diff --git a/Userland/Applications/VideoPlayer/VideoPlayerWidget.cpp b/Userland/Applications/VideoPlayer/VideoPlayerWidget.cpp deleted file mode 100644 index dba09d83b4b..00000000000 --- a/Userland/Applications/VideoPlayer/VideoPlayerWidget.cpp +++ /dev/null @@ -1,426 +0,0 @@ -/* - * Copyright (c) 2022, Gregory Bertilson - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "VideoPlayerWidget.h" - -namespace VideoPlayer { - -ErrorOr VideoPlayerWidget::initialize() -{ - m_video_display = find_descendant_of_type_named("video_frame"); - m_video_display->on_click = [&]() { toggle_pause(); }; - m_video_display->on_doubleclick = [&]() { toggle_fullscreen(); }; - - m_seek_slider = find_descendant_of_type_named("seek_slider"); - m_seek_slider->on_drag_start = [&]() { - if (!m_playback_manager) - return; - m_was_playing_before_seek = m_playback_manager->is_playing(); - m_playback_manager->pause_playback(); - }; - m_seek_slider->on_drag_end = [&]() { - if (!m_playback_manager || !m_was_playing_before_seek) - return; - m_was_playing_before_seek = false; - m_playback_manager->resume_playback(); - }; - m_seek_slider->on_change = [&](int value) { - if (!m_playback_manager) - return; - update_seek_slider_max(); - auto progress = value / static_cast(m_seek_slider->max()); - auto duration = m_playback_manager->duration().to_milliseconds(); - Duration timestamp = Duration::from_milliseconds(static_cast(round(progress * static_cast(duration)))); - auto seek_mode_to_use = m_seek_slider->knob_dragging() ? seek_mode() : Video::PlaybackManager::SeekMode::Accurate; - m_playback_manager->seek_to_timestamp(timestamp, seek_mode_to_use); - set_current_timestamp(m_playback_manager->current_playback_time()); - }; - - m_play_icon = TRY(Gfx::Bitmap::load_from_file("/res/icons/16x16/play.png"sv)); - m_pause_icon = TRY(Gfx::Bitmap::load_from_file("/res/icons/16x16/pause.png"sv)); - - m_play_pause_action = GUI::Action::create("Play", { Key_Space }, m_play_icon, [&](auto&) { - toggle_pause(); - }); - - m_cycle_sizing_modes_action = GUI::Action::create( - "Sizing", TRY(Gfx::Bitmap::load_from_file("/res/icons/16x16/fit-image-to-view.png"sv)), [&](auto&) { - cycle_sizing_modes(); - }); - - m_toggle_fullscreen_action = GUI::CommonActions::make_fullscreen_action([&](auto&) { - toggle_fullscreen(); - }); - - m_timestamp_label = find_descendant_of_type_named("timestamp"); - m_volume_slider = find_descendant_of_type_named("volume_slider"); - find_descendant_of_type_named("playback")->set_action(*m_play_pause_action); - find_descendant_of_type_named("sizing")->set_action(*m_cycle_sizing_modes_action); - find_descendant_of_type_named("fullscreen")->set_action(*m_toggle_fullscreen_action); - - m_size_fit_action = GUI::Action::create_checkable("&Fit", [&](auto&) { - set_sizing_mode(VideoSizingMode::Fit); - }); - - m_size_fill_action = GUI::Action::create_checkable("Fi&ll", [&](auto&) { - set_sizing_mode(VideoSizingMode::Fill); - }); - - m_size_stretch_action = GUI::Action::create_checkable("&Stretch", [&](auto&) { - set_sizing_mode(VideoSizingMode::Stretch); - }); - - m_size_fullsize_action = GUI::Action::create_checkable("F&ull Size", [&](auto&) { - set_sizing_mode(VideoSizingMode::FullSize); - }); - - // Load the current video sizing mode - // The default fallback for `read_u32` is 0, which is also our desired default for the sizing mode, VideoSizingMode::Fit - auto sizing_mode_value = Config::read_u32("VideoPlayer"sv, "Playback"sv, "SizingMode"sv); - if (sizing_mode_value >= to_underlying(VideoSizingMode::Sentinel)) { - sizing_mode_value = 0; - } - - set_sizing_mode(static_cast(sizing_mode_value)); - - return {}; -} - -void VideoPlayerWidget::close_file() -{ - if (m_playback_manager) - m_playback_manager = nullptr; -} - -void VideoPlayerWidget::open_file(FileSystemAccessClient::File file) -{ - auto mapped_file_result = Core::MappedFile::map_from_file(file.release_stream(), file.filename()); - if (mapped_file_result.is_error()) { - GUI::MessageBox::show_error(window(), String::formatted("Failed to read file: {}", file.filename()).release_value_but_fixme_should_propagate_errors()); - return; - } - - auto load_file_result = Video::PlaybackManager::from_mapped_file(mapped_file_result.release_value()); - if (load_file_result.is_error()) { - on_decoding_error(load_file_result.release_error()); - return; - } - - m_path = MUST(String::from_byte_string(file.filename())); - update_title(); - close_file(); - - m_playback_manager = load_file_result.release_value(); - - m_playback_manager->on_video_frame = [this](auto frame) { - m_video_display->set_bitmap(move(frame)); - m_video_display->repaint(); - - update_seek_slider_max(); - set_current_timestamp(m_playback_manager->current_playback_time()); - }; - - m_playback_manager->on_playback_state_change = [this]() { - update_play_pause_icon(); - // If we are seeking, do not set the timestamp, as that will override the seek position. - if (!m_was_playing_before_seek && m_playback_manager->get_state() != Video::PlaybackState::Seeking) { - set_current_timestamp(m_playback_manager->current_playback_time()); - } - }; - - m_playback_manager->on_decoder_error = [this](auto error) { - on_decoding_error(error); - }; - - m_playback_manager->on_fatal_playback_error = [this](auto) { - close_file(); - }; - - update_seek_slider_max(); - resume_playback(); -} - -void VideoPlayerWidget::update_play_pause_icon() -{ - if (!m_playback_manager) { - m_play_pause_action->set_enabled(false); - m_play_pause_action->set_icon(m_play_icon); - m_play_pause_action->set_text("Play"sv); - return; - } - - m_play_pause_action->set_enabled(true); - - if (m_playback_manager->is_playing() || m_was_playing_before_seek) { - m_play_pause_action->set_icon(m_pause_icon); - m_play_pause_action->set_text("Pause"sv); - } else { - m_play_pause_action->set_icon(m_play_icon); - m_play_pause_action->set_text("Play"sv); - } -} - -void VideoPlayerWidget::resume_playback() -{ - if (!m_playback_manager || m_seek_slider->knob_dragging()) - return; - m_playback_manager->resume_playback(); -} - -void VideoPlayerWidget::pause_playback() -{ - if (!m_playback_manager || m_seek_slider->knob_dragging()) - return; - m_playback_manager->pause_playback(); -} - -void VideoPlayerWidget::toggle_pause() -{ - if (!m_playback_manager) - return; - if (m_playback_manager->is_playing()) - pause_playback(); - else - resume_playback(); -} - -void VideoPlayerWidget::on_decoding_error(Video::DecoderError const& error) -{ - StringView text_format; - - switch (error.category()) { - case Video::DecoderErrorCategory::IO: - text_format = "Error while reading video:\n{}"sv; - break; - case Video::DecoderErrorCategory::Memory: - text_format = "Ran out of memory:\n{}"sv; - break; - case Video::DecoderErrorCategory::Corrupted: - text_format = "Video was corrupted:\n{}"sv; - break; - case Video::DecoderErrorCategory::Invalid: - text_format = "Invalid call:\n{}"sv; - break; - case Video::DecoderErrorCategory::NotImplemented: - text_format = "Video feature is not yet implemented:\n{}"sv; - break; - default: - text_format = "Unexpected error:\n{}"sv; - break; - } - - GUI::MessageBox::show(window(), ByteString::formatted(text_format, error.string_literal()), "Video Player encountered an error"sv); -} - -void VideoPlayerWidget::update_seek_slider_max() -{ - if (!m_playback_manager) { - m_seek_slider->set_enabled(false); - return; - } - - m_seek_slider->set_max(static_cast(min(m_playback_manager->duration().to_milliseconds(), NumericLimits::max()))); - m_seek_slider->set_enabled(true); -} - -void VideoPlayerWidget::set_current_timestamp(Duration timestamp) -{ - set_time_label(timestamp); - if (!m_playback_manager) - return; - auto progress = static_cast(timestamp.to_milliseconds()) / static_cast(m_playback_manager->duration().to_milliseconds()); - m_seek_slider->set_value(static_cast(round(progress * m_seek_slider->max())), GUI::AllowCallback::No); -} - -void VideoPlayerWidget::set_time_label(Duration timestamp) -{ - StringBuilder string_builder; - auto append_time = [&](Duration time) { - auto seconds = (time.to_milliseconds() + 500) / 1000; - string_builder.append(human_readable_digital_time(seconds)); - }; - - append_time(timestamp); - - if (m_playback_manager) { - string_builder.append(" / "sv); - append_time(m_playback_manager->duration()); - } else { - string_builder.append(" / --:--:--"sv); - } - - m_timestamp_label->set_text(string_builder.to_string().release_value_but_fixme_should_propagate_errors()); -} - -void VideoPlayerWidget::drop_event(GUI::DropEvent& event) -{ - event.accept(); - window()->move_to_front(); - - if (event.mime_data().has_urls()) { - auto urls = event.mime_data().urls(); - if (urls.is_empty()) - return; - if (urls.size() > 1) { - GUI::MessageBox::show_error(window(), "VideoPlayer can only view one clip at a time!"sv); - return; - } - auto response = FileSystemAccessClient::Client::the().request_file_read_only_approved(window(), urls.first().serialize_path()); - if (response.is_error()) - return; - - open_file(response.release_value()); - } -} - -void VideoPlayerWidget::cycle_sizing_modes() -{ - auto sizing_mode = m_video_display->sizing_mode(); - sizing_mode = static_cast((to_underlying(sizing_mode) + 1) % to_underlying(VideoSizingMode::Sentinel)); - set_sizing_mode(sizing_mode); -} - -void VideoPlayerWidget::set_current_sizing_mode_checked() -{ - switch (m_video_display->sizing_mode()) { - case VideoSizingMode::Fit: - m_size_fit_action->set_checked(true); - break; - - case VideoSizingMode::Fill: - m_size_fill_action->set_checked(true); - break; - - case VideoSizingMode::Stretch: - m_size_stretch_action->set_checked(true); - break; - - case VideoSizingMode::FullSize: - m_size_fullsize_action->set_checked(true); - break; - - case VideoSizingMode::Sentinel: - break; - } -} - -void VideoPlayerWidget::toggle_fullscreen() -{ - auto* parent_window = window(); - parent_window->set_fullscreen(!parent_window->is_fullscreen()); - auto* bottom_container = find_descendant_of_type_named("bottom_container"); - bottom_container->set_visible(!parent_window->is_fullscreen()); - auto* video_frame = find_descendant_of_type_named("video_frame"); - video_frame->set_frame_style(parent_window->is_fullscreen() ? Gfx::FrameStyle::NoFrame : Gfx::FrameStyle::SunkenContainer); -} - -void VideoPlayerWidget::update_title() -{ - StringBuilder string_builder; - if (m_path.is_empty()) { - string_builder.append("No video"sv); - } else { - string_builder.append(m_path); - } - - string_builder.append("[*] - Video Player"sv); - window()->set_title(string_builder.to_byte_string()); -} - -Video::PlaybackManager::SeekMode VideoPlayerWidget::seek_mode() -{ - if (m_use_fast_seeking->is_checked()) - return Video::PlaybackManager::SeekMode::Fast; - return Video::PlaybackManager::SeekMode::Accurate; -} - -void VideoPlayerWidget::set_seek_mode(Video::PlaybackManager::SeekMode seek_mode) -{ - m_use_fast_seeking->set_checked(seek_mode == Video::PlaybackManager::SeekMode::Fast); -} - -void VideoPlayerWidget::set_sizing_mode(VideoSizingMode sizing_mode) -{ - if (m_video_display->sizing_mode() == sizing_mode) - return; - - m_video_display->set_sizing_mode(sizing_mode); - Config::write_u32("VideoPlayer"sv, "Playback"sv, "SizingMode"sv, to_underlying(sizing_mode)); - - set_current_sizing_mode_checked(); -} - -ErrorOr VideoPlayerWidget::initialize_menubar(GUI::Window& window) -{ - // File menu - auto file_menu = window.add_menu("&File"_string); - file_menu->add_action(GUI::CommonActions::make_open_action([&](auto&) { - FileSystemAccessClient::OpenFileOptions options { - .allowed_file_types = { { GUI::FileTypeFilter { "Video Files", { { "mkv", "webm" } } }, GUI::FileTypeFilter::all_files() } }, - }; - auto response = FileSystemAccessClient::Client::the().open_file(&window, options); - if (response.is_error()) - return; - - open_file(response.release_value()); - })); - file_menu->add_separator(); - file_menu->add_action(GUI::CommonActions::make_quit_action([&](auto&) { - window.close(); - })); - - // Playback menu - auto playback_menu = window.add_menu("&Playback"_string); - - // FIXME: Maybe seek mode should be in an options dialog instead. The playback menu may get crowded. - // For now, leave it here for convenience. - m_use_fast_seeking = GUI::Action::create_checkable("&Fast Seeking", [&](auto&) {}); - playback_menu->add_action(*m_use_fast_seeking); - set_seek_mode(Video::PlaybackManager::DEFAULT_SEEK_MODE); - - // View menu - auto view_menu = window.add_menu("&View"_string); - view_menu->add_action(*m_toggle_fullscreen_action); - - auto sizing_mode_menu = view_menu->add_submenu("&Sizing Mode"_string); - sizing_mode_menu->set_icon(TRY(Gfx::Bitmap::load_from_file("/res/icons/16x16/fit-image-to-view.png"sv))); - - m_sizing_mode_group = make(); - m_sizing_mode_group->set_exclusive(true); - m_sizing_mode_group->add_action(*m_size_fit_action); - m_sizing_mode_group->add_action(*m_size_fill_action); - m_sizing_mode_group->add_action(*m_size_stretch_action); - m_sizing_mode_group->add_action(*m_size_fullsize_action); - - sizing_mode_menu->add_action(*m_size_fit_action); - sizing_mode_menu->add_action(*m_size_fill_action); - sizing_mode_menu->add_action(*m_size_stretch_action); - sizing_mode_menu->add_action(*m_size_fullsize_action); - - // Help menu - auto help_menu = window.add_menu("&Help"_string); - help_menu->add_action(GUI::CommonActions::make_about_action("Video Player"_string, TRY(GUI::Icon::try_create_default_icon("app-video-player"sv)), &window)); - - return {}; -} - -} diff --git a/Userland/Applications/VideoPlayer/VideoPlayerWidget.gml b/Userland/Applications/VideoPlayer/VideoPlayerWidget.gml deleted file mode 100644 index 86c1977a1b6..00000000000 --- a/Userland/Applications/VideoPlayer/VideoPlayerWidget.gml +++ /dev/null @@ -1,69 +0,0 @@ -@VideoPlayer::VideoPlayerWidget { - fill_with_background_color: true - layout: @GUI::VerticalBoxLayout {} - - @VideoPlayer::VideoFrameWidget { - name: "video_frame" - auto_resize: false - } - - @GUI::Widget { - name: "bottom_container" - max_height: 50 - layout: @GUI::VerticalBoxLayout {} - - @GUI::HorizontalSlider { - name: "seek_slider" - fixed_height: 20 - enabled: false - jump_to_cursor: true - } - - @GUI::ToolbarContainer { - @GUI::Toolbar { - name: "toolbar" - - @GUI::Button { - name: "playback" - icon_from_path: "/res/icons/16x16/play.png" - fixed_width: 24 - button_style: "Coolbar" - } - - @GUI::VerticalSeparator {} - - @GUI::Label { - name: "timestamp" - autosize: true - } - - @GUI::Layout::Spacer {} - - @GUI::Button { - name: "sizing" - icon_from_path: "/res/icons/16x16/fit-image-to-view.png" - fixed_width: 24 - button_style: "Coolbar" - } - - @GUI::VerticalSeparator {} - - @GUI::HorizontalSlider { - name: "volume_slider" - min: 0 - max: 100 - fixed_width: 100 - } - - @GUI::VerticalSeparator {} - - @GUI::Button { - name: "fullscreen" - icon_from_path: "/res/icons/16x16/fullscreen.png" - fixed_width: 24 - button_style: "Coolbar" - } - } - } - } -} diff --git a/Userland/Applications/VideoPlayer/VideoPlayerWidget.h b/Userland/Applications/VideoPlayer/VideoPlayerWidget.h deleted file mode 100644 index e51ae33634f..00000000000 --- a/Userland/Applications/VideoPlayer/VideoPlayerWidget.h +++ /dev/null @@ -1,87 +0,0 @@ -/* - * Copyright (c) 2022, Gregory Bertilson - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "VideoFrameWidget.h" - -namespace VideoPlayer { - -class VideoPlayerWidget final : public GUI::Widget { - C_OBJECT_ABSTRACT(VideoPlayerWidget) - -public: - static ErrorOr> try_create(); - ErrorOr initialize(); - virtual ~VideoPlayerWidget() override = default; - void close_file(); - void open_file(FileSystemAccessClient::File filename); - void resume_playback(); - void pause_playback(); - void toggle_pause(); - - void update_title(); - - Video::PlaybackManager::SeekMode seek_mode(); - void set_seek_mode(Video::PlaybackManager::SeekMode seek_mode); - void set_sizing_mode(VideoSizingMode sizing_mode); - - ErrorOr initialize_menubar(GUI::Window&); - -private: - VideoPlayerWidget() = default; - void update_play_pause_icon(); - void update_seek_slider_max(); - void set_current_timestamp(Duration); - void set_time_label(Duration); - void on_decoding_error(Video::DecoderError const&); - - void cycle_sizing_modes(); - void set_current_sizing_mode_checked(); - - void toggle_fullscreen(); - - virtual void drop_event(GUI::DropEvent&) override; - - String m_path; - - RefPtr m_video_display; - RefPtr m_seek_slider; - - RefPtr m_play_icon; - RefPtr m_pause_icon; - - RefPtr m_play_pause_action; - RefPtr m_timestamp_label; - RefPtr m_cycle_sizing_modes_action; - RefPtr m_volume_slider; - - RefPtr m_use_fast_seeking; - - RefPtr m_toggle_fullscreen_action; - - OwnPtr m_sizing_mode_group; - RefPtr m_size_fit_action; - RefPtr m_size_fill_action; - RefPtr m_size_stretch_action; - RefPtr m_size_fullsize_action; - - OwnPtr m_playback_manager; - - bool m_was_playing_before_seek { false }; -}; - -} diff --git a/Userland/Applications/VideoPlayer/main.cpp b/Userland/Applications/VideoPlayer/main.cpp deleted file mode 100644 index 97d485548df..00000000000 --- a/Userland/Applications/VideoPlayer/main.cpp +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright (c) 2021, Hunter Salyer - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include -#include -#include - -#include "VideoPlayerWidget.h" - -ErrorOr serenity_main(Main::Arguments arguments) -{ - StringView filename = ""sv; - Core::ArgsParser args_parser; - args_parser.add_positional_argument(filename, "The video file to display.", "filename", Core::ArgsParser::Required::No); - args_parser.parse(arguments); - - Config::pledge_domain("VideoPlayer"); - - auto app = TRY(GUI::Application::create(arguments)); - app->set_config_domain("VideoPlayer"_string); - - auto window = GUI::Window::construct(); - window->resize(640, 480); - window->set_resizable(true); - - TRY(Core::System::unveil("/tmp/session/%sid/portal/filesystemaccess", "rw")); - TRY(Core::System::unveil("/res", "r")); - TRY(Core::System::unveil(nullptr, nullptr)); - - auto main_widget = TRY(VideoPlayer::VideoPlayerWidget::try_create()); - window->set_main_widget(main_widget); - main_widget->update_title(); - TRY(main_widget->initialize_menubar(window)); - - window->show(); - window->set_icon(GUI::Icon::default_icon("app-video-player"sv).bitmap_for_size(16)); - - if (!filename.is_empty()) { - auto response = FileSystemAccessClient::Client::the().request_file_read_only_approved(window, filename); - if (!response.is_error()) { - main_widget->open_file(response.release_value()); - } - } - - return app->exec(); -} diff --git a/Userland/Applications/Welcome/CMakeLists.txt b/Userland/Applications/Welcome/CMakeLists.txt deleted file mode 100644 index 39f2f791a3b..00000000000 --- a/Userland/Applications/Welcome/CMakeLists.txt +++ /dev/null @@ -1,16 +0,0 @@ -serenity_component( - Welcome - TARGETS Welcome - DEPENDS Help WebContent -) - -compile_gml(WelcomeWindow.gml WelcomeWindowGML.cpp) - -set(SOURCES - WelcomeWindowGML.cpp - WelcomeWidget.cpp - main.cpp -) - -serenity_app(Welcome ICON app-welcome) -target_link_libraries(Welcome PRIVATE LibConfig LibCore LibGfx LibGUI LibWebView LibWeb LibMain LibURL) diff --git a/Userland/Applications/Welcome/WelcomeWidget.cpp b/Userland/Applications/Welcome/WelcomeWidget.cpp deleted file mode 100644 index 0495807d586..00000000000 --- a/Userland/Applications/Welcome/WelcomeWidget.cpp +++ /dev/null @@ -1,133 +0,0 @@ -/* - * Copyright (c) 2021-2022, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "WelcomeWidget.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace Welcome { - -static String tips_file_path = "/usr/share/Welcome/tips.txt"_string; - -ErrorOr> WelcomeWidget::create() -{ - auto welcome_widget = TRY(WelcomeWidget::try_create()); - TRY(welcome_widget->create_widgets()); - - return welcome_widget; -} - -ErrorOr WelcomeWidget::create_widgets() -{ - m_banner_widget = find_descendant_of_type_named("welcome_banner"); - m_banner_font = TRY(Gfx::BitmapFont::try_load_from_uri("resource://fonts/MarietaRegular24.font"sv)); - - m_web_view = find_descendant_of_type_named("web_view"); - m_web_view->use_native_user_style_sheet(); - auto path = TRY(String::formatted("{}/README.md", Core::StandardPaths::home_directory())); - m_web_view->load(URL::create_with_file_scheme(path.to_byte_string())); - - m_tip_label = find_descendant_of_type_named("tip_label"); - m_tip_frame = find_descendant_of_type_named("tip_frame"); - - m_next_button = find_descendant_of_type_named("next_button"); - m_next_button->on_click = [&](auto) { - m_web_view->set_visible(false); - m_tip_frame->set_visible(true); - if (m_tips.is_empty()) - return; - m_tip_index++; - if (m_tip_index >= m_tips.size()) - m_tip_index = 0; - m_tip_label->set_text(m_tips[m_tip_index]); - }; - - m_help_button = find_descendant_of_type_named("help_button"); - m_help_button->on_click = [&](auto) { - GUI::Process::spawn_or_show_error(window(), "/bin/Help"sv); - }; - - m_new_button = find_descendant_of_type_named("new_button"); - m_new_button->on_click = [&](auto) { - m_web_view->set_visible(!m_web_view->is_visible()); - m_tip_frame->set_visible(!m_tip_frame->is_visible()); - }; - - m_close_button = find_descendant_of_type_named("close_button"); - m_close_button->on_click = [](auto) { - GUI::Application::the()->quit(); - }; - - auto welcome = Config::list_groups("SystemServer"sv).first_matching([](auto& group) { return group == "Welcome"sv; }); - m_startup_checkbox = find_descendant_of_type_named("startup_checkbox"); - m_startup_checkbox->set_checked(welcome.has_value()); - m_startup_checkbox->on_checked = [](bool is_checked) { - if (is_checked) - Config::add_group("SystemServer"sv, "Welcome"sv); - else - Config::remove_group("SystemServer"sv, "Welcome"sv); - }; - - if (auto result = open_and_parse_tips_file(); result.is_error()) { - auto error = TRY(String::formatted("Opening \"{}\" failed: {}", tips_file_path, result.error())); - m_tip_label->set_text(error); - warnln(error); - } - - set_random_tip(); - - return {}; -} - -ErrorOr WelcomeWidget::open_and_parse_tips_file() -{ - auto file = TRY(Core::File::open(tips_file_path, Core::File::OpenMode::Read)); - auto buffered_file = TRY(Core::InputBufferedFile::create(move(file))); - Array buffer; - - while (TRY(buffered_file->can_read_line())) { - auto line = TRY(buffered_file->read_line(buffer)); - if (line.starts_with('#') || line.is_empty()) - continue; - TRY(m_tips.try_append(TRY(String::from_utf8(line)))); - } - - return {}; -} - -void WelcomeWidget::set_random_tip() -{ - if (m_tips.is_empty()) - return; - - m_tip_index = get_random_uniform(m_tips.size()); - m_tip_label->set_text(m_tips[m_tip_index]); -} - -void WelcomeWidget::paint_event(GUI::PaintEvent& event) -{ - GUI::Painter painter(*this); - painter.add_clip_rect(event.rect()); - - auto rect = m_banner_widget->relative_rect(); - painter.draw_text(rect, "Welcome to "sv, *m_banner_font, Gfx::TextAlignment::CenterLeft, palette().base_text()); - rect.set_x(rect.x() + static_cast(ceilf(m_banner_font->width("Welcome to "sv)))); - painter.draw_text(rect, "Serenity"sv, m_banner_font->bold_variant(), Gfx::TextAlignment::CenterLeft, palette().base_text()); - rect.set_x(rect.x() + static_cast(ceilf(m_banner_font->bold_variant().width("Serenity"sv)))); - painter.draw_text(rect, "OS"sv, m_banner_font->bold_variant(), Gfx::TextAlignment::CenterLeft, palette().tray_text()); -} - -} diff --git a/Userland/Applications/Welcome/WelcomeWidget.h b/Userland/Applications/Welcome/WelcomeWidget.h deleted file mode 100644 index 0cc3a20c13a..00000000000 --- a/Userland/Applications/Welcome/WelcomeWidget.h +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright (c) 2021-2022, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include - -namespace Welcome { -class WelcomeWidget final : public GUI::Widget { - C_OBJECT(WelcomeWidget); - -public: - static ErrorOr> create(); - virtual ~WelcomeWidget() override = default; - -private: - WelcomeWidget() = default; - ErrorOr create_widgets(); - static ErrorOr> try_create(); - - virtual void paint_event(GUI::PaintEvent&) override; - - void set_random_tip(); - ErrorOr open_and_parse_tips_file(); - - RefPtr m_banner_font; - RefPtr m_banner_widget; - - RefPtr m_close_button; - RefPtr m_next_button; - RefPtr m_help_button; - RefPtr m_new_button; - RefPtr m_tip_frame; - RefPtr m_tip_label; - RefPtr m_startup_checkbox; - RefPtr m_web_view; - - size_t m_tip_index { 0 }; - Vector m_tips; -}; -} diff --git a/Userland/Applications/Welcome/WelcomeWindow.gml b/Userland/Applications/Welcome/WelcomeWindow.gml deleted file mode 100644 index b3d447424c9..00000000000 --- a/Userland/Applications/Welcome/WelcomeWindow.gml +++ /dev/null @@ -1,115 +0,0 @@ -@Welcome::WelcomeWidget { - fill_with_background_color: true - layout: @GUI::VerticalBoxLayout { - margins: [8] - spacing: 4 - } - - @GUI::Widget { - name: "welcome_banner" - fixed_height: 30 - } - - @GUI::Widget { - layout: @GUI::HorizontalBoxLayout { - spacing: 8 - } - - @GUI::Widget { - layout: @GUI::VerticalBoxLayout { - spacing: 8 - } - - @GUI::Frame { - name: "tip_frame" - min_width: 340 - min_height: 170 - fill_with_background_color: true - background_role: "Base" - layout: @GUI::HorizontalBoxLayout { - margins: [0, 16, 0, 0] - } - - @GUI::Widget { - fixed_width: 60 - layout: @GUI::VerticalBoxLayout {} - - @GUI::ImageWidget { - fixed_height: 60 - auto_resize: false - bitmap: "/res/icons/32x32/app-welcome.png" - } - } - - @GUI::Widget { - layout: @GUI::VerticalBoxLayout {} - - @GUI::Label { - fixed_height: 60 - name: "did_you_know_label" - text: "Did you know..." - text_alignment: "CenterLeft" - font_size: 12 - font_weight: "Bold" - } - - @GUI::Label { - name: "tip_label" - text_alignment: "TopLeft" - text_wrapping: "Wrap" - font_size: 12 - } - } - } - - @WebView::OutOfProcessWebView { - name: "web_view" - min_width: 340 - min_height: 170 - visible: false - } - - @GUI::CheckBox { - name: "startup_checkbox" - text: "Show Welcome the next time SerenityOS starts" - } - } - - @GUI::Widget { - name: "navigation_column" - fixed_width: 116 - min_height: 170 - layout: @GUI::VerticalBoxLayout { - spacing: 4 - } - - @GUI::Button { - name: "new_button" - text: "What's New" - } - - @GUI::Button { - name: "help_button" - text: "Help Contents" - icon_from_path: "/res/icons/16x16/book-open.png" - } - - @GUI::Button { - name: "next_button" - text: "Next Tip" - icon_from_path: "/res/icons/16x16/go-forward.png" - } - - @GUI::Layout::Spacer {} - - @GUI::HorizontalSeparator { - fixed_height: 10 - } - - @GUI::Button { - name: "close_button" - text: "Close" - } - } - } -} diff --git a/Userland/Applications/Welcome/main.cpp b/Userland/Applications/Welcome/main.cpp deleted file mode 100644 index 8fc0915fd32..00000000000 --- a/Userland/Applications/Welcome/main.cpp +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright (c) 2021-2022, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "WelcomeWidget.h" -#include -#include -#include -#include -#include -#include - -ErrorOr serenity_main(Main::Arguments arguments) -{ - TRY(Core::System::pledge("stdio recvfd sendfd rpath unix proc exec")); - auto app = TRY(GUI::Application::create(arguments)); - - Config::pledge_domain("SystemServer"); - - TRY(Core::System::unveil("/tmp/session/%sid/portal/webcontent", "rw")); - TRY(Core::System::unveil("/tmp/session/%sid/portal/filesystemaccess", "rw")); - TRY(Core::System::unveil("/res", "r")); - TRY(Core::System::unveil("/home", "r")); - TRY(Core::System::unveil("/usr/share/Welcome", "r")); - TRY(Core::System::unveil("/bin/Help", "x")); - TRY(Core::System::unveil(nullptr, nullptr)); - auto app_icon = TRY(GUI::Icon::try_create_default_icon("app-welcome"sv)); - - auto window = GUI::Window::construct(); - window->resize(480, 250); - window->center_on_screen(); - window->set_title("Welcome"); - window->set_icon(app_icon.bitmap_for_size(16)); - auto welcome_widget = TRY(Welcome::WelcomeWidget::create()); - window->set_main_widget(welcome_widget); - - window->show(); - - return app->exec(); -} diff --git a/Userland/BuggieBox/CMakeLists.txt b/Userland/BuggieBox/CMakeLists.txt deleted file mode 100644 index 393ebb79c84..00000000000 --- a/Userland/BuggieBox/CMakeLists.txt +++ /dev/null @@ -1,51 +0,0 @@ -serenity_component( - BuggieBox - REQUIRED - TARGETS BuggieBox -) - -function (buggiebox_utility src) - get_filename_component(utility ${src} NAME_WE) - target_sources(BuggieBox PRIVATE ${src}) - set_source_files_properties(${src} PROPERTIES COMPILE_DEFINITIONS "serenity_main=${utility}_main") -endfunction() - -set(utility_srcs - ../Utilities/cat.cpp - ../Utilities/checksum.cpp - ../Utilities/chmod.cpp - ../Utilities/chown.cpp - ../Utilities/cp.cpp - ../Utilities/df.cpp - ../Utilities/env.cpp - ../Utilities/file.cpp - ../Utilities/find.cpp - ../Utilities/id.cpp - ../Utilities/less.cpp - ../Utilities/ln.cpp - ../Utilities/ls.cpp - ../Utilities/lsblk.cpp - ../Utilities/mkdir.cpp - ../Utilities/mknod.cpp - ../Utilities/mount.cpp - ../Utilities/mv.cpp - ../Utilities/ps.cpp - ../Utilities/rm.cpp - ../Utilities/rmdir.cpp - ../Utilities/tail.cpp - ../Utilities/tree.cpp - ../Utilities/umount.cpp - ../Utilities/uname.cpp - ../Utilities/uniq.cpp -) - -serenity_bin(BuggieBox) -target_sources(BuggieBox PRIVATE main.cpp) -target_link_libraries(BuggieBox PRIVATE LibMain LibShell LibArchive LibCompress LibCore LibCrypto LibELF LibFileSystem LibGfx LibLine LibRegex LibAudio LibURL) - -foreach(file IN LISTS utility_srcs) - buggiebox_utility(${file}) -endforeach() - -target_sources(BuggieBox PRIVATE ../Shell/main.cpp) -set_source_files_properties( ../Shell/main.cpp PROPERTIES COMPILE_DEFINITIONS "serenity_main=sh_main") diff --git a/Userland/BuggieBox/main.cpp b/Userland/BuggieBox/main.cpp deleted file mode 100644 index 8ce77244654..00000000000 --- a/Userland/BuggieBox/main.cpp +++ /dev/null @@ -1,136 +0,0 @@ -/* - * Copyright (c) 2022, Liav A. - * Copyright (c) 2022, Tim Schumacher - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include - -#define ENUMERATE_UTILITIES(E, ALIAS) \ - ALIAS(b2sum, checksum) \ - E(cat) \ - E(checksum) \ - E(chmod) \ - E(chown) \ - E(cp) \ - E(df) \ - E(env) \ - E(file) \ - E(find) \ - E(id) \ - E(less) \ - E(ln) \ - E(ls) \ - E(lsblk) \ - ALIAS(md5sum, checksum) \ - E(mkdir) \ - E(mknod) \ - E(mount) \ - E(mv) \ - E(ps) \ - E(rm) \ - E(rmdir) \ - E(sh) \ - ALIAS(sha1sum, checksum) \ - ALIAS(sha256sum, checksum) \ - ALIAS(sha512sum, checksum) \ - ALIAS(Shell, sh) \ - E(tail) \ - E(tree) \ - E(umount) \ - E(uname) \ - E(uniq) - -// Declare the entrypoints of all the tools that we delegate to. -// Some tools have additional aliases that we skip in the declarations. -// Relying on `decltype(serenity_main)` ensures that we always stay consistent with the `serenity_main` signature. -#define DECLARE_ENTRYPOINT(name) decltype(serenity_main) name##_main; -#define SKIP(alias, name) -ENUMERATE_UTILITIES(DECLARE_ENTRYPOINT, SKIP) -#undef DECLARE_ENTRYPOINT -#undef SKIP - -static void fail() -{ -#define TOOL_NAME(name) #name##sv, -#define ALIAS_NAME(alias, name) #alias##sv, - auto const tool_names = { - ENUMERATE_UTILITIES(TOOL_NAME, ALIAS_NAME) - }; -#undef TOOL_NAME -#undef ALIAS_NAME - - outln(stderr); - outln(stderr, "Usage:"); - outln(stderr, "* Specify a utility as an argument:"); - outln(stderr, " $ BuggieBox UTILITY"); - outln(stderr, "* Create a symbolic link with the target being this binary,"); - outln(stderr, " and ensure the basename is one of the supported utilities' name."); - - outln(stderr); - outln(stderr, "The following utilities are supported:"); - int count = 0; - for (auto const& tool_name : tool_names) { - if (++count % 5 == 1) - out(stderr, "\n\t"); - out(stderr, "{:12}", tool_name); - } - outln(stderr); -} - -struct Runner { - StringView name; - ErrorOr (*func)(Main::Arguments arguments) = nullptr; -}; - -static constexpr Runner s_runners[] = { -#define RUNNER_ENTRY(name) { #name##sv, name##_main }, -#define ALIAS_RUNNER_ENTRY(alias, name) { #alias##sv, name##_main }, - ENUMERATE_UTILITIES(RUNNER_ENTRY, ALIAS_RUNNER_ENTRY) -#undef RUNNER_ENTRY -#undef ALIAS_RUNNER_ENTRY -}; - -static ErrorOr run_program(Main::Arguments arguments, LexicalPath const& runbase, bool& found_runner) -{ - for (auto& runner : s_runners) { - if (runbase.basename() == runner.name) { - found_runner = true; - return runner.func(arguments); - } - } - return 0; -} - -static ErrorOr buggiebox_main(Main::Arguments arguments) -{ - if (arguments.argc == 0) { - outln(stderr, "Detected directly running BuggieBox without specifying a utility."); - fail(); - return 1; - } - bool found_runner = false; - LexicalPath runbase { arguments.strings[0] }; - auto result = TRY(run_program(arguments, runbase, found_runner)); - if (!found_runner) { - outln(stderr, "'{}' is not supported by BuggieBox.", runbase); - fail(); - } - return result; -} - -ErrorOr serenity_main(Main::Arguments arguments) -{ - LexicalPath runbase { arguments.strings[0] }; - if (runbase.basename() == "BuggieBox"sv) { - Main::Arguments utility_arguments = arguments; - utility_arguments.argc--; - utility_arguments.argv++; - utility_arguments.strings = arguments.strings.slice(1); - return buggiebox_main(utility_arguments); - } - return buggiebox_main(arguments); -} diff --git a/Userland/Demos/CMakeLists.txt b/Userland/Demos/CMakeLists.txt deleted file mode 100644 index 411133ae046..00000000000 --- a/Userland/Demos/CMakeLists.txt +++ /dev/null @@ -1,11 +0,0 @@ -add_subdirectory(CatDog) -add_subdirectory(Eyes) -add_subdirectory(Gradient) -add_subdirectory(LibGfxDemo) -add_subdirectory(LibGfxScaleDemo) -add_subdirectory(Mandelbrot) -add_subdirectory(ModelGallery) -add_subdirectory(Screensaver) -add_subdirectory(Starfield) -add_subdirectory(Tubes) -add_subdirectory(WidgetGallery) diff --git a/Userland/Demos/CatDog/CMakeLists.txt b/Userland/Demos/CatDog/CMakeLists.txt deleted file mode 100644 index 7b16f54cd97..00000000000 --- a/Userland/Demos/CatDog/CMakeLists.txt +++ /dev/null @@ -1,14 +0,0 @@ -serenity_component( - CatDog - RECOMMENDED - TARGETS CatDog -) - -set(SOURCES - CatDog.cpp - SpeechBubble.cpp - main.cpp -) - -serenity_app(CatDog ICON app-catdog) -target_link_libraries(CatDog PRIVATE LibGUI LibGfx LibCore LibMain) diff --git a/Userland/Demos/CatDog/CatDog.cpp b/Userland/Demos/CatDog/CatDog.cpp deleted file mode 100644 index d23cce501f8..00000000000 --- a/Userland/Demos/CatDog/CatDog.cpp +++ /dev/null @@ -1,202 +0,0 @@ -/* - * Copyright (c) 2021, Richard Gráčik - * Copyright (c) 2022, kleines Filmröllchen - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "CatDog.h" -#include -#include -#include -#include -#include - -ErrorOr> CatDog::create() -{ - struct ImageSource { - State state; - StringView path; - }; - - // NOTE: The order of the elements is important. Matching is done in best-match order. - // So items with the more bits should be placed before items with less bits to - // ensure correct matching order. This also means that "Frame2" has to be first. - static constexpr Array const image_sources = { - ImageSource { State::Up | State::Right | State::Frame2, "/res/graphics/catdog/nerun2.png"sv }, - { State::Up | State::Right, "/res/graphics/catdog/nerun1.png"sv }, - { State::Up | State::Left | State::Frame2, "/res/graphics/catdog/nwrun2.png"sv }, - { State::Up | State::Left, "/res/graphics/catdog/nwrun1.png"sv }, - { State::Down | State::Right | State::Frame2, "/res/graphics/catdog/serun2.png"sv }, - { State::Down | State::Right, "/res/graphics/catdog/serun1.png"sv }, - { State::Down | State::Left | State::Frame2, "/res/graphics/catdog/swrun2.png"sv }, - { State::Down | State::Left, "/res/graphics/catdog/swrun1.png"sv }, - { State::Up | State::Frame2, "/res/graphics/catdog/nrun2.png"sv }, - { State::Up, "/res/graphics/catdog/nrun1.png"sv }, - { State::Down | State::Frame2, "/res/graphics/catdog/srun2.png"sv }, - { State::Down, "/res/graphics/catdog/srun1.png"sv }, - { State::Left | State::Frame2, "/res/graphics/catdog/wrun2.png"sv }, - { State::Left, "/res/graphics/catdog/wrun1.png"sv }, - { State::Right | State::Frame2, "/res/graphics/catdog/erun2.png"sv }, - { State::Right, "/res/graphics/catdog/erun1.png"sv }, - { State::Sleeping | State::Frame2, "/res/graphics/catdog/sleep2.png"sv }, - { State::Sleeping, "/res/graphics/catdog/sleep1.png"sv }, - { State::Idle | State::Artist, "/res/graphics/catdog/artist.png"sv }, - { State::Idle | State::Inspector, "/res/graphics/catdog/inspector.png"sv }, - { State::Idle, "/res/graphics/catdog/still.png"sv }, - { State::Alert | State::Artist, "/res/graphics/catdog/artist.png"sv }, - { State::Alert | State::Inspector, "/res/graphics/catdog/inspector.png"sv }, - { State::Alert, "/res/graphics/catdog/alert.png"sv } - }; - - auto catdog = TRY(adopt_nonnull_ref_or_enomem(new (nothrow) CatDog)); - for (auto const& image_source : image_sources) - TRY(catdog->m_images.try_append({ image_source.state, *TRY(Gfx::Bitmap::load_from_file(image_source.path)) })); - - return catdog; -} - -CatDog::CatDog() - : m_proc_all(MUST(Core::File::open("/sys/kernel/processes"sv, Core::File::OpenMode::Read))) -{ - m_idle_sleep_timer.start(); -} - -void CatDog::set_roaming(bool roaming) -{ - m_state = (roaming ? State::Idle : State::Alert) | special_application_states(); - update(); -} - -void CatDog::set_sleeping(bool sleeping) -{ - m_state = (sleeping ? State::Sleeping : State::Idle) | special_application_states(); - update(); -} - -CatDog::State CatDog::special_application_states() const -{ - auto maybe_proc_info = Core::ProcessStatisticsReader::get_all(*m_proc_all); - if (maybe_proc_info.is_error()) - return State::GenericCatDog; - - auto proc_info = maybe_proc_info.release_value(); - auto maybe_paint_program = proc_info.processes.first_matching([](auto& process) { - return process.name.equals_ignoring_ascii_case("pixelpaint"sv) || process.name.equals_ignoring_ascii_case("fonteditor"sv); - }); - if (maybe_paint_program.has_value()) - return State::Artist; - - auto maybe_inspector_program = proc_info.processes.first_matching([](auto& process) { - return process.name.equals_ignoring_ascii_case("systemmonitor"sv) || process.name.equals_ignoring_ascii_case("profiler"sv); - }); - if (maybe_inspector_program.has_value()) - return State::Inspector; - - return State::GenericCatDog; -} - -bool CatDog::is_artist() const -{ - return has_flag(special_application_states(), State::Artist); -} - -bool CatDog::is_inspector() const -{ - return has_flag(special_application_states(), State::Inspector); -} - -bool CatDog::is_sleeping() const -{ - return has_flag(m_state, State::Sleeping); -} - -void CatDog::timer_event(Core::TimerEvent&) -{ - using namespace AK::TimeLiterals; - - if (has_flag(m_state, State::Alert)) - return; - - if (has_flag(m_state, State::Sleeping)) - return; - - m_state = special_application_states(); - - auto const size = window()->size(); - Gfx::IntPoint move; - - if (m_mouse_offset.x() < 0) { - m_state |= State::Left; - move.set_x(max(m_mouse_offset.x(), -size.width() / 2)); - } else if (m_mouse_offset.x() > size.width()) { - m_state |= State::Right; - move.set_x(min(m_mouse_offset.x(), size.width() / 2)); - } - - if (m_mouse_offset.y() < 0) { - m_state |= State::Up; - move.set_y(max(m_mouse_offset.y(), -size.height() / 2)); - } else if (m_mouse_offset.y() > size.height()) { - m_state |= State::Down; - move.set_y(min(m_mouse_offset.y(), size.height() / 2)); - } - - if (has_any_flag(m_state, State::Directions)) { - m_idle_sleep_timer.start(); - } else { - if (m_idle_sleep_timer.elapsed_time() > 5_sec) - m_state |= State::Sleeping; - else - m_state |= State::Idle; - } - - window()->move_to(window()->position() + move); - m_mouse_offset -= move; - - m_frame = m_frame == State::Frame1 ? State::Frame2 : State::Frame1; - m_state |= m_frame; - - update(); -} - -Gfx::Bitmap& CatDog::bitmap_for_state() const -{ - auto const iter = m_images.find_if([&](auto const& image) { return (m_state & image.state) == image.state; }); - return iter != m_images.end() ? *iter->bitmap : *m_images[m_images.size() - 1].bitmap; -} - -void CatDog::paint_event(GUI::PaintEvent& event) -{ - auto const& bmp = bitmap_for_state(); - GUI::Painter painter(*this); - painter.clear_rect(event.rect(), Gfx::Color()); - painter.blit(Gfx::IntPoint(0, 0), bmp, bmp.rect()); -} - -void CatDog::track_mouse_move(Gfx::IntPoint point) -{ - if (has_flag(m_state, State::Alert)) - return; - - if (has_flag(m_state, State::Sleeping)) - return; - - Gfx::IntPoint relative_offset = point - window()->position(); - if (m_mouse_offset != relative_offset) { - m_mouse_offset = relative_offset; - m_idle_sleep_timer.start(); - } -} - -void CatDog::mousedown_event(GUI::MouseEvent& event) -{ - if (event.button() == GUI::MouseButton::Primary && on_click) - on_click(); -} - -void CatDog::context_menu_event(GUI::ContextMenuEvent& event) -{ - if (on_context_menu_request) - on_context_menu_request(event); -} diff --git a/Userland/Demos/CatDog/CatDog.h b/Userland/Demos/CatDog/CatDog.h deleted file mode 100644 index 9b45317a270..00000000000 --- a/Userland/Demos/CatDog/CatDog.h +++ /dev/null @@ -1,87 +0,0 @@ -/* - * Copyright (c) 2021, Richard Gráčik - * Copyright (c) 2022, kleines Filmröllchen - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -class CatDog final : public GUI::Widget - , GUI::MouseTracker { - C_OBJECT(CatDog); - -public: - static ErrorOr> create(); - - virtual void timer_event(Core::TimerEvent&) override; - virtual void paint_event(GUI::PaintEvent& event) override; - virtual void track_mouse_move(Gfx::IntPoint point) override; - virtual void mousedown_event(GUI::MouseEvent& event) override; - virtual void context_menu_event(GUI::ContextMenuEvent& event) override; - - Function on_click; - Function on_context_menu_request; - - void set_roaming(bool roaming); - void set_sleeping(bool sleeping); - - [[nodiscard]] bool is_artist() const; - [[nodiscard]] bool is_inspector() const; - [[nodiscard]] bool is_sleeping() const; - -private: - enum class State : u16 { - Frame1 = 0x0, - Frame2 = 0x1, - - Up = 0x10, - Down = 0x20, - Left = 0x40, - Right = 0x80, - - Directions = Up | Down | Left | Right, - - Roaming = 0x0100, - Idle = 0x0200, - Sleeping = 0x0400, - Alert = 0x0800, - - GenericCatDog = 0x0000, - Inspector = 0x1000, - Artist = 0x2000 - }; - - AK_ENUM_BITWISE_FRIEND_OPERATORS(State); - - struct ImageForState { - State state; - NonnullRefPtr bitmap; - }; - - Vector m_images; - - Gfx::IntPoint m_mouse_offset {}; - Core::ElapsedTimer m_idle_sleep_timer; - - NonnullOwnPtr m_proc_all; - - State m_state { State::Roaming }; - State m_frame { State::Frame1 }; - - CatDog(); - - [[nodiscard]] Gfx::Bitmap& bitmap_for_state() const; - [[nodiscard]] State special_application_states() const; -}; diff --git a/Userland/Demos/CatDog/SpeechBubble.cpp b/Userland/Demos/CatDog/SpeechBubble.cpp deleted file mode 100644 index 66c11a90283..00000000000 --- a/Userland/Demos/CatDog/SpeechBubble.cpp +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright (c) 2021, Gunnar Beutner - * Copyright (c) 2022, kleines Filmröllchen - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "SpeechBubble.h" -#include -#include -#include -#include -#include - -static Array default_messages = { - "It looks like you're trying to debug\na program. Would you like some help?"sv, - "It looks like you're trying to shave\na yak. Would you like some help?"sv, - "Well Hello Friend!"sv, -}; - -static Array artist_messages = { - "It looks like you're creating art.\nWould you like some help?"sv, - "It looks like you're making a meme\nfor Discord. \U0010CD65"sv, - "It looks like you're using the filter\ngallery. Would you like a suggestion?"sv, -}; -static Array inspector_messages = { - "It looks like you're trying to kill\na program. Would you like some help?"sv, - "It looks like you're profiling a\nprogram. Would you like some help?"sv, - "It looks like you're interested in\nCPU usage. Would you like some help?"sv, -}; - -void SpeechBubble::paint_event(GUI::PaintEvent&) -{ - GUI::Painter painter(*this); - painter.clear_rect(rect(), Gfx::Color()); - - constexpr auto background_color = Gfx::Color::from_rgb(0xeaf688); - - auto text_area = rect(); - text_area.set_height(text_area.height() - 10); - painter.draw_rect(text_area, palette().active_window_border1()); - text_area.shrink(2, 2); - painter.fill_rect(text_area, background_color); - - auto connector_top_left = Gfx::IntPoint { rect().width() / 2 - 5, text_area.height() + 1 }; - auto connector_top_right = Gfx::IntPoint { rect().width() / 2 + 5, text_area.height() + 1 }; - auto connector_bottom = Gfx::IntPoint { rect().width() / 2 + 10, rect().height() }; - painter.draw_triangle(connector_top_left, connector_top_right, connector_bottom, background_color); - painter.draw_line(connector_top_left, Gfx::IntPoint { connector_bottom.x() - 1, connector_bottom.y() }, palette().active_window_border1()); - painter.draw_line(connector_top_right, connector_bottom, palette().active_window_border1()); - - auto& message_list = m_cat_dog->is_artist() ? artist_messages : (m_cat_dog->is_inspector() ? inspector_messages : default_messages); - auto message = message_list[get_random() % message_list.size()]; - painter.draw_text(text_area, message, Gfx::TextAlignment::Center); -} - -void SpeechBubble::mousedown_event(GUI::MouseEvent& event) -{ - if (event.button() != GUI::MouseButton::Primary) - return; - if (on_dismiss) - on_dismiss(); -} diff --git a/Userland/Demos/CatDog/SpeechBubble.h b/Userland/Demos/CatDog/SpeechBubble.h deleted file mode 100644 index 087c6c1dd78..00000000000 --- a/Userland/Demos/CatDog/SpeechBubble.h +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright (c) 2021, Gunnar Beutner - * Copyright (c) 2022, kleines Filmröllchen - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include "CatDog.h" -#include -#include - -class SpeechBubble final : public GUI::Widget { - C_OBJECT(SpeechBubble); - -public: - virtual void paint_event(GUI::PaintEvent&) override; - virtual void mousedown_event(GUI::MouseEvent&) override; - - Function on_dismiss; - NonnullRefPtr m_cat_dog; - -private: - SpeechBubble(NonnullRefPtr cat_dog) - : m_cat_dog(move(cat_dog)) - { - } -}; diff --git a/Userland/Demos/CatDog/main.cpp b/Userland/Demos/CatDog/main.cpp deleted file mode 100644 index 8f32622d213..00000000000 --- a/Userland/Demos/CatDog/main.cpp +++ /dev/null @@ -1,106 +0,0 @@ -/* - * Copyright (c) 2021, Richard Gráčik - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "CatDog.h" -#include "SpeechBubble.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -ErrorOr serenity_main(Main::Arguments arguments) -{ - TRY(Core::System::pledge("stdio recvfd sendfd rpath wpath cpath unix")); - - auto app = TRY(GUI::Application::create(arguments)); - auto app_icon = TRY(GUI::Icon::try_create_default_icon("app-catdog"sv)); - - auto catdog_icon_sleep = TRY(Gfx::Bitmap::load_from_file("/res/icons/16x16/catdog-sleeping.png"sv)); - auto catdog_icon_wake = TRY(Gfx::Bitmap::load_from_file("/res/icons/16x16/catdog-wake-up.png"sv)); - - TRY(Core::System::pledge("stdio recvfd sendfd rpath")); - TRY(Core::System::unveil("/res", "r")); - TRY(Core::System::unveil("/sys/kernel/processes", "r")); - // FIXME: For some reason, this is needed in the /sys/kernel/processes shenanigans. - TRY(Core::System::unveil("/etc/passwd", "r")); - TRY(Core::System::unveil(nullptr, nullptr)); - - auto window = GUI::Window::construct(); - window->set_title("CatDog Demo"); - window->resize(32, 32); - window->set_frameless(true); - window->set_resizable(false); - window->set_has_alpha_channel(true); - window->set_alpha_hit_threshold(1.0f); - window->set_icon(app_icon.bitmap_for_size(16)); - - auto catdog_widget = TRY(CatDog::create()); - window->set_main_widget(catdog_widget); - catdog_widget->set_layout(GUI::Margins {}, 0); - - auto context_menu = GUI::Menu::construct(); - context_menu->add_action(GUI::CommonActions::make_about_action("CatDog Demo"_string, app_icon, window)); - context_menu->add_action(GUI::Action::create("Put CatDog to sleep...", catdog_icon_sleep, [&](GUI::Action& action) { - catdog_widget->set_sleeping(!catdog_widget->is_sleeping()); - - action.set_text(catdog_widget->is_sleeping() ? "Wake CatDog..." : "Put CatDog to sleep..."); - action.set_icon(catdog_widget->is_sleeping() ? catdog_icon_wake : catdog_icon_sleep); - })); - context_menu->add_separator(); - context_menu->add_action(GUI::CommonActions::make_quit_action([&](auto&) { app->quit(); })); - - window->show(); - window->set_always_on_top(); - catdog_widget->start_timer(250, Core::TimerShouldFireWhenNotVisible::Yes); - - auto advice_window = GUI::Window::construct(); - advice_window->set_title("CatDog Advice"); - advice_window->resize(225, 50); - advice_window->set_frameless(true); - advice_window->set_resizable(false); - advice_window->set_has_alpha_channel(true); - advice_window->set_alpha_hit_threshold(1.0f); - - auto advice_widget = advice_window->set_main_widget(catdog_widget); - advice_widget->set_layout(GUI::Margins {}, 0); - - auto advice_timer = Core::Timer::create_single_shot(15'000, [&] { - window->move_to_front(); - advice_window->move_to_front(); - catdog_widget->set_roaming(false); - advice_window->move_to(window->x() - advice_window->width() / 2, window->y() - advice_window->height()); - advice_window->show(); - advice_window->set_always_on_top(); - }); - advice_timer->start(); - - advice_widget->on_dismiss = [&] { - catdog_widget->set_roaming(true); - advice_window->hide(); - advice_timer->start(); - }; - - // Let users toggle the advice functionality by clicking on catdog. - catdog_widget->on_click = [&] { - if (advice_timer->is_active()) - advice_timer->stop(); - else - advice_timer->start(); - }; - - catdog_widget->on_context_menu_request = [&](GUI::ContextMenuEvent& event) { - if (catdog_widget->rect().contains(event.position())) - context_menu->popup(event.screen_position()); - }; - - return app->exec(); -} diff --git a/Userland/Demos/Eyes/CMakeLists.txt b/Userland/Demos/Eyes/CMakeLists.txt deleted file mode 100644 index d16e83e7a0c..00000000000 --- a/Userland/Demos/Eyes/CMakeLists.txt +++ /dev/null @@ -1,12 +0,0 @@ -serenity_component( - Eyes - TARGETS Eyes -) - -set(SOURCES - main.cpp - EyesWidget.cpp -) - -serenity_app(Eyes ICON app-eyes) -target_link_libraries(Eyes PRIVATE LibCore LibDesktop LibGUI LibGfx LibMain LibURL) diff --git a/Userland/Demos/Eyes/EyesWidget.cpp b/Userland/Demos/Eyes/EyesWidget.cpp deleted file mode 100644 index cf4e97eeb78..00000000000 --- a/Userland/Demos/Eyes/EyesWidget.cpp +++ /dev/null @@ -1,104 +0,0 @@ -/* - * Copyright (c) 2020, Sergey Bugaev - * Copyright (c) 2022, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "EyesWidget.h" -#include -#include -#include -#include - -void EyesWidget::track_mouse_move(Gfx::IntPoint point) -{ - m_mouse_position = point - window()->position(); - update(); -} - -void EyesWidget::paint_event(GUI::PaintEvent& event) -{ - GUI::Painter painter(*this); - Gfx::AntiAliasingPainter aa_painter { painter }; - - painter.clear_rect(event.rect(), Gfx::Color()); - - for (int i = 0; i < m_full_rows; i++) { - for (int j = 0; j < m_eyes_in_row; j++) - render_eyeball(i, j, aa_painter); - } - for (int i = 0; i < m_extra_columns; ++i) - render_eyeball(m_full_rows, i, aa_painter); -} - -void EyesWidget::render_eyeball(int row, int column, Gfx::AntiAliasingPainter& painter) const -{ - auto eye_width = width() / m_eyes_in_row; - auto eye_height = height() / m_num_rows; - Gfx::IntRect bounds { column * eye_width, row * eye_height, eye_width, eye_height }; - auto width_thickness = max(int(eye_width / 5.5), 1); - auto height_thickness = max(int(eye_height / 5.5), 1); - - bounds.shrink(int(eye_width / 12.5), 0); - painter.fill_ellipse(bounds, palette().base_text()); - bounds.shrink(width_thickness, height_thickness); - painter.fill_ellipse(bounds, palette().base()); - - Gfx::IntPoint pupil_center = this->pupil_center(bounds); - Gfx::IntSize pupil_size { - bounds.width() / 5, - bounds.height() / 5 - }; - Gfx::IntRect pupil { - pupil_center.x() - pupil_size.width() / 2, - pupil_center.y() - pupil_size.height() / 2, - pupil_size.width(), - pupil_size.height() - }; - - painter.fill_ellipse(pupil, palette().base_text()); -} - -Gfx::IntPoint EyesWidget::pupil_center(Gfx::IntRect& eyeball_bounds) const -{ - auto mouse_vector = m_mouse_position - eyeball_bounds.center(); - double dx = mouse_vector.x(); - double dy = mouse_vector.y(); - double mouse_distance = AK::hypot(dx, dy); - - if (mouse_distance == 0.0) - return eyeball_bounds.center(); - - double width_squared = eyeball_bounds.width() * eyeball_bounds.width(); - double height_squared = eyeball_bounds.height() * eyeball_bounds.height(); - - double max_distance_along_this_direction; - - // clang-format off - if (dx != 0 && AK::abs(dx) >= AK::abs(dy)) { - double slope = dy / dx; - double slope_squared = slope * slope; - max_distance_along_this_direction = 0.25 * AK::sqrt( - (slope_squared + 1) / - (1 / width_squared + slope_squared / height_squared) - ); - } else if (dy != 0 && AK::abs(dy) >= AK::abs(dx)) { - double slope = dx / dy; - double slope_squared = slope * slope; - max_distance_along_this_direction = 0.25 * AK::sqrt( - (slope_squared + 1) / - (slope_squared / width_squared + 1 / height_squared) - ); - } else { - VERIFY_NOT_REACHED(); - } - // clang-format on - - double scale = min(1.0, max_distance_along_this_direction / mouse_distance); - - return { - eyeball_bounds.center().x() + int(dx * scale), - eyeball_bounds.center().y() + int(dy * scale) - }; -} diff --git a/Userland/Demos/Eyes/EyesWidget.h b/Userland/Demos/Eyes/EyesWidget.h deleted file mode 100644 index 6e03adeae00..00000000000 --- a/Userland/Demos/Eyes/EyesWidget.h +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright (c) 2020, Sergey Bugaev - * Copyright (c) 2022, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include - -class EyesWidget final : public GUI::Widget - , GUI::MouseTracker { - C_OBJECT(EyesWidget) - -public: - virtual ~EyesWidget() override = default; - - Function on_context_menu_request; - -protected: - virtual void context_menu_event(GUI::ContextMenuEvent& event) override - { - if (on_context_menu_request) - on_context_menu_request(event); - } - -private: - EyesWidget(int num_eyes, int full_rows, int extra) - : m_full_rows(full_rows) - , m_extra_columns(extra) - { - m_num_rows = m_extra_columns > 0 ? m_full_rows + 1 : m_full_rows; - m_eyes_in_row = m_full_rows > 0 ? (num_eyes - m_extra_columns) / m_full_rows : m_extra_columns; - } - - virtual void paint_event(GUI::PaintEvent&) override; - virtual void track_mouse_move(Gfx::IntPoint) override; - - void render_eyeball(int row, int column, Gfx::AntiAliasingPainter& aa_painter) const; - Gfx::IntPoint pupil_center(Gfx::IntRect& eyeball_bounds) const; - - Gfx::IntPoint m_mouse_position; - int m_eyes_in_row { -1 }; - int m_full_rows { -1 }; - int m_extra_columns { -1 }; - int m_num_rows { -1 }; -}; diff --git a/Userland/Demos/Eyes/main.cpp b/Userland/Demos/Eyes/main.cpp deleted file mode 100644 index 40946232292..00000000000 --- a/Userland/Demos/Eyes/main.cpp +++ /dev/null @@ -1,107 +0,0 @@ -/* - * Copyright (c) 2020, Sergey Bugaev - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "EyesWidget.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -ErrorOr serenity_main(Main::Arguments arguments) -{ - int num_eyes = 2; - int max_in_row = 13; - - // Alternatively, allow the user to ask for a grid. - int grid_rows = -1; - int grid_columns = -1; - - bool hide_window_frame = false; - - Core::ArgsParser args_parser; - args_parser.add_option(num_eyes, "Number of eyes", "num-eyes", 'n', "number"); - args_parser.add_option(max_in_row, "Maximum number of eyes in a row", "max-in-row", 'm', "number"); - args_parser.add_option(grid_rows, "Number of rows in grid (incompatible with --number)", "grid-rows", 'r', "number"); - args_parser.add_option(grid_columns, "Number of columns in grid (incompatible with --number)", "grid-cols", 'c', "number"); - args_parser.add_option(hide_window_frame, "Hide window frame", "hide-window", 'h'); - args_parser.parse(arguments); - - TRY(Core::System::pledge("stdio recvfd sendfd rpath unix cpath wpath thread")); - - auto app = TRY(GUI::Application::create(arguments)); - - TRY(Core::System::unveil("/tmp/session/%sid/portal/launch", "rw")); - TRY(Core::System::unveil("/res", "r")); - TRY(Core::System::unveil(nullptr, nullptr)); - - if ((grid_rows > 0) ^ (grid_columns > 0)) { - warnln("Expected either both or none of 'grid-rows' and 'grid-cols' to be passed."); - return 1; - } - - int full_rows, extra_columns; - - if (grid_rows > 0) { - full_rows = grid_rows; - extra_columns = 0; - num_eyes = grid_rows * grid_columns; - max_in_row = grid_columns; - } else { - full_rows = num_eyes / max_in_row; - extra_columns = num_eyes % max_in_row; - } - - auto app_icon = TRY(GUI::Icon::try_create_default_icon("app-eyes"sv)); - - auto window = GUI::Window::construct(); - window->set_title("Eyes"); - window->set_icon(app_icon.bitmap_for_size(16)); - window->resize(75 * (full_rows > 0 ? max_in_row : extra_columns), 100 * (full_rows + (extra_columns > 0 ? 1 : 0))); - window->set_has_alpha_channel(true); - - bool window_frame_enabled = true; - auto set_window_frame_enabled = [&](bool enable) { - if (enable == window_frame_enabled) - return; - window_frame_enabled = enable; - window->set_frameless(!window_frame_enabled); - window->set_alpha_hit_threshold(window_frame_enabled ? 0 : 1); - }; - - auto show_window_frame_action = GUI::Action::create_checkable("Show Window &Frame", [&](auto& action) { - set_window_frame_enabled(action.is_checked()); - }); - set_window_frame_enabled(!hide_window_frame); - show_window_frame_action->set_checked(window_frame_enabled); - - auto file_menu = window->add_menu("&File"_string); - file_menu->add_action(move(show_window_frame_action)); - file_menu->add_separator(); - file_menu->add_action(GUI::CommonActions::make_quit_action([&](auto&) { app->quit(); })); - - auto help_menu = window->add_menu("&Help"_string); - help_menu->add_action(GUI::CommonActions::make_command_palette_action(window)); - help_menu->add_action(GUI::CommonActions::make_help_action([](auto&) { - Desktop::Launcher::open(URL::create_with_file_scheme("/usr/share/man/man1/Applications/Eyes.md"), "/bin/Help"); - })); - help_menu->add_action(GUI::CommonActions::make_about_action("Eyes Demo"_string, app_icon, window)); - - auto eyes_widget = window->set_main_widget(num_eyes, full_rows, extra_columns); - eyes_widget->on_context_menu_request = [&](auto& event) { - file_menu->popup(event.screen_position()); - }; - - window->show(); - - return app->exec(); -} diff --git a/Userland/Demos/Gradient/CMakeLists.txt b/Userland/Demos/Gradient/CMakeLists.txt deleted file mode 100644 index 2a550d166a9..00000000000 --- a/Userland/Demos/Gradient/CMakeLists.txt +++ /dev/null @@ -1,11 +0,0 @@ -serenity_component( - Gradient - TARGETS Gradient -) - -set(SOURCES - Gradient.cpp -) - -serenity_app(Gradient ICON app-gradient) -target_link_libraries(Gradient PRIVATE LibDesktop LibGUI LibCore LibGfx LibMain) diff --git a/Userland/Demos/Gradient/Gradient.cpp b/Userland/Demos/Gradient/Gradient.cpp deleted file mode 100644 index 56168c6057b..00000000000 --- a/Userland/Demos/Gradient/Gradient.cpp +++ /dev/null @@ -1,111 +0,0 @@ -/* - * Copyright (c) 2020-2022, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -class Gradient final : public Desktop::Screensaver { - C_OBJECT(Gradient) -public: - virtual ~Gradient() override = default; - -private: - Gradient(int width = 64, int height = 48, int interval = 10000); - RefPtr m_bitmap; - - void draw(); - virtual void paint_event(GUI::PaintEvent&) override; - virtual void timer_event(Core::TimerEvent&) override; -}; - -Gradient::Gradient(int width, int height, int interval) -{ - on_screensaver_exit = []() { GUI::Application::the()->quit(); }; - m_bitmap = Gfx::Bitmap::create(Gfx::BitmapFormat::BGRx8888, { width, height }).release_value_but_fixme_should_propagate_errors(); - srand(time(nullptr)); - stop_timer(); - start_timer(interval); - draw(); -} - -void Gradient::paint_event(GUI::PaintEvent& event) -{ - GUI::Painter painter(*this); - painter.add_clip_rect(event.rect()); - painter.draw_scaled_bitmap(rect(), *m_bitmap, m_bitmap->rect()); -} - -void Gradient::timer_event(Core::TimerEvent&) -{ - draw(); - update(); -} - -void Gradient::draw() -{ - Color const colors[] { - Color::Blue, - Color::Cyan, - Color::Green, - Color::Magenta, - Color::Red, - Color::Yellow, - }; - - Orientation const orientations[] { - Gfx::Orientation::Horizontal, - Gfx::Orientation::Vertical - }; - - int start_color_index = 0; - int end_color_index = 0; - while (start_color_index == end_color_index) { - start_color_index = rand() % (sizeof(colors) / sizeof(colors[0])); - end_color_index = rand() % (sizeof(colors) / sizeof(colors[0])); - } - - GUI::Painter painter(*m_bitmap); - painter.fill_rect_with_gradient( - orientations[rand() % (sizeof(orientations) / sizeof(orientations[0]))], - m_bitmap->rect(), - colors[start_color_index], - colors[end_color_index]); -} - -ErrorOr serenity_main(Main::Arguments arguments) -{ - TRY(Core::System::pledge("stdio rpath recvfd sendfd unix")); - - auto app = TRY(GUI::Application::create(arguments)); - - TRY(Core::System::pledge("stdio rpath recvfd sendfd")); - TRY(Core::System::unveil("/res", "r")); - TRY(Core::System::unveil(nullptr, nullptr)); - - auto window = TRY(Desktop::Screensaver::create_window("Gradient"sv, "app-gradient"sv)); - - auto gradient_widget = window->set_main_widget(64, 48, 10000); - gradient_widget->set_fill_with_background_color(false); - gradient_widget->set_override_cursor(Gfx::StandardCursor::Hidden); - gradient_widget->update(); - - window->show(); - window->move_to_front(); - window->set_cursor(Gfx::StandardCursor::Hidden); - window->update(); - - return app->exec(); -} diff --git a/Userland/Demos/LibGfxDemo/CMakeLists.txt b/Userland/Demos/LibGfxDemo/CMakeLists.txt deleted file mode 100644 index 890a5b00448..00000000000 --- a/Userland/Demos/LibGfxDemo/CMakeLists.txt +++ /dev/null @@ -1,11 +0,0 @@ -serenity_component( - LibGfxDemo - TARGETS LibGfxDemo -) - -set(SOURCES - main.cpp -) - -serenity_app(LibGfxDemo ICON app-libgfx-demo) -target_link_libraries(LibGfxDemo PRIVATE LibGUI LibIPC LibGfx LibCore LibMain) diff --git a/Userland/Demos/LibGfxDemo/main.cpp b/Userland/Demos/LibGfxDemo/main.cpp deleted file mode 100644 index 90714c367c2..00000000000 --- a/Userland/Demos/LibGfxDemo/main.cpp +++ /dev/null @@ -1,214 +0,0 @@ -/* - * Copyright (c) 2020, Linus Groh - * Copyright (c) 2022, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -int const WIDTH = 780; -int const HEIGHT = 600; - -class Canvas final : public GUI::Widget { - C_OBJECT(Canvas) -public: - virtual ~Canvas() override = default; - -private: - Canvas(); - RefPtr m_bitmap; - - void draw(); - virtual void paint_event(GUI::PaintEvent&) override; -}; - -Canvas::Canvas() -{ - m_bitmap = Gfx::Bitmap::create(Gfx::BitmapFormat::BGRx8888, { WIDTH, HEIGHT }).release_value_but_fixme_should_propagate_errors(); - draw(); -} - -void Canvas::paint_event(GUI::PaintEvent& event) -{ - GUI::Painter painter(*this); - painter.add_clip_rect(event.rect()); - painter.draw_scaled_bitmap(rect(), *m_bitmap, m_bitmap->rect()); -} - -void Canvas::draw() -{ - GUI::Painter painter(*m_bitmap); - - painter.fill_rect({ 20, 20, 100, 100 }, Color::Magenta); - painter.draw_rect({ 20, 140, 100, 100 }, Color::Yellow); - - painter.fill_rect_with_gradient(Gfx::Orientation::Horizontal, { 140, 20, 100, 100 }, Color::Yellow, Color::DarkGreen); - painter.fill_rect_with_gradient(Gfx::Orientation::Vertical, { 140, 140, 100, 100 }, Color::Red, Color::Blue); - - painter.fill_rect_with_dither_pattern({ 260, 20, 100, 100 }, Color::MidGray, Color::Black); - painter.fill_rect_with_checkerboard({ 260, 140, 100, 100 }, { 10, 10 }, Color::LightGray, Color::White); - - painter.draw_line({ 430, 35 }, { 465, 70 }, Color::Green); - painter.draw_line({ 465, 70 }, { 430, 105 }, Color::Green); - painter.draw_line({ 430, 105 }, { 395, 70 }, Color::Green); - painter.draw_line({ 395, 70 }, { 430, 35 }, Color::Green); - painter.draw_rect({ 395, 35, 70, 70 }, Color::Blue); - painter.draw_ellipse_intersecting({ 395, 35, 70, 70 }, Color::Red); - painter.draw_rect({ 380, 20, 100, 100 }, Color::Yellow); - - painter.fill_rect({ 380, 140, 100, 100 }, Color::Blue); - painter.draw_triangle({ 430, 140 }, { 380, 140 }, { 380, 240 }, Color::Green); - painter.draw_triangle({ 430, 240 }, { 480, 140 }, { 480, 240 }, Color::Red); - painter.draw_rect({ 380, 140, 100, 100 }, Color::Yellow); - - painter.draw_line({ 500, 20 }, { 750, 20 }, Color::Green, 1, Gfx::Painter::LineStyle::Solid); - painter.draw_line({ 500, 30 }, { 750, 30 }, Color::Red, 5, Gfx::Painter::LineStyle::Solid); - painter.draw_line({ 500, 45 }, { 750, 45 }, Color::Blue, 10, Gfx::Painter::LineStyle::Solid); - - painter.draw_line({ 500, 60 }, { 750, 60 }, Color::Green, 1, Gfx::Painter::LineStyle::Dotted); - painter.draw_line({ 500, 70 }, { 750, 70 }, Color::Red, 5, Gfx::Painter::LineStyle::Dotted); - painter.draw_line({ 500, 85 }, { 750, 85 }, Color::Blue, 10, Gfx::Painter::LineStyle::Dotted); - - painter.draw_line({ 500, 100 }, { 750, 100 }, Color::Green, 1, Gfx::Painter::LineStyle::Dashed); - painter.draw_line({ 500, 110 }, { 750, 110 }, Color::Red, 5, Gfx::Painter::LineStyle::Dashed); - painter.draw_line({ 500, 125 }, { 750, 125 }, Color::Blue, 10, Gfx::Painter::LineStyle::Dashed); - - painter.draw_line({ 500, 140 }, { 500, 240 }, Color::Green, 1, Gfx::Painter::LineStyle::Solid); - painter.draw_line({ 510, 140 }, { 510, 240 }, Color::Red, 5, Gfx::Painter::LineStyle::Solid); - painter.draw_line({ 525, 140 }, { 525, 240 }, Color::Blue, 10, Gfx::Painter::LineStyle::Solid); - - painter.draw_line({ 540, 140 }, { 540, 240 }, Color::Green, 1, Gfx::Painter::LineStyle::Dotted); - painter.draw_line({ 550, 140 }, { 550, 240 }, Color::Red, 5, Gfx::Painter::LineStyle::Dotted); - painter.draw_line({ 565, 140 }, { 565, 240 }, Color::Blue, 10, Gfx::Painter::LineStyle::Dotted); - - painter.draw_line({ 580, 140 }, { 580, 240 }, Color::Green, 1, Gfx::Painter::LineStyle::Dashed); - painter.draw_line({ 590, 140 }, { 590, 240 }, Color::Red, 5, Gfx::Painter::LineStyle::Dashed); - painter.draw_line({ 605, 140 }, { 605, 240 }, Color::Blue, 10, Gfx::Painter::LineStyle::Dashed); - - painter.draw_line({ 640, 190 }, { 740, 240 }, Color::Green, 1, Gfx::Painter::LineStyle::Dashed); - painter.draw_line({ 640, 140 }, { 740, 240 }, Color::Red, 5, Gfx::Painter::LineStyle::Solid); - painter.draw_line({ 690, 140 }, { 740, 240 }, Color::Blue, 10, Gfx::Painter::LineStyle::Solid); - painter.draw_line({ 740, 190 }, { 640, 240 }, Color::Green, 1, Gfx::Painter::LineStyle::Dotted); - painter.draw_line({ 740, 140 }, { 640, 240 }, Color::Red, 5, Gfx::Painter::LineStyle::Solid); - painter.draw_line({ 690, 140 }, { 640, 240 }, Color::Blue, 10, Gfx::Painter::LineStyle::Solid); - - auto bg = Gfx::Bitmap::load_from_file("/res/html/misc/90s-bg.png"sv).release_value_but_fixme_should_propagate_errors(); - painter.draw_tiled_bitmap({ 20, 260, 480, 320 }, *bg); - - painter.draw_line({ 40, 480 }, { 20, 260 }, Color::Red); - painter.draw_line({ 40, 480 }, { 120, 300 }, Color::Red); - painter.draw_quadratic_bezier_curve({ 40, 480 }, { 20, 260 }, { 120, 300 }, Color::Blue); - - painter.draw_line({ 240, 280 }, { 80, 420 }, Color::Red, 3); - painter.draw_line({ 240, 280 }, { 260, 360 }, Color::Red, 3); - painter.draw_quadratic_bezier_curve({ 240, 280 }, { 80, 420 }, { 260, 360 }, Color::Blue, 3); - - auto path = Gfx::Path(); - path.move_to({ 60, 500 }); - path.line_to({ 90, 540 }); - path.quadratic_bezier_curve_to({ 320, 500 }, { 220, 400 }); - path.line_to({ 300, 440 }); - path.line_to({ 90, 460 }); - path.elliptical_arc_to({ 260, 540 }, { 40, 30 }, 0, true, false); - path.close(); - painter.fill_path(path, Color::Yellow, Gfx::Painter::WindingRule::EvenOdd); - - auto buggie = Gfx::Bitmap::load_from_file("/res/graphics/buggie.png"sv).release_value_but_fixme_should_propagate_errors(); - painter.blit({ 280, 280 }, *buggie, buggie->rect(), 0.5); - painter.draw_scaled_bitmap({ 360, 280, buggie->rect().width() * 2, buggie->rect().height() * 2 }, *buggie, buggie->rect()); - - painter.draw_rect({ 20, 260, 480, 320 }, Color::DarkGray); - - painter.draw_rect({ 520, 260, 240, 80 }, Color::DarkGray); - painter.draw_text(Gfx::IntRect { 520, 260, 240, 80 }, "CenterLeft"sv, Gfx::TextAlignment::CenterLeft, Color::White); - painter.draw_text(Gfx::IntRect { 520, 260, 240, 80 }, "Center"sv, Gfx::TextAlignment::Center, Color::White); - painter.draw_text(Gfx::IntRect { 520, 260, 240, 80 }, "CenterRight"sv, Gfx::TextAlignment::CenterRight, Color::White); - painter.draw_text(Gfx::IntRect { 520, 260, 240, 80 }, "TopLeft"sv, Gfx::TextAlignment::TopLeft, Color::White); - painter.draw_text(Gfx::IntRect { 520, 260, 240, 80 }, "TopCenter"sv, Gfx::TextAlignment::TopCenter, Color::White); - painter.draw_text(Gfx::IntRect { 520, 260, 240, 80 }, "TopRight"sv, Gfx::TextAlignment::TopRight, Color::White); - painter.draw_text(Gfx::IntRect { 520, 260, 240, 80 }, "BottomLeft"sv, Gfx::TextAlignment::BottomLeft, Color::White); - painter.draw_text(Gfx::IntRect { 520, 260, 240, 80 }, "BottomRight"sv, Gfx::TextAlignment::BottomRight, Color::White); - painter.draw_text(Gfx::IntRect { 520, 260, 240, 80 }, "BottomCenter"sv, Gfx::TextAlignment::BottomCenter, Color::White); - - painter.draw_rect({ 520, 360, 240, 30 }, Color::DarkGray); - painter.draw_text(Gfx::IntRect { 520, 360, 240, 30 }, "Emojis! 🙂😂🐞🦄"sv, Gfx::TextAlignment::Center, Color::White); - - painter.draw_rect({ 520, 410, 240, 80 }, Color::DarkGray); - painter.draw_text(Gfx::IntRect { 520, 415, 240, 20 }, "Normal text"sv, Gfx::FontDatabase::default_font(), Gfx::TextAlignment::CenterLeft, Color::Red); - painter.draw_text(Gfx::IntRect { 520, 430, 240, 20 }, "Bold text"sv, Gfx::FontDatabase::default_font().bold_variant(), Gfx::TextAlignment::CenterLeft, Color::Green); - painter.draw_text(Gfx::IntRect { 520, 450, 240, 20 }, "Normal text (fixed width)"sv, Gfx::FontDatabase::default_fixed_width_font(), Gfx::TextAlignment::CenterLeft, Color::Blue); - painter.draw_text(Gfx::IntRect { 520, 465, 240, 20 }, "Bold text (fixed width)"sv, Gfx::FontDatabase::default_fixed_width_font().bold_variant(), Gfx::TextAlignment::CenterLeft, Color::Yellow); - - auto font = Gfx::BitmapFont::load_from_uri("resource://fonts/PebbletonBold14.font"sv); - painter.draw_rect({ 520, 510, 240, 30 }, Color::DarkGray); - painter.draw_text(Gfx::IntRect { 520, 510, 240, 30 }, "Hello friends! :^)"sv, *font, Gfx::TextAlignment::Center, Color::White); - - painter.fill_rect({ 520, 560, 10, 20 }, Color::White); - painter.fill_rect({ 530, 560, 10, 20 }, Color::WarmGray); - painter.fill_rect({ 540, 560, 10, 20 }, Color::LightGray); - painter.fill_rect({ 550, 560, 10, 20 }, Color::MidGray); - painter.fill_rect({ 560, 560, 10, 20 }, Color::DarkGray); - painter.fill_rect({ 570, 560, 10, 20 }, Color::Black); - painter.fill_rect({ 580, 560, 10, 20 }, Color::Blue); - painter.fill_rect({ 590, 560, 10, 20 }, Color::MidBlue); - painter.fill_rect({ 600, 560, 10, 20 }, Color::DarkBlue); - painter.fill_rect({ 610, 560, 10, 20 }, Color::Cyan); - painter.fill_rect({ 620, 560, 10, 20 }, Color::MidCyan); - painter.fill_rect({ 630, 560, 10, 20 }, Color::DarkCyan); - painter.fill_rect({ 640, 560, 10, 20 }, Color::Green); - painter.fill_rect({ 650, 560, 10, 20 }, Color::MidGreen); - painter.fill_rect({ 660, 560, 10, 20 }, Color::DarkGreen); - painter.fill_rect({ 670, 560, 10, 20 }, Color::Yellow); - painter.fill_rect({ 680, 560, 10, 20 }, Color::Red); - painter.fill_rect({ 690, 560, 10, 20 }, Color::MidRed); - painter.fill_rect({ 700, 560, 10, 20 }, Color::DarkRed); - painter.fill_rect({ 710, 560, 10, 20 }, Color::Magenta); - painter.fill_rect({ 720, 560, 10, 20 }, Color::MidMagenta); - - update(); -} - -ErrorOr serenity_main(Main::Arguments arguments) -{ - auto app = TRY(GUI::Application::create(arguments)); - - TRY(Core::System::pledge("stdio recvfd sendfd rpath")); - TRY(Core::System::unveil("/res", "r")); - TRY(Core::System::unveil(nullptr, nullptr)); - - auto window = GUI::Window::construct(); - window->set_double_buffering_enabled(true); - window->set_title("LibGfx Demo"); - window->set_resizable(false); - window->resize(WIDTH, HEIGHT); - - auto file_menu = window->add_menu("&File"_string); - file_menu->add_action(GUI::CommonActions::make_quit_action([&](auto&) { app->quit(); })); - - auto view_menu = window->add_menu("&View"_string); - view_menu->add_action(GUI::CommonActions::make_fullscreen_action([&](auto&) { - window->set_fullscreen(!window->is_fullscreen()); - })); - - auto app_icon = TRY(GUI::Icon::try_create_default_icon("app-libgfx-demo"sv)); - window->set_icon(app_icon.bitmap_for_size(16)); - (void)window->set_main_widget(); - window->show(); - - return app->exec(); -} diff --git a/Userland/Demos/LibGfxScaleDemo/CMakeLists.txt b/Userland/Demos/LibGfxScaleDemo/CMakeLists.txt deleted file mode 100644 index 76695d3f4a1..00000000000 --- a/Userland/Demos/LibGfxScaleDemo/CMakeLists.txt +++ /dev/null @@ -1,11 +0,0 @@ -serenity_component( - LibGfxScaleDemo - TARGETS LibGfxScaleDemo -) - -set(SOURCES - main.cpp -) - -serenity_app(LibGfxScaleDemo ICON app-libgfx-demo) -target_link_libraries(LibGfxScaleDemo PRIVATE LibGUI LibIPC LibGfx LibCore LibMain) diff --git a/Userland/Demos/LibGfxScaleDemo/main.cpp b/Userland/Demos/LibGfxScaleDemo/main.cpp deleted file mode 100644 index e4e061465cd..00000000000 --- a/Userland/Demos/LibGfxScaleDemo/main.cpp +++ /dev/null @@ -1,133 +0,0 @@ -/* - * Copyright (c) 2020, Nico Weber - * Copyright (c) 2022, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -int const WIDTH = 300; -int const HEIGHT = 200; - -class Canvas final : public GUI::Widget { - C_OBJECT(Canvas) -public: - virtual ~Canvas() override = default; - -private: - Canvas(); - RefPtr m_bitmap_1x; - RefPtr m_bitmap_2x; - RefPtr m_bitmap_2x_as_1x; - - void draw(Gfx::Painter& painter); - virtual void paint_event(GUI::PaintEvent&) override; -}; - -Canvas::Canvas() -{ - m_bitmap_1x = Gfx::Bitmap::create(Gfx::BitmapFormat::BGRA8888, { WIDTH, HEIGHT }, 1).release_value_but_fixme_should_propagate_errors(); - m_bitmap_2x = Gfx::Bitmap::create(Gfx::BitmapFormat::BGRA8888, { WIDTH, HEIGHT }, 2).release_value_but_fixme_should_propagate_errors(); - - // m_bitmap_1x and m_bitmap_2x have the same logical size, so LibGfx will try to draw them at the same physical size: - // When drawing on a 2x backing store it'd scale m_bitmap_1x up 2x and paint m_bitmap_2x at its physical size. - // When drawing on a 1x backing store it'd draw m_bitmap_1x at its physical size, and it would have to scale down m_bitmap_2x to 0.5x its size. - // But the system can't current scale down, and we want to draw the 2x bitmap at twice the size of the 1x bitmap in this particular application, - // so make a 1x alias of the 2x bitmap to make LibGfx paint it without any scaling at paint time, mapping once pixel to one pixel. - m_bitmap_2x_as_1x = Gfx::Bitmap::create_wrapper(Gfx::BitmapFormat::BGRA8888, m_bitmap_2x->physical_size(), 1, m_bitmap_2x->pitch(), m_bitmap_2x->scanline(0)).release_value_but_fixme_should_propagate_errors(); - - Gfx::Painter painter_1x(*m_bitmap_1x); - draw(painter_1x); - - Gfx::Painter painter_2x(*m_bitmap_2x); - draw(painter_2x); - - update(); -} - -void Canvas::paint_event(GUI::PaintEvent& event) -{ - GUI::Painter painter(*this); - painter.add_clip_rect(event.rect()); - painter.fill_rect(event.rect(), Color::Magenta); - painter.blit({ 0, 0 }, *m_bitmap_1x, m_bitmap_1x->rect()); - painter.blit({ 0, HEIGHT }, *m_bitmap_2x_as_1x, m_bitmap_2x_as_1x->rect()); -} - -void Canvas::draw(Gfx::Painter& painter) -{ - auto active_window_icon = Gfx::Bitmap::load_from_file("/res/icons/16x16/window.png"sv).release_value_but_fixme_should_propagate_errors(); - - Gfx::WindowTheme::current().paint_normal_frame(painter, Gfx::WindowTheme::WindowState::Active, Gfx::WindowTheme::WindowMode::Other, { 4, 18, WIDTH - 8, HEIGHT - 29 }, "Well hello friends 🐞"sv, *active_window_icon, palette(), { WIDTH - 20, 6, 16, 16 }, 0, false); - - painter.fill_rect({ 4, 25, WIDTH - 8, HEIGHT - 30 }, palette().color(Gfx::ColorRole::Background)); - painter.draw_rect({ 20, 34, WIDTH - 40, HEIGHT - 45 }, palette().color(Gfx::ColorRole::Selection), true); - painter.draw_rect({ 24, 38, WIDTH - 48, HEIGHT - 53 }, palette().color(Gfx::ColorRole::Selection)); - - // buggie.png has an alpha channel. - auto buggie = Gfx::Bitmap::load_from_file("/res/graphics/buggie.png"sv).release_value_but_fixme_should_propagate_errors(); - painter.blit({ 25, 39 }, *buggie, { 2, 30, 62, 20 }); - painter.draw_scaled_bitmap({ 88, 39, 62 * 2, 20 * 2 }, *buggie, Gfx::IntRect { 2, 30, 62, 20 }); - painter.draw_scaled_bitmap({ 202, 39, 80, 40 }, *buggie, Gfx::IntRect { 2, 30, 62, 20 }); - - painter.draw_tiled_bitmap({ 25, 60, WIDTH - 50, 40 }, *buggie); - - painter.blit({ 25, 101 }, *buggie, { 2, 30, 3 * buggie->width(), 20 }); - - // grid does not have an alpha channel. - auto grid = Gfx::Bitmap::load_from_file("/res/wallpapers/grid.png"sv).release_value_but_fixme_should_propagate_errors(); - VERIFY(!grid->has_alpha_channel()); - painter.fill_rect({ 25, 122, 62, 20 }, Color::Green); - painter.blit({ 25, 122 }, *grid, { (grid->width() - 62) / 2, (grid->height() - 20) / 2 + 40, 62, 20 }, 0.9); - - painter.blit_brightened({ 88, 122 }, *buggie, { 2, 30, 62, 20 }); - painter.blit_dimmed({ 140, 122 }, *buggie, { 2, 30, 62, 20 }); - painter.blit_disabled({ 192, 122 }, *buggie, { 2, 30, 62, 20 }, palette()); -} - -ErrorOr serenity_main(Main::Arguments arguments) -{ - auto app = TRY(GUI::Application::create(arguments)); - - TRY(Core::System::pledge("stdio recvfd sendfd rpath")); - TRY(Core::System::unveil("/res", "r")); - TRY(Core::System::unveil(nullptr, nullptr)); - - auto window = GUI::Window::construct(); - window->set_title("LibGfx Scale Demo"); - window->set_resizable(false); - window->resize(WIDTH * 2, HEIGHT * 3); - - auto file_menu = window->add_menu("&File"_string); - file_menu->add_action(GUI::CommonActions::make_quit_action([&](auto&) { app->quit(); })); - - auto view_menu = window->add_menu("&View"_string); - view_menu->add_action(GUI::CommonActions::make_fullscreen_action([&](auto&) { - window->set_fullscreen(!window->is_fullscreen()); - })); - - auto app_icon = TRY(GUI::Icon::try_create_default_icon("app-libgfx-demo"sv)); - window->set_icon(app_icon.bitmap_for_size(16)); - (void)window->set_main_widget(); - window->show(); - - return app->exec(); -} diff --git a/Userland/Demos/Mandelbrot/CMakeLists.txt b/Userland/Demos/Mandelbrot/CMakeLists.txt deleted file mode 100644 index b53a5f739ce..00000000000 --- a/Userland/Demos/Mandelbrot/CMakeLists.txt +++ /dev/null @@ -1,11 +0,0 @@ -serenity_component( - Mandelbrot - TARGETS Mandelbrot -) - -set(SOURCES - Mandelbrot.cpp -) - -serenity_app(Mandelbrot ICON app-mandelbrot) -target_link_libraries(Mandelbrot PRIVATE LibCore LibFileSystemAccessClient LibGfx LibGUI LibMain) diff --git a/Userland/Demos/Mandelbrot/Mandelbrot.cpp b/Userland/Demos/Mandelbrot/Mandelbrot.cpp deleted file mode 100644 index 103ebfd97f7..00000000000 --- a/Userland/Demos/Mandelbrot/Mandelbrot.cpp +++ /dev/null @@ -1,524 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * Copyright (c) 2021, Gunnar Beutner - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -class MandelbrotSet { -public: - MandelbrotSet() - { - set_view(); - } - - void reset() - { - set_view(); - correct_aspect(); - } - - void resize(Gfx::IntSize size) - { - m_bitmap = Gfx::Bitmap::create(Gfx::BitmapFormat::BGRx8888, size).release_value_but_fixme_should_propagate_errors(); - correct_aspect(); - } - - void zoom(Gfx::IntRect const& rect) - { - set_view( - rect.left() * (m_x_end - m_x_start) / m_bitmap->width() + m_x_start, - (rect.right() - 1) * (m_x_end - m_x_start) / m_bitmap->width() + m_x_start, - rect.top() * (m_y_end - m_y_start) / m_bitmap->height() + m_y_start, - (rect.bottom() - 1) * (m_y_end - m_y_start) / m_bitmap->height() + m_y_start); - correct_aspect(); - } - - void pan_by(Gfx::IntPoint delta) - { - auto relative_width_pixel = (m_x_end - m_x_start) / m_bitmap->width(); - auto relative_height_pixel = (m_y_end - m_y_start) / m_bitmap->height(); - - set_view( - m_x_start - delta.x() * relative_width_pixel, - m_x_end - delta.x() * relative_width_pixel, - m_y_start - delta.y() * relative_height_pixel, - m_y_end - delta.y() * relative_height_pixel); - - Gfx::IntRect horizontal_missing, vertical_missing; - if (delta.y() >= 0) { - horizontal_missing = { 0, 0, m_bitmap->width(), delta.y() }; - } else { - horizontal_missing = { 0, m_bitmap->height() + delta.y(), m_bitmap->width(), -delta.y() }; - } - - if (delta.x() >= 0) { - vertical_missing = { 0, 0, delta.x(), m_bitmap->height() }; - } else { - vertical_missing = { m_bitmap->width() + delta.x(), 0, -delta.x(), m_bitmap->height() }; - } - - move_contents_by(delta); - calculate_rect(horizontal_missing); - calculate_rect(vertical_missing); - } - - double mandelbrot(double px, double py, i32 max_iterations) - { - // Based on https://en.wikipedia.org/wiki/Plotting_algorithms_for_the_Mandelbrot_set - double const x0 = px * (m_x_end - m_x_start) / m_bitmap->width() + m_x_start; - double const y0 = py * (m_y_end - m_y_start) / m_bitmap->height() + m_y_start; - double x = 0; - double y = 0; - i32 iteration = 0; - double x2 = 0; - double y2 = 0; - - while (x2 + y2 <= 4 && iteration < max_iterations) { - y = 2 * x * y + y0; - x = x2 - y2 + x0; - x2 = x * x; - y2 = y * y; - iteration++; - } - - if (iteration == max_iterations) - return iteration; - - // Renormalized fractional iteration count from https://linas.org/art-gallery/escape/escape.html - // mu = n + 1 - log( log |Z(n)| ) / log(2) - auto lz = log(sqrt(x2 + y2)); - auto mu = iteration + 1 - log(lz) / log(2); - if (mu < 0) - mu = 0; - return mu; - } - - static double linear_interpolate(double v0, double v1, double t) - { - return v0 + t * (v1 - v0); - } - - void calculate_pixel(int px, int py, int max_iterations) - { - auto iterations = mandelbrot(px, py, max_iterations); - auto whole_iterations = floor(iterations); - auto partial_iterations = fmod(iterations, 1); - double hue1 = whole_iterations * 360.0 / max_iterations; - if (hue1 >= 360.0) - hue1 = 0.0; - double hue2 = (whole_iterations + 1) * 360.0 / max_iterations; - if (hue2 >= 360.0) - hue2 = 0.0; - double hue = linear_interpolate(hue1, hue2, partial_iterations); - double saturation = 1.0; - double value = iterations < max_iterations ? 1.0 : 0; - m_bitmap->set_pixel(px, py, Color::from_hsv(hue, saturation, value)); - } - - void calculate(int max_iterations = 100) - { - VERIFY(m_bitmap); - - calculate_rect(m_bitmap->rect(), max_iterations); - } - - void calculate_rect(Gfx::IntRect const& rect, int max_iterations = 100) - { - VERIFY(m_bitmap); - - if (rect.is_empty()) - return; - - for (int py = rect.top(); py < rect.bottom(); py++) - for (int px = rect.left(); px < rect.right(); px++) - calculate_pixel(px, py, max_iterations); - } - - Gfx::Bitmap const& bitmap() const - { - return *m_bitmap; - } - -private: - double m_x_start { 0 }; - double m_x_end { 0 }; - double m_y_start { 0 }; - double m_y_end { 0 }; - RefPtr m_bitmap; - - void set_view(double x_start = -2.5, double x_end = 1.0, double y_start = -1.75, double y_end = 1.75) - { - m_x_start = x_start; - m_x_end = x_end; - m_y_start = y_start; - m_y_end = y_end; - } - - void correct_aspect() - { - auto y_mid = m_y_start + (m_y_end - m_y_start) / 2; - auto aspect_corrected_y_length = (m_x_end - m_x_start) * m_bitmap->height() / m_bitmap->width(); - m_y_start = y_mid - aspect_corrected_y_length / 2; - m_y_end = y_mid + aspect_corrected_y_length / 2; - } - - void move_contents_by(Gfx::IntPoint delta) - { - // If we're moving down we paint upwards, else we paint downwards, to - // avoid overwriting. - if (delta.y() >= 0) { - for (int row = m_bitmap->physical_height() - 1; row >= delta.y(); row--) - move_row(row - delta.y(), row, delta.x()); - } else { - for (int row = 0; row < m_bitmap->physical_height() + delta.y(); row++) - move_row(row - delta.y(), row, delta.x()); - } - } - - void move_row(int from, int to, int x_delta) - { - // If we're moving right we paint RTL, else we paint LTR, to avoid - // overwriting. - if (x_delta >= 0) { - for (int column = m_bitmap->physical_width() - 1; column >= x_delta; column--) { - m_bitmap->set_pixel(column, to, m_bitmap->get_pixel(column - x_delta, from)); - } - } else { - for (int column = 0; column < m_bitmap->physical_width() + x_delta; column++) { - m_bitmap->set_pixel(column, to, m_bitmap->get_pixel(column - x_delta, from)); - } - } - } -}; - -enum class ImageType { - BMP, - PNG, - QOI -}; - -class Mandelbrot : public GUI::Frame { - C_OBJECT(Mandelbrot) - - ErrorOr export_image(Core::File& export_path, ImageType image_type); - - enum class Zoom { - In, - Out, - }; - void zoom(Zoom in_out, Gfx::IntPoint center); - - void reset(); - -private: - Mandelbrot() = default; - - virtual void paint_event(GUI::PaintEvent&) override; - virtual void keydown_event(GUI::KeyEvent& event) override; - virtual void keyup_event(GUI::KeyEvent& event) override; - virtual void mousedown_event(GUI::MouseEvent& event) override; - virtual void mousemove_event(GUI::MouseEvent& event) override; - virtual void mouseup_event(GUI::MouseEvent& event) override; - virtual void mousewheel_event(GUI::MouseEvent& event) override; - virtual void resize_event(GUI::ResizeEvent& event) override; - - bool m_dragging { false }; - Gfx::IntPoint m_selection_start; - Gfx::IntPoint m_selection_end; - - bool m_panning { false }; - Gfx::IntPoint m_last_pan_position; - - MandelbrotSet m_set; -}; - -void Mandelbrot::zoom(Zoom in_out, Gfx::IntPoint center) -{ - static constexpr double zoom_in_multiplier = 0.8; - static constexpr double zoom_out_multiplier = 1.25; - - bool zooming_in = in_out == Zoom::In; - double multiplier = zooming_in ? zoom_in_multiplier : zoom_out_multiplier; - - auto relative_rect = this->relative_rect(); - Gfx::IntRect zoomed_rect = relative_rect; - - zoomed_rect.set_width(zoomed_rect.width() * multiplier); - zoomed_rect.set_height(zoomed_rect.height() * multiplier); - - auto leftover_width = abs(relative_rect.width() - zoomed_rect.width()); - auto leftover_height = abs(relative_rect.height() - zoomed_rect.height()); - - double cursor_x_percentage = static_cast(center.x()) / relative_rect.width(); - double cursor_y_percentage = static_cast(center.y()) / relative_rect.height(); - - zoomed_rect.set_x((zooming_in ? 1 : -1) * leftover_width * cursor_x_percentage); - zoomed_rect.set_y((zooming_in ? 1 : -1) * leftover_height * cursor_y_percentage); - - m_set.zoom(zoomed_rect); - update(); -} - -void Mandelbrot::reset() -{ - m_set.reset(); - update(); -} - -void Mandelbrot::paint_event(GUI::PaintEvent& event) -{ - Frame::paint_event(event); - - if (!m_dragging && !m_panning) - m_set.calculate(); - - GUI::Painter painter(*this); - painter.add_clip_rect(frame_inner_rect()); - painter.add_clip_rect(event.rect()); - painter.draw_scaled_bitmap(rect(), m_set.bitmap(), m_set.bitmap().rect()); - - if (m_dragging) - painter.draw_rect(Gfx::IntRect::from_two_points(m_selection_start, m_selection_end), Color::Blue); -} - -void Mandelbrot::keydown_event(GUI::KeyEvent& event) -{ - switch (event.key()) { - case KeyCode::Key_Left: - m_set.pan_by(Gfx::IntPoint { 10, 0 }); - break; - case KeyCode::Key_Right: - m_set.pan_by(Gfx::IntPoint { -10, 0 }); - break; - case KeyCode::Key_Up: - m_set.pan_by(Gfx::IntPoint { 0, 10 }); - break; - case KeyCode::Key_Down: - m_set.pan_by(Gfx::IntPoint { 0, -10 }); - break; - default: - GUI::Widget::keydown_event(event); - return; - } - - m_panning = true; - update(); -} - -void Mandelbrot::keyup_event(GUI::KeyEvent& event) -{ - switch (event.key()) { - case KeyCode::Key_Left: - case KeyCode::Key_Right: - case KeyCode::Key_Up: - case KeyCode::Key_Down: - m_panning = false; - break; - default: - GUI::Widget::keydown_event(event); - } -} - -void Mandelbrot::mousedown_event(GUI::MouseEvent& event) -{ - if (event.button() == GUI::MouseButton::Primary) { - if (!m_dragging) { - m_selection_start = event.position(); - m_selection_end = event.position(); - m_dragging = true; - } - } else if (event.button() == GUI::MouseButton::Middle) { - if (!m_panning) { - m_last_pan_position = event.position(); - m_panning = true; - } - } - - return GUI::Widget::mousedown_event(event); -} - -void Mandelbrot::mousemove_event(GUI::MouseEvent& event) -{ - if (m_dragging) { - // Maintain aspect ratio - int selection_width = event.position().x() - m_selection_start.x(); - int selection_height = event.position().y() - m_selection_start.y(); - int aspect_corrected_selection_width = selection_height * width() / height(); - int aspect_corrected_selection_height = selection_width * height() / width(); - if (selection_width * aspect_corrected_selection_height > aspect_corrected_selection_width * selection_height) - m_selection_end = { event.position().x(), m_selection_start.y() + abs(aspect_corrected_selection_height) * ((selection_height < 0) ? -1 : 1) }; - else - m_selection_end = { m_selection_start.x() + abs(aspect_corrected_selection_width) * ((selection_width < 0) ? -1 : 1), event.position().y() }; - update(); - } - - if (m_panning) { - m_set.pan_by(event.position() - m_last_pan_position); - m_last_pan_position = event.position(); - - update(); - } - - return GUI::Widget::mousemove_event(event); -} - -void Mandelbrot::mouseup_event(GUI::MouseEvent& event) -{ - if (event.button() == GUI::MouseButton::Primary) { - auto selection = Gfx::IntRect::from_two_points(m_selection_start, m_selection_end); - if (selection.width() > 0 && selection.height() > 0) { - m_set.zoom(selection); - update(); - } - m_dragging = false; - } else if (event.button() == GUI::MouseButton::Middle) { - m_panning = false; - update(); - } else if (event.button() == GUI::MouseButton::Secondary) { - reset(); - } - - return GUI::Widget::mouseup_event(event); -} - -void Mandelbrot::mousewheel_event(GUI::MouseEvent& event) -{ - zoom(event.wheel_delta_y() < 0 ? Zoom::In : Zoom::Out, event.position()); -} - -void Mandelbrot::resize_event(GUI::ResizeEvent& event) -{ - m_set.resize(event.size()); -} - -ErrorOr Mandelbrot::export_image(Core::File& export_file, ImageType image_type) -{ - m_set.resize(Gfx::IntSize { 1920, 1080 }); - m_set.calculate(); - ByteBuffer encoded_data; - switch (image_type) { - case ImageType::BMP: - encoded_data = TRY(Gfx::BMPWriter::encode(m_set.bitmap())); - break; - case ImageType::PNG: - encoded_data = TRY(Gfx::PNGWriter::encode(m_set.bitmap())); - break; - case ImageType::QOI: - encoded_data = TRY(Gfx::QOIWriter::encode(m_set.bitmap())); - break; - default: - VERIFY_NOT_REACHED(); - } - m_set.resize(size()); - - TRY(export_file.write_until_depleted(encoded_data)); - return {}; -} - -ErrorOr serenity_main(Main::Arguments arguments) -{ - auto app = TRY(GUI::Application::create(arguments)); - - TRY(Core::System::pledge("stdio thread recvfd sendfd rpath unix wpath cpath")); - - TRY(Core::System::unveil("/res", "r")); - TRY(Core::System::unveil("/tmp/session/%sid/portal/filesystemaccess", "rw")); - TRY(Core::System::unveil(nullptr, nullptr)); - - auto window = GUI::Window::construct(); - window->set_double_buffering_enabled(false); - window->set_title("Mandelbrot"); - window->set_obey_widget_min_size(false); - window->set_minimum_size(320, 240); - window->resize(window->minimum_size() * 2); - auto mandelbrot = window->set_main_widget(); - - auto file_menu = window->add_menu("&File"_string); - - auto export_submenu = file_menu->add_submenu("&Export"_string); - - auto const save_image = [&](ImageType type, StringView extension) { - auto const export_path = FileSystemAccessClient::Client::the().save_file(window, "mandelbrot"sv, extension); - - if (export_path.is_error()) - return; - - if (auto result = mandelbrot->export_image(export_path.value().stream(), type); result.is_error()) - GUI::MessageBox::show_error(window, ByteString::formatted("{}", result.error())); - }; - - export_submenu->add_action(GUI::Action::create("As &BMP...", - [&](GUI::Action&) { - save_image(ImageType::BMP, ".bmp"sv); - })); - export_submenu->add_action(GUI::Action::create("As &PNG...", { Mod_Ctrl | Mod_Shift, Key_S }, - [&](GUI::Action&) { - save_image(ImageType::PNG, ".png"sv); - })); - export_submenu->add_action(GUI::Action::create("As &QOI...", - [&](GUI::Action&) { - save_image(ImageType::QOI, ".qoi"sv); - })); - - export_submenu->set_icon(TRY(Gfx::Bitmap::load_from_file("/res/icons/16x16/save.png"sv))); - - file_menu->add_separator(); - file_menu->add_action(GUI::CommonActions::make_quit_action([&](auto&) { app->quit(); })); - - auto zoom_in_action = GUI::CommonActions::make_zoom_in_action( - [&](auto&) { - mandelbrot->zoom(Mandelbrot::Zoom::In, mandelbrot->relative_rect().center()); - }, - window); - - auto reset_zoom_action = GUI::CommonActions::make_reset_zoom_action( - [&](auto&) { - // FIXME: Ideally, this would only reset zoom. Currently, it resets pan too. - mandelbrot->reset(); - }, - window); - - auto zoom_out_action = GUI::CommonActions::make_zoom_out_action( - [&](auto&) { - mandelbrot->zoom(Mandelbrot::Zoom::Out, mandelbrot->relative_rect().center()); - }, - window); - - auto app_icon = GUI::Icon::default_icon("app-mandelbrot"sv); - window->set_icon(app_icon.bitmap_for_size(16)); - - auto view_menu = window->add_menu("&View"_string); - view_menu->add_action(zoom_in_action); - view_menu->add_action(reset_zoom_action); - view_menu->add_action(zoom_out_action); - view_menu->add_action(GUI::CommonActions::make_fullscreen_action([&](auto&) { - window->set_fullscreen(!window->is_fullscreen()); - })); - - auto help_menu = window->add_menu("&Help"_string); - help_menu->add_action(GUI::CommonActions::make_command_palette_action(window)); - help_menu->add_action(GUI::CommonActions::make_about_action("Mandelbrot Demo"_string, app_icon, window)); - - window->show(); - window->set_cursor(Gfx::StandardCursor::Zoom); - return app->exec(); -} diff --git a/Userland/Demos/ModelGallery/BasicModel.cpp b/Userland/Demos/ModelGallery/BasicModel.cpp deleted file mode 100644 index 10645907f61..00000000000 --- a/Userland/Demos/ModelGallery/BasicModel.cpp +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright (c) 2021, sin-ack - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "BasicModel.h" - -GUI::Variant BasicModel::data(GUI::ModelIndex const& index, GUI::ModelRole role) const -{ - if (role != GUI::ModelRole::Display) - return {}; - if (!is_within_range(index)) - return {}; - - return m_items.at(index.row()); -} - -GUI::Model::MatchResult BasicModel::data_matches(GUI::ModelIndex const& index, GUI::Variant const& data) const -{ - if (!is_within_range(index)) - return { TriState::False }; - if (!data.is_string()) - return { TriState::False }; - - auto& value = m_items.at(index.row()); - - if (value.contains(data.as_string())) - return { TriState::True }; - - return { TriState::False }; -} - -void BasicModel::invalidate() -{ - Model::invalidate(); - if (on_invalidate) - on_invalidate(); -} - -GUI::ModelIndex BasicModel::index(int row, int column, GUI::ModelIndex const& parent) const -{ - if (column != 0) - return {}; - if (parent.is_valid()) - return {}; - if (row < 0 || row >= static_cast(m_items.size())) - return {}; - - return create_index(row, column); -} - -void BasicModel::add_item(ByteString const& item) -{ - begin_insert_rows({}, m_items.size(), m_items.size()); - m_items.append(item); - end_insert_rows(); - - did_update(UpdateFlag::DontInvalidateIndices); -} - -void BasicModel::remove_item(GUI::ModelIndex const& index) -{ - if (!index.is_valid() || !is_within_range(index)) - return; - - begin_delete_rows({}, index.row(), index.row()); - m_items.remove(index.row()); - end_delete_rows(); - - did_update(UpdateFlag::DontInvalidateIndices); -} diff --git a/Userland/Demos/ModelGallery/BasicModel.h b/Userland/Demos/ModelGallery/BasicModel.h deleted file mode 100644 index 5b059d27560..00000000000 --- a/Userland/Demos/ModelGallery/BasicModel.h +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright (c) 2021, sin-ack - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include - -class BasicModel final : public GUI::Model { -public: - static NonnullRefPtr create() - { - return adopt_ref(*new BasicModel()); - } - - virtual int row_count(GUI::ModelIndex const& = GUI::ModelIndex()) const override { return m_items.size(); } - virtual int column_count(GUI::ModelIndex const& = GUI::ModelIndex()) const override { return 1; } - virtual ErrorOr column_name(int) const override { return "Item"_string; } - - virtual GUI::Variant data(GUI::ModelIndex const&, GUI::ModelRole = GUI::ModelRole::Display) const override; - virtual GUI::Model::MatchResult data_matches(GUI::ModelIndex const&, GUI::Variant const&) const override; - virtual void invalidate() override; - virtual GUI::ModelIndex index(int row, int column = 0, GUI::ModelIndex const& parent = GUI::ModelIndex()) const override; - - Function on_invalidate; - - void add_item(ByteString const& item); - void remove_item(GUI::ModelIndex const&); - -private: - BasicModel() - { - } - - Vector m_items; -}; diff --git a/Userland/Demos/ModelGallery/BasicModelTab.gml b/Userland/Demos/ModelGallery/BasicModelTab.gml deleted file mode 100644 index 9859acc23f0..00000000000 --- a/Userland/Demos/ModelGallery/BasicModelTab.gml +++ /dev/null @@ -1,40 +0,0 @@ -@GUI::Widget { - name: "basic_model_tab" - layout: @GUI::VerticalBoxLayout { - margins: [4] - } - - @GUI::Label { - text: "Here is a basic model, displayed on a table widget. Its clients are updated via granular updates. You can add or remove items with the widgets below." - text_alignment: "CenterLeft" - fixed_height: 34 - } - - @GUI::TableView { - name: "model_table" - } - - @GUI::Widget { - layout: @GUI::HorizontalBoxLayout {} - fixed_height: 30 - - @GUI::TextBox { - name: "new_item_name" - placeholder: "Enter some text to be added..." - } - - @GUI::Button { - name: "add_new_item" - fixed_width: 22 - fixed_height: 22 - tooltip: "Add the text as an item to the model" - } - - @GUI::Button { - name: "remove_selected_item" - fixed_width: 22 - fixed_height: 22 - tooltip: "Remove the selected item from the model" - } - } -} diff --git a/Userland/Demos/ModelGallery/CMakeLists.txt b/Userland/Demos/ModelGallery/CMakeLists.txt deleted file mode 100644 index e7bb4b71ed9..00000000000 --- a/Userland/Demos/ModelGallery/CMakeLists.txt +++ /dev/null @@ -1,20 +0,0 @@ -serenity_component( - ModelGallery - TARGETS ModelGallery -) - -stringify_gml(./BasicModelTab.gml BasicModelTabGML.h basic_model_tab_gml) - -set(SOURCES - main.cpp - GalleryWidget.cpp - BasicModel.cpp -) - -set(GENERATED_SOURCES - BasicModelTabGML.h -) - -serenity_app(ModelGallery ICON app-model-gallery) - -target_link_libraries(ModelGallery PRIVATE LibCore LibGUI LibGfx LibMain) diff --git a/Userland/Demos/ModelGallery/GalleryWidget.cpp b/Userland/Demos/ModelGallery/GalleryWidget.cpp deleted file mode 100644 index 8de3d896442..00000000000 --- a/Userland/Demos/ModelGallery/GalleryWidget.cpp +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Copyright (c) 2021, sin-ack - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "GalleryWidget.h" -#include - -GalleryWidget::GalleryWidget() -{ - set_fill_with_background_color(true); - set_layout(); - - auto& inner_widget = add(); - inner_widget.set_layout(4); - - m_tab_widget = inner_widget.add(); - m_statusbar = add(); - - (void)load_basic_model_tab(); - load_sorting_filtering_tab(); -} - -ErrorOr GalleryWidget::load_basic_model_tab() -{ - auto& tab = m_tab_widget->add_tab("Basic Model"_string); - TRY(tab.load_from_gml(basic_model_tab_gml)); - - m_basic_model = BasicModel::create(); - m_basic_model_table = *tab.find_descendant_of_type_named("model_table"); - m_basic_model_table->set_model(m_basic_model); - - m_basic_model->on_invalidate = [&] { - m_invalidation_count++; - m_statusbar->set_text(String::formatted("Times invalidated: {}", m_invalidation_count).release_value_but_fixme_should_propagate_errors()); - }; - - m_statusbar->set_text(TRY(String::formatted("Times invalidated: {}", m_invalidation_count))); - - m_basic_model->add_item("Well..."); - m_basic_model->add_item("...hello..."); - m_basic_model->add_item("...friends! :^)"); - - m_new_item_name = *tab.find_descendant_of_type_named("new_item_name"); - m_add_new_item = *tab.find_descendant_of_type_named("add_new_item"); - m_remove_selected_item = *tab.find_descendant_of_type_named("remove_selected_item"); - - m_add_new_item->set_icon(TRY(Gfx::Bitmap::load_from_file("/res/icons/16x16/plus.png"sv))); - m_remove_selected_item->set_icon(TRY(Gfx::Bitmap::load_from_file("/res/icons/16x16/minus.png"sv))); - - m_new_item_name->on_return_pressed = [&] { add_textbox_contents_to_basic_model(); }; - m_add_new_item->on_click = [&](auto) { add_textbox_contents_to_basic_model(); }; - - m_remove_selected_item->on_click = [&](auto) { - auto index = m_basic_model_table->cursor_index(); - if (index.is_valid()) { - m_basic_model->remove_item(index); - } - }; - - return {}; -} - -void GalleryWidget::load_sorting_filtering_tab() -{ - // TODO: Add the SortingFilteringProxyModel here. -} - -void GalleryWidget::add_textbox_contents_to_basic_model() -{ - if (!m_new_item_name->current_line().is_empty()) { - m_basic_model->add_item(m_new_item_name->current_line().to_utf8()); - m_new_item_name->set_text(""sv); - } -} diff --git a/Userland/Demos/ModelGallery/GalleryWidget.h b/Userland/Demos/ModelGallery/GalleryWidget.h deleted file mode 100644 index d1ac6c359ff..00000000000 --- a/Userland/Demos/ModelGallery/GalleryWidget.h +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright (c) 2021, sin-ack - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include "BasicModel.h" -#include -#include -#include -#include -#include -#include -#include - -class GalleryWidget final : public GUI::Widget { - C_OBJECT(GalleryWidget) - -private: - GalleryWidget(); - - ErrorOr load_basic_model_tab(); - void load_sorting_filtering_tab(); - - void add_textbox_contents_to_basic_model(); - - RefPtr m_tab_widget; - RefPtr m_statusbar; - - size_t m_invalidation_count { 0 }; - RefPtr m_basic_model; - RefPtr m_basic_model_table; - RefPtr m_new_item_name; - RefPtr m_add_new_item; - RefPtr m_remove_selected_item; -}; diff --git a/Userland/Demos/ModelGallery/main.cpp b/Userland/Demos/ModelGallery/main.cpp deleted file mode 100644 index f15f2788cf1..00000000000 --- a/Userland/Demos/ModelGallery/main.cpp +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright (c) 2021, sin-ack - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "GalleryWidget.h" -#include -#include -#include -#include -#include -#include -#include -#include - -ErrorOr serenity_main(Main::Arguments arguments) -{ - TRY(Core::System::pledge("stdio recvfd sendfd rpath wpath cpath unix")); - - auto app = TRY(GUI::Application::create(arguments)); - - TRY(Core::System::pledge("stdio recvfd sendfd rpath")); - - auto app_icon = TRY(GUI::Icon::try_create_default_icon("app-model-gallery"sv)); - - auto window = GUI::Window::construct(); - window->set_title("Model Gallery"); - window->set_icon(app_icon.bitmap_for_size(16)); - window->resize(430, 480); - (void)window->set_main_widget(); - - window->show(); - return app->exec(); -} diff --git a/Userland/Demos/Screensaver/CMakeLists.txt b/Userland/Demos/Screensaver/CMakeLists.txt deleted file mode 100644 index 767fc3a35d9..00000000000 --- a/Userland/Demos/Screensaver/CMakeLists.txt +++ /dev/null @@ -1,11 +0,0 @@ -serenity_component( - Screensaver - TARGETS Screensaver -) - -set(SOURCES - main.cpp -) - -serenity_app(Screensaver ICON app-screensaver) -target_link_libraries(Screensaver PRIVATE LibDesktop LibGUI LibCore LibGfx LibMain) diff --git a/Userland/Demos/Screensaver/main.cpp b/Userland/Demos/Screensaver/main.cpp deleted file mode 100644 index e97b6096140..00000000000 --- a/Userland/Demos/Screensaver/main.cpp +++ /dev/null @@ -1,111 +0,0 @@ -/* - * Copyright (c) 2023, Tim Flynn - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -class ScreensaverAppsModel final : public GUI::Model { -public: - ScreensaverAppsModel() - { - Desktop::AppFile::for_each([&](Desktop::AppFile& app_file) { - if (app_file.category() != "Demos/Screensaver"sv) - return; - m_apps.append(app_file); - }); - - quick_sort(m_apps, [](auto& a, auto& b) { return a->name() < b->name(); }); - } - - virtual int row_count(GUI::ModelIndex const&) const override { return static_cast(m_apps.size()); } - virtual int column_count(GUI::ModelIndex const&) const override { return 1; } - - virtual GUI::ModelIndex index(int row, int column, GUI::ModelIndex const&) const override - { - if (row < 0 || row >= static_cast(m_apps.size())) - return {}; - return create_index(row, column, &m_apps[row]); - } - - virtual GUI::Variant data(GUI::ModelIndex const& index, GUI::ModelRole role) const override - { - auto const& app = m_apps[index.row()]; - if (role == GUI::ModelRole::Icon) - return app->icon(); - - if (role == GUI::ModelRole::Display) { - if (app->name().ends_with(" Screensaver"sv)) - return app->name().substring(0, app->name().length() - " Screensaver"sv.length()); - return app->name(); - } - - if (role == GUI::ModelRole::Custom) - return app->executable(); - - return {}; - } - -private: - Vector> m_apps; -}; - -ErrorOr serenity_main(Main::Arguments arguments) -{ - TRY(Core::System::pledge("stdio thread recvfd sendfd rpath cpath wpath unix proc exec")); - - auto app = TRY(GUI::Application::create(arguments)); - - TRY(Core::System::pledge("stdio thread recvfd sendfd rpath cpath wpath proc exec")); - - auto app_icon = GUI::Icon::default_icon("app-screensaver"sv); - - auto window = GUI::Window::construct(); - window->set_title("Screensaver"); - window->resize(360, 240); - - auto file_menu = window->add_menu("&File"_string); - file_menu->add_action(GUI::CommonActions::make_quit_action([&](auto&) { - app->quit(); - })); - - auto view_menu = window->add_menu("&View"_string); - view_menu->add_action(GUI::CommonActions::make_fullscreen_action([&](auto&) { - window->set_fullscreen(!window->is_fullscreen()); - })); - - auto help_menu = window->add_menu("&Help"_string); - help_menu->add_action(GUI::CommonActions::make_command_palette_action(window)); - help_menu->add_action(GUI::CommonActions::make_about_action("Screensaver"_string, app_icon, window)); - - auto main_widget = window->set_main_widget(); - main_widget->set_fill_with_background_color(true); - main_widget->set_layout(); - - auto& icon_view = main_widget->add(); - icon_view.set_should_hide_unnecessary_scrollbars(true); - auto model = adopt_ref(*new ScreensaverAppsModel); - icon_view.set_model(*model); - - icon_view.on_activation = [&](GUI::ModelIndex const& index) { - auto executable = model->data(index, GUI::ModelRole::Custom).as_string(); - GUI::Process::spawn_or_show_error(window, executable); - }; - - window->set_icon(app_icon.bitmap_for_size(16)); - window->show(); - - return app->exec(); -} diff --git a/Userland/Demos/Starfield/CMakeLists.txt b/Userland/Demos/Starfield/CMakeLists.txt deleted file mode 100644 index ddc5b9941ae..00000000000 --- a/Userland/Demos/Starfield/CMakeLists.txt +++ /dev/null @@ -1,11 +0,0 @@ -serenity_component( - Starfield - TARGETS Starfield -) - -set(SOURCES - Starfield.cpp -) - -serenity_app(Starfield ICON app-starfield) -target_link_libraries(Starfield PRIVATE LibDesktop LibGUI LibCore LibGfx LibMain) diff --git a/Userland/Demos/Starfield/Starfield.cpp b/Userland/Demos/Starfield/Starfield.cpp deleted file mode 100644 index d1286ad82ef..00000000000 --- a/Userland/Demos/Starfield/Starfield.cpp +++ /dev/null @@ -1,185 +0,0 @@ -/* - * Copyright (c) 2021, Jagger De Leo - * Copyright (c) 2022, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -struct Coordinate { - int x; - int y; - int z; - - operator Gfx::IntPoint() const - { - return { x, y }; - } -}; - -class Starfield final : public Desktop::Screensaver { - C_OBJECT(Starfield) -public: - virtual ~Starfield() override = default; - ErrorOr create_stars(int, int, int); - - void set_speed(unsigned speed) { m_speed = speed; } - -private: - Starfield(int); - RefPtr m_bitmap; - - void draw(); - virtual void paint_event(GUI::PaintEvent&) override; - virtual void timer_event(Core::TimerEvent&) override; - virtual void keydown_event(GUI::KeyEvent&) override; - virtual void mousewheel_event(GUI::MouseEvent&) override; - - Vector m_stars; - int m_sweep_plane = 2000; - unsigned m_speed = 1; -}; - -Starfield::Starfield(int interval) -{ - on_screensaver_exit = []() { GUI::Application::the()->quit(); }; - srand(time(nullptr)); - stop_timer(); - start_timer(interval); -} - -ErrorOr Starfield::create_stars(int width, int height, int stars) -{ - m_bitmap = TRY(Gfx::Bitmap::create(Gfx::BitmapFormat::BGRx8888, { width, height })); - - m_stars.grow_capacity(stars); - for (int i = 0; i < stars; i++) { - m_stars.append({ rand() % width - width / 2, - rand() % height - height / 2, - rand() % 1999 + 1 }); - } - draw(); - return {}; -} - -void Starfield::keydown_event(GUI::KeyEvent& event) -{ - switch (event.key()) { - case Key_Plus: - m_speed++; - break; - case Key_Minus: - if (--m_speed < 1) - m_speed = 1; - break; - default: - Desktop::Screensaver::keydown_event(event); - } -} - -void Starfield::mousewheel_event(GUI::MouseEvent& event) -{ - if (event.wheel_delta_y() == 0) - return; - - m_speed += event.wheel_delta_y() > 0 ? -1 : 1; - if (m_speed < 1) - m_speed = 1; -} - -void Starfield::paint_event(GUI::PaintEvent& event) -{ - GUI::Painter painter(*this); - painter.add_clip_rect(event.rect()); - painter.draw_scaled_bitmap(rect(), *m_bitmap, m_bitmap->rect()); -} - -void Starfield::timer_event(Core::TimerEvent&) -{ - m_bitmap->fill(Color::Black); - - auto computed_point = Gfx::IntPoint(); - auto half_x = width() / 2; - auto half_y = height() / 2; - - GUI::Painter painter(*m_bitmap); - - for (auto star : m_stars) { - auto z = ((star.z + m_sweep_plane) % 2000) * 0.0005; - computed_point.set_x(half_x + star.x / z); - computed_point.set_y(half_y + star.y / z); - - auto computed_end_point = Gfx::IntPoint(); - - auto z_end = ((star.z + m_sweep_plane - m_speed) % 2000) * 0.0005; - computed_end_point.set_x(half_x + star.x / z_end); - computed_end_point.set_y(half_y + star.y / z_end); - - if (computed_point.x() < 0 || computed_point.x() >= width() || computed_point.y() < 0 || computed_point.y() >= height()) - continue; - - u8 falloff = (1 - z * z) * 255; - painter.draw_line(computed_point, computed_end_point, Color(falloff, falloff, falloff)); - } - m_sweep_plane -= m_speed; - if (m_sweep_plane < 0) - m_sweep_plane = 2000; - update(); -} - -void Starfield::draw() -{ - GUI::Painter painter(*m_bitmap); - painter.fill_rect(m_bitmap->rect(), Color::Black); -} - -ErrorOr serenity_main(Main::Arguments arguments) -{ - TRY(Core::System::pledge("stdio recvfd sendfd rpath unix")); - - unsigned star_count = 1000; - unsigned refresh_rate = 16; - unsigned speed = 1; - - Core::ArgsParser args_parser; - args_parser.set_general_help("The classic starfield screensaver."); - args_parser.add_option(star_count, "Number of stars to draw (default = 1000)", "stars", 'c', "number"); - args_parser.add_option(refresh_rate, "Refresh rate (default = 16)", "rate", 'r', "milliseconds"); - args_parser.add_option(speed, "Speed (default = 1)", "speed", 's', "number"); - args_parser.parse(arguments); - - auto app = TRY(GUI::Application::create(arguments)); - - TRY(Core::System::pledge("stdio recvfd sendfd rpath")); - - auto window = TRY(Desktop::Screensaver::create_window("Starfield"sv, "app-starfield"sv)); - - auto starfield_widget = window->set_main_widget(refresh_rate); - starfield_widget->set_fill_with_background_color(false); - starfield_widget->set_override_cursor(Gfx::StandardCursor::Hidden); - starfield_widget->update(); - window->show(); - - TRY(starfield_widget->create_stars(window->width(), window->height(), star_count)); - starfield_widget->set_speed(speed); - starfield_widget->update(); - - window->move_to_front(); - window->set_cursor(Gfx::StandardCursor::Hidden); - window->update(); - - return app->exec(); -} diff --git a/Userland/Demos/Tubes/CMakeLists.txt b/Userland/Demos/Tubes/CMakeLists.txt deleted file mode 100644 index 3743536eff3..00000000000 --- a/Userland/Demos/Tubes/CMakeLists.txt +++ /dev/null @@ -1,13 +0,0 @@ -serenity_component( - Tubes - TARGETS Tubes -) - -set(SOURCES - Shapes.cpp - Tubes.cpp - main.cpp -) - -serenity_app(Tubes ICON app-tubes) -target_link_libraries(Tubes PRIVATE LibCore LibDesktop LibGfx LibGL LibGUI LibMain) diff --git a/Userland/Demos/Tubes/Shapes.cpp b/Userland/Demos/Tubes/Shapes.cpp deleted file mode 100644 index 67b7e342127..00000000000 --- a/Userland/Demos/Tubes/Shapes.cpp +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Copyright (c) 2022, Jelle Raaijmakers - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include - -constexpr u8 sphere_number_of_segments = 4; -constexpr u8 tube_number_of_segments = 12; - -void draw_sphere() -{ - // Draw a sphere by drawing a cube with many segments with normalized coordinates - glBegin(GL_QUADS); - auto draw_segment = [](Array corners, Optional flip_a, Optional flip_b, Optional swap_a, Optional swap_b) { - for (DoubleVector3& corner : corners) { - if (flip_a.has_value()) - corner[flip_a.value()] *= -1; - if (flip_b.has_value()) - corner[flip_b.value()] *= -1; - if (swap_a.has_value() && swap_b.has_value()) - swap(corner[swap_a.value()], corner[swap_b.value()]); - - glNormal3d(corner.x(), corner.y(), corner.z()); - glVertex3d(corner.x(), corner.y(), corner.z()); - } - }; - double const segment_size = 2. / sphere_number_of_segments; - for (int y = 0; y < sphere_number_of_segments; y++) { - for (int x = 0; x < sphere_number_of_segments; x++) { - DoubleVector3 bottomleft = { -1. + x * segment_size, -1. + y * segment_size, 1. }; - DoubleVector3 bottomright = { -1. + (x + 1) * segment_size, -1. + y * segment_size, 1. }; - DoubleVector3 topright = { -1. + (x + 1) * segment_size, -1. + (y + 1) * segment_size, 1. }; - DoubleVector3 topleft = { -1. + x * segment_size, -1. + (y + 1) * segment_size, 1. }; - - Array normalized_corners = { - bottomleft.normalized(), - bottomright.normalized(), - topright.normalized(), - topleft.normalized(), - }; - - draw_segment(normalized_corners, {}, {}, {}, {}); // front face - draw_segment(normalized_corners, 0, 2, {}, {}); // back face - draw_segment(normalized_corners, 2, {}, 0, 2); // left face - draw_segment(normalized_corners, 0, {}, 0, 2); // right face - draw_segment(normalized_corners, 1, {}, 1, 2); // top face - draw_segment(normalized_corners, 2, {}, 1, 2); // bottom face - } - } - glEnd(); -} - -void draw_tube() -{ - glBegin(GL_QUADS); - double const segment_angle = 2 * M_PI / tube_number_of_segments; - double last_x = 0.; - double last_y = 1.; - for (int i = 1; i <= tube_number_of_segments; ++i) { - double angle = i * segment_angle; - double segment_x = sin(angle); - double segment_y = cos(angle); - - glNormal3d(last_x, last_y, 0.); - glVertex3d(last_x, last_y, 0.); - glNormal3d(segment_x, segment_y, 0.); - glVertex3d(segment_x, segment_y, 0.); - glVertex3d(segment_x, segment_y, -2.); - glNormal3d(last_x, last_y, 0.); - glVertex3d(last_x, last_y, -2.); - - last_x = segment_x; - last_y = segment_y; - } - glEnd(); -} diff --git a/Userland/Demos/Tubes/Shapes.h b/Userland/Demos/Tubes/Shapes.h deleted file mode 100644 index aa6141bc6f3..00000000000 --- a/Userland/Demos/Tubes/Shapes.h +++ /dev/null @@ -1,12 +0,0 @@ -/* - * Copyright (c) 2022, Jelle Raaijmakers - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include - -void draw_sphere(); -void draw_tube(); diff --git a/Userland/Demos/Tubes/Tubes.cpp b/Userland/Demos/Tubes/Tubes.cpp deleted file mode 100644 index a7f2b274a76..00000000000 --- a/Userland/Demos/Tubes/Tubes.cpp +++ /dev/null @@ -1,306 +0,0 @@ -/* - * Copyright (c) 2022, Jelle Raaijmakers - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -constexpr size_t grid_resolution = 15; -constexpr int reset_every_ticks = 900; -constexpr double rotation_range = 35.; -constexpr u8 tube_maximum_count = 12; -constexpr u8 tube_minimum_count = 3; -constexpr double tube_movement_per_tick = .25; -constexpr double tube_relative_thickness = .6; -constexpr int tube_travel_max_stretch = 6; - -static double random_double() -{ - return get_random() / static_cast(NumericLimits::max()); -} - -static int random_int(int min, int max) -{ - return min + round_to(random_double() * (max - min)); -} - -static IntVector4 tube_rotation_for_direction(Direction direction) -{ - switch (direction) { - case Direction::XPositive: - return { 0, 1, 0, -90 }; - case Direction::XNegative: - return { 0, 1, 0, 90 }; - case Direction::YPositive: - return { 1, 0, 0, 90 }; - case Direction::YNegative: - return { 1, 0, 0, -90 }; - case Direction::ZPositive: - return { 0, 1, 0, 180 }; - case Direction::ZNegative: - return { 0, 0, 0, 0 }; - default: - VERIFY_NOT_REACHED(); - } -} - -static IntVector3 vector_for_direction(Direction direction) -{ - switch (direction) { - case Direction::XPositive: - return { 1, 0, 0 }; - case Direction::XNegative: - return { -1, 0, 0 }; - case Direction::YPositive: - return { 0, 1, 0 }; - case Direction::YNegative: - return { 0, -1, 0 }; - case Direction::ZPositive: - return { 0, 0, 1 }; - case Direction::ZNegative: - return { 0, 0, -1 }; - default: - VERIFY_NOT_REACHED(); - } -} - -Tubes::Tubes(int interval) - : m_grid(MUST(FixedArray::create(grid_resolution * grid_resolution * grid_resolution))) -{ - on_screensaver_exit = []() { GUI::Application::the()->quit(); }; - start_timer(interval); -} - -void Tubes::choose_new_direction_for_tube(Tube& tube) -{ - // Find all possible directions - Vector possible_directions; - for (int i = 1; i <= 6; ++i) { - auto direction = static_cast(i); - auto direction_vector = vector_for_direction(direction); - auto check_position = tube.position + direction_vector; - if (is_valid_grid_position(check_position) && get_grid(check_position) == 0) - possible_directions.append(direction); - } - - // If tube is stuck, kill it :^( - if (possible_directions.is_empty()) { - tube.direction = Direction::None; - tube.active = false; - return; - } - - // Remove our old direction if we have other options available - Direction const old_direction = tube.direction; - if (possible_directions.size() >= 2 && possible_directions.contains_slow(old_direction)) - possible_directions.remove_all_matching([&old_direction](Direction const& item) { return item == old_direction; }); - - // Select a random new direction - tube.direction = possible_directions[random_int(0, static_cast(possible_directions.size()) - 1)]; - - // Determine how far we can go in this direction - auto direction_vector = vector_for_direction(tube.direction); - int max_stretch = random_int(1, tube_travel_max_stretch); - IntVector3 new_target = tube.position; - while (max_stretch-- > 0) { - new_target += direction_vector; - if (!is_valid_grid_position(new_target) || get_grid(new_target) != 0) - break; - set_grid(new_target, 1); - tube.target_position = new_target; - } - tube.progress_to_target = 0.; -} - -ErrorOr Tubes::create_buffer(Gfx::IntSize size) -{ - m_bitmap = TRY(Gfx::Bitmap::create(Gfx::BitmapFormat::BGRx8888, size)); - m_gl_context = TRY(GL::create_context(*m_bitmap)); - return {}; -} - -u8 Tubes::get_grid(IntVector3 position) -{ - return m_grid[position.z() * grid_resolution * grid_resolution + position.y() * grid_resolution + position.x()]; -} - -bool Tubes::is_valid_grid_position(Gfx::IntVector3 position) -{ - return position.x() >= 0 - && position.x() < static_cast(grid_resolution) - && position.y() >= 0 - && position.y() < static_cast(grid_resolution) - && position.z() >= 0 - && position.z() < static_cast(grid_resolution); -} - -void Tubes::set_grid(IntVector3 position, u8 value) -{ - m_grid[position.z() * grid_resolution * grid_resolution + position.y() * grid_resolution + position.x()] = value; -} - -void Tubes::paint_event(GUI::PaintEvent& event) -{ - GUI::Painter painter(*this); - painter.add_clip_rect(event.rect()); - painter.blit(rect().location(), *m_bitmap, m_bitmap->rect()); -} - -void Tubes::reset_tubes() -{ - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - - // Random rotation - glMatrixMode(GL_PROJECTION); - glPopMatrix(); - glPushMatrix(); - glRotated((random_double() - .5) * 2 * rotation_range, 0., 1., 0.); - glMatrixMode(GL_MODELVIEW); - - // Clear grid - m_grid.fill_with(0); - - // Create new set of tubes - auto free_grid_position = [&]() { - for (;;) { - IntVector3 position = { - random_int(0, grid_resolution - 1), - random_int(0, grid_resolution - 1), - random_int(0, grid_resolution - 1), - }; - if (get_grid(position) != 0) - continue; - return position; - } - }; - m_tubes.clear_with_capacity(); - int tube_count = random_int(tube_minimum_count, tube_maximum_count); - while (tube_count-- > 0) { - Tube new_tube = { - .color = { - random_double(), - random_double(), - random_double(), - }, - .position = free_grid_position(), - }; - choose_new_direction_for_tube(new_tube); - m_tubes.append(new_tube); - set_grid(new_tube.position, 1); - } -} - -void Tubes::setup_view() -{ - glClearColor(0.f, 0.f, 0.f, 1.f); - - glMatrixMode(GL_PROJECTION); - double const zoom = .25; - auto const half_aspect_ratio = static_cast(m_bitmap->width()) / m_bitmap->height() * zoom; - glFrustum(-half_aspect_ratio, half_aspect_ratio, -zoom, zoom, .5, 10.); - glTranslated(0., 0., -2.); - glPushMatrix(); - glMatrixMode(GL_MODELVIEW); - - // Set up lighting - glEnable(GL_LIGHTING); - glEnable(GL_LIGHT0); - GLfloat light_ambient[] { .0f, .0f, .0f, 1.f }; - glLightfv(GL_LIGHT0, GL_AMBIENT, light_ambient); - GLfloat light_diffuse[] { 1.f, 1.f, 1.f, 1.f }; - glLightfv(GL_LIGHT0, GL_DIFFUSE, light_diffuse); - GLfloat light_specular[] { 1.f, 1.f, 1.f, 1.f }; - glLightfv(GL_LIGHT0, GL_SPECULAR, light_specular); - GLfloat light_position[] { .5f, 1.f, .5f, 0.f }; - glLightfv(GL_LIGHT0, GL_POSITION, light_position); - - GLfloat mat_specular[] { 1.f, 1.f, 1.f, 1.f }; - glMaterialfv(GL_FRONT, GL_SPECULAR, mat_specular); - glMaterialf(GL_FRONT, GL_SHININESS, 8.f); - - // Adapt the vertex color as ambient and diffuse colors - glEnable(GL_COLOR_MATERIAL); - glColorMaterial(GL_FRONT, GL_AMBIENT_AND_DIFFUSE); - - glEnable(GL_DEPTH_TEST); - glDepthFunc(GL_LEQUAL); - glEnable(GL_NORMALIZE); - glShadeModel(GL_SMOOTH); -} - -void Tubes::timer_event(Core::TimerEvent&) -{ - update_tubes(); - m_gl_context->present(); - repaint(); -} - -void Tubes::update_tubes() -{ - if (++m_ticks % reset_every_ticks == 0) - reset_tubes(); - - double const primitive_size = 2.; // our tubes and spheres are 1 in diameter, so object size is 2 - double const grid_width = 2.; - double const grid_scale = 1. / grid_resolution; - double const primitive_scale = 1. / primitive_size; - double const tube_length_scale = tube_movement_per_tick * primitive_size; - double const tube_thickness_scale = tube_relative_thickness * primitive_scale; - for (auto& tube : m_tubes) { - if (!tube.active) - continue; - - glColor3d(tube.color.x(), tube.color.y(), tube.color.z()); - glPushMatrix(); - - auto pos = tube.position; - glTranslated( - pos.x() * grid_scale * grid_width - (grid_width / 2.), - pos.y() * grid_scale * grid_width - (grid_width / 2.), - pos.z() * grid_scale * grid_width - (grid_width / 2.)); - glScaled(grid_scale, grid_scale, grid_scale); - - // Draw sphere if we're at the start or a corner - if (tube.progress_to_target == 0.) { - glPushMatrix(); - glScaled(tube_thickness_scale, tube_thickness_scale, tube_thickness_scale); - draw_sphere(); - glPopMatrix(); - } - - // Draw tube at the current position - glPushMatrix(); - auto direction_vector = vector_for_direction(tube.direction); - auto distance_to_target = (tube.target_position - tube.position).length(); - auto movement_magnitude = tube.progress_to_target * (distance_to_target - tube_movement_per_tick) / distance_to_target * grid_width; - glTranslated( - direction_vector.x() * movement_magnitude, - direction_vector.y() * movement_magnitude, - direction_vector.z() * movement_magnitude); - auto tube_rotation = tube_rotation_for_direction(tube.direction); - glRotated(tube_rotation.w(), tube_rotation.x(), tube_rotation.y(), tube_rotation.z()); - glScaled(tube_thickness_scale, tube_thickness_scale, primitive_scale * tube_length_scale); - - draw_tube(); - glPopMatrix(); - - // Move towards target - if (tube.progress_to_target >= distance_to_target) { - tube.position = tube.target_position; - choose_new_direction_for_tube(tube); - } else { - tube.progress_to_target = min(tube.progress_to_target + tube_movement_per_tick, distance_to_target); - } - - glPopMatrix(); - } -} diff --git a/Userland/Demos/Tubes/Tubes.h b/Userland/Demos/Tubes/Tubes.h deleted file mode 100644 index 4354f41a0b1..00000000000 --- a/Userland/Demos/Tubes/Tubes.h +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright (c) 2022, Jelle Raaijmakers - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include - -enum class Direction : u8 { - None = 0, - XPositive = 1, - XNegative = 2, - YPositive = 3, - YNegative = 4, - ZPositive = 5, - ZNegative = 6, -}; - -struct Tube { - bool active { true }; - DoubleVector3 color; - IntVector3 position; - Direction direction { Direction::None }; - IntVector3 target_position { 0, 0, 0 }; - double progress_to_target { 0 }; -}; - -class Tubes final : public Desktop::Screensaver { - C_OBJECT(Tubes) -public: - virtual ~Tubes() override = default; - - ErrorOr create_buffer(Gfx::IntSize); - void reset_tubes(); - void setup_view(); - void update_tubes(); - -private: - Tubes(int); - - void choose_new_direction_for_tube(Tube&); - u8 get_grid(IntVector3); - bool is_valid_grid_position(IntVector3); - void set_grid(IntVector3, u8 value); - - virtual void paint_event(GUI::PaintEvent&) override; - virtual void timer_event(Core::TimerEvent&) override; - - RefPtr m_bitmap; - FixedArray m_grid; - OwnPtr m_gl_context; - u64 m_ticks { 0 }; - Vector m_tubes; -}; diff --git a/Userland/Demos/Tubes/main.cpp b/Userland/Demos/Tubes/main.cpp deleted file mode 100644 index 19ca3b897a0..00000000000 --- a/Userland/Demos/Tubes/main.cpp +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright (c) 2022, Jelle Raaijmakers - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include -#include - -ErrorOr serenity_main(Main::Arguments arguments) -{ - TRY(Core::System::pledge("stdio recvfd sendfd rpath unix prot_exec map_fixed")); - - unsigned refresh_rate = 12; - - Core::ArgsParser args_parser; - args_parser.set_general_help("Screensaver rendering colorful moving tubes using LibGL"); - args_parser.add_option(refresh_rate, "Refresh rate", "rate", 'r', "milliseconds"); - args_parser.parse(arguments); - - auto app = TRY(GUI::Application::create(arguments)); - - TRY(Core::System::pledge("stdio recvfd sendfd rpath prot_exec map_fixed")); - - auto window = TRY(Desktop::Screensaver::create_window("Tubes"sv, "app-tubes"sv)); - window->update(); - - auto tubes_widget = window->set_main_widget(refresh_rate); - tubes_widget->set_fill_with_background_color(false); - tubes_widget->set_override_cursor(Gfx::StandardCursor::Hidden); - window->show(); - - TRY(tubes_widget->create_buffer(window->size())); - tubes_widget->setup_view(); - tubes_widget->reset_tubes(); - - window->move_to_front(); - window->set_cursor(Gfx::StandardCursor::Hidden); - - return app->exec(); -} diff --git a/Userland/Demos/WidgetGallery/CMakeLists.txt b/Userland/Demos/WidgetGallery/CMakeLists.txt deleted file mode 100644 index 65bc16c40fc..00000000000 --- a/Userland/Demos/WidgetGallery/CMakeLists.txt +++ /dev/null @@ -1,33 +0,0 @@ -serenity_component( - WidgetGallery - TARGETS WidgetGallery -) - -stringify_gml(./GalleryGML/Window.gml WindowGML.h window_gml) -stringify_gml(./GalleryGML/BasicsTab.gml BasicsTabGML.h basics_tab_gml) -stringify_gml(./GalleryGML/SlidersTab.gml SlidersTabGML.h sliders_tab_gml) -stringify_gml(./GalleryGML/CursorsTab.gml CursorsTabGML.h cursors_tab_gml) -stringify_gml(./GalleryGML/IconsTab.gml IconsTabGML.h icons_tab_gml) -stringify_gml(./GalleryGML/WizardsTab.gml WizardsTabGML.h wizards_tab_gml) -stringify_gml(DemoWizardPage1.gml DemoWizardPage1GML.h demo_wizard_page_1_gml) -stringify_gml(DemoWizardPage2.gml DemoWizardPage2GML.h demo_wizard_page_2_gml) - -set(SOURCES - main.cpp - GalleryWidget.cpp - DemoWizardDialog.cpp -) - -set(GENERATED_SOURCES - WindowGML.h - BasicsTabGML.h - SlidersTabGML.h - CursorsTabGML.h - IconsTabGML.h - WizardsTabGML.h - DemoWizardPage1GML.h - DemoWizardPage2GML.h -) - -serenity_app(WidgetGallery ICON app-widget-gallery) -target_link_libraries(WidgetGallery PRIVATE LibCore LibGfx LibGUI LibMain LibFileSystemAccessClient LibIPC) diff --git a/Userland/Demos/WidgetGallery/DemoWizardDialog.cpp b/Userland/Demos/WidgetGallery/DemoWizardDialog.cpp deleted file mode 100644 index 4b3664d89cb..00000000000 --- a/Userland/Demos/WidgetGallery/DemoWizardDialog.cpp +++ /dev/null @@ -1,79 +0,0 @@ -/* - * Copyright (c) 2021, Nick Vella - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "DemoWizardDialog.h" -#include -#include -#include -#include -#include -#include - -DemoWizardDialog::DemoWizardDialog(GUI::Window* parent_window) - : GUI::WizardDialog(parent_window) -{ - set_title("Demo Wizard"); - build().release_value_but_fixme_should_propagate_errors(); - - // Create the front cover - m_front_page = GUI::CoverWizardPage::create( - "Welcome to the SerenityOS demo wizard!"sv, - "This wizard demonstrates the amazing wizardry\ncapabilities of LibGUI :^)"sv) - .release_value_but_fixme_should_propagate_errors(); - m_front_page->on_next_page = [&]() { - return m_page_1; - }; - - // Create Page 1 - m_page_1 = GUI::WizardPage::create( - "Installation location"sv, - "Choose where Demo Application is installed on your computer."sv) - .release_value_but_fixme_should_propagate_errors(); - m_page_1->body_widget().load_from_gml(demo_wizard_page_1_gml).release_value_but_fixme_should_propagate_errors(); - m_page_1_location_text_box = m_page_1->body_widget().find_descendant_of_type_named("page_1_location_text_box"); - m_page_1->on_next_page = [&]() { - return m_page_2; - }; - - // Create Page 2 with a progress bar :^) - m_page_2 = GUI::WizardPage::create( - "Installation in progress..."sv, - "Please wait. Do not turn off your computer."sv) - .release_value_but_fixme_should_propagate_errors(); - m_page_2->body_widget().load_from_gml(demo_wizard_page_2_gml).release_value_but_fixme_should_propagate_errors(); - m_page_2_progressbar = m_page_2->body_widget().find_descendant_of_type_named("page_2_progressbar"); - m_page_2_timer = Core::Timer::create_repeating( - 100, [&]() { - if (m_page_2_progress_value < 100) - m_page_2_progress_value++; - m_page_2_progressbar->set_value(m_page_2_progress_value); - - // Go to final page on progress completion - if (m_page_2_progress_value == 100) { - m_page_2_progress_value = 0; - replace_page(*m_back_page); - } - }, - this); - m_page_2->on_page_enter = [&]() { - m_page_2_progress_value = 0; - m_page_2_timer->restart(); - }; - m_page_2->on_page_leave = [&]() { - m_page_2_progress_value = 0; - m_page_2_timer->stop(); - }; - // Don't set a on_next_page handler for page 2 as we automatically navigate to the final page on progress completion - - // Create the back cover - m_back_page = GUI::CoverWizardPage::create( - "Wizard complete."sv, - "That concludes the SerenityOS demo wizard :^)"sv) - .release_value_but_fixme_should_propagate_errors(); - m_back_page->set_is_final_page(true); - - push_page(*m_front_page); -} diff --git a/Userland/Demos/WidgetGallery/DemoWizardDialog.h b/Userland/Demos/WidgetGallery/DemoWizardDialog.h deleted file mode 100644 index cb7ca08563b..00000000000 --- a/Userland/Demos/WidgetGallery/DemoWizardDialog.h +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright (c) 2021, Nick Vella - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include -#include - -class DemoWizardDialog : public GUI::WizardDialog { - C_OBJECT(DemoWizardDialog); - -public: - ByteString page_1_location() { return m_page_1_location_text_box->get_text(); } - -private: - DemoWizardDialog(GUI::Window* parent_window); - - RefPtr m_front_page; - RefPtr m_page_1; - RefPtr m_page_1_location_text_box; - - RefPtr m_page_2; - RefPtr m_page_2_progressbar; - int m_page_2_progress_value { 0 }; - RefPtr m_page_2_timer; - - RefPtr m_back_page; -}; diff --git a/Userland/Demos/WidgetGallery/DemoWizardPage1.gml b/Userland/Demos/WidgetGallery/DemoWizardPage1.gml deleted file mode 100644 index 6aea235524f..00000000000 --- a/Userland/Demos/WidgetGallery/DemoWizardPage1.gml +++ /dev/null @@ -1,32 +0,0 @@ -@GUI::Widget { - layout: @GUI::VerticalBoxLayout { - margins: [20] - } - - @GUI::Label { - text: "Please select an installation directory." - text_alignment: "TopLeft" - fixed_height: 32 - } - - @GUI::Widget { - fixed_height: 25 - layout: @GUI::HorizontalBoxLayout {} - - @GUI::Label { - text: "Location: " - autosize: true - } - - @GUI::TextBox { - name: "page_1_location_text_box" - } - - @GUI::Button { - text: "Browse" - fixed_width: 75 - } - } - - @GUI::Layout::Spacer {} -} diff --git a/Userland/Demos/WidgetGallery/DemoWizardPage2.gml b/Userland/Demos/WidgetGallery/DemoWizardPage2.gml deleted file mode 100644 index 542509ca82f..00000000000 --- a/Userland/Demos/WidgetGallery/DemoWizardPage2.gml +++ /dev/null @@ -1,18 +0,0 @@ -@GUI::Widget { - layout: @GUI::VerticalBoxLayout { - margins: [20] - } - - @GUI::Label { - text: "Please wait..." - text_alignment: "TopLeft" - fixed_height: 32 - } - - @GUI::Progressbar { - name: "page_2_progressbar" - fixed_height: 28 - } - - @GUI::Layout::Spacer {} -} diff --git a/Userland/Demos/WidgetGallery/GalleryGML/BasicsTab.gml b/Userland/Demos/WidgetGallery/GalleryGML/BasicsTab.gml deleted file mode 100644 index 418fe54f9e2..00000000000 --- a/Userland/Demos/WidgetGallery/GalleryGML/BasicsTab.gml +++ /dev/null @@ -1,330 +0,0 @@ -@GUI::Widget { - name: "basics_tab" - layout: @GUI::VerticalBoxLayout { - margins: [4] - } - - @GUI::GroupBox { - preferred_height: "fit" - layout: @GUI::VerticalBoxLayout { - margins: [8] - } - - @GUI::HorizontalSplitter { - layout: @GUI::HorizontalBoxLayout { - spacing: 4 - } - - @GUI::Frame { - name: "label_frame" - frame_style: "SunkenPanel" - layout: @GUI::VerticalBoxLayout { - margins: [3, 4] - } - - @GUI::Label { - name: "enabled_label" - text: "Label" - min_height: 16 - } - - @GUI::Label { - name: "disabled_label" - text: "Disabled" - min_height: 16 - enabled: false - } - } - - @GUI::Widget { - layout: @GUI::VerticalBoxLayout { - margins: [0, 4] - } - - @GUI::Label { - name: "word_wrap_label" - word_wrap: true - text_alignment: "TopLeft" - text: "Lorem ipsum sistema serenitas, per construxit klingre sed quis awesomatia, ergo salve amici." - } - } - } - - @GUI::HorizontalSeparator {} - - @GUI::Widget { - fixed_height: 22 - layout: @GUI::HorizontalBoxLayout {} - - @GUI::ComboBox { - name: "frame_style_combobox" - placeholder: "Combo box" - } - - @GUI::ComboBox { - placeholder: "Disabled" - enabled: false - } - - @GUI::VerticalSeparator {} - - @GUI::SpinBox {} - - @GUI::SpinBox { - enabled: false - } - } - } - - @GUI::Widget { - fixed_height: 125 - layout: @GUI::VerticalBoxLayout { - margins: [3, 8] - } - - @GUI::Widget { - layout: @GUI::HorizontalBoxLayout {} - - @GUI::Widget { - layout: @GUI::VerticalBoxLayout {} - - @GUI::Layout::Spacer {} - - @GUI::Button { - name: "normal_button" - text: "Button" - } - - @GUI::Button { - name: "disabled_normal_button" - text: "Disabled" - enabled: false - } - - @GUI::Layout::Spacer {} - } - - @GUI::VerticalSeparator {} - - @GUI::Widget { - layout: @GUI::VerticalBoxLayout {} - - @GUI::Layout::Spacer {} - - @GUI::Button { - name: "enabled_coolbar_button" - text: "Coolbar Button" - button_style: "Coolbar" - } - - @GUI::Button { - name: "disabled_coolbar_button" - text: "Disabled" - enabled: false - button_style: "Coolbar" - } - - @GUI::Layout::Spacer {} - } - } - - @GUI::HorizontalSeparator {} - - @GUI::Widget { - layout: @GUI::HorizontalBoxLayout {} - - @GUI::Widget { - layout: @GUI::HorizontalBoxLayout {} - - @GUI::Widget { - fixed_width: 60 - layout: @GUI::VerticalBoxLayout {} - - @GUI::Layout::Spacer {} - - @GUI::RadioButton { - name: "top_radiobutton" - text: "Radio 1" - checked: true - } - - @GUI::RadioButton { - name: "bottom_radiobutton" - text: "Radio 2" - } - - @GUI::Layout::Spacer {} - } - - @GUI::Layout::Spacer {} - - @GUI::Widget { - fixed_width: 70 - layout: @GUI::VerticalBoxLayout {} - - @GUI::Layout::Spacer {} - - @GUI::CheckBox { - name: "top_checkbox" - text: "Checkbox" - } - - @GUI::CheckBox { - name: "bottom_checkbox" - text: "Disabled" - enabled: false - } - - @GUI::Layout::Spacer {} - } - - @GUI::Layout::Spacer {} - } - - @GUI::VerticalSeparator {} - - @GUI::Widget { - layout: @GUI::VerticalBoxLayout {} - - @GUI::Layout::Spacer {} - - @GUI::Button { - name: "icon_button" - text: "Icon Button" - } - - @GUI::Button { - name: "disabled_icon_button" - text: "Disabled" - enabled: false - } - - @GUI::Layout::Spacer {} - } - } - } - - @GUI::GroupBox { - layout: @GUI::VerticalBoxLayout { - margins: [6] - } - - @GUI::Widget { - fixed_height: 47 - layout: @GUI::HorizontalBoxLayout {} - - @GUI::Widget { - layout: @GUI::VerticalBoxLayout {} - - @GUI::TextBox { - placeholder: "Text box" - mode: "Editable" - } - - @GUI::TextBox { - text: "Disabled" - enabled: false - } - } - - @GUI::VerticalSeparator {} - - @GUI::Widget { - layout: @GUI::VerticalBoxLayout {} - - @GUI::TextBox { - text: "Read only" - mode: "ReadOnly" - } - - @GUI::TextBox { - text: "Display only" - mode: "DisplayOnly" - } - } - } - - @GUI::HorizontalSeparator {} - - @GUI::Widget { - layout: @GUI::HorizontalBoxLayout {} - - @GUI::TextEditor { - name: "text_editor" - placeholder: "Text editor" - } - - @GUI::VerticalSeparator {} - - @GUI::Widget { - layout: @GUI::VerticalBoxLayout {} - - @GUI::Widget { - fixed_height: 22 - layout: @GUI::HorizontalBoxLayout {} - - @GUI::ColorInput { - name: "font_colorinput" - placeholder: "Color Picker" - } - - @GUI::ColorInput { - placeholder: "Disabled" - enabled: false - } - } - - @GUI::Layout::Spacer {} - - @GUI::Button { - name: "font_button" - text: "Font Picker" - } - - @GUI::Button { - name: "file_button" - text: "File Picker" - } - - @GUI::Button { - name: "input_button" - text: "Input Box" - } - - @GUI::Layout::Spacer {} - } - } - - @GUI::HorizontalSeparator {} - - @GUI::Widget { - fixed_height: 22 - layout: @GUI::HorizontalBoxLayout {} - - @GUI::Widget { - layout: @GUI::HorizontalBoxLayout {} - - @GUI::ComboBox { - name: "msgbox_icon_combobox" - model_only: true - } - - @GUI::ComboBox { - name: "msgbox_buttons_combobox" - model_only: true - } - } - - @GUI::VerticalSeparator {} - - @GUI::Widget { - layout: @GUI::HorizontalBoxLayout {} - - @GUI::Button { - name: "msgbox_button" - text: "Message Box" - } - } - } - } -} diff --git a/Userland/Demos/WidgetGallery/GalleryGML/CursorsTab.gml b/Userland/Demos/WidgetGallery/GalleryGML/CursorsTab.gml deleted file mode 100644 index 489f620ec19..00000000000 --- a/Userland/Demos/WidgetGallery/GalleryGML/CursorsTab.gml +++ /dev/null @@ -1,17 +0,0 @@ -@GUI::Widget { - name: "cursors_tab" - layout: @GUI::VerticalBoxLayout { - margins: [4] - } - - @GUI::GroupBox { - layout: @GUI::VerticalBoxLayout { - margins: [6] - } - - @GUI::TableView { - name: "cursors_tableview" - font_size: 12 - } - } -} diff --git a/Userland/Demos/WidgetGallery/GalleryGML/IconsTab.gml b/Userland/Demos/WidgetGallery/GalleryGML/IconsTab.gml deleted file mode 100644 index 7ca363af28e..00000000000 --- a/Userland/Demos/WidgetGallery/GalleryGML/IconsTab.gml +++ /dev/null @@ -1,17 +0,0 @@ -@GUI::Widget { - name: "icons_tab" - layout: @GUI::VerticalBoxLayout { - margins: [4] - } - - @GUI::GroupBox { - layout: @GUI::VerticalBoxLayout { - margins: [6] - } - - @GUI::TableView { - name: "icons_tableview" - font_size: 12 - } - } -} diff --git a/Userland/Demos/WidgetGallery/GalleryGML/SlidersTab.gml b/Userland/Demos/WidgetGallery/GalleryGML/SlidersTab.gml deleted file mode 100644 index 82ebe553e9e..00000000000 --- a/Userland/Demos/WidgetGallery/GalleryGML/SlidersTab.gml +++ /dev/null @@ -1,174 +0,0 @@ -@GUI::Widget { - name: "sliders_tab" - layout: @GUI::VerticalBoxLayout { - margins: [4] - } - - @GUI::GroupBox { - preferred_height: "fit" - layout: @GUI::VerticalBoxLayout { - margins: [6] - } - - @GUI::GroupBox { - preferred_height: "fit" - layout: @GUI::HorizontalBoxLayout { - margins: [8] - } - - @GUI::HorizontalOpacitySlider { - name: "opacity_slider" - tooltip: "Opacity Slider" - } - - @GUI::VerticalSeparator {} - - @GUI::ValueSlider { - name: "opacity_value_slider" - min: 0 - max: 100 - value: 100 - tooltip: "Value Slider" - } - } - - @GUI::HorizontalSeparator {} - - @GUI::Frame { - frame_style: "SunkenPanel" - preferred_height: "fit" - layout: @GUI::VerticalBoxLayout { - margins: [1] - } - - @GUI::ImageWidget { - name: "opacity_imagewidget" - } - } - } - - @GUI::Widget { - preferred_height: "fit" - layout: @GUI::VerticalBoxLayout { - margins: [0, 8] - } - - @GUI::Scrollbar { - name: "enabled_scrollbar" - fixed_height: 16 - min: 0 - max: 100 - value: 50 - } - - @GUI::HorizontalSeparator {} - - @GUI::Scrollbar { - name: "disabled_scrollbar" - fixed_height: 16 - } - - @GUI::Layout::Spacer {} - } - - @GUI::GroupBox { - layout: @GUI::HorizontalBoxLayout { - margins: [6] - } - preferred_height: "opportunistic_grow" - - @GUI::Layout::Spacer {} - - @GUI::VerticalProgressbar { - name: "vertical_progressbar_left" - fixed_width: 36 - } - - @GUI::VerticalSlider { - name: "vertical_slider_left" - knob_size_mode: "Fixed" - min: 0 - max: 100 - value: 100 - tooltip: "Fixed" - } - - @GUI::Layout::Spacer {} - - @GUI::VerticalSeparator {} - - @GUI::VerticalSlider { - enabled: false - tooltip: "Disabled" - min: 0 - max: 10 - value: 5 - } - - @GUI::VerticalSeparator {} - - @GUI::Layout::Spacer {} - - @GUI::VerticalProgressbar { - name: "vertical_progressbar_right" - fixed_width: 36 - } - - @GUI::VerticalSlider { - name: "vertical_slider_right" - knob_size_mode: "Proportional" - min: 0 - max: 4 - value: 0 - tooltip: "Proportional" - } - - @GUI::Layout::Spacer {} - } - - @GUI::GroupBox { - layout: @GUI::VerticalBoxLayout { - margins: [6] - } - preferred_height: "fit" - - @GUI::Widget { - layout: @GUI::HorizontalBoxLayout {} - preferred_height: "fit" - - @GUI::HorizontalSlider { - name: "horizontal_slider_left" - knob_size_mode: "Fixed" - min: 0 - max: 100 - value: 0 - } - - @GUI::VerticalSeparator {} - - @GUI::HorizontalSlider { - enabled: false - min: 0 - max: 10 - value: 5 - } - - @GUI::VerticalSeparator {} - - @GUI::HorizontalSlider { - name: "horizontal_slider_right" - knob_size_mode: "Proportional" - min: 0 - max: 5 - value: 0 - } - } - - @GUI::HorizontalSeparator {} - - @GUI::HorizontalProgressbar { - name: "horizontal_progressbar" - fixed_height: 20 - } - } -} diff --git a/Userland/Demos/WidgetGallery/GalleryGML/Window.gml b/Userland/Demos/WidgetGallery/GalleryGML/Window.gml deleted file mode 100644 index bb8be74cd57..00000000000 --- a/Userland/Demos/WidgetGallery/GalleryGML/Window.gml +++ /dev/null @@ -1,11 +0,0 @@ -@GUI::Widget { - fill_with_background_color: true - layout: @GUI::VerticalBoxLayout { - margins: [4] - } - - @GUI::TabWidget { - name: "tab_widget" - reorder_allowed: true - } -} diff --git a/Userland/Demos/WidgetGallery/GalleryGML/WizardsTab.gml b/Userland/Demos/WidgetGallery/GalleryGML/WizardsTab.gml deleted file mode 100644 index 3f0778f2ca4..00000000000 --- a/Userland/Demos/WidgetGallery/GalleryGML/WizardsTab.gml +++ /dev/null @@ -1,24 +0,0 @@ -@GUI::Widget { - name: "wizards_tab" - layout: @GUI::VerticalBoxLayout { - margins: [4] - } - - @GUI::GroupBox { - layout: @GUI::VerticalBoxLayout { - margins: [6] - } - - @GUI::Button { - name: "wizard_button" - text: "Start Wizard" - } - - @GUI::HorizontalSeparator {} - - @GUI::TextEditor { - name: "wizard_output" - mode: "ReadOnly" - } - } -} diff --git a/Userland/Demos/WidgetGallery/GalleryModels.h b/Userland/Demos/WidgetGallery/GalleryModels.h deleted file mode 100644 index 0d14c47034e..00000000000 --- a/Userland/Demos/WidgetGallery/GalleryModels.h +++ /dev/null @@ -1,201 +0,0 @@ -/* - * Copyright (c) 2021-2022, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include -#include -#include - -class MouseCursorModel final : public GUI::Model { -public: - static NonnullRefPtr create() { return adopt_ref(*new MouseCursorModel); } - virtual ~MouseCursorModel() override = default; - - enum Column { - Bitmap, - Name, - __Count, - }; - - virtual int row_count(const GUI::ModelIndex&) const override { return m_cursors.size(); } - virtual int column_count(const GUI::ModelIndex&) const override { return Column::__Count; } - virtual ErrorOr column_name(int column_index) const override - { - switch (column_index) { - case Column::Bitmap: - return String {}; - case Column::Name: - return "Name"_string; - } - VERIFY_NOT_REACHED(); - } - - virtual GUI::Variant data(const GUI::ModelIndex& index, GUI::ModelRole role) const override - { - auto& cursor = m_cursors[index.row()]; - - if (role == GUI::ModelRole::Display) { - switch (index.column()) { - case Column::Bitmap: - if (!cursor.bitmap) - return {}; - return *cursor.bitmap; - case Column::Name: - return cursor.name; - } - VERIFY_NOT_REACHED(); - } - return {}; - } - - virtual void invalidate() override - { - m_cursors.clear(); - - Core::DirIterator iterator(ByteString::formatted("/res/cursor-themes/{}", GUI::ConnectionToWindowServer::the().get_cursor_theme()), Core::DirIterator::Flags::SkipDots); - - while (iterator.has_next()) { - auto path = iterator.next_full_path(); - if (path.ends_with(".ini"sv)) - continue; - if (path.contains("2x"sv)) - continue; - Cursor cursor; - cursor.path = move(path); - cursor.name = LexicalPath::basename(cursor.path); - - // FIXME: Animated cursor bitmaps - auto cursor_bitmap = Gfx::Bitmap::load_from_file(cursor.path).release_value_but_fixme_should_propagate_errors(); - auto cursor_bitmap_rect = cursor_bitmap->rect(); - - cursor.params = Gfx::CursorParams::parse_from_filename(cursor.name, cursor_bitmap_rect.center()).constrained(*cursor_bitmap); - cursor.bitmap = cursor_bitmap->cropped(Gfx::IntRect(Gfx::FloatRect(cursor_bitmap_rect).scaled(1.0 / cursor.params.frames(), 1.0))).release_value_but_fixme_should_propagate_errors(); - - m_cursors.append(move(cursor)); - } - - Model::invalidate(); - } - -private: - MouseCursorModel() = default; - - struct Cursor { - RefPtr bitmap; - ByteString path; - ByteString name; - Gfx::CursorParams params; - }; - - Vector m_cursors; -}; - -class FileIconsModel final : public GUI::Model { -public: - static NonnullRefPtr create() { return adopt_ref(*new FileIconsModel); } - virtual ~FileIconsModel() override = default; - - enum Column { - BigIcon, - LittleIcon, - Name, - __Count, - }; - - virtual int row_count(const GUI::ModelIndex&) const override { return m_icon_sets.size(); } - virtual int column_count(const GUI::ModelIndex&) const override { return Column::__Count; } - virtual ErrorOr column_name(int column_index) const override - { - switch (column_index) { - case Column::BigIcon: - return String {}; - case Column::LittleIcon: - return String {}; - case Column::Name: - return "Name"_string; - } - VERIFY_NOT_REACHED(); - } - - virtual GUI::Variant data(const GUI::ModelIndex& index, GUI::ModelRole role) const override - { - auto& icon_set = m_icon_sets[index.row()]; - - if (role == GUI::ModelRole::Display) { - switch (index.column()) { - case Column::BigIcon: - if (!icon_set.big_icon) - return {}; - return *icon_set.big_icon; - case Column::LittleIcon: - if (!icon_set.little_icon) - return {}; - return *icon_set.little_icon; - case Column::Name: - return icon_set.name; - } - VERIFY_NOT_REACHED(); - } - return {}; - } - - virtual void invalidate() override - { - m_icon_sets.clear(); - - Core::DirIterator big_iterator("/res/icons/32x32", Core::DirIterator::Flags::SkipDots); - - while (big_iterator.has_next()) { - auto path = big_iterator.next_full_path(); - if (!path.contains("filetype-"sv) && !path.contains("app-"sv)) - continue; - IconSet icon_set; - icon_set.big_icon = Gfx::Bitmap::load_from_file(path).release_value_but_fixme_should_propagate_errors(); - icon_set.name = LexicalPath::basename(path); - m_icon_sets.append(move(icon_set)); - } - - auto big_icons_found = m_icon_sets.size(); - - Core::DirIterator little_iterator("/res/icons/16x16", Core::DirIterator::Flags::SkipDots); - - while (little_iterator.has_next()) { - auto path = little_iterator.next_full_path(); - if (!path.contains("filetype-"sv) && !path.contains("app-"sv)) - continue; - IconSet icon_set; - icon_set.little_icon = Gfx::Bitmap::load_from_file(path).release_value_but_fixme_should_propagate_errors(); - icon_set.name = LexicalPath::basename(path); - for (size_t i = 0; i < big_icons_found; i++) { - if (icon_set.name == m_icon_sets[i].name) { - m_icon_sets[i].little_icon = icon_set.little_icon; - goto next_iteration; - } - } - m_icon_sets.append(move(icon_set)); - next_iteration: - continue; - } - - Model::invalidate(); - } - -private: - FileIconsModel() = default; - - struct IconSet { - RefPtr big_icon; - RefPtr little_icon; - ByteString name; - }; - - Vector m_icon_sets; -}; diff --git a/Userland/Demos/WidgetGallery/GalleryWidget.cpp b/Userland/Demos/WidgetGallery/GalleryWidget.cpp deleted file mode 100644 index edc9b9a9977..00000000000 --- a/Userland/Demos/WidgetGallery/GalleryWidget.cpp +++ /dev/null @@ -1,321 +0,0 @@ -/* - * Copyright (c) 2021-2022, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "GalleryWidget.h" -#include "DemoWizardDialog.h" -#include "GalleryModels.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -GalleryWidget::GalleryWidget() -{ - load_from_gml(window_gml).release_value_but_fixme_should_propagate_errors(); - - auto& tab_widget = *find_descendant_of_type_named("tab_widget"); - - auto& basics_tab = tab_widget.add_tab("Basics"_string); - basics_tab.load_from_gml(basics_tab_gml).release_value_but_fixme_should_propagate_errors(); - - m_enabled_label = basics_tab.find_descendant_of_type_named("enabled_label"); - m_label_frame = basics_tab.find_descendant_of_type_named("label_frame"); - - m_frame_shapes.append("No Frame"); - m_frame_shapes.append("Window"); - m_frame_shapes.append("Plain"); - m_frame_shapes.append("Raised Box"); - m_frame_shapes.append("Sunken Box"); - m_frame_shapes.append("Raised Container"); - m_frame_shapes.append("Sunken Container"); - m_frame_shapes.append("Raised Panel"); - m_frame_shapes.append("Sunken Panel"); - - m_frame_shape_combobox = basics_tab.find_descendant_of_type_named("frame_style_combobox"); - m_frame_shape_combobox->set_model(*GUI::ItemListModel::create(m_frame_shapes)); - - m_frame_shape_combobox->on_change = [&](auto&, auto const& index) { - m_label_frame->set_frame_style(static_cast(index.row())); - m_label_frame->update(); - }; - - m_frame_shape_combobox->on_return_pressed = [&]() { - m_enabled_label->set_text(String::from_byte_string(m_frame_shape_combobox->text()).release_value_but_fixme_should_propagate_errors()); - }; - - m_button_icons.append(Gfx::Bitmap::load_from_file("/res/icons/16x16/book-open.png"sv).release_value_but_fixme_should_propagate_errors()); - m_button_icons.append(Gfx::Bitmap::load_from_file("/res/icons/16x16/inspector-object.png"sv).release_value_but_fixme_should_propagate_errors()); - m_button_icons.append(Gfx::Bitmap::load_from_file("/res/icons/16x16/ladybug.png"sv).release_value_but_fixme_should_propagate_errors()); - - m_icon_button = basics_tab.find_descendant_of_type_named("icon_button"); - m_icon_button->set_icon(*m_button_icons[2]); - - m_disabled_icon_button = basics_tab.find_descendant_of_type_named("disabled_icon_button"); - m_disabled_icon_button->set_icon(*m_button_icons[2]); - - m_icon_button->on_click = [&](auto) { - static size_t i; - if (i >= m_button_icons.size()) - i = 0; - m_icon_button->set_icon(*m_button_icons[i]); - m_disabled_icon_button->set_icon(*m_button_icons[i]); - i++; - }; - - m_text_editor = basics_tab.find_descendant_of_type_named("text_editor"); - - m_font_button = basics_tab.find_descendant_of_type_named("font_button"); - m_font_button->set_icon(Gfx::Bitmap::load_from_file("/res/icons/16x16/app-font-editor.png"sv).release_value_but_fixme_should_propagate_errors()); - - m_font_button->on_click = [&](auto) { - auto picker = GUI::FontPicker::construct(window(), &m_text_editor->font(), false); - if (picker->exec() == GUI::Dialog::ExecResult::OK) { - m_text_editor->set_font(picker->font()); - } - }; - - m_file_button = basics_tab.find_descendant_of_type_named("file_button"); - m_file_button->set_icon(Gfx::Bitmap::load_from_file("/res/icons/16x16/open.png"sv).release_value_but_fixme_should_propagate_errors()); - - m_file_button->on_click = [&](auto) { - auto response = FileSystemAccessClient::Client::the().open_file(window()); - if (response.is_error()) - return; - m_text_editor->set_text(response.release_value().filename()); - }; - - m_input_button = basics_tab.find_descendant_of_type_named("input_button"); - m_input_button->set_icon(Gfx::Bitmap::load_from_file("/res/icons/16x16/properties.png"sv).release_value_but_fixme_should_propagate_errors()); - - m_input_button->on_click = [&](auto) { - String value; - if (GUI::InputBox::show(window(), value, "Enter input:"sv, "Input"sv, GUI::InputType::NonemptyText) == GUI::InputBox::ExecResult::OK) - m_text_editor->set_text(value); - }; - - m_font_colorinput = basics_tab.find_descendant_of_type_named("font_colorinput"); - - m_font_colorinput->on_change = [&]() { - auto palette = m_text_editor->palette(); - palette.set_color(Gfx::ColorRole::BaseText, m_font_colorinput->color()); - m_text_editor->set_palette(palette); - m_text_editor->update(); - }; - - m_msgbox_button = basics_tab.find_descendant_of_type_named("msgbox_button"); - m_msgbox_button->set_icon(Gfx::Bitmap::load_from_file("/res/icons/16x16/app-browser.png"sv).release_value_but_fixme_should_propagate_errors()); - - m_msgbox_type = GUI::MessageBox::Type::None; - m_msgbox_input_type = GUI::MessageBox::InputType::OK; - - m_msgbox_icons.append("None"); - m_msgbox_icons.append("Information"); - m_msgbox_icons.append("Warning"); - m_msgbox_icons.append("Error"); - m_msgbox_icons.append("Question"); - - m_msgbox_buttons.append("OK"); - m_msgbox_buttons.append("OK Cancel"); - m_msgbox_buttons.append("Yes No"); - m_msgbox_buttons.append("Yes No Cancel"); - - m_msgbox_icon_combobox = basics_tab.find_descendant_of_type_named("msgbox_icon_combobox"); - m_msgbox_icon_combobox->set_model(*GUI::ItemListModel::create(m_msgbox_icons)); - m_msgbox_icon_combobox->set_selected_index(0); - - m_msgbox_icon_combobox->on_change = [&](auto&, auto const& index) { - m_msgbox_type = static_cast(index.row()); - }; - - m_msgbox_buttons_combobox = basics_tab.find_descendant_of_type_named("msgbox_buttons_combobox"); - m_msgbox_buttons_combobox->set_model(*GUI::ItemListModel::create(m_msgbox_buttons)); - m_msgbox_buttons_combobox->set_selected_index(0); - - m_msgbox_buttons_combobox->on_change = [&](auto&, auto const& index) { - m_msgbox_input_type = static_cast(index.row()); - }; - - m_msgbox_button->on_click = [&](auto) { - GUI::MessageBox::show(window(), m_text_editor->text(), "Message"sv, m_msgbox_type, m_msgbox_input_type); - }; - - auto& sliders_tab = tab_widget.add_tab("Sliders"_string); - sliders_tab.load_from_gml(sliders_tab_gml).release_value_but_fixme_should_propagate_errors(); - - m_vertical_progressbar_left = sliders_tab.find_descendant_of_type_named("vertical_progressbar_left"); - m_vertical_progressbar_left->set_value(0); - - m_vertical_progressbar_right = sliders_tab.find_descendant_of_type_named("vertical_progressbar_right"); - m_vertical_progressbar_right->set_value(100); - - m_vertical_slider_left = sliders_tab.find_descendant_of_type_named("vertical_slider_left"); - m_vertical_slider_right = sliders_tab.find_descendant_of_type_named("vertical_slider_right"); - - m_vertical_slider_left->on_change = [&](auto value) { - m_vertical_progressbar_left->set_value(m_vertical_slider_left->max() - value); - }; - - m_vertical_slider_right->on_change = [&](auto value) { - m_vertical_progressbar_right->set_value((100 / m_vertical_slider_right->max()) * (m_vertical_slider_right->max() - value)); - }; - - m_horizontal_progressbar = sliders_tab.find_descendant_of_type_named("horizontal_progressbar"); - m_horizontal_progressbar->set_value(0); - - m_horizontal_slider_left = sliders_tab.find_descendant_of_type_named("horizontal_slider_left"); - m_horizontal_slider_right = sliders_tab.find_descendant_of_type_named("horizontal_slider_right"); - - m_horizontal_slider_left->on_change = [&](auto value) { - m_horizontal_progressbar->set_value(value); - if (!(value % (100 / m_horizontal_slider_right->max()))) - m_horizontal_slider_right->set_value(value / (100 / m_horizontal_slider_right->max())); - }; - - m_horizontal_slider_right->on_change = [&](auto value) { - m_horizontal_progressbar->set_value((value * 100) / m_horizontal_slider_right->max()); - m_horizontal_slider_left->set_value((value * 100) / m_horizontal_slider_right->max()); - }; - - m_enabled_scrollbar = sliders_tab.find_descendant_of_type_named("enabled_scrollbar"); - m_enabled_scrollbar->set_orientation(Orientation::Horizontal); - - m_disabled_scrollbar = sliders_tab.find_descendant_of_type_named("disabled_scrollbar"); - m_disabled_scrollbar->set_orientation(Orientation::Horizontal); - - m_opacity_imagewidget = sliders_tab.find_descendant_of_type_named("opacity_imagewidget"); - m_opacity_imagewidget->load_from_file("/res/graphics/brand-banner.png"sv); - - m_opacity_slider = sliders_tab.find_descendant_of_type_named("opacity_slider"); - - m_opacity_slider->on_change = [&](auto percent) { - m_opacity_imagewidget->set_opacity_percent(percent); - m_opacity_value_slider->set_value(percent); - }; - - m_opacity_value_slider = sliders_tab.find_descendant_of_type_named("opacity_value_slider"); - m_opacity_value_slider->set_range(0, 100); - - m_opacity_value_slider->on_change = [&](auto percent) { - m_opacity_imagewidget->set_opacity_percent(percent); - m_opacity_slider->set_value(percent); - }; - - auto& wizards_tab = tab_widget.add_tab("Wizards"_string); - wizards_tab.load_from_gml(wizards_tab_gml).release_value_but_fixme_should_propagate_errors(); - - m_wizard_button = wizards_tab.find_descendant_of_type_named("wizard_button"); - m_wizard_output = wizards_tab.find_descendant_of_type_named("wizard_output"); - m_wizard_output->set_should_hide_unnecessary_scrollbars(true); - - char const* serenityos_ascii = { - " ____ _ __ ____ ____\n" - " / __/__ _______ ___ (_) /___ __/ __ \\/ __/\n" - " _\\ \\/ -_) __/ -_) _ \\/ / __/ // / /_/ /\\ \\\n" - "/___/\\__/_/ \\__/_//_/_/\\__/\\_, /\\____/___/\n" - " /___/\n" - }; - - char const* wizard_ascii = { - " _,-'|\n" - " ,-'._ |\n" - " .||, |####\\ |\n" - "\\`' ,/ \\'L' | |\n" - "= ,. = |-,#| |\n" - "/ || \\ ,-'\\#/,'`.\n" - " || ,' `,,. `.\n" - " ,|____,' , ,;' \\| |\n" - " (3|\\ _/|/' _| |\n" - " ||/,-'' | >-'' _,\\\\\n" - " ||' ==\\ ,-' ,'\n" - " || | V \\ ,|\n" - " || | |` |\n" - " || | | \\\n" - " || | \\ \\\n" - " || | | \\\n" - " || | \\_,-'\n" - " || |___,,--')_\\\n" - " || |_| _ccc/-\n" - " || ccc/__\n" - " _||_-\n" - }; - - m_wizard_output->set_text(ByteString::formatted("{}{}", serenityos_ascii, wizard_ascii)); - - m_wizard_button->on_click = [&](auto) { - StringBuilder sb; - sb.append(m_wizard_output->get_text()); - sb.append("\nWizard started."sv); - m_wizard_output->set_text(sb.to_byte_string()); - - auto wizard = DemoWizardDialog::try_create(window()).release_value_but_fixme_should_propagate_errors(); - auto result = wizard->exec(); - - sb.append(ByteString::formatted("\nWizard execution complete.\nDialog ExecResult code: {}", to_underlying(result))); - if (result == GUI::Dialog::ExecResult::OK) - sb.append(ByteString::formatted(" (ExecResult::OK)\n'Installation' location: \"{}\"", wizard->page_1_location())); - m_wizard_output->set_text(sb.string_view()); - }; - - auto& cursors_tab = tab_widget.add_tab("Cursors"_string); - cursors_tab.load_from_gml(cursors_tab_gml).release_value_but_fixme_should_propagate_errors(); - - m_cursors_tableview = cursors_tab.find_descendant_of_type_named("cursors_tableview"); - m_cursors_tableview->set_highlight_selected_rows(true); - m_cursors_tableview->set_alternating_row_colors(false); - m_cursors_tableview->set_vertical_padding(16); - m_cursors_tableview->set_column_headers_visible(false); - m_cursors_tableview->set_highlight_key_column(false); - - auto sorting_proxy_model = MUST(GUI::SortingProxyModel::create(MouseCursorModel::create())); - sorting_proxy_model->set_sort_role(GUI::ModelRole::Display); - - m_cursors_tableview->set_model(sorting_proxy_model); - m_cursors_tableview->set_key_column_and_sort_order(MouseCursorModel::Column::Name, GUI::SortOrder::Ascending); - m_cursors_tableview->model()->invalidate(); - m_cursors_tableview->set_column_width(0, 25); - - m_cursors_tableview->on_activation = [&](const GUI::ModelIndex& index) { - auto icon_index = index.model()->index(index.row(), MouseCursorModel::Column::Bitmap); - m_cursors_tableview->set_override_cursor(NonnullRefPtr(icon_index.data().as_bitmap())); - }; - - auto& icons_tab = tab_widget.add_tab("Icons"_string); - icons_tab.load_from_gml(icons_tab_gml).release_value_but_fixme_should_propagate_errors(); - - m_icons_tableview = icons_tab.find_descendant_of_type_named("icons_tableview"); - m_icons_tableview->set_highlight_selected_rows(true); - m_icons_tableview->set_alternating_row_colors(false); - m_icons_tableview->set_vertical_padding(24); - m_icons_tableview->set_column_headers_visible(false); - m_icons_tableview->set_highlight_key_column(false); - - auto sorting_proxy_icons_model = MUST(GUI::SortingProxyModel::create(FileIconsModel::create())); - sorting_proxy_icons_model->set_sort_role(GUI::ModelRole::Display); - - m_icons_tableview->set_model(sorting_proxy_icons_model); - m_icons_tableview->set_key_column_and_sort_order(FileIconsModel::Column::Name, GUI::SortOrder::Ascending); - m_icons_tableview->model()->invalidate(); - m_icons_tableview->set_column_width(0, 36); - m_icons_tableview->set_column_width(1, 20); -} diff --git a/Userland/Demos/WidgetGallery/GalleryWidget.h b/Userland/Demos/WidgetGallery/GalleryWidget.h deleted file mode 100644 index d94c9f1d9c5..00000000000 --- a/Userland/Demos/WidgetGallery/GalleryWidget.h +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Copyright (c) 2021-2022, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -class GalleryWidget final : public GUI::Widget { - C_OBJECT(GalleryWidget) -public: - virtual ~GalleryWidget() override = default; - -private: - GalleryWidget(); - - RefPtr m_font_button; - RefPtr m_file_button; - RefPtr m_icon_button; - RefPtr m_input_button; - RefPtr m_wizard_button; - RefPtr m_msgbox_button; - RefPtr m_disabled_icon_button; - - RefPtr m_frame_shape_combobox; - RefPtr m_msgbox_icon_combobox; - RefPtr m_msgbox_buttons_combobox; - - RefPtr m_vertical_slider_left; - RefPtr m_vertical_slider_right; - RefPtr m_horizontal_slider_left; - RefPtr m_horizontal_slider_right; - - RefPtr m_vertical_progressbar_left; - RefPtr m_vertical_progressbar_right; - RefPtr m_horizontal_progressbar; - - RefPtr m_enabled_scrollbar; - RefPtr m_disabled_scrollbar; - - RefPtr m_text_editor; - RefPtr m_wizard_output; - - RefPtr m_label_frame; - RefPtr m_enabled_label; - RefPtr m_font_colorinput; - RefPtr m_icons_tableview; - RefPtr m_cursors_tableview; - RefPtr m_opacity_slider; - RefPtr m_opacity_value_slider; - RefPtr m_opacity_imagewidget; - - Vector m_frame_shapes; - Vector m_msgbox_icons; - Vector m_msgbox_buttons; - Vector> m_button_icons; - - GUI::MessageBox::Type m_msgbox_type; - GUI::MessageBox::InputType m_msgbox_input_type; -}; diff --git a/Userland/Demos/WidgetGallery/main.cpp b/Userland/Demos/WidgetGallery/main.cpp deleted file mode 100644 index cae9c422dd4..00000000000 --- a/Userland/Demos/WidgetGallery/main.cpp +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright (c) 2018-2021, Andreas Kling - * Copyright (c) 2020, Linus Groh - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "GalleryWidget.h" -#include -#include -#include -#include -#include - -ErrorOr serenity_main(Main::Arguments arguments) -{ - TRY(Core::System::pledge("stdio recvfd sendfd rpath unix thread")); - auto app = TRY(GUI::Application::create(arguments)); - - TRY(Core::System::unveil("/tmp/session/%sid/portal/filesystemaccess", "rw")); - TRY(Core::System::unveil("/res", "r")); - TRY(Core::System::unveil("/etc/FileIconProvider.ini", "r")); - TRY(Core::System::unveil(nullptr, nullptr)); - auto app_icon = TRY(GUI::Icon::try_create_default_icon("app-widget-gallery"sv)); - - auto window = GUI::Window::construct(); - window->resize(430, 480); - window->set_title("Widget Gallery"); - window->set_icon(app_icon.bitmap_for_size(16)); - (void)window->set_main_widget(); - window->show(); - - return app->exec(); -} diff --git a/Userland/DevTools/CMakeLists.txt b/Userland/DevTools/CMakeLists.txt deleted file mode 100644 index b61e03d54bd..00000000000 --- a/Userland/DevTools/CMakeLists.txt +++ /dev/null @@ -1,4 +0,0 @@ -add_subdirectory(GMLPlayground) -add_subdirectory(Profiler) -add_subdirectory(HackStudio) -add_subdirectory(SQLStudio) diff --git a/Userland/DevTools/GMLPlayground/CMakeLists.txt b/Userland/DevTools/GMLPlayground/CMakeLists.txt deleted file mode 100644 index 775ebe52328..00000000000 --- a/Userland/DevTools/GMLPlayground/CMakeLists.txt +++ /dev/null @@ -1,19 +0,0 @@ -serenity_component( - GMLPlayground - RECOMMENDED - TARGETS GMLPlayground -) - -stringify_gml(GMLPlaygroundWindow.gml GMLPlaygroundWindowGML.h gml_playground_window_gml) - -set(SOURCES - MainWidget.cpp - main.cpp -) - -set(GENERATED_SOURCES - GMLPlaygroundWindowGML.h -) - -serenity_app(GMLPlayground ICON app-gml-playground) -target_link_libraries(GMLPlayground PRIVATE LibConfig LibCore LibDesktop LibFileSystemAccessClient LibGfx LibGUI LibMain LibSyntax LibURL) diff --git a/Userland/DevTools/GMLPlayground/GMLPlaygroundWindow.gml b/Userland/DevTools/GMLPlayground/GMLPlaygroundWindow.gml deleted file mode 100644 index 23d041f4b72..00000000000 --- a/Userland/DevTools/GMLPlayground/GMLPlaygroundWindow.gml +++ /dev/null @@ -1,27 +0,0 @@ -@GUI::Widget { - layout: @GUI::VerticalBoxLayout {} - fill_with_background_color: true - - @GUI::ToolbarContainer { - @GUI::Toolbar { - name: "toolbar" - } - } - - @GUI::HorizontalSplitter { - layout: @GUI::HorizontalBoxLayout {} - name: "splitter" - - @GUI::TextEditor { - name: "text_editor" - } - - @GUI::Frame { - name: "preview_frame" - } - } - - @GUI::Statusbar { - name: "statusbar" - } -} diff --git a/Userland/DevTools/GMLPlayground/MainWidget.cpp b/Userland/DevTools/GMLPlayground/MainWidget.cpp deleted file mode 100644 index 6d1ee29073c..00000000000 --- a/Userland/DevTools/GMLPlayground/MainWidget.cpp +++ /dev/null @@ -1,349 +0,0 @@ -/* - * Copyright (c) 2020-2021, Andreas Kling - * Copyright (c) 2021, Julius Heijmen - * Copyright (c) 2022, kleines Filmröllchen - * Copyright (c) 2022-2023, Sam Atkins - * Copyright (c) 2023, Karol Kosek - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "MainWidget.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace { - -class UnregisteredWidget final : public GUI::Widget { - C_OBJECT(UnregisteredWidget); - -private: - UnregisteredWidget(ByteString const& class_name); - - virtual void paint_event(GUI::PaintEvent& event) override; - - ByteString m_text; -}; - -UnregisteredWidget::UnregisteredWidget(ByteString const& class_name) -{ - StringBuilder builder; - builder.append(class_name); - builder.append("\nnot registered"sv); - m_text = builder.to_byte_string(); -} - -void UnregisteredWidget::paint_event(GUI::PaintEvent& event) -{ - GUI::Painter painter(*this); - painter.add_clip_rect(event.rect()); - painter.fill_rect(event.rect(), Gfx::Color::DarkRed); - painter.draw_text(rect(), m_text, Gfx::TextAlignment::Center, Color::White); -} - -} - -ErrorOr> MainWidget::try_create(GUI::Icon const& icon) -{ - auto main_widget = TRY(adopt_nonnull_ref_or_enomem(new (nothrow) MainWidget())); - TRY(main_widget->load_from_gml(gml_playground_window_gml)); - main_widget->m_icon = icon; - - main_widget->m_toolbar = main_widget->find_descendant_of_type_named("toolbar"); - main_widget->m_splitter = main_widget->find_descendant_of_type_named("splitter"); - main_widget->m_editor = main_widget->find_descendant_of_type_named("text_editor"); - main_widget->m_preview_frame_widget = main_widget->find_descendant_of_type_named("preview_frame"); - main_widget->m_statusbar = main_widget->find_descendant_of_type_named("statusbar"); - - main_widget->m_preview_window = GUI::Window::construct(main_widget); - main_widget->m_preview_window->set_title("Preview - GML Playground"); - main_widget->m_preview_window->set_icon(icon.bitmap_for_size(16)); - main_widget->m_preview_window_widget = main_widget->m_preview_window->set_main_widget(); - main_widget->m_preview_window_widget->set_fill_with_background_color(true); - - main_widget->m_preview = main_widget->m_preview_frame_widget; - - main_widget->m_editor->set_syntax_highlighter(TRY(try_make())); - main_widget->m_editor->set_autocomplete_provider(TRY(try_make())); - main_widget->m_editor->set_should_autocomplete_automatically(true); - main_widget->m_editor->set_automatic_indentation_enabled(true); - main_widget->m_editor->set_ruler_visible(true); - - main_widget->m_editor->on_change = [main_widget = main_widget.ptr()] { - main_widget->m_preview->remove_all_children(); - // FIXME: Parsing errors happen while the user is typing. What should we do about them? - (void)main_widget->m_preview->load_from_gml(main_widget->m_editor->text(), [](StringView class_name) -> ErrorOr> { - return UnregisteredWidget::try_create(class_name); - }); - }; - - main_widget->m_editor->on_modified_change = [main_widget = main_widget.ptr()](bool modified) { - main_widget->window()->set_modified(modified); - }; - - return main_widget; -} - -MainWidget::MainWidget() -{ - GUI::Application::the()->on_action_enter = [this](GUI::Action& action) { - m_statusbar->set_override_text(action.status_tip()); - }; - GUI::Application::the()->on_action_leave = [this](GUI::Action&) { - m_statusbar->set_override_text({}); - }; -} - -void MainWidget::update_title() -{ - window()->set_title(ByteString::formatted("{}[*] - GML Playground", m_file_path.is_empty() ? "Untitled"sv : m_file_path.view())); -} - -void MainWidget::load_file(FileSystemAccessClient::File file) -{ - auto buffer_or_error = file.stream().read_until_eof(); - if (buffer_or_error.is_error()) - return; - - m_editor->set_text(buffer_or_error.release_value()); - m_editor->set_focus(true); - - m_file_path = file.filename(); - update_title(); - - GUI::Application::the()->set_most_recently_open_file(file.filename()); -} - -ErrorOr MainWidget::initialize_menubar(GUI::Window& window) -{ - auto file_menu = window.add_menu("&File"_string); - - m_save_as_action = GUI::CommonActions::make_save_as_action([&](auto&) { - LexicalPath initial_path(m_file_path.is_empty() ? "Untitled.gml" : m_file_path); - auto response = FileSystemAccessClient::Client::the().save_file(&window, initial_path.title(), initial_path.extension()); - if (response.is_error()) - return; - - auto file = response.value().release_stream(); - if (auto result = m_editor->write_to_file(*file); result.is_error()) { - GUI::MessageBox::show(&window, ByteString::formatted("Unable to save file: {}\n"sv, result.release_error()), "Error"sv, GUI::MessageBox::Type::Error); - return; - } - m_file_path = response.value().filename(); - update_title(); - - GUI::Application::the()->set_most_recently_open_file(response.value().filename()); - }); - - m_save_action = GUI::CommonActions::make_save_action([&](auto&) { - if (m_file_path.is_empty()) { - m_save_as_action->activate(); - return; - } - auto response = FileSystemAccessClient::Client::the().request_file(&window, m_file_path, Core::File::OpenMode::Truncate | Core::File::OpenMode::Write); - if (response.is_error()) - return; - - auto file = response.value().release_stream(); - if (auto result = m_editor->write_to_file(*file); result.is_error()) { - GUI::MessageBox::show(&window, ByteString::formatted("Unable to save file: {}\n"sv, result.release_error()), "Error"sv, GUI::MessageBox::Type::Error); - return; - } - update_title(); - }); - - auto open_action = GUI::CommonActions::make_open_action([&](auto&) { - if (request_close() == GUI::Window::CloseRequestDecision::StayOpen) - return; - FileSystemAccessClient::OpenFileOptions options { - .path = "/usr/src/serenity/Userland/Applications"sv, - .allowed_file_types = Vector { - GUI::FileTypeFilter { "GML Files", { { "gml" } } }, - GUI::FileTypeFilter::all_files(), - } - }; - auto response = FileSystemAccessClient::Client::the().open_file(&window, options); - if (response.is_error()) - return; - - load_file(response.release_value()); - }); - - file_menu->add_action(open_action); - file_menu->add_action(*m_save_action); - file_menu->add_action(*m_save_as_action); - file_menu->add_separator(); - - file_menu->add_recent_files_list([&](auto& action) { - if (request_close() == GUI::Window::CloseRequestDecision::StayOpen) - return; - auto response = FileSystemAccessClient::Client::the().request_file_read_only_approved(&window, action.text()); - if (response.is_error()) - return; - load_file(response.release_value()); - }); - - file_menu->add_action(GUI::CommonActions::make_quit_action([&](auto&) { - if (window.on_close_request() == GUI::Window::CloseRequestDecision::Close) - GUI::Application::the()->quit(); - })); - - auto edit_menu = window.add_menu("&Edit"_string); - edit_menu->add_action(m_editor->undo_action()); - edit_menu->add_action(m_editor->redo_action()); - edit_menu->add_separator(); - edit_menu->add_action(m_editor->cut_action()); - edit_menu->add_action(m_editor->copy_action()); - edit_menu->add_action(m_editor->paste_action()); - edit_menu->add_separator(); - edit_menu->add_action(m_editor->select_all_action()); - edit_menu->add_action(m_editor->go_to_line_or_column_action()); - edit_menu->add_separator(); - - auto format_gml_action = GUI::Action::create("&Format GML", { Mod_Ctrl | Mod_Shift, Key_I }, TRY(Gfx::Bitmap::load_from_file("/res/icons/16x16/reformat.png"sv)), [&](auto&) { - auto formatted_gml_or_error = GUI::GML::format_gml(m_editor->text()); - if (!formatted_gml_or_error.is_error()) { - m_editor->replace_all_text_without_resetting_undo_stack(formatted_gml_or_error.release_value(), "Format GML"sv); - } else { - GUI::MessageBox::show( - &window, - ByteString::formatted("GML could not be formatted: {}", formatted_gml_or_error.error()), - "Error"sv, - GUI::MessageBox::Type::Error); - } - }); - edit_menu->add_action(format_gml_action); - - auto vim_emulation_setting_action = GUI::Action::create_checkable("&Vim Emulation", { Mod_Ctrl | Mod_Shift | Mod_Alt, Key_V }, [&](auto& action) { - if (action.is_checked()) - m_editor->set_editing_engine(make()); - else - m_editor->set_editing_engine(make()); - }); - vim_emulation_setting_action->set_checked(false); - edit_menu->add_action(vim_emulation_setting_action); - - auto view_menu = window.add_menu("&View"_string); - m_views_group.set_exclusive(true); - m_views_group.set_unchecking_allowed(false); - - m_view_frame_action = GUI::Action::create_checkable("&Frame", [&](auto&) { - dbgln("View switched to frame"); - m_preview = m_preview_frame_widget; - m_editor->on_change(); - m_preview_window->hide(); - m_preview_frame_widget->set_preferred_width(m_splitter->width() / 2); - m_preview_frame_widget->set_visible(true); - }); - view_menu->add_action(*m_view_frame_action); - m_views_group.add_action(*m_view_frame_action); - m_view_frame_action->set_checked(true); - - m_view_window_action = GUI::Action::create_checkable("&Window", [&](auto&) { - dbgln("View switched to window"); - m_preview = m_preview_window_widget; - m_editor->on_change(); - m_preview_window->resize(400, 300); - m_preview_window->show(); - m_preview_frame_widget->set_visible(false); - }); - view_menu->add_action(*m_view_window_action); - m_views_group.add_action(*m_view_window_action); - view_menu->add_separator(); - view_menu->add_action(GUI::CommonActions::make_fullscreen_action([&](auto&) { - window.set_fullscreen(!window.is_fullscreen()); - })); - - m_preview_window->on_close = [&] { - m_view_frame_action->activate(); - }; - - auto help_menu = window.add_menu("&Help"_string); - help_menu->add_action(GUI::CommonActions::make_command_palette_action(&window)); - help_menu->add_action(GUI::CommonActions::make_help_action([](auto&) { - Desktop::Launcher::open(URL::create_with_file_scheme("/usr/share/man/man1/Applications/GMLPlayground.md"), "/bin/Help"); - })); - help_menu->add_action(GUI::CommonActions::make_about_action("GML Playground"_string, m_icon, &window)); - - m_toolbar->add_action(open_action); - m_toolbar->add_action(*m_save_action); - m_toolbar->add_action(*m_save_as_action); - m_toolbar->add_separator(); - m_toolbar->add_action(m_editor->cut_action()); - m_toolbar->add_action(m_editor->copy_action()); - m_toolbar->add_action(m_editor->paste_action()); - m_toolbar->add_separator(); - m_toolbar->add_action(m_editor->undo_action()); - m_toolbar->add_action(m_editor->redo_action()); - m_toolbar->add_separator(); - m_toolbar->add_action(format_gml_action); - - return {}; -} - -GUI::Window::CloseRequestDecision MainWidget::request_close() -{ - if (!window()->is_modified()) - return GUI::Window::CloseRequestDecision::Close; - - auto result = GUI::MessageBox::ask_about_unsaved_changes(window(), m_file_path, m_editor->document().undo_stack().last_unmodified_timestamp()); - if (result == GUI::MessageBox::ExecResult::Yes) { - m_save_action->activate(); - if (window()->is_modified()) - return GUI::Window::CloseRequestDecision::StayOpen; - return GUI::Window::CloseRequestDecision::Close; - } - - if (result == GUI::MessageBox::ExecResult::No) - return GUI::Window::CloseRequestDecision::Close; - - return GUI::Window::CloseRequestDecision::StayOpen; -} - -void MainWidget::drag_enter_event(GUI::DragEvent& event) -{ - auto const& mime_types = event.mime_types(); - if (mime_types.contains_slow("text/uri-list"sv)) - event.accept(); -} - -void MainWidget::drop_event(GUI::DropEvent& event) -{ - event.accept(); - window()->move_to_front(); - - if (event.mime_data().has_urls()) { - auto urls = event.mime_data().urls(); - if (urls.is_empty()) - return; - if (urls.size() > 1) { - GUI::MessageBox::show(window(), "GML Playground can only open one file at a time!"sv, "One at a time please!"sv, GUI::MessageBox::Type::Error); - return; - } - if (request_close() == GUI::Window::CloseRequestDecision::StayOpen) - return; - - auto response = FileSystemAccessClient::Client::the().request_file_read_only_approved(window(), urls.first().serialize_path()); - if (response.is_error()) - return; - load_file(response.release_value()); - } -} diff --git a/Userland/DevTools/GMLPlayground/MainWidget.h b/Userland/DevTools/GMLPlayground/MainWidget.h deleted file mode 100644 index 7d2bb4df5da..00000000000 --- a/Userland/DevTools/GMLPlayground/MainWidget.h +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright (c) 2020-2021, Andreas Kling - * Copyright (c) 2021, Julius Heijmen - * Copyright (c) 2022, kleines Filmröllchen - * Copyright (c) 2022-2023, Sam Atkins - * Copyright (c) 2023, Karol Kosek - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include - -class MainWidget final : public GUI::Widget { - C_OBJECT_ABSTRACT(MainWidget) - -public: - static ErrorOr> try_create(GUI::Icon const&); - ErrorOr initialize_menubar(GUI::Window&); - GUI::Window::CloseRequestDecision request_close(); - - void load_file(FileSystemAccessClient::File); - void update_title(); - - GUI::TextEditor& editor() const { return *m_editor; } - -private: - MainWidget(); - virtual void drag_enter_event(GUI::DragEvent&) override; - virtual void drop_event(GUI::DropEvent&) override; - - RefPtr m_save_action; - RefPtr m_save_as_action; - RefPtr m_editor; - RefPtr m_toolbar; - RefPtr m_splitter; - RefPtr m_statusbar; - - RefPtr m_preview_frame_widget; - RefPtr m_preview_window; - RefPtr m_preview_window_widget; - GUI::Widget* m_preview; - - GUI::ActionGroup m_views_group; - RefPtr m_view_frame_action; - RefPtr m_view_window_action; - - GUI::Icon m_icon; - ByteString m_file_path; -}; diff --git a/Userland/DevTools/GMLPlayground/main.cpp b/Userland/DevTools/GMLPlayground/main.cpp deleted file mode 100644 index 0422e6d1924..00000000000 --- a/Userland/DevTools/GMLPlayground/main.cpp +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright (c) 2020-2021, Andreas Kling - * Copyright (c) 2021, Julius Heijmen - * Copyright (c) 2022, kleines Filmröllchen - * Copyright (c) 2022-2024, Sam Atkins - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "MainWidget.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include - -ErrorOr serenity_main(Main::Arguments arguments) -{ - TRY(Core::System::pledge("stdio thread recvfd sendfd cpath rpath wpath unix")); - auto app = TRY(GUI::Application::create(arguments)); - - Config::enable_permissive_mode(); - Config::pledge_domain("GMLPlayground"); - app->set_config_domain("GMLPlayground"_string); - - TRY(Core::System::unveil("/res", "r")); - TRY(Core::System::unveil("/tmp/session/%sid/portal/launch", "rw")); - TRY(Core::System::unveil("/tmp/session/%sid/portal/filesystemaccess", "rw")); - TRY(Core::System::unveil(nullptr, nullptr)); - - TRY(Desktop::Launcher::add_allowed_handler_with_only_specific_urls("/bin/Help", { URL::create_with_file_scheme("/usr/share/man/man1/Applications/GMLPlayground.md") })); - TRY(Desktop::Launcher::seal_allowlist()); - - StringView path; - Core::ArgsParser args_parser; - args_parser.add_positional_argument(path, "GML file to edit", "file", Core::ArgsParser::Required::No); - args_parser.parse(arguments); - - auto app_icon = TRY(GUI::Icon::try_create_default_icon("app-gml-playground"sv)); - auto window = GUI::Window::construct(); - window->set_title("GML Playground"); - window->set_icon(app_icon.bitmap_for_size(16)); - window->restore_size_and_position("GMLPlayground"sv, "Window"sv, { { 800, 600 } }); - window->save_size_and_position_on_close("GMLPlayground"sv, "Window"sv); - - auto main_widget = TRY(MainWidget::try_create(app_icon)); - window->set_main_widget(main_widget); - - TRY(main_widget->initialize_menubar(window)); - - window->on_close_request = [&]() -> GUI::Window::CloseRequestDecision { - return main_widget->request_close(); - }; - - window->show(); - - if (path.is_empty()) { - main_widget->editor().set_text(R"~~~(@GUI::Frame { - layout: @GUI::VerticalBoxLayout { - } - - // Now add some widgets! -} -)~~~"sv); - main_widget->editor().set_cursor(4, 28); // after "...widgets!" - main_widget->update_title(); - } else { - auto file = TRY(FileSystemAccessClient::Client::the().request_file_read_only_approved(window, path)); - main_widget->load_file(move(file)); - } - - return app->exec(); -} diff --git a/Userland/DevTools/HackStudio/AutoCompleteResponse.h b/Userland/DevTools/HackStudio/AutoCompleteResponse.h deleted file mode 100644 index ca4cb4c8f97..00000000000 --- a/Userland/DevTools/HackStudio/AutoCompleteResponse.h +++ /dev/null @@ -1,125 +0,0 @@ -/* - * Copyright (c) 2020, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include -#include - -namespace IPC { - -template<> -inline ErrorOr encode(Encoder& encoder, CodeComprehension::AutocompleteResultEntry const& response) -{ - TRY(encoder.encode(response.completion)); - TRY(encoder.encode(response.partial_input_length)); - TRY(encoder.encode(response.language)); - TRY(encoder.encode(response.display_text)); - TRY(encoder.encode(response.hide_autocomplete_after_applying)); - return {}; -} - -template<> -inline ErrorOr decode(Decoder& decoder) -{ - auto completion = TRY(decoder.decode()); - auto partial_input_length = TRY(decoder.decode()); - auto language = TRY(decoder.decode()); - auto display_text = TRY(decoder.decode()); - auto hide_autocomplete_after_applying = TRY(decoder.decode()); - - return CodeComprehension::AutocompleteResultEntry { move(completion), partial_input_length, language, move(display_text), hide_autocomplete_after_applying }; -} - -template<> -inline ErrorOr encode(Encoder& encoder, CodeComprehension::ProjectLocation const& location) -{ - TRY(encoder.encode(location.file)); - TRY(encoder.encode(location.line)); - TRY(encoder.encode(location.column)); - return {}; -} - -template<> -inline ErrorOr decode(Decoder& decoder) -{ - auto file = TRY(decoder.decode()); - auto line = TRY(decoder.decode()); - auto column = TRY(decoder.decode()); - - return CodeComprehension::ProjectLocation { move(file), line, column }; -} - -template<> -inline ErrorOr encode(Encoder& encoder, CodeComprehension::Declaration const& declaration) -{ - TRY(encoder.encode(declaration.name)); - TRY(encoder.encode(declaration.position)); - TRY(encoder.encode(declaration.type)); - TRY(encoder.encode(declaration.scope)); - return {}; -} - -template<> -inline ErrorOr decode(Decoder& decoder) -{ - auto name = TRY(decoder.decode()); - auto position = TRY(decoder.decode()); - auto type = TRY(decoder.decode()); - auto scope = TRY(decoder.decode()); - - return CodeComprehension::Declaration { move(name), position, type, move(scope) }; -} - -template<> -inline ErrorOr encode(Encoder& encoder, CodeComprehension::TodoEntry const& entry) -{ - TRY(encoder.encode(entry.content)); - TRY(encoder.encode(entry.filename)); - TRY(encoder.encode(entry.line)); - TRY(encoder.encode(entry.column)); - return {}; -} - -template<> -inline ErrorOr decode(Decoder& decoder) -{ - auto content = TRY(decoder.decode()); - auto filename = TRY(decoder.decode()); - auto line = TRY(decoder.decode()); - auto column = TRY(decoder.decode()); - - return CodeComprehension::TodoEntry { move(content), move(filename), line, column }; -} - -template<> -inline ErrorOr encode(Encoder& encoder, CodeComprehension::TokenInfo const& location) -{ - TRY(encoder.encode(location.type)); - TRY(encoder.encode(location.start_line)); - TRY(encoder.encode(location.start_column)); - TRY(encoder.encode(location.end_line)); - TRY(encoder.encode(location.end_column)); - return {}; -} - -template<> -inline ErrorOr decode(Decoder& decoder) -{ - auto type = TRY(decoder.decode()); - auto start_line = TRY(decoder.decode()); - auto start_column = TRY(decoder.decode()); - auto end_line = TRY(decoder.decode()); - auto end_column = TRY(decoder.decode()); - - return CodeComprehension::TokenInfo { type, start_line, start_column, end_line, end_column }; -} - -} diff --git a/Userland/DevTools/HackStudio/CMakeLists.txt b/Userland/DevTools/HackStudio/CMakeLists.txt deleted file mode 100644 index 01f25187ccc..00000000000 --- a/Userland/DevTools/HackStudio/CMakeLists.txt +++ /dev/null @@ -1,59 +0,0 @@ -serenity_component( - HackStudio - RECOMMENDED - TARGETS HackStudio - DEPENDS CppLanguageServer ShellLanguageServer WebContent -) - -add_subdirectory(LanguageServers) -add_subdirectory(LanguageClients) - -stringify_gml(Dialogs/NewProjectDialog.gml Dialogs/NewProjectDialogGML.h new_project_dialog_gml) -stringify_gml(Dialogs/Git/GitCommitDialog.gml Dialogs/Git/GitCommitDialogGML.h git_commit_dialog_gml) - -set(SOURCES - CodeDocument.cpp - ClassViewWidget.cpp - Debugger/BacktraceModel.cpp - Debugger/DebugInfoWidget.cpp - Debugger/Debugger.cpp - Debugger/DisassemblyModel.cpp - Debugger/DisassemblyWidget.cpp - Debugger/RegistersModel.cpp - Debugger/VariablesModel.cpp - DeclarationsModel.cpp - Dialogs/Git/GitCommitDialog.cpp - Dialogs/NewProjectDialog.cpp - Dialogs/ProjectTemplatesModel.cpp - Editor.cpp - EditorWrapper.cpp - FindInFilesWidget.cpp - Git/DiffViewer.cpp - Git/GitFilesModel.cpp - Git/GitFilesView.cpp - Git/GitRepo.cpp - Git/GitWidget.cpp - GMLPreviewWidget.cpp - HackStudioWidget.cpp - LanguageClient.cpp - Locator.cpp - Project.cpp - ProjectBuilder.cpp - ProjectConfig.cpp - ProjectDeclarations.cpp - ProjectFile.cpp - ProjectTemplate.cpp - TerminalWrapper.cpp - ToDoEntries.cpp - ToDoEntriesWidget.cpp - main.cpp -) - -set(GENERATED_SOURCES - Dialogs/Git/GitCommitDialogGML.h - Dialogs/NewProjectDialogGML.h -) - -serenity_app(HackStudio ICON app-hack-studio) -target_link_libraries(HackStudio PRIVATE LibELF LibWebView LibWeb LibMarkdown LibGUI LibCpp LibCMake LibGfx LibCore LibVT LibDebug LibX86 LibDiff LibShell LibSymbolication LibSyntax LibRegex LibSQL LibConfig LibCore LibCoredump LibDesktop LibFileSystem LibIPC LibJS LibMain LibThreading LibURL) -add_dependencies(HackStudio CppLanguageServer) diff --git a/Userland/DevTools/HackStudio/ClassViewWidget.cpp b/Userland/DevTools/HackStudio/ClassViewWidget.cpp deleted file mode 100644 index f5e4e9f7b0a..00000000000 --- a/Userland/DevTools/HackStudio/ClassViewWidget.cpp +++ /dev/null @@ -1,193 +0,0 @@ -/* - * Copyright (c) 2021, Itamar S. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "ClassViewWidget.h" -#include "HackStudio.h" -#include "ProjectDeclarations.h" -#include -#include -#include - -namespace HackStudio { - -ClassViewWidget::ClassViewWidget() -{ - set_layout(); - m_class_tree = add(); - - m_class_tree->on_selection_change = [this] { - auto const& index = m_class_tree->selection().first(); - if (!index.is_valid()) - return; - - auto* node = static_cast(index.internal_data()); - if (!node->declaration) - return; - - open_file(node->declaration->position.file, node->declaration->position.line, node->declaration->position.column); - }; -} - -RefPtr ClassViewModel::create() -{ - return adopt_ref(*new ClassViewModel()); -} - -int ClassViewModel::row_count(const GUI::ModelIndex& index) const -{ - if (!index.is_valid()) - return m_root_scope.size(); - auto* node = static_cast(index.internal_data()); - return node->children.size(); -} - -GUI::Variant ClassViewModel::data(const GUI::ModelIndex& index, GUI::ModelRole role) const -{ - auto* node = static_cast(index.internal_data()); - switch (role) { - case GUI::ModelRole::Display: { - return node->name; - } - case GUI::ModelRole::Icon: { - if (!node->declaration) - return {}; - auto icon = ProjectDeclarations::get_icon_for(node->declaration->type); - if (icon.has_value()) - return icon.value(); - return {}; - } - default: - return {}; - } -} - -GUI::ModelIndex ClassViewModel::parent_index(const GUI::ModelIndex& index) const -{ - if (!index.is_valid()) - return {}; - auto* child = static_cast(index.internal_data()); - auto* parent = child->parent; - if (parent == nullptr) - return {}; - - if (parent->parent == nullptr) { - for (size_t row = 0; row < m_root_scope.size(); row++) { - if (m_root_scope[row].ptr() == parent) - return create_index(row, 0, parent); - } - VERIFY_NOT_REACHED(); - } - for (size_t row = 0; row < parent->parent->children.size(); row++) { - ClassViewNode* child_at_row = parent->parent->children[row].ptr(); - if (child_at_row == parent) - return create_index(row, 0, parent); - } - VERIFY_NOT_REACHED(); -} - -GUI::ModelIndex ClassViewModel::index(int row, int column, const GUI::ModelIndex& parent_index) const -{ - if (!parent_index.is_valid()) - return create_index(row, column, m_root_scope[row].ptr()); - auto* parent = static_cast(parent_index.internal_data()); - auto* child = parent->children[row].ptr(); - return create_index(row, column, child); -} - -ClassViewModel::ClassViewModel() -{ - m_root_scope.clear(); - ProjectDeclarations::the().for_each_declared_symbol([this](auto& decl) { - if (decl.type == CodeComprehension::DeclarationType::Class - || decl.type == CodeComprehension::DeclarationType::Struct - || decl.type == CodeComprehension::DeclarationType::Member - || decl.type == CodeComprehension::DeclarationType::Namespace) { - add_declaration(decl); - } - }); -} - -static ClassViewNode& add_child_node(Vector>& children, NonnullOwnPtr&& node_ptr, ClassViewNode* parent, CodeComprehension::Declaration const* declaration) -{ - node_ptr->parent = parent; - node_ptr->declaration = declaration; - - size_t inserted_index = 0; - ClassViewNode& node = *node_ptr; - // Insert into parent's children list, sorted lexicographically by name. - children.insert_before_matching( - move(node_ptr), [&node](auto& other_node) { - return strncmp(node.name.characters_without_null_termination(), other_node->name.characters_without_null_termination(), min(node.name.length(), other_node->name.length())) < 0; - }, - 0, &inserted_index); - - return *children.at(inserted_index); -} - -void ClassViewModel::add_declaration(CodeComprehension::Declaration const& decl) -{ - ClassViewNode* parent = nullptr; - auto scope_parts = decl.scope.view().split_view("::"sv); - - if (!scope_parts.is_empty()) { - // Traverse declarations tree to the parent of 'decl' - for (auto& node : m_root_scope) { - if (node->name == scope_parts.first()) - parent = node; - } - - if (parent == nullptr) { - m_root_scope.append(make(scope_parts.first())); - parent = m_root_scope.last(); - } - - for (size_t i = 1; i < scope_parts.size(); ++i) { - auto& scope = scope_parts[i]; - ClassViewNode* next { nullptr }; - for (auto& child : parent->children) { - if (child->name == scope) { - next = child; - break; - } - } - - if (next) { - parent = next; - continue; - } - - parent = &add_child_node(parent->children, make(scope), parent, nullptr); - } - } - - Vector>* children_of_parent = nullptr; - if (parent) { - children_of_parent = &parent->children; - } else { - children_of_parent = &m_root_scope; - } - - bool already_exists = false; - for (auto& child : *children_of_parent) { - if (child->name == decl.name) { - already_exists = true; - if (!child->declaration) { - child->declaration = &decl; - } - break; - } - } - if (!already_exists) { - add_child_node(*children_of_parent, make(decl.name), parent, &decl); - } -} - -void ClassViewWidget::refresh() -{ - m_class_tree->set_model(ClassViewModel::create()); -} - -} diff --git a/Userland/DevTools/HackStudio/ClassViewWidget.h b/Userland/DevTools/HackStudio/ClassViewWidget.h deleted file mode 100644 index bd717a67b2e..00000000000 --- a/Userland/DevTools/HackStudio/ClassViewWidget.h +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright (c) 2021, Itamar S. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include - -namespace HackStudio { - -class ClassViewWidget final : public GUI::Widget { - C_OBJECT(ClassViewWidget) -public: - virtual ~ClassViewWidget() override { } - - void refresh(); - -private: - ClassViewWidget(); - - RefPtr m_class_tree; -}; - -// Note: A ClassViewNode stores a raw pointer to the Declaration from ProjectDeclarations and a StringView into its name. -// We should take care to update the ClassViewModel whenever the declarations change, because otherwise we may be holding pointers to freed memory. -// This is currently achieved with the on_update callback of ProjectDeclarations. -struct ClassViewNode { - StringView name; - CodeComprehension::Declaration const* declaration { nullptr }; - Vector> children; - ClassViewNode* parent { nullptr }; - - explicit ClassViewNode(StringView name) - : name(name) {}; -}; - -class ClassViewModel final : public GUI::Model { -public: - static RefPtr create(); - virtual int row_count(const GUI::ModelIndex& = GUI::ModelIndex()) const override; - virtual int column_count(const GUI::ModelIndex& = GUI::ModelIndex()) const override { return 1; } - virtual GUI::Variant data(const GUI::ModelIndex&, GUI::ModelRole role) const override; - virtual GUI::ModelIndex parent_index(const GUI::ModelIndex&) const override; - virtual GUI::ModelIndex index(int row, int column = 0, const GUI::ModelIndex& parent_index = GUI::ModelIndex()) const override; - -private: - explicit ClassViewModel(); - void add_declaration(CodeComprehension::Declaration const&); - Vector> m_root_scope; -}; - -} diff --git a/Userland/DevTools/HackStudio/CodeDocument.cpp b/Userland/DevTools/HackStudio/CodeDocument.cpp deleted file mode 100644 index 1cbec9969fe..00000000000 --- a/Userland/DevTools/HackStudio/CodeDocument.cpp +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright (c) 2020, Itamar S. - * Copyright (c) 2022, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "CodeDocument.h" - -namespace HackStudio { - -NonnullRefPtr CodeDocument::create(ByteString const& file_path, Client* client) -{ - return adopt_ref(*new CodeDocument(file_path, client)); -} - -NonnullRefPtr CodeDocument::create(Client* client) -{ - return adopt_ref(*new CodeDocument(client)); -} - -CodeDocument::CodeDocument(ByteString const& file_path, Client* client) - : TextDocument(client) - , m_file_path(file_path) -{ - auto lexical_path = LexicalPath(file_path); - m_language = Syntax::language_from_filename(lexical_path); -} - -CodeDocument::CodeDocument(Client* client) - : TextDocument(client) -{ -} - -CodeDocument::DiffType CodeDocument::line_difference(size_t line) const -{ - return m_line_differences[line]; -} - -void CodeDocument::set_line_differences(Badge, Vector line_differences) -{ - m_line_differences = move(line_differences); -} - -} diff --git a/Userland/DevTools/HackStudio/CodeDocument.h b/Userland/DevTools/HackStudio/CodeDocument.h deleted file mode 100644 index e9004811e11..00000000000 --- a/Userland/DevTools/HackStudio/CodeDocument.h +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright (c) 2020, Itamar S. - * Copyright (c) 2022, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include - -namespace HackStudio { - -class Editor; - -class CodeDocument final : public GUI::TextDocument { -public: - virtual ~CodeDocument() override = default; - static NonnullRefPtr create(ByteString const& file_path, Client* client = nullptr); - static NonnullRefPtr create(Client* client = nullptr); - - Vector const& breakpoint_lines() const { return m_breakpoint_lines; } - Vector& breakpoint_lines() { return m_breakpoint_lines; } - Optional execution_position() const { return m_execution_position; } - void set_execution_position(size_t line) { m_execution_position = line; } - void clear_execution_position() { m_execution_position.clear(); } - ByteString const& file_path() const { return m_file_path; } - Optional const& language() const { return m_language; } - - enum class DiffType { - None, - AddedLine, - ModifiedLine, - DeletedLinesBefore, - }; - DiffType line_difference(size_t line) const; - void set_line_differences(Badge, Vector); - -private: - explicit CodeDocument(ByteString const& file_path, Client* client = nullptr); - explicit CodeDocument(Client* client = nullptr); - - ByteString m_file_path; - Optional m_language; - Vector m_breakpoint_lines; - Optional m_execution_position; - - Vector m_line_differences; -}; - -} diff --git a/Userland/DevTools/HackStudio/Debugger/BacktraceModel.cpp b/Userland/DevTools/HackStudio/Debugger/BacktraceModel.cpp deleted file mode 100644 index 9222b36dcb6..00000000000 --- a/Userland/DevTools/HackStudio/Debugger/BacktraceModel.cpp +++ /dev/null @@ -1,80 +0,0 @@ -/* - * Copyright (c) 2020, Itamar S. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "BacktraceModel.h" -#include "Debugger.h" -#include - -namespace HackStudio { - -NonnullRefPtr BacktraceModel::create(Debug::ProcessInspector const& inspector, PtraceRegisters const& regs) -{ - return adopt_ref(*new BacktraceModel(create_backtrace(inspector, regs))); -} - -GUI::Variant BacktraceModel::data(const GUI::ModelIndex& index, GUI::ModelRole role) const -{ - if (role == GUI::ModelRole::Display) { - auto& frame = m_frames.at(index.row()); - return frame.function_name; - } - return {}; -} - -GUI::ModelIndex BacktraceModel::index(int row, int column, const GUI::ModelIndex&) const -{ - if (row < 0 || row >= static_cast(m_frames.size())) - return {}; - return create_index(row, column, &m_frames.at(row)); -} - -Vector BacktraceModel::create_backtrace(Debug::ProcessInspector const& inspector, PtraceRegisters const& regs) -{ - Vector frames; - - auto add_frame = [&frames, &inspector](FlatPtr address, FlatPtr frame_pointer) { - auto const* lib = inspector.library_at(address); - - if (lib) { - ByteString name = lib->debug_info->elf().symbolicate(address - lib->base_address); - if (name.is_empty()) { - dbgln("BacktraceModel: couldn't find containing function for address: {:p} (library={})", address, lib->name); - name = ""; - } - - auto source_position = lib->debug_info->get_source_position(address - lib->base_address); - - frames.append({ name, address, frame_pointer, source_position }); - } else { - dbgln("BacktraceModel: couldn't find containing library for address: {:p}", address); - frames.append({ "", address, frame_pointer, {} }); - } - }; - - add_frame(regs.ip(), regs.bp()); - - MUST(AK::unwind_stack_from_frame_pointer( - regs.bp(), - [&](FlatPtr address) -> ErrorOr { - auto maybe_value = inspector.peek(address); - if (!maybe_value.has_value()) - return EFAULT; - - return maybe_value.value(); - }, - [&add_frame](AK::StackFrame stack_frame) -> ErrorOr { - // Subtract one from return_address to go back to the calling instruction to get accurate source position information. - auto address = stack_frame.return_address - 1; - - add_frame(address, stack_frame.previous_frame_pointer); - - return IterationDecision::Continue; - })); - - return frames; -} - -} diff --git a/Userland/DevTools/HackStudio/Debugger/BacktraceModel.h b/Userland/DevTools/HackStudio/Debugger/BacktraceModel.h deleted file mode 100644 index e3770b47a78..00000000000 --- a/Userland/DevTools/HackStudio/Debugger/BacktraceModel.h +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright (c) 2020, Itamar S. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include - -namespace Debug { - -class DebugSession; - -} - -namespace HackStudio { - -class BacktraceModel final : public GUI::Model { -public: - static NonnullRefPtr create(Debug::ProcessInspector const&, PtraceRegisters const& regs); - - virtual int row_count(const GUI::ModelIndex& = GUI::ModelIndex()) const override { return m_frames.size(); } - virtual int column_count(const GUI::ModelIndex& = GUI::ModelIndex()) const override { return 1; } - - virtual ErrorOr column_name(int) const override { return String {}; } - - virtual GUI::Variant data(const GUI::ModelIndex&, GUI::ModelRole) const override; - - virtual GUI::ModelIndex index(int row, int column, const GUI::ModelIndex&) const override; - - struct FrameInfo { - ByteString function_name; - FlatPtr instruction_address { 0 }; - FlatPtr frame_base { 0 }; - Optional m_source_position; - }; - - Vector const& frames() const { return m_frames; } - -private: - explicit BacktraceModel(Vector&& frames) - : m_frames(move(frames)) - { - } - - static Vector create_backtrace(Debug::ProcessInspector const&, PtraceRegisters const&); - - Vector m_frames; -}; - -} diff --git a/Userland/DevTools/HackStudio/Debugger/BreakpointCallback.h b/Userland/DevTools/HackStudio/Debugger/BreakpointCallback.h deleted file mode 100644 index b57fa473e14..00000000000 --- a/Userland/DevTools/HackStudio/Debugger/BreakpointCallback.h +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright (c) 2020, Itamar S. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include - -namespace HackStudio { - -enum class BreakpointChange { - Added, - Removed, -}; - -using BreakpointChangeCallback = Function; -} diff --git a/Userland/DevTools/HackStudio/Debugger/DebugInfoWidget.cpp b/Userland/DevTools/HackStudio/Debugger/DebugInfoWidget.cpp deleted file mode 100644 index 5b2c161a867..00000000000 --- a/Userland/DevTools/HackStudio/Debugger/DebugInfoWidget.cpp +++ /dev/null @@ -1,219 +0,0 @@ -/* - * Copyright (c) 2020, Itamar S. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "DebugInfoWidget.h" -#include "BacktraceModel.h" -#include "Debugger.h" -#include "RegistersModel.h" -#include "VariablesModel.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace HackStudio { - -ErrorOr DebugInfoWidget::init_toolbar() -{ - m_continue_action = GUI::Action::create("Continue", TRY(Gfx::Bitmap::load_from_file("/res/icons/16x16/debug-continue.png"sv)), [](auto&) { - Debugger::the().set_requested_debugger_action(Debugger::DebuggerAction::Continue); - }); - - m_singlestep_action = GUI::Action::create("Step Over", { Mod_None, Key_F10 }, TRY(Gfx::Bitmap::load_from_file("/res/icons/16x16/debug-step-over.png"sv)), [](auto&) { - Debugger::the().set_requested_debugger_action(Debugger::DebuggerAction::SourceStepOver); - }); - - m_step_in_action = GUI::Action::create("Step In", { Mod_None, Key_F11 }, TRY(Gfx::Bitmap::load_from_file("/res/icons/16x16/debug-step-in.png"sv)), [](auto&) { - Debugger::the().set_requested_debugger_action(Debugger::DebuggerAction::SourceSingleStep); - }); - - m_step_out_action = GUI::Action::create("Step Out", { Mod_Shift, Key_F11 }, TRY(Gfx::Bitmap::load_from_file("/res/icons/16x16/debug-step-out.png"sv)), [](auto&) { - Debugger::the().set_requested_debugger_action(Debugger::DebuggerAction::SourceStepOut); - }); - - m_pause_action = GUI::Action::create("Pause", {}, TRY(Gfx::Bitmap::load_from_file("/res/icons/16x16/debug-pause.png"sv)), [](auto&) { - Debugger::the().stop_debuggee(); - }); - - m_toolbar->add_action(*m_continue_action); - m_toolbar->add_action(*m_singlestep_action); - m_toolbar->add_action(*m_step_in_action); - m_toolbar->add_action(*m_step_out_action); - m_toolbar->add_action(*m_pause_action); - - set_debug_actions_enabled(false, {}); - - return {}; -} - -ErrorOr> DebugInfoWidget::create() -{ - auto debuginfo_widget = TRY(adopt_nonnull_ref_or_enomem(new (nothrow) DebugInfoWidget)); - - auto& toolbar_container = debuginfo_widget->add(); - debuginfo_widget->m_toolbar = toolbar_container.add(); - - TRY(debuginfo_widget->init_toolbar()); - - return debuginfo_widget; -} - -DebugInfoWidget::DebugInfoWidget() -{ - set_layout(); - auto& bottom_box = add(); - bottom_box.set_layout(); - - auto& splitter = bottom_box.add(); - m_backtrace_view = splitter.add(); - auto& variables_tab_widget = splitter.add(); - variables_tab_widget.set_tab_position(TabPosition::Bottom); - variables_tab_widget.add_widget(build_variables_tab()); - variables_tab_widget.add_widget(build_registers_tab()); - - m_backtrace_view->on_selection_change = [this] { - auto const& index = m_backtrace_view->selection().first(); - - if (!index.is_valid()) { - return; - } - - auto& model = static_cast(*m_backtrace_view->model()); - // Note: The reconstruction of the register set here is obviously incomplete. - // We currently only reconstruct eip & ebp. Ideally would also reconstruct the other registers somehow. - // (Other registers may be needed to get the values of variables who are not stored on the stack) - PtraceRegisters frame_regs {}; - auto backtrace_frame = model.frames()[index.row()]; - frame_regs.set_ip(backtrace_frame.instruction_address); - frame_regs.set_bp(backtrace_frame.frame_base); - - m_variables_view->set_model(VariablesModel::create(static_cast(*m_variables_view->model()).inspector(), frame_regs)); - if (on_backtrace_frame_selection && backtrace_frame.m_source_position.has_value()) { - on_backtrace_frame_selection(*backtrace_frame.m_source_position); - } else { - dbgln("no source position info"); - } - }; -} - -bool DebugInfoWidget::does_variable_support_writing(Debug::DebugInfo::VariableInfo const* variable) -{ - if (variable->location_type != Debug::DebugInfo::VariableInfo::LocationType::Address) - return false; - return variable->is_enum_type() || variable->type_name.is_one_of("int", "bool"); -} - -RefPtr DebugInfoWidget::get_context_menu_for_variable(const GUI::ModelIndex& index) -{ - if (!index.is_valid()) - return nullptr; - auto context_menu = GUI::Menu::construct(); - - auto* variable = static_cast(index.internal_data()); - if (does_variable_support_writing(variable)) { - context_menu->add_action(GUI::Action::create("Change value", [&](auto&) { - String value; - if (GUI::InputBox::show(window(), value, "Enter new value:"sv, "Set variable value"sv) == GUI::InputBox::ExecResult::OK) { - auto& model = static_cast(*m_variables_view->model()); - model.set_variable_value(index, value, window()); - } - })); - } - - auto variable_address = variable->location_data.address; - if (Debugger::the().session()->watchpoint_exists(variable_address)) { - context_menu->add_action(GUI::Action::create("Remove watchpoint", [variable_address](auto&) { - Debugger::the().session()->remove_watchpoint(variable_address); - })); - } else { - auto& backtrace_model = static_cast(*m_backtrace_view->model()); - auto current_frame = m_backtrace_view->selection().first().row(); - auto ebp = backtrace_model.frames()[current_frame].frame_base; - context_menu->add_action(GUI::Action::create("Add watchpoint", [variable_address, ebp](auto&) { - Debugger::the().session()->insert_watchpoint(variable_address, ebp); - })); - } - return context_menu; -} - -NonnullRefPtr DebugInfoWidget::build_variables_tab() -{ - auto variables_widget = GUI::Widget::construct(); - variables_widget->set_title("Variables"_string); - variables_widget->set_layout(); - - m_variables_view = variables_widget->add(); - - m_variables_view->on_context_menu_request = [this](auto& index, auto& event) { - m_variable_context_menu = get_context_menu_for_variable(index); - if (m_variable_context_menu) - m_variable_context_menu->popup(event.screen_position()); - }; - - return variables_widget; -} - -NonnullRefPtr DebugInfoWidget::build_registers_tab() -{ - auto registers_widget = GUI::Widget::construct(); - registers_widget->set_title("Registers"_string); - registers_widget->set_layout(); - - m_registers_view = registers_widget->add(); - - return registers_widget; -} - -void DebugInfoWidget::update_state(Debug::ProcessInspector& inspector, PtraceRegisters const& regs) -{ - m_variables_view->set_model(VariablesModel::create(inspector, regs)); - m_backtrace_view->set_model(BacktraceModel::create(inspector, regs)); - if (m_registers_view->model()) { - auto& previous_registers = static_cast(m_registers_view->model())->raw_registers(); - m_registers_view->set_model(RegistersModel::create(regs, previous_registers)); - } else { - m_registers_view->set_model(RegistersModel::create(regs)); - } - auto selected_index = m_backtrace_view->model()->index(0); - if (!selected_index.is_valid()) { - dbgln("Warning: DebugInfoWidget: backtrace selected index is invalid"); - return; - } - m_backtrace_view->selection().set(selected_index); -} - -void DebugInfoWidget::program_stopped() -{ - m_variables_view->set_model({}); - m_backtrace_view->set_model({}); - m_registers_view->set_model({}); -} - -void DebugInfoWidget::set_debug_actions_enabled(bool enabled, Optional state) -{ - if (!enabled) { - m_continue_action->set_enabled(false); - m_singlestep_action->set_enabled(false); - m_step_in_action->set_enabled(false); - m_step_out_action->set_enabled(false); - m_pause_action->set_enabled(false); - return; - } - - m_continue_action->set_enabled(state == DebugActionsState::DebuggeeStopped); - m_singlestep_action->set_enabled(state == DebugActionsState::DebuggeeStopped); - m_step_in_action->set_enabled(state == DebugActionsState::DebuggeeStopped); - m_step_out_action->set_enabled(state == DebugActionsState::DebuggeeStopped); - m_pause_action->set_enabled(state == DebugActionsState::DebuggeeRunning); -} - -} diff --git a/Userland/DevTools/HackStudio/Debugger/DebugInfoWidget.h b/Userland/DevTools/HackStudio/Debugger/DebugInfoWidget.h deleted file mode 100644 index e08b23dfaed..00000000000 --- a/Userland/DevTools/HackStudio/Debugger/DebugInfoWidget.h +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright (c) 2020, Itamar S. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include "Debugger.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace HackStudio { - -class DebugInfoWidget final : public GUI::Widget { - C_OBJECT(DebugInfoWidget) -public: - static ErrorOr> create(); - virtual ~DebugInfoWidget() override { } - - void update_state(Debug::ProcessInspector&, PtraceRegisters const&); - void program_stopped(); - - enum class DebugActionsState { - DebuggeeRunning, - DebuggeeStopped, - }; - void set_debug_actions_enabled(bool enabled, Optional); - - Function on_backtrace_frame_selection; - -private: - explicit DebugInfoWidget(); - ErrorOr init_toolbar(); - - NonnullRefPtr build_variables_tab(); - NonnullRefPtr build_registers_tab(); - bool does_variable_support_writing(Debug::DebugInfo::VariableInfo const*); - RefPtr get_context_menu_for_variable(const GUI::ModelIndex&); - - RefPtr m_variables_view; - RefPtr m_registers_view; - RefPtr m_backtrace_view; - RefPtr m_variable_context_menu; - RefPtr m_toolbar; - RefPtr m_continue_action; - RefPtr m_singlestep_action; - RefPtr m_step_in_action; - RefPtr m_step_out_action; - RefPtr m_pause_action; -}; - -} diff --git a/Userland/DevTools/HackStudio/Debugger/Debugger.cpp b/Userland/DevTools/HackStudio/Debugger/Debugger.cpp deleted file mode 100644 index 2e4ec3d9975..00000000000 --- a/Userland/DevTools/HackStudio/Debugger/Debugger.cpp +++ /dev/null @@ -1,356 +0,0 @@ -/* - * Copyright (c) 2020, Itamar S. - * Copyright (c) 2024, Sam Atkins - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "Debugger.h" -#include - -namespace HackStudio { - -static Debugger* s_the; - -Debugger& Debugger::the() -{ - VERIFY(s_the); - return *s_the; -} - -void Debugger::initialize( - ByteString source_root, - Function on_stop_callback, - Function on_continue_callback, - Function on_exit_callback, - Function on_initialization_progress) -{ - s_the = new Debugger(source_root, move(on_stop_callback), move(on_continue_callback), move(on_exit_callback), move(on_initialization_progress)); -} - -bool Debugger::is_initialized() -{ - return s_the; -} - -Debugger::Debugger( - ByteString source_root, - Function on_stop_callback, - Function on_continue_callback, - Function on_exit_callback, - Function on_initialization_progress) - : m_source_root(source_root) - , m_on_stopped_callback(move(on_stop_callback)) - , m_on_continue_callback(move(on_continue_callback)) - , m_on_exit_callback(move(on_exit_callback)) - , m_on_initialization_progress(move(on_initialization_progress)) -{ - pthread_mutex_init(&m_ui_action_mutex, nullptr); - pthread_cond_init(&m_ui_action_cond, nullptr); -} - -bool Debugger::change_breakpoint(ByteString const& file, size_t line, BreakpointChange change_type) -{ - auto position = create_source_position(file, line); - auto session = Debugger::the().session(); - if (session) { - auto address = session->get_address_from_source_position(position.file_path, position.line_number); - if (!address.has_value()) { - dbgln("Warning: couldn't get instruction address from source"); - return false; - } - - switch (change_type) { - case BreakpointChange::Added: - if (session->insert_breakpoint(address.value().address)) { - m_breakpoints.append(position); - return true; - } - break; - case BreakpointChange::Removed: - if (session->remove_breakpoint(address.value().address)) { - m_breakpoints.remove_all_matching([&](Debug::DebugInfo::SourcePosition const& val) { return val == position; }); - return true; - } - break; - } - return false; - } - - // No active session, so just modify our internal list of breakpoints - switch (change_type) { - case BreakpointChange::Added: - m_breakpoints.append(position); - return true; - case BreakpointChange::Removed: - m_breakpoints.remove_all_matching([&](Debug::DebugInfo::SourcePosition const& val) { return val == position; }); - return true; - } - VERIFY_NOT_REACHED(); -} - -bool Debugger::set_execution_position(ByteString const& file, size_t line) -{ - auto position = create_source_position(file, line); - auto session = Debugger::the().session(); - if (!session) - return false; - auto address = session->get_address_from_source_position(position.file_path, position.line_number); - if (!address.has_value()) - return false; - auto registers = session->get_registers(); - registers.set_ip(address.value().address); - session->set_registers(registers); - return true; -} - -Debug::DebugInfo::SourcePosition Debugger::create_source_position(ByteString const& file, size_t line) -{ - if (file.starts_with('/')) - return { file, line + 1 }; - return { LexicalPath::canonicalized_path(ByteString::formatted("{}/{}", m_source_root, file)), line + 1 }; -} - -intptr_t Debugger::start_static() -{ - Debugger::the().start(); - return 0; -} - -void Debugger::stop() -{ - set_requested_debugger_action(DebuggerAction::Exit); -} - -void Debugger::start() -{ - auto [debug_session, initial_state] = create_debug_session(); - m_debug_session = move(debug_session); - - for (auto const& breakpoint : m_breakpoints) { - dbgln("inserting breakpoint at: {}:{}", breakpoint.file_path, breakpoint.line_number); - auto address = m_debug_session->get_address_from_source_position(breakpoint.file_path, breakpoint.line_number); - if (address.has_value()) { - bool success = m_debug_session->insert_breakpoint(address.value().address); - VERIFY(success); - } else { - // FIXME: Report the invalid breakpoint to the GUI somehow. - dbgln("couldn't insert breakpoint"); - } - } - - debugger_loop(initial_state); -} - -Debugger::CreateDebugSessionResult Debugger::create_debug_session() -{ - if (!m_executable_path.is_empty()) { - auto child_setup_callback = [this]() { - if (m_child_setup_callback) - return m_child_setup_callback(); - return ErrorOr {}; - }; - - auto on_initialization_progress = [this](float progress) { - if (m_on_initialization_progress) - m_on_initialization_progress(progress); - }; - - auto debug_session = Debug::DebugSession::exec_and_attach(m_executable_path, m_source_root, move(child_setup_callback), move(on_initialization_progress)); - VERIFY(!!debug_session); - return { debug_session.release_nonnull(), Debug::DebugSession::Running }; - } - - if (m_pid_to_attach.has_value()) { - auto on_initialization_progress = [this](float progress) { - if (m_on_initialization_progress) - m_on_initialization_progress(progress); - }; - - auto debug_session = Debug::DebugSession::attach(m_pid_to_attach.value(), m_source_root, move(on_initialization_progress)); - VERIFY(!!debug_session); - return { debug_session.release_nonnull(), Debug::DebugSession::Stopped }; - } - - VERIFY_NOT_REACHED(); -} - -int Debugger::debugger_loop(Debug::DebugSession::DesiredInitialDebugeeState initial_state) -{ - VERIFY(m_debug_session); - - m_debug_session->run(initial_state, [this](Debug::DebugSession::DebugBreakReason reason, Optional optional_regs) { - if (reason == Debug::DebugSession::DebugBreakReason::Exited) { - dbgln("Program exited"); - m_on_exit_callback(); - return Debug::DebugSession::DebugDecision::Detach; - } - remove_temporary_breakpoints(); - VERIFY(optional_regs.has_value()); - PtraceRegisters const& regs = optional_regs.value(); - - auto source_position = m_debug_session->get_source_position(regs.ip()); - if (!source_position.has_value()) - return Debug::DebugSession::DebugDecision::SingleStep; - - // We currently do no support stepping through assembly source - if (source_position.value().file_path.ends_with(".S"sv)) - return Debug::DebugSession::DebugDecision::SingleStep; - - VERIFY(source_position.has_value()); - if (m_state.get() == Debugger::DebuggingState::SingleStepping) { - if (m_state.should_stop_single_stepping(source_position.value())) { - m_state.set_normal(); - } else { - return Debug::DebugSession::DebugDecision::SingleStep; - } - } - - auto control_passed_to_user = m_on_stopped_callback(regs); - - if (control_passed_to_user == HasControlPassedToUser::Yes) { - pthread_mutex_lock(&m_ui_action_mutex); - pthread_cond_wait(&m_ui_action_cond, &m_ui_action_mutex); - pthread_mutex_unlock(&m_ui_action_mutex); - - if (m_requested_debugger_action != DebuggerAction::Exit) - m_on_continue_callback(); - - } else { - m_requested_debugger_action = DebuggerAction::Continue; - } - - switch (m_requested_debugger_action) { - case DebuggerAction::Continue: - m_state.set_normal(); - return Debug::DebugSession::DebugDecision::Continue; - case DebuggerAction::SourceSingleStep: - m_state.set_single_stepping(source_position.value()); - return Debug::DebugSession::DebugDecision::SingleStep; - case DebuggerAction::SourceStepOut: - m_state.set_stepping_out(); - do_step_out(regs); - return Debug::DebugSession::DebugDecision::Continue; - case DebuggerAction::SourceStepOver: - m_state.set_stepping_over(); - do_step_over(regs); - return Debug::DebugSession::DebugDecision::Continue; - case DebuggerAction::Exit: - dbgln("Debugger exiting"); - m_on_exit_callback(); - return Debug::DebugSession::DebugDecision::Kill; - } - VERIFY_NOT_REACHED(); - }); - m_debug_session.clear(); - return 0; -} - -void Debugger::DebuggingState::set_normal() -{ - m_state = State::Normal; - m_original_source_position.clear(); -} - -void Debugger::DebuggingState::set_single_stepping(Debug::DebugInfo::SourcePosition original_source_position) -{ - m_state = State::SingleStepping; - m_original_source_position = original_source_position; -} - -bool Debugger::DebuggingState::should_stop_single_stepping(Debug::DebugInfo::SourcePosition const& current_source_position) const -{ - VERIFY(m_state == State::SingleStepping); - return m_original_source_position.value() != current_source_position; -} - -void Debugger::remove_temporary_breakpoints() -{ - for (auto breakpoint_address : m_state.temporary_breakpoints()) { - VERIFY(m_debug_session->breakpoint_exists(breakpoint_address)); - bool rc = m_debug_session->remove_breakpoint(breakpoint_address); - VERIFY(rc); - } - m_state.clear_temporary_breakpoints(); -} - -void Debugger::DebuggingState::clear_temporary_breakpoints() -{ - m_addresses_of_temporary_breakpoints.clear(); -} -void Debugger::DebuggingState::add_temporary_breakpoint(FlatPtr address) -{ - m_addresses_of_temporary_breakpoints.append(address); -} - -void Debugger::do_step_out(PtraceRegisters const& regs) -{ - // To step out, we simply insert a temporary breakpoint at the - // instruction the current function returns to, and continue - // execution until we hit that instruction (or some other breakpoint). - insert_temporary_breakpoint_at_return_address(regs); -} - -void Debugger::do_step_over(PtraceRegisters const& regs) -{ - // To step over, we insert a temporary breakpoint at each line in the current function, - // as well as at the current function's return point, and continue execution. - auto lib = m_debug_session->library_at(regs.ip()); - if (!lib) - return; - auto current_function = lib->debug_info->get_containing_function(regs.ip() - lib->base_address); - if (!current_function.has_value()) { - dbgln("cannot perform step_over, failed to find containing function of: {:p}", regs.ip()); - return; - } - VERIFY(current_function.has_value()); - auto lines_in_current_function = lib->debug_info->source_lines_in_scope(current_function.value()); - for (auto const& line : lines_in_current_function) { - insert_temporary_breakpoint(line.address_of_first_statement.value() + lib->base_address); - } - insert_temporary_breakpoint_at_return_address(regs); -} - -void Debugger::insert_temporary_breakpoint_at_return_address(PtraceRegisters const& regs) -{ - Optional return_address; - MUST(AK::unwind_stack_from_frame_pointer( - regs.bp(), - [this](FlatPtr address) -> ErrorOr { - auto maybe_value = m_debug_session->peek(address); - if (!maybe_value.has_value()) - return EFAULT; - - return maybe_value.value(); - }, - [&return_address](AK::StackFrame stack_frame) -> ErrorOr { - return_address = stack_frame.return_address; - return IterationDecision::Break; - })); - - VERIFY(return_address.has_value()); - insert_temporary_breakpoint(return_address.value()); -} - -void Debugger::insert_temporary_breakpoint(FlatPtr address) -{ - if (m_debug_session->breakpoint_exists(address)) - return; - bool success = m_debug_session->insert_breakpoint(address); - VERIFY(success); - m_state.add_temporary_breakpoint(address); -} - -void Debugger::set_requested_debugger_action(DebuggerAction action) -{ - pthread_mutex_lock(continue_mutex()); - m_requested_debugger_action = action; - pthread_cond_signal(continue_cond()); - pthread_mutex_unlock(continue_mutex()); -} -void Debugger::stop_debuggee() -{ - return m_debug_session->stop_debuggee(); -} - -} diff --git a/Userland/DevTools/HackStudio/Debugger/Debugger.h b/Userland/DevTools/HackStudio/Debugger/Debugger.h deleted file mode 100644 index 10873a8c013..00000000000 --- a/Userland/DevTools/HackStudio/Debugger/Debugger.h +++ /dev/null @@ -1,140 +0,0 @@ -/* - * Copyright (c) 2020, Itamar S. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include "BreakpointCallback.h" -#include -#include -#include -#include -#include -#include - -namespace HackStudio { - -class Debugger { -public: - static Debugger& the(); - - enum class HasControlPassedToUser { - No, - Yes, - }; - - static void initialize( - ByteString source_root, - Function on_stop_callback, - Function on_continue_callback, - Function on_exit_callback, - Function on_initialization_progress); - - static bool is_initialized(); - - [[nodiscard]] bool change_breakpoint(ByteString const& file, size_t line, BreakpointChange change_type); - bool set_execution_position(ByteString const& file, size_t line); - - void set_executable_path(ByteString const& path) { m_executable_path = path; } - void set_source_root(ByteString const& source_root) { m_source_root = source_root; } - void set_pid_to_attach(pid_t pid) { m_pid_to_attach = pid; } - - Debug::DebugSession* session() { return m_debug_session.ptr(); } - - void stop(); - - // Thread entry point - static intptr_t start_static(); - - pthread_mutex_t* continue_mutex() { return &m_ui_action_mutex; } - pthread_cond_t* continue_cond() { return &m_ui_action_cond; } - - enum class DebuggerAction { - Continue, - SourceSingleStep, - SourceStepOut, - SourceStepOver, - Exit, - }; - - void set_requested_debugger_action(DebuggerAction); - void reset_breakpoints() { m_breakpoints.clear(); } - - void set_child_setup_callback(Function()> callback) { m_child_setup_callback = move(callback); } - - void stop_debuggee(); - -private: - class DebuggingState { - public: - enum State { - Normal, // Continue normally until we hit a breakpoint / program terminates - SingleStepping, - SteppingOut, - SteppingOver, - }; - State get() const { return m_state; } - - void set_normal(); - void set_single_stepping(Debug::DebugInfo::SourcePosition original_source_position); - void set_stepping_out() { m_state = State::SteppingOut; } - void set_stepping_over() { m_state = State::SteppingOver; } - - bool should_stop_single_stepping(Debug::DebugInfo::SourcePosition const& current_source_position) const; - void clear_temporary_breakpoints(); - void add_temporary_breakpoint(FlatPtr address); - Vector const& temporary_breakpoints() const { return m_addresses_of_temporary_breakpoints; } - - private: - State m_state { Normal }; - Optional m_original_source_position; // The source position at which we started the current single step - Vector m_addresses_of_temporary_breakpoints; - }; - - explicit Debugger( - ByteString source_root, - Function on_stop_callback, - Function on_continue_callback, - Function on_exit_callback, - Function on_initialization_progress); - - Debug::DebugInfo::SourcePosition create_source_position(ByteString const& file, size_t line); - - void start(); - int debugger_loop(Debug::DebugSession::DesiredInitialDebugeeState); - - void remove_temporary_breakpoints(); - void do_step_out(PtraceRegisters const&); - void do_step_over(PtraceRegisters const&); - void insert_temporary_breakpoint(FlatPtr address); - void insert_temporary_breakpoint_at_return_address(PtraceRegisters const&); - - struct CreateDebugSessionResult { - NonnullOwnPtr session; - Debug::DebugSession::DesiredInitialDebugeeState initial_state { Debug::DebugSession::Stopped }; - }; - CreateDebugSessionResult create_debug_session(); - - OwnPtr m_debug_session; - ByteString m_source_root; - DebuggingState m_state; - - pthread_mutex_t m_ui_action_mutex {}; - pthread_cond_t m_ui_action_cond {}; - DebuggerAction m_requested_debugger_action { DebuggerAction::Continue }; - - Vector m_breakpoints; - - ByteString m_executable_path; - Optional m_pid_to_attach; - - Function m_on_stopped_callback; - Function m_on_continue_callback; - Function m_on_exit_callback; - Function()> m_child_setup_callback; - Function m_on_initialization_progress; -}; - -} diff --git a/Userland/DevTools/HackStudio/Debugger/DisassemblyModel.cpp b/Userland/DevTools/HackStudio/Debugger/DisassemblyModel.cpp deleted file mode 100644 index cadb7141dc7..00000000000 --- a/Userland/DevTools/HackStudio/Debugger/DisassemblyModel.cpp +++ /dev/null @@ -1,110 +0,0 @@ -/* - * Copyright (c) 2020, Luke Wilde - * Copyright (c) 2022, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "DisassemblyModel.h" -#include -#include -#include -#include -#include -#include -#include -#include - -namespace HackStudio { - -DisassemblyModel::DisassemblyModel(Debug::DebugSession const& debug_session, PtraceRegisters const& regs) -{ - auto lib = debug_session.library_at(regs.ip()); - if (!lib) - return; - auto containing_function = lib->debug_info->get_containing_function(regs.ip() - lib->base_address); - if (!containing_function.has_value()) { - dbgln("Cannot disassemble as the containing function was not found."); - return; - } - - OwnPtr kernel_elf; - const ELF::Image* elf = nullptr; - - auto maybe_kernel_base = Symbolication::kernel_base(); - - if (maybe_kernel_base.has_value() && containing_function.value().address_low >= maybe_kernel_base.value()) { - auto file_or_error = Core::MappedFile::map("/boot/Kernel.debug"sv); - if (file_or_error.is_error()) - return; - kernel_elf = make(file_or_error.value()->bytes()); - elf = kernel_elf.ptr(); - } else { - elf = &lib->debug_info->elf(); - } - - auto symbol = elf->find_symbol(containing_function.value().address_low); - if (!symbol.has_value()) - return; - VERIFY(symbol.has_value()); - - auto view = symbol.value().raw_data(); - - X86::ELFSymbolProvider symbol_provider(*elf); - X86::SimpleInstructionStream stream((u8 const*)view.characters_without_null_termination(), view.length()); - X86::Disassembler disassembler(stream); - - size_t offset_into_symbol = 0; - for (;;) { - auto insn = disassembler.next(); - if (!insn.has_value()) - break; - FlatPtr address_in_profiled_program = symbol.value().value() + offset_into_symbol; - auto disassembly = insn.value().to_byte_string(address_in_profiled_program, &symbol_provider); - StringView instruction_bytes = view.substring_view(offset_into_symbol, insn.value().length()); - m_instructions.append({ insn.value(), disassembly, instruction_bytes, address_in_profiled_program }); - - offset_into_symbol += insn.value().length(); - } -} - -int DisassemblyModel::row_count(const GUI::ModelIndex&) const -{ - return m_instructions.size(); -} - -ErrorOr DisassemblyModel::column_name(int column) const -{ - switch (column) { - case Column::Address: - return "Address"_string; - case Column::InstructionBytes: - return "Insn Bytes"_string; - case Column::Disassembly: - return "Disassembly"_string; - default: - VERIFY_NOT_REACHED(); - } -} - -GUI::Variant DisassemblyModel::data(const GUI::ModelIndex& index, GUI::ModelRole role) const -{ - auto& insn = m_instructions[index.row()]; - - if (role == GUI::ModelRole::Display) { - if (index.column() == Column::Address) - return ByteString::formatted("{:p}", insn.address); - if (index.column() == Column::InstructionBytes) { - StringBuilder builder; - for (auto ch : insn.bytes) - builder.appendff("{:02x} ", static_cast(ch)); - return builder.to_byte_string(); - } - if (index.column() == Column::Disassembly) - return insn.disassembly; - return {}; - } - return {}; -} - -} diff --git a/Userland/DevTools/HackStudio/Debugger/DisassemblyModel.h b/Userland/DevTools/HackStudio/Debugger/DisassemblyModel.h deleted file mode 100644 index c6239e02162..00000000000 --- a/Userland/DevTools/HackStudio/Debugger/DisassemblyModel.h +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright (c) 2020, Luke Wilde - * Copyright (c) 2022, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include - -namespace Debug { - -class DebugSession; - -} - -namespace HackStudio { - -struct InstructionData { - X86::Instruction insn; - ByteString disassembly; - StringView bytes; - FlatPtr address { 0 }; -}; - -class DisassemblyModel final : public GUI::Model { -public: - static NonnullRefPtr create(Debug::DebugSession const& debug_session, PtraceRegisters const& regs) - { - return adopt_ref(*new DisassemblyModel(debug_session, regs)); - } - - enum Column { - Address, - InstructionBytes, - Disassembly, - __Count - }; - - virtual ~DisassemblyModel() override = default; - - virtual int row_count(const GUI::ModelIndex& = GUI::ModelIndex()) const override; - virtual int column_count(const GUI::ModelIndex& = GUI::ModelIndex()) const override { return Column::__Count; } - virtual ErrorOr column_name(int) const override; - virtual GUI::Variant data(const GUI::ModelIndex&, GUI::ModelRole) const override; - -private: - DisassemblyModel(Debug::DebugSession const&, PtraceRegisters const&); - - Vector m_instructions; -}; - -} diff --git a/Userland/DevTools/HackStudio/Debugger/DisassemblyWidget.cpp b/Userland/DevTools/HackStudio/Debugger/DisassemblyWidget.cpp deleted file mode 100644 index 9382a23d76a..00000000000 --- a/Userland/DevTools/HackStudio/Debugger/DisassemblyWidget.cpp +++ /dev/null @@ -1,85 +0,0 @@ -/* - * Copyright (c) 2020, Luke Wilde - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "DisassemblyWidget.h" -#include "DisassemblyModel.h" -#include -#include -#include - -namespace HackStudio { - -void UnavailableDisassemblyWidget::paint_event(GUI::PaintEvent& event) -{ - Frame::paint_event(event); - if (reason().is_empty()) - return; - GUI::Painter painter(*this); - painter.add_clip_rect(event.rect()); - painter.draw_text(frame_inner_rect(), reason(), Gfx::TextAlignment::Center, palette().window_text(), Gfx::TextElision::Right); -} - -DisassemblyWidget::DisassemblyWidget() -{ - set_layout(); - - m_top_container = add(); - m_top_container->set_layout(); - m_top_container->set_fixed_height(20); - - m_function_name_label = m_top_container->add(); - - m_disassembly_view = add(); - - m_unavailable_disassembly_widget = add(""); - - hide_disassembly("Program isn't running"); -} - -void DisassemblyWidget::update_state(Debug::DebugSession const& debug_session, PtraceRegisters const& regs) -{ - m_disassembly_view->set_model(DisassemblyModel::create(debug_session, regs)); - - if (m_disassembly_view->model()->row_count() > 0) { - auto lib = debug_session.library_at(regs.ip()); - if (!lib) - return; - auto containing_function = lib->debug_info->get_containing_function(regs.ip() - lib->base_address); - if (containing_function.has_value()) - m_function_name_label->set_text(String::from_byte_string(containing_function.value().name).release_value_but_fixme_should_propagate_errors()); - else - m_function_name_label->set_text(""_string); - show_disassembly(); - } else { - hide_disassembly("No disassembly to show for this function"); - } -} - -void DisassemblyWidget::program_stopped() -{ - m_disassembly_view->set_model({}); - m_function_name_label->set_text({}); - hide_disassembly("Program isn't running"); -} - -void DisassemblyWidget::show_disassembly() -{ - m_top_container->set_visible(true); - m_disassembly_view->set_visible(true); - m_function_name_label->set_visible(true); - m_unavailable_disassembly_widget->set_visible(false); -} - -void DisassemblyWidget::hide_disassembly(ByteString const& reason) -{ - m_top_container->set_visible(false); - m_disassembly_view->set_visible(false); - m_function_name_label->set_visible(false); - m_unavailable_disassembly_widget->set_visible(true); - m_unavailable_disassembly_widget->set_reason(reason); -} - -} diff --git a/Userland/DevTools/HackStudio/Debugger/DisassemblyWidget.h b/Userland/DevTools/HackStudio/Debugger/DisassemblyWidget.h deleted file mode 100644 index 61277660164..00000000000 --- a/Userland/DevTools/HackStudio/Debugger/DisassemblyWidget.h +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright (c) 2020, Luke Wilde - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include "Debugger.h" -#include -#include -#include -#include -#include -#include - -namespace HackStudio { - -class UnavailableDisassemblyWidget final : public GUI::Frame { - C_OBJECT(UnavailableDisassemblyWidget) -public: - virtual ~UnavailableDisassemblyWidget() override { } - - ByteString const& reason() const { return m_reason; } - void set_reason(ByteString const& text) { m_reason = text; } - -private: - UnavailableDisassemblyWidget(ByteString const& reason) - : m_reason(reason) - { - } - - virtual void paint_event(GUI::PaintEvent& event) override; - - ByteString m_reason; -}; - -class DisassemblyWidget final : public GUI::Widget { - C_OBJECT(DisassemblyWidget) -public: - virtual ~DisassemblyWidget() override { } - - void update_state(Debug::DebugSession const&, PtraceRegisters const&); - void program_stopped(); - -private: - DisassemblyWidget(); - - void show_disassembly(); - void hide_disassembly(ByteString const&); - - RefPtr m_top_container; - RefPtr m_disassembly_view; - RefPtr m_function_name_label; - RefPtr m_unavailable_disassembly_widget; -}; - -} diff --git a/Userland/DevTools/HackStudio/Debugger/RegistersModel.cpp b/Userland/DevTools/HackStudio/Debugger/RegistersModel.cpp deleted file mode 100644 index 127f0248710..00000000000 --- a/Userland/DevTools/HackStudio/Debugger/RegistersModel.cpp +++ /dev/null @@ -1,127 +0,0 @@ -/* - * Copyright (c) 2020, Luke Wilde - * Copyright (c) 2022, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "RegistersModel.h" - -namespace HackStudio { - -RegistersModel::RegistersModel(PtraceRegisters const& regs) - : m_raw_registers(regs) -{ -#if ARCH(X86_64) - m_registers.append({ "rax", regs.rax }); - m_registers.append({ "rbx", regs.rbx }); - m_registers.append({ "rcx", regs.rcx }); - m_registers.append({ "rdx", regs.rdx }); - m_registers.append({ "rsp", regs.rsp }); - m_registers.append({ "rbp", regs.rbp }); - m_registers.append({ "rsi", regs.rsi }); - m_registers.append({ "rdi", regs.rdi }); - m_registers.append({ "rip", regs.rip }); - m_registers.append({ "r8", regs.r8 }); - m_registers.append({ "r9", regs.r9 }); - m_registers.append({ "r10", regs.r10 }); - m_registers.append({ "r11", regs.r11 }); - m_registers.append({ "r12", regs.r12 }); - m_registers.append({ "r13", regs.r13 }); - m_registers.append({ "r14", regs.r14 }); - m_registers.append({ "r15", regs.r15 }); - m_registers.append({ "rflags", regs.rflags }); - - m_registers.append({ "cs", regs.cs }); - m_registers.append({ "ss", regs.ss }); - m_registers.append({ "ds", regs.ds }); - m_registers.append({ "es", regs.es }); - m_registers.append({ "fs", regs.fs }); - m_registers.append({ "gs", regs.gs }); -#elif ARCH(AARCH64) - TODO_AARCH64(); -#elif ARCH(RISCV64) - TODO_RISCV64(); -#else -# error Unknown architecture -#endif -} - -RegistersModel::RegistersModel(PtraceRegisters const& current_regs, PtraceRegisters const& previous_regs) - : m_raw_registers(current_regs) -{ -#if ARCH(X86_64) - m_registers.append({ "rax", current_regs.rax, current_regs.rax != previous_regs.rax }); - m_registers.append({ "rbx", current_regs.rbx, current_regs.rbx != previous_regs.rbx }); - m_registers.append({ "rcx", current_regs.rcx, current_regs.rcx != previous_regs.rcx }); - m_registers.append({ "rdx", current_regs.rdx, current_regs.rdx != previous_regs.rdx }); - m_registers.append({ "rsp", current_regs.rsp, current_regs.rsp != previous_regs.rsp }); - m_registers.append({ "rbp", current_regs.rbp, current_regs.rbp != previous_regs.rbp }); - m_registers.append({ "rsi", current_regs.rsi, current_regs.rsi != previous_regs.rsi }); - m_registers.append({ "rdi", current_regs.rdi, current_regs.rdi != previous_regs.rdi }); - m_registers.append({ "rip", current_regs.rip, current_regs.rip != previous_regs.rip }); - m_registers.append({ "r8", current_regs.r8, current_regs.r8 != previous_regs.r8 }); - m_registers.append({ "r9", current_regs.r9, current_regs.r9 != previous_regs.r9 }); - m_registers.append({ "r10", current_regs.r10, current_regs.r10 != previous_regs.r10 }); - m_registers.append({ "r11", current_regs.r11, current_regs.r11 != previous_regs.r11 }); - m_registers.append({ "r12", current_regs.r12, current_regs.r12 != previous_regs.r12 }); - m_registers.append({ "r13", current_regs.r13, current_regs.r13 != previous_regs.r13 }); - m_registers.append({ "r14", current_regs.r14, current_regs.r14 != previous_regs.r14 }); - m_registers.append({ "r15", current_regs.r15, current_regs.r15 != previous_regs.r15 }); - m_registers.append({ "rflags", current_regs.rflags, current_regs.rflags != previous_regs.rflags }); - m_registers.append({ "cs", current_regs.cs, current_regs.cs != previous_regs.cs }); - m_registers.append({ "ss", current_regs.ss, current_regs.ss != previous_regs.ss }); - m_registers.append({ "ds", current_regs.ds, current_regs.ds != previous_regs.ds }); - m_registers.append({ "es", current_regs.es, current_regs.es != previous_regs.es }); - m_registers.append({ "fs", current_regs.fs, current_regs.fs != previous_regs.fs }); - m_registers.append({ "gs", current_regs.gs, current_regs.gs != previous_regs.gs }); -#elif ARCH(AARCH64) - (void)previous_regs; - TODO_AARCH64(); -#elif ARCH(RISCV64) - (void)previous_regs; - TODO_RISCV64(); -#else -# error Unknown architecture -#endif -} - -int RegistersModel::row_count(const GUI::ModelIndex&) const -{ - return m_registers.size(); -} - -ErrorOr RegistersModel::column_name(int column) const -{ - switch (column) { - case Column::Register: - return "Register"_string; - case Column::Value: - return "Value"_string; - default: - VERIFY_NOT_REACHED(); - } -} - -GUI::Variant RegistersModel::data(const GUI::ModelIndex& index, GUI::ModelRole role) const -{ - auto& reg = m_registers[index.row()]; - - if (role == GUI::ModelRole::ForegroundColor) { - if (reg.changed) - return Color(Color::Red); - else - return Color(Color::Black); - } - - if (role == GUI::ModelRole::Display) { - if (index.column() == Column::Register) - return reg.name; - if (index.column() == Column::Value) - return ByteString::formatted("{:p}", reg.value); - return {}; - } - return {}; -} - -} diff --git a/Userland/DevTools/HackStudio/Debugger/RegistersModel.h b/Userland/DevTools/HackStudio/Debugger/RegistersModel.h deleted file mode 100644 index b9d347c81fe..00000000000 --- a/Userland/DevTools/HackStudio/Debugger/RegistersModel.h +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright (c) 2020, Luke Wilde - * Copyright (c) 2022, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include - -namespace HackStudio { - -struct RegisterData { - ByteString name; - FlatPtr value; - bool changed { false }; -}; - -class RegistersModel final : public GUI::Model { -public: - static RefPtr create(PtraceRegisters const& regs) - { - return adopt_ref(*new RegistersModel(regs)); - } - - static RefPtr create(PtraceRegisters const& current_regs, PtraceRegisters const& previous_regs) - { - return adopt_ref(*new RegistersModel(current_regs, previous_regs)); - } - - enum Column { - Register, - Value, - __Count - }; - - virtual ~RegistersModel() override = default; - - virtual int row_count(const GUI::ModelIndex& = GUI::ModelIndex()) const override; - virtual int column_count(const GUI::ModelIndex& = GUI::ModelIndex()) const override { return Column::__Count; } - virtual ErrorOr column_name(int) const override; - virtual GUI::Variant data(const GUI::ModelIndex&, GUI::ModelRole) const override; - - PtraceRegisters const& raw_registers() const { return m_raw_registers; } - -private: - explicit RegistersModel(PtraceRegisters const& regs); - RegistersModel(PtraceRegisters const& current_regs, PtraceRegisters const& previous_regs); - - PtraceRegisters m_raw_registers; - Vector m_registers; -}; - -} diff --git a/Userland/DevTools/HackStudio/Debugger/VariablesModel.cpp b/Userland/DevTools/HackStudio/Debugger/VariablesModel.cpp deleted file mode 100644 index 178585c3b5c..00000000000 --- a/Userland/DevTools/HackStudio/Debugger/VariablesModel.cpp +++ /dev/null @@ -1,175 +0,0 @@ -/* - * Copyright (c) 2020, Itamar S. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "VariablesModel.h" -#include -#include - -namespace HackStudio { - -GUI::ModelIndex VariablesModel::index(int row, int column, const GUI::ModelIndex& parent_index) const -{ - if (!parent_index.is_valid()) { - if (static_cast(row) >= m_variables.size()) - return {}; - return create_index(row, column, m_variables[row].ptr()); - } - auto* parent = static_cast(parent_index.internal_data()); - if (static_cast(row) >= parent->members.size()) - return {}; - auto* child = &parent->members[row]; - return create_index(row, column, child); -} - -GUI::ModelIndex VariablesModel::parent_index(const GUI::ModelIndex& index) const -{ - if (!index.is_valid()) - return {}; - auto* child = static_cast(index.internal_data()); - auto* parent = child->parent; - if (parent == nullptr) - return {}; - - if (parent->parent == nullptr) { - for (size_t row = 0; row < m_variables.size(); row++) - if (m_variables[row].ptr() == parent) - return create_index(row, 0, parent); - VERIFY_NOT_REACHED(); - } - for (size_t row = 0; row < parent->parent->members.size(); row++) { - Debug::DebugInfo::VariableInfo* child_at_row = parent->parent->members[row].ptr(); - if (child_at_row == parent) - return create_index(row, 0, parent); - } - VERIFY_NOT_REACHED(); -} - -int VariablesModel::row_count(const GUI::ModelIndex& index) const -{ - if (!index.is_valid()) - return m_variables.size(); - auto* node = static_cast(index.internal_data()); - return node->members.size(); -} - -static ByteString variable_value_as_string(Debug::DebugInfo::VariableInfo const& variable) -{ - if (variable.location_type != Debug::DebugInfo::VariableInfo::LocationType::Address) - return "N/A"; - - auto variable_address = variable.location_data.address; - - if (variable.is_enum_type()) { - auto value = Debugger::the().session()->peek(variable_address); - VERIFY(value.has_value()); - auto it = variable.type->members.find_if([&enumerator_value = value.value()](auto const& enumerator) { - return enumerator->constant_data.as_u32 == enumerator_value; - }); - if (it.is_end()) - return ByteString::formatted("Unknown ({})", value.value()); - return ByteString::formatted("{}::{}", variable.type_name, (*it)->name); - } - - if (variable.type_name == "int") { - auto value = Debugger::the().session()->peek(variable_address); - VERIFY(value.has_value()); - return ByteString::formatted("{}", static_cast(value.value())); - } - - if (variable.type_name == "char") { - auto value = Debugger::the().session()->peek(variable_address); - VERIFY(value.has_value()); - return ByteString::formatted("'{0:c}'", (char)value.value()); - } - - if (variable.type_name == "bool") { - auto value = Debugger::the().session()->peek(variable_address); - VERIFY(value.has_value()); - return (value.value() & 1) ? "true" : "false"; - } - - return ByteString::formatted("type: {} @ {:p}, ", variable.type_name, variable_address); -} - -static Optional string_to_variable_value(StringView string_value, Debug::DebugInfo::VariableInfo const& variable) -{ - if (variable.is_enum_type()) { - auto prefix_string = ByteString::formatted("{}::", variable.type_name); - auto string_to_use = string_value; - if (string_value.starts_with(prefix_string)) - string_to_use = string_value.substring_view(prefix_string.length(), string_value.length() - prefix_string.length()); - - auto it = variable.type->members.find_if([string_to_use](auto const& enumerator) { - return enumerator->name == string_to_use; - }); - - if (it.is_end()) - return {}; - return (*it)->constant_data.as_u32; - } - - if (variable.type_name == "int") { - auto value = string_value.to_number(); - if (value.has_value()) - return value.value(); - return {}; - } - - if (variable.type_name == "bool") { - if (string_value == "true") - return true; - if (string_value == "false") - return false; - return {}; - } - - return {}; -} - -void VariablesModel::set_variable_value(const GUI::ModelIndex& index, StringView string_value, GUI::Window* parent_window) -{ - auto variable = static_cast(index.internal_data()); - - auto value = string_to_variable_value(string_value, *variable); - - if (value.has_value()) { - auto success = Debugger::the().session()->poke(variable->location_data.address, value.value()); - VERIFY(success); - return; - } - - GUI::MessageBox::show( - parent_window, - ByteString::formatted("String value \"{}\" could not be converted to a value of type {}.", string_value, variable->type_name), - "Set value failed"sv, - GUI::MessageBox::Type::Error); -} - -GUI::Variant VariablesModel::data(const GUI::ModelIndex& index, GUI::ModelRole role) const -{ - auto* variable = static_cast(index.internal_data()); - switch (role) { - case GUI::ModelRole::Display: { - auto value_as_string = variable_value_as_string(*variable); - return ByteString::formatted("{}: {}", variable->name, value_as_string); - } - case GUI::ModelRole::Icon: - return m_variable_icon; - default: - return {}; - } -} - -RefPtr VariablesModel::create(Debug::ProcessInspector& inspector, PtraceRegisters const& regs) -{ - auto lib = inspector.library_at(regs.ip()); - if (!lib) - return nullptr; - auto variables = lib->debug_info->get_variables_in_current_scope(regs).release_value_but_fixme_should_propagate_errors(); - return adopt_ref(*new VariablesModel(inspector, move(variables), regs)); -} - -} diff --git a/Userland/DevTools/HackStudio/Debugger/VariablesModel.h b/Userland/DevTools/HackStudio/Debugger/VariablesModel.h deleted file mode 100644 index 439e8a0a590..00000000000 --- a/Userland/DevTools/HackStudio/Debugger/VariablesModel.h +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright (c) 2020, Itamar S. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include "Debugger.h" -#include -#include -#include -#include - -namespace HackStudio { - -class VariablesModel final : public GUI::Model { -public: - static RefPtr create(Debug::ProcessInspector&, PtraceRegisters const& regs); - - void set_variable_value(const GUI::ModelIndex&, StringView, GUI::Window*); - - virtual int row_count(const GUI::ModelIndex& = GUI::ModelIndex()) const override; - virtual int column_count(const GUI::ModelIndex& = GUI::ModelIndex()) const override { return 1; } - virtual GUI::Variant data(const GUI::ModelIndex& index, GUI::ModelRole role) const override; - virtual GUI::ModelIndex parent_index(const GUI::ModelIndex&) const override; - virtual GUI::ModelIndex index(int row, int column = 0, const GUI::ModelIndex& = GUI::ModelIndex()) const override; - Debug::ProcessInspector& inspector() { return m_inspector; } - -private: - explicit VariablesModel(Debug::ProcessInspector& inspector, Vector>&& variables, PtraceRegisters const& regs) - : m_variables(move(variables)) - , m_regs(regs) - , m_inspector(inspector) - { - m_variable_icon.set_bitmap_for_size(16, Gfx::Bitmap::load_from_file("/res/icons/16x16/inspector-object.png"sv).release_value_but_fixme_should_propagate_errors()); - } - Vector> m_variables; - PtraceRegisters m_regs; - - GUI::Icon m_variable_icon; - Debug::ProcessInspector& m_inspector; -}; - -} diff --git a/Userland/DevTools/HackStudio/DeclarationsModel.cpp b/Userland/DevTools/HackStudio/DeclarationsModel.cpp deleted file mode 100644 index 9c202c9adba..00000000000 --- a/Userland/DevTools/HackStudio/DeclarationsModel.cpp +++ /dev/null @@ -1,97 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * Copyright (c) 2022, the SerenityOS developers. - * Copyright (c) 2024, Sam Atkins - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "DeclarationsModel.h" -#include "ProjectDeclarations.h" -#include - -namespace HackStudio { - -static String qualified_symbol_name(CodeComprehension::Declaration symbol) -{ - if (!symbol.scope.is_empty()) - return MUST(String::from_byte_string(symbol.name)); - return MUST(String::formatted("{}::{}", symbol.scope, symbol.name)); -} - -Declaration Declaration::create_filename(ByteString const& filename) -{ - Declaration s; - s.as_filename = filename; - return s; -} -Declaration Declaration::create_symbol_declaration(CodeComprehension::Declaration const& decl) -{ - Declaration s; - s.as_symbol_declaration = decl; - return s; -} - -GUI::Variant DeclarationsModel::data(GUI::ModelIndex const& index, GUI::ModelRole role) const -{ - auto& suggestion = m_declarations.at(index.row()); - if (role != GUI::ModelRole::Display) - return {}; - - if (suggestion.is_filename()) { - if (index.column() == Column::Name) - return suggestion.as_filename.value(); - if (index.column() == Column::Filename) - return ""; - if (index.column() == Column::Icon) - return GUI::FileIconProvider::icon_for_path(suggestion.as_filename.value()); - } - if (suggestion.is_symbol_declaration()) { - if (index.column() == Column::Name) - return qualified_symbol_name(suggestion.as_symbol_declaration.value()); - if (index.column() == Column::Filename) - return suggestion.as_symbol_declaration.value().position.file; - if (index.column() == Column::Icon) { - auto icon = ProjectDeclarations::get_icon_for(suggestion.as_symbol_declaration.value().type); - if (icon.has_value()) - return icon.value(); - return {}; - } - } - - return {}; -} - -GUI::Model::MatchResult DeclarationsModel::data_matches(GUI::ModelIndex const& index, GUI::Variant const& term) const -{ - if (index.row() < 0 || (size_t)index.row() >= m_declarations.size()) - return { TriState::False }; - - auto needle = term.as_string(); - if (needle.is_empty()) - return { TriState::True }; - - auto& declaration = m_declarations[index.row()]; - if (declaration.is_filename()) { - if (auto match_result = fuzzy_match(needle, *declaration.as_filename); match_result.matched) - return { TriState::True, match_result.score }; - return { TriState::False }; - } - if (declaration.is_symbol_declaration()) { - auto& symbol = *declaration.as_symbol_declaration; - auto haystack = qualified_symbol_name(symbol); - if (auto match_result = fuzzy_match(needle, haystack); match_result.matched) - return { TriState::True, match_result.score }; - return { TriState::False }; - } - - return { TriState::False }; -} - -void DeclarationsModel::set_declarations(Vector&& declarations) -{ - m_declarations = move(declarations); - did_update(); -} - -} diff --git a/Userland/DevTools/HackStudio/DeclarationsModel.h b/Userland/DevTools/HackStudio/DeclarationsModel.h deleted file mode 100644 index 310c503ccf5..00000000000 --- a/Userland/DevTools/HackStudio/DeclarationsModel.h +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * Copyright (c) 2022, the SerenityOS developers. - * Copyright (c) 2024, Sam Atkins - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include - -namespace HackStudio { - -struct Declaration { - static Declaration create_filename(ByteString const& filename); - static Declaration create_symbol_declaration(CodeComprehension::Declaration const&); - - bool is_filename() const { return as_filename.has_value(); } - bool is_symbol_declaration() const { return as_symbol_declaration.has_value(); } - - Optional as_filename; - Optional as_symbol_declaration; -}; - -class DeclarationsModel final : public GUI::Model { -public: - explicit DeclarationsModel(Vector&& declarations) - : m_declarations(move(declarations)) - { - } - - enum Column { - Icon, - Name, - Filename, - __Column_Count, - }; - - virtual int row_count(GUI::ModelIndex const& index = GUI::ModelIndex()) const override - { - if (!index.is_valid()) - return m_declarations.size(); - return 0; - } - - virtual int column_count(GUI::ModelIndex const& = GUI::ModelIndex()) const override { return Column::__Column_Count; } - virtual GUI::Variant data(GUI::ModelIndex const& index, GUI::ModelRole role) const override; - virtual MatchResult data_matches(GUI::ModelIndex const&, GUI::Variant const&) const override; - - Vector const& declarations() const { return m_declarations; } - void set_declarations(Vector&&); - -private: - Vector m_declarations; -}; - -} diff --git a/Userland/DevTools/HackStudio/Dialogs/Git/GitCommitDialog.cpp b/Userland/DevTools/HackStudio/Dialogs/Git/GitCommitDialog.cpp deleted file mode 100644 index ed2fe937a65..00000000000 --- a/Userland/DevTools/HackStudio/Dialogs/Git/GitCommitDialog.cpp +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright (c) 2021, Conor Byrne - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "GitCommitDialog.h" -#include - -namespace HackStudio { - -GitCommitDialog::GitCommitDialog(GUI::Window* parent) - : Dialog(parent) -{ - resize(400, 260); - center_within(*parent); - set_title("Commit"); - set_icon(parent->icon()); - - auto widget = set_main_widget(); - widget->load_from_gml(git_commit_dialog_gml).release_value_but_fixme_should_propagate_errors(); - - m_message_editor = widget->find_descendant_of_type_named("message_editor"); - m_cancel_button = widget->find_descendant_of_type_named("cancel_button"); - m_commit_button = widget->find_descendant_of_type_named("commit_button"); - m_line_and_col_label = widget->find_descendant_of_type_named("line_and_col_label"); - - m_message_editor->on_change = [this]() { - m_commit_button->set_enabled(!m_message_editor->text().is_empty() && on_commit); - }; - m_message_editor->on_cursor_change = [this]() { - auto line = m_message_editor->cursor().line() + 1; - auto col = m_message_editor->cursor().column(); - - m_line_and_col_label->set_text(String::formatted("Line: {}, Col: {}", line, col).release_value_but_fixme_should_propagate_errors()); - }; - - m_commit_button->set_enabled(!m_message_editor->text().is_empty() && on_commit); - m_commit_button->on_click = [this](auto) { - on_commit(m_message_editor->text()); - done(ExecResult::OK); - }; - - m_cancel_button->on_click = [this](auto) { - done(ExecResult::Cancel); - }; -} - -} diff --git a/Userland/DevTools/HackStudio/Dialogs/Git/GitCommitDialog.gml b/Userland/DevTools/HackStudio/Dialogs/Git/GitCommitDialog.gml deleted file mode 100644 index 1a07181bc62..00000000000 --- a/Userland/DevTools/HackStudio/Dialogs/Git/GitCommitDialog.gml +++ /dev/null @@ -1,41 +0,0 @@ -@GUI::Frame { - fill_with_background_color: true - layout: @GUI::VerticalBoxLayout { - spacing: 4 - margins: [4, 4, 4, 4] - } - - @GUI::Label { - text: "Enter commit message:" - text_alignment: "CenterLeft" - fixed_height: 20 - } - - @GUI::TextEditor { - name: "message_editor" - } - - @GUI::Widget { - layout: @GUI::HorizontalBoxLayout {} - shrink_to_fit: true - - @GUI::Label { - name: "line_and_col_label" - text: "Line: 1, Col: 0" - text_alignment: "CenterLeft" - fixed_height: 20 - } - - @GUI::Button { - name: "commit_button" - text: "Commit" - fixed_width: 75 - } - - @GUI::Button { - name: "cancel_button" - text: "Cancel" - fixed_width: 75 - } - } -} diff --git a/Userland/DevTools/HackStudio/Dialogs/Git/GitCommitDialog.h b/Userland/DevTools/HackStudio/Dialogs/Git/GitCommitDialog.h deleted file mode 100644 index 20145d16190..00000000000 --- a/Userland/DevTools/HackStudio/Dialogs/Git/GitCommitDialog.h +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright (c) 2021, Conor Byrne - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include -#include - -namespace HackStudio { - -using OnCommitCallback = Function; - -class GitCommitDialog final : public GUI::Dialog { - C_OBJECT(GitCommitDialog); - -public: - OnCommitCallback on_commit; - -private: - GitCommitDialog(GUI::Window* parent); - - RefPtr m_commit_button; - RefPtr m_cancel_button; - RefPtr m_message_editor; - RefPtr m_line_and_col_label; -}; - -} diff --git a/Userland/DevTools/HackStudio/Dialogs/NewProjectDialog.cpp b/Userland/DevTools/HackStudio/Dialogs/NewProjectDialog.cpp deleted file mode 100644 index dbf2643fbd7..00000000000 --- a/Userland/DevTools/HackStudio/Dialogs/NewProjectDialog.cpp +++ /dev/null @@ -1,213 +0,0 @@ -/* - * Copyright (c) 2021, Nick Vella - * Copyright (c) 2022, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "NewProjectDialog.h" -#include "ProjectTemplatesModel.h" -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace HackStudio { - -static Regex const s_project_name_validity_regex("^([A-Za-z0-9_-])*$"); - -GUI::Dialog::ExecResult NewProjectDialog::show(GUI::Window* parent_window) -{ - auto dialog = NewProjectDialog::construct(parent_window); - - if (parent_window) - dialog->set_icon(parent_window->icon()); - - auto result = dialog->exec(); - - return result; -} - -NewProjectDialog::NewProjectDialog(GUI::Window* parent) - : Dialog(parent) - , m_model(ProjectTemplatesModel::create()) -{ - resize(500, 385); - center_on_screen(); - set_resizable(false); - set_title("New Project"); - - auto main_widget = set_main_widget(); - main_widget->load_from_gml(new_project_dialog_gml).release_value_but_fixme_should_propagate_errors(); - - m_icon_view_container = *main_widget->find_descendant_of_type_named("icon_view_container"); - m_icon_view = m_icon_view_container->add(); - m_icon_view->set_always_wrap_item_labels(true); - m_icon_view->set_model(m_model); - m_icon_view->set_model_column(ProjectTemplatesModel::Column::Name); - m_icon_view->on_selection_change = [&]() { - update_dialog(); - }; - m_icon_view->on_activation = [&](auto&) { - if (m_input_valid) - do_create_project(); - }; - - m_description_label = *main_widget->find_descendant_of_type_named("description_label"); - m_name_input = *main_widget->find_descendant_of_type_named("name_input"); - m_name_input->on_change = [&]() { - update_dialog(); - }; - m_create_in_input = *main_widget->find_descendant_of_type_named("create_in_input"); - m_create_in_input->on_change = [&]() { - update_dialog(); - }; - m_full_path_label = *main_widget->find_descendant_of_type_named("full_path_label"); - - m_ok_button = *main_widget->find_descendant_of_type_named("ok_button"); - m_ok_button->set_default(true); - m_ok_button->on_click = [this](auto) { - do_create_project(); - }; - - m_cancel_button = *main_widget->find_descendant_of_type_named("cancel_button"); - m_cancel_button->on_click = [this](auto) { - done(ExecResult::Cancel); - }; - - m_browse_button = *find_descendant_of_type_named("browse_button"); - m_browse_button->on_click = [this](auto) { - Optional path = GUI::FilePicker::get_open_filepath(this, {}, Core::StandardPaths::home_directory(), true); - if (path.has_value()) - m_create_in_input->set_text(path.value().view()); - }; -} - -RefPtr NewProjectDialog::selected_template() -{ - if (m_icon_view->selection().is_empty()) { - return {}; - } - - auto project_template = m_model->template_for_index(m_icon_view->selection().first()); - VERIFY(!project_template.is_null()); - - return project_template; -} - -void NewProjectDialog::update_dialog() -{ - auto project_template = selected_template(); - m_input_valid = true; - - if (project_template) { - m_description_label->set_text(String::from_byte_string(project_template->description()).release_value_but_fixme_should_propagate_errors()); - } else { - m_description_label->set_text("Select a project template to continue."_string); - m_input_valid = false; - } - - auto maybe_project_path = get_project_full_path(); - - if (maybe_project_path.has_value()) { - m_full_path_label->set_text(String::from_byte_string(maybe_project_path.value()).release_value_but_fixme_should_propagate_errors()); - } else { - m_full_path_label->set_text("Invalid name or creation directory."_string); - m_input_valid = false; - } - - m_ok_button->set_enabled(m_input_valid); -} - -Optional NewProjectDialog::get_available_project_name() -{ - auto create_in = m_create_in_input->text(); - auto chosen_name = m_name_input->text(); - - // Ensure project name isn't empty or entirely whitespace - if (chosen_name.is_empty() || chosen_name.is_whitespace()) - return {}; - - // Validate project name with validity regex - if (!s_project_name_validity_regex.has_match(chosen_name)) - return {}; - - // Check for up-to 999 variations of the project name, in case it's already taken - for (int i = 0; i < 1000; i++) { - auto candidate = (i == 0) - ? chosen_name - : ByteString::formatted("{}-{}", chosen_name, i); - - if (!FileSystem::exists(ByteString::formatted("{}/{}", create_in, candidate))) - return candidate; - } - - return {}; -} - -Optional NewProjectDialog::get_project_full_path() -{ - // Do not permit forward-slashes in project names - if (m_name_input->text().contains('/')) - return {}; - - auto create_in = m_create_in_input->text(); - auto maybe_project_name = get_available_project_name(); - - if (!maybe_project_name.has_value()) - return {}; - - return LexicalPath::join(create_in, *maybe_project_name).string(); -} - -void NewProjectDialog::do_create_project() -{ - auto project_template = selected_template(); - if (!project_template) { - GUI::MessageBox::show_error(this, "Could not create project: no template selected."sv); - return; - } - - auto maybe_project_name = get_available_project_name(); - auto maybe_project_full_path = get_project_full_path(); - if (!maybe_project_name.has_value() || !maybe_project_full_path.has_value()) { - GUI::MessageBox::show_error(this, "Could not create project: invalid project name or path."sv); - return; - } - - auto create_in = m_create_in_input->text(); - if (!FileSystem::exists(create_in) || !FileSystem::is_directory(create_in)) { - auto result = GUI::MessageBox::show(this, ByteString::formatted("The directory \"{}\" does not exist yet, would you like to create it?", create_in), "New Project"sv, GUI::MessageBox::Type::Question, GUI::MessageBox::InputType::YesNo); - if (result != GUI::MessageBox::ExecResult::Yes) - return; - - auto created = Core::Directory::create(maybe_project_full_path.value(), Core::Directory::CreateDirectories::Yes); - if (created.is_error()) { - GUI::MessageBox::show_error(this, ByteString::formatted("Could not create directory \"{}\"", create_in)); - return; - } - } - - auto creation_result = project_template->create_project(maybe_project_name.value(), maybe_project_full_path.value()); - if (!creation_result.is_error()) { - // Successfully created, attempt to open the new project - m_created_project_path = maybe_project_full_path.value(); - done(ExecResult::OK); - } else { - GUI::MessageBox::show_error(this, ByteString::formatted("Could not create project: {}", creation_result.error())); - } -} - -} diff --git a/Userland/DevTools/HackStudio/Dialogs/NewProjectDialog.gml b/Userland/DevTools/HackStudio/Dialogs/NewProjectDialog.gml deleted file mode 100644 index eda5ea654bd..00000000000 --- a/Userland/DevTools/HackStudio/Dialogs/NewProjectDialog.gml +++ /dev/null @@ -1,99 +0,0 @@ -@GUI::Widget { - fill_with_background_color: true - layout: @GUI::VerticalBoxLayout { - margins: [4] - } - - @GUI::Label { - text: "Templates:" - text_alignment: "CenterLeft" - max_height: 20 - } - - @GUI::Widget { - layout: @GUI::VerticalBoxLayout {} - name: "icon_view_container" - } - - @GUI::Label { - name: "description_label" - text_alignment: "CenterLeft" - frame_style: "SunkenContainer" - max_height: 24 - } - - @GUI::Widget { - layout: @GUI::HorizontalBoxLayout {} - max_height: 24 - - @GUI::Label { - text: "Name:" - text_alignment: "CenterLeft" - max_width: 75 - } - - @GUI::TextBox { - name: "name_input" - } - } - - @GUI::Widget { - layout: @GUI::HorizontalBoxLayout {} - max_height: 24 - - @GUI::Label { - text: "Create in:" - text_alignment: "CenterLeft" - max_width: 75 - } - - @GUI::TextBox { - name: "create_in_input" - text: "/home/anon/Source" - } - - @GUI::Button { - name: "browse_button" - text: "Browse" - max_width: 75 - } - } - - @GUI::Widget { - layout: @GUI::HorizontalBoxLayout {} - max_height: 24 - - @GUI::Label { - text: "Full path:" - text_alignment: "CenterLeft" - max_width: 75 - } - - @GUI::Label { - name: "full_path_label" - text_alignment: "CenterLeft" - text: "" - frame_style: "SunkenContainer" - max_height: 22 - } - } - - @GUI::Widget { - layout: @GUI::HorizontalBoxLayout {} - max_height: 24 - - @GUI::Layout::Spacer {} - - @GUI::Button { - name: "ok_button" - text: "OK" - max_width: 75 - } - - @GUI::Button { - name: "cancel_button" - text: "Cancel" - max_width: 75 - } - } -} diff --git a/Userland/DevTools/HackStudio/Dialogs/NewProjectDialog.h b/Userland/DevTools/HackStudio/Dialogs/NewProjectDialog.h deleted file mode 100644 index 14219682e3e..00000000000 --- a/Userland/DevTools/HackStudio/Dialogs/NewProjectDialog.h +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright (c) 2021, Nick Vella - * Copyright (c) 2022, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include "ProjectTemplatesModel.h" -#include - -#include -#include -#include -#include -#include - -namespace HackStudio { - -class NewProjectDialog : public GUI::Dialog { - C_OBJECT(NewProjectDialog); - -public: - static ExecResult show(GUI::Window* parent_window); - - Optional created_project_path() const { return m_created_project_path; } - -private: - NewProjectDialog(GUI::Window* parent); - virtual ~NewProjectDialog() override = default; - - void update_dialog(); - Optional get_available_project_name(); - Optional get_project_full_path(); - - void do_create_project(); - - RefPtr selected_template(); - - NonnullRefPtr m_model; - bool m_input_valid { false }; - - RefPtr m_icon_view_container; - RefPtr m_icon_view; - - RefPtr m_description_label; - RefPtr m_name_input; - RefPtr m_create_in_input; - RefPtr m_full_path_label; - - RefPtr m_ok_button; - RefPtr m_cancel_button; - RefPtr m_browse_button; - - Optional m_created_project_path; -}; - -} diff --git a/Userland/DevTools/HackStudio/Dialogs/ProjectTemplatesModel.cpp b/Userland/DevTools/HackStudio/Dialogs/ProjectTemplatesModel.cpp deleted file mode 100644 index 2c6c17e51b3..00000000000 --- a/Userland/DevTools/HackStudio/Dialogs/ProjectTemplatesModel.cpp +++ /dev/null @@ -1,141 +0,0 @@ -/* - * Copyright (c) 2021, Nick Vella - * Copyright (c) 2021, sin-ack - * Copyright (c) 2022, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "ProjectTemplatesModel.h" - -#include -#include -#include -#include -#include -#include - -namespace HackStudio { - -ProjectTemplatesModel::ProjectTemplatesModel() - : m_templates() - , m_mapping() -{ - auto watcher_or_error = Core::FileWatcher::create(); - if (!watcher_or_error.is_error()) { - m_file_watcher = watcher_or_error.release_value(); - m_file_watcher->on_change = [&](auto) { - invalidate(); - }; - - auto watch_result = m_file_watcher->add_watch( - ProjectTemplate::templates_path(), - Core::FileWatcherEvent::Type::ChildCreated - | Core::FileWatcherEvent::Type::ChildDeleted); - - if (watch_result.is_error()) { - warnln("Unable to watch templates directory, templates will not automatically refresh. Error: {}", watch_result.error()); - } - } else { - warnln("Unable to watch templates directory, templates will not automatically refresh. Error: {}", watcher_or_error.error()); - } - - rescan_templates(); -} - -int ProjectTemplatesModel::row_count(const GUI::ModelIndex&) const -{ - return m_mapping.size(); -} - -int ProjectTemplatesModel::column_count(const GUI::ModelIndex&) const -{ - return Column::__Count; -} - -ErrorOr ProjectTemplatesModel::column_name(int column) const -{ - switch (column) { - case Column::Icon: - return "Icon"_string; - case Column::Id: - return "ID"_string; - case Column::Name: - return "Name"_string; - } - VERIFY_NOT_REACHED(); -} - -GUI::Variant ProjectTemplatesModel::data(const GUI::ModelIndex& index, GUI::ModelRole role) const -{ - if (static_cast(index.row()) >= m_mapping.size()) - return {}; - - if (role == GUI::ModelRole::TextAlignment) - return Gfx::TextAlignment::CenterLeft; - - if (role == GUI::ModelRole::Display) { - switch (index.column()) { - case Column::Name: - return m_mapping[index.row()]->name(); - case Column::Id: - return m_mapping[index.row()]->id(); - } - } - - if (role == GUI::ModelRole::Icon) { - return m_mapping[index.row()]->icon(); - } - - return {}; -} - -RefPtr ProjectTemplatesModel::template_for_index(const GUI::ModelIndex& index) -{ - if (static_cast(index.row()) >= m_mapping.size()) - return {}; - - return m_mapping[index.row()]; -} - -void ProjectTemplatesModel::update() -{ - rescan_templates(); - did_update(); -} - -void ProjectTemplatesModel::rescan_templates() -{ - m_templates.clear(); - - // Iterate over template manifest INI files in the templates path - Core::DirIterator di(ProjectTemplate::templates_path(), Core::DirIterator::SkipDots); - if (di.has_error()) { - warnln("DirIterator: {}", di.error()); - return; - } - - while (di.has_next()) { - auto full_path = LexicalPath(di.next_full_path()); - if (!full_path.has_extension(".ini"sv)) - continue; - - auto project_template = ProjectTemplate::load_from_manifest(full_path.string()); - if (!project_template) { - warnln("Template manifest {} is invalid.", full_path.string()); - continue; - } - - m_templates.append(project_template.release_nonnull()); - } - - // Enumerate the loaded projects into a sorted mapping, by priority value descending. - m_mapping.clear(); - for (auto& project_template : m_templates) - m_mapping.append(project_template); - quick_sort(m_mapping, [](auto a, auto b) { - return a->priority() > b->priority(); - }); -} - -} diff --git a/Userland/DevTools/HackStudio/Dialogs/ProjectTemplatesModel.h b/Userland/DevTools/HackStudio/Dialogs/ProjectTemplatesModel.h deleted file mode 100644 index c897632f6ac..00000000000 --- a/Userland/DevTools/HackStudio/Dialogs/ProjectTemplatesModel.h +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright (c) 2021, Nick Vella - * Copyright (c) 2022, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include - -namespace HackStudio { - -class ProjectTemplatesModel final : public GUI::Model { -public: - static NonnullRefPtr create() - { - return adopt_ref(*new ProjectTemplatesModel()); - } - - enum Column { - Icon = 0, - Id, - Name, - __Count - }; - - virtual ~ProjectTemplatesModel() override = default; - - RefPtr template_for_index(const GUI::ModelIndex& index); - - virtual int row_count(const GUI::ModelIndex& = GUI::ModelIndex()) const override; - virtual int column_count(const GUI::ModelIndex& = GUI::ModelIndex()) const override; - virtual ErrorOr column_name(int) const override; - virtual GUI::Variant data(const GUI::ModelIndex&, GUI::ModelRole) const override; - - void update(); - void rescan_templates(); - -private: - explicit ProjectTemplatesModel(); - - Vector> m_templates; - Vector m_mapping; - - RefPtr m_file_watcher; -}; - -} diff --git a/Userland/DevTools/HackStudio/Editor.cpp b/Userland/DevTools/HackStudio/Editor.cpp deleted file mode 100644 index e28078424a8..00000000000 --- a/Userland/DevTools/HackStudio/Editor.cpp +++ /dev/null @@ -1,835 +0,0 @@ -/* - * Copyright (c) 2018-2021, Andreas Kling - * Copyright (c) 2018-2022, the SerenityOS developers. - * Copyright (c) 2023-2024, Sam Atkins - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "Editor.h" -#include "Debugger/Debugger.h" -#include "EditorWrapper.h" -#include "HackStudio.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace HackStudio { - -enum class TooltipRole { - Documentation, - ParametersHint, -}; - -static RefPtr s_tooltip_window; -static RefPtr s_tooltip_page_view; -static Optional m_tooltip_role; - -ErrorOr> Editor::try_create() -{ - NonnullRefPtr editor = TRY(adopt_nonnull_ref_or_enomem(new (nothrow) Editor())); - TRY(initialize_tooltip_window()); - return editor; -} - -Editor::Editor() -{ - create_tokens_info_timer(); - - set_document(CodeDocument::create()); - m_move_execution_to_line_action = GUI::Action::create("Set Execution Point to Cursor Line", [this](auto&) { - VERIFY(is_program_running()); - auto success = Debugger::the().set_execution_position(currently_open_file(), cursor().line()); - if (success) { - set_execution_position(cursor().line()); - } else { - GUI::MessageBox::show(window(), "Failed to set execution position"sv, "Error"sv, GUI::MessageBox::Type::Error); - } - }); - - set_debug_mode(false); - - add_custom_context_menu_action(*m_move_execution_to_line_action); - - set_gutter_visible(true); - on_gutter_click = [&](auto line, auto) { - add_breakpoint(line).release_value_but_fixme_should_propagate_errors(); - }; - - m_git_diff_indicator_id = register_gutter_indicator( - [&](auto& painter, Gfx::IntRect rect, size_t line) { - auto diff_type = code_document().line_difference(line); - switch (diff_type) { - case CodeDocument::DiffType::AddedLine: - painter.draw_text(rect, "+"sv, font(), Gfx::TextAlignment::Center); - break; - case CodeDocument::DiffType::ModifiedLine: - painter.draw_text(rect, "!"sv, font(), Gfx::TextAlignment::Center); - break; - case CodeDocument::DiffType::DeletedLinesBefore: - painter.draw_text(rect, "-"sv, font(), Gfx::TextAlignment::Center); - break; - case CodeDocument::DiffType::None: - VERIFY_NOT_REACHED(); - } - }).release_value_but_fixme_should_propagate_errors(); - - m_breakpoint_indicator_id = register_gutter_indicator( - [&](auto& painter, Gfx::IntRect rect, size_t) { - auto const& icon = breakpoint_icon_bitmap(); - painter.draw_scaled_bitmap(rect, icon, icon.rect()); - }, - [&](size_t line_index, auto) { - remove_breakpoint(line_index); - }).release_value_but_fixme_should_propagate_errors(); - - m_execution_indicator_id = register_gutter_indicator( - [&](auto& painter, Gfx::IntRect rect, size_t) { - auto const& icon = current_position_icon_bitmap(); - painter.draw_scaled_bitmap(rect, icon, icon.rect()); - }).release_value_but_fixme_should_propagate_errors(); - - if (Config::read_string("HackStudio"sv, "Global"sv, "DocumentationSearchPaths"sv).is_empty()) { - Config::write_string("HackStudio"sv, "Global"sv, "DocumentationSearchPaths"sv, "[\"/usr/share/man/man2\", \"/usr/share/man/man3\"]"sv); - } -} - -ErrorOr Editor::initialize_tooltip_window() -{ - if (s_tooltip_window.is_null()) { - s_tooltip_window = GUI::Window::construct(); - s_tooltip_window->set_window_type(GUI::WindowType::Tooltip); - } - if (s_tooltip_page_view.is_null()) { - s_tooltip_page_view = s_tooltip_window->set_main_widget(); - } - return {}; -} - -EditorWrapper& Editor::wrapper() -{ - return static_cast(*parent()); -} -EditorWrapper const& Editor::wrapper() const -{ - return static_cast(*parent()); -} - -void Editor::paint_event(GUI::PaintEvent& event) -{ - GUI::TextEditor::paint_event(event); - - GUI::Painter painter(*this); - if (is_focused()) { - painter.add_clip_rect(event.rect()); - - auto rect = frame_inner_rect(); - if (vertical_scrollbar().is_visible()) - rect.set_width(rect.width() - vertical_scrollbar().width()); - if (horizontal_scrollbar().is_visible()) - rect.set_height(rect.height() - horizontal_scrollbar().height()); - painter.draw_rect(rect, palette().selection()); - } -} - -static HashMap& man_paths() -{ - static HashMap paths; - if (paths.is_empty()) { - auto json = Config::read_string("HackStudio"sv, "Global"sv, "DocumentationSearchPaths"sv); - AK::JsonParser parser(json); - - auto value_or_error = parser.parse(); - if (value_or_error.is_error()) - return paths; - - auto value = value_or_error.release_value(); - if (!value.is_array()) - return paths; - - for (auto& json_value : value.as_array().values()) { - if (!json_value.is_string()) - continue; - - Core::DirIterator it(json_value.as_string(), Core::DirIterator::Flags::SkipDots); - while (it.has_next()) { - auto path = it.next_full_path(); - auto title = LexicalPath::title(path); - paths.set(title, path); - } - } - } - - return paths; -} - -void Editor::show_documentation_tooltip_if_available(ByteString const& hovered_token, Gfx::IntPoint screen_location) -{ - auto it = man_paths().find(hovered_token); - if (it == man_paths().end()) { - dbgln_if(EDITOR_DEBUG, "no man path for {}", hovered_token); - if (m_tooltip_role == TooltipRole::Documentation) { - s_tooltip_window->hide(); - m_tooltip_role.clear(); - } - return; - } - - if (s_tooltip_window->is_visible() && m_tooltip_role == TooltipRole::Documentation && hovered_token == m_last_parsed_token) { - return; - } - - dbgln_if(EDITOR_DEBUG, "opening {}", it->value); - auto file_or_error = Core::File::open(it->value, Core::File::OpenMode::Read); - if (file_or_error.is_error()) { - dbgln("Failed to open {}, {}", it->value, file_or_error.error()); - return; - } - - auto buffer_or_error = file_or_error.release_value()->read_until_eof(); - if (buffer_or_error.is_error()) { - dbgln("Couldn't read file: {}", buffer_or_error.error()); - return; - } - - auto man_document = Markdown::Document::parse(buffer_or_error.release_value()); - if (!man_document) { - dbgln("failed to parse markdown"); - return; - } - - s_tooltip_page_view->load_html(man_document->render_to_html(""sv)); - - s_tooltip_window->set_rect(0, 0, 500, 400); - s_tooltip_window->move_to(screen_location.translated(4, 4)); - m_tooltip_role = TooltipRole::Documentation; - s_tooltip_window->show(); - - m_last_parsed_token = hovered_token; -} - -void Editor::mousemove_event(GUI::MouseEvent& event) -{ - GUI::TextEditor::mousemove_event(event); - - if (document().spans().is_empty()) - return; - - auto text_position = text_position_at(event.position()); - if (!text_position.is_valid() && m_tooltip_role == TooltipRole::Documentation) { - s_tooltip_window->hide(); - m_tooltip_role.clear(); - return; - } - - auto highlighter = wrapper().editor().syntax_highlighter(); - if (!highlighter) - return; - - bool hide_tooltip = (m_tooltip_role == TooltipRole::Documentation); - bool is_over_clickable = false; - - if (m_hovering_editor && event.position().x() > fixed_elements_width()) - set_override_cursor(m_hovering_clickable && event.ctrl() ? Gfx::StandardCursor::Hand : Gfx::StandardCursor::IBeam); - - for (auto& span : document().spans()) { - bool is_clickable = (highlighter->is_navigatable(span.data) || highlighter->is_identifier(span.data)); - if (span.range.contains(m_previous_text_position) && !span.range.contains(text_position)) { - if (is_clickable && span.attributes.underline_style.has_value()) { - span.attributes.underline_style.clear(); - wrapper().editor().update(); - } - } - - if (span.range.contains(text_position)) { - auto hovered_span_text = document().text_in_range(span.range); - dbgln_if(EDITOR_DEBUG, "Hovering: {} \"{}\"", span.range, hovered_span_text); - - if (is_clickable) { - is_over_clickable = true; - bool was_underlined = span.attributes.underline_style.has_value(); - bool now_underlined = event.modifiers() & Mod_Ctrl; - span.attributes.underline_style.clear(); - if (now_underlined) - span.attributes.underline_style = Gfx::TextAttributes::UnderlineStyle::Solid; - if (now_underlined != was_underlined) { - wrapper().editor().update(); - } - } - - if (highlighter->is_identifier(span.data)) { - show_documentation_tooltip_if_available(hovered_span_text, event.position().translated(screen_relative_rect().location())); - hide_tooltip = false; - } - } - } - - m_previous_text_position = text_position; - if (hide_tooltip) { - s_tooltip_window->hide(); - m_tooltip_role.clear(); - } - - m_hovering_clickable = (is_over_clickable) && (event.modifiers() & Mod_Ctrl); -} - -void Editor::mousedown_event(GUI::MouseEvent& event) -{ - if (m_tooltip_role == TooltipRole::ParametersHint) { - s_tooltip_window->hide(); - m_tooltip_role.clear(); - } - - auto highlighter = wrapper().editor().syntax_highlighter(); - if (!highlighter) { - GUI::TextEditor::mousedown_event(event); - return; - } - - auto text_position = text_position_at(event.position()); - - if (!(event.modifiers() & Mod_Ctrl)) { - GUI::TextEditor::mousedown_event(event); - return; - } - - if (!text_position.is_valid()) { - GUI::TextEditor::mousedown_event(event); - return; - } - - if (auto* span = document().span_at(text_position)) { - if (highlighter->is_navigatable(span->data)) { - on_navigatable_link_click(*span); - return; - } - if (highlighter->is_identifier(span->data)) { - on_identifier_click(*span); - return; - } - } - - GUI::TextEditor::mousedown_event(event); -} - -void Editor::drag_enter_event(GUI::DragEvent& event) -{ - auto const& mime_types = event.mime_types(); - if (mime_types.contains_slow("text/uri-list"sv)) - event.accept(); -} - -void Editor::drop_event(GUI::DropEvent& event) -{ - event.accept(); - - if (event.mime_data().has_urls()) { - auto urls = event.mime_data().urls(); - if (urls.is_empty()) - return; - window()->move_to_front(); - if (urls.size() > 1) { - GUI::MessageBox::show(window(), "HackStudio can only open one file at a time!"sv, "One at a time please!"sv, GUI::MessageBox::Type::Error); - return; - } - set_current_editor_wrapper(static_cast(parent())); - open_file(urls.first().serialize_path()); - } -} - -void Editor::enter_event(Core::Event& event) -{ - m_hovering_editor = true; - GUI::TextEditor::enter_event(event); -} - -void Editor::leave_event(Core::Event& event) -{ - m_hovering_editor = false; - GUI::TextEditor::leave_event(event); -} - -static HashMap& include_paths() -{ - static HashMap paths; - - auto add_directory = [](ByteString base, Optional recursive, auto handle_directory) -> void { - Core::DirIterator it(recursive.value_or(base), Core::DirIterator::Flags::SkipDots); - while (it.has_next()) { - auto path = it.next_full_path(); - if (!FileSystem::is_directory(path)) { - auto key = path.substring(base.length() + 1, path.length() - base.length() - 1); - dbgln_if(EDITOR_DEBUG, "Adding header \"{}\" in path \"{}\"", key, path); - paths.set(key, path); - } else { - handle_directory(base, path, handle_directory); - } - } - }; - - if (paths.is_empty()) { - add_directory(".", {}, add_directory); - add_directory("/usr/local/include", {}, add_directory); - add_directory("/usr/local/include/c++/9.2.0", {}, add_directory); - add_directory("/usr/include", {}, add_directory); - } - - return paths; -} - -void Editor::navigate_to_include_if_available(ByteString path) -{ - auto it = include_paths().find(path); - if (it == include_paths().end()) { - dbgln_if(EDITOR_DEBUG, "no header {} found.", path); - return; - } - - on_open(it->value); -} - -void Editor::set_execution_position(size_t line_number) -{ - if (execution_position().has_value()) - remove_gutter_indicator(m_execution_indicator_id, execution_position().value()); - add_gutter_indicator(m_execution_indicator_id, line_number); - code_document().set_execution_position(line_number); - scroll_position_into_view({ line_number, 0 }); -} - -void Editor::clear_execution_position() -{ - if (!execution_position().has_value()) { - return; - } - size_t previous_position = execution_position().value(); - code_document().clear_execution_position(); - remove_gutter_indicator(m_execution_indicator_id, previous_position); -} - -Gfx::Bitmap const& Editor::breakpoint_icon_bitmap() -{ - static auto bitmap = Gfx::Bitmap::load_from_file("/res/icons/16x16/breakpoint.png"sv).release_value_but_fixme_should_propagate_errors(); - return *bitmap; -} - -Gfx::Bitmap const& Editor::current_position_icon_bitmap() -{ - static auto bitmap = Gfx::Bitmap::load_from_file("/res/icons/16x16/go-forward.png"sv).release_value_but_fixme_should_propagate_errors(); - return *bitmap; -} - -CodeDocument const& Editor::code_document() const -{ - return verify_cast(document()); -} - -CodeDocument& Editor::code_document() -{ - return const_cast(static_cast(*this).code_document()); -} - -void Editor::set_document(GUI::TextDocument& doc) -{ - if (has_document() && &document() == &doc) - return; - - VERIFY(is(doc)); - GUI::TextEditor::set_document(doc); - - auto& code_document = static_cast(doc); - - set_language_client_for(code_document); - set_syntax_highlighter_for(code_document); - - if (m_language_client) { - set_autocomplete_provider(make(*m_language_client)); - // NOTE: - // When a file is opened for the first time in HackStudio, its content is already synced with the filesystem. - // Otherwise, if the file has already been opened before in some Editor instance, it should exist in the LanguageServer's - // FileDB, and the LanguageServer should already have its up-to-date content. - // So it's OK to just pass an fd here (rather than the TextDocument's content). - auto maybe_fd = Core::System::open(code_document.file_path(), O_RDONLY | O_NOCTTY); - if (maybe_fd.is_error()) { - warnln("Failed to open `{}`: {}", code_document.file_path(), maybe_fd.release_error()); - return; - } - auto fd = maybe_fd.release_value(); - m_language_client->open_file(code_document.file_path(), fd); - close(fd); - } else { - set_autocomplete_provider_for(code_document); - } -} - -Optional Editor::get_autocomplete_request_data() -{ - if (!wrapper().editor().m_language_client) - return {}; - - return Editor::AutoCompleteRequestData { cursor() }; -} - -void Editor::LanguageServerAidedAutocompleteProvider::provide_completions(Function)> callback) -{ - auto& editor = static_cast(*m_editor).wrapper().editor(); - auto data = editor.get_autocomplete_request_data(); - if (!data.has_value()) - callback({}); - - m_language_client.on_autocomplete_suggestions = [callback = move(callback)](auto suggestions) { - callback(suggestions); - }; - - m_language_client.request_autocomplete( - editor.code_document().file_path(), - data.value().position.line(), - data.value().position.column()); -} - -void Editor::after_execute(GUI::TextDocumentUndoCommand const& command) -{ - if (!m_language_client) - return; - - if (is(command)) { - auto const& insert_command = static_cast(command); - m_language_client->insert_text( - code_document().file_path(), - insert_command.text(), - insert_command.range().start().line(), - insert_command.range().start().column()); - return; - } - - if (is(command)) { - auto const& remove_command = static_cast(command); - m_language_client->remove_text( - code_document().file_path(), - remove_command.range().start().line(), - remove_command.range().start().column(), - remove_command.range().end().line(), - remove_command.range().end().column()); - return; - } - - flush_file_content_to_langauge_server(); -} - -void Editor::undo() -{ - TextEditor::undo(); - flush_file_content_to_langauge_server(); -} - -void Editor::redo() -{ - TextEditor::redo(); - flush_file_content_to_langauge_server(); -} - -void Editor::flush_file_content_to_langauge_server() -{ - if (!m_language_client) - return; - - m_language_client->set_file_content( - code_document().file_path(), - document().text()); -} - -void Editor::on_navigatable_link_click(const GUI::TextDocumentSpan& span) -{ - auto span_text = document().text_in_range(span.range); - auto header_path = span_text.substring(1, span_text.length() - 2); - dbgln_if(EDITOR_DEBUG, "Ctrl+click: {} \"{}\"", span.range, header_path); - navigate_to_include_if_available(header_path); -} - -void Editor::on_identifier_click(const GUI::TextDocumentSpan& span) -{ - if (!m_language_client) - return; - - m_language_client->on_declaration_found = [](ByteString const& file, size_t line, size_t column) { - HackStudio::open_file(file, line, column); - }; - m_language_client->search_declaration(code_document().file_path(), span.range.start().line(), span.range.start().column()); -} - -void Editor::set_syntax_highlighter_for(CodeDocument const& document) -{ - if (!document.language().has_value()) { - set_syntax_highlighter({}); - force_rehighlight(); - return; - } - - switch (document.language().value()) { - case Syntax::Language::Cpp: - if (m_use_semantic_syntax_highlighting) { - set_syntax_highlighter(make()); - on_token_info_timer_tick(); - m_tokens_info_timer->restart(); - } else { - set_syntax_highlighter(make()); - } - break; - case Syntax::Language::CMake: - set_syntax_highlighter(make()); - break; - case Syntax::Language::CMakeCache: - set_syntax_highlighter(make()); - break; - case Syntax::Language::CSS: - set_syntax_highlighter(make()); - break; - case Syntax::Language::GitCommit: - set_syntax_highlighter(make()); - break; - case Syntax::Language::GML: - set_syntax_highlighter(make()); - break; - case Syntax::Language::HTML: - set_syntax_highlighter(make()); - break; - case Syntax::Language::INI: - set_syntax_highlighter(make()); - break; - case Syntax::Language::JavaScript: - set_syntax_highlighter(make()); - break; - case Syntax::Language::Markdown: - set_syntax_highlighter(make()); - break; - case Syntax::Language::Shell: - set_syntax_highlighter(make()); - break; - case Syntax::Language::SQL: - set_syntax_highlighter(make()); - break; - default: - set_syntax_highlighter({}); - } - - force_rehighlight(); -} - -void Editor::set_autocomplete_provider_for(CodeDocument const& document) -{ - if (document.language() == Syntax::Language::GML) { - set_autocomplete_provider(make()); - } else { - set_autocomplete_provider({}); - } -} - -void Editor::set_language_client_for(CodeDocument const& document) -{ - if (m_language_client && m_language_client->language() == document.language()) - return; - - if (document.language() == Syntax::Language::Cpp) - m_language_client = get_language_client(project().root_path()); - - if (document.language() == Syntax::Language::Shell) - m_language_client = get_language_client(project().root_path()); - - if (m_language_client) { - m_language_client->on_tokens_info_result = [this](Vector const& tokens_info) { - on_tokens_info_result(tokens_info); - }; - } -} - -void Editor::keydown_event(GUI::KeyEvent& event) -{ - TextEditor::keydown_event(event); - - if (m_tooltip_role == TooltipRole::ParametersHint) { - s_tooltip_window->hide(); - m_tooltip_role.clear(); - } - - if (!event.shift() && !event.alt() && event.ctrl() && event.key() == KeyCode::Key_P) { - handle_function_parameters_hint_request(); - } - - m_tokens_info_timer->restart(); -} - -void Editor::handle_function_parameters_hint_request() -{ - if (!m_language_client) - return; - - m_language_client->on_function_parameters_hint_result = [this](Vector const& params, size_t argument_index) { - dbgln("on_function_parameters_hint_result"); - - StringBuilder html; - for (size_t i = 0; i < params.size(); ++i) { - if (i == argument_index) - html.append(""sv); - - html.appendff("{}", params[i]); - - if (i == argument_index) - html.append(""sv); - - if (i < params.size() - 1) - html.append(", "sv); - } - html.append(""sv); - - s_tooltip_page_view->load_html(html.to_byte_string()); - - auto cursor_rect = current_editor().cursor_content_rect().location().translated(screen_relative_rect().location()); - - Gfx::Rect content(cursor_rect.x(), cursor_rect.y(), s_tooltip_page_view->children_clip_rect().width(), s_tooltip_page_view->children_clip_rect().height()); - - m_tooltip_role = TooltipRole::ParametersHint; - s_tooltip_window->set_rect(0, 0, 280, 35); - s_tooltip_window->move_to(cursor_rect.x(), cursor_rect.y() - s_tooltip_window->height() - vertical_scrollbar().value()); - s_tooltip_window->show(); - }; - - m_language_client->get_parameters_hint( - code_document().file_path(), - cursor().line(), - cursor().column()); -} - -void Editor::set_debug_mode(bool enabled) -{ - m_move_execution_to_line_action->set_enabled(enabled); -} - -void Editor::on_token_info_timer_tick() -{ - if (!semantic_syntax_highlighting_is_enabled()) - return; - if (!m_language_client || !m_language_client->is_active_client()) - return; - - m_language_client->get_tokens_info(code_document().file_path()); -} - -void Editor::on_tokens_info_result(Vector const& tokens_info) -{ - auto highlighter = syntax_highlighter(); - if (highlighter && highlighter->is_cpp_semantic_highlighter()) { - auto& semantic_cpp_highlighter = verify_cast(*highlighter); - semantic_cpp_highlighter.update_tokens_info(tokens_info); - force_rehighlight(); - } -} - -void Editor::create_tokens_info_timer() -{ - static constexpr size_t token_info_timer_interval_ms = 1000; - m_tokens_info_timer = Core::Timer::create_repeating((int)token_info_timer_interval_ms, [this] { - on_token_info_timer_tick(); - m_tokens_info_timer->stop(); - }); - m_tokens_info_timer->start(); -} - -void Editor::set_semantic_syntax_highlighting(bool value) -{ - m_use_semantic_syntax_highlighting = value; - set_syntax_highlighter_for(code_document()); -} - -ErrorOr Editor::add_breakpoint(size_t line_number) -{ - if (!breakpoint_lines().contains_slow(line_number)) { - if (Debugger::the().change_breakpoint(wrapper().filename_title(), line_number, BreakpointChange::Added)) { - add_gutter_indicator(m_breakpoint_indicator_id, line_number); - TRY(breakpoint_lines().try_append(line_number)); - } - } - - return {}; -} - -void Editor::remove_breakpoint(size_t line_number) -{ - if (Debugger::the().change_breakpoint(wrapper().filename_title(), line_number, BreakpointChange::Removed)) { - remove_gutter_indicator(m_breakpoint_indicator_id, line_number); - breakpoint_lines().remove_first_matching([&](size_t line) { return line == line_number; }); - } -} - -ErrorOr Editor::update_git_diff_indicators() -{ - clear_gutter_indicators(m_git_diff_indicator_id); - - if (!wrapper().git_repo()) - return {}; - - Vector line_differences; - TRY(line_differences.try_ensure_capacity(document().line_count())); - for (auto i = 0u; i < document().line_count(); ++i) - line_differences.unchecked_append(CodeDocument::DiffType::None); - - for (auto const& hunk : wrapper().hunks()) { - auto start_line = hunk.location.new_range.start_line; - // Account for 1 indexed hunk location - if (start_line != 0) - start_line--; - auto finish_line = start_line + hunk.location.new_range.number_of_lines; - - auto additions = hunk.location.new_range.number_of_lines; - auto deletions = hunk.location.old_range.number_of_lines; - - for (size_t line_offset = 0; line_offset < additions; line_offset++) { - auto line = start_line + line_offset; - auto difference = (line_offset < deletions) ? CodeDocument::DiffType::ModifiedLine : CodeDocument::DiffType::AddedLine; - line_differences[line] = difference; - add_gutter_indicator(m_git_diff_indicator_id, line); - } - if (additions < deletions) { - auto deletions_line = min(finish_line, line_count() - 1); - line_differences[deletions_line] = CodeDocument::DiffType::DeletedLinesBefore; - add_gutter_indicator(m_git_diff_indicator_id, deletions_line); - } - } - code_document().set_line_differences({}, move(line_differences)); - update(); - - return {}; -} - -} diff --git a/Userland/DevTools/HackStudio/Editor.h b/Userland/DevTools/HackStudio/Editor.h deleted file mode 100644 index db60d41e9d1..00000000000 --- a/Userland/DevTools/HackStudio/Editor.h +++ /dev/null @@ -1,127 +0,0 @@ -/* - * Copyright (c) 2018-2021, Andreas Kling - * Copyright (c) 2020-2022, Itamar S. - * Copyright (c) 2018-2022, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include "CodeDocument.h" -#include "Debugger/BreakpointCallback.h" -#include "LanguageClient.h" -#include -#include -#include -#include -#include - -namespace HackStudio { - -class EditorWrapper; - -class Editor final : public GUI::TextEditor { - C_OBJECT_ABSTRACT(Editor) -public: - static ErrorOr> try_create(); - - virtual ~Editor() override = default; - - Function on_open; - - EditorWrapper& wrapper(); - EditorWrapper const& wrapper() const; - - Vector const& breakpoint_lines() const { return code_document().breakpoint_lines(); } - Vector& breakpoint_lines() { return code_document().breakpoint_lines(); } - ErrorOr add_breakpoint(size_t line_number); - void remove_breakpoint(size_t line_number); - Optional execution_position() const { return code_document().execution_position(); } - bool is_program_running() const { return execution_position().has_value(); } - void set_execution_position(size_t line_number); - void clear_execution_position(); - void set_debug_mode(bool); - - ErrorOr update_git_diff_indicators(); - - CodeDocument const& code_document() const; - CodeDocument& code_document(); - - virtual void set_document(GUI::TextDocument&) override; - virtual void after_execute(GUI::TextDocumentUndoCommand const&) override; - - virtual void undo() override; - virtual void redo() override; - - LanguageClient& language_client() - { - VERIFY(m_language_client); - return *m_language_client; - } - void set_semantic_syntax_highlighting(bool value); - -private: - virtual void paint_event(GUI::PaintEvent&) override; - virtual void mousemove_event(GUI::MouseEvent&) override; - virtual void mousedown_event(GUI::MouseEvent&) override; - virtual void drag_enter_event(GUI::DragEvent&) override; - virtual void drop_event(GUI::DropEvent&) override; - virtual void enter_event(Core::Event&) override; - virtual void leave_event(Core::Event&) override; - virtual void keydown_event(GUI::KeyEvent&) override; - - void show_documentation_tooltip_if_available(ByteString const&, Gfx::IntPoint screen_location); - void navigate_to_include_if_available(ByteString); - void on_navigatable_link_click(const GUI::TextDocumentSpan&); - void on_identifier_click(const GUI::TextDocumentSpan&); - - static Gfx::Bitmap const& breakpoint_icon_bitmap(); - static Gfx::Bitmap const& current_position_icon_bitmap(); - static ErrorOr initialize_tooltip_window(); - - struct AutoCompleteRequestData { - GUI::TextPosition position; - }; - - class LanguageServerAidedAutocompleteProvider final : virtual public GUI::AutocompleteProvider { - public: - LanguageServerAidedAutocompleteProvider(LanguageClient& client) - : m_language_client(client) - { - } - virtual ~LanguageServerAidedAutocompleteProvider() override { } - - private: - virtual void provide_completions(Function)> callback) override; - LanguageClient& m_language_client; - }; - - Optional get_autocomplete_request_data(); - - void flush_file_content_to_langauge_server(); - void set_syntax_highlighter_for(CodeDocument const&); - void set_language_client_for(CodeDocument const&); - void set_autocomplete_provider_for(CodeDocument const&); - void handle_function_parameters_hint_request(); - void on_token_info_timer_tick(); - void on_tokens_info_result(Vector const& tokens_info); - void create_tokens_info_timer(); - - explicit Editor(); - - ByteString m_last_parsed_token; - GUI::TextPosition m_previous_text_position { 0, 0 }; - bool m_hovering_editor { false }; - bool m_hovering_clickable { false }; - RefPtr m_move_execution_to_line_action; - RefPtr m_tokens_info_timer; // Used for querying language server for syntax highlighting info - OwnPtr m_language_client; - bool m_use_semantic_syntax_highlighting { false }; - - GutterIndicatorID m_breakpoint_indicator_id; - GutterIndicatorID m_execution_indicator_id; - GutterIndicatorID m_git_diff_indicator_id; -}; - -} diff --git a/Userland/DevTools/HackStudio/EditorWrapper.cpp b/Userland/DevTools/HackStudio/EditorWrapper.cpp deleted file mode 100644 index 9077b415371..00000000000 --- a/Userland/DevTools/HackStudio/EditorWrapper.cpp +++ /dev/null @@ -1,137 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * Copyright (c) 2022, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "EditorWrapper.h" -#include "Editor.h" -#include "HackStudio.h" -#include -#include -#include -#include -#include -#include -#include - -namespace HackStudio { - -EditorWrapper::EditorWrapper() -{ - set_layout(); - m_filename_title = untitled_label; - - // FIXME: Propagate errors instead of giving up - m_editor = MUST(Editor::try_create()); - - add_child(*m_editor); - m_editor->set_ruler_visible(true); - m_editor->set_automatic_indentation_enabled(true); - - m_editor->on_focusin = [this] { - set_current_editor_wrapper(this); - }; - - m_editor->on_open = [](ByteString const& path) { - open_file(path); - }; - - m_editor->on_modified_change = [this](bool) { - update_title(); - update_editor_window_title(); - }; -} - -LanguageClient& EditorWrapper::language_client() { return m_editor->language_client(); } - -void EditorWrapper::set_mode_displayable() -{ - editor().set_mode(GUI::TextEditor::Editable); - editor().set_background_role(Gfx::ColorRole::Base); - auto palette = GUI::Application::the()->palette(); - editor().set_palette(palette); -} - -void EditorWrapper::set_mode_non_displayable() -{ - editor().set_mode(GUI::TextEditor::ReadOnly); - editor().set_background_role(Gfx::ColorRole::InactiveSelection); - auto palette = editor().palette(); - palette.set_color(Gfx::ColorRole::BaseText, Color::from_rgb(0xffffff)); - editor().set_palette(palette); - editor().document().set_text("The contents of this file could not be displayed. Is it a binary file?"sv); -} - -void EditorWrapper::set_filename(ByteString const& filename) -{ - m_filename = filename; - update_title(); - update_diff(); -} - -bool EditorWrapper::save() -{ - if (filename().is_empty()) { - auto file_picker_action = GUI::CommonActions::make_save_as_action([&](auto&) { - Optional save_path = GUI::FilePicker::get_save_filepath(window(), "file"sv, "txt"sv, project_root().value()); - if (save_path.has_value()) - set_filename(save_path.value()); - }); - file_picker_action->activate(); - - if (filename().is_empty()) - return false; - } - editor().write_to_file(filename()).release_value_but_fixme_should_propagate_errors(); - update_diff(); - editor().update(); - - return true; -} - -void EditorWrapper::update_diff() -{ - if (m_git_repo) { - m_hunks = Diff::parse_hunks(m_git_repo->unstaged_diff(filename()).value()).release_value_but_fixme_should_propagate_errors(); - editor().update_git_diff_indicators().release_value_but_fixme_should_propagate_errors(); - } -} - -void EditorWrapper::set_project_root(ByteString const& project_root) -{ - m_project_root = project_root; - auto result = GitRepo::try_to_create(*m_project_root); - switch (result.type) { - case GitRepo::CreateResult::Type::Success: - m_git_repo = result.repo; - break; - case GitRepo::CreateResult::Type::GitProgramNotFound: - break; - case GitRepo::CreateResult::Type::NoGitRepo: - break; - default: - VERIFY_NOT_REACHED(); - } -} - -void EditorWrapper::update_title() -{ - StringBuilder title; - if (m_filename.is_empty()) - title.append(untitled_label); - else - title.append(m_filename); - - if (editor().document().is_modified()) - title.append(" (*)"sv); - m_filename_title = title.to_byte_string(); -} - -void EditorWrapper::set_debug_mode(bool enabled) -{ - m_editor->set_debug_mode(enabled); -} - -} diff --git a/Userland/DevTools/HackStudio/EditorWrapper.h b/Userland/DevTools/HackStudio/EditorWrapper.h deleted file mode 100644 index fcbe23d523c..00000000000 --- a/Userland/DevTools/HackStudio/EditorWrapper.h +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * Copyright (c) 2022, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include "Debugger/BreakpointCallback.h" -#include "Git/GitRepo.h" -#include "LanguageClient.h" -#include -#include -#include -#include -#include -#include - -namespace HackStudio { - -class Editor; - -class EditorWrapper : public GUI::Widget { - C_OBJECT(EditorWrapper) - -public: - virtual ~EditorWrapper() override = default; - - Editor& editor() { return *m_editor; } - Editor const& editor() const { return *m_editor; } - - bool save(); - - LanguageClient& language_client(); - - void set_mode_displayable(); - void set_mode_non_displayable(); - void set_debug_mode(bool); - void set_filename(ByteString const&); - ByteString const& filename() const { return m_filename; } - ByteString const& filename_title() const { return m_filename_title; } - - Optional const& project_root() const { return m_project_root; } - void set_project_root(ByteString const& project_root); - - GitRepo const* git_repo() const { return m_git_repo; } - - void update_diff(); - Vector const& hunks() const { return m_hunks; } - - Function on_change; - Function on_tab_close_request; - -private: - static constexpr auto untitled_label = "(Untitled)"sv; - - EditorWrapper(); - - void update_title(); - - ByteString m_filename; - ByteString m_filename_title; - RefPtr m_editor; - - Optional m_project_root; - RefPtr m_git_repo; - Vector m_hunks; -}; - -} diff --git a/Userland/DevTools/HackStudio/FindInFilesWidget.cpp b/Userland/DevTools/HackStudio/FindInFilesWidget.cpp deleted file mode 100644 index 8c25aef260e..00000000000 --- a/Userland/DevTools/HackStudio/FindInFilesWidget.cpp +++ /dev/null @@ -1,156 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "FindInFilesWidget.h" -#include "HackStudio.h" -#include "Project.h" -#include -#include -#include -#include -#include -#include - -namespace HackStudio { - -struct Match { - ByteString filename; - GUI::TextRange range; - ByteString text; -}; - -class SearchResultsModel final : public GUI::Model { -public: - enum Column { - Filename, - Location, - MatchedText, - __Count - }; - - explicit SearchResultsModel(Vector const&& matches) - : m_matches(move(matches)) - { - } - - virtual int row_count(const GUI::ModelIndex& = GUI::ModelIndex()) const override { return m_matches.size(); } - virtual int column_count(const GUI::ModelIndex& = GUI::ModelIndex()) const override { return Column::__Count; } - - virtual ErrorOr column_name(int column) const override - { - switch (column) { - case Column::Filename: - return "Filename"_string; - case Column::Location: - return "#"_string; - case Column::MatchedText: - return "Text"_string; - default: - VERIFY_NOT_REACHED(); - } - } - - virtual GUI::Variant data(const GUI::ModelIndex& index, GUI::ModelRole role) const override - { - if (role == GUI::ModelRole::TextAlignment) - return Gfx::TextAlignment::CenterLeft; - if (role == GUI::ModelRole::Font) { - if (index.column() == Column::MatchedText) - return Gfx::FontDatabase::default_fixed_width_font(); - return {}; - } - if (role == GUI::ModelRole::Display) { - auto& match = m_matches.at(index.row()); - switch (index.column()) { - case Column::Filename: - return match.filename; - case Column::Location: - return (int)match.range.start().line(); - case Column::MatchedText: - return match.text; - } - } - return {}; - } - - virtual GUI::ModelIndex index(int row, int column = 0, const GUI::ModelIndex& = GUI::ModelIndex()) const override - { - if (row < 0 || row >= (int)m_matches.size()) - return {}; - if (column < 0 || column >= Column::__Count) - return {}; - return create_index(row, column, &m_matches.at(row)); - } - -private: - Vector m_matches; -}; - -static RefPtr find_in_files(StringView text) -{ - Vector matches; - project().for_each_text_file([&](auto& file) { - auto matches_in_file = file.document().find_all(text); - for (auto& range : matches_in_file) { - auto whole_line_range = file.document().range_for_entire_line(range.start().line()); - auto whole_line_containing_match = file.document().text_in_range(whole_line_range); - auto left_part = whole_line_containing_match.substring(0, range.start().column()); - auto right_part = whole_line_containing_match.substring(range.end().column(), whole_line_containing_match.length() - range.end().column()); - StringBuilder builder; - builder.append(left_part); - builder.append(0x01); - builder.append(file.document().text_in_range(range)); - builder.append(0x02); - builder.append(right_part); - matches.append({ file.name(), range, builder.to_byte_string() }); - } - }); - - return adopt_ref(*new SearchResultsModel(move(matches))); -} - -FindInFilesWidget::FindInFilesWidget() -{ - set_layout(); - - auto& top_container = add(); - top_container.set_layout(); - top_container.set_fixed_height(22); - - m_textbox = top_container.add(); - - m_button = top_container.add("Find"_string); - m_button->set_fixed_width(50); - - m_result_view = add(); - - m_result_view->on_activation = [](auto& index) { - auto& match = *(Match const*)index.internal_data(); - open_file(match.filename); - current_editor().set_selection(match.range); - current_editor().set_focus(true); - }; - - m_button->on_click = [this](auto) { - auto results_model = find_in_files(m_textbox->text()); - m_result_view->set_model(results_model); - }; - m_textbox->on_return_pressed = [this] { - m_button->click(); - }; -} - -void FindInFilesWidget::focus_textbox_and_select_all() -{ - m_textbox->select_all(); - m_textbox->set_focus(true); -} -void FindInFilesWidget::reset() -{ - m_result_view->set_model(nullptr); -} - -} diff --git a/Userland/DevTools/HackStudio/FindInFilesWidget.h b/Userland/DevTools/HackStudio/FindInFilesWidget.h deleted file mode 100644 index 38fd360efe5..00000000000 --- a/Userland/DevTools/HackStudio/FindInFilesWidget.h +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include - -namespace HackStudio { - -class FindInFilesWidget final : public GUI::Widget { - C_OBJECT(FindInFilesWidget) -public: - virtual ~FindInFilesWidget() override { } - - void focus_textbox_and_select_all(); - - void reset(); - -private: - explicit FindInFilesWidget(); - - RefPtr m_textbox; - RefPtr m_button; - RefPtr m_result_view; -}; - -} diff --git a/Userland/DevTools/HackStudio/GMLPreviewWidget.cpp b/Userland/DevTools/HackStudio/GMLPreviewWidget.cpp deleted file mode 100644 index 94e3e180545..00000000000 --- a/Userland/DevTools/HackStudio/GMLPreviewWidget.cpp +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright (c) 2021, Conor Byrne - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "GMLPreviewWidget.h" -#include -#include - -namespace HackStudio { - -GMLPreviewWidget::GMLPreviewWidget(ByteString const& gml_content) -{ - set_layout(); - load_gml(gml_content); -} - -void GMLPreviewWidget::load_gml(ByteString const& gml) -{ - remove_all_children(); - - if (gml.is_empty()) { - auto& label = add(); - label.set_text("Open a .gml file to show the preview"_string); - - return; - } - - // FIXME: Parsing errors happen while the user is typing. What should we do about them? - (void)load_from_gml(gml, [](StringView name) -> ErrorOr> { - return GUI::Label::construct(TRY(String::formatted("{} is not registered as a GML element!", name))); - }); - - if (children().is_empty()) { - auto& label = add(); - label.set_text("Failed to load GML!"_string); - } -} - -} diff --git a/Userland/DevTools/HackStudio/GMLPreviewWidget.h b/Userland/DevTools/HackStudio/GMLPreviewWidget.h deleted file mode 100644 index 17646b0fbc0..00000000000 --- a/Userland/DevTools/HackStudio/GMLPreviewWidget.h +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright (c) 2021, Conor Byrne - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include - -namespace HackStudio { - -class GMLPreviewWidget final : public GUI::Widget { - C_OBJECT(GMLPreviewWidget) -public: - void load_gml(ByteString const&); - -private: - explicit GMLPreviewWidget(ByteString const&); -}; - -} diff --git a/Userland/DevTools/HackStudio/Git/DiffViewer.cpp b/Userland/DevTools/HackStudio/Git/DiffViewer.cpp deleted file mode 100644 index 7e5e0e53b18..00000000000 --- a/Userland/DevTools/HackStudio/Git/DiffViewer.cpp +++ /dev/null @@ -1,232 +0,0 @@ -/* - * Copyright (c) 2020, Itamar S. - * Copyright (c) 2022, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "DiffViewer.h" -#include -#include -#include -#include -#include -#include -#include -#include - -namespace HackStudio { - -void DiffViewer::paint_event(GUI::PaintEvent& event) -{ - GUI::Painter painter(*this); - painter.add_clip_rect(widget_inner_rect()); - painter.add_clip_rect(event.rect()); - painter.fill_rect(event.rect(), palette().color(background_role())); - painter.translate(frame_thickness(), frame_thickness()); - painter.translate(-horizontal_scrollbar().value(), -vertical_scrollbar().value()); - - // Why we need to translate here again? We've already translated the painter. - // Anyways, it paints correctly so I'll leave it like this. - painter.fill_rect_with_dither_pattern( - separator_rect().translated(horizontal_scrollbar().value(), vertical_scrollbar().value()), - Gfx::Color::LightGray, - Gfx::Color::White); - - size_t y_offset = 10; - size_t current_original_line_index = 0; - for (auto const& hunk : m_hunks) { - for (size_t i = current_original_line_index; i < hunk.location.old_range.start_line; ++i) { - draw_line(painter, m_original_lines[i], y_offset, LinePosition::Both, LineType::Normal); - y_offset += line_height(); - } - current_original_line_index = hunk.location.old_range.start_line + hunk.location.old_range.number_of_lines; - - size_t left_y_offset = y_offset; - for (auto const& line : hunk.lines) { - if (line.operation != Diff::Line::Operation::Removal) - continue; - draw_line(painter, line.content, left_y_offset, LinePosition::Left, LineType::Diff); - left_y_offset += line_height(); - } - for (int i = 0; i < (int)hunk.location.new_range.number_of_lines - (int)hunk.location.old_range.number_of_lines; ++i) { - draw_line(painter, ""sv, left_y_offset, LinePosition::Left, LineType::Missing); - left_y_offset += line_height(); - } - - size_t right_y_offset = y_offset; - for (auto const& line : hunk.lines) { - if (line.operation != Diff::Line::Operation::Addition) - continue; - draw_line(painter, line.content, right_y_offset, LinePosition::Right, LineType::Diff); - right_y_offset += line_height(); - } - for (int i = 0; i < (int)hunk.location.old_range.number_of_lines - (int)hunk.location.new_range.number_of_lines; ++i) { - draw_line(painter, ""sv, right_y_offset, LinePosition::Right, LineType::Missing); - right_y_offset += line_height(); - } - - VERIFY(left_y_offset == right_y_offset); - y_offset = left_y_offset; - } - for (size_t i = current_original_line_index; i < m_original_lines.size(); ++i) { - draw_line(painter, m_original_lines[i], y_offset, LinePosition::Both, LineType::Normal); - y_offset += line_height(); - } -} - -void DiffViewer::draw_line(GUI::Painter& painter, StringView line, size_t y_offset, LinePosition line_position, LineType line_type) -{ - size_t line_width = font().width(line); - - constexpr size_t padding = 10; - size_t left_side_x_offset = padding; - size_t right_side_x_offset = separator_rect().x() + padding; - - // FIXME: Long lines will overflow out of their side of the diff view - Gfx::IntRect left_line_rect { (int)left_side_x_offset, (int)y_offset, (int)line_width, (int)line_height() }; - Gfx::IntRect right_line_rect { (int)right_side_x_offset, (int)y_offset, (int)line_width, (int)line_height() }; - auto color = palette().color(foreground_role()); - - if (line_position == LinePosition::Left || line_position == LinePosition::Both) { - painter.draw_text(left_line_rect, line, Gfx::TextAlignment::TopLeft, color); - if (line_type != LineType::Normal) { - Gfx::IntRect outline = { (int)left_side_x_offset, ((int)y_offset) - 2, separator_rect().x() - (int)(padding * 2), (int)line_height() }; - if (line_type == LineType::Diff) { - painter.fill_rect( - outline, - red_background()); - } - if (line_type == LineType::Missing) { - painter.fill_rect( - outline, - gray_background()); - } - } - } - if (line_position == LinePosition::Right || line_position == LinePosition::Both) { - painter.draw_text(right_line_rect, line, Gfx::TextAlignment::TopLeft, color); - if (line_type != LineType::Normal) { - Gfx::IntRect outline = { (int)right_side_x_offset, ((int)y_offset) - 2, frame_inner_rect().width() - separator_rect().x() - (int)(padding * 2) - 10, (int)line_height() }; - if (line_type == LineType::Diff) { - painter.fill_rect( - outline, - green_background()); - } - if (line_type == LineType::Missing) { - painter.fill_rect( - outline, - gray_background()); - } - } - } -} - -size_t DiffViewer::line_height() const -{ - return font().pixel_size_rounded_up() + 4; -} - -Gfx::IntRect DiffViewer::separator_rect() const -{ - return Gfx::IntRect { frame_inner_rect().width() / 2 - 2, - 0, - 4, - frame_inner_rect().height() }; -} - -void DiffViewer::set_content(ByteString const& original, ByteString const& diff) -{ - m_original_lines = split_to_lines(original); - m_hunks = Diff::parse_hunks(diff).release_value_but_fixme_should_propagate_errors(); - - if constexpr (DIFF_DEBUG) { - for (size_t i = 0; i < m_original_lines.size(); ++i) - dbgln("{}:{}", i, m_original_lines[i]); - } -} - -DiffViewer::DiffViewer() -{ - setup_properties(); -} - -DiffViewer::DiffViewer(ByteString const& original, ByteString const& diff) - : m_original_lines(split_to_lines(original)) - , m_hunks(Diff::parse_hunks(diff).release_value_but_fixme_should_propagate_errors()) -{ - setup_properties(); -} - -void DiffViewer::setup_properties() -{ - set_font(Gfx::FontDatabase::default_fixed_width_font()); - set_background_role(ColorRole::Base); - set_foreground_role(ColorRole::BaseText); -} - -Vector DiffViewer::split_to_lines(ByteString const& text) -{ - // NOTE: This is slightly different than text.split('\n') - Vector lines; - size_t next_line_start_index = 0; - for (size_t i = 0; i < text.length(); ++i) { - if (text[i] == '\n') { - auto line_text = text.substring(next_line_start_index, i - next_line_start_index); - lines.append(move(line_text)); - next_line_start_index = i + 1; - } - } - lines.append(text.substring(next_line_start_index, text.length() - next_line_start_index)); - return lines; -} - -Gfx::Color DiffViewer::red_background() -{ - static Gfx::Color color = Gfx::Color::from_argb(0x88ff0000); - return color; -} - -Gfx::Color DiffViewer::green_background() -{ - static Gfx::Color color = Gfx::Color::from_argb(0x8800ff00); - return color; -} - -Gfx::Color DiffViewer::gray_background() -{ - static Gfx::Color color = Gfx::Color::from_argb(0x88888888); - return color; -} - -void DiffViewer::update_content_size() -{ - if (m_hunks.is_empty()) { - set_content_size({ 0, 0 }); - return; - } - - size_t num_lines = 0; - size_t current_original_line_index = 0; - for (auto const& hunk : m_hunks) { - num_lines += (hunk.location.old_range.start_line - (int)current_original_line_index); - - num_lines += hunk.location.old_range.number_of_lines; - if (hunk.location.new_range.number_of_lines > hunk.location.old_range.number_of_lines) { - num_lines += ((int)hunk.location.new_range.number_of_lines - (int)hunk.location.old_range.number_of_lines); - } - current_original_line_index = hunk.location.old_range.start_line + hunk.location.old_range.number_of_lines; - } - num_lines += ((int)m_original_lines.size() - (int)current_original_line_index); - - // TODO: Support Horizontal scrolling - set_content_size({ 0, (int)(num_lines * line_height()) }); -} - -void DiffViewer::resize_event(GUI::ResizeEvent& event) -{ - AbstractScrollableWidget::resize_event(event); - update_content_size(); -} - -} diff --git a/Userland/DevTools/HackStudio/Git/DiffViewer.h b/Userland/DevTools/HackStudio/Git/DiffViewer.h deleted file mode 100644 index 827c7397ea7..00000000000 --- a/Userland/DevTools/HackStudio/Git/DiffViewer.h +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright (c) 2020, Itamar S. - * Copyright (c) 2022, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include - -namespace HackStudio { -class DiffViewer final : public GUI::AbstractScrollableWidget { - C_OBJECT(DiffViewer) -public: - virtual ~DiffViewer() override = default; - - void set_content(ByteString const& original, ByteString const& diff); - -private: - DiffViewer(ByteString const& original, ByteString const& diff); - DiffViewer(); - - void setup_properties(); - - virtual void paint_event(GUI::PaintEvent&) override; - virtual void resize_event(GUI::ResizeEvent&) override; - - void update_content_size(); - - enum class LinePosition { - Left, - Right, - Both, - }; - - enum class LineType { - Normal, - Diff, - Missing, - }; - - void draw_line(GUI::Painter&, StringView line, size_t y_offset, LinePosition, LineType); - - static Vector split_to_lines(ByteString const& text); - - static Gfx::Color red_background(); - static Gfx::Color green_background(); - static Gfx::Color gray_background(); - - size_t line_height() const; - - Gfx::IntRect separator_rect() const; - - Vector m_original_lines; - Vector m_hunks; -}; -} diff --git a/Userland/DevTools/HackStudio/Git/GitFilesModel.cpp b/Userland/DevTools/HackStudio/Git/GitFilesModel.cpp deleted file mode 100644 index 7311d874b16..00000000000 --- a/Userland/DevTools/HackStudio/Git/GitFilesModel.cpp +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright (c) 2020, Itamar S. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "GitFilesModel.h" - -namespace HackStudio { - -NonnullRefPtr GitFilesModel::create(Vector&& files) -{ - return adopt_ref(*new GitFilesModel(move(files))); -} - -GitFilesModel::GitFilesModel(Vector&& files) - : m_files(move(files)) -{ -} - -GUI::Variant GitFilesModel::data(const GUI::ModelIndex& index, GUI::ModelRole role) const -{ - if (role == GUI::ModelRole::Display) { - return m_files.at(index.row()); - } - return {}; -} - -GUI::ModelIndex GitFilesModel::index(int row, int column, const GUI::ModelIndex&) const -{ - if (row < 0 || row >= static_cast(m_files.size())) - return {}; - return create_index(row, column, &m_files.at(row)); -} - -}; diff --git a/Userland/DevTools/HackStudio/Git/GitFilesModel.h b/Userland/DevTools/HackStudio/Git/GitFilesModel.h deleted file mode 100644 index 3a91bcb1b54..00000000000 --- a/Userland/DevTools/HackStudio/Git/GitFilesModel.h +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright (c) 2020, Itamar S. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include "GitRepo.h" -#include -#include - -namespace HackStudio { - -class GitFilesModel final : public GUI::Model { -public: - static NonnullRefPtr create(Vector&& files); - - virtual int row_count(const GUI::ModelIndex& = GUI::ModelIndex()) const override { return m_files.size(); } - virtual int column_count(const GUI::ModelIndex& = GUI::ModelIndex()) const override { return 1; } - - virtual ErrorOr column_name(int) const override { return String {}; } - - virtual GUI::Variant data(const GUI::ModelIndex&, GUI::ModelRole) const override; - - virtual GUI::ModelIndex index(int row, int column, const GUI::ModelIndex&) const override; - -private: - explicit GitFilesModel(Vector&& files); - Vector m_files; -}; -} diff --git a/Userland/DevTools/HackStudio/Git/GitFilesView.cpp b/Userland/DevTools/HackStudio/Git/GitFilesView.cpp deleted file mode 100644 index bff70cee402..00000000000 --- a/Userland/DevTools/HackStudio/Git/GitFilesView.cpp +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright (c) 2020, Itamar S. - * Copyright (c) 2022, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "GitFilesView.h" -#include -#include -#include -#include - -namespace HackStudio { - -void GitFilesView::paint_list_item(GUI::Painter& painter, int row_index, int painted_item_index) -{ - ListView::paint_list_item(painter, row_index, painted_item_index); - - painter.blit(action_icon_rect((size_t)painted_item_index).top_left(), *m_action_icon, m_action_icon->rect()); -} - -Gfx::IntRect GitFilesView::action_icon_rect(size_t painted_item_index) -{ - int y = painted_item_index * item_height(); - return { content_width() - 20, y, m_action_icon->rect().width(), m_action_icon->rect().height() }; -} - -GitFilesView::GitFilesView(GitFileActionCallback callback, NonnullRefPtr action_icon) - : m_action_callback(move(callback)) - , m_action_icon(action_icon) -{ - set_alternating_row_colors(false); -} - -void GitFilesView::mousedown_event(GUI::MouseEvent& event) -{ - if (event.button() != GUI::MouseButton::Primary) { - ListView::mousedown_event(event); - return; - } - - if (event.x() < action_icon_rect(0).x() || event.x() >= action_icon_rect(0).right()) { - ListView::mousedown_event(event); - return; - } - - size_t item_index = (event.y() + vertical_scrollbar().value()) / item_height(); - if (model()->row_count() == 0 || item_index > (size_t)model()->row_count()) { - ListView::mousedown_event(event); - return; - } - - auto data = model()->index(item_index, model_column()).data(); - - VERIFY(data.is_string()); - m_action_callback(data.to_byte_string()); -} - -}; diff --git a/Userland/DevTools/HackStudio/Git/GitFilesView.h b/Userland/DevTools/HackStudio/Git/GitFilesView.h deleted file mode 100644 index c60e076ef64..00000000000 --- a/Userland/DevTools/HackStudio/Git/GitFilesView.h +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright (c) 2020, Itamar S. - * Copyright (c) 2022, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include - -namespace HackStudio { - -// A "GitFileAction" is either the staging or the unstaging of a file. -using GitFileActionCallback = Function; - -class GitFilesView : public GUI::ListView { - C_OBJECT(GitFilesView) -public: - virtual ~GitFilesView() override = default; - -protected: - GitFilesView(GitFileActionCallback, NonnullRefPtr action_icon); - -private: - virtual void paint_list_item(GUI::Painter& painter, int row_index, int painted_item_index) override; - - virtual void mousedown_event(GUI::MouseEvent&) override; - virtual Gfx::IntRect action_icon_rect(size_t painted_item_index); - - GitFileActionCallback m_action_callback; - NonnullRefPtr m_action_icon; -}; - -} diff --git a/Userland/DevTools/HackStudio/Git/GitRepo.cpp b/Userland/DevTools/HackStudio/Git/GitRepo.cpp deleted file mode 100644 index 34ec2e2904e..00000000000 --- a/Userland/DevTools/HackStudio/Git/GitRepo.cpp +++ /dev/null @@ -1,133 +0,0 @@ -/* - * Copyright (c) 2020, Itamar S. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "GitRepo.h" -#include - -namespace HackStudio { - -GitRepo::CreateResult GitRepo::try_to_create(ByteString const& repository_root) -{ - if (!git_is_installed()) { - return { CreateResult::Type::GitProgramNotFound, nullptr }; - } - if (!git_repo_exists(repository_root)) { - return { CreateResult::Type::NoGitRepo, nullptr }; - } - - return { CreateResult::Type::Success, adopt_ref(*new GitRepo(repository_root)) }; -} - -RefPtr GitRepo::initialize_repository(ByteString const& repository_root) -{ - auto res = command_wrapper({ "init" }, repository_root); - if (!res.has_value()) - return {}; - - VERIFY(git_repo_exists(repository_root)); - return adopt_ref(*new GitRepo(repository_root)); -} - -Vector GitRepo::unstaged_files() const -{ - auto modified = modified_files(); - auto untracked = untracked_files(); - modified.extend(move(untracked)); - return modified; -} -// -Vector GitRepo::staged_files() const -{ - auto raw_result = command({ "diff", "--cached", "--name-only" }); - if (!raw_result.has_value()) - return {}; - return parse_files_list(*raw_result); -} - -Vector GitRepo::modified_files() const -{ - auto raw_result = command({ "ls-files", "--modified", "--exclude-standard" }); - if (!raw_result.has_value()) - return {}; - return parse_files_list(*raw_result); -} - -Vector GitRepo::untracked_files() const -{ - auto raw_result = command({ "ls-files", "--others", "--exclude-standard" }); - if (!raw_result.has_value()) - return {}; - return parse_files_list(*raw_result); -} - -Vector GitRepo::parse_files_list(ByteString const& raw_result) -{ - auto lines = raw_result.split('\n'); - Vector files; - for (auto const& line : lines) { - files.empend(line); - } - return files; -} - -Optional GitRepo::command(Vector const& command_parts) const -{ - return command_wrapper(command_parts, m_repository_root); -} - -Optional GitRepo::command_wrapper(Vector const& command_parts, ByteString const& chdir) -{ - auto const result = Core::command("git", command_parts, LexicalPath(chdir)); - if (result.is_error() || result.value().exit_code != 0) - return {}; - return ByteString(result.value().output.bytes()); -} - -bool GitRepo::git_is_installed() -{ - return command_wrapper({ "--help" }, "/").has_value(); -} - -bool GitRepo::git_repo_exists(ByteString const& repo_root) -{ - return command_wrapper({ "status" }, repo_root).has_value(); -} - -bool GitRepo::stage(ByteString const& file) -{ - return command({ "add", file }).has_value(); -} - -bool GitRepo::unstage(ByteString const& file) -{ - return command({ "reset", "HEAD", "--", file }).has_value(); -} - -bool GitRepo::commit(ByteString const& message) -{ - return command({ "commit", "-m", message }).has_value(); -} - -Optional GitRepo::original_file_content(ByteString const& file) const -{ - return command({ "show", ByteString::formatted("HEAD:{}", file) }); -} - -Optional GitRepo::unstaged_diff(ByteString const& file) const -{ - return command({ "diff", "-U0", file.characters() }); -} - -bool GitRepo::is_tracked(ByteString const& file) const -{ - auto res = command({ "ls-files", file }); - if (!res.has_value()) - return false; - - return !res->is_empty(); -} - -} diff --git a/Userland/DevTools/HackStudio/Git/GitRepo.h b/Userland/DevTools/HackStudio/Git/GitRepo.h deleted file mode 100644 index ec04f96ad19..00000000000 --- a/Userland/DevTools/HackStudio/Git/GitRepo.h +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright (c) 2020, Itamar S. - * Copyright (c) 2021, Conor Byrne - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include -#include - -namespace HackStudio { - -class GitRepo final : public RefCounted { -public: - struct CreateResult { - enum class Type { - Success, - NoGitRepo, - GitProgramNotFound, - }; - Type type; - RefPtr repo; - }; - - static CreateResult try_to_create(ByteString const& repository_root); - static RefPtr initialize_repository(ByteString const& repository_root); - - bool stage(ByteString const& file); - bool unstage(ByteString const& file); - bool commit(ByteString const& message); - bool is_tracked(ByteString const& file) const; - - Vector unstaged_files() const; - Vector staged_files() const; - Optional original_file_content(ByteString const& file) const; - Optional unstaged_diff(ByteString const& file) const; - -private: - static bool git_is_installed(); - static bool git_repo_exists(ByteString const& repo_root); - - static Optional command_wrapper(Vector const& command_parts, ByteString const& chdir); - static Vector parse_files_list(ByteString const&); - - explicit GitRepo(ByteString const& repository_root) - : m_repository_root(repository_root) - { - } - - Vector modified_files() const; - Vector untracked_files() const; - - Optional command(Vector const& command_parts) const; - - ByteString m_repository_root; -}; - -} diff --git a/Userland/DevTools/HackStudio/Git/GitWidget.cpp b/Userland/DevTools/HackStudio/Git/GitWidget.cpp deleted file mode 100644 index d63f263828b..00000000000 --- a/Userland/DevTools/HackStudio/Git/GitWidget.cpp +++ /dev/null @@ -1,178 +0,0 @@ -/* - * Copyright (c) 2020, Itamar S. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "GitWidget.h" -#include "../Dialogs/Git/GitCommitDialog.h" -#include "GitFilesModel.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace HackStudio { - -GitWidget::GitWidget() -{ - set_layout(); - - auto& unstaged = add(); - unstaged.set_layout(); - auto& unstaged_header = unstaged.add(); - unstaged_header.set_layout(); - - auto& refresh_button = unstaged_header.add(); - refresh_button.set_icon(Gfx::Bitmap::load_from_file("/res/icons/16x16/reload.png"sv).release_value_but_fixme_should_propagate_errors()); - refresh_button.set_fixed_size(16, 16); - refresh_button.set_tooltip("refresh"_string); - refresh_button.on_click = [this](int) { refresh(); }; - - auto& unstaged_label = unstaged_header.add(); - unstaged_label.set_text("Unstaged"_string); - - unstaged_header.set_fixed_height(20); - m_unstaged_files = unstaged.add( - [this](auto const& file) { stage_file(file); }, - Gfx::Bitmap::load_from_file("/res/icons/16x16/plus.png"sv).release_value_but_fixme_should_propagate_errors()); - m_unstaged_files->on_selection_change = [this] { - auto const& index = m_unstaged_files->selection().first(); - if (!index.is_valid()) - return; - - auto const& selected = index.data().as_string(); - show_diff(selected); - }; - - m_unstaged_files->set_foreground_role(Gfx::ColorRole::Red); - - auto& staged = add(); - staged.set_layout(); - - auto& staged_header = staged.add(); - staged_header.set_layout(); - - auto& commit_button = staged_header.add(); - commit_button.set_icon(Gfx::Bitmap::load_from_file("/res/icons/16x16/commit.png"sv).release_value_but_fixme_should_propagate_errors()); - commit_button.set_fixed_size(16, 16); - commit_button.set_tooltip("commit"_string); - commit_button.on_click = [this](int) { commit(); }; - - auto& staged_label = staged_header.add(); - staged_label.set_text("Staged"_string); - - staged_header.set_fixed_height(20); - m_staged_files = staged.add( - [this](auto const& file) { unstage_file(file); }, - Gfx::Bitmap::load_from_file("/res/icons/16x16/minus.png"sv).release_value_but_fixme_should_propagate_errors()); - m_staged_files->set_foreground_role(Gfx::ColorRole::Green); -} - -bool GitWidget::initialize() -{ - auto result = GitRepo::try_to_create(m_repo_root); - switch (result.type) { - case GitRepo::CreateResult::Type::Success: - m_git_repo = result.repo; - return true; - case GitRepo::CreateResult::Type::GitProgramNotFound: - GUI::MessageBox::show(window(), "Please install the Git port"sv, "Error"sv, GUI::MessageBox::Type::Error); - return false; - case GitRepo::CreateResult::Type::NoGitRepo: { - auto decision = GUI::MessageBox::show(window(), "Create git repository?"sv, "Git"sv, GUI::MessageBox::Type::Question, GUI::MessageBox::InputType::YesNo); - if (decision != GUI::Dialog::ExecResult::Yes) - return false; - m_git_repo = GitRepo::initialize_repository(m_repo_root); - return true; - } - default: - VERIFY_NOT_REACHED(); - } -} - -bool GitWidget::initialize_if_needed() -{ - if (initialized()) - return true; - - return initialize(); -} - -void GitWidget::refresh() -{ - if (!initialize_if_needed()) { - dbgln("GitWidget initialization failed"); - return; - } - - VERIFY(!m_git_repo.is_null()); - - m_unstaged_files->set_model(GitFilesModel::create(m_git_repo->unstaged_files())); - m_staged_files->set_model(GitFilesModel::create(m_git_repo->staged_files())); -} - -void GitWidget::stage_file(ByteString const& file) -{ - dbgln("staging: {}", file); - bool rc = m_git_repo->stage(file); - VERIFY(rc); - refresh(); -} - -void GitWidget::unstage_file(ByteString const& file) -{ - dbgln("unstaging: {}", file); - bool rc = m_git_repo->unstage(file); - VERIFY(rc); - refresh(); -} - -void GitWidget::commit() -{ - if (m_git_repo.is_null()) { - GUI::MessageBox::show(window(), "There is no git repository to commit to!"sv, "Error"sv, GUI::MessageBox::Type::Error); - return; - } - - auto dialog = GitCommitDialog::construct(window()); - dialog->on_commit = [this](auto& message) { - m_git_repo->commit(message); - refresh(); - }; - dialog->exec(); -} - -void GitWidget::set_view_diff_callback(ViewDiffCallback callback) -{ - m_view_diff_callback = move(callback); -} - -void GitWidget::show_diff(ByteString const& file_path) -{ - if (!m_git_repo->is_tracked(file_path)) { - auto file = Core::File::open(file_path, Core::File::OpenMode::Read).release_value_but_fixme_should_propagate_errors(); - auto content = file->read_until_eof().release_value_but_fixme_should_propagate_errors(); - m_view_diff_callback("", Diff::generate_only_additions(content)); - return; - } - auto const& original_content = m_git_repo->original_file_content(file_path); - auto const& diff = m_git_repo->unstaged_diff(file_path); - VERIFY(original_content.has_value() && diff.has_value()); - m_view_diff_callback(original_content.value(), diff.value()); -} - -void GitWidget::change_repo(ByteString const& repo_root) -{ - m_repo_root = repo_root; - m_git_repo = nullptr; - m_unstaged_files->set_model(nullptr); - m_staged_files->set_model(nullptr); -} -} diff --git a/Userland/DevTools/HackStudio/Git/GitWidget.h b/Userland/DevTools/HackStudio/Git/GitWidget.h deleted file mode 100644 index 18c1e4860a9..00000000000 --- a/Userland/DevTools/HackStudio/Git/GitWidget.h +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright (c) 2020, Itamar S. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include "GitFilesView.h" -#include "GitRepo.h" -#include -#include -#include - -namespace HackStudio { - -using ViewDiffCallback = Function; - -class GitWidget final : public GUI::Widget { - C_OBJECT(GitWidget) -public: - virtual ~GitWidget() override { } - - void refresh(); - void set_view_diff_callback(ViewDiffCallback callback); - bool initialized() const { return !m_git_repo.is_null(); } - void change_repo(ByteString const& repo_root); - -private: - explicit GitWidget(); - - bool initialize(); - bool initialize_if_needed(); - void stage_file(ByteString const&); - void unstage_file(ByteString const&); - void commit(); - void show_diff(ByteString const&); - - ByteString m_repo_root; - RefPtr m_unstaged_files; - RefPtr m_staged_files; - RefPtr m_git_repo; - ViewDiffCallback m_view_diff_callback; -}; - -} diff --git a/Userland/DevTools/HackStudio/HackStudio.h b/Userland/DevTools/HackStudio/HackStudio.h deleted file mode 100644 index 6923981e8fe..00000000000 --- a/Userland/DevTools/HackStudio/HackStudio.h +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright (c) 2020, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include "EditorWrapper.h" -#include "LanguageClients/ConnectionsToServer.h" -#include "Project.h" -#include -#include - -namespace HackStudio { - -GUI::TextEditor& current_editor(); -void open_file(ByteString const&); -RefPtr current_editor_wrapper(); -void open_file(ByteString const&, size_t line, size_t column); -Project& project(); -ByteString currently_open_file(); -void set_current_editor_wrapper(RefPtr); -void update_editor_window_title(); -void for_each_open_file(Function); -bool semantic_syntax_highlighting_is_enabled(); - -class Locator; -Locator& locator(); - -} diff --git a/Userland/DevTools/HackStudio/HackStudioWidget.cpp b/Userland/DevTools/HackStudio/HackStudioWidget.cpp deleted file mode 100644 index 0b080ad6c7a..00000000000 --- a/Userland/DevTools/HackStudio/HackStudioWidget.cpp +++ /dev/null @@ -1,1935 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * Copyright (c) 2020-2022, Itamar S. - * Copyright (c) 2023-2024, Abhishek R. - * Copyright (c) 2020-2022, the SerenityOS developers. - * Copyright (c) 2024, Sam Atkins - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "HackStudioWidget.h" -#include "Debugger/DebugInfoWidget.h" -#include "Debugger/Debugger.h" -#include "Debugger/DisassemblyWidget.h" -#include "Dialogs/NewProjectDialog.h" -#include "Editor.h" -#include "EditorWrapper.h" -#include "FindInFilesWidget.h" -#include "Git/DiffViewer.h" -#include "Git/GitWidget.h" -#include "HackStudio.h" -#include "Locator.h" -#include "Project.h" -#include "ProjectDeclarations.h" -#include "TerminalWrapper.h" -#include "ToDoEntries.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace HackStudio { - -ErrorOr> HackStudioWidget::create(ByteString path_to_project) -{ - auto widget = TRY(adopt_nonnull_ref_or_enomem(new (nothrow) HackStudioWidget)); - - widget->m_editor_font = widget->read_editor_font_from_config(); - widget->set_fill_with_background_color(true); - widget->set_layout(GUI::Margins {}, 2); - - auto& toolbar_container = widget->add(); - - auto& outer_splitter = widget->add(); - outer_splitter.layout()->set_spacing(4); - - auto& left_hand_splitter = outer_splitter.add(); - left_hand_splitter.layout()->set_spacing(6); - left_hand_splitter.set_preferred_width(150); - - widget->m_project_tree_view_context_menu = TRY(widget->create_project_tree_view_context_menu()); - - widget->m_right_hand_splitter = outer_splitter.add(); - widget->m_right_hand_stack = widget->m_right_hand_splitter->add(); - - TRY(widget->create_action_tab(*widget->m_right_hand_splitter)); - - widget->open_project(path_to_project); - widget->create_project_tab(left_hand_splitter); - widget->create_open_files_view(left_hand_splitter); - - // Put a placeholder widget front & center since we don't have a file open yet. - widget->m_right_hand_stack->add(); - - widget->m_diff_viewer = widget->m_right_hand_stack->add(); - - widget->m_editors_splitter = widget->m_right_hand_stack->add(); - widget->m_editors_splitter->layout()->set_margins({ 3, 0, 0 }); - widget->add_new_editor_tab_widget(*widget->m_editors_splitter); - - widget->m_switch_to_next_editor_tab_widget = widget->create_switch_to_next_editor_tab_widget_action(); - widget->m_switch_to_next_editor = widget->create_switch_to_next_editor_action(); - widget->m_switch_to_previous_editor = widget->create_switch_to_previous_editor_action(); - - widget->m_remove_current_editor_tab_widget_action = widget->create_remove_current_editor_tab_widget_action(); - widget->m_remove_current_editor_action = TRY(widget->create_remove_current_editor_action()); - widget->m_open_action = TRY(widget->create_open_action()); - widget->m_save_action = widget->create_save_action(); - widget->m_save_as_action = widget->create_save_as_action(); - widget->m_new_project_action = TRY(widget->create_new_project_action()); - - widget->m_add_editor_tab_widget_action = widget->create_add_editor_tab_widget_action(); - widget->m_add_editor_action = TRY(widget->create_add_editor_action()); - widget->m_add_terminal_action = TRY(widget->create_add_terminal_action()); - widget->m_remove_current_terminal_action = TRY(widget->create_remove_current_terminal_action()); - - widget->m_locator = widget->add(); - - widget->m_terminal_wrapper->on_command_exit = [widget] { - widget->m_stop_action->set_enabled(false); - }; - - widget->m_open_project_configuration_action = TRY(widget->create_open_project_configuration_action()); - widget->m_build_action = TRY(widget->create_build_action()); - widget->m_run_action = TRY(widget->create_run_action()); - widget->m_stop_action = TRY(widget->create_stop_action()); - widget->m_debug_action = TRY(widget->create_debug_action()); - - widget->initialize_debugger(); - - widget->create_toolbar(toolbar_container); - - widget->m_statusbar = widget->add(3); - widget->m_statusbar->segment(1).set_mode(GUI::Statusbar::Segment::Mode::Auto); - widget->m_statusbar->segment(2).set_mode(GUI::Statusbar::Segment::Mode::Fixed); - auto width = widget->font().width("Ln 0,000 Col 000"sv) + widget->font().max_glyph_width(); - widget->m_statusbar->segment(2).set_fixed_width(width); - widget->update_statusbar(); - - GUI::Application::the()->on_action_enter = [widget](GUI::Action& action) { - widget->m_statusbar->set_override_text(action.status_tip()); - }; - - GUI::Application::the()->on_action_leave = [widget](GUI::Action&) { - widget->m_statusbar->set_override_text({}); - }; - - auto maybe_watcher = Core::FileWatcher::create(); - if (maybe_watcher.is_error()) { - warnln("Couldn't create a file watcher, deleted files won't be noticed! Error: {}", maybe_watcher.error()); - } else { - widget->m_file_watcher = maybe_watcher.release_value(); - widget->m_file_watcher->on_change = [widget](Core::FileWatcherEvent const& event) { - if (event.type != Core::FileWatcherEvent::Type::Deleted) - return; - - if (event.event_path.starts_with(widget->project().root_path())) { - ByteString relative_path = LexicalPath::relative_path(event.event_path, widget->project().root_path()); - widget->handle_external_file_deletion(relative_path); - } else { - widget->handle_external_file_deletion(event.event_path); - } - }; - } - - widget->project().model().set_should_show_dotfiles(Config::read_bool("HackStudio"sv, "Global"sv, "ShowDotfiles"sv, false)); - - return widget; -} - -void HackStudioWidget::update_actions() -{ - auto is_remove_terminal_enabled = [this]() { - auto widget = m_action_tab_widget->active_widget(); - if (!widget) - return false; - if ("TerminalWrapper"sv != widget->class_name()) - return false; - if (!reinterpret_cast(widget)->user_spawned()) - return false; - return true; - }; - - m_remove_current_editor_action->set_enabled(m_all_editor_wrappers.size() > 1); - m_remove_current_terminal_action->set_enabled(is_remove_terminal_enabled()); -} - -void HackStudioWidget::on_action_tab_change() -{ - update_actions(); - if (auto* active_widget = m_action_tab_widget->active_widget()) { - if (is(*active_widget)) - static_cast(*active_widget).refresh(); - } -} - -Vector HackStudioWidget::read_recent_projects() -{ - auto json = Config::read_string("HackStudio"sv, "Global"sv, "RecentProjects"sv); - AK::JsonParser parser(json); - auto value_or_error = parser.parse(); - if (value_or_error.is_error()) - return {}; - - auto value = value_or_error.release_value(); - if (!value.is_array()) - return {}; - - Vector paths; - for (auto& json_value : value.as_array().values()) { - if (!json_value.is_string()) - return {}; - paths.append(json_value.as_string()); - } - - return paths; -} - -void HackStudioWidget::open_project(ByteString const& root_path) -{ - if (warn_unsaved_changes("There are unsaved changes, do you want to save before closing current project?") == ContinueDecision::No) - return; - auto absolute_root_path = FileSystem::absolute_path(root_path).release_value_but_fixme_should_propagate_errors(); - if (auto result = Core::System::chdir(absolute_root_path); result.is_error()) { - warnln("Failed to open project: {}", result.release_error()); - exit(1); - } - if (m_project) { - close_current_project(); - } - m_project = Project::open_with_root_path(absolute_root_path); - VERIFY(m_project); - m_project_builder = make(*m_terminal_wrapper, *m_project); - if (m_project_tree_view) { - m_project_tree_view->set_model(m_project->model()); - m_project_tree_view->update(); - } - if (m_git_widget->initialized()) { - m_git_widget->change_repo(absolute_root_path); - m_git_widget->refresh(); - } - if (Debugger::is_initialized()) { - auto& debugger = Debugger::the(); - debugger.reset_breakpoints(); - debugger.set_source_root(m_project->root_path()); - } - for (auto& editor_wrapper : m_all_editor_wrappers) - editor_wrapper->set_project_root(m_project->root_path()); - - m_locations_history.clear(); - m_locations_history_end_index = 0; - - m_project->model().on_rename_successful = [this](auto& absolute_old_path, auto& absolute_new_path) { - file_renamed( - LexicalPath::relative_path(absolute_old_path, m_project->root_path()), - LexicalPath::relative_path(absolute_new_path, m_project->root_path())); - }; - - GUI::Application::the()->set_most_recently_open_file(absolute_root_path); -} - -Vector HackStudioWidget::selected_file_paths() const -{ - Vector files; - m_project_tree_view->selection().for_each_index([&](const GUI::ModelIndex& index) { - ByteString sub_path = index.data().as_string(); - - GUI::ModelIndex parent_or_invalid = index.parent(); - - while (parent_or_invalid.is_valid()) { - sub_path = ByteString::formatted("{}/{}", parent_or_invalid.data().as_string(), sub_path); - - parent_or_invalid = parent_or_invalid.parent(); - } - - files.append(sub_path); - }); - return files; -} - -bool HackStudioWidget::open_file(ByteString const& full_filename, size_t line, size_t column) -{ - ByteString filename = full_filename; - if (full_filename.starts_with(project().root_path())) { - filename = LexicalPath::relative_path(full_filename, project().root_path()); - } - if (FileSystem::is_directory(filename) || !FileSystem::exists(filename)) - return false; - - auto editor_wrapper_or_none = m_all_editor_wrappers.first_matching([&](auto& wrapper) { - return wrapper->filename() == filename; - }); - - if (editor_wrapper_or_none.has_value()) { - set_current_editor_wrapper(editor_wrapper_or_none.release_value()); - current_editor().set_cursor_and_focus_line(line, column); - return true; - } else if (active_file().is_empty() && !current_editor().document().is_modified() && !full_filename.is_empty()) { - // Replace "Untitled" blank file since it hasn't been modified - } else { - add_new_editor(*m_current_editor_tab_widget); - } - - if (!active_file().is_empty()) { - // Since the file is previously open, it should always be in m_open_files. - VERIFY(m_open_files.find(active_file()) != m_open_files.end()); - auto previous_open_project_file = m_open_files.get(active_file()).value(); - - // Update the scrollbar values of the previous_open_project_file and save them to m_open_files. - previous_open_project_file->vertical_scroll_value(current_editor().vertical_scrollbar().value()); - previous_open_project_file->horizontal_scroll_value(current_editor().horizontal_scrollbar().value()); - } - - RefPtr new_project_file = nullptr; - if (auto it = m_open_files.find(filename); it != m_open_files.end()) { - new_project_file = it->value; - } else { - new_project_file = m_project->create_file(filename); - m_open_files.set(filename, *new_project_file); - m_open_files_vector.append(filename); - - if (!m_file_watcher.is_null()) { - auto watch_result = m_file_watcher->add_watch(filename, Core::FileWatcherEvent::Type::Deleted); - if (watch_result.is_error()) { - warnln("Couldn't watch '{}'", filename); - } - } - m_open_files_view->model()->invalidate(); - } - - current_editor().on_cursor_change = nullptr; // Disable callback while we're swapping the document. - current_editor().set_document(const_cast(new_project_file->document())); - if (new_project_file->could_render_text()) { - current_editor_wrapper().set_mode_displayable(); - } else { - current_editor_wrapper().set_mode_non_displayable(); - } - current_editor().horizontal_scrollbar().set_value(new_project_file->horizontal_scroll_value()); - current_editor().vertical_scrollbar().set_value(new_project_file->vertical_scroll_value()); - if (current_editor().editing_engine()->is_regular()) - current_editor().set_editing_engine(make()); - else if (current_editor().editing_engine()->is_vim()) - current_editor().set_editing_engine(make()); - else - VERIFY_NOT_REACHED(); - - set_edit_mode(EditMode::Text); - - ByteString relative_file_path = filename; - if (filename.starts_with(m_project->root_path())) - relative_file_path = filename.substring(m_project->root_path().length() + 1); - - m_project_tree_view->update(); - - current_editor_wrapper().set_filename(filename); - update_current_editor_title(); - current_editor().set_focus(true); - - current_editor().on_cursor_change = [this] { on_cursor_change(); }; - current_editor().on_change = [this] { update_window_title(); }; - current_editor_wrapper().on_change = [this] { update_gml_preview(); }; - current_editor().set_cursor_and_focus_line(line, column); - update_gml_preview(); - - return true; -} - -void HackStudioWidget::close_file_in_all_editors(ByteString const& filename) -{ - m_open_files.remove(filename); - m_open_files_vector.remove_all_matching( - [&filename](ByteString const& element) { return element == filename; }); - - for (auto& editor_wrapper : m_all_editor_wrappers) { - Editor& editor = editor_wrapper->editor(); - ByteString editor_file_path = editor.code_document().file_path(); - ByteString relative_editor_file_path = LexicalPath::relative_path(editor_file_path, project().root_path()); - - if (relative_editor_file_path == filename) { - if (m_open_files_vector.is_empty()) { - editor.set_document(CodeDocument::create()); - editor_wrapper->set_filename(""); - } else { - auto& first_path = m_open_files_vector[0]; - auto& document = m_open_files.get(first_path).value()->code_document(); - editor.set_document(document); - editor_wrapper->set_filename(first_path); - } - } - } - - m_open_files_view->model()->invalidate(); -} - -GUI::TabWidget& HackStudioWidget::current_editor_tab_widget() -{ - VERIFY(m_current_editor_tab_widget); - return *m_current_editor_tab_widget; -} - -GUI::TabWidget const& HackStudioWidget::current_editor_tab_widget() const -{ - VERIFY(m_current_editor_tab_widget); - return *m_current_editor_tab_widget; -} - -EditorWrapper& HackStudioWidget::current_editor_wrapper() -{ - VERIFY(m_current_editor_wrapper); - return *m_current_editor_wrapper; -} - -EditorWrapper const& HackStudioWidget::current_editor_wrapper() const -{ - VERIFY(m_current_editor_wrapper); - return *m_current_editor_wrapper; -} - -GUI::TextEditor& HackStudioWidget::current_editor() -{ - return current_editor_wrapper().editor(); -} - -GUI::TextEditor const& HackStudioWidget::current_editor() const -{ - return current_editor_wrapper().editor(); -} - -void HackStudioWidget::set_edit_mode(EditMode mode) -{ - if (mode == EditMode::Text) { - m_right_hand_stack->set_active_widget(m_editors_splitter); - } else if (mode == EditMode::Diff) { - m_right_hand_stack->set_active_widget(m_diff_viewer); - } else { - VERIFY_NOT_REACHED(); - } - m_right_hand_stack->active_widget()->update(); -} - -ErrorOr> HackStudioWidget::create_project_tree_view_context_menu() -{ - TRY(m_new_file_actions.try_append(TRY(create_new_file_action("&C++ Source File", "/res/icons/16x16/filetype-cplusplus.png", "cpp")))); - TRY(m_new_file_actions.try_append(TRY(create_new_file_action("C++ &Header File", "/res/icons/16x16/filetype-header.png", "h")))); - TRY(m_new_file_actions.try_append(TRY(create_new_file_action("&GML File", "/res/icons/16x16/filetype-gml.png", "gml")))); - TRY(m_new_file_actions.try_append(TRY(create_new_file_action("P&ython Source File", "/res/icons/16x16/filetype-python.png", "py")))); - TRY(m_new_file_actions.try_append(TRY(create_new_file_action("Ja&va Source File", "/res/icons/16x16/filetype-java.png", "java")))); - TRY(m_new_file_actions.try_append(TRY(create_new_file_action("C Source File", "/res/icons/16x16/filetype-c.png", "c")))); - TRY(m_new_file_actions.try_append(TRY(create_new_file_action("&JavaScript Source File", "/res/icons/16x16/filetype-javascript.png", "js")))); - TRY(m_new_file_actions.try_append(TRY(create_new_file_action("HT&ML File", "/res/icons/16x16/filetype-html.png", "html")))); - TRY(m_new_file_actions.try_append(TRY(create_new_file_action("C&SS File", "/res/icons/16x16/filetype-css.png", "css")))); - TRY(m_new_file_actions.try_append(TRY(create_new_file_action("&PHP File", "/res/icons/16x16/filetype-php.png", "php")))); - TRY(m_new_file_actions.try_append(TRY(create_new_file_action("&Wasm File", "/res/icons/16x16/filetype-wasm.png", "wasm")))); - TRY(m_new_file_actions.try_append(TRY(create_new_file_action("&INI File", "/res/icons/16x16/filetype-ini.png", "ini")))); - TRY(m_new_file_actions.try_append(TRY(create_new_file_action("JS&ON File", "/res/icons/16x16/filetype-json.png", "json")))); - TRY(m_new_file_actions.try_append(TRY(create_new_file_action("Mark&down File", "/res/icons/16x16/filetype-markdown.png", "md")))); - - m_new_plain_file_action = TRY(create_new_file_action("Plain &File", "/res/icons/16x16/new.png", "")); - - m_open_selected_action = TRY(create_open_selected_action()); - m_show_in_file_manager_action = create_show_in_file_manager_action(); - m_copy_relative_path_action = create_copy_relative_path_action(); - m_copy_full_path_action = create_copy_full_path_action(); - - m_new_directory_action = TRY(create_new_directory_action()); - m_delete_action = create_delete_action(); - m_tree_view_rename_action = GUI::CommonActions::make_rename_action([this](GUI::Action const&) { - m_project_tree_view->begin_editing(m_project_tree_view->cursor_index()); - }); - auto project_tree_view_context_menu = GUI::Menu::construct("Project Files"_string); - - auto new_file_submenu = project_tree_view_context_menu->add_submenu("N&ew..."_string); - for (auto& new_file_action : m_new_file_actions) { - new_file_submenu->add_action(new_file_action); - } - auto icon = TRY(Gfx::Bitmap::load_from_file("/res/icons/16x16/new.png"sv)); - new_file_submenu->set_icon(icon); - new_file_submenu->add_action(*m_new_plain_file_action); - new_file_submenu->add_separator(); - new_file_submenu->add_action(*m_new_directory_action); - - project_tree_view_context_menu->add_action(*m_open_selected_action); - project_tree_view_context_menu->add_action(*m_show_in_file_manager_action); - project_tree_view_context_menu->add_action(*m_copy_relative_path_action); - project_tree_view_context_menu->add_action(*m_copy_full_path_action); - // TODO: Cut, copy, duplicate with new name... - project_tree_view_context_menu->add_separator(); - project_tree_view_context_menu->add_action(*m_tree_view_rename_action); - project_tree_view_context_menu->add_action(*m_delete_action); - return project_tree_view_context_menu; -} - -ErrorOr> HackStudioWidget::create_new_file_action(ByteString const& label, ByteString const& icon, ByteString const& extension) -{ - auto icon_no_shadow = TRY(Gfx::Bitmap::load_from_file(icon)); - return GUI::Action::create(label, icon_no_shadow, [this, extension](const GUI::Action&) { - String filename; - if (GUI::InputBox::show(window(), filename, "Enter a name:"sv, "New File"sv) != GUI::InputBox::ExecResult::OK) - return; - - if (!extension.is_empty() && !AK::StringUtils::ends_with(filename, ByteString::formatted(".{}", extension), CaseSensitivity::CaseSensitive)) { - filename = String::formatted("{}.{}", filename, extension).release_value_but_fixme_should_propagate_errors(); - } - - auto path_to_selected = selected_file_paths(); - - ByteString filepath; - - if (!path_to_selected.is_empty()) { - VERIFY(FileSystem::exists(path_to_selected.first())); - - LexicalPath selected(path_to_selected.first()); - - ByteString dir_path; - - if (FileSystem::is_directory(selected.string())) - dir_path = selected.string(); - else - dir_path = selected.dirname(); - - filepath = ByteString::formatted("{}/", dir_path); - } - - filepath = ByteString::formatted("{}{}", filepath, filename); - - auto file_or_error = Core::File::open(filepath, Core::File::OpenMode::Write | Core::File::OpenMode::MustBeNew); - if (file_or_error.is_error()) { - GUI::MessageBox::show_error(window(), ByteString::formatted("Failed to create '{}': {}", filepath, file_or_error.error())); - return; - } - open_file(filepath); - }); -} - -ErrorOr> HackStudioWidget::create_new_directory_action() -{ - auto icon = TRY(Gfx::Bitmap::load_from_file("/res/icons/16x16/mkdir.png"sv)); - return GUI::Action::create("&Directory...", { Mod_Ctrl | Mod_Shift, Key_N }, icon, [this](const GUI::Action&) { - String directory_name; - if (GUI::InputBox::show(window(), directory_name, "Enter a name:"sv, "New Directory"sv) != GUI::InputBox::ExecResult::OK) - return; - - auto path_to_selected = selected_file_paths(); - - if (!path_to_selected.is_empty()) { - LexicalPath selected(path_to_selected.first()); - - ByteString dir_path; - - if (FileSystem::is_directory(selected.string())) - dir_path = selected.string(); - else - dir_path = selected.dirname(); - - directory_name = String::formatted("{}/{}", dir_path, directory_name).release_value_but_fixme_should_propagate_errors(); - } - - auto formatted_dir_name = LexicalPath::canonicalized_path(ByteString::formatted("{}/{}", m_project->model().root_path(), directory_name)); - int rc = mkdir(formatted_dir_name.characters(), 0755); - if (rc < 0) { - GUI::MessageBox::show(window(), "Failed to create new directory"sv, "Error"sv, GUI::MessageBox::Type::Error); - return; - } - }); -} - -ErrorOr> HackStudioWidget::create_open_selected_action() -{ - auto open_selected_action = GUI::Action::create("&Open", [this](const GUI::Action&) { - auto files = selected_file_paths(); - for (auto& file : files) - open_file(file); - }); - auto icon = TRY(Gfx::Bitmap::load_from_file("/res/icons/16x16/open.png"sv)); - open_selected_action->set_icon(icon); - open_selected_action->set_enabled(true); - return open_selected_action; -} - -NonnullRefPtr HackStudioWidget::create_show_in_file_manager_action() -{ - auto show_in_file_manager_action = GUI::Action::create("Show in File &Manager", [this](const GUI::Action&) { - auto files = selected_file_paths(); - for (auto& file : files) - Desktop::Launcher::open(URL::create_with_file_scheme(m_project->root_path(), file)); - }); - show_in_file_manager_action->set_enabled(true); - show_in_file_manager_action->set_icon(GUI::Icon::default_icon("app-file-manager"sv).bitmap_for_size(16)); - - return show_in_file_manager_action; -} - -NonnullRefPtr HackStudioWidget::create_copy_relative_path_action() -{ - auto copy_relative_path_action = GUI::Action::create("Copy &Relative Path", [this](const GUI::Action&) { - auto paths = selected_file_paths(); - VERIFY(!paths.is_empty()); - auto paths_string = ByteString::join('\n', paths); - GUI::Clipboard::the().set_plain_text(paths_string); - }); - copy_relative_path_action->set_enabled(true); - copy_relative_path_action->set_icon(GUI::Icon::default_icon("hard-disk"sv).bitmap_for_size(16)); - - return copy_relative_path_action; -} - -NonnullRefPtr HackStudioWidget::create_copy_full_path_action() -{ - auto copy_full_path_action = GUI::Action::create("Copy &Full Path", [this](const GUI::Action&) { - auto paths = selected_file_paths(); - VERIFY(!paths.is_empty()); - Vector full_paths; - for (auto& path : paths) - full_paths.append(get_absolute_path(path)); - auto paths_string = ByteString::join('\n', full_paths); - GUI::Clipboard::the().set_plain_text(paths_string); - }); - copy_full_path_action->set_enabled(true); - copy_full_path_action->set_icon(GUI::Icon::default_icon("hard-disk"sv).bitmap_for_size(16)); - - return copy_full_path_action; -} - -NonnullRefPtr HackStudioWidget::create_delete_action() -{ - auto delete_action = GUI::CommonActions::make_delete_action([this](const GUI::Action&) { - auto files = selected_file_paths(); - if (files.is_empty()) - return; - - ByteString message; - if (files.size() == 1) { - LexicalPath file(files[0]); - message = ByteString::formatted("Really remove \"{}\" from disk?", file.basename()); - } else { - message = ByteString::formatted("Really remove \"{}\" files from disk?", files.size()); - } - - auto result = GUI::MessageBox::show(window(), - message, - "Confirm Deletion"sv, - GUI::MessageBox::Type::Warning, - GUI::MessageBox::InputType::OKCancel); - if (result == GUI::MessageBox::ExecResult::Cancel) - return; - - for (auto& file : files) { - struct stat st; - if (lstat(file.characters(), &st) < 0) { - GUI::MessageBox::show(window(), - ByteString::formatted("lstat ({}) failed: {}", file, strerror(errno)), - "Removal Failed"sv, - GUI::MessageBox::Type::Error); - break; - } - - bool is_directory = S_ISDIR(st.st_mode); - if (auto result = FileSystem::remove(file, FileSystem::RecursionMode::Allowed); result.is_error()) { - auto& error = result.error(); - if (is_directory) { - GUI::MessageBox::show(window(), - ByteString::formatted("Removing directory \"{}\" from the project failed: {}", file, error), - "Removal Failed"sv, - GUI::MessageBox::Type::Error); - } else { - GUI::MessageBox::show(window(), - ByteString::formatted("Removing file \"{}\" from the project failed: {}", file, error), - "Removal Failed"sv, - GUI::MessageBox::Type::Error); - } - } - } - }, - m_project_tree_view); - delete_action->set_enabled(false); - return delete_action; -} - -ErrorOr> HackStudioWidget::create_new_project_action() -{ - auto icon = TRY(Gfx::Bitmap::load_from_file("/res/icons/16x16/hackstudio-project.png"sv)); - return GUI::Action::create( - "&Project...", icon, - [this](const GUI::Action&) { - if (warn_unsaved_changes("There are unsaved changes. Would you like to save before creating a new project?") == ContinueDecision::No) - return; - // If the user wishes to save the changes, this occurs in warn_unsaved_changes. If they do not, - // we need to mark the documents as clean so open_project works properly without asking again. - for (auto& editor_wrapper : m_all_editor_wrappers) - editor_wrapper->editor().document().set_unmodified(); - auto dialog = NewProjectDialog::construct(window()); - dialog->set_icon(window()->icon()); - auto result = dialog->exec(); - - if (result == GUI::Dialog::ExecResult::OK && dialog->created_project_path().has_value()) - open_project(dialog->created_project_path().value()); - }); -} - -NonnullRefPtr HackStudioWidget::create_remove_current_editor_tab_widget_action() -{ - return GUI::Action::create("Switch to Next Editor Group", { Mod_Alt | Mod_Shift, Key_Backslash }, [this](auto&) { - if (m_all_editor_tab_widgets.size() <= 1) - return; - auto tab_widget = m_current_editor_tab_widget; - while (tab_widget->children().size() > 1) { - auto active_wrapper = tab_widget->active_widget(); - tab_widget->remove_tab(*active_wrapper); - m_all_editor_wrappers.remove_first_matching([&active_wrapper](auto& entry) { return entry == active_wrapper; }); - } - tab_widget->on_tab_close_click(*tab_widget->active_widget()); - }); -} - -void HackStudioWidget::add_new_editor_tab_widget(GUI::Widget& parent) -{ - auto tab_widget = GUI::TabWidget::construct(); - if (m_action_tab_widget) { - parent.insert_child_before(tab_widget, *m_action_tab_widget); - } else { - parent.add_child(tab_widget); - } - - m_current_editor_tab_widget = tab_widget; - m_all_editor_tab_widgets.append(tab_widget); - - tab_widget->set_reorder_allowed(true); - - if (m_all_editor_tab_widgets.size() > 1) { - for (auto& widget : m_all_editor_tab_widgets) - widget->set_close_button_enabled(true); - } - - tab_widget->on_change = [&](auto& widget) { - auto& wrapper = static_cast(widget); - set_current_editor_wrapper(wrapper); - current_editor().set_focus(true); - }; - - tab_widget->on_middle_click = [](auto& widget) { - auto& wrapper = static_cast(widget); - wrapper.on_tab_close_request(wrapper); - }; - - tab_widget->on_tab_close_click = [](auto& widget) { - auto& wrapper = static_cast(widget); - wrapper.on_tab_close_request(wrapper); - }; - - add_new_editor(*m_current_editor_tab_widget); -} - -void HackStudioWidget::add_new_editor(GUI::TabWidget& parent) -{ - auto& wrapper = parent.add_tab("(Untitled)"_string); - parent.set_active_widget(&wrapper); - if (parent.children().size() > 1 || m_all_editor_tab_widgets.size() > 1) - parent.set_close_button_enabled(true); - - auto previous_editor_wrapper = m_current_editor_wrapper; - m_current_editor_wrapper = wrapper; - m_all_editor_wrappers.append(wrapper); - wrapper.editor().set_focus(true); - wrapper.editor().set_font(*m_editor_font); - wrapper.editor().set_wrapping_mode(m_wrapping_mode); - wrapper.set_project_root(m_project->root_path()); - wrapper.editor().on_cursor_change = [this] { on_cursor_change(); }; - wrapper.on_change = [this] { update_gml_preview(); }; - set_edit_mode(EditMode::Text); - if (previous_editor_wrapper && previous_editor_wrapper->editor().editing_engine()->is_regular()) - wrapper.editor().set_editing_engine(make()); - else if (previous_editor_wrapper && previous_editor_wrapper->editor().editing_engine()->is_vim()) - wrapper.editor().set_editing_engine(make()); - else - wrapper.editor().set_editing_engine(make()); - - wrapper.on_tab_close_request = [this, &parent](auto& tab) { - parent.deferred_invoke([this, &parent, &tab] { - tab.editor().document().unregister_client(tab.editor()); - set_current_editor_wrapper(tab); - parent.remove_tab(tab); - m_all_editor_wrappers.remove_first_matching([&tab](auto& entry) { return entry == &tab; }); - if (parent.children().is_empty() && m_all_editor_tab_widgets.size() > 1) { - m_switch_to_next_editor_tab_widget->activate(); - m_editors_splitter->remove_child(parent); - m_all_editor_tab_widgets.remove_first_matching([&parent](auto& entry) { return entry == &parent; }); - } - update_actions(); - }); - }; -} - -NonnullRefPtr HackStudioWidget::create_switch_to_next_editor_tab_widget_action() -{ - return GUI::Action::create("Switch to Next Editor Group", { Mod_Ctrl | Mod_Shift, Key_T }, [this](auto&) { - if (m_all_editor_tab_widgets.size() <= 1) - return; - Vector tab_widgets; - m_editors_splitter->for_each_child_of_type([&tab_widgets](auto& child) { - tab_widgets.append(child); - return IterationDecision::Continue; - }); - for (size_t i = 0; i < tab_widgets.size(); ++i) { - if (m_current_editor_tab_widget.ptr() == &tab_widgets[i]) { - if (i == tab_widgets.size() - 1) - m_current_editor_tab_widget = tab_widgets[0]; - else - m_current_editor_tab_widget = tab_widgets[i + 1]; - auto wrapper = static_cast(m_current_editor_tab_widget->active_widget()); - set_current_editor_wrapper(wrapper); - current_editor().set_focus(true); - break; - } - } - }); -} - -NonnullRefPtr HackStudioWidget::create_switch_to_next_editor_action() -{ - return GUI::Action::create("Switch to &Next Editor", { Mod_Ctrl, Key_E }, [this](auto&) { - m_current_editor_tab_widget->activate_next_tab(); - }); -} - -NonnullRefPtr HackStudioWidget::create_switch_to_previous_editor_action() -{ - return GUI::Action::create("Switch to &Previous Editor", { Mod_Ctrl | Mod_Shift, Key_E }, [this](auto&) { - m_current_editor_tab_widget->activate_previous_tab(); - }); -} - -ErrorOr> HackStudioWidget::create_remove_current_editor_action() -{ - auto icon = TRY(Gfx::Bitmap::load_from_file("/res/icons/hackstudio/remove-editor.png"sv)); - return GUI::Action::create("&Remove Current Editor", { Mod_Alt | Mod_Shift, Key_E }, icon, [this](auto&) { - if (m_all_editor_wrappers.size() <= 1) - return; - auto tab_widget = m_current_editor_tab_widget; - auto* active_wrapper = tab_widget->active_widget(); - VERIFY(active_wrapper); - tab_widget->on_tab_close_click(*active_wrapper); - update_actions(); - }); -} - -ErrorOr> HackStudioWidget::create_toggle_open_file_in_single_click_action() -{ - return GUI::Action::create_checkable("&Open File with Single Click", [this](auto&) { - m_project_tree_view->set_activates_on_selection(!m_project_tree_view->activates_on_selection()); - }); -} - -ErrorOr> HackStudioWidget::create_open_action() -{ - auto icon = TRY(Gfx::Bitmap::load_from_file("/res/icons/16x16/open.png"sv)); - return GUI::Action::create("&Open Project...", { Mod_Ctrl | Mod_Shift, Key_O }, icon, [this](auto&) { - auto open_path = GUI::FilePicker::get_open_filepath(window(), "Open Project", m_project->root_path(), true); - if (!open_path.has_value()) - return; - open_project(open_path.value()); - update_actions(); - }); -} - -NonnullRefPtr HackStudioWidget::create_save_action() -{ - return GUI::CommonActions::make_save_action([&](auto&) { - if (active_file().is_empty()) - m_save_as_action->activate(); - - // NOTE active_file() could still be empty after a cancelled save_as_action - if (!active_file().is_empty()) - current_editor_wrapper().save(); - - if (m_git_widget->initialized()) - m_git_widget->refresh(); - }); -} - -NonnullRefPtr HackStudioWidget::create_save_as_action() -{ - return GUI::CommonActions::make_save_as_action([&](auto&) { - auto const old_filename = current_editor_wrapper().filename(); - LexicalPath const old_path(old_filename); - - auto suggested_path = FileSystem::absolute_path(old_path.string()).release_value_but_fixme_should_propagate_errors(); - Optional save_path = GUI::FilePicker::get_save_filepath(window(), - old_filename.is_empty() ? "Untitled"sv : old_path.title(), - old_filename.is_empty() ? "txt"sv : old_path.extension(), - suggested_path); - if (!save_path.has_value()) { - return; - } - - ByteString const relative_file_path = LexicalPath::relative_path(save_path.value(), m_project->root_path()); - if (current_editor_wrapper().filename().is_empty()) { - current_editor_wrapper().set_filename(relative_file_path); - } else { - for (auto& editor_wrapper : m_all_editor_wrappers) { - if (editor_wrapper->filename() == old_filename) - editor_wrapper->set_filename(relative_file_path); - } - } - current_editor_wrapper().save(); - - auto new_project_file = m_project->create_file(relative_file_path); - m_open_files.set(relative_file_path, *new_project_file); - m_open_files.remove(old_filename); - - m_open_files_vector.append(relative_file_path); - m_open_files_vector.remove_all_matching([&old_filename](auto const& element) { return element == old_filename; }); - - update_window_title(); - update_current_editor_title(); - - m_project->model().invalidate(); - update_tree_view(); - }); -} - -ErrorOr> HackStudioWidget::create_remove_current_terminal_action() -{ - auto icon = TRY(Gfx::Bitmap::load_from_file("/res/icons/hackstudio/remove-terminal.png"sv)); - return GUI::Action::create("Remove &Current Terminal", { Mod_Alt | Mod_Shift, Key_T }, icon, [this](auto&) { - auto widget = m_action_tab_widget->active_widget(); - if (!widget) - return; - if (!is(widget)) - return; - auto& terminal = *static_cast(widget); - if (!terminal.user_spawned()) - return; - m_action_tab_widget->remove_tab(terminal); - update_actions(); - }); -} - -NonnullRefPtr HackStudioWidget::create_add_editor_tab_widget_action() -{ - return GUI::Action::create("Add New Editor Group", { Mod_Ctrl | Mod_Alt, Key_Backslash }, - [this](auto&) { - add_new_editor_tab_widget(*m_editors_splitter); - update_actions(); - }); -} - -ErrorOr> HackStudioWidget::create_add_editor_action() -{ - auto icon = TRY(Gfx::Bitmap::load_from_file("/res/icons/hackstudio/add-editor.png"sv)); - return GUI::Action::create("Add New &Editor", { Mod_Ctrl | Mod_Alt, Key_E }, - icon, - [this](auto&) { - add_new_editor(*m_current_editor_tab_widget); - update_actions(); - }); -} - -ErrorOr> HackStudioWidget::create_add_terminal_action() -{ - auto icon = TRY(Gfx::Bitmap::load_from_file("/res/icons/hackstudio/add-terminal.png"sv)); - return GUI::Action::create("Add New &Terminal", { Mod_Ctrl | Mod_Alt, Key_T }, - icon, - [this](auto&) { - auto& terminal_wrapper = m_action_tab_widget->add_tab("Terminal"_string); - terminal_wrapper.on_command_exit = [&]() { - deferred_invoke([this]() { - m_action_tab_widget->remove_tab(*m_action_tab_widget->active_widget()); - }); - }; - reveal_action_tab(terminal_wrapper); - update_actions(); - terminal_wrapper.terminal().set_focus(true); - }); -} - -void HackStudioWidget::reveal_action_tab(GUI::Widget& widget) -{ - if (m_action_tab_widget->effective_min_size().height().as_int() < 200) - m_action_tab_widget->set_preferred_height(200); - m_action_tab_widget->set_active_widget(&widget); -} - -ErrorOr> HackStudioWidget::create_debug_action() -{ - auto icon = TRY(Gfx::Bitmap::load_from_file("/res/icons/16x16/debug-run.png"sv)); - return GUI::Action::create("&Debug", icon, [this](auto&) { - if (!FileSystem::exists(get_project_executable_path())) { - GUI::MessageBox::show(window(), ByteString::formatted("Could not find file: {}. (did you build the project?)", get_project_executable_path()), "Error"sv, GUI::MessageBox::Type::Error); - return; - } - if (Debugger::the().session()) { - GUI::MessageBox::show(window(), "Debugger is already running"sv, "Error"sv, GUI::MessageBox::Type::Error); - return; - } - - Debugger::the().set_executable_path(get_project_executable_path()); - - m_terminal_wrapper->clear_including_history(); - - // The debugger calls wait() on the debuggee, so the TerminalWrapper can't do that. - auto ptm_res = m_terminal_wrapper->setup_master_pseudoterminal(TerminalWrapper::WaitForChildOnExit::No); - if (ptm_res.is_error()) { - warnln("Failed to set up master pseudoterminal: {}", ptm_res.release_error()); - return; - } - - Debugger::the().set_child_setup_callback([this, ptm_res = ptm_res.release_value()]() { - return m_terminal_wrapper->setup_slave_pseudoterminal(ptm_res); - }); - - m_debugger_thread = Threading::Thread::construct(Debugger::start_static); - m_debugger_thread->start(); - m_stop_action->set_enabled(true); - m_run_action->set_enabled(false); - - for (auto& editor_wrapper : m_all_editor_wrappers) { - editor_wrapper->set_debug_mode(true); - } - }); -} - -void HackStudioWidget::initialize_debugger() -{ - Debugger::initialize( - m_project->root_path(), - [this](PtraceRegisters const& regs) { - VERIFY(Debugger::the().session()); - auto const& debug_session = *Debugger::the().session(); - auto source_position = debug_session.get_source_position(regs.ip()); - if (!source_position.has_value()) { - dbgln("Could not find source position for address: {:p}", regs.ip()); - return Debugger::HasControlPassedToUser::No; - } - dbgln("Debugger stopped at source position: {}:{}", source_position.value().file_path, source_position.value().line_number); - - GUI::Application::the()->event_loop().deferred_invoke([this, source_position, ®s] { - m_current_editor_in_execution = get_editor_of_file(source_position.value().file_path); - if (m_current_editor_in_execution) - m_current_editor_in_execution->editor().set_execution_position(source_position.value().line_number - 1); - m_debug_info_widget->update_state(*Debugger::the().session(), regs); - m_debug_info_widget->set_debug_actions_enabled(true, DebugInfoWidget::DebugActionsState::DebuggeeStopped); - m_disassembly_widget->update_state(*Debugger::the().session(), regs); - HackStudioWidget::reveal_action_tab(*m_debug_info_widget); - }); - GUI::Application::the()->event_loop().wake(); - - return Debugger::HasControlPassedToUser::Yes; - }, - [this]() { - GUI::Application::the()->event_loop().deferred_invoke([this] { - m_debug_info_widget->set_debug_actions_enabled(true, DebugInfoWidget::DebugActionsState::DebuggeeRunning); - if (m_current_editor_in_execution) - m_current_editor_in_execution->editor().clear_execution_position(); - }); - GUI::Application::the()->event_loop().wake(); - }, - [this]() { - GUI::Application::the()->event_loop().deferred_invoke([this] { - m_debug_info_widget->set_debug_actions_enabled(false, {}); - if (m_current_editor_in_execution) - m_current_editor_in_execution->editor().clear_execution_position(); - m_debug_info_widget->program_stopped(); - m_disassembly_widget->program_stopped(); - m_stop_action->set_enabled(false); - m_run_action->set_enabled(true); - m_debugger_thread.clear(); - - for (auto& editor_wrapper : m_all_editor_wrappers) { - editor_wrapper->set_debug_mode(false); - } - - HackStudioWidget::hide_action_tabs(); - GUI::MessageBox::show(window(), "Program Exited"sv, "Debugger"sv, GUI::MessageBox::Type::Information); - }); - GUI::Application::the()->event_loop().wake(); - }, - [](float progress) { - if (GUI::Application::the()->active_window()) - GUI::Application::the()->active_window()->set_progress(progress * 100); - }); -} - -ByteString HackStudioWidget::get_full_path_of_serenity_source(ByteString const& file) -{ - auto path_parts = LexicalPath(file).parts(); - while (!path_parts.is_empty() && path_parts[0] == "..") { - path_parts.remove(0); - } - StringBuilder relative_path_builder; - relative_path_builder.join('/', path_parts); - constexpr char SERENITY_LIBS_PREFIX[] = "/usr/src/serenity"; - LexicalPath serenity_sources_base(SERENITY_LIBS_PREFIX); - return ByteString::formatted("{}/{}", serenity_sources_base, relative_path_builder.to_byte_string()); -} - -ByteString HackStudioWidget::get_absolute_path(ByteString const& path) const -{ - // TODO: We can probably do a more specific condition here, something like - // "if (file.starts_with("../Libraries/") || file.starts_with("../AK/"))" - if (path.starts_with(".."sv)) { - return get_full_path_of_serenity_source(path); - } - return m_project->to_absolute_path(path); -} - -RefPtr HackStudioWidget::get_editor_of_file(ByteString const& filename) -{ - ByteString file_path = filename; - - if (filename.starts_with("../"sv)) { - file_path = get_full_path_of_serenity_source(filename); - } - - if (!open_file(file_path)) - return nullptr; - return current_editor_wrapper(); -} - -ByteString HackStudioWidget::get_project_executable_path() const -{ - // FIXME: Dumb heuristic ahead! - // e.g /my/project => /my/project/project - // TODO: Perhaps a Makefile rule for getting the value of $(PROGRAM) would be better? - return ByteString::formatted("{}/{}", m_project->root_path(), LexicalPath::basename(m_project->root_path())); -} - -void HackStudioWidget::build() -{ - auto result = m_project_builder->build(active_file()); - if (result.is_error()) { - GUI::MessageBox::show(window(), ByteString::formatted("{}", result.error()), "Build Failed"sv, GUI::MessageBox::Type::Error); - m_build_action->set_enabled(true); - m_stop_action->set_enabled(false); - } else { - m_stop_action->set_enabled(true); - } -} - -void HackStudioWidget::run() -{ - auto result = m_project_builder->run(active_file()); - if (result.is_error()) { - GUI::MessageBox::show(window(), ByteString::formatted("{}", result.error()), "Run Failed"sv, GUI::MessageBox::Type::Error); - m_run_action->set_enabled(true); - m_stop_action->set_enabled(false); - } else { - m_stop_action->set_enabled(true); - } -} - -void HackStudioWidget::hide_action_tabs() -{ - m_action_tab_widget->set_preferred_height(24); -} - -Project& HackStudioWidget::project() -{ - return *m_project; -} - -void HackStudioWidget::set_current_editor_tab_widget(RefPtr tab_widget) -{ - m_current_editor_tab_widget = tab_widget; -} - -void HackStudioWidget::set_current_editor_wrapper(RefPtr editor_wrapper) -{ - if (m_current_editor_wrapper) - m_current_editor_wrapper->editor().hide_autocomplete(); - - m_current_editor_wrapper = editor_wrapper; - update_window_title(); - update_current_editor_title(); - update_tree_view(); - update_toolbar_actions(); - set_current_editor_tab_widget(static_cast(m_current_editor_wrapper->parent())); - m_current_editor_tab_widget->set_active_widget(editor_wrapper); - update_statusbar(); -} - -void HackStudioWidget::file_renamed(ByteString const& old_name, ByteString const& new_name) -{ - auto editor_or_none = m_all_editor_wrappers.first_matching([&old_name](auto const& editor) { - return editor->filename() == old_name; - }); - if (editor_or_none.has_value()) { - (*editor_or_none)->set_filename(new_name); - (*editor_or_none)->set_name(new_name); - } - - if (m_open_files.contains(old_name)) { - VERIFY(m_open_files_vector.remove_first_matching([&old_name](auto const& file) { return file == old_name; })); - m_open_files_vector.append(new_name); - - ProjectFile* f = m_open_files.get(old_name).release_value(); - m_open_files.set(new_name, *f); - m_open_files.remove(old_name); - m_open_files_view->model()->invalidate(); - } - - if (m_file_watcher->is_watching(old_name)) { - VERIFY(!m_file_watcher->remove_watch(old_name).is_error()); - VERIFY(!m_file_watcher->add_watch(new_name, Core::FileWatcherEvent::Type::Deleted).is_error()); - } - - update_window_title(); - update_current_editor_title(); -} - -void HackStudioWidget::configure_project_tree_view() -{ - m_project_tree_view->set_model(m_project->model()); - m_project_tree_view->set_selection_mode(GUI::AbstractView::SelectionMode::MultiSelection); - m_project_tree_view->set_editable(true); - m_project_tree_view->aid_create_editing_delegate = [](auto&) { - return make(); - }; - - for (int column_index = 0; column_index < m_project->model().column_count(); ++column_index) - m_project_tree_view->set_column_visible(column_index, false); - - m_project_tree_view->set_column_visible(GUI::FileSystemModel::Column::Name, true); - - m_project_tree_view->on_context_menu_request = [this](const GUI::ModelIndex& index, const GUI::ContextMenuEvent& event) { - if (index.is_valid()) { - m_project_tree_view_context_menu->popup(event.screen_position(), m_open_selected_action); - } - }; - - m_project_tree_view->on_selection_change = [this] { - m_open_selected_action->set_enabled(!m_project_tree_view->selection().is_empty()); - - auto selections = m_project_tree_view->selection().indices(); - auto it = selections.find_if([&](auto selected_file) { - return FileSystem::can_delete_or_move(m_project->model().full_path(selected_file)); - }); - bool has_permissions = it != selections.end(); - m_tree_view_rename_action->set_enabled(has_permissions); - m_delete_action->set_enabled(has_permissions); - }; - - m_project_tree_view->on_activation = [this](auto& index) { - auto full_path_to_file = m_project->model().full_path(index); - open_file(full_path_to_file); - }; -} - -void HackStudioWidget::create_open_files_view(GUI::Widget& parent) -{ - m_open_files_view = parent.add(); - auto open_files_model = GUI::ItemListModel::create(m_open_files_vector); - m_open_files_view->set_model(open_files_model); - - m_open_files_view->on_activation = [this](auto& index) { - open_file(index.data().to_byte_string()); - }; -} - -void HackStudioWidget::create_toolbar(GUI::Widget& parent) -{ - auto& toolbar = parent.add(); - toolbar.add_action(*m_new_plain_file_action); - toolbar.add_action(*m_new_directory_action); - toolbar.add_action(*m_save_action); - toolbar.add_action(*m_delete_action); - toolbar.add_separator(); - - m_cut_button = toolbar.add_action(current_editor().cut_action()); - m_copy_button = toolbar.add_action(current_editor().copy_action()); - m_paste_button = toolbar.add_action(current_editor().paste_action()); - toolbar.add_separator(); - toolbar.add_action(GUI::CommonActions::make_undo_action([this](auto&) { current_editor().undo_action().activate(); }, m_editors_splitter)); - toolbar.add_action(GUI::CommonActions::make_redo_action([this](auto&) { current_editor().redo_action().activate(); }, m_editors_splitter)); - toolbar.add_separator(); - - toolbar.add_action(*m_build_action); - toolbar.add_separator(); - - toolbar.add_action(*m_run_action); - toolbar.add_action(*m_stop_action); - toolbar.add_separator(); - - toolbar.add_action(*m_debug_action); -} - -ErrorOr> HackStudioWidget::create_build_action() -{ - auto icon = TRY(Gfx::Bitmap::load_from_file("/res/icons/16x16/build.png"sv)); - return GUI::Action::create("&Build", { Mod_Ctrl, Key_B }, icon, [this](auto&) { - if (m_auto_save_before_build_or_run) { - if (!save_file_changes()) - return; - } else { - if (warn_unsaved_changes("There are unsaved changes, do you want to save before building?") == ContinueDecision::No) - return; - } - - reveal_action_tab(*m_terminal_wrapper); - build(); - }); -} - -ErrorOr> HackStudioWidget::create_run_action() -{ - auto icon = TRY(Gfx::Bitmap::load_from_file("/res/icons/16x16/program-run.png"sv)); - return GUI::Action::create("&Run", { Mod_Ctrl, Key_R }, icon, [this](auto&) { - if (m_auto_save_before_build_or_run) { - if (!save_file_changes()) - return; - } else { - if (warn_unsaved_changes("There are unsaved changes, do you want to save before running?") == ContinueDecision::No) - return; - } - - reveal_action_tab(*m_terminal_wrapper); - run(); - }); -} - -ErrorOr HackStudioWidget::create_action_tab(GUI::Widget& parent) -{ - m_action_tab_widget = parent.add(); - - m_action_tab_widget->set_preferred_height(24); - m_action_tab_widget->on_change = [this](auto&) { - on_action_tab_change(); - - static bool first_time = true; - if (!first_time) - m_action_tab_widget->set_preferred_height(200); - first_time = false; - }; - - m_find_in_files_widget = m_action_tab_widget->add_tab("Find"_string); - m_todo_entries_widget = m_action_tab_widget->add_tab("TODO"_string); - m_terminal_wrapper = m_action_tab_widget->add_tab("Console"_string, false); - auto debug_info_widget = TRY(DebugInfoWidget::create()); - m_action_tab_widget->add_tab(debug_info_widget, "Debug"_string); - m_debug_info_widget = debug_info_widget; - - m_debug_info_widget->on_backtrace_frame_selection = [this](Debug::DebugInfo::SourcePosition const& source_position) { - open_file(get_absolute_path(source_position.file_path), source_position.line_number - 1); - }; - - m_disassembly_widget = m_action_tab_widget->add_tab("Disassembly"_string); - m_git_widget = m_action_tab_widget->add_tab("Git"_string); - m_git_widget->set_view_diff_callback([this](auto const& original_content, auto const& diff) { - m_diff_viewer->set_content(original_content, diff); - set_edit_mode(EditMode::Diff); - }); - m_gml_preview_widget = m_action_tab_widget->add_tab("GML Preview"_string, ""); - - ToDoEntries::the().on_update = [this]() { - m_todo_entries_widget->refresh(); - }; - - return {}; -} - -void HackStudioWidget::create_project_tab(GUI::Widget& parent) -{ - m_project_tab = parent.add(); - m_project_tab->set_tab_position(TabPosition::Bottom); - - auto& tree_view_container = m_project_tab->add_tab("Files"_string); - tree_view_container.set_layout(); - - m_project_tree_view = tree_view_container.add(); - configure_project_tree_view(); - - auto& class_view_container = m_project_tab->add_tab("Classes"_string); - class_view_container.set_layout(); - - m_class_view = class_view_container.add(); - - ProjectDeclarations::the().on_update = [this]() { - m_class_view->refresh(); - }; -} - -ErrorOr HackStudioWidget::create_file_menu(GUI::Window& window) -{ - auto file_menu = window.add_menu("&File"_string); - - auto new_submenu = file_menu->add_submenu("&New..."_string); - new_submenu->add_action(*m_new_project_action); - new_submenu->add_separator(); - for (auto& new_file_action : m_new_file_actions) { - new_submenu->add_action(new_file_action); - } - - { - auto icon = TRY(Gfx::Bitmap::load_from_file("/res/icons/16x16/new.png"sv)); - new_submenu->set_icon(icon); - } - new_submenu->add_action(*m_new_plain_file_action); - new_submenu->add_separator(); - new_submenu->add_action(*m_new_directory_action); - - file_menu->add_action(*m_open_action); - m_recent_projects_submenu = file_menu->add_submenu("Open &Recent"_string); - { - auto icon = TRY(Gfx::Bitmap::load_from_file("/res/icons/16x16/open-recent.png"sv)); - m_recent_projects_submenu->set_icon(icon); - m_recent_projects_submenu->add_recent_files_list( - [this](GUI::Action& action) { open_project(action.text()); }, - GUI::Menu::AddTrailingSeparator::No); - } - file_menu->add_action(*m_save_action); - file_menu->add_action(*m_save_as_action); - file_menu->add_separator(); - file_menu->add_action(GUI::CommonActions::make_quit_action([](auto&) { - GUI::Application::the()->quit(); - })); - return {}; -} - -ErrorOr HackStudioWidget::create_edit_menu(GUI::Window& window) -{ - auto edit_menu = window.add_menu("&Edit"_string); - auto icon = TRY(Gfx::Bitmap::load_from_file("/res/icons/16x16/find.png"sv)); - edit_menu->add_action(GUI::Action::create("&Find in Files...", { Mod_Ctrl | Mod_Shift, Key_F }, icon, [this](auto&) { - reveal_action_tab(*m_find_in_files_widget); - m_find_in_files_widget->focus_textbox_and_select_all(); - })); - - edit_menu->add_separator(); - - auto vim_emulation_setting_action = GUI::Action::create_checkable("&Vim Emulation", { Mod_Ctrl | Mod_Shift | Mod_Alt, Key_V }, [this](auto& action) { - if (action.is_checked()) { - for (auto& editor_wrapper : m_all_editor_wrappers) - editor_wrapper->editor().set_editing_engine(make()); - } else { - for (auto& editor_wrapper : m_all_editor_wrappers) - editor_wrapper->editor().set_editing_engine(make()); - } - }); - vim_emulation_setting_action->set_checked(false); - edit_menu->add_action(vim_emulation_setting_action); - - auto auto_save_before_build_or_run_action = GUI::Action::create_checkable("&Auto Save before Build or Run", [this](auto& action) { - m_auto_save_before_build_or_run = action.is_checked(); - Config::write_bool("HackStudio"sv, "Global"sv, "AutoSaveBeforeBuildOrRun"sv, m_auto_save_before_build_or_run); - }); - m_auto_save_before_build_or_run = Config::read_bool("HackStudio"sv, "Global"sv, "AutoSaveBeforeBuildOrRun"sv, false); - auto_save_before_build_or_run_action->set_checked(m_auto_save_before_build_or_run); - edit_menu->add_action(auto_save_before_build_or_run_action); - - edit_menu->add_separator(); - edit_menu->add_action(*m_open_project_configuration_action); - return {}; -} - -void HackStudioWidget::create_build_menu(GUI::Window& window) -{ - auto build_menu = window.add_menu("&Build"_string); - build_menu->add_action(*m_build_action); - build_menu->add_separator(); - build_menu->add_action(*m_run_action); - build_menu->add_action(*m_stop_action); - build_menu->add_separator(); - build_menu->add_action(*m_debug_action); -} - -ErrorOr HackStudioWidget::create_view_menu(GUI::Window& window) -{ - auto hide_action_tabs_action = GUI::Action::create("&Hide Action Tabs", { Mod_Ctrl | Mod_Shift, Key_X }, [this](auto&) { - hide_action_tabs(); - }); - auto open_locator_action = GUI::Action::create("Open &Locator", { Mod_Ctrl, Key_K }, [this](auto&) { - m_locator->open(); - }); - auto show_dotfiles_action = GUI::Action::create_checkable("S&how Dotfiles", { Mod_Ctrl, Key_H }, [&](auto& checked) { - project().model().set_should_show_dotfiles(checked.is_checked()); - Config::write_bool("HackStudio"sv, "Global"sv, "ShowDotfiles"sv, checked.is_checked()); - }); - show_dotfiles_action->set_checked(Config::read_bool("HackStudio"sv, "Global"sv, "ShowDotfiles"sv, false)); - - auto view_menu = window.add_menu("&View"_string); - view_menu->add_action(hide_action_tabs_action); - view_menu->add_action(open_locator_action); - view_menu->add_action(show_dotfiles_action); - m_toggle_semantic_highlighting_action = TRY(create_toggle_syntax_highlighting_mode_action()); - view_menu->add_action(*m_toggle_semantic_highlighting_action); - m_toggle_view_file_in_single_click_action = TRY(create_toggle_open_file_in_single_click_action()); - view_menu->add_action(*m_toggle_view_file_in_single_click_action); - view_menu->add_separator(); - - m_wrapping_mode_actions.set_exclusive(true); - auto wrapping_mode_menu = view_menu->add_submenu("&Wrapping Mode"_string); - m_no_wrapping_action = GUI::Action::create_checkable("&No Wrapping", [&](auto&) { - m_wrapping_mode = GUI::TextEditor::WrappingMode::NoWrap; - for (auto& wrapper : m_all_editor_wrappers) - wrapper->editor().set_wrapping_mode(GUI::TextEditor::WrappingMode::NoWrap); - }); - m_wrap_anywhere_action = GUI::Action::create_checkable("Wrap &Anywhere", [&](auto&) { - m_wrapping_mode = GUI::TextEditor::WrappingMode::WrapAnywhere; - for (auto& wrapper : m_all_editor_wrappers) - wrapper->editor().set_wrapping_mode(GUI::TextEditor::WrappingMode::WrapAnywhere); - }); - m_wrap_at_words_action = GUI::Action::create_checkable("Wrap at &Words", [&](auto&) { - m_wrapping_mode = GUI::TextEditor::WrappingMode::WrapAtWords; - for (auto& wrapper : m_all_editor_wrappers) - wrapper->editor().set_wrapping_mode(GUI::TextEditor::WrappingMode::WrapAtWords); - }); - - m_wrapping_mode_actions.add_action(*m_no_wrapping_action); - m_wrapping_mode_actions.add_action(*m_wrap_anywhere_action); - m_wrapping_mode_actions.add_action(*m_wrap_at_words_action); - - wrapping_mode_menu->add_action(*m_no_wrapping_action); - wrapping_mode_menu->add_action(*m_wrap_anywhere_action); - wrapping_mode_menu->add_action(*m_wrap_at_words_action); - - switch (m_wrapping_mode) { - case GUI::TextEditor::NoWrap: - m_no_wrapping_action->set_checked(true); - break; - case GUI::TextEditor::WrapAtWords: - m_wrap_at_words_action->set_checked(true); - break; - case GUI::TextEditor::WrapAnywhere: - m_wrap_anywhere_action->set_checked(true); - break; - } - - auto icon = TRY(Gfx::Bitmap::load_from_file("/res/icons/16x16/app-font-editor.png"sv)); - m_editor_font_action = GUI::Action::create("Change &Font...", icon, - [&](auto&) { - auto picker = GUI::FontPicker::construct(&window, m_editor_font, false); - if (picker->exec() == GUI::Dialog::ExecResult::OK) { - change_editor_font(picker->font()); - } - }); - view_menu->add_action(*m_editor_font_action); - - view_menu->add_separator(); - view_menu->add_action(*m_add_editor_tab_widget_action); - view_menu->add_action(*m_add_editor_action); - view_menu->add_action(*m_remove_current_editor_action); - view_menu->add_action(*m_add_terminal_action); - view_menu->add_action(*m_remove_current_terminal_action); - - view_menu->add_separator(); - - TRY(create_location_history_actions()); - view_menu->add_action(*m_locations_history_back_action); - view_menu->add_action(*m_locations_history_forward_action); - - view_menu->add_separator(); - - view_menu->add_action(GUI::CommonActions::make_fullscreen_action([&](auto&) { - window.set_fullscreen(!window.is_fullscreen()); - })); - return {}; -} - -void HackStudioWidget::create_help_menu(GUI::Window& window) -{ - auto help_menu = window.add_menu("&Help"_string); - help_menu->add_action(GUI::CommonActions::make_command_palette_action(&window)); - help_menu->add_action(GUI::CommonActions::make_about_action("Hack Studio"_string, GUI::Icon::default_icon("app-hack-studio"sv), &window)); -} - -ErrorOr> HackStudioWidget::create_stop_action() -{ - auto icon = TRY(Gfx::Bitmap::load_from_file("/res/icons/16x16/program-stop.png"sv)); - auto action = GUI::Action::create("&Stop", icon, [this](auto&) { - if (!Debugger::the().session()) { - if (auto result = m_terminal_wrapper->kill_running_command(); result.is_error()) - warnln("{}", result.error()); - return; - } - - Debugger::the().stop(); - }); - - action->set_enabled(false); - return action; -} - -ErrorOr HackStudioWidget::initialize_menubar(GUI::Window& window) -{ - TRY(create_file_menu(window)); - TRY(create_edit_menu(window)); - create_build_menu(window); - TRY(create_view_menu(window)); - create_help_menu(window); - return {}; -} - -void HackStudioWidget::update_statusbar() -{ - StringBuilder builder; - if (current_editor().has_selection()) { - ByteString selected_text = current_editor().selected_text(); - auto word_count = current_editor().number_of_selected_words(); - builder.appendff("Selected: {:'d} {} ({:'d} {})", selected_text.length(), selected_text.length() == 1 ? "character" : "characters", word_count, word_count != 1 ? "words" : "word"); - } - - m_statusbar->set_text(0, builder.to_string().release_value_but_fixme_should_propagate_errors()); - m_statusbar->set_text(1, String::from_utf8(Syntax::language_to_string(current_editor_wrapper().editor().code_document().language().value_or(Syntax::Language::PlainText))).release_value_but_fixme_should_propagate_errors()); - m_statusbar->set_text(2, String::formatted("Ln {:'d} Col {:'d}", current_editor().cursor().line() + 1, current_editor().cursor().column()).release_value_but_fixme_should_propagate_errors()); -} - -void HackStudioWidget::handle_external_file_deletion(ByteString const& filepath) -{ - close_file_in_all_editors(filepath); -} - -void HackStudioWidget::stop_debugger_if_running() -{ - if (!m_debugger_thread.is_null()) { - Debugger::the().stop(); - dbgln("Waiting for debugger thread to terminate"); - auto rc = m_debugger_thread->join(); - if (rc.is_error()) { - warnln("pthread_join: {}", strerror(rc.error().value())); - dbgln("error joining debugger thread"); - } - } -} - -void HackStudioWidget::close_current_project() -{ - m_editors_splitter->remove_all_children(); - m_all_editor_tab_widgets.clear(); - m_all_editor_wrappers.clear(); - add_new_editor_tab_widget(*m_editors_splitter); - m_open_files.clear(); - m_open_files_vector.clear(); - m_find_in_files_widget->reset(); - m_todo_entries_widget->clear(); - m_terminal_wrapper->clear_including_history(); - stop_debugger_if_running(); - update_gml_preview(); -} - -HackStudioWidget::~HackStudioWidget() -{ - stop_debugger_if_running(); -} - -HackStudioWidget::ContinueDecision HackStudioWidget::warn_unsaved_changes(ByteString const& prompt) -{ - if (!any_document_is_dirty()) - return ContinueDecision::Yes; - - auto result = GUI::MessageBox::show(window(), prompt, "Unsaved Changes"sv, GUI::MessageBox::Type::Warning, GUI::MessageBox::InputType::YesNoCancel); - - if (result == GUI::MessageBox::ExecResult::Cancel) - return ContinueDecision::No; - - if (result == GUI::MessageBox::ExecResult::Yes) { - if (!save_file_changes()) - return ContinueDecision::No; - } - - return ContinueDecision::Yes; -} - -bool HackStudioWidget::save_file_changes() -{ - for (auto& editor_wrapper : m_all_editor_wrappers) { - if (editor_wrapper->editor().document().is_modified()) { - if (!editor_wrapper->save()) - return false; - } - } - - return true; -} - -bool HackStudioWidget::any_document_is_dirty() const -{ - return any_of(m_all_editor_wrappers, [](auto& editor_wrapper) { - return editor_wrapper->editor().document().is_modified(); - }); -} - -void HackStudioWidget::update_gml_preview() -{ - auto gml_content = current_editor_wrapper().filename().ends_with(".gml"sv) ? current_editor_wrapper().editor().text() : ByteString::empty(); - m_gml_preview_widget->load_gml(gml_content); -} - -void HackStudioWidget::update_tree_view() -{ - auto index = m_project->model().index(m_current_editor_wrapper->filename(), GUI::FileSystemModel::Column::Name); - if (index.is_valid()) { - m_project_tree_view->expand_all_parents_of(index); - m_project_tree_view->set_cursor(index, GUI::AbstractView::SelectionUpdate::Set); - } -} - -void HackStudioWidget::update_toolbar_actions() -{ - m_copy_button->set_action(current_editor().copy_action()); - m_paste_button->set_action(current_editor().paste_action()); - m_cut_button->set_action(current_editor().cut_action()); -} - -void HackStudioWidget::update_window_title() -{ - window()->set_title(ByteString::formatted("{} - {} - Hack Studio", m_current_editor_wrapper->filename_title(), m_project->name())); - window()->set_modified(any_document_is_dirty()); -} - -void HackStudioWidget::update_current_editor_title() -{ - current_editor_tab_widget().set_tab_title(current_editor_wrapper(), String::from_byte_string(current_editor_wrapper().filename_title()).release_value_but_fixme_should_propagate_errors()); -} - -void HackStudioWidget::on_cursor_change() -{ - update_statusbar(); - if (current_editor_wrapper().filename().is_empty()) - return; - - auto current_location = current_project_location(); - - if (m_locations_history_end_index != 0) { - auto last = m_locations_history[m_locations_history_end_index - 1]; - if (current_location.filename == last.filename && current_location.line == last.line) - return; - } - - // Clear "Go Forward" locations - VERIFY(m_locations_history_end_index <= m_locations_history.size()); - m_locations_history.remove(m_locations_history_end_index, m_locations_history.size() - m_locations_history_end_index); - - m_locations_history.append(current_location); - - constexpr size_t max_locations = 30; - if (m_locations_history.size() > max_locations) - m_locations_history.take_first(); - - m_locations_history_end_index = m_locations_history.size(); - - update_history_actions(); -} - -ErrorOr HackStudioWidget::create_location_history_actions() -{ - { - auto icon = TRY(Gfx::Bitmap::load_from_file("/res/icons/16x16/go-back.png"sv)); - m_locations_history_back_action = GUI::Action::create("Go Back", { Mod_Alt | Mod_Shift, Key_Left }, icon, [this](auto&) { - if (m_locations_history_end_index <= 1) - return; - - auto location = m_locations_history[m_locations_history_end_index - 2]; - --m_locations_history_end_index; - - m_locations_history_disabled = true; - open_file(location.filename, location.line, location.column); - m_locations_history_disabled = false; - - update_history_actions(); - }); - } - - { - auto icon = TRY(Gfx::Bitmap::load_from_file("/res/icons/16x16/go-forward.png"sv)); - m_locations_history_forward_action = GUI::Action::create("Go Forward", { Mod_Alt | Mod_Shift, Key_Right }, icon, [this](auto&) { - if (m_locations_history_end_index == m_locations_history.size()) - return; - - auto location = m_locations_history[m_locations_history_end_index]; - ++m_locations_history_end_index; - - m_locations_history_disabled = true; - open_file(location.filename, location.line, location.column); - m_locations_history_disabled = false; - - update_history_actions(); - }); - } - m_locations_history_forward_action->set_enabled(false); - return {}; -} - -ErrorOr> HackStudioWidget::create_open_project_configuration_action() -{ - auto icon = TRY(Gfx::Bitmap::load_from_file("/res/icons/16x16/settings.png"sv)); - return GUI::Action::create("Project Configuration", icon, [&](auto&) { - auto parent_directory = LexicalPath::dirname(Project::config_file_path); - auto absolute_config_file_path = LexicalPath::absolute_path(m_project->root_path(), Project::config_file_path); - - ByteString formatted_error_string_holder; - auto save_configuration_or_error = [&]() -> ErrorOr { - if (FileSystem::exists(absolute_config_file_path)) - return {}; - - if (FileSystem::exists(parent_directory) && !FileSystem::is_directory(parent_directory)) { - formatted_error_string_holder = ByteString::formatted("Cannot create the '{}' directory because there is already a file with that name", parent_directory); - return Error::from_string_view(formatted_error_string_holder.view()); - } - - auto maybe_error = Core::System::mkdir(LexicalPath::absolute_path(m_project->root_path(), parent_directory), 0755); - if (maybe_error.is_error() && maybe_error.error().code() != EEXIST) - return maybe_error.release_error(); - - auto file = TRY(Core::File::open(absolute_config_file_path, Core::File::OpenMode::Write)); - TRY(file->write_until_depleted( - "{\n" - " \"build_command\": \"your build command here\",\n" - " \"run_command\": \"your run command here\"\n" - "}\n"sv.bytes())); - return {}; - }(); - if (save_configuration_or_error.is_error()) { - GUI::MessageBox::show_error(window(), ByteString::formatted("Saving configuration failed: {}.", save_configuration_or_error.error())); - return; - } - - open_file(Project::config_file_path); - }); -} - -HackStudioWidget::ProjectLocation HackStudioWidget::current_project_location() const -{ - return ProjectLocation { current_editor_wrapper().filename(), current_editor().cursor().line(), current_editor().cursor().column() }; -} - -void HackStudioWidget::update_history_actions() -{ - if (m_locations_history_end_index <= 1) - m_locations_history_back_action->set_enabled(false); - else - m_locations_history_back_action->set_enabled(true); - - if (m_locations_history_end_index == m_locations_history.size()) - m_locations_history_forward_action->set_enabled(false); - else - m_locations_history_forward_action->set_enabled(true); -} - -RefPtr HackStudioWidget::read_editor_font_from_config() -{ - auto font_family = Config::read_string("HackStudio"sv, "EditorFont"sv, "Family"sv); - auto font_variant = Config::read_string("HackStudio"sv, "EditorFont"sv, "Variant"sv); - auto font_size = Config::read_i32("HackStudio"sv, "EditorFont"sv, "Size"sv); - - auto font = Gfx::FontDatabase::the().get(MUST(FlyString::from_deprecated_fly_string(font_family)), FlyString::from_deprecated_fly_string(font_variant).release_value_but_fixme_should_propagate_errors(), font_size); - if (font.is_null()) - return Gfx::FontDatabase::the().default_fixed_width_font(); - - return font; -} - -void HackStudioWidget::change_editor_font(RefPtr font) -{ - m_editor_font = move(font); - for (auto& editor_wrapper : m_all_editor_wrappers) { - editor_wrapper->editor().set_font(*m_editor_font); - } - - Config::write_string("HackStudio"sv, "EditorFont"sv, "Family"sv, m_editor_font->family()); - Config::write_string("HackStudio"sv, "EditorFont"sv, "Variant"sv, m_editor_font->variant()); - Config::write_i32("HackStudio"sv, "EditorFont"sv, "Size"sv, m_editor_font->presentation_size()); -} - -void HackStudioWidget::open_coredump(StringView coredump_path) -{ - open_project("/usr/src/serenity"); - m_mode = Mode::Coredump; - - m_coredump_inspector = Coredump::Inspector::create(coredump_path, [this](float progress) { - window()->set_progress(progress * 100); - }); - window()->set_progress(0); - - if (m_coredump_inspector) { - m_debug_info_widget->update_state(*m_coredump_inspector, m_coredump_inspector->get_registers()); - reveal_action_tab(*m_debug_info_widget); - } -} - -void HackStudioWidget::debug_process(pid_t pid) -{ - open_project("/usr/src/serenity"); - Debugger::the().set_pid_to_attach(pid); - - m_debugger_thread = Threading::Thread::construct(Debugger::start_static); - m_debugger_thread->start(); - m_stop_action->set_enabled(true); - m_run_action->set_enabled(false); - - for (auto& editor_wrapper : m_all_editor_wrappers) { - editor_wrapper->set_debug_mode(true); - } -} - -void HackStudioWidget::for_each_open_file(Function func) -{ - for (auto& open_file : m_open_files) { - func(*open_file.value); - } -} - -ErrorOr> HackStudioWidget::create_toggle_syntax_highlighting_mode_action() -{ - auto icon = TRY(Gfx::Bitmap::load_from_file("/res/icons/16x16/filetype-cplusplus.png"sv)); - auto action = GUI::Action::create_checkable("&Semantic Highlighting", icon, [this](auto& action) { - for (auto& editor_wrapper : m_all_editor_wrappers) - editor_wrapper->editor().set_semantic_syntax_highlighting(action.is_checked()); - }); - - return action; -} - -bool HackStudioWidget::semantic_syntax_highlighting_is_enabled() const -{ - return m_toggle_semantic_highlighting_action->is_checked(); -} -} diff --git a/Userland/DevTools/HackStudio/HackStudioWidget.h b/Userland/DevTools/HackStudio/HackStudioWidget.h deleted file mode 100644 index 17583616bc8..00000000000 --- a/Userland/DevTools/HackStudio/HackStudioWidget.h +++ /dev/null @@ -1,274 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * Copyright (c) 2020-2022, Itamar S. - * Copyright (c) 2020-2021, the SerenityOS developers. - * Copyright (c) 2024, Sam Atkins - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include "ClassViewWidget.h" -#include "Debugger/DebugInfoWidget.h" -#include "Debugger/DisassemblyWidget.h" -#include "EditorWrapper.h" -#include "FindInFilesWidget.h" -#include "GMLPreviewWidget.h" -#include "Git/DiffViewer.h" -#include "Git/GitWidget.h" -#include "Locator.h" -#include "Project.h" -#include "ProjectBuilder.h" -#include "ProjectFile.h" -#include "TerminalWrapper.h" -#include "ToDoEntriesWidget.h" -#include -#include -#include -#include -#include -#include -#include -#include - -namespace HackStudio { - -class HackStudioWidget : public GUI::Widget { - C_OBJECT_ABSTRACT(HackStudioWidget) - -public: - static ErrorOr> create(ByteString path_to_project); - virtual ~HackStudioWidget() override; - - bool open_file(ByteString const& filename, size_t line = 0, size_t column = 0); - void close_file_in_all_editors(ByteString const& filename); - - void update_actions(); - Project& project(); - GUI::TextEditor& current_editor(); - GUI::TextEditor const& current_editor() const; - EditorWrapper& current_editor_wrapper(); - EditorWrapper const& current_editor_wrapper() const; - void set_current_editor_wrapper(RefPtr); - void set_current_editor_tab_widget(RefPtr); - - GUI::TabWidget& current_editor_tab_widget(); - GUI::TabWidget const& current_editor_tab_widget() const; - - ByteString const& active_file() const { return m_current_editor_wrapper->filename(); } - ErrorOr initialize_menubar(GUI::Window&); - - Locator& locator() - { - VERIFY(m_locator); - return *m_locator; - } - - enum class ContinueDecision { - No, - Yes - }; - ContinueDecision warn_unsaved_changes(ByteString const& prompt); - - enum class Mode { - Code, - Coredump - }; - - void open_coredump(StringView coredump_path); - void debug_process(pid_t pid); - void for_each_open_file(Function); - bool semantic_syntax_highlighting_is_enabled() const; - - static Vector read_recent_projects(); - - void update_current_editor_title(); - void update_window_title(); - -private: - static ByteString get_full_path_of_serenity_source(ByteString const& file); - ByteString get_absolute_path(ByteString const&) const; - Vector selected_file_paths() const; - - void open_project(ByteString const& root_path); - - enum class EditMode { - Text, - Diff, - }; - - void set_edit_mode(EditMode); - - ErrorOr> create_project_tree_view_context_menu(); - ErrorOr> create_new_file_action(ByteString const& label, ByteString const& icon, ByteString const& extension); - ErrorOr> create_new_directory_action(); - ErrorOr> create_open_selected_action(); - NonnullRefPtr create_delete_action(); - ErrorOr> create_new_project_action(); - NonnullRefPtr create_switch_to_next_editor_tab_widget_action(); - NonnullRefPtr create_switch_to_next_editor_action(); - NonnullRefPtr create_switch_to_previous_editor_action(); - NonnullRefPtr create_remove_current_editor_tab_widget_action(); - ErrorOr> create_remove_current_editor_action(); - ErrorOr> create_open_action(); - ErrorOr> create_toggle_open_file_in_single_click_action(); - NonnullRefPtr create_save_action(); - NonnullRefPtr create_save_as_action(); - NonnullRefPtr create_show_in_file_manager_action(); - NonnullRefPtr create_copy_relative_path_action(); - NonnullRefPtr create_copy_full_path_action(); - NonnullRefPtr create_add_editor_tab_widget_action(); - ErrorOr> create_add_editor_action(); - ErrorOr> create_add_terminal_action(); - ErrorOr> create_remove_current_terminal_action(); - ErrorOr> create_debug_action(); - ErrorOr> create_build_action(); - ErrorOr> create_run_action(); - ErrorOr> create_stop_action(); - ErrorOr> create_toggle_syntax_highlighting_mode_action(); - ErrorOr> create_open_project_configuration_action(); - ErrorOr create_location_history_actions(); - - void add_new_editor_tab_widget(GUI::Widget& parent); - void add_new_editor(GUI::TabWidget& parent); - RefPtr get_editor_of_file(ByteString const& filename); - ByteString get_project_executable_path() const; - - void on_action_tab_change(); - void reveal_action_tab(GUI::Widget&); - void initialize_debugger(); - void update_statusbar(); - - void handle_external_file_deletion(ByteString const& filepath); - void stop_debugger_if_running(); - void close_current_project(); - - void create_open_files_view(GUI::Widget& parent); - void create_toolbar(GUI::Widget& parent); - ErrorOr create_action_tab(GUI::Widget& parent); - ErrorOr create_file_menu(GUI::Window&); - ErrorOr create_edit_menu(GUI::Window&); - void create_build_menu(GUI::Window&); - ErrorOr create_view_menu(GUI::Window&); - void create_help_menu(GUI::Window&); - void create_project_tab(GUI::Widget& parent); - void configure_project_tree_view(); - - void run(); - void build(); - - void hide_action_tabs(); - bool any_document_is_dirty() const; - - void update_gml_preview(); - void update_tree_view(); - void update_toolbar_actions(); - void on_cursor_change(); - void file_renamed(ByteString const& old_name, ByteString const& new_name); - bool save_file_changes(); - - struct ProjectLocation { - ByteString filename; - size_t line { 0 }; - size_t column { 0 }; - }; - - ProjectLocation current_project_location() const; - void update_history_actions(); - - Vector> m_all_editor_wrappers; - RefPtr m_current_editor_wrapper; - Vector> m_all_editor_tab_widgets; - RefPtr m_current_editor_tab_widget; - - HashMap> m_open_files; - RefPtr m_file_watcher; - Vector m_open_files_vector; // NOTE: This contains the keys from m_open_files and m_file_watchers - - OwnPtr m_project; - - Vector m_locations_history; - // This index is the boundary between the "Go Back" and "Go Forward" locations. - // It always points at one past the current location in the list. - size_t m_locations_history_end_index { 0 }; - bool m_locations_history_disabled { false }; - - bool m_auto_save_before_build_or_run { false }; - - RefPtr m_project_tree_view; - RefPtr m_open_files_view; - RefPtr m_right_hand_splitter; - RefPtr m_right_hand_stack; - RefPtr m_editors_splitter; - RefPtr m_diff_viewer; - RefPtr m_git_widget; - RefPtr m_gml_preview_widget; - RefPtr m_class_view; - RefPtr m_project_tree_view_context_menu; - RefPtr m_statusbar; - RefPtr m_action_tab_widget; - RefPtr m_project_tab; - RefPtr m_terminal_wrapper; - RefPtr m_locator; - RefPtr m_find_in_files_widget; - RefPtr m_todo_entries_widget; - RefPtr m_debug_info_widget; - RefPtr m_disassembly_widget; - RefPtr m_debugger_thread; - RefPtr m_current_editor_in_execution; - RefPtr m_recent_projects_submenu { nullptr }; - - Vector> m_new_file_actions; - RefPtr m_new_plain_file_action; - - RefPtr m_new_directory_action; - RefPtr m_open_selected_action; - RefPtr m_show_in_file_manager_action; - RefPtr m_copy_relative_path_action; - RefPtr m_copy_full_path_action; - RefPtr m_delete_action; - RefPtr m_tree_view_rename_action; - RefPtr m_new_project_action; - RefPtr m_switch_to_next_editor_tab_widget; - RefPtr m_switch_to_next_editor; - RefPtr m_switch_to_previous_editor; - RefPtr m_remove_current_editor_tab_widget_action; - RefPtr m_remove_current_editor_action; - RefPtr m_open_action; - RefPtr m_save_action; - RefPtr m_save_as_action; - RefPtr m_add_editor_action; - RefPtr m_add_editor_tab_widget_action; - RefPtr m_add_terminal_action; - RefPtr m_remove_current_terminal_action; - RefPtr m_stop_action; - RefPtr m_debug_action; - RefPtr m_build_action; - RefPtr m_run_action; - RefPtr m_locations_history_back_action; - RefPtr m_locations_history_forward_action; - RefPtr m_toggle_semantic_highlighting_action; - RefPtr m_toggle_view_file_in_single_click_action; - RefPtr m_open_project_configuration_action; - - RefPtr read_editor_font_from_config(); - void change_editor_font(RefPtr); - RefPtr m_editor_font; - RefPtr m_editor_font_action; - - GUI::TextEditor::WrappingMode m_wrapping_mode { GUI::TextEditor::NoWrap }; - GUI::ActionGroup m_wrapping_mode_actions; - RefPtr m_no_wrapping_action; - RefPtr m_wrap_anywhere_action; - RefPtr m_wrap_at_words_action; - - RefPtr m_cut_button; - RefPtr m_paste_button; - RefPtr m_copy_button; - - Mode m_mode { Mode::Code }; - OwnPtr m_coredump_inspector; - OwnPtr m_project_builder; -}; -} diff --git a/Userland/DevTools/HackStudio/LanguageClient.cpp b/Userland/DevTools/HackStudio/LanguageClient.cpp deleted file mode 100644 index 58af26c956d..00000000000 --- a/Userland/DevTools/HackStudio/LanguageClient.cpp +++ /dev/null @@ -1,279 +0,0 @@ -/* - * Copyright (c) 2020, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "LanguageClient.h" -#include "HackStudio.h" -#include "ProjectDeclarations.h" -#include "ToDoEntries.h" -#include -#include -#include - -namespace HackStudio { - -void ConnectionToServer::auto_complete_suggestions(Vector const& suggestions) -{ - if (!m_current_language_client) { - dbgln("Language Server connection has no attached language client"); - return; - } - m_current_language_client->provide_autocomplete_suggestions(suggestions); -} - -void ConnectionToServer::declaration_location(CodeComprehension::ProjectLocation const& location) -{ - if (!m_current_language_client) { - dbgln("Language Server connection has no attached language client"); - return; - } - m_current_language_client->declaration_found(location.file, location.line, location.column); -} - -void ConnectionToServer::parameters_hint_result(Vector const& params, int argument_index) -{ - if (!m_current_language_client) { - dbgln("Language Server connection has no attached language client"); - return; - } - - VERIFY(argument_index >= 0); - m_current_language_client->parameters_hint_result(params, static_cast(argument_index)); -} - -void ConnectionToServer::tokens_info_result(Vector const& tokens_info) -{ - if (!m_current_language_client) { - dbgln("Language Server connection has no attached language client"); - return; - } - VERIFY(m_current_language_client->on_tokens_info_result); - m_current_language_client->on_tokens_info_result(tokens_info); -} - -void ConnectionToServer::die() -{ - VERIFY(m_wrapper); - // Wrapper destructs us here - m_wrapper->on_crash(); -} - -void LanguageClient::open_file(ByteString const& path, int fd) -{ - if (!m_connection_wrapper.connection()) - return; - m_connection_wrapper.connection()->async_file_opened(path, MUST(IPC::File::clone_fd(fd))); -} - -void LanguageClient::set_file_content(ByteString const& path, ByteString const& content) -{ - if (!m_connection_wrapper.connection()) - return; - m_connection_wrapper.connection()->async_set_file_content(path, content); -} - -void LanguageClient::insert_text(ByteString const& path, ByteString const& text, size_t line, size_t column) -{ - if (!m_connection_wrapper.connection()) - return; - m_connection_wrapper.connection()->async_file_edit_insert_text(path, text, line, column); -} - -void LanguageClient::remove_text(ByteString const& path, size_t from_line, size_t from_column, size_t to_line, size_t to_column) -{ - if (!m_connection_wrapper.connection()) - return; - m_connection_wrapper.connection()->async_file_edit_remove_text(path, from_line, from_column, to_line, to_column); -} - -void LanguageClient::request_autocomplete(ByteString const& path, size_t cursor_line, size_t cursor_column) -{ - if (!m_connection_wrapper.connection()) - return; - set_active_client(); - m_connection_wrapper.connection()->async_auto_complete_suggestions(CodeComprehension::ProjectLocation { path, cursor_line, cursor_column }); -} - -void LanguageClient::provide_autocomplete_suggestions(Vector const& suggestions) const -{ - if (on_autocomplete_suggestions) - on_autocomplete_suggestions(suggestions); - - // Otherwise, drop it on the floor :shrug: -} - -void LanguageClient::set_active_client() -{ - if (!m_connection_wrapper.connection()) - return; - m_connection_wrapper.set_active_client(*this); -} - -bool LanguageClient::is_active_client() const -{ - if (!m_connection_wrapper.connection()) - return false; - return m_connection_wrapper.connection()->active_client() == this; -} - -HashMap> ConnectionToServerInstances::s_instance_for_language; - -void ConnectionToServer::declarations_in_document(ByteString const& filename, Vector const& declarations) -{ - ProjectDeclarations::the().set_declared_symbols(filename, declarations); -} - -void ConnectionToServer::todo_entries_in_document(ByteString const& filename, Vector const& todo_entries) -{ - ToDoEntries::the().set_entries(filename, move(todo_entries)); -} - -void LanguageClient::search_declaration(ByteString const& path, size_t line, size_t column) -{ - if (!m_connection_wrapper.connection()) - return; - set_active_client(); - m_connection_wrapper.connection()->async_find_declaration(CodeComprehension::ProjectLocation { path, line, column }); -} - -void LanguageClient::get_parameters_hint(ByteString const& path, size_t line, size_t column) -{ - if (!m_connection_wrapper.connection()) - return; - set_active_client(); - m_connection_wrapper.connection()->async_get_parameters_hint(CodeComprehension::ProjectLocation { path, line, column }); -} - -void LanguageClient::get_tokens_info(ByteString const& filename) -{ - if (!m_connection_wrapper.connection()) - return; - VERIFY(is_active_client()); - m_connection_wrapper.connection()->async_get_tokens_info(filename); -} - -void LanguageClient::declaration_found(ByteString const& file, size_t line, size_t column) const -{ - if (!on_declaration_found) { - dbgln("on_declaration_found callback is not set"); - return; - } - on_declaration_found(file, line, column); -} - -void LanguageClient::parameters_hint_result(Vector const& params, size_t argument_index) const -{ - if (!on_function_parameters_hint_result) { - dbgln("on_function_parameters_hint_result callback is not set"); - return; - } - on_function_parameters_hint_result(params, argument_index); -} - -void ConnectionToServerInstances::set_instance_for_language(ByteString const& language_name, NonnullOwnPtr&& connection_wrapper) -{ - s_instance_for_language.set(language_name, move(connection_wrapper)); -} - -void ConnectionToServerInstances::remove_instance_for_language(ByteString const& language_name) -{ - s_instance_for_language.remove(language_name); -} - -ConnectionToServerWrapper* ConnectionToServerInstances::get_instance_wrapper(ByteString const& language_name) -{ - if (auto instance = s_instance_for_language.get(language_name); instance.has_value()) { - return const_cast(instance.value()); - } - return nullptr; -} - -void ConnectionToServerWrapper::on_crash() -{ - using namespace AK::TimeLiterals; - - show_crash_notification(); - m_connection.clear(); - - static constexpr Duration max_crash_frequency = 10_sec; - if (m_last_crash_timer.is_valid() && m_last_crash_timer.elapsed_time() < max_crash_frequency) { - dbgln("LanguageServer crash frequency is too high"); - m_respawn_allowed = false; - - show_frequent_crashes_notification(); - } else { - m_last_crash_timer.start(); - try_respawn_connection(); - } -} -void ConnectionToServerWrapper::show_frequent_crashes_notification() const -{ - auto notification = GUI::Notification::construct(); - notification->set_icon(Gfx::Bitmap::load_from_file("/res/icons/32x32/app-hack-studio.png"sv).release_value_but_fixme_should_propagate_errors()); - notification->set_title("LanguageServer Crashes too much!"_string); - notification->set_text("LanguageServer aided features will not be available in this session"_string); - notification->show(); -} -void ConnectionToServerWrapper::show_crash_notification() const -{ - auto notification = GUI::Notification::construct(); - notification->set_icon(Gfx::Bitmap::load_from_file("/res/icons/32x32/app-hack-studio.png"sv).release_value_but_fixme_should_propagate_errors()); - notification->set_title("Oops!"_string); - notification->set_text("LanguageServer has crashed"_string); - notification->show(); -} - -ConnectionToServerWrapper::ConnectionToServerWrapper(ByteString const& language_name, Function()> connection_creator) - : m_language(Syntax::language_from_name(language_name).value()) - , m_connection_creator(move(connection_creator)) -{ - create_connection(); -} - -void ConnectionToServerWrapper::create_connection() -{ - VERIFY(m_connection.is_null()); - m_connection = m_connection_creator(); - m_connection->set_wrapper(*this); -} - -ConnectionToServer* ConnectionToServerWrapper::connection() -{ - return m_connection.ptr(); -} - -void ConnectionToServerWrapper::attach(LanguageClient& client) -{ - m_connection->m_current_language_client = &client; -} - -void ConnectionToServerWrapper::detach() -{ - m_connection->m_current_language_client.clear(); -} - -void ConnectionToServerWrapper::set_active_client(LanguageClient& client) -{ - m_connection->m_current_language_client = &client; -} - -void ConnectionToServerWrapper::try_respawn_connection() -{ - if (!m_respawn_allowed) - return; - - dbgln("Respawning ConnectionToServer"); - create_connection(); - - // After respawning the language-server, we have to send the content of open project files - // so the server's FileDB will be up-to-date. - for_each_open_file([this](ProjectFile const& file) { - if (file.code_document().language() != m_language) - return; - m_connection->async_set_file_content(file.code_document().file_path(), file.document().text()); - }); -} - -} diff --git a/Userland/DevTools/HackStudio/LanguageClient.h b/Userland/DevTools/HackStudio/LanguageClient.h deleted file mode 100644 index b2f1dcb573a..00000000000 --- a/Userland/DevTools/HackStudio/LanguageClient.h +++ /dev/null @@ -1,174 +0,0 @@ -/* - * Copyright (c) 2020, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include "AutoCompleteResponse.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -namespace HackStudio { - -class LanguageClient; -class ConnectionToServerWrapper; - -class ConnectionToServer - : public IPC::ConnectionToServer - , public LanguageClientEndpoint { - friend class ConnectionToServerWrapper; - -public: - ConnectionToServer(NonnullOwnPtr socket, ByteString const& project_path) - : IPC::ConnectionToServer(*this, move(socket)) - { - m_project_path = project_path; - async_greet(m_project_path); - } - - WeakPtr language_client() { return m_current_language_client; } - ByteString const& project_path() const { return m_project_path; } - - virtual void die() override; - - LanguageClient const* active_client() const { return !m_current_language_client ? nullptr : m_current_language_client.ptr(); } - -protected: - virtual void auto_complete_suggestions(Vector const&) override; - virtual void declaration_location(CodeComprehension::ProjectLocation const&) override; - virtual void declarations_in_document(ByteString const&, Vector const&) override; - virtual void todo_entries_in_document(ByteString const&, Vector const&) override; - virtual void parameters_hint_result(Vector const&, int index) override; - virtual void tokens_info_result(Vector const&) override; - void set_wrapper(ConnectionToServerWrapper& wrapper) { m_wrapper = &wrapper; } - - ByteString m_project_path; - WeakPtr m_current_language_client; - ConnectionToServerWrapper* m_wrapper { nullptr }; -}; - -class ConnectionToServerWrapper { - AK_MAKE_NONCOPYABLE(ConnectionToServerWrapper); - -public: - explicit ConnectionToServerWrapper(ByteString const& language_name, Function()> connection_creator); - ~ConnectionToServerWrapper() = default; - - template - static ConnectionToServerWrapper& get_or_create(ByteString const& project_path); - - Syntax::Language language() const { return m_language; } - ConnectionToServer* connection(); - void on_crash(); - void try_respawn_connection(); - - void attach(LanguageClient& client); - void detach(); - void set_active_client(LanguageClient& client); - -private: - void create_connection(); - void show_crash_notification() const; - void show_frequent_crashes_notification() const; - - Syntax::Language m_language; - Function()> m_connection_creator; - RefPtr m_connection; - - Core::ElapsedTimer m_last_crash_timer; - bool m_respawn_allowed { true }; -}; - -class ConnectionToServerInstances { -public: - static void set_instance_for_language(ByteString const& language_name, NonnullOwnPtr&& connection_wrapper); - static void remove_instance_for_language(ByteString const& language_name); - - static ConnectionToServerWrapper* get_instance_wrapper(ByteString const& language_name); - -private: - static HashMap> s_instance_for_language; -}; - -class LanguageClient : public Weakable { -public: - explicit LanguageClient(ConnectionToServerWrapper& connection_wrapper) - : m_connection_wrapper(connection_wrapper) - { - if (m_connection_wrapper.connection()) { - m_previous_client = m_connection_wrapper.connection()->language_client(); - VERIFY(m_previous_client.ptr() != this); - m_connection_wrapper.attach(*this); - } - } - - virtual ~LanguageClient() - { - // m_connection_wrapper is nullified if the server crashes - if (m_connection_wrapper.connection()) - m_connection_wrapper.detach(); - - VERIFY(m_previous_client.ptr() != this); - if (m_previous_client && m_connection_wrapper.connection()) - m_connection_wrapper.set_active_client(*m_previous_client); - } - - Syntax::Language language() const { return m_connection_wrapper.language(); } - void set_active_client(); - bool is_active_client() const; - virtual void open_file(ByteString const& path, int fd); - virtual void set_file_content(ByteString const& path, ByteString const& content); - virtual void insert_text(ByteString const& path, ByteString const& text, size_t line, size_t column); - virtual void remove_text(ByteString const& path, size_t from_line, size_t from_column, size_t to_line, size_t to_column); - virtual void request_autocomplete(ByteString const& path, size_t cursor_line, size_t cursor_column); - virtual void search_declaration(ByteString const& path, size_t line, size_t column); - virtual void get_parameters_hint(ByteString const& path, size_t line, size_t column); - virtual void get_tokens_info(ByteString const& filename); - - void provide_autocomplete_suggestions(Vector const&) const; - void declaration_found(ByteString const& file, size_t line, size_t column) const; - void parameters_hint_result(Vector const& params, size_t argument_index) const; - - // Callbacks that get called when the result of a language server query is ready - Function)> on_autocomplete_suggestions; - Function on_declaration_found; - Function const&, size_t)> on_function_parameters_hint_result; - Function const&)> on_tokens_info_result; - -private: - ConnectionToServerWrapper& m_connection_wrapper; - WeakPtr m_previous_client; -}; - -template -static inline NonnullOwnPtr get_language_client(ByteString const& project_path) -{ - return make(ConnectionToServerWrapper::get_or_create(project_path)); -} - -template -ConnectionToServerWrapper& ConnectionToServerWrapper::get_or_create(ByteString const& project_path) -{ - auto* wrapper = ConnectionToServerInstances::get_instance_wrapper(LanguageServerType::language_name()); - if (wrapper) - return *wrapper; - - auto connection_wrapper_ptr = make(LanguageServerType::language_name(), [project_path]() { return LanguageServerType::try_create(project_path).release_value_but_fixme_should_propagate_errors(); }); - auto& connection_wrapper = *connection_wrapper_ptr; - ConnectionToServerInstances::set_instance_for_language(LanguageServerType::language_name(), move(connection_wrapper_ptr)); - return connection_wrapper; -} - -} diff --git a/Userland/DevTools/HackStudio/LanguageClients/CMakeLists.txt b/Userland/DevTools/HackStudio/LanguageClients/CMakeLists.txt deleted file mode 100644 index e06c45f98c2..00000000000 --- a/Userland/DevTools/HackStudio/LanguageClients/CMakeLists.txt +++ /dev/null @@ -1,4 +0,0 @@ -set(GENERATED_SOURCES - ../../LanguageServers/LanguageServerEndpoint.h - ../../LanguageServers/LanguageClientEndpoint.h -) diff --git a/Userland/DevTools/HackStudio/LanguageClients/ConnectionsToServer.h b/Userland/DevTools/HackStudio/LanguageClients/ConnectionsToServer.h deleted file mode 100644 index a511f5e7387..00000000000 --- a/Userland/DevTools/HackStudio/LanguageClients/ConnectionsToServer.h +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright (c) 2020, Itamar S. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include "../LanguageClient.h" -#include -#include -#include -#include - -#define LANGUAGE_CLIENT(language_name_, socket_name) \ - namespace language_name_ { \ - class ConnectionToServer final : public HackStudio::ConnectionToServer { \ - IPC_CLIENT_CONNECTION(ConnectionToServer, "/tmp/session/%sid/portal/language/" socket_name) \ - public: \ - static char const* language_name() \ - { \ - return #language_name_; \ - } \ - \ - private: \ - ConnectionToServer(NonnullOwnPtr socket, ByteString const& project_path) \ - : HackStudio::ConnectionToServer(move(socket), project_path) \ - { \ - } \ - }; \ - } - -namespace LanguageClients { - -LANGUAGE_CLIENT(Cpp, "cpp"sv) -LANGUAGE_CLIENT(Shell, "shell"sv) - -} - -#undef LANGUAGE_CLIENT diff --git a/Userland/DevTools/HackStudio/LanguageServers/CMakeLists.txt b/Userland/DevTools/HackStudio/LanguageServers/CMakeLists.txt deleted file mode 100644 index 64e5a27c917..00000000000 --- a/Userland/DevTools/HackStudio/LanguageServers/CMakeLists.txt +++ /dev/null @@ -1,15 +0,0 @@ -compile_ipc(LanguageServer.ipc LanguageServerEndpoint.h) -compile_ipc(LanguageClient.ipc LanguageClientEndpoint.h) - -set(SOURCES - ConnectionFromClient.cpp - FileDB.cpp) -set(GENERATED_SOURCES - LanguageClientEndpoint.h - LanguageServerEndpoint.h) - -serenity_lib(LibLanguageServer languageserver) -target_link_libraries(LibLanguageServer PRIVATE LibCodeComprehension LibCore) - -add_subdirectory(Cpp) -add_subdirectory(Shell) diff --git a/Userland/DevTools/HackStudio/LanguageServers/ConnectionFromClient.cpp b/Userland/DevTools/HackStudio/LanguageServers/ConnectionFromClient.cpp deleted file mode 100644 index f8af67d4004..00000000000 --- a/Userland/DevTools/HackStudio/LanguageServers/ConnectionFromClient.cpp +++ /dev/null @@ -1,155 +0,0 @@ -/* - * Copyright (c) 2020, Itamar S. - * Copyright (c) 2022, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "ConnectionFromClient.h" -#include -#include -#include - -namespace LanguageServers { - -static HashMap> s_connections; - -ConnectionFromClient::ConnectionFromClient(NonnullOwnPtr socket) - : IPC::ConnectionFromClient(*this, move(socket), 1) -{ - s_connections.set(1, *this); -} - -void ConnectionFromClient::die() -{ - s_connections.remove(client_id()); - exit(0); -} - -void ConnectionFromClient::greet(ByteString const& project_root) -{ - m_filedb.set_project_root(project_root); - if (auto result = Core::System::unveil(project_root, "r"sv); result.is_error()) { - warnln("Failed to unveil `{}`: {}", project_root, result.error()); - exit(1); - } - if (auto result = Core::System::unveil(nullptr, nullptr); result.is_error()) { - warnln("Failed to lock the veil: {}", result.error()); - exit(1); - } -} - -void ConnectionFromClient::file_opened(ByteString const& filename, IPC::File const& file) -{ - if (m_filedb.is_open(filename)) { - return; - } - m_filedb.add(filename, file.take_fd()); - m_autocomplete_engine->file_opened(filename); -} - -void ConnectionFromClient::file_edit_insert_text(ByteString const& filename, ByteString const& text, i32 start_line, i32 start_column) -{ - dbgln_if(LANGUAGE_SERVER_DEBUG, "InsertText for file: {}", filename); - dbgln_if(LANGUAGE_SERVER_DEBUG, "Text: {}", text); - dbgln_if(LANGUAGE_SERVER_DEBUG, "[{}:{}]", start_line, start_column); - m_filedb.on_file_edit_insert_text(filename, text, start_line, start_column); - m_autocomplete_engine->on_edit(filename); -} - -void ConnectionFromClient::file_edit_remove_text(ByteString const& filename, i32 start_line, i32 start_column, i32 end_line, i32 end_column) -{ - dbgln_if(LANGUAGE_SERVER_DEBUG, "RemoveText for file: {}", filename); - dbgln_if(LANGUAGE_SERVER_DEBUG, "[{}:{} - {}:{}]", start_line, start_column, end_line, end_column); - m_filedb.on_file_edit_remove_text(filename, start_line, start_column, end_line, end_column); - m_autocomplete_engine->on_edit(filename); -} - -void ConnectionFromClient::auto_complete_suggestions(CodeComprehension::ProjectLocation const& location) -{ - dbgln_if(LANGUAGE_SERVER_DEBUG, "AutoCompleteSuggestions for: {} {}:{}", location.file, location.line, location.column); - - auto document = m_filedb.get_document(location.file); - if (!document) { - dbgln("file {} has not been opened", location.file); - return; - } - - GUI::TextPosition autocomplete_position = { (size_t)location.line, (size_t)max(location.column, location.column - 1) }; - Vector suggestions = m_autocomplete_engine->get_suggestions(location.file, autocomplete_position); - async_auto_complete_suggestions(move(suggestions)); -} - -void ConnectionFromClient::set_file_content(ByteString const& filename, ByteString const& content) -{ - dbgln_if(LANGUAGE_SERVER_DEBUG, "SetFileContent: {}", filename); - auto document = m_filedb.get_document(filename); - if (!document) { - m_filedb.add(filename, content); - VERIFY(m_filedb.is_open(filename)); - } else { - document->set_text(content.view()); - } - VERIFY(m_filedb.is_open(filename)); - m_autocomplete_engine->on_edit(filename); -} - -void ConnectionFromClient::find_declaration(CodeComprehension::ProjectLocation const& location) -{ - dbgln_if(LANGUAGE_SERVER_DEBUG, "FindDeclaration: {} {}:{}", location.file, location.line, location.column); - auto document = m_filedb.get_document(location.file); - if (!document) { - dbgln("file {} has not been opened", location.file); - return; - } - - GUI::TextPosition identifier_position = { (size_t)location.line, (size_t)location.column }; - auto decl_location = m_autocomplete_engine->find_declaration_of(location.file, identifier_position); - if (!decl_location.has_value()) { - dbgln("could not find declaration"); - return; - } - - dbgln_if(LANGUAGE_SERVER_DEBUG, "declaration location: {} {}:{}", decl_location.value().file, decl_location.value().line, decl_location.value().column); - async_declaration_location(CodeComprehension::ProjectLocation { decl_location.value().file, decl_location.value().line, decl_location.value().column }); -} - -void ConnectionFromClient::get_parameters_hint(CodeComprehension::ProjectLocation const& location) -{ - dbgln_if(LANGUAGE_SERVER_DEBUG, "GetParametersHint: {} {}:{}", location.file, location.line, location.column); - auto document = m_filedb.get_document(location.file); - if (!document) { - dbgln("file {} has not been opened", location.file); - return; - } - - GUI::TextPosition identifier_position = { (size_t)location.line, (size_t)location.column }; - auto params = m_autocomplete_engine->get_function_params_hint(location.file, identifier_position); - if (!params.has_value()) { - dbgln("could not get parameters hint"); - return; - } - - dbgln_if(LANGUAGE_SERVER_DEBUG, "parameters hint:"); - for (auto& param : params->params) { - dbgln_if(LANGUAGE_SERVER_DEBUG, "{}", param); - } - dbgln_if(LANGUAGE_SERVER_DEBUG, "Parameter index: {}", params->current_index); - - async_parameters_hint_result(params->params, params->current_index); -} - -void ConnectionFromClient::get_tokens_info(ByteString const& filename) -{ - dbgln_if(LANGUAGE_SERVER_DEBUG, "GetTokenInfo: {}", filename); - auto document = m_filedb.get_document(filename); - if (!document) { - dbgln("file {} has not been opened", filename); - return; - } - - auto tokens_info = m_autocomplete_engine->get_tokens_info(filename); - async_tokens_info_result(move(tokens_info)); -} - -} diff --git a/Userland/DevTools/HackStudio/LanguageServers/ConnectionFromClient.h b/Userland/DevTools/HackStudio/LanguageServers/ConnectionFromClient.h deleted file mode 100644 index 9498e929100..00000000000 --- a/Userland/DevTools/HackStudio/LanguageServers/ConnectionFromClient.h +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright (c) 2020, Itamar S. - * Copyright (c) 2022, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include "../AutoCompleteResponse.h" -#include "FileDB.h" -#include -#include -#include -#include - -#include -#include - -namespace LanguageServers { - -class ConnectionFromClient : public IPC::ConnectionFromClient { -public: - explicit ConnectionFromClient(NonnullOwnPtr); - ~ConnectionFromClient() override = default; - - virtual void die() override; - -protected: - virtual void greet(ByteString const&) override; - virtual void file_opened(ByteString const&, IPC::File const&) override; - virtual void file_edit_insert_text(ByteString const&, ByteString const&, i32, i32) override; - virtual void file_edit_remove_text(ByteString const&, i32, i32, i32, i32) override; - virtual void set_file_content(ByteString const&, ByteString const&) override; - virtual void auto_complete_suggestions(CodeComprehension::ProjectLocation const&) override; - virtual void find_declaration(CodeComprehension::ProjectLocation const&) override; - virtual void get_parameters_hint(CodeComprehension::ProjectLocation const&) override; - virtual void get_tokens_info(ByteString const&) override; - - FileDB m_filedb; - OwnPtr m_autocomplete_engine; -}; - -} diff --git a/Userland/DevTools/HackStudio/LanguageServers/Cpp/CMakeLists.txt b/Userland/DevTools/HackStudio/LanguageServers/Cpp/CMakeLists.txt deleted file mode 100644 index 31ac240cff1..00000000000 --- a/Userland/DevTools/HackStudio/LanguageServers/Cpp/CMakeLists.txt +++ /dev/null @@ -1,18 +0,0 @@ -serenity_component( - CppLanguageServer - TARGETS CppLanguageServer -) - -set(SOURCES - main.cpp -) - -set(GENERATED_SOURCES - ../LanguageServerEndpoint.h - ../LanguageClientEndpoint.h) - -serenity_bin(CppLanguageServer) - -# We link with LibGUI because we use GUI::TextDocument to update -# the content of files according to the edit actions we receive over IPC. -target_link_libraries(CppLanguageServer PRIVATE LibIPC LibCore LibCpp LibGUI LibLanguageServer LibCppComprehension LibMain LibRegex) diff --git a/Userland/DevTools/HackStudio/LanguageServers/Cpp/ConnectionFromClient.h b/Userland/DevTools/HackStudio/LanguageServers/Cpp/ConnectionFromClient.h deleted file mode 100644 index 1b7671e6e3f..00000000000 --- a/Userland/DevTools/HackStudio/LanguageServers/Cpp/ConnectionFromClient.h +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright (c) 2020, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include - -namespace LanguageServers::Cpp { - -class ConnectionFromClient final : public LanguageServers::ConnectionFromClient { - C_OBJECT(ConnectionFromClient); - -private: - ConnectionFromClient(NonnullOwnPtr socket) - : LanguageServers::ConnectionFromClient(move(socket)) - { - m_autocomplete_engine = adopt_own(*new CodeComprehension::Cpp::CppComprehensionEngine(m_filedb)); - m_autocomplete_engine->set_declarations_of_document_callback = [this](ByteString const& filename, Vector&& declarations) { - async_declarations_in_document(filename, move(declarations)); - }; - m_autocomplete_engine->set_todo_entries_of_document_callback = [this](ByteString const& filename, Vector&& todo_entries) { - async_todo_entries_in_document(filename, move(todo_entries)); - }; - } - - virtual ~ConnectionFromClient() override = default; -}; -} diff --git a/Userland/DevTools/HackStudio/LanguageServers/Cpp/main.cpp b/Userland/DevTools/HackStudio/LanguageServers/Cpp/main.cpp deleted file mode 100644 index b8511d0117b..00000000000 --- a/Userland/DevTools/HackStudio/LanguageServers/Cpp/main.cpp +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright (c) 2021, Itamar S. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "ConnectionFromClient.h" -#include -#include -#include -#include -#include -#include - -ErrorOr serenity_main(Main::Arguments) -{ - Core::EventLoop event_loop; - TRY(Core::System::pledge("stdio unix recvfd rpath")); - - auto client = TRY(IPC::take_over_accepted_client_from_system_server()); - - TRY(Core::System::pledge("stdio recvfd rpath")); - TRY(Core::System::unveil("/usr/include", "r")); - - // unveil will be sealed later, when we know the project's root path. - return event_loop.exec(); -} diff --git a/Userland/DevTools/HackStudio/LanguageServers/FileDB.cpp b/Userland/DevTools/HackStudio/LanguageServers/FileDB.cpp deleted file mode 100644 index 831abe43c7d..00000000000 --- a/Userland/DevTools/HackStudio/LanguageServers/FileDB.cpp +++ /dev/null @@ -1,161 +0,0 @@ -/* - * Copyright (c) 2021, Itamar S. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "FileDB.h" - -#include -#include -#include -#include - -namespace LanguageServers { - -RefPtr FileDB::get_document(ByteString const& filename) const -{ - auto absolute_path = to_absolute_path(filename); - auto document_optional = m_open_files.get(absolute_path); - if (!document_optional.has_value()) - return nullptr; - - return *document_optional.value(); -} - -RefPtr FileDB::get_document(ByteString const& filename) -{ - auto document = reinterpret_cast(this)->get_document(filename); - if (document.is_null()) - return nullptr; - return adopt_ref(*const_cast(document.leak_ref())); -} - -Optional FileDB::get_or_read_from_filesystem(StringView filename) const -{ - auto absolute_path = to_absolute_path(filename); - auto document = get_document(absolute_path); - if (document) - return document->text(); - - auto document_or_error = create_from_filesystem(absolute_path); - if (document_or_error.is_error()) { - dbgln("Failed to create document '{}': {}", absolute_path, document_or_error.error()); - return {}; - } - return document_or_error.value()->text(); -} - -bool FileDB::is_open(ByteString const& filename) const -{ - return m_open_files.contains(to_absolute_path(filename)); -} - -bool FileDB::add(ByteString const& filename, int fd) -{ - auto document_or_error = create_from_fd(fd); - if (document_or_error.is_error()) { - dbgln("Failed to create document: {}", document_or_error.error()); - return false; - } - - m_open_files.set(to_absolute_path(filename), document_or_error.release_value()); - return true; -} - -ByteString FileDB::to_absolute_path(ByteString const& filename) const -{ - if (LexicalPath { filename }.is_absolute()) { - return filename; - } - if (!m_project_root.has_value()) - return filename; - return LexicalPath { ByteString::formatted("{}/{}", *m_project_root, filename) }.string(); -} - -ErrorOr> FileDB::create_from_filesystem(ByteString const& filename) const -{ - auto file = TRY(Core::File::open(to_absolute_path(filename), Core::File::OpenMode::Read)); - return create_from_file(move(file)); -} - -ErrorOr> FileDB::create_from_fd(int fd) const -{ - auto file = TRY(Core::File::adopt_fd(fd, Core::File::OpenMode::Read)); - return create_from_file(move(file)); -} - -class DefaultDocumentClient final : public GUI::TextDocument::Client { -public: - virtual ~DefaultDocumentClient() override = default; - virtual void document_did_append_line() override {}; - virtual void document_did_insert_line(size_t) override {}; - virtual void document_did_remove_line(size_t) override {}; - virtual void document_did_remove_all_lines() override {}; - virtual void document_did_change(GUI::AllowCallback) override {}; - virtual void document_did_set_text(GUI::AllowCallback) override {}; - virtual void document_did_set_cursor(const GUI::TextPosition&) override {}; - virtual void document_did_update_undo_stack() override { } - - virtual bool is_automatic_indentation_enabled() const override { return false; } - virtual int soft_tab_width() const override { return 4; } -}; -static DefaultDocumentClient s_default_document_client; - -ErrorOr> FileDB::create_from_file(NonnullOwnPtr file) const -{ - auto content = TRY(file->read_until_eof()); - auto document = GUI::TextDocument::create(&s_default_document_client); - document->set_text(content); - return document; -} - -void FileDB::on_file_edit_insert_text(ByteString const& filename, ByteString const& inserted_text, size_t start_line, size_t start_column) -{ - VERIFY(is_open(filename)); - auto document = get_document(filename); - VERIFY(document); - GUI::TextPosition start_position { start_line, start_column }; - document->insert_at(start_position, inserted_text, &s_default_document_client); - - dbgln_if(FILE_CONTENT_DEBUG, "{}", document->text()); -} - -void FileDB::on_file_edit_remove_text(ByteString const& filename, size_t start_line, size_t start_column, size_t end_line, size_t end_column) -{ - // TODO: If file is not open - need to get its contents - // Otherwise- somehow verify that respawned language server is synced with all file contents - VERIFY(is_open(filename)); - auto document = get_document(filename); - VERIFY(document); - GUI::TextPosition start_position { start_line, start_column }; - GUI::TextRange range { - GUI::TextPosition { start_line, start_column }, - GUI::TextPosition { end_line, end_column } - }; - - document->remove(range); - dbgln_if(FILE_CONTENT_DEBUG, "{}", document->text()); -} - -RefPtr FileDB::create_with_content(ByteString const& content) -{ - StringView content_view(content); - auto document = GUI::TextDocument::create(&s_default_document_client); - document->set_text(content_view); - return document; -} - -bool FileDB::add(ByteString const& filename, ByteString const& content) -{ - auto document = create_with_content(content); - if (!document) { - VERIFY_NOT_REACHED(); - return false; - } - - m_open_files.set(to_absolute_path(filename), document.release_nonnull()); - return true; -} - -} diff --git a/Userland/DevTools/HackStudio/LanguageServers/FileDB.h b/Userland/DevTools/HackStudio/LanguageServers/FileDB.h deleted file mode 100644 index 96221e6fdfa..00000000000 --- a/Userland/DevTools/HackStudio/LanguageServers/FileDB.h +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright (c) 2021, Itamar S. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include - -namespace LanguageServers { - -class FileDB final : public CodeComprehension::FileDB { -public: - FileDB() = default; - virtual Optional get_or_read_from_filesystem(StringView filename) const override; - - RefPtr get_document(ByteString const& filename) const; - RefPtr get_document(ByteString const& filename); - - bool add(ByteString const& filename, int fd); - bool add(ByteString const& filename, ByteString const& content); - - void on_file_edit_insert_text(ByteString const& filename, ByteString const& inserted_text, size_t start_line, size_t start_column); - void on_file_edit_remove_text(ByteString const& filename, size_t start_line, size_t start_column, size_t end_line, size_t end_column); - ByteString to_absolute_path(ByteString const& filename) const; - bool is_open(ByteString const& filename) const; - -private: - ErrorOr> create_from_filesystem(ByteString const& filename) const; - ErrorOr> create_from_fd(int fd) const; - ErrorOr> create_from_file(NonnullOwnPtr) const; - static RefPtr create_with_content(ByteString const&); - -private: - HashMap> m_open_files; - Optional m_project_root; -}; - -} diff --git a/Userland/DevTools/HackStudio/LanguageServers/LanguageClient.ipc b/Userland/DevTools/HackStudio/LanguageServers/LanguageClient.ipc deleted file mode 100644 index f20fd2e7ccf..00000000000 --- a/Userland/DevTools/HackStudio/LanguageServers/LanguageClient.ipc +++ /dev/null @@ -1,9 +0,0 @@ -endpoint LanguageClient -{ - auto_complete_suggestions(Vector suggestions) =| - declaration_location(CodeComprehension::ProjectLocation location) =| - declarations_in_document(ByteString filename, Vector declarations) =| - todo_entries_in_document(ByteString filename, Vector todo_entries) =| - parameters_hint_result(Vector params, int current_index) =| - tokens_info_result(Vector tokens_info) =| -} diff --git a/Userland/DevTools/HackStudio/LanguageServers/LanguageServer.ipc b/Userland/DevTools/HackStudio/LanguageServers/LanguageServer.ipc deleted file mode 100644 index 5f4b7b45e42..00000000000 --- a/Userland/DevTools/HackStudio/LanguageServers/LanguageServer.ipc +++ /dev/null @@ -1,15 +0,0 @@ -endpoint LanguageServer -{ - greet(ByteString project_root) =| - - file_opened(ByteString filename, IPC::File file) =| - file_edit_insert_text(ByteString filename, ByteString text, i32 start_line, i32 start_column) =| - file_edit_remove_text(ByteString filename, i32 start_line, i32 start_column, i32 end_line, i32 end_column) =| - set_file_content(ByteString filename, ByteString content) =| - - auto_complete_suggestions(CodeComprehension::ProjectLocation location) =| - find_declaration(CodeComprehension::ProjectLocation location) =| - get_parameters_hint(CodeComprehension::ProjectLocation location) =| - get_tokens_info(ByteString filename) =| - -} diff --git a/Userland/DevTools/HackStudio/LanguageServers/Shell/CMakeLists.txt b/Userland/DevTools/HackStudio/LanguageServers/Shell/CMakeLists.txt deleted file mode 100644 index 2b3efe190e2..00000000000 --- a/Userland/DevTools/HackStudio/LanguageServers/Shell/CMakeLists.txt +++ /dev/null @@ -1,18 +0,0 @@ -serenity_component( - ShellLanguageServer - TARGETS ShellLanguageServer -) - -set(SOURCES - main.cpp -) - -set(GENERATED_SOURCES - ../LanguageServerEndpoint.h - ../LanguageClientEndpoint.h) - -serenity_bin(ShellLanguageServer) - -# We link with LibGUI because we use GUI::TextDocument to update -# the content of files according to the edit actions we receive over IPC. -target_link_libraries(ShellLanguageServer PRIVATE LibCore LibIPC LibShell LibGUI LibLanguageServer LibShellComprehension LibMain) diff --git a/Userland/DevTools/HackStudio/LanguageServers/Shell/ConnectionFromClient.h b/Userland/DevTools/HackStudio/LanguageServers/Shell/ConnectionFromClient.h deleted file mode 100644 index aab13a056bf..00000000000 --- a/Userland/DevTools/HackStudio/LanguageServers/Shell/ConnectionFromClient.h +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright (c) 2020, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include - -namespace LanguageServers::Shell { - -class ConnectionFromClient final : public LanguageServers::ConnectionFromClient { - C_OBJECT(ConnectionFromClient); - -private: - ConnectionFromClient(NonnullOwnPtr socket) - : LanguageServers::ConnectionFromClient(move(socket)) - { - m_autocomplete_engine = make(m_filedb); - m_autocomplete_engine->set_declarations_of_document_callback = [this](ByteString const& filename, Vector&& declarations) { - async_declarations_in_document(filename, move(declarations)); - }; - m_autocomplete_engine->set_todo_entries_of_document_callback = [this](ByteString const& filename, Vector&& todo_entries) { - async_todo_entries_in_document(filename, move(todo_entries)); - }; - } - virtual ~ConnectionFromClient() override = default; -}; -} diff --git a/Userland/DevTools/HackStudio/LanguageServers/Shell/main.cpp b/Userland/DevTools/HackStudio/LanguageServers/Shell/main.cpp deleted file mode 100644 index 5281db9f3e5..00000000000 --- a/Userland/DevTools/HackStudio/LanguageServers/Shell/main.cpp +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright (c) 2020, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "ConnectionFromClient.h" -#include -#include -#include -#include -#include - -ErrorOr serenity_main(Main::Arguments) -{ - Core::EventLoop event_loop; - TRY(Core::System::pledge("stdio unix rpath recvfd")); - - auto client = TRY(IPC::take_over_accepted_client_from_system_server()); - - TRY(Core::System::pledge("stdio rpath recvfd")); - TRY(Core::System::unveil("/etc/passwd", "r")); - - return event_loop.exec(); -} diff --git a/Userland/DevTools/HackStudio/Locator.cpp b/Userland/DevTools/HackStudio/Locator.cpp deleted file mode 100644 index 2c8fda2beb6..00000000000 --- a/Userland/DevTools/HackStudio/Locator.cpp +++ /dev/null @@ -1,128 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * Copyright (c) 2022, the SerenityOS developers. - * Copyright (c) 2024, Sam Atkins - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "Locator.h" -#include "DeclarationsModel.h" -#include "HackStudio.h" -#include "Project.h" -#include "ProjectDeclarations.h" -#include -#include -#include -#include - -namespace HackStudio { - -Locator::Locator(Core::EventReceiver* parent) -{ - set_layout(); - set_fixed_height(22); - m_textbox = add(); - m_textbox->on_change = [this] { - update_suggestions(); - }; - - m_textbox->on_escape_pressed = [this] { - m_popup_window->hide(); - m_textbox->set_focus(false); - }; - - m_textbox->on_up_pressed = [this] { - GUI::ModelIndex new_index = m_suggestion_view->selection().first(); - if (new_index.is_valid()) - new_index = m_suggestion_view->model()->index(new_index.row() - 1); - else - new_index = m_suggestion_view->model()->index(0); - - if (m_suggestion_view->model()->is_within_range(new_index)) { - m_suggestion_view->selection().set(new_index); - m_suggestion_view->scroll_into_view(new_index, Orientation::Vertical); - } - }; - m_textbox->on_down_pressed = [this] { - GUI::ModelIndex new_index = m_suggestion_view->selection().first(); - if (new_index.is_valid()) - new_index = m_suggestion_view->model()->index(new_index.row() + 1); - else - new_index = m_suggestion_view->model()->index(0); - - if (m_suggestion_view->model()->is_within_range(new_index)) { - m_suggestion_view->selection().set(new_index); - m_suggestion_view->scroll_into_view(new_index, Orientation::Vertical); - } - }; - - m_textbox->on_return_pressed = [this] { - auto selected_index = m_suggestion_view->selection().first(); - if (!selected_index.is_valid()) - return; - open_suggestion(selected_index); - }; - - m_textbox->on_focusout = [&]() { - close(); - }; - - m_popup_window = GUI::Window::construct(parent); - m_popup_window->set_window_type(GUI::WindowType::Autocomplete); - m_popup_window->set_rect(0, 0, 500, 200); - - m_suggestion_view = m_popup_window->set_main_widget(); - m_suggestion_view->set_column_headers_visible(false); - - m_suggestion_view->on_activation = [this](auto& index) { - open_suggestion(index); - }; - - m_model = GUI::FilteringProxyModel::create(ProjectDeclarations::the().declarations_model(), GUI::FilteringProxyModel::FilteringOptions::SortByScore).release_value_but_fixme_should_propagate_errors(); - m_suggestion_view->set_model(m_model); -} - -void Locator::open_suggestion(const GUI::ModelIndex& index) -{ - auto original_index = m_model->map(index); - auto suggestion = ProjectDeclarations::the().declarations_model().declarations()[original_index.row()]; - if (suggestion.is_filename()) { - auto filename = suggestion.as_filename.value(); - open_file(filename); - } - if (suggestion.is_symbol_declaration()) { - auto position = suggestion.as_symbol_declaration.value().position; - open_file(position.file, position.line, position.column); - } - close(); -} - -void Locator::open() -{ - m_textbox->set_focus(true); - if (!m_textbox->text().is_empty()) { - m_textbox->select_all(); - m_popup_window->show(); - } -} - -void Locator::close() -{ - m_popup_window->hide(); - m_textbox->set_focus(false); -} - -void Locator::update_suggestions() -{ - m_model->set_filter_term(m_textbox->text()); - - if (m_model->row_count() == 0) - m_suggestion_view->selection().clear(); - else - m_suggestion_view->selection().set(m_suggestion_view->model()->index(0)); - - m_popup_window->move_to(screen_relative_rect().top_left().translated(0, -m_popup_window->height())); - m_popup_window->show(); -} -} diff --git a/Userland/DevTools/HackStudio/Locator.h b/Userland/DevTools/HackStudio/Locator.h deleted file mode 100644 index 3ad48c2b0a8..00000000000 --- a/Userland/DevTools/HackStudio/Locator.h +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * Copyright (c) 2022, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include - -namespace HackStudio { - -class Locator final : public GUI::Widget { - C_OBJECT(Locator) -public: - virtual ~Locator() override = default; - - void open(); - void close(); - -private: - void update_suggestions(); - void open_suggestion(const GUI::ModelIndex&); - - Locator(Core::EventReceiver* parent = nullptr); - - RefPtr m_textbox; - RefPtr m_popup_window; - RefPtr m_suggestion_view; - RefPtr m_model; -}; - -} diff --git a/Userland/DevTools/HackStudio/Project.cpp b/Userland/DevTools/HackStudio/Project.cpp deleted file mode 100644 index 06115762289..00000000000 --- a/Userland/DevTools/HackStudio/Project.cpp +++ /dev/null @@ -1,79 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "Project.h" -#include "HackStudio.h" -#include - -namespace HackStudio { - -Project::Project(ByteString const& root_path) - : m_root_path(root_path) -{ - m_model = GUI::FileSystemModel::create(root_path, GUI::FileSystemModel::Mode::FilesAndDirectories); -} - -OwnPtr Project::open_with_root_path(ByteString const& root_path) -{ - VERIFY(LexicalPath(root_path).is_absolute()); - if (!FileSystem::is_directory(root_path)) - return {}; - return adopt_own(*new Project(root_path)); -} - -template -static void traverse_model(const GUI::FileSystemModel& model, const GUI::ModelIndex& index, Callback callback) -{ - if (index.is_valid()) - callback(index); - auto row_count = model.row_count(index); - if (!row_count) - return; - for (int row = 0; row < row_count; ++row) { - auto child_index = model.index(row, GUI::FileSystemModel::Column::Name, index); - traverse_model(model, child_index, callback); - } -} - -void Project::for_each_text_file(Function callback) const -{ - traverse_model(model(), {}, [&](auto& index) { - auto file = create_file(model().full_path(index)); - callback(*file); - }); -} - -NonnullRefPtr Project::create_file(ByteString const& path) const -{ - auto full_path = to_absolute_path(path); - return ProjectFile::construct_with_name(full_path); -} - -ByteString Project::to_absolute_path(ByteString const& path) const -{ - if (LexicalPath { path }.is_absolute()) { - return path; - } - return LexicalPath { ByteString::formatted("{}/{}", m_root_path, path) }.string(); -} - -bool Project::project_is_serenity() const -{ - // FIXME: Improve this heuristic - // Running "Meta/serenity.sh copy-src" installs the serenity repository at this path in the home directory - return m_root_path.ends_with("Source/serenity"sv); -} - -NonnullOwnPtr Project::config() const -{ - auto config_or_error = ProjectConfig::try_load_project_config(LexicalPath::absolute_path(m_root_path, config_file_path)); - if (config_or_error.is_error()) - return ProjectConfig::create_empty(); - - return config_or_error.release_value(); -} - -} diff --git a/Userland/DevTools/HackStudio/Project.h b/Userland/DevTools/HackStudio/Project.h deleted file mode 100644 index f0698b91143..00000000000 --- a/Userland/DevTools/HackStudio/Project.h +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include "ProjectConfig.h" -#include "ProjectFile.h" -#include -#include -#include -#include - -namespace HackStudio { - -class Project { - AK_MAKE_NONCOPYABLE(Project); - AK_MAKE_NONMOVABLE(Project); - -public: - static OwnPtr open_with_root_path(ByteString const& root_path); - - GUI::FileSystemModel& model() { return *m_model; } - const GUI::FileSystemModel& model() const { return *m_model; } - ByteString name() const { return LexicalPath::basename(m_root_path); } - ByteString root_path() const { return m_root_path; } - - NonnullRefPtr create_file(ByteString const& path) const; - - void for_each_text_file(Function) const; - ByteString to_absolute_path(ByteString const&) const; - bool project_is_serenity() const; - - static constexpr auto config_file_path = ".hackstudio/config.json"sv; - NonnullOwnPtr config() const; - -private: - explicit Project(ByteString const& root_path); - - RefPtr m_model; - - ByteString m_root_path; -}; - -} diff --git a/Userland/DevTools/HackStudio/ProjectBuilder.cpp b/Userland/DevTools/HackStudio/ProjectBuilder.cpp deleted file mode 100644 index 6f8a2703334..00000000000 --- a/Userland/DevTools/HackStudio/ProjectBuilder.cpp +++ /dev/null @@ -1,275 +0,0 @@ -/* - * Copyright (c) 2022, Itamar S. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "ProjectBuilder.h" -#include -#include -#include -#include -#include -#include - -namespace HackStudio { - -ProjectBuilder::ProjectBuilder(NonnullRefPtr terminal, Project const& project) - : m_project_root(project.root_path()) - , m_project(project) - , m_terminal(move(terminal)) - , m_is_serenity(project.project_is_serenity() ? IsSerenityRepo::Yes : IsSerenityRepo::No) -{ -} - -ErrorOr ProjectBuilder::build(StringView active_file) -{ - m_terminal->clear_including_history(); - - if (auto command = m_project.config()->build_command(); command.has_value()) { - TRY(m_terminal->run_command(command.value())); - return {}; - } - - if (active_file.is_null()) - return Error::from_string_literal("no active file"); - - if (active_file.ends_with(".js"sv)) { - TRY(m_terminal->run_command(ByteString::formatted("js -A {}", active_file))); - return {}; - } - - if (m_is_serenity == IsSerenityRepo::No) { - TRY(verify_make_is_installed()); - TRY(m_terminal->run_command("make")); - return {}; - } - - TRY(update_active_file(active_file)); - - return build_serenity_component(); -} - -ErrorOr ProjectBuilder::run(StringView active_file) -{ - if (auto command = m_project.config()->run_command(); command.has_value()) { - TRY(m_terminal->run_command(command.value())); - return {}; - } - - if (active_file.is_null()) - return Error::from_string_literal("no active file"); - - if (active_file.ends_with(".js"sv)) { - TRY(m_terminal->run_command(ByteString::formatted("js {}", active_file))); - return {}; - } - - if (m_is_serenity == IsSerenityRepo::No) { - TRY(verify_make_is_installed()); - TRY(m_terminal->run_command("make run")); - return {}; - } - - TRY(update_active_file(active_file)); - - return run_serenity_component(); -} - -ErrorOr ProjectBuilder::run_serenity_component() -{ - auto relative_path_to_dir = LexicalPath::relative_path(LexicalPath::dirname(m_serenity_component_cmake_file), m_project_root); - TRY(m_terminal->run_command(LexicalPath::join(relative_path_to_dir, m_serenity_component_name).string(), build_directory())); - return {}; -} - -ErrorOr ProjectBuilder::update_active_file(StringView active_file) -{ - TRY(verify_cmake_is_installed()); - auto cmake_file = find_cmake_file_for(active_file); - if (!cmake_file.has_value()) { - warnln("did not find cmake file for: {}", active_file); - return Error::from_string_literal("did not find cmake file"); - } - - if (m_serenity_component_cmake_file == cmake_file.value()) - return {}; - - m_serenity_component_cmake_file = cmake_file.value(); - m_serenity_component_name = TRY(component_name(m_serenity_component_cmake_file)); - - TRY(initialize_build_directory()); - return {}; -} - -ErrorOr ProjectBuilder::build_serenity_component() -{ - TRY(verify_make_is_installed()); - TRY(m_terminal->run_command(ByteString::formatted("make {}", m_serenity_component_name), build_directory(), TerminalWrapper::WaitForExit::Yes, "Make failed"sv)); - return {}; -} - -ErrorOr ProjectBuilder::component_name(StringView cmake_file_path) -{ - auto file = TRY(Core::File::open(cmake_file_path, Core::File::OpenMode::Read)); - auto content = TRY(file->read_until_eof()); - - static Regex const component_name(R"~~~(serenity_component\([\s]*(\w+)[\s\S]*\))~~~"); - RegexResult result; - if (!component_name.search(StringView { content }, result)) - return Error::from_string_literal("component not found"); - - return ByteString { result.capture_group_matches.at(0).at(0).view.string_view() }; -} - -ErrorOr ProjectBuilder::initialize_build_directory() -{ - if (!FileSystem::exists(build_directory())) { - if (mkdir(LexicalPath::join(build_directory()).string().characters(), 0700)) { - return Error::from_errno(errno); - } - } - - auto cmake_file_path = LexicalPath::join(build_directory(), "CMakeLists.txt"sv).string(); - if (FileSystem::exists(cmake_file_path)) - MUST(FileSystem::remove(cmake_file_path, FileSystem::RecursionMode::Disallowed)); - - auto cmake_file = TRY(Core::File::open(cmake_file_path, Core::File::OpenMode::Write)); - TRY(cmake_file->write_until_depleted(generate_cmake_file_content())); - - TRY(m_terminal->run_command(ByteString::formatted("cmake -S {} -DHACKSTUDIO_BUILD=ON -DHACKSTUDIO_BUILD_CMAKE_FILE={}" - " -DENABLE_UNICODE_DATABASE_DOWNLOAD=OFF", - m_project_root, cmake_file_path), - build_directory(), TerminalWrapper::WaitForExit::Yes, "CMake error"sv)); - - return {}; -} - -Optional ProjectBuilder::find_cmake_file_for(StringView file_path) const -{ - auto directory = LexicalPath::dirname(file_path); - while (!directory.is_empty()) { - auto cmake_path = LexicalPath::join(m_project_root, directory, "CMakeLists.txt"sv); - if (FileSystem::exists(cmake_path.string())) - return cmake_path.string(); - directory = LexicalPath::dirname(directory); - } - return {}; -} - -ByteString ProjectBuilder::generate_cmake_file_content() const -{ - StringBuilder builder; - builder.appendff("add_subdirectory({})\n", LexicalPath::dirname(m_serenity_component_cmake_file)); - - auto defined_libraries = get_defined_libraries(); - for (auto& library : defined_libraries) { - builder.appendff("add_library({} SHARED IMPORTED GLOBAL)\n", library.key); - builder.appendff("set_target_properties({} PROPERTIES IMPORTED_LOCATION {})\n", library.key, library.value->path); - - if (library.key == "LibCStaticWithoutDeps"sv) - continue; - - // We need to specify the dependencies for each defined library in CMake because some applications do not specify - // all of their direct dependencies in the CMakeLists file. - // For example, a target may directly use LibGFX but only specify LibGUI as a dependency (which in turn depends on LibGFX). - // In this example, if we don't specify the dependencies of LibGUI in the CMake file, linking will fail because of undefined LibGFX symbols. - builder.appendff("target_link_libraries({} INTERFACE {})\n", library.key, ByteString::join(' ', library.value->dependencies)); - } - - return builder.to_byte_string(); -} - -HashMap> ProjectBuilder::get_defined_libraries() -{ - HashMap> libraries; - - for_each_library_definition([&libraries](ByteString name, ByteString path) { - libraries.set(name, make(move(path))); - }); - for_each_library_dependencies([&libraries](ByteString name, Vector const& dependencies) { - auto library = libraries.get(name); - if (!library.has_value()) - return; - for (auto const& dependency : dependencies) { - if (libraries.contains(dependency)) - library.value()->dependencies.append(dependency); - } - }); - return libraries; -} - -void ProjectBuilder::for_each_library_definition(Function func) -{ - Vector arguments = { "-c", "find Userland -name CMakeLists.txt | xargs grep serenity_lib" }; - auto res = Core::command("/bin/sh", arguments, {}); - if (res.is_error()) { - warnln("{}", res.error()); - return; - } - - static Regex const parse_library_definition(R"~~~(.+:serenity_lib[c]?\((\w+) (\w+)\).*)~~~"); - for (auto& line : StringView(res.value().output).split_view('\n')) { - RegexResult result; - if (!parse_library_definition.search(line, result)) - continue; - if (result.capture_group_matches.size() != 1 || result.capture_group_matches[0].size() != 2) - continue; - - auto library_name = result.capture_group_matches.at(0).at(0).view.string_view(); - auto library_obj_name = result.capture_group_matches.at(0).at(1).view.string_view(); - auto so_path = ByteString::formatted("{}.so", LexicalPath::join("/usr/lib"sv, ByteString::formatted("lib{}", library_obj_name)).string()); - func(library_name, so_path); - } - - // ssp is defined with "add_library" so it doesn't get picked up with the current logic for finding library definitions. - func("ssp", "/usr/lib/libssp.a"); -} - -void ProjectBuilder::for_each_library_dependencies(Function)> func) -{ - Vector arguments = { "-c", "find Userland/Libraries -name CMakeLists.txt | xargs grep target_link_libraries" }; - auto res = Core::command("/bin/sh", arguments, {}); - if (res.is_error()) { - warnln("{}", res.error()); - return; - } - auto libraries = StringView(res.value().output).split_view('\n'); - - static Regex const parse_library_definition(R"~~~(.+:target_link_libraries\((\w+) ([\w\s]+)\).*)~~~"); - for (auto& line : libraries) { - RegexResult result; - if (!parse_library_definition.search(line, result)) - continue; - if (result.capture_group_matches.size() != 1 || result.capture_group_matches[0].size() != 2) - continue; - - auto library_name = result.capture_group_matches.at(0).at(0).view.string_view(); - auto dependencies_string = result.capture_group_matches.at(0).at(1).view.string_view(); - - func(library_name, dependencies_string.split_view(' ')); - } -} - -ErrorOr ProjectBuilder::verify_cmake_is_installed() -{ - auto res = Core::command("cmake --version", {}); - if (!res.is_error() && res.value().exit_code == 0) - return {}; - return Error::from_string_literal("CMake port is not installed"); -} - -ErrorOr ProjectBuilder::verify_make_is_installed() -{ - auto res = Core::command("make --version", {}); - if (!res.is_error() && res.value().exit_code == 0) - return {}; - return Error::from_string_literal("Make port is not installed"); -} - -ByteString ProjectBuilder::build_directory() const -{ - return LexicalPath::join(m_project_root, "Build"sv).string(); -} - -} diff --git a/Userland/DevTools/HackStudio/ProjectBuilder.h b/Userland/DevTools/HackStudio/ProjectBuilder.h deleted file mode 100644 index 194f38d5d7a..00000000000 --- a/Userland/DevTools/HackStudio/ProjectBuilder.h +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright (c) 2022, Itamar S. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include "Project.h" -#include "TerminalWrapper.h" -#include -#include - -namespace HackStudio { -class ProjectBuilder { - - AK_MAKE_NONCOPYABLE(ProjectBuilder); - -public: - ProjectBuilder(NonnullRefPtr, Project const&); - ~ProjectBuilder() = default; - - ErrorOr build(StringView active_file); - ErrorOr run(StringView active_file); - -private: - enum class IsSerenityRepo { - No, - Yes - }; - - ErrorOr build_serenity_component(); - ErrorOr run_serenity_component(); - ErrorOr initialize_build_directory(); - Optional find_cmake_file_for(StringView file_path) const; - ByteString generate_cmake_file_content() const; - ErrorOr update_active_file(StringView active_file); - ByteString build_directory() const; - - struct LibraryInfo { - ByteString path; - Vector dependencies {}; - }; - static HashMap> get_defined_libraries(); - static void for_each_library_definition(Function); - static void for_each_library_dependencies(Function)>); - static ErrorOr component_name(StringView cmake_file_path); - static ErrorOr verify_cmake_is_installed(); - static ErrorOr verify_make_is_installed(); - - ByteString m_project_root; - Project const& m_project; - NonnullRefPtr m_terminal; - IsSerenityRepo m_is_serenity { IsSerenityRepo::No }; - ByteString m_serenity_component_cmake_file; - ByteString m_serenity_component_name; -}; -} diff --git a/Userland/DevTools/HackStudio/ProjectConfig.cpp b/Userland/DevTools/HackStudio/ProjectConfig.cpp deleted file mode 100644 index 746b5c4938d..00000000000 --- a/Userland/DevTools/HackStudio/ProjectConfig.cpp +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright (c) 2022, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "ProjectConfig.h" -#include -#include - -namespace HackStudio { - -ProjectConfig::ProjectConfig(JsonObject config) - : m_config(move(config)) -{ -} - -ErrorOr> ProjectConfig::try_load_project_config(ByteString path) -{ - auto file = TRY(Core::File::open(path, Core::File::OpenMode::Read)); - auto file_contents = TRY(file->read_until_eof()); - - auto json = TRY(JsonValue::from_string(file_contents)); - if (!json.is_object()) - return Error::from_string_literal("The topmost JSON element is not an object"); - - return try_make(json.as_object()); -} - -NonnullOwnPtr ProjectConfig::create_empty() -{ - JsonObject empty {}; - return adopt_own(*new ProjectConfig(empty)); -} - -Optional ProjectConfig::read_key(ByteString key_name) const -{ - return m_config.get_byte_string(key_name); -} - -} diff --git a/Userland/DevTools/HackStudio/ProjectConfig.h b/Userland/DevTools/HackStudio/ProjectConfig.h deleted file mode 100644 index 30ee7d2ffcd..00000000000 --- a/Userland/DevTools/HackStudio/ProjectConfig.h +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright (c) 2022, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include - -namespace HackStudio { - -class ProjectConfig { -public: - static ErrorOr> try_load_project_config(ByteString path); - static NonnullOwnPtr create_empty(); - - ProjectConfig(JsonObject); - - Optional build_command() const { return read_key("build_command"); } - Optional run_command() const { return read_key("run_command"); } - -private: - Optional read_key(ByteString key_name) const; - - JsonObject m_config; -}; - -} diff --git a/Userland/DevTools/HackStudio/ProjectDeclarations.cpp b/Userland/DevTools/HackStudio/ProjectDeclarations.cpp deleted file mode 100644 index e9e7e133236..00000000000 --- a/Userland/DevTools/HackStudio/ProjectDeclarations.cpp +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Copyright (c) 2021, Itamar S. - * Copyright (c) 2024, Sam Atkins - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "ProjectDeclarations.h" -#include "HackStudio.h" - -namespace HackStudio { - -ProjectDeclarations::ProjectDeclarations() - : m_declarations_model(adopt_ref(*new DeclarationsModel({}))) -{ -} - -ProjectDeclarations& ProjectDeclarations::the() -{ - static ProjectDeclarations s_instance; - return s_instance; -} - -void ProjectDeclarations::set_declared_symbols(ByteString const& filename, Vector const& declarations) -{ - m_document_to_declarations.set(filename, declarations); - // FIXME: Partially invalidate the model instead of fully rebuilding it. - update_declarations_model(); - if (on_update) - on_update(); -} - -Optional ProjectDeclarations::get_icon_for(CodeComprehension::DeclarationType type) -{ - static GUI::Icon struct_icon(Gfx::Bitmap::load_from_file("/res/icons/hackstudio/Struct.png"sv).release_value_but_fixme_should_propagate_errors()); - static GUI::Icon class_icon(Gfx::Bitmap::load_from_file("/res/icons/hackstudio/Class.png"sv).release_value_but_fixme_should_propagate_errors()); - static GUI::Icon function_icon(Gfx::Bitmap::load_from_file("/res/icons/hackstudio/Function.png"sv).release_value_but_fixme_should_propagate_errors()); - static GUI::Icon variable_icon(Gfx::Bitmap::load_from_file("/res/icons/hackstudio/Variable.png"sv).release_value_but_fixme_should_propagate_errors()); - static GUI::Icon preprocessor_icon(Gfx::Bitmap::load_from_file("/res/icons/hackstudio/Preprocessor.png"sv).release_value_but_fixme_should_propagate_errors()); - static GUI::Icon member_icon(Gfx::Bitmap::load_from_file("/res/icons/hackstudio/Member.png"sv).release_value_but_fixme_should_propagate_errors()); - static GUI::Icon namespace_icon(Gfx::Bitmap::load_from_file("/res/icons/hackstudio/Namespace.png"sv).release_value_but_fixme_should_propagate_errors()); - switch (type) { - case CodeComprehension::DeclarationType::Struct: - return struct_icon; - case CodeComprehension::DeclarationType::Class: - return class_icon; - case CodeComprehension::DeclarationType::Function: - return function_icon; - case CodeComprehension::DeclarationType::Variable: - return variable_icon; - case CodeComprehension::DeclarationType::PreprocessorDefinition: - return preprocessor_icon; - case CodeComprehension::DeclarationType::Member: - return member_icon; - case CodeComprehension::DeclarationType::Namespace: - return namespace_icon; - default: - return {}; - } -} - -void ProjectDeclarations::update_declarations_model() -{ - Vector declarations; - project().for_each_text_file([&](auto& file) { - declarations.append(Declaration::create_filename(file.name())); - }); - for_each_declared_symbol([&declarations](auto& decl) { - declarations.append((Declaration::create_symbol_declaration(decl))); - }); - m_declarations_model->set_declarations(move(declarations)); -} - -} diff --git a/Userland/DevTools/HackStudio/ProjectDeclarations.h b/Userland/DevTools/HackStudio/ProjectDeclarations.h deleted file mode 100644 index da045165740..00000000000 --- a/Userland/DevTools/HackStudio/ProjectDeclarations.h +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright (c) 2021, Itamar S. - * Copyright (c) 2024, Sam Atkins - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include "DeclarationsModel.h" -#include -#include -#include -#include -#include -#include - -namespace HackStudio { - -class ProjectDeclarations { - AK_MAKE_NONCOPYABLE(ProjectDeclarations); - -public: - static ProjectDeclarations& the(); - template - void for_each_declared_symbol(Func); - - void set_declared_symbols(ByteString const& filename, Vector const&); - - DeclarationsModel& declarations_model() { return m_declarations_model; } - void update_declarations_model(); - - static Optional get_icon_for(CodeComprehension::DeclarationType); - - Function on_update = nullptr; - -private: - ProjectDeclarations(); - - HashMap> m_document_to_declarations; - NonnullRefPtr m_declarations_model; -}; - -template -void ProjectDeclarations::for_each_declared_symbol(Func f) -{ - for (auto& item : m_document_to_declarations) { - for (auto& decl : item.value) { - f(decl); - } - } -} - -} diff --git a/Userland/DevTools/HackStudio/ProjectFile.cpp b/Userland/DevTools/HackStudio/ProjectFile.cpp deleted file mode 100644 index 98ab665c475..00000000000 --- a/Userland/DevTools/HackStudio/ProjectFile.cpp +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "ProjectFile.h" -#include - -namespace HackStudio { - -ProjectFile::ProjectFile(ByteString const& name) - : m_name(name) -{ -} - -GUI::TextDocument& ProjectFile::document() const -{ - create_document_if_needed(); - VERIFY(m_document); - return *m_document; -} - -int ProjectFile::vertical_scroll_value() const -{ - return m_vertical_scroll_value; -} - -void ProjectFile::vertical_scroll_value(int vertical_scroll_value) -{ - m_vertical_scroll_value = vertical_scroll_value; -} - -int ProjectFile::horizontal_scroll_value() const -{ - return m_horizontal_scroll_value; -} - -void ProjectFile::horizontal_scroll_value(int horizontal_scroll_value) -{ - m_horizontal_scroll_value = horizontal_scroll_value; -} - -CodeDocument& ProjectFile::code_document() const -{ - create_document_if_needed(); - VERIFY(m_document); - return *m_document; -} - -void ProjectFile::create_document_if_needed() const -{ - if (m_document) - return; - - m_document = CodeDocument::create(m_name); - auto file_or_error = Core::File::open(m_name, Core::File::OpenMode::Read); - if (file_or_error.is_error()) { - warnln("Couldn't open '{}': {}", m_name, file_or_error.error()); - // This is okay though, we'll just go with an empty document and create the file when saving. - return; - } - - auto& file = *file_or_error.value(); - m_could_render_text = m_document->set_text(file.read_until_eof().release_value_but_fixme_should_propagate_errors()); -} - -} diff --git a/Userland/DevTools/HackStudio/ProjectFile.h b/Userland/DevTools/HackStudio/ProjectFile.h deleted file mode 100644 index d8fd6e10a0b..00000000000 --- a/Userland/DevTools/HackStudio/ProjectFile.h +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include "CodeDocument.h" -#include -#include -#include -#include - -namespace HackStudio { - -class ProjectFile : public RefCounted { -public: - static NonnullRefPtr construct_with_name(ByteString const& name) - { - return adopt_ref(*new ProjectFile(name)); - } - - ByteString const& name() const { return m_name; } - bool could_render_text() const { return m_could_render_text; } - - GUI::TextDocument& document() const; - CodeDocument& code_document() const; - - int vertical_scroll_value() const; - void vertical_scroll_value(int); - int horizontal_scroll_value() const; - void horizontal_scroll_value(int); - -private: - explicit ProjectFile(ByteString const& name); - void create_document_if_needed() const; - - ByteString m_name; - mutable RefPtr m_document; - mutable bool m_could_render_text { false }; - int m_vertical_scroll_value { 0 }; - int m_horizontal_scroll_value { 0 }; -}; - -} diff --git a/Userland/DevTools/HackStudio/ProjectTemplate.cpp b/Userland/DevTools/HackStudio/ProjectTemplate.cpp deleted file mode 100644 index b703fd4937c..00000000000 --- a/Userland/DevTools/HackStudio/ProjectTemplate.cpp +++ /dev/null @@ -1,109 +0,0 @@ -/* - * Copyright (c) 2021, Nick Vella - * Copyright (c) 2024, Sam Atkins - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "ProjectTemplate.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace HackStudio { - -ProjectTemplate::ProjectTemplate(ByteString const& id, ByteString const& name, ByteString const& description, const GUI::Icon& icon, int priority) - : m_id(id) - , m_name(name) - , m_description(description) - , m_icon(icon) - , m_priority(priority) -{ -} - -RefPtr ProjectTemplate::load_from_manifest(ByteString const& manifest_path) -{ - auto maybe_config = Core::ConfigFile::open(manifest_path); - if (maybe_config.is_error()) - return {}; - auto config = maybe_config.release_value(); - - if (!config->has_group("HackStudioTemplate") - || !config->has_key("HackStudioTemplate", "Name") - || !config->has_key("HackStudioTemplate", "Description") - || !config->has_key("HackStudioTemplate", "IconName32x")) - return {}; - - auto id = LexicalPath::title(manifest_path); - auto name = config->read_entry("HackStudioTemplate", "Name"); - auto description = config->read_entry("HackStudioTemplate", "Description"); - int priority = config->read_num_entry("HackStudioTemplate", "Priority", 0); - - // Attempt to read in the template icons - // Fallback to a generic executable icon if one isn't found - auto icon = GUI::Icon::default_icon("filetype-executable"sv); - - auto bitmap_path_32 = ByteString::formatted("/res/icons/hackstudio/templates-32x32/{}.png", config->read_entry("HackStudioTemplate", "IconName32x")); - - if (FileSystem::exists(bitmap_path_32)) { - auto bitmap_or_error = Gfx::Bitmap::load_from_file(bitmap_path_32); - if (!bitmap_or_error.is_error()) - icon = GUI::Icon(bitmap_or_error.release_value()); - } - - return adopt_ref(*new ProjectTemplate(id, name, description, icon, priority)); -} - -ErrorOr ProjectTemplate::create_project(ByteString const& name, ByteString const& path) -{ - // Check if a file or directory already exists at the project path - if (FileSystem::exists(path)) - return Error::from_string_literal("File or directory already exists at specified location."); - - dbgln("Creating project at path '{}' with name '{}'", path, name); - - // Verify that the template content directory exists. If it does, copy it's contents. - // Otherwise, create an empty directory at the project path. - if (FileSystem::is_directory(content_path())) { - dbgln("Copying {} -> {}", content_path(), path); - TRY(FileSystem::copy_file_or_directory(path, content_path())); - } else { - dbgln("No template content directory found for '{}', creating an empty directory for the project.", m_id); - TRY(Core::System::mkdir(path, 0755)); - } - - // Check for an executable post-create script in $TEMPLATES_DIR/$ID.postcreate, - // and run it with the path and name - - auto postcreate_script_path = LexicalPath::canonicalized_path(ByteString::formatted("{}/{}.postcreate", templates_path(), m_id)); - struct stat postcreate_st; - int result = stat(postcreate_script_path.characters(), &postcreate_st); - if (result == 0 && (postcreate_st.st_mode & S_IXOTH) == S_IXOTH) { - dbgln("Running post-create script '{}'", postcreate_script_path); - - // Generate a namespace-safe project name (replace hyphens with underscores) - auto namespace_safe = name.replace("-"sv, "_"sv, ReplaceMode::All); - auto child_process = TRY(Core::Process::spawn({ - .executable = postcreate_script_path, - .arguments = { name, path, namespace_safe }, - })); - - // Command spawned, wait for exit. - auto child_exited_with_0 = TRY(child_process.wait_for_termination()); - if (!child_exited_with_0) - return Error::from_string_literal("Project post-creation script exited with non-zero error code."); - } - - return {}; -} - -} diff --git a/Userland/DevTools/HackStudio/ProjectTemplate.h b/Userland/DevTools/HackStudio/ProjectTemplate.h deleted file mode 100644 index 9e5f0750ac0..00000000000 --- a/Userland/DevTools/HackStudio/ProjectTemplate.h +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright (c) 2021, Nick Vella - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include -#include - -namespace HackStudio { - -class ProjectTemplate : public RefCounted { -public: - static ByteString templates_path() { return "/res/devel/templates"; } - - static RefPtr load_from_manifest(ByteString const& manifest_path); - - explicit ProjectTemplate(ByteString const& id, ByteString const& name, ByteString const& description, const GUI::Icon& icon, int priority); - - ErrorOr create_project(ByteString const& name, ByteString const& path); - - ByteString const& id() const { return m_id; } - ByteString const& name() const { return m_name; } - ByteString const& description() const { return m_description; } - const GUI::Icon& icon() const { return m_icon; } - ByteString const content_path() const - { - return LexicalPath::canonicalized_path(ByteString::formatted("{}/{}", templates_path(), m_id)); - } - int priority() const { return m_priority; } - -private: - ByteString m_id; - ByteString m_name; - ByteString m_description; - GUI::Icon m_icon; - int m_priority { 0 }; -}; - -} diff --git a/Userland/DevTools/HackStudio/TerminalWrapper.cpp b/Userland/DevTools/HackStudio/TerminalWrapper.cpp deleted file mode 100644 index 67f9c329be3..00000000000 --- a/Userland/DevTools/HackStudio/TerminalWrapper.cpp +++ /dev/null @@ -1,185 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * Copyright (c) 2022, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "TerminalWrapper.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace HackStudio { - -ErrorOr TerminalWrapper::run_command(ByteString const& command, Optional working_directory, WaitForExit wait_for_exit, Optional failure_message) -{ - if (m_pid != -1) { - GUI::MessageBox::show(window(), - "A command is already running in this TerminalWrapper"sv, - "Can't run command"sv, - GUI::MessageBox::Type::Error); - return {}; - } - - auto ptm_fd = TRY(setup_master_pseudoterminal()); - - m_child_exited = false; - m_child_exit_status.clear(); - - m_pid = TRY(Core::System::fork()); - - if (m_pid > 0) { - m_terminal_widget->set_startup_process_id(m_pid); - - if (wait_for_exit == WaitForExit::Yes) { - GUI::Application::the()->event_loop().spin_until([this]() { - return m_child_exited; - }); - - VERIFY(m_child_exit_status.has_value()); - if (m_child_exit_status.value() != 0) - return Error::from_string_view(failure_message.value_or("Command execution failed"sv)); - } - - return {}; - } - - if (working_directory.has_value()) - TRY(Core::System::chdir(working_directory->view())); - - TRY(setup_slave_pseudoterminal(ptm_fd)); - - auto args = command.split_view(' '); - VERIFY(!args.is_empty()); - TRY(Core::System::exec(args[0], args, Core::System::SearchInPath::Yes)); - VERIFY_NOT_REACHED(); -} - -ErrorOr TerminalWrapper::setup_master_pseudoterminal(WaitForChildOnExit wait_for_child) -{ - int ptm_fd = TRY(Core::System::posix_openpt(O_RDWR | O_CLOEXEC)); - bool error_happened = true; - - ScopeGuard close_ptm { [&]() { - if (error_happened) { - if (auto result = Core::System::close(ptm_fd); result.is_error()) - warnln("{}", result.release_error()); - } - } }; - - TRY(Core::System::grantpt(ptm_fd)); - TRY(Core::System::unlockpt(ptm_fd)); - - m_terminal_widget->set_pty_master_fd(ptm_fd); - m_terminal_widget->on_command_exit = [this, wait_for_child] { - if (wait_for_child == WaitForChildOnExit::Yes) { - auto result = Core::System::waitpid(m_pid, 0); - if (result.is_error()) { - warnln("{}", result.error()); - VERIFY_NOT_REACHED(); - } - int wstatus = result.release_value().status; - - if (WIFEXITED(wstatus)) { - m_terminal_widget->inject_string(ByteString::formatted("\033[{};1m(Command exited with code {})\033[0m\r\n", wstatus == 0 ? 32 : 31, WEXITSTATUS(wstatus))); - } else if (WIFSTOPPED(wstatus)) { - m_terminal_widget->inject_string("\033[34;1m(Command stopped!)\033[0m\r\n"sv); - } else if (WIFSIGNALED(wstatus)) { - m_terminal_widget->inject_string(ByteString::formatted("\033[34;1m(Command signaled with {}!)\033[0m\r\n", strsignal(WTERMSIG(wstatus)))); - } - - m_child_exit_status = WEXITSTATUS(wstatus); - m_child_exited = true; - } - m_pid = -1; - - if (on_command_exit) - on_command_exit(); - }; - - terminal().scroll_to_bottom(); - - error_happened = false; - - return ptm_fd; -} - -ErrorOr TerminalWrapper::setup_slave_pseudoterminal(int master_fd) -{ - setsid(); - - auto tty_name = TRY(Core::System::ptsname(master_fd)); - - close(master_fd); - - int pts_fd = TRY(Core::System::open(tty_name, O_RDWR)); - - tcsetpgrp(pts_fd, getpid()); - - // NOTE: It's okay if this fails. - ioctl(0, TIOCNOTTY); - - close(0); - close(1); - close(2); - - TRY(Core::System::dup2(pts_fd, 0)); - TRY(Core::System::dup2(pts_fd, 1)); - TRY(Core::System::dup2(pts_fd, 2)); - - TRY(Core::System::close(pts_fd)); - - TRY(Core::System::ioctl(0, TIOCSCTTY)); - - setenv("TERM", "xterm", true); - - return {}; -} - -ErrorOr TerminalWrapper::kill_running_command() -{ - VERIFY(m_pid != -1); - - // Kill our child process and its whole process group. - TRY(Core::System::killpg(m_pid, SIGTERM)); - return {}; -} - -void TerminalWrapper::clear_including_history() -{ - m_terminal_widget->clear_including_history(); -} - -TerminalWrapper::TerminalWrapper(bool user_spawned) - : m_user_spawned(user_spawned) -{ - set_layout(); - - m_terminal_widget = add(-1, false); - if (user_spawned) { - auto maybe_error = run_command("Shell"); - if (maybe_error.is_error()) - warnln("{}", maybe_error.release_error()); - } -} - -int TerminalWrapper::child_exit_status() const -{ - VERIFY(m_child_exit_status.has_value()); - return m_child_exit_status.value(); -} - -} diff --git a/Userland/DevTools/HackStudio/TerminalWrapper.h b/Userland/DevTools/HackStudio/TerminalWrapper.h deleted file mode 100644 index 5355a8504ba..00000000000 --- a/Userland/DevTools/HackStudio/TerminalWrapper.h +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * Copyright (c) 2022, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include - -namespace HackStudio { - -class TerminalWrapper final : public GUI::Widget { - C_OBJECT(TerminalWrapper) -public: - virtual ~TerminalWrapper() override = default; - enum class WaitForExit { - No, - Yes - }; - ErrorOr run_command(ByteString const&, Optional working_directory = {}, WaitForExit = WaitForExit::No, Optional failure_message = {}); - ErrorOr kill_running_command(); - void clear_including_history(); - - bool user_spawned() const { return m_user_spawned; } - VT::TerminalWidget& terminal() { return *m_terminal_widget; } - - enum class WaitForChildOnExit { - No, - Yes, - }; - ErrorOr setup_master_pseudoterminal(WaitForChildOnExit = WaitForChildOnExit::Yes); - static ErrorOr setup_slave_pseudoterminal(int master_fd); - int child_exit_status() const; - - Function on_command_exit; - -private: - explicit TerminalWrapper(bool user_spawned = true); - - RefPtr m_terminal_widget; - pid_t m_pid { -1 }; - bool m_user_spawned { true }; - bool m_child_exited { false }; - Optional m_child_exit_status; -}; - -} diff --git a/Userland/DevTools/HackStudio/ToDoEntries.cpp b/Userland/DevTools/HackStudio/ToDoEntries.cpp deleted file mode 100644 index 5860946a7f9..00000000000 --- a/Userland/DevTools/HackStudio/ToDoEntries.cpp +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright (c) 2021, Federico Guerinoni - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "ToDoEntries.h" - -namespace HackStudio { - -ToDoEntries& HackStudio::ToDoEntries::the() -{ - static ToDoEntries s_instance; - return s_instance; -} - -void ToDoEntries::set_entries(ByteString const& filename, Vector const&& entries) -{ - m_document_to_entries.set(filename, move(entries)); - if (on_update) - on_update(); -} - -Vector ToDoEntries::get_entries() -{ - Vector ret; - for (auto& it : m_document_to_entries) { - for (auto& entry : it.value) - ret.append({ entry.content, it.key, entry.line, entry.column }); - } - return ret; -} - -void ToDoEntries::clear_entries() -{ - m_document_to_entries.clear(); -} - -} diff --git a/Userland/DevTools/HackStudio/ToDoEntries.h b/Userland/DevTools/HackStudio/ToDoEntries.h deleted file mode 100644 index 31e3358dba1..00000000000 --- a/Userland/DevTools/HackStudio/ToDoEntries.h +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright (c) 2021, Federico Guerinoni - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include - -namespace HackStudio { - -class ToDoEntries { - AK_MAKE_NONCOPYABLE(ToDoEntries); - -public: - static ToDoEntries& the(); - - void set_entries(ByteString const& filename, Vector const&& entries); - - Vector get_entries(); - - void clear_entries(); - - Function on_update = nullptr; - -private: - ToDoEntries() = default; - HashMap> m_document_to_entries; -}; - -} diff --git a/Userland/DevTools/HackStudio/ToDoEntriesWidget.cpp b/Userland/DevTools/HackStudio/ToDoEntriesWidget.cpp deleted file mode 100644 index d3b73557ec3..00000000000 --- a/Userland/DevTools/HackStudio/ToDoEntriesWidget.cpp +++ /dev/null @@ -1,111 +0,0 @@ -/* - * Copyright (c) 2021, Federico Guerinoni - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "ToDoEntriesWidget.h" -#include "HackStudio.h" -#include "ToDoEntries.h" -#include -#include - -namespace HackStudio { - -class ToDoEntriesModel final : public GUI::Model { -public: - enum Column { - Filename, - Text, - Line, - Column, - __Count - }; - - explicit ToDoEntriesModel(Vector const&& matches) - : m_matches(move(matches)) - { - } - - virtual int row_count(GUI::ModelIndex const& = GUI::ModelIndex()) const override { return m_matches.size(); } - virtual int column_count(GUI::ModelIndex const& = GUI::ModelIndex()) const override { return Column::__Count; } - - virtual ErrorOr column_name(int column) const override - { - switch (column) { - case Column::Filename: - return "Filename"_string; - case Column::Text: - return "Text"_string; - case Column::Line: - return "Line"_string; - case Column::Column: - return "Col"_string; - default: - VERIFY_NOT_REACHED(); - } - } - - virtual GUI::Variant data(GUI::ModelIndex const& index, GUI::ModelRole role) const override - { - if (role == GUI::ModelRole::TextAlignment) - return Gfx::TextAlignment::CenterLeft; - if (role == GUI::ModelRole::Font) { - if (index.column() == Column::Text) - return Gfx::FontDatabase::default_fixed_width_font(); - return {}; - } - if (role == GUI::ModelRole::Display) { - auto& match = m_matches.at(index.row()); - switch (index.column()) { - case Column::Filename: - return match.filename; - case Column::Text: - return match.content; - case Column::Line: - return ByteString::formatted("{}", match.line + 1); - case Column::Column: - return ByteString::formatted("{}", match.column); - } - } - return {}; - } - - virtual GUI::ModelIndex index(int row, int column = 0, const GUI::ModelIndex& = GUI::ModelIndex()) const override - { - if (row < 0 || row >= (int)m_matches.size()) - return {}; - if (column < 0 || column >= Column::__Count) - return {}; - return create_index(row, column, &m_matches.at(row)); - } - -private: - Vector m_matches; -}; - -void ToDoEntriesWidget::refresh() -{ - auto const& entries = ToDoEntries::the().get_entries(); - auto results_model = adopt_ref(*new ToDoEntriesModel(move(entries))); - m_result_view->set_model(results_model); -} - -void ToDoEntriesWidget::clear() -{ - ToDoEntries::the().clear_entries(); - refresh(); -} - -ToDoEntriesWidget::ToDoEntriesWidget() -{ - set_layout(); - m_result_view = add(); - - m_result_view->on_activation = [](auto& index) { - auto& match = *(CodeComprehension::TodoEntry const*)index.internal_data(); - open_file(match.filename, match.line, match.column); - }; -} - -} diff --git a/Userland/DevTools/HackStudio/ToDoEntriesWidget.h b/Userland/DevTools/HackStudio/ToDoEntriesWidget.h deleted file mode 100644 index 881b7cf3697..00000000000 --- a/Userland/DevTools/HackStudio/ToDoEntriesWidget.h +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright (c) 2021, Federico Guerinoni - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include - -namespace HackStudio { - -class ToDoEntriesWidget final : public GUI::Widget { - C_OBJECT(ToDoEntriesWidget) -public: - virtual ~ToDoEntriesWidget() override { } - - void refresh(); - - void clear(); - -private: - explicit ToDoEntriesWidget(); - - RefPtr m_result_view; -}; - -} diff --git a/Userland/DevTools/HackStudio/main.cpp b/Userland/DevTools/HackStudio/main.cpp deleted file mode 100644 index e42f942b895..00000000000 --- a/Userland/DevTools/HackStudio/main.cpp +++ /dev/null @@ -1,238 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * Copyright (c) 2024, Sam Atkins - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "Editor.h" -#include "HackStudio.h" -#include "HackStudioWidget.h" -#include "Project.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -using namespace HackStudio; - -static WeakPtr s_hack_studio_widget; - -static bool make_is_available(); -static ErrorOr notify_make_not_available(); -static void update_path_environment_variable(); -static Optional last_opened_project_path(); -static ErrorOr> create_hack_studio_widget(bool mode_coredump, StringView path, pid_t pid_to_debug); - -ErrorOr serenity_main(Main::Arguments arguments) -{ - TRY(Core::System::pledge("stdio recvfd sendfd tty rpath cpath wpath proc exec unix fattr thread ptrace")); - - auto app = TRY(GUI::Application::create(arguments)); - app->set_config_domain("HackStudio"_string); - Config::enable_permissive_mode(); - Config::pledge_domains({ "HackStudio", "Terminal", "FileManager" }); - - auto window = GUI::Window::construct(); - window->restore_size_and_position("HackStudio"sv, "Window"sv, { { 840, 600 } }); - window->save_size_and_position_on_close("HackStudio"sv, "Window"sv); - auto icon = TRY(Gfx::Bitmap::load_from_file("/res/icons/16x16/app-hack-studio.png"sv)); - window->set_icon(icon); - - update_path_environment_variable(); - - if (!make_is_available()) { - TRY(notify_make_not_available()); - } - - StringView path_argument; - bool mode_coredump = false; - pid_t pid_to_debug = -1; - Core::ArgsParser args_parser; - args_parser.add_positional_argument(path_argument, "Path to a workspace or a file", "path", Core::ArgsParser::Required::No); - args_parser.add_option(mode_coredump, "Debug a coredump in HackStudio", "coredump", 'c'); - args_parser.add_option(pid_to_debug, "Attach debugger to running process", "pid", 'p', "PID"); - args_parser.parse(arguments); - - auto hack_studio_widget = TRY(create_hack_studio_widget(mode_coredump, path_argument, pid_to_debug)); - window->set_main_widget(hack_studio_widget); - s_hack_studio_widget = hack_studio_widget; - - window->set_title(ByteString::formatted("{} - Hack Studio", hack_studio_widget->project().name())); - - TRY(hack_studio_widget->initialize_menubar(*window)); - - window->on_close_request = [&]() -> GUI::Window::CloseRequestDecision { - hack_studio_widget->locator().close(); - if (hack_studio_widget->warn_unsaved_changes("There are unsaved changes, do you want to save before exiting?") == HackStudioWidget::ContinueDecision::Yes) - return GUI::Window::CloseRequestDecision::Close; - return GUI::Window::CloseRequestDecision::StayOpen; - }; - - window->show(); - hack_studio_widget->update_actions(); - - if (mode_coredump) - hack_studio_widget->open_coredump(path_argument); - - if (pid_to_debug != -1) - hack_studio_widget->debug_process(pid_to_debug); - - return app->exec(); -} - -static bool make_is_available() -{ - auto maybe_process = Core::Process::spawn({ - .executable = "make", - .search_for_executable_in_path = true, - .arguments = { "--version" }, - .file_actions = { Core::FileAction::OpenFile { - .path = "/dev/null"sv, - .mode = Core::File::OpenMode::Write, - .fd = STDOUT_FILENO, - } }, - }); - if (maybe_process.is_error()) { - warnln("Failed to spawn make: {}", maybe_process.release_error()); - return false; - } - auto process = maybe_process.release_value(); - - auto maybe_result = process.wait_for_termination(); - if (maybe_result.is_error()) { - warnln("Error running make: {}", maybe_result.release_error()); - return false; - } - - return maybe_result.value(); -} - -static ErrorOr notify_make_not_available() -{ - auto notification = GUI::Notification::construct(); - auto icon = TRY(Gfx::Bitmap::load_from_file("/res/icons/32x32/app-hack-studio.png"sv)); - notification->set_icon(icon); - notification->set_title("'make' Not Available"_string); - notification->set_text("You probably want to install the binutils, gcc, and make ports from the root of the Serenity repository"_string); - notification->show(); - return {}; -} - -static void update_path_environment_variable() -{ - StringBuilder path; - - auto const* path_env_ptr = getenv("PATH"); - if (path_env_ptr != NULL) - path.append({ path_env_ptr, strlen(path_env_ptr) }); - - if (path.length()) - path.append(':'); - path.append(DEFAULT_PATH_SV); - setenv("PATH", path.to_byte_string().characters(), true); -} - -static Optional last_opened_project_path() -{ - auto projects = HackStudioWidget::read_recent_projects(); - if (projects.size() == 0) - return {}; - - if (!FileSystem::exists(projects[0])) - return {}; - - return { projects[0] }; -} - -namespace HackStudio { - -GUI::TextEditor& current_editor() -{ - return s_hack_studio_widget->current_editor(); -} - -void open_file(ByteString const& filename) -{ - s_hack_studio_widget->open_file(filename); -} - -void open_file(ByteString const& filename, size_t line, size_t column) -{ - s_hack_studio_widget->open_file(filename, line, column); -} - -RefPtr current_editor_wrapper() -{ - if (!s_hack_studio_widget) - return nullptr; - return s_hack_studio_widget->current_editor_wrapper(); -} - -Project& project() -{ - return s_hack_studio_widget->project(); -} - -ByteString currently_open_file() -{ - if (!s_hack_studio_widget) - return {}; - return s_hack_studio_widget->active_file(); -} - -void set_current_editor_wrapper(RefPtr wrapper) -{ - s_hack_studio_widget->set_current_editor_wrapper(wrapper); -} - -void update_editor_window_title() -{ - s_hack_studio_widget->update_current_editor_title(); - s_hack_studio_widget->update_window_title(); -} - -Locator& locator() -{ - return s_hack_studio_widget->locator(); -} - -void for_each_open_file(Function func) -{ - s_hack_studio_widget->for_each_open_file(move(func)); -} - -bool semantic_syntax_highlighting_is_enabled() -{ - return s_hack_studio_widget->semantic_syntax_highlighting_is_enabled(); -} - -} - -static ErrorOr> create_hack_studio_widget(bool mode_coredump, StringView raw_path_argument, pid_t pid_to_debug) -{ - ByteString project_path; - if (pid_to_debug != -1 || mode_coredump) - project_path = "/usr/src/serenity"; - else if (!raw_path_argument.is_null()) - project_path = raw_path_argument; - else if (auto last_path = last_opened_project_path(); last_path.has_value()) - project_path = last_path.release_value(); - else - project_path = TRY(FileSystem::real_path("."sv)); - - return HackStudioWidget::create(project_path); -} diff --git a/Userland/DevTools/Profiler/CMakeLists.txt b/Userland/DevTools/Profiler/CMakeLists.txt deleted file mode 100644 index a61a77392eb..00000000000 --- a/Userland/DevTools/Profiler/CMakeLists.txt +++ /dev/null @@ -1,27 +0,0 @@ -serenity_component( - Profiler - RECOMMENDED - TARGETS Profiler -) - -set(SOURCES - DisassemblyModel.cpp - main.cpp - IndividualSampleModel.cpp - FlameGraphView.cpp - FilesystemEventModel.cpp - Gradient.cpp - Process.cpp - Profile.cpp - ProfileModel.cpp - SamplesModel.cpp - SignpostsModel.cpp - SourceModel.cpp - TimelineContainer.cpp - TimelineHeader.cpp - TimelineTrack.cpp - TimelineView.cpp - ) - -serenity_app(Profiler ICON app-profiler) -target_link_libraries(Profiler PRIVATE LibCore LibDebug LibELF LibFileSystem LibGfx LibGUI LibDesktop LibX86 LibSymbolication LibMain LibURL) diff --git a/Userland/DevTools/Profiler/DisassemblyModel.cpp b/Userland/DevTools/Profiler/DisassemblyModel.cpp deleted file mode 100644 index be5c7672b2f..00000000000 --- a/Userland/DevTools/Profiler/DisassemblyModel.cpp +++ /dev/null @@ -1,240 +0,0 @@ -/* - * Copyright (c) 2020, Andreas Kling - * Copyright (c) 2023, Jelle Raaijmakers - * Copyright (c) 2022, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "DisassemblyModel.h" -#include "Gradient.h" -#include "PercentageFormatting.h" -#include "Profile.h" -#include -#include -#include -#include -#include -#include -#include - -namespace Profiler { - -static Optional s_kernel_binary; - -static ELF::Image* try_load_kernel_binary() -{ - if (s_kernel_binary.has_value()) - return &s_kernel_binary->elf; - auto kernel_binary_or_error = Core::MappedFile::map("/boot/Kernel"sv); - if (!kernel_binary_or_error.is_error()) { - auto kernel_binary = kernel_binary_or_error.release_value(); - auto image = ELF::Image(kernel_binary->bytes()); - s_kernel_binary = { { move(kernel_binary), image } }; - return &s_kernel_binary->elf; - } - return nullptr; -} - -DisassemblyModel::DisassemblyModel(Profile& profile, ProfileNode& node) - : m_profile(profile) - , m_node(node) -{ - FlatPtr base_address = 0; - Debug::DebugInfo const* debug_info; - ELF::Image const* elf; - if (auto maybe_kernel_base = Symbolication::kernel_base(); maybe_kernel_base.has_value() && m_node.address() >= *maybe_kernel_base) { - if (!g_kernel_debuginfo_object.has_value()) - return; - base_address = maybe_kernel_base.release_value(); - elf = try_load_kernel_binary(); - if (elf == nullptr) - return; - if (g_kernel_debug_info == nullptr) - g_kernel_debug_info = make(g_kernel_debuginfo_object->elf, ByteString::empty(), base_address); - debug_info = g_kernel_debug_info.ptr(); - } else { - auto const& process = node.process(); - auto const* library_data = process.library_metadata.library_containing(node.address()); - if (!library_data) { - dbgln("no library data for address {:p}", node.address()); - return; - } - base_address = library_data->base; - elf = &library_data->object->elf; - debug_info = &library_data->load_debug_info(base_address); - } - - VERIFY(elf != nullptr); - VERIFY(debug_info != nullptr); - - FlatPtr function_address = node.address() - base_address; - auto is_function_address = false; - auto function = debug_info->get_containing_function(function_address); - if (function.has_value()) { - if (function_address == function->address_low) - is_function_address = true; - function_address = function->address_low; - } else { - dbgln("DisassemblyModel: Function containing {:p} ({}) not found", node.address() - base_address, node.symbol()); - } - - auto symbol = elf->find_symbol(function_address); - if (!symbol.has_value()) { - dbgln("DisassemblyModel: symbol not found"); - return; - } - if (!symbol.value().raw_data().length()) { - dbgln("DisassemblyModel: Found symbol without code"); - return; - } - VERIFY(symbol.has_value()); - - auto symbol_offset_from_function_start = node.address() - base_address - symbol->value(); - auto view = symbol.value().raw_data().substring_view(symbol_offset_from_function_start); - - X86::ELFSymbolProvider symbol_provider(*elf, base_address); - X86::SimpleInstructionStream stream((u8 const*)view.characters_without_null_termination(), view.length()); - X86::Disassembler disassembler(stream); - - size_t offset_into_symbol = 0; - FlatPtr last_instruction_offset = 0; - if (!is_function_address) { - FlatPtr last_instruction_address = 0; - for (auto const& event : node.events_per_address()) - last_instruction_address = max(event.key, last_instruction_address); - last_instruction_offset = last_instruction_address - node.address(); - } - for (;;) { - if (!is_function_address && offset_into_symbol > last_instruction_offset) - break; - - auto insn = disassembler.next(); - if (!insn.has_value()) - break; - FlatPtr address_in_profiled_program = node.address() + offset_into_symbol; - - auto disassembly = insn.value().to_byte_string(address_in_profiled_program, &symbol_provider); - - StringView instruction_bytes = view.substring_view(offset_into_symbol, insn.value().length()); - u32 samples_at_this_instruction = m_node.events_per_address().get(address_in_profiled_program).value_or(0); - float percent = ((float)samples_at_this_instruction / (float)m_node.event_count()) * 100.0f; - auto source_position = debug_info->get_source_position_with_inlines(address_in_profiled_program - base_address).release_value_but_fixme_should_propagate_errors(); - - m_instructions.append({ insn.value(), disassembly, instruction_bytes, address_in_profiled_program, samples_at_this_instruction, percent, source_position }); - - offset_into_symbol += insn.value().length(); - } -} - -int DisassemblyModel::row_count(GUI::ModelIndex const&) const -{ - return m_instructions.size(); -} - -ErrorOr DisassemblyModel::column_name(int column) const -{ - switch (column) { - case Column::SampleCount: - return m_profile.show_percentages() ? "% Samples"_string : "# Samples"_string; - case Column::Address: - return "Address"_string; - case Column::InstructionBytes: - return "Insn Bytes"_string; - case Column::Disassembly: - return "Disassembly"_string; - case Column::SourceLocation: - return "Source Location"_string; - default: - VERIFY_NOT_REACHED(); - } -} - -struct ColorPair { - Color background; - Color foreground; -}; - -static Optional color_pair_for(InstructionData const& insn) -{ - if (insn.percent == 0) - return {}; - - Color background = color_for_percent(insn.percent); - Color foreground; - if (insn.percent > 50) - foreground = Color::White; - else - foreground = Color::Black; - return ColorPair { background, foreground }; -} - -GUI::Variant DisassemblyModel::data(GUI::ModelIndex const& index, GUI::ModelRole role) const -{ - auto const& insn = m_instructions[index.row()]; - - if (role == GUI::ModelRole::BackgroundColor) { - auto colors = color_pair_for(insn); - if (!colors.has_value()) - return {}; - return colors.value().background; - } - - if (role == GUI::ModelRole::ForegroundColor) { - auto colors = color_pair_for(insn); - if (!colors.has_value()) - return {}; - return colors.value().foreground; - } - - if (role == GUI::ModelRole::TextAlignment) { - if (index.column() == Column::SampleCount) - return Gfx::TextAlignment::CenterRight; - } - - if (role == GUI::ModelRole::Display) { - if (index.column() == Column::SampleCount) { - if (m_profile.show_percentages()) - return format_percentage(insn.event_count, m_node.event_count()); - return insn.event_count; - } - - if (index.column() == Column::Address) - return ByteString::formatted("{:p}", insn.address); - - if (index.column() == Column::InstructionBytes) { - StringBuilder builder; - for (auto ch : insn.bytes) { - builder.appendff("{:02x} ", (u8)ch); - } - return builder.to_byte_string(); - } - - if (index.column() == Column::Disassembly) - return insn.disassembly; - - if (index.column() == Column::SourceLocation) { - StringBuilder builder; - auto first = true; - for (auto const& entry : insn.source_position_with_inlines.inline_chain) { - if (first) - first = false; - else - builder.append(" => "sv); - builder.appendff("{}:{}", entry.file_path, entry.line_number); - } - if (insn.source_position_with_inlines.source_position.has_value()) { - if (!first) - builder.append(" => "sv); - auto const& entry = insn.source_position_with_inlines.source_position.value(); - builder.appendff("{}:{}", entry.file_path, entry.line_number); - } - return builder.to_byte_string(); - } - - return {}; - } - return {}; -} - -} diff --git a/Userland/DevTools/Profiler/DisassemblyModel.h b/Userland/DevTools/Profiler/DisassemblyModel.h deleted file mode 100644 index 038298e5639..00000000000 --- a/Userland/DevTools/Profiler/DisassemblyModel.h +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright (c) 2020, Andreas Kling - * Copyright (c) 2022, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include - -namespace Profiler { - -class Profile; -class ProfileNode; - -struct InstructionData { - X86::Instruction insn; - ByteString disassembly; - StringView bytes; - FlatPtr address { 0 }; - u32 event_count { 0 }; - float percent { 0 }; - Debug::DebugInfo::SourcePositionWithInlines source_position_with_inlines; -}; - -class DisassemblyModel final : public GUI::Model { -public: - static NonnullRefPtr create(Profile& profile, ProfileNode& node) - { - return adopt_ref(*new DisassemblyModel(profile, node)); - } - - enum Column { - Address, - SampleCount, - InstructionBytes, - Disassembly, - SourceLocation, - __Count - }; - - virtual ~DisassemblyModel() override = default; - - virtual int row_count(GUI::ModelIndex const& = GUI::ModelIndex()) const override; - virtual int column_count(GUI::ModelIndex const& = GUI::ModelIndex()) const override { return Column::__Count; } - virtual ErrorOr column_name(int) const override; - virtual GUI::Variant data(GUI::ModelIndex const&, GUI::ModelRole) const override; - virtual bool is_column_sortable(int) const override { return false; } - -private: - DisassemblyModel(Profile&, ProfileNode&); - - Profile& m_profile; - ProfileNode& m_node; - - Vector m_instructions; -}; - -} diff --git a/Userland/DevTools/Profiler/EventSerialNumber.h b/Userland/DevTools/Profiler/EventSerialNumber.h deleted file mode 100644 index a1934c5b332..00000000000 --- a/Userland/DevTools/Profiler/EventSerialNumber.h +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright (c) 2021, Gunnar Beutner - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include - -namespace Profiler { - -// DistinctNumeric's constructor is non-explicit which makes accidentally turning -// an unrelated u64 into a serial number all too easy. -class EventSerialNumber { -public: - constexpr EventSerialNumber() = default; - - void increment() - { - m_serial++; - } - - size_t to_number() const - { - return m_serial; - } - - static EventSerialNumber max_valid_serial() - { - return EventSerialNumber { NumericLimits::max() }; - } - - bool operator==(EventSerialNumber const& rhs) const - { - return m_serial == rhs.m_serial; - } - - bool operator<(EventSerialNumber const& rhs) const - { - return m_serial < rhs.m_serial; - } - - bool operator>(EventSerialNumber const& rhs) const - { - return m_serial > rhs.m_serial; - } - - bool operator<=(EventSerialNumber const& rhs) const - { - return m_serial <= rhs.m_serial; - } - - bool operator>=(EventSerialNumber const& rhs) const - { - return m_serial >= rhs.m_serial; - } - -private: - size_t m_serial { 0 }; - - constexpr explicit EventSerialNumber(size_t serial) - : m_serial(serial) - { - } -}; - -} diff --git a/Userland/DevTools/Profiler/FilesystemEventModel.cpp b/Userland/DevTools/Profiler/FilesystemEventModel.cpp deleted file mode 100644 index 80e844a0409..00000000000 --- a/Userland/DevTools/Profiler/FilesystemEventModel.cpp +++ /dev/null @@ -1,234 +0,0 @@ -/* - * Copyright (c) 2022-2023, Jakub Berkop - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "FilesystemEventModel.h" -#include "Profile.h" -#include -#include -#include -#include - -namespace Profiler { - -FileEventModel::FileEventModel(Profile& profile) - : m_profile(profile) -{ -} - -FileEventModel::~FileEventModel() -{ -} - -u64 FileEventNode::total_count() const -{ - return m_open.count + m_close.count + m_readv.count + m_read.count + m_pread.count; -} - -Duration FileEventNode::total_duration() const -{ - return m_open.duration + m_close.duration + m_readv.duration + m_read.duration + m_pread.duration; -} - -FileEventNode& FileEventNode::find_or_create_node(ByteString const& searched_path) -{ - // TODO: Optimize this function. - if (searched_path == ""sv || searched_path == "/"sv) { - return *this; - } - - auto lex_path = LexicalPath(searched_path); - auto parts = lex_path.parts(); - auto current = parts.take_first(); - - StringBuilder sb; - sb.join('/', parts); - auto new_s = sb.to_byte_string(); - - for (auto& child : m_children) { - if (child->m_path == current) { - return child->find_or_create_node(new_s); - } - } - - if (m_parent) { - for (auto& child : m_children) { - if (child->m_path == current) { - return child->find_or_create_node(new_s); - } - } - return create_recursively(searched_path); - } else { - if (!searched_path.starts_with("/"sv)) { - m_children.append(create(searched_path, this)); - return *m_children.last(); - } - - return create_recursively(searched_path); - } -} - -FileEventNode& FileEventNode::create_recursively(ByteString new_path) -{ - auto const lex_path = LexicalPath(new_path); - auto parts = lex_path.parts(); - - if (parts.size() == 1) { - auto new_node = FileEventNode::create(parts.take_first(), this); - m_children.append(new_node); - return *new_node.ptr(); - } else { - auto new_node = FileEventNode::create(parts.take_first(), this); - m_children.append(new_node); - - StringBuilder sb; - sb.join('/', parts); - - return new_node->create_recursively(sb.to_byte_string()); - } -} - -void FileEventNode::for_each_parent_node(Function callback) -{ - auto* current = this; - while (current) { - callback(*current); - current = current->m_parent; - } -} - -GUI::ModelIndex FileEventModel::index(int row, int column, GUI::ModelIndex const& parent) const -{ - if (!parent.is_valid()) { - return create_index(row, column, m_profile.file_event_nodes()->children()[row].ptr()); - } - auto& remote_parent = *static_cast(parent.internal_data()); - return create_index(row, column, remote_parent.children().at(row).ptr()); -} - -GUI::ModelIndex FileEventModel::parent_index(GUI::ModelIndex const& index) const -{ - if (!index.is_valid()) - return {}; - auto& node = *static_cast(index.internal_data()); - if (!node.parent()) - return {}; - - if (node.parent()->parent()) { - auto const& children = node.parent()->parent()->children(); - - for (size_t row = 0; row < children.size(); ++row) { - if (children.at(row).ptr() == node.parent()) { - return create_index(row, index.column(), node.parent()); - } - } - } - - auto const& children = node.parent()->children(); - - for (size_t row = 0; row < children.size(); ++row) { - if (children.at(row).ptr() == &node) { - return create_index(row, index.column(), node.parent()); - } - } - - VERIFY_NOT_REACHED(); - return {}; -} - -int FileEventModel::row_count(GUI::ModelIndex const& index) const -{ - if (!index.is_valid()) - return m_profile.file_event_nodes()->children().size(); - auto& node = *static_cast(index.internal_data()); - return node.children().size(); -} - -int FileEventModel::column_count(GUI::ModelIndex const&) const -{ - return Column::__Count; -} - -ErrorOr FileEventModel::column_name(int column) const -{ - switch (column) { - case Column::Path: - return "Path"_string; - case Column::TotalCount: - return "Total Count"_string; - case Column::TotalDuration: - return "Total Duration [ms]"_string; - case Column::OpenCount: - return "Open Count"_string; - case Column::OpenDuration: - return "Open Duration [ms]"_string; - case Column::CloseCount: - return "Close Count"_string; - case Column::CloseDuration: - return "Close Duration [ms]"_string; - case Column::ReadvCount: - return "Readv Count"_string; - case Column::ReadvDuration: - return "Readv Duration [ms]"_string; - case Column::ReadCount: - return "Read Count"_string; - case Column::ReadDuration: - return "Read Duration [ms]"_string; - case Column::PreadCount: - return "Pread Count"_string; - case Column::PreadDuration: - return "Pread Duration [ms]"_string; - default: - VERIFY_NOT_REACHED(); - } -} - -GUI::Variant FileEventModel::data(GUI::ModelIndex const& index, GUI::ModelRole role) const -{ - if (role == GUI::ModelRole::TextAlignment) { - if (index.column() == Path) - return Gfx::TextAlignment::CenterLeft; - return Gfx::TextAlignment::CenterRight; - } - - auto* node = static_cast(index.internal_data()); - - if (role == GUI::ModelRole::Display) { - switch (index.column()) { - case Column::Path: - return node->path(); - case Column::TotalCount: - return node->total_count(); - case Column::TotalDuration: - return static_cast(node->total_duration().to_nanoseconds()) / 1'000'000; - case Column::OpenCount: - return node->open().count; - case Column::OpenDuration: - return static_cast(node->open().duration.to_nanoseconds()) / 1'000'000; - case Column::CloseCount: - return node->close().count; - case Column::CloseDuration: - return static_cast(node->close().duration.to_nanoseconds()) / 1'000'000; - case Column::ReadvCount: - return node->readv().count; - case Column::ReadvDuration: - return static_cast(node->readv().duration.to_nanoseconds()) / 1'000'000; - case Column::ReadCount: - return node->read().count; - case Column::ReadDuration: - return static_cast(node->read().duration.to_nanoseconds()) / 1'000'000; - case Column::PreadCount: - return node->pread().count; - case Column::PreadDuration: - return static_cast(node->pread().duration.to_nanoseconds()) / 1'000'000; - default: - return {}; - } - } - - return {}; -} - -} diff --git a/Userland/DevTools/Profiler/FilesystemEventModel.h b/Userland/DevTools/Profiler/FilesystemEventModel.h deleted file mode 100644 index 80e45693771..00000000000 --- a/Userland/DevTools/Profiler/FilesystemEventModel.h +++ /dev/null @@ -1,115 +0,0 @@ -/* - * Copyright (c) 2022-2023, Jakub Berkop - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include - -namespace Profiler { - -class Profile; - -class FileEventNode : public RefCounted { -public: - static NonnullRefPtr create(ByteString const& path, FileEventNode* parent = nullptr) - { - return adopt_ref(*new FileEventNode(path, parent)); - } - - FileEventNode& find_or_create_node(ByteString const&); - - Vector>& children() { return m_children; } - Vector> const& children() const { return m_children; } - - FileEventNode* parent() { return m_parent; } - - FileEventNode& create_recursively(ByteString); - - void for_each_parent_node(Function callback); - - ByteString const& path() const { return m_path; } - - u64 total_count() const; - Duration total_duration() const; - - struct FileEventType { - u64 count = 0; - Duration duration = {}; - }; - - FileEventType& open() { return m_open; } - FileEventType& close() { return m_close; } - FileEventType& readv() { return m_readv; } - FileEventType& read() { return m_read; } - FileEventType& pread() { return m_pread; } - -private: - FileEventNode(ByteString const& path, FileEventNode* parent = nullptr) - : m_path(path) - , m_parent(parent) {}; - - ByteString m_path; - - FileEventType m_open; - FileEventType m_close; - FileEventType m_readv; - FileEventType m_read; - FileEventType m_pread; - - Vector> m_children; - FileEventNode* m_parent = nullptr; -}; - -class FileEventModel final : public GUI::Model { -public: - static NonnullRefPtr create(Profile& profile) - { - return adopt_ref(*new FileEventModel(profile)); - } - - enum Column { - Path, - TotalCount, - TotalDuration, - OpenCount, - OpenDuration, - CloseCount, - CloseDuration, - ReadvCount, - ReadvDuration, - ReadCount, - ReadDuration, - PreadCount, - PreadDuration, - __Count - }; - - virtual ~FileEventModel() override; - - virtual int row_count(GUI::ModelIndex const& = GUI::ModelIndex()) const override; - virtual int column_count(GUI::ModelIndex const& = GUI::ModelIndex()) const override; - virtual ErrorOr column_name(int) const override; - virtual GUI::Variant data(GUI::ModelIndex const&, GUI::ModelRole) const override; - virtual GUI::ModelIndex index(int row, int column, GUI::ModelIndex const& parent = GUI::ModelIndex()) const override; - virtual GUI::ModelIndex parent_index(GUI::ModelIndex const&) const override; - virtual int tree_column() const override { return Column::Path; } - virtual bool is_column_sortable(int) const override { return false; } - virtual bool is_searchable() const override { return true; } - -private: - explicit FileEventModel(Profile&); - - Profile& m_profile; - - GUI::Icon m_user_frame_icon; - GUI::Icon m_kernel_frame_icon; -}; - -} diff --git a/Userland/DevTools/Profiler/FlameGraphView.cpp b/Userland/DevTools/Profiler/FlameGraphView.cpp deleted file mode 100644 index c59f69d8970..00000000000 --- a/Userland/DevTools/Profiler/FlameGraphView.cpp +++ /dev/null @@ -1,275 +0,0 @@ -/* - * Copyright (c) 2021, Nicholas Hollett - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "FlameGraphView.h" -#include "Profile.h" -#include -#include -#include -#include -#include -#include - -namespace Profiler { - -constexpr int bar_rounding = 2; -constexpr int bar_margin = 2; -constexpr int bar_padding = 8; -constexpr int bar_height = 20; -constexpr int text_threshold = 30; - -Vector s_colors; - -static Vector const& get_colors() -{ - if (s_colors.is_empty()) { - // Start with a nice orange, then make shades of it - Gfx::Color midpoint(255, 94, 19); - s_colors.extend(midpoint.shades(3, 0.5f)); - s_colors.append(midpoint); - s_colors.extend(midpoint.tints(3, 0.5f)); - } - - return s_colors; -} - -FlameGraphView::FlameGraphView(GUI::Model& model, int text_column, int width_column) - : m_model(model) - , m_text_column(text_column) - , m_width_column(width_column) -{ - set_fill_with_background_color(true); - set_background_role(Gfx::ColorRole::Base); - set_scrollbars_enabled(true); - set_frame_style(Gfx::FrameStyle::NoFrame); - set_should_hide_unnecessary_scrollbars(false); - horizontal_scrollbar().set_visible(false); - - m_model.register_client(*this); - - m_colors = get_colors(); - layout_bars(); - scroll_to_bottom(); -} - -GUI::ModelIndex FlameGraphView::hovered_index() const -{ - if (!m_hovered_bar) - return GUI::ModelIndex(); - return m_hovered_bar->index; -} - -void FlameGraphView::model_did_update(unsigned) -{ - m_selected_indexes.clear(); - layout_bars(); - update(); -} - -void FlameGraphView::mousemove_event(GUI::MouseEvent& event) -{ - StackBar* hovered_bar = nullptr; - - for (size_t i = 0; i < m_bars.size(); ++i) { - auto& bar = m_bars[i]; - if (to_widget_rect(bar.rect).contains(event.x(), event.y())) { - hovered_bar = &bar; - break; - } - } - - if (m_hovered_bar == hovered_bar) - return; - - m_hovered_bar = hovered_bar; - - if (on_hover_change) - on_hover_change(); - - String label; - if (m_hovered_bar != nullptr && m_hovered_bar->index.is_valid()) { - label = bar_label(*m_hovered_bar); - } - set_tooltip(label); - show_or_hide_tooltip(); - - update(); -} - -void FlameGraphView::mousedown_event(GUI::MouseEvent& event) -{ - if (event.button() != GUI::MouseButton::Primary) - return; - - if (!m_hovered_bar) - return; - - m_selected_indexes.clear(); - GUI::ModelIndex selected_index = m_hovered_bar->index; - while (selected_index.is_valid()) { - m_selected_indexes.append(selected_index); - selected_index = selected_index.parent(); - } - - layout_bars(); - update(); -} - -void FlameGraphView::resize_event(GUI::ResizeEvent& event) -{ - auto old_scroll = vertical_scrollbar().value(); - - AbstractScrollableWidget::resize_event(event); - - // Adjust scroll to keep the bottom of the graph fixed - auto available_height_delta = m_old_available_size.height() - available_size().height(); - - vertical_scrollbar().set_value(old_scroll + available_height_delta); - - layout_bars(); - - m_old_available_size = available_size(); -} - -void FlameGraphView::paint_event(GUI::PaintEvent& event) -{ - GUI::Painter painter(*this); - painter.add_clip_rect(event.rect()); - - auto content_clip_rect = to_content_rect(event.rect()); - - for (auto const& bar : m_bars) { - if (!content_clip_rect.intersects_vertically(bar.rect)) - continue; - - auto label = bar_label(bar); - - auto color = m_colors[label.hash() % m_colors.size()]; - - if (&bar == m_hovered_bar) - color = color.lightened(1.2f); - - if (bar.selected) - color = color.with_alpha(128); - - auto rect = to_widget_rect(bar.rect); - - // Do rounded corners if the node will draw with enough width - if (rect.width() > (bar_rounding * 3)) - painter.fill_rect_with_rounded_corners(rect.shrunken(0, bar_margin), color, bar_rounding); - else - painter.fill_rect(rect.shrunken(0, bar_margin), color); - - if (rect.width() > text_threshold) { - painter.draw_text( - rect.shrunken(bar_padding, 0), - label, - painter.font(), - Gfx::TextAlignment::CenterLeft, - Gfx::Color::Black, - Gfx::TextElision::Right); - } - } -} - -String FlameGraphView::bar_label(StackBar const& bar) const -{ - auto label_index = bar.index.sibling_at_column(m_text_column); - String label = "All"_string; - if (label_index.is_valid()) { - label = MUST(String::from_byte_string(m_model.data(label_index).to_byte_string())); - } - return label; -} - -void FlameGraphView::layout_bars() -{ - m_bars.clear(); - m_hovered_bar = nullptr; - - // Explicit copy here so the layout can mutate - Vector selected = m_selected_indexes; - GUI::ModelIndex null_index; - layout_children(null_index, 0, 0, available_size().width(), selected); - - // Translate bars from (-height..0) to (0..height) now that we know the height, - // use available height as minimum to keep the graph at the bottom when it's small - int height = available_size().height(); - - for (auto& bar : m_bars) - height = max(height, -bar.rect.top()); - for (auto& bar : m_bars) - bar.rect.translate_by(0, height); - - // Update scrollbars if height changed - if (height != content_size().height()) { - auto old_content_height = content_size().height(); - auto old_scroll = vertical_scrollbar().value(); - - set_content_size(Gfx::IntSize(available_size().width(), height)); - - // Adjust scroll to keep the bottom of the graph fixed, so it doesn't jump - // around when double-clicking - auto content_height_delta = old_content_height - content_size().height(); - - vertical_scrollbar().set_value(old_scroll - content_height_delta); - } -} - -void FlameGraphView::layout_children(GUI::ModelIndex& index, int depth, int left, int right, Vector& selected_nodes) -{ - auto available_width = right - left; - if (available_width < 1) - return; - - auto y = -(bar_height * depth) - bar_height; - - u32 node_event_count = 0; - if (!index.is_valid()) { - // We're at the root, so calculate the event count across all roots - for (auto i = 0; i < m_model.row_count(index); ++i) { - auto const& root = *static_cast(m_model.index(i).internal_data()); - node_event_count += root.event_count(); - } - m_bars.append({ {}, { left, y, available_width, bar_height }, false }); - } else { - auto const* node = static_cast(index.internal_data()); - - bool selected = !selected_nodes.is_empty(); - if (selected) { - VERIFY(selected_nodes.take_last() == index); - } - - node_event_count = node->event_count(); - - Gfx::IntRect node_rect { left, y, available_width, bar_height }; - m_bars.append({ index, node_rect, selected }); - } - - float width_per_sample = static_cast(available_width) / node_event_count; - float new_left = static_cast(left); - - for (auto i = 0; i < m_model.row_count(index); ++i) { - auto child_index = m_model.index(i, 0, index); - if (!child_index.is_valid()) - continue; - - if (!selected_nodes.is_empty()) { - if (selected_nodes.last() != child_index) - continue; - - layout_children(child_index, depth + 1, left, right, selected_nodes); - return; - } - - auto const* child = static_cast(child_index.internal_data()); - float child_width = width_per_sample * child->event_count(); - layout_children(child_index, depth + 1, static_cast(new_left), static_cast(new_left + child_width), selected_nodes); - new_left += child_width; - } -} - -} diff --git a/Userland/DevTools/Profiler/FlameGraphView.h b/Userland/DevTools/Profiler/FlameGraphView.h deleted file mode 100644 index 97035e3d8bf..00000000000 --- a/Userland/DevTools/Profiler/FlameGraphView.h +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright (c) 2021, Nicholas Hollett - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include "Profile.h" -#include -#include -#include -#include -#include -#include -#include - -namespace Profiler { - -class FlameGraphView final : public GUI::AbstractScrollableWidget - , GUI::ModelClient { - C_OBJECT(FlameGraphView); - -public: - virtual ~FlameGraphView() override = default; - - Function on_hover_change; - - GUI::ModelIndex hovered_index() const; - -protected: - virtual void model_did_update(unsigned flags) override; - - virtual void mousemove_event(GUI::MouseEvent&) override; - virtual void mousedown_event(GUI::MouseEvent&) override; - - virtual void resize_event(GUI::ResizeEvent&) override; - virtual void paint_event(GUI::PaintEvent&) override; - -private: - explicit FlameGraphView(GUI::Model&, int text_column, int width_column); - - struct StackBar { - GUI::ModelIndex const index; - Gfx::IntRect rect; - bool selected; - }; - - String bar_label(StackBar const&) const; - void layout_bars(); - void layout_children(GUI::ModelIndex& parent, int depth, int left, int right, Vector& selected); - - GUI::Model& m_model; - int m_text_column { -1 }; - int m_width_column { -1 }; - Vector m_colors; - Vector m_bars; - StackBar* m_hovered_bar {}; - Vector m_selected_indexes; - Gfx::IntSize m_old_available_size {}; -}; - -} diff --git a/Userland/DevTools/Profiler/Gradient.cpp b/Userland/DevTools/Profiler/Gradient.cpp deleted file mode 100644 index 17a50a47424..00000000000 --- a/Userland/DevTools/Profiler/Gradient.cpp +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright (c) 2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "Gradient.h" -#include - -namespace Profiler { - -static Gfx::Bitmap const& heat_gradient() -{ - static RefPtr bitmap; - if (!bitmap) { - bitmap = Gfx::Bitmap::create(Gfx::BitmapFormat::BGRx8888, { 101, 1 }).release_value_but_fixme_should_propagate_errors(); - GUI::Painter painter(*bitmap); - painter.fill_rect_with_gradient(Orientation::Horizontal, bitmap->rect(), Color::from_rgb(0xffc080), Color::from_rgb(0xff3000)); - } - return *bitmap; -} - -Color color_for_percent(u8 percent) -{ - VERIFY(percent <= 100); - return heat_gradient().get_pixel(percent, 0); -} - -} diff --git a/Userland/DevTools/Profiler/Gradient.h b/Userland/DevTools/Profiler/Gradient.h deleted file mode 100644 index 60a198a42d0..00000000000 --- a/Userland/DevTools/Profiler/Gradient.h +++ /dev/null @@ -1,15 +0,0 @@ -/* - * Copyright (c) 2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include - -namespace Profiler { - -Color color_for_percent(u8 percent); - -} diff --git a/Userland/DevTools/Profiler/Histogram.h b/Userland/DevTools/Profiler/Histogram.h deleted file mode 100644 index 3380e7741ae..00000000000 --- a/Userland/DevTools/Profiler/Histogram.h +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright (c) 2021, Gunnar Beutner - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include - -template -class Histogram { -public: - Histogram(Timestamp start, Timestamp end, size_t bucket_count) - : m_start(start) - , m_end(end) - { - m_buckets.resize(bucket_count); - } - - void insert(Timestamp const& timestamp, Value value = 1) - { - VERIFY(timestamp >= m_start && timestamp <= m_end); - auto bucket = (timestamp - m_start) * (m_buckets.size() - 1) / (m_end - m_start); - m_buckets[bucket] += value; - } - - Value at(size_t index) const - { - return m_buckets[index]; - } - - size_t size() const - { - return m_buckets.size(); - } - -private: - Timestamp m_start { 0 }; - Timestamp m_end { 0 }; - Vector m_buckets; -}; diff --git a/Userland/DevTools/Profiler/IndividualSampleModel.cpp b/Userland/DevTools/Profiler/IndividualSampleModel.cpp deleted file mode 100644 index e4d587f80a9..00000000000 --- a/Userland/DevTools/Profiler/IndividualSampleModel.cpp +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright (c) 2018-2021, Andreas Kling - * Copyright (c) 2022, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "IndividualSampleModel.h" -#include "Profile.h" -#include -#include - -namespace Profiler { - -IndividualSampleModel::IndividualSampleModel(Profile& profile, size_t event_index) - : m_profile(profile) - , m_event_index(event_index) -{ -} - -int IndividualSampleModel::row_count(GUI::ModelIndex const&) const -{ - auto const& event = m_profile.events().at(m_event_index); - return event.frames.size(); -} - -int IndividualSampleModel::column_count(GUI::ModelIndex const&) const -{ - return Column::__Count; -} - -ErrorOr IndividualSampleModel::column_name(int column) const -{ - switch (column) { - case Column::Address: - return "Address"_string; - case Column::ObjectName: - return "Object"_string; - case Column::Symbol: - return "Symbol"_string; - default: - VERIFY_NOT_REACHED(); - } -} - -GUI::Variant IndividualSampleModel::data(GUI::ModelIndex const& index, GUI::ModelRole role) const -{ - auto const& event = m_profile.events().at(m_event_index); - auto const& frame = event.frames[event.frames.size() - index.row() - 1]; - - if (role == GUI::ModelRole::Display) { - if (index.column() == Column::Address) - return ByteString::formatted("{:p}", frame.address); - - if (index.column() == Column::Symbol) { - return frame.symbol; - } - - if (index.column() == Column::ObjectName) { - return frame.object_name; - } - return {}; - } - return {}; -} - -} diff --git a/Userland/DevTools/Profiler/IndividualSampleModel.h b/Userland/DevTools/Profiler/IndividualSampleModel.h deleted file mode 100644 index 3b1102b06e8..00000000000 --- a/Userland/DevTools/Profiler/IndividualSampleModel.h +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright (c) 2018-2021, Andreas Kling - * Copyright (c) 2022, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include - -namespace Profiler { - -class Profile; - -class IndividualSampleModel final : public GUI::Model { -public: - static NonnullRefPtr create(Profile& profile, size_t event_index) - { - return adopt_ref(*new IndividualSampleModel(profile, event_index)); - } - - enum Column { - Address, - ObjectName, - Symbol, - __Count - }; - - virtual ~IndividualSampleModel() override = default; - - virtual int row_count(GUI::ModelIndex const& = GUI::ModelIndex()) const override; - virtual int column_count(GUI::ModelIndex const& = GUI::ModelIndex()) const override; - virtual ErrorOr column_name(int) const override; - virtual GUI::Variant data(GUI::ModelIndex const&, GUI::ModelRole) const override; - -private: - IndividualSampleModel(Profile&, size_t event_index); - - Profile& m_profile; - size_t const m_event_index { 0 }; -}; - -} diff --git a/Userland/DevTools/Profiler/PercentageFormatting.h b/Userland/DevTools/Profiler/PercentageFormatting.h deleted file mode 100644 index b3d2fe91ad5..00000000000 --- a/Userland/DevTools/Profiler/PercentageFormatting.h +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright (c) 2023, Jelle Raaijmakers - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include - -namespace Profiler { - -// Number of digits after the decimal point for sample percentages. -static constexpr int const number_of_percent_digits = 2; -static constexpr int const percent_digits_rounding = AK::pow(10, number_of_percent_digits); - -ByteString format_percentage(auto value, auto total) -{ - auto percentage_full_precision = round_to(value * 100.f / total * percent_digits_rounding); - return ByteString::formatted( - "{}.{:02}", - percentage_full_precision / percent_digits_rounding, - percentage_full_precision % percent_digits_rounding); -} - -} diff --git a/Userland/DevTools/Profiler/Process.cpp b/Userland/DevTools/Profiler/Process.cpp deleted file mode 100644 index dd4fcdbeba5..00000000000 --- a/Userland/DevTools/Profiler/Process.cpp +++ /dev/null @@ -1,137 +0,0 @@ -/* - * Copyright (c) 2018-2021, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "Process.h" -#include - -namespace Profiler { - -Thread* Process::find_thread(pid_t tid, EventSerialNumber serial) -{ - auto it = threads.find(tid); - if (it == threads.end()) - return nullptr; - for (auto& thread : it->value) { - if (thread.start_valid < serial && (thread.end_valid == EventSerialNumber {} || thread.end_valid > serial)) - return &thread; - } - return nullptr; -} - -void Process::handle_thread_create(pid_t tid, EventSerialNumber serial) -{ - auto it = threads.find(tid); - if (it == threads.end()) { - threads.set(tid, {}); - it = threads.find(tid); - } - - auto thread = Thread { tid, serial, {} }; - it->value.append(move(thread)); -} - -void Process::handle_thread_exit(pid_t tid, EventSerialNumber serial) -{ - auto* thread = find_thread(tid, serial); - if (!thread) - return; - thread->end_valid = serial; -} - -HashMap> g_mapped_object_cache; - -static MappedObject* get_or_create_mapped_object(ByteString const& path) -{ - if (auto it = g_mapped_object_cache.find(path); it != g_mapped_object_cache.end()) - return it->value.ptr(); - - auto file_or_error = Core::MappedFile::map(path); - if (file_or_error.is_error()) { - g_mapped_object_cache.set(path, {}); - return nullptr; - } - auto elf = ELF::Image(file_or_error.value()->bytes()); - if (!elf.is_valid()) { - g_mapped_object_cache.set(path, {}); - return nullptr; - } - auto new_mapped_object = adopt_own(*new MappedObject { - .file = file_or_error.release_value(), - .elf = elf, - }); - auto* ptr = new_mapped_object.ptr(); - g_mapped_object_cache.set(path, move(new_mapped_object)); - return ptr; -} - -void LibraryMetadata::handle_mmap(FlatPtr base, size_t size, ByteString const& name) -{ - StringView path; - if (name.contains("Loader.so"sv)) - path = "Loader.so"sv; - else if (!name.contains(':')) - return; - else - path = name.substring_view(0, name.view().find(':').value()); - - // Each loaded object has at least 4 segments associated with it: .rodata, .text, .relro, .data. - // We only want to create a single LibraryMetadata object for each library, so we need to update the - // associated base address and size as new regions are discovered. - - // We don't allocate a temporary String object if an entry already exists. - // This assumes that ByteString::hash and StringView::hash return the same result. - auto string_view_compare = [&path](auto& entry) { return path == entry.key.view(); }; - if (auto existing_it = m_libraries.find(path.hash(), string_view_compare); existing_it != m_libraries.end()) { - auto& entry = *existing_it->value; - entry.base = min(entry.base, base); - entry.size = max(entry.size + size, base - entry.base + size); - } else { - ByteString path_string = path.to_byte_string(); - ByteString full_path; - if (path_string.starts_with('/')) - full_path = path_string; - else if (FileSystem::looks_like_shared_library(path_string)) - full_path = ByteString::formatted("/usr/lib/{}", path); - else - full_path = path_string; - - auto* mapped_object = get_or_create_mapped_object(full_path); - if (!mapped_object) { - full_path = ByteString::formatted("/usr/local/lib/{}", path); - mapped_object = get_or_create_mapped_object(full_path); - if (!mapped_object) - return; - } - m_libraries.set(path_string, adopt_own(*new Library { base, size, path_string, mapped_object, {} })); - } -} - -Debug::DebugInfo const& LibraryMetadata::Library::load_debug_info(FlatPtr base_address) const -{ - if (debug_info == nullptr) - debug_info = make(object->elf, ByteString::empty(), base_address); - return *debug_info.ptr(); -} - -ByteString LibraryMetadata::Library::symbolicate(FlatPtr ptr, u32* offset) const -{ - if (!object) - return ByteString::formatted("?? <{:p}>", ptr); - - return object->elf.symbolicate(ptr - base, offset); -} - -LibraryMetadata::Library const* LibraryMetadata::library_containing(FlatPtr ptr) const -{ - for (auto& it : m_libraries) { - auto& library = *it.value; - if (ptr >= library.base && ptr < (library.base + library.size)) - return &library; - } - return nullptr; -} - -} diff --git a/Userland/DevTools/Profiler/Process.h b/Userland/DevTools/Profiler/Process.h deleted file mode 100644 index e1ee2080908..00000000000 --- a/Userland/DevTools/Profiler/Process.h +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright (c) 2018-2021, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include "EventSerialNumber.h" -#include -#include -#include -#include -#include -#include - -namespace Profiler { - -struct MappedObject { - NonnullOwnPtr file; - ELF::Image elf; -}; - -extern HashMap> g_mapped_object_cache; - -class LibraryMetadata { -public: - struct Library { - FlatPtr base; - size_t size; - ByteString name; - MappedObject* object { nullptr }; - // This is loaded lazily because we only need it in disassembly view - mutable OwnPtr debug_info; - - ByteString symbolicate(FlatPtr, u32* offset) const; - Debug::DebugInfo const& load_debug_info(FlatPtr base_address) const; - }; - - void handle_mmap(FlatPtr base, size_t size, ByteString const& name); - Library const* library_containing(FlatPtr) const; - -private: - mutable HashMap> m_libraries; -}; - -struct Thread { - pid_t tid; - EventSerialNumber start_valid; - EventSerialNumber end_valid; - - bool valid_at(EventSerialNumber serial) const - { - return serial >= start_valid && (end_valid == EventSerialNumber {} || serial <= end_valid); - } -}; - -struct Process { - pid_t pid {}; - ByteString executable; - ByteString basename; - HashMap> threads {}; - LibraryMetadata library_metadata {}; - EventSerialNumber start_valid; - EventSerialNumber end_valid; - - Thread* find_thread(pid_t tid, EventSerialNumber serial); - void handle_thread_create(pid_t tid, EventSerialNumber serial); - void handle_thread_exit(pid_t tid, EventSerialNumber serial); - - bool valid_at(EventSerialNumber serial) const - { - return serial >= start_valid && (end_valid == EventSerialNumber {} || serial <= end_valid); - } -}; - -} diff --git a/Userland/DevTools/Profiler/Profile.cpp b/Userland/DevTools/Profiler/Profile.cpp deleted file mode 100644 index 74d3ad7afa4..00000000000 --- a/Userland/DevTools/Profiler/Profile.cpp +++ /dev/null @@ -1,701 +0,0 @@ -/* - * Copyright (c) 2018-2021, Andreas Kling - * Copyright (c) 2023, Jakub Berkop - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "Profile.h" -#include "DisassemblyModel.h" -#include "ProfileModel.h" -#include "SamplesModel.h" -#include "SourceModel.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace Profiler { - -static void sort_profile_nodes(Vector>& nodes) -{ - quick_sort(nodes.begin(), nodes.end(), [](auto& a, auto& b) { - return a->event_count() >= b->event_count(); - }); - - for (auto& child : nodes) - child->sort_children(); -} - -Profile::Profile(Vector processes, Vector events) - : m_processes(move(processes)) - , m_events(move(events)) - , m_file_event_nodes(FileEventNode::create("")) -{ - for (size_t i = 0; i < m_events.size(); ++i) { - if (m_events[i].data.has()) - m_signpost_indices.append(i); - } - - m_first_timestamp = m_events.first().timestamp; - m_last_timestamp = m_events.last().timestamp; - - m_model = ProfileModel::create(*this); - m_samples_model = SamplesModel::create(*this); - m_signposts_model = SignpostsModel::create(*this); - m_file_event_model = FileEventModel::create(*this); - - rebuild_tree(); -} - -GUI::Model& Profile::model() -{ - return *m_model; -} - -GUI::Model& Profile::samples_model() -{ - return *m_samples_model; -} - -GUI::Model& Profile::signposts_model() -{ - return *m_signposts_model; -} - -void Profile::rebuild_tree() -{ - Vector> roots; - - auto find_or_create_process_node = [this, &roots](pid_t pid, EventSerialNumber serial) -> ProfileNode& { - auto const* process = find_process(pid, serial); - if (!process) { - dbgln("Profile contains event for unknown process with pid={}, serial={}", pid, serial.to_number()); - VERIFY_NOT_REACHED(); - } - for (auto root : roots) { - if (&root->process() == process) - return root; - } - auto new_root = ProfileNode::create_process_node(*process); - roots.append(new_root); - return new_root; - }; - - HashTable live_allocations; - - for_each_event_in_filter_range([&](Event const& event) { - event.data.visit( - [&](Event::MallocData const& data) { - live_allocations.set(data.ptr); - }, - [&](Event::FreeData const& data) { - live_allocations.remove(data.ptr); - }, - [&](auto&) {}); - }); - - m_filtered_event_indices.clear(); - m_filtered_signpost_indices.clear(); - m_file_event_nodes->children().clear(); - - for (size_t event_index = 0; event_index < m_events.size(); ++event_index) { - auto& event = m_events.at(event_index); - - if (has_timestamp_filter_range()) { - auto timestamp = event.timestamp; - if (timestamp < m_timestamp_filter_range_start || timestamp > m_timestamp_filter_range_end) - continue; - } - - if (!process_filter_contains(event.pid, event.serial)) - continue; - - if (event.data.has()) { - m_filtered_signpost_indices.append(event_index); - continue; - } - - m_filtered_event_indices.append(event_index); - - if (auto* malloc_data = event.data.get_pointer(); malloc_data && !live_allocations.contains(malloc_data->ptr)) - continue; - - if (event.data.has()) - continue; - - auto for_each_frame = [&](Callback callback) { - if (!m_inverted) { - for (size_t i = 0; i < event.frames.size(); ++i) { - if (callback(event.frames.at(i), i == event.frames.size() - 1) == IterationDecision::Break) - break; - } - } else { - for (ssize_t i = event.frames.size() - 1; i >= 0; --i) { - if (callback(event.frames.at(i), static_cast(i) == event.frames.size() - 1) == IterationDecision::Break) - break; - } - } - }; - - if (!m_show_top_functions) { - ProfileNode* node = nullptr; - auto& process_node = find_or_create_process_node(event.pid, event.serial); - process_node.increment_event_count(); - for_each_frame([&](Frame const& frame, bool is_innermost_frame) { - auto const& object_name = frame.object_name; - auto const& symbol = frame.symbol; - auto const& address = frame.address; - auto const& offset = frame.offset; - - if (symbol.is_empty()) - return IterationDecision::Break; - - // FIXME: More cheating with intentional mixing of TID/PID here: - if (!node) - node = &process_node; - node = &node->find_or_create_child(object_name, symbol, address, offset, event.timestamp, event.pid); - - node->increment_event_count(); - if (is_innermost_frame) { - node->add_event_address(address); - node->increment_self_count(); - } - return IterationDecision::Continue; - }); - } else { - auto& process_node = find_or_create_process_node(event.pid, event.serial); - process_node.increment_event_count(); - for (size_t i = 0; i < event.frames.size(); ++i) { - ProfileNode* node = nullptr; - ProfileNode* root = nullptr; - for (size_t j = i; j < event.frames.size(); ++j) { - auto& frame = event.frames.at(j); - auto& object_name = frame.object_name; - auto& symbol = frame.symbol; - auto& address = frame.address; - auto& offset = frame.offset; - if (symbol.is_empty()) - break; - - // FIXME: More PID/TID mixing cheats here: - if (!node) { - node = &find_or_create_process_node(event.pid, event.serial); - node = &node->find_or_create_child(object_name, symbol, address, offset, event.timestamp, event.pid); - root = node; - root->will_track_seen_events(m_events.size()); - } else { - node = &node->find_or_create_child(object_name, symbol, address, offset, event.timestamp, event.pid); - } - - if (!root->has_seen_event(event_index)) { - root->did_see_event(event_index); - root->increment_event_count(); - } else if (node != root) { - node->increment_event_count(); - } - - if (j == event.frames.size() - 1) { - node->add_event_address(address); - node->increment_self_count(); - } - } - } - } - if (event.data.has()) { - auto const& filesystem_event = event.data.get(); - auto const& path = filesystem_event.data.visit( - [&](Event::OpenEventData const& data) { - return data.path; - }, - [&](Event::CloseEventData const& data) { - return data.path; - }, - [&](Event::ReadvEventData const& data) { - return data.path; - }, - [&](Event::ReadEventData const& data) { - return data.path; - }, - [&](Event::PreadEventData const& data) { - return data.path; - }); - - auto& event_node = m_file_event_nodes->find_or_create_node(path); - - event_node.for_each_parent_node([&](FileEventNode& node) { - auto const duration = filesystem_event.duration; - - filesystem_event.data.visit( - [&](Event::OpenEventData const&) { - node.open().duration += duration; - node.open().count++; - }, - [&](Event::CloseEventData const&) { - node.close().duration += duration; - node.close().count++; - }, - [&](Event::ReadvEventData const&) { - node.readv().duration += duration; - node.readv().count++; - }, - [&](Event::ReadEventData const&) { - node.read().duration += duration; - node.read().count++; - }, - [&](Event::PreadEventData const&) { - node.pread().duration += duration; - node.pread().count++; - }); - }); - } - } - - sort_profile_nodes(roots); - - m_roots = move(roots); - m_model->invalidate(); -} - -Optional g_kernel_debuginfo_object; -OwnPtr g_kernel_debug_info; - -ErrorOr> Profile::load_from_perfcore_file(StringView path) -{ - auto file = TRY(Core::File::open(path, Core::File::OpenMode::Read)); - - auto json = JsonValue::from_string(TRY(file->read_until_eof())); - if (json.is_error() || !json.value().is_object()) - return Error::from_string_literal("Invalid perfcore format (not a JSON object)"); - - auto const& object = json.value().as_object(); - - if (!g_kernel_debuginfo_object.has_value()) { - auto debuginfo_file_or_error = Core::MappedFile::map("/boot/Kernel.debug"sv); - if (!debuginfo_file_or_error.is_error()) { - auto debuginfo_file = debuginfo_file_or_error.release_value(); - auto debuginfo_image = ELF::Image(debuginfo_file->bytes()); - g_kernel_debuginfo_object = { { move(debuginfo_file), move(debuginfo_image) } }; - } - } - - auto strings_value = object.get_array("strings"sv); - if (!strings_value.has_value()) - return Error::from_string_literal("Malformed profile (strings is not an array)"); - auto const& strings = strings_value.value(); - - HashMap profile_strings; - for (FlatPtr string_id = 0; string_id < strings.size(); ++string_id) { - auto const& value = strings.at(string_id); - profile_strings.set(string_id, value.as_string()); - } - - auto events_value = object.get_array("events"sv); - if (!events_value.has_value()) - return Error::from_string_literal("Malformed profile (events is not an array)"); - auto const& perf_events = events_value.value(); - - Vector> all_processes; - HashMap current_processes; - Vector events; - EventSerialNumber next_serial; - - for (auto const& perf_event_value : perf_events.values()) { - auto const& perf_event = perf_event_value.as_object(); - - Event event; - - event.serial = next_serial; - next_serial.increment(); - event.timestamp = perf_event.get_u64("timestamp"sv).value_or(0); - event.lost_samples = perf_event.get_u32("lost_samples"sv).value_or(0); - event.pid = perf_event.get_i32("pid"sv).value_or(0); - event.tid = perf_event.get_i32("tid"sv).value_or(0); - - auto type_string = perf_event.get_byte_string("type"sv).value_or({}); - - if (type_string == "sample"sv) { - event.data = Event::SampleData {}; - } else if (type_string == "kmalloc"sv) { - event.data = Event::MallocData { - .ptr = perf_event.get_addr("ptr"sv).value_or(0), - .size = perf_event.get_integer("size"sv).value_or(0), - }; - } else if (type_string == "kfree"sv) { - event.data = Event::FreeData { - .ptr = perf_event.get_addr("ptr"sv).value_or(0), - }; - } else if (type_string == "signpost"sv) { - auto string_id = perf_event.get_addr("arg1"sv).value_or(0); - event.data = Event::SignpostData { - .string = profile_strings.get(string_id).value_or(ByteString::formatted("Signpost #{}", string_id)), - .arg = perf_event.get_addr("arg2"sv).value_or(0), - }; - } else if (type_string == "mmap"sv) { - auto ptr = perf_event.get_addr("ptr"sv).value_or(0); - auto size = perf_event.get_integer("size"sv).value_or(0); - auto name = perf_event.get_byte_string("name"sv).value_or({}); - - event.data = Event::MmapData { - .ptr = ptr, - .size = size, - .name = name, - }; - - auto it = current_processes.find(event.pid); - if (it != current_processes.end()) - it->value->library_metadata.handle_mmap(ptr, size, name); - continue; - } else if (type_string == "munmap"sv) { - event.data = Event::MunmapData { - .ptr = perf_event.get_addr("ptr"sv).value_or(0), - .size = perf_event.get_integer("size"sv).value_or(0), - }; - continue; - } else if (type_string == "process_create"sv) { - auto parent_pid = perf_event.get_integer("parent_pid"sv).value_or(0); - auto executable = perf_event.get_byte_string("executable"sv).value_or({}); - event.data = Event::ProcessCreateData { - .parent_pid = parent_pid, - .executable = executable, - }; - - auto sampled_process = TRY(adopt_nonnull_own_or_enomem(new (nothrow) Process { - .pid = event.pid, - .executable = executable, - .basename = LexicalPath::basename(executable), - .start_valid = event.serial, - .end_valid = {}, - })); - - current_processes.set(sampled_process->pid, sampled_process); - all_processes.append(move(sampled_process)); - continue; - } else if (type_string == "process_exec"sv) { - auto executable = perf_event.get_byte_string("executable"sv).value_or({}); - event.data = Event::ProcessExecData { - .executable = executable, - }; - - auto* old_process = current_processes.get(event.pid).value(); - old_process->end_valid = event.serial; - - current_processes.remove(event.pid); - - auto sampled_process = TRY(adopt_nonnull_own_or_enomem(new (nothrow) Process { - .pid = event.pid, - .executable = executable, - .basename = LexicalPath::basename(executable), - .start_valid = event.serial, - .end_valid = {}, - })); - - current_processes.set(sampled_process->pid, sampled_process); - all_processes.append(move(sampled_process)); - continue; - } else if (type_string == "process_exit"sv) { - auto* old_process = current_processes.get(event.pid).value(); - old_process->end_valid = event.serial; - - current_processes.remove(event.pid); - continue; - } else if (type_string == "thread_create"sv) { - auto parent_tid = perf_event.get_integer("parent_tid"sv).value_or(0); - event.data = Event::ThreadCreateData { - .parent_tid = parent_tid, - }; - auto it = current_processes.find(event.pid); - if (it != current_processes.end()) - it->value->handle_thread_create(event.tid, event.serial); - continue; - } else if (type_string == "thread_exit"sv) { - auto it = current_processes.find(event.pid); - if (it != current_processes.end()) - it->value->handle_thread_exit(event.tid, event.serial); - continue; - } else if (type_string == "filesystem"sv) { - Event::FilesystemEventData fsdata { - .duration = Duration::from_nanoseconds(perf_event.get_integer("durationNs"sv).value_or(0)), - .data = Event::OpenEventData {}, - }; - auto const filesystem_event_type = perf_event.get("fs_event_type"sv).value_or("").as_string(); - if (filesystem_event_type == "open"sv) { - auto const string_index = perf_event.get_addr("filename_index"sv).value_or(0); - auto const filename = profile_strings.get(string_index).value_or(""); - fsdata.data = Event::OpenEventData { - .dirfd = perf_event.get_integer("dirfd"sv).value_or(0), - .path = filename, - .options = perf_event.get_integer("options"sv).value_or(0), - .mode = perf_event.get_integer("mode"sv).value_or(0), - }; - } else if (filesystem_event_type == "close"sv) { - auto const string_index = perf_event.get_addr("filename_index"sv).value_or(0); - auto const filename = profile_strings.get(string_index).value_or(""); - fsdata.data = Event::CloseEventData { - .fd = perf_event.get_integer("fd"sv).value_or(0), - .path = filename, - }; - } else if (filesystem_event_type == "readv"sv) { - auto const string_index = perf_event.get_addr("filename_index"sv).value_or(0); - auto const filename = profile_strings.get(string_index).value(); - fsdata.data = Event::ReadvEventData { - .fd = perf_event.get_integer("fd"sv).value_or(0), - .path = filename, - }; - } else if (filesystem_event_type == "read"sv) { - auto const string_index = perf_event.get_addr("filename_index"sv).value_or(0); - auto const filename = profile_strings.get(string_index).value(); - fsdata.data = Event::ReadEventData { - .fd = perf_event.get_integer("fd"sv).value_or(0), - .path = filename, - }; - } else if (filesystem_event_type == "pread"sv) { - auto const string_index = perf_event.get_addr("filename_index"sv).value_or(0); - auto const filename = profile_strings.get(string_index).value(); - fsdata.data = Event::PreadEventData { - .fd = perf_event.get_integer("fd"sv).value_or(0), - .path = filename, - .buffer_ptr = perf_event.get_integer("buffer_ptr"sv).value_or(0), - .size = perf_event.get_integer("size"sv).value_or(0), - .offset = perf_event.get_integer("offset"sv).value_or(0), - }; - } - - event.data = fsdata; - } else { - dbgln("Unknown event type '{}'", type_string); - VERIFY_NOT_REACHED(); - } - - auto maybe_kernel_base = Symbolication::kernel_base(); - - auto stack = perf_event.get_array("stack"sv); - VERIFY(stack.has_value()); - auto const& stack_array = stack.value(); - for (ssize_t i = stack_array.values().size() - 1; i >= 0; --i) { - auto const& frame = stack_array.at(i); - auto ptr = frame.as_integer(); - u32 offset = 0; - DeprecatedFlyString object_name; - ByteString symbol; - - if (maybe_kernel_base.has_value() && ptr >= maybe_kernel_base.value()) { - if (g_kernel_debuginfo_object.has_value()) { - symbol = g_kernel_debuginfo_object->elf.symbolicate(ptr - maybe_kernel_base.value(), &offset); - } else { - symbol = ByteString::formatted("?? <{:p}>", ptr); - } - } else { - auto it = current_processes.find(event.pid); - // FIXME: This logic is kinda gnarly, find a way to clean it up. - LibraryMetadata* library_metadata {}; - if (it != current_processes.end()) - library_metadata = &it->value->library_metadata; - if (auto const* library = library_metadata ? library_metadata->library_containing(ptr) : nullptr) { - object_name = library->name; - symbol = library->symbolicate(ptr, &offset); - } else { - symbol = ByteString::formatted("?? <{:p}>", ptr); - } - } - - event.frames.append({ object_name, symbol, (FlatPtr)ptr, offset }); - } - - if (event.frames.size() < 2) - continue; - - FlatPtr innermost_frame_address = event.frames.at(1).address; - event.in_kernel = maybe_kernel_base.has_value() && innermost_frame_address >= maybe_kernel_base.value(); - - events.append(move(event)); - } - - if (events.is_empty()) - return Error::from_string_literal("No events captured (targeted process was never on CPU)"); - - quick_sort(all_processes, [](auto& a, auto& b) { - if (a->pid == b->pid) - return a->start_valid < b->start_valid; - - return a->pid < b->pid; - }); - - Vector processes; - for (auto& it : all_processes) - processes.append(move(*it)); - - return adopt_nonnull_own_or_enomem(new (nothrow) Profile(move(processes), move(events))); -} - -void ProfileNode::sort_children() -{ - sort_profile_nodes(m_children); -} - -void Profile::set_timestamp_filter_range(u64 start, u64 end) -{ - if (m_has_timestamp_filter_range && m_timestamp_filter_range_start == start && m_timestamp_filter_range_end == end) - return; - m_has_timestamp_filter_range = true; - - m_timestamp_filter_range_start = min(start, end); - m_timestamp_filter_range_end = max(start, end); - - rebuild_tree(); - m_samples_model->invalidate(); - m_signposts_model->invalidate(); -} - -void Profile::clear_timestamp_filter_range() -{ - if (!m_has_timestamp_filter_range) - return; - m_has_timestamp_filter_range = false; - rebuild_tree(); - m_samples_model->invalidate(); - m_signposts_model->invalidate(); -} - -void Profile::add_process_filter(pid_t pid, EventSerialNumber start_valid, EventSerialNumber end_valid) -{ - auto filter = ProcessFilter { pid, start_valid, end_valid }; - if (m_process_filters.contains_slow(filter)) - return; - m_process_filters.append(move(filter)); - - rebuild_tree(); - if (m_disassembly_model) - m_disassembly_model->invalidate(); - m_samples_model->invalidate(); - m_signposts_model->invalidate(); -} - -void Profile::remove_process_filter(pid_t pid, EventSerialNumber start_valid, EventSerialNumber end_valid) -{ - auto filter = ProcessFilter { pid, start_valid, end_valid }; - if (!m_process_filters.contains_slow(filter)) - return; - m_process_filters.remove_first_matching([&filter](ProcessFilter const& other_filter) { - return other_filter == filter; - }); - - rebuild_tree(); - if (m_disassembly_model) - m_disassembly_model->invalidate(); - m_samples_model->invalidate(); - m_signposts_model->invalidate(); -} - -void Profile::clear_process_filter() -{ - if (m_process_filters.is_empty()) - return; - m_process_filters.clear(); - rebuild_tree(); - if (m_disassembly_model) - m_disassembly_model->invalidate(); - m_samples_model->invalidate(); - m_signposts_model->invalidate(); -} - -bool Profile::process_filter_contains(pid_t pid, EventSerialNumber serial) -{ - if (!has_process_filter()) - return true; - - return AK::any_of(m_process_filters, - [&](auto const& process_filter) { return pid == process_filter.pid && serial >= process_filter.start_valid && serial <= process_filter.end_valid; }); -} - -void Profile::set_inverted(bool inverted) -{ - if (m_inverted == inverted) - return; - m_inverted = inverted; - rebuild_tree(); -} - -void Profile::set_show_top_functions(bool show) -{ - if (m_show_top_functions == show) - return; - m_show_top_functions = show; - rebuild_tree(); -} - -void Profile::set_show_percentages(bool show_percentages) -{ - if (m_show_percentages == show_percentages) - return; - m_show_percentages = show_percentages; -} - -void Profile::set_disassembly_index(GUI::ModelIndex const& index) -{ - if (m_disassembly_index == index) - return; - m_disassembly_index = index; - auto* node = static_cast(index.internal_data()); - if (!node) - m_disassembly_model = nullptr; - else - m_disassembly_model = DisassemblyModel::create(*this, *node); -} - -GUI::Model* Profile::disassembly_model() -{ - return m_disassembly_model; -} - -void Profile::set_source_index(GUI::ModelIndex const& index) -{ - if (m_source_index == index) - return; - m_source_index = index; - auto* node = static_cast(index.internal_data()); - if (!node) - m_source_model = nullptr; - else - m_source_model = SourceModel::create(*this, *node); -} - -GUI::Model* Profile::source_model() -{ - return m_source_model; -} - -GUI::Model* Profile::file_event_model() -{ - return m_file_event_model; -} - -ProfileNode::ProfileNode(Process const& process) - : m_root(true) - , m_process(process) -{ -} - -ProfileNode::ProfileNode(Process const& process, DeprecatedFlyString const& object_name, ByteString symbol, FlatPtr address, u32 offset, u64 timestamp, pid_t pid) - : m_process(process) - , m_symbol(move(symbol)) - , m_pid(pid) - , m_address(address) - , m_offset(offset) - , m_timestamp(timestamp) -{ - ByteString object; - if (object_name.ends_with(": .text"sv)) { - object = object_name.view().substring_view(0, object_name.length() - 7); - } else { - object = object_name; - } - m_object_name = LexicalPath::basename(object); -} - -} diff --git a/Userland/DevTools/Profiler/Profile.h b/Userland/DevTools/Profiler/Profile.h deleted file mode 100644 index 00d7c431440..00000000000 --- a/Userland/DevTools/Profiler/Profile.h +++ /dev/null @@ -1,358 +0,0 @@ -/* - * Copyright (c) 2018-2021, Andreas Kling - * Copyright (c) 2023, Jakub Berkop - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include "DisassemblyModel.h" -#include "FilesystemEventModel.h" -#include "Process.h" -#include "Profile.h" -#include "ProfileModel.h" -#include "SamplesModel.h" -#include "SignpostsModel.h" -#include "SourceModel.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace Profiler { - -extern Optional g_kernel_debuginfo_object; -extern OwnPtr g_kernel_debug_info; - -class ProfileNode : public RefCounted { -public: - static NonnullRefPtr create(Process const& process, DeprecatedFlyString const& object_name, ByteString symbol, FlatPtr address, u32 offset, u64 timestamp, pid_t pid) - { - return adopt_ref(*new ProfileNode(process, object_name, move(symbol), address, offset, timestamp, pid)); - } - - static NonnullRefPtr create_process_node(Process const& process) - { - return adopt_ref(*new ProfileNode(process)); - } - - // These functions are only relevant for root nodes - void will_track_seen_events(size_t profile_event_count) - { - if (m_seen_events.size() != profile_event_count) - m_seen_events = Bitmap::create(profile_event_count, false).release_value_but_fixme_should_propagate_errors(); - } - bool has_seen_event(size_t event_index) const { return m_seen_events.get(event_index); } - void did_see_event(size_t event_index) { m_seen_events.set(event_index, true); } - - DeprecatedFlyString const& object_name() const { return m_object_name; } - ByteString const& symbol() const { return m_symbol; } - FlatPtr address() const { return m_address; } - u32 offset() const { return m_offset; } - u64 timestamp() const { return m_timestamp; } - - u32 event_count() const { return m_event_count; } - u32 self_count() const { return m_self_count; } - - int child_count() const { return m_children.size(); } - Vector> const& children() const { return m_children; } - - void add_child(ProfileNode& child) - { - if (child.m_parent == this) - return; - VERIFY(!child.m_parent); - child.m_parent = this; - m_children.append(child); - } - - ProfileNode& find_or_create_child(DeprecatedFlyString const& object_name, ByteString symbol, FlatPtr address, u32 offset, u64 timestamp, pid_t pid) - { - for (size_t i = 0; i < m_children.size(); ++i) { - auto& child = m_children[i]; - if (child->symbol() == symbol) { - return child; - } - } - auto new_child = ProfileNode::create(m_process, object_name, move(symbol), address, offset, timestamp, pid); - add_child(new_child); - return new_child; - } - - ProfileNode* parent() { return m_parent; } - ProfileNode const* parent() const { return m_parent; } - - void increment_event_count() { ++m_event_count; } - void increment_self_count() { ++m_self_count; } - - void sort_children(); - - HashMap const& events_per_address() const { return m_events_per_address; } - void add_event_address(FlatPtr address) - { - auto it = m_events_per_address.find(address); - if (it == m_events_per_address.end()) - m_events_per_address.set(address, 1); - else - m_events_per_address.set(address, it->value + 1); - } - - pid_t pid() const { return m_pid; } - - Process const& process() const { return m_process; } - bool is_root() const { return m_root; } - -private: - explicit ProfileNode(Process const&); - explicit ProfileNode(Process const&, DeprecatedFlyString const& object_name, ByteString symbol, FlatPtr address, u32 offset, u64 timestamp, pid_t); - - bool m_root { false }; - Process const& m_process; - ProfileNode* m_parent { nullptr }; - DeprecatedFlyString m_object_name; - ByteString m_symbol; - pid_t m_pid { 0 }; - FlatPtr m_address { 0 }; - u32 m_offset { 0 }; - u32 m_event_count { 0 }; - u32 m_self_count { 0 }; - u64 m_timestamp { 0 }; - Vector> m_children; - HashMap m_events_per_address; - Bitmap m_seen_events; -}; - -struct ProcessFilter { - pid_t pid { 0 }; - EventSerialNumber start_valid; - EventSerialNumber end_valid; - - bool operator==(ProcessFilter const& rhs) const - { - return pid == rhs.pid && start_valid == rhs.start_valid && end_valid == rhs.end_valid; - } -}; - -class Profile { -public: - static ErrorOr> load_from_perfcore_file(StringView path); - - GUI::Model& model(); - GUI::Model& samples_model(); - GUI::Model& signposts_model(); - GUI::Model* disassembly_model(); - GUI::Model* source_model(); - GUI::Model* file_event_model(); - - Process const* find_process(pid_t pid, EventSerialNumber serial) const - { - auto it = m_processes.find_if([&pid, &serial](auto& entry) { - return entry.pid == pid && entry.valid_at(serial); - }); - return it.is_end() ? nullptr : &(*it); - } - - void set_disassembly_index(GUI::ModelIndex const&); - void set_source_index(GUI::ModelIndex const&); - - Vector> const& roots() const { return m_roots; } - - struct Frame { - DeprecatedFlyString object_name; - ByteString symbol; - FlatPtr address { 0 }; - u32 offset { 0 }; - }; - - struct Event { - u64 timestamp { 0 }; - EventSerialNumber serial; - pid_t pid { 0 }; - pid_t tid { 0 }; - u32 lost_samples { 0 }; - bool in_kernel { false }; - - Vector frames; - - struct SampleData { - }; - - struct MallocData { - FlatPtr ptr {}; - size_t size {}; - }; - - struct FreeData { - FlatPtr ptr {}; - }; - - struct SignpostData { - ByteString string; - FlatPtr arg {}; - }; - - struct MmapData { - FlatPtr ptr {}; - size_t size {}; - ByteString name; - }; - - struct MunmapData { - FlatPtr ptr {}; - size_t size {}; - }; - - struct ProcessCreateData { - pid_t parent_pid { 0 }; - ByteString executable; - }; - - struct ProcessExecData { - ByteString executable; - }; - - struct ThreadCreateData { - pid_t parent_tid {}; - }; - - // Based on Syscall::SC_open_params - struct OpenEventData { - int dirfd; - ByteString path; - int options; - u64 mode; - }; - - struct CloseEventData { - int fd; - ByteString path; - }; - - struct ReadvEventData { - int fd; - ByteString path; - // struct iovec* iov; // TODO: Implement - // int iov_count; // TODO: Implement - }; - - struct ReadEventData { - int fd; - ByteString path; - }; - - struct PreadEventData { - int fd; - ByteString path; - FlatPtr buffer_ptr; - size_t size; - off_t offset; - }; - - struct FilesystemEventData { - Duration duration; - Variant data; - }; - - Variant data { nullptr }; - }; - - Vector const& events() const { return m_events; } - Vector const& filtered_event_indices() const { return m_filtered_event_indices; } - Vector const& filtered_signpost_indices() const { return m_filtered_signpost_indices; } - NonnullRefPtr const& file_event_nodes() { return m_file_event_nodes; } - - u64 length_in_ms() const { return m_last_timestamp - m_first_timestamp; } - u64 first_timestamp() const { return m_first_timestamp; } - u64 last_timestamp() const { return m_last_timestamp; } - - void set_timestamp_filter_range(u64 start, u64 end); - void clear_timestamp_filter_range(); - bool has_timestamp_filter_range() const { return m_has_timestamp_filter_range; } - - void add_process_filter(pid_t pid, EventSerialNumber start_valid, EventSerialNumber end_valid); - void remove_process_filter(pid_t pid, EventSerialNumber start_valid, EventSerialNumber end_valid); - void clear_process_filter(); - bool has_process_filter() const { return !m_process_filters.is_empty(); } - bool process_filter_contains(pid_t pid, EventSerialNumber serial); - - bool is_inverted() const { return m_inverted; } - void set_inverted(bool); - - void set_show_top_functions(bool); - - bool show_percentages() const { return m_show_percentages; } - void set_show_percentages(bool); - - Vector const& processes() const { return m_processes; } - - template - void for_each_event_in_filter_range(Callback callback) - { - for (auto& event : m_events) { - if (has_timestamp_filter_range()) { - auto timestamp = event.timestamp; - if (timestamp < m_timestamp_filter_range_start || timestamp > m_timestamp_filter_range_end) - continue; - } - callback(event); - } - } - - template - void for_each_signpost(Callback callback) const - { - for (auto index : m_signpost_indices) { - auto const& event = m_events[index]; - if (callback(event) == IterationDecision::Break) - break; - } - } - -private: - Profile(Vector, Vector); - - void rebuild_tree(); - - RefPtr m_model; - RefPtr m_samples_model; - RefPtr m_signposts_model; - RefPtr m_disassembly_model; - RefPtr m_source_model; - RefPtr m_file_event_model; - - GUI::ModelIndex m_disassembly_index; - GUI::ModelIndex m_source_index; - - Vector> m_roots; - Vector m_filtered_event_indices; - u64 m_first_timestamp { 0 }; - u64 m_last_timestamp { 0 }; - - Vector m_processes; - Vector m_events; - Vector m_signpost_indices; - Vector m_filtered_signpost_indices; - - bool m_has_timestamp_filter_range { false }; - u64 m_timestamp_filter_range_start { 0 }; - u64 m_timestamp_filter_range_end { 0 }; - - Vector m_process_filters; - - NonnullRefPtr m_file_event_nodes; - - bool m_inverted { false }; - bool m_show_top_functions { false }; - bool m_show_percentages { false }; -}; - -} diff --git a/Userland/DevTools/Profiler/ProfileModel.cpp b/Userland/DevTools/Profiler/ProfileModel.cpp deleted file mode 100644 index 464faacb00f..00000000000 --- a/Userland/DevTools/Profiler/ProfileModel.cpp +++ /dev/null @@ -1,171 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * Copyright (c) 2023, Jelle Raaijmakers - * Copyright (c) 2022, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "ProfileModel.h" -#include "PercentageFormatting.h" -#include "Profile.h" -#include -#include -#include - -namespace Profiler { - -ProfileModel::ProfileModel(Profile& profile) - : m_profile(profile) -{ - m_user_frame_icon.set_bitmap_for_size(16, Gfx::Bitmap::load_from_file("/res/icons/16x16/inspector-object.png"sv).release_value_but_fixme_should_propagate_errors()); - m_kernel_frame_icon.set_bitmap_for_size(16, Gfx::Bitmap::load_from_file("/res/icons/16x16/inspector-object-red.png"sv).release_value_but_fixme_should_propagate_errors()); -} - -GUI::ModelIndex ProfileModel::index(int row, int column, GUI::ModelIndex const& parent) const -{ - if (!parent.is_valid()) { - if (m_profile.roots().is_empty()) - return {}; - return create_index(row, column, m_profile.roots().at(row).ptr()); - } - auto& remote_parent = *static_cast(parent.internal_data()); - return create_index(row, column, remote_parent.children().at(row).ptr()); -} - -GUI::ModelIndex ProfileModel::parent_index(GUI::ModelIndex const& index) const -{ - if (!index.is_valid()) - return {}; - auto& node = *static_cast(index.internal_data()); - if (!node.parent()) - return {}; - - // NOTE: If the parent has no parent, it's a root, so we have to look among the roots. - if (!node.parent()->parent()) { - for (size_t row = 0; row < m_profile.roots().size(); ++row) { - if (m_profile.roots()[row].ptr() == node.parent()) { - return create_index(row, index.column(), node.parent()); - } - } - VERIFY_NOT_REACHED(); - return {}; - } - - for (size_t row = 0; row < node.parent()->parent()->children().size(); ++row) { - if (node.parent()->parent()->children()[row].ptr() == node.parent()) - return create_index(row, index.column(), node.parent()); - } - - VERIFY_NOT_REACHED(); - return {}; -} - -int ProfileModel::row_count(GUI::ModelIndex const& index) const -{ - if (!index.is_valid()) - return m_profile.roots().size(); - auto& node = *static_cast(index.internal_data()); - return node.children().size(); -} - -int ProfileModel::column_count(GUI::ModelIndex const&) const -{ - return Column::__Count; -} - -ErrorOr ProfileModel::column_name(int column) const -{ - switch (column) { - case Column::SampleCount: - return m_profile.show_percentages() ? "% Samples"_string : "# Samples"_string; - case Column::SelfCount: - return m_profile.show_percentages() ? "% Self"_string : "# Self"_string; - case Column::ObjectName: - return "Object"_string; - case Column::StackFrame: - return "Stack Frame"_string; - case Column::SymbolAddress: - return "Symbol Address"_string; - default: - VERIFY_NOT_REACHED(); - } -} - -GUI::Variant ProfileModel::data(GUI::ModelIndex const& index, GUI::ModelRole role) const -{ - auto* node = static_cast(index.internal_data()); - if (role == GUI::ModelRole::TextAlignment) { - if (index.column() == Column::SampleCount || index.column() == Column::SelfCount) - return Gfx::TextAlignment::CenterRight; - } - if (role == GUI::ModelRole::Icon) { - if (index.column() == Column::StackFrame) { - if (node->is_root()) { - return GUI::FileIconProvider::icon_for_executable(node->process().executable); - } - auto maybe_kernel_base = Symbolication::kernel_base(); - if (maybe_kernel_base.has_value() && node->address() >= maybe_kernel_base.value()) - return m_kernel_frame_icon; - return m_user_frame_icon; - } - return {}; - } - if (role == GUI::ModelRole::Display) { - if (index.column() == Column::SampleCount) { - if (m_profile.show_percentages()) - return format_percentage(node->event_count(), m_profile.filtered_event_indices().size()); - return node->event_count(); - } - if (index.column() == Column::SelfCount) { - if (m_profile.show_percentages()) - return format_percentage(node->self_count(), m_profile.filtered_event_indices().size()); - return node->self_count(); - } - if (index.column() == Column::ObjectName) - return node->object_name(); - if (index.column() == Column::StackFrame) { - if (node->is_root()) { - return ByteString::formatted("{} ({})", node->process().basename, node->process().pid); - } - return node->symbol(); - } - if (index.column() == Column::SymbolAddress) { - if (node->is_root()) - return ""; - auto const* library = node->process().library_metadata.library_containing(node->address()); - if (!library) - return ""; - return ByteString::formatted("{:p} (offset {:p})", node->address(), node->address() - library->base); - } - return {}; - } - return {}; -} - -Vector ProfileModel::matches(StringView searching, unsigned flags, GUI::ModelIndex const& parent) -{ - RemoveReference* nodes { nullptr }; - - if (!parent.is_valid()) - nodes = &m_profile.roots(); - else - nodes = &static_cast(parent.internal_data())->children(); - - if (!nodes) - return {}; - - Vector found_indices; - for (auto it = nodes->begin(); !it.is_end(); ++it) { - GUI::ModelIndex index = this->index(it.index(), StackFrame, parent); - if (!string_matches(data(index, GUI::ModelRole::Display).as_string(), searching, flags)) - continue; - - found_indices.append(index); - if (flags & FirstMatchOnly) - break; - } - return found_indices; -} - -} diff --git a/Userland/DevTools/Profiler/ProfileModel.h b/Userland/DevTools/Profiler/ProfileModel.h deleted file mode 100644 index e94a03e8b8c..00000000000 --- a/Userland/DevTools/Profiler/ProfileModel.h +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * Copyright (c) 2022, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include - -namespace Profiler { - -class Profile; - -class ProfileModel final : public GUI::Model { -public: - static NonnullRefPtr create(Profile& profile) - { - return adopt_ref(*new ProfileModel(profile)); - } - - enum Column { - SampleCount, - SelfCount, - ObjectName, - StackFrame, - SymbolAddress, - __Count - }; - - virtual ~ProfileModel() override = default; - - virtual int row_count(GUI::ModelIndex const& = GUI::ModelIndex()) const override; - virtual int column_count(GUI::ModelIndex const& = GUI::ModelIndex()) const override; - virtual ErrorOr column_name(int) const override; - virtual GUI::Variant data(GUI::ModelIndex const&, GUI::ModelRole) const override; - virtual GUI::ModelIndex index(int row, int column, GUI::ModelIndex const& parent = GUI::ModelIndex()) const override; - virtual GUI::ModelIndex parent_index(GUI::ModelIndex const&) const override; - virtual int tree_column() const override { return Column::StackFrame; } - virtual bool is_column_sortable(int) const override { return false; } - virtual bool is_searchable() const override { return true; } - virtual Vector matches(StringView, unsigned flags, GUI::ModelIndex const&) override; - -private: - explicit ProfileModel(Profile&); - - Profile& m_profile; - - GUI::Icon m_user_frame_icon; - GUI::Icon m_kernel_frame_icon; -}; - -} diff --git a/Userland/DevTools/Profiler/SamplesModel.cpp b/Userland/DevTools/Profiler/SamplesModel.cpp deleted file mode 100644 index 7c0af2f61c6..00000000000 --- a/Userland/DevTools/Profiler/SamplesModel.cpp +++ /dev/null @@ -1,119 +0,0 @@ -/* - * Copyright (c) 2018-2021, Andreas Kling - * Copyright (c) 2022, the SerenityOS developers. - * Copyright (c) 2023, Jakub Berkop - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "SamplesModel.h" -#include "Profile.h" -#include - -namespace Profiler { - -SamplesModel::SamplesModel(Profile& profile) - : m_profile(profile) -{ - m_user_frame_icon.set_bitmap_for_size(16, Gfx::Bitmap::load_from_file("/res/icons/16x16/inspector-object.png"sv).release_value_but_fixme_should_propagate_errors()); - m_kernel_frame_icon.set_bitmap_for_size(16, Gfx::Bitmap::load_from_file("/res/icons/16x16/inspector-object-red.png"sv).release_value_but_fixme_should_propagate_errors()); -} - -int SamplesModel::row_count(GUI::ModelIndex const&) const -{ - return m_profile.filtered_event_indices().size(); -} - -int SamplesModel::column_count(GUI::ModelIndex const&) const -{ - return Column::__Count; -} - -ErrorOr SamplesModel::column_name(int column) const -{ - switch (column) { - case Column::SampleIndex: - return "#"_string; - case Column::Timestamp: - return "Timestamp"_string; - case Column::ProcessID: - return "PID"_string; - case Column::ThreadID: - return "TID"_string; - case Column::ExecutableName: - return "Executable"_string; - case Column::LostSamples: - return "Lost Samples"_string; - case Column::InnermostStackFrame: - return "Innermost Frame"_string; - case Column::Path: - return "Path"_string; - default: - VERIFY_NOT_REACHED(); - } -} - -GUI::Variant SamplesModel::data(GUI::ModelIndex const& index, GUI::ModelRole role) const -{ - u32 event_index = m_profile.filtered_event_indices()[index.row()]; - auto const& event = m_profile.events().at(event_index); - - if (role == GUI::ModelRole::Custom) { - return event_index; - } - - if (role == GUI::ModelRole::Display) { - if (index.column() == Column::SampleIndex) - return event_index; - - if (index.column() == Column::ProcessID) - return event.pid; - - if (index.column() == Column::ThreadID) - return event.tid; - - if (index.column() == Column::ExecutableName) { - if (auto const* process = m_profile.find_process(event.pid, event.serial)) - return process->executable; - return ""; - } - - if (index.column() == Column::Timestamp) { - return (u32)event.timestamp; - } - - if (index.column() == Column::LostSamples) { - return event.lost_samples; - } - - if (index.column() == Column::InnermostStackFrame) { - return event.frames.last().symbol; - } - - if (index.column() == Column::Path) { - if (event.data.has()) { - return event.data.get().data.visit( - [&](Profile::Event::OpenEventData const& data) { - return data.path; - }, - [&](Profile::Event::CloseEventData const& data) { - return data.path; - }, - [&](Profile::Event::ReadvEventData const& data) { - return data.path; - }, - [&](Profile::Event::ReadEventData const& data) { - return data.path; - }, - [&](Profile::Event::PreadEventData const& data) { - return data.path; - }); - } - } - - return {}; - } - return {}; -} - -} diff --git a/Userland/DevTools/Profiler/SamplesModel.h b/Userland/DevTools/Profiler/SamplesModel.h deleted file mode 100644 index fe91f7d8cf6..00000000000 --- a/Userland/DevTools/Profiler/SamplesModel.h +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright (c) 2018-2021, Andreas Kling - * Copyright (c) 2022, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include - -namespace Profiler { - -class Profile; - -class SamplesModel final : public GUI::Model { -public: - static NonnullRefPtr create(Profile& profile) - { - return adopt_ref(*new SamplesModel(profile)); - } - - enum Column { - SampleIndex, - Timestamp, - ProcessID, - ThreadID, - ExecutableName, - LostSamples, - InnermostStackFrame, - Path, - __Count - }; - - virtual ~SamplesModel() override = default; - - virtual int row_count(GUI::ModelIndex const& = GUI::ModelIndex()) const override; - virtual int column_count(GUI::ModelIndex const& = GUI::ModelIndex()) const override; - virtual ErrorOr column_name(int) const override; - virtual GUI::Variant data(GUI::ModelIndex const&, GUI::ModelRole) const override; - virtual bool is_column_sortable(int) const override { return false; } - -private: - explicit SamplesModel(Profile&); - - Profile& m_profile; - - GUI::Icon m_user_frame_icon; - GUI::Icon m_kernel_frame_icon; -}; - -} diff --git a/Userland/DevTools/Profiler/SignpostsModel.cpp b/Userland/DevTools/Profiler/SignpostsModel.cpp deleted file mode 100644 index aba5e0f561e..00000000000 --- a/Userland/DevTools/Profiler/SignpostsModel.cpp +++ /dev/null @@ -1,92 +0,0 @@ -/* - * Copyright (c) 2021, Andreas Kling - * Copyright (c) 2022, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "SignpostsModel.h" -#include "Profile.h" -#include - -namespace Profiler { - -SignpostsModel::SignpostsModel(Profile& profile) - : m_profile(profile) -{ -} - -int SignpostsModel::row_count(GUI::ModelIndex const&) const -{ - return m_profile.filtered_signpost_indices().size(); -} - -int SignpostsModel::column_count(GUI::ModelIndex const&) const -{ - return Column::__Count; -} - -ErrorOr SignpostsModel::column_name(int column) const -{ - switch (column) { - case Column::SignpostIndex: - return "#"_string; - case Column::Timestamp: - return "Timestamp"_string; - case Column::ProcessID: - return "PID"_string; - case Column::ThreadID: - return "TID"_string; - case Column::ExecutableName: - return "Executable"_string; - case Column::SignpostString: - return "String"_string; - case Column::SignpostArgument: - return "Argument"_string; - default: - VERIFY_NOT_REACHED(); - } -} - -GUI::Variant SignpostsModel::data(GUI::ModelIndex const& index, GUI::ModelRole role) const -{ - u32 event_index = m_profile.filtered_signpost_indices()[index.row()]; - auto const& event = m_profile.events().at(event_index); - - if (role == GUI::ModelRole::Custom) { - return event_index; - } - - if (role == GUI::ModelRole::Display) { - if (index.column() == Column::SignpostIndex) - return event_index; - - if (index.column() == Column::ProcessID) - return event.pid; - - if (index.column() == Column::ThreadID) - return event.tid; - - if (index.column() == Column::ExecutableName) { - if (auto const* process = m_profile.find_process(event.pid, event.serial)) - return process->executable; - return ""; - } - - if (index.column() == Column::Timestamp) { - return (u32)event.timestamp; - } - - if (index.column() == Column::SignpostString) { - return event.data.get().string; - } - - if (index.column() == Column::SignpostArgument) { - return event.data.get().arg; - } - return {}; - } - return {}; -} - -} diff --git a/Userland/DevTools/Profiler/SignpostsModel.h b/Userland/DevTools/Profiler/SignpostsModel.h deleted file mode 100644 index 8f7b5871e9c..00000000000 --- a/Userland/DevTools/Profiler/SignpostsModel.h +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright (c) 2021, Andreas Kling - * Copyright (c) 2022, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include - -namespace Profiler { - -class Profile; - -class SignpostsModel final : public GUI::Model { -public: - static NonnullRefPtr create(Profile& profile) - { - return adopt_ref(*new SignpostsModel(profile)); - } - - enum Column { - SignpostIndex, - Timestamp, - ProcessID, - ThreadID, - ExecutableName, - SignpostString, - SignpostArgument, - __Count - }; - - virtual ~SignpostsModel() override = default; - - virtual int row_count(GUI::ModelIndex const& = GUI::ModelIndex()) const override; - virtual int column_count(GUI::ModelIndex const& = GUI::ModelIndex()) const override; - virtual ErrorOr column_name(int) const override; - virtual GUI::Variant data(GUI::ModelIndex const&, GUI::ModelRole) const override; - virtual bool is_column_sortable(int) const override { return false; } - -private: - explicit SignpostsModel(Profile&); - - Profile& m_profile; -}; - -} diff --git a/Userland/DevTools/Profiler/SourceModel.cpp b/Userland/DevTools/Profiler/SourceModel.cpp deleted file mode 100644 index def4c06a234..00000000000 --- a/Userland/DevTools/Profiler/SourceModel.cpp +++ /dev/null @@ -1,205 +0,0 @@ -/* - * Copyright (c) 2021, Stephan Unverwerth - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "SourceModel.h" -#include "Gradient.h" -#include "Profile.h" -#include -#include -#include -#include - -namespace Profiler { - -class SourceFile final { -public: - struct Line { - ByteString content; - size_t num_samples { 0 }; - }; - - static constexpr StringView source_root_path = "/usr/src/serenity/"sv; - - SourceFile(StringView filename) - { - ByteString source_file_name = filename.replace("../../"sv, source_root_path, ReplaceMode::FirstOnly); - - auto try_read_lines = [&]() -> ErrorOr { - auto unbuffered_file = TRY(Core::File::open(source_file_name, Core::File::OpenMode::Read)); - auto file = TRY(Core::InputBufferedFile::create(move(unbuffered_file))); - - Array buffer; - while (!file->is_eof()) - m_lines.append({ TRY(file->read_line(buffer)), 0 }); - - return {}; - }; - - auto maybe_error = try_read_lines(); - if (maybe_error.is_error()) { - dbgln("Could not map source file \"{}\". Tried {}. {} (errno={})", filename, source_file_name, maybe_error.error().string_literal(), maybe_error.error().code()); - return; - } - } - - void try_add_samples(size_t line, size_t samples) - { - if (line < 1 || line - 1 >= m_lines.size()) - return; - - m_lines[line - 1].num_samples += samples; - } - - Vector const& lines() const { return m_lines; } - -private: - Vector m_lines; -}; - -SourceModel::SourceModel(Profile& profile, ProfileNode& node) - : m_profile(profile) - , m_node(node) -{ - FlatPtr base_address = 0; - Debug::DebugInfo const* debug_info; - if (auto maybe_kernel_base = Symbolication::kernel_base(); maybe_kernel_base.has_value() && m_node.address() >= *maybe_kernel_base) { - if (!g_kernel_debuginfo_object.has_value()) - return; - base_address = maybe_kernel_base.release_value(); - if (g_kernel_debug_info == nullptr) - g_kernel_debug_info = make(g_kernel_debuginfo_object->elf, ByteString::empty(), base_address); - debug_info = g_kernel_debug_info.ptr(); - } else { - auto const& process = node.process(); - auto const* library_data = process.library_metadata.library_containing(node.address()); - if (!library_data) { - dbgln("no library data for address {:p}", node.address()); - return; - } - base_address = library_data->base; - debug_info = &library_data->load_debug_info(base_address); - } - - VERIFY(debug_info != nullptr); - - // Try to read all source files contributing to the selected function and aggregate the samples by line. - HashMap source_files; - for (auto const& pair : node.events_per_address()) { - auto position = debug_info->get_source_position(pair.key - base_address); - if (position.has_value()) { - auto it = source_files.find(position.value().file_path); - if (it == source_files.end()) { - source_files.set(position.value().file_path, SourceFile(position.value().file_path)); - it = source_files.find(position.value().file_path); - } - - it->value.try_add_samples(position.value().line_number, pair.value); - } - } - - // Process source file map and turn content into view model - for (auto const& file_iterator : source_files) { - u32 line_number = 0; - for (auto const& line_iterator : file_iterator.value.lines()) { - line_number++; - - m_source_lines.append({ - (u32)line_iterator.num_samples, - line_iterator.num_samples * 100.0f / node.event_count(), - file_iterator.key, - line_number, - line_iterator.content, - }); - } - } -} - -int SourceModel::row_count(GUI::ModelIndex const&) const -{ - return m_source_lines.size(); -} - -ErrorOr SourceModel::column_name(int column) const -{ - switch (column) { - case Column::SampleCount: - return m_profile.show_percentages() ? "% Samples"_string : "# Samples"_string; - case Column::SourceCode: - return "Source Code"_string; - case Column::Location: - return "Location"_string; - case Column::LineNumber: - return "Line"_string; - default: - VERIFY_NOT_REACHED(); - } -} - -struct ColorPair { - Color background; - Color foreground; -}; - -static Optional color_pair_for(SourceLineData const& line) -{ - if (line.percent == 0) - return {}; - - Color background = color_for_percent(line.percent); - Color foreground; - if (line.percent > 50) - foreground = Color::White; - else - foreground = Color::Black; - return ColorPair { background, foreground }; -} - -GUI::Variant SourceModel::data(GUI::ModelIndex const& index, GUI::ModelRole role) const -{ - auto const& line = m_source_lines[index.row()]; - - if (role == GUI::ModelRole::BackgroundColor) { - auto colors = color_pair_for(line); - if (!colors.has_value()) - return {}; - return colors.value().background; - } - - if (role == GUI::ModelRole::ForegroundColor) { - auto colors = color_pair_for(line); - if (!colors.has_value()) - return {}; - return colors.value().foreground; - } - - if (role == GUI::ModelRole::Font) { - if (index.column() == Column::SourceCode) - return Gfx::FontDatabase::default_fixed_width_font(); - return {}; - } - - if (role == GUI::ModelRole::Display) { - if (index.column() == Column::SampleCount) { - if (m_profile.show_percentages()) - return ((float)line.event_count / (float)m_node.event_count()) * 100.0f; - return line.event_count; - } - - if (index.column() == Column::Location) - return line.location; - - if (index.column() == Column::LineNumber) - return line.line_number; - - if (index.column() == Column::SourceCode) - return line.source_code; - - return {}; - } - return {}; -} - -} diff --git a/Userland/DevTools/Profiler/SourceModel.h b/Userland/DevTools/Profiler/SourceModel.h deleted file mode 100644 index 396c1a4499a..00000000000 --- a/Userland/DevTools/Profiler/SourceModel.h +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright (c) 2021, Stephan Unverwerth - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include - -namespace Profiler { - -class Profile; -class ProfileNode; - -struct SourceLineData { - u32 event_count { 0 }; - float percent { 0 }; - ByteString location; - u32 line_number { 0 }; - ByteString source_code; -}; - -class SourceModel final : public GUI::Model { -public: - static NonnullRefPtr create(Profile& profile, ProfileNode& node) - { - return adopt_ref(*new SourceModel(profile, node)); - } - - enum Column { - SampleCount, - Location, - LineNumber, - SourceCode, - __Count - }; - - virtual int row_count(GUI::ModelIndex const& = GUI::ModelIndex()) const override; - virtual int column_count(GUI::ModelIndex const& = GUI::ModelIndex()) const override { return Column::__Count; } - virtual ErrorOr column_name(int) const override; - virtual GUI::Variant data(GUI::ModelIndex const&, GUI::ModelRole) const override; - virtual bool is_column_sortable(int) const override { return false; } - -private: - SourceModel(Profile&, ProfileNode&); - - Profile& m_profile; - ProfileNode& m_node; - - Vector m_source_lines; -}; - -} diff --git a/Userland/DevTools/Profiler/TimelineContainer.cpp b/Userland/DevTools/Profiler/TimelineContainer.cpp deleted file mode 100644 index f3927812287..00000000000 --- a/Userland/DevTools/Profiler/TimelineContainer.cpp +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Copyright (c) 2021, Andreas Kling - * Copyright (c) 2022, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "TimelineContainer.h" -#include "TimelineView.h" -#include - -namespace Profiler { - -TimelineContainer::TimelineContainer(GUI::Widget& header_container, TimelineView& timeline_view) -{ - set_should_hide_unnecessary_scrollbars(true); - m_header_container = header_container; - m_timeline_view = timeline_view; - add_child(header_container); - add_child(timeline_view); - header_container.move_to_back(); - timeline_view.move_to_back(); - update_widget_sizes(); - update_widget_positions(); - - int initial_height = min(300, timeline_view.height() + 16 + frame_thickness() * 2); - set_preferred_height(initial_height); - - m_timeline_view->on_scale_change = [this] { - update_widget_sizes(); - update_widget_positions(); - }; -} - -void TimelineContainer::did_scroll() -{ - AbstractScrollableWidget::did_scroll(); - update_widget_positions(); -} - -void TimelineContainer::update_widget_positions() -{ - m_header_container->move_to(0, -vertical_scrollbar().value()); - m_timeline_view->move_to(m_header_container->width() + -horizontal_scrollbar().value(), -vertical_scrollbar().value()); -} - -void TimelineContainer::update_widget_sizes() -{ - { - m_timeline_view->do_layout(); - auto preferred_size = m_timeline_view->effective_preferred_size(); - m_timeline_view->resize(Gfx::IntSize(preferred_size)); - set_content_size(Gfx::IntSize(preferred_size)); - } - - { - m_header_container->do_layout(); - auto preferred_size = m_header_container->effective_preferred_size(); - m_header_container->resize(Gfx::IntSize(preferred_size)); - set_size_occupied_by_fixed_elements({ preferred_size.width().as_int(), 0 }); - } -} - -void TimelineContainer::resize_event(GUI::ResizeEvent& event) -{ - AbstractScrollableWidget::resize_event(event); - update_widget_sizes(); - update_widget_positions(); -} - -} diff --git a/Userland/DevTools/Profiler/TimelineContainer.h b/Userland/DevTools/Profiler/TimelineContainer.h deleted file mode 100644 index c096d8d45d3..00000000000 --- a/Userland/DevTools/Profiler/TimelineContainer.h +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright (c) 2021, Andreas Kling - * Copyright (c) 2022, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include - -namespace Profiler { - -class TimelineView; - -class TimelineContainer : public GUI::AbstractScrollableWidget { - C_OBJECT(TimelineContainer); - -public: - virtual ~TimelineContainer() override = default; - -protected: - virtual void did_scroll() override; - virtual void resize_event(GUI::ResizeEvent&) override; - -private: - void update_widget_sizes(); - void update_widget_positions(); - - TimelineContainer(GUI::Widget& header_container, TimelineView&); - - RefPtr m_timeline_view; - RefPtr m_header_container; -}; - -} diff --git a/Userland/DevTools/Profiler/TimelineHeader.cpp b/Userland/DevTools/Profiler/TimelineHeader.cpp deleted file mode 100644 index d8128459ca3..00000000000 --- a/Userland/DevTools/Profiler/TimelineHeader.cpp +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Copyright (c) 2021, Andreas Kling - * Copyright (c) 2022, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "TimelineHeader.h" -#include "Process.h" -#include "Profile.h" -#include -#include -#include -#include - -namespace Profiler { - -TimelineHeader::TimelineHeader(Profile& profile, Process const& process) - : m_profile(profile) - , m_process(process) -{ - set_frame_style(Gfx::FrameStyle::RaisedPanel); - set_fixed_size(200, 40); - update_selection(); - - m_icon = GUI::FileIconProvider::icon_for_executable(m_process.executable).bitmap_for_size(32); - m_text = ByteString::formatted("{} ({})", LexicalPath::basename(m_process.executable), m_process.pid); -} - -void TimelineHeader::paint_event(GUI::PaintEvent& event) -{ - GUI::Frame::paint_event(event); - GUI::Painter painter(*this); - painter.add_clip_rect(event.rect()); - - painter.fill_rect(frame_inner_rect(), m_selected ? palette().selection() : palette().button()); - - Gfx::IntRect icon_rect { frame_thickness() + 2, 0, 32, 32 }; - icon_rect.center_vertically_within(frame_inner_rect()); - - if (m_icon) - painter.blit(icon_rect.location(), *m_icon, m_icon->rect()); - - Gfx::IntRect text_rect { - icon_rect.right() + 5, - icon_rect.y(), - width() - 32, - 32 - }; - text_rect.center_vertically_within(frame_inner_rect()); - - auto const& font = m_selected ? painter.font().bold_variant() : painter.font(); - auto color = m_selected ? palette().selection_text() : palette().button_text(); - painter.draw_text(text_rect, m_text, font, Gfx::TextAlignment::CenterLeft, color); -} - -void TimelineHeader::update_selection() -{ - m_selected = m_profile.has_process_filter() && m_profile.process_filter_contains(m_process.pid, m_process.start_valid); - update(); -} - -void TimelineHeader::mousedown_event(GUI::MouseEvent& event) -{ - if (event.button() != GUI::MouseButton::Primary) - return; - m_selected = !m_selected; - on_selection_change(m_selected); -} - -} diff --git a/Userland/DevTools/Profiler/TimelineHeader.h b/Userland/DevTools/Profiler/TimelineHeader.h deleted file mode 100644 index a4a0e0c198e..00000000000 --- a/Userland/DevTools/Profiler/TimelineHeader.h +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright (c) 2021, Andreas Kling - * Copyright (c) 2022, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include - -namespace Profiler { - -class Profile; -struct Process; - -class TimelineHeader final : public GUI::Frame { - C_OBJECT(TimelineHeader); - -public: - virtual ~TimelineHeader() override = default; - - Function on_selection_change; - - void update_selection(); - -private: - TimelineHeader(Profile& profile, Process const&); - - virtual void paint_event(GUI::PaintEvent&) override; - virtual void mousedown_event(GUI::MouseEvent&) override; - - Profile& m_profile; - Process const& m_process; - RefPtr m_icon; - ByteString m_text; - bool m_selected; -}; - -} diff --git a/Userland/DevTools/Profiler/TimelineTrack.cpp b/Userland/DevTools/Profiler/TimelineTrack.cpp deleted file mode 100644 index 3588b8c8804..00000000000 --- a/Userland/DevTools/Profiler/TimelineTrack.cpp +++ /dev/null @@ -1,187 +0,0 @@ -/* - * Copyright (c) 2018-2021, Andreas Kling - * Copyright (c) 2022, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "TimelineTrack.h" -#include "Profile.h" -#include "TimelineView.h" -#include -#include -#include - -namespace Profiler { - -TimelineTrack::TimelineTrack(TimelineView const& view, Profile const& profile, Process const& process) - : m_view(view) - , m_profile(profile) - , m_process(process) -{ - set_fill_with_background_color(true); - set_background_role(Gfx::ColorRole::Base); - set_fixed_height(40); - set_scale(view.scale()); - set_frame_style(Gfx::FrameStyle::SunkenPanel); -} - -void TimelineTrack::set_scale(float scale) -{ - set_fixed_width(m_profile.length_in_ms() / scale); -} - -void TimelineTrack::event(Core::Event& event) -{ - switch (event.type()) { - case GUI::Event::MouseUp: - case GUI::Event::MouseDown: - case GUI::Event::MouseMove: - event.ignore(); - break; - default: - break; - } - GUI::Frame::event(event); -} - -void TimelineTrack::paint_event(GUI::PaintEvent& event) -{ - GUI::Frame::paint_event(event); - - GUI::Painter painter(*this); - painter.add_clip_rect(event.rect()); - - u64 const start_of_trace = m_profile.first_timestamp(); - u64 const end_of_trace = start_of_trace + m_profile.length_in_ms(); - - auto const clamp_timestamp = [start_of_trace, end_of_trace](u64 timestamp) -> u64 { - return min(end_of_trace, max(timestamp, start_of_trace)); - }; - - recompute_histograms_if_needed({ start_of_trace, end_of_trace, (size_t)m_profile.length_in_ms() }); - - float column_width = this->column_width(); - float frame_height = (float)frame_inner_rect().height() / (float)m_max_value; - - for_each_signpost([&](auto& signpost) { - int x = (int)((float)(signpost.timestamp - start_of_trace) * column_width); - int y1 = frame_thickness(); - int y2 = height() - frame_thickness() * 2; - - painter.draw_line({ x, y1 }, { x, y2 }, Color::Magenta); - - return IterationDecision::Continue; - }); - - for (size_t bucket = 0; bucket < m_kernel_histogram->size(); bucket++) { - auto kernel_value = m_kernel_histogram->at(bucket); - auto user_value = m_user_histogram->at(bucket); - if (kernel_value + user_value == 0) - continue; - - auto t = bucket; - - int x = (int)((float)t * column_width); - int cw = max(1, (int)column_width); - - int kernel_column_height = frame_inner_rect().height() - (int)((float)kernel_value * frame_height); - int user_column_height = frame_inner_rect().height() - (int)((float)(kernel_value + user_value) * frame_height); - - constexpr auto kernel_color = Color::from_rgb(0xc25e5a); - constexpr auto user_color = Color::from_rgb(0x5a65c2); - painter.fill_rect({ x, frame_thickness() + user_column_height, cw, height() - frame_thickness() * 2 }, user_color); - painter.fill_rect({ x, frame_thickness() + kernel_column_height, cw, height() - frame_thickness() * 2 }, kernel_color); - } - - u64 normalized_start_time = clamp_timestamp(min(m_view.select_start_time(), m_view.select_end_time())); - u64 normalized_end_time = clamp_timestamp(max(m_view.select_start_time(), m_view.select_end_time())); - u64 normalized_hover_time = clamp_timestamp(m_view.hover_time()); - - int select_start_x = (int)((float)(normalized_start_time - start_of_trace) * column_width); - int select_end_x = (int)((float)(normalized_end_time - start_of_trace) * column_width); - int select_hover_x = (int)((float)(normalized_hover_time - start_of_trace) * column_width); - painter.fill_rect({ select_start_x, frame_thickness(), select_end_x - select_start_x, height() - frame_thickness() * 2 }, Color(0, 0, 0, 60)); - painter.fill_rect({ select_hover_x, frame_thickness(), 1, height() - frame_thickness() * 2 }, Color::NamedColor::Black); -} - -template -void TimelineTrack::for_each_signpost(Callback callback) -{ - m_profile.for_each_signpost([&](auto& signpost) { - if (signpost.pid != m_process.pid) - return IterationDecision::Continue; - - if (!m_process.valid_at(signpost.serial)) - return IterationDecision::Continue; - - return callback(signpost); - }); -} - -void TimelineTrack::mousemove_event(GUI::MouseEvent& event) -{ - auto column_width = this->column_width(); - bool hovering_a_signpost = false; - - for_each_signpost([&](auto& signpost) { - int x = (int)((float)(signpost.timestamp - m_profile.first_timestamp()) * column_width); - constexpr int hoverable_padding = 2; - Gfx::IntRect hoverable_rect { x - hoverable_padding, frame_thickness(), hoverable_padding * 2, height() - frame_thickness() * 2 }; - if (hoverable_rect.contains_horizontally(event.x())) { - auto const& data = signpost.data.template get(); - GUI::Application::the()->show_tooltip_immediately(MUST(String::formatted("{}, {}", data.string, data.arg)), this); - hovering_a_signpost = true; - return IterationDecision::Break; - } - return IterationDecision::Continue; - }); - - if (!hovering_a_signpost) - GUI::Application::the()->hide_tooltip(); -} - -void TimelineTrack::recompute_histograms_if_needed(HistogramInputs const& inputs) -{ - if (m_cached_histogram_inputs == inputs && m_kernel_histogram.has_value()) - return; - - auto const clamp_timestamp = [&inputs](u64 timestamp) -> u64 { - return min(inputs.end, max(timestamp, inputs.start)); - }; - - m_kernel_histogram = Histogram { inputs.start, inputs.end, inputs.columns }; - m_user_histogram = Histogram { inputs.start, inputs.end, inputs.columns }; - - for (auto const& event : m_profile.events()) { - if (event.pid != m_process.pid) - continue; - - if (!m_process.valid_at(event.serial)) - continue; - - auto& histogram = event.in_kernel ? *m_kernel_histogram : *m_user_histogram; - histogram.insert(clamp_timestamp(event.timestamp), 1 + event.lost_samples); - } - - auto shorter_histogram_size = min(m_kernel_histogram->size(), m_user_histogram->size()); - for (size_t bucket = 0; bucket < shorter_histogram_size; ++bucket) { - auto value = m_kernel_histogram->at(bucket) + m_user_histogram->at(bucket); - if (value > m_max_value) - m_max_value = value; - } - - auto& longer_histogram = m_kernel_histogram->size() > m_user_histogram->size() ? *m_kernel_histogram : *m_user_histogram; - for (size_t bucket = shorter_histogram_size; bucket < longer_histogram.size(); ++bucket) { - auto value = longer_histogram.at(bucket); - if (value > m_max_value) - m_max_value = value; - } -} - -float TimelineTrack::column_width() const -{ - return (float)frame_inner_rect().width() / (float)m_profile.length_in_ms(); -} - -} diff --git a/Userland/DevTools/Profiler/TimelineTrack.h b/Userland/DevTools/Profiler/TimelineTrack.h deleted file mode 100644 index 4a9591477d7..00000000000 --- a/Userland/DevTools/Profiler/TimelineTrack.h +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright (c) 2018-2021, Andreas Kling - * Copyright (c) 2022, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include "Histogram.h" -#include - -namespace Profiler { - -struct Process; -class Profile; -class TimelineView; - -class TimelineTrack final : public GUI::Frame { - C_OBJECT(TimelineTrack); - -public: - virtual ~TimelineTrack() override = default; - - void set_scale(float); - -private: - float column_width() const; - - template - void for_each_signpost(Callback); - - virtual void event(Core::Event&) override; - virtual void paint_event(GUI::PaintEvent&) override; - virtual void mousemove_event(GUI::MouseEvent&) override; - - struct HistogramInputs { - bool operator==(HistogramInputs const&) const = default; - u64 start { 0 }; - u64 end { 0 }; - size_t columns { 0 }; - }; - - void recompute_histograms_if_needed(HistogramInputs const&); - - explicit TimelineTrack(TimelineView const&, Profile const&, Process const&); - - TimelineView const& m_view; - Profile const& m_profile; - Process const& m_process; - - HistogramInputs m_cached_histogram_inputs; - - Optional> m_kernel_histogram; - Optional> m_user_histogram; - decltype(m_kernel_histogram->at(0)) m_max_value { 0 }; -}; - -} diff --git a/Userland/DevTools/Profiler/TimelineView.cpp b/Userland/DevTools/Profiler/TimelineView.cpp deleted file mode 100644 index f43e76bab26..00000000000 --- a/Userland/DevTools/Profiler/TimelineView.cpp +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright (c) 2021, Andreas Kling - * Copyright (c) 2022, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "TimelineView.h" -#include "Profile.h" -#include "TimelineTrack.h" -#include - -namespace Profiler { - -TimelineView::TimelineView(Profile& profile) - : m_profile(profile) -{ - set_layout(); - set_shrink_to_fit(true); -} - -u64 TimelineView::timestamp_at_x(int x) const -{ - float column_width = (float)width() / (float)m_profile.length_in_ms(); - float ms_into_profile = (float)x / column_width; - return m_profile.first_timestamp() + (u64)ms_into_profile; -} - -void TimelineView::mousedown_event(GUI::MouseEvent& event) -{ - if (event.button() != GUI::MouseButton::Primary) - return; - - set_selecting(true); - set_select_start_time(timestamp_at_x(event.x())); - set_select_end_time(select_start_time()); - m_profile.set_timestamp_filter_range(select_start_time(), select_end_time()); - update(); -} - -void TimelineView::mousemove_event(GUI::MouseEvent& event) -{ - set_hover_time(timestamp_at_x(event.x())); - - if (is_selecting()) { - set_select_end_time(hover_time()); - m_profile.set_timestamp_filter_range(select_start_time(), select_end_time()); - } - - update(); -} - -void TimelineView::mouseup_event(GUI::MouseEvent& event) -{ - if (event.button() != GUI::MouseButton::Primary) - return; - - set_selecting(false); - if (select_start_time() == select_end_time()) - m_profile.clear_timestamp_filter_range(); -} - -void TimelineView::mousewheel_event(GUI::MouseEvent& event) -{ - if (event.modifiers() == Mod_Ctrl) { - event.accept(); - m_scale += event.wheel_delta_y(); - m_scale = clamp(m_scale, 1.0f, 100.0f); - for_each_child_of_type([&](auto& track) { - track.set_scale(m_scale); - return IterationDecision::Continue; - }); - on_scale_change(); - } -} - -} diff --git a/Userland/DevTools/Profiler/TimelineView.h b/Userland/DevTools/Profiler/TimelineView.h deleted file mode 100644 index 8be0694ceea..00000000000 --- a/Userland/DevTools/Profiler/TimelineView.h +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Copyright (c) 2021, Andreas Kling - * Copyright (c) 2022, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include - -namespace Profiler { - -class Profile; -class TimelineTrack; - -class TimelineView final : public GUI::Widget { - C_OBJECT(TimelineView); - -public: - virtual ~TimelineView() override = default; - - Function on_selection_change; - Function on_scale_change; - - float scale() const { return m_scale; } - - bool is_selecting() const { return m_selecting; } - u64 select_start_time() const { return m_select_start_time; } - u64 select_end_time() const { return m_select_end_time; } - u64 hover_time() const { return m_hover_time; } - - void set_selecting(bool value) - { - if (m_selecting == value) - return; - m_selecting = value; - } - - void set_select_start_time(u64 value) - { - if (m_select_start_time == value) - return; - m_select_start_time = value; - update(); - if (on_selection_change) - on_selection_change(); - } - void set_select_end_time(u64 value) - { - if (m_select_end_time == value) - return; - m_select_end_time = value; - update(); - if (on_selection_change) - on_selection_change(); - } - void set_hover_time(u64 value) - { - if (m_hover_time == value) - return; - m_hover_time = value; - update(); - if (on_selection_change) - on_selection_change(); - } - -private: - virtual void mousedown_event(GUI::MouseEvent&) override; - virtual void mousemove_event(GUI::MouseEvent&) override; - virtual void mouseup_event(GUI::MouseEvent&) override; - virtual void mousewheel_event(GUI::MouseEvent&) override; - - explicit TimelineView(Profile&); - - u64 timestamp_at_x(int x) const; - - Profile& m_profile; - bool m_selecting { false }; - u64 m_select_start_time { 0 }; - u64 m_select_end_time { 0 }; - u64 m_hover_time { 0 }; - float m_scale { 10 }; -}; - -} diff --git a/Userland/DevTools/Profiler/main.cpp b/Userland/DevTools/Profiler/main.cpp deleted file mode 100644 index 4308235cd49..00000000000 --- a/Userland/DevTools/Profiler/main.cpp +++ /dev/null @@ -1,384 +0,0 @@ -/* - * Copyright (c) 2018-2021, Andreas Kling - * Copyright (c) 2021, Julius Heijmen - * Copyright (c) 2023, Jelle Raaijmakers - * Copyright (c) 2023, Jakub Berkop - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "FlameGraphView.h" -#include "IndividualSampleModel.h" -#include "Profile.h" -#include "ProfileModel.h" -#include "TimelineContainer.h" -#include "TimelineHeader.h" -#include "TimelineTrack.h" -#include "TimelineView.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -using namespace Profiler; - -static bool generate_profile(pid_t& pid); - -ErrorOr serenity_main(Main::Arguments arguments) -{ - int pid = 0; - StringView perfcore_file_arg; - Core::ArgsParser args_parser; - args_parser.add_option(pid, "PID to profile", "pid", 'p', "PID"); - args_parser.add_positional_argument(perfcore_file_arg, "Path of perfcore file", "perfcore-file", Core::ArgsParser::Required::No); - args_parser.parse(arguments); - - if (pid && !perfcore_file_arg.is_empty()) { - warnln("-p/--pid option and perfcore-file argument must not be used together!"); - return 1; - } - - auto app = TRY(GUI::Application::create(arguments)); - auto app_icon = TRY(GUI::Icon::try_create_default_icon("app-profiler"sv)); - - ByteString perfcore_file; - if (perfcore_file_arg.is_empty()) { - if (!generate_profile(pid)) - return 0; - perfcore_file = ByteString::formatted("/proc/{}/perf_events", pid); - } else { - perfcore_file = perfcore_file_arg; - } - - auto profile_or_error = Profile::load_from_perfcore_file(perfcore_file); - if (profile_or_error.is_error()) { - GUI::MessageBox::show(nullptr, ByteString::formatted("{}", profile_or_error.error()), "Profiler"sv, GUI::MessageBox::Type::Error); - return 0; - } - - auto& profile = profile_or_error.value(); - - auto window = GUI::Window::construct(); - - TRY(Desktop::Launcher::add_allowed_handler_with_only_specific_urls("/bin/Help", { URL::create_with_file_scheme("/usr/share/man/man1/Applications/Profiler.md") })); - TRY(Desktop::Launcher::seal_allowlist()); - - window->set_title("Profiler"); - window->set_icon(app_icon.bitmap_for_size(16)); - window->restore_size_and_position("Profiler"sv, "Window"sv, { { 800, 600 } }); - window->save_size_and_position_on_close("Profiler"sv, "Window"sv); - - auto main_widget = window->set_main_widget(); - main_widget->set_fill_with_background_color(true); - main_widget->set_layout(); - - auto timeline_header_container = GUI::Widget::construct(); - timeline_header_container->set_layout(); - timeline_header_container->set_fill_with_background_color(true); - timeline_header_container->set_shrink_to_fit(true); - - auto timeline_view = TRY(TimelineView::try_create(*profile)); - for (auto const& process : profile->processes()) { - bool matching_event_found = false; - for (auto const& event : profile->events()) { - if (event.pid == process.pid && process.valid_at(event.serial)) { - matching_event_found = true; - break; - } - } - if (!matching_event_found) - continue; - auto& timeline_header = timeline_header_container->add(*profile, process); - timeline_header.set_shrink_to_fit(true); - timeline_header.on_selection_change = [&](bool selected) { - auto end_valid = process.end_valid == EventSerialNumber {} ? EventSerialNumber::max_valid_serial() : process.end_valid; - if (selected) - profile->add_process_filter(process.pid, process.start_valid, end_valid); - else - profile->remove_process_filter(process.pid, process.start_valid, end_valid); - - timeline_header_container->for_each_child_widget([](auto& other_timeline_header) { - static_cast(other_timeline_header).update_selection(); - return IterationDecision::Continue; - }); - }; - - timeline_view->add(*timeline_view, *profile, process); - } - - auto& main_splitter = main_widget->add(); - - [[maybe_unused]] auto& timeline_container = main_splitter.add(*timeline_header_container, *timeline_view); - - auto& tab_widget = main_splitter.add(); - - auto& tree_tab = tab_widget.add_tab("Call Tree"_string); - tree_tab.set_layout(4); - auto& bottom_splitter = tree_tab.add(); - - auto& tree_view = bottom_splitter.add(); - tree_view.set_should_fill_selected_rows(true); - tree_view.set_column_headers_visible(true); - tree_view.set_selection_behavior(GUI::TreeView::SelectionBehavior::SelectRows); - tree_view.set_model(profile->model()); - - auto& disassembly_view = bottom_splitter.add(); - disassembly_view.set_visible(false); - - auto update_disassembly_model = [&] { - if (disassembly_view.is_visible() && !tree_view.selection().is_empty()) { - profile->set_disassembly_index(tree_view.selection().first()); - disassembly_view.set_model(profile->disassembly_model()); - } else { - disassembly_view.set_model(nullptr); - } - }; - - auto& source_view = bottom_splitter.add(); - source_view.set_visible(false); - - auto update_source_model = [&] { - if (source_view.is_visible() && !tree_view.selection().is_empty()) { - profile->set_source_index(tree_view.selection().first()); - source_view.set_model(profile->source_model()); - } else { - source_view.set_model(nullptr); - } - }; - - tree_view.on_selection_change = [&] { - update_disassembly_model(); - update_source_model(); - }; - - auto disassembly_action = GUI::Action::create_checkable("Show &Disassembly", { Mod_Ctrl, Key_D }, TRY(Gfx::Bitmap::load_from_file("/res/icons/16x16/x86.png"sv)), [&](auto& action) { - disassembly_view.set_visible(action.is_checked()); - update_disassembly_model(); - }); - - auto source_action = GUI::Action::create_checkable("Show &Source", { Mod_Ctrl, Key_S }, TRY(Gfx::Bitmap::load_from_file("/res/icons/16x16/x86.png"sv)), [&](auto& action) { - source_view.set_visible(action.is_checked()); - update_source_model(); - }); - - auto& samples_tab = tab_widget.add_tab("Samples"_string); - samples_tab.set_layout(4); - - auto& samples_splitter = samples_tab.add(); - auto& samples_table_view = samples_splitter.add(); - samples_table_view.set_model(profile->samples_model()); - - auto& individual_sample_view = samples_splitter.add(); - samples_table_view.on_selection_change = [&] { - auto const& index = samples_table_view.selection().first(); - auto model = IndividualSampleModel::create(*profile, index.data(GUI::ModelRole::Custom).to_integer()); - individual_sample_view.set_model(move(model)); - }; - - auto& signposts_tab = tab_widget.add_tab("Signposts"_string); - signposts_tab.set_layout(4); - - auto& signposts_splitter = signposts_tab.add(); - auto& signposts_table_view = signposts_splitter.add(); - signposts_table_view.set_model(profile->signposts_model()); - - auto& individual_signpost_view = signposts_splitter.add(); - signposts_table_view.on_selection_change = [&] { - auto const& index = signposts_table_view.selection().first(); - auto model = IndividualSampleModel::create(*profile, index.data(GUI::ModelRole::Custom).to_integer()); - individual_signpost_view.set_model(move(model)); - }; - - auto& flamegraph_tab = tab_widget.add_tab("Flame Graph"_string); - flamegraph_tab.set_layout(GUI::Margins { 4, 4, 4, 4 }); - - auto& flamegraph_view = flamegraph_tab.add(profile->model(), ProfileModel::Column::StackFrame, ProfileModel::Column::SampleCount); - - u64 const start_of_trace = profile->first_timestamp(); - u64 const end_of_trace = start_of_trace + profile->length_in_ms(); - auto const clamp_timestamp = [start_of_trace, end_of_trace](u64 timestamp) -> u64 { - return min(end_of_trace, max(timestamp, start_of_trace)); - }; - - // FIXME: Make this constexpr once String is able to. - auto const format_sample_count = [&profile](auto const sample_count) { - if (profile->show_percentages()) - return ByteString::formatted("{}%", sample_count.as_string()); - return ByteString::formatted("{} Samples", sample_count.to_i32()); - }; - - auto& statusbar = main_widget->add(); - auto statusbar_update = [&] { - auto& view = *timeline_view; - StringBuilder builder; - - auto flamegraph_hovered_index = flamegraph_view.hovered_index(); - if (flamegraph_hovered_index.is_valid()) { - auto stack = profile->model().data(flamegraph_hovered_index.sibling_at_column(ProfileModel::Column::StackFrame)).to_byte_string(); - auto sample_count = profile->model().data(flamegraph_hovered_index.sibling_at_column(ProfileModel::Column::SampleCount)); - auto self_count = profile->model().data(flamegraph_hovered_index.sibling_at_column(ProfileModel::Column::SelfCount)); - builder.appendff("{}, ", stack); - builder.appendff("Samples: {}, ", format_sample_count(sample_count)); - builder.appendff("Self: {}", format_sample_count(self_count)); - } else { - u64 normalized_start_time = clamp_timestamp(min(view.select_start_time(), view.select_end_time())); - u64 normalized_end_time = clamp_timestamp(max(view.select_start_time(), view.select_end_time())); - u64 normalized_hover_time = clamp_timestamp(view.hover_time()); - builder.appendff("Time: {} ms", normalized_hover_time - start_of_trace); - if (normalized_start_time != normalized_end_time) { - auto start = normalized_start_time - start_of_trace; - auto end = normalized_end_time - start_of_trace; - builder.appendff(", Selection: {} - {} ms", start, end); - builder.appendff(", Duration: {} ms", end - start); - } - } - statusbar.set_text(builder.to_string().release_value_but_fixme_should_propagate_errors()); - }; - timeline_view->on_selection_change = [&] { statusbar_update(); }; - flamegraph_view.on_hover_change = [&] { statusbar_update(); }; - - auto& filesystem_events_tab = tab_widget.add_tab("Filesystem events"_string); - filesystem_events_tab.set_layout(4); - - auto& filesystem_events_tree_view = filesystem_events_tab.add(); - filesystem_events_tree_view.set_should_fill_selected_rows(true); - filesystem_events_tree_view.set_column_headers_visible(true); - filesystem_events_tree_view.set_selection_behavior(GUI::TreeView::SelectionBehavior::SelectRows); - filesystem_events_tree_view.set_model(profile->file_event_model()); - filesystem_events_tree_view.set_column_visible(FileEventModel::Column::OpenDuration, false); - filesystem_events_tree_view.set_column_visible(FileEventModel::Column::CloseDuration, false); - filesystem_events_tree_view.set_column_visible(FileEventModel::Column::ReadvDuration, false); - filesystem_events_tree_view.set_column_visible(FileEventModel::Column::ReadDuration, false); - filesystem_events_tree_view.set_column_visible(FileEventModel::Column::PreadDuration, false); - - auto file_menu = window->add_menu("&File"_string); - file_menu->add_action(GUI::CommonActions::make_quit_action([&](auto&) { app->quit(); })); - - auto view_menu = window->add_menu("&View"_string); - - auto invert_action = GUI::Action::create_checkable("&Invert Tree", { Mod_Ctrl, Key_I }, [&](auto& action) { - profile->set_inverted(action.is_checked()); - }); - invert_action->set_checked(false); - view_menu->add_action(invert_action); - - auto top_functions_action = GUI::Action::create_checkable("&Top Functions", { Mod_Ctrl, Key_T }, [&](auto& action) { - profile->set_show_top_functions(action.is_checked()); - }); - top_functions_action->set_checked(false); - view_menu->add_action(top_functions_action); - - auto percent_action = GUI::Action::create_checkable("Show &Percentages", { Mod_Ctrl, Key_P }, [&](auto& action) { - profile->set_show_percentages(action.is_checked()); - tree_view.update(); - disassembly_view.update(); - source_view.update(); - }); - percent_action->set_checked(false); - view_menu->add_action(percent_action); - - view_menu->add_action(disassembly_action); - view_menu->add_action(source_action); - - auto help_menu = window->add_menu("&Help"_string); - help_menu->add_action(GUI::CommonActions::make_command_palette_action(window)); - help_menu->add_action(GUI::CommonActions::make_help_action([](auto&) { - Desktop::Launcher::open(URL::create_with_file_scheme("/usr/share/man/man1/Applications/Profiler.md"), "/bin/Help"); - })); - help_menu->add_action(GUI::CommonActions::make_about_action("Profiler"_string, app_icon, window)); - - window->show(); - return app->exec(); -} - -static bool prompt_to_stop_profiling(pid_t pid, ByteString const& process_name) -{ - auto window = GUI::Window::construct(); - window->set_title(ByteString::formatted("Profiling {}({})", process_name, pid)); - window->resize(240, 100); - window->set_icon(Gfx::Bitmap::load_from_file("/res/icons/16x16/app-profiler.png"sv).release_value_but_fixme_should_propagate_errors()); - window->center_on_screen(); - - auto widget = window->set_main_widget(); - widget->set_fill_with_background_color(true); - widget->set_layout(GUI::Margins { 0, 0, 16 }); - - auto& timer_label = widget->add("..."_string); - Core::ElapsedTimer clock; - clock.start(); - auto update_timer = Core::Timer::create_repeating(100, [&] { - timer_label.set_text(String::formatted("{:.1} seconds", static_cast(clock.elapsed()) / 1000.0f).release_value_but_fixme_should_propagate_errors()); - }); - update_timer->start(); - - auto& stop_button = widget->add("Stop"_string); - stop_button.set_fixed_size(140, 22); - stop_button.on_click = [&](auto) { - GUI::Application::the()->quit(); - }; - - window->show(); - return GUI::Application::the()->exec() == 0; -} - -bool generate_profile(pid_t& pid) -{ - if (!pid) { - auto process_chooser = GUI::ProcessChooser::construct("Profiler"sv, "Profile"_string, Gfx::Bitmap::load_from_file("/res/icons/16x16/app-profiler.png"sv).release_value_but_fixme_should_propagate_errors()); - if (process_chooser->exec() == GUI::Dialog::ExecResult::Cancel) - return false; - pid = process_chooser->pid(); - } - - ByteString process_name; - - auto all_processes = Core::ProcessStatisticsReader::get_all(); - if (!all_processes.is_error()) { - auto& processes = all_processes.value().processes; - if (auto it = processes.find_if([&](auto& entry) { return entry.pid == pid; }); it != processes.end()) - process_name = it->name; - else - process_name = "(unknown)"; - } else { - process_name = "(unknown)"; - } - - static constexpr u64 event_mask = PERF_EVENT_SAMPLE | PERF_EVENT_MMAP | PERF_EVENT_MUNMAP | PERF_EVENT_PROCESS_CREATE - | PERF_EVENT_PROCESS_EXEC | PERF_EVENT_PROCESS_EXIT | PERF_EVENT_THREAD_CREATE | PERF_EVENT_THREAD_EXIT; - - if (profiling_enable(pid, event_mask) < 0) { - int saved_errno = errno; - GUI::MessageBox::show(nullptr, ByteString::formatted("Unable to profile process {}({}): {}", process_name, pid, strerror(saved_errno)), "Profiler"sv, GUI::MessageBox::Type::Error); - return false; - } - - if (!prompt_to_stop_profiling(pid, process_name)) - return false; - - if (profiling_disable(pid) < 0) { - return false; - } - - return true; -} diff --git a/Userland/DevTools/SQLStudio/CMakeLists.txt b/Userland/DevTools/SQLStudio/CMakeLists.txt deleted file mode 100644 index 0e3c301e15d..00000000000 --- a/Userland/DevTools/SQLStudio/CMakeLists.txt +++ /dev/null @@ -1,17 +0,0 @@ -serenity_component( - SQLStudio - RECOMMENDED - TARGETS SQLStudio -) - -compile_gml(SQLStudio.gml SQLStudioGML.cpp) - -set(SOURCES - main.cpp - MainWidget.cpp - ScriptEditor.cpp - SQLStudioGML.cpp -) - -serenity_app(SQLStudio ICON app-sql-studio) -target_link_libraries(SQLStudio PRIVATE LibCore LibDesktop LibFileSystem LibGfx LibGUI LibIPC LibMain LibSQL LibSyntax LibURL) diff --git a/Userland/DevTools/SQLStudio/MainWidget.cpp b/Userland/DevTools/SQLStudio/MainWidget.cpp deleted file mode 100644 index a757fe155b8..00000000000 --- a/Userland/DevTools/SQLStudio/MainWidget.cpp +++ /dev/null @@ -1,571 +0,0 @@ -/* - * Copyright (c) 2022, Dylan Katz - * Copyright (c) 2022, Tim Flynn - * Copyright (c) 2023, Cameron Youell - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "MainWidget.h" -#include "ScriptEditor.h" - -REGISTER_WIDGET(SQLStudio, MainWidget); - -namespace SQLStudio { - -static Vector lookup_database_names() -{ - static constexpr auto database_extension = ".db"sv; - - auto database_path = ByteString::formatted("{}/sql", Core::StandardPaths::data_directory()); - if (!FileSystem::exists(database_path)) - return {}; - - Core::DirIterator iterator(move(database_path), Core::DirIterator::SkipParentAndBaseDir); - Vector database_names; - - while (iterator.has_next()) { - if (auto database = iterator.next_path(); database.ends_with(database_extension)) - database_names.append(database.substring(0, database.length() - database_extension.length())); - } - - return database_names; -} - -ErrorOr MainWidget::initialize() -{ - m_new_action = GUI::Action::create("&New", { Mod_Ctrl, Key_N }, TRY(Gfx::Bitmap::load_from_file("/res/icons/16x16/new.png"sv)), [this](auto&) { - open_new_script(); - }); - - m_open_action = GUI::CommonActions::make_open_action([&](auto&) { - if (auto result = GUI::FilePicker::get_open_filepath(window()); result.has_value()) - open_script_from_file(LexicalPath { result.release_value() }); - }); - - m_save_action = GUI::CommonActions::make_save_action([&](auto&) { - auto* editor = active_editor(); - VERIFY(editor); - - if (auto result = editor->save(); result.is_error()) - GUI::MessageBox::show_error(window(), ByteString::formatted("Failed to save {}\n{}", editor->path(), result.error())); - }); - - m_save_as_action = GUI::CommonActions::make_save_as_action([&](auto&) { - auto* editor = active_editor(); - VERIFY(editor); - - if (auto result = editor->save_as(); result.is_error()) - GUI::MessageBox::show_error(window(), ByteString::formatted("Failed to save {}\n{}", editor->path(), result.error())); - }); - - m_save_all_action = GUI::Action::create("Save All", { Mod_Ctrl | Mod_Alt, Key_S }, [this](auto&) { - auto* editor = active_editor(); - VERIFY(editor); - - m_tab_widget->for_each_child_widget([&](auto& child) { - auto& editor = verify_cast(child); - m_tab_widget->set_active_widget(&editor); - - if (auto result = editor.save(); result.is_error()) { - GUI::MessageBox::show_error(window(), ByteString::formatted("Failed to save {}\n{}", editor.path(), result.error())); - return IterationDecision::Break; - } else if (!result.value()) { - return IterationDecision::Break; - } - - return IterationDecision::Continue; - }); - - m_tab_widget->set_active_widget(editor); - }); - - m_copy_action = GUI::CommonActions::make_copy_action([&](auto&) { - auto* editor = active_editor(); - VERIFY(editor); - - editor->copy_action().activate(); - update_editor_actions(editor); - }); - - m_cut_action = GUI::CommonActions::make_cut_action([&](auto&) { - auto* editor = active_editor(); - VERIFY(editor); - - editor->cut_action().activate(); - update_editor_actions(editor); - }); - - m_paste_action = GUI::CommonActions::make_paste_action([&](auto&) { - auto* editor = active_editor(); - VERIFY(editor); - - editor->paste_action().activate(); - update_editor_actions(editor); - }); - - m_undo_action = GUI::CommonActions::make_undo_action([&](auto&) { - auto* editor = active_editor(); - VERIFY(editor); - - editor->document().undo(); - update_editor_actions(editor); - }); - - m_redo_action = GUI::CommonActions::make_redo_action([&](auto&) { - auto* editor = active_editor(); - VERIFY(editor); - - editor->document().redo(); - update_editor_actions(editor); - }); - - m_connect_to_database_action = GUI::Action::create("Connect to Database"sv, { Mod_Alt, Key_C }, TRY(Gfx::Bitmap::load_from_file("/res/icons/16x16/go-forward.png"sv)), [this](auto&) { - auto database_name = m_databases_combo_box->text().trim_whitespace(); - if (database_name.is_empty()) - return; - - m_run_script_action->set_enabled(false); - m_statusbar->set_text(1, "Disconnected"_string); - - if (m_connection_id.has_value()) { - m_sql_client->disconnect(*m_connection_id); - m_connection_id.clear(); - } - - if (auto connection_id = m_sql_client->connect(database_name); connection_id.has_value()) { - m_statusbar->set_text(1, String::formatted("Connected to: {}", database_name).release_value_but_fixme_should_propagate_errors()); - m_connection_id = *connection_id; - m_run_script_action->set_enabled(true); - } else { - GUI::MessageBox::show_error(window(), ByteString::formatted("Could not connect to {}", database_name)); - } - }); - - m_run_script_action = GUI::Action::create("Run Script", { Mod_Alt, Key_F9 }, TRY(Gfx::Bitmap::load_from_file("/res/icons/16x16/play.png"sv)), [&](auto&) { - m_results.clear(); - m_current_line_for_parsing = 0; - read_next_sql_statement_of_editor(); - }); - m_run_script_action->set_enabled(false); - - static auto database_names = lookup_database_names(); - m_databases_combo_box = GUI::ComboBox::construct(); - m_databases_combo_box->set_editor_placeholder("Enter new database or select existing database"sv); - m_databases_combo_box->set_max_width(font().width(m_databases_combo_box->editor_placeholder()) + font().max_glyph_width() + 16); - m_databases_combo_box->set_model(*GUI::ItemListModel::create(database_names)); - m_databases_combo_box->on_return_pressed = [this]() { - m_connect_to_database_action->activate(m_databases_combo_box); - }; - - auto& toolbar = *find_descendant_of_type_named("toolbar"sv); - toolbar.add_action(*m_new_action); - toolbar.add_action(*m_open_action); - toolbar.add_action(*m_save_action); - toolbar.add_action(*m_save_as_action); - toolbar.add_separator(); - toolbar.add_action(*m_copy_action); - toolbar.add_action(*m_cut_action); - toolbar.add_action(*m_paste_action); - toolbar.add_separator(); - toolbar.add_action(*m_undo_action); - toolbar.add_action(*m_redo_action); - toolbar.add_separator(); - toolbar.add_child(*m_databases_combo_box); - toolbar.add_action(*m_connect_to_database_action); - toolbar.add_separator(); - toolbar.add_action(*m_run_script_action); - - m_tab_widget = find_descendant_of_type_named("script_tab_widget"sv); - - m_tab_widget->on_tab_close_click = [&](auto& widget) { - auto& editor = verify_cast(widget); - - if (auto result = editor.attempt_to_close(); result.is_error()) { - GUI::MessageBox::show_error(window(), ByteString::formatted("Failed to save {}\n{}", editor.path(), result.error())); - } else if (result.value()) { - m_tab_widget->remove_tab(editor); - update_title(); - on_editor_change(); - } - }; - - m_tab_widget->on_change = [&](auto&) { - update_title(); - on_editor_change(); - }; - - m_action_tab_widget = find_descendant_of_type_named("action_tab_widget"sv); - - m_query_results_widget = m_action_tab_widget->add_tab("Results"_string); - m_query_results_widget->set_layout(6); - m_query_results_table_view = m_query_results_widget->add(); - - m_action_tab_widget->on_tab_close_click = [this](auto&) { - m_action_tab_widget->set_visible(false); - }; - - m_statusbar = find_descendant_of_type_named("statusbar"sv); - m_statusbar->segment(1).set_mode(GUI::Statusbar::Segment::Mode::Auto); - m_statusbar->set_text(1, "Disconnected"_string); - m_statusbar->segment(2).set_mode(GUI::Statusbar::Segment::Mode::Fixed); - m_statusbar->segment(2).set_fixed_width(font().width("Ln 0,000 Col 000"sv) + font().max_glyph_width()); - - GUI::Application::the()->on_action_enter = [this](GUI::Action& action) { - m_statusbar->set_override_text(action.status_tip()); - }; - - GUI::Application::the()->on_action_leave = [this](GUI::Action&) { - m_statusbar->set_override_text({}); - }; - - m_sql_client = TRY(SQL::SQLClient::try_create()); - m_sql_client->on_execution_success = [this](auto result) { - m_result_column_names = move(result.column_names); - read_next_sql_statement_of_editor(); - }; - m_sql_client->on_execution_error = [this](auto result) { - auto* editor = active_editor(); - VERIFY(editor); - - GUI::MessageBox::show_error(window(), ByteString::formatted("Error executing {}\n{}", editor->path(), result.error_message)); - }; - m_sql_client->on_next_result = [this](auto result) { - m_results.append({}); - m_results.last().ensure_capacity(result.values.size()); - - for (auto const& value : result.values) - m_results.last().unchecked_append(value.to_byte_string()); - }; - m_sql_client->on_results_exhausted = [this](auto) { - if (m_results.size() == 0) - return; - if (m_results[0].size() == 0) - return; - - Vector query_result_fields; - for (auto& column_name : m_result_column_names) - query_result_fields.empend(column_name, String::from_byte_string(column_name).release_value_but_fixme_should_propagate_errors(), Gfx::TextAlignment::CenterLeft); - - auto query_results_model = GUI::JsonArrayModel::create("{}", move(query_result_fields)); - m_query_results_table_view->set_model(MUST(GUI::SortingProxyModel::create(*query_results_model))); - for (auto& result_row : m_results) { - Vector individual_result_as_json; - for (auto& result_row_column : result_row) - individual_result_as_json.append(result_row_column); - MUST(query_results_model->add(move(individual_result_as_json))); - } - m_action_tab_widget->set_visible(true); - }; - - return {}; -} - -ErrorOr MainWidget::initialize_menu(GUI::Window* window) -{ - auto file_menu = window->add_menu("&File"_string); - file_menu->add_action(*m_new_action); - file_menu->add_action(*m_open_action); - file_menu->add_action(*m_save_action); - file_menu->add_action(*m_save_as_action); - file_menu->add_action(*m_save_all_action); - file_menu->add_separator(); - file_menu->add_action(GUI::CommonActions::make_quit_action([](auto&) { - GUI::Application::the()->quit(); - })); - - auto edit_menu = window->add_menu("&Edit"_string); - edit_menu->add_action(*m_copy_action); - edit_menu->add_action(*m_cut_action); - edit_menu->add_action(*m_paste_action); - edit_menu->add_separator(); - edit_menu->add_action(*m_undo_action); - edit_menu->add_action(*m_redo_action); - edit_menu->add_separator(); - edit_menu->add_action(*m_run_script_action); - - auto view_menu = window->add_menu("&View"_string); - view_menu->add_action(GUI::CommonActions::make_fullscreen_action([window](auto&) { - window->set_fullscreen(!window->is_fullscreen()); - })); - - auto help_menu = window->add_menu("&Help"_string); - help_menu->add_action(GUI::CommonActions::make_command_palette_action(window)); - help_menu->add_action(GUI::CommonActions::make_help_action([](auto&) { - Desktop::Launcher::open(URL::create_with_file_scheme("/usr/share/man/man1/Applications/SQLStudio.md"), "/bin/Help"); - })); - help_menu->add_action(GUI::CommonActions::make_about_action("SQL Studio"_string, GUI::Icon::default_icon("app-sql-studio"sv), window)); - return {}; -} - -void MainWidget::open_new_script() -{ - auto new_script_name = ByteString::formatted("New Script - {}", m_new_script_counter); - ++m_new_script_counter; - - auto& editor = m_tab_widget->add_tab(String::from_byte_string(new_script_name).release_value_but_fixme_should_propagate_errors()); - editor.new_script_with_temp_name(new_script_name); - - editor.on_cursor_change = [this] { on_editor_change(); }; - editor.on_selection_change = [this] { on_editor_change(); }; - editor.on_highlighter_change = [this] { on_editor_change(); }; - - m_tab_widget->set_active_widget(&editor); -} - -void MainWidget::open_script_from_file(LexicalPath const& file_path) -{ - auto& editor = m_tab_widget->add_tab(String::from_utf8(file_path.title()).release_value_but_fixme_should_propagate_errors()); - - if (auto result = editor.open_script_from_file(file_path); result.is_error()) { - GUI::MessageBox::show_error(window(), ByteString::formatted("Failed to open {}\n{}", file_path, result.error())); - return; - } - - editor.on_cursor_change = [this] { on_editor_change(); }; - editor.on_selection_change = [this] { on_editor_change(); }; - editor.on_highlighter_change = [this] { on_editor_change(); }; - - m_tab_widget->set_active_widget(&editor); -} - -bool MainWidget::request_close() -{ - auto any_scripts_modified { false }; - auto is_script_modified = [&](auto& child) { - auto& editor = verify_cast(child); - - if (editor.document().is_modified()) { - any_scripts_modified = true; - return IterationDecision::Break; - } - - return IterationDecision::Continue; - }; - - m_tab_widget->for_each_child_widget(is_script_modified); - if (!any_scripts_modified) - return true; - - auto result = GUI::MessageBox::ask_about_unsaved_changes(window(), {}); - switch (result) { - case GUI::Dialog::ExecResult::Yes: - break; - case GUI::Dialog::ExecResult::No: - return true; - default: - return false; - } - - m_save_all_action->activate(); - any_scripts_modified = false; - - m_tab_widget->for_each_child_widget(is_script_modified); - return !any_scripts_modified; -} - -ScriptEditor* MainWidget::active_editor() -{ - if (!m_tab_widget || !m_tab_widget->active_widget()) - return nullptr; - return verify_cast(m_tab_widget->active_widget()); -} - -void MainWidget::update_title() -{ - if (auto* editor = active_editor()) - window()->set_title(ByteString::formatted("{} - SQL Studio", editor->name())); - else - window()->set_title("SQL Studio"); -} - -void MainWidget::on_editor_change() -{ - auto* editor = active_editor(); - update_statusbar(editor); - update_editor_actions(editor); -} - -void MainWidget::update_statusbar(ScriptEditor* editor) -{ - if (!editor) { - m_statusbar->set_text(0, {}); - m_statusbar->set_text(2, {}); - return; - } - - StringBuilder builder; - if (editor->has_selection()) { - auto character_count = editor->selected_text().length(); - auto word_count = editor->number_of_selected_words(); - builder.appendff("Selected: {:'d} {} ({:'d} {})", character_count, character_count == 1 ? "character" : "characters", word_count, word_count != 1 ? "words" : "word"); - } - - m_statusbar->set_text(0, builder.to_string().release_value_but_fixme_should_propagate_errors()); - m_statusbar->set_text(2, String::formatted("Ln {:'d} Col {:'d}", editor->cursor().line() + 1, editor->cursor().column()).release_value_but_fixme_should_propagate_errors()); -} - -void MainWidget::update_editor_actions(ScriptEditor* editor) -{ - if (!editor) { - m_save_action->set_enabled(false); - m_save_as_action->set_enabled(false); - m_save_all_action->set_enabled(false); - m_run_script_action->set_enabled(false); - - m_copy_action->set_enabled(false); - m_cut_action->set_enabled(false); - m_paste_action->set_enabled(false); - m_undo_action->set_enabled(false); - m_redo_action->set_enabled(false); - - return; - } - - m_save_action->set_enabled(true); - m_save_as_action->set_enabled(true); - m_save_all_action->set_enabled(true); - m_run_script_action->set_enabled(m_connection_id.has_value()); - - m_copy_action->set_enabled(editor->copy_action().is_enabled()); - m_cut_action->set_enabled(editor->cut_action().is_enabled()); - m_paste_action->set_enabled(editor->paste_action().is_enabled()); - m_undo_action->set_enabled(editor->undo_action().is_enabled()); - m_redo_action->set_enabled(editor->redo_action().is_enabled()); -} - -void MainWidget::drag_enter_event(GUI::DragEvent& event) -{ - auto const& mime_types = event.mime_types(); - if (mime_types.contains_slow("text/uri-list"sv)) - event.accept(); -} - -void MainWidget::drop_event(GUI::DropEvent& drop_event) -{ - drop_event.accept(); - window()->move_to_front(); - - if (drop_event.mime_data().has_urls()) { - auto urls = drop_event.mime_data().urls(); - if (urls.is_empty()) - return; - - for (auto& url : urls) { - auto& scheme = url.scheme(); - if (!scheme.bytes_as_string_view().equals_ignoring_ascii_case("file"sv)) - continue; - - auto lexical_path = LexicalPath(url.serialize_path()); - open_script_from_file(lexical_path); - } - } -} - -void MainWidget::read_next_sql_statement_of_editor() -{ - if (!m_connection_id.has_value()) - return; - - StringBuilder piece; - do { - if (!piece.is_empty()) - piece.append('\n'); - - auto line_maybe = read_next_line_of_editor(); - - if (!line_maybe.has_value()) - return; - - auto& line = line_maybe.value(); - auto lexer = SQL::AST::Lexer(line); - - piece.append(line); - - bool is_first_token = true; - bool is_command = false; - bool last_token_ended_statement = false; - bool tokens_found = false; - - for (SQL::AST::Token token = lexer.next(); token.type() != SQL::AST::TokenType::Eof; token = lexer.next()) { - tokens_found = true; - switch (token.type()) { - case SQL::AST::TokenType::ParenOpen: - ++m_editor_line_level; - break; - case SQL::AST::TokenType::ParenClose: - --m_editor_line_level; - break; - case SQL::AST::TokenType::SemiColon: - last_token_ended_statement = true; - break; - case SQL::AST::TokenType::Period: - if (is_first_token) - is_command = true; - break; - default: - last_token_ended_statement = is_command; - break; - } - - is_first_token = false; - } - - if (tokens_found) - m_editor_line_level = last_token_ended_statement ? 0 : (m_editor_line_level > 0 ? m_editor_line_level : 1); - } while ((m_editor_line_level > 0) || piece.is_empty()); - - auto sql_statement = piece.to_byte_string(); - - if (auto statement_id = m_sql_client->prepare_statement(*m_connection_id, sql_statement); statement_id.has_value()) { - m_sql_client->async_execute_statement(*statement_id, {}); - } else { - auto* editor = active_editor(); - VERIFY(editor); - - GUI::MessageBox::show_error(window(), ByteString::formatted("Could not parse {}\n{}", editor->path(), sql_statement)); - } -} - -Optional MainWidget::read_next_line_of_editor() -{ - auto* editor = active_editor(); - if (!editor) - return {}; - - if (m_current_line_for_parsing >= editor->document().line_count()) - return {}; - - auto result = editor->document().line(m_current_line_for_parsing).to_utf8(); - ++m_current_line_for_parsing; - return result; -} - -} diff --git a/Userland/DevTools/SQLStudio/MainWidget.h b/Userland/DevTools/SQLStudio/MainWidget.h deleted file mode 100644 index 1053189c4a6..00000000000 --- a/Userland/DevTools/SQLStudio/MainWidget.h +++ /dev/null @@ -1,80 +0,0 @@ -/* - * Copyright (c) 2022, Dylan Katz - * Copyright (c) 2022, Tim Flynn - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include -#include - -namespace SQLStudio { - -class ScriptEditor; - -class MainWidget : public GUI::Widget { - C_OBJECT_ABSTRACT(MainWidget) - -public: - virtual ~MainWidget() = default; - static ErrorOr> try_create(); - ErrorOr initialize(); - - ErrorOr initialize_menu(GUI::Window*); - void open_new_script(); - void open_script_from_file(LexicalPath const&); - - bool request_close(); - -private: - ScriptEditor* active_editor(); - - void update_title(); - void on_editor_change(); - void update_statusbar(ScriptEditor*); - void update_editor_actions(ScriptEditor*); - - virtual void drag_enter_event(GUI::DragEvent&) override; - virtual void drop_event(GUI::DropEvent&) override; - - RefPtr m_new_action; - RefPtr m_open_action; - RefPtr m_save_action; - RefPtr m_save_as_action; - RefPtr m_save_all_action; - RefPtr m_copy_action; - RefPtr m_cut_action; - RefPtr m_paste_action; - RefPtr m_undo_action; - RefPtr m_redo_action; - RefPtr m_connect_to_database_action; - RefPtr m_run_script_action; - - int m_new_script_counter { 1 }; - RefPtr m_databases_combo_box; - RefPtr m_tab_widget; - RefPtr m_statusbar; - RefPtr m_action_tab_widget; - RefPtr m_query_results_widget; - RefPtr m_query_results_table_view; - - RefPtr m_sql_client; - Optional m_connection_id; - Vector m_result_column_names; - Vector> m_results; - - void read_next_sql_statement_of_editor(); - Optional read_next_line_of_editor(); - size_t m_current_line_for_parsing { 0 }; - int m_editor_line_level { 0 }; -}; - -} - -using SQLStudio::MainWidget; diff --git a/Userland/DevTools/SQLStudio/SQLStudio.gml b/Userland/DevTools/SQLStudio/SQLStudio.gml deleted file mode 100644 index 0cb51c8eb4b..00000000000 --- a/Userland/DevTools/SQLStudio/SQLStudio.gml +++ /dev/null @@ -1,29 +0,0 @@ -@SQLStudio::MainWidget { - layout: @GUI::VerticalBoxLayout {} - fill_with_background_color: true - - @GUI::ToolbarContainer { - @GUI::Toolbar { - name: "toolbar" - } - } - - @GUI::VerticalSplitter { - @GUI::TabWidget { - name: "script_tab_widget" - reorder_allowed: true - close_button_enabled: true - } - - @GUI::TabWidget { - name: "action_tab_widget" - close_button_enabled: true - visible: false - } - } - - @GUI::Statusbar { - name: "statusbar" - segment_count: 3 - } -} diff --git a/Userland/DevTools/SQLStudio/ScriptEditor.cpp b/Userland/DevTools/SQLStudio/ScriptEditor.cpp deleted file mode 100644 index 25d0b112bdd..00000000000 --- a/Userland/DevTools/SQLStudio/ScriptEditor.cpp +++ /dev/null @@ -1,97 +0,0 @@ -/* - * Copyright (c) 2022, Dylan Katz - * Copyright (c) 2022, Tim Flynn - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include - -#include "ScriptEditor.h" - -namespace SQLStudio { - -ScriptEditor::ScriptEditor() -{ - set_syntax_highlighter(make()); - set_ruler_visible(true); -} - -void ScriptEditor::new_script_with_temp_name(ByteString name) -{ - set_name(name); -} - -ErrorOr ScriptEditor::open_script_from_file(LexicalPath const& file_path) -{ - auto file = TRY(Core::File::open(file_path.string(), Core::File::OpenMode::Read)); - auto buffer = TRY(file->read_until_eof()); - - set_text({ buffer.bytes() }); - m_path = file_path.string(); - set_name(file_path.title()); - return {}; -} - -static ErrorOr save_text_to_file(StringView filename, ByteString text) -{ - auto file = TRY(Core::File::open(filename, Core::File::OpenMode::Write)); - - if (!text.is_empty()) - TRY(file->write_until_depleted(text.bytes())); - - return {}; -} - -ErrorOr ScriptEditor::save() -{ - if (m_path.is_empty()) - return save_as(); - - TRY(save_text_to_file(m_path, text())); - document().set_unmodified(); - return true; -} - -ErrorOr ScriptEditor::save_as() -{ - auto maybe_save_path = GUI::FilePicker::get_save_filepath(window(), name(), "sql"); - if (!maybe_save_path.has_value()) - return false; - - auto save_path = maybe_save_path.release_value(); - TRY(save_text_to_file(save_path, text())); - m_path = save_path; - - auto lexical_path = LexicalPath(save_path); - set_name(lexical_path.title()); - - auto parent = static_cast(parent_widget()); - if (parent) - parent->set_tab_title(*this, String::from_utf8(lexical_path.title()).release_value_but_fixme_should_propagate_errors()); - - document().set_unmodified(); - return true; -} - -ErrorOr ScriptEditor::attempt_to_close() -{ - if (!document().is_modified()) - return true; - - auto result = GUI::MessageBox::ask_about_unsaved_changes(window(), m_path.is_empty() ? name() : m_path, document().undo_stack().last_unmodified_timestamp()); - switch (result) { - case GUI::Dialog::ExecResult::Yes: - return save(); - case GUI::Dialog::ExecResult::No: - return true; - default: - return false; - } -} - -} diff --git a/Userland/DevTools/SQLStudio/ScriptEditor.h b/Userland/DevTools/SQLStudio/ScriptEditor.h deleted file mode 100644 index 17e11beb571..00000000000 --- a/Userland/DevTools/SQLStudio/ScriptEditor.h +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright (c) 2022, Dylan Katz - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include - -namespace SQLStudio { - -class ScriptEditor : public GUI::TextEditor { - C_OBJECT(ScriptEditor) - -public: - virtual ~ScriptEditor() = default; - - void new_script_with_temp_name(ByteString); - ErrorOr open_script_from_file(LexicalPath const&); - - ErrorOr save(); - ErrorOr save_as(); - ErrorOr attempt_to_close(); - - ByteString const& path() const { return m_path; } - -private: - ScriptEditor(); - - ByteString m_path; -}; - -} diff --git a/Userland/DevTools/SQLStudio/main.cpp b/Userland/DevTools/SQLStudio/main.cpp deleted file mode 100644 index e984e2c8141..00000000000 --- a/Userland/DevTools/SQLStudio/main.cpp +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright (c) 2022, Dylan Katz - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include -#include - -#include "MainWidget.h" - -ErrorOr serenity_main(Main::Arguments arguments) -{ - StringView file_to_open; - - Core::ArgsParser args_parser; - args_parser.add_positional_argument(file_to_open, "Path to SQL script or DB", "file", Core::ArgsParser::Required::No); - args_parser.parse(arguments); - - auto app = TRY(GUI::Application::create(arguments)); - - auto app_icon = GUI::Icon::default_icon("app-sql-studio"sv); - - auto window = GUI::Window::construct(); - window->restore_size_and_position("SQLStudio"sv, "Window"sv, { { 640, 480 } }); - window->save_size_and_position_on_close("SQLStudio"sv, "Window"sv); - window->set_icon(app_icon.bitmap_for_size(16)); - window->set_title("SQL Studio"); - - auto main_widget = TRY(MainWidget::try_create()); - window->set_main_widget(main_widget); - TRY(main_widget->initialize_menu(window)); - - window->on_close_request = [&] { - if (main_widget->request_close()) - return GUI::Window::CloseRequestDecision::Close; - return GUI::Window::CloseRequestDecision::StayOpen; - }; - - if (!file_to_open.is_empty()) { - auto path = LexicalPath(file_to_open); - main_widget->open_script_from_file(path); - } else { - main_widget->open_new_script(); - } - - window->show(); - return app->exec(); -} diff --git a/Userland/DynamicLoader/CMakeLists.txt b/Userland/DynamicLoader/CMakeLists.txt deleted file mode 100644 index a04c473a4bb..00000000000 --- a/Userland/DynamicLoader/CMakeLists.txt +++ /dev/null @@ -1,23 +0,0 @@ -set(SOURCES - main.cpp - misc.cpp -) - -add_library(DynamicLoader_CompileOptions INTERFACE) -target_compile_definitions(DynamicLoader_CompileOptions INTERFACE NO_TLS _DYNAMIC_LOADER) -target_compile_options(DynamicLoader_CompileOptions INTERFACE -fno-rtti -fpie -ffunction-sections -fdata-sections) -target_link_options(DynamicLoader_CompileOptions INTERFACE -nolibc -nostdlib++ -nostartfiles -static-libgcc -fpie -Wl,--gc-sections) -target_link_options(DynamicLoader_CompileOptions INTERFACE -fno-sanitize=undefined) # Sanitizer runtime is linked in manually -add_dependencies(DynamicLoader_CompileOptions install_libc_headers) - -add_executable(Loader.so ${SOURCES}) - -target_link_libraries(Loader.so PRIVATE DynamicLoader_CompileOptions DynamicLoader_LibELF DynamicLoader_LibCoreArgsParser) -if (ENABLE_UNDEFINED_SANITIZER) - target_link_libraries(Loader.so PRIVATE DynamicLoader_LibUBSanitizer) -endif() -target_link_options(Loader.so PRIVATE LINKER:--no-dynamic-linker) -# Don't confuse the coverage results by instrumenting Loader -target_link_libraries(Loader.so PRIVATE NoCoverage) - -install(TARGETS Loader.so RUNTIME DESTINATION usr/lib/) diff --git a/Userland/DynamicLoader/main.cpp b/Userland/DynamicLoader/main.cpp deleted file mode 100644 index 6857aa5f44b..00000000000 --- a/Userland/DynamicLoader/main.cpp +++ /dev/null @@ -1,239 +0,0 @@ -/* - * Copyright (c) 2020, Itamar S. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -char* __static_environ[] = { nullptr }; // We don't get the environment without some libc workarounds.. -char** environ = __static_environ; -uintptr_t __stack_chk_guard = 0; - -extern "C" { - -[[noreturn]] void _invoke_entry(int argc, char** argv, char** envp, ELF::EntryPointFunction entry); - -static ErrorOr open_executable(StringView path) -{ - int rc = open(path.characters_without_null_termination(), O_RDONLY | O_EXEC); - if (rc < 0) - return Error::from_errno(errno); - int checked_fd = rc; - ArmedScopeGuard close_on_failure([checked_fd] { - close(checked_fd); - }); - - struct stat executable_stat = {}; - rc = fstat(checked_fd, &executable_stat); - if (rc < 0) - return Error::from_errno(errno); - if (!S_ISREG(executable_stat.st_mode)) { - if (S_ISDIR(executable_stat.st_mode)) - return Error::from_errno(EISDIR); - return Error::from_errno(EINVAL); - } - - close_on_failure.disarm(); - return checked_fd; -} - -static int print_loaded_libraries_callback(struct dl_phdr_info* info, size_t, void*) -{ - outln("{}", info->dlpi_name); - return 0; -} - -static int _main(int argc, char** argv, char** envp, bool is_secure) -{ - Vector arguments; - arguments.ensure_capacity(argc); - for (int i = 0; i < argc; ++i) - arguments.unchecked_append({ argv[i], strlen(argv[i]) }); - - bool flag_dry_run { false }; - bool flag_list_loaded_dependencies { false }; - Vector command; - StringView argv0; - Core::ArgsParser args_parser; - - args_parser.set_general_help("Run dynamically-linked ELF executables"); - args_parser.set_stop_on_first_non_option(true); - - if (LexicalPath::basename(arguments[0]) == "ldd"sv) { - flag_list_loaded_dependencies = true; - flag_dry_run = true; - } else { - args_parser.add_option(flag_dry_run, "Run in dry-run mode", "dry-run", 'd'); - args_parser.add_option(flag_list_loaded_dependencies, "List all loaded dependencies", "list", 'l'); - args_parser.add_option(argv0, "Run with custom argv0", "argv0", 'E', "custom argv0"); - } - args_parser.add_positional_argument(command, "Command to execute", "command"); - // NOTE: Don't use regular PrintUsageAndExit policy for ArgsParser, as it will simply - // fail with a nullptr-dereference as the LibC exit function is not suitable for usage - // in this piece of code. - if (!args_parser.parse(arguments.span(), Core::ArgsParser::FailureBehavior::PrintUsage)) - return 1; - - if (command.is_empty()) { - args_parser.print_usage(stderr, arguments[0]); - return 1; - } - - auto error_or_fd = open_executable(command[0]); - if (error_or_fd.is_error()) { - warnln("Loader.so: Loading {} failed: {}", command[0], strerror(error_or_fd.error().code())); - return 1; - } - - int main_program_fd = error_or_fd.release_value(); - ByteString main_program_path = command[0]; - - // NOTE: We need to extract the command with its arguments to be able - // to run the actual requested executable with the requested parameters - // from argv. - VERIFY(command.size() <= static_cast(argc)); - auto command_with_args = command.span(); - for (size_t index = 0; index < command.size(); index++) - argv[index] = const_cast(command_with_args[index].characters_without_null_termination()); - - if (!argv0.is_empty()) - argv[0] = const_cast(argv0.characters_without_null_termination()); - - auto entry_point = ELF::DynamicLinker::linker_main(move(main_program_path), main_program_fd, is_secure, envp); - if (flag_list_loaded_dependencies) - ELF::DynamicLinker::iterate_over_loaded_shared_objects(print_loaded_libraries_callback, nullptr); - if (flag_dry_run) - return 0; - _invoke_entry(command.size(), argv, envp, entry_point); - VERIFY_NOT_REACHED(); -} - -// The compiler expects a previous declaration -void _start(int, char**, char**) __attribute__((used)); -void _entry(int, char**, char**) __attribute__((used)); - -NAKED void _start(int, char**, char**) -{ -#if ARCH(AARCH64) - // Make sure backtrace computation stops here by setting FP and LR to 0. - // FIXME: The kernel should ensure that registers are zeroed on program start - asm( - "mov x29, 0\n" - "mov x30, 0\n" - "bl _entry\n"); -#elif ARCH(RISCV64) - asm( - "li fp, 0\n" - "li ra, 0\n" - "tail _entry@plt\n"); -#elif ARCH(X86_64) - asm( - "push $0\n" - "jmp _entry@plt\n"); -#else -# error "Unknown architecture" -#endif -} - -ALWAYS_INLINE static void optimizer_fence() -{ - asm("" ::: "memory"); -} - -[[gnu::no_stack_protector]] void _entry(int argc, char** argv, char** envp) -{ - char** env; - for (env = envp; *env; ++env) { - } - - auxv_t* auxvp = (auxv_t*)++env; - - bool at_random_found = false; - FlatPtr base_address = 0; - bool base_address_found = false; - - for (auxv_t* entry = auxvp; entry->a_type != AT_NULL; ++entry) { - if (entry->a_type == ELF::AuxiliaryValue::Random) { - at_random_found = true; - __stack_chk_guard = *reinterpret_cast(entry->a_un.a_ptr); - } else if (entry->a_type == ELF::AuxiliaryValue::BaseAddress) { - base_address_found = true; - base_address = entry->a_un.a_val; - } - } - VERIFY(at_random_found && base_address_found); - - // Make sure compiler won't move any functions calls above __stack_chk_guard initialization even - // if their definitions somehow become available. - optimizer_fence(); - - // We need to relocate ourselves. - // (these relocations seem to be generated because of our vtables) - if (!ELF::perform_relative_relocations(base_address)) { - syscall(SC_dbgputstr, "Unable to perform relative relocations!\n", 40); - VERIFY_NOT_REACHED(); - } - - // Similarly, make sure no non-offset-agnostic language features are used above this point. - optimizer_fence(); - - // Initialize the copy of libc included statically in Loader.so, - // initialization of the dynamic libc.so is done by the DynamicLinker - __libc_init(); - - int main_program_fd = -1; - ByteString main_program_path; - bool is_secure = false; - for (; auxvp->a_type != AT_NULL; ++auxvp) { - if (auxvp->a_type == ELF::AuxiliaryValue::ExecFileDescriptor) { - main_program_fd = auxvp->a_un.a_val; - } - if (auxvp->a_type == ELF::AuxiliaryValue::ExecFilename) { - main_program_path = (char const*)auxvp->a_un.a_ptr; - } - if (auxvp->a_type == ELF::AuxiliaryValue::Secure) { - is_secure = auxvp->a_un.a_val == 1; - } - } - - if (main_program_fd == -1) { - // Allow syscalls from our code since the kernel won't do that automatically for us if we - // were invoked directly. - Elf_Ehdr* header = reinterpret_cast(base_address); - Elf_Phdr* pheader = reinterpret_cast(base_address + header->e_phoff); - - for (size_t i = 0; i < header->e_phnum; ++i) { - auto const& segment = pheader[i]; - if (segment.p_type == PT_LOAD && (segment.p_flags & PF_X)) { - auto flags = VirtualMemoryRangeFlags::SyscallCode | VirtualMemoryRangeFlags::Immutable; - auto rc = syscall(Syscall::SC_annotate_mapping, segment.p_vaddr + base_address, flags); - VERIFY(rc == 0); - } - } - - // We've been invoked directly as an executable rather than as the - // ELF interpreter for some other binary. - int exit_status = _main(argc, argv, envp, is_secure); - _exit(exit_status); - } - - VERIFY(main_program_fd >= 0); - VERIFY(!main_program_path.is_empty()); - - auto entry_point = ELF::DynamicLinker::linker_main(move(main_program_path), main_program_fd, is_secure, envp); - _invoke_entry(argc, argv, envp, entry_point); - VERIFY_NOT_REACHED(); -} -} diff --git a/Userland/DynamicLoader/misc.cpp b/Userland/DynamicLoader/misc.cpp deleted file mode 100644 index 3bd22e2eecf..00000000000 --- a/Userland/DynamicLoader/misc.cpp +++ /dev/null @@ -1,18 +0,0 @@ -/* - * Copyright (c) 2020, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "misc.h" -#include - -extern "C" { -char const* __cxa_demangle(char const*, void*, void*, int*) -{ - dbgln("WARNING: __cxa_demangle not supported"); - return ""; -} - -void* __dso_handle __attribute__((__weak__)); -} diff --git a/Userland/DynamicLoader/misc.h b/Userland/DynamicLoader/misc.h deleted file mode 100644 index 104ebc0de32..00000000000 --- a/Userland/DynamicLoader/misc.h +++ /dev/null @@ -1,13 +0,0 @@ -/* - * Copyright (c) 2020, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -extern "C" { -char const* __cxa_demangle(char const*, void*, void*, int*); - -extern void* __dso_handle; -} diff --git a/Userland/Games/BrickGame/BrickGame.cpp b/Userland/Games/BrickGame/BrickGame.cpp deleted file mode 100644 index ba61cb1ac12..00000000000 --- a/Userland/Games/BrickGame/BrickGame.cpp +++ /dev/null @@ -1,703 +0,0 @@ -/* - * Copyright (c) 2022, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "BrickGame.h" -#include -#include -#include -#include -#include -#include -#include - -using Position = Gfx::Point; - -class Well final { -public: - using Row = u32; - - Well() - { - reset(); - } - - [[nodiscard]] static constexpr size_t number_of_columns() { return 10; } - - [[nodiscard]] static constexpr size_t number_of_rows() { return top_margin() + 18 + bottom_margin(); } - - [[nodiscard]] static constexpr size_t left_margin() { return margin_left; } - - [[nodiscard]] static constexpr size_t top_margin() { return margin_top; } - - [[nodiscard]] static constexpr size_t bottom_margin() { return margin_bottom; } - - [[nodiscard]] Row operator[](size_t i) const { return m_rows[i]; } - - Row& operator[](size_t i) { return m_rows[i]; } - - [[nodiscard]] bool operator[](Position pos) const - { - return (m_rows[pos.y()] & (1 << (31 - pos.x()))) != 0; - } - - void reset() - { - auto const rows = number_of_rows(); - for (size_t row_index = 0; row_index < rows; ++row_index) - m_rows[row_index] = s_empty_row; - - for (size_t row_index = rows - margin_bottom; row_index < rows; ++row_index) - m_rows[row_index] = s_full_row; - } - - size_t check_and_remove_full_rows() - { - size_t number_of_removed_rows {}; - auto current = int(number_of_rows() - bottom_margin()); - for (auto row { current - 1 }; row >= 0; --row) { - if (m_rows[row] == s_full_row) { - number_of_removed_rows += 1; - continue; - } - m_rows[--current] = m_rows[row]; - } - for (; current >= 0; --current) - m_rows[current] = s_empty_row; - return number_of_removed_rows; - } - -private: - static constexpr size_t column_count = 10; - static constexpr size_t row_count = 18; - - static constexpr size_t margin_left = 4; - static constexpr size_t margin_top = 1; - static constexpr size_t margin_right = 32 - margin_left - column_count; - static constexpr size_t margin_bottom = 4; - - static constexpr size_t total_row_count = row_count + margin_top + margin_bottom; - - // empty row looks like 0b1111'0000'0000'0011'1111'1111'1111'1111 - // note, we have margin on both sides to implement collision checking for - // block shapes. - static constexpr Row s_empty_row = ~((~(~0u << column_count)) << margin_right); - - // full row looks like 0b1111'1111'1111'1111'1111'1111'1111'1111 - static constexpr Row s_full_row = ~0; - - // A well is an array of rows, each row has 32 columns, each column is represented as a bit in the u32. - // First column has index 0, it is the most significant bit in the u32. - // An empty cell in the row is represented as a zero bit. - // For convenience of testing of block-wall collisions the well starts at the non-zero margin - // from top, left, right and bottom, i.e. it is surrounded with walls of specified width/height (margin). - // Note, that block-well collision testing is a simple and fast 'and' bit operation of well row bit contents and - // the shape row bits contents. - Array m_rows {}; // 0 is the topmost row in the well -}; - -class Block final { -public: - static constexpr size_t shape_size = 4; - - Block() = default; - Block(Block const&) = default; - Block& operator=(Block const&) = default; - - Block& rotate_left() - { - m_rotation = m_rotation == 0 ? number_of_rotations - 1 : m_rotation - 1; - return *this; - } - - Block& rotate_right() - { - m_rotation = m_rotation == number_of_rotations - 1 ? 0 : m_rotation + 1; - return *this; - } - - Block& move_left() - { - m_position = m_position.moved_left(1); - return *this; - } - - Block& move_right() - { - m_position = m_position.moved_right(1); - return *this; - } - - Block& move_down() - { - m_position = m_position.moved_down(1); - return *this; - } - - Block& move_to(Position pos) - { - m_position = pos; - return *this; - } - - Block& random_shape() - { - m_shape = get_random_uniform(number_of_shapes); - m_rotation = 0; - m_position = { 6, 0 }; - return *this; - } - - [[nodiscard]] bool operator[](Position pos) const - { - return (block_row(pos.y() - m_position.y()) & (1 << (31 - pos.x()))) != 0; - } - - [[nodiscard]] bool has_collision(Well const& well) const - { - for (size_t start_row = 0; start_row < shape_size; ++start_row) { - auto const row_index = start_row + m_position.y(); - if (row_index >= Well::number_of_rows() || (well[row_index] & block_row(start_row)) != 0) - return true; - } - return false; - } - - void place_into(Well& well) - { - for (size_t row_index = 0; row_index < shape_size; ++row_index) - well[m_position.y() + row_index] |= block_row(row_index); - } - - [[nodiscard]] bool dot_at(Position position) const - { - return ((shape_data_at(position.y()) & (1 << (3 - position.x()))) != 0); - } - -private: - static constexpr u8 number_of_shapes = 7; - static constexpr u8 number_of_rotations = 4; - - using Shape = u16; - using Row = u32; - - // Each shape is stored in one u16, each nibble is representing one shape row, highest nibble being the first row. - // Every shape has 4x4 dimension and there are 4 possible shape rotations. - static constexpr Shape s_shapes[number_of_shapes][number_of_rotations] = { - // Shape: I - { 0b0000'1111'0000'0000, 0b0010'0010'0010'0010, 0b0000'1111'0000'0000, - 0b0010'0010'0010'0010 }, - - // Shape: J - { 0b0000'0111'0001'0000, 0b0001'0001'0011'0000, 0b0000'0100'0111'0000, - 0b0011'0010'0010'0000 }, - - // Shape: L - { 0b0000'0111'0100'0000, 0b0110'0010'0010'0000, 0b0000'0001'0111'0000, - 0b0010'0010'0011'0000 }, - - // Shape: O - { 0b0000'0110'0110'0000, 0b0000'0110'0110'0000, 0b0000'0110'0110'0000, - 0b0000'0110'0110'0000 }, - - // Shape: S - { 0b0000'0011'0110'0000, 0b0100'0110'0010'0000, 0b0000'0011'0110'0000, - 0b0100'0110'0010'0000 }, - - // Shape: T - { 0b0000'0111'0010'0000, 0b0001'0011'0001'0000, 0b0000'0010'0111'0000, - 0b0100'0110'0100'0000 }, - - // Shape: Z - { 0b0000'0110'0011'0000, 0b0001'0011'0010'0000, 0b0000'0110'0011'0000, - 0b0001'0011'0010'0000 } - }; - - Position m_position {}; - u8 m_rotation {}; - u8 m_shape {}; - - [[nodiscard]] Row block_row(size_t row) const - { - return shape_data_at(row) << (32 - m_position.x() - shape_size); - } - - [[nodiscard]] Row shape_data_at(size_t row) const - { - switch (row) { - case 0: - return Row((s_shapes[m_shape][m_rotation] >> 12) & 0xf); - case 1: - return Row((s_shapes[m_shape][m_rotation] >> 8) & 0xf); - case 2: - return Row((s_shapes[m_shape][m_rotation] >> 4) & 0xf); - case 3: - return Row(s_shapes[m_shape][m_rotation] & 0xf); - default: - return Row {}; - } - } -}; - -class Bricks final { - -public: - enum class GameState { - Active, - Paused, - GameOver - }; - - // Game will request a UI update when any of these events occur: - // - score changes - // - level changes - // - current block position or rotation changes - // - any well row(s) state change - enum class RenderRequest { - SkipRender, - RequestUpdate - }; - - Bricks() { reset(); } - - [[nodiscard]] unsigned score() const { return m_score; } - - [[nodiscard]] unsigned level() const { return m_level; } - - [[nodiscard]] GameState state() const { return m_state; } - - void add_new_block() - { - m_block = m_next_block; - m_next_block.random_shape(); - m_state = m_block.has_collision(m_well) ? GameState::GameOver : GameState::Active; - } - - [[nodiscard]] Block const& next_block() const { return m_next_block; } - - [[nodiscard]] BrickGame::BoardSpace operator[](Position pos) const - { - if (m_well[pos] || m_block[pos]) - return BrickGame::BoardSpace::FullyOn; - if (m_shadow_hint_block[pos]) - return BrickGame::BoardSpace::ShadowHint; - return BrickGame::BoardSpace::Off; - } - - [[nodiscard]] RenderRequest rotate_left() { return set_current_block(Block(m_block).rotate_left()); } - - [[nodiscard]] RenderRequest rotate_right() { return set_current_block(Block(m_block).rotate_right()); } - - [[nodiscard]] RenderRequest move_left() { return set_current_block(Block(m_block).move_left()); } - - [[nodiscard]] RenderRequest move_right() { return set_current_block(Block(m_block).move_right()); } - - [[nodiscard]] RenderRequest move_down() - { - auto const block = Block(m_block).move_down(); - if (block.has_collision(m_well)) { - m_block.place_into(m_well); - check_and_remove_full_rows(); - add_new_block(); - update_shadow_hint_block(); - return RenderRequest::RequestUpdate; - } - m_block = block; - update_shadow_hint_block(); - return RenderRequest::RequestUpdate; - } - - [[nodiscard]] RenderRequest move_down_fast() - { - for (auto block = m_block;; block.move_down()) { - if (block.has_collision(m_well)) { - m_block.place_into(m_well); - check_and_remove_full_rows(); - add_new_block(); - break; - } - m_block = block; - } - update_shadow_hint_block(); - return RenderRequest::RequestUpdate; - } - - void update_shadow_hint_block() - { - for (auto block = m_block;; block.move_down()) { - if (block.has_collision(m_well)) - return; - m_shadow_hint_block = block; - } - } - - void toggle_pause() - { - switch (m_state) { - case GameState::Active: - m_state = GameState::Paused; - break; - case GameState::Paused: - m_state = GameState::Active; - break; - case GameState::GameOver: - break; - } - } - - [[nodiscard]] RenderRequest update() - { - auto const current_level { m_level }; - for (size_t i = 0; i < s_level_map.size(); i++) { - if (m_score < s_level_map[i].m_score) - break; - m_level = i; - } - auto const now { UnixDateTime::now() }; - auto const delay = s_level_map[m_level].m_delay; - if (now - m_last_update > delay) { - m_last_update = now; - return move_down(); - } - return current_level == m_level ? RenderRequest::SkipRender : RenderRequest::RequestUpdate; - } - - void reset() - { - m_level = 0; - m_score = 0; - m_well.reset(); - m_block.random_shape(); - m_next_block.random_shape(); - update_shadow_hint_block(); - m_last_update = UnixDateTime::now(); - m_state = GameState::Active; - } - -private: - Well m_well {}; - Block m_block {}; - Block m_next_block {}; - Block m_shadow_hint_block {}; - unsigned m_level {}; - unsigned m_score {}; - GameState m_state { GameState::GameOver }; - // FIXME: Should probably use a monotonic clock instead. - UnixDateTime m_last_update {}; - - struct LevelMap final { - unsigned const m_score; - Duration const m_delay; - }; - - static constexpr Array s_level_map = { - LevelMap { 0, Duration::from_milliseconds(38000 / 60) }, - LevelMap { 1000, Duration::from_milliseconds(34000 / 60) }, - LevelMap { 2000, Duration::from_milliseconds(29000 / 60) }, - LevelMap { 3000, Duration::from_milliseconds(25000 / 60) }, - LevelMap { 4000, Duration::from_milliseconds(22000 / 60) }, - LevelMap { 5000, Duration::from_milliseconds(18000 / 60) }, - LevelMap { 6000, Duration::from_milliseconds(15000 / 60) }, - LevelMap { 7000, Duration::from_milliseconds(11000 / 60) }, - LevelMap { 8000, Duration::from_milliseconds(7000 / 60) }, - LevelMap { 9000, Duration::from_milliseconds(5000 / 60) }, - LevelMap { 10000, Duration::from_milliseconds(4000 / 60) }, - LevelMap { 20000, Duration::from_milliseconds(3000 / 60) }, - LevelMap { 30000, Duration::from_milliseconds(2000 / 60) }, - LevelMap { 10000000, Duration::from_milliseconds(1000 / 60) } - }; - - [[nodiscard]] RenderRequest set_current_block(Block const& block) - { - if (!block.has_collision(m_well)) { - m_block = block; - update_shadow_hint_block(); - return RenderRequest::RequestUpdate; - } - return RenderRequest::SkipRender; - } - - RenderRequest check_and_remove_full_rows() - { - auto const number_of_removed_rows { m_well.check_and_remove_full_rows() }; - switch (number_of_removed_rows) { - case 0: - return RenderRequest::SkipRender; - case 1: - m_score += 40 * (m_level + 1); - break; - case 2: - m_score += 100 * (m_level + 1); - break; - case 3: - m_score += 300 * (m_level + 1); - break; - case 4: - m_score += 1200 * (m_level + 1); - break; - default: - VERIFY_NOT_REACHED(); - } - return RenderRequest::RequestUpdate; - } -}; - -BrickGame::BrickGame(StringView app_name) - : m_app_name { app_name } - , m_state { GameState::Idle } - , m_brick_game(make()) -{ - set_font(Gfx::FontDatabase::default_fixed_width_font().bold_variant()); - m_high_score = Config::read_i32(m_app_name, m_app_name, "HighScore"sv, 0); - reset(); -} - -void BrickGame::reset() -{ - m_state = GameState::Active; - m_brick_game->reset(); - stop_timer(); - start_timer(15); // 66.6ms - m_brick_game->add_new_block(); - // A new game must always succeed to start, otherwise it is not fun to play - VERIFY(m_brick_game->state() == Bricks::GameState::Active); - update(); -} - -void BrickGame::toggle_pause() -{ - m_brick_game->toggle_pause(); - update(); -} - -void BrickGame::set_show_shadow_hint(bool should_show) -{ - m_show_shadow_hint = should_show; - repaint(); -} - -bool BrickGame::show_shadow_hint() -{ - return m_show_shadow_hint; -} - -void BrickGame::timer_event(Core::TimerEvent&) -{ - switch (m_brick_game->state()) { - case Bricks::GameState::GameOver: - game_over(); - break; - case Bricks::GameState::Active: - if (m_brick_game->update() == Bricks::RenderRequest::RequestUpdate) - update(); - break; - case Bricks::GameState::Paused: - break; - } -} - -void BrickGame::keydown_event(GUI::KeyEvent& event) -{ - switch (event.key()) { - case KeyCode::Key_Escape: - case KeyCode::Key_P: - toggle_pause(); - return; - default: - break; - } - - if (m_brick_game->state() == Bricks::GameState::Paused) { - event.ignore(); - return; - } - - Bricks::RenderRequest render_request { Bricks::RenderRequest::SkipRender }; - switch (event.key()) { - case KeyCode::Key_A: - case KeyCode::Key_H: - case KeyCode::Key_Left: - render_request = m_brick_game->move_left(); - break; - case KeyCode::Key_D: - case KeyCode::Key_L: - case KeyCode::Key_Right: - render_request = m_brick_game->move_right(); - break; - case KeyCode::Key_W: - case KeyCode::Key_K: - case KeyCode::Key_Up: - render_request = m_brick_game->rotate_right(); - break; - case KeyCode::Key_E: - render_request = m_brick_game->rotate_left(); - break; - case KeyCode::Key_S: - case KeyCode::Key_Down: - render_request = m_brick_game->move_down(); - break; - case KeyCode::Key_Space: - render_request = m_brick_game->move_down_fast(); - break; - default: - event.ignore(); - break; - } - if (render_request == Bricks::RenderRequest::RequestUpdate) - update(); -} - -void BrickGame::paint_cell(GUI::Painter& painter, Gfx::IntRect rect, BrickGame::BoardSpace space) -{ - Color inside_color; - Color outside_color; - - switch (space) { - case BrickGame::BoardSpace::FullyOn: - inside_color = m_front_color; - outside_color = m_front_color; - break; - case BrickGame::BoardSpace::ShadowHint: - inside_color = m_shadow_color; - outside_color = m_show_shadow_hint ? m_hint_block_color : m_shadow_color; - break; - case BrickGame::BoardSpace::Off: - inside_color = m_shadow_color; - outside_color = m_shadow_color; - break; - } - - painter.draw_rect(rect, m_back_color); - rect.inflate(-1, -1, -1, -1); - painter.draw_rect(rect, outside_color); - painter.set_pixel(rect.top_left(), m_back_color); - painter.set_pixel(rect.bottom_left().moved_up(1), m_back_color); - painter.set_pixel(rect.top_right().moved_left(1), m_back_color); - painter.set_pixel(rect.bottom_right().translated(-1), m_back_color); - rect.inflate(-2, -2); - painter.draw_rect(rect, outside_color); - rect.inflate(-2, -2); - painter.draw_rect(rect, m_back_color); - rect.inflate(-2, -2); - painter.draw_rect(rect, m_back_color); - rect.inflate(-2, -2); - painter.fill_rect(rect, inside_color); -} - -void BrickGame::paint_sidebar_text(GUI::Painter& painter, int row, StringView text) -{ - auto const text_width = font().width_rounded_up(text); - auto const entire_area_rect { frame_inner_rect() }; - auto const margin = 4; - auto const rect { Gfx::IntRect { entire_area_rect.x() + entire_area_rect.width() - 116, - 2 * margin + entire_area_rect.y() + (font().pixel_size_rounded_up() + margin) * row, - text_width, font().pixel_size_rounded_up() } }; - painter.draw_text(rect, text, Gfx::TextAlignment::TopLeft, Color::Black); -} - -void BrickGame::paint_paused_text(GUI::Painter& painter) -{ - auto const paused_text = "Paused"sv; - auto const paused_text_width = font().width_rounded_up(paused_text); - auto const more_or_less_font_height = static_cast(font().pixel_size_rounded_up()); - auto const entire_area_rect { frame_inner_rect() }; - auto const margin = more_or_less_font_height * 2; - - auto pause_text_box = Gfx::IntRect({}, { paused_text_width + margin, more_or_less_font_height + margin }).centered_within(entire_area_rect); - painter.fill_rect(pause_text_box, m_front_color); - - pause_text_box.inflate(-2, -2); - painter.fill_rect(pause_text_box, m_back_color); - - painter.draw_text(frame_inner_rect(), paused_text, Gfx::TextAlignment::Center, Color::Black); -} - -ErrorOr BrickGame::paint_game(GUI::Painter& painter, Gfx::IntRect const& rect) -{ - painter.fill_rect(rect, m_back_color); - if (m_state == GameState::Active) { - // TODO: optimize repainting - painter.draw_rect(rect.inflated(-4, -4), m_front_color); - - auto const entire_area_rect { frame_inner_rect() }; - Gfx::IntRect well_rect { entire_area_rect }; - well_rect.inflate(0, -120, 0, 0); - well_rect.inflate(-4, -4); - painter.draw_rect(well_rect, m_front_color); - well_rect.inflate(-4, -4); - - auto const cell_size = Gfx::IntSize(well_rect.width() / Well::number_of_columns(), well_rect.height() / (Well::number_of_rows() - Well::top_margin() - Well::bottom_margin())); - auto cell_rect = [&](Position pos) { - return Gfx::IntRect { - well_rect.x() + pos.x() * cell_size.width(), - well_rect.y() + pos.y() * cell_size.height(), - cell_size.width() - 1, - cell_size.height() - 1 - }; - }; - - auto const number_of_columns = int(Well::number_of_columns()); - auto const number_of_rows = int(Well::number_of_rows() - Well::top_margin() - Well::bottom_margin()); - for (int row = 0; row < number_of_rows; ++row) - for (int col = 0; col < number_of_columns; ++col) { - auto const position = Position { col, row }; - auto const board_position = position.translated(int(Well::left_margin()), int(Well::top_margin())); - paint_cell(painter, cell_rect(position), (*m_brick_game)[board_position]); - } - - paint_sidebar_text(painter, 0, TRY(String::formatted("Score: {}", m_brick_game->score()))); - paint_sidebar_text(painter, 1, TRY(String::formatted("Level: {}", m_brick_game->level()))); - paint_sidebar_text(painter, 4, TRY(String::formatted("Hi-Score: {}", m_high_score))); - paint_sidebar_text(painter, 12, "Next:"sv); - - auto const hint_rect = Gfx::IntRect { - frame_inner_rect().x() + frame_inner_rect().width() - 105, - frame_inner_rect().y() + 200, - int(cell_size.width() * Block::shape_size), - int(cell_size.height() * Block::shape_size) - }; - - painter.draw_rect(hint_rect.inflated(4, 4), m_front_color); - - auto const dot_rect = Gfx::IntRect { hint_rect.x(), hint_rect.y(), cell_size.width() - 1, cell_size.height() - 1 }; - for (size_t y = 0; y < Block::shape_size; ++y) - for (size_t x = 0; x < Block::shape_size; ++x) - paint_cell(painter, - dot_rect.translated(int(x * cell_size.width()), int(y * cell_size.height())), - m_brick_game->next_block().dot_at({ x, y }) ? BrickGame::BoardSpace::FullyOn : BrickGame::BoardSpace::Off); - - if (m_brick_game->state() == Bricks::GameState::Paused) - paint_paused_text(painter); - } - - return {}; -} - -void BrickGame::paint_event(GUI::PaintEvent& event) -{ - GUI::Frame::paint_event(event); - GUI::Painter painter(*this); - painter.add_clip_rect(frame_inner_rect()); - painter.add_clip_rect(event.rect()); - paint_game(painter, frame_inner_rect()).release_value_but_fixme_should_propagate_errors(); -} - -void BrickGame::game_over() -{ - stop_timer(); - StringBuilder text; - auto const current_score = m_brick_game->score(); - text.appendff("Your score was {}", current_score); - if (current_score > m_high_score) { - text.append("\nThat's a new high score!"sv); - Config::write_i32(m_app_name, m_app_name, "HighScore"sv, int(m_high_score = current_score)); - } - GUI::MessageBox::show(window(), - text.string_view(), - "Game Over"sv, - GUI::MessageBox::Type::Information); - - reset(); -} diff --git a/Userland/Games/BrickGame/BrickGame.h b/Userland/Games/BrickGame/BrickGame.h deleted file mode 100644 index 0cf019d220b..00000000000 --- a/Userland/Games/BrickGame/BrickGame.h +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright (c) 2022, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include - -class Bricks; - -class BrickGame : public GUI::Frame { - C_OBJECT(BrickGame); - -public: - // How should a particular space on the board be presented to the user - enum class BoardSpace { - FullyOn, - ShadowHint, - Off - }; - - virtual ~BrickGame() override = default; - - void reset(); - void toggle_pause(); - void set_show_shadow_hint(bool); - bool show_shadow_hint(); - -private: - BrickGame(StringView app_name); - virtual void paint_event(GUI::PaintEvent&) override; - virtual void keydown_event(GUI::KeyEvent&) override; - virtual void timer_event(Core::TimerEvent&) override; - - void paint_sidebar_text(GUI::Painter&, int row, StringView); - void paint_paused_text(GUI::Painter&); - void paint_cell(GUI::Painter&, Gfx::IntRect, BoardSpace); - ErrorOr paint_game(GUI::Painter&, Gfx::IntRect const&); - void game_over(); - - enum class GameState { - Idle = 0, - Active - }; - - StringView const m_app_name; - GameState m_state {}; - NonnullOwnPtr m_brick_game; - unsigned m_high_score {}; - bool m_show_shadow_hint { true }; - - Color m_back_color { Color::from_rgb(0x8fbc8f) }; - Color m_front_color { Color::Black }; - Color m_shadow_color { Color::from_rgb(0x729672) }; - Color m_hint_block_color { Color::from_rgb(0x485e48) }; -}; diff --git a/Userland/Games/BrickGame/CMakeLists.txt b/Userland/Games/BrickGame/CMakeLists.txt deleted file mode 100644 index 0602a6335ed..00000000000 --- a/Userland/Games/BrickGame/CMakeLists.txt +++ /dev/null @@ -1,13 +0,0 @@ -serenity_component( - BrickGame - RECOMMENDED - TARGETS BrickGame -) - -set(SOURCES - main.cpp - BrickGame.cpp -) - -serenity_app(BrickGame ICON app-brickgame) -target_link_libraries(BrickGame PRIVATE LibGUI LibCore LibGfx LibConfig LibMain LibDesktop LibURL) diff --git a/Userland/Games/BrickGame/main.cpp b/Userland/Games/BrickGame/main.cpp deleted file mode 100644 index cfd42623939..00000000000 --- a/Userland/Games/BrickGame/main.cpp +++ /dev/null @@ -1,93 +0,0 @@ -/* - * Copyright (c) 2022, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "BrickGame.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -ErrorOr serenity_main(Main::Arguments arguments) -{ - TRY(Core::System::pledge("stdio rpath recvfd sendfd unix")); - - auto app = TRY(GUI::Application::create(arguments)); - - auto const app_name = "BrickGame"sv; - auto const title = "Brick Game"_string; - auto const man_file = "/usr/share/man/man6/BrickGame.md"sv; - - Config::pledge_domain(app_name); - - TRY(Desktop::Launcher::add_allowed_handler_with_only_specific_urls("/bin/Help", { URL::create_with_file_scheme(man_file) })); - TRY(Desktop::Launcher::seal_allowlist()); - - TRY(Core::System::pledge("stdio rpath recvfd sendfd")); - - TRY(Core::System::unveil("/tmp/session/%sid/portal/launch", "rw")); - TRY(Core::System::unveil("/res", "r")); - TRY(Core::System::unveil(nullptr, nullptr)); - - auto app_icon = TRY(GUI::Icon::try_create_default_icon("app-brickgame"sv)); - - auto window = GUI::Window::construct(); - - window->set_double_buffering_enabled(false); - window->set_title(title.bytes_as_string_view()); - window->resize(360, 462); - window->set_resizable(false); - - auto game = window->set_main_widget(app_name); - - auto game_menu = window->add_menu("&Game"_string); - - game_menu->add_action(GUI::Action::create("&New Game", { Mod_None, Key_F2 }, TRY(Gfx::Bitmap::load_from_file("/res/icons/16x16/reload.png"sv)), [&](auto&) { - game->reset(); - })); - game_menu->add_action(GUI::Action::create("Toggle &Pause", { Mod_None, Key_P }, [&](auto&) { - game->toggle_pause(); - })); - - auto show_shadow_piece_action = GUI::Action::create_checkable("Show Shadow Piece", GUI::Shortcut {}, [&](auto& action) { - game->set_show_shadow_hint(action.is_checked()); - Config::write_bool(app_name, app_name, "ShowShadowPiece"sv, action.is_checked()); - }); - game->set_show_shadow_hint(Config::read_bool(app_name, app_name, "ShowShadowPiece"sv, true)); - show_shadow_piece_action->set_checked(game->show_shadow_hint()); - - game_menu->add_action(show_shadow_piece_action); - game_menu->add_separator(); - game_menu->add_action(GUI::CommonActions::make_quit_action([](auto&) { - GUI::Application::the()->quit(); - })); - - auto view_menu = window->add_menu("&View"_string); - view_menu->add_action(GUI::CommonActions::make_fullscreen_action([&](auto&) { - window->set_fullscreen(!window->is_fullscreen()); - })); - - auto help_menu = window->add_menu("&Help"_string); - help_menu->add_action(GUI::CommonActions::make_command_palette_action(window)); - help_menu->add_action(GUI::CommonActions::make_help_action([&man_file](auto&) { - Desktop::Launcher::open(URL::create_with_file_scheme(man_file), "/bin/Help"); - })); - help_menu->add_action(GUI::CommonActions::make_about_action(title, app_icon, window)); - - window->show(); - - window->set_icon(app_icon.bitmap_for_size(16)); - - return app->exec(); -} diff --git a/Userland/Games/CMakeLists.txt b/Userland/Games/CMakeLists.txt deleted file mode 100644 index 894c095dc97..00000000000 --- a/Userland/Games/CMakeLists.txt +++ /dev/null @@ -1,13 +0,0 @@ -add_subdirectory(BrickGame) -add_subdirectory(Chess) -add_subdirectory(ColorLines) -add_subdirectory(FlappyBug) -add_subdirectory(Flood) -add_subdirectory(GameOfLife) -add_subdirectory(Hearts) -add_subdirectory(MasterWord) -add_subdirectory(Minesweeper) -add_subdirectory(Snake) -add_subdirectory(Solitaire) -add_subdirectory(Spider) -add_subdirectory(TwentyFourtyEight) diff --git a/Userland/Games/Chess/CMakeLists.txt b/Userland/Games/Chess/CMakeLists.txt deleted file mode 100644 index 1f483b6160c..00000000000 --- a/Userland/Games/Chess/CMakeLists.txt +++ /dev/null @@ -1,21 +0,0 @@ -serenity_component( - Chess - RECOMMENDED - TARGETS Chess - DEPENDS ChessEngine -) - -compile_gml(Chess.gml ChessGML.cpp chess_gml) -compile_gml(PromotionWidget.gml PromotionWidgetGML.cpp promotionWidget_gml) - -set(SOURCES - main.cpp - ChessWidget.cpp - PromotionDialog.cpp - Engine.cpp - ChessGML.cpp - PromotionWidgetGML.cpp -) - -serenity_app(Chess ICON app-chess) -target_link_libraries(Chess PRIVATE LibChess LibConfig LibFileSystemAccessClient LibGfx LibGUI LibCore LibMain LibDesktop LibURL) diff --git a/Userland/Games/Chess/Chess.gml b/Userland/Games/Chess/Chess.gml deleted file mode 100644 index d71821e7d5f..00000000000 --- a/Userland/Games/Chess/Chess.gml +++ /dev/null @@ -1,35 +0,0 @@ -@Chess::MainWidget { - fill_with_background_color: true - layout: @GUI::VerticalBoxLayout {} - - @GUI::HorizontalSplitter { - @GUI::Frame { - name: "chess_widget_frame" - min_width: 508 - min_height: 508 - layout: @GUI::HorizontalBoxLayout {} - - @Chess::ChessWidget { - name: "chess_widget" - } - } - - @GUI::Frame { - layout: @GUI::VerticalBoxLayout {} - - @GUI::Label { - text: "Moves" - text_alignment: "Center" - font_weight: "Bold" - fixed_height: 24 - name: "moves_display_widget_label" - } - - @GUI::TextEditor { - name: "move_display_widget" - mode: "DisplayOnly" - focus_policy: "NoFocus" - } - } - } -} diff --git a/Userland/Games/Chess/ChessWidget.cpp b/Userland/Games/Chess/ChessWidget.cpp deleted file mode 100644 index 374a4046d99..00000000000 --- a/Userland/Games/Chess/ChessWidget.cpp +++ /dev/null @@ -1,916 +0,0 @@ -/* - * Copyright (c) 2020-2022, the SerenityOS developers. - * Copyright (c) 2023, Tim Ledbetter - * Copyright (c) 2023, Sam Atkins - * Copyright (c) 2024, Daniel Gaston - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "ChessWidget.h" -#include "PromotionDialog.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace Chess { - -ErrorOr> ChessWidget::try_create() -{ - auto widget = TRY(AK::adopt_nonnull_ref_or_enomem(new (nothrow) ChessWidget)); - widget->set_piece_set("Classic"sv); - - return widget; -} - -void ChessWidget::paint_event(GUI::PaintEvent& event) -{ - int const min_size = min(width(), height()); - int const widget_offset_y = (window()->height() - min_size) / 2; - - GUI::Frame::paint_event(event); - - GUI::Painter painter(*this); - painter.add_clip_rect(event.rect()); - - painter.fill_rect(frame_inner_rect(), Gfx::Color::Black); - - painter.translate(frame_thickness(), frame_thickness() + widget_offset_y); - - auto square_width = min_size / 8; - auto square_height = min_size / 8; - auto square_margin = square_width / 10; - int coord_rank_file = (side() == Chess::Color::White) ? 0 : 7; - - Chess::Board& active_board = (m_playback ? board_playback() : board()); - - auto& coordinate_font = Gfx::FontDatabase::default_font().bold_variant(); - - Chess::Square::for_each([&](Chess::Square sq) { - Gfx::IntRect tile_rect; - if (side() == Chess::Color::White) { - tile_rect = { sq.file * square_width, (7 - sq.rank) * square_height, square_width, square_height }; - } else { - tile_rect = { (7 - sq.file) * square_width, sq.rank * square_height, square_width, square_height }; - } - - painter.fill_rect(tile_rect, (sq.is_light()) ? board_theme().light_square_color : board_theme().dark_square_color); - - if (active_board.last_move().has_value()) { - auto const last_move = active_board.last_move().value(); - if (last_move.to == sq || last_move.from == sq) - painter.fill_rect(tile_rect, m_move_highlight_color); - - auto const piece = active_board.get_piece(sq); - if (m_highlight_checks && last_move.is_check && piece.type == Chess::Type::King && piece.color == active_board.turn()) { - Array colors = { - Gfx::ColorStop { .color = Gfx::Color::Red, .position = 0.16f }, - Gfx::ColorStop { .color = Gfx::Color::Transparent, .position = .66f } - }; - - painter.fill_rect_with_radial_gradient(tile_rect, colors, tile_rect.center() - tile_rect.top_left(), tile_rect.size()); - } - } - - if (m_coordinates) { - auto text_color = (sq.is_light()) ? board_theme().dark_square_color : board_theme().light_square_color; - - auto shrunken_rect = tile_rect; - shrunken_rect.shrink(4, 4); - - if (sq.rank == coord_rank_file) { - auto file_char = sq.file_char(); - painter.draw_text(shrunken_rect, { &file_char, 1 }, coordinate_font, Gfx::TextAlignment::BottomRight, text_color); - } - - if (sq.file == coord_rank_file) { - auto rank_char = sq.rank_char(); - painter.draw_text(shrunken_rect, { &rank_char, 1 }, coordinate_font, Gfx::TextAlignment::TopLeft, text_color); - } - } - - for (auto& m : m_board_markings) { - if (m.type() == BoardMarking::Type::Square && m.from == sq) { - Gfx::Color color = m.secondary_color ? m_marking_secondary_color : (m.alternate_color ? m_marking_alternate_color : m_marking_primary_color); - painter.fill_rect(tile_rect, color); - } - } - - if (!(m_dragging_piece && sq == m_moving_square)) { - auto bmp = get_piece_graphic(active_board.get_piece(sq)); - if (bmp) { - painter.draw_scaled_bitmap(tile_rect.shrunken(square_margin, square_margin, square_margin, square_margin), *bmp, bmp->rect(), 1.0f, Gfx::Painter::ScalingMode::BilinearBlend); - } - } - - return IterationDecision::Continue; - }); - - auto draw_arrow = [&painter](Gfx::FloatPoint A, Gfx::FloatPoint B, float w1, float w2, float h, Gfx::Color color) { - float dx = B.x() - A.x(); - float dy = A.y() - B.y(); - float phi = atan2f(dy, dx); - float hdx = h * cosf(phi); - float hdy = h * sinf(phi); - - auto const cos_pi_2_phi = cosf(float { M_PI_2 } - phi); - auto const sin_pi_2_phi = sinf(float { M_PI_2 } - phi); - - Gfx::FloatPoint A1(A.x() - (w1 / 2) * cos_pi_2_phi, A.y() - (w1 / 2) * sin_pi_2_phi); - Gfx::FloatPoint B3(A.x() + (w1 / 2) * cos_pi_2_phi, A.y() + (w1 / 2) * sin_pi_2_phi); - Gfx::FloatPoint A2(A1.x() + (dx - hdx), A1.y() - (dy - hdy)); - Gfx::FloatPoint B2(B3.x() + (dx - hdx), B3.y() - (dy - hdy)); - Gfx::FloatPoint A3(A2.x() - w2 * cos_pi_2_phi, A2.y() - w2 * sin_pi_2_phi); - Gfx::FloatPoint B1(B2.x() + w2 * cos_pi_2_phi, B2.y() + w2 * sin_pi_2_phi); - - auto path = Gfx::Path(); - path.move_to(A); - path.line_to(A1); - path.line_to(A2); - path.line_to(A3); - path.line_to(B); - path.line_to(B1); - path.line_to(B2); - path.line_to(B3); - path.line_to(A); - path.close(); - - painter.fill_path(path, color, Gfx::Painter::WindingRule::EvenOdd); - }; - - for (auto& m : m_board_markings) { - if (m.type() == BoardMarking::Type::Arrow) { - Gfx::FloatPoint arrow_start; - Gfx::FloatPoint arrow_end; - - if (side() == Chess::Color::White) { - arrow_start = { m.from.file * square_width + square_width / 2.0f, (7 - m.from.rank) * square_height + square_height / 2.0f }; - arrow_end = { m.to.file * square_width + square_width / 2.0f, (7 - m.to.rank) * square_height + square_height / 2.0f }; - } else { - arrow_start = { (7 - m.from.file) * square_width + square_width / 2.0f, m.from.rank * square_height + square_height / 2.0f }; - arrow_end = { (7 - m.to.file) * square_width + square_width / 2.0f, m.to.rank * square_height + square_height / 2.0f }; - } - - Gfx::Color color = m.secondary_color ? m_marking_secondary_color : (m.alternate_color ? m_marking_primary_color : m_marking_alternate_color); - draw_arrow(arrow_start, arrow_end, square_width / 8.0f, square_width / 10.0f, square_height / 2.5f, color); - } - } - - if (m_dragging_piece) { - if (m_show_available_moves) { - Gfx::IntPoint move_point; - Gfx::IntPoint point_offset = { square_width / 3, square_height / 3 }; - Gfx::IntSize rect_size = { square_width / 3, square_height / 3 }; - for (auto const& square : m_available_moves) { - if (side() == Chess::Color::White) { - move_point = { square.file * square_width, (7 - square.rank) * square_height }; - } else { - move_point = { (7 - square.file) * square_width, square.rank * square_height }; - } - - Gfx::AntiAliasingPainter aa_painter { painter }; - aa_painter.fill_ellipse({ move_point + point_offset, rect_size }, Gfx::Color::LightGray); - } - } - - Gfx::IntRect origin_square; - if (side() == Chess::Color::White) { - origin_square = { m_moving_square.file * square_width, (7 - m_moving_square.rank) * square_height, square_width, square_height }; - } else { - origin_square = { (7 - m_moving_square.file) * square_width, m_moving_square.rank * square_height, square_width, square_height }; - } - painter.fill_rect(origin_square, m_move_highlight_color); - - auto bmp = get_piece_graphic(active_board.get_piece(m_moving_square)); - if (bmp) { - auto center = m_drag_point - Gfx::IntPoint(square_width / 2, square_height / 2); - painter.draw_scaled_bitmap({ center, { square_width, square_height } }, *bmp, bmp->rect(), 1.0f, Gfx::Painter::ScalingMode::BilinearBlend); - } - } - - if (m_any_piece_images_are_missing) { - auto warning_rect = rect(); - warning_rect.set_height(coordinate_font.preferred_line_height() + 4); - painter.fill_rect(warning_rect, palette().base()); - painter.draw_text(warning_rect.shrunken(4, 4), "Warning: This set is missing images for some pieces!"sv, coordinate_font, Gfx::TextAlignment::CenterLeft, palette().base_text()); - } -} - -void ChessWidget::mousedown_event(GUI::MouseEvent& event) -{ - int const min_size = min(width(), height()); - int const widget_offset_y = (window()->height() - min_size) / 2; - - if (!frame_inner_rect().contains(event.position())) - return; - - auto square = mouse_to_square(event); - if (event.button() == GUI::MouseButton::Secondary) { - if (m_dragging_piece) { - m_dragging_piece = false; - set_override_cursor(Gfx::StandardCursor::None); - m_available_moves.clear(); - } else if (square.has_value()) { - m_current_marking.from = square.release_value(); - } - return; - } - m_board_markings.clear(); - - if (!square.has_value()) - return; - - auto piece = board().get_piece(square.value()); - if (drag_enabled() && piece.color == board().turn() && !m_playback) { - m_dragging_piece = true; - set_override_cursor(Gfx::StandardCursor::Drag); - m_drag_point = { event.position().x(), event.position().y() - widget_offset_y }; - m_moving_square = square.value(); - - m_board.generate_moves([&](Chess::Move move) { - if (move.from == m_moving_square) { - m_available_moves.append(move.to); - } - return IterationDecision::Continue; - }); - } - - update(); -} - -void ChessWidget::mouseup_event(GUI::MouseEvent& event) -{ - if (!frame_inner_rect().contains(event.position())) - return; - - auto target_square = mouse_to_square(event); - if (event.button() == GUI::MouseButton::Secondary) { - if (!target_square.has_value()) - return; - - m_current_marking.secondary_color = event.shift(); - m_current_marking.alternate_color = event.ctrl(); - m_current_marking.to = target_square.release_value(); - auto match_index = m_board_markings.find_first_index(m_current_marking); - if (match_index.has_value()) { - m_board_markings.remove(match_index.value()); - update(); - return; - } - m_board_markings.append(m_current_marking); - update(); - return; - } - - if (!m_dragging_piece) - return; - - m_dragging_piece = false; - set_override_cursor(Gfx::StandardCursor::Hand); - m_available_moves.clear(); - - if (!target_square.has_value()) { - update(); - return; - } - - Chess::Move move = { m_moving_square, target_square.release_value() }; - if (board().is_promotion_move(move)) { - auto promotion_dialog = MUST(PromotionDialog::try_create(*this)); - if (promotion_dialog->exec() == PromotionDialog::ExecResult::OK) - move.promote_to = promotion_dialog->selected_piece(); - } - - if (board().apply_move(move)) { - update_move_display_widget(m_board); - m_playback_move_number = board().moves().size(); - m_playback = false; - m_board_playback = m_board; - // If two humans are playing, ask whether they wish to accept a draw. - auto claim_draw_behavior = m_engine.is_null() ? ClaimDrawBehavior::Prompt : ClaimDrawBehavior::Always; - if (!check_game_over(claim_draw_behavior)) - input_engine_move(); - } - - update(); -} - -void ChessWidget::mousemove_event(GUI::MouseEvent& event) -{ - int const min_size = min(width(), height()); - int const widget_offset_y = (window()->height() - min_size) / 2; - - if (!frame_inner_rect().contains(event.position())) - return; - - if (m_engine && board().turn() != side()) - return; - - if (!m_dragging_piece) { - auto square = mouse_to_square(event); - if (!square.has_value()) { - set_override_cursor(Gfx::StandardCursor::None); - return; - } - - auto piece = board().get_piece(square.release_value()); - if (piece.color == board().turn()) - set_override_cursor(Gfx::StandardCursor::Hand); - else - set_override_cursor(Gfx::StandardCursor::None); - return; - } - - m_drag_point = { event.position().x(), event.position().y() - widget_offset_y }; - update(); -} - -void ChessWidget::keydown_event(GUI::KeyEvent& event) -{ - set_override_cursor(Gfx::StandardCursor::None); - switch (event.key()) { - case KeyCode::Key_Left: - playback_move(PlaybackDirection::Backward); - break; - case KeyCode::Key_Right: - playback_move(PlaybackDirection::Forward); - break; - case KeyCode::Key_Up: - playback_move(PlaybackDirection::Last); - break; - case KeyCode::Key_Down: - playback_move(PlaybackDirection::First); - break; - case KeyCode::Key_Home: - playback_move(PlaybackDirection::First); - break; - case KeyCode::Key_End: - playback_move(PlaybackDirection::Last); - break; - default: - event.ignore(); - return; - } - update(); -} - -void ChessWidget::set_piece_set(StringView set) -{ - auto load_piece_image = [&](Chess::Color color, Chess::Type piece, StringView filename) { - auto path = MUST(String::formatted("/res/graphics/chess/sets/{}/{}", set, filename)); - auto image = Gfx::Bitmap::load_from_file(path.bytes_as_string_view()); - if (image.is_error()) { - m_any_piece_images_are_missing = true; - return; - } - m_pieces.set({ color, piece }, image.release_value()); - }; - - m_pieces.clear(); - m_any_piece_images_are_missing = false; - - load_piece_image(Chess::Color::White, Chess::Type::Pawn, "white-pawn.png"sv); - load_piece_image(Chess::Color::Black, Chess::Type::Pawn, "black-pawn.png"sv); - load_piece_image(Chess::Color::White, Chess::Type::Knight, "white-knight.png"sv); - load_piece_image(Chess::Color::Black, Chess::Type::Knight, "black-knight.png"sv); - load_piece_image(Chess::Color::White, Chess::Type::Bishop, "white-bishop.png"sv); - load_piece_image(Chess::Color::Black, Chess::Type::Bishop, "black-bishop.png"sv); - load_piece_image(Chess::Color::White, Chess::Type::Rook, "white-rook.png"sv); - load_piece_image(Chess::Color::Black, Chess::Type::Rook, "black-rook.png"sv); - load_piece_image(Chess::Color::White, Chess::Type::Queen, "white-queen.png"sv); - load_piece_image(Chess::Color::Black, Chess::Type::Queen, "black-queen.png"sv); - load_piece_image(Chess::Color::White, Chess::Type::King, "white-king.png"sv); - load_piece_image(Chess::Color::Black, Chess::Type::King, "black-king.png"sv); -} - -Optional ChessWidget::mouse_to_square(GUI::MouseEvent& event) const -{ - int const min_size = min(width(), height()); - int const widget_offset_y = (window()->height() - min_size) / 2; - - auto x = event.x(); - auto y = event.y() - widget_offset_y; - if (x < 0 || y < 0 || x > min_size || y > min_size) - return {}; - - int square_width = min_size / 8; - int square_height = min_size / 8; - - auto rank = y / square_height; - auto file = x / square_width; - if (rank < 0 || file < 0 || rank > 7 || file > 7) - return {}; - - if (side() == Chess::Color::White) - return Chess::Square { 7 - rank, file }; - - return Chess::Square { rank, 7 - file }; -} - -RefPtr ChessWidget::get_piece_graphic(Chess::Piece const& piece) const -{ - return m_pieces.get(piece).value_or(nullptr); -} - -void ChessWidget::reset() -{ - m_board_markings.clear(); - m_playback = false; - m_playback_move_number = 0; - m_board_playback = Chess::Board(); - m_board = Chess::Board(); - update_move_display_widget(m_board); - m_side = (get_random() % 2) ? Chess::Color::White : Chess::Color::Black; - m_drag_enabled = true; - if (m_engine) - m_engine->start_new_game(); - - input_engine_move(); - update(); -} - -void ChessWidget::set_board_theme(StringView name) -{ - // FIXME: Add some kind of themes.json - // The following Colors have been taken from lichess.org, but i'm pretty sure they took them from chess.com. - if (name == "Beige") { - m_board_theme = { "Beige"sv, Gfx::Color::from_rgb(0xb58863), Gfx::Color::from_rgb(0xf0d9b5) }; - } else if (name == "Green") { - m_board_theme = { "Green"sv, Gfx::Color::from_rgb(0x86a666), Gfx::Color::from_rgb(0xffffdd) }; - } else if (name == "Blue") { - m_board_theme = { "Blue"sv, Gfx::Color::from_rgb(0x8ca2ad), Gfx::Color::from_rgb(0xdee3e6) }; - } else { - set_board_theme("Beige"sv); - } -} - -bool ChessWidget::want_engine_move() -{ - if (!m_engine) - return false; - if (board().turn() == side() || board().game_finished()) - return false; - return true; -} - -void ChessWidget::input_engine_move() -{ - if (!want_engine_move()) - return; - - bool drag_was_enabled = drag_enabled(); - if (drag_was_enabled) - set_drag_enabled(false); - - set_override_cursor(Gfx::StandardCursor::Wait); - m_engine->get_best_move(board(), 4000, [this, drag_was_enabled](ErrorOr move) { - set_override_cursor(Gfx::StandardCursor::None); - if (!want_engine_move()) - return; - set_drag_enabled(drag_was_enabled); - if (!move.is_error()) { - VERIFY(board().apply_move(move.release_value())); - update_move_display_widget(board()); - if (check_game_over(ClaimDrawBehavior::Prompt)) - return; - } - m_playback_move_number = m_board.moves().size(); - m_playback = false; - m_board_markings.clear(); - update(); - }); -} - -void ChessWidget::playback_move(PlaybackDirection direction) -{ - if (m_board.moves().is_empty()) - return; - - m_playback = true; - m_board_markings.clear(); - - switch (direction) { - case PlaybackDirection::Backward: - if (m_playback_move_number == 0) - return; - m_board_playback = Chess::Board(); - for (size_t i = 0; i < m_playback_move_number - 1; i++) - m_board_playback.apply_move(m_board.moves().at(i)); - update_move_display_widget(m_board_playback); - m_playback_move_number--; - break; - case PlaybackDirection::Forward: - if (m_playback_move_number + 1 > m_board.moves().size()) { - m_playback = false; - return; - } - m_board_playback.apply_move(m_board.moves().at(m_playback_move_number++)); - update_move_display_widget(m_board_playback); - if (m_playback_move_number == m_board.moves().size()) - m_playback = false; - break; - case PlaybackDirection::First: - m_board_playback = Chess::Board(); - m_playback_move_number = 0; - break; - case PlaybackDirection::Last: - while (m_playback) { - playback_move(PlaybackDirection::Forward); - } - break; - default: - VERIFY_NOT_REACHED(); - } - update(); -} - -void ChessWidget::update_move_display_widget(Chess::Board& board) -{ - size_t turn = 1; - StringBuilder sb; - for (auto [i, move] : enumerate(board.moves())) { - if (i % 2 == 0) { - sb.append(MUST(String::formatted("{}. {}", turn, MUST(move.to_algebraic())))); - } else { - sb.append(MUST(String::formatted(" {}\n", MUST(move.to_algebraic())))); - turn++; - } - } - m_move_display_widget->set_text(sb.string_view()); -} - -ErrorOr ChessWidget::get_fen() const -{ - return TRY(m_playback ? m_board_playback.to_fen() : m_board.to_fen()); -} - -ErrorOr ChessWidget::import_pgn(Core::File& file) -{ - reset(); - enum class TokenType { - Move, - TagSymbol, - TagString, - Comment, - GameTerminator, - RAVStart, - RAVEnd, - Nag - }; - - struct Token { - TokenType type; - StringView value; - }; - auto maybe_file = file.read_until_eof(); - if (maybe_file.is_error()) { - return PGNParseError::from_string_formatted(String::formatted("Could not read file")); - } - ByteString bytes = ByteString { maybe_file.release_value().bytes() }; - Vector tokens; - Vector rav_stack; - // FIXME: Engine cannot parse suffixes ? and !. - StringView suffix_characters = "+#"sv; - StringView closing_characters = "]})"sv; - StringView opening_characters = "({["sv; - LineTrackingLexer lexer { StringView { bytes } }; - - while (!lexer.is_eof()) { - - if (lexer.next_is(is_any_of(closing_characters))) { - return PGNParseError::from_string_formatted(String::formatted("Unexpected character: {}.\n(line {} column {})", lexer.consume(1), lexer.current_position().line + 1, lexer.current_position().column)); - } - if (lexer.next_is('[')) { - lexer.consume_specific('['); - auto value = lexer.consume_until(" "); - tokens.append(Token { TokenType::TagSymbol, value }); - lexer.ignore_while(is_ascii_space); - if (!lexer.consume_specific('"')) { - return PGNParseError::from_string_formatted(String::formatted("Expected opening \".\n(line {} column {})", lexer.current_position().line + 1, lexer.current_position().column)); - } - // Parsing will only succeed if a " is reached, if the lexer goes to a ] the next - // consume_specific will fail. - value = lexer.consume_until(is_any_of("\"]\n"sv)); - tokens.append(Token { TokenType::TagString, value }); - if (!lexer.consume_specific('"')) { - return PGNParseError::from_string_formatted(String::formatted("Expected closing \".\n(line {} column {})", lexer.current_position().line + 1, lexer.current_position().column)); - } - // The end quote must be followed by a closing bracket. - if (!lexer.consume_specific(']')) { - return PGNParseError::from_string_formatted(String::formatted("Expected closing bracket.\n(line {} column {})", lexer.current_position().line + 1, lexer.current_position().column)); - } - // Deal with trailing white space - lexer.ignore_while(is_ascii_space); - continue; - } - - if (lexer.next_is('{')) { - lexer.consume_specific('{'); - // Deal with leading white space - lexer.ignore_while(is_ascii_space); - auto value = lexer.consume_until('}'); - tokens.append(Token { TokenType::Comment, value }); - if (!lexer.consume_specific('}')) { - return PGNParseError::from_string_formatted(String::formatted("Expected closing brace.\n(line {} column {})", lexer.current_position().line + 1, lexer.current_position().column)); - } - // Deal with trailing white space - lexer.ignore_while(is_ascii_space); - continue; - } - - if (lexer.next_is('(')) { - // FIXME: Actually implement RAV instead of just ignoring them. - rav_stack.append(lexer.consume(1)); - while (!rav_stack.is_empty() && !lexer.is_eof()) { - lexer.ignore_until(is_any_of("()"sv)); - if (lexer.next_is('(')) { - rav_stack.append(lexer.consume(1)); - tokens.append(Token { TokenType::RAVStart, rav_stack.last() }); - } else { - rav_stack.take_last(); - tokens.append(Token { TokenType::RAVStart, lexer.consume(1) }); - } - } - if (!rav_stack.is_empty() || lexer.is_eof()) { - return PGNParseError::from_string_formatted(String::formatted("Unclosed recursive annotation.\n(line {} column {})", lexer.current_position().line + 1, lexer.current_position().column)); - } - continue; - } - - if (lexer.next_is("1-0"sv)) { - auto value = lexer.consume(3); - tokens.append(Token { TokenType::GameTerminator, value }); - break; - } - if (lexer.next_is("0-1"sv)) { - auto value = lexer.consume(3); - tokens.append(Token { TokenType::GameTerminator, value }); - break; - } - - if (lexer.next_is("1/2-1/2"sv)) { - auto value = lexer.consume(3); - tokens.append(Token { TokenType::GameTerminator, value }); - break; - } - - if (lexer.next_is("*"sv)) { - auto value = lexer.consume(1); - tokens.append(Token { TokenType::GameTerminator, value }); - break; - } - - if (lexer.next_is(is_ascii_alpha)) { - // Parse move. - auto value = lexer.consume_while([&](char c) { return is_ascii_alphanumeric(c) || suffix_characters.contains(c) || c == '=' || c == '-'; }); - tokens.append(Token { TokenType::Move, value }); - - if (!lexer.next_is(is_any_of(" ({$"sv)) && !lexer.next_is(is_ascii_space)) { - return PGNParseError::from_string_formatted(String::formatted("Unexpected character {}.\n(line {} column {})", lexer.consume(1), lexer.current_position().line + 1, lexer.current_position().column)); - } - - // Deal with any extra trailing white space - lexer.ignore_while(is_ascii_space); - continue; - } - - if (lexer.next_is("$"sv)) { - lexer.consume_specific('$'); - if (!lexer.next_is(is_ascii_digit)) { - return PGNParseError::from_string_formatted(String::formatted("Unexpected character {}.\n(line {} column {})", lexer.consume(1), lexer.current_position().line + 1, lexer.current_position().column)); - } - - auto value = lexer.consume_while(is_ascii_digit); - tokens.append(Token { TokenType::Nag, value }); - - // Ensure that a number has been parsed and it's in range 0-255. - auto optional_number = value.to_number(); - if (!optional_number.has_value()) { - return PGNParseError::from_string_formatted(String::formatted("Could not parse Nag.\n(line {} column {})", lexer.current_position().line + 1, lexer.current_position().column)); - } - - auto number = optional_number.value(); - if (number < 0 || number > 255) { - return PGNParseError::from_string_formatted(String::formatted("Nag must be number between 0-255 but got {}.\n(line {} column {})", number, lexer.current_position().line + 1, lexer.current_position().column)); - } - - // Ensure that the Nag is followed by a whitespace. - if (!lexer.consume_specific(' ')) { - return PGNParseError::from_string_formatted(String::formatted("Unexpected character {}.\n(line {} column {})", lexer.consume(1), lexer.current_position().line + 1, lexer.current_position().column)); - } - - // Deal with trailing white space - lexer.ignore_while(is_ascii_space); - continue; - } - - // Advance to the start of a token or to an invalid closing character to be dealt with. - lexer.ignore_until([&](char c) { return is_ascii_alpha(c) || opening_characters.contains(c) || closing_characters.contains(c) || c == '$'; }); - } - - // FIXME: Display more of this information in the UI. - Chess::Color turn = Chess::Color::White; - for (auto& token : tokens) { - switch (token.type) { - case TokenType::Move: - // FIXME: Add some move validation so the engine doesn't crash. - m_board.apply_move(Chess::Move::from_algebraic(token.value, turn, m_board)); - turn = Chess::opposing_color(turn); - break; - case TokenType::TagSymbol: - break; - case TokenType::TagString: - break; - case TokenType::GameTerminator: - if (token.value == "1-0"sv) { - m_board.set_resigned(Chess::Color::Black); - } - if (token.value == "0-1"sv) { - m_board.set_resigned(Chess::Color::White); - } - break; - case TokenType::Comment: - break; - case TokenType::RAVStart: - break; - case TokenType::RAVEnd: - break; - case TokenType::Nag: - break; - } - } - - m_board_markings.clear(); - m_board_playback = m_board; - m_playback_move_number = m_board_playback.moves().size(); - m_playback = true; - update_move_display_widget(m_board_playback); - update(); - - return {}; -} - -ErrorOr ChessWidget::export_pgn(Core::File& file) const -{ - // Tag Pair Section - TRY(file.write_until_depleted("[Event \"Casual Game\"]\n"sv.bytes())); - TRY(file.write_until_depleted("[Site \"SerenityOS Chess\"]\n"sv.bytes())); - TRY(file.write_formatted("[Date \"{}\"]\n", Core::DateTime::now().to_byte_string("%Y.%m.%d"sv))); - TRY(file.write_until_depleted("[Round \"1\"]\n"sv.bytes())); - - auto current_user = TRY(Core::Account::self(Core::Account::Read::PasswdOnly)); - auto const username = TRY(String::from_byte_string(current_user.username())); - - auto const player1 = (!username.is_empty() ? username : "?"sv); - auto const player2 = (!m_engine.is_null() ? "SerenityOS ChessEngine"sv : "?"sv); - - TRY(file.write_formatted("[White \"{}\"]\n", m_side == Chess::Color::White ? player1 : player2)); - TRY(file.write_formatted("[Black \"{}\"]\n", m_side == Chess::Color::Black ? player1 : player2)); - - TRY(file.write_formatted("[Result \"{}\"]\n", Chess::Board::result_to_points_string(m_board.game_result(), m_board.turn()))); - TRY(file.write_until_depleted("[WhiteElo \"?\"]\n"sv.bytes())); - TRY(file.write_until_depleted("[BlackElo \"?\"]\n"sv.bytes())); - TRY(file.write_until_depleted("[Variant \"Standard\"]\n"sv.bytes())); - TRY(file.write_until_depleted("[TimeControl \"-\"]\n"sv.bytes())); - TRY(file.write_until_depleted("[Annotator \"SerenityOS Chess\"]\n"sv.bytes())); - TRY(file.write_until_depleted("\n"sv.bytes())); - - // Movetext Section - for (size_t i = 0, move_no = 1; i < m_board.moves().size(); i += 2, move_no++) { - auto const white = TRY(m_board.moves().at(i).to_algebraic()); - - if (i + 1 < m_board.moves().size()) { - auto const black = TRY(m_board.moves().at(i + 1).to_algebraic()); - TRY(file.write_until_depleted(TRY(String::formatted("{}. {} {} ", move_no, white, black)).bytes())); - } else { - TRY(file.write_until_depleted(TRY(String::formatted("{}. {} ", move_no, white)).bytes())); - } - } - - TRY(file.write_until_depleted("{ "sv.bytes())); - TRY(file.write_until_depleted(Chess::Board::result_to_string(m_board.game_result(), m_board.turn()).bytes())); - TRY(file.write_until_depleted(" } "sv.bytes())); - TRY(file.write_until_depleted(Chess::Board::result_to_points_string(m_board.game_result(), m_board.turn()).bytes())); - TRY(file.write_until_depleted("\n"sv.bytes())); - - return {}; -} - -void ChessWidget::flip_board() -{ - if (want_engine_move()) { - GUI::MessageBox::show(window(), "You can only flip the board on your turn."sv, "Flip Board"sv, GUI::MessageBox::Type::Information); - return; - } - m_side = Chess::opposing_color(m_side); - input_engine_move(); - update(); -} - -int ChessWidget::resign() -{ - // FIXME: Disable the resign checkbox if the game is finished - if (board().game_finished()) - return -1; - - if (want_engine_move()) { - GUI::MessageBox::show(window(), "You can only resign on your turn."sv, "Resign"sv, GUI::MessageBox::Type::Information); - return -1; - } - - auto result = GUI::MessageBox::show(window(), "Are you sure you wish to resign?"sv, "Resign"sv, GUI::MessageBox::Type::Warning, GUI::MessageBox::InputType::YesNo); - if (result != GUI::MessageBox::ExecResult::Yes) - return -1; - - board().set_resigned(m_board.turn()); - - set_drag_enabled(false); - update(); - auto const msg = Chess::Board::result_to_string(m_board.game_result(), m_board.turn()); - GUI::MessageBox::show(window(), msg, "Game Over"sv, GUI::MessageBox::Type::Information); - - return 0; -} - -bool ChessWidget::check_game_over(ClaimDrawBehavior claim_draw_behavior) -{ - if (board().game_result() == Chess::Board::Result::NotFinished) - return false; - - auto over = true; - switch (board().game_result()) { - case Chess::Board::Result::FiftyMoveRule: - if (claim_draw_behavior == ClaimDrawBehavior::Prompt) { - update(); - auto dialog_result = GUI::MessageBox::show(window(), "50 moves have elapsed without a capture or pawn advance. Claim Draw?"sv, "Claim Draw?"sv, - GUI::MessageBox::Type::Information, GUI::MessageBox::InputType::YesNo); - - if (dialog_result != GUI::Dialog::ExecResult::Yes) - over = false; - } - break; - case Chess::Board::Result::ThreeFoldRepetition: - if (claim_draw_behavior == ClaimDrawBehavior::Prompt) { - update(); - auto dialog_result = GUI::MessageBox::show(window(), "The same board state has repeated three times. Claim Draw?"sv, "Claim Draw?"sv, - GUI::MessageBox::Type::Information, GUI::MessageBox::InputType::YesNo); - if (dialog_result != GUI::Dialog::ExecResult::Yes) - over = false; - } - break; - default: - break; - } - - if (!over) - return false; - - set_override_cursor(Gfx::StandardCursor::None); - set_drag_enabled(false); - update(); - auto msg = Chess::Board::result_to_string(board().game_result(), board().turn()); - GUI::MessageBox::show(window(), msg, "Game Over"sv, GUI::MessageBox::Type::Information); - return true; -} - -void ChessWidget::config_string_did_change(StringView domain, StringView group, StringView key, StringView value) -{ - if (domain != "Games"sv && group != "Chess"sv) - return; - - if (key == "PieceSet"sv) { - set_piece_set(value); - update(); - } else if (key == "BoardTheme"sv) { - set_board_theme(value); - update(); - } -} - -void ChessWidget::config_bool_did_change(StringView domain, StringView group, StringView key, bool value) -{ - if (domain != "Games"sv && group != "Chess"sv) - return; - - if (key == "ShowCoordinates"sv) { - set_coordinates(value); - update(); - } else if (key == "HighlightChecks"sv) { - set_highlight_checks(value); - update(); - } -} - -} diff --git a/Userland/Games/Chess/ChessWidget.h b/Userland/Games/Chess/ChessWidget.h deleted file mode 100644 index 53a85f4a78b..00000000000 --- a/Userland/Games/Chess/ChessWidget.h +++ /dev/null @@ -1,183 +0,0 @@ -/* - * Copyright (c) 2020-2022, the SerenityOS developers. - * Copyright (c) 2023, Sam Atkins - * Copyright (c) 2023, Tim Ledbetter - * Copyright (c) 2024, Daniel Gaston - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include "Engine.h" -#include -#include -#include -#include -#include -#include -#include -#include - -namespace Chess { - -class PGNParseError { -public: - PGNParseError() = default; - PGNParseError(String&& message) - : m_message(move(message)) - { - } - - String const& message() const { return m_message; } - - static PGNParseError from_string_formatted(ErrorOr maybe_formatted_string) - { - if (maybe_formatted_string.is_error()) { - return PGNParseError {}; - } - return PGNParseError { maybe_formatted_string.release_value() }; - } - -private: - String m_message; -}; - -class ChessWidget final - : public GUI::Frame - , public Config::Listener { - C_OBJECT_ABSTRACT(ChessWidget); - -public: - static ErrorOr> try_create(); - - virtual ~ChessWidget() override = default; - - virtual void paint_event(GUI::PaintEvent&) override; - virtual void mousedown_event(GUI::MouseEvent&) override; - virtual void mouseup_event(GUI::MouseEvent&) override; - virtual void mousemove_event(GUI::MouseEvent&) override; - virtual void keydown_event(GUI::KeyEvent&) override; - - Chess::Board& board() { return m_board; } - Chess::Board const& board() const { return m_board; } - - Chess::Board& board_playback() { return m_board_playback; } - Chess::Board const& board_playback() const { return m_board_playback; } - - Chess::Color side() const { return m_side; } - void set_side(Chess::Color side) { m_side = side; } - - void set_piece_set(StringView set); - - Optional mouse_to_square(GUI::MouseEvent& event) const; - - bool drag_enabled() const { return m_drag_enabled; } - void set_drag_enabled(bool e) { m_drag_enabled = e; } - RefPtr get_piece_graphic(Chess::Piece const& piece) const; - - bool show_available_moves() const { return m_show_available_moves; } - void set_show_available_moves(bool e) { m_show_available_moves = e; } - - ErrorOr get_fen() const; - ErrorOr import_pgn(Core::File&); - ErrorOr export_pgn(Core::File&) const; - void update_move_display_widget(Chess::Board&); - - int resign(); - void flip_board(); - void reset(); - - struct BoardTheme { - StringView name; - Gfx::Color dark_square_color; - Gfx::Color light_square_color; - }; - - BoardTheme const& board_theme() const { return m_board_theme; } - void set_board_theme(BoardTheme const& theme) { m_board_theme = theme; } - void set_board_theme(StringView name); - - enum class PlaybackDirection { - First, - Backward, - Forward, - Last - }; - - void playback_move(PlaybackDirection); - - void set_engine(RefPtr engine) { m_engine = engine; } - void set_move_display_widget(RefPtr move_display_widget) { m_move_display_widget = move_display_widget; } - - void input_engine_move(); - bool want_engine_move(); - - void set_coordinates(bool coordinates) { m_coordinates = coordinates; } - bool coordinates() const { return m_coordinates; } - - void set_highlight_checks(bool highlight_checks) { m_highlight_checks = highlight_checks; } - bool highlight_checks() const { return m_highlight_checks; } - - struct BoardMarking { - Chess::Square from { 50, 50 }; - Chess::Square to { 50, 50 }; - bool alternate_color { false }; - bool secondary_color { false }; - enum class Type { - Square, - Arrow, - None - }; - Type type() const - { - if (from.in_bounds() && to.in_bounds() && from != to) - return Type::Arrow; - else if ((from.in_bounds() && !to.in_bounds()) || (from.in_bounds() && to.in_bounds() && from == to)) - return Type::Square; - - return Type::None; - } - bool operator==(BoardMarking const& other) const { return from == other.from && to == other.to; } - }; - -private: - enum class ClaimDrawBehavior { - Always, - Prompt - }; - - ChessWidget() = default; - - virtual void config_string_did_change(StringView domain, StringView group, StringView key, StringView value) override; - virtual void config_bool_did_change(StringView domain, StringView group, StringView key, bool value) override; - - bool check_game_over(ClaimDrawBehavior); - - Chess::Board m_board; - Chess::Board m_board_playback; - bool m_playback { false }; - size_t m_playback_move_number { 0 }; - BoardMarking m_current_marking; - Vector m_board_markings; - BoardTheme m_board_theme { "Beige"sv, Gfx::Color::from_rgb(0xb58863), Gfx::Color::from_rgb(0xf0d9b5) }; - Gfx::Color m_move_highlight_color { Gfx::Color::from_argb(0x66ccee00) }; - Gfx::Color m_marking_primary_color { Gfx::Color::from_argb(0x66ff0000) }; - Gfx::Color m_marking_alternate_color { Gfx::Color::from_argb(0x66ffaa00) }; - Gfx::Color m_marking_secondary_color { Gfx::Color::from_argb(0x6655dd55) }; - Chess::Color m_side { Chess::Color::White }; - HashMap> m_pieces; - bool m_any_piece_images_are_missing { false }; - Chess::Square m_moving_square { 50, 50 }; - Gfx::IntPoint m_drag_point; - bool m_dragging_piece { false }; - bool m_drag_enabled { true }; - bool m_show_available_moves { true }; - Vector m_available_moves; - RefPtr m_engine; - bool m_coordinates { true }; - bool m_highlight_checks { true }; - RefPtr m_move_display_widget; -}; - -} diff --git a/Userland/Games/Chess/Engine.cpp b/Userland/Games/Chess/Engine.cpp deleted file mode 100644 index 3245f10deda..00000000000 --- a/Userland/Games/Chess/Engine.cpp +++ /dev/null @@ -1,105 +0,0 @@ -/* - * Copyright (c) 2020, the SerenityOS developers. - * Copyright (c) 2023, Tim Ledbetter - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "Engine.h" -#include -#include -#include -#include -#include -#include - -Engine::~Engine() -{ - quit(); -} - -Engine::Engine(String command) - : m_command(move(command)) -{ - connect_to_engine_service(); -} - -void Engine::connect_to_engine_service() -{ - int wpipefds[2]; - int rpipefds[2]; - if (pipe2(wpipefds, O_CLOEXEC) < 0) { - perror("pipe2"); - VERIFY_NOT_REACHED(); - } - - if (pipe2(rpipefds, O_CLOEXEC) < 0) { - perror("pipe2"); - VERIFY_NOT_REACHED(); - } - - posix_spawn_file_actions_t file_actions; - posix_spawn_file_actions_init(&file_actions); - posix_spawn_file_actions_adddup2(&file_actions, wpipefds[0], STDIN_FILENO); - posix_spawn_file_actions_adddup2(&file_actions, rpipefds[1], STDOUT_FILENO); - - auto command_length = m_command.code_points().length(); - auto command_name = new char[command_length + 1]; - memcpy(command_name, m_command.bytes_as_string_view().characters_without_null_termination(), command_length); - command_name[command_length] = '\0'; - - char const* argv[] = { command_name, nullptr }; - - pid_t pid = -1; - if (posix_spawnp(&pid, command_name, &file_actions, nullptr, const_cast(argv), environ) < 0) { - perror("posix_spawnp"); - VERIFY_NOT_REACHED(); - } - - delete[] command_name; - - posix_spawn_file_actions_destroy(&file_actions); - - close(wpipefds[0]); - close(rpipefds[1]); - - auto infile = Core::File::adopt_fd(rpipefds[0], Core::File::OpenMode::Read).release_value_but_fixme_should_propagate_errors(); - infile->set_blocking(false).release_value_but_fixme_should_propagate_errors(); - set_in(move(infile)).release_value_but_fixme_should_propagate_errors(); - - auto outfile = Core::File::adopt_fd(wpipefds[1], Core::File::OpenMode::Write).release_value_but_fixme_should_propagate_errors(); - outfile->set_blocking(false).release_value_but_fixme_should_propagate_errors(); - set_out(move(outfile)); - - send_command(Chess::UCI::UCICommand()); - m_connected = true; -} - -void Engine::handle_bestmove(Chess::UCI::BestMoveCommand const& command) -{ - if (m_bestmove_callback) - m_bestmove_callback(command.move()); - - m_bestmove_callback = nullptr; -} - -void Engine::quit() -{ - if (!m_connected) - return; - - send_command(Chess::UCI::QuitCommand()); - m_connected = false; -} - -void Engine::handle_unexpected_eof() -{ - m_connected = false; - if (m_bestmove_callback) - m_bestmove_callback(Error::from_errno(EPIPE)); - - m_bestmove_callback = nullptr; - - if (on_connection_lost) - on_connection_lost(); -} diff --git a/Userland/Games/Chess/Engine.h b/Userland/Games/Chess/Engine.h deleted file mode 100644 index d5e7f8524b0..00000000000 --- a/Userland/Games/Chess/Engine.h +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright (c) 2020, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include - -class Engine : public Chess::UCI::Endpoint { - C_OBJECT(Engine) -public: - virtual ~Engine() override; - - Engine(String command); - - Engine(Engine const&) = delete; - Engine& operator=(Engine const&) = delete; - - Function on_connection_lost; - - virtual void handle_bestmove(Chess::UCI::BestMoveCommand const&) override; - virtual void handle_unexpected_eof() override; - - template - void get_best_move(Chess::Board const& board, int time_limit, Callback&& callback) - { - if (!m_connected) - connect_to_engine_service(); - - send_command(Chess::UCI::PositionCommand({}, board.moves())); - Chess::UCI::GoCommand go_command; - go_command.movetime = time_limit; - send_command(go_command); - m_bestmove_callback = move(callback); - } - - void start_new_game() - { - if (!m_connected) - return; - - Chess::UCI::UCINewGameCommand ucinewgame_command; - send_command(ucinewgame_command); - } - -private: - void quit(); - void connect_to_engine_service(); - - String m_command; - Function)> m_bestmove_callback; - bool m_connected { false }; -}; diff --git a/Userland/Games/Chess/MainWidget.h b/Userland/Games/Chess/MainWidget.h deleted file mode 100644 index b1bbbab407b..00000000000 --- a/Userland/Games/Chess/MainWidget.h +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright (c) 2024, the SerenityOS developers - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include - -namespace Chess { - -class MainWidget : public GUI::Widget { - C_OBJECT_ABSTRACT(MainWidget) -public: - static ErrorOr> try_create(); - virtual ~MainWidget() override = default; - -private: - MainWidget() = default; -}; - -} diff --git a/Userland/Games/Chess/PromotionDialog.cpp b/Userland/Games/Chess/PromotionDialog.cpp deleted file mode 100644 index eb159d03c7a..00000000000 --- a/Userland/Games/Chess/PromotionDialog.cpp +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright (c) 2020-2024, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "PromotionDialog.h" -#include -#include -#include - -namespace Chess { - -ErrorOr> PromotionDialog::try_create(ChessWidget& chess_widget) -{ - auto promotion_widget = TRY(Chess::PromotionWidget::try_create()); - auto promotion_dialog = TRY(adopt_nonnull_ref_or_enomem(new (nothrow) PromotionDialog(move(promotion_widget), chess_widget))); - return promotion_dialog; -} - -PromotionDialog::PromotionDialog(NonnullRefPtr promotion_widget, ChessWidget& chess_widget) - : Dialog(chess_widget.window()) - , m_selected_piece(Chess::Type::None) -{ - set_title("Choose piece to promote to"); - set_icon(chess_widget.window()->icon()); - set_main_widget(promotion_widget); - - auto initialize_promotion_button = [&](StringView button_name, Chess::Type piece) { - auto button = promotion_widget->find_descendant_of_type_named(button_name); - button->set_icon(chess_widget.get_piece_graphic({ chess_widget.board().turn(), piece })); - button->on_click = [this, piece](auto) { - m_selected_piece = piece; - done(ExecResult::OK); - }; - }; - - initialize_promotion_button("queen_button"sv, Type::Queen); - initialize_promotion_button("knight_button"sv, Type::Knight); - initialize_promotion_button("rook_button"sv, Type::Rook); - initialize_promotion_button("bishop_button"sv, Type::Bishop); -} - -void PromotionDialog::event(Core::Event& event) -{ - Dialog::event(event); -} - -} diff --git a/Userland/Games/Chess/PromotionDialog.h b/Userland/Games/Chess/PromotionDialog.h deleted file mode 100644 index 5085f9692cf..00000000000 --- a/Userland/Games/Chess/PromotionDialog.h +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright (c) 2020-2024, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include "ChessWidget.h" -#include "PromotionWidget.h" -#include - -namespace Chess { - -class PromotionDialog final : public GUI::Dialog { - C_OBJECT_ABSTRACT(PromotionDialog) -public: - static ErrorOr> try_create(ChessWidget& chess_widget); - Chess::Type selected_piece() const { return m_selected_piece; } - -private: - PromotionDialog(NonnullRefPtr promotion_widget, ChessWidget& chess_widget); - virtual void event(Core::Event&) override; - - Chess::Type m_selected_piece; -}; - -} diff --git a/Userland/Games/Chess/PromotionWidget.gml b/Userland/Games/Chess/PromotionWidget.gml deleted file mode 100644 index 31620b4d873..00000000000 --- a/Userland/Games/Chess/PromotionWidget.gml +++ /dev/null @@ -1,29 +0,0 @@ -@Chess::PromotionWidget { - fixed_height: 70 - fill_with_background_color: true - layout: @GUI::HorizontalBoxLayout {} - - @GUI::Button { - fixed_width: 70 - fixed_height: 70 - name: "queen_button" - } - - @GUI::Button { - fixed_width: 70 - fixed_height: 70 - name: "knight_button" - } - - @GUI::Button { - fixed_width: 70 - fixed_height: 70 - name: "rook_button" - } - - @GUI::Button { - fixed_width: 70 - fixed_height: 70 - name: "bishop_button" - } -} diff --git a/Userland/Games/Chess/PromotionWidget.h b/Userland/Games/Chess/PromotionWidget.h deleted file mode 100644 index 1d2d99b1ea3..00000000000 --- a/Userland/Games/Chess/PromotionWidget.h +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright (c) 2024, the SerenityOS developers - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include - -namespace Chess { - -class PromotionWidget : public GUI::Widget { - C_OBJECT_ABSTRACT(PromotionWidget) -public: - static ErrorOr> try_create(); - virtual ~PromotionWidget() override = default; - -private: - PromotionWidget() = default; -}; - -} diff --git a/Userland/Games/Chess/main.cpp b/Userland/Games/Chess/main.cpp deleted file mode 100644 index c61ac30722e..00000000000 --- a/Userland/Games/Chess/main.cpp +++ /dev/null @@ -1,224 +0,0 @@ -/* - * Copyright (c) 2020, the SerenityOS developers. - * Copyright (c) 2023, Sam Atkins - * Copyright (c) 2023, Tim Ledbetter - * Copyright (c) 2024, Daniel Gaston - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "ChessWidget.h" -#include "MainWidget.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -struct EngineDetails { - StringView command; - StringView name { command }; - String path {}; -}; - -static Vector s_all_engines { - { "ChessEngine"sv }, - { "stockfish"sv, "Stockfish"sv }, -}; - -static ErrorOr> available_engines() -{ - Vector available_engines; - for (auto& engine : s_all_engines) { - auto path_or_error = Core::System::resolve_executable_from_environment(engine.command); - if (path_or_error.is_error()) - continue; - - engine.path = path_or_error.release_value(); - TRY(available_engines.try_append(engine)); - } - - return available_engines; -} - -ErrorOr serenity_main(Main::Arguments arguments) -{ - TRY(Core::System::pledge("stdio rpath recvfd sendfd thread proc exec unix")); - - auto app = TRY(GUI::Application::create(arguments)); - - Config::pledge_domain("Games"); - Config::monitor_domain("Games"); - - TRY(Desktop::Launcher::add_allowed_handler_with_only_specific_urls("/bin/Help", { URL::create_with_file_scheme("/usr/share/man/man6/Chess.md") })); - TRY(Desktop::Launcher::seal_allowlist()); - - auto app_icon = TRY(GUI::Icon::try_create_default_icon("app-chess"sv)); - - auto window = GUI::Window::construct(); - auto main_widget = TRY(Chess::MainWidget::try_create()); - - auto& chess_widget = *main_widget->find_descendant_of_type_named("chess_widget"); - auto& move_display_widget = *main_widget->find_descendant_of_type_named("move_display_widget"); - chess_widget.set_move_display_widget(move(move_display_widget)); - - window->set_main_widget(main_widget); - window->set_focused_widget(&chess_widget); - - auto engines = TRY(available_engines()); - for (auto const& engine : engines) - TRY(Core::System::unveil(engine.path, "x"sv)); - - TRY(Core::System::unveil("/etc/passwd", "r")); - TRY(Core::System::unveil("/res", "r")); - TRY(Core::System::unveil("/bin/GamesSettings", "x")); - TRY(Core::System::unveil("/tmp/session/%sid/portal/launch", "rw")); - TRY(Core::System::unveil("/tmp/session/%sid/portal/filesystemaccess", "rw")); - TRY(Core::System::unveil(nullptr, nullptr)); - - window->set_title("Chess"); - window->set_base_size({ 4, 4 }); - window->set_size_increment({ 8, 8 }); - window->resize(668, 508); - - window->set_icon(app_icon.bitmap_for_size(16)); - - chess_widget.set_piece_set(Config::read_string("Games"sv, "Chess"sv, "PieceSet"sv, "Classic"sv)); - chess_widget.set_board_theme(Config::read_string("Games"sv, "Chess"sv, "BoardTheme"sv, "Beige"sv)); - chess_widget.set_coordinates(Config::read_bool("Games"sv, "Chess"sv, "ShowCoordinates"sv, true)); - chess_widget.set_show_available_moves(Config::read_bool("Games"sv, "Chess"sv, "ShowAvailableMoves"sv, true)); - chess_widget.set_highlight_checks(Config::read_bool("Games"sv, "Chess"sv, "HighlightChecks"sv, true)); - - auto game_menu = window->add_menu("&Game"_string); - - game_menu->add_action(GUI::Action::create("&Resign", { Mod_None, Key_F3 }, [&](auto&) { - chess_widget.resign(); - })); - game_menu->add_action(GUI::Action::create("&Flip Board", { Mod_Ctrl, Key_F }, [&](auto&) { - chess_widget.flip_board(); - })); - game_menu->add_separator(); - - game_menu->add_action(GUI::Action::create("&Import PGN...", { Mod_Ctrl, Key_O }, [&](auto&) { - FileSystemAccessClient::OpenFileOptions options { - .allowed_file_types = Vector { - GUI::FileTypeFilter { "PGN Files", { { "pgn" } } }, - GUI::FileTypeFilter::all_files(), - } - }; - auto result = FileSystemAccessClient::Client::the().open_file(window, options); - if (result.is_error()) - return; - - if (auto maybe_error = chess_widget.import_pgn(*result.value().release_stream()); maybe_error.is_error()) { - auto error_message = maybe_error.release_error().message(); - dbgln("Failed to import PGN: {}", error_message); - GUI::MessageBox::show(window, error_message, "Import Error"sv, GUI::MessageBox::Type::Information); - } else { - dbgln("Imported PGN file from {}", result.value().filename()); - } - })); - game_menu->add_action(GUI::Action::create("&Export PGN...", { Mod_Ctrl, Key_S }, [&](auto&) { - auto result = FileSystemAccessClient::Client::the().save_file(window, "Untitled", "pgn"); - if (result.is_error()) - return; - - if (auto maybe_error = chess_widget.export_pgn(*result.value().release_stream()); maybe_error.is_error()) - dbgln("Failed to export PGN: {}", maybe_error.release_error()); - else - dbgln("Exported PGN file to {}", result.value().filename()); - })); - game_menu->add_action(GUI::Action::create("&Copy FEN", { Mod_Ctrl, Key_C }, [&](auto&) { - GUI::Clipboard::the().set_data(chess_widget.get_fen().release_value_but_fixme_should_propagate_errors().bytes()); - GUI::MessageBox::show(window, "Board state copied to clipboard as FEN."sv, "Copy FEN"sv, GUI::MessageBox::Type::Information); - })); - game_menu->add_separator(); - - game_menu->add_action(GUI::Action::create("&New Game", { Mod_None, Key_F2 }, TRY(Gfx::Bitmap::load_from_file("/res/icons/16x16/reload.png"sv)), [&](auto&) { - if (chess_widget.board().game_result() == Chess::Board::Result::NotFinished) { - if (chess_widget.resign() < 0) - return; - } - chess_widget.reset(); - })); - game_menu->add_separator(); - - auto settings_action = GUI::Action::create( - "Chess &Settings", {}, TRY(Gfx::Bitmap::load_from_file("/res/icons/16x16/games.png"sv)), [window](auto&) { - GUI::Process::spawn_or_show_error(window, "/bin/GamesSettings"sv, Array { "--open-tab", "chess" }); - }, - window); - settings_action->set_status_tip("Open the Game Settings for Chess"_string); - game_menu->add_action(settings_action); - - auto show_available_moves_action = GUI::Action::create_checkable("Show Available Moves", [&](auto& action) { - chess_widget.set_show_available_moves(action.is_checked()); - chess_widget.update(); - Config::write_bool("Games"sv, "Chess"sv, "ShowAvailableMoves"sv, action.is_checked()); - }); - show_available_moves_action->set_checked(chess_widget.show_available_moves()); - game_menu->add_action(show_available_moves_action); - game_menu->add_separator(); - - game_menu->add_action(GUI::CommonActions::make_quit_action([](auto&) { - GUI::Application::the()->quit(); - })); - - auto engine_menu = window->add_menu("&Engine"_string); - - GUI::ActionGroup engines_action_group; - engines_action_group.set_exclusive(true); - auto engine_submenu = engine_menu->add_submenu("&Engine"_string); - auto human_engine_checkbox = GUI::Action::create_checkable("Human", [&](auto&) { - chess_widget.set_engine(nullptr); - }); - human_engine_checkbox->set_checked(true); - engines_action_group.add_action(human_engine_checkbox); - engine_submenu->add_action(human_engine_checkbox); - - for (auto const& engine : engines) { - auto action = GUI::Action::create_checkable(engine.name, [&](auto&) { - auto new_engine = Engine::construct(engine.path); - new_engine->on_connection_lost = [&]() { - if (!chess_widget.want_engine_move()) - return; - - auto rc = GUI::MessageBox::show(window, "Connection to the chess engine was lost while waiting for a move. Do you want to try again?"sv, "Chess"sv, GUI::MessageBox::Type::Question, GUI::MessageBox::InputType::YesNo); - if (rc == GUI::Dialog::ExecResult::Yes) - chess_widget.input_engine_move(); - else - human_engine_checkbox->activate(); - }; - chess_widget.set_engine(move(new_engine)); - chess_widget.input_engine_move(); - }); - engines_action_group.add_action(*action); - engine_submenu->add_action(*action); - } - - auto view_menu = window->add_menu("&View"_string); - view_menu->add_action(GUI::CommonActions::make_fullscreen_action([&](auto&) { - window->set_fullscreen(!window->is_fullscreen()); - })); - - auto help_menu = window->add_menu("&Help"_string); - help_menu->add_action(GUI::CommonActions::make_command_palette_action(window)); - help_menu->add_action(GUI::CommonActions::make_help_action([](auto&) { - Desktop::Launcher::open(URL::create_with_file_scheme("/usr/share/man/man6/Chess.md"), "/bin/Help"); - })); - help_menu->add_action(GUI::CommonActions::make_about_action("Chess"_string, app_icon, window)); - - window->show(); - chess_widget.reset(); - - return app->exec(); -} diff --git a/Userland/Games/ColorLines/CMakeLists.txt b/Userland/Games/ColorLines/CMakeLists.txt deleted file mode 100644 index b51e0067718..00000000000 --- a/Userland/Games/ColorLines/CMakeLists.txt +++ /dev/null @@ -1,13 +0,0 @@ -serenity_component( - ColorLines - RECOMMENDED - TARGETS ColorLines -) - -set(SOURCES - ColorLines.cpp - main.cpp -) - -serenity_app(ColorLines ICON app-colorlines) -target_link_libraries(ColorLines PRIVATE LibGUI LibCore LibGfx LibConfig LibMain LibDesktop LibURL) diff --git a/Userland/Games/ColorLines/ColorLines.cpp b/Userland/Games/ColorLines/ColorLines.cpp deleted file mode 100644 index 21ca1748bb9..00000000000 --- a/Userland/Games/ColorLines/ColorLines.cpp +++ /dev/null @@ -1,398 +0,0 @@ -/* - * Copyright (c) 2022, Oleg Kosenkov - * Copyright (c) 2022, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "ColorLines.h" -#include "HueFilter.h" -#include "Marble.h" -#include "MarbleBoard.h" -#include -#include -#include -#include -#include - -ColorLines::BitmapArray ColorLines::build_marble_color_bitmaps() -{ - auto marble_bitmap = MUST(Gfx::Bitmap::load_from_file("/res/graphics/colorlines/colorlines.png"sv)); - float constexpr hue_degrees[Marble::number_of_colors] = { - 0, // Red - 45, // Brown/Yellow - 90, // Green - 180, // Cyan - 225, // Blue - 300 // Purple - }; - BitmapArray colored_bitmaps; - colored_bitmaps.ensure_capacity(Marble::number_of_colors); - for (int i = 0; i < Marble::number_of_colors; ++i) { - auto bitmap = MUST(marble_bitmap->clone()); - HueFilter filter { hue_degrees[i] }; - filter.apply(*bitmap, bitmap->rect(), *marble_bitmap, marble_bitmap->rect()); - colored_bitmaps.append(bitmap); - } - return colored_bitmaps; -} - -ColorLines::BitmapArray ColorLines::build_marble_trace_bitmaps() -{ - // Use "Paw Prints" Unicode Character (U+1F43E) - auto trace_bitmap = NonnullRefPtr(*Gfx::Emoji::emoji_for_code_point(0x1F43E)); - BitmapArray result; - result.ensure_capacity(number_of_marble_trace_bitmaps); - result.append(trace_bitmap); - result.append(MUST(result.last()->rotated(Gfx::RotationDirection::Clockwise))); - result.append(MUST(result.last()->rotated(Gfx::RotationDirection::Clockwise))); - result.append(MUST(result.last()->rotated(Gfx::RotationDirection::Clockwise))); - return result; -} - -ColorLines::ColorLines(StringView app_name) - : m_app_name { app_name } - , m_game_state { GameState::Idle } - , m_board { make() } - , m_marble_bitmaps { build_marble_color_bitmaps() } - , m_trace_bitmaps { build_marble_trace_bitmaps() } - , m_score_font { Gfx::BitmapFont::load_from_uri("resource://fonts/MarietaBold24.font"sv) } -{ - VERIFY(m_marble_bitmaps.size() == Marble::number_of_colors); - set_font(Gfx::FontDatabase::default_fixed_width_font().bold_variant()); - m_high_score = Config::read_i32(m_app_name, m_app_name, "HighScore"sv, 0); - reset(); -} - -void ColorLines::reset() -{ - set_game_state(GameState::StartingGame); -} - -void ColorLines::mousedown_event(GUI::MouseEvent& event) -{ - if (m_game_state != GameState::Idle && m_game_state != GameState::MarbleSelected) - return; - auto const event_position = event.position().translated( - -frame_inner_rect().x(), - -frame_inner_rect().y() - board_vertical_margin); - if (event_position.x() < 0 || event_position.y() < 0) - return; - auto const clicked_cell = Point { event_position.x() / board_cell_dimension.width(), - event_position.y() / board_cell_dimension.height() }; - if (!MarbleBoard::in_bounds(clicked_cell)) - return; - if (m_board->has_selected_marble()) { - auto const selected_cell = m_board->selected_marble().position(); - if (selected_cell == clicked_cell) { - m_board->reset_selection(); - set_game_state(GameState::Idle); - return; - } - if (m_board->is_empty_cell_at(clicked_cell)) { - if (m_board->build_marble_path(selected_cell, clicked_cell, m_marble_path)) - set_game_state(GameState::MarbleMoving); - return; - } - if (m_board->select_marble(clicked_cell)) - set_game_state(GameState::MarbleSelected); - return; - } - if (m_board->select_marble(clicked_cell)) - set_game_state(GameState::MarbleSelected); -} - -void ColorLines::timer_event(Core::TimerEvent&) -{ - switch (m_game_state) { - case GameState::GeneratingMarbles: - update(); - if (--m_marble_animation_frame < AnimationFrames::marble_generating_end) { - m_marble_animation_frame = AnimationFrames::marble_default; - set_game_state(GameState::CheckingMarbles); - } - break; - - case GameState::MarbleSelected: - m_marble_animation_frame = (m_marble_animation_frame + 1) % AnimationFrames::number_of_marble_bounce_frames; - update(); - break; - - case GameState::MarbleMoving: - m_marble_animation_frame = (m_marble_animation_frame + 1) % AnimationFrames::number_of_marble_bounce_frames; - update(); - if (m_marble_path.remaining_steps() != 1 && m_marble_animation_frame != AnimationFrames::marble_at_top) - break; - if (auto const point = m_marble_path.next_point(); m_marble_path.is_empty()) { - auto const color = m_board->selected_marble().color(); - m_board->reset_selection(); - m_board->set_color_at(point, color); - if (m_board->check_and_remove_marbles()) - set_game_state(GameState::MarblesRemoving); - else - set_game_state(GameState::GeneratingMarbles); - } - break; - - case GameState::MarblesRemoving: - update(); - if (++m_marble_animation_frame > AnimationFrames::marble_removing_end) { - m_marble_animation_frame = AnimationFrames::marble_default; - m_score += 2 * m_board->removed_marbles().size(); - set_game_state(GameState::Idle); - } - break; - - case GameState::StartingGame: - case GameState::Idle: - case GameState::CheckingMarbles: - break; - - case GameState::GameOver: { - stop_timer(); - update(); - StringBuilder text; - text.appendff("Your score is {}", m_score); - if (m_score > m_high_score) { - text.append("\nThis is a new high score!"sv); - Config::write_i32(m_app_name, m_app_name, "HighScore"sv, int(m_high_score = m_score)); - } - GUI::MessageBox::show(window(), - text.string_view(), - "Game Over"sv, - GUI::MessageBox::Type::Information); - reset(); - break; - } - - default: - VERIFY_NOT_REACHED(); - } -} - -void ColorLines::paint_event(GUI::PaintEvent& event) -{ - GUI::Frame::paint_event(event); - GUI::Painter painter(*this); - painter.add_clip_rect(frame_inner_rect()); - painter.add_clip_rect(event.rect()); - - auto paint_cell = [&](GUI::Painter& painter, Gfx::IntRect rect, int color, int animation_frame) { - painter.draw_rect(rect, Color::Black); - rect.shrink(0, 2, 2, 0); - painter.draw_line(rect.bottom_left(), rect.top_left(), Color::White); - painter.draw_line(rect.top_left(), rect.top_right(), Color::White); - painter.draw_line(rect.top_right(), rect.bottom_right(), Color::DarkGray); - painter.draw_line(rect.bottom_right(), rect.bottom_left(), Color::DarkGray); - rect.shrink(1, 1, 1, 1); - painter.draw_line(rect.bottom_left(), rect.top_left(), Color::LightGray); - painter.draw_line(rect.top_left(), rect.top_right(), Color::LightGray); - painter.draw_line(rect.top_right(), rect.bottom_right(), Color::MidGray); - painter.draw_line(rect.bottom_right(), rect.bottom_left(), Color::MidGray); - painter.fill_rect(rect, tile_color); - rect.shrink(1, 1, 1, 1); - if (color >= 0 && color < Marble::number_of_colors) { - auto const source_rect = Gfx::IntRect { animation_frame * marble_pixel_size, 0, marble_pixel_size, marble_pixel_size }; - painter.draw_scaled_bitmap(rect, *m_marble_bitmaps[color], source_rect, - 1.0f, Gfx::Painter::ScalingMode::BilinearBlend); - } - }; - - painter.set_font(*m_score_font); - - // Draw board header with score, high score - auto board_header_size = frame_inner_rect().size(); - board_header_size.set_height(board_vertical_margin); - auto const board_header_rect = Gfx::IntRect { frame_inner_rect().top_left(), board_header_size }; - painter.fill_rect(board_header_rect, Color::Black); - - auto const text_margin = 8; - - // Draw score - auto const score_text = MUST(String::formatted("{:05}"sv, m_score)); - auto text_width = static_cast(ceilf(m_score_font->width(score_text))); - auto const score_text_rect = Gfx::IntRect { - frame_inner_rect().top_left().translated(text_margin), - Gfx::IntSize { text_width, font().pixel_size_rounded_up() } - }; - painter.draw_text(score_text_rect, score_text, Gfx::TextAlignment::CenterLeft, text_color); - - // Draw high score - auto const high_score_text = MUST(String::formatted("{:05}"sv, m_high_score)); - text_width = m_score_font->width(high_score_text); - auto const high_score_text_rect = Gfx::IntRect { - frame_inner_rect().top_right().translated(-(text_margin + text_width) - 1, text_margin), - Gfx::IntSize { text_width, font().pixel_size_rounded_up() } - }; - painter.draw_text(high_score_text_rect, high_score_text, Gfx::TextAlignment::CenterLeft, text_color); - - auto const cell_rect - = Gfx::IntRect(frame_inner_rect().top_left(), board_cell_dimension) - .translated(0, board_vertical_margin); - - // Draw all cells and the selected marble if it exists - for (int y = 0; y < MarbleBoard::board_size.height(); ++y) - for (int x = 0; x < MarbleBoard::board_size.width(); ++x) { - auto const& destination_rect = cell_rect.translated( - x * board_cell_dimension.width(), - y * board_cell_dimension.height()); - auto const point = Point { x, y }; - auto const animation_frame = m_game_state == GameState::MarbleSelected && m_board->has_selected_marble() - && m_board->selected_marble().position() == point - ? m_marble_animation_frame - : AnimationFrames::marble_default; - paint_cell(painter, destination_rect, m_board->color_at(point), animation_frame); - } - - // Draw preview marbles in the board - for (auto const& marble : m_board->preview_marbles()) { - auto const& point = marble.position(); - if (m_marble_path.contains(point) || !m_board->is_empty_cell_at(point)) - continue; - auto const& destination_rect = cell_rect.translated( - point.x() * board_cell_dimension.width(), - point.y() * board_cell_dimension.height()); - auto get_animation_frame = [this]() -> int { - switch (m_game_state) { - case GameState::GameOver: - return AnimationFrames::marble_default; - case GameState::GeneratingMarbles: - case GameState::CheckingMarbles: - return m_marble_animation_frame; - default: - return AnimationFrames::marble_generating_start; - } - }; - paint_cell(painter, destination_rect, marble.color(), get_animation_frame()); - } - - // Draw preview marbles in the board header - for (size_t i = 0; i < MarbleBoard::number_of_preview_marbles; ++i) { - auto const& marble = m_board->preview_marbles()[i]; - auto const& destination_rect = cell_rect.translated( - int(i + 3) * board_cell_dimension.width(), - -board_vertical_margin) - .shrunken(10, 10); - paint_cell(painter, destination_rect, marble.color(), AnimationFrames::marble_preview); - } - - // Draw moving marble - if (!m_marble_path.is_empty()) { - auto const point = m_marble_path.current_point(); - auto const& destination_rect = cell_rect.translated( - point.x() * board_cell_dimension.width(), - point.y() * board_cell_dimension.height()); - paint_cell(painter, destination_rect, m_board->selected_marble().color(), m_marble_animation_frame); - } - - // Draw removing marble - if (m_game_state == GameState::MarblesRemoving) - for (auto const& marble : m_board->removed_marbles()) { - auto const& point = marble.position(); - auto const& destination_rect = cell_rect.translated( - point.x() * board_cell_dimension.width(), - point.y() * board_cell_dimension.height()); - paint_cell(painter, destination_rect, marble.color(), m_marble_animation_frame); - } - - // Draw marble move trace - if (m_game_state == GameState::MarbleMoving && m_marble_path.remaining_steps() > 1) { - auto const trace_size = Gfx::IntSize { m_trace_bitmaps.first()->width(), m_trace_bitmaps.first()->height() }; - auto const target_trace_size = Gfx::IntSize { 14, 14 }; - auto const source_rect = Gfx::FloatRect(Gfx::IntPoint {}, trace_size); - for (size_t i = 0; i < m_marble_path.remaining_steps() - 1; ++i) { - auto const& current_step = m_marble_path[i]; - auto const destination_rect = Gfx::IntRect(frame_inner_rect().top_left(), target_trace_size) - .translated( - current_step.x() * board_cell_dimension.width(), - board_vertical_margin + current_step.y() * board_cell_dimension.height()) - .translated( - (board_cell_dimension.width() - target_trace_size.width()) / 2, - (board_cell_dimension.height() - target_trace_size.height()) / 2); - auto get_direction_bitmap_index = [&]() -> size_t { - auto const& previous_step = m_marble_path[i + 1]; - if (previous_step.x() > current_step.x()) - return 3; - if (previous_step.x() < current_step.x()) - return 1; - if (previous_step.y() > current_step.y()) - return 0; - return 2; - }; - painter.draw_scaled_bitmap(destination_rect, *m_trace_bitmaps[get_direction_bitmap_index()], source_rect, - 1.0f, Gfx::Painter::ScalingMode::BilinearBlend); - } - } -} - -void ColorLines::restart_timer(int milliseconds) -{ - stop_timer(); - start_timer(milliseconds); -} - -void ColorLines::set_game_state(GameState state) -{ - m_game_state = state; - switch (state) { - case GameState::StartingGame: - m_marble_path.reset(); - m_board->reset(); - m_score = 0; - m_marble_animation_frame = AnimationFrames::marble_default; - update(); - if (m_board->update_preview_marbles(false)) - set_game_state(GameState::GeneratingMarbles); - else - set_game_state(GameState::GameOver); - break; - case GameState::GeneratingMarbles: - m_board->reset_selection(); - m_marble_animation_frame = AnimationFrames::marble_generating_start; - update(); - if (m_board->ensure_all_preview_marbles_are_on_empty_cells()) - restart_timer(TimerIntervals::generating_marbles); - else - set_game_state(GameState::GameOver); - break; - case GameState::MarblesRemoving: - m_marble_animation_frame = AnimationFrames::marble_removing_start; - update(); - restart_timer(TimerIntervals::removing_marbles); - break; - case GameState::Idle: - m_marble_animation_frame = AnimationFrames::marble_default; - update(); - if (m_board->ensure_all_preview_marbles_are_on_empty_cells() && m_board->has_empty_cells()) - stop_timer(); - else - set_game_state(GameState::GameOver); - break; - case GameState::MarbleSelected: - restart_timer(TimerIntervals::selected_marble); - m_marble_animation_frame = AnimationFrames::marble_default; - update(); - break; - case GameState::CheckingMarbles: - m_marble_animation_frame = AnimationFrames::marble_default; - update(); - if (!m_board->place_preview_marbles_on_board()) - set_game_state(GameState::GameOver); - else if (m_board->check_and_remove_marbles()) - set_game_state(GameState::MarblesRemoving); - else - set_game_state(GameState::Idle); - break; - case GameState::MarbleMoving: - restart_timer(TimerIntervals::moving_marble); - m_board->clear_color_at(m_board->selected_marble().position()); - update(); - break; - case GameState::GameOver: - m_marble_animation_frame = AnimationFrames::marble_default; - update(); - break; - default: - VERIFY_NOT_REACHED(); - } -} diff --git a/Userland/Games/ColorLines/ColorLines.h b/Userland/Games/ColorLines/ColorLines.h deleted file mode 100644 index 362bbd8937e..00000000000 --- a/Userland/Games/ColorLines/ColorLines.h +++ /dev/null @@ -1,90 +0,0 @@ -/* - * Copyright (c) 2022, Oleg Kosenkov - * Copyright (c) 2022, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include "MarblePath.h" -#include -#include -#include -#include -#include -#include - -class MarbleBoard; - -class ColorLines : public GUI::Frame { - C_OBJECT(ColorLines); - -public: - virtual ~ColorLines() override = default; - - void reset(); - -private: - enum class GameState { - Idle = 0, // No marble is selected, waiting for marble selection - StartingGame, // Game is starting - GeneratingMarbles, // Three new marbles are being generated - MarbleSelected, // Marble is selected, waiting for the target cell selection - MarbleMoving, // Selected marble is moving to the target cell - MarblesRemoving, // Selected marble has completed the move and some marbles are being removed from the board - CheckingMarbles, // Checking whether marbles on the board form lines of 5 or more marbles - GameOver // Game is over - }; - - ColorLines(StringView app_name); - - virtual void paint_event(GUI::PaintEvent&) override; - virtual void mousedown_event(GUI::MouseEvent&) override; - virtual void timer_event(Core::TimerEvent&) override; - - void set_game_state(GameState state); - void restart_timer(int milliseconds); - - using Point = Gfx::IntPoint; - using BitmapArray = Vector>; - - StringView const m_app_name; - GameState m_game_state { GameState::Idle }; - NonnullOwnPtr m_board; - BitmapArray const m_marble_bitmaps; - BitmapArray const m_trace_bitmaps; - RefPtr m_score_font; - MarblePath m_marble_path {}; - int m_marble_animation_frame {}; - unsigned m_score {}; - unsigned m_high_score {}; - - static BitmapArray build_marble_color_bitmaps(); - static BitmapArray build_marble_trace_bitmaps(); - - static constexpr auto marble_pixel_size { 40 }; - static constexpr auto board_vertical_margin { 45 }; - static constexpr auto board_cell_dimension = Gfx::IntSize { 48, 48 }; - static constexpr auto number_of_marble_trace_bitmaps { 4 }; - static constexpr auto tile_color { Color::from_rgb(0xc0c0c0) }; - static constexpr auto text_color { Color::from_rgb(0x00a0ff) }; - - enum AnimationFrames { - marble_default = 0, - marble_at_top = 2, - marble_preview = 18, - marble_generating_start = 21, - marble_generating_end = 17, - marble_removing_start = 7, - marble_removing_end = 16, - number_of_marble_bounce_frames = 7 - }; - - enum TimerIntervals { - generating_marbles = 80, - removing_marbles = 60, - selected_marble = 70, - moving_marble = 28 - }; -}; diff --git a/Userland/Games/ColorLines/HueFilter.h b/Userland/Games/ColorLines/HueFilter.h deleted file mode 100644 index b48f0f99ad2..00000000000 --- a/Userland/Games/ColorLines/HueFilter.h +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright (c) 2022, Oleg Kosenkov - * Copyright (c) 2022, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include - -// This filter is similar to LibGfx/Filters/HueRotateFilter.h, however it uses -// a different formula (matrix) for hue rotation. This filter provides brighter -// colors compared to the filter provided in LibGfx. -class HueFilter : public Gfx::MatrixFilter { -public: - HueFilter(float angle_degrees) - : Gfx::MatrixFilter(calculate_hue_rotate_matrix(angle_degrees)) - { - } - - virtual bool amount_handled_in_filter() const override - { - return true; - } - - virtual StringView class_name() const override { return "HueFilter"sv; } - -private: - static FloatMatrix3x3 calculate_hue_rotate_matrix(float angle_degrees) - { - float const angle_rads = AK::to_radians(angle_degrees); - float cos_angle = 0.; - float sin_angle = 0.; - AK::sincos(angle_rads, sin_angle, cos_angle); - return FloatMatrix3x3 { - float(cos_angle + (1.0f - cos_angle) / 3.0f), - float(1.0f / 3.0f * (1.0f - cos_angle) - sqrtf(1.0f / 3.0f) * sin_angle), - float(1.0f / 3.0f * (1.0f - cos_angle) + sqrtf(1.0f / 3.0f) * sin_angle), - - float(1.0f / 3.0f * (1.0f - cos_angle) + sqrtf(1.0f / 3.0f) * sin_angle), - float(cos_angle + 1.0f / 3.0f * (1.0f - cos_angle)), - float(1.0f / 3.0f * (1.0f - cos_angle) - sqrtf(1.0f / 3.0f) * sin_angle), - - float(1.0f / 3.0f * (1.0f - cos_angle) - sqrtf(1.0f / 3.0f) * sin_angle), - float(1.0f / 3.0f * (1.0f - cos_angle) + sqrtf(1.0f / 3.0f) * sin_angle), - float(cos_angle + 1.0f / 3.0f * (1.0f - cos_angle)) - }; - } -}; diff --git a/Userland/Games/ColorLines/Marble.h b/Userland/Games/ColorLines/Marble.h deleted file mode 100644 index f72951a3f8c..00000000000 --- a/Userland/Games/ColorLines/Marble.h +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright (c) 2022, Oleg Kosenkov - * Copyright (c) 2022, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include - -class Marble final { -public: - using Point = Gfx::IntPoint; - using Color = u8; - - static constexpr int number_of_colors { 6 }; - static constexpr Color empty_cell = NumericLimits::max(); - - Marble() = default; - Marble(Point position, Color color) - : m_position { position } - , m_color { color } - { - } - - bool operator==(Marble const& other) const = default; - - [[nodiscard]] constexpr Point position() const { return m_position; } - - [[nodiscard]] constexpr Color color() const { return m_color; } - -private: - Point m_position {}; - Color m_color {}; -}; - -namespace AK { -template<> -struct Traits : public DefaultTraits { - static unsigned hash(Marble const& marble) - { - return Traits::hash(marble.position()); - } -}; -} diff --git a/Userland/Games/ColorLines/MarbleBoard.h b/Userland/Games/ColorLines/MarbleBoard.h deleted file mode 100644 index 74a4e396a91..00000000000 --- a/Userland/Games/ColorLines/MarbleBoard.h +++ /dev/null @@ -1,348 +0,0 @@ -/* - * Copyright (c) 2022, Oleg Kosenkov - * Copyright (c) 2022, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include "Marble.h" -#include "MarblePath.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -class MarbleBoard final { -public: - using Color = Marble::Color; - using Point = Gfx::IntPoint; - using PointArray = Vector; - using SelectedMarble = Marble; - using PreviewMarble = Marble; - using MarbleArray = Vector; - - static constexpr Gfx::IntSize board_size { 9, 9 }; - static constexpr size_t number_of_preview_marbles = 3; - static constexpr Color empty_cell = Marble::empty_cell; - - using PreviewMarbles = Array; - - MarbleBoard() - { - reset(); - } - - ~MarbleBoard() = default; - - MarbleBoard(MarbleBoard const&) = delete; - - [[nodiscard]] bool has_empty_cells() const - { - bool result = false; - for_each_cell([&](Point point) { - result = is_empty_cell_at(point); - return result ? IterationDecision::Break : IterationDecision::Continue; - }); - return result; - } - - [[nodiscard]] PointArray get_empty_cells() const - { - PointArray result; - for_each_cell([&](Point point) { - if (is_empty_cell_at(point)) - result.append(point); - return IterationDecision::Continue; - }); - shuffle(result); - return result; - } - - void set_preview_marble(size_t i, PreviewMarble const& marble) - { - VERIFY(i < number_of_preview_marbles); - m_preview_marbles[i] = marble; - } - - [[nodiscard]] bool place_preview_marbles_on_board() - { - if (!ensure_all_preview_marbles_are_on_empty_cells()) - return false; - for (auto const& marble : m_preview_marbles) - if (!place_preview_marble_on_board(marble)) - return false; - return true; - } - - [[nodiscard]] bool check_preview_marbles_are_valid() - { - // Check marbles pairwise and also check the board cell under this marble is empty - static_assert(number_of_preview_marbles == 3); - return m_preview_marbles[0].position() != m_preview_marbles[1].position() && m_preview_marbles[0].position() != m_preview_marbles[2].position() - && m_preview_marbles[1].position() != m_preview_marbles[2].position() - && is_empty_cell_at(m_preview_marbles[0].position()) - && is_empty_cell_at(m_preview_marbles[1].position()) - && is_empty_cell_at(m_preview_marbles[2].position()); - } - - [[nodiscard]] bool update_preview_marbles(bool use_current) - { - auto empty_cells = get_empty_cells(); - for (size_t i = 0; i < number_of_preview_marbles; ++i) { - auto marble = m_preview_marbles[i]; - // Check marbles pairwise and also check the board cell under this marble is empty - auto const is_valid_marble = [&]() { - switch (i) { - case 0: - return marble.position() != m_preview_marbles[1].position() && marble.position() != m_preview_marbles[2].position() && is_empty_cell_at(marble.position()); - case 1: - return marble.position() != m_preview_marbles[0].position() && marble.position() != m_preview_marbles[2].position() && is_empty_cell_at(marble.position()); - case 2: - return marble.position() != m_preview_marbles[0].position() && marble.position() != m_preview_marbles[1].position() && is_empty_cell_at(marble.position()); - default: - VERIFY_NOT_REACHED(); - } - }; - if (use_current && is_valid_marble()) { - continue; - } - while (!empty_cells.is_empty()) { - auto const position = empty_cells.take_last(); - Color const new_color = get_random_uniform(Marble::number_of_colors); - marble = Marble { position, new_color }; - if (!is_valid_marble()) - continue; - set_preview_marble(i, marble); - break; - } - if (empty_cells.is_empty()) - return false; - } - return empty_cells.size() > 0; - } - - [[nodiscard]] bool ensure_all_preview_marbles_are_on_empty_cells() - { - if (check_preview_marbles_are_valid()) - return true; - return update_preview_marbles(true); - } - - [[nodiscard]] Color color_at(Point point) const - { - VERIFY(in_bounds(point)); - return m_board[point.y()][point.x()]; - } - - void set_color_at(Point point, Color color) - { - VERIFY(in_bounds(point)); - m_board[point.y()][point.x()] = color; - } - - void clear_color_at(Point point) - { - set_color_at(point, empty_cell); - } - - [[nodiscard]] bool is_empty_cell_at(Point point) const - { - return color_at(point) == empty_cell; - } - - [[nodiscard]] static bool in_bounds(Point point) - { - return point.x() >= 0 && point.x() < board_size.width() && point.y() >= 0 && point.y() < board_size.height(); - } - - [[nodiscard]] bool build_marble_path(Point from, Point to, MarblePath& path) const - { - path.reset(); - - if (from == to || !MarbleBoard::in_bounds(from) || !MarbleBoard::in_bounds(to)) { - return false; - } - - struct Trace { - public: - using Value = u8; - - Trace() { reset(); } - - ~Trace() = default; - - [[nodiscard]] Value operator[](Point point) const - { - return m_map[point.y()][point.x()]; - } - - Value& operator[](Point point) - { - return m_map[point.y()][point.x()]; - } - - void reset() - { - for (size_t y = 0; y < board_size.height(); ++y) - for (size_t x = 0; x < board_size.width(); ++x) - m_map[y][x] = NumericLimits::max(); - } - - private: - BoardMap m_map; - }; - - Trace trace; - trace[from] = 1; - - Queue queue; - queue.enqueue(from); - - auto add_path_point = [&](Point point, u8 value) { - if (MarbleBoard::in_bounds(point) && is_empty_cell_at(point) && trace[point] > value) { - trace[point] = value; - queue.enqueue(point); - } - }; - - constexpr Point connected_four_ways[4] = { - { 0, -1 }, // to the top - { 0, 1 }, // to the bottom - { -1, 0 }, // to the left - { 1, 0 } // to the right - }; - - while (!queue.is_empty()) { - auto current = queue.dequeue(); - if (current == to) { - while (current != from) { - path.add_point(current); - for (auto delta : connected_four_ways) - if (auto next = current.translated(delta); MarbleBoard::in_bounds(next) && trace[next] < trace[current]) { - current = next; - break; - } - } - path.add_point(current); - return true; - } - for (auto delta : connected_four_ways) - add_path_point(current.translated(delta), trace[current] + 1); - } - return false; - } - - [[nodiscard]] bool check_and_remove_marbles() - { - m_removed_marbles.clear(); - constexpr Point connected_four_ways[] = { - { -1, 0 }, // to the left - { 0, -1 }, // to the top - { -1, -1 }, // to the top-left - { 1, -1 } // to the top-right - }; - HashTable> marbles; - for_each_cell([&](Point current_point) { - if (is_empty_cell_at(current_point)) - return IterationDecision::Continue; - auto const color { color_at(current_point) }; - for (auto direction : connected_four_ways) { - size_t marble_count = 0; - for (auto p = current_point; in_bounds(p) && color_at(p) == color; p.translate_by(direction)) - ++marble_count; - if (marble_count >= number_of_marbles_to_remove) - for (auto p = current_point; in_bounds(p) && color_at(p) == color; p.translate_by(direction)) - marbles.set({ p, color }); - } - return IterationDecision::Continue; - }); - m_removed_marbles.ensure_capacity(marbles.size()); - for (auto const& marble : marbles) { - m_removed_marbles.append(marble); - clear_color_at(marble.position()); - } - return !m_removed_marbles.is_empty(); - } - - [[nodiscard]] PreviewMarbles const& preview_marbles() const - { - return m_preview_marbles; - } - - [[nodiscard]] bool has_selected_marble() const - { - return m_selected_marble != nullptr; - } - - [[nodiscard]] SelectedMarble const& selected_marble() const - { - VERIFY(has_selected_marble()); - return *m_selected_marble; - } - - [[nodiscard]] bool select_marble(Point point) - { - if (!is_empty_cell_at(point)) { - m_selected_marble = make(point, color_at(point)); - return true; - } - return false; - } - - void reset_selection() - { - m_selected_marble.clear(); - } - - [[nodiscard]] MarbleArray const& removed_marbles() const - { - return m_removed_marbles; - } - - void reset() - { - reset_selection(); - for (size_t i = 0; i < number_of_preview_marbles; ++i) - m_preview_marbles[i] = { { 0, 0 }, empty_cell }; - m_removed_marbles.clear(); - for_each_cell([&](Point point) { - set_color_at(point, empty_cell); - return IterationDecision::Continue; - }); - } - -private: - static void for_each_cell(Function functor) - { - for (int y = 0; y < board_size.height(); ++y) - for (int x = 0; x < board_size.width(); ++x) - if (functor({ x, y }) == IterationDecision::Break) - return; - } - - [[nodiscard]] bool place_preview_marble_on_board(PreviewMarble const& marble) - { - if (!is_empty_cell_at(marble.position())) - return false; - set_color_at(marble.position(), marble.color()); - return true; - } - - static constexpr int number_of_marbles_to_remove { 5 }; - - using Row = Array; - using BoardMap = Array; - - BoardMap m_board; - PreviewMarbles m_preview_marbles; - MarbleArray m_removed_marbles; - OwnPtr m_selected_marble {}; -}; diff --git a/Userland/Games/ColorLines/MarblePath.h b/Userland/Games/ColorLines/MarblePath.h deleted file mode 100644 index e6d178e988b..00000000000 --- a/Userland/Games/ColorLines/MarblePath.h +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright (c) 2022, Oleg Kosenkov - * Copyright (c) 2022, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include - -class MarblePath final { -public: - using Point = Gfx::IntPoint; - - MarblePath() = default; - - void add_point(Point point) - { - m_path.append(point); - } - - [[nodiscard]] bool is_empty() const - { - return m_path.is_empty(); - } - - [[nodiscard]] bool contains(Point point) const - { - return m_path.contains_slow(point); - } - - [[nodiscard]] size_t remaining_steps() const - { - return m_path.size(); - } - - [[nodiscard]] Point current_point() const - { - VERIFY(!m_path.is_empty()); - return m_path.last(); - } - - [[nodiscard]] Point next_point() - { - auto const point = current_point(); - m_path.resize(m_path.size() - 1); - return point; - } - - [[nodiscard]] Point operator[](size_t index) const - { - return m_path[index]; - } - - void reset() - { - m_path.clear(); - } - -private: - Vector m_path; -}; diff --git a/Userland/Games/ColorLines/main.cpp b/Userland/Games/ColorLines/main.cpp deleted file mode 100644 index 61d5112ca82..00000000000 --- a/Userland/Games/ColorLines/main.cpp +++ /dev/null @@ -1,80 +0,0 @@ -/* - * Copyright (c) 2022, Oleg Kosenkov - * Copyright (c) 2022, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "ColorLines.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -ErrorOr serenity_main(Main::Arguments arguments) -{ - TRY(Core::System::pledge("stdio rpath recvfd sendfd unix")); - - auto app = TRY(GUI::Application::create(arguments)); - - auto const app_name = "ColorLines"sv; - auto const title = "Color Lines"_string; - auto const man_file = "/usr/share/man/man6/ColorLines.md"sv; - - Config::pledge_domain(app_name); - - TRY(Desktop::Launcher::add_allowed_handler_with_only_specific_urls("/bin/Help", { URL::create_with_file_scheme(man_file) })); - TRY(Desktop::Launcher::seal_allowlist()); - - TRY(Core::System::pledge("stdio rpath recvfd sendfd")); - - TRY(Core::System::unveil("/tmp/session/%sid/portal/launch", "rw")); - TRY(Core::System::unveil("/res", "r")); - TRY(Core::System::unveil(nullptr, nullptr)); - - auto app_icon = TRY(GUI::Icon::try_create_default_icon("app-colorlines"sv)); - - auto window = GUI::Window::construct(); - - window->set_double_buffering_enabled(false); - window->set_title(title.bytes_as_string_view()); - window->resize(436, 481); - window->set_resizable(false); - - auto game = window->set_main_widget(app_name); - - auto game_menu = window->add_menu("&Game"_string); - - game_menu->add_action(GUI::Action::create("&New Game", { Mod_None, Key_F2 }, TRY(Gfx::Bitmap::load_from_file("/res/icons/16x16/reload.png"sv)), [&](auto&) { - game->reset(); - })); - game_menu->add_separator(); - game_menu->add_action(GUI::CommonActions::make_quit_action([](auto&) { - GUI::Application::the()->quit(); - })); - - auto view_menu = window->add_menu("&View"_string); - view_menu->add_action(GUI::CommonActions::make_fullscreen_action([&](auto&) { - window->set_fullscreen(!window->is_fullscreen()); - })); - - auto help_menu = window->add_menu("&Help"_string); - help_menu->add_action(GUI::CommonActions::make_command_palette_action(window)); - help_menu->add_action(GUI::CommonActions::make_help_action([&man_file](auto&) { - Desktop::Launcher::open(URL::create_with_file_scheme(man_file), "/bin/Help"); - })); - help_menu->add_action(GUI::CommonActions::make_about_action(title, app_icon, window)); - - window->show(); - - window->set_icon(app_icon.bitmap_for_size(16)); - - return app->exec(); -} diff --git a/Userland/Games/FlappyBug/CMakeLists.txt b/Userland/Games/FlappyBug/CMakeLists.txt deleted file mode 100644 index 2dc62c1e34d..00000000000 --- a/Userland/Games/FlappyBug/CMakeLists.txt +++ /dev/null @@ -1,13 +0,0 @@ -serenity_component( - FlappyBug - RECOMMENDED - TARGETS FlappyBug -) - -set(SOURCES - main.cpp - Game.cpp -) - -serenity_app(FlappyBug ICON app-flappybug) -target_link_libraries(FlappyBug PRIVATE LibCore LibGfx LibGUI LibConfig LibMain LibDesktop LibURL) diff --git a/Userland/Games/FlappyBug/Game.cpp b/Userland/Games/FlappyBug/Game.cpp deleted file mode 100644 index b7316b36e47..00000000000 --- a/Userland/Games/FlappyBug/Game.cpp +++ /dev/null @@ -1,162 +0,0 @@ -/* - * Copyright (c) 2021, Mim Hufford - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "Game.h" - -namespace FlappyBug { - -Game::Game(Bug bug, Cloud cloud) - : m_bug(move(bug)) - , m_cloud(move(cloud)) -{ - set_override_cursor(Gfx::StandardCursor::Hidden); - start_timer(16); - reset(); -} - -void Game::reset() -{ - m_active = false; - m_last_score = m_difficulty; - m_difficulty = 1; - m_restart_cooldown = 3; - m_bug.reset(); - m_obstacle.reset(); -} - -void Game::game_over() -{ - if (on_game_end) - m_high_score = on_game_end(get_final_score(m_difficulty)); - - reset(); -} - -bool Game::ready_to_start() const -{ - if (!m_high_score.has_value()) { - return true; - } - - if (m_restart_cooldown <= 0) { - return true; - } - - return false; -} - -void Game::timer_event(Core::TimerEvent&) -{ - tick(); -} - -void Game::paint_event(GUI::PaintEvent& event) -{ - GUI::Frame::paint_event(event); - - GUI::Painter painter(*this); - painter.add_clip_rect(frame_inner_rect()); - painter.add_clip_rect(event.rect()); - - painter.draw_tiled_bitmap(frame_inner_rect(), *m_background_bitmap); - - painter.draw_scaled_bitmap(m_cloud.rect(), *m_cloud.bitmap(), m_cloud.bitmap()->rect(), 0.2f); - - painter.fill_rect(enclosing_int_rect(m_obstacle.top_rect()), m_obstacle.color); - painter.fill_rect(enclosing_int_rect(m_obstacle.bottom_rect()), m_obstacle.color); - - painter.draw_scaled_bitmap(enclosing_int_rect(m_bug.rect()), *m_bug.current_bitmap(), m_bug.flapping_bitmap->rect()); - - if (m_active) { - painter.draw_text(m_score_rect, String::formatted("{:.0}", m_difficulty).release_value_but_fixme_should_propagate_errors(), Gfx::TextAlignment::TopLeft, Color::White); - } else if (m_high_score.has_value()) { - auto message = String::formatted("Your score: {}\nHigh score: {}\n\n{}", get_final_score(m_last_score), m_high_score.value(), m_restart_cooldown < 0 ? "Press any key to play again" : " ").release_value_but_fixme_should_propagate_errors(); - painter.draw_text(m_text_rect, message, Gfx::TextAlignment::Center, Color::White); - } else { - painter.draw_text(m_text_rect, "Press any key to start"sv, Gfx::TextAlignment::Center, Color::White); - } -} - -void Game::keydown_event(GUI::KeyEvent& event) -{ - if (event.modifiers() || event.key() == Key_F1 || event.key() == Key_F11) { - event.ignore(); - return; - } - switch (event.key()) { - case Key_Escape: - GUI::Application::the()->quit(); - break; - default: - player_input(); - break; - } -} - -void Game::mousedown_event(GUI::MouseEvent&) -{ - player_input(); -} - -void Game::player_input() -{ - if (ready_to_start()) { - m_active = true; - } - if (m_active) { - m_bug.flap(); - } -} - -void Game::tick() -{ - auto queue_update = [&]() { - update(m_score_rect); - update(m_text_rect); - update(enclosing_int_rect(m_bug.rect())); - update(enclosing_int_rect(m_obstacle.top_rect())); - update(enclosing_int_rect(m_obstacle.bottom_rect())); - update(m_cloud.rect()); - }; - - if (m_active) { - queue_update(); - - m_difficulty += 1.0f / 16.0f; - - m_bug.fall(); - m_bug.apply_velocity(); - m_obstacle.x -= 4 + m_difficulty / 16.0f; - m_cloud.x -= m_difficulty / 16.0f; - - if (m_bug.y > game_height || m_bug.y < 0) { - game_over(); - } - - if (m_bug.rect().intersects(m_obstacle.top_rect()) || m_bug.rect().intersects(m_obstacle.bottom_rect())) { - game_over(); - } - - if (m_obstacle.x < 0) { - m_obstacle.reset(); - } - - if (m_cloud.x < 0) { - m_cloud.reset(); - } - } - - m_restart_cooldown -= 1.0f / 16.0f; - - queue_update(); -} - -u32 Game::get_final_score(float score) -{ - return static_cast(roundf(score)); -} - -} diff --git a/Userland/Games/FlappyBug/Game.h b/Userland/Games/FlappyBug/Game.h deleted file mode 100644 index d58a0f8ec10..00000000000 --- a/Userland/Games/FlappyBug/Game.h +++ /dev/null @@ -1,185 +0,0 @@ -/* - * Copyright (c) 2021, Mim Hufford - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace FlappyBug { - -class Game final : public GUI::Frame { - C_OBJECT(Game); - -public: - static constexpr int game_width = 560; - static constexpr int game_height = 480; - - Function on_game_end; - -private: - virtual void paint_event(GUI::PaintEvent&) override; - virtual void keydown_event(GUI::KeyEvent&) override; - virtual void mousedown_event(GUI::MouseEvent&) override; - virtual void timer_event(Core::TimerEvent&) override; - - void tick(); - void reset(); - void game_over(); - bool ready_to_start() const; - void player_input(); - - static u32 get_final_score(float score); - -public: - struct Bug { - float const x { 50 }; - float const radius { 16 }; - float const starting_y { 200 }; - NonnullRefPtr falling_bitmap; - NonnullRefPtr flapping_bitmap; - float y {}; - float velocity {}; - - private: - Bug(NonnullRefPtr falling_bitmap_value, NonnullRefPtr flapping_bitmap_value) - : falling_bitmap(move(falling_bitmap_value)) - , flapping_bitmap(move(flapping_bitmap_value)) - { - } - - public: - static ErrorOr construct() - { - NonnullRefPtr falling_bitmap = TRY(Gfx::Bitmap::load_from_file("/res/graphics/flappybug/falling.png"sv)); - NonnullRefPtr flapping_bitmap = TRY(Gfx::Bitmap::load_from_file("/res/graphics/flappybug/flapping.png"sv)); - return Bug(move(falling_bitmap), move(flapping_bitmap)); - } - - void reset() - { - y = starting_y; - } - - NonnullRefPtr current_bitmap() const - { - return velocity < 0 ? falling_bitmap : flapping_bitmap; - } - - Gfx::FloatRect rect() const - { - return { x - radius, y - radius, radius * 2, radius * 2 }; - } - - void flap() - { - float const flap_strength = 10.0f; - velocity = -flap_strength; - } - - void fall() - { - float const gravity = 1.0f; - velocity += gravity; - } - - void apply_velocity() - { - y += velocity; - } - }; - - struct Obstacle { - float const width { 20 }; - Color color { Color::DarkGray }; - float x {}; - float gap_top_y { 200 }; - float gap_height { 175 }; - - void reset() - { - x = game_width + width; - gap_top_y = get_random_uniform(game_height - gap_height); - } - - Gfx::FloatRect top_rect() const - { - return { x - width, 0, width, gap_top_y }; - } - - Gfx::FloatRect bottom_rect() const - { - return { x - width, gap_top_y + gap_height, width, game_height - gap_top_y - gap_height }; - } - }; - - struct Cloud { - Vector> const cloud_bitmaps; - float x {}; - float y {}; - int bitmap_id {}; - - private: - Cloud(Vector> const cloud_bitmaps_value) - : cloud_bitmaps(move(cloud_bitmaps_value)) - { - reset(); - x = get_random_uniform(game_width); - } - - public: - static ErrorOr construct() - { - Vector> const cloud_bitmaps { - TRY(Gfx::Bitmap::load_from_file("/res/graphics/flappybug/cloud_0.png"sv)), - TRY(Gfx::Bitmap::load_from_file("/res/graphics/flappybug/cloud_1.png"sv)), - TRY(Gfx::Bitmap::load_from_file("/res/graphics/flappybug/cloud_2.png"sv)), - }; - return Cloud(move(cloud_bitmaps)); - } - - void reset() - { - bitmap_id = get_random_uniform(cloud_bitmaps.size()); - x = game_width + bitmap()->width(); - y = get_random_uniform(game_height / 2) + bitmap()->height(); - } - - NonnullRefPtr bitmap() const - { - return cloud_bitmaps[bitmap_id]; - } - - Gfx::IntRect rect() const - { - return { (int)x - bitmap()->width(), (int)y - bitmap()->height(), bitmap()->width(), bitmap()->height() }; - } - }; - -private: - Bug m_bug; - Obstacle m_obstacle; - Cloud m_cloud; - bool m_active; - Optional m_high_score {}; - float m_last_score {}; - float m_difficulty {}; - float m_restart_cooldown {}; - NonnullRefPtr m_background_bitmap { Gfx::Bitmap::load_from_file("/res/graphics/flappybug/background.png"sv).release_value_but_fixme_should_propagate_errors() }; - Gfx::IntRect const m_score_rect { 10, 10, 20, 20 }; - Gfx::IntRect const m_text_rect { game_width / 2 - 80, game_height / 2 - 40, 160, 80 }; - - Game(Bug, Cloud); -}; - -} diff --git a/Userland/Games/FlappyBug/main.cpp b/Userland/Games/FlappyBug/main.cpp deleted file mode 100644 index b3345d4077c..00000000000 --- a/Userland/Games/FlappyBug/main.cpp +++ /dev/null @@ -1,78 +0,0 @@ -/* - * Copyright (c) 2021, Mim Hufford - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "Game.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -ErrorOr serenity_main(Main::Arguments arguments) -{ - TRY(Core::System::pledge("stdio rpath recvfd sendfd unix")); - - auto app = TRY(GUI::Application::create(arguments)); - - Config::pledge_domain("FlappyBug"); - - TRY(Desktop::Launcher::add_allowed_handler_with_only_specific_urls("/bin/Help", { URL::create_with_file_scheme("/usr/share/man/man6/FlappyBug.md") })); - TRY(Desktop::Launcher::seal_allowlist()); - - TRY(Core::System::pledge("stdio rpath recvfd sendfd")); - - TRY(Core::System::unveil("/tmp/session/%sid/portal/launch", "rw")); - TRY(Core::System::unveil("/res", "r")); - TRY(Core::System::unveil(nullptr, nullptr)); - - u32 high_score = Config::read_i32("FlappyBug"sv, "Game"sv, "HighScore"sv, 0); - - auto window = GUI::Window::construct(); - window->resize(FlappyBug::Game::game_width, FlappyBug::Game::game_height); - auto app_icon = TRY(GUI::Icon::try_create_default_icon("app-flappybug"sv)); - window->set_icon(app_icon.bitmap_for_size(16)); - window->set_title("Flappy Bug"); - window->set_double_buffering_enabled(false); - window->set_resizable(false); - auto widget = window->set_main_widget(TRY(FlappyBug::Game::Bug::construct()), TRY(FlappyBug::Game::Cloud::construct())); - - widget->on_game_end = [&](u32 score) { - if (score <= high_score) - return high_score; - - Config::write_i32("FlappyBug"sv, "Game"sv, "HighScore"sv, score); - high_score = score; - - return high_score; - }; - - auto game_menu = window->add_menu("&Game"_string); - game_menu->add_action(GUI::CommonActions::make_quit_action([](auto&) { - GUI::Application::the()->quit(); - })); - - auto view_menu = window->add_menu("&View"_string); - view_menu->add_action(GUI::CommonActions::make_fullscreen_action([&](auto&) { - window->set_fullscreen(!window->is_fullscreen()); - })); - - auto help_menu = window->add_menu("&Help"_string); - help_menu->add_action(GUI::CommonActions::make_command_palette_action(window)); - help_menu->add_action(GUI::CommonActions::make_help_action([](auto&) { - Desktop::Launcher::open(URL::create_with_file_scheme("/usr/share/man/man6/FlappyBug.md"), "/bin/Help"); - })); - help_menu->add_action(GUI::CommonActions::make_about_action("Flappy Bug"_string, app_icon, window)); - - window->show(); - - return app->exec(); -} diff --git a/Userland/Games/Flood/Board.cpp b/Userland/Games/Flood/Board.cpp deleted file mode 100644 index dbc8bfdd751..00000000000 --- a/Userland/Games/Flood/Board.cpp +++ /dev/null @@ -1,128 +0,0 @@ -/* - * Copyright (c) 2022, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "Board.h" -#include -#include - -Board::Board(size_t rows, size_t columns) -{ - resize(rows, columns); -} - -void Board::clear() -{ - for (size_t row = 0; row < m_rows; ++row) { - for (size_t column = 0; column < m_columns; ++column) { - set_cell(row, column, 0); - } - } -} - -bool Board::is_flooded() const -{ - auto first_cell_value = cell(0, 0); - for (size_t row = 0; row < rows(); ++row) { - for (size_t column = 0; column < columns(); ++column) { - if (first_cell_value == cell(row, column)) - continue; - return false; - } - } - return true; -} - -void Board::randomize() -{ - for (size_t row = 0; row < m_rows; ++row) { - for (size_t column = 0; column < m_columns; ++column) { - set_cell(row, column, get_random_uniform(m_colors.size())); - } - } - m_previous_value = m_cells[0][0]; - m_current_value = m_cells[0][0]; -} - -void Board::resize(size_t rows, size_t columns) -{ - m_rows = rows; - m_columns = columns; - - // Vector values get default-initialized, we don't need to set them explicitly. - m_cells.resize(rows); - for (size_t row = 0; row < rows; ++row) - m_cells[row].resize(columns); -} - -void Board::set_cell(size_t row, size_t column, int value) -{ - VERIFY(row < m_rows && column < m_columns); - - m_cells[row][column] = value; -} - -int Board::cell(size_t row, size_t column) const -{ - return m_cells[row][column]; -} - -void Board::set_current_value(int new_value) -{ - m_previous_value = m_current_value; - m_current_value = new_value; -} - -void Board::set_color_scheme(Vector colors) -{ - VERIFY(colors.size() == 8); - m_colors = move(colors); -} - -void Board::reset() -{ - clear(); - m_current_value = 0; - m_previous_value = 0; -} - -// Adapted from Userland/PixelPaint/Tools/BucketTool.cpp::flood_fill. -u32 Board::update_values(bool only_calculate_flooded_area) -{ - Queue points_to_visit; - - points_to_visit.enqueue({ 0, 0 }); - set_cell(0, 0, get_current_value()); - - Vector> visited_board; - visited_board.resize(cells().size()); - for (size_t row = 0; row < cells().size(); ++row) - visited_board[row].resize(cells()[row].size()); - u32 painted = 1; - - // This implements a non-recursive flood fill. This is a breadth-first search of paintable neighbors - // As we find neighbors that are paintable we update their pixel, add them to the queue, and mark them in the "visited_board". - while (!points_to_visit.is_empty()) { - auto current_point = points_to_visit.dequeue(); - auto candidate_points = Array { - current_point.moved_left(1), - current_point.moved_right(1), - current_point.moved_up(1), - current_point.moved_down(1) - }; - for (auto candidate_point : candidate_points) { - if (candidate_point.y() >= static_cast(m_rows) || candidate_point.x() >= static_cast(m_columns) || candidate_point.y() < 0 || candidate_point.x() < 0) - continue; - if (!visited_board[candidate_point.y()][candidate_point.x()] && cell(candidate_point.y(), candidate_point.x()) == (only_calculate_flooded_area ? get_current_value() : get_previous_value())) { - ++painted; - points_to_visit.enqueue(candidate_point); - visited_board[candidate_point.y()][candidate_point.x()] = true; - if (!only_calculate_flooded_area) - set_cell(candidate_point.y(), candidate_point.x(), get_current_value()); - } - } - } - return painted; -} diff --git a/Userland/Games/Flood/Board.h b/Userland/Games/Flood/Board.h deleted file mode 100644 index 08526aa81f2..00000000000 --- a/Userland/Games/Flood/Board.h +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright (c) 2022, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include -#include - -class Board { -public: - Board(size_t rows, size_t columns); - ~Board() = default; - - size_t columns() const { return m_columns; } - size_t rows() const { return m_rows; } - - bool is_flooded() const; - void set_cell(size_t row, size_t column, int value); - int cell(size_t row, size_t column) const; - auto const& cells() const { return m_cells; } - - void clear(); - void randomize(); - void reset(); - void resize(size_t rows, size_t columns); - u32 update_values(bool only_calculate_flooded_area = false); - - int get_current_value() { return m_current_value; } - int get_previous_value() { return m_previous_value; } - Vector get_color_scheme() { return m_colors; } - - void set_current_value(int new_value); - void set_color_scheme(Vector colors); - - struct RowAndColumn { - size_t row { 0 }; - size_t column { 0 }; - }; - -private: - size_t m_rows { 0 }; - size_t m_columns { 0 }; - - int m_current_value; - int m_previous_value; - - Vector m_colors; - Vector> m_cells; -}; diff --git a/Userland/Games/Flood/BoardWidget.cpp b/Userland/Games/Flood/BoardWidget.cpp deleted file mode 100644 index 306b88a5e71..00000000000 --- a/Userland/Games/Flood/BoardWidget.cpp +++ /dev/null @@ -1,113 +0,0 @@ -/* - * Copyright (c) 2022, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "BoardWidget.h" - -#include -#include -#include - -BoardWidget::BoardWidget(size_t rows, size_t columns) - : m_board(make(rows, columns)) -{ - update_color_scheme(); -} - -void BoardWidget::update_color_scheme() -{ - auto const& palette = GUI::Widget::palette(); - m_board->set_color_scheme({ - palette.bright_black(), - palette.bright_red(), - palette.bright_green(), - palette.bright_yellow(), - palette.bright_blue(), - palette.bright_magenta(), - palette.bright_cyan(), - palette.bright_white(), - }); - m_background_color = palette.background(); -} - -void BoardWidget::resize_board(size_t rows, size_t columns) -{ - if (columns == m_board->columns() && rows == m_board->rows()) - return; - m_board->resize(rows, columns); -} - -int BoardWidget::get_cell_size() const -{ - int width = rect().width() / m_board->columns(); - int height = rect().height() / m_board->rows(); - - return min(width, height); -} - -Gfx::IntSize BoardWidget::get_board_offset() const -{ - int cell_size = get_cell_size(); - return { - (width() - cell_size * m_board->columns()) / 2, - (height() - cell_size * m_board->rows()) / 2, - }; -} - -void BoardWidget::event(Core::Event& event) -{ - if (event.type() == GUI::Event::ThemeChange) - update_color_scheme(); - GUI::Frame::event(event); -} - -void BoardWidget::paint_event(GUI::PaintEvent& event) -{ - GUI::Widget::paint_event(event); - - GUI::Painter painter(*this); - painter.add_clip_rect(event.rect()); - painter.fill_rect(event.rect(), m_background_color); - - int cell_size = get_cell_size(); - Gfx::IntSize board_offset = get_board_offset(); - - for (size_t row = 0; row < m_board->rows(); ++row) { - for (size_t column = 0; column < m_board->columns(); ++column) { - int cell_x = column * cell_size + board_offset.width(); - int cell_y = row * cell_size + board_offset.height(); - - Gfx::Rect cell_rect(cell_x, cell_y, cell_size, cell_size); - Color fill_color = m_board->get_color_scheme()[m_board->cell(row, column)]; - painter.fill_rect(cell_rect, fill_color); - } - } -} - -void BoardWidget::mousedown_event(GUI::MouseEvent& event) -{ - if (event.button() == GUI::MouseButton::Primary && on_move) { - auto row_and_column = get_row_and_column_for_point(event.x(), event.y()); - if (!row_and_column.has_value()) - return; - on_move(row_and_column.value()); - } -} - -Optional BoardWidget::get_row_and_column_for_point(int x, int y) const -{ - auto board_offset = get_board_offset(); - auto cell_size = get_cell_size(); - auto board_width = m_board->columns() * cell_size; - auto board_height = m_board->rows() * cell_size; - if (x <= board_offset.width() || static_cast(x) >= board_offset.width() + board_width) - return {}; - if (y <= board_offset.height() || static_cast(y) >= board_offset.height() + board_height) - return {}; - return { { - .row = static_cast((y - board_offset.height()) / cell_size), - .column = static_cast((x - board_offset.width()) / cell_size), - } }; -} diff --git a/Userland/Games/Flood/BoardWidget.h b/Userland/Games/Flood/BoardWidget.h deleted file mode 100644 index 645dfcec1b4..00000000000 --- a/Userland/Games/Flood/BoardWidget.h +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright (c) 2022, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include "Board.h" -#include -#include -#include -#include -#include -#include - -class BoardWidget final : public GUI::Frame { - C_OBJECT(BoardWidget); - -public: - Function on_move; - - int get_cell_size() const; - Gfx::IntSize get_board_offset() const; - - Optional get_row_and_column_for_point(int x, int y) const; - - void resize_board(size_t rows, size_t columns); - Board* board() { return m_board.ptr(); } - -private: - BoardWidget(size_t rows, size_t columns); - void update_color_scheme(); - NonnullOwnPtr m_board; - - virtual void event(Core::Event&) override; - virtual void paint_event(GUI::PaintEvent&) override; - virtual void mousedown_event(GUI::MouseEvent&) override; - - Color m_background_color = Color::Black; -}; diff --git a/Userland/Games/Flood/CMakeLists.txt b/Userland/Games/Flood/CMakeLists.txt deleted file mode 100644 index 9c466e6b1db..00000000000 --- a/Userland/Games/Flood/CMakeLists.txt +++ /dev/null @@ -1,20 +0,0 @@ -serenity_component( - Flood - RECOMMENDED - TARGETS Flood -) - -compile_gml(FloodWindow.gml FloodWindowGML.cpp) -compile_gml(SettingsWidget.gml SettingsWidgetGML.cpp) - -set(SOURCES - Board.cpp - BoardWidget.cpp - FloodWindowGML.cpp - SettingsDialog.cpp - SettingsWidgetGML.cpp - main.cpp -) - -serenity_app(Flood ICON app-flood) -target_link_libraries(Flood PRIVATE LibConfig LibCore LibDesktop LibGfx LibGUI LibMain LibURL) diff --git a/Userland/Games/Flood/FloodWindow.gml b/Userland/Games/Flood/FloodWindow.gml deleted file mode 100644 index a556732a44a..00000000000 --- a/Userland/Games/Flood/FloodWindow.gml +++ /dev/null @@ -1,17 +0,0 @@ -@Flood::MainWidget { - fill_with_background_color: true - layout: @GUI::VerticalBoxLayout {} - - @GUI::Widget { - layout: @GUI::VerticalBoxLayout {} - - @GUI::Widget { - name: "board_widget_container" - layout: @GUI::VerticalBoxLayout {} - } - - @GUI::Statusbar { - name: "statusbar" - } - } -} diff --git a/Userland/Games/Flood/MainWidget.h b/Userland/Games/Flood/MainWidget.h deleted file mode 100644 index 09889ab6dc7..00000000000 --- a/Userland/Games/Flood/MainWidget.h +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright (c) 2023, the SerenityOS developers - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include - -namespace Flood { - -class MainWidget : public GUI::Widget { - C_OBJECT_ABSTRACT(MainWidget) -public: - static ErrorOr> try_create(); - virtual ~MainWidget() override = default; - -private: - MainWidget() = default; -}; - -} diff --git a/Userland/Games/Flood/SettingsDialog.cpp b/Userland/Games/Flood/SettingsDialog.cpp deleted file mode 100644 index d927acbac4d..00000000000 --- a/Userland/Games/Flood/SettingsDialog.cpp +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright (c) 2022, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "SettingsDialog.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -ErrorOr> SettingsDialog::try_create(GUI::Window* parent, size_t board_rows, size_t board_columns) -{ - auto settings_widget = TRY(Flood::SettingsWidget::try_create()); - auto settings_dialog = TRY(adopt_nonnull_ref_or_enomem(new (nothrow) SettingsDialog(move(settings_widget), move(parent), move(board_rows), move(board_columns)))); - return settings_dialog; -} - -SettingsDialog::SettingsDialog(NonnullRefPtr settings_widget, GUI::Window* parent, size_t board_rows, size_t board_columns) - : GUI::Dialog(parent) - , m_board_rows(board_rows) - , m_board_columns(board_columns) -{ - set_rect({ 0, 0, 250, 150 }); - set_title("New Game"); - set_icon(parent->icon()); - set_resizable(false); - - set_main_widget(settings_widget); - - auto board_rows_spinbox = settings_widget->find_descendant_of_type_named("board_rows_spinbox"); - board_rows_spinbox->set_value(m_board_rows); - - board_rows_spinbox->on_change = [&](auto value) { - m_board_rows = value; - }; - - auto board_columns_spinbox = settings_widget->find_descendant_of_type_named("board_columns_spinbox"); - board_columns_spinbox->set_value(m_board_columns); - - board_columns_spinbox->on_change = [&](auto value) { - m_board_columns = value; - }; - - auto cancel_button = settings_widget->find_descendant_of_type_named("cancel_button"); - cancel_button->on_click = [this](auto) { - done(ExecResult::Cancel); - }; - - auto ok_button = settings_widget->find_descendant_of_type_named("ok_button"); - ok_button->on_click = [this](auto) { - done(ExecResult::OK); - }; -} diff --git a/Userland/Games/Flood/SettingsDialog.h b/Userland/Games/Flood/SettingsDialog.h deleted file mode 100644 index 15d133e2f6a..00000000000 --- a/Userland/Games/Flood/SettingsDialog.h +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright (c) 2022, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include "SettingsWidget.h" -#include -#include - -class SettingsDialog : public GUI::Dialog { - C_OBJECT_ABSTRACT(SettingsDialog) -public: - static ErrorOr> try_create(GUI::Window* parent, size_t board_rows, size_t board_columns); - size_t board_rows() const { return m_board_rows; } - size_t board_columns() const { return m_board_columns; } - -private: - SettingsDialog(NonnullRefPtr settings_widget, GUI::Window* parent, size_t board_rows, size_t board_columns); - - size_t m_board_rows; - size_t m_board_columns; -}; diff --git a/Userland/Games/Flood/SettingsWidget.gml b/Userland/Games/Flood/SettingsWidget.gml deleted file mode 100644 index 3a365b99e21..00000000000 --- a/Userland/Games/Flood/SettingsWidget.gml +++ /dev/null @@ -1,56 +0,0 @@ -@Flood::SettingsWidget { - fill_with_background_color: true - layout: @GUI::VerticalBoxLayout { - margins: [4] - } - - @GUI::Widget { - layout: @GUI::HorizontalBoxLayout { - spacing: 4 - } - - @GUI::Label { - text: "Board rows:" - text_alignment: "CenterLeft" - } - - @GUI::SpinBox { - name: "board_rows_spinbox" - max: 32 - min: 2 - } - } - - @GUI::Widget { - layout: @GUI::HorizontalBoxLayout { - spacing: 4 - } - - @GUI::Label { - text: "Board columns:" - text_alignment: "CenterLeft" - } - - @GUI::SpinBox { - name: "board_columns_spinbox" - max: 32 - min: 2 - } - } - - @GUI::Widget { - layout: @GUI::HorizontalBoxLayout { - spacing: 10 - } - - @GUI::Button { - name: "cancel_button" - text: "Cancel" - } - - @GUI::Button { - name: "ok_button" - text: "OK" - } - } -} diff --git a/Userland/Games/Flood/SettingsWidget.h b/Userland/Games/Flood/SettingsWidget.h deleted file mode 100644 index e7532d1f4ae..00000000000 --- a/Userland/Games/Flood/SettingsWidget.h +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright (c) 2023, the SerenityOS developers - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include - -namespace Flood { - -class SettingsWidget : public GUI::Widget { - C_OBJECT_ABSTRACT(SettingsWidget) -public: - static ErrorOr> try_create(); - virtual ~SettingsWidget() override = default; - -private: - SettingsWidget() = default; -}; - -} diff --git a/Userland/Games/Flood/main.cpp b/Userland/Games/Flood/main.cpp deleted file mode 100644 index 62807f62cc5..00000000000 --- a/Userland/Games/Flood/main.cpp +++ /dev/null @@ -1,207 +0,0 @@ -/* - * Copyright (c) 2022, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "BoardWidget.h" -#include "MainWidget.h" -#include "SettingsDialog.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -// FIXME: Improve this AI. -// Currently, this AI always chooses a move that gets the most cells flooded immediately. -// This far from being able to generate an optimal solution, and is something that needs to be improved -// if a user-facing auto-solver is implemented or a harder difficulty is wanted. -// A fairly simple way to improve this would be to test deeper moves and then choose the most efficient sequence. -static int get_number_of_moves_from_ai(Board const& board) -{ - Board ai_board { board }; - auto const color_scheme = ai_board.get_color_scheme(); - ai_board.set_current_value(ai_board.cell(0, 0)); - int moves { 0 }; - while (!ai_board.is_flooded()) { - ++moves; - int most_painted = 0; - int best_value = ai_board.cell(0, 0); - for (size_t i = 0; i < color_scheme.size(); ++i) { - Board test_board { ai_board }; - test_board.set_current_value(i); - // The first update applies the current value, and the second update is done to obtain the new area. - test_board.update_values(); - int new_area = test_board.update_values(true); - if (new_area > most_painted) { - most_painted = new_area; - best_value = i; - } - } - ai_board.set_current_value(best_value); - ai_board.update_values(); - } - return moves; -} - -ErrorOr serenity_main(Main::Arguments arguments) -{ - TRY(Core::System::pledge("stdio rpath recvfd sendfd unix")); - auto app = TRY(GUI::Application::create(arguments)); - auto app_icon = TRY(GUI::Icon::try_create_default_icon("app-flood"sv)); - - auto window = GUI::Window::construct(); - - Config::pledge_domain("Flood"); - - TRY(Desktop::Launcher::add_allowed_handler_with_only_specific_urls("/bin/Help", { URL::create_with_file_scheme("/usr/share/man/man6/Flood.md") })); - TRY(Desktop::Launcher::seal_allowlist()); - - TRY(Core::System::pledge("stdio rpath recvfd sendfd")); - - TRY(Core::System::unveil("/tmp/session/%sid/portal/launch", "rw")); - TRY(Core::System::unveil("/res", "r")); - TRY(Core::System::unveil(nullptr, nullptr)); - - size_t board_rows = Config::read_i32("Flood"sv, ""sv, "board_rows"sv, 16); - size_t board_columns = Config::read_i32("Flood"sv, ""sv, "board_columns"sv, 16); - - Config::write_i32("Flood"sv, ""sv, "board_rows"sv, board_rows); - Config::write_i32("Flood"sv, ""sv, "board_columns"sv, board_columns); - - window->set_double_buffering_enabled(false); - window->set_title("Flood"); - window->resize(304, 325); - - auto main_widget = TRY(Flood::MainWidget::try_create()); - window->set_main_widget(main_widget); - - auto board_widget = TRY(main_widget->find_descendant_of_type_named("board_widget_container")->try_add(board_rows, board_columns)); - board_widget->board()->randomize(); - int ai_moves = get_number_of_moves_from_ai(*board_widget->board()); - int moves_made = 0; - - auto statusbar = main_widget->find_descendant_of_type_named("statusbar"); - - app->on_action_enter = [&](GUI::Action& action) { - statusbar->set_override_text(action.status_tip()); - }; - - app->on_action_leave = [&](GUI::Action&) { - statusbar->set_override_text({}); - }; - - auto update = [&]() { - board_widget->update(); - statusbar->set_text(String::formatted("Moves remaining: {}", ai_moves - moves_made).release_value_but_fixme_should_propagate_errors()); - }; - - update(); - - auto change_settings = [&] { - auto settings_dialog_or_error = SettingsDialog::try_create(window, board_rows, board_columns); - if (settings_dialog_or_error.is_error()) { - GUI::MessageBox::show(window, "Failed to load the settings window"sv, "Unable to Open Settings"sv, GUI::MessageBox::Type::Error); - return; - } - - auto settings_dialog = settings_dialog_or_error.release_value(); - if (settings_dialog->exec() != GUI::Dialog::ExecResult::OK) - return; - - board_rows = settings_dialog->board_rows(); - board_columns = settings_dialog->board_columns(); - - Config::write_i32("Flood"sv, ""sv, "board_rows"sv, board_rows); - Config::write_i32("Flood"sv, ""sv, "board_columns"sv, board_columns); - - GUI::MessageBox::show(settings_dialog, "New settings have been saved and will be applied on a new game"sv, "Settings Changed Successfully"sv, GUI::MessageBox::Type::Information); - }; - - auto start_a_new_game = [&] { - board_widget->resize_board(board_rows, board_columns); - board_widget->board()->reset(); - board_widget->board()->randomize(); - ai_moves = get_number_of_moves_from_ai(*board_widget->board()); - moves_made = 0; - update(); - window->update(); - }; - - board_widget->on_move = [&](Board::RowAndColumn row_and_column) { - auto const [row, column] = row_and_column; - board_widget->board()->set_current_value(board_widget->board()->cell(row, column)); - if (board_widget->board()->get_previous_value() != board_widget->board()->get_current_value()) { - ++moves_made; - board_widget->board()->update_values(); - update(); - if (board_widget->board()->is_flooded()) { - auto dialog_text = "You have tied with the AI."_string; - auto dialog_title("Congratulations!"sv); - if (ai_moves - moves_made == 1) - dialog_text = "You defeated the AI by 1 move."_string; - else if (ai_moves - moves_made > 1) - dialog_text = String::formatted("You defeated the AI by {} moves.", ai_moves - moves_made).release_value_but_fixme_should_propagate_errors(); - else - dialog_title = "Game over!"sv; - GUI::MessageBox::show(window, - dialog_text, - dialog_title, - GUI::MessageBox::Type::Information, - GUI::MessageBox::InputType::OK); - start_a_new_game(); - } else if (moves_made == ai_moves) { - GUI::MessageBox::show(window, - StringView("You have no more moves left."sv), - "You lost!"sv, - GUI::MessageBox::Type::Information, - GUI::MessageBox::InputType::OK); - start_a_new_game(); - } - } - }; - - auto game_menu = window->add_menu("&Game"_string); - - game_menu->add_action(GUI::Action::create("&New Game", { Mod_None, Key_F2 }, TRY(Gfx::Bitmap::load_from_file("/res/icons/16x16/reload.png"sv)), [&](auto&) { - start_a_new_game(); - })); - - game_menu->add_separator(); - game_menu->add_action(GUI::Action::create("&Settings", TRY(Gfx::Bitmap::load_from_file("/res/icons/16x16/settings.png"sv)), [&](auto&) { - change_settings(); - })); - - game_menu->add_separator(); - game_menu->add_action(GUI::CommonActions::make_quit_action([](auto&) { - GUI::Application::the()->quit(); - })); - - auto view_menu = window->add_menu("&View"_string); - view_menu->add_action(GUI::CommonActions::make_fullscreen_action([&](auto&) { - window->set_fullscreen(!window->is_fullscreen()); - })); - - auto help_menu = window->add_menu("&Help"_string); - help_menu->add_action(GUI::CommonActions::make_command_palette_action(window)); - help_menu->add_action(GUI::CommonActions::make_help_action([](auto&) { - Desktop::Launcher::open(URL::create_with_file_scheme("/usr/share/man/man6/Flood.md"), "/bin/Help"); - })); - help_menu->add_action(GUI::CommonActions::make_about_action("Flood"_string, app_icon, window)); - - window->show(); - - window->set_icon(app_icon.bitmap_for_size(16)); - - return app->exec(); -} diff --git a/Userland/Games/GameOfLife/Board.cpp b/Userland/Games/GameOfLife/Board.cpp deleted file mode 100644 index 1e693809c6c..00000000000 --- a/Userland/Games/GameOfLife/Board.cpp +++ /dev/null @@ -1,112 +0,0 @@ -/* - * Copyright (c) 2021, Andres Crucitti - * Copyright (c) 2021, Linus Groh - * Copyright (c) 2022, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "Board.h" -#include -#include - -Board::Board(size_t rows, size_t columns) -{ - resize(rows, columns); -} - -void Board::run_generation() -{ - m_stalled = true; - Vector> new_cells; - new_cells.resize(m_rows); - for (size_t row = 0; row < m_rows; ++row) - new_cells[row].resize(m_columns); - - for_each_cell([&](auto row, auto column) { - bool old_value = m_cells[row][column]; - bool new_value = calculate_next_value(row, column); - new_cells[row][column] = new_value; - if (old_value != new_value) - m_stalled = false; - }); - - if (m_stalled) - return; - - m_cells = move(new_cells); -} - -bool Board::calculate_next_value(size_t row, size_t column) const -{ - int top_left = cell(row - 1, column - 1); - int top_mid = cell(row - 1, column); - int top_right = cell(row - 1, column + 1); - int left = cell(row, column - 1); - int right = cell(row, column + 1); - int bottom_left = cell(row + 1, column - 1); - int bottom_mid = cell(row + 1, column); - int bottom_right = cell(row + 1, column + 1); - - int sum = top_left + top_mid + top_right + left + right + bottom_left + bottom_mid + bottom_right; - - bool old_value = m_cells[row][column]; - bool new_value = old_value; - - if (old_value) { - if (sum < 2 || sum > 3) - new_value = false; - } else { - if (sum == 3) - new_value = true; - } - - return new_value; -} - -void Board::clear() -{ - for_each_cell([this](auto row, auto column) { - set_cell(row, column, false); - }); -} - -void Board::randomize() -{ - for_each_cell([this](auto row, auto column) { - set_cell(row, column, get_random() % 2); - }); -} - -void Board::resize(size_t rows, size_t columns) -{ - m_rows = rows; - m_columns = columns; - - // Vector values get default-initialized, we don't need to set them to false explicitly. - m_cells.resize(rows); - for (size_t row = 0; row < rows; ++row) - m_cells[row].resize(columns); -} - -void Board::toggle_cell(size_t row, size_t column) -{ - VERIFY(row < m_rows && column < m_columns); - - m_cells[row][column] = !m_cells[row][column]; -} - -void Board::set_cell(size_t row, size_t column, bool on) -{ - VERIFY(row < m_rows && column < m_columns); - - m_cells[row][column] = on; -} - -bool Board::cell(size_t row, size_t column) const -{ - if (row >= m_rows || column >= m_columns) - return false; - - return m_cells[row][column]; -} diff --git a/Userland/Games/GameOfLife/Board.h b/Userland/Games/GameOfLife/Board.h deleted file mode 100644 index 8f1408858f5..00000000000 --- a/Userland/Games/GameOfLife/Board.h +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright (c) 2021, Andres Crucitti - * Copyright (c) 2021, Linus Groh - * Copyright (c) 2022, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include - -class Board { -public: - Board(size_t rows, size_t columns); - ~Board() = default; - - size_t columns() const { return m_columns; } - size_t rows() const { return m_rows; } - - void toggle_cell(size_t row, size_t column); - void set_cell(size_t row, size_t column, bool on); - bool cell(size_t row, size_t column) const; - Vector> const& cells() const { return m_cells; } - - void run_generation(); - bool is_stalled() const { return m_stalled; } - - void clear(); - void randomize(); - void resize(size_t rows, size_t columns); - - struct RowAndColumn { - size_t row { 0 }; - size_t column { 0 }; - }; - -private: - bool calculate_next_value(size_t row, size_t column) const; - - template - void for_each_cell(Callback callback) - { - for (size_t row = 0; row < m_rows; ++row) { - for (size_t column = 0; column < m_columns; ++column) - callback(row, column); - } - } - - size_t m_rows { 0 }; - size_t m_columns { 0 }; - - bool m_stalled { false }; - - Vector> m_cells; -}; diff --git a/Userland/Games/GameOfLife/BoardWidget.cpp b/Userland/Games/GameOfLife/BoardWidget.cpp deleted file mode 100644 index 1e78ad99e6a..00000000000 --- a/Userland/Games/GameOfLife/BoardWidget.cpp +++ /dev/null @@ -1,439 +0,0 @@ -/* - * Copyright (c) 2021, Andres Crucitti - * Copyright (c) 2021, Linus Groh - * Copyright (c) 2021, Ryan Wilson - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "BoardWidget.h" -#include -#include - -BoardWidget::BoardWidget(size_t rows, size_t columns) - : m_board(make(rows, columns)) -{ - m_timer = add(); - m_pattern_preview_timer = add(); - m_timer->stop(); - m_pattern_preview_timer->stop(); - m_timer->on_timeout = [this] { - run_generation(); - }; - m_pattern_preview_timer->on_timeout = [this] { - update(); - }; - m_timer->set_interval(m_running_timer_interval); - m_pattern_preview_timer->set_interval(m_running_pattern_preview_timer_interval); - - on_pattern_selection = [this](auto* pattern) { - m_selected_pattern = pattern; - if (on_pattern_selection_state_change) - on_pattern_selection_state_change(); - }; - - setup_patterns(); -} - -void BoardWidget::run_generation() -{ - m_board->run_generation(); - if (!m_board->is_stalled()) - m_ticks++; - - if (on_tick) - on_tick(m_ticks); - - update(); - if (m_board->is_stalled()) { - if (on_stall) - on_stall(); - update(); - }; -} - -void BoardWidget::resize_board(size_t rows, size_t columns) -{ - if (columns == m_board->columns() && rows == m_board->rows()) - return; - m_board->resize(rows, columns); - m_last_cell_toggled = { rows, columns }; - set_min_size(columns, rows); -} - -void BoardWidget::set_running_timer_interval(int interval) -{ - if (is_running()) - return; - - m_running_timer_interval = interval; - m_timer->set_interval(m_running_timer_interval); - - if (on_running_state_change) - on_running_state_change(); -} - -void BoardWidget::set_running(bool running) -{ - if (running == m_running) - return; - - clear_selected_pattern(); - - m_running = running; - - if (m_running) { - m_timer->start(); - } else { - m_timer->stop(); - } - - if (on_running_state_change) - on_running_state_change(); - - update(); -} - -void BoardWidget::toggle_cell(size_t row, size_t column) -{ - if (m_running || !m_toggling_cells || (m_last_cell_toggled.row == row && m_last_cell_toggled.column == column)) - return; - - m_ticks = 0; - - m_last_cell_toggled = { row, column }; - m_board->toggle_cell(row, column); - - if (on_cell_toggled) - on_cell_toggled(m_board, row, column); - - update(); -} - -void BoardWidget::clear_cells() -{ - m_ticks = 0; - m_board->clear(); -} - -void BoardWidget::randomize_cells() -{ - m_ticks = 0; - m_board->randomize(); -} - -int BoardWidget::get_cell_size() const -{ - int width = rect().width() / m_board->columns(); - int height = rect().height() / m_board->rows(); - - return min(width, height); -} - -Gfx::IntSize BoardWidget::get_board_offset() const -{ - int cell_size = get_cell_size(); - return { - (width() - cell_size * m_board->columns()) / 2, - (height() - cell_size * m_board->rows()) / 2, - }; -} - -void BoardWidget::paint_event(GUI::PaintEvent& event) -{ - GUI::Widget::paint_event(event); - - GUI::Painter painter(*this); - painter.add_clip_rect(event.rect()); - painter.fill_rect(event.rect(), Color::Black); - - int cell_size = get_cell_size(); - Gfx::IntSize board_offset = get_board_offset(); - - for (size_t row = 0; row < m_board->rows(); ++row) { - for (size_t column = 0; column < m_board->columns(); ++column) { - int cell_x = column * cell_size + board_offset.width(); - int cell_y = row * cell_size + board_offset.height(); - - Gfx::Rect cell_rect(cell_x, cell_y, cell_size, cell_size); - - Color border_color = Color::DarkGray; - Color fill_color; - - bool on = m_board->cell(row, column); - if (on) { - fill_color = Color::from_rgb(0xdcdc50); - } else { - fill_color = Color::MidGray; - } - - if (m_selected_pattern != nullptr) { - int y_offset = 0; - for (auto const& line : m_selected_pattern->pattern()) { - int x_offset = 0; - for (auto c : line.bytes_as_string_view()) { - if (c == 'O' && (m_last_cell_hovered.row + y_offset) < m_board->rows() - && (m_last_cell_hovered.column + x_offset) < m_board->columns() && row == (m_last_cell_hovered.row + y_offset) && column == (m_last_cell_hovered.column + x_offset)) - fill_color = Color::Green; - x_offset++; - } - y_offset++; - } - } - - painter.fill_rect(cell_rect, fill_color); - if (cell_size > 4) { - painter.draw_rect(cell_rect, border_color); - } - } - } -} - -void BoardWidget::mousedown_event(GUI::MouseEvent& event) -{ - if (event.button() == GUI::MouseButton::Primary) { - m_dragging_enabled = (m_selected_pattern == nullptr); - set_toggling_cells(true); - auto row_and_column = get_row_and_column_for_point(event.x(), event.y()); - if (!row_and_column.has_value()) - return; - auto [row, column] = row_and_column.value(); - - if (m_selected_pattern) { - place_pattern(row, column); - if (!event.ctrl()) { - clear_selected_pattern(); - } - } else { - toggle_cell(row, column); - } - } -} - -void BoardWidget::keydown_event(GUI::KeyEvent& event) -{ - if (event.key() == Key_Escape) { - clear_selected_pattern(); - update(); - return; - } - - event.ignore(); -} - -void BoardWidget::context_menu_event(GUI::ContextMenuEvent& event) -{ - if (!m_context_menu) { - m_context_menu = GUI::Menu::construct(); - - auto insert_pattern_menu = m_context_menu->add_submenu("&Insert Pattern"_string); - for_each_pattern([&](auto& pattern) { - if (pattern.action()) - insert_pattern_menu->add_action(*pattern.action()); - }); - } - if (!m_running) - m_context_menu->popup(event.screen_position()); -} - -void BoardWidget::mousemove_event(GUI::MouseEvent& event) -{ - auto row_and_column = get_row_and_column_for_point(event.x(), event.y()); - if (!row_and_column.has_value()) - return; - auto [row, column] = row_and_column.value(); - if (m_toggling_cells && m_dragging_enabled) { - if (m_last_cell_toggled.row != row || m_last_cell_toggled.column != column) - toggle_cell(row, column); - } - m_last_cell_hovered = { row, column }; - if (m_selected_pattern != nullptr) { - if (!m_pattern_preview_timer->is_active()) - m_pattern_preview_timer->start(); - } -} - -void BoardWidget::mouseup_event(GUI::MouseEvent&) -{ - set_toggling_cells(false); - m_dragging_enabled = true; -} - -Optional BoardWidget::get_row_and_column_for_point(int x, int y) const -{ - auto board_offset = get_board_offset(); - auto cell_size = get_cell_size(); - auto board_width = m_board->columns() * cell_size; - auto board_height = m_board->rows() * cell_size; - if (x <= board_offset.width() || static_cast(x) >= board_offset.width() + board_width) - return {}; - if (y <= board_offset.height() || static_cast(y) >= board_offset.height() + board_height) - return {}; - return { { - .row = static_cast((y - board_offset.height()) / cell_size), - .column = static_cast((x - board_offset.width()) / cell_size), - } }; -} - -void BoardWidget::place_pattern(size_t row, size_t column) -{ - int y_offset = 0; - for (auto const& line : m_selected_pattern->pattern()) { - int x_offset = 0; - for (auto c : line.bytes_as_string_view()) { - if (c == 'O' && (row + y_offset) < m_board->rows() && (column + x_offset) < m_board->columns()) - toggle_cell(row + y_offset, column + x_offset); - x_offset++; - } - y_offset++; - } -} - -void BoardWidget::clear_selected_pattern() -{ - if (!m_selected_pattern) - return; - - m_selected_pattern = nullptr; - if (on_pattern_selection_state_change) - on_pattern_selection_state_change(); - - if (m_pattern_preview_timer->is_active()) - m_pattern_preview_timer->stop(); -} - -void BoardWidget::setup_patterns() -{ - auto add_pattern = [&](auto name, NonnullOwnPtr pattern) { - auto action = GUI::Action::create(name, [this, pattern = pattern.ptr()](const GUI::Action&) { - on_pattern_selection(pattern); - }); - pattern->set_action(action); - m_patterns.append(move(pattern)); - }; - - Vector blinker = { - "OOO"_string - }; - - Vector toad = { - ".OOO"_string, - "OOO."_string - }; - - Vector glider = { - ".O."_string, - "..O"_string, - "OOO"_string, - }; - - Vector lightweight_spaceship = { - ".OO.."_string, - "OOOO."_string, - "OO.OO"_string, - "..OO."_string - }; - - Vector middleweight_spaceship = { - ".OOOOO"_string, - "O....O"_string, - ".....O"_string, - "O...O."_string, - "..O..."_string - }; - - Vector heavyweight_spaceship = { - "..OO..."_string, - "O....O."_string, - "......O"_string, - "O.....O"_string, - ".OOOOOO"_string - }; - - Vector infinite_1 = { "OOOOOOOO.OOOOO...OOO......OOOOOOO.OOOOO"_string }; - - Vector infinite_2 = { - "......O."_string, - "....O.OO"_string, - "....O.O."_string, - "....O..."_string, - "..O....."_string, - "O.O....."_string - }; - - Vector infinite_3 = { - "OOO.O"_string, - "O...."_string, - "...OO"_string, - ".OO.O"_string, - "O.O.O"_string - }; - - Vector simkin_glider_gun = { - "OO.....OO........................"_string, - "OO.....OO........................"_string, - "................................."_string, - "....OO..........................."_string, - "....OO..........................."_string, - "................................."_string, - "................................."_string, - "................................."_string, - "................................."_string, - "......................OO.OO......"_string, - ".....................O.....O....."_string, - ".....................O......O..OO"_string, - ".....................OOO...O...OO"_string, - "..........................O......"_string, - "................................."_string, - "................................."_string, - "................................."_string, - "....................OO..........."_string, - "....................O............"_string, - ".....................OOO........."_string, - ".......................O........."_string - }; - Vector gosper_glider_gun = { - "........................O..........."_string, - "......................O.O..........."_string, - "............OO......OO............OO"_string, - "...........O...O....OO............OO"_string, - "OO........O.....O...OO.............."_string, - "OO........O...O.OO....O.O..........."_string, - "..........O.....O.......O..........."_string, - "...........O...O...................."_string, - "............OO......................"_string - }; - - Vector r_pentomino = { - ".OO"_string, - "OO."_string, - ".O."_string - }; - - Vector diehard = { - "......O."_string, - "OO......"_string, - ".O...OOO"_string - }; - - Vector acorn = { - ".O....."_string, - "...O..."_string, - "OO..OOO"_string - }; - - add_pattern("Blinker", make(move(blinker))); - add_pattern("Toad", make(move(toad))); - add_pattern("Glider", make(move(glider))); - add_pattern("Lightweight Spaceship", make(move(lightweight_spaceship))); - add_pattern("Middleweight Spaceship", make(move(middleweight_spaceship))); - add_pattern("Heavyweight Spaceship", make(move(heavyweight_spaceship))); - add_pattern("Infinite 1", make(move(infinite_1))); - add_pattern("Infinite 2", make(move(infinite_2))); - add_pattern("Infinite 3", make(move(infinite_3))); - add_pattern("R-Pentomino", make(move(r_pentomino))); - add_pattern("Diehard", make(move(diehard))); - add_pattern("Acorn", make(move(acorn))); - add_pattern("Simkin's Glider Gun", make(move(simkin_glider_gun))); - add_pattern("Gosper's Glider Gun", make(move(gosper_glider_gun))); -} diff --git a/Userland/Games/GameOfLife/BoardWidget.h b/Userland/Games/GameOfLife/BoardWidget.h deleted file mode 100644 index 42dae403365..00000000000 --- a/Userland/Games/GameOfLife/BoardWidget.h +++ /dev/null @@ -1,100 +0,0 @@ -/* - * Copyright (c) 2021, Andres Crucitti - * Copyright (c) 2021, Linus Groh - * Copyright (c) 2021, Ryan Wilson - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include "Board.h" -#include "Pattern.h" -#include -#include -#include -#include -#include -#include -#include - -class BoardWidget final : public GUI::Widget { - C_OBJECT(BoardWidget); - -public: - virtual void paint_event(GUI::PaintEvent&) override; - virtual void mousemove_event(GUI::MouseEvent&) override; - virtual void mouseup_event(GUI::MouseEvent&) override; - virtual void mousedown_event(GUI::MouseEvent&) override; - virtual void keydown_event(GUI::KeyEvent&) override; - virtual void context_menu_event(GUI::ContextMenuEvent&) override; - - void set_toggling_cells(bool toggling) - { - m_toggling_cells = toggling; - if (!toggling) - m_last_cell_toggled = { m_board->rows(), m_board->columns() }; - } - - void toggle_cell(size_t row, size_t column); - void clear_cells(); - void randomize_cells(); - - int get_cell_size() const; - Gfx::IntSize get_board_offset() const; - - Optional get_row_and_column_for_point(int x, int y) const; - - void resize_board(size_t rows, size_t columns); - Board const* board() const { return m_board.ptr(); } - - bool is_running() const { return m_running; } - void set_running(bool r); - - Pattern* selected_pattern() { return m_selected_pattern; } - Function on_pattern_selection; - template - void for_each_pattern(Callback callback) - { - for (auto& pattern : m_patterns) - callback(*pattern); - } - - void run_generation(); - - int running_timer_interval() const { return m_running_timer_interval; } - void set_running_timer_interval(int interval); - - Function on_tick; - Function on_running_state_change; - Function on_stall; - Function on_pattern_selection_state_change; - Function on_cell_toggled; - -private: - BoardWidget(size_t rows, size_t columns); - void setup_patterns(); - void place_pattern(size_t row, size_t column); - void clear_selected_pattern(); - - bool m_toggling_cells { false }; - Board::RowAndColumn m_last_cell_toggled {}; - Board::RowAndColumn m_last_cell_hovered {}; - Pattern* m_selected_pattern { nullptr }; - Vector> m_patterns; - - NonnullOwnPtr m_board; - - bool m_running { false }; - bool m_dragging_enabled { true }; - - int m_running_timer_interval { 500 }; - int m_running_pattern_preview_timer_interval { 100 }; - - u64 m_ticks { 0 }; - - RefPtr m_context_menu; - - RefPtr m_timer; - RefPtr m_pattern_preview_timer; -}; diff --git a/Userland/Games/GameOfLife/CMakeLists.txt b/Userland/Games/GameOfLife/CMakeLists.txt deleted file mode 100644 index 50a060af8ef..00000000000 --- a/Userland/Games/GameOfLife/CMakeLists.txt +++ /dev/null @@ -1,18 +0,0 @@ -serenity_component( - GameOfLife - RECOMMENDED - TARGETS GameOfLife -) - -compile_gml(GameOfLife.gml GameOfLifeGML.cpp) - -set(SOURCES - main.cpp - Board.cpp - BoardWidget.cpp - GameOfLifeGML.cpp - Pattern.cpp -) - -serenity_app(GameOfLife ICON app-gameoflife) -target_link_libraries(GameOfLife PRIVATE LibCore LibGfx LibGUI LibMain LibDesktop LibURL) diff --git a/Userland/Games/GameOfLife/GameOfLife.gml b/Userland/Games/GameOfLife/GameOfLife.gml deleted file mode 100644 index d438e4447c7..00000000000 --- a/Userland/Games/GameOfLife/GameOfLife.gml +++ /dev/null @@ -1,66 +0,0 @@ -@GameOfLife::MainWidget { - layout: @GUI::VerticalBoxLayout {} - - @GUI::ToolbarContainer { - @GUI::Toolbar { - name: "toolbar" - - @GUI::Label { - text: "Columns: " - autosize: true - } - - @GUI::SpinBox { - name: "columns_spinbox" - min: 10 - max: 999 - fixed_width: 40 - } - - @GUI::VerticalSeparator {} - - @GUI::Label { - text: "Rows: " - autosize: true - } - - @GUI::SpinBox { - name: "rows_spinbox" - min: 10 - max: 999 - fixed_width: 40 - } - - @GUI::VerticalSeparator {} - - @GUI::Label { - text: "Speed: " - autosize: true - } - - @GUI::SpinBox { - name: "interval_spinbox" - min: 10 - max: 5000 - fixed_width: 60 - } - - @GUI::Label { - text: " ms" - autosize: true - } - - @GUI::VerticalSeparator {} - } - } - - @GUI::Frame { - name: "board_widget_container" - fill_with_background_color: true - } - - @GUI::Statusbar { - name: "statusbar" - segment_count: 2 - } -} diff --git a/Userland/Games/GameOfLife/MainWidget.h b/Userland/Games/GameOfLife/MainWidget.h deleted file mode 100644 index 3e43f6ec72c..00000000000 --- a/Userland/Games/GameOfLife/MainWidget.h +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright (c) 2023, Tim Ledbetter - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include - -namespace GameOfLife { - -class MainWidget : public GUI::Widget { - C_OBJECT_ABSTRACT(MainWidget) -public: - static ErrorOr> try_create(); - virtual ~MainWidget() override = default; - -private: - MainWidget() = default; -}; - -} diff --git a/Userland/Games/GameOfLife/Pattern.cpp b/Userland/Games/GameOfLife/Pattern.cpp deleted file mode 100644 index 5aef45ba501..00000000000 --- a/Userland/Games/GameOfLife/Pattern.cpp +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright (c) 2021, Ryan Wilson - * Copyright (c) 2022, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "Pattern.h" -#include -#include -#include -#include - -Pattern::Pattern(Vector&& pattern) - : m_pattern(move(pattern)) -{ -} - -void Pattern::set_action(GUI::Action* action) -{ - m_action = action; -} - -void Pattern::rotate_clockwise() -{ - Vector rotated; - for (size_t i = 0; i < m_pattern.first().bytes_as_string_view().length(); i++) { - StringBuilder builder; - for (int j = m_pattern.size() - 1; j >= 0; j--) { - builder.append(m_pattern.at(j).bytes_as_string_view().substring_view(i, 1)); - } - rotated.append(MUST(builder.to_string())); - } - m_pattern = move(rotated); -} diff --git a/Userland/Games/GameOfLife/Pattern.h b/Userland/Games/GameOfLife/Pattern.h deleted file mode 100644 index 5f7aa90b97e..00000000000 --- a/Userland/Games/GameOfLife/Pattern.h +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright (c) 2021, Ryan Wilson - * Copyright (c) 2022, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include "Board.h" -#include -#include -#include - -class Pattern final { -public: - Pattern(Vector&&); - Vector pattern() const { return m_pattern; } - GUI::Action* action() { return m_action; } - void set_action(GUI::Action*); - void rotate_clockwise(); - -private: - RefPtr m_action; - Vector m_pattern; -}; diff --git a/Userland/Games/GameOfLife/main.cpp b/Userland/Games/GameOfLife/main.cpp deleted file mode 100644 index f5958d1d887..00000000000 --- a/Userland/Games/GameOfLife/main.cpp +++ /dev/null @@ -1,214 +0,0 @@ -/* - * Copyright (c) 2021, Andres Crucitti - * Copyright (c) 2021, networkException - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "BoardWidget.h" -#include "MainWidget.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -ErrorOr serenity_main(Main::Arguments arguments) -{ - TRY(Core::System::pledge("stdio rpath recvfd sendfd unix")); - - auto app = TRY(GUI::Application::create(arguments)); - - TRY(Desktop::Launcher::add_allowed_handler_with_only_specific_urls("/bin/Help", { URL::create_with_file_scheme("/usr/share/man/man6/GameOfLife.md") })); - TRY(Desktop::Launcher::seal_allowlist()); - - TRY(Core::System::pledge("stdio rpath recvfd sendfd")); - - TRY(Core::System::unveil("/tmp/session/%sid/portal/launch", "rw")); - TRY(Core::System::unveil("/res", "r")); - TRY(Core::System::unveil(nullptr, nullptr)); - - auto toggle_cells_tip = "Tip: click the board to toggle individual cells, or click+drag to toggle multiple cells"_string; - auto pattern_place_tip = "Tip: hold Ctrl to place multiple patterns"_string; - - auto app_icon = TRY(GUI::Icon::try_create_default_icon("app-gameoflife"sv)); - - auto window = GUI::Window::construct(); - window->set_icon(app_icon.bitmap_for_size(16)); - - size_t board_columns = 35; - size_t board_rows = 35; - - window->set_double_buffering_enabled(false); - window->set_title("Game of Life"); - - auto main_widget = TRY(GameOfLife::MainWidget::try_create()); - window->set_main_widget(main_widget); - main_widget->set_fill_with_background_color(true); - - auto& main_toolbar = *main_widget->find_descendant_of_type_named("toolbar"); - main_toolbar.layout()->set_margins({ 0, 6 }); - - auto& board_widget_container = *main_widget->find_descendant_of_type_named("board_widget_container"); - board_widget_container.set_layout(GUI::Margins {}, 0); - auto& board_widget = board_widget_container.add(board_rows, board_columns); - board_widget.set_focus_policy(GUI::FocusPolicy::StrongFocus); - board_widget.set_focus(true); - board_widget.randomize_cells(); - board_widget.set_min_size(board_columns, board_rows); - - auto& statusbar = *main_widget->find_descendant_of_type_named("statusbar"); - auto width = board_widget.font().width("Ticks: 000,000,000"sv) + board_widget.font().max_glyph_width(); - statusbar.segment(1).set_fixed_width(ceil(width)); - - auto show_statusbar_hint = [&]() { - auto tip = board_widget.selected_pattern() ? pattern_place_tip : toggle_cells_tip; - statusbar.segment(0).set_text(tip); - }; - show_statusbar_hint(); - - auto& columns_spinbox = *main_widget->find_descendant_of_type_named("columns_spinbox"); - auto& rows_spinbox = *main_widget->find_descendant_of_type_named("rows_spinbox"); - - columns_spinbox.set_value(board_columns); - rows_spinbox.set_value(board_rows); - - auto size_changed_function = [&] { - show_statusbar_hint(); - board_widget.resize_board(rows_spinbox.value(), columns_spinbox.value()); - board_widget.update(); - }; - - rows_spinbox.on_change = [&](auto) { size_changed_function(); }; - columns_spinbox.on_change = [&](auto) { size_changed_function(); }; - - auto& interval_spinbox = *main_widget->find_descendant_of_type_named("interval_spinbox"); - - interval_spinbox.on_change = [&](auto value) { - board_widget.set_running_timer_interval(value); - }; - - interval_spinbox.set_value(150); - - auto paused_icon = TRY(Gfx::Bitmap::load_from_file("/res/icons/16x16/pause.png"sv)); - auto play_icon = TRY(Gfx::Bitmap::load_from_file("/res/icons/16x16/play.png"sv)); - - auto play_pause_action = GUI::Action::create("&Play", { Mod_None, Key_Return }, *play_icon, [&](GUI::Action&) { - board_widget.set_running(!board_widget.is_running()); - }); - - main_toolbar.add_action(play_pause_action); - - auto run_one_generation_action = GUI::Action::create("Run &Next Generation", { Mod_Ctrl, Key_Equal }, TRY(Gfx::Bitmap::load_from_file("/res/icons/16x16/go-forward.png"sv)), [&](const GUI::Action&) { - show_statusbar_hint(); - board_widget.run_generation(); - }); - main_toolbar.add_action(run_one_generation_action); - - auto clear_board_action = GUI::Action::create("&Clear board", { Mod_Ctrl, Key_N }, TRY(Gfx::Bitmap::load_from_file("/res/icons/16x16/delete.png"sv)), [&](auto&) { - show_statusbar_hint(); - statusbar.segment(1).set_text({}); - board_widget.clear_cells(); - board_widget.update(); - }); - main_toolbar.add_action(clear_board_action); - - auto randomize_cells_action = GUI::Action::create("&Randomize board", { Mod_Ctrl, Key_R }, TRY(Gfx::Bitmap::load_from_file("/res/icons/16x16/reload.png"sv)), [&](auto&) { - show_statusbar_hint(); - statusbar.segment(1).set_text({}); - board_widget.randomize_cells(); - board_widget.update(); - }); - main_toolbar.add_action(randomize_cells_action); - - auto rotate_pattern_action = GUI::Action::create("&Rotate pattern", { 0, Key_R }, TRY(Gfx::Bitmap::load_from_file("/res/icons/16x16/redo.png"sv)), [&](auto&) { - board_widget.selected_pattern()->rotate_clockwise(); - }); - rotate_pattern_action->set_enabled(false); - main_toolbar.add_action(rotate_pattern_action); - - auto game_menu = window->add_menu("&Game"_string); - - game_menu->add_action(clear_board_action); - game_menu->add_action(randomize_cells_action); - game_menu->add_separator(); - game_menu->add_action(play_pause_action); - game_menu->add_action(run_one_generation_action); - game_menu->add_separator(); - game_menu->add_action(GUI::CommonActions::make_quit_action([](auto&) { - GUI::Application::the()->quit(); - })); - - auto view_menu = window->add_menu("&View"_string); - view_menu->add_action(GUI::CommonActions::make_fullscreen_action([&](auto&) { - window->set_fullscreen(!window->is_fullscreen()); - })); - - auto help_menu = window->add_menu("&Help"_string); - help_menu->add_action(GUI::CommonActions::make_command_palette_action(window)); - help_menu->add_action(GUI::CommonActions::make_help_action([](auto&) { - Desktop::Launcher::open(URL::create_with_file_scheme("/usr/share/man/man6/GameOfLife.md"), "/bin/Help"); - })); - help_menu->add_action(GUI::CommonActions::make_about_action("Game of Life"_string, app_icon, window)); - - board_widget.on_tick = [&](u64 ticks) { - statusbar.segment(1).set_text(String::formatted("Ticks: {:'}", ticks).release_value_but_fixme_should_propagate_errors()); - }; - - board_widget.on_running_state_change = [&]() { - if (board_widget.is_running()) { - statusbar.segment(0).set_text("Running..."_string); - play_pause_action->set_icon(paused_icon); - play_pause_action->set_text("&Pause"); - main_widget->set_override_cursor(Gfx::StandardCursor::None); - } else { - statusbar.segment(0).set_text("Paused"_string); - play_pause_action->set_icon(play_icon); - play_pause_action->set_text("&Play"); - main_widget->set_override_cursor(Gfx::StandardCursor::Drag); - } - - interval_spinbox.set_value(board_widget.running_timer_interval()); - - rows_spinbox.set_enabled(!board_widget.is_running()); - columns_spinbox.set_enabled(!board_widget.is_running()); - interval_spinbox.set_enabled(!board_widget.is_running()); - - run_one_generation_action->set_enabled(!board_widget.is_running()); - clear_board_action->set_enabled(!board_widget.is_running()); - randomize_cells_action->set_enabled(!board_widget.is_running()); - - board_widget.update(); - }; - - board_widget.on_stall = [&] { - play_pause_action->activate(); - statusbar.segment(0).set_text("Stalled"_string); - }; - - board_widget.on_cell_toggled = [&](auto, auto, auto) { - show_statusbar_hint(); - statusbar.segment(1).set_text({}); - }; - - board_widget.on_pattern_selection_state_change = [&] { - show_statusbar_hint(); - rotate_pattern_action->set_enabled(board_widget.selected_pattern() != nullptr); - }; - - window->resize(600, 500); - window->show(); - - return app->exec(); -} diff --git a/Userland/Games/Hearts/CMakeLists.txt b/Userland/Games/Hearts/CMakeLists.txt deleted file mode 100644 index 384324bc395..00000000000 --- a/Userland/Games/Hearts/CMakeLists.txt +++ /dev/null @@ -1,20 +0,0 @@ -serenity_component( - Hearts - DESCRIPTION "Hearts game" - RECOMMENDED - TARGETS Hearts -) - -compile_gml(Hearts.gml HeartsGML.cpp) - -set(SOURCES - Game.cpp - main.cpp - Player.cpp - ScoreCard.cpp - SettingsDialog.cpp - HeartsGML.cpp -) - -serenity_app(Hearts ICON app-hearts) -target_link_libraries(Hearts PRIVATE LibCards LibGUI LibGfx LibCore LibConfig LibMain LibDesktop LibURL) diff --git a/Userland/Games/Hearts/Game.cpp b/Userland/Games/Hearts/Game.cpp deleted file mode 100644 index f65618467dd..00000000000 --- a/Userland/Games/Hearts/Game.cpp +++ /dev/null @@ -1,936 +0,0 @@ -/* - * Copyright (c) 2020, Till Mayer - * Copyright (c) 2021, Gunnar Beutner - * Copyright (c) 2022, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "Game.h" -#include "Helpers.h" -#include "ScoreCard.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace Hearts { - -Game::Game() -{ - m_delay_timer = Core::Timer::create_single_shot(0, [this] { - dbgln_if(HEARTS_DEBUG, "Continuing game after delay..."); - advance_game(); - }); - - constexpr int card_overlap = 20; - constexpr int outer_border_size = 15; - constexpr int player_deck_width = 12 * card_overlap + Card::width; - constexpr int player_deck_height = 12 * card_overlap + Card::height; - constexpr int text_height = 15; - constexpr int text_offset = 5; - - m_players[0].first_card_position = { (width - player_deck_width) / 2, height - outer_border_size - Card::height }; - m_players[0].card_offset = { card_overlap, 0 }; - m_players[0].name_position = { - (width - player_deck_width) / 2 - 50, height - outer_border_size - text_height - text_offset, - 50 - text_offset, text_height - }; - m_players[0].name_alignment = Gfx::TextAlignment::BottomRight; - m_players[0].name = "Gunnar"; - m_players[0].is_human = true; - m_players[0].taken_cards_target = { width / 2 - Card::width / 2, height }; - - m_players[1].first_card_position = { outer_border_size, (height - player_deck_height) / 2 }; - m_players[1].card_offset = { 0, card_overlap }; - m_players[1].name_position = { - outer_border_size, (height - player_deck_height) / 2 - text_height - text_offset, - Card::width, text_height - }; - m_players[1].name_alignment = Gfx::TextAlignment::BottomLeft; - m_players[1].name = "Paul"; - m_players[1].taken_cards_target = { -Card::width, height / 2 - Card::height / 2 }; - - m_players[2].first_card_position = { width - (width - player_deck_width) / 2 - Card::width, outer_border_size }; - m_players[2].card_offset = { -card_overlap, 0 }; - m_players[2].name_position = { - width - (width - player_deck_width) / 2 + text_offset, outer_border_size + text_offset, - Card::width, text_height - }; - m_players[2].name_alignment = Gfx::TextAlignment::TopLeft; - m_players[2].name = "Simon"; - m_players[2].taken_cards_target = { width / 2 - Card::width / 2, -Card::height }; - - m_players[3].first_card_position = { width - outer_border_size - Card::width, height - (height - player_deck_height) / 2 - Card::height }; - m_players[3].card_offset = { 0, -card_overlap }; - m_players[3].name_position = { - width - outer_border_size - Card::width, height - (height - player_deck_height) / 2 + text_offset, - Card::width, text_height - }; - m_players[3].name_alignment = Gfx::TextAlignment::TopRight; - m_players[3].name = "Lisa"; - m_players[3].taken_cards_target = { width, height / 2 - Card::height / 2 }; - - m_passing_button = add("Pass Left"_string); - constexpr int button_width = 120; - constexpr int button_height = 30; - m_passing_button->set_relative_rect(width / 2 - button_width / 2, height - 3 * outer_border_size - Card::height - button_height, button_width, button_height); - m_passing_button->on_click = [this](unsigned int) { - if (m_state == State::PassingSelect) - m_state = State::PassingSelectConfirmed; - else - m_state = State::Play; - advance_game(); - }; - - reset(); -} - -void Game::reset() -{ - dbgln_if(HEARTS_DEBUG, "====="); - dbgln_if(HEARTS_DEBUG, "Resetting game"); - - stop_animation(); - - m_hand_number = 0; - - m_passing_button->set_enabled(false); - m_passing_button->set_visible(false); - - m_cards_highlighted.clear(); - - m_trick.clear_with_capacity(); - m_trick_number = 0; - - for (auto& player : m_players) { - player.hand.clear_with_capacity(); - player.cards_taken.clear_with_capacity(); - } -} - -void Game::show_score_card(bool game_over) -{ - auto score_dialog = GUI::Dialog::construct(window()); - score_dialog->set_resizable(false); - score_dialog->set_icon(window()->icon()); - - auto score_widget = score_dialog->set_main_widget(); - score_widget->set_fill_with_background_color(true); - score_widget->set_layout(10, 15); - - auto& card_container = score_widget->add(); - auto& score_card = card_container.add(m_players, game_over); - - auto& button_container = score_widget->add(); - button_container.set_shrink_to_fit(true); - button_container.set_layout(); - - auto& close_button = button_container.add("OK"_string); - close_button.on_click = [&score_dialog](auto) { - score_dialog->done(GUI::Dialog::ExecResult::OK); - }; - close_button.set_min_width(70); - close_button.resize(70, 30); - - // FIXME: Why is this necessary? - score_dialog->resize({ 20 + score_card.width() + 15 + close_button.width(), 20 + score_card.height() }); - - StringBuilder title_builder; - title_builder.append("Score Card"sv); - if (game_over) - title_builder.append(" - Game Over"sv); - score_dialog->set_title(title_builder.to_byte_string()); - - RefPtr close_timer; - if (!m_players[0].is_human) { - close_timer = Core::Timer::create_single_shot(2000, [&] { - score_dialog->close(); - }); - close_timer->start(); - } - - score_dialog->exec(); -} - -void Game::setup(ByteString player_name, int hand_number) -{ - m_players[0].name = move(player_name); - - reset(); - - m_hand_number = hand_number; - - if (m_hand_number == 0) { - for (auto& player : m_players) - player.scores.clear_with_capacity(); - } - - if (m_hand_number % 4 != 3) { - m_state = State::PassingSelect; - m_human_can_play = true; - switch (passing_direction()) { - case PassingDirection::Left: - m_passing_button->set_text("Pass Left"_string); - break; - case PassingDirection::Across: - m_passing_button->set_text("Pass Across"_string); - break; - case PassingDirection::Right: - m_passing_button->set_text("Pass Right"_string); - break; - default: - VERIFY_NOT_REACHED(); - } - } else - m_state = State::Play; - - if (m_hand_number % 4 != 3) { - m_passing_button->set_visible(true); - m_passing_button->set_focus(false); - } - - Vector> deck = Cards::create_standard_deck(Cards::Shuffle::Yes).release_value_but_fixme_should_propagate_errors(); - - for (auto& player : m_players) { - player.hand.ensure_capacity(Card::card_count); - for (uint8_t i = 0; i < Card::card_count; ++i) { - auto card = deck.take_last(); - if constexpr (!HEARTS_DEBUG) { - if (&player != &m_players[0]) - card->set_upside_down(true); - } - player.hand.append(card); - } - player.sort_hand(); - Gfx::IntRect update_rect; - reposition_hand(player); - for (auto& card : player.hand) - update_rect = update_rect.united(card->rect()); - update(update_rect); - } - - continue_game_after_delay(); -} - -void Game::start_animation(Vector> cards, Gfx::IntPoint end, Function did_finish_callback, int initial_delay_ms, int steps) -{ - stop_animation(); - - m_animation_end = end; - m_animation_current_step = 0; - m_animation_steps = steps; - m_animation_cards.clear_with_capacity(); - for (auto& card : cards) - m_animation_cards.empend(card, card->position()); - m_animation_did_finish = make>(move(did_finish_callback)); - m_animation_delay_timer = Core::Timer::create_single_shot(initial_delay_ms, [&] { - m_animation_playing = true; - start_timer(10); - }); - m_animation_delay_timer->start(); -} - -void Game::stop_animation() -{ - if (m_animation_playing) { - for (auto& animation : m_animation_cards) - animation.card->set_position(m_animation_end); - m_animation_playing = false; - } - if (m_animation_delay_timer) - m_animation_delay_timer->stop(); - stop_timer(); -} - -void Game::timer_event(Core::TimerEvent&) -{ - if (!m_animation_playing) - return; - for (auto& animation : m_animation_cards) { - Gfx::IntRect update_rect = animation.card->rect(); - animation.card->set_position(animation.start + (m_animation_end - animation.start) * m_animation_current_step / m_animation_steps); - update_rect = update_rect.united(animation.card->rect()); - update(update_rect); - } - if (m_animation_current_step >= m_animation_steps) { - stop_timer(); - m_animation_playing = false; - if (m_animation_did_finish) { - // The did_finish handler might end up destroying/replacing the handler - // so we have to save it first. - OwnPtr> animation_did_finish = move(m_animation_did_finish); - (*animation_did_finish)(); - } - } - m_animation_current_step++; -} - -bool Game::other_player_has_lower_value_card(Player& player, Card& card) -{ - for (auto& other_player : m_players) { - if (&player != &other_player) { - for (auto& other_card : other_player.hand) { - if (other_card && card.suit() == other_card->suit() && hearts_card_value(*other_card) < hearts_card_value(card)) - return true; - } - } - } - return false; -} - -bool Game::other_player_has_higher_value_card(Player& player, Card& card) -{ - for (auto& other_player : m_players) { - if (&player != &other_player) { - for (auto& other_card : other_player.hand) { - if (other_card && card.suit() == other_card->suit() && hearts_card_value(*other_card) > hearts_card_value(card)) - return true; - } - } - } - return false; -} - -bool Game::other_player_has_queen_of_spades(Player& player) -{ - for (auto& other_player : m_players) { - if (&player != &other_player) { - for (auto& other_card : other_player.hand) { - if (other_card && other_card->suit() == Cards::Suit::Spades && hearts_card_value(*other_card) == CardValue::Queen) - return true; - } - } - } - return false; -} - -#define RETURN_CARD_IF_VALID(card) \ - do { \ - auto card_index = (card); \ - if (card_index.has_value()) \ - return card_index.value(); \ - } while (0) - -size_t Game::pick_card(Player& player) -{ - bool is_leading_player = m_trick.is_empty(); - bool is_first_trick = m_trick_number == 0; - if (is_leading_player) { - if (is_first_trick) { - auto clubs_2 = player.pick_specific_card(Cards::Suit::Clubs, CardValue::Number_2); - VERIFY(clubs_2.has_value()); - return clubs_2.value(); - } else { - auto valid_card = [this, &player](Card& card) { - return is_valid_play(player, card); - }; - auto prefer_card = [this, &player](Card& card) { - return !other_player_has_lower_value_card(player, card) && other_player_has_higher_value_card(player, card); - }; - return player.pick_lead_card(move(valid_card), move(prefer_card)); - } - } - auto* high_card = m_trick[0].ptr(); - for (auto& card : m_trick) - if (high_card->suit() == card->suit() && hearts_card_value(card) > hearts_card_value(*high_card)) - high_card = card; - if (high_card->suit() == Cards::Suit::Spades && hearts_card_value(*high_card) > CardValue::Queen) - RETURN_CARD_IF_VALID(player.pick_specific_card(Cards::Suit::Spades, CardValue::Queen)); - auto card_has_points = [](Card& card) { return hearts_card_points(card) > 0; }; - auto trick_has_points = m_trick.first_matching(card_has_points).has_value(); - bool is_trailing_player = m_trick.size() == 3; - if (!trick_has_points && is_trailing_player) { - RETURN_CARD_IF_VALID(player.pick_low_points_high_value_card(m_trick[0]->suit())); - if (is_first_trick) - return player.pick_low_points_high_value_card().value(); - else { - auto ignore_card = [this, &player](Card& card) { - return !other_player_has_higher_value_card(player, card); - }; - return player.pick_max_points_card(move(ignore_card)); - } - } - RETURN_CARD_IF_VALID(player.pick_lower_value_card(*high_card)); - bool is_third_player = m_trick.size() == 2; - bool play_highest_value_card = false; - if (is_trailing_player) - play_highest_value_card = true; - if (is_third_player && !trick_has_points) { - play_highest_value_card = true; - - if (high_card->suit() == Cards::Suit::Spades && other_player_has_queen_of_spades(player)) { - Optional chosen_card_index = player.pick_low_points_high_value_card(high_card->suit()); - if (chosen_card_index.has_value()) { - auto& card = player.hand[chosen_card_index.value()]; - if (hearts_card_value(*card) > CardValue::Queen) - play_highest_value_card = false; - } - } - } - if (play_highest_value_card) - RETURN_CARD_IF_VALID(player.pick_low_points_high_value_card(high_card->suit())); - else - RETURN_CARD_IF_VALID(player.pick_slightly_higher_value_card(*high_card)); - if (is_first_trick) - return player.pick_low_points_high_value_card().value(); - auto ignore_card = [this, &player](Card& card) { - return !other_player_has_higher_value_card(player, card); - }; - return player.pick_max_points_card(move(ignore_card)); -} - -size_t Game::pick_first_card_ltr(Player& player) -{ - for (size_t i = 0; i < player.hand.size(); i++) { - auto& card = player.hand[i]; - if (card.is_null()) - continue; - if (is_valid_play(player, *card)) { - return i; - } - } - VERIFY_NOT_REACHED(); -} - -void Game::let_player_play_card() -{ - auto& player = current_player(); - - if (&player == &m_players[0]) - on_status_change("Select a card to play."_string); - else - on_status_change(String::formatted("Waiting for {} to play a card...", player).release_value_but_fixme_should_propagate_errors()); - - if (player.is_human) { - m_human_can_play = true; - if constexpr (HEARTS_DEBUG) { - auto card_index = pick_card(player); - auto& card = player.hand[card_index]; - card->set_inverted(true); - update(card->rect()); - } - return; - } - - play_card(player, pick_card(player)); -} - -size_t Game::player_index(Player& player) -{ - return &player - m_players; -} - -Player& Game::current_player() -{ - VERIFY(m_trick.size() < 4); - auto player_index = m_leading_player - m_players; - auto current_player_index = (player_index + m_trick.size()) % 4; - dbgln_if(HEARTS_DEBUG, "Leading player: {}, current player: {}", *m_leading_player, m_players[current_player_index]); - return m_players[current_player_index]; -} - -void Game::continue_game_after_delay(int interval_ms) -{ - m_delay_timer->start(interval_ms); -} - -void Game::advance_game() -{ - if (m_animation_playing) - return; - - if (m_inverted_card) { - m_inverted_card->set_inverted(false); - update(m_inverted_card->rect()); - m_inverted_card.clear(); - } - - if (m_state == State::Play && game_ended()) { - m_state = State::GameEnded; - on_status_change("Game ended."_string); - advance_game(); - return; - } - - if (m_state == State::GameEnded) { - int highest_score = 0; - for (auto& player : m_players) { - int previous_score = player.scores.is_empty() ? 0 : player.scores[player.scores.size() - 1]; - auto score = previous_score + calculate_score((player)); - player.scores.append(score); - if (score > highest_score) - highest_score = score; - } - bool game_over = highest_score >= 100; - show_score_card(game_over); - auto next_hand_number = m_hand_number + 1; - if (game_over) - next_hand_number = 0; - setup(move(m_players[0].name), next_hand_number); - return; - } - - if (m_state == State::PassingSelect) { - if (!m_players[0].is_human) { - select_cards_for_passing(); - m_state = State::PassingSelectConfirmed; - continue_game_after_delay(); - } - return; - } - - if (m_state == State::PassingSelectConfirmed) { - pass_cards(); - continue_game_after_delay(); - return; - } - - if (m_state == State::PassingAccept) { - if (!m_players[0].is_human) { - m_state = State::Play; - continue_game_after_delay(); - } - return; - } - - clear_highlighted_cards(); - m_passing_button->set_visible(false); - - if (m_trick_number == 0 && m_trick.is_empty()) { - // Find whoever has 2 of Clubs, they get to play the first card - for (auto& player : m_players) { - auto clubs_2_card = player.hand.first_matching([](auto& card) { - return card->suit() == Cards::Suit::Clubs && hearts_card_value(*card) == CardValue::Number_2; - }); - if (clubs_2_card.has_value()) { - m_leading_player = &player; - let_player_play_card(); - return; - } - } - } - - if (m_trick.size() < 4) { - let_player_play_card(); - return; - } - - auto leading_card_suit = m_trick[0]->suit(); - size_t taker_index = 0; - auto taker_value = hearts_card_value(m_trick[0]); - for (size_t i = 1; i < 4; i++) { - if (m_trick[i]->suit() != leading_card_suit) - continue; - if (hearts_card_value(m_trick[i]) <= taker_value) - continue; - taker_index = i; - taker_value = hearts_card_value(m_trick[i]); - } - auto leading_player_index = player_index(*m_leading_player); - auto taking_player_index = (leading_player_index + taker_index) % 4; - auto& taking_player = m_players[taking_player_index]; - dbgln_if(HEARTS_DEBUG, "{} takes the trick", taking_player); - for (auto& card : m_trick) { - if (hearts_card_points(card) == 0) - continue; - dbgln_if(HEARTS_DEBUG, "{} takes card {}", taking_player, card); - taking_player.cards_taken.append(card); - } - - start_animation( - m_trick, - taking_player.taken_cards_target, - [&] { - ++m_trick_number; - - if (game_ended()) - for (auto& player : m_players) - quick_sort(player.cards_taken, hearts_card_less); - - m_trick.clear_with_capacity(); - m_leading_player = &taking_player; - dbgln_if(HEARTS_DEBUG, "-----"); - advance_game(); - }, - 750); - - return; -} - -void Game::keydown_event(GUI::KeyEvent& event) -{ - if (event.shift() && event.key() == KeyCode::Key_F10) { - m_players[0].is_human = !m_players[0].is_human; - advance_game(); - } else if (event.key() == KeyCode::Key_F10) { - if (m_human_can_play && m_state == State::Play) - play_card(m_players[0], pick_card(m_players[0])); - else if (m_state == State::PassingSelect) - select_cards_for_passing(); - } else if (event.key() == KeyCode::Key_Space) { - if (m_human_can_play && m_state == State::Play) - play_card(m_players[0], pick_first_card_ltr(m_players[0])); - } else if (event.shift() && event.key() == KeyCode::Key_F11) { - dump_state(); - } else { - event.ignore(); - } -} - -void Game::play_card(Player& player, size_t card_index) -{ - if (player.is_human) - m_human_can_play = false; - VERIFY(player.hand[card_index]); - VERIFY(m_trick.size() < 4); - RefPtr card; - swap(player.hand[card_index], card); - dbgln_if(HEARTS_DEBUG, "{} plays {}", player, *card); - VERIFY(is_valid_play(player, *card)); - card->set_upside_down(false); - m_trick.append(*card); - - Gfx::IntPoint const trick_card_positions[] = { - { width / 2 - Card::width / 2, height / 2 - 30 }, - { width / 2 - Card::width + 15, height / 2 - Card::height / 2 - 15 }, - { width / 2 - Card::width / 2 + 15, height / 2 - Card::height + 15 }, - { width / 2, height / 2 - Card::height / 2 }, - }; - - VERIFY(m_leading_player); - size_t leading_player_index = player_index(*m_leading_player); - - Vector> cards; - cards.append(*card); - start_animation( - cards, - trick_card_positions[(leading_player_index + m_trick.size() - 1) % 4], - [&] { - advance_game(); - }, - 0); -} - -bool Game::is_valid_play(Player& player, Card& card, ByteString* explanation) const -{ - // First card must be 2 of Clubs. - if (m_trick_number == 0 && m_trick.is_empty()) { - if (explanation) - *explanation = "The first card must be Two of Clubs."; - return card.suit() == Cards::Suit::Clubs && hearts_card_value(card) == CardValue::Number_2; - } - - // Can't play hearts or The Queen in the first trick. - if (m_trick_number == 0 && hearts_card_points(card) > 0) { - bool all_points_cards = true; - for (auto& other_card : player.hand) { - if (hearts_card_points(*other_card) == 0) { - all_points_cards = false; - break; - } - } - // ... unless the player only has points cards (e.g. all Hearts or - // 12 Hearts + Queen of Spades), in which case they're allowed to play Hearts. - if (all_points_cards && card.suit() == Cards::Suit::Hearts) - return true; - if (explanation) - *explanation = "You can't play a card worth points in the first trick."; - return false; - } - - // Leading card can't be hearts until hearts are broken - // unless the player only has hearts cards. - if (m_trick.is_empty()) { - if (are_hearts_broken() || card.suit() != Cards::Suit::Hearts) - return true; - auto non_hearts_card = player.hand.first_matching([](auto const& other_card) { - return !other_card.is_null() && other_card->suit() != Cards::Suit::Hearts; - }); - auto only_has_hearts = !non_hearts_card.has_value(); - if (!only_has_hearts && explanation) - *explanation = "Hearts haven't been broken."; - return only_has_hearts; - } - - // Player must follow suit unless they don't have any matching cards. - auto leading_card_suit = m_trick[0]->suit(); - if (leading_card_suit == card.suit()) - return true; - auto has_matching_card = player.has_card_of_suit(leading_card_suit); - if (has_matching_card && explanation) - *explanation = "You must follow suit."; - return !has_matching_card; -} - -bool Game::are_hearts_broken() const -{ - for (auto& player : m_players) - for (auto& card : player.cards_taken) - if (card->suit() == Cards::Suit::Hearts) - return true; - return false; -} - -void Game::card_clicked_during_passing(size_t, Card& card) -{ - if (!is_card_highlighted(card)) { - if (m_cards_highlighted.size() < 3) - highlight_card(card); - } else - unhighlight_card(card); - - m_passing_button->set_enabled(m_cards_highlighted.size() == 3); -} - -void Game::card_clicked_during_play(size_t card_index, Card& card) -{ - ByteString explanation; - if (!is_valid_play(m_players[0], card, &explanation)) { - if (m_inverted_card) - m_inverted_card->set_inverted(false); - card.set_inverted(true); - update(card.rect()); - m_inverted_card = card; - on_status_change(String::formatted("You can't play this card: {}", explanation).release_value_but_fixme_should_propagate_errors()); - continue_game_after_delay(); - return; - } - play_card(m_players[0], card_index); -} - -void Game::card_clicked(size_t card_index, Card& card) -{ - if (m_state == State::PassingSelect) - card_clicked_during_passing(card_index, card); - else - card_clicked_during_play(card_index, card); -} - -void Game::mouseup_event(GUI::MouseEvent& event) -{ - GUI::Frame::mouseup_event(event); - - if (event.button() != GUI::MouseButton::Primary) - return; - - if (!m_human_can_play) - return; - - for (ssize_t i = m_players[0].hand.size() - 1; i >= 0; i--) { - auto& card = m_players[0].hand[i]; - if (card.is_null()) - continue; - if (card->rect().contains(event.position())) { - card_clicked(i, *card); - break; - } - } -} - -int Game::calculate_score(Player& player) -{ - Optional min_score; - Optional max_score; - int player_score = 0; - for (auto& other_player : m_players) { - int score = 0; - for (auto& card : other_player.cards_taken) - if (card->suit() == Cards::Suit::Spades && card->rank() == Cards::Rank::Queen) - score += 13; - else if (card->suit() == Cards::Suit::Hearts) - score++; - if (!min_score.has_value() || score < min_score.value()) - min_score = score; - if (!max_score.has_value() || score > max_score.value()) - max_score = score; - if (&other_player == &player) - player_score = score; - } - constexpr int sum_points_of_all_cards = 26; - if (player_score == sum_points_of_all_cards) - return 0; - else if (max_score.value() == sum_points_of_all_cards) - return 26; - else - return player_score; -} - -static constexpr int card_highlight_offset = -20; - -bool Game::is_card_highlighted(Card& card) -{ - return m_cards_highlighted.contains(card); -} - -void Game::highlight_card(Card& card) -{ - VERIFY(!m_cards_highlighted.contains(card)); - m_cards_highlighted.set(card); - Gfx::IntRect update_rect = card.rect(); - card.set_position(card.position().translated(0, card_highlight_offset)); - update_rect = update_rect.united(card.rect()); - update(update_rect); -} - -void Game::unhighlight_card(Card& card) -{ - VERIFY(m_cards_highlighted.contains(card)); - m_cards_highlighted.remove(card); - Gfx::IntRect update_rect = card.rect(); - card.set_position(card.position().translated(0, -card_highlight_offset)); - update_rect = update_rect.united(card.rect()); - update(update_rect); -} - -void Game::clear_highlighted_cards() -{ - for (auto& card : m_cards_highlighted) - card->set_position(card->position().translated(0, -card_highlight_offset)); - m_cards_highlighted.clear(); -} - -void Game::reposition_hand(Player& player) -{ - auto card_position = player.first_card_position; - for (auto& card : player.hand) { - card->set_position(is_card_highlighted(*card) ? card_position.translated(0, card_highlight_offset) : card_position); - card_position.translate_by(player.card_offset); - } -} - -void Game::select_cards_for_passing() -{ - clear_highlighted_cards(); - auto selected_cards = m_players[0].pick_cards_to_pass(passing_direction()); - highlight_card(selected_cards[0]); - highlight_card(selected_cards[1]); - highlight_card(selected_cards[2]); - m_passing_button->set_enabled(true); -} - -void Game::pass_cards() -{ - Vector> first_player_cards; - for (auto& card : m_cards_highlighted) - first_player_cards.append(*card); - clear_highlighted_cards(); - VERIFY(first_player_cards.size() == 3); - - Vector> passed_cards[4]; - passed_cards[0] = first_player_cards; - passed_cards[1] = m_players[1].pick_cards_to_pass(passing_direction()); - passed_cards[2] = m_players[2].pick_cards_to_pass(passing_direction()); - passed_cards[3] = m_players[3].pick_cards_to_pass(passing_direction()); - - for (size_t i = 0; i < 4; i++) { - m_players[i].remove_cards(passed_cards[i]); - - int destination_player_index = i; - switch (passing_direction()) { - case PassingDirection::Left: - destination_player_index += 1; - break; - case PassingDirection::Across: - destination_player_index += 2; - break; - case PassingDirection::Right: - destination_player_index += 3; - break; - default: - VERIFY_NOT_REACHED(); - } - destination_player_index %= 4; - - for (auto& card : passed_cards[i]) { - m_players[destination_player_index].hand.append(card); - if constexpr (!HEARTS_DEBUG) - card->set_upside_down(destination_player_index != 0); - if (destination_player_index == 0) - highlight_card(card); - } - } - - for (auto& player : m_players) { - VERIFY(player.hand.size() == 13); - player.sort_hand(); - reposition_hand(player); - Gfx::IntRect update_rect; - for (auto& card : player.hand) - update_rect = update_rect.united(card->rect()); - update(update_rect); - } - - m_state = State::PassingAccept; - m_passing_button->set_text("OK"_string); - m_passing_button->set_enabled(true); -} - -PassingDirection Game::passing_direction() const -{ - VERIFY(m_hand_number % 4 != 3); - return static_cast(m_hand_number % 4); -} - -void Game::paint_event(GUI::PaintEvent& event) -{ - GUI::Frame::paint_event(event); - - GUI::Painter painter(*this); - painter.add_clip_rect(frame_inner_rect()); - painter.add_clip_rect(event.rect()); - - Gfx::Color background_color = this->background_color(); - painter.clear_rect(frame_inner_rect(), background_color); - - for (auto& player : m_players) { - auto& font = painter.font().bold_variant(); - Gfx::Color text_color = background_color.luminosity() > 80 ? Color::Black : Color::White; - painter.draw_text(player.name_position, player.name, font, player.name_alignment, text_color, Gfx::TextElision::None); - - if (!game_ended()) { - for (auto& card : player.hand) - if (!card.is_null()) - card->paint(painter); - } else { - // FIXME: reposition cards in advance_game() maybe - auto card_position = player.first_card_position; - for (auto& card : player.cards_taken) { - card->set_upside_down(false); - card->set_position(card_position); - card->paint(painter); - card_position.translate_by(player.card_offset); - } - } - } - - for (size_t i = 0; i < m_trick.size(); i++) - m_trick[i]->paint(painter); -} - -void Game::dump_state() const -{ - if constexpr (HEARTS_DEBUG) { - dbgln("------------------------------"); - for (uint8_t i = 0; i < 4; ++i) { - auto& player = m_players[i]; - dbgln("Player {}", player.name); - dbgln("Hand:"); - for (auto const& card : player.hand) - if (card.is_null()) - dbgln(" "); - else - dbgln(" {}", *card); - dbgln("Taken:"); - for (auto const& card : player.cards_taken) - dbgln(" {}", card); - } - } -} - -} diff --git a/Userland/Games/Hearts/Game.h b/Userland/Games/Hearts/Game.h deleted file mode 100644 index 605f231d055..00000000000 --- a/Userland/Games/Hearts/Game.h +++ /dev/null @@ -1,117 +0,0 @@ -/* - * Copyright (c) 2020, Till Mayer - * Copyright (c) 2021, Gunnar Beutner - * Copyright (c) 2022, the SerenityOS developers. - * Copyright (c) 2022, Sam Atkins - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include "Player.h" -#include -#include -#include - -using Cards::Card; - -namespace Hearts { - -class Game final : public Cards::CardGame { - C_OBJECT(Game) -public: - static constexpr int width = 640; - static constexpr int height = 480; - - virtual ~Game() override = default; - - void setup(ByteString player_name, int hand_number = 0); - - Function on_status_change; - -private: - Game(); - - void reset(); - - void show_score_card(bool game_over); - - void dump_state() const; - - void play_card(Player& player, size_t card_index); - bool are_hearts_broken() const; - bool is_valid_play(Player& player, Card& card, ByteString* explanation = nullptr) const; - void let_player_play_card(); - void continue_game_after_delay(int interval_ms = 750); - void advance_game(); - size_t pick_card(Player& player); - size_t pick_first_card_ltr(Player& player); - size_t player_index(Player& player); - Player& current_player(); - bool game_ended() const { return m_trick_number == 13; } - int calculate_score(Player& player); - bool other_player_has_lower_value_card(Player& player, Card& card); - bool other_player_has_higher_value_card(Player& player, Card& card); - bool other_player_has_queen_of_spades(Player& player); - - void reposition_hand(Player&); - bool is_card_highlighted(Card& card); - void clear_highlighted_cards(); - void highlight_card(Card& card); - void unhighlight_card(Card& card); - void select_cards_for_passing(); - void pass_cards(); - PassingDirection passing_direction() const; - - void start_animation(Vector> cards, Gfx::IntPoint end, Function did_finish_callback, int initial_delay_ms, int steps = 30); - void stop_animation(); - - virtual void paint_event(GUI::PaintEvent&) override; - virtual void mouseup_event(GUI::MouseEvent&) override; - virtual void keydown_event(GUI::KeyEvent&) override; - virtual void timer_event(Core::TimerEvent&) override; - - void card_clicked(size_t card_index, Card& card); - void card_clicked_during_passing(size_t card_index, Card& card); - void card_clicked_during_play(size_t card_index, Card& card); - - RefPtr m_passing_button; - - enum class State { - PassingSelect, - PassingSelectConfirmed, - PassingAccept, - Play, - GameEnded, - }; - - State m_state { State::PassingSelect }; - int m_hand_number { 0 }; - - HashTable> m_cards_highlighted; - - Player m_players[4]; - Vector> m_trick; - Player* m_leading_player { nullptr }; - u8 m_trick_number { 0 }; - RefPtr m_delay_timer; - bool m_human_can_play { false }; - - struct AnimatedCard { - NonnullRefPtr card; - Gfx::IntPoint start; - }; - - RefPtr m_animation_delay_timer; - bool m_animation_playing { false }; - Vector m_animation_cards; - Gfx::IntPoint m_animation_end; - int m_animation_current_step { 0 }; - int m_animation_steps { 0 }; - OwnPtr> m_animation_did_finish; - - RefPtr m_inverted_card; -}; - -} diff --git a/Userland/Games/Hearts/Hearts.gml b/Userland/Games/Hearts/Hearts.gml deleted file mode 100644 index 01f08d5a4b6..00000000000 --- a/Userland/Games/Hearts/Hearts.gml +++ /dev/null @@ -1,13 +0,0 @@ -@Hearts::MainWidget { - fill_with_background_color: true - layout: @GUI::VerticalBoxLayout {} - - @Hearts::Game { - name: "game" - fill_with_background_color: true - } - - @GUI::Statusbar { - name: "statusbar" - } -} diff --git a/Userland/Games/Hearts/Helpers.h b/Userland/Games/Hearts/Helpers.h deleted file mode 100644 index 61273be5f00..00000000000 --- a/Userland/Games/Hearts/Helpers.h +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright (c) 2021, Gunnar Beutner - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include - -using Cards::Card; - -namespace Hearts { - -enum class CardValue : uint8_t { - Number_2, - Number_3, - Number_4, - Number_5, - Number_6, - Number_7, - Number_8, - Number_9, - Number_10, - Jack, - Queen, - King, - Ace -}; - -inline CardValue hearts_card_value(Card const& card) -{ - // Ace has a higher value than all other cards in Hearts - if (card.rank() == Cards::Rank::Ace) - return CardValue::Ace; - else - return static_cast(to_underlying(card.rank()) - 1); -} - -inline uint8_t hearts_card_points(Card const& card) -{ - if (card.suit() == Cards::Suit::Hearts) - return 1; - else if (card.suit() == Cards::Suit::Spades && hearts_card_value(card) == CardValue::Queen) - return 13; - else - return 0; -} - -inline bool hearts_card_less(RefPtr& card1, RefPtr& card2) -{ - if (card1->suit() != card2->suit()) - return card1->suit() < card2->suit(); - else - return hearts_card_value(*card1) < hearts_card_value(*card2); -} - -} diff --git a/Userland/Games/Hearts/MainWidget.h b/Userland/Games/Hearts/MainWidget.h deleted file mode 100644 index 2a63d649924..00000000000 --- a/Userland/Games/Hearts/MainWidget.h +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright (c) 2024, Sanil Gupta . - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include - -namespace Hearts { - -class MainWidget : public GUI::Widget { - C_OBJECT_ABSTRACT(MainWidget) -public: - static ErrorOr> try_create(); - virtual ~MainWidget() override = default; - -private: - MainWidget() = default; -}; - -} diff --git a/Userland/Games/Hearts/Player.cpp b/Userland/Games/Hearts/Player.cpp deleted file mode 100644 index 7a72f1063b9..00000000000 --- a/Userland/Games/Hearts/Player.cpp +++ /dev/null @@ -1,170 +0,0 @@ -/* - * Copyright (c) 2021, Gunnar Beutner - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "Player.h" -#include "Helpers.h" -#include -#include - -namespace Hearts { - -static bool compare_card_value(CardWithIndex& cwi1, CardWithIndex& cwi2) -{ - return hearts_card_value(*cwi2.card) < hearts_card_value(*cwi1.card); -} - -static bool compare_card_points_and_value(CardWithIndex& cwi1, CardWithIndex& cwi2) -{ - if (hearts_card_points(*cwi2.card) < hearts_card_points(*cwi1.card)) - return true; - if (hearts_card_points(*cwi1.card) == hearts_card_points(*cwi2.card) && hearts_card_value(*cwi2.card) < hearts_card_value(*cwi1.card)) - return true; - return false; -} - -Vector> Player::pick_cards_to_pass(PassingDirection) -{ - auto sorted_hand = hand_sorted_by_fn(compare_card_value); - Vector> cards; - cards.append(*sorted_hand[0].card); - cards.append(*sorted_hand[1].card); - cards.append(*sorted_hand[2].card); - return cards; -} - -Vector Player::hand_sorted_by_fn(bool (*fn)(CardWithIndex&, CardWithIndex&)) const -{ - Vector sorted_hand; - for (size_t i = 0; i < hand.size(); i++) { - auto& card = hand[i]; - if (card) - sorted_hand.empend(*card, i); - } - quick_sort(sorted_hand, fn); - return sorted_hand; -} - -size_t Player::pick_lead_card(Function valid_play, Function prefer_card) -{ - auto sorted_hand = hand_sorted_by_fn(compare_card_points_and_value); - - if constexpr (HEARTS_DEBUG) { - dbgln("Sorted hand:"); - for (auto& cwi : sorted_hand) - dbgln("{}", *cwi.card); - dbgln("----"); - } - - ssize_t last_index = -1; - for (auto& cwi : sorted_hand) { - if (!valid_play(*cwi.card)) - continue; - if (prefer_card(*cwi.card)) { - dbgln_if(HEARTS_DEBUG, "Preferring card {}", *cwi.card); - return cwi.index; - } - last_index = cwi.index; - } - return last_index; -} - -Optional Player::pick_low_points_high_value_card(Optional suit) -{ - auto sorted_hand = hand_sorted_by_fn(compare_card_value); - int min_points = -1; - Optional card_index; - for (auto& cwi : sorted_hand) { - if (suit.has_value() && cwi.card->suit() != suit.value()) - continue; - auto points = hearts_card_points(*cwi.card); - if (min_points == -1 || points < min_points) { - min_points = points; - card_index = cwi.index; - } - } - VERIFY(card_index.has_value() || suit.has_value()); - return card_index; -} - -Optional Player::pick_lower_value_card(Card& other_card) -{ - for (ssize_t i = hand.size() - 1; i >= 0; i--) { - auto& card = hand[i]; - if (card.is_null()) - continue; - if (card->suit() == other_card.suit() && hearts_card_value(*card) < hearts_card_value(other_card)) - return i; - } - return {}; -} - -Optional Player::pick_slightly_higher_value_card(Card& other_card) -{ - for (size_t i = 0; i < hand.size(); i++) { - auto& card = hand[i]; - if (card.is_null()) - continue; - if (card->suit() == other_card.suit() && hearts_card_value(*card) > hearts_card_value(other_card)) - return i; - } - return {}; -} - -size_t Player::pick_max_points_card(Function ignore_card) -{ - auto queen_of_spades_maybe = pick_specific_card(Cards::Suit::Spades, CardValue::Queen); - if (queen_of_spades_maybe.has_value()) - return queen_of_spades_maybe.value(); - if (has_card_of_suit(Cards::Suit::Hearts)) { - auto highest_hearts_card_index = pick_last_card(); - auto& card = hand[highest_hearts_card_index]; - if (!ignore_card(*card)) - return highest_hearts_card_index; - } - return pick_low_points_high_value_card().value(); -} - -Optional Player::pick_specific_card(Cards::Suit suit, CardValue value) -{ - for (size_t i = 0; i < hand.size(); i++) { - auto& card = hand[i]; - if (card.is_null()) - continue; - if (card->suit() == suit && hearts_card_value(*card) == value) - return i; - } - return {}; -} - -size_t Player::pick_last_card() -{ - for (ssize_t i = hand.size() - 1; i >= 0; i--) { - auto& card = hand[i]; - if (card.is_null()) - continue; - return i; - } - VERIFY_NOT_REACHED(); -} - -bool Player::has_card_of_suit(Cards::Suit suit) -{ - auto matching_card = hand.first_matching([&](auto const& other_card) { - return !other_card.is_null() && other_card->suit() == suit; - }); - return matching_card.has_value(); -} - -void Player::remove_cards(Vector> const& cards) -{ - for (auto& card : cards) { - hand.remove_first_matching([&card](auto& other_card) { - return other_card == card; - }); - } -} - -} diff --git a/Userland/Games/Hearts/Player.h b/Userland/Games/Hearts/Player.h deleted file mode 100644 index d401eb7c4af..00000000000 --- a/Userland/Games/Hearts/Player.h +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright (c) 2021, Gunnar Beutner - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include "Helpers.h" -#include -#include - -using Cards::Card; - -namespace Hearts { - -enum class PassingDirection { - Left, - Right, - Across -}; - -struct CardWithIndex { - NonnullRefPtr card; - size_t index; -}; - -struct Player { - AK_MAKE_NONMOVABLE(Player); - -public: - Player() - { - } - - Vector> pick_cards_to_pass(PassingDirection); - size_t pick_lead_card(Function, Function); - Optional pick_low_points_high_value_card(Optional suit = {}); - Optional pick_lower_value_card(Card& other_card); - Optional pick_slightly_higher_value_card(Card& other_card); - size_t pick_max_points_card(Function); - Optional pick_specific_card(Cards::Suit suit, CardValue value); - size_t pick_last_card(); - bool has_card_of_suit(Cards::Suit suit); - Vector hand_sorted_by_fn(bool (*)(CardWithIndex&, CardWithIndex&)) const; - - void sort_hand() { quick_sort(hand, hearts_card_less); } - void remove_cards(Vector> const& cards); - - Vector> hand; - Vector> cards_taken; - Vector scores; - Gfx::IntPoint first_card_position; - Gfx::IntPoint card_offset; - Gfx::IntRect name_position; - Gfx::TextAlignment name_alignment; - Gfx::IntPoint taken_cards_target; - ByteString name; - bool is_human { false }; -}; - -} - -template<> -struct AK::Formatter : Formatter { - ErrorOr format(FormatBuilder& builder, Hearts::Player const& player) - { - return builder.put_string(player.name); - } -}; diff --git a/Userland/Games/Hearts/ScoreCard.cpp b/Userland/Games/Hearts/ScoreCard.cpp deleted file mode 100644 index 41077681f9c..00000000000 --- a/Userland/Games/Hearts/ScoreCard.cpp +++ /dev/null @@ -1,89 +0,0 @@ -/* - * Copyright (c) 2021, Gunnar Beutner - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "ScoreCard.h" -#include -#include -#include -#include - -namespace Hearts { - -ScoreCard::ScoreCard(Player (&players)[4], bool game_over) - : m_players(players) - , m_game_over(game_over) -{ - set_min_size(recommended_size()); - resize(recommended_size()); -} - -Gfx::IntSize ScoreCard::recommended_size() -{ - auto& card_font = font().bold_variant(); - - return Gfx::IntSize { - 4 * column_width + 3 * cell_padding, - 16 * card_font.pixel_size_rounded_up() + 15 * cell_padding - }; -} -void ScoreCard::paint_event(GUI::PaintEvent& event) -{ - GUI::Widget::paint_event(event); - - GUI::Painter painter(*this); - painter.add_clip_rect(frame_inner_rect()); - painter.add_clip_rect(event.rect()); - - auto& font = painter.font().bold_variant(); - - auto cell_rect = [this, &font](int x, int y) { - return Gfx::IntRect { - frame_inner_rect().left() + x * column_width + x * cell_padding, - frame_inner_rect().top() + y * font.pixel_size_rounded_up() + y * cell_padding, - column_width, - font.pixel_size_rounded_up(), - }; - }; - - VERIFY(!m_players[0].scores.is_empty()); - - int leading_score = -1; - for (size_t player_index = 0; player_index < 4; player_index++) { - auto& player = m_players[player_index]; - auto cumulative_score = player.scores[player.scores.size() - 1]; - if (leading_score == -1 || cumulative_score < leading_score) - leading_score = cumulative_score; - } - - for (int player_index = 0; player_index < 4; player_index++) { - auto& player = m_players[player_index]; - auto cumulative_score = player.scores[player.scores.size() - 1]; - auto leading_color = m_game_over ? Color::Magenta : Color::Blue; - auto text_color = cumulative_score == leading_score ? leading_color : Color::Black; - dbgln("text_rect: {}", cell_rect(player_index, 0)); - painter.draw_text(cell_rect(player_index, 0), - player.name, - font, Gfx::TextAlignment::Center, - text_color); - for (int score_index = 0; score_index < (int)player.scores.size(); score_index++) { - auto text_rect = cell_rect(player_index, 1 + score_index); - auto score_text = ByteString::formatted("{}", player.scores[score_index]); - auto score_text_width = font.width_rounded_up(score_text); - if (score_index != (int)player.scores.size() - 1) { - painter.draw_line( - { text_rect.left() + text_rect.width() / 2 - score_text_width / 2 - 3, text_rect.top() + font.pixel_size_rounded_up() / 2 }, - { text_rect.right() - text_rect.width() / 2 + score_text_width / 2 + 2, text_rect.top() + font.pixel_size_rounded_up() / 2 }, - text_color); - } - painter.draw_text(text_rect, - score_text, - font, Gfx::TextAlignment::Center, - text_color); - } - } -} - -} diff --git a/Userland/Games/Hearts/ScoreCard.h b/Userland/Games/Hearts/ScoreCard.h deleted file mode 100644 index 7275815c463..00000000000 --- a/Userland/Games/Hearts/ScoreCard.h +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright (c) 2021, Gunnar Beutner - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include "Player.h" -#include -#include - -namespace Hearts { - -class ScoreCard : public GUI::Frame { - C_OBJECT(ScoreCard); - - Gfx::IntSize recommended_size(); - -private: - ScoreCard(Player (&players)[4], bool game_over); - - virtual void paint_event(GUI::PaintEvent&) override; - - static constexpr int column_width = 70; - static constexpr int cell_padding = 5; - - Player (&m_players)[4]; - bool m_game_over { false }; -}; - -} diff --git a/Userland/Games/Hearts/SettingsDialog.cpp b/Userland/Games/Hearts/SettingsDialog.cpp deleted file mode 100644 index 3371f5e5827..00000000000 --- a/Userland/Games/Hearts/SettingsDialog.cpp +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright (c) 2021, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "SettingsDialog.h" -#include -#include -#include -#include - -SettingsDialog::SettingsDialog(GUI::Window* parent, ByteString player_name) - : GUI::Dialog(parent) - , m_player_name(move(player_name)) -{ - set_rect({ 0, 0, 250, 75 }); - set_title("Settings"); - set_icon(parent->icon()); - set_resizable(false); - - auto main_widget = set_main_widget(); - main_widget->set_fill_with_background_color(true); - - main_widget->set_layout(4); - - auto& name_box = main_widget->add(); - name_box.set_layout(GUI::Margins {}, 4); - - auto& name_label = name_box.add("Name:"_string); - name_label.set_text_alignment(Gfx::TextAlignment::CenterLeft); - - auto& textbox = name_box.add(); - textbox.set_text(m_player_name); - textbox.on_change = [&] { - m_player_name = textbox.text(); - }; - - auto& button_box = main_widget->add(); - button_box.set_layout(GUI::Margins {}, 12); - - button_box.add("Cancel"_string).on_click = [this](auto) { - done(ExecResult::Cancel); - }; - - button_box.add("OK"_string).on_click = [this](auto) { - done(ExecResult::OK); - }; -} diff --git a/Userland/Games/Hearts/SettingsDialog.h b/Userland/Games/Hearts/SettingsDialog.h deleted file mode 100644 index d85407c82d9..00000000000 --- a/Userland/Games/Hearts/SettingsDialog.h +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright (c) 2021, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include - -class SettingsDialog : public GUI::Dialog { - C_OBJECT(SettingsDialog) -public: - ByteString const& player_name() const { return m_player_name; } - -private: - SettingsDialog(GUI::Window* parent, ByteString player_name); - - ByteString m_player_name { "Gunnar" }; -}; diff --git a/Userland/Games/Hearts/main.cpp b/Userland/Games/Hearts/main.cpp deleted file mode 100644 index ccb14f990ed..00000000000 --- a/Userland/Games/Hearts/main.cpp +++ /dev/null @@ -1,119 +0,0 @@ -/* - * Copyright (c) 2020, Till Mayer - * Copyright (c) 2021, Gunnar Beutner - * Copyright (c) 2021, Mustafa Quraish - * Copyright (c) 2022, Sam Atkins - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "Game.h" -#include "MainWidget.h" -#include "SettingsDialog.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -ErrorOr serenity_main(Main::Arguments arguments) -{ - auto app = TRY(GUI::Application::create(arguments)); - auto app_icon = TRY(GUI::Icon::try_create_default_icon("app-hearts"sv)); - - Config::pledge_domains({ "Games", "Hearts" }); - Config::monitor_domain("Games"); - - TRY(Core::System::pledge("stdio recvfd sendfd rpath unix proc exec")); - - TRY(Desktop::Launcher::add_allowed_handler_with_only_specific_urls("/bin/Help", { URL::create_with_file_scheme("/usr/share/man/man6/Hearts.md") })); - TRY(Desktop::Launcher::seal_allowlist()); - - TRY(Core::System::pledge("stdio recvfd sendfd rpath proc exec")); - - TRY(Core::System::unveil("/tmp/session/%sid/portal/launch", "rw")); - TRY(Core::System::unveil("/res", "r")); - TRY(Core::System::unveil("/bin/GamesSettings", "x")); - TRY(Core::System::unveil(nullptr, nullptr)); - - auto window = GUI::Window::construct(); - window->set_title("Hearts"); - - auto widget = TRY(Hearts::MainWidget::try_create()); - window->set_main_widget(widget); - - auto& game = *widget->find_descendant_of_type_named("game"); - game.set_focus(true); - - auto& statusbar = *widget->find_descendant_of_type_named("statusbar"); - statusbar.set_text(0, "Score: 0"_string); - - ByteString player_name = Config::read_string("Hearts"sv, ""sv, "player_name"sv, "Gunnar"sv); - - game.on_status_change = [&](String const& status) { - statusbar.set_override_text(status); - }; - - app->on_action_enter = [&](GUI::Action& action) { - statusbar.set_override_text(action.status_tip()); - }; - - app->on_action_leave = [&](GUI::Action&) { - statusbar.set_override_text({}); - }; - - auto change_settings = [&] { - auto settings_dialog = SettingsDialog::construct(window, player_name); - if (settings_dialog->exec() != GUI::Dialog::ExecResult::OK) - return; - - player_name = settings_dialog->player_name(); - - Config::write_string("Hearts"sv, ""sv, "player_name"sv, player_name); - - GUI::MessageBox::show(settings_dialog, "Settings have been successfully saved and will take effect in the next game."sv, "Settings Changed Successfully"sv, GUI::MessageBox::Type::Information); - }; - - auto game_menu = window->add_menu("&Game"_string); - - game_menu->add_action(GUI::Action::create("&New Game", { Mod_None, Key_F2 }, TRY(Gfx::Bitmap::load_from_file("/res/icons/16x16/reload.png"sv)), [&](auto&) { - game.setup(player_name); - })); - game_menu->add_separator(); - game_menu->add_action(TRY(Cards::make_cards_settings_action(window))); - game_menu->add_action(GUI::Action::create("&Settings", TRY(Gfx::Bitmap::load_from_file("/res/icons/16x16/settings.png"sv)), [&](auto&) { - change_settings(); - })); - game_menu->add_separator(); - game_menu->add_action(GUI::CommonActions::make_quit_action([&](auto&) { app->quit(); })); - - auto view_menu = window->add_menu("&View"_string); - view_menu->add_action(GUI::CommonActions::make_fullscreen_action([&](auto&) { - window->set_fullscreen(!window->is_fullscreen()); - })); - - auto help_menu = window->add_menu("&Help"_string); - help_menu->add_action(GUI::CommonActions::make_command_palette_action(window)); - help_menu->add_action(GUI::CommonActions::make_help_action([](auto&) { - Desktop::Launcher::open(URL::create_with_file_scheme("/usr/share/man/man6/Hearts.md"), "/bin/Help"); - })); - help_menu->add_action(GUI::CommonActions::make_about_action("Hearts"_string, app_icon, window)); - - window->set_resizable(false); - window->resize(Hearts::Game::width, Hearts::Game::height + statusbar.max_height().as_int()); - window->set_icon(app_icon.bitmap_for_size(16)); - window->show(); - game.setup(player_name); - - return app->exec(); -} diff --git a/Userland/Games/MasterWord/CMakeLists.txt b/Userland/Games/MasterWord/CMakeLists.txt deleted file mode 100644 index c4948473c9f..00000000000 --- a/Userland/Games/MasterWord/CMakeLists.txt +++ /dev/null @@ -1,16 +0,0 @@ -serenity_component( - MasterWord - RECOMMENDED - TARGETS MasterWord -) - -compile_gml(MasterWord.gml MasterWordGML.cpp master_word_gml) - -set(SOURCES - main.cpp - WordGame.cpp - MasterWordGML.cpp -) - -serenity_app(MasterWord ICON app-masterword) -target_link_libraries(MasterWord PRIVATE LibCore LibGfx LibGUI LibConfig LibMain LibDesktop LibURL) diff --git a/Userland/Games/MasterWord/MainWidget.h b/Userland/Games/MasterWord/MainWidget.h deleted file mode 100644 index 540568e75b5..00000000000 --- a/Userland/Games/MasterWord/MainWidget.h +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright (c) 2023, the SerenityOS developers - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include - -namespace MasterWord { - -class MainWidget : public GUI::Widget { - C_OBJECT_ABSTRACT(MainWidget) -public: - static ErrorOr> try_create(); - virtual ~MainWidget() override = default; - -private: - MainWidget() = default; -}; - -} diff --git a/Userland/Games/MasterWord/MasterWord.gml b/Userland/Games/MasterWord/MasterWord.gml deleted file mode 100644 index d7af8ec7430..00000000000 --- a/Userland/Games/MasterWord/MasterWord.gml +++ /dev/null @@ -1,12 +0,0 @@ -@MasterWord::MainWidget { - fill_with_background_color: true - layout: @GUI::VerticalBoxLayout {} - - @MasterWord::WordGame { - name: "word_game" - } - - @GUI::Statusbar { - name: "statusbar" - } -} diff --git a/Userland/Games/MasterWord/WordGame.cpp b/Userland/Games/MasterWord/WordGame.cpp deleted file mode 100644 index d18a2149e54..00000000000 --- a/Userland/Games/MasterWord/WordGame.cpp +++ /dev/null @@ -1,323 +0,0 @@ -/* - * Copyright (c) 2022, Joe Petrus - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "WordGame.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -// TODO: Add stats -namespace MasterWord { - -WordGame::WordGame() - : m_clear_message_timer(Core::Timer::create_single_shot(5000, [this] { clear_message(); })) -{ - read_words(); - m_num_letters = Config::read_i32("MasterWord"sv, ""sv, "word_length"sv, 5); - m_max_guesses = Config::read_i32("MasterWord"sv, ""sv, "max_guesses"sv, 6); - m_check_guesses = Config::read_bool("MasterWord"sv, ""sv, "check_guesses_in_dictionary"sv, false); - reset(); - pick_font(); -} - -void WordGame::reset() -{ - m_current_guess = {}; - m_guesses.clear(); - auto maybe_word = WordGame::random_word(m_num_letters); - if (maybe_word.has_value()) - m_current_word = maybe_word.value(); - else { - GUI::MessageBox::show(window(), ByteString::formatted("Could not get a random {} letter word. Defaulting to 5.", m_num_letters), "MasterWord"sv); - if (m_num_letters != 5) { - m_num_letters = 5; - reset(); - } - } - set_fixed_size(game_size()); - clear_message(); - update(); -} - -void WordGame::pick_font() -{ - ByteString best_font_name; - auto best_font_size = -1; - auto& font_database = Gfx::FontDatabase::the(); - font_database.for_each_font([&](Gfx::Font const& font) { - if (font.family() != "Liza" || font.weight() != 700) - return; - auto size = font.pixel_size_rounded_up(); - if (size * 2 <= m_letter_height && size > best_font_size) { - best_font_name = font.qualified_name().to_byte_string(); - best_font_size = size; - } - }); - - auto font = font_database.get_by_name(best_font_name); - set_font(font); -} - -void WordGame::resize_event(GUI::ResizeEvent&) -{ - pick_font(); - update(); -} - -void WordGame::keydown_event(GUI::KeyEvent& event) -{ - // If we can still add a letter and the key was alpha - if (m_current_guess.length() < m_num_letters && is_ascii_alpha(event.code_point())) { - m_current_guess = ByteString::formatted("{}{}", m_current_guess, event.text().to_uppercase()); - m_last_word_invalid = false; - } - // If backspace pressed and already have some letters entered - else if (event.key() == KeyCode::Key_Backspace && m_current_guess.length() > 0) { - m_current_guess = m_current_guess.substring(0, m_current_guess.length() - 1); - m_last_word_invalid = false; - } - // If return pressed - else if (event.key() == KeyCode::Key_Return) { - if (m_current_guess.length() < m_num_letters) { - show_message("Not enough letters"sv); - m_last_word_invalid = true; - } else if (!is_in_dictionary(m_current_guess)) { - show_message("Not in dictionary"sv); - m_last_word_invalid = true; - } else { - m_last_word_invalid = false; - clear_message(); - - add_guess(m_current_guess); - auto won = m_current_guess == m_current_word; - m_current_guess = {}; - if (won) { - GUI::MessageBox::show(window(), "You win!"sv, "MasterWord"sv); - reset(); - } else if (m_guesses.size() == m_max_guesses) { - GUI::MessageBox::show(window(), ByteString::formatted("You lose!\nThe word was {}", m_current_word), "MasterWord"sv); - reset(); - } - } - } else { - event.ignore(); - } - - update(); -} - -void WordGame::paint_event(GUI::PaintEvent& event) -{ - GUI::Frame::paint_event(event); - GUI::Painter painter(*this); - painter.add_clip_rect(frame_inner_rect()); - painter.add_clip_rect(event.rect()); - painter.fill_rect(event.rect(), m_background_color); - - for (size_t guess_index = 0; guess_index < m_max_guesses; ++guess_index) { - for (size_t letter_index = 0; letter_index < m_num_letters; ++letter_index) { - auto this_rect = letter_rect(guess_index, letter_index); - - if (guess_index < m_guesses.size()) { - - switch (m_guesses[guess_index].letter_states.at(letter_index)) { - case Correct: - painter.fill_rect(this_rect, m_right_letter_right_spot_color); - break; - case WrongSpot: - painter.fill_rect(this_rect, m_right_letter_wrong_spot_color); - break; - case Incorrect: - painter.fill_rect(this_rect, m_wrong_letter_color); - break; - } - - painter.draw_text(this_rect, m_guesses[guess_index].text.substring_view(letter_index, 1), font(), Gfx::TextAlignment::Center, m_text_color); - } else if (guess_index == m_guesses.size()) { - if (letter_index < m_current_guess.length()) - painter.draw_text(this_rect, m_current_guess.substring_view(letter_index, 1), font(), Gfx::TextAlignment::Center, m_text_color); - if (m_last_word_invalid) { - painter.fill_rect(this_rect, m_word_not_in_dict_color); - } - } - - painter.draw_rect(this_rect, m_border_color); - } - } -} - -Gfx::IntRect WordGame::letter_rect(size_t guess_number, size_t letter_number) const -{ - auto letter_left = m_outer_margin + letter_number * m_letter_width + m_letter_spacing * letter_number; - auto letter_top = m_outer_margin + guess_number * m_letter_height + m_letter_spacing * guess_number; - return Gfx::IntRect(int(letter_left), int(letter_top), m_letter_width, m_letter_height); -} - -bool WordGame::is_in_dictionary(AK::StringView guess) -{ - return !m_check_guesses || !m_words.ensure(guess.length()).find(guess).is_end(); -} - -void WordGame::read_words() -{ - m_words.clear(); - - auto try_load_words = [&]() -> ErrorOr { - auto response = TRY(Core::File::open("/res/words.txt"sv, Core::File::OpenMode::Read)); - auto words_file = TRY(Core::InputBufferedFile::create(move(response))); - Array buffer; - - while (!words_file->is_eof()) { - auto current_word = TRY(words_file->read_line(buffer)); - if (!current_word.starts_with('#') and current_word.length() > 0) - m_words.ensure(current_word.length()).append(current_word.to_uppercase_string()); - } - - return {}; - }; - - if (try_load_words().is_error()) { - GUI::MessageBox::show(nullptr, "Could not read /res/words.txt.\nPlease ensure this file exists and restart MasterWord."sv, "MasterWord"sv); - exit(0); - } -} - -Optional WordGame::random_word(size_t length) -{ - auto words_for_length = m_words.get(length); - if (words_for_length.has_value()) { - auto i = get_random_uniform(words_for_length->size()); - return words_for_length->at(i); - } - - return {}; -} - -size_t WordGame::shortest_word() -{ - auto available_lengths = m_words.keys(); - AK::quick_sort(available_lengths); - return available_lengths.first(); -} - -size_t WordGame::longest_word() -{ - auto available_lengths = m_words.keys(); - AK::quick_sort(available_lengths); - return available_lengths.last(); -} - -void WordGame::set_use_system_theme(bool b) -{ - if (b) { - auto theme = palette(); - m_right_letter_wrong_spot_color = Color::from_rgb(0xb59f3b); - m_right_letter_right_spot_color = Color::from_rgb(0x538d4e); - m_border_color = Color::Black; - m_wrong_letter_color = theme.window(); - m_background_color = theme.window(); - m_text_color = theme.accent(); - } else { - m_right_letter_wrong_spot_color = Color::from_rgb(0xb59f3b); - m_right_letter_right_spot_color = Color::from_rgb(0x538d4e); - m_border_color = Color::from_rgb(0x3a3a3c); - m_wrong_letter_color = m_border_color; - m_background_color = Color::from_rgb(0x121213); - m_text_color = Color::White; - } - - update(); -} - -void WordGame::set_word_length(size_t length) -{ - m_num_letters = length; - reset(); -} - -void WordGame::set_max_guesses(size_t max_guesses) -{ - m_max_guesses = max_guesses; - reset(); -} - -void WordGame::set_check_guesses_in_dictionary(bool b) -{ - m_check_guesses = b; - update(); -} - -bool WordGame::is_checking_guesses() const -{ - return m_check_guesses; -} - -Gfx::IntSize WordGame::game_size() const -{ - auto w = 2 * m_outer_margin + m_num_letters * m_letter_width + (m_num_letters - 1) * m_letter_spacing; - auto h = 2 * m_outer_margin + m_max_guesses * m_letter_height + (m_max_guesses - 1) * m_letter_spacing; - return Gfx::IntSize(w, h); -} - -void WordGame::add_guess(AK::StringView guess) -{ - AK::Vector letter_states; - - auto number_correct_for_letter = [this, &guess](StringView letter) -> size_t { - VERIFY(m_current_word.length() == guess.length()); - auto correct_count = 0; - for (size_t i = 0; i < m_current_word.length(); ++i) { - if (m_current_word.substring_view(i, 1) == letter && guess.substring_view(i, 1) == letter) - ++correct_count; - } - return correct_count; - }; - - for (size_t letter_index = 0; letter_index < m_num_letters; ++letter_index) { - auto guess_letter = guess.substring_view(letter_index, 1); - - if (m_current_word[letter_index] == guess_letter[0]) - letter_states.append(Correct); - else if (m_current_word.contains(guess_letter)) { - auto occurrences_in_word = m_current_word.count(guess_letter); - auto occurrences_in_guess_already_counted = guess.substring_view(0, letter_index).count(guess_letter); - auto correct = number_correct_for_letter(guess_letter); - if (occurrences_in_word > correct && occurrences_in_guess_already_counted < occurrences_in_word) - letter_states.append(WrongSpot); - else - letter_states.append(Incorrect); - } else - letter_states.append(Incorrect); - } - - m_guesses.append({ guess, letter_states }); - update(); -} - -void WordGame::show_message(StringView message) const -{ - m_clear_message_timer->restart(); - if (on_message) - on_message(message); -} - -void WordGame::clear_message() const -{ - m_clear_message_timer->stop(); - if (on_message) - on_message({}); -} - -} diff --git a/Userland/Games/MasterWord/WordGame.h b/Userland/Games/MasterWord/WordGame.h deleted file mode 100644 index ebda18b2649..00000000000 --- a/Userland/Games/MasterWord/WordGame.h +++ /dev/null @@ -1,93 +0,0 @@ -/* - * Copyright (c) 2022, Joe Petrus - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include -#include -#include -#include - -namespace MasterWord { - -class WordGame : public GUI::Frame { - C_OBJECT(WordGame); - -public: - virtual ~WordGame() override = default; - - void reset(); - void set_use_system_theme(bool b); - void set_check_guesses_in_dictionary(bool b); - void set_word_length(size_t length); - void set_max_guesses(size_t max_guesses); - Gfx::IntSize game_size() const; - - Optional random_word(size_t length); - size_t shortest_word(); - size_t longest_word(); - bool is_checking_guesses() const; - - void add_guess(AK::StringView guess); - bool is_in_dictionary(AK::StringView guess); - - Function)> on_message; - -private: - WordGame(); - void read_words(); - void show_message(StringView message) const; - void clear_message() const; - - virtual void paint_event(GUI::PaintEvent&) override; - virtual void keydown_event(GUI::KeyEvent&) override; - virtual void resize_event(GUI::ResizeEvent&) override; - void pick_font(); - - Gfx::IntRect letter_rect(size_t guess_number, size_t letter_number) const; - - size_t m_max_guesses { 6 }; - size_t m_num_letters { 5 }; - bool m_check_guesses { false }; - bool m_last_word_invalid { false }; - static constexpr int m_letter_width { 40 }; - static constexpr int m_letter_spacing { 5 }; - static constexpr int m_outer_margin { 20 }; - static constexpr int m_letter_height { 60 }; - - Color m_right_letter_wrong_spot_color { Color::from_rgb(0xb59f3b) }; - Color m_right_letter_right_spot_color { Color::from_rgb(0x538d4e) }; - Color m_border_color { Color::from_rgb(0x3a3a3c) }; - Color m_wrong_letter_color { m_border_color }; - Color m_background_color { Color::from_rgb(0x121213) }; - Color m_text_color { Color::White }; - Color m_word_not_in_dict_color { Color::from_argb(0x40aa0000) }; - - enum LetterState { - Correct, - WrongSpot, - Incorrect - }; - - struct Guess { - AK::ByteString text; - AK::Vector letter_states; - }; - - AK::Vector m_guesses; - AK::ByteString m_current_guess; - AK::ByteString m_current_word; - - HashMap> m_words; - - NonnullRefPtr m_clear_message_timer; -}; - -} diff --git a/Userland/Games/MasterWord/main.cpp b/Userland/Games/MasterWord/main.cpp deleted file mode 100644 index a00c43a3375..00000000000 --- a/Userland/Games/MasterWord/main.cpp +++ /dev/null @@ -1,152 +0,0 @@ -/* - * Copyright (c) 2022, Joe Petrus - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "MainWidget.h" -#include "WordGame.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -ErrorOr serenity_main(Main::Arguments arguments) -{ - TRY(Core::System::pledge("stdio rpath recvfd sendfd unix")); - - auto app = TRY(GUI::Application::create(arguments)); - - Config::pledge_domain("MasterWord"); - - TRY(Desktop::Launcher::add_allowed_handler_with_only_specific_urls("/bin/Help", { URL::create_with_file_scheme("/usr/share/man/man6/MasterWord.md") })); - TRY(Desktop::Launcher::seal_allowlist()); - - TRY(Core::System::pledge("stdio rpath recvfd sendfd")); - - TRY(Core::System::unveil("/tmp/session/%sid/portal/launch", "rw")); - TRY(Core::System::unveil("/res", "r")); - TRY(Core::System::unveil(nullptr, nullptr)); - - auto app_icon = TRY(GUI::Icon::try_create_default_icon("app-masterword"sv)); - - auto window = GUI::Window::construct(); - window->set_icon(app_icon.bitmap_for_size(16)); - window->set_double_buffering_enabled(false); - window->set_title("MasterWord"); - window->set_resizable(true); - window->set_auto_shrink(true); - - auto main_widget = TRY(MasterWord::MainWidget::try_create()); - window->set_main_widget(main_widget); - auto& game = *main_widget->find_descendant_of_type_named("word_game"); - auto& statusbar = *main_widget->find_descendant_of_type_named("statusbar"); - GUI::Application::the()->on_action_enter = [&statusbar](GUI::Action& action) { - statusbar.set_override_text(action.status_tip()); - }; - GUI::Application::the()->on_action_leave = [&statusbar](GUI::Action&) { - statusbar.set_override_text({}); - }; - - auto use_system_theme = Config::read_bool("MasterWord"sv, ""sv, "use_system_theme"sv, false); - game.set_use_system_theme(use_system_theme); - - auto shortest_word = game.shortest_word(); - auto longest_word = game.longest_word(); - - window->set_focused_widget(&game); - - auto game_menu = window->add_menu("&Game"_string); - - game_menu->add_action(GUI::Action::create("&New Game", { Mod_None, Key_F2 }, [&](auto&) { - game.reset(); - })); - - game_menu->add_separator(); - game_menu->add_action(GUI::CommonActions::make_quit_action([](auto&) { - GUI::Application::the()->quit(); - })); - - auto settings_menu = window->add_menu("&Settings"_string); - - settings_menu->add_action(GUI::Action::create("Set &Word Length...", [&](auto&) { - auto word_length = Config::read_i32("MasterWord"sv, ""sv, "word_length"sv, 5); - auto result = GUI::InputBox::show_numeric(window, word_length, shortest_word, longest_word, "Word Length"sv); - if (!result.is_error() && result.value() == GUI::InputBox::ExecResult::OK) { - Config::write_i32("MasterWord"sv, ""sv, "word_length"sv, word_length); - game.set_word_length(word_length); - } - })); - settings_menu->add_action(GUI::Action::create("Set &Number of Guesses...", [&](auto&) { - auto max_guesses = Config::read_i32("MasterWord"sv, ""sv, "max_guesses"sv, 5); - auto result = GUI::InputBox::show_numeric(window, max_guesses, 1, 20, "Number of Guesses"sv); - if (!result.is_error() && result.value() == GUI::InputBox::ExecResult::OK) { - Config::write_i32("MasterWord"sv, ""sv, "max_guesses"sv, max_guesses); - game.set_max_guesses(max_guesses); - } - })); - - auto toggle_check_guesses = GUI::Action::create_checkable("Check &Guesses in Dictionary", [&](auto& action) { - auto checked = action.is_checked(); - game.set_check_guesses_in_dictionary(checked); - Config::write_bool("MasterWord"sv, ""sv, "check_guesses_in_dictionary"sv, checked); - }); - toggle_check_guesses->set_checked(game.is_checking_guesses()); - settings_menu->add_action(toggle_check_guesses); - - auto theme_menu = window->add_menu("&Theme"_string); - auto system_theme_action = GUI::Action::create("&System", [&](auto&) { - game.set_use_system_theme(true); - Config::write_bool("MasterWord"sv, ""sv, "use_system_theme"sv, true); - }); - system_theme_action->set_checkable(true); - system_theme_action->set_checked(use_system_theme); - theme_menu->add_action(system_theme_action); - - auto wordle_theme_action = GUI::Action::create("&Wordle", [&](auto&) { - game.set_use_system_theme(false); - Config::write_bool("MasterWord"sv, ""sv, "use_system_theme"sv, false); - }); - wordle_theme_action->set_checkable(true); - wordle_theme_action->set_checked(!use_system_theme); - theme_menu->add_action(wordle_theme_action); - - GUI::ActionGroup theme_actions; - theme_actions.set_exclusive(true); - theme_actions.set_unchecking_allowed(false); - theme_actions.add_action(system_theme_action); - theme_actions.add_action(wordle_theme_action); - - auto view_menu = window->add_menu("&View"_string); - view_menu->add_action(GUI::CommonActions::make_fullscreen_action([&](auto&) { - window->set_fullscreen(!window->is_fullscreen()); - })); - - auto help_menu = window->add_menu("&Help"_string); - help_menu->add_action(GUI::CommonActions::make_command_palette_action(window)); - help_menu->add_action(GUI::CommonActions::make_help_action([](auto&) { - Desktop::Launcher::open(URL::create_with_file_scheme("/usr/share/man/man6/MasterWord.md"), "/bin/Help"); - })); - help_menu->add_action(GUI::CommonActions::make_about_action("MasterWord"_string, app_icon, window)); - - game.on_message = [&](auto message) { - if (!message.has_value()) - statusbar.set_text({}); - else - statusbar.set_text(String::from_utf8(*message).release_value_but_fixme_should_propagate_errors()); - }; - - window->show(); - - return app->exec(); -} diff --git a/Userland/Games/Minesweeper/CMakeLists.txt b/Userland/Games/Minesweeper/CMakeLists.txt deleted file mode 100644 index 10d5d00251b..00000000000 --- a/Userland/Games/Minesweeper/CMakeLists.txt +++ /dev/null @@ -1,19 +0,0 @@ -serenity_component( - Minesweeper - RECOMMENDED - TARGETS Minesweeper -) - -compile_gml(MainWidget.gml MainWidgetGML.cpp) -compile_gml(CustomGameWidget.gml CustomGameWidgetGML.cpp) - -set(SOURCES - CustomGameDialog.cpp - CustomGameWidgetGML.cpp - Field.cpp - main.cpp - MainWidgetGML.cpp -) - -serenity_app(Minesweeper ICON app-minesweeper) -target_link_libraries(Minesweeper PRIVATE LibCore LibGfx LibGUI LibConfig LibMain LibDesktop LibURL) diff --git a/Userland/Games/Minesweeper/CustomGameDialog.cpp b/Userland/Games/Minesweeper/CustomGameDialog.cpp deleted file mode 100644 index 7363b8412b3..00000000000 --- a/Userland/Games/Minesweeper/CustomGameDialog.cpp +++ /dev/null @@ -1,90 +0,0 @@ -/* - * Copyright (c) 2021, Pedro Pereira - * Copyright (c) 2022, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "CustomGameDialog.h" -#include "Field.h" - -namespace Minesweeper { - -ErrorOr> CustomGameDialog::try_create(GUI::Window* parent) -{ - auto settings_widget = TRY(CustomGameWidget::try_create()); - auto settings_dialog = TRY(adopt_nonnull_ref_or_enomem(new (nothrow) - CustomGameDialog(move(settings_widget), move(parent)))); - return settings_dialog; -} - -GUI::Dialog::ExecResult CustomGameDialog::show(GUI::Window* parent_window, Field& field) -{ - auto dialog_or_error = CustomGameDialog::try_create(parent_window); - if (dialog_or_error.is_error()) { - GUI::MessageBox::show(parent_window, "Couldn't load custom game dialog"sv, "Error while opening custom game dialog"sv, GUI::MessageBox::Type::Error); - return ExecResult::Aborted; - } - - auto dialog = dialog_or_error.release_value(); - - if (parent_window) { - dialog->set_icon(parent_window->icon()); - dialog->center_within(*parent_window); - } - - dialog->m_columns_spinbox->set_value(field.columns()); - dialog->m_rows_spinbox->set_value(field.rows()); - dialog->m_mines_spinbox->set_value(field.mine_count()); - - auto result = dialog->exec(); - - if (result != ExecResult::OK) - return result; - - field.set_field_size(Field::Difficulty::Custom, dialog->m_rows_spinbox->value(), dialog->m_columns_spinbox->value(), dialog->m_mines_spinbox->value()); - - return ExecResult::OK; -} - -void CustomGameDialog::set_max_mines() -{ - // NOTE: this is the maximum number of mines possible in a given minesweeper board - m_mines_spinbox->set_max((m_rows_spinbox->value() * m_columns_spinbox->value()) - 9); -} - -CustomGameDialog::CustomGameDialog(NonnullRefPtr custom_game_widget, GUI::Window* parent_window) - : Dialog(parent_window) -{ - resize(300, 82); - set_resizable(false); - set_title("Custom Game"); - - set_main_widget(custom_game_widget); - - m_columns_spinbox = *custom_game_widget->find_descendant_of_type_named("columns_spinbox"); - m_rows_spinbox = *custom_game_widget->find_descendant_of_type_named("rows_spinbox"); - m_mines_spinbox = *custom_game_widget->find_descendant_of_type_named("mines_spinbox"); - m_ok_button = *custom_game_widget->find_descendant_of_type_named("ok_button"); - m_cancel_button = *custom_game_widget->find_descendant_of_type_named("cancel_button"); - - m_columns_spinbox->on_change = [this](auto) { - set_max_mines(); - }; - - m_rows_spinbox->on_change = [this](auto) { - set_max_mines(); - }; - - m_ok_button->on_click = [this](auto) { - done(ExecResult::OK); - }; - - m_cancel_button->on_click = [this](auto) { - done(ExecResult::Cancel); - }; - - set_max_mines(); -} - -} diff --git a/Userland/Games/Minesweeper/CustomGameDialog.h b/Userland/Games/Minesweeper/CustomGameDialog.h deleted file mode 100644 index f88183f658d..00000000000 --- a/Userland/Games/Minesweeper/CustomGameDialog.h +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright (c) 2021, Pedro Pereira - * Copyright (c) 2022, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include "CustomGameWidget.h" -#include -#include -#include -#include - -class Field; - -namespace Minesweeper { - -class CustomGameDialog : public GUI::Dialog { - C_OBJECT_ABSTRACT(CustomGameDialog); - -public: - static ExecResult show(GUI::Window* parent_window, Field& field); - static ErrorOr> try_create(GUI::Window* parent); - -private: - CustomGameDialog(NonnullRefPtr custom_game_widget, GUI::Window* parent_window); - virtual ~CustomGameDialog() override = default; - - void set_max_mines(); - - RefPtr m_ok_button; - RefPtr m_cancel_button; - RefPtr m_columns_spinbox; - RefPtr m_rows_spinbox; - RefPtr m_mines_spinbox; -}; - -} diff --git a/Userland/Games/Minesweeper/CustomGameWidget.gml b/Userland/Games/Minesweeper/CustomGameWidget.gml deleted file mode 100644 index 857b81c1528..00000000000 --- a/Userland/Games/Minesweeper/CustomGameWidget.gml +++ /dev/null @@ -1,72 +0,0 @@ -@Minesweeper::CustomGameWidget { - fill_with_background_color: true - layout: @GUI::VerticalBoxLayout { - margins: [4] - spacing: 6 - } - - @GUI::GroupBox { - title: "Field" - layout: @GUI::HorizontalBoxLayout { - margins: [6] - } - - @GUI::Label { - text: "Columns: " - autosize: true - } - - @GUI::SpinBox { - name: "columns_spinbox" - min: 9 - max: 50 - fixed_width: 40 - } - - @GUI::Layout::Spacer {} - - @GUI::Label { - text: "Rows: " - autosize: true - } - - @GUI::SpinBox { - name: "rows_spinbox" - min: 9 - max: 50 - fixed_width: 40 - } - - @GUI::Layout::Spacer {} - - @GUI::Label { - text: "Mines: " - autosize: true - } - - @GUI::SpinBox { - name: "mines_spinbox" - min: 1 - max: 2500 - fixed_width: 50 - } - } - - @GUI::Widget { - layout: @GUI::HorizontalBoxLayout { - spacing: 10 - } - - @GUI::Layout::Spacer {} - - @GUI::DialogButton { - name: "ok_button" - text: "OK" - } - - @GUI::DialogButton { - name: "cancel_button" - text: "Cancel" - } - } -} diff --git a/Userland/Games/Minesweeper/CustomGameWidget.h b/Userland/Games/Minesweeper/CustomGameWidget.h deleted file mode 100644 index 09850caa3e4..00000000000 --- a/Userland/Games/Minesweeper/CustomGameWidget.h +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright (c) 2023, the SerenityOS developers - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include - -namespace Minesweeper { - -class CustomGameWidget : public GUI::Widget { - C_OBJECT_ABSTRACT(CustomGameWidget) -public: - static ErrorOr> try_create(); - virtual ~CustomGameWidget() override = default; - -private: - CustomGameWidget() = default; -}; - -} diff --git a/Userland/Games/Minesweeper/Field.cpp b/Userland/Games/Minesweeper/Field.cpp deleted file mode 100644 index 930b2e38f51..00000000000 --- a/Userland/Games/Minesweeper/Field.cpp +++ /dev/null @@ -1,583 +0,0 @@ -/* - * Copyright (c) 2018-2021, Andreas Kling - * Copyright (c) 2022, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "Field.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -class SquareButton final : public GUI::Button { - C_OBJECT(SquareButton); - -public: - Function on_secondary_click; - Function on_middle_click; - - virtual void mousedown_event(GUI::MouseEvent& event) override - { - if (event.button() == GUI::MouseButton::Secondary) { - if (on_secondary_click) - on_secondary_click(); - } - if (event.button() == GUI::MouseButton::Middle) { - if (on_middle_click) - on_middle_click(); - } - GUI::Button::mousedown_event(event); - } - -private: - SquareButton() - { - set_focus_policy(GUI::FocusPolicy::TabFocus); - } -}; - -class SquareImage final : public GUI::ImageWidget { - C_OBJECT(SquareImage); - -public: - Function on_chord_click; - - virtual void mousedown_event(GUI::MouseEvent& event) override - { - if (event.button() == GUI::MouseButton::Secondary || event.button() == GUI::MouseButton::Primary) { - if (event.buttons() == (GUI::MouseButton::Secondary | GUI::MouseButton::Primary) || m_square.field->is_single_chording()) { - m_chord = true; - m_square.field->set_chord_preview(m_square, true); - } - } - if (event.button() == GUI::MouseButton::Middle) { - m_square.field->for_each_square([](auto& square) { - if (square.is_considering) { - square.is_considering = false; - square.button->set_icon(nullptr); - } - }); - } - GUI::ImageWidget::mousedown_event(event); - } - - virtual void mousemove_event(GUI::MouseEvent& event) override - { - if (m_chord) { - if (rect().contains(event.position())) { - m_square.field->set_chord_preview(m_square, true); - } else { - m_square.field->set_chord_preview(m_square, false); - } - } - GUI::ImageWidget::mousemove_event(event); - } - - virtual void mouseup_event(GUI::MouseEvent& event) override - { - if (m_chord) { - if (event.button() == GUI::MouseButton::Primary || event.button() == GUI::MouseButton::Secondary) { - if (rect().contains(event.position())) { - if (on_chord_click) - on_chord_click(); - } - m_chord = false; - } - } - m_square.field->set_chord_preview(m_square, m_chord); - GUI::ImageWidget::mouseup_event(event); - } - -private: - explicit SquareImage(Square& square) - : m_square(square) - { - } - - Square& m_square; - bool m_chord { false }; -}; - -ErrorOr> Field::create(GUI::Label& flag_label, GUI::Label& time_label, GUI::Button& face_button) -{ - auto field = TRY(adopt_nonnull_ref_or_enomem(new (nothrow) Field(flag_label, time_label, face_button))); - field->m_mine_bitmap = TRY(Gfx::Bitmap::load_from_file("/res/graphics/minesweeper/mine.png"sv)); - field->m_flag_bitmap = TRY(Gfx::Bitmap::load_from_file("/res/graphics/minesweeper/flag.png"sv)); - field->m_badflag_bitmap = TRY(Gfx::Bitmap::load_from_file("/res/graphics/minesweeper/badflag.png"sv)); - field->m_consider_bitmap = TRY(Gfx::Bitmap::load_from_file("/res/graphics/minesweeper/consider.png"sv)); - field->m_default_face_bitmap = TRY(Gfx::Bitmap::load_from_file("/res/graphics/minesweeper/face-default.png"sv)); - field->m_good_face_bitmap = TRY(Gfx::Bitmap::load_from_file("/res/graphics/minesweeper/face-good.png"sv)); - field->m_bad_face_bitmap = TRY(Gfx::Bitmap::load_from_file("/res/graphics/minesweeper/face-bad.png"sv)); - for (int i = 0; i < 8; ++i) - field->m_number_bitmap[i] = TRY(Gfx::Bitmap::load_from_file(ByteString::formatted("/res/graphics/minesweeper/{}.png", i + 1))); - field->initialize(); - return field; -} - -Field::Field(GUI::Label& flag_label, GUI::Label& time_label, GUI::Button& face_button) - : m_mine_palette(GUI::Application::the()->palette().impl().clone()) - , m_face_button(face_button) - , m_flag_label(flag_label) - , m_time_label(time_label) -{ -} - -void Field::initialize() -{ - m_timer = Core::Timer::create_repeating( - 1000, [this] { - ++m_time_elapsed; - m_time_label.set_text(human_readable_digital_time(m_time_elapsed)); - }, - this); - - // Square with mine will be filled with background color later, i.e. red - m_mine_palette.set_color(Gfx::ColorRole::Base, Color::from_rgb(0xff4040)); - - set_fill_with_background_color(true); - - m_face_button.on_click = [this](auto) { reset(); }; - set_face(Face::Default); - - { - bool single_chording = Config::read_bool("Minesweeper"sv, "Game"sv, "SingleChording"sv, false); - int mine_count = Config::read_i32("Minesweeper"sv, "Game"sv, "MineCount"sv, 10); - int rows = Config::read_i32("Minesweeper"sv, "Game"sv, "Rows"sv, 9); - int columns = Config::read_i32("Minesweeper"sv, "Game"sv, "Columns"sv, 9); - auto difficulty_string = Config::read_string("Minesweeper"sv, "Game"sv, "Difficulty"sv, "beginner"sv); - auto difficulty = difficulty_from_string(difficulty_string); - - // Do a quick sanity check to make sure the user hasn't tried anything crazy - if (!difficulty.has_value() || mine_count > rows * columns || rows <= 0 || columns <= 0 || mine_count <= 0) - set_field_difficulty(Difficulty::Beginner); - else if (difficulty.value() == Difficulty::Custom) - set_field_size(Difficulty::Custom, rows, columns, mine_count); - else - set_field_difficulty(difficulty.value()); - - set_single_chording(single_chording); - } -} - -void Field::set_face(Face face) -{ - switch (face) { - case Face::Default: - m_face_button.set_icon(*m_default_face_bitmap); - break; - case Face::Good: - m_face_button.set_icon(*m_good_face_bitmap); - break; - case Face::Bad: - m_face_button.set_icon(*m_bad_face_bitmap); - break; - } -} - -template -void Square::for_each_neighbor(Callback callback) -{ - size_t r = row; - size_t c = column; - if (r > 0) // Up - callback(field->square(r - 1, c)); - if (c > 0) // Left - callback(field->square(r, c - 1)); - if (r < (field->m_rows - 1)) // Down - callback(field->square(r + 1, c)); - if (c < (field->m_columns - 1)) // Right - callback(field->square(r, c + 1)); - if (r > 0 && c > 0) // UpLeft - callback(field->square(r - 1, c - 1)); - if (r > 0 && c < (field->m_columns - 1)) // UpRight - callback(field->square(r - 1, c + 1)); - if (r < (field->m_rows - 1) && c > 0) // DownLeft - callback(field->square(r + 1, c - 1)); - if (r < (field->m_rows - 1) && c < (field->m_columns - 1)) // DownRight - callback(field->square(r + 1, c + 1)); -} - -void Field::reset() -{ - m_first_click = true; - set_updates_enabled(false); - m_time_elapsed = 0; - m_time_label.set_text("00:00"_string); - m_flags_left = m_mine_count; - m_flag_label.set_text(String::number(m_flags_left).release_value_but_fixme_should_propagate_errors()); - m_timer->stop(); - set_greedy_for_hits(false); - set_face(Face::Default); - - m_squares.resize(max(m_squares.size(), rows() * columns())); - - for (int i = rows() * columns(); i < static_cast(m_squares.size()); ++i) { - auto& square = m_squares[i]; - square->button->set_visible(false); - square->image->set_visible(false); - } - - size_t i = 0; - for (size_t r = 0; r < rows(); ++r) { - for (size_t c = 0; c < columns(); ++c) { - if (!m_squares[i]) - m_squares[i] = make(); - Gfx::IntRect rect = { frame_thickness() + static_cast(c) * square_size(), frame_thickness() + static_cast(r) * square_size(), square_size(), square_size() }; - auto& square = this->square(r, c); - square.field = this; - square.row = r; - square.column = c; - square.has_mine = false; - square.has_flag = false; - square.is_considering = false; - square.is_swept = false; - if (!square.image) { - square.image = add(square); - square.image->set_palette(m_mine_palette); - square.image->set_background_role(Gfx::ColorRole::Base); - } - square.image->set_fill_with_background_color(false); - square.image->set_relative_rect(rect); - square.image->set_visible(false); - square.image->set_bitmap(nullptr); - if (!square.button) { - square.button = add(); - square.button->on_click = [this, &square](auto) { - on_square_clicked(square); - }; - square.button->on_secondary_click = [this, &square] { - on_square_secondary_clicked(square); - }; - square.button->on_middle_click = [this, &square] { - on_square_middle_clicked(square); - }; - square.image->on_chord_click = [this, &square] { - on_square_chorded(square); - }; - } - square.button->set_checked(false); - square.button->set_icon(nullptr); - square.button->set_relative_rect(rect); - square.button->set_visible(true); - - ++i; - } - } - - set_updates_enabled(true); -} - -void Field::generate_field(size_t start_row, size_t start_column) -{ - VERIFY(m_squares.size() >= rows() * columns()); - size_t board_size = rows() * columns(); - - // FIXME: Handle possible errors - HashTable free_squares; - - size_t start_index = start_row * columns() + start_column; - free_squares.set(start_index); - - square(start_row, start_column).for_each_neighbor([&](auto const& neighbor) { - size_t neighbor_index = neighbor.row * columns() + neighbor.column; - free_squares.set(neighbor_index); - }); - - VERIFY(m_mine_count <= board_size - free_squares.size()); - - Vector possible_mine_positions; - possible_mine_positions.ensure_capacity(board_size - free_squares.size()); - - for (size_t i = 0; i < board_size; ++i) { - m_squares[i]->has_mine = false; - m_squares[i]->has_flag = false; - m_squares[i]->is_considering = false; - m_squares[i]->is_swept = false; - m_squares[i]->number = 0; - if (!free_squares.contains(i)) - possible_mine_positions.unchecked_append(i); - } - - AK::shuffle(possible_mine_positions); - - for (size_t i = 0; i < m_mine_count; i++) { - size_t mine_location = possible_mine_positions[i]; - m_squares[mine_location]->has_mine = true; - } - - for (size_t r = 0; r < rows(); ++r) { - for (size_t c = 0; c < columns(); ++c) { - auto& square = this->square(r, c); - size_t number = 0; - square.for_each_neighbor([&number](auto& neighbor) { - number += neighbor.has_mine; - }); - square.number = number; - if (square.has_mine) { - square.image->set_bitmap(m_mine_bitmap); - } else if (square.number) { - square.image->set_bitmap(m_number_bitmap[square.number - 1]); - } - } - } - - m_unswept_empties = rows() * columns() - m_mine_count; -} - -void Field::flood_fill(Square& square) -{ - Queue queue; - queue.enqueue(&square); - - while (!queue.is_empty()) { - Square* s = queue.dequeue(); - s->for_each_neighbor([this, &queue](Square& neighbor) { - if (!neighbor.is_swept && !neighbor.has_mine && neighbor.number == 0) { - on_square_clicked_impl(neighbor, false); - queue.enqueue(&neighbor); - } - if (!neighbor.has_mine && neighbor.number) - on_square_clicked_impl(neighbor, false); - }); - } -} - -void Field::paint_event(GUI::PaintEvent& event) -{ - GUI::Frame::paint_event(event); - GUI::Painter painter(*this); - painter.add_clip_rect(event.rect()); - - auto inner_rect = frame_inner_rect(); - painter.add_clip_rect(inner_rect); - - for (int y = inner_rect.top() - 1; y < inner_rect.bottom(); y += square_size()) { - Gfx::IntPoint a { inner_rect.left(), y }; - Gfx::IntPoint b { inner_rect.right() - 1, y }; - painter.draw_line(a, b, palette().threed_shadow1()); - } - for (int x = frame_inner_rect().left() - 1; x < frame_inner_rect().right(); x += square_size()) { - Gfx::IntPoint a { x, inner_rect.top() }; - Gfx::IntPoint b { x, inner_rect.bottom() - 1 }; - painter.draw_line(a, b, palette().threed_shadow1()); - } -} - -void Field::on_square_clicked_impl(Square& square, bool should_flood_fill) -{ - if (m_first_click) { - reset(); - generate_field(square.row, square.column); - } - m_first_click = false; - - if (square.is_swept) - return; - if (square.has_flag) - return; - if (square.is_considering) - return; - if (!m_timer->is_active()) { - m_timer->on_timeout(); - m_timer->start(); - } - update(); - square.is_swept = true; - square.button->set_visible(false); - square.image->set_visible(true); - if (square.has_mine) { - square.image->set_fill_with_background_color(true); - game_over(); - return; - } - - --m_unswept_empties; - if (should_flood_fill && square.number == 0) - flood_fill(square); - - if (!m_unswept_empties) - win(); -} - -void Field::on_square_clicked(Square& square) -{ - on_square_clicked_impl(square, true); -} - -void Field::on_square_chorded(Square& square) -{ - if (!square.is_swept) - return; - if (!square.number) - return; - size_t adjacent_flags = 0; - square.for_each_neighbor([&](auto& neighbor) { - if (neighbor.has_flag) - ++adjacent_flags; - }); - if (square.number != adjacent_flags) - return; - square.for_each_neighbor([&](auto& neighbor) { - if (neighbor.has_flag) - return; - on_square_clicked(neighbor); - }); -} - -void Field::on_square_secondary_clicked(Square& square) -{ - if (square.is_swept) - return; - if (!square.has_flag && !m_flags_left) - return; - - set_flag(square, !square.has_flag); -} - -void Field::set_flag(Square& square, bool flag) -{ - VERIFY(!square.is_swept); - if (square.has_flag == flag) - return; - square.is_considering = false; - - if (!flag) { - ++m_flags_left; - } else { - - VERIFY(m_flags_left); - --m_flags_left; - } - square.has_flag = flag; - - m_flag_label.set_text(String::number(m_flags_left).release_value_but_fixme_should_propagate_errors()); - square.button->set_icon(square.has_flag ? m_flag_bitmap : nullptr); - square.button->update(); -} - -void Field::on_square_middle_clicked(Square& square) -{ - if (square.is_swept) - return; - if (square.has_flag) { - ++m_flags_left; - square.has_flag = false; - m_flag_label.set_text(String::number(m_flags_left).release_value_but_fixme_should_propagate_errors()); - } - square.is_considering = !square.is_considering; - square.button->set_icon(square.is_considering ? m_consider_bitmap : nullptr); - square.button->update(); -} - -void Field::win() -{ - m_timer->stop(); - set_greedy_for_hits(true); - set_face(Face::Good); - for_each_square([&](auto& square) { - if (!square.has_flag && square.has_mine) - set_flag(square, true); - }); - reveal_mines(); -} - -void Field::game_over() -{ - m_timer->stop(); - set_greedy_for_hits(true); - set_face(Face::Bad); - reveal_mines(); -} - -void Field::reveal_mines() -{ - for (size_t r = 0; r < rows(); ++r) { - for (size_t c = 0; c < columns(); ++c) { - auto& square = this->square(r, c); - if (square.has_mine && !square.has_flag) { - square.button->set_visible(false); - square.image->set_visible(true); - } - if (!square.has_mine && square.has_flag) { - square.button->set_icon(*m_badflag_bitmap); - square.button->set_visible(true); - square.image->set_visible(false); - } - } - } - update(); -} - -void Field::set_chord_preview(Square& square, bool chord_preview) -{ - if (m_chord_preview == chord_preview) - return; - m_chord_preview = chord_preview; - square.for_each_neighbor([&](auto& neighbor) { - neighbor.button->set_checked(false); - if (!neighbor.has_flag && !neighbor.is_considering) - neighbor.button->set_checked(chord_preview); - }); -} - -void Field::set_field_difficulty(Difficulty difficulty) -{ - switch (difficulty) { - case Difficulty::Beginner: - set_field_size(difficulty, 9, 9, 10); - break; - case Difficulty::Intermediate: - set_field_size(difficulty, 16, 16, 40); - break; - case Difficulty::Expert: - set_field_size(difficulty, 16, 30, 99); - break; - case Difficulty::Madwoman: - set_field_size(difficulty, 32, 60, 350); - break; - default: - VERIFY_NOT_REACHED(); - } -} - -void Field::set_field_size(Difficulty difficulty, size_t rows, size_t columns, size_t mine_count) -{ - if (m_rows == rows && m_columns == columns && m_mine_count == mine_count) - return; - { - Config::write_i32("Minesweeper"sv, "Game"sv, "MineCount"sv, mine_count); - Config::write_i32("Minesweeper"sv, "Game"sv, "Rows"sv, rows); - Config::write_i32("Minesweeper"sv, "Game"sv, "Columns"sv, columns); - Config::write_string("Minesweeper"sv, "Game"sv, "Difficulty"sv, difficulty_to_string(difficulty)); - } - m_difficulty = difficulty; - m_rows = rows; - m_columns = columns; - m_mine_count = mine_count; - set_fixed_size(frame_thickness() * 2 + m_columns * square_size(), frame_thickness() * 2 + m_rows * square_size()); - reset(); -} - -void Field::set_single_chording(bool enabled) -{ - m_single_chording = enabled; - Config::write_bool("Minesweeper"sv, "Game"sv, "SingleChording"sv, m_single_chording); -} - -template -void Field::for_each_square(Callback callback) -{ - for (size_t i = 0; i < rows() * columns(); ++i) - callback(*m_squares[i]); -} diff --git a/Userland/Games/Minesweeper/Field.h b/Userland/Games/Minesweeper/Field.h deleted file mode 100644 index 46fc13cf060..00000000000 --- a/Userland/Games/Minesweeper/Field.h +++ /dev/null @@ -1,169 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * Copyright (c) 2022, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include - -class Field; -class SquareButton; -class SquareImage; - -class Square { - AK_MAKE_NONCOPYABLE(Square); - -public: - Square() = default; - ~Square() = default; - - Field* field { nullptr }; - bool is_swept { false }; - bool has_mine { false }; - bool has_flag { false }; - bool is_considering { false }; - size_t row { 0 }; - size_t column { 0 }; - size_t number { 0 }; - RefPtr button; - RefPtr image; - - template - void for_each_neighbor(Callback); -}; - -class Field final : public GUI::Frame { - C_OBJECT(Field) - friend class Square; - friend class SquareImage; - -public: - static ErrorOr> create(GUI::Label& flag_label, GUI::Label& time_label, GUI::Button& face_button); - virtual ~Field() override = default; - - enum class Difficulty { - Beginner, - Intermediate, - Expert, - Madwoman, - Custom - }; - - StringView difficulty_to_string(Difficulty difficulty) const - { - switch (difficulty) { - case Difficulty::Beginner: - return "beginner"sv; - case Difficulty::Intermediate: - return "intermediate"sv; - case Difficulty::Expert: - return "expert"sv; - case Difficulty::Madwoman: - return "madwoman"sv; - case Difficulty::Custom: - return "custom"sv; - default: - VERIFY_NOT_REACHED(); - } - } - - Optional difficulty_from_string(StringView difficulty_string) const - { - if (difficulty_string.matches("beginner"sv)) - return Difficulty::Beginner; - - if (difficulty_string.equals_ignoring_ascii_case("intermediate"sv)) - return Difficulty::Intermediate; - - if (difficulty_string.equals_ignoring_ascii_case("expert"sv)) - return Difficulty::Expert; - - if (difficulty_string.equals_ignoring_ascii_case("madwoman"sv)) - return Difficulty::Madwoman; - - if (difficulty_string.equals_ignoring_ascii_case("custom"sv)) - return Difficulty::Custom; - - return {}; - } - - Difficulty difficulty() const { return m_difficulty; } - size_t rows() const { return m_rows; } - size_t columns() const { return m_columns; } - size_t mine_count() const { return m_mine_count; } - int square_size() const { return 15; } - bool is_single_chording() const { return m_single_chording; } - - void set_field_difficulty(Difficulty difficulty); - void set_field_size(Difficulty difficulty, size_t rows, size_t columns, size_t mine_count); - - void set_single_chording(bool new_val); - - void reset(); - void generate_field(size_t start_row, size_t start_column); - -private: - Field(GUI::Label& flag_label, GUI::Label& time_label, GUI::Button& face_button); - - void initialize(); - - virtual void paint_event(GUI::PaintEvent&) override; - - void on_square_clicked(Square&); - void on_square_secondary_clicked(Square&); - void on_square_middle_clicked(Square&); - void on_square_chorded(Square&); - void game_over(); - void win(); - void reveal_mines(); - void set_chord_preview(Square&, bool); - void set_flag(Square&, bool); - - Square& square(size_t row, size_t column) { return *m_squares[row * columns() + column]; } - Square const& square(size_t row, size_t column) const { return *m_squares[row * columns() + column]; } - - void flood_fill(Square&); - void on_square_clicked_impl(Square&, bool); - - template - void for_each_square(Callback); - - enum class Face { - Default, - Good, - Bad - }; - void set_face(Face); - - Difficulty m_difficulty { Difficulty::Beginner }; - size_t m_rows { 0 }; - size_t m_columns { 0 }; - size_t m_mine_count { 0 }; - size_t m_unswept_empties { 0 }; - Vector> m_squares; - RefPtr m_mine_bitmap; - RefPtr m_flag_bitmap; - RefPtr m_badflag_bitmap; - RefPtr m_consider_bitmap; - RefPtr m_default_face_bitmap; - RefPtr m_good_face_bitmap; - RefPtr m_bad_face_bitmap; - RefPtr m_number_bitmap[8]; - Gfx::Palette m_mine_palette; - GUI::Button& m_face_button; - GUI::Label& m_flag_label; - GUI::Label& m_time_label; - RefPtr m_timer; - size_t m_time_elapsed { 0 }; - size_t m_flags_left { 0 }; - Face m_face { Face::Default }; - bool m_chord_preview { false }; - bool m_first_click { true }; - bool m_single_chording { true }; -}; diff --git a/Userland/Games/Minesweeper/MainWidget.gml b/Userland/Games/Minesweeper/MainWidget.gml deleted file mode 100644 index 41639548923..00000000000 --- a/Userland/Games/Minesweeper/MainWidget.gml +++ /dev/null @@ -1,52 +0,0 @@ -@Minesweeper::MainWidget { - fill_with_background_color: true - layout: @GUI::VerticalBoxLayout { - spacing: 0 - } - - @GUI::HorizontalSeparator { - name: "separator" - fixed_height: 2 - } - - @GUI::Widget { - name: "container" - fixed_height: 36 - layout: @GUI::HorizontalBoxLayout {} - - @GUI::Layout::Spacer {} - - @GUI::ImageWidget { - name: "flag_image" - bitmap: "/res/graphics/minesweeper/flag.png" - } - - @GUI::Label { - name: "flag_label" - autosize: true - } - - @GUI::Layout::Spacer {} - - @GUI::Button { - name: "face_button" - fixed_size: [36, 36] - focus_policy: "TabFocus" - button_style: "Coolbar" - } - - @GUI::Layout::Spacer {} - - @GUI::ImageWidget { - name: "time_image" - bitmap: "/res/graphics/minesweeper/timer.png" - } - - @GUI::Label { - name: "time_label" - autosize: true - } - - @GUI::Layout::Spacer {} - } -} diff --git a/Userland/Games/Minesweeper/MainWidget.h b/Userland/Games/Minesweeper/MainWidget.h deleted file mode 100644 index 26216e18688..00000000000 --- a/Userland/Games/Minesweeper/MainWidget.h +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright (c) 2023, the SerenityOS developers - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include - -namespace Minesweeper { - -class MainWidget : public GUI::Widget { - C_OBJECT_ABSTRACT(MainWidget) -public: - static ErrorOr> try_create(); - virtual ~MainWidget() override = default; - -private: - MainWidget() = default; -}; - -} diff --git a/Userland/Games/Minesweeper/main.cpp b/Userland/Games/Minesweeper/main.cpp deleted file mode 100644 index a3620c2105b..00000000000 --- a/Userland/Games/Minesweeper/main.cpp +++ /dev/null @@ -1,140 +0,0 @@ -/* - * Copyright (c) 2018-2021, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "CustomGameDialog.h" -#include "Field.h" -#include "MainWidget.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -ErrorOr serenity_main(Main::Arguments arguments) -{ - TRY(Core::System::pledge("stdio rpath recvfd sendfd unix")); - - auto app = TRY(GUI::Application::create(arguments)); - - Config::pledge_domain("Minesweeper"); - - TRY(Desktop::Launcher::add_allowed_handler_with_only_specific_urls("/bin/Help", { URL::create_with_file_scheme("/usr/share/man/man6/Minesweeper.md") })); - TRY(Desktop::Launcher::seal_allowlist()); - - TRY(Core::System::pledge("stdio rpath recvfd sendfd")); - - TRY(Core::System::unveil("/tmp/session/%sid/portal/launch", "rw")); - TRY(Core::System::unveil("/res", "r")); - TRY(Core::System::unveil(nullptr, nullptr)); - - auto app_icon = TRY(GUI::Icon::try_create_default_icon("app-minesweeper"sv)); - - auto window = GUI::Window::construct(); - window->set_resizable(false); - window->set_title("Minesweeper"); - window->set_auto_shrink(true); - - auto main_widget = TRY(Minesweeper::MainWidget::try_create()); - window->set_main_widget(main_widget); - - auto& flag_label = *main_widget->find_descendant_of_type_named("flag_label"); - auto& time_label = *main_widget->find_descendant_of_type_named("time_label"); - auto& face_button = *main_widget->find_descendant_of_type_named("face_button"); - auto field = TRY(Field::create(flag_label, time_label, face_button)); - TRY(main_widget->try_add_child(field)); - - auto game_menu = window->add_menu("&Game"_string); - - game_menu->add_action(GUI::Action::create("&New Game", { Mod_None, Key_F2 }, TRY(Gfx::Bitmap::load_from_file("/res/icons/16x16/reload.png"sv)), [&](auto&) { - field->reset(); - })); - - game_menu->add_separator(); - - auto chord_toggler_action = GUI::Action::create_checkable("&Single-click Chording", [&](auto& action) { - field->set_single_chording(action.is_checked()); - }); - chord_toggler_action->set_checked(field->is_single_chording()); - - game_menu->add_action(*chord_toggler_action); - game_menu->add_separator(); - - // Put Fullscreen in Game rather than View - // When in beginner mode it can only show 3 menus. Adding View makes 4 - game_menu->add_action(GUI::CommonActions::make_fullscreen_action([&](auto&) { - window->set_fullscreen(!window->is_fullscreen()); - })); - game_menu->add_separator(); - - game_menu->add_action(GUI::CommonActions::make_quit_action([](auto&) { - GUI::Application::the()->quit(); - })); - - auto difficulty_menu = window->add_menu("&Difficulty"_string); - GUI::ActionGroup difficulty_actions; - difficulty_actions.set_exclusive(true); - - auto action = GUI::Action::create_checkable("&Beginner", { Mod_Ctrl, Key_B }, [&](auto&) { - field->set_field_difficulty(Field::Difficulty::Beginner); - }); - action->set_checked(field->difficulty() == Field::Difficulty::Beginner); - difficulty_menu->add_action(action); - difficulty_actions.add_action(action); - - action = GUI::Action::create_checkable("&Intermediate", { Mod_Ctrl, Key_I }, [&](auto&) { - field->set_field_difficulty(Field::Difficulty::Intermediate); - }); - action->set_checked(field->difficulty() == Field::Difficulty::Intermediate); - difficulty_menu->add_action(action); - difficulty_actions.add_action(action); - - action = GUI::Action::create_checkable("&Expert", { Mod_Ctrl, Key_E }, [&](auto&) { - field->set_field_difficulty(Field::Difficulty::Expert); - }); - action->set_checked(field->difficulty() == Field::Difficulty::Expert); - difficulty_menu->add_action(action); - difficulty_actions.add_action(action); - - action = GUI::Action::create_checkable("&Madwoman", { Mod_Ctrl, Key_M }, [&](auto&) { - field->set_field_difficulty(Field::Difficulty::Madwoman); - }); - action->set_checked(field->difficulty() == Field::Difficulty::Madwoman); - difficulty_menu->add_action(action); - difficulty_actions.add_action(action); - - difficulty_menu->add_separator(); - action = GUI::Action::create_checkable("&Custom Game...", { Mod_Ctrl, Key_C }, [&](auto&) { - Minesweeper::CustomGameDialog::show(window, field); - }); - action->set_checked(field->difficulty() == Field::Difficulty::Custom); - difficulty_menu->add_action(action); - difficulty_actions.add_action(action); - - auto help_menu = window->add_menu("&Help"_string); - help_menu->add_action(GUI::CommonActions::make_command_palette_action(window)); - help_menu->add_action(GUI::CommonActions::make_help_action([](auto&) { - Desktop::Launcher::open(URL::create_with_file_scheme("/usr/share/man/man6/Minesweeper.md"), "/bin/Help"); - })); - help_menu->add_action(GUI::CommonActions::make_about_action("Minesweeper"_string, app_icon, window)); - - window->show(); - - window->set_icon(app_icon.bitmap_for_size(16)); - - return app->exec(); -} diff --git a/Userland/Games/Snake/CMakeLists.txt b/Userland/Games/Snake/CMakeLists.txt deleted file mode 100644 index 8fab6c6efb5..00000000000 --- a/Userland/Games/Snake/CMakeLists.txt +++ /dev/null @@ -1,19 +0,0 @@ -serenity_component( - Snake - RECOMMENDED - TARGETS Snake -) - -compile_gml(Snake.gml SnakeGML.cpp) - -set(SOURCES - Game.cpp - main.cpp - Skins/ClassicSkin.cpp - Skins/ImageSkin.cpp - Skins/SnakeSkin.cpp - SnakeGML.cpp -) - -serenity_app(Snake ICON app-snake) -target_link_libraries(Snake PRIVATE LibCore LibFileSystem LibGfx LibGUI LibConfig LibMain LibDesktop LibURL) diff --git a/Userland/Games/Snake/Game.cpp b/Userland/Games/Snake/Game.cpp deleted file mode 100644 index bb1d5cd2191..00000000000 --- a/Userland/Games/Snake/Game.cpp +++ /dev/null @@ -1,364 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * Copyright (c) 2021, Mustafa Quraish - * Copyright (c) 2022, the SerenityOS developers. - * Copyright (c) 2023, Sam Atkins - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "Game.h" -#include -#include -#include -#include -#include -#include -#include - -namespace Snake { - -ErrorOr> Game::try_create() -{ - static constexpr auto food_bitmaps_files = Array { - "/res/emoji/U+1F41F.png"sv, - "/res/emoji/U+1F95A.png"sv, - "/res/emoji/U+1F99C.png"sv, - "/res/emoji/U+1F986.png"sv, - "/res/emoji/U+1FAB2.png"sv, - "/res/emoji/U+1F426.png"sv, - "/res/emoji/U+1F424.png"sv, - "/res/emoji/U+1F40D.png"sv, - "/res/emoji/U+1F989.png"sv, - "/res/emoji/U+1F54A.png"sv, - "/res/emoji/U+1F408.png"sv, - "/res/emoji/U+1F420.png"sv, - "/res/emoji/U+1F415.png"sv, - "/res/emoji/U+1F429.png"sv, - "/res/emoji/U+1F98C.png"sv, - "/res/emoji/U+1F416.png"sv, - "/res/emoji/U+1F401.png"sv, - "/res/emoji/U+1F400.png"sv, - "/res/emoji/U+1F407.png"sv, - "/res/emoji/U+1F43F.png"sv, - "/res/emoji/U+1F9A5.png"sv, - "/res/emoji/U+1F423.png"sv, - "/res/emoji/U+1F425.png"sv, - "/res/emoji/U+1F98E.png"sv, - "/res/emoji/U+1F997.png"sv, - "/res/emoji/U+1FAB3.png"sv, - "/res/emoji/U+1F413.png"sv, - "/res/emoji/U+1FAB0.png"sv, - "/res/emoji/U+1FAB1.png"sv, - }; - - Vector> food_bitmaps; - TRY(food_bitmaps.try_ensure_capacity(food_bitmaps_files.size())); - - for (auto file : food_bitmaps_files) { - auto bitmap = Gfx::Bitmap::load_from_file(file); - if (bitmap.is_error()) { - dbgln("\033[31;1mCould not load bitmap file\033[0m '{}': {}", file, bitmap.error()); - return bitmap.release_error(); - } - - food_bitmaps.unchecked_append(bitmap.release_value()); - } - - auto color = Color::from_argb(Config::read_u32("Snake"sv, "Snake"sv, "BaseColor"sv, Color(Color::Green).value())); - auto skin_name = TRY(String::from_byte_string(Config::read_string("Snake"sv, "Snake"sv, "SnakeSkin"sv, "Snake"sv))); - auto skin = TRY(SnakeSkin::create(skin_name, color)); - - return adopt_nonnull_ref_or_enomem(new (nothrow) Game(move(food_bitmaps), color, skin_name, move(skin))); -} - -Game::Game(Vector> food_bitmaps, Color snake_color, String snake_skin_name, NonnullOwnPtr skin) - : m_food_bitmaps(move(food_bitmaps)) - , m_snake_color(move(snake_color)) - , m_snake_skin_name(move(snake_skin_name)) - , m_snake_skin(move(skin)) -{ - set_font(Gfx::FontDatabase::default_fixed_width_font().bold_variant()); - reset(); -} - -void Game::pause() -{ - stop_timer(); -} - -void Game::start() -{ - static constexpr int timer_ms = 100; - start_timer(timer_ms); -} - -void Game::reset() -{ - m_head = { m_rows / 2, m_columns / 2 }; - m_tail.clear_with_capacity(); - m_length = 2; - m_score = 0; - m_is_new_high_score = false; - m_velocity_queue.clear(); - - if (on_score_update) - on_score_update(m_score); - - pause(); - start(); - spawn_fruit(); - update(); -} - -bool Game::is_available(Coordinate const& coord) -{ - for (size_t i = 0; i < m_tail.size(); ++i) { - if (m_tail[i] == coord) - return false; - } - if (m_head == coord) - return false; - if (m_fruit == coord) - return false; - return true; -} - -void Game::spawn_fruit() -{ - Coordinate coord; - for (;;) { - coord.row = get_random_uniform(m_rows); - coord.column = get_random_uniform(m_columns); - if (is_available(coord)) - break; - } - m_fruit = coord; - m_fruit_type = get_random_uniform(m_food_bitmaps.size()); -} - -void Game::timer_event(Core::TimerEvent&) -{ - Vector dirty_cells; - - m_tail.prepend(m_head); - - if (m_tail.size() > m_length) { - dirty_cells.append(m_tail.last()); - m_tail.take_last(); - } - - if (!m_velocity_queue.is_empty()) - m_velocity = m_velocity_queue.dequeue(); - - dirty_cells.append(m_head); - dirty_cells.append(m_tail.last()); - - m_head.row += m_velocity.vertical; - m_head.column += m_velocity.horizontal; - - m_last_velocity = m_velocity; - - if (m_head.row >= m_rows) - m_head.row = 0; - if (m_head.row < 0) - m_head.row = m_rows - 1; - if (m_head.column >= m_columns) - m_head.column = 0; - if (m_head.column < 0) - m_head.column = m_columns - 1; - - dirty_cells.append(m_head); - - for (size_t i = 0; i < m_tail.size(); ++i) { - if (m_head == m_tail[i]) { - game_over(); - return; - } - } - - if (m_head == m_fruit) { - ++m_length; - ++m_score; - - if (on_score_update) - m_is_new_high_score = on_score_update(m_score); - - dirty_cells.append(m_fruit); - spawn_fruit(); - dirty_cells.append(m_fruit); - } - - for (auto& coord : dirty_cells) { - update(cell_rect(coord)); - } -} - -void Game::keydown_event(GUI::KeyEvent& event) -{ - switch (event.key()) { - case KeyCode::Key_A: - case KeyCode::Key_Left: - if (last_velocity().horizontal == 1) - break; - queue_velocity(0, -1); - break; - case KeyCode::Key_D: - case KeyCode::Key_Right: - if (last_velocity().horizontal == -1) - break; - queue_velocity(0, 1); - break; - case KeyCode::Key_W: - case KeyCode::Key_Up: - if (last_velocity().vertical == 1) - break; - queue_velocity(-1, 0); - break; - case KeyCode::Key_S: - case KeyCode::Key_Down: - if (last_velocity().vertical == -1) - break; - queue_velocity(1, 0); - break; - default: - event.ignore(); - break; - } -} - -Gfx::IntRect Game::cell_rect(Coordinate const& coord) const -{ - auto game_rect = frame_inner_rect(); - auto cell_size = Gfx::IntSize(game_rect.width() / m_columns, game_rect.height() / m_rows); - return { - game_rect.x() + coord.column * cell_size.width(), - game_rect.y() + coord.row * cell_size.height(), - cell_size.width(), - cell_size.height() - }; -} - -void Game::paint_event(GUI::PaintEvent& event) -{ - GUI::Frame::paint_event(event); - GUI::Painter painter(*this); - painter.add_clip_rect(frame_inner_rect()); - painter.add_clip_rect(event.rect()); - painter.fill_rect(event.rect(), Color::Black); - - auto head_rect = cell_rect(m_head); - m_snake_skin->draw_head(painter, head_rect, m_last_velocity.as_direction()); - - for (size_t i = 0; i < m_tail.size(); i++) { - auto previous_position = i > 0 ? m_tail[i - 1] : m_head; - auto rect = cell_rect(m_tail[i]); - - if (i == m_tail.size() - 1) { - m_snake_skin->draw_tail(painter, rect, direction_to_position(m_tail[i], previous_position)); - continue; - } - - m_snake_skin->draw_body(painter, rect, direction_to_position(m_tail[i], previous_position), direction_to_position(m_tail[i], m_tail[i + 1])); - } - - painter.draw_scaled_bitmap(cell_rect(m_fruit), m_food_bitmaps[m_fruit_type], m_food_bitmaps[m_fruit_type]->rect()); -} - -void Game::game_over() -{ - stop_timer(); - - StringBuilder text; - text.appendff("Your score was {}", m_score); - if (m_is_new_high_score) { - text.append("\nThat's a new high score!"sv); - } - GUI::MessageBox::show(window(), - text.to_byte_string(), - "Game Over"sv, - GUI::MessageBox::Type::Information); - - reset(); -} - -void Game::queue_velocity(int v, int h) -{ - if (last_velocity().vertical == v && last_velocity().horizontal == h) - return; - m_velocity_queue.enqueue({ v, h }); -} - -Velocity const& Game::last_velocity() const -{ - if (!m_velocity_queue.is_empty()) - return m_velocity_queue.last(); - - return m_last_velocity; -} - -Direction Game::direction_to_position(Snake::Coordinate const& from, Snake::Coordinate const& to) const -{ - auto x_difference = to.column - from.column; - auto y_difference = to.row - from.row; - - if (y_difference == 1) - return Direction::Down; - if (y_difference == -1) - return Direction::Up; - if (y_difference != 0) { - // We wrapped around the screen, so invert the direction. - return (y_difference > 0) ? Direction::Up : Direction::Down; - } - - if (x_difference == 1) - return Direction::Right; - if (x_difference == -1) - return Direction::Left; - if (x_difference != 0) { - // We wrapped around the screen, so invert the direction. - return (x_difference > 0) ? Direction::Left : Direction::Right; - } - - VERIFY_NOT_REACHED(); -} - -void Game::config_string_did_change(StringView domain, StringView group, StringView key, StringView value) -{ - if (domain == "Snake"sv && group == "Snake"sv && key == "SnakeSkin"sv) { - set_skin_name(String::from_utf8(value).release_value_but_fixme_should_propagate_errors()); - return; - } -} - -void Game::config_u32_did_change(StringView domain, StringView group, StringView key, u32 value) -{ - if (domain == "Snake"sv && group == "Snake"sv && key == "BaseColor"sv) { - set_skin_color(Color::from_argb(value)); - return; - } -} - -void Game::set_skin_color(Gfx::Color color) -{ - if (m_snake_color != color) { - m_snake_color = color; - set_skin(SnakeSkin::create(m_snake_skin_name, m_snake_color).release_value_but_fixme_should_propagate_errors()); - } -} - -void Game::set_skin_name(String name) -{ - if (m_snake_skin_name != name) { - m_snake_skin_name = name; - set_skin(SnakeSkin::create(m_snake_skin_name, m_snake_color).release_value_but_fixme_should_propagate_errors()); - } -} - -void Game::set_skin(NonnullOwnPtr skin) -{ - m_snake_skin = move(skin); - update(); -} - -} diff --git a/Userland/Games/Snake/Game.h b/Userland/Games/Snake/Game.h deleted file mode 100644 index e7e5f8d417f..00000000000 --- a/Userland/Games/Snake/Game.h +++ /dev/null @@ -1,85 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * Copyright (c) 2022, the SerenityOS developers. - * Copyright (c) 2023, Sam Atkins - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include "Geometry.h" -#include "Skins/SnakeSkin.h" -#include -#include -#include -#include - -namespace Snake { - -class Game - : public GUI::Frame - , public Config::Listener { - C_OBJECT_ABSTRACT(Game); - -public: - static ErrorOr> try_create(); - - virtual ~Game() override = default; - - bool is_paused() const { return !has_timer(); } - void start(); - void pause(); - void reset(); - - Function on_score_update; - - void set_skin_color(Color); - Gfx::Color get_skin_color() const { return m_snake_color; } - void set_skin_name(String); - void set_skin(NonnullOwnPtr skin); - -private: - explicit Game(Vector> food_bitmaps, Color snake_color, String snake_skin_name, NonnullOwnPtr skin); - - virtual void paint_event(GUI::PaintEvent&) override; - virtual void keydown_event(GUI::KeyEvent&) override; - virtual void timer_event(Core::TimerEvent&) override; - - virtual void config_string_did_change(StringView domain, StringView group, StringView key, StringView value) override; - void config_u32_did_change(StringView domain, StringView group, StringView key, u32 value) override; - - void game_over(); - void spawn_fruit(); - bool is_available(Coordinate const&); - void queue_velocity(int v, int h); - Velocity const& last_velocity() const; - Gfx::IntRect cell_rect(Coordinate const&) const; - Direction direction_to_position(Coordinate const& from, Coordinate const& to) const; - - int m_rows { 20 }; - int m_columns { 20 }; - - Velocity m_velocity { 0, 1 }; - Velocity m_last_velocity { 0, 1 }; - - CircularQueue m_velocity_queue; - - Coordinate m_head; - Vector m_tail; - - Coordinate m_fruit; - int m_fruit_type { 0 }; - - size_t m_length { 0 }; - unsigned m_score { 0 }; - bool m_is_new_high_score { false }; - - Vector> m_food_bitmaps; - - Color m_snake_color; - String m_snake_skin_name; - NonnullOwnPtr m_snake_skin; -}; - -} diff --git a/Userland/Games/Snake/Geometry.h b/Userland/Games/Snake/Geometry.h deleted file mode 100644 index 3308cc9a2b6..00000000000 --- a/Userland/Games/Snake/Geometry.h +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * Copyright (c) 2022, the SerenityOS developers. - * Copyright (c) 2023, Sam Atkins - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include - -namespace Snake { - -enum class Direction { - Up, - Right, - Down, - Left, -}; - -struct Coordinate { - int row { 0 }; - int column { 0 }; - - bool operator==(Coordinate const& other) const - { - return row == other.row && column == other.column; - } -}; - -struct Velocity { - int vertical { 0 }; - int horizontal { 0 }; - - Direction as_direction() const - { - if (vertical > 0) - return Direction::Down; - if (vertical < 0) - return Direction::Up; - if (horizontal > 0) - return Direction::Right; - if (horizontal < 0) - return Direction::Left; - - return Direction::Up; - } -}; - -} diff --git a/Userland/Games/Snake/MainWidget.h b/Userland/Games/Snake/MainWidget.h deleted file mode 100644 index bcd4364dd65..00000000000 --- a/Userland/Games/Snake/MainWidget.h +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright (c) 2023, the SerenityOS developers - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include - -namespace Snake { - -class MainWidget : public GUI::Widget { - C_OBJECT_ABSTRACT(MainWidget) -public: - static ErrorOr> try_create(); - virtual ~MainWidget() override = default; - -private: - MainWidget() = default; -}; - -} diff --git a/Userland/Games/Snake/Skins/ClassicSkin.cpp b/Userland/Games/Snake/Skins/ClassicSkin.cpp deleted file mode 100644 index 2ee86af2575..00000000000 --- a/Userland/Games/Snake/Skins/ClassicSkin.cpp +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * Copyright (c) 2021, Mustafa Quraish - * Copyright (c) 2023, the SerenityOS developers. - * Copyright (c) 2023, Sam Atkins - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "ClassicSkin.h" - -namespace Snake { - -ClassicSkin::ClassicSkin(Color color) - : m_skin_color(color) -{ -} - -void ClassicSkin::draw_tile_at(Gfx::Painter& painter, Gfx::IntRect const& rect) -{ - painter.fill_rect(rect, m_skin_color.darkened(0.77)); - - Gfx::IntRect left_side(rect.x(), rect.y(), 2, rect.height()); - Gfx::IntRect top_side(rect.x(), rect.y(), rect.width(), 2); - Gfx::IntRect right_side(rect.right() - 2, rect.y(), 2, rect.height()); - Gfx::IntRect bottom_side(rect.x(), rect.bottom() - 2, rect.width(), 2); - auto top_left_color = m_skin_color.lightened(0.88); - auto bottom_right_color = m_skin_color.darkened(0.55); - painter.fill_rect(left_side, top_left_color); - painter.fill_rect(right_side, bottom_right_color); - painter.fill_rect(top_side, top_left_color); - painter.fill_rect(bottom_side, bottom_right_color); -} - -void ClassicSkin::draw_head(Gfx::Painter& painter, Gfx::IntRect const& head, Direction) -{ - painter.fill_rect(head, m_skin_color); -} -void ClassicSkin::draw_body(Gfx::Painter& painter, Gfx::IntRect const& rect, Direction, Direction) -{ - draw_tile_at(painter, rect); -} -void ClassicSkin::draw_tail(Gfx::Painter& painter, Gfx::IntRect const& tail, Direction) -{ - draw_tile_at(painter, tail); -} - -} diff --git a/Userland/Games/Snake/Skins/ClassicSkin.h b/Userland/Games/Snake/Skins/ClassicSkin.h deleted file mode 100644 index 9a742eeb74c..00000000000 --- a/Userland/Games/Snake/Skins/ClassicSkin.h +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * Copyright (c) 2021, Mustafa Quraish - * Copyright (c) 2023, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include "SnakeSkin.h" -#include - -namespace Snake { - -class ClassicSkin : public SnakeSkin { -public: - ClassicSkin(Color); - - virtual ~ClassicSkin() override = default; - - void draw_head(Gfx::Painter&, Gfx::IntRect const& head, Direction body_direction) override; - void draw_body(Gfx::Painter&, Gfx::IntRect const& rect, Direction previous_direction, Direction next_direction) override; - void draw_tail(Gfx::Painter& painter, Gfx::IntRect const& tail, Direction body_direction) override; - -private: - void draw_tile_at(Gfx::Painter&, Gfx::IntRect const&); - - Gfx::Color m_skin_color = { Color::Yellow }; -}; - -} diff --git a/Userland/Games/Snake/Skins/ImageSkin.cpp b/Userland/Games/Snake/Skins/ImageSkin.cpp deleted file mode 100644 index 608a73937f0..00000000000 --- a/Userland/Games/Snake/Skins/ImageSkin.cpp +++ /dev/null @@ -1,103 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * Copyright (c) 2021, Mustafa Quraish - * Copyright (c) 2023, the SerenityOS developers. - * Copyright (c) 2023, Sam Atkins - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "ImageSkin.h" -#include - -namespace Snake { - -ErrorOr> ImageSkin::create(StringView skin_name) -{ - auto skin_directory = TRY(Core::Directory::create(ByteString::formatted("/res/graphics/snake/skins/{}", skin_name), Core::Directory::CreateDirectories::No)); - - auto head = TRY(Gfx::Bitmap::load_from_file(TRY(skin_directory.open("head.png"sv, Core::File::OpenMode::Read)), "head.png"sv)); - Vector> head_bitmaps; - TRY(head_bitmaps.try_ensure_capacity(4)); - TRY(head_bitmaps.try_append(head)); - TRY(head_bitmaps.try_append(TRY(head->rotated(Gfx::RotationDirection::Clockwise)))); - TRY(head_bitmaps.try_append(TRY(head_bitmaps[1]->rotated(Gfx::RotationDirection::Clockwise)))); - TRY(head_bitmaps.try_append(TRY(head_bitmaps[2]->rotated(Gfx::RotationDirection::Clockwise)))); - - Vector> body_bitmaps; - TRY(body_bitmaps.try_ensure_capacity(16)); - auto tail_up = TRY(Gfx::Bitmap::load_from_file(TRY(skin_directory.open("tail.png"sv, Core::File::OpenMode::Read)), "tail.png"sv)); - auto tail_right = TRY(tail_up->rotated(Gfx::RotationDirection::Clockwise)); - auto tail_down = TRY(tail_right->rotated(Gfx::RotationDirection::Clockwise)); - auto tail_left = TRY(tail_down->rotated(Gfx::RotationDirection::Clockwise)); - auto corner_ur = TRY(Gfx::Bitmap::load_from_file(TRY(skin_directory.open("corner.png"sv, Core::File::OpenMode::Read)), "corner.png"sv)); - auto corner_dr = TRY(corner_ur->rotated(Gfx::RotationDirection::Clockwise)); - auto corner_dl = TRY(corner_dr->rotated(Gfx::RotationDirection::Clockwise)); - auto corner_ul = TRY(corner_dl->rotated(Gfx::RotationDirection::Clockwise)); - auto horizontal = TRY(Gfx::Bitmap::load_from_file(TRY(skin_directory.open("horizontal.png"sv, Core::File::OpenMode::Read)), "horizontal.png"sv)); - auto vertical = TRY(Gfx::Bitmap::load_from_file(TRY(skin_directory.open("vertical.png"sv, Core::File::OpenMode::Read)), "vertical.png"sv)); - - TRY(body_bitmaps.try_append(tail_up)); - TRY(body_bitmaps.try_append(corner_ur)); - TRY(body_bitmaps.try_append(vertical)); - TRY(body_bitmaps.try_append(corner_ul)); - - TRY(body_bitmaps.try_append(corner_ur)); - TRY(body_bitmaps.try_append(tail_right)); - TRY(body_bitmaps.try_append(corner_dr)); - TRY(body_bitmaps.try_append(horizontal)); - - TRY(body_bitmaps.try_append(vertical)); - TRY(body_bitmaps.try_append(corner_dr)); - TRY(body_bitmaps.try_append(tail_down)); - TRY(body_bitmaps.try_append(corner_dl)); - - TRY(body_bitmaps.try_append(corner_ul)); - TRY(body_bitmaps.try_append(horizontal)); - TRY(body_bitmaps.try_append(corner_dl)); - TRY(body_bitmaps.try_append(tail_left)); - - return adopt_nonnull_own_or_enomem(new (nothrow) ImageSkin(skin_name, move(head_bitmaps), move(body_bitmaps))); -} - -ImageSkin::ImageSkin(StringView skin_name, Vector> head_bitmaps, Vector> body_bitmaps) - : m_head_bitmaps(move(head_bitmaps)) - , m_body_bitmaps(move(body_bitmaps)) -{ - m_skin_name = String::from_utf8(skin_name).release_value_but_fixme_should_propagate_errors(); -} - -static int image_index_from_directions(Direction from, Direction to) -{ - // Sprites are ordered in memory like this, to make the calculation easier: - // - // From direction - // U R D L - // ╹ ┗ ┃ ┛ Up To direction - // ┗ ╺ ┏ ━ Right - // ┃ ┏ ╻ ┓ Down - // ┛ ━ ┓ ╸ Left - // (Numbered 0-15, starting top left, one row at a time.) - // - // This does cause some redundancy for now, but RefPtrs are small. - return to_underlying(to) * 4 + to_underlying(from); -} - -void ImageSkin::draw_head(Gfx::Painter& painter, Gfx::IntRect const& head, Direction facing_direction) -{ - auto& bitmap = m_head_bitmaps[to_underlying(facing_direction)]; - painter.draw_scaled_bitmap(head, bitmap, bitmap->rect()); -} - -void ImageSkin::draw_body(Gfx::Painter& painter, Gfx::IntRect const& rect, Direction previous_direction, Direction next_direction) -{ - auto& bitmap = m_body_bitmaps[image_index_from_directions(previous_direction, next_direction)]; - painter.draw_scaled_bitmap(rect, bitmap, bitmap->rect()); -} - -void ImageSkin::draw_tail(Gfx::Painter& painter, Gfx::IntRect const& rect, Direction body_direction) -{ - draw_body(painter, rect, body_direction, body_direction); -} - -} diff --git a/Userland/Games/Snake/Skins/ImageSkin.h b/Userland/Games/Snake/Skins/ImageSkin.h deleted file mode 100644 index af09e89f8af..00000000000 --- a/Userland/Games/Snake/Skins/ImageSkin.h +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * Copyright (c) 2021, Mustafa Quraish - * Copyright (c) 2023, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include "SnakeSkin.h" -#include -#include -#include -#include -#include - -namespace Snake { - -class ImageSkin : public SnakeSkin { -public: - static ErrorOr> create(StringView skin_name); - - virtual ~ImageSkin() override = default; - - void draw_head(Gfx::Painter&, Gfx::IntRect const& head, Direction facing_direction) override; - void draw_body(Gfx::Painter&, Gfx::IntRect const& rect, Direction previous_direction, Direction next_direction) override; - void draw_tail(Gfx::Painter&, Gfx::IntRect const& tail, Direction body_direction) override; - -private: - ImageSkin(StringView skin_name, Vector> head_bitmaps, Vector> body_bitmaps); - - String m_skin_name; - - Vector> m_head_bitmaps; - Vector> m_body_bitmaps; -}; - -} diff --git a/Userland/Games/Snake/Skins/SnakeSkin.cpp b/Userland/Games/Snake/Skins/SnakeSkin.cpp deleted file mode 100644 index cdb213a8cfc..00000000000 --- a/Userland/Games/Snake/Skins/SnakeSkin.cpp +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright (c) 2023, Sam Atkins - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "SnakeSkin.h" -#include "ClassicSkin.h" -#include "ImageSkin.h" -#include -#include - -namespace Snake { - -ErrorOr> SnakeSkin::create(StringView skin_name, Color color) -{ - if (skin_name == "Classic"sv) - return try_make(color); - - // Try to find an image-based skin matching the name. - if (FileSystem::exists(TRY(String::formatted("/res/graphics/snake/skins/{}", skin_name)))) - return ImageSkin::create(skin_name); - - // Fall-back on classic - return try_make(color); -} - -} diff --git a/Userland/Games/Snake/Skins/SnakeSkin.h b/Userland/Games/Snake/Skins/SnakeSkin.h deleted file mode 100644 index 64f747f4c5d..00000000000 --- a/Userland/Games/Snake/Skins/SnakeSkin.h +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright (c) 2023, the SerenityOS developers. - * Copyright (c) 2023, Sam Atkins - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include "../Geometry.h" -#include -#include - -namespace Snake { - -class SnakeSkin { -public: - static ErrorOr> create(StringView skin_name, Color color); - - virtual ~SnakeSkin() = default; - - virtual void draw_head(Gfx::Painter&, Gfx::IntRect const& rect, Direction facing_direction) = 0; - virtual void draw_body(Gfx::Painter&, Gfx::IntRect const& rect, Direction previous_direction, Direction next_direction) = 0; - virtual void draw_tail(Gfx::Painter&, Gfx::IntRect const& rect, Direction body_direction) = 0; -}; - -} diff --git a/Userland/Games/Snake/Snake.gml b/Userland/Games/Snake/Snake.gml deleted file mode 100644 index 3857d749d46..00000000000 --- a/Userland/Games/Snake/Snake.gml +++ /dev/null @@ -1,14 +0,0 @@ -@Snake::MainWidget { - layout: @GUI::VerticalBoxLayout {} - fill_with_background_color: true - - @Snake::Game { - name: "game" - fill_with_background_color: true - } - - @GUI::Statusbar { - name: "statusbar" - segment_count: 2 - } -} diff --git a/Userland/Games/Snake/main.cpp b/Userland/Games/Snake/main.cpp deleted file mode 100644 index c9aa392de45..00000000000 --- a/Userland/Games/Snake/main.cpp +++ /dev/null @@ -1,171 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * Copyright (c) 2023, Sam Atkins - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "Game.h" -#include "MainWidget.h" -#include "Skins/SnakeSkin.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -ErrorOr serenity_main(Main::Arguments arguments) -{ - TRY(Core::System::pledge("stdio rpath recvfd sendfd unix")); - - auto app = TRY(GUI::Application::create(arguments)); - - Config::pledge_domain("Snake"); - Config::monitor_domain("Snake"); - - TRY(Desktop::Launcher::add_allowed_handler_with_only_specific_urls("/bin/Help", { URL::create_with_file_scheme("/usr/share/man/man6/Snake.md") })); - TRY(Desktop::Launcher::seal_allowlist()); - - TRY(Core::System::pledge("stdio rpath recvfd sendfd")); - - TRY(Core::System::unveil("/tmp/session/%sid/portal/launch", "rw")); - TRY(Core::System::unveil("/res", "r")); - TRY(Core::System::unveil(nullptr, nullptr)); - - auto app_icon = TRY(GUI::Icon::try_create_default_icon("app-snake"sv)); - - auto window = GUI::Window::construct(); - - window->set_double_buffering_enabled(false); - window->set_title("Snake"); - window->resize(324, 345); - - auto widget = TRY(Snake::MainWidget::try_create()); - window->set_main_widget(widget); - - auto& game = *widget->find_descendant_of_type_named("game"); - game.set_focus(true); - - auto high_score = Config::read_u32("Snake"sv, "Snake"sv, "HighScore"sv, 0); - auto snake_skin_name = Config::read_string("Snake"sv, "Snake"sv, "SnakeSkin"sv, "Snake"sv); - - auto& statusbar = *widget->find_descendant_of_type_named("statusbar"sv); - statusbar.set_text(0, "Score: 0"_string); - statusbar.set_text(1, TRY(String::formatted("High Score: {}", high_score))); - GUI::Application::the()->on_action_enter = [&statusbar](GUI::Action& action) { - statusbar.set_override_text(action.status_tip()); - }; - GUI::Application::the()->on_action_leave = [&statusbar](GUI::Action&) { - statusbar.set_override_text({}); - }; - - game.on_score_update = [&](auto score) { - statusbar.set_text(0, String::formatted("Score: {}", score).release_value_but_fixme_should_propagate_errors()); - if (score <= high_score) - return false; - - statusbar.set_text(1, String::formatted("High Score: {}", score).release_value_but_fixme_should_propagate_errors()); - Config::write_u32("Snake"sv, "Snake"sv, "HighScore"sv, score); - - high_score = score; - return true; - }; - - auto game_menu = window->add_menu("&Game"_string); - - game_menu->add_action(GUI::Action::create("&New Game", { Mod_None, Key_F2 }, TRY(Gfx::Bitmap::load_from_file("/res/icons/16x16/reload.png"sv)), [&](auto&) { - game.reset(); - })); - static String const pause_text = "&Pause Game"_string; - auto const pause_icon = TRY(Gfx::Bitmap::load_from_file("/res/icons/16x16/pause.png"sv)); - static String const continue_text = "&Continue Game"_string; - auto const continue_icon = TRY(Gfx::Bitmap::load_from_file("/res/icons/16x16/play.png"sv)); - game_menu->add_action(GUI::Action::create(pause_text.to_byte_string(), { Mod_None, Key_Space }, pause_icon, [&](auto& action) { - if (game.has_timer()) { - game.pause(); - action.set_text(continue_text.to_byte_string()); - action.set_icon(continue_icon); - } else { - game.start(); - action.set_text(pause_text.to_byte_string()); - action.set_icon(pause_icon); - } - })); - - auto change_snake_color = GUI::Action::create("&Change Snake Color", TRY(Gfx::Bitmap::load_from_file("/res/icons/16x16/color-chooser.png"sv)), [&](auto&) { - auto was_paused = game.is_paused(); - if (!was_paused) - game.pause(); - auto dialog = GUI::ColorPicker::construct(game.get_skin_color(), window); - dialog->on_color_changed = [&game](Gfx::Color color) { - game.set_skin_color(color); - }; - if (dialog->exec() == GUI::Dialog::ExecResult::OK) - Config::write_u32("Snake"sv, "Snake"sv, "BaseColor"sv, dialog->color().value()); - if (!was_paused) - game.start(); - }); - change_snake_color->set_enabled(snake_skin_name == "Classic"sv); - game_menu->add_action(change_snake_color); - - GUI::ActionGroup skin_action_group; - skin_action_group.set_exclusive(true); - - auto skin_menu = game_menu->add_submenu("&Skin"_string); - skin_menu->set_icon(app_icon.bitmap_for_size(16)); - - auto add_skin_action = [&](StringView name, bool enable_color) -> ErrorOr { - auto action = GUI::Action::create_checkable(name, GUI::Shortcut {}, [&, enable_color](auto& action) { - Config::write_string("Snake"sv, "Snake"sv, "SnakeSkin"sv, action.text()); - game.set_skin_name(String::from_byte_string(action.text()).release_value_but_fixme_should_propagate_errors()); - change_snake_color->set_enabled(enable_color); - }); - - skin_action_group.add_action(*action); - if (snake_skin_name == name) - action->set_checked(true); - skin_menu->add_action(*action); - return {}; - }; - - TRY(Core::Directory::for_each_entry("/res/graphics/snake/skins/"sv, Core::DirIterator::SkipParentAndBaseDir, [&](auto& entry, auto&) -> ErrorOr { - TRY(add_skin_action(entry.name, false)); - return IterationDecision::Continue; - })); - TRY(add_skin_action("Classic"sv, true)); - - game_menu->add_separator(); - game_menu->add_action(GUI::CommonActions::make_quit_action([](auto&) { - GUI::Application::the()->quit(); - })); - - auto view_menu = window->add_menu("&View"_string); - view_menu->add_action(GUI::CommonActions::make_fullscreen_action([&](auto&) { - window->set_fullscreen(!window->is_fullscreen()); - })); - - auto help_menu = window->add_menu("&Help"_string); - help_menu->add_action(GUI::CommonActions::make_command_palette_action(window)); - help_menu->add_action(GUI::CommonActions::make_help_action([](auto&) { - Desktop::Launcher::open(URL::create_with_file_scheme("/usr/share/man/man6/Snake.md"), "/bin/Help"); - })); - help_menu->add_action(GUI::CommonActions::make_about_action("Snake"_string, app_icon, window)); - - window->show(); - - window->set_icon(app_icon.bitmap_for_size(16)); - - return app->exec(); -} diff --git a/Userland/Games/Solitaire/CMakeLists.txt b/Userland/Games/Solitaire/CMakeLists.txt deleted file mode 100644 index c486c05cdc3..00000000000 --- a/Userland/Games/Solitaire/CMakeLists.txt +++ /dev/null @@ -1,16 +0,0 @@ -serenity_component( - Solitaire - RECOMMENDED - TARGETS Solitaire -) - -compile_gml(Solitaire.gml SolitaireGML.cpp solitaire_gml) - -set(SOURCES - Game.cpp - SolitaireGML.cpp - main.cpp -) - -serenity_app(Solitaire ICON app-solitaire) -target_link_libraries(Solitaire PRIVATE LibCards LibConfig LibGUI LibDesktop LibGfx LibCore LibMain LibURL) diff --git a/Userland/Games/Solitaire/Game.cpp b/Userland/Games/Solitaire/Game.cpp deleted file mode 100644 index 66ac1d827ed..00000000000 --- a/Userland/Games/Solitaire/Game.cpp +++ /dev/null @@ -1,736 +0,0 @@ -/* - * Copyright (c) 2020, Till Mayer - * Copyright (c) 2021-2023, Sam Atkins - * Copyright (c) 2022, the SerenityOS developers. - * Copyright (c) 2023, David Ganz - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "Game.h" -#include -#include -#include -#include - -namespace Solitaire { - -static constexpr uint8_t new_game_animation_delay = 2; -static constexpr int s_timer_interval_ms = 1000 / 60; -static constexpr int s_timer_solving_interval_ms = 100; - -ErrorOr> Game::try_create() -{ - auto game = TRY(adopt_nonnull_ref_or_enomem(new (nothrow) Game())); - - TRY(game->add_stack(Gfx::IntPoint { 10, 10 }, CardStack::Type::Stock)); - TRY(game->add_stack(Gfx::IntPoint { 10 + Card::width + 10, 10 }, CardStack::Type::Waste)); - TRY(game->add_stack(Gfx::IntPoint { 10 + Card::width + 10, 10 }, CardStack::Type::Play, game->stack_at_location(Waste))); - TRY(game->add_stack(Gfx::IntPoint { Game::width - 4 * Card::width - 40, 10 }, CardStack::Type::Foundation)); - TRY(game->add_stack(Gfx::IntPoint { Game::width - 3 * Card::width - 30, 10 }, CardStack::Type::Foundation)); - TRY(game->add_stack(Gfx::IntPoint { Game::width - 2 * Card::width - 20, 10 }, CardStack::Type::Foundation)); - TRY(game->add_stack(Gfx::IntPoint { Game::width - Card::width - 10, 10 }, CardStack::Type::Foundation)); - TRY(game->add_stack(Gfx::IntPoint { 10, 10 + Card::height + 10 }, CardStack::Type::Normal)); - TRY(game->add_stack(Gfx::IntPoint { 10 + Card::width + 10, 10 + Card::height + 10 }, CardStack::Type::Normal)); - TRY(game->add_stack(Gfx::IntPoint { 10 + 2 * Card::width + 20, 10 + Card::height + 10 }, CardStack::Type::Normal)); - TRY(game->add_stack(Gfx::IntPoint { 10 + 3 * Card::width + 30, 10 + Card::height + 10 }, CardStack::Type::Normal)); - TRY(game->add_stack(Gfx::IntPoint { 10 + 4 * Card::width + 40, 10 + Card::height + 10 }, CardStack::Type::Normal)); - TRY(game->add_stack(Gfx::IntPoint { 10 + 5 * Card::width + 50, 10 + Card::height + 10 }, CardStack::Type::Normal)); - TRY(game->add_stack(Gfx::IntPoint { 10 + 6 * Card::width + 60, 10 + Card::height + 10 }, CardStack::Type::Normal)); - - return game; -} - -Game::Game() = default; - -static float rand_float() -{ - return get_random_uniform(RAND_MAX) / static_cast(RAND_MAX); -} - -void Game::deal_next_card() -{ - VERIFY(m_state == State::NewGameAnimation); - - auto& current_pile = stack_at_location(piles.at(m_new_game_animation_pile)); - - if (current_pile.count() < m_new_game_animation_pile) { - auto card = m_new_deck.take_last(); - card->set_upside_down(true); - current_pile.push(card).release_value_but_fixme_should_propagate_errors(); - } else { - current_pile.push(m_new_deck.take_last()).release_value_but_fixme_should_propagate_errors(); - ++m_new_game_animation_pile; - } - - update(current_pile.bounding_box()); - - if (m_new_game_animation_pile == piles.size()) { - auto& stock_pile = stack_at_location(Stock); - while (!m_new_deck.is_empty()) - stock_pile.push(m_new_deck.take_last()).release_value_but_fixme_should_propagate_errors(); - - update(stock_pile.bounding_box()); - - m_state = State::WaitingForNewGame; - stop_timer(); - } -} - -void Game::timer_event(Core::TimerEvent&) -{ - switch (m_state) { - case State::StartGameOverAnimationNextFrame: { - m_state = State::GameOverAnimation; - set_background_fill_enabled(false); - break; - } - case State::GameOverAnimation: { - if (m_animation.position().x() >= Game::width || m_animation.card_rect().right() <= 0) - create_new_animation_card(); - - if (m_animation.tick()) - update(m_animation.card_rect()); - break; - } - case State::NewGameAnimation: { - if (m_new_game_animation_delay < new_game_animation_delay) { - ++m_new_game_animation_delay; - } else { - m_new_game_animation_delay = 0; - deal_next_card(); - } - break; - } - case State::Solving: { - step_solve(); - break; - } - default: - break; - } -} - -void Game::create_new_animation_card() -{ - auto suit = static_cast(get_random_uniform(to_underlying(Cards::Suit::__Count))); - auto rank = static_cast(get_random_uniform(to_underlying(Cards::Rank::__Count))); - Gfx::IntPoint position { get_random_uniform(Game::width - Card::width), get_random_uniform(Game::height / 8) }; - - int x_direction = position.x() > (Game::width / 2) ? -1 : 1; - m_animation = Animation(suit, rank, position, rand_float() + .4f, x_direction * (get_random_uniform(3) + 2), .6f + rand_float() * .4f); -} - -void Game::set_background_fill_enabled(bool enabled) -{ - Widget* widget = this; - while (widget) { - widget->set_fill_with_background_color(enabled); - widget = widget->parent_widget(); - } -} - -void Game::start_game_over_animation() -{ - if (m_state == State::GameOverAnimation || m_state == State::StartGameOverAnimationNextFrame) - return; - - m_last_move = {}; - if (on_undo_availability_change) - on_undo_availability_change(false); - - create_new_animation_card(); - - // We wait one frame, to make sure that the foundation stacks are repainted before we start. - // Otherwise, if the game ended from an attempt_to_move_card_to_foundations() move, the - // foundations could appear empty or otherwise incorrect. - m_state = State::StartGameOverAnimationNextFrame; - - start_timer(s_timer_interval_ms); - - if (on_game_end) - on_game_end(GameOverReason::Victory, m_score); -} - -void Game::stop_game_over_animation() -{ - if (m_state != State::GameOverAnimation) - return; - - set_background_fill_enabled(true); - m_state = State::NewGameAnimation; - update(); - - stop_timer(); -} - -void Game::setup(Mode mode) -{ - if (m_state == State::NewGameAnimation) - stop_timer(); - - stop_game_over_animation(); - m_mode = mode; - - if (on_game_end) - on_game_end(GameOverReason::NewGame, m_score); - - for (auto& stack : stacks()) - stack->clear(); - - m_new_deck.clear(); - m_new_game_animation_pile = 0; - m_passes_left_before_punishment = recycle_rules().passes_allowed_before_punishment; - m_score = 0; - update_score(0); - if (on_undo_availability_change) - on_undo_availability_change(false); - - m_new_deck = Cards::create_standard_deck(Cards::Shuffle::Yes).release_value_but_fixme_should_propagate_errors(); - - clear_moving_cards(); - - m_state = State::NewGameAnimation; - start_timer(s_timer_interval_ms); - update(); -} - -void Game::start_timer_if_necessary() -{ - if (on_game_start && m_state == State::WaitingForNewGame) { - on_game_start(); - m_state = State::GameInProgress; - } -} - -void Game::score_move(CardStack& from, CardStack& to, bool inverse) -{ - if (from.type() == CardStack::Type::Play && to.type() == CardStack::Type::Normal) { - update_score(5 * (inverse ? -1 : 1)); - } else if (from.type() == CardStack::Type::Play && to.type() == CardStack::Type::Foundation) { - update_score(10 * (inverse ? -1 : 1)); - } else if (from.type() == CardStack::Type::Normal && to.type() == CardStack::Type::Foundation) { - update_score(10 * (inverse ? -1 : 1)); - } else if (from.type() == CardStack::Type::Foundation && to.type() == CardStack::Type::Normal) { - update_score(-15 * (inverse ? -1 : 1)); - } -} - -void Game::score_flip(bool inverse) -{ - update_score(5 * (inverse ? -1 : 1)); -} - -void Game::update_score(int to_add) -{ - m_score = max(static_cast(m_score) + to_add, 0); - - if (on_score_update) - on_score_update(m_score); -} - -void Game::keydown_event(GUI::KeyEvent& event) -{ - if (is_moving_cards() || m_state == State::NewGameAnimation || m_state == State::GameOverAnimation) { - event.ignore(); - return; - } - - if (event.shift() && event.key() == KeyCode::Key_F12) { - start_game_over_animation(); - } else if (event.key() == KeyCode::Key_Tab) { - auto_move_eligible_cards_to_foundations(); - } else if (event.key() == KeyCode::Key_Space) { - draw_cards(); - } else if (event.shift() && event.key() == KeyCode::Key_F11) { - if constexpr (SOLITAIRE_DEBUG) { - dump_layout(); - } - } else { - event.ignore(); - } -} - -void Game::mousedown_event(GUI::MouseEvent& event) -{ - GUI::Frame::mousedown_event(event); - - if (m_state == State::NewGameAnimation || m_state == State::GameOverAnimation) - return; - - auto click_location = event.position(); - for (auto& to_check : stacks()) { - if (to_check->type() == CardStack::Type::Waste) - continue; - - if (to_check->bounding_box().contains(click_location)) { - if (to_check->type() == CardStack::Type::Stock) { - draw_cards(); - } else if (!to_check->is_empty()) { - auto& top_card = to_check->peek(); - - if (top_card.is_upside_down()) { - if (top_card.rect().contains(click_location)) { - top_card.set_upside_down(false); - score_flip(); - start_timer_if_necessary(); - update(top_card.rect()); - remember_flip_for_undo(top_card); - - if (on_move) - on_move(); - } - } else if (!is_moving_cards()) { - if (is_auto_collecting() && attempt_to_move_card_to_foundations(to_check)) { - if (on_move) - on_move(); - break; - } - - if (event.button() == GUI::MouseButton::Secondary) { - preview_card(to_check, click_location); - } else { - pick_up_cards_from_stack(to_check, click_location, Cards::CardStack::MovementRule::Alternating).release_value_but_fixme_should_propagate_errors(); - m_mouse_down_location = click_location; - m_mouse_down = true; - } - - start_timer_if_necessary(); - } - } - break; - } - } -} - -void Game::mouseup_event(GUI::MouseEvent& event) -{ - GUI::Frame::mouseup_event(event); - clear_hovered_stack(); - - if (is_previewing_card()) { - clear_card_preview(); - return; - } - - if (!is_moving_cards() || m_state == State::NewGameAnimation || m_state == State::GameOverAnimation) - return; - - bool rebound = true; - if (auto target_stack = find_stack_to_drop_on(Cards::CardStack::MovementRule::Alternating); !target_stack.is_null()) { - auto& stack = *target_stack; - remember_move_for_undo(*moving_cards_source_stack(), stack, moving_cards()); - - drop_cards_on_stack(stack, Cards::CardStack::MovementRule::Alternating).release_value_but_fixme_should_propagate_errors(); - - if (moving_cards_source_stack()->type() == CardStack::Type::Play) - pop_waste_to_play_stack(); - - score_move(*moving_cards_source_stack(), stack); - rebound = false; - - if (on_move) - on_move(); - } - - if (rebound) { - for (auto& to_intersect : moving_cards()) - mark_intersecting_stacks_dirty(to_intersect); - - moving_cards_source_stack()->rebound_cards(); - update(moving_cards_source_stack()->bounding_box()); - } - - m_mouse_down = false; -} - -void Game::mousemove_event(GUI::MouseEvent& event) -{ - GUI::Frame::mousemove_event(event); - - if (!m_mouse_down || m_state == State::NewGameAnimation || m_state == State::GameOverAnimation) - return; - - auto click_location = event.position(); - int dx = click_location.dx_relative_to(m_mouse_down_location); - int dy = click_location.dy_relative_to(m_mouse_down_location); - - if (auto target_stack = find_stack_to_drop_on(Cards::CardStack::MovementRule::Alternating)) { - if (target_stack != m_hovered_stack) { - clear_hovered_stack(); - - m_hovered_stack = move(target_stack); - m_hovered_stack->set_highlighted(true); - update(m_hovered_stack->bounding_box()); - } - } else { - clear_hovered_stack(); - } - - for (auto& to_intersect : moving_cards()) { - mark_intersecting_stacks_dirty(to_intersect); - to_intersect->rect().translate_by(dx, dy); - update(to_intersect->rect()); - } - - m_mouse_down_location = click_location; -} - -void Game::doubleclick_event(GUI::MouseEvent& event) -{ - GUI::Frame::doubleclick_event(event); - - if (m_state == State::GameOverAnimation) { - setup(mode()); - return; - } - - if (m_state == State::NewGameAnimation) { - while (m_state == State::NewGameAnimation) - deal_next_card(); - return; - } - - auto click_location = event.position(); - for (auto& to_check : stacks()) { - if (to_check->type() != CardStack::Type::Normal && to_check->type() != CardStack::Type::Play) - continue; - - if (to_check->bounding_box().contains(click_location) && !to_check->is_empty()) { - auto& top_card = to_check->peek(); - if (!top_card.is_upside_down() && top_card.rect().contains(click_location)) - attempt_to_move_card_to_foundations(to_check); - - break; - } - } -} - -void Game::check_for_game_over() -{ - for (auto foundationID : foundations) { - auto& foundation = stack_at_location(foundationID); - - if (foundation.count() != Card::card_count) - return; - } - - if (has_timer()) - stop_timer(); - start_game_over_animation(); -} - -void Game::draw_cards() -{ - auto& waste = stack_at_location(Waste); - auto& stock = stack_at_location(Stock); - auto& play = stack_at_location(Play); - - if (stock.is_empty()) { - if (waste.is_empty() && play.is_empty()) - return; - - update(waste.bounding_box()); - update(play.bounding_box()); - - Vector> moved_cards; - while (!play.is_empty()) { - auto card = play.pop(); - stock.push(card).release_value_but_fixme_should_propagate_errors(); - moved_cards.prepend(card); - } - - while (!waste.is_empty()) { - auto card = waste.pop(); - stock.push(card).release_value_but_fixme_should_propagate_errors(); - moved_cards.prepend(card); - } - - remember_move_for_undo(waste, stock, moved_cards); - - if (m_passes_left_before_punishment == 0) - update_score(recycle_rules().punishment); - else - --m_passes_left_before_punishment; - - update(stock.bounding_box()); - } else { - auto play_bounding_box = play.bounding_box(); - play.take_all(waste).release_value_but_fixme_should_propagate_errors(); - - size_t cards_to_draw = 0; - switch (m_mode) { - case Mode::SingleCardDraw: - cards_to_draw = 1; - break; - case Mode::ThreeCardDraw: - cards_to_draw = 3; - break; - default: - VERIFY_NOT_REACHED(); - break; - } - - update(stock.bounding_box()); - - Vector> cards_drawn; - for (size_t i = 0; (i < cards_to_draw) && !stock.is_empty(); ++i) { - auto card = stock.pop(); - cards_drawn.prepend(card); - play.push(move(card)).release_value_but_fixme_should_propagate_errors(); - } - - remember_move_for_undo(stock, play, cards_drawn); - - if (play.bounding_box().size().width() > play_bounding_box.size().width()) - update(play.bounding_box()); - else - update(play_bounding_box); - } - - start_timer_if_necessary(); -} - -void Game::pop_waste_to_play_stack() -{ - auto& waste = stack_at_location(Waste); - auto& play = stack_at_location(Play); - if (play.is_empty() && !waste.is_empty()) { - auto card = waste.pop(); - moving_cards().append(card); - play.push(move(card)).release_value_but_fixme_should_propagate_errors(); - } -} - -bool Game::attempt_to_move_card_to_foundations(CardStack& from) -{ - if (from.is_empty()) - return false; - - auto& top_card = from.peek(); - if (top_card.is_upside_down()) - return false; - - bool card_was_moved = false; - - for (auto foundationID : foundations) { - auto& foundation = stack_at_location(foundationID); - - if (foundation.is_allowed_to_push(top_card)) { - update(from.bounding_box()); - - auto card = from.pop(); - - mark_intersecting_stacks_dirty(card); - foundation.push(card).release_value_but_fixme_should_propagate_errors(); - - Vector> moved_card; - moved_card.append(card); - remember_move_for_undo(from, foundation, moved_card); - - score_move(from, foundation); - - update(foundation.bounding_box()); - - card_was_moved = true; - break; - } - } - - if (card_was_moved) { - if (from.type() == CardStack::Type::Play) - pop_waste_to_play_stack(); - - start_timer_if_necessary(); - check_for_game_over(); - } - - return card_was_moved; -} - -void Game::auto_move_eligible_cards_to_foundations() -{ - while (true) { - bool card_was_moved = false; - for (auto& to_check : stacks()) { - if (to_check->type() != CardStack::Type::Normal && to_check->type() != CardStack::Type::Play) - continue; - - if (attempt_to_move_card_to_foundations(to_check)) - card_was_moved = true; - } - - if (!card_was_moved) - break; - } -} - -void Game::paint_event(GUI::PaintEvent& event) -{ - Gfx::Color background_color = this->background_color(); - - GUI::Frame::paint_event(event); - - GUI::Painter painter(*this); - painter.add_clip_rect(frame_inner_rect()); - painter.add_clip_rect(event.rect()); - - if (m_state == State::GameOverAnimation) { - m_animation.draw(painter); - return; - } - - if (is_moving_cards()) { - for (auto& card : moving_cards()) - card->clear(painter, background_color); - } - - for (auto& stack : stacks()) { - stack->paint(painter, background_color); - } - - if (is_moving_cards()) { - for (auto& card : moving_cards()) { - card->paint(painter); - card->save_old_position(); - } - } - - if (!m_mouse_down) { - if (is_moving_cards()) { - check_for_game_over(); - for (auto& card : moving_cards()) - card->set_moving(false); - } - - clear_moving_cards(); - } -} - -void Game::remember_move_for_undo(CardStack& from, CardStack& to, Vector> moved_cards) -{ - m_last_move.type = LastMove::Type::MoveCards; - m_last_move.from = &from; - m_last_move.cards = moved_cards; - m_last_move.to = &to; - if (on_undo_availability_change) - on_undo_availability_change(true); -} - -void Game::remember_flip_for_undo(Card& card) -{ - Vector> cards; - cards.append(card); - m_last_move.type = LastMove::Type::FlipCard; - m_last_move.cards = cards; - if (on_undo_availability_change) - on_undo_availability_change(true); -} - -void Game::perform_undo() -{ - if (m_last_move.type == LastMove::Type::Invalid) - return; - - if (m_last_move.type == LastMove::Type::FlipCard) { - m_last_move.cards[0]->set_upside_down(true); - if (on_undo_availability_change) - on_undo_availability_change(false); - invalidate_layout(); - score_flip(true); - return; - } - - if (m_last_move.from->type() == CardStack::Type::Play && m_mode == Mode::SingleCardDraw) { - auto& waste = stack_at_location(Waste); - if (!m_last_move.from->is_empty()) - waste.push(m_last_move.from->pop()).release_value_but_fixme_should_propagate_errors(); - } - - for (auto& to_intersect : m_last_move.cards) { - mark_intersecting_stacks_dirty(to_intersect); - m_last_move.from->push(to_intersect).release_value_but_fixme_should_propagate_errors(); - (void)m_last_move.to->pop(); - } - - if (m_last_move.from->type() == CardStack::Type::Stock) { - auto& waste = stack_at_location(Waste); - auto& play = stack_at_location(Play); - Vector> cards_popped; - for (size_t i = 0; i < m_last_move.cards.size(); i++) { - if (!waste.is_empty()) { - auto card = waste.pop(); - cards_popped.prepend(card); - } - } - for (auto& card : cards_popped) { - play.push(card).release_value_but_fixme_should_propagate_errors(); - } - } - - if (m_last_move.from->type() == CardStack::Type::Waste && m_last_move.to->type() == CardStack::Type::Stock) - pop_waste_to_play_stack(); - - score_move(*m_last_move.from, *m_last_move.to, true); - - m_last_move = {}; - if (on_undo_availability_change) - on_undo_availability_change(false); - invalidate_layout(); -} - -bool Game::can_solve() -{ - if (m_state != State::GameInProgress) - return false; - - for (auto const& stack : stacks()) { - switch (stack->type()) { - case Cards::CardStack::Type::Waste: - case Cards::CardStack::Type::Stock: - if (!stack->is_empty()) - return false; - break; - case Cards::CardStack::Type::Normal: - if (!stack->is_empty() && stack->stack().first()->is_upside_down()) - return false; - break; - default: - break; - } - } - - return true; -} - -void Game::start_solving() -{ - if (!can_solve()) - return; - - m_state = State::Solving; - start_timer(s_timer_solving_interval_ms); -} - -void Game::step_solve() -{ - for (auto& stack : stacks()) { - if (stack->type() != Cards::CardStack::Type::Normal) - continue; - - if (attempt_to_move_card_to_foundations(stack)) - break; - } -} - -void Game::clear_hovered_stack() -{ - if (!m_hovered_stack) - return; - - m_hovered_stack->set_highlighted(false); - update(m_hovered_stack->bounding_box()); - m_hovered_stack = nullptr; -} - -} diff --git a/Userland/Games/Solitaire/Game.h b/Userland/Games/Solitaire/Game.h deleted file mode 100644 index 140efa760e0..00000000000 --- a/Userland/Games/Solitaire/Game.h +++ /dev/null @@ -1,236 +0,0 @@ -/* - * Copyright (c) 2020, Till Mayer - * Copyright (c) 2021-2023, Sam Atkins - * Copyright (c) 2022, the SerenityOS developers. - * Copyright (c) 2023, David Ganz - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include - -using Cards::Card; -using Cards::CardStack; - -namespace Solitaire { - -enum class Mode : u8 { - SingleCardDraw, - ThreeCardDraw, - __Count -}; - -enum class GameOverReason { - Victory, - NewGame, -}; - -class Game final : public Cards::CardGame { - C_OBJECT_ABSTRACT(Game) -public: - static constexpr int width = 640; - static constexpr int height = 480; - - static ErrorOr> try_create(); - virtual ~Game() override = default; - - Mode mode() const { return m_mode; } - void setup(Mode); - void perform_undo(); - - bool is_auto_collecting() const { return m_auto_collect; } - void set_auto_collect(bool collect) { m_auto_collect = collect; } - - bool can_solve(); - void start_solving(); - - Function on_score_update; - Function on_game_start; - Function on_game_end; - Function on_undo_availability_change; - Function on_move; - -private: - Game(); - - class Animation { - public: - Animation() - { - } - - Animation(Cards::Suit suit, Cards::Rank rank, Gfx::IntPoint start_position, float gravity, int x_vel, float bouncyness) - : m_suit(suit) - , m_rank(rank) - , m_position(start_position) - , m_gravity(gravity) - , m_x_velocity(x_vel) - , m_bouncyness(bouncyness) - { - } - - Gfx::IntRect card_rect() const - { - return { - m_position.x(), m_position.y(), Card::width, Card::height - }; - } - - Gfx::IntPoint position() const { return m_position; } - - void draw(GUI::Painter& painter) - { - auto bitmap = Cards::CardPainter::the().card_front(m_suit, m_rank); - painter.blit(m_position, bitmap, bitmap->rect()); - m_dirty = false; - } - - bool tick() - { - // Don't move the animation card until the event loop has had a chance to paint its current location. - if (m_dirty) - return false; - - m_y_velocity += m_gravity; - - if (m_position.y() + Card::height + m_y_velocity > Game::height + 1 && m_y_velocity > 0) { - m_y_velocity = min((m_y_velocity * -m_bouncyness), -8.f); - m_position.set_y(Game::height - Card::height); - m_position.translate_by(m_x_velocity, 0); - } else { - m_position.translate_by(m_x_velocity, m_y_velocity); - } - - m_dirty = true; - return true; - } - - private: - Cards::Suit m_suit { Cards::Suit::Spades }; - Cards::Rank m_rank { Cards::Rank::Ace }; - Gfx::IntPoint m_position { 0, 0 }; - float m_gravity { 0 }; - int m_x_velocity { 0 }; - float m_y_velocity { 0 }; - float m_bouncyness { 0 }; - bool m_dirty { false }; - }; - - struct WasteRecycleRules { - uint8_t passes_allowed_before_punishment { 0 }; - int8_t punishment { 0 }; - }; - - struct LastMove { - enum class Type { - Invalid, - MoveCards, - FlipCard - }; - - Type type { Type::Invalid }; - CardStack* from { nullptr }; - Vector> cards; - CardStack* to { nullptr }; - }; - - enum StackLocation { - Stock, - Waste, - Play, - Foundation1, - Foundation2, - Foundation3, - Foundation4, - Pile1, - Pile2, - Pile3, - Pile4, - Pile5, - Pile6, - Pile7, - __Count - }; - static constexpr Array piles = { Pile1, Pile2, Pile3, Pile4, Pile5, Pile6, Pile7 }; - static constexpr Array foundations = { Foundation1, Foundation2, Foundation3, Foundation4 }; - - ALWAYS_INLINE WasteRecycleRules const& recycle_rules() - { - static constexpr Array rules { { - { 0, -100 }, - { 2, -20 }, - } }; - - switch (m_mode) { - case Mode::SingleCardDraw: - return rules[0]; - case Mode::ThreeCardDraw: - return rules[1]; - default: - VERIFY_NOT_REACHED(); - } - } - - void score_move(CardStack& from, CardStack& to, bool inverse = false); - void score_flip(bool inverse = false); - void remember_move_for_undo(CardStack& from, CardStack& to, Vector> moved_cards); - void remember_flip_for_undo(Card& card); - void update_score(int to_add); - void draw_cards(); - void pop_waste_to_play_stack(); - bool attempt_to_move_card_to_foundations(CardStack& from); - void auto_move_eligible_cards_to_foundations(); - void start_timer_if_necessary(); - void start_game_over_animation(); - void stop_game_over_animation(); - void create_new_animation_card(); - void set_background_fill_enabled(bool); - void check_for_game_over(); - void clear_hovered_stack(); - void deal_next_card(); - void step_solve(); - - virtual void paint_event(GUI::PaintEvent&) override; - virtual void mousedown_event(GUI::MouseEvent&) override; - virtual void mouseup_event(GUI::MouseEvent&) override; - virtual void mousemove_event(GUI::MouseEvent&) override; - virtual void doubleclick_event(GUI::MouseEvent&) override; - virtual void keydown_event(GUI::KeyEvent&) override; - virtual void timer_event(Core::TimerEvent&) override; - - Mode m_mode { Mode::SingleCardDraw }; - - LastMove m_last_move; - Vector> m_new_deck; - Gfx::IntPoint m_mouse_down_location; - - bool m_mouse_down { false }; - - enum class State { - WaitingForNewGame, - NewGameAnimation, - GameInProgress, - StartGameOverAnimationNextFrame, - GameOverAnimation, - Solving, - }; - State m_state { State::WaitingForNewGame }; - - Animation m_animation; - uint8_t m_new_game_animation_pile { 0 }; - uint8_t m_new_game_animation_delay { 0 }; - - uint32_t m_score { 0 }; - uint8_t m_passes_left_before_punishment { 0 }; - - bool m_auto_collect { false }; - - RefPtr m_hovered_stack; -}; - -} diff --git a/Userland/Games/Solitaire/MainWidget.h b/Userland/Games/Solitaire/MainWidget.h deleted file mode 100644 index 8b8b61b5aa3..00000000000 --- a/Userland/Games/Solitaire/MainWidget.h +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright (c) 2023, the SerenityOS developers - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include - -namespace Solitaire { - -class MainWidget : public GUI::Widget { - C_OBJECT_ABSTRACT(MainWidget) -public: - static ErrorOr> try_create(); - virtual ~MainWidget() override = default; - -private: - MainWidget() = default; -}; - -} diff --git a/Userland/Games/Solitaire/Solitaire.gml b/Userland/Games/Solitaire/Solitaire.gml deleted file mode 100644 index 0e13de342f6..00000000000 --- a/Userland/Games/Solitaire/Solitaire.gml +++ /dev/null @@ -1,33 +0,0 @@ -@Solitaire::MainWidget { - fill_with_background_color: true - layout: @GUI::VerticalBoxLayout {} - - @Solitaire::Game { - name: "game" - fill_with_background_color: true - } - - @GUI::Frame { - name: "game_action_bar" - fill_with_background_color: true - fixed_height: 32 - layout: @GUI::HorizontalBoxLayout { - margins: [3] - } - - @GUI::Layout::Spacer {} - - @GUI::Button { - name: "solve_button" - text: "Solve" - fixed_width: 80 - } - - @GUI::Layout::Spacer {} - } - - @GUI::Statusbar { - name: "statusbar" - segment_count: 3 - } -} diff --git a/Userland/Games/Solitaire/main.cpp b/Userland/Games/Solitaire/main.cpp deleted file mode 100644 index de21814993b..00000000000 --- a/Userland/Games/Solitaire/main.cpp +++ /dev/null @@ -1,266 +0,0 @@ -/* - * Copyright (c) 2020, Till Mayer - * Copyright (c) 2021, the SerenityOS developers. - * Copyright (c) 2022-2023, Sam Atkins - * Copyright (c) 2023, David Ganz - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "Game.h" -#include "MainWidget.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -ErrorOr serenity_main(Main::Arguments arguments) -{ - TRY(Core::System::pledge("stdio recvfd sendfd rpath unix proc exec")); - - auto app = TRY(GUI::Application::create(arguments)); - auto app_icon = TRY(GUI::Icon::try_create_default_icon("app-solitaire"sv)); - - auto const man_file = "/usr/share/man/man6/Solitaire.md"sv; - - TRY(Desktop::Launcher::add_allowed_handler_with_only_specific_urls("/bin/Help", { URL::create_with_file_scheme(man_file) })); - TRY(Desktop::Launcher::seal_allowlist()); - - Config::pledge_domains({ "Games", "Solitaire" }); - Config::monitor_domain("Games"); - - TRY(Core::System::pledge("stdio recvfd sendfd rpath proc exec")); - - TRY(Core::System::unveil("/res", "r")); - TRY(Core::System::unveil("/bin/GamesSettings", "x")); - TRY(Core::System::unveil(nullptr, nullptr)); - - auto window = GUI::Window::construct(); - window->set_title("Solitaire"); - - auto mode = static_cast(Config::read_u32("Solitaire"sv, "Settings"sv, "Mode"sv, to_underlying(Solitaire::Mode::SingleCardDraw))); - - auto update_mode = [&](Solitaire::Mode new_mode) { - mode = new_mode; - Config::write_u32("Solitaire"sv, "Settings"sv, "Mode"sv, to_underlying(mode)); - }; - - auto high_score = [&]() { - switch (mode) { - case Solitaire::Mode::SingleCardDraw: - return Config::read_u32("Solitaire"sv, "HighScores"sv, "SingleCardDraw"sv, 0); - case Solitaire::Mode::ThreeCardDraw: - return Config::read_u32("Solitaire"sv, "HighScores"sv, "ThreeCardDraw"sv, 0); - default: - VERIFY_NOT_REACHED(); - } - }; - - auto update_high_score = [&](u32 new_high_score) { - switch (mode) { - case Solitaire::Mode::SingleCardDraw: - Config::write_u32("Solitaire"sv, "HighScores"sv, "SingleCardDraw"sv, new_high_score); - break; - case Solitaire::Mode::ThreeCardDraw: - Config::write_u32("Solitaire"sv, "HighScores"sv, "ThreeCardDraw"sv, new_high_score); - break; - default: - VERIFY_NOT_REACHED(); - } - }; - - if (mode >= Solitaire::Mode::__Count) - update_mode(Solitaire::Mode::SingleCardDraw); - - auto widget = TRY(Solitaire::MainWidget::try_create()); - window->set_main_widget(widget); - - auto& game = *widget->find_descendant_of_type_named("game"); - game.set_focus(true); - - auto& action_bar = *widget->find_descendant_of_type_named("game_action_bar"); - action_bar.set_background_color(game.background_color()); - action_bar.set_visible(false); - - auto& solve_button = *action_bar.find_descendant_of_type_named("solve_button"); - solve_button.on_click = [&](auto) { - game.start_solving(); - solve_button.set_enabled(false); - }; - solve_button.set_enabled(false); - - auto& statusbar = *widget->find_descendant_of_type_named("statusbar"); - statusbar.set_text(0, "Score: 0"_string); - statusbar.set_text(1, TRY(String::formatted("High Score: {}", high_score()))); - statusbar.set_text(2, "Time: 00:00"_string); - - app->on_action_enter = [&](GUI::Action& action) { - statusbar.set_override_text(action.status_tip()); - }; - - app->on_action_leave = [&](GUI::Action&) { - statusbar.set_override_text({}); - }; - - game.on_score_update = [&](uint32_t score) { - statusbar.set_text(0, String::formatted("Score: {}", score).release_value_but_fixme_should_propagate_errors()); - }; - - uint64_t seconds_elapsed = 0; - - auto timer = Core::Timer::create_repeating(1000, [&]() { - ++seconds_elapsed; - statusbar.set_text(2, String::formatted("Time: {}", human_readable_digital_time(seconds_elapsed)).release_value_but_fixme_should_propagate_errors()); - }); - - game.on_game_start = [&]() { - seconds_elapsed = 0; - timer->start(); - statusbar.set_text(2, "Time: 00:00"_string); - }; - game.on_move = [&]() { - solve_button.set_enabled(true); - action_bar.set_visible(game.can_solve()); - }; - game.on_game_end = [&](Solitaire::GameOverReason reason, uint32_t score) { - if (timer->is_active()) - timer->stop(); - - solve_button.set_enabled(false); - action_bar.set_visible(false); - - if (reason == Solitaire::GameOverReason::Victory) { - if (seconds_elapsed >= 30) { - uint32_t bonus = (20'000 / seconds_elapsed) * 35; - statusbar.set_text(0, String::formatted("Score: {} (Bonus: {})", score, bonus).release_value_but_fixme_should_propagate_errors()); - score += bonus; - } - - if (score > high_score()) { - update_high_score(score); - statusbar.set_text(1, String::formatted("High Score: {}", score).release_value_but_fixme_should_propagate_errors()); - } - } - statusbar.set_text(2, "Timer starts after your first move"_string); - }; - - auto confirm_end_current_game = [&]() { - auto game_in_progress = timer->is_active(); - if (game_in_progress) { - auto result = GUI::MessageBox::show(window, - "A game is still in progress, are you sure you would like to end it?"sv, - "Game in progress"sv, - GUI::MessageBox::Type::Warning, - GUI::MessageBox::InputType::YesNo); - - return result == GUI::MessageBox::ExecResult::Yes; - } - - return true; - }; - - window->on_close_request = [&]() { - if (confirm_end_current_game()) - return GUI::Window::CloseRequestDecision::Close; - return GUI::Window::CloseRequestDecision::StayOpen; - }; - - GUI::ActionGroup draw_setting_actions; - draw_setting_actions.set_exclusive(true); - - auto single_card_draw_action = GUI::Action::create_checkable("&Single Card Draw", [&](auto&) { - update_mode(Solitaire::Mode::SingleCardDraw); - - if (!confirm_end_current_game()) - return; - - statusbar.set_text(1, String::formatted("High Score: {}", high_score()).release_value_but_fixme_should_propagate_errors()); - game.setup(mode); - }); - single_card_draw_action->set_checked(mode == Solitaire::Mode::SingleCardDraw); - single_card_draw_action->set_status_tip("Draw one card at a time"_string); - draw_setting_actions.add_action(single_card_draw_action); - - auto three_card_draw_action = GUI::Action::create_checkable("&Three Card Draw", [&](auto&) { - update_mode(Solitaire::Mode::ThreeCardDraw); - - if (!confirm_end_current_game()) - return; - - statusbar.set_text(1, String::formatted("High Score: {}", high_score()).release_value_but_fixme_should_propagate_errors()); - game.setup(mode); - }); - three_card_draw_action->set_checked(mode == Solitaire::Mode::ThreeCardDraw); - three_card_draw_action->set_status_tip("Draw three cards at a time"_string); - draw_setting_actions.add_action(three_card_draw_action); - - game.set_auto_collect(Config::read_bool("Solitaire"sv, "Settings"sv, "AutoCollect"sv, false)); - auto toggle_auto_collect_action = GUI::Action::create_checkable("Auto-&Collect", [&](auto& action) { - auto checked = action.is_checked(); - game.set_auto_collect(checked); - Config::write_bool("Solitaire"sv, "Settings"sv, "AutoCollect"sv, checked); - }); - toggle_auto_collect_action->set_checked(game.is_auto_collecting()); - toggle_auto_collect_action->set_status_tip("Auto-collect to foundation piles"_string); - - auto game_menu = window->add_menu("&Game"_string); - - game_menu->add_action(GUI::Action::create("&New Game", { Mod_None, Key_F2 }, TRY(Gfx::Bitmap::load_from_file("/res/icons/16x16/reload.png"sv)), [&](auto&) { - if (!confirm_end_current_game()) - return; - - game.setup(mode); - })); - game_menu->add_separator(); - auto undo_action = GUI::CommonActions::make_undo_action([&](auto&) { - game.perform_undo(); - }); - undo_action->set_enabled(false); - game_menu->add_action(undo_action); - game_menu->add_separator(); - game_menu->add_action(TRY(Cards::make_cards_settings_action(window))); - game_menu->add_action(single_card_draw_action); - game_menu->add_action(three_card_draw_action); - game_menu->add_separator(); - game_menu->add_action(toggle_auto_collect_action); - game_menu->add_separator(); - game_menu->add_action(GUI::CommonActions::make_quit_action([&](auto&) { app->quit(); })); - - auto view_menu = window->add_menu("&View"_string); - view_menu->add_action(GUI::CommonActions::make_fullscreen_action([&](auto&) { - window->set_fullscreen(!window->is_fullscreen()); - })); - - auto help_menu = window->add_menu("&Help"_string); - help_menu->add_action(GUI::CommonActions::make_command_palette_action(window)); - help_menu->add_action(GUI::CommonActions::make_help_action([&man_file](auto&) { - Desktop::Launcher::open(URL::create_with_file_scheme(man_file), "/bin/Help"); - })); - help_menu->add_action(GUI::CommonActions::make_about_action("Solitaire"_string, app_icon, window)); - - window->set_resizable(false); - window->resize(Solitaire::Game::width, Solitaire::Game::height + statusbar.max_height().as_int() + action_bar.height()); - window->set_icon(app_icon.bitmap_for_size(16)); - window->show(); - - game.on_undo_availability_change = [&](bool undo_available) { - undo_action->set_enabled(undo_available); - }; - - game.setup(mode); - - return app->exec(); -} diff --git a/Userland/Games/Spider/CMakeLists.txt b/Userland/Games/Spider/CMakeLists.txt deleted file mode 100644 index 5fc369f400d..00000000000 --- a/Userland/Games/Spider/CMakeLists.txt +++ /dev/null @@ -1,16 +0,0 @@ -serenity_component( - Spider - RECOMMENDED - TARGETS Spider -) - -compile_gml(Spider.gml SpiderGML.cpp spider_gml) - -set(SOURCES - Game.cpp - main.cpp - SpiderGML.cpp -) - -serenity_app(Spider ICON app-spider) -target_link_libraries(Spider PRIVATE LibCards LibGUI LibGfx LibCore LibDesktop LibConfig LibMain LibURL) diff --git a/Userland/Games/Spider/Game.cpp b/Userland/Games/Spider/Game.cpp deleted file mode 100644 index 17b6cc79ce7..00000000000 --- a/Userland/Games/Spider/Game.cpp +++ /dev/null @@ -1,476 +0,0 @@ -/* - * Copyright (c) 2021, Jamie Mansfield - * Copyright (c) 2022, Jonas Höpner - * Copyright (c) 2022, the SerenityOS developers. - * Copyright (c) 2023, David Ganz - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "Game.h" -#include -#include -#include -#include - -REGISTER_WIDGET(Spider, Game); - -namespace Spider { - -static constexpr uint8_t new_game_animation_delay = 2; -static constexpr uint8_t draw_animation_delay = 2; -static constexpr int s_timer_interval_ms = 1000 / 60; - -ErrorOr> Game::try_create() -{ - auto game = TRY(adopt_nonnull_ref_or_enomem(new (nothrow) Game())); - - TRY(game->add_stack(Gfx::IntPoint { 10, Game::height - Card::height - 10 }, CardStack::Type::Waste)); - TRY(game->add_stack(Gfx::IntPoint { Game::width - Card::width - 10, Game::height - Card::height - 10 }, CardStack::Type::Stock)); - - for (int i = 0; i < 10; i++) { - TRY(game->add_stack(Gfx::IntPoint { 10 + i * (Card::width + 10), 10 }, CardStack::Type::Normal)); - } - - return game; -} - -Game::Game() = default; - -void Game::setup(Mode mode) -{ - if (m_state == State::NewGameAnimation) - stop_timer(); - - m_mode = mode; - - if (on_undo_availability_change) - on_undo_availability_change(false); - - if (on_game_end) - on_game_end(GameOverReason::NewGame, m_score); - - for (auto& stack : stacks()) - stack->clear(); - - m_new_game_animation_pile = 0; - - m_score = 500; - update_score(0); - - unsigned heart_suits = 0; - unsigned spade_suits = 0; - - switch (m_mode) { - case Mode::SingleSuit: - spade_suits = 8; - heart_suits = 0; - break; - case Mode::TwoSuit: - spade_suits = 4; - heart_suits = 4; - break; - default: - VERIFY_NOT_REACHED(); - break; - } - - m_new_deck = Cards::create_deck(0, 0, heart_suits, spade_suits, Cards::Shuffle::Yes).release_value_but_fixme_should_propagate_errors(); - - clear_moving_cards(); - - m_state = State::NewGameAnimation; - start_timer(s_timer_interval_ms); - update(); -} - -void Game::perform_undo() -{ - if (m_last_move.type == LastMove::Type::Invalid) - return; - - if (!m_last_move.was_visible) - m_last_move.from->peek().set_upside_down(true); - - Vector> cards; - for (size_t i = 0; i < m_last_move.card_count; i++) - cards.append(m_last_move.to->pop()); - for (ssize_t i = m_last_move.card_count - 1; i >= 0; i--) - m_last_move.from->push(cards[i]).release_value_but_fixme_should_propagate_errors(); - - update_score(-1); - - m_last_move = {}; - if (on_undo_availability_change) - on_undo_availability_change(false); - - update_disabled_cards(); - invalidate_layout(); -} - -void Game::start_timer_if_necessary() -{ - if (on_game_start && m_state == State::WaitingForNewGame) { - on_game_start(); - m_state = State::GameInProgress; - } -} - -void Game::update_score(int delta) -{ - m_score = max(static_cast(m_score) + delta, 0); - - if (on_score_update) - on_score_update(m_score); -} - -void Game::draw_cards() -{ - // draw a single card from the stock for each pile - auto& stock_pile = stack_at_location(Stock); - if (stock_pile.is_empty()) - return; - - update_score(-1); - - m_state = State::DrawAnimation; - m_original_stock_rect = stock_pile.bounding_box(); - start_timer(s_timer_interval_ms); -} - -void Game::detect_full_stacks() -{ - auto& completed_stack = stack_at_location(Completed); - for (auto pile : piles) { - auto& current_pile = stack_at_location(pile); - - bool started = false; - uint8_t last_value; - Color color; - for (size_t i = current_pile.stack().size(); i > 0; i--) { - auto card = current_pile.stack().at(i - 1); - if (card->is_upside_down()) - break; - - if (!started) { - if (card->rank() != Cards::Rank::Ace) - break; - - started = true; - color = card->color(); - } else if (to_underlying(card->rank()) != last_value + 1 || card->color() != color) { - break; - } else if (card->rank() == Cards::Rank::King) { - // we have a full set - auto original_current_rect = current_pile.bounding_box(); - - for (size_t j = 0; j < Card::card_count; j++) { - completed_stack.push(current_pile.pop()).release_value_but_fixme_should_propagate_errors(); - } - - update(original_current_rect); - update(completed_stack.bounding_box()); - - if (current_pile.make_top_card_visible()) - update(current_pile.peek().rect()); - - update_score(101); - - if (on_undo_availability_change) - on_undo_availability_change(false); - } - - last_value = to_underlying(card->rank()); - } - } - - update_disabled_cards(); - detect_victory(); -} - -void Game::detect_victory() -{ - for (auto pile : piles) { - auto& current_pile = stack_at_location(pile); - - if (!current_pile.is_empty()) - return; - } - - if (on_undo_availability_change) - on_undo_availability_change(false); - - if (on_game_end) - on_game_end(GameOverReason::Victory, m_score); -} - -void Game::paint_event(GUI::PaintEvent& event) -{ - Gfx::Color background_color = this->background_color(); - - GUI::Frame::paint_event(event); - - GUI::Painter painter(*this); - painter.add_clip_rect(frame_inner_rect()); - painter.add_clip_rect(event.rect()); - - if (is_moving_cards()) { - for (auto& card : moving_cards()) - card->clear(painter, background_color); - } - - for (auto& stack : stacks()) { - stack->paint(painter, background_color); - } - - if (is_moving_cards()) { - for (auto& card : moving_cards()) { - card->paint(painter); - card->save_old_position(); - } - } - - if (!m_mouse_down) { - if (is_moving_cards()) { - for (auto& card : moving_cards()) - card->set_moving(false); - } - clear_moving_cards(); - } -} - -void Game::remember_move_for_undo(CardStack& from, CardStack& to, size_t card_count, bool was_visible) -{ - m_last_move.type = LastMove::Type::MoveCards; - m_last_move.from = &from; - m_last_move.card_count = card_count; - m_last_move.to = &to; - m_last_move.was_visible = was_visible; - if (on_undo_availability_change) - on_undo_availability_change(true); -} - -void Game::mousedown_event(GUI::MouseEvent& event) -{ - GUI::Frame::mousedown_event(event); - - if (m_state == State::NewGameAnimation || m_state == State::DrawAnimation) - return; - - auto click_location = event.position(); - for (auto& to_check : stacks()) { - if (to_check->type() == CardStack::Type::Waste) - continue; - - if (to_check->bounding_box().contains(click_location)) { - if (to_check->type() == CardStack::Type::Stock) { - start_timer_if_necessary(); - draw_cards(); - } else if (!to_check->is_empty()) { - auto& top_card = to_check->peek(); - - if (top_card.is_upside_down()) { - if (top_card.rect().contains(click_location)) { - top_card.set_upside_down(false); - start_timer_if_necessary(); - update(top_card.rect()); - } - } else if (!is_moving_cards()) { - pick_up_cards_from_stack(to_check, click_location, Cards::CardStack::MovementRule::Same).release_value_but_fixme_should_propagate_errors(); - m_mouse_down_location = click_location; - // When the user wants to automatically move cards, do not go into the drag mode. - if (event.button() != GUI::MouseButton::Secondary) - m_mouse_down = true; - start_timer_if_necessary(); - } - } - break; - } - } -} - -void Game::move_focused_cards(CardStack& stack) -{ - auto card_count = moving_cards().size(); - drop_cards_on_stack(stack, Cards::CardStack::MovementRule::Any).release_value_but_fixme_should_propagate_errors(); - bool was_visible = moving_cards_source_stack()->is_empty() || !moving_cards_source_stack()->peek().is_upside_down(); - remember_move_for_undo(*moving_cards_source_stack(), stack, card_count, was_visible); - update_score(-1); - moving_cards_source_stack()->make_top_card_visible(); - detect_full_stacks(); -} - -void Game::mouseup_event(GUI::MouseEvent& event) -{ - GUI::Frame::mouseup_event(event); - clear_hovered_stack(); - - if (!is_moving_cards() || m_state == State::NewGameAnimation || m_state == State::DrawAnimation) - return; - - bool rebound = true; - if (event.button() == GUI::MouseButton::Secondary) { - // This enables the game to move the focused cards to the first possible stack excluding empty stacks. - // NOTE: This ignores empty stacks, as the game has no undo button, and a card, which has been moved to an empty stack without any other possibilities is not reversible. - for (auto& stack : stacks()) { - if (stack == moving_cards_source_stack()) - continue; - - if (stack->is_allowed_to_push(moving_cards().at(0), moving_cards().size(), Cards::CardStack::MovementRule::Any) && !stack->is_empty()) { - move_focused_cards(stack); - - rebound = false; - break; - } - } - } else if (auto target_stack = find_stack_to_drop_on(Cards::CardStack::MovementRule::Any); !target_stack.is_null()) { - auto& stack = *target_stack; - move_focused_cards(stack); - rebound = false; - } - - if (rebound) { - for (auto& to_intersect : moving_cards()) - mark_intersecting_stacks_dirty(to_intersect); - - moving_cards_source_stack()->rebound_cards(); - update(moving_cards_source_stack()->bounding_box()); - } - - update_disabled_cards(); - m_mouse_down = false; -} - -void Game::mousemove_event(GUI::MouseEvent& event) -{ - GUI::Frame::mousemove_event(event); - - if (!m_mouse_down || m_state == State::NewGameAnimation || m_state == State::DrawAnimation) - return; - - auto click_location = event.position(); - int dx = click_location.dx_relative_to(m_mouse_down_location); - int dy = click_location.dy_relative_to(m_mouse_down_location); - - if (auto target_stack = find_stack_to_drop_on(Cards::CardStack::MovementRule::Any)) { - if (target_stack != m_hovered_stack) { - clear_hovered_stack(); - - m_hovered_stack = move(target_stack); - m_hovered_stack->set_highlighted(true); - update(m_hovered_stack->bounding_box()); - } - } else { - clear_hovered_stack(); - } - - for (auto& to_intersect : moving_cards()) { - mark_intersecting_stacks_dirty(to_intersect); - to_intersect->rect().translate_by(dx, dy); - update(to_intersect->rect()); - } - - m_mouse_down_location = click_location; -} - -void Game::doubleclick_event(GUI::MouseEvent& event) -{ - GUI::Frame::doubleclick_event(event); - - if (m_state == State::NewGameAnimation) { - while (m_state == State::NewGameAnimation) - deal_next_card(); - return; - } -} - -void Game::deal_next_card() -{ - auto& current_pile = stack_at_location(piles.at(m_new_game_animation_pile)); - - // for first 4 piles, draw 6 cards - // for last 6 piles, draw 5 cards - size_t cards_to_draw = m_new_game_animation_pile < 4 ? 6 : 5; - - if (current_pile.count() < (cards_to_draw - 1)) { - auto card = m_new_deck.take_last(); - card->set_upside_down(true); - current_pile.push(card).release_value_but_fixme_should_propagate_errors(); - } else { - current_pile.push(m_new_deck.take_last()).release_value_but_fixme_should_propagate_errors(); - ++m_new_game_animation_pile; - } - - update(current_pile.bounding_box()); - - if (m_new_game_animation_pile == piles.size()) { - VERIFY(m_new_deck.size() == 50); - - auto& stock_pile = stack_at_location(Stock); - while (!m_new_deck.is_empty()) - stock_pile.push(m_new_deck.take_last()).release_value_but_fixme_should_propagate_errors(); - - update(stock_pile.bounding_box()); - update_disabled_cards(); - - m_state = State::WaitingForNewGame; - stop_timer(); - } -} - -void Game::update_disabled_cards() -{ - for (auto& stack : stacks()) { - if (stack->type() != CardStack::Type::Normal) - continue; - stack->update_disabled_cards(CardStack::MovementRule::Same); - update(stack->bounding_box()); - } -} - -void Game::timer_event(Core::TimerEvent&) -{ - if (m_state == State::NewGameAnimation) { - if (m_new_game_animation_delay < new_game_animation_delay) { - ++m_new_game_animation_delay; - } else { - m_new_game_animation_delay = 0; - deal_next_card(); - } - } else if (m_state == State::DrawAnimation) { - if (m_draw_animation_delay < draw_animation_delay) { - ++m_draw_animation_delay; - } else { - auto& stock_pile = stack_at_location(Stock); - auto& current_pile = stack_at_location(piles.at(m_draw_animation_pile)); - auto card = stock_pile.pop(); - card->set_upside_down(false); - current_pile.push(card).release_value_but_fixme_should_propagate_errors(); - update(current_pile.bounding_box()); - ++m_draw_animation_pile; - - if (m_draw_animation_pile == piles.size()) { - update_disabled_cards(); - update(m_original_stock_rect); - detect_full_stacks(); - - m_state = State::GameInProgress; - m_draw_animation_delay = 0; - m_draw_animation_pile = 0; - stop_timer(); - } - } - } -} - -void Game::clear_hovered_stack() -{ - if (!m_hovered_stack) - return; - - m_hovered_stack->set_highlighted(false); - update(m_hovered_stack->bounding_box()); - m_hovered_stack = nullptr; -} - -} diff --git a/Userland/Games/Spider/Game.h b/Userland/Games/Spider/Game.h deleted file mode 100644 index 25e33386424..00000000000 --- a/Userland/Games/Spider/Game.h +++ /dev/null @@ -1,134 +0,0 @@ -/* - * Copyright (c) 2021, Jamie Mansfield - * Copyright (c) 2022, the SerenityOS developers. - * Copyright (c) 2022, Sam Atkins - * Copyright (c) 2023, David Ganz - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include - -using Cards::Card; -using Cards::CardStack; - -namespace Spider { - -enum class Mode : u8 { - SingleSuit, - TwoSuit, - __Count -}; - -enum class GameOverReason { - Victory, - NewGame, - Quit, -}; - -class Game final : public Cards::CardGame { - C_OBJECT_ABSTRACT(Game) -public: - static constexpr int width = 10 + 10 * Card::width + 90 + 10; - static constexpr int height = 480; - - static ErrorOr> try_create(); - ~Game() override = default; - - Mode mode() const { return m_mode; } - void setup(Mode); - - void perform_undo(); - - Function on_score_update; - Function on_game_start; - Function on_game_end; - Function on_undo_availability_change; - -private: - Game(); - - struct LastMove { - enum class Type { - Invalid, - MoveCards - }; - - Type type { Type::Invalid }; - CardStack* from { nullptr }; - size_t card_count; - bool was_visible; - CardStack* to { nullptr }; - }; - - enum StackLocation { - Completed, - Stock, - Pile1, - Pile2, - Pile3, - Pile4, - Pile5, - Pile6, - Pile7, - Pile8, - Pile9, - Pile10, - __Count - }; - static constexpr Array piles = { - Pile1, Pile2, Pile3, Pile4, Pile5, - Pile6, Pile7, Pile8, Pile9, Pile10 - }; - - void start_timer_if_necessary(); - void remember_move_for_undo(CardStack&, CardStack&, size_t, bool); - void update_score(int delta); - void draw_cards(); - void detect_full_stacks(); - void detect_victory(); - void move_focused_cards(CardStack& stack); - void clear_hovered_stack(); - void deal_next_card(); - void update_disabled_cards(); - - virtual void paint_event(GUI::PaintEvent&) override; - virtual void mousedown_event(GUI::MouseEvent&) override; - virtual void mouseup_event(GUI::MouseEvent&) override; - virtual void mousemove_event(GUI::MouseEvent&) override; - virtual void doubleclick_event(GUI::MouseEvent&) override; - virtual void timer_event(Core::TimerEvent&) override; - - Mode m_mode { Mode::SingleSuit }; - - LastMove m_last_move; - Vector> m_new_deck; - Gfx::IntPoint m_mouse_down_location; - - bool m_mouse_down { false }; - - enum class State { - WaitingForNewGame, - NewGameAnimation, - GameInProgress, - DrawAnimation, - Victory, - }; - State m_state { State::WaitingForNewGame }; - uint8_t m_new_game_animation_delay { 0 }; - uint8_t m_new_game_animation_pile { 0 }; - - uint8_t m_draw_animation_delay { 0 }; - uint8_t m_draw_animation_pile { 0 }; - Gfx::IntRect m_original_stock_rect; - - uint32_t m_score { 500 }; - - RefPtr m_hovered_stack; -}; - -} diff --git a/Userland/Games/Spider/MainWidget.h b/Userland/Games/Spider/MainWidget.h deleted file mode 100644 index af4e0d85b60..00000000000 --- a/Userland/Games/Spider/MainWidget.h +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright (c) 2023, the SerenityOS developers - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include - -namespace Spider { - -class MainWidget : public GUI::Widget { - C_OBJECT_ABSTRACT(MainWidget) -public: - static ErrorOr> try_create(); - virtual ~MainWidget() override = default; - -private: - MainWidget() = default; -}; - -} diff --git a/Userland/Games/Spider/Spider.gml b/Userland/Games/Spider/Spider.gml deleted file mode 100644 index 33e17ffcbf4..00000000000 --- a/Userland/Games/Spider/Spider.gml +++ /dev/null @@ -1,14 +0,0 @@ -@Spider::MainWidget { - fill_with_background_color: true - layout: @GUI::VerticalBoxLayout {} - - @Spider::Game { - name: "game" - fill_with_background_color: true - } - - @GUI::Statusbar { - name: "statusbar" - segment_count: 3 - } -} diff --git a/Userland/Games/Spider/main.cpp b/Userland/Games/Spider/main.cpp deleted file mode 100644 index 7160a09746c..00000000000 --- a/Userland/Games/Spider/main.cpp +++ /dev/null @@ -1,311 +0,0 @@ -/* - * Copyright (c) 2021, Jamie Mansfield - * Copyright (c) 2021, Mustafa Quraish - * Copyright (c) 2022-2023, Sam Atkins - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "Game.h" -#include "MainWidget.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -enum class StatisticDisplay : u8 { - HighScore, - BestTime, - __Count -}; - -ErrorOr serenity_main(Main::Arguments arguments) -{ - TRY(Core::System::pledge("stdio recvfd sendfd rpath unix proc exec")); - - auto app = TRY(GUI::Application::create(arguments)); - auto app_icon = TRY(GUI::Icon::try_create_default_icon("app-spider"sv)); - - Config::pledge_domains({ "Games", "Spider" }); - Config::monitor_domain("Games"); - - auto const man_file = "/usr/share/man/man6/Spider.md"; - - TRY(Desktop::Launcher::add_allowed_handler_with_only_specific_urls("/bin/Help", { URL::create_with_file_scheme(man_file) })); - TRY(Desktop::Launcher::seal_allowlist()); - - TRY(Core::System::pledge("stdio recvfd sendfd rpath proc exec")); - - TRY(Core::System::unveil("/res", "r")); - TRY(Core::System::unveil("/bin/GamesSettings", "x")); - TRY(Core::System::unveil(nullptr, nullptr)); - - auto window = GUI::Window::construct(); - window->set_title("Spider"); - - auto mode = static_cast(Config::read_u32("Spider"sv, "Settings"sv, "Mode"sv, to_underlying(Spider::Mode::SingleSuit))); - - auto update_mode = [&](Spider::Mode new_mode) { - mode = new_mode; - Config::write_u32("Spider"sv, "Settings"sv, "Mode"sv, to_underlying(mode)); - }; - - auto mode_id = [&]() { - switch (mode) { - case Spider::Mode::SingleSuit: - return "SingleSuit"sv; - case Spider::Mode::TwoSuit: - return "TwoSuit"sv; - default: - VERIFY_NOT_REACHED(); - } - }; - - auto statistic_display = static_cast(Config::read_u32("Spider"sv, "Settings"sv, "StatisticDisplay"sv, to_underlying(StatisticDisplay::HighScore))); - auto update_statistic_display = [&](StatisticDisplay new_statistic_display) { - statistic_display = new_statistic_display; - Config::write_u32("Spider"sv, "Settings"sv, "StatisticDisplay"sv, to_underlying(statistic_display)); - }; - - auto high_score = [&]() { - return Config::read_u32("Spider"sv, "HighScores"sv, mode_id(), 0); - }; - - auto update_high_score = [&](u32 new_high_score) { - Config::write_u32("Spider"sv, "HighScores"sv, mode_id(), new_high_score); - }; - - auto best_time = [&]() { - return Config::read_u32("Spider"sv, "BestTimes"sv, mode_id(), 0); - }; - - auto update_best_time = [&](u32 new_best_time) { - Config::write_u32("Spider"sv, "BestTimes"sv, mode_id(), new_best_time); - }; - - auto total_wins = [&]() { - return Config::read_u32("Spider"sv, "TotalWins"sv, mode_id(), 0); - }; - auto increment_total_wins = [&]() { - Config::write_u32("Spider"sv, "TotalWins"sv, mode_id(), total_wins() + 1); - }; - - auto total_losses = [&]() { - return Config::read_u32("Spider"sv, "TotalLosses"sv, mode_id(), 0); - }; - auto increment_total_losses = [&]() { - Config::write_u32("Spider"sv, "TotalLosses"sv, mode_id(), total_losses() + 1); - }; - - if (mode >= Spider::Mode::__Count) - update_mode(Spider::Mode::SingleSuit); - - if (statistic_display >= StatisticDisplay::__Count) - update_statistic_display(StatisticDisplay::HighScore); - - auto widget = TRY(Spider::MainWidget::try_create()); - window->set_main_widget(widget); - - auto& game = *widget->find_descendant_of_type_named("game"); - game.set_focus(true); - - auto& statusbar = *widget->find_descendant_of_type_named("statusbar"); - auto reset_statistic_status = [&]() { - switch (statistic_display) { - case StatisticDisplay::HighScore: - statusbar.set_text(1, String::formatted("High Score: {}", high_score()).release_value_but_fixme_should_propagate_errors()); - break; - case StatisticDisplay::BestTime: - statusbar.set_text(1, String::formatted("Best Time: {}", human_readable_digital_time(best_time())).release_value_but_fixme_should_propagate_errors()); - break; - default: - VERIFY_NOT_REACHED(); - } - }; - - statusbar.set_text(0, "Score: 0"_string); - reset_statistic_status(); - statusbar.set_text(2, "Time: 00:00:00"_string); - - app->on_action_enter = [&](GUI::Action& action) { - statusbar.set_override_text(action.status_tip()); - }; - - app->on_action_leave = [&](GUI::Action&) { - statusbar.set_override_text({}); - }; - - game.on_score_update = [&](uint32_t score) { - statusbar.set_text(0, String::formatted("Score: {}", score).release_value_but_fixme_should_propagate_errors()); - }; - - uint64_t seconds_elapsed = 0; - - auto timer = Core::Timer::create_repeating(1000, [&]() { - ++seconds_elapsed; - - statusbar.set_text(2, String::formatted("Time: {}", human_readable_digital_time(seconds_elapsed)).release_value_but_fixme_should_propagate_errors()); - }); - - game.on_game_start = [&]() { - seconds_elapsed = 0; - timer->start(); - statusbar.set_text(2, "Time: 00:00:00"_string); - }; - game.on_game_end = [&](Spider::GameOverReason reason, uint32_t score) { - auto game_was_in_progress = timer->is_active(); - if (game_was_in_progress) { - timer->stop(); - - if (reason != Spider::GameOverReason::Victory) - increment_total_losses(); - } - - if (reason == Spider::GameOverReason::Victory) { - increment_total_wins(); - - if (score > high_score()) { - update_high_score(score); - } - - auto current_best_time = best_time(); - if (seconds_elapsed < current_best_time || current_best_time == 0) { - update_best_time(seconds_elapsed); - } - - reset_statistic_status(); - } - statusbar.set_text(2, "Timer starts after your first move"_string); - }; - - auto confirm_end_current_game = [&]() { - auto game_in_progress = timer->is_active(); - if (game_in_progress) { - auto result = GUI::MessageBox::show(window, - "A game is still in progress, are you sure you would like to end it? Doing so will count as a loss."sv, - "Game in progress"sv, - GUI::MessageBox::Type::Warning, - GUI::MessageBox::InputType::YesNo); - - return result == GUI::MessageBox::ExecResult::Yes; - } - - return true; - }; - - window->on_close_request = [&]() { - if (confirm_end_current_game()) - return GUI::Window::CloseRequestDecision::Close; - return GUI::Window::CloseRequestDecision::StayOpen; - }; - window->on_close = [&]() { - game.on_game_end(Spider::GameOverReason::Quit, 0); - }; - - GUI::ActionGroup suit_actions; - suit_actions.set_exclusive(true); - - auto single_suit_action = GUI::Action::create_checkable("&Single Suit", [&](auto&) { - update_mode(Spider::Mode::SingleSuit); - - if (!confirm_end_current_game()) - return; - - reset_statistic_status(); - game.setup(mode); - }); - single_suit_action->set_checked(mode == Spider::Mode::SingleSuit); - suit_actions.add_action(single_suit_action); - - auto two_suit_action = GUI::Action::create_checkable("&Two Suit", [&](auto&) { - update_mode(Spider::Mode::TwoSuit); - - if (!confirm_end_current_game()) - return; - - reset_statistic_status(); - game.setup(mode); - }); - two_suit_action->set_checked(mode == Spider::Mode::TwoSuit); - suit_actions.add_action(two_suit_action); - - auto game_menu = window->add_menu("&Game"_string); - game_menu->add_action(GUI::Action::create("&New Game", { Mod_None, Key_F2 }, TRY(Gfx::Bitmap::load_from_file("/res/icons/16x16/reload.png"sv)), [&](auto&) { - if (!confirm_end_current_game()) - return; - - game.setup(mode); - })); - game_menu->add_separator(); - auto undo_action = GUI::CommonActions::make_undo_action([&](auto&) { - game.perform_undo(); - }); - undo_action->set_enabled(false); - game_menu->add_action(undo_action); - game_menu->add_separator(); - game_menu->add_action(TRY(Cards::make_cards_settings_action(window))); - game_menu->add_action(single_suit_action); - game_menu->add_action(two_suit_action); - game_menu->add_separator(); - game_menu->add_action(GUI::CommonActions::make_quit_action([&](auto&) { app->quit(); })); - - auto view_menu = window->add_menu("&View"_string); - - GUI::ActionGroup statistic_display_actions; - statistic_display_actions.set_exclusive(true); - - auto high_score_action = GUI::Action::create_checkable("&High Score", [&](auto&) { - update_statistic_display(StatisticDisplay::HighScore); - reset_statistic_status(); - }); - high_score_action->set_checked(statistic_display == StatisticDisplay::HighScore); - statistic_display_actions.add_action(high_score_action); - - auto best_time_actions = GUI::Action::create_checkable("&Best Time", [&](auto&) { - update_statistic_display(StatisticDisplay::BestTime); - reset_statistic_status(); - }); - best_time_actions->set_checked(statistic_display == StatisticDisplay::BestTime); - statistic_display_actions.add_action(best_time_actions); - - view_menu->add_action(high_score_action); - view_menu->add_action(best_time_actions); - - view_menu->add_separator(); - view_menu->add_action(GUI::CommonActions::make_fullscreen_action([&](auto&) { - window->set_fullscreen(!window->is_fullscreen()); - })); - - auto help_menu = window->add_menu("&Help"_string); - help_menu->add_action(GUI::CommonActions::make_command_palette_action(window)); - help_menu->add_action(GUI::CommonActions::make_help_action([&man_file](auto&) { - Desktop::Launcher::open(URL::create_with_file_scheme(man_file), "/bin/Help"); - })); - help_menu->add_action(GUI::CommonActions::make_about_action("Spider"_string, app_icon, window)); - - window->set_resizable(false); - window->resize(Spider::Game::width, Spider::Game::height + statusbar.max_height().as_int()); - window->set_icon(app_icon.bitmap_for_size(16)); - window->show(); - - game.on_undo_availability_change = [&](bool undo_available) { - undo_action->set_enabled(undo_available); - }; - - game.setup(mode); - - return app->exec(); -} diff --git a/Userland/Games/TwentyFourtyEight/BoardView.cpp b/Userland/Games/TwentyFourtyEight/BoardView.cpp deleted file mode 100644 index a3e9ff6dae8..00000000000 --- a/Userland/Games/TwentyFourtyEight/BoardView.cpp +++ /dev/null @@ -1,260 +0,0 @@ -/* - * Copyright (c) 2020, the SerenityOS developers. - * Copyright (c) 2022, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "BoardView.h" -#include -#include -#include -#include - -BoardView::BoardView(Game::Board const* board) - : m_board(board) -{ -} - -void BoardView::set_board(Game::Board const* board) -{ - if (has_timer()) - stop_timer(); - - slide_animation_frame = 0; - pop_in_animation_frame = 0; - start_timer(frame_duration_ms); - - if (m_board == board) - return; - - if (!board) { - m_board = nullptr; - return; - } - - bool must_resize = !m_board || m_board->tiles().size() != board->tiles().size(); - - m_board = board; - - if (must_resize) - resize(); - - update(); -} - -void BoardView::pick_font() -{ - String best_font_name; - int best_font_size = -1; - auto& font_database = Gfx::FontDatabase::the(); - font_database.for_each_font([&](Gfx::Font const& font) { - if (font.family() != "Liza" || font.weight() != 700) - return; - auto size = font.pixel_size_rounded_up(); - if (size * 2 <= m_cell_size && size > best_font_size) { - best_font_name = font.qualified_name(); - best_font_size = size; - } - }); - - if (best_font_name.is_empty()) { - dbgln("Failed to find a good font for size {}, using the default font", m_cell_size / 2); - best_font_name = font_database.default_font().qualified_name(); - } - auto const font = font_database.get_by_name(best_font_name); - set_font(font); - - m_min_cell_size = font->pixel_size_rounded_up(); -} - -size_t BoardView::rows() const -{ - if (!m_board) - return 0; - return m_board->tiles().size(); -} - -size_t BoardView::columns() const -{ - if (!m_board) - return 0; - if (m_board->tiles().is_empty()) - return 0; - return m_board->tiles()[0].size(); -} - -void BoardView::resize_event(GUI::ResizeEvent&) -{ - resize(); -} - -void BoardView::resize() -{ - constexpr float padding_ratio = 7; - m_padding = min( - width() / (columns() * (padding_ratio + 1) + 1), - height() / (rows() * (padding_ratio + 1) + 1)); - m_cell_size = m_padding * padding_ratio; - - pick_font(); -} - -void BoardView::keydown_event(GUI::KeyEvent& event) -{ - if (!on_move) { - event.ignore(); - return; - } - - switch (event.key()) { - case KeyCode::Key_A: - case KeyCode::Key_Left: - on_move(Game::Direction::Left); - return; - case KeyCode::Key_D: - case KeyCode::Key_Right: - on_move(Game::Direction::Right); - return; - case KeyCode::Key_W: - case KeyCode::Key_Up: - on_move(Game::Direction::Up); - return; - case KeyCode::Key_S: - case KeyCode::Key_Down: - on_move(Game::Direction::Down); - return; - default: - break; - } - - event.ignore(); -} - -Gfx::Color BoardView::background_color_for_cell(u32 value) -{ - switch (value) { - case 0: - return Color::from_rgb(0xcdc1b4); - case 2: - return Color::from_rgb(0xeee4da); - case 4: - return Color::from_rgb(0xede0c8); - case 8: - return Color::from_rgb(0xf2b179); - case 16: - return Color::from_rgb(0xf59563); - case 32: - return Color::from_rgb(0xf67c5f); - case 64: - return Color::from_rgb(0xf65e3b); - case 128: - return Color::from_rgb(0xedcf72); - case 256: - return Color::from_rgb(0xedcc61); - case 512: - return Color::from_rgb(0xedc850); - case 1024: - return Color::from_rgb(0xedc53f); - case 2048: - return Color::from_rgb(0xedc22e); - default: - VERIFY(value > 2048); - return Color::from_rgb(0x3c3a32); - } -} - -Gfx::Color BoardView::text_color_for_cell(u32 value) -{ - if (value <= 4) - return Color::from_rgb(0x776e65); - return Color::from_rgb(0xf9f6f2); -} - -void BoardView::timer_event(Core::TimerEvent&) -{ - if (slide_animation_frame < animation_duration) { - slide_animation_frame++; - update(); - } else if (pop_in_animation_frame < animation_duration) { - pop_in_animation_frame++; - update(); - if (pop_in_animation_frame == animation_duration) - stop_timer(); - } -} - -void BoardView::paint_event(GUI::PaintEvent& event) -{ - Frame::paint_event(event); - - Color background_color = Color::from_rgb(0xbbada0); - - GUI::Painter painter(*this); - painter.add_clip_rect(event.rect()); - painter.add_clip_rect(frame_inner_rect()); - painter.translate(frame_thickness(), frame_thickness()); - - if (!m_board) { - painter.fill_rect(rect(), background_color); - return; - } - auto& tiles = m_board->tiles(); - - Gfx::IntRect field_rect { - 0, - 0, - static_cast(m_padding + (m_cell_size + m_padding) * columns()), - static_cast(m_padding + (m_cell_size + m_padding) * rows()) - }; - field_rect.center_within(rect()); - painter.fill_rect(field_rect, background_color); - - auto tile_center = [&](size_t row, size_t column) { - return Gfx::IntPoint { - field_rect.x() + m_padding + (m_cell_size + m_padding) * column + m_cell_size / 2, - field_rect.y() + m_padding + (m_cell_size + m_padding) * row + m_cell_size / 2, - }; - }; - - if (slide_animation_frame < animation_duration) { - // background - for (size_t column = 0; column < columns(); ++column) { - for (size_t row = 0; row < rows(); ++row) { - auto center = tile_center(row, column); - auto tile_size = Gfx::IntSize { m_cell_size, m_cell_size }; - auto rect = Gfx::IntRect::centered_on(center, tile_size); - painter.fill_rect(rect, background_color_for_cell(0)); - } - } - - for (auto& sliding_tile : m_board->sliding_tiles()) { - auto center_from = tile_center(sliding_tile.row_from, sliding_tile.column_from); - auto center_to = tile_center(sliding_tile.row_to, sliding_tile.column_to); - auto offset = Gfx::FloatPoint(center_to - center_from); - auto center = center_from + Gfx::IntPoint(offset * (slide_animation_frame / (float)animation_duration)); - - auto tile_size = Gfx::IntSize { m_cell_size, m_cell_size }; - auto rect = Gfx::IntRect::centered_on(center, tile_size); - - painter.fill_rect(rect, background_color_for_cell(sliding_tile.value_from)); - painter.draw_text(rect, String::number(sliding_tile.value_from).release_value_but_fixme_should_propagate_errors(), font(), Gfx::TextAlignment::Center, text_color_for_cell(sliding_tile.value_from)); - } - } else { - for (size_t column = 0; column < columns(); ++column) { - for (size_t row = 0; row < rows(); ++row) { - auto center = tile_center(row, column); - auto tile_size = Gfx::IntSize { m_cell_size, m_cell_size }; - if (pop_in_animation_frame < animation_duration && Game::Board::Position { row, column } == m_board->last_added_position()) { - float pop_in_size = m_min_cell_size + (m_cell_size - m_min_cell_size) * (pop_in_animation_frame / (float)animation_duration); - tile_size = Gfx::IntSize { pop_in_size, pop_in_size }; - } - auto rect = Gfx::IntRect::centered_on(center, tile_size); - auto entry = tiles[row][column]; - painter.fill_rect(rect, background_color_for_cell(entry)); - if (entry > 0) - painter.draw_text(rect, String::number(entry).release_value_but_fixme_should_propagate_errors(), font(), Gfx::TextAlignment::Center, text_color_for_cell(entry)); - } - } - } -} diff --git a/Userland/Games/TwentyFourtyEight/BoardView.h b/Userland/Games/TwentyFourtyEight/BoardView.h deleted file mode 100644 index 5a3b7a27d08..00000000000 --- a/Userland/Games/TwentyFourtyEight/BoardView.h +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright (c) 2020, the SerenityOS developers. - * Copyright (c) 2022, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include "Game.h" -#include - -class BoardView final : public GUI::Frame { - C_OBJECT(BoardView); - -public: - virtual ~BoardView() override = default; - void set_board(Game::Board const* board); - - Function on_move; - -private: - explicit BoardView(Game::Board const*); - - virtual void resize_event(GUI::ResizeEvent&) override; - virtual void paint_event(GUI::PaintEvent&) override; - virtual void keydown_event(GUI::KeyEvent&) override; - virtual void timer_event(Core::TimerEvent&) override; - - size_t rows() const; - size_t columns() const; - - void pick_font(); - void resize(); - - Color background_color_for_cell(u32 value); - Color text_color_for_cell(u32 value); - - float m_padding { 0 }; - float m_min_cell_size { 0 }; - float m_cell_size { 0 }; - - Game::Board const* m_board { nullptr }; - - static constexpr int frame_duration_ms = 1000 / 60; - static constexpr int animation_duration = 5; - - int pop_in_animation_frame = 0; - int slide_animation_frame = 0; -}; diff --git a/Userland/Games/TwentyFourtyEight/CMakeLists.txt b/Userland/Games/TwentyFourtyEight/CMakeLists.txt deleted file mode 100644 index 59770283a5a..00000000000 --- a/Userland/Games/TwentyFourtyEight/CMakeLists.txt +++ /dev/null @@ -1,20 +0,0 @@ -serenity_component( - 2048 - RECOMMENDED - TARGETS 2048 -) - -compile_gml(GameSizeDialog.gml GameSizeDialogGML.cpp) -compile_gml(GameWindowWidget.gml GameWindowWidgetGML.cpp) - -set(SOURCES - BoardView.cpp - Game.cpp - GameSizeDialog.cpp - GameSizeDialogGML.cpp - GameWindowWidgetGML.cpp - main.cpp -) - -serenity_app(2048 ICON app-2048) -target_link_libraries(2048 PRIVATE LibConfig LibCore LibGfx LibGUI LibMain LibDesktop LibURL) diff --git a/Userland/Games/TwentyFourtyEight/Game.cpp b/Userland/Games/TwentyFourtyEight/Game.cpp deleted file mode 100644 index 70f29fd8409..00000000000 --- a/Userland/Games/TwentyFourtyEight/Game.cpp +++ /dev/null @@ -1,313 +0,0 @@ -/* - * Copyright (c) 2020, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "Game.h" -#include -#include -#include -#include - -Game::Game(size_t grid_size, size_t target_tile, bool evil_ai) - : m_grid_size(grid_size) - , m_evil_ai(evil_ai) -{ - if (target_tile == 0) - m_target_tile = 2048; - else if ((target_tile & (target_tile - 1)) != 0) - m_target_tile = 1 << max_power_for_board(grid_size); - else - m_target_tile = target_tile; - - m_board.m_tiles.resize(grid_size); - for (auto& row : m_board.m_tiles) { - row.ensure_capacity(grid_size); - for (size_t i = 0; i < grid_size; i++) - row.append(0); - } - - add_tile(); - add_tile(); -} - -void Game::add_random_tile() -{ - int row; - int column; - do { - row = rand() % m_grid_size; - column = rand() % m_grid_size; - } while (m_board.m_tiles[row][column] != 0); - - size_t value = rand() < RAND_MAX * 0.9 ? 2 : 4; - m_board.add_tile(row, column, value); -} - -void Game::Board::transpose() -{ - for (size_t i = 1; i < m_tiles.size(); ++i) { - for (size_t j = 0; j < i; j++) - swap(m_tiles[i][j], m_tiles[j][i]); - } - for (auto& t : m_sliding_tiles) { - swap(t.row_from, t.column_from); - swap(t.row_to, t.column_to); - } -} - -void Game::Board::reverse() -{ - for (auto& row : m_tiles) { - for (size_t i = 0; i < row.size() / 2; ++i) - swap(row[i], row[row.size() - i - 1]); - } - - auto const row_size = m_tiles[0].size(); - for (auto& t : m_sliding_tiles) { - t.column_from = row_size - t.column_from - 1; - t.column_to = row_size - t.column_to - 1; - } -} - -size_t Game::Board::slide_row(size_t row_index) -{ - Game::Board::Row& row = m_tiles[row_index]; - size_t successful_merge_score = 0; - - auto next_nonempty = [&](size_t start_index) { - size_t next = start_index; - for (; next < row.size(); next++) { - if (row[next] != 0) - break; - } - return next; - }; - - size_t current_index = 0; - - size_t first = next_nonempty(0); - if (first == row.size()) // empty row - return 0; - - while (first < row.size()) { - auto second = next_nonempty(first + 1); - if (second == row.size() || row[first] != row[second]) { - m_sliding_tiles.append({ row_index, first, row[first], row_index, current_index, row[first] }); - - row[current_index] = row[first]; - current_index++; - first = second; - } else { - VERIFY(row[first] == row[second]); - - m_sliding_tiles.append({ row_index, first, row[first], row_index, current_index, 2 * row[first] }); - m_sliding_tiles.append({ row_index, second, row[second], row_index, current_index, 2 * row[first] }); - - row[current_index] = 2 * row[first]; - current_index++; - successful_merge_score += 2 * row[first]; - first = next_nonempty(second + 1); - } - } - - for (; current_index < row.size(); current_index++) - row[current_index] = 0; - - return successful_merge_score; -} - -size_t Game::Board::slide_left() -{ - m_sliding_tiles.clear(); - - size_t successful_merge_score = 0; - - for (size_t row_index = 0; row_index < m_tiles.size(); row_index++) - successful_merge_score += slide_row(row_index); - - return successful_merge_score; -} - -static bool is_complete(Game::Board const& board, size_t target) -{ - for (auto& row : board.tiles()) { - if (row.contains_slow(target)) - return true; - } - - return false; -} - -static bool has_no_neighbors(ReadonlySpan const& row) -{ - if (row.size() < 2) - return true; - - auto x = row[0]; - auto y = row[1]; - - if (x == y) - return false; - - return has_no_neighbors(row.slice(1, row.size() - 1)); -} - -bool Game::Board::is_stalled() -{ - static auto stalled = [](auto& row) { - return !row.contains_slow(0) && has_no_neighbors(row.span()); - }; - - for (auto& row : m_tiles) - if (!stalled(row)) - return false; - - transpose(); - auto scope_guard = ScopeGuard([&]() { transpose(); }); - for (auto& row : m_tiles) - if (!stalled(row)) - return false; - - return true; -} - -static size_t get_number_of_free_cells(Game::Board const& board) -{ - size_t accumulator = 0; - for (auto& row : board.tiles()) { - for (auto& cell : row) - accumulator += cell == 0; - } - return accumulator; -} - -Game::Board::SlideResult Game::Board::slide_tiles(Direction direction) -{ - size_t successful_merge_score = 0; - - switch (direction) { - case Direction::Left: - successful_merge_score = slide_left(); - break; - - case Direction::Right: - reverse(); - successful_merge_score = slide_left(); - reverse(); - break; - - case Direction::Up: - transpose(); - successful_merge_score = slide_left(); - transpose(); - break; - - case Direction::Down: - transpose(); - reverse(); - successful_merge_score = slide_left(); - reverse(); - transpose(); - break; - } - - bool moved = false; - for (auto& t : m_sliding_tiles) { - if (t.row_from != t.row_to || t.column_from != t.column_to) - moved = true; - } - - return { moved, successful_merge_score }; -} - -Game::MoveOutcome Game::attempt_move(Direction direction) -{ - auto [moved, successful_merge_score] = m_board.slide_tiles(direction); - if (moved) { - m_turns++; - m_score += successful_merge_score; - add_tile(); - } - - if (is_complete(m_board, m_target_tile) && !m_want_to_continue) - return MoveOutcome::Won; - if (m_board.is_stalled()) - return MoveOutcome::GameOver; - if (moved) - return MoveOutcome::OK; - return MoveOutcome::InvalidMove; -} - -void Game::add_evil_tile() -{ - size_t worst_row = 0; - size_t worst_column = 0; - u32 worst_value = 2; - - size_t most_free_cells = NumericLimits::max(); - size_t worst_score = NumericLimits::max(); - - for (size_t row = 0; row < m_grid_size; row++) { - for (size_t column = 0; column < m_grid_size; column++) { - if (m_board.m_tiles[row][column] != 0) - continue; - - for (u32 value : Array { 2, 4 }) { - Game saved_state = *this; - saved_state.m_board.m_tiles[row][column] = value; - - if (saved_state.m_board.is_stalled()) { - // We can stall the board now, instant game over. - worst_row = row; - worst_column = column; - worst_value = value; - - goto found_worst_tile; - } - - // These are the best outcome and score the player can achieve in one move. - // We want this to be as low as possible. - size_t best_outcome = 0; - size_t best_score = 0; - for (auto direction : Array { Direction::Down, Direction::Left, Direction::Right, Direction::Up }) { - Game moved_state = saved_state; - auto [moved, score_delta] = moved_state.m_board.slide_tiles(direction); - if (!moved) // invalid move - continue; - best_outcome = max(best_outcome, get_number_of_free_cells(moved_state.board())); - best_score = max(best_score, score_delta); - } - - // We already know a worse cell placement; discard. - if (best_outcome > most_free_cells) - continue; - - // This tile is the same as the worst we know in terms of board population, - // but the player can achieve the same or better score; discard. - if (best_outcome == most_free_cells && best_score >= worst_score) - continue; - - worst_row = row; - worst_column = column; - worst_value = value; - - most_free_cells = best_outcome; - worst_score = best_score; - } - } - } -found_worst_tile: - m_board.add_tile(worst_row, worst_column, worst_value); -} - -u32 Game::largest_tile() const -{ - u32 tile = 0; - for (auto& row : m_board.m_tiles) { - for (auto& cell : row) - tile = max(tile, cell); - } - return tile; -} diff --git a/Userland/Games/TwentyFourtyEight/Game.h b/Userland/Games/TwentyFourtyEight/Game.h deleted file mode 100644 index 8be815e3eb9..00000000000 --- a/Userland/Games/TwentyFourtyEight/Game.h +++ /dev/null @@ -1,127 +0,0 @@ -/* - * Copyright (c) 2020, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include - -class Game final { -public: - Game(size_t grid_size, size_t target_tile, bool evil_ai); - Game(Game const&) = default; - Game& operator=(Game const&) = default; - - enum class MoveOutcome { - OK, - InvalidMove, - GameOver, - Won, - }; - - enum class Direction { - Up, - Down, - Left, - Right, - }; - - MoveOutcome attempt_move(Direction); - - size_t score() const { return m_score; } - size_t turns() const { return m_turns; } - u32 target_tile() const { return m_target_tile; } - u32 largest_tile() const; - void set_want_to_continue() { m_want_to_continue = true; } - class Board { - public: - using Row = Vector; - using Tiles = Vector; - - Tiles const& tiles() const { return m_tiles; } - - bool is_stalled(); - - struct Position { - size_t row; - size_t column; - - bool operator==(Position const& other) const - { - return row == other.row && column == other.column; - } - }; - - void add_tile(size_t row, size_t column, u32 value) - { - m_tiles[row][column] = value; - m_last_added_position = Position { row, column }; - } - Position const& last_added_position() const { return m_last_added_position; } - - struct SlideResult { - bool has_moved; - size_t score_delta; - }; - SlideResult slide_tiles(Direction); - - struct SlidingTile { - size_t row_from; - size_t column_from; - u32 value_from; - - size_t row_to; - size_t column_to; - u32 value_to; - }; - Vector const& sliding_tiles() const { return m_sliding_tiles; } - - private: - void reverse(); - void transpose(); - - size_t slide_row(size_t row_index); - size_t slide_left(); - - friend Game; - - Tiles m_tiles; - - Position m_last_added_position { 0, 0 }; - Vector m_sliding_tiles; - }; - - Board const& board() const { return m_board; } - - static size_t max_power_for_board(size_t size) - { - if (size >= 6) - return 31; - - return size * size + 1; - } - -private: - void add_tile() - { - if (m_evil_ai) - add_evil_tile(); - else - add_random_tile(); - } - - void add_random_tile(); - void add_evil_tile(); - - size_t m_grid_size { 0 }; - u32 m_target_tile { 0 }; - - bool m_evil_ai { false }; - bool m_want_to_continue { false }; - - Board m_board; - size_t m_score { 0 }; - size_t m_turns { 0 }; -}; diff --git a/Userland/Games/TwentyFourtyEight/GameSizeDialog.cpp b/Userland/Games/TwentyFourtyEight/GameSizeDialog.cpp deleted file mode 100644 index ec65af5fbce..00000000000 --- a/Userland/Games/TwentyFourtyEight/GameSizeDialog.cpp +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Copyright (c) 2020, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "GameSizeDialog.h" -#include "Game.h" -#include "GameSizeDialogWidget.h" -#include -#include -#include -#include -#include -#include - -namespace TwentyFourtyEight { -GameSizeDialog::GameSizeDialog(GUI::Window* parent, size_t board_size, size_t target, bool evil_ai) - : GUI::Dialog(parent) - , m_board_size(board_size) - , m_target_tile_power(AK::log2(target)) - , m_evil_ai(evil_ai) -{ - set_rect({ 0, 0, 250, 150 }); - set_title("New Game"); - set_icon(parent->icon()); - set_resizable(false); - - auto main_widget = GameSizeDialogWidget::try_create().release_value_but_fixme_should_propagate_errors(); - set_main_widget(main_widget); - - auto board_size_spinbox = main_widget->find_descendant_of_type_named("board_size_spinbox"); - board_size_spinbox->set_value(m_board_size); - - auto tile_value_label = main_widget->find_descendant_of_type_named("tile_value_label"); - tile_value_label->set_text(String::number(target_tile()).release_value_but_fixme_should_propagate_errors()); - auto target_spinbox = main_widget->find_descendant_of_type_named("target_spinbox"); - target_spinbox->set_max(Game::max_power_for_board(m_board_size)); - target_spinbox->set_value(m_target_tile_power); - - board_size_spinbox->on_change = [this, target_spinbox](auto value) { - m_board_size = value; - target_spinbox->set_max(Game::max_power_for_board(m_board_size)); - }; - - target_spinbox->on_change = [this, tile_value_label](auto value) { - m_target_tile_power = value; - tile_value_label->set_text(String::number(target_tile()).release_value_but_fixme_should_propagate_errors()); - }; - - auto evil_ai_checkbox = main_widget->find_descendant_of_type_named("evil_ai_checkbox"); - evil_ai_checkbox->set_checked(m_evil_ai); - evil_ai_checkbox->on_checked = [this](auto checked) { m_evil_ai = checked; }; - - auto temporary_checkbox = main_widget->find_descendant_of_type_named("temporary_checkbox"); - temporary_checkbox->set_checked(m_temporary); - temporary_checkbox->on_checked = [this](auto checked) { m_temporary = checked; }; - - auto cancel_button = main_widget->find_descendant_of_type_named("cancel_button"); - cancel_button->on_click = [this](auto) { - done(ExecResult::Cancel); - }; - - auto ok_button = main_widget->find_descendant_of_type_named("ok_button"); - ok_button->on_click = [this](auto) { - done(ExecResult::OK); - }; -} -} diff --git a/Userland/Games/TwentyFourtyEight/GameSizeDialog.gml b/Userland/Games/TwentyFourtyEight/GameSizeDialog.gml deleted file mode 100644 index 543126539f8..00000000000 --- a/Userland/Games/TwentyFourtyEight/GameSizeDialog.gml +++ /dev/null @@ -1,70 +0,0 @@ -@TwentyFourtyEight::GameSizeDialogWidget { - fill_with_background_color: true - layout: @GUI::VerticalBoxLayout { - margins: [4] - } - - @GUI::Widget { - layout: @GUI::HorizontalBoxLayout { - spacing: 4 - } - - @GUI::Label { - text: "Board size:" - text_alignment: "CenterLeft" - } - - @GUI::SpinBox { - name: "board_size_spinbox" - max: 100 - min: 2 - } - } - - @GUI::Widget { - layout: @GUI::HorizontalBoxLayout { - spacing: 4 - } - - @GUI::Label { - text: "Target tile:" - text_alignment: "CenterLeft" - } - - @GUI::Label { - name: "tile_value_label" - text_alignment: "CenterRight" - } - - @GUI::SpinBox { - name: "target_spinbox" - min: 3 - } - } - - @GUI::CheckBox { - name: "evil_ai_checkbox" - text: "Evil AI" - } - - @GUI::CheckBox { - name: "temporary_checkbox" - text: "Temporarily apply changes" - } - - @GUI::Widget { - layout: @GUI::HorizontalBoxLayout { - spacing: 10 - } - - @GUI::Button { - name: "cancel_button" - text: "Cancel" - } - - @GUI::Button { - name: "ok_button" - text: "OK" - } - } -} diff --git a/Userland/Games/TwentyFourtyEight/GameSizeDialog.h b/Userland/Games/TwentyFourtyEight/GameSizeDialog.h deleted file mode 100644 index 43e4a60a3f6..00000000000 --- a/Userland/Games/TwentyFourtyEight/GameSizeDialog.h +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright (c) 2020, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include - -namespace TwentyFourtyEight { -class GameSizeDialog : public GUI::Dialog { - C_OBJECT(GameSizeDialog) -public: - size_t board_size() const { return m_board_size; } - u32 target_tile() const { return 1u << m_target_tile_power; } - bool evil_ai() const { return m_evil_ai; } - bool temporary() const { return m_temporary; } - -private: - GameSizeDialog(GUI::Window* parent, size_t board_size, size_t target_tile, bool evil_ai); - - size_t m_board_size; - size_t m_target_tile_power; - bool m_evil_ai; - bool m_temporary { false }; -}; -} diff --git a/Userland/Games/TwentyFourtyEight/GameSizeDialogWidget.h b/Userland/Games/TwentyFourtyEight/GameSizeDialogWidget.h deleted file mode 100644 index 3a2644e5309..00000000000 --- a/Userland/Games/TwentyFourtyEight/GameSizeDialogWidget.h +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright (c) 2024, Aryan Baburajan . - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include - -namespace TwentyFourtyEight { - -class GameSizeDialogWidget : public GUI::Widget { - C_OBJECT_ABSTRACT(GameSizeDialogWidget) - -public: - static ErrorOr> try_create(); - virtual ~GameSizeDialogWidget() override = default; - -private: - GameSizeDialogWidget() = default; -}; - -} diff --git a/Userland/Games/TwentyFourtyEight/GameWindowWidget.gml b/Userland/Games/TwentyFourtyEight/GameWindowWidget.gml deleted file mode 100644 index ae03d5b5b5b..00000000000 --- a/Userland/Games/TwentyFourtyEight/GameWindowWidget.gml +++ /dev/null @@ -1,17 +0,0 @@ -@TwentyFourtyEight::GameWindowWidget { - fill_with_background_color: true - layout: @GUI::VerticalBoxLayout {} - - @GUI::Widget { - layout: @GUI::VerticalBoxLayout {} - - @GUI::Widget { - name: "board_view_container" - layout: @GUI::VerticalBoxLayout {} - } - - @GUI::Statusbar { - name: "statusbar" - } - } -} diff --git a/Userland/Games/TwentyFourtyEight/GameWindowWidget.h b/Userland/Games/TwentyFourtyEight/GameWindowWidget.h deleted file mode 100644 index b4f73c607f1..00000000000 --- a/Userland/Games/TwentyFourtyEight/GameWindowWidget.h +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright (c) 2024, Aryan Baburajan . - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include - -namespace TwentyFourtyEight { - -class GameWindowWidget : public GUI::Widget { - C_OBJECT_ABSTRACT(GameWindowWidget) - -public: - static ErrorOr> try_create(); - virtual ~GameWindowWidget() override = default; - -private: - GameWindowWidget() = default; -}; - -} diff --git a/Userland/Games/TwentyFourtyEight/main.cpp b/Userland/Games/TwentyFourtyEight/main.cpp deleted file mode 100644 index 348f5be79af..00000000000 --- a/Userland/Games/TwentyFourtyEight/main.cpp +++ /dev/null @@ -1,236 +0,0 @@ -/* - * Copyright (c) 2020-2021, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "BoardView.h" -#include "Game.h" -#include "GameSizeDialog.h" -#include "GameWindowWidget.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -ErrorOr serenity_main(Main::Arguments arguments) -{ - TRY(Core::System::pledge("stdio rpath recvfd sendfd unix")); - - srand(time(nullptr)); - - auto app = TRY(GUI::Application::create(arguments)); - auto app_icon = TRY(GUI::Icon::try_create_default_icon("app-2048"sv)); - - auto window = GUI::Window::construct(); - - Config::pledge_domain("2048"); - - TRY(Desktop::Launcher::add_allowed_handler_with_only_specific_urls("/bin/Help", { URL::create_with_file_scheme("/usr/share/man/man6/2048.md") })); - TRY(Desktop::Launcher::seal_allowlist()); - - TRY(Core::System::pledge("stdio rpath recvfd sendfd")); - - TRY(Core::System::unveil("/tmp/session/%sid/portal/launch", "rw")); - TRY(Core::System::unveil("/res", "r")); - TRY(Core::System::unveil(nullptr, nullptr)); - - size_t board_size = Config::read_i32("2048"sv, ""sv, "board_size"sv, 4); - u32 target_tile = Config::read_i32("2048"sv, ""sv, "target_tile"sv, 2048); - bool evil_ai = Config::read_bool("2048"sv, ""sv, "evil_ai"sv, false); - - if ((target_tile & (target_tile - 1)) != 0) { - // If the target tile is not a power of 2, reset to its default value. - target_tile = 2048; - } - - Config::write_i32("2048"sv, ""sv, "board_size"sv, board_size); - Config::write_i32("2048"sv, ""sv, "target_tile"sv, target_tile); - Config::write_bool("2048"sv, ""sv, "evil_ai"sv, evil_ai); - - window->set_double_buffering_enabled(false); - window->set_title("2048"); - window->resize(315, 336); - - auto main_widget = TwentyFourtyEight::GameWindowWidget::try_create().release_value_but_fixme_should_propagate_errors(); - window->set_main_widget(main_widget); - - Game game { board_size, target_tile, evil_ai }; - - auto board_view = TRY(main_widget->find_descendant_of_type_named("board_view_container")->try_add(&game.board())); - board_view->set_focus(true); - auto statusbar = main_widget->find_descendant_of_type_named("statusbar"); - - app->on_action_enter = [&](GUI::Action& action) { - statusbar->set_override_text(action.status_tip()); - }; - - app->on_action_leave = [&](GUI::Action&) { - statusbar->set_override_text({}); - }; - - auto update = [&]() { - board_view->set_board(&game.board()); - board_view->update(); - statusbar->set_text(String::formatted("Score: {}", game.score()).release_value_but_fixme_should_propagate_errors()); - }; - - update(); - - Vector undo_stack; - Vector redo_stack; - - RefPtr undo_action; - RefPtr redo_action; - - undo_action = GUI::CommonActions::make_undo_action([&](auto& action) { - redo_stack.append(game); - redo_action->set_enabled(true); - game = undo_stack.take_last(); - if (undo_stack.is_empty()) - action.set_enabled(false); - - update(); - }); - undo_action->set_enabled(false); - - redo_action = GUI::CommonActions::make_redo_action([&](auto& action) { - undo_stack.append(game); - undo_action->set_enabled(true); - game = redo_stack.take_last(); - if (redo_stack.is_empty()) - action.set_enabled(false); - - update(); - }); - redo_action->set_enabled(false); - - auto change_settings = [&] { - auto size_dialog = TwentyFourtyEight::GameSizeDialog::construct(window, board_size, target_tile, evil_ai); - if (size_dialog->exec() != GUI::Dialog::ExecResult::OK) - return; - - board_size = size_dialog->board_size(); - target_tile = size_dialog->target_tile(); - evil_ai = size_dialog->evil_ai(); - - if (!size_dialog->temporary()) { - - Config::write_i32("2048"sv, ""sv, "board_size"sv, board_size); - Config::write_i32("2048"sv, ""sv, "target_tile"sv, target_tile); - Config::write_bool("2048"sv, ""sv, "evil_ai"sv, evil_ai); - - GUI::MessageBox::show(size_dialog, "New settings have been saved and will be applied on a new game"sv, "Settings Changed Successfully"sv, GUI::MessageBox::Type::Information); - return; - } - - GUI::MessageBox::show(size_dialog, "New settings have been set and will be applied on the next game"sv, "Settings Changed Successfully"sv, GUI::MessageBox::Type::Information); - }; - auto start_a_new_game = [&] { - // Do not leak game states between games. - undo_stack.clear(); - redo_stack.clear(); - undo_action->set_enabled(false); - redo_action->set_enabled(false); - - game = Game(board_size, target_tile, evil_ai); - - // This ensures that the sizes are correct. - board_view->set_board(nullptr); - board_view->set_board(&game.board()); - - update(); - window->update(); - }; - - board_view->on_move = [&](Game::Direction direction) { - undo_stack.append(game); - undo_action->set_enabled(true); - - redo_stack.clear(); - redo_action->set_enabled(false); - - auto outcome = game.attempt_move(direction); - switch (outcome) { - case Game::MoveOutcome::OK: - if (undo_stack.size() >= 16) - undo_stack.take_first(); - update(); - break; - case Game::MoveOutcome::InvalidMove: - undo_stack.take_last(); - break; - case Game::MoveOutcome::Won: { - update(); - auto want_to_continue = GUI::MessageBox::show(window, - String::formatted("You won the game in {} turns with a score of {}. Would you like to continue?", game.turns(), game.score()).release_value_but_fixme_should_propagate_errors(), - "Congratulations!"sv, - GUI::MessageBox::Type::Question, - GUI::MessageBox::InputType::YesNo); - if (want_to_continue == GUI::MessageBox::ExecResult::Yes) - game.set_want_to_continue(); - else - start_a_new_game(); - break; - } - case Game::MoveOutcome::GameOver: - update(); - GUI::MessageBox::show(window, - String::formatted("You reached {} in {} turns with a score of {}", game.largest_tile(), game.turns(), game.score()).release_value_but_fixme_should_propagate_errors(), - "You lost!"sv, - GUI::MessageBox::Type::Information); - start_a_new_game(); - break; - } - }; - - auto game_menu = window->add_menu("&Game"_string); - - game_menu->add_action(GUI::Action::create("&New Game", { Mod_None, Key_F2 }, TRY(Gfx::Bitmap::load_from_file("/res/icons/16x16/reload.png"sv)), [&](auto&) { - start_a_new_game(); - })); - - game_menu->add_action(*undo_action); - game_menu->add_action(*redo_action); - - game_menu->add_separator(); - game_menu->add_action(GUI::Action::create("&Settings", TRY(Gfx::Bitmap::load_from_file("/res/icons/16x16/settings.png"sv)), [&](auto&) { - change_settings(); - })); - - game_menu->add_separator(); - game_menu->add_action(GUI::CommonActions::make_quit_action([](auto&) { - GUI::Application::the()->quit(); - })); - - auto view_menu = window->add_menu("&View"_string); - view_menu->add_action(GUI::CommonActions::make_fullscreen_action([&](auto&) { - window->set_fullscreen(!window->is_fullscreen()); - })); - - auto help_menu = window->add_menu("&Help"_string); - help_menu->add_action(GUI::CommonActions::make_command_palette_action(window)); - help_menu->add_action(GUI::CommonActions::make_help_action([](auto&) { - Desktop::Launcher::open(URL::create_with_file_scheme("/usr/share/man/man6/2048.md"), "/bin/Help"); - })); - help_menu->add_action(GUI::CommonActions::make_about_action("2048"_string, app_icon, window)); - - window->show(); - - window->set_icon(app_icon.bitmap_for_size(16)); - - return app->exec(); -} diff --git a/Userland/Libraries/LibC/CMakeLists.txt b/Userland/Libraries/LibC/CMakeLists.txt deleted file mode 100644 index 343f928f2c2..00000000000 --- a/Userland/Libraries/LibC/CMakeLists.txt +++ /dev/null @@ -1,320 +0,0 @@ -set(SOURCES - arch/${SERENITY_ARCH}/fenv.cpp - arch/${SERENITY_ARCH}/setjmp.S - arpa/inet.cpp - assert.cpp - ctype.cpp - cxxabi.cpp - dirent.cpp - dlfcn.cpp - fcntl.cpp - fenv.cpp - fnmatch.cpp - ifaddrs.cpp - getopt.cpp - getsubopt.cpp - glob.cpp - grp.cpp - inttypes.cpp - ioctl.cpp - langinfo.cpp - libcinit.cpp - libgen.cpp - link.cpp - locale.cpp - malloc.cpp - math.cpp - mntent.cpp - net.cpp - netdb.cpp - poll.cpp - priority.cpp - pthread.cpp - pthread_cond.cpp - pthread_integration.cpp - pthread_once.cpp - pthread_tls.cpp - pty.cpp - pwd.cpp - qsort.cpp - regex.cpp - resolv.cpp - scanf.cpp - sched.cpp - search.cpp - semaphore.cpp - serenity.cpp - shadow.cpp - signal.cpp - spawn.cpp - ssp.cpp - stat.cpp - stdio.cpp - stdlib.cpp - string.cpp - strings.cpp - stubs.cpp - sys/archctl.cpp - sys/auxv.cpp - sys/file.cpp - sys/mman.cpp - sys/prctl.cpp - sys/ptrace.cpp - sys/select.cpp - sys/socket.cpp - sys/statvfs.cpp - sys/uio.cpp - sys/wait.cpp - syslog.cpp - termcap.cpp - termios.cpp - time.cpp - times.cpp - tls.cpp - ulimit.cpp - unistd.cpp - utime.cpp - utsname.cpp - wchar.cpp - wctype.cpp - wstdio.cpp - - ${AK_SOURCES} -) - -if (SERENITY_ARCH STREQUAL "x86_64") - list(APPEND SOURCES - arch/x86_64/memset.cpp - arch/x86_64/memset.S - ) -endif() - -# FIXME: This, ideally, should be an exhaustive list of headers (transitively) used by libc's own -# headers. (Like, for example, we shouldn't include AK/Platform.h in them for no reason at -# all.) -set(HEADERS - arch/${SERENITY_ARCH}/fenv.h - arch/fenv.h - arpa/inet.h - bits/dlfcn_integration.h - bits/FILE.h - bits/getopt.h - bits/mutex_locker.h - bits/posix1_lim.h - bits/pthread_cancel.h - bits/pthread_integration.h - bits/search.h - bits/sighow.h - bits/stdint.h - bits/stdio_file_implementation.h - bits/utimens.h - bits/wchar.h - bits/wchar_size.h - net/if.h - net/if_arp.h - net/route.h - netinet/if_ether.h - netinet/in.h - netinet/in_systm.h - netinet/ip.h - netinet/ip_icmp.h - netinet/tcp.h - sys/arch/${SERENITY_ARCH}/regs.h - sys/arch/regs.h - sys/archctl.h - sys/auxv.h - sys/cdefs.h - sys/device.h - sys/devices/gpu.h - sys/file.h - sys/internals.h - sys/ioctl.h - sys/mman.h - sys/param.h - sys/poll.h - sys/prctl.h - sys/ptrace.h - sys/resource.h - sys/select.h - sys/socket.h - sys/stat.h - sys/statvfs.h - sys/sysmacros.h - sys/time.h - sys/times.h - sys/ttydefaults.h - sys/types.h - sys/uio.h - sys/un.h - sys/utsname.h - sys/wait.h - alloca.h - assert.h - byteswap.h - complex.h - ctype.h - dirent.h - dlfcn.h - elf.h - endian.h - errno_codes.h - errno.h - fcntl.h - fd_set.h - fenv.h - float.h - fnmatch.h - getopt.h - glob.h - grp.h - ifaddrs.h - inttypes.h - langinfo.h - libgen.h - limits.h - link.h - locale.h - mallocdefs.h - math.h - memory.h - mntent.h - netdb.h - nl_types.h - paths.h - poll.h - pthread.h - pty.h - pwd.h - regex.h - resolv.h - sched.h - search.h - semaphore.h - serenity.h - setjmp.h - shadow.h - signal.h - spawn.h - stdarg.h - stdint.h - stdio_ext.h - stdio.h - stdlib.h - string.h - strings.h - sysexits.h - syslog.h - termcap.h - termios.h - time.h - ucontext.h - ulimit.h - unistd.h - utime.h - utmp.h - wchar.h - wctype.h - - ../LibELF/ELFABI.h - ../LibRegex/RegexDefs.h -) - -# ===== LibC headers ===== -add_custom_target(install_libc_headers) - -# Copy LibC's headers into the sysroot to satisfy libc++'s include priority requirements. -foreach(RELATIVE_HEADER_PATH IN LISTS HEADERS) - get_filename_component(directory ${RELATIVE_HEADER_PATH} DIRECTORY) - string(REPLACE "../" "" subdirectory "${directory}") - file(MAKE_DIRECTORY "${CMAKE_STAGING_PREFIX}/${CMAKE_INSTALL_INCLUDEDIR}/${subdirectory}") - add_custom_command( - TARGET install_libc_headers - PRE_BUILD - COMMAND "${CMAKE_COMMAND}" -E copy_if_different "${RELATIVE_HEADER_PATH}" "${CMAKE_STAGING_PREFIX}/${CMAKE_INSTALL_INCLUDEDIR}/${subdirectory}" - WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" - VERBATIM - ) -endforeach() - -# ===== Start files ===== -# NOTE: We link all these against NoCoverage so that we don't break ports by requiring coverage -# symbols in runtime/startup objects. -add_library(crt0 STATIC crt0.cpp) -add_dependencies(crt0 install_libc_headers) -target_link_libraries(crt0 PRIVATE NoCoverage) -add_custom_command( - TARGET crt0 - COMMAND "${CMAKE_COMMAND}" -E copy $ ${CMAKE_INSTALL_PREFIX}/usr/lib/crt0.o -) - -add_library(crt0_shared STATIC crt0_shared.cpp) -add_dependencies(crt0_shared install_libc_headers) -target_link_libraries(crt0_shared PRIVATE NoCoverage) -add_custom_command( - TARGET crt0_shared - COMMAND "${CMAKE_COMMAND}" -E copy $ ${CMAKE_INSTALL_PREFIX}/usr/lib/crt0_shared.o -) - -add_library(crti STATIC arch/${SERENITY_ARCH}/crti.S) -target_link_libraries(crti PRIVATE NoCoverage) -add_custom_command( - TARGET crti - COMMAND "${CMAKE_COMMAND}" -E copy $ ${CMAKE_INSTALL_PREFIX}/usr/lib/crti.o -) - -add_library(crtn STATIC arch/${SERENITY_ARCH}/crtn.S) -target_link_libraries(crtn PRIVATE NoCoverage) -add_custom_command( - TARGET crtn - COMMAND "${CMAKE_COMMAND}" -E copy $ ${CMAKE_INSTALL_PREFIX}/usr/lib/crtn.o -) - -# ===== LibC ===== -# Prevent GCC from removing null checks by marking the `FILE*` argument non-null -set_source_files_properties(stdio.cpp PROPERTIES COMPILE_FLAGS "-fno-builtin-fputc -fno-builtin-fputs -fno-builtin-fwrite") - -# Prevent naively implemented string functions (like strlen) from being "optimized" into a call to themselves. -if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU") - set_source_files_properties(string.cpp wchar.cpp PROPERTIES COMPILE_FLAGS "-fno-tree-loop-distribution -fno-tree-loop-distribute-patterns") -else() - set_source_files_properties(string.cpp wchar.cpp PROPERTIES COMPILE_FLAGS "-fno-builtin") -endif() - -serenity_libc(LibC c) -add_dependencies(LibC crti crt0 crt0_shared crtn install_libc_headers) -target_link_libraries(LibC PRIVATE LibSystem LibTimeZone) -target_link_options(LibC PRIVATE -nolibc) - -# Provide a linker script instead of various other libraries that tells everything to link against LibC. -file(WRITE "${CMAKE_STAGING_PREFIX}/${CMAKE_INSTALL_LIBDIR}/libpthread.so" "INPUT(libc.so)") -file(WRITE "${CMAKE_STAGING_PREFIX}/${CMAKE_INSTALL_LIBDIR}/libdl.so" "INPUT(libc.so)") -file(WRITE "${CMAKE_STAGING_PREFIX}/${CMAKE_INSTALL_LIBDIR}/libm.so" "INPUT(libc.so)") -file(WRITE "${CMAKE_STAGING_PREFIX}/${CMAKE_INSTALL_LIBDIR}/libssp.so" "INPUT(libc.so)") - -# ===== LibC for DynamicLoader ===== -add_library(DynamicLoader_LibC STATIC ${SOURCES}) -target_link_libraries(DynamicLoader_LibC - PUBLIC DynamicLoader_CompileOptions - PRIVATE DynamicLoader_LibSystem -) - -# ===== Compatibility with the GCC toolchain without TARGET_LIBC_PROVIDES_SSP ===== -# FIXME: Remove this on the next GCC toolchain version bump -if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU") - add_custom_target(create_compat_ssp_libraries) - add_dependencies(Loader.so create_compat_ssp_libraries) - - add_custom_command(TARGET create_compat_ssp_libraries - PRE_BUILD - COMMAND "${CMAKE_COMMAND}" -E remove libssp.so - COMMAND "${CMAKE_AR}" cr libssp.a - WORKING_DIRECTORY "${CMAKE_STAGING_PREFIX}/${CMAKE_INSTALL_LIBDIR}" - ) - - add_custom_command(TARGET create_compat_ssp_libraries - PRE_BUILD - COMMAND "${CMAKE_COMMAND}" -E remove libssp_nonshared.a - COMMAND "${CMAKE_AR}" cr libssp_nonshared.a - WORKING_DIRECTORY "${CMAKE_STAGING_PREFIX}/${CMAKE_INSTALL_LIBDIR}" - ) -endif() diff --git a/Userland/Libraries/LibC/alloca.h b/Userland/Libraries/LibC/alloca.h deleted file mode 100644 index e8b150c6e21..00000000000 --- a/Userland/Libraries/LibC/alloca.h +++ /dev/null @@ -1,9 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#define alloca __builtin_alloca diff --git a/Userland/Libraries/LibC/arch/aarch64/crti.S b/Userland/Libraries/LibC/arch/aarch64/crti.S deleted file mode 100644 index 660ba90bf68..00000000000 --- a/Userland/Libraries/LibC/arch/aarch64/crti.S +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright (c) 2021, Nico Weber - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -.section .init, "ax", @progbits -.p2align 2 -.global _init -.type _init, @function -_init: - # FIXME: Possibly incomplete. - ret - -.section .fini, "ax", @progbits -.p2align 4 -.global _fini -.type _fini, @function -_fini: - # FIXME: Possibly incomplete. - ret diff --git a/Userland/Libraries/LibC/arch/aarch64/crtn.S b/Userland/Libraries/LibC/arch/aarch64/crtn.S deleted file mode 100644 index aea4d4d1338..00000000000 --- a/Userland/Libraries/LibC/arch/aarch64/crtn.S +++ /dev/null @@ -1,13 +0,0 @@ -/* - * Copyright (c) 2021, Nico Weber - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -.section .init, "ax", @progbits - # FIXME: Possibly incomplete. - ret - -.section .fini, "ax", @progbits - # FIXME: Possibly incomplete. - ret diff --git a/Userland/Libraries/LibC/arch/aarch64/fenv.cpp b/Userland/Libraries/LibC/arch/aarch64/fenv.cpp deleted file mode 100644 index c85a56ff7db..00000000000 --- a/Userland/Libraries/LibC/arch/aarch64/fenv.cpp +++ /dev/null @@ -1,112 +0,0 @@ -/* - * Copyright (c) 2024, Tom Finet - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include - -extern "C" { - -int fegetenv(fenv_t* env) -{ - if (!env) - return 1; - - (void)env; - TODO_AARCH64(); - return 0; -} - -int fesetenv(fenv_t const* env) -{ - if (!env) - return 1; - - (void)env; - TODO_AARCH64(); - return 0; -} - -int feholdexcept(fenv_t* env) -{ - fegetenv(env); - - fenv_t current_env; - fegetenv(¤t_env); - - (void)env; - TODO_AARCH64(); - - fesetenv(¤t_env); - return 0; -} - -int fesetexceptflag(fexcept_t const* except, int exceptions) -{ - if (!except) - return 1; - - fenv_t current_env; - fegetenv(¤t_env); - - exceptions &= FE_ALL_EXCEPT; - - (void)exceptions; - (void)except; - TODO_AARCH64(); - - fesetenv(¤t_env); - return 0; -} - -int fegetround() -{ - TODO_AARCH64(); -} - -int fesetround(int rounding_mode) -{ - if (rounding_mode < FE_TONEAREST || rounding_mode > FE_TOMAXMAGNITUDE) - return 1; - - if (rounding_mode == FE_TOMAXMAGNITUDE) - rounding_mode = FE_TONEAREST; - - TODO_AARCH64(); - return 0; -} - -int feclearexcept(int exceptions) -{ - exceptions &= FE_ALL_EXCEPT; - - fenv_t current_env; - fegetenv(¤t_env); - - (void)exceptions; - TODO_AARCH64(); - - fesetenv(¤t_env); - return 0; -} - -int fetestexcept(int exceptions) -{ - (void)exceptions; - TODO_AARCH64(); -} - -int feraiseexcept(int exceptions) -{ - fenv_t env; - fegetenv(&env); - - exceptions &= FE_ALL_EXCEPT; - - (void)exceptions; - TODO_AARCH64(); -} -} diff --git a/Userland/Libraries/LibC/arch/aarch64/fenv.h b/Userland/Libraries/LibC/arch/aarch64/fenv.h deleted file mode 100644 index a17752abd86..00000000000 --- a/Userland/Libraries/LibC/arch/aarch64/fenv.h +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Copyright (c) 2024, Dan Klishch - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include - -#ifndef __aarch64__ -# error "This file should not be included on architectures other than AArch64." -#endif - -__BEGIN_DECLS - -// TODO: Implement this. -typedef struct fenv_t { - char __dummy; // NOTE: This silences -Wextern-c-compat. -} fenv_t; - -__END_DECLS diff --git a/Userland/Libraries/LibC/arch/aarch64/setjmp.S b/Userland/Libraries/LibC/arch/aarch64/setjmp.S deleted file mode 100644 index fe8f4c0cfb6..00000000000 --- a/Userland/Libraries/LibC/arch/aarch64/setjmp.S +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright (c) 2021, Nico Weber - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -.global _setjmp -.global setjmp -_setjmp: -setjmp: - # FIXME: Possibly incomplete. - ret - -.global _longjmp -.global longjmp -_longjmp: -longjmp: - # FIXME: Possibly incomplete. - ret - -.global _sigsetjmp -.global sigsetjmp -_sigsetjmp: -sigsetjmp: - # FIXME: Possibly incomplete. - ret diff --git a/Userland/Libraries/LibC/arch/fenv.h b/Userland/Libraries/LibC/arch/fenv.h deleted file mode 100644 index 0121f1afc31..00000000000 --- a/Userland/Libraries/LibC/arch/fenv.h +++ /dev/null @@ -1,17 +0,0 @@ -/* - * Copyright (c) 2024, Dan Klishch - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#if defined(__x86_64__) -# include -#elif defined(__aarch64__) -# include -#elif defined(__riscv) && __riscv_xlen == 64 -# include -#else -# error Unknown architecture -#endif diff --git a/Userland/Libraries/LibC/arch/riscv64/crti.S b/Userland/Libraries/LibC/arch/riscv64/crti.S deleted file mode 100644 index 97f3ba8057e..00000000000 --- a/Userland/Libraries/LibC/arch/riscv64/crti.S +++ /dev/null @@ -1 +0,0 @@ -# Intentionally empty. diff --git a/Userland/Libraries/LibC/arch/riscv64/crtn.S b/Userland/Libraries/LibC/arch/riscv64/crtn.S deleted file mode 100644 index 97f3ba8057e..00000000000 --- a/Userland/Libraries/LibC/arch/riscv64/crtn.S +++ /dev/null @@ -1 +0,0 @@ -# Intentionally empty. diff --git a/Userland/Libraries/LibC/arch/riscv64/fenv.cpp b/Userland/Libraries/LibC/arch/riscv64/fenv.cpp deleted file mode 100644 index 68e7bcea02d..00000000000 --- a/Userland/Libraries/LibC/arch/riscv64/fenv.cpp +++ /dev/null @@ -1,263 +0,0 @@ -/* - * Copyright (c) 2024, Tom Finet - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include - -static_assert(AssertSize()); - -// RISC-V F extension version 2.2 -// Table 11.1 (frm rounding mode encoding) -enum class RoundingMode : u8 { - // Round to Nearest, ties to Even - RNE = 0b000, - // Round towards Zero - RTZ = 0b001, - // Round Down (towards −∞) - RDN = 0b010, - // Round Up (towards +∞) - RUP = 0b011, - // Round to Nearest, ties to Max Magnitude - RMM = 0b100, - // Reserved for future use. - Reserved5 = 0b101, - Reserved6 = 0b110, - // In instruction’s rm field, selects dynamic rounding mode; In Rounding Mode register, Invalid. - DYN = 0b111, -}; - -static RoundingMode frm_from_feround(int c_rounding_mode) -{ - switch (c_rounding_mode) { - case FE_TONEAREST: - return RoundingMode::RNE; - case FE_TOWARDZERO: - return RoundingMode::RTZ; - case FE_DOWNWARD: - return RoundingMode::RDN; - case FE_UPWARD: - return RoundingMode::RUP; - case FE_TOMAXMAGNITUDE: - return RoundingMode::RMM; - default: - VERIFY_NOT_REACHED(); - } -} - -static int feround_from_frm(RoundingMode frm) -{ - switch (frm) { - case RoundingMode::RNE: - return FE_TONEAREST; - case RoundingMode::RTZ: - return FE_TOWARDZERO; - case RoundingMode::RDN: - return FE_DOWNWARD; - case RoundingMode::RUP: - return FE_UPWARD; - case RoundingMode::RMM: - return FE_TOMAXMAGNITUDE; - default: - // DYN is invalid in the frm register and therefore should never appear here. - case RoundingMode::DYN: - VERIFY_NOT_REACHED(); - } -} - -static RoundingMode get_rounding_mode() -{ - size_t rounding_mode; - asm volatile("frrm %0" - : "=r"(rounding_mode)); - return static_cast(rounding_mode); -} - -// Returns the old rounding mode, since we get that for free. -static RoundingMode set_rounding_mode(RoundingMode frm) -{ - size_t old_rounding_mode; - size_t const new_rounding_mode = to_underlying(frm); - asm volatile("fsrm %0, %1" - : "=r"(old_rounding_mode) - : "r"(new_rounding_mode)); - return static_cast(old_rounding_mode); -} - -// Figure 11.2 (fflags) -enum class AccruedExceptions : u8 { - None = 0, - // Inexact - NX = 1 << 0, - // Underflow - UF = 1 << 1, - // Overflow - OF = 1 << 2, - // Divide by Zero - DZ = 1 << 3, - // Invalid Operation - NV = 1 << 4, - All = NX | UF | OF | DZ | NV, -}; - -AK_ENUM_BITWISE_OPERATORS(AccruedExceptions); - -static AccruedExceptions fflags_from_fexcept(fexcept_t c_exceptions) -{ - AccruedExceptions exceptions = AccruedExceptions::None; - if ((c_exceptions & FE_INEXACT) != 0) - exceptions |= AccruedExceptions::NX; - if ((c_exceptions & FE_UNDERFLOW) != 0) - exceptions |= AccruedExceptions::UF; - if ((c_exceptions & FE_OVERFLOW) != 0) - exceptions |= AccruedExceptions::OF; - if ((c_exceptions & FE_DIVBYZERO) != 0) - exceptions |= AccruedExceptions::DZ; - if ((c_exceptions & FE_INVALID) != 0) - exceptions |= AccruedExceptions::NV; - - return exceptions; -} - -static fexcept_t fexcept_from_fflags(AccruedExceptions fflags) -{ - fexcept_t c_exceptions = 0; - if ((fflags & AccruedExceptions::NX) != AccruedExceptions::None) - c_exceptions |= FE_INEXACT; - if ((fflags & AccruedExceptions::UF) != AccruedExceptions::None) - c_exceptions |= FE_UNDERFLOW; - if ((fflags & AccruedExceptions::OF) != AccruedExceptions::None) - c_exceptions |= FE_OVERFLOW; - if ((fflags & AccruedExceptions::DZ) != AccruedExceptions::None) - c_exceptions |= FE_DIVBYZERO; - if ((fflags & AccruedExceptions::NV) != AccruedExceptions::None) - c_exceptions |= FE_INVALID; - - return c_exceptions; -} - -static AccruedExceptions get_accrued_exceptions() -{ - size_t fflags; - asm volatile("frflags %0" - : "=r"(fflags)); - return static_cast(fflags); -} - -// Returns the old exceptions, since we get them for free. -static AccruedExceptions set_accrued_exceptions(AccruedExceptions exceptions) -{ - size_t old_exceptions; - size_t const new_exceptions = to_underlying(exceptions); - asm volatile("fsflags %0, %1" - : "=r"(old_exceptions) - : "r"(new_exceptions)); - return static_cast(old_exceptions); -} - -static void clear_accrued_exceptions(AccruedExceptions exceptions) -{ - asm volatile("csrc fcsr, %0" ::"r"(to_underlying(exceptions))); -} - -extern "C" { - -int fegetenv(fenv_t* env) -{ - if (!env) - return 1; - - FlatPtr fcsr; - asm volatile("csrr %0, fcsr" - : "=r"(fcsr)); - env->fcsr = fcsr; - - return 0; -} - -int fesetenv(fenv_t const* env) -{ - if (!env) - return 1; - - FlatPtr fcsr = env->fcsr; - asm volatile("csrw fcsr, %0" ::"r"(fcsr)); - return 0; -} - -int feholdexcept(fenv_t* env) -{ - fegetenv(env); - - // RISC-V does not have trapping floating point exceptions. Therefore, feholdexcept just clears fflags. - clear_accrued_exceptions(AccruedExceptions::All); - return 0; -} - -int fesetexceptflag(fexcept_t const* except, int exceptions) -{ - if (!except) - return 1; - - exceptions &= FE_ALL_EXCEPT; - - auto exceptions_to_set = fflags_from_fexcept(*except) & fflags_from_fexcept(exceptions); - set_accrued_exceptions(exceptions_to_set); - - return 0; -} - -int fegetround() -{ - auto rounding_mode = get_rounding_mode(); - return feround_from_frm(rounding_mode); -} - -int fesetround(int rounding_mode) -{ - if (rounding_mode < FE_TONEAREST || rounding_mode > FE_TOMAXMAGNITUDE) - return 1; - - auto frm = frm_from_feround(rounding_mode); - set_rounding_mode(frm); - - return 0; -} - -int feclearexcept(int exceptions) -{ - exceptions &= FE_ALL_EXCEPT; - - auto exception_clear_flag = fflags_from_fexcept(exceptions); - // Use CSRRC to directly clear exception flags in fcsr which is faster. - // Conveniently, the exception flags are the lower bits, so we don't need to shift anything around. - clear_accrued_exceptions(exception_clear_flag); - - return 0; -} - -int fetestexcept(int exceptions) -{ - auto fflags = get_accrued_exceptions(); - auto mask = fflags_from_fexcept(exceptions); - return fexcept_from_fflags(fflags & mask); -} - -int feraiseexcept(int exceptions) -{ - fenv_t env; - fegetenv(&env); - - exceptions &= FE_ALL_EXCEPT; - - // RISC-V does not have trapping floating-point exceptions, so this function behaves as a simple exception setter. - set_accrued_exceptions(fflags_from_fexcept(exceptions)); - - return 0; -} -} diff --git a/Userland/Libraries/LibC/arch/riscv64/fenv.h b/Userland/Libraries/LibC/arch/riscv64/fenv.h deleted file mode 100644 index 805a362d627..00000000000 --- a/Userland/Libraries/LibC/arch/riscv64/fenv.h +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright (c) 2024, Dan Klishch - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include - -#if !defined(__riscv) || __riscv_xlen != 64 -# error "This file should not be included on architectures other than riscv64." -#endif - -__BEGIN_DECLS - -// Chapter numbers from RISC-V Unprivileged ISA V20191213 -// RISC-V F extension version 2.2, Figure 11.1 -typedef struct fenv_t { - union { - // 11.2: fcsr is always 32 bits, even for the D and Q extensions, since only the lowest byte of data is in use. - uint32_t fcsr; - struct { - // Accrued exceptions (fflags). - uint8_t inexact : 1; // NX - uint8_t underflow : 1; // UF - uint8_t overflow : 1; // OF - uint8_t divide_by_zero : 1; // DZ - uint8_t invalid_operation : 1; // NV - uint8_t rounding_mode : 3; // frm - uint32_t reserved : 24; - }; - }; -} fenv_t; - -__END_DECLS diff --git a/Userland/Libraries/LibC/arch/riscv64/setjmp.S b/Userland/Libraries/LibC/arch/riscv64/setjmp.S deleted file mode 100644 index f31760b123d..00000000000 --- a/Userland/Libraries/LibC/arch/riscv64/setjmp.S +++ /dev/null @@ -1,119 +0,0 @@ -/* - * Copyright (c) 2024, Sönke Holz - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include - -// -// /!\ Read setjmp.h before modifying this file! -// - -#define DID_SAVE_SIGNAL_MASK_SLOT (14 * 8) -#define SAVED_SIGNAL_MASK_SLOT (14 * 8 + 4) - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/setjmp.html -// int setjmp(jmp_buf env) -// int _setjmp(jmp_buf env) -.global _setjmp -.global setjmp -_setjmp: -setjmp: - li a1, 0 // Set savemask argument to 0 - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/sigsetjmp.html -// int sigsetjmp(sigjmp_buf env, int savemask) -.global sigsetjmp -sigsetjmp: - sw a1, DID_SAVE_SIGNAL_MASK_SLOT(a0) // Store savemask into did_save_signal_mask - sw zero, SAVED_SIGNAL_MASK_SLOT(a0) // Clear saved_signal_mask - beqz a1, .Lsaveregs - - addi sp, sp, -16 // Prepare ABI-compliant call to sigprocmask - sd a0, 0(sp) - sd ra, 8(sp) - - addi a2, a0, SAVED_SIGNAL_MASK_SLOT // Set argument oldset - li a1, 0 // Set argument set - li a0, 0 // Set argument how - call sigprocmask@plt - - ld ra, 8(sp) - ld a0, 0(sp) - addi sp, sp, 16 - -.Lsaveregs: - sd s0, 0*8(a0) // Save registers - sd s1, 1*8(a0) - sd s2, 2*8(a0) - sd s3, 3*8(a0) - sd s4, 4*8(a0) - sd s5, 5*8(a0) - sd s6, 6*8(a0) - sd s7, 7*8(a0) - sd s8, 8*8(a0) - sd s9, 9*8(a0) - sd s10, 10*8(a0) - sd s11, 11*8(a0) - - fsd fs0, 12*8(a0) // Save floating-point registers - fsd fs1, 13*8(a0) // NOTE: We only support ABI_FLEN=64 in LibELF/Validation.cpp, - fsd fs2, 14*8(a0) // so we only save the lower 64 bits of the fs* registers. - fsd fs3, 15*8(a0) - fsd fs4, 16*8(a0) - fsd fs5, 17*8(a0) - fsd fs6, 18*8(a0) - fsd fs7, 19*8(a0) - fsd fs8, 20*8(a0) - fsd fs9, 21*8(a0) - fsd fs10, 22*8(a0) - fsd fs11, 23*8(a0) - - sd sp, 24*8(a0) - sd ra, 25*8(a0) - - li a0, 0 - ret - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/longjmp.html -// void longjmp(jmp_buf env, int val) -// void _longjmp(jmp_buf env, int val) -.global _longjmp -.global longjmp -_longjmp: -longjmp: - ld s0, 0*8(a0) // Restore registers - ld s1, 1*8(a0) - ld s2, 2*8(a0) - ld s3, 3*8(a0) - ld s4, 4*8(a0) - ld s5, 5*8(a0) - ld s6, 6*8(a0) - ld s7, 7*8(a0) - ld s8, 8*8(a0) - ld s9, 9*8(a0) - ld s10, 10*8(a0) - ld s11, 11*8(a0) - - fld fs0, 12*8(a0) // Restore floating-point registers - fld fs1, 13*8(a0) // NOTE: We only support ABI_FLEN=64 in LibELF/Validation.cpp, - fld fs2, 14*8(a0) // so we only restore the lower 64 bits of the fs* registers. - fld fs3, 15*8(a0) - fld fs4, 16*8(a0) - fld fs5, 17*8(a0) - fld fs6, 18*8(a0) - fld fs7, 19*8(a0) - fld fs8, 20*8(a0) - fld fs9, 21*8(a0) - fld fs10, 22*8(a0) - fld fs11, 23*8(a0) - - ld sp, 24*8(a0) - ld ra, 25*8(a0) - - mv a0, a1 - bnez a0, .Lnonzero - li a0, 1 -.Lnonzero: - ret diff --git a/Userland/Libraries/LibC/arch/x86_64/crti.S b/Userland/Libraries/LibC/arch/x86_64/crti.S deleted file mode 100644 index af58391ae88..00000000000 --- a/Userland/Libraries/LibC/arch/x86_64/crti.S +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright (c) 2021, Daniel Bertalan - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -.section .init, "ax", @progbits -.align 4 -.global _init -.type _init, @function -_init: - pushq %rbp - movq %rsp, %rbp - andq $-16, %rsp - -.section .fini, "ax", @progbits -.align 4 -.global _fini -.type _fini, @function -_fini: - pushq %rbp - movq %rsp, %rbp - andq $-16, %rsp diff --git a/Userland/Libraries/LibC/arch/x86_64/crtn.S b/Userland/Libraries/LibC/arch/x86_64/crtn.S deleted file mode 100644 index 71d6b29b546..00000000000 --- a/Userland/Libraries/LibC/arch/x86_64/crtn.S +++ /dev/null @@ -1,15 +0,0 @@ -/* - * Copyright (c) 2021, Daniel Bertalan - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -.section .init, "ax", @progbits - movq %rbp, %rsp - popq %rbp - retq - -.section .fini, "ax", @progbits - movq %rbp, %rsp - popq %rbp - retq diff --git a/Userland/Libraries/LibC/arch/x86_64/fenv.cpp b/Userland/Libraries/LibC/arch/x86_64/fenv.cpp deleted file mode 100644 index 55d70cf5c36..00000000000 --- a/Userland/Libraries/LibC/arch/x86_64/fenv.cpp +++ /dev/null @@ -1,191 +0,0 @@ -/* - * Copyright (c) 2021-2024, Mițca Dumitru - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include - -// This is the size of the floating point environment image in protected mode -static_assert(sizeof(__x87_floating_point_environment) == 28); - -static u16 read_status_register() -{ - u16 status_register; - asm volatile("fnstsw %0" - : "=m"(status_register)); - return status_register; -} - -static u16 read_control_word() -{ - u16 control_word; - asm volatile("fnstcw %0" - : "=m"(control_word)); - return control_word; -} - -static void set_control_word(u16 new_control_word) -{ - asm volatile("fldcw %0" ::"m"(new_control_word)); -} - -static u32 read_mxcsr() -{ - u32 mxcsr; - asm volatile("stmxcsr %0" - : "=m"(mxcsr)); - return mxcsr; -} - -static void set_mxcsr(u32 new_mxcsr) -{ - asm volatile("ldmxcsr %0" ::"m"(new_mxcsr)); -} - -static constexpr u32 default_mxcsr_value = 0x1f80; - -extern "C" { - -int fegetenv(fenv_t* env) -{ - if (!env) - return 1; - - asm volatile("fnstenv %0" - : "=m"(env->__x87_fpu_env)::"memory"); - - env->__mxcsr = read_mxcsr(); - return 0; -} - -int fesetenv(fenv_t const* env) -{ - if (!env) - return 1; - - if (env == FE_DFL_ENV) { - asm volatile("finit"); - set_mxcsr(default_mxcsr_value); - return 0; - } - - asm volatile("fldenv %0" ::"m"(env->__x87_fpu_env) - : "memory"); - - set_mxcsr(env->__mxcsr); - return 0; -} - -int feholdexcept(fenv_t* env) -{ - fegetenv(env); - - fenv_t current_env; - fegetenv(¤t_env); - - current_env.__x87_fpu_env.__status_word &= ~FE_ALL_EXCEPT; - current_env.__x87_fpu_env.__status_word &= ~(1 << 7); // Clear the "Exception Status Summary" bit - current_env.__x87_fpu_env.__control_word &= FE_ALL_EXCEPT; // Masking these bits stops the corresponding exceptions from being generated according to the Intel Programmer's Manual - - fesetenv(¤t_env); - return 0; -} - -int fesetexceptflag(fexcept_t const* except, int exceptions) -{ - if (!except) - return 1; - - fenv_t current_env; - fegetenv(¤t_env); - - exceptions &= FE_ALL_EXCEPT; - current_env.__x87_fpu_env.__status_word &= exceptions; - current_env.__x87_fpu_env.__status_word &= ~(1 << 7); // Make sure exceptions don't get raised - - fesetenv(¤t_env); - return 0; -} - -int fegetround() -{ - // There's no way to signal whether the SSE rounding mode and x87 ones are different, so we assume they're the same - return (read_control_word() >> 10) & 3; -} - -int fesetround(int rounding_mode) -{ - if (rounding_mode < FE_TONEAREST || rounding_mode > FE_TOMAXMAGNITUDE) - return 1; - - if (rounding_mode == FE_TOMAXMAGNITUDE) - rounding_mode = FE_TONEAREST; - - auto control_word = read_control_word(); - - control_word &= ~(3 << 10); - control_word |= rounding_mode << 10; - - set_control_word(control_word); - - auto mxcsr = read_mxcsr(); - - mxcsr &= ~(3 << 13); - mxcsr |= rounding_mode << 13; - - set_mxcsr(mxcsr); - return 0; -} - -int feclearexcept(int exceptions) -{ - exceptions &= FE_ALL_EXCEPT; - - fenv_t current_env; - fegetenv(¤t_env); - - current_env.__x87_fpu_env.__status_word &= ~exceptions; - current_env.__x87_fpu_env.__status_word &= ~(1 << 7); // Clear the "Exception Status Summary" bit - - fesetenv(¤t_env); - return 0; -} - -int fetestexcept(int exceptions) -{ - u16 status_register = read_status_register() & FE_ALL_EXCEPT; - exceptions &= FE_ALL_EXCEPT; - - return status_register & exceptions; -} - -int feraiseexcept(int exceptions) -{ - fenv_t env; - fegetenv(&env); - - exceptions &= FE_ALL_EXCEPT; - - // While the order in which the exceptions is raised is unspecified, FE_OVERFLOW and FE_UNDERFLOW must be raised before FE_INEXACT, so handle that case in this branch - if (exceptions & FE_INEXACT) { - env.__x87_fpu_env.__status_word &= ((u16)exceptions & ~FE_INEXACT); - fesetenv(&env); - asm volatile("fwait"); // "raise" the exception by performing a floating point operation - - fegetenv(&env); - env.__x87_fpu_env.__status_word &= FE_INEXACT; - fesetenv(&env); - asm volatile("fwait"); - - return 0; - } - - env.__x87_fpu_env.__status_word &= exceptions; - fesetenv(&env); - asm volatile("fwait"); - - return 0; -} -} diff --git a/Userland/Libraries/LibC/arch/x86_64/fenv.h b/Userland/Libraries/LibC/arch/x86_64/fenv.h deleted file mode 100644 index 68d67d7958b..00000000000 --- a/Userland/Libraries/LibC/arch/x86_64/fenv.h +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright (c) 2024, Dan Klishch - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include - -#if !defined(__x86_64__) -# error "This file should not be included on architectures other than x86_64." -#endif - -__BEGIN_DECLS - -struct __x87_floating_point_environment { - uint16_t __control_word; - uint16_t __reserved1; - uint16_t __status_word; - uint16_t __reserved2; - uint16_t __tag_word; - uint16_t __reserved3; - uint32_t __fpu_ip_offset; - uint16_t __fpu_ip_selector; - uint16_t __opcode : 11; - uint16_t __reserved4 : 5; - uint32_t __fpu_data_offset; - uint16_t __fpu_data_selector; - uint16_t __reserved5; -}; - -typedef struct fenv_t { - struct __x87_floating_point_environment __x87_fpu_env; - uint32_t __mxcsr; -} fenv_t; - -__END_DECLS diff --git a/Userland/Libraries/LibC/arch/x86_64/memset.S b/Userland/Libraries/LibC/arch/x86_64/memset.S deleted file mode 100644 index 1c8ef221c07..00000000000 --- a/Userland/Libraries/LibC/arch/x86_64/memset.S +++ /dev/null @@ -1,196 +0,0 @@ -/* - * Copyright (c) 2022, Daniel Bertalan - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -// Optimized x86-64 memset routine based on the following post from the MSRC blog: -// https://msrc-blog.microsoft.com/2021/01/11/building-faster-amd64-memset-routines -// -// This algorithm -// - makes use of REP MOVSB on CPUs where it is fast (a notable exception is -// qemu's TCG backend used in CI) -// - uses SSE stores otherwise -// - performs quick branchless stores for sizes < 64 bytes where REP STOSB would have -// a large overhead - -.intel_syntax noprefix - -.global memset_sse2_erms -.type memset_sse2_erms, @function -.p2align 4 - -memset_sse2_erms: - // Fill all bytes of esi and xmm0 with the given character. - movzx esi, sil - imul esi, 0x01010101 - movd xmm0, esi - pshufd xmm0, xmm0, 0 - - // Store the original address for the return value. - mov rax, rdi - - cmp rdx, 64 - jb .Lunder_64 - - // Limit taken from the article. Could be lower (256 or 512) if we want to - // tune it for the latest CPUs. - cmp rdx, 800 - jb .Lbig - -.Lerms: - // We're going to align the pointer to 64 bytes, and then use REP STOSB. - - // Fill the first 64 bytes of the memory using SSE stores. - movups [rdi], xmm0 - movups [rdi + 16], xmm0 - movups [rdi + 32], xmm0 - movups [rdi + 48], xmm0 - - // Store the address of the last byte in r8. - lea r8, [rdi + rdx] - - // Align the start pointer to 64 bytes. - add rdi, 63 - and rdi, ~63 - - // Calculate the number of remaining bytes to store. - mov rcx, r8 - sub rcx, rdi - - // Use REP STOSB to fill the rest. This is implemented in microcode on - // recent Intel and AMD CPUs, and can automatically use the widest stores - // available in the CPU, so it's strictly faster than SSE for sizes of more - // than a couple hundred bytes. - xchg rax, rsi - rep stosb - mov rax, rsi - - ret - -.global memset_sse2 -.type memset_sse2, @function -.p2align 4 - -memset_sse2: - // Fill all bytes of esi and xmm0 with the given character. - movzx esi, sil - imul rsi, 0x01010101 - movd xmm0, esi - pshufd xmm0, xmm0, 0 - - // Store the original address for the return value. - mov rax, rdi - - cmp rdx, 64 - jb .Lunder_64 - -.Lbig: - // We're going to align the pointer to 16 bytes, fill 4*16 bytes in a hot - // loop, and then fill the last 48-64 bytes separately to take care of any - // trailing bytes. - - // Fill the first 16 bytes, which might be unaligned. - movups [rdi], xmm0 - - // Calculate the first 16 byte aligned address for the SSE stores. - lea rsi, [rdi + 16] - and rsi, ~15 - - // Calculate the number of remaining bytes. - sub rdi, rsi - add rdx, rdi - - // Calculate the last aligned address for trailing stores such that - // 48-64 bytes are left. - lea rcx, [rsi + rdx - 48] - and rcx, ~15 - - // Calculate the address 16 bytes from the end. - lea r8, [rsi + rdx - 16] - - cmp rdx, 64 - jb .Ltrailing - -.Lbig_loop: - // Fill 4*16 bytes in a loop. - movaps [rsi], xmm0 - movaps [rsi + 16], xmm0 - movaps [rsi + 32], xmm0 - movaps [rsi + 48], xmm0 - - add rsi, 64 - cmp rsi, rcx - jb .Lbig_loop - -.Ltrailing: - // We have 48-64 bytes left. Fill the first 48 and the last 16 bytes. - movaps [rcx], xmm0 - movaps [rcx + 16], xmm0 - movaps [rcx + 32], xmm0 - movups [r8], xmm0 - - ret - -.Lunder_64: - cmp rdx, 16 - jb .Lunder_16 - - // We're going to fill 16-63 bytes using variable sized branchess stores. - // Although this means that we might set the same byte up to 4 times, we - // can avoid branching which is expensive compared to straight-line code. - - // Calculate the address of the last SSE store. - lea r8, [rdi + rdx - 16] - - // Set rdx to 32 if there are >= 32 bytes, otherwise let its value be 0. - and rdx, 32 - - // Fill the first 16 bytes. - movups [rdi], xmm0 - - // Set rdx to 16 if there are >= 32 bytes, otherwise let its value be 0. - shr rdx, 1 - - // Fill the last 16 bytes. - movups [r8], xmm0 - - // Fill bytes 16 - 32 if there are more than 32 bytes, otherwise fill the first 16 again. - movups [rdi + rdx], xmm0 - - // Fill bytes (n-32) - (n-16) if there are n >= 32 bytes, otherwise fill the last 16 again. - neg rdx - movups [r8 + rdx], xmm0 - - ret - -.Lunder_16: - cmp rdx, 4 - jb .Lunder_4 - - // We're going to fill 4-15 bytes using variable sized branchless stores like above. - lea r8, [rdi + rdx - 4] - and rdx, 8 - mov [rdi], esi - shr rdx, 1 - mov [r8], esi - mov [rdi + rdx], esi - neg rdx - mov [r8 + rdx], esi - ret - -.Lunder_4: - cmp rdx, 1 - jb .Lend - - // Fill the first byte. - mov [rdi], sil - - jbe .Lend - - // The size is 2 or 3 bytes. Fill the second and the last one. - mov [rdi + 1], sil - mov [rdi + rdx - 1], sil - -.Lend: - ret diff --git a/Userland/Libraries/LibC/arch/x86_64/memset.cpp b/Userland/Libraries/LibC/arch/x86_64/memset.cpp deleted file mode 100644 index 8ff6667e6f3..00000000000 --- a/Userland/Libraries/LibC/arch/x86_64/memset.cpp +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright (c) 2022, Daniel Bertalan - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include - -extern "C" { - -extern void* memset_sse2(void*, int, size_t); -extern void* memset_sse2_erms(void*, int, size_t); - -constexpr u32 tcg_signature_ebx = 0x54474354; -constexpr u32 tcg_signature_ecx = 0x43544743; -constexpr u32 tcg_signature_edx = 0x47435447; - -// Bit 9 of ebx in cpuid[eax = 7] indicates support for "Enhanced REP MOVSB/STOSB" -constexpr u32 cpuid_7_ebx_bit_erms = 1 << 9; - -namespace { -[[gnu::used]] decltype(&memset) resolve_memset() -{ - u32 eax, ebx, ecx, edx; - - __cpuid(0x40000000, eax, ebx, ecx, edx); - bool is_tcg = ebx == tcg_signature_ebx && ecx == tcg_signature_ecx && edx == tcg_signature_edx; - - // Although TCG reports ERMS support, testing shows that rep stosb performs strictly worse than - // SSE copies on all data sizes except <= 4 bytes. - if (is_tcg) - return memset_sse2; - - __cpuid_count(7, 0, eax, ebx, ecx, edx); - if (ebx & cpuid_7_ebx_bit_erms) - return memset_sse2_erms; - - return memset_sse2; -} -} - -#if !defined(AK_COMPILER_CLANG) && !defined(_DYNAMIC_LOADER) -[[gnu::ifunc("resolve_memset")]] void* memset(void*, int, size_t); -#else -// DynamicLoader can't self-relocate IFUNCs. -// FIXME: There's a circular dependency between LibC and libunwind when built with Clang, -// so the IFUNC resolver could be called before LibC has been relocated, returning bogus addresses. -void* memset(void* dest_ptr, int c, size_t n) -{ - static decltype(&memset) s_impl = nullptr; - if (s_impl == nullptr) - s_impl = resolve_memset(); - - return s_impl(dest_ptr, c, n); -} -#endif -} diff --git a/Userland/Libraries/LibC/arch/x86_64/setjmp.S b/Userland/Libraries/LibC/arch/x86_64/setjmp.S deleted file mode 100644 index db33fcf28e8..00000000000 --- a/Userland/Libraries/LibC/arch/x86_64/setjmp.S +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Copyright (c) 2021, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include - -// -// /!\ Read setjmp.h before modifying this file! -// - -.global _setjmp -.global setjmp -_setjmp: -setjmp: - mov $0, %esi // Set val argument to 0 - -.global sigsetjmp -sigsetjmp: - mov %esi, 64(%rdi) // Store val into did_save_signal_mask - movl $0, 68(%rdi) // Clear saved_signal_mask - test %esi, %esi - jz .Lsaveregs - - push %rbp // Prepare ABI-compliant call to sigprocmask - mov %rsp, %rbp - push %rdi - lea 68(%rdi), %rdx // Set argument oldset - mov $0, %rsi // Set argument set - mov $0, %rdi // Set argument how - call sigprocmask@plt - pop %rdi - pop %rbp - -.Lsaveregs: - mov %rbx, (0 * 8)(%rdi) // Save registers - mov %r12, (1 * 8)(%rdi) - mov %r13, (2 * 8)(%rdi) - mov %r14, (3 * 8)(%rdi) - mov %r15, (4 * 8)(%rdi) - mov %rbp, (5 * 8)(%rdi) - mov %rsp, (6 * 8)(%rdi) - mov (%rsp), %rax // Grab return address - mov %rax, (7 * 8)(%rdi) - xor %eax, %eax - ret - -.global _longjmp -.global longjmp -_longjmp: -longjmp: - mov %esi, %eax - test %eax, %eax - jnz .Lnonzero - mov $1, %eax - -.Lnonzero: - mov (0 * 8)(%rdi), %rbx // Restore registers - mov (1 * 8)(%rdi), %r12 - mov (2 * 8)(%rdi), %r13 - mov (3 * 8)(%rdi), %r14 - mov (4 * 8)(%rdi), %r15 - mov (5 * 8)(%rdi), %rbp - // - // Until this point, the stack is still from the caller. - // - mov (6 * 8)(%rdi), %rsp - mov (7 * 8)(%rdi), %rcx - mov %rcx, (%rsp) // Patch return address - // - // From this point on, the former stack has been restored. - // - ret diff --git a/Userland/Libraries/LibC/arpa/inet.cpp b/Userland/Libraries/LibC/arpa/inet.cpp deleted file mode 100644 index 028d67ef2f3..00000000000 --- a/Userland/Libraries/LibC/arpa/inet.cpp +++ /dev/null @@ -1,101 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include - -extern "C" { - -char const* inet_ntop(int af, void const* src, char* dst, socklen_t len) -{ - if (af == AF_INET) { - if (len < INET_ADDRSTRLEN) { - errno = ENOSPC; - return nullptr; - } - auto* bytes = (unsigned char const*)src; - snprintf(dst, len, "%u.%u.%u.%u", bytes[0], bytes[1], bytes[2], bytes[3]); - return (char const*)dst; - } else if (af == AF_INET6) { - if (len < INET6_ADDRSTRLEN) { - errno = ENOSPC; - return nullptr; - } - auto str_or_error = IPv6Address(((in6_addr const*)src)->s6_addr).to_string(); - if (str_or_error.is_error()) { - errno = ENOMEM; - return nullptr; - } - auto str = str_or_error.release_value(); - if (!str.bytes_as_string_view().copy_characters_to_buffer(dst, len)) { - errno = ENOSPC; - return nullptr; - } - return (char const*)dst; - } - - errno = EAFNOSUPPORT; - return nullptr; -} - -int inet_pton(int af, char const* src, void* dst) -{ - if (af == AF_INET) { - unsigned a, b, c, d; - int count = sscanf(src, "%u.%u.%u.%u", &a, &b, &c, &d); - if (count != 4) { - errno = EINVAL; - return 0; - } - union { - struct { - uint8_t a; - uint8_t b; - uint8_t c; - uint8_t d; - }; - uint32_t l; - } u; - u.a = a; - u.b = b; - u.c = c; - u.d = d; - *(uint32_t*)dst = u.l; - return 1; - } else if (af == AF_INET6) { - auto addr = IPv6Address::from_string({ src, strlen(src) }); - if (!addr.has_value()) { - errno = EINVAL; - return 0; - } - - memcpy(dst, addr->to_in6_addr_t(), sizeof(in6_addr)); - return 1; - } - - errno = EAFNOSUPPORT; - return -1; -} - -in_addr_t inet_addr(char const* str) -{ - in_addr_t tmp {}; - int rc = inet_pton(AF_INET, str, &tmp); - if (rc <= 0) - return INADDR_NONE; - return tmp; -} - -char* inet_ntoa(struct in_addr in) -{ - static char buffer[32]; - inet_ntop(AF_INET, &in.s_addr, buffer, sizeof(buffer)); - return buffer; -} -} diff --git a/Userland/Libraries/LibC/arpa/inet.h b/Userland/Libraries/LibC/arpa/inet.h deleted file mode 100644 index fcfbc3ec3f6..00000000000 --- a/Userland/Libraries/LibC/arpa/inet.h +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -// Includes essentially mandated by POSIX: -// https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/arpa_inet.h.html -#include -#include - -#include -#include - -__BEGIN_DECLS - -#define INET_ADDRSTRLEN 16 -#define INET6_ADDRSTRLEN 46 - -char const* inet_ntop(int af, void const* src, char* dst, socklen_t); -int inet_pton(int af, char const* src, void* dst); - -static inline int inet_aton(char const* cp, struct in_addr* inp) -{ - return inet_pton(AF_INET, cp, inp); -} - -char* inet_ntoa(struct in_addr); - -__END_DECLS diff --git a/Userland/Libraries/LibC/assert.cpp b/Userland/Libraries/LibC/assert.cpp deleted file mode 100644 index cc54d4bdc16..00000000000 --- a/Userland/Libraries/LibC/assert.cpp +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -extern "C" { - -extern bool __stdio_is_initialized; - -void __assertion_failed(char const* msg) -{ - if (__heap_is_stable) { - dbgln("ASSERTION FAILED: {}", msg); - if (__stdio_is_initialized) - warnln("ASSERTION FAILED: {}", msg); - } - - Syscall::SC_set_coredump_metadata_params params { - { "assertion", strlen("assertion") }, - { msg, strlen(msg) }, - }; - syscall(SC_prctl, PR_SET_COREDUMP_METADATA_VALUE, ¶ms, nullptr, nullptr); - abort(); -} -} diff --git a/Userland/Libraries/LibC/assert.h b/Userland/Libraries/LibC/assert.h deleted file mode 100644 index 6e378967d8a..00000000000 --- a/Userland/Libraries/LibC/assert.h +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#ifndef _ASSERT_H -# define _ASSERT_H - -# define __stringify_helper(x) #x -# define __stringify(x) __stringify_helper(x) - -# ifndef __cplusplus -# define static_assert _Static_assert -# endif -#endif - -#include - -#undef assert - -__BEGIN_DECLS - -#ifndef NDEBUG -__attribute__((noreturn)) void __assertion_failed(char const* msg); -# define assert(expr) \ - (__builtin_expect(!(expr), 0) \ - ? __assertion_failed(#expr "\n" __FILE__ ":" __stringify(__LINE__)) \ - : (void)0) - -#else -# define assert(expr) ((void)(0)) -#endif - -__END_DECLS diff --git a/Userland/Libraries/LibC/bits/FILE.h b/Userland/Libraries/LibC/bits/FILE.h deleted file mode 100644 index 4e626f44be5..00000000000 --- a/Userland/Libraries/LibC/bits/FILE.h +++ /dev/null @@ -1,18 +0,0 @@ -/* - * Copyright (c) 2018-2020, Jesse Buhagiar - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include - -#define BUFSIZ 1024 - -__BEGIN_DECLS - -typedef struct FILE FILE; - -__END_DECLS diff --git a/Userland/Libraries/LibC/bits/dlfcn_integration.h b/Userland/Libraries/LibC/bits/dlfcn_integration.h deleted file mode 100644 index 00f3776b105..00000000000 --- a/Userland/Libraries/LibC/bits/dlfcn_integration.h +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright (c) 2021, Gunnar Beutner - * Copyright (c) 2022, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include - -struct DlErrorMessage { - DlErrorMessage(ByteString&& other) - : text(move(other)) - { - } - - // The virtual destructor is required because we're passing this - // struct to the dynamic loader - whose operator delete differs - // from the one in libc.so - virtual ~DlErrorMessage() = default; - - ByteString text; -}; - -struct __Dl_info; -typedef struct __Dl_info Dl_info; diff --git a/Userland/Libraries/LibC/bits/getopt.h b/Userland/Libraries/LibC/bits/getopt.h deleted file mode 100644 index 217041f5b98..00000000000 --- a/Userland/Libraries/LibC/bits/getopt.h +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright (c) 2023, the SerenityOS contributors. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include - -__BEGIN_DECLS - -// If opterr is set (the default), print error messages to stderr. -extern int opterr; -// On errors, optopt is set to the erroneous *character*. -extern int optopt; -// Index of the next argument to process upon a getopt*() call. -extern int optind; -// If set, reset the internal state kept by getopt*(). You may also want to set -// optind to 1 in that case. -extern int optreset; -// After parsing an option that accept an argument, set to point to the argument -// value. -extern char* optarg; - -int getopt(int argc, char* const* argv, char const* short_options); -int getsubopt(char** optionp, char* const* tokens, char** valuep); - -__END_DECLS diff --git a/Userland/Libraries/LibC/bits/mutex_locker.h b/Userland/Libraries/LibC/bits/mutex_locker.h deleted file mode 100644 index e3771c38180..00000000000 --- a/Userland/Libraries/LibC/bits/mutex_locker.h +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright (c) 2021, Daniel Bertalan - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include - -// We don't want to bring LibThreading headers into LibC, so we use plain -// pthread mutexes and this RAII guard. -namespace LibC { - -class [[nodiscard]] MutexLocker { -public: - explicit MutexLocker(pthread_mutex_t& mutex) - : m_mutex(mutex) - { - lock(); - } - - ~MutexLocker() - { - unlock(); - } - - void lock() { pthread_mutex_lock(&m_mutex); } - void unlock() { pthread_mutex_unlock(&m_mutex); } - -private: - pthread_mutex_t& m_mutex; -}; - -} diff --git a/Userland/Libraries/LibC/bits/posix1_lim.h b/Userland/Libraries/LibC/bits/posix1_lim.h deleted file mode 100644 index 254687852d0..00000000000 --- a/Userland/Libraries/LibC/bits/posix1_lim.h +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright (c) 2020, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#define _POSIX_AIO_LISTIO_MAX 2 /* The number of I/O operations that can be specified in a list I/O call. */ -#define _POSIX_AIO_MAX 1 /* The number of outstanding asynchronous I/O operations. */ -#define _POSIX_ARG_MAX 4096 /* Maximum length of argument to the exec functions including environment data. */ -#define _POSIX_CHILD_MAX 25 /* Maximum number of simultaneous processes per real user ID. */ -#define _POSIX_DELAYTIMER_MAX 32 /* The number of timer expiration overruns. */ -#define _POSIX_HOST_NAME_MAX 255 /* Maximum length of a host name (not including the terminating null) as returned from the gethostname() function. */ -#define _POSIX_LINK_MAX 8 /* Maximum number of links to a single file. */ -#define _POSIX_LOGIN_NAME_MAX 9 /* The size of the storage required for a login name, in bytes, including the terminating null. */ -#define _POSIX_MAX_CANON 255 /* Maximum number of bytes in a terminal canonical input queue. */ -#define _POSIX_MAX_INPUT 255 /* Maximum number of bytes allowed in a terminal input queue. */ -#define _POSIX_MQ_OPEN_MAX 8 /* The number of message queues that can be open for a single process.) */ -#define _POSIX_MQ_PRIO_MAX 32 /* The maximum number of message priorities supported by the implementation. */ -#define _POSIX_NAME_MAX 14 /* Maximum number of bytes in a filename (not including terminating null). */ -#define _POSIX_NGROUPS_MAX 8 /* Maximum number of simultaneous supplementary group IDs per process. */ -#define _POSIX_OPEN_MAX 20 /* Maximum number of files that one process can have open at any one time. */ -#define _POSIX_PATH_MAX 256 /* Maximum number of bytes in a pathname. */ -#define _POSIX_PIPE_BUF 512 /* Maximum number of bytes that is guaranteed to be atomic when writing to a pipe. */ -#define _POSIX_RE_DUP_MAX 255 /* The number of repeated occurrences of a BRE permitted by the regexec() and regcomp() functions when using the interval notation #define \(m,n\}; see BREs Matching Multiple Characters. */ -#define _POSIX_RTSIG_MAX 8 /* The number of realtime signal numbers reserved for application use. */ -#define _POSIX_SEM_NSEMS_MAX 256 /* The number of semaphores that a process may have. */ -#define _POSIX_SEM_VALUE_MAX 32767 /* The maximum value a semaphore may have. */ -#define _POSIX_SIGQUEUE_MAX 32 /* The number of queued signals that a process may send and have pending at the receiver(s) at any time. */ -#define _POSIX_SSIZE_MAX 32767 /* The value that can be stored in an object of type ssize_t. */ -#define _POSIX_SS_REPL_MAX 4 /* The number of replenishment operations that may be simultaneously pending for a particular sporadic server scheduler. */ -#define _POSIX_STREAM_MAX 8 /* The number of streams that one process can have open at one time. */ -#define _POSIX_SYMLINK_MAX 255 /* The number of bytes in a symbolic link. */ -#define _POSIX_SYMLOOP_MAX 8 /* The number of symbolic links that can be traversed in the resolution of a pathname in the absence of a loop. */ -#define _POSIX_THREAD_DESTRUCTOR_ITERATIONS 4 /* The number of attempts made to destroy a thread's thread-specific data values on thread exit. */ -#define _POSIX_THREAD_KEYS_MAX 128 /* The number of data keys per process. */ -#define _POSIX_THREAD_THREADS_MAX 64 /* The number of threads per process. */ -#define _POSIX_TIMER_MAX 32 /* The per-process number of timers. */ -#define _POSIX_TRACE_EVENT_NAME_MAX 30 /* The length in bytes of a trace event name. */ -#define _POSIX_TRACE_NAME_MAX 8 /* The length in bytes of a trace generation version string or a trace stream name. */ -#define _POSIX_TRACE_SYS_MAX 8 /* The number of trace streams that may simultaneously exist in the system. */ -#define _POSIX_TRACE_USER_EVENT_MAX 32 /* The number of user trace event type identifiers that may simultaneously exist in a traced process, including the predefined user trace event POSIX_TRACE_UNNAMED_USER_EVENT. */ -#define _POSIX_TTY_NAME_MAX 9 /* The size of the storage required for a terminal device name, in bytes, including the terminating null. */ -#define _POSIX_TZNAME_MAX 6 /* Maximum number of bytes supported for the name of a timezone (not of the TZ variable). */ diff --git a/Userland/Libraries/LibC/bits/pthread_cancel.h b/Userland/Libraries/LibC/bits/pthread_cancel.h deleted file mode 100644 index 174c1bd9a62..00000000000 --- a/Userland/Libraries/LibC/bits/pthread_cancel.h +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright (c) 2022, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include - -__BEGIN_DECLS - -// This is our hook for cancellation points. -#ifdef _DYNAMIC_LOADER -inline void __pthread_maybe_cancel(void) -{ -} -#else -void __pthread_maybe_cancel(void); -#endif - -__END_DECLS diff --git a/Userland/Libraries/LibC/bits/pthread_integration.h b/Userland/Libraries/LibC/bits/pthread_integration.h deleted file mode 100644 index cdc58658c4b..00000000000 --- a/Userland/Libraries/LibC/bits/pthread_integration.h +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright (c) 2021, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include - -__BEGIN_DECLS - -void __pthread_fork_prepare(void); -void __pthread_fork_child(void); -void __pthread_fork_parent(void); -void __pthread_fork_atfork_register_prepare(void (*)(void)); -void __pthread_fork_atfork_register_parent(void (*)(void)); -void __pthread_fork_atfork_register_child(void (*)(void)); - -int __pthread_mutex_lock_pessimistic_np(pthread_mutex_t*); - -typedef void (*KeyDestructor)(void*); - -void __pthread_key_destroy_for_current_thread(void); - -#define __PTHREAD_MUTEX_NORMAL 0 -#define __PTHREAD_MUTEX_RECURSIVE 1 -#define __PTHREAD_MUTEX_INITIALIZER \ - { \ - 0, 0, 0, __PTHREAD_MUTEX_NORMAL \ - } - -#define __PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP \ - { \ - 0, 0, 0, __PTHREAD_MUTEX_RECURSIVE \ - } - -__END_DECLS diff --git a/Userland/Libraries/LibC/bits/search.h b/Userland/Libraries/LibC/bits/search.h deleted file mode 100644 index 5807267ad99..00000000000 --- a/Userland/Libraries/LibC/bits/search.h +++ /dev/null @@ -1,18 +0,0 @@ -/* - * Copyright (c) 2021, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -// This is technically an implementation detail, but we require this for testing. -// The key always has to be the first struct member. -struct search_tree_node { - void const* key; - struct search_tree_node* left; - struct search_tree_node* right; -}; - -struct search_tree_node* new_tree_node(void const* key); -void delete_node_recursive(struct search_tree_node* node); diff --git a/Userland/Libraries/LibC/bits/sighow.h b/Userland/Libraries/LibC/bits/sighow.h deleted file mode 100644 index 73d1a003f7e..00000000000 --- a/Userland/Libraries/LibC/bits/sighow.h +++ /dev/null @@ -1,11 +0,0 @@ -/* - * Copyright (c) 2021, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#define SIG_BLOCK 0 -#define SIG_UNBLOCK 1 -#define SIG_SETMASK 2 diff --git a/Userland/Libraries/LibC/bits/stdint.h b/Userland/Libraries/LibC/bits/stdint.h deleted file mode 100644 index db63bbaddb5..00000000000 --- a/Userland/Libraries/LibC/bits/stdint.h +++ /dev/null @@ -1,141 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include - -__BEGIN_DECLS - -typedef __UINT64_TYPE__ uint64_t; -typedef __UINT32_TYPE__ uint32_t; -typedef __UINT16_TYPE__ uint16_t; -typedef __UINT8_TYPE__ uint8_t; - -typedef __INT64_TYPE__ int64_t; -typedef __INT32_TYPE__ int32_t; -typedef __INT16_TYPE__ int16_t; -typedef __INT8_TYPE__ int8_t; - -typedef __UINT_FAST8_TYPE__ uint_fast8_t; -typedef __UINT_FAST16_TYPE__ uint_fast16_t; -typedef __UINT_FAST32_TYPE__ uint_fast32_t; -typedef __UINT_FAST64_TYPE__ uint_fast64_t; - -typedef __INT_FAST8_TYPE__ int_fast8_t; -typedef __INT_FAST16_TYPE__ int_fast16_t; -typedef __INT_FAST32_TYPE__ int_fast32_t; -typedef __INT_FAST64_TYPE__ int_fast64_t; - -typedef __UINT_LEAST8_TYPE__ uint_least8_t; -typedef __UINT_LEAST16_TYPE__ uint_least16_t; -typedef __UINT_LEAST32_TYPE__ uint_least32_t; -typedef __UINT_LEAST64_TYPE__ uint_least64_t; - -typedef __INT_LEAST8_TYPE__ int_least8_t; -typedef __INT_LEAST16_TYPE__ int_least16_t; -typedef __INT_LEAST32_TYPE__ int_least32_t; -typedef __INT_LEAST64_TYPE__ int_least64_t; - -#define __int8_t_defined 1 -#define __uint8_t_defined 1 -#define __int16_t_defined 1 -#define __uint16_t_defined 1 -#define __int32_t_defined 1 -#define __uint32_t_defined 1 -#define __int64_t_defined 1 -#define __uint64_t_defined 1 - -#define INT8_C(x) x -#define UINT8_C(x) x - -#define INT16_C(x) x -#define UINT16_C(x) x - -#define INT32_C(x) x -#define UINT32_C(x) x##U - -#ifdef __clang__ -# define __int_c_concat(a, b) a##b -# define __int_c(var, suffix) __int_c_concat(var, suffix) - -# define INT64_C(x) __int_c(x, __INT64_C_SUFFIX__) -# define UINT64_C(x) __int_c(x, __UINT64_C_SUFFIX__) - -# define INTMAX_C(x) __int_c(x, __INTMAX_C_SUFFIX__) -# define UINTMAX_C(x) __int_c(x, __UINTMAX_C_SUFFIX__) -#else -# define INT64_C(x) __INT64_C(x) -# define UINT64_C(x) __UINT64_C(x) - -# define INTMAX_C(x) __INTMAX_C(x) -# define UINTMAX_C(x) __UINTMAX_C(x) -#endif - -typedef __UINTPTR_TYPE__ uintptr_t; -typedef __INTPTR_TYPE__ intptr_t; - -typedef __UINTMAX_TYPE__ uintmax_t; -#define UINTMAX_MAX __UINTMAX_MAX__ - -typedef __INTMAX_TYPE__ intmax_t; -#define INTMAX_MAX __INTMAX_MAX__ -#define INTMAX_MIN (-INTMAX_MAX - 1) - -#define INT8_MIN (-128) -#define INT16_MIN (-32767 - 1) -#define INT32_MIN (-2147483647 - 1) -#define INT64_MIN (-INT64_C(9223372036854775807) - 1) -#define INT8_MAX (127) -#define INT16_MAX (32767) -#define INT32_MAX (2147483647) -#define INT64_MAX (INT64_C(9223372036854775807)) -#define UINT8_MAX (255) -#define UINT16_MAX (65535) -#define UINT32_MAX (4294967295U) -#define UINT64_MAX (UINT64_C(18446744073709551615)) - -#define INTPTR_MAX __INTPTR_MAX__ -#define INTPTR_MIN (-INTPTR_MAX - 1) -#define UINTPTR_MAX __UINTPTR_MAX__ - -#define INT_FAST8_MIN INT8_MIN -#define INT_FAST16_MIN INT16_MIN -#define INT_FAST32_MIN INT32_MIN -#define INT_FAST64_MIN INT64_MIN - -#define INT_FAST8_MAX INT8_MAX -#define INT_FAST16_MAX INT16_MAX -#define INT_FAST32_MAX INT32_MAX -#define INT_FAST64_MAX INT64_MAX - -#define UINT_FAST8_MAX UINT8_MAX -#define UINT_FAST16_MAX UINT16_MAX -#define UINT_FAST32_MAX UINT32_MAX -#define UINT_FAST64_MAX UINT64_MAX - -#define INT_LEAST8_MIN INT8_MIN -#define INT_LEAST16_MIN INT16_MIN -#define INT_LEAST32_MIN INT32_MIN -#define INT_LEAST64_MIN INT64_MIN - -#define INT_LEAST8_MAX INT8_MAX -#define INT_LEAST16_MAX INT16_MAX -#define INT_LEAST32_MAX INT32_MAX -#define INT_LEAST64_MAX INT64_MAX - -#define UINT_LEAST8_MAX UINT8_MAX -#define UINT_LEAST16_MAX UINT16_MAX -#define UINT_LEAST32_MAX UINT32_MAX -#define UINT_LEAST64_MAX UINT64_MAX - -#define SIZE_MAX __SIZE_MAX__ - -#define PTRDIFF_MAX __PTRDIFF_MAX__ -#define PTRDIFF_MIN (-__PTRDIFF_MAX__ - 1) - -__END_DECLS diff --git a/Userland/Libraries/LibC/bits/stdio_file_implementation.h b/Userland/Libraries/LibC/bits/stdio_file_implementation.h deleted file mode 100644 index 2bf8aa2e205..00000000000 --- a/Userland/Libraries/LibC/bits/stdio_file_implementation.h +++ /dev/null @@ -1,158 +0,0 @@ -/* - * Copyright (c) 2021, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include -#include -#include -#include - -struct FILE { -public: - FILE(int fd, int mode) - : m_fd(fd) - , m_mode(mode) - { - pthread_mutexattr_t attr = { __PTHREAD_MUTEX_RECURSIVE }; - pthread_mutex_init(&m_mutex, &attr); - } - ~FILE(); - - static FILE* create(int fd, int mode); - - void setbuf(u8* data, int mode, size_t size) { m_buffer.setbuf(data, mode, size); } - - bool flush(); - void purge(); - size_t pending(); - bool close(); - - void lock(); - void unlock(); - - int fileno() const { return m_fd; } - bool eof() const { return m_eof; } - int mode() const { return m_mode; } - u8 flags() const { return m_flags; } - - int error() const { return m_error; } - void clear_err() { m_error = 0; } - void set_err() { m_error = 1; } - - size_t read(u8*, size_t); - size_t write(u8 const*, size_t); - - template - bool gets(CharType*, size_t); - - bool ungetc(u8 byte) { return m_buffer.enqueue_front(byte); } - - int seek(off_t offset, int whence); - off_t tell(); - - pid_t popen_child() { return m_popen_child; } - void set_popen_child(pid_t child_pid) { m_popen_child = child_pid; } - - void reopen(int fd, int mode); - - u8 const* readptr(size_t& available_size); - void readptr_increase(size_t increment); - - enum Flags : u8 { - None = 0, - LastRead = 1, - LastWrite = 2, - }; - -private: - struct Buffer { - // A ringbuffer that also transparently implements ungetc(). - public: - ~Buffer(); - - int mode() const { return m_mode; } - void setbuf(u8* data, int mode, size_t size); - // Make sure to call realize() before enqueuing any data. - // Dequeuing can be attempted without it. - void realize(int fd); - void drop(); - - bool may_use() const; - bool is_not_empty() const { return m_ungotten || !m_empty; } - size_t buffered_size() const; - - u8 const* begin_dequeue(size_t& available_size) const; - void did_dequeue(size_t actual_size); - - u8* begin_enqueue(size_t& available_size) const; - void did_enqueue(size_t actual_size); - - bool enqueue_front(u8 byte); - - private: - constexpr static auto unget_buffer_size = MB_CUR_MAX; - constexpr static u32 ungotten_mask = ((u32)0xffffffff) >> (sizeof(u32) * 8 - unget_buffer_size); - - // Note: the fields here are arranged this way - // to make sizeof(Buffer) smaller. - u8* m_data { nullptr }; - size_t m_capacity { BUFSIZ }; - size_t m_begin { 0 }; - size_t m_end { 0 }; - - int m_mode { -1 }; - Array m_unget_buffer { 0 }; - u32 m_ungotten : unget_buffer_size { 0 }; - bool m_data_is_malloced : 1 { false }; - // When m_begin == m_end, we want to distinguish whether - // the buffer is full or empty. - bool m_empty : 1 { true }; - }; - - // Read or write using the underlying fd, bypassing the buffer. - ssize_t do_read(u8*, size_t); - ssize_t do_write(u8 const*, size_t); - - // Read some data into the buffer. - bool read_into_buffer(); - // Flush *some* data from the buffer. - bool write_from_buffer(); - - int m_fd { -1 }; - int m_mode { 0 }; - u8 m_flags { Flags::None }; - int m_error { 0 }; - bool m_eof { false }; - pid_t m_popen_child { -1 }; - Buffer m_buffer; - __pthread_mutex_t m_mutex; - IntrusiveListNode m_list_node; - -public: - using List = IntrusiveList<&FILE::m_list_node>; -}; - -class ScopedFileLock { -public: - ScopedFileLock(FILE* file) - : m_file(file) - { - m_file->lock(); - } - - ~ScopedFileLock() - { - m_file->unlock(); - } - -private: - FILE* m_file; -}; diff --git a/Userland/Libraries/LibC/bits/utimens.h b/Userland/Libraries/LibC/bits/utimens.h deleted file mode 100644 index 649acedda5b..00000000000 --- a/Userland/Libraries/LibC/bits/utimens.h +++ /dev/null @@ -1,18 +0,0 @@ -/* - * Copyright (c) 2018-2022, Andreas Kling - * Copyright (c) 2021, sin-ack - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include - -__BEGIN_DECLS - -int __utimens(int fd, char const* path, struct timespec const times[2], int flag); - -__END_DECLS diff --git a/Userland/Libraries/LibC/bits/wchar.h b/Userland/Libraries/LibC/bits/wchar.h deleted file mode 100644 index 224dbc477cc..00000000000 --- a/Userland/Libraries/LibC/bits/wchar.h +++ /dev/null @@ -1,10 +0,0 @@ -/* - * Copyright (c) 2021, Ali Mohammad Pur - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#define MB_CUR_MAX 4 -#define MB_LEN_MAX 16 diff --git a/Userland/Libraries/LibC/bits/wchar_size.h b/Userland/Libraries/LibC/bits/wchar_size.h deleted file mode 100644 index 6620e200604..00000000000 --- a/Userland/Libraries/LibC/bits/wchar_size.h +++ /dev/null @@ -1,15 +0,0 @@ -/* - * Copyright (c) 2022, Tim Schumacher - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#define WCHAR_MAX __WCHAR_MAX__ -#ifdef __WCHAR_MIN__ -# define WCHAR_MIN __WCHAR_MIN__ -#else -// Note: This assumes that wchar_t is a signed type. -# define WCHAR_MIN (-WCHAR_MAX - 1) -#endif diff --git a/Userland/Libraries/LibC/byteswap.h b/Userland/Libraries/LibC/byteswap.h deleted file mode 100644 index 139da4a2717..00000000000 --- a/Userland/Libraries/LibC/byteswap.h +++ /dev/null @@ -1,17 +0,0 @@ -/* - * Copyright (c) 2020, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include - -__BEGIN_DECLS - -#define bswap_16(x) (__builtin_bswap16(x)) -#define bswap_32(x) (__builtin_bswap32(x)) -#define bswap_64(x) (__builtin_bswap64(x)) - -__END_DECLS diff --git a/Userland/Libraries/LibC/complex.h b/Userland/Libraries/LibC/complex.h deleted file mode 100644 index 3cf7d73c29c..00000000000 --- a/Userland/Libraries/LibC/complex.h +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright (c) 2022, Peter Elliott - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -/* complex arithmetic - * - * https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/complex.h.html - */ - -#pragma once - -#ifdef __cplusplus -# error "C++ code must not include complex.h. Use AK/Complex.h instead." -#endif - -#include -#include - -__BEGIN_DECLS - -#define complex _Complex - -#define _Complex_I (0.0f + 1.0fi) -#define I _Complex_I - -#define CMPLX(x, y) ((double complex)__builtin_complex((double)x, (double)y)) -#define CMPLXF(x, y) ((float complex)__builtin_complex((float)x, (float)y)) -#define CMPLXL(x, y) ((long double complex)__builtin_complex((long double)x, (long double)y)) - -// These are macro implementations of the above functions, so that they will always be inlined. -#define creal(z) ((double)__real__((double complex)z)) -#define crealf(z) ((float)__real__((float complex)z)) -#define creall(z) ((long double)__real__((long double complex)z)) - -#define cimag(z) ((double)__imag__((double complex)z)) -#define cimagf(z) ((float)__imag__((float complex)z)) -#define cimagl(z) ((long double)__imag__((long double complex)z)) - -// Function definitions of this form "type (name)(args)" are intentional, to -// prevent macro versions of "name" from being incorrectly expanded. These -// functions are here to provide external linkage to their macro implementations. - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/creal.html -static inline float(crealf)(float complex z) -{ - return crealf(z); -} - -static inline double(creal)(double complex z) -{ - return creal(z); -} - -static inline long double(creall)(long double complex z) -{ - return creall(z); -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/cimag.html -static inline double(cimag)(double complex z) -{ - return cimag(z); -} - -static inline float(cimagf)(float complex z) -{ - return cimagf(z); -} - -static inline long double(cimagl)(long double complex z) -{ - return cimagl(z); -} - -__END_DECLS diff --git a/Userland/Libraries/LibC/crt0.cpp b/Userland/Libraries/LibC/crt0.cpp deleted file mode 100644 index c5dc4f98ba8..00000000000 --- a/Userland/Libraries/LibC/crt0.cpp +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include -#include - -#ifndef _DYNAMIC_LOADER -extern "C" { - -int main(int, char**, char**); - -// Tell the compiler that this may be called from somewhere else. -int _entry(int argc, char** argv) __attribute__((used)); -void _start(int, char**, char**) __attribute__((used)); - -NAKED void _start(int, char**, char**) -{ -# if ARCH(AARCH64) - asm( - "mov x29, 0\n" - "mov x30, 0\n" - "bl _entry\n"); -# elif ARCH(RISCV64) - asm( - "li fp, 0\n" - "li ra, 0\n" - "tail _entry@plt\n"); -# elif ARCH(X86_64) - asm( - "push $0\n" - "jmp _entry@plt\n"); -# else -# error "Unknown architecture" -# endif -} - -int _entry(int argc, char** argv) -{ - __begin_atexit_locking(); - - int status = main(argc, argv, environ); - - exit(status); - - return 20150614; -} -} -#endif diff --git a/Userland/Libraries/LibC/crt0_shared.cpp b/Userland/Libraries/LibC/crt0_shared.cpp deleted file mode 100644 index b3ca3a5c8f4..00000000000 --- a/Userland/Libraries/LibC/crt0_shared.cpp +++ /dev/null @@ -1,15 +0,0 @@ -/* - * Copyright (c) 2020, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include - -#ifndef _DYNAMIC_LOADER -void* __dso_handle __attribute__((__weak__)); -#endif diff --git a/Userland/Libraries/LibC/ctype.cpp b/Userland/Libraries/LibC/ctype.cpp deleted file mode 100644 index c866ed6aaf9..00000000000 --- a/Userland/Libraries/LibC/ctype.cpp +++ /dev/null @@ -1,126 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include - -extern "C" { - -char const _ctype_[1 + 256] = { - 0, - _C, _C, _C, _C, _C, _C, _C, _C, - _C, _C | _S, _C | _S, _C | _S, _C | _S, _C | _S, _C, _C, - _C, _C, _C, _C, _C, _C, _C, _C, - _C, _C, _C, _C, _C, _C, _C, _C, - (char)(_S | _B), _P, _P, _P, _P, _P, _P, _P, - _P, _P, _P, _P, _P, _P, _P, _P, - _N, _N, _N, _N, _N, _N, _N, _N, - _N, _N, _P, _P, _P, _P, _P, _P, - _P, _U | _X, _U | _X, _U | _X, _U | _X, _U | _X, _U | _X, _U, - _U, _U, _U, _U, _U, _U, _U, _U, - _U, _U, _U, _U, _U, _U, _U, _U, - _U, _U, _U, _P, _P, _P, _P, _P, - _P, _L | _X, _L | _X, _L | _X, _L | _X, _L | _X, _L | _X, _L, - _L, _L, _L, _L, _L, _L, _L, _L, - _L, _L, _L, _L, _L, _L, _L, _L, - _L, _L, _L, _P, _P, _P, _P, _C -}; - -#undef isalnum -int isalnum(int c) -{ - return __inline_isalnum(c); -} - -#undef isalpha -int isalpha(int c) -{ - return __inline_isalpha(c); -} - -#undef iscntrl -int iscntrl(int c) -{ - return __inline_iscntrl(c); -} - -#undef isdigit -int isdigit(int c) -{ - return __inline_isdigit(c); -} - -#undef isxdigit -int isxdigit(int c) -{ - return __inline_isxdigit(c); -} - -#undef isspace -int isspace(int c) -{ - return __inline_isspace(c); -} - -#undef ispunct -int ispunct(int c) -{ - return __inline_ispunct(c); -} - -#undef isprint -int isprint(int c) -{ - return __inline_isprint(c); -} - -#undef isgraph -int isgraph(int c) -{ - return __inline_isgraph(c); -} - -#undef isupper -int isupper(int c) -{ - return __inline_isupper(c); -} - -#undef islower -int islower(int c) -{ - return __inline_islower(c); -} - -#undef isascii -int isascii(int c) -{ - return __inline_isascii(c); -} - -#undef isblank -int isblank(int c) -{ - return __inline_isblank(c); -} - -#undef toascii -int toascii(int c) -{ - return __inline_toascii(c); -} - -#undef tolower -int tolower(int c) -{ - return __inline_tolower(c); -} - -#undef toupper -int toupper(int c) -{ - return __inline_toupper(c); -} -} diff --git a/Userland/Libraries/LibC/ctype.h b/Userland/Libraries/LibC/ctype.h deleted file mode 100644 index 1947f3416b9..00000000000 --- a/Userland/Libraries/LibC/ctype.h +++ /dev/null @@ -1,159 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include - -__BEGIN_DECLS - -#ifndef EOF -# define EOF (-1) -#endif - -/* Do what newlib does to appease GCC's --with-newlib option. */ -#define _U 01 -#define _L 02 -#define _N 04 -#define _S 010 -#define _P 020 -#define _C 040 -#define _X 0100 -#define _B 0200 - -/** - * newlib has a 257 byte _ctype_ array to enable compiler tricks to catch - * people passing char instead of int. We don't engage in those tricks, - * but still claim to be newlib to the toolchains - */ -extern char const _ctype_[1 + 256] __attribute__((visibility("default"))); - -static inline int __inline_isalnum(int c) -{ - return _ctype_[(unsigned char)(c) + 1] & (_U | _L | _N); -} - -static inline int __inline_isalpha(int c) -{ - return _ctype_[(unsigned char)(c) + 1] & (_U | _L); -} - -static inline int __inline_isascii(int c) -{ - return (unsigned)c <= 127; -} - -static inline int __inline_iscntrl(int c) -{ - return _ctype_[(unsigned char)(c) + 1] & (_C); -} - -static inline int __inline_isdigit(int c) -{ - return _ctype_[(unsigned char)(c) + 1] & (_N); -} - -static inline int __inline_isxdigit(int c) -{ - return _ctype_[(unsigned char)(c) + 1] & (_N | _X); -} - -static inline int __inline_isspace(int c) -{ - return _ctype_[(unsigned char)(c) + 1] & (_S); -} - -static inline int __inline_ispunct(int c) -{ - return _ctype_[(unsigned char)(c) + 1] & (_P); -} - -static inline int __inline_isprint(int c) -{ - return _ctype_[(unsigned char)(c) + 1] & (_P | _U | _L | _N | _B); -} - -static inline int __inline_isgraph(int c) -{ - return _ctype_[(unsigned char)(c) + 1] & (_P | _U | _L | _N); -} - -static inline int __inline_islower(int c) -{ - return _ctype_[(unsigned char)(c) + 1] & (_L); -} - -static inline int __inline_isupper(int c) -{ - return _ctype_[(unsigned char)(c) + 1] & (_U); -} - -static inline int __inline_isblank(int c) -{ - return _ctype_[(unsigned char)(c) + 1] & (_B) || (c == '\t'); -} - -static inline int __inline_toascii(int c) -{ - return c & 127; -} - -static inline int __inline_tolower(int c) -{ - if (c >= 'A' && c <= 'Z') - return c | 0x20; - return c; -} - -static inline int __inline_toupper(int c) -{ - if (c >= 'a' && c <= 'z') - return c & ~0x20; - return c; -} - -#ifdef __cplusplus -extern "C" { -#endif - -int isalnum(int c); -int isalpha(int c); -int isascii(int c); -int iscntrl(int c); -int isdigit(int c); -int isxdigit(int c); -int isspace(int c); -int ispunct(int c); -int isprint(int c); -int isgraph(int c); -int islower(int c); -int isupper(int c); -int isblank(int c); -int toascii(int c); -int tolower(int c); -int toupper(int c); - -#ifdef __cplusplus -} -#endif -#define isalnum __inline_isalnum -#define isalpha __inline_isalpha -#define isascii __inline_isascii -#define iscntrl __inline_iscntrl -#define isdigit __inline_isdigit -#define isxdigit __inline_isxdigit -#define isspace __inline_isspace -#define ispunct __inline_ispunct -#define isprint __inline_isprint -#define isgraph __inline_isgraph -#define islower __inline_islower -#define isupper __inline_isupper -#define isblank __inline_isblank -#define toascii __inline_toascii -#define tolower __inline_tolower -#define toupper __inline_toupper - -__END_DECLS diff --git a/Userland/Libraries/LibC/cxxabi.cpp b/Userland/Libraries/LibC/cxxabi.cpp deleted file mode 100644 index 6b23aec0132..00000000000 --- a/Userland/Libraries/LibC/cxxabi.cpp +++ /dev/null @@ -1,165 +0,0 @@ -/* - * Copyright (c) 2019-2021, Andrew Kaster - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -extern "C" { - -struct AtExitEntry { - AtExitFunction method { nullptr }; - void* parameter { nullptr }; - void* dso_handle { nullptr }; -}; - -// We'll re-allocate the region if it ends up being too small at runtime. -// Invariant: atexit_entry_region_capacity * sizeof(AtExitEntry) does not overflow. -static size_t atexit_entry_region_capacity = PAGE_SIZE / sizeof(AtExitEntry); - -static size_t atexit_region_bytes(size_t capacity = atexit_entry_region_capacity) -{ - return PAGE_ROUND_UP(capacity * sizeof(AtExitEntry)); -} - -static size_t atexit_next_capacity() -{ - size_t original_num_bytes = atexit_region_bytes(); - VERIFY(!Checked::addition_would_overflow(original_num_bytes, PAGE_SIZE)); - return (original_num_bytes + PAGE_SIZE) / sizeof(AtExitEntry); -} - -static AtExitEntry* atexit_entries; -static size_t atexit_entry_count = 0; -static pthread_mutex_t atexit_mutex = __PTHREAD_MUTEX_INITIALIZER; - -// The C++ compiler automagically registers the destructor of this object with __cxa_atexit. -// However, we can't control the order in which these destructors are run, so we might still want to access this data after the registered entry. -// Hence, we will call the destructor manually, when we know it is safe to do so. -static NeverDestroyed atexit_called_entries; - -// During startup, it is sufficiently unlikely that the attacker can exploit any write primitive. -// We use this to avoid unnecessary syscalls to mprotect. -static bool atexit_region_should_lock = false; - -static void lock_atexit_handlers() -{ - if (atexit_region_should_lock && mprotect(atexit_entries, atexit_region_bytes(), PROT_READ) < 0) { - perror("lock_atexit_handlers"); - _exit(1); - } -} - -static void unlock_atexit_handlers() -{ - if (atexit_region_should_lock && mprotect(atexit_entries, atexit_region_bytes(), PROT_READ | PROT_WRITE) < 0) { - perror("unlock_atexit_handlers"); - _exit(1); - } -} - -void __begin_atexit_locking() -{ - atexit_region_should_lock = true; - lock_atexit_handlers(); -} - -int __cxa_atexit(AtExitFunction exit_function, void* parameter, void* dso_handle) -{ - pthread_mutex_lock(&atexit_mutex); - - // allocate initial atexit region - if (!atexit_entries) { - atexit_entries = (AtExitEntry*)mmap(nullptr, atexit_region_bytes(), PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, 0, 0); - if (atexit_entries == MAP_FAILED) { - pthread_mutex_unlock(&atexit_mutex); - perror("__cxa_atexit mmap"); - _exit(1); - } - } - - // reallocate atexit region, increasing size by PAGE_SIZE - if (atexit_entry_count >= atexit_entry_region_capacity) { - size_t new_capacity = atexit_next_capacity(); - size_t new_atexit_region_size = atexit_region_bytes(new_capacity); - dbgln_if(GLOBAL_DTORS_DEBUG, "__cxa_atexit: Growing exit handler region from {} entries to {} entries", atexit_entry_region_capacity, new_capacity); - - auto* new_atexit_entries = (AtExitEntry*)mmap(nullptr, new_atexit_region_size, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, 0, 0); - if (new_atexit_entries == MAP_FAILED) { - pthread_mutex_unlock(&atexit_mutex); - perror("__cxa_atexit mmap (new size)"); - return -1; - } - // Note: We must make sure to only copy initialized entries, as even touching uninitialized bytes will trigger UBSan. - memcpy(new_atexit_entries, atexit_entries, atexit_entry_count * sizeof(AtExitEntry)); - if (munmap(atexit_entries, atexit_region_bytes()) < 0) { - perror("__cxa_atexit munmap old region"); - // leak the old region on failure - } - atexit_entries = new_atexit_entries; - atexit_entry_region_capacity = new_capacity; - } - - unlock_atexit_handlers(); - atexit_entries[atexit_entry_count++] = { exit_function, parameter, dso_handle }; - lock_atexit_handlers(); - - pthread_mutex_unlock(&atexit_mutex); - - return 0; -} - -void __cxa_finalize(void* dso_handle) -{ - // From the itanium abi, https://itanium-cxx-abi.github.io/cxx-abi/abi.html#dso-dtor-runtime-api - // - // When __cxa_finalize(d) is called, it should walk the termination function list, calling each in turn - // if d matches __dso_handle for the termination function entry. If d == NULL, it should call all of them. - // Multiple calls to __cxa_finalize shall not result in calling termination function entries multiple times; - // the implementation may either remove entries or mark them finished. - - pthread_mutex_lock(&atexit_mutex); - - if (atexit_entry_count > atexit_called_entries->size()) - atexit_called_entries->grow(atexit_entry_count, false); - - ssize_t entry_index = atexit_entry_count; - - dbgln_if(GLOBAL_DTORS_DEBUG, "__cxa_finalize: {} entries in the finalizer list", entry_index); - - while (--entry_index >= 0) { - auto& exit_entry = atexit_entries[entry_index]; - bool needs_calling = !atexit_called_entries->get(entry_index) && (!dso_handle || dso_handle == exit_entry.dso_handle); - if (needs_calling) { - dbgln_if(GLOBAL_DTORS_DEBUG, "__cxa_finalize: calling entry[{}] {:p}({:p}) dso: {:p}", entry_index, exit_entry.method, exit_entry.parameter, exit_entry.dso_handle); - atexit_called_entries->set(entry_index, true); - pthread_mutex_unlock(&atexit_mutex); - exit_entry.method(exit_entry.parameter); - pthread_mutex_lock(&atexit_mutex); - } - } - - pthread_mutex_unlock(&atexit_mutex); -} - -__attribute__((noreturn)) void __cxa_pure_virtual() -{ - VERIFY_NOT_REACHED(); -} - -} // extern "C" diff --git a/Userland/Libraries/LibC/dirent.cpp b/Userland/Libraries/LibC/dirent.cpp deleted file mode 100644 index 1a063ae96f6..00000000000 --- a/Userland/Libraries/LibC/dirent.cpp +++ /dev/null @@ -1,293 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -extern "C" { - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/opendir.html -DIR* opendir(char const* name) -{ - int fd = open(name, O_RDONLY | O_DIRECTORY); - if (fd == -1) - return nullptr; - return fdopendir(fd); -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/fdopendir.html -DIR* fdopendir(int fd) -{ - if (fd == -1) - return nullptr; - DIR* dirp = (DIR*)malloc(sizeof(DIR)); - dirp->fd = fd; - dirp->buffer = nullptr; - dirp->buffer_size = 0; - dirp->nextptr = nullptr; - return dirp; -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/closedir.html -int closedir(DIR* dirp) -{ - if (!dirp || dirp->fd == -1) - return -EBADF; - free(dirp->buffer); - int rc = close(dirp->fd); - if (rc == 0) - dirp->fd = -1; - free(dirp); - return rc; -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/rewinddir.html -void rewinddir(DIR* dirp) -{ - free(dirp->buffer); - dirp->buffer = nullptr; - dirp->buffer_size = 0; - dirp->nextptr = nullptr; - lseek(dirp->fd, 0, SEEK_SET); -} - -struct [[gnu::packed]] sys_dirent { - ino_t ino; - u8 file_type; - u32 namelen; - char name[]; - size_t total_size() - { - return sizeof(ino_t) + sizeof(u8) + sizeof(u32) + sizeof(char) * namelen; - } -}; - -static void create_struct_dirent(sys_dirent* sys_ent, struct dirent* str_ent) -{ - str_ent->d_ino = sys_ent->ino; - str_ent->d_type = sys_ent->file_type; - str_ent->d_off = 0; - str_ent->d_reclen = sizeof(struct dirent); - - VERIFY(sizeof(str_ent->d_name) > sys_ent->namelen); - - // Note: We can't use any normal string function as sys_ent->name is - // not null terminated. All string copy functions will attempt to read - // the non-existent null terminator past the end of the source string. - memcpy(str_ent->d_name, sys_ent->name, sys_ent->namelen); - str_ent->d_name[sys_ent->namelen] = '\0'; -} - -static int allocate_dirp_buffer(DIR* dirp) -{ - if (dirp->buffer) { - return 0; - } - - struct stat st; - // preserve errno since this could be a reentrant call - int old_errno = errno; - int rc = fstat(dirp->fd, &st); - if (rc < 0) { - int new_errno = errno; - errno = old_errno; - return new_errno; - } - size_t size_to_allocate = max(st.st_size, static_cast(4096)); - dirp->buffer = (char*)malloc(size_to_allocate); - if (!dirp->buffer) - return ENOMEM; - for (;;) { - ssize_t nread = syscall(SC_get_dir_entries, dirp->fd, dirp->buffer, size_to_allocate); - if (nread < 0) { - if (nread == -EINVAL) { - size_to_allocate *= 2; - char* new_buffer = (char*)realloc(dirp->buffer, size_to_allocate); - if (new_buffer) { - dirp->buffer = new_buffer; - continue; - } else { - nread = -ENOMEM; - } - } - // uh-oh, the syscall returned an error - free(dirp->buffer); - dirp->buffer = nullptr; - return -nread; - } - dirp->buffer_size = nread; - dirp->nextptr = dirp->buffer; - break; - } - return 0; -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/readdir.html -dirent* readdir(DIR* dirp) -{ - if (!dirp) - return nullptr; - if (dirp->fd == -1) - return nullptr; - - if (int new_errno = allocate_dirp_buffer(dirp)) { - // readdir is allowed to mutate errno - errno = new_errno; - return nullptr; - } - - if (dirp->nextptr >= (dirp->buffer + dirp->buffer_size)) - return nullptr; - - auto* sys_ent = (sys_dirent*)dirp->nextptr; - create_struct_dirent(sys_ent, &dirp->cur_ent); - - dirp->nextptr += sys_ent->total_size(); - return &dirp->cur_ent; -} - -static bool compare_sys_struct_dirent(sys_dirent* sys_ent, struct dirent* str_ent) -{ - size_t namelen = min((size_t)256, sys_ent->namelen); - // These fields are guaranteed by create_struct_dirent to be the same - return sys_ent->ino == str_ent->d_ino - && sys_ent->file_type == str_ent->d_type - && sys_ent->total_size() == str_ent->d_reclen - && strncmp(sys_ent->name, str_ent->d_name, namelen) == 0; -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/readdir_r.html -int readdir_r(DIR* dirp, struct dirent* entry, struct dirent** result) -{ - if (!dirp || dirp->fd == -1) { - *result = nullptr; - return EBADF; - } - - if (int new_errno = allocate_dirp_buffer(dirp)) { - *result = nullptr; - return new_errno; - } - - // This doesn't care about dirp state; seek until we find the entry. - // Unfortunately, we can't just compare struct dirent to sys_dirent, so - // manually compare the fields. This seems a bit risky, but could work. - auto* buffer = dirp->buffer; - auto* sys_ent = (sys_dirent*)buffer; - bool found = false; - while (!(found || buffer >= dirp->buffer + dirp->buffer_size)) { - found = compare_sys_struct_dirent(sys_ent, entry); - - // Make sure if we found one, it's the one after (end of buffer or not) - buffer += sys_ent->total_size(); - sys_ent = (sys_dirent*)buffer; - } - - // If we found one, but hit end of buffer, then EOD - if (found && buffer >= dirp->buffer + dirp->buffer_size) { - *result = nullptr; - return 0; - } - // If we never found a match for entry in buffer, start from the beginning - else if (!found) { - buffer = dirp->buffer; - sys_ent = (sys_dirent*)buffer; - } - - *result = entry; - create_struct_dirent(sys_ent, entry); - - return 0; -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/dirfd.html -int dirfd(DIR* dirp) -{ - VERIFY(dirp); - return dirp->fd; -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/alphasort.html -int alphasort(const struct dirent** d1, const struct dirent** d2) -{ - return strcoll((*d1)->d_name, (*d2)->d_name); -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/scandir.html -int scandir(char const* dir_name, - struct dirent*** namelist, - int (*select)(const struct dirent*), - int (*compare)(const struct dirent**, const struct dirent**)) -{ - auto dir = opendir(dir_name); - if (dir == nullptr) - return -1; - ScopeGuard guard = [&] { - closedir(dir); - }; - - Vector tmp_names; - ScopeGuard names_guard = [&] { - tmp_names.remove_all_matching([&](auto& entry) { - free(entry); - return true; - }); - }; - - while (true) { - errno = 0; - auto entry = readdir(dir); - if (!entry) - break; - - // Omit entries the caller chooses to ignore. - if (select && !select(entry)) - continue; - - auto entry_copy = (struct dirent*)malloc(entry->d_reclen); - if (!entry_copy) - break; - memcpy(entry_copy, entry, entry->d_reclen); - tmp_names.append(entry_copy); - } - - // Propagate any errors encountered while accumulating back to the user. - if (errno) { - return -1; - } - - // Sort the entries if the user provided a comparator. - if (compare) { - qsort(tmp_names.data(), tmp_names.size(), sizeof(struct dirent*), (int (*)(void const*, void const*))compare); - } - - int const size = tmp_names.size(); - auto** names = static_cast(kmalloc_array(size, sizeof(struct dirent*))); - if (names == nullptr) { - return -1; - } - for (auto i = 0; i < size; i++) { - names[i] = tmp_names[i]; - } - - // Disable the scope guard which free's names on error. - tmp_names.clear(); - - *namelist = names; - return size; -} -} diff --git a/Userland/Libraries/LibC/dirent.h b/Userland/Libraries/LibC/dirent.h deleted file mode 100644 index 8f813b35bab..00000000000 --- a/Userland/Libraries/LibC/dirent.h +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include - -__BEGIN_DECLS - -struct dirent { - ino_t d_ino; - off_t d_off; - unsigned short d_reclen; - unsigned char d_type; - char d_name[256]; -}; - -struct __DIR { - int fd; - struct dirent cur_ent; - char* buffer; - size_t buffer_size; - char* nextptr; -}; -typedef struct __DIR DIR; - -DIR* fdopendir(int fd); -DIR* opendir(char const* name); -int closedir(DIR*); -void rewinddir(DIR*); -struct dirent* readdir(DIR*); -int readdir_r(DIR*, struct dirent*, struct dirent**); -int dirfd(DIR*); - -int alphasort(const struct dirent** d1, const struct dirent** d2); -int scandir(char const* dirp, struct dirent*** namelist, - int (*filter)(const struct dirent*), - int (*compar)(const struct dirent**, const struct dirent**)); - -__END_DECLS diff --git a/Userland/Libraries/LibC/dlfcn.cpp b/Userland/Libraries/LibC/dlfcn.cpp deleted file mode 100644 index ef578297fa4..00000000000 --- a/Userland/Libraries/LibC/dlfcn.cpp +++ /dev/null @@ -1,80 +0,0 @@ -/* - * Copyright (c) 2021, Gunnar Beutner - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include -#include - -[[gnu::weak]] Result __dlclose(void*) asm("__dlclose"); -[[gnu::weak]] Result __dlopen(char const*, int) asm("__dlopen"); -[[gnu::weak]] Result __dlsym(void*, char const*) asm("__dlsym"); -[[gnu::weak]] Result __dladdr(void const*, Dl_info*) asm("__dladdr"); - -// FIXME: use thread_local and a String once TLS works -__thread char* s_dlerror_text = NULL; -__thread bool s_dlerror_retrieved = false; - -static void store_error(ByteString const& error) -{ - free(s_dlerror_text); - s_dlerror_text = strdup(error.characters()); - s_dlerror_retrieved = false; -} - -int dlclose(void* handle) -{ - auto result = __dlclose(handle); - if (result.is_error()) { - store_error(result.error().text); - return -1; - } - return 0; -} - -char* dlerror() -{ - if (s_dlerror_retrieved) { - free(s_dlerror_text); - s_dlerror_text = nullptr; - } - s_dlerror_retrieved = true; - return const_cast(s_dlerror_text); -} - -void* dlopen(char const* filename, int flags) -{ - auto result = __dlopen(filename, flags); - if (result.is_error()) { - store_error(result.error().text); - return nullptr; - } - return result.value(); -} - -void* dlsym(void* handle, char const* symbol_name) -{ - auto result = __dlsym(handle, symbol_name); - if (result.is_error()) { - store_error(result.error().text); - return nullptr; - } - return result.value(); -} - -int dladdr(void const* addr, Dl_info* info) -{ - auto result = __dladdr(addr, info); - if (result.is_error()) { - // FIXME: According to the man page glibc does _not_ make the error - // available via dlerror(), however we do. Does this break anything? - store_error(result.error().text); - return 0; - } - return 1; -} diff --git a/Userland/Libraries/LibC/dlfcn.h b/Userland/Libraries/LibC/dlfcn.h deleted file mode 100644 index a08bb42d265..00000000000 --- a/Userland/Libraries/LibC/dlfcn.h +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include - -__BEGIN_DECLS - -#define RTLD_DEFAULT 0 -#define RTLD_LAZY 2 -#define RTLD_NOW 4 -#define RTLD_GLOBAL 8 -#define RTLD_LOCAL 16 - -typedef struct __Dl_info { - char const* dli_fname; - void* dli_fbase; - char const* dli_sname; - void* dli_saddr; -} Dl_info; - -int dlclose(void*); -char* dlerror(void); -void* dlopen(char const*, int); -void* dlsym(void*, char const*); -int dladdr(void const*, Dl_info*); - -__END_DECLS diff --git a/Userland/Libraries/LibC/elf.h b/Userland/Libraries/LibC/elf.h deleted file mode 100644 index 002a05a7d20..00000000000 --- a/Userland/Libraries/LibC/elf.h +++ /dev/null @@ -1,9 +0,0 @@ -/* - * Copyright (c) 2023, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include diff --git a/Userland/Libraries/LibC/endian.h b/Userland/Libraries/LibC/endian.h deleted file mode 100644 index d1f6b5217e2..00000000000 --- a/Userland/Libraries/LibC/endian.h +++ /dev/null @@ -1,89 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include - -__BEGIN_DECLS - -#define __LITTLE_ENDIAN 1234 -#define __BIG_ENDIAN 4321 -#define __PDP_ENDIAN 3412 - -#if defined(__GNUC__) && defined(__BYTE_ORDER__) -# define __BYTE_ORDER __BYTE_ORDER__ -#else -# include -#endif - -#if defined(_GNU_SOURCE) || defined(_BSD_SOURCE) - -# include - -static __inline uint16_t __bswap16(uint16_t x) -{ - return __builtin_bswap16(x); -} - -static __inline uint32_t __bswap32(uint32_t x) -{ - return __builtin_bswap32(x); -} - -static __inline uint64_t __bswap64(uint64_t x) -{ - return __builtin_bswap64(x); -} - -# define LITTLE_ENDIAN __LITTLE_ENDIAN -# define BIG_ENDIAN __BIG_ENDIAN -# define PDP_ENDIAN __PDP_ENDIAN -# define BYTE_ORDER __BYTE_ORDER - -# if __BYTE_ORDER == __LITTLE_ENDIAN -# define htole16(x) ((uint16_t)(x)) -# define le16toh(x) ((uint16_t)(x)) -# define letoh16(x) ((uint16_t)(x)) -# define htole32(x) ((uint32_t)(x)) -# define le32toh(x) ((uint32_t)(x)) -# define letoh32(x) ((uint32_t)(x)) -# define htole64(x) ((uint64_t)(x)) -# define le64toh(x) ((uint64_t)(x)) -# define letoh64(x) ((uint64_t)(x)) -# define htobe16(x) (__builtin_bswap16(x)) -# define be16toh(x) (__builtin_bswap16(x)) -# define betoh16(x) (__builtin_bswap16(x)) -# define htobe32(x) (__builtin_bswap32(x)) -# define be32toh(x) (__builtin_bswap32(x)) -# define betoh32(x) (__builtin_bswap32(x)) -# define htobe64(x) (__builtin_bswap64(x)) -# define be64toh(x) (__builtin_bswap64(x)) -# define betoh64(x) (__builtin_bswap64(x)) -# else -# define htole16(x) (__builtin_bswap16(x)) -# define le16toh(x) (__builtin_bswap16(x)) -# define letoh16(x) (__builtin_bswap16(x)) -# define htole32(x) (__builtin_bswap32(x)) -# define le32toh(x) (__builtin_bswap32(x)) -# define letoh32(x) (__builtin_bswap32(x)) -# define htole64(x) (__builtin_bswap64(x)) -# define le64toh(x) (__builtin_bswap64(x)) -# define letoh64(x) (__builtin_bswap64(x)) -# define htobe16(x) ((uint16_t)(x)) -# define be16toh(x) ((uint16_t)(x)) -# define betoh16(x) ((uint16_t)(x)) -# define htobe32(x) ((uint32_t)(x)) -# define be32toh(x) ((uint32_t)(x)) -# define betoh32(x) ((uint32_t)(x)) -# define htobe64(x) ((uint64_t)(x)) -# define be64toh(x) ((uint64_t)(x)) -# define betoh64(x) ((uint64_t)(x)) -# endif - -#endif - -__END_DECLS diff --git a/Userland/Libraries/LibC/errno.h b/Userland/Libraries/LibC/errno.h deleted file mode 100644 index e3e8e84a40f..00000000000 --- a/Userland/Libraries/LibC/errno.h +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright (c) 2018-2022, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include - -#define __RETURN_WITH_ERRNO(rc, good_ret, bad_ret) \ - do { \ - if (rc < 0) { \ - errno = -rc; \ - return (bad_ret); \ - } \ - return (good_ret); \ - } while (0) - -__BEGIN_DECLS - -extern char const* const sys_errlist[]; -extern int sys_nerr; - -#ifdef NO_TLS -extern int errno; -#else -extern __thread int errno; -#endif - -int* __errno_location() __attribute__((const)); -#define errno (*__errno_location()) - -__END_DECLS diff --git a/Userland/Libraries/LibC/errno_codes.h b/Userland/Libraries/LibC/errno_codes.h deleted file mode 100644 index 6362446074c..00000000000 --- a/Userland/Libraries/LibC/errno_codes.h +++ /dev/null @@ -1,97 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include - -// NOTE: You can't define with a macro, so these have to be duplicated. -#define ESUCCESS ESUCCESS -#define EPERM EPERM -#define ENOENT ENOENT -#define ESRCH ESRCH -#define EINTR EINTR -#define EIO EIO -#define ENXIO ENXIO -#define E2BIG E2BIG -#define ENOEXEC ENOEXEC -#define EBADF EBADF -#define ECHILD ECHILD -#define EAGAIN EAGAIN -#define ENOMEM ENOMEM -#define EACCES EACCES -#define EFAULT EFAULT -#define ENOTBLK ENOTBLK -#define EBUSY EBUSY -#define EEXIST EEXIST -#define EXDEV EXDEV -#define ENODEV ENODEV -#define ENOTDIR ENOTDIR -#define EISDIR EISDIR -#define EINVAL EINVAL -#define ENFILE ENFILE -#define EMFILE EMFILE -#define ENOTTY ENOTTY -#define ETXTBSY ETXTBSY -#define EFBIG EFBIG -#define ENOSPC ENOSPC -#define ESPIPE ESPIPE -#define EROFS EROFS -#define EMLINK EMLINK -#define EPIPE EPIPE -#define ERANGE ERANGE -#define ENAMETOOLONG ENAMETOOLONG -#define ELOOP ELOOP -#define EOVERFLOW EOVERFLOW -#define EOPNOTSUPP EOPNOTSUPP -#define ENOSYS ENOSYS -#define ENOTIMPL ENOTIMPL -#define EAFNOSUPPORT EAFNOSUPPORT -#define ENOTSOCK ENOTSOCK -#define EADDRINUSE EADDRINUSE -#define ENOTEMPTY ENOTEMPTY -#define EDOM EDOM -#define ECONNREFUSED ECONNREFUSED -#define EHOSTDOWN EHOSTDOWN -#define EADDRNOTAVAIL EADDRNOTAVAIL -#define EISCONN EISCONN -#define ECONNABORTED ECONNABORTED -#define EALREADY EALREADY -#define ECONNRESET ECONNRESET -#define EDESTADDRREQ EDESTADDRREQ -#define EHOSTUNREACH EHOSTUNREACH -#define EILSEQ EILSEQ -#define EMSGSIZE EMSGSIZE -#define ENETDOWN ENETDOWN -#define ENETUNREACH ENETUNREACH -#define ENETRESET ENETRESET -#define ENOBUFS ENOBUFS -#define ENOLCK ENOLCK -#define ENOMSG ENOMSG -#define ENOPROTOOPT ENOPROTOOPT -#define ENOTCONN ENOTCONN -#define ESHUTDOWN ESHUTDOWN -#define ETOOMANYREFS ETOOMANYREFS -#define ESOCKTNOSUPPORT ESOCKTNOSUPPORT -#define EPROTONOSUPPORT EPROTONOSUPPORT -#define EDEADLK EDEADLK -#define ETIMEDOUT ETIMEDOUT -#define EPROTOTYPE EPROTOTYPE -#define EINPROGRESS EINPROGRESS -#define ENOTHREAD ENOTHREAD -#define EPROTO EPROTO -#define ENOTSUP ENOTSUP -#define EPFNOSUPPORT EPFNOSUPPORT -#define EDIRINTOSELF EDIRINTOSELF -#define EDQUOT EDQUOT -#define ENOTRECOVERABLE ENOTRECOVERABLE -#define ECANCELED ECANCELED -#define EPROMISEVIOLATION EPROMISEVIOLATION -#define ESTALE ESTALE -#define EMAXERRNO EMAXERRNO - -#define EWOULDBLOCK EAGAIN -#define ELAST EMAXERRNO diff --git a/Userland/Libraries/LibC/fcntl.cpp b/Userland/Libraries/LibC/fcntl.cpp deleted file mode 100644 index 4d3a9b40960..00000000000 --- a/Userland/Libraries/LibC/fcntl.cpp +++ /dev/null @@ -1,184 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * Copyright (c) 2021, sin-ack - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -extern "C" { - -int fcntl(int fd, int cmd, ...) -{ - __pthread_maybe_cancel(); - - va_list ap; - va_start(ap, cmd); - uintptr_t extra_arg = va_arg(ap, uintptr_t); - int rc = syscall(SC_fcntl, fd, cmd, extra_arg); - va_end(ap); - __RETURN_WITH_ERRNO(rc, rc, -1); -} - -int create_inode_watcher(unsigned flags) -{ - int rc = syscall(SC_create_inode_watcher, flags); - __RETURN_WITH_ERRNO(rc, rc, -1); -} - -int inode_watcher_add_watch(int fd, char const* path, size_t path_length, unsigned event_mask) -{ - Syscall::SC_inode_watcher_add_watch_params params { { path, path_length }, fd, event_mask }; - int rc = syscall(SC_inode_watcher_add_watch, ¶ms); - __RETURN_WITH_ERRNO(rc, rc, -1); -} - -int inode_watcher_remove_watch(int fd, int wd) -{ - int rc = syscall(SC_inode_watcher_remove_watch, fd, wd); - __RETURN_WITH_ERRNO(rc, rc, -1); -} - -int creat(char const* path, mode_t mode) -{ - __pthread_maybe_cancel(); - - return open(path, O_CREAT | O_WRONLY | O_TRUNC, mode); -} - -int open(char const* path, int options, ...) -{ - __pthread_maybe_cancel(); - - if (!path) { - errno = EFAULT; - return -1; - } - auto path_length = strlen(path); - if (path_length > INT32_MAX) { - errno = EINVAL; - return -1; - } - va_list ap; - va_start(ap, options); - auto mode = (mode_t)va_arg(ap, unsigned); - va_end(ap); - Syscall::SC_open_params params { AT_FDCWD, { path, path_length }, options, mode }; - int rc = syscall(SC_open, ¶ms); - __RETURN_WITH_ERRNO(rc, rc, -1); -} - -int openat(int dirfd, char const* path, int options, ...) -{ - __pthread_maybe_cancel(); - - if (!path) { - errno = EFAULT; - return -1; - } - auto path_length = strlen(path); - if (path_length > INT32_MAX) { - errno = EINVAL; - return -1; - } - va_list ap; - va_start(ap, options); - auto mode = (mode_t)va_arg(ap, unsigned); - va_end(ap); - Syscall::SC_open_params params { dirfd, { path, path_length }, options, mode }; - int rc = syscall(SC_open, ¶ms); - __RETURN_WITH_ERRNO(rc, rc, -1); -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/posix_fadvise.html -int posix_fadvise(int fd, off_t offset, off_t len, int advice) -{ - // Per POSIX: - // "The posix_fadvise() function shall have no effect on the semantics of other operations on the specified data, - // although it may affect the performance of other operations." - - // For now, we simply ignore posix_fadvise() requests. In the future we may use them to optimize performance. - (void)fd; - (void)offset; - (void)len; - (void)advice; - return 0; -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/posix_fallocate.html -int posix_fallocate(int fd, off_t offset, off_t len) -{ - // posix_fallocate does not set errno. - return -static_cast(syscall(SC_posix_fallocate, fd, offset, len)); -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/utimensat.html -int utimensat(int dirfd, char const* path, struct timespec const times[2], int flag) -{ - if (!path) { - errno = EFAULT; - return -1; - } - return __utimens(dirfd, path, times, flag); -} - -int __utimens(int fd, char const* path, struct timespec const times[2], int flag) -{ - size_t path_length = 0; - if (path) { - path_length = strlen(path); - if (path_length > INT32_MAX) { - errno = EINVAL; - return -1; - } - } - - // POSIX allows AT_SYMLINK_NOFOLLOW flag or no flags. - if (flag & ~AT_SYMLINK_NOFOLLOW) { - errno = EINVAL; - return -1; - } - - // Return early without error since both changes are to be omitted. - if (times && times[0].tv_nsec == UTIME_OMIT && times[1].tv_nsec == UTIME_OMIT) - return 0; - - // According to POSIX, when times is a nullptr, it's equivalent to setting - // both last access time and last modification time to the current time. - // Setting the times argument to nullptr if it matches this case prevents - // the need to copy it in the kernel. - if (times && times[0].tv_nsec == UTIME_NOW && times[1].tv_nsec == UTIME_NOW) - times = nullptr; - - if (times) { - for (int i = 0; i < 2; ++i) { - if ((times[i].tv_nsec != UTIME_NOW && times[i].tv_nsec != UTIME_OMIT) - && (times[i].tv_nsec < 0 || times[i].tv_nsec >= 1'000'000'000L)) { - errno = EINVAL; - return -1; - } - } - } - - int rc = 0; - if (path) { - // NOTE: fd is treated as dirfd for this syscall. - Syscall::SC_utimensat_params params { fd, { path, path_length }, times, flag }; - rc = syscall(SC_utimensat, ¶ms); - } else { - Syscall::SC_futimens_params params { fd, times }; - rc = syscall(SC_futimens, ¶ms); - } - - __RETURN_WITH_ERRNO(rc, rc, -1); -} -} diff --git a/Userland/Libraries/LibC/fcntl.h b/Userland/Libraries/LibC/fcntl.h deleted file mode 100644 index ceb5896c474..00000000000 --- a/Userland/Libraries/LibC/fcntl.h +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright (c) 2018-2022, Andreas Kling - * Copyright (c) 2021, sin-ack - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -// Includes essentially mandated by POSIX: -// https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/fcntl.h.html -#include -#include - -#include -#include -#include - -__BEGIN_DECLS - -#define POSIX_FADV_DONTNEED 1 -#define POSIX_FADV_NOREUSE 2 -#define POSIX_FADV_NORMAL 3 -#define POSIX_FADV_RANDOM 4 -#define POSIX_FADV_SEQUENTIAL 5 -#define POSIX_FADV_WILLNEED 6 - -int creat(char const* path, mode_t); -int open(char const* path, int options, ...); -int openat(int dirfd, char const* path, int options, ...); - -int fcntl(int fd, int cmd, ...); -int create_inode_watcher(unsigned flags); -int inode_watcher_add_watch(int fd, char const* path, size_t path_length, unsigned event_mask); -int inode_watcher_remove_watch(int fd, int wd); - -int posix_fadvise(int fd, off_t offset, off_t len, int advice); -int posix_fallocate(int fd, off_t offset, off_t len); - -int utimensat(int dirfd, char const* path, struct timespec const times[2], int flag); - -__END_DECLS diff --git a/Userland/Libraries/LibC/fd_set.h b/Userland/Libraries/LibC/fd_set.h deleted file mode 100644 index 3aae3225c03..00000000000 --- a/Userland/Libraries/LibC/fd_set.h +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include - -#define FD_ZERO(set) \ - do { \ - memset((set), 0, sizeof(fd_set)); \ - } while (0) -#define FD_CLR(fd, set) ((set)->fds_bits[(fd / 8)] &= ~(1 << (fd) % 8)) -#define FD_SET(fd, set) ((set)->fds_bits[(fd / 8)] |= (1 << (fd) % 8)) -#define FD_ISSET(fd, set) ((set)->fds_bits[(fd / 8)] & (1 << (fd) % 8)) - -struct __fd_set { - unsigned char fds_bits[FD_SETSIZE / 8]; -}; - -typedef struct __fd_set fd_set; diff --git a/Userland/Libraries/LibC/fenv.cpp b/Userland/Libraries/LibC/fenv.cpp deleted file mode 100644 index d3906f5086b..00000000000 --- a/Userland/Libraries/LibC/fenv.cpp +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright (c) 2021, Mițca Dumitru - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include - -extern "C" { - -int feupdateenv(fenv_t const* env) -{ - auto currently_raised_exceptions = fetestexcept(FE_ALL_EXCEPT); - - fesetenv(env); - feraiseexcept(currently_raised_exceptions); - - return 0; -} - -int fegetexceptflag(fexcept_t* except, int exceptions) -{ - if (!except) - return 1; - *except = (uint16_t)fetestexcept(exceptions); - return 0; -} -} diff --git a/Userland/Libraries/LibC/fenv.h b/Userland/Libraries/LibC/fenv.h deleted file mode 100644 index 7617921d16e..00000000000 --- a/Userland/Libraries/LibC/fenv.h +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright (c) 2021, Mițca Dumitru - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include - -__BEGIN_DECLS - -#define FE_DFL_ENV ((fenv_t const*)-1) - -int fegetenv(fenv_t*); -int fesetenv(fenv_t const*); -int feholdexcept(fenv_t*); -int feupdateenv(fenv_t const*); - -#define FE_INVALID 1u << 0 -#define FE_DIVBYZERO 1u << 2 -#define FE_OVERFLOW 1u << 3 -#define FE_UNDERFLOW 1u << 4 -#define FE_INEXACT 1u << 5 -#define FE_ALL_EXCEPT (FE_DIVBYZERO | FE_INEXACT | FE_INVALID | FE_OVERFLOW | FE_UNDERFLOW) - -typedef uint16_t fexcept_t; -int fegetexceptflag(fexcept_t*, int exceptions); -int fesetexceptflag(fexcept_t const*, int exceptions); - -int feclearexcept(int exceptions); -int fetestexcept(int exceptions); -int feraiseexcept(int exceptions); - -#define FE_TONEAREST 0 -#define FE_DOWNWARD 1 -#define FE_UPWARD 2 -#define FE_TOWARDZERO 3 -// Only exists in RISC-V at the moment; on other architectures this is replaced with FE_TONEAREST. -#define FE_TOMAXMAGNITUDE 4 - -int fesetround(int round); -int fegetround(void); - -__END_DECLS diff --git a/Userland/Libraries/LibC/float.h b/Userland/Libraries/LibC/float.h deleted file mode 100644 index 575278e24a0..00000000000 --- a/Userland/Libraries/LibC/float.h +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * Copyright (c) 2021, Mițca Dumitru - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -// Defined in fenv.cpp, but we must not include fenv.h, so here's its prototype. -int fegetround(void); - -#define FLT_RADIX 2 -#define DECIMAL_DIG 21 -#define FLT_DECIMAL_DIG 9 -#define DBL_DECIMAL_DIG 17 -#define LDBL_DECIMAL_DIG 21 -#define FLT_MIN 1.17549e-38 -#define DBL_MIN 2.22507e-308 -#define LDBL_MIN 3.3621e-4932 -#define FLT_TRUE_MIN 1.4013e-45 -#define DBL_TRUE_MIN 4.94066e-324 -#define LDBL_TRUE_MIN 3.6452e-4951 -#define FLT_MAX 3.40282e+38 -#define DBL_MAX 1.79769e+308 -#define LDBL_MAX 1.18973e+4932 -#define FLT_EPSILON 1.19209e-07 -#define DBL_EPSILON 2.22045e-16 -#define LDBL_EPSILON 1.0842e-19 -#define FLT_DIG 6 -#define DBL_DIG 15 -#define LDBL_DIG 18 -#define FLT_MANT_DIG 24 -#define DBL_MANT_DIG 53 -#define LDBL_MANT_DIG 64 -#define FLT_MIN_EXP -125 -#define DBL_MIN_EXP -1021 -#define LDBL_MIN_EXP -16381 -#define FLT_MIN_10_EXP -37 -#define DBL_MIN_10_EXP -307 -#define LDBL_MIN_10_EXP -4931 -#define FLT_MAX_EXP 128 -#define DBL_MAX_EXP 1024 -#define LDBL_MAX_EXP 16384 -#define FLT_MAX_10_EXP 38 -#define DBL_MAX_10_EXP 308 -#define LDBL_MAX_10_EXP 4932 - -#define FLT_ROUNDS (fegetround()) // Note: this not might be true for non-x86 platforms - -#define FLT_HAS_SUBNORM 1 -#define DBL_HAS_SUBNORM 1 -#define LDBL_HAS_SUBNORM 1 diff --git a/Userland/Libraries/LibC/fnmatch.cpp b/Userland/Libraries/LibC/fnmatch.cpp deleted file mode 100644 index b0d47cfb407..00000000000 --- a/Userland/Libraries/LibC/fnmatch.cpp +++ /dev/null @@ -1,14 +0,0 @@ -/* - * Copyright (c) 2021, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include - -int fnmatch(char const*, char const*, int) -{ - dbgln("FIXME: Implement fnmatch()"); - return 0; -} diff --git a/Userland/Libraries/LibC/fnmatch.h b/Userland/Libraries/LibC/fnmatch.h deleted file mode 100644 index 1808e7b4b56..00000000000 --- a/Userland/Libraries/LibC/fnmatch.h +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright (c) 2021, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include - -#define FNM_NOMATCH 1 -#define FNM_PATHNAME 1 -#define FNM_NOESCAPE 2 -#define FNM_PERIOD 4 -#define FNM_FILE_NAME FNM_PATHNAME -#define FNM_LEADING_DIR 8 -#define FNM_CASEFOLD 16 -#define FNM_EXTMATCH 32 - -__BEGIN_DECLS - -int fnmatch(char const* pattern, char const* string, int flags); - -__END_DECLS diff --git a/Userland/Libraries/LibC/getopt.cpp b/Userland/Libraries/LibC/getopt.cpp deleted file mode 100644 index 4f9a6bc816b..00000000000 --- a/Userland/Libraries/LibC/getopt.cpp +++ /dev/null @@ -1,94 +0,0 @@ -/* - * Copyright (c) 2020, Sergey Bugaev - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include -#include -#include - -int opterr = 1; -int optopt = 0; -int optind = 1; -int optreset = 0; -char* optarg = nullptr; - -// POSIX says, "When an element of argv[] contains multiple option characters, -// it is unspecified how getopt() determines which options have already been -// processed". Well, this is how we do it. -namespace { -Vector s_args; -OptionParser s_parser; -} - -int getopt(int argc, char* const* argv, char const* short_options) -{ - s_args.clear_with_capacity(); - s_args.ensure_capacity(argc); - for (auto i = 1; i < argc; ++i) - s_args.append({ argv[i], strlen(argv[i]) }); - - if (optind == 1 || optreset == 1) { - s_parser.reset_state(); - optind = 1; - optreset = 0; - } - - auto result = s_parser.getopt(s_args.span(), { short_options, strlen(short_options) }, {}, {}); - - optind += result.consumed_args; - optarg = result.optarg_value.map([](auto x) { return const_cast(x.characters_without_null_termination()); }).value_or(optarg); - optopt = result.optopt_value.value_or(optopt); - return result.result; -} - -int getopt_long(int argc, char* const* argv, char const* short_options, const struct option* long_options, int* out_long_option_index) -{ - s_args.clear_with_capacity(); - s_args.ensure_capacity(argc); - for (auto i = 1; i < argc; ++i) - s_args.append({ argv[i], strlen(argv[i]) }); - - size_t long_option_count = 0; - for (auto option = long_options; option && option->name; option += 1) - long_option_count++; - - Vector translated_long_options; - translated_long_options.ensure_capacity(long_option_count); - for (size_t i = 0; i < long_option_count; ++i) { - auto option = &long_options[i]; - - translated_long_options.append(OptionParser::Option { - .name = { option->name, strlen(option->name) }, - .requirement = option->has_arg == no_argument - ? AK::OptionParser::ArgumentRequirement::NoArgument - : option->has_arg == optional_argument - ? AK::OptionParser::ArgumentRequirement::HasOptionalArgument - : AK::OptionParser::ArgumentRequirement::HasRequiredArgument, - .flag = option->flag, - .val = option->val, - }); - } - - if (optind == 1 || optreset == 1) { - s_parser.reset_state(); - optind = 1; - optreset = 0; - } - - auto result = s_parser.getopt( - s_args.span(), - { short_options, strlen(short_options) }, - translated_long_options.span(), - out_long_option_index ? *out_long_option_index : Optional()); - - optind += result.consumed_args; - optarg = result.optarg_value.map([](auto x) { return const_cast(x.characters_without_null_termination()); }).value_or(optarg); - optopt = result.optopt_value.value_or(optopt); - return result.result; -} diff --git a/Userland/Libraries/LibC/getopt.h b/Userland/Libraries/LibC/getopt.h deleted file mode 100644 index 1d7b689deb0..00000000000 --- a/Userland/Libraries/LibC/getopt.h +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright (c) 2020, Sergey Bugaev - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include - -__BEGIN_DECLS - -#define no_argument 0 -#define required_argument 1 -#define optional_argument 2 - -struct option { - char const* name; - int has_arg; - int* flag; - int val; -}; - -int getopt_long(int argc, char* const* argv, char const* short_options, const struct option* long_options, int* out_long_option_index); - -__END_DECLS diff --git a/Userland/Libraries/LibC/getsubopt.cpp b/Userland/Libraries/LibC/getsubopt.cpp deleted file mode 100644 index ea24411bec2..00000000000 --- a/Userland/Libraries/LibC/getsubopt.cpp +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright (c) 2022, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/getsubopt.html -int getsubopt(char** option_array, char* const* tokens, char** option_value) -{ - if (**option_array == '\0') - return -1; - - auto const* option_ptr = *option_array; - StringView option_string { option_ptr, strlen(option_ptr) }; - - auto possible_comma_location = option_string.find(','); - char* option_end = const_cast(option_string.characters_without_null_termination()) + possible_comma_location.value_or(option_string.length()); - - auto possible_equals_char_location = option_string.find('='); - char* value_start = option_end; - if (possible_equals_char_location.has_value()) { - value_start = const_cast(option_string.characters_without_null_termination()) + possible_equals_char_location.value(); - } - - ScopeGuard ensure_end_array_contains_null_char([&]() { - if (*option_end != '\0') - *option_end++ = '\0'; - *option_array = option_end; - }); - - for (int count = 0; tokens[count] != NULL; ++count) { - auto const* token = tokens[count]; - StringView token_stringview { token, strlen(token) }; - if (!option_string.starts_with(token_stringview)) - continue; - if (tokens[count][value_start - *option_array] != '\0') - continue; - - *option_value = value_start != option_end ? value_start + 1 : nullptr; - return count; - } - - // Note: The current sub-option does not match any option, so prepare to tell this - // to the application. - *option_value = *option_array; - return -1; -} diff --git a/Userland/Libraries/LibC/glob.cpp b/Userland/Libraries/LibC/glob.cpp deleted file mode 100644 index 690f26fc761..00000000000 --- a/Userland/Libraries/LibC/glob.cpp +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright (c) 2021, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include - -extern "C" { - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/glob.html -int glob(char const*, int, int (*)(char const* epath, int eerrno), glob_t*) -{ - dbgln("FIXME: Implement glob()"); - TODO(); -} - -void globfree(glob_t*) -{ - dbgln("FIXME: Implement globfree()"); - TODO(); -} -} diff --git a/Userland/Libraries/LibC/glob.h b/Userland/Libraries/LibC/glob.h deleted file mode 100644 index 3a6cd6c52f8..00000000000 --- a/Userland/Libraries/LibC/glob.h +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright (c) 2021, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include - -__BEGIN_DECLS - -typedef struct { - size_t gl_pathc; - char** gl_pathv; - size_t gl_offs; -} glob_t; - -#define GLOB_APPEND (1 << 0) -#define GLOB_DOOFS (1 << 1) -#define GLOB_ERR (1 << 2) -#define GLOB_MARK (1 << 3) -#define GLOB_NOCHECK (1 << 4) -#define GLOB_NOESCAPE (1 << 5) -#define GLOB_NOSORT (1 << 6) - -#define GLOB_ABORTED 1 -#define GLOB_NOMATCH 2 -#define GLOB_NOSPACE 3 - -int glob(char const* pattern, int flags, int (*errfunc)(char const* epath, int eerrno), glob_t* pglob); - -void globfree(glob_t* pglob); - -__END_DECLS diff --git a/Userland/Libraries/LibC/grp.cpp b/Userland/Libraries/LibC/grp.cpp deleted file mode 100644 index 6d9ef042ce0..00000000000 --- a/Userland/Libraries/LibC/grp.cpp +++ /dev/null @@ -1,267 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * Copyright (c) 2021, Maxime Friess - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -extern "C" { - -static FILE* s_stream = nullptr; -static unsigned s_line_number = 0; - -void setgrent() -{ - s_line_number = 0; - if (s_stream) { - rewind(s_stream); - } else { - s_stream = fopen("/etc/group", "r"); - if (!s_stream) { - perror("open /etc/group"); - } - } -} - -void endgrent() -{ - s_line_number = 0; - if (s_stream) { - fclose(s_stream); - s_stream = nullptr; - } -} - -struct group* getgrgid(gid_t gid) -{ - setgrent(); - ScopeGuard guard = [] { endgrent(); }; - while (auto* gr = getgrent()) { - if (gr->gr_gid == gid) - return gr; - } - return nullptr; -} - -int getgrgid_r(gid_t gid, struct group* group_buf, char* buffer, size_t buffer_size, struct group** group_entry_ptr) -{ - while (0 == getgrent_r(group_buf, buffer, buffer_size, group_entry_ptr)) { - if (group_buf->gr_gid == gid) { - return 0; - } - } - return ENOENT; -} - -struct group* getgrnam(char const* name) -{ - setgrent(); - ScopeGuard guard = [] { endgrent(); }; - while (auto* gr = getgrent()) { - if (!strcmp(gr->gr_name, name)) - return gr; - } - return nullptr; -} -int getgrnam_r(char const* name, struct group* group_buf, char* buffer, size_t buffer_size, struct group** group_entry_ptr) -{ - while (0 == getgrent_r(group_buf, buffer, buffer_size, group_entry_ptr)) { - if (!strcmp(group_buf->gr_name, name)) - return 0; - } - return ENOENT; -} - -static bool parse_grpdb_entry(char* buffer, size_t buffer_size, struct group& group_entry) -{ - size_t line_length = strlen(buffer); - - for (size_t i = 0; i < line_length; ++i) { - auto& ch = buffer[i]; - if (ch == '\r' || ch == '\n') - line_length = i; - if (ch == ':' || ch == '\r' || ch == '\n') - ch = '\0'; - } - - auto line = StringView { buffer, line_length }; - auto parts = line.split_view('\0', SplitBehavior::KeepEmpty); - if (parts.size() != 4) { - warnln("parse_grpdb_entry(): Malformed entry on line {}: '{}' has {} parts", s_line_number, line, parts.size()); - return false; - } - - auto name = parts[0]; - auto passwd = parts[1]; - auto& gid_string = parts[2]; - StringView members_string = parts[3]; - - auto gid = gid_string.to_number(); - if (!gid.has_value()) { - warnln("parse_grpdb_entry(): Malformed GID on line {}", s_line_number); - return false; - } - - // Generate table of members pointers. - Vector members_ptrs; - auto members = members_string.split_view(','); - members_ptrs.clear_with_capacity(); - members_ptrs.ensure_capacity(members.size() + 1); - for (auto& member : members) { - members_ptrs.append(member.characters_without_null_termination()); - } - members_ptrs.append(nullptr); - - // Convert remaining commas to null terminators. Last gr_mem entry uses the whole line's null terminator. - // 3 for 3 null terminators. - size_t members_position = name.length() + passwd.length() + gid_string.length() + 3; - for (size_t i = members_position; i < line_length; i++) - if (buffer[i] == ',') - buffer[i] = '\0'; - - // Must have room at the end of the buffer for the new table. - // Remaining space is one byte past null terminator generated by original line. - size_t bytes_used = round_up_to_power_of_two(line_length + 1, alignof(char*)); - size_t ptrs_size = sizeof(char const*) * members_ptrs.size(); - - if (bytes_used + ptrs_size < buffer_size) { - char* buffer_remaining = buffer + bytes_used; - memcpy(buffer_remaining, members_ptrs.data(), ptrs_size); - - group_entry.gr_gid = gid.value(); - group_entry.gr_name = const_cast(name.characters_without_null_termination()); - group_entry.gr_passwd = const_cast(passwd.characters_without_null_termination()); - group_entry.gr_mem = reinterpret_cast(buffer_remaining); - - return true; - } else { - warnln("parse_grpdb_entry(): Provided buffer too small to fit table for gr_mem"); - errno = ERANGE; - return false; - } -} - -struct group* getgrent() -{ - static struct group group_entry; - static char buffer[1024]; - struct group* result; - if (getgrent_r(&group_entry, buffer, sizeof(buffer), &result) < 0) - return nullptr; - return result; -} - -int getgrent_r(struct group* group_buf, char* buffer, size_t buffer_size, struct group** group_entry_ptr) -{ - if (!s_stream) - setgrent(); - - while (true) { - if (!s_stream || feof(s_stream)) { - errno = EIO; - return -1; - } - - if (ferror(s_stream)) { - warnln("getgrent_r(): Read error: {}", strerror(ferror(s_stream))); - errno = EIO; - return -1; - } - - ++s_line_number; - char* s = fgets(buffer, buffer_size, s_stream); - - // Silently tolerate an empty line at the end. - if ((!s || !s[0]) && feof(s_stream)) { - *group_entry_ptr = nullptr; - return 0; - } - - if (strlen(s) == buffer_size - 1) { - errno = ERANGE; - return -1; - } - - if (parse_grpdb_entry(buffer, buffer_size, *group_buf)) { - *group_entry_ptr = group_buf; - return 0; - } - // Otherwise, proceed to the next line. - } -} - -int initgroups(char const* user, gid_t extra_gid) -{ - size_t count = 0; - gid_t gids[32]; - bool extra_gid_added = false; - setgrent(); - while (auto* gr = getgrent()) { - for (auto* mem = gr->gr_mem; *mem; ++mem) { - if (!strcmp(*mem, user)) { - gids[count++] = gr->gr_gid; - if (gr->gr_gid == extra_gid) - extra_gid_added = true; - break; - } - } - } - endgrent(); - if (!extra_gid_added) - gids[count++] = extra_gid; - return setgroups(count, gids); -} - -int putgrent(const struct group* group, FILE* stream) -{ - if (!group || !stream || !group->gr_name || !group->gr_passwd) { - errno = EINVAL; - return -1; - } - - auto is_valid_field = [](char const* str) { - return str && !strpbrk(str, ":\n"); - }; - - if (!is_valid_field(group->gr_name) || !is_valid_field(group->gr_passwd)) { - errno = EINVAL; - return -1; - } - - int nwritten = fprintf(stream, "%s:%s:%u:", group->gr_name, group->gr_passwd, group->gr_gid); - if (!nwritten || nwritten < 0) { - errno = ferror(stream); - return -1; - } - - if (group->gr_mem) { - for (size_t i = 0; group->gr_mem[i] != nullptr; i++) { - nwritten = fprintf(stream, i == 0 ? "%s" : ",%s", group->gr_mem[i]); - if (!nwritten || nwritten < 0) { - errno = ferror(stream); - return -1; - } - } - } - - nwritten = fprintf(stream, "\n"); - if (!nwritten || nwritten < 0) { - errno = ferror(stream); - return -1; - } - - return 0; -} -} diff --git a/Userland/Libraries/LibC/grp.h b/Userland/Libraries/LibC/grp.h deleted file mode 100644 index f66ad2e2b52..00000000000 --- a/Userland/Libraries/LibC/grp.h +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * Copyright (c) 2021, Maxime Friess - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include - -__BEGIN_DECLS - -struct group { - char* gr_name; - char* gr_passwd; - gid_t gr_gid; - char** gr_mem; -}; - -struct group* getgrent(void); -int getgrent_r(struct group* group_buf, char* buffer, size_t buffer_size, struct group** group_entry_ptr); -void setgrent(void); -void endgrent(void); -struct group* getgrnam(char const* name); -int getgrnam_r(char const* name, struct group* group_buf, char* buffer, size_t buffer_size, struct group** group_entry_ptr); -struct group* getgrgid(gid_t); -int getgrgid_r(gid_t gid, struct group* group_buf, char* buffer, size_t buffer_size, struct group** group_entry_ptr); -int putgrent(const struct group*, FILE*); - -int initgroups(char const* user, gid_t); - -__END_DECLS diff --git a/Userland/Libraries/LibC/ifaddrs.cpp b/Userland/Libraries/LibC/ifaddrs.cpp deleted file mode 100644 index 697579a3b69..00000000000 --- a/Userland/Libraries/LibC/ifaddrs.cpp +++ /dev/null @@ -1,18 +0,0 @@ -/* - * Copyright (c) 2021, Idan Horowitz - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include - -int getifaddrs(struct ifaddrs**) -{ - errno = ENOSYS; - return -1; -} - -void freeifaddrs(struct ifaddrs*) -{ -} diff --git a/Userland/Libraries/LibC/ifaddrs.h b/Userland/Libraries/LibC/ifaddrs.h deleted file mode 100644 index 7a10dd5e5d3..00000000000 --- a/Userland/Libraries/LibC/ifaddrs.h +++ /dev/null @@ -1,17 +0,0 @@ -/* - * Copyright (c) 2021, Idan Horowitz - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include - -__BEGIN_DECLS - -int getifaddrs(struct ifaddrs** ifap); -void freeifaddrs(struct ifaddrs* ifa); - -__END_DECLS diff --git a/Userland/Libraries/LibC/inttypes.cpp b/Userland/Libraries/LibC/inttypes.cpp deleted file mode 100644 index c4160ad4da3..00000000000 --- a/Userland/Libraries/LibC/inttypes.cpp +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright (c) 2021, Mițca Dumitru - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include - -extern "C" { - -imaxdiv_t imaxdiv(intmax_t numerator, intmax_t denominator) -{ - imaxdiv_t result; - result.quot = numerator / denominator; - result.rem = numerator % denominator; - - if (numerator >= 0 && result.rem < 0) { - result.quot++; - result.rem -= denominator; - } - - return result; -} - -intmax_t strtoimax(char const* str, char** endptr, int base) -{ - long long_value = strtoll(str, endptr, base); - - intmax_t max_int_value = NumericLimits::max(); - intmax_t min_int_value = NumericLimits::min(); - if (long_value > max_int_value) { - errno = -ERANGE; - return max_int_value; - } else if (long_value < min_int_value) { - errno = -ERANGE; - return min_int_value; - } - - return long_value; -} - -uintmax_t strtoumax(char const* str, char** endptr, int base) -{ - unsigned long ulong_value = strtoull(str, endptr, base); - - uintmax_t max_uint_value = NumericLimits::max(); - if (ulong_value > max_uint_value) { - errno = -ERANGE; - return max_uint_value; - } - - return ulong_value; -} -} diff --git a/Userland/Libraries/LibC/inttypes.h b/Userland/Libraries/LibC/inttypes.h deleted file mode 100644 index aad48a1022d..00000000000 --- a/Userland/Libraries/LibC/inttypes.h +++ /dev/null @@ -1,203 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * Copyright (c) 2022, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include - -__BEGIN_DECLS - -#define __PRI64_PREFIX "l" -#define __PRIPTR_PREFIX "l" - -#define PRId8 "d" -#define PRId16 "d" -#define PRId32 "d" -#define PRId64 __PRI64_PREFIX "d" -#define PRIdPTR __PRIPTR_PREFIX "d" -#define PRIdMAX __PRI64_PREFIX "d" - -#define PRIdLEAST8 PRId8 -#define PRIdLEAST16 PRId16 -#define PRIdLEAST32 PRId32 -#define PRIdLEAST64 PRId64 -#define PRIdFAST8 PRId8 -#define PRIdFAST16 PRId16 -#define PRIdFAST32 PRId32 -#define PRIdFAST64 PRId64 - -#define PRIi8 "d" -#define PRIi16 "d" -#define PRIi32 "d" -#define PRIi64 __PRI64_PREFIX "d" -#define PRIiPTR __PRIPTR_PREFIX "d" -#define PRIiMAX __PRI64_PREFIX "d" - -#define PRIiLEAST8 PRIi8 -#define PRIiLEAST16 PRIi16 -#define PRIiLEAST32 PRIi32 -#define PRIiLEAST64 PRIi64 -#define PRIiFAST8 PRIi8 -#define PRIiFAST16 PRIi16 -#define PRIiFAST32 PRIi32 -#define PRIiFAST64 PRIi64 - -#define PRIo8 "o" -#define PRIo16 "o" -#define PRIo32 "o" -#define PRIo64 __PRI64_PREFIX "o" -#define PRIoPTR __PRIPTR_PREFIX "o" -#define PRIoMAX __PRI64_PREFIX "o" - -#define PRIoLEAST8 PRIo8 -#define PRIoLEAST16 PRIo16 -#define PRIoLEAST32 PRIo32 -#define PRIoLEAST64 PRIo64 -#define PRIoFAST8 PRIo8 -#define PRIoFAST16 PRIo16 -#define PRIoFAST32 PRIo32 -#define PRIoFAST64 PRIo64 - -#define PRIu8 "u" -#define PRIu16 "u" -#define PRIu32 "u" -#define PRIu64 __PRI64_PREFIX "u" -#define PRIuPTR __PRIPTR_PREFIX "u" -#define PRIuMAX __PRI64_PREFIX "u" - -#define PRIuLEAST8 PRIu8 -#define PRIuLEAST16 PRIu16 -#define PRIuLEAST32 PRIu32 -#define PRIuLEAST64 PRIu64 -#define PRIuFAST8 PRIu8 -#define PRIuFAST16 PRIu16 -#define PRIuFAST32 PRIu32 -#define PRIuFAST64 PRIu64 - -#define PRIx8 "x" -#define PRIx16 "x" -#define PRIx32 "x" -#define PRIx64 __PRI64_PREFIX "x" -#define PRIxPTR __PRIPTR_PREFIX "x" -#define PRIxMAX __PRI64_PREFIX "x" - -#define PRIxLEAST8 PRIx8 -#define PRIxLEAST16 PRIx16 -#define PRIxLEAST32 PRIx32 -#define PRIxLEAST64 PRIx64 -#define PRIxFAST8 PRIx8 -#define PRIxFAST16 PRIx16 -#define PRIxFAST32 PRIx32 -#define PRIxFAST64 PRIx64 - -#define PRIX8 "X" -#define PRIX16 "X" -#define PRIX32 "X" -#define PRIX64 __PRI64_PREFIX "X" -#define PRIXPTR __PRIPTR_PREFIX "X" -#define PRIXMAX __PRI64_PREFIX "X" - -#define PRIXLEAST8 PRIX8 -#define PRIXLEAST16 PRIX16 -#define PRIXLEAST32 PRIX32 -#define PRIXLEAST64 PRIX64 -#define PRIXFAST8 PRIX8 -#define PRIXFAST16 PRIX16 -#define PRIXFAST32 PRIX32 -#define PRIXFAST64 PRIX64 - -#define SCNd8 "hhd" -#define SCNd16 "hd" -#define SCNd32 "ld" -#define SCNd64 __PRI64_PREFIX "d" -#define SCNdPTR __PRIPTR_PREFIX "d" -#define SCNdMAX __PRI64_PREFIX "d" - -#define SCNdLEAST8 SCNd8 -#define SCNdLEAST16 SCNd16 -#define SCNdLEAST32 SCNd32 -#define SCNdLEAST64 SCNd64 -#define SCNdFAST8 SCNd8 -#define SCNdFAST16 SCNd16 -#define SCNdFAST32 SCNd32 -#define SCNdFAST64 SCNd64 - -#define SCNi8 "hhi" -#define SCNi16 "hi" -#define SCNi32 "li" -#define SCNi64 __PRI64_PREFIX "i" -#define SCNiPTR __PRIPTR_PREFIX "i" -#define SCNiMAX __PRI64_PREFIX "i" - -#define SCNiLEAST8 SCNi8 -#define SCNiLEAST16 SCNi16 -#define SCNiLEAST32 SCNi32 -#define SCNiLEAST64 SCNi64 -#define SCNiFAST8 SCNi8 -#define SCNiFAST16 SCNi16 -#define SCNiFAST32 SCNi32 -#define SCNiFAST64 SCNi64 - -#define SCNu8 "hhu" -#define SCNu16 "hu" -#define SCNu32 "lu" -#define SCNu64 __PRI64_PREFIX "u" -#define SCNuPTR __PRIPTR_PREFIX "u" -#define SCNuMAX __PRI64_PREFIX "u" - -#define SCNuLEAST8 SCNu8 -#define SCNuLEAST16 SCNu16 -#define SCNuLEAST32 SCNu32 -#define SCNuLEAST64 SCNu64 -#define SCNuFAST8 SCNu8 -#define SCNuFAST16 SCNu16 -#define SCNuFAST32 SCNu32 -#define SCNuFAST64 SCNu64 - -#define SCNo8 "hho" -#define SCNo16 "ho" -#define SCNo32 "lo" -#define SCNo64 __PRI64_PREFIX "o" -#define SCNoPTR __PRIPTR_PREFIX "o" -#define SCNoMAX __PRI64_PREFIX "o" - -#define SCNoLEAST8 SCNo8 -#define SCNoLEAST16 SCNo16 -#define SCNoLEAST32 SCNo32 -#define SCNoLEAST64 SCNo64 -#define SCNoFAST8 SCNo8 -#define SCNoFAST16 SCNo16 -#define SCNoFAST32 SCNo32 -#define SCNoFAST64 SCNo64 - -#define SCNx8 "hhx" -#define SCNx16 "hx" -#define SCNx32 "lx" -#define SCNx64 __PRI64_PREFIX "x" -#define SCNxPTR __PRIPTR_PREFIX "x" -#define SCNxMAX __PRI64_PREFIX "x" - -#define SCNxLEAST8 SCNx8 -#define SCNxLEAST16 SCNx16 -#define SCNxLEAST32 SCNx32 -#define SCNxLEAST64 SCNx64 -#define SCNxFAST8 SCNx8 -#define SCNxFAST16 SCNx16 -#define SCNxFAST32 SCNx32 -#define SCNxFAST64 SCNx64 - -typedef struct imaxdiv_t { - intmax_t quot; - intmax_t rem; -} imaxdiv_t; -imaxdiv_t imaxdiv(intmax_t, intmax_t); - -intmax_t strtoimax(char const*, char** endptr, int base); -uintmax_t strtoumax(char const*, char** endptr, int base); - -__END_DECLS diff --git a/Userland/Libraries/LibC/ioctl.cpp b/Userland/Libraries/LibC/ioctl.cpp deleted file mode 100644 index 8267e5e8d05..00000000000 --- a/Userland/Libraries/LibC/ioctl.cpp +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include - -extern "C" { - -int ioctl(int fd, unsigned request, ...) -{ - va_list ap; - va_start(ap, request); - FlatPtr arg = va_arg(ap, FlatPtr); - int rc = syscall(SC_ioctl, fd, request, arg); - va_end(ap); - __RETURN_WITH_ERRNO(rc, rc, -1); -} -} diff --git a/Userland/Libraries/LibC/langinfo.cpp b/Userland/Libraries/LibC/langinfo.cpp deleted file mode 100644 index a44f70afe16..00000000000 --- a/Userland/Libraries/LibC/langinfo.cpp +++ /dev/null @@ -1,99 +0,0 @@ -/* - * Copyright (c) 2021, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include - -// Values taken from glibc's en_US locale files. -static char const* __nl_langinfo(nl_item item) -{ - switch (item) { - case CODESET: - return "UTF-8"; - case D_T_FMT: - return "%a %d %b %Y %r %Z"; - case D_FMT: - return "%m/%d/%Y"; - case T_FMT: - return "%r"; - case T_FMT_AMPM: - return "%I:%M:%S %p"; - case AM_STR: - return "AM"; - case PM_STR: - return "PM"; - case DAY_1: - case DAY_2: - case DAY_3: - case DAY_4: - case DAY_5: - case DAY_6: - case DAY_7: - return long_day_names[item - DAY_1].characters_without_null_termination(); - case ABDAY_1: - case ABDAY_2: - case ABDAY_3: - case ABDAY_4: - case ABDAY_5: - case ABDAY_6: - case ABDAY_7: - return short_day_names[item - ABDAY_1].characters_without_null_termination(); - case MON_1: - case MON_2: - case MON_3: - case MON_4: - case MON_5: - case MON_6: - case MON_7: - case MON_8: - case MON_9: - case MON_10: - case MON_11: - case MON_12: - return long_month_names[item - MON_1].characters_without_null_termination(); - case ABMON_1: - case ABMON_2: - case ABMON_3: - case ABMON_4: - case ABMON_5: - case ABMON_6: - case ABMON_7: - case ABMON_8: - case ABMON_9: - case ABMON_10: - case ABMON_11: - case ABMON_12: - return short_month_names[item - ABMON_1].characters_without_null_termination(); - case RADIXCHAR: - return "."; - case THOUSEP: - return ","; - case YESEXPR: - return "^[+1yY]"; - case NOEXPR: - return "^[-0nN]"; - // en_US does not have ERA. - case ERA: - case ERA_D_FMT: - case ERA_D_T_FMT: - case ERA_T_FMT: - // en_US also doesn't have special digit symbols. - case ALT_DIGITS: - // Invalid values also return an empty string. - default: - return ""; - } -} - -extern "C" { - -char* nl_langinfo(nl_item item) -{ - // POSIX states that returned strings should not be modified, - // so this cast is probably fine. - return const_cast(__nl_langinfo(item)); -} -} diff --git a/Userland/Libraries/LibC/langinfo.h b/Userland/Libraries/LibC/langinfo.h deleted file mode 100644 index f5836915ba6..00000000000 --- a/Userland/Libraries/LibC/langinfo.h +++ /dev/null @@ -1,84 +0,0 @@ -/* - * Copyright (c) 2021, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -// Includes essentially mandated by POSIX: -// https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/langinfo.h.html -#include - -#include - -__BEGIN_DECLS - -enum { - CODESET, - - D_T_FMT, - D_FMT, - T_FMT, - T_FMT_AMPM, - AM_STR, - PM_STR, - - DAY_1, - DAY_2, - DAY_3, - DAY_4, - DAY_5, - DAY_6, - DAY_7, - - ABDAY_1, - ABDAY_2, - ABDAY_3, - ABDAY_4, - ABDAY_5, - ABDAY_6, - ABDAY_7, - - MON_1, - MON_2, - MON_3, - MON_4, - MON_5, - MON_6, - MON_7, - MON_8, - MON_9, - MON_10, - MON_11, - MON_12, - - ABMON_1, - ABMON_2, - ABMON_3, - ABMON_4, - ABMON_5, - ABMON_6, - ABMON_7, - ABMON_8, - ABMON_9, - ABMON_10, - ABMON_11, - ABMON_12, - - ERA, - ERA_D_FMT, - ERA_D_T_FMT, - ERA_T_FMT, - - ALT_DIGITS, - RADIXCHAR, - THOUSEP, - YESEXPR, - NOEXPR, - CRNCYSTR, -}; - -char* nl_langinfo(nl_item); - -__END_DECLS diff --git a/Userland/Libraries/LibC/libcinit.cpp b/Userland/Libraries/LibC/libcinit.cpp deleted file mode 100644 index 11148fd9394..00000000000 --- a/Userland/Libraries/LibC/libcinit.cpp +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright (c) 2018-2022, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include -#include - -[[gnu::weak]] char** __environ_value() asm("__environ_value"); - -extern "C" { - -#ifdef NO_TLS -int errno_storage; -#else -__thread int errno_storage; -#endif -bool __environ_is_malloced = false; -bool __stdio_is_initialized = false; -void* __auxiliary_vector = reinterpret_cast(explode_byte(0xe1)); - -#ifndef _DYNAMIC_LOADER -char** environ = reinterpret_cast(explode_byte(0xe2)); -uintptr_t __stack_chk_guard; -#endif - -int* __errno_location() -{ - return &errno_storage; -} - -void __libc_init() -{ -#ifndef _DYNAMIC_LOADER - // We can only call magic functions until __stack_chk_guard is initialized. - environ = __environ_value(); -#endif - - char** env; - for (env = environ; *env; ++env) - ; - __auxiliary_vector = (void*)++env; - -#ifndef _DYNAMIC_LOADER - for (auxv_t* entry = reinterpret_cast(__auxiliary_vector); entry->a_type != AT_NULL; ++entry) - if (entry->a_type == AT_RANDOM) - __stack_chk_guard = *(reinterpret_cast(entry->a_un.a_ptr) + 1); - - // We include an additional hardening: zero the first byte of the stack guard to avoid leaking - // or overwriting the stack guard with C-style string functions. - __stack_chk_guard &= ~0xffULL; -#endif - __malloc_init(); - __stdio_init(); -} -} diff --git a/Userland/Libraries/LibC/libgen.cpp b/Userland/Libraries/LibC/libgen.cpp deleted file mode 100644 index e31533b6ca8..00000000000 --- a/Userland/Libraries/LibC/libgen.cpp +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright (c) 2019-2020, Sergey Bugaev - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include - -static char dot[] = "."; -static char slash[] = "/"; - -char* dirname(char* path) -{ - if (path == nullptr) - return dot; - - int len = strlen(path); - if (len == 0) - return dot; - - while (len > 1 && path[len - 1] == '/') { - path[len - 1] = 0; - len--; - } - - char* last_slash = strrchr(path, '/'); - if (last_slash == nullptr) - return dot; - - if (last_slash == path) - return slash; - - *last_slash = 0; - return path; -} - -char* basename(char* path) -{ - if (path == nullptr) - return dot; - - int len = strlen(path); - if (len == 0) - return dot; - - while (len > 1 && path[len - 1] == '/') { - path[len - 1] = 0; - len--; - } - - char* last_slash = strrchr(path, '/'); - if (last_slash == nullptr) - return path; - - if (len == 1) { - VERIFY(last_slash == path); - VERIFY(path[0] == '/'); - return slash; - } - - return last_slash + 1; -} diff --git a/Userland/Libraries/LibC/libgen.h b/Userland/Libraries/LibC/libgen.h deleted file mode 100644 index e9ea22f57c2..00000000000 --- a/Userland/Libraries/LibC/libgen.h +++ /dev/null @@ -1,16 +0,0 @@ -/* - * Copyright (c) 2019-2020, Sergey Bugaev - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include - -__BEGIN_DECLS - -char* dirname(char* path); -char* basename(char* path); - -__END_DECLS diff --git a/Userland/Libraries/LibC/limits.h b/Userland/Libraries/LibC/limits.h deleted file mode 100644 index b873b4cf3e4..00000000000 --- a/Userland/Libraries/LibC/limits.h +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Copyright (c) 2018-2022, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include - -#define PIPE_BUF 4096 - -#define INT_MAX INT32_MAX -#define INT_MIN INT32_MIN - -#define UINT_MAX UINT32_MAX - -#define CHAR_BIT 8 -#define SCHAR_MIN (-128) -#define SCHAR_MAX 127 -#define UCHAR_MAX 255 - -#define SHRT_MAX 32767 -#define SHRT_MIN (-SHRT_MAX - 1) - -#define USHRT_MAX 65535 - -#define LONG_MAX 9223372036854775807L -#define LONG_MIN (-LONG_MAX - 1L) - -#define ULONG_MAX 18446744073709551615UL - -#define LONG_LONG_MAX 9223372036854775807LL -#define LONG_LONG_MIN (-LONG_LONG_MAX - 1LL) - -#define LLONG_MAX LONG_LONG_MAX -#define LLONG_MIN LONG_LONG_MIN - -#define ULONG_LONG_MAX 18446744073709551615ULL -#define ULLONG_MAX ULONG_LONG_MAX - -#define CHAR_MIN SCHAR_MIN -#define CHAR_MAX SCHAR_MAX - -#define CHAR_WIDTH 8 -#define SCHAR_WIDTH 8 -#define UCHAR_WIDTH 8 - -#define SHRT_WIDTH 16 -#define USHRT_WIDTH 16 - -#define INT_WIDTH 32 -#define UINT_WIDTH 32 - -#define LONG_WIDTH 64 -#define ULONG_WIDTH 64 - -#define LLONG_WIDTH 64 -#define ULLONG_WIDTH 64 - -#define SSIZE_MAX LONG_MAX - -#define LINK_MAX 4096 - -#define TZNAME_MAX 64 - -#define PASS_MAX 128 diff --git a/Userland/Libraries/LibC/link.cpp b/Userland/Libraries/LibC/link.cpp deleted file mode 100644 index 09896048d30..00000000000 --- a/Userland/Libraries/LibC/link.cpp +++ /dev/null @@ -1,16 +0,0 @@ -/* - * Copyright (c) 2021, Gunnar Beutner - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include - -using DlIteratePhdrCallbackFunction = int (*)(struct dl_phdr_info*, size_t, void*); -[[gnu::weak]] extern int __dl_iterate_phdr(DlIteratePhdrCallbackFunction, void*) asm("__dl_iterate_phdr"); - -extern "C" int dl_iterate_phdr(int (*callback)(struct dl_phdr_info* info, size_t size, void* data), void* data) -{ - return __dl_iterate_phdr(callback, data); -} diff --git a/Userland/Libraries/LibC/link.h b/Userland/Libraries/LibC/link.h deleted file mode 100644 index cab297f93e9..00000000000 --- a/Userland/Libraries/LibC/link.h +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright (c) 2021, Gunnar Beutner - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include - -__BEGIN_DECLS - -struct dl_phdr_info { - Elf_Addr dlpi_addr; - char const* dlpi_name; - Elf_Phdr const* dlpi_phdr; - Elf_Half dlpi_phnum; -}; - -int dl_iterate_phdr(int (*callback)(struct dl_phdr_info* info, size_t size, void* data), void* data); - -__END_DECLS diff --git a/Userland/Libraries/LibC/locale.cpp b/Userland/Libraries/LibC/locale.cpp deleted file mode 100644 index 36ff8010d42..00000000000 --- a/Userland/Libraries/LibC/locale.cpp +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include - -extern "C" { - -static char default_decimal_point[] = "."; -static char default_thousands_sep[] = ","; -static char default_grouping[] = "\x03\x03"; - -static char default_empty_string[] = ""; -static char default_empty_value = 127; - -static struct lconv default_locale = { - default_decimal_point, - default_thousands_sep, - default_grouping, - default_empty_string, - default_empty_string, - default_empty_string, - default_empty_string, - default_empty_string, - default_empty_string, - default_empty_string, - default_empty_value, - default_empty_value, - default_empty_value, - default_empty_value, - default_empty_value, - default_empty_value, - default_empty_value, - default_empty_value, - default_empty_value, - default_empty_value, - default_empty_value, - default_empty_value, - default_empty_value, - default_empty_value -}; - -char* setlocale(int, char const* locale) -{ - static char c_locale_string[2]; - memcpy(c_locale_string, "C", 2); - - // If we get a null pointer, return the current locale as per POSIX spec. - if (locale == nullptr) - return c_locale_string; - - if (strcmp(locale, "POSIX") == 0 || strcmp(locale, "C") == 0 || strcmp(locale, "") == 0) - return c_locale_string; - - return nullptr; -} - -struct lconv* localeconv() -{ - return &default_locale; -} -} diff --git a/Userland/Libraries/LibC/locale.h b/Userland/Libraries/LibC/locale.h deleted file mode 100644 index 0fe99ba7d81..00000000000 --- a/Userland/Libraries/LibC/locale.h +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include - -__BEGIN_DECLS - -enum { - LC_ALL, -#define LC_ALL LC_ALL - LC_NUMERIC, -#define LC_NUMERIC LC_NUMERIC - LC_CTYPE, -#define LC_CTYPE LC_CTYPE - LC_COLLATE, -#define LC_COLLATE LC_COLLATE - LC_TIME, -#define LC_TIME LC_TIME - LC_MONETARY, -#define LC_MONETARY LC_MONETARY - LC_MESSAGES, -#define LC_MESSAGES LC_MESSAGES -}; - -struct lconv { - char* decimal_point; - char* thousands_sep; - char* grouping; - char* int_curr_symbol; - char* currency_symbol; - char* mon_decimal_point; - char* mon_thousands_sep; - char* mon_grouping; - char* positive_sign; - char* negative_sign; - char int_frac_digits; - char frac_digits; - char p_cs_precedes; - char p_sep_by_space; - char n_cs_precedes; - char n_sep_by_space; - char p_sign_posn; - char n_sign_posn; - char int_p_cs_precedes; - char int_p_sep_by_space; - char int_n_cs_precedes; - char int_n_sep_by_space; - char int_p_sign_posn; - char int_n_sign_posn; -}; - -struct lconv* localeconv(void); -char* setlocale(int category, char const* locale); - -__END_DECLS diff --git a/Userland/Libraries/LibC/malloc.cpp b/Userland/Libraries/LibC/malloc.cpp deleted file mode 100644 index 7077d2fc333..00000000000 --- a/Userland/Libraries/LibC/malloc.cpp +++ /dev/null @@ -1,708 +0,0 @@ -/* - * Copyright (c) 2018-2021, Andreas Kling - * Copyright (c) 2022, Peter Elliott - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -class PthreadMutexLocker { -public: - ALWAYS_INLINE explicit PthreadMutexLocker(pthread_mutex_t& mutex) - : m_mutex(mutex) - { - lock(); - __heap_is_stable = false; - } - ALWAYS_INLINE ~PthreadMutexLocker() - { - __heap_is_stable = true; - unlock(); - } - ALWAYS_INLINE void lock() { pthread_mutex_lock(&m_mutex); } - ALWAYS_INLINE void unlock() { pthread_mutex_unlock(&m_mutex); } - -private: - pthread_mutex_t& m_mutex; -}; - -#define RECYCLE_BIG_ALLOCATIONS - -static pthread_mutex_t s_malloc_mutex = PTHREAD_MUTEX_INITIALIZER; -bool __heap_is_stable = true; - -constexpr size_t number_of_hot_chunked_blocks_to_keep_around = 16; -constexpr size_t number_of_cold_chunked_blocks_to_keep_around = 16; -constexpr size_t number_of_big_blocks_to_keep_around_per_size_class = 8; - -static bool s_log_malloc = false; -static bool s_scrub_malloc = true; -static bool s_scrub_free = true; -static bool s_profiling = false; -static bool s_in_userspace_emulator = false; - -ALWAYS_INLINE static void ue_notify_malloc(void const* ptr, size_t size) -{ - if (s_in_userspace_emulator) - syscall(SC_emuctl, 1, size, (FlatPtr)ptr); -} - -ALWAYS_INLINE static void ue_notify_free(void const* ptr) -{ - if (s_in_userspace_emulator) - syscall(SC_emuctl, 2, (FlatPtr)ptr, 0); -} - -ALWAYS_INLINE static void ue_notify_realloc(void const* ptr, size_t size) -{ - if (s_in_userspace_emulator) - syscall(SC_emuctl, 3, size, (FlatPtr)ptr); -} - -ALWAYS_INLINE static void ue_notify_chunk_size_changed(void const* block, size_t chunk_size) -{ - if (s_in_userspace_emulator) - syscall(SC_emuctl, 4, chunk_size, (FlatPtr)block); -} - -struct MemoryAuditingSuppressor { - ALWAYS_INLINE MemoryAuditingSuppressor() - { - if (s_in_userspace_emulator) - syscall(SC_emuctl, 7); - } - ALWAYS_INLINE ~MemoryAuditingSuppressor() - { - if (s_in_userspace_emulator) - syscall(SC_emuctl, 8); - } -}; - -struct MallocStats { - size_t number_of_malloc_calls; - - size_t number_of_big_allocator_hits; - size_t number_of_big_allocator_purge_hits; - size_t number_of_big_allocs; - - size_t number_of_hot_empty_block_hits; - size_t number_of_cold_empty_block_hits; - size_t number_of_cold_empty_block_purge_hits; - size_t number_of_block_allocs; - size_t number_of_blocks_full; - - size_t number_of_free_calls; - - size_t number_of_big_allocator_keeps; - size_t number_of_big_allocator_frees; - - size_t number_of_freed_full_blocks; - size_t number_of_hot_keeps; - size_t number_of_cold_keeps; - size_t number_of_frees; -}; -static MallocStats g_malloc_stats = {}; - -static size_t s_hot_empty_block_count { 0 }; -static ChunkedBlock* s_hot_empty_blocks[number_of_hot_chunked_blocks_to_keep_around] { nullptr }; -static size_t s_cold_empty_block_count { 0 }; -static ChunkedBlock* s_cold_empty_blocks[number_of_cold_chunked_blocks_to_keep_around] { nullptr }; - -struct Allocator { - size_t size { 0 }; - size_t block_count { 0 }; - ChunkedBlock::List usable_blocks; - ChunkedBlock::List full_blocks; -}; - -struct BigAllocator { - Vector blocks; -}; - -// Allocators will be initialized in __malloc_init. -// We can not rely on global constructors to initialize them, -// because they must be initialized before other global constructors -// are run. Similarly, we can not allow global destructors to destruct -// them. We could have used AK::NeverDestoyed to prevent the latter, -// but it would have not helped with the former. -alignas(Allocator) static u8 g_allocators_storage[sizeof(Allocator) * num_size_classes]; -alignas(BigAllocator) static u8 g_big_allocators_storage[sizeof(BigAllocator)]; - -static inline Allocator (&allocators())[num_size_classes] -{ - return reinterpret_cast(g_allocators_storage); -} - -static inline BigAllocator (&big_allocators())[1] -{ - return reinterpret_cast(g_big_allocators_storage); -} - -// --- BEGIN MATH --- -// This stuff is only used for checking if there exists an aligned block in a -// chunk. It has no bearing on the rest of the allocator, especially for -// regular malloc. - -static inline unsigned long modulo(long a, long b) -{ - return (b + (a % b)) % b; -} - -struct EuclideanResult { - long x; - long y; - long gcd; -}; - -// Returns x, y, gcd. -static inline EuclideanResult extended_euclid(long a, long b) -{ - EuclideanResult old = { 1, 0, a }; - EuclideanResult current = { 0, 1, b }; - - while (current.gcd != 0) { - long quotient = old.gcd / current.gcd; - - EuclideanResult next = { - old.x - quotient * current.x, - old.y - quotient * current.y, - old.gcd - quotient * current.gcd, - }; - - old = current; - current = next; - } - - return old; -} - -static inline bool block_has_aligned_chunk(long align, long bytes_per_chunk, long chunk_capacity) -{ - // Never do math on a normal malloc. - if ((size_t)align <= sizeof(ChunkedBlock)) - return true; - - // Solve the linear congruence n*bytes_per_chunk = -sizeof(ChunkedBlock) (mod align). - auto [x, y, gcd] = extended_euclid(bytes_per_chunk % align, align); - long constant = modulo(-sizeof(ChunkedBlock), align); - if (constant % gcd != 0) - // No solution. Chunk size is probably a multiple of align. - return false; - - long n = modulo(x * (constant / gcd), align); - if (x < 0) - n = (n + align / gcd) % align; - - // Don't ask me to prove this. - VERIFY(n > 0); - return n < chunk_capacity; -} - -// --- END MATH --- - -static Allocator* allocator_for_size(size_t size, size_t& good_size, size_t align = 1) -{ - for (size_t i = 0; size_classes[i]; ++i) { - auto& allocator = allocators()[i]; - if (size <= size_classes[i] && block_has_aligned_chunk(align, allocator.size, (ChunkedBlock::block_size - sizeof(ChunkedBlock)) / allocator.size)) { - good_size = size_classes[i]; - return &allocator; - } - } - good_size = PAGE_ROUND_UP(size); - return nullptr; -} - -#ifdef RECYCLE_BIG_ALLOCATIONS -static BigAllocator* big_allocator_for_size(size_t size) -{ - if (size == 65536) - return &big_allocators()[0]; - return nullptr; -} -#endif - -extern "C" { - -static ErrorOr os_alloc(size_t size, char const* name) -{ - int flags = MAP_ANONYMOUS | MAP_PRIVATE | MAP_PURGEABLE; -#if ARCH(X86_64) - flags |= MAP_RANDOMIZED; -#endif - auto* ptr = serenity_mmap(nullptr, size, PROT_READ | PROT_WRITE, flags, 0, 0, ChunkedBlock::block_size, name); - VERIFY(ptr != nullptr); - if (ptr == MAP_FAILED) { - return ENOMEM; - } - return ptr; -} - -static void os_free(void* ptr, size_t size) -{ - int rc = munmap(ptr, size); - VERIFY(rc == 0); -} - -static void* try_allocate_chunk_aligned(size_t align, ChunkedBlock& block) -{ - // These loops are guaranteed to run only once for a standard-aligned malloc. - for (FreelistEntry** entry = &(block.m_freelist); *entry != nullptr; entry = &((*entry)->next)) { - if ((reinterpret_cast(*entry) & (align - 1)) == 0) { - --block.m_free_chunks; - void* ptr = *entry; - *entry = (*entry)->next; // Delete the entry. - return ptr; - } - } - for (; block.m_next_lazy_freelist_index < block.chunk_capacity(); block.m_next_lazy_freelist_index++) { - void* ptr = block.m_slot + block.m_next_lazy_freelist_index * block.m_size; - if ((reinterpret_cast(ptr) & (align - 1)) == 0) { - --block.m_free_chunks; - block.m_next_lazy_freelist_index++; - return ptr; - } - auto* entry = (FreelistEntry*)ptr; - entry->next = block.m_freelist; - block.m_freelist = entry; - } - return nullptr; -} - -enum class CallerWillInitializeMemory { - No, - Yes, -}; - -#ifndef NO_TLS -__thread bool s_allocation_enabled = true; -#endif - -static ErrorOr malloc_impl(size_t size, size_t align, CallerWillInitializeMemory caller_will_initialize_memory) -{ -#ifndef NO_TLS - VERIFY(s_allocation_enabled); -#endif - - // Align must be a power of 2. - if (popcount(align) != 1) - return EINVAL; - - // FIXME: Support larger than 32KiB alignments (if you dare). - if (sizeof(BigAllocationBlock) + align >= ChunkedBlock::block_size) - return EINVAL; - - if (s_log_malloc) - dbgln("LibC: malloc({})", size); - - if (!size) { - // Legally we could just return a null pointer here, but this is more - // compatible with existing software. - size = 1; - } - - g_malloc_stats.number_of_malloc_calls++; - - size_t good_size; - auto* allocator = allocator_for_size(size, good_size, align); - - PthreadMutexLocker locker(s_malloc_mutex); - - if (!allocator) { - size_t real_size = round_up_to_power_of_two(sizeof(BigAllocationBlock) + size + ((align > 16) ? align : 0), ChunkedBlock::block_size); - if (real_size < size) { - dbgln_if(MALLOC_DEBUG, "LibC: Detected overflow trying to do big allocation of size {} for {}", real_size, size); - return ENOMEM; - } -#ifdef RECYCLE_BIG_ALLOCATIONS - if (auto* allocator = big_allocator_for_size(real_size)) { - if (!allocator->blocks.is_empty()) { - g_malloc_stats.number_of_big_allocator_hits++; - auto* block = allocator->blocks.take_last(); - int rc = madvise(block, real_size, MADV_SET_NONVOLATILE); - bool this_block_was_purged = rc == 1; - if (rc < 0) { - perror("madvise"); - VERIFY_NOT_REACHED(); - } - if (mprotect(block, real_size, PROT_READ | PROT_WRITE) < 0) { - perror("mprotect"); - VERIFY_NOT_REACHED(); - } - if (this_block_was_purged) { - g_malloc_stats.number_of_big_allocator_purge_hits++; - new (block) BigAllocationBlock(real_size); - } - - void* ptr = reinterpret_cast(round_up_to_power_of_two(reinterpret_cast(&block->m_slot[0]), align)); - - ue_notify_malloc(ptr, size); - return ptr; - } - } -#endif - auto* block = (BigAllocationBlock*)TRY(os_alloc(real_size, "malloc: BigAllocationBlock")); - g_malloc_stats.number_of_big_allocs++; - new (block) BigAllocationBlock(real_size); - - void* ptr = reinterpret_cast(round_up_to_power_of_two(reinterpret_cast(&block->m_slot[0]), align)); - ue_notify_malloc(ptr, size); - return ptr; - } - - ChunkedBlock* block = nullptr; - void* ptr = nullptr; - for (auto& current : allocator->usable_blocks) { - if (current.free_chunks()) { - ptr = try_allocate_chunk_aligned(align, current); - if (ptr) { - block = ¤t; - break; - } - } - } - - if (!block && s_hot_empty_block_count) { - g_malloc_stats.number_of_hot_empty_block_hits++; - block = s_hot_empty_blocks[--s_hot_empty_block_count]; - if (block->m_size != good_size) { - new (block) ChunkedBlock(good_size); - ue_notify_chunk_size_changed(block, good_size); - char buffer[64]; - snprintf(buffer, sizeof(buffer), "malloc: ChunkedBlock(%zu)", good_size); - set_mmap_name(block, ChunkedBlock::block_size, buffer); - } - allocator->usable_blocks.append(*block); - } - - if (!block && s_cold_empty_block_count) { - g_malloc_stats.number_of_cold_empty_block_hits++; - block = s_cold_empty_blocks[--s_cold_empty_block_count]; - int rc = madvise(block, ChunkedBlock::block_size, MADV_SET_NONVOLATILE); - bool this_block_was_purged = rc == 1; - if (rc < 0) { - perror("madvise"); - VERIFY_NOT_REACHED(); - } - rc = mprotect(block, ChunkedBlock::block_size, PROT_READ | PROT_WRITE); - if (rc < 0) { - perror("mprotect"); - VERIFY_NOT_REACHED(); - } - if (this_block_was_purged || block->m_size != good_size) { - if (this_block_was_purged) - g_malloc_stats.number_of_cold_empty_block_purge_hits++; - new (block) ChunkedBlock(good_size); - ue_notify_chunk_size_changed(block, good_size); - } - allocator->usable_blocks.append(*block); - } - - if (!block) { - g_malloc_stats.number_of_block_allocs++; - char buffer[64]; - snprintf(buffer, sizeof(buffer), "malloc: ChunkedBlock(%zu)", good_size); - block = (ChunkedBlock*)TRY(os_alloc(ChunkedBlock::block_size, buffer)); - new (block) ChunkedBlock(good_size); - allocator->usable_blocks.append(*block); - ++allocator->block_count; - } - - if (!ptr) { - ptr = try_allocate_chunk_aligned(align, *block); - } - - VERIFY(ptr); - if (block->is_full()) { - g_malloc_stats.number_of_blocks_full++; - dbgln_if(MALLOC_DEBUG, "Block {:p} is now full in size class {}", block, good_size); - allocator->usable_blocks.remove(*block); - allocator->full_blocks.append(*block); - } - dbgln_if(MALLOC_DEBUG, "LibC: allocated {:p} (chunk in block {:p}, size {})", ptr, block, block->bytes_per_chunk()); - - if (s_scrub_malloc && caller_will_initialize_memory == CallerWillInitializeMemory::No) - memset(ptr, MALLOC_SCRUB_BYTE, block->m_size); - - ue_notify_malloc(ptr, size); - return ptr; -} - -static void free_impl(void* ptr) -{ -#ifndef NO_TLS - VERIFY(s_allocation_enabled); -#endif - - ScopedValueRollback rollback(errno); - - if (!ptr) - return; - - g_malloc_stats.number_of_free_calls++; - - void* block_base = (void*)((FlatPtr)ptr & ChunkedBlock::ChunkedBlock::block_mask); - size_t magic = *(size_t*)block_base; - - PthreadMutexLocker locker(s_malloc_mutex); - - if (magic == MAGIC_BIGALLOC_HEADER) { - auto* block = (BigAllocationBlock*)block_base; -#ifdef RECYCLE_BIG_ALLOCATIONS - if (auto* allocator = big_allocator_for_size(block->m_size)) { - if (allocator->blocks.size() < number_of_big_blocks_to_keep_around_per_size_class) { - g_malloc_stats.number_of_big_allocator_keeps++; - allocator->blocks.append(block); - size_t this_block_size = block->m_size; - if (mprotect(block, this_block_size, PROT_NONE) < 0) { - perror("mprotect"); - VERIFY_NOT_REACHED(); - } - if (madvise(block, this_block_size, MADV_SET_VOLATILE) != 0) { - perror("madvise"); - VERIFY_NOT_REACHED(); - } - return; - } - } -#endif - g_malloc_stats.number_of_big_allocator_frees++; - os_free(block, block->m_size); - return; - } - - VERIFY(magic == MAGIC_PAGE_HEADER); - auto* block = (ChunkedBlock*)block_base; - - dbgln_if(MALLOC_DEBUG, "LibC: freeing {:p} in allocator {:p} (size={}, used={})", ptr, block, block->bytes_per_chunk(), block->used_chunks()); - - if (s_scrub_free) - memset(ptr, FREE_SCRUB_BYTE, block->bytes_per_chunk()); - - auto* entry = (FreelistEntry*)ptr; - entry->next = block->m_freelist; - block->m_freelist = entry; - - if (block->is_full()) { - size_t good_size; - auto* allocator = allocator_for_size(block->m_size, good_size); - dbgln_if(MALLOC_DEBUG, "Block {:p} no longer full in size class {}", block, good_size); - g_malloc_stats.number_of_freed_full_blocks++; - allocator->full_blocks.remove(*block); - allocator->usable_blocks.prepend(*block); - } - - ++block->m_free_chunks; - - if (!block->used_chunks()) { - size_t good_size; - auto* allocator = allocator_for_size(block->m_size, good_size); - if (s_hot_empty_block_count < number_of_hot_chunked_blocks_to_keep_around) { - dbgln_if(MALLOC_DEBUG, "Keeping hot block {:p} around", block); - g_malloc_stats.number_of_hot_keeps++; - allocator->usable_blocks.remove(*block); - s_hot_empty_blocks[s_hot_empty_block_count++] = block; - return; - } - if (s_cold_empty_block_count < number_of_cold_chunked_blocks_to_keep_around) { - dbgln_if(MALLOC_DEBUG, "Keeping cold block {:p} around", block); - g_malloc_stats.number_of_cold_keeps++; - allocator->usable_blocks.remove(*block); - s_cold_empty_blocks[s_cold_empty_block_count++] = block; - mprotect(block, ChunkedBlock::block_size, PROT_NONE); - madvise(block, ChunkedBlock::block_size, MADV_SET_VOLATILE); - return; - } - dbgln_if(MALLOC_DEBUG, "Releasing block {:p} for size class {}", block, good_size); - g_malloc_stats.number_of_frees++; - allocator->usable_blocks.remove(*block); - --allocator->block_count; - os_free(block, ChunkedBlock::block_size); - } -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/malloc.html -void* malloc(size_t size) -{ - MemoryAuditingSuppressor suppressor; - auto ptr_or_error = malloc_impl(size, 16, CallerWillInitializeMemory::No); - - if (ptr_or_error.is_error()) { - errno = ptr_or_error.error().code(); - return nullptr; - } - - if (s_profiling) - perf_event(PERF_EVENT_MALLOC, size, reinterpret_cast(ptr_or_error.value())); - - return ptr_or_error.value(); -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/free.html -void free(void* ptr) -{ - MemoryAuditingSuppressor suppressor; - if (s_profiling) - perf_event(PERF_EVENT_FREE, reinterpret_cast(ptr), 0); - ue_notify_free(ptr); - free_impl(ptr); -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/calloc.html -void* calloc(size_t count, size_t size) -{ - MemoryAuditingSuppressor suppressor; - if (Checked::multiplication_would_overflow(count, size)) { - errno = ENOMEM; - return nullptr; - } - size_t new_size = count * size; - auto ptr_or_error = malloc_impl(new_size, 16, CallerWillInitializeMemory::Yes); - - if (ptr_or_error.is_error()) { - errno = ptr_or_error.error().code(); - return nullptr; - } - - memset(ptr_or_error.value(), 0, new_size); - return ptr_or_error.value(); -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/posix_memalign.html -int posix_memalign(void** memptr, size_t alignment, size_t size) -{ - MemoryAuditingSuppressor suppressor; - auto ptr_or_error = malloc_impl(size, alignment, CallerWillInitializeMemory::No); - - if (ptr_or_error.is_error()) - return ptr_or_error.error().code(); - - *memptr = ptr_or_error.value(); - return 0; -} - -void* aligned_alloc(size_t alignment, size_t size) -{ - MemoryAuditingSuppressor suppressor; - auto ptr_or_error = malloc_impl(size, alignment, CallerWillInitializeMemory::No); - - if (ptr_or_error.is_error()) { - errno = ptr_or_error.error().code(); - return nullptr; - } - - return ptr_or_error.value(); -} - -size_t malloc_size(void const* ptr) -{ - MemoryAuditingSuppressor suppressor; - if (!ptr) - return 0; - void* page_base = (void*)((FlatPtr)ptr & ChunkedBlock::block_mask); - auto* header = (CommonHeader const*)page_base; - auto size = header->m_size; - if (header->m_magic == MAGIC_BIGALLOC_HEADER) - size -= sizeof(BigAllocationBlock); - else - VERIFY(header->m_magic == MAGIC_PAGE_HEADER); - return size; -} - -size_t malloc_good_size(size_t size) -{ - size_t good_size; - allocator_for_size(size, good_size); - return good_size; -} - -void* realloc(void* ptr, size_t size) -{ - MemoryAuditingSuppressor suppressor; - if (!ptr) - return malloc(size); - if (!size) { - free(ptr); - return nullptr; - } - - auto existing_allocation_size = malloc_size(ptr); - - if (size <= existing_allocation_size) { - ue_notify_realloc(ptr, size); - return ptr; - } - auto* new_ptr = malloc(size); - if (new_ptr) { - memcpy(new_ptr, ptr, min(existing_allocation_size, size)); - free(ptr); - } - return new_ptr; -} - -void __malloc_init() -{ - s_in_userspace_emulator = (int)syscall(SC_emuctl, 0) != -ENOSYS; - if (s_in_userspace_emulator) { - // Don't bother scrubbing memory if we're running in UE since it - // keeps track of heap memory anyway. - s_scrub_malloc = false; - s_scrub_free = false; - } - - if (secure_getenv("LIBC_NOSCRUB_MALLOC")) - s_scrub_malloc = false; - if (secure_getenv("LIBC_NOSCRUB_FREE")) - s_scrub_free = false; - if (secure_getenv("LIBC_LOG_MALLOC")) - s_log_malloc = true; - if (secure_getenv("LIBC_PROFILE_MALLOC")) - s_profiling = true; - - for (size_t i = 0; i < num_size_classes; ++i) { - new (&allocators()[i]) Allocator(); - allocators()[i].size = size_classes[i]; - } - - new (&big_allocators()[0])(BigAllocator); -} - -void serenity_dump_malloc_stats() -{ - dbgln("# malloc() calls: {}", g_malloc_stats.number_of_malloc_calls); - dbgln(); - dbgln("big alloc hits: {}", g_malloc_stats.number_of_big_allocator_hits); - dbgln("big alloc hits that were purged: {}", g_malloc_stats.number_of_big_allocator_purge_hits); - dbgln("big allocs: {}", g_malloc_stats.number_of_big_allocs); - dbgln(); - dbgln("empty hot block hits: {}", g_malloc_stats.number_of_hot_empty_block_hits); - dbgln("empty cold block hits: {}", g_malloc_stats.number_of_cold_empty_block_hits); - dbgln("empty cold block hits that were purged: {}", g_malloc_stats.number_of_cold_empty_block_purge_hits); - dbgln("block allocs: {}", g_malloc_stats.number_of_block_allocs); - dbgln("filled blocks: {}", g_malloc_stats.number_of_blocks_full); - dbgln(); - dbgln("# free() calls: {}", g_malloc_stats.number_of_free_calls); - dbgln(); - dbgln("big alloc keeps: {}", g_malloc_stats.number_of_big_allocator_keeps); - dbgln("big alloc frees: {}", g_malloc_stats.number_of_big_allocator_frees); - dbgln(); - dbgln("full block frees: {}", g_malloc_stats.number_of_freed_full_blocks); - dbgln("number of hot keeps: {}", g_malloc_stats.number_of_hot_keeps); - dbgln("number of cold keeps: {}", g_malloc_stats.number_of_cold_keeps); - dbgln("number of frees: {}", g_malloc_stats.number_of_frees); -} -} diff --git a/Userland/Libraries/LibC/mallocdefs.h b/Userland/Libraries/LibC/mallocdefs.h deleted file mode 100644 index 63fd0071f35..00000000000 --- a/Userland/Libraries/LibC/mallocdefs.h +++ /dev/null @@ -1,85 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include - -#define MAGIC_PAGE_HEADER 0x42657274 // 'Bert' -#define MAGIC_BIGALLOC_HEADER 0x42697267 // 'Birg' -#define MALLOC_SCRUB_BYTE 0xdc -#define FREE_SCRUB_BYTE 0xed - -#define PAGE_ROUND_UP(x) ((((size_t)(x)) + PAGE_SIZE - 1) & (~(PAGE_SIZE - 1))) - -static constexpr unsigned short size_classes[] = { 16, 32, 64, 128, 256, 496, 1008, 2032, 4080, 8176, 16368, 32752, 0 }; -static constexpr size_t num_size_classes = (sizeof(size_classes) / sizeof(unsigned short)) - 1; - -#ifndef NO_TLS -extern "C" { -extern __thread bool s_allocation_enabled; -} -#endif - -consteval bool check_size_classes_alignment() -{ - for (size_t i = 0; i < num_size_classes; i++) { - if ((size_classes[i] % 16) != 0) - return false; - } - return true; -} -static_assert(check_size_classes_alignment()); - -struct CommonHeader { - size_t m_magic; - size_t m_size; -}; - -struct BigAllocationBlock : public CommonHeader { - BigAllocationBlock(size_t size) - { - m_magic = MAGIC_BIGALLOC_HEADER; - m_size = size; - } - alignas(16) unsigned char* m_slot[0]; -}; - -struct FreelistEntry { - FreelistEntry* next; -}; - -struct ChunkedBlock : public CommonHeader { - - static constexpr size_t block_size = 64 * KiB; - static constexpr size_t block_mask = ~(block_size - 1); - - ChunkedBlock(size_t bytes_per_chunk) - { - m_magic = MAGIC_PAGE_HEADER; - m_size = bytes_per_chunk; - m_free_chunks = chunk_capacity(); - } - - IntrusiveListNode m_list_node; - size_t m_next_lazy_freelist_index { 0 }; - FreelistEntry* m_freelist { nullptr }; - size_t m_free_chunks { 0 }; - alignas(16) unsigned char m_slot[0]; - - void* chunk(size_t index) - { - return &m_slot[index * m_size]; - } - bool is_full() const { return m_free_chunks == 0; } - size_t bytes_per_chunk() const { return m_size; } - size_t free_chunks() const { return m_free_chunks; } - size_t used_chunks() const { return chunk_capacity() - m_free_chunks; } - size_t chunk_capacity() const { return (block_size - sizeof(ChunkedBlock)) / m_size; } - - using List = IntrusiveList<&ChunkedBlock::m_list_node>; -}; diff --git a/Userland/Libraries/LibC/math.cpp b/Userland/Libraries/LibC/math.cpp deleted file mode 100644 index dbb76a33e09..00000000000 --- a/Userland/Libraries/LibC/math.cpp +++ /dev/null @@ -1,1254 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * Copyright (c) 2021, Mițca Dumitru - * Copyright (c) 2022, the SerenityOS developers. - * Copyright (c) 2022, Leon Albrecht - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#if ARCH(X86_64) -# include -#endif -#include -#include -#include -#include -#include -#include -#include - -#if defined(AK_COMPILER_CLANG) -# pragma clang diagnostic push -# pragma clang diagnostic ignored "-Wdouble-promotion" -#endif - -template -constexpr double e_to_power(); -template<> -constexpr double e_to_power<0>() { return 1; } -template -constexpr double e_to_power() { return M_E * e_to_power(); } - -template -constexpr size_t factorial(); -template<> -constexpr size_t factorial<0>() { return 1; } -template -constexpr size_t factorial() { return value * factorial(); } - -template -constexpr size_t product_even(); -template<> -constexpr size_t product_even<2>() { return 2; } -template -constexpr size_t product_even() { return value * product_even(); } - -template -constexpr size_t product_odd(); -template<> -constexpr size_t product_odd<1>() { return 1; } -template -constexpr size_t product_odd() { return value * product_odd(); } - -enum class RoundingMode { - ToZero = FE_TOWARDZERO, - Up = FE_UPWARD, - Down = FE_DOWNWARD, - ToEven = FE_TONEAREST, - // Round to nearest, ties away from zero. - ToMaxMagnitude = FE_TOMAXMAGNITUDE, -}; - -// This is much branchier than it really needs to be -template -static FloatType internal_to_integer(FloatType x, RoundingMode rounding_mode) -{ - if (!isfinite(x)) - return x; - - using Extractor = FloatExtractor; - // Most component types are larger than int. - constexpr auto zero = static_cast(0); - constexpr auto one = static_cast(1); - Extractor extractor; - extractor.d = x; - - auto unbiased_exponent = extractor.exponent - Extractor::exponent_bias; - - bool has_half_fraction = false; - bool has_nonhalf_fraction = false; - if (unbiased_exponent < 0) { - // it was easier to special case [0..1) as it saves us from - // handling subnormals, underflows, etc - if (unbiased_exponent == -1) { - has_half_fraction = true; - } - - has_nonhalf_fraction = unbiased_exponent < -1 || extractor.mantissa != 0; - extractor.mantissa = 0; - extractor.exponent = 0; - } else { - if (unbiased_exponent >= Extractor::mantissa_bits) - return x; - - auto dead_bitcount = Extractor::mantissa_bits - unbiased_exponent; - // Avoid shifting by the integer type's size since that's UB. - auto dead_mask = dead_bitcount == sizeof(typename Extractor::ComponentType) * 8 ? ~zero : (one << dead_bitcount) - 1; - auto dead_bits = extractor.mantissa & dead_mask; - extractor.mantissa &= ~dead_mask; -#ifdef AK_HAS_FLOAT_80 - if constexpr (IsSame) { - // x86 80-bit extended floating point requires the top mantissa bit to always be 1, or we get a special Intel NaN. - if (extractor.mantissa == 0) - extractor.mantissa = one << (Extractor::mantissa_bits - 1); - } -#endif - - auto nonhalf_fraction_mask = dead_mask >> 1; - has_nonhalf_fraction = (dead_bits & nonhalf_fraction_mask) != 0; - has_half_fraction = (dead_bits & ~nonhalf_fraction_mask) != 0; - } - - bool should_round = false; - switch (rounding_mode) { - case RoundingMode::ToEven: - should_round = has_half_fraction; - break; - case RoundingMode::Up: - if (!extractor.sign) - should_round = has_nonhalf_fraction || has_half_fraction; - break; - case RoundingMode::Down: - if (extractor.sign) - should_round = has_nonhalf_fraction || has_half_fraction; - break; - case RoundingMode::ToZero: - break; - case RoundingMode::ToMaxMagnitude: - should_round = true; - break; - } - - if (should_round) { - // We could do this ourselves, but this saves us from manually - // handling overflow. - if (extractor.sign) - extractor.d -= static_cast(1.0); - else - extractor.d += static_cast(1.0); - } - - return extractor.d; -} - -// This is much branchier than it really needs to be -template -static FloatType internal_nextafter(FloatType x, bool up) -{ - if (!isfinite(x)) - return x; - using Extractor = FloatExtractor; - Extractor extractor; - extractor.d = x; - if (x == 0) { - if (!extractor.sign) { - extractor.mantissa = 1; - extractor.sign = !up; - return extractor.d; - } - if (up) { - extractor.sign = false; - extractor.mantissa = 1; - return extractor.d; - } - extractor.mantissa = 1; - extractor.sign = up != extractor.sign; - return extractor.d; - } - if (up != extractor.sign) { - extractor.mantissa++; - if (!extractor.mantissa) { - // no need to normalize the mantissa as we just hit a power - // of two. - extractor.exponent++; - if (extractor.exponent == Extractor::exponent_max) { - extractor.exponent = Extractor::exponent_max - 1; - extractor.mantissa = Extractor::mantissa_max; - } - } - return extractor.d; - } - - if (!extractor.mantissa) { - if (extractor.exponent) { - extractor.exponent--; - extractor.mantissa = Extractor::mantissa_max; - } else { - extractor.d = 0; - } - return extractor.d; - } - - extractor.mantissa--; - if (extractor.mantissa != Extractor::mantissa_max) - return extractor.d; - if (extractor.exponent) { - extractor.exponent--; - // normalize - extractor.mantissa <<= 1; - } else { - if (extractor.sign) { - // Negative infinity - extractor.mantissa = 0; - extractor.exponent = Extractor::exponent_max; - } - } - return extractor.d; -} - -template -static int internal_ilogb(FloatT x) NOEXCEPT -{ - if (x == 0) - return FP_ILOGB0; - - if (isnan(x)) - return FP_ILOGNAN; - - if (!isfinite(x)) - return INT_MAX; - - using Extractor = FloatExtractor; - - Extractor extractor; - extractor.d = x; - - return (int)extractor.exponent - Extractor::exponent_bias; -} - -template -static FloatT internal_modf(FloatT x, FloatT* intpart) NOEXCEPT -{ - FloatT integer_part = internal_to_integer(x, RoundingMode::ToZero); - *intpart = integer_part; - auto fraction = x - integer_part; - if (signbit(fraction) != signbit(x)) - fraction = -fraction; - return fraction; -} - -template -static FloatT internal_scalbn(FloatT x, int exponent) NOEXCEPT -{ - if (x == 0 || !isfinite(x) || isnan(x) || exponent == 0) - return x; - - using Extractor = FloatExtractor; - Extractor extractor; - extractor.d = x; - - if (extractor.exponent != 0) { - extractor.exponent = clamp((int)extractor.exponent + exponent, 0, (int)Extractor::exponent_max); - return extractor.d; - } - - unsigned leading_mantissa_zeroes = extractor.mantissa == 0 ? 32 : count_leading_zeroes(extractor.mantissa); - int shift = min((int)leading_mantissa_zeroes, exponent); - exponent = max(exponent - shift, 0); - - extractor.exponent <<= shift; - extractor.exponent = exponent + 1; - - return extractor.d; -} - -template -static FloatT internal_copysign(FloatT x, FloatT y) NOEXCEPT -{ - using Extractor = FloatExtractor; - Extractor ex, ey; - ex.d = x; - ey.d = y; - ex.sign = ey.sign; - return ex.d; -} - -template -static FloatT internal_gamma(FloatT x) NOEXCEPT -{ - if (isnan(x)) - return (FloatT)NAN; - - if (x == (FloatT)0.0) - return signbit(x) ? (FloatT)-INFINITY : (FloatT)INFINITY; - - if (x < (FloatT)0 && (rintl(x) == x || isinf(x))) - return (FloatT)NAN; - - if (isinf(x)) - return (FloatT)INFINITY; - - using Extractor = FloatExtractor; - // These constants were obtained through use of WolframAlpha - constexpr long long max_integer_whose_factorial_fits = (Extractor::mantissa_bits == FloatExtractor::mantissa_bits ? 20 : (Extractor::mantissa_bits == FloatExtractor::mantissa_bits ? 18 : (Extractor::mantissa_bits == FloatExtractor::mantissa_bits ? 10 : 0))); - static_assert(max_integer_whose_factorial_fits != 0, "internal_gamma needs to be aware of the integer factorial that fits in this floating point type."); - if ((int)x == x && x <= max_integer_whose_factorial_fits + 1) { - long long result = 1; - for (long long cursor = 2; cursor < (long long)x; cursor++) - result *= cursor; - return (FloatT)result; - } - - // Stirling approximation - return sqrtl(2.0 * M_PIl / static_cast(x)) * powl(static_cast(x) / M_El, static_cast(x)); -} - -extern "C" { - -float nanf(char const* s) NOEXCEPT -{ - return __builtin_nanf(s); -} - -double nan(char const* s) NOEXCEPT -{ - return __builtin_nan(s); -} - -long double nanl(char const* s) NOEXCEPT -{ - return __builtin_nanl(s); -} - -#define MAKE_AK_BACKED1(name) \ - long double name##l(long double arg) NOEXCEPT \ - { \ - return AK::name(arg); \ - } \ - double name(double arg) NOEXCEPT \ - { \ - return AK::name(arg); \ - } \ - float name##f(float arg) NOEXCEPT \ - { \ - return AK::name(arg); \ - } -#define MAKE_AK_BACKED2(name) \ - long double name##l(long double arg1, long double arg2) NOEXCEPT \ - { \ - return AK::name(arg1, arg2); \ - } \ - double name(double arg1, double arg2) NOEXCEPT \ - { \ - return AK::name(arg1, arg2); \ - } \ - float name##f(float arg1, float arg2) NOEXCEPT \ - { \ - return AK::name(arg1, arg2); \ - } - -MAKE_AK_BACKED1(sin); -MAKE_AK_BACKED1(cos); -MAKE_AK_BACKED1(tan); -MAKE_AK_BACKED1(asin); -MAKE_AK_BACKED1(acos); -MAKE_AK_BACKED1(atan); -MAKE_AK_BACKED1(sinh); -MAKE_AK_BACKED1(cosh); -MAKE_AK_BACKED1(tanh); -MAKE_AK_BACKED1(asinh); -MAKE_AK_BACKED1(acosh); -MAKE_AK_BACKED1(atanh); -MAKE_AK_BACKED1(sqrt); -MAKE_AK_BACKED1(cbrt); -MAKE_AK_BACKED1(log); -MAKE_AK_BACKED1(log2); -MAKE_AK_BACKED1(log10); -MAKE_AK_BACKED1(exp); -MAKE_AK_BACKED1(exp2); -MAKE_AK_BACKED1(fabs); - -MAKE_AK_BACKED2(atan2); -MAKE_AK_BACKED2(hypot); -MAKE_AK_BACKED2(fmod); -MAKE_AK_BACKED2(pow); -MAKE_AK_BACKED2(remainder); - -long double truncl(long double x) NOEXCEPT -{ -#if ARCH(X86_64) - if (fabsl(x) < LONG_LONG_MAX) { - // This is 1.6 times faster than the implementation using the "internal_to_integer" - // helper (on x86_64) - // https://quick-bench.com/q/xBmxuY8am9qibSYVna90Y6PIvqA - u64 temp; - asm( - "fisttpq %[temp]\n" - "fildq %[temp]" - : "+t"(x) - : [temp] "m"(temp)); - return x; - } -#endif - - return internal_to_integer(x, RoundingMode::ToZero); -} - -double trunc(double x) NOEXCEPT -{ -#if ARCH(X86_64) - if (fabs(x) < LONG_LONG_MAX) { - u64 temp; - asm( - "fisttpq %[temp]\n" - "fildq %[temp]" - : "+t"(x) - : [temp] "m"(temp)); - return x; - } -#elif ARCH(RISCV64) - if (fabs(x) < LONG_LONG_MAX) { - i64 output; - asm("fcvt.l.d %0, %1, rtz" - : "=r"(output) - : "f"(x)); - return static_cast(output); - } -#endif - - return internal_to_integer(x, RoundingMode::ToZero); -} - -float truncf(float x) NOEXCEPT -{ -#if ARCH(X86_64) - if (fabsf(x) < LONG_LONG_MAX) { - u64 temp; - asm( - "fisttpq %[temp]\n" - "fildq %[temp]" - : "+t"(x) - : [temp] "m"(temp)); - return x; - } -#elif ARCH(RISCV64) - if (fabsf(x) < LONG_LONG_MAX) { - i64 output; - asm("fcvt.l.s %0, %1, rtz" - : "=r"(output) - : "f"(x)); - return static_cast(output); - } -#endif - - return internal_to_integer(x, RoundingMode::ToZero); -} - -long double rintl(long double value) -{ -#if ARCH(AARCH64) - (void)value; - TODO_AARCH64(); -#elif ARCH(RISCV64) - (void)value; - TODO_RISCV64(); -#elif ARCH(X86_64) - long double res; - asm( - "frndint\n" - : "=t"(res) - : "0"(value)); - return res; -#else -# error "Unknown architecture" -#endif -} -double rint(double value) -{ -#if ARCH(AARCH64) - (void)value; - TODO_AARCH64(); -#elif ARCH(RISCV64) - i64 output; - // FIXME: This saturates at 64-bit integer boundaries; see Table 11.4 (RISC-V Unprivileged ISA V20191213) - asm("fcvt.l.d %0, %1, dyn" - : "=r"(output) - : "f"(value)); - return static_cast(output); -#elif ARCH(X86_64) - double res; - asm( - "frndint\n" - : "=t"(res) - : "0"(value)); - return res; -#else -# error "Unknown architecture" -#endif -} -float rintf(float value) -{ -#if ARCH(AARCH64) - (void)value; - TODO_AARCH64(); -#elif ARCH(RISCV64) - i64 output; - // FIXME: This saturates at 64-bit integer boundaries; see Table 11.4 (RISC-V Unprivileged ISA V20191213) - asm("fcvt.l.s %0, %1, dyn" - : "=r"(output) - : "f"(value)); - return static_cast(output); -#elif ARCH(X86_64) - float res; - asm( - "frndint\n" - : "=t"(res) - : "0"(value)); - return res; -#else -# error "Unknown architecture" -#endif -} - -long lrintl(long double value) -{ -#if ARCH(AARCH64) - (void)value; - TODO_AARCH64(); -#elif ARCH(RISCV64) - (void)value; - TODO_RISCV64(); -#elif ARCH(X86_64) - long res; - asm( - "fistpl %0\n" - : "+m"(res) - : "t"(value) - : "st"); - return res; -#else -# error "Unknown architecture" -#endif -} -long lrint(double value) -{ -#if ARCH(AARCH64) - (void)value; - TODO_AARCH64(); -#elif ARCH(RISCV64) - i64 output; - // FIXME: This saturates at 64-bit integer boundaries; see Table 11.4 (RISC-V Unprivileged ISA V20191213) - asm("fcvt.l.d %0, %1, dyn" - : "=r"(output) - : "f"(value)); - return output; -#elif ARCH(X86_64) - long res; - asm( - "fistpl %0\n" - : "+m"(res) - : "t"(value) - : "st"); - return res; -#else -# error "Unknown architecture" -#endif -} -long lrintf(float value) -{ -#if ARCH(AARCH64) - (void)value; - TODO_AARCH64(); -#elif ARCH(RISCV64) - i64 output; - // FIXME: This saturates at 64-bit integer boundaries; see Table 11.4 (RISC-V Unprivileged ISA V20191213) - asm("fcvt.l.s %0, %1, dyn" - : "=r"(output) - : "f"(value)); - return output; -#elif ARCH(X86_64) - long res; - asm( - "fistpl %0\n" - : "+m"(res) - : "t"(value) - : "st"); - return res; -#else -# error "Unknown architecture" -#endif -} - -long long llrintl(long double value) -{ -#if ARCH(AARCH64) - (void)value; - TODO_AARCH64(); -#elif ARCH(RISCV64) - // NOTE: RISC-V LP64 specifies long long == long. - return static_cast(lrintl(value)); -#elif ARCH(X86_64) - long long res; - asm( - "fistpq %0\n" - : "+m"(res) - : "t"(value) - : "st"); - return res; -#else -# error "Unknown architecture" -#endif -} -long long llrint(double value) -{ -#if ARCH(AARCH64) - (void)value; - TODO_AARCH64(); -#elif ARCH(RISCV64) - // NOTE: RISC-V LP64 specifies long long == long. - return static_cast(lrint(value)); -#elif ARCH(X86_64) - long long res; - asm( - "fistpq %0\n" - : "+m"(res) - : "t"(value) - : "st"); - return res; -#else -# error "Unknown architecture" -#endif -} -long long llrintf(float value) -{ -#if ARCH(AARCH64) - (void)value; - TODO_AARCH64(); -#elif ARCH(RISCV64) - // NOTE: RISC-V LP64 specifies long long == long. - return static_cast(lrintf(value)); -#elif ARCH(X86_64) - long long res; - asm( - "fistpq %0\n" - : "+m"(res) - : "t"(value) - : "st"); - return res; -#else -# error "Unknown architecture" -#endif -} - -// On systems where FLT_RADIX == 2, ldexp is equivalent to scalbn -long double ldexpl(long double x, int exp) NOEXCEPT -{ - return internal_scalbn(x, exp); -} - -double ldexp(double x, int exp) NOEXCEPT -{ - return internal_scalbn(x, exp); -} - -float ldexpf(float x, int exp) NOEXCEPT -{ - return internal_scalbn(x, exp); -} - -[[maybe_unused]] static long double ampsin(long double angle) NOEXCEPT -{ - long double looped_angle = fmodl(M_PI + angle, M_PI * 2) - M_PI; - long double looped_angle_squared = looped_angle * looped_angle; - - long double quadratic_term; - if (looped_angle > 0) { - quadratic_term = -looped_angle_squared; - } else { - quadratic_term = looped_angle_squared; - } - - long double linear_term = M_PI * looped_angle; - - return quadratic_term + linear_term; -} - -int ilogbl(long double x) NOEXCEPT -{ - return internal_ilogb(x); -} - -int ilogb(double x) NOEXCEPT -{ - return internal_ilogb(x); -} - -int ilogbf(float x) NOEXCEPT -{ - return internal_ilogb(x); -} - -long double logbl(long double x) NOEXCEPT -{ - return ilogbl(x); -} - -double logb(double x) NOEXCEPT -{ - return ilogb(x); -} - -float logbf(float x) NOEXCEPT -{ - return ilogbf(x); -} - -double frexp(double x, int* exp) NOEXCEPT -{ - *exp = (x == 0) ? 0 : (1 + ilogb(x)); - return scalbn(x, -(*exp)); -} - -float frexpf(float x, int* exp) NOEXCEPT -{ - *exp = (x == 0) ? 0 : (1 + ilogbf(x)); - return scalbnf(x, -(*exp)); -} - -long double frexpl(long double x, int* exp) NOEXCEPT -{ - *exp = (x == 0) ? 0 : (1 + ilogbl(x)); - return scalbnl(x, -(*exp)); -} - -double round(double x) NOEXCEPT -{ -#if ARCH(RISCV64) - if (fabs(x) < LONG_LONG_MAX) { - i64 output; - asm("fcvt.l.d %0, %1, rmm" - : "=r"(output) - : "f"(x)); - return static_cast(output); - } -#endif - - return internal_to_integer(x, RoundingMode::ToEven); -} - -float roundf(float x) NOEXCEPT -{ -#if ARCH(RISCV64) - if (fabsf(x) < LONG_LONG_MAX) { - i64 output; - asm("fcvt.l.s %0, %1, rmm" - : "=r"(output) - : "f"(x)); - return static_cast(output); - } -#endif - - return internal_to_integer(x, RoundingMode::ToEven); -} - -long double roundl(long double value) NOEXCEPT -{ - return internal_to_integer(value, RoundingMode::ToEven); -} - -long lroundf(float value) NOEXCEPT -{ -#if ARCH(RISCV64) - i64 output; - asm("fcvt.l.s %0, %1, rmm" - : "=r"(output) - : "f"(value)); - return output; -#endif - - return internal_to_integer(value, RoundingMode::ToEven); -} - -long lround(double value) NOEXCEPT -{ -#if ARCH(RISCV64) - i64 output; - asm("fcvt.l.d %0, %1, rmm" - : "=r"(output) - : "f"(value)); - return output; -#endif - - return internal_to_integer(value, RoundingMode::ToEven); -} - -long lroundl(long double value) NOEXCEPT -{ - return internal_to_integer(value, RoundingMode::ToEven); -} - -long long llroundf(float value) NOEXCEPT -{ -#if ARCH(RISCV64) - i64 output; - asm("fcvt.l.s %0, %1, rmm" - : "=r"(output) - : "f"(value)); - return output; -#endif - - return internal_to_integer(value, RoundingMode::ToEven); -} - -long long llround(double value) NOEXCEPT -{ -#if ARCH(RISCV64) - i64 output; - asm("fcvt.l.d %0, %1, rmm" - : "=r"(output) - : "f"(value)); - return output; -#endif - - return internal_to_integer(value, RoundingMode::ToEven); -} - -long long llroundd(long double value) NOEXCEPT -{ - return internal_to_integer(value, RoundingMode::ToEven); -} - -float floorf(float value) NOEXCEPT -{ -#if ARCH(RISCV64) - if (fabsf(value) < LONG_LONG_MAX) { - i64 output; - asm("fcvt.l.s %0, %1, rdn" - : "=r"(output) - : "f"(value)); - return static_cast(output); - } -#elif ARCH(X86_64) - AK::X87RoundingModeScope scope { AK::RoundingMode::DOWN }; - asm("frndint" - : "+t"(value)); - return value; -#endif - return internal_to_integer(value, RoundingMode::Down); -} - -double floor(double value) NOEXCEPT -{ -#if ARCH(RISCV64) - if (fabs(value) < LONG_LONG_MAX) { - i64 output; - asm("fcvt.l.d %0, %1, rdn" - : "=r"(output) - : "f"(value)); - return static_cast(output); - } -#elif ARCH(X86_64) - AK::X87RoundingModeScope scope { AK::RoundingMode::DOWN }; - asm("frndint" - : "+t"(value)); - return value; -#endif - return internal_to_integer(value, RoundingMode::Down); -} - -long double floorl(long double value) NOEXCEPT -{ -#if ARCH(X86_64) - AK::X87RoundingModeScope scope { AK::RoundingMode::DOWN }; - asm("frndint" - : "+t"(value)); - return value; -#endif - return internal_to_integer(value, RoundingMode::Down); -} - -float ceilf(float value) NOEXCEPT -{ -#if ARCH(RISCV64) - if (fabsf(value) < LONG_LONG_MAX) { - i64 output; - asm("fcvt.l.s %0, %1, rup" - : "=r"(output) - : "f"(value)); - return static_cast(output); - } -#elif ARCH(X86_64) - AK::X87RoundingModeScope scope { AK::RoundingMode::UP }; - asm("frndint" - : "+t"(value)); - return value; -#endif - return internal_to_integer(value, RoundingMode::Up); -} - -double ceil(double value) NOEXCEPT -{ -#if ARCH(RISCV64) - if (fabs(value) < LONG_LONG_MAX) { - i64 output; - asm("fcvt.l.d %0, %1, rup" - : "=r"(output) - : "f"(value)); - return static_cast(output); - } -#elif ARCH(X86_64) - AK::X87RoundingModeScope scope { AK::RoundingMode::UP }; - asm("frndint" - : "+t"(value)); - return value; -#endif - return internal_to_integer(value, RoundingMode::Up); -} - -long double ceill(long double value) NOEXCEPT -{ -#if ARCH(X86_64) - AK::X87RoundingModeScope scope { AK::RoundingMode::UP }; - asm("frndint" - : "+t"(value)); - return value; -#endif - return internal_to_integer(value, RoundingMode::Up); -} - -long double modfl(long double x, long double* intpart) NOEXCEPT -{ - return internal_modf(x, intpart); -} - -double modf(double x, double* intpart) NOEXCEPT -{ - return internal_modf(x, intpart); -} - -float modff(float x, float* intpart) NOEXCEPT -{ - return internal_modf(x, intpart); -} - -double gamma(double x) NOEXCEPT -{ - // Stirling approximation - return sqrt(2.0 * M_PI / x) * pow(x / M_E, x); -} - -long double tgammal(long double value) NOEXCEPT -{ - return internal_gamma(value); -} - -double tgamma(double value) NOEXCEPT -{ - return internal_gamma(value); -} - -float tgammaf(float value) NOEXCEPT -{ - return internal_gamma(value); -} - -int signgam = 0; - -long double lgammal(long double value) NOEXCEPT -{ - return lgammal_r(value, &signgam); -} - -double lgamma(double value) NOEXCEPT -{ - return lgamma_r(value, &signgam); -} - -float lgammaf(float value) NOEXCEPT -{ - return lgammaf_r(value, &signgam); -} - -long double lgammal_r(long double value, int* sign) NOEXCEPT -{ - if (value == 1.0 || value == 2.0) - return 0.0; - if (isinf(value) || value == 0.0) - return INFINITY; - long double result = logl(internal_gamma(value)); - *sign = signbit(result) ? -1 : 1; - return result; -} - -double lgamma_r(double value, int* sign) NOEXCEPT -{ - if (value == 1.0 || value == 2.0) - return 0.0; - if (isinf(value) || value == 0.0) - return INFINITY; - double result = log(internal_gamma(value)); - *sign = signbit(result) ? -1 : 1; - return result; -} - -float lgammaf_r(float value, int* sign) NOEXCEPT -{ - if (value == 1.0f || value == 2.0f) - return 0.0; - if (isinf(value) || value == 0.0f) - return INFINITY; - float result = logf(internal_gamma(value)); - *sign = signbit(result) ? -1 : 1; - return result; -} - -long double expm1l(long double x) NOEXCEPT -{ - return expl(x) - 1; -} - -double expm1(double x) NOEXCEPT -{ - return exp(x) - 1; -} - -float expm1f(float x) NOEXCEPT -{ - return expf(x) - 1; -} - -long double log1pl(long double x) NOEXCEPT -{ - return logl(1 + x); -} - -double log1p(double x) NOEXCEPT -{ - return log(1 + x); -} - -float log1pf(float x) NOEXCEPT -{ - return logf(1 + x); -} - -long double erfl(long double x) NOEXCEPT -{ - // algorithm taken from Abramowitz and Stegun (no. 26.2.17) - long double t = 1 / (1 + 0.47047l * fabsl(x)); - long double poly = t * (0.3480242l + t * (-0.958798l + t * 0.7478556l)); - long double answer = 1 - poly * expl(-x * x); - if (x < 0) - return -answer; - - return answer; -} - -double erf(double x) NOEXCEPT -{ - return (double)erfl(x); -} - -float erff(float x) NOEXCEPT -{ - return (float)erf(x); -} - -long double erfcl(long double x) NOEXCEPT -{ - return 1 - erfl(x); -} - -double erfc(double x) NOEXCEPT -{ - return 1 - erf(x); -} - -float erfcf(float x) NOEXCEPT -{ - return 1 - erff(x); -} - -double nextafter(double x, double target) NOEXCEPT -{ - if (x == target) - return target; - return internal_nextafter(x, target >= x); -} - -float nextafterf(float x, float target) NOEXCEPT -{ - if (x == target) - return target; - return internal_nextafter(x, target >= x); -} - -long double nextafterl(long double x, long double target) NOEXCEPT -{ - return internal_nextafter(x, target >= x); -} - -double nexttoward(double x, long double target) NOEXCEPT -{ - if (x == target) - return target; - return internal_nextafter(x, target >= x); -} - -float nexttowardf(float x, long double target) NOEXCEPT -{ - if (x == target) - return target; - return internal_nextafter(x, target >= x); -} - -long double nexttowardl(long double x, long double target) NOEXCEPT -{ - if (x == target) - return target; - return internal_nextafter(x, target >= x); -} - -float copysignf(float x, float y) NOEXCEPT -{ - return internal_copysign(x, y); -} - -double copysign(double x, double y) NOEXCEPT -{ - return internal_copysign(x, y); -} - -long double copysignl(long double x, long double y) NOEXCEPT -{ - return internal_copysign(x, y); -} - -float scalbnf(float x, int exponent) NOEXCEPT -{ - return internal_scalbn(x, exponent); -} - -double scalbn(double x, int exponent) NOEXCEPT -{ - return internal_scalbn(x, exponent); -} - -long double scalbnl(long double x, int exponent) NOEXCEPT -{ - return internal_scalbn(x, exponent); -} - -float scalbnlf(float x, long exponent) NOEXCEPT -{ - return internal_scalbn(x, exponent); -} - -double scalbln(double x, long exponent) NOEXCEPT -{ - return internal_scalbn(x, exponent); -} - -long double scalblnl(long double x, long exponent) NOEXCEPT -{ - return internal_scalbn(x, exponent); -} - -long double fmaxl(long double x, long double y) NOEXCEPT -{ - if (isnan(x)) - return y; - if (isnan(y)) - return x; - - return x > y ? x : y; -} - -double fmax(double x, double y) NOEXCEPT -{ - if (isnan(x)) - return y; - if (isnan(y)) - return x; - - return x > y ? x : y; -} - -float fmaxf(float x, float y) NOEXCEPT -{ - if (isnan(x)) - return y; - if (isnan(y)) - return x; - - return x > y ? x : y; -} - -long double fminl(long double x, long double y) NOEXCEPT -{ - if (isnan(x)) - return y; - if (isnan(y)) - return x; - - return x < y ? x : y; -} - -double fmin(double x, double y) NOEXCEPT -{ - if (isnan(x)) - return y; - if (isnan(y)) - return x; - - return x < y ? x : y; -} - -float fminf(float x, float y) NOEXCEPT -{ - if (isnan(x)) - return y; - if (isnan(y)) - return x; - - return x < y ? x : y; -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/fma.html -long double fmal(long double x, long double y, long double z) NOEXCEPT -{ - return (x * y) + z; -} - -double fma(double x, double y, double z) NOEXCEPT -{ - return (x * y) + z; -} - -float fmaf(float x, float y, float z) NOEXCEPT -{ - return (x * y) + z; -} - -long double nearbyintl(long double value) NOEXCEPT -{ - return internal_to_integer(value, RoundingMode { fegetround() }); -} - -double nearbyint(double value) NOEXCEPT -{ - return internal_to_integer(value, RoundingMode { fegetround() }); -} - -float nearbyintf(float value) NOEXCEPT -{ - return internal_to_integer(value, RoundingMode { fegetround() }); -} -} - -#if defined(AK_COMPILER_CLANG) -# pragma clang diagnostic pop -#endif diff --git a/Userland/Libraries/LibC/math.h b/Userland/Libraries/LibC/math.h deleted file mode 100644 index b5269044b02..00000000000 --- a/Userland/Libraries/LibC/math.h +++ /dev/null @@ -1,316 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include - -#if __cplusplus >= 201103L -# define NOEXCEPT noexcept -#else -# define NOEXCEPT -#endif - -__BEGIN_DECLS - -#define MATH_ERRNO 1 -#define MATH_ERREXCEPT 2 -#define math_errhandling MATH_ERREXCEPT - -#define HUGE_VALF __builtin_huge_valf() -#define HUGE_VAL __builtin_huge_val() -#define HUGE_VALL __builtin_huge_vall() -#define INFINITY __builtin_huge_valf() -#define NAN __builtin_nan("") -#define MAXFLOAT FLT_MAX - -#define M_El 2.718281828459045235360287471352662498L -#define M_LOG2El 1.442695040888963407359924681001892137L -#define M_LOG10El 0.434294481903251827651128918916605082L -#define M_LN2l 0.693147180559945309417232121458176568L -#define M_LN10l 2.302585092994045684017991454684364208L -#define M_PIl 3.141592653589793238462643383279502884L -#define M_PI_2l 1.570796326794896619231321691639751442L -#define M_PI_4l 0.785398163397448309615660845819875721L -#define M_1_PIl 0.318309886183790671537767526745028724L -#define M_2_PIl 0.636619772367581343075535053490057448L -#define M_2_SQRTPIl 1.128379167095512573896158903121545172L -#define M_SQRT2l 1.414213562373095048801688724209698079L -#define M_SQRT1_2l 0.707106781186547524400844362104849039L - -#define M_E 2.7182818284590452354 -#define M_LOG2E 1.4426950408889634074 -#define M_LOG10E 0.43429448190325182765 -#define M_LN2 0.69314718055994530942 -#define M_LN10 2.30258509299404568402 -#define M_PI 3.14159265358979323846 -#define M_PI_2 1.57079632679489661923 -#define M_PI_4 0.78539816339744830962 -#define M_1_PI 0.31830988618379067154 -#define M_2_PI 0.63661977236758134308 -#define M_2_SQRTPI 1.12837916709551257390 -#define M_SQRT2 1.41421356237309504880 -#define M_SQRT1_2 0.70710678118654752440 - -#define M_Ef32 2.7182818284590452354f -#define M_LOG2Ef32 1.4426950408889634074f -#define M_LOG10Ef32 0.43429448190325182765f -#define M_LN2f32 0.69314718055994530942f -#define M_LN10f32 2.30258509299404568402f -#define M_PIf32 3.14159265358979323846f -#define M_PI_2f32 1.57079632679489661923f -#define M_PI_4f32 0.78539816339744830962f -#define M_1_PIf32 0.31830988618379067154f -#define M_2_PIf32 0.63661977236758134308f -#define M_2_SQRTPIf32 1.12837916709551257390f -#define M_SQRT2f32 1.41421356237309504880f -#define M_SQRT1_2f32 0.70710678118654752440f - -#define FP_NAN 0 -#define FP_INFINITE 1 -#define FP_ZERO 2 -#define FP_SUBNORMAL 3 -#define FP_NORMAL 4 -#define fpclassify(x) __builtin_fpclassify(FP_NAN, FP_INFINITE, FP_ZERO, FP_SUBNORMAL, FP_ZERO, x) - -#define signbit(x) __builtin_signbit(x) -#define isnan(x) __builtin_isnan(x) -#define isinf(x) __builtin_isinf_sign(x) -#define isfinite(x) __builtin_isfinite(x) -#define isnormal(x) __builtin_isnormal(x) -#define isgreater(x, y) __builtin_isgreater((x), (y)) -#define isgreaterequal(x, y) __builtin_isgreaterequal((x), (y)) -#define isless(x, y) __builtin_isless((x), (y)) -#define islessequal(x, y) __builtin_islessequal((x), (y)) -#define islessgreater(x, y) __builtin_islessgreater((x), (y)) -#define isunordered(x, y) __builtin_isunordered((x), (y)) - -#define DOUBLE_MAX ((double)0b0111111111101111111111111111111111111111111111111111111111111111) -#define DOUBLE_MIN ((double)0b0000000000010000000000000000000000000000000000000000000000000000) - -#define FP_ILOGB0 INT_MIN -#define FP_ILOGNAN INT_MAX - -#define FLT_EVAL_METHOD __FLT_EVAL_METHOD__ - -#if FLT_EVAL_METHOD == 0 -typedef float float_t; -typedef double double_t; -#elif FLT_EVAL_METHOD == 1 -typedef double float_t; -typedef double double_t; -#elif FLT_EVAL_METHOD == 2 -typedef long double float_t; -typedef long double double_t; -#else -typedef float float_t; -typedef double double_t; -#endif - -/* Basic floating point operations */ -long double fabsl(long double) NOEXCEPT; -double fabs(double) NOEXCEPT; -float fabsf(float) NOEXCEPT; -long double fmodl(long double, long double) NOEXCEPT; -double fmod(double, double) NOEXCEPT; -float fmodf(float, float) NOEXCEPT; -long double fmaxl(long double, long double) NOEXCEPT; -double fmax(double, double) NOEXCEPT; -float fmaxf(float, float) NOEXCEPT; -long double fminl(long double, long double) NOEXCEPT; -double fmin(double, double) NOEXCEPT; -float fminf(float, float) NOEXCEPT; -long double remainderl(long double, long double) NOEXCEPT; -double remainder(double, double) NOEXCEPT; -float remainderf(float, float) NOEXCEPT; -long double nanl(char const*) NOEXCEPT; -double nan(char const*) NOEXCEPT; -float nanf(char const*) NOEXCEPT; - -/* Exponential functions */ -long double expl(long double) NOEXCEPT; -double exp(double) NOEXCEPT; -float expf(float) NOEXCEPT; -long double exp2l(long double) NOEXCEPT; -double exp2(double) NOEXCEPT; -float exp2f(float) NOEXCEPT; -long double expm1l(long double) NOEXCEPT; -double expm1(double) NOEXCEPT; -float expm1f(float) NOEXCEPT; -long double logl(long double) NOEXCEPT; -double log(double) NOEXCEPT; -float logf(float) NOEXCEPT; -double log2(double) NOEXCEPT; -float log2f(float) NOEXCEPT; -long double log2l(long double) NOEXCEPT; -long double log10l(long double) NOEXCEPT; -double log10(double) NOEXCEPT; -float log10f(float) NOEXCEPT; -long double log1pl(long double) NOEXCEPT; -double log1p(double) NOEXCEPT; -float log1pf(float) NOEXCEPT; - -/* Power functions */ -long double powl(long double x, long double y) NOEXCEPT; -double pow(double x, double y) NOEXCEPT; -float powf(float x, float y) NOEXCEPT; -long double sqrtl(long double) NOEXCEPT; -double sqrt(double) NOEXCEPT; -float sqrtf(float) NOEXCEPT; -long double cbrtl(long double) NOEXCEPT; -double cbrt(double) NOEXCEPT; -float cbrtf(float) NOEXCEPT; -long double hypotl(long double, long double) NOEXCEPT; -double hypot(double, double) NOEXCEPT; -float hypotf(float, float) NOEXCEPT; - -/* Trigonometric functions */ -long double sinl(long double) NOEXCEPT; -double sin(double) NOEXCEPT; -float sinf(float) NOEXCEPT; -long double cosl(long double) NOEXCEPT; -double cos(double) NOEXCEPT; -float cosf(float) NOEXCEPT; -long double tanl(long double) NOEXCEPT; -double tan(double) NOEXCEPT; -float tanf(float) NOEXCEPT; -long double asinl(long double) NOEXCEPT; -double asin(double) NOEXCEPT; -float asinf(float) NOEXCEPT; -long double acosl(long double) NOEXCEPT; -double acos(double) NOEXCEPT; -float acosf(float) NOEXCEPT; -long double atanl(long double) NOEXCEPT; -double atan(double) NOEXCEPT; -float atanf(float) NOEXCEPT; -long double atan2l(long double, long double) NOEXCEPT; -double atan2(double, double) NOEXCEPT; -float atan2f(float, float) NOEXCEPT; - -/* Hyperbolic functions*/ -long double sinhl(long double) NOEXCEPT; -double sinh(double) NOEXCEPT; -float sinhf(float) NOEXCEPT; -long double coshl(long double) NOEXCEPT; -double cosh(double) NOEXCEPT; -float coshf(float) NOEXCEPT; -long double tanhl(long double) NOEXCEPT; -double tanh(double) NOEXCEPT; -float tanhf(float) NOEXCEPT; -long double asinhl(long double) NOEXCEPT; -double asinh(double) NOEXCEPT; -float asinhf(float) NOEXCEPT; -long double acoshl(long double) NOEXCEPT; -double acosh(double) NOEXCEPT; -float acoshf(float) NOEXCEPT; -long double atanhl(long double) NOEXCEPT; -double atanh(double) NOEXCEPT; -float atanhf(float) NOEXCEPT; - -/* Error and gamma functions */ -long double erfl(long double) NOEXCEPT; -double erf(double) NOEXCEPT; -float erff(float) NOEXCEPT; -long double erfcl(long double) NOEXCEPT; -double erfc(double) NOEXCEPT; -float erfcf(float) NOEXCEPT; -double gamma(double) NOEXCEPT; -long double tgammal(long double) NOEXCEPT; -double tgamma(double) NOEXCEPT; -float tgammaf(float) NOEXCEPT; -long double lgammal(long double) NOEXCEPT; -double lgamma(double) NOEXCEPT; -float lgammaf(float) NOEXCEPT; -long double lgammal_r(long double, int*) NOEXCEPT; -double lgamma_r(double, int*) NOEXCEPT; -float lgammaf_r(float, int*) NOEXCEPT; -extern int signgam; - -/* Nearest integer floating point operations */ -long double ceill(long double) NOEXCEPT; -double ceil(double) NOEXCEPT; -float ceilf(float) NOEXCEPT; -long double floorl(long double) NOEXCEPT; -double floor(double) NOEXCEPT; -float floorf(float) NOEXCEPT; -long double truncl(long double) NOEXCEPT; -double trunc(double) NOEXCEPT; -float truncf(float) NOEXCEPT; -float roundf(float) NOEXCEPT; -double round(double) NOEXCEPT; -long double roundl(long double) NOEXCEPT; -long lroundf(float) NOEXCEPT; -long lround(double) NOEXCEPT; -long lroundl(long double) NOEXCEPT; -long long llroundf(float) NOEXCEPT; -long long llround(double) NOEXCEPT; -long long llroundd(long double) NOEXCEPT; -long long llroundl(long double x) NOEXCEPT; -double nearbyint(double x) NOEXCEPT; -float nearbyintf(float x) NOEXCEPT; -long double nearbyintl(long double x) NOEXCEPT; - -float rintf(float) NOEXCEPT; -double rint(double) NOEXCEPT; -long double rintl(long double) NOEXCEPT; -long lrintl(long double) NOEXCEPT; -long lrint(double) NOEXCEPT; -long lrintf(float) NOEXCEPT; -long long llrintl(long double) NOEXCEPT; -long long llrint(double) NOEXCEPT; -long long llrintf(float) NOEXCEPT; - -/* Floating point manipulation functions */ -long double frexpl(long double, int* exp) NOEXCEPT; -double frexp(double, int* exp) NOEXCEPT; -float frexpf(float, int* exp) NOEXCEPT; -long double ldexpl(long double, int exp) NOEXCEPT; -double ldexp(double, int exp) NOEXCEPT; -float ldexpf(float, int exp) NOEXCEPT; -long double modfl(long double, long double*) NOEXCEPT; -double modf(double, double*) NOEXCEPT; -float modff(float, float*) NOEXCEPT; -float scalbnf(float, int) NOEXCEPT; -double scalbn(double, int) NOEXCEPT; -long double scalbnl(long double, int) NOEXCEPT; -float scalbnlf(float, long) NOEXCEPT; -double scalbln(double, long) NOEXCEPT; -float scalblnf(float, long) NOEXCEPT; -long double scalblnl(long double, long) NOEXCEPT; -int ilogbl(long double) NOEXCEPT; -int ilogb(double) NOEXCEPT; -int ilogbf(float) NOEXCEPT; -long double logbl(long double) NOEXCEPT; -double logb(double) NOEXCEPT; -float logbf(float) NOEXCEPT; -double nextafter(double, double) NOEXCEPT; -float nextafterf(float, float) NOEXCEPT; -long double nextafterl(long double, long double) NOEXCEPT; -double nexttoward(double, long double) NOEXCEPT; -float nexttowardf(float, long double) NOEXCEPT; -long double nexttowardl(long double, long double) NOEXCEPT; -float copysignf(float x, float y) NOEXCEPT; -double copysign(double x, double y) NOEXCEPT; -long double copysignl(long double x, long double y) NOEXCEPT; - -/* positive difference */ -double fdim(double x, double y) NOEXCEPT; -float fdimf(float x, float y) NOEXCEPT; -long double fdiml(long double x, long double y) NOEXCEPT; - -/* floating-point multiply and add */ -double fma(double x, double y, double z) NOEXCEPT; -float fmaf(float x, float y, float z) NOEXCEPT; -long double fmal(long double x, long double y, long double z) NOEXCEPT; - -/* remainder and part of quotient */ -double remquo(double x, double y, int* quo) NOEXCEPT; -float remquof(float x, float y, int* quo) NOEXCEPT; -long double remquol(long double x, long double y, int* quo) NOEXCEPT; - -__END_DECLS diff --git a/Userland/Libraries/LibC/memory.h b/Userland/Libraries/LibC/memory.h deleted file mode 100644 index ae248d22884..00000000000 --- a/Userland/Libraries/LibC/memory.h +++ /dev/null @@ -1,9 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include diff --git a/Userland/Libraries/LibC/mntent.cpp b/Userland/Libraries/LibC/mntent.cpp deleted file mode 100644 index d2746599ca3..00000000000 --- a/Userland/Libraries/LibC/mntent.cpp +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include - -extern "C" { - -struct mntent* getmntent(FILE*) -{ - dbgln("FIXME: Implement getmntent()"); - return nullptr; -} - -FILE* setmntent(char const*, char const*) -{ - dbgln("FIXME: Implement setmntent()"); - return nullptr; -} - -int endmntent(FILE*) -{ - dbgln("FIXME: Implement endmntent()"); - return 0; -} - -struct mntent* getmntent_r(FILE*, struct mntent*, char*, int) -{ - dbgln("FIXME: Implement getmntent_r()"); - return 0; -} -} diff --git a/Userland/Libraries/LibC/mntent.h b/Userland/Libraries/LibC/mntent.h deleted file mode 100644 index 8d51ceab147..00000000000 --- a/Userland/Libraries/LibC/mntent.h +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include - -__BEGIN_DECLS - -#define MOUNTED "/etc/mtab" -#define MNTTAB "/etc/fstab" - -struct mntent { - char* mnt_fsname; - char* mnt_dir; - char* mnt_type; - char* mnt_opts; - int mnt_freq; - int mnt_passno; -}; - -struct mntent* getmntent(FILE* stream); -FILE* setmntent(char const* filename, char const* type); -int endmntent(FILE* streamp); -struct mntent* getmntent_r(FILE* streamp, struct mntent* mntbuf, char* buf, int buflen); - -__END_DECLS diff --git a/Userland/Libraries/LibC/net.cpp b/Userland/Libraries/LibC/net.cpp deleted file mode 100644 index 88ab6fb3db0..00000000000 --- a/Userland/Libraries/LibC/net.cpp +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright (c) 2021, Gunnar Beutner - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include -#include - -in6_addr const in6addr_any = IN6ADDR_ANY_INIT; -in6_addr const in6addr_loopback = IN6ADDR_LOOPBACK_INIT; - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/if_nametoindex.html -unsigned int if_nametoindex([[maybe_unused]] char const* ifname) -{ - int dummy_socket = socket(AF_INET, SOCK_DGRAM, 0); - if (dummy_socket < 0) { - errno = -dummy_socket; - return 0; - } - - struct ifreq ifr; - memcpy(ifr.ifr_name, ifname, IF_NAMESIZE); - - int rc = ioctl(dummy_socket, SIOCGIFINDEX, &ifr); - if (rc) { - errno = -rc; - return 0; - } - - return ifr.ifr_index; -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/if_indextoname.html#tag_16_236 -char* if_indextoname([[maybe_unused]] unsigned int ifindex, [[maybe_unused]] char* ifname) -{ - int dummy_socket = socket(AF_INET, SOCK_DGRAM, 0); - if (dummy_socket < 0) { - errno = -dummy_socket; - return 0; - } - - struct ifreq ifr; - ifr.ifr_index = ifindex; - - int rc = ioctl(dummy_socket, SIOCGIFNAME, &ifr); - if (rc) { - errno = -rc; - return nullptr; - } - - memcpy(ifname, ifr.ifr_name, IF_NAMESIZE); - return ifname; -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/if_nameindex.html -struct if_nameindex* if_nameindex() -{ - errno = ENOSYS; - return nullptr; -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/if_freenameindex.html -void if_freenameindex(struct if_nameindex*) -{ -} diff --git a/Userland/Libraries/LibC/net/if.h b/Userland/Libraries/LibC/net/if.h deleted file mode 100644 index 560d5e3bd44..00000000000 --- a/Userland/Libraries/LibC/net/if.h +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Copyright (c) 2018-2021, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include - -__BEGIN_DECLS - -unsigned int if_nametoindex(char const* ifname); -char* if_indextoname(unsigned int ifindex, char* ifname); -struct if_nameindex* if_nameindex(void); -void if_freenameindex(struct if_nameindex* ptr); - -__END_DECLS diff --git a/Userland/Libraries/LibC/net/if_arp.h b/Userland/Libraries/LibC/net/if_arp.h deleted file mode 100644 index 8cfd5037f0c..00000000000 --- a/Userland/Libraries/LibC/net/if_arp.h +++ /dev/null @@ -1,9 +0,0 @@ -/* - * Copyright (c) 2018-2021, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include diff --git a/Userland/Libraries/LibC/net/route.h b/Userland/Libraries/LibC/net/route.h deleted file mode 100644 index cc6c0faa3bd..00000000000 --- a/Userland/Libraries/LibC/net/route.h +++ /dev/null @@ -1,9 +0,0 @@ -/* - * Copyright (c) 2020, Marios Prokopakis - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include diff --git a/Userland/Libraries/LibC/netdb.cpp b/Userland/Libraries/LibC/netdb.cpp deleted file mode 100644 index b3b44130e36..00000000000 --- a/Userland/Libraries/LibC/netdb.cpp +++ /dev/null @@ -1,1038 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -extern "C" { - -__thread int h_errno; - -static hostent __gethostbyname_buffer; -static in_addr_t __gethostbyname_address; -static in_addr_t* __gethostbyname_address_list_buffer[2]; -static char* __gethostbyname_alias_list_buffer[1]; - -static hostent __gethostbyaddr_buffer; -static in_addr_t* __gethostbyaddr_address_list_buffer[2]; -static char* __gethostbyaddr_alias_list_buffer[1]; -// IPCCompiler depends on LibC. Because of this, it cannot be compiled -// before LibC is. However, the lookup magic can only be obtained from the -// endpoint itself if IPCCompiler has compiled the IPC file, so this creates -// a chicken-and-egg situation. Because of this, the LookupServer endpoint magic -// is hardcoded here. -// Keep the name synchronized with LookupServer/LookupServer.ipc. -static constexpr u32 lookup_server_endpoint_magic = "LookupServer"sv.hash(); - -// Get service entry buffers and file information for the getservent() family of functions. -static FILE* services_file = nullptr; -static char const* services_path = "/etc/services"; - -struct ServiceFileLine { - String name; - String protocol; - int port; - Vector aliases; -}; - -static ErrorOr> parse_service_file_line(char const* line, ssize_t read); -static servent __getserv_buffer; -static ByteString __getserv_name_buffer; -static ByteString __getserv_protocol_buffer; -static int __getserv_port_buffer; -static Vector __getserv_alias_list_buffer; -static Vector __getserv_alias_list; -static bool keep_service_file_open = false; -static ssize_t service_file_offset = 0; - -// Get protocol entry buffers and file information for the getprotent() family of functions. -static FILE* protocols_file = nullptr; -static char const* protocols_path = "/etc/protocols"; - -static bool fill_getproto_buffers(char const* line, ssize_t read); -static protoent __getproto_buffer; -static ByteString __getproto_name_buffer; -static Vector __getproto_alias_list_buffer; -static Vector __getproto_alias_list; -static int __getproto_protocol_buffer; -static bool keep_protocols_file_open = false; -static ssize_t protocol_file_offset = 0; - -static int connect_to_lookup_server() -{ - int fd = socket(AF_LOCAL, SOCK_STREAM | SOCK_CLOEXEC, 0); - if (fd < 0) { - perror("socket"); - return -1; - } - - sockaddr_un address { - AF_LOCAL, - "/tmp/portal/lookup" - }; - - if (connect(fd, (sockaddr const*)&address, sizeof(address)) < 0) { - perror("connect_to_lookup_server"); - close(fd); - return -1; - } - return fd; -} - -static ByteString gethostbyname_name_buffer; - -hostent* gethostbyname(char const* name) -{ - struct hostent ret = {}; - struct hostent* result = nullptr; - size_t buffer_size = 1024; - char* buffer = nullptr; - - auto free_buffer_on_exit = ScopeGuard([buffer] { - if (buffer != nullptr) - free(buffer); - }); - - while (true) { - buffer = (char*)realloc(buffer, buffer_size); - if (buffer == nullptr) { - // NOTE: Since gethostbyname usually can't fail because of memory, - // it has no way of representing OOM or allocation failure. - // NO_RECOVERY is the next best thing. - h_errno = NO_RECOVERY; - return NULL; - } - - int rc = gethostbyname_r(name, &ret, buffer, buffer_size, &result, &h_errno); - if (rc == ERANGE) { - buffer_size *= 2; - continue; - } - - if (rc < 0) - return nullptr; - - break; - } - - gethostbyname_name_buffer = name; - __gethostbyname_buffer.h_name = const_cast(gethostbyname_name_buffer.characters()); - __gethostbyname_alias_list_buffer[0] = nullptr; - __gethostbyname_buffer.h_aliases = __gethostbyname_alias_list_buffer; - __gethostbyname_buffer.h_addrtype = AF_INET; - memcpy(&__gethostbyname_address, result->h_addr_list[0], sizeof(in_addr_t)); - __gethostbyname_address_list_buffer[0] = &__gethostbyname_address; - __gethostbyname_address_list_buffer[1] = nullptr; - __gethostbyname_buffer.h_addr_list = (char**)__gethostbyname_address_list_buffer; - __gethostbyname_buffer.h_length = result->h_length; - - return &__gethostbyname_buffer; -} - -int gethostbyname_r(char const* __restrict name, struct hostent* __restrict ret, char* buffer, size_t buffer_size, struct hostent** __restrict result, int* __restrict h_errnop) -{ - *h_errnop = 0; - *result = nullptr; - size_t buffer_offset = 0; - memset(buffer, 0, buffer_size); - - auto add_string_to_buffer = [&](char const* data) -> Optional { - size_t data_lenth = strlen(data); - - if (buffer_offset + data_lenth + 1 >= buffer_size) - return {}; - - auto* buffer_beginning = buffer + buffer_offset; - - memcpy(buffer + buffer_offset, data, data_lenth); - buffer_offset += data_lenth; - buffer[buffer_offset++] = '\0'; - - buffer_offset += 8 - (buffer_offset % 8); - - return buffer_beginning; - }; - - auto add_data_to_buffer = [&](void const* data, size_t size, size_t count = 1) -> Optional { - auto bytes = size * count; - - if (buffer_offset + bytes >= buffer_size) - return {}; - - auto* buffer_beginning = buffer + buffer_offset; - - memcpy(buffer + buffer_offset, data, bytes); - buffer_offset += bytes; - - buffer_offset += 8 - (buffer_offset % 8); - - return buffer_beginning; - }; - - auto add_ptr_to_buffer = [&](void* ptr) -> Optional { - return add_data_to_buffer(&ptr, sizeof(ptr)); - }; - - auto populate_ret = [&](char const* name, in_addr_t address) -> int { - auto h_name = add_string_to_buffer(name); - if (!h_name.has_value()) - return ERANGE; - - ret->h_name = static_cast(h_name.value()); - - auto null_list_item = add_ptr_to_buffer(nullptr); - if (!null_list_item.has_value()) - return ERANGE; - - ret->h_aliases = static_cast(null_list_item.value()); - - auto address_item = add_data_to_buffer(&address, sizeof(address)); - if (!address_item.has_value()) - return ERANGE; - - auto address_list = add_ptr_to_buffer(address_item.value()); - if (!address_list.has_value()) - return ERANGE; - - if (!add_ptr_to_buffer(nullptr).has_value()) - return ERANGE; - - ret->h_addr_list = static_cast(address_list.value()); - - ret->h_addrtype = AF_INET; - ret->h_length = 4; - - *result = ret; - return 0; - }; - - auto ipv4_address = IPv4Address::from_string({ name, strlen(name) }); - - if (ipv4_address.has_value()) { - return populate_ret(ipv4_address.value().to_byte_string().characters(), ipv4_address.value().to_in_addr_t()); - } - - int fd = connect_to_lookup_server(); - if (fd < 0) { - *h_errnop = TRY_AGAIN; - return -TRY_AGAIN; - } - - auto close_fd_on_exit = ScopeGuard([fd] { - close(fd); - }); - - auto name_length = strlen(name); - VERIFY(name_length <= NumericLimits::max()); - - struct [[gnu::packed]] { - u32 message_size; - u32 endpoint_magic; - i32 message_id; - u32 name_length; - } request_header = { - (u32)(sizeof(request_header) - sizeof(request_header.message_size) + name_length), - lookup_server_endpoint_magic, - 1, - static_cast(name_length), - }; - if (auto nsent = write(fd, &request_header, sizeof(request_header)); nsent < 0) { - *h_errnop = TRY_AGAIN; - return -TRY_AGAIN; - } else if (nsent != sizeof(request_header)) { - *h_errnop = NO_RECOVERY; - return -NO_RECOVERY; - } - - if (auto nsent = write(fd, name, name_length); nsent < 0) { - *h_errnop = TRY_AGAIN; - return -TRY_AGAIN; - } else if (static_cast(nsent) != name_length) { - *h_errnop = NO_RECOVERY; - return -NO_RECOVERY; - } - - struct [[gnu::packed]] { - u32 message_size; - u32 endpoint_magic; - i32 message_id; - i32 code; - u32 addresses_count; - } response_header; - - if (auto nreceived = read(fd, &response_header, sizeof(response_header)); nreceived < 0) { - *h_errnop = TRY_AGAIN; - return -TRY_AGAIN; - } else if (nreceived != sizeof(response_header)) { - *h_errnop = NO_RECOVERY; - return -NO_RECOVERY; - } - if (response_header.endpoint_magic != lookup_server_endpoint_magic || response_header.message_id != 2) { - *h_errnop = NO_RECOVERY; - return -NO_RECOVERY; - } - if (response_header.code != 0) { - *h_errnop = NO_RECOVERY; - return -NO_RECOVERY; - } - if (response_header.addresses_count == 0) { - *h_errnop = HOST_NOT_FOUND; - return -HOST_NOT_FOUND; - } - i32 response_length; - if (auto nreceived = read(fd, &response_length, sizeof(response_length)); nreceived < 0) { - *h_errnop = TRY_AGAIN; - return -TRY_AGAIN; - } else if (nreceived != sizeof(response_length) - || response_length != sizeof(in_addr_t)) { - *h_errnop = NO_RECOVERY; - return -NO_RECOVERY; - } - - in_addr_t address; - if (auto nreceived = read(fd, &address, response_length); nreceived < 0) { - *h_errnop = TRY_AGAIN; - return -TRY_AGAIN; - } else if (nreceived != response_length) { - *h_errnop = NO_RECOVERY; - return -NO_RECOVERY; - } - - return populate_ret(name, address); -} - -static ByteString gethostbyaddr_name_buffer; - -hostent* gethostbyaddr(void const* addr, socklen_t addr_size, int type) -{ - h_errno = 0; - - if (type != AF_INET) { - errno = EAFNOSUPPORT; - return nullptr; - } - - if (addr_size < sizeof(in_addr)) { - errno = EINVAL; - return nullptr; - } - - int fd = connect_to_lookup_server(); - if (fd < 0) { - h_errno = TRY_AGAIN; - return nullptr; - } - - auto close_fd_on_exit = ScopeGuard([fd] { - close(fd); - }); - - in_addr_t const& in_addr = ((const struct in_addr*)addr)->s_addr; - - struct [[gnu::packed]] { - u32 message_size; - u32 endpoint_magic; - i32 message_id; - i32 address_length; - } request_header = { - sizeof(request_header) - sizeof(request_header.message_size) + sizeof(in_addr), - lookup_server_endpoint_magic, - 3, - sizeof(in_addr), - }; - if (auto nsent = write(fd, &request_header, sizeof(request_header)); nsent < 0) { - h_errno = TRY_AGAIN; - return nullptr; - } else if (nsent != sizeof(request_header)) { - h_errno = NO_RECOVERY; - return nullptr; - } - if (auto nsent = write(fd, &in_addr, sizeof(in_addr)); nsent < 0) { - h_errno = TRY_AGAIN; - return nullptr; - } else if (nsent != sizeof(in_addr)) { - h_errno = TRY_AGAIN; - return nullptr; - } - - struct [[gnu::packed]] { - u32 message_size; - u32 endpoint_magic; - i32 message_id; - i32 code; - u32 name_length; - } response_header; - - if (auto nreceived = read(fd, &response_header, sizeof(response_header)); nreceived < 0) { - h_errno = TRY_AGAIN; - return nullptr; - } else if (nreceived != sizeof(response_header)) { - h_errno = NO_RECOVERY; - return nullptr; - } - if (response_header.endpoint_magic != lookup_server_endpoint_magic - || response_header.message_id != 4 - || response_header.code != 0) { - h_errno = NO_RECOVERY; - return nullptr; - } - - ssize_t nreceived; - - gethostbyaddr_name_buffer = ByteString::create_and_overwrite(response_header.name_length, [&](Bytes bytes) { - nreceived = read(fd, bytes.data(), bytes.size()); - }); - - if (nreceived < 0) { - h_errno = TRY_AGAIN; - return nullptr; - } else if (static_cast(nreceived) != response_header.name_length) { - h_errno = NO_RECOVERY; - return nullptr; - } - - __gethostbyaddr_buffer.h_name = const_cast(gethostbyaddr_name_buffer.characters()); - __gethostbyaddr_alias_list_buffer[0] = nullptr; - __gethostbyaddr_buffer.h_aliases = __gethostbyaddr_alias_list_buffer; - __gethostbyaddr_buffer.h_addrtype = AF_INET; - // FIXME: Should we populate the hostent's address list here with a sockaddr_in for the provided host? - __gethostbyaddr_address_list_buffer[0] = nullptr; - __gethostbyaddr_buffer.h_addr_list = (char**)__gethostbyaddr_address_list_buffer; - __gethostbyaddr_buffer.h_length = 4; - - return &__gethostbyaddr_buffer; -} - -struct servent* getservent() -{ - // If the services file is not open, attempt to open it and return null if it fails. - if (!services_file) { - services_file = fopen(services_path, "r"); - - if (!services_file) { - perror("error opening services file"); - return nullptr; - } - } - - if (fseek(services_file, service_file_offset, SEEK_SET) != 0) { - perror("error seeking file"); - fclose(services_file); - return nullptr; - } - char* line = nullptr; - size_t len = 0; - ssize_t read; - - auto free_line_on_exit = ScopeGuard([line] { - if (line) { - free(line); - } - }); - - Optional service_file_line = {}; - - // Read lines from services file until an actual service name is found. - do { - read = getline(&line, &len, services_file); - service_file_offset += read; - - auto service_file_line_or_error = parse_service_file_line(line, read); - if (service_file_line_or_error.is_error()) - return nullptr; - - service_file_line = service_file_line_or_error.release_value(); - - if (service_file_line.has_value()) - break; - - } while (read != -1); - if (read == -1) { - fclose(services_file); - services_file = nullptr; - service_file_offset = 0; - return nullptr; - } - - if (!service_file_line.has_value()) - return nullptr; - - servent* service_entry = nullptr; - - __getserv_name_buffer = service_file_line.value().name.to_byte_string(); - __getserv_port_buffer = service_file_line.value().port; - __getserv_protocol_buffer = service_file_line.value().protocol.to_byte_string(); - __getserv_alias_list_buffer = service_file_line.value().aliases; - - __getserv_buffer.s_name = const_cast(__getserv_name_buffer.characters()); - __getserv_buffer.s_port = htons(__getserv_port_buffer); - __getserv_buffer.s_proto = const_cast(__getserv_protocol_buffer.characters()); - - __getserv_alias_list.clear_with_capacity(); - __getserv_alias_list.ensure_capacity(__getserv_alias_list_buffer.size() + 1); - for (auto& alias : __getserv_alias_list_buffer) - __getserv_alias_list.unchecked_append(reinterpret_cast(alias.data())); - __getserv_alias_list.unchecked_append(nullptr); - - __getserv_buffer.s_aliases = __getserv_alias_list.data(); - service_entry = &__getserv_buffer; - - if (!keep_service_file_open) { - endservent(); - } - return service_entry; -} - -struct servent* getservbyname(char const* name, char const* protocol) -{ - if (name == nullptr) - return nullptr; - - bool previous_file_open_setting = keep_service_file_open; - setservent(1); - struct servent* current_service = nullptr; - auto service_file_handler = ScopeGuard([previous_file_open_setting] { - if (!previous_file_open_setting) { - endservent(); - } - }); - - while (true) { - current_service = getservent(); - if (current_service == nullptr) - break; - else if (!protocol && strcmp(current_service->s_name, name) == 0) - break; - else if (strcmp(current_service->s_name, name) == 0 && strcmp(current_service->s_proto, protocol) == 0) - break; - } - - return current_service; -} - -struct servent* getservbyport(int port, char const* protocol) -{ - bool previous_file_open_setting = keep_service_file_open; - setservent(1); - struct servent* current_service = nullptr; - auto service_file_handler = ScopeGuard([previous_file_open_setting] { - if (!previous_file_open_setting) { - endservent(); - } - }); - while (true) { - current_service = getservent(); - if (current_service == nullptr) - break; - else if (!protocol && current_service->s_port == port) - break; - else if (current_service->s_port == port && (strcmp(current_service->s_proto, protocol) == 0)) - break; - } - - return current_service; -} - -void setservent(int stay_open) -{ - if (!services_file) { - services_file = fopen(services_path, "r"); - - if (!services_file) { - perror("error opening services file"); - return; - } - } - rewind(services_file); - keep_service_file_open = stay_open; - service_file_offset = 0; -} - -void endservent() -{ - if (!services_file) { - return; - } - fclose(services_file); - services_file = nullptr; -} - -static ErrorOr> parse_service_file_line(char const* line, ssize_t read) -{ - // If the line isn't a service (eg. empty or a comment) - if (read <= 0 || line[0] < 65 || line[0] > 122) - return { Optional {} }; - - auto split_line = StringView(line, read).replace(" "sv, "\t"sv, ReplaceMode::All).split('\t'); - - if (split_line.size() < 2) - return Error::from_string_view("malformed service file"sv); - - auto name = TRY(String::from_byte_string(split_line[0])); - - auto port_protocol = TRY(String::from_byte_string(split_line[1])); - auto port_protocol_split = TRY(port_protocol.split('/')); - - if (port_protocol_split.size() < 2) - return Error::from_string_view("malformed service file"sv); - - auto port = port_protocol_split[0].to_number(); - if (!port.has_value()) - return Error::from_string_view("port isn't a number"sv); - - // Remove whitespace at the end of the protocol - auto protocol = TRY(port_protocol_split[1].replace(" "sv, ""sv, ReplaceMode::All)); - protocol = TRY(protocol.replace("\t"sv, ""sv, ReplaceMode::All)); - protocol = TRY(protocol.replace("\n"sv, ""sv, ReplaceMode::All)); - - Vector aliases; - - // If there are aliases for the service, we will fill the aliases list - if (split_line.size() > 2 && !split_line[2].starts_with('#')) { - for (size_t i = 2; i < split_line.size(); i++) { - if (split_line[i].starts_with('#')) { - break; - } - auto alias = split_line[i].to_byte_buffer(); - if (alias.try_append("\0", sizeof(char)).is_error()) - return Error::from_string_view("Failed to add null-byte to service alias"sv); - - aliases.append(move(alias)); - } - } - - return ServiceFileLine { - name, protocol, port.value(), aliases - }; -} - -struct protoent* getprotoent() -{ - // If protocols file isn't open, attempt to open and return null on failure. - if (!protocols_file) { - protocols_file = fopen(protocols_path, "r"); - - if (!protocols_file) { - perror("error opening protocols file"); - return nullptr; - } - } - - if (fseek(protocols_file, protocol_file_offset, SEEK_SET) != 0) { - perror("error seeking protocols file"); - fclose(protocols_file); - return nullptr; - } - - char* line = nullptr; - size_t len = 0; - ssize_t read; - - auto free_line_on_exit = ScopeGuard([line] { - if (line) { - free(line); - } - }); - - do { - read = getline(&line, &len, protocols_file); - protocol_file_offset += read; - if (read > 0 && (line[0] >= 65 && line[0] <= 122)) { - break; - } - } while (read != -1); - - if (read == -1) { - fclose(protocols_file); - protocols_file = nullptr; - protocol_file_offset = 0; - return nullptr; - } - - struct protoent* protocol_entry = nullptr; - if (!fill_getproto_buffers(line, read)) - return nullptr; - - __getproto_buffer.p_name = const_cast(__getproto_name_buffer.characters()); - __getproto_buffer.p_proto = __getproto_protocol_buffer; - - __getproto_alias_list.clear_with_capacity(); - __getproto_alias_list.ensure_capacity(__getproto_alias_list_buffer.size() + 1); - for (auto& alias : __getproto_alias_list_buffer) - __getproto_alias_list.unchecked_append(reinterpret_cast(alias.data())); - __getserv_alias_list.unchecked_append(nullptr); - - __getproto_buffer.p_aliases = __getproto_alias_list.data(); - protocol_entry = &__getproto_buffer; - - if (!keep_protocols_file_open) - endprotoent(); - - return protocol_entry; -} - -struct protoent* getprotobyname(char const* name) -{ - bool previous_file_open_setting = keep_protocols_file_open; - setprotoent(1); - struct protoent* current_protocol = nullptr; - auto protocol_file_handler = ScopeGuard([previous_file_open_setting] { - if (!previous_file_open_setting) { - endprotoent(); - } - }); - - while (true) { - current_protocol = getprotoent(); - if (current_protocol == nullptr) - break; - else if (strcmp(current_protocol->p_name, name) == 0) - break; - } - - return current_protocol; -} - -struct protoent* getprotobynumber(int proto) -{ - bool previous_file_open_setting = keep_protocols_file_open; - setprotoent(1); - struct protoent* current_protocol = nullptr; - auto protocol_file_handler = ScopeGuard([previous_file_open_setting] { - if (!previous_file_open_setting) { - endprotoent(); - } - }); - - while (true) { - current_protocol = getprotoent(); - if (current_protocol == nullptr) - break; - else if (current_protocol->p_proto == proto) - break; - } - - return current_protocol; -} - -void setprotoent(int stay_open) -{ - if (!protocols_file) { - protocols_file = fopen(protocols_path, "r"); - - if (!protocols_file) { - perror("setprotoent(): error opening protocols file"); - return; - } - } - rewind(protocols_file); - keep_protocols_file_open = stay_open; - protocol_file_offset = 0; -} - -void endprotoent() -{ - if (!protocols_file) { - return; - } - fclose(protocols_file); - protocols_file = nullptr; -} - -static bool fill_getproto_buffers(char const* line, ssize_t read) -{ - ByteString string_line = ByteString(line, read); - auto split_line = string_line.replace(" "sv, "\t"sv, ReplaceMode::All).split('\t'); - - // This indicates an incorrect file format. Protocols file entries should - // always have at least a name and a protocol. - if (split_line.size() < 2) { - warnln("getprotoent(): malformed protocols file"); - return false; - } - __getproto_name_buffer = split_line[0]; - - auto number = split_line[1].to_number(); - if (!number.has_value()) - return false; - - __getproto_protocol_buffer = number.value(); - - __getproto_alias_list_buffer.clear(); - - // If there are aliases for the protocol, we will fill the alias list buffer. - if (split_line.size() > 2 && !split_line[2].starts_with('#')) { - - for (size_t i = 2; i < split_line.size(); i++) { - if (split_line[i].starts_with('#')) - break; - auto alias = split_line[i].to_byte_buffer(); - if (alias.try_append("\0", sizeof(char)).is_error()) - return false; - __getproto_alias_list_buffer.append(move(alias)); - } - } - - return true; -} - -int getaddrinfo(char const* __restrict node, char const* __restrict service, const struct addrinfo* __restrict hints, struct addrinfo** __restrict res) -{ - *res = nullptr; - - if (hints && hints->ai_family != AF_INET && hints->ai_family != AF_UNSPEC) - return EAI_FAMILY; - - if (!node) { - if (hints && hints->ai_flags & AI_PASSIVE) - node = "0.0.0.0"; - else - node = "127.0.0.1"; - } - - size_t buffer_size = 1024; - char* buffer = nullptr; - int gethostbyname_errno = 0; - struct hostent ret = {}; - struct hostent* host_ent = nullptr; - - while (true) { - buffer = (char*)realloc(buffer, buffer_size); - - if (buffer == nullptr) - return EAI_MEMORY; - - int rc = gethostbyname_r(node, &ret, buffer, buffer_size, &host_ent, &gethostbyname_errno); - if (rc == ERANGE) { - buffer_size *= 2; - continue; - } - - if (!host_ent) - return EAI_FAIL; - break; - } - - char const* proto = nullptr; - if (hints && hints->ai_socktype) { - switch (hints->ai_socktype) { - case SOCK_STREAM: - proto = "tcp"; - break; - case SOCK_DGRAM: - proto = "udp"; - break; - default: - return EAI_SOCKTYPE; - } - } - - long port; - int socktype; - - Optional service_file_line = {}; - - if ((!hints || (hints->ai_flags & AI_NUMERICSERV) == 0) && service) { - services_file = fopen(services_path, "r"); - if (!services_file) { - return EAI_FAIL; - } - - auto close_services_file_handler = ScopeGuard([&] { - fclose(services_file); - }); - - char* line = nullptr; - size_t length = 0; - ssize_t read; - - while (true) { - do { - read = getline(&line, &length, services_file); - - auto service_file_line_or_error = parse_service_file_line(line, read); - if (service_file_line_or_error.is_error()) - return EAI_SYSTEM; - - service_file_line = service_file_line_or_error.release_value(); - - if (service_file_line.has_value()) - break; - } while (read != -1); - - if (read == -1 || !service_file_line.has_value()) - break; - - if (service_file_line.value().name != service) - continue; - - if (service_file_line.value().protocol != proto) - continue; - - break; - } - } - - if (!service_file_line.has_value()) { - if (service) { - char* end; - port = strtol(service, &end, 10); - if (*end) - return EAI_FAIL; - } else { - port = 0; - } - - if (hints && hints->ai_socktype != 0) - socktype = hints->ai_socktype; - else - socktype = SOCK_STREAM; - } else { - port = service_file_line.value().port; - socktype = service_file_line.value().protocol == "tcp" ? SOCK_STREAM : SOCK_DGRAM; - } - - addrinfo* first_info = nullptr; - addrinfo* prev_info = nullptr; - - for (int host_index = 0; host_ent->h_addr_list[host_index]; host_index++) { - sockaddr_in* sin = new sockaddr_in; - sin->sin_family = AF_INET; - sin->sin_port = htons(port); - memcpy(&sin->sin_addr.s_addr, host_ent->h_addr_list[host_index], host_ent->h_length); - - addrinfo* info = new addrinfo; - info->ai_flags = 0; - info->ai_family = AF_INET; - info->ai_socktype = socktype; - info->ai_protocol = PF_INET; - info->ai_addrlen = sizeof(*sin); - info->ai_addr = reinterpret_cast(sin); - - if (hints && hints->ai_flags & AI_CANONNAME) - info->ai_canonname = strdup(host_ent->h_name); - else - info->ai_canonname = nullptr; - - info->ai_next = nullptr; - - if (!first_info) - first_info = info; - - if (prev_info) - prev_info->ai_next = info; - - prev_info = info; - } - - if (first_info) { - *res = first_info; - return 0; - } else - return EAI_NONAME; -} - -void freeaddrinfo(struct addrinfo* res) -{ - if (res) { - delete reinterpret_cast(res->ai_addr); - free(res->ai_canonname); - freeaddrinfo(res->ai_next); - delete res; - } -} - -char const* gai_strerror(int errcode) -{ - switch (errcode) { - case EAI_ADDRFAMILY: - return "no address for this address family available"; - case EAI_AGAIN: - return "name server returned temporary failure"; - case EAI_BADFLAGS: - return "invalid flags"; - case EAI_FAIL: - return "name server returned permanent failure"; - case EAI_FAMILY: - return "unsupported address family"; - case EAI_MEMORY: - return "out of memory"; - case EAI_NODATA: - return "no address available"; - case EAI_NONAME: - return "node or service is not known"; - case EAI_SERVICE: - return "service not available"; - case EAI_SOCKTYPE: - return "unsupported socket type"; - case EAI_SYSTEM: - return "system error"; - case EAI_OVERFLOW: - return "buffer too small"; - default: - return "invalid error code"; - } -} - -int getnameinfo(const struct sockaddr* __restrict addr, socklen_t addrlen, char* __restrict host, socklen_t hostlen, char* __restrict serv, socklen_t servlen, int flags) -{ - if (addr->sa_family != AF_INET || addrlen < sizeof(sockaddr_in)) - return EAI_FAMILY; - - sockaddr_in const* sin = reinterpret_cast(addr); - - if (host && hostlen > 0) { - if (flags != 0) - dbgln("getnameinfo flags are not implemented: {:#x}", flags); - - if (!inet_ntop(AF_INET, &sin->sin_addr, host, hostlen)) { - if (errno == ENOSPC) - return EAI_OVERFLOW; - else - return EAI_SYSTEM; - } - } - - if (serv && servlen > 0) { - if (snprintf(serv, servlen, "%d", (int)ntohs(sin->sin_port)) > (int)servlen) - return EAI_OVERFLOW; - } - - return 0; -} - -void herror(char const* s) -{ - dbgln("herror(): {}: {}", s, hstrerror(h_errno)); - warnln("{}: {}", s, hstrerror(h_errno)); -} - -char const* hstrerror(int err) -{ - switch (err) { - case HOST_NOT_FOUND: - return "The specified host is unknown."; - case NO_DATA: - return "The requested name is valid but does not have an IP address."; - case NO_RECOVERY: - return "A nonrecoverable name server error occurred."; - case TRY_AGAIN: - return "A temporary error occurred on an authoritative name server. Try again later."; - default: - return "Unknown error."; - } -} -} diff --git a/Userland/Libraries/LibC/netdb.h b/Userland/Libraries/LibC/netdb.h deleted file mode 100644 index 3a40ca04d9a..00000000000 --- a/Userland/Libraries/LibC/netdb.h +++ /dev/null @@ -1,115 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -// Includes essentially mandated by POSIX: -// https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/netdb.h.html -#include -#include -#include - -#include -#include - -__BEGIN_DECLS - -struct hostent { - char* h_name; - char** h_aliases; - int h_addrtype; - int h_length; - char** h_addr_list; -#define h_addr h_addr_list[0] -}; - -struct hostent* gethostbyname(char const*); -int gethostbyname_r(char const* __restrict name, struct hostent* __restrict ret, char* buffer, size_t buffer_size, struct hostent** __restrict result, int* __restrict h_errnop); -struct hostent* gethostbyaddr(void const* addr, socklen_t len, int type); - -struct servent { - char* s_name; - char** s_aliases; - int s_port; - char* s_proto; -}; - -struct servent* getservent(void); -struct servent* getservbyname(char const* name, char const* protocol); -struct servent* getservbyport(int port, char const* protocol); -void setservent(int stay_open); -void endservent(void); - -struct protoent { - char* p_name; - char** p_aliases; - int p_proto; -}; - -void endprotoent(void); -struct protoent* getprotobyname(char const* name); -struct protoent* getprotobynumber(int proto); -struct protoent* getprotoent(void); -void setprotoent(int stay_open); - -extern __thread int h_errno; - -#define HOST_NOT_FOUND 101 -#define NO_DATA 102 -#define NO_ADDRESS NO_DATA -#define NO_RECOVERY 103 -#define TRY_AGAIN 104 - -struct addrinfo { - int ai_flags; - int ai_family; - int ai_socktype; - int ai_protocol; - socklen_t ai_addrlen; - struct sockaddr* ai_addr; - char* ai_canonname; - struct addrinfo* ai_next; -}; - -#define EAI_ADDRFAMILY 1 -#define EAI_AGAIN 2 -#define EAI_BADFLAGS 3 -#define EAI_FAIL 4 -#define EAI_FAMILY 5 -#define EAI_MEMORY 6 -#define EAI_NODATA 7 -#define EAI_NONAME 8 -#define EAI_SERVICE 9 -#define EAI_SOCKTYPE 10 -#define EAI_SYSTEM 11 -#define EAI_OVERFLOW 12 - -#define AI_PASSIVE 0x0001 -#define AI_CANONNAME 0x0002 -#define AI_NUMERICHOST 0x0004 -#define AI_NUMERICSERV 0x0008 -#define AI_V4MAPPED 0x0010 -#define AI_ALL 0x0020 -#define AI_ADDRCONFIG 0x0040 - -#define NI_MAXHOST 1025 -#define NI_MAXSERV 32 - -#define NI_NUMERICHOST (1 << 0) -#define NI_NUMERICSERV (1 << 1) -#define NI_NAMEREQD (1 << 2) -#define NI_NOFQDN (1 << 3) -#define NI_DGRAM (1 << 4) - -int getaddrinfo(char const* __restrict node, char const* __restrict service, const struct addrinfo* __restrict hints, struct addrinfo** __restrict res); -void freeaddrinfo(struct addrinfo* res); -char const* gai_strerror(int errcode); -int getnameinfo(const struct sockaddr* __restrict addr, socklen_t addrlen, char* __restrict host, socklen_t hostlen, char* __restrict serv, socklen_t servlen, int flags); - -void herror(char const* s); -char const* hstrerror(int err); - -__END_DECLS diff --git a/Userland/Libraries/LibC/netinet/if_ether.h b/Userland/Libraries/LibC/netinet/if_ether.h deleted file mode 100644 index 4b0f390c429..00000000000 --- a/Userland/Libraries/LibC/netinet/if_ether.h +++ /dev/null @@ -1,9 +0,0 @@ -/* - * Copyright (c) 2021, Idan Horowitz - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#define ETH_ALEN 6 // Length of an ethernet address (MAC) in bytes diff --git a/Userland/Libraries/LibC/netinet/in.h b/Userland/Libraries/LibC/netinet/in.h deleted file mode 100644 index 1dc1da98ae5..00000000000 --- a/Userland/Libraries/LibC/netinet/in.h +++ /dev/null @@ -1,95 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -// Includes essentially mandated by POSIX: -// https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/netinet_in.h.html -#include -#include - -#include -#include -#include - -__BEGIN_DECLS - -in_addr_t inet_addr(char const*); - -static inline uint16_t htons(uint16_t value) -{ -#if __BYTE_ORDER == __LITTLE_ENDIAN - return __builtin_bswap16(value); -#else - return value; -#endif -} - -static inline uint16_t ntohs(uint16_t value) -{ - return htons(value); -} - -static inline uint32_t htonl(uint32_t value) -{ -#if __BYTE_ORDER == __LITTLE_ENDIAN - return __builtin_bswap32(value); -#else - return value; -#endif -} - -static inline uint32_t ntohl(uint32_t value) -{ - return htonl(value); -} - -#define IN_CLASSA(addr) ((((uint32_t)(addr)) & (128 << 24)) == 0) -#define IN_CLASSB(addr) ((((uint32_t)(addr)) & (192 << 24)) == (128 << 24)) - -#define IN_MULTICAST(x) (((x) & 0xf0000000) == 0xe0000000) - -// NOTE: The IPv6 Addressing Scheme that we detect are documented in RFC# 2373. -// See: https://datatracker.ietf.org/doc/html/rfc2373 - -// RFC# 2373 - 2.5.3 The Loopback Address -#define IN6_IS_ADDR_LOOPBACK(addr) \ - ((addr)->s6_addr[0] == 0 && (addr)->s6_addr[1] == 0 && (addr)->s6_addr[2] == 0 && (addr)->s6_addr[3] == 0 && (addr)->s6_addr[4] == 0 && (addr)->s6_addr[5] == 0 && (addr)->s6_addr[6] == 0 && (addr)->s6_addr[7] == 0 && (addr)->s6_addr[8] == 0 && (addr)->s6_addr[9] == 0 && (addr)->s6_addr[10] == 0 && (addr)->s6_addr[11] == 0 && (addr)->s6_addr[12] == 0 && (addr)->s6_addr[13] == 0 && (addr)->s6_addr[14] == 0 && (addr)->s6_addr[15] == 1) - -// RFC# 2373 - 2.5.4 IPv6 Addresses with Embedded IPv4 Addresses -#define IN6_IS_ADDR_V4COMPAT(addr) \ - ((addr)->s6_addr32[0] == 0 && ((addr)->s6_addr32[1]) == 0 && ((addr)->s6_addr32[2]) == 0 && ntohl((addr)->s6_addr32[3]) >= 2) - -#define IN6_IS_ADDR_V4MAPPED(addr) \ - ((((addr)->s6_addr[0]) == 0) && (((addr)->s6_addr[1]) == 0) && (((addr)->s6_addr[2]) == 0) && (((addr)->s6_addr[3]) == 0) && (((addr)->s6_addr[4]) == 0) && (((addr)->s6_addr[5]) == 0) && (((addr)->s6_addr[6]) == 0) && (((addr)->s6_addr[7]) == 0) && (((addr)->s6_addr[8]) == 0) && (((addr)->s6_addr[9]) == 0) && (((addr)->s6_addr[10]) == 0xFF) && (((addr)->s6_addr[11]) == 0xFF)) - -// RFC# 2373 - 2.5.8 Local-Use IPv6 Unicast Addresses -#define IN6_IS_ADDR_LINKLOCAL(addr) \ - (((addr)->s6_addr[0] == 0xfe) && ((addr)->s6_addr[1] == 0x80) && ((addr)->s6_addr[2] == 0) && ((addr)->s6_addr[3] == 0) && (((addr)->s6_addr32[1]) == 0)) - -#define IN6_IS_ADDR_SITELOCAL(addr) \ - ((addr)->s6_addr[0] == 0xfe && (addr)->s6_addr[1] == 0xc0 && (addr)->s6_addr[2] == 0 && (addr)->s6_addr[3] == 0 && (addr)->s6_addr[4] == 0 && (addr)->s6_addr[5] == 0) - -// RFC# 2373 - 2.7 Multicast Addresses -#define IN6_IS_ADDR_MULTICAST(addr) \ - ((addr)->s6_addr[0] == 0xff) - -#define IN6_IS_ADDR_MC_NODELOCAL(addr) \ - (IN6_IS_ADDR_MULTICAST(addr) && (((addr)->s6_addr[1] & 0xf) == 0x1)) - -#define IN6_IS_ADDR_MC_LINKLOCAL(addr) \ - (IN6_IS_ADDR_MULTICAST(addr) && (((addr)->s6_addr[1] & 0xf) == 0x2)) - -#define IN6_IS_ADDR_MC_SITELOCAL(addr) \ - (IN6_IS_ADDR_MULTICAST(addr) && (((addr)->s6_addr[1] & 0xf) == 0x5)) - -#define IN6_IS_ADDR_MC_ORGLOCAL(addr) \ - (IN6_IS_ADDR_MULTICAST(addr) && (((addr)->s6_addr[1] & 0xf) == 0x8)) - -#define IN6_IS_ADDR_MC_GLOBAL(addr) \ - (IN6_IS_ADDR_MULTICAST(addr) && (((addr)->s6_addr[1] & 0xf) == 0xe)) - -__END_DECLS diff --git a/Userland/Libraries/LibC/netinet/in_systm.h b/Userland/Libraries/LibC/netinet/in_systm.h deleted file mode 100644 index d612ea2e47e..00000000000 --- a/Userland/Libraries/LibC/netinet/in_systm.h +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Copyright (c) 2021, Leon Albrecht - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include - -__BEGIN_DECLS - -typedef uint16_t n_short; -typedef uint32_t n_long; - -typedef uint32_t n_time; - -__END_DECLS diff --git a/Userland/Libraries/LibC/netinet/ip.h b/Userland/Libraries/LibC/netinet/ip.h deleted file mode 100644 index ba606d12b93..00000000000 --- a/Userland/Libraries/LibC/netinet/ip.h +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include - -#include "in.h" - -__BEGIN_DECLS - -struct ip { -#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ - uint8_t ip_v : 4; - uint8_t ip_hl : 4; -#elif __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ - uint8_t ip_hl : 4; - uint8_t ip_v : 4; -#endif - uint8_t ip_tos; - uint16_t ip_len; - uint16_t ip_id; - uint16_t ip_off; - uint8_t ip_ttl; - uint8_t ip_p; - uint16_t ip_sum; - struct in_addr ip_src; - struct in_addr ip_dst; -} __attribute__((packed)); -static_assert(sizeof(struct ip) == 20, "struct ip: invalid length"); - -__END_DECLS diff --git a/Userland/Libraries/LibC/netinet/ip_icmp.h b/Userland/Libraries/LibC/netinet/ip_icmp.h deleted file mode 100644 index 6d8e3a8d457..00000000000 --- a/Userland/Libraries/LibC/netinet/ip_icmp.h +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include - -__BEGIN_DECLS - -struct icmphdr { - uint8_t type; - uint8_t code; - uint16_t checksum; - union { - struct { - uint16_t id; - uint16_t sequence; - } echo; - uint32_t gateway; - } un; -}; - -#define ICMP_ECHOREPLY 0 // Echo Reply -#define ICMP_DEST_UNREACH 3 // Destination Unreachable -#define ICMP_SOURCE_QUENCH 4 // Source Quench -#define ICMP_REDIRECT 5 // Redirect -#define ICMP_ECHO 8 // Echo Request -#define ICMP_TIME_EXCEEDED 11 // Time Exceeded -#define ICMP_PARAMETERPROB 12 // Parameter Problem -#define ICMP_TIMESTAMP 13 // Timestamp Request -#define ICMP_TIMESTAMPREPLY 14 // Timestamp Reply -#define ICMP_INFO_REQUEST 15 // Information Request -#define ICMP_INFO_REPLY 16 // Information Reply -#define ICMP_ADDRESS 17 // Address Mask Request -#define ICMP_ADDRESSREPLY 18 // Address Mask Reply -#define NR_ICMP_TYPES 18 - -__END_DECLS diff --git a/Userland/Libraries/LibC/netinet/tcp.h b/Userland/Libraries/LibC/netinet/tcp.h deleted file mode 100644 index 6e304da6da9..00000000000 --- a/Userland/Libraries/LibC/netinet/tcp.h +++ /dev/null @@ -1,9 +0,0 @@ -/* - * Copyright (c) 2020, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include diff --git a/Userland/Libraries/LibC/nl_types.h b/Userland/Libraries/LibC/nl_types.h deleted file mode 100644 index d7bd79fa4e3..00000000000 --- a/Userland/Libraries/LibC/nl_types.h +++ /dev/null @@ -1,15 +0,0 @@ -/* - * Copyright (c) 2021, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include - -__BEGIN_DECLS - -typedef int nl_item; - -__END_DECLS diff --git a/Userland/Libraries/LibC/paths.h b/Userland/Libraries/LibC/paths.h deleted file mode 100644 index 636f564be58..00000000000 --- a/Userland/Libraries/LibC/paths.h +++ /dev/null @@ -1,16 +0,0 @@ -/* - * Copyright (c) 2020, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -// FIXME: This is just a default value to satisfy OpenSSH, feel free to change it. -#define _PATH_MAILDIR "/var/mail" - -// Deprecated definition for dosfstools port. -#define _PATH_MOUNTED "/etc/mtab" - -// Default value used for lowdown port. -#define _PATH_TTY "/dev/tty" diff --git a/Userland/Libraries/LibC/poll.cpp b/Userland/Libraries/LibC/poll.cpp deleted file mode 100644 index 2d80d13cce3..00000000000 --- a/Userland/Libraries/LibC/poll.cpp +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include - -extern "C" { - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/poll.html -int poll(pollfd* fds, nfds_t nfds, int timeout_ms) -{ - __pthread_maybe_cancel(); - - timespec timeout; - timespec* timeout_ts = &timeout; - if (timeout_ms < 0) - timeout_ts = nullptr; - else - timeout = { timeout_ms / 1000, (timeout_ms % 1000) * 1'000'000 }; - return ppoll(fds, nfds, timeout_ts, nullptr); -} - -int ppoll(pollfd* fds, nfds_t nfds, timespec const* timeout, sigset_t const* sigmask) -{ - Syscall::SC_poll_params params { fds, nfds, timeout, sigmask }; - int rc = syscall(SC_poll, ¶ms); - __RETURN_WITH_ERRNO(rc, rc, -1); -} -} diff --git a/Userland/Libraries/LibC/poll.h b/Userland/Libraries/LibC/poll.h deleted file mode 100644 index 3a0aa551ad2..00000000000 --- a/Userland/Libraries/LibC/poll.h +++ /dev/null @@ -1,18 +0,0 @@ -/* - * Copyright (c) 2018-2021, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include - -__BEGIN_DECLS - -int poll(struct pollfd* fds, nfds_t nfds, int timeout); -int ppoll(struct pollfd* fds, nfds_t nfds, const struct timespec* timeout, sigset_t const* sigmask); - -__END_DECLS diff --git a/Userland/Libraries/LibC/priority.cpp b/Userland/Libraries/LibC/priority.cpp deleted file mode 100644 index 05b97c731a8..00000000000 --- a/Userland/Libraries/LibC/priority.cpp +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright (c) 2021, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include - -extern "C" { - -int getpriority([[maybe_unused]] int which, [[maybe_unused]] id_t who) -{ - dbgln("FIXME: Implement getpriority()"); - return -1; -} - -int setpriority([[maybe_unused]] int which, [[maybe_unused]] id_t who, [[maybe_unused]] int value) -{ - dbgln("FIXME: Implement setpriority()"); - return -1; -} -} diff --git a/Userland/Libraries/LibC/pthread.cpp b/Userland/Libraries/LibC/pthread.cpp deleted file mode 100644 index 29046ce16d4..00000000000 --- a/Userland/Libraries/LibC/pthread.cpp +++ /dev/null @@ -1,940 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace { -using PthreadAttrImpl = Syscall::SC_create_thread_params; - -} // end anonymous namespace - -static constexpr size_t required_stack_alignment = 4 * MiB; -static constexpr size_t highest_reasonable_guard_size = 32 * PAGE_SIZE; - -__thread void* s_stack_location; -__thread size_t s_stack_size; - -__thread int s_thread_cancel_state = PTHREAD_CANCEL_ENABLE; -__thread int s_thread_cancel_type = PTHREAD_CANCEL_DEFERRED; - -#define __RETURN_PTHREAD_ERROR(rc) \ - return ((rc) < 0 ? -(rc) : 0) - -struct CleanupHandler { - void (*routine)(void*); - void* argument; -}; - -static thread_local SinglyLinkedList cleanup_handlers; - -static __thread bool pending_cancellation = false; - -[[gnu::weak]] extern ErrorOr __create_new_tls_region() asm("__create_new_tls_region"); -[[gnu::weak]] extern ErrorOr __free_tls_region(FlatPtr thread_pointer) asm("__free_tls_region"); - -extern "C" { - -[[noreturn]] static void exit_thread(void* code, void* stack_location, size_t stack_size) -{ - __pthread_key_destroy_for_current_thread(); - MUST(__free_tls_region(bit_cast(__builtin_thread_pointer()))); - syscall(SC_exit_thread, code, stack_location, stack_size); - VERIFY_NOT_REACHED(); -} - -[[noreturn]] static void pthread_exit_without_cleanup_handlers(void* value_ptr) -{ - exit_thread(value_ptr, s_stack_location, s_stack_size); -} - -static void* pthread_create_helper(void* (*routine)(void*), void* argument, void* stack_location, size_t stack_size) -{ - s_stack_location = stack_location; - s_stack_size = stack_size; - void* ret_val = routine(argument); - pthread_exit_without_cleanup_handlers(ret_val); -} - -static int create_thread(pthread_t* thread, void* (*entry)(void*), void* argument, PthreadAttrImpl* thread_params) -{ - void** stack = (void**)((uintptr_t)thread_params->stack_location + thread_params->stack_size); - - auto push_on_stack = [&](void* data) { - stack--; - *stack = data; - thread_params->stack_size -= sizeof(void*); - }; - - // We set up the stack for pthread_create_helper. - // Note that we need to align the stack to 16B, accounting for - // the fact that we also push 16 bytes. - while (((uintptr_t)stack - 16) % 16 != 0) - push_on_stack(nullptr); - - thread_params->entry = entry; - thread_params->entry_argument = argument; - - auto maybe_thread_pointer = __create_new_tls_region(); - if (maybe_thread_pointer.is_error()) - return maybe_thread_pointer.error().code(); - - thread_params->tls_pointer = bit_cast(maybe_thread_pointer.release_value()); - - VERIFY((uintptr_t)stack % 16 == 0); - - // Push a fake return address - push_on_stack(nullptr); - - int rc = syscall(SC_create_thread, pthread_create_helper, thread_params); - if (rc >= 0) - *thread = rc; - else - MUST(__free_tls_region(bit_cast(thread_params->tls_pointer))); - - __RETURN_PTHREAD_ERROR(rc); -} - -// https://pubs.opengroup.org/onlinepubs/009695399/functions/pthread_create.html -int pthread_create(pthread_t* thread, pthread_attr_t const* attributes, void* (*start_routine)(void*), void* argument_to_start_routine) -{ - if (!thread) - return -EINVAL; - - PthreadAttrImpl default_attributes {}; - PthreadAttrImpl* const* arg_attributes = reinterpret_cast(attributes); - - PthreadAttrImpl* used_attributes = arg_attributes ? *arg_attributes : &default_attributes; - - if (!used_attributes->stack_location) { - // adjust stack size, user might have called setstacksize, which has no restrictions on size/alignment - if (0 != (used_attributes->stack_size % required_stack_alignment)) - used_attributes->stack_size += required_stack_alignment - (used_attributes->stack_size % required_stack_alignment); - - used_attributes->stack_location = mmap_with_name(nullptr, used_attributes->stack_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_STACK, 0, 0, "Thread stack"); - if (!used_attributes->stack_location) - return -1; - } - - dbgln_if(PTHREAD_DEBUG, "pthread_create: Creating thread with attributes at {}, detach state {}, priority {}, guard page size {}, stack size {}, stack location {}", - used_attributes, - (PTHREAD_CREATE_JOINABLE == used_attributes->detach_state) ? "joinable" : "detached", - used_attributes->schedule_priority, - used_attributes->guard_page_size, - used_attributes->stack_size, - used_attributes->stack_location); - - return create_thread(thread, start_routine, argument_to_start_routine, used_attributes); -} - -// https://pubs.opengroup.org/onlinepubs/009695399/functions/pthread_exit.html -void pthread_exit(void* value_ptr) -{ - while (!cleanup_handlers.is_empty()) { - auto handler = cleanup_handlers.take_first(); - handler.routine(handler.argument); - } - - pthread_exit_without_cleanup_handlers(value_ptr); -} - -#ifndef _DYNAMIC_LOADER -void __pthread_maybe_cancel() -{ - // Check if we have cancellations enabled. - if (s_thread_cancel_state != PTHREAD_CANCEL_ENABLE) - return; - - // Check if a cancellation request is pending. - if (!pending_cancellation) - return; - - // Exit the thread via `pthread_exit`. This handles passing the - // return value and calling the cleanup handlers for us. - pthread_exit(PTHREAD_CANCELED); -} -#endif - -// https://pubs.opengroup.org/onlinepubs/009695399/functions/pthread_cleanup_push.html -void pthread_cleanup_push(void (*routine)(void*), void* arg) -{ - cleanup_handlers.prepend({ routine, arg }); -} - -// https://pubs.opengroup.org/onlinepubs/009695399/functions/pthread_cleanup_pop.html -void pthread_cleanup_pop(int execute) -{ - VERIFY(!cleanup_handlers.is_empty()); - - auto handler = cleanup_handlers.take_first(); - - if (execute) - handler.routine(handler.argument); -} - -// https://pubs.opengroup.org/onlinepubs/009695399/functions/pthread_join.html -int pthread_join(pthread_t thread, void** exit_value_ptr) -{ - __pthread_maybe_cancel(); - - int rc = syscall(SC_join_thread, thread, exit_value_ptr); - __RETURN_PTHREAD_ERROR(rc); -} - -// https://pubs.opengroup.org/onlinepubs/009695399/functions/pthread_kill.html -int pthread_kill(pthread_t thread, int sig) -{ - int rc = syscall(SC_kill_thread, thread, sig); - __RETURN_PTHREAD_ERROR(rc); -} - -// https://pubs.opengroup.org/onlinepubs/009695399/functions/pthread_detach.html -int pthread_detach(pthread_t thread) -{ - int rc = syscall(SC_detach_thread, thread); - __RETURN_PTHREAD_ERROR(rc); -} - -// https://pubs.opengroup.org/onlinepubs/009695399/functions/pthread_sigmask.html -int pthread_sigmask(int how, sigset_t const* set, sigset_t* old_set) -{ - if (sigprocmask(how, set, old_set)) - return errno; - return 0; -} - -// https://pubs.opengroup.org/onlinepubs/009695399/functions/pthread_mutex_destroy.html -int pthread_mutex_destroy(pthread_mutex_t*) -{ - return 0; -} - -// https://pubs.opengroup.org/onlinepubs/009695399/functions/pthread_mutexattr_init.html -int pthread_mutexattr_init(pthread_mutexattr_t* attr) -{ - attr->type = PTHREAD_MUTEX_NORMAL; - return 0; -} - -// https://pubs.opengroup.org/onlinepubs/009695399/functions/pthread_mutexattr_destroy.html -int pthread_mutexattr_destroy(pthread_mutexattr_t*) -{ - return 0; -} - -int pthread_mutexattr_settype(pthread_mutexattr_t* attr, int type) -{ - if (!attr) - return EINVAL; - if (type != PTHREAD_MUTEX_NORMAL && type != PTHREAD_MUTEX_RECURSIVE) - return EINVAL; - attr->type = type; - return 0; -} - -// https://pubs.opengroup.org/onlinepubs/009695399/functions/pthread_mutexattr_gettype.html -int pthread_mutexattr_gettype(pthread_mutexattr_t* attr, int* type) -{ - *type = attr->type; - return 0; -} - -// https://pubs.opengroup.org/onlinepubs/009695399/functions/pthread_attr_init.html -int pthread_attr_init(pthread_attr_t* attributes) -{ - auto* impl = new PthreadAttrImpl {}; - *attributes = impl; - - dbgln_if(PTHREAD_DEBUG, "pthread_attr_init: New thread attributes at {}, detach state {}, priority {}, guard page size {}, stack size {}, stack location {}", - impl, - (PTHREAD_CREATE_JOINABLE == impl->detach_state) ? "joinable" : "detached", - impl->schedule_priority, - impl->guard_page_size, - impl->stack_size, - impl->stack_location); - - return 0; -} - -// https://pubs.opengroup.org/onlinepubs/009695399/functions/pthread_attr_destroy.html -int pthread_attr_destroy(pthread_attr_t* attributes) -{ - auto* attributes_impl = *(reinterpret_cast(attributes)); - delete attributes_impl; - return 0; -} - -// https://pubs.opengroup.org/onlinepubs/009695399/functions/pthread_attr_getdetachstate.html -int pthread_attr_getdetachstate(pthread_attr_t const* attributes, int* p_detach_state) -{ - auto* attributes_impl = *(reinterpret_cast(attributes)); - - if (!attributes_impl || !p_detach_state) - return EINVAL; - - *p_detach_state = attributes_impl->detach_state; - return 0; -} - -// https://pubs.opengroup.org/onlinepubs/009695399/functions/pthread_attr_setdetachstate.html -int pthread_attr_setdetachstate(pthread_attr_t* attributes, int detach_state) -{ - auto* attributes_impl = *(reinterpret_cast(attributes)); - - if (!attributes_impl) - return EINVAL; - - if (detach_state != PTHREAD_CREATE_JOINABLE && detach_state != PTHREAD_CREATE_DETACHED) - return EINVAL; - - attributes_impl->detach_state = detach_state; - - dbgln_if(PTHREAD_DEBUG, "pthread_attr_setdetachstate: Thread attributes at {}, detach state {}, priority {}, guard page size {}, stack size {}, stack location {}", - attributes_impl, - (PTHREAD_CREATE_JOINABLE == attributes_impl->detach_state) ? "joinable" : "detached", - attributes_impl->schedule_priority, - attributes_impl->guard_page_size, - attributes_impl->stack_size, - attributes_impl->stack_location); - - return 0; -} - -// https://pubs.opengroup.org/onlinepubs/009695399/functions/pthread_attr_getguardsize.html -int pthread_attr_getguardsize(pthread_attr_t const* attributes, size_t* p_guard_size) -{ - auto* attributes_impl = *(reinterpret_cast(attributes)); - - if (!attributes_impl || !p_guard_size) - return EINVAL; - - *p_guard_size = attributes_impl->reported_guard_page_size; - return 0; -} - -// https://pubs.opengroup.org/onlinepubs/009695399/functions/pthread_attr_setguardsize.html -int pthread_attr_setguardsize(pthread_attr_t* attributes, size_t guard_size) -{ - auto* attributes_impl = *(reinterpret_cast(attributes)); - - if (!attributes_impl) - return EINVAL; - - size_t actual_guard_size = guard_size; - // round up - if (0 != (guard_size % PAGE_SIZE)) - actual_guard_size += PAGE_SIZE - (guard_size % PAGE_SIZE); - - // what is the user even doing? - if (actual_guard_size > highest_reasonable_guard_size) { - return EINVAL; - } - - attributes_impl->guard_page_size = actual_guard_size; - attributes_impl->reported_guard_page_size = guard_size; // POSIX, why? - - dbgln_if(PTHREAD_DEBUG, "pthread_attr_setguardsize: Thread attributes at {}, detach state {}, priority {}, guard page size {}, stack size {}, stack location {}", - attributes_impl, - (PTHREAD_CREATE_JOINABLE == attributes_impl->detach_state) ? "joinable" : "detached", - attributes_impl->schedule_priority, - attributes_impl->guard_page_size, - attributes_impl->stack_size, - attributes_impl->stack_location); - - return 0; -} - -// https://pubs.opengroup.org/onlinepubs/009695399/functions/pthread_attr_getschedparam.html -int pthread_attr_getschedparam(pthread_attr_t const* attributes, struct sched_param* p_sched_param) -{ - auto* attributes_impl = *(reinterpret_cast(attributes)); - - if (!attributes_impl || !p_sched_param) - return EINVAL; - - p_sched_param->sched_priority = attributes_impl->schedule_priority; - return 0; -} - -// https://pubs.opengroup.org/onlinepubs/009695399/functions/pthread_attr_setschedparam.html -int pthread_attr_setschedparam(pthread_attr_t* attributes, const struct sched_param* p_sched_param) -{ - auto* attributes_impl = *(reinterpret_cast(attributes)); - if (!attributes_impl || !p_sched_param) - return EINVAL; - - if (p_sched_param->sched_priority < THREAD_PRIORITY_MIN || p_sched_param->sched_priority > THREAD_PRIORITY_MAX) - return ENOTSUP; - - attributes_impl->schedule_priority = p_sched_param->sched_priority; - - dbgln_if(PTHREAD_DEBUG, "pthread_attr_setschedparam: Thread attributes at {}, detach state {}, priority {}, guard page size {}, stack size {}, stack location {}", - attributes_impl, - (PTHREAD_CREATE_JOINABLE == attributes_impl->detach_state) ? "joinable" : "detached", - attributes_impl->schedule_priority, - attributes_impl->guard_page_size, - attributes_impl->stack_size, - attributes_impl->stack_location); - - return 0; -} - -// https://pubs.opengroup.org/onlinepubs/009695399/functions/pthread_attr_getstack.html -int pthread_attr_getstack(pthread_attr_t const* attributes, void** p_stack_ptr, size_t* p_stack_size) -{ - auto* attributes_impl = *(reinterpret_cast(attributes)); - - if (!attributes_impl || !p_stack_ptr || !p_stack_size) - return EINVAL; - - *p_stack_ptr = attributes_impl->stack_location; - *p_stack_size = attributes_impl->stack_size; - - return 0; -} - -// https://pubs.opengroup.org/onlinepubs/009695399/functions/pthread_attr_setstack.html -int pthread_attr_setstack(pthread_attr_t* attributes, void* p_stack, size_t stack_size) -{ - auto* attributes_impl = *(reinterpret_cast(attributes)); - - if (!attributes_impl || !p_stack) - return EINVAL; - - // Check for required alignment on size - if (0 != (stack_size % required_stack_alignment)) - return EINVAL; - - // FIXME: Check for required alignment on pointer? - - // FIXME: "[EACCES] The stack page(s) described by stackaddr and stacksize are not both readable and writable by the thread." - // Have to check that the whole range is mapped to this process/thread? Can we defer this to create_thread? - - attributes_impl->stack_size = stack_size; - attributes_impl->stack_location = p_stack; - - dbgln_if(PTHREAD_DEBUG, "pthread_attr_setstack: Thread attributes at {}, detach state {}, priority {}, guard page size {}, stack size {}, stack location {}", - attributes_impl, - (PTHREAD_CREATE_JOINABLE == attributes_impl->detach_state) ? "joinable" : "detached", - attributes_impl->schedule_priority, - attributes_impl->guard_page_size, - attributes_impl->stack_size, - attributes_impl->stack_location); - - return 0; -} - -// https://pubs.opengroup.org/onlinepubs/009695399/functions/pthread_attr_getstacksize.html -int pthread_attr_getstacksize(pthread_attr_t const* attributes, size_t* p_stack_size) -{ - auto* attributes_impl = *(reinterpret_cast(attributes)); - - if (!attributes_impl || !p_stack_size) - return EINVAL; - - *p_stack_size = attributes_impl->stack_size; - return 0; -} - -// https://pubs.opengroup.org/onlinepubs/009695399/functions/pthread_attr_setstacksize.html -int pthread_attr_setstacksize(pthread_attr_t* attributes, size_t stack_size) -{ - auto* attributes_impl = *(reinterpret_cast(attributes)); - - if (!attributes_impl) - return EINVAL; - - if (stack_size < PTHREAD_STACK_MIN || stack_size > PTHREAD_STACK_MAX) - return EINVAL; - - attributes_impl->stack_size = stack_size; - - dbgln_if(PTHREAD_DEBUG, "pthread_attr_setstacksize: Thread attributes at {}, detach state {}, priority {}, guard page size {}, stack size {}, stack location {}", - attributes_impl, - (PTHREAD_CREATE_JOINABLE == attributes_impl->detach_state) ? "joinable" : "detached", - attributes_impl->schedule_priority, - attributes_impl->guard_page_size, - attributes_impl->stack_size, - attributes_impl->stack_location); - - return 0; -} - -// https://pubs.opengroup.org/onlinepubs/009695399/functions/pthread_attr_getscope.html -int pthread_attr_getscope([[maybe_unused]] pthread_attr_t const* attributes, [[maybe_unused]] int* contention_scope) -{ - return 0; -} - -// https://pubs.opengroup.org/onlinepubs/009695399/functions/pthread_attr_setscope.html -int pthread_attr_setscope([[maybe_unused]] pthread_attr_t* attributes, [[maybe_unused]] int contention_scope) -{ - return 0; -} - -// https://pubs.opengroup.org/onlinepubs/009695399/functions/pthread_getschedparam.html -int pthread_getschedparam(pthread_t thread, [[maybe_unused]] int* policy, struct sched_param* param) -{ - Syscall::SC_scheduler_parameters_params parameters { - .pid_or_tid = thread, - .mode = Syscall::SchedulerParametersMode::Thread, - .parameters = *param, - }; - int rc = syscall(Syscall::SC_scheduler_get_parameters, ¶meters); - if (rc == 0) - *param = parameters.parameters; - - __RETURN_PTHREAD_ERROR(rc); -} - -// https://pubs.opengroup.org/onlinepubs/009695399/functions/pthread_setschedparam.html -int pthread_setschedparam(pthread_t thread, [[maybe_unused]] int policy, struct sched_param const* param) -{ - Syscall::SC_scheduler_parameters_params parameters { - .pid_or_tid = thread, - .mode = Syscall::SchedulerParametersMode::Thread, - .parameters = *param, - }; - int rc = syscall(Syscall::SC_scheduler_set_parameters, ¶meters); - __RETURN_PTHREAD_ERROR(rc); -} - -static void pthread_cancel_signal_handler(int signal) -{ - // SIGCANCEL is a custom signal that is beyond the usual range of signal numbers. - // Let's make sure we know about it in case we still end up in here, but the signal - // number is being mangled. - VERIFY(signal == SIGCANCEL); - - // Note: We don't handle PTHREAD_CANCEL_ASYNCHRONOUS any different from PTHREAD_CANCEL_DEFERRED, - // since ASYNCHRONOUS just means that the thread can be cancelled at any time (instead of just - // at the next cancellation point) and it seems to be generally discouraged to use it at all. - pending_cancellation = true; -} - -// https://pubs.opengroup.org/onlinepubs/009695399/functions/pthread_cancel.html -// NOTE: libgcc expects this function to exist in libpthread, even if it is not implemented. -int pthread_cancel(pthread_t thread) -{ - // Set up our signal handler, which listens on SIGCANCEL and flips the cancellation indicator. - // Note that signal handlers are shared across the whole process, so we can just set that up at any time. - static bool set_up_cancel_handler = false; - - if (!set_up_cancel_handler) { - struct sigaction act = {}; - act.sa_handler = pthread_cancel_signal_handler; - sigaction(SIGCANCEL, &act, nullptr); - set_up_cancel_handler = true; - } - - return pthread_kill(thread, SIGCANCEL); -} - -// https://pubs.opengroup.org/onlinepubs/009695399/functions/pthread_testcancel.html -void pthread_testcancel(void) -{ - __pthread_maybe_cancel(); -} - -int pthread_setname_np(pthread_t thread, char const* name) -{ - if (!name) - return EFAULT; - int rc = prctl(PR_SET_THREAD_NAME, thread, name, strlen(name)); - __RETURN_PTHREAD_ERROR(rc); -} - -int pthread_getname_np(pthread_t thread, char* buffer, size_t buffer_size) -{ - int rc = prctl(PR_GET_THREAD_NAME, thread, buffer, buffer_size); - __RETURN_PTHREAD_ERROR(rc); -} - -// https://pubs.opengroup.org/onlinepubs/009695399/functions/pthread_setcancelstate.html -int pthread_setcancelstate(int state, int* oldstate) -{ - if (state != PTHREAD_CANCEL_ENABLE && state != PTHREAD_CANCEL_DISABLE) - return EINVAL; - if (oldstate) - *oldstate = s_thread_cancel_state; - s_thread_cancel_state = state; - return 0; -} - -// https://pubs.opengroup.org/onlinepubs/009695399/functions/pthread_setcanceltype.html -int pthread_setcanceltype(int type, int* oldtype) -{ - if (type != PTHREAD_CANCEL_DEFERRED && type != PTHREAD_CANCEL_ASYNCHRONOUS) - return EINVAL; - if (oldtype) - *oldtype = s_thread_cancel_type; - s_thread_cancel_type = type; - return 0; -} - -constexpr static pid_t spinlock_unlock_sentinel = 0; -// https://pubs.opengroup.org/onlinepubs/009695399/functions/pthread_spin_destroy.html -int pthread_spin_destroy(pthread_spinlock_t* lock) -{ - auto current = AK::atomic_load(&lock->m_lock); - - if (current != spinlock_unlock_sentinel) - return EBUSY; - - return 0; -} - -// https://pubs.opengroup.org/onlinepubs/009695399/functions/pthread_spin_init.html -int pthread_spin_init(pthread_spinlock_t* lock, [[maybe_unused]] int shared) -{ - lock->m_lock = spinlock_unlock_sentinel; - return 0; -} - -// https://pubs.opengroup.org/onlinepubs/009695399/functions/pthread_spin_lock.html -int pthread_spin_lock(pthread_spinlock_t* lock) -{ - auto const desired = gettid(); - while (true) { - auto current = AK::atomic_load(&lock->m_lock); - - if (current == desired) - return EDEADLK; - - if (AK::atomic_compare_exchange_strong(&lock->m_lock, current, desired, AK::MemoryOrder::memory_order_acquire)) - break; - } - - return 0; -} - -// https://pubs.opengroup.org/onlinepubs/009695399/functions/pthread_spin_trylock.html -int pthread_spin_trylock(pthread_spinlock_t* lock) -{ - // We expect the current value to be unlocked, as the specification - // states that trylock should lock only if it is not held by ANY thread. - auto current = spinlock_unlock_sentinel; - auto desired = gettid(); - - if (AK::atomic_compare_exchange_strong(&lock->m_lock, current, desired, AK::MemoryOrder::memory_order_acquire)) { - return 0; - } else { - return EBUSY; - } -} - -// https://pubs.opengroup.org/onlinepubs/009695399/functions/pthread_spin_unlock.html -int pthread_spin_unlock(pthread_spinlock_t* lock) -{ - auto current = AK::atomic_load(&lock->m_lock); - - if (gettid() != current) - return EPERM; - - AK::atomic_store(&lock->m_lock, spinlock_unlock_sentinel); - return 0; -} - -// https://pubs.opengroup.org/onlinepubs/009695399/functions/pthread_equal.html -int pthread_equal(pthread_t t1, pthread_t t2) -{ - return t1 == t2; -} - -// FIXME: Use the fancy futex mechanism above to write an rw lock. -// For the time being, let's just use a less-than-good lock to get things working. -// https://pubs.opengroup.org/onlinepubs/009695399/functions/pthread_rwlock_destroy.html -int pthread_rwlock_destroy(pthread_rwlock_t* rl) -{ - if (!rl) - return 0; - return 0; -} - -// In a very non-straightforward way, this value is composed of two 32-bit integers -// the top 32 bits are reserved for the ID of write-locking thread (if any) -// and the bottom 32 bits are: -// top 2 bits (30,31): reader wake mask, writer wake mask -// middle 16 bits: information -// bit 16: someone is waiting to write -// bit 17: locked for write -// bottom 16 bits (0..15): reader count -constexpr static u32 reader_wake_mask = 1 << 30; -constexpr static u32 writer_wake_mask = 1 << 31; -constexpr static u32 writer_locked_mask = 1 << 17; -constexpr static u32 writer_intent_mask = 1 << 16; -// https://pubs.opengroup.org/onlinepubs/009695399/functions/pthread_rwlock_init.html -int pthread_rwlock_init(pthread_rwlock_t* __restrict lockp, pthread_rwlockattr_t const* __restrict attr) -{ - // Just ignore the attributes. use defaults for now. - (void)attr; - - // No readers, no writer, not locked at all. - *lockp = 0; - return 0; -} - -// Note that this function does not care about the top 32 bits at all. -static int rwlock_rdlock_maybe_timed(u32* lockp, const struct timespec* timeout = nullptr, bool only_once = false, int value_if_timeout = -1, int value_if_okay = -2) -{ - auto current = AK::atomic_load(lockp); - for (; !only_once;) { - // First, see if this is locked for writing - // if it's not, try to add to the counter. - // If someone is waiting to write, and there is one or no other readers, let them have the lock. - if (!(current & writer_locked_mask)) { - auto count = (u16)current; - if (!(current & writer_intent_mask) || count > 1) { - ++count; - auto desired = (current & 0xffff0000u) | count; - auto did_exchange = AK::atomic_compare_exchange_strong(lockp, current, desired, AK::MemoryOrder::memory_order_acquire); - if (!did_exchange) - continue; // tough luck, try again. - return value_if_okay; - } - } - - // If no one else is waiting for the read wake bit, set it. - if (!(current & reader_wake_mask)) { - auto desired = current | reader_wake_mask; - auto did_exchange = AK::atomic_compare_exchange_strong(lockp, current, desired, AK::MemoryOrder::memory_order_acquire); - if (!did_exchange) - continue; // Something interesting happened! - - current = desired; - } - - // Seems like someone is writing (or is interested in writing and we let them have the lock) - // wait until they're done. - auto rc = futex(lockp, FUTEX_WAIT_BITSET | FUTEX_PRIVATE_FLAG, current, timeout, nullptr, reader_wake_mask); - if (rc < 0 && errno == ETIMEDOUT && timeout) { - return value_if_timeout; - } - if (rc < 0 && errno != EAGAIN) { - // Something broke. let's just bail out. - return errno; - } - errno = 0; - // Reload the 'current' value - current = AK::atomic_load(lockp); - } - return value_if_timeout; -} - -static int rwlock_wrlock_maybe_timed(pthread_rwlock_t* lockval_p, const struct timespec* timeout = nullptr, bool only_once = false, int value_if_timeout = -1, int value_if_okay = -2) -{ - u32* lockp = reinterpret_cast(lockval_p); - auto current = AK::atomic_load(lockp); - for (; !only_once;) { - // First, see if this is locked for writing, and if there are any readers. - // if not, lock it. - // If someone is waiting to write, let them have the lock. - if (!(current & writer_locked_mask) && ((u16)current) == 0) { - if (!(current & writer_intent_mask)) { - auto desired = current | writer_locked_mask | writer_intent_mask; - auto did_exchange = AK::atomic_compare_exchange_strong(lockp, current, desired, AK::MemoryOrder::memory_order_acquire); - if (!did_exchange) - continue; - - // Now that we've locked the value, it's safe to set our thread ID. - AK::atomic_store(reinterpret_cast(lockval_p) + 1, pthread_self()); - return value_if_okay; - } - } - - // That didn't work, if no one else is waiting for the write bit, set it. - if (!(current & writer_wake_mask)) { - auto desired = current | writer_wake_mask | writer_intent_mask; - auto did_exchange = AK::atomic_compare_exchange_strong(lockp, current, desired, AK::MemoryOrder::memory_order_acquire); - if (!did_exchange) - continue; // Something interesting happened! - - current = desired; - } - - // Seems like someone is writing (or is interested in writing and we let them have the lock) - // wait until they're done. - auto rc = futex(lockp, FUTEX_WAIT_BITSET | FUTEX_PRIVATE_FLAG, current, timeout, nullptr, writer_wake_mask); - if (rc < 0 && errno == ETIMEDOUT && timeout) { - return value_if_timeout; - } - if (rc < 0 && errno != EAGAIN) { - // Something broke. let's just bail out. - return errno; - } - errno = 0; - // Reload the 'current' value - current = AK::atomic_load(lockp); - } - - return value_if_timeout; -} - -// https://pubs.opengroup.org/onlinepubs/009695399/functions/pthread_rwlock_rdlock.html -int pthread_rwlock_rdlock(pthread_rwlock_t* lockp) -{ - if (!lockp) - return EINVAL; - - return rwlock_rdlock_maybe_timed(reinterpret_cast(lockp), nullptr, false, 0, 0); -} - -// https://pubs.opengroup.org/onlinepubs/009695399/functions/pthread_rwlock_timedrdlock.html -int pthread_rwlock_timedrdlock(pthread_rwlock_t* __restrict lockp, const struct timespec* __restrict timespec) -{ - if (!lockp) - return EINVAL; - - auto rc = rwlock_rdlock_maybe_timed(reinterpret_cast(lockp), timespec); - if (rc == -1) // "ok" - return 0; - if (rc == -2) // "timed out" - return 1; - return rc; -} - -// https://pubs.opengroup.org/onlinepubs/009695399/functions/pthread_rwlock_timedwrlock.html -int pthread_rwlock_timedwrlock(pthread_rwlock_t* __restrict lockp, const struct timespec* __restrict timespec) -{ - if (!lockp) - return EINVAL; - - auto rc = rwlock_wrlock_maybe_timed(lockp, timespec); - if (rc == -1) // "ok" - return 0; - if (rc == -2) // "timed out" - return 1; - return rc; -} - -// https://pubs.opengroup.org/onlinepubs/009695399/functions/pthread_rwlock_tryrdlock.html -int pthread_rwlock_tryrdlock(pthread_rwlock_t* lockp) -{ - if (!lockp) - return EINVAL; - - return rwlock_rdlock_maybe_timed(reinterpret_cast(lockp), nullptr, true, EBUSY, 0); -} - -// https://pubs.opengroup.org/onlinepubs/009695399/functions/pthread_rwlock_trywrlock.html -int pthread_rwlock_trywrlock(pthread_rwlock_t* lockp) -{ - if (!lockp) - return EINVAL; - - return rwlock_wrlock_maybe_timed(lockp, nullptr, true, EBUSY, 0); -} - -// https://pubs.opengroup.org/onlinepubs/009695399/functions/pthread_rwlock_unlock.html -int pthread_rwlock_unlock(pthread_rwlock_t* lockval_p) -{ - if (!lockval_p) - return EINVAL; - - // This is a weird API, we don't really know whether we're unlocking write or read... - auto lockp = reinterpret_cast(lockval_p); - auto current = AK::atomic_load(lockp, AK::MemoryOrder::memory_order_relaxed); - if (current & writer_locked_mask) { - // If this lock is locked for writing, its owner better be us! - auto owner_id = AK::atomic_load(reinterpret_cast(lockval_p) + 1); - auto my_id = pthread_self(); - if (owner_id != my_id) - return EINVAL; // you don't own this lock, silly. - - // Now just unlock it. - auto desired = current & ~(writer_locked_mask | writer_intent_mask); - AK::atomic_store(lockp, desired, AK::MemoryOrder::memory_order_release); - // Then wake both readers and writers, if any. - auto rc = futex(lockp, FUTEX_WAKE_BITSET | FUTEX_PRIVATE_FLAG, UINT32_MAX, nullptr, nullptr, (current & writer_wake_mask) | reader_wake_mask); - if (rc < 0) - return errno; - return 0; - } - - for (;;) { - auto count = (u16)current; - if (!count) { - // Are you crazy? this isn't even locked! - return EINVAL; - } - --count; - auto desired = (current & 0xffff0000u) | count; - auto did_exchange = AK::atomic_compare_exchange_strong(lockp, current, desired, AK::MemoryOrder::memory_order_release); - if (did_exchange) - break; - // tough luck, try again. - } - - // Finally, unlocked at last! - return 0; -} - -// https://pubs.opengroup.org/onlinepubs/009695399/functions/pthread_rwlock_wrlock.html -int pthread_rwlock_wrlock(pthread_rwlock_t* lockp) -{ - if (!lockp) - return EINVAL; - - return rwlock_wrlock_maybe_timed(lockp, nullptr, false, 0, 0); -} - -// https://pubs.opengroup.org/onlinepubs/009695399/functions/pthread_rwlockattr_destroy.html -int pthread_rwlockattr_destroy(pthread_rwlockattr_t*) -{ - return 0; -} - -// https://pubs.opengroup.org/onlinepubs/009695399/functions/pthread_rwlockattr_getpshared.html -int pthread_rwlockattr_getpshared(pthread_rwlockattr_t const* __restrict, int* __restrict) -{ - VERIFY_NOT_REACHED(); -} - -// https://pubs.opengroup.org/onlinepubs/009695399/functions/pthread_rwlockattr_init.html -int pthread_rwlockattr_init(pthread_rwlockattr_t*) -{ - VERIFY_NOT_REACHED(); -} - -// https://pubs.opengroup.org/onlinepubs/009695399/functions/pthread_rwlockattr_setpshared.html -int pthread_rwlockattr_setpshared(pthread_rwlockattr_t*, int) -{ - VERIFY_NOT_REACHED(); -} - -// https://pubs.opengroup.org/onlinepubs/009695399/functions/pthread_atfork.html -int pthread_atfork(void (*prepare)(void), void (*parent)(void), void (*child)(void)) -{ - if (prepare) - __pthread_fork_atfork_register_prepare(prepare); - if (parent) - __pthread_fork_atfork_register_parent(parent); - if (child) - __pthread_fork_atfork_register_child(child); - return 0; -} - -} // extern "C" diff --git a/Userland/Libraries/LibC/pthread.h b/Userland/Libraries/LibC/pthread.h deleted file mode 100644 index 7129fbdde43..00000000000 --- a/Userland/Libraries/LibC/pthread.h +++ /dev/null @@ -1,149 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -// Includes essentially mandated by POSIX: -// https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/pthread.h.html -#include -#include - -#include -#include -#include -#include - -__BEGIN_DECLS - -int pthread_create(pthread_t*, pthread_attr_t const*, void* (*)(void*), void*); -void pthread_exit(void*) __attribute__((noreturn)); -int pthread_kill(pthread_t, int); -void pthread_cleanup_push(void (*)(void*), void*); -void pthread_cleanup_pop(int); -int pthread_join(pthread_t, void**); -int pthread_mutex_lock(pthread_mutex_t*); -int pthread_mutex_trylock(pthread_mutex_t* mutex); -int pthread_mutex_unlock(pthread_mutex_t*); -int pthread_mutex_init(pthread_mutex_t*, pthread_mutexattr_t const*); -int pthread_mutex_destroy(pthread_mutex_t*); - -int pthread_attr_init(pthread_attr_t*); -int pthread_attr_destroy(pthread_attr_t*); - -#define PTHREAD_CREATE_JOINABLE 0 -#define PTHREAD_CREATE_DETACHED 1 - -#define PTHREAD_CANCELED ((void*)-1) - -int pthread_attr_getdetachstate(pthread_attr_t const*, int*); -int pthread_attr_setdetachstate(pthread_attr_t*, int); - -int pthread_attr_getguardsize(pthread_attr_t const*, size_t*); -int pthread_attr_setguardsize(pthread_attr_t*, size_t); - -int pthread_attr_getschedparam(pthread_attr_t const*, struct sched_param*); -int pthread_attr_setschedparam(pthread_attr_t*, const struct sched_param*); - -int pthread_attr_getstack(pthread_attr_t const*, void**, size_t*); -int pthread_attr_setstack(pthread_attr_t* attr, void*, size_t); - -int pthread_attr_getstacksize(pthread_attr_t const*, size_t*); -int pthread_attr_setstacksize(pthread_attr_t*, size_t); - -#define PTHREAD_SCOPE_SYSTEM 0 -#define PTHREAD_SCOPE_PROCESS 1 - -int pthread_attr_getscope(pthread_attr_t const*, int*); -int pthread_attr_setscope(pthread_attr_t*, int); - -int pthread_once(pthread_once_t*, void (*)(void)); -#define PTHREAD_ONCE_INIT 0 -void* pthread_getspecific(pthread_key_t key); -int pthread_setspecific(pthread_key_t key, void const* value); - -int pthread_getschedparam(pthread_t thread, int* policy, struct sched_param* param); -int pthread_setschedparam(pthread_t thread, int policy, const struct sched_param* param); - -#define PTHREAD_MUTEX_NORMAL __PTHREAD_MUTEX_NORMAL -#define PTHREAD_MUTEX_RECURSIVE __PTHREAD_MUTEX_RECURSIVE -#define PTHREAD_MUTEX_DEFAULT PTHREAD_MUTEX_NORMAL -#define PTHREAD_MUTEX_INITIALIZER __PTHREAD_MUTEX_INITIALIZER -#define PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP __PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP - -#define PTHREAD_PROCESS_PRIVATE 1 -#define PTHREAD_PROCESS_SHARED 2 - -#define PTHREAD_COND_INITIALIZER \ - { \ - 0, 0, CLOCK_MONOTONIC_COARSE \ - } - -// FIXME: Actually implement this! -#define PTHREAD_RWLOCK_INITIALIZER \ - NULL - -#define PTHREAD_KEYS_MAX 64 -#define PTHREAD_DESTRUCTOR_ITERATIONS 4 - -int pthread_key_create(pthread_key_t* key, void (*destructor)(void*)); -int pthread_key_delete(pthread_key_t key); -int pthread_cond_broadcast(pthread_cond_t*); -int pthread_cond_init(pthread_cond_t*, pthread_condattr_t const*); -int pthread_cond_signal(pthread_cond_t*); -int pthread_cond_wait(pthread_cond_t*, pthread_mutex_t*); -int pthread_condattr_init(pthread_condattr_t*); -int pthread_condattr_getclock(pthread_condattr_t* attr, clockid_t* clock); -int pthread_condattr_setclock(pthread_condattr_t*, clockid_t); -int pthread_condattr_destroy(pthread_condattr_t*); -int pthread_cond_destroy(pthread_cond_t*); -int pthread_cond_timedwait(pthread_cond_t*, pthread_mutex_t*, const struct timespec*); - -#define PTHREAD_CANCEL_ENABLE 1 -#define PTHREAD_CANCEL_DISABLE 2 - -#define PTHREAD_CANCEL_DEFERRED 1 -#define PTHREAD_CANCEL_ASYNCHRONOUS 2 - -int pthread_cancel(pthread_t); -int pthread_setcancelstate(int state, int* oldstate); -int pthread_setcanceltype(int type, int* oldtype); -void pthread_testcancel(void); - -int pthread_spin_destroy(pthread_spinlock_t*); -int pthread_spin_init(pthread_spinlock_t*, int); -int pthread_spin_lock(pthread_spinlock_t*); -int pthread_spin_trylock(pthread_spinlock_t*); -int pthread_spin_unlock(pthread_spinlock_t*); -pthread_t pthread_self(void); -int pthread_detach(pthread_t); -int pthread_equal(pthread_t, pthread_t); -int pthread_mutexattr_init(pthread_mutexattr_t*); -int pthread_mutexattr_settype(pthread_mutexattr_t*, int); -int pthread_mutexattr_gettype(pthread_mutexattr_t*, int*); -int pthread_mutexattr_destroy(pthread_mutexattr_t*); - -int pthread_setname_np(pthread_t, char const*); -int pthread_getname_np(pthread_t, char*, size_t); - -int pthread_equal(pthread_t t1, pthread_t t2); - -int pthread_rwlock_destroy(pthread_rwlock_t*); -int pthread_rwlock_init(pthread_rwlock_t* __restrict, pthread_rwlockattr_t const* __restrict); -int pthread_rwlock_rdlock(pthread_rwlock_t*); -int pthread_rwlock_timedrdlock(pthread_rwlock_t* __restrict, const struct timespec* __restrict); -int pthread_rwlock_timedwrlock(pthread_rwlock_t* __restrict, const struct timespec* __restrict); -int pthread_rwlock_tryrdlock(pthread_rwlock_t*); -int pthread_rwlock_trywrlock(pthread_rwlock_t*); -int pthread_rwlock_unlock(pthread_rwlock_t*); -int pthread_rwlock_wrlock(pthread_rwlock_t*); -int pthread_rwlockattr_destroy(pthread_rwlockattr_t*); -int pthread_rwlockattr_getpshared(pthread_rwlockattr_t const* __restrict, int* __restrict); -int pthread_rwlockattr_init(pthread_rwlockattr_t*); -int pthread_rwlockattr_setpshared(pthread_rwlockattr_t*, int); - -int pthread_atfork(void (*prepare)(void), void (*parent)(void), void (*child)(void)); - -__END_DECLS diff --git a/Userland/Libraries/LibC/pthread_cond.cpp b/Userland/Libraries/LibC/pthread_cond.cpp deleted file mode 100644 index 20070fd42ab..00000000000 --- a/Userland/Libraries/LibC/pthread_cond.cpp +++ /dev/null @@ -1,161 +0,0 @@ -/* - * Copyright (c) 2019, Andreas Kling - * Copyright (c) 2021, Sergey Bugaev - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -// Condition variable attributes. - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_condattr_init.html -int pthread_condattr_init(pthread_condattr_t* attr) -{ - attr->clockid = CLOCK_MONOTONIC_COARSE; - return 0; -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_condattr_destroy.html -int pthread_condattr_destroy(pthread_condattr_t*) -{ - return 0; -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_condattr_getclock.html -int pthread_condattr_getclock(pthread_condattr_t* attr, clockid_t* clock) -{ - *clock = attr->clockid; - return 0; -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_condattr_setclock.html -int pthread_condattr_setclock(pthread_condattr_t* attr, clockid_t clock) -{ - switch (clock) { - case CLOCK_REALTIME: - case CLOCK_REALTIME_COARSE: - case CLOCK_MONOTONIC: - case CLOCK_MONOTONIC_COARSE: - case CLOCK_MONOTONIC_RAW: - attr->clockid = clock; - return 0; - default: - return EINVAL; - } -} - -// Condition variables. - -// cond->value is the generation number (number of times the variable has been -// signaled) multiplied by INCREMENT, or'ed with the NEED_TO_WAKE flags. It's -// done this way instead of putting the flags into the high bits because the -// sequence number can easily overflow, which is completely fine but should not -// cause it to corrupt the flags. -static constexpr u32 NEED_TO_WAKE_ONE = 1; -static constexpr u32 NEED_TO_WAKE_ALL = 2; -static constexpr u32 INCREMENT = 4; - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_cond_init.html -int pthread_cond_init(pthread_cond_t* cond, pthread_condattr_t const* attr) -{ - cond->mutex = nullptr; - cond->value = 0; - cond->clockid = attr ? attr->clockid : CLOCK_REALTIME_COARSE; - return 0; -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_cond_destroy.html -int pthread_cond_destroy(pthread_cond_t*) -{ - return 0; -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_cond_wait.html -int pthread_cond_wait(pthread_cond_t* cond, pthread_mutex_t* mutex) -{ - return pthread_cond_timedwait(cond, mutex, nullptr); -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_cond_timedwait.html -int pthread_cond_timedwait(pthread_cond_t* cond, pthread_mutex_t* mutex, const struct timespec* abstime) -{ - __pthread_maybe_cancel(); - - // Save the mutex this condition variable is associated with. We don't (yet) - // support changing this mutex once set. - pthread_mutex_t* old_mutex = AK::atomic_exchange(&cond->mutex, mutex, AK::memory_order_relaxed); - if (old_mutex && old_mutex != mutex) - TODO(); - - // Fetch the current value, and record that we're about to wait. Fetching - // the current value has to be done while we hold the mutex, because the - // value might change as soon as we unlock it. - u32 value = AK::atomic_fetch_or(&cond->value, NEED_TO_WAKE_ONE | NEED_TO_WAKE_ALL, AK::memory_order_release) | NEED_TO_WAKE_ONE | NEED_TO_WAKE_ALL; - pthread_mutex_unlock(mutex); - int rc = futex_wait(&cond->value, value, abstime, cond->clockid, false); - if (rc < 0 && errno != EAGAIN) - return errno; - - // We might have been re-queued onto the mutex while we were sleeping. Take - // the pessimistic locking path. - __pthread_mutex_lock_pessimistic_np(mutex); - return 0; -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_cond_signal.html -int pthread_cond_signal(pthread_cond_t* cond) -{ - // Increment the generation. - u32 value = AK::atomic_fetch_add(&cond->value, INCREMENT, AK::memory_order_relaxed); - // Fast path: nobody's waiting (or at least, nobody has to be woken). - if (!(value & NEED_TO_WAKE_ONE)) [[likely]] - return 0; - - // Wake someone, and clear the NEED_TO_WAKE_ONE flag if there was nobody for - // us to wake, to take the fast path the next time. Since we only learn - // whether there has been somebody waiting or not after we have tried to - // wake them, it would make sense for us to clear the flag after trying to - // wake someone up and seeing there was nobody waiting; but that would race - // with somebody else setting the flag. Therefore, we do it like this: - // attempt to clear the flag first... - value = AK::atomic_fetch_and(&cond->value, ~NEED_TO_WAKE_ONE, AK::memory_order_relaxed); - // ...check if it was already cleared by someone else... - if (!(value & NEED_TO_WAKE_ONE)) [[likely]] - return 0; - // ...try to wake someone... - int rc = futex_wake(&cond->value, 1, false); - VERIFY(rc >= 0); - // ...and if we have woken someone, put the flag back. - if (rc > 0) - AK::atomic_fetch_or(&cond->value, NEED_TO_WAKE_ONE, AK::memory_order_relaxed); - - return 0; -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_cond_broadcast.html -int pthread_cond_broadcast(pthread_cond_t* cond) -{ - // Increment the generation. - u32 value = AK::atomic_fetch_add(&cond->value, INCREMENT, AK::memory_order_relaxed); - // Fast path: nobody's waiting (or at least, nobody has to be woken). - if (!(value & NEED_TO_WAKE_ALL)) [[likely]] - return 0; - - AK::atomic_fetch_and(&cond->value, ~(NEED_TO_WAKE_ONE | NEED_TO_WAKE_ALL), AK::memory_order_acquire); - - pthread_mutex_t* mutex = AK::atomic_load(&cond->mutex, AK::memory_order_relaxed); - VERIFY(mutex); - - int rc = futex(&cond->value, FUTEX_REQUEUE | FUTEX_PRIVATE_FLAG, -1, nullptr, &mutex->lock, INT_MAX); - VERIFY(rc >= 0); - return 0; -} diff --git a/Userland/Libraries/LibC/pthread_integration.cpp b/Userland/Libraries/LibC/pthread_integration.cpp deleted file mode 100644 index 1fc96bf34b7..00000000000 --- a/Userland/Libraries/LibC/pthread_integration.cpp +++ /dev/null @@ -1,204 +0,0 @@ -/* - * Copyright (c) 2021, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace { - -// Most programs don't need this, no need to incur an extra mutex lock/unlock on them -static Atomic g_did_touch_atfork { false }; -static pthread_mutex_t g_atfork_list_mutex __PTHREAD_MUTEX_INITIALIZER; -static NeverDestroyed> g_atfork_prepare_list; -static NeverDestroyed> g_atfork_child_list; -static NeverDestroyed> g_atfork_parent_list; - -} - -extern "C" { -void __pthread_fork_prepare(void) -{ - if (!g_did_touch_atfork.load()) - return; - - pthread_mutex_lock(&g_atfork_list_mutex); - for (auto entry : g_atfork_prepare_list.get()) - entry(); - pthread_mutex_unlock(&g_atfork_list_mutex); -} - -void __pthread_fork_child(void) -{ - if (!g_did_touch_atfork.load()) - return; - - pthread_mutex_lock(&g_atfork_list_mutex); - for (auto entry : g_atfork_child_list.get()) - entry(); - pthread_mutex_unlock(&g_atfork_list_mutex); -} - -void __pthread_fork_parent(void) -{ - if (!g_did_touch_atfork.load()) - return; - - pthread_mutex_lock(&g_atfork_list_mutex); - for (auto entry : g_atfork_parent_list.get()) - entry(); - pthread_mutex_unlock(&g_atfork_list_mutex); -} - -void __pthread_fork_atfork_register_prepare(void (*func)(void)) -{ - g_did_touch_atfork.store(true); - - pthread_mutex_lock(&g_atfork_list_mutex); - g_atfork_prepare_list->append(func); - pthread_mutex_unlock(&g_atfork_list_mutex); -} - -void __pthread_fork_atfork_register_parent(void (*func)(void)) -{ - g_did_touch_atfork.store(true); - - pthread_mutex_lock(&g_atfork_list_mutex); - g_atfork_parent_list->append(func); - pthread_mutex_unlock(&g_atfork_list_mutex); -} - -void __pthread_fork_atfork_register_child(void (*func)(void)) -{ - g_did_touch_atfork.store(true); - - pthread_mutex_lock(&g_atfork_list_mutex); - g_atfork_child_list->append(func); - pthread_mutex_unlock(&g_atfork_list_mutex); -} - -// https://pubs.opengroup.org/onlinepubs/009695399/functions/pthread_self.html -int pthread_self() -{ - return gettid(); -} - -static constexpr u32 MUTEX_UNLOCKED = 0; -static constexpr u32 MUTEX_LOCKED_NO_NEED_TO_WAKE = 1; -static constexpr u32 MUTEX_LOCKED_NEED_TO_WAKE = 2; - -// https://pubs.opengroup.org/onlinepubs/009695399/functions/pthread_mutex_init.html -int pthread_mutex_init(pthread_mutex_t* mutex, pthread_mutexattr_t const* attributes) -{ - mutex->lock = 0; - mutex->owner = 0; - mutex->level = 0; - mutex->type = attributes ? attributes->type : __PTHREAD_MUTEX_NORMAL; - return 0; -} - -// https://pubs.opengroup.org/onlinepubs/009695399/functions/pthread_mutex_trylock.html -int pthread_mutex_trylock(pthread_mutex_t* mutex) -{ - u32 expected = MUTEX_UNLOCKED; - bool exchanged = AK::atomic_compare_exchange_strong(&mutex->lock, expected, MUTEX_LOCKED_NO_NEED_TO_WAKE, AK::memory_order_acquire); - - if (exchanged) [[likely]] { - if (mutex->type == __PTHREAD_MUTEX_RECURSIVE) - AK::atomic_store(&mutex->owner, pthread_self(), AK::memory_order_relaxed); - mutex->level = 0; - return 0; - } else if (mutex->type == __PTHREAD_MUTEX_RECURSIVE) { - pthread_t owner = AK::atomic_load(&mutex->owner, AK::memory_order_relaxed); - if (owner == pthread_self()) { - // We already own the mutex! - mutex->level++; - return 0; - } - } - return EBUSY; -} - -// https://pubs.opengroup.org/onlinepubs/009695399/functions/pthread_mutex_lock.html -int pthread_mutex_lock(pthread_mutex_t* mutex) -{ - // Fast path: attempt to claim the mutex without waiting. - u32 value = MUTEX_UNLOCKED; - bool exchanged = AK::atomic_compare_exchange_strong(&mutex->lock, value, MUTEX_LOCKED_NO_NEED_TO_WAKE, AK::memory_order_acquire); - if (exchanged) [[likely]] { - if (mutex->type == __PTHREAD_MUTEX_RECURSIVE) - AK::atomic_store(&mutex->owner, pthread_self(), AK::memory_order_relaxed); - mutex->level = 0; - return 0; - } else if (mutex->type == __PTHREAD_MUTEX_RECURSIVE) { - pthread_t owner = AK::atomic_load(&mutex->owner, AK::memory_order_relaxed); - if (owner == pthread_self()) { - // We already own the mutex! - mutex->level++; - return 0; - } - } - - // Slow path: wait, record the fact that we're going to wait, and always - // remember to wake the next thread up once we release the mutex. - if (value != MUTEX_LOCKED_NEED_TO_WAKE) - value = AK::atomic_exchange(&mutex->lock, MUTEX_LOCKED_NEED_TO_WAKE, AK::memory_order_acquire); - - while (value != MUTEX_UNLOCKED) { - futex_wait(&mutex->lock, MUTEX_LOCKED_NEED_TO_WAKE, nullptr, 0, false); - value = AK::atomic_exchange(&mutex->lock, MUTEX_LOCKED_NEED_TO_WAKE, AK::memory_order_acquire); - } - - if (mutex->type == __PTHREAD_MUTEX_RECURSIVE) - AK::atomic_store(&mutex->owner, pthread_self(), AK::memory_order_relaxed); - mutex->level = 0; - return 0; -} - -int __pthread_mutex_lock_pessimistic_np(pthread_mutex_t* mutex) -{ - // Same as pthread_mutex_lock(), but always set MUTEX_LOCKED_NEED_TO_WAKE, - // and also don't bother checking for already owning the mutex recursively, - // because we know we don't. Used in the condition variable implementation. - u32 value = AK::atomic_exchange(&mutex->lock, MUTEX_LOCKED_NEED_TO_WAKE, AK::memory_order_acquire); - while (value != MUTEX_UNLOCKED) { - futex_wait(&mutex->lock, value, nullptr, 0, false); - value = AK::atomic_exchange(&mutex->lock, MUTEX_LOCKED_NEED_TO_WAKE, AK::memory_order_acquire); - } - - if (mutex->type == __PTHREAD_MUTEX_RECURSIVE) - AK::atomic_store(&mutex->owner, pthread_self(), AK::memory_order_relaxed); - mutex->level = 0; - return 0; -} - -// https://pubs.opengroup.org/onlinepubs/009695399/functions/pthread_mutex_unlock.html -int pthread_mutex_unlock(pthread_mutex_t* mutex) -{ - if (mutex->type == __PTHREAD_MUTEX_RECURSIVE && mutex->level > 0) { - mutex->level--; - return 0; - } - - if (mutex->type == __PTHREAD_MUTEX_RECURSIVE) - AK::atomic_store(&mutex->owner, 0, AK::memory_order_relaxed); - - u32 value = AK::atomic_exchange(&mutex->lock, MUTEX_UNLOCKED, AK::memory_order_release); - if (value == MUTEX_LOCKED_NEED_TO_WAKE) [[unlikely]] { - int rc = futex_wake(&mutex->lock, 1, false); - VERIFY(rc >= 0); - } - - return 0; -} -} diff --git a/Userland/Libraries/LibC/pthread_once.cpp b/Userland/Libraries/LibC/pthread_once.cpp deleted file mode 100644 index f13f4eaa7a1..00000000000 --- a/Userland/Libraries/LibC/pthread_once.cpp +++ /dev/null @@ -1,88 +0,0 @@ -/* - * Copyright (c) 2020, Sergey Bugaev - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include - -enum State : i32 { - INITIAL = PTHREAD_ONCE_INIT, - DONE, - PERFORMING_NO_WAITERS, - PERFORMING_WITH_WAITERS, -}; - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_once.html -int pthread_once(pthread_once_t* self, void (*callback)(void)) -{ - auto& state = reinterpret_cast&>(*self); - - // See what the current state is, and at the same time grab the lock if we - // got here first. We need acquire ordering here because if we see - // State::DONE, everything we do after that should "happen after" everything - // the other thread has done before writing the State::DONE. - State state2 = State::INITIAL; - bool have_exchanged = state.compare_exchange_strong( - state2, State::PERFORMING_NO_WAITERS, AK::memory_order_acquire); - - if (have_exchanged) { - // We observed State::INITIAL and we've changed it to - // State::PERFORMING_NO_WAITERS, so it's us who should perform the - // operation. - callback(); - // Now, record that we're done. - state2 = state.exchange(State::DONE, AK::memory_order_release); - switch (state2) { - case State::INITIAL: - case State::DONE: - VERIFY_NOT_REACHED(); - case State::PERFORMING_NO_WAITERS: - // The fast path: there's no contention, so we don't have to wake - // anyone. - break; - case State::PERFORMING_WITH_WAITERS: - futex_wake(self, INT_MAX, false); - break; - } - - return 0; - } - - // We did not get there first. Let's see if we have to wait. - // state2 contains the observed state. - while (true) { - switch (state2) { - case State::INITIAL: - VERIFY_NOT_REACHED(); - case State::DONE: - // Awesome, nothing to do then. - return 0; - case State::PERFORMING_NO_WAITERS: - // We're going to wait for it, but we have to record that we're - // waiting and the other thread should wake us up. We need acquire - // ordering here for the same reason as above. - have_exchanged = state.compare_exchange_strong( - state2, State::PERFORMING_WITH_WAITERS, AK::memory_order_acquire); - if (!have_exchanged) { - // Something has changed already, reevaluate without waiting. - continue; - } - state2 = State::PERFORMING_WITH_WAITERS; - [[fallthrough]]; - case State::PERFORMING_WITH_WAITERS: - // Let's wait for it. - futex_wait(self, state2, nullptr, 0, false); - // We have been woken up, but that might have been due to a signal - // or something, so we have to reevaluate. We need acquire ordering - // here for the same reason as above. Hopefully we'll just see - // State::DONE this time, but who knows. - state2 = state.load(AK::memory_order_acquire); - continue; - } - } -} diff --git a/Userland/Libraries/LibC/pthread_tls.cpp b/Userland/Libraries/LibC/pthread_tls.cpp deleted file mode 100644 index 355ea4497e7..00000000000 --- a/Userland/Libraries/LibC/pthread_tls.cpp +++ /dev/null @@ -1,107 +0,0 @@ -/* - * Copyright (c) 2021, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include - -#ifndef _DYNAMIC_LOADER -extern "C" { - -static constexpr int max_keys = PTHREAD_KEYS_MAX; - -struct KeyTable { - KeyDestructor destructors[max_keys] { nullptr }; - int next { 0 }; - pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; -}; - -struct SpecificTable { - void* values[max_keys] { nullptr }; -}; - -static KeyTable s_keys; - -__thread SpecificTable t_specifics; - -// https://pubs.opengroup.org/onlinepubs/009695399/functions/pthread_key_create.html -int pthread_key_create(pthread_key_t* key, KeyDestructor destructor) -{ - int ret = 0; - pthread_mutex_lock(&s_keys.mutex); - if (s_keys.next >= max_keys) { - ret = EAGAIN; - } else { - *key = s_keys.next++; - s_keys.destructors[*key] = destructor; - ret = 0; - } - pthread_mutex_unlock(&s_keys.mutex); - return ret; -} - -// https://pubs.opengroup.org/onlinepubs/009695399/functions/pthread_key_delete.html -int pthread_key_delete(pthread_key_t key) -{ - if (key < 0 || key >= max_keys) - return EINVAL; - pthread_mutex_lock(&s_keys.mutex); - s_keys.destructors[key] = nullptr; - pthread_mutex_unlock(&s_keys.mutex); - return 0; -} - -// https://pubs.opengroup.org/onlinepubs/009695399/functions/pthread_getspecific.html -void* pthread_getspecific(pthread_key_t key) -{ - if (key < 0) - return nullptr; - if (key >= max_keys) - return nullptr; - return t_specifics.values[key]; -} - -// https://pubs.opengroup.org/onlinepubs/009695399/functions/pthread_setspecific.html -int pthread_setspecific(pthread_key_t key, void const* value) -{ - if (key < 0) - return EINVAL; - if (key >= max_keys) - return EINVAL; - - t_specifics.values[key] = const_cast(value); - return 0; -} - -void __pthread_key_destroy_for_current_thread() -{ - // This function will either be called during exit_thread, for a pthread, or - // during global program shutdown for the main thread. - - pthread_mutex_lock(&s_keys.mutex); - size_t num_used_keys = s_keys.next; - - // Dr. POSIX accounts for weird key destructors setting their own key again. - // Or even, setting other unrelated keys? Odd, but whatever the Doc says goes. - - for (size_t destruct_iteration = 0; destruct_iteration < PTHREAD_DESTRUCTOR_ITERATIONS; ++destruct_iteration) { - bool any_nonnull_destructors = false; - for (size_t key_index = 0; key_index < num_used_keys; ++key_index) { - void* value = exchange(t_specifics.values[key_index], nullptr); - - if (value && s_keys.destructors[key_index]) { - any_nonnull_destructors = true; - (*s_keys.destructors[key_index])(value); - } - } - if (!any_nonnull_destructors) - break; - } - pthread_mutex_unlock(&s_keys.mutex); -} -} -#endif diff --git a/Userland/Libraries/LibC/pty.cpp b/Userland/Libraries/LibC/pty.cpp deleted file mode 100644 index 01f9aeb5efe..00000000000 --- a/Userland/Libraries/LibC/pty.cpp +++ /dev/null @@ -1,125 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * Copyright (c) 2021, Gunnar Beutner - * Copyright (c) 2021, Linus Groh - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include -#include -#include - -int openpty(int* amaster, int* aslave, char* name, const struct termios* termp, const struct winsize* winp) -{ - *amaster = posix_openpt(O_RDWR); - if (*amaster < 0) { - return -1; - } - if (grantpt(*amaster) < 0) { - int error = errno; - close(*amaster); - errno = error; - return -1; - } - if (unlockpt(*amaster) < 0) { - int error = errno; - close(*amaster); - errno = error; - return -1; - } - - char tty_name[32]; - int rc = ptsname_r(*amaster, tty_name, sizeof(tty_name)); - if (rc < 0) { - int error = errno; - close(*amaster); - errno = error; - return -1; - } - - if (name) { - /* The spec doesn't say how large name has to be. Good luck. */ - [[maybe_unused]] auto rc = strlcpy(name, tty_name, 128); - } - - *aslave = open(tty_name, O_RDWR | O_NOCTTY); - if (*aslave < 0) { - int error = errno; - close(*amaster); - errno = error; - return -1; - } - if (termp) { - if (tcsetattr(*aslave, TCSAFLUSH, termp) == -1) { - int error = errno; - close(*aslave); - close(*amaster); - errno = error; - return -1; - } - } - if (winp) { - if (ioctl(*aslave, TIOCGWINSZ, winp) == -1) { - int error = errno; - close(*aslave); - close(*amaster); - errno = error; - return -1; - } - } - return 0; -} - -pid_t forkpty(int* amaster, char* name, const struct termios* termp, const struct winsize* winp) -{ - int master; - int slave; - if (openpty(&master, &slave, name, termp, winp) < 0) - return -1; - pid_t pid = fork(); - if (pid < 0) { - close(master); - close(slave); - return -1; - } - *amaster = master; - if (pid == 0) { - close(master); - if (login_tty(slave) < 0) - _exit(1); - return 0; - } - close(slave); - return pid; -} - -int login_tty(int fd) -{ - setsid(); - - close(0); - close(1); - close(2); - - int rc = dup2(fd, 0); - if (rc < 0) - return rc; - rc = dup2(fd, 1); - if (rc < 0) - return -1; - rc = dup2(fd, 2); - if (rc < 0) - return rc; - rc = close(fd); - if (rc < 0) - return rc; - rc = ioctl(0, TIOCSCTTY); - if (rc < 0) - return rc; - return 0; -} diff --git a/Userland/Libraries/LibC/pty.h b/Userland/Libraries/LibC/pty.h deleted file mode 100644 index e2068f1105e..00000000000 --- a/Userland/Libraries/LibC/pty.h +++ /dev/null @@ -1,18 +0,0 @@ -/* - * Copyright (c) 2021, Gunnar Beutner - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include - -__BEGIN_DECLS - -int openpty(int* amaster, int* aslave, char* name, const struct termios* termp, const struct winsize* winp); -pid_t forkpty(int* amaster, char* name, const struct termios* termp, const struct winsize* winp); -int login_tty(int fd); - -__END_DECLS diff --git a/Userland/Libraries/LibC/pwd.cpp b/Userland/Libraries/LibC/pwd.cpp deleted file mode 100644 index 8cef76ce3bc..00000000000 --- a/Userland/Libraries/LibC/pwd.cpp +++ /dev/null @@ -1,207 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -extern "C" { - -static FILE* s_stream = nullptr; -static unsigned s_line_number = 0; - -void setpwent() -{ - s_line_number = 0; - if (s_stream) { - rewind(s_stream); - } else { - s_stream = fopen("/etc/passwd", "r"); - if (!s_stream) { - perror("open /etc/passwd"); - } - } -} - -void endpwent() -{ - s_line_number = 0; - if (s_stream) { - fclose(s_stream); - s_stream = nullptr; - } -} - -struct passwd* getpwuid(uid_t uid) -{ - setpwent(); - ScopeGuard guard = [] { endpwent(); }; - while (auto* pw = getpwent()) { - if (pw->pw_uid == uid) - return pw; - } - return nullptr; -} - -struct passwd* getpwnam(char const* name) -{ - setpwent(); - ScopeGuard guard = [] { endpwent(); }; - while (auto* pw = getpwent()) { - if (!strcmp(pw->pw_name, name)) - return pw; - } - return nullptr; -} - -static bool parse_pwddb_entry(char* raw_line, struct passwd& passwd_entry) -{ - size_t line_length = strlen(raw_line); - for (size_t i = 0; i < line_length; ++i) { - auto& ch = raw_line[i]; - if (ch == '\r' || ch == '\n') - line_length = i; - if (ch == ':' || ch == '\r' || ch == '\n') - ch = '\0'; - } - auto line = StringView { raw_line, line_length }; - auto parts = line.split_view('\0', SplitBehavior::KeepEmpty); - if (parts.size() != 7) { - dbgln("getpwent(): Malformed entry on line {}", s_line_number); - return false; - } - - auto& name = parts[0]; - auto& passwd = parts[1]; - auto& uid_string = parts[2]; - auto& gid_string = parts[3]; - auto& gecos = parts[4]; - auto& dir = parts[5]; - auto& shell = parts[6]; - - auto uid = uid_string.to_number(); - if (!uid.has_value()) { - dbgln("getpwent(): Malformed UID on line {}", s_line_number); - return false; - } - auto gid = gid_string.to_number(); - if (!gid.has_value()) { - dbgln("getpwent(): Malformed GID on line {}", s_line_number); - return false; - } - - passwd_entry.pw_name = const_cast(name.characters_without_null_termination()); - passwd_entry.pw_passwd = const_cast(passwd.characters_without_null_termination()); - passwd_entry.pw_uid = uid.value(); - passwd_entry.pw_gid = gid.value(); - passwd_entry.pw_gecos = const_cast(gecos.characters_without_null_termination()); - passwd_entry.pw_dir = const_cast(dir.characters_without_null_termination()); - passwd_entry.pw_shell = const_cast(shell.characters_without_null_termination()); - - return true; -} - -struct passwd* getpwent() -{ - static struct passwd passwd_entry; - static char buffer[1024]; - struct passwd* result; - if (getpwent_r(&passwd_entry, buffer, sizeof(buffer), &result) < 0) - return nullptr; - return result; -} - -int getpwent_r(struct passwd* passwd_buf, char* buffer, size_t buffer_size, struct passwd** passwd_entry_ptr) -{ - if (!s_stream) - setpwent(); - - while (true) { - if (!s_stream || feof(s_stream)) { - *passwd_entry_ptr = nullptr; - return ENOENT; - } - - if (ferror(s_stream)) { - *passwd_entry_ptr = nullptr; - return ferror(s_stream); - } - - ++s_line_number; - char* s = fgets(buffer, buffer_size, s_stream); - - if ((!s || !s[0]) && feof(s_stream)) { - *passwd_entry_ptr = nullptr; - return ENOENT; - } - - if (strlen(s) == buffer_size - 1) { - *passwd_entry_ptr = nullptr; - return ERANGE; - } - - if (parse_pwddb_entry(buffer, *passwd_buf)) { - *passwd_entry_ptr = passwd_buf; - return 0; - } - } -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/getpwnam.html -int getpwnam_r(char const* name, struct passwd* pwd, char* buffer, size_t bufsize, struct passwd** result) -{ - setpwent(); - for (;;) { - if (auto rc = getpwent_r(pwd, buffer, bufsize, result); rc != 0) - return rc; - if (strcmp(pwd->pw_name, name) == 0) - return 0; - } -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/getpwuid.html -int getpwuid_r(uid_t uid, struct passwd* pwd, char* buffer, size_t bufsize, struct passwd** result) -{ - setpwent(); - for (;;) { - if (auto rc = getpwent_r(pwd, buffer, bufsize, result); rc != 0) - return rc; - if (pwd->pw_uid == uid) - return 0; - } -} - -int putpwent(const struct passwd* p, FILE* stream) -{ - if (!p || !stream || !p->pw_passwd || !p->pw_name || !p->pw_dir || !p->pw_gecos || !p->pw_shell) { - errno = EINVAL; - return -1; - } - - auto is_valid_field = [](char const* str) { - return str && !strpbrk(str, ":\n"); - }; - - if (!is_valid_field(p->pw_name) || !is_valid_field(p->pw_dir) || !is_valid_field(p->pw_gecos) || !is_valid_field(p->pw_shell)) { - errno = EINVAL; - return -1; - } - - int nwritten = fprintf(stream, "%s:%s:%u:%u:%s,,,:%s:%s\n", p->pw_name, p->pw_passwd, p->pw_uid, p->pw_gid, p->pw_gecos, p->pw_dir, p->pw_shell); - if (!nwritten || nwritten < 0) { - errno = ferror(stream); - return -1; - } - - return 0; -} -} diff --git a/Userland/Libraries/LibC/pwd.h b/Userland/Libraries/LibC/pwd.h deleted file mode 100644 index 7f0c1110018..00000000000 --- a/Userland/Libraries/LibC/pwd.h +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include - -__BEGIN_DECLS - -struct passwd { - char* pw_name; - char* pw_passwd; - uid_t pw_uid; - gid_t pw_gid; - char* pw_gecos; - char* pw_dir; - char* pw_shell; -}; - -struct passwd* getpwent(void); -int getpwent_r(struct passwd*, char*, size_t, struct passwd**); -void setpwent(void); -void endpwent(void); -struct passwd* getpwnam(char const* name); -struct passwd* getpwuid(uid_t); -int putpwent(const struct passwd* p, FILE* stream); - -int getpwnam_r(char const* name, struct passwd* pwd, char* buf, size_t buflen, struct passwd** result); -int getpwuid_r(uid_t, struct passwd* pwd, char* buf, size_t buflen, struct passwd** result); - -__END_DECLS diff --git a/Userland/Libraries/LibC/qsort.cpp b/Userland/Libraries/LibC/qsort.cpp deleted file mode 100644 index 1bd437a93e9..00000000000 --- a/Userland/Libraries/LibC/qsort.cpp +++ /dev/null @@ -1,83 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include - -class SizedObject { -public: - SizedObject() = delete; - SizedObject(void* data, size_t size) - : m_data(data) - , m_size(size) - { - } - void* data() const { return m_data; } - size_t size() const { return m_size; } - -private: - void* m_data; - size_t m_size; -}; - -namespace AK { - -template<> -inline void swap(SizedObject const& a, SizedObject const& b) -{ - VERIFY(a.size() == b.size()); - size_t const size = a.size(); - auto const a_data = reinterpret_cast(a.data()); - auto const b_data = reinterpret_cast(b.data()); - for (auto i = 0u; i < size; ++i) { - swap(a_data[i], b_data[i]); - } -} - -} - -class SizedObjectSlice { -public: - SizedObjectSlice() = delete; - SizedObjectSlice(void* data, size_t element_size) - : m_data(data) - , m_element_size(element_size) - { - } - SizedObject const operator[](size_t index) - { - return { static_cast(m_data) + index * m_element_size, m_element_size }; - } - -private: - void* m_data; - size_t m_element_size; -}; - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/qsort.html -void qsort(void* bot, size_t nmemb, size_t size, int (*compar)(void const*, void const*)) -{ - if (nmemb <= 1) { - return; - } - - SizedObjectSlice slice { bot, size }; - - AK::dual_pivot_quick_sort(slice, 0, nmemb - 1, [=](SizedObject const& a, SizedObject const& b) { return compar(a.data(), b.data()) < 0; }); -} - -void qsort_r(void* bot, size_t nmemb, size_t size, int (*compar)(void const*, void const*, void*), void* arg) -{ - if (nmemb <= 1) { - return; - } - - SizedObjectSlice slice { bot, size }; - - AK::dual_pivot_quick_sort(slice, 0, nmemb - 1, [=](SizedObject const& a, SizedObject const& b) { return compar(a.data(), b.data(), arg) < 0; }); -} diff --git a/Userland/Libraries/LibC/regex.cpp b/Userland/Libraries/LibC/regex.cpp deleted file mode 100644 index 8bc2d3f92d5..00000000000 --- a/Userland/Libraries/LibC/regex.cpp +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright (c) 2020, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include - -static void* s_libregex; -static pthread_mutex_t s_libregex_lock; - -static int (*s_regcomp)(regex_t*, char const*, int); -static int (*s_regexec)(regex_t const*, char const*, size_t, regmatch_t[], int); -static size_t (*s_regerror)(int, regex_t const*, char*, size_t); -static void (*s_regfree)(regex_t*); - -static void ensure_libregex() -{ - pthread_mutex_lock(&s_libregex_lock); - if (!s_libregex) { - s_libregex = dlopen("libregex.so", RTLD_NOW); - VERIFY(s_libregex); - - s_regcomp = reinterpret_cast(dlsym(s_libregex, "regcomp")); - VERIFY(s_regcomp); - - s_regexec = reinterpret_cast(dlsym(s_libregex, "regexec")); - VERIFY(s_regexec); - - s_regerror = reinterpret_cast(dlsym(s_libregex, "regerror")); - VERIFY(s_regerror); - - s_regfree = reinterpret_cast(dlsym(s_libregex, "regfree")); - VERIFY(s_regerror); - } - pthread_mutex_unlock(&s_libregex_lock); -} - -extern "C" { - -int __attribute__((weak)) regcomp(regex_t* reg, char const* pattern, int cflags) -{ - ensure_libregex(); - return s_regcomp(reg, pattern, cflags); -} - -int __attribute__((weak)) regexec(regex_t const* reg, char const* string, size_t nmatch, regmatch_t pmatch[], int eflags) -{ - ensure_libregex(); - return s_regexec(reg, string, nmatch, pmatch, eflags); -} - -size_t __attribute__((weak)) regerror(int errcode, regex_t const* reg, char* errbuf, size_t errbuf_size) -{ - ensure_libregex(); - return s_regerror(errcode, reg, errbuf, errbuf_size); -} - -void __attribute__((weak)) regfree(regex_t* reg) -{ - ensure_libregex(); - return s_regfree(reg); -} -} diff --git a/Userland/Libraries/LibC/regex.h b/Userland/Libraries/LibC/regex.h deleted file mode 100644 index e408b52bb49..00000000000 --- a/Userland/Libraries/LibC/regex.h +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright (c) 2020, Emanuel Sprung - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include - -__BEGIN_DECLS - -typedef ssize_t regoff_t; - -typedef struct { - void* __data; - // Number of capture groups, Dr.POSIX requires this. - size_t re_nsub; -} regex_t; - -enum ReError { - REG_NOERR = __Regex_NoError, - REG_BADPAT = __Regex_InvalidPattern, // Invalid regular expression. - REG_ECOLLATE = __Regex_InvalidCollationElement, // Invalid collating element referenced. - REG_ECTYPE = __Regex_InvalidCharacterClass, // Invalid character class type referenced. - REG_EESCAPE = __Regex_InvalidTrailingEscape, // Trailing \ in pattern. - REG_ESUBREG = __Regex_InvalidNumber, // Number in \digit invalid or in error. - REG_EBRACK = __Regex_MismatchingBracket, // [ ] imbalance. - REG_EPAREN = __Regex_MismatchingParen, // \( \) or ( ) imbalance. - REG_EBRACE = __Regex_MismatchingBrace, // \{ \} imbalance. - REG_BADBR = __Regex_InvalidBraceContent, // Content of \{ \} invalid: not a number, number too large, more than two numbers, first larger than second. - REG_ERANGE = __Regex_InvalidRange, // Invalid endpoint in range expression. - REG_BADRPT = __Regex_InvalidRepetitionMarker, // ?, * or + not preceded by valid regular expression. - REG_EMPTY_EXPR = __Regex_EmptySubExpression, // Empty expression - REG_ENOSYS, // The implementation does not support the function. - REG_ESPACE, // Out of memory. - REG_NOMATCH, // regexec() failed to match. -}; - -typedef struct { - regoff_t rm_so; // byte offset from start of string to start of substring - regoff_t rm_eo; // byte offset from start of string of the first character after the end of substring - regoff_t rm_cnt; // number of matches -} regmatch_t; - -// Values for the cflags parameter to the regcomp() function: -#define REG_EXTENDED __Regex_Extended // Use Extended Regular Expressions. -#define REG_ICASE __Regex_Insensitive // Ignore case in match. -#define REG_NOSUB __Regex_SkipSubExprResults // Report only success or fail in regexec(). -#define REG_GLOBAL __Regex_Global // Don't stop searching for more match -#define REG_NEWLINE (__Regex_Multiline | REG_GLOBAL) // Change the handling of newline. - -// Values for the eflags parameter to the regexec() function: -#define REG_NOTBOL __Regex_MatchNotBeginOfLine // The circumflex character (^), when taken as a special character, will not match the beginning of string. -#define REG_NOTEOL __Regex_MatchNotEndOfLine // The dollar sign ($), when taken as a special character, will not match the end of string. - -#define REG_SEARCH __Regex_Last << 1 - -int regcomp(regex_t*, char const*, int); -int regexec(regex_t const*, char const*, size_t, regmatch_t[], int); -size_t regerror(int, regex_t const*, char*, size_t); -void regfree(regex_t*); - -__END_DECLS diff --git a/Userland/Libraries/LibC/resolv.cpp b/Userland/Libraries/LibC/resolv.cpp deleted file mode 100644 index e69dab3cb0d..00000000000 --- a/Userland/Libraries/LibC/resolv.cpp +++ /dev/null @@ -1,17 +0,0 @@ -/* - * Copyright (c) 2021, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include - -extern "C" { - -int res_query(char const*, int, int, unsigned char*, int) -{ - dbgln("FIXME: Implement res_query()"); - return 0; -} -} diff --git a/Userland/Libraries/LibC/resolv.h b/Userland/Libraries/LibC/resolv.h deleted file mode 100644 index 23f4ece30ad..00000000000 --- a/Userland/Libraries/LibC/resolv.h +++ /dev/null @@ -1,16 +0,0 @@ -/* - * Copyright (c) 2021, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include - -__BEGIN_DECLS - -int res_query(char const* dname, int class_, int type, unsigned char* answer, int anslen); - -__END_DECLS diff --git a/Userland/Libraries/LibC/scanf.cpp b/Userland/Libraries/LibC/scanf.cpp deleted file mode 100644 index bcd26abb9f6..00000000000 --- a/Userland/Libraries/LibC/scanf.cpp +++ /dev/null @@ -1,618 +0,0 @@ -/* - * Copyright (c) 2021, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -enum class LengthModifier { - None, - Default, - Char, - Short, - Long, - LongLong, - IntMax, - Size, - PtrDiff, - LongDouble, -}; - -enum class ConversionSpecifier { - Unspecified, - Decimal, - Integer, - Octal, - Unsigned, - Hex, - Floating, - String, - UseScanList, - Character, - Pointer, - OutputNumberOfBytes, - Invalid, -}; - -enum class ReadKind { - Normal, - Octal, - Hex, - Infer, -}; - -template -struct ReadElementConcrete { - bool operator()(GenericLexer&, va_list) - { - return false; - } -}; - -template -struct ReadElementConcrete { - bool operator()(GenericLexer& lexer, va_list* ap, bool suppress_assignment) - { - long value = 0; - char* endptr = nullptr; - auto nptr = lexer.remaining().characters_without_null_termination(); - if constexpr (kind == ReadKind::Normal) - value = strtol(nptr, &endptr, 10); - if constexpr (kind == ReadKind::Octal) - value = strtol(nptr, &endptr, 8); - if constexpr (kind == ReadKind::Hex) - value = strtol(nptr, &endptr, 16); - if constexpr (kind == ReadKind::Infer) - value = strtol(nptr, &endptr, 0); - - if (!endptr) - return false; - - if (endptr == nptr) - return false; - - auto diff = endptr - nptr; - VERIFY(diff > 0); - lexer.ignore((size_t)diff); - - if (!suppress_assignment) { - auto* ptr = va_arg(*ap, ApT*); - *ptr = value; - } - return true; - } -}; - -template -struct ReadElementConcrete { - bool operator()(GenericLexer& lexer, va_list* ap, bool suppress_assignment) - { - static_assert(kind == ReadKind::Normal, "Can't read a non-normal character"); - - if (lexer.is_eof()) - return false; - - auto ch = lexer.consume(); - if (!suppress_assignment) { - auto* ptr = va_arg(*ap, ApT*); - *ptr = ch; - } - return true; - } -}; - -template -struct ReadElementConcrete { - bool operator()(GenericLexer& lexer, va_list* ap, bool suppress_assignment) - { - unsigned long value = 0; - char* endptr = nullptr; - auto nptr = lexer.remaining().characters_without_null_termination(); - if constexpr (kind == ReadKind::Normal) - value = strtoul(nptr, &endptr, 10); - if constexpr (kind == ReadKind::Octal) - value = strtoul(nptr, &endptr, 8); - if constexpr (kind == ReadKind::Hex) - value = strtoul(nptr, &endptr, 16); - if constexpr (kind == ReadKind::Infer) - value = strtoul(nptr, &endptr, 0); - - if (!endptr) - return false; - - if (endptr == nptr) - return false; - - auto diff = endptr - nptr; - VERIFY(diff > 0); - lexer.ignore((size_t)diff); - - if (!suppress_assignment) { - auto* ptr = va_arg(*ap, ApT*); - *ptr = value; - } - return true; - } -}; - -template -struct ReadElementConcrete { - bool operator()(GenericLexer& lexer, va_list* ap, bool suppress_assignment) - { - long long value = 0; - char* endptr = nullptr; - auto nptr = lexer.remaining().characters_without_null_termination(); - if constexpr (kind == ReadKind::Normal) - value = strtoll(nptr, &endptr, 10); - if constexpr (kind == ReadKind::Octal) - value = strtoll(nptr, &endptr, 8); - if constexpr (kind == ReadKind::Hex) - value = strtoll(nptr, &endptr, 16); - if constexpr (kind == ReadKind::Infer) - value = strtoll(nptr, &endptr, 0); - - if (!endptr) - return false; - - if (endptr == nptr) - return false; - - auto diff = endptr - nptr; - VERIFY(diff > 0); - lexer.ignore((size_t)diff); - - if (!suppress_assignment) { - auto* ptr = va_arg(*ap, ApT*); - *ptr = value; - } - return true; - } -}; - -template -struct ReadElementConcrete { - bool operator()(GenericLexer& lexer, va_list* ap, bool suppress_assignment) - { - unsigned long long value = 0; - char* endptr = nullptr; - auto nptr = lexer.remaining().characters_without_null_termination(); - if constexpr (kind == ReadKind::Normal) - value = strtoull(nptr, &endptr, 10); - if constexpr (kind == ReadKind::Octal) - value = strtoull(nptr, &endptr, 8); - if constexpr (kind == ReadKind::Hex) - value = strtoull(nptr, &endptr, 16); - if constexpr (kind == ReadKind::Infer) - value = strtoull(nptr, &endptr, 0); - - if (!endptr) - return false; - - if (endptr == nptr) - return false; - - auto diff = endptr - nptr; - VERIFY(diff > 0); - lexer.ignore((size_t)diff); - - if (!suppress_assignment) { - auto* ptr = va_arg(*ap, ApT*); - *ptr = value; - } - return true; - } -}; - -template -struct ReadElementConcrete { - bool operator()(GenericLexer& lexer, va_list* ap, bool suppress_assignment) - { - double value = 0; - char* endptr = nullptr; - auto nptr = lexer.remaining().characters_without_null_termination(); - if constexpr (kind == ReadKind::Normal) - value = strtod(nptr, &endptr); - else - return false; - - if (!endptr) - return false; - - if (endptr == nptr) - return false; - - auto diff = endptr - nptr; - VERIFY(diff > 0); - lexer.ignore((size_t)diff); - - if (!suppress_assignment) { - auto* ptr = va_arg(*ap, ApT*); - *ptr = value; - } - return true; - } -}; - -template -struct ReadElement { - bool operator()(LengthModifier length_modifier, GenericLexer& input_lexer, va_list* ap, bool suppress_assignment) - { - switch (length_modifier) { - default: - case LengthModifier::None: - VERIFY_NOT_REACHED(); - case LengthModifier::Default: - return ReadElementConcrete {}(input_lexer, ap, suppress_assignment); - case LengthModifier::Char: - return ReadElementConcrete {}(input_lexer, ap, suppress_assignment); - case LengthModifier::Short: - return ReadElementConcrete {}(input_lexer, ap, suppress_assignment); - case LengthModifier::Long: - if constexpr (IsSame) - return ReadElementConcrete {}(input_lexer, ap, suppress_assignment); - if constexpr (IsSame) - return ReadElementConcrete {}(input_lexer, ap, suppress_assignment); - if constexpr (IsSame) - return ReadElementConcrete {}(input_lexer, ap, suppress_assignment); - return false; - case LengthModifier::LongLong: - if constexpr (IsSame) - return ReadElementConcrete {}(input_lexer, ap, suppress_assignment); - if constexpr (IsSame) - return ReadElementConcrete {}(input_lexer, ap, suppress_assignment); - if constexpr (IsSame) - return ReadElementConcrete {}(input_lexer, ap, suppress_assignment); - return false; - case LengthModifier::IntMax: - return ReadElementConcrete {}(input_lexer, ap, suppress_assignment); - case LengthModifier::Size: - return ReadElementConcrete {}(input_lexer, ap, suppress_assignment); - case LengthModifier::PtrDiff: - return ReadElementConcrete {}(input_lexer, ap, suppress_assignment); - case LengthModifier::LongDouble: - return ReadElementConcrete {}(input_lexer, ap, suppress_assignment); - } - } -}; - -template<> -struct ReadElement { - ReadElement(StringView scan_set = {}, bool invert = false) - : scan_set(scan_set.is_null() ? " \t\n\f\r"sv : scan_set) - , invert(scan_set.is_null() ? true : invert) - { - } - - bool operator()(LengthModifier length_modifier, GenericLexer& input_lexer, va_list* ap, bool suppress_assignment) - { - // FIXME: Implement wide strings and such. - if (length_modifier != LengthModifier::Default) - return false; - - auto str = input_lexer.consume_while([this](auto c) { return this->matches(c); }); - if (str.is_empty()) - return false; - - if (!suppress_assignment) { - auto* ptr = va_arg(*ap, char*); - memcpy(ptr, str.characters_without_null_termination(), str.length()); - ptr[str.length()] = 0; - } - - return true; - } - -private: - bool matches(char c) const - { - return invert ^ scan_set.contains(c); - } - - StringView const scan_set; - bool invert { false }; -}; - -template<> -struct ReadElement { - bool operator()(LengthModifier length_modifier, GenericLexer& input_lexer, va_list* ap, bool suppress_assignment) - { - if (length_modifier != LengthModifier::Default) - return false; - - auto str = input_lexer.consume_while([this](auto c) { return this->should_consume(c); }); - - if (count != 8) { - fail:; - for (size_t i = 0; i < count; ++i) - input_lexer.retreat(); - return false; - } - - char buf[9] { 0 }; - memcpy(buf, str.characters_without_null_termination(), 8); - buf[8] = 0; - char* endptr = nullptr; - auto value = strtoull(buf, &endptr, 16); - - if (endptr != &buf[8]) - goto fail; - - if (!suppress_assignment) { - auto* ptr = va_arg(*ap, void**); - memcpy(ptr, &value, sizeof(value)); - } - return true; - } - -private: - bool should_consume(char c) - { - if (count == 8) - return false; - if (!isxdigit(c)) - return false; - - ++count; - return true; - } - size_t count { 0 }; -}; - -extern "C" int vsscanf(char const* input, char const* format, va_list ap) -{ - GenericLexer format_lexer { { format, strlen(format) } }; - GenericLexer input_lexer { { input, strlen(input) } }; - - int elements_matched = 0; - - va_list copy; - __builtin_va_copy(copy, ap); - - while (!format_lexer.is_eof()) { - if (format_lexer.next_is(isspace)) { - format_lexer.ignore_while(isspace); - input_lexer.ignore_while(isspace); - } - - if (!format_lexer.next_is('%')) { - read_one_literal:; - if (format_lexer.is_eof()) - break; - - auto next_char = format_lexer.consume(); - if (!input_lexer.consume_specific(next_char)) - return elements_matched; - continue; - } - - if (format_lexer.next_is("%%")) { - format_lexer.ignore(); - goto read_one_literal; - } - - format_lexer.ignore(); // '%' - - bool suppress_assignment = false; - if (format_lexer.next_is('*')) { - suppress_assignment = true; - format_lexer.ignore(); - } - - // Parse width specification - [[maybe_unused]] int width_specifier = 0; - if (format_lexer.next_is(isdigit)) { - auto width_digits = format_lexer.consume_while([](char c) { return isdigit(c); }); - width_specifier = width_digits.to_number().value(); - // FIXME: Actually use width specifier - } - - bool invert_scanlist = false; - StringView scanlist; - LengthModifier length_modifier { LengthModifier::None }; - ConversionSpecifier conversion_specifier { ConversionSpecifier::Unspecified }; - reread_lookahead:; - auto format_lookahead = format_lexer.peek(); - if (length_modifier == LengthModifier::None) { - switch (format_lookahead) { - case 'h': - if (format_lexer.peek(1) == 'h') { - format_lexer.consume(2); - length_modifier = LengthModifier::Char; - } else { - format_lexer.consume(1); - length_modifier = LengthModifier::Short; - } - break; - case 'l': - if (format_lexer.peek(1) == 'l') { - format_lexer.consume(2); - length_modifier = LengthModifier::LongLong; - } else { - format_lexer.consume(1); - length_modifier = LengthModifier::Long; - } - break; - case 'j': - format_lexer.consume(); - length_modifier = LengthModifier::IntMax; - break; - case 'z': - format_lexer.consume(); - length_modifier = LengthModifier::Size; - break; - case 't': - format_lexer.consume(); - length_modifier = LengthModifier::PtrDiff; - break; - case 'L': - format_lexer.consume(); - length_modifier = LengthModifier::LongDouble; - break; - default: - length_modifier = LengthModifier::Default; - break; - } - goto reread_lookahead; - } - if (conversion_specifier == ConversionSpecifier::Unspecified) { - switch (format_lookahead) { - case 'd': - format_lexer.consume(); - conversion_specifier = ConversionSpecifier::Decimal; - break; - case 'i': - format_lexer.consume(); - conversion_specifier = ConversionSpecifier::Integer; - break; - case 'o': - format_lexer.consume(); - conversion_specifier = ConversionSpecifier::Octal; - break; - case 'u': - format_lexer.consume(); - conversion_specifier = ConversionSpecifier::Unsigned; - break; - case 'x': - case 'X': - format_lexer.consume(); - conversion_specifier = ConversionSpecifier::Hex; - break; - case 'a': - case 'e': - case 'f': - case 'g': - format_lexer.consume(); - conversion_specifier = ConversionSpecifier::Floating; - break; - case 's': - format_lexer.consume(); - conversion_specifier = ConversionSpecifier::String; - break; - case '[': - format_lexer.consume(); - scanlist = format_lexer.consume_until(']'); - format_lexer.ignore(); - if (scanlist.starts_with('^')) { - scanlist = scanlist.substring_view(1); - invert_scanlist = true; - } - conversion_specifier = ConversionSpecifier::UseScanList; - break; - case 'c': - format_lexer.consume(); - conversion_specifier = ConversionSpecifier::Character; - break; - case 'p': - format_lexer.consume(); - conversion_specifier = ConversionSpecifier::Pointer; - break; - case 'n': - format_lexer.consume(); - conversion_specifier = ConversionSpecifier::OutputNumberOfBytes; - break; - case 'C': - format_lexer.consume(); - length_modifier = LengthModifier::Long; - conversion_specifier = ConversionSpecifier::Character; - break; - case 'S': - format_lexer.consume(); - length_modifier = LengthModifier::Long; - conversion_specifier = ConversionSpecifier::String; - break; - default: - format_lexer.consume(); - conversion_specifier = ConversionSpecifier::Invalid; - break; - } - } - - // Now try to read. - switch (conversion_specifier) { - case ConversionSpecifier::Invalid: - case ConversionSpecifier::Unspecified: - default: - // "undefined behavior", let's be nice and crash. - dbgln("Invalid conversion specifier {} in scanf!", (int)conversion_specifier); - VERIFY_NOT_REACHED(); - case ConversionSpecifier::Decimal: - if (!ReadElement {}(length_modifier, input_lexer, ©, suppress_assignment)) - format_lexer.consume_all(); - else if (!suppress_assignment) - ++elements_matched; - break; - case ConversionSpecifier::Integer: - if (!ReadElement {}(length_modifier, input_lexer, ©, suppress_assignment)) - format_lexer.consume_all(); - else if (!suppress_assignment) - ++elements_matched; - break; - case ConversionSpecifier::Octal: - if (!ReadElement {}(length_modifier, input_lexer, ©, suppress_assignment)) - format_lexer.consume_all(); - else if (!suppress_assignment) - ++elements_matched; - break; - case ConversionSpecifier::Unsigned: - if (!ReadElement {}(length_modifier, input_lexer, ©, suppress_assignment)) - format_lexer.consume_all(); - else if (!suppress_assignment) - ++elements_matched; - break; - case ConversionSpecifier::Hex: - if (!ReadElement {}(length_modifier, input_lexer, ©, suppress_assignment)) - format_lexer.consume_all(); - else if (!suppress_assignment) - ++elements_matched; - break; - case ConversionSpecifier::Floating: - if (!ReadElement {}(length_modifier, input_lexer, ©, suppress_assignment)) - format_lexer.consume_all(); - else if (!suppress_assignment) - ++elements_matched; - break; - case ConversionSpecifier::String: - if (!ReadElement {}(length_modifier, input_lexer, ©, suppress_assignment)) - format_lexer.consume_all(); - else if (!suppress_assignment) - ++elements_matched; - break; - case ConversionSpecifier::UseScanList: - if (!ReadElement { scanlist, invert_scanlist }(length_modifier, input_lexer, ©, suppress_assignment)) - format_lexer.consume_all(); - else if (!suppress_assignment) - ++elements_matched; - break; - case ConversionSpecifier::Character: - if (!ReadElement {}(length_modifier, input_lexer, ©, suppress_assignment)) - format_lexer.consume_all(); - else if (!suppress_assignment) - ++elements_matched; - break; - case ConversionSpecifier::Pointer: - if (!ReadElement {}(length_modifier, input_lexer, ©, suppress_assignment)) - format_lexer.consume_all(); - else if (!suppress_assignment) - ++elements_matched; - break; - case ConversionSpecifier::OutputNumberOfBytes: { - if (!suppress_assignment) { - auto* ptr = va_arg(copy, int*); - *ptr = input_lexer.tell(); - } - break; - } - } - } - va_end(copy); - - return elements_matched; -} diff --git a/Userland/Libraries/LibC/sched.cpp b/Userland/Libraries/LibC/sched.cpp deleted file mode 100644 index 914e2eee5bb..00000000000 --- a/Userland/Libraries/LibC/sched.cpp +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include - -extern "C" { - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/sched_yield.html -int sched_yield() -{ - int rc = syscall(SC_yield); - __RETURN_WITH_ERRNO(rc, rc, -1); -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/sched_get_priority_min.html -int sched_get_priority_min([[maybe_unused]] int policy) -{ - return THREAD_PRIORITY_MIN; -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/sched_get_priority_max.html -int sched_get_priority_max([[maybe_unused]] int policy) -{ - return THREAD_PRIORITY_MAX; -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/sched_setparam.html -int sched_setparam(pid_t pid, const struct sched_param* param) -{ - Syscall::SC_scheduler_parameters_params parameters { - .pid_or_tid = pid, - .mode = Syscall::SchedulerParametersMode::Process, - .parameters = *param, - }; - int rc = syscall(SC_scheduler_set_parameters, ¶meters); - __RETURN_WITH_ERRNO(rc, rc, -1); -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/sched_getparam.html -int sched_getparam(pid_t pid, struct sched_param* param) -{ - Syscall::SC_scheduler_parameters_params parameters { - .pid_or_tid = pid, - .mode = Syscall::SchedulerParametersMode::Process, - .parameters = {}, - }; - int rc = syscall(SC_scheduler_get_parameters, ¶meters); - if (rc == 0) - *param = parameters.parameters; - __RETURN_WITH_ERRNO(rc, rc, -1); -} -} diff --git a/Userland/Libraries/LibC/sched.h b/Userland/Libraries/LibC/sched.h deleted file mode 100644 index b6867df04be..00000000000 --- a/Userland/Libraries/LibC/sched.h +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -// Includes essentially mandated by POSIX: -// https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/sched.h.html -#include - -#include -#include -#include - -__BEGIN_DECLS - -int sched_yield(void); - -#define SCHED_FIFO 0 -#define SCHED_RR 1 -#define SCHED_OTHER 2 -#define SCHED_BATCH 3 - -int sched_get_priority_min(int policy); -int sched_get_priority_max(int policy); -int sched_setparam(pid_t pid, const struct sched_param* param); -int sched_getparam(pid_t pid, struct sched_param* param); - -__END_DECLS diff --git a/Userland/Libraries/LibC/search.cpp b/Userland/Libraries/LibC/search.cpp deleted file mode 100644 index bad67a0b79f..00000000000 --- a/Userland/Libraries/LibC/search.cpp +++ /dev/null @@ -1,126 +0,0 @@ -/* - * Copyright (c) 2021, the SerenityOS developers. - * Copyright (c) 2021, Tim Schumacher - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include - -struct search_tree_node* new_tree_node(void const* key) -{ - auto* node = static_cast(malloc(sizeof(struct search_tree_node))); - - if (!node) - return nullptr; - - node->key = key; - node->left = nullptr; - node->right = nullptr; - - return node; -} - -void delete_node_recursive(struct search_tree_node* node) -{ - if (!node) - return; - - delete_node_recursive(node->left); - delete_node_recursive(node->right); - - free(node); -} - -extern "C" { - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/tsearch.html -void* tsearch(void const* key, void** rootp, int (*comparator)(void const*, void const*)) -{ - if (!rootp) - return nullptr; - - if (!*rootp) { - *rootp = new_tree_node(key); - return *rootp; - } - - auto node = static_cast(*rootp); - - while (node != nullptr) { - auto comp = comparator(key, node->key); - - if (comp < 0 && node->left) { - node = node->left; - } else if (comp < 0 && !node->left) { - node->left = new_tree_node(key); - return node->left; - } else if (comp > 0 && node->right) { - node = node->right; - } else if (comp > 0 && !node->right) { - node->right = new_tree_node(key); - return node->right; - } else { - return node; - } - } - - VERIFY_NOT_REACHED(); -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/tfind.html -void* tfind(void const* key, void* const* rootp, int (*comparator)(void const*, void const*)) -{ - if (!rootp) - return nullptr; - - auto node = static_cast(*rootp); - - while (node != nullptr) { - auto comp = comparator(key, node->key); - - if (comp < 0) - node = node->left; - else if (comp > 0) - node = node->right; - else - return node; - } - - return nullptr; -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/tdelete.html -void* tdelete(void const*, void**, int (*)(void const*, void const*)) -{ - dbgln("FIXME: Implement tdelete()"); - return nullptr; -} - -static void twalk_internal(const struct search_tree_node* node, void (*action)(void const*, VISIT, int), int depth) -{ - if (!node) - return; - - if (!node->right && !node->left) { - action(node, leaf, depth); - return; - } - - action(node, preorder, depth); - twalk_internal(node->left, action, depth + 1); - action(node, postorder, depth); - twalk_internal(node->right, action, depth + 1); - action(node, endorder, depth); -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/twalk.html -void twalk(void const* rootp, void (*action)(void const*, VISIT, int)) -{ - auto node = static_cast(rootp); - - twalk_internal(node, action, 0); -} -} diff --git a/Userland/Libraries/LibC/search.h b/Userland/Libraries/LibC/search.h deleted file mode 100644 index 1020c344876..00000000000 --- a/Userland/Libraries/LibC/search.h +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright (c) 2021, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include - -__BEGIN_DECLS - -typedef enum { - preorder, - postorder, - endorder, - leaf, -} VISIT; - -void* tsearch(void const*, void**, int (*)(void const*, void const*)); -void* tfind(void const*, void* const*, int (*)(void const*, void const*)); -void* tdelete(void const*, void**, int (*)(void const*, void const*)); -void twalk(void const*, void (*)(void const*, VISIT, int)); - -__END_DECLS diff --git a/Userland/Libraries/LibC/semaphore.cpp b/Userland/Libraries/LibC/semaphore.cpp deleted file mode 100644 index f6a6dbaf9d3..00000000000 --- a/Userland/Libraries/LibC/semaphore.cpp +++ /dev/null @@ -1,376 +0,0 @@ -/* - * Copyright (c) 2021, Gunnar Beutner - * Copyright (c) 2021, Sergey Bugaev - * Copyright (c) 2022, Idan Horowitz - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -static constexpr u32 SEM_MAGIC = 0x78951230; - -// Whether sem_wait() or sem_post() is responsible for waking any sleeping -// threads. -static constexpr u32 POST_WAKES = 1 << 31; - -static constexpr auto sem_path_prefix = "/tmp/semaphore/"sv; -static constexpr auto SEM_NAME_MAX = PATH_MAX - sem_path_prefix.length(); - -// The returned string will always contain a null terminator, since it is used in C APIs. -static ErrorOr sem_name_to_path(char const* name) -{ - if (name[0] != '/') - return EINVAL; - ++name; - - auto name_length = strnlen(name, SEM_NAME_MAX); - if (name[name_length]) - return ENAMETOOLONG; - - auto name_view = StringView { name, name_length }; - if (name_view.contains('/')) - return EINVAL; - - StringBuilder builder; - TRY(builder.try_append(sem_path_prefix)); - TRY(builder.try_append(name_view)); - - TRY(builder.try_append_code_point(0)); - return builder.to_string(); -} - -struct NamedSemaphore { - size_t times_opened { 0 }; - dev_t dev { 0 }; - ino_t ino { 0 }; - sem_t* sem { nullptr }; -}; - -static HashMap s_named_semaphores; -static pthread_mutex_t s_sem_mutex = PTHREAD_MUTEX_INITIALIZER; -static pthread_once_t s_sem_once = PTHREAD_ONCE_INIT; - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/sem_open.html -sem_t* sem_open(char const* name, int flags, ...) -{ - auto path_or_error = sem_name_to_path(name); - if (path_or_error.is_error()) { - errno = path_or_error.error().code(); - return SEM_FAILED; - } - auto path = path_or_error.release_value(); - - if (flags & ~(O_CREAT | O_EXCL)) { - errno = EINVAL; - return SEM_FAILED; - } - - mode_t mode = 0; - unsigned int value = 0; - if (flags & O_CREAT) { - va_list ap; - va_start(ap, flags); - mode = va_arg(ap, unsigned int); - value = va_arg(ap, unsigned int); - va_end(ap); - } - - // Ensure we are not in the middle of modifying this structure while a child is being forked, which will cause the child to end up with a partially-modified entry - pthread_once(&s_sem_once, []() { - pthread_atfork([]() { pthread_mutex_lock(&s_sem_mutex); }, []() { pthread_mutex_unlock(&s_sem_mutex); }, []() { pthread_mutex_unlock(&s_sem_mutex); }); - }); - - pthread_mutex_lock(&s_sem_mutex); - ScopeGuard unlock_guard = [] { pthread_mutex_unlock(&s_sem_mutex); }; - - // sem_name_to_path guarantees a null terminator. - int fd = open(path.bytes_as_string_view().characters_without_null_termination(), O_RDWR | O_CLOEXEC | flags, mode); - if (fd == -1) - return SEM_FAILED; - - ScopeGuard close_guard = [&fd] { - if (fd != -1) - close(fd); - }; - - if (flock(fd, LOCK_EX) == -1) - return SEM_FAILED; - - struct stat statbuf; - if (fstat(fd, &statbuf) == -1) - return SEM_FAILED; - - auto existing_semaphore = s_named_semaphores.get(path); - if (existing_semaphore.has_value()) { - // If the file did not exist (aka if O_CREAT && O_EXCL but no EEXIST), or if the inode was replaced, remove the entry and start from scratch - if ((flags & (O_CREAT | O_EXCL)) == (O_CREAT | O_EXCL) || existing_semaphore->dev != statbuf.st_dev || existing_semaphore->ino != statbuf.st_ino) { - s_named_semaphores.remove(path); - } else { // otherwise, this is valid pre-existing named semaphore, so just increase the count and return it - existing_semaphore->times_opened++; - return existing_semaphore->sem; - } - } - - // If the file is smaller than the size, it's an uninitialized semaphore, so let's write an initial value - if (statbuf.st_size < (off_t)sizeof(sem_t)) { - sem_t init_sem; - init_sem.magic = SEM_MAGIC; - init_sem.value = value; - init_sem.flags = SEM_FLAG_PROCESS_SHARED | SEM_FLAG_NAMED; - if (write(fd, &init_sem, sizeof(sem_t)) != sizeof(sem_t)) - return SEM_FAILED; - } - - if (flock(fd, LOCK_UN) == -1) - return SEM_FAILED; - - auto* sem = (sem_t*)mmap(nullptr, sizeof(sem_t), PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); - if (sem == MAP_FAILED) - return SEM_FAILED; - - ArmedScopeGuard munmap_guard = [&sem] { - munmap(sem, sizeof(sem_t)); - }; - - if (sem->magic != SEM_MAGIC) { - errno = EINVAL; - return SEM_FAILED; - } - - auto result = s_named_semaphores.try_set(move(path), { .times_opened = 1, .dev = statbuf.st_dev, .ino = statbuf.st_ino, .sem = sem }); - if (result.is_error()) { - errno = result.error().code(); - return SEM_FAILED; - } - - munmap_guard.disarm(); - return sem; -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/sem_close.html -int sem_close(sem_t* sem) -{ - if (sem->magic != SEM_MAGIC) { - errno = EINVAL; - return -1; - } - - if ((sem->flags & SEM_FLAG_NAMED) == 0) { - errno = EINVAL; - return -1; - } - - pthread_mutex_lock(&s_sem_mutex); - ScopeGuard unlock_guard = [] { pthread_mutex_unlock(&s_sem_mutex); }; - - auto it = s_named_semaphores.begin(); - for (; it != s_named_semaphores.end(); ++it) { - if (it->value.sem != sem) - continue; - auto is_last = --it->value.times_opened == 0; - if (is_last) { - munmap(it->value.sem, sizeof(sem_t)); - s_named_semaphores.remove(it); - } - return 0; - } - - errno = EINVAL; - return -1; -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/sem_unlink.html -int sem_unlink(char const* name) -{ - auto path_or_error = sem_name_to_path(name); - if (path_or_error.is_error()) { - errno = path_or_error.error().code(); - return -1; - } - auto path = path_or_error.release_value(); - - return unlink(path.bytes_as_string_view().characters_without_null_termination()); -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/sem_init.html -int sem_init(sem_t* sem, int process_shared, unsigned int value) -{ - if (value > SEM_VALUE_MAX) { - errno = EINVAL; - return -1; - } - - sem->magic = SEM_MAGIC; - sem->value = value; - sem->flags = process_shared ? SEM_FLAG_PROCESS_SHARED : 0; - return 0; -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/sem_destroy.html -int sem_destroy(sem_t* sem) -{ - if (sem->magic != SEM_MAGIC) { - errno = EINVAL; - return -1; - } - - if (sem->flags & SEM_FLAG_NAMED) { - errno = EINVAL; - return -1; - } - - sem->magic = 0; - return 0; -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/sem_getvalue.html -int sem_getvalue(sem_t* sem, int* sval) -{ - if (sem->magic != SEM_MAGIC) { - errno = EINVAL; - return -1; - } - - u32 value = AK::atomic_load(&sem->value, AK::memory_order_relaxed); - *sval = value & ~POST_WAKES; - return 0; -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/sem_post.html -int sem_post(sem_t* sem) -{ - if (sem->magic != SEM_MAGIC) { - errno = EINVAL; - return -1; - } - - u32 value = AK::atomic_fetch_add(&sem->value, 1u, AK::memory_order_release); - // Fast path: no need to wake. - if (!(value & POST_WAKES)) [[likely]] - return 0; - - // Pass the responsibility for waking more threads if more slots become - // available later to sem_wait() in the thread we're about to wake, as - // opposed to further sem_post() calls that free up those slots. - value = AK::atomic_fetch_and(&sem->value, ~POST_WAKES, AK::memory_order_relaxed); - // Check if another sem_post() call has handled it already. - if (!(value & POST_WAKES)) [[likely]] - return 0; - int rc = futex_wake(&sem->value, 1, sem->flags & SEM_FLAG_PROCESS_SHARED); - VERIFY(rc >= 0); - return 0; -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/sem_trywait.html -int sem_trywait(sem_t* sem) -{ - if (sem->magic != SEM_MAGIC) { - errno = EINVAL; - return -1; - } - - u32 value = AK::atomic_load(&sem->value, AK::memory_order_relaxed); - u32 count = value & ~POST_WAKES; - if (count == 0) { - errno = EAGAIN; - return -1; - } - // Decrement the count without touching the flag. - u32 desired = (count - 1) | (value & POST_WAKES); - bool exchanged = AK::atomic_compare_exchange_strong(&sem->value, value, desired, AK::memory_order_acquire); - if (exchanged) [[likely]] { - return 0; - } else { - errno = EAGAIN; - return -1; - } -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/sem_wait.html -int sem_wait(sem_t* sem) -{ - if (sem->magic != SEM_MAGIC) { - errno = EINVAL; - return -1; - } - - return sem_timedwait(sem, nullptr); -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/sem_timedwait.html -int sem_timedwait(sem_t* sem, const struct timespec* abstime) -{ - __pthread_maybe_cancel(); - - if (sem->magic != SEM_MAGIC) { - errno = EINVAL; - return -1; - } - - u32 value = AK::atomic_load(&sem->value, AK::memory_order_relaxed); - bool responsible_for_waking = false; - bool process_shared = sem->flags & SEM_FLAG_PROCESS_SHARED; - - while (true) { - u32 count = value & ~POST_WAKES; - if (count > 0) [[likely]] { - // It looks like there are some free slots. - u32 whether_post_wakes = value & POST_WAKES; - bool going_to_wake = false; - if (responsible_for_waking && !whether_post_wakes) { - // If we have ourselves been woken up previously, and the - // POST_WAKES flag is not set, that means some more slots might - // be available now, and it's us who has to wake up additional - // threads. - if (count > 1) [[unlikely]] - going_to_wake = true; - // Pass the responsibility for waking up further threads back to - // sem_post() calls. In particular, we don't want the threads - // we're about to wake to try to wake anyone else. - whether_post_wakes = POST_WAKES; - } - // Now, try to commit this. - u32 desired = (count - 1) | whether_post_wakes; - bool exchanged = AK::atomic_compare_exchange_strong(&sem->value, value, desired, AK::memory_order_acquire); - if (!exchanged) [[unlikely]] - // Re-evaluate. - continue; - if (going_to_wake) [[unlikely]] { - int rc = futex_wake(&sem->value, count - 1, process_shared); - VERIFY(rc >= 0); - } - return 0; - } - // We're probably going to sleep, so attempt to set the flag. We do not - // commit to sleeping yet, though, as setting the flag may fail and - // cause us to reevaluate what we're doing. - if (value == 0) { - bool exchanged = AK::atomic_compare_exchange_strong(&sem->value, value, POST_WAKES, AK::memory_order_relaxed); - if (!exchanged) [[unlikely]] - // Re-evaluate. - continue; - value = POST_WAKES; - } - // At this point, we're committed to sleeping. - responsible_for_waking = true; - futex_wait(&sem->value, value, abstime, CLOCK_REALTIME, process_shared); - // This is the state we will probably see upon being waked: - value = 1; - } -} diff --git a/Userland/Libraries/LibC/semaphore.h b/Userland/Libraries/LibC/semaphore.h deleted file mode 100644 index 7a415246bfb..00000000000 --- a/Userland/Libraries/LibC/semaphore.h +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright (c) 2021, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -// Includes essentially mandated by POSIX: -// https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/semaphore.h.html -#include -#include - -#include -#include -#include -#include - -__BEGIN_DECLS - -#define SEM_FLAG_PROCESS_SHARED (1 << 0) -#define SEM_FLAG_NAMED (1 << 1) -typedef struct { - uint32_t magic; - uint32_t value; - uint8_t flags; -} sem_t; - -int sem_close(sem_t*); -int sem_destroy(sem_t*); -int sem_getvalue(sem_t*, int*); -int sem_init(sem_t*, int, unsigned int); -sem_t* sem_open(char const*, int, ...); -int sem_post(sem_t*); -int sem_trywait(sem_t*); -int sem_unlink(char const*); -int sem_wait(sem_t*); -int sem_timedwait(sem_t*, const struct timespec* abstime); - -#define SEM_FAILED ((sem_t*)0) -#define SEM_VALUE_MAX INT_MAX - -__END_DECLS diff --git a/Userland/Libraries/LibC/serenity.cpp b/Userland/Libraries/LibC/serenity.cpp deleted file mode 100644 index aae2d5fc988..00000000000 --- a/Userland/Libraries/LibC/serenity.cpp +++ /dev/null @@ -1,177 +0,0 @@ -/* - * Copyright (c) 2018-2021, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include -#include -#include - -extern "C" { - -int disown(pid_t pid) -{ - int rc = syscall(SC_disown, pid); - __RETURN_WITH_ERRNO(rc, rc, -1); -} - -int profiling_enable(pid_t pid, uint64_t event_mask) -{ - int rc = syscall(SC_profiling_enable, pid, event_mask); - __RETURN_WITH_ERRNO(rc, rc, -1); -} - -int profiling_disable(pid_t pid) -{ - int rc = syscall(SC_profiling_disable, pid); - __RETURN_WITH_ERRNO(rc, rc, -1); -} - -int profiling_free_buffer(pid_t pid) -{ - int rc = syscall(SC_profiling_free_buffer, pid); - __RETURN_WITH_ERRNO(rc, rc, -1); -} - -int futex(uint32_t* userspace_address, int futex_op, uint32_t value, const struct timespec* timeout, uint32_t* userspace_address2, uint32_t value3) -{ - int rc; - switch (futex_op & FUTEX_CMD_MASK) { - case FUTEX_WAKE_OP: { - // These interpret timeout as a u32 value for val2 - Syscall::SC_futex_params params { - .userspace_address = userspace_address, - .futex_op = futex_op, - .val = value, - .val2 = (FlatPtr)timeout, - .userspace_address2 = userspace_address2, - .val3 = value3 - }; - rc = syscall(SC_futex, ¶ms); - break; - } - default: { - Syscall::SC_futex_params params { - .userspace_address = userspace_address, - .futex_op = futex_op, - .val = value, - .timeout = timeout, - .userspace_address2 = userspace_address2, - .val3 = value3 - }; - rc = syscall(SC_futex, ¶ms); - break; - } - } - __RETURN_WITH_ERRNO(rc, rc, -1); -} - -int purge(int mode) -{ - int rc = syscall(SC_purge, mode); - __RETURN_WITH_ERRNO(rc, rc, -1); -} - -int perf_event(int type, uintptr_t arg1, FlatPtr arg2) -{ - int rc = syscall(SC_perf_event, type, arg1, arg2); - __RETURN_WITH_ERRNO(rc, rc, -1); -} - -int perf_register_string(char const* string, size_t string_length) -{ - int rc = syscall(SC_perf_register_string, string, string_length); - __RETURN_WITH_ERRNO(rc, rc, -1); -} - -int get_stack_bounds(uintptr_t* user_stack_base, size_t* user_stack_size) -{ - int rc = syscall(SC_get_stack_bounds, user_stack_base, user_stack_size); - __RETURN_WITH_ERRNO(rc, rc, -1); -} - -int anon_create(size_t size, int options) -{ - int rc = syscall(SC_anon_create, size, options); - __RETURN_WITH_ERRNO(rc, rc, -1); -} - -int serenity_readlink(char const* path, size_t path_length, char* buffer, size_t buffer_size) -{ - Syscall::SC_readlink_params small_params { - { path, path_length }, - { buffer, buffer_size }, - AT_FDCWD - }; - int rc = syscall(SC_readlink, &small_params); - __RETURN_WITH_ERRNO(rc, rc, -1); -} - -int setkeymap(char const* name, u32 const* map, u32* const shift_map, u32 const* alt_map, u32 const* altgr_map, u32 const* shift_altgr_map) -{ - Syscall::SC_setkeymap_params params { map, shift_map, alt_map, altgr_map, shift_altgr_map, { name, strlen(name) } }; - return syscall(SC_setkeymap, ¶ms); -} - -int getkeymap(char* name_buffer, size_t name_buffer_size, u32* map, u32* shift_map, u32* alt_map, u32* altgr_map, u32* shift_altgr_map) -{ - Syscall::SC_getkeymap_params params { - map, - shift_map, - alt_map, - altgr_map, - shift_altgr_map, - { name_buffer, name_buffer_size } - }; - int rc = syscall(SC_getkeymap, ¶ms); - __RETURN_WITH_ERRNO(rc, rc, -1); -} - -u16 internet_checksum(void const* ptr, size_t count) -{ - u32 checksum = 0; - auto* w = (u16 const*)ptr; - while (count > 1) { - checksum += ntohs(*w++); - if (checksum & 0x80000000) - checksum = (checksum & 0xffff) | (checksum >> 16); - count -= 2; - } - while (checksum >> 16) - checksum = (checksum & 0xffff) + (checksum >> 16); - return htons(~checksum); -} - -int emuctl(uintptr_t command, uintptr_t arg0, uintptr_t arg1) -{ - return syscall(SC_emuctl, command, arg0, arg1); -} - -int serenity_open(char const* path, size_t path_length, int options, ...) -{ - if (!path) { - errno = EFAULT; - return -1; - } - - if (path_length > INT32_MAX) { - errno = EINVAL; - return -1; - } - - va_list ap; - va_start(ap, options); - auto mode = (mode_t)va_arg(ap, unsigned); - va_end(ap); - - Syscall::SC_open_params params { AT_FDCWD, { path, path_length }, options, mode }; - int rc = syscall(SC_open, ¶ms); - - __RETURN_WITH_ERRNO(rc, rc, -1); -} -} diff --git a/Userland/Libraries/LibC/serenity.h b/Userland/Libraries/LibC/serenity.h deleted file mode 100644 index 97735d7d4fc..00000000000 --- a/Userland/Libraries/LibC/serenity.h +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Copyright (c) 2018-2021, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include -#include - -__BEGIN_DECLS - -int disown(pid_t); - -int profiling_enable(pid_t, uint64_t); -int profiling_disable(pid_t); -int profiling_free_buffer(pid_t); - -int futex(uint32_t* userspace_address, int futex_op, uint32_t value, const struct timespec* timeout, uint32_t* userspace_address2, uint32_t value3); - -#ifndef ALWAYS_INLINE -# define ALWAYS_INLINE inline __attribute__((always_inline)) -# define ALWAYS_INLINE_SERENITY_H -#endif - -static ALWAYS_INLINE int futex_wait(uint32_t* userspace_address, uint32_t value, const struct timespec* abstime, int clockid, int process_shared) -{ - int op; - - if (abstime) { - // NOTE: FUTEX_WAIT takes a relative timeout, so use FUTEX_WAIT_BITSET instead! - op = FUTEX_WAIT_BITSET; - if (clockid == CLOCK_REALTIME || clockid == CLOCK_REALTIME_COARSE) - op |= FUTEX_CLOCK_REALTIME; - } else { - op = FUTEX_WAIT; - } - return futex(userspace_address, op | (process_shared ? 0 : FUTEX_PRIVATE_FLAG), value, abstime, NULL, FUTEX_BITSET_MATCH_ANY); -} - -static ALWAYS_INLINE int futex_wake(uint32_t* userspace_address, uint32_t count, int process_shared) -{ - return futex(userspace_address, FUTEX_WAKE | (process_shared ? 0 : FUTEX_PRIVATE_FLAG), count, NULL, NULL, 0); -} - -#ifdef ALWAYS_INLINE_SERENITY_H -# undef ALWAYS_INLINE -#endif - -int purge(int mode); - -int perf_event(int type, uintptr_t arg1, uintptr_t arg2); -int perf_register_string(char const* string, size_t string_length); - -int get_stack_bounds(uintptr_t* user_stack_base, size_t* user_stack_size); - -int anon_create(size_t size, int options); - -int serenity_readlink(char const* path, size_t path_length, char* buffer, size_t buffer_size); - -int getkeymap(char* name_buffer, size_t name_buffer_size, uint32_t* map, uint32_t* shift_map, uint32_t* alt_map, uint32_t* altgr_map, uint32_t* shift_altgr_map); -int setkeymap(char const* name, uint32_t const* map, uint32_t* const shift_map, uint32_t const* alt_map, uint32_t const* altgr_map, uint32_t const* shift_altgr_map); - -uint16_t internet_checksum(void const* ptr, size_t count); - -int emuctl(uintptr_t command, uintptr_t arg0, uintptr_t arg1); - -int serenity_open(char const* path, size_t path_length, int options, ...); - -__END_DECLS diff --git a/Userland/Libraries/LibC/setjmp.h b/Userland/Libraries/LibC/setjmp.h deleted file mode 100644 index a69a0adfe6b..00000000000 --- a/Userland/Libraries/LibC/setjmp.h +++ /dev/null @@ -1,92 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include - -__BEGIN_DECLS - -// -// /!\ This structure is accessed inside setjmp.S, keep both files in sync! -// - -struct __jmp_buf { -#if defined(__x86_64__) - uint64_t rbx; - uint64_t r12; - uint64_t r13; - uint64_t r14; - uint64_t r15; - uint64_t rbp; - uint64_t rsp; - uint64_t rip; -#elif defined(__aarch64__) - // FIXME: This is likely incorrect. - uint64_t regs[22]; -#elif defined(__riscv) && __riscv_xlen == 64 - uint64_t s[12]; - uint64_t fs[12]; - uint64_t sp; - uint64_t ra; -#else -# error "Unknown architecture" -#endif - int did_save_signal_mask; - sigset_t saved_signal_mask; -}; - -typedef struct __jmp_buf jmp_buf[1]; -typedef struct __jmp_buf sigjmp_buf[1]; - -/** - * Since setjmp.h may be included by ports written in C, we need to guard this. - */ -#ifdef __cplusplus -# if defined(__x86_64__) -static_assert(sizeof(struct __jmp_buf) == 72, "struct __jmp_buf unsynchronized with x86_64/setjmp.S"); -# elif defined(__aarch64__) -static_assert(sizeof(struct __jmp_buf) == 184, "struct __jmp_buf unsynchronized with aarch64/setjmp.S"); -# elif defined(__riscv) && __riscv_xlen == 64 -static_assert(sizeof(struct __jmp_buf) == 216, "struct __jmp_buf unsynchronized with riscv64/setjmp.S"); -# else -# error "Unknown architecture" -# endif -#endif - -/** - * Calling conventions mandates that sigsetjmp() cannot call setjmp(), - * otherwise the restored calling environment will not be the original caller's - * but sigsetjmp()'s and we'll return to the wrong call site on siglongjmp(). - * - * The setjmp(), sigsetjmp() and longjmp() functions have to be implemented in - * assembly because they touch the call stack and registers in non-portable - * ways. However, we *can* implement siglongjmp() as a standard C function. - */ - -int setjmp(jmp_buf); -__attribute__((noreturn)) void longjmp(jmp_buf, int val); - -int sigsetjmp(sigjmp_buf, int savesigs); -__attribute__((noreturn)) void siglongjmp(sigjmp_buf, int val); - -/** - * _setjmp() and _longjmp() are specified as behaving the exactly the same as - * setjmp() and longjmp(), except they are not supposed to modify the signal mask. - * - * Our implementations already follow this restriction, so we just map them directly - * to the same functions. - * - * https://pubs.opengroup.org/onlinepubs/9699969599/functions/_setjmp.html - */ -int _setjmp(jmp_buf); -__attribute__((noreturn)) void _longjmp(jmp_buf, int val); - -__END_DECLS diff --git a/Userland/Libraries/LibC/shadow.cpp b/Userland/Libraries/LibC/shadow.cpp deleted file mode 100644 index d0a3b1cba24..00000000000 --- a/Userland/Libraries/LibC/shadow.cpp +++ /dev/null @@ -1,311 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * Copyright (c) 2021, Gunnar Beutner - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -extern "C" { - -static FILE* s_stream = nullptr; -static unsigned s_line_number = 0; -static struct spwd s_shadow_entry; - -static ByteString s_name; -static ByteString s_pwdp; - -void setspent() -{ - s_line_number = 0; - if (s_stream) { - rewind(s_stream); - } else { - s_stream = fopen("/etc/shadow", "r"); - if (!s_stream) { - dbgln("open /etc/shadow failed: {}", strerror(errno)); - } - } -} - -void endspent() -{ - s_line_number = 0; - if (s_stream) { - fclose(s_stream); - s_stream = nullptr; - } - - memset(&s_shadow_entry, 0, sizeof(s_shadow_entry)); - - s_name = {}; - s_pwdp = {}; -} - -struct spwd* getspnam(char const* name) -{ - setspent(); - while (auto* sp = getspent()) { - if (!strcmp(sp->sp_namp, name)) { - return sp; - } - } - return nullptr; -} - -static bool parse_shadow_entry(ByteString const& line) -{ - auto parts = line.split_view(':', SplitBehavior::KeepEmpty); - if (parts.size() != 9) { - dbgln("getspent(): Malformed entry on line {}", s_line_number); - return false; - } - - s_name = parts[0]; - s_pwdp = parts[1]; - auto& lstchg_string = parts[2]; - auto& min_string = parts[3]; - auto& max_string = parts[4]; - auto& warn_string = parts[5]; - auto& inact_string = parts[6]; - auto& expire_string = parts[7]; - auto& flag_string = parts[8]; - - auto lstchg = lstchg_string.to_number(); - if (!lstchg.has_value()) { - dbgln("getspent(): Malformed lstchg on line {}", s_line_number); - return false; - } - - if (min_string.is_empty()) - min_string = "-1"sv; - auto min_value = min_string.to_number(); - if (!min_value.has_value()) { - dbgln("getspent(): Malformed min value on line {}", s_line_number); - return false; - } - - if (max_string.is_empty()) - max_string = "-1"sv; - auto max_value = max_string.to_number(); - if (!max_value.has_value()) { - dbgln("getspent(): Malformed max value on line {}", s_line_number); - return false; - } - - if (warn_string.is_empty()) - warn_string = "-1"sv; - auto warn = warn_string.to_number(); - if (!warn.has_value()) { - dbgln("getspent(): Malformed warn on line {}", s_line_number); - return false; - } - - if (inact_string.is_empty()) - inact_string = "-1"sv; - auto inact = inact_string.to_number(); - if (!inact.has_value()) { - dbgln("getspent(): Malformed inact on line {}", s_line_number); - return false; - } - - if (expire_string.is_empty()) - expire_string = "-1"sv; - auto expire = expire_string.to_number(); - if (!expire.has_value()) { - dbgln("getspent(): Malformed expire on line {}", s_line_number); - return false; - } - - if (flag_string.is_empty()) - flag_string = "0"sv; - auto flag = flag_string.to_number(); - if (!flag.has_value()) { - dbgln("getspent(): Malformed flag on line {}", s_line_number); - return false; - } - - s_shadow_entry.sp_namp = const_cast(s_name.characters()); - s_shadow_entry.sp_pwdp = const_cast(s_pwdp.characters()); - s_shadow_entry.sp_lstchg = lstchg.value(); - s_shadow_entry.sp_min = min_value.value(); - s_shadow_entry.sp_max = max_value.value(); - s_shadow_entry.sp_warn = warn.value(); - s_shadow_entry.sp_inact = inact.value(); - s_shadow_entry.sp_expire = expire.value(); - s_shadow_entry.sp_flag = flag.value(); - - return true; -} - -struct spwd* getspent() -{ - if (!s_stream) - setspent(); - - while (true) { - if (!s_stream || feof(s_stream)) - return nullptr; - - if (ferror(s_stream)) { - dbgln("getspent(): Read error: {}", strerror(ferror(s_stream))); - return nullptr; - } - - char buffer[1024]; - ++s_line_number; - char* s = fgets(buffer, sizeof(buffer), s_stream); - - // Silently tolerate an empty line at the end. - if ((!s || !s[0]) && feof(s_stream)) - return nullptr; - - ByteString line(s, Chomp); - if (parse_shadow_entry(line)) - return &s_shadow_entry; - // Otherwise, proceed to the next line. - } -} - -static void construct_spwd(struct spwd* sp, char* buf, struct spwd** result) -{ - auto* buf_name = &buf[0]; - auto* buf_pwdp = &buf[s_name.length() + 1]; - - bool ok = true; - ok = ok && s_name.copy_characters_to_buffer(buf_name, s_name.length() + 1); - ok = ok && s_pwdp.copy_characters_to_buffer(buf_pwdp, s_pwdp.length() + 1); - - VERIFY(ok); - - *result = sp; - sp->sp_namp = buf_name; - sp->sp_pwdp = buf_pwdp; -} - -int getspnam_r(char const* name, struct spwd* sp, char* buf, size_t buflen, struct spwd** result) -{ - // FIXME: This is a HACK! - TemporaryChange name_change { s_name, {} }; - TemporaryChange pwdp_change { s_pwdp, {} }; - - setspent(); - bool found = false; - while (auto* sp = getspent()) { - if (!strcmp(sp->sp_namp, name)) { - found = true; - break; - } - } - - if (!found) { - *result = nullptr; - return 0; - } - - auto const total_buffer_length = s_name.length() + s_pwdp.length() + 8; - if (buflen < total_buffer_length) - return ERANGE; - - construct_spwd(sp, buf, result); - return 0; -} - -int putspent(struct spwd* p, FILE* stream) -{ - if (!p || !stream || !p->sp_namp || !p->sp_pwdp) { - errno = EINVAL; - return -1; - } - - auto is_valid_field = [](char const* str) { - return str && !strpbrk(str, ":\n"); - }; - - if (!is_valid_field(p->sp_namp) || !is_valid_field(p->sp_pwdp)) { - errno = EINVAL; - return -1; - } - - int nwritten; - - nwritten = fprintf(stream, "%s:%s:", p->sp_namp, p->sp_pwdp); - if (!nwritten || nwritten < 0) { - errno = ferror(stream); - return -1; - } - - if (p->sp_lstchg != (long int)-1) - nwritten = fprintf(stream, "%ld:", p->sp_lstchg); - else - nwritten = fprintf(stream, "%c", ':'); - if (!nwritten || nwritten < 0) { - errno = ferror(stream); - return -1; - } - - if (p->sp_min != (long int)-1) - nwritten = fprintf(stream, "%ld:", p->sp_min); - else - nwritten = fprintf(stream, "%c", ':'); - if (!nwritten || nwritten < 0) { - errno = ferror(stream); - return -1; - } - - if (p->sp_max != (long int)-1) - nwritten = fprintf(stream, "%ld:", p->sp_max); - else - nwritten = fprintf(stream, "%c", ':'); - if (!nwritten || nwritten < 0) { - errno = ferror(stream); - return -1; - } - - if (p->sp_warn != (long int)-1) - nwritten = fprintf(stream, "%ld:", p->sp_warn); - else - nwritten = fprintf(stream, "%c", ':'); - if (!nwritten || nwritten < 0) { - errno = ferror(stream); - return -1; - } - - if (p->sp_inact != (long int)-1) - nwritten = fprintf(stream, "%ld:", p->sp_inact); - else - nwritten = fprintf(stream, "%c", ':'); - if (!nwritten || nwritten < 0) { - errno = ferror(stream); - return -1; - } - - if (p->sp_expire != (long int)-1) - nwritten = fprintf(stream, "%ld:", p->sp_expire); - else - nwritten = fprintf(stream, "%c", ':'); - if (!nwritten || nwritten < 0) { - errno = ferror(stream); - return -1; - } - - if (p->sp_flag != (unsigned long int)-1) - nwritten = fprintf(stream, "%ld\n", p->sp_flag); - else - nwritten = fprintf(stream, "%c", '\n'); - if (!nwritten || nwritten < 0) { - errno = ferror(stream); - return -1; - } - - return 0; -} -} diff --git a/Userland/Libraries/LibC/shadow.h b/Userland/Libraries/LibC/shadow.h deleted file mode 100644 index 0d3e463931b..00000000000 --- a/Userland/Libraries/LibC/shadow.h +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright (c) 2021, Gunnar Beutner - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include - -__BEGIN_DECLS - -struct spwd { - char* sp_namp; - char* sp_pwdp; - long int sp_lstchg; - long int sp_min; - long int sp_max; - long int sp_warn; - long int sp_inact; - long int sp_expire; - unsigned long int sp_flag; -}; - -struct spwd* getspent(void); -void setspent(void); -void endspent(void); -struct spwd* getspnam(char const* name); -int putspent(struct spwd* p, FILE* stream); - -int getspent_r(struct spwd* spbuf, char* buf, size_t buflen, struct spwd** spbufp); -int getspnam_r(char const* name, struct spwd* spbuf, char* buf, size_t buflen, struct spwd** spbufp); - -int fgetspent_r(FILE* fp, struct spwd* spbuf, char* buf, size_t buflen, struct spwd** spbufp); -int sgetspent_r(char const* s, struct spwd* spbuf, char* buf, size_t buflen, struct spwd** spbufp); - -__END_DECLS diff --git a/Userland/Libraries/LibC/signal.cpp b/Userland/Libraries/LibC/signal.cpp deleted file mode 100644 index e26fb5e8173..00000000000 --- a/Userland/Libraries/LibC/signal.cpp +++ /dev/null @@ -1,270 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -extern "C" { - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/kill.html -int kill(pid_t pid, int sig) -{ - int rc = syscall(SC_kill, pid, sig); - __RETURN_WITH_ERRNO(rc, rc, -1); -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/killpg.html -int killpg(int pgrp, int sig) -{ - int rc = syscall(SC_killpg, pgrp, sig); - __RETURN_WITH_ERRNO(rc, rc, -1); -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/raise.html -int raise(int sig) -{ - // FIXME: Support multi-threaded programs. - return kill(getpid(), sig); -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/signal.html -sighandler_t signal(int signum, sighandler_t handler) -{ - struct sigaction new_act; - struct sigaction old_act; - new_act.sa_handler = handler; - new_act.sa_flags = 0; - new_act.sa_mask = 0; - int rc = sigaction(signum, &new_act, &old_act); - if (rc < 0) - return SIG_ERR; - return old_act.sa_handler; -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/sigaction.html -int sigaction(int signum, const struct sigaction* act, struct sigaction* old_act) -{ - int rc = syscall(SC_sigaction, signum, act, old_act); - __RETURN_WITH_ERRNO(rc, rc, -1); -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/sigemptyset.html -int sigemptyset(sigset_t* set) -{ - *set = 0; - return 0; -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/sigfillset.html -int sigfillset(sigset_t* set) -{ - *set = 0xffffffff; - return 0; -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/sigaddset.html -int sigaddset(sigset_t* set, int sig) -{ - if (sig < 1 || sig > 32) { - errno = EINVAL; - return -1; - } - *set |= 1 << (sig - 1); - return 0; -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/sigaltstack.html -int sigaltstack(stack_t const* ss, stack_t* old_ss) -{ - int rc = syscall(SC_sigaltstack, ss, old_ss); - __RETURN_WITH_ERRNO(rc, rc, -1); -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/sigdelset.html -int sigdelset(sigset_t* set, int sig) -{ - if (sig < 1 || sig > 32) { - errno = EINVAL; - return -1; - } - *set &= ~(1 << (sig - 1)); - return 0; -} - -// https://pubs.opengroup.org/onlinepubs/009696699/functions/siginterrupt.html -int siginterrupt(int sig, int flag) -{ - struct sigaction act; - int rc = sigaction(sig, nullptr, &act); - if (rc < 0) - return rc; - if (flag) - act.sa_flags &= ~SA_RESTART; - else - act.sa_flags |= SA_RESTART; - return sigaction(sig, &act, nullptr); -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/sigismember.html -int sigismember(sigset_t const* set, int sig) -{ - if (sig < 1 || sig > 32) { - errno = EINVAL; - return -1; - } - if (*set & (1 << (sig - 1))) - return 1; - return 0; -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/sigprocmask.html -int sigprocmask(int how, sigset_t const* set, sigset_t* old_set) -{ - int rc = syscall(SC_sigprocmask, how, set, old_set); - __RETURN_WITH_ERRNO(rc, rc, -1); -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/sigpending.html -int sigpending(sigset_t* set) -{ - int rc = syscall(SC_sigpending, set); - __RETURN_WITH_ERRNO(rc, rc, -1); -} - -// Signal 0 (the null signal) and Signal 32 (SIGCANCEL) are deliberately set to null here. -// They are not intended to be resolved by strsignal(), getsignalname() or getsignalbyname(). -#define ENUMERATE_SIGNALS \ - __ENUMERATE_SIGNAL(nullptr, nullptr) \ - __ENUMERATE_SIGNAL("HUP", "Hangup") \ - __ENUMERATE_SIGNAL("INT", "Interrupt") \ - __ENUMERATE_SIGNAL("QUIT", "Quit") \ - __ENUMERATE_SIGNAL("ILL", "Illegal instruction") \ - __ENUMERATE_SIGNAL("TRAP", "Trap") \ - __ENUMERATE_SIGNAL("ABRT", "Aborted") \ - __ENUMERATE_SIGNAL("BUS", "Bus error") \ - __ENUMERATE_SIGNAL("FPE", "Division by zero") \ - __ENUMERATE_SIGNAL("KILL", "Killed") \ - __ENUMERATE_SIGNAL("USR1", "User signal 1") \ - __ENUMERATE_SIGNAL("SEGV", "Segmentation violation") \ - __ENUMERATE_SIGNAL("USR2", "User signal 2") \ - __ENUMERATE_SIGNAL("PIPE", "Broken pipe") \ - __ENUMERATE_SIGNAL("ALRM", "Alarm clock") \ - __ENUMERATE_SIGNAL("TERM", "Terminated") \ - __ENUMERATE_SIGNAL("STKFLT", "Stack fault") \ - __ENUMERATE_SIGNAL("CHLD", "Child exited") \ - __ENUMERATE_SIGNAL("CONT", "Continued") \ - __ENUMERATE_SIGNAL("STOP", "Stopped (signal)") \ - __ENUMERATE_SIGNAL("TSTP", "Stopped") \ - __ENUMERATE_SIGNAL("TTIN", "Stopped (tty input)") \ - __ENUMERATE_SIGNAL("TTOU", "Stopped (tty output)") \ - __ENUMERATE_SIGNAL("URG", "Urgent I/O condition)") \ - __ENUMERATE_SIGNAL("XCPU", "CPU limit exceeded") \ - __ENUMERATE_SIGNAL("XFSZ", "File size limit exceeded") \ - __ENUMERATE_SIGNAL("VTALRM", "Virtual timer expired") \ - __ENUMERATE_SIGNAL("PROF", "Profiling timer expired") \ - __ENUMERATE_SIGNAL("WINCH", "Window changed") \ - __ENUMERATE_SIGNAL("IO", "I/O possible") \ - __ENUMERATE_SIGNAL("INFO", "Power failure") \ - __ENUMERATE_SIGNAL("SYS", "Bad system call") \ - __ENUMERATE_SIGNAL(nullptr, nullptr) - -char const* sys_siglist[NSIG] = { -#define __ENUMERATE_SIGNAL(name, description) description, - ENUMERATE_SIGNALS -#undef __ENUMERATE_SIGNAL -}; - -char const* sys_signame[NSIG] = { -#define __ENUMERATE_SIGNAL(name, description) name, - ENUMERATE_SIGNALS -#undef __ENUMERATE_SIGNAL -}; - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/siglongjmp.html -void siglongjmp(jmp_buf env, int val) -{ - if (env->did_save_signal_mask) { - int rc = sigprocmask(SIG_SETMASK, &env->saved_signal_mask, nullptr); - assert(rc == 0); - } - longjmp(env, val); -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/sigsuspend.html -int sigsuspend(sigset_t const* set) -{ - __pthread_maybe_cancel(); - - int rc = syscall(SC_sigsuspend, set); - __RETURN_WITH_ERRNO(rc, rc, -1); -} - -// https://pubs.opengroup.org/onlinepubs/009604499/functions/sigwait.html -int sigwait(sigset_t const* set, int* sig) -{ - __pthread_maybe_cancel(); - - int rc = syscall(Syscall::SC_sigtimedwait, set, nullptr, nullptr); - VERIFY(rc != 0); - if (rc < 0) - return -rc; - *sig = rc; - return 0; -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/sigwaitinfo.html -int sigwaitinfo(sigset_t const* set, siginfo_t* info) -{ - return sigtimedwait(set, info, nullptr); -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/sigtimedwait.html -int sigtimedwait(sigset_t const* set, siginfo_t* info, struct timespec const* timeout) -{ - __pthread_maybe_cancel(); - - int rc = syscall(Syscall::SC_sigtimedwait, set, info, timeout); - __RETURN_WITH_ERRNO(rc, rc, -1); -} - -int getsignalbyname(char const* name) -{ - VERIFY(name); - StringView name_sv { name, strlen(name) }; - for (size_t i = 1; i < NSIG; ++i) { - if (!sys_signame[i]) - continue; - - StringView signal_name { sys_signame[i], strlen(sys_signame[i]) }; - if (signal_name == name_sv || (name_sv.starts_with("SIG"sv) && signal_name == name_sv.substring_view(3))) - return i; - } - errno = EINVAL; - return -1; -} - -char const* getsignalname(int signal) -{ - if (signal <= 0 || signal >= NSIG) { - errno = EINVAL; - return nullptr; - } - - auto const* result = sys_signame[signal]; - if (!result) - errno = EINVAL; - - return result; -} -} diff --git a/Userland/Libraries/LibC/signal.h b/Userland/Libraries/LibC/signal.h deleted file mode 100644 index 355d13cf547..00000000000 --- a/Userland/Libraries/LibC/signal.h +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -// Includes essentially mandated by POSIX: -// https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/signal.h.html -#include - -#include -#include -#include -#include -#include - -__BEGIN_DECLS - -int kill(pid_t, int sig); -int killpg(int pgrp, int sig); -sighandler_t signal(int sig, sighandler_t); -int pthread_sigmask(int how, sigset_t const* set, sigset_t* old_set); -int sigaction(int sig, const struct sigaction* act, struct sigaction* old_act); -int sigemptyset(sigset_t*); -int sigfillset(sigset_t*); -int sigaddset(sigset_t*, int sig); -int sigaltstack(stack_t const* ss, stack_t* old_ss); -int sigdelset(sigset_t*, int sig); -int siginterrupt(int sig, int flag); -int sigismember(sigset_t const*, int sig); -int sigprocmask(int how, sigset_t const* set, sigset_t* old_set); -int sigpending(sigset_t*); -int sigsuspend(sigset_t const*); -int sigtimedwait(sigset_t const*, siginfo_t*, struct timespec const*); -int sigwait(sigset_t const*, int*); -int sigwaitinfo(sigset_t const*, siginfo_t*); -int raise(int sig); -int getsignalbyname(char const*); -char const* getsignalname(int); - -extern char const* sys_siglist[NSIG]; -extern char const* sys_signame[NSIG]; - -__END_DECLS diff --git a/Userland/Libraries/LibC/spawn.cpp b/Userland/Libraries/LibC/spawn.cpp deleted file mode 100644 index 5d4a3b3bf80..00000000000 --- a/Userland/Libraries/LibC/spawn.cpp +++ /dev/null @@ -1,290 +0,0 @@ -/* - * Copyright (c) 2020, Nico Weber - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -/* posix_spawn and friends - * - * values from POSIX standard unix specification - * - * https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/spawn.h.html - */ - -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -struct posix_spawn_file_actions_state { - Vector, 4> actions; -}; - -extern "C" { - -[[noreturn]] static void posix_spawn_child(char const* path, posix_spawn_file_actions_t const* file_actions, posix_spawnattr_t const* attr, char* const argv[], char* const envp[], int (*exec)(char const*, char* const[], char* const[])) -{ - if (attr) { - short flags = attr->flags; - if (flags & POSIX_SPAWN_RESETIDS) { - if (seteuid(getuid()) < 0) { - perror("posix_spawn seteuid"); - _exit(127); - } - if (setegid(getgid()) < 0) { - perror("posix_spawn setegid"); - _exit(127); - } - } - if (flags & POSIX_SPAWN_SETPGROUP) { - if (setpgid(0, attr->pgroup) < 0) { - perror("posix_spawn setpgid"); - _exit(127); - } - } - if (flags & POSIX_SPAWN_SETSCHEDPARAM) { - if (sched_setparam(0, &attr->schedparam) < 0) { - perror("posix_spawn sched_setparam"); - _exit(127); - } - } - if (flags & POSIX_SPAWN_SETSIGDEF) { - struct sigaction default_action; - default_action.sa_flags = 0; - sigemptyset(&default_action.sa_mask); - default_action.sa_handler = SIG_DFL; - - sigset_t sigdefault = attr->sigdefault; - for (int i = 0; i < NSIG; ++i) { - if (sigismember(&sigdefault, i) && sigaction(i, &default_action, nullptr) < 0) { - perror("posix_spawn sigaction"); - _exit(127); - } - } - } - if (flags & POSIX_SPAWN_SETSIGMASK) { - if (sigprocmask(SIG_SETMASK, &attr->sigmask, nullptr) < 0) { - perror("posix_spawn sigprocmask"); - _exit(127); - } - } - if (flags & POSIX_SPAWN_SETSID) { - if (setsid() < 0) { - perror("posix_spawn setsid"); - _exit(127); - } - } - - // FIXME: POSIX_SPAWN_SETSCHEDULER - } - - if (file_actions) { - for (auto const& action : file_actions->state->actions) { - if (action() < 0) { - perror("posix_spawn file action"); - _exit(127); - } - } - } - - exec(path, argv, envp); - perror("posix_spawn exec"); - _exit(127); -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/posix_spawn.html -int posix_spawn(pid_t* out_pid, char const* path, posix_spawn_file_actions_t const* file_actions, posix_spawnattr_t const* attr, char* const argv[], char* const envp[]) -{ - pid_t child_pid = fork(); - if (child_pid < 0) - return errno; - - if (child_pid != 0) { - *out_pid = child_pid; - return 0; - } - - posix_spawn_child(path, file_actions, attr, argv, envp, execve); -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/posix_spawnp.html -int posix_spawnp(pid_t* out_pid, char const* file, posix_spawn_file_actions_t const* file_actions, posix_spawnattr_t const* attr, char* const argv[], char* const envp[]) -{ - pid_t child_pid = fork(); - if (child_pid < 0) - return errno; - - if (child_pid != 0) { - *out_pid = child_pid; - return 0; - } - - posix_spawn_child(file, file_actions, attr, argv, envp, execvpe); -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/posix_spawn_file_actions_addchdir.html -int posix_spawn_file_actions_addchdir(posix_spawn_file_actions_t* actions, char const* path) -{ - actions->state->actions.append([path]() { return chdir(path); }); - return 0; -} - -int posix_spawn_file_actions_addfchdir(posix_spawn_file_actions_t* actions, int fd) -{ - actions->state->actions.append([fd]() { return fchdir(fd); }); - return 0; -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/posix_spawn_file_actions_addclose.html -int posix_spawn_file_actions_addclose(posix_spawn_file_actions_t* actions, int fd) -{ - actions->state->actions.append([fd]() { return close(fd); }); - return 0; -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/posix_spawn_file_actions_adddup2.html -int posix_spawn_file_actions_adddup2(posix_spawn_file_actions_t* actions, int old_fd, int new_fd) -{ - actions->state->actions.append([old_fd, new_fd]() { return dup2(old_fd, new_fd); }); - return 0; -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/posix_spawn_file_actions_addopen.html -int posix_spawn_file_actions_addopen(posix_spawn_file_actions_t* actions, int want_fd, char const* path, int flags, mode_t mode) -{ - actions->state->actions.append([want_fd, path, flags, mode]() { - int opened_fd = open(path, flags, mode); - if (opened_fd < 0 || opened_fd == want_fd) - return opened_fd; - if (int rc = dup2(opened_fd, want_fd); rc < 0) - return rc; - return close(opened_fd); - }); - return 0; -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/posix_spawn_file_actions_destroy.html -int posix_spawn_file_actions_destroy(posix_spawn_file_actions_t* actions) -{ - delete actions->state; - return 0; -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/posix_spawn_file_actions_init.html -int posix_spawn_file_actions_init(posix_spawn_file_actions_t* actions) -{ - actions->state = new posix_spawn_file_actions_state; - return 0; -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/posix_spawnattr_destroy.html -int posix_spawnattr_destroy(posix_spawnattr_t*) -{ - return 0; -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/posix_spawnattr_getflags.html -int posix_spawnattr_getflags(posix_spawnattr_t const* attr, short* out_flags) -{ - *out_flags = attr->flags; - return 0; -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/posix_spawnattr_getpgroup.html -int posix_spawnattr_getpgroup(posix_spawnattr_t const* attr, pid_t* out_pgroup) -{ - *out_pgroup = attr->pgroup; - return 0; -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/posix_spawnattr_getschedparam.html -int posix_spawnattr_getschedparam(posix_spawnattr_t const* attr, struct sched_param* out_schedparam) -{ - *out_schedparam = attr->schedparam; - return 0; -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/posix_spawnattr_getschedpolicy.html -int posix_spawnattr_getschedpolicy(posix_spawnattr_t const* attr, int* out_schedpolicy) -{ - *out_schedpolicy = attr->schedpolicy; - return 0; -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/posix_spawnattr_getsigdefault.html -int posix_spawnattr_getsigdefault(posix_spawnattr_t const* attr, sigset_t* out_sigdefault) -{ - *out_sigdefault = attr->sigdefault; - return 0; -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/posix_spawnattr_getsigmask.html -int posix_spawnattr_getsigmask(posix_spawnattr_t const* attr, sigset_t* out_sigmask) -{ - *out_sigmask = attr->sigmask; - return 0; -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/posix_spawnattr_init.html -int posix_spawnattr_init(posix_spawnattr_t* attr) -{ - attr->flags = 0; - attr->pgroup = 0; - // attr->schedparam intentionally not written; its default value is unspecified. - // attr->schedpolicy intentionally not written; its default value is unspecified. - sigemptyset(&attr->sigdefault); - // attr->sigmask intentionally not written; its default value is unspecified. - return 0; -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/posix_spawnattr_setflags.html -int posix_spawnattr_setflags(posix_spawnattr_t* attr, short flags) -{ - if (flags & ~(POSIX_SPAWN_RESETIDS | POSIX_SPAWN_SETPGROUP | POSIX_SPAWN_SETSCHEDPARAM | POSIX_SPAWN_SETSCHEDULER | POSIX_SPAWN_SETSIGDEF | POSIX_SPAWN_SETSIGMASK | POSIX_SPAWN_SETSID)) - return EINVAL; - - attr->flags = flags; - return 0; -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/posix_spawnattr_setpgroup.html -int posix_spawnattr_setpgroup(posix_spawnattr_t* attr, pid_t pgroup) -{ - attr->pgroup = pgroup; - return 0; -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/posix_spawnattr_setschedparam.html -int posix_spawnattr_setschedparam(posix_spawnattr_t* attr, const struct sched_param* schedparam) -{ - attr->schedparam = *schedparam; - return 0; -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/posix_spawnattr_setschedpolicy.html -int posix_spawnattr_setschedpolicy(posix_spawnattr_t* attr, int schedpolicy) -{ - attr->schedpolicy = schedpolicy; - return 0; -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/posix_spawnattr_setsigdefault.html -int posix_spawnattr_setsigdefault(posix_spawnattr_t* attr, sigset_t const* sigdefault) -{ - attr->sigdefault = *sigdefault; - return 0; -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/posix_spawnattr_setsigmask.html -int posix_spawnattr_setsigmask(posix_spawnattr_t* attr, sigset_t const* sigmask) -{ - attr->sigmask = *sigmask; - return 0; -} -} diff --git a/Userland/Libraries/LibC/spawn.h b/Userland/Libraries/LibC/spawn.h deleted file mode 100644 index 9458628da33..00000000000 --- a/Userland/Libraries/LibC/spawn.h +++ /dev/null @@ -1,81 +0,0 @@ -/* - * Copyright (c) 2020, Nico Weber - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -/* posix_spawn and friends - * - * values from POSIX standard unix specification - * - * https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/spawn.h.html - */ - -#pragma once - -// Includes essentially mandated by POSIX: -// https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/spawn.h.html -#include -#include - -#include -#include - -__BEGIN_DECLS - -enum { - POSIX_SPAWN_RESETIDS = 1 << 0, - POSIX_SPAWN_SETPGROUP = 1 << 1, - - POSIX_SPAWN_SETSCHEDPARAM = 1 << 2, - POSIX_SPAWN_SETSCHEDULER = 1 << 3, - - POSIX_SPAWN_SETSIGDEF = 1 << 4, - POSIX_SPAWN_SETSIGMASK = 1 << 5, - - POSIX_SPAWN_SETSID = 1 << 6, -}; - -#define POSIX_SPAWN_SETSID POSIX_SPAWN_SETSID - -struct posix_spawn_file_actions_state; -typedef struct { - struct posix_spawn_file_actions_state* state; -} posix_spawn_file_actions_t; - -typedef struct { - short flags; - pid_t pgroup; - struct sched_param schedparam; - int schedpolicy; - sigset_t sigdefault; - sigset_t sigmask; -} posix_spawnattr_t; - -int posix_spawn(pid_t*, char const*, posix_spawn_file_actions_t const*, posix_spawnattr_t const*, char* const argv[], char* const envp[]); -int posix_spawnp(pid_t*, char const*, posix_spawn_file_actions_t const*, posix_spawnattr_t const*, char* const argv[], char* const envp[]); - -int posix_spawn_file_actions_addchdir(posix_spawn_file_actions_t*, char const*); -int posix_spawn_file_actions_addfchdir(posix_spawn_file_actions_t*, int); -int posix_spawn_file_actions_addclose(posix_spawn_file_actions_t*, int); -int posix_spawn_file_actions_adddup2(posix_spawn_file_actions_t*, int old_fd, int new_fd); -int posix_spawn_file_actions_addopen(posix_spawn_file_actions_t*, int fd, char const*, int flags, mode_t); -int posix_spawn_file_actions_destroy(posix_spawn_file_actions_t*); -int posix_spawn_file_actions_init(posix_spawn_file_actions_t*); - -int posix_spawnattr_destroy(posix_spawnattr_t*); -int posix_spawnattr_getflags(posix_spawnattr_t const*, short*); -int posix_spawnattr_getpgroup(posix_spawnattr_t const*, pid_t*); -int posix_spawnattr_getschedparam(posix_spawnattr_t const*, struct sched_param*); -int posix_spawnattr_getschedpolicy(posix_spawnattr_t const*, int*); -int posix_spawnattr_getsigdefault(posix_spawnattr_t const*, sigset_t*); -int posix_spawnattr_getsigmask(posix_spawnattr_t const*, sigset_t*); -int posix_spawnattr_init(posix_spawnattr_t*); -int posix_spawnattr_setflags(posix_spawnattr_t*, short); -int posix_spawnattr_setpgroup(posix_spawnattr_t*, pid_t); -int posix_spawnattr_setschedparam(posix_spawnattr_t*, const struct sched_param*); -int posix_spawnattr_setschedpolicy(posix_spawnattr_t*, int); -int posix_spawnattr_setsigdefault(posix_spawnattr_t*, sigset_t const*); -int posix_spawnattr_setsigmask(posix_spawnattr_t*, sigset_t const*); - -__END_DECLS diff --git a/Userland/Libraries/LibC/ssp.cpp b/Userland/Libraries/LibC/ssp.cpp deleted file mode 100644 index 2c98c524b46..00000000000 --- a/Userland/Libraries/LibC/ssp.cpp +++ /dev/null @@ -1,20 +0,0 @@ -/* - * Copyright (c) 2021, Brian Gianforcaro - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include -#include - -extern "C" [[gnu::noreturn, gnu::no_stack_protector]] void __stack_chk_fail() -{ - dbgln("Error: USERSPACE({}) Stack protector failure, stack smashing detected!", getpid()); - if (__stdio_is_initialized) - warnln("Error: Stack protector failure, stack smashing detected!"); - abort(); -} diff --git a/Userland/Libraries/LibC/stat.cpp b/Userland/Libraries/LibC/stat.cpp deleted file mode 100644 index 769634b971a..00000000000 --- a/Userland/Libraries/LibC/stat.cpp +++ /dev/null @@ -1,131 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -extern "C" { - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/umask.html -mode_t umask(mode_t mask) -{ - return syscall(SC_umask, mask); -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/mkdir.html -int mkdir(char const* pathname, mode_t mode) -{ - return mkdirat(AT_FDCWD, pathname, mode); -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/mkdirat.html -int mkdirat(int dirfd, char const* pathname, mode_t mode) -{ - if (!pathname) { - errno = EFAULT; - return -1; - } - int rc = syscall(SC_mkdir, dirfd, pathname, strlen(pathname), mode); - __RETURN_WITH_ERRNO(rc, rc, -1); -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/chmod.html -int chmod(char const* pathname, mode_t mode) -{ - return fchmodat(AT_FDCWD, pathname, mode, 0); -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/fchmodat.html -int fchmodat(int dirfd, char const* pathname, mode_t mode, int flags) -{ - if (!pathname) { - errno = EFAULT; - return -1; - } - - if (flags & ~AT_SYMLINK_NOFOLLOW) { - errno = EINVAL; - return -1; - } - - Syscall::SC_chmod_params params { - dirfd, - { pathname, strlen(pathname) }, - mode, - !(flags & AT_SYMLINK_NOFOLLOW) - }; - int rc = syscall(SC_chmod, ¶ms); - __RETURN_WITH_ERRNO(rc, rc, -1); -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/fchmod.html -int fchmod(int fd, mode_t mode) -{ - int rc = syscall(SC_fchmod, fd, mode); - __RETURN_WITH_ERRNO(rc, rc, -1); -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/mkfifo.html -int mkfifo(char const* pathname, mode_t mode) -{ - return mknod(pathname, mode | S_IFIFO, 0); -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/mkfifoat.html -int mkfifoat(int dirfd, char const* pathname, mode_t mode) -{ - return mknodat(dirfd, pathname, mode | S_IFIFO, 0); -} - -static int do_stat(int dirfd, char const* path, struct stat* statbuf, bool follow_symlinks) -{ - if (!path) { - errno = EFAULT; - return -1; - } - Syscall::SC_stat_params params { { path, strlen(path) }, statbuf, dirfd, follow_symlinks }; - int rc = syscall(SC_stat, ¶ms); - __RETURN_WITH_ERRNO(rc, rc, -1); -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/lstat.html -int lstat(char const* path, struct stat* statbuf) -{ - return do_stat(AT_FDCWD, path, statbuf, false); -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/stat.html -int stat(char const* path, struct stat* statbuf) -{ - return do_stat(AT_FDCWD, path, statbuf, true); -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/fstat.html -int fstat(int fd, struct stat* statbuf) -{ - int rc = syscall(SC_fstat, fd, statbuf); - __RETURN_WITH_ERRNO(rc, rc, -1); -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/fstatat.html -int fstatat(int fd, char const* path, struct stat* statbuf, int flags) -{ - return do_stat(fd, path, statbuf, !(flags & AT_SYMLINK_NOFOLLOW)); -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/futimens.html -int futimens(int fd, struct timespec const times[2]) -{ - return __utimens(fd, nullptr, times, 0); -} -} diff --git a/Userland/Libraries/LibC/stdarg.h b/Userland/Libraries/LibC/stdarg.h deleted file mode 100644 index 37145b5bf6e..00000000000 --- a/Userland/Libraries/LibC/stdarg.h +++ /dev/null @@ -1,13 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -typedef __builtin_va_list va_list; - -#define va_start(v, l) __builtin_va_start(v, l) -#define va_end(v) __builtin_va_end(v) -#define va_arg(v, l) __builtin_va_arg(v, l) diff --git a/Userland/Libraries/LibC/stdint.h b/Userland/Libraries/LibC/stdint.h deleted file mode 100644 index f16fc160ba2..00000000000 --- a/Userland/Libraries/LibC/stdint.h +++ /dev/null @@ -1,11 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include - -#include diff --git a/Userland/Libraries/LibC/stdio.cpp b/Userland/Libraries/LibC/stdio.cpp deleted file mode 100644 index 32c6e5851c8..00000000000 --- a/Userland/Libraries/LibC/stdio.cpp +++ /dev/null @@ -1,1414 +0,0 @@ -/* - * Copyright (c) 2018-2021, Andreas Kling - * Copyright (c) 2020, Sergey Bugaev - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -static constinit pthread_mutex_t s_open_streams_lock = __PTHREAD_MUTEX_INITIALIZER; - -// The list of open files is initialized in __stdio_init. -// We cannot rely on global constructors to initialize it, because it must -// be initialized before other global constructors run. Similarly, we cannot -// allow global destructors to destruct it. -alignas(FILE::List) static u8 s_open_streams_storage[sizeof(FILE::List)]; -static FILE::List* const s_open_streams = reinterpret_cast(s_open_streams_storage); - -FILE::~FILE() -{ - bool already_closed = m_fd == -1; - VERIFY(already_closed); -} - -FILE* FILE::create(int fd, int mode) -{ - void* file_location = calloc(1, sizeof(FILE)); - if (file_location == nullptr) - return nullptr; - auto* file = new (file_location) FILE(fd, mode); - LibC::MutexLocker locker(s_open_streams_lock); - s_open_streams->append(*file); - return file; -} - -bool FILE::close() -{ - bool flush_ok = flush(); - int rc = ::close(m_fd); - m_fd = -1; - if (!flush_ok) { - // Restore the original error from flush(). - errno = m_error; - } - return flush_ok && rc == 0; -} - -bool FILE::flush() -{ - if (m_mode & O_WRONLY && m_buffer.may_use()) { - // When open for writing, write out all the buffered data. - while (m_buffer.is_not_empty()) { - bool ok = write_from_buffer(); - if (!ok) - return false; - } - } - if (m_mode & O_RDONLY) { - // When open for reading, just drop the buffered data. - if constexpr (sizeof(size_t) >= sizeof(off_t)) - VERIFY(m_buffer.buffered_size() <= NumericLimits::max()); - off_t had_buffered = static_cast(m_buffer.buffered_size()); - m_buffer.drop(); - // Attempt to reset the underlying file position to what the user - // expects. - if (lseek(m_fd, -had_buffered, SEEK_CUR) < 0) { - if (errno == ESPIPE) { - // We can't set offset on this file; oh well, the user will just - // have to cope. - errno = 0; - } else { - return false; - } - } - } - - return true; -} - -void FILE::purge() -{ - m_buffer.drop(); -} - -size_t FILE::pending() -{ - if (m_mode & O_RDONLY) { - return 0; - } - - // FIXME: Check if our buffer is a write buffer, and only count those bytes. - return m_buffer.buffered_size(); -} - -ssize_t FILE::do_read(u8* data, size_t size) -{ - int nread = ::read(m_fd, data, size); - - if (nread < 0) { - m_error = errno; - } else if (nread == 0) { - m_eof = true; - } - return nread; -} - -ssize_t FILE::do_write(u8 const* data, size_t size) -{ - int nwritten = ::write(m_fd, data, size); - - if (nwritten < 0) - m_error = errno; - return nwritten; -} - -bool FILE::read_into_buffer() -{ - m_buffer.realize(m_fd); - - size_t available_size; - u8* data = m_buffer.begin_enqueue(available_size); - // If we want to read, the buffer must have some space! - VERIFY(available_size); - - ssize_t nread = do_read(data, available_size); - - if (nread <= 0) - return false; - - m_buffer.did_enqueue(nread); - return true; -} - -bool FILE::write_from_buffer() -{ - size_t size; - u8 const* data = m_buffer.begin_dequeue(size); - // If we want to write, the buffer must have something in it! - VERIFY(size); - - ssize_t nwritten = do_write(data, size); - - if (nwritten < 0) - return false; - - m_buffer.did_dequeue(nwritten); - return true; -} - -size_t FILE::read(u8* data, size_t size) -{ - size_t total_read = 0; - - m_flags |= Flags::LastRead; - m_flags &= ~Flags::LastWrite; - - while (size > 0) { - size_t actual_size; - - if (m_buffer.may_use()) { - // Let's see if the buffer has something queued for us. - size_t queued_size; - u8 const* queued_data = m_buffer.begin_dequeue(queued_size); - if (queued_size == 0) { - // Nothing buffered; we're going to have to read some. - bool read_some_more = read_into_buffer(); - if (read_some_more) { - // Great, now try this again. - continue; - } - return total_read; - } - actual_size = min(size, queued_size); - memcpy(data, queued_data, actual_size); - m_buffer.did_dequeue(actual_size); - } else { - // Read directly into the user buffer. - ssize_t nread = do_read(data, size); - if (nread <= 0) - return total_read; - actual_size = nread; - } - - total_read += actual_size; - data += actual_size; - size -= actual_size; - } - - return total_read; -} - -size_t FILE::write(u8 const* data, size_t size) -{ - size_t total_written = 0; - - m_flags &= ~Flags::LastRead; - m_flags |= Flags::LastWrite; - - while (size > 0) { - size_t actual_size; - - if (m_buffer.may_use()) { - m_buffer.realize(m_fd); - // Try writing into the buffer. - size_t available_size; - u8* buffer_data = m_buffer.begin_enqueue(available_size); - if (available_size == 0) { - // There's no space in the buffer; we're going to free some. - bool freed_some_space = write_from_buffer(); - if (freed_some_space) { - // Great, now try this again. - continue; - } - return total_written; - } - actual_size = min(size, available_size); - memcpy(buffer_data, data, actual_size); - m_buffer.did_enqueue(actual_size); - // See if we have to flush it. - if (m_buffer.mode() == _IOLBF) { - bool includes_newline = memchr(data, '\n', actual_size); - if (includes_newline) - flush(); - } - } else { - // Write directly from the user buffer. - ssize_t nwritten = do_write(data, size); - if (nwritten < 0) - return total_written; - actual_size = nwritten; - } - - total_written += actual_size; - data += actual_size; - size -= actual_size; - } - - return total_written; -} - -template -bool FILE::gets(T* data, size_t size) -{ - // gets() is a lot like read(), but it is different enough in how it - // processes newlines and null-terminates the buffer that it deserves a - // separate implementation. - size_t total_read = 0; - - if (size == 0) - return false; - - m_flags |= Flags::LastRead; - m_flags &= ~Flags::LastWrite; - - while (size > 1) { - if (m_buffer.may_use()) { - // Let's see if the buffer has something queued for us. - size_t queued_size; - T const* queued_data = bit_cast(m_buffer.begin_dequeue(queued_size)); - queued_size /= sizeof(T); - if (queued_size == 0) { - // Nothing buffered; we're going to have to read some. - bool read_some_more = read_into_buffer(); - if (read_some_more) { - // Great, now try this again. - continue; - } - *data = 0; - return total_read > 0; - } - size_t actual_size = min(size - 1, queued_size); - T const* newline = nullptr; - for (size_t i = 0; i < actual_size; ++i) { - if (queued_data[i] != '\n') - continue; - - newline = &queued_data[i]; - actual_size = i + 1; - break; - } - memcpy(data, queued_data, actual_size * sizeof(T)); - m_buffer.did_dequeue(actual_size * sizeof(T)); - total_read += actual_size; - data += actual_size; - size -= actual_size; - if (newline) - break; - } else { - // Sadly, we have to actually read these characters one by one. - T value; - ssize_t nread = do_read(bit_cast(&value), sizeof(T)); - if (nread <= 0) { - *data = 0; - return total_read > 0; - } - VERIFY(nread == sizeof(T)); - *data = value; - total_read++; - data++; - size--; - if (value == '\n') - break; - } - } - - *data = 0; - return total_read > 0; -} - -int FILE::seek(off_t offset, int whence) -{ - bool ok = flush(); - if (!ok) - return -1; - - off_t off = lseek(m_fd, offset, whence); - if (off < 0) { - // Note: do not set m_error. - return off; - } - - m_eof = false; - return 0; -} - -off_t FILE::tell() -{ - bool ok = flush(); - if (!ok) - return -1; - - return lseek(m_fd, 0, SEEK_CUR); -} - -void FILE::reopen(int fd, int mode) -{ - // Dr. POSIX says: "Failure to flush or close the file descriptor - // successfully shall be ignored" - // and so we ignore any failures these two might have. - flush(); - close(); - - // Just in case flush() and close() didn't drop the buffer. - m_buffer.drop(); - - m_fd = fd; - m_mode = mode; - m_error = 0; - m_eof = false; -} - -u8 const* FILE::readptr(size_t& available_size) -{ - return m_buffer.begin_dequeue(available_size); -} - -void FILE::readptr_increase(size_t increment) -{ - m_buffer.did_dequeue(increment); -} - -FILE::Buffer::~Buffer() -{ - if (m_data_is_malloced) - free(m_data); -} - -bool FILE::Buffer::may_use() const -{ - return m_ungotten != 0u || m_mode != _IONBF; -} - -void FILE::Buffer::realize(int fd) -{ - if (m_mode == -1) - m_mode = isatty(fd) ? _IOLBF : _IOFBF; - - if (m_mode != _IONBF && m_data == nullptr) { - m_data = reinterpret_cast(malloc(m_capacity)); - m_data_is_malloced = true; - } -} - -void FILE::Buffer::setbuf(u8* data, int mode, size_t size) -{ - drop(); - m_mode = mode; - if (data != nullptr) { - m_data = data; - m_capacity = size; - } -} - -void FILE::Buffer::drop() -{ - if (m_data_is_malloced) { - free(m_data); - m_data = nullptr; - m_data_is_malloced = false; - } - m_begin = m_end = 0; - m_empty = true; - m_ungotten = 0u; -} - -size_t FILE::Buffer::buffered_size() const -{ - // Note: does not include the ungetc() buffer. - - if (m_empty) - return 0; - - if (m_begin < m_end) - return m_end - m_begin; - else - return m_capacity - (m_begin - m_end); -} - -u8 const* FILE::Buffer::begin_dequeue(size_t& available_size) const -{ - if (m_ungotten != 0u) { - auto available_bytes = count_trailing_zeroes(m_ungotten) + 1; - available_size = available_bytes; - return &m_unget_buffer[unget_buffer_size - available_bytes]; - } - - if (m_empty) { - available_size = 0; - return nullptr; - } - - if (m_begin < m_end) - available_size = m_end - m_begin; - else - available_size = m_capacity - m_begin; - - return &m_data[m_begin]; -} - -void FILE::Buffer::did_dequeue(size_t actual_size) -{ - VERIFY(actual_size > 0); - - if (m_ungotten != 0u) { - VERIFY(actual_size <= static_cast(popcount(m_ungotten & ungotten_mask))); - auto available_bytes = count_trailing_zeroes(m_ungotten); - m_ungotten &= (0xffffffffu << (actual_size + available_bytes)); - return; - } - - m_begin += actual_size; - - VERIFY(m_begin <= m_capacity); - if (m_begin == m_capacity) { - // Wrap around. - m_begin = 0; - } - - if (m_begin == m_end) { - m_empty = true; - // As an optimization, move both pointers to the beginning of the - // buffer, so that more consecutive space is available next time. - m_begin = m_end = 0; - } -} - -u8* FILE::Buffer::begin_enqueue(size_t& available_size) const -{ - VERIFY(m_data != nullptr); - - if (m_begin < m_end || m_empty) - available_size = m_capacity - m_end; - else - available_size = m_begin - m_end; - - return const_cast(&m_data[m_end]); -} - -void FILE::Buffer::did_enqueue(size_t actual_size) -{ - VERIFY(m_data != nullptr); - VERIFY(actual_size > 0); - - m_end += actual_size; - - VERIFY(m_end <= m_capacity); - if (m_end == m_capacity) { - // Wrap around. - m_end = 0; - } - - m_empty = false; -} - -bool FILE::Buffer::enqueue_front(u8 byte) -{ - size_t placement_index; - if (m_ungotten == 0u) { - placement_index = 3u; - m_ungotten = 1u; - } else { - auto first_zero_index = count_trailing_zeroes(bit_cast(~m_ungotten)); // Thanks C. - if (first_zero_index >= unget_buffer_size) { - // Sorry, the place is already taken! - return false; - } - placement_index = unget_buffer_size - first_zero_index - 1; - m_ungotten |= (1 << first_zero_index); - } - - m_unget_buffer[placement_index] = byte; - return true; -} - -void FILE::lock() -{ - pthread_mutex_lock(&m_mutex); -} - -void FILE::unlock() -{ - pthread_mutex_unlock(&m_mutex); -} - -extern "C" { - -alignas(FILE) static u8 default_streams[3][sizeof(FILE)]; -FILE* stdin = reinterpret_cast(&default_streams[0]); -FILE* stdout = reinterpret_cast(&default_streams[1]); -FILE* stderr = reinterpret_cast(&default_streams[2]); - -void __stdio_init() -{ - new (s_open_streams) FILE::List(); - new (stdin) FILE(0, O_RDONLY); - new (stdout) FILE(1, O_WRONLY); - new (stderr) FILE(2, O_WRONLY); - stderr->setbuf(nullptr, _IONBF, 0); - s_open_streams->append(*stdin); - s_open_streams->append(*stdout); - s_open_streams->append(*stderr); - __stdio_is_initialized = true; -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/setvbuf.html -int setvbuf(FILE* stream, char* buf, int mode, size_t size) -{ - VERIFY(stream); - ScopedFileLock lock(stream); - if (mode != _IONBF && mode != _IOLBF && mode != _IOFBF) { - errno = EINVAL; - return -1; - } - stream->setbuf(reinterpret_cast(buf), mode, size); - return 0; -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/setbuf.html -void setbuf(FILE* stream, char* buf) -{ - setvbuf(stream, buf, buf ? _IOFBF : _IONBF, BUFSIZ); -} - -void setlinebuf(FILE* stream) -{ - setvbuf(stream, nullptr, _IOLBF, 0); -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/fileno.html -int fileno(FILE* stream) -{ - VERIFY(stream); - ScopedFileLock lock(stream); - return stream->fileno(); -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/feof.html -int feof(FILE* stream) -{ - VERIFY(stream); - ScopedFileLock lock(stream); - return stream->eof(); -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/fflush.html -int fflush(FILE* stream) -{ - if (!stream) { - int rc = 0; - LibC::MutexLocker locker(s_open_streams_lock); - for (auto& file : *s_open_streams) { - ScopedFileLock lock(&file); - rc = file.flush() ? rc : EOF; - } - return rc; - } - ScopedFileLock lock(stream); - return stream->flush() ? 0 : EOF; -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/fgets.html -char* fgets(char* buffer, int size, FILE* stream) -{ - VERIFY(stream); - ScopedFileLock lock(stream); - bool ok = stream->gets(reinterpret_cast(buffer), size); - return ok ? buffer : nullptr; -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/fgetc.html -int fgetc(FILE* stream) -{ - VERIFY(stream); - unsigned char ch; - size_t nread = fread(&ch, sizeof(unsigned char), 1, stream); - if (nread == 1) { - return ch; - } - return EOF; -} - -int fgetc_unlocked(FILE* stream) -{ - VERIFY(stream); - unsigned char ch; - size_t nread = fread_unlocked(&ch, sizeof(unsigned char), 1, stream); - if (nread == 1) - return ch; - return EOF; -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/getc.html -int getc(FILE* stream) -{ - return fgetc(stream); -} - -int getc_unlocked(FILE* stream) -{ - return fgetc_unlocked(stream); -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/getchar.html -int getchar() -{ - return getc(stdin); -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/getdelim.html -ssize_t getdelim(char** lineptr, size_t* n, int delim, FILE* stream) -{ - if (!lineptr || !n) { - errno = EINVAL; - return -1; - } - - if (*lineptr == nullptr || *n == 0) { - *n = BUFSIZ; - if ((*lineptr = static_cast(malloc(*n))) == nullptr) { - return -1; - } - } - - char* ptr; - char* eptr; - for (ptr = *lineptr, eptr = *lineptr + *n;;) { - int c = fgetc(stream); - if (c == -1) { - if (feof(stream)) { - *ptr = '\0'; - return ptr == *lineptr ? -1 : ptr - *lineptr; - } else { - return -1; - } - } - *ptr++ = c; - if (c == delim) { - *ptr = '\0'; - return ptr - *lineptr; - } - if (ptr + 2 >= eptr) { - char* nbuf; - size_t nbuf_sz = *n * 2; - ssize_t d = ptr - *lineptr; - if ((nbuf = static_cast(realloc(*lineptr, nbuf_sz))) == nullptr) { - return -1; - } - *lineptr = nbuf; - *n = nbuf_sz; - eptr = nbuf + nbuf_sz; - ptr = nbuf + d; - } - } -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/getline.html -ssize_t getline(char** lineptr, size_t* n, FILE* stream) -{ - return getdelim(lineptr, n, '\n', stream); -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/ungetc.html -int ungetc(int c, FILE* stream) -{ - VERIFY(stream); - ScopedFileLock lock(stream); - bool ok = stream->ungetc(c); - return ok ? c : EOF; -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/fputc.html -int fputc(int ch, FILE* stream) -{ - VERIFY(stream); - u8 byte = ch; - ScopedFileLock lock(stream); - size_t nwritten = stream->write(&byte, 1); - if (nwritten == 0) - return EOF; - VERIFY(nwritten == 1); - return byte; -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/putc.html -int putc(int ch, FILE* stream) -{ - return fputc(ch, stream); -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/putchar.html -int putchar(int ch) -{ - return putc(ch, stdout); -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/fputs.html -int fputs(char const* s, FILE* stream) -{ - VERIFY(stream); - size_t len = strlen(s); - ScopedFileLock lock(stream); - size_t nwritten = stream->write(reinterpret_cast(s), len); - if (nwritten < len) - return EOF; - return 1; -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/puts.html -int puts(char const* s) -{ - int rc = fputs(s, stdout); - if (rc == EOF) - return EOF; - return fputc('\n', stdout); -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/clearerr.html -void clearerr(FILE* stream) -{ - VERIFY(stream); - ScopedFileLock lock(stream); - stream->clear_err(); -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/ferror.html -int ferror(FILE* stream) -{ - VERIFY(stream); - ScopedFileLock lock(stream); - return stream->error(); -} - -size_t fread_unlocked(void* ptr, size_t size, size_t nmemb, FILE* stream) -{ - VERIFY(stream); - VERIFY(!Checked::multiplication_would_overflow(size, nmemb)); - - size_t nread = stream->read(reinterpret_cast(ptr), size * nmemb); - if (!nread) - return 0; - return nread / size; -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/fread.html -size_t fread(void* ptr, size_t size, size_t nmemb, FILE* stream) -{ - VERIFY(stream); - ScopedFileLock lock(stream); - return fread_unlocked(ptr, size, nmemb, stream); -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/fwrite.html -size_t fwrite(void const* ptr, size_t size, size_t nmemb, FILE* stream) -{ - VERIFY(stream); - VERIFY(!Checked::multiplication_would_overflow(size, nmemb)); - - ScopedFileLock lock(stream); - size_t nwritten = stream->write(reinterpret_cast(ptr), size * nmemb); - if (!nwritten) - return 0; - return nwritten / size; -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/fseek.html -int fseek(FILE* stream, long offset, int whence) -{ - VERIFY(stream); - ScopedFileLock lock(stream); - return stream->seek(offset, whence); -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/fseeko.html -int fseeko(FILE* stream, off_t offset, int whence) -{ - VERIFY(stream); - ScopedFileLock lock(stream); - return stream->seek(offset, whence); -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/ftell.html -long ftell(FILE* stream) -{ - VERIFY(stream); - ScopedFileLock lock(stream); - return stream->tell(); -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/ftello.html -off_t ftello(FILE* stream) -{ - VERIFY(stream); - ScopedFileLock lock(stream); - return stream->tell(); -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/fgetpos.html -int fgetpos(FILE* stream, fpos_t* pos) -{ - VERIFY(stream); - VERIFY(pos); - - ScopedFileLock lock(stream); - off_t val = stream->tell(); - if (val == -1L) - return 1; - - *pos = val; - return 0; -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/fsetpos.html -int fsetpos(FILE* stream, fpos_t const* pos) -{ - VERIFY(stream); - VERIFY(pos); - - ScopedFileLock lock(stream); - return stream->seek(*pos, SEEK_SET); -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/rewind.html -void rewind(FILE* stream) -{ - fseek(stream, 0, SEEK_SET); - clearerr(stream); -} - -ALWAYS_INLINE void stdout_putch(char*&, char ch) -{ - putchar(ch); -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/vfprintf.html -int vfprintf(FILE* stream, char const* fmt, va_list ap) -{ - return printf_internal([stream](auto, char ch) { fputc(ch, stream); }, nullptr, fmt, ap); -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/fprintf.html -int fprintf(FILE* stream, char const* fmt, ...) -{ - va_list ap; - va_start(ap, fmt); - int ret = vfprintf(stream, fmt, ap); - va_end(ap); - return ret; -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/vdprintf.html -int vdprintf(int fd, char const* fmt, va_list ap) -{ - // FIXME: Implement buffering so that we don't issue one write syscall for every character. - return printf_internal([fd](auto, char ch) { write(fd, &ch, 1); }, nullptr, fmt, ap); -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/dprintf.html -int dprintf(int fd, char const* fmt, ...) -{ - va_list ap; - va_start(ap, fmt); - int ret = vdprintf(fd, fmt, ap); - va_end(ap); - return ret; -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/vprintf.html -int vprintf(char const* fmt, va_list ap) -{ - return printf_internal(stdout_putch, nullptr, fmt, ap); -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/printf.html -int printf(char const* fmt, ...) -{ - va_list ap; - va_start(ap, fmt); - int ret = vprintf(fmt, ap); - va_end(ap); - return ret; -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/vasprintf.html -int vasprintf(char** strp, char const* fmt, va_list ap) -{ - StringBuilder builder; - builder.appendvf(fmt, ap); - VERIFY(builder.length() <= NumericLimits::max()); - int length = builder.length(); - *strp = strdup(builder.to_byte_string().characters()); - return length; -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/asprintf.html -int asprintf(char** strp, char const* fmt, ...) -{ - StringBuilder builder; - va_list ap; - va_start(ap, fmt); - builder.appendvf(fmt, ap); - va_end(ap); - VERIFY(builder.length() <= NumericLimits::max()); - int length = builder.length(); - *strp = strdup(builder.to_byte_string().characters()); - return length; -} - -static void buffer_putch(char*& bufptr, char ch) -{ - *bufptr++ = ch; -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/vsprintf.html -int vsprintf(char* buffer, char const* fmt, va_list ap) -{ - int ret = printf_internal(buffer_putch, buffer, fmt, ap); - buffer[ret] = '\0'; - return ret; -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/sprintf.html -int sprintf(char* buffer, char const* fmt, ...) -{ - va_list ap; - va_start(ap, fmt); - int ret = vsprintf(buffer, fmt, ap); - va_end(ap); - return ret; -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/vsnprintf.html -int vsnprintf(char* buffer, size_t size, char const* fmt, va_list ap) -{ - size_t space_remaining = 0; - if (size) { - space_remaining = size - 1; - } else { - space_remaining = 0; - } - auto sized_buffer_putch = [&](char*& bufptr, char ch) { - if (space_remaining) { - *bufptr++ = ch; - --space_remaining; - } - }; - int ret = printf_internal(sized_buffer_putch, buffer, fmt, ap); - if (space_remaining) { - buffer[ret] = '\0'; - } else if (size > 0) { - buffer[size - 1] = '\0'; - } - return ret; -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/snprintf.html -int snprintf(char* buffer, size_t size, char const* fmt, ...) -{ - va_list ap; - va_start(ap, fmt); - int ret = vsnprintf(buffer, size, fmt, ap); - va_end(ap); - return ret; -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/perror.html -void perror(char const* s) -{ - int saved_errno = errno; - dbgln("perror(): {}: {}", s, strerror(saved_errno)); - warnln("{}: {}", s, strerror(saved_errno)); -} - -static int parse_mode(char const* mode) -{ - int flags = 0; - - // NOTE: rt is a non-standard mode which opens a file for read, explicitly - // specifying that it's a text file - for (auto* ptr = mode; *ptr; ++ptr) { - switch (*ptr) { - case 'r': - flags |= O_RDONLY; - break; - case 'w': - flags |= O_WRONLY | O_CREAT | O_TRUNC; - break; - case 'a': - flags |= O_WRONLY | O_APPEND | O_CREAT; - break; - case '+': - flags |= O_RDWR; - break; - case 'e': - flags |= O_CLOEXEC; - break; - case 'b': - // Ok... - break; - case 't': - // Ok... - break; - default: - dbgln("Potentially unsupported fopen mode _{}_ (because of '{}')", mode, *ptr); - } - } - - return flags; -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/fopen.html -FILE* fopen(char const* pathname, char const* mode) -{ - int flags = parse_mode(mode); - int fd = open(pathname, flags, 0666); - if (fd < 0) - return nullptr; - return FILE::create(fd, flags); -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/freopen.html -FILE* freopen(char const* pathname, char const* mode, FILE* stream) -{ - VERIFY(stream); - if (!pathname) { - // FIXME: Someone should probably implement this path. - TODO(); - } - - int flags = parse_mode(mode); - int fd = open(pathname, flags, 0666); - if (fd < 0) - return nullptr; - - stream->reopen(fd, flags); - return stream; -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/fdopen.html -FILE* fdopen(int fd, char const* mode) -{ - int flags = parse_mode(mode); - // FIXME: Verify that the mode matches how fd is already open. - if (fd < 0) - return nullptr; - return FILE::create(fd, flags); -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/fmemopen.html -FILE* fmemopen(void*, size_t, char const*) -{ - // FIXME: Implement me :^) - TODO(); -} - -static inline bool is_default_stream(FILE* stream) -{ - return stream == stdin || stream == stdout || stream == stderr; -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/fclose.html -int fclose(FILE* stream) -{ - VERIFY(stream); - bool ok; - - { - ScopedFileLock lock(stream); - ok = stream->close(); - } - ScopedValueRollback errno_restorer(errno); - - { - LibC::MutexLocker locker(s_open_streams_lock); - s_open_streams->remove(*stream); - } - stream->~FILE(); - if (!is_default_stream(stream)) - free(stream); - - return ok ? 0 : EOF; -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/rename.html -int rename(char const* oldpath, char const* newpath) -{ - return renameat(AT_FDCWD, oldpath, AT_FDCWD, newpath); -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/renameat.html -int renameat(int olddirfd, char const* oldpath, int newdirfd, char const* newpath) -{ - if (!oldpath || !newpath) { - errno = EFAULT; - return -1; - } - Syscall::SC_rename_params params { olddirfd, { oldpath, strlen(oldpath) }, newdirfd, { newpath, strlen(newpath) } }; - int rc = syscall(SC_rename, ¶ms); - __RETURN_WITH_ERRNO(rc, rc, -1); -} - -void dbgputstr(char const* characters, size_t length) -{ - syscall(SC_dbgputstr, characters, length); -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/tmpnam.html -char* tmpnam(char*) -{ - dbgln("FIXME: Implement tmpnam()"); - TODO(); -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/popen.html -FILE* popen(char const* command, char const* type) -{ - if (!type || (*type != 'r' && *type != 'w')) { - errno = EINVAL; - return nullptr; - } - - int pipe_fds[2]; - - if (pipe(pipe_fds) < 0) { - ScopedValueRollback rollback(errno); - perror("pipe"); - return nullptr; - } - - pid_t child_pid = fork(); - if (child_pid < 0) { - ScopedValueRollback rollback(errno); - perror("fork"); - close(pipe_fds[0]); - close(pipe_fds[1]); - return nullptr; - } else if (child_pid == 0) { - if (*type == 'r') { - if (dup2(pipe_fds[1], STDOUT_FILENO) < 0) { - perror("dup2"); - exit(1); - } - close(pipe_fds[0]); - close(pipe_fds[1]); - } else if (*type == 'w') { - if (dup2(pipe_fds[0], STDIN_FILENO) < 0) { - perror("dup2"); - exit(1); - } - close(pipe_fds[0]); - close(pipe_fds[1]); - } - - if (execl("/bin/sh", "sh", "-c", command, nullptr) < 0) - perror("execl"); - exit(1); - } - - FILE* file = nullptr; - if (*type == 'r') { - file = FILE::create(pipe_fds[0], O_RDONLY); - close(pipe_fds[1]); - } else if (*type == 'w') { - file = FILE::create(pipe_fds[1], O_WRONLY); - close(pipe_fds[0]); - } - - file->set_popen_child(child_pid); - return file; -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/pclose.html -int pclose(FILE* stream) -{ - VERIFY(stream); - VERIFY(stream->popen_child() != 0); - - int wstatus = 0; - if (waitpid(stream->popen_child(), &wstatus, 0) < 0) - return -1; - - return wstatus; -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/remove.html -int remove(char const* pathname) -{ - if (unlink(pathname) < 0) { - if (errno == EISDIR) - return rmdir(pathname); - return -1; - } - return 0; -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/scanf.html -int scanf(char const* fmt, ...) -{ - va_list ap; - va_start(ap, fmt); - int count = vfscanf(stdin, fmt, ap); - va_end(ap); - return count; -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/fscanf.html -int fscanf(FILE* stream, char const* fmt, ...) -{ - va_list ap; - va_start(ap, fmt); - int count = vfscanf(stream, fmt, ap); - va_end(ap); - return count; -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/sscanf.html -int sscanf(char const* buffer, char const* fmt, ...) -{ - va_list ap; - va_start(ap, fmt); - int count = vsscanf(buffer, fmt, ap); - va_end(ap); - return count; -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/vfscanf.html -int vfscanf(FILE* stream, char const* fmt, va_list ap) -{ - char buffer[BUFSIZ]; - if (!fgets(buffer, sizeof(buffer) - 1, stream)) - return -1; - return vsscanf(buffer, fmt, ap); -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/vscanf.html -int vscanf(char const* fmt, va_list ap) -{ - return vfscanf(stdin, fmt, ap); -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/flockfile.html -void flockfile(FILE* filehandle) -{ - VERIFY(filehandle); - filehandle->lock(); -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/funlockfile.html -void funlockfile(FILE* filehandle) -{ - VERIFY(filehandle); - filehandle->unlock(); -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/tmpfile.html -FILE* tmpfile() -{ - char tmp_path[] = "/tmp/XXXXXX"; - int fd = mkstemp(tmp_path); - if (fd < 0) - return nullptr; - // FIXME: instead of using this hack, implement with O_TMPFILE or similar - unlink(tmp_path); - return fdopen(fd, "rw"); -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/ctermid.html -char* ctermid(char* s) -{ - static char tty_path[L_ctermid] = "/dev/tty"; - if (s) - return strcpy(s, tty_path); - return tty_path; -} - -size_t __fpending(FILE* stream) -{ - ScopedFileLock lock(stream); - return stream->pending(); -} - -int __freading(FILE* stream) -{ - ScopedFileLock lock(stream); - - if ((stream->mode() & O_RDWR) == O_RDONLY) { - return 1; - } - - return (stream->flags() & FILE::Flags::LastRead); -} - -int __fwriting(FILE* stream) -{ - ScopedFileLock lock(stream); - - if ((stream->mode() & O_RDWR) == O_WRONLY) { - return 1; - } - - return (stream->flags() & FILE::Flags::LastWrite); -} - -void __fpurge(FILE* stream) -{ - ScopedFileLock lock(stream); - stream->purge(); -} - -size_t __freadahead(FILE* stream) -{ - VERIFY(stream); - - ScopedFileLock lock(stream); - - size_t available_size; - stream->readptr(available_size); - return available_size; -} - -char const* __freadptr(FILE* stream, size_t* sizep) -{ - VERIFY(stream); - VERIFY(sizep); - - ScopedFileLock lock(stream); - - size_t available_size; - u8 const* ptr = stream->readptr(available_size); - - if (available_size == 0) - return nullptr; - - *sizep = available_size; - return reinterpret_cast(ptr); -} - -void __freadptrinc(FILE* stream, size_t increment) -{ - VERIFY(stream); - - ScopedFileLock lock(stream); - - stream->readptr_increase(increment); -} - -void __fseterr(FILE* stream) -{ - ScopedFileLock lock(stream); - stream->set_err(); -} -} - -template bool FILE::gets(u8*, size_t); -template bool FILE::gets(u32*, size_t); diff --git a/Userland/Libraries/LibC/stdio.h b/Userland/Libraries/LibC/stdio.h deleted file mode 100644 index df01c1f7424..00000000000 --- a/Userland/Libraries/LibC/stdio.h +++ /dev/null @@ -1,112 +0,0 @@ -/* - * Copyright (c) 2018-2021, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#define _STDIO_H // Make GMP believe we exist. - -// Includes essentially mandated by POSIX: -// https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/stdio.h.html -#include - -#include -#include -#include -#include -#include -#include - -#define FILENAME_MAX 1024 -#define FOPEN_MAX 1024 - -__BEGIN_DECLS -#ifndef EOF -# define EOF (-1) -#endif - -#define _IOFBF 0 -#define _IOLBF 1 -#define _IONBF 2 - -#define L_ctermid 9 -#define L_tmpnam 256 -#define P_tmpdir "/tmp" - -extern FILE* stdin; -extern FILE* stdout; -extern FILE* stderr; - -typedef off_t fpos_t; - -int fseek(FILE*, long offset, int whence); -int fseeko(FILE*, off_t offset, int whence); -int fgetpos(FILE*, fpos_t*); -int fsetpos(FILE*, fpos_t const*); -long ftell(FILE*); -off_t ftello(FILE*); -char* fgets(char* buffer, int size, FILE*); -int fputc(int ch, FILE*); -int fileno(FILE*); -int fgetc(FILE*); -int fgetc_unlocked(FILE*); -int getc(FILE*); -int getc_unlocked(FILE* stream); -int getchar(void); -ssize_t getdelim(char**, size_t*, int, FILE*); -ssize_t getline(char**, size_t*, FILE*); -int ungetc(int c, FILE*); -int remove(char const* pathname); -FILE* fdopen(int fd, char const* mode); -FILE* fopen(char const* pathname, char const* mode); -FILE* freopen(char const* pathname, char const* mode, FILE*); -FILE* fmemopen(void* buf, size_t size, char const* mode); -void flockfile(FILE* filehandle); -void funlockfile(FILE* filehandle); -int fclose(FILE*); -void rewind(FILE*); -void clearerr(FILE*); -int ferror(FILE*); -int feof(FILE*); -int fflush(FILE*); -size_t fread(void* ptr, size_t size, size_t nmemb, FILE*); -size_t fread_unlocked(void* ptr, size_t size, size_t nmemb, FILE*); -size_t fwrite(void const* ptr, size_t size, size_t nmemb, FILE*); -int vprintf(char const* fmt, va_list) __attribute__((format(printf, 1, 0))); -int vfprintf(FILE*, char const* fmt, va_list) __attribute__((format(printf, 2, 0))); -int vasprintf(char** strp, char const* fmt, va_list) __attribute__((format(printf, 2, 0))); -int vsprintf(char* buffer, char const* fmt, va_list) __attribute__((format(printf, 2, 0))); -int vsnprintf(char* buffer, size_t, char const* fmt, va_list) __attribute__((format(printf, 3, 0))); -int vdprintf(int, char const* fmt, va_list) __attribute__((format(printf, 2, 0))); -int dprintf(int, char const* fmt, ...) __attribute__((format(printf, 2, 3))); -int fprintf(FILE*, char const* fmt, ...) __attribute__((format(printf, 2, 3))); -int printf(char const* fmt, ...) __attribute__((format(printf, 1, 2))); -void dbgputstr(char const*, size_t); -int sprintf(char* buffer, char const* fmt, ...) __attribute__((format(printf, 2, 3))); -int asprintf(char** strp, char const* fmt, ...) __attribute__((format(printf, 2, 3))); -int snprintf(char* buffer, size_t, char const* fmt, ...) __attribute__((format(printf, 3, 4))); -int putchar(int ch); -int putc(int ch, FILE*); -int puts(char const*); -int fputs(char const*, FILE*); -void perror(char const*); -int scanf(char const* fmt, ...) __attribute__((format(scanf, 1, 2))); -int sscanf(char const* str, char const* fmt, ...) __attribute__((format(scanf, 2, 3))); -int fscanf(FILE*, char const* fmt, ...) __attribute__((format(scanf, 2, 3))); -int vscanf(char const*, va_list) __attribute__((format(scanf, 1, 0))); -int vfscanf(FILE*, char const*, va_list) __attribute__((format(scanf, 2, 0))); -int vsscanf(char const*, char const*, va_list) __attribute__((format(scanf, 2, 0))); -int setvbuf(FILE*, char* buf, int mode, size_t); -void setbuf(FILE*, char* buf); -void setlinebuf(FILE*); -int rename(char const* oldpath, char const* newpath); -int renameat(int olddirfd, char const* oldpath, int newdirfd, char const* newpath); -FILE* tmpfile(void); -char* tmpnam(char*); -FILE* popen(char const* command, char const* type); -int pclose(FILE*); -char* ctermid(char* s); - -__END_DECLS diff --git a/Userland/Libraries/LibC/stdio_ext.h b/Userland/Libraries/LibC/stdio_ext.h deleted file mode 100644 index 57004b611b9..00000000000 --- a/Userland/Libraries/LibC/stdio_ext.h +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright (c) 2021, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include - -__BEGIN_DECLS - -size_t __fpending(FILE*); -int __freading(FILE*); -int __fwriting(FILE*); -void __fpurge(FILE*); - -size_t __freadahead(FILE*); -char const* __freadptr(FILE*, size_t*); -void __freadptrinc(FILE*, size_t); -void __fseterr(FILE*); - -__END_DECLS diff --git a/Userland/Libraries/LibC/stdlib.cpp b/Userland/Libraries/LibC/stdlib.cpp deleted file mode 100644 index 6bd70e77075..00000000000 --- a/Userland/Libraries/LibC/stdlib.cpp +++ /dev/null @@ -1,1259 +0,0 @@ -/* - * Copyright (c) 2018-2021, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -static void strtons(char const* str, char** endptr) -{ - assert(endptr); - char* ptr = const_cast(str); - while (isspace(*ptr)) { - ptr += 1; - } - *endptr = ptr; -} - -enum Sign { - Negative, - Positive, -}; - -static Sign strtosign(char const* str, char** endptr) -{ - assert(endptr); - if (*str == '+') { - *endptr = const_cast(str + 1); - return Sign::Positive; - } else if (*str == '-') { - *endptr = const_cast(str + 1); - return Sign::Negative; - } else { - *endptr = const_cast(str); - return Sign::Positive; - } -} - -enum DigitConsumeDecision { - Consumed, - PosOverflow, - NegOverflow, - Invalid, -}; - -template -class NumParser { - AK_MAKE_NONMOVABLE(NumParser); - -public: - NumParser(Sign sign, int base) - : m_base(base) - , m_num(0) - , m_sign(sign) - { - m_cutoff = positive() ? (max_value / base) : (min_value / base); - m_max_digit_after_cutoff = positive() ? (max_value % base) : (min_value % base); - } - - int parse_digit(char ch) - { - int digit; - if (isdigit(ch)) - digit = ch - '0'; - else if (islower(ch)) - digit = ch - ('a' - 10); - else if (isupper(ch)) - digit = ch - ('A' - 10); - else - return -1; - - if (static_cast(digit) >= m_base) - return -1; - - return digit; - } - - DigitConsumeDecision consume(char ch) - { - int digit = parse_digit(ch); - if (digit == -1) - return DigitConsumeDecision::Invalid; - - if (!can_append_digit(digit)) { - if (m_sign != Sign::Negative) { - return DigitConsumeDecision::PosOverflow; - } else { - return DigitConsumeDecision::NegOverflow; - } - } - - m_num *= m_base; - m_num += positive() ? digit : -digit; - - return DigitConsumeDecision::Consumed; - } - - T number() const { return m_num; } - -private: - bool can_append_digit(int digit) - { - bool const is_below_cutoff = positive() ? (m_num < m_cutoff) : (m_num > m_cutoff); - - if (is_below_cutoff) { - return true; - } else { - return m_num == m_cutoff && digit <= m_max_digit_after_cutoff; - } - } - - bool positive() const - { - return m_sign != Sign::Negative; - } - - T const m_base; - T m_num; - T m_cutoff; - int m_max_digit_after_cutoff; - Sign m_sign; -}; - -typedef NumParser IntParser; -typedef NumParser LongLongParser; -typedef NumParser ULongLongParser; - -static bool is_either(char* str, int offset, char lower, char upper) -{ - char ch = *(str + offset); - return ch == lower || ch == upper; -} - -template -inline int generate_unique_filename(char* pattern, size_t suffix_length, Callback callback) -{ - size_t length = strlen(pattern); - - if (length < 6 + suffix_length || memcmp(pattern + length - 6 - suffix_length, "XXXXXX", 6)) - return EINVAL; - - size_t start = length - 6 - suffix_length; - - constexpr char random_characters[] = "abcdefghijklmnopqrstuvwxyz0123456789"; - - for (int attempt = 0; attempt < 100; ++attempt) { - for (int i = 0; i < 6; ++i) - pattern[start + i] = random_characters[(arc4random() % (sizeof(random_characters) - 1))]; - if (callback() == IterationDecision::Break) - return 0; - } - - return EEXIST; -} - -static bool is_infinity_string(char* parse_ptr, char** endptr) -{ - if (is_either(parse_ptr, 0, 'i', 'I')) { - if (is_either(parse_ptr, 1, 'n', 'N')) { - if (is_either(parse_ptr, 2, 'f', 'F')) { - parse_ptr += 3; - if (is_either(parse_ptr, 0, 'i', 'I')) { - if (is_either(parse_ptr, 1, 'n', 'N')) { - if (is_either(parse_ptr, 2, 'i', 'I')) { - if (is_either(parse_ptr, 3, 't', 'T')) { - if (is_either(parse_ptr, 4, 'y', 'Y')) { - parse_ptr += 5; - } - } - } - } - } - if (endptr) - *endptr = parse_ptr; - - return true; - } - } - } - - return false; -} - -static bool is_nan_string(char* parse_ptr, char** endptr) -{ - // FIXME: Actually parse (or at least skip) the (n-char-sequenceopt) part - if (is_either(parse_ptr, 0, 'n', 'N')) { - if (is_either(parse_ptr, 1, 'a', 'A')) { - if (is_either(parse_ptr, 2, 'n', 'N')) { - if (endptr) - *endptr = parse_ptr + 3; - return true; - } - } - } - - return false; -} - -template -static T c_str_to_floating_point(char const* str, char** endptr) -{ - // First, they decompose the input string into three parts: - char* parse_ptr = const_cast(str); - - // An initial, possibly empty, sequence of white-space characters (as specified by isspace()) - strtons(parse_ptr, &parse_ptr); - - // A subject sequence interpreted as a floating-point constant or representing infinity or NaN - - if (*parse_ptr == '\0') { - if (endptr) - *endptr = const_cast(str); - return 0.; - } - - bool is_hex = [&] { - // A hexfloat must start with either 0x, 0X, -0x or -0X and have something after it - char const* parse_head = parse_ptr; - if (*parse_head == '-') - ++parse_head; - - if (*parse_head != '0') - return false; - - ++parse_head; - - if (*parse_head != 'x') - return false; - - ++parse_head; - - // We must have at least one digit but it can come after the "decimal" point. - - if (is_ascii_hex_digit(*parse_head)) - return true; - - if (*parse_head != '.') - return false; - - ++parse_head; - - return is_ascii_hex_digit(*parse_head); - }(); - - AK::FloatingPointParseResults double_parse_result; - if (is_hex) { - // A 0x or 0X, then a non-empty sequence of hexadecimal digits optionally containing a radix character; - // then an optional binary exponent part consisting of the character 'p' or the character 'P', - // optionally followed by a '+' or '-' character, and then followed by one or more decimal digits - - double_parse_result = AK::parse_first_hexfloat_until_zero_character(parse_ptr); - } else { - // A non-empty sequence of decimal digits optionally containing a radix character; - // then an optional exponent part consisting of the character 'e' or the character 'E', - // optionally followed by a '+' or '-' character, and then followed by one or more decimal digits - double_parse_result = AK::parse_first_floating_point_until_zero_character(parse_ptr); - } - - if (double_parse_result.error == AK::FloatingPointError::None) { - // The only way to get NaN (which we shouldn't) or infinities is rounding up to them so we - // have to set ERANGE in that case. - if (!__builtin_isfinite(double_parse_result.value)) - errno = ERANGE; - - if (endptr) - *endptr = const_cast(double_parse_result.end_ptr); - return double_parse_result.value; - } - - if (double_parse_result.error == AK::FloatingPointError::RoundedDownToZero || double_parse_result.error == AK::FloatingPointError::OutOfRange) { - // This is a special case for strtod, where we have a double so close to zero we had to round - // it to zero, in which case we have to set ERANGE - errno = ERANGE; - - if (endptr) - *endptr = const_cast(double_parse_result.end_ptr); - return double_parse_result.value; - } - - // The only way we are here is if the input was not valid for parse_first_floating_point or not a valid hex float - // So the only cases left are: - // - One of INF or INFINITY, ignoring case - // - One of NAN or NAN(n-char-sequenceopt), ignoring case in the NAN part - - Sign const sign = strtosign(parse_ptr, &parse_ptr); - - if (is_infinity_string(parse_ptr, endptr)) { - // Don't set errno to ERANGE here: - // The caller may want to distinguish between "input is - // literal infinity" and "input is not literal infinity - // but did not fit into double". - if (sign != Sign::Negative) - return static_cast(__builtin_huge_val()); - else - return static_cast(-__builtin_huge_val()); - } - - if (is_nan_string(parse_ptr, endptr)) { - errno = ERANGE; - // FIXME: Do we actually want to return "different" NaN bit values? - if (sign != Sign::Negative) - return static_cast(__builtin_nan("")); - else - return static_cast(-__builtin_nan("")); - } - - // If no conversion could be performed, 0 shall be returned, and errno may be set to [EINVAL]. - // FIXME: This is in the posix standard linked from strtod but not in implementations of strtod - // and not in the man pages for linux strtod. - if (endptr) - *endptr = const_cast(str); - return 0; -} - -[[gnu::weak]] extern void __call_fini_functions() asm("__call_fini_functions"); - -extern "C" { - -void exit(int status) -{ - __cxa_finalize(nullptr); - - if (secure_getenv("LIBC_DUMP_MALLOC_STATS")) - serenity_dump_malloc_stats(); - - __call_fini_functions(); - fflush(nullptr); - -#ifndef _DYNAMIC_LOADER - __pthread_key_destroy_for_current_thread(); -#endif - - _exit(status); -} - -static void __atexit_to_cxa_atexit(void* handler) -{ - reinterpret_cast(handler)(); -} - -int atexit(void (*handler)()) -{ - return __cxa_atexit(__atexit_to_cxa_atexit, (void*)handler, nullptr); -} - -void _abort() -{ - // According to the GCC manual __builtin_trap() can call abort() so using it here might not seem safe at first. However, - // on all the platforms we support GCC emits an undefined instruction instead of a call. - __builtin_trap(); -} - -void abort() -{ - // For starters, send ourselves a SIGABRT. - raise(SIGABRT); - // If that didn't kill us, try harder. - sigset_t set; - sigemptyset(&set); - sigaddset(&set, SIGABRT); - sigprocmask(SIG_UNBLOCK, &set, nullptr); - raise(SIGABRT); - _abort(); -} - -static HashTable s_malloced_environment_variables; - -static void free_environment_variable_if_needed(char const* var) -{ - if (!s_malloced_environment_variables.contains((FlatPtr)var)) - return; - s_malloced_environment_variables.remove((FlatPtr)var); - free(const_cast(var)); -} - -char* getenv(char const* name) -{ - size_t vl = strlen(name); - for (size_t i = 0; environ[i]; ++i) { - char const* decl = environ[i]; - char* eq = strchr(decl, '='); - if (!eq) - continue; - size_t varLength = eq - decl; - if (vl != varLength) - continue; - if (strncmp(decl, name, varLength) == 0) - return eq + 1; - } - return nullptr; -} - -char* secure_getenv(char const* name) -{ - if (getauxval(AT_SECURE)) - return nullptr; - return getenv(name); -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/unsetenv.html -int unsetenv(char const* name) -{ - auto new_var_len = strlen(name); - if (new_var_len == 0 || strchr(name, '=')) { - errno = EINVAL; - return -1; - } - - size_t environ_size = 0; - int skip = -1; - - for (; environ[environ_size]; ++environ_size) { - char* old_var = environ[environ_size]; - char* old_eq = strchr(old_var, '='); - VERIFY(old_eq); - size_t old_var_len = old_eq - old_var; - - if (new_var_len != old_var_len) - continue; // can't match - - if (strncmp(name, old_var, new_var_len) == 0) - skip = environ_size; - } - - if (skip == -1) - return 0; // not found: no failure. - - // Shuffle the existing array down by one. - memmove(&environ[skip], &environ[skip + 1], ((environ_size - 1) - skip) * sizeof(environ[0])); - environ[environ_size - 1] = nullptr; - - free_environment_variable_if_needed(name); - return 0; -} - -int clearenv() -{ - size_t environ_size = 0; - for (; environ[environ_size]; ++environ_size) { - environ[environ_size] = NULL; - } - *environ = NULL; - return 0; -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/setenv.html -int setenv(char const* name, char const* value, int overwrite) -{ - if (!overwrite && getenv(name)) - return 0; - auto const total_length = strlen(name) + strlen(value) + 2; - auto* var = (char*)malloc(total_length); - snprintf(var, total_length, "%s=%s", name, value); - s_malloced_environment_variables.set((FlatPtr)var); - return putenv(var); -} - -// A non-evil version of putenv that will strdup the env (and free it later) -int serenity_putenv(char const* new_var, size_t length) -{ - auto* var = strndup(new_var, length); - s_malloced_environment_variables.set((FlatPtr)var); - return putenv(var); -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/putenv.html -int putenv(char* new_var) -{ - char* new_eq = strchr(new_var, '='); - if (!new_eq) - return unsetenv(new_var); - - auto new_var_name_len = new_eq - new_var; - int environ_size = 0; - for (; environ[environ_size]; ++environ_size) { - char* old_var = environ[environ_size]; - auto old_var_name_max_length = strnlen(old_var, new_var_name_len); - char* old_eq = static_cast(memchr(old_var, '=', old_var_name_max_length + 1)); - if (!old_eq) - continue; // name is longer, or possibly freed or overwritten value - - auto old_var_name_len = old_eq - old_var; - if (new_var_name_len != old_var_name_len) - continue; // can't match - - if (strncmp(new_var, old_var, new_var_name_len) == 0) { - free_environment_variable_if_needed(old_var); - environ[environ_size] = new_var; - return 0; - } - } - - // At this point, we need to append the new var. - // 2 here: one for the new var, one for the sentinel value. - auto** new_environ = static_cast(kmalloc_array(environ_size + 2, sizeof(char*))); - if (new_environ == nullptr) { - errno = ENOMEM; - return -1; - } - - for (int i = 0; environ[i]; ++i) - new_environ[i] = environ[i]; - - new_environ[environ_size] = new_var; - new_environ[environ_size + 1] = nullptr; - - // swap new and old - // note that the initial environ is not heap allocated! - extern bool __environ_is_malloced; - if (__environ_is_malloced) - free(environ); - __environ_is_malloced = true; - environ = new_environ; - return 0; -} - -static char const* __progname = NULL; - -char const* getprogname() -{ - return __progname; -} - -void setprogname(char const* progname) -{ - for (int i = strlen(progname) - 1; i >= 0; i--) { - if (progname[i] == '/') { - __progname = progname + i + 1; - return; - } - } - - __progname = progname; -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/strtod.html -double strtod(char const* str, char** endptr) -{ - return c_str_to_floating_point(str, endptr); -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/strtold.html -long double strtold(char const* str, char** endptr) -{ - assert(sizeof(double) == sizeof(long double)); - return strtod(str, endptr); -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/strtof.html -float strtof(char const* str, char** endptr) -{ - return c_str_to_floating_point(str, endptr); -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/atof.html -double atof(char const* str) -{ - return strtod(str, nullptr); -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/atoi.html -int atoi(char const* str) -{ - long value = strtol(str, nullptr, 10); - if (value > INT_MAX) { - return INT_MAX; - } - return value; -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/atol.html -long atol(char const* str) -{ - return strtol(str, nullptr, 10); -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/atoll.html -long long atoll(char const* str) -{ - return strtoll(str, nullptr, 10); -} - -static char ptsname_buf[32]; -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/ptsname.html -char* ptsname(int fd) -{ - if (ptsname_r(fd, ptsname_buf, sizeof(ptsname_buf)) < 0) - return nullptr; - return ptsname_buf; -} - -int ptsname_r(int fd, char* buffer, size_t size) -{ - struct stat stat; - if (fstat(fd, &stat) < 0) - return -1; - - StringBuilder devpts_path_builder; - devpts_path_builder.append("/dev/pts/"sv); - - int master_pty_index = 0; - // Note: When the user opens a PTY from /dev/ptmx with posix_openpt(), the open file descriptor - // points to /dev/ptmx, (major number is 5 and minor number is 2), but internally - // in the kernel, it points to a new MasterPTY device. When we do ioctl with TIOCGPTN option - // on the open file descriptor, it actually asks the MasterPTY what is the assigned index - // of it when the PTYMultiplexer created it. - if (ioctl(fd, TIOCGPTN, &master_pty_index) < 0) - return -1; - - if (master_pty_index < 0) { - errno = EINVAL; - return -1; - } - - devpts_path_builder.appendff("{:d}", master_pty_index); - if (devpts_path_builder.length() > size) { - errno = ERANGE; - return -1; - } - memset(buffer, 0, devpts_path_builder.length() + 1); - auto full_devpts_path_string = devpts_path_builder.to_byte_string(); - if (!full_devpts_path_string.copy_characters_to_buffer(buffer, size)) { - errno = ERANGE; - return -1; - } - return 0; -} - -static unsigned long s_next_rand = 1; -static long s_next_rand48 = 0; - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/rand.html -int rand() -{ - s_next_rand = s_next_rand * 1103515245 + 12345; - return ((unsigned)(s_next_rand / ((RAND_MAX + 1) * 2)) % (RAND_MAX + 1)); -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/srand.html -void srand(unsigned seed) -{ - s_next_rand = seed; -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/drand48.html -double drand48() -{ - constexpr u64 a = 0x5DEECE66DULL; - constexpr u64 c = 0xBULL; - constexpr u64 m = 1ULL << 48; - - s_next_rand48 = (a * s_next_rand48 + c) & (m - 1); - return static_cast(s_next_rand48) / m; -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/srand48.html -void srand48(long seed) -{ - s_next_rand48 = (seed & 0xFFFFFFFF) << 16 | 0x330E; -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/abs.html -int abs(int i) -{ - return i < 0 ? -i : i; -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/labs.html -long int labs(long int i) -{ - return i < 0 ? -i : i; -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/llabs.html -long long int llabs(long long int i) -{ - return i < 0 ? -i : i; -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/random.html -long int random() -{ - return rand(); -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/srandom.html -void srandom(unsigned seed) -{ - srand(seed); -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/system.html -int system(char const* command) -{ - __pthread_maybe_cancel(); - - if (!command) - return 1; - - pid_t child; - char const* argv[] = { "sh", "-c", command, nullptr }; - if ((errno = posix_spawn(&child, "/bin/sh", nullptr, nullptr, const_cast(argv), environ))) - return -1; - int wstatus; - waitpid(child, &wstatus, 0); - return WEXITSTATUS(wstatus); -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/mktemp.html -char* mktemp(char* pattern) -{ - auto error = generate_unique_filename(pattern, 0, [&] { - struct stat st; - int rc = lstat(pattern, &st); - if (rc < 0 && errno == ENOENT) - return IterationDecision::Break; - return IterationDecision::Continue; - }); - if (error) { - pattern[0] = '\0'; - errno = error; - } - return pattern; -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/mkstemp.html -int mkstemp(char* pattern) -{ - return mkstemps(pattern, 0); -} - -// https://man7.org/linux/man-pages/man3/mkstemps.3.html -int mkstemps(char* pattern, int suffix_length) -{ - VERIFY(suffix_length >= 0); - int fd = -1; - auto error = generate_unique_filename(pattern, static_cast(suffix_length), [&] { - fd = open(pattern, O_RDWR | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR); // I'm using the flags I saw glibc using. - if (fd >= 0) - return IterationDecision::Break; - return IterationDecision::Continue; - }); - if (error) { - errno = error; - return -1; - } - return fd; -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/mkdtemp.html -char* mkdtemp(char* pattern) -{ - auto error = generate_unique_filename(pattern, 0, [&] { - if (mkdir(pattern, 0700) == 0) - return IterationDecision::Break; - return IterationDecision::Continue; - }); - if (error) { - errno = error; - return nullptr; - } - return pattern; -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/bsearch.html -void* bsearch(void const* key, void const* base, size_t nmemb, size_t size, int (*compar)(void const*, void const*)) -{ - char* start = static_cast(const_cast(base)); - while (nmemb > 0) { - char* middle_memb = start + (nmemb / 2) * size; - int comparison = compar(key, middle_memb); - if (comparison == 0) - return middle_memb; - else if (comparison > 0) { - start = middle_memb + size; - --nmemb; - } - nmemb /= 2; - } - - return nullptr; -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/div.html -div_t div(int numerator, int denominator) -{ - div_t result; - result.quot = numerator / denominator; - result.rem = numerator % denominator; - - if (numerator >= 0 && result.rem < 0) { - result.quot++; - result.rem -= denominator; - } - return result; -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/ldiv.html -ldiv_t ldiv(long numerator, long denominator) -{ - ldiv_t result; - result.quot = numerator / denominator; - result.rem = numerator % denominator; - - if (numerator >= 0 && result.rem < 0) { - result.quot++; - result.rem -= denominator; - } - return result; -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/lldiv.html -lldiv_t lldiv(long long numerator, long long denominator) -{ - lldiv_t result; - result.quot = numerator / denominator; - result.rem = numerator % denominator; - - if (numerator >= 0 && result.rem < 0) { - result.quot++; - result.rem -= denominator; - } - return result; -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/mblen.html -int mblen(char const* s, size_t n) -{ - // POSIX: Equivalent to mbtowc(NULL, s, n), but we mustn't change the state of mbtowc. - static mbstate_t internal_state = {}; - - // Reset the internal state and ask whether we have shift states. - if (s == nullptr) { - internal_state = {}; - return 0; - } - - size_t ret = mbrtowc(nullptr, s, n, &internal_state); - - // Incomplete characters get returned as illegal sequence. - if (ret == -2ul) { - errno = EILSEQ; - return -1; - } - - return ret; -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/mbstowcs.html -size_t mbstowcs(wchar_t* pwcs, char const* s, size_t n) -{ - static mbstate_t state = {}; - return mbsrtowcs(pwcs, &s, n, &state); -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/mbtowc.html -int mbtowc(wchar_t* pwc, char const* s, size_t n) -{ - static mbstate_t internal_state = {}; - - // Reset the internal state and ask whether we have shift states. - if (s == nullptr) { - internal_state = {}; - return 0; - } - - size_t ret = mbrtowc(pwc, s, n, &internal_state); - - // Incomplete characters get returned as illegal sequence. - // Internal state is undefined, so don't bother with resetting. - if (ret == -2ul) { - errno = EILSEQ; - return -1; - } - - return ret; -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/wctomb.html -int wctomb(char* s, wchar_t wc) -{ - static mbstate_t _internal_state = {}; - - // nullptr asks whether we have state-dependent encodings, but we don't have any. - if (s == nullptr) - return 0; - - return static_cast(wcrtomb(s, wc, &_internal_state)); -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/wcstombs.html -size_t wcstombs(char* dest, wchar_t const* src, size_t max) -{ - char* original_dest = dest; - while ((size_t)(dest - original_dest) < max) { - StringView v { (char const*)src, sizeof(wchar_t) }; - - // FIXME: dependent on locale, for now utf-8 is supported. - Utf8View utf8 { v }; - if (*utf8.begin() == '\0') { - *dest = '\0'; - return (size_t)(dest - original_dest); // Exclude null character in returned size - } - - for (auto byte : utf8) { - if (byte != '\0') - *dest++ = byte; - } - ++src; - } - return max; -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/strtol.html -long strtol(char const* str, char** endptr, int base) -{ - long long value = strtoll(str, endptr, base); - if (value < LONG_MIN) { - errno = ERANGE; - return LONG_MIN; - } else if (value > LONG_MAX) { - errno = ERANGE; - return LONG_MAX; - } - return value; -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/strtoul.html -unsigned long strtoul(char const* str, char** endptr, int base) -{ - unsigned long long value = strtoull(str, endptr, base); - if (value > ULONG_MAX) { - errno = ERANGE; - return ULONG_MAX; - } - return value; -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/strtoll.html -long long strtoll(char const* str, char** endptr, int base) -{ - // Parse spaces and sign - char* parse_ptr = const_cast(str); - strtons(parse_ptr, &parse_ptr); - Sign const sign = strtosign(parse_ptr, &parse_ptr); - - // Parse base - if (base == 0) { - if (*parse_ptr == '0') { - if (tolower(*(parse_ptr + 1)) == 'x') { - base = 16; - parse_ptr += 2; - } else { - base = 8; - } - } else { - base = 10; - } - } - - // Parse actual digits. - LongLongParser digits { sign, base }; - bool digits_usable = false; - bool should_continue = true; - bool overflow = false; - do { - bool is_a_digit; - if (overflow) { - is_a_digit = digits.parse_digit(*parse_ptr) >= 0; - } else { - DigitConsumeDecision decision = digits.consume(*parse_ptr); - switch (decision) { - case DigitConsumeDecision::Consumed: - is_a_digit = true; - // The very first actual digit must pass here: - digits_usable = true; - break; - case DigitConsumeDecision::PosOverflow: - case DigitConsumeDecision::NegOverflow: - is_a_digit = true; - overflow = true; - break; - case DigitConsumeDecision::Invalid: - is_a_digit = false; - break; - default: - VERIFY_NOT_REACHED(); - } - } - - should_continue = is_a_digit; - parse_ptr += should_continue; - } while (should_continue); - - if (!digits_usable) { - // No actual number value available. - if (endptr) - *endptr = const_cast(str); - return 0; - } - - if (endptr) - *endptr = parse_ptr; - - if (overflow) { - errno = ERANGE; - if (sign != Sign::Negative) { - return LONG_LONG_MAX; - } else { - return LONG_LONG_MIN; - } - } - - return digits.number(); -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/strtoull.html -unsigned long long strtoull(char const* str, char** endptr, int base) -{ - // Parse spaces and sign - char* parse_ptr = const_cast(str); - strtons(parse_ptr, &parse_ptr); - - if (base == 16) { - // Dr. POSIX: "If the value of base is 16, the characters 0x or 0X may optionally precede - // the sequence of letters and digits, following the sign if present." - if (*parse_ptr == '0') { - if (tolower(*(parse_ptr + 1)) == 'x') - parse_ptr += 2; - } - } - // Parse base - if (base == 0) { - if (*parse_ptr == '0') { - if (tolower(*(parse_ptr + 1)) == 'x') { - base = 16; - parse_ptr += 2; - } else { - base = 8; - } - } else { - base = 10; - } - } - - // Parse actual digits. - ULongLongParser digits { Sign::Positive, base }; - bool digits_usable = false; - bool should_continue = true; - bool overflow = false; - do { - bool is_a_digit; - if (overflow) { - is_a_digit = digits.parse_digit(*parse_ptr) >= 0; - } else { - DigitConsumeDecision decision = digits.consume(*parse_ptr); - switch (decision) { - case DigitConsumeDecision::Consumed: - is_a_digit = true; - // The very first actual digit must pass here: - digits_usable = true; - break; - case DigitConsumeDecision::PosOverflow: - case DigitConsumeDecision::NegOverflow: - is_a_digit = true; - overflow = true; - break; - case DigitConsumeDecision::Invalid: - is_a_digit = false; - break; - default: - VERIFY_NOT_REACHED(); - } - } - - should_continue = is_a_digit; - parse_ptr += should_continue; - } while (should_continue); - - if (!digits_usable) { - // No actual number value available. - if (endptr) - *endptr = const_cast(str); - return 0; - } - - if (endptr) - *endptr = parse_ptr; - - if (overflow) { - errno = ERANGE; - return LONG_LONG_MAX; - } - - return digits.number(); -} - -uint32_t arc4random(void) -{ - uint32_t buf; - arc4random_buf(&buf, sizeof(buf)); - return buf; -} - -static pthread_mutex_t s_randomness_mutex = PTHREAD_MUTEX_INITIALIZER; -static u8* s_randomness_buffer; -static size_t s_randomness_index; - -void arc4random_buf(void* buffer, size_t buffer_size) -{ - pthread_mutex_lock(&s_randomness_mutex); - - size_t bytes_needed = buffer_size; - auto* ptr = static_cast(buffer); - - while (bytes_needed > 0) { - if (!s_randomness_buffer || s_randomness_index >= PAGE_SIZE) { - if (!s_randomness_buffer) { - s_randomness_buffer = static_cast(mmap(nullptr, PAGE_SIZE, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE | MAP_RANDOMIZED, 0, 0)); - VERIFY(s_randomness_buffer != MAP_FAILED); - __pthread_fork_atfork_register_child( - [] { - munmap(s_randomness_buffer, PAGE_SIZE); - s_randomness_buffer = nullptr; - s_randomness_index = 0; - }); - } - syscall(SC_getrandom, s_randomness_buffer, PAGE_SIZE); - s_randomness_index = 0; - } - - size_t available_bytes = PAGE_SIZE - s_randomness_index; - size_t bytes_to_copy = min(bytes_needed, available_bytes); - - memcpy(ptr, s_randomness_buffer + s_randomness_index, bytes_to_copy); - - s_randomness_index += bytes_to_copy; - bytes_needed -= bytes_to_copy; - ptr += bytes_to_copy; - } - - pthread_mutex_unlock(&s_randomness_mutex); -} - -uint32_t arc4random_uniform(uint32_t max_bounds) -{ - return AK::get_random_uniform(max_bounds); -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/realpath.html -char* realpath(char const* pathname, char* buffer) -{ - if (!pathname) { - errno = EFAULT; - return nullptr; - } - size_t size = PATH_MAX; - bool self_allocated = false; - if (buffer == nullptr) { - // Since we self-allocate, try to sneakily use a smaller buffer instead, in an attempt to use less memory. - size = 64; - buffer = (char*)malloc(size); - self_allocated = true; - } - Syscall::SC_realpath_params params { { pathname, strlen(pathname) }, { buffer, size } }; - int rc = syscall(SC_realpath, ¶ms); - if (rc < 0) { - if (self_allocated) - free(buffer); - errno = -rc; - return nullptr; - } - if (self_allocated && static_cast(rc) > size) { - // There was silent truncation, *and* we can simply retry without the caller noticing. - free(buffer); - size = static_cast(rc); - buffer = (char*)malloc(size); - params.buffer = { buffer, size }; - rc = syscall(SC_realpath, ¶ms); - if (rc < 0) { - // Can only happen if we lose a race. Let's pretend we lost the race in the first place. - free(buffer); - errno = -rc; - return nullptr; - } - size_t new_size = static_cast(rc); - if (new_size < size) { - // If we're here, the symlink has become longer while we were looking at it. - // There's not much we can do, unless we want to loop endlessly - // in this case. Let's leave it up to the caller whether to loop. - free(buffer); - errno = EAGAIN; - return nullptr; - } - } - errno = 0; - return buffer; -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/posix_openpt.html -int posix_openpt(int flags) -{ - if (flags & ~(O_RDWR | O_NOCTTY | O_CLOEXEC)) { - errno = EINVAL; - return -1; - } - - return open("/dev/ptmx", flags); -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/grantpt.html -int grantpt([[maybe_unused]] int fd) -{ - return 0; -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/unlockpt.html -int unlockpt([[maybe_unused]] int fd) -{ - return 0; -} -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/_Exit.html -void _Exit(int status) -{ - _exit(status); -} diff --git a/Userland/Libraries/LibC/stdlib.h b/Userland/Libraries/LibC/stdlib.h deleted file mode 100644 index cfcd544526b..00000000000 --- a/Userland/Libraries/LibC/stdlib.h +++ /dev/null @@ -1,114 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -// Includes essentially mandated by POSIX: -// https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/stdlib.h.html -#include -#include -#include -#include - -#include -#include -#include - -__BEGIN_DECLS - -#define EXIT_SUCCESS 0 -#define EXIT_FAILURE 1 - -__attribute__((noreturn)) void _abort(void); - -__attribute__((malloc)) __attribute__((alloc_size(1))) void* malloc(size_t); -__attribute__((malloc)) __attribute__((alloc_size(1, 2))) void* calloc(size_t nmemb, size_t); -size_t malloc_size(void const*); -size_t malloc_good_size(size_t); -void serenity_dump_malloc_stats(void); -void free(void*); -__attribute__((alloc_size(2))) void* realloc(void* ptr, size_t); -char* getenv(char const* name); -char* secure_getenv(char const* name); -int putenv(char*); -int serenity_putenv(char const* new_var, size_t length); -int unsetenv(char const*); -int clearenv(void); -int setenv(char const* name, char const* value, int overwrite); -char const* getprogname(void); -void setprogname(char const*); -int atoi(char const*); -long atol(char const*); -long long atoll(char const*); -double strtod(char const*, char** endptr); -long double strtold(char const*, char** endptr); -float strtof(char const*, char** endptr); -long strtol(char const*, char** endptr, int base); -long long strtoll(char const*, char** endptr, int base); -unsigned long long strtoull(char const*, char** endptr, int base); -unsigned long strtoul(char const*, char** endptr, int base); -void qsort(void* base, size_t nmemb, size_t size, int (*compar)(void const*, void const*)); -void qsort_r(void* base, size_t nmemb, size_t size, int (*compar)(void const*, void const*, void*), void* arg); -int atexit(void (*function)(void)); -__attribute__((noreturn)) void exit(int status); -__attribute__((noreturn)) void abort(void); -char* ptsname(int fd); -int ptsname_r(int fd, char* buffer, size_t); -int abs(int); -long labs(long); -long long int llabs(long long int); -double atof(char const*); -int system(char const* command); -char* mktemp(char*); -int mkstemp(char*); -int mkstemps(char*, int); -char* mkdtemp(char*); -void* bsearch(void const* key, void const* base, size_t nmemb, size_t size, int (*compar)(void const*, void const*)); -int mblen(char const*, size_t); -size_t mbstowcs(wchar_t*, char const*, size_t); -int mbtowc(wchar_t*, char const*, size_t); -int wctomb(char*, wchar_t); -size_t wcstombs(char*, wchar_t const*, size_t); -char* realpath(char const* pathname, char* buffer); -__attribute__((noreturn)) void _Exit(int status); - -#define RAND_MAX 32767 -int rand(void); -void srand(unsigned seed); -double drand48(void); -void srand48(long seed); - -long int random(void); -void srandom(unsigned seed); - -uint32_t arc4random(void); -void arc4random_buf(void*, size_t); -uint32_t arc4random_uniform(uint32_t); - -typedef struct { - int quot; - int rem; -} div_t; -div_t div(int, int); -typedef struct { - long quot; - long rem; -} ldiv_t; -ldiv_t ldiv(long, long); -typedef struct { - long long quot; - long long rem; -} lldiv_t; -lldiv_t lldiv(long long, long long); - -int posix_openpt(int flags); -int grantpt(int fd); -int unlockpt(int fd); - -int posix_memalign(void**, size_t alignment, size_t size); -__attribute__((malloc, alloc_size(2), alloc_align(1))) void* aligned_alloc(size_t alignment, size_t size); - -__END_DECLS diff --git a/Userland/Libraries/LibC/string.cpp b/Userland/Libraries/LibC/string.cpp deleted file mode 100644 index cb30931cba7..00000000000 --- a/Userland/Libraries/LibC/string.cpp +++ /dev/null @@ -1,526 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * Copyright (c) 2021-2022, Brian Gianforcaro - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -extern "C" { - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/strspn.html -size_t strspn(char const* s, char const* accept) -{ - char const* p = s; -cont: - char ch = *p++; - char ac; - for (char const* ap = accept; (ac = *ap++) != '\0';) { - if (ac == ch) - goto cont; - } - return p - 1 - s; -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/strcspn.html -size_t strcspn(char const* s, char const* reject) -{ - for (auto* p = s;;) { - char c = *p++; - auto* rp = reject; - char rc; - do { - if ((rc = *rp++) == c) - return p - 1 - s; - } while (rc); - } -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/strlen.html -size_t strlen(char const* str) -{ - size_t len = 0; - while (*(str++)) - ++len; - return len; -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/strnlen.html -size_t strnlen(char const* str, size_t maxlen) -{ - size_t len = 0; - for (; len < maxlen && *str; str++) - len++; - return len; -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/strdup.html -char* strdup(char const* str) -{ - size_t len = strlen(str); - char* new_str = (char*)malloc(len + 1); - if (!new_str) - return nullptr; - memcpy(new_str, str, len); - new_str[len] = '\0'; - return new_str; -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/strndup.html -char* strndup(char const* str, size_t maxlen) -{ - size_t len = strnlen(str, maxlen); - char* new_str = (char*)malloc(len + 1); - if (!new_str) - return nullptr; - memcpy(new_str, str, len); - new_str[len] = 0; - return new_str; -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/strcmp.html -int strcmp(char const* s1, char const* s2) -{ - while (*s1 == *s2++) - if (*s1++ == 0) - return 0; - return *(unsigned char const*)s1 - *(unsigned char const*)--s2; -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/strncmp.html -int strncmp(char const* s1, char const* s2, size_t n) -{ - if (!n) - return 0; - do { - if (*s1 != *s2++) - return *(unsigned char const*)s1 - *(unsigned char const*)--s2; - if (*s1++ == 0) - break; - } while (--n); - return 0; -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/memcmp.html -int memcmp(void const* v1, void const* v2, size_t n) -{ - auto* s1 = (uint8_t const*)v1; - auto* s2 = (uint8_t const*)v2; - while (n-- > 0) { - if (*s1++ != *s2++) - return s1[-1] < s2[-1] ? -1 : 1; - } - return 0; -} - -// Not in POSIX, originated in BSD -// https://man.openbsd.org/timingsafe_memcmp.3 -int timingsafe_memcmp(void const* b1, void const* b2, size_t len) -{ - return AK::timing_safe_compare(b1, b2, len) ? 1 : 0; -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/memcpy.html -void* memcpy(void* dest_ptr, void const* src_ptr, size_t n) -{ -#if ARCH(X86_64) - void* original_dest = dest_ptr; - asm volatile( - "rep movsb" - : "+D"(dest_ptr), "+S"(src_ptr), "+c"(n)::"memory"); - return original_dest; -#else - u8* pd = (u8*)dest_ptr; - u8 const* ps = (u8 const*)src_ptr; - for (; n--;) - *pd++ = *ps++; - return dest_ptr; -#endif -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/memccpy.html -void* memccpy(void* dest_ptr, void const* src_ptr, int c, size_t n) -{ - u8* pd = static_cast(dest_ptr); - u8 const* ps = static_cast(src_ptr); - for (; n--; pd++, ps++) { - *pd = *ps; - if (*pd == static_cast(c)) - return pd + 1; - } - return nullptr; -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/memset.html -// For x86-64, an optimized ASM implementation is found in ./arch/x86_64/memset.S -#if ARCH(X86_64) -#else -void* memset(void* dest_ptr, int c, size_t n) -{ - u8* pd = (u8*)dest_ptr; - for (; n--;) - *pd++ = c; - return dest_ptr; -} -#endif - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/memmove.html -void* memmove(void* dest, void const* src, size_t n) -{ - if (dest < src) - return memcpy(dest, src, n); - if (((FlatPtr)dest - (FlatPtr)src) >= n) - return memcpy(dest, src, n); - - u8* pd = (u8*)dest; - u8 const* ps = (u8 const*)src; - for (pd += n, ps += n; n--;) - *--pd = *--ps; - return dest; -} - -// https://linux.die.net/man/3/memmem (GNU extension) -void* memmem(void const* haystack, size_t haystack_length, void const* needle, size_t needle_length) -{ - return const_cast(AK::memmem(haystack, haystack_length, needle, needle_length)); -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/strcpy.html -char* strcpy(char* dest, char const* src) -{ - char* original_dest = dest; - while ((*dest = *src) != '\0') { - dest++; - src++; - } - return original_dest; -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/stpcpy.html -char* stpcpy(char* dest, char const* src) -{ - while ((*dest = *src) != '\0') { - dest++; - src++; - } - return dest; -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/strncpy.html -char* strncpy(char* dest, char const* src, size_t n) -{ - size_t i; - for (i = 0; i < n && src[i] != '\0'; ++i) - dest[i] = src[i]; - for (; i < n; ++i) - dest[i] = '\0'; - return dest; -} - -// Not in POSIX, originated in BSD but also supported on Linux. -// https://man.openbsd.org/strlcpy.3 -size_t strlcpy(char* dest, char const* src, size_t n) -{ - size_t i; - // Would like to test i < n - 1 here, but n might be 0. - for (i = 0; i + 1 < n && src[i] != '\0'; ++i) - dest[i] = src[i]; - if (n) - dest[i] = '\0'; - for (; src[i] != '\0'; ++i) - ; // Determine the length of src, don't copy. - return i; -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/strchr.html -char* strchr(char const* str, int c) -{ - char ch = c; - for (;; ++str) { - if (*str == ch) - return const_cast(str); - if (!*str) - return nullptr; - } -} - -// https://pubs.opengroup.org/onlinepubs/9699959399/functions/index.html -char* index(char const* str, int c) -{ - return strchr(str, c); -} - -// https://linux.die.net/man/3/strchrnul (GNU extension) -char* strchrnul(char const* str, int c) -{ - char ch = c; - for (;; ++str) { - if (*str == ch || !*str) - return const_cast(str); - } -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/memchr.html -void* memchr(void const* ptr, int c, size_t size) -{ - char ch = c; - auto* cptr = (char const*)ptr; - for (size_t i = 0; i < size; ++i) { - if (cptr[i] == ch) - return const_cast(cptr + i); - } - return nullptr; -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/strrchr.html -char* strrchr(char const* str, int ch) -{ - char* last = nullptr; - char c; - for (; (c = *str); ++str) { - if (c == ch) - last = const_cast(str); - } - return last; -} - -// https://pubs.opengroup.org/onlinepubs/9699959399/functions/rindex.html -char* rindex(char const* str, int ch) -{ - return strrchr(str, ch); -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/strcat.html -char* strcat(char* dest, char const* src) -{ - size_t dest_length = strlen(dest); - size_t i; - for (i = 0; src[i] != '\0'; i++) - dest[dest_length + i] = src[i]; - dest[dest_length + i] = '\0'; - return dest; -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/strncat.html -char* strncat(char* dest, char const* src, size_t n) -{ - size_t dest_length = strlen(dest); - size_t i; - for (i = 0; i < n && src[i] != '\0'; i++) - dest[dest_length + i] = src[i]; - dest[dest_length + i] = '\0'; - return dest; -} - -char const* const sys_errlist[] = { -#define __ENUMERATE_ERRNO_CODE(c, s) s, - ENUMERATE_ERRNO_CODES(__ENUMERATE_ERRNO_CODE) -#undef __ENUMERATE_ERRNO_CODE -}; -static_assert(array_size(sys_errlist) == (EMAXERRNO + 1)); - -int sys_nerr = EMAXERRNO; - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/strerror_r.html -int strerror_r(int errnum, char* buf, size_t buflen) -{ - auto saved_errno = errno; - if (errnum < 0 || errnum >= EMAXERRNO) { - auto rc = strlcpy(buf, "unknown error", buflen); - if (rc >= buflen) - dbgln("strerror_r(): Invalid error number '{}' specified and the output buffer is too small.", errnum); - errno = saved_errno; - return EINVAL; - } - auto text = strerror(errnum); - auto rc = strlcpy(buf, text, buflen); - if (rc >= buflen) { - errno = saved_errno; - return ERANGE; - } - errno = saved_errno; - return 0; -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/strerror.html -char* strerror(int errnum) -{ - if (errnum < 0 || errnum >= EMAXERRNO) { - return const_cast("Unknown error"); - } - return const_cast(sys_errlist[errnum]); -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/strsignal.html -char* strsignal(int signum) -{ - if (signum <= 0 || signum >= NSIG || !sys_siglist[signum]) { - dbgln("strsignal() missing string for signum={}", signum); - return const_cast("Unknown signal"); - } - return const_cast(sys_siglist[signum]); -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/strstr.html -char* strstr(char const* haystack, char const* needle) -{ - char nch; - char hch; - - if ((nch = *needle++) != 0) { - size_t len = strlen(needle); - do { - do { - if ((hch = *haystack++) == 0) - return nullptr; - } while (hch != nch); - } while (strncmp(haystack, needle, len) != 0); - --haystack; - } - return const_cast(haystack); -} - -// https://linux.die.net/man/3/strcasestr -char* strcasestr(char const* haystack, char const* needle) -{ - char nch; - char hch; - - if ((nch = *needle++) != 0) { - size_t len = strlen(needle); - do { - do { - if ((hch = *haystack++) == 0) - return nullptr; - } while (toupper(hch) != toupper(nch)); - } while (strncasecmp(haystack, needle, len) != 0); - --haystack; - } - return const_cast(haystack); -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/strpbrk.html -char* strpbrk(char const* s, char const* accept) -{ - while (*s) - if (strchr(accept, *s++)) - return const_cast(--s); - return nullptr; -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/strtok_r.html -char* strtok_r(char* str, char const* delim, char** saved_str) -{ - if (!str) { - if (!saved_str || *saved_str == nullptr) - return nullptr; - str = *saved_str; - } - - size_t token_start = 0; - size_t token_end = 0; - size_t str_len = strlen(str); - size_t delim_len = strlen(delim); - - for (size_t i = 0; i < str_len; ++i) { - bool is_proper_delim = false; - - for (size_t j = 0; j < delim_len; ++j) { - if (str[i] == delim[j]) { - // Skip beginning delimiters - if (token_end - token_start == 0) { - ++token_start; - break; - } - - is_proper_delim = true; - } - } - - ++token_end; - if (is_proper_delim && token_end > 0) { - --token_end; - break; - } - } - - if (str[token_start] == '\0') { - *saved_str = nullptr; - return nullptr; - } - - if (token_end == 0) { - *saved_str = nullptr; - return &str[token_start]; - } - - if (str[token_end] == '\0') - *saved_str = &str[token_end]; - else - *saved_str = &str[token_end + 1]; - - str[token_end] = '\0'; - return &str[token_start]; -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/strtok.html -char* strtok(char* str, char const* delim) -{ - static char* saved_str; - return strtok_r(str, delim, &saved_str); -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/strcoll.html -int strcoll(char const* s1, char const* s2) -{ - return strcmp(s1, s2); -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/strxfrm.html -size_t strxfrm(char* dest, char const* src, size_t n) -{ - size_t i; - for (i = 0; i < n && src[i] != '\0'; ++i) - dest[i] = src[i]; - for (; i < n; ++i) - dest[i] = '\0'; - return i; -} - -// Not in POSIX, originated in BSD but also supported on Linux. -// https://man.openbsd.org/strsep.3 -char* strsep(char** str, char const* delim) -{ - if (*str == nullptr) - return nullptr; - auto* begin = *str; - auto* end = begin + strcspn(begin, delim); - if (*end) { - *end = '\0'; - *str = ++end; - } else { - *str = nullptr; - } - return begin; -} - -// Not in POSIX, originated in BSD but also supported on Linux. -// https://man.openbsd.org/explicit_bzero.3 -void explicit_bzero(void* ptr, size_t size) -{ - secure_zero(ptr, size); -} -} diff --git a/Userland/Libraries/LibC/string.h b/Userland/Libraries/LibC/string.h deleted file mode 100644 index 657aa41369a..00000000000 --- a/Userland/Libraries/LibC/string.h +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * Copyright (c) 2021-2022, Brian Gianforcaro - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -// Includes essentially mandated by POSIX: -// https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/string.h.html -#include - -#include -#include - -__BEGIN_DECLS - -#ifdef __cplusplus -# define __CORRECT_ISO_CPP_STRING_H_PROTO -#endif - -// A few C Standard Libraries include this header in , and hence expect -// `strcasecmp` etcetera to be available as part of a include, so let's -// do the same here to maintain compatibility -#include - -size_t strlen(char const*); -size_t strnlen(char const*, size_t maxlen); - -int strcmp(char const*, char const*); -int strncmp(char const*, char const*, size_t); - -int memcmp(void const*, void const*, size_t); -int timingsafe_memcmp(void const*, void const*, size_t); -void* memcpy(void*, void const*, size_t); -void* memccpy(void*, void const*, int, size_t); -void* memmove(void*, void const*, size_t); -void* memchr(void const*, int c, size_t); -void* memmem(void const* haystack, size_t, void const* needle, size_t); - -void* memset(void*, int, size_t); -void explicit_bzero(void*, size_t) __attribute__((nonnull(1))); - -__attribute__((malloc)) char* strdup(char const*); -__attribute__((malloc)) char* strndup(char const*, size_t); - -char* strcpy(char* dest, char const* src); -char* stpcpy(char* dest, char const* src); -char* strncpy(char* dest, char const* src, size_t); -__attribute__((warn_unused_result)) size_t strlcpy(char* dest, char const* src, size_t); - -char* strchr(char const*, int c); -char* strchrnul(char const*, int c); -char* strstr(char const* haystack, char const* needle); -char* strcasestr(char const* haystack, char const* needle); -char* strrchr(char const*, int c); - -char* index(char const* str, int ch); -char* rindex(char const* str, int ch); - -char* strcat(char* dest, char const* src); -char* strncat(char* dest, char const* src, size_t); - -size_t strspn(char const*, char const* accept); -size_t strcspn(char const*, char const* reject); -int strerror_r(int, char*, size_t); -char* strerror(int errnum); -char* strsignal(int signum); -char* strpbrk(char const*, char const* accept); -char* strtok_r(char* str, char const* delim, char** saved_str); -char* strtok(char* str, char const* delim); -int strcoll(char const* s1, char const* s2); -size_t strxfrm(char* dest, char const* src, size_t n); -char* strsep(char** str, char const* delim); - -__END_DECLS diff --git a/Userland/Libraries/LibC/strings.cpp b/Userland/Libraries/LibC/strings.cpp deleted file mode 100644 index 0614719335e..00000000000 --- a/Userland/Libraries/LibC/strings.cpp +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include - -extern "C" { - -void bzero(void* dest, size_t n) -{ - memset(dest, 0, n); -} - -void bcopy(void const* src, void* dest, size_t n) -{ - memmove(dest, src, n); -} - -static char foldcase(char ch) -{ - if (isalpha(ch)) - return tolower(ch); - return ch; -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/strcasecmp.html -int strcasecmp(char const* s1, char const* s2) -{ - for (; foldcase(*s1) == foldcase(*s2); ++s1, ++s2) { - if (*s1 == 0) - return 0; - } - return foldcase(*(unsigned char const*)s1) < foldcase(*(unsigned char const*)s2) ? -1 : 1; -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/strncasecmp.html -int strncasecmp(char const* s1, char const* s2, size_t n) -{ - if (!n) - return 0; - do { - if (foldcase(*s1) != foldcase(*s2++)) - return foldcase(*(unsigned char const*)s1) - foldcase(*(unsigned char const*)--s2); - if (*s1++ == 0) - break; - } while (--n); - return 0; -} - -// https://pubs.opengroup.org/onlinepubs/009696799/functions/ffs.html -int ffs(int i) -{ - return bit_scan_forward(i); -} - -// https://linux.die.net/man/3/ffsl (GNU extension) -int ffsl(long int i) -{ - return bit_scan_forward(i); -} - -// https://linux.die.net/man/3/ffsll (GNU extension) -int ffsll(long long int i) -{ - return bit_scan_forward(i); -} -} diff --git a/Userland/Libraries/LibC/strings.h b/Userland/Libraries/LibC/strings.h deleted file mode 100644 index a87f8ea0325..00000000000 --- a/Userland/Libraries/LibC/strings.h +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include - -__BEGIN_DECLS - -int strcasecmp(char const*, char const*); -int strncasecmp(char const*, char const*, size_t); -void bzero(void*, size_t); -void bcopy(void const*, void*, size_t); -int ffs(int); -int ffsl(long int); -int ffsll(long long int); - -__END_DECLS diff --git a/Userland/Libraries/LibC/stubs.cpp b/Userland/Libraries/LibC/stubs.cpp deleted file mode 100644 index a88017103c0..00000000000 --- a/Userland/Libraries/LibC/stubs.cpp +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Copyright (c) 2021, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -extern "C" { - -#define DO_STUB(name) \ - void name(); \ - void name() \ - { \ - } - -DO_STUB(__register_frame_info); -DO_STUB(__deregister_frame_info); -DO_STUB(_ITM_registerTMCloneTable); -DO_STUB(_ITM_deregisterTMCloneTable); -} diff --git a/Userland/Libraries/LibC/sys/arch/aarch64/regs.h b/Userland/Libraries/LibC/sys/arch/aarch64/regs.h deleted file mode 100644 index 24aa2eb40a7..00000000000 --- a/Userland/Libraries/LibC/sys/arch/aarch64/regs.h +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright (c) 2020, Itamar S. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include - -#if defined(__cplusplus) && defined(__cpp_concepts) -# include -#else -# include -#endif - -#include - -#ifdef __cplusplus -struct [[gnu::packed]] PtraceRegisters : public __mcontext { -# if defined(__cplusplus) && defined(__cpp_concepts) - FlatPtr ip() const - { - return pc; - } - - void set_ip(FlatPtr ip) - { - pc = ip; - } - - FlatPtr bp() const - { - return x[29]; - } - - void set_bp(FlatPtr bp) - { - x[29] = bp; - } -# endif -}; - -#else -typedef struct __mcontext PthreadRegisters; -#endif diff --git a/Userland/Libraries/LibC/sys/arch/regs.h b/Userland/Libraries/LibC/sys/arch/regs.h deleted file mode 100644 index f27f531ac8a..00000000000 --- a/Userland/Libraries/LibC/sys/arch/regs.h +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Copyright (c) 2021, Leon Albrecht - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include - -#if ARCH(X86_64) -# include "x86_64/regs.h" -#elif ARCH(AARCH64) -# include "aarch64/regs.h" -#elif ARCH(RISCV64) -# include "riscv64/regs.h" -#else -# error Unknown architecture -#endif diff --git a/Userland/Libraries/LibC/sys/arch/riscv64/regs.h b/Userland/Libraries/LibC/sys/arch/riscv64/regs.h deleted file mode 100644 index 84e852b6afd..00000000000 --- a/Userland/Libraries/LibC/sys/arch/riscv64/regs.h +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright (c) 2023, Sönke Holz - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include - -#if defined(__cplusplus) && defined(__cpp_concepts) -# include -#else -# include -#endif - -#include - -#ifdef __cplusplus -struct [[gnu::packed]] PtraceRegisters : public __mcontext { -# if defined(__cplusplus) && defined(__cpp_concepts) - FlatPtr ip() const - { - return pc; - } - - void set_ip(FlatPtr ip) - { - pc = ip; - } - - FlatPtr bp() const - { - return x[7]; - } - - void set_bp(FlatPtr bp) - { - x[7] = bp; - } -# endif -}; - -#else -typedef struct __mcontext PthreadRegisters; -#endif diff --git a/Userland/Libraries/LibC/sys/arch/x86_64/regs.h b/Userland/Libraries/LibC/sys/arch/x86_64/regs.h deleted file mode 100644 index aee4ad5b27a..00000000000 --- a/Userland/Libraries/LibC/sys/arch/x86_64/regs.h +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright (c) 2021, Leon Albrecht - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include - -#if defined(__cplusplus) && defined(__cpp_concepts) -# include -#else -# include -#endif - -#include - -#ifdef __cplusplus -struct [[gnu::packed]] PtraceRegisters : public __mcontext { -# if defined(__cplusplus) && defined(__cpp_concepts) - FlatPtr ip() const - { - return rip; - } - - void set_ip(FlatPtr ip) - { - rip = ip; - } - - FlatPtr bp() const - { - return rbp; - } - - void set_bp(FlatPtr bp) - { - rbp = bp; - } -# endif -}; - -#else -typedef struct __mcontext PthreadRegisters; -#endif diff --git a/Userland/Libraries/LibC/sys/archctl.cpp b/Userland/Libraries/LibC/sys/archctl.cpp deleted file mode 100644 index 5cea03c6845..00000000000 --- a/Userland/Libraries/LibC/sys/archctl.cpp +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright (c) 2024, Sönke Holz - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include - -extern "C" { - -int archctl(int option, ...) -{ - va_list args; - va_start(args, option); - - uintptr_t arg1 = va_arg(args, uintptr_t); - - va_end(args); - - int rc = syscall(SC_archctl, option, arg1); - __RETURN_WITH_ERRNO(rc, rc, -1); -} -} diff --git a/Userland/Libraries/LibC/sys/archctl.h b/Userland/Libraries/LibC/sys/archctl.h deleted file mode 100644 index 3e35a0d2e56..00000000000 --- a/Userland/Libraries/LibC/sys/archctl.h +++ /dev/null @@ -1,17 +0,0 @@ -/* - * Copyright (c) 2024, Sönke Holz - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include - -__BEGIN_DECLS - -int archctl(int option, ...); - -__END_DECLS diff --git a/Userland/Libraries/LibC/sys/auxv.cpp b/Userland/Libraries/LibC/sys/auxv.cpp deleted file mode 100644 index 38323df9f6b..00000000000 --- a/Userland/Libraries/LibC/sys/auxv.cpp +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright (c) 2021, Idan Horowitz - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include - -extern "C" { - -long getauxval(long type) -{ - errno = 0; - - auxv_t* auxvp = (auxv_t*)__auxiliary_vector; - for (; auxvp->a_type != AT_NULL; ++auxvp) { - if (auxvp->a_type == type) - return auxvp->a_un.a_val; - } - errno = ENOENT; - return 0; -} -} diff --git a/Userland/Libraries/LibC/sys/auxv.h b/Userland/Libraries/LibC/sys/auxv.h deleted file mode 100644 index c38cbfa92d6..00000000000 --- a/Userland/Libraries/LibC/sys/auxv.h +++ /dev/null @@ -1,16 +0,0 @@ -/* - * Copyright (c) 2021, Idan Horowitz - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include - -__BEGIN_DECLS - -long getauxval(long type); - -__END_DECLS diff --git a/Userland/Libraries/LibC/sys/cdefs.h b/Userland/Libraries/LibC/sys/cdefs.h deleted file mode 100644 index e428ad6085a..00000000000 --- a/Userland/Libraries/LibC/sys/cdefs.h +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#define _POSIX_VERSION 200809L - -#ifdef __cplusplus -# ifndef __BEGIN_DECLS -# define __BEGIN_DECLS extern "C" { -# define __END_DECLS } -# endif -#else -# ifndef __BEGIN_DECLS -# define __BEGIN_DECLS -# define __END_DECLS -# endif -#endif - -#undef __P -#define __P(a) a diff --git a/Userland/Libraries/LibC/sys/device.h b/Userland/Libraries/LibC/sys/device.h deleted file mode 100644 index 980c9d73905..00000000000 --- a/Userland/Libraries/LibC/sys/device.h +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright (c) 2018-2021, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include - -__BEGIN_DECLS - -static ALWAYS_INLINE dev_t serenity_dev_makedev(unsigned major, unsigned minor) -{ - return (minor & 0xffu) | (major << 8u) | ((minor & ~0xffu) << 12u); -} - -static ALWAYS_INLINE unsigned int serenity_dev_major(dev_t dev) -{ - return (dev & 0xfff00u) >> 8u; -} - -static ALWAYS_INLINE unsigned int serenity_dev_minor(dev_t dev) -{ - return (dev & 0xffu) | ((dev >> 12u) & 0xfff00u); -} - -__END_DECLS diff --git a/Userland/Libraries/LibC/sys/devices/gpu.h b/Userland/Libraries/LibC/sys/devices/gpu.h deleted file mode 100644 index dadeeaf272e..00000000000 --- a/Userland/Libraries/LibC/sys/devices/gpu.h +++ /dev/null @@ -1,117 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -__BEGIN_DECLS - -ALWAYS_INLINE int graphics_connector_get_properties(int fd, GraphicsConnectorProperties* info) -{ - return ioctl(fd, GRAPHICS_IOCTL_GET_PROPERTIES, info); -} - -ALWAYS_INLINE int graphics_connector_get_head_edid(int fd, GraphicsHeadEDID* info) -{ - // FIXME: Optimize this function to get a minor number instead of a file descriptor. - struct stat display_connector_stat; - if (auto rc = fstat(fd, &display_connector_stat); rc < 0) { - return rc; - } - auto minor_number = minor(display_connector_stat.st_rdev); - - auto edid_fd = open(ByteString::formatted("/sys/devices/graphics/connectors/{}/edid", minor_number).characters(), O_RDONLY); - if (edid_fd < 0) { - return edid_fd; - } - - ScopeGuard close_on_return([&]() { - close(edid_fd); - }); - - if (auto rc = read(edid_fd, info->bytes, info->bytes_size); rc < 0) { - return rc; - } - - return 0; -} - -ALWAYS_INLINE int graphics_connector_set_responsible(int fd) -{ - return ioctl(fd, GRAPHICS_IOCTL_SET_RESPONSIBLE, nullptr); -} - -ALWAYS_INLINE int graphics_connector_unset_responsible(int fd) -{ - return ioctl(fd, GRAPHICS_IOCTL_UNSET_RESPONSIBLE, nullptr); -} - -ALWAYS_INLINE int fb_get_head_vertical_offset_buffer(int fd, GraphicsHeadVerticalOffset* vertical_offset) -{ - return ioctl(fd, GRAPHICS_IOCTL_GET_HEAD_VERTICAL_OFFSET_BUFFER, vertical_offset); -} - -ALWAYS_INLINE int fb_set_head_vertical_offset_buffer(int fd, GraphicsHeadVerticalOffset* vertical_offset) -{ - return ioctl(fd, GRAPHICS_IOCTL_SET_HEAD_VERTICAL_OFFSET_BUFFER, vertical_offset); -} - -ALWAYS_INLINE int graphics_connector_set_head_mode_setting(int fd, GraphicsHeadModeSetting* mode_setting) -{ - return ioctl(fd, GRAPHICS_IOCTL_SET_HEAD_MODE_SETTING, mode_setting); -} - -ALWAYS_INLINE int graphics_connector_set_safe_head_mode_setting(int fd) -{ - return ioctl(fd, GRAPHICS_IOCTL_SET_SAFE_HEAD_MODE_SETTING, nullptr); -} - -ALWAYS_INLINE int graphics_connector_get_head_mode_setting(int fd, GraphicsHeadModeSetting* mode_setting) -{ - GraphicsHeadModeSetting head_mode_setting; - if (auto rc = ioctl(fd, GRAPHICS_IOCTL_GET_HEAD_MODE_SETTING, &head_mode_setting); rc < 0) - return rc; - mode_setting->horizontal_stride = head_mode_setting.horizontal_stride; - mode_setting->pixel_clock_in_khz = head_mode_setting.pixel_clock_in_khz; - mode_setting->horizontal_active = head_mode_setting.horizontal_active; - mode_setting->horizontal_front_porch_pixels = head_mode_setting.horizontal_front_porch_pixels; - mode_setting->horizontal_sync_time_pixels = head_mode_setting.horizontal_sync_time_pixels; - mode_setting->horizontal_blank_pixels = head_mode_setting.horizontal_blank_pixels; - mode_setting->vertical_active = head_mode_setting.vertical_active; - mode_setting->vertical_front_porch_lines = head_mode_setting.vertical_front_porch_lines; - mode_setting->vertical_sync_time_lines = head_mode_setting.vertical_sync_time_lines; - mode_setting->vertical_blank_lines = head_mode_setting.vertical_blank_lines; - mode_setting->horizontal_offset = head_mode_setting.horizontal_offset; - mode_setting->vertical_offset = head_mode_setting.vertical_offset; - return 0; -} - -ALWAYS_INLINE int fb_flush_buffers(int fd, int index, FBRect const* rects, unsigned count) -{ - FBFlushRects fb_flush_rects; - fb_flush_rects.buffer_index = index; - fb_flush_rects.count = count; - fb_flush_rects.rects = rects; - return ioctl(fd, GRAPHICS_IOCTL_FLUSH_HEAD_BUFFERS, &fb_flush_rects); -} - -ALWAYS_INLINE int fb_flush_head(int fd) -{ - return ioctl(fd, GRAPHICS_IOCTL_FLUSH_HEAD, nullptr); -} - -__END_DECLS diff --git a/Userland/Libraries/LibC/sys/file.cpp b/Userland/Libraries/LibC/sys/file.cpp deleted file mode 100644 index 826ca21af8b..00000000000 --- a/Userland/Libraries/LibC/sys/file.cpp +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Copyright (c) 2021, Peter Elliott - * Copyright (c) 2022, Idan Horowitz - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include - -extern "C" { - -int flock(int fd, int operation) -{ - struct flock lock { - short(operation & 0b11), SEEK_SET, 0, 0, 0 - }; - return fcntl(fd, (operation & LOCK_NB) ? F_SETLK : F_SETLKW, &lock); -} -} diff --git a/Userland/Libraries/LibC/sys/file.h b/Userland/Libraries/LibC/sys/file.h deleted file mode 100644 index 2abfb7665df..00000000000 --- a/Userland/Libraries/LibC/sys/file.h +++ /dev/null @@ -1,20 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include - -__BEGIN_DECLS - -#define LOCK_SH 0 -#define LOCK_EX 1 -#define LOCK_UN 2 -#define LOCK_NB (1 << 2) - -int flock(int fd, int operation); - -__END_DECLS diff --git a/Userland/Libraries/LibC/sys/internals.h b/Userland/Libraries/LibC/sys/internals.h deleted file mode 100644 index e88b66fc543..00000000000 --- a/Userland/Libraries/LibC/sys/internals.h +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright (c) 2020-2023, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include - -__BEGIN_DECLS - -typedef void (*AtExitFunction)(void*); - -extern void __libc_init(); -extern void __malloc_init(void); -extern void __stdio_init(void); -extern void __begin_atexit_locking(void); -extern void _init(void); -extern bool __environ_is_malloced; -extern bool __stdio_is_initialized; -extern bool __heap_is_stable; -extern void* __auxiliary_vector; - -int __cxa_atexit(AtExitFunction exit_function, void* parameter, void* dso_handle); -void __cxa_finalize(void* dso_handle); -__attribute__((noreturn)) void __cxa_pure_virtual(void) __attribute__((weak)); -__attribute__((noreturn)) void __stack_chk_fail(void); -__attribute__((noreturn)) void __stack_chk_fail_local(void); - -struct __tls_index { - size_t ti_module; - size_t ti_offset; -}; - -void* __tls_get_addr(__tls_index*); - -__END_DECLS diff --git a/Userland/Libraries/LibC/sys/ioctl.h b/Userland/Libraries/LibC/sys/ioctl.h deleted file mode 100644 index e1d6fdcb670..00000000000 --- a/Userland/Libraries/LibC/sys/ioctl.h +++ /dev/null @@ -1,16 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include - -__BEGIN_DECLS - -int ioctl(int fd, unsigned request, ...); - -__END_DECLS diff --git a/Userland/Libraries/LibC/sys/mman.cpp b/Userland/Libraries/LibC/sys/mman.cpp deleted file mode 100644 index 66fe64bb424..00000000000 --- a/Userland/Libraries/LibC/sys/mman.cpp +++ /dev/null @@ -1,109 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include -#include -#include - -extern "C" { - -void* serenity_mmap(void* addr, size_t size, int prot, int flags, int fd, off_t offset, size_t alignment, char const* name) -{ - Syscall::SC_mmap_params params { addr, size, alignment, prot, flags, fd, offset, { name, name ? strlen(name) : 0 } }; - ptrdiff_t rc = syscall(SC_mmap, ¶ms); - if (rc < 0 && rc > -EMAXERRNO) { - errno = -rc; - return MAP_FAILED; - } - return (void*)rc; -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/mmap.html -void* mmap(void* addr, size_t size, int prot, int flags, int fd, off_t offset) -{ - return serenity_mmap(addr, size, prot, flags, fd, offset, PAGE_SIZE, nullptr); -} - -void* mmap_with_name(void* addr, size_t size, int prot, int flags, int fd, off_t offset, char const* name) -{ - return serenity_mmap(addr, size, prot, flags, fd, offset, PAGE_SIZE, name); -} - -void* mremap(void* old_address, size_t old_size, size_t new_size, int flags) -{ - Syscall::SC_mremap_params params { old_address, old_size, new_size, flags }; - ptrdiff_t rc = syscall(SC_mremap, ¶ms); - if (rc < 0 && rc > -EMAXERRNO) { - errno = -rc; - return MAP_FAILED; - } - return (void*)rc; -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/munmap.html -int munmap(void* addr, size_t size) -{ - int rc = syscall(SC_munmap, addr, size); - __RETURN_WITH_ERRNO(rc, rc, -1); -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/mprotect.html -int mprotect(void* addr, size_t size, int prot) -{ - int rc = syscall(SC_mprotect, addr, size, prot); - __RETURN_WITH_ERRNO(rc, rc, -1); -} - -int set_mmap_name(void* addr, size_t size, char const* name) -{ - if (!name) { - errno = EFAULT; - return -1; - } - Syscall::SC_set_mmap_name_params params { addr, size, { name, strlen(name) } }; - int rc = syscall(SC_set_mmap_name, ¶ms); - __RETURN_WITH_ERRNO(rc, rc, -1); -} - -int madvise(void* address, size_t size, int advice) -{ - int rc = syscall(SC_madvise, address, size, advice); - __RETURN_WITH_ERRNO(rc, rc, -1); -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/posix_madvise.html -int posix_madvise(void* address, size_t len, int advice) -{ - return madvise(address, len, advice); -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/mlock.html -int mlock(void const*, size_t) -{ - dbgln("FIXME: Implement mlock()"); - return 0; -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/munlock.html -int munlock(void const*, size_t) -{ - dbgln("FIXME: Implement munlock()"); - return 0; -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/msync.html -int msync(void* address, size_t size, int flags) -{ - __pthread_maybe_cancel(); - - int rc = syscall(SC_msync, address, size, flags); - __RETURN_WITH_ERRNO(rc, rc, -1); -} -} diff --git a/Userland/Libraries/LibC/sys/mman.h b/Userland/Libraries/LibC/sys/mman.h deleted file mode 100644 index 76d460b0a51..00000000000 --- a/Userland/Libraries/LibC/sys/mman.h +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright (c) 2018-2021, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include - -__BEGIN_DECLS - -void* mmap(void* addr, size_t, int prot, int flags, int fd, off_t); -void* mmap_with_name(void* addr, size_t, int prot, int flags, int fd, off_t, char const* name); -void* serenity_mmap(void* addr, size_t, int prot, int flags, int fd, off_t, size_t alignment, char const* name); -void* mremap(void* old_address, size_t old_size, size_t new_size, int flags); -int munmap(void*, size_t); -int mprotect(void*, size_t, int prot); -int set_mmap_name(void*, size_t, char const*); -int madvise(void*, size_t, int advice); -int posix_madvise(void*, size_t, int advice); -int mlock(void const*, size_t); -int munlock(void const*, size_t); -int msync(void*, size_t, int flags); - -__END_DECLS diff --git a/Userland/Libraries/LibC/sys/param.h b/Userland/Libraries/LibC/sys/param.h deleted file mode 100644 index d86a6e8f396..00000000000 --- a/Userland/Libraries/LibC/sys/param.h +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Copyright (c) 2018-2022, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include - -#ifndef MIN -# define MIN(a, b) (((a) < (b)) ? (a) : (b)) -#endif - -#ifndef MAX -# define MAX(a, b) (((a) > (b)) ? (a) : (b)) -#endif - -#ifndef howmany -# define howmany(x, y) (((x) + ((y) - 1)) / (y)) -#endif diff --git a/Userland/Libraries/LibC/sys/poll.h b/Userland/Libraries/LibC/sys/poll.h deleted file mode 100644 index 5a380f28cb3..00000000000 --- a/Userland/Libraries/LibC/sys/poll.h +++ /dev/null @@ -1,9 +0,0 @@ -/* - * Copyright (c) 2021, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include diff --git a/Userland/Libraries/LibC/sys/prctl.cpp b/Userland/Libraries/LibC/sys/prctl.cpp deleted file mode 100644 index 7042fc81efe..00000000000 --- a/Userland/Libraries/LibC/sys/prctl.cpp +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright (c) 2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include - -extern "C" { - -int prctl(int option, ...) -{ - va_list args; - va_start(args, option); - - uintptr_t arg1 = va_arg(args, uintptr_t); - uintptr_t arg2 = va_arg(args, uintptr_t); - uintptr_t arg3 = va_arg(args, uintptr_t); - - va_end(args); - - int rc = syscall(SC_prctl, option, arg1, arg2, arg3); - __RETURN_WITH_ERRNO(rc, rc, -1); -} -} diff --git a/Userland/Libraries/LibC/sys/prctl.h b/Userland/Libraries/LibC/sys/prctl.h deleted file mode 100644 index d06b4f9351a..00000000000 --- a/Userland/Libraries/LibC/sys/prctl.h +++ /dev/null @@ -1,17 +0,0 @@ -/* - * Copyright (c) 2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include - -__BEGIN_DECLS - -int prctl(int option, ...); - -__END_DECLS diff --git a/Userland/Libraries/LibC/sys/ptrace.cpp b/Userland/Libraries/LibC/sys/ptrace.cpp deleted file mode 100644 index 5b498d6e171..00000000000 --- a/Userland/Libraries/LibC/sys/ptrace.cpp +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright (c) 2020, Itamar S. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include - -extern "C" { - -long ptrace(int request, pid_t tid, void* addr, void* data) -{ - if (request == PT_PEEKBUF) { - // PT_PEEKBUF cannot easily be correctly used through this function signature: - // The amount of data to be copied is not available. - // We could VERIFY() here, but to safeguard against ports that attempt to use - // the same number, let's claim that the Kernel just doesn't know the command. - // Use Core::System::ptrace_peekbuf instead. - return EINVAL; - } - - // PT_PEEK needs special handling since the syscall wrapper - // returns the peeked value as an int, which can be negative because of the cast. - // When using PT_PEEK, the user can check if an error occurred - // by looking at errno rather than the return value. - - FlatPtr out_data; - auto is_peek_type = request == PT_PEEK || request == PT_PEEKDEBUG; - if (is_peek_type) { - data = &out_data; - } - - Syscall::SC_ptrace_params params { - request, - tid, - addr, - (FlatPtr)data - }; - long rc = syscall(SC_ptrace, ¶ms); - - if (is_peek_type) { - if (rc < 0) { - errno = -rc; - return -1; - } - errno = 0; - return static_cast(out_data); - } - - __RETURN_WITH_ERRNO(rc, rc, -1); -} -} diff --git a/Userland/Libraries/LibC/sys/ptrace.h b/Userland/Libraries/LibC/sys/ptrace.h deleted file mode 100644 index a240fbb6ad0..00000000000 --- a/Userland/Libraries/LibC/sys/ptrace.h +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Copyright (c) 2020, Itamar S. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include - -__BEGIN_DECLS - -// FIXME: PID/TID ISSUE -// Affects the entirety of LibDebug and Userland/strace.cpp. -// See also Kernel/Ptrace.cpp -long ptrace(int request, pid_t tid, void* addr, void* data); - -__END_DECLS diff --git a/Userland/Libraries/LibC/sys/resource.h b/Userland/Libraries/LibC/sys/resource.h deleted file mode 100644 index ce954d9e399..00000000000 --- a/Userland/Libraries/LibC/sys/resource.h +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -// Includes essentially mandated by POSIX: -// https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/sys_resource.h.html -#include - -#include -#include - -__BEGIN_DECLS - -struct rusage { - struct timeval ru_utime; - struct timeval ru_stime; - long ru_maxrss; - long ru_ixrss; - long ru_idrss; - long ru_isrss; - long ru_minflt; - long ru_majflt; - long ru_nswap; - long ru_inblock; - long ru_oublock; - long ru_msgsnd; - long ru_msgrcv; - long ru_nsignals; - long ru_nvcsw; - long ru_nivcsw; -}; - -#define RUSAGE_SELF 1 -#define RUSAGE_CHILDREN 2 - -int getrusage(int who, struct rusage* usage); - -#define RLIMIT_CORE 1 -#define RLIMIT_CPU 2 -#define RLIMIT_DATA 3 -#define RLIMIT_FSIZE 4 -#define RLIMIT_NOFILE 5 -#define RLIMIT_STACK 6 -#define RLIMIT_AS 7 - -#define RLIM_NLIMITS 8 - -#define RLIM_INFINITY SIZE_MAX - -typedef size_t rlim_t; - -struct rlimit { - rlim_t rlim_cur; - rlim_t rlim_max; -}; - -int getrlimit(int, struct rlimit*); -int setrlimit(int, struct rlimit const*); - -#define PRIO_PROCESS 0 -#define PRIO_PGRP 1 -#define PRIO_USER 2 - -int getpriority(int, id_t); -int setpriority(int, id_t, int); - -__END_DECLS diff --git a/Userland/Libraries/LibC/sys/select.cpp b/Userland/Libraries/LibC/sys/select.cpp deleted file mode 100644 index 2669a0832f4..00000000000 --- a/Userland/Libraries/LibC/sys/select.cpp +++ /dev/null @@ -1,87 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include -#include -#include - -#include - -extern "C" { - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/select.html -int select(int nfds, fd_set* readfds, fd_set* writefds, fd_set* exceptfds, timeval* timeout_tv) -{ - __pthread_maybe_cancel(); - - timespec* timeout_ts = nullptr; - timespec timeout; - if (timeout_tv) { - timeout_ts = &timeout; - TIMEVAL_TO_TIMESPEC(timeout_tv, timeout_ts); - } - return pselect(nfds, readfds, writefds, exceptfds, timeout_ts, nullptr); -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/pselect.html -int pselect(int nfds, fd_set* readfds, fd_set* writefds, fd_set* exceptfds, timespec const* timeout, sigset_t const* sigmask) -{ - __pthread_maybe_cancel(); - - Vector fds; - - if (nfds < 0 || static_cast(nfds) >= fds.capacity()) - return EINVAL; - - for (int fd = 0; fd < nfds; fd++) { - short events = 0; - if (readfds && FD_ISSET(fd, readfds)) - events |= POLLIN; - if (writefds && FD_ISSET(fd, writefds)) - events |= POLLOUT; - if (exceptfds && FD_ISSET(fd, exceptfds)) - events |= POLLPRI; - if (!events) - continue; - - fds.unchecked_append({ fd, events, 0 }); - } - - if (ppoll(fds.data(), fds.size(), timeout, sigmask) < 0) - return -1; - - if (readfds) - FD_ZERO(readfds); - if (writefds) - FD_ZERO(writefds); - if (exceptfds) - FD_ZERO(exceptfds); - - int marked_fd_count = 0; - for (auto& fd_entry : fds) { - if (fd_entry.revents == 0) - continue; - if (readfds && (fd_entry.revents & POLLIN)) { - FD_SET(fd_entry.fd, readfds); - marked_fd_count++; - } - if (writefds && (fd_entry.revents & POLLOUT)) { - FD_SET(fd_entry.fd, writefds); - marked_fd_count++; - } - if (exceptfds && (fd_entry.revents & (POLLPRI | POLLERR | POLLHUP))) { - FD_SET(fd_entry.fd, exceptfds); - marked_fd_count++; - } - } - - return marked_fd_count; -} -} diff --git a/Userland/Libraries/LibC/sys/select.h b/Userland/Libraries/LibC/sys/select.h deleted file mode 100644 index 029d0e2aa72..00000000000 --- a/Userland/Libraries/LibC/sys/select.h +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -// Make sure we have the time type definitions. We include the kernel API -// header and not the LibC to avoid issues with circular includes. -#include - -// Includes essentially mandated by POSIX: -// https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/sys_select.h.html -#include -#include - -#include -#include -#include -#include - -__BEGIN_DECLS - -int select(int nfds, fd_set* readfds, fd_set* writefds, fd_set* exceptfds, struct timeval* timeout); -int pselect(int nfds, fd_set* readfds, fd_set* writefds, fd_set* exceptfds, const struct timespec* timeout, sigset_t const* sigmask); - -__END_DECLS diff --git a/Userland/Libraries/LibC/sys/socket.cpp b/Userland/Libraries/LibC/sys/socket.cpp deleted file mode 100644 index 54d67403b06..00000000000 --- a/Userland/Libraries/LibC/sys/socket.cpp +++ /dev/null @@ -1,180 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -extern "C" { - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/socket.html -int socket(int domain, int type, int protocol) -{ - int rc = syscall(SC_socket, domain, type, protocol); - __RETURN_WITH_ERRNO(rc, rc, -1); -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/bind.html -int bind(int sockfd, sockaddr const* addr, socklen_t addrlen) -{ - int rc = syscall(SC_bind, sockfd, addr, addrlen); - __RETURN_WITH_ERRNO(rc, rc, -1); -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/listen.html -int listen(int sockfd, int backlog) -{ - int rc = syscall(SC_listen, sockfd, backlog); - __RETURN_WITH_ERRNO(rc, rc, -1); -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/accept.html -int accept(int sockfd, sockaddr* addr, socklen_t* addrlen) -{ - __pthread_maybe_cancel(); - - return accept4(sockfd, addr, addrlen, 0); -} - -int accept4(int sockfd, sockaddr* addr, socklen_t* addrlen, int flags) -{ - Syscall::SC_accept4_params params { addr, addrlen, sockfd, flags }; - int rc = syscall(SC_accept4, ¶ms); - __RETURN_WITH_ERRNO(rc, rc, -1); -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/connect.html -int connect(int sockfd, sockaddr const* addr, socklen_t addrlen) -{ - __pthread_maybe_cancel(); - - int rc = syscall(SC_connect, sockfd, addr, addrlen); - __RETURN_WITH_ERRNO(rc, rc, -1); -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/shutdown.html -int shutdown(int sockfd, int how) -{ - int rc = syscall(SC_shutdown, sockfd, how); - __RETURN_WITH_ERRNO(rc, rc, -1); -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/sendmsg.html -ssize_t sendmsg(int sockfd, const struct msghdr* msg, int flags) -{ - __pthread_maybe_cancel(); - - int rc = syscall(SC_sendmsg, sockfd, msg, flags); - __RETURN_WITH_ERRNO(rc, rc, -1); -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/sendto.html -ssize_t sendto(int sockfd, void const* data, size_t data_length, int flags, const struct sockaddr* addr, socklen_t addr_length) -{ - iovec iov = { const_cast(data), data_length }; - msghdr msg = { const_cast(addr), addr_length, &iov, 1, nullptr, 0, 0 }; - return sendmsg(sockfd, &msg, flags); -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/send.html -ssize_t send(int sockfd, void const* data, size_t data_length, int flags) -{ - return sendto(sockfd, data, data_length, flags, nullptr, 0); -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/recvmsg.html -ssize_t recvmsg(int sockfd, struct msghdr* msg, int flags) -{ - __pthread_maybe_cancel(); - - int rc = syscall(SC_recvmsg, sockfd, msg, flags); - __RETURN_WITH_ERRNO(rc, rc, -1); -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/recvfrom.html -ssize_t recvfrom(int sockfd, void* buffer, size_t buffer_length, int flags, struct sockaddr* addr, socklen_t* addr_length) -{ - __pthread_maybe_cancel(); - - if (!addr_length && addr) { - errno = EINVAL; - return -1; - } - - sockaddr_storage internal_addr; - iovec iov = { buffer, buffer_length }; - msghdr msg = { addr ? &internal_addr : nullptr, addr ? (socklen_t)sizeof(internal_addr) : 0, &iov, 1, nullptr, 0, 0 }; - ssize_t rc = recvmsg(sockfd, &msg, flags); - if (rc >= 0 && addr) { - memcpy(addr, &internal_addr, min(*addr_length, msg.msg_namelen)); - *addr_length = msg.msg_namelen; - } - return rc; -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/recv.html -ssize_t recv(int sockfd, void* buffer, size_t buffer_length, int flags) -{ - return recvfrom(sockfd, buffer, buffer_length, flags, nullptr, nullptr); -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/getsockopt.html -int getsockopt(int sockfd, int level, int option, void* value, socklen_t* value_size) -{ - Syscall::SC_getsockopt_params params { sockfd, level, option, value, value_size }; - int rc = syscall(SC_getsockopt, ¶ms); - __RETURN_WITH_ERRNO(rc, rc, -1); -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/setsockopt.html -int setsockopt(int sockfd, int level, int option, void const* value, socklen_t value_size) -{ - Syscall::SC_setsockopt_params params { value, sockfd, level, option, value_size }; - int rc = syscall(SC_setsockopt, ¶ms); - __RETURN_WITH_ERRNO(rc, rc, -1); -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/getsockname.html -int getsockname(int sockfd, struct sockaddr* addr, socklen_t* addrlen) -{ - Syscall::SC_getsockname_params params { sockfd, addr, addrlen }; - int rc = syscall(SC_getsockname, ¶ms); - __RETURN_WITH_ERRNO(rc, rc, -1); -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/getpeername.html -int getpeername(int sockfd, struct sockaddr* addr, socklen_t* addrlen) -{ - Syscall::SC_getpeername_params params { sockfd, addr, addrlen }; - int rc = syscall(SC_getpeername, ¶ms); - __RETURN_WITH_ERRNO(rc, rc, -1); -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/socketpair.html -int socketpair(int domain, int type, int protocol, int sv[2]) -{ - Syscall::SC_socketpair_params params { domain, type, protocol, sv }; - int rc = syscall(SC_socketpair, ¶ms); - __RETURN_WITH_ERRNO(rc, rc, -1); -} - -int sendfd(int sockfd, int fd) -{ - int rc = syscall(SC_sendfd, sockfd, fd); - __RETURN_WITH_ERRNO(rc, rc, -1); -} - -int recvfd(int sockfd, int options) -{ - int rc = syscall(SC_recvfd, sockfd, options); - __RETURN_WITH_ERRNO(rc, rc, -1); -} -} diff --git a/Userland/Libraries/LibC/sys/socket.h b/Userland/Libraries/LibC/sys/socket.h deleted file mode 100644 index bb8aee03edc..00000000000 --- a/Userland/Libraries/LibC/sys/socket.h +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -// Includes essentially mandated by POSIX: -// https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/sys_socket.h.html -#include - -#include -#include -#include - -__BEGIN_DECLS - -int socket(int domain, int type, int protocol); -int bind(int sockfd, const struct sockaddr* addr, socklen_t); -int listen(int sockfd, int backlog); -int accept(int sockfd, struct sockaddr*, socklen_t*); -int accept4(int sockfd, struct sockaddr*, socklen_t*, int); -int connect(int sockfd, const struct sockaddr*, socklen_t); -int shutdown(int sockfd, int how); -ssize_t send(int sockfd, void const*, size_t, int flags); -ssize_t sendmsg(int sockfd, const struct msghdr*, int flags); -ssize_t sendto(int sockfd, void const*, size_t, int flags, const struct sockaddr*, socklen_t); -ssize_t recv(int sockfd, void*, size_t, int flags); -ssize_t recvmsg(int sockfd, struct msghdr*, int flags); -ssize_t recvfrom(int sockfd, void*, size_t, int flags, struct sockaddr*, socklen_t*); -int getsockopt(int sockfd, int level, int option, void*, socklen_t*); -int setsockopt(int sockfd, int level, int option, void const*, socklen_t); -int getsockname(int sockfd, struct sockaddr*, socklen_t*); -int getpeername(int sockfd, struct sockaddr*, socklen_t*); -int socketpair(int domain, int type, int protocol, int sv[2]); -int sendfd(int sockfd, int fd); -int recvfd(int sockfd, int options); - -__END_DECLS diff --git a/Userland/Libraries/LibC/sys/stat.h b/Userland/Libraries/LibC/sys/stat.h deleted file mode 100644 index 67417a11ef9..00000000000 --- a/Userland/Libraries/LibC/sys/stat.h +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright (c) 2018-2021, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include - -__BEGIN_DECLS - -mode_t umask(mode_t); -int chmod(char const* pathname, mode_t); -int fchmodat(int fd, char const* path, mode_t mode, int flag); -int fchmod(int fd, mode_t); -int mkdir(char const* pathname, mode_t); -int mkdirat(int dirfd, char const* pathname, mode_t); -int mkfifo(char const* pathname, mode_t); -int mkfifoat(int dirfd, char const* pathname, mode_t); -int fstat(int fd, struct stat* statbuf); -int lstat(char const* path, struct stat* statbuf); -int stat(char const* path, struct stat* statbuf); -int fstatat(int fd, char const* path, struct stat* statbuf, int flags); -int futimens(int fd, struct timespec const times[2]); - -__END_DECLS diff --git a/Userland/Libraries/LibC/sys/statvfs.cpp b/Userland/Libraries/LibC/sys/statvfs.cpp deleted file mode 100644 index d7b238520b2..00000000000 --- a/Userland/Libraries/LibC/sys/statvfs.cpp +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright (c) 2021, Justin Mietzner - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include - -extern "C" { - -int statvfs(char const* path, struct statvfs* buf) -{ - Syscall::SC_statvfs_params params { { path, strlen(path) }, buf }; - int rc = syscall(SC_statvfs, ¶ms); - __RETURN_WITH_ERRNO(rc, rc, -1); -} - -int fstatvfs(int fd, struct statvfs* buf) -{ - int rc = syscall(SC_fstatvfs, fd, buf); - __RETURN_WITH_ERRNO(rc, rc, -1); -} -} diff --git a/Userland/Libraries/LibC/sys/statvfs.h b/Userland/Libraries/LibC/sys/statvfs.h deleted file mode 100644 index f697c5e6b86..00000000000 --- a/Userland/Libraries/LibC/sys/statvfs.h +++ /dev/null @@ -1,17 +0,0 @@ -/* - * Copyright (c) 2021, Justin Mietzner - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include - -__BEGIN_DECLS - -int statvfs(char const* path, struct statvfs* buf); -int fstatvfs(int fd, struct statvfs* buf); - -__END_DECLS diff --git a/Userland/Libraries/LibC/sys/sysmacros.h b/Userland/Libraries/LibC/sys/sysmacros.h deleted file mode 100644 index 5d42b76abcb..00000000000 --- a/Userland/Libraries/LibC/sys/sysmacros.h +++ /dev/null @@ -1,13 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include - -#define makedev(major, minor) serenity_dev_makedev((major), (minor)) -#define major(dev) serenity_dev_major(dev) -#define minor(dev) serenity_dev_minor(dev) diff --git a/Userland/Libraries/LibC/sys/time.h b/Userland/Libraries/LibC/sys/time.h deleted file mode 100644 index e98ff48d655..00000000000 --- a/Userland/Libraries/LibC/sys/time.h +++ /dev/null @@ -1,117 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -// Includes essentially mandated by POSIX: -// https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/sys_time.h.html -#include - -#include -#include -#include - -__BEGIN_DECLS - -struct timezone { - int tz_minuteswest; - int tz_dsttime; -}; - -int adjtime(const struct timeval* delta, struct timeval* old_delta); -int gettimeofday(struct timeval* __restrict__, void* __restrict__); -int settimeofday(struct timeval* __restrict__, void* __restrict__); -int utimes(char const* pathname, struct timeval const times[2]); -int lutimes(char const* pathname, struct timeval const times[2]); -int futimes(int fd, struct timeval const times[2]); - -#define timeradd timeradd -#define timersub timersub -#define timerclear timerclear -#define timerisset timerisset -#define timercmp(tvp, uvp, cmp) \ - (((tvp)->tv_sec == (uvp)->tv_sec) ? ((tvp)->tv_usec cmp(uvp)->tv_usec) : ((tvp)->tv_sec cmp(uvp)->tv_sec)) - -static inline void timeradd(const struct timeval* a, const struct timeval* b, struct timeval* out) -{ - out->tv_sec = a->tv_sec + b->tv_sec; - out->tv_usec = a->tv_usec + b->tv_usec; - if (out->tv_usec >= 1000 * 1000) { - out->tv_sec++; - out->tv_usec -= 1000 * 1000; - } -} - -static inline void timersub(const struct timeval* a, const struct timeval* b, struct timeval* out) -{ - out->tv_sec = a->tv_sec - b->tv_sec; - out->tv_usec = a->tv_usec - b->tv_usec; - if (out->tv_usec < 0) { - out->tv_sec--; - out->tv_usec += 1000 * 1000; - } -} - -static inline void timerclear(struct timeval* out) -{ - out->tv_sec = out->tv_usec = 0; -} - -static inline int timerisset(const struct timeval* tv) -{ - return tv->tv_sec || tv->tv_usec; -} - -#define timespecadd timespecadd -#define timespecsub timespecsub -#define timespecclear timespecclear -#define timespecisset timespecisset -#define timespeccmp(ts, us, cmp) \ - (((ts)->tv_sec == (us)->tv_sec) ? ((ts)->tv_nsec cmp(us)->tv_nsec) : ((ts)->tv_sec cmp(us)->tv_sec)) - -static inline void timespecadd(const struct timespec* a, const struct timespec* b, struct timespec* out) -{ - out->tv_sec = a->tv_sec + b->tv_sec; - out->tv_nsec = a->tv_nsec + b->tv_nsec; - if (out->tv_nsec >= 1000 * 1000 * 1000) { - out->tv_sec++; - out->tv_nsec -= 1000 * 1000 * 1000; - } -} - -static inline void timespecsub(const struct timespec* a, const struct timespec* b, struct timespec* out) -{ - out->tv_sec = a->tv_sec - b->tv_sec; - out->tv_nsec = a->tv_nsec - b->tv_nsec; - if (out->tv_nsec < 0) { - out->tv_sec--; - out->tv_nsec += 1000 * 1000 * 1000; - } -} - -static inline void timespecclear(struct timespec* out) -{ - out->tv_sec = out->tv_nsec = 0; -} - -static inline int timespecisset(const struct timespec* ts) -{ - return ts->tv_sec || ts->tv_nsec; -} - -static inline void TIMEVAL_TO_TIMESPEC(const struct timeval* tv, struct timespec* ts) -{ - ts->tv_sec = tv->tv_sec; - ts->tv_nsec = tv->tv_usec * 1000; -} - -static inline void TIMESPEC_TO_TIMEVAL(struct timeval* tv, const struct timespec* ts) -{ - tv->tv_sec = ts->tv_sec; - tv->tv_usec = ts->tv_nsec / 1000; -} - -__END_DECLS diff --git a/Userland/Libraries/LibC/sys/times.h b/Userland/Libraries/LibC/sys/times.h deleted file mode 100644 index 066f1590907..00000000000 --- a/Userland/Libraries/LibC/sys/times.h +++ /dev/null @@ -1,16 +0,0 @@ -/* - * Copyright (c) 2018-2021, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include - -__BEGIN_DECLS - -clock_t times(struct tms*); - -__END_DECLS diff --git a/Userland/Libraries/LibC/sys/ttydefaults.h b/Userland/Libraries/LibC/sys/ttydefaults.h deleted file mode 100644 index 057f7e7c8ba..00000000000 --- a/Userland/Libraries/LibC/sys/ttydefaults.h +++ /dev/null @@ -1,15 +0,0 @@ -/* - * Copyright (c) 2021, Daniel Bertalan - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include - -#ifdef TTYDEFCHARS -# include - -# include -#endif diff --git a/Userland/Libraries/LibC/sys/types.h b/Userland/Libraries/LibC/sys/types.h deleted file mode 100644 index 09101867129..00000000000 --- a/Userland/Libraries/LibC/sys/types.h +++ /dev/null @@ -1,13 +0,0 @@ -/* - * Copyright (c) 2018-2021, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include - -#include diff --git a/Userland/Libraries/LibC/sys/uio.cpp b/Userland/Libraries/LibC/sys/uio.cpp deleted file mode 100644 index 8e7d19162d2..00000000000 --- a/Userland/Libraries/LibC/sys/uio.cpp +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include - -extern "C" { - -ssize_t writev(int fd, const struct iovec* iov, int iov_count) -{ - return pwritev(fd, iov, iov_count, -1); -} - -ssize_t readv(int fd, const struct iovec* iov, int iov_count) -{ - __pthread_maybe_cancel(); - - int rc = syscall(SC_readv, fd, iov, iov_count); - __RETURN_WITH_ERRNO(rc, rc, -1); -} - -ssize_t pwritev(int fd, struct iovec const* iov, int iov_count, off_t offset) -{ - __pthread_maybe_cancel(); - - int rc = syscall(SC_pwritev, fd, iov, iov_count, offset); - __RETURN_WITH_ERRNO(rc, rc, -1); -} -} diff --git a/Userland/Libraries/LibC/sys/uio.h b/Userland/Libraries/LibC/sys/uio.h deleted file mode 100644 index ad5090dfa2a..00000000000 --- a/Userland/Libraries/LibC/sys/uio.h +++ /dev/null @@ -1,18 +0,0 @@ -/* - * Copyright (c) 2018-2021, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include - -__BEGIN_DECLS - -ssize_t writev(int fd, const struct iovec*, int iov_count); -ssize_t readv(int fd, const struct iovec*, int iov_count); -ssize_t pwritev(int fd, const struct iovec*, int iov_count, off_t); - -__END_DECLS diff --git a/Userland/Libraries/LibC/sys/un.h b/Userland/Libraries/LibC/sys/un.h deleted file mode 100644 index ac7e8d24b81..00000000000 --- a/Userland/Libraries/LibC/sys/un.h +++ /dev/null @@ -1,9 +0,0 @@ -/* - * Copyright (c) 2018-2021, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include diff --git a/Userland/Libraries/LibC/sys/utsname.h b/Userland/Libraries/LibC/sys/utsname.h deleted file mode 100644 index 9ec62e925c9..00000000000 --- a/Userland/Libraries/LibC/sys/utsname.h +++ /dev/null @@ -1,16 +0,0 @@ -/* - * Copyright (c) 2018-2021, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include - -__BEGIN_DECLS - -int uname(struct utsname*); - -__END_DECLS diff --git a/Userland/Libraries/LibC/sys/wait.cpp b/Userland/Libraries/LibC/sys/wait.cpp deleted file mode 100644 index dab010de60f..00000000000 --- a/Userland/Libraries/LibC/sys/wait.cpp +++ /dev/null @@ -1,90 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include -#include - -extern "C" { - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/wait.html -pid_t wait(int* wstatus) -{ - return waitpid(-1, wstatus, 0); -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/waitpid.html -pid_t waitpid(pid_t waitee, int* wstatus, int options) -{ - __pthread_maybe_cancel(); - - siginfo_t siginfo; - idtype_t idtype; - id_t id; - - if (waitee < -1) { - idtype = P_PGID; - id = -waitee; - } else if (waitee == -1) { - idtype = P_ALL; - id = 0; - } else if (waitee == 0) { - idtype = P_PGID; - id = getgid(); - } else { - idtype = P_PID; - id = waitee; - } - - // To be able to detect if a child was found when WNOHANG is set, - // we need to clear si_pid, which will only be set if it was found. - siginfo.si_pid = 0; - int rc = waitid(idtype, id, &siginfo, options | WEXITED); - - if (rc < 0) - return rc; - - if ((options & WNOHANG) && siginfo.si_pid == 0) { - // No child in a waitable state was found. All other fields - // in siginfo are undefined - return 0; - } - - if (wstatus) { - switch (siginfo.si_code) { - case CLD_EXITED: - *wstatus = siginfo.si_status << 8; - break; - case CLD_KILLED: - *wstatus = siginfo.si_status; - break; - case CLD_STOPPED: - *wstatus = siginfo.si_status << 8 | 0x7f; - break; - case CLD_CONTINUED: - *wstatus = 0xffff; - break; - default: - VERIFY_NOT_REACHED(); - } - } - - return siginfo.si_pid; -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/waitid.html -int waitid(idtype_t idtype, id_t id, siginfo_t* infop, int options) -{ - __pthread_maybe_cancel(); - - Syscall::SC_waitid_params params { idtype, id, infop, options }; - int rc = syscall(SC_waitid, ¶ms); - __RETURN_WITH_ERRNO(rc, rc, -1); -} -} diff --git a/Userland/Libraries/LibC/sys/wait.h b/Userland/Libraries/LibC/sys/wait.h deleted file mode 100644 index 5d1fd7af36c..00000000000 --- a/Userland/Libraries/LibC/sys/wait.h +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -// Includes essentially mandated by POSIX: -// https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/sys_wait.h.html -#include - -#include -#include -#include - -__BEGIN_DECLS - -pid_t waitpid(pid_t, int* wstatus, int options); -pid_t wait(int* wstatus); -int waitid(idtype_t idtype, id_t id, siginfo_t* infop, int options); - -__END_DECLS diff --git a/Userland/Libraries/LibC/sysexits.h b/Userland/Libraries/LibC/sysexits.h deleted file mode 100644 index 0a55dd8565e..00000000000 --- a/Userland/Libraries/LibC/sysexits.h +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright (c) 2021, Panagiotis Vasilopoulos - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -// This file was modeled after the FreeBSD man pages for -// software compatibility purposes. - -#pragma once - -#define EX_OK 0 -#define EX__BASE 64 -#define EX_USAGE 64 -#define EX_DATAERR 65 -#define EX_NOINPUT 66 -#define EX_NOUSER 67 -#define EX_NOHOST 68 -#define EX_UNAVAILABLE 69 -#define EX_SOFTWARE 70 -#define EX_OSERR 71 -#define EX_OSFILE 72 -#define EX_CANTCREAT 73 -#define EX_IOERR 74 -#define EX_TEMPFAIL 75 -#define EX_PROTOCOL 76 -#define EX_NOPERM 77 -#define EX_CONFIG 78 -#define EX__MAX 78 diff --git a/Userland/Libraries/LibC/syslog.cpp b/Userland/Libraries/LibC/syslog.cpp deleted file mode 100644 index 9131f0e60de..00000000000 --- a/Userland/Libraries/LibC/syslog.cpp +++ /dev/null @@ -1,139 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -// Has to be defined before including due to legacy Unices -#define SYSLOG_NAMES 1 - -#include -#include -#include -#include -#include -#include - -// This implementation doesn't talk to a syslog server. Any options related to -// that are no-ops. - -extern "C" { - -// For implementation simplicity, we actually only use the re-entrant version -// of each function, and the version that isn't just redirects with a static -// struct to share. -static struct syslog_data global_log_data = { - .ident = nullptr, - .logopt = 0, - .facility = LOG_USER, - .maskpri = LOG_UPTO(LOG_DEBUG) -}; - -// Used when ident is null, since syslog traditionally prints the program's -// own name; the process name will always be the same unless we exec. -static char program_name_buffer[256]; -static bool program_name_set = false; - -// Convenience function for initialization and checking what string to use -// for the program name. -static char const* get_syslog_ident(struct syslog_data* data) -{ - if (!program_name_set && data->ident == nullptr) - program_name_set = get_process_name(program_name_buffer, sizeof(program_name_buffer)) >= 0; - - if (data->ident != nullptr) - return data->ident; - else if (program_name_set) - return program_name_buffer; - - VERIFY_NOT_REACHED(); -} - -void openlog_r(char const* ident, int logopt, int facility, struct syslog_data* data) -{ - data->ident = ident; - data->logopt = logopt; - data->facility = facility; - // default value - data->maskpri = LOG_UPTO(LOG_DEBUG); - // would be where we connect to a daemon -} - -void openlog(char const* ident, int logopt, int facility) -{ - openlog_r(ident, logopt, facility, &global_log_data); -} - -void closelog_r(struct syslog_data* data) -{ - // would be where we disconnect from a daemon - // restore defaults - data->ident = nullptr; - data->logopt = 0; - data->facility = LOG_USER; - data->maskpri = LOG_UPTO(LOG_DEBUG); -} - -void closelog(void) -{ - closelog_r(&global_log_data); -} - -int setlogmask_r(int maskpri, struct syslog_data* data) -{ - // Remember, this takes the input of LOG_MASK/LOG_UPTO - int old_maskpri = data->maskpri; - data->maskpri = maskpri; - return old_maskpri; -} - -int setlogmask(int maskpri) -{ - return setlogmask_r(maskpri, &global_log_data); -} - -void syslog_r(int priority, struct syslog_data* data, char const* message, ...) -{ - va_list ap; - va_start(ap, message); - vsyslog_r(priority, data, message, ap); - va_end(ap); -} - -void syslog(int priority, char const* message, ...) -{ - va_list ap; - va_start(ap, message); - vsyslog_r(priority, &global_log_data, message, ap); - va_end(ap); -} - -void vsyslog_r(int priority, struct syslog_data* data, char const* message, va_list args) -{ - StringBuilder combined; - - int real_priority = LOG_PRI(priority); - // Lots of parens, but it just extracts the priority from combo and masks. - if (!(data->maskpri & LOG_MASK(real_priority))) - return; - - // Some metadata would be consumed by a syslog daemon, if we had one. - if (data->logopt & LOG_PID) - combined.appendff("{}[{}]: ", get_syslog_ident(data), getpid()); - else - combined.appendff("{}: ", get_syslog_ident(data)); - - combined.appendvf(message, args); - auto combined_string = combined.to_byte_string(); - - if (data->logopt & LOG_CONS) - dbgputstr(combined_string.characters(), combined_string.length()); - if (data->logopt & LOG_PERROR) - fputs(combined_string.characters(), stderr); -} - -void vsyslog(int priority, char const* message, va_list args) -{ - vsyslog_r(priority, &global_log_data, message, args); -} -} diff --git a/Userland/Libraries/LibC/syslog.h b/Userland/Libraries/LibC/syslog.h deleted file mode 100644 index 5e822b14875..00000000000 --- a/Userland/Libraries/LibC/syslog.h +++ /dev/null @@ -1,160 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include - -__BEGIN_DECLS - -struct syslog_data { - char const* ident; - int logopt; - int facility; - int maskpri; -}; - -/* The severity of the message. This is ordered. */ -#define LOG_EMERG 0 -#define LOG_ALERT 1 -#define LOG_CRIT 2 -#define LOG_ERR 3 -#define LOG_WARNING 4 -#define LOG_NOTICE 5 -#define LOG_INFO 6 -#define LOG_DEBUG 7 - -/* Macros for masking out the priority of a combined priority */ -#define LOG_PRIMASK (7) -#define LOG_PRI(priority) ((priority) & LOG_PRIMASK) - -/* - * Many of these facilities don't really make sense anymore, but we keep them - * for compatibility purposes. - */ -#define LOG_KERN (0 << 3) -#define LOG_USER (1 << 3) -#define LOG_MAIL (2 << 3) -#define LOG_DAEMON (3 << 3) -#define LOG_AUTH (4 << 3) -#define LOG_SYSLOG (5 << 3) -#define LOG_LPR (6 << 3) -#define LOG_NEWS (7 << 3) -#define LOG_UUCP (8 << 3) -#define LOG_CRON (9 << 3) -#define LOG_AUTHPRIV (10 << 3) -#define LOG_FTP (11 << 3) -/* glibc and OpenBSD reserve 12..15 for future system usage, we will too */ -#define LOG_LOCAL0 (16 << 3) -#define LOG_LOCAL1 (17 << 3) -#define LOG_LOCAL2 (18 << 3) -#define LOG_LOCAL3 (19 << 3) -#define LOG_LOCAL4 (20 << 3) -#define LOG_LOCAL5 (21 << 3) -#define LOG_LOCAL6 (22 << 3) -#define LOG_LOCAL7 (23 << 3) - -#define LOG_NFACILITIES 24 - -/* Macros to get the facility from a combined priority. */ -#define LOG_FACMASK (~7) -#define LOG_FAC(priority) (((priority) & LOG_FACMASK) >> 3) - -/* For masking logs, we use these macros with just the priority. */ -#define LOG_MASK(priority) (1 << (priority)) -#define LOG_UPTO(priority) (LOG_MASK(priority) + (LOG_MASK(priority) - 1)) - -/* Macro to make a combined priority. */ -#define LOG_MAKEPRI(facility, priority) ((facility) | (priority)) - -/* Include a PID with the message. */ -#define LOG_PID (1 << 0) -/* Log on the console. */ -#define LOG_CONS (1 << 1) -/* Open the syslogd connection at the first call. (not implemented, default) */ -#define LOG_ODELAY (1 << 2) -/* Open the syslogd connection immediately. (not implemented) */ -#define LOG_NDELAY (1 << 3) -/* Log to stderr as well. */ -#define LOG_PERROR (1 << 4) -/* Don't wait for child processes created while logging the message. */ -#define LOG_NOWAIT (1 << 5) - -/* This is useful to have, but has to be stored weirdly for compatibility. */ -#ifdef SYSLOG_NAMES -/* Used for marking the fallback; some applications check for these defines. */ -# define INTERNAL_NOPRI 0x10 -# define INTERNAL_MARK LOG_MAKEPRI(LOG_NFACILITIES << 3, 0) - -typedef struct _code { - /* - * Most Unices define this as char*, but in C++, we have to define it as a - * const char* if we want to use string constants. - */ - char const* c_name; - int c_val; -} CODE; - -/* - * The names we use are the same as what glibc and OpenBSD use. We omit - * deprecated values in the hope that no one uses them. Sorted, as well. - */ - -CODE prioritynames[] = { - { "alert", LOG_ALERT }, - { "crit", LOG_CRIT }, - { "debug", LOG_DEBUG }, - { "emerg", LOG_EMERG }, - { "err", LOG_ERR }, - { "info", LOG_INFO }, - /* Fallback */ - { "none", INTERNAL_NOPRI }, - { "notice", LOG_NOTICE }, - { "warning", LOG_WARNING }, - { NULL, -1 }, -}; - -CODE facilitynames[] = { - { "auth", LOG_AUTH }, - { "authpriv", LOG_AUTHPRIV }, - { "cron", LOG_CRON }, - { "daemon", LOG_DAEMON }, - { "ftp", LOG_FTP }, - { "kern", LOG_KERN }, - { "local0", LOG_LOCAL0 }, - { "local1", LOG_LOCAL1 }, - { "local2", LOG_LOCAL2 }, - { "local3", LOG_LOCAL3 }, - { "local4", LOG_LOCAL4 }, - { "local5", LOG_LOCAL5 }, - { "local6", LOG_LOCAL6 }, - { "local7", LOG_LOCAL7 }, - { "lpr", LOG_LPR }, - { "mail", LOG_MAIL }, - /* Fallback */ - { "mark", INTERNAL_MARK }, - { "news", LOG_NEWS }, - { "syslog", LOG_SYSLOG }, - { "user", LOG_USER }, - { "uucp", LOG_UUCP }, - { NULL, -1 }, -}; -#endif - -/* The re-entrant versions are an OpenBSD extension we also implement. */ -void syslog(int, char const*, ...); -void syslog_r(int, struct syslog_data*, char const*, ...); -void vsyslog(int, char const* message, va_list); -void vsyslog_r(int, struct syslog_data* data, char const* message, va_list); -void openlog(char const*, int, int); -void openlog_r(char const*, int, int, struct syslog_data*); -void closelog(void); -void closelog_r(struct syslog_data*); -int setlogmask(int); -int setlogmask_r(int, struct syslog_data*); - -__END_DECLS diff --git a/Userland/Libraries/LibC/termcap.cpp b/Userland/Libraries/LibC/termcap.cpp deleted file mode 100644 index 86ab25d537b..00000000000 --- a/Userland/Libraries/LibC/termcap.cpp +++ /dev/null @@ -1,138 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include -#include -#include - -extern "C" { - -char PC; -char* UP; -char* BC; - -int __attribute__((weak)) tgetent([[maybe_unused]] char* bp, [[maybe_unused]] char const* name) -{ - warnln_if(TERMCAP_DEBUG, "tgetent: bp={:p}, name='{}'", bp, name); - PC = '\0'; - BC = const_cast("\033[D"); - UP = const_cast("\033[A"); - return 1; -} - -static HashMap* caps = nullptr; - -static void ensure_caps() -{ - if (caps) - return; - caps = new HashMap; - caps->set("DC"_string, "\033[%p1%dP"); - caps->set("IC"_string, "\033[%p1%d@"); - caps->set("ce"_string, "\033[K"); - caps->set("cl"_string, "\033[H\033[J"); - caps->set("cr"_string, "\015"); - caps->set("dc"_string, "\033[P"); - caps->set("ei"_string, ""); - caps->set("ic"_string, ""); - caps->set("im"_string, ""); - caps->set("kd"_string, "\033[B"); - caps->set("kl"_string, "\033[D"); - caps->set("kr"_string, "\033[C"); - caps->set("ku"_string, "\033[A"); - caps->set("ks"_string, ""); - caps->set("ke"_string, ""); - caps->set("le"_string, "\033[D"); - caps->set("mm"_string, ""); - caps->set("mo"_string, ""); - caps->set("pc"_string, ""); - caps->set("up"_string, "\033[A"); - caps->set("vb"_string, ""); - caps->set("am"_string, ""); - caps->set("@7"_string, ""); - caps->set("kH"_string, ""); - caps->set("kI"_string, "\033[L"); - caps->set("kh"_string, "\033[H"); - caps->set("vs"_string, ""); - caps->set("ve"_string, ""); - caps->set("E3"_string, ""); - caps->set("kD"_string, ""); - caps->set("nd"_string, "\033[C"); - - caps->set("co"_string, "80"); - caps->set("li"_string, "25"); -} - -// Unfortunately, tgetstr() doesn't accept a size argument for the buffer -// pointed to by area, so we have to use bare strcpy(). -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wdeprecated-declarations" - -char* __attribute__((weak)) tgetstr(char const* id, char** area) -{ - ensure_caps(); - warnln_if(TERMCAP_DEBUG, "tgetstr: id='{}'", id); - auto it = caps->find(StringView { id, strlen(id) }); - if (it != caps->end()) { - char* ret = *area; - char const* val = (*it).value; - strcpy(*area, val); - *area += strlen(val) + 1; - return ret; - } - warnln_if(TERMCAP_DEBUG, "tgetstr: missing cap id='{}'", id); - return nullptr; -} - -#pragma GCC diagnostic pop - -int __attribute__((weak)) tgetflag([[maybe_unused]] char const* id) -{ - warnln_if(TERMCAP_DEBUG, "tgetflag: '{}'", id); - auto it = caps->find(StringView { id, strlen(id) }); - if (it != caps->end()) - return 1; - return 0; -} - -int __attribute__((weak)) tgetnum(char const* id) -{ - warnln_if(TERMCAP_DEBUG, "tgetnum: '{}'", id); - auto it = caps->find(StringView { id, strlen(id) }); - if (it != caps->end()) - return atoi((*it).value); - return -1; -} - -static Vector s_tgoto_buffer; -char* __attribute__((weak)) tgoto([[maybe_unused]] char const* cap, [[maybe_unused]] int col, [[maybe_unused]] int row) -{ - auto row_string = String::number(row); - auto column_string = String::number(col); - if (row_string.is_error() || column_string.is_error()) { - // According to Linux man pages (https://man7.org/linux/man-pages/man3/tgoto.3x.html) we apparently don't have to set errno. - return nullptr; - } - auto cap_str = StringView { cap, strlen(cap) }.replace("%p1%d"sv, column_string.release_value(), ReplaceMode::FirstOnly).replace("%p2%d"sv, row_string.release_value(), ReplaceMode::FirstOnly); - - s_tgoto_buffer.clear_with_capacity(); - s_tgoto_buffer.ensure_capacity(cap_str.length()); - (void)cap_str.copy_characters_to_buffer(s_tgoto_buffer.data(), cap_str.length()); - return s_tgoto_buffer.data(); -} - -int __attribute__((weak)) tputs(char const* str, [[maybe_unused]] int affcnt, int (*putc)(int)) -{ - size_t len = strlen(str); - for (size_t i = 0; i < len; ++i) - putc(str[i]); - return 0; -} -} diff --git a/Userland/Libraries/LibC/termcap.h b/Userland/Libraries/LibC/termcap.h deleted file mode 100644 index 451151aa759..00000000000 --- a/Userland/Libraries/LibC/termcap.h +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include - -__BEGIN_DECLS - -extern char PC; -extern char* UP; -extern char* BC; - -int tgetent(char* bp, char const* name); -int tgetflag(char const* id); -int tgetnum(char const* id); -char* tgetstr(char const* id, char** area); -char* tgoto(char const* cap, int col, int row); -int tputs(char const* str, int affcnt, int (*putc)(int)); - -__END_DECLS diff --git a/Userland/Libraries/LibC/termios.cpp b/Userland/Libraries/LibC/termios.cpp deleted file mode 100644 index 328424ef9df..00000000000 --- a/Userland/Libraries/LibC/termios.cpp +++ /dev/null @@ -1,157 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include - -extern "C" { - -int tcgetattr(int fd, struct termios* t) -{ - return ioctl(fd, TCGETS, t); -} - -int tcsetattr(int fd, int optional_actions, const struct termios* t) -{ - switch (optional_actions) { - case TCSANOW: - return ioctl(fd, TCSETS, t); - case TCSADRAIN: - return ioctl(fd, TCSETSW, t); - case TCSAFLUSH: - return ioctl(fd, TCSETSF, t); - } - errno = EINVAL; - return -1; -} - -// https://pubs.opengroup.org/onlinepubs/009695399/functions/tcsendbreak.html -int tcsendbreak([[maybe_unused]] int fd, [[maybe_unused]] int duration) -{ - // FIXME: Implement this for real. - return 0; -} - -int tcflow([[maybe_unused]] int fd, [[maybe_unused]] int action) -{ - errno = EINVAL; - return -1; -} - -int tcflush(int fd, int queue_selector) -{ - return ioctl(fd, TCFLSH, queue_selector); -} - -// https://pubs.opengroup.org/onlinepubs/009695399/functions/tcdrain.html -int tcdrain([[maybe_unused]] int fd) -{ - __pthread_maybe_cancel(); - - // FIXME: Implement this for real. - return 0; -} - -speed_t cfgetispeed(const struct termios* tp) -{ - return tp->c_ispeed; -} - -speed_t cfgetospeed(const struct termios* tp) -{ - return tp->c_ospeed; -} - -static int baud_rate_from_speed(speed_t speed) -{ - int rate = -EINVAL; - switch (speed) { - case B0: - rate = 0; - break; - case B50: - rate = 50; - break; - case B75: - rate = 75; - break; - case B110: - rate = 110; - break; - case B134: - rate = 134; - break; - case B150: - rate = 150; - break; - case B200: - rate = 200; - break; - case B300: - rate = 300; - break; - case B600: - rate = 600; - break; - case B1200: - rate = 1200; - break; - case B1800: - rate = 1800; - break; - case B2400: - rate = 2400; - break; - case B4800: - rate = 4800; - break; - case B9600: - rate = 9600; - break; - case B19200: - rate = 19200; - break; - case B38400: - rate = 38400; - break; - } - - return rate; -} - -int cfsetispeed(struct termios* tp, speed_t speed) -{ - auto ispeed = baud_rate_from_speed(speed); - if (ispeed > 0) { - tp->c_ispeed = ispeed; - } - __RETURN_WITH_ERRNO(ispeed, 0, -1); -} - -int cfsetospeed(struct termios* tp, speed_t speed) -{ - auto ospeed = baud_rate_from_speed(speed); - if (ospeed > 0) { - tp->c_ispeed = ospeed; - } - __RETURN_WITH_ERRNO(ospeed, 0, -1); -} - -void cfmakeraw(struct termios* tp) -{ - if (!tp) - return; - - auto& termios = *tp; - termios.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL | IXON); - termios.c_lflag &= ~OPOST; - termios.c_cflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN); - termios.c_cflag |= CS8; -} -} diff --git a/Userland/Libraries/LibC/termios.h b/Userland/Libraries/LibC/termios.h deleted file mode 100644 index a8c4bc4056b..00000000000 --- a/Userland/Libraries/LibC/termios.h +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include - -__BEGIN_DECLS - -int tcdrain(int fd); -int tcgetattr(int fd, struct termios*); -int tcsetattr(int fd, int optional_actions, const struct termios*); -int tcsendbreak(int fd, int duration); -int tcflow(int fd, int action); -int tcflush(int fd, int queue_selector); - -speed_t cfgetispeed(const struct termios*); -speed_t cfgetospeed(const struct termios*); -int cfsetispeed(struct termios*, speed_t); -int cfsetospeed(struct termios*, speed_t); -void cfmakeraw(struct termios*); - -__END_DECLS diff --git a/Userland/Libraries/LibC/time.cpp b/Userland/Libraries/LibC/time.cpp deleted file mode 100644 index 67798b877d6..00000000000 --- a/Userland/Libraries/LibC/time.cpp +++ /dev/null @@ -1,527 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -extern "C" { - -static constexpr char const* __utc = "UTC"; -static StringView __tzname { __utc, __builtin_strlen(__utc) }; -static char __tzname_standard[TZNAME_MAX]; -static char __tzname_daylight[TZNAME_MAX]; - -long timezone = 0; -long altzone = 0; -char* tzname[2] = { const_cast(__utc), const_cast(__utc) }; -int daylight = 0; - -time_t time(time_t* tloc) -{ - struct timeval tv; - struct timezone tz; - if (gettimeofday(&tv, &tz) < 0) - return (time_t)-1; - if (tloc) - *tloc = tv.tv_sec; - return tv.tv_sec; -} - -int adjtime(const struct timeval* delta, struct timeval* old_delta) -{ - int rc = syscall(SC_adjtime, delta, old_delta); - __RETURN_WITH_ERRNO(rc, rc, -1); -} - -int gettimeofday(struct timeval* __restrict__ tv, void* __restrict__) -{ - if (!tv) { - errno = EFAULT; - return -1; - } - - struct timespec ts = {}; - if (clock_gettime(CLOCK_REALTIME_COARSE, &ts) < 0) - return -1; - - TIMESPEC_TO_TIMEVAL(tv, &ts); - return 0; -} - -int settimeofday(struct timeval* __restrict__ tv, void* __restrict__) -{ - if (!tv) { - errno = EFAULT; - return -1; - } - - timespec ts; - TIMEVAL_TO_TIMESPEC(tv, &ts); - return clock_settime(CLOCK_REALTIME, &ts); -} - -int utimes(char const* pathname, struct timeval const times[2]) -{ - if (!times) - return utime(pathname, nullptr); - // FIXME: implement support for tv_usec in the utime (or a new) syscall - utimbuf buf = { times[0].tv_sec, times[1].tv_sec }; - return utime(pathname, &buf); -} - -// Not in POSIX, originated in BSD but also supported on Linux. -// https://man.netbsd.org/NetBSD-6.0/lutimes.2 -int lutimes(char const* pathname, struct timeval const times[2]) -{ - if (!times) - return utimensat(AT_FDCWD, pathname, nullptr, AT_SYMLINK_NOFOLLOW); - timespec ts[2]; - TIMEVAL_TO_TIMESPEC(×[0], &ts[0]); - TIMEVAL_TO_TIMESPEC(×[1], &ts[1]); - return utimensat(AT_FDCWD, pathname, ts, AT_SYMLINK_NOFOLLOW); -} - -// Not in POSIX, originated in BSD but also supported on Linux. -// https://man.netbsd.org/NetBSD-6.0/futimes.2 -int futimes(int fd, struct timeval const times[2]) -{ - if (!times) - return utimensat(fd, nullptr, nullptr, 0); - timespec ts[2]; - TIMEVAL_TO_TIMESPEC(×[0], &ts[0]); - TIMEVAL_TO_TIMESPEC(×[1], &ts[1]); - return utimensat(fd, nullptr, ts, 0); -} - -char* ctime(time_t const* t) -{ - return asctime(localtime(t)); -} - -char* ctime_r(time_t const* t, char* buf) -{ - struct tm tm_buf; - return asctime_r(localtime_r(t, &tm_buf), buf); -} - -static int const __seconds_per_day = 60 * 60 * 24; - -static bool is_valid_time(time_t timestamp) -{ - // Note: these correspond to the number of seconds from epoch to the dates "Jan 1 00:00:00 -2147483648" and "Dec 31 23:59:59 2147483647", - // respectively, which are the smallest and biggest representable dates without overflowing tm->tm_year, if it is an int. - constexpr time_t smallest_possible_time = -67768040609740800; - constexpr time_t biggest_possible_time = 67768036191676799; - - return (timestamp >= smallest_possible_time) && (timestamp <= biggest_possible_time); -} - -static struct tm* time_to_tm(struct tm* tm, time_t t, StringView time_zone) -{ - if (!is_valid_time(t)) { - errno = EOVERFLOW; - return nullptr; - } - - if (auto offset = TimeZone::get_time_zone_offset(time_zone, AK::UnixDateTime::from_seconds_since_epoch(t)); offset.has_value()) { - tm->tm_isdst = offset->in_dst == TimeZone::InDST::Yes; - t += offset->seconds; - } - - int year = 1970; - for (; t >= days_in_year(year) * __seconds_per_day; ++year) - t -= days_in_year(year) * __seconds_per_day; - for (; t < 0; --year) - t += days_in_year(year - 1) * __seconds_per_day; - tm->tm_year = year - 1900; - - VERIFY(t >= 0); - int days = t / __seconds_per_day; - tm->tm_yday = days; - int remaining = t % __seconds_per_day; - tm->tm_sec = remaining % 60; - remaining /= 60; - tm->tm_min = remaining % 60; - tm->tm_hour = remaining / 60; - - int month; - for (month = 1; month < 12 && days >= days_in_month(year, month); ++month) - days -= days_in_month(year, month); - - tm->tm_mday = days + 1; - tm->tm_wday = day_of_week(year, month, tm->tm_mday); - tm->tm_mon = month - 1; - - return tm; -} - -static time_t tm_to_time(struct tm* tm, StringView time_zone) -{ - // "The original values of the tm_wday and tm_yday components of the structure are ignored, - // and the original values of the other components are not restricted to the ranges described in . - // [...] - // Upon successful completion, the values of the tm_wday and tm_yday components of the structure shall be set appropriately, - // and the other components are set to represent the specified time since the Epoch, - // but with their values forced to the ranges indicated in the entry; - // the final value of tm_mday shall not be set until tm_mon and tm_year are determined." - - tm->tm_year += tm->tm_mon / 12; - tm->tm_mon %= 12; - if (tm->tm_mon < 0) { - tm->tm_year--; - tm->tm_mon += 12; - } - - tm->tm_yday = day_of_year(1900 + tm->tm_year, tm->tm_mon + 1, tm->tm_mday); - time_t days_since_epoch = years_to_days_since_epoch(1900 + tm->tm_year) + tm->tm_yday; - auto timestamp = ((days_since_epoch * 24 + tm->tm_hour) * 60 + tm->tm_min) * 60 + tm->tm_sec; - - if (tm->tm_isdst < 0) { - if (auto offset = TimeZone::get_time_zone_offset(time_zone, AK::UnixDateTime::from_seconds_since_epoch(timestamp)); offset.has_value()) - timestamp -= offset->seconds; - } else { - auto index = tm->tm_isdst == 0 ? 0 : 1; - - if (auto offsets = TimeZone::get_named_time_zone_offsets(time_zone, AK::UnixDateTime::from_seconds_since_epoch(timestamp)); offsets.has_value()) - timestamp -= offsets->at(index).seconds; - } - - if (!is_valid_time(timestamp)) { - errno = EOVERFLOW; - return -1; - } - - return timestamp; -} - -time_t mktime(struct tm* tm) -{ - tzset(); - return tm_to_time(tm, __tzname); -} - -struct tm* localtime(time_t const* t) -{ - tzset(); - - static struct tm tm_buf; - return localtime_r(t, &tm_buf); -} - -struct tm* localtime_r(time_t const* t, struct tm* tm) -{ - if (!t) - return nullptr; - - return time_to_tm(tm, *t, __tzname); -} - -time_t timegm(struct tm* tm) -{ - tm->tm_isdst = 0; - return tm_to_time(tm, { __utc, __builtin_strlen(__utc) }); -} - -struct tm* gmtime(time_t const* t) -{ - static struct tm tm_buf; - return gmtime_r(t, &tm_buf); -} - -struct tm* gmtime_r(time_t const* t, struct tm* tm) -{ - if (!t) - return nullptr; - return time_to_tm(tm, *t, { __utc, __builtin_strlen(__utc) }); -} - -char* asctime(const struct tm* tm) -{ - static char buffer[69]; - return asctime_r(tm, buffer); -} - -char* asctime_r(const struct tm* tm, char* buffer) -{ - // Spec states buffer must be at least 26 bytes. - constexpr size_t assumed_len = 26; - size_t filled_size = strftime(buffer, assumed_len, "%a %b %e %T %Y\n", tm); - - // If the buffer was not large enough, set EOVERFLOW and return null. - if (filled_size == 0) { - errno = EOVERFLOW; - return nullptr; - } - - return buffer; -} - -// FIXME: Some formats are not supported. -size_t strftime(char* destination, size_t max_size, char const* format, const struct tm* tm) -{ - tzset(); - - StringBuilder builder { max_size }; - - int const format_len = strlen(format); - for (int i = 0; i < format_len; ++i) { - if (format[i] != '%') { - builder.append(format[i]); - } else { - if (++i >= format_len) - return 0; - - switch (format[i]) { - case 'a': - builder.append(short_day_names[tm->tm_wday]); - break; - case 'A': - builder.append(long_day_names[tm->tm_wday]); - break; - case 'b': - builder.append(short_month_names[tm->tm_mon]); - break; - case 'B': - builder.append(long_month_names[tm->tm_mon]); - break; - case 'C': - builder.appendff("{:02}", (tm->tm_year + 1900) / 100); - break; - case 'd': - builder.appendff("{:02}", tm->tm_mday); - break; - case 'D': - builder.appendff("{:02}/{:02}/{:02}", tm->tm_mon + 1, tm->tm_mday, (tm->tm_year + 1900) % 100); - break; - case 'e': - builder.appendff("{:2}", tm->tm_mday); - break; - case 'h': - builder.append(short_month_names[tm->tm_mon]); - break; - case 'H': - builder.appendff("{:02}", tm->tm_hour); - break; - case 'I': { - int display_hour = tm->tm_hour % 12; - if (display_hour == 0) - display_hour = 12; - builder.appendff("{:02}", display_hour); - break; - } - case 'j': - builder.appendff("{:03}", tm->tm_yday + 1); - break; - case 'm': - builder.appendff("{:02}", tm->tm_mon + 1); - break; - case 'M': - builder.appendff("{:02}", tm->tm_min); - break; - case 'n': - builder.append('\n'); - break; - case 'p': - builder.append(tm->tm_hour < 12 ? "AM"sv : "PM"sv); - break; - case 'r': { - int display_hour = tm->tm_hour % 12; - if (display_hour == 0) - display_hour = 12; - builder.appendff("{:02}:{:02}:{:02} {}", display_hour, tm->tm_min, tm->tm_sec, tm->tm_hour < 12 ? "AM" : "PM"); - break; - } - case 'R': - builder.appendff("{:02}:{:02}", tm->tm_hour, tm->tm_min); - break; - case 'S': - builder.appendff("{:02}", tm->tm_sec); - break; - case 't': - builder.append('\t'); - break; - case 'T': - builder.appendff("{:02}:{:02}:{:02}", tm->tm_hour, tm->tm_min, tm->tm_sec); - break; - case 'u': - builder.appendff("{}", tm->tm_wday ? tm->tm_wday : 7); - break; - case 'U': { - int const wday_of_year_beginning = (tm->tm_wday + 6 * tm->tm_yday) % 7; - int const week_number = (tm->tm_yday + wday_of_year_beginning) / 7; - builder.appendff("{:02}", week_number); - break; - } - case 'V': { - int const wday_of_year_beginning = (tm->tm_wday + 6 + 6 * tm->tm_yday) % 7; - int week_number = (tm->tm_yday + wday_of_year_beginning) / 7 + 1; - if (wday_of_year_beginning > 3) { - if (tm->tm_yday >= 7 - wday_of_year_beginning) - --week_number; - else { - int const days_of_last_year = days_in_year(tm->tm_year + 1900 - 1); - int const wday_of_last_year_beginning = (wday_of_year_beginning + 6 * days_of_last_year) % 7; - week_number = (days_of_last_year + wday_of_last_year_beginning) / 7 + 1; - if (wday_of_last_year_beginning > 3) - --week_number; - } - } - builder.appendff("{:02}", week_number); - break; - } - case 'w': - builder.appendff("{}", tm->tm_wday); - break; - case 'W': { - int const wday_of_year_beginning = (tm->tm_wday + 6 + 6 * tm->tm_yday) % 7; - int const week_number = (tm->tm_yday + wday_of_year_beginning) / 7; - builder.appendff("{:02}", week_number); - break; - } - case 'y': - builder.appendff("{:02}", (tm->tm_year + 1900) % 100); - break; - case 'Y': - builder.appendff("{}", tm->tm_year + 1900); - break; - case '%': - builder.append('%'); - break; - default: - return 0; - } - } - if (builder.length() + 1 > max_size) - return 0; - } - - auto str = builder.to_byte_string(); - bool fits = str.copy_characters_to_buffer(destination, max_size); - return fits ? str.length() : 0; -} - -void tzset() -{ - __tzname = TimeZone::current_time_zone(); - - auto set_default_values = []() { - timezone = 0; - altzone = 0; - daylight = 0; - __tzname = StringView { __utc, __builtin_strlen(__utc) }; - tzname[0] = const_cast(__utc); - tzname[1] = const_cast(__utc); - }; - - if (auto offsets = TimeZone::get_named_time_zone_offsets(__tzname, AK::UnixDateTime::now()); offsets.has_value()) { - if (!offsets->at(0).name.copy_characters_to_buffer(__tzname_standard, TZNAME_MAX)) - return set_default_values(); - if (!offsets->at(1).name.copy_characters_to_buffer(__tzname_daylight, TZNAME_MAX)) - return set_default_values(); - - // timezone and altzone are seconds west of UTC, i.e. the offsets are negated. - timezone = -offsets->at(0).seconds; - altzone = -offsets->at(1).seconds; - daylight = timezone != altzone; - tzname[0] = __tzname_standard; - tzname[1] = __tzname_daylight; - } else { - set_default_values(); - } -} - -clock_t clock() -{ - struct tms tms; - times(&tms); - return tms.tms_utime + tms.tms_stime; -} - -static Kernel::TimePage* get_kernel_time_page() -{ - static Kernel::TimePage* s_kernel_time_page; - // FIXME: Thread safety - if (!s_kernel_time_page) { - auto rc = syscall(SC_map_time_page); - if ((int)rc < 0 && (int)rc > -EMAXERRNO) { - errno = -(int)rc; - return nullptr; - } - s_kernel_time_page = (Kernel::TimePage*)rc; - } - return s_kernel_time_page; -} - -int clock_gettime(clockid_t clock_id, struct timespec* ts) -{ - if (Kernel::time_page_supports(clock_id)) { - if (!ts) { - errno = EFAULT; - return -1; - } - - if (auto* kernel_time_page = get_kernel_time_page()) { - u32 update_iteration; - do { - update_iteration = AK::atomic_load(&kernel_time_page->update1, AK::memory_order_acquire); - *ts = kernel_time_page->clocks[clock_id]; - } while (update_iteration != AK::atomic_load(&kernel_time_page->update2, AK::memory_order_acquire)); - return 0; - } - } - - int rc = syscall(SC_clock_gettime, clock_id, ts); - __RETURN_WITH_ERRNO(rc, rc, -1); -} - -int clock_settime(clockid_t clock_id, struct timespec* ts) -{ - int rc = syscall(SC_clock_settime, clock_id, ts); - __RETURN_WITH_ERRNO(rc, rc, -1); -} - -int clock_nanosleep(clockid_t clock_id, int flags, const struct timespec* requested_sleep, struct timespec* remaining_sleep) -{ - __pthread_maybe_cancel(); - - Syscall::SC_clock_nanosleep_params params { clock_id, flags, requested_sleep, remaining_sleep }; - int rc = syscall(SC_clock_nanosleep, ¶ms); - __RETURN_WITH_ERRNO(rc, rc, -1); -} - -int nanosleep(const struct timespec* requested_sleep, struct timespec* remaining_sleep) -{ - return clock_nanosleep(CLOCK_REALTIME, 0, requested_sleep, remaining_sleep); -} - -int clock_getres(clockid_t clock_id, struct timespec* result) -{ - Syscall::SC_clock_getres_params params { clock_id, result }; - int rc = syscall(SC_clock_getres, ¶ms); - __RETURN_WITH_ERRNO(rc, rc, -1); -} - -double difftime(time_t t1, time_t t0) -{ - return (double)(t1 - t0); -} -} diff --git a/Userland/Libraries/LibC/time.h b/Userland/Libraries/LibC/time.h deleted file mode 100644 index 66a09d32bd6..00000000000 --- a/Userland/Libraries/LibC/time.h +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -// Includes essentially mandated by POSIX: -// https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/time.h.html -#include - -#include -#include - -__BEGIN_DECLS - -struct tm { - int tm_sec; /* Seconds (0-60) */ - int tm_min; /* Minutes (0-59) */ - int tm_hour; /* Hours (0-23) */ - int tm_mday; /* Day of the month (1-31) */ - int tm_mon; /* Month (0-11) */ - int tm_year; /* Year - 1900 */ - int tm_wday; /* Day of the week (0-6, Sunday = 0) */ - int tm_yday; /* Day in the year (0-365, 1 Jan = 0) */ - int tm_isdst; /* Daylight saving time */ -}; - -extern long timezone; /* The difference in seconds between UTC and local time */ -extern long altzone; -extern char* tzname[2]; -extern int daylight; - -struct tm* localtime(time_t const*); -struct tm* gmtime(time_t const*); -time_t mktime(struct tm*); -time_t timegm(struct tm*); -time_t time(time_t*); -char* ctime(time_t const*); -char* ctime_r(time_t const* tm, char* buf); -void tzset(void); -char* asctime(const struct tm*); -char* asctime_r(const struct tm*, char* buf); - -clock_t clock(void); - -int clock_gettime(clockid_t, struct timespec*); -int clock_settime(clockid_t, struct timespec*); -int clock_nanosleep(clockid_t, int flags, const struct timespec* requested_sleep, struct timespec* remaining_sleep); -int clock_getres(clockid_t, struct timespec* result); -int nanosleep(const struct timespec* requested_sleep, struct timespec* remaining_sleep); -struct tm* gmtime_r(time_t const* timep, struct tm* result); -struct tm* localtime_r(time_t const* timep, struct tm* result); - -double difftime(time_t, time_t); -size_t strftime(char* s, size_t max, char const* format, const struct tm*) __attribute__((format(strftime, 3, 0))); - -__END_DECLS diff --git a/Userland/Libraries/LibC/times.cpp b/Userland/Libraries/LibC/times.cpp deleted file mode 100644 index 9211373f022..00000000000 --- a/Userland/Libraries/LibC/times.cpp +++ /dev/null @@ -1,15 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include - -clock_t times(struct tms* buf) -{ - int rc = syscall(SC_times, buf); - __RETURN_WITH_ERRNO(rc, rc, (clock_t)-1); -} diff --git a/Userland/Libraries/LibC/tls.cpp b/Userland/Libraries/LibC/tls.cpp deleted file mode 100644 index c773cb6021c..00000000000 --- a/Userland/Libraries/LibC/tls.cpp +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright (c) 2023, Daniel Bertalan - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include - -extern "C" { - -// This function is called to compute the address of a thread-local variable -// which might not be stored in the static TLS block (local-dynamic and -// global-dynamic models). Compilers default to this when creating shared -// libraries, as they may be loaded after program startup by `dlopen()`. -// -// We currently only support a static TLS block, so we take a shortcut in the -// implementation of this interface: instead of storing the module ID in -// ti_module, we store the module's TLS block offset. This avoids the need to -// have a per-thread module ID -> TLS block address. This will have to be -// changed if we support dynamically allocated TLS blocks. -void* __tls_get_addr(__tls_index* index) -{ - return reinterpret_cast(reinterpret_cast(__builtin_thread_pointer()) + index->ti_module + index->ti_offset + ELF::TLS_DTV_OFFSET); -} -} diff --git a/Userland/Libraries/LibC/ucontext.h b/Userland/Libraries/LibC/ucontext.h deleted file mode 100644 index 4d18d3d7ce3..00000000000 --- a/Userland/Libraries/LibC/ucontext.h +++ /dev/null @@ -1,9 +0,0 @@ -/* - * Copyright (c) 2022, Andrew Kaster - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include diff --git a/Userland/Libraries/LibC/ulimit.cpp b/Userland/Libraries/LibC/ulimit.cpp deleted file mode 100644 index 6ca11d33704..00000000000 --- a/Userland/Libraries/LibC/ulimit.cpp +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * Copyright (c) 2022, Lucas Chollet - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include - -extern "C" { - -long ulimit([[maybe_unused]] int cmd, [[maybe_unused]] long newlimit) -{ - dbgln("FIXME: Implement ulimit()"); - TODO(); - return -1; -} - -// https://pubs.opengroup.org/onlinepubs/009696699/functions/getrusage.html -int getrusage(int who, struct rusage* usage) -{ - int rc = syscall(SC_getrusage, who, usage); - __RETURN_WITH_ERRNO(rc, rc, -1); -} - -int getrlimit([[maybe_unused]] int resource, rlimit* rl) -{ - rl->rlim_cur = RLIM_INFINITY; - rl->rlim_max = RLIM_INFINITY; - return 0; -} - -int setrlimit([[maybe_unused]] int resource, [[maybe_unused]] rlimit const* rl) -{ - return 0; -} -} diff --git a/Userland/Libraries/LibC/ulimit.h b/Userland/Libraries/LibC/ulimit.h deleted file mode 100644 index 1725404ac2e..00000000000 --- a/Userland/Libraries/LibC/ulimit.h +++ /dev/null @@ -1,15 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include - -__BEGIN_DECLS - -long ulimit(int cmd, long newlimit); - -__END_DECLS diff --git a/Userland/Libraries/LibC/unistd.cpp b/Userland/Libraries/LibC/unistd.cpp deleted file mode 100644 index 06b7a09eba8..00000000000 --- a/Userland/Libraries/LibC/unistd.cpp +++ /dev/null @@ -1,1159 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -extern "C" { - -#ifdef NO_TLS -static int s_cached_tid = 0; -#else -static __thread int s_cached_tid = 0; -#endif - -static int s_cached_pid = 0; - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/lchown.html -int lchown(char const* pathname, uid_t uid, gid_t gid) -{ - if (!pathname) { - errno = EFAULT; - return -1; - } - Syscall::SC_chown_params params { { pathname, strlen(pathname) }, uid, gid, AT_FDCWD, false }; - int rc = syscall(SC_chown, ¶ms); - __RETURN_WITH_ERRNO(rc, rc, -1); -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/chown.html -int chown(char const* pathname, uid_t uid, gid_t gid) -{ - if (!pathname) { - errno = EFAULT; - return -1; - } - Syscall::SC_chown_params params { { pathname, strlen(pathname) }, uid, gid, AT_FDCWD, true }; - int rc = syscall(SC_chown, ¶ms); - __RETURN_WITH_ERRNO(rc, rc, -1); -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/fchown.html -int fchown(int fd, uid_t uid, gid_t gid) -{ - int rc = syscall(SC_fchown, fd, uid, gid); - __RETURN_WITH_ERRNO(rc, rc, -1); -} - -int fchownat(int fd, char const* pathname, uid_t uid, gid_t gid, int flags) -{ - if (!pathname) { - errno = EFAULT; - return -1; - } - Syscall::SC_chown_params params { { pathname, strlen(pathname) }, uid, gid, fd, !(flags & AT_SYMLINK_NOFOLLOW) }; - int rc = syscall(SC_chown, ¶ms); - __RETURN_WITH_ERRNO(rc, rc, -1); -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/fork.html -pid_t fork() -{ - __pthread_fork_prepare(); - - int rc = syscall(SC_fork); - if (rc == 0) { - s_cached_tid = 0; - s_cached_pid = 0; - __pthread_fork_child(); - } else if (rc != -1) { - __pthread_fork_parent(); - } - __RETURN_WITH_ERRNO(rc, rc, -1); -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/vfork.html -pid_t vfork() -{ - return fork(); -} - -// Non-POSIX, but present in BSDs and Linux -// https://man.openbsd.org/daemon.3 -int daemon(int nochdir, int noclose) -{ - pid_t pid = fork(); - if (pid < 0) - return -1; - - // exit parent, continue execution in child - if (pid > 0) - _exit(0); - - pid = setsid(); - if (pid < 0) - return -1; - - if (nochdir == 0) - (void)chdir("/"); - - if (noclose == 0) { - // redirect stdout and stderr to /dev/null - int fd = open("/dev/null", O_WRONLY); - if (fd < 0) - return -1; - (void)close(STDOUT_FILENO); - (void)close(STDERR_FILENO); - (void)dup2(fd, STDOUT_FILENO); - (void)dup2(fd, STDERR_FILENO); - (void)close(fd); - } - return 0; -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/execv.html -int execv(char const* path, char* const argv[]) -{ - return execve(path, argv, environ); -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/execve.html -int execve(char const* filename, char* const argv[], char* const envp[]) -{ - size_t arg_count = 0; - for (size_t i = 0; argv[i]; ++i) - ++arg_count; - - size_t env_count = 0; - for (size_t i = 0; envp[i]; ++i) - ++env_count; - - auto copy_strings = [&](auto& vec, size_t count, auto& output) { - output.length = count; - for (size_t i = 0; vec[i]; ++i) { - output.strings[i].characters = vec[i]; - output.strings[i].length = strlen(vec[i]); - } - }; - - Syscall::SC_execve_params params; - params.arguments.strings = (Syscall::StringArgument*)alloca(arg_count * sizeof(Syscall::StringArgument)); - params.environment.strings = (Syscall::StringArgument*)alloca(env_count * sizeof(Syscall::StringArgument)); - - params.path = { filename, strlen(filename) }; - copy_strings(argv, arg_count, params.arguments); - copy_strings(envp, env_count, params.environment); - - int rc = syscall(SC_execve, ¶ms); - __RETURN_WITH_ERRNO(rc, rc, -1); -} - -// https://linux.die.net/man/3/execvpe (GNU extension) -int execvpe(char const* filename, char* const argv[], char* const envp[]) -{ - if (strchr(filename, '/')) - return execve(filename, argv, envp); - - ScopedValueRollback errno_rollback(errno); - - // TODO: Make this use the PATH search implementation from LibFileSystem. - ByteString path = getenv("PATH"); - if (path.is_empty()) - path = DEFAULT_PATH; - auto parts = path.split(':'); - for (auto& part : parts) { - auto candidate = ByteString::formatted("{}/{}", part, filename); - int rc = execve(candidate.characters(), argv, envp); - if (rc < 0 && errno != ENOENT) { - errno_rollback.set_override_rollback_value(errno); - dbgln("execvpe() failed on attempt ({}) with {}", candidate, strerror(errno)); - return rc; - } - } - errno_rollback.set_override_rollback_value(ENOENT); - dbgln("execvpe() leaving :("); - return -1; -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/execvp.html -int execvp(char const* filename, char* const argv[]) -{ - int rc = execvpe(filename, argv, environ); - int saved_errno = errno; - dbgln("execvp({}, ...) about to return {} with errno={}", filename, rc, saved_errno); - errno = saved_errno; - return rc; -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/execl.html -int execl(char const* filename, char const* arg0, ...) -{ - Vector args; - args.append(arg0); - - va_list ap; - va_start(ap, arg0); - for (;;) { - char const* arg = va_arg(ap, char const*); - if (!arg) - break; - args.append(arg); - } - va_end(ap); - args.append(nullptr); - return execve(filename, const_cast(args.data()), environ); -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/execle.html -int execle(char const* filename, char const* arg0, ...) -{ - Vector args; - args.append(arg0); - - va_list ap; - va_start(ap, arg0); - char const* arg; - do { - arg = va_arg(ap, char const*); - args.append(arg); - } while (arg); - - auto argv = const_cast(args.data()); - auto envp = const_cast(va_arg(ap, char* const*)); - va_end(ap); - - return execve(filename, argv, envp); -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/execlp.html -int execlp(char const* filename, char const* arg0, ...) -{ - Vector args; - args.append(arg0); - - va_list ap; - va_start(ap, arg0); - for (;;) { - char const* arg = va_arg(ap, char const*); - if (!arg) - break; - args.append(arg); - } - va_end(ap); - args.append(nullptr); - return execvpe(filename, const_cast(args.data()), environ); -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/getuid.html -uid_t geteuid() -{ - return syscall(SC_geteuid); -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/getegid.html -gid_t getegid() -{ - return syscall(SC_getegid); -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/getuid.html -uid_t getuid() -{ - return syscall(SC_getuid); -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/getgid.html -gid_t getgid() -{ - return syscall(SC_getgid); -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/getpid.html -pid_t getpid() -{ - int cached_pid = s_cached_pid; - if (!cached_pid) { - cached_pid = syscall(SC_getpid); - s_cached_pid = cached_pid; - } - return cached_pid; -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/getppid.html -pid_t getppid() -{ - return syscall(SC_getppid); -} - -int getresuid(uid_t* ruid, uid_t* euid, uid_t* suid) -{ - return syscall(SC_getresuid, ruid, euid, suid); -} - -int getresgid(gid_t* rgid, gid_t* egid, gid_t* sgid) -{ - return syscall(SC_getresgid, rgid, egid, sgid); -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/getsid.html -pid_t getsid(pid_t pid) -{ - int rc = syscall(SC_getsid, pid); - __RETURN_WITH_ERRNO(rc, rc, -1); -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/setsid.html -pid_t setsid() -{ - int rc = syscall(SC_setsid); - __RETURN_WITH_ERRNO(rc, rc, -1); -} - -pid_t tcgetpgrp(int fd) -{ - pid_t pgrp; - int rc = ioctl(fd, TIOCGPGRP, &pgrp); - if (rc < 0) - return rc; - return pgrp; -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/tcgetpgrp.html -int tcsetpgrp(int fd, pid_t pgid) -{ - return ioctl(fd, TIOCSPGRP, pgid); -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/setpgid.html -int setpgid(pid_t pid, pid_t pgid) -{ - int rc = syscall(SC_setpgid, pid, pgid); - __RETURN_WITH_ERRNO(rc, rc, -1); -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/getpgid.html -pid_t getpgid(pid_t pid) -{ - int rc = syscall(SC_getpgid, pid); - __RETURN_WITH_ERRNO(rc, rc, -1); -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/getpgrp.html -pid_t getpgrp() -{ - int rc = syscall(SC_getpgrp); - __RETURN_WITH_ERRNO(rc, rc, -1); -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/read.html -ssize_t read(int fd, void* buf, size_t count) -{ - __pthread_maybe_cancel(); - - int rc = syscall(SC_read, fd, buf, count); - __RETURN_WITH_ERRNO(rc, rc, -1); -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/pread.html -ssize_t pread(int fd, void* buf, size_t count, off_t offset) -{ - __pthread_maybe_cancel(); - - int rc = syscall(SC_pread, fd, buf, count, offset); - __RETURN_WITH_ERRNO(rc, rc, -1); -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/write.html -ssize_t write(int fd, void const* buf, size_t count) -{ - __pthread_maybe_cancel(); - - int rc = syscall(SC_write, fd, buf, count); - __RETURN_WITH_ERRNO(rc, rc, -1); -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/pwrite.html -ssize_t pwrite(int fd, void const* buf, size_t count, off_t offset) -{ - __pthread_maybe_cancel(); - - // FIXME: This is not thread safe and should be implemented in the kernel instead. - off_t old_offset = lseek(fd, 0, SEEK_CUR); - lseek(fd, offset, SEEK_SET); - ssize_t nwritten = write(fd, buf, count); - lseek(fd, old_offset, SEEK_SET); - return nwritten; -} - -// Note: Be sure to send to directory_name parameter a directory name ended with trailing slash. -static int ttyname_r_for_directory(char const* directory_name, dev_t device_mode, ino_t inode_number, char* buffer, size_t size) -{ - DIR* dirstream = opendir(directory_name); - if (!dirstream) { - return -1; - } - - auto close_dir_stream_on_exit = ScopeGuard([dirstream] { - closedir(dirstream); - }); - - struct dirent* entry = nullptr; - char* name_path = nullptr; - auto const directory_name_length = strlen(directory_name); - - // FIXME: Use LibCore DirIterator here instead - while ((entry = readdir(dirstream)) != nullptr) { - if (((ino_t)entry->d_ino == inode_number) - && strcmp(entry->d_name, "stdin") - && strcmp(entry->d_name, "stdout") - && strcmp(entry->d_name, "stderr")) { - - size_t name_length = directory_name_length + strlen(entry->d_name) + 1; - - if (name_length > size) { - errno = ERANGE; - return -1; - } - - name_path = (char*)malloc(name_length); - // FIXME: ttyname_r() is not allowed to return ENOMEM, find better way to store name_path, - // perhaps a static storage. - VERIFY(name_path); - memset(name_path, 0, name_length); - memcpy(name_path, directory_name, directory_name_length); - memcpy(&name_path[directory_name_length], entry->d_name, strlen(entry->d_name)); - struct stat st; - if (lstat(name_path, &st) < 0) { - free(name_path); - name_path = nullptr; - continue; - } - - if (device_mode == st.st_rdev) { - memset(buffer, 0, name_length); - memcpy(buffer, name_path, name_length); - free(name_path); - return 0; - } - } - } - free(name_path); - return -1; -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/ttyname_r.html -int ttyname_r(int fd, char* buffer, size_t size) -{ - struct stat stat; - if (fstat(fd, &stat) < 0) - return -1; - dev_t major_minor_numbers = stat.st_rdev; - ino_t inode_number = stat.st_ino; - if (ttyname_r_for_directory("/dev/", major_minor_numbers, inode_number, buffer, size) < 0) { - if (ttyname_r_for_directory("/dev/pts/", major_minor_numbers, inode_number, buffer, size) < 0) { - errno = ENOTTY; - return -1; - } - } - return 0; -} - -static char ttyname_buf[32]; -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/ttyname.html -char* ttyname(int fd) -{ - if (ttyname_r(fd, ttyname_buf, sizeof(ttyname_buf)) < 0) - return nullptr; - return ttyname_buf; -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/close.html -int close(int fd) -{ - __pthread_maybe_cancel(); - - int rc = syscall(SC_close, fd); - __RETURN_WITH_ERRNO(rc, rc, -1); -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/chdir.html -int chdir(char const* path) -{ - if (!path) { - errno = EFAULT; - return -1; - } - int rc = syscall(SC_chdir, path, strlen(path)); - __RETURN_WITH_ERRNO(rc, rc, -1); -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/fchdir.html -int fchdir(int fd) -{ - int rc = syscall(SC_fchdir, fd); - __RETURN_WITH_ERRNO(rc, rc, -1); -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/getcwd.html -char* getcwd(char* buffer, size_t size) -{ - if (buffer && size == 0) { - // POSIX requires that we set errno to EINVAL here, but in our syscall it makes sense to - // allow "probing" the Kernel with a zero-sized buffer, and it does not return -EINVAL. - // So we have to inject EINVAL here. - errno = EINVAL; - return nullptr; - } - - bool self_allocated = false; - if (!buffer) { - size = size ? size : 64; - buffer = (char*)malloc(size); - self_allocated = true; - } - - int rc = syscall(SC_getcwd, buffer, size); - if (rc < 0) { - if (self_allocated) - free(buffer); - errno = -rc; - return nullptr; - } - - size_t actual_size = static_cast(rc); - if (actual_size <= size) { - return buffer; - } - - // If we get here, the current directory path was silently truncated. - - if (!self_allocated) { - // In this case, POSIX causes information loss: the caller cannot learn about the ideal - // buffer size. This is the reason we went with silently truncation instead. - errno = ERANGE; - return nullptr; - } - - // Try again. - free(buffer); - size = actual_size; - buffer = (char*)malloc(size); - rc = syscall(SC_getcwd, buffer, size); - if (rc < 0) { - // Can only happen if we lose a race. Let's pretend we lost the race in the first place. - free(buffer); - errno = -rc; - return nullptr; - } - - actual_size = static_cast(rc); - if (actual_size < size) { - // If we're here, then cwd has become longer while we were looking at it. (Race with another thread?) - // There's not much we can do, unless we want to loop endlessly - // in this case. Let's leave it up to the caller whether to loop. - free(buffer); - errno = EAGAIN; - return nullptr; - } - - return buffer; -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/getwd.html -char* getwd(char* buf) -{ - if (buf == nullptr) { - errno = EINVAL; - return nullptr; - } - auto* p = getcwd(buf, PATH_MAX); - if (errno == ERANGE) { - // POSIX quirk - errno = ENAMETOOLONG; - } - return p; -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/sleep.html -unsigned int sleep(unsigned int seconds) -{ - struct timespec ts = { seconds, 0 }; - if (clock_nanosleep(CLOCK_MONOTONIC_COARSE, 0, &ts, nullptr) < 0) - return ts.tv_sec; - return 0; -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/usleep.html -int usleep(useconds_t usec) -{ - struct timespec ts = { (long)(usec / 1000000), (long)(usec % 1000000) * 1000 }; - return clock_nanosleep(CLOCK_MONOTONIC_COARSE, 0, &ts, nullptr); -} - -int gethostname(char* buffer, size_t size) -{ - int rc = syscall(SC_gethostname, buffer, size); - __RETURN_WITH_ERRNO(rc, rc, -1); -} - -int sethostname(char const* hostname, ssize_t size) -{ - int rc = syscall(SC_sethostname, hostname, size); - __RETURN_WITH_ERRNO(rc, rc, -1); -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/readlinkat.html -ssize_t readlink(char const* path, char* buffer, size_t size) -{ - return readlinkat(AT_FDCWD, path, buffer, size); -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/readlinkat.html -ssize_t readlinkat(int dirfd, char const* path, char* buffer, size_t size) -{ - Syscall::SC_readlink_params params { { path, strlen(path) }, { buffer, size }, dirfd }; - int rc = syscall(SC_readlink, ¶ms); - // Return the number of bytes placed in the buffer, not the full path size. - __RETURN_WITH_ERRNO(rc, min((size_t)rc, size), -1); -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/lseek.html -off_t lseek(int fd, off_t offset, int whence) -{ - int rc = syscall(SC_lseek, fd, &offset, whence); - __RETURN_WITH_ERRNO(rc, offset, -1); -} -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/link.html -int link(char const* old_path, char const* new_path) -{ - if (!old_path || !new_path) { - errno = EFAULT; - return -1; - } - Syscall::SC_link_params params { { old_path, strlen(old_path) }, { new_path, strlen(new_path) } }; - int rc = syscall(SC_link, ¶ms); - __RETURN_WITH_ERRNO(rc, rc, -1); -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/unlink.html -int unlink(char const* pathname) -{ - return unlinkat(AT_FDCWD, pathname, 0); -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/unlinkat.html -int unlinkat(int dirfd, char const* pathname, int flags) -{ - int rc = syscall(SC_unlink, dirfd, pathname, strlen(pathname), flags); - __RETURN_WITH_ERRNO(rc, rc, -1); -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/symlink.html -int symlink(char const* target, char const* linkpath) -{ - return symlinkat(target, AT_FDCWD, linkpath); -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/symlinkat.html -int symlinkat(char const* target, int newdirfd, char const* linkpath) -{ - if (!target || !linkpath) { - errno = EFAULT; - return -1; - } - Syscall::SC_symlink_params params { { target, strlen(target) }, { linkpath, strlen(linkpath) }, newdirfd }; - int rc = syscall(SC_symlink, ¶ms); - __RETURN_WITH_ERRNO(rc, rc, -1); -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/rmdir.html -int rmdir(char const* pathname) -{ - if (!pathname) { - errno = EFAULT; - return -1; - } - int rc = syscall(SC_rmdir, pathname, strlen(pathname)); - __RETURN_WITH_ERRNO(rc, rc, -1); -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/isatty.html -int isatty(int fd) -{ - return fcntl(fd, F_ISTTY); -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/dup.html -int dup(int old_fd) -{ - return fcntl(old_fd, F_DUPFD, 0); -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/dup2.html -int dup2(int old_fd, int new_fd) -{ - int rc = syscall(SC_dup2, old_fd, new_fd); - __RETURN_WITH_ERRNO(rc, rc, -1); -} - -int setgroups(size_t size, gid_t const* list) -{ - int rc = syscall(SC_setgroups, size, list); - __RETURN_WITH_ERRNO(rc, rc, -1); -} - -int getgroups(int size, gid_t list[]) -{ - int rc = syscall(SC_getgroups, size, list); - __RETURN_WITH_ERRNO(rc, rc, -1); -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/pipe.html -int pipe(int pipefd[2]) -{ - return pipe2(pipefd, 0); -} - -// -int pipe2(int pipefd[2], int flags) -{ - int rc = syscall(SC_pipe, pipefd, flags); - __RETURN_WITH_ERRNO(rc, rc, -1); -} - -unsigned int alarm(unsigned int seconds) -{ - return syscall(SC_alarm, seconds); -} - -int seteuid(uid_t euid) -{ - int rc = syscall(SC_seteuid, euid); - __RETURN_WITH_ERRNO(rc, rc, -1); -} - -int setegid(gid_t egid) -{ - int rc = syscall(SC_setegid, egid); - __RETURN_WITH_ERRNO(rc, rc, -1); -} - -int setuid(uid_t uid) -{ - int rc = syscall(SC_setuid, uid); - __RETURN_WITH_ERRNO(rc, rc, -1); -} - -int setgid(gid_t gid) -{ - int rc = syscall(SC_setgid, gid); - __RETURN_WITH_ERRNO(rc, rc, -1); -} - -int setreuid(uid_t ruid, uid_t euid) -{ - int rc = syscall(SC_setreuid, ruid, euid); - __RETURN_WITH_ERRNO(rc, rc, -1); -} - -int setresuid(uid_t ruid, uid_t euid, uid_t suid) -{ - int rc = syscall(SC_setresuid, ruid, euid, suid); - __RETURN_WITH_ERRNO(rc, rc, -1); -} - -int setregid(gid_t rgid, gid_t egid) -{ - int rc = syscall(SC_setresgid, rgid, egid); - __RETURN_WITH_ERRNO(rc, rc, -1); -} - -int setresgid(gid_t rgid, gid_t egid, gid_t sgid) -{ - int rc = syscall(SC_setresgid, rgid, egid, sgid); - __RETURN_WITH_ERRNO(rc, rc, -1); -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/access.html -int access(char const* pathname, int mode) -{ - return faccessat(AT_FDCWD, pathname, mode, 0); -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/faccessat.html -int faccessat(int dirfd, char const* pathname, int mode, int flags) -{ - if (!pathname) { - errno = EFAULT; - return -1; - } - - Syscall::SC_faccessat_params params { dirfd, { pathname, strlen(pathname) }, mode, flags }; - int rc = syscall(SC_faccessat, ¶ms); - __RETURN_WITH_ERRNO(rc, rc, -1); -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/mknod.html -int mknod(char const* pathname, mode_t mode, dev_t dev) -{ - return mknodat(AT_FDCWD, pathname, mode, dev); -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/mknodat.html -int mknodat(int dirfd, char const* pathname, mode_t mode, dev_t dev) -{ - if (!pathname) { - errno = EFAULT; - return -1; - } - Syscall::SC_mknod_params params { { pathname, strlen(pathname) }, mode, dev, dirfd }; - int rc = syscall(SC_mknod, ¶ms); - __RETURN_WITH_ERRNO(rc, rc, -1); -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/fpathconf.html -long fpathconf([[maybe_unused]] int fd, int name) -{ - switch (name) { - case _PC_NAME_MAX: - return NAME_MAX; - case _PC_PATH_MAX: - return PATH_MAX; - case _PC_VDISABLE: - return _POSIX_VDISABLE; - case _PC_LINK_MAX: - return LINK_MAX; - } - - VERIFY_NOT_REACHED(); -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/pathconf.html -long pathconf([[maybe_unused]] char const* path, int name) -{ - switch (name) { - case _PC_NAME_MAX: - return NAME_MAX; - case _PC_PATH_MAX: - return PATH_MAX; - case _PC_PIPE_BUF: - return PIPE_BUF; - case _PC_LINK_MAX: - return LINK_MAX; - } - - VERIFY_NOT_REACHED(); -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/_exit.html -void _exit(int status) -{ - syscall(SC_exit, status); - VERIFY_NOT_REACHED(); -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/sync.html -void sync() -{ - syscall(SC_sync); -} - -static Optional getlogin_buffer {}; - -char* getlogin() -{ - if (!getlogin_buffer.has_value()) { - if (auto* passwd = getpwuid(getuid())) { - getlogin_buffer = ByteString(passwd->pw_name); - } - endpwent(); - } - return const_cast(getlogin_buffer->characters()); -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/ftruncate.html -int ftruncate(int fd, off_t length) -{ - int rc = syscall(SC_ftruncate, fd, length); - __RETURN_WITH_ERRNO(rc, rc, -1); -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/truncate.html -int truncate(char const* path, off_t length) -{ - int fd = open(path, O_RDWR | O_CREAT, 0666); - if (fd < 0) - return fd; - int rc = ftruncate(fd, length); - int saved_errno = errno; - if (int close_rc = close(fd); close_rc < 0) - return close_rc; - errno = saved_errno; - return rc; -} - -int gettid() -{ - int cached_tid = s_cached_tid; - if (!cached_tid) { - cached_tid = syscall(SC_gettid); - s_cached_tid = cached_tid; - } - return cached_tid; -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/fsync.html -int fsync(int fd) -{ - __pthread_maybe_cancel(); - - int rc = syscall(SC_fsync, fd); - __RETURN_WITH_ERRNO(rc, rc, -1); -} - -int fsopen(char const* fs_type, int flags) -{ - if (!fs_type) { - errno = EFAULT; - return -1; - } - - Syscall::SC_fsopen_params params { - { fs_type, strlen(fs_type) }, - flags, - }; - int rc = syscall(SC_fsopen, ¶ms); - __RETURN_WITH_ERRNO(rc, rc, -1); -} - -int fsmount(int mount_fd, int source_fd, char const* target) -{ - if (!target) { - errno = EFAULT; - return -1; - } - - Syscall::SC_fsmount_params params { - mount_fd, - { target, strlen(target) }, - source_fd, - }; - int rc = syscall(SC_fsmount, ¶ms); - __RETURN_WITH_ERRNO(rc, rc, -1); -} - -int bindmount(int source_fd, char const* target, int flags) -{ - if (!target) { - errno = EFAULT; - return -1; - } - - Syscall::SC_bindmount_params params { - { target, strlen(target) }, - source_fd, - flags, - }; - int rc = syscall(SC_bindmount, ¶ms); - __RETURN_WITH_ERRNO(rc, rc, -1); -} - -int mount(int source_fd, char const* target, char const* fs_type, int flags) -{ - if (flags & MS_BIND) - return bindmount(source_fd, target, flags); - - int mount_fd = fsopen(fs_type, flags); - if (mount_fd < 0) - return -1; - - return fsmount(mount_fd, source_fd, target); -} - -int umount(char const* mountpoint) -{ - int rc = syscall(SC_umount, mountpoint, strlen(mountpoint)); - __RETURN_WITH_ERRNO(rc, rc, -1); -} - -void dump_backtrace() -{ - syscall(SC_dump_backtrace); -} - -int get_process_name(char* buffer, int buffer_size) -{ - int rc = syscall(SC_prctl, PR_GET_PROCESS_NAME, buffer, buffer_size, nullptr); - __RETURN_WITH_ERRNO(rc, rc, -1); -} - -int set_process_name(char const* name, size_t name_length) -{ - int rc = syscall(SC_prctl, PR_SET_PROCESS_NAME, name, name_length, nullptr); - __RETURN_WITH_ERRNO(rc, rc, -1); -} - -int pledge(char const* promises, char const* execpromises) -{ - Syscall::SC_pledge_params params { - { promises, promises ? strlen(promises) : 0 }, - { execpromises, execpromises ? strlen(execpromises) : 0 } - }; - int rc = syscall(SC_pledge, ¶ms); - __RETURN_WITH_ERRNO(rc, rc, -1); -} - -int unveil(char const* path, char const* permissions) -{ - Syscall::SC_unveil_params params { - static_cast(UnveilFlags::CurrentProgram), - { path, path ? strlen(path) : 0 }, - { permissions, permissions ? strlen(permissions) : 0 } - }; - int rc = syscall(SC_unveil, ¶ms); - __RETURN_WITH_ERRNO(rc, rc, -1); -} - -// https://pubs.opengroup.org/onlinepubs/7908799/xsh/getpass.html -char* getpass(char const* prompt) -{ - int tty = open("/dev/tty", O_RDWR | O_NOCTTY | O_CLOEXEC); - if (tty < 0) - return nullptr; - - static char password[PASS_MAX]; - ssize_t chars_read; - - { - auto close_tty = ScopeGuard([tty] { - close(tty); - }); - - struct termios backup { }; - if (tcgetattr(tty, &backup) < 0) - return nullptr; - - { - auto restore_termios = ScopeGuard([tty, backup] { - tcsetattr(tty, TCSAFLUSH, &backup); - }); - - struct termios noecho = backup; - noecho.c_lflag &= ~(ECHO); - noecho.c_lflag |= ICANON; - - if (tcsetattr(tty, TCSAFLUSH, &noecho) < 0) - return nullptr; - if (tcdrain(tty) < 0) - return nullptr; - - if (prompt) { - if (write(tty, prompt, strlen(prompt)) < 0) - return nullptr; - } - - chars_read = read(tty, password, sizeof(password)); - } - - write(tty, "\n", 1); - } - - if (chars_read < 0) - return nullptr; - - if (chars_read > 0 && (password[chars_read - 1] == '\n' || chars_read == sizeof(password))) - password[chars_read - 1] = '\0'; - else - password[chars_read] = '\0'; - - return password; -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/sysconf.html -long sysconf(int name) -{ - int rc = syscall(SC_sysconf, name); - __RETURN_WITH_ERRNO(rc, rc, -1); -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/getpagesize.html -int getpagesize() -{ - return PAGE_SIZE; -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/pause.html -int pause() -{ - return select(0, nullptr, nullptr, nullptr, nullptr); -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/chroot.html -int chroot(char const* path) -{ - dbgln("FIXME: chroot(\"{}\")", path); - return -1; -} - -// https://pubs.opengroup.org/onlinepubs/7908799/xsh/getdtablesize.html -int getdtablesize() -{ - rlimit dtablesize; - int rc = getrlimit(RLIMIT_NOFILE, &dtablesize); - __RETURN_WITH_ERRNO(rc, dtablesize.rlim_cur, rc); -} - -// https://pubs.opengroup.org/onlinepubs/007904975/functions/nice.html -int nice(int incr) -{ - dbgln("FIXME: nice was called with: {}, not implemented", incr); - return incr; -} - -int brk(void* addr) -{ - dbgln("TODO: brk({:#x})", addr); - errno = ENOMEM; - return -1; -} - -void* sbrk(intptr_t incr) -{ - dbgln("TODO: sbrk({:#x})", incr); - errno = ENOMEM; - return reinterpret_cast(-1); -} -} diff --git a/Userland/Libraries/LibC/unistd.h b/Userland/Libraries/LibC/unistd.h deleted file mode 100644 index 0ecea2b36cb..00000000000 --- a/Userland/Libraries/LibC/unistd.h +++ /dev/null @@ -1,166 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -/* standard symbolic constants and types - * - * values from POSIX standard unix specification - * - * https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/unistd.h.html - */ - -#pragma once - -#include -#include -#include -#include -#include - -__BEGIN_DECLS - -#define HZ 1000 - -/* lseek whence values */ -#ifndef _STDIO_H /* also defined in stdio.h */ -# define SEEK_SET 0 /* from beginning of file. */ -# define SEEK_CUR 1 /* from current position in file. */ -# define SEEK_END 2 /* from the end of the file. */ -#endif - -extern char** environ; - -int get_process_name(char* buffer, int buffer_size); -int set_process_name(char const* name, size_t name_length); -void dump_backtrace(void); -int fsync(int fd); -int gettid(void); -int getpagesize(void); -pid_t fork(void); -pid_t vfork(void); -int daemon(int nochdir, int noclose); -int execv(char const* path, char* const argv[]); -int execve(char const* filename, char* const argv[], char* const envp[]); -int execvpe(char const* filename, char* const argv[], char* const envp[]); -int execvp(char const* filename, char* const argv[]); -int execl(char const* filename, char const* arg, ...); -int execle(char const* filename, char const* arg, ...); -int execlp(char const* filename, char const* arg, ...); -void sync(void); -__attribute__((noreturn)) void _exit(int status); -pid_t getsid(pid_t); -pid_t setsid(void); -int setpgid(pid_t pid, pid_t pgid); -pid_t getpgid(pid_t); -pid_t getpgrp(void); -uid_t geteuid(void); -gid_t getegid(void); -uid_t getuid(void); -gid_t getgid(void); -pid_t getpid(void); -pid_t getppid(void); -int getresuid(uid_t*, uid_t*, uid_t*); -int getresgid(gid_t*, gid_t*, gid_t*); -int getgroups(int size, gid_t list[]); -int setgroups(size_t, gid_t const*); -int seteuid(uid_t); -int setegid(gid_t); -int setuid(uid_t); -int setgid(gid_t); -int setreuid(uid_t, uid_t); -int setresuid(uid_t, uid_t, uid_t); -int setregid(gid_t, gid_t); -int setresgid(gid_t, gid_t, gid_t); -pid_t tcgetpgrp(int fd); -int tcsetpgrp(int fd, pid_t pgid); -ssize_t read(int fd, void* buf, size_t count); -ssize_t pread(int fd, void* buf, size_t count, off_t); -ssize_t write(int fd, void const* buf, size_t count); -ssize_t pwrite(int fd, void const* buf, size_t count, off_t); -int close(int fd); -int chdir(char const* path); -int fchdir(int fd); -char* getcwd(char* buffer, size_t size); -char* getwd(char* buffer); -unsigned int sleep(unsigned int seconds); -int usleep(useconds_t); -int gethostname(char*, size_t); -int sethostname(char const*, ssize_t); -ssize_t readlink(char const* path, char* buffer, size_t); -ssize_t readlinkat(int dirfd, char const* path, char* buffer, size_t); -char* ttyname(int fd); -int ttyname_r(int fd, char* buffer, size_t); -off_t lseek(int fd, off_t, int whence); -int link(char const* oldpath, char const* newpath); -int unlink(char const* pathname); -int unlinkat(int dirfd, char const* pathname, int flags); -int symlink(char const* target, char const* linkpath); -int symlinkat(char const* target, int newdirfd, char const* linkpath); -int rmdir(char const* pathname); -int dup(int old_fd); -int dup2(int old_fd, int new_fd); -int pipe(int pipefd[2]); -int pipe2(int pipefd[2], int flags); -unsigned int alarm(unsigned int seconds); -int access(char const* pathname, int mode); -int faccessat(int dirfd, char const* pathname, int mode, int flags); -int isatty(int fd); -int mknod(char const* pathname, mode_t, dev_t); -int mknodat(int dirfd, char const* pathname, mode_t, dev_t); -long fpathconf(int fd, int name); -long pathconf(char const* path, int name); -char* getlogin(void); -int lchown(char const* pathname, uid_t uid, gid_t gid); -int chown(char const* pathname, uid_t, gid_t); -int fchown(int fd, uid_t, gid_t); -int fchownat(int fd, char const* pathname, uid_t uid, gid_t gid, int flags); -int ftruncate(int fd, off_t length); -int truncate(char const* path, off_t length); -int fsopen(char const* fs_type, int flags); -int fsmount(int mount_fd, int source_fd, char const* target); -int bindmount(int source_fd, char const* target, int flags); -int mount(int source_fd, char const* target, char const* fs_type, int flags); -int umount(char const* mountpoint); -int pledge(char const* promises, char const* execpromises); -int unveil(char const* path, char const* permissions); -char* getpass(char const* prompt); -int pause(void); -int chroot(char const*); -int getdtablesize(void); -int nice(int incr); -int brk(void* addr); -void* sbrk(intptr_t incr); - -enum { - _PC_NAME_MAX, - _PC_PATH_MAX, - _PC_PIPE_BUF, - _PC_VDISABLE, - _PC_LINK_MAX -}; - -#define _POSIX_FSYNC 200112L -#define _POSIX_MAPPED_FILES 200112L -#define _POSIX_MEMORY_PROTECTION 200112L -#define _POSIX_MONOTONIC_CLOCK 200112L -#define _POSIX_RAW_SOCKETS 200112L -#define _POSIX_REGEXP 1 -#define _POSIX_SAVED_IDS 1 -#define _POSIX_SPAWN 200112L -#define _POSIX_THREADS 200112L -#define _POSIX_THREAD_ATTR_STACKADDR 200112L -#define _POSIX_THREAD_ATTR_STACKSIZE 200112L -#define _POSIX_TIMERS 200809L - -/* - * We aren't fully compliant (don't support policies, and don't have a wide - * range of values), but we do have process priorities. - */ -#define _POSIX_PRIORITY_SCHEDULING -#define _POSIX_VDISABLE '\0' - -long sysconf(int name); - -__END_DECLS diff --git a/Userland/Libraries/LibC/utime.cpp b/Userland/Libraries/LibC/utime.cpp deleted file mode 100644 index 4fe8147a1a6..00000000000 --- a/Userland/Libraries/LibC/utime.cpp +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include - -extern "C" { - -int utime(char const* pathname, const struct utimbuf* buf) -{ - if (!pathname) { - errno = EFAULT; - return -1; - } - int rc = syscall(SC_utime, pathname, strlen(pathname), buf); - __RETURN_WITH_ERRNO(rc, rc, -1); -} -} diff --git a/Userland/Libraries/LibC/utime.h b/Userland/Libraries/LibC/utime.h deleted file mode 100644 index 71904ba7e74..00000000000 --- a/Userland/Libraries/LibC/utime.h +++ /dev/null @@ -1,15 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include - -__BEGIN_DECLS - -int utime(char const* pathname, const struct utimbuf*); - -__END_DECLS diff --git a/Userland/Libraries/LibC/utmp.h b/Userland/Libraries/LibC/utmp.h deleted file mode 100644 index 07a115b85e7..00000000000 --- a/Userland/Libraries/LibC/utmp.h +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include - -__BEGIN_DECLS - -struct exit_status { /* Type for ut_exit, below */ - short int e_termination; /* Process termination status */ - short int e_exit; /* Process exit status */ -}; - -#define USER_PROCESS 7 -#define DEAD_PROCESS 8 - -#define UT_NAMESIZE 32 -#define UT_LINESIZE 32 -#define UT_HOSTSIZE 256 - -struct utmp { - short ut_type; /* Type of record */ - pid_t ut_pid; /* PID of login process */ - char ut_line[UT_LINESIZE]; /* Device name of tty - "/dev/" */ - char ut_id[4]; /* Terminal name suffix, - or inittab(5) ID */ - char ut_user[UT_NAMESIZE]; /* Username */ - char ut_host[UT_HOSTSIZE]; /* Hostname for remote login, or - kernel version for run-level - messages */ - struct exit_status ut_exit; /* Exit status of a process - marked as DEAD_PROCESS; not - used by Linux init (1 */ - - long ut_session; /* Session ID */ - struct timeval ut_tv; /* Time entry was made */ - - int32_t ut_addr_v6[4]; /* Internet address of remote - host; IPv4 address uses - just ut_addr_v6[0] */ - - char __unused[20]; /* Reserved for future use */ -}; - -/* Backward compatibility hacks */ -#define ut_name ut_user -#ifndef _NO_UT_TIME -# define ut_time ut_tv.tv_sec -#endif -#define ut_xtime ut_tv.tv_sec -#define ut_addr ut_addr_v6[0] - -__END_DECLS diff --git a/Userland/Libraries/LibC/utsname.cpp b/Userland/Libraries/LibC/utsname.cpp deleted file mode 100644 index e8585c16b92..00000000000 --- a/Userland/Libraries/LibC/utsname.cpp +++ /dev/null @@ -1,18 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include - -extern "C" { - -int uname(struct utsname* buf) -{ - int rc = syscall(SC_uname, buf); - __RETURN_WITH_ERRNO(rc, rc, -1); -} -} diff --git a/Userland/Libraries/LibC/wchar.cpp b/Userland/Libraries/LibC/wchar.cpp deleted file mode 100644 index e389a2f19c7..00000000000 --- a/Userland/Libraries/LibC/wchar.cpp +++ /dev/null @@ -1,744 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * Copyright (c) 2022, Tim Schumacher - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -static unsigned int mbstate_expected_bytes(mbstate_t* state) -{ - if (state->stored_bytes == 0) { - return 0; - } - - unsigned char first = state->bytes[0]; - - // Single-byte sequences have their first bit unset - if ((first & 0b10000000) == 0) { - return 1; - } - - // Two-byte sequences start with 0b110xxxxx - if ((first & 0b11100000) == 0b11000000) { - return 2; - } - - // Three-byte sequences start with 0b1110xxxx - if ((first & 0b11110000) == 0b11100000) { - return 3; - } - - // Four-byte sequences start with 0b11110xxx - if ((first & 0b11111000) == 0b11110000) { - return 4; - } - - // Everything else is invalid - return 0; -} - -extern "C" { - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/wcslen.html -size_t wcslen(wchar_t const* str) -{ - size_t len = 0; - while (*(str++)) - ++len; - return len; -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/wcscpy.html -wchar_t* wcscpy(wchar_t* dest, wchar_t const* src) -{ - wchar_t* original_dest = dest; - while ((*dest++ = *src++) != '\0') - ; - return original_dest; -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/wcsdup.html -wchar_t* wcsdup(wchar_t const* str) -{ - size_t length = wcslen(str); - wchar_t* new_str = (wchar_t*)malloc(sizeof(wchar_t) * (length + 1)); - - if (!new_str) { - errno = ENOMEM; - return nullptr; - } - - return wcscpy(new_str, str); -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/wcsncpy.html -wchar_t* wcsncpy(wchar_t* dest, wchar_t const* src, size_t num) -{ - wchar_t* original_dest = dest; - while (((*dest++ = *src++) != '\0') && ((size_t)(dest - original_dest) < num)) - ; - return original_dest; -} - -size_t wcslcpy(wchar_t* dest, wchar_t const* src, size_t n) -{ - size_t i; - for (i = 0; i + 1 < n && src[i] != L'\0'; ++i) - dest[i] = src[i]; - if (n) - dest[i] = L'\0'; - for (; src[i] != L'\0'; ++i) - ; // Determine the length of src, don't copy. - return i; -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/wcscmp.html -int wcscmp(wchar_t const* s1, wchar_t const* s2) -{ - while (*s1 == *s2++) - if (*s1++ == 0) - return 0; - return *(wchar_t const*)s1 - *(wchar_t const*)--s2; -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/wcsncmp.html -int wcsncmp(wchar_t const* s1, wchar_t const* s2, size_t n) -{ - if (!n) - return 0; - do { - if (*s1 != *s2++) - return *(wchar_t const*)s1 - *(wchar_t const*)--s2; - if (*s1++ == 0) - break; - } while (--n); - return 0; -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/wcschr.html -wchar_t* wcschr(wchar_t const* str, int c) -{ - wchar_t ch = c; - for (;; ++str) { - if (*str == ch) - return const_cast(str); - if (!*str) - return nullptr; - } -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/wcsrchr.html -wchar_t* wcsrchr(wchar_t const* str, wchar_t wc) -{ - wchar_t* last = nullptr; - wchar_t c; - for (; (c = *str); ++str) { - if (c == wc) - last = const_cast(str); - } - return last; -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/wcscat.html -wchar_t* wcscat(wchar_t* dest, wchar_t const* src) -{ - size_t dest_length = wcslen(dest); - size_t i; - for (i = 0; src[i] != '\0'; i++) - dest[dest_length + i] = src[i]; - dest[dest_length + i] = '\0'; - return dest; -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/wcsncat.html -wchar_t* wcsncat(wchar_t* dest, wchar_t const* src, size_t n) -{ - size_t dest_length = wcslen(dest); - size_t i; - for (i = 0; i < n && src[i] != '\0'; i++) - dest[dest_length + i] = src[i]; - dest[dest_length + i] = '\0'; - return dest; -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/wcstok.html -wchar_t* wcstok(wchar_t* str, wchar_t const* delim, wchar_t** ptr) -{ - wchar_t* used_str = str; - if (!used_str) { - used_str = *ptr; - } - - size_t token_start = 0; - size_t token_end = 0; - size_t str_len = wcslen(used_str); - size_t delim_len = wcslen(delim); - - for (size_t i = 0; i < str_len; ++i) { - bool is_proper_delim = false; - - for (size_t j = 0; j < delim_len; ++j) { - if (used_str[i] == delim[j]) { - // Skip beginning delimiters - if (token_end - token_start == 0) { - ++token_start; - break; - } - - is_proper_delim = true; - } - } - - ++token_end; - if (is_proper_delim && token_end > 0) { - --token_end; - break; - } - } - - if (used_str[token_start] == '\0') - return nullptr; - - if (token_end == 0) { - return &used_str[token_start]; - } - - used_str[token_end] = '\0'; - return &used_str[token_start]; -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/wcstol.html -long wcstol(wchar_t const*, wchar_t**, int) -{ - dbgln("FIXME: Implement wcstol()"); - TODO(); -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/wcstoll.html -long long wcstoll(wchar_t const*, wchar_t**, int) -{ - dbgln("FIXME: Implement wcstoll()"); - TODO(); -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/btowc.html -wint_t btowc(int c) -{ - if (c == EOF) { - return WEOF; - } - - // Multi-byte sequences in UTF-8 have their highest bit set - if (c & (1 << 7)) { - return WEOF; - } - - return c; -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/mbrtowc.html -size_t mbrtowc(wchar_t* pwc, char const* s, size_t n, mbstate_t* state) -{ - static mbstate_t _anonymous_state = {}; - - if (state == nullptr) { - state = &_anonymous_state; - } - - // s being a null pointer is a shorthand for reading a single null byte. - if (s == nullptr) { - pwc = nullptr; - s = ""; - n = 1; - } - - // Stop early if we can't read anything - if (n == 0) { - return 0; - } - - size_t consumed_bytes = 0; - - // Fill the first byte if we haven't done that yet - if (state->stored_bytes == 0) { - state->bytes[state->stored_bytes++] = s[0]; - consumed_bytes++; - } - - size_t expected_bytes = mbstate_expected_bytes(state); - - // Check if the first byte is invalid - if (expected_bytes == 0) { - *state = {}; - errno = EILSEQ; - return -1; - } - - while (state->stored_bytes < expected_bytes) { - if (consumed_bytes == n) { - // No complete multibyte character - return -2; - } - - unsigned char c = s[consumed_bytes]; - - // Continuation bytes have to start with 0b10xxxxxx - if ((c & 0b11000000) != 0b10000000) { - // Invalid multibyte character - *state = {}; - errno = EILSEQ; - return -1; - } - - state->bytes[state->stored_bytes++] = c; - consumed_bytes++; - } - - wchar_t codepoint = state->bytes[0]; - - // Mask out the "length" bits if necessary - if (expected_bytes > 1) { - codepoint &= (1 << (7 - expected_bytes)) - 1; - } - - for (unsigned int i = 1; i < expected_bytes; i++) { - // Each continuation byte contains 6 bits of data - codepoint = codepoint << 6; - codepoint |= state->bytes[i] & 0b111111; - } - - if (pwc) { - *pwc = codepoint; - } - - // We want to read the next multibyte character, but keep all other properties. - state->stored_bytes = 0; - - if (codepoint == 0) { - *state = {}; - return 0; - } - - return consumed_bytes; -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/mbrlen.html -size_t mbrlen(char const* s, size_t n, mbstate_t* ps) -{ - static mbstate_t anonymous_state = {}; - - if (ps == nullptr) - ps = &anonymous_state; - - return mbrtowc(nullptr, s, n, ps); -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/wcrtomb.html -size_t wcrtomb(char* s, wchar_t wc, mbstate_t*) -{ - if (s == nullptr) - wc = L'\0'; - - auto nwritten = AK::UnicodeUtils::code_point_to_utf8(wc, [&s](char byte) { - if (s != nullptr) - *s++ = byte; - }); - - if (nwritten < 0) { - errno = EILSEQ; - return (size_t)-1; - } else { - return nwritten; - } -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/wcscoll.html -int wcscoll(wchar_t const* ws1, wchar_t const* ws2) -{ - // TODO: Actually implement a sensible sort order for this, - // because right now we are doing what LC_COLLATE=C would do. - return wcscmp(ws1, ws2); -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/wcsxfrm.html -size_t wcsxfrm(wchar_t* dest, wchar_t const* src, size_t n) -{ - // TODO: This needs to be changed when wcscoll is not just doing wcscmp - return wcslcpy(dest, src, n); -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/wctob.html -int wctob(wint_t c) -{ - if (c > 0x7f) - return EOF; - - return static_cast(c); -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/mbsinit.html -int mbsinit(mbstate_t const* state) -{ - if (!state) { - return 1; - } - - if (state->stored_bytes != 0) { - return 0; - } - - return 1; -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/wcspbrk.html -wchar_t* wcspbrk(wchar_t const* wcs, wchar_t const* accept) -{ - for (wchar_t const* cur = accept; *cur; cur++) { - wchar_t* res = wcschr(wcs, *cur); - if (res) - return res; - } - - return nullptr; -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/wcsstr.html -wchar_t* wcsstr(wchar_t const* haystack, wchar_t const* needle) -{ - size_t nlen = wcslen(needle); - - if (nlen == 0) - return const_cast(haystack); - - size_t hlen = wcslen(haystack); - - while (hlen >= nlen) { - if (wcsncmp(haystack, needle, nlen) == 0) - return const_cast(haystack); - - haystack++; - hlen--; - } - - return nullptr; -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/wmemchr.html -wchar_t* wmemchr(wchar_t const* s, wchar_t c, size_t n) -{ - for (size_t i = 0; i < n; i++) { - if (s[i] == c) - return const_cast(&s[i]); - } - - return nullptr; -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/wmemcpy.html -wchar_t* wmemcpy(wchar_t* dest, wchar_t const* src, size_t n) -{ - for (size_t i = 0; i < n; i++) - dest[i] = src[i]; - - return dest; -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/wmemset.html -wchar_t* wmemset(wchar_t* wcs, wchar_t wc, size_t n) -{ - for (size_t i = 0; i < n; i++) { - wcs[i] = wc; - } - - return wcs; -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/wmemmove.html -wchar_t* wmemmove(wchar_t* dest, wchar_t const* src, size_t n) -{ - if (dest > src) { - for (size_t i = 1; i <= n; i++) { - dest[n - i] = src[n - i]; - } - } else if (dest < src) { - for (size_t i = 0; i < n; i++) { - dest[i] = src[i]; - } - } - - return dest; -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/wcstoul.html -unsigned long wcstoul(wchar_t const*, wchar_t**, int) -{ - dbgln("TODO: Implement wcstoul()"); - TODO(); -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/wcstoull.html -unsigned long long wcstoull(wchar_t const*, wchar_t**, int) -{ - dbgln("TODO: Implement wcstoull()"); - TODO(); -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/wcstof.html -float wcstof(wchar_t const*, wchar_t**) -{ - dbgln("TODO: Implement wcstof()"); - TODO(); -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/wcstod.html -double wcstod(wchar_t const*, wchar_t**) -{ - dbgln("TODO: Implement wcstod()"); - TODO(); -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/wcstold.html -long double wcstold(wchar_t const*, wchar_t**) -{ - dbgln("TODO: Implement wcstold()"); - TODO(); -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/wcwidth.html -int wcwidth(wchar_t wc) -{ - if (wc == L'\0') - return 0; - - // Printable ASCII. - if (wc >= 0x20 && wc <= 0x7e) - return 1; - - // Non-printable ASCII. - if (wc <= 0x7f) - return -1; - - // TODO: Implement wcwidth for non-ASCII characters. - return 1; -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/wcswidth.html -int wcswidth(wchar_t const* pwcs, size_t n) -{ - int len = 0; - - for (size_t i = 0; i < n && pwcs[i]; i++) { - int char_len = wcwidth(pwcs[i]); - - if (char_len == -1) - return -1; - - len += char_len; - } - - return len; -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/wcsnrtombs.html -size_t wcsnrtombs(char* dest, wchar_t const** src, size_t nwc, size_t len, mbstate_t* ps) -{ - static mbstate_t _anonymous_state = {}; - - if (ps == nullptr) - ps = &_anonymous_state; - - size_t written = 0; - size_t read = 0; - while (read < nwc) { - size_t ret = 0; - char buf[MB_LEN_MAX]; - - // Convert next wchar to multibyte. - ret = wcrtomb(buf, **src, ps); - - // wchar can't be represented as multibyte. - if (ret == (size_t)-1) { - errno = EILSEQ; - return (size_t)-1; - } - - // New bytes don't fit the buffer. - if (dest && len < written + ret) { - return written; - } - - if (dest) { - memcpy(dest, buf, ret); - dest += ret; - } - - // Null character has been reached - if (**src == L'\0') { - *src = nullptr; - return written; - } - - *src += 1; - read += 1; - written += ret; - } - - return written; -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/mbsnrtowcs.html -size_t mbsnrtowcs(wchar_t* dst, char const** src, size_t nms, size_t len, mbstate_t* ps) -{ - static mbstate_t _anonymous_state = {}; - - if (ps == nullptr) - ps = &_anonymous_state; - - size_t written = 0; - while (written < len || !dst) { - // End of source buffer, no incomplete character. - // src continues to point to the next byte. - if (nms == 0) { - return written; - } - - // Convert next multibyte to wchar. - size_t ret = mbrtowc(dst, *src, nms, ps); - - // Multibyte sequence is incomplete. - if (ret == -2ul) { - // Point just past the last processed byte. - *src += nms; - return written; - } - - // Multibyte sequence is invalid. - if (ret == -1ul) { - errno = EILSEQ; - return (size_t)-1; - } - - // Null byte has been reached. - if (**src == '\0') { - *src = nullptr; - return written; - } - - *src += ret; - nms -= ret; - written += 1; - if (dst) - dst += 1; - } - - // If we are here, we have written `len` wchars, but not reached the null byte. - return written; -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/wmemcmp.html -int wmemcmp(wchar_t const* s1, wchar_t const* s2, size_t n) -{ - while (n-- > 0) { - if (*s1++ != *s2++) - return s1[-1] < s2[-1] ? -1 : 1; - } - return 0; -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/wcsrtombs.html -size_t wcsrtombs(char* dest, wchar_t const** src, size_t len, mbstate_t* ps) -{ - static mbstate_t anonymous_state = {}; - - if (ps == nullptr) - ps = &anonymous_state; - - // SIZE_MAX is as close as we are going to get to "unlimited". - return wcsnrtombs(dest, src, SIZE_MAX, len, ps); -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/mbsrtowcs.html -size_t mbsrtowcs(wchar_t* dst, char const** src, size_t len, mbstate_t* ps) -{ - static mbstate_t anonymous_state = {}; - - if (ps == nullptr) - ps = &anonymous_state; - - // SIZE_MAX is as close as we are going to get to "unlimited". - return mbsnrtowcs(dst, src, SIZE_MAX, len, ps); -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/wcscspn.html -size_t wcscspn(wchar_t const* wcs, wchar_t const* reject) -{ - for (auto const* wc_pointer = wcs;;) { - auto c = *wc_pointer++; - wchar_t rc; - auto const* reject_copy = reject; - do { - if ((rc = *reject_copy++) == c) - return wc_pointer - 1 - wcs; - } while (rc != 0); - } -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/wcsspn.html -size_t wcsspn(wchar_t const* wcs, wchar_t const* accept) -{ - for (auto const* wc_pointer = wcs;;) { - auto c = *wc_pointer++; - wchar_t rc; - auto const* accept_copy = accept; - do { - if ((rc = *accept_copy++) != c) - return wc_pointer - 1 - wcs; - } while (rc != 0); - } -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/wcsftime.html -size_t wcsftime(wchar_t* destination, size_t maxsize, wchar_t const* format, const struct tm* tm) -{ - // FIXME: Add actual wide char support for this. - char* ascii_format = static_cast(malloc(wcslen(format) + 1)); - char* ascii_destination = static_cast(malloc(maxsize)); - - VERIFY(ascii_format && ascii_destination); - - // These are copied by value because we will change the pointers without rolling them back. - ScopeGuard free_ascii = [ascii_format, ascii_destination] { - free(ascii_format); - free(ascii_destination); - }; - - char* ascii_format_copy = ascii_format; - do { - VERIFY(*format <= 0x7f); - *ascii_format_copy++ = static_cast(*format); - } while (*format++ != L'\0'); - -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wformat-nonliteral" - size_t ret = strftime(ascii_destination, maxsize, ascii_format, tm); -#pragma GCC diagnostic pop - - if (ret == 0) - return 0; - - do { - *destination++ = *ascii_destination; - } while (*ascii_destination++ != '\0'); - - return ret; -} -} diff --git a/Userland/Libraries/LibC/wchar.h b/Userland/Libraries/LibC/wchar.h deleted file mode 100644 index 090f340c436..00000000000 --- a/Userland/Libraries/LibC/wchar.h +++ /dev/null @@ -1,114 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -// Includes essentially mandated by POSIX: -// https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/wchar.h.html -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -__BEGIN_DECLS - -#ifndef WEOF -# define WEOF (0xffffffffu) -#endif - -#ifdef __cplusplus -# define _LIBCPP_WCHAR_H_HAS_CONST_OVERLOADS -#endif - -typedef __WINT_TYPE__ wint_t; -typedef unsigned long int wctype_t; - -// A zero-initialized mbstate_t struct must be a valid initial state. -typedef struct { - unsigned char bytes[4]; - unsigned int stored_bytes; -} mbstate_t; - -struct tm; - -size_t wcslen(wchar_t const*); -wchar_t* wcscpy(wchar_t*, wchar_t const*); -wchar_t* wcsdup(wchar_t const*); -wchar_t* wcsncpy(wchar_t*, wchar_t const*, size_t); -__attribute__((warn_unused_result)) size_t wcslcpy(wchar_t*, wchar_t const*, size_t); -int wcscmp(wchar_t const*, wchar_t const*); -int wcsncmp(wchar_t const*, wchar_t const*, size_t); -wchar_t* wcschr(wchar_t const*, int); -wchar_t* wcsrchr(wchar_t const*, wchar_t); -wchar_t* wcscat(wchar_t*, wchar_t const*); -wchar_t* wcsncat(wchar_t*, wchar_t const*, size_t); -wchar_t* wcstok(wchar_t*, wchar_t const*, wchar_t**); -long wcstol(wchar_t const*, wchar_t**, int); -long long wcstoll(wchar_t const*, wchar_t**, int); -wint_t btowc(int c); -size_t mbrtowc(wchar_t*, char const*, size_t, mbstate_t*); -size_t mbrlen(char const*, size_t, mbstate_t*); -size_t wcrtomb(char*, wchar_t, mbstate_t*); -int wcscoll(wchar_t const*, wchar_t const*); -size_t wcsxfrm(wchar_t*, wchar_t const*, size_t); -int wctob(wint_t); -int mbsinit(mbstate_t const*); -wchar_t* wcspbrk(wchar_t const*, wchar_t const*); -wchar_t* wcsstr(wchar_t const*, wchar_t const*); -wchar_t* wmemchr(wchar_t const*, wchar_t, size_t); -wchar_t* wmemcpy(wchar_t*, wchar_t const*, size_t); -wchar_t* wmemset(wchar_t*, wchar_t, size_t); -wchar_t* wmemmove(wchar_t*, wchar_t const*, size_t); -unsigned long wcstoul(wchar_t const*, wchar_t**, int); -unsigned long long wcstoull(wchar_t const*, wchar_t**, int); -float wcstof(wchar_t const*, wchar_t**); -double wcstod(wchar_t const*, wchar_t**); -long double wcstold(wchar_t const*, wchar_t**); -int wcwidth(wchar_t); -int wcswidth(wchar_t const*, size_t); -size_t wcsrtombs(char*, wchar_t const**, size_t, mbstate_t*); -size_t mbsrtowcs(wchar_t*, char const**, size_t, mbstate_t*); -int wmemcmp(wchar_t const*, wchar_t const*, size_t); -size_t wcsnrtombs(char*, wchar_t const**, size_t, size_t, mbstate_t*); -size_t mbsnrtowcs(wchar_t*, char const**, size_t, size_t, mbstate_t*); -size_t wcscspn(wchar_t const* wcs, wchar_t const* reject); -size_t wcsspn(wchar_t const* wcs, wchar_t const* accept); - -wint_t fgetwc(FILE* stream); -wint_t getwc(FILE* stream); -wint_t getwchar(void); -wint_t fputwc(wchar_t wc, FILE* stream); -wint_t putwc(wchar_t wc, FILE* stream); -wint_t putwchar(wchar_t wc); -wchar_t* fgetws(wchar_t* __restrict ws, int n, FILE* __restrict stream); -int fputws(wchar_t const* __restrict ws, FILE* __restrict stream); -wint_t ungetwc(wint_t wc, FILE* stream); -int fwide(FILE* stream, int mode); - -int wprintf(wchar_t const* __restrict format, ...); -int fwprintf(FILE* __restrict stream, wchar_t const* __restrict format, ...); -int swprintf(wchar_t* __restrict wcs, size_t maxlen, wchar_t const* __restrict format, ...); -int vwprintf(wchar_t const* __restrict format, va_list args); -int vfwprintf(FILE* __restrict stream, wchar_t const* __restrict format, va_list args); -int vswprintf(wchar_t* __restrict wcs, size_t maxlen, wchar_t const* __restrict format, va_list args); - -int fwscanf(FILE* __restrict stream, wchar_t const* __restrict format, ...); -int swscanf(wchar_t const* __restrict ws, wchar_t const* __restrict format, ...); -int wscanf(wchar_t const* __restrict format, ...); -int vfwscanf(FILE* __restrict stream, wchar_t const* __restrict format, va_list arg); -int vswscanf(wchar_t const* __restrict ws, wchar_t const* __restrict format, va_list arg); -int vwscanf(wchar_t const* __restrict format, va_list arg); - -size_t wcsftime(wchar_t* __restrict wcs, size_t maxsize, wchar_t const* __restrict format, const struct tm* __restrict timeptr); - -__END_DECLS diff --git a/Userland/Libraries/LibC/wctype.cpp b/Userland/Libraries/LibC/wctype.cpp deleted file mode 100644 index f026a34f1c3..00000000000 --- a/Userland/Libraries/LibC/wctype.cpp +++ /dev/null @@ -1,213 +0,0 @@ -/* - * Copyright (c) 2021, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include - -enum { - WCTYPE_INVALID = 0, - WCTYPE_ALNUM, - WCTYPE_ALPHA, - WCTYPE_BLANK, - WCTYPE_CNTRL, - WCTYPE_DIGIT, - WCTYPE_GRAPH, - WCTYPE_LOWER, - WCTYPE_PRINT, - WCTYPE_PUNCT, - WCTYPE_SPACE, - WCTYPE_UPPER, - WCTYPE_XDIGIT, -}; - -enum { - WCTRANS_INVALID = 0, - WCTRANS_TOLOWER, - WCTRANS_TOUPPER, -}; - -extern "C" { - -int iswalnum(wint_t wc) -{ - return __inline_isalnum(wc); -} - -int iswalpha(wint_t wc) -{ - return __inline_isalpha(wc); -} - -int iswcntrl(wint_t wc) -{ - return __inline_iscntrl(wc); -} - -int iswdigit(wint_t wc) -{ - return __inline_isdigit(wc); -} - -int iswxdigit(wint_t wc) -{ - return __inline_isxdigit(wc); -} - -int iswspace(wint_t wc) -{ - return __inline_isspace(wc); -} - -int iswpunct(wint_t wc) -{ - return __inline_ispunct(wc); -} - -int iswprint(wint_t wc) -{ - return __inline_isprint(wc); -} - -int iswgraph(wint_t wc) -{ - return __inline_isgraph(wc); -} - -int iswlower(wint_t wc) -{ - return __inline_islower(wc); -} - -int iswupper(wint_t wc) -{ - return __inline_isupper(wc); -} - -int iswblank(wint_t wc) -{ - return __inline_isblank(wc); -} - -int iswctype(wint_t wc, wctype_t charclass) -{ - switch (charclass) { - case WCTYPE_ALNUM: - return iswalnum(wc); - - case WCTYPE_ALPHA: - return iswalpha(wc); - - case WCTYPE_BLANK: - return iswblank(wc); - - case WCTYPE_CNTRL: - return iswcntrl(wc); - - case WCTYPE_DIGIT: - return iswdigit(wc); - - case WCTYPE_GRAPH: - return iswgraph(wc); - - case WCTYPE_LOWER: - return iswlower(wc); - - case WCTYPE_PRINT: - return iswprint(wc); - - case WCTYPE_PUNCT: - return iswpunct(wc); - - case WCTYPE_SPACE: - return iswspace(wc); - - case WCTYPE_UPPER: - return iswupper(wc); - - case WCTYPE_XDIGIT: - return iswxdigit(wc); - - default: - return 0; - } -} - -wctype_t wctype(char const* property) -{ - if (strcmp(property, "alnum") == 0) - return WCTYPE_ALNUM; - - if (strcmp(property, "alpha") == 0) - return WCTYPE_ALPHA; - - if (strcmp(property, "blank") == 0) - return WCTYPE_BLANK; - - if (strcmp(property, "cntrl") == 0) - return WCTYPE_CNTRL; - - if (strcmp(property, "digit") == 0) - return WCTYPE_DIGIT; - - if (strcmp(property, "graph") == 0) - return WCTYPE_GRAPH; - - if (strcmp(property, "lower") == 0) - return WCTYPE_LOWER; - - if (strcmp(property, "print") == 0) - return WCTYPE_PRINT; - - if (strcmp(property, "punct") == 0) - return WCTYPE_PUNCT; - - if (strcmp(property, "space") == 0) - return WCTYPE_SPACE; - - if (strcmp(property, "upper") == 0) - return WCTYPE_UPPER; - - if (strcmp(property, "xdigit") == 0) - return WCTYPE_XDIGIT; - - return WCTYPE_INVALID; -} - -wint_t towlower(wint_t wc) -{ - return __inline_tolower(wc); -} - -wint_t towupper(wint_t wc) -{ - return __inline_toupper(wc); -} - -wint_t towctrans(wint_t wc, wctrans_t desc) -{ - switch (desc) { - case WCTRANS_TOLOWER: - return towlower(wc); - - case WCTRANS_TOUPPER: - return towupper(wc); - - default: - return wc; - } -} - -wctrans_t wctrans(char const* charclass) -{ - if (strcmp(charclass, "tolower") == 0) - return WCTRANS_TOLOWER; - - if (strcmp(charclass, "toupper") == 0) - return WCTRANS_TOUPPER; - - return WCTRANS_INVALID; -} -} diff --git a/Userland/Libraries/LibC/wctype.h b/Userland/Libraries/LibC/wctype.h deleted file mode 100644 index a154f983b80..00000000000 --- a/Userland/Libraries/LibC/wctype.h +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright (c) 2021, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -// Includes essentially mandated by POSIX: -// https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/wctype.h.html -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -__BEGIN_DECLS - -typedef long wctrans_t; - -int iswalnum(wint_t wc); -int iswalpha(wint_t wc); -int iswcntrl(wint_t wc); -int iswdigit(wint_t wc); -int iswxdigit(wint_t wc); -int iswspace(wint_t wc); -int iswpunct(wint_t wc); -int iswprint(wint_t wc); -int iswgraph(wint_t wc); -int iswlower(wint_t wc); -int iswupper(wint_t wc); -int iswblank(wint_t wc); -int iswctype(wint_t, wctype_t); -wctype_t wctype(char const*); -wint_t towlower(wint_t wc); -wint_t towupper(wint_t wc); -wint_t towctrans(wint_t, wctrans_t); -wctrans_t wctrans(char const*); - -__END_DECLS diff --git a/Userland/Libraries/LibC/wstdio.cpp b/Userland/Libraries/LibC/wstdio.cpp deleted file mode 100644 index 61e59b33326..00000000000 --- a/Userland/Libraries/LibC/wstdio.cpp +++ /dev/null @@ -1,275 +0,0 @@ -/* - * Copyright (c) 2021, Ali Mohammad Pur - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -static_assert(AssertSize()); - -extern "C" { - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/fwide.html -int fwide(FILE*, int mode) -{ - // Nope Nope Nope. - return mode; -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/fgetwc.html -wint_t fgetwc(FILE* stream) -{ - VERIFY(stream); - Array underlying; - auto underlying_bytes = underlying.span(); - size_t encoded_length = 0; - size_t bytes_read = 0; - do { - size_t nread = fread(underlying_bytes.offset_pointer(bytes_read), 1, 1, stream); - if (nread != 1) { - errno = EILSEQ; - return WEOF; - } - ++bytes_read; - if (bytes_read == 1) { - if (underlying[0] >> 7 == 0) { - encoded_length = 1; - } else if (underlying[0] >> 5 == 6) { - encoded_length = 2; - } else if (underlying[0] >> 4 == 0xe) { - encoded_length = 3; - } else if (underlying[0] >> 3 == 0x1e) { - encoded_length = 4; - } else { - errno = EILSEQ; - return WEOF; - } - } - } while (bytes_read < encoded_length); - - wchar_t code_point; - auto read_bytes = mbrtowc(&code_point, bit_cast(underlying.data()), encoded_length, nullptr); - VERIFY(read_bytes == encoded_length); - return code_point; -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/getwc.html -wint_t getwc(FILE* stream) -{ - return fgetwc(stream); -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/getwchar.html -wint_t getwchar() -{ - return getwc(stdin); -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/fputwc.html -wint_t fputwc(wchar_t wc, FILE* stream) -{ - VERIFY(stream); - // Negative wide chars are weird - if constexpr (IsSigned) { - if (wc < 0) { - errno = EILSEQ; - return WEOF; - } - } - StringBuilder sb; - sb.append_code_point(static_cast(wc)); - auto bytes = sb.string_view().bytes(); - ScopedFileLock lock(stream); - size_t nwritten = stream->write(bytes.data(), bytes.size()); - if (nwritten < bytes.size()) - return WEOF; - return wc; -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/putwc.html -wint_t putwc(wchar_t wc, FILE* stream) -{ - return fputwc(wc, stream); -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/putwchar.html -wint_t putwchar(wchar_t wc) -{ - return fputwc(wc, stdout); -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/fgetws.html -wchar_t* fgetws(wchar_t* __restrict buffer, int size, FILE* __restrict stream) -{ - VERIFY(stream); - ScopedFileLock lock(stream); - bool ok = stream->gets(bit_cast(buffer), size); - return ok ? buffer : nullptr; -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/fputws.html -int fputws(wchar_t const* __restrict ws, FILE* __restrict stream) -{ - VERIFY(stream); - ScopedFileLock lock(stream); - int size = 0; - for (auto const* p = ws; *p != 0; ++p, ++size) { - if (putwc(*p, stream) == WEOF) - return WEOF; - } - return size; -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/ungetwc.html -wint_t ungetwc(wint_t wc, FILE* stream) -{ - VERIFY(stream); - ScopedFileLock lock(stream); - StringBuilder sb; - sb.append_code_point(static_cast(wc)); - auto bytes = sb.string_view().bytes(); - size_t ok_bytes = 0; - for (auto byte : bytes) { - if (!stream->ungetc(byte)) { - // Discard the half-ungotten bytes. - stream->read(const_cast(bytes.data()), ok_bytes); - return WEOF; - } - ++ok_bytes; - } - return wc; -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/wprintf.html -int wprintf(wchar_t const* __restrict format, ...) -{ - va_list ap; - va_start(ap, format); - auto rc = vfwprintf(stdout, format, ap); - va_end(ap); - return rc; -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/fwprintf.html -int fwprintf(FILE* __restrict stream, wchar_t const* __restrict format, ...) -{ - va_list ap; - va_start(ap, format); - auto rc = vfwprintf(stream, format, ap); - va_end(ap); - return rc; -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/swprintf.html -int swprintf(wchar_t* __restrict wcs, size_t max_length, wchar_t const* __restrict format, ...) -{ - va_list ap; - va_start(ap, format); - auto rc = vswprintf(wcs, max_length, format, ap); - va_end(ap); - return rc; -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/vwprintf.html -int vwprintf(wchar_t const* __restrict format, va_list args) -{ - return vfwprintf(stdout, format, args); -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/vfwprintf.html -int vfwprintf(FILE* __restrict stream, wchar_t const* __restrict format, va_list args) -{ - auto const* fmt = bit_cast(format); - return printf_internal([stream](wchar_t*&, wchar_t wc) { - putwc(wc, stream); - }, - nullptr, fmt, args); -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/vswprintf.html -int vswprintf(wchar_t* __restrict wcs, size_t max_length, wchar_t const* __restrict format, va_list args) -{ - auto const* fmt = bit_cast(format); - size_t length_so_far = 0; - printf_internal([max_length, &length_so_far](wchar_t*& buffer, wchar_t wc) { - if (length_so_far > max_length) - return; - *buffer++ = wc; - ++length_so_far; - }, - wcs, fmt, args); - if (length_so_far < max_length) - wcs[length_so_far] = L'\0'; - else - wcs[max_length - 1] = L'\0'; - return static_cast(length_so_far); -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/fwscanf.html -int fwscanf(FILE* __restrict stream, wchar_t const* __restrict format, ...) -{ - va_list ap; - va_start(ap, format); - auto rc = vfwscanf(stream, format, ap); - va_end(ap); - return rc; -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/swscanf.html -int swscanf(wchar_t const* __restrict ws, wchar_t const* __restrict format, ...) -{ - va_list ap; - va_start(ap, format); - auto rc = vswscanf(ws, format, ap); - va_end(ap); - return rc; -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/wscanf.html -int wscanf(wchar_t const* __restrict format, ...) -{ - va_list ap; - va_start(ap, format); - auto rc = vfwscanf(stdout, format, ap); - va_end(ap); - return rc; -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/vfwscanf.html -int vfwscanf(FILE* __restrict stream, wchar_t const* __restrict format, va_list arg) -{ - (void)stream; - (void)format; - (void)arg; - dbgln("FIXME: Implement vfwscanf()"); - TODO(); -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/vswscanf.html -int vswscanf(wchar_t const* __restrict ws, wchar_t const* __restrict format, va_list arg) -{ - (void)ws; - (void)format; - (void)arg; - dbgln("FIXME: Implement vswscanf()"); - TODO(); -} - -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/vwscanf.html -int vwscanf(wchar_t const* __restrict format, va_list arg) -{ - (void)format; - (void)arg; - dbgln("FIXME: Implement vwscanf()"); - TODO(); -} -} diff --git a/Userland/Libraries/LibCMake/CMakeCache/Lexer.cpp b/Userland/Libraries/LibCMake/CMakeCache/Lexer.cpp deleted file mode 100644 index b70e57502f5..00000000000 --- a/Userland/Libraries/LibCMake/CMakeCache/Lexer.cpp +++ /dev/null @@ -1,203 +0,0 @@ -/* - * Copyright (c) 2023, Sam Atkins - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "Lexer.h" -#include -#include -#include - -namespace CMake::Cache { - -static bool is_identifier_start_character(u32 c) -{ - return AK::is_ascii_alpha(c) || c == '_' || c == '-'; -} - -static bool is_identifier_character(u32 c) -{ - return AK::is_ascii_alphanumeric(c) || c == '_' || c == '-'; -} - -Lexer::Lexer(StringView input) - : GenericLexer(input) -{ -} - -ErrorOr> Lexer::lex(StringView input) -{ - Lexer lexer { input }; - return lexer.lex_file(); -} - -ErrorOr> Lexer::lex_file() -{ - ScopeLogger logger; - - while (!is_eof()) { - skip_whitespace(); - - if (is_eof()) - break; - - if (next_is('#')) { - consume_comment(); - continue; - } - - if (next_is("//"sv)) { - consume_help_text(); - continue; - } - - if (next_is(is_identifier_start_character)) { - consume_variable_definition(); - continue; - } - - consume_garbage(); - } - - return m_tokens; -} - -void Lexer::skip_whitespace() -{ - ScopeLogger log; - - while (!is_eof()) { - if (next_is('\n')) { - next_line(); - continue; - } - auto consumed = consume_while(AK::is_ascii_space); - if (consumed.is_empty()) - break; - } -} - -void Lexer::consume_comment() -{ - ScopeLogger log; - - auto start = position(); - VERIFY(consume_specific('#')); - auto comment = consume_until('\n'); - emit_token(Token::Type::Comment, comment, start, position()); -} - -void Lexer::consume_help_text() -{ - ScopeLogger log; - - auto start = position(); - VERIFY(consume_specific("//"sv)); - auto help_text = consume_until('\n'); - emit_token(Token::Type::HelpText, help_text, start, position()); -} - -void Lexer::consume_variable_definition() -{ - ScopeLogger log; - - consume_key(); - - if (!next_is(':')) { - consume_garbage(); - return; - } - consume_colon(); - - if (!next_is(is_identifier_start_character)) { - consume_garbage(); - return; - } - consume_type(); - - if (!next_is('=')) { - consume_garbage(); - return; - } - consume_equals(); - - consume_value(); -} - -void Lexer::consume_key() -{ - ScopeLogger log; - - auto start = position(); - auto key = consume_while(is_identifier_character); - emit_token(Token::Type::Key, key, start, position()); -} - -void Lexer::consume_colon() -{ - ScopeLogger log; - - auto start = position(); - VERIFY(consume_specific(':')); - emit_token(Token::Type::Colon, ":"sv, start, position()); -} - -void Lexer::consume_type() -{ - ScopeLogger log; - - auto start = position(); - auto type = consume_while(is_identifier_character); - emit_token(Token::Type::Type, type, start, position()); -} - -void Lexer::consume_equals() -{ - ScopeLogger log; - - auto start = position(); - VERIFY(consume_specific('=')); - emit_token(Token::Type::Colon, "="sv, start, position()); -} - -void Lexer::consume_value() -{ - ScopeLogger log; - - auto start = position(); - auto value = consume_until('\n'); - emit_token(Token::Type::Value, value, start, position()); -} - -void Lexer::consume_garbage() -{ - ScopeLogger log; - - auto start = position(); - auto garbage = consume_until('\n'); - emit_token(Token::Type::Garbage, garbage, start, position()); -} - -Position Lexer::position() const -{ - return Position { - .line = m_line, - .column = tell() - m_string_offset_after_previous_newline, - }; -} - -void Lexer::next_line() -{ - VERIFY(consume_specific('\n')); - m_string_offset_after_previous_newline = tell(); - m_line++; -} - -void Lexer::emit_token(Token::Type type, StringView value, Position start, Position end) -{ - dbgln_if(CMAKE_DEBUG, "Emitting {} token: `{}` ({}:{} to {}:{})", to_string(type), value, start.line, start.column, end.line, end.column); - m_tokens.empend(type, value, start, end); -} - -} diff --git a/Userland/Libraries/LibCMake/CMakeCache/Lexer.h b/Userland/Libraries/LibCMake/CMakeCache/Lexer.h deleted file mode 100644 index f258972fb84..00000000000 --- a/Userland/Libraries/LibCMake/CMakeCache/Lexer.h +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright (c) 2023, Sam Atkins - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include - -namespace CMake::Cache { - -class Lexer : private GenericLexer { -public: - static ErrorOr> lex(StringView input); - -private: - Lexer(StringView input); - - ErrorOr> lex_file(); - - void skip_whitespace(); - - void consume_comment(); - void consume_help_text(); - void consume_variable_definition(); - void consume_key(); - void consume_colon(); - void consume_type(); - void consume_equals(); - void consume_value(); - void consume_garbage(); - - Position position() const; - void next_line(); - - void emit_token(Token::Type, StringView value, Position start, Position end); - - Vector m_tokens; - size_t m_line { 0 }; - size_t m_string_offset_after_previous_newline { 0 }; -}; - -} diff --git a/Userland/Libraries/LibCMake/CMakeCache/SyntaxHighlighter.cpp b/Userland/Libraries/LibCMake/CMakeCache/SyntaxHighlighter.cpp deleted file mode 100644 index b04df0be4b1..00000000000 --- a/Userland/Libraries/LibCMake/CMakeCache/SyntaxHighlighter.cpp +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Copyright (c) 2023, Sam Atkins - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "SyntaxHighlighter.h" -#include - -namespace CMake::Cache { - -static Gfx::TextAttributes style_for_token_type(Gfx::Palette const& palette, Token::Type type) -{ - switch (type) { - case Token::Type::Comment: - case Token::Type::HelpText: - return { palette.syntax_comment() }; - case Token::Type::Key: - return { palette.syntax_identifier() }; - case Token::Type::Type: - return { palette.syntax_type() }; - case Token::Type::Colon: - case Token::Type::Equals: - return { palette.syntax_punctuation() }; - case Token::Type::Value: - return { palette.syntax_string() }; - case Token::Type::Garbage: - return { palette.red(), {}, false, Gfx::TextAttributes::UnderlineStyle::Wavy, palette.red() }; - default: - return { palette.base_text() }; - } -} - -bool SyntaxHighlighter::is_identifier(u64 token_type) const -{ - auto cmake_token = static_cast(token_type); - return cmake_token == Token::Type::Key; -} - -void SyntaxHighlighter::rehighlight(Gfx::Palette const& palette) -{ - auto text = m_client->get_text(); - auto tokens = Lexer::lex(text).release_value_but_fixme_should_propagate_errors(); - - Vector spans; - auto highlight_span = [&](Token::Type type, Position const& start, Position const& end) { - Syntax::TextDocumentSpan span; - span.range.set_start({ start.line, start.column }); - span.range.set_end({ end.line, end.column }); - if (!span.range.is_valid()) - return; - - span.attributes = style_for_token_type(palette, type); - span.is_skippable = false; - span.data = static_cast(type); - spans.append(move(span)); - }; - - for (auto const& token : tokens) { - highlight_span(token.type, token.start, token.end); - } - - m_client->do_set_spans(move(spans)); - - m_has_brace_buddies = false; - highlight_matching_token_pair(); - - m_client->do_update(); -} - -Vector SyntaxHighlighter::matching_token_pairs_impl() const -{ - static Vector empty; - return empty; -} - -bool SyntaxHighlighter::token_types_equal(u64 token1, u64 token2) const -{ - return static_cast(token1) == static_cast(token2); -} - -} diff --git a/Userland/Libraries/LibCMake/CMakeCache/SyntaxHighlighter.h b/Userland/Libraries/LibCMake/CMakeCache/SyntaxHighlighter.h deleted file mode 100644 index 62c7d546107..00000000000 --- a/Userland/Libraries/LibCMake/CMakeCache/SyntaxHighlighter.h +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright (c) 2023, Sam Atkins - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include - -namespace CMake::Cache { - -class SyntaxHighlighter final : public Syntax::Highlighter { -public: - SyntaxHighlighter() = default; - virtual ~SyntaxHighlighter() override = default; - - virtual bool is_identifier(u64) const override; - - virtual Syntax::Language language() const override { return Syntax::Language::CMakeCache; } - virtual Optional comment_prefix() const override { return "#"sv; } - virtual Optional comment_suffix() const override { return {}; } - virtual void rehighlight(Palette const&) override; - -protected: - virtual Vector matching_token_pairs_impl() const override; - virtual bool token_types_equal(u64, u64) const override; -}; - -} diff --git a/Userland/Libraries/LibCMake/CMakeCache/Token.h b/Userland/Libraries/LibCMake/CMakeCache/Token.h deleted file mode 100644 index 35f84d970f5..00000000000 --- a/Userland/Libraries/LibCMake/CMakeCache/Token.h +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright (c) 2023, Sam Atkins - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include - -namespace CMake::Cache { - -struct Token { - enum class Type { - Comment, - HelpText, - Key, - Colon, - Type, - Equals, - Value, - Garbage, - }; - Type type; - StringView value; - - Position start; - Position end; -}; - -static constexpr StringView to_string(Token::Type type) -{ - switch (type) { - case Token::Type::Comment: - return "Comment"sv; - case Token::Type::HelpText: - return "HelpText"sv; - case Token::Type::Key: - return "Key"sv; - case Token::Type::Colon: - return "Colon"sv; - case Token::Type::Type: - return "Type"sv; - case Token::Type::Equals: - return "Equals"sv; - case Token::Type::Value: - return "Value"sv; - case Token::Type::Garbage: - return "Garbage"sv; - } - - VERIFY_NOT_REACHED(); -} - -} diff --git a/Userland/Libraries/LibCMake/CMakeLists.txt b/Userland/Libraries/LibCMake/CMakeLists.txt deleted file mode 100644 index 6f07e1a3ddb..00000000000 --- a/Userland/Libraries/LibCMake/CMakeLists.txt +++ /dev/null @@ -1,10 +0,0 @@ -set(SOURCES - CMakeCache/Lexer.cpp - CMakeCache/SyntaxHighlighter.cpp - Lexer.cpp - SyntaxHighlighter.cpp - Token.cpp -) - -serenity_lib(LibCMake cmake) -target_link_libraries(LibCMake PRIVATE LibSyntax) diff --git a/Userland/Libraries/LibCMake/Lexer.cpp b/Userland/Libraries/LibCMake/Lexer.cpp deleted file mode 100644 index 72b467fc340..00000000000 --- a/Userland/Libraries/LibCMake/Lexer.cpp +++ /dev/null @@ -1,371 +0,0 @@ -/* - * Copyright (c) 2023, Sam Atkins - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "Lexer.h" -#include -#include -#include -#include - -namespace CMake { - -static bool is_valid_identifier_initial_char(char c) -{ - return is_ascii_alpha(c) || c == '_'; -} - -static bool is_valid_identifier_char(char c) -{ - return is_ascii_alphanumeric(c) || c == '_'; -} - -ErrorOr> Lexer::lex(StringView input) -{ - Lexer lexer { input }; - return lexer.lex_file(); -} - -Lexer::Lexer(StringView input) - : GenericLexer(input) -{ -} - -ErrorOr> Lexer::lex_file() -{ - m_tokens.clear_with_capacity(); - - while (!is_eof()) { - consume_whitespace_or_comments(); - - if (is_eof()) - break; - - if (is_valid_identifier_initial_char(peek())) { - consume_command_invocation(); - } else { - consume_garbage(); - } - } - - return m_tokens; -} - -void Lexer::skip_whitespace() -{ - while (!is_eof()) { - if (next_is('\n')) { - next_line(); - continue; - } - auto consumed = consume_while([&](char c) { - return c == ' ' || c == '\t'; - }); - if (consumed.is_empty()) - break; - } -} - -void Lexer::consume_whitespace_or_comments() -{ - ScopeLogger log; - while (!is_eof()) { - skip_whitespace(); - - if (next_is('#')) { - consume_comment(); - } else { - break; - } - } -} - -// https://cmake.org/cmake/help/latest/manual/cmake-language.7.html#command-invocations -void Lexer::consume_command_invocation() -{ - ScopeLogger log; - auto identifier_start = position(); - auto identifier = consume_while(is_valid_identifier_char); - auto control_keyword = control_keyword_from_string(identifier); - if (control_keyword.has_value()) { - emit_token(Token::Type::ControlKeyword, identifier, identifier_start, position(), control_keyword.release_value()); - } else { - emit_token(Token::Type::Identifier, identifier, identifier_start, position()); - } - - consume_whitespace_or_comments(); - - if (next_is('(')) - consume_open_paren(); - - consume_arguments(); - - if (next_is(')')) - consume_close_paren(); -} - -void Lexer::consume_arguments() -{ - ScopeLogger log; - while (!is_eof()) { - consume_whitespace_or_comments(); - - if (next_is('(')) { - consume_open_paren(); - - consume_whitespace_or_comments(); - consume_arguments(); - consume_whitespace_or_comments(); - - if (next_is(')')) - consume_close_paren(); - - continue; - } - - if (next_is(')')) - return; - - consume_argument(); - } -} - -// https://cmake.org/cmake/help/latest/manual/cmake-language.7.html#command-arguments -void Lexer::consume_argument() -{ - ScopeLogger log; - consume_whitespace_or_comments(); - - if (next_is('[')) { - consume_bracket_argument(); - return; - } - - if (next_is('"')) { - consume_quoted_argument(); - return; - } - - consume_unquoted_argument(); -} - -// https://cmake.org/cmake/help/latest/manual/cmake-language.7.html#bracket-argument -void Lexer::consume_bracket_argument() -{ - ScopeLogger log; - auto start = position(); - auto value = read_bracket_argument(); - emit_token(Token::Type::BracketArgument, value, start, position()); -} - -// https://cmake.org/cmake/help/latest/manual/cmake-language.7.html#quoted-argument -void Lexer::consume_quoted_argument() -{ - ScopeLogger log; - auto start = position(); - auto start_offset = tell(); - - VERIFY(consume_specific('"')); - while (!is_eof()) { - if (next_is('"')) { - ignore(); - break; - } - - if (next_is("\\\""sv)) { - ignore(2); - continue; - } - - if (next_is('\n')) { - next_line(); - continue; - } - - ignore(); - } - - auto whole_token = m_input.substring_view(start_offset, tell() - start_offset); - auto value = whole_token.substring_view(1, whole_token.length() - 2); - auto variable_references = parse_variable_references_from_argument(whole_token, start); - emit_token(Token::Type::QuotedArgument, value, start, position(), {}, move(variable_references)); -} - -// https://cmake.org/cmake/help/latest/manual/cmake-language.7.html#unquoted-argument -void Lexer::consume_unquoted_argument() -{ - ScopeLogger log; - auto start_offset = tell(); - auto start = position(); - - while (!is_eof()) { - if (next_is('\\')) { - consume_escaped_character('\\'); - continue; - } - - auto consumed = consume_until([](char c) { return is_ascii_space(c) || "()#\"\\'"sv.contains(c); }); - if (consumed.is_empty()) - break; - - // FIXME: `unquoted_legacy` - } - - auto value = m_input.substring_view(start_offset, tell() - start_offset); - auto variable_references = parse_variable_references_from_argument(value, start); - emit_token(Token::Type::UnquotedArgument, value, start, position(), {}, move(variable_references)); -} - -// https://cmake.org/cmake/help/latest/manual/cmake-language.7.html#comments -void Lexer::consume_comment() -{ - ScopeLogger log; - auto start = position(); - - VERIFY(consume_specific('#')); - if (next_is('[')) { - // Bracket comment - // https://cmake.org/cmake/help/latest/manual/cmake-language.7.html#bracket-comment - auto comment = read_bracket_argument(); - emit_token(Token::Type::BracketComment, comment, start, position()); - return; - } - - // Line comment - // https://cmake.org/cmake/help/latest/manual/cmake-language.7.html#line-comment - auto comment = consume_until('\n'); - emit_token(Token::Type::LineComment, comment, start, position()); -} - -void Lexer::consume_open_paren() -{ - auto start = position(); - VERIFY(consume_specific('(')); - emit_token(Token::Type::OpenParen, "("sv, start, position()); -} - -void Lexer::consume_close_paren() -{ - auto start = position(); - VERIFY(consume_specific(')')); - emit_token(Token::Type::CloseParen, ")"sv, start, position()); -} - -void Lexer::consume_garbage() -{ - ScopeLogger log; - auto start = position(); - auto contents = consume_until(is_ascii_space); - if (!contents.is_empty()) - emit_token(Token::Type::Garbage, contents, start, position()); -} - -// https://cmake.org/cmake/help/latest/manual/cmake-language.7.html#bracket-argument -// Used by both bracket arguments and bracket comments. -StringView Lexer::read_bracket_argument() -{ - VERIFY(consume_specific('[')); - auto leading_equals_signs = consume_while([](char c) { return c == '='; }); - consume_specific('['); - auto start = tell(); - auto end = start; - while (!is_eof()) { - // Read everything until we see `]={len}]`. - ignore_until(']'); - end = tell(); - ignore(); - if (next_is(leading_equals_signs)) - ignore(leading_equals_signs.length()); - if (consume_specific(']')) - break; - } - - return m_input.substring_view(start, end - start); -} - -// https://cmake.org/cmake/help/latest/manual/cmake-language.7.html#variable-references -Vector Lexer::parse_variable_references_from_argument(StringView argument_value, Position argument_start) -{ - auto position = argument_start; - GenericLexer lexer { argument_value }; - Vector variable_references; - - while (!lexer.is_eof()) { - if (lexer.next_is('\n')) { - lexer.ignore(); - position.column = 0; - position.line++; - continue; - } - - if (lexer.next_is('\\')) { - lexer.ignore(); - if (lexer.next_is('\n')) { - lexer.ignore(); - position.column = 0; - position.line++; - continue; - } - lexer.ignore(); - position.column += 2; - } - - if (lexer.next_is('$')) { - auto start = position; - lexer.ignore(); - position.column++; - - if (lexer.next_is("ENV{"sv)) { - lexer.ignore(4); - position.column += 4; - } else if (lexer.next_is('{')) { - lexer.ignore(); - position.column++; - } else { - auto skipped = lexer.consume_until(is_any_of("$ \n"sv)); - position.column += skipped.length(); - continue; - } - - auto variable_name = lexer.consume_until(is_any_of("} \n"sv)); - position.column += variable_name.length(); - if (lexer.next_is('}')) { - lexer.ignore(); - position.column++; - variable_references.empend(variable_name, start, position); - } - - continue; - } - - lexer.ignore(); - position.column++; - } - - return variable_references; -} - -Position Lexer::position() const -{ - return Position { - .line = m_line, - .column = tell() - m_string_offset_after_previous_newline, - }; -} - -void Lexer::next_line() -{ - VERIFY(consume_specific('\n')); - m_string_offset_after_previous_newline = tell(); - m_line++; -} - -void Lexer::emit_token(Token::Type type, StringView value, Position start, Position end, Optional control_keyword, Vector variable_references) -{ - dbgln_if(CMAKE_DEBUG, "Emitting {} token: `{}` ({}:{} to {}:{})", to_string(type), value, start.line, start.column, end.line, end.column); - m_tokens.empend(type, value, start, end, move(control_keyword), move(variable_references)); -} - -} diff --git a/Userland/Libraries/LibCMake/Lexer.h b/Userland/Libraries/LibCMake/Lexer.h deleted file mode 100644 index 833623439ab..00000000000 --- a/Userland/Libraries/LibCMake/Lexer.h +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright (c) 2023, Sam Atkins - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include - -namespace CMake { - -class Lexer : private GenericLexer { -public: - static ErrorOr> lex(StringView input); - -private: - Lexer(StringView input); - - ErrorOr> lex_file(); - - void skip_whitespace(); - - void consume_whitespace_or_comments(); - void consume_command_invocation(); - void consume_arguments(); - void consume_argument(); - void consume_bracket_argument(); - void consume_quoted_argument(); - void consume_unquoted_argument(); - void consume_comment(); - void consume_open_paren(); - void consume_close_paren(); - void consume_garbage(); - - StringView read_bracket_argument(); - static Vector parse_variable_references_from_argument(StringView argument_value, Position argument_start); - - Position position() const; - void next_line(); - - void emit_token(Token::Type, StringView value, Position start, Position end, Optional = {}, Vector = {}); - - Vector m_tokens; - size_t m_line { 0 }; - size_t m_string_offset_after_previous_newline { 0 }; -}; - -} diff --git a/Userland/Libraries/LibCMake/Position.h b/Userland/Libraries/LibCMake/Position.h deleted file mode 100644 index 1870cfffe9e..00000000000 --- a/Userland/Libraries/LibCMake/Position.h +++ /dev/null @@ -1,18 +0,0 @@ -/* - * Copyright (c) 2023, Sam Atkins - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include - -namespace CMake { - -struct Position { - size_t line { 0 }; - size_t column { 0 }; -}; - -} diff --git a/Userland/Libraries/LibCMake/SyntaxHighlighter.cpp b/Userland/Libraries/LibCMake/SyntaxHighlighter.cpp deleted file mode 100644 index 11071d4b6f1..00000000000 --- a/Userland/Libraries/LibCMake/SyntaxHighlighter.cpp +++ /dev/null @@ -1,211 +0,0 @@ -/* - * Copyright (c) 2023, Sam Atkins - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "SyntaxHighlighter.h" -#include -#include - -namespace CMake { - -static Gfx::TextAttributes style_for_token_type(Gfx::Palette const& palette, Token::Type type) -{ - switch (type) { - case Token::Type::BracketComment: - case Token::Type::LineComment: - return { palette.syntax_comment() }; - case Token::Type::Identifier: - return { palette.syntax_function() }; - case Token::Type::ControlKeyword: - return { palette.syntax_control_keyword() }; - case Token::Type::OpenParen: - case Token::Type::CloseParen: - return { palette.syntax_punctuation() }; - case Token::Type::BracketArgument: - return { palette.syntax_parameter() }; - case Token::Type::QuotedArgument: - return { palette.syntax_string() }; - case Token::Type::UnquotedArgument: - return { palette.syntax_parameter() }; - case Token::Type::Garbage: - return { palette.red(), {}, false, Gfx::TextAttributes::UnderlineStyle::Wavy, palette.red() }; - case Token::Type::VariableReference: - // This is a bit arbitrary, since we don't have a color specifically for this. - return { palette.syntax_preprocessor_value() }; - default: - return { palette.base_text() }; - } -} - -bool SyntaxHighlighter::is_identifier(u64 token_type) const -{ - auto cmake_token = static_cast(token_type); - return cmake_token == Token::Type::Identifier; -} - -void SyntaxHighlighter::rehighlight(Gfx::Palette const& palette) -{ - auto text = m_client->get_text(); - auto tokens = Lexer::lex(text).release_value_but_fixme_should_propagate_errors(); - auto& document = m_client->get_document(); - - struct OpenBlock { - Token token; - int open_paren_count { 0 }; - Optional ending_paren {}; - }; - Vector open_blocks; - Vector folding_regions; - Vector spans; - auto highlight_span = [&](Token::Type type, Position const& start, Position const& end) { - Syntax::TextDocumentSpan span; - span.range.set_start({ start.line, start.column }); - span.range.set_end({ end.line, end.column }); - if (!span.range.is_valid()) - return; - - span.attributes = style_for_token_type(palette, type); - span.is_skippable = false; - span.data = static_cast(type); - spans.append(move(span)); - }; - - auto create_region_from_block_type = [&](auto control_keywords, Token const& end_token) { - if (open_blocks.is_empty()) - return; - - // Find the most recent open block with a matching keyword. - Optional found_index; - OpenBlock open_block; - for (int i = open_blocks.size() - 1; i >= 0; i--) { - for (auto value : control_keywords) { - if (open_blocks[i].token.control_keyword == value) { - found_index = i; - open_block = open_blocks[i]; - break; - } - } - if (found_index.has_value()) - break; - } - - if (found_index.has_value()) { - // Remove the found token and all after it. - open_blocks.shrink(found_index.value()); - - // Create a region. - Syntax::TextDocumentFoldingRegion region; - if (open_block.ending_paren.has_value()) { - region.range.set_start({ open_block.ending_paren->end.line, open_block.ending_paren->end.column }); - } else { - // The opening command is invalid, it does not have a closing paren. - // So, we just start the region at the end of the line where the command identifier was. (eg, `if`) - region.range.set_start({ open_block.token.end.line, document.line(open_block.token.end.line).last_non_whitespace_column().value() }); - } - region.range.set_end({ end_token.start.line, end_token.start.column }); - folding_regions.append(move(region)); - } - }; - - for (auto const& token : tokens) { - if (token.type == Token::Type::QuotedArgument || token.type == Token::Type::UnquotedArgument) { - // Alternately highlight the regular/variable-reference parts. - // 0-length ranges are caught in highlight_span() so we don't have to worry about them. - Position previous_position = token.start; - for (auto const& reference : token.variable_references) { - highlight_span(token.type, previous_position, reference.start); - highlight_span(Token::Type::VariableReference, reference.start, reference.end); - previous_position = reference.end; - } - highlight_span(token.type, previous_position, token.end); - continue; - } - - highlight_span(token.type, token.start, token.end); - - if (!open_blocks.is_empty() && !open_blocks.last().ending_paren.has_value()) { - auto& open_block = open_blocks.last(); - if (token.type == Token::Type::OpenParen) { - open_block.open_paren_count++; - } else if (token.type == Token::Type::CloseParen) { - open_block.open_paren_count--; - if (open_block.open_paren_count == 0) - open_block.ending_paren = token; - } - } - - // Create folding regions from control-keyword blocks. - if (token.type == Token::Type::ControlKeyword) { - switch (token.control_keyword.value()) { - case ControlKeywordType::If: - open_blocks.empend(token); - break; - case ControlKeywordType::ElseIf: - case ControlKeywordType::Else: - create_region_from_block_type(Array { ControlKeywordType::If, ControlKeywordType::ElseIf }, token); - open_blocks.empend(token); - break; - case ControlKeywordType::EndIf: - create_region_from_block_type(Array { ControlKeywordType::If, ControlKeywordType::ElseIf, ControlKeywordType::Else }, token); - break; - case ControlKeywordType::ForEach: - open_blocks.empend(token); - break; - case ControlKeywordType::EndForEach: - create_region_from_block_type(Array { ControlKeywordType::ForEach }, token); - break; - case ControlKeywordType::While: - open_blocks.empend(token); - break; - case ControlKeywordType::EndWhile: - create_region_from_block_type(Array { ControlKeywordType::While }, token); - break; - case ControlKeywordType::Macro: - open_blocks.empend(token); - break; - case ControlKeywordType::EndMacro: - create_region_from_block_type(Array { ControlKeywordType::Macro }, token); - break; - case ControlKeywordType::Function: - open_blocks.empend(token); - break; - case ControlKeywordType::EndFunction: - create_region_from_block_type(Array { ControlKeywordType::Function }, token); - break; - case ControlKeywordType::Block: - open_blocks.empend(token); - break; - case ControlKeywordType::EndBlock: - create_region_from_block_type(Array { ControlKeywordType::Block }, token); - break; - default: - break; - } - } - } - m_client->do_set_spans(move(spans)); - m_client->do_set_folding_regions(move(folding_regions)); - - m_has_brace_buddies = false; - highlight_matching_token_pair(); - - m_client->do_update(); -} - -Vector SyntaxHighlighter::matching_token_pairs_impl() const -{ - static Vector pairs; - if (pairs.is_empty()) { - pairs.append({ static_cast(Token::Type::OpenParen), static_cast(Token::Type::CloseParen) }); - } - return pairs; -} - -bool SyntaxHighlighter::token_types_equal(u64 token1, u64 token2) const -{ - return static_cast(token1) == static_cast(token2); -} - -} diff --git a/Userland/Libraries/LibCMake/SyntaxHighlighter.h b/Userland/Libraries/LibCMake/SyntaxHighlighter.h deleted file mode 100644 index 74aa4fd17fd..00000000000 --- a/Userland/Libraries/LibCMake/SyntaxHighlighter.h +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright (c) 2023, Sam Atkins - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include - -namespace CMake { - -class SyntaxHighlighter final : public Syntax::Highlighter { -public: - SyntaxHighlighter() = default; - virtual ~SyntaxHighlighter() override = default; - - virtual bool is_identifier(u64) const override; - - virtual Syntax::Language language() const override { return Syntax::Language::CMake; } - virtual Optional comment_prefix() const override { return "#"sv; } - virtual Optional comment_suffix() const override { return {}; } - virtual void rehighlight(Palette const&) override; - -protected: - virtual Vector matching_token_pairs_impl() const override; - virtual bool token_types_equal(u64, u64) const override; -}; - -} diff --git a/Userland/Libraries/LibCMake/Token.cpp b/Userland/Libraries/LibCMake/Token.cpp deleted file mode 100644 index 7cc30ca0710..00000000000 --- a/Userland/Libraries/LibCMake/Token.cpp +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright (c) 2023, Sam Atkins - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "Token.h" - -namespace CMake { - -Optional control_keyword_from_string(StringView value) -{ - if (value.equals_ignoring_ascii_case("if"sv)) - return ControlKeywordType::If; - if (value.equals_ignoring_ascii_case("elseif"sv)) - return ControlKeywordType::ElseIf; - if (value.equals_ignoring_ascii_case("else"sv)) - return ControlKeywordType::Else; - if (value.equals_ignoring_ascii_case("endif"sv)) - return ControlKeywordType::EndIf; - if (value.equals_ignoring_ascii_case("foreach"sv)) - return ControlKeywordType::ForEach; - if (value.equals_ignoring_ascii_case("endforeach"sv)) - return ControlKeywordType::EndForEach; - if (value.equals_ignoring_ascii_case("while"sv)) - return ControlKeywordType::While; - if (value.equals_ignoring_ascii_case("endwhile"sv)) - return ControlKeywordType::EndWhile; - if (value.equals_ignoring_ascii_case("break"sv)) - return ControlKeywordType::Break; - if (value.equals_ignoring_ascii_case("continue"sv)) - return ControlKeywordType::Continue; - if (value.equals_ignoring_ascii_case("return"sv)) - return ControlKeywordType::Return; - if (value.equals_ignoring_ascii_case("macro"sv)) - return ControlKeywordType::Macro; - if (value.equals_ignoring_ascii_case("endmacro"sv)) - return ControlKeywordType::EndMacro; - if (value.equals_ignoring_ascii_case("function"sv)) - return ControlKeywordType::Function; - if (value.equals_ignoring_ascii_case("endfunction"sv)) - return ControlKeywordType::EndFunction; - if (value.equals_ignoring_ascii_case("block"sv)) - return ControlKeywordType::Block; - if (value.equals_ignoring_ascii_case("endblock"sv)) - return ControlKeywordType::EndBlock; - return {}; -} - -} diff --git a/Userland/Libraries/LibCMake/Token.h b/Userland/Libraries/LibCMake/Token.h deleted file mode 100644 index ca45a319459..00000000000 --- a/Userland/Libraries/LibCMake/Token.h +++ /dev/null @@ -1,101 +0,0 @@ -/* - * Copyright (c) 2023, Sam Atkins - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include - -namespace CMake { - -struct VariableReference { - StringView value; - Position start; - Position end; -}; - -enum class ControlKeywordType { - If, - ElseIf, - Else, - EndIf, - ForEach, - EndForEach, - While, - EndWhile, - Break, - Continue, - Return, - Macro, - EndMacro, - Function, - EndFunction, - Block, - EndBlock, -}; - -struct Token { - enum class Type { - BracketComment, - LineComment, - Identifier, - ControlKeyword, - OpenParen, - CloseParen, - BracketArgument, - QuotedArgument, - UnquotedArgument, - Garbage, - - // These are elements inside argument tokens - VariableReference, - }; - - Type type; - StringView value; - - Position start; - Position end; - - // Type-specific - Optional control_keyword {}; - Vector variable_references {}; -}; - -static constexpr StringView to_string(Token::Type type) -{ - switch (type) { - case Token::Type::BracketComment: - return "BracketComment"sv; - case Token::Type::LineComment: - return "LineComment"sv; - case Token::Type::Identifier: - return "Identifier"sv; - case Token::Type::ControlKeyword: - return "ControlKeyword"sv; - case Token::Type::OpenParen: - return "OpenParen"sv; - case Token::Type::CloseParen: - return "CloseParen"sv; - case Token::Type::BracketArgument: - return "BracketArgument"sv; - case Token::Type::QuotedArgument: - return "QuotedArgument"sv; - case Token::Type::UnquotedArgument: - return "UnquotedArgument"sv; - case Token::Type::Garbage: - return "Garbage"sv; - case Token::Type::VariableReference: - return "VariableReference"sv; - } - - VERIFY_NOT_REACHED(); -} - -Optional control_keyword_from_string(StringView value); - -} diff --git a/Userland/Libraries/LibCards/CMakeLists.txt b/Userland/Libraries/LibCards/CMakeLists.txt deleted file mode 100644 index 70c6d949c3d..00000000000 --- a/Userland/Libraries/LibCards/CMakeLists.txt +++ /dev/null @@ -1,9 +0,0 @@ -set(SOURCES - Card.cpp - CardGame.cpp - CardPainter.cpp - CardStack.cpp -) - -serenity_lib(LibCards cards) -target_link_libraries(LibCards PRIVATE LibCore LibConfig LibGfx LibGUI) diff --git a/Userland/Libraries/LibCards/Card.cpp b/Userland/Libraries/LibCards/Card.cpp deleted file mode 100644 index fef9afe05e5..00000000000 --- a/Userland/Libraries/LibCards/Card.cpp +++ /dev/null @@ -1,94 +0,0 @@ -/* - * Copyright (c) 2020, Till Mayer - * Copyright (c) 2022, the SerenityOS developers. - * Copyright (c) 2022, Sam Atkins - * Copyright (c) 2023, David Ganz - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "Card.h" -#include -#include - -namespace Cards { - -Card::Card(Suit suit, Rank rank) - : m_rect(Gfx::IntRect({}, { width, height })) - , m_suit(suit) - , m_rank(rank) -{ - VERIFY(to_underlying(rank) < card_count); -} - -void Card::paint(GUI::Painter& painter, bool highlighted) const -{ - VERIFY(!(highlighted && m_disabled)); - - auto& card_painter = CardPainter::the(); - auto bitmap = [&]() { - if (m_inverted) - return m_upside_down ? card_painter.card_back_inverted() : card_painter.card_front_inverted(m_suit, m_rank); - if (highlighted) { - VERIFY(!m_upside_down); - return card_painter.card_front_highlighted(m_suit, m_rank); - } - if (m_disabled) { - return m_upside_down ? card_painter.card_back_disabled() : card_painter.card_front_disabled(m_suit, m_rank); - } - return m_upside_down ? card_painter.card_back() : card_painter.card_front(m_suit, m_rank); - }(); - painter.blit(position(), bitmap, bitmap->rect()); -} - -void Card::clear(GUI::Painter& painter, Color background_color) const -{ - painter.fill_rect({ old_position(), { width, height } }, background_color); -} - -void Card::save_old_position() -{ - m_old_position = m_rect.location(); - m_old_position_valid = true; -} - -void Card::clear_and_paint(GUI::Painter& painter, Color background_color, bool highlighted) -{ - if (is_old_position_valid()) - clear(painter, background_color); - - paint(painter, highlighted); - save_old_position(); -} - -ErrorOr>> create_standard_deck(Shuffle shuffle) -{ - return create_deck(1, 1, 1, 1, shuffle); -} - -ErrorOr>> create_deck(unsigned full_club_suit_count, unsigned full_diamond_suit_count, unsigned full_heart_suit_count, unsigned full_spade_suit_count, Shuffle shuffle) -{ - Vector> deck; - TRY(deck.try_ensure_capacity(Card::card_count * (full_club_suit_count + full_diamond_suit_count + full_heart_suit_count + full_spade_suit_count))); - - auto add_cards_for_suit = [&deck](Cards::Suit suit, unsigned number_of_suits) -> ErrorOr { - for (auto i = 0u; i < number_of_suits; ++i) { - for (auto rank = 0; rank < Card::card_count; ++rank) { - deck.unchecked_append(TRY(Card::try_create(suit, static_cast(rank)))); - } - } - return {}; - }; - - TRY(add_cards_for_suit(Cards::Suit::Clubs, full_club_suit_count)); - TRY(add_cards_for_suit(Cards::Suit::Diamonds, full_diamond_suit_count)); - TRY(add_cards_for_suit(Cards::Suit::Hearts, full_heart_suit_count)); - TRY(add_cards_for_suit(Cards::Suit::Spades, full_spade_suit_count)); - - if (shuffle == Shuffle::Yes) - AK::shuffle(deck); - - return deck; -} - -} diff --git a/Userland/Libraries/LibCards/Card.h b/Userland/Libraries/LibCards/Card.h deleted file mode 100644 index d126c39e23b..00000000000 --- a/Userland/Libraries/LibCards/Card.h +++ /dev/null @@ -1,168 +0,0 @@ -/* - * Copyright (c) 2020, Till Mayer - * Copyright (c) 2022, the SerenityOS developers. - * Copyright (c) 2022, Sam Atkins - * Copyright (c) 2023, David Ganz - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include -#include - -namespace Cards { - -enum class Rank : u8 { - Ace, - Two, - Three, - Four, - Five, - Six, - Seven, - Eight, - Nine, - Ten, - Jack, - Queen, - King, - __Count -}; - -constexpr StringView card_rank_label(Rank rank) -{ - switch (rank) { - case Rank::Ace: - return "A"sv; - case Rank::Two: - return "2"sv; - case Rank::Three: - return "3"sv; - case Rank::Four: - return "4"sv; - case Rank::Five: - return "5"sv; - case Rank::Six: - return "6"sv; - case Rank::Seven: - return "7"sv; - case Rank::Eight: - return "8"sv; - case Rank::Nine: - return "9"sv; - case Rank::Ten: - return "10"sv; - case Rank::Jack: - return "J"sv; - case Rank::Queen: - return "Q"sv; - case Rank::King: - return "K"sv; - case Rank::__Count: - VERIFY_NOT_REACHED(); - } - VERIFY_NOT_REACHED(); -} - -enum class Suit : u8 { - Clubs, - Diamonds, - Spades, - Hearts, - __Count -}; - -class Card final : public Core::EventReceiver { - C_OBJECT(Card) -public: - static constexpr int width = 80; - static constexpr int height = 110; - static constexpr int card_count = to_underlying(Rank::__Count); - static constexpr int card_radius = 7; - - virtual ~Card() override = default; - - Gfx::IntRect& rect() { return m_rect; } - Gfx::IntRect const& rect() const { return m_rect; } - Gfx::IntPoint position() const { return m_rect.location(); } - Gfx::IntPoint old_position() const { return m_old_position; } - Rank rank() const { return m_rank; } - Suit suit() const { return m_suit; } - - bool is_old_position_valid() const { return m_old_position_valid; } - bool is_moving() const { return m_moving; } - bool is_upside_down() const { return m_upside_down; } - bool is_inverted() const { return m_inverted; } - bool is_previewed() const { return m_previewed; } - bool is_disabled() const { return m_disabled; } - Gfx::Color color() const { return (m_suit == Suit::Diamonds || m_suit == Suit::Hearts) ? Color::Red : Color::Black; } - - void set_position(Gfx::IntPoint const p) { m_rect.set_location(p); } - void set_moving(bool moving) { m_moving = moving; } - void set_upside_down(bool upside_down) { m_upside_down = upside_down; } - void set_inverted(bool inverted) { m_inverted = inverted; } - void set_previewed(bool previewed) { m_previewed = previewed; } - void set_disabled(bool disabled) { m_disabled = disabled; } - - void save_old_position(); - - void paint(GUI::Painter&, bool highlighted = false) const; - void clear(GUI::Painter&, Color background_color) const; - void clear_and_paint(GUI::Painter& painter, Color background_color, bool highlighted); - -private: - Card(Suit, Rank); - - Gfx::IntRect m_rect; - Gfx::IntPoint m_old_position; - Suit m_suit; - Rank m_rank; - bool m_old_position_valid { false }; - bool m_moving { false }; - bool m_upside_down { false }; - bool m_inverted { false }; - bool m_previewed { false }; - bool m_disabled { false }; -}; - -enum class Shuffle { - No, - Yes, -}; -ErrorOr>> create_standard_deck(Shuffle); -ErrorOr>> create_deck(unsigned full_club_suit_count, unsigned full_diamond_suit_count, unsigned full_heart_suit_count, unsigned full_spade_suit_count, Shuffle); - -} - -template<> -struct AK::Formatter : Formatter { - ErrorOr format(FormatBuilder& builder, Cards::Card const& card) - { - StringView suit; - - switch (card.suit()) { - case Cards::Suit::Clubs: - suit = "C"sv; - break; - case Cards::Suit::Diamonds: - suit = "D"sv; - break; - case Cards::Suit::Hearts: - suit = "H"sv; - break; - case Cards::Suit::Spades: - suit = "S"sv; - break; - default: - VERIFY_NOT_REACHED(); - } - - return Formatter::format(builder, "{:>2}{}"sv, Cards::card_rank_label(card.rank()), suit); - } -}; diff --git a/Userland/Libraries/LibCards/CardGame.cpp b/Userland/Libraries/LibCards/CardGame.cpp deleted file mode 100644 index 96f6377ee7f..00000000000 --- a/Userland/Libraries/LibCards/CardGame.cpp +++ /dev/null @@ -1,168 +0,0 @@ -/* - * Copyright (c) 2020, Till Mayer - * Copyright (c) 2022, Sam Atkins - * Copyright (c) 2022, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "CardGame.h" -#include -#include -#include -#include -#include -#include - -namespace Cards { - -ErrorOr> make_cards_settings_action(GUI::Window* parent) -{ - auto action = GUI::Action::create( - "&Cards Settings", {}, TRY(Gfx::Bitmap::load_from_file("/res/icons/16x16/games.png"sv)), [parent](auto&) { - GUI::Process::spawn_or_show_error(parent, "/bin/GamesSettings"sv, Array { "--open-tab", "cards" }); - }, - parent); - action->set_status_tip("Open the Game Settings for Cards"_string); - return action; -} - -CardGame::CardGame() -{ - auto background_color = Gfx::Color::from_string(Config::read_string("Games"sv, "Cards"sv, "BackgroundColor"sv)); - set_background_color(background_color.value_or(Color::from_rgb(0x008000))); -} - -void CardGame::mark_intersecting_stacks_dirty(Cards::Card const& intersecting_card) -{ - for (auto& stack : stacks()) { - if (intersecting_card.rect().intersects(stack->bounding_box())) - update(stack->bounding_box()); - } - - update(intersecting_card.rect()); -} - -Gfx::IntRect CardGame::moving_cards_bounds() const -{ - if (!is_moving_cards()) - return {}; - - // Note: This assumes that the cards are arranged in a line. - return m_moving_cards.first()->rect().united(m_moving_cards.last()->rect()); -} - -ErrorOr CardGame::pick_up_cards_from_stack(Cards::CardStack& stack, Gfx::IntPoint click_location, CardStack::MovementRule movement_rule) -{ - TRY(stack.add_all_grabbed_cards(click_location, m_moving_cards, movement_rule)); - m_moving_cards_source_stack = stack; - return {}; -} - -RefPtr CardGame::find_stack_to_drop_on(CardStack::MovementRule movement_rule) -{ - auto bounds_to_check = moving_cards_bounds(); - - RefPtr closest_stack; - float closest_distance = FLT_MAX; - - for (auto& stack : stacks()) { - if (stack == moving_cards_source_stack()) - continue; - - if (stack->bounding_box().intersects(bounds_to_check) - && stack->is_allowed_to_push(moving_cards().at(0), moving_cards().size(), movement_rule)) { - - auto distance = bounds_to_check.center().distance_from(stack->bounding_box().center()); - if (distance < closest_distance) { - closest_stack = stack; - closest_distance = distance; - } - } - } - - return closest_stack; -} - -ErrorOr CardGame::drop_cards_on_stack(Cards::CardStack& stack, CardStack::MovementRule movement_rule) -{ - VERIFY(stack.is_allowed_to_push(m_moving_cards.at(0), m_moving_cards.size(), movement_rule)); - for (auto& to_intersect : moving_cards()) { - mark_intersecting_stacks_dirty(to_intersect); - TRY(stack.push(to_intersect)); - (void)moving_cards_source_stack()->pop(); - } - - update(moving_cards_source_stack()->bounding_box()); - update(stack.bounding_box()); - - return {}; -} - -void CardGame::clear_moving_cards() -{ - m_moving_cards_source_stack.clear(); - m_moving_cards.clear(); -} - -void CardGame::dump_layout() const -{ - dbgln("------------------------------"); - for (auto const& stack : stacks()) - dbgln("{}", stack); -} - -void CardGame::config_string_did_change(StringView domain, StringView group, StringView key, StringView value) -{ - if (domain == "Games" && group == "Cards") { - if (key == "BackgroundColor") { - if (auto maybe_color = Gfx::Color::from_string(value); maybe_color.has_value()) - set_background_color(maybe_color.value()); - return; - } - if (key == "CardBackImage") { - CardPainter::the().set_back_image_path(String::from_utf8(value).release_value_but_fixme_should_propagate_errors()); - update(); - return; - } - if (key == "CardFrontImages") { - CardPainter::the().set_front_images_set_name(String::from_utf8(value).release_value_but_fixme_should_propagate_errors()); - update(); - return; - } - } -} - -Gfx::Color CardGame::background_color() const -{ - return palette().color(background_role()); -} - -void CardGame::set_background_color(Gfx::Color color) -{ - auto new_palette = palette(); - new_palette.set_color(Gfx::ColorRole::Background, color); - set_palette(new_palette); - - CardPainter::the().set_background_color(color); -} - -void CardGame::preview_card(CardStack& stack, Gfx::IntPoint click_location) -{ - if (!stack.preview_card(click_location)) - return; - - m_previewed_card_stack = stack; - update(stack.bounding_box()); -} - -void CardGame::clear_card_preview() -{ - VERIFY(m_previewed_card_stack); - - update(m_previewed_card_stack->bounding_box()); - m_previewed_card_stack->clear_card_preview(); - m_previewed_card_stack = nullptr; -} - -} diff --git a/Userland/Libraries/LibCards/CardGame.h b/Userland/Libraries/LibCards/CardGame.h deleted file mode 100644 index ed59d183717..00000000000 --- a/Userland/Libraries/LibCards/CardGame.h +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Copyright (c) 2022, Sam Atkins - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include - -namespace Cards { - -ErrorOr> make_cards_settings_action(GUI::Window* parent = nullptr); - -class CardGame - : public GUI::Frame - , public Config::Listener { -public: - virtual ~CardGame() = default; - - Gfx::Color background_color() const; - void set_background_color(Gfx::Color); - - Vector>& stacks() { return m_stacks; } - Vector> const& stacks() const { return m_stacks; } - CardStack& stack_at_location(int location) { return m_stacks[location]; } - - template - ErrorOr add_stack(Args&&... args) - { - auto stack = TRY(try_make_ref_counted(forward(args)...)); - return m_stacks.try_append(move(stack)); - } - void mark_intersecting_stacks_dirty(Card const& intersecting_card); - - bool is_moving_cards() const { return !m_moving_cards.is_empty(); } - Vector>& moving_cards() { return m_moving_cards; } - Vector> const& moving_cards() const { return m_moving_cards; } - Gfx::IntRect moving_cards_bounds() const; - RefPtr moving_cards_source_stack() const { return m_moving_cards_source_stack; } - ErrorOr pick_up_cards_from_stack(CardStack&, Gfx::IntPoint click_location, CardStack::MovementRule); - RefPtr find_stack_to_drop_on(CardStack::MovementRule); - ErrorOr drop_cards_on_stack(CardStack&, CardStack::MovementRule); - void clear_moving_cards(); - - bool is_previewing_card() const { return !m_previewed_card_stack.is_null(); } - void preview_card(CardStack&, Gfx::IntPoint click_location); - void clear_card_preview(); - - void dump_layout() const; - -protected: - CardGame(); - -private: - virtual void config_string_did_change(StringView domain, StringView group, StringView key, StringView value) override; - - Vector> m_stacks; - - Vector> m_moving_cards; - RefPtr m_moving_cards_source_stack; - RefPtr m_previewed_card_stack; -}; - -} diff --git a/Userland/Libraries/LibCards/CardPainter.cpp b/Userland/Libraries/LibCards/CardPainter.cpp deleted file mode 100644 index 498f4d1adba..00000000000 --- a/Userland/Libraries/LibCards/CardPainter.cpp +++ /dev/null @@ -1,493 +0,0 @@ -/* - * Copyright (c) 2020, Till Mayer - * Copyright (c) 2022, the SerenityOS developers. - * Copyright (c) 2022-2023, Sam Atkins - * Copyright (c) 2023, David Ganz - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "CardPainter.h" -#include -#include -#include -#include -#include - -namespace Cards { - -CardPainter& CardPainter::the() -{ - static CardPainter s_card_painter; - return s_card_painter; -} - -CardPainter::CardPainter() -{ - m_back_image_path = MUST(String::from_byte_string(Config::read_string("Games"sv, "Cards"sv, "CardBackImage"sv, "/res/graphics/cards/backs/Red.png"sv))); - set_front_images_set_name(MUST(String::from_byte_string(Config::read_string("Games"sv, "Cards"sv, "CardFrontImages"sv, "Classic"sv)))); -} - -static constexpr Gfx::CharacterBitmap s_diamond { - " # " - " ### " - " ##### " - " ####### " - "#########" - " ####### " - " ##### " - " ### " - " # "sv, - 9, 9 -}; - -static constexpr Gfx::CharacterBitmap s_heart { - " # # " - " ### ### " - "#########" - "#########" - "#########" - " ####### " - " ##### " - " ### " - " # "sv, - 9, 9 -}; - -static constexpr Gfx::CharacterBitmap s_spade { - " # " - " ### " - " ##### " - " ####### " - "#########" - "#########" - " ## # ## " - " ### " - " ### "sv, - 9, 9 -}; - -static constexpr Gfx::CharacterBitmap s_club { - " ### " - " ##### " - " ##### " - "## ### ##" - "#########" - "#########" - " ## # ## " - " ### " - " ### "sv, - 9, 9 -}; - -static constexpr u8 s_disabled_alpha = 90; - -NonnullRefPtr CardPainter::get_bitmap_or_create(Suit suit, Rank rank, CardPainter::PaintCache& cache, Function creator) -{ - auto suit_id = to_underlying(suit); - auto rank_id = to_underlying(rank); - - auto& existing_bitmap = cache[suit_id][rank_id]; - if (!existing_bitmap.is_null()) - return *existing_bitmap; - - auto bitmap = create_card_bitmap(); - creator(bitmap); - cache[suit_id][rank_id] = move(bitmap); - return *cache[suit_id][rank_id]; -} - -NonnullRefPtr CardPainter::card_front(Suit suit, Rank rank) -{ - return get_bitmap_or_create(suit, rank, m_cards, [this, suit, rank](auto& bitmap) { - paint_card_front(bitmap, suit, rank); - }); -} - -NonnullRefPtr CardPainter::card_back() -{ - if (!m_card_back.is_null()) - return *m_card_back; - - m_card_back = create_card_bitmap(); - paint_card_back(*m_card_back); - - return *m_card_back; -} - -NonnullRefPtr CardPainter::card_front_highlighted(Suit suit, Rank rank) -{ - return get_bitmap_or_create(suit, rank, m_cards_highlighted, [this, suit, rank](auto& bitmap) { - paint_highlighted_card(bitmap, card_front(suit, rank)); - }); -} - -NonnullRefPtr CardPainter::card_front_disabled(Suit suit, Rank rank) -{ - return get_bitmap_or_create(suit, rank, m_cards_disabled, [this, suit, rank](auto& bitmap) { - paint_disabled_card(bitmap, card_front(suit, rank)); - }); -} - -NonnullRefPtr CardPainter::card_front_inverted(Suit suit, Rank rank) -{ - return get_bitmap_or_create(suit, rank, m_cards_inverted, [this, suit, rank](auto& bitmap) { - paint_inverted_card(bitmap, card_front(suit, rank)); - }); -} - -NonnullRefPtr CardPainter::card_back_inverted() -{ - if (!m_card_back_inverted.is_null()) - return *m_card_back_inverted; - - m_card_back_inverted = create_card_bitmap(); - paint_inverted_card(card_back(), *m_card_back_inverted); - - return *m_card_back_inverted; -} - -NonnullRefPtr CardPainter::card_back_disabled() -{ - if (!m_card_back_disabled.is_null()) - return *m_card_back_disabled; - - m_card_back_disabled = create_card_bitmap(); - paint_disabled_card(*m_card_back_disabled, card_back()); - - return *m_card_back_disabled; -} - -void CardPainter::set_back_image_path(StringView path) -{ - if (m_back_image_path == path) - return; - - m_back_image_path = MUST(String::from_utf8(path)); - if (!m_card_back.is_null()) - paint_card_back(*m_card_back); - if (!m_card_back_inverted.is_null()) - paint_inverted_card(*m_card_back_inverted, *m_card_back); -} - -void CardPainter::set_front_images_set_name(AK::StringView path) -{ - if (m_front_images_set_name == path) - return; - - m_front_images_set_name = MUST(String::from_utf8(path)); - - if (m_front_images_set_name.is_empty()) { - for (auto& pip_bitmap : m_suit_pips) - pip_bitmap = nullptr; - for (auto& pip_bitmap : m_suit_pips_flipped_vertically) - pip_bitmap = nullptr; - } else { - auto diamond = Gfx::Bitmap::load_from_file(MUST(String::formatted("/res/graphics/cards/fronts/{}/diamond.png", m_front_images_set_name))).release_value_but_fixme_should_propagate_errors(); - m_suit_pips[to_underlying(Suit::Diamonds)] = diamond; - m_suit_pips_flipped_vertically[to_underlying(Suit::Diamonds)] = diamond->flipped(Gfx::Orientation::Vertical).release_value_but_fixme_should_propagate_errors(); - - auto club = Gfx::Bitmap::load_from_file(MUST(String::formatted("/res/graphics/cards/fronts/{}/club.png", m_front_images_set_name))).release_value_but_fixme_should_propagate_errors(); - m_suit_pips[to_underlying(Suit::Clubs)] = club; - m_suit_pips_flipped_vertically[to_underlying(Suit::Clubs)] = club->flipped(Gfx::Orientation::Vertical).release_value_but_fixme_should_propagate_errors(); - - auto heart = Gfx::Bitmap::load_from_file(MUST(String::formatted("/res/graphics/cards/fronts/{}/heart.png", m_front_images_set_name))).release_value_but_fixme_should_propagate_errors(); - m_suit_pips[to_underlying(Suit::Hearts)] = heart; - m_suit_pips_flipped_vertically[to_underlying(Suit::Hearts)] = heart->flipped(Gfx::Orientation::Vertical).release_value_but_fixme_should_propagate_errors(); - - auto spade = Gfx::Bitmap::load_from_file(MUST(String::formatted("/res/graphics/cards/fronts/{}/spade.png", m_front_images_set_name))).release_value_but_fixme_should_propagate_errors(); - m_suit_pips[to_underlying(Suit::Spades)] = spade; - m_suit_pips_flipped_vertically[to_underlying(Suit::Spades)] = spade->flipped(Gfx::Orientation::Vertical).release_value_but_fixme_should_propagate_errors(); - } - - // Clear all bitmaps using front images - for (auto& suit_array : m_cards) { - for (auto& card_bitmap : suit_array) - card_bitmap = nullptr; - } - - for (auto& suit_array : m_cards_highlighted) { - for (auto& card_bitmap : suit_array) - card_bitmap = nullptr; - } -} - -void CardPainter::set_background_color(Color background_color) -{ - m_background_color = background_color; - - // Clear any cached card bitmaps that depend on the background color. - for (auto& suit_array : m_cards_highlighted) { - for (auto& rank_array : suit_array) - rank_array = nullptr; - } -} - -NonnullRefPtr CardPainter::create_card_bitmap() -{ - return Gfx::Bitmap::create(Gfx::BitmapFormat::BGRA8888, { Card::width, Card::height }).release_value_but_fixme_should_propagate_errors(); -} - -void CardPainter::paint_card_front_pips(Gfx::Bitmap& bitmap, Suit suit, Rank rank) -{ - Gfx::Painter painter { bitmap }; - auto& pip_bitmap = m_suit_pips[to_underlying(suit)]; - auto& pip_bitmap_flipped_vertically = m_suit_pips_flipped_vertically[to_underlying(suit)]; - - struct Pip { - int x; - int y; - bool flip_vertically; - }; - - auto paint_pips = [&](Span pips) { - for (auto& pip : pips) { - auto& bitmap = pip.flip_vertically ? pip_bitmap_flipped_vertically : pip_bitmap; - painter.blit({ pip.x - bitmap->width() / 2, pip.y - bitmap->height() / 2 }, *bitmap, bitmap->rect()); - } - }; - - constexpr int column_left = Card::width * 1 / 3; - constexpr int column_middle = Card::width * 1 / 2; - constexpr int column_right = Card::width - column_left; - constexpr int row_top = Card::height / 6; - constexpr int row_middle = Card::height / 2; - constexpr int row_bottom = Card::height - row_top - 1; - constexpr int row_2_of_4 = row_top + (row_bottom - row_top) * 1 / 3; - constexpr int row_3_of_4 = Card::height - row_2_of_4 - 1; - constexpr int row_2_of_5 = row_top + (row_bottom - row_top) * 1 / 4; - constexpr int row_4_of_5 = Card::height - row_2_of_5 - 1; - constexpr int row_2_of_7 = row_top + (row_bottom - row_top) * 1 / 6; - constexpr int row_6_of_7 = Card::height - row_2_of_7 - 1; - - switch (rank) { - case Rank::Ace: - paint_pips(Array({ Pip { column_middle, row_middle, false } })); - break; - case Rank::Two: - paint_pips(Array({ { column_middle, row_top, false }, - { column_middle, row_bottom, true } })); - break; - case Rank::Three: - paint_pips(Array({ { column_middle, row_top, false }, - { column_middle, row_middle, false }, - { column_middle, row_bottom, true } })); - break; - case Rank::Four: - paint_pips(Array({ { column_left, row_top, false }, - { column_right, row_top, false }, - { column_left, row_bottom, true }, - { column_right, row_bottom, true } })); - break; - case Rank::Five: - paint_pips(Array({ { column_left, row_top, false }, - { column_right, row_top, false }, - { column_middle, row_middle, false }, - { column_left, row_bottom, true }, - { column_right, row_bottom, true } })); - break; - case Rank::Six: - paint_pips(Array({ { column_left, row_top, false }, - { column_right, row_top, false }, - { column_left, row_middle, false }, - { column_right, row_middle, false }, - { column_left, row_bottom, true }, - { column_right, row_bottom, true } })); - break; - case Rank::Seven: - paint_pips(Array({ { column_left, row_top, false }, - { column_right, row_top, false }, - { column_middle, row_2_of_5, false }, - { column_left, row_middle, false }, - { column_right, row_middle, false }, - { column_left, row_bottom, true }, - { column_right, row_bottom, true } })); - break; - case Rank::Eight: - paint_pips(Array({ { column_left, row_top, false }, - { column_right, row_top, false }, - { column_middle, row_2_of_5, false }, - { column_left, row_middle, false }, - { column_right, row_middle, false }, - { column_middle, row_4_of_5, true }, - { column_left, row_bottom, true }, - { column_right, row_bottom, true } })); - break; - case Rank::Nine: - paint_pips(Array({ { column_left, row_top, false }, - { column_right, row_top, false }, - { column_left, row_2_of_4, false }, - { column_right, row_2_of_4, false }, - { column_middle, row_middle, false }, - { column_left, row_3_of_4, true }, - { column_right, row_3_of_4, true }, - { column_left, row_bottom, true }, - { column_right, row_bottom, true } })); - break; - case Rank::Ten: - paint_pips(Array({ { column_left, row_top, false }, - { column_right, row_top, false }, - { column_middle, row_2_of_7, false }, - { column_left, row_2_of_4, false }, - { column_right, row_2_of_4, false }, - { column_left, row_3_of_4, true }, - { column_right, row_3_of_4, true }, - { column_middle, row_6_of_7, true }, - { column_left, row_bottom, true }, - { column_right, row_bottom, true } })); - break; - - case Rank::Jack: - case Rank::Queen: - case Rank::King: - case Rank::__Count: - break; - } -} - -void CardPainter::paint_card_front(Gfx::Bitmap& bitmap, Cards::Suit suit, Cards::Rank rank) -{ - auto const suit_color = (suit == Suit::Diamonds || suit == Suit::Hearts) ? Color::Red : Color::Black; - - auto const& suit_symbol = [&]() -> Gfx::CharacterBitmap const& { - switch (suit) { - case Suit::Diamonds: - return s_diamond; - case Suit::Clubs: - return s_club; - case Suit::Spades: - return s_spade; - case Suit::Hearts: - return s_heart; - default: - VERIFY_NOT_REACHED(); - } - }(); - - Gfx::Painter painter { bitmap }; - auto paint_rect = bitmap.rect(); - auto& font = Gfx::FontDatabase::default_font().bold_variant(); - - painter.fill_rect_with_rounded_corners(paint_rect, Color::Black, Card::card_radius); - paint_rect.shrink(2, 2); - painter.fill_rect_with_rounded_corners(paint_rect, Color::White, Card::card_radius - 1); - - paint_rect.set_height(paint_rect.height() / 2); - paint_rect.shrink(10, 6); - - auto text_rect = Gfx::IntRect { 1, 6, font.width_rounded_up("10"sv), font.pixel_size_rounded_up() }; - painter.draw_text(text_rect, card_rank_label(rank), font, Gfx::TextAlignment::Center, suit_color); - - painter.draw_bitmap( - { text_rect.x() + (text_rect.width() - suit_symbol.size().width()) / 2, text_rect.bottom() + 4 }, - suit_symbol, suit_color); - - for (int y = Card::height / 2; y < Card::height; ++y) { - for (int x = 0; x < Card::width; ++x) - bitmap.set_pixel(x, y, bitmap.get_pixel(Card::width - x - 1, Card::height - y - 1)); - } - - if (!m_front_images_set_name.is_empty()) { - // Paint pips for number cards except the ace of spades - if (!first_is_one_of(rank, Rank::Ace, Rank::Jack, Rank::Queen, Rank::King) - || (rank == Rank::Ace && suit != Suit::Spades)) { - paint_card_front_pips(bitmap, suit, rank); - } else { - // Paint pictures for royal cards and ace of spades - StringView rank_name; - switch (rank) { - case Rank::Ace: - rank_name = "ace"sv; - break; - case Rank::Jack: - rank_name = "jack"sv; - break; - case Rank::Queen: - rank_name = "queen"sv; - break; - case Rank::King: - rank_name = "king"sv; - break; - default: - break; - } - - StringView suit_name; - switch (suit) { - case Suit::Diamonds: - suit_name = "diamonds"sv; - break; - case Suit::Clubs: - suit_name = "clubs"sv; - break; - case Suit::Hearts: - suit_name = "hearts"sv; - break; - case Suit::Spades: - suit_name = "spades"sv; - break; - case Suit::__Count: - return; - } - - auto front_image_path = MUST(String::formatted("/res/graphics/cards/fronts/{}/{}-{}.png", m_front_images_set_name, suit_name, rank_name)); - auto maybe_front_image = Gfx::Bitmap::load_from_file(front_image_path); - if (maybe_front_image.is_error()) { - dbgln("Failed to load `{}`: {}", front_image_path, maybe_front_image.error()); - return; - } - auto front_image = maybe_front_image.release_value(); - painter.blit({ (bitmap.width() - front_image->width()) / 2, (bitmap.height() - front_image->height()) / 2 }, front_image, front_image->rect()); - } - } -} - -void CardPainter::paint_card_back(Gfx::Bitmap& bitmap) -{ - Gfx::Painter painter { bitmap }; - auto paint_rect = bitmap.rect(); - painter.clear_rect(paint_rect, Gfx::Color::Transparent); - - painter.fill_rect_with_rounded_corners(paint_rect, Color::Black, Card::card_radius); - auto inner_paint_rect = paint_rect.shrunken(2, 2); - painter.fill_rect_with_rounded_corners(inner_paint_rect, Color::White, Card::card_radius - 1); - - auto image = Gfx::Bitmap::load_from_file(m_back_image_path).release_value_but_fixme_should_propagate_errors(); - painter.blit({ (bitmap.width() - image->width()) / 2, (bitmap.height() - image->height()) / 2 }, image, image->rect()); -} - -void CardPainter::paint_inverted_card(Gfx::Bitmap& bitmap, Gfx::Bitmap const& source_to_invert) -{ - Gfx::Painter painter { bitmap }; - painter.clear_rect(bitmap.rect(), Gfx::Color::Transparent); - painter.blit_filtered(Gfx::IntPoint {}, source_to_invert, source_to_invert.rect(), [&](Color color) { - return color.inverted(); - }); -} - -void CardPainter::paint_highlighted_card(Gfx::Bitmap& bitmap, Gfx::Bitmap const& source_to_highlight) -{ - Gfx::Painter painter { bitmap }; - auto paint_rect = source_to_highlight.rect(); - auto background_complement = m_background_color.xored(Color::White); - - painter.fill_rect_with_rounded_corners(paint_rect, Color::Black, Card::card_radius); - paint_rect.shrink(2, 2); - painter.fill_rect_with_rounded_corners(paint_rect, background_complement, Card::card_radius - 1); - paint_rect.shrink(4, 4); - painter.fill_rect_with_rounded_corners(paint_rect, Color::White, Card::card_radius - 1); - painter.blit({ 4, 4 }, source_to_highlight, source_to_highlight.rect().shrunken(8, 8)); -} - -void CardPainter::paint_disabled_card(Gfx::Bitmap& bitmap, Gfx::Bitmap const& source_to_disable) -{ - Gfx::Painter painter { bitmap }; - auto disabled_color = Color(Color::Black); - disabled_color.set_alpha(s_disabled_alpha); - - painter.blit_filtered(Gfx::IntPoint {}, source_to_disable, source_to_disable.rect(), [&](Color color) { - return color.blend(disabled_color); - }); -} - -} diff --git a/Userland/Libraries/LibCards/CardPainter.h b/Userland/Libraries/LibCards/CardPainter.h deleted file mode 100644 index b5c400f5eaf..00000000000 --- a/Userland/Libraries/LibCards/CardPainter.h +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright (c) 2022-2023, Sam Atkins - * Copyright (c) 2023, David Ganz - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include - -namespace Cards { - -class CardPainter { -public: - static CardPainter& the(); - - NonnullRefPtr card_front(Suit, Rank); - NonnullRefPtr card_back(); - NonnullRefPtr card_front_inverted(Suit, Rank); - NonnullRefPtr card_back_inverted(); - NonnullRefPtr card_front_highlighted(Suit, Rank); - NonnullRefPtr card_front_disabled(Suit, Rank); - NonnullRefPtr card_back_disabled(); - - void set_back_image_path(StringView path); - void set_front_images_set_name(StringView path); - void set_background_color(Color); - -private: - using PaintCache = Array, to_underlying(Rank::__Count)>, to_underlying(Suit::__Count)>; - - CardPainter(); - - NonnullRefPtr get_bitmap_or_create(Suit, Rank, PaintCache&, Function); - - NonnullRefPtr create_card_bitmap(); - void paint_card_front(Gfx::Bitmap&, Suit, Rank); - void paint_card_front_pips(Gfx::Bitmap&, Suit, Rank); - void paint_card_back(Gfx::Bitmap&); - void paint_inverted_card(Gfx::Bitmap& bitmap, Gfx::Bitmap const& source_to_invert); - void paint_highlighted_card(Gfx::Bitmap& bitmap, Gfx::Bitmap const& source_to_highlight); - void paint_disabled_card(Gfx::Bitmap& bitmap, Gfx::Bitmap const& source_to_disable); - - Array, to_underlying(Suit::__Count)> m_suit_pips; - Array, to_underlying(Suit::__Count)> m_suit_pips_flipped_vertically; - - PaintCache m_cards; - PaintCache m_cards_inverted; - PaintCache m_cards_highlighted; - PaintCache m_cards_disabled; - - RefPtr m_card_back; - RefPtr m_card_back_inverted; - RefPtr m_card_back_disabled; - - String m_back_image_path; - String m_front_images_set_name; - Color m_background_color; -}; - -} diff --git a/Userland/Libraries/LibCards/CardStack.cpp b/Userland/Libraries/LibCards/CardStack.cpp deleted file mode 100644 index 9a6aa642972..00000000000 --- a/Userland/Libraries/LibCards/CardStack.cpp +++ /dev/null @@ -1,422 +0,0 @@ -/* - * Copyright (c) 2020, Till Mayer - * Copyright (c) 2023, David Ganz - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "CardStack.h" - -namespace Cards { - -CardStack::CardStack() - : m_position({ 0, 0 }) - , m_type(Type::Invalid) - , m_base(m_position, { Card::width, Card::height }) -{ -} - -CardStack::CardStack(Gfx::IntPoint position, Type type, RefPtr covered_stack) - : m_covered_stack(move(covered_stack)) - , m_position(position) - , m_type(type) - , m_rules(rules_for_type(type)) - , m_base(m_position, { Card::width, Card::height }) -{ - VERIFY(type != Type::Invalid); - calculate_bounding_box(); -} - -void CardStack::clear() -{ - m_stack.clear(); - m_stack_positions.clear(); -} - -void CardStack::paint(GUI::Painter& painter, Gfx::Color background_color) -{ - auto background_markings_color = (background_color.luminosity() > 64) ? Color(0, 0, 0, 128) : Color(255, 255, 255, 128); - - auto draw_background_if_empty = [&]() { - size_t number_of_moving_cards = 0; - for (auto const& card : m_stack) - number_of_moving_cards += card->is_moving() ? 1 : 0; - - if (m_covered_stack && !m_covered_stack->is_empty()) - return false; - if (!is_empty() && (m_stack.size() != number_of_moving_cards)) - return false; - - auto paint_rect = m_base; - painter.fill_rect_with_rounded_corners(paint_rect, background_markings_color, Card::card_radius); - paint_rect.shrink(2, 2); - - if (m_highlighted) { - auto background_complement = background_color.xored(Color::White); - painter.fill_rect_with_rounded_corners(paint_rect, background_complement, Card::card_radius - 1); - paint_rect.shrink(4, 4); - } - - painter.fill_rect_with_rounded_corners(paint_rect, background_color, Card::card_radius - 1); - return true; - }; - - switch (m_type) { - case Type::Stock: - if (draw_background_if_empty()) { - auto stock_highlight_color = (background_color.luminosity() < 196) ? Color(255, 255, 255, 128) : Color(0, 0, 0, 64); - painter.fill_rect(m_base.shrunken(Card::width / 4, Card::height / 4), stock_highlight_color); - painter.fill_rect(m_base.shrunken(Card::width / 2, Card::height / 2), background_color); - } - break; - case Type::Foundation: - if (draw_background_if_empty()) { - for (int y = 0; y < (m_base.height() - 4) / 8; ++y) { - for (int x = 0; x < (m_base.width() - 4) / 5; ++x) { - painter.draw_rect({ 4 + m_base.x() + x * 5, 4 + m_base.y() + y * 8, 1, 1 }, background_markings_color); - } - } - } - break; - case Type::Play: - case Type::Normal: - draw_background_if_empty(); - break; - case Type::Waste: - break; - default: - VERIFY_NOT_REACHED(); - } - - if (is_empty()) - return; - - if (m_rules.shift_x == 0 && m_rules.shift_y == 0) { - auto& card = peek(); - card.paint(painter); - return; - } - - RefPtr previewed_card; - - for (size_t i = 0; i < m_stack.size(); ++i) { - if (auto& card = m_stack[i]; !card->is_moving()) { - if (card->is_previewed()) { - VERIFY(!previewed_card); - previewed_card = card; - continue; - } - - auto highlighted = m_highlighted && (i == m_stack.size() - 1); - card->clear_and_paint(painter, Gfx::Color::Transparent, highlighted); - } - } - - if (previewed_card) - previewed_card->clear_and_paint(painter, Gfx::Color::Transparent, false); -} - -void CardStack::rebound_cards() -{ - VERIFY(m_stack_positions.size() == m_stack.size()); - - size_t card_index = 0; - for (auto& card : m_stack) - card->set_position(m_stack_positions.at(card_index++)); -} - -ErrorOr CardStack::add_all_grabbed_cards(Gfx::IntPoint click_location, Vector>& grabbed, MovementRule movement_rule) -{ - VERIFY(grabbed.is_empty()); - - if (m_type != Type::Normal) { - auto& top_card = peek(); - if (top_card.rect().contains(click_location)) { - top_card.set_moving(true); - TRY(grabbed.try_append(top_card)); - } - return {}; - } - - RefPtr last_intersect; - - for (auto& card : m_stack) { - if (card->rect().contains(click_location)) { - if (card->is_upside_down()) - continue; - - last_intersect = card; - } else if (!last_intersect.is_null()) { - if (grabbed.is_empty()) { - TRY(grabbed.try_append(*last_intersect)); - last_intersect->set_moving(true); - } - - if (card->is_upside_down()) { - grabbed.clear(); - return {}; - } - - card->set_moving(true); - TRY(grabbed.try_append(card)); - } - } - - if (grabbed.is_empty() && !last_intersect.is_null()) { - TRY(grabbed.try_append(*last_intersect)); - last_intersect->set_moving(true); - } - - // verify valid stack - bool valid_stack = true; - uint8_t last_value; - Color last_color; - for (size_t i = 0; i < grabbed.size(); i++) { - auto& card = grabbed.at(i); - if (i != 0) { - bool color_match; - switch (movement_rule) { - case MovementRule::Alternating: - color_match = card->color() != last_color; - break; - case MovementRule::Same: - color_match = card->color() == last_color; - break; - case MovementRule::Any: - color_match = true; - break; - } - - if (!color_match || to_underlying(card->rank()) != last_value - 1) { - valid_stack = false; - break; - } - } - last_value = to_underlying(card->rank()); - last_color = card->color(); - } - - if (!valid_stack) { - for (auto& card : grabbed) { - card->set_moving(false); - } - grabbed.clear(); - } - - return {}; -} - -void CardStack::update_disabled_cards(MovementRule movement_rule) -{ - if (m_stack.is_empty()) - return; - - for (auto& card : m_stack) - card->set_disabled(false); - - Optional last_valid_card = {}; - uint8_t last_rank; - Color last_color; - for (size_t idx = m_stack.size(); idx > 0; idx--) { - auto i = idx - 1; - auto card = m_stack[i]; - if (card->is_upside_down()) { - if (!last_valid_card.has_value()) - last_valid_card = i + 1; - break; - } - - if (i != m_stack.size() - 1) { - bool color_valid; - switch (movement_rule) { - case MovementRule::Alternating: - color_valid = card->color() != last_color; - break; - case MovementRule::Same: - color_valid = card->color() == last_color; - break; - case MovementRule::Any: - color_valid = true; - break; - } - - if (!color_valid || to_underlying(card->rank()) != last_rank + 1) { - last_valid_card = i + 1; - break; - } - } - - last_rank = to_underlying(card->rank()); - last_color = card->color(); - } - - if (!last_valid_card.has_value()) - return; - - for (size_t i = 0; i < last_valid_card.value(); i++) - m_stack[i]->set_disabled(true); -} - -bool CardStack::is_allowed_to_push(Card const& card, size_t stack_size, MovementRule movement_rule) const -{ - if (m_type == Type::Stock || m_type == Type::Waste || m_type == Type::Play) - return false; - - if (m_type == Type::Normal && is_empty()) { - // FIXME: proper solution for this - if (movement_rule == MovementRule::Alternating) { - return card.rank() == Rank::King; - } - return true; - } - - if (m_type == Type::Foundation && is_empty()) - return card.rank() == Rank::Ace; - - if (!is_empty()) { - auto const& top_card = peek(); - if (top_card.is_upside_down()) - return false; - - if (m_type == Type::Foundation) { - // Prevent player from dragging an entire stack of cards to the foundation stack - if (stack_size > 1) - return false; - return top_card.suit() == card.suit() && m_stack.size() == to_underlying(card.rank()); - } - if (m_type == Type::Normal) { - bool color_match; - switch (movement_rule) { - case MovementRule::Alternating: - color_match = card.color() != top_card.color(); - break; - case MovementRule::Same: - color_match = card.color() == top_card.color(); - break; - case MovementRule::Any: - color_match = true; - break; - } - - return color_match && to_underlying(top_card.rank()) == to_underlying(card.rank()) + 1; - } - - VERIFY_NOT_REACHED(); - } - - return true; -} - -bool CardStack::preview_card(Gfx::IntPoint click_location) -{ - RefPtr last_intersect; - - for (auto& card : m_stack) { - if (!card->rect().contains(click_location)) - continue; - if (card->is_upside_down()) - continue; - - last_intersect = card; - } - - if (!last_intersect) - return false; - - last_intersect->set_previewed(true); - return true; -} - -void CardStack::clear_card_preview() -{ - for (auto& card : m_stack) - card->set_previewed(false); -} - -bool CardStack::make_top_card_visible() -{ - if (is_empty()) - return false; - - auto& top_card = peek(); - if (top_card.is_upside_down()) { - top_card.set_upside_down(false); - return true; - } - - return false; -} - -ErrorOr CardStack::push(NonnullRefPtr card) -{ - auto top_most_position = m_stack_positions.is_empty() ? m_position : m_stack_positions.last(); - - if (!m_stack.is_empty() && m_stack.size() % m_rules.step == 0) { - if (peek().is_upside_down()) - top_most_position.translate_by(m_rules.shift_x, m_rules.shift_y_upside_down); - else - top_most_position.translate_by(m_rules.shift_x, m_rules.shift_y); - } - - if (m_type == Type::Stock) - card->set_upside_down(true); - - card->set_position(top_most_position); - - TRY(m_stack.try_append(card)); - TRY(m_stack_positions.try_append(top_most_position)); - calculate_bounding_box(); - return {}; -} - -NonnullRefPtr CardStack::pop() -{ - auto card = m_stack.take_last(); - - calculate_bounding_box(); - if (m_type == Type::Stock) - card->set_upside_down(false); - - m_stack_positions.take_last(); - return card; -} - -ErrorOr CardStack::take_all(CardStack& stack) -{ - while (!m_stack.is_empty()) { - auto card = m_stack.take_first(); - m_stack_positions.take_first(); - TRY(stack.push(move(card))); - } - - calculate_bounding_box(); - return {}; -} - -void CardStack::calculate_bounding_box() -{ - m_bounding_box = Gfx::IntRect(m_position, { Card::width, Card::height }); - - if (m_stack.is_empty()) - return; - - uint16_t width = 0; - uint16_t height = 0; - size_t card_position = 0; - for (auto& card : m_stack) { - if (card_position % m_rules.step == 0 && card_position != 0) { - if (card->is_upside_down()) { - width += m_rules.shift_x; - height += m_rules.shift_y_upside_down; - } else { - width += m_rules.shift_x; - height += m_rules.shift_y; - } - } - ++card_position; - } - - m_bounding_box.set_size(Card::width + width, Card::height + height); -} - -} diff --git a/Userland/Libraries/LibCards/CardStack.h b/Userland/Libraries/LibCards/CardStack.h deleted file mode 100644 index 49b482de746..00000000000 --- a/Userland/Libraries/LibCards/CardStack.h +++ /dev/null @@ -1,145 +0,0 @@ -/* - * Copyright (c) 2020, Till Mayer - * Copyright (c) 2023, David Ganz - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include "Card.h" -#include -#include -#include - -namespace Cards { - -class CardStack final : public RefCounted { -public: - enum class Type { - Invalid, - Stock, - Normal, - Waste, - Play, - Foundation - }; - - enum class MovementRule { - Alternating, - Same, - Any, - }; - - CardStack(); - CardStack(Gfx::IntPoint position, Type type, RefPtr covered_stack = nullptr); - - bool is_empty() const { return m_stack.is_empty(); } - Type type() const { return m_type; } - Vector> const& stack() const { return m_stack; } - size_t count() const { return m_stack.size(); } - Card const& peek() const { return m_stack.last(); } - Card& peek() { return m_stack.last(); } - Gfx::IntRect const& bounding_box() const { return m_bounding_box; } - - bool make_top_card_visible(); // Returns true if the card was flipped. - - ErrorOr push(NonnullRefPtr); - NonnullRefPtr pop(); - ErrorOr take_all(CardStack&); - void rebound_cards(); - - bool is_allowed_to_push(Card const&, size_t stack_size = 1, MovementRule movement_rule = MovementRule::Alternating) const; - ErrorOr add_all_grabbed_cards(Gfx::IntPoint click_location, Vector>& grabbed, MovementRule movement_rule = MovementRule::Alternating); - - void update_disabled_cards(MovementRule); - - bool preview_card(Gfx::IntPoint click_location); - void clear_card_preview(); - - void paint(GUI::Painter&, Gfx::Color background_color); - void clear(); - - void set_highlighted(bool highlighted) { m_highlighted = highlighted; } - -private: - struct StackRules { - uint8_t shift_x { 0 }; - uint8_t shift_y { 0 }; - uint8_t step { 1 }; - uint8_t shift_y_upside_down { 0 }; - }; - - static constexpr StackRules rules_for_type(Type stack_type) - { - switch (stack_type) { - case Type::Foundation: - return { 2, 1, 4, 1 }; - case Type::Normal: - return { 0, 20, 1, 3 }; - case Type::Stock: - return { 2, 1, 8, 1 }; - case Type::Waste: - return { 0, 0, 1, 0 }; - case Type::Play: - return { 15, 0, 1, 0 }; - default: - return {}; - } - } - - void calculate_bounding_box(); - - // An optional stack that this stack is painted on top of. - // eg, in Solitaire the Play stack is positioned over the Waste stack. - RefPtr m_covered_stack; - - Vector> m_stack; - Vector m_stack_positions; - Gfx::IntPoint m_position; - Gfx::IntRect m_bounding_box; - Type m_type { Type::Invalid }; - StackRules m_rules; - Gfx::IntRect m_base; - bool m_highlighted { false }; -}; - -} - -template<> -struct AK::Formatter : Formatter { - ErrorOr format(FormatBuilder& builder, Cards::CardStack const& stack) - { - StringView type; - - switch (stack.type()) { - case Cards::CardStack::Type::Stock: - type = "Stock"sv; - break; - case Cards::CardStack::Type::Normal: - type = "Normal"sv; - break; - case Cards::CardStack::Type::Foundation: - type = "Foundation"sv; - break; - case Cards::CardStack::Type::Waste: - type = "Waste"sv; - break; - case Cards::CardStack::Type::Play: - type = "Play"sv; - break; - default: - VERIFY_NOT_REACHED(); - } - - StringBuilder cards; - bool first_card = true; - - for (auto const& card : stack.stack()) { - cards.appendff("{}{}", (first_card ? "" : " "), card); - first_card = false; - } - - return Formatter::format(builder, "{:<10} {:>16}: {}"sv, type, stack.bounding_box(), cards.to_byte_string()); - } -}; diff --git a/Userland/Libraries/LibChess/CMakeLists.txt b/Userland/Libraries/LibChess/CMakeLists.txt deleted file mode 100644 index 2a55c8fbce2..00000000000 --- a/Userland/Libraries/LibChess/CMakeLists.txt +++ /dev/null @@ -1,8 +0,0 @@ -set(SOURCES - Chess.cpp - UCICommand.cpp - UCIEndpoint.cpp -) - -serenity_lib(LibChess chess) -target_link_libraries(LibChess PRIVATE LibCore) diff --git a/Userland/Libraries/LibChess/Chess.cpp b/Userland/Libraries/LibChess/Chess.cpp deleted file mode 100644 index a29269c33ec..00000000000 --- a/Userland/Libraries/LibChess/Chess.cpp +++ /dev/null @@ -1,965 +0,0 @@ -/* - * Copyright (c) 2020, the SerenityOS developers. - * Copyright (c) 2023, Sam Atkins - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include -#include -#include - -namespace Chess { - -Optional char_for_piece(Type type, Notation notation) -{ - switch (type) { - case Type::Knight: - return 'N'; - case Type::Bishop: - return 'B'; - case Type::Rook: - return 'R'; - case Type::Queen: - return 'Q'; - case Type::King: - return 'K'; - case Type::Pawn: - if (notation == Notation::FEN) - return 'P'; - return {}; - case Type::None: - return {}; - } - VERIFY_NOT_REACHED(); -} - -Type piece_from_char(char c) -{ - switch (to_ascii_lowercase(c)) { - case 'n': - return Type::Knight; - case 'b': - return Type::Bishop; - case 'r': - return Type::Rook; - case 'q': - return Type::Queen; - case 'k': - return Type::King; - case 'p': - return Type::Pawn; - } - - return Type::None; -} - -Color opposing_color(Color color) -{ - return (color == Color::White) ? Color::Black : Color::White; -} - -Square::Square(StringView name) -{ - VERIFY(name.length() == 2); - char filec = name[0]; - char rankc = name[1]; - - if (filec >= 'a' && filec <= 'h') { - file = filec - 'a'; - } else if (filec >= 'A' && filec <= 'H') { - file = filec - 'A'; - } else { - VERIFY_NOT_REACHED(); - } - - if (rankc >= '1' && rankc <= '8') { - rank = rankc - '1'; - } else { - VERIFY_NOT_REACHED(); - } -} - -char Square::file_char() const -{ - return file + 'a'; -} - -char Square::rank_char() const -{ - return rank + '1'; -} - -ErrorOr Square::to_algebraic() const -{ - return String::formatted("{}{}", file_char(), rank_char()); -} - -Move::Move(StringView long_algebraic) - : from(long_algebraic.substring_view(0, 2)) - , to(long_algebraic.substring_view(2, 2)) - , promote_to((long_algebraic.length() >= 5) ? piece_from_char(long_algebraic[4]) : Type::None) -{ -} - -ErrorOr Move::to_long_algebraic() const -{ - StringBuilder builder; - TRY(builder.try_append(TRY(from.to_algebraic()))); - TRY(builder.try_append(TRY(to.to_algebraic()))); - if (auto promoted_char = char_for_piece(promote_to, Notation::Algebraic); promoted_char.has_value()) - TRY(builder.try_append(to_ascii_lowercase(promoted_char.value()))); - return builder.to_string(); -} - -Move Move::from_algebraic(StringView algebraic, Color const turn, Board const& board) -{ - auto move_string = algebraic; - Move move({ 50, 50 }, { 50, 50 }); - - if (move_string.contains('-')) { - move.from = Square(turn == Color::White ? 0 : 7, 4); - move.to = Square(turn == Color::White ? 0 : 7, move_string == "O-O" ? 6 : 2); - move.promote_to = Type::None; - move.piece = { turn, Type::King }; - - return move; - } - - if (algebraic.contains('#')) { - move.is_mate = true; - move_string = move_string.substring_view(0, move_string.length() - 1); - } else if (algebraic.contains('+')) { - move.is_check = true; - move_string = move_string.substring_view(0, move_string.length() - 1); - } - - if (algebraic.contains('=')) { - auto parts = move_string.split_view('='); - move.promote_to = piece_from_char(parts[1][0]); - move_string = parts[0]; - } - - move.to = Square(move_string.substring_view(move_string.length() - 2, 2)); - move_string = move_string.substring_view(0, move_string.length() - 2); - - if (move_string.contains('x')) { - move.is_capture = true; - move_string = move_string.substring_view(0, move_string.length() - 1); - } - - if (move_string.is_empty() || move_string[0] >= 'a') { - move.piece = Piece(turn, Type::Pawn); - } else { - move.piece = Piece(turn, piece_from_char(move_string[0])); - move_string = move_string.substring_view(1, move_string.length() - 1); - } - - Square::for_each([&](Square const& square) { - if (!move_string.is_empty()) { - if (board.get_piece(square).type == move.piece.type && board.is_legal(Move(square, move.to, move.promote_to), turn)) { - if (move_string.length() >= 2) { - if (square == Square(move_string.substring_view(0, 2))) { - move.from = square; - return IterationDecision::Break; - } - } else if (move_string[0] <= 57) { - if (square.rank == (move_string[0] - '0')) { - move.from = square; - return IterationDecision::Break; - } - } else { - if (square.file == (move_string[0] - 'a')) { - move.from = square; - return IterationDecision::Break; - } - } - } - return IterationDecision::Continue; - } else { - if (board.get_piece(square).type == move.piece.type && board.is_legal(Move(square, move.to, move.promote_to), turn)) { - move.from = square; - return IterationDecision::Break; - } - return IterationDecision::Continue; - } - }); - - return move; -} - -ErrorOr Move::to_algebraic() const -{ - if (piece.type == Type::King && from.file == 4) { - if (to.file == 2) - return "O-O-O"_string; - if (to.file == 6) - return "O-O"_string; - } - - StringBuilder builder; - - if (auto piece_char = char_for_piece(piece.type, Notation::Algebraic); piece_char.has_value()) - TRY(builder.try_append(*piece_char)); - - if (is_ambiguous) { - if (from.file != ambiguous.file) - TRY(builder.try_append(from.file_char())); - else if (from.rank != ambiguous.rank) - TRY(builder.try_append(from.rank_char())); - else - TRY(builder.try_append(TRY(from.to_algebraic()))); - } - - if (is_capture) { - if (piece.type == Type::Pawn && !is_ambiguous) - TRY(builder.try_append(from.file_char())); - TRY(builder.try_append('x')); - } - - TRY(builder.try_append(TRY(to.to_algebraic()))); - - if (promote_to != Type::None && promote_to != Type::Pawn) { - TRY(builder.try_append('=')); - TRY(builder.try_append(char_for_piece(promote_to, Notation::Algebraic).value())); - } - - if (is_mate) - TRY(builder.try_append('#')); - else if (is_check) - TRY(builder.try_append('+')); - - return builder.to_string(); -} - -Board::Board() -{ - // Fill empty spaces. - for (int rank = 2; rank < 6; ++rank) { - for (int file = 0; file < 8; ++file) { - set_piece({ rank, file }, EmptyPiece); - } - } - - // Fill white pawns. - for (int file = 0; file < 8; ++file) { - set_piece({ 1, file }, { Color::White, Type::Pawn }); - } - - // Fill black pawns. - for (int file = 0; file < 8; ++file) { - set_piece({ 6, file }, { Color::Black, Type::Pawn }); - } - - // Fill while pieces. - set_piece(Square("a1"), { Color::White, Type::Rook }); - set_piece(Square("b1"), { Color::White, Type::Knight }); - set_piece(Square("c1"), { Color::White, Type::Bishop }); - set_piece(Square("d1"), { Color::White, Type::Queen }); - set_piece(Square("e1"), { Color::White, Type::King }); - set_piece(Square("f1"), { Color::White, Type::Bishop }); - set_piece(Square("g1"), { Color::White, Type::Knight }); - set_piece(Square("h1"), { Color::White, Type::Rook }); - - // Fill black pieces. - set_piece(Square("a8"), { Color::Black, Type::Rook }); - set_piece(Square("b8"), { Color::Black, Type::Knight }); - set_piece(Square("c8"), { Color::Black, Type::Bishop }); - set_piece(Square("d8"), { Color::Black, Type::Queen }); - set_piece(Square("e8"), { Color::Black, Type::King }); - set_piece(Square("f8"), { Color::Black, Type::Bishop }); - set_piece(Square("g8"), { Color::Black, Type::Knight }); - set_piece(Square("h8"), { Color::Black, Type::Rook }); -} - -Board Board::clone_without_history() const -{ - // Note: When used in the MCTSTree, the board doesn't need to have all information about previous states. - // It spares a huge amount of memory. - auto result = *this; - result.m_moves.clear(); - result.m_previous_states.clear(); - return result; -} - -ErrorOr Board::to_fen() const -{ - StringBuilder builder; - - // 1. Piece placement - int empty = 0; - for (int rank = 0; rank < 8; rank++) { - for (int file = 0; file < 8; file++) { - Piece const p(get_piece({ 7 - rank, file })); - if (p.type == Type::None) { - empty++; - continue; - } - if (empty > 0) { - TRY(builder.try_appendff("{}", empty)); - empty = 0; - } - auto const piece = char_for_piece(p.type, Notation::FEN).value(); - if (p.color == Color::Black) - TRY(builder.try_append(to_ascii_lowercase(piece))); - else - TRY(builder.try_append(piece)); - } - if (empty > 0) { - TRY(builder.try_appendff("{}", empty)); - empty = 0; - } - if (rank < 7) - TRY(builder.try_append('/')); - } - - // 2. Active color - VERIFY(m_turn != Color::None); - TRY(builder.try_append(m_turn == Color::White ? " w "sv : " b "sv)); - - // 3. Castling availability - if (m_white_can_castle_kingside) - TRY(builder.try_append('K')); - if (m_white_can_castle_queenside) - TRY(builder.try_append('Q')); - if (m_black_can_castle_kingside) - TRY(builder.try_append('k')); - if (m_black_can_castle_queenside) - TRY(builder.try_append('q')); - TRY(builder.try_append(' ')); - - // 4. En passant target square - if (!m_last_move.has_value()) - TRY(builder.try_append('-')); - else if (m_last_move.value().piece.type == Type::Pawn) { - if (m_last_move.value().from.rank == 1 && m_last_move.value().to.rank == 3) - TRY(builder.try_append(TRY(Square(m_last_move.value().to.rank - 1, m_last_move.value().to.file).to_algebraic()))); - else if (m_last_move.value().from.rank == 6 && m_last_move.value().to.rank == 4) - TRY(builder.try_append(TRY(Square(m_last_move.value().to.rank + 1, m_last_move.value().to.file).to_algebraic()))); - else - TRY(builder.try_append('-')); - } else { - TRY(builder.try_append('-')); - } - TRY(builder.try_append(' ')); - - // 5. Halfmove clock - TRY(builder.try_appendff("{}", (min(m_moves_since_capture, m_moves_since_pawn_advance)))); - TRY(builder.try_append(' ')); - - // 6. Fullmove number - TRY(builder.try_appendff("{}", (1 + m_moves.size() / 2))); - - return builder.to_string(); -} - -Piece Board::get_piece(Square const& square) const -{ - VERIFY(square.in_bounds()); - return m_board[square.rank][square.file]; -} - -Piece Board::set_piece(Square const& square, Piece const& piece) -{ - VERIFY(square.in_bounds()); - return m_board[square.rank][square.file] = piece; -} - -bool Board::is_legal_promotion(Move const& move, Color color) const -{ - auto piece = get_piece(move.from); - - if (move.promote_to == Type::Pawn || move.promote_to == Type::King) { - // attempted promotion to invalid piece - return false; - } - - if (piece.type != Type::Pawn && move.promote_to != Type::None) { - // attempted promotion from invalid piece - return false; - } - - int promotion_rank = (color == Color::White) ? 7 : 0; - - if (move.to.rank != promotion_rank && move.promote_to != Type::None) { - // attempted promotion from invalid rank - return false; - } - - if (piece.type == Type::Pawn && move.to.rank == promotion_rank && move.promote_to == Type::None) { - // attempted move to promotion rank without promoting - return false; - } - - return true; -} - -bool Board::is_legal(Move const& move, Color color) const -{ - if (color == Color::None) - color = turn(); - - if (!is_legal_no_check(move, color)) - return false; - - if (!is_legal_promotion(move, color)) - return false; - - Board clone = *this; - clone.apply_illegal_move(move, color); - if (clone.in_check(color)) - return false; - - // Don't allow castling through check or out of check. - Vector check_squares; - if (color == Color::White && move.from == Square("e1") && get_piece(Square("e1")) == Piece(Color::White, Type::King)) { - if (move.to == Square("a1") || move.to == Square("c1")) { - check_squares = { Square("e1"), Square("d1"), Square("c1") }; - } else if (move.to == Square("h1") || move.to == Square("g1")) { - check_squares = { Square("e1"), Square("f1"), Square("g1") }; - } - } else if (color == Color::Black && move.from == Square("e8") && get_piece(Square("e8")) == Piece(Color::Black, Type::King)) { - if (move.to == Square("a8") || move.to == Square("c8")) { - check_squares = { Square("e8"), Square("d8"), Square("c8") }; - } else if (move.to == Square("h8") || move.to == Square("g8")) { - check_squares = { Square("e8"), Square("f8"), Square("g8") }; - } - } - for (auto& square : check_squares) { - Board clone = *this; - clone.set_piece(move.from, EmptyPiece); - clone.set_piece(square, { color, Type::King }); - if (clone.in_check(color)) - return false; - } - - return true; -} - -bool Board::is_legal_no_check(Move const& move, Color color) const -{ - auto piece = get_piece(move.from); - - if (piece.color != color) - // attempted move of opponent's piece - return false; - - if (!move.to.in_bounds()) - // attempted move outside of board - return false; - - // Check castling first to allow dragging king onto the rook. - if (piece.type == Type::King) { - if (color == Color::White) { - if ((move.to == Square("a1") || move.to == Square("c1")) && m_white_can_castle_queenside && get_piece(Square("b1")).type == Type::None && get_piece(Square("c1")).type == Type::None && get_piece(Square("d1")).type == Type::None) { - return true; - } else if ((move.to == Square("h1") || move.to == Square("g1")) && m_white_can_castle_kingside && get_piece(Square("f1")).type == Type::None && get_piece(Square("g1")).type == Type::None) { - return true; - } - } else { - if ((move.to == Square("a8") || move.to == Square("c8")) && m_black_can_castle_queenside && get_piece(Square("b8")).type == Type::None && get_piece(Square("c8")).type == Type::None && get_piece(Square("d8")).type == Type::None) { - return true; - } else if ((move.to == Square("h8") || move.to == Square("g8")) && m_black_can_castle_kingside && get_piece(Square("f8")).type == Type::None && get_piece(Square("g8")).type == Type::None) { - return true; - } - } - } - - if (piece.color == get_piece(move.to).color) - // Attempted move to a square occupied by a piece of the same color. - return false; - - if (piece.type == Type::Pawn) { - int dir = (color == Color::White) ? +1 : -1; - int start_rank = (color == Color::White) ? 1 : 6; - - if (move.from.rank == start_rank && move.to.rank == move.from.rank + (2 * dir) && move.to.file == move.from.file - && get_piece(move.to).type == Type::None && get_piece({ move.from.rank + dir, move.from.file }).type == Type::None) { - // 2 square pawn move from initial position. - return true; - } - - if (move.to.rank != move.from.rank + dir) - // attempted backwards or sideways move - return false; - - if (move.to.file == move.from.file && get_piece(move.to).type == Type::None) { - // Regular pawn move. - return true; - } - - if (move.to.file == move.from.file + 1 || move.to.file == move.from.file - 1) { - int other_start_rank = (color == Color::White) ? 6 : 1; - int en_passant_rank = (color == Color::White) ? 4 : 3; - Move en_passant_last_move = { { other_start_rank, move.to.file }, { en_passant_rank, move.to.file } }; - if (get_piece(move.to).color == opposing_color(color)) { - // Pawn capture. - return true; - } - if (m_last_move.has_value() && move.from.rank == en_passant_rank && m_last_move.value() == en_passant_last_move - && get_piece(en_passant_last_move.to) == Piece(opposing_color(color), Type::Pawn)) { - // En passant. - return true; - } - } - - return false; - } else if (piece.type == Type::Knight) { - int rank_delta = abs(move.to.rank - move.from.rank); - int file_delta = abs(move.to.file - move.from.file); - if (max(rank_delta, file_delta) == 2 && min(rank_delta, file_delta) == 1) { - return true; - } - } else if (piece.type == Type::Bishop) { - int rank_delta = move.to.rank - move.from.rank; - int file_delta = move.to.file - move.from.file; - if (abs(rank_delta) == abs(file_delta)) { - int dr = rank_delta / abs(rank_delta); - int df = file_delta / abs(file_delta); - for (Square sq = move.from; sq != move.to; sq.rank += dr, sq.file += df) { - if (get_piece(sq).type != Type::None && sq != move.from) { - return false; - } - } - return true; - } - } else if (piece.type == Type::Rook) { - int rank_delta = move.to.rank - move.from.rank; - int file_delta = move.to.file - move.from.file; - if (rank_delta == 0 || file_delta == 0) { - int dr = (rank_delta) ? rank_delta / abs(rank_delta) : 0; - int df = (file_delta) ? file_delta / abs(file_delta) : 0; - for (Square sq = move.from; sq != move.to; sq.rank += dr, sq.file += df) { - if (get_piece(sq).type != Type::None && sq != move.from) { - return false; - } - } - return true; - } - } else if (piece.type == Type::Queen) { - int rank_delta = move.to.rank - move.from.rank; - int file_delta = move.to.file - move.from.file; - if (abs(rank_delta) == abs(file_delta) || rank_delta == 0 || file_delta == 0) { - int dr = (rank_delta) ? rank_delta / abs(rank_delta) : 0; - int df = (file_delta) ? file_delta / abs(file_delta) : 0; - for (Square sq = move.from; sq != move.to; sq.rank += dr, sq.file += df) { - if (get_piece(sq).type != Type::None && sq != move.from) { - return false; - } - } - return true; - } - } else if (piece.type == Type::King) { - int rank_delta = move.to.rank - move.from.rank; - int file_delta = move.to.file - move.from.file; - if (abs(rank_delta) <= 1 && abs(file_delta) <= 1) { - return true; - } - } - - return false; -} - -bool Board::in_check(Color color) const -{ - Square king_square = { 50, 50 }; - Square::for_each([&](Square const& square) { - if (get_piece(square) == Piece(color, Type::King)) { - king_square = square; - return IterationDecision::Break; - } - return IterationDecision::Continue; - }); - - bool check = false; - Square::for_each([&](Square const& square) { - if (is_legal_no_check({ square, king_square }, opposing_color(color))) { - check = true; - return IterationDecision::Break; - } - return IterationDecision::Continue; - }); - - return check; -} - -bool Board::apply_move(Move const& move, Color color) -{ - if (color == Color::None) - color = turn(); - - if (!is_legal(move, color)) - return false; - - const_cast(move).piece = get_piece(move.from); - - return apply_illegal_move(move, color); -} - -bool Board::apply_illegal_move(Move const& move, Color color) -{ - auto state = Traits::hash(*this); - auto state_count = 0; - if (m_previous_states.contains(state)) - state_count = m_previous_states.get(state).value(); - - m_previous_states.set(state, state_count + 1); - m_moves.append(move); - - m_turn = opposing_color(color); - - m_last_move = move; - m_moves_since_capture++; - m_moves_since_pawn_advance++; - - if (move.from == Square("a1") || move.to == Square("a1") || move.from == Square("e1")) - m_white_can_castle_queenside = false; - if (move.from == Square("h1") || move.to == Square("h1") || move.from == Square("e1")) - m_white_can_castle_kingside = false; - if (move.from == Square("a8") || move.to == Square("a8") || move.from == Square("e8")) - m_black_can_castle_queenside = false; - if (move.from == Square("h8") || move.to == Square("h8") || move.from == Square("e8")) - m_black_can_castle_kingside = false; - - if (color == Color::White && move.from == Square("e1") && get_piece(Square("e1")) == Piece(Color::White, Type::King)) { - if (move.to == Square("a1") || move.to == Square("c1")) { - set_piece(Square("e1"), EmptyPiece); - set_piece(Square("a1"), EmptyPiece); - set_piece(Square("c1"), { Color::White, Type::King }); - set_piece(Square("d1"), { Color::White, Type::Rook }); - return true; - } else if (move.to == Square("h1") || move.to == Square("g1")) { - set_piece(Square("e1"), EmptyPiece); - set_piece(Square("h1"), EmptyPiece); - set_piece(Square("g1"), { Color::White, Type::King }); - set_piece(Square("f1"), { Color::White, Type::Rook }); - return true; - } - } else if (color == Color::Black && move.from == Square("e8") && get_piece(Square("e8")) == Piece(Color::Black, Type::King)) { - if (move.to == Square("a8") || move.to == Square("c8")) { - set_piece(Square("e8"), EmptyPiece); - set_piece(Square("a8"), EmptyPiece); - set_piece(Square("c8"), { Color::Black, Type::King }); - set_piece(Square("d8"), { Color::Black, Type::Rook }); - return true; - } else if (move.to == Square("h8") || move.to == Square("g8")) { - set_piece(Square("e8"), EmptyPiece); - set_piece(Square("h8"), EmptyPiece); - set_piece(Square("g8"), { Color::Black, Type::King }); - set_piece(Square("f8"), { Color::Black, Type::Rook }); - return true; - } - } - - if (move.piece.type == Type::Pawn) - m_moves_since_pawn_advance = 0; - - if (get_piece(move.to).color != Color::None) { - const_cast(move).is_capture = true; - m_moves_since_capture = 0; - } - - if (get_piece(move.from).type == Type::Pawn && ((color == Color::Black && move.to.rank == 0) || (color == Color::White && move.to.rank == 7))) { - // Pawn Promotion - set_piece(move.to, { color, move.promote_to }); - set_piece(move.from, EmptyPiece); - - if (in_check(m_turn)) - const_cast(move).is_check = true; - - return true; - } - - if (get_piece(move.from).type == Type::Pawn && move.from.file != move.to.file && get_piece(move.to).type == Type::None) { - // En passant. - if (color == Color::White) { - set_piece({ move.to.rank - 1, move.to.file }, EmptyPiece); - } else { - set_piece({ move.to.rank + 1, move.to.file }, EmptyPiece); - } - const_cast(move).is_capture = true; - m_moves_since_capture = 0; - } - - Square::for_each([&](Square sq) { - // Ambiguous Move - if (sq != move.from && get_piece(sq).type == move.piece.type && get_piece(sq).color == move.piece.color) { - if (is_legal(Move(sq, move.to), get_piece(sq).color)) { - m_moves.last().is_ambiguous = true; - m_moves.last().ambiguous = sq; - - return IterationDecision::Break; - } - } - return IterationDecision::Continue; - }); - - set_piece(move.to, get_piece(move.from)); - set_piece(move.from, EmptyPiece); - - if (in_check(m_turn)) - const_cast(move).is_check = true; - - return true; -} - -Move Board::random_move(Color color) const -{ - if (color == Color::None) - color = turn(); - - Move move = { { 50, 50 }, { 50, 50 } }; - int probability = 1; - generate_moves([&](Move m) { - if (rand() % probability == 0) - move = m; - ++probability; - return IterationDecision::Continue; - }); - - return move; -} - -Board::Result Board::game_result() const -{ - if (m_resigned != Color::None) - return (m_resigned == Color::White) ? Result::WhiteResign : Result::BlackResign; - - bool sufficient_material = false; - bool no_more_pieces_allowed = false; - Optional bishop; - Square::for_each([&](Square sq) { - if (get_piece(sq).type == Type::Queen || get_piece(sq).type == Type::Rook || get_piece(sq).type == Type::Pawn) { - sufficient_material = true; - return IterationDecision::Break; - } - - if (get_piece(sq).type != Type::None && get_piece(sq).type != Type::King && no_more_pieces_allowed) { - sufficient_material = true; - return IterationDecision::Break; - } - - if (get_piece(sq).type == Type::Knight) - no_more_pieces_allowed = true; - - if (get_piece(sq).type == Type::Bishop) { - if (bishop.has_value()) { - if (get_piece(sq).color == get_piece(bishop.value()).color) { - sufficient_material = true; - return IterationDecision::Break; - } else if (sq.is_light() != bishop.value().is_light()) { - sufficient_material = true; - return IterationDecision::Break; - } - no_more_pieces_allowed = true; - } else { - bishop = sq; - } - } - - return IterationDecision::Continue; - }); - - if (!sufficient_material) - return Result::InsufficientMaterial; - - bool are_legal_moves = false; - generate_moves([&]([[maybe_unused]] Move m) { - are_legal_moves = true; - return IterationDecision::Break; - }); - - if (are_legal_moves) { - if (m_moves_since_capture >= 75 * 2 && m_moves_since_pawn_advance >= 75 * 2) - return Result::SeventyFiveMoveRule; - - if ((m_moves_since_capture >= 50 * 2 && m_moves_since_pawn_advance == 50 * 2) - || (m_moves_since_pawn_advance >= 50 * 2 && m_moves_since_capture == 50 * 2)) - return Result::FiftyMoveRule; - - auto repeats = m_previous_states.get(Traits::hash(*this)); - if (repeats.has_value()) { - if (repeats.value() == 3) - return Result::ThreeFoldRepetition; - if (repeats.value() >= 5) - return Result::FiveFoldRepetition; - } - - return Result::NotFinished; - } - - if (in_check(turn())) { - const_cast&>(m_moves).last().is_mate = true; - return Result::CheckMate; - } - - return Result::StaleMate; -} - -Color Board::game_winner() const -{ - if (game_result() == Result::CheckMate) - return opposing_color(turn()); - - return Color::None; -} - -int Board::game_score() const -{ - switch (game_winner()) { - case Color::White: - return +1; - case Color::Black: - return -1; - case Color::None: - return 0; - } - return 0; -} - -bool Board::game_finished() const -{ - return game_result() != Result::NotFinished; -} - -int Board::material_imbalance() const -{ - int imbalance = 0; - Square::for_each([&](Square square) { - int value = 0; - switch (get_piece(square).type) { - case Type::Pawn: - value = 1; - break; - case Type::Knight: - case Type::Bishop: - value = 3; - break; - case Type::Rook: - value = 5; - break; - case Type::Queen: - value = 9; - break; - default: - break; - } - - if (get_piece(square).color == Color::White) { - imbalance += value; - } else { - imbalance -= value; - } - return IterationDecision::Continue; - }); - return imbalance; -} - -bool Board::is_promotion_move(Move const& move, Color color) const -{ - if (color == Color::None) - color = turn(); - - int promotion_rank = (color == Color::White) ? 7 : 0; - if (move.to.rank != promotion_rank) - return false; - - if (get_piece(move.from).type != Type::Pawn) - return false; - - Move queen_move = move; - queen_move.promote_to = Type::Queen; - if (!is_legal(queen_move, color)) - return false; - - return true; -} - -bool Board::operator==(Board const& other) const -{ - bool equal_squares = true; - Square::for_each([&](Square sq) { - if (get_piece(sq) != other.get_piece(sq)) { - equal_squares = false; - return IterationDecision::Break; - } - return IterationDecision::Continue; - }); - if (!equal_squares) - return false; - - if (m_white_can_castle_queenside != other.m_white_can_castle_queenside) - return false; - if (m_white_can_castle_kingside != other.m_white_can_castle_kingside) - return false; - if (m_black_can_castle_queenside != other.m_black_can_castle_queenside) - return false; - if (m_black_can_castle_kingside != other.m_black_can_castle_kingside) - return false; - - return turn() == other.turn(); -} - -void Board::set_resigned(Chess::Color c) -{ - m_resigned = c; -} - -StringView Board::result_to_string(Result result, Color turn) -{ - switch (result) { - case Result::CheckMate: - VERIFY(turn != Chess::Color::None); - return turn == Chess::Color::White ? "Black wins by Checkmate"sv : "White wins by Checkmate"sv; - case Result::WhiteResign: - return "Black wins by Resignation"sv; - case Result::BlackResign: - return "White wins by Resignation"sv; - case Result::StaleMate: - return "Draw by Stalemate"sv; - case Chess::Board::Result::FiftyMoveRule: - return "Draw by 50 move rule"sv; - case Chess::Board::Result::SeventyFiveMoveRule: - return "Draw by 75 move rule"sv; - case Chess::Board::Result::ThreeFoldRepetition: - return "Draw by threefold repetition"sv; - case Chess::Board::Result::FiveFoldRepetition: - return "Draw by fivefold repetition"sv; - case Chess::Board::Result::InsufficientMaterial: - return "Draw by insufficient material"sv; - case Chess::Board::Result::NotFinished: - return "Game not finished"sv; - default: - VERIFY_NOT_REACHED(); - } -} - -StringView Board::result_to_points_string(Result result, Color turn) -{ - switch (result) { - case Result::CheckMate: - VERIFY(turn != Chess::Color::None); - return turn == Chess::Color::White ? "0-1"sv : "1-0"sv; - case Result::WhiteResign: - return "0-1"sv; - case Result::BlackResign: - return "1-0"sv; - case Result::StaleMate: - case Chess::Board::Result::FiftyMoveRule: - case Chess::Board::Result::SeventyFiveMoveRule: - case Chess::Board::Result::ThreeFoldRepetition: - case Chess::Board::Result::FiveFoldRepetition: - case Chess::Board::Result::InsufficientMaterial: - return "1/2-1/2"sv; - case Chess::Board::Result::NotFinished: - return "*"sv; - default: - VERIFY_NOT_REACHED(); - } -} - -} diff --git a/Userland/Libraries/LibChess/Chess.h b/Userland/Libraries/LibChess/Chess.h deleted file mode 100644 index 47d2691bcfa..00000000000 --- a/Userland/Libraries/LibChess/Chess.h +++ /dev/null @@ -1,320 +0,0 @@ -/* - * Copyright (c) 2020, the SerenityOS developers. - * Copyright (c) 2023, Sam Atkins - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include -#include - -namespace Chess { - -enum class Type : u8 { - Pawn, - Knight, - Bishop, - Rook, - Queen, - King, - None, -}; - -enum class Notation { - Algebraic, - FEN, -}; -Optional char_for_piece(Type, Notation); -Type piece_from_char(char); - -enum class Color : u8 { - White, - Black, - None, -}; - -Color opposing_color(Color color); - -struct Piece { - constexpr Piece() - : color(Color::None) - , type(Type::None) - { - } - constexpr Piece(Color c, Type t) - : color(c) - , type(t) - { - } - Color color : 4; - Type type : 4; - bool operator==(Piece const& other) const { return color == other.color && type == other.type; } -}; - -constexpr Piece EmptyPiece = { Color::None, Type::None }; - -struct Square { - i8 rank; // zero indexed; - i8 file; - - Square(StringView name); - - Square(char const name[3]) - : Square({ name, 2 }) - { - } - - Square(int const& rank, int const& file) - : rank(rank) - , file(file) - { - } - bool operator==(Square const& other) const { return rank == other.rank && file == other.file; } - - template - static void for_each(Callback callback) - { - for (int rank = 0; rank < 8; ++rank) { - for (int file = 0; file < 8; ++file) { - if (callback(Square(rank, file)) == IterationDecision::Break) - return; - } - } - } - - bool in_bounds() const { return rank >= 0 && file >= 0 && rank < 8 && file < 8; } - bool is_light() const { return (rank % 2) != (file % 2); } - - char file_char() const; - char rank_char() const; - ErrorOr to_algebraic() const; -}; - -class Board; - -struct Move { - Square from; - Square to; - Type promote_to; - Piece piece; - bool is_check : 1 = false; - bool is_mate : 1 = false; - bool is_capture : 1 = false; - bool is_ambiguous : 1 = false; - Square ambiguous { 50, 50 }; - Move(StringView long_algebraic); - Move(Square const& from, Square const& to, Type const& promote_to = Type::None) - : from(from) - , to(to) - , promote_to(promote_to) - { - } - bool operator==(Move const& other) const { return from == other.from && to == other.to && promote_to == other.promote_to; } - - static Move from_algebraic(StringView algebraic, Color const turn, Board const& board); - ErrorOr to_long_algebraic() const; - ErrorOr to_algebraic() const; -}; - -class Board { -public: - Board(); - Board clone_without_history() const; - - Piece get_piece(Square const&) const; - Piece set_piece(Square const&, Piece const&); - - bool is_legal(Move const&, Color color = Color::None) const; - bool in_check(Color color) const; - - bool is_promotion_move(Move const&, Color color = Color::None) const; - - bool apply_move(Move const&, Color color = Color::None); - Optional const& last_move() const { return m_last_move; } - - ErrorOr to_fen() const; - - enum class Result { - CheckMate, - StaleMate, - WhiteResign, - BlackResign, - FiftyMoveRule, - SeventyFiveMoveRule, - ThreeFoldRepetition, - FiveFoldRepetition, - InsufficientMaterial, - NotFinished, - }; - - static StringView result_to_string(Result, Color turn); - static StringView result_to_points_string(Result, Color turn); - - template - void generate_moves(Callback callback, Color color = Color::None) const; - Move random_move(Color color = Color::None) const; - Result game_result() const; - Color game_winner() const; - int game_score() const; - bool game_finished() const; - void set_resigned(Color); - int material_imbalance() const; - - Color turn() const { return m_turn; } - Vector const& moves() const { return m_moves; } - - bool operator==(Board const& other) const; - -private: - bool is_legal_no_check(Move const&, Color color) const; - bool is_legal_promotion(Move const&, Color color) const; - bool apply_illegal_move(Move const&, Color color); - - Piece m_board[8][8]; - Optional m_last_move; - short m_moves_since_capture { 0 }; - short m_moves_since_pawn_advance { 0 }; - - Color m_turn : 2 { Color::White }; - Color m_resigned : 2 { Color::None }; - - bool m_white_can_castle_kingside : 1 { true }; - bool m_white_can_castle_queenside : 1 { true }; - bool m_black_can_castle_kingside : 1 { true }; - bool m_black_can_castle_queenside : 1 { true }; - - // We trust that hash collisions will not happen to save lots of memory and time. - HashMap m_previous_states; - Vector m_moves; - friend struct Traits; -}; - -template -void Board::generate_moves(Callback callback, Color color) const -{ - if (color == Color::None) - color = turn(); - - auto try_move = [&](Move m) { - if (is_legal(m, color)) { - if (callback(m) == IterationDecision::Break) - return false; - } - return true; - }; - - Square::for_each([&](Square sq) { - auto piece = get_piece(sq); - if (piece.color != color) - return IterationDecision::Continue; - - bool keep_going = true; - if (piece.type == Type::Pawn) { - for (auto& piece : Vector({ Type::None, Type::Knight, Type::Bishop, Type::Rook, Type::Queen })) { - keep_going = try_move({ sq, { sq.rank + 1, sq.file }, piece }) - && try_move({ sq, { sq.rank + 2, sq.file }, piece }) - && try_move({ sq, { sq.rank - 1, sq.file }, piece }) - && try_move({ sq, { sq.rank - 2, sq.file }, piece }) - && try_move({ sq, { sq.rank + 1, sq.file + 1 }, piece }) - && try_move({ sq, { sq.rank + 1, sq.file - 1 }, piece }) - && try_move({ sq, { sq.rank - 1, sq.file + 1 }, piece }) - && try_move({ sq, { sq.rank - 1, sq.file - 1 }, piece }); - } - } else if (piece.type == Type::Knight) { - keep_going = try_move({ sq, { sq.rank + 2, sq.file + 1 } }) - && try_move({ sq, { sq.rank + 2, sq.file - 1 } }) - && try_move({ sq, { sq.rank + 1, sq.file + 2 } }) - && try_move({ sq, { sq.rank + 1, sq.file - 2 } }) - && try_move({ sq, { sq.rank - 2, sq.file + 1 } }) - && try_move({ sq, { sq.rank - 2, sq.file - 1 } }) - && try_move({ sq, { sq.rank - 1, sq.file + 2 } }) - && try_move({ sq, { sq.rank - 1, sq.file - 2 } }); - } else if (piece.type == Type::Bishop) { - for (int dr = -1; dr <= 1; dr += 2) { - for (int df = -1; df <= 1; df += 2) { - for (Square to = sq; to.in_bounds(); to = { to.rank + dr, to.file + df }) { - if (!try_move({ sq, to })) - return IterationDecision::Break; - } - } - } - } else if (piece.type == Type::Rook) { - for (int dr = -1; dr <= 1; dr++) { - for (int df = -1; df <= 1; df++) { - if ((dr == 0) != (df == 0)) { - for (Square to = sq; to.in_bounds(); to = { to.rank + dr, to.file + df }) { - if (!try_move({ sq, to })) - return IterationDecision::Break; - } - } - } - } - } else if (piece.type == Type::Queen) { - for (int dr = -1; dr <= 1; dr++) { - for (int df = -1; df <= 1; df++) { - if (dr != 0 || df != 0) { - for (Square to = sq; to.in_bounds(); to = { to.rank + dr, to.file + df }) { - if (!try_move({ sq, to })) - return IterationDecision::Break; - } - } - } - } - } else if (piece.type == Type::King) { - for (int dr = -1; dr <= 1; dr++) { - for (int df = -1; df <= 1; df++) { - if (!try_move({ sq, { sq.rank + dr, sq.file + df } })) - return IterationDecision::Break; - } - } - - // Castling moves. - if (sq == Square("e1")) { - keep_going = try_move({ sq, Square("c1") }) && try_move({ sq, Square("g1") }); - } else if (sq == Square("e8")) { - keep_going = try_move({ sq, Square("c8") }) && try_move({ sq, Square("g8") }); - } - } - - if (keep_going) { - return IterationDecision::Continue; - } else { - return IterationDecision::Break; - } - }); -} - -} - -template<> -struct AK::Traits : public DefaultTraits { - static unsigned hash(Chess::Piece const& piece) - { - return pair_int_hash(static_cast(piece.color), static_cast(piece.type)); - } -}; - -template<> -struct AK::Traits : public DefaultTraits { - static unsigned hash(Chess::Board const& chess) - { - unsigned hash = 0; - hash = pair_int_hash(hash, static_cast(chess.m_white_can_castle_queenside)); - hash = pair_int_hash(hash, static_cast(chess.m_white_can_castle_kingside)); - hash = pair_int_hash(hash, static_cast(chess.m_black_can_castle_queenside)); - hash = pair_int_hash(hash, static_cast(chess.m_black_can_castle_kingside)); - - Chess::Square::for_each([&](Chess::Square sq) { - hash = pair_int_hash(hash, Traits::hash(chess.get_piece(sq))); - return IterationDecision::Continue; - }); - - return hash; - } -}; diff --git a/Userland/Libraries/LibChess/UCICommand.cpp b/Userland/Libraries/LibChess/UCICommand.cpp deleted file mode 100644 index d2cad2a5aae..00000000000 --- a/Userland/Libraries/LibChess/UCICommand.cpp +++ /dev/null @@ -1,539 +0,0 @@ -/* - * Copyright (c) 2020, the SerenityOS developers. - * Copyright (c) 2023, Sam Atkins - * Copyright (c) 2023, Tim Ledbetter - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "UCICommand.h" -#include - -namespace Chess::UCI { - -ErrorOr> UCICommand::from_string(StringView command) -{ - auto tokens = command.split_view(' '); - VERIFY(tokens[0] == "uci"); - VERIFY(tokens.size() == 1); - return adopt_nonnull_own_or_enomem(new (nothrow) UCICommand); -} - -ErrorOr UCICommand::to_string() const -{ - return "uci\n"_string; -} - -ErrorOr> DebugCommand::from_string(StringView command) -{ - auto tokens = command.split_view(' '); - VERIFY(tokens[0] == "debug"); - VERIFY(tokens.size() == 2); - if (tokens[1] == "on") - return adopt_nonnull_own_or_enomem(new (nothrow) DebugCommand(Flag::On)); - if (tokens[1] == "off") - return adopt_nonnull_own_or_enomem(new (nothrow) DebugCommand(Flag::Off)); - - VERIFY_NOT_REACHED(); -} - -ErrorOr DebugCommand::to_string() const -{ - if (flag() == Flag::On) { - return "debug on\n"_string; - } else { - return "debug off\n"_string; - } -} - -ErrorOr> IsReadyCommand::from_string(StringView command) -{ - auto tokens = command.split_view(' '); - VERIFY(tokens[0] == "isready"); - VERIFY(tokens.size() == 1); - return adopt_nonnull_own_or_enomem(new (nothrow) IsReadyCommand); -} - -ErrorOr IsReadyCommand::to_string() const -{ - return "isready\n"_string; -} - -ErrorOr> SetOptionCommand::from_string(StringView command) -{ - auto tokens = command.split_view(' '); - VERIFY(tokens[0] == "setoption"); - VERIFY(tokens[1] == "name"); - VERIFY(tokens.size() > 2); - - StringBuilder name; - StringBuilder value; - bool in_name = false; - bool in_value = false; - for (auto& part : tokens) { - if (in_name) { - if (part == "value") { - in_name = false; - in_value = true; - continue; - } - TRY(name.try_append(part)); - TRY(name.try_append(' ')); - continue; - } - if (in_value) { - TRY(value.try_append(part)); - TRY(value.try_append(' ')); - continue; - } - if (part == "name") { - in_name = true; - continue; - } - } - - VERIFY(!name.is_empty()); - - auto name_string = TRY(String::from_utf8(name.string_view().trim_whitespace())); - auto value_string = TRY(String::from_utf8(value.string_view().trim_whitespace())); - return adopt_nonnull_own_or_enomem(new (nothrow) SetOptionCommand(name_string, value_string)); -} - -ErrorOr SetOptionCommand::to_string() const -{ - StringBuilder builder; - TRY(builder.try_append("setoption name "sv)); - TRY(builder.try_append(name())); - if (value().has_value()) { - TRY(builder.try_append(" value "sv)); - TRY(builder.try_append(value().value())); - } - TRY(builder.try_append('\n')); - return builder.to_string(); -} - -ErrorOr> PositionCommand::from_string(StringView command) -{ - auto tokens = command.split_view(' '); - VERIFY(tokens.size() >= 3); - VERIFY(tokens[0] == "position"); - VERIFY(tokens[2] == "moves"); - - Optional fen; - if (tokens[1] != "startpos") - fen = TRY(String::from_utf8(tokens[1])); - - Vector moves; - for (size_t i = 3; i < tokens.size(); ++i) { - TRY(moves.try_append(Move(tokens[i]))); - } - return adopt_nonnull_own_or_enomem(new (nothrow) PositionCommand(move(fen), move(moves))); -} - -ErrorOr PositionCommand::to_string() const -{ - StringBuilder builder; - TRY(builder.try_append("position "sv)); - if (fen().has_value()) { - TRY(builder.try_append(fen().value())); - } else { - TRY(builder.try_append("startpos "sv)); - } - TRY(builder.try_append("moves"sv)); - for (auto& move : moves()) { - TRY(builder.try_append(' ')); - TRY(builder.try_append(TRY(move.to_long_algebraic()))); - } - TRY(builder.try_append('\n')); - return builder.to_string(); -} - -ErrorOr> GoCommand::from_string(StringView command) -{ - auto tokens = command.split_view(' '); - VERIFY(tokens[0] == "go"); - - auto go_command = TRY(adopt_nonnull_own_or_enomem(new (nothrow) GoCommand)); - for (size_t i = 1; i < tokens.size(); ++i) { - if (tokens[i] == "searchmoves") { - VERIFY_NOT_REACHED(); - } else if (tokens[i] == "ponder") { - go_command->ponder = true; - } else if (tokens[i] == "wtime") { - VERIFY(i++ < tokens.size()); - go_command->wtime = tokens[i].to_number().value(); - } else if (tokens[i] == "btime") { - VERIFY(i++ < tokens.size()); - go_command->btime = tokens[i].to_number().value(); - } else if (tokens[i] == "winc") { - VERIFY(i++ < tokens.size()); - go_command->winc = tokens[i].to_number().value(); - } else if (tokens[i] == "binc") { - VERIFY(i++ < tokens.size()); - go_command->binc = tokens[i].to_number().value(); - } else if (tokens[i] == "movestogo") { - VERIFY(i++ < tokens.size()); - go_command->movestogo = tokens[i].to_number().value(); - } else if (tokens[i] == "depth") { - VERIFY(i++ < tokens.size()); - go_command->depth = tokens[i].to_number().value(); - } else if (tokens[i] == "nodes") { - VERIFY(i++ < tokens.size()); - go_command->nodes = tokens[i].to_number().value(); - } else if (tokens[i] == "mate") { - VERIFY(i++ < tokens.size()); - go_command->mate = tokens[i].to_number().value(); - } else if (tokens[i] == "movetime") { - VERIFY(i++ < tokens.size()); - go_command->movetime = tokens[i].to_number().value(); - } else if (tokens[i] == "infinite") { - go_command->infinite = true; - } - } - - return go_command; -} - -ErrorOr GoCommand::to_string() const -{ - StringBuilder builder; - TRY(builder.try_append("go"sv)); - - if (searchmoves.has_value()) { - TRY(builder.try_append(" searchmoves"sv)); - for (auto& move : searchmoves.value()) { - TRY(builder.try_append(' ')); - TRY(builder.try_append(TRY(move.to_long_algebraic()))); - } - } - - if (ponder) - TRY(builder.try_append(" ponder"sv)); - if (wtime.has_value()) - TRY(builder.try_appendff(" wtime {}", wtime.value())); - if (btime.has_value()) - TRY(builder.try_appendff(" btime {}", btime.value())); - if (winc.has_value()) - TRY(builder.try_appendff(" winc {}", winc.value())); - if (binc.has_value()) - TRY(builder.try_appendff(" binc {}", binc.value())); - if (movestogo.has_value()) - TRY(builder.try_appendff(" movestogo {}", movestogo.value())); - if (depth.has_value()) - TRY(builder.try_appendff(" depth {}", depth.value())); - if (nodes.has_value()) - TRY(builder.try_appendff(" nodes {}", nodes.value())); - if (mate.has_value()) - TRY(builder.try_appendff(" mate {}", mate.value())); - if (movetime.has_value()) - TRY(builder.try_appendff(" movetime {}", movetime.value())); - if (infinite) - TRY(builder.try_append(" infinite"sv)); - - TRY(builder.try_append('\n')); - return builder.to_string(); -} - -ErrorOr> StopCommand::from_string(StringView command) -{ - auto tokens = command.split_view(' '); - VERIFY(tokens[0] == "stop"); - VERIFY(tokens.size() == 1); - return adopt_nonnull_own_or_enomem(new (nothrow) StopCommand); -} - -ErrorOr StopCommand::to_string() const -{ - return "stop\n"_string; -} - -ErrorOr> IdCommand::from_string(StringView command) -{ - auto tokens = command.split_view(' '); - VERIFY(tokens[0] == "id"); - StringBuilder value; - for (size_t i = 2; i < tokens.size(); ++i) { - if (i != 2) - TRY(value.try_append(' ')); - - TRY(value.try_append(tokens[i])); - } - - auto value_string = TRY(value.to_string()); - if (tokens[1] == "name") { - return adopt_nonnull_own_or_enomem(new (nothrow) IdCommand(Type::Name, value_string)); - } else if (tokens[1] == "author") { - return adopt_nonnull_own_or_enomem(new (nothrow) IdCommand(Type::Author, value_string)); - } - VERIFY_NOT_REACHED(); -} - -ErrorOr IdCommand::to_string() const -{ - StringBuilder builder; - TRY(builder.try_append("id "sv)); - if (field_type() == Type::Name) { - TRY(builder.try_append("name "sv)); - } else { - TRY(builder.try_append("author "sv)); - } - TRY(builder.try_append(value())); - TRY(builder.try_append('\n')); - return builder.to_string(); -} - -ErrorOr> UCIOkCommand::from_string(StringView command) -{ - auto tokens = command.split_view(' '); - VERIFY(tokens[0] == "uciok"); - VERIFY(tokens.size() == 1); - return adopt_nonnull_own_or_enomem(new (nothrow) UCIOkCommand); -} - -ErrorOr UCIOkCommand::to_string() const -{ - return "uciok\n"_string; -} - -ErrorOr> ReadyOkCommand::from_string(StringView command) -{ - auto tokens = command.split_view(' '); - VERIFY(tokens[0] == "readyok"); - VERIFY(tokens.size() == 1); - return adopt_nonnull_own_or_enomem(new (nothrow) ReadyOkCommand); -} - -ErrorOr ReadyOkCommand::to_string() const -{ - return "readyok\n"_string; -} - -ErrorOr> BestMoveCommand::from_string(StringView command) -{ - auto tokens = command.split_view(' '); - VERIFY(tokens[0] == "bestmove"); - VERIFY(tokens.size() == 2 || tokens.size() == 4); - auto best_move = Move(tokens[1]); - Optional move_to_ponder; - if (tokens.size() == 4) { - VERIFY(tokens[2] == "ponder"); - move_to_ponder = Move(tokens[3]); - } - - return adopt_nonnull_own_or_enomem(new (nothrow) BestMoveCommand(best_move, move_to_ponder)); -} - -ErrorOr BestMoveCommand::to_string() const -{ - StringBuilder builder; - TRY(builder.try_append("bestmove "sv)); - TRY(builder.try_append(TRY(move().to_long_algebraic()))); - if (move_to_ponder().has_value()) { - TRY(builder.try_append(" ponder "sv)); - TRY(builder.try_append(TRY(move_to_ponder()->to_long_algebraic()))); - } - TRY(builder.try_append('\n')); - return builder.to_string(); -} - -ErrorOr> InfoCommand::from_string(StringView command) -{ - auto tokens = command.split_view(' '); - VERIFY(tokens[0] == "info"); - - auto info_command = TRY(try_make()); - - auto parse_integer_token = [](StringView value_token) -> ErrorOr { - auto value_as_integer = value_token.to_number(); - if (!value_as_integer.has_value()) - return Error::from_string_literal("Expected integer token"); - - return value_as_integer.release_value(); - }; - - auto parse_line = [](auto const& move_tokens) -> ErrorOr> { - Vector moves; - TRY(moves.try_ensure_capacity(move_tokens.size())); - for (auto move_token : move_tokens) - moves.unchecked_append({ move_token }); - - return moves; - }; - - size_t i = 1; - while (i < tokens.size()) { - auto name = tokens[i++]; - if (name == "depth"sv) { - info_command->m_depth = TRY(parse_integer_token(tokens[i++])); - } else if (name == "seldepth"sv) { - info_command->m_seldepth = TRY(parse_integer_token(tokens[i++])); - } else if (name == "time"sv) { - info_command->m_time = TRY(parse_integer_token(tokens[i++])); - } else if (name == "nodes"sv) { - info_command->m_nodes = TRY(parse_integer_token(tokens[i++])); - } else if (name == "multipv"sv) { - info_command->m_multipv = TRY(parse_integer_token(tokens[i++])); - } else if (name == "score"sv) { - auto score_type_string = tokens[i++]; - ScoreType score_type; - if (score_type_string == "cp"sv) { - score_type = ScoreType::Centipawns; - } else if (score_type_string == "mate"sv) { - score_type = ScoreType::Mate; - } else { - return Error::from_string_literal("Invalid score type"); - } - auto score_value = TRY(parse_integer_token(tokens[i++])); - auto maybe_score_bound_string = tokens[i]; - auto score_bound = ScoreBound::None; - if (maybe_score_bound_string == "upperbound"sv) - score_bound = ScoreBound::Upper; - else if (maybe_score_bound_string == "lowerbound"sv) - score_bound = ScoreBound::Lower; - - if (score_bound != ScoreBound::None) - i++; - - info_command->m_score = Score { score_type, score_value, score_bound }; - } else if (name == "currmove"sv) { - info_command->m_currmove = Chess::Move { tokens[i++] }; - } else if (name == "currmovenumber"sv) { - info_command->m_currmovenumber = TRY(parse_integer_token(tokens[i++])); - } else if (name == "hashfull"sv) { - info_command->m_hashfull = TRY(parse_integer_token(tokens[i++])); - } else if (name == "nps"sv) { - info_command->m_nps = TRY(parse_integer_token(tokens[i++])); - } else if (name == "tbhits"sv) { - info_command->m_tbhits = TRY(parse_integer_token(tokens[i++])); - } else if (name == "cpuload"sv) { - info_command->m_cpuload = TRY(parse_integer_token(tokens[i++])); - } - // We assume the info types: pv, string, refutation, and currline, are the final info type in a command. - else if (name == "pv"sv) { - info_command->m_pv = TRY(parse_line(tokens.span().slice(i))); - break; - } else if (name == "string"sv) { - info_command->m_string = TRY(String::join(' ', tokens.span().slice(i))); - break; - } else if (name == "refutation"sv) { - info_command->m_refutation = TRY(parse_line(tokens.span().slice(i))); - break; - } else if (name == "currline"sv) { - info_command->m_currline = TRY(parse_line(tokens.span().slice(i))); - break; - } else { - return Error::from_string_literal("Unknown info type"); - } - } - - return info_command; -} - -ErrorOr InfoCommand::to_string() const -{ - StringBuilder builder; - - auto append_moves = [&](Vector const& moves) -> ErrorOr { - bool first = true; - for (auto const& move : moves) { - if (!first) - TRY(builder.try_append(' ')); - - first = false; - TRY(builder.try_append(TRY(move.to_long_algebraic()))); - } - return {}; - }; - - TRY(builder.try_append("info"sv)); - if (m_depth.has_value()) - TRY(builder.try_appendff(" depth {}", m_depth.value())); - if (m_seldepth.has_value()) - TRY(builder.try_appendff(" seldepth {}", m_seldepth.value())); - if (m_time.has_value()) - TRY(builder.try_appendff(" time {}", m_time.value())); - if (m_nodes.has_value()) - TRY(builder.try_appendff(" nodes {}", m_nodes.value())); - if (m_multipv.has_value()) - TRY(builder.try_appendff(" multipv {}", m_multipv.value())); - if (m_score.has_value()) { - TRY(builder.try_append(" score"sv)); - switch (m_score->type) { - case ScoreType::Centipawns: - TRY(builder.try_append(" cp"sv)); - break; - case ScoreType::Mate: - TRY(builder.try_append(" mate"sv)); - break; - } - - TRY(builder.try_appendff(" {}", m_score->value)); - - switch (m_score->bound) { - case ScoreBound::None: - break; - case ScoreBound::Lower: - TRY(builder.try_append(" lowerbound"sv)); - break; - case ScoreBound::Upper: - TRY(builder.try_append(" upperbound"sv)); - break; - } - } - if (m_currmove.has_value()) - TRY(builder.try_appendff(" currmove {}", TRY(m_currmove->to_long_algebraic()))); - if (m_currmovenumber.has_value()) - TRY(builder.try_appendff(" currmovenumber {}", m_currmovenumber.value())); - if (m_hashfull.has_value()) - TRY(builder.try_appendff(" hashfull {}", m_hashfull.value())); - if (m_nps.has_value()) - TRY(builder.try_appendff(" nps {}", m_nps.value())); - if (m_tbhits.has_value()) - TRY(builder.try_appendff(" tbhits {}", m_tbhits.value())); - if (m_cpuload.has_value()) - TRY(builder.try_appendff(" cpuload {}", m_cpuload.value())); - if (m_string.has_value()) - TRY(builder.try_appendff(" string {}", m_string.value())); - if (m_pv.has_value()) { - TRY(builder.try_append(" pv "sv)); - TRY(append_moves(m_pv.value())); - } - if (m_refutation.has_value()) { - TRY(builder.try_append(" refutation "sv)); - TRY(append_moves(m_refutation.value())); - } - if (m_currline.has_value()) { - TRY(builder.try_append(" currline "sv)); - TRY(append_moves(m_currline.value())); - } - - return builder.to_string(); -} - -ErrorOr> QuitCommand::from_string(StringView command) -{ - auto tokens = command.split_view(' '); - VERIFY(tokens[0] == "quit"); - VERIFY(tokens.size() == 1); - return adopt_nonnull_own_or_enomem(new (nothrow) QuitCommand); -} - -ErrorOr QuitCommand::to_string() const -{ - return "quit\n"_string; -} - -ErrorOr> UCINewGameCommand::from_string(StringView command) -{ - auto tokens = command.split_view(' '); - VERIFY(tokens[0] == "ucinewgame"); - VERIFY(tokens.size() == 1); - return adopt_nonnull_own_or_enomem(new (nothrow) UCINewGameCommand); -} - -ErrorOr UCINewGameCommand::to_string() const -{ - return "ucinewgame\n"_string; -} - -} diff --git a/Userland/Libraries/LibChess/UCICommand.h b/Userland/Libraries/LibChess/UCICommand.h deleted file mode 100644 index 217f9554332..00000000000 --- a/Userland/Libraries/LibChess/UCICommand.h +++ /dev/null @@ -1,322 +0,0 @@ -/* - * Copyright (c) 2020-2022, the SerenityOS developers. - * Copyright (c) 2023, Sam Atkins - * Copyright (c) 2023, Tim Ledbetter - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include - -namespace Chess::UCI { - -class Command : public Core::Event { -public: - enum class Type { - // GUI to engine commands. - UCI = 12000, - Debug, - IsReady, - SetOption, - Register, - UCINewGame, - Position, - Go, - Stop, - PonderHit, - Quit, - // Engine to GUI commands. - Id, - UCIOk, - ReadyOk, - BestMove, - CopyProtection, - Registration, - Info, - Option, - }; - virtual ErrorOr to_string() const = 0; - - virtual ~Command() = default; - -protected: - explicit Command(Type type) - : Core::Event(to_underlying(type)) - { - } -}; - -class UCICommand : public Command { -public: - explicit UCICommand() - : Command(Command::Type::UCI) - { - } - - static ErrorOr> from_string(StringView command); - - virtual ErrorOr to_string() const override; -}; - -class DebugCommand : public Command { -public: - enum class Flag { - On, - Off - }; - - explicit DebugCommand(Flag flag) - : Command(Command::Type::Debug) - , m_flag(flag) - { - } - - static ErrorOr> from_string(StringView command); - - virtual ErrorOr to_string() const override; - - Flag flag() const { return m_flag; } - -private: - Flag m_flag; -}; - -class IsReadyCommand : public Command { -public: - explicit IsReadyCommand() - : Command(Command::Type::IsReady) - { - } - - static ErrorOr> from_string(StringView command); - - virtual ErrorOr to_string() const override; -}; - -class SetOptionCommand : public Command { -public: - explicit SetOptionCommand(String name, Optional value = {}) - : Command(Command::Type::SetOption) - , m_name(move(name)) - , m_value(move(value)) - { - } - - static ErrorOr> from_string(StringView command); - - virtual ErrorOr to_string() const override; - - String const& name() const { return m_name; } - Optional const& value() const { return m_value; } - -private: - String m_name; - Optional m_value; -}; - -class PositionCommand : public Command { -public: - explicit PositionCommand(Optional fen, Vector moves) - : Command(Command::Type::Position) - , m_fen(move(fen)) - , m_moves(move(moves)) - { - } - - static ErrorOr> from_string(StringView command); - - virtual ErrorOr to_string() const override; - - Optional const& fen() const { return m_fen; } - Vector const& moves() const { return m_moves; } - -private: - Optional m_fen; - Vector m_moves; -}; - -class GoCommand : public Command { -public: - explicit GoCommand() - : Command(Command::Type::Go) - { - } - - static ErrorOr> from_string(StringView command); - - virtual ErrorOr to_string() const override; - - Optional> searchmoves; - bool ponder { false }; - Optional wtime; - Optional btime; - Optional winc; - Optional binc; - Optional movestogo; - Optional depth; - Optional nodes; - Optional mate; - Optional movetime; - bool infinite { false }; -}; - -class StopCommand : public Command { -public: - explicit StopCommand() - : Command(Command::Type::Stop) - { - } - - static ErrorOr> from_string(StringView command); - - virtual ErrorOr to_string() const override; -}; - -class IdCommand : public Command { -public: - enum class Type { - Name, - Author, - }; - - explicit IdCommand(Type field_type, String value) - : Command(Command::Type::Id) - , m_field_type(field_type) - , m_value(move(value)) - { - } - - static ErrorOr> from_string(StringView command); - - virtual ErrorOr to_string() const override; - - Type field_type() const { return m_field_type; } - String const& value() const { return m_value; } - -private: - Type m_field_type; - String m_value; -}; - -class UCIOkCommand : public Command { -public: - explicit UCIOkCommand() - : Command(Command::Type::UCIOk) - { - } - - static ErrorOr> from_string(StringView command); - - virtual ErrorOr to_string() const override; -}; - -class ReadyOkCommand : public Command { -public: - explicit ReadyOkCommand() - : Command(Command::Type::ReadyOk) - { - } - - static ErrorOr> from_string(StringView command); - - virtual ErrorOr to_string() const override; -}; - -class BestMoveCommand : public Command { -public: - explicit BestMoveCommand(Chess::Move move, Optional move_to_ponder = {}) - : Command(Command::Type::BestMove) - , m_move(::move(move)) - , m_move_to_ponder(::move(move_to_ponder)) - { - } - - static ErrorOr> from_string(StringView command); - - virtual ErrorOr to_string() const override; - - Chess::Move move() const { return m_move; } - Optional move_to_ponder() const { return m_move_to_ponder; } - -private: - Chess::Move m_move; - Optional m_move_to_ponder; -}; - -class InfoCommand : public Command { -public: - enum class ScoreType { - Centipawns, - Mate - }; - - enum class ScoreBound { - None, - Lower, - Upper - }; - - struct Score { - ScoreType type; - int value; - ScoreBound bound; - }; - - explicit InfoCommand() - : Command(Command::Type::Info) - { - } - - static ErrorOr> from_string(StringView command); - - virtual ErrorOr to_string() const override; - -private: - Optional m_depth; - Optional m_seldepth; - Optional m_time; - Optional m_nodes; - Optional> m_pv; - Optional m_multipv; - Optional m_score; - Optional m_currmove; - Optional m_currmovenumber; - Optional m_hashfull; - Optional m_nps; - Optional m_tbhits; - Optional m_cpuload; - Optional m_string; - Optional> m_refutation; - Optional> m_currline; -}; - -class QuitCommand : public Command { -public: - explicit QuitCommand() - : Command(Command::Type::Quit) - { - } - - static ErrorOr> from_string(StringView command); - - virtual ErrorOr to_string() const override; -}; - -class UCINewGameCommand : public Command { -public: - explicit UCINewGameCommand() - : Command(Command::Type::UCINewGame) - { - } - - static ErrorOr> from_string(StringView command); - - virtual ErrorOr to_string() const override; -}; - -} diff --git a/Userland/Libraries/LibChess/UCIEndpoint.cpp b/Userland/Libraries/LibChess/UCIEndpoint.cpp deleted file mode 100644 index 25101ecaddc..00000000000 --- a/Userland/Libraries/LibChess/UCIEndpoint.cpp +++ /dev/null @@ -1,132 +0,0 @@ -/* - * Copyright (c) 2020, the SerenityOS developers. - * Copyright (c) 2023, Tim Ledbetter - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "UCIEndpoint.h" -#include -#include -#include - -namespace Chess::UCI { - -void Endpoint::send_command(Command const& command) -{ - auto command_string = command.to_string().release_value_but_fixme_should_propagate_errors(); - dbgln_if(UCI_DEBUG, "{} Sent UCI Command: {}", class_name(), command_string); - m_out->write_until_depleted(command_string.bytes()).release_value_but_fixme_should_propagate_errors(); -} - -void Endpoint::event(Core::Event& event) -{ - switch (static_cast(event.type())) { - case Command::Type::UCI: - return handle_uci(); - case Command::Type::Debug: - return handle_debug(static_cast(event)); - case Command::Type::IsReady: - return handle_uci(); - case Command::Type::SetOption: - return handle_setoption(static_cast(event)); - case Command::Type::Position: - return handle_position(static_cast(event)); - case Command::Type::Go: - return handle_go(static_cast(event)); - case Command::Type::Stop: - return handle_stop(); - case Command::Type::Id: - return handle_id(static_cast(event)); - case Command::Type::UCIOk: - return handle_uciok(); - case Command::Type::ReadyOk: - return handle_readyok(); - case Command::Type::BestMove: - return handle_bestmove(static_cast(event)); - case Command::Type::Info: - return handle_info(static_cast(event)); - case Command::Type::Quit: - return handle_quit(); - case Command::Type::UCINewGame: - return handle_ucinewgame(); - default: - EventReceiver::event(event); - break; - } -} - -void Endpoint::custom_event(Core::CustomEvent& custom_event) -{ - if (custom_event.custom_type() == EndpointEventType::UnexpectedEof) - handle_unexpected_eof(); -} - -void Endpoint::set_in_notifier() -{ - m_in_notifier = Core::Notifier::construct(m_in_fd.value(), Core::Notifier::Type::Read); - m_in_notifier->on_activation = [this] { - if (!m_in->can_read_line().release_value_but_fixme_should_propagate_errors()) { - Core::EventLoop::current().post_event(*this, make(EndpointEventType::UnexpectedEof)); - m_in_notifier->set_enabled(false); - return; - } - auto buffer = ByteBuffer::create_zeroed(4096).release_value_but_fixme_should_propagate_errors(); - - while (m_in->can_read_line().release_value_but_fixme_should_propagate_errors()) { - auto line = m_in->read_line(buffer).release_value_but_fixme_should_propagate_errors().trim_whitespace(); - if (line.is_empty()) - continue; - - auto maybe_command = read_command(line); - if (maybe_command.is_error()) { - dbgln_if(UCI_DEBUG, "{} Error while parsing UCI command: {}, error: {}", class_name(), maybe_command.error(), line); - if (on_command_read_error) - on_command_read_error(move(line), maybe_command.release_error()); - - continue; - } - - Core::EventLoop::current().post_event(*this, maybe_command.release_value()); - } - }; -} - -ErrorOr> Endpoint::read_command(StringView line) const -{ - dbgln_if(UCI_DEBUG, "{} Received UCI Command: {}", class_name(), line); - - if (line == "uci") { - return UCICommand::from_string(line); - } else if (line.starts_with("debug"sv)) { - return DebugCommand::from_string(line); - } else if (line.starts_with("isready"sv)) { - return IsReadyCommand::from_string(line); - } else if (line.starts_with("setoption"sv)) { - return SetOptionCommand::from_string(line); - } else if (line.starts_with("position"sv)) { - return PositionCommand::from_string(line); - } else if (line.starts_with("go"sv)) { - return GoCommand::from_string(line); - } else if (line.starts_with("stop"sv)) { - return StopCommand::from_string(line); - } else if (line.starts_with("id"sv)) { - return IdCommand::from_string(line); - } else if (line.starts_with("uciok"sv)) { - return UCIOkCommand::from_string(line); - } else if (line.starts_with("readyok"sv)) { - return ReadyOkCommand::from_string(line); - } else if (line.starts_with("bestmove"sv)) { - return BestMoveCommand::from_string(line); - } else if (line.starts_with("info"sv)) { - return InfoCommand::from_string(line); - } else if (line.starts_with("quit"sv)) { - return QuitCommand::from_string(line); - } else if (line.starts_with("ucinewgame"sv)) { - return UCINewGameCommand::from_string(line); - } - - return Error::from_string_literal("Unknown command"); -} - -}; diff --git a/Userland/Libraries/LibChess/UCIEndpoint.h b/Userland/Libraries/LibChess/UCIEndpoint.h deleted file mode 100644 index 406b65acff7..00000000000 --- a/Userland/Libraries/LibChess/UCIEndpoint.h +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright (c) 2020-2022, the SerenityOS developers. - * Copyright (c) 2023, Tim Ledbetter - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include - -namespace Chess::UCI { - -class Endpoint : public Core::EventReceiver { - C_OBJECT(Endpoint) -public: - virtual ~Endpoint() override = default; - - Function on_command_read_error; - - virtual void handle_uci() { } - virtual void handle_debug(DebugCommand const&) { } - virtual void handle_isready() { } - virtual void handle_setoption(SetOptionCommand const&) { } - virtual void handle_position(PositionCommand const&) { } - virtual void handle_go(GoCommand const&) { } - virtual void handle_stop() { } - virtual void handle_id(IdCommand const&) { } - virtual void handle_uciok() { } - virtual void handle_readyok() { } - virtual void handle_bestmove(BestMoveCommand const&) { } - virtual void handle_info(InfoCommand const&) { } - virtual void handle_quit() { } - virtual void handle_ucinewgame() { } - virtual void handle_unexpected_eof() { } - - void send_command(Command const&); - - virtual void event(Core::Event&) override; - - ErrorOr set_in(NonnullOwnPtr in) - { - m_in_fd = in->fd(); - m_in = TRY(Core::InputBufferedFile::create(move(in))); - set_in_notifier(); - return {}; - } - void set_out(NonnullOwnPtr out) { m_out = move(out); } - -protected: - Endpoint() = default; - virtual void custom_event(Core::CustomEvent&) override; - -private: - enum EndpointEventType { - UnexpectedEof - }; - void set_in_notifier(); - ErrorOr> read_command(StringView line) const; - - Optional m_in_fd {}; - OwnPtr m_in; - OwnPtr m_out; - RefPtr m_in_notifier; -}; - -} diff --git a/Userland/Libraries/LibConfig/CMakeLists.txt b/Userland/Libraries/LibConfig/CMakeLists.txt deleted file mode 100644 index bde823fd0fc..00000000000 --- a/Userland/Libraries/LibConfig/CMakeLists.txt +++ /dev/null @@ -1,12 +0,0 @@ -set(SOURCES - Client.cpp - Listener.cpp -) - -set(GENERATED_SOURCES - ../../Services/ConfigServer/ConfigServerEndpoint.h - ../../Services/ConfigServer/ConfigClientEndpoint.h -) - -serenity_lib(LibConfig config) -target_link_libraries(LibConfig PRIVATE LibCore LibIPC) diff --git a/Userland/Libraries/LibConfig/Client.cpp b/Userland/Libraries/LibConfig/Client.cpp deleted file mode 100644 index f474d318085..00000000000 --- a/Userland/Libraries/LibConfig/Client.cpp +++ /dev/null @@ -1,151 +0,0 @@ -/* - * Copyright (c) 2021, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include - -namespace Config { - -static RefPtr s_the = nullptr; - -Client& Client::the() -{ - if (!s_the || !s_the->is_open()) { - s_the = Client::try_create().release_value_but_fixme_should_propagate_errors(); - } - return *s_the; -} - -void Client::enable_permissive_mode() -{ - async_enable_permissive_mode(); -} - -void Client::pledge_domains(Vector const& domains) -{ - async_pledge_domains(domains); -} - -void Client::monitor_domain(ByteString const& domain) -{ - async_monitor_domain(domain); -} - -Vector Client::list_keys(StringView domain, StringView group) -{ - return list_config_keys(domain, group); -} - -Vector Client::list_groups(StringView domain) -{ - return list_config_groups(domain); -} - -ByteString Client::read_string(StringView domain, StringView group, StringView key, StringView fallback) -{ - return read_string_value(domain, group, key).value_or(fallback); -} - -i32 Client::read_i32(StringView domain, StringView group, StringView key, i32 fallback) -{ - return read_i32_value(domain, group, key).value_or(fallback); -} - -u32 Client::read_u32(StringView domain, StringView group, StringView key, u32 fallback) -{ - return read_u32_value(domain, group, key).value_or(fallback); -} - -bool Client::read_bool(StringView domain, StringView group, StringView key, bool fallback) -{ - return read_bool_value(domain, group, key).value_or(fallback); -} - -void Client::write_string(StringView domain, StringView group, StringView key, StringView value) -{ - write_string_value(domain, group, key, value); -} - -void Client::write_i32(StringView domain, StringView group, StringView key, i32 value) -{ - write_i32_value(domain, group, key, value); -} - -void Client::write_u32(StringView domain, StringView group, StringView key, u32 value) -{ - write_u32_value(domain, group, key, value); -} - -void Client::write_bool(StringView domain, StringView group, StringView key, bool value) -{ - write_bool_value(domain, group, key, value); -} - -void Client::remove_key(StringView domain, StringView group, StringView key) -{ - remove_key_entry(domain, group, key); -} - -void Client::remove_group(StringView domain, StringView group) -{ - remove_group_entry(domain, group); -} - -void Client::add_group(StringView domain, StringView group) -{ - add_group_entry(domain, group); -} - -void Client::notify_changed_string_value(ByteString const& domain, ByteString const& group, ByteString const& key, ByteString const& value) -{ - Listener::for_each([&](auto& listener) { - listener.config_string_did_change(domain, group, key, value); - }); -} - -void Client::notify_changed_i32_value(ByteString const& domain, ByteString const& group, ByteString const& key, i32 value) -{ - Listener::for_each([&](auto& listener) { - listener.config_i32_did_change(domain, group, key, value); - }); -} - -void Client::notify_changed_u32_value(ByteString const& domain, ByteString const& group, ByteString const& key, u32 value) -{ - Listener::for_each([&](auto& listener) { - listener.config_u32_did_change(domain, group, key, value); - }); -} - -void Client::notify_changed_bool_value(ByteString const& domain, ByteString const& group, ByteString const& key, bool value) -{ - Listener::for_each([&](auto& listener) { - listener.config_bool_did_change(domain, group, key, value); - }); -} - -void Client::notify_removed_key(ByteString const& domain, ByteString const& group, ByteString const& key) -{ - Listener::for_each([&](auto& listener) { - listener.config_key_was_removed(domain, group, key); - }); -} - -void Client::notify_removed_group(ByteString const& domain, ByteString const& group) -{ - Listener::for_each([&](auto& listener) { - listener.config_group_was_removed(domain, group); - }); -} - -void Client::notify_added_group(ByteString const& domain, ByteString const& group) -{ - Listener::for_each([&](auto& listener) { - listener.config_group_was_added(domain, group); - }); -} - -} diff --git a/Userland/Libraries/LibConfig/Client.h b/Userland/Libraries/LibConfig/Client.h deleted file mode 100644 index 4715ea9ec1b..00000000000 --- a/Userland/Libraries/LibConfig/Client.h +++ /dev/null @@ -1,146 +0,0 @@ -/* - * Copyright (c) 2021, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include - -namespace Config { - -class Client final - : public IPC::ConnectionToServer - , public ConfigClientEndpoint { - IPC_CLIENT_CONNECTION(Client, "/tmp/session/%sid/portal/config"sv) - -public: - /// Permissive mode makes reads and writes to non-pledged domains into no-ops instead of client misbehavior errors. - void enable_permissive_mode(); - void pledge_domains(Vector const&); - void monitor_domain(ByteString const&); - - Vector list_groups(StringView domain); - Vector list_keys(StringView domain, StringView group); - - ByteString read_string(StringView domain, StringView group, StringView key, StringView fallback); - i32 read_i32(StringView domain, StringView group, StringView key, i32 fallback); - u32 read_u32(StringView domain, StringView group, StringView key, u32 fallback); - bool read_bool(StringView domain, StringView group, StringView key, bool fallback); - - void write_string(StringView domain, StringView group, StringView key, StringView value); - void write_i32(StringView domain, StringView group, StringView key, i32 value); - void write_u32(StringView domain, StringView group, StringView key, u32 value); - void write_bool(StringView domain, StringView group, StringView key, bool value); - void remove_key(StringView domain, StringView group, StringView key); - void remove_group(StringView domain, StringView group); - void add_group(StringView domain, StringView group); - - static Client& the(); - -private: - explicit Client(NonnullOwnPtr socket) - : IPC::ConnectionToServer(*this, move(socket)) - { - } - - void notify_changed_string_value(ByteString const& domain, ByteString const& group, ByteString const& key, ByteString const& value) override; - void notify_changed_i32_value(ByteString const& domain, ByteString const& group, ByteString const& key, i32 value) override; - void notify_changed_u32_value(ByteString const& domain, ByteString const& group, ByteString const& key, u32 value) override; - void notify_changed_bool_value(ByteString const& domain, ByteString const& group, ByteString const& key, bool value) override; - void notify_removed_key(ByteString const& domain, ByteString const& group, ByteString const& key) override; - void notify_removed_group(ByteString const& domain, ByteString const& group) override; - void notify_added_group(ByteString const& domain, ByteString const& group) override; -}; - -inline Vector list_groups(StringView domain) -{ - return Client::the().list_groups(domain); -} - -inline Vector list_keys(StringView domain, StringView group) -{ - return Client::the().list_keys(domain, group); -} - -inline ByteString read_string(StringView domain, StringView group, StringView key, StringView fallback = {}) -{ - return Client::the().read_string(domain, group, key, fallback); -} - -inline i32 read_i32(StringView domain, StringView group, StringView key, i32 fallback = 0) -{ - return Client::the().read_i32(domain, group, key, fallback); -} - -inline u32 read_u32(StringView domain, StringView group, StringView key, u32 fallback = 0) -{ - return Client::the().read_u32(domain, group, key, fallback); -} - -inline bool read_bool(StringView domain, StringView group, StringView key, bool fallback = false) -{ - return Client::the().read_bool(domain, group, key, fallback); -} - -inline void write_string(StringView domain, StringView group, StringView key, StringView value) -{ - Client::the().write_string(domain, group, key, value); -} - -inline void write_i32(StringView domain, StringView group, StringView key, i32 value) -{ - Client::the().write_i32(domain, group, key, value); -} - -inline void write_u32(StringView domain, StringView group, StringView key, u32 value) -{ - Client::the().write_u32(domain, group, key, value); -} - -inline void write_bool(StringView domain, StringView group, StringView key, bool value) -{ - Client::the().write_bool(domain, group, key, value); -} - -inline void remove_key(StringView domain, StringView group, StringView key) -{ - Client::the().remove_key(domain, group, key); -} - -inline void remove_group(StringView domain, StringView group) -{ - Client::the().remove_group(domain, group); -} - -inline void add_group(StringView domain, StringView group) -{ - Client::the().add_group(domain, group); -} - -inline void enable_permissive_mode() -{ - Client::the().enable_permissive_mode(); -} - -inline void pledge_domains(Vector const& domains) -{ - Client::the().pledge_domains(domains); -} - -inline void pledge_domain(ByteString const& domain) -{ - Client::the().pledge_domains({ domain }); -} - -inline void monitor_domain(ByteString const& domain) -{ - Client::the().monitor_domain(domain); -} - -} diff --git a/Userland/Libraries/LibConfig/Listener.cpp b/Userland/Libraries/LibConfig/Listener.cpp deleted file mode 100644 index cb95fc95a6e..00000000000 --- a/Userland/Libraries/LibConfig/Listener.cpp +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright (c) 2021, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include - -namespace Config { - -static HashTable s_listeners; - -Listener::Listener() -{ - s_listeners.set(this); -} - -Listener::~Listener() -{ - s_listeners.remove(this); -} - -void Listener::for_each(Function callback) -{ - for (auto* listener : s_listeners) - callback(*listener); -} - -void Listener::config_string_did_change(StringView, StringView, StringView, StringView) -{ -} - -void Listener::config_i32_did_change(StringView, StringView, StringView, i32) -{ -} - -void Listener::config_u32_did_change(StringView, StringView, StringView, u32) -{ -} - -void Listener::config_bool_did_change(StringView, StringView, StringView, bool) -{ -} - -void Listener::config_key_was_removed(StringView, StringView, StringView) -{ -} - -void Listener::config_group_was_removed(StringView, StringView) -{ -} - -void Listener::config_group_was_added(StringView, StringView) -{ -} - -} diff --git a/Userland/Libraries/LibConfig/Listener.h b/Userland/Libraries/LibConfig/Listener.h deleted file mode 100644 index 0d33e322880..00000000000 --- a/Userland/Libraries/LibConfig/Listener.h +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright (c) 2021, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include - -namespace Config { - -class Listener { -public: - virtual ~Listener(); - - static void for_each(Function); - - virtual void config_string_did_change(StringView domain, StringView group, StringView key, StringView value); - virtual void config_i32_did_change(StringView domain, StringView group, StringView key, i32 value); - virtual void config_u32_did_change(StringView domain, StringView group, StringView key, u32 value); - virtual void config_bool_did_change(StringView domain, StringView group, StringView key, bool value); - virtual void config_key_was_removed(StringView domain, StringView group, StringView key); - virtual void config_group_was_removed(StringView domain, StringView group); - virtual void config_group_was_added(StringView domain, StringView group); - -protected: - Listener(); -}; - -} diff --git a/Userland/Libraries/LibCoredump/Backtrace.cpp b/Userland/Libraries/LibCoredump/Backtrace.cpp deleted file mode 100644 index 1ebd34a9bd1..00000000000 --- a/Userland/Libraries/LibCoredump/Backtrace.cpp +++ /dev/null @@ -1,164 +0,0 @@ -/* - * Copyright (c) 2020, Linus Groh - * Copyright (c) 2022, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace Coredump { - -ELFObjectInfo const* Backtrace::object_info_for_region(Reader const& coredump, MemoryRegionInfo const& region) -{ - ByteString path = coredump.resolve_object_path(region.object_name()); - - auto maybe_ptr = m_debug_info_cache.get(path); - if (maybe_ptr.has_value()) - return *maybe_ptr; - - if (!FileSystem::exists(path)) - return nullptr; - - auto file_or_error = Core::MappedFile::map(path); - if (file_or_error.is_error()) - return nullptr; - - auto image = make(file_or_error.value()->bytes()); - auto& image_reference = *image; - auto info = make(file_or_error.release_value(), make(image_reference), move(image)); - auto* info_ptr = info.ptr(); - m_debug_info_cache.set(path, move(info)); - return info_ptr; -} - -Backtrace::Backtrace(Reader const& coredump, const ELF::Core::ThreadInfo& thread_info, Function on_progress) - : m_thread_info(move(thread_info)) -{ - // In order to provide progress updates, we first have to walk the - // call stack to determine how many frames it has. - size_t frame_count = 0; - MUST(AK::unwind_stack_from_frame_pointer( - thread_info.regs.bp(), - [&coredump](FlatPtr address) -> ErrorOr { - auto maybe_value = coredump.peek_memory(address); - if (!maybe_value.has_value()) - return EFAULT; - - return maybe_value.value(); - }, - [&frame_count](AK::StackFrame) -> ErrorOr { - ++frame_count; - return IterationDecision::Continue; - })); - - size_t frame_index = 0; - - auto on_entry = [this, &coredump, &on_progress, &frame_index, frame_count](FlatPtr address) { - add_entry(coredump, address); - if (on_progress) - on_progress(frame_index, frame_count); - ++frame_index; - }; - - on_entry(thread_info.regs.ip()); - - MUST(AK::unwind_stack_from_frame_pointer( - thread_info.regs.bp(), - [&coredump](FlatPtr address) -> ErrorOr { - auto maybe_value = coredump.peek_memory(address); - if (!maybe_value.has_value()) - return EFAULT; - - return maybe_value.value(); - }, - [&on_entry](AK::StackFrame stack_frame) -> ErrorOr { - // We use return_address - 1 because the return address from a function frame - // is the instruction that comes after the calling instruction. - // However, because the first frame represents the faulting - // instruction rather than the return address we don't subtract - // 1 there. - VERIFY(stack_frame.return_address > 0); - on_entry(stack_frame.return_address - 1); - - return IterationDecision::Continue; - })); -} - -void Backtrace::add_entry(Reader const& coredump, FlatPtr ip) -{ - auto ip_region = coredump.region_containing(ip); - if (!ip_region.has_value()) { - m_entries.append({ ip, {}, {}, {} }); - return; - } - auto object_name = ip_region->object_name(); - // Only skip addresses coming from Loader.so if the faulting instruction is not in Loader.so - if (object_name == "Loader.so") { - if (m_skip_loader_so) - return; - } else { - m_skip_loader_so = true; - } - // We need to find the first region for the object, just in case - // the PT_LOAD header for the .text segment isn't the first one - // in the object file. - auto region = coredump.first_region_for_object(object_name); - auto object_info = object_info_for_region(coredump, *region); - if (!object_info) { - m_entries.append({ ip, object_name, {}, {} }); - return; - } - - auto function_name = object_info->debug_info->elf().symbolicate(ip - region->region_start); - auto source_position = object_info->debug_info->get_source_position_with_inlines(ip - region->region_start).release_value_but_fixme_should_propagate_errors(); - m_entries.append({ ip, object_name, function_name, source_position }); -} - -ByteString Backtrace::Entry::to_byte_string(bool color) const -{ - StringBuilder builder; - builder.appendff("{:p}: ", eip); - if (object_name.is_empty()) { - builder.append("???"sv); - return builder.to_byte_string(); - } - builder.appendff("[{}] {}", object_name, function_name.is_empty() ? "???" : function_name); - builder.append(" ("sv); - - Vector source_positions; - - for (auto& position : source_position_with_inlines.inline_chain) { - if (!source_positions.contains_slow(position)) - source_positions.append(position); - } - - if (source_position_with_inlines.source_position.has_value() && !source_positions.contains_slow(source_position_with_inlines.source_position.value())) { - source_positions.insert(0, source_position_with_inlines.source_position.value()); - } - - for (size_t i = 0; i < source_positions.size(); ++i) { - auto& position = source_positions[i]; - auto fmt = color ? "\033[34;1m{}\033[0m:{}"sv : "{}:{}"sv; - builder.appendff(fmt, LexicalPath::basename(position.file_path), position.line_number); - if (i != source_positions.size() - 1) { - builder.append(" => "sv); - } - } - - builder.append(')'); - - return builder.to_byte_string(); -} - -} diff --git a/Userland/Libraries/LibCoredump/Backtrace.h b/Userland/Libraries/LibCoredump/Backtrace.h deleted file mode 100644 index 6c22776d574..00000000000 --- a/Userland/Libraries/LibCoredump/Backtrace.h +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright (c) 2020, Linus Groh - * Copyright (c) 2022, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include - -namespace Coredump { - -struct ELFObjectInfo { - ELFObjectInfo(NonnullOwnPtr file, NonnullOwnPtr&& debug_info, NonnullOwnPtr image) - : file(move(file)) - , debug_info(move(debug_info)) - , image(move(image)) - { - } - - NonnullOwnPtr file; - NonnullOwnPtr debug_info; - NonnullOwnPtr image; -}; - -class Backtrace { -public: - struct Entry { - FlatPtr eip; - ByteString object_name; - ByteString function_name; - Debug::DebugInfo::SourcePositionWithInlines source_position_with_inlines; - - ByteString to_byte_string(bool color = false) const; - }; - - Backtrace(Reader const&, const ELF::Core::ThreadInfo&, Function on_progress = {}); - ~Backtrace() = default; - - ELF::Core::ThreadInfo const& thread_info() const { return m_thread_info; } - Vector const& entries() const { return m_entries; } - -private: - void add_entry(Reader const&, FlatPtr ip); - ELFObjectInfo const* object_info_for_region(Reader const&, MemoryRegionInfo const&); - - bool m_skip_loader_so { false }; - ELF::Core::ThreadInfo m_thread_info; - Vector m_entries; - HashMap> m_debug_info_cache; -}; - -} diff --git a/Userland/Libraries/LibCoredump/CMakeLists.txt b/Userland/Libraries/LibCoredump/CMakeLists.txt deleted file mode 100644 index b87905bb2b5..00000000000 --- a/Userland/Libraries/LibCoredump/CMakeLists.txt +++ /dev/null @@ -1,8 +0,0 @@ -set(SOURCES - Backtrace.cpp - Inspector.cpp - Reader.cpp -) - -serenity_lib(LibCoredump coredump) -target_link_libraries(LibCoredump PRIVATE LibCompress LibCore LibDebug LibELF LibFileSystem) diff --git a/Userland/Libraries/LibCoredump/Forward.h b/Userland/Libraries/LibCoredump/Forward.h deleted file mode 100644 index a8fd28de759..00000000000 --- a/Userland/Libraries/LibCoredump/Forward.h +++ /dev/null @@ -1,14 +0,0 @@ -/* - * Copyright (c) 2020, Linus Groh - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -namespace Coredump { - -class Backtrace; -class Reader; - -} diff --git a/Userland/Libraries/LibCoredump/Inspector.cpp b/Userland/Libraries/LibCoredump/Inspector.cpp deleted file mode 100644 index d9d4423fa77..00000000000 --- a/Userland/Libraries/LibCoredump/Inspector.cpp +++ /dev/null @@ -1,81 +0,0 @@ -/* - * Copyright (c) 2021, Itamar S. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "Inspector.h" - -namespace Coredump { - -OwnPtr Inspector::create(StringView coredump_path, Function on_progress) -{ - auto reader = Reader::create(coredump_path); - if (!reader) - return {}; - return AK::adopt_own_if_nonnull(new (nothrow) Inspector(reader.release_nonnull(), move(on_progress))); -} - -Inspector::Inspector(NonnullOwnPtr&& reader, Function on_progress) - : m_reader(move(reader)) -{ - parse_loaded_libraries(move(on_progress)); -} - -size_t Inspector::number_of_libraries_in_coredump() const -{ - size_t count = 0; - m_reader->for_each_library([&count](Coredump::Reader::LibraryInfo) { - ++count; - }); - return count; -} - -void Inspector::parse_loaded_libraries(Function on_progress) -{ - size_t number_of_libraries = number_of_libraries_in_coredump(); - size_t library_index = 0; - - m_reader->for_each_library([this, number_of_libraries, &library_index, &on_progress](Coredump::Reader::LibraryInfo library) { - ++library_index; - if (on_progress) - on_progress(library_index / static_cast(number_of_libraries)); - - auto file_or_error = Core::MappedFile::map(library.path); - if (file_or_error.is_error()) - return; - - auto image = make(file_or_error.value()->bytes()); - auto debug_info = make(*image, ByteString {}, library.base_address); - m_loaded_libraries.append(make(library.name, file_or_error.release_value(), move(image), move(debug_info), library.base_address)); - }); -} - -bool Inspector::poke(FlatPtr, FlatPtr) { return false; } - -Optional Inspector::peek(FlatPtr address) const -{ - return m_reader->peek_memory(address); -} - -PtraceRegisters Inspector::get_registers() const -{ - PtraceRegisters registers {}; - m_reader->for_each_thread_info([&](ELF::Core::ThreadInfo const& thread_info) { - registers = thread_info.regs; - return IterationDecision::Break; - }); - return registers; -} - -void Inspector::set_registers(PtraceRegisters const&) {}; - -void Inspector::for_each_loaded_library(Function func) const -{ - for (auto& library : m_loaded_libraries) { - if (func(*library) == IterationDecision::Break) - break; - } -} - -} diff --git a/Userland/Libraries/LibCoredump/Inspector.h b/Userland/Libraries/LibCoredump/Inspector.h deleted file mode 100644 index 2fd889dfcd2..00000000000 --- a/Userland/Libraries/LibCoredump/Inspector.h +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright (c) 2021, Itamar S. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include "Reader.h" -#include -#include - -namespace Coredump { - -class Inspector : public Debug::ProcessInspector { - AK_MAKE_NONCOPYABLE(Inspector); - AK_MAKE_NONMOVABLE(Inspector); - -public: - static OwnPtr create(StringView coredump_path, Function on_progress = {}); - virtual ~Inspector() override = default; - - // ^Debug::ProcessInspector - virtual bool poke(FlatPtr address, FlatPtr data) override; - virtual Optional peek(FlatPtr address) const override; - virtual PtraceRegisters get_registers() const override; - virtual void set_registers(PtraceRegisters const&) override; - virtual void for_each_loaded_library(Function) const override; - -private: - Inspector(NonnullOwnPtr&&, Function on_progress); - - void parse_loaded_libraries(Function on_progress); - size_t number_of_libraries_in_coredump() const; - - NonnullOwnPtr m_reader; - - Vector> m_loaded_libraries; -}; - -} diff --git a/Userland/Libraries/LibCoredump/Reader.cpp b/Userland/Libraries/LibCoredump/Reader.cpp deleted file mode 100644 index f823f8de9d8..00000000000 --- a/Userland/Libraries/LibCoredump/Reader.cpp +++ /dev/null @@ -1,354 +0,0 @@ -/* - * Copyright (c) 2020, Itamar S. - * Copyright (c) 2022, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace Coredump { - -OwnPtr Reader::create(StringView path) -{ - auto file_or_error = Core::MappedFile::map(path); - if (file_or_error.is_error()) - return {}; - - if (!Compress::GzipDecompressor::is_likely_compressed(file_or_error.value()->bytes())) { - // It's an uncompressed coredump. - return AK::adopt_own_if_nonnull(new (nothrow) Reader(file_or_error.release_value())); - } - - auto decompressed_data = decompress_coredump(file_or_error.value()->bytes()); - if (!decompressed_data.has_value()) - return {}; - return adopt_own_if_nonnull(new (nothrow) Reader(decompressed_data.release_value())); -} - -Reader::Reader(ByteBuffer buffer) - : Reader(buffer.bytes()) -{ - m_coredump_buffer = move(buffer); -} - -Reader::Reader(NonnullOwnPtr file) - : Reader(file->bytes()) -{ - m_mapped_file = move(file); -} - -Reader::Reader(ReadonlyBytes coredump_bytes) - : m_coredump_bytes(coredump_bytes) - , m_coredump_image(m_coredump_bytes) -{ - size_t index = 0; - m_coredump_image.for_each_program_header([this, &index](auto pheader) { - if (pheader.type() == PT_NOTE) { - m_notes_segment_index = index; - return IterationDecision::Break; - } - ++index; - return IterationDecision::Continue; - }); - VERIFY(m_notes_segment_index != -1); -} - -Optional Reader::decompress_coredump(ReadonlyBytes raw_coredump) -{ - auto decompressed_coredump = Compress::GzipDecompressor::decompress_all(raw_coredump); - if (!decompressed_coredump.is_error()) - return decompressed_coredump.release_value(); - - // If we didn't manage to decompress it, try and parse it as decompressed coredump - auto bytebuffer = ByteBuffer::copy(raw_coredump); - if (bytebuffer.is_error()) - return {}; - return bytebuffer.release_value(); -} - -Reader::NotesEntryIterator::NotesEntryIterator(u8 const* notes_data) - : m_current(bit_cast(notes_data)) - , start(notes_data) -{ -} - -ELF::Core::NotesEntryHeader::Type Reader::NotesEntryIterator::type() const -{ - VERIFY(m_current->header.type == ELF::Core::NotesEntryHeader::Type::ProcessInfo - || m_current->header.type == ELF::Core::NotesEntryHeader::Type::MemoryRegionInfo - || m_current->header.type == ELF::Core::NotesEntryHeader::Type::ThreadInfo - || m_current->header.type == ELF::Core::NotesEntryHeader::Type::Metadata - || m_current->header.type == ELF::Core::NotesEntryHeader::Type::Null); - return m_current->header.type; -} - -const ELF::Core::NotesEntry* Reader::NotesEntryIterator::current() const -{ - return m_current; -} - -void Reader::NotesEntryIterator::next() -{ - VERIFY(!at_end()); - switch (type()) { - case ELF::Core::NotesEntryHeader::Type::ProcessInfo: { - auto const* current = bit_cast(m_current); - m_current = bit_cast(current->json_data + strlen(current->json_data) + 1); - break; - } - case ELF::Core::NotesEntryHeader::Type::ThreadInfo: { - auto const* current = bit_cast(m_current); - m_current = bit_cast(current + 1); - break; - } - case ELF::Core::NotesEntryHeader::Type::MemoryRegionInfo: { - auto const* current = bit_cast(m_current); - m_current = bit_cast(current->region_name + strlen(current->region_name) + 1); - break; - } - case ELF::Core::NotesEntryHeader::Type::Metadata: { - auto const* current = bit_cast(m_current); - m_current = bit_cast(current->json_data + strlen(current->json_data) + 1); - break; - } - default: - VERIFY_NOT_REACHED(); - } -} - -bool Reader::NotesEntryIterator::at_end() const -{ - return type() == ELF::Core::NotesEntryHeader::Type::Null; -} - -Optional Reader::peek_memory(FlatPtr address) const -{ - auto region = region_containing(address); - if (!region.has_value()) - return {}; - - FlatPtr offset_in_region = address - region->region_start; - auto* region_data = bit_cast(image().program_header(region->program_header_index).raw_data()); - FlatPtr value { 0 }; - ByteReader::load(region_data + offset_in_region, value); - return value; -} - -JsonObject const Reader::process_info() const -{ - const ELF::Core::ProcessInfo* process_info_notes_entry = nullptr; - NotesEntryIterator it(bit_cast(m_coredump_image.program_header(m_notes_segment_index).raw_data())); - for (; !it.at_end(); it.next()) { - if (it.type() != ELF::Core::NotesEntryHeader::Type::ProcessInfo) - continue; - process_info_notes_entry = bit_cast(it.current()); - break; - } - if (!process_info_notes_entry) - return {}; - auto const* json_data_ptr = process_info_notes_entry->json_data; - auto process_info_json_value = JsonValue::from_string({ json_data_ptr, strlen(json_data_ptr) }); - if (process_info_json_value.is_error()) - return {}; - if (!process_info_json_value.value().is_object()) - return {}; - return process_info_json_value.value().as_object(); - // FIXME: Maybe just cache this on the Reader instance after first access. -} - -Optional Reader::first_region_for_object(StringView object_name) const -{ - Optional ret; - for_each_memory_region_info([&ret, &object_name](auto& region_info) { - if (region_info.object_name() == object_name) { - ret = region_info; - return IterationDecision::Break; - } - return IterationDecision::Continue; - }); - return ret; -} - -Optional Reader::region_containing(FlatPtr address) const -{ - Optional ret; - for_each_memory_region_info([&ret, address](auto const& region_info) { - if (region_info.region_start <= address && region_info.region_end >= address) { - ret = region_info; - return IterationDecision::Break; - } - return IterationDecision::Continue; - }); - return ret; -} - -int Reader::process_pid() const -{ - auto process_info = this->process_info(); - auto pid = process_info.get_integer("pid"sv).value_or(0); - return pid; -} - -u8 Reader::process_termination_signal() const -{ - auto process_info = this->process_info(); - auto termination_signal = process_info.get_u8("termination_signal"sv); - if (!termination_signal.has_value() || *termination_signal <= SIGINVAL || *termination_signal >= NSIG) - return SIGINVAL; - return *termination_signal; -} - -ByteString Reader::process_executable_path() const -{ - auto process_info = this->process_info(); - auto executable_path = process_info.get_byte_string("executable_path"sv); - return executable_path.value_or({}); -} - -Vector Reader::process_arguments() const -{ - auto process_info = this->process_info(); - auto arguments = process_info.get_array("arguments"sv); - if (!arguments.has_value()) - return {}; - Vector vector; - arguments->for_each([&](auto& value) { - if (value.is_string()) - vector.append(value.as_string()); - }); - return vector; -} - -Vector Reader::process_environment() const -{ - auto process_info = this->process_info(); - auto environment = process_info.get_array("environment"sv); - if (!environment.has_value()) - return {}; - Vector vector; - environment->for_each([&](auto& value) { - if (value.is_string()) - vector.append(value.as_string()); - }); - return vector; -} - -HashMap Reader::metadata() const -{ - const ELF::Core::Metadata* metadata_notes_entry = nullptr; - NotesEntryIterator it(bit_cast(m_coredump_image.program_header(m_notes_segment_index).raw_data())); - for (; !it.at_end(); it.next()) { - if (it.type() != ELF::Core::NotesEntryHeader::Type::Metadata) - continue; - metadata_notes_entry = bit_cast(it.current()); - break; - } - if (!metadata_notes_entry) - return {}; - auto const* json_data_ptr = metadata_notes_entry->json_data; - auto metadata_json_value = JsonValue::from_string({ json_data_ptr, strlen(json_data_ptr) }); - if (metadata_json_value.is_error()) - return {}; - if (!metadata_json_value.value().is_object()) - return {}; - HashMap metadata; - metadata_json_value.value().as_object().for_each_member([&](auto& key, auto& value) { - metadata.set(key, value.as_string_or({})); - }); - return metadata; -} - -Reader::LibraryData const* Reader::library_containing(FlatPtr address) const -{ - static HashMap> cached_libs; - auto region = region_containing(address); - if (!region.has_value()) - return {}; - - auto name = region->object_name(); - ByteString path = resolve_object_path(name); - - if (!cached_libs.contains(path)) { - auto file_or_error = Core::MappedFile::map(path); - if (file_or_error.is_error()) - return {}; - auto image = ELF::Image(file_or_error.value()->bytes()); - cached_libs.set(path, make(name, static_cast(region->region_start), file_or_error.release_value(), move(image))); - } - - auto lib_data = cached_libs.get(path).value(); - return lib_data; -} - -ByteString Reader::resolve_object_path(StringView name) const -{ - // TODO: There are other places where similar method is implemented or would be useful. - // (e.g. LibSymbolication, Profiler, and DynamicLinker itself) - // We should consider creating unified implementation in the future. - - if (name.starts_with('/') || !FileSystem::looks_like_shared_library(name)) { - return name; - } - - Vector library_search_directories; - - // If LD_LIBRARY_PATH is present, check its folders first - for (auto& environment_variable : process_environment()) { - auto prefix = "LD_LIBRARY_PATH="sv; - if (environment_variable.starts_with(prefix)) { - auto ld_library_path = environment_variable.substring_view(prefix.length()); - - // FIXME: This code won't handle folders with ":" in the name correctly. - for (auto directory : ld_library_path.split_view(':')) { - library_search_directories.append(directory); - } - } - } - - // Add default paths that DynamicLinker uses - library_search_directories.append("/usr/lib/"sv); - library_search_directories.append("/usr/local/lib/"sv); - - // Search for the first readable library file - for (auto& directory : library_search_directories) { - auto full_path = LexicalPath::join(directory, name).string(); - - if (access(full_path.characters(), R_OK) != 0) - continue; - - return full_path; - } - - return name; -} - -void Reader::for_each_library(Function func) const -{ - HashTable libraries; - for_each_memory_region_info([&](auto const& region) { - auto name = region.object_name(); - if (name.is_null() || libraries.contains(name)) - return IterationDecision::Continue; - - libraries.set(name); - - ByteString path = resolve_object_path(name); - - func(LibraryInfo { name, path, static_cast(region.region_start) }); - return IterationDecision::Continue; - }); -} - -} diff --git a/Userland/Libraries/LibCoredump/Reader.h b/Userland/Libraries/LibCoredump/Reader.h deleted file mode 100644 index 5a8813f5334..00000000000 --- a/Userland/Libraries/LibCoredump/Reader.h +++ /dev/null @@ -1,166 +0,0 @@ -/* - * Copyright (c) 2020, Itamar S. - * Copyright (c) 2022, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include -#include -#include - -namespace Coredump { - -struct MemoryRegionInfo { - ELF::Core::NotesEntryHeader header; - uint64_t region_start; - uint64_t region_end; - uint16_t program_header_index; - StringView region_name; - - StringView object_name() const - { - if (region_name.contains("Loader.so"sv)) - return "Loader.so"sv; - auto maybe_colon_index = region_name.find(':'); - if (!maybe_colon_index.has_value()) - return {}; - return region_name.substring_view(0, *maybe_colon_index); - } -}; - -class Reader { - AK_MAKE_NONCOPYABLE(Reader); - AK_MAKE_NONMOVABLE(Reader); - -public: - static OwnPtr create(StringView); - ~Reader() = default; - - template - void for_each_memory_region_info(Func func) const; - - struct LibraryInfo { - ByteString name; - ByteString path; - FlatPtr base_address { 0 }; - }; - - void for_each_library(Function func) const; - - template - void for_each_thread_info(Func func) const; - - const ELF::Image& image() const { return m_coredump_image; } - - Optional peek_memory(FlatPtr address) const; - Optional first_region_for_object(StringView object_name) const; - Optional region_containing(FlatPtr address) const; - - struct LibraryData { - ByteString name; - FlatPtr base_address { 0 }; - NonnullOwnPtr file; - ELF::Image lib_elf; - }; - LibraryData const* library_containing(FlatPtr address) const; - - ByteString resolve_object_path(StringView object_name) const; - - int process_pid() const; - u8 process_termination_signal() const; - ByteString process_executable_path() const; - Vector process_arguments() const; - Vector process_environment() const; - HashMap metadata() const; - -private: - explicit Reader(ReadonlyBytes); - explicit Reader(ByteBuffer); - explicit Reader(NonnullOwnPtr); - - static Optional decompress_coredump(ReadonlyBytes); - - class NotesEntryIterator { - public: - NotesEntryIterator(u8 const* notes_data); - - ELF::Core::NotesEntryHeader::Type type() const; - const ELF::Core::NotesEntry* current() const; - - void next(); - bool at_end() const; - - private: - const ELF::Core::NotesEntry* m_current { nullptr }; - u8 const* start { nullptr }; - }; - - // Private as we don't need anyone poking around in this JsonObject - // manually - we know very well what should be included and expose that - // as getters with the appropriate (non-JsonValue) types. - JsonObject const process_info() const; - - // For uncompressed coredumps, we keep the MappedFile - OwnPtr m_mapped_file; - - // For compressed coredumps, we decompress them into a ByteBuffer - ByteBuffer m_coredump_buffer; - - ReadonlyBytes m_coredump_bytes; - - ELF::Image m_coredump_image; - ssize_t m_notes_segment_index { -1 }; -}; - -template -void Reader::for_each_memory_region_info(Func func) const -{ - NotesEntryIterator it(bit_cast(m_coredump_image.program_header(m_notes_segment_index).raw_data())); - for (; !it.at_end(); it.next()) { - if (it.type() != ELF::Core::NotesEntryHeader::Type::MemoryRegionInfo) - continue; - ELF::Core::MemoryRegionInfo raw_memory_region_info; - ReadonlyBytes raw_data { - it.current(), - sizeof(raw_memory_region_info), - }; - ByteReader::load(raw_data.data(), raw_memory_region_info); - - auto const* region_name_ptr = bit_cast(raw_data.offset_pointer(raw_data.size())); - MemoryRegionInfo memory_region_info { - raw_memory_region_info.header, - raw_memory_region_info.region_start, - raw_memory_region_info.region_end, - raw_memory_region_info.program_header_index, - { region_name_ptr, strlen(region_name_ptr) }, - }; - IterationDecision decision = func(memory_region_info); - if (decision == IterationDecision::Break) - return; - } -} - -template -void Reader::for_each_thread_info(Func func) const -{ - NotesEntryIterator it(bit_cast(m_coredump_image.program_header(m_notes_segment_index).raw_data())); - for (; !it.at_end(); it.next()) { - if (it.type() != ELF::Core::NotesEntryHeader::Type::ThreadInfo) - continue; - ELF::Core::ThreadInfo thread_info; - ByteReader::load(bit_cast(it.current()), thread_info); - - IterationDecision decision = func(thread_info); - if (decision == IterationDecision::Break) - return; - } -} - -} diff --git a/Userland/Libraries/LibDNS/Answer.cpp b/Userland/Libraries/LibDNS/Answer.cpp deleted file mode 100644 index 68c1ce983a5..00000000000 --- a/Userland/Libraries/LibDNS/Answer.cpp +++ /dev/null @@ -1,128 +0,0 @@ -/* - * Copyright (c) 2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "Answer.h" -#include -#include -#include - -namespace DNS { - -Answer::Answer(Name const& name, RecordType type, RecordClass class_code, u32 ttl, ByteString const& record_data, bool mdns_cache_flush) - : m_name(name) - , m_type(type) - , m_class_code(class_code) - , m_ttl(ttl) - , m_record_data(record_data) - , m_mdns_cache_flush(mdns_cache_flush) -{ - time(&m_received_time); -} - -bool Answer::has_expired() const -{ - return time(nullptr) >= static_cast(m_received_time + m_ttl); -} - -unsigned Answer::hash() const -{ - auto hash = pair_int_hash(CaseInsensitiveStringTraits::hash(name().as_string()), (u32)type()); - hash = pair_int_hash(hash, pair_int_hash((u32)class_code(), ttl())); - hash = pair_int_hash(hash, record_data().hash()); - hash = pair_int_hash(hash, (u32)mdns_cache_flush()); - return hash; -} - -bool Answer::operator==(Answer const& other) const -{ - if (&other == this) - return true; - if (!Name::Traits::equals(name(), other.name())) - return false; - if (type() != other.type()) - return false; - if (class_code() != other.class_code()) - return false; - if (ttl() != other.ttl()) - return false; - if (record_data() != other.record_data()) - return false; - if (mdns_cache_flush() != other.mdns_cache_flush()) - return false; - return true; -} - -} - -ErrorOr AK::Formatter::format(AK::FormatBuilder& builder, DNS::RecordType value) -{ - switch (value) { - case DNS::RecordType::A: - return builder.put_string("A"sv); - case DNS::RecordType::NS: - return builder.put_string("NS"sv); - case DNS::RecordType::CNAME: - return builder.put_string("CNAME"sv); - case DNS::RecordType::SOA: - return builder.put_string("SOA"sv); - case DNS::RecordType::PTR: - return builder.put_string("PTR"sv); - case DNS::RecordType::MX: - return builder.put_string("MX"sv); - case DNS::RecordType::TXT: - return builder.put_string("TXT"sv); - case DNS::RecordType::AAAA: - return builder.put_string("AAAA"sv); - case DNS::RecordType::SRV: - return builder.put_string("SRV"sv); - } - - TRY(builder.put_string("DNS record type "sv)); - TRY(builder.put_u64((u16)value)); - return {}; -} - -ErrorOr AK::Formatter::format(AK::FormatBuilder& builder, DNS::RecordClass value) -{ - switch (value) { - case DNS::RecordClass::IN: - return builder.put_string("IN"sv); - } - - TRY(builder.put_string("DNS record class "sv)); - TRY(builder.put_u64((u16)value)); - return {}; -} - -namespace IPC { - -template<> -ErrorOr encode(Encoder& encoder, DNS::Answer const& answer) -{ - TRY(encoder.encode(answer.name().as_string())); - TRY(encoder.encode(static_cast(answer.type()))); - TRY(encoder.encode(static_cast(answer.class_code()))); - TRY(encoder.encode(answer.ttl())); - TRY(encoder.encode(answer.record_data())); - TRY(encoder.encode(answer.mdns_cache_flush())); - - return {}; -} - -template<> -ErrorOr decode(Decoder& decoder) -{ - auto name = TRY(decoder.decode()); - auto record_type = TRY(decoder.decode()); - auto class_code = TRY(decoder.decode()); - auto ttl = TRY(decoder.decode()); - auto record_data = TRY(decoder.decode()); - auto cache_flush = TRY(decoder.decode()); - - return DNS::Answer { name, record_type, class_code, ttl, record_data, cache_flush }; -} - -} diff --git a/Userland/Libraries/LibDNS/Answer.h b/Userland/Libraries/LibDNS/Answer.h deleted file mode 100644 index f7b49414b56..00000000000 --- a/Userland/Libraries/LibDNS/Answer.h +++ /dev/null @@ -1,103 +0,0 @@ -/* - * Copyright (c) 2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include "Name.h" -#include -#include -#include -#include -#include - -namespace DNS { - -enum class RecordType : u16 { - A = 1, - NS = 2, - CNAME = 5, - SOA = 6, - PTR = 12, - MX = 15, - TXT = 16, - AAAA = 28, - SRV = 33, -}; - -enum class RecordClass : u16 { - IN = 1 -}; - -#define MDNS_CACHE_FLUSH 0x8000 - -class Answer { -public: - Answer() = default; - Answer(Name const& name, RecordType type, RecordClass class_code, u32 ttl, ByteString const& record_data, bool mdns_cache_flush); - - Name const& name() const { return m_name; } - RecordType type() const { return m_type; } - RecordClass class_code() const { return m_class_code; } - u16 raw_class_code() const { return (u16)m_class_code | (m_mdns_cache_flush ? MDNS_CACHE_FLUSH : 0); } - u32 ttl() const { return m_ttl; } - time_t received_time() const { return m_received_time; } - ByteString const& record_data() const { return m_record_data; } - bool mdns_cache_flush() const { return m_mdns_cache_flush; } - - bool has_expired() const; - - unsigned hash() const; - bool operator==(Answer const&) const; - -private: - Name m_name; - RecordType m_type { 0 }; - RecordClass m_class_code { 0 }; - u32 m_ttl { 0 }; - time_t m_received_time { 0 }; - ByteString m_record_data; - bool m_mdns_cache_flush { false }; -}; - -} - -template<> -struct AK::Traits : public DefaultTraits { - static constexpr bool is_trivial() { return false; } - static unsigned hash(DNS::Answer a) { return a.hash(); } -}; - -template<> -struct AK::Formatter : StandardFormatter { - Formatter() = default; - explicit Formatter(StandardFormatter formatter) - : StandardFormatter(formatter) - { - } - - ErrorOr format(AK::FormatBuilder&, DNS::RecordType); -}; - -template<> -struct AK::Formatter : StandardFormatter { - Formatter() = default; - explicit Formatter(StandardFormatter formatter) - : StandardFormatter(formatter) - { - } - - ErrorOr format(AK::FormatBuilder&, DNS::RecordClass); -}; - -namespace IPC { - -template<> -ErrorOr encode(Encoder&, DNS::Answer const&); - -template<> -ErrorOr decode(Decoder&); - -} diff --git a/Userland/Libraries/LibDNS/CMakeLists.txt b/Userland/Libraries/LibDNS/CMakeLists.txt deleted file mode 100644 index 327558725b2..00000000000 --- a/Userland/Libraries/LibDNS/CMakeLists.txt +++ /dev/null @@ -1,8 +0,0 @@ -set(SOURCES - Answer.cpp - Name.cpp - Packet.cpp -) - -serenity_lib(LibDNS dns) -target_link_libraries(LibDNS PRIVATE LibIPC) diff --git a/Userland/Libraries/LibDNS/Name.cpp b/Userland/Libraries/LibDNS/Name.cpp deleted file mode 100644 index f0627f44926..00000000000 --- a/Userland/Libraries/LibDNS/Name.cpp +++ /dev/null @@ -1,111 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * Copyright (c) 2021, Sergey Bugaev - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "Name.h" -#include -#include -#include -#include - -namespace DNS { - -Name::Name(ByteString const& name) -{ - if (name.ends_with('.')) - m_name = name.substring(0, name.length() - 1); - else - m_name = name; -} - -ErrorOr Name::parse(ReadonlyBytes data, size_t& offset, size_t recursion_level) -{ - static constexpr size_t MAX_LABEL_SIZE = 63; - static constexpr size_t MAX_NAME_SIZE = 253; - - if (recursion_level > 4) - return Name {}; - - StringBuilder builder; - while (true) { - if (offset >= data.size()) - return Error::from_string_literal("Unexpected EOF when parsing name"); - u8 b = data[offset++]; - if (b == '\0') { - if (builder.length() > MAX_NAME_SIZE) - return Error::from_string_literal("Domain name exceeds maximum allowed length"); - // This terminates the name. - return builder.to_byte_string(); - } else if ((b & 0xc0) == 0xc0) { - // The two bytes tell us the offset when to continue from. - if (offset >= data.size()) - return Error::from_string_literal("Unexpected EOF when parsing name"); - size_t dummy = (b & 0x3f) << 8 | data[offset++]; - auto rest_of_name = TRY(parse(data, dummy, recursion_level + 1)); - builder.append(rest_of_name.as_string()); - if (builder.length() > MAX_NAME_SIZE) - return Error::from_string_literal("Domain name exceeds maximum allowed length"); - return builder.to_byte_string(); - } else { - // This is the length of a part. - if (offset + b >= data.size()) - return Error::from_string_literal("Unexpected EOF when parsing name"); - if (b > MAX_LABEL_SIZE) - return Error::from_string_literal("DNS label exceeds maximum allowed length"); - builder.append({ data.offset_pointer(offset), b }); - builder.append('.'); - if (builder.length() > MAX_NAME_SIZE) - return Error::from_string_literal("Domain name exceeds maximum allowed length"); - offset += b; - } - } -} - -size_t Name::serialized_size() const -{ - if (m_name.is_empty()) - return 1; - return m_name.length() + 2; -} - -void Name::randomize_case() -{ - StringBuilder builder; - for (char c : m_name) { - // Randomize the 0x20 bit in every ASCII character. - if (isalpha(c)) { - if (get_random_uniform(2)) - c |= 0x20; - else - c &= ~0x20; - } - builder.append(c); - } - m_name = builder.to_byte_string(); -} - -ErrorOr Name::write_to_stream(Stream& stream) const -{ - auto parts = as_string().split_view('.'); - for (auto& part : parts) { - TRY(stream.write_value(part.length())); - TRY(stream.write_until_depleted(part.bytes())); - } - TRY(stream.write_value('\0')); - return {}; -} - -unsigned Name::Traits::hash(Name const& name) -{ - return CaseInsensitiveStringTraits::hash(name.as_string()); -} - -bool Name::Traits::equals(Name const& a, Name const& b) -{ - return CaseInsensitiveStringTraits::equals(a.as_string(), b.as_string()); -} - -} diff --git a/Userland/Libraries/LibDNS/Name.h b/Userland/Libraries/LibDNS/Name.h deleted file mode 100644 index 29bbd39a496..00000000000 --- a/Userland/Libraries/LibDNS/Name.h +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * Copyright (c) 2021, Sergey Bugaev - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include - -namespace DNS { - -class Name { -public: - Name() = default; - Name(ByteString const&); - - static ErrorOr parse(ReadonlyBytes data, size_t& offset, size_t recursion_level = 0); - - size_t serialized_size() const; - ByteString const& as_string() const { return m_name; } - ErrorOr write_to_stream(Stream&) const; - - void randomize_case(); - - bool operator==(Name const& other) const { return Traits::equals(*this, other); } - - class Traits : public AK::Traits { - public: - static unsigned hash(Name const& name); - static bool equals(Name const&, Name const&); - }; - -private: - ByteString m_name; -}; - -} - -template<> -struct AK::Formatter : Formatter { - ErrorOr format(FormatBuilder& builder, DNS::Name const& value) - { - return Formatter::format(builder, value.as_string()); - } -}; diff --git a/Userland/Libraries/LibDNS/Packet.cpp b/Userland/Libraries/LibDNS/Packet.cpp deleted file mode 100644 index e829eaaf380..00000000000 --- a/Userland/Libraries/LibDNS/Packet.cpp +++ /dev/null @@ -1,187 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * Copyright (c) 2021, Sergey Bugaev - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "Packet.h" -#include "Name.h" -#include "PacketHeader.h" -#include -#include -#include -#include - -namespace DNS { - -void Packet::add_question(Question const& question) -{ - m_questions.empend(question); - - VERIFY(m_questions.size() <= UINT16_MAX); -} - -void Packet::add_answer(Answer const& answer) -{ - m_answers.empend(answer); - - VERIFY(m_answers.size() <= UINT16_MAX); -} - -ErrorOr Packet::to_byte_buffer() const -{ - PacketHeader header; - header.set_id(m_id); - if (is_query()) - header.set_is_query(); - else - header.set_is_response(); - header.set_authoritative_answer(m_authoritative_answer); - // FIXME: What should this be? - header.set_opcode(0); - header.set_response_code(m_code); - header.set_truncated(false); // hopefully... - header.set_recursion_desired(m_recursion_desired); - // FIXME: what should the be for requests? - header.set_recursion_available(m_recursion_available); - header.set_question_count(m_questions.size()); - header.set_answer_count(m_answers.size()); - - AllocatingMemoryStream stream; - - TRY(stream.write_value(header)); - for (auto& question : m_questions) { - TRY(stream.write_value(question.name())); - TRY(stream.write_value(htons((u16)question.record_type()))); - TRY(stream.write_value(htons(question.raw_class_code()))); - } - for (auto& answer : m_answers) { - TRY(stream.write_value(answer.name())); - TRY(stream.write_value(htons((u16)answer.type()))); - TRY(stream.write_value(htons(answer.raw_class_code()))); - TRY(stream.write_value(htonl(answer.ttl()))); - if (answer.type() == RecordType::PTR) { - Name name { answer.record_data() }; - TRY(stream.write_value(htons(name.serialized_size()))); - TRY(stream.write_value(name)); - } else { - TRY(stream.write_value(htons(answer.record_data().length()))); - TRY(stream.write_until_depleted(answer.record_data().bytes())); - } - } - - auto buffer = TRY(ByteBuffer::create_uninitialized(stream.used_buffer_size())); - TRY(stream.read_until_filled(buffer)); - return buffer; -} - -class [[gnu::packed]] DNSRecordWithoutName { -public: - DNSRecordWithoutName() = default; - - u16 type() const { return m_type; } - u16 record_class() const { return m_class; } - u32 ttl() const { return m_ttl; } - u16 data_length() const { return m_data_length; } - - void* data() { return this + 1; } - void const* data() const { return this + 1; } - -private: - NetworkOrdered m_type; - NetworkOrdered m_class; - NetworkOrdered m_ttl; - NetworkOrdered m_data_length; -}; - -static_assert(sizeof(DNSRecordWithoutName) == 10); - -ErrorOr Packet::from_raw_packet(ReadonlyBytes bytes) -{ - if (bytes.size() < sizeof(PacketHeader)) { - dbgln_if(LOOKUPSERVER_DEBUG, "DNS response not large enough ({} out of {}) to be a DNS packet", bytes.size(), sizeof(PacketHeader)); - return Error::from_string_literal("DNS response not large enough to be a DNS packet"); - } - - auto const& header = *bit_cast(bytes.data()); - dbgln_if(LOOKUPSERVER_DEBUG, "Got packet (ID: {})", header.id()); - dbgln_if(LOOKUPSERVER_DEBUG, " Question count: {}", header.question_count()); - dbgln_if(LOOKUPSERVER_DEBUG, " Answer count: {}", header.answer_count()); - dbgln_if(LOOKUPSERVER_DEBUG, " Authority count: {}", header.authority_count()); - dbgln_if(LOOKUPSERVER_DEBUG, "Additional count: {}", header.additional_count()); - - Packet packet; - packet.m_id = header.id(); - packet.m_query_or_response = header.is_response(); - packet.m_code = header.response_code(); - - // FIXME: Should we parse further in this case? - if (packet.code() != Code::NOERROR) - return packet; - - size_t offset = sizeof(PacketHeader); - - for (u16 i = 0; i < header.question_count(); i++) { - auto name = TRY(Name::parse(bytes, offset)); - struct RawDNSAnswerQuestion { - NetworkOrdered record_type; - NetworkOrdered class_code; - }; - if (offset >= bytes.size() || bytes.size() - offset < sizeof(RawDNSAnswerQuestion)) - return Error::from_string_literal("Unexpected EOF when parsing DNS packet"); - - auto const& record_and_class = *bit_cast(bytes.offset_pointer(offset)); - u16 class_code = record_and_class.class_code & ~MDNS_WANTS_UNICAST_RESPONSE; - bool mdns_wants_unicast_response = record_and_class.class_code & MDNS_WANTS_UNICAST_RESPONSE; - packet.m_questions.empend(name, (RecordType)(u16)record_and_class.record_type, (RecordClass)class_code, mdns_wants_unicast_response); - offset += 4; - auto& question = packet.m_questions.last(); - dbgln_if(LOOKUPSERVER_DEBUG, "Question #{}: name=_{}_, type={}, class={}", i, question.name(), question.record_type(), question.class_code()); - } - - for (u16 i = 0; i < header.answer_count(); ++i) { - auto name = TRY(Name::parse(bytes, offset)); - if (offset >= bytes.size() || bytes.size() - offset < sizeof(DNSRecordWithoutName)) - return Error::from_string_literal("Unexpected EOF when parsing DNS packet"); - - auto const& record = *bit_cast(bytes.offset_pointer(offset)); - offset += sizeof(DNSRecordWithoutName); - if (record.data_length() > bytes.size() - offset) - return Error::from_string_literal("Unexpected EOF when parsing DNS packet"); - - ByteString data; - - switch ((RecordType)record.type()) { - case RecordType::PTR: { - size_t dummy_offset = offset; - data = TRY(Name::parse(bytes, dummy_offset)).as_string(); - break; - } - case RecordType::CNAME: - // Fall through - case RecordType::A: - // Fall through - case RecordType::TXT: - // Fall through - case RecordType::AAAA: - // Fall through - case RecordType::SRV: - data = ReadonlyBytes { record.data(), record.data_length() }; - break; - default: - // FIXME: Parse some other record types perhaps? - dbgln("data=(unimplemented record type {})", (u16)record.type()); - } - - dbgln_if(LOOKUPSERVER_DEBUG, "Answer #{}: name=_{}_, type={}, ttl={}, length={}, data=_{}_", i, name, record.type(), record.ttl(), record.data_length(), data); - u16 class_code = record.record_class() & ~MDNS_CACHE_FLUSH; - bool mdns_cache_flush = record.record_class() & MDNS_CACHE_FLUSH; - packet.m_answers.empend(name, (RecordType)record.type(), (RecordClass)class_code, record.ttl(), data, mdns_cache_flush); - offset += record.data_length(); - } - - return packet; -} - -} diff --git a/Userland/Libraries/LibDNS/Packet.h b/Userland/Libraries/LibDNS/Packet.h deleted file mode 100644 index 8198bc6b069..00000000000 --- a/Userland/Libraries/LibDNS/Packet.h +++ /dev/null @@ -1,89 +0,0 @@ -/* - * Copyright (c) 2020, Andreas Kling - * Copyright (c) 2021, Sergey Bugaev - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include "Answer.h" -#include "Question.h" -#include -#include -#include - -namespace DNS { - -enum class ShouldRandomizeCase { - No = 0, - Yes -}; - -class Packet { -public: - Packet() = default; - - static ErrorOr from_raw_packet(ReadonlyBytes bytes); - ErrorOr to_byte_buffer() const; - - bool is_query() const { return !m_query_or_response; } - bool is_response() const { return m_query_or_response; } - bool is_authoritative_answer() const { return m_authoritative_answer; } - bool recursion_desired() const { return m_recursion_desired; } - bool recursion_available() const { return m_recursion_available; } - void set_is_query() { m_query_or_response = false; } - void set_is_response() { m_query_or_response = true; } - void set_authoritative_answer(bool authoritative_answer) { m_authoritative_answer = authoritative_answer; } - void set_recursion_desired(bool recursion_desired) { m_recursion_desired = recursion_desired; } - void set_recursion_available(bool recursion_available) { m_recursion_available = recursion_available; } - - u16 id() const { return m_id; } - void set_id(u16 id) { m_id = id; } - - Vector const& questions() const { return m_questions; } - Vector const& answers() const { return m_answers; } - - u16 question_count() const - { - VERIFY(m_questions.size() <= UINT16_MAX); - return m_questions.size(); - } - - u16 answer_count() const - { - VERIFY(m_answers.size() <= UINT16_MAX); - return m_answers.size(); - } - - void add_question(Question const&); - void add_answer(Answer const&); - - enum class Code : u8 { - NOERROR = 0, - FORMERR = 1, - SERVFAIL = 2, - NXDOMAIN = 3, - NOTIMP = 4, - REFUSED = 5, - YXDOMAIN = 6, - XRRSET = 7, - NOTAUTH = 8, - NOTZONE = 9, - }; - - Code code() const { return (Code)m_code; } - void set_code(Code code) { m_code = (u8)code; } - -private: - u16 m_id { 0 }; - u8 m_code { 0 }; - bool m_authoritative_answer { false }; - bool m_query_or_response { false }; - bool m_recursion_desired { true }; - bool m_recursion_available { true }; - Vector m_questions; - Vector m_answers; -}; - -} diff --git a/Userland/Libraries/LibDNS/PacketHeader.h b/Userland/Libraries/LibDNS/PacketHeader.h deleted file mode 100644 index fe4d46677d3..00000000000 --- a/Userland/Libraries/LibDNS/PacketHeader.h +++ /dev/null @@ -1,104 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include - -namespace DNS { - -class [[gnu::packed]] PacketHeader { -public: - PacketHeader() - : m_recursion_desired(false) - , m_truncated(false) - , m_authoritative_answer(false) - , m_opcode(0) - , m_query_or_response(false) - , m_response_code(0) - , m_checking_disabled(false) - , m_authenticated_data(false) - , m_zero(false) - , m_recursion_available(false) - { - } - - u16 id() const { return m_id; } - void set_id(u16 w) { m_id = w; } - - bool recursion_desired() const { return m_recursion_desired; } - void set_recursion_desired(bool b) { m_recursion_desired = b; } - - bool is_truncated() const { return m_truncated; } - void set_truncated(bool b) { m_truncated = b; } - - bool is_authoritative_answer() const { return m_authoritative_answer; } - void set_authoritative_answer(bool b) { m_authoritative_answer = b; } - - u8 opcode() const { return m_opcode; } - void set_opcode(u8 b) { m_opcode = b; } - - bool is_query() const { return !m_query_or_response; } - bool is_response() const { return m_query_or_response; } - void set_is_query() { m_query_or_response = false; } - void set_is_response() { m_query_or_response = true; } - - u8 response_code() const { return m_response_code; } - void set_response_code(u8 b) { m_response_code = b; } - - bool checking_disabled() const { return m_checking_disabled; } - void set_checking_disabled(bool b) { m_checking_disabled = b; } - - bool is_authenticated_data() const { return m_authenticated_data; } - void set_authenticated_data(bool b) { m_authenticated_data = b; } - - bool is_recursion_available() const { return m_recursion_available; } - void set_recursion_available(bool b) { m_recursion_available = b; } - - u16 question_count() const { return m_question_count; } - void set_question_count(u16 w) { m_question_count = w; } - - u16 answer_count() const { return m_answer_count; } - void set_answer_count(u16 w) { m_answer_count = w; } - - u16 authority_count() const { return m_authority_count; } - void set_authority_count(u16 w) { m_authority_count = w; } - - u16 additional_count() const { return m_additional_count; } - void set_additional_count(u16 w) { m_additional_count = w; } - - void* payload() { return this + 1; } - void const* payload() const { return this + 1; } - -private: - NetworkOrdered m_id; - - bool m_recursion_desired : 1; - bool m_truncated : 1; - bool m_authoritative_answer : 1; - u8 m_opcode : 4; - bool m_query_or_response : 1; - u8 m_response_code : 4; - bool m_checking_disabled : 1; - bool m_authenticated_data : 1; - bool m_zero : 1; - bool m_recursion_available : 1; - - NetworkOrdered m_question_count; - NetworkOrdered m_answer_count; - NetworkOrdered m_authority_count; - NetworkOrdered m_additional_count; -}; - -static_assert(sizeof(PacketHeader) == 12); - -} - -template<> -struct AK::Traits : public AK::DefaultTraits { - static constexpr bool is_trivially_serializable() { return true; } -}; diff --git a/Userland/Libraries/LibDNS/Question.h b/Userland/Libraries/LibDNS/Question.h deleted file mode 100644 index e01c2066220..00000000000 --- a/Userland/Libraries/LibDNS/Question.h +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright (c) 2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include "Answer.h" -#include "Name.h" -#include - -namespace DNS { - -#define MDNS_WANTS_UNICAST_RESPONSE 0x8000 - -class Question { -public: - Question(Name const& name, RecordType record_type, RecordClass class_code, bool mdns_wants_unicast_response) - : m_name(name) - , m_record_type(record_type) - , m_class_code(class_code) - , m_mdns_wants_unicast_response(mdns_wants_unicast_response) - { - } - - RecordType record_type() const { return m_record_type; } - RecordClass class_code() const { return m_class_code; } - u16 raw_class_code() const { return (u16)m_class_code | (m_mdns_wants_unicast_response ? MDNS_WANTS_UNICAST_RESPONSE : 0); } - Name const& name() const { return m_name; } - bool mdns_wants_unicast_response() const { return m_mdns_wants_unicast_response; } - -private: - Name m_name; - RecordType m_record_type { 0 }; - RecordClass m_class_code { 0 }; - bool m_mdns_wants_unicast_response { false }; -}; - -} diff --git a/Userland/Libraries/LibDesktop/AppFile.cpp b/Userland/Libraries/LibDesktop/AppFile.cpp deleted file mode 100644 index 0e33baf6f88..00000000000 --- a/Userland/Libraries/LibDesktop/AppFile.cpp +++ /dev/null @@ -1,236 +0,0 @@ -/* - * Copyright (c) 2020, Linus Groh - * Copyright (c) 2021, Spencer Dixon - * Copyright (c) 2022, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace Desktop { - -ByteString AppFile::app_file_path_for_app(StringView app_name) -{ - return ByteString::formatted("{}/{}.af", APP_FILES_DIRECTORY, app_name); -} - -bool AppFile::exists_for_app(StringView app_name) -{ - return FileSystem::exists(app_file_path_for_app(app_name)); -} - -NonnullRefPtr AppFile::get_for_app(StringView app_name) -{ - return open(app_file_path_for_app(app_name)); -} - -NonnullRefPtr AppFile::open(StringView path) -{ - return adopt_ref(*new AppFile(path)); -} - -void AppFile::for_each(Function)> callback, StringView directory) -{ - Core::DirIterator di(directory, Core::DirIterator::SkipDots); - if (di.has_error()) - return; - while (di.has_next()) { - auto name = di.next_path(); - if (!name.ends_with(".af"sv)) - continue; - auto path = ByteString::formatted("{}/{}", directory, name); - auto af = AppFile::open(path); - if (!af->is_valid()) - continue; - callback(af); - } -} - -AppFile::AppFile(StringView path) - : m_config(Core::ConfigFile::open(path).release_value_but_fixme_should_propagate_errors()) - , m_valid(validate()) -{ -} - -bool AppFile::validate() const -{ - if (m_config->read_entry("App", "Name").trim_whitespace().is_empty()) - return false; - if (m_config->read_entry("App", "Executable").trim_whitespace().is_empty()) - return false; - return true; -} - -ByteString AppFile::name() const -{ - auto name = m_config->read_entry("App", "Name").trim_whitespace().replace("&"sv, ""sv); - VERIFY(!name.is_empty()); - return name; -} - -ByteString AppFile::menu_name() const -{ - auto name = m_config->read_entry("App", "Name").trim_whitespace(); - VERIFY(!name.is_empty()); - return name; -} - -ByteString AppFile::executable() const -{ - auto executable = m_config->read_entry("App", "Executable").trim_whitespace(); - VERIFY(!executable.is_empty()); - return executable; -} - -Vector AppFile::arguments() const -{ - auto arguments = m_config->read_entry("App", "Arguments").trim_whitespace(); - return arguments.split(' '); -} - -ByteString AppFile::description() const -{ - return m_config->read_entry("App", "Description").trim_whitespace(); -} - -ByteString AppFile::category() const -{ - return m_config->read_entry("App", "Category").trim_whitespace(); -} - -ByteString AppFile::working_directory() const -{ - return m_config->read_entry("App", "WorkingDirectory").trim_whitespace(); -} - -ByteString AppFile::icon_path() const -{ - return m_config->read_entry("App", "IconPath").trim_whitespace(); -} - -GUI::Icon AppFile::icon() const -{ - auto override_icon = icon_path(); - // FIXME: support pointing to actual .ico files - if (!override_icon.is_empty()) - return GUI::FileIconProvider::icon_for_path(override_icon); - - return GUI::FileIconProvider::icon_for_path(executable()); -} - -bool AppFile::run_in_terminal() const -{ - return m_config->read_bool_entry("App", "RunInTerminal", false); -} - -bool AppFile::requires_root() const -{ - return m_config->read_bool_entry("App", "RequiresRoot", false); -} - -bool AppFile::exclude_from_system_menu() const -{ - return m_config->read_bool_entry("App", "ExcludeFromSystemMenu", false); -} - -Vector AppFile::launcher_mime_types() const -{ - Vector mime_types; - for (auto& entry : m_config->read_entry("Launcher", "MimeTypes").split(',')) { - entry = entry.trim_whitespace(); - if (!entry.is_empty()) - mime_types.append(entry); - } - return mime_types; -} - -Vector AppFile::launcher_file_types() const -{ - Vector file_types; - for (auto& entry : m_config->read_entry("Launcher", "FileTypes").split(',')) { - entry = entry.trim_whitespace(); - if (!entry.is_empty()) - file_types.append(entry); - } - return file_types; -} - -Vector AppFile::launcher_protocols() const -{ - Vector protocols; - for (auto& entry : m_config->read_entry("Launcher", "Protocols").split(',')) { - entry = entry.trim_whitespace(); - if (!entry.is_empty()) - protocols.append(entry); - } - return protocols; -} - -ErrorOr AppFile::spawn(ReadonlySpan user_arguments) const -{ - if (!is_valid()) - return Error::from_string_literal("AppFile is invalid"); - - Vector args; - - auto arguments = AppFile::arguments(); - for (auto const& argument : arguments) - args.append(argument); - - args.extend(Vector(user_arguments)); - - TRY(Core::Process::spawn(executable(), args, working_directory())); - return {}; -} - -ErrorOr AppFile::spawn_with_escalation(ReadonlySpan user_arguments) const -{ - if (!is_valid()) - return Error::from_string_literal("AppFile is invalid"); - - StringView exe; - Vector args; - - auto executable = AppFile::executable(); - auto arguments = AppFile::arguments(); - - for (auto const& argument : arguments) - args.append(argument); - - // FIXME: These single quotes won't be enough for executables with single quotes in their name. - auto pls_with_executable = ByteString::formatted("/bin/pls '{}'", executable); - if (run_in_terminal() && !requires_root()) { - exe = "/bin/Terminal"sv; - args = { "-e"sv, executable }; - } else if (!run_in_terminal() && requires_root()) { - exe = "/bin/Escalator"sv; - args = { executable }; - } else if (run_in_terminal() && requires_root()) { - exe = "/bin/Terminal"sv; - args = { "-e"sv, pls_with_executable }; - } else { - exe = executable; - } - args.extend(Vector(user_arguments)); - - TRY(Core::Process::spawn(exe, args.span(), - working_directory().is_empty() ? Core::StandardPaths::home_directory() : working_directory())); - return {}; -} - -void AppFile::spawn_with_escalation_or_show_error(GUI::Window& window, ReadonlySpan arguments) const -{ - if (auto result = spawn_with_escalation(arguments); result.is_error()) - GUI::MessageBox::show_error(&window, ByteString::formatted("Failed to spawn {} with escalation: {}", executable(), result.error())); -} - -} diff --git a/Userland/Libraries/LibDesktop/AppFile.h b/Userland/Libraries/LibDesktop/AppFile.h deleted file mode 100644 index e1eb49d6671..00000000000 --- a/Userland/Libraries/LibDesktop/AppFile.h +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright (c) 2020, Linus Groh - * Copyright (c) 2022, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include - -namespace Desktop { - -class AppFile : public RefCounted { -public: - static constexpr auto APP_FILES_DIRECTORY = "/res/apps"sv; - - static bool exists_for_app(StringView app_name); - static ByteString file_for_app(StringView app_name); - static ByteString app_file_path_for_app(StringView app_name); - - static NonnullRefPtr get_for_app(StringView app_name); - static NonnullRefPtr open(StringView path); - static void for_each(Function)>, StringView directory = APP_FILES_DIRECTORY); - ~AppFile() = default; - - bool is_valid() const { return m_valid; } - ByteString filename() const { return m_config->filename(); } - - ByteString name() const; - ByteString menu_name() const; - ByteString executable() const; - Vector arguments() const; - ByteString category() const; - ByteString description() const; - ByteString working_directory() const; - ByteString icon_path() const; - GUI::Icon icon() const; - bool run_in_terminal() const; - bool requires_root() const; - bool exclude_from_system_menu() const; - Vector launcher_mime_types() const; - Vector launcher_file_types() const; - Vector launcher_protocols() const; - ErrorOr spawn(ReadonlySpan arguments = {}) const; - ErrorOr spawn_with_escalation(ReadonlySpan arguments = {}) const; - void spawn_with_escalation_or_show_error(GUI::Window&, ReadonlySpan arguments = {}) const; - -private: - explicit AppFile(StringView path); - - bool validate() const; - - RefPtr m_config; - bool m_valid { false }; -}; - -} diff --git a/Userland/Libraries/LibDesktop/CMakeLists.txt b/Userland/Libraries/LibDesktop/CMakeLists.txt deleted file mode 100644 index 5320eb54a39..00000000000 --- a/Userland/Libraries/LibDesktop/CMakeLists.txt +++ /dev/null @@ -1,13 +0,0 @@ -set(SOURCES - AppFile.cpp - Launcher.cpp - Screensaver.cpp -) - -set(GENERATED_SOURCES - ../../Services/LaunchServer/LaunchClientEndpoint.h - ../../Services/LaunchServer/LaunchServerEndpoint.h -) - -serenity_lib(LibDesktop desktop) -target_link_libraries(LibDesktop PRIVATE LibCore LibIPC LibGfx LibGUI LibFileSystem LibURL) diff --git a/Userland/Libraries/LibDesktop/Launcher.cpp b/Userland/Libraries/LibDesktop/Launcher.cpp deleted file mode 100644 index ebaa1b5bce4..00000000000 --- a/Userland/Libraries/LibDesktop/Launcher.cpp +++ /dev/null @@ -1,120 +0,0 @@ -/* - * Copyright (c) 2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include -#include - -namespace Desktop { - -auto Launcher::Details::from_details_str(ByteString const& details_str) -> NonnullRefPtr
-{ - auto details = adopt_ref(*new Details); - auto json = JsonValue::from_string(details_str).release_value_but_fixme_should_propagate_errors(); - auto const& obj = json.as_object(); - details->executable = obj.get_byte_string("executable"sv).value_or({}); - details->name = obj.get_byte_string("name"sv).value_or({}); - - obj.get_array("arguments"sv).value().for_each([&](JsonValue const& argument) { - details->arguments.append(argument.as_string()); - }); - - if (auto type_value = obj.get_byte_string("type"sv); type_value.has_value()) { - auto const& type_str = type_value.value(); - if (type_str == "app") - details->launcher_type = LauncherType::Application; - else if (type_str == "userpreferred") - details->launcher_type = LauncherType::UserPreferred; - else if (type_str == "userdefault") - details->launcher_type = LauncherType::UserDefault; - } - return details; -} - -class ConnectionToLaunchServer final - : public IPC::ConnectionToServer - , public LaunchClientEndpoint { - IPC_CLIENT_CONNECTION(ConnectionToLaunchServer, "/tmp/session/%sid/portal/launch"sv) -private: - ConnectionToLaunchServer(NonnullOwnPtr socket) - : IPC::ConnectionToServer(*this, move(socket)) - { - } -}; - -static ConnectionToLaunchServer& connection() -{ - static auto connection = ConnectionToLaunchServer::try_create().release_value_but_fixme_should_propagate_errors(); - return connection; -} - -void Launcher::ensure_connection() -{ - [[maybe_unused]] auto& conn = connection(); -} - -ErrorOr Launcher::add_allowed_url(URL::URL const& url) -{ - auto response_or_error = connection().try_add_allowed_url(url); - if (response_or_error.is_error()) - return Error::from_string_literal("Launcher::add_allowed_url: Failed"); - return {}; -} - -ErrorOr Launcher::add_allowed_handler_with_any_url(ByteString const& handler) -{ - auto response_or_error = connection().try_add_allowed_handler_with_any_url(handler); - if (response_or_error.is_error()) - return Error::from_string_literal("Launcher::add_allowed_handler_with_any_url: Failed"); - return {}; -} - -ErrorOr Launcher::add_allowed_handler_with_only_specific_urls(ByteString const& handler, Vector const& urls) -{ - auto response_or_error = connection().try_add_allowed_handler_with_only_specific_urls(handler, urls); - if (response_or_error.is_error()) - return Error::from_string_literal("Launcher::add_allowed_handler_with_only_specific_urls: Failed"); - return {}; -} - -ErrorOr Launcher::seal_allowlist() -{ - auto response_or_error = connection().try_seal_allowlist(); - if (response_or_error.is_error()) - return Error::from_string_literal("Launcher::seal_allowlist: Failed"); - return {}; -} - -bool Launcher::open(const URL::URL& url, ByteString const& handler_name) -{ - return connection().open_url(url, handler_name); -} - -bool Launcher::open(const URL::URL& url, Details const& details) -{ - VERIFY(details.launcher_type != LauncherType::Application); // Launcher should not be used to execute arbitrary applications - return open(url, details.executable); -} - -Vector Launcher::get_handlers_for_url(const URL::URL& url) -{ - return connection().get_handlers_for_url(url.to_byte_string()); -} - -auto Launcher::get_handlers_with_details_for_url(const URL::URL& url) -> Vector> -{ - auto details = connection().get_handlers_with_details_for_url(url.to_byte_string()); - Vector> handlers_with_details; - for (auto& value : details) { - handlers_with_details.append(Details::from_details_str(value)); - } - return handlers_with_details; -} - -} diff --git a/Userland/Libraries/LibDesktop/Launcher.h b/Userland/Libraries/LibDesktop/Launcher.h deleted file mode 100644 index c7e445569d8..00000000000 --- a/Userland/Libraries/LibDesktop/Launcher.h +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright (c) 2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include - -namespace Desktop { - -class Launcher { -public: - enum class LauncherType { - Default = 0, - Application, - UserPreferred, - UserDefault - }; - - struct Details : public RefCounted
{ - ByteString name; - ByteString executable; - Vector arguments; - LauncherType launcher_type { LauncherType::Default }; - - static NonnullRefPtr
from_details_str(ByteString const&); - }; - - static void ensure_connection(); - static ErrorOr add_allowed_url(URL::URL const&); - static ErrorOr add_allowed_handler_with_any_url(ByteString const& handler); - static ErrorOr add_allowed_handler_with_only_specific_urls(ByteString const& handler, Vector const&); - static ErrorOr seal_allowlist(); - static bool open(const URL::URL&, ByteString const& handler_name = {}); - static bool open(const URL::URL&, Details const& details); - static Vector get_handlers_for_url(const URL::URL&); - static Vector> get_handlers_with_details_for_url(const URL::URL&); -}; - -} diff --git a/Userland/Libraries/LibDesktop/Screensaver.cpp b/Userland/Libraries/LibDesktop/Screensaver.cpp deleted file mode 100644 index ee90763186d..00000000000 --- a/Userland/Libraries/LibDesktop/Screensaver.cpp +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright (c) 2022, Jelle Raaijmakers - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include - -namespace Desktop { - -static constexpr int mouse_max_distance_move = 10; -static constexpr int mouse_tracking_delay_milliseconds = 750; - -ErrorOr> Screensaver::create_window(StringView title, StringView icon) -{ - auto window = GUI::Window::construct(); - window->set_double_buffering_enabled(false); - window->set_frameless(true); - window->set_fullscreen(true); - window->set_minimizable(false); - window->set_resizable(false); - window->set_title(title); - - auto app_icon = TRY(GUI::Icon::try_create_default_icon(icon)); - window->set_icon(app_icon.bitmap_for_size(16)); - - return window; -} - -void Screensaver::keydown_event(GUI::KeyEvent&) -{ - trigger_exit(); -} - -void Screensaver::mousedown_event(GUI::MouseEvent&) -{ - trigger_exit(); -} - -void Screensaver::mousemove_event(GUI::MouseEvent& event) -{ - auto now = MonotonicTime::now(); - if ((now - m_start_time).to_milliseconds() < mouse_tracking_delay_milliseconds) - return; - - if (!m_mouse_origin.has_value()) - m_mouse_origin = event.position(); - else if (event.position().distance_from(m_mouse_origin.value()) > mouse_max_distance_move) - trigger_exit(); -} - -void Screensaver::trigger_exit() -{ - if (on_screensaver_exit) - on_screensaver_exit(); -} - -} diff --git a/Userland/Libraries/LibDesktop/Screensaver.h b/Userland/Libraries/LibDesktop/Screensaver.h deleted file mode 100644 index 8fc452e035e..00000000000 --- a/Userland/Libraries/LibDesktop/Screensaver.h +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright (c) 2022, Jelle Raaijmakers - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include -#include -#include -#include - -namespace Desktop { - -class Screensaver : public GUI::Widget { - C_OBJECT_ABSTRACT(Screensaver) -public: - Function on_screensaver_exit; - - static ErrorOr> create_window(StringView title, StringView icon); - - virtual void keydown_event(GUI::KeyEvent&) override; - virtual void mousedown_event(GUI::MouseEvent& event) override; - virtual void mousemove_event(GUI::MouseEvent& event) override; - -protected: - Screensaver() - : m_start_time(MonotonicTime::now()) - { - } - -private: - void trigger_exit(); - - Optional m_mouse_origin; - MonotonicTime m_start_time; -}; - -} diff --git a/Userland/Libraries/LibDeviceTree/CMakeLists.txt b/Userland/Libraries/LibDeviceTree/CMakeLists.txt deleted file mode 100644 index 78b2e0241cb..00000000000 --- a/Userland/Libraries/LibDeviceTree/CMakeLists.txt +++ /dev/null @@ -1,8 +0,0 @@ - -set(SOURCES - DeviceTree.cpp - FlattenedDeviceTree.cpp - Validation.cpp -) - -serenity_lib(LibDeviceTree DeviceTree) diff --git a/Userland/Libraries/LibDeviceTree/DeviceTree.cpp b/Userland/Libraries/LibDeviceTree/DeviceTree.cpp deleted file mode 100644 index f29951ee2a6..00000000000 --- a/Userland/Libraries/LibDeviceTree/DeviceTree.cpp +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Copyright (c) 2024, Leon Albrecht - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "DeviceTree.h" -#include "FlattenedDeviceTree.h" -#include -#include - -namespace DeviceTree { - -ErrorOr> DeviceTree::parse(ReadonlyBytes flattened_device_tree) -{ - // Device tree must be 8-byte aligned - if ((bit_cast(flattened_device_tree.data()) & 0b111) != 0) - return Error::from_errno(EINVAL); - - auto device_tree = TRY(adopt_nonnull_own_or_enomem(new (nothrow) DeviceTree { flattened_device_tree })); - DeviceTreeNodeView* current_node = device_tree.ptr(); - - auto const& header = *reinterpret_cast(flattened_device_tree.data()); - - TRY(walk_device_tree(header, flattened_device_tree, - { - .on_node_begin = [¤t_node, &device_tree](StringView name) -> ErrorOr { - // Skip the root node, which has an empty name - if (current_node == device_tree.ptr() && name.is_empty()) - return IterationDecision::Continue; - - // FIXME: Use something like children.emplace - TRY(current_node->children().try_set(name, DeviceTreeNodeView { current_node })); - auto& new_node = current_node->children().get(name).value(); - current_node = &new_node; - return IterationDecision::Continue; - }, - .on_node_end = [¤t_node](StringView) -> ErrorOr { - current_node = current_node->parent(); - return IterationDecision::Continue; - }, - .on_property = [¤t_node](StringView name, ReadonlyBytes value) -> ErrorOr { - TRY(current_node->properties().try_set(name, DeviceTreeProperty { value })); - return IterationDecision::Continue; - }, - .on_noop = []() -> ErrorOr { - return IterationDecision::Continue; - }, - .on_end = [&]() -> ErrorOr { - return {}; - }, - })); - - // FIXME: While growing the a nodes children map, we might have reallocated it's storage - // breaking the parent pointers of the children, so we need to fix them here - auto fix_parent = [](auto self, DeviceTreeNodeView& node) -> void { - for (auto& [name, child] : node.children()) { - child.m_parent = &node; - self(self, child); - } - }; - - fix_parent(fix_parent, *device_tree); - // Note: For the same reason as above, we need to postpone setting the phandles until the tree is fully built - TRY(device_tree->for_each_node([&device_tree]([[maybe_unused]] StringView name, DeviceTreeNodeView& node) -> ErrorOr { - if (auto phandle = node.get_property("phandle"sv); phandle.has_value()) { - auto phandle_value = phandle.value().as(); - TRY(device_tree->set_phandle(phandle_value, &node)); - } - return RecursionDecision::Recurse; - })); - - return device_tree; -} - -} diff --git a/Userland/Libraries/LibDeviceTree/DeviceTree.h b/Userland/Libraries/LibDeviceTree/DeviceTree.h deleted file mode 100644 index 793de276f2a..00000000000 --- a/Userland/Libraries/LibDeviceTree/DeviceTree.h +++ /dev/null @@ -1,268 +0,0 @@ -/* - * Copyright (c) 2024, Leon Albrecht - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace DeviceTree { - -struct DeviceTreeProperty { - class ValueStream : public FixedMemoryStream { - public: - using AK::FixedMemoryStream::FixedMemoryStream; - - ErrorOr read_cell() - { - return read_value>(); - } - - ErrorOr read_cells(u32 cell_size) - { - // FIXME: There are rare cases of 3 cell size big values, even in addresses, especially in addresses - VERIFY(cell_size <= 2); - if (cell_size == 1) - return read_value>(); - return read_value>(); - } - }; - - ReadonlyBytes raw_data; - - size_t size() const { return raw_data.size(); } - - StringView as_string() const { return StringView(raw_data.data(), raw_data.size() - 1); } - Vector as_strings() const { return as_string().split_view('\0'); } - template - auto for_each_string(T callback) const { return as_string().for_each_split_view('\0', SplitBehavior::Nothing, callback); } - - // Note: as does not convert endianness, so all structures passed in - // should use BigEndians for their members and keep ordering in mind - // Note: The Integral variant does convert endianness, so no need to pass in BigEndians - template - T as() const - { - VERIFY(raw_data.size() == sizeof(T)); - T value; - __builtin_memcpy(&value, raw_data.data(), sizeof(T)); - return value; - } - - template - requires(alignof(T) <= 4 && !IsIntegral) - T const& as() const - { - return *reinterpret_cast(raw_data.data()); - } - - template - I as() const - { - VERIFY(raw_data.size() == sizeof(I)); - BigEndian value; - __builtin_memcpy(&value, raw_data.data(), sizeof(I)); - return value; - } - - template - ErrorOr for_each_in_array_of(CallableAs, T const&> auto callback) const - { - VERIFY(raw_data.size() % sizeof(T) == 0); - size_t count = raw_data.size() / sizeof(T); - size_t offset = 0; - for (size_t i = 0; i < count; ++i, offset += sizeof(T)) { - auto sub_property = DeviceTreeProperty { raw_data.slice(offset, sizeof(T)) }; - auto result = callback(sub_property.as()); - if (result.is_error()) - return result; - if (result.value() == IterationDecision::Break) - break; - } - return {}; - } - - ValueStream as_stream() const { return ValueStream { raw_data }; } -}; - -class DeviceTreeNodeView { - AK_MAKE_NONCOPYABLE(DeviceTreeNodeView); - AK_MAKE_DEFAULT_MOVABLE(DeviceTreeNodeView); - -public: - bool has_property(StringView prop) const { return m_properties.contains(prop); } - bool has_child(StringView child) const { return m_children.contains(child); } - bool child(StringView name) const { return has_property(name) || has_child(name); } - - Optional get_property(StringView prop) const { return m_properties.get(prop); } - - // FIXME: The spec says that @address parts of the name should be ignored when looking up nodes - // when they do not appear in the queried name, and all nodes with the same name should be returned - Optional get_child(StringView child) const { return m_children.get(child); } - - HashMap const& children() const { return m_children; } - HashMap const& properties() const { return m_properties; } - - DeviceTreeNodeView const* parent() const { return m_parent; } - - // FIXME: Add convenience functions for common properties like "reg" and "compatible" - // Note: The "reg" property is a list of address and size pairs, but the address is not always a u32 or u64 - // In pci devices the #address-size is 3 cells: (phys.lo phys.mid phys.hi) - // with the following format: - // phys.lo, phys.mid: 64-bit Address - BigEndian - // phys.hi: relocatable(1), prefetchable(1), aliased(1), 000(3), space type(2), bus number(8), device number(5), function number(3), register number(8) - BigEndian - - // FIXME: Stringify? - // FIXME: Flatten? - // Note: That we dont have a oder of children and properties in this view -protected: - friend class DeviceTree; - DeviceTreeNodeView(DeviceTreeNodeView* parent) - : m_parent(parent) - { - } - HashMap& children() { return m_children; } - HashMap& properties() { return m_properties; } - DeviceTreeNodeView* parent() { return m_parent; } - -private: - DeviceTreeNodeView* m_parent; - HashMap m_children; - HashMap m_properties; -}; - -class DeviceTree : public DeviceTreeNodeView { -public: - static ErrorOr> parse(ReadonlyBytes); - - DeviceTreeNodeView const* resolve_node(StringView path) const - { - // FIXME: May children of aliases be referenced? - // Note: Aliases may not contain a '/' in their name - // And as all paths other than aliases should start with '/', we can just check for the first '/' - if (!path.starts_with('/')) { - if (auto alias_list = get_child("aliases"sv); alias_list.has_value()) { - if (auto alias = alias_list->get_property(path); alias.has_value()) { - path = alias.value().as_string(); - } else { - dbgln("DeviceTree: '{}' not found in /aliases, treating as absolute path", path); - } - } else { - dbgln("DeviceTree: No /aliases node found, treating '{}' as absolute path", path); - } - } - - DeviceTreeNodeView const* node = this; - path.for_each_split_view('/', SplitBehavior::Nothing, [&](auto const& part) { - if (auto child = node->get_child(part); child.has_value()) { - node = &child.value(); - } else { - node = nullptr; - return IterationDecision::Break; - } - return IterationDecision::Continue; - }); - - return node; - } - - Optional resolve_property(StringView path) const - { - auto property_name = path.find_last_split_view('/'); - auto node_path = path.substring_view(0, path.length() - property_name.length() - 1); - auto const* node = resolve_node(node_path); - if (!node) - return {}; - return node->get_property(property_name); - } - - // FIXME: Add a helper to iterate over each descendant fulfilling some properties - // Like each node with a "compatible" property containing "pci" or "usb", - // bonus points if it could automatically recurse in the tree under some conditions, - // like "simple-bus" or "pci-bridge" nodes - - auto for_each_node(CallableAs, StringView, DeviceTreeNodeView const&> auto callback) const - { - auto iterate = [&](auto self, StringView name, DeviceTreeNodeView const& node) -> ErrorOr { - auto result = TRY(callback(name, node)); - - if (result == RecursionDecision::Recurse) { - for (auto const& [name, child] : node.children()) { - auto child_result = TRY(self(self, name, child)); - - if (child_result == RecursionDecision::Break) - return RecursionDecision::Break; - } - - return RecursionDecision::Continue; - } - - return result; - }; - - return iterate(iterate, "/"sv, *this); - } - - DeviceTreeNodeView const* phandle(u32 phandle) const - { - if (phandle >= m_phandles.size()) - return nullptr; - return m_phandles[phandle]; - } - - ReadonlyBytes flattened_device_tree() const { return m_flattened_device_tree; } - -private: - DeviceTree(ReadonlyBytes flattened_device_tree) - : DeviceTreeNodeView(nullptr) - , m_flattened_device_tree(flattened_device_tree) - { - } - - auto for_each_node(CallableAs, StringView, DeviceTreeNodeView&> auto callback) - { - auto iterate = [&](auto self, StringView name, DeviceTreeNodeView& node) -> ErrorOr { - auto result = TRY(callback(name, node)); - - if (result == RecursionDecision::Recurse) { - for (auto& [name, child] : node.children()) { - auto child_result = TRY(self(self, name, child)); - - if (child_result == RecursionDecision::Break) - return RecursionDecision::Break; - } - - return RecursionDecision::Continue; - } - - return result; - }; - - return iterate(iterate, "/"sv, *this); - } - - ErrorOr set_phandle(u32 phandle, DeviceTreeNodeView* node) - { - if (m_phandles.size() > phandle && m_phandles[phandle] != nullptr) - return Error::from_string_view_or_print_error_and_return_errno("Duplicate phandle entry in DeviceTree"sv, EINVAL); - if (m_phandles.size() <= phandle) - TRY(m_phandles.try_resize(phandle + 1)); - m_phandles[phandle] = node; - return {}; - } - - ReadonlyBytes m_flattened_device_tree; - Vector m_phandles; -}; - -} diff --git a/Userland/Libraries/LibDeviceTree/FlattenedDeviceTree.cpp b/Userland/Libraries/LibDeviceTree/FlattenedDeviceTree.cpp deleted file mode 100644 index af523beed70..00000000000 --- a/Userland/Libraries/LibDeviceTree/FlattenedDeviceTree.cpp +++ /dev/null @@ -1,179 +0,0 @@ -/* - * Copyright (c) 2023, Andrew Kaster - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include -#include -#include - -#ifdef KERNEL -# include -#else -# include -#endif - -namespace DeviceTree { - -static ErrorOr read_string_view(ReadonlyBytes bytes, StringView error_string) -{ - auto len = strnlen(reinterpret_cast(bytes.data()), bytes.size()); - if (len == bytes.size()) { - return Error::from_string_view_or_print_error_and_return_errno(error_string, EINVAL); - } - return StringView { bytes.slice(0, len) }; -} - -ErrorOr walk_device_tree(FlattenedDeviceTreeHeader const& header, ReadonlyBytes raw_device_tree, DeviceTreeCallbacks callbacks) -{ - ReadonlyBytes struct_bytes { raw_device_tree.data() + header.off_dt_struct, header.size_dt_struct }; - FixedMemoryStream stream(struct_bytes); - char const* begin_strings_block = reinterpret_cast(raw_device_tree.data() + header.off_dt_strings); - - FlattenedDeviceTreeTokenType prev_token = EndNode; - StringView current_node_name; - - while (!stream.is_eof()) { - auto current_token = TRY(stream.read_value>()); - - switch (current_token) { - case BeginNode: { - current_node_name = TRY(read_string_view(struct_bytes.slice(stream.offset()), "Non-null terminated name for FDT_BEGIN_NODE token!"sv)); - size_t const consume_len = round_up_to_power_of_two(current_node_name.length() + 1, 4); - TRY(stream.discard(consume_len)); - if (callbacks.on_node_begin) { - if (IterationDecision::Break == TRY(callbacks.on_node_begin(current_node_name))) - return {}; - } - break; - } - case EndNode: - if (callbacks.on_node_end) { - if (IterationDecision::Break == TRY(callbacks.on_node_end(current_node_name))) - return {}; - } - break; - case Property: { - if (prev_token == EndNode) { - return Error::from_string_view_or_print_error_and_return_errno("Invalid node sequence, FDT_PROP after FDT_END_NODE"sv, EINVAL); - } - auto len = TRY(stream.read_value>()); - auto nameoff = TRY(stream.read_value>()); - if (nameoff >= header.size_dt_strings) { - return Error::from_string_view_or_print_error_and_return_errno("Invalid name offset in FDT_PROP"sv, EINVAL); - } - size_t const prop_name_max_len = header.size_dt_strings - nameoff; - size_t const prop_name_len = strnlen(begin_strings_block + nameoff, prop_name_max_len); - if (prop_name_len == prop_name_max_len) { - return Error::from_string_view_or_print_error_and_return_errno("Non-null terminated name for FDT_PROP token!"sv, EINVAL); - } - StringView prop_name(begin_strings_block + nameoff, prop_name_len); - if (len >= stream.remaining()) { - return Error::from_string_view_or_print_error_and_return_errno("Property value length too large"sv, EINVAL); - } - ReadonlyBytes prop_value; - if (len != 0) { - prop_value = { struct_bytes.slice(stream.offset()).data(), len }; - size_t const consume_len = round_up_to_power_of_two(static_cast(len), 4); - TRY(stream.discard(consume_len)); - } - if (callbacks.on_property) { - if (IterationDecision::Break == TRY(callbacks.on_property(prop_name, prop_value))) - return {}; - } - break; - } - case NoOp: - if (callbacks.on_noop) { - if (IterationDecision::Break == TRY(callbacks.on_noop())) - return {}; - } - break; - case End: { - if (prev_token == BeginNode || prev_token == Property) { - return Error::from_string_view_or_print_error_and_return_errno("Invalid node sequence, FDT_END after BEGIN_NODE or PROP"sv, EINVAL); - } - if (!stream.is_eof()) { - return Error::from_string_view_or_print_error_and_return_errno("Expected EOF at FTD_END but more data remains"sv, EINVAL); - } - - if (callbacks.on_end) { - return callbacks.on_end(); - } - return {}; - } - default: - return Error::from_string_view_or_print_error_and_return_errno("Invalid token"sv, EINVAL); - } - prev_token = static_cast(static_cast(current_token)); - } - return Error::from_string_view_or_print_error_and_return_errno("Unexpected end of stream"sv, EINVAL); -} - -static ErrorOr slow_get_property_raw(StringView name, FlattenedDeviceTreeHeader const& header, ReadonlyBytes raw_device_tree) -{ - // Name is a path like /path/to/node/property - Vector path; - TRY(name.for_each_split_view('/', SplitBehavior::Nothing, [&path](StringView view) -> ErrorOr { - if (path.size() == path.capacity()) { - return Error::from_errno(ENAMETOOLONG); - } - // This can never fail as all entries go into the inline buffer, enforced by the check above. - path.unchecked_append(view); - return {}; - })); - - bool check_property_name = path.size() == 1; // Properties on root node should be checked immediately - ssize_t current_path_idx = -1; // Start "before" the root FDT_BEGIN_NODE tag - ReadonlyBytes found_property_value; - - DeviceTreeCallbacks callbacks = { - .on_node_begin = [&](StringView token_name) -> ErrorOr { - if (current_path_idx < 0) { - ++current_path_idx; // Root node - return IterationDecision::Continue; - } - // FIXME: This might need to ignore address details in the path - if (token_name == path[current_path_idx]) { - ++current_path_idx; - if (current_path_idx == static_cast(path.size() - 1)) { - check_property_name = true; - } - } - return IterationDecision::Continue; - }, - .on_node_end = [&](StringView) -> ErrorOr { - if (check_property_name) { - // Not found, but we were looking for the property - return Error::from_errno(EINVAL); - } - return IterationDecision::Continue; - }, - .on_property = [&](StringView property_name, ReadonlyBytes property_value) -> ErrorOr { - if (check_property_name && property_name == path[current_path_idx]) { - found_property_value = property_value; - return IterationDecision::Break; - } - return IterationDecision::Continue; - }, - .on_noop = nullptr, - .on_end = [&]() -> ErrorOr { - return Error::from_string_view_or_print_error_and_return_errno("Property not found"sv, EINVAL); - } - }; - - TRY(walk_device_tree(header, raw_device_tree, move(callbacks))); - return found_property_value; -} - -ErrorOr slow_get_property(StringView name, FlattenedDeviceTreeHeader const& header, ReadonlyBytes raw_device_tree) -{ - return DeviceTreeProperty { TRY(slow_get_property_raw(name, header, raw_device_tree)) }; -} - -} // namespace DeviceTree diff --git a/Userland/Libraries/LibDeviceTree/FlattenedDeviceTree.h b/Userland/Libraries/LibDeviceTree/FlattenedDeviceTree.h deleted file mode 100644 index 7b2d263725e..00000000000 --- a/Userland/Libraries/LibDeviceTree/FlattenedDeviceTree.h +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright (c) 2021-2023, Andrew Kaster - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include -#include - -#include "DeviceTree.h" - -namespace DeviceTree { - -// https://devicetree-specification.readthedocs.io/en/v0.3/flattened-format.html -struct FlattenedDeviceTreeHeader { - BigEndian magic; // 0xDOODFEED (BE) - BigEndian totalsize; // Total size of entire blob including padding b/w and after fields - BigEndian off_dt_struct; // Offset of StructureBlock from beginning of header - // https://devicetree-specification.readthedocs.io/en/v0.3/flattened-format.html#sect-fdt-structure-block - BigEndian off_dt_strings; // Offset of StringsBlock from beginning of header - // https://devicetree-specification.readthedocs.io/en/v0.3/flattened-format.html#sect-fdt-strings-block - BigEndian off_mem_rsvmap; // Offset of MemoryReservationBlock from beginning of header - BigEndian version; // 0.3 spec defines version 17 - BigEndian last_comp_version; // 0.3 spec back-compatible with version 16, mandated to be 16 - BigEndian boot_cpuid_phys; // Physical ID given in `reg` property of CPU node of boot cpu - BigEndian size_dt_strings; // Length in bytes of StringsBlock - BigEndian size_dt_struct; // Length in bytes of StructureBlock -}; -static_assert(sizeof(FlattenedDeviceTreeHeader) == 40, "FDT Header size must match specification"); - -// https://devicetree-specification.readthedocs.io/en/v0.3/flattened-format.html#format -struct FlattenedDeviceTreeReserveEntry { - BigEndian address; - BigEndian size; - - bool operator==(FlattenedDeviceTreeReserveEntry const& other) const { return other.address == address && other.size == size; } -}; -static_assert(sizeof(FlattenedDeviceTreeReserveEntry) == 16, "FDT Memory Reservation entry size must match specification"); - -// https://devicetree-specification.readthedocs.io/en/v0.3/flattened-format.html#lexical-structure -enum FlattenedDeviceTreeTokenType : u32 { - BeginNode = 1, - EndNode = 2, - Property = 3, - NoOp = 4, - End = 9 -}; - -struct DeviceTreeCallbacks { - Function(StringView)> on_node_begin; - Function(StringView)> on_node_end; - Function(StringView, ReadonlyBytes)> on_property; - Function()> on_noop; - Function()> on_end; -}; - -ErrorOr walk_device_tree(FlattenedDeviceTreeHeader const&, ReadonlyBytes raw_device_tree, DeviceTreeCallbacks); - -ErrorOr slow_get_property(StringView name, FlattenedDeviceTreeHeader const&, ReadonlyBytes raw_device_tree); - -} // namespace DeviceTree diff --git a/Userland/Libraries/LibDeviceTree/Validation.cpp b/Userland/Libraries/LibDeviceTree/Validation.cpp deleted file mode 100644 index e33845108a9..00000000000 --- a/Userland/Libraries/LibDeviceTree/Validation.cpp +++ /dev/null @@ -1,224 +0,0 @@ -/* - * Copyright (c) 2021-2023, Andrew Kaster - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace DeviceTree { - -#ifdef KERNEL -# define warnln dbgln -# define outln dbgln -#endif - -bool validate_flattened_device_tree(FlattenedDeviceTreeHeader const& header, ReadonlyBytes raw_device_tree, Verbose verbose) -{ - if (header.magic != 0xD00DFEEDU) { - if (verbose == Verbose::Yes) - warnln("FDT Header has invalid magic value {:#08x}. Are you sure it's a flattened device tree?", header.magic); - return false; - } - - if ((header.off_mem_rsvmap & ~0x7) != header.off_mem_rsvmap) { - if (verbose == Verbose::Yes) - warnln("FDT Header's MemoryReservationBlock is not 8 byte aligned! Offset: {:#08x}", header.off_mem_rsvmap); - return false; - } - - if ((header.off_dt_struct & ~0x3) != header.off_dt_struct) { - if (verbose == Verbose::Yes) - warnln("FDT Header's StructureBlock is not 4 byte aligned! Offset: {:#08x}", header.off_dt_struct); - return false; - } - - if (header.totalsize != raw_device_tree.size()) { - if (verbose == Verbose::Yes) - warnln("FDT Header total size mismatch: {}, expected {}!", header.totalsize, raw_device_tree.size()); - return false; - } - - if (header.off_dt_struct > raw_device_tree.size()) { - if (verbose == Verbose::Yes) - warnln("FDT Header reports larger StructureBlock offset than possible: {} but total size is {}!", header.off_dt_struct, raw_device_tree.size()); - return false; - } - - if (header.off_dt_strings > raw_device_tree.size()) { - if (verbose == Verbose::Yes) - warnln("FDT Header reports larger StringsBlock offset than possible: {} but total size is {}!", header.off_dt_strings, raw_device_tree.size()); - return false; - } - - if (header.off_mem_rsvmap > raw_device_tree.size()) { - if (verbose == Verbose::Yes) - warnln("FDT Header reports larger MemoryReservationBlock offset than possible: {} but total size is {}!", header.off_mem_rsvmap, raw_device_tree.size()); - return false; - } - - // Verify format is correct. Header --> MemoryReservation --> Structures --> Strings - if (header.off_dt_strings <= header.off_dt_struct) { - if (verbose == Verbose::Yes) - warnln("FDT Header has invalid StringsBlock offset {}, must be after v (@ {})", header.off_dt_strings, header.off_dt_struct); - return false; - } - - if (header.off_dt_struct <= header.off_mem_rsvmap) { - if (verbose == Verbose::Yes) - warnln("FDT Header has invalid StructureBlock offset {}, must be after MemoryReservationBlock (@ {})", header.off_dt_struct, header.off_mem_rsvmap); - return false; - } - - if (header.version != 17) { - if (verbose == Verbose::Yes) - warnln("Expected FDT header version 17, got {}", header.version); - return false; - } - - if (header.last_comp_version != 16) { - if (verbose == Verbose::Yes) - warnln("Expected FDT header last compatible version 16, got {}", header.last_comp_version); - return false; - } - - auto* mem_reserve_block = reinterpret_cast(&raw_device_tree[header.off_mem_rsvmap]); - u64 next_block_offset = header.off_mem_rsvmap + sizeof(FlattenedDeviceTreeReserveEntry); - while ((next_block_offset < header.off_dt_struct) && (*mem_reserve_block != FlattenedDeviceTreeReserveEntry {})) { - ++mem_reserve_block; - next_block_offset += sizeof(FlattenedDeviceTreeReserveEntry); - } - - if (next_block_offset >= header.off_dt_strings) { - if (verbose == Verbose::Yes) - warnln("FDT malformed, MemoryReservationBlock spill into StructureBlock section!"); - return false; - } - - // check for overlap. Overflow not possible b/c the fields are u32 - u64 structure_block_size = header.off_dt_struct + header.size_dt_struct; - if ((structure_block_size > header.off_dt_strings) || (structure_block_size > raw_device_tree.size())) { - if (verbose == Verbose::Yes) - warnln("FDT Header reports invalid StructureBlock block size: {} is too large given StringsBlock offset {} and total size {}", structure_block_size, header.off_dt_strings, raw_device_tree.size()); - return false; - } - - u64 strings_block_size = header.off_dt_strings + header.size_dt_strings; - if (strings_block_size > raw_device_tree.size()) { - if (verbose == Verbose::Yes) - warnln("FDT Header reports invalid StringsBlock size: {} is too large given total size {}", strings_block_size, raw_device_tree.size()); - return false; - } - - return true; -} - -ErrorOr dump(FlattenedDeviceTreeHeader const& header, ReadonlyBytes raw_device_tree) -{ - outln("/dts-v1/;"); - outln("// magic: {:#08x}", header.magic); - outln("// totalsize: {:#08x} ({})", header.totalsize, header.totalsize); - outln("// off_dt_struct: {:#x}", header.off_dt_struct); - outln("// off_dt_strings: {:#x}", header.off_dt_strings); - outln("// off_mem_rsvmap: {:#x}", header.off_mem_rsvmap); - outln("// version: {}", header.version); - outln("// last_comp_version: {}", header.last_comp_version); - outln("// boot_cpuid_phys: {:#x}", header.boot_cpuid_phys); - outln("// size_dt_strings: {:#x}", header.size_dt_strings); - outln("// size_dt_struct: {:#x}", header.size_dt_struct); - - if (!validate_flattened_device_tree(header, raw_device_tree, Verbose::Yes)) - return Error::from_errno(EINVAL); - - // Now that we know the device tree is valid, print out the rest of the information - auto const* mem_reserve_block = reinterpret_cast(&raw_device_tree[header.off_mem_rsvmap]); - u64 next_block_offset = header.off_mem_rsvmap + sizeof(FlattenedDeviceTreeReserveEntry); - while ((next_block_offset < header.off_dt_struct) && (*mem_reserve_block != FlattenedDeviceTreeReserveEntry {})) { - outln("/memreserve/ {:#08x} {:#08x};", mem_reserve_block->address, mem_reserve_block->size); - ++mem_reserve_block; - next_block_offset += sizeof(FlattenedDeviceTreeReserveEntry); - } - - return dump_flattened_device_tree_structure(header, raw_device_tree); -} - -ErrorOr dump_flattened_device_tree_structure(FlattenedDeviceTreeHeader const& header, ReadonlyBytes raw_device_tree) -{ - u8 indent = 0; - DeviceTreeCallbacks callbacks = { - .on_node_begin = [&](StringView token_name) -> ErrorOr { - // Root Entry: - if (indent == 0) - outln("/ {{"); - else - outln("{: >{}}{} {{", ""sv, indent * 2, token_name); - ++indent; - return IterationDecision::Continue; - }, - .on_node_end = [&](StringView) -> ErrorOr { - --indent; - outln("{: >{}}}};", ""sv, indent * 2); - return IterationDecision::Continue; - }, - .on_property = [&](StringView property_name, ReadonlyBytes property_value) -> ErrorOr { - // Note: We want to figure out if the value is a string, a stringlist, a number or something unprintable. - // In reality, the entity retrieving the value needs to know if it's a u32, u64, string, stringlist, or "property-encoded-value" a priori - bool had_valid_character = false; - bool const is_print = all_of(property_value, [&had_valid_character](char c) -> bool { - if (AK::is_ascii_printable(c)) { - had_valid_character = true; - return true; - } - if (had_valid_character) { - had_valid_character = false; - return c == 0; - } - return false; - }); - if (property_value.size() == 0) { - outln("{: >{}}{};", ""sv, indent * 2, property_name); - } else if (is_print) { - StringView property_as_string { property_value }; - StringBuilder property; - TRY(property.try_appendff("{: >{}}{} = ", ""sv, indent * 2, property_name)); - TRY(property.try_join(", "sv, property_as_string.split_view('\00'), "\"{}\""sv)); - outln("{};", property.string_view()); - } else { - StringBuilder property; - if (property_value.size() % 4 != 0) { - // This is the best hint we can use, that we are given an array - // without looking at the schema of the current tree node - TRY(property.try_appendff("{: >{}}{} = [", ""sv, indent * 2, property_name)); - TRY(property.try_join(' ', property_value, "{:02x}"sv)); - outln("{}];", property.string_view()); - } else { - TRY(property.try_appendff("{: >{}}{} = <", ""sv, indent * 2, property_name)); - auto value_stream = FixedMemoryStream(property_value); - bool first_value = true; - while (!value_stream.is_eof()) { - if (first_value) - property.appendff("{:#08x}", TRY(value_stream.read_value>())); - else - property.appendff(" {:#08x}", TRY(value_stream.read_value>())); - first_value = false; - } - outln("{}>;", property.string_view()); - } - } - return IterationDecision::Continue; - }, - .on_noop = []() -> ErrorOr { return IterationDecision::Continue; }, - .on_end = []() -> ErrorOr { return {}; } - }; - - return walk_device_tree(header, raw_device_tree, move(callbacks)); -} -} // namespace DeviceTree diff --git a/Userland/Libraries/LibDeviceTree/Validation.h b/Userland/Libraries/LibDeviceTree/Validation.h deleted file mode 100644 index 63cceba81fc..00000000000 --- a/Userland/Libraries/LibDeviceTree/Validation.h +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright (c) 2021-2023, Andrew Kaster - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include - -namespace DeviceTree { - -enum class Verbose { - No, - Yes -}; - -bool validate_flattened_device_tree(FlattenedDeviceTreeHeader const& header, ReadonlyBytes raw_device_tree, Verbose = Verbose::No); - -ErrorOr dump(FlattenedDeviceTreeHeader const& header, ReadonlyBytes raw_device_tree); -ErrorOr dump_flattened_device_tree_structure(FlattenedDeviceTreeHeader const& header, ReadonlyBytes raw_device_tree); - -} diff --git a/Userland/Libraries/LibEDID/CMakeLists.txt b/Userland/Libraries/LibEDID/CMakeLists.txt deleted file mode 100644 index 34d461047a0..00000000000 --- a/Userland/Libraries/LibEDID/CMakeLists.txt +++ /dev/null @@ -1,11 +0,0 @@ -include(${SerenityOS_SOURCE_DIR}/Meta/CMake/pnp_ids.cmake) - -set(SOURCES - DMT.cpp - EDID.cpp - VIC.cpp - ${PNP_IDS_SOURCES} -) - -serenity_lib(LibEDID edid) -target_compile_definitions(LibEDID PRIVATE ENABLE_PNP_IDS_DATA=$) diff --git a/Userland/Libraries/LibEDID/DMT.cpp b/Userland/Libraries/LibEDID/DMT.cpp deleted file mode 100644 index 6ce87d36ead..00000000000 --- a/Userland/Libraries/LibEDID/DMT.cpp +++ /dev/null @@ -1,168 +0,0 @@ -/* - * Copyright (c) 2022, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include - -#ifndef KERNEL -# include -#endif - -namespace EDID { - -// Monitor timings as per Display Monitor Timing Standard (DMT) 1.0 rev 13 -static constexpr DMT::MonitorTiming s_monitor_timings[] = { - { 0x1, {}, 8, 640, 350, 37861, 85080, 31500, 32, 32, 192, 95, 64, 3 }, - { 0x2, { 0x31, 0x19 }, 8, 640, 400, 37861, 85080, 31500, 32, 1, 192, 45, 64, 3 }, - { 0x3, {}, 9, 720, 400, 37927, 85039, 35500, 36, 1, 216, 46, 72, 3 }, - { 0x4, { 0x31, 0x40 }, 8, 640, 480, 31469, 59940, 25175, 8, 2, 144, 29, 96, 2 }, - { 0x5, { 0x31, 0x4c }, 8, 640, 480, 37861, 72809, 31500, 16, 1, 176, 24, 40, 3 }, - { 0x6, { 0x31, 0x4f }, 8, 640, 480, 37500, 75000, 31500, 16, 1, 200, 20, 64, 3 }, - { 0x7, { 0x31, 0x59 }, 8, 640, 480, 43269, 85008, 36000, 56, 1, 192, 29, 56, 3 }, - { 0x8, {}, 8, 800, 600, 35156, 56250, 36000, 24, 1, 224, 25, 72, 2 }, - { 0x9, { 0x45, 0x40 }, 8, 800, 600, 37879, 60317, 40000, 40, 1, 256, 28, 128, 4 }, - { 0xa, { 0x45, 0x4c }, 8, 800, 600, 48077, 72188, 50000, 56, 37, 240, 66, 120, 6 }, - { 0xb, { 0x45, 0x4f }, 8, 800, 600, 46875, 75000, 49500, 16, 1, 256, 25, 80, 3 }, - { 0xc, { 0x45, 0x59 }, 8, 800, 600, 53674, 85061, 56250, 32, 1, 248, 31, 64, 3 }, - { 0xd, {}, 8, 800, 600, 76302, 119972, 73250, 48, 3, 160, 36, 32, 4, DMT::MonitorTiming::CVTCompliance::CompliantReducedBlanking }, - { 0xe, {}, 8, 848, 480, 31020, 60000, 33750, 16, 6, 240, 37, 112, 8 }, - { 0xf, {}, 8, 1024, 768, 35522, 86957, 44900, 8, 0, 240, 24, 176, 4, DMT::MonitorTiming::CVTCompliance::NotCompliant, {}, DMT::MonitorTiming::ScanType::Interlaced }, - { 0x10, { 0x61, 0x40 }, 8, 1024, 768, 48363, 60004, 65000, 24, 3, 320, 38, 136, 6 }, - { 0x11, { 0x61, 0x4a }, 8, 1024, 768, 56476, 70069, 75000, 24, 3, 304, 38, 136, 6 }, - { 0x12, { 0x61, 0x4f }, 8, 1024, 768, 60023, 75029, 78750, 16, 1, 288, 32, 96, 3 }, - { 0x13, { 0x61, 0x59 }, 8, 1024, 768, 68677, 84997, 94500, 48, 1, 352, 40, 96, 3 }, - { 0x14, {}, 8, 1024, 768, 97551, 119989, 115500, 48, 3, 160, 45, 32, 4, DMT::MonitorTiming::CVTCompliance::CompliantReducedBlanking }, - { 0x15, { 0x71, 0x4f }, 8, 1152, 864, 67500, 75000, 108000, 64, 1, 448, 36, 128, 3 }, - { 0x55, { 0x81, 0xc0 }, 1, 1280, 720, 45000, 60000, 74250, 110, 5, 370, 30, 40, 5 }, - { 0x16, {}, 8, 1280, 768, 47396, 59995, 68250, 48, 3, 160, 22, 32, 7, DMT::MonitorTiming::CVTCompliance::CompliantReducedBlanking, { 0x7f, 0x1c, 0x22 } }, - { 0x17, {}, 8, 1280, 768, 47776, 59870, 79500, 64, 3, 384, 30, 128, 7, DMT::MonitorTiming::CVTCompliance::Compliant, { 0x7f, 0x1c, 0x28 } }, - { 0x18, {}, 8, 1280, 768, 60289, 74893, 102250, 80, 3, 416, 37, 128, 7, DMT::MonitorTiming::CVTCompliance::Compliant, { 0x7f, 0x1c, 0x44 } }, - { 0x19, {}, 8, 1280, 768, 68633, 84837, 117500, 80, 3, 432, 41, 136, 7, DMT::MonitorTiming::CVTCompliance::Compliant, { 0x7f, 0x1c, 0x62 } }, - { 0x1a, {}, 8, 1280, 768, 97396, 119798, 140250, 48, 3, 160, 45, 32, 7, DMT::MonitorTiming::CVTCompliance::CompliantReducedBlanking }, - { 0x1b, {}, 8, 1280, 800, 49306, 59910, 71000, 48, 3, 160, 23, 32, 6, DMT::MonitorTiming::CVTCompliance::CompliantReducedBlanking, { 0x8f, 0x18, 0x21 } }, - { 0x1c, { 0x81, 0x0 }, 8, 1280, 800, 49702, 59810, 83500, 72, 3, 400, 31, 128, 6, DMT::MonitorTiming::CVTCompliance::Compliant, { 0x8f, 0x18, 0x28 } }, - { 0x1d, { 0x81, 0xf }, 8, 1280, 800, 62795, 74934, 106500, 80, 3, 416, 38, 128, 6, DMT::MonitorTiming::CVTCompliance::Compliant, { 0x8f, 0x18, 0x44 } }, - { 0x1e, { 0x81, 0x19 }, 8, 1280, 800, 71554, 84880, 122500, 80, 3, 432, 43, 136, 6, DMT::MonitorTiming::CVTCompliance::Compliant, { 0x8f, 0x18, 0x62 } }, - { 0x1f, {}, 8, 1280, 800, 101563, 119909, 146250, 48, 3, 160, 47, 32, 6, DMT::MonitorTiming::CVTCompliance::CompliantReducedBlanking }, - { 0x20, { 0x81, 0x40 }, 8, 1280, 960, 60000, 60000, 108000, 96, 1, 520, 40, 112, 3 }, - { 0x21, { 0x81, 0x59 }, 8, 1280, 960, 85938, 85002, 148500, 64, 1, 448, 3, 160, 3 }, - { 0x22, {}, 8, 1280, 960, 121875, 119838, 175500, 48, 3, 160, 57, 32, 4, DMT::MonitorTiming::CVTCompliance::CompliantReducedBlanking }, - { 0x23, { 0x81, 0x80 }, 8, 1280, 1024, 63981, 60020, 108000, 48, 1, 408, 42, 112, 3 }, - { 0x24, { 0x81, 0x8f }, 8, 1280, 1024, 79976, 75025, 135000, 16, 1, 408, 42, 144, 3 }, - { 0x25, { 0x81, 0x99 }, 8, 1280, 1024, 91146, 85024, 157500, 64, 1, 448, 48, 160, 3 }, - { 0x26, {}, 8, 1280, 1024, 130035, 119958, 187250, 48, 3, 160, 60, 32, 7, DMT::MonitorTiming::CVTCompliance::CompliantReducedBlanking }, - { 0x27, {}, 8, 1360, 768, 47712, 60015, 85500, 64, 3, 432, 27, 112, 6 }, - { 0x28, {}, 8, 1360, 768, 97533, 119967, 148250, 48, 5, 160, 45, 32, 5, DMT::MonitorTiming::CVTCompliance::CompliantReducedBlanking }, - { 0x51, {}, 1, 1366, 768, 57712, 59790, 85500, 70, 3, 426, 30, 143, 3 }, - { 0x56, {}, 1, 1366, 768, 48000, 60000, 72000, 14, 1, 134, 32, 56, 3 }, - { 0x29, {}, 8, 1400, 1050, 64744, 59948, 101000, 48, 3, 160, 30, 32, 4, DMT::MonitorTiming::CVTCompliance::CompliantReducedBlanking, { 0x0c, 0x20, 0x21 } }, - { 0x2a, { 0x90, 0x40 }, 8, 1400, 1050, 65317, 59978, 121750, 88, 3, 464, 39, 144, 4, DMT::MonitorTiming::CVTCompliance::Compliant, { 0x0c, 0x20, 0x28 } }, - { 0x2b, { 0x90, 0x4f }, 8, 1400, 1050, 82278, 74867, 156000, 104, 3, 496, 49, 144, 4, DMT::MonitorTiming::CVTCompliance::Compliant, { 0x0c, 0x20, 0x44 } }, - { 0x2c, { 0x90, 0x59 }, 8, 1400, 1050, 93881, 84960, 179500, 104, 3, 512, 55, 152, 4, DMT::MonitorTiming::CVTCompliance::Compliant, { 0x0c, 0x20, 0x62 } }, - { 0x2d, {}, 8, 1400, 1050, 133333, 119904, 208000, 48, 3, 160, 62, 32, 4, DMT::MonitorTiming::CVTCompliance::CompliantReducedBlanking }, - { 0x2e, {}, 8, 1440, 900, 55469, 59901, 88750, 48, 3, 160, 26, 32, 6, DMT::MonitorTiming::CVTCompliance::CompliantReducedBlanking, { 0xc1, 0x18, 0x21 } }, - { 0x2f, { 0x95, 0x0 }, 8, 1440, 900, 55935, 59887, 106500, 80, 3, 464, 34, 152, 6, DMT::MonitorTiming::CVTCompliance::Compliant, { 0xc1, 0x18, 0x28 } }, - { 0x30, { 0x95, 0xf }, 8, 1440, 900, 70635, 74984, 136750, 96, 3, 496, 42, 152, 6, DMT::MonitorTiming::CVTCompliance::Compliant, { 0xc1, 0x18, 0x44 } }, - { 0x31, { 0x95, 0x19 }, 8, 1440, 900, 80430, 84842, 157000, 104, 3, 512, 48, 152, 6, DMT::MonitorTiming::CVTCompliance::Compliant, { 0xc1, 0x18, 0x68 } }, - { 0x32, {}, 8, 1440, 900, 114219, 119852, 182750, 48, 3, 160, 53, 32, 6, DMT::MonitorTiming::CVTCompliance::CompliantReducedBlanking }, - { 0x53, { 0xa9, 0xc0 }, 8, 1600, 900, 60000, 60000, 108000, 24, 1, 200, 100, 80, 3 }, - { 0x33, { 0xa9, 0x40 }, 8, 1600, 1200, 75000, 60000, 162000, 64, 1, 560, 50, 192, 3 }, - { 0x34, { 0xa9, 0x45 }, 8, 1600, 1200, 81250, 65000, 175500, 64, 1, 560, 50, 192, 3 }, - { 0x35, { 0xa9, 0x4a }, 8, 1600, 1200, 87500, 70000, 189000, 64, 1, 560, 50, 192, 3 }, - { 0x36, { 0xa9, 0x4f }, 8, 1600, 1200, 93750, 75000, 202500, 64, 1, 560, 50, 192, 3 }, - { 0x37, { 0xa9, 0x59 }, 8, 1600, 1200, 106250, 85000, 229500, 64, 1, 560, 50, 192, 3 }, - { 0x38, {}, 8, 1600, 1200, 152415, 119917, 268250, 48, 3, 160, 71, 32, 4, DMT::MonitorTiming::CVTCompliance::CompliantReducedBlanking }, - { 0x39, {}, 8, 1680, 1050, 64674, 59883, 119000, 48, 3, 160, 30, 32, 6, DMT::MonitorTiming::CVTCompliance::CompliantReducedBlanking, { 0x0c, 0x28, 0x21 } }, - { 0x3a, { 0xb3, 0x0 }, 8, 1680, 1050, 65290, 59954, 146250, 104, 3, 560, 39, 176, 6, DMT::MonitorTiming::CVTCompliance::Compliant, { 0x0c, 0x28, 0x28 } }, - { 0x3b, { 0xb3, 0xf }, 8, 1680, 1050, 82306, 74892, 187000, 120, 3, 592, 49, 176, 6, DMT::MonitorTiming::CVTCompliance::Compliant, { 0x0c, 0x28, 0x44 } }, - { 0x3c, { 0xb3, 0x19 }, 8, 1680, 1050, 93859, 84941, 214750, 128, 3, 608, 55, 176, 6, DMT::MonitorTiming::CVTCompliance::Compliant, { 0x0c, 0x28, 0x68 } }, - { 0x3d, {}, 8, 1680, 1050, 133424, 119986, 245500, 48, 3, 160, 62, 32, 6, DMT::MonitorTiming::CVTCompliance::CompliantReducedBlanking }, - { 0x3e, { 0xc1, 0x40 }, 8, 1792, 1344, 83640, 60000, 204750, 128, 1, 656, 50, 200, 3 }, - { 0x3f, { 0xc1, 0x4f }, 8, 1792, 1344, 106270, 74997, 261000, 96, 1, 664, 73, 216, 3 }, - { 0x40, {}, 8, 1792, 1344, 170722, 119974, 333250, 48, 3, 160, 79, 32, 4, DMT::MonitorTiming::CVTCompliance::CompliantReducedBlanking }, - { 0x41, { 0xc9, 0x40 }, 8, 1856, 1392, 86333, 59995, 218250, 96, 1, 672, 47, 224, 3 }, - { 0x42, { 0xc9, 0x4f }, 8, 1856, 1392, 112500, 75000, 288000, 128, 1, 704, 108, 224, 3 }, - { 0x43, {}, 8, 1856, 1392, 176835, 119970, 356500, 48, 3, 160, 82, 32, 4, DMT::MonitorTiming::CVTCompliance::CompliantReducedBlanking }, - { 0x52, { 0xd1, 0xc0 }, 4, 1920, 1080, 67500, 60000, 148500, 88, 4, 280, 45, 44, 5 }, - { 0x44, {}, 8, 1920, 1200, 74038, 59950, 154000, 48, 3, 160, 35, 32, 6, DMT::MonitorTiming::CVTCompliance::CompliantReducedBlanking, { 0x57, 0x28, 0x21 } }, - { 0x45, { 0xd1, 0x0 }, 8, 1920, 1200, 74556, 59885, 193250, 136, 3, 672, 45, 200, 6, DMT::MonitorTiming::CVTCompliance::Compliant, { 0x57, 0x28, 0x28 } }, - { 0x46, { 0xd1, 0xf }, 8, 1920, 1200, 94038, 74930, 245250, 136, 3, 688, 55, 208, 6, DMT::MonitorTiming::CVTCompliance::Compliant, { 0x57, 0x28, 0x44 } }, - { 0x47, { 0xd1, 0x19 }, 8, 1920, 1200, 107184, 84932, 281250, 144, 3, 704, 62, 208, 6, DMT::MonitorTiming::CVTCompliance::Compliant, { 0x57, 0x28, 0x62 } }, - { 0x48, {}, 8, 1920, 1200, 152404, 119909, 317000, 48, 3, 160, 71, 32, 6, DMT::MonitorTiming::CVTCompliance::CompliantReducedBlanking }, - { 0x49, { 0xd1, 0x40 }, 8, 1920, 1440, 90000, 60000, 234000, 128, 1, 680, 60, 208, 3 }, - { 0x4a, { 0xd1, 0x4f }, 8, 1920, 1440, 112500, 75000, 297000, 144, 1, 720, 60, 224, 3 }, - { 0x4b, {}, 8, 1920, 1440, 182933, 119956, 380500, 48, 3, 160, 85, 32, 4, DMT::MonitorTiming::CVTCompliance::CompliantReducedBlanking }, - { 0x54, { 0xe1, 0xc0 }, 1, 2048, 1152, 72000, 60000, 162000, 26, 1, 202, 48, 80, 3 }, - { 0x4c, {}, 8, 2560, 1600, 98713, 59972, 268500, 48, 3, 160, 46, 32, 6, DMT::MonitorTiming::CVTCompliance::Compliant, { 0x1f, 0x38, 0x21 } }, - { 0x4d, {}, 8, 2650, 1600, 99458, 59987, 348500, 192, 3, 944, 58, 280, 6, DMT::MonitorTiming::CVTCompliance::Compliant, { 0x1f, 0x38, 0x28 } }, - { 0x4e, {}, 8, 2560, 1600, 125354, 74972, 443250, 208, 3, 976, 72, 280, 6, DMT::MonitorTiming::CVTCompliance::Compliant, { 0x1f, 0x38, 0x44 } }, - { 0x4f, {}, 8, 2560, 1600, 142887, 84951, 505250, 208, 3, 976, 82, 280, 6, DMT::MonitorTiming::CVTCompliance::Compliant, { 0x1f, 0x38, 0x62 } }, - { 0x50, {}, 8, 2560, 1600, 203217, 119963, 552750, 48, 3, 160, 94, 32, 6, DMT::MonitorTiming::CVTCompliance::CompliantReducedBlanking }, - { 0x57, {}, 1, 4096, 2160, 133320, 60000, 556744, 8, 48, 80, 62, 32, 8, DMT::MonitorTiming::CVTCompliance::CompliantReducedBlankingV2 }, - { 0x58, {}, 1, 4096, 2160, 133187, 59940, 556188, 8, 48, 80, 62, 32, 8, DMT::MonitorTiming::CVTCompliance::CompliantReducedBlankingV2 }, -}; - -FixedPoint<16, u32> DMT::MonitorTiming::horizontal_frequency_khz() const -{ - return FixedPoint<16, u32>(horizontal_frequency_hz) / 1000; -} - -FixedPoint<16, u32> DMT::MonitorTiming::vertical_frequency_hz() const -{ - return FixedPoint<16, u32>(vertical_frequency_millihz) / 1000; -} - -u32 DMT::MonitorTiming::refresh_rate_hz() const -{ - return vertical_frequency_hz().ltrunc(); -} - -#ifndef KERNEL -ByteString DMT::MonitorTiming::name() const -{ - if (scan_type == ScanType::Interlaced) - return ByteString::formatted("{} x {} @ {}Hz (Interlaced)", horizontal_pixels, vertical_lines, refresh_rate_hz()); - return ByteString::formatted("{} x {} @ {}Hz", horizontal_pixels, vertical_lines, refresh_rate_hz()); -} -#endif - -auto DMT::find_timing_by_dmt_id(u8 dmt_id) -> MonitorTiming const* -{ - if (dmt_id == 0) - return nullptr; - - for (auto& monitor_timing : s_monitor_timings) { - if (monitor_timing.dmt_id == dmt_id) - return &monitor_timing; - } - - return nullptr; -} - -auto DMT::find_timing_by_std_id(u8 std_id_byte1, u8 std_id_byte2) -> MonitorTiming const* -{ - for (auto& monitor_timing : s_monitor_timings) { - if (!monitor_timing.has_std()) - continue; - if (monitor_timing.std_bytes[0] == std_id_byte1 && monitor_timing.std_bytes[1] == std_id_byte2) - return &monitor_timing; - } - - return nullptr; -} - -auto DMT::find_timing_by_cvt(CVT cvt) -> MonitorTiming const* -{ - for (auto& monitor_timing : s_monitor_timings) { - if (!monitor_timing.has_cvt()) - continue; - if (monitor_timing.cvt_bytes[0] == cvt.bytes[0] && monitor_timing.cvt_bytes[1] == cvt.bytes[1] && monitor_timing.cvt_bytes[2] == cvt.bytes[2]) - return &monitor_timing; - } - - return nullptr; -} - -} diff --git a/Userland/Libraries/LibEDID/DMT.h b/Userland/Libraries/LibEDID/DMT.h deleted file mode 100644 index 10d50ce64f0..00000000000 --- a/Userland/Libraries/LibEDID/DMT.h +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright (c) 2022, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include - -namespace EDID { - -class DMT final { -public: - struct CVT { - u8 bytes[3]; - }; - struct MonitorTiming { - enum class ScanType : u8 { - NonInterlaced, - Interlaced - }; - enum class CVTCompliance : u8 { - NotCompliant, - Compliant, - CompliantReducedBlanking, - CompliantReducedBlankingV2, - }; - - u8 dmt_id; - u8 std_bytes[2]; - u8 char_width_pixels; - u16 horizontal_pixels; - u16 vertical_lines; - u32 horizontal_frequency_hz; - u32 vertical_frequency_millihz; - u32 pixel_clock_khz; - u8 horizontal_front_porch_pixels; - u8 vertical_front_porch_lines; - u16 horizontal_blank_pixels; - u16 vertical_blank_lines; - u16 horizontal_sync_time_pixels; - u8 vertical_sync_time_lines; - CVTCompliance cvt_compliance { CVTCompliance::NotCompliant }; - u8 cvt_bytes[3] {}; - ScanType scan_type { ScanType::NonInterlaced }; - - ALWAYS_INLINE bool has_std() const { return std_bytes[0] != 0; } - ALWAYS_INLINE bool has_cvt() const { return cvt_bytes[0] != 0; } - - FixedPoint<16, u32> horizontal_frequency_khz() const; - FixedPoint<16, u32> vertical_frequency_hz() const; - u32 refresh_rate_hz() const; -#ifndef KERNEL - ByteString name() const; -#endif - }; - - static MonitorTiming const* find_timing_by_dmt_id(u8); - static MonitorTiming const* find_timing_by_std_id(u8, u8); - static MonitorTiming const* find_timing_by_cvt(CVT); -}; - -} diff --git a/Userland/Libraries/LibEDID/Definitions.h b/Userland/Libraries/LibEDID/Definitions.h deleted file mode 100644 index 97db7769836..00000000000 --- a/Userland/Libraries/LibEDID/Definitions.h +++ /dev/null @@ -1,158 +0,0 @@ -/* - * Copyright (c) 2022, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include - -namespace EDID::Definitions { - -struct [[gnu::packed]] StandardTimings { - u8 horizontal_8_pixels; - u8 ratio_and_refresh_rate; -}; - -struct [[gnu::packed]] DetailedTiming { - u16 pixel_clock; - u8 horizontal_addressable_pixels_low; - u8 horizontal_blanking_pixels_low; - u8 horizontal_addressable_and_blanking_pixels_high; - u8 vertical_addressable_lines_low; - u8 vertical_blanking_lines_low; - u8 vertical_addressable_and_blanking_lines_high; - u8 horizontal_front_porch_pixels_low; - u8 horizontal_sync_pulse_width_pixels_low; - u8 vertical_front_porch_and_sync_pulse_width_lines_low; - u8 horizontal_and_vertical_front_porch_sync_pulse_width_high; - u8 horizontal_addressable_image_size_mm_low; - u8 vertical_addressable_image_size_mm_low; - u8 horizontal_vertical_addressable_image_size_mm_high; - u8 right_or_left_horizontal_border_pixels; - u8 top_or_bottom_vertical_border_lines; - u8 features; -}; - -enum class DisplayDescriptorTag : u8 { - ManufacturerSpecified_First = 0x0, - ManufacturerSpecified_Last = 0xf, - Dummy = 0x10, - EstablishedTimings3 = 0xf7, - CVTTimingCodes = 0xf8, - DisplayColorManagementData = 0xf9, - StandardTimingIdentifications = 0xfa, - ColorPointData = 0xfb, - DisplayProductName = 0xfc, - DisplayRangeLimits = 0xfd, - AlphanumericDataString = 0xfe, - DisplayProductSerialNumber = 0xff -}; - -struct [[gnu::packed]] DisplayDescriptor { - u16 zero; - u8 reserved1; - u8 tag; - u8 reserved2; - union { - struct [[gnu::packed]] { - u8 ascii_name[13]; - } display_product_name; - struct [[gnu::packed]] { - u8 ascii_str[13]; - } display_product_serial_number; - struct [[gnu::packed]] { - u8 revision; - u8 dmt_bits[6]; - u8 reserved[6]; - } established_timings3; - struct [[gnu::packed]] { - u8 version; - u8 cvt[4][3]; - } coordinated_video_timings; - }; -}; - -static_assert(sizeof(DetailedTiming) == sizeof(DisplayDescriptor)); - -struct [[gnu::packed]] EDID { - u64 header; - struct [[gnu::packed]] { - u16 manufacturer_id; - u16 product_code; - u32 serial_number; - u8 week_of_manufacture; - u8 year_of_manufacture; - } vendor; - struct [[gnu::packed]] { - u8 version; - u8 revision; - } version; - struct [[gnu::packed]] { - u8 video_input_definition; - u8 horizontal_size_or_aspect_ratio; - u8 vertical_size_or_aspect_ratio; - u8 display_transfer_characteristics; - u8 feature_support; - } basic_display_parameters; - struct [[gnu::packed]] { - u8 red_green_low_order_bits; - u8 blue_white_low_order_bits; - u8 red_x_high_order_bits; - u8 red_y_high_order_bits; - u8 green_x_high_order_bits; - u8 green_y_high_order_bits; - u8 blue_x_high_order_bits; - u8 blue_y_high_order_bits; - u8 white_x_high_order_bits; - u8 white_y_high_order_bits; - } color_characteristics; - struct [[gnu::packed]] { - u8 timings_1; - u8 timings_2; - u8 manufacturer_reserved; - } established_timings; - StandardTimings standard_timings[8]; - union { - DetailedTiming detailed_timing; - DisplayDescriptor display_descriptor; - } detailed_timing_or_display_descriptors[4]; - u8 extension_block_count; - u8 checksum; -}; - -enum class ExtensionBlockTag : u8 { - CEA_861 = 0x2, - VideoTimingBlock = 0x10, - DisplayInformation = 0x40, - LocalizedString = 0x50, - DigitalPacketVideoLink = 0x60, - ExtensionBlockMap = 0xf0, - ManufacturerDefined = 0xff -}; - -struct [[gnu::packed]] ExtensionBlock { - u8 tag; - union { - struct [[gnu::packed]] { - u8 block_tags[126]; - } map; - struct [[gnu::packed]] { - u8 revision; - u8 bytes[125]; - } block; - struct [[gnu::packed]] { - u8 revision; - u8 dtd_start_offset; - u8 flags; - union { - u8 bytes[123]; - }; - } cea861extension; - }; - u8 checksum; -}; -static_assert(AssertSize()); - -} diff --git a/Userland/Libraries/LibEDID/EDID.cpp b/Userland/Libraries/LibEDID/EDID.cpp deleted file mode 100644 index 0a2d3f1eaf9..00000000000 --- a/Userland/Libraries/LibEDID/EDID.cpp +++ /dev/null @@ -1,1122 +0,0 @@ -/* - * Copyright (c) 2022, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include - -#ifdef KERNEL -# include -#else -# include -# include -# include -# include - -# if ENABLE_PNP_IDS_DATA -# include -# endif -#endif - -namespace EDID { - -// clang doesn't like passing around pointers to members in packed structures, -// even though we're only using them for arithmetic purposes -#if defined(AK_COMPILER_CLANG) -# pragma clang diagnostic ignored "-Waddress-of-packed-member" -#endif - -static_assert(sizeof(Definitions::EDID) == Parser::BufferSize); - -class CEA861ExtensionBlock final { - friend class Parser; - -public: - enum class DataBlockTag : u8 { - Reserved = 0, - Audio, - Video, - VendorSpecific, - SpeakerAllocation, - VesaDTC, - Reserved2, - Extended - }; - - ErrorOr for_each_short_video_descriptor(Function callback) const - { - return for_each_data_block([&](DataBlockTag tag, ReadonlyBytes bytes) -> ErrorOr { - if (tag != DataBlockTag::Video) - return IterationDecision::Continue; - - // Short video descriptors are one byte values - for (size_t i = 0; i < bytes.size(); i++) { - u8 byte = m_edid.read_host(&bytes[i]); - bool is_native = (byte & 0x80) != 0; - u8 vic_id = byte & 0x7f; - - auto* vic_details = VIC::find_details_by_vic_id(vic_id); - if (!vic_details) - return Error::from_string_view_or_print_error_and_return_errno("CEA 861 extension block has invalid short video descriptor"sv, EINVAL); - - IterationDecision decision = callback(is_native, *vic_details); - if (decision != IterationDecision::Continue) - return decision; - } - return IterationDecision::Continue; - }); - } - - ErrorOr for_each_dtd(Function callback) const - { - u8 dtd_start = m_edid.read_host(&m_block->cea861extension.dtd_start_offset); - if (dtd_start < 4) { - // dtd_start == 4 means there are no data blocks, but there are still DTDs - return IterationDecision::Continue; - } - - if (dtd_start > offsetof(Definitions::ExtensionBlock, checksum) - sizeof(Definitions::DetailedTiming)) - return Error::from_string_view_or_print_error_and_return_errno("CEA 861 extension block has invalid DTD list"sv, EINVAL); - - for (size_t offset = dtd_start; offset <= offsetof(Definitions::ExtensionBlock, checksum) - sizeof(Definitions::DetailedTiming); offset += sizeof(Definitions::DetailedTiming)) { - auto& dtd = *(Definitions::DetailedTiming const*)((u8 const*)m_block + offset); - if (m_edid.read_host(&dtd.pixel_clock) == 0) - break; - - IterationDecision decision = callback(Parser::DetailedTiming(m_edid, &dtd)); - if (decision != IterationDecision::Continue) - return decision; - } - return IterationDecision::Continue; - } - -private: - CEA861ExtensionBlock(Parser const& edid, Definitions::ExtensionBlock const* block) - : m_edid(edid) - , m_block(block) - { - } - - ErrorOr for_each_data_block(Function(DataBlockTag, ReadonlyBytes)> callback) const - { - u8 dtd_start = m_edid.read_host(&m_block->cea861extension.dtd_start_offset); - if (dtd_start <= 4) - return IterationDecision::Continue; - - if (dtd_start > offsetof(Definitions::ExtensionBlock, checksum)) - return Error::from_string_view_or_print_error_and_return_errno("CEA 861 extension block has invalid DTD start offset"sv, EINVAL); - - auto* data_block_header = &m_block->cea861extension.bytes[0]; - auto* data_block_end = (u8 const*)m_block + dtd_start; - while (data_block_header < data_block_end) { - auto header_byte = m_edid.read_host(data_block_header); - size_t payload_size = header_byte & 0x1f; - auto tag = (DataBlockTag)((header_byte >> 5) & 0x7); - if (tag == DataBlockTag::Extended && payload_size == 0) - return Error::from_string_view_or_print_error_and_return_errno("CEA 861 extension block has invalid extended data block size"sv, EINVAL); - - auto decision = TRY(callback(tag, m_edid.m_bytes.slice(data_block_header - m_edid.m_bytes.data() + 1, payload_size))); - if (decision != IterationDecision::Continue) - return decision; - - data_block_header += 1 + payload_size; - } - return IterationDecision::Continue; - } - - ErrorOr for_each_display_descriptor(Function callback) const - { - u8 dtd_start = m_edid.read_host(&m_block->cea861extension.dtd_start_offset); - if (dtd_start <= 4) - return IterationDecision::Continue; - - if (dtd_start > offsetof(Definitions::ExtensionBlock, checksum) - sizeof(Definitions::DetailedTiming)) - return Error::from_string_view_or_print_error_and_return_errno("CEA 861 extension block has invalid DTD list"sv, EINVAL); - - for (size_t offset = dtd_start; offset <= offsetof(Definitions::ExtensionBlock, checksum) - sizeof(Definitions::DisplayDescriptor); offset += sizeof(Definitions::DisplayDescriptor)) { - auto& dd = *(Definitions::DisplayDescriptor const*)((u8 const*)m_block + offset); - if (m_edid.read_host(&dd.zero) != 0 || m_edid.read_host(&dd.reserved1) != 0) - continue; - - u8 tag = m_edid.read_host(&dd.tag); - IterationDecision decision = callback(tag, dd); - if (decision != IterationDecision::Continue) - return decision; - } - return IterationDecision::Continue; - } - - Parser const& m_edid; - Definitions::ExtensionBlock const* m_block; -}; - -template -T Parser::read_host(T const* field) const -{ - VERIFY((u8 const*)field >= m_bytes.data() && (u8 const*)field + sizeof(T) <= m_bytes.data() + m_bytes.size()); - size_t offset = (u8 const*)field - m_bytes.data(); - T value; - if constexpr (sizeof(T) > 1) - ByteReader::load(m_bytes.offset(offset), value); - else - value = m_bytes.at(offset); - - return value; -} - -template -requires(sizeof(T) > 1) -T Parser::read_le(T const* field) const -{ - static_assert(sizeof(T) > 1); - return AK::convert_between_host_and_little_endian(read_host(field)); -} - -template -requires(sizeof(T) > 1) -T Parser::read_be(T const* field) const -{ - static_assert(sizeof(T) > 1); - return AK::convert_between_host_and_big_endian(read_host(field)); -} - -ErrorOr Parser::from_bytes(ReadonlyBytes bytes) -{ - Parser edid(bytes); - TRY(edid.parse()); - return edid; -} - -ErrorOr Parser::from_bytes(ByteBuffer&& bytes) -{ - Parser edid(move(bytes)); - TRY(edid.parse()); - return edid; -} - -#ifndef KERNEL -ErrorOr Parser::from_display_connector_device(int display_connector_fd) -{ - RawBytes edid_bytes; - GraphicsHeadEDID edid_info {}; - edid_info.bytes = &edid_bytes[0]; - edid_info.bytes_size = sizeof(edid_bytes); - if (graphics_connector_get_head_edid(display_connector_fd, &edid_info) < 0) { - int err = errno; - if (err == EOVERFLOW) { - // We need a bigger buffer with at least bytes_size bytes - auto edid_byte_buffer = TRY(ByteBuffer::create_zeroed(edid_info.bytes_size)); - edid_info.bytes = edid_byte_buffer.data(); - if (graphics_connector_get_head_edid(display_connector_fd, &edid_info) < 0) { - err = errno; - return Error::from_errno(err); - } - - return from_bytes(move(edid_byte_buffer)); - } - - return Error::from_errno(err); - } - - auto edid_byte_buffer = TRY(ByteBuffer::copy((void const*)edid_bytes, sizeof(edid_bytes))); - return from_bytes(move(edid_byte_buffer)); -} - -ErrorOr Parser::from_display_connector_device(ByteString const& display_connector_device) -{ - int display_connector_fd = open(display_connector_device.characters(), O_RDWR | O_CLOEXEC); - if (display_connector_fd < 0) { - int err = errno; - return Error::from_errno(err); - } - ScopeGuard fd_guard([&] { - close(display_connector_fd); - }); - return from_display_connector_device(display_connector_fd); -} -#endif - -Parser::Parser(ReadonlyBytes bytes) - : m_bytes(move(bytes)) -{ -} - -Parser::Parser(ByteBuffer&& bytes) - : m_bytes_buffer(move(bytes)) - , m_bytes(m_bytes_buffer) -{ -} - -Parser::Parser(Parser const& other) - : m_bytes_buffer(other.m_bytes_buffer) - , m_revision(other.m_revision) -{ - if (m_bytes_buffer.is_empty()) - m_bytes = other.m_bytes_buffer; // We don't own the buffer - else - m_bytes = m_bytes_buffer; // We own the buffer -} - -Parser& Parser::operator=(Parser&& from) -{ - m_bytes_buffer = move(from.m_bytes_buffer); - m_bytes = move(from.m_bytes); - m_revision = from.m_revision; - return *this; -} - -Parser& Parser::operator=(Parser const& other) -{ - if (this == &other) - return *this; - - m_bytes_buffer = other.m_bytes_buffer; - if (m_bytes_buffer.is_empty()) - m_bytes = other.m_bytes_buffer; // We don't own the buffer - else - m_bytes = m_bytes_buffer; // We own the buffer - m_revision = other.m_revision; - return *this; -} - -bool Parser::operator==(Parser const& other) const -{ - if (this == &other) - return true; - return m_bytes == other.m_bytes; -} - -Definitions::EDID const& Parser::raw_edid() const -{ - return *(Definitions::EDID const*)m_bytes.data(); -} - -ErrorOr Parser::parse() -{ - if (m_bytes.size() < sizeof(Definitions::EDID)) - return Error::from_string_view_or_print_error_and_return_errno("Incomplete Parser structure"sv, EINVAL); - - auto const& edid = raw_edid(); - u64 header = read_le(&edid.header); - if (header != 0x00ffffffffffff00ull) - return Error::from_string_view_or_print_error_and_return_errno("No Parser header"sv, EINVAL); - - u8 major_version = read_host(&edid.version.version); - m_revision = read_host(&edid.version.revision); - if (major_version != 1 || m_revision > 4) - return Error::from_string_view_or_print_error_and_return_errno("Unsupported Parser version"sv, EINVAL); - -#ifdef KERNEL - m_version = TRY(Kernel::KString::formatted("1.{}", (int)m_revision)); -#else - m_version = ByteString::formatted("1.{}", (int)m_revision); -#endif - - u8 checksum = 0x0; - for (size_t i = 0; i < sizeof(Definitions::EDID); i++) - checksum += m_bytes[i]; - - if (checksum != 0) { - if (m_revision >= 4) - return Error::from_string_view_or_print_error_and_return_errno("Parser checksum mismatch"sv, EINVAL); - else - dbgln("EDID checksum mismatch, data may be corrupted!"); - } - - u16 packed_id = read_be(&raw_edid().vendor.manufacturer_id); - if (packed_id == 0x0) - return {}; - m_legacy_manufacturer_id[0] = (char)((u16)'A' + ((packed_id >> 10) & 0x1f) - 1); - m_legacy_manufacturer_id[1] = (char)((u16)'A' + ((packed_id >> 5) & 0x1f) - 1); - m_legacy_manufacturer_id[2] = (char)((u16)'A' + (packed_id & 0x1f) - 1); - m_legacy_manufacturer_id[3] = '\0'; - m_legacy_manufacturer_id_valid = true; - - return {}; -} - -ErrorOr Parser::for_each_extension_block(Function callback) const -{ - auto& edid = raw_edid(); - u8 raw_extension_block_count = read_host(&edid.extension_block_count); - if (raw_extension_block_count == 0) - return IterationDecision::Continue; - if (sizeof(Definitions::EDID) + (size_t)raw_extension_block_count * sizeof(Definitions::ExtensionBlock) > m_bytes.size()) - return Error::from_string_view_or_print_error_and_return_errno("Truncated EDID"sv, EINVAL); - - auto validate_block_checksum = [&](Definitions::ExtensionBlock const& block) { - u8 checksum = 0x0; - auto* bytes = (u8 const*)█ - for (size_t i = 0; i < sizeof(block); i++) - checksum += bytes[i]; - - return checksum == 0; - }; - - auto* raw_extension_blocks = (Definitions::ExtensionBlock const*)(m_bytes.data() + sizeof(Definitions::EDID)); - Definitions::ExtensionBlock const* current_extension_map = nullptr; - - unsigned raw_index = 0; - if (m_revision <= 3) { - if (raw_extension_block_count > 1) { - current_extension_map = &raw_extension_blocks[0]; - raw_index++; - if (read_host(¤t_extension_map->tag) != (u8)Definitions::ExtensionBlockTag::ExtensionBlockMap) - return Error::from_string_view_or_print_error_and_return_errno("Did not find extension map at block 1"sv, EINVAL); - - if (!validate_block_checksum(*current_extension_map)) - return Error::from_string_view_or_print_error_and_return_errno("Extension block map checksum mismatch"sv, EINVAL); - } - } else if (read_host(&raw_extension_blocks[0].tag) == (u8)Definitions::ExtensionBlockTag::ExtensionBlockMap) { - current_extension_map = &raw_extension_blocks[0]; - raw_index++; - } - - for (; raw_index < raw_extension_block_count; raw_index++) { - auto& raw_block = raw_extension_blocks[raw_index]; - u8 tag = read_host(&raw_block.tag); - - if (current_extension_map && raw_index == 127) { - if (tag != (u8)Definitions::ExtensionBlockTag::ExtensionBlockMap) - return Error::from_string_view_or_print_error_and_return_errno("Did not find extension map at block 128"sv, EINVAL); - - current_extension_map = &raw_extension_blocks[127]; - if (!validate_block_checksum(*current_extension_map)) - return Error::from_string_view_or_print_error_and_return_errno("Extension block map checksum mismatch"sv, EINVAL); - continue; - } - - if (tag == (u8)Definitions::ExtensionBlockTag::ExtensionBlockMap) - return Error::from_string_view_or_print_error_and_return_errno("Unexpected extension map encountered"sv, EINVAL); - - if (!validate_block_checksum(raw_block)) - return Error::from_string_view_or_print_error_and_return_errno("Extension block checksum mismatch"sv, EINVAL); - - size_t offset = (u8 const*)&raw_block - m_bytes.data(); - IterationDecision decision = callback(raw_index + 1, tag, raw_block.block.revision, m_bytes.slice(offset, sizeof(Definitions::ExtensionBlock))); - if (decision != IterationDecision::Continue) - return decision; - } - return IterationDecision::Continue; -} - -StringView Parser::version() const -{ -#ifdef KERNEL - return m_version->view(); -#else - return m_version; -#endif -} - -StringView Parser::legacy_manufacturer_id() const -{ - return { m_legacy_manufacturer_id, strlen(m_legacy_manufacturer_id) }; -} - -#ifndef KERNEL -ByteString Parser::manufacturer_name() const -{ - if (!m_legacy_manufacturer_id_valid) - return "Unknown"; - auto manufacturer_id = legacy_manufacturer_id(); -# if ENABLE_PNP_IDS_DATA - if (auto pnp_id_data = PnpIDs::find_by_manufacturer_id(manufacturer_id); pnp_id_data.has_value()) - return pnp_id_data.value().manufacturer_name; -# endif - return manufacturer_id; -} -#endif - -u16 Parser::product_code() const -{ - return read_le(&raw_edid().vendor.product_code); -} - -u32 Parser::serial_number() const -{ - return read_le(&raw_edid().vendor.serial_number); -} - -auto Parser::digital_display() const -> Optional -{ - auto& edid = raw_edid(); - u8 video_input_definition = read_host(&edid.basic_display_parameters.video_input_definition); - if (!(video_input_definition & 0x80)) - return {}; // This is an analog display - - u8 feature_support = read_host(&edid.basic_display_parameters.feature_support); - return DigitalDisplay(video_input_definition, feature_support, m_revision); -} - -auto Parser::analog_display() const -> Optional -{ - auto& edid = raw_edid(); - u8 video_input_definition = read_host(&edid.basic_display_parameters.video_input_definition); - if ((video_input_definition & 0x80) != 0) - return {}; // This is a digital display - - u8 feature_support = read_host(&edid.basic_display_parameters.feature_support); - return AnalogDisplay(video_input_definition, feature_support, m_revision); -} - -auto Parser::screen_size() const -> Optional -{ - auto& edid = raw_edid(); - u8 horizontal_size_or_aspect_ratio = read_host(&edid.basic_display_parameters.horizontal_size_or_aspect_ratio); - u8 vertical_size_or_aspect_ratio = read_host(&edid.basic_display_parameters.vertical_size_or_aspect_ratio); - - if (horizontal_size_or_aspect_ratio == 0 || vertical_size_or_aspect_ratio == 0) { - // EDID < 1.4: Unknown or undefined - // EDID >= 1.4: If both are 0 it is unknown or undefined - // If one of them is 0 then we're dealing with aspect ratios - return {}; - } - - return ScreenSize(horizontal_size_or_aspect_ratio, vertical_size_or_aspect_ratio); -} - -auto Parser::aspect_ratio() const -> Optional -{ - if (m_revision < 4) - return {}; - - auto& edid = raw_edid(); - u8 value_1 = read_host(&edid.basic_display_parameters.horizontal_size_or_aspect_ratio); - u8 value_2 = read_host(&edid.basic_display_parameters.vertical_size_or_aspect_ratio); - - if (value_1 == 0 && value_2 == 0) - return {}; // Unknown or undefined - if (value_1 != 0 && value_2 != 0) - return {}; // Dimensions are in cm - - if (value_1 == 0) - return ScreenAspectRatio(ScreenAspectRatio::Orientation::Portrait, FixedPoint<16>(100) / FixedPoint<16>((i32)value_2 + 99)); - - VERIFY(value_2 == 0); - return ScreenAspectRatio(ScreenAspectRatio::Orientation::Landscape, FixedPoint<16>((i32)value_1 + 99) / 100); -} - -Optional> Parser::gamma() const -{ - u8 display_transfer_characteristics = read_host(&raw_edid().basic_display_parameters.display_transfer_characteristics); - if (display_transfer_characteristics == 0xff) { - if (m_revision < 4) - return {}; - - // TODO: EDID >= 1.4 stores more gamma details in an extension block (e.g. DI-EXT) - return {}; - } - - FixedPoint<16> gamma { (i32)display_transfer_characteristics + 100 }; - gamma /= 100; - return gamma; -} - -u32 Parser::DetailedTiming::pixel_clock_khz() const -{ - // Note: The stored value is in units of 10 kHz, which means that to get the - // value in kHz, we need to multiply it by 10. - return (u32)m_edid.read_le(&m_detailed_timings.pixel_clock) * 10; -} - -u16 Parser::DetailedTiming::horizontal_addressable_pixels() const -{ - u8 low = m_edid.read_host(&m_detailed_timings.horizontal_addressable_pixels_low); - u8 high = m_edid.read_host(&m_detailed_timings.horizontal_addressable_and_blanking_pixels_high) >> 4; - return ((u16)high << 8) | (u16)low; -} - -u16 Parser::DetailedTiming::horizontal_blanking_pixels() const -{ - u8 low = m_edid.read_host(&m_detailed_timings.horizontal_blanking_pixels_low); - u8 high = m_edid.read_host(&m_detailed_timings.horizontal_addressable_and_blanking_pixels_high) & 0xf; - return ((u16)high << 8) | (u16)low; -} - -u16 Parser::DetailedTiming::vertical_addressable_lines_raw() const -{ - u8 low = m_edid.read_host(&m_detailed_timings.vertical_addressable_lines_low); - u8 high = m_edid.read_host(&m_detailed_timings.vertical_addressable_and_blanking_lines_high) >> 4; - return ((u16)high << 8) | (u16)low; -} - -u16 Parser::DetailedTiming::vertical_addressable_lines() const -{ - auto lines = vertical_addressable_lines_raw(); - return is_interlaced() ? lines * 2 : lines; -} - -u16 Parser::DetailedTiming::vertical_blanking_lines() const -{ - u8 low = m_edid.read_host(&m_detailed_timings.vertical_blanking_lines_low); - u8 high = m_edid.read_host(&m_detailed_timings.vertical_addressable_and_blanking_lines_high) & 0xf; - return ((u16)high << 8) | (u16)low; -} - -u16 Parser::DetailedTiming::horizontal_front_porch_pixels() const -{ - u8 low = m_edid.read_host(&m_detailed_timings.horizontal_front_porch_pixels_low); - u8 high = m_edid.read_host(&m_detailed_timings.horizontal_and_vertical_front_porch_sync_pulse_width_high) >> 6; - return ((u16)high << 8) | (u16)low; -} - -u16 Parser::DetailedTiming::horizontal_sync_pulse_width_pixels() const -{ - u8 low = m_edid.read_host(&m_detailed_timings.horizontal_sync_pulse_width_pixels_low); - u8 high = (m_edid.read_host(&m_detailed_timings.horizontal_and_vertical_front_porch_sync_pulse_width_high) >> 4) & 3; - return ((u16)high << 8) | (u16)low; -} - -u16 Parser::DetailedTiming::vertical_front_porch_lines() const -{ - u8 low = m_edid.read_host(&m_detailed_timings.vertical_front_porch_and_sync_pulse_width_lines_low) >> 4; - u8 high = (m_edid.read_host(&m_detailed_timings.horizontal_and_vertical_front_porch_sync_pulse_width_high) >> 2) & 3; - return ((u16)high << 4) | (u16)low; -} - -u16 Parser::DetailedTiming::vertical_sync_pulse_width_lines() const -{ - u8 low = m_edid.read_host(&m_detailed_timings.vertical_front_porch_and_sync_pulse_width_lines_low) & 0xf; - u8 high = m_edid.read_host(&m_detailed_timings.horizontal_and_vertical_front_porch_sync_pulse_width_high) & 3; - return ((u16)high << 4) | (u16)low; -} - -u16 Parser::DetailedTiming::horizontal_image_size_mm() const -{ - u8 low = m_edid.read_host(&m_detailed_timings.horizontal_addressable_image_size_mm_low); - u8 high = m_edid.read_host(&m_detailed_timings.horizontal_vertical_addressable_image_size_mm_high) >> 4; - return ((u16)high << 8) | (u16)low; -} - -u16 Parser::DetailedTiming::vertical_image_size_mm() const -{ - u8 low = m_edid.read_host(&m_detailed_timings.vertical_addressable_image_size_mm_low); - u8 high = m_edid.read_host(&m_detailed_timings.horizontal_vertical_addressable_image_size_mm_high) & 0xf; - return ((u16)high << 8) | (u16)low; -} - -u8 Parser::DetailedTiming::horizontal_right_or_left_border_pixels() const -{ - return m_edid.read_host(&m_detailed_timings.right_or_left_horizontal_border_pixels); -} - -u8 Parser::DetailedTiming::vertical_top_or_bottom_border_lines() const -{ - return m_edid.read_host(&m_detailed_timings.top_or_bottom_vertical_border_lines); -} - -bool Parser::DetailedTiming::is_interlaced() const -{ - return (m_edid.read_host(&m_detailed_timings.features) & (1 << 7)) != 0; -} - -FixedPoint<16, u32> Parser::DetailedTiming::refresh_rate() const -{ - // Blanking = front porch + sync pulse width + back porch - u32 total_horizontal_pixels = (u32)horizontal_addressable_pixels() + (u32)horizontal_blanking_pixels(); - u32 total_vertical_lines = (u32)vertical_addressable_lines_raw() + (u32)vertical_blanking_lines(); - u32 total_pixels = total_horizontal_pixels * total_vertical_lines; - if (total_pixels == 0) - return {}; - // Use a bigger fixed point representation due to the large numbers involved and then downcast - // Note: We need to convert the pixel clock from kHz to Hertz to actually calculate this correctly. - return FixedPoint<32, u64>(pixel_clock_khz() * 1000) / total_pixels; -} - -ErrorOr Parser::for_each_established_timing(Function callback) const -{ - static constexpr EstablishedTiming established_timing_byte1[8] = { - { EstablishedTiming::Source::VESA, 800, 600, 60, 0x9 }, - { EstablishedTiming::Source::VESA, 800, 600, 56, 0x8 }, - { EstablishedTiming::Source::VESA, 640, 480, 75, 0x6 }, - { EstablishedTiming::Source::VESA, 640, 480, 73, 0x5 }, - { EstablishedTiming::Source::Apple, 640, 480, 67 }, - { EstablishedTiming::Source::IBM, 640, 480, 60, 0x4 }, - { EstablishedTiming::Source::IBM, 720, 400, 88 }, - { EstablishedTiming::Source::IBM, 720, 400, 70 } - }; - static constexpr EstablishedTiming established_timing_byte2[8] = { - { EstablishedTiming::Source::VESA, 1280, 1024, 75, 0x24 }, - { EstablishedTiming::Source::VESA, 1024, 768, 75, 0x12 }, - { EstablishedTiming::Source::VESA, 1024, 768, 70, 0x11 }, - { EstablishedTiming::Source::VESA, 1024, 768, 60, 0x10 }, - { EstablishedTiming::Source::IBM, 1024, 768, 87, 0xf }, - { EstablishedTiming::Source::Apple, 832, 624, 75 }, - { EstablishedTiming::Source::VESA, 800, 600, 75, 0xb }, - { EstablishedTiming::Source::VESA, 800, 600, 72, 0xa } - }; - static constexpr EstablishedTiming established_timing_byte3[1] = { - { EstablishedTiming::Source::Apple, 1152, 870, 75 } - }; - - auto& established_timings = raw_edid().established_timings; - for (int i = 7; i >= 0; i--) { - if (!(established_timings.timings_1 & (1 << i))) - continue; - IterationDecision decision = callback(established_timing_byte1[i]); - if (decision != IterationDecision::Continue) - return decision; - } - for (int i = 7; i >= 0; i--) { - if (!(established_timings.timings_2 & (1 << i))) - continue; - IterationDecision decision = callback(established_timing_byte2[i]); - if (decision != IterationDecision::Continue) - return decision; - } - - if ((established_timings.manufacturer_reserved & (1 << 7)) != 0) { - IterationDecision decision = callback(established_timing_byte3[0]); - if (decision != IterationDecision::Continue) - return decision; - } - - u8 manufacturer_specific = established_timings.manufacturer_reserved & 0x7f; - if (manufacturer_specific != 0) { - IterationDecision decision = callback(EstablishedTiming(EstablishedTiming::Source::Manufacturer, 0, 0, manufacturer_specific)); - if (decision != IterationDecision::Continue) - return decision; - } - - auto callback_decision = IterationDecision::Continue; - TRY(for_each_display_descriptor([&](u8 descriptor_tag, auto& display_descriptor) { - if (descriptor_tag != (u8)Definitions::DisplayDescriptorTag::EstablishedTimings3) - return IterationDecision::Continue; - - static constexpr EstablishedTiming established_timings3_bytes[] = { - // Byte 1 - { EstablishedTiming::Source::VESA, 640, 350, 85, 0x1 }, - { EstablishedTiming::Source::VESA, 640, 400, 85, 0x2 }, - { EstablishedTiming::Source::VESA, 720, 400, 85, 0x3 }, - { EstablishedTiming::Source::VESA, 640, 480, 85, 0x7 }, - { EstablishedTiming::Source::VESA, 848, 480, 60, 0xe }, - { EstablishedTiming::Source::VESA, 800, 600, 85, 0xc }, - { EstablishedTiming::Source::VESA, 1024, 768, 85, 0x13 }, - { EstablishedTiming::Source::VESA, 1152, 864, 75, 0x15 }, - // Byte 2 - { EstablishedTiming::Source::VESA, 1280, 768, 60, 0x16 }, - { EstablishedTiming::Source::VESA, 1280, 768, 60, 0x17 }, - { EstablishedTiming::Source::VESA, 1280, 768, 75, 0x18 }, - { EstablishedTiming::Source::VESA, 1280, 768, 85, 0x19 }, - { EstablishedTiming::Source::VESA, 1280, 960, 60, 0x20 }, - { EstablishedTiming::Source::VESA, 1280, 960, 85, 0x21 }, - { EstablishedTiming::Source::VESA, 1280, 1024, 60, 0x23 }, - { EstablishedTiming::Source::VESA, 1280, 1024, 85, 0x25 }, - // Byte 3 - { EstablishedTiming::Source::VESA, 1360, 768, 60, 0x27 }, - { EstablishedTiming::Source::VESA, 1440, 900, 60, 0x2e }, - { EstablishedTiming::Source::VESA, 1440, 900, 60, 0x2f }, - { EstablishedTiming::Source::VESA, 1440, 900, 75, 0x30 }, - { EstablishedTiming::Source::VESA, 1440, 900, 85, 0x31 }, - { EstablishedTiming::Source::VESA, 1400, 1050, 60, 0x29 }, - { EstablishedTiming::Source::VESA, 1400, 1050, 60, 0x2a }, - { EstablishedTiming::Source::VESA, 1400, 1050, 75, 0x2b }, - // Byte 4 - { EstablishedTiming::Source::VESA, 1400, 1050, 85, 0x2c }, - { EstablishedTiming::Source::VESA, 1680, 1050, 60, 0x39 }, - { EstablishedTiming::Source::VESA, 1680, 1050, 60, 0x3a }, - { EstablishedTiming::Source::VESA, 1680, 1050, 75, 0x3b }, - { EstablishedTiming::Source::VESA, 1680, 1050, 85, 0x3c }, - { EstablishedTiming::Source::VESA, 1600, 1200, 60, 0x33 }, - { EstablishedTiming::Source::VESA, 1600, 1200, 65, 0x34 }, - { EstablishedTiming::Source::VESA, 1600, 1200, 70, 0x35 }, - // Byte 5 - { EstablishedTiming::Source::VESA, 1600, 1200, 75, 0x36 }, - { EstablishedTiming::Source::VESA, 1600, 1200, 85, 0x37 }, - { EstablishedTiming::Source::VESA, 1792, 1344, 60, 0x3e }, - { EstablishedTiming::Source::VESA, 1792, 1344, 75, 0x3f }, - { EstablishedTiming::Source::VESA, 1856, 1392, 60, 0x41 }, - { EstablishedTiming::Source::VESA, 1856, 1392, 75, 0x42 }, - { EstablishedTiming::Source::VESA, 1920, 1200, 60, 0x44 }, - { EstablishedTiming::Source::VESA, 1920, 1200, 60, 0x45 }, - // Byte 6 - { EstablishedTiming::Source::VESA, 1920, 1200, 75, 0x46 }, - { EstablishedTiming::Source::VESA, 1920, 1200, 85, 0x47 }, - { EstablishedTiming::Source::VESA, 1920, 1440, 60, 0x49 }, - { EstablishedTiming::Source::VESA, 1920, 1440, 75, 0x4a } - // Reserved - }; - - size_t byte_index = 0; - for (u8 dmt_bits : display_descriptor.established_timings3.dmt_bits) { - for (int i = 7; i >= 0; i--) { - if ((dmt_bits & (1 << i)) == 0) - continue; - - size_t table_index = byte_index * 8 + (size_t)(7 - i); - if (table_index >= (sizeof(established_timings3_bytes) + 7) / sizeof(established_timings3_bytes[0])) - break; // Sometimes reserved bits are set - - callback_decision = callback(established_timings3_bytes[table_index]); - if (callback_decision != IterationDecision::Continue) - return IterationDecision::Break; - } - byte_index++; - } - return IterationDecision::Break; // Only process one descriptor - })); - return callback_decision; -} - -ErrorOr Parser::for_each_standard_timing(Function callback) const -{ - for (size_t index = 0; index < 8; index++) { - auto& standard_timings = raw_edid().standard_timings[index]; - if (standard_timings.horizontal_8_pixels == 0x1 && standard_timings.ratio_and_refresh_rate == 0x1) - continue; // Skip unused records - u16 width = 8 * ((u16)read_host(&standard_timings.horizontal_8_pixels) + 31); - u8 aspect_ratio_and_refresh_rate = read_host(&standard_timings.ratio_and_refresh_rate); - u8 refresh_rate = (aspect_ratio_and_refresh_rate & 0x3f) + 60; - u16 height; - StandardTiming::AspectRatio aspect_ratio; - switch ((aspect_ratio_and_refresh_rate >> 6) & 3) { - case 0: - height = (width * 10) / 16; - aspect_ratio = StandardTiming::AspectRatio::AR_16_10; - break; - case 1: - height = (width * 3) / 4; - aspect_ratio = StandardTiming::AspectRatio::AR_4_3; - break; - case 2: - height = (width * 4) / 5; - aspect_ratio = StandardTiming::AspectRatio::AR_5_4; - break; - case 3: - height = (width * 9) / 16; - aspect_ratio = StandardTiming::AspectRatio::AR_16_9; - break; - default: - VERIFY_NOT_REACHED(); - } - - auto* dmt = DMT::find_timing_by_std_id(standard_timings.horizontal_8_pixels, standard_timings.ratio_and_refresh_rate); - IterationDecision decision = callback(StandardTiming(width, height, refresh_rate, aspect_ratio, dmt ? dmt->dmt_id : 0)); - if (decision != IterationDecision::Continue) - return decision; - } - - return IterationDecision::Continue; -} - -u16 Parser::CoordinatedVideoTiming::horizontal_addressable_pixels() const -{ - u32 aspect_h, aspect_v; - switch (aspect_ratio()) { - case AspectRatio::AR_4_3: - aspect_h = 4; - aspect_v = 3; - break; - case AspectRatio::AR_16_9: - aspect_h = 16; - aspect_v = 9; - break; - case AspectRatio::AR_16_10: - aspect_h = 16; - aspect_v = 10; - break; - case AspectRatio::AR_15_9: - aspect_h = 15; - aspect_v = 9; - break; - } - // Round down to nearest cell as per 3.10.3.8 - return (u16)(8 * ((((u32)vertical_addressable_lines() * aspect_h) / aspect_v) / 8)); -} - -u16 Parser::CoordinatedVideoTiming::vertical_addressable_lines() const -{ - return ((u16)(m_cvt.bytes[1] >> 4) << 8) | (u16)m_cvt.bytes[0]; -} - -auto Parser::CoordinatedVideoTiming::aspect_ratio() const -> AspectRatio -{ - return (AspectRatio)((m_cvt.bytes[2] >> 2) & 0x3); -} - -u16 Parser::CoordinatedVideoTiming::preferred_refresh_rate() -{ - switch ((m_cvt.bytes[2] >> 5) & 3) { - case 0: - return 50; - case 1: - return 60; - case 2: - return 75; - case 3: - return 85; - default: - VERIFY_NOT_REACHED(); - } -} - -ErrorOr Parser::for_each_coordinated_video_timing(Function callback) const -{ - return for_each_display_descriptor([&](u8 descriptor_tag, Definitions::DisplayDescriptor const& display_descriptor) { - if (descriptor_tag != (u8)Definitions::DisplayDescriptorTag::CVTTimingCodes) - return IterationDecision::Continue; - u8 version = read_host(&display_descriptor.coordinated_video_timings.version); - if (version != 1) { - dbgln("Unsupported CVT display descriptor version: {}", version); - return IterationDecision::Continue; - } - - for (size_t i = 0; i < 4; i++) { - const DMT::CVT cvt { - { - read_host(&display_descriptor.coordinated_video_timings.cvt[i][0]), - read_host(&display_descriptor.coordinated_video_timings.cvt[i][1]), - read_host(&display_descriptor.coordinated_video_timings.cvt[i][2]), - } - }; - if (cvt.bytes[0] == 0 && cvt.bytes[1] == 0 && cvt.bytes[2] == 0) - continue; - - IterationDecision decision = callback(CoordinatedVideoTiming(cvt)); - if (decision != IterationDecision::Continue) - return decision; - } - return IterationDecision::Continue; - }); -} - -ErrorOr Parser::for_each_detailed_timing(Function callback) const -{ - auto& edid = raw_edid(); - for (size_t raw_index = 0; raw_index < 4; raw_index++) { - if (raw_index == 0 || read_le(&edid.detailed_timing_or_display_descriptors[raw_index].detailed_timing.pixel_clock) != 0) { - IterationDecision decision = callback(DetailedTiming(*this, &edid.detailed_timing_or_display_descriptors[raw_index].detailed_timing), 0); - if (decision != IterationDecision::Continue) - return decision; - } - } - - Optional extension_error; - auto result = TRY(for_each_extension_block([&](u8 block_id, u8 tag, u8, ReadonlyBytes bytes) { - if (tag != (u8)Definitions::ExtensionBlockTag::CEA_861) - return IterationDecision::Continue; - - CEA861ExtensionBlock cea861(*this, (Definitions::ExtensionBlock const*)bytes.data()); - auto result = cea861.for_each_dtd([&](auto& dtd) { - return callback(dtd, block_id); - }); - if (result.is_error()) { - dbgln("Failed to iterate DTDs in CEA861 extension block: {}", result.error()); - extension_error = result.release_error(); - return IterationDecision::Break; - } - - return result.value(); - })); - if (extension_error.has_value()) - return extension_error.release_value(); - return result; -} - -auto Parser::detailed_timing(size_t index) const -> Optional -{ - Optional found_dtd; - auto result = for_each_detailed_timing([&](DetailedTiming const& dtd, unsigned) { - if (index == 0) { - found_dtd = dtd; - return IterationDecision::Break; - } - index--; - return IterationDecision::Continue; - }); - if (result.is_error()) { - dbgln("Error getting Parser detailed timing #{}: {}", index, result.error()); - return {}; - } - return found_dtd; -} - -ErrorOr Parser::for_each_short_video_descriptor(Function callback) const -{ - Optional extension_error; - auto result = for_each_extension_block([&](u8 block_id, u8 tag, u8, ReadonlyBytes bytes) { - if (tag != (u8)Definitions::ExtensionBlockTag::CEA_861) - return IterationDecision::Continue; - - CEA861ExtensionBlock cea861(*this, (Definitions::ExtensionBlock const*)bytes.data()); - auto result = cea861.for_each_short_video_descriptor([&](bool is_native, VIC::Details const& vic) { - return callback(block_id, is_native, vic); - }); - if (result.is_error()) { - extension_error = result.release_error(); - return IterationDecision::Break; - } - return result.value(); - }); - if (result.is_error()) { - dbgln("Failed to iterate Parser extension blocks: {}", result.error()); - return IterationDecision::Break; - } - return result.value(); -} - -ErrorOr Parser::for_each_display_descriptor(Function callback) const -{ - auto& edid = raw_edid(); - for (size_t raw_index = 1; raw_index < 4; raw_index++) { - auto& display_descriptor = edid.detailed_timing_or_display_descriptors[raw_index].display_descriptor; - if (read_le(&display_descriptor.zero) != 0 || read_host(&display_descriptor.reserved1) != 0) - continue; - - u8 tag = read_host(&display_descriptor.tag); - IterationDecision decision = callback(tag, display_descriptor); - if (decision != IterationDecision::Continue) - return decision; - } - - Optional extension_error; - auto result = TRY(for_each_extension_block([&](u8, u8 tag, u8, ReadonlyBytes bytes) { - if (tag != (u8)Definitions::ExtensionBlockTag::CEA_861) - return IterationDecision::Continue; - - CEA861ExtensionBlock cea861(*this, (Definitions::ExtensionBlock const*)bytes.data()); - auto result = cea861.for_each_display_descriptor([&](u8 tag, auto& display_descriptor) { - return callback(tag, display_descriptor); - }); - if (result.is_error()) { - dbgln("Failed to iterate display descriptors in CEA861 extension block: {}", result.error()); - extension_error = result.release_error(); - return IterationDecision::Break; - } - - return result.value(); - })); - if (extension_error.has_value()) - return extension_error.release_value(); - return result; -} - -#ifndef KERNEL -ByteString Parser::display_product_name() const -{ - ByteString product_name; - auto result = for_each_display_descriptor([&](u8 descriptor_tag, Definitions::DisplayDescriptor const& display_descriptor) { - if (descriptor_tag != (u8)Definitions::DisplayDescriptorTag::DisplayProductName) - return IterationDecision::Continue; - - StringBuilder str; - for (u8 byte : display_descriptor.display_product_name.ascii_name) { - if (byte == 0xa) - break; - str.append((char)byte); - } - product_name = str.to_byte_string(); - return IterationDecision::Break; - }); - if (result.is_error()) { - dbgln("Failed to locate product name display descriptor: {}", result.error()); - return {}; - } - return product_name; -} - -ByteString Parser::display_product_serial_number() const -{ - ByteString product_name; - auto result = for_each_display_descriptor([&](u8 descriptor_tag, Definitions::DisplayDescriptor const& display_descriptor) { - if (descriptor_tag != (u8)Definitions::DisplayDescriptorTag::DisplayProductSerialNumber) - return IterationDecision::Continue; - - StringBuilder str; - for (u8 byte : display_descriptor.display_product_serial_number.ascii_str) { - if (byte == 0xa) - break; - str.append((char)byte); - } - product_name = str.to_byte_string(); - return IterationDecision::Break; - }); - if (result.is_error()) { - dbgln("Failed to locate product name display descriptor: {}", result.error()); - return {}; - } - return product_name; -} -#endif - -auto Parser::supported_resolutions() const -> ErrorOr> -{ - Vector resolutions; - - auto add_resolution = [&](unsigned width, unsigned height, FixedPoint<16, u32> refresh_rate, bool preferred = false) { - auto it = resolutions.find_if([&](auto& info) { - return info.width == width && info.height == height; - }); - if (it == resolutions.end()) { - resolutions.append({ width, height, { { refresh_rate, preferred } } }); - } else { - auto& info = *it; - SupportedResolution::RefreshRate* found_refresh_rate = nullptr; - for (auto& supported_refresh_rate : info.refresh_rates) { - if (supported_refresh_rate.rate == refresh_rate) { - found_refresh_rate = &supported_refresh_rate; - break; - } - } - if (found_refresh_rate) - found_refresh_rate->preferred |= preferred; - else - info.refresh_rates.append({ refresh_rate, preferred }); - } - }; - - TRY(for_each_established_timing([&](auto& established_timing) { - if (established_timing.source() != EstablishedTiming::Source::Manufacturer) - add_resolution(established_timing.width(), established_timing.height(), established_timing.refresh_rate()); - return IterationDecision::Continue; - })); - - TRY(for_each_standard_timing([&](auto& standard_timing) { - add_resolution(standard_timing.width(), standard_timing.height(), standard_timing.refresh_rate()); - return IterationDecision::Continue; - })); - - size_t detailed_timing_index = 0; - TRY(for_each_detailed_timing([&](auto& detailed_timing, auto) { - bool is_preferred = detailed_timing_index++ == 0; - add_resolution(detailed_timing.horizontal_addressable_pixels(), detailed_timing.vertical_addressable_lines(), detailed_timing.refresh_rate(), is_preferred); - return IterationDecision::Continue; - })); - - TRY(for_each_short_video_descriptor([&](unsigned, bool, VIC::Details const& vic_details) { - add_resolution(vic_details.horizontal_pixels, vic_details.vertical_lines, vic_details.refresh_rate_hz()); - return IterationDecision::Continue; - })); - - TRY(for_each_coordinated_video_timing([&](auto& coordinated_video_timing) { - if (auto* dmt = DMT::find_timing_by_cvt(coordinated_video_timing.cvt_code())) { - add_resolution(dmt->horizontal_pixels, dmt->vertical_lines, dmt->vertical_frequency_hz()); - } else { - // TODO: We couldn't find this cvt code, try to decode it - auto cvt = coordinated_video_timing.cvt_code(); - dbgln("TODO: Decode CVT code: {:02x},{:02x},{:02x}", cvt.bytes[0], cvt.bytes[1], cvt.bytes[2]); - } - return IterationDecision::Continue; - })); - - quick_sort(resolutions, [&](auto& info1, auto& info2) { - if (info1.width < info2.width) - return true; - if (info1.width == info2.width && info1.height < info2.height) - return true; - return false; - }); - for (auto& res : resolutions) { - if (res.refresh_rates.size() > 1) - quick_sort(res.refresh_rates); - } - return resolutions; -} - -} diff --git a/Userland/Libraries/LibEDID/EDID.h b/Userland/Libraries/LibEDID/EDID.h deleted file mode 100644 index f6bf83d45ea..00000000000 --- a/Userland/Libraries/LibEDID/EDID.h +++ /dev/null @@ -1,464 +0,0 @@ -/* - * Copyright (c) 2022, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#ifdef KERNEL -# include -#else -# include -#endif - -namespace EDID { - -namespace Definitions { -struct EDID; -struct DetailedTiming; -struct DisplayDescriptor; -struct ExtensionBlock; -} - -class Parser final { - friend class CEA861ExtensionBlock; - -public: - static constexpr size_t BufferSize = 128; - using RawBytes = unsigned char[BufferSize]; - -protected: - class DisplayFeatures { - public: - bool supports_standby() const { return (m_features & (1 << 7)) != 0; } - bool supports_suspend() const { return (m_features & (1 << 6)) != 0; } - bool supports_off() const { return (m_features & (1 << 5)) != 0; } - - bool preferred_timing_mode_includes_pixel_format_and_refresh_rate() const - { - if (m_edid_revision < 4) - return true; // Bit 1 must be set to 1 - return (m_features & (1 << 1)) != 0; - } - - bool srgb_is_default_color_space() const { return (m_features & (1 << 2)) != 0; } - - enum class Frequency : u8 { - Continuous, - NonContinuous, - DefaultGTF, - VESA_DMT - }; - Frequency frequency() const - { - if (m_edid_revision < 4) - return ((m_features & 1) != 0) ? Frequency::DefaultGTF : Frequency::VESA_DMT; - return ((m_features & 1) != 0) ? Frequency::Continuous : Frequency::NonContinuous; - } - - protected: - DisplayFeatures(u8 features, u8 edid_revision) - : m_features(features) - , m_edid_revision(edid_revision) - { - } - - u8 m_features { 0 }; - u8 m_edid_revision { 0 }; - }; - -public: - static ErrorOr from_bytes(ReadonlyBytes); - static ErrorOr from_bytes(ByteBuffer&&); - -#ifndef KERNEL - static ErrorOr from_display_connector_device(int); - static ErrorOr from_display_connector_device(ByteString const&); -#endif - - StringView legacy_manufacturer_id() const; -#ifndef KERNEL - ByteString manufacturer_name() const; -#endif - - u16 product_code() const; - u32 serial_number() const; - - class DigitalDisplayFeatures final : public DisplayFeatures { - friend class Parser; - - public: - enum class SupportedColorEncodings : u8 { - RGB444, - RGB444_YCrCb444, - RGB444_YCrCb422, - RGB444_YCrCb444_YCrCb422 - }; - - SupportedColorEncodings supported_color_encodings() const { return (SupportedColorEncodings)((m_features >> 3) & 3); } - - private: - DigitalDisplayFeatures(u8 features, u8 edid_revision) - : DisplayFeatures(features, edid_revision) - { - } - }; - - class AnalogDisplayFeatures final : public DisplayFeatures { - friend class Parser; - - public: - enum class DisplayColorType : u8 { - MonochromeOrGrayscale, - RGB, - NonRGB, - Undefined - }; - - DisplayColorType display_color_type() const { return (DisplayColorType)((m_features >> 3) & 3); } - - private: - AnalogDisplayFeatures(u8 features, u8 edid_revision) - : DisplayFeatures(features, edid_revision) - { - } - }; - - class DigitalDisplay final { - friend class Parser; - - public: - enum class ColorBitDepth : u8 { - Undefined = 0, - BPP_6, - BPP_8, - BPP_10, - BPP_12, - BPP_14, - BPP_16, - Reserved - }; - enum class SupportedInterface : u8 { - Undefined = 0, - DVI, - HDMI_A, - HDMI_B, - MDDI, - DisplayPort, - Reserved - }; - - ColorBitDepth color_bit_depth() const { return (ColorBitDepth)((m_video_input_definition >> 4) & 7); } - SupportedInterface supported_interface() const { return ((m_video_input_definition & 0xf) <= 5) ? (SupportedInterface)(m_video_input_definition & 0xf) : SupportedInterface::Reserved; } - - DigitalDisplayFeatures const& features() { return m_features; } - - private: - DigitalDisplay(u8 video_input_definition, u8 features, u8 edid_revision) - : m_video_input_definition(video_input_definition) - , m_features(features, edid_revision) - { - } - - u8 m_video_input_definition { 0 }; - DigitalDisplayFeatures m_features; - }; - Optional digital_display() const; - - class AnalogDisplay final { - friend class Parser; - - public: - bool separate_sync_h_and_v_supported() const { return (m_video_input_definition & (1 << 3)) != 0; } - - private: - AnalogDisplay(u8 video_input_definition, u8 features, u8 edid_revision) - : m_video_input_definition(video_input_definition) - , m_features(features, edid_revision) - { - } - - u8 m_video_input_definition { 0 }; - AnalogDisplayFeatures m_features; - }; - Optional analog_display() const; - - class ScreenSize final { - friend class Parser; - - public: - unsigned horizontal_cm() const { return m_horizontal_cm; } - unsigned vertical_cm() const { return m_vertical_cm; } - - private: - ScreenSize(u8 horizontal_cm, u8 vertical_cm) - : m_horizontal_cm(horizontal_cm) - , m_vertical_cm(vertical_cm) - { - } - - u8 m_horizontal_cm { 0 }; - u8 m_vertical_cm { 0 }; - }; - Optional screen_size() const; - - class ScreenAspectRatio final { - friend class Parser; - - public: - enum class Orientation { - Landscape, - Portrait - }; - - Orientation orientation() const { return m_orientation; } - auto ratio() const { return m_ratio; } - - private: - ScreenAspectRatio(Orientation orientation, FixedPoint<16> ratio) - : m_orientation(orientation) - , m_ratio(ratio) - { - } - - Orientation m_orientation { Orientation::Landscape }; - FixedPoint<16> m_ratio {}; - }; - Optional aspect_ratio() const; - - Optional> gamma() const; - - class EstablishedTiming final { - friend class Parser; - - public: - enum class Source { - IBM, - Apple, - VESA, - Manufacturer - }; - - ALWAYS_INLINE Source source() const { return m_source; } - ALWAYS_INLINE unsigned width() const { return m_width; } - ALWAYS_INLINE unsigned height() const { return m_height; } - - ALWAYS_INLINE unsigned refresh_rate() const - { - if (m_source == Source::Manufacturer) - return 0; - return m_refresh_rate_or_manufacturer_specific; - } - - ALWAYS_INLINE u8 manufacturer_specific() const - { - VERIFY(m_source == Source::Manufacturer); - return m_refresh_rate_or_manufacturer_specific; - } - - ALWAYS_INLINE u8 dmt_id() const { return m_dmt_id; } - - private: - constexpr EstablishedTiming(Source source, u16 width, u16 height, u8 refresh_rate_or_manufacturer_specific, u8 dmt_id = 0) - : m_source(source) - , m_width(width) - , m_height(height) - , m_refresh_rate_or_manufacturer_specific(refresh_rate_or_manufacturer_specific) - , m_dmt_id(dmt_id) - { - } - - Source m_source { Source::IBM }; - u16 m_width { 0 }; - u16 m_height { 0 }; - u8 m_refresh_rate_or_manufacturer_specific { 0 }; - u8 m_dmt_id { 0 }; - }; - - ErrorOr for_each_established_timing(Function) const; - - class StandardTiming final { - friend class Parser; - - public: - enum class AspectRatio { - AR_16_10, - AR_4_3, - AR_5_4, - AR_16_9 - }; - unsigned width() const { return m_width; } - unsigned height() const { return m_height; } - unsigned refresh_rate() const { return m_refresh_rate; } - AspectRatio aspect_ratio() const { return m_aspect_ratio; } - u8 dmt_id() const { return m_dmt_id; } - - private: - constexpr StandardTiming(u16 width, u16 height, u8 refresh_rate, AspectRatio aspect_ratio, u8 dmt_id) - : m_width(width) - , m_height(height) - , m_refresh_rate(refresh_rate) - , m_aspect_ratio(aspect_ratio) - , m_dmt_id(dmt_id) - { - } - - u16 m_width { 0 }; - u16 m_height { 0 }; - u8 m_refresh_rate { 0 }; - AspectRatio m_aspect_ratio { AspectRatio::AR_16_10 }; - u8 m_dmt_id { 0 }; - }; - - ErrorOr for_each_standard_timing(Function) const; - - class DetailedTiming final { - friend class Parser; - friend class CEA861ExtensionBlock; - - public: - u32 pixel_clock_khz() const; - u16 horizontal_addressable_pixels() const; - u16 horizontal_blanking_pixels() const; - u16 vertical_addressable_lines() const; - u16 vertical_blanking_lines() const; - u16 horizontal_front_porch_pixels() const; - ALWAYS_INLINE u16 horizontal_back_porch_pixels() const { return horizontal_blanking_pixels() - horizontal_sync_pulse_width_pixels() - horizontal_front_porch_pixels(); } - u16 horizontal_sync_pulse_width_pixels() const; - u16 vertical_front_porch_lines() const; - ALWAYS_INLINE u16 vertical_back_porch_lines() const { return vertical_blanking_lines() - vertical_sync_pulse_width_lines() - vertical_front_porch_lines(); } - u16 vertical_sync_pulse_width_lines() const; - u16 horizontal_image_size_mm() const; - u16 vertical_image_size_mm() const; - u8 horizontal_right_or_left_border_pixels() const; - u8 vertical_top_or_bottom_border_lines() const; - - bool is_interlaced() const; - FixedPoint<16, u32> refresh_rate() const; - - private: - DetailedTiming(Parser const& edid, Definitions::DetailedTiming const* detailed_timings) - : m_edid(edid) - , m_detailed_timings(*detailed_timings) - { - } - - u16 vertical_addressable_lines_raw() const; - - Parser const& m_edid; - Definitions::DetailedTiming const& m_detailed_timings; - }; - - ErrorOr for_each_detailed_timing(Function) const; - Optional detailed_timing(size_t) const; - -#ifndef KERNEL - ByteString display_product_name() const; - ByteString display_product_serial_number() const; -#endif - - ErrorOr for_each_short_video_descriptor(Function) const; - - class CoordinatedVideoTiming final { - friend class Parser; - - public: - enum class AspectRatio : u8 { - AR_4_3 = 0, - AR_16_9 = 1, - AR_16_10 = 2, - AR_15_9 = 3 - }; - - u16 horizontal_addressable_pixels() const; - u16 vertical_addressable_lines() const; - AspectRatio aspect_ratio() const; - u16 preferred_refresh_rate(); - - ALWAYS_INLINE DMT::CVT cvt_code() const { return m_cvt; } - - private: - CoordinatedVideoTiming(DMT::CVT const& cvt) - : m_cvt(cvt) - { - } - - DMT::CVT m_cvt; - }; - - ErrorOr for_each_coordinated_video_timing(Function) const; - - ErrorOr for_each_extension_block(Function) const; - - struct SupportedResolution { - unsigned width { 0 }; - unsigned height { 0 }; - struct RefreshRate { - FixedPoint<16, u32> rate; - bool preferred { false }; - - bool operator<(RefreshRate const& rhs) const { return rate < rhs.rate; } - }; - Vector refresh_rates; - }; - ErrorOr> supported_resolutions() const; - - Parser() = default; - Parser(Parser&&) = default; - Parser(Parser const&); - Parser& operator=(Parser&&); - Parser& operator=(Parser const&); - - bool operator==(Parser const& other) const; - - StringView version() const; - - auto bytes() const { return m_bytes; } - -private: - Parser(ReadonlyBytes); - Parser(ByteBuffer&&); - - ErrorOr parse(); - - template - T read_host(T const*) const; - - template - requires(sizeof(T) > 1) - T read_le(T const*) const; - - template - requires(sizeof(T) > 1) - T read_be(T const*) const; - - Definitions::EDID const& raw_edid() const; - ErrorOr for_each_display_descriptor(Function) const; - - ByteBuffer m_bytes_buffer; - ReadonlyBytes m_bytes; - u8 m_revision { 0 }; -#ifdef KERNEL - OwnPtr m_version; -#else - ByteString m_version; -#endif - char m_legacy_manufacturer_id[4] {}; - bool m_legacy_manufacturer_id_valid { false }; -}; - -} diff --git a/Userland/Libraries/LibEDID/VIC.cpp b/Userland/Libraries/LibEDID/VIC.cpp deleted file mode 100644 index 460c664bde9..00000000000 --- a/Userland/Libraries/LibEDID/VIC.cpp +++ /dev/null @@ -1,198 +0,0 @@ -/* - * Copyright (c) 2022, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include - -namespace EDID { - -// Video ID Code details as per CTA-861-G revised 2018 Table 3 -static constexpr VIC::Details s_vic_details[] = { - { 1, 640, 480, 59940, VIC::Details::ScanType::NonInterlaced, VIC::Details::AspectRatio::AR_4_3 }, - { 2, 720, 480, 59940, VIC::Details::ScanType::NonInterlaced, VIC::Details::AspectRatio::AR_4_3 }, - { 3, 720, 480, 59940, VIC::Details::ScanType::NonInterlaced, VIC::Details::AspectRatio::AR_16_9 }, - { 4, 1280, 720, 59940, VIC::Details::ScanType::NonInterlaced, VIC::Details::AspectRatio::AR_16_9 }, - { 5, 1920, 1080, 59940, VIC::Details::ScanType::Interlaced, VIC::Details::AspectRatio::AR_16_9 }, - { 6, 1440, 480, 59940, VIC::Details::ScanType::Interlaced, VIC::Details::AspectRatio::AR_4_3 }, - { 7, 1440, 480, 59940, VIC::Details::ScanType::Interlaced, VIC::Details::AspectRatio::AR_16_9 }, - { 8, 1440, 240, 59940, VIC::Details::ScanType::NonInterlaced, VIC::Details::AspectRatio::AR_4_3 }, - { 9, 1440, 240, 59940, VIC::Details::ScanType::NonInterlaced, VIC::Details::AspectRatio::AR_16_9 }, - { 10, 2880, 480, 59940, VIC::Details::ScanType::Interlaced, VIC::Details::AspectRatio::AR_4_3 }, - { 11, 2880, 480, 59940, VIC::Details::ScanType::Interlaced, VIC::Details::AspectRatio::AR_16_9 }, - { 12, 2880, 240, 59940, VIC::Details::ScanType::NonInterlaced, VIC::Details::AspectRatio::AR_4_3 }, - { 13, 2880, 240, 59940, VIC::Details::ScanType::NonInterlaced, VIC::Details::AspectRatio::AR_16_9 }, - { 14, 1440, 480, 59940, VIC::Details::ScanType::NonInterlaced, VIC::Details::AspectRatio::AR_4_3 }, - { 15, 1440, 480, 59940, VIC::Details::ScanType::NonInterlaced, VIC::Details::AspectRatio::AR_16_9 }, - { 16, 1920, 180, 59940, VIC::Details::ScanType::NonInterlaced, VIC::Details::AspectRatio::AR_16_9 }, - { 17, 720, 576, 50000, VIC::Details::ScanType::NonInterlaced, VIC::Details::AspectRatio::AR_4_3 }, - { 18, 720, 576, 50000, VIC::Details::ScanType::NonInterlaced, VIC::Details::AspectRatio::AR_16_9 }, - { 19, 1280, 720, 50000, VIC::Details::ScanType::NonInterlaced, VIC::Details::AspectRatio::AR_16_9 }, - { 20, 1920, 1080, 50000, VIC::Details::ScanType::Interlaced, VIC::Details::AspectRatio::AR_16_9 }, - { 21, 1440, 576, 50000, VIC::Details::ScanType::Interlaced, VIC::Details::AspectRatio::AR_4_3 }, - { 22, 1440, 576, 50000, VIC::Details::ScanType::Interlaced, VIC::Details::AspectRatio::AR_16_9 }, - { 23, 1440, 288, 50000, VIC::Details::ScanType::NonInterlaced, VIC::Details::AspectRatio::AR_4_3 }, - { 24, 1440, 288, 50000, VIC::Details::ScanType::NonInterlaced, VIC::Details::AspectRatio::AR_16_9 }, - { 25, 2880, 576, 50000, VIC::Details::ScanType::Interlaced, VIC::Details::AspectRatio::AR_4_3 }, - { 26, 2880, 576, 50000, VIC::Details::ScanType::Interlaced, VIC::Details::AspectRatio::AR_16_9 }, - { 27, 2880, 288, 50000, VIC::Details::ScanType::NonInterlaced, VIC::Details::AspectRatio::AR_4_3 }, - { 28, 2880, 288, 50000, VIC::Details::ScanType::NonInterlaced, VIC::Details::AspectRatio::AR_16_9 }, - { 29, 1440, 576, 50000, VIC::Details::ScanType::NonInterlaced, VIC::Details::AspectRatio::AR_4_3 }, - { 30, 1440, 576, 50000, VIC::Details::ScanType::NonInterlaced, VIC::Details::AspectRatio::AR_16_9 }, - { 31, 1920, 1080, 50000, VIC::Details::ScanType::NonInterlaced, VIC::Details::AspectRatio::AR_16_9 }, - { 32, 1920, 1080, 23980, VIC::Details::ScanType::NonInterlaced, VIC::Details::AspectRatio::AR_16_9 }, - { 33, 1920, 1080, 25000, VIC::Details::ScanType::NonInterlaced, VIC::Details::AspectRatio::AR_16_9 }, - { 34, 1920, 1080, 29970, VIC::Details::ScanType::NonInterlaced, VIC::Details::AspectRatio::AR_16_9 }, - { 35, 2880, 480, 59940, VIC::Details::ScanType::NonInterlaced, VIC::Details::AspectRatio::AR_4_3 }, - { 36, 2880, 480, 59940, VIC::Details::ScanType::NonInterlaced, VIC::Details::AspectRatio::AR_16_9 }, - { 37, 2880, 576, 50000, VIC::Details::ScanType::NonInterlaced, VIC::Details::AspectRatio::AR_4_3 }, - { 38, 2880, 576, 50000, VIC::Details::ScanType::NonInterlaced, VIC::Details::AspectRatio::AR_16_9 }, - { 39, 1920, 1080, 50000, VIC::Details::ScanType::Interlaced, VIC::Details::AspectRatio::AR_16_9 }, - { 40, 1920, 1080, 100000, VIC::Details::ScanType::Interlaced, VIC::Details::AspectRatio::AR_16_9 }, - { 41, 1280, 720, 100000, VIC::Details::ScanType::NonInterlaced, VIC::Details::AspectRatio::AR_16_9 }, - { 42, 720, 576, 100000, VIC::Details::ScanType::NonInterlaced, VIC::Details::AspectRatio::AR_4_3 }, - { 43, 720, 576, 100000, VIC::Details::ScanType::NonInterlaced, VIC::Details::AspectRatio::AR_16_9 }, - { 44, 1440, 576, 100000, VIC::Details::ScanType::Interlaced, VIC::Details::AspectRatio::AR_4_3 }, - { 45, 1440, 576, 100000, VIC::Details::ScanType::Interlaced, VIC::Details::AspectRatio::AR_16_9 }, - { 46, 1920, 1080, 119880, VIC::Details::ScanType::Interlaced, VIC::Details::AspectRatio::AR_16_9 }, - { 47, 1280, 720, 119880, VIC::Details::ScanType::NonInterlaced, VIC::Details::AspectRatio::AR_16_9 }, - { 48, 720, 480, 119880, VIC::Details::ScanType::NonInterlaced, VIC::Details::AspectRatio::AR_4_3 }, - { 49, 720, 480, 119880, VIC::Details::ScanType::NonInterlaced, VIC::Details::AspectRatio::AR_16_9 }, - { 50, 1440, 480, 119880, VIC::Details::ScanType::Interlaced, VIC::Details::AspectRatio::AR_4_3 }, - { 51, 1440, 480, 119880, VIC::Details::ScanType::Interlaced, VIC::Details::AspectRatio::AR_16_9 }, - { 52, 720, 576, 200000, VIC::Details::ScanType::NonInterlaced, VIC::Details::AspectRatio::AR_4_3 }, - { 53, 720, 576, 200000, VIC::Details::ScanType::NonInterlaced, VIC::Details::AspectRatio::AR_16_9 }, - { 54, 1440, 576, 200000, VIC::Details::ScanType::Interlaced, VIC::Details::AspectRatio::AR_4_3 }, - { 55, 1440, 576, 200000, VIC::Details::ScanType::Interlaced, VIC::Details::AspectRatio::AR_16_9 }, - { 56, 720, 480, 239760, VIC::Details::ScanType::NonInterlaced, VIC::Details::AspectRatio::AR_4_3 }, - { 57, 720, 480, 239760, VIC::Details::ScanType::NonInterlaced, VIC::Details::AspectRatio::AR_16_9 }, - { 58, 1440, 480, 239760, VIC::Details::ScanType::Interlaced, VIC::Details::AspectRatio::AR_4_3 }, - { 59, 1440, 480, 239760, VIC::Details::ScanType::Interlaced, VIC::Details::AspectRatio::AR_16_9 }, - { 60, 1280, 720, 23980, VIC::Details::ScanType::NonInterlaced, VIC::Details::AspectRatio::AR_16_9 }, - { 61, 1280, 720, 25000, VIC::Details::ScanType::NonInterlaced, VIC::Details::AspectRatio::AR_16_9 }, - { 62, 1280, 720, 29970, VIC::Details::ScanType::NonInterlaced, VIC::Details::AspectRatio::AR_16_9 }, - { 63, 1920, 1080, 119880, VIC::Details::ScanType::NonInterlaced, VIC::Details::AspectRatio::AR_16_9 }, - { 64, 1920, 1080, 100000, VIC::Details::ScanType::NonInterlaced, VIC::Details::AspectRatio::AR_16_9 }, - { 65, 1280, 720, 23980, VIC::Details::ScanType::NonInterlaced, VIC::Details::AspectRatio::AR_64_27 }, - { 66, 1280, 720, 25000, VIC::Details::ScanType::NonInterlaced, VIC::Details::AspectRatio::AR_64_27 }, - { 67, 1280, 720, 29970, VIC::Details::ScanType::NonInterlaced, VIC::Details::AspectRatio::AR_64_27 }, - { 68, 1280, 720, 50000, VIC::Details::ScanType::NonInterlaced, VIC::Details::AspectRatio::AR_64_27 }, - { 69, 1280, 720, 59940, VIC::Details::ScanType::NonInterlaced, VIC::Details::AspectRatio::AR_64_27 }, - { 70, 1280, 720, 100000, VIC::Details::ScanType::NonInterlaced, VIC::Details::AspectRatio::AR_64_27 }, - { 71, 1280, 720, 119880, VIC::Details::ScanType::NonInterlaced, VIC::Details::AspectRatio::AR_64_27 }, - { 72, 1920, 1080, 23980, VIC::Details::ScanType::NonInterlaced, VIC::Details::AspectRatio::AR_64_27 }, - { 73, 1920, 1080, 25000, VIC::Details::ScanType::NonInterlaced, VIC::Details::AspectRatio::AR_64_27 }, - { 74, 1920, 1080, 29970, VIC::Details::ScanType::NonInterlaced, VIC::Details::AspectRatio::AR_64_27 }, - { 75, 1920, 1080, 50000, VIC::Details::ScanType::NonInterlaced, VIC::Details::AspectRatio::AR_64_27 }, - { 76, 1920, 1080, 59940, VIC::Details::ScanType::NonInterlaced, VIC::Details::AspectRatio::AR_64_27 }, - { 77, 1920, 1080, 100000, VIC::Details::ScanType::NonInterlaced, VIC::Details::AspectRatio::AR_64_27 }, - { 78, 1920, 1080, 119880, VIC::Details::ScanType::NonInterlaced, VIC::Details::AspectRatio::AR_64_27 }, - { 79, 1680, 720, 23980, VIC::Details::ScanType::NonInterlaced, VIC::Details::AspectRatio::AR_64_27 }, - { 80, 1680, 720, 25000, VIC::Details::ScanType::NonInterlaced, VIC::Details::AspectRatio::AR_64_27 }, - { 81, 1680, 720, 29970, VIC::Details::ScanType::NonInterlaced, VIC::Details::AspectRatio::AR_64_27 }, - { 82, 1680, 720, 50000, VIC::Details::ScanType::NonInterlaced, VIC::Details::AspectRatio::AR_64_27 }, - { 83, 1680, 720, 59940, VIC::Details::ScanType::NonInterlaced, VIC::Details::AspectRatio::AR_64_27 }, - { 84, 1680, 720, 100000, VIC::Details::ScanType::NonInterlaced, VIC::Details::AspectRatio::AR_64_27 }, - { 85, 1680, 720, 119880, VIC::Details::ScanType::NonInterlaced, VIC::Details::AspectRatio::AR_64_27 }, - { 86, 2560, 1080, 23980, VIC::Details::ScanType::NonInterlaced, VIC::Details::AspectRatio::AR_64_27 }, - { 87, 2560, 1080, 25000, VIC::Details::ScanType::NonInterlaced, VIC::Details::AspectRatio::AR_64_27 }, - { 88, 2560, 1080, 29970, VIC::Details::ScanType::NonInterlaced, VIC::Details::AspectRatio::AR_64_27 }, - { 89, 2560, 1080, 50000, VIC::Details::ScanType::NonInterlaced, VIC::Details::AspectRatio::AR_64_27 }, - { 90, 2560, 1080, 59940, VIC::Details::ScanType::NonInterlaced, VIC::Details::AspectRatio::AR_64_27 }, - { 91, 2560, 1080, 100000, VIC::Details::ScanType::NonInterlaced, VIC::Details::AspectRatio::AR_64_27 }, - { 92, 2560, 1080, 119880, VIC::Details::ScanType::NonInterlaced, VIC::Details::AspectRatio::AR_64_27 }, - { 93, 3840, 2160, 23980, VIC::Details::ScanType::NonInterlaced, VIC::Details::AspectRatio::AR_16_9 }, - { 94, 3840, 2160, 25000, VIC::Details::ScanType::NonInterlaced, VIC::Details::AspectRatio::AR_16_9 }, - { 95, 3840, 2160, 29970, VIC::Details::ScanType::NonInterlaced, VIC::Details::AspectRatio::AR_16_9 }, - { 96, 3840, 2160, 50000, VIC::Details::ScanType::NonInterlaced, VIC::Details::AspectRatio::AR_16_9 }, - { 97, 3840, 2160, 59940, VIC::Details::ScanType::NonInterlaced, VIC::Details::AspectRatio::AR_16_9 }, - { 98, 4096, 2160, 23980, VIC::Details::ScanType::NonInterlaced, VIC::Details::AspectRatio::AR_256_135 }, - { 99, 4096, 2160, 25000, VIC::Details::ScanType::NonInterlaced, VIC::Details::AspectRatio::AR_256_135 }, - { 100, 4096, 2160, 29970, VIC::Details::ScanType::NonInterlaced, VIC::Details::AspectRatio::AR_256_135 }, - { 101, 4096, 2160, 50000, VIC::Details::ScanType::NonInterlaced, VIC::Details::AspectRatio::AR_256_135 }, - { 102, 4096, 2160, 59940, VIC::Details::ScanType::NonInterlaced, VIC::Details::AspectRatio::AR_256_135 }, - { 103, 3840, 2160, 23980, VIC::Details::ScanType::NonInterlaced, VIC::Details::AspectRatio::AR_64_27 }, - { 104, 3840, 2160, 25000, VIC::Details::ScanType::NonInterlaced, VIC::Details::AspectRatio::AR_64_27 }, - { 105, 3840, 2160, 29970, VIC::Details::ScanType::NonInterlaced, VIC::Details::AspectRatio::AR_64_27 }, - { 106, 3840, 2160, 50000, VIC::Details::ScanType::NonInterlaced, VIC::Details::AspectRatio::AR_64_27 }, - { 107, 3840, 2160, 59940, VIC::Details::ScanType::NonInterlaced, VIC::Details::AspectRatio::AR_64_27 }, - { 108, 1280, 720, 47950, VIC::Details::ScanType::NonInterlaced, VIC::Details::AspectRatio::AR_16_9 }, - { 109, 1280, 720, 47950, VIC::Details::ScanType::NonInterlaced, VIC::Details::AspectRatio::AR_64_27 }, - { 110, 1680, 720, 47950, VIC::Details::ScanType::NonInterlaced, VIC::Details::AspectRatio::AR_16_9 }, - { 111, 1920, 1080, 47950, VIC::Details::ScanType::NonInterlaced, VIC::Details::AspectRatio::AR_16_9 }, - { 112, 1920, 1080, 47950, VIC::Details::ScanType::NonInterlaced, VIC::Details::AspectRatio::AR_64_27 }, - { 113, 2560, 1080, 47950, VIC::Details::ScanType::NonInterlaced, VIC::Details::AspectRatio::AR_64_27 }, - { 114, 3840, 2160, 47950, VIC::Details::ScanType::NonInterlaced, VIC::Details::AspectRatio::AR_16_9 }, - { 115, 4096, 2160, 47950, VIC::Details::ScanType::NonInterlaced, VIC::Details::AspectRatio::AR_256_135 }, - { 116, 3840, 2160, 47950, VIC::Details::ScanType::NonInterlaced, VIC::Details::AspectRatio::AR_64_27 }, - { 117, 3840, 2160, 100000, VIC::Details::ScanType::NonInterlaced, VIC::Details::AspectRatio::AR_16_9 }, - { 118, 3840, 2160, 119880, VIC::Details::ScanType::NonInterlaced, VIC::Details::AspectRatio::AR_16_9 }, - { 119, 3840, 2160, 100000, VIC::Details::ScanType::NonInterlaced, VIC::Details::AspectRatio::AR_64_27 }, - { 120, 3840, 2160, 119880, VIC::Details::ScanType::NonInterlaced, VIC::Details::AspectRatio::AR_64_27 }, - { 121, 5120, 2160, 23980, VIC::Details::ScanType::NonInterlaced, VIC::Details::AspectRatio::AR_64_27 }, - { 122, 5120, 2160, 25000, VIC::Details::ScanType::NonInterlaced, VIC::Details::AspectRatio::AR_64_27 }, - { 123, 5120, 2160, 29970, VIC::Details::ScanType::NonInterlaced, VIC::Details::AspectRatio::AR_64_27 }, - { 124, 5120, 2160, 47950, VIC::Details::ScanType::NonInterlaced, VIC::Details::AspectRatio::AR_64_27 }, - { 125, 5120, 2160, 50000, VIC::Details::ScanType::NonInterlaced, VIC::Details::AspectRatio::AR_64_27 }, - { 126, 5120, 2160, 59940, VIC::Details::ScanType::NonInterlaced, VIC::Details::AspectRatio::AR_64_27 }, - { 127, 5120, 2160, 100000, VIC::Details::ScanType::NonInterlaced, VIC::Details::AspectRatio::AR_64_27 }, - // 128...192 forbidden range - { 193, 5120, 2160, 119880, VIC::Details::ScanType::NonInterlaced, VIC::Details::AspectRatio::AR_64_27 }, - { 194, 7680, 4320, 23980, VIC::Details::ScanType::NonInterlaced, VIC::Details::AspectRatio::AR_16_9 }, - { 195, 7680, 4320, 25000, VIC::Details::ScanType::NonInterlaced, VIC::Details::AspectRatio::AR_16_9 }, - { 196, 7680, 4320, 29970, VIC::Details::ScanType::NonInterlaced, VIC::Details::AspectRatio::AR_16_9 }, - { 197, 7680, 4320, 47950, VIC::Details::ScanType::NonInterlaced, VIC::Details::AspectRatio::AR_16_9 }, - { 198, 7680, 4320, 50000, VIC::Details::ScanType::NonInterlaced, VIC::Details::AspectRatio::AR_16_9 }, - { 199, 7680, 4320, 59940, VIC::Details::ScanType::NonInterlaced, VIC::Details::AspectRatio::AR_16_9 }, - { 200, 7680, 4320, 100000, VIC::Details::ScanType::NonInterlaced, VIC::Details::AspectRatio::AR_16_9 }, - { 201, 7680, 4320, 119880, VIC::Details::ScanType::NonInterlaced, VIC::Details::AspectRatio::AR_16_9 }, - { 202, 7680, 4320, 23980, VIC::Details::ScanType::NonInterlaced, VIC::Details::AspectRatio::AR_64_27 }, - { 203, 7680, 4320, 25000, VIC::Details::ScanType::NonInterlaced, VIC::Details::AspectRatio::AR_64_27 }, - { 204, 7680, 4320, 29970, VIC::Details::ScanType::NonInterlaced, VIC::Details::AspectRatio::AR_64_27 }, - { 205, 7680, 4320, 47950, VIC::Details::ScanType::NonInterlaced, VIC::Details::AspectRatio::AR_64_27 }, - { 206, 7680, 4320, 50000, VIC::Details::ScanType::NonInterlaced, VIC::Details::AspectRatio::AR_64_27 }, - { 207, 7680, 4320, 59940, VIC::Details::ScanType::NonInterlaced, VIC::Details::AspectRatio::AR_64_27 }, - { 208, 7680, 4320, 100000, VIC::Details::ScanType::NonInterlaced, VIC::Details::AspectRatio::AR_64_27 }, - { 209, 7680, 4320, 119880, VIC::Details::ScanType::NonInterlaced, VIC::Details::AspectRatio::AR_64_27 }, - { 210, 10240, 4320, 23980, VIC::Details::ScanType::NonInterlaced, VIC::Details::AspectRatio::AR_64_27 }, - { 211, 10240, 4320, 25000, VIC::Details::ScanType::NonInterlaced, VIC::Details::AspectRatio::AR_64_27 }, - { 212, 10240, 4320, 29970, VIC::Details::ScanType::NonInterlaced, VIC::Details::AspectRatio::AR_64_27 }, - { 213, 10240, 4320, 47950, VIC::Details::ScanType::NonInterlaced, VIC::Details::AspectRatio::AR_64_27 }, - { 214, 10240, 4320, 50000, VIC::Details::ScanType::NonInterlaced, VIC::Details::AspectRatio::AR_64_27 }, - { 215, 10240, 4320, 59940, VIC::Details::ScanType::NonInterlaced, VIC::Details::AspectRatio::AR_64_27 }, - { 216, 10240, 4320, 100000, VIC::Details::ScanType::NonInterlaced, VIC::Details::AspectRatio::AR_64_27 }, - { 217, 10240, 4320, 119880, VIC::Details::ScanType::NonInterlaced, VIC::Details::AspectRatio::AR_64_27 }, - { 218, 4096, 2160, 100000, VIC::Details::ScanType::NonInterlaced, VIC::Details::AspectRatio::AR_256_135 }, - { 219, 4096, 2160, 119880, VIC::Details::ScanType::NonInterlaced, VIC::Details::AspectRatio::AR_256_135 }, - // 220...255 reserved -}; - -static constexpr size_t s_vic_details_count = sizeof(s_vic_details) / sizeof(s_vic_details[0]); -static constexpr u8 s_reserved_vic_id_start = 220; -static_assert(s_vic_details[s_vic_details_count - 1].vic_id == s_reserved_vic_id_start - 1); - -FixedPoint<16, u32> VIC::Details::refresh_rate_hz() const -{ - return FixedPoint<16, u32>(refresh_rate_millihz) / 1000; -} - -auto VIC::find_details_by_vic_id(u8 vic_id) -> Details const* -{ - if (vic_id == 0 || (vic_id >= 128 && vic_id <= 192) || vic_id >= s_reserved_vic_id_start) - return nullptr; - - u8 table_index = vic_id - 1; - if (table_index < 128) { - // Before the forbidden block (128...192) - VERIFY(s_vic_details[table_index].vic_id == vic_id); - return &s_vic_details[table_index]; - } - - // After the forbidden block range (128...192) - table_index = table_index - 192 + 128 - 1; - VERIFY(s_vic_details[table_index].vic_id == vic_id); - return &s_vic_details[table_index]; -} - -} diff --git a/Userland/Libraries/LibEDID/VIC.h b/Userland/Libraries/LibEDID/VIC.h deleted file mode 100644 index 2ccb61ed293..00000000000 --- a/Userland/Libraries/LibEDID/VIC.h +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright (c) 2022, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include - -namespace EDID { - -class VIC final { -public: - struct Details { - enum class ScanType : u8 { - NonInterlaced, - Interlaced - }; - enum class AspectRatio : u8 { - AR_4_3, - AR_16_9, - AR_64_27, - AR_256_135, - }; - - u8 vic_id; - u16 horizontal_pixels; - u16 vertical_lines; - u32 refresh_rate_millihz; - ScanType scan_type; - AspectRatio aspect_ratio; - - FixedPoint<16, u32> refresh_rate_hz() const; - }; - - static Details const* find_details_by_vic_id(u8); -}; - -} diff --git a/Userland/Libraries/LibFileSystemAccessClient/CMakeLists.txt b/Userland/Libraries/LibFileSystemAccessClient/CMakeLists.txt deleted file mode 100644 index 7d0b4537b5b..00000000000 --- a/Userland/Libraries/LibFileSystemAccessClient/CMakeLists.txt +++ /dev/null @@ -1,12 +0,0 @@ -set(SOURCES - Client.cpp -) - -set(GENERATED_SOURCES - ../../Services/FileSystemAccessServer/FileSystemAccessClientEndpoint.h - ../../Services/FileSystemAccessServer/FileSystemAccessServerEndpoint.h -) - -serenity_lib(LibFileSystemAccessClient filesystemaccessclient) -target_link_libraries(LibFileSystemAccessClient PRIVATE LibCore LibFileSystem LibIPC) -add_dependencies(LibFileSystemAccessClient WindowServer) diff --git a/Userland/Libraries/LibFileSystemAccessClient/Client.cpp b/Userland/Libraries/LibFileSystemAccessClient/Client.cpp deleted file mode 100644 index c73ef7f7c19..00000000000 --- a/Userland/Libraries/LibFileSystemAccessClient/Client.cpp +++ /dev/null @@ -1,182 +0,0 @@ -/* - * Copyright (c) 2021, timmot - * Copyright (c) 2022, Mustafa Quraish - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include -#include - -namespace FileSystemAccessClient { - -static RefPtr s_the = nullptr; - -Client& Client::the() -{ - if (!s_the || !s_the->is_open()) - s_the = Client::try_create().release_value_but_fixme_should_propagate_errors(); - return *s_the; -} - -Result Client::request_file_read_only_approved(GUI::Window* parent_window, ByteString const& path) -{ - auto const id = get_new_id(); - m_promises.set(id, RequestData { { Core::Promise::construct() }, parent_window, Core::File::OpenMode::Read }); - - if (path.starts_with('/')) { - async_request_file_read_only_approved(id, path); - } else { - auto full_path = LexicalPath::join(TRY(FileSystem::current_working_directory()), path).string(); - async_request_file_read_only_approved(id, full_path); - } - - return handle_promise(id); -} - -Result Client::request_file(GUI::Window* parent_window, ByteString const& path, Core::File::OpenMode mode) -{ - auto const id = get_new_id(); - m_promises.set(id, RequestData { { Core::Promise::construct() }, parent_window, mode }); - - auto parent_window_server_client_id = GUI::ConnectionToWindowServer::the().expose_client_id(); - auto child_window_server_client_id = expose_window_server_client_id(); - auto parent_window_id = parent_window->window_id(); - - GUI::ConnectionToWindowServer::the().add_window_stealing_for_client(child_window_server_client_id, parent_window_id); - - ScopeGuard guard([parent_window_id, child_window_server_client_id] { - GUI::ConnectionToWindowServer::the().remove_window_stealing_for_client(child_window_server_client_id, parent_window_id); - }); - - if (path.starts_with('/')) { - async_request_file(id, parent_window_server_client_id, parent_window_id, path, mode); - } else { - auto full_path = LexicalPath::join(TRY(FileSystem::current_working_directory()), path).string(); - async_request_file(id, parent_window_server_client_id, parent_window_id, full_path, mode); - } - - return handle_promise(id); -} - -Result Client::open_file(GUI::Window* parent_window, OpenFileOptions const& options) -{ - auto const id = get_new_id(); - m_promises.set(id, RequestData { { Core::Promise::construct() }, parent_window, options.requested_access }); - - auto parent_window_server_client_id = GUI::ConnectionToWindowServer::the().expose_client_id(); - auto child_window_server_client_id = expose_window_server_client_id(); - auto parent_window_id = parent_window->window_id(); - - GUI::ConnectionToWindowServer::the().add_window_stealing_for_client(child_window_server_client_id, parent_window_id); - - ScopeGuard guard([parent_window_id, child_window_server_client_id] { - GUI::ConnectionToWindowServer::the().remove_window_stealing_for_client(child_window_server_client_id, parent_window_id); - }); - - async_prompt_open_file(id, parent_window_server_client_id, parent_window_id, options.window_title, options.path, options.requested_access, options.allowed_file_types); - - return handle_promise(id); -} - -Result Client::save_file(GUI::Window* parent_window, ByteString const& name, ByteString const ext, Core::File::OpenMode requested_access) -{ - auto const id = get_new_id(); - m_promises.set(id, RequestData { { Core::Promise::construct() }, parent_window, requested_access }); - - auto parent_window_server_client_id = GUI::ConnectionToWindowServer::the().expose_client_id(); - auto child_window_server_client_id = expose_window_server_client_id(); - auto parent_window_id = parent_window->window_id(); - - GUI::ConnectionToWindowServer::the().add_window_stealing_for_client(child_window_server_client_id, parent_window_id); - - ScopeGuard guard([parent_window_id, child_window_server_client_id] { - GUI::ConnectionToWindowServer::the().remove_window_stealing_for_client(child_window_server_client_id, parent_window_id); - }); - - async_prompt_save_file(id, parent_window_server_client_id, parent_window_id, name.is_empty() ? "Untitled" : name, ext.is_empty() ? "txt" : ext, Core::StandardPaths::home_directory(), requested_access); - - return handle_promise(id); -} - -void Client::handle_prompt_end(i32 request_id, i32 error, Optional const& ipc_file, Optional const& chosen_file) -{ - auto potential_data = m_promises.get(request_id); - VERIFY(potential_data.has_value()); - auto& request_data = potential_data.value(); - - auto action = "Requesting"sv; - if (has_flag(request_data.mode, Core::File::OpenMode::Read)) - action = "Opening"sv; - else if (has_flag(request_data.mode, Core::File::OpenMode::Write)) - action = "Saving"sv; - - if (ipc_file.has_value()) { - if (FileSystem::is_device(ipc_file->fd())) - error = is_silencing_devices() ? ESUCCESS : EINVAL; - else if (FileSystem::is_directory(ipc_file->fd())) - error = is_silencing_directories() ? ESUCCESS : EISDIR; - } - - switch (error) { - case ESUCCESS: - case ECANCELED: - break; - case ENOENT: - if (is_silencing_nonexistent_entries()) - break; - [[fallthrough]]; - default: - ErrorOr maybe_message = String {}; - if (error == ECONNRESET) - maybe_message = String::formatted("FileSystemAccessClient: {}", Error::from_errno(error)); - else - maybe_message = String::formatted("{} \"{}\" failed: {}", action, *chosen_file, Error::from_errno(error)); - if (!maybe_message.is_error()) - (void)GUI::MessageBox::try_show_error(request_data.parent_window, maybe_message.release_value()); - } - - if (error != ESUCCESS) - return (void)request_data.promise->resolve(Error::from_errno(error)); - - auto file_or_error = [&]() -> ErrorOr { - auto stream = TRY(Core::File::adopt_fd(ipc_file->take_fd(), Core::File::OpenMode::ReadWrite)); - return File({}, move(stream), *chosen_file); - }(); - if (file_or_error.is_error()) { - auto maybe_message = String::formatted("{} \"{}\" failed: {}", action, *chosen_file, file_or_error.error()); - if (!maybe_message.is_error()) - (void)GUI::MessageBox::try_show_error(request_data.parent_window, maybe_message.release_value()); - return (void)request_data.promise->resolve(file_or_error.release_error()); - } - - (void)request_data.promise->resolve(file_or_error.release_value()); -} - -void Client::die() -{ - for (auto const& entry : m_promises) - handle_prompt_end(entry.key, ECONNRESET, {}, ""); -} - -int Client::get_new_id() -{ - auto const new_id = m_last_id++; - // Note: This verify shouldn't fail, and we should provide a valid ID - // But we probably have more issues if this test fails. - VERIFY(!m_promises.contains(new_id)); - return new_id; -} - -Result Client::handle_promise(int id) -{ - auto result = TRY(m_promises.get(id)->promise->await()); - m_promises.remove(id); - return result; -} - -} diff --git a/Userland/Libraries/LibFileSystemAccessClient/Client.h b/Userland/Libraries/LibFileSystemAccessClient/Client.h deleted file mode 100644 index b0236df8551..00000000000 --- a/Userland/Libraries/LibFileSystemAccessClient/Client.h +++ /dev/null @@ -1,106 +0,0 @@ -/* - * Copyright (c) 2021, timmot - * Copyright (c) 2022, Mustafa Quraish - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace FileSystemAccessClient { - -enum ErrorFlag : u32 { - Devices = 1 << 0, - Directories = 1 << 1, - NoEntries = 1 << 2, - - None = 0, -}; - -class Client; -class File { -public: - File(Badge, NonnullOwnPtr stream, ByteString filename) - : m_stream(move(stream)) - , m_filename(filename) - { - } - - Core::File& stream() const { return *m_stream; } - NonnullOwnPtr release_stream() { return move(m_stream); } - ByteString const& filename() const { return m_filename; } - -private: - NonnullOwnPtr m_stream; - ByteString m_filename; -}; - -using Result = ErrorOr; - -struct OpenFileOptions { - StringView window_title = {}; - ByteString path = Core::StandardPaths::home_directory(); - Core::File::OpenMode requested_access = Core::File::OpenMode::Read; - Optional> allowed_file_types = {}; -}; - -class Client final - : public IPC::ConnectionToServer - , public FileSystemAccessClientEndpoint { - IPC_CLIENT_CONNECTION(Client, "/tmp/session/%sid/portal/filesystemaccess"sv) - -public: - Result request_file_read_only_approved(GUI::Window* parent_window, ByteString const& path); - Result request_file(GUI::Window* parent_window, ByteString const& path, Core::File::OpenMode requested_access); - Result open_file(GUI::Window* parent_window, OpenFileOptions const& = {}); - Result save_file(GUI::Window* parent_window, ByteString const& name, ByteString const ext, Core::File::OpenMode requested_access = Core::File::OpenMode::Write | Core::File::OpenMode::Truncate); - - void set_silence_errors(u32 flags) { m_silenced_errors = flags; } - u32 silenced_errors() const { return m_silenced_errors; } - - bool is_silencing_devices() { return m_silenced_errors & ErrorFlag::Devices; } - bool is_silencing_directories() { return m_silenced_errors & ErrorFlag::Directories; } - bool is_silencing_nonexistent_entries() { return m_silenced_errors & ErrorFlag::NoEntries; } - - static Client& the(); - -protected: - void die() override; - -private: - explicit Client(NonnullOwnPtr socket) - : IPC::ConnectionToServer(*this, move(socket)) - { - } - - virtual void handle_prompt_end(i32 request_id, i32 error, Optional const& fd, Optional const& chosen_file) override; - - int get_new_id(); - Result handle_promise(int); - - template - using PromiseType = RefPtr>; - - struct RequestData { - PromiseType promise; - GUI::Window* parent_window { nullptr }; - Core::File::OpenMode mode { Core::File::OpenMode::NotOpen }; - }; - - HashMap m_promises {}; - int m_last_id { 0 }; - u32 m_silenced_errors { ErrorFlag::None }; -}; - -} diff --git a/Userland/Libraries/LibIMAP/CMakeLists.txt b/Userland/Libraries/LibIMAP/CMakeLists.txt deleted file mode 100644 index b738e2d322f..00000000000 --- a/Userland/Libraries/LibIMAP/CMakeLists.txt +++ /dev/null @@ -1,12 +0,0 @@ -set(SOURCES - Client.cpp - Objects.cpp - Parser.cpp - QuotedPrintable.cpp - MessageHeaderEncoding.cpp -) - -set(GENERATED_SOURCES) - -serenity_lib(LibIMAP imap) -target_link_libraries(LibIMAP PRIVATE LibCore LibCrypto LibTextCodec LibTLS) diff --git a/Userland/Libraries/LibIMAP/Client.cpp b/Userland/Libraries/LibIMAP/Client.cpp deleted file mode 100644 index 70fbb3e6c3d..00000000000 --- a/Userland/Libraries/LibIMAP/Client.cpp +++ /dev/null @@ -1,461 +0,0 @@ -/* - * Copyright (c) 2021, Kyle Pereira - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include - -namespace IMAP { - -Client::Client(StringView host, u16 port, NonnullOwnPtr socket) - : m_host(host) - , m_port(port) - , m_socket(move(socket)) - , m_connect_pending(Promise::construct()) -{ - setup_callbacks(); -} - -Client::Client(Client&& other) - : m_host(other.m_host) - , m_port(other.m_port) - , m_socket(move(other.m_socket)) - , m_connect_pending(move(other.m_connect_pending)) -{ - setup_callbacks(); -} - -void Client::setup_callbacks() -{ - m_socket->on_ready_to_read = [&] { - auto maybe_error = on_ready_to_receive(); - if (maybe_error.is_error()) { - dbgln("Error receiving from the socket: {}", maybe_error.error()); - close(); - } - }; -} - -ErrorOr> Client::connect_tls(StringView host, u16 port) -{ - auto tls_socket = TRY(TLS::TLSv12::connect(host, port)); - dbgln("connecting to {}:{}", host, port); - - return adopt_nonnull_own_or_enomem(new (nothrow) Client(host, port, move(tls_socket))); -} - -ErrorOr> Client::connect_plaintext(StringView host, u16 port) -{ - auto socket = TRY(Core::TCPSocket::connect(host, port)); - dbgln("Connected to {}:{}", host, port); - return adopt_nonnull_own_or_enomem(new (nothrow) Client(host, port, move(socket))); -} - -bool Client::verify_response_is_complete() -{ - // FIXME: This is still more of a heuristic than a proper approach. - // I would imagine this breaks if we happen to get an email that - // contains this pattern we're looking for. - dbgln("Waiting for a complete IMAP response, buffer size is now {}", m_buffer.size()); - Vector statuses = { "OK"sv, "BAD"sv, "NO"sv }; - auto slice_size = m_buffer.size() >= 100 ? 100 : m_buffer.size(); // Arbitrary slice size, should contain what we're looking for. - auto slice_data = MUST(m_buffer.slice(m_buffer.size() - slice_size, slice_size)); - StringView slice = StringView(slice_data); - for (auto status : statuses) { - ByteString pattern = ByteString::formatted("A{} {}", m_current_command, status); - if (slice.contains(pattern)) { - dbgln("IMAP server replied {}, sending to parser", pattern); - return true; - } - } - return false; -} - -ErrorOr Client::on_ready_to_receive() -{ - if (!TRY(m_socket->can_read_without_blocking())) - return {}; - - auto pending_bytes = TRY(m_socket->pending_bytes()); - auto receive_buffer = TRY(m_buffer.get_bytes_for_writing(pending_bytes)); - TRY(m_socket->read_until_filled(receive_buffer)); - - // Once we get server hello we can start sending. - if (m_connect_pending) { - m_connect_pending->resolve({}); - m_connect_pending.clear(); - m_buffer.clear(); - return {}; - } - - // Don't try parsing until we have a complete response. - if (verify_response_is_complete()) { - auto response = m_parser.parse(move(m_buffer), m_expecting_response); - TRY(handle_parsed_response(move(response))); - m_buffer.clear(); - } - - return {}; -} - -static ReadonlyBytes command_byte_buffer(CommandType command) -{ - switch (command) { - case CommandType::Noop: - return "NOOP"sv.bytes(); - case CommandType::Capability: - return "CAPABILITY"sv.bytes(); - case CommandType::Logout: - return "LOGOUT"sv.bytes(); - case CommandType ::Idle: - return "IDLE"sv.bytes(); - case CommandType::Login: - return "LOGIN"sv.bytes(); - case CommandType::List: - return "LIST"sv.bytes(); - case CommandType::Select: - return "SELECT"sv.bytes(); - case CommandType::Fetch: - return "FETCH"sv.bytes(); - case CommandType::Store: - return "STORE"sv.bytes(); - case CommandType::Copy: - return "COPY"sv.bytes(); - case CommandType::Create: - return "CREATE"sv.bytes(); - case CommandType::Delete: - return "DELETE"sv.bytes(); - case CommandType::Search: - return "SEARCH"sv.bytes(); - case CommandType::UIDFetch: - return "UID FETCH"sv.bytes(); - case CommandType::UIDStore: - return "UID STORE"sv.bytes(); - case CommandType::UIDCopy: - return "UID COPY"sv.bytes(); - case CommandType::UIDSearch: - return "UID SEARCH"sv.bytes(); - case CommandType::Append: - return "APPEND"sv.bytes(); - case CommandType::Examine: - return "EXAMINE"sv.bytes(); - case CommandType::ListSub: - return "LSUB"sv.bytes(); - case CommandType::Expunge: - return "EXPUNGE"sv.bytes(); - case CommandType::Subscribe: - return "SUBSCRIBE"sv.bytes(); - case CommandType::Unsubscribe: - return "UNSUBSCRIBE"sv.bytes(); - case CommandType::Authenticate: - return "AUTHENTICATE"sv.bytes(); - case CommandType::Check: - return "CHECK"sv.bytes(); - case CommandType::Close: - return "CLOSE"sv.bytes(); - case CommandType::Rename: - return "RENAME"sv.bytes(); - case CommandType::Status: - return "STATUS"sv.bytes(); - } - VERIFY_NOT_REACHED(); -} - -ErrorOr Client::send_raw(StringView data) -{ - TRY(m_socket->write_until_depleted(data.bytes())); - TRY(m_socket->write_until_depleted("\r\n"sv.bytes())); - - return {}; -} - -NonnullRefPtr> Client::send_command(Command&& command) -{ - m_command_queue.append(move(command)); - m_current_command++; - - auto promise = Promise::construct(); - m_pending_promises.append(promise); - - if (m_pending_promises.size() == 1) { - auto maybe_error = send_next_command(); - if (maybe_error.is_error()) - promise->reject(maybe_error.release_error()); - } - - return promise; -} - -template -NonnullRefPtr> cast_promise(NonnullRefPtr> promise_variant) -{ - auto new_promise = promise_variant->map( - [](Response& variant) { - return move(variant.get()); - }); - return new_promise; -} - -NonnullRefPtr> Client::login(StringView username, StringView password) -{ - auto command = Command { CommandType::Login, m_current_command, { serialize_astring(username), serialize_astring(password) } }; - return cast_promise(send_command(move(command))); -} - -NonnullRefPtr> Client::list(StringView reference_name, StringView mailbox) -{ - auto command = Command { CommandType::List, m_current_command, - { ByteString::formatted("\"{}\"", reference_name), - ByteString::formatted("\"{}\"", mailbox) } }; - return cast_promise(send_command(move(command))); -} - -NonnullRefPtr> Client::lsub(StringView reference_name, StringView mailbox) -{ - auto command = Command { CommandType::ListSub, m_current_command, - { ByteString::formatted("\"{}\"", reference_name), - ByteString::formatted("\"{}\"", mailbox) } }; - return cast_promise(send_command(move(command))); -} - -NonnullRefPtr> Client::fetch(FetchCommand request, bool uid) -{ - auto command = Command { uid ? CommandType::UIDFetch : CommandType::Fetch, m_current_command, { request.serialize() } }; - return cast_promise(send_command(move(command))); -} - -NonnullRefPtr> Client::send_simple_command(CommandType type) -{ - auto command = Command { type, m_current_command, {} }; - return send_command(move(command)); -} - -NonnullRefPtr> Client::select(StringView string) -{ - auto command = Command { CommandType::Select, m_current_command, { serialize_astring(string) } }; - return cast_promise(send_command(move(command))); -} - -ErrorOr Client::handle_parsed_response(ParseStatus&& parse_status) -{ - if (!m_expecting_response) { - if (!parse_status.successful) { - dbgln("Parsing failed on unrequested data!"); - } else if (parse_status.response.has_value()) { - unrequested_response_callback(move(parse_status.response.value().get().data())); - } - } else { - bool should_send_next = false; - if (!parse_status.successful) { - m_expecting_response = false; - m_pending_promises.take_first()->reject(Error::from_string_literal("Failed to parse message")); - } - if (parse_status.response.has_value()) { - m_expecting_response = false; - should_send_next = parse_status.response->has(); - m_pending_promises.take_first()->resolve(parse_status.response.release_value()); - } - - if (should_send_next && !m_command_queue.is_empty()) { - TRY(send_next_command()); - } - } - - return {}; -} - -ErrorOr Client::send_next_command() -{ - auto command = m_command_queue.take_first(); - ByteBuffer buffer; - auto tag = AK::ByteString::formatted("A{} ", m_current_command); - buffer += tag.to_byte_buffer(); - auto command_type = command_byte_buffer(command.type); - buffer.append(command_type.data(), command_type.size()); - - for (auto& arg : command.args) { - buffer.append(" ", 1); - buffer.append(arg.bytes().data(), arg.length()); - } - - TRY(send_raw(buffer)); - m_expecting_response = true; - return {}; -} - -NonnullRefPtr> Client::examine(StringView string) -{ - auto command = Command { CommandType::Examine, m_current_command, { serialize_astring(string) } }; - return cast_promise(send_command(move(command))); -} - -NonnullRefPtr> Client::create_mailbox(StringView name) -{ - auto command = Command { CommandType::Create, m_current_command, { serialize_astring(name) } }; - return cast_promise(send_command(move(command))); -} - -NonnullRefPtr> Client::delete_mailbox(StringView name) -{ - auto command = Command { CommandType::Delete, m_current_command, { serialize_astring(name) } }; - return cast_promise(send_command(move(command))); -} - -NonnullRefPtr> Client::store(StoreMethod method, Sequence sequence_set, bool silent, Vector const& flags, bool uid) -{ - StringBuilder data_item_name; - switch (method) { - case StoreMethod::Replace: - data_item_name.append("FLAGS"sv); - break; - case StoreMethod::Add: - data_item_name.append("+FLAGS"sv); - break; - case StoreMethod::Remove: - data_item_name.append("-FLAGS"sv); - break; - } - if (silent) { - data_item_name.append(".SILENT"sv); - } - - StringBuilder flags_builder; - flags_builder.append('('); - flags_builder.join(' ', flags); - flags_builder.append(')'); - - auto command = Command { uid ? CommandType::UIDStore : CommandType::Store, m_current_command, { sequence_set.serialize(), data_item_name.to_byte_string(), flags_builder.to_byte_string() } }; - return cast_promise(send_command(move(command))); -} -NonnullRefPtr> Client::search(Optional charset, Vector&& keys, bool uid) -{ - Vector args; - if (charset.has_value()) { - args.append("CHARSET "sv); - args.append(charset.value()); - } - for (auto const& item : keys) { - args.append(item.serialize()); - } - auto command = Command { uid ? CommandType::UIDSearch : CommandType::Search, m_current_command, args }; - return cast_promise(send_command(move(command))); -} - -NonnullRefPtr> Client::idle() -{ - auto promise = send_simple_command(CommandType::Idle); - return cast_promise(promise); -} -NonnullRefPtr> Client::finish_idle() -{ - auto promise = Promise::construct(); - m_pending_promises.append(promise); - MUST(send_raw("DONE"sv)); - m_expecting_response = true; - return cast_promise(promise); -} - -NonnullRefPtr> Client::status(StringView mailbox, Vector const& types) -{ - Vector args; - for (auto type : types) { - switch (type) { - case StatusItemType::Recent: - args.append("RECENT"sv); - break; - case StatusItemType::UIDNext: - args.append("UIDNEXT"sv); - break; - case StatusItemType::UIDValidity: - args.append("UIDVALIDITY"sv); - break; - case StatusItemType::Unseen: - args.append("UNSEEN"sv); - break; - case StatusItemType::Messages: - args.append("MESSAGES"sv); - break; - } - } - StringBuilder types_list; - types_list.append('('); - types_list.join(' ', args); - types_list.append(')'); - auto command = Command { CommandType::Status, m_current_command, { mailbox, types_list.to_byte_string() } }; - return cast_promise(send_command(move(command))); -} - -NonnullRefPtr> Client::append(StringView mailbox, Message&& message, Optional> flags, Optional date_time) -{ - Vector args = { mailbox }; - if (flags.has_value()) { - StringBuilder flags_sb; - flags_sb.append('('); - flags_sb.join(' ', flags.value()); - flags_sb.append(')'); - args.append(flags_sb.to_byte_string()); - } - if (date_time.has_value()) - args.append(date_time.value().to_byte_string("\"%d-%b-%Y %H:%M:%S +0000\""sv)); - - args.append(ByteString::formatted("{{{}}}", message.data.length())); - - auto continue_req = send_command(Command { CommandType::Append, m_current_command, args }); - - auto response_promise = Promise::construct(); - m_pending_promises.append(response_promise); - - continue_req->on_resolution = [this, message2 { move(message) }](auto&) -> ErrorOr { - TRY(send_raw(message2.data)); - m_expecting_response = true; - return {}; - }; - continue_req->on_rejection = [this](Error&) { - // NOTE: This never fails. - MUST(handle_parsed_response({ .successful = false, .response = {} })); - }; - - return cast_promise(response_promise); -} -NonnullRefPtr> Client::subscribe(StringView mailbox) -{ - auto command = Command { CommandType::Subscribe, m_current_command, { serialize_astring(mailbox) } }; - return cast_promise(send_command(move(command))); -} -NonnullRefPtr> Client::unsubscribe(StringView mailbox) -{ - auto command = Command { CommandType::Unsubscribe, m_current_command, { serialize_astring(mailbox) } }; - return cast_promise(send_command(move(command))); -} -NonnullRefPtr> Client::authenticate(StringView method) -{ - auto command = Command { CommandType::Authenticate, m_current_command, { method } }; - return send_command(move(command)); -} -NonnullRefPtr> Client::rename(StringView from, StringView to) -{ - auto command = Command { CommandType::Rename, m_current_command, { serialize_astring(from), serialize_astring(to) } }; - return cast_promise(send_command(move(command))); -} -NonnullRefPtr> Client::copy(Sequence sequence_set, StringView name, bool uid) -{ - auto command = Command { - uid ? CommandType::UIDCopy : CommandType::Copy, m_current_command, { sequence_set.serialize(), serialize_astring(name) } - }; - - return cast_promise(send_command(move(command))); -} - -void Client::close() -{ - m_socket->close(); -} - -bool Client::is_open() -{ - return m_socket->is_open(); -} - -} diff --git a/Userland/Libraries/LibIMAP/Client.h b/Userland/Libraries/LibIMAP/Client.h deleted file mode 100644 index a44ea04ad2f..00000000000 --- a/Userland/Libraries/LibIMAP/Client.h +++ /dev/null @@ -1,89 +0,0 @@ -/* - * Copyright (c) 2021, Kyle Pereira - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include - -namespace IMAP { -template -using Promise = Core::Promise; - -class Client { - AK_MAKE_NONCOPYABLE(Client); - friend class Parser; - -public: - static ErrorOr> connect_tls(StringView host, u16 port); - static ErrorOr> connect_plaintext(StringView host, u16 port); - - Client(Client&&); - - RefPtr> connection_promise() - { - return m_connect_pending; - } - - NonnullRefPtr> send_command(Command&&); - NonnullRefPtr> send_simple_command(CommandType); - ErrorOr send_raw(StringView data); - NonnullRefPtr> login(StringView username, StringView password); - NonnullRefPtr> list(StringView reference_name, StringView mailbox_name); - NonnullRefPtr> lsub(StringView reference_name, StringView mailbox_name); - NonnullRefPtr> select(StringView string); - NonnullRefPtr> examine(StringView string); - NonnullRefPtr> search(Optional charset, Vector&& search_keys, bool uid); - NonnullRefPtr> fetch(FetchCommand request, bool uid); - NonnullRefPtr> store(StoreMethod, Sequence, bool silent, Vector const& flags, bool uid); - NonnullRefPtr> copy(Sequence sequence_set, StringView name, bool uid); - NonnullRefPtr> create_mailbox(StringView name); - NonnullRefPtr> delete_mailbox(StringView name); - NonnullRefPtr> subscribe(StringView mailbox); - NonnullRefPtr> unsubscribe(StringView mailbox); - NonnullRefPtr> rename(StringView from, StringView to); - NonnullRefPtr> authenticate(StringView method); - NonnullRefPtr> idle(); - NonnullRefPtr> finish_idle(); - NonnullRefPtr> status(StringView mailbox, Vector const& types); - NonnullRefPtr> append(StringView mailbox, Message&& message, Optional> flags = {}, Optional date_time = {}); - - bool is_open(); - void close(); - - Function unrequested_response_callback; - -private: - Client(StringView host, u16 port, NonnullOwnPtr); - void setup_callbacks(); - - ErrorOr on_ready_to_receive(); - bool verify_response_is_complete(); - - ErrorOr handle_parsed_response(ParseStatus&& parse_status); - ErrorOr send_next_command(); - - StringView m_host; - u16 m_port; - - NonnullOwnPtr m_socket; - RefPtr> m_connect_pending {}; - - int m_current_command = 1; - - // Sent but response not received - Vector>> m_pending_promises; - // Not yet sent - Vector m_command_queue {}; - - ByteBuffer m_buffer; - Parser m_parser {}; - - bool m_expecting_response { false }; -}; -} diff --git a/Userland/Libraries/LibIMAP/MessageHeaderEncoding.cpp b/Userland/Libraries/LibIMAP/MessageHeaderEncoding.cpp deleted file mode 100644 index 47151b1f977..00000000000 --- a/Userland/Libraries/LibIMAP/MessageHeaderEncoding.cpp +++ /dev/null @@ -1,97 +0,0 @@ -/* - * Copyright (c) 2023, Valtteri Koskivuori - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "QuotedPrintable.h" -#include -#include -#include -#include -#include - -namespace IMAP { - -ErrorOr decode_rfc2047_encoded_words(StringView input) -{ - GenericLexer lexer(input); - StringBuilder output; - - while (!lexer.is_eof()) { - auto ascii_view = lexer.consume_until("=?"sv); - ByteString ascii = ascii_view.replace("\r"sv, " "sv, ReplaceMode::All); - ascii = ascii.replace("\n"sv, " "sv, ReplaceMode::All); - TRY(output.try_append(ascii)); - if (lexer.is_eof()) - break; - lexer.consume_specific("=?"sv); - auto charset = lexer.consume_until('?'); - lexer.consume(); - auto encoding = lexer.consume_until('?'); - lexer.consume(); - auto encoded_text = lexer.consume_until("?="); - lexer.consume_specific("?="sv); - - // RFC 2047 Section 6.2, "...any 'linear-white-space' that separates a pair of adjacent 'encoded-word's is ignored." - // https://datatracker.ietf.org/doc/html/rfc2047#section-6.2 - bool found_next_start = false; - int spaces = 0; - for (size_t i = 0; i < lexer.tell_remaining(); ++i) { - if (lexer.peek(i) == ' ' || lexer.peek(i) == '\r' || lexer.peek(i) == '\n') { - spaces++; - if (lexer.peek(i + 1) == '=' && lexer.peek(i + 2) == '?') { - found_next_start = true; - break; - } - } else { - break; - } - } - if (found_next_start) { - for (int i = 0; i < spaces; i++) { - lexer.consume(); - } - } - - ByteBuffer first_pass_decoded; - if (encoding == 'Q' || encoding == 'q') { - auto maybe_decoded_data = decode_quoted_printable(encoded_text); - if (maybe_decoded_data.is_error()) { - dbgln("Failed to decode quoted-printable rfc2047 text, skipping."); - continue; - } - // RFC 2047 Section 4.2.2, https://datatracker.ietf.org/doc/html/rfc2047#section-4.2 - auto decoded_data = maybe_decoded_data.release_value(); - for (auto character : decoded_data.bytes()) { - if (character == '_') - first_pass_decoded.append(' '); - else - first_pass_decoded.append(character); - } - } else if (encoding == 'B' || encoding == 'b') { - auto maybe_decoded_data = AK::decode_base64(encoded_text); - if (maybe_decoded_data.is_error()) { - dbgln("Failed to decode base64-encoded rfc2047 text, skipping."); - continue; - } - first_pass_decoded = maybe_decoded_data.release_value(); - } else { - dbgln("Unknown encoding \"{}\" found, skipping, original string: \"{}\"", encoding, input); - continue; - } - if (first_pass_decoded.is_empty()) - continue; - auto maybe_decoder = TextCodec::decoder_for(charset); - if (!maybe_decoder.has_value()) { - dbgln("No decoder found for charset \"{}\", skipping.", charset); - continue; - } - auto decoded_text = TRY(maybe_decoder->to_utf8(first_pass_decoded)); - TRY(output.try_append(decoded_text)); - } - - return output.to_byte_buffer(); -} - -} diff --git a/Userland/Libraries/LibIMAP/MessageHeaderEncoding.h b/Userland/Libraries/LibIMAP/MessageHeaderEncoding.h deleted file mode 100644 index f0a4b7437e8..00000000000 --- a/Userland/Libraries/LibIMAP/MessageHeaderEncoding.h +++ /dev/null @@ -1,15 +0,0 @@ -/* - * Copyright (c) 2023, Valtteri Koskivuori - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include - -namespace IMAP { - -ErrorOr decode_rfc2047_encoded_words(StringView input); - -} diff --git a/Userland/Libraries/LibIMAP/Objects.cpp b/Userland/Libraries/LibIMAP/Objects.cpp deleted file mode 100644 index e6e2926b585..00000000000 --- a/Userland/Libraries/LibIMAP/Objects.cpp +++ /dev/null @@ -1,188 +0,0 @@ -/* - * Copyright (c) 2021, Kyle Pereira - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include - -namespace IMAP { - -ByteString Sequence::serialize() const -{ - if (start == end) { - return AK::ByteString::formatted("{}", start); - } else { - auto start_char = start != -1 ? ByteString::formatted("{}", start) : "*"; - auto end_char = end != -1 ? ByteString::formatted("{}", end) : "*"; - return ByteString::formatted("{}:{}", start_char, end_char); - } -} - -ByteString FetchCommand::DataItem::Section::serialize() const -{ - StringBuilder headers_builder; - switch (type) { - case SectionType::Header: - return "HEADER"; - case SectionType::HeaderFields: - case SectionType::HeaderFieldsNot: { - if (type == SectionType::HeaderFields) - headers_builder.append("HEADER.FIELDS ("sv); - else - headers_builder.append("HEADERS.FIELDS.NOT ("sv); - - bool first = true; - for (auto& field : headers.value()) { - if (!first) - headers_builder.append(' '); - headers_builder.append(field); - first = false; - } - headers_builder.append(')'); - return headers_builder.to_byte_string(); - } - case SectionType::Text: - return "TEXT"; - case SectionType::Parts: { - StringBuilder sb; - bool first = true; - for (int part : parts.value()) { - if (!first) - sb.append('.'); - sb.appendff("{}", part); - first = false; - } - if (ends_with_mime) { - sb.append(".MIME"sv); - } - return sb.to_byte_string(); - } - } - VERIFY_NOT_REACHED(); -} -ByteString FetchCommand::DataItem::serialize() const -{ - switch (type) { - case DataItemType::Envelope: - return "ENVELOPE"; - case DataItemType::Flags: - return "FLAGS"; - case DataItemType::InternalDate: - return "INTERNALDATE"; - case DataItemType::UID: - return "UID"; - case DataItemType::PeekBody: - case DataItemType::BodySection: { - StringBuilder sb; - if (type == DataItemType::BodySection) - sb.appendff("BODY[{}]", section.value().serialize()); - else - sb.appendff("BODY.PEEK[{}]", section.value().serialize()); - if (partial_fetch) { - sb.appendff("<{}.{}>", start, octets); - } - - return sb.to_byte_string(); - } - case DataItemType::BodyStructure: - return "BODYSTRUCTURE"; - } - VERIFY_NOT_REACHED(); -} -ByteString FetchCommand::serialize() -{ - StringBuilder sequence_builder; - bool first = true; - for (auto& sequence : sequence_set) { - if (!first) { - sequence_builder.append(','); - } - sequence_builder.append(sequence.serialize()); - first = false; - } - - StringBuilder data_items_builder; - first = true; - for (auto& data_item : data_items) { - if (!first) { - data_items_builder.append(' '); - } - data_items_builder.append(data_item.serialize()); - first = false; - } - - return AK::ByteString::formatted("{} ({})", sequence_builder.to_byte_string(), data_items_builder.to_byte_string()); -} -ByteString serialize_astring(StringView string) -{ - // Try to send an atom - auto is_non_atom_char = [](char x) { - auto non_atom_chars = { '(', ')', '{', ' ', '%', '*', '"', '\\', ']' }; - return AK::find(non_atom_chars.begin(), non_atom_chars.end(), x) != non_atom_chars.end(); - }; - auto is_atom = all_of(string, [&](auto ch) { return is_ascii_control(ch) && !is_non_atom_char(ch); }); - if (is_atom) { - return string; - } - - // Try to quote - auto can_be_quoted = !(string.contains('\n') || string.contains('\r')); - if (can_be_quoted) { - auto escaped_str = string.replace("\\"sv, "\\\\"sv, ReplaceMode::All).replace("\""sv, "\\\""sv, ReplaceMode::All); - return ByteString::formatted("\"{}\"", escaped_str); - } - - // Just send a literal - return ByteString::formatted("{{{}}}\r\n{}", string.length(), string); -} -ByteString SearchKey::serialize() const -{ - return data.visit( - [&](All const&) { return ByteString("ALL"); }, - [&](Answered const&) { return ByteString("ANSWERED"); }, - [&](Bcc const& x) { return ByteString::formatted("BCC {}", serialize_astring(x.bcc)); }, - [&](Cc const& x) { return ByteString::formatted("CC {}", serialize_astring(x.cc)); }, - [&](Deleted const&) { return ByteString("DELETED"); }, - [&](Draft const&) { return ByteString("DRAFT"); }, - [&](From const& x) { return ByteString::formatted("FROM {}", serialize_astring(x.from)); }, - [&](Header const& x) { return ByteString::formatted("HEADER {} {}", serialize_astring(x.header), serialize_astring(x.value)); }, - [&](Keyword const& x) { return ByteString::formatted("KEYWORD {}", x.keyword); }, - [&](Larger const& x) { return ByteString::formatted("LARGER {}", x.number); }, - [&](New const&) { return ByteString("NEW"); }, - [&](Not const& x) { return ByteString::formatted("NOT {}", x.operand->serialize()); }, - [&](Old const&) { return ByteString("OLD"); }, - [&](On const& x) { return ByteString::formatted("ON {}", x.date.to_byte_string("%d-%b-%Y"sv)); }, - [&](Or const& x) { return ByteString::formatted("OR {} {}", x.lhs->serialize(), x.rhs->serialize()); }, - [&](Recent const&) { return ByteString("RECENT"); }, - [&](SearchKeys const& x) { - StringBuilder sb; - sb.append('('); - bool first = true; - for (auto const& item : x.keys) { - if (!first) - sb.append(", "sv); - sb.append(item->serialize()); - first = false; - } - return sb.to_byte_string(); - }, - [&](Seen const&) { return ByteString("SEEN"); }, - [&](SentBefore const& x) { return ByteString::formatted("SENTBEFORE {}", x.date.to_byte_string("%d-%b-%Y"sv)); }, - [&](SentOn const& x) { return ByteString::formatted("SENTON {}", x.date.to_byte_string("%d-%b-%Y"sv)); }, - [&](SentSince const& x) { return ByteString::formatted("SENTSINCE {}", x.date.to_byte_string("%d-%b-%Y"sv)); }, - [&](SequenceSet const& x) { return x.sequence.serialize(); }, - [&](Since const& x) { return ByteString::formatted("SINCE {}", x.date.to_byte_string("%d-%b-%Y"sv)); }, - [&](Smaller const& x) { return ByteString::formatted("SMALLER {}", x.number); }, - [&](Subject const& x) { return ByteString::formatted("SUBJECT {}", serialize_astring(x.subject)); }, - [&](Text const& x) { return ByteString::formatted("TEXT {}", serialize_astring(x.text)); }, - [&](To const& x) { return ByteString::formatted("TO {}", serialize_astring(x.to)); }, - [&](UID const& x) { return ByteString::formatted("UID {}", x.uid); }, - [&](Unanswered const&) { return ByteString("UNANSWERED"); }, - [&](Undeleted const&) { return ByteString("UNDELETED"); }, - [&](Undraft const&) { return ByteString("UNDRAFT"); }, - [&](Unkeyword const& x) { return ByteString::formatted("UNKEYWORD {}", serialize_astring(x.flag_keyword)); }, - [&](Unseen const&) { return ByteString("UNSEEN"); }); -} -} diff --git a/Userland/Libraries/LibIMAP/Objects.h b/Userland/Libraries/LibIMAP/Objects.h deleted file mode 100644 index 0bd186e107e..00000000000 --- a/Userland/Libraries/LibIMAP/Objects.h +++ /dev/null @@ -1,771 +0,0 @@ -/* - * Copyright (c) 2021, Kyle Pereira - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include - -namespace IMAP { -enum class CommandType { - Append, - Authenticate, - Capability, - Copy, - Check, - Close, - Create, - Delete, - Examine, - Expunge, - Fetch, - Idle, - List, - ListSub, - Login, - Logout, - Noop, - Rename, - Search, - Select, - Status, - Store, - Subscribe, - UIDCopy, - UIDFetch, - UIDSearch, - UIDStore, - Unsubscribe, -}; - -enum class MailboxFlag : unsigned { - All = 1u << 0, - Drafts = 1u << 1, - Flagged = 1u << 2, - HasChildren = 1u << 3, - HasNoChildren = 1u << 4, - Important = 1u << 5, - Junk = 1u << 6, - Marked = 1u << 7, - NoInferiors = 1u << 8, - NoSelect = 1u << 9, - Sent = 1u << 10, - Trash = 1u << 11, - Unmarked = 1u << 12, - Unknown = 1u << 13, -}; - -enum class ResponseType : unsigned { - Capability = 1u << 0, - List = 1u << 1, - Exists = 1u << 2, - Recent = 1u << 3, - Flags = 1u << 4, - UIDNext = 1u << 5, - UIDValidity = 1u << 6, - Unseen = 1u << 7, - PermanentFlags = 1u << 8, - Fetch = 1u << 9, - Search = 1u << 10, - ListSub = 1u << 11, - Expunged = 1u << 12, - Bye = 1u << 13, - Status = 1u << 14 -}; - -enum class FetchResponseType : unsigned { - Body = 1u << 1, - UID = 1u << 2, - InternalDate = 1u << 3, - Envelope = 1u << 4, - Flags = 1u << 5, - BodyStructure = 1u << 6, -}; - -enum class StatusItemType : unsigned { - Recent = 1u << 1, - UIDNext = 1u << 2, - UIDValidity = 1u << 3, - Unseen = 1u << 4, - Messages = 1u << 5, -}; - -class Parser; - -class StatusItem { -public: - [[nodiscard]] unsigned status_items() const - { - return m_status_items; - } - - [[nodiscard]] bool contains_status_item_type(StatusItemType type) const - { - return (static_cast(type) & m_status_items) != 0; - } - - void add_status_item_type(StatusItemType type) - { - m_status_items |= static_cast(type); - } - - void set_mailbox(ByteString&& mailbox) { m_mailbox = move(mailbox); } - ByteString& mailbox() { return m_mailbox; } - - unsigned get(StatusItemType type) const - { - VERIFY(contains_status_item_type(type)); - switch (type) { - case StatusItemType::Recent: - return m_recent; - case StatusItemType::UIDNext: - return m_uid_next; - case StatusItemType::UIDValidity: - return m_uid_validity; - case StatusItemType::Unseen: - return m_unseen; - case StatusItemType::Messages: - return m_messages; - } - VERIFY_NOT_REACHED(); - } - - void set(StatusItemType type, unsigned value) - { - add_status_item_type(type); - switch (type) { - case StatusItemType::Recent: - m_recent = value; - break; - case StatusItemType::UIDNext: - m_uid_next = value; - break; - case StatusItemType::UIDValidity: - m_uid_validity = value; - break; - case StatusItemType::Unseen: - m_unseen = value; - break; - case StatusItemType::Messages: - m_uid_next = value; - break; - } - } - -private: - unsigned m_status_items { 0 }; - unsigned m_messages { 0 }; - unsigned m_recent { 0 }; - unsigned m_uid_next { 0 }; - unsigned m_uid_validity { 0 }; - unsigned m_unseen { 0 }; - ByteString m_mailbox; -}; - -struct Address { - ByteString name; - ByteString source_route; - ByteString mailbox; - ByteString host; -}; - -struct Envelope { - ByteString date; // Format of date not specified. - ByteString subject; - Vector
from; - Vector
sender; - Vector
reply_to; - Vector
to; - Vector
cc; - Vector
bcc; - ByteString in_reply_to; - ByteString message_id; -}; - -class BodyStructure; - -struct BodyExtension { - AK::Variant, unsigned, Vector>> data; -}; - -struct MultiPartBodyStructureData { - Optional>> disposition; - Vector> bodies; - Vector langs; - ByteString multipart_subtype; - HashMap params; - ByteString location; - Vector extensions; -}; - -struct BodyStructureData { - ByteString type; - ByteString subtype; - ByteString id {}; - ByteString desc {}; - ByteString encoding; - HashMap fields; - unsigned bytes { 0 }; - unsigned lines { 0 }; - Optional>> contanied_message; - - ByteString md5 {}; - Optional>> disposition {}; - Optional> langs {}; - ByteString location {}; - - Vector extensions {}; -}; - -class BodyStructure { - friend Parser; - -public: - explicit BodyStructure(BodyStructureData&& data) - : m_data(move(data)) - { - } - - explicit BodyStructure(MultiPartBodyStructureData&& data) - : m_data(move(data)) - { - } - - AK::Variant const& data() const { return m_data; } - -private: - AK::Variant m_data; -}; - -// Set -1 for '*' i.e highest possible value. -struct Sequence { - int start; - int end; - - [[nodiscard]] ByteString serialize() const; -}; - -struct FetchCommand { - enum class DataItemType { - BodyStructure, - Envelope, - Flags, - InternalDate, - UID, - PeekBody, - BodySection - }; - - struct DataItem { - enum class SectionType { - Header, - HeaderFields, - HeaderFieldsNot, - Text, - Parts - }; - struct Section { - SectionType type; - - Optional> parts {}; - bool ends_with_mime {}; - - Optional> headers {}; - - [[nodiscard]] ByteString serialize() const; - }; - - DataItemType type; - - Optional
section {}; - bool partial_fetch { false }; - int start { 0 }; - int octets { 0 }; - - [[nodiscard]] ByteString serialize() const; - }; - - Vector sequence_set; - Vector data_items; - - ByteString serialize(); -}; -struct Command { -public: - CommandType type; - int tag; - Vector args; -}; - -enum class ResponseStatus { - Bad, - No, - OK, -}; - -struct ListItem { - unsigned flags; - ByteString reference; - ByteString name; -}; - -class FetchResponseData { -public: - [[nodiscard]] unsigned response_type() const - { - return m_response_type; - } - - [[nodiscard]] bool contains_response_type(FetchResponseType response_type) const - { - return (static_cast(response_type) & m_response_type) != 0; - } - - void add_response_type(FetchResponseType type) - { - m_response_type |= static_cast(type); - } - - void add_body_data(FetchCommand::DataItem&& data_item, ByteString&& body) - { - add_response_type(FetchResponseType::Body); - m_bodies.append({ move(data_item), move(body) }); - } - - Vector>& body_data() - { - VERIFY(contains_response_type(FetchResponseType::Body)); - return m_bodies; - } - - void set_uid(unsigned uid) - { - add_response_type(FetchResponseType::UID); - m_uid = uid; - } - - [[nodiscard]] unsigned uid() const - { - VERIFY(contains_response_type(FetchResponseType::UID)); - return m_uid; - } - - void set_internal_date(Core::DateTime time) - { - add_response_type(FetchResponseType::InternalDate); - m_internal_date = time; - } - - Core::DateTime& internal_date() - { - VERIFY(contains_response_type(FetchResponseType::InternalDate)); - return m_internal_date; - } - - void set_envelope(Envelope&& envelope) - { - add_response_type(FetchResponseType::Envelope); - m_envelope = move(envelope); - } - - Envelope& envelope() - { - VERIFY(contains_response_type(FetchResponseType::Envelope)); - return m_envelope; - } - - void set_flags(Vector&& flags) - { - add_response_type(FetchResponseType::Flags); - m_flags = move(flags); - } - - Vector& flags() - { - VERIFY(contains_response_type(FetchResponseType::Flags)); - return m_flags; - } - - void set_body_structure(BodyStructure&& structure) - { - add_response_type(FetchResponseType::BodyStructure); - m_body_structure = move(structure); - } - - BodyStructure& body_structure() - { - VERIFY(contains_response_type(FetchResponseType::BodyStructure)); - return m_body_structure; - } - - FetchResponseData() - : m_body_structure(BodyStructureData {}) - { - } - -private: - Vector m_flags; - Vector> m_bodies; - Core::DateTime m_internal_date; - Envelope m_envelope; - unsigned m_uid { 0 }; - unsigned m_response_type { 0 }; - BodyStructure m_body_structure; -}; - -ByteString serialize_astring(StringView string); - -struct SearchKey { -public: - // clang-format off - struct All { }; - struct Answered { }; - struct Bcc { ByteString bcc; }; - struct Cc { ByteString cc; }; - struct Deleted { }; - struct Draft { }; - struct From { ByteString from; }; - struct Header { ByteString header; ByteString value; }; - struct Keyword { ByteString keyword; }; - struct Larger { unsigned number; }; - struct New { }; - struct Not { OwnPtr operand; }; - struct Old { }; - struct On { Core::DateTime date; }; - struct Or { OwnPtr lhs; OwnPtr rhs; }; - struct Recent { }; - struct SearchKeys { Vector> keys; }; - struct Seen { }; - struct SentBefore { Core::DateTime date; }; - struct SentOn { Core::DateTime date; }; - struct SentSince { Core::DateTime date; }; - struct SequenceSet { Sequence sequence; }; - struct Since { Core::DateTime date; }; - struct Smaller { unsigned number; }; - struct Subject { ByteString subject; }; - struct Text { ByteString text; }; - struct To { ByteString to; }; - struct UID { unsigned uid; }; - struct Unanswered { }; - struct Undeleted { }; - struct Undraft { }; - struct Unkeyword { ByteString flag_keyword; }; - struct Unseen { }; - // clang-format on - - Variant - data; - - SearchKey(SearchKey&& other) noexcept - : data(move(other.data)) - { - } - - template - explicit SearchKey(T&& t) - : data(forward(t)) - { - } - - SearchKey& operator=(SearchKey&& other) noexcept - { - if (this == &other) { - return *this; - } - - this->data = move(other.data); - - return *this; - } - - [[nodiscard]] ByteString serialize() const; -}; - -class ResponseData { -public: - [[nodiscard]] unsigned response_type() const - { - return m_response_type; - } - - ResponseData() - : m_response_type(0) - { - } - - ResponseData(ResponseData&) = delete; - ResponseData(ResponseData&&) = default; - ResponseData& operator=(ResponseData const&) = delete; - ResponseData& operator=(ResponseData&&) = default; - - [[nodiscard]] bool contains_response_type(ResponseType response_type) const - { - return (static_cast(response_type) & m_response_type) != 0; - } - - void add_response_type(ResponseType response_type) - { - m_response_type = m_response_type | static_cast(response_type); - } - - void add_capabilities(Vector&& capabilities) - { - m_capabilities = move(capabilities); - add_response_type(ResponseType::Capability); - } - - Vector& capabilities() - { - VERIFY(contains_response_type(ResponseType::Capability)); - return m_capabilities; - } - - void add_list_item(ListItem&& item) - { - add_response_type(ResponseType::List); - m_list_items.append(move(item)); - } - - Vector& list_items() - { - VERIFY(contains_response_type(ResponseType::List)); - return m_list_items; - } - - void add_lsub_item(ListItem&& item) - { - add_response_type(ResponseType::List); - m_lsub_items.append(move(item)); - } - - Vector& lsub_items() - { - VERIFY(contains_response_type(ResponseType::ListSub)); - return m_lsub_items; - } - - void set_exists(unsigned exists) - { - add_response_type(ResponseType::Exists); - m_exists = exists; - } - - [[nodiscard]] unsigned exists() const - { - VERIFY(contains_response_type(ResponseType::Exists)); - return m_exists; - } - - void set_recent(unsigned recent) - { - add_response_type(ResponseType::Recent); - m_recent = recent; - } - - [[nodiscard]] unsigned recent() const - { - VERIFY(contains_response_type(ResponseType::Recent)); - return m_recent; - } - - void set_uid_next(unsigned uid_next) - { - add_response_type(ResponseType::UIDNext); - m_uid_next = uid_next; - } - - [[nodiscard]] unsigned uid_next() const - { - VERIFY(contains_response_type(ResponseType::UIDNext)); - return m_uid_next; - } - - void set_uid_validity(unsigned uid_validity) - { - add_response_type(ResponseType::UIDValidity); - m_uid_validity = uid_validity; - } - - [[nodiscard]] unsigned uid_validity() const - { - VERIFY(contains_response_type(ResponseType::UIDValidity)); - return m_uid_validity; - } - - void set_unseen(unsigned unseen) - { - add_response_type(ResponseType::Unseen); - m_unseen = unseen; - } - - [[nodiscard]] unsigned unseen() const - { - VERIFY(contains_response_type(ResponseType::Unseen)); - return m_unseen; - } - - void set_flags(Vector&& flags) - { - m_response_type |= static_cast(ResponseType::Flags); - m_flags = move(flags); - } - - Vector& flags() - { - VERIFY(contains_response_type(ResponseType::Flags)); - return m_flags; - } - - void set_permanent_flags(Vector&& flags) - { - add_response_type(ResponseType::PermanentFlags); - m_permanent_flags = move(flags); - } - - Vector& permanent_flags() - { - VERIFY(contains_response_type(ResponseType::PermanentFlags)); - return m_permanent_flags; - } - - void add_fetch_response(unsigned message, FetchResponseData&& data) - { - add_response_type(ResponseType::Fetch); - m_fetch_responses.append(Tuple { move(message), move(data) }); - } - - Vector>& fetch_data() - { - VERIFY(contains_response_type(ResponseType::Fetch)); - return m_fetch_responses; - } - - void set_search_results(Vector&& results) - { - add_response_type(ResponseType::Search); - m_search_results = move(results); - } - - Vector& search_results() - { - VERIFY(contains_response_type(ResponseType::Search)); - return m_search_results; - } - - void add_expunged(unsigned message) - { - add_response_type(ResponseType::Expunged); - m_expunged.append(message); - } - - Vector& expunged() - { - VERIFY(contains_response_type(ResponseType::Expunged)); - return m_expunged; - } - - void set_bye(Optional message) - { - add_response_type(ResponseType::Bye); - m_bye_message = move(message); - } - - Optional& bye_message() - { - VERIFY(contains_response_type(ResponseType::Bye)); - return m_bye_message; - } - - void set_status(StatusItem&& status_item) - { - add_response_type(ResponseType::Status); - m_status_item = move(status_item); - } - - StatusItem& status_item() - { - return m_status_item; - } - -private: - unsigned m_response_type; - - Vector m_capabilities; - Vector m_list_items; - Vector m_lsub_items; - Vector m_expunged; - - unsigned m_recent {}; - unsigned m_exists {}; - - unsigned m_uid_next {}; - unsigned m_uid_validity {}; - unsigned m_unseen {}; - Vector m_permanent_flags; - Vector m_flags; - Vector> m_fetch_responses; - Vector m_search_results; - Optional m_bye_message; - StatusItem m_status_item; -}; - -enum class StoreMethod { - Replace, - Add, - Remove -}; - -class SolidResponse { - // Parser is allowed to set up fields - friend class Parser; - -public: - ResponseStatus status() { return m_status; } - - int tag() const { return m_tag; } - - ResponseData& data() { return m_data; } - - ByteString response_text() { return m_response_text; } - - SolidResponse() - : SolidResponse(ResponseStatus::Bad, -1) - { - } - - SolidResponse(ResponseStatus status, int tag) - : m_status(status) - , m_tag(tag) - , m_data(ResponseData()) - { - } - -private: - ResponseStatus m_status; - ByteString m_response_text; - unsigned m_tag; - - ResponseData m_data; -}; - -struct ContinueRequest { - ByteString data; -}; - -using Response = Variant; -} - -// An RFC 2822 message -// https://datatracker.ietf.org/doc/html/rfc2822 -struct Message { - ByteString data; -}; diff --git a/Userland/Libraries/LibIMAP/Parser.cpp b/Userland/Libraries/LibIMAP/Parser.cpp deleted file mode 100644 index 66bd7f34a49..00000000000 --- a/Userland/Libraries/LibIMAP/Parser.cpp +++ /dev/null @@ -1,866 +0,0 @@ -/* - * Copyright (c) 2021, Kyle Pereira - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include - -namespace IMAP { - -ParseStatus Parser::parse(ByteBuffer&& buffer, bool expecting_tag) -{ - auto response_or_error = try_parse(move(buffer), expecting_tag); - if (response_or_error.is_error()) - return { false, {} }; - - auto response = response_or_error.release_value(); - return response; -} - -ErrorOr Parser::try_parse(ByteBuffer&& buffer, bool expecting_tag) -{ - dbgln_if(IMAP_PARSER_DEBUG, "Parser received {} bytes:\n\"{}\"", buffer.size(), StringView(buffer.data(), buffer.size())); - if (m_incomplete) { - m_buffer += buffer; - m_incomplete = false; - } else { - m_buffer = move(buffer); - m_position = 0; - m_response = SolidResponse(); - } - - if (consume_if("+"sv)) { - TRY(consume(" "sv)); - auto data = consume_until_end_of_line(); - TRY(consume(" "sv)); - return ParseStatus { true, { ContinueRequest { data } } }; - } - - while (consume_if("*"sv)) { - TRY(parse_untagged()); - } - - if (expecting_tag) { - if (at_end()) { - m_incomplete = true; - return ParseStatus { true, {} }; - } - TRY(parse_response_done()); - } - - return ParseStatus { true, { move(m_response) } }; -} - -bool Parser::consume_if(StringView x) -{ - dbgln_if(IMAP_PARSER_DEBUG, "p: {}, consume({})", m_position, x); - size_t i = 0; - auto previous_position = m_position; - while (i < x.length() && !at_end() && to_ascii_lowercase(x[i]) == to_ascii_lowercase(m_buffer[m_position])) { - i++; - m_position++; - } - if (i != x.length()) { - // We didn't match the full string. - m_position = previous_position; - dbgln_if(IMAP_PARSER_DEBUG, "ret false"); - return false; - } - - dbgln_if(IMAP_PARSER_DEBUG, "ret true"); - return true; -} - -ErrorOr Parser::parse_response_done() -{ - TRY(consume("A"sv)); - auto tag = TRY(parse_number()); - TRY(consume(" "sv)); - - ResponseStatus status = TRY(parse_status()); - TRY(consume(" "sv)); - - m_response.m_tag = tag; - m_response.m_status = status; - - StringBuilder response_data; - - while (!at_end() && m_buffer[m_position] != '\r') { - response_data.append((char)m_buffer[m_position]); - m_position += 1; - } - - TRY(consume("\r\n"sv)); - m_response.m_response_text = response_data.to_byte_string(); - return {}; -} - -ErrorOr Parser::consume(StringView x) -{ - if (!consume_if(x)) { - dbgln("\"{}\" not matched at {}, (buffer length {})", x, m_position, m_buffer.size()); - return Error::from_string_literal("Token not matched"); - } - - return {}; -} - -Optional Parser::try_parse_number() -{ - dbgln_if(IMAP_PARSER_DEBUG, "p: {}, try_parse_number()", m_position); - auto number_matched = 0; - while (!at_end() && 0 <= m_buffer[m_position] - '0' && m_buffer[m_position] - '0' <= 9) { - number_matched++; - m_position++; - } - if (number_matched == 0) { - dbgln_if(IMAP_PARSER_DEBUG, "p: {}, ret empty", m_position); - return {}; - } - - auto number = StringView(m_buffer.data() + m_position - number_matched, number_matched); - - dbgln_if(IMAP_PARSER_DEBUG, "p: {}, ret \"{}\"", m_position, number.to_number()); - return number.to_number(); -} - -ErrorOr Parser::parse_number() -{ - auto number = try_parse_number(); - if (!number.has_value()) { - dbgln("Failed to parse number at {}, (buffer length {})", m_position, m_buffer.size()); - return Error::from_string_view("Failed to parse expected number"sv); - } - - return number.value(); -} - -ErrorOr Parser::parse_untagged() -{ - TRY(consume(" "sv)); - - // Certain messages begin with a number like: - // * 15 EXISTS - auto number = try_parse_number(); - if (number.has_value()) { - TRY(consume(" "sv)); - auto data_type = TRY(parse_atom()); - if (data_type == "EXISTS"sv) { - m_response.data().set_exists(number.value()); - TRY(consume("\r\n"sv)); - } else if (data_type == "RECENT"sv) { - m_response.data().set_recent(number.value()); - TRY(consume("\r\n"sv)); - } else if (data_type == "FETCH"sv) { - auto fetch_response = TRY(parse_fetch_response()); - m_response.data().add_fetch_response(number.value(), move(fetch_response)); - } else if (data_type == "EXPUNGE"sv) { - m_response.data().add_expunged(number.value()); - TRY(consume("\r\n"sv)); - } - return {}; - } - - if (consume_if("CAPABILITY"sv)) { - TRY(parse_capability_response()); - } else if (consume_if("LIST"sv)) { - auto item = TRY(parse_list_item()); - m_response.data().add_list_item(move(item)); - } else if (consume_if("LSUB"sv)) { - auto item = TRY(parse_list_item()); - m_response.data().add_lsub_item(move(item)); - } else if (consume_if("FLAGS"sv)) { - TRY(consume(" "sv)); - auto flags = TRY(parse_list(+[](StringView x) { return ByteString(x); })); - m_response.data().set_flags(move(flags)); - TRY(consume("\r\n"sv)); - } else if (consume_if("OK"sv)) { - TRY(consume(" "sv)); - if (consume_if("["sv)) { - auto actual_type = TRY(parse_atom()); - if (actual_type == "CLOSED"sv) { - // No-op. - } else if (actual_type == "UIDNEXT"sv) { - TRY(consume(" "sv)); - auto n = TRY(parse_number()); - m_response.data().set_uid_next(n); - } else if (actual_type == "UIDVALIDITY"sv) { - TRY(consume(" "sv)); - auto n = TRY(parse_number()); - m_response.data().set_uid_validity(n); - } else if (actual_type == "UNSEEN"sv) { - TRY(consume(" "sv)); - auto n = TRY(parse_number()); - m_response.data().set_unseen(n); - } else if (actual_type == "PERMANENTFLAGS"sv) { - TRY(consume(" "sv)); - auto flags = TRY(parse_list(+[](StringView x) { return ByteString(x); })); - m_response.data().set_permanent_flags(move(flags)); - } else if (actual_type == "HIGHESTMODSEQ"sv) { - TRY(consume(" "sv)); - TRY(parse_number()); - // No-op for now. - } else { - dbgln("Unknown: {}", actual_type); - consume_while([](u8 x) { return x != ']'; }); - } - TRY(consume("]"sv)); - } - consume_until_end_of_line(); - TRY(consume("\r\n"sv)); - } else if (consume_if("SEARCH"sv)) { - Vector ids; - while (!consume_if("\r\n"sv)) { - TRY(consume(" "sv)); - auto id = TRY(parse_number()); - ids.append(id); - } - m_response.data().set_search_results(move(ids)); - } else if (consume_if("BYE"sv)) { - auto message = consume_until_end_of_line(); - TRY(consume("\r\n"sv)); - m_response.data().set_bye(message.is_empty() ? Optional() : Optional(message)); - } else if (consume_if("STATUS"sv)) { - TRY(consume(" "sv)); - auto mailbox = TRY(parse_astring()); - TRY(consume(" ("sv)); - auto status_item = StatusItem(); - status_item.set_mailbox(mailbox); - while (!consume_if(")"sv)) { - auto status_att = TRY(parse_atom()); - TRY(consume(" "sv)); - auto value = TRY(parse_number()); - - auto type = StatusItemType::Recent; - if (status_att == "MESSAGES"sv) { - type = StatusItemType::Messages; - } else if (status_att == "UNSEEN"sv) { - type = StatusItemType::Unseen; - } else if (status_att == "UIDNEXT"sv) { - type = StatusItemType::UIDNext; - } else if (status_att == "UIDVALIDITY"sv) { - type = StatusItemType::UIDValidity; - } else if (status_att == "RECENT"sv) { - type = StatusItemType::Recent; - } else { - dbgln("Unmatched status attribute: {}", status_att); - return Error::from_string_literal("Failed to parse status attribute"); - } - - status_item.set(type, value); - - if (!at_end() && m_buffer[m_position] != ')') - TRY(consume(" "sv)); - } - m_response.data().set_status(move(status_item)); - consume_if(" "sv); // Not in the spec but the Outlook server sends a space for some reason. - TRY(consume("\r\n"sv)); - } else { - auto x = consume_until_end_of_line(); - TRY(consume("\r\n"sv)); - dbgln("ignored {}", x); - } - - return {}; -} - -ErrorOr Parser::parse_quoted_string() -{ - dbgln_if(IMAP_PARSER_DEBUG, "p: {}, parse_quoted_string()", m_position); - auto str = consume_while([](u8 x) { return x != '"'; }); - TRY(consume("\""sv)); - dbgln_if(IMAP_PARSER_DEBUG, "p: {}, ret \"{}\"", m_position, str); - return str; -} - -ErrorOr Parser::parse_string() -{ - if (consume_if("\""sv)) - return parse_quoted_string(); - - return parse_literal_string(); -} - -ErrorOr Parser::parse_nstring() -{ - dbgln_if(IMAP_PARSER_DEBUG, "p: {} parse_nstring()", m_position); - if (consume_if("NIL"sv)) - return StringView {}; - - return { TRY(parse_string()) }; -} - -ErrorOr Parser::parse_fetch_response() -{ - TRY(consume(" ("sv)); - auto fetch_response = FetchResponseData(); - - while (!consume_if(")"sv)) { - auto data_item = TRY(parse_fetch_data_item()); - switch (data_item.type) { - case FetchCommand::DataItemType::BodyStructure: { - TRY(consume(" ("sv)); - auto structure = TRY(parse_body_structure()); - fetch_response.set_body_structure(move(structure)); - break; - } - case FetchCommand::DataItemType::Envelope: { - TRY(consume(" "sv)); - fetch_response.set_envelope(TRY(parse_envelope())); - break; - } - case FetchCommand::DataItemType::Flags: { - TRY(consume(" "sv)); - auto flags = TRY(parse_list(+[](StringView x) { return ByteString(x); })); - fetch_response.set_flags(move(flags)); - break; - } - case FetchCommand::DataItemType::InternalDate: { - TRY(consume(" \""sv)); - auto date_view = consume_while([](u8 x) { return x != '"'; }); - TRY(consume("\""sv)); - auto date = Core::DateTime::parse("%d-%b-%Y %H:%M:%S %z"sv, date_view).value(); - fetch_response.set_internal_date(date); - break; - } - case FetchCommand::DataItemType::UID: { - TRY(consume(" "sv)); - fetch_response.set_uid(TRY(parse_number())); - break; - } - case FetchCommand::DataItemType::PeekBody: - // Spec doesn't allow for this in a response. - return Error::from_string_literal("Unexpected fetch command type"); - case FetchCommand::DataItemType::BodySection: { - auto body = TRY(parse_nstring()); - fetch_response.add_body_data(move(data_item), body); - break; - } - } - if (!at_end() && m_buffer[m_position] != ')') - TRY(consume(" "sv)); - } - TRY(consume("\r\n"sv)); - return fetch_response; -} - -ErrorOr Parser::parse_envelope() -{ - TRY(consume("("sv)); - auto date = TRY(parse_nstring()); - TRY(consume(" "sv)); - auto subject = TRY(parse_nstring()); - TRY(consume(" "sv)); - auto from = TRY(parse_address_list()); - TRY(consume(" "sv)); - auto sender = TRY(parse_address_list()); - TRY(consume(" "sv)); - auto reply_to = TRY(parse_address_list()); - TRY(consume(" "sv)); - auto to = TRY(parse_address_list()); - TRY(consume(" "sv)); - auto cc = TRY(parse_address_list()); - TRY(consume(" "sv)); - auto bcc = TRY(parse_address_list()); - TRY(consume(" "sv)); - auto in_reply_to = TRY(parse_nstring()); - TRY(consume(" "sv)); - auto message_id = TRY(parse_nstring()); - TRY(consume(")"sv)); - Envelope envelope = { - date, - subject, - from, - sender, - reply_to, - to, - cc, - bcc, - in_reply_to, - message_id - }; - return envelope; -} - -ErrorOr Parser::parse_body_structure() -{ - if (!at_end() && m_buffer[m_position] == '(') { - auto data = MultiPartBodyStructureData(); - while (consume_if("("sv)) { - auto child = TRY(parse_body_structure()); - data.bodies.append(make(move(child))); - } - TRY(consume(" "sv)); - data.multipart_subtype = TRY(parse_string()); - - if (!consume_if(")"sv)) { - TRY(consume(" "sv)); - data.params = consume_if("NIL"sv) ? HashMap {} : TRY(parse_body_fields_params()); - if (!consume_if(")"sv)) { - TRY(consume(" "sv)); - if (!consume_if("NIL"sv)) { - data.disposition = { TRY(parse_disposition()) }; - } - - if (!consume_if(")"sv)) { - TRY(consume(" "sv)); - if (!consume_if("NIL"sv)) { - data.langs = { TRY(parse_langs()) }; - } - - if (!consume_if(")"sv)) { - TRY(consume(" "sv)); - data.location = consume_if("NIL"sv) ? ByteString {} : ByteString(TRY(parse_string())); - - if (!consume_if(")"sv)) { - TRY(consume(" "sv)); - Vector extensions; - while (!consume_if(")"sv)) { - extensions.append(TRY(parse_body_extension())); - consume_if(" "sv); - } - data.extensions = { move(extensions) }; - } - } - } - } - } - - return BodyStructure(move(data)); - } - - return parse_one_part_body(); -} - -// body-type-1part -ErrorOr Parser::parse_one_part_body() -{ - // NOTE: We share common parts between body-type-basic, body-type-msg and body-type-text types for readability. - BodyStructureData data; - - // media-basic / media-message / media-text - data.type = TRY(parse_string()); - TRY(consume(" "sv)); - data.subtype = TRY(parse_string()); - TRY(consume(" "sv)); - - // body-fields - data.fields = TRY(parse_body_fields_params()); - TRY(consume(" "sv)); - data.id = TRY(parse_nstring()); - TRY(consume(" "sv)); - data.desc = TRY(parse_nstring()); - TRY(consume(" "sv)); - data.encoding = TRY(parse_string()); - TRY(consume(" "sv)); - data.bytes = TRY(parse_number()); - - if (data.type.equals_ignoring_ascii_case("TEXT"sv)) { - // body-type-text - // NOTE: "media-text SP body-fields" part is already parsed. - TRY(consume(" "sv)); - data.lines = TRY(parse_number()); - } else if (data.type.equals_ignoring_ascii_case("MESSAGE"sv) && data.subtype.is_one_of_ignoring_ascii_case("RFC822"sv, "GLOBAL"sv)) { - // body-type-msg - // NOTE: "media-message SP body-fields" part is already parsed. - TRY(consume(" "sv)); - auto envelope = TRY(parse_envelope()); - - TRY(consume(" ("sv)); - auto body = TRY(parse_body_structure()); - data.contanied_message = Tuple { move(envelope), make(move(body)) }; - - TRY(consume(" "sv)); - data.lines = TRY(parse_number()); - } else { - // body-type-basic - // NOTE: "media-basic SP body-fields" is already parsed. - } - - if (!consume_if(")"sv)) { - TRY(consume(" "sv)); - - // body-ext-1part - TRY([&]() -> ErrorOr { - data.md5 = TRY(parse_nstring()); - - if (consume_if(")"sv)) - return {}; - TRY(consume(" "sv)); - if (!consume_if("NIL"sv)) { - data.disposition = { TRY(parse_disposition()) }; - } - - if (consume_if(")"sv)) - return {}; - TRY(consume(" "sv)); - if (!consume_if("NIL"sv)) { - data.langs = { TRY(parse_langs()) }; - } - - if (consume_if(")"sv)) - return {}; - TRY(consume(" "sv)); - data.location = TRY(parse_nstring()); - - Vector extensions; - while (!consume_if(")"sv)) { - extensions.append(TRY(parse_body_extension())); - consume_if(" "sv); - } - data.extensions = { move(extensions) }; - return {}; - }()); - } - - return BodyStructure(move(data)); -} - -ErrorOr> Parser::parse_langs() -{ - AK::Vector langs; - if (!consume_if("("sv)) { - langs.append(TRY(parse_string())); - } else { - while (!consume_if(")"sv)) { - langs.append(TRY(parse_string())); - consume_if(" "sv); - } - } - return langs; -} - -ErrorOr>> Parser::parse_disposition() -{ - TRY(consume("("sv)); - auto disposition_type = TRY(parse_string()); - TRY(consume(" "sv)); - auto disposition_vals = TRY(parse_body_fields_params()); - TRY(consume(")"sv)); - return Tuple> { move(disposition_type), move(disposition_vals) }; -} - -ErrorOr Parser::parse_literal_string() -{ - dbgln_if(IMAP_PARSER_DEBUG, "p: {}, parse_literal_string()", m_position); - TRY(consume("{"sv)); - auto num_bytes = TRY(parse_number()); - TRY(consume("}\r\n"sv)); - - if (m_buffer.size() < m_position + num_bytes) { - dbgln("Attempted to parse string with length: {} at position {} (buffer length {})", num_bytes, m_position, m_position); - return Error::from_string_literal("Failed to parse string"); - } - - m_position += num_bytes; - auto s = StringView(m_buffer.data() + m_position - num_bytes, num_bytes); - dbgln_if(IMAP_PARSER_DEBUG, "p: {}, ret \"{}\"", m_position, s); - return s; -} - -ErrorOr Parser::parse_list_item() -{ - TRY(consume(" "sv)); - auto flags_vec = TRY(parse_list(parse_mailbox_flag)); - unsigned flags = 0; - for (auto flag : flags_vec) { - flags |= static_cast(flag); - } - TRY(consume(" \""sv)); - auto reference = consume_while([](u8 x) { return x != '"'; }); - TRY(consume("\" "sv)); - auto mailbox = TRY(parse_astring()); - TRY(consume("\r\n"sv)); - return ListItem { flags, ByteString(reference), ByteString(mailbox) }; -} - -ErrorOr Parser::parse_capability_response() -{ - auto capability = AK::Vector(); - while (!consume_if("\r\n"sv)) { - TRY(consume(" "sv)); - capability.append(TRY(parse_atom())); - } - m_response.data().add_capabilities(move(capability)); - return {}; -} - -ErrorOr Parser::parse_atom() -{ - dbgln_if(IMAP_PARSER_DEBUG, "p: {}, parse_atom()", m_position); - auto is_non_atom_char = [](u8 x) { - auto non_atom_chars = { '(', ')', '{', ' ', '%', '*', '"', '\\', ']' }; - return AK::find(non_atom_chars.begin(), non_atom_chars.end(), x) != non_atom_chars.end(); - }; - - auto start = m_position; - auto count = 0; - while (!at_end() && !is_ascii_control(m_buffer[m_position]) && !is_non_atom_char(m_buffer[m_position])) { - count++; - m_position++; - } - - if (count == 0) - return Error::from_string_literal("Invalid atom value"); - - StringView s = StringView(m_buffer.data() + start, count); - dbgln_if(IMAP_PARSER_DEBUG, "p: {}, ret \"{}\"", m_position, s); - return s; -} - -ErrorOr Parser::parse_status() -{ - auto atom = TRY(parse_atom()); - - if (atom == "OK"sv) { - return ResponseStatus::OK; - } else if (atom == "BAD"sv) { - return ResponseStatus::Bad; - } else if (atom == "NO"sv) { - return ResponseStatus::No; - } - - dbgln("Invalid ResponseStatus value: {}", atom); - return Error::from_string_literal("Failed to parse status type"); -} - -template -ErrorOr> Parser::parse_list(T converter(StringView)) -{ - TRY(consume("("sv)); - Vector x; - bool first = true; - while (!consume_if(")"sv)) { - if (!first) - TRY(consume(" "sv)); - auto item = consume_while([](u8 x) { - return x != ' ' && x != ')'; - }); - x.append(converter(item)); - first = false; - } - - return x; -} - -MailboxFlag Parser::parse_mailbox_flag(StringView s) -{ - if (s == "\\All"sv) - return MailboxFlag::All; - if (s == "\\Drafts"sv) - return MailboxFlag::Drafts; - if (s == "\\Flagged"sv) - return MailboxFlag::Flagged; - if (s == "\\HasChildren"sv) - return MailboxFlag::HasChildren; - if (s == "\\HasNoChildren"sv) - return MailboxFlag::HasNoChildren; - if (s == "\\Important"sv) - return MailboxFlag::Important; - if (s == "\\Junk"sv) - return MailboxFlag::Junk; - if (s == "\\Marked"sv) - return MailboxFlag::Marked; - if (s == "\\Noinferiors"sv) - return MailboxFlag::NoInferiors; - if (s == "\\Noselect"sv) - return MailboxFlag::NoSelect; - if (s == "\\Sent"sv) - return MailboxFlag::Sent; - if (s == "\\Trash"sv) - return MailboxFlag::Trash; - if (s == "\\Unmarked"sv) - return MailboxFlag::Unmarked; - - dbgln("Unrecognized mailbox flag {}", s); - return MailboxFlag::Unknown; -} - -StringView Parser::consume_while(Function should_consume) -{ - dbgln_if(IMAP_PARSER_DEBUG, "p: {}, consume_while()", m_position); - int chars = 0; - while (!at_end() && should_consume(m_buffer[m_position])) { - m_position++; - chars++; - } - auto s = StringView(m_buffer.data() + m_position - chars, chars); - - dbgln_if(IMAP_PARSER_DEBUG, "p: {}, ret \"{}\"", m_position, s); - return s; -} - -StringView Parser::consume_until_end_of_line() -{ - return consume_while([](u8 x) { return x != '\r'; }); -} - -ErrorOr Parser::parse_fetch_data_item() -{ - auto msg_attr = consume_while([](u8 x) { return is_ascii_alpha(x) != 0; }); - if (msg_attr.equals_ignoring_ascii_case("BODY"sv) && consume_if("["sv)) { - auto data_item = FetchCommand::DataItem { - .type = FetchCommand::DataItemType::BodySection, - .section = { {} } - }; - auto section_type = consume_while([](u8 x) { return x != ']' && x != ' '; }); - if (section_type.equals_ignoring_ascii_case("HEADER.FIELDS"sv)) { - data_item.section->type = FetchCommand::DataItem::SectionType::HeaderFields; - data_item.section->headers = Vector(); - TRY(consume(" "sv)); - auto headers = TRY(parse_list(+[](StringView x) { return x; })); - for (auto& header : headers) { - data_item.section->headers->append(header); - } - TRY(consume("]"sv)); - } else if (section_type.equals_ignoring_ascii_case("HEADER.FIELDS.NOT"sv)) { - data_item.section->type = FetchCommand::DataItem::SectionType::HeaderFieldsNot; - data_item.section->headers = Vector(); - TRY(consume(" ("sv)); - auto headers = TRY(parse_list(+[](StringView x) { return x; })); - for (auto& header : headers) { - data_item.section->headers->append(header); - } - TRY(consume("]"sv)); - } else if (is_ascii_digit(section_type[0])) { - data_item.section->type = FetchCommand::DataItem::SectionType::Parts; - data_item.section->parts = Vector(); - - while (!consume_if("]"sv)) { - auto num = try_parse_number(); - if (num.has_value()) { - data_item.section->parts->append(num.value()); - continue; - } - auto atom = TRY(parse_atom()); - if (atom.equals_ignoring_ascii_case("MIME"sv)) { - data_item.section->ends_with_mime = true; - continue; - } - } - } else if (section_type.equals_ignoring_ascii_case("TEXT"sv)) { - data_item.section->type = FetchCommand::DataItem::SectionType::Text; - } else if (section_type.equals_ignoring_ascii_case("HEADER"sv)) { - data_item.section->type = FetchCommand::DataItem::SectionType::Header; - } else { - dbgln("Unmatched section type {}", section_type); - return Error::from_string_literal("Failed to parse section type"); - } - if (consume_if("<"sv)) { - auto start = TRY(parse_number()); - data_item.partial_fetch = true; - data_item.start = (int)start; - TRY(consume(">"sv)); - } - consume_if(" "sv); - return data_item; - } else if (msg_attr.equals_ignoring_ascii_case("FLAGS"sv)) { - return FetchCommand::DataItem { - .type = FetchCommand::DataItemType::Flags - }; - } else if (msg_attr.equals_ignoring_ascii_case("UID"sv)) { - return FetchCommand::DataItem { - .type = FetchCommand::DataItemType::UID - }; - } else if (msg_attr.equals_ignoring_ascii_case("INTERNALDATE"sv)) { - return FetchCommand::DataItem { - .type = FetchCommand::DataItemType::InternalDate - }; - } else if (msg_attr.equals_ignoring_ascii_case("ENVELOPE"sv)) { - return FetchCommand::DataItem { - .type = FetchCommand::DataItemType::Envelope - }; - } else if (msg_attr.equals_ignoring_ascii_case("BODY"sv) || msg_attr.equals_ignoring_ascii_case("BODYSTRUCTURE"sv)) { - return FetchCommand::DataItem { - .type = FetchCommand::DataItemType::BodyStructure - }; - } else { - dbgln("msg_attr not matched: {}", msg_attr); - return Error::from_string_literal("Failed to parse msg_attr"); - } -} - -ErrorOr> Parser::parse_address_list() -{ - if (consume_if("NIL"sv)) - return Vector
{}; - - auto addresses = Vector
(); - TRY(consume("("sv)); - while (!consume_if(")"sv)) { - addresses.append(TRY(parse_address())); - if (!at_end() && m_buffer[m_position] != ')') - TRY(consume(" "sv)); - } - return { addresses }; -} - -ErrorOr
Parser::parse_address() -{ - TRY(consume("("sv)); - auto address = Address(); - auto name = TRY(parse_nstring()); - address.name = name; - TRY(consume(" "sv)); - auto source_route = TRY(parse_nstring()); - address.source_route = source_route; - TRY(consume(" "sv)); - auto mailbox = TRY(parse_nstring()); - address.mailbox = mailbox; - TRY(consume(" "sv)); - auto host = TRY(parse_nstring()); - address.host = host; - TRY(consume(")"sv)); - return address; -} - -ErrorOr Parser::parse_astring() -{ - if (!at_end() && (m_buffer[m_position] == '{' || m_buffer[m_position] == '"')) - return parse_string(); - - return parse_atom(); -} - -ErrorOr> Parser::parse_body_fields_params() -{ - if (consume_if("NIL"sv)) - return HashMap {}; - - HashMap fields; - TRY(consume("("sv)); - while (!consume_if(")"sv)) { - auto key = TRY(parse_string()); - TRY(consume(" "sv)); - auto value = TRY(parse_string()); - fields.set(key, value); - consume_if(" "sv); - } - - return fields; -} - -ErrorOr Parser::parse_body_extension() -{ - if (consume_if("NIL"sv)) - return BodyExtension { Optional {} }; - - if (consume_if("("sv)) { - Vector> extensions; - while (!consume_if(")"sv)) { - extensions.append(make(TRY(parse_body_extension()))); - consume_if(" "sv); - } - return BodyExtension { move(extensions) }; - } - - if (!at_end() && (m_buffer[m_position] == '"' || m_buffer[m_position] == '{')) - return BodyExtension { { TRY(parse_string()) } }; - - return BodyExtension { TRY(parse_number()) }; -} - -} diff --git a/Userland/Libraries/LibIMAP/Parser.h b/Userland/Libraries/LibIMAP/Parser.h deleted file mode 100644 index d3ea539c057..00000000000 --- a/Userland/Libraries/LibIMAP/Parser.h +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Copyright (c) 2021, Kyle Pereira - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include - -namespace IMAP { -class Client; - -struct ParseStatus { - bool successful; - Optional response; -}; - -class Parser { -public: - ParseStatus parse(ByteBuffer&& buffer, bool expecting_tag); - -private: - static MailboxFlag parse_mailbox_flag(StringView); - - ErrorOr try_parse(ByteBuffer&& buffer, bool expecting_tag); - - ErrorOr consume(StringView); - bool consume_if(StringView); - StringView consume_while(Function should_consume); - StringView consume_until_end_of_line(); - - bool at_end() { return m_position >= m_buffer.size(); } - - ErrorOr parse_number(); - Optional try_parse_number(); - - ErrorOr parse_response_done(); - ErrorOr parse_untagged(); - ErrorOr parse_capability_response(); - - ErrorOr parse_atom(); - ErrorOr parse_quoted_string(); - ErrorOr parse_literal_string(); - ErrorOr parse_string(); - ErrorOr parse_astring(); - ErrorOr parse_nstring(); - - ErrorOr parse_status(); - ErrorOr parse_list_item(); - ErrorOr parse_fetch_data_item(); - ErrorOr parse_fetch_response(); - ErrorOr> parse_address_list(); - ErrorOr
parse_address(); - ErrorOr> parse_body_fields_params(); - ErrorOr parse_body_structure(); - ErrorOr parse_one_part_body(); - ErrorOr parse_body_extension(); - ErrorOr>> parse_disposition(); - ErrorOr> parse_langs(); - ErrorOr parse_envelope(); - - template - ErrorOr> parse_list(T (*converter)(StringView)); - - // To retain state if parsing is not finished - ByteBuffer m_buffer; - SolidResponse m_response; - unsigned m_position { 0 }; - bool m_incomplete { false }; -}; -} diff --git a/Userland/Libraries/LibIMAP/QuotedPrintable.cpp b/Userland/Libraries/LibIMAP/QuotedPrintable.cpp deleted file mode 100644 index 27a3fc7cdf3..00000000000 --- a/Userland/Libraries/LibIMAP/QuotedPrintable.cpp +++ /dev/null @@ -1,98 +0,0 @@ -/* - * Copyright (c) 2021, Luke Wilde - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include - -namespace IMAP { - -static constexpr bool is_illegal_character(char c) -{ - return (u8)c > 0x7E || (is_ascii_control(c) && c != '\t' && c != '\r' && c != '\n'); -} - -// RFC 2045 Section 6.7 "Quoted-Printable Content-Transfer-Encoding", https://datatracker.ietf.org/doc/html/rfc2045#section-6.7 -ErrorOr decode_quoted_printable(StringView input) -{ - GenericLexer lexer(input); - StringBuilder output; - - // For any invalid escape sequence. RFC 2045 says a reasonable solution is just to append '=' followed by the unaltered escape characters. - auto append_invalid_escape_sequence = [&](Optional first = {}, Optional second = {}) -> ErrorOr { - TRY(output.try_append('=')); - if (first.has_value() && !is_illegal_character(first.value())) - TRY(output.try_append(first.value())); - - if (second.has_value() && !is_illegal_character(second.value())) - TRY(output.try_append(second.value())); - - return {}; - }; - - // NOTE: The RFC says that encoded lines must not be longer than 76 characters. - // However, the RFC says implementations can ignore this and parse as is, - // which is the approach we're taking. - - while (!lexer.is_eof()) { - char potential_character = lexer.consume(); - - if (is_illegal_character(potential_character)) - continue; - - if (potential_character == '=') { - if (lexer.is_eof()) { - TRY(append_invalid_escape_sequence()); - continue; - } - - char first_escape_character = lexer.consume(); - - // The RFC doesn't formally allow lowercase, but says implementations can treat lowercase the same as uppercase. - // Thus we can use is_ascii_hex_digit. - if (is_ascii_hex_digit(first_escape_character)) { - if (lexer.is_eof()) { - TRY(append_invalid_escape_sequence(first_escape_character)); - continue; - } - - char second_escape_character = lexer.consume(); - - if (is_ascii_hex_digit(second_escape_character)) { - u8 actual_character = (parse_ascii_hex_digit(first_escape_character) << 4) | parse_ascii_hex_digit(second_escape_character); - TRY(output.try_append(actual_character)); - } else { - TRY(append_invalid_escape_sequence(first_escape_character, second_escape_character)); - continue; - } - } else if (first_escape_character == '\r') { - if (lexer.is_eof()) { - TRY(append_invalid_escape_sequence(first_escape_character)); - continue; - } - - char second_escape_character = lexer.consume(); - - if (second_escape_character == '\n') { - // This is a soft line break. Don't append anything to the output. - } else { - TRY(append_invalid_escape_sequence(first_escape_character, second_escape_character)); - continue; - } - } else { - TRY(append_invalid_escape_sequence(first_escape_character)); - continue; - } - } else { - TRY(output.try_append(potential_character)); - } - } - - return output.to_byte_buffer(); -} - -} diff --git a/Userland/Libraries/LibIMAP/QuotedPrintable.h b/Userland/Libraries/LibIMAP/QuotedPrintable.h deleted file mode 100644 index 7b1867ffc76..00000000000 --- a/Userland/Libraries/LibIMAP/QuotedPrintable.h +++ /dev/null @@ -1,15 +0,0 @@ -/* - * Copyright (c) 2021, Luke Wilde - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include - -namespace IMAP { - -ErrorOr decode_quoted_printable(StringView); - -} diff --git a/Userland/Libraries/LibKeyboard/CMakeLists.txt b/Userland/Libraries/LibKeyboard/CMakeLists.txt deleted file mode 100644 index 42feecf78a6..00000000000 --- a/Userland/Libraries/LibKeyboard/CMakeLists.txt +++ /dev/null @@ -1,7 +0,0 @@ -set(SOURCES - CharacterMap.cpp - CharacterMapFile.cpp -) - -serenity_lib(LibKeyboard keyboard) -target_link_libraries(LibKeyboard PRIVATE LibCore) diff --git a/Userland/Libraries/LibKeyboard/CharacterMap.cpp b/Userland/Libraries/LibKeyboard/CharacterMap.cpp deleted file mode 100644 index 9b6cf632154..00000000000 --- a/Userland/Libraries/LibKeyboard/CharacterMap.cpp +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright (c) 2020, Hüseyin Aslıtürk - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "CharacterMap.h" -#include -#include -#include -#include - -namespace Keyboard { - -ErrorOr CharacterMap::load_from_file(ByteString const& map_name) -{ - auto result = TRY(CharacterMapFile::load_from_file(map_name)); - - return CharacterMap(map_name, result); -} - -CharacterMap::CharacterMap(ByteString const& map_name, CharacterMapData const& map_data) - : m_character_map_data(map_data) - , m_character_map_name(map_name) -{ -} - -int CharacterMap::set_system_map() -{ - return setkeymap(m_character_map_name.characters(), m_character_map_data.map, m_character_map_data.shift_map, m_character_map_data.alt_map, m_character_map_data.altgr_map, m_character_map_data.shift_altgr_map); -} - -ErrorOr CharacterMap::fetch_system_map() -{ - CharacterMapData map_data; - char keymap_name[50 + 1] = { 0 }; - - if (getkeymap(keymap_name, sizeof(keymap_name), map_data.map, map_data.shift_map, map_data.alt_map, map_data.altgr_map, map_data.shift_altgr_map) < 0) - return Error::from_errno(errno); - - return CharacterMap { keymap_name, map_data }; -} - -ByteString const& CharacterMap::character_map_name() const -{ - return m_character_map_name; -} -} diff --git a/Userland/Libraries/LibKeyboard/CharacterMap.h b/Userland/Libraries/LibKeyboard/CharacterMap.h deleted file mode 100644 index 40c1d979617..00000000000 --- a/Userland/Libraries/LibKeyboard/CharacterMap.h +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright (c) 2020, Hüseyin Aslıtürk - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include - -namespace Keyboard { - -class CharacterMap { - -public: - CharacterMap(ByteString const& map_name, CharacterMapData const& map_data); - static ErrorOr load_from_file(ByteString const& filename); - - int set_system_map(); - static ErrorOr fetch_system_map(); - - CharacterMapData const& character_map_data() const { return m_character_map_data; } - ByteString const& character_map_name() const; - -private: - CharacterMapData m_character_map_data; - ByteString m_character_map_name; -}; - -} diff --git a/Userland/Libraries/LibKeyboard/CharacterMapData.h b/Userland/Libraries/LibKeyboard/CharacterMapData.h deleted file mode 100644 index d56426ac916..00000000000 --- a/Userland/Libraries/LibKeyboard/CharacterMapData.h +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright (c) 2020, Hüseyin Aslıtürk - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include - -#define CHAR_MAP_SIZE 0x80 - -namespace Keyboard { - -struct CharacterMapData { - u32 map[CHAR_MAP_SIZE]; - u32 shift_map[CHAR_MAP_SIZE]; - u32 alt_map[CHAR_MAP_SIZE]; - u32 altgr_map[CHAR_MAP_SIZE]; - u32 shift_altgr_map[CHAR_MAP_SIZE]; -}; - -} diff --git a/Userland/Libraries/LibKeyboard/CharacterMapFile.cpp b/Userland/Libraries/LibKeyboard/CharacterMapFile.cpp deleted file mode 100644 index c87f5b0e47d..00000000000 --- a/Userland/Libraries/LibKeyboard/CharacterMapFile.cpp +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Copyright (c) 2020, Hüseyin Aslıtürk - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "CharacterMapFile.h" -#include -#include -#include - -namespace Keyboard { - -ErrorOr CharacterMapFile::load_from_file(ByteString const& filename) -{ - auto path = filename; - if (!path.ends_with(".json"sv)) { - StringBuilder full_path; - full_path.append("/res/keymaps/"sv); - full_path.append(filename); - full_path.append(".json"sv); - path = full_path.to_byte_string(); - } - - auto file = TRY(Core::File::open(path, Core::File::OpenMode::Read)); - auto file_contents = TRY(file->read_until_eof()); - auto json_result = TRY(JsonValue::from_string(file_contents)); - auto const& json = json_result.as_object(); - - Vector map = read_map(json, "map"); - Vector shift_map = read_map(json, "shift_map"); - Vector alt_map = read_map(json, "alt_map"); - Vector altgr_map = read_map(json, "altgr_map"); - Vector shift_altgr_map = read_map(json, "shift_altgr_map"); - - CharacterMapData character_map; - for (int i = 0; i < CHAR_MAP_SIZE; i++) { - character_map.map[i] = map.at(i); - character_map.shift_map[i] = shift_map.at(i); - character_map.alt_map[i] = alt_map.at(i); - if (altgr_map.is_empty()) { - // AltGr map was not found, using Alt map as fallback. - character_map.altgr_map[i] = alt_map.at(i); - } else { - character_map.altgr_map[i] = altgr_map.at(i); - } - if (shift_altgr_map.is_empty()) { - // Shift+AltGr map was not found, using Alt map as fallback. - character_map.shift_altgr_map[i] = alt_map.at(i); - } else { - character_map.shift_altgr_map[i] = shift_altgr_map.at(i); - } - } - - return character_map; -} - -Vector CharacterMapFile::read_map(JsonObject const& json, ByteString const& name) -{ - if (!json.has(name)) - return {}; - - Vector buffer; - buffer.resize(CHAR_MAP_SIZE); - - auto map_arr = json.get_array(name).value(); - for (size_t i = 0; i < map_arr.size(); i++) { - auto key_value = map_arr.at(i).as_string(); - if (key_value.length() == 0) { - buffer[i] = 0; - } else if (key_value.length() == 1) { - buffer[i] = key_value.characters()[0]; - } else { - Utf8View m_utf8_view(key_value); - buffer[i] = *m_utf8_view.begin(); - } - } - - return buffer; -} - -} diff --git a/Userland/Libraries/LibKeyboard/CharacterMapFile.h b/Userland/Libraries/LibKeyboard/CharacterMapFile.h deleted file mode 100644 index 2bdb9dd867c..00000000000 --- a/Userland/Libraries/LibKeyboard/CharacterMapFile.h +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright (c) 2020, Hüseyin Aslıtürk - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include - -namespace Keyboard { - -class CharacterMapFile { - -public: - static ErrorOr load_from_file(ByteString const& filename); - -private: - static Vector read_map(JsonObject const& json, ByteString const& name); -}; - -} diff --git a/Userland/Libraries/LibManual/CMakeLists.txt b/Userland/Libraries/LibManual/CMakeLists.txt deleted file mode 100644 index af1d08d87dc..00000000000 --- a/Userland/Libraries/LibManual/CMakeLists.txt +++ /dev/null @@ -1,10 +0,0 @@ -set(SOURCES - Node.cpp - PageNode.cpp - Path.cpp - SectionNode.cpp - SubsectionNode.cpp -) - -serenity_lib(LibManual manual) -target_link_libraries(LibManual PRIVATE LibCore LibFileSystem LibURL) diff --git a/Userland/Libraries/LibManual/Node.cpp b/Userland/Libraries/LibManual/Node.cpp deleted file mode 100644 index c19220bff10..00000000000 --- a/Userland/Libraries/LibManual/Node.cpp +++ /dev/null @@ -1,121 +0,0 @@ -/* - * Copyright (c) 2022, kleines Filmröllchen - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "Node.h" -#include "PageNode.h" -#include "SectionNode.h" -#include -#include -#include -#include -#include -#include -#include - -namespace Manual { - -ErrorOr> Node::try_create_from_query(Vector const& query_parameters) -{ - if (query_parameters.size() > 2) - return Error::from_string_literal("Queries longer than 2 strings are not supported yet"); - - if (query_parameters.size() == 1 && query_parameters[0].starts_with("help://"sv)) { - auto help_url = URL::create_with_url_or_path(query_parameters[0].trim("/"sv, TrimMode::Right)); - auto node_from_url = TRY(Manual::Node::try_find_from_help_url(help_url)); - return *node_from_url->document(); - } - - auto query_parameter_iterator = query_parameters.begin(); - - if (query_parameter_iterator.is_end()) - return PageNode::help_index_page(); - - auto first_query_parameter = *query_parameter_iterator; - ++query_parameter_iterator; - if (query_parameter_iterator.is_end()) { - // [/path/to/docs.md] - auto path_from_query = LexicalPath { first_query_parameter }; - constexpr auto MARKDOWN_FILE_EXTENSION = "md"sv; - if (path_from_query.is_absolute() - && path_from_query.is_child_of(manual_base_path) - && path_from_query.extension() == MARKDOWN_FILE_EXTENSION) { - // Parse the section number and page name from a directory string of the form: - // /usr/share/man/man[section_number]/[page_name].md - // The page_name includes any subsections. - auto const& section_directory = path_from_query.string(); - auto section_name_start_index = manual_base_path.string().length() + 4; - auto section_name_end_index = section_directory.find('/', section_name_start_index); - if (!section_name_end_index.has_value()) - return Error::from_string_literal("Page is inside invalid section"); - auto section_name = section_directory.substring_view(section_name_start_index, section_name_end_index.value() - section_name_start_index); - auto section = TRY(SectionNode::try_create_from_number(section_name)); - auto page_name_end_index = section_directory.length() - section_name_end_index.value() - MARKDOWN_FILE_EXTENSION.length() - 1; - // +1 to trim the leading '/' from the start. - auto page_name = section_directory.substring_view(section_name_end_index.value() + 1, page_name_end_index - 1); - return try_make_ref_counted(section, TRY(String::from_utf8(page_name))); - } - - // [page] (in any section) - Optional> maybe_page; - for (auto const& section : sections) { - auto const page = TRY(try_make_ref_counted(section, TRY(String::from_utf8(first_query_parameter)))); - if (FileSystem::exists(TRY(page->path()))) { - maybe_page = page; - break; - } - } - if (maybe_page.has_value()) - return maybe_page.release_value(); - return Error::from_string_literal("Page not found"); - } - // [section] [name] - auto second_query_parameter = *query_parameter_iterator; - auto section = TRY(SectionNode::try_create_from_number(first_query_parameter)); - auto const page = TRY(try_make_ref_counted(section, TRY(String::from_utf8(second_query_parameter)))); - if (FileSystem::exists(TRY(page->path()))) - return page; - return Error::from_string_literal("Page doesn't exist in section"); -} - -ErrorOr> Node::try_find_from_help_url(URL::URL const& url) -{ - if (url.host() != "man"_string) - return Error::from_string_view("Bad help operation"sv); - if (url.path_segment_count() < 2) - return Error::from_string_view("Bad help page URL"sv); - - auto const section = url.path_segment_at_index(0); - auto maybe_section_number = section.to_number(); - if (!maybe_section_number.has_value()) - return Error::from_string_view("Bad section number"sv); - auto section_number = maybe_section_number.value(); - if (section_number > number_of_sections) - return Error::from_string_view("Section number out of bounds"sv); - - NonnullRefPtr current_node = sections[section_number - 1]; - bool child_node_found; - for (size_t i = 1; i < url.path_segment_count(); i++) { - child_node_found = false; - auto children = TRY(current_node->children()); - for (auto const& child : children) { - if (auto path = url.path_segment_at_index(i); TRY(child->name()) == path.view()) { - child_node_found = true; - current_node = child; - break; - } - } - - if (!child_node_found) - break; - } - - if (!child_node_found) - return Error::from_string_view("Page not found"sv); - - return current_node; -} - -} diff --git a/Userland/Libraries/LibManual/Node.h b/Userland/Libraries/LibManual/Node.h deleted file mode 100644 index 58b0e4a47f3..00000000000 --- a/Userland/Libraries/LibManual/Node.h +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright (c) 2019-2020, Sergey Bugaev - * Copyright (c) 2022, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include - -namespace Manual { - -class PageNode; - -class Node : public RefCounted { -public: - virtual ~Node() = default; - - virtual ErrorOr>> children() const = 0; - virtual Node const* parent() const = 0; - virtual ErrorOr name() const = 0; - virtual bool is_page() const { return false; } - virtual bool is_open() const { return false; } - virtual ErrorOr path() const = 0; - virtual PageNode const* document() const = 0; - virtual unsigned section_number() const = 0; - - // Backend for the command-line argument format that Help and man accept. Handles: - // [/path/to/documentation.md] (no second argument) - // [page] (no second argument) - will find first section with that page - // [section] [page] - // Help can also (externally) handle search queries, which is not possible (yet) in man. - static ErrorOr> try_create_from_query(Vector const& query_parameters); - - // Finds a page via the help://man///page URLs. - // This will automatically start discovering pages by inspecting the filesystem. - static ErrorOr> try_find_from_help_url(URL::URL const&); - - bool operator==(Node const& other) const - { - if (auto this_path = this->path(), other_path = other.path(); - !this_path.is_error() && !other_path.is_error()) { - return this_path.release_value() == other_path.release_value(); - } - return false; - } -}; - -} - -namespace AK { - -template -requires(IsBaseOf) struct Traits : public DefaultTraits { - static unsigned hash(T p) { return Traits::hash(p.path()); } -}; - -} diff --git a/Userland/Libraries/LibManual/PageNode.cpp b/Userland/Libraries/LibManual/PageNode.cpp deleted file mode 100644 index d6bdce65697..00000000000 --- a/Userland/Libraries/LibManual/PageNode.cpp +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright (c) 2019-2020, Sergey Bugaev - * Copyright (c) 2022, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "PageNode.h" -#include "SectionNode.h" -#include - -namespace Manual { - -Node const* PageNode::parent() const -{ - return m_section.ptr(); -} - -ErrorOr>> PageNode::children() const -{ - static Vector> empty_vector; - return empty_vector.span(); -} - -ErrorOr PageNode::path() const -{ - return TRY(String::formatted("{}/{}.md", TRY(m_section->path()), m_page)); -} - -unsigned PageNode::section_number() const -{ - return m_section->section_number(); -} - -ErrorOr> PageNode::help_index_page() -{ - static NonnullRefPtr const help_index_page = TRY(try_make_ref_counted(sections[7 - 1], "Help-index"_string)); - return help_index_page; -} - -} diff --git a/Userland/Libraries/LibManual/PageNode.h b/Userland/Libraries/LibManual/PageNode.h deleted file mode 100644 index 57ea6d0f5dd..00000000000 --- a/Userland/Libraries/LibManual/PageNode.h +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright (c) 2019-2020, Sergey Bugaev - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include - -namespace Manual { - -class SectionNode; - -class PageNode : public Node { -public: - virtual ~PageNode() override = default; - - PageNode(NonnullRefPtr section, String page) - : m_section(move(section)) - , m_page(move(page)) - { - } - - virtual ErrorOr>> children() const override; - virtual Node const* parent() const override; - virtual ErrorOr name() const override { return m_page; } - virtual bool is_page() const override { return true; } - virtual PageNode const* document() const override { return this; } - virtual unsigned section_number() const override; - - virtual ErrorOr path() const override; - - static ErrorOr> help_index_page(); - -private: - NonnullRefPtr m_section; - String m_page; -}; - -} diff --git a/Userland/Libraries/LibManual/Path.cpp b/Userland/Libraries/LibManual/Path.cpp deleted file mode 100644 index d6f09c9646d..00000000000 --- a/Userland/Libraries/LibManual/Path.cpp +++ /dev/null @@ -1,13 +0,0 @@ -/* - * Copyright (c) 2022, kleines Filmröllchen - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "Path.h" - -namespace Manual { - -LexicalPath const manual_base_path { "/usr/share/man" }; - -} diff --git a/Userland/Libraries/LibManual/Path.h b/Userland/Libraries/LibManual/Path.h deleted file mode 100644 index d9f45496f8a..00000000000 --- a/Userland/Libraries/LibManual/Path.h +++ /dev/null @@ -1,17 +0,0 @@ -/* - * Copyright (c) 2022, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include - -namespace Manual { - -extern LexicalPath const manual_base_path; - -constexpr StringView const top_level_section_prefix = "man"sv; - -} diff --git a/Userland/Libraries/LibManual/SectionNode.cpp b/Userland/Libraries/LibManual/SectionNode.cpp deleted file mode 100644 index 5af4504a573..00000000000 --- a/Userland/Libraries/LibManual/SectionNode.cpp +++ /dev/null @@ -1,108 +0,0 @@ -/* - * Copyright (c) 2019-2020, Sergey Bugaev - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "SectionNode.h" -#include "PageNode.h" -#include "Path.h" -#include "SubsectionNode.h" -#include -#include -#include -#include -#include - -namespace Manual { - -ErrorOr> SectionNode::try_create_from_number(StringView section) -{ - auto maybe_section_number = section.to_number(); - if (!maybe_section_number.has_value()) - return Error::from_string_literal("Section is not a number"); - auto section_number = maybe_section_number.release_value(); - if (section_number < 1 || section_number > number_of_sections) - return Error::from_string_literal("Section number is not valid"); - return sections[section_number - 1]; -} - -ErrorOr SectionNode::path() const -{ - return String::formatted("{}/{}{}", manual_base_path, top_level_section_prefix, m_section); -} - -ErrorOr SectionNode::name() const -{ - return String::formatted("{}. {}", m_section, m_name); -} - -ErrorOr SectionNode::reify_if_needed() const -{ - if (m_reified) - return {}; - m_reified = true; - - auto own_path = TRY(path()); - Core::DirIterator dir_iterator { own_path.to_byte_string(), Core::DirIterator::Flags::SkipDots }; - Vector directories; - HashTable files; - while (dir_iterator.has_next()) { - auto entry = dir_iterator.next(); - if (entry->type == Core::DirectoryEntry::Type::Directory) - TRY(directories.try_append(entry->name)); - else if (entry->type == Core::DirectoryEntry::Type::File && entry->name.ends_with(".md"sv, CaseSensitivity::CaseInsensitive)) - TRY(files.try_set(entry->name)); - } - - struct Child { - NonnullRefPtr node; - String name_for_sorting; - }; - Vector children; - - for (auto const& directory : directories) { - LexicalPath lexical_path(directory); - RefPtr associated_page; - auto matching_page_name = ByteString::formatted("{}.md", directory); - if (files.remove(matching_page_name)) - associated_page = TRY(try_make_ref_counted(*this, TRY(String::from_utf8(lexical_path.title())))); - - TRY(children.try_append({ .node = TRY(try_make_ref_counted(*this, lexical_path.title(), associated_page)), - .name_for_sorting = TRY(String::from_utf8(lexical_path.title())) })); - } - - for (auto const& file : files) { - LexicalPath lexical_path(file); - children.append({ .node = TRY(try_make_ref_counted(*this, TRY(String::from_utf8(lexical_path.title())))), - .name_for_sorting = TRY(String::from_utf8(lexical_path.title())) }); - } - - quick_sort(children, [](auto const& a, auto const& b) { return a.name_for_sorting < b.name_for_sorting; }); - - m_children.ensure_capacity(children.size()); - for (auto child : children) - m_children.unchecked_append(move(child.node)); - - return {}; -} - -void SectionNode::set_open(bool open) -{ - if (m_open == open) - return; - m_open = open; -} - -Array, number_of_sections> const sections = { { - make_ref_counted("1"sv, "User Programs"sv), - make_ref_counted("2"sv, "System Calls"sv), - make_ref_counted("3"sv, "Library Functions"sv), - make_ref_counted("4"sv, "Special Files"sv), - make_ref_counted("5"sv, "File Formats"sv), - make_ref_counted("6"sv, "Games"sv), - make_ref_counted("7"sv, "Miscellanea"sv), - make_ref_counted("8"sv, "Sysadmin Tools"sv), -} }; - -} diff --git a/Userland/Libraries/LibManual/SectionNode.h b/Userland/Libraries/LibManual/SectionNode.h deleted file mode 100644 index 99b9df08953..00000000000 --- a/Userland/Libraries/LibManual/SectionNode.h +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright (c) 2019-2020, Sergey Bugaev - * Copyright (c) 2022, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include - -namespace Manual { - -class SectionNode : public Node { -public: - virtual ~SectionNode() override = default; - - SectionNode(StringView section, StringView name) - : m_section(MUST(String::from_utf8(section))) - , m_name(MUST(String::from_utf8(name))) - { - } - - virtual ErrorOr>> children() const override - { - TRY(reify_if_needed()); - return m_children.span(); - } - - virtual Node const* parent() const override { return nullptr; } - virtual ErrorOr name() const override; - String const& section_name() const { return m_section; } - virtual unsigned section_number() const override { return m_section.to_number().value_or(0); } - virtual ErrorOr path() const override; - virtual PageNode const* document() const override { return nullptr; } - - virtual bool is_open() const override { return m_open; } - void set_open(bool open); - - static ErrorOr> try_create_from_number(StringView section_number); - -protected: - // In this class, the section is a number, but in lower sections it might be the same as the name. - String m_section; - String m_name; - -private: - ErrorOr reify_if_needed() const; - - mutable Vector> m_children; - mutable bool m_reified { false }; - bool m_open { false }; -}; - -constexpr size_t number_of_sections = 8; - -extern Array, number_of_sections> const sections; - -constexpr Array const section_numbers = { - "1"sv, - "2"sv, - "3"sv, - "4"sv, - "5"sv, - "6"sv, - "7"sv, - "8"sv, -}; - -} diff --git a/Userland/Libraries/LibManual/SubsectionNode.cpp b/Userland/Libraries/LibManual/SubsectionNode.cpp deleted file mode 100644 index f5535a1bf89..00000000000 --- a/Userland/Libraries/LibManual/SubsectionNode.cpp +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright (c) 2022, kleines Filmröllchen - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "SubsectionNode.h" -#include "PageNode.h" -#include - -namespace Manual { - -SubsectionNode::SubsectionNode(NonnullRefPtr parent, StringView name, RefPtr page) - : SectionNode(name, name) - , m_parent(move(parent)) - , m_page(move(page)) -{ -} - -Node const* SubsectionNode::parent() const { return m_parent; } - -PageNode const* SubsectionNode::document() const { return m_page; } - -ErrorOr SubsectionNode::name() const { return m_name; } - -ErrorOr SubsectionNode::path() const -{ - return String::formatted("{}/{}", TRY(m_parent->path()), m_section); -} - -} diff --git a/Userland/Libraries/LibManual/SubsectionNode.h b/Userland/Libraries/LibManual/SubsectionNode.h deleted file mode 100644 index c25dd8213be..00000000000 --- a/Userland/Libraries/LibManual/SubsectionNode.h +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright (c) 2022, kleines Filmröllchen - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include - -namespace Manual { - -// A non-toplevel (i.e. not numbered) manual section. -class SubsectionNode : public SectionNode { -public: - SubsectionNode(NonnullRefPtr parent, StringView name, RefPtr page = {}); - virtual ~SubsectionNode() = default; - - virtual Node const* parent() const override; - virtual ErrorOr path() const override; - virtual ErrorOr name() const override; - virtual PageNode const* document() const override; - virtual unsigned section_number() const override { return m_parent->section_number(); } - -protected: - NonnullRefPtr m_parent; - -private: - RefPtr m_page; -}; - -} diff --git a/Userland/Libraries/LibPCIDB/CMakeLists.txt b/Userland/Libraries/LibPCIDB/CMakeLists.txt deleted file mode 100644 index 2517aee6e6d..00000000000 --- a/Userland/Libraries/LibPCIDB/CMakeLists.txt +++ /dev/null @@ -1,5 +0,0 @@ -set(SOURCES - Database.cpp -) - -serenity_lib(LibPCIDB pcidb) diff --git a/Userland/Libraries/LibPCIDB/Database.cpp b/Userland/Libraries/LibPCIDB/Database.cpp deleted file mode 100644 index a7a21134ef5..00000000000 --- a/Userland/Libraries/LibPCIDB/Database.cpp +++ /dev/null @@ -1,228 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include - -#include "Database.h" - -namespace PCIDB { - -RefPtr Database::open(ByteString const& filename) -{ - auto file_or_error = Core::MappedFile::map(filename); - if (file_or_error.is_error()) - return nullptr; - auto res = adopt_ref(*new Database(file_or_error.release_value())); - if (res->init() != 0) - return nullptr; - return res; -} - -StringView const Database::get_vendor(u16 vendor_id) const -{ - auto const& vendor = m_vendors.get(vendor_id); - if (!vendor.has_value()) - return ""sv; - return vendor.value()->name; -} - -StringView const Database::get_device(u16 vendor_id, u16 device_id) const -{ - auto const& vendor = m_vendors.get(vendor_id); - if (!vendor.has_value()) - return ""sv; - auto const& device = vendor.value()->devices.get(device_id); - if (!device.has_value()) - return ""sv; - return device.value()->name; -} - -StringView const Database::get_subsystem(u16 vendor_id, u16 device_id, u16 subvendor_id, u16 subdevice_id) const -{ - auto const& vendor = m_vendors.get(vendor_id); - if (!vendor.has_value()) - return ""sv; - auto const& device = vendor.value()->devices.get(device_id); - if (!device.has_value()) - return ""sv; - auto const& subsystem = device.value()->subsystems.get((subvendor_id << 16) + subdevice_id); - if (!subsystem.has_value()) - return ""sv; - return subsystem.value()->name; -} - -StringView const Database::get_class(u8 class_id) const -{ - auto const& xclass = m_classes.get(class_id); - if (!xclass.has_value()) - return ""sv; - return xclass.value()->name; -} - -StringView const Database::get_subclass(u8 class_id, u8 subclass_id) const -{ - auto const& xclass = m_classes.get(class_id); - if (!xclass.has_value()) - return ""sv; - auto const& subclass = xclass.value()->subclasses.get(subclass_id); - if (!subclass.has_value()) - return ""sv; - return subclass.value()->name; -} - -StringView const Database::get_programming_interface(u8 class_id, u8 subclass_id, u8 programming_interface_id) const -{ - auto const& xclass = m_classes.get(class_id); - if (!xclass.has_value()) - return ""sv; - auto const& subclass = xclass.value()->subclasses.get(subclass_id); - if (!subclass.has_value()) - return ""sv; - auto const& programming_interface = subclass.value()->programming_interfaces.get(programming_interface_id); - if (!programming_interface.has_value()) - return ""sv; - return programming_interface.value()->name; -} - -static u8 parse_hex_digit(char digit) -{ - if (digit >= '0' && digit <= '9') - return digit - '0'; - VERIFY(digit >= 'a' && digit <= 'f'); - return 10 + (digit - 'a'); -} - -template -static T parse_hex(StringView str, size_t count) -{ - VERIFY(str.length() >= count); - - T res = 0; - for (size_t i = 0; i < count; i++) - res = (res << 4) + parse_hex_digit(str[i]); - - return res; -} - -int Database::init() -{ - if (m_ready) - return 0; - - m_view = StringView { m_file->bytes() }; - - ParseMode mode = ParseMode::UnknownMode; - - OwnPtr current_vendor {}; - OwnPtr current_device {}; - OwnPtr current_class {}; - OwnPtr current_subclass {}; - - auto commit_device = [&]() { - if (current_device && current_vendor) { - auto id = current_device->id; - current_vendor->devices.set(id, current_device.release_nonnull()); - } - }; - - auto commit_vendor = [&]() { - commit_device(); - if (current_vendor) { - auto id = current_vendor->id; - m_vendors.set(id, current_vendor.release_nonnull()); - } - }; - - auto commit_subclass = [&]() { - if (current_subclass && current_class) { - auto id = current_subclass->id; - current_class->subclasses.set(id, current_subclass.release_nonnull()); - } - }; - - auto commit_class = [&]() { - commit_subclass(); - if (current_class) { - auto id = current_class->id; - m_classes.set(id, current_class.release_nonnull()); - } - }; - - auto commit_all = [&]() { - commit_vendor(); - commit_class(); - }; - - auto lines = m_view.split_view('\n'); - - for (auto& line : lines) { - if (line.length() < 2 || line[0] == '#') - continue; - - if (line[0] == 'C') { - mode = ParseMode::ClassMode; - commit_all(); - } else if ((line[0] >= '0' && line[0] <= '9') || (line[0] >= 'a' && line[0] <= 'f')) { - mode = ParseMode::VendorMode; - commit_all(); - } else if (line[0] != '\t') { - mode = ParseMode::UnknownMode; - continue; - } - - switch (mode) { - case ParseMode::VendorMode: - if (line[0] != '\t') { - commit_vendor(); - current_vendor = make(); - current_vendor->id = parse_hex(line, 4); - current_vendor->name = line.substring_view(6, line.length() - 6); - } else if (line[0] == '\t' && line[1] != '\t') { - commit_device(); - current_device = make(); - current_device->id = parse_hex(line.substring_view(1, line.length() - 1), 4); - current_device->name = line.substring_view(7, line.length() - 7); - } else if (line[0] == '\t' && line[1] == '\t') { - auto subsystem = make(); - subsystem->vendor_id = parse_hex(line.substring_view(2, 4), 4); - subsystem->device_id = parse_hex(line.substring_view(7, 4), 4); - subsystem->name = line.substring_view(13, line.length() - 13); - current_device->subsystems.set((subsystem->vendor_id << 8) + subsystem->device_id, move(subsystem)); - } - break; - case ParseMode::ClassMode: - if (line[0] != '\t') { - commit_class(); - current_class = make(); - current_class->id = parse_hex(line.substring_view(2, 2), 2); - current_class->name = line.substring_view(6, line.length() - 6); - } else if (line[0] == '\t' && line[1] != '\t') { - commit_subclass(); - current_subclass = make(); - current_subclass->id = parse_hex(line.substring_view(1, 2), 2); - current_subclass->name = line.substring_view(5, line.length() - 5); - } else if (line[0] == '\t' && line[1] == '\t') { - auto programming_interface = make(); - programming_interface->id = parse_hex(line.substring_view(2, 2), 2); - programming_interface->name = line.substring_view(6, line.length() - 6); - current_subclass->programming_interfaces.set(programming_interface->id, move(programming_interface)); - } - break; - default: - break; - } - } - - commit_all(); - - m_ready = true; - - return 0; -} - -} diff --git a/Userland/Libraries/LibPCIDB/Database.h b/Userland/Libraries/LibPCIDB/Database.h deleted file mode 100644 index 25400a8ec0b..00000000000 --- a/Userland/Libraries/LibPCIDB/Database.h +++ /dev/null @@ -1,87 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include -#include -#include - -namespace PCIDB { - -struct Subsystem { - u16 vendor_id; - u16 device_id; - StringView name; -}; - -struct Device { - u16 id; - StringView name; - HashMap> subsystems; -}; - -struct Vendor { - u16 id; - StringView name; - HashMap> devices; -}; - -struct ProgrammingInterface { - u8 id { 0 }; - StringView name {}; -}; - -struct Subclass { - u8 id { 0 }; - StringView name {}; - HashMap> programming_interfaces; -}; - -struct Class { - u8 id { 0 }; - StringView name {}; - HashMap> subclasses; -}; - -class Database : public RefCounted { -public: - static RefPtr open(ByteString const& filename); - static RefPtr open() { return open("/res/pci.ids"); } - - StringView const get_vendor(u16 vendor_id) const; - StringView const get_device(u16 vendor_id, u16 device_id) const; - StringView const get_subsystem(u16 vendor_id, u16 device_id, u16 subvendor_id, u16 subdevice_id) const; - StringView const get_class(u8 class_id) const; - StringView const get_subclass(u8 class_id, u8 subclass_id) const; - StringView const get_programming_interface(u8 class_id, u8 subclass_id, u8 programming_interface_id) const; - -private: - explicit Database(NonnullOwnPtr file) - : m_file(move(file)) - { - } - - int init(); - - enum ParseMode { - UnknownMode, - VendorMode, - ClassMode, - }; - - NonnullOwnPtr m_file; - StringView m_view {}; - HashMap> m_vendors; - HashMap> m_classes; - bool m_ready { false }; -}; - -} diff --git a/Userland/Libraries/LibPartition/CMakeLists.txt b/Userland/Libraries/LibPartition/CMakeLists.txt deleted file mode 100644 index e8afeeb7072..00000000000 --- a/Userland/Libraries/LibPartition/CMakeLists.txt +++ /dev/null @@ -1,11 +0,0 @@ -set(SOURCES - DiskPartitionMetadata.cpp - EBRPartitionTable.cpp - GUIDPartitionTable.cpp - MBRPartitionTable.cpp - PartitionableDevice.cpp - PartitionTable.cpp -) - -serenity_lib(LibPartition partition) -target_link_libraries(LibPartition PRIVATE LibCore) diff --git a/Userland/Libraries/LibPartition/DiskPartitionMetadata.cpp b/Userland/Libraries/LibPartition/DiskPartitionMetadata.cpp deleted file mode 100644 index 0ce3eced3b5..00000000000 --- a/Userland/Libraries/LibPartition/DiskPartitionMetadata.cpp +++ /dev/null @@ -1,98 +0,0 @@ -/* - * Copyright (c) 2020, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include - -namespace Partition { - -DiskPartitionMetadata::PartitionType::PartitionType(u8 partition_type) -{ - m_partition_type[0] = partition_type; -} -DiskPartitionMetadata::PartitionType::PartitionType(Array partition_type) - : m_partition_type_is_uuid(true) -{ - m_partition_type.span().overwrite(0, partition_type.data(), partition_type.size()); -} -UUID DiskPartitionMetadata::PartitionType::to_uuid() const -{ - VERIFY(is_uuid()); - return m_partition_type; -} -u8 DiskPartitionMetadata::PartitionType::to_byte_indicator() const -{ - VERIFY(!is_uuid()); - return m_partition_type[0]; -} -bool DiskPartitionMetadata::PartitionType::is_uuid() const -{ - return m_partition_type_is_uuid; -} -bool DiskPartitionMetadata::PartitionType::is_valid() const -{ - return !all_of(m_partition_type, [](auto const octet) { return octet == 0; }); -} - -DiskPartitionMetadata::DiskPartitionMetadata(u64 start_block, u64 end_block, u8 partition_type) - : m_start_block(start_block) - , m_end_block(end_block) - , m_type(partition_type) -{ - VERIFY(m_type.is_valid()); -} - -DiskPartitionMetadata::DiskPartitionMetadata(u64 start_block, u64 end_block, Array partition_type) - : m_start_block(start_block) - , m_end_block(end_block) - , m_type(partition_type) -{ - VERIFY(m_type.is_valid()); -} - -DiskPartitionMetadata::DiskPartitionMetadata(u64 start_block, u64 end_block, Array partition_type, UUID unique_guid, u64 special_attributes) - : m_start_block(start_block) - , m_end_block(end_block) - , m_type(partition_type) - , m_unique_guid(unique_guid) - , m_attributes(special_attributes) -{ - VERIFY(m_type.is_valid()); - VERIFY(!m_unique_guid.is_zero()); -} - -DiskPartitionMetadata DiskPartitionMetadata::offset(u64 blocks_count) const -{ - return { blocks_count + m_start_block, blocks_count + m_end_block, m_type.m_partition_type }; -} - -u64 DiskPartitionMetadata::start_block() const -{ - return m_start_block; -} - -u64 DiskPartitionMetadata::end_block() const -{ - return m_end_block; -} - -Optional DiskPartitionMetadata::special_attributes() const -{ - if (m_attributes == 0) - return {}; - return m_attributes; -} - -DiskPartitionMetadata::PartitionType const& DiskPartitionMetadata::type() const -{ - return m_type; -} - -const UUID& DiskPartitionMetadata::unique_guid() const -{ - return m_unique_guid; -} - -} diff --git a/Userland/Libraries/LibPartition/DiskPartitionMetadata.h b/Userland/Libraries/LibPartition/DiskPartitionMetadata.h deleted file mode 100644 index 252be047a79..00000000000 --- a/Userland/Libraries/LibPartition/DiskPartitionMetadata.h +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright (c) 2020, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include - -namespace Partition { - -class DiskPartitionMetadata { -private: - class PartitionType { - friend class DiskPartitionMetadata; - - public: - explicit PartitionType(u8 partition_type); - explicit PartitionType(Array partition_type); - UUID to_uuid() const; - u8 to_byte_indicator() const; - bool is_uuid() const; - bool is_valid() const; - - private: - Array m_partition_type {}; - bool m_partition_type_is_uuid { false }; - }; - -public: - DiskPartitionMetadata(u64 block_offset, u64 block_limit, u8 partition_type); - DiskPartitionMetadata(u64 start_block, u64 end_block, Array partition_type); - DiskPartitionMetadata(u64 block_offset, u64 block_limit, Array partition_type, UUID unique_guid, u64 special_attributes); - u64 start_block() const; - u64 end_block() const; - - DiskPartitionMetadata offset(u64 blocks_count) const; - - Optional special_attributes() const; - PartitionType const& type() const; - const UUID& unique_guid() const; - -private: - u64 m_start_block; - u64 m_end_block; - PartitionType m_type; - UUID m_unique_guid {}; - u64 m_attributes { 0 }; -}; - -} diff --git a/Userland/Libraries/LibPartition/EBRPartitionTable.cpp b/Userland/Libraries/LibPartition/EBRPartitionTable.cpp deleted file mode 100644 index 9011526578d..00000000000 --- a/Userland/Libraries/LibPartition/EBRPartitionTable.cpp +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Copyright (c) 2020-2022, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include - -namespace Partition { - -ErrorOr> EBRPartitionTable::try_to_initialize(PartitionableDevice device) -{ - auto table = TRY(adopt_nonnull_own_or_enomem(new (nothrow) EBRPartitionTable(move(device)))); - if (table->is_protective_mbr()) - return Error::from_errno(ENOTSUP); - if (!table->is_valid()) - return Error::from_errno(EINVAL); - return table; -} - -void EBRPartitionTable::search_extended_partition(MBRPartitionTable& checked_ebr, u64 current_block_offset, size_t limit) -{ - if (limit == 0) - return; - // EBRs should not carry more than 2 partitions (because they need to form a linked list) - VERIFY(checked_ebr.partitions_count() <= 2); - // FIXME: We should not crash the Kernel or any apps when the EBR is malformed. - auto checked_logical_partition = checked_ebr.partition(0); - - // If we are pointed to an invalid logical partition, something is seriously wrong. - VERIFY(checked_logical_partition.has_value()); - m_partitions.append(checked_logical_partition.value().offset(current_block_offset)); - if (!checked_ebr.contains_ebr()) - return; - current_block_offset += checked_ebr.partition(1).value().start_block(); - auto next_ebr = MBRPartitionTable::try_to_initialize(m_device.clone_unowned(), current_block_offset); - if (!next_ebr) - return; - // FIXME: Should not rely on TCO here, since this might be called from inside the Kernel, where stack space isn't exactly free. - search_extended_partition(*next_ebr, current_block_offset, (limit - 1)); -} - -EBRPartitionTable::EBRPartitionTable(PartitionableDevice device) - : MBRPartitionTable(move(device)) -{ - if (!is_header_valid()) - return; - m_valid = true; - - VERIFY(partitions_count() == 0); - - auto& header = this->header(); - for (size_t index = 0; index < 4; index++) { - auto& entry = header.entry[index]; - // Start enumerating all logical partitions - if (entry.type == 0xf) { - auto checked_ebr = MBRPartitionTable::try_to_initialize(m_device.clone_unowned(), entry.offset); - if (!checked_ebr) - continue; - // It's quite unlikely to see that amount of partitions, so stop at 128 partitions. - search_extended_partition(*checked_ebr, entry.offset, 128); - continue; - } - - if (entry.offset == 0x00) { - continue; - } - MUST(m_partitions.try_empend(entry.offset, (entry.offset + entry.length) - 1, entry.type)); - } -} - -EBRPartitionTable::~EBRPartitionTable() = default; - -} diff --git a/Userland/Libraries/LibPartition/EBRPartitionTable.h b/Userland/Libraries/LibPartition/EBRPartitionTable.h deleted file mode 100644 index 41a76f5addf..00000000000 --- a/Userland/Libraries/LibPartition/EBRPartitionTable.h +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright (c) 2020-2022, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include - -namespace Partition { - -struct EBRPartitionHeader; -class EBRPartitionTable : public MBRPartitionTable { -public: - ~EBRPartitionTable(); - - static ErrorOr> try_to_initialize(PartitionableDevice); - explicit EBRPartitionTable(PartitionableDevice); - - virtual bool is_valid() const override - { - return m_valid; - } - -private: - void search_extended_partition(MBRPartitionTable&, u64, size_t limit); - - bool m_valid { false }; -}; - -} diff --git a/Userland/Libraries/LibPartition/GUIDPartitionTable.cpp b/Userland/Libraries/LibPartition/GUIDPartitionTable.cpp deleted file mode 100644 index 7ff39051c42..00000000000 --- a/Userland/Libraries/LibPartition/GUIDPartitionTable.cpp +++ /dev/null @@ -1,123 +0,0 @@ -/* - * Copyright (c) 2020-2022, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include - -namespace Partition { - -#define GPT_SIGNATURE2 0x54524150 -#define GPT_SIGNATURE 0x20494645 - -struct [[gnu::packed]] GPTPartitionEntry { - u8 partition_guid[16]; - u8 unique_guid[16]; - - u64 first_lba; - u64 last_lba; - - u64 attributes; - char partition_name[72]; -}; - -struct [[gnu::packed]] GUIDPartitionHeader { - u32 sig[2]; - u32 revision; - u32 header_size; - u32 crc32_header; - u32 reserved; - u64 current_lba; - u64 backup_lba; - - u64 first_usable_lba; - u64 last_usable_lba; - - u64 disk_guid1[2]; - - u64 partition_array_start_lba; - - u32 entries_count; - u32 partition_entry_size; - u32 crc32_entries_array; -}; - -ErrorOr> GUIDPartitionTable::try_to_initialize(PartitionableDevice device) -{ - auto table = TRY(adopt_nonnull_own_or_enomem(new (nothrow) GUIDPartitionTable(move(device)))); - if (!table->is_valid()) - return Error::from_errno(EINVAL); - return table; -} - -GUIDPartitionTable::GUIDPartitionTable(PartitionableDevice device) - : MBRPartitionTable(move(device)) -{ - // FIXME: Handle OOM failure here. - m_cached_header = ByteBuffer::create_zeroed(block_size()).release_value_but_fixme_should_propagate_errors(); - VERIFY(partitions_count() == 0); - if (!initialize()) - m_valid = false; -} - -GUIDPartitionHeader const& GUIDPartitionTable::header() const -{ - return *(GUIDPartitionHeader const*)m_cached_header.data(); -} - -bool GUIDPartitionTable::initialize() -{ - VERIFY(m_cached_header.data() != nullptr); - - auto first_gpt_block = (block_size() == 512) ? 1 : 0; - - auto maybe_error = m_device.read_block(first_gpt_block, m_cached_header.bytes()); - if (maybe_error.is_error()) - return false; - - dbgln_if(GPT_DEBUG, "GUIDPartitionTable: signature - {:#08x} {:#08x}", header().sig[1], header().sig[0]); - - if (header().sig[0] != GPT_SIGNATURE && header().sig[1] != GPT_SIGNATURE2) { - dbgln("GUIDPartitionTable: bad signature {:#08x} {:#08x}", header().sig[1], header().sig[0]); - return false; - } - - auto entries_buffer_result = ByteBuffer::create_zeroed(block_size()); - if (entries_buffer_result.is_error()) { - dbgln("GUIDPartitionTable: not enough memory for entries buffer"); - return false; - } - auto entries_buffer = entries_buffer_result.release_value(); - size_t raw_byte_index = header().partition_array_start_lba * block_size(); - for (size_t entry_index = 0; entry_index < header().entries_count; entry_index++) { - maybe_error = m_device.read_block(raw_byte_index / block_size(), entries_buffer.bytes()); - if (maybe_error.is_error()) - return false; - auto* entries = (GPTPartitionEntry const*)entries_buffer.data(); - auto& entry = entries[entry_index % (block_size() / header().partition_entry_size)]; - Array partition_type {}; - partition_type.span().overwrite(0, entry.partition_guid, partition_type.size()); - - if (is_unused_entry(partition_type)) { - raw_byte_index += header().partition_entry_size; - continue; - } - - Array unique_guid {}; - unique_guid.span().overwrite(0, entry.unique_guid, unique_guid.size()); - dbgln("Detected GPT partition (entry={}), offset={}, limit={}", entry_index, entry.first_lba, entry.last_lba); - m_partitions.append({ entry.first_lba, entry.last_lba, partition_type, unique_guid, entry.attributes }); - raw_byte_index += header().partition_entry_size; - } - - return true; -} - -bool GUIDPartitionTable::is_unused_entry(Array partition_type) const -{ - return all_of(partition_type, [](auto const octet) { return octet == 0; }); -} - -} diff --git a/Userland/Libraries/LibPartition/GUIDPartitionTable.h b/Userland/Libraries/LibPartition/GUIDPartitionTable.h deleted file mode 100644 index a7e102beb9d..00000000000 --- a/Userland/Libraries/LibPartition/GUIDPartitionTable.h +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright (c) 2020-2022, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include - -namespace Partition { - -struct GUIDPartitionHeader; -class GUIDPartitionTable final : public MBRPartitionTable { -public: - virtual ~GUIDPartitionTable() = default; - - static ErrorOr> try_to_initialize(PartitionableDevice); - explicit GUIDPartitionTable(PartitionableDevice); - - virtual bool is_valid() const override - { - return m_valid; - } - -private: - bool is_unused_entry(Array) const; - GUIDPartitionHeader const& header() const; - bool initialize(); - - bool m_valid { true }; - ByteBuffer m_cached_header; -}; - -} diff --git a/Userland/Libraries/LibPartition/MBRPartitionTable.cpp b/Userland/Libraries/LibPartition/MBRPartitionTable.cpp deleted file mode 100644 index bcb1ec2f080..00000000000 --- a/Userland/Libraries/LibPartition/MBRPartitionTable.cpp +++ /dev/null @@ -1,126 +0,0 @@ -/* - * Copyright (c) 2020-2022, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include - -namespace Partition { - -#define MBR_SIGNATURE 0xaa55 -#define MBR_PROTECTIVE 0xEE -#define EBR_CHS_CONTAINER 0x05 -#define EBR_LBA_CONTAINER 0x0F - -ErrorOr> MBRPartitionTable::try_to_initialize(PartitionableDevice device) -{ - auto table = TRY(adopt_nonnull_own_or_enomem(new (nothrow) MBRPartitionTable(move(device)))); - if (table->contains_ebr()) - return Error::from_errno(ENOTSUP); - if (table->is_protective_mbr()) - return Error::from_errno(ENOTSUP); - if (!table->is_valid()) - return Error::from_errno(EINVAL); - return table; -} - -OwnPtr MBRPartitionTable::try_to_initialize(PartitionableDevice device, u32 start_lba) -{ - auto table = adopt_nonnull_own_or_enomem(new (nothrow) MBRPartitionTable(move(device), start_lba)).release_value_but_fixme_should_propagate_errors(); - if (!table->is_valid()) - return {}; - return table; -} - -bool MBRPartitionTable::read_boot_record() -{ - if (block_size() != 512) - return false; - auto maybe_error = m_device.read_block(m_start_lba, m_cached_header.bytes()); - m_header_valid = !maybe_error.is_error(); - return m_header_valid; -} - -MBRPartitionTable::MBRPartitionTable(PartitionableDevice device, u32 start_lba) - : PartitionTable(move(device)) - , m_start_lba(start_lba) - , m_cached_header(ByteBuffer::create_zeroed(block_size()).release_value_but_fixme_should_propagate_errors()) // FIXME: Do something sensible if this fails because of OOM. -{ - if (!read_boot_record() || !initialize()) - return; - - m_header_valid = true; - - auto& header = this->header(); - for (size_t index = 0; index < 4; index++) { - auto& entry = header.entry[index]; - if (entry.offset == 0x00) { - continue; - } - // We have to place these in stack variables, since try_empend will try to take a reference to them, which is UB (since they're gnu::packed and unaligned) - u64 const block_offset = entry.offset; - u64 const block_limit = (entry.offset + entry.length) - 1; - u8 const partition_type = entry.type; - MUST(m_partitions.try_empend(block_offset, block_limit, partition_type)); - } - m_valid = true; -} - -MBRPartitionTable::MBRPartitionTable(PartitionableDevice device) - : PartitionTable(move(device)) - , m_start_lba(0) - , m_cached_header(ByteBuffer::create_zeroed(block_size()).release_value_but_fixme_should_propagate_errors()) // FIXME: Do something sensible if this fails because of OOM. -{ - if (!read_boot_record() || contains_ebr() || is_protective_mbr() || !initialize()) - return; - - auto& header = this->header(); - for (size_t index = 0; index < 4; index++) { - auto& entry = header.entry[index]; - if (entry.offset == 0x00) { - continue; - } - // We have to place these in stack variables, since try_empend will try to take a reference to them, which is UB (since they're gnu::packed and unaligned) - u64 const block_offset = entry.offset; - u64 const block_limit = (entry.offset + entry.length) - 1; - u8 const partition_type = entry.type; - MUST(m_partitions.try_empend(block_offset, block_limit, partition_type)); - } - m_valid = true; -} - -MBRPartitionTable::~MBRPartitionTable() = default; - -MBRPartitionTable::Header const& MBRPartitionTable::header() const -{ - return *(MBRPartitionTable::Header const*)m_cached_header.data(); -} - -bool MBRPartitionTable::initialize() -{ - auto& header = this->header(); - dbgln_if(MBR_DEBUG, "Master Boot Record: mbr_signature={:#08x}", header.mbr_signature); - if (header.mbr_signature != MBR_SIGNATURE) { - dbgln("Master Boot Record: invalid signature"); - return false; - } - return true; -} - -bool MBRPartitionTable::contains_ebr() const -{ - for (int i = 0; i < 4; i++) { - if (header().entry[i].type == EBR_CHS_CONTAINER || header().entry[i].type == EBR_LBA_CONTAINER) - return true; - } - return false; -} - -bool MBRPartitionTable::is_protective_mbr() const -{ - return header().entry[0].type == MBR_PROTECTIVE; -} - -} diff --git a/Userland/Libraries/LibPartition/MBRPartitionTable.h b/Userland/Libraries/LibPartition/MBRPartitionTable.h deleted file mode 100644 index 1c83eff58cd..00000000000 --- a/Userland/Libraries/LibPartition/MBRPartitionTable.h +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright (c) 2020-2022, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include - -namespace Partition { - -class MBRPartitionTable : public PartitionTable { -public: - struct [[gnu::packed]] Entry { - u8 status; - u8 chs1[3]; - u8 type; - u8 chs2[3]; - u32 offset; - u32 length; - }; - struct [[gnu::packed]] Header { - u8 code1[218]; - u16 ts_zero; - u8 ts_drive; - u8 ts_seconds; - u8 ts_minutes; - u8 ts_hours; - u8 code2[216]; - u32 disk_signature; - u16 disk_signature_zero; - Entry entry[4]; - u16 mbr_signature; - }; - -public: - ~MBRPartitionTable(); - - static ErrorOr> try_to_initialize(PartitionableDevice); - static OwnPtr try_to_initialize(PartitionableDevice, u32 start_lba); - explicit MBRPartitionTable(PartitionableDevice); - MBRPartitionTable(PartitionableDevice, u32 start_lba); - - bool is_protective_mbr() const; - bool contains_ebr() const; - virtual bool is_valid() const override { return m_valid; } - -protected: - Header const& header() const; - bool is_header_valid() const { return m_header_valid; } - -private: - bool read_boot_record(); - bool initialize(); - bool m_valid { false }; - bool m_header_valid { false }; - u32 const m_start_lba; - ByteBuffer m_cached_header; -}; - -} diff --git a/Userland/Libraries/LibPartition/PartitionTable.cpp b/Userland/Libraries/LibPartition/PartitionTable.cpp deleted file mode 100644 index f7ab34df2b9..00000000000 --- a/Userland/Libraries/LibPartition/PartitionTable.cpp +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright (c) 2020, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include - -#ifndef KERNEL -# include -#endif - -namespace Partition { - -PartitionTable::PartitionTable(PartitionableDevice&& device) - : m_device(move(device)) -{ -} - -Optional PartitionTable::partition(unsigned index) const -{ - if (index > partitions_count()) - return {}; - return m_partitions[index]; -} - -} diff --git a/Userland/Libraries/LibPartition/PartitionTable.h b/Userland/Libraries/LibPartition/PartitionTable.h deleted file mode 100644 index e35be0cae2b..00000000000 --- a/Userland/Libraries/LibPartition/PartitionTable.h +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright (c) 2020-2022, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include - -namespace Partition { - -class PartitionTable { -public: - Optional partition(unsigned index) const; - size_t partitions_count() const { return m_partitions.size(); } - virtual ~PartitionTable() = default; - virtual bool is_valid() const = 0; - - Vector partitions() const { return m_partitions; } - size_t block_size() const { return m_device.block_size(); } - -protected: - explicit PartitionTable(PartitionableDevice&&); - PartitionableDevice m_device; - - Vector m_partitions; -}; - -} diff --git a/Userland/Libraries/LibPartition/PartitionableDevice.cpp b/Userland/Libraries/LibPartition/PartitionableDevice.cpp deleted file mode 100644 index d23edbc921f..00000000000 --- a/Userland/Libraries/LibPartition/PartitionableDevice.cpp +++ /dev/null @@ -1,102 +0,0 @@ -/* - * Copyright (c) 2023, Ben Wiederhake - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include - -#ifndef KERNEL -# include -#endif - -namespace Partition { - -#ifdef KERNEL -ErrorOr PartitionableDevice::create(Kernel::StorageDevice& device) -{ - return PartitionableDevice(device); -} -#else -ErrorOr PartitionableDevice::create(MaybeOwned device_file) -{ - VERIFY(device_file.ptr() != nullptr); - size_t block_size; - int rc = ioctl(device_file->fd(), STORAGE_DEVICE_GET_BLOCK_SIZE, &block_size); - if (rc < 0) - return Error::from_string_view("ioctl on device failed"sv); - return PartitionableDevice(move(device_file), block_size); -} -#endif - -#ifdef KERNEL -PartitionableDevice::PartitionableDevice(Kernel::StorageDevice& device) - : m_device(device) -{ -} -#else -PartitionableDevice::PartitionableDevice(MaybeOwned device_file, size_t block_size) - : m_device_file(move(device_file)) - , m_block_size(block_size) -{ -} -#endif - -#ifdef KERNEL -PartitionableDevice PartitionableDevice::clone_unowned() -{ - return PartitionableDevice(m_device); -} -#else -PartitionableDevice PartitionableDevice::clone_unowned() -{ - return PartitionableDevice(MaybeOwned(*m_device_file), m_block_size); -} -#endif - -#ifdef KERNEL -ErrorOr PartitionableDevice::clone_owned() -{ - return PartitionableDevice(m_device); -} -#else -ErrorOr PartitionableDevice::clone_owned() -{ - auto cloned_file = TRY(Core::File::adopt_fd(m_device_file->fd(), Core::File::OpenMode::Read, Core::File::ShouldCloseFileDescriptor::No)); - return PartitionableDevice(move(cloned_file), m_block_size); -} -#endif - -#ifdef KERNEL -size_t PartitionableDevice::block_size() const -{ - return m_device.block_size(); -} -#else -size_t PartitionableDevice::block_size() const -{ - return m_block_size; -} -#endif - -#ifdef KERNEL -ErrorOr PartitionableDevice::read_block(size_t block_index, Bytes block_buffer) -{ - VERIFY(block_buffer.size() == block_size()); - auto buffer = UserOrKernelBuffer::for_kernel_buffer(block_buffer.data()); - bool read_successful = m_device.read_block(block_index, buffer); - if (!read_successful) - return Error::from_errno(EIO); - return {}; -} -#else -ErrorOr PartitionableDevice::read_block(size_t block_index, Bytes block_buffer) -{ - VERIFY(block_buffer.size() == block_size()); - TRY(m_device_file->seek(block_index * block_size(), SeekMode::SetPosition)); - TRY(m_device_file->read_until_filled(block_buffer)); - return {}; -} -#endif - -} diff --git a/Userland/Libraries/LibPartition/PartitionableDevice.h b/Userland/Libraries/LibPartition/PartitionableDevice.h deleted file mode 100644 index d557b5ef717..00000000000 --- a/Userland/Libraries/LibPartition/PartitionableDevice.h +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright (c) 2023, Ben Wiederhake - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include - -#ifdef KERNEL -# include -#else -# include -# include -#endif - -namespace Partition { - -class PartitionableDevice { - AK_MAKE_NONCOPYABLE(PartitionableDevice); - -public: -#ifdef KERNEL - PartitionableDevice(Kernel::StorageDevice&); - // Userland doesn't get an implicit constructor. -#endif - PartitionableDevice(PartitionableDevice&&) = default; - // Unused, and "move out of reference" isn't well-defined anyway: - PartitionableDevice& operator=(PartitionableDevice&&) = delete; - -#ifdef KERNEL - static ErrorOr create(Kernel::StorageDevice& device); -#else - static ErrorOr create(MaybeOwned device_file); -#endif - ~PartitionableDevice() = default; - - PartitionableDevice clone_unowned(); - ErrorOr clone_owned(); - size_t block_size() const; - ErrorOr read_block(size_t block_index, Bytes block_buffer); - -private: -#ifdef KERNEL - Kernel::StorageDevice& m_device; -#else - explicit PartitionableDevice(MaybeOwned, size_t block_size); - MaybeOwned m_device_file; - size_t m_block_size; -#endif -}; - -} diff --git a/Userland/Libraries/LibSanitizer/CMakeLists.txt b/Userland/Libraries/LibSanitizer/CMakeLists.txt deleted file mode 100644 index b4d02cc294f..00000000000 --- a/Userland/Libraries/LibSanitizer/CMakeLists.txt +++ /dev/null @@ -1,11 +0,0 @@ -set(SOURCES - UBSanitizer.cpp - ../LibC/ssp.cpp -) - -serenity_libc(LibUBSanitizer ubsan) -add_dependencies(LibUBSanitizer install_libc_headers) -target_link_options(LibUBSanitizer PRIVATE -nostdlib) - -add_library(DynamicLoader_LibUBSanitizer STATIC UBSanitizer.cpp) -target_link_libraries(DynamicLoader_LibUBSanitizer PUBLIC DynamicLoader_CompileOptions) diff --git a/Userland/Libraries/LibSanitizer/UBSanitizer.cpp b/Userland/Libraries/LibSanitizer/UBSanitizer.cpp deleted file mode 100644 index 46b36819d77..00000000000 --- a/Userland/Libraries/LibSanitizer/UBSanitizer.cpp +++ /dev/null @@ -1,504 +0,0 @@ -/* - * Copyright (c) 2021, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include - -using namespace AK::UBSanitizer; - -Atomic AK::UBSanitizer::g_ubsan_is_deadly; - -#define WARNLN_AND_DBGLN(fmt, ...) \ - warnln(fmt, ##__VA_ARGS__); \ - dbgln("\x1B[31m" fmt "\x1B[0m", ##__VA_ARGS__); - -#define ABORT_ALWAYS() \ - do { \ - WARNLN_AND_DBGLN("UBSAN: This error is not recoverable"); \ - abort(); \ - } while (0) - -// FIXME: Dump backtrace of this process (with symbols? without symbols?) in case the user wants non-deadly UBSAN -// Should probably go through the kernel for SC_dump_backtrace, then access the loader's symbol tables -// rather than going through the symbolizer service? -#define ABORT_IF_DEADLY() \ - do { \ - if (g_ubsan_is_deadly.load(AK::MemoryOrder::memory_order_acquire)) { \ - WARNLN_AND_DBGLN("UBSAN: UB is configured to be deadly"); \ - abort(); \ - } \ - } while (0) - -extern "C" { - -[[gnu::constructor]] static void init_ubsan_options() -{ - auto const* options_ptr = getenv("UBSAN_OPTIONS"); - auto options = options_ptr != NULL ? StringView { options_ptr, strlen(options_ptr) } : StringView {}; - // FIXME: Parse more options and complain about invalid options - if (!options.is_null()) { - if (options.contains("halt_on_error=1"sv)) - g_ubsan_is_deadly.store(true, AK::MemoryOrder::memory_order_relaxed); - else if (options.contains("halt_on_error=0"sv)) - g_ubsan_is_deadly.store(false, AK::MemoryOrder::memory_order_relaxed); - } -} - -static void print_location(SourceLocation const& location) -{ - if (!location.filename()) { - WARNLN_AND_DBGLN("UBSAN: in unknown file"); - } else { - WARNLN_AND_DBGLN("UBSAN: at {}, line {}, column: {}", location.filename(), location.line(), location.column()); - } -} - -// Calls to these functions are automatically inserted by the compiler, -// so there is no point in declaring them in a header. -#pragma GCC diagnostic ignored "-Wmissing-declarations" - -static void handle_load_invalid_value(InvalidValueData& data, ValueHandle) -{ - auto location = data.location.permanently_clear(); - if (!location.needs_logging()) - return; - WARNLN_AND_DBGLN("UBSAN: load-invalid-value: {} ({}-bit)", data.type.name(), data.type.bit_width()); - print_location(location); -} -[[gnu::used]] void __ubsan_handle_load_invalid_value(InvalidValueData& data, ValueHandle handle) -{ - handle_load_invalid_value(data, handle); - ABORT_IF_DEADLY(); -} -[[gnu::used, noreturn]] void __ubsan_handle_load_invalid_value_abort(InvalidValueData& data, ValueHandle handle) -{ - handle_load_invalid_value(data, handle); - ABORT_ALWAYS(); -} - -static void handle_nonnull_arg(NonnullArgData& data) -{ - auto location = data.location.permanently_clear(); - if (!location.needs_logging()) - return; - WARNLN_AND_DBGLN("UBSAN: null pointer passed as argument {}, which is declared to never be null", data.argument_index); - print_location(location); -} - -[[gnu::used]] void __ubsan_handle_nonnull_arg(NonnullArgData& data) -{ - handle_nonnull_arg(data); - ABORT_IF_DEADLY(); -} -[[gnu::used, noreturn]] void __ubsan_handle_nonnull_arg_abort(NonnullArgData& data) -{ - handle_nonnull_arg(data); - ABORT_ALWAYS(); -} - -static void handle_nullability_arg(NonnullArgData& data) -{ - auto location = data.location.permanently_clear(); - if (!location.needs_logging()) - return; - WARNLN_AND_DBGLN("UBSAN: null pointer passed as argument {}, which is declared to never be null", data.argument_index); - print_location(location); -} -[[gnu::used]] void __ubsan_handle_nullability_arg(NonnullArgData& data) -{ - handle_nullability_arg(data); - ABORT_IF_DEADLY(); -} -[[gnu::used, noreturn]] void __ubsan_handle_nullability_arg_abort(NonnullArgData& data) -{ - handle_nullability_arg(data); - ABORT_ALWAYS(); -} - -static void handle_nonnull_return_v1(NonnullReturnData const&, SourceLocation& location) -{ - auto loc = location.permanently_clear(); - if (!loc.needs_logging()) - return; - WARNLN_AND_DBGLN("UBSAN: null pointer return from function declared to never return null"); - print_location(loc); -} -[[gnu::used]] void __ubsan_handle_nonnull_return_v1(NonnullReturnData const& data, SourceLocation& location) -{ - handle_nonnull_return_v1(data, location); - ABORT_IF_DEADLY(); -} -[[gnu::used, noreturn]] void __ubsan_handle_nonnull_return_v1_abort(NonnullReturnData const& data, SourceLocation& location) -{ - handle_nonnull_return_v1(data, location); - ABORT_ALWAYS(); -} - -static void handle_nullability_return_v1(NonnullReturnData const&, SourceLocation& location) -{ - auto loc = location.permanently_clear(); - if (!loc.needs_logging()) - return; - WARNLN_AND_DBGLN("UBSAN: null pointer return from function declared to never return null"); - print_location(loc); -} -[[gnu::used]] void __ubsan_handle_nullability_return_v1(NonnullReturnData const& data, SourceLocation& location) -{ - handle_nullability_return_v1(data, location); - ABORT_IF_DEADLY(); -} -[[gnu::used, noreturn]] void __ubsan_handle_nullability_return_v1_abort(NonnullReturnData const& data, SourceLocation& location) -{ - handle_nullability_return_v1(data, location); - ABORT_ALWAYS(); -} - -static void handle_vla_bound_not_positive(VLABoundData& data, ValueHandle) -{ - auto location = data.location.permanently_clear(); - if (!location.needs_logging()) - return; - WARNLN_AND_DBGLN("UBSAN: VLA bound not positive {} ({}-bit)", data.type.name(), data.type.bit_width()); - print_location(location); -} -[[gnu::used]] void __ubsan_handle_vla_bound_not_positive(VLABoundData& data, ValueHandle handle) -{ - handle_vla_bound_not_positive(data, handle); - ABORT_IF_DEADLY(); -} -[[gnu::used, noreturn]] void __ubsan_handle_vla_bound_not_positive_abort(VLABoundData& data, ValueHandle handle) -{ - handle_vla_bound_not_positive(data, handle); - ABORT_ALWAYS(); -} - -static void handle_add_overflow(OverflowData& data, ValueHandle, ValueHandle) -{ - auto location = data.location.permanently_clear(); - if (!location.needs_logging()) - return; - WARNLN_AND_DBGLN("UBSAN: addition overflow, {} ({}-bit)", data.type.name(), data.type.bit_width()); - print_location(location); -} -[[gnu::used]] void __ubsan_handle_add_overflow(OverflowData& data, ValueHandle lhs, ValueHandle rhs) -{ - handle_add_overflow(data, lhs, rhs); - ABORT_IF_DEADLY(); -} -[[gnu::used, noreturn]] void __ubsan_handle_add_overflow_abort(OverflowData& data, ValueHandle lhs, ValueHandle rhs) -{ - handle_add_overflow(data, lhs, rhs); - ABORT_ALWAYS(); -} - -static void handle_sub_overflow(OverflowData& data, ValueHandle, ValueHandle) -{ - auto location = data.location.permanently_clear(); - if (!location.needs_logging()) - return; - WARNLN_AND_DBGLN("UBSAN: subtraction overflow, {} ({}-bit)", data.type.name(), data.type.bit_width()); - print_location(location); -} -[[gnu::used]] void __ubsan_handle_sub_overflow(OverflowData& data, ValueHandle lhs, ValueHandle rhs) -{ - handle_sub_overflow(data, lhs, rhs); - ABORT_IF_DEADLY(); -} -[[gnu::used, noreturn]] void __ubsan_handle_sub_overflow_abort(OverflowData& data, ValueHandle lhs, ValueHandle rhs) -{ - handle_sub_overflow(data, lhs, rhs); - ABORT_ALWAYS(); -} - -static void handle_negate_overflow(OverflowData& data, ValueHandle) -{ - auto location = data.location.permanently_clear(); - if (!location.needs_logging()) - return; - WARNLN_AND_DBGLN("UBSAN: negation overflow, {} ({}-bit)", data.type.name(), data.type.bit_width()); - print_location(location); -} -[[gnu::used]] void __ubsan_handle_negate_overflow(OverflowData& data, ValueHandle value) -{ - handle_negate_overflow(data, value); - ABORT_IF_DEADLY(); -} -[[gnu::used, noreturn]] void __ubsan_handle_negate_overflow_abort(OverflowData& data, ValueHandle value) -{ - handle_negate_overflow(data, value); - ABORT_ALWAYS(); -} - -static void handle_mul_overflow(OverflowData& data, ValueHandle, ValueHandle) -{ - auto location = data.location.permanently_clear(); - if (!location.needs_logging()) - return; - WARNLN_AND_DBGLN("UBSAN: multiplication overflow, {} ({}-bit)", data.type.name(), data.type.bit_width()); - print_location(location); -} -[[gnu::used]] void __ubsan_handle_mul_overflow(OverflowData& data, ValueHandle lhs, ValueHandle rhs) -{ - handle_mul_overflow(data, lhs, rhs); - ABORT_IF_DEADLY(); -} -[[gnu::used, noreturn]] void __ubsan_handle_mul_overflow_abort(OverflowData& data, ValueHandle lhs, ValueHandle rhs) -{ - handle_mul_overflow(data, lhs, rhs); - ABORT_ALWAYS(); -} - -static void handle_shift_out_of_bounds(ShiftOutOfBoundsData& data, ValueHandle, ValueHandle) -{ - auto location = data.location.permanently_clear(); - if (!location.needs_logging()) - return; - WARNLN_AND_DBGLN("UBSAN: shift out of bounds, {} ({}-bit) shifted by {} ({}-bit)", data.lhs_type.name(), data.lhs_type.bit_width(), data.rhs_type.name(), data.rhs_type.bit_width()); - print_location(location); -} -[[gnu::used]] void __ubsan_handle_shift_out_of_bounds(ShiftOutOfBoundsData& data, ValueHandle lhs, ValueHandle rhs) -{ - handle_shift_out_of_bounds(data, lhs, rhs); - ABORT_IF_DEADLY(); -} -[[gnu::used, noreturn]] void __ubsan_handle_shift_out_of_bounds_abort(ShiftOutOfBoundsData& data, ValueHandle lhs, ValueHandle rhs) -{ - handle_shift_out_of_bounds(data, lhs, rhs); - ABORT_ALWAYS(); -} - -static void handle_divrem_overflow(OverflowData& data, ValueHandle, ValueHandle) -{ - auto location = data.location.permanently_clear(); - if (!location.needs_logging()) - return; - WARNLN_AND_DBGLN("UBSAN: divrem overflow, {} ({}-bit)", data.type.name(), data.type.bit_width()); - print_location(location); -} -[[gnu::used]] void __ubsan_handle_divrem_overflow(OverflowData& data, ValueHandle lhs, ValueHandle rhs) -{ - handle_divrem_overflow(data, lhs, rhs); - ABORT_IF_DEADLY(); -} -[[gnu::used, noreturn]] void __ubsan_handle_divrem_overflow_abort(OverflowData& data, ValueHandle lhs, ValueHandle rhs) -{ - handle_divrem_overflow(data, lhs, rhs); - ABORT_ALWAYS(); -} - -static void handle_out_of_bounds(OutOfBoundsData& data, ValueHandle) -{ - auto location = data.location.permanently_clear(); - if (!location.needs_logging()) - return; - WARNLN_AND_DBGLN("UBSAN: out of bounds access into array of {} ({}-bit), index type {} ({}-bit)", data.array_type.name(), data.array_type.bit_width(), data.index_type.name(), data.index_type.bit_width()); - print_location(location); -} -[[gnu::used]] void __ubsan_handle_out_of_bounds(OutOfBoundsData& data, ValueHandle index) -{ - handle_out_of_bounds(data, index); - ABORT_IF_DEADLY(); -} -[[gnu::used, noreturn]] void __ubsan_handle_out_of_bounds_abort(OutOfBoundsData& data, ValueHandle index) -{ - handle_out_of_bounds(data, index); - ABORT_ALWAYS(); -} - -static void handle_type_mismatch_v1(TypeMismatchData& data, ValueHandle ptr) -{ - constexpr StringView kinds[] = { - "load of"sv, - "store to"sv, - "reference binding to"sv, - "member access within"sv, - "member call on"sv, - "constructor call on"sv, - "downcast of"sv, - "downcast of"sv, - "upcast of"sv, - "cast to virtual base of"sv, - "_Nonnull binding to"sv, - "dynamic operation on"sv - }; - - auto location = data.location.permanently_clear(); - if (!location.needs_logging()) - return; - - FlatPtr alignment = (FlatPtr)1 << data.log_alignment; - auto kind = kinds[data.type_check_kind]; - - if (!ptr) { - WARNLN_AND_DBGLN("UBSAN: {} null pointer of type {}", kind, data.type.name()); - } else if ((FlatPtr)ptr & (alignment - 1)) { - WARNLN_AND_DBGLN("UBSAN: {} misaligned address {:p} of type {} which requires {} byte alignment", kind, ptr, data.type.name(), alignment); - } else { - WARNLN_AND_DBGLN("UBSAN: {} address {:p} with insufficient space for type {}", kind, ptr, data.type.name()); - } - - print_location(location); -} -[[gnu::used]] void __ubsan_handle_type_mismatch_v1(TypeMismatchData& data, ValueHandle ptr) -{ - handle_type_mismatch_v1(data, ptr); - ABORT_IF_DEADLY(); -} -[[gnu::used, noreturn]] void __ubsan_handle_type_mismatch_v1_abort(TypeMismatchData& data, ValueHandle ptr) -{ - handle_type_mismatch_v1(data, ptr); - ABORT_ALWAYS(); -} - -static void handle_alignment_assumption(AlignmentAssumptionData& data, ValueHandle pointer, ValueHandle alignment, ValueHandle offset) -{ - auto location = data.location.permanently_clear(); - if (!location.needs_logging()) - return; - if (offset) { - WARNLN_AND_DBGLN( - "UBSAN: assumption of {:p} byte alignment (with offset of {:p} byte) for pointer {:p}" - "of type {} failed", - alignment, offset, pointer, data.type.name()); - } else { - WARNLN_AND_DBGLN("UBSAN: assumption of {:p} byte alignment for pointer {:p}" - "of type {} failed", - alignment, pointer, data.type.name()); - } - - print_location(location); -} -[[gnu::used]] void __ubsan_handle_alignment_assumption(AlignmentAssumptionData& data, ValueHandle pointer, ValueHandle alignment, ValueHandle offset) -{ - handle_alignment_assumption(data, pointer, alignment, offset); - ABORT_IF_DEADLY(); -} -[[gnu::used, noreturn]] void __ubsan_handle_alignment_assumption_abort(AlignmentAssumptionData& data, ValueHandle pointer, ValueHandle alignment, ValueHandle offset) -{ - handle_alignment_assumption(data, pointer, alignment, offset); - ABORT_ALWAYS(); -} - -[[gnu::used, noreturn]] void __ubsan_handle_builtin_unreachable(UnreachableData& data) -{ - WARNLN_AND_DBGLN("UBSAN: execution reached an unreachable program point"); - print_location(data.location); - ABORT_ALWAYS(); -} - -[[gnu::used, noreturn]] void __ubsan_handle_missing_return(UnreachableData& data) -{ - WARNLN_AND_DBGLN("UBSAN: execution reached the end of a value-returning function without returning a value"); - print_location(data.location); - ABORT_ALWAYS(); -} - -static void handle_implicit_conversion(ImplicitConversionData& data, ValueHandle, ValueHandle) -{ - auto location = data.location.permanently_clear(); - if (!location.needs_logging()) - return; - char const* src_signed = data.from_type.is_signed() ? "" : "un"; - char const* dst_signed = data.to_type.is_signed() ? "" : "un"; - WARNLN_AND_DBGLN("UBSAN: implicit conversion from type {} ({}-bit, {}signed) to type {} ({}-bit, {}signed)", - data.from_type.name(), data.from_type.bit_width(), src_signed, data.to_type.name(), data.to_type.bit_width(), dst_signed); - print_location(location); -} -[[gnu::used]] void __ubsan_handle_implicit_conversion(ImplicitConversionData& data, ValueHandle from, ValueHandle to) -{ - handle_implicit_conversion(data, from, to); - ABORT_IF_DEADLY(); -} -[[gnu::used, noreturn]] void __ubsan_handle_implicit_conversion_abort(ImplicitConversionData& data, ValueHandle from, ValueHandle to) -{ - handle_implicit_conversion(data, from, to); - ABORT_ALWAYS(); -} - -static void handle_invalid_builtin(InvalidBuiltinData& data) -{ - auto location = data.location.permanently_clear(); - if (!location.needs_logging()) - return; - WARNLN_AND_DBGLN("UBSAN: passing invalid argument"); - print_location(location); -} -[[gnu::used]] void __ubsan_handle_invalid_builtin(InvalidBuiltinData& data) -{ - handle_invalid_builtin(data); - ABORT_IF_DEADLY(); -} -[[gnu::used, noreturn]] void __ubsan_handle_invalid_builtin_abort(InvalidBuiltinData& data) -{ - handle_invalid_builtin(data); - ABORT_ALWAYS(); -} - -static void handle_pointer_overflow(PointerOverflowData& data, ValueHandle base, ValueHandle result) -{ - auto location = data.location.permanently_clear(); - if (!location.needs_logging()) - return; - if (base == 0 && result == 0) { - WARNLN_AND_DBGLN("UBSAN: applied zero offset to nullptr"); - } else if (base == 0 && result != 0) { - WARNLN_AND_DBGLN("UBSAN: applied non-zero offset {:p} to nullptr", result); - } else if (base != 0 && result == 0) { - WARNLN_AND_DBGLN("UBSAN: applying non-zero offset to non-null pointer {:p} produced null pointer", base); - } else { - WARNLN_AND_DBGLN("UBSAN: addition of unsigned offset to {:p} overflowed to {:p}", base, result); - } - print_location(location); -} -[[gnu::used]] void __ubsan_handle_pointer_overflow(PointerOverflowData& data, ValueHandle base, ValueHandle result) -{ - handle_pointer_overflow(data, base, result); - ABORT_IF_DEADLY(); -} -[[gnu::used, noreturn]] void __ubsan_handle_pointer_overflow_abort(PointerOverflowData& data, ValueHandle base, ValueHandle result) -{ - handle_pointer_overflow(data, base, result); - ABORT_ALWAYS(); -} - -static void handle_float_cast_overflow(FloatCastOverflowData& data, ValueHandle) -{ - auto location = data.location.permanently_clear(); - if (!location.needs_logging()) - return; - WARNLN_AND_DBGLN("UBSAN: overflow when casting from {} to {}", data.from_type.name(), data.to_type.name()); - print_location(location); -} -[[gnu::used]] void __ubsan_handle_float_cast_overflow(FloatCastOverflowData& data, ValueHandle value) -{ - handle_float_cast_overflow(data, value); - ABORT_IF_DEADLY(); -} -[[gnu::used, noreturn]] void __ubsan_handle_float_cast_overflow_abort(FloatCastOverflowData& data, ValueHandle value) -{ - handle_float_cast_overflow(data, value); - ABORT_ALWAYS(); -} - -static void handle_function_type_mismatch(FunctionTypeMismatchData& data, ValueHandle) -{ - auto location = data.location.permanently_clear(); - if (!location.needs_logging()) - return; - WARNLN_AND_DBGLN("UBSAN: call to function through pointer to incorrect function type {}", data.type.name()); - print_location(location); -} -[[gnu::used]] void __ubsan_handle_function_type_mismatch(FunctionTypeMismatchData& data, ValueHandle value) -{ - handle_function_type_mismatch(data, value); - ABORT_IF_DEADLY(); -} -[[gnu::used, noreturn]] void __ubsan_handle_function_type_mismatch_abort(FunctionTypeMismatchData& data, ValueHandle value) -{ - handle_function_type_mismatch(data, value); - ABORT_ALWAYS(); -} -} // extern "C" diff --git a/Userland/Libraries/LibSemVer/CMakeLists.txt b/Userland/Libraries/LibSemVer/CMakeLists.txt deleted file mode 100644 index 6e489f1239d..00000000000 --- a/Userland/Libraries/LibSemVer/CMakeLists.txt +++ /dev/null @@ -1,5 +0,0 @@ -set(SOURCES - SemVer.cpp -) - -serenity_lib(LibSemVer semver) diff --git a/Userland/Libraries/LibSemVer/SemVer.cpp b/Userland/Libraries/LibSemVer/SemVer.cpp deleted file mode 100644 index e0db62e70eb..00000000000 --- a/Userland/Libraries/LibSemVer/SemVer.cpp +++ /dev/null @@ -1,303 +0,0 @@ -/* - * Copyright (c) 2023, Gurkirat Singh - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include -#include - -namespace SemVer { -String SemVer::suffix() const -{ - StringBuilder sb; - if (!m_prerelease_identifiers.is_empty()) - sb.appendff("-{}", prerelease()); - if (!m_build_metadata_identifiers.is_empty()) - sb.appendff("+{}", build_metadata()); - return sb.to_string().release_value_but_fixme_should_propagate_errors(); -} - -String SemVer::to_string() const -{ - return String::formatted("{}{}{}{}{}{}", m_major, m_number_separator, m_minor, m_number_separator, m_patch, suffix()).release_value_but_fixme_should_propagate_errors(); -} - -SemVer SemVer::bump(BumpType type) const -{ - switch (type) { - case BumpType::Major: - return SemVer(m_major + 1, 0, 0, m_number_separator); - case BumpType::Minor: - return SemVer(m_major, m_minor + 1, 0, m_number_separator); - case BumpType::Patch: - return SemVer(m_major, m_minor, m_patch + 1, m_number_separator); - case BumpType::Prerelease: { - Vector prerelease_identifiers = m_prerelease_identifiers; - bool is_found = false; - - // Unlike comparision, prerelease bumps take from RTL. - for (auto& identifier : AK::ReverseWrapper::in_reverse(prerelease_identifiers)) { - auto numeric_identifier = identifier.to_number(); - if (numeric_identifier.has_value()) { - is_found = true; - identifier = String::formatted("{}", numeric_identifier.value() + 1).release_value_but_fixme_should_propagate_errors(); - break; - } - } - - // Append 0 identifier if there is no numeric found to be bumped. - if (!is_found) - prerelease_identifiers.append("0"_string); - - return SemVer(m_major, m_minor, m_patch, m_number_separator, prerelease_identifiers, {}); - } - default: - VERIFY_NOT_REACHED(); - } -} - -bool SemVer::is_same(SemVer const& other, CompareType compare_type) const -{ - switch (compare_type) { - case CompareType::Major: - return m_major == other.m_major; - case CompareType::Minor: - return m_major == other.m_major && m_minor == other.m_minor; - case CompareType::Patch: - return m_major == other.m_major && m_minor == other.m_minor && m_patch == other.m_patch; - default: - // Build metadata MUST be ignored when determining version precedence. - return m_major == other.m_major && m_minor == other.m_minor && m_patch == other.m_patch && prerelease() == other.prerelease(); - } -} - -bool SemVer::is_greater_than(SemVer const& other) const -{ - // Priortize the normal version string. - // Precedence is determined by the first difference when comparing them from left to right. - // Major > Minor > Patch - if (m_major > other.m_major || m_minor > other.m_minor || m_patch > other.m_patch) - return true; - - // When major, minor, and patch are equal, a pre-release version has lower precedence than a normal version. - // Example: 1.0.0-alpha < 1.0.0 - if (prerelease() == other.prerelease() || other.prerelease().is_empty()) - return false; - if (prerelease().is_empty()) - return true; - - // Both the versions have non-zero length of pre-release identifiers. - for (size_t i = 0; i < min(prerelease_identifiers().size(), other.prerelease_identifiers().size()); ++i) { - auto const this_numerical_identifier = m_prerelease_identifiers[i].to_number(); - auto const other_numerical_identifier = other.m_prerelease_identifiers[i].to_number(); - - // 1. Identifiers consisting of only digits are compared numerically. - if (this_numerical_identifier.has_value() && other_numerical_identifier.has_value()) { - auto const this_value = this_numerical_identifier.value(); - auto const other_value = other_numerical_identifier.value(); - - if (this_value == other_value) { - continue; - } - return this_value > other_value; - } - - // 2. Identifiers with letters or hyphens are compared lexically in ASCII sort order. - if (!this_numerical_identifier.has_value() && !other_numerical_identifier.has_value()) { - if (m_prerelease_identifiers[i] == other.m_prerelease_identifiers[i]) { - continue; - } - return m_prerelease_identifiers[i] > other.m_prerelease_identifiers[i]; - } - - // 3. Numeric identifiers always have lower precedence than non-numeric identifiers. - if (this_numerical_identifier.has_value() && !other_numerical_identifier.has_value()) - return false; - if (!this_numerical_identifier.has_value() && other_numerical_identifier.has_value()) - return true; - } - - // 4. If all of the preceding identifiers are equal, larger set of pre-release fields has a higher precedence than a smaller set. - return m_prerelease_identifiers.size() > other.m_prerelease_identifiers.size(); -} - -bool SemVer::satisfies(StringView const& semver_spec) const -{ - GenericLexer lexer(semver_spec.trim_whitespace()); - if (lexer.tell_remaining() == 0) - return false; - - auto compare_op = lexer.consume_until([](auto const& ch) { return ch >= '0' && ch <= '9'; }); - - auto spec_version = MUST(from_string_view(lexer.consume_all())); - // Lenient compare, tolerance for any patch and pre-release. - if (compare_op.is_empty()) - return is_same(spec_version, CompareType::Minor); - if (compare_op == "!="sv) - return !is_same(spec_version); - - // Adds strictness based on number of equal sign. - if (compare_op == "="sv) - return is_same(spec_version, CompareType::Patch); - // Exact version string match. - if (compare_op == "=="sv) - return is_same(spec_version); - - // Current version is greater than spec. - if (compare_op == ">"sv) - return is_greater_than(spec_version); - if (compare_op == "<"sv) - return is_lesser_than(spec_version); - if (compare_op == ">="sv) - return is_same(spec_version) || is_greater_than(spec_version); - if (compare_op == "<="sv) - return is_same(spec_version) || !is_greater_than(spec_version); - - return false; -} - -ErrorOr from_string_view(StringView const& version, char normal_version_separator) -{ - if (is_ascii_space(normal_version_separator) || is_ascii_digit(normal_version_separator)) { - return Error::from_string_view("Version separator can't be a space or digit character"sv); - } - - if (version.count(normal_version_separator) < 2) - return Error::from_string_view("Insufficient occurrences of version separator"sv); - - if (version.count('+') > 1) - return Error::from_string_view("Build metadata must be defined at most once"sv); - - // Checks for the bad charaters - // Spec: https://semver.org/#backusnaur-form-grammar-for-valid-semver-versions - auto trimmed_version = version.trim_whitespace(); - for (auto const& code_point : trimmed_version.bytes()) { - if (is_ascii_space(code_point) || code_point == '_') { - return Error::from_string_view("Bad characters found in the version string"sv); - } - } - - GenericLexer lexer(trimmed_version); - if (lexer.tell_remaining() == 0) - return Error::from_string_view("Version string is empty"sv); - - // Parse the normal version parts. - // https://semver.org/#spec-item-2 - auto version_part = lexer.consume_until(normal_version_separator).to_number(); - if (!version_part.has_value()) - return Error::from_string_view("Major version is not numeric"sv); - auto version_major = version_part.value(); - - lexer.consume(); - - version_part = lexer.consume_until(normal_version_separator).to_number(); - if (!version_part.has_value()) - return Error::from_string_view("Minor version is not numeric"sv); - auto version_minor = version_part.value(); - - lexer.consume(); - - version_part = lexer.consume_while([](char ch) { return ch >= '0' && ch <= '9'; }).to_number(); - if (!version_part.has_value()) - return Error::from_string_view("Patch version is not numeric"sv); - auto version_patch = version_part.value(); - - if (lexer.is_eof()) - return SemVer(version_major, version_minor, version_patch, normal_version_separator); - - Vector build_metadata_identifiers; - Vector prerelease_identifiers; - - auto process_build_metadata = [&lexer, &build_metadata_identifiers]() -> ErrorOr { - // Function body strictly adheres to the spec - // Spec: https://semver.org/#spec-item-10 - if (lexer.is_eof()) { - return Error::from_string_view("Build metadata can't be empty"sv); - } - - auto build_metadata = TRY(String::from_utf8(lexer.consume_all())); - build_metadata_identifiers = TRY(build_metadata.split('.')); - - // Because there is no mention about leading zero in the spec, only empty check is used - for (auto& identifier : build_metadata_identifiers) { - if (identifier.is_empty()) { - return Error::from_string_view("Build metadata identifier must be non empty string"sv); - } - } - - return {}; - }; - - switch (lexer.consume()) { - case '+': { - // Build metadata always starts with the + symbol after normal version string. - TRY(process_build_metadata()); - break; - } - case '-': { - // Pre-releases always start with the - symbol after normal version string. - // Spec: https://semver.org/#spec-item-9 - if (lexer.is_eof()) - return Error::from_string_view("Pre-release can't be empty"sv); - - auto prerelease = TRY(String::from_utf8(lexer.consume_until('+'))); - - constexpr auto is_valid_identifier = [](String const& identifier) { - for (auto const& code_point : identifier.code_points()) { - if (!is_ascii_alphanumeric(code_point) && code_point != '-') { - return false; - } - } - return true; - }; - // Parts of prerelease (identitifers) are separated by dot (.) - prerelease_identifiers = TRY(prerelease.split('.')); - for (auto const& prerelease_identifier : prerelease_identifiers) { - // Empty identifiers are not allowed. - if (prerelease_identifier.is_empty()) - return Error::from_string_view("Prerelease identifier can't be empty"sv); - - // If there are multiple digits, it can't start with 0 digit. - // 1.2.3-0 or 1.2.3-0is.legal are valid, but not 1.2.3-00 or 1.2.3-01 - auto identifier_bytes = prerelease_identifier.bytes(); - if (identifier_bytes.size() > 1 && prerelease_identifier.starts_with('0') && is_ascii_digit(identifier_bytes[1])) - return Error::from_string_view("Prerelease identifier has leading redundant zeroes"sv); - - // Validate identifier against charset - if (!is_valid_identifier(prerelease_identifier)) - return Error::from_string_view("Characters in prerelease identifier must be either hyphen (-), dot (.) or alphanumeric"sv); - } - - if (!lexer.is_eof()) { - // This would invalidate the following versions. - // 1.2.3-pre$ss 1.2.3-pre.1.0*build-meta - if (lexer.consume() != '+') { - return Error::from_string_view("After processing pre-release, only + character is allowed for build metadata information"sv); - } - - // Process the pending build metadata information, ignoring invalids like following. - // 1.2.3-pre+ is not a valid version. - TRY(process_build_metadata()); - } - break; - } - default: - // TODO: Add context information like actual character (peek) and its index, use the following format. - // "Expected prerelease (-) or build metadata (+) character at {}. Found {}" - return Error::from_string_view("Malformed version syntax. Expected + or - characters"sv); - } - - return SemVer(version_major, version_minor, version_patch, normal_version_separator, prerelease_identifiers, build_metadata_identifiers); -} - -bool is_valid(StringView const& version, char normal_version_separator) -{ - auto result = from_string_view(version, normal_version_separator); - return !result.is_error() && result.release_value().to_string() == version; -} -} diff --git a/Userland/Libraries/LibSemVer/SemVer.h b/Userland/Libraries/LibSemVer/SemVer.h deleted file mode 100644 index 15834a7f50e..00000000000 --- a/Userland/Libraries/LibSemVer/SemVer.h +++ /dev/null @@ -1,100 +0,0 @@ -/* - * Copyright (c) 2023, Gurkirat Singh - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include - -namespace SemVer { -enum class BumpType { - Major, - Minor, - Patch, - Prerelease, -}; - -enum class CompareType { - Exact, - Major, - Minor, - Patch -}; - -class SemVer { - -public: - SemVer(u64 major, u64 minor, u64 patch, char m_number_separator) - : m_number_separator(m_number_separator) - , m_major(major) - , m_minor(minor) - , m_patch(patch) - { - } - - SemVer(u64 major, u64 minor, u64 patch, char m_number_separator, Vector const& prereleases, Vector const& build_metadata) - : m_number_separator(m_number_separator) - , m_major(major) - , m_minor(minor) - , m_patch(patch) - , m_prerelease_identifiers(prereleases) - , m_build_metadata_identifiers(build_metadata) - { - } - - [[nodiscard]] u64 major() const { return m_major; } - [[nodiscard]] u64 minor() const { return m_minor; } - [[nodiscard]] u64 patch() const { return m_patch; } - [[nodiscard]] ReadonlySpan prerelease_identifiers() const { return m_prerelease_identifiers.span(); } - [[nodiscard]] String prerelease() const - { - return String::join('.', m_prerelease_identifiers).release_value_but_fixme_should_propagate_errors(); - } - [[nodiscard]] ReadonlySpan build_metadata_identifiers() const { return m_build_metadata_identifiers.span(); } - [[nodiscard]] String build_metadata() const { return String::join('.', m_build_metadata_identifiers).release_value_but_fixme_should_propagate_errors(); } - - [[nodiscard]] SemVer bump(BumpType) const; - - [[nodiscard]] bool is_same(SemVer const&, CompareType = CompareType::Exact) const; - [[nodiscard]] bool is_greater_than(SemVer const&) const; - [[nodiscard]] bool is_lesser_than(SemVer const& other) const { return !is_same(other) && !is_greater_than(other); } - [[nodiscard]] bool operator==(SemVer const& other) const { return is_same(other); } - [[nodiscard]] bool operator!=(SemVer const& other) const { return !is_same(other); } - [[nodiscard]] bool operator>(SemVer const& other) const { return is_lesser_than(other); } - [[nodiscard]] bool operator<(SemVer const& other) const { return is_greater_than(other); } - [[nodiscard]] bool operator>=(SemVer const& other) const { return *this == other || *this > other; } - [[nodiscard]] bool operator<=(SemVer const& other) const { return *this == other || *this < other; } - - [[nodiscard]] bool satisfies(StringView const& semver_spec) const; - - [[nodiscard]] String suffix() const; - [[nodiscard]] String to_string() const; - -private: - char m_number_separator; - u64 m_major; - u64 m_minor; - u64 m_patch; - Vector m_prerelease_identifiers; - Vector m_build_metadata_identifiers; -}; - -ErrorOr from_string_view(StringView const&, char normal_version_separator = '.'); - -bool is_valid(StringView const&, char normal_version_separator = '.'); - -} - -template<> -struct AK::Formatter : Formatter { - ErrorOr format(FormatBuilder& builder, SemVer::SemVer const& value) - { - return Formatter::format(builder, value.to_string()); - } -}; diff --git a/Userland/Libraries/LibSymbolication/CMakeLists.txt b/Userland/Libraries/LibSymbolication/CMakeLists.txt deleted file mode 100644 index 02ecbb0abd2..00000000000 --- a/Userland/Libraries/LibSymbolication/CMakeLists.txt +++ /dev/null @@ -1,6 +0,0 @@ -set(SOURCES - Symbolication.cpp -) - -serenity_lib(LibSymbolication symbolication) -target_link_libraries(LibSymbolication PRIVATE LibCore LibDebug LibELF LibFileSystem) diff --git a/Userland/Libraries/LibSymbolication/Symbolication.cpp b/Userland/Libraries/LibSymbolication/Symbolication.cpp deleted file mode 100644 index e00e68dba24..00000000000 --- a/Userland/Libraries/LibSymbolication/Symbolication.cpp +++ /dev/null @@ -1,282 +0,0 @@ -/* - * Copyright (c) 2021, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace Symbolication { - -struct CachedELF { - NonnullOwnPtr mapped_file; - NonnullOwnPtr debug_info; - NonnullOwnPtr image; -}; - -static HashMap> s_cache; - -enum class KernelBaseState { - Uninitialized, - Valid, - Invalid, -}; - -static FlatPtr s_kernel_base; -static KernelBaseState s_kernel_base_state = KernelBaseState::Uninitialized; - -Optional kernel_base() -{ - if (s_kernel_base_state == KernelBaseState::Uninitialized) { - auto file = Core::File::open("/sys/kernel/load_base"sv, Core::File::OpenMode::Read); - if (file.is_error()) { - s_kernel_base_state = KernelBaseState::Invalid; - return {}; - } - - auto file_content = file.value()->read_until_eof(); - if (file_content.is_error()) { - s_kernel_base_state = KernelBaseState::Invalid; - return {}; - } - - auto kernel_base_str = ByteString { file_content.value(), NoChomp }; - using AddressType = u64; - auto maybe_kernel_base = kernel_base_str.to_number(); - if (!maybe_kernel_base.has_value()) { - s_kernel_base_state = KernelBaseState::Invalid; - return {}; - } - s_kernel_base = maybe_kernel_base.value(); - s_kernel_base_state = KernelBaseState::Valid; - } - if (s_kernel_base_state == KernelBaseState::Invalid) - return {}; - return s_kernel_base; -} - -Optional symbolicate(ByteString const& path, FlatPtr address, IncludeSourcePosition include_source_positions) -{ - ByteString full_path = path; - if (!path.starts_with('/')) { - Array search_paths { "/usr/lib"sv, "/usr/local/lib"sv }; - bool found = false; - for (auto& search_path : search_paths) { - full_path = LexicalPath::join(search_path, path).string(); - if (FileSystem::exists(full_path)) { - found = true; - break; - } - } - if (!found) { - dbgln("Failed to find candidate for {}", path); - s_cache.set(path, {}); - return {}; - } - } - if (!s_cache.contains(full_path)) { - auto mapped_file = Core::MappedFile::map(full_path); - if (mapped_file.is_error()) { - dbgln("Failed to map {}: {}", full_path, mapped_file.error()); - s_cache.set(full_path, {}); - return {}; - } - auto elf = make(mapped_file.value()->bytes()); - if (!elf->is_valid()) { - dbgln("ELF not valid: {}", full_path); - s_cache.set(full_path, {}); - return {}; - } - auto cached_elf = make(mapped_file.release_value(), make(*elf), move(elf)); - s_cache.set(full_path, move(cached_elf)); - } - - auto it = s_cache.find(full_path); - VERIFY(it != s_cache.end()); - auto& cached_elf = it->value; - - if (!cached_elf) - return {}; - - u32 offset = 0; - auto symbol = cached_elf->debug_info->elf().symbolicate(address, &offset); - - Vector positions; - if (include_source_positions == IncludeSourcePosition::Yes) { - auto source_position_with_inlines = cached_elf->debug_info->get_source_position_with_inlines(address).release_value_but_fixme_should_propagate_errors(); - - for (auto& position : source_position_with_inlines.inline_chain) { - if (!positions.contains_slow(position)) - positions.append(position); - } - - if (source_position_with_inlines.source_position.has_value() && !positions.contains_slow(source_position_with_inlines.source_position.value())) { - positions.insert(0, source_position_with_inlines.source_position.value()); - } - } - - return Symbol { - .address = address, - .name = move(symbol), - .object = LexicalPath::basename(path), - .offset = offset, - .source_positions = move(positions), - }; -} - -Vector symbolicate_thread(pid_t pid, pid_t tid, IncludeSourcePosition include_source_positions) -{ - struct RegionWithSymbols { - FlatPtr base { 0 }; - size_t size { 0 }; - ByteString path; - }; - - Vector stack; - Vector regions; - - if (auto maybe_kernel_base = kernel_base(); maybe_kernel_base.has_value()) { - regions.append(RegionWithSymbols { - .base = maybe_kernel_base.value(), - .size = 0x3fffffff, - .path = "/boot/Kernel.debug", - }); - } - - { - auto stack_path = ByteString::formatted("/proc/{}/stacks/{}", pid, tid); - auto file_or_error = Core::File::open(stack_path, Core::File::OpenMode::Read); - if (file_or_error.is_error()) { - warnln("Could not open {}: {}", stack_path, file_or_error.error()); - return {}; - } - - auto file_content = file_or_error.value()->read_until_eof(); - if (file_content.is_error()) { - warnln("Could not read {}: {}", stack_path, file_or_error.error()); - return {}; - } - - auto json = JsonValue::from_string(file_content.value()); - if (json.is_error() || !json.value().is_array()) { - warnln("Invalid contents in {}", stack_path); - return {}; - } - - stack.ensure_capacity(json.value().as_array().size()); - for (auto& value : json.value().as_array().values()) { - stack.append(value.get_addr().value()); - } - } - - { - auto vm_path = ByteString::formatted("/proc/{}/vm", pid); - auto file_or_error = Core::File::open(vm_path, Core::File::OpenMode::Read); - if (file_or_error.is_error()) { - warnln("Could not open {}: {}", vm_path, file_or_error.error()); - return {}; - } - - auto file_content = file_or_error.value()->read_until_eof(); - if (file_content.is_error()) { - warnln("Could not read {}: {}", vm_path, file_or_error.error()); - return {}; - } - - auto json = JsonValue::from_string(file_content.value()); - if (json.is_error() || !json.value().is_array()) { - warnln("Invalid contents in {}", vm_path); - return {}; - } - - for (auto& region_value : json.value().as_array().values()) { - auto& region = region_value.as_object(); - auto name = region.get_byte_string("name"sv).value_or({}); - auto address = region.get_addr("address"sv).value_or(0); - auto size = region.get_addr("size"sv).value_or(0); - - ByteString path; - if (name == "/usr/lib/Loader.so") { - path = name; - } else if (name.ends_with(": .text"sv) || name.ends_with(": .rodata"sv)) { - auto parts = name.split_view(':'); - path = parts[0]; - } else { - continue; - } - - RegionWithSymbols r; - r.base = address; - r.size = size; - r.path = path; - regions.append(move(r)); - } - } - - Vector symbols; - bool first_frame = true; - - for (auto address : stack) { - RegionWithSymbols const* found_region = nullptr; - for (auto& region : regions) { - FlatPtr region_end; - if (Checked::addition_would_overflow(region.base, region.size)) - region_end = NumericLimits::max(); - else - region_end = region.base + region.size; - if (address >= region.base && address < region_end) { - found_region = ®ion; - break; - } - } - - if (!found_region) { - outln("{:p} ??", address); - continue; - } - - // We found an address inside of a region, but the base of that region - // may not be the base of the ELF image. For example, there could be an - // .rodata mapping at a lower address than the first .text mapping from - // the same image. look for the lowest address region with the same path. - RegionWithSymbols const* base_region = nullptr; - for (auto& region : regions) { - if (region.path != found_region->path) - continue; - if (!base_region || region.base <= base_region->base) - base_region = ®ion; - } - - FlatPtr adjusted_address = address - base_region->base; - - // We're subtracting 1 from the address because this is the return address, - // i.e. it is one instruction past the call instruction. - // However, because the first frame represents the current - // instruction pointer rather than the return address we don't - // subtract 1 for that. - auto result = symbolicate(found_region->path, adjusted_address - (first_frame ? 0 : 1), include_source_positions); - first_frame = false; - if (!result.has_value()) { - symbols.append(Symbol { - .address = address, - .source_positions = {}, - }); - continue; - } - - symbols.append(result.value()); - } - return symbols; -} - -} diff --git a/Userland/Libraries/LibSymbolication/Symbolication.h b/Userland/Libraries/LibSymbolication/Symbolication.h deleted file mode 100644 index 5c324098c02..00000000000 --- a/Userland/Libraries/LibSymbolication/Symbolication.h +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright (c) 2021, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include - -namespace Symbolication { - -struct Symbol { - FlatPtr address { 0 }; - ByteString name {}; - ByteString object {}; - u32 offset { 0 }; - Vector source_positions; - bool operator==(Symbol const&) const = default; -}; - -enum class IncludeSourcePosition { - Yes, - No -}; - -Optional kernel_base(); -Vector symbolicate_thread(pid_t pid, pid_t tid, IncludeSourcePosition = IncludeSourcePosition::Yes); -Optional symbolicate(ByteString const& path, FlatPtr address, IncludeSourcePosition = IncludeSourcePosition::Yes); - -} diff --git a/Userland/Libraries/LibSystem/CMakeLists.txt b/Userland/Libraries/LibSystem/CMakeLists.txt deleted file mode 100644 index 5fe4d7953f9..00000000000 --- a/Userland/Libraries/LibSystem/CMakeLists.txt +++ /dev/null @@ -1,10 +0,0 @@ -set(SOURCES - syscall.cpp -) - -serenity_libc(LibSystem system) -add_dependencies(LibSystem install_libc_headers) -target_link_options(LibSystem PRIVATE -nostdlib) - -add_library(DynamicLoader_LibSystem STATIC ${SOURCES}) -target_link_libraries(DynamicLoader_LibSystem PUBLIC DynamicLoader_CompileOptions) diff --git a/Userland/Libraries/LibSystem/syscall.cpp b/Userland/Libraries/LibSystem/syscall.cpp deleted file mode 100644 index e04121d7905..00000000000 --- a/Userland/Libraries/LibSystem/syscall.cpp +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright (c) 2021, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include - -extern "C" { - -uintptr_t syscall0(uintptr_t function) -{ - return Syscall::invoke((Syscall::Function)function); -} - -uintptr_t syscall1(uintptr_t function, uintptr_t arg0) -{ - return Syscall::invoke((Syscall::Function)function, arg0); -} - -uintptr_t syscall2(uintptr_t function, uintptr_t arg0, uintptr_t arg1) -{ - return Syscall::invoke((Syscall::Function)function, arg0, arg1); -} - -uintptr_t syscall3(uintptr_t function, uintptr_t arg0, uintptr_t arg1, uintptr_t arg2) -{ - return Syscall::invoke((Syscall::Function)function, arg0, arg1, arg2); -} - -uintptr_t syscall4(uintptr_t function, uintptr_t arg0, uintptr_t arg1, uintptr_t arg2, uintptr_t arg3) -{ - return Syscall::invoke((Syscall::Function)function, arg0, arg1, arg2, arg3); -} -} diff --git a/Userland/Libraries/LibSystem/syscall.h b/Userland/Libraries/LibSystem/syscall.h deleted file mode 100644 index a6cdabb227f..00000000000 --- a/Userland/Libraries/LibSystem/syscall.h +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright (c) 2021, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include - -extern "C" { - -uintptr_t syscall0(uintptr_t function); -uintptr_t syscall1(uintptr_t function, uintptr_t arg0); -uintptr_t syscall2(uintptr_t function, uintptr_t arg0, uintptr_t arg1); -uintptr_t syscall3(uintptr_t function, uintptr_t arg0, uintptr_t arg1, uintptr_t arg2); -uintptr_t syscall4(uintptr_t function, uintptr_t arg0, uintptr_t arg1, uintptr_t arg2, uintptr_t arg3); -} - -#ifdef __cplusplus - -inline uintptr_t syscall(auto function) -{ - return syscall0(function); -} - -inline uintptr_t syscall(auto function, auto arg0) -{ - return syscall1((uintptr_t)function, (uintptr_t)arg0); -} - -inline uintptr_t syscall(auto function, auto arg0, auto arg1) -{ - return syscall2((uintptr_t)function, (uintptr_t)arg0, (uintptr_t)arg1); -} - -inline uintptr_t syscall(auto function, auto arg0, auto arg1, auto arg2) -{ - return syscall3((uintptr_t)function, (uintptr_t)arg0, (uintptr_t)arg1, (uintptr_t)arg2); -} - -inline uintptr_t syscall(auto function, auto arg0, auto arg1, auto arg2, auto arg3) -{ - return syscall4((uintptr_t)function, (uintptr_t)arg0, (uintptr_t)arg1, (uintptr_t)arg2, (uintptr_t)arg3); -} - -#endif diff --git a/Userland/Libraries/LibUSBDB/CMakeLists.txt b/Userland/Libraries/LibUSBDB/CMakeLists.txt deleted file mode 100644 index b3b9de07dd0..00000000000 --- a/Userland/Libraries/LibUSBDB/CMakeLists.txt +++ /dev/null @@ -1,5 +0,0 @@ -set(SOURCES - Database.cpp -) - -serenity_lib(LibUSBDB usbdb) diff --git a/Userland/Libraries/LibUSBDB/Database.cpp b/Userland/Libraries/LibUSBDB/Database.cpp deleted file mode 100644 index dba57232cd5..00000000000 --- a/Userland/Libraries/LibUSBDB/Database.cpp +++ /dev/null @@ -1,208 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include - -#include "Database.h" - -namespace USBDB { - -RefPtr Database::open(ByteString const& filename) -{ - auto file_or_error = Core::MappedFile::map(filename); - if (file_or_error.is_error()) - return nullptr; - auto res = adopt_ref(*new Database(file_or_error.release_value())); - if (res->init() != 0) - return nullptr; - return res; -} - -StringView const Database::get_vendor(u16 vendor_id) const -{ - auto const& vendor = m_vendors.get(vendor_id); - if (!vendor.has_value()) - return ""sv; - return vendor.value()->name; -} - -StringView const Database::get_device(u16 vendor_id, u16 device_id) const -{ - auto const& vendor = m_vendors.get(vendor_id); - if (!vendor.has_value()) { - return ""sv; - } - auto const& device = vendor.value()->devices.get(device_id); - if (!device.has_value()) - return ""sv; - return device.value()->name; -} - -StringView const Database::get_interface(u16 vendor_id, u16 device_id, u16 interface_id) const -{ - auto const& vendor = m_vendors.get(vendor_id); - if (!vendor.has_value()) - return ""sv; - auto const& device = vendor.value()->devices.get(device_id); - if (!device.has_value()) - return ""sv; - auto const& interface = device.value()->interfaces.get(interface_id); - if (!interface.has_value()) - return ""sv; - return interface.value()->name; -} - -StringView const Database::get_class(u8 class_id) const -{ - auto const& xclass = m_classes.get(class_id); - if (!xclass.has_value()) - return ""sv; - return xclass.value()->name; -} - -StringView const Database::get_subclass(u8 class_id, u8 subclass_id) const -{ - auto const& xclass = m_classes.get(class_id); - if (!xclass.has_value()) - return ""sv; - auto const& subclass = xclass.value()->subclasses.get(subclass_id); - if (!subclass.has_value()) - return ""sv; - return subclass.value()->name; -} - -StringView const Database::get_protocol(u8 class_id, u8 subclass_id, u8 protocol_id) const -{ - auto const& xclass = m_classes.get(class_id); - if (!xclass.has_value()) - return ""sv; - auto const& subclass = xclass.value()->subclasses.get(subclass_id); - if (!subclass.has_value()) - return ""sv; - auto const& protocol = subclass.value()->protocols.get(protocol_id); - if (!protocol.has_value()) - return ""sv; - return protocol.value()->name; -} - -int Database::init() -{ - if (m_ready) - return 0; - - m_view = StringView { m_file->bytes() }; - - ParseMode mode = ParseMode::UnknownMode; - - OwnPtr current_vendor {}; - OwnPtr current_device {}; - OwnPtr current_class {}; - OwnPtr current_subclass {}; - - auto commit_device = [&]() { - if (current_device && current_vendor) { - auto id = current_device->id; - current_vendor->devices.set(id, current_device.release_nonnull()); - } - }; - - auto commit_vendor = [&]() { - commit_device(); - if (current_vendor) { - auto id = current_vendor->id; - m_vendors.set(id, current_vendor.release_nonnull()); - } - }; - - auto commit_subclass = [&]() { - if (current_subclass && current_class) { - auto id = current_subclass->id; - current_class->subclasses.set(id, current_subclass.release_nonnull()); - } - }; - - auto commit_class = [&]() { - commit_subclass(); - if (current_class) { - auto id = current_class->id; - m_classes.set(id, current_class.release_nonnull()); - } - }; - - auto commit_all = [&]() { - commit_vendor(); - commit_class(); - }; - - auto lines = m_view.split_view('\n'); - - for (auto& line : lines) { - if (line.length() < 2 || line[0] == '#') - continue; - - if (line[0] == 'C') { - mode = ParseMode::ClassMode; - commit_all(); - } else if ((line[0] >= '0' && line[0] <= '9') || (line[0] >= 'a' && line[0] <= 'f')) { - mode = ParseMode::VendorMode; - commit_all(); - } else if (line[0] != '\t') { - mode = ParseMode::UnknownMode; - continue; - } - - switch (mode) { - case ParseMode::VendorMode: - if (line[0] != '\t') { - commit_vendor(); - current_vendor = make(); - current_vendor->id = AK::StringUtils::convert_to_uint_from_hex(line.substring_view(0, 4)).value_or(0); - current_vendor->name = line.substring_view(6, line.length() - 6); - } else if (line[0] == '\t' && line[1] != '\t') { - commit_device(); - current_device = make(); - current_device->id = AK::StringUtils::convert_to_uint_from_hex((line.substring_view(1, 4))).value_or(0); - current_device->name = line.substring_view(7, line.length() - 7); - } else if (line[0] == '\t' && line[1] == '\t') { - auto interface = make(); - interface->interface = AK::StringUtils::convert_to_uint_from_hex((line.substring_view(2, 4))).value_or(0); - interface->name = line.substring_view(7, line.length() - 7); - current_device->interfaces.set(interface->interface, move(interface)); - } - break; - case ParseMode::ClassMode: - if (line[0] != '\t') { - commit_class(); - current_class = make(); - current_class->id = AK::StringUtils::convert_to_uint_from_hex((line.substring_view(2, 2))).value_or(0); - current_class->name = line.substring_view(6, line.length() - 6); - } else if (line[0] == '\t' && line[1] != '\t') { - commit_subclass(); - current_subclass = make(); - current_subclass->id = AK::StringUtils::convert_to_uint_from_hex((line.substring_view(1, 2))).value_or(0); - current_subclass->name = line.substring_view(5, line.length() - 5); - } else if (line[0] == '\t' && line[1] == '\t') { - auto protocol = make(); - protocol->id = AK::StringUtils::convert_to_uint_from_hex((line.substring_view(2, 2))).value_or(0); - protocol->name = line.substring_view(6, line.length() - 6); - current_subclass->protocols.set(protocol->id, move(protocol)); - } - break; - default: - break; - } - } - - commit_all(); - - m_ready = true; - - return 0; -} - -} diff --git a/Userland/Libraries/LibUSBDB/Database.h b/Userland/Libraries/LibUSBDB/Database.h deleted file mode 100644 index afc9a9b6918..00000000000 --- a/Userland/Libraries/LibUSBDB/Database.h +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include -#include -#include - -namespace USBDB { - -struct Interface { - u16 interface; - StringView name; -}; - -struct Device { - u16 id; - StringView name; - HashMap> interfaces; -}; - -struct Vendor { - u16 id; - StringView name; - HashMap> devices; -}; - -struct Protocol { - u8 id { 0 }; - StringView name {}; -}; - -struct Subclass { - u8 id { 0 }; - StringView name {}; - HashMap> protocols; -}; - -struct Class { - u8 id { 0 }; - StringView name {}; - HashMap> subclasses; -}; - -class Database : public RefCounted { -public: - static RefPtr open(ByteString const& filename); - static RefPtr open() { return open("/res/usb.ids"); } - - StringView const get_vendor(u16 vendor_id) const; - StringView const get_device(u16 vendor_id, u16 device_id) const; - StringView const get_interface(u16 vendor_id, u16 device_id, u16 interface_id) const; - StringView const get_class(u8 class_id) const; - StringView const get_subclass(u8 class_id, u8 subclass_id) const; - StringView const get_protocol(u8 class_id, u8 subclass_id, u8 protocol_id) const; - -private: - explicit Database(NonnullOwnPtr file) - : m_file(move(file)) - { - } - - int init(); - - enum ParseMode { - UnknownMode, - VendorMode, - ClassMode, - }; - - NonnullOwnPtr m_file; - StringView m_view {}; - HashMap> m_vendors; - HashMap> m_classes; - bool m_ready { false }; -}; - -} diff --git a/Userland/Libraries/LibVT/Attribute.h b/Userland/Libraries/LibVT/Attribute.h deleted file mode 100644 index e3f7409feed..00000000000 --- a/Userland/Libraries/LibVT/Attribute.h +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include - -#ifndef KERNEL -# include -#endif - -namespace VT { - -struct Attribute { - static constexpr Color default_foreground_color = Color::named(Color::ANSIColor::DefaultForeground); - static constexpr Color default_background_color = Color::named(Color::ANSIColor::DefaultBackground); - - void reset() - { - foreground_color = default_foreground_color; - background_color = default_background_color; - flags = Flags::NoAttributes; -#ifndef KERNEL - href = {}; - href_id = {}; -#endif - } - - Color foreground_color { default_foreground_color }; - Color background_color { default_background_color }; - -#ifndef KERNEL - ByteString href; - Optional href_id; -#endif - - enum class Flags : u8 { - NoAttributes = 0x00, - Bold = 0x01, - Italic = 0x02, - Underline = 0x04, - Negative = 0x08, - Blink = 0x10, - Touched = 0x20, - }; - AK_ENUM_BITWISE_FRIEND_OPERATORS(Flags); - - constexpr Color effective_background_color() const { return has_flag(flags, Flags::Negative) ? foreground_color : background_color; } - constexpr Color effective_foreground_color() const { return has_flag(flags, Flags::Negative) ? background_color : foreground_color; } - - constexpr bool is_untouched() const { return !has_flag(flags, Flags::Touched); } - - Flags flags { Flags::NoAttributes }; - - constexpr bool operator==(Attribute const& other) const - { - return foreground_color == other.foreground_color && background_color == other.background_color && flags == other.flags; - } -}; - -} diff --git a/Userland/Libraries/LibVT/CMakeLists.txt b/Userland/Libraries/LibVT/CMakeLists.txt deleted file mode 100644 index 6d07972f4c4..00000000000 --- a/Userland/Libraries/LibVT/CMakeLists.txt +++ /dev/null @@ -1,14 +0,0 @@ -set(SOURCES - Line.cpp - Terminal.cpp - TerminalWidget.cpp - EscapeSequenceParser.cpp -) - -set(GENERATED_SOURCES - EscapeSequenceStateMachine.h -) - -generate_state_machine(StateMachine.txt EscapeSequenceStateMachine.h) -serenity_lib(LibVT vt) -target_link_libraries(LibVT PRIVATE LibCore LibGUI LibGfx LibDesktop LibConfig LibURL) diff --git a/Userland/Libraries/LibVT/CharacterSet.h b/Userland/Libraries/LibVT/CharacterSet.h deleted file mode 100644 index 7b1fe9570f8..00000000000 --- a/Userland/Libraries/LibVT/CharacterSet.h +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright (c) 2021, The SerenityOS Developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include - -namespace VT { - -enum CharacterSet { - Iso_8859_1, - Null, - UserDefined, - VT100, -}; - -class CharacterSetTranslator { -public: - u32 translate_code_point(CharacterSet active_set, u32 code_point) - { - // Only translate 0x7F and lower - if (code_point > 127) - return code_point; - - // FIXME: implement other character sets - if (active_set != CharacterSet::VT100) - return code_point; - - // VT100 translation table - https://en.wikipedia.org/wiki/Box-drawing_character#Unix,_CP/M,_BBS - switch (code_point) { - case 0x6A: - return 0x2518; - case 0x6B: - return 0x2510; - case 0x6C: - return 0x250C; - case 0x6D: - return 0x2514; - case 0x6E: - return 0x253C; - case 0x71: - return 0x2500; - case 0x74: - return 0x251C; - case 0x75: - return 0x2524; - case 0x76: - return 0x2534; - case 0x77: - return 0x252C; - case 0x78: - return 0x2502; - } - return code_point; - } -}; - -} diff --git a/Userland/Libraries/LibVT/Color.h b/Userland/Libraries/LibVT/Color.h deleted file mode 100644 index 0144f55ea70..00000000000 --- a/Userland/Libraries/LibVT/Color.h +++ /dev/null @@ -1,153 +0,0 @@ -/* - * Copyright (c) 2021, Daniel Bertalan - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include - -namespace VT { - -class Color { -public: - enum class ANSIColor : u16 { - Black = 0, - Red, - Green, - Yellow, - Blue, - Magenta, - Cyan, - White, - BrightBlack, - BrightRed, - BrightGreen, - BrightYellow, - BrightBlue, - BrightMagenta, - BrightCyan, - BrightWhite, - // We use the values above to directly index into the color lookup table, - // but the ones below are handled separately. - DefaultForeground = 256, - DefaultBackground - }; - - static constexpr Color rgb(u32 rgb) - { - return Color(rgb); - } - - static constexpr Color indexed(u8 index) - { - return Color(index); - } - - static constexpr Color named(ANSIColor name) - { - return Color(name); - } - - constexpr bool is_rgb() const - { - return m_kind == Kind::RGB; - } - - constexpr bool is_indexed() const - { - return m_kind == Kind::Indexed; - } - - constexpr bool is_named() const - { - return m_kind == Kind::Named; - } - - constexpr u32 as_rgb() const - { - VERIFY(is_rgb()); - return m_value.as_rgb; - } - - constexpr u8 as_indexed() const - { - VERIFY(is_indexed()); - return m_value.as_indexed; - } - - constexpr ANSIColor as_named() const - { - VERIFY(is_named()); - return m_value.as_named; - } - - constexpr Color to_bright() const - { - if (is_named()) { - auto numeric_value = static_cast(as_named()); - if (numeric_value < 8) - return Color::named(static_cast(numeric_value + 8)); - return *this; - } else { - return *this; - } - } - - constexpr bool operator==(Color const& other) const - { - if (m_kind != other.kind()) - return false; - - switch (m_kind) { - case RGB: - return m_value.as_rgb == other.as_rgb(); - case Indexed: - return m_value.as_indexed == other.as_indexed(); - case Named: - return m_value.as_named == other.as_named(); - default: - VERIFY_NOT_REACHED(); - }; - } - - enum Kind { - RGB, - Indexed, - Named - }; - - constexpr Kind kind() const - { - return m_kind; - } - -private: - Kind m_kind; - - union { - u32 as_rgb; - u8 as_indexed; - ANSIColor as_named; - } m_value; - - constexpr Color(u32 rgb) - : m_kind(Kind::RGB) - { - m_value.as_rgb = rgb; - } - - constexpr Color(u8 index) - : m_kind(Kind::Indexed) - { - m_value.as_indexed = index; - } - - constexpr Color(ANSIColor name) - : m_kind(Kind::Named) - { - m_value.as_named = name; - } -}; -} diff --git a/Userland/Libraries/LibVT/EscapeSequenceParser.cpp b/Userland/Libraries/LibVT/EscapeSequenceParser.cpp deleted file mode 100644 index 249b5478bc2..00000000000 --- a/Userland/Libraries/LibVT/EscapeSequenceParser.cpp +++ /dev/null @@ -1,158 +0,0 @@ -/* - * Copyright (c) 2022, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include - -namespace VT { -EscapeSequenceParser::EscapeSequenceParser(EscapeSequenceExecutor& executor) - : m_executor(executor) - , m_state_machine([this](auto action, auto byte) { perform_action(action, byte); }) -{ -} - -Vector EscapeSequenceParser::osc_parameters() const -{ - VERIFY(m_osc_raw.size() >= m_osc_parameter_indexes.last()); - Vector params; - size_t prev_idx = 0; - for (auto end_idx : m_osc_parameter_indexes) { - // If the parameter is empty, we take an out of bounds index as the beginning of the Span. - // This should not be a problem as we won't dereference the 0-length Span that's created. - // Using &m_osc_raw[prev_idx] to get the start pointer checks whether we're out of bounds, - // so we would crash. - params.append({ m_osc_raw.data() + prev_idx, end_idx - prev_idx }); - prev_idx = end_idx; - } - return params; -} - -void EscapeSequenceParser::perform_action(EscapeSequenceStateMachine::Action action, u8 byte) -{ - auto advance_utf8 = [&](u8 byte) { - u32 new_code_point = m_code_point; - new_code_point <<= 6; - new_code_point |= byte & 0x3f; - return new_code_point; - }; - - switch (action) { - case EscapeSequenceStateMachine::Action::_Ignore: - break; - case EscapeSequenceStateMachine::Action::Print: - m_executor.emit_code_point((u32)byte); - break; - case EscapeSequenceStateMachine::Action::PrintUTF8: - m_executor.emit_code_point(advance_utf8(byte)); - break; - case EscapeSequenceStateMachine::Action::Execute: - m_executor.execute_control_code(byte); - break; - case EscapeSequenceStateMachine::Action::Hook: - if (m_param_vector.size() == MAX_PARAMETERS) - m_ignoring = true; - else - m_param_vector.append(m_param); - m_executor.dcs_hook(m_param_vector, intermediates(), m_ignoring, byte); - break; - case EscapeSequenceStateMachine::Action::Put: - m_executor.receive_dcs_char(byte); - break; - case EscapeSequenceStateMachine::Action::BeginUTF8: - if ((byte & 0xe0) == 0xc0) { - m_code_point = byte & 0x1f; - } else if ((byte & 0xf0) == 0xe0) { - m_code_point = byte & 0x0f; - } else if ((byte & 0xf8) == 0xf0) { - m_code_point = byte & 0x07; - } else { - dbgln("Invalid character was parsed as UTF-8 initial byte {:02x}", byte); - VERIFY_NOT_REACHED(); - } - break; - case EscapeSequenceStateMachine::Action::AdvanceUTF8: - VERIFY((byte & 0xc0) == 0x80); - m_code_point = advance_utf8(byte); - break; - case EscapeSequenceStateMachine::Action::FailUTF8: - m_executor.emit_code_point(U'�'); - break; - case EscapeSequenceStateMachine::Action::OscStart: - m_osc_raw.clear(); - m_osc_parameter_indexes.clear(); - break; - case EscapeSequenceStateMachine::Action::OscPut: - if (byte == ';') { - if (m_osc_parameter_indexes.size() == MAX_OSC_PARAMETERS) { - dbgln("EscapeSequenceParser::perform_action: shenanigans! OSC sequence has too many parameters"); - } else { - m_osc_parameter_indexes.append(m_osc_raw.size()); - } - } else { - m_osc_raw.append(byte); - } - break; - case EscapeSequenceStateMachine::Action::OscEnd: - if (m_osc_parameter_indexes.size() == MAX_OSC_PARAMETERS) { - dbgln("EscapeSequenceParser::perform_action: shenanigans! OSC sequence has too many parameters"); - } else { - m_osc_parameter_indexes.append(m_osc_raw.size()); - } - m_executor.execute_osc_sequence(osc_parameters(), byte); - break; - case EscapeSequenceStateMachine::Action::Unhook: - m_executor.execute_dcs_sequence(); - break; - case EscapeSequenceStateMachine::Action::CsiDispatch: - if (m_param_vector.size() > MAX_PARAMETERS) { - dbgln("EscapeSequenceParser::perform_action: shenanigans! CSI sequence has too many parameters"); - m_ignoring = true; - } else { - m_param_vector.append(m_param); - } - - m_executor.execute_csi_sequence(m_param_vector, intermediates(), m_ignoring, byte); - break; - - case EscapeSequenceStateMachine::Action::EscDispatch: - m_executor.execute_escape_sequence(intermediates(), m_ignoring, byte); - break; - case EscapeSequenceStateMachine::Action::Collect: - if (m_intermediate_idx == MAX_INTERMEDIATES) { - dbgln("EscapeSequenceParser::perform_action: shenanigans! escape sequence has too many intermediates"); - m_ignoring = true; - } else { - m_intermediates[m_intermediate_idx++] = byte; - } - break; - case EscapeSequenceStateMachine::Action::Param: - if (m_param_vector.size() == MAX_PARAMETERS) { - dbgln("EscapeSequenceParser::perform_action: shenanigans! escape sequence has too many parameters"); - m_ignoring = true; - } else { - if (byte == ';') { - m_param_vector.append(m_param); - m_param = 0; - } else if (byte == ':') { - dbgln("EscapeSequenceParser::perform_action: subparameters are not yet implemented"); - } else { - m_param *= 10; - m_param += (byte - '0'); - } - } - break; - case EscapeSequenceStateMachine::Action::Clear: - m_intermediate_idx = 0; - m_ignoring = false; - - m_param = 0; - m_param_vector.clear_with_capacity(); - break; - } -} -} diff --git a/Userland/Libraries/LibVT/EscapeSequenceParser.h b/Userland/Libraries/LibVT/EscapeSequenceParser.h deleted file mode 100644 index 54a96110756..00000000000 --- a/Userland/Libraries/LibVT/EscapeSequenceParser.h +++ /dev/null @@ -1,78 +0,0 @@ -/* - * Copyright (c) 2021-2022, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include -#include -#include - -namespace VT { -class EscapeSequenceExecutor { -public: - virtual ~EscapeSequenceExecutor() = default; - - using Parameters = ReadonlySpan; - using Intermediates = ReadonlyBytes; - using OscParameter = ReadonlyBytes; - using OscParameters = ReadonlySpan; - - virtual void emit_code_point(u32) = 0; - virtual void execute_control_code(u8) = 0; - virtual void execute_escape_sequence(Intermediates intermediates, bool ignore, u8 last_byte) = 0; - virtual void execute_csi_sequence(Parameters parameters, Intermediates intermediates, bool ignore, u8 last_byte) = 0; - virtual void execute_osc_sequence(OscParameters parameters, u8 last_byte) = 0; - virtual void dcs_hook(Parameters parameters, Intermediates intermediates, bool ignore, u8 last_byte) = 0; - virtual void receive_dcs_char(u8 byte) = 0; - virtual void execute_dcs_sequence() = 0; -}; - -class EscapeSequenceParser { -public: - explicit EscapeSequenceParser(EscapeSequenceExecutor&); - ~EscapeSequenceParser() = default; - - ALWAYS_INLINE void on_input(u8 byte) - { - dbgln_if(ESCAPE_SEQUENCE_DEBUG, "on_input {:02x}", byte); - m_state_machine.advance(byte); - } - -private: - static constexpr size_t MAX_INTERMEDIATES = 2; - static constexpr size_t MAX_PARAMETERS = 16; - static constexpr size_t MAX_OSC_PARAMETERS = 16; - - using Intermediates = EscapeSequenceExecutor::Intermediates; - using OscParameter = EscapeSequenceExecutor::OscParameter; - - void perform_action(EscapeSequenceStateMachine::Action, u8); - - EscapeSequenceExecutor& m_executor; - EscapeSequenceStateMachine m_state_machine; - - u32 m_code_point { 0 }; - - u8 m_intermediates[MAX_INTERMEDIATES]; - u8 m_intermediate_idx { 0 }; - - Intermediates intermediates() const { return { m_intermediates, m_intermediate_idx }; } - Vector osc_parameters() const; - - Vector m_param_vector; - unsigned m_param { 0 }; - - Vector m_osc_parameter_indexes; - Vector m_osc_raw; - - bool m_ignoring { false }; -}; - -} diff --git a/Userland/Libraries/LibVT/Line.cpp b/Userland/Libraries/LibVT/Line.cpp deleted file mode 100644 index 6c09c0056b8..00000000000 --- a/Userland/Libraries/LibVT/Line.cpp +++ /dev/null @@ -1,145 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * Copyright (c) 2022, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include - -namespace VT { - -Line::Line(size_t length) -{ - set_length(length); -} - -void Line::rewrap(size_t new_length, Line* next_line, CursorPosition* cursor, bool cursor_is_on_next_line) -{ - size_t old_length = length(); - if (old_length == new_length) - return; - - // Drop the empty cells - if (m_terminated_at.has_value() && m_cells.size() > m_terminated_at.value()) - m_cells.remove(m_terminated_at.value(), m_cells.size() - m_terminated_at.value()); - - if (!next_line) - return set_length(new_length); - - if (old_length < new_length) - take_cells_from_next_line(new_length, next_line, cursor_is_on_next_line, cursor); - else - push_cells_into_next_line(new_length, next_line, cursor_is_on_next_line, cursor); -} - -void Line::set_length(size_t new_length) -{ - m_cells.resize(new_length); - if (m_terminated_at.has_value()) - m_terminated_at = min(*m_terminated_at, new_length); -} - -void Line::push_cells_into_next_line(size_t new_length, Line* next_line, bool cursor_is_on_next_line, CursorPosition* cursor) -{ - if (is_empty()) - return; - - if (length() <= new_length) - return; - - // Push as many cells as _wouldn't_ fit into the next line. - auto cells_to_preserve = !next_line->m_terminated_at.has_value() && next_line->is_empty() ? 0 : m_terminated_at.value_or(0); - auto preserved_cells = max(new_length, cells_to_preserve); - auto cells_to_push_into_next_line = length() - preserved_cells; - if (!cells_to_push_into_next_line) - return; - - if (next_line->m_terminated_at.has_value()) - next_line->m_terminated_at = next_line->m_terminated_at.value() + cells_to_push_into_next_line; - - if (m_terminated_at.has_value() && cells_to_preserve == 0) { - m_terminated_at.clear(); - if (!next_line->m_terminated_at.has_value()) - next_line->m_terminated_at = cells_to_push_into_next_line; - } - - if (cursor) { - if (cursor_is_on_next_line) { - cursor->column += cells_to_push_into_next_line; - } else if (cursor->column >= preserved_cells) { - cursor->row++; - cursor->column = cursor->column - preserved_cells; - } - } - - MUST(next_line->m_cells.try_prepend(m_cells.span().slice_from_end(cells_to_push_into_next_line).data(), cells_to_push_into_next_line)); - m_cells.remove(m_cells.size() - cells_to_push_into_next_line, cells_to_push_into_next_line); - if (m_terminated_at.has_value()) - m_terminated_at = m_terminated_at.value() - cells_to_push_into_next_line; -} - -void Line::take_cells_from_next_line(size_t new_length, Line* next_line, bool cursor_is_on_next_line, CursorPosition* cursor) -{ - // Take as many cells as would fit from the next line - if (m_terminated_at.has_value()) - return; - - if (length() >= new_length) - return; - - auto cells_to_grab_from_next_line = min(new_length - length(), next_line->length()); - auto clear_next_line = false; - if (next_line->m_terminated_at.has_value()) { - if (cells_to_grab_from_next_line >= *next_line->m_terminated_at) { - m_terminated_at = length() + *next_line->m_terminated_at; - next_line->m_terminated_at.clear(); - clear_next_line = true; - } else { - next_line->m_terminated_at = next_line->m_terminated_at.value() - cells_to_grab_from_next_line; - } - } - - if (cells_to_grab_from_next_line) { - if (cursor && cursor_is_on_next_line) { - if (cursor->column <= cells_to_grab_from_next_line) { - cursor->row--; - cursor->column += m_cells.size(); - } else { - cursor->column -= cells_to_grab_from_next_line; - } - } - MUST(m_cells.try_append(next_line->m_cells.data(), cells_to_grab_from_next_line)); - next_line->m_cells.remove(0, cells_to_grab_from_next_line); - } - - if (clear_next_line) - next_line->m_cells.clear(); -} - -void Line::clear_range(size_t first_column, size_t last_column, Attribute const& attribute) -{ - VERIFY(first_column <= last_column); - VERIFY(last_column < m_cells.size()); - for (size_t i = first_column; i <= last_column; ++i) { - auto& cell = m_cells[i]; - if (!m_dirty) - m_dirty = cell.code_point != ' ' || cell.attribute != attribute; - cell = Cell { .code_point = ' ', .attribute = attribute }; - } -} - -bool Line::has_only_one_background_color() const -{ - if (!length()) - return true; - // FIXME: Cache this result? - auto color = attribute_at(0).effective_background_color(); - for (size_t i = 1; i < length(); ++i) { - if (attribute_at(i).effective_background_color() != color) - return false; - } - return true; -} - -} diff --git a/Userland/Libraries/LibVT/Line.h b/Userland/Libraries/LibVT/Line.h deleted file mode 100644 index aadfdbe5d5b..00000000000 --- a/Userland/Libraries/LibVT/Line.h +++ /dev/null @@ -1,109 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * Copyright (c) 2022, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include -#include -#include - -namespace VT { - -AK_TYPEDEF_DISTINCT_ORDERED_ID(u32, Mark); - -inline static constexpr Mark Unmarked = 0; - -class Line { - AK_MAKE_NONCOPYABLE(Line); - AK_MAKE_NONMOVABLE(Line); - -public: - explicit Line(size_t length); - ~Line() = default; - - struct Cell { - u32 code_point { ' ' }; - Attribute attribute; - - bool operator!=(Cell const& other) const { return code_point != other.code_point || attribute != other.attribute; } - }; - - Attribute const& attribute_at(size_t index) const { return m_cells[index].attribute; } - Attribute& attribute_at(size_t index) { return m_cells[index].attribute; } - - Cell& cell_at(size_t index) { return m_cells[index]; } - Cell const& cell_at(size_t index) const { return m_cells[index]; } - - void clear(Attribute const& attribute = Attribute()) - { - m_terminated_at.clear(); - m_mark = Unmarked; - clear_range(0, m_cells.size() - 1, attribute); - } - void clear_range(size_t first_column, size_t last_column, Attribute const& attribute = Attribute()); - bool has_only_one_background_color() const; - - bool is_empty() const - { - return !any_of(m_cells, [](auto& cell) { return cell != Cell(); }); - } - - size_t length() const - { - return m_cells.size(); - } - void set_length(size_t); - void rewrap(size_t new_length, Line* next_line, CursorPosition* cursor, bool cursor_is_on_next_line = true); - - u32 code_point(size_t index) const - { - return m_cells[index].code_point; - } - - void set_code_point(size_t index, u32 code_point) - { - if (m_terminated_at.has_value()) { - if (index > *m_terminated_at) { - m_terminated_at = index + 1; - } - } - - m_cells[index].code_point = code_point; - } - - bool is_dirty() const { return m_dirty; } - void set_dirty(bool b) { m_dirty = b; } - - Optional mark() const - { - return m_mark == Unmarked ? OptionalNone {} : Optional(m_mark); - } - void set_marked(Mark mark) - { - set_dirty(m_mark != mark); - m_mark = mark; - } - - Optional termination_column() const { return m_terminated_at; } - void set_terminated(u16 column) { m_terminated_at = column; } - -private: - void take_cells_from_next_line(size_t new_length, Line* next_line, bool cursor_is_on_next_line, CursorPosition* cursor); - void push_cells_into_next_line(size_t new_length, Line* next_line, bool cursor_is_on_next_line, CursorPosition* cursor); - - Vector m_cells; - Mark m_mark { Unmarked }; - bool m_dirty { false }; - // Note: The alignment is 8, so this member lives in the padding (that already existed before it was introduced) - [[no_unique_address]] Optional m_terminated_at; -}; - -} diff --git a/Userland/Libraries/LibVT/Position.h b/Userland/Libraries/LibVT/Position.h deleted file mode 100644 index b24e6e6a8fb..00000000000 --- a/Userland/Libraries/LibVT/Position.h +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include - -namespace VT { - -class Position { -public: - Position() = default; - Position(int row, int column) - : m_row(row) - , m_column(column) - { - } - - bool is_valid() const { return m_row >= 0 && m_column >= 0; } - int row() const { return m_row; } - int column() const { return m_column; } - - bool operator<(Position const& other) const - { - return m_row < other.m_row || (m_row == other.m_row && m_column < other.m_column); - } - - bool operator<=(Position const& other) const - { - return *this < other || *this == other; - } - - bool operator>=(Position const& other) const - { - return !(*this < other); - } - - bool operator==(Position const& other) const - { - return m_row == other.m_row && m_column == other.m_column; - } - - bool operator!=(Position const& other) const - { - return !(*this == other); - } - -private: - int m_row { -1 }; - int m_column { -1 }; -}; - -struct CursorPosition { - size_t row { 0 }; - size_t column { 0 }; - - void clamp(u16 max_row, u16 max_column) - { - row = min(row, max_row); - column = min(column, max_column); - } -}; - -} diff --git a/Userland/Libraries/LibVT/Range.h b/Userland/Libraries/LibVT/Range.h deleted file mode 100644 index bc7566d923b..00000000000 --- a/Userland/Libraries/LibVT/Range.h +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Copyright (c) 2020-2022, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include - -namespace VT { - -class Range { -public: - Range() = default; - Range(const VT::Position& start, const VT::Position& end) - : m_start(start) - , m_end(end) - { - } - - bool is_valid() const { return m_start.is_valid() && m_end.is_valid(); } - void clear() - { - m_start = {}; - m_end = {}; - } - - VT::Position& start() { return m_start; } - VT::Position& end() { return m_end; } - const VT::Position& start() const { return m_start; } - const VT::Position& end() const { return m_end; } - - Range normalized() const { return Range(normalized_start(), normalized_end()); } - - void set_start(const VT::Position& position) { m_start = position; } - void set_end(const VT::Position& position) { m_end = position; } - - void set(const VT::Position& start, const VT::Position& end) - { - m_start = start; - m_end = end; - } - - void offset_row(int delta) - { - m_start = Position(m_start.row() + delta, m_start.column()); - m_end = Position(m_end.row() + delta, m_end.column()); - } - - bool operator==(Range const& other) const - { - return m_start == other.m_start && m_end == other.m_end; - } - - bool contains(const VT::Position& position) const - { - if (!(position.row() > m_start.row() || (position.row() == m_start.row() && position.column() >= m_start.column()))) - return false; - if (!(position.row() < m_end.row() || (position.row() == m_end.row() && position.column() <= m_end.column()))) - return false; - return true; - } - -private: - VT::Position normalized_start() const { return m_start < m_end ? m_start : m_end; } - VT::Position normalized_end() const { return m_start < m_end ? m_end : m_start; } - - VT::Position m_start; - VT::Position m_end; -}; - -}; diff --git a/Userland/Libraries/LibVT/StateMachine.txt b/Userland/Libraries/LibVT/StateMachine.txt deleted file mode 100644 index 39343a0bba1..00000000000 --- a/Userland/Libraries/LibVT/StateMachine.txt +++ /dev/null @@ -1,216 +0,0 @@ -// This file is used for automatically generating the ANSI escape sequence state machine -// -// The description of the state machine is taken from https://vt100.net/emu/dec_ansi_parser -// with added support for UTF-8 parsing - -@name EscapeSequenceStateMachine -@namespace VT -@begin Ground - -@anywhere { - 0x18 => (Ground, Execute) - 0x1a => (Ground, Execute) - 0x1b => (Escape, _) -} - -Ground { - [0x00..0x17] => (_, Execute) - 0x19 => (_, Execute) - [0x1c..0x1f] => (_, Execute) - - [0x20..0x7f] => (_, Print) - - [0x80..0xc1] => (Ground, FailUTF8) - [0xc2..0xdf] => (UTF81ByteNeeded, BeginUTF8) - [0xe0..0xef] => (UTF82BytesNeeded, BeginUTF8) - [0xf0..0xf4] => (UTF83BytesNeeded, BeginUTF8) - [0xf5..0xff] => (Ground, FailUTF8) -} - -UTF81ByteNeeded { - [0x00..0x7f] => (Ground, FailUTF8) - [0x80..0xbf] => (Ground, PrintUTF8) - [0xc0..0xff] => (Ground, FailUTF8) -} - -UTF82BytesNeeded { - [0x00..0x7f] => (Ground, FailUTF8) - [0x80..0xbf] => (UTF81ByteNeeded, AdvanceUTF8) - [0xc0..0xff] => (Ground, FailUTF8) -} - -UTF83BytesNeeded { - [0x00..0x7f] => (Ground, FailUTF8) - [0x80..0xbf] => (UTF82BytesNeeded, AdvanceUTF8) - [0xc0..0xff] => (Ground, FailUTF8) -} - -Escape { - @entry Clear - - [0x00..0x17] => (_, Execute) - 0x19 => (_, Execute) - [0x1c..0x1f] => (_, Execute) - - [0x20..0x2f] => (EscapeIntermediate, Collect) - [0x30..0x4f] => (Ground, EscDispatch) - 0x50 => (DcsEntry, _) - [0x51..0x57] => (Ground, EscDispatch) - 0x58 => (SosPmApcString, _) - 0x59 => (Ground, EscDispatch) - 0x5a => (Ground, EscDispatch) - 0x5b => (CsiEntry, _) - 0x5c => (Ground, EscDispatch) - 0x5d => (OscString, _) - 0x5e => (SosPmApcString, _) - 0x5f => (SosPmApcString, _) - [0x60..0x7e] => (Ground, EscDispatch) - 0x7f => (_, _) -} - -EscapeIntermediate { - [0x00..0x17] => (_, Execute) - 0x19 => (_, Execute) - [0x1c..0x1f] => (_, Execute) - [0x20..0x2f] => (_, Collect) - [0x30..0x7e] => (Ground, EscDispatch) - 0x7f => (_, _) -} - -CsiEntry { - @entry Clear - - [0x00..0x17] => (_, Execute) - 0x19 => (_, Execute) - [0x1c..0x1f] => (_, Execute) - - [0x20..0x2f] => (CsiIntermediate, Execute) - [0x30..0x39] => (CsiParam, Param) - 0x3a => (CsiIgnore, _) - 0x3b => (CsiParam, Param) - [0x3c..0x3f] => (CsiParam, Collect) - [0x40..0x7e] => (Ground, CsiDispatch) - 0x7f => (_, _) -} - -CsiIgnore { - [0x00..0x17] => (_, Execute) - 0x19 => (_, Execute) - [0x1c..0x1f] => (_, Execute) - - [0x20..0x3f] => (_, _) - [0x40..0x7e] => (Ground, _) - 0x7f => (_, _) -} - -CsiParam { - [0x00..0x17] => (_, Execute) - 0x19 => (_, Execute) - [0x1c..0x1f] => (_, Execute) - - [0x20..0x2f] => (CsiIntermediate, Collect) - [0x30..0x39] => (_, Param) - 0x3a => (CsiIgnore, _) - 0x3b => (_, Param) - [0x3c..0x3f] => (CsiIgnore, _) - [0x40..0x7e] => (Ground, CsiDispatch) - 0x7f => (_, _) -} - -CsiIntermediate { - [0x00..0x17] => (_, Execute) - 0x19 => (_, Execute) - [0x1c..0x1f] => (_, Execute) - - [0x20..0x2f] => (_, Collect) - [0x30..0x3f] => (CsiIgnore, _) - [0x40..0x7e] => (Ground, CsiDispatch) - 0x7f => (_, _) -} - -DcsEntry { - @entry Clear - - [0x00..0x17] => (_, _) - 0x19 => (_, _) - [0x1c..0x1f] => (_, _) - - [0x20..0x2f] => (DcsIntermediate, Collect) - [0x30..0x39] => (DcsParam, Param) - 0x3a => (DcsIgnore, _) - 0x3b => (DcsParam, Param) - [0x3c..0x3f] => (DcsParam, Collect) - [0x40..0x7e] => (DcsPassthrough, _) - 0x7f => (_, _) -} - -DcsIntermediate { - [0x00..0x17] => (_, _) - 0x19 => (_, _) - [0x1c..0x1f] => (_, _) - - [0x20..0x2f] => (_, Collect) - [0x30..0x3f] => (DcsIgnore, _) - [0x40..0x7e] => (DcsPassthrough, _) - 0x7f => (_, _) -} - -DcsIgnore { - [0x00..0x17] => (_, _) - 0x19 => (_, _) - [0x1c..0x1f] => (_, _) - [0x20..0x7f] => (_, _) - 0x9c => (Ground, _) -} - -DcsParam { - [0x00..0x17] => (_, _) - 0x19 => (_, _) - [0x1c..0x1f] => (_, _) - - [0x20..0x2f] => (DcsIntermediate, Collect) - [0x30..0x39] => (_, Param) - 0x3a => (DcsIgnore, _) - 0x3b => (_, Param) - [0x3c..0x3f] => (DcsIgnore, _) - [0x40..0x7e] => (DcsPassthrough, _) - 0x7f => (_, _) -} - -DcsPassthrough { - @entry Hook - - [0x00..0x17] => (_, Put) - 0x19 => (_, Put) - [0x1c..0x1f] => (_, Put) - [0x20..0x7e] => (_, Put) - 0x7f => (_, _) - 0x9c => (Ground, _) - - @exit Unhook -} - -SosPmApcString { - [0x00..0x17] => (_, _) - 0x19 => (_, _) - [0x1c..0x1f] => (_, _) - [0x20..0x7f] => (_, _) - 0x9c => (Ground, _) -} - -OscString { - @entry OscStart - - [0x00..0x06] => (_, _) - - // While the standard says that only ST can terminate the string, - // xterm uses BEL (0x07) - 0x07 => (Ground, _) - - [0x08..0x17] => (_, _) - 0x19 => (_, _) - [0x1c..0x1f] => (_, _) - - [0x20..0xff] => (_, OscPut) - @exit OscEnd -} diff --git a/Userland/Libraries/LibVT/Terminal.cpp b/Userland/Libraries/LibVT/Terminal.cpp deleted file mode 100644 index 4864cceccfe..00000000000 --- a/Userland/Libraries/LibVT/Terminal.cpp +++ /dev/null @@ -1,1789 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * Copyright (c) 2021, Daniel Bertalan - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include -#include -#include -#ifdef KERNEL -# include -#endif - -namespace VT { - -#ifndef KERNEL -Terminal::Terminal(TerminalClient& client) -#else -Terminal::Terminal(Kernel::VirtualConsole& client) -#endif - : m_client(client) - , m_parser(*this) -{ -} - -#ifndef KERNEL -void Terminal::clear() -{ - dbgln_if(TERMINAL_DEBUG, "Clear the entire screen"); - for (size_t i = 0; i < rows(); ++i) - active_buffer()[i]->clear(); - set_cursor(0, 0); - m_client.terminal_did_perform_possibly_partial_clear(); -} - -void Terminal::clear_history() -{ - dbgln_if(TERMINAL_DEBUG, "Clear history"); - auto previous_history_size = m_history.size(); - m_history.clear(); - m_history_start = 0; - m_client.terminal_history_changed(-previous_history_size); -} - -void Terminal::clear_to_mark(Mark mark) -{ - auto cursor_row = this->cursor_row(); - ScopeGuard send_sigwinch = [&] { - set_cursor(cursor_row, 1); - mark_cursor(); - m_client.terminal_did_perform_possibly_partial_clear(); - }; - m_valid_marks.remove(mark); - - { - auto it = active_buffer().rbegin(); - size_t row = m_rows - 1; - // Skip to the cursor line. - for (size_t i = this->cursor_row() + 1; i < active_buffer().size(); ++i, row--) - ++it; - for (; it != active_buffer().rend(); ++it, row--) { - auto& line = *it; - auto line_mark = line->mark(); - auto is_target_line = line_mark == mark; - if (line_mark.has_value()) - m_valid_marks.remove(*line_mark); - line->clear(); - if (is_target_line) { - cursor_row = row; - return; - } - } - } - - // If the mark is not found, go through the history. - auto it = AK::find_if( - m_history.rbegin(), - m_history.rend(), - [mark](auto& line) { - return line->mark() == mark; - }); - auto index = it == m_history.rend() ? 0 : m_history.size() - it.index(); - m_client.terminal_history_changed(m_history.size() - index); - auto count = m_history.size() - index; - for (size_t i = 0; i < count; ++i) { - if (auto mark = m_history[index + i]->mark(); mark.has_value()) - m_valid_marks.remove(*mark); - } - m_history.remove(index, count); - cursor_row = 0; -} - -void Terminal::mark_cursor() -{ - static u32 next_mark_id { 0 }; - - auto& line = active_buffer()[cursor_row()]; - if (line->mark().has_value()) { - return; - } - - auto mark = Mark(next_mark_id++); - line->set_marked(mark); - m_valid_marks.set(mark); -} -#endif - -void Terminal::alter_ansi_mode(bool should_set, Parameters params) -{ - for (auto mode : params) { - switch (mode) { - // FIXME: implement *something* for this - default: - dbgln("Terminal::alter_ansi_mode: Unimplemented mode {} (should_set={})", mode, should_set); - break; - } - } -} - -void Terminal::alter_private_mode(bool should_set, Parameters params) -{ - for (auto mode : params) { - switch (mode) { - case 1: - // Cursor Keys Mode (DECCKM) - dbgln_if(TERMINAL_DEBUG, "Setting cursor keys mode (should_set={})", should_set); - m_cursor_keys_mode = should_set ? CursorKeysMode::Application : CursorKeysMode::Cursor; - break; - case 3: { - // 80/132-column mode (DECCOLM) - unsigned new_columns = should_set ? 132 : 80; - dbgln_if(TERMINAL_DEBUG, "Setting {}-column mode", new_columns); - set_size(new_columns, rows()); - clear(); - break; - } - case 12: - if (should_set) { - // Start blinking cursor - m_client.set_cursor_blinking(true); - } else { - // Stop blinking cursor - m_client.set_cursor_blinking(false); - } - break; - case 25: - if (should_set) { - // Show cursor - m_cursor_shape = m_saved_cursor_shape; - m_client.set_cursor_shape(m_cursor_shape); - } else { - // Hide cursor - m_saved_cursor_shape = m_cursor_shape; - m_cursor_shape = VT::CursorShape::None; - m_client.set_cursor_shape(VT::CursorShape::None); - } - break; - case 1047: -#ifndef KERNEL - if (should_set) { - dbgln_if(TERMINAL_DEBUG, "Switching to Alternate Screen Buffer"); - m_use_alternate_screen_buffer = true; - clear(); - m_client.terminal_history_changed(-m_history.size()); - } else { - dbgln_if(TERMINAL_DEBUG, "Switching to Normal Screen Buffer"); - m_use_alternate_screen_buffer = false; - m_client.terminal_history_changed(m_history.size()); - } - m_need_full_flush = true; -#else - dbgln("Alternate Screen Buffer is not supported"); -#endif - break; - case 1048: - if (should_set) - SCOSC(); - else - SCORC(); - break; - case 1049: -#ifndef KERNEL - if (should_set) { - dbgln_if(TERMINAL_DEBUG, "Switching to Alternate Screen Buffer and saving state"); - m_normal_saved_state = m_current_state; - m_use_alternate_screen_buffer = true; - clear(); - m_client.terminal_history_changed(-m_history.size()); - } else { - dbgln_if(TERMINAL_DEBUG, "Switching to Normal Screen Buffer and restoring state"); - m_current_state = m_normal_saved_state; - m_use_alternate_screen_buffer = false; - set_cursor(cursor_row(), cursor_column()); - m_client.terminal_history_changed(m_history.size()); - } - m_need_full_flush = true; -#else - dbgln("Alternate Screen Buffer is not supported"); -#endif - break; - case 2004: - dbgln_if(TERMINAL_DEBUG, "Setting bracketed mode enabled={}", should_set); - m_needs_bracketed_paste = should_set; - break; - default: - dbgln("Terminal::alter_private_mode: Unimplemented private mode {} (should_set={})", mode, should_set); - break; - } - } -} - -void Terminal::RM(Parameters params) -{ - alter_ansi_mode(false, params); -} - -void Terminal::DECRST(Parameters params) -{ - alter_private_mode(false, params); -} - -void Terminal::SM(Parameters params) -{ - alter_ansi_mode(true, params); -} - -void Terminal::DECSET(Parameters params) -{ - alter_private_mode(true, params); -} - -void Terminal::SGR(Parameters params) -{ - if (params.is_empty()) { - m_current_state.attribute.reset(); - return; - } - auto parse_color = [&]() -> Optional { - if (params.size() < 2) { - dbgln("Color code has no type"); - return {}; - } - u32 rgb = 0; - switch (params[1]) { - case 5: // 8-bit - if (params.size() < 3) { - dbgln("8-bit color code has too few parameters"); - return {}; - } - if (params[2] > 255) { - dbgln("8-bit color code has out-of-bounds value"); - return {}; - } - return Color::indexed(params[2]); - case 2: // 24-bit - if (params.size() < 5) { - dbgln("24-bit color code has too few parameters"); - return {}; - } - for (size_t i = 0; i < 3; ++i) { - rgb <<= 8; - rgb |= params[i + 2]; - } - return Color::rgb(rgb); - default: - dbgln("Unknown color type {}", params[1]); - return {}; - } - }; - - if (params[0] == 38) { - m_current_state.attribute.foreground_color = parse_color().value_or(m_current_state.attribute.foreground_color); - } else if (params[0] == 48) { - m_current_state.attribute.background_color = parse_color().value_or(m_current_state.attribute.background_color); - } else { - // A single escape sequence may set multiple parameters. - for (auto param : params) { - switch (param) { - case 0: - // Reset - m_current_state.attribute.reset(); - break; - case 1: - m_current_state.attribute.flags |= Attribute::Flags::Bold; - break; - case 3: - m_current_state.attribute.flags |= Attribute::Flags::Italic; - break; - case 4: - m_current_state.attribute.flags |= Attribute::Flags::Underline; - break; - case 5: - m_current_state.attribute.flags |= Attribute::Flags::Blink; - break; - case 7: - m_current_state.attribute.flags |= Attribute::Flags::Negative; - break; - case 22: - m_current_state.attribute.flags &= ~Attribute::Flags::Bold; - break; - case 23: - m_current_state.attribute.flags &= ~Attribute::Flags::Italic; - break; - case 24: - m_current_state.attribute.flags &= ~Attribute::Flags::Underline; - break; - case 25: - m_current_state.attribute.flags &= ~Attribute::Flags::Blink; - break; - case 27: - m_current_state.attribute.flags &= ~Attribute::Flags::Negative; - break; - case 30: - case 31: - case 32: - case 33: - case 34: - case 35: - case 36: - case 37: - // Foreground color - m_current_state.attribute.foreground_color = Color::named(static_cast(param - 30)); - break; - case 39: - // reset foreground - m_current_state.attribute.foreground_color = Attribute::default_foreground_color; - break; - case 40: - case 41: - case 42: - case 43: - case 44: - case 45: - case 46: - case 47: - // Background color - m_current_state.attribute.background_color = Color::named(static_cast(param - 40)); - break; - case 49: - // reset background - m_current_state.attribute.background_color = Attribute::default_background_color; - break; - case 90: - case 91: - case 92: - case 93: - case 94: - case 95: - case 96: - case 97: - // Bright foreground color - m_current_state.attribute.foreground_color = Color::named(static_cast(8 + param - 90)); - break; - case 100: - case 101: - case 102: - case 103: - case 104: - case 105: - case 106: - case 107: - // Bright background color - m_current_state.attribute.background_color = Color::named(static_cast(8 + param - 100)); - break; - default: - dbgln("FIXME: SGR: p: {}", param); - } - } - } -} - -void Terminal::SCOSC() -{ - dbgln_if(TERMINAL_DEBUG, "Save cursor position"); - m_saved_cursor_position = m_current_state.cursor; -} - -void Terminal::SCORC() -{ - dbgln_if(TERMINAL_DEBUG, "Restore cursor position"); - m_current_state.cursor = m_saved_cursor_position; - set_cursor(cursor_row(), cursor_column()); -} - -void Terminal::DECSC() -{ - dbgln_if(TERMINAL_DEBUG, "Save cursor (and other state)"); - if (m_use_alternate_screen_buffer) { - m_alternate_saved_state = m_current_state; - } else { - m_normal_saved_state = m_current_state; - } -} - -void Terminal::DECRC() -{ - dbgln_if(TERMINAL_DEBUG, "Restore cursor (and other state)"); - if (m_use_alternate_screen_buffer) { - m_current_state = m_alternate_saved_state; - } else { - m_current_state = m_normal_saved_state; - } - set_cursor(cursor_row(), cursor_column()); -} - -void Terminal::XTERM_WM(Parameters params) -{ - if (params.size() < 1) - return; - switch (params[0]) { - case 22: { -#ifndef KERNEL - if (params.size() > 1 && params[1] == 1) { - dbgln("FIXME: we don't support icon titles"); - return; - } - dbgln_if(TERMINAL_DEBUG, "Title stack push: {}", m_current_window_title); - (void)m_title_stack.try_append(move(m_current_window_title)); -#endif - break; - } - case 23: { -#ifndef KERNEL - if (params.size() > 1 && params[1] == 1) - return; - if (m_title_stack.is_empty()) { - dbgln("Shenanigans: Tried to pop from empty title stack"); - return; - } - m_current_window_title = m_title_stack.take_last(); - dbgln_if(TERMINAL_DEBUG, "Title stack pop: {}", m_current_window_title); - m_client.set_window_title(m_current_window_title); -#endif - break; - } - default: - dbgln("FIXME: XTERM_WM: Ps: {} (param count: {})", params[0], params.size()); - } -} - -void Terminal::DECSTBM(Parameters params) -{ - unsigned top = 1; - unsigned bottom = m_rows; - if (params.size() >= 1 && params[0] != 0) - top = params[0]; - if (params.size() >= 2 && params[1] != 0) - bottom = params[1]; - if ((bottom - top) < 2 || bottom > m_rows) { - dbgln("Error: DECSTBM: scrolling region invalid: {}-{}", top, bottom); - return; - } - if (top >= bottom) { - return; - } - m_scroll_region_top = top - 1; - m_scroll_region_bottom = bottom - 1; - set_cursor(0, 0); - dbgln_if(TERMINAL_DEBUG, "Set scrolling region: {}-{}", m_scroll_region_top, m_scroll_region_bottom); -} - -void Terminal::CUP(Parameters params) -{ - // CUP – Cursor Position - unsigned row = 1; - unsigned col = 1; - if (params.size() >= 1 && params[0] != 0) - row = params[0]; - if (params.size() >= 2 && params[1] != 0) - col = params[1]; - set_cursor(row - 1, col - 1); -} - -void Terminal::HVP(Parameters params) -{ - unsigned row = 1; - unsigned col = 1; - if (params.size() >= 1 && params[0] != 0) - row = params[0]; - if (params.size() >= 2 && params[1] != 0) - col = params[1]; - set_cursor(row - 1, col - 1); -} - -void Terminal::CUU(Parameters params) -{ - unsigned num = 1; - if (params.size() >= 1 && params[0] != 0) - num = params[0]; - int new_row = cursor_row() - num; - if (new_row < 0) - new_row = 0; - set_cursor(new_row, cursor_column()); -} - -void Terminal::CUD(Parameters params) -{ - unsigned num = 1; - if (params.size() >= 1 && params[0] != 0) - num = params[0]; - unsigned new_row = cursor_row() + num; - if (new_row >= m_rows) - new_row = m_rows - 1; - set_cursor(new_row, cursor_column()); -} - -void Terminal::CUF(Parameters params) -{ - unsigned num = 1; - if (params.size() >= 1 && params[0] != 0) - num = params[0]; - unsigned new_column = cursor_column() + num; - if (new_column >= m_columns) - new_column = m_columns - 1; - set_cursor(cursor_row(), new_column); -} - -void Terminal::CUB(Parameters params) -{ - unsigned num = 1; - if (params.size() >= 1 && params[0] != 0) - num = params[0]; - int new_column = (int)cursor_column() - num; - if (new_column < 0) - new_column = 0; - set_cursor(cursor_row(), new_column); -} - -void Terminal::CNL(Parameters params) -{ - unsigned num = 1; - if (params.size() >= 1 && params[0] != 0) - num = params[0]; - unsigned new_row = cursor_row() + num; - if (new_row >= m_columns) - new_row = m_columns - 1; - set_cursor(new_row, 0); -} - -void Terminal::CPL(Parameters params) -{ - unsigned num = 1; - if (params.size() >= 1 && params[0] != 0) - num = params[0]; - int new_row = (int)cursor_row() - num; - if (new_row < 0) - new_row = 0; - set_cursor(new_row, 0); -} - -void Terminal::CHA(Parameters params) -{ - unsigned new_column = 1; - if (params.size() >= 1 && params[0] != 0) - new_column = params[0]; - if (new_column > m_columns) - new_column = m_columns; - set_cursor(cursor_row(), new_column - 1); -} - -void Terminal::REP(Parameters params) -{ - unsigned count = 1; - if (params.size() >= 1 && params[0] != 0) - count = params[0]; - - for (unsigned i = 0; i < count; ++i) - put_character_at(m_current_state.cursor.row, m_current_state.cursor.column++, m_last_code_point); -} - -void Terminal::VPA(Parameters params) -{ - unsigned new_row = 1; - if (params.size() >= 1 && params[0] != 0) - new_row = params[0]; - if (new_row > m_rows) - new_row = m_rows; - set_cursor(new_row - 1, cursor_column()); -} - -void Terminal::VPR(Parameters params) -{ - unsigned num = 1; - if (params.size() >= 1 && params[0] != 0) - num = params[0]; - int new_row = cursor_row() + num; - if (new_row >= m_rows) - new_row = m_rows - 1; - set_cursor(new_row, cursor_column()); -} - -void Terminal::HPA(Parameters params) -{ - unsigned new_column = 1; - if (params.size() >= 1 && params[0] != 0) - new_column = params[0]; - if (new_column > m_columns) - new_column = m_columns; - set_cursor(cursor_row(), new_column - 1); -} - -void Terminal::HPR(Parameters params) -{ - unsigned num = 1; - if (params.size() >= 1 && params[0] != 0) - num = params[0]; - unsigned new_column = cursor_column() + num; - if (new_column >= m_columns) - new_column = m_columns - 1; - set_cursor(cursor_row(), new_column); -} - -void Terminal::ECH(Parameters params) -{ - // Erase characters (without moving cursor) - unsigned num = 1; - if (params.size() >= 1 && params[0] != 0) - num = params[0]; - // Clear num characters from the right of the cursor. - auto clear_end = min(m_columns, cursor_column() + num - 1); - dbgln_if(TERMINAL_DEBUG, "Erase characters {}-{} on line {}", cursor_column(), clear_end, cursor_row()); - clear_in_line(cursor_row(), cursor_column(), clear_end); -} - -void Terminal::EL(Parameters params) -{ - unsigned mode = 0; - if (params.size() >= 1) - mode = params[0]; - switch (mode) { - case 0: - dbgln_if(TERMINAL_DEBUG, "Clear line {} from cursor column ({}) to the end", cursor_row(), cursor_column()); - clear_in_line(cursor_row(), cursor_column(), m_columns - 1); - break; - case 1: - dbgln_if(TERMINAL_DEBUG, "Clear line {} from the start to cursor column ({})", cursor_row(), cursor_column()); - clear_in_line(cursor_row(), 0, cursor_column()); - break; - case 2: - dbgln_if(TERMINAL_DEBUG, "Clear line {} completely", cursor_row()); - clear_in_line(cursor_row(), 0, m_columns - 1); - break; - default: - unimplemented_csi_sequence(params, {}, 'K'); - break; - } -} - -void Terminal::ED(Parameters params) -{ - unsigned mode = 0; - if (params.size() >= 1) - mode = params[0]; - switch (mode) { - case 0: - dbgln_if(TERMINAL_DEBUG, "Clear from cursor ({},{}) to end of screen", cursor_row(), cursor_column()); - clear_in_line(cursor_row(), cursor_column(), m_columns - 1); - for (int row = cursor_row() + 1; row < m_rows; ++row) - clear_in_line(row, 0, m_columns - 1); - break; - case 1: - dbgln_if(TERMINAL_DEBUG, "Clear from beginning of screen to cursor ({},{})", cursor_row(), cursor_column()); - clear_in_line(cursor_row(), 0, cursor_column()); - for (int row = cursor_row() - 1; row >= 0; --row) - clear_in_line(row, 0, m_columns - 1); - break; - case 2: - clear(); - break; - case 3: - clear_history(); - break; - default: - unimplemented_csi_sequence(params, {}, 'J'); - break; - } -} - -void Terminal::SU(Parameters params) -{ - unsigned count = 1; - if (params.size() >= 1 && params[0] != 0) - count = params[0]; - - scroll_up(count); -} - -void Terminal::SD(Parameters params) -{ - unsigned count = 1; - if (params.size() >= 1 && params[0] != 0) - count = params[0]; - - scroll_down(count); -} - -void Terminal::DECSCUSR(Parameters params) -{ - unsigned style = 1; - if (params.size() >= 1 && params[0] != 0) - style = params[0]; - switch (style) { - case 1: - m_client.set_cursor_shape(VT::CursorShape::Block); - m_client.set_cursor_blinking(true); - break; - case 2: - m_client.set_cursor_shape(VT::CursorShape::Block); - m_client.set_cursor_blinking(false); - break; - case 3: - m_client.set_cursor_shape(VT::CursorShape::Underline); - m_client.set_cursor_blinking(true); - break; - case 4: - m_client.set_cursor_shape(VT::CursorShape::Underline); - m_client.set_cursor_blinking(false); - break; - case 5: - m_client.set_cursor_shape(VT::CursorShape::Bar); - m_client.set_cursor_blinking(true); - break; - case 6: - m_client.set_cursor_shape(VT::CursorShape::Bar); - m_client.set_cursor_blinking(false); - break; - default: - dbgln("Unknown cursor style {}", style); - } -} - -void Terminal::IL(Parameters params) -{ - size_t count = 1; - if (params.size() >= 1 && params[0] != 0) - count = params[0]; - if (!is_within_scroll_region(cursor_row())) { - dbgln("Shenanigans! Tried to insert line outside the scroll region"); - return; - } - scroll_down(cursor_row(), m_scroll_region_bottom, count); -} - -void Terminal::DA(Parameters) -{ - emit_string("\033[?1;0c"sv); -} - -void Terminal::DL(Parameters params) -{ - size_t count = 1; - if (params.size() >= 1 && params[0] != 0) - count = params[0]; - if (!is_within_scroll_region(cursor_row())) { - dbgln("Shenanigans! Tried to delete line outside the scroll region"); - return; - } - scroll_up(cursor_row(), m_scroll_region_bottom, count); -} - -void Terminal::DCH(Parameters params) -{ - int num = 1; - if (params.size() >= 1 && params[0] != 0) - num = params[0]; - - num = min(num, columns() - cursor_column()); - scroll_left(cursor_row(), cursor_column(), num); -} - -void Terminal::linefeed() -{ - u16 new_row = cursor_row(); -#ifndef KERNEL - if (!m_controls_are_logically_generated) - active_buffer()[new_row]->set_terminated(m_column_before_carriage_return.value_or(cursor_column())); -#endif - if (cursor_row() == m_scroll_region_bottom) { - scroll_up(); - } else { - ++new_row; - }; - // We shouldn't jump to the first column after receiving a line feed. - // The TTY will take care of generating the carriage return. - set_cursor(new_row, cursor_column()); -} - -void Terminal::carriage_return() -{ - dbgln_if(TERMINAL_DEBUG, "Carriage return"); - m_column_before_carriage_return = cursor_column(); - set_cursor(cursor_row(), 0); -} - -void Terminal::scroll_up(size_t count) -{ - scroll_up(m_scroll_region_top, m_scroll_region_bottom, count); -} - -void Terminal::scroll_down(size_t count) -{ - scroll_down(m_scroll_region_top, m_scroll_region_bottom, count); -} - -#ifndef KERNEL -// Insert `count` blank lines at the bottom of the region. Text moves up, top lines get added to the scrollback. -void Terminal::scroll_up(u16 region_top, u16 region_bottom, size_t count) -{ - VERIFY(region_top <= region_bottom); - VERIFY(region_bottom < rows()); - // Only the specified region should be affected. - size_t region_size = region_bottom - region_top + 1; - count = min(count, region_size); - dbgln_if(TERMINAL_DEBUG, "Scroll up {} lines in region {}-{}", count, region_top, region_bottom); - // NOTE: We have to invalidate the cursor first. - invalidate_cursor(); - - int history_delta = -count; - bool should_move_to_scrollback = !m_use_alternate_screen_buffer && max_history_size() != 0; - if (should_move_to_scrollback) { - auto remaining_lines = max_history_size() - history_size(); - history_delta = (count > remaining_lines) ? remaining_lines - count : 0; - for (size_t i = 0; i < count; ++i) - add_line_to_history(move(active_buffer().at(region_top + i))); - } - - // Move lines into their new place. - for (u16 row = region_top; row + count <= region_bottom; ++row) - swap(active_buffer().at(row), active_buffer().at(row + count)); - // Clear 'new' lines at the bottom. - if (should_move_to_scrollback) { - // Since we moved the previous lines into history, we can't just clear them. - for (u16 row = region_bottom + 1 - count; row <= region_bottom; ++row) - active_buffer().at(row) = make(columns()); - } else { - // The new lines haven't been moved and we don't want to leak memory. - for (u16 row = region_bottom + 1 - count; row <= region_bottom; ++row) - active_buffer()[row]->clear(); - } - // Set dirty flag on swapped lines. - // The other lines have implicitly been set dirty by being cleared. - for (u16 row = region_top; row + count <= region_bottom; ++row) - active_buffer()[row]->set_dirty(true); - m_client.terminal_history_changed(history_delta); -} - -// Insert `count` blank lines at the top of the region. Text moves down. Does not affect the scrollback buffer. -void Terminal::scroll_down(u16 region_top, u16 region_bottom, size_t count) -{ - VERIFY(region_top <= region_bottom); - VERIFY(region_bottom < rows()); - // Only the specified region should be affected. - size_t region_size = region_bottom - region_top + 1; - count = min(count, region_size); - dbgln_if(TERMINAL_DEBUG, "Scroll down {} lines in region {}-{}", count, region_top, region_bottom); - // NOTE: We have to invalidate the cursor first. - invalidate_cursor(); - - // Move lines into their new place. - for (int row = region_bottom; row >= static_cast(region_top + count); --row) - swap(active_buffer().at(row), active_buffer().at(row - count)); - // Clear the 'new' lines at the top. - for (u16 row = region_top; row < region_top + count; ++row) - active_buffer()[row]->clear(); - // Set dirty flag on swapped lines. - // The other lines have implicitly been set dirty by being cleared. - for (u16 row = region_top + count; row <= region_bottom; ++row) - active_buffer()[row]->set_dirty(true); -} - -// Insert `count` blank cells at the end of the line. Text moves left. -void Terminal::scroll_left(u16 row, u16 column, size_t count) -{ - VERIFY(row < rows()); - VERIFY(column < columns()); - count = min(count, columns() - column); - dbgln_if(TERMINAL_DEBUG, "Scroll left {} columns from line {} column {}", count, row, column); - - auto& line = active_buffer()[row]; - for (size_t i = column; i < columns() - count; ++i) - swap(line->cell_at(i), line->cell_at(i + count)); - clear_in_line(row, columns() - count, columns() - 1); - line->set_dirty(true); -} - -// Insert `count` blank cells after `row`. Text moves right. -void Terminal::scroll_right(u16 row, u16 column, size_t count) -{ - VERIFY(row < rows()); - VERIFY(column < columns()); - count = min(count, columns() - column); - dbgln_if(TERMINAL_DEBUG, "Scroll right {} columns from line {} column {}", count, row, column); - - auto& line = active_buffer()[row]; - for (int i = columns() - 1; i >= static_cast(column + count); --i) - swap(line->cell_at(i), line->cell_at(i - count)); - clear_in_line(row, column, column + count - 1); - line->set_dirty(true); -} - -void Terminal::put_character_at(unsigned row, unsigned column, u32 code_point) -{ - VERIFY(row < rows()); - VERIFY(column < columns()); - auto& line = active_buffer()[row]; - line->set_code_point(column, code_point); - line->attribute_at(column) = m_current_state.attribute; - line->attribute_at(column).flags |= Attribute::Flags::Touched; - line->set_dirty(true); - - m_last_code_point = code_point; -} - -void Terminal::clear_in_line(u16 row, u16 first_column, u16 last_column) -{ - VERIFY(row < rows()); - active_buffer()[row]->clear_range(first_column, last_column, m_current_state.attribute); -} -#endif - -void Terminal::set_cursor(unsigned a_row, unsigned a_column, bool skip_debug) -{ - unsigned row = min(a_row, m_rows - 1u); - unsigned column = min(a_column, m_columns - 1u); - m_stomp = false; - if (row == cursor_row() && column == cursor_column()) - return; - VERIFY(row < rows()); - VERIFY(column < columns()); - invalidate_cursor(); - m_current_state.cursor.row = row; - m_current_state.cursor.column = column; - invalidate_cursor(); - if (!skip_debug) - dbgln_if(TERMINAL_DEBUG, "Set cursor position: {},{}", cursor_row(), cursor_column()); -} - -void Terminal::NEL() -{ - if (cursor_row() == m_scroll_region_bottom) - scroll_up(); - else - set_cursor(cursor_row() + 1, 0); -} - -void Terminal::IND() -{ - // Not equivalent to CUD: if we are at the bottom margin, we have to scroll up. - if (cursor_row() == m_scroll_region_bottom) - scroll_up(); - else - set_cursor(cursor_row() + 1, cursor_column()); -} - -void Terminal::RI() -{ - // Not equivalent to CUU : if we at the top margin , we have to scroll down. - if (cursor_row() == m_scroll_region_top) - scroll_down(); - else - set_cursor(cursor_row() - 1, cursor_column()); -} - -void Terminal::DECFI() -{ - if (cursor_column() == columns() - 1) - scroll_left(cursor_row(), 0, 1); - else - set_cursor(cursor_row(), cursor_column() + 1); -} - -void Terminal::DECBI() -{ - if (cursor_column() == 0) - scroll_right(cursor_row(), 0, 1); - else - set_cursor(cursor_row(), cursor_column() - 1); -} - -void Terminal::DECIC(Parameters params) -{ - unsigned num = 1; - if (params.size() >= 1 && params[0] != 0) - num = params[0]; - - num = min(num, columns() - cursor_column()); - for (unsigned row = cursor_row(); row <= m_scroll_region_bottom; ++row) - scroll_right(row, cursor_column(), num); -} - -void Terminal::DECDC(Parameters params) -{ - unsigned num = 1; - if (params.size() >= 1 && params[0] != 0) - num = params[0]; - - num = min(num, columns() - cursor_column()); - for (unsigned row = cursor_row(); row <= m_scroll_region_bottom; ++row) - scroll_left(row, cursor_column(), num); -} - -void Terminal::DECKPNM() -{ - m_in_application_keypad_mode = false; -} - -void Terminal::DECKPAM() -{ - m_in_application_keypad_mode = true; -} - -void Terminal::DSR(Parameters params) -{ - if (params.size() == 1 && params[0] == 5) { - // Device status - emit_string("\033[0n"sv); // Terminal status OK! - } else if (params.size() == 1 && params[0] == 6) { - // Cursor position query - StringBuilder builder; - MUST(builder.try_appendff("\e[{};{}R", cursor_row() + 1, cursor_column() + 1)); // StringBuilder's inline capacity of 256 is enough to guarantee no allocations - emit_string(builder.string_view()); - } else { - dbgln("Unknown DSR"); - } -} - -void Terminal::ICH(Parameters params) -{ - unsigned num = 1; - if (params.size() >= 1 && params[0] != 0) - num = params[0]; - - num = min(num, columns() - cursor_column()); - scroll_right(cursor_row(), cursor_column(), num); -} - -void Terminal::on_input(u8 byte) -{ - m_parser.on_input(byte); -} - -void Terminal::emit_code_point(u32 code_point) -{ - auto working_set = m_working_sets[m_active_working_set_index]; - code_point = m_character_set_translator.translate_code_point(working_set, code_point); - - auto new_column = cursor_column() + 1; - if (new_column < columns()) { - put_character_at(cursor_row(), cursor_column(), code_point); - set_cursor(cursor_row(), new_column, true); - return; - } - if (m_stomp) { - m_stomp = false; - TemporaryChange change { m_controls_are_logically_generated, true }; - carriage_return(); - linefeed(); - put_character_at(cursor_row(), cursor_column(), code_point); - set_cursor(cursor_row(), 1); - } else { - // Curious: We wait once on the right-hand side - m_stomp = true; - put_character_at(cursor_row(), cursor_column(), code_point); - } -} - -void Terminal::execute_control_code(u8 code) -{ - ArmedScopeGuard clear_position_before_cr { - [&] { - m_column_before_carriage_return.clear(); - } - }; - switch (code) { - case '\a': - m_client.beep(); - return; - case '\b': - if (cursor_column()) { - set_cursor(cursor_row(), cursor_column() - 1); - return; - } - return; - case '\t': { - for (unsigned i = cursor_column() + 1; i < columns(); ++i) { - if (m_horizontal_tabs[i]) { - set_cursor(cursor_row(), i); - return; - } - } - return; - } - case '\n': - case '\v': - case '\f': - if (m_column_before_carriage_return == m_columns - 1) - m_column_before_carriage_return = m_columns; - linefeed(); - return; - case '\r': - carriage_return(); - clear_position_before_cr.disarm(); - return; - default: - unimplemented_control_code(code); - } -} - -void Terminal::execute_escape_sequence(Intermediates intermediates, bool ignore, u8 last_byte) -{ - // FIXME: Handle it somehow? - if (ignore) - dbgln("Escape sequence has its ignore flag set."); - - if (intermediates.size() == 0) { - switch (last_byte) { - case 'D': - IND(); - return; - case 'E': - NEL(); - return; - case 'M': - RI(); - return; - case '\\': - // ST (string terminator) -- do nothing - return; - case '6': - DECBI(); - return; - case '7': - DECSC(); - return; - case '8': - DECRC(); - return; - case '9': - DECFI(); - return; - case '=': - DECKPAM(); - return; - case '>': - DECKPNM(); - return; - } - unimplemented_escape_sequence(intermediates, last_byte); - return; - } - - char intermediate = intermediates[0]; - switch (intermediate) { - case '#': - switch (last_byte) { - case '8': - // Confidence Test - Fill screen with E's - for (size_t row = 0; row < m_rows; ++row) { - for (size_t column = 0; column < m_columns; ++column) { - put_character_at(row, column, 'E'); - } - } - return; - } - break; - case '(': - case ')': - case '*': - case '+': - // Determine G0..G3 index - size_t working_set_index = intermediate - '('; - - CharacterSet new_set; - switch (last_byte) { - case 'B': - new_set = CharacterSet::Iso_8859_1; - break; - case '0': - new_set = CharacterSet::VT100; - break; - case 'U': - new_set = CharacterSet::Null; - break; - case 'K': - new_set = CharacterSet::UserDefined; - break; - default: - unimplemented_escape_sequence(intermediates, last_byte); - return; - } - - dbgln_if(TERMINAL_DEBUG, "Setting G{} working set to character set {}", working_set_index, to_underlying(new_set)); - VERIFY(working_set_index <= 3); - m_working_sets[working_set_index] = new_set; - return; - } - - unimplemented_escape_sequence(intermediates, last_byte); -} - -void Terminal::execute_csi_sequence(Parameters parameters, Intermediates intermediates, bool ignore, u8 last_byte) -{ - // FIXME: Handle it somehow? - if (ignore) - dbgln("CSI sequence has its ignore flag set."); - - if (intermediates.is_empty()) { - switch (last_byte) { - case '@': - return ICH(parameters); - case 'A': - return CUU(parameters); - case 'B': - return CUD(parameters); - case 'C': - return CUF(parameters); - case 'D': - return CUB(parameters); - case 'E': - return CNL(parameters); - case 'F': - return CPL(parameters); - case 'G': - return CHA(parameters); - case 'H': - return CUP(parameters); - case 'J': - return ED(parameters); - case 'K': - return EL(parameters); - case 'L': - return IL(parameters); - case 'M': - return DL(parameters); - case 'P': - return DCH(parameters); - case 'S': - return SU(parameters); - case 'T': - return SD(parameters); - case 'X': - return ECH(parameters); - case '`': - return HPA(parameters); - case 'a': - return HPR(parameters); - case 'b': - return REP(parameters); - case 'c': - return DA(parameters); - case 'd': - return VPA(parameters); - case 'e': - return VPR(parameters); - case 'f': - return HVP(parameters); - case 'h': - return SM(parameters); - case 'l': - return RM(parameters); - case 'm': - return SGR(parameters); - case 'n': - return DSR(parameters); - case 'r': - return DECSTBM(parameters); - case 's': - return SCOSC(); - case 't': - return XTERM_WM(parameters); - case 'u': - return SCORC(); - } - } else if (intermediates.size() == 1 && intermediates[0] == '?') { - switch (last_byte) { - case 'h': - return DECSET(parameters); - case 'l': - return DECRST(parameters); - } - } else if (intermediates.size() == 1 && intermediates[0] == '\'') { - switch (last_byte) { - case '}': - return DECIC(parameters); - case '~': - return DECDC(parameters); - } - } else if (intermediates.size() == 1 && intermediates[0] == ' ') { - switch (last_byte) { - case 'q': - return DECSCUSR(parameters); - } - } - - unimplemented_csi_sequence(parameters, intermediates, last_byte); -} - -void Terminal::execute_osc_sequence(OscParameters parameters, u8 last_byte) -{ - auto stringview_ify = [&](size_t param_idx) { - return StringView(parameters[param_idx]); - }; - - if (parameters.size() == 0 || parameters[0].is_empty()) { - unimplemented_osc_sequence(parameters, last_byte); - return; - } - - auto command_number = stringview_ify(0).to_number(); - if (!command_number.has_value()) { - unimplemented_osc_sequence(parameters, last_byte); - return; - } - - switch (command_number.value()) { - case 0: - case 1: - case 2: - if (parameters.size() < 2) { - dbgln("Attempted to set window title without any parameters"); - } else { - // FIXME: the split breaks titles containing semicolons. - // Should we expose the raw OSC string from the parser? Or join by semicolon? -#ifndef KERNEL - m_current_window_title = stringview_ify(1).to_byte_string(); - m_client.set_window_title(m_current_window_title); -#endif - } - break; - case 8: -#ifndef KERNEL - if (parameters.size() < 3) { - dbgln("Attempted to set href but gave too few parameters"); - } else if (parameters[1].is_empty() && parameters[2].is_empty()) { - // Clear hyperlink - m_current_state.attribute.href = {}; - m_current_state.attribute.href_id = {}; - } else { - m_current_state.attribute.href = stringview_ify(2); - // FIXME: Respect the provided ID - m_current_state.attribute.href_id = ByteString::number(m_next_href_id++); - } -#endif - break; - case 9: - if (parameters.size() < 2) - dbgln("Atttempted to set window progress but gave too few parameters"); - else if (parameters.size() == 2) - m_client.set_window_progress(stringview_ify(1).to_number().value_or(-1), 0); - else - m_client.set_window_progress(stringview_ify(1).to_number().value_or(-1), stringview_ify(2).to_number().value_or(0)); - break; - default: - unimplemented_osc_sequence(parameters, last_byte); - } -} - -void Terminal::dcs_hook(Parameters, Intermediates, bool, u8) -{ - dbgln("Received DCS parameters, but we don't support it yet"); -} - -void Terminal::receive_dcs_char(u8 byte) -{ - dbgln_if(TERMINAL_DEBUG, "DCS string character {:c}", byte); -} - -void Terminal::execute_dcs_sequence() -{ -} - -void Terminal::inject_string(StringView str) -{ - for (size_t i = 0; i < str.length(); ++i) - on_input(str[i]); -} - -void Terminal::emit_string(StringView string) -{ - m_client.emit((u8 const*)string.characters_without_null_termination(), string.length()); -} - -void Terminal::handle_key_press(KeyCode key, u32 code_point, u8 flags) -{ - bool ctrl = flags & Mod_Ctrl; - bool alt = flags & Mod_Alt; - bool shift = flags & Mod_Shift; - bool keypad = flags & Mod_Keypad; - unsigned modifier_mask = int(shift) + (int(alt) << 1) + (int(ctrl) << 2); - - auto emit_final_with_modifier = [this, modifier_mask](char final) { - char escape_character = m_cursor_keys_mode == CursorKeysMode::Application ? 'O' : '['; - StringBuilder builder; - if (modifier_mask) - MUST(builder.try_appendff("\e{}1;{}{:c}", escape_character, modifier_mask + 1, final)); // StringBuilder's inline capacity of 256 is enough to guarantee no allocations - else - MUST(builder.try_appendff("\e{}{:c}", escape_character, final)); // StringBuilder's inline capacity of 256 is enough to guarantee no allocations - emit_string(builder.string_view()); - }; - auto emit_tilde_with_modifier = [this, modifier_mask](unsigned num) { - StringBuilder builder; - if (modifier_mask) - MUST(builder.try_appendff("\e[{};{}~", num, modifier_mask + 1)); // StringBuilder's inline capacity of 256 is enough to guarantee no allocations - else - MUST(builder.try_appendff("\e[{}~", num)); // StringBuilder's inline capacity of 256 is enough to guarantee no allocations - emit_string(builder.string_view()); - }; - - auto emit_application_code = [this](KeyCode key) { - // The table providing mapping from numeric keys to application keys can be found at https://vt100.net/docs/vt100-ug/chapter3.html#T3-7 - StringBuilder builder; - builder.append("\x1bO"sv); - switch (key) { - case KeyCode::Key_0: - builder.append('p'); - break; - case KeyCode::Key_1: - builder.append('q'); - break; - case KeyCode::Key_2: - builder.append('r'); - break; - case KeyCode::Key_3: - builder.append('s'); - break; - case KeyCode::Key_4: - builder.append('t'); - break; - case KeyCode::Key_5: - builder.append('u'); - break; - case KeyCode::Key_6: - builder.append('v'); - break; - case KeyCode::Key_7: - builder.append('w'); - break; - case KeyCode::Key_8: - builder.append('x'); - break; - case KeyCode::Key_9: - builder.append('y'); - break; - case KeyCode::Key_Minus: - builder.append('m'); - break; - case KeyCode::Key_Comma: - builder.append('l'); - break; - case KeyCode::Key_Period: - builder.append('n'); - break; - case KeyCode::Key_Return: - builder.append('M'); - break; - default: - break; - } - emit_string(builder.string_view()); - }; - - if (keypad && m_in_application_keypad_mode) { - emit_application_code(key); - return; - } - - switch (key) { - case KeyCode::Key_Up: - emit_final_with_modifier('A'); - return; - case KeyCode::Key_Down: - emit_final_with_modifier('B'); - return; - case KeyCode::Key_Right: - emit_final_with_modifier('C'); - return; - case KeyCode::Key_Left: - emit_final_with_modifier('D'); - return; - case KeyCode::Key_Insert: - emit_tilde_with_modifier(2); - return; - case KeyCode::Key_Delete: - emit_tilde_with_modifier(3); - return; - case KeyCode::Key_Home: - emit_final_with_modifier('H'); - return; - case KeyCode::Key_End: - emit_final_with_modifier('F'); - return; - case KeyCode::Key_PageUp: - emit_tilde_with_modifier(5); - return; - case KeyCode::Key_PageDown: - emit_tilde_with_modifier(6); - return; - case KeyCode::Key_Backspace: - if (ctrl) { - // This is an extension that allows Editor.cpp to delete whole words when - // Ctrl+Backspace is pressed. Ctrl cannot be transmitted without a CSI, and - // ANSI delete (127) is within the valid range for CSI codes in Editor.cpp. - // The code also has the same behavior as backspace when emitted with no CSI, - // though the backspace code (8) is preserved when Ctrl is not pressed. - emit_final_with_modifier(127); - return; - } - break; - case KeyCode::Key_Return: - // The standard says that CR should be generated by the return key. - // The TTY will take care of translating it to CR LF for the terminal. - emit_string("\r"sv); - return; - default: - break; - } - - if (!code_point) { - // Probably a modifier being pressed. - return; - } - - if (shift && key == KeyCode::Key_Tab) { - emit_string("\033[Z"sv); - return; - } - - // Key event was not one of the above special cases, - // attempt to treat it as a character... - if (ctrl) { - constexpr auto ESC = '\033'; - constexpr auto NUL = 0; - constexpr auto DEL = 0x7f; - - if (code_point >= '@' && code_point < DEL) - code_point &= 0x1f; - // Legacy aliases - else if (code_point == '2' || code_point == ' ') - // Ctrl+{2, Space, @, `} -> ^@ (NUL) - code_point = NUL; - else if (code_point >= '3' && code_point <= '7') - // Ctrl+3 -> ^[ (ESC), Ctrl+4 -> ^\, Ctrl+5 -> ^], Ctrl+6 -> ^^, Ctrl+7 -> ^_ - code_point = ESC + (code_point - '3'); - else if (code_point == '8') - // Ctrl+8 -> ^? (DEL) - code_point = DEL; - else if (code_point == '/') - // Ctrl+/ -> ^_ - code_point = '_' & 0x1f; - } - - // Alt modifier sends escape prefix. - if (alt) - emit_string("\033"sv); - - StringBuilder sb; - sb.append_code_point(code_point); - emit_string(sb.string_view()); -} - -void Terminal::unimplemented_control_code(u8 code) -{ - dbgln_if(TERMINAL_DEBUG, "Unimplemented control code {:02x}", code); -} - -void Terminal::unimplemented_escape_sequence(Intermediates intermediates, u8 last_byte) -{ - StringBuilder builder; - builder.appendff("Unimplemented escape sequence {:c}", last_byte); - if (!intermediates.is_empty()) { - builder.append(", intermediates: "sv); - for (size_t i = 0; i < intermediates.size(); ++i) - builder.append((char)intermediates[i]); - } - dbgln("{}", builder.string_view()); -} - -void Terminal::unimplemented_csi_sequence(Parameters parameters, Intermediates intermediates, u8 last_byte) -{ - StringBuilder builder; - builder.appendff("Unimplemented CSI sequence: {:c}", last_byte); - if (!parameters.is_empty()) { - builder.append(", parameters: ["sv); - for (size_t i = 0; i < parameters.size(); ++i) - builder.appendff("{}{}", (i == 0) ? "" : ", ", parameters[i]); - builder.append("]"sv); - } - if (!intermediates.is_empty()) { - builder.append(", intermediates:"sv); - for (size_t i = 0; i < intermediates.size(); ++i) - builder.append((char)intermediates[i]); - } - dbgln("{}", builder.string_view()); -} - -void Terminal::unimplemented_osc_sequence(OscParameters parameters, u8 last_byte) -{ - StringBuilder builder; - builder.appendff("Unimplemented OSC sequence parameters: (bel_terminated={}) [ ", last_byte == '\a'); - bool first = true; - for (auto parameter : parameters) { - if (!first) - builder.append(", "sv); - builder.append('['); - for (auto character : parameter) - builder.append((char)character); - builder.append(']'); - first = false; - } - - builder.append(" ]"sv); - dbgln("{}", builder.string_view()); -} - -#ifndef KERNEL -void Terminal::set_size(u16 columns, u16 rows) -{ - if (!columns) - columns = 1; - if (!rows) - rows = 1; - - if (columns == m_columns && rows == m_rows) - return; - - // If we're making the terminal larger (column-wise), start at the end and go up, taking cells from the line below. - // otherwise start at the beginning and go down, pushing cells into the line below. - auto resize_and_rewrap = [&](auto& buffer, auto& old_cursor) { - auto cursor_on_line = [&](auto index) { - return index == old_cursor.row ? &old_cursor : nullptr; - }; - // Two passes, one from top to bottom, another from bottom to top - for (size_t pass = 0; pass < 2; ++pass) { - auto forwards = (pass == 0) ^ (columns < m_columns); - if (forwards) { - for (size_t i = 1; i <= buffer.size(); ++i) { - auto is_at_seam = i == 1; - Line* next_line = is_at_seam ? nullptr : buffer[buffer.size() - i + 1].ptr(); - Line* line = buffer[buffer.size() - i].ptr(); - auto next_cursor = cursor_on_line(buffer.size() - i + 1); - line->rewrap(columns, next_line, next_cursor ?: cursor_on_line(buffer.size() - i), !!next_cursor); - } - } else { - for (size_t i = 0; i < buffer.size(); ++i) { - auto is_at_seam = i + 1 == buffer.size(); - Line* next_line = is_at_seam ? nullptr : buffer[i + 1].ptr(); - auto next_cursor = cursor_on_line(i + 1); - buffer[i]->rewrap(columns, next_line, next_cursor ?: cursor_on_line(i), !!next_cursor); - } - } - - Queue lines_to_reevaluate; - for (size_t i = 0; i < buffer.size(); ++i) { - if (buffer[i]->length() != columns) - lines_to_reevaluate.enqueue(i); - } - while (!lines_to_reevaluate.is_empty()) { - auto index = lines_to_reevaluate.dequeue(); - auto is_at_seam = index + 1 == buffer.size(); - Line* const next_line = is_at_seam ? nullptr : buffer[index + 1].ptr(); - Line* const line = buffer[index].ptr(); - auto next_cursor = cursor_on_line(index + 1); - line->rewrap(columns, next_line, next_cursor ?: cursor_on_line(index), !!next_cursor); - if (line->length() > columns) { - auto current_cursor = cursor_on_line(index); - // Split the line into two (or more) - ++index; - buffer.insert(index, make(0)); - VERIFY(buffer[index]->length() == 0); - line->rewrap(columns, buffer[index].ptr(), current_cursor, false); - // If we inserted a line and the old cursor was after that line, increment its row - if (!current_cursor && old_cursor.row >= index) - ++old_cursor.row; - - if (buffer[index]->length() != columns) - lines_to_reevaluate.enqueue(index); - } - if (next_line && next_line->length() != columns) - lines_to_reevaluate.enqueue(index + 1); - } - } - - for (auto& line : buffer) - line->set_length(columns); - - return old_cursor; - }; - - auto old_history_size = m_history.size(); - m_history.extend(move(m_normal_screen_buffer)); - CursorPosition cursor_tracker { cursor_row() + old_history_size, cursor_column() }; - resize_and_rewrap(m_history, cursor_tracker); - if (auto extra_lines = m_history.size() - rows) { - while (extra_lines > 0) { - if (m_history.size() <= cursor_tracker.row) - break; - if (m_history.last()->is_empty()) { - if (m_history.size() >= 2 && m_history[m_history.size() - 2]->termination_column().has_value()) - break; - --extra_lines; - (void)m_history.take_last(); - continue; - } - break; - } - } - - // FIXME: This can use a more performant way to move the last N entries - // from the history into the normal buffer - m_normal_screen_buffer.ensure_capacity(rows); - while (m_normal_screen_buffer.size() < rows) { - if (!m_history.is_empty()) - m_normal_screen_buffer.prepend(m_history.take_last()); - else - m_normal_screen_buffer.unchecked_append(make(columns)); - } - - cursor_tracker.row -= m_history.size(); - - if (m_history.size() != old_history_size) { - m_client.terminal_history_changed(-old_history_size); - m_client.terminal_history_changed(m_history.size()); - } - - CursorPosition dummy_cursor_tracker {}; - resize_and_rewrap(m_alternate_screen_buffer, dummy_cursor_tracker); - if (m_alternate_screen_buffer.size() > rows) - m_alternate_screen_buffer.remove(0, m_alternate_screen_buffer.size() - rows); - - if (rows > m_rows) { - while (m_normal_screen_buffer.size() < rows) - m_normal_screen_buffer.append(make(columns)); - while (m_alternate_screen_buffer.size() < rows) - m_alternate_screen_buffer.append(make(columns)); - } else { - m_normal_screen_buffer.shrink(rows); - m_alternate_screen_buffer.shrink(rows); - } - - m_columns = columns; - m_rows = rows; - - m_scroll_region_top = 0; - m_scroll_region_bottom = rows - 1; - - m_current_state.cursor.clamp(m_rows - 1, m_columns - 1); - m_normal_saved_state.cursor.clamp(m_rows - 1, m_columns - 1); - m_alternate_saved_state.cursor.clamp(m_rows - 1, m_columns - 1); - m_saved_cursor_position.clamp(m_rows - 1, m_columns - 1); - - m_horizontal_tabs.resize(columns); - for (unsigned i = 0; i < columns; ++i) - m_horizontal_tabs[i] = (i % 8) == 0; - // Rightmost column is always last tab on line. - m_horizontal_tabs[columns - 1] = 1; - - set_cursor(cursor_tracker.row, cursor_tracker.column); - - m_client.terminal_did_resize(m_columns, m_rows); - - dbgln_if(TERMINAL_DEBUG, "Set terminal size: {}x{}", m_rows, m_columns); -} -#endif - -#ifndef KERNEL -void Terminal::invalidate_cursor() -{ - if (cursor_row() < active_buffer().size()) - active_buffer()[cursor_row()]->set_dirty(true); -} - -Attribute Terminal::attribute_at(Position const& position) const -{ - if (!position.is_valid()) - return {}; - if (position.row() >= static_cast(line_count())) - return {}; - auto& line = this->line(position.row()); - if (static_cast(position.column()) >= line.length()) - return {}; - return line.attribute_at(position.column()); -} -#endif -} diff --git a/Userland/Libraries/LibVT/Terminal.h b/Userland/Libraries/LibVT/Terminal.h deleted file mode 100644 index cf705626ae9..00000000000 --- a/Userland/Libraries/LibVT/Terminal.h +++ /dev/null @@ -1,467 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * Copyright (c) 2021, Daniel Bertalan - * Copyright (c) 2022, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include -#include - -#ifndef KERNEL -# include -# include -# include -# include -#else -namespace Kernel { -class VirtualConsole; -} -# include -#endif - -namespace VT { - -enum class CursorShape { - None, - Block, - Underline, - Bar, -}; - -enum CursorKeysMode { - Application, - Cursor, -}; - -class TerminalClient { -public: - virtual ~TerminalClient() = default; - - virtual void beep() = 0; - virtual void set_window_title(StringView) = 0; - virtual void set_window_progress(int value, int max) = 0; - virtual void terminal_did_resize(u16 columns, u16 rows) = 0; - virtual void terminal_history_changed(int delta) = 0; - virtual void terminal_did_perform_possibly_partial_clear() = 0; - virtual void emit(u8 const*, size_t) = 0; - virtual void set_cursor_shape(CursorShape) = 0; - virtual void set_cursor_blinking(bool) = 0; -}; - -class Terminal : public EscapeSequenceExecutor { -public: -#ifndef KERNEL - explicit Terminal(TerminalClient&); -#else - explicit Terminal(Kernel::VirtualConsole&); -#endif - - virtual ~Terminal() - { - } - - bool m_need_full_flush { false }; - -#ifndef KERNEL - void invalidate_cursor(); -#else - virtual void invalidate_cursor() = 0; -#endif - - void on_input(u8); - - void set_cursor(unsigned row, unsigned column, bool skip_debug = false); - - void clear_including_history() - { - clear_history(); - clear(); - } - -#ifndef KERNEL - void mark_cursor(); - OrderedHashTable const& marks() const { return m_valid_marks; } - - void clear(); - void clear_history(); - void clear_to_mark(Mark); -#else - virtual void clear() = 0; - virtual void clear_history() = 0; -#endif - -#ifndef KERNEL - void set_size(u16 columns, u16 rows); -#else - virtual void set_size(u16 columns, u16 rows) = 0; -#endif - - u16 columns() const - { - return m_columns; - } - u16 rows() const { return m_rows; } - - u16 cursor_column() const { return m_current_state.cursor.column; } - u16 cursor_row() const { return m_current_state.cursor.row; } - -#ifndef KERNEL - size_t line_count() const - { - if (m_use_alternate_screen_buffer) - return m_alternate_screen_buffer.size(); - else - return m_history.size() + m_normal_screen_buffer.size(); - } - - Line& line(size_t index) - { - if (m_use_alternate_screen_buffer) { - return *m_alternate_screen_buffer[index]; - } else { - if (index < m_history.size()) - return *m_history[(m_history_start + index) % m_history.size()]; - return *m_normal_screen_buffer[index - m_history.size()]; - } - } - Line const& line(size_t index) const - { - return const_cast(this)->line(index); - } - - Line& visible_line(size_t index) - { - return *active_buffer()[index]; - } - - Line const& visible_line(size_t index) const - { - return *active_buffer()[index]; - } - - size_t max_history_size() const { return m_max_history_lines; } - void set_max_history_size(size_t value) - { - if (value == 0) { - auto previous_size = m_history.size(); - m_max_history_lines = 0; - m_history_start = 0; - m_history.clear(); - m_client.terminal_history_changed(-previous_size); - return; - } - - if (m_max_history_lines > value) { - Vector> new_history; - new_history.ensure_capacity(value); - auto existing_line_count = min(m_history.size(), value); - for (size_t i = m_history.size() - existing_line_count; i < m_history.size(); ++i) { - auto j = (m_history_start + i) % m_history.size(); - new_history.unchecked_append(move(static_cast>&>(m_history).at(j))); - } - m_history = move(new_history); - m_history_start = 0; - m_client.terminal_history_changed(value - existing_line_count); - } - m_max_history_lines = value; - } - size_t history_size() const { return m_use_alternate_screen_buffer ? 0 : m_history.size(); } -#endif - - void inject_string(StringView); - void handle_key_press(KeyCode, u32, u8 flags); - -#ifndef KERNEL - Attribute attribute_at(Position const&) const; -#endif - - bool needs_bracketed_paste() const - { - return m_needs_bracketed_paste; - } - - bool is_within_scroll_region(u16 line) const - { - return line >= m_scroll_region_top && line <= m_scroll_region_bottom; - } - -protected: - // ^EscapeSequenceExecutor - virtual void emit_code_point(u32) override; - virtual void execute_control_code(u8) override; - virtual void execute_escape_sequence(Intermediates intermediates, bool ignore, u8 last_byte) override; - virtual void execute_csi_sequence(Parameters parameters, Intermediates intermediates, bool ignore, u8 last_byte) override; - virtual void execute_osc_sequence(OscParameters parameters, u8 last_byte) override; - virtual void dcs_hook(Parameters parameters, Intermediates intermediates, bool ignore, u8 last_byte) override; - virtual void receive_dcs_char(u8 byte) override; - virtual void execute_dcs_sequence() override; - - struct BufferState { - Attribute attribute; - CursorPosition cursor; - }; - - void carriage_return(); - inline void scroll_up(size_t count = 1); - inline void scroll_down(size_t count = 1); - void linefeed(); -#ifndef KERNEL - void scroll_up(u16 region_top, u16 region_bottom, size_t count); - void scroll_down(u16 region_top, u16 region_bottom, size_t count); - void scroll_left(u16 row, u16 column, size_t count); - void scroll_right(u16 row, u16 column, size_t count); - void put_character_at(unsigned row, unsigned column, u32 ch); - void clear_in_line(u16 row, u16 first_column, u16 last_column); -#else - virtual void scroll_up(u16 region_top, u16 region_bottom, size_t count) = 0; - virtual void scroll_down(u16 region_top, u16 region_bottom, size_t count) = 0; - virtual void scroll_left(u16 row, u16 column, size_t count) = 0; - virtual void scroll_right(u16 row, u16 column, size_t count) = 0; - virtual void put_character_at(unsigned row, unsigned column, u32 ch) = 0; - virtual void clear_in_line(u16 row, u16 first_column, u16 last_column) = 0; -#endif - - void unimplemented_control_code(u8); - void unimplemented_escape_sequence(Intermediates, u8 last_byte); - void unimplemented_csi_sequence(Parameters, Intermediates, u8 last_byte); - void unimplemented_osc_sequence(OscParameters, u8 last_byte); - - void emit_string(StringView); - - void alter_ansi_mode(bool should_set, Parameters); - void alter_private_mode(bool should_set, Parameters); - - // CUU – Cursor Up - void CUU(Parameters); - - // CUD – Cursor Down - void CUD(Parameters); - - // CUF – Cursor Forward - void CUF(Parameters); - - // CUB – Cursor Backward - void CUB(Parameters); - - // CNL - Cursor Next Line - void CNL(Parameters); - - // CPL - Cursor Previous Line - void CPL(Parameters); - - // CUP - Cursor Position - void CUP(Parameters); - - // ED - Erase in Display - void ED(Parameters); - - // EL - Erase in Line - void EL(Parameters); - - // SGR – Select Graphic Rendition - void SGR(Parameters); - - // Save Current Cursor Position - void SCOSC(); - - // Restore Saved Cursor Position - void SCORC(); - - // Save Cursor (and other attributes) - void DECSC(); - - // Restore Cursor (and other attributes) - void DECRC(); - - // DECSTBM – Set Top and Bottom Margins ("Scrolling Region") - void DECSTBM(Parameters); - - // RM – Reset Mode - void RM(Parameters); - - // DECRST - DEC Private Mode Reset - void DECRST(Parameters); - - // SM – Set Mode - void SM(Parameters); - - // DECSET - Dec Private Mode Set - void DECSET(Parameters); - - // DA - Device Attributes - void DA(Parameters); - - // HVP – Horizontal and Vertical Position - void HVP(Parameters); - - // NEL - Next Line - void NEL(); - - // IND - Index (move down) - void IND(); - - // RI - Reverse Index (move up) - void RI(); - - // DECBI - Back Index - void DECBI(); - - // DECFI - Forward Index - void DECFI(); - - // DSR - Device Status Reports - void DSR(Parameters); - - // DECSCUSR - Set Cursor Style - void DECSCUSR(Parameters); - - // ICH - Insert Character - void ICH(Parameters); - - // SU - Scroll Up (called "Pan Down" in VT510) - void SU(Parameters); - - // SD - Scroll Down (called "Pan Up" in VT510) - void SD(Parameters); - - // IL - Insert Line - void IL(Parameters); - - // DCH - Delete Character - void DCH(Parameters); - - // DL - Delete Line - void DL(Parameters); - - // CHA - Cursor Horizontal Absolute - void CHA(Parameters); - - // REP - Repeat - void REP(Parameters); - - // VPA - Line Position Absolute - void VPA(Parameters); - - // VPR - Line Position Relative - void VPR(Parameters); - - // HPA - Character Position Absolute - void HPA(Parameters); - - // HPR - Character Position Relative - void HPR(Parameters); - - // ECH - Erase Character - void ECH(Parameters); - - // FIXME: Find the right names for these. - void XTERM_WM(Parameters); - - // DECIC - Insert Column - void DECIC(Parameters); - - // DECDC - Delete Column - void DECDC(Parameters); - - // DECPNM - Set numeric keypad mode - void DECKPNM(); - - // DECPAM - Set application keypad mode - void DECKPAM(); - -#ifndef KERNEL - TerminalClient& m_client; -#else - Kernel::VirtualConsole& m_client; -#endif - - EscapeSequenceParser m_parser; -#ifndef KERNEL - size_t m_history_start = 0; - Vector> m_history; - void add_line_to_history(NonnullOwnPtr&& line) - { - if (max_history_size() == 0) - return; - - // If m_history can expand, add the new line to the end of the list. - // If there is an overflow wrap, the end is at the index before the start. - if (m_history.size() < max_history_size()) { - if (m_history_start == 0) - m_history.append(move(line)); - else - m_history.insert(m_history_start - 1, move(line)); - - return; - } - m_history[m_history_start] = move(line); - m_history_start = (m_history_start + 1) % m_history.size(); - } - - Vector>& active_buffer() { return m_use_alternate_screen_buffer ? m_alternate_screen_buffer : m_normal_screen_buffer; } - Vector> const& active_buffer() const { return m_use_alternate_screen_buffer ? m_alternate_screen_buffer : m_normal_screen_buffer; } - Vector> m_normal_screen_buffer; - Vector> m_alternate_screen_buffer; -#endif - - bool m_use_alternate_screen_buffer { false }; - - size_t m_scroll_region_top { 0 }; - size_t m_scroll_region_bottom { 0 }; - - u16 m_columns { 1 }; - u16 m_rows { 1 }; - - BufferState m_current_state; - BufferState m_normal_saved_state; - BufferState m_alternate_saved_state; - - // Separate from *_saved_state: some escape sequences only save/restore the cursor position, - // while others impact the text attributes and other state too. - CursorPosition m_saved_cursor_position; - - bool m_swallow_current { false }; - bool m_stomp { false }; - bool m_in_application_keypad_mode { false }; - - CursorShape m_cursor_shape { VT::CursorShape::Block }; - CursorShape m_saved_cursor_shape { VT::CursorShape::Block }; - bool m_cursor_is_blinking_set { true }; - - bool m_needs_bracketed_paste { false }; - - Attribute m_current_attribute; - Attribute m_saved_attribute; - -#ifndef KERNEL - ByteString m_current_window_title; - Vector m_title_stack; - OrderedHashTable m_valid_marks; -#endif - -#ifndef KERNEL - u32 m_next_href_id { 0 }; -#endif - - Vector m_horizontal_tabs; - u32 m_last_code_point { 0 }; - size_t m_max_history_lines { 1024 }; - - Optional m_column_before_carriage_return; - bool m_controls_are_logically_generated { false }; - CursorKeysMode m_cursor_keys_mode { Cursor }; - - CharacterSetTranslator m_character_set_translator {}; - size_t m_active_working_set_index { 0 }; - CharacterSet m_working_sets[4] { Iso_8859_1 }; -}; - -} diff --git a/Userland/Libraries/LibVT/TerminalWidget.cpp b/Userland/Libraries/LibVT/TerminalWidget.cpp deleted file mode 100644 index 9f9cfd7f239..00000000000 --- a/Userland/Libraries/LibVT/TerminalWidget.cpp +++ /dev/null @@ -1,1468 +0,0 @@ -/* - * Copyright (c) 2018-2021, Andreas Kling - * Copyright (c) 2022, the SerenityOS developers. - * Copyright (c) 2023, networkException - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "TerminalWidget.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace VT { - -void TerminalWidget::set_pty_master_fd(int fd) -{ - m_ptm_fd = fd; - if (m_ptm_fd == -1) { - m_notifier = nullptr; - return; - } - m_notifier = Core::Notifier::construct(m_ptm_fd, Core::Notifier::Type::Read); - m_notifier->on_activation = [this] { - u8 buffer[BUFSIZ]; - ssize_t nread = read(m_ptm_fd, buffer, sizeof(buffer)); - if (nread < 0) { - dbgln("Terminal read error: {}", strerror(errno)); - perror("read(ptm)"); - GUI::Application::the()->quit(1); - return; - } - if (nread == 0) { - dbgln("TerminalWidget: EOF on master pty, firing on_command_exit hook."); - if (on_command_exit) - on_command_exit(); - int rc = close(m_ptm_fd); - if (rc < 0) { - perror("close"); - } - set_pty_master_fd(-1); - return; - } - - for (ssize_t i = 0; i < nread; ++i) - m_terminal.on_input(buffer[i]); - - auto owned_by_startup_process = m_startup_process_owns_pty; - auto pgrp = tcgetpgrp(m_ptm_fd); - m_startup_process_owns_pty = pgrp == m_startup_process_id; - if (m_startup_process_owns_pty != owned_by_startup_process) { - // pty owner state changed, handle it. - handle_pty_owner_change(pgrp); - } - - flush_dirty_lines(); - }; -} - -TerminalWidget::TerminalWidget(int ptm_fd, bool automatic_size_policy) - : m_terminal(*this) - , m_automatic_size_policy(automatic_size_policy) -{ - static_assert(sizeof(m_colors) == sizeof(xterm_colors)); - memcpy(m_colors, xterm_colors, sizeof(m_colors)); - - set_override_cursor(Gfx::StandardCursor::IBeam); - set_focus_policy(GUI::FocusPolicy::StrongFocus); - set_pty_master_fd(ptm_fd); - - on_emoji_input = [this](auto emoji) { - emit(emoji.bytes().data(), emoji.length()); - }; - - m_cursor_blink_timer = add(); - m_visual_beep_timer = add(); - m_auto_scroll_timer = add(); - - m_scrollbar = add(Orientation::Vertical); - m_scrollbar->set_scroll_animation(GUI::Scrollbar::Animation::CoarseScroll); - m_scrollbar->set_relative_rect(0, 0, 16, 0); - m_scrollbar->on_change = [this](int) { - update(); - }; - - m_cursor_blink_timer->set_interval(Config::read_i32("Terminal"sv, "Text"sv, "CursorBlinkInterval"sv, 500)); - m_cursor_blink_timer->on_timeout = [this] { - m_cursor_blink_state = !m_cursor_blink_state; - update_cursor(); - }; - - m_auto_scroll_timer->set_interval(50); - m_auto_scroll_timer->on_timeout = [this] { - if (m_auto_scroll_direction != AutoScrollDirection::None) { - int scroll_amount = m_auto_scroll_direction == AutoScrollDirection::Up ? -1 : 1; - m_scrollbar->increase_slider_by(scroll_amount); - } - }; - m_auto_scroll_timer->start(); - - auto font_entry = Config::read_string("Terminal"sv, "Text"sv, "Font"sv, "default"sv); - if (font_entry == "default") - set_font(Gfx::FontDatabase::default_fixed_width_font()); - else - set_font(Gfx::FontDatabase::the().get_by_name(font_entry)); - - update_cached_font_metrics(); - - m_terminal.set_size(Config::read_i32("Terminal"sv, "Window"sv, "Width"sv, 80), Config::read_i32("Terminal"sv, "Window"sv, "Height"sv, 25)); - - m_copy_action = GUI::Action::create("&Copy", { Mod_Ctrl | Mod_Shift, Key_C }, Gfx::Bitmap::load_from_file("/res/icons/16x16/edit-copy.png"sv).release_value_but_fixme_should_propagate_errors(), [this](auto&) { - copy(); - }); - m_copy_action->set_swallow_key_event_when_disabled(true); - - m_paste_action = GUI::Action::create("&Paste", { Mod_Ctrl | Mod_Shift, Key_V }, Gfx::Bitmap::load_from_file("/res/icons/16x16/paste.png"sv).release_value_but_fixme_should_propagate_errors(), [this](auto&) { - paste(); - }); - m_paste_action->set_swallow_key_event_when_disabled(true); - - m_clear_including_history_action = GUI::Action::create("Clear Including &History", { Mod_Ctrl | Mod_Shift, Key_K }, [this](auto&) { - clear_including_history(); - }); - - m_clear_to_previous_mark_action = GUI::Action::create("Clear &Previous Command", { Mod_Ctrl | Mod_Shift, Key_U }, [this](auto&) { - clear_to_previous_mark(); - }); - - m_context_menu = GUI::Menu::construct(); - m_context_menu->add_action(copy_action()); - m_context_menu->add_action(paste_action()); - m_context_menu->add_separator(); - m_context_menu->add_action(clear_including_history_action()); - m_context_menu->add_action(clear_to_previous_mark_action()); - - update_copy_action(); - update_paste_action(); - update_color_scheme(); -} - -Gfx::IntRect TerminalWidget::glyph_rect(u16 row, u16 column) -{ - int y = row * m_line_height; - int x = column * m_column_width; - return { x + frame_thickness() + m_inset, y + frame_thickness() + m_inset, m_column_width, m_cell_height }; -} - -Gfx::IntRect TerminalWidget::row_rect(u16 row) -{ - int y = row * m_line_height; - Gfx::IntRect rect = { frame_thickness() + m_inset, y + frame_thickness() + m_inset, m_column_width * m_terminal.columns(), m_cell_height }; - rect.inflate(0, m_line_spacing); - return rect; -} - -void TerminalWidget::set_logical_focus(bool focus) -{ - m_has_logical_focus = focus; - if (!m_has_logical_focus) { - m_cursor_blink_timer->stop(); - m_cursor_blink_state = true; - } else if (m_cursor_is_blinking_set) { - m_cursor_blink_timer->stop(); - m_cursor_blink_state = true; - m_cursor_blink_timer->start(); - } - set_auto_scroll_direction(AutoScrollDirection::None); - invalidate_cursor(); - update(); -} - -void TerminalWidget::focusin_event(GUI::FocusEvent& event) -{ - set_logical_focus(true); - return GUI::Frame::focusin_event(event); -} - -void TerminalWidget::focusout_event(GUI::FocusEvent& event) -{ - set_logical_focus(false); - return GUI::Frame::focusout_event(event); -} - -void TerminalWidget::event(Core::Event& event) -{ - if (event.type() == GUI::Event::ThemeChange) - update_color_scheme(); - else if (event.type() == GUI::Event::WindowBecameActive) - set_logical_focus(true); - else if (event.type() == GUI::Event::WindowBecameInactive) - set_logical_focus(false); - return GUI::Frame::event(event); -} - -void TerminalWidget::keydown_event(GUI::KeyEvent& event) -{ - // We specifically need to process shortcuts before input to the Terminal is done - // since otherwise escape sequences will eat all our shortcuts for dinner. - window()->propagate_shortcuts(event, this); - if (event.is_accepted()) - return; - - if (m_ptm_fd == -1) { - return GUI::Frame::keydown_event(event); - } - - // Reset timer so cursor doesn't blink while typing. - if (m_cursor_is_blinking_set) { - m_cursor_blink_timer->stop(); - m_cursor_blink_state = true; - m_cursor_blink_timer->start(); - } - - if (event.key() == KeyCode::Key_PageUp && event.modifiers() == Mod_Shift) { - m_scrollbar->decrease_slider_by(m_terminal.rows()); - return; - } - if (event.key() == KeyCode::Key_PageDown && event.modifiers() == Mod_Shift) { - m_scrollbar->increase_slider_by(m_terminal.rows()); - return; - } - if (event.key() == KeyCode::Key_Alt) { - m_alt_key_held = true; - return; - } - - // Clear the selection if we type in/behind it. - auto future_cursor_column = (event.key() == KeyCode::Key_Backspace) ? m_terminal.cursor_column() - 1 : m_terminal.cursor_column(); - auto min_selection_row = min(m_selection.start().row(), m_selection.end().row()); - auto max_selection_row = max(m_selection.start().row(), m_selection.end().row()); - - if (future_cursor_column <= last_selection_column_on_row(m_terminal.cursor_row()) && m_terminal.cursor_row() >= min_selection_row && m_terminal.cursor_row() <= max_selection_row) { - m_selection.set_end({}); - update_copy_action(); - update(); - } - - m_terminal.handle_key_press(event.key(), event.code_point(), event.modifiers()); - - if (event.key() != Key_Control && event.key() != Key_Alt && event.key() != Key_LeftShift && event.key() != Key_RightShift && event.key() != Key_Super) - scroll_to_bottom(); -} - -void TerminalWidget::keyup_event(GUI::KeyEvent& event) -{ - switch (event.key()) { - case KeyCode::Key_Alt: - m_alt_key_held = false; - return; - default: - break; - } -} - -void TerminalWidget::paint_event(GUI::PaintEvent& event) -{ - GUI::Frame::paint_event(event); - - GUI::Painter painter(*this); - - auto visual_beep_active = m_visual_beep_timer->is_active(); - - painter.add_clip_rect(event.rect()); - - if (visual_beep_active) - painter.clear_rect(frame_inner_rect(), terminal_color_to_rgb(VT::Color::named(VT::Color::ANSIColor::Red))); - else - painter.clear_rect(frame_inner_rect(), terminal_color_to_rgb(VT::Color::named(VT::Color::ANSIColor::DefaultBackground)).with_alpha(m_opacity)); - invalidate_cursor(); - - int rows_from_history = 0; - int first_row_from_history = m_terminal.history_size(); - int row_with_cursor = m_terminal.cursor_row(); - if (m_scrollbar->value() != m_scrollbar->max()) { - rows_from_history = min((int)m_terminal.rows(), m_scrollbar->max() - m_scrollbar->value()); - first_row_from_history = m_terminal.history_size() - (m_scrollbar->max() - m_scrollbar->value()); - row_with_cursor = m_terminal.cursor_row() + rows_from_history; - } - - // Pass: Compute the rect(s) of the currently hovered link, if any. - Vector hovered_href_rects; - if (m_hovered_href_id.has_value()) { - for (u16 visual_row = 0; visual_row < m_terminal.rows(); ++visual_row) { - auto& line = m_terminal.line(first_row_from_history + visual_row); - for (size_t column = 0; column < line.length(); ++column) { - if (m_hovered_href_id == line.attribute_at(column).href_id) { - bool merged_with_existing_rect = false; - auto glyph_rect = this->glyph_rect(visual_row, column); - for (auto& rect : hovered_href_rects) { - if (rect.inflated(1, 1).intersects(glyph_rect)) { - rect = rect.united(glyph_rect); - merged_with_existing_rect = true; - break; - } - } - if (!merged_with_existing_rect) - hovered_href_rects.append(glyph_rect); - } - } - } - } - - // Pass: Paint background & text decorations. - for (u16 visual_row = 0; visual_row < m_terminal.rows(); ++visual_row) { - auto row_rect = this->row_rect(visual_row); - if (!event.rect().contains(row_rect)) - continue; - auto& line = m_terminal.line(first_row_from_history + visual_row); - bool has_only_one_background_color = line.has_only_one_background_color(); - if (visual_beep_active) - painter.clear_rect(row_rect, terminal_color_to_rgb(VT::Color::named(VT::Color::ANSIColor::Red))); - else if (has_only_one_background_color) - painter.clear_rect(row_rect, terminal_color_to_rgb(line.attribute_at(0).effective_background_color()).with_alpha(m_opacity)); - - for (size_t column = 0; column < line.length(); ++column) { - bool should_reverse_fill_for_cursor_or_selection = m_cursor_blink_state - && m_cursor_shape == VT::CursorShape::Block - && m_has_logical_focus - && visual_row == row_with_cursor - && column == m_terminal.cursor_column(); - should_reverse_fill_for_cursor_or_selection |= selection_contains({ first_row_from_history + visual_row, (int)column }); - auto attribute = line.attribute_at(column); - auto character_rect = glyph_rect(visual_row, column); - auto cell_rect = character_rect.inflated(0, m_line_spacing); - auto text_color_before_bold_change = should_reverse_fill_for_cursor_or_selection ? attribute.effective_background_color() : attribute.effective_foreground_color(); - auto text_color = terminal_color_to_rgb(m_show_bold_text_as_bright ? text_color_before_bold_change.to_bright() : text_color_before_bold_change); - if ((!visual_beep_active && !has_only_one_background_color) || should_reverse_fill_for_cursor_or_selection) - painter.clear_rect(cell_rect, terminal_color_to_rgb(should_reverse_fill_for_cursor_or_selection ? attribute.effective_foreground_color() : attribute.effective_background_color())); - - if constexpr (TERMINAL_DEBUG) { - if (line.termination_column() == column) - painter.clear_rect(cell_rect, Gfx::Color::Magenta); - } - - enum class UnderlineStyle { - None, - Dotted, - Solid, - }; - - auto underline_style = UnderlineStyle::None; - auto underline_color = text_color; - - if (has_flag(attribute.flags, VT::Attribute::Flags::Underline)) { - // Content has specified underline - underline_style = UnderlineStyle::Solid; - } else if (!attribute.href.is_empty()) { - // We're hovering a hyperlink - if (m_hovered_href_id == attribute.href_id || m_active_href_id == attribute.href_id) { - underline_style = UnderlineStyle::Solid; - underline_color = palette().active_link(); - } else { - underline_style = UnderlineStyle::Dotted; - underline_color = text_color.darkened(0.6f); - } - } - - if (underline_style == UnderlineStyle::Solid) { - painter.draw_line(cell_rect.bottom_left().moved_up(1), cell_rect.bottom_right().translated(-1), underline_color); - } else if (underline_style == UnderlineStyle::Dotted) { - int x1 = cell_rect.left(); - int x2 = cell_rect.right(); - int y = cell_rect.bottom() - 1; - for (int x = x1; x < x2; ++x) { - if ((x % 3) == 0) - painter.set_pixel({ x, y }, underline_color); - } - } - } - } - - // Paint the hovered link rects, if any. - for (auto rect : hovered_href_rects) { - rect.inflate(6, 6); - painter.fill_rect(rect, palette().base()); - painter.draw_rect(rect.inflated(2, 2).intersected(frame_inner_rect()), palette().base_text()); - } - - auto& font = this->font(); - auto& bold_font = font.bold_variant(); - - // Pass: Paint foreground (text). - for (u16 visual_row = 0; visual_row < m_terminal.rows(); ++visual_row) { - auto row_rect = this->row_rect(visual_row); - if (!event.rect().contains(row_rect)) - continue; - auto& line = m_terminal.line(first_row_from_history + visual_row); - for (size_t column = 0; column < line.length(); ++column) { - auto attribute = line.attribute_at(column); - bool should_reverse_fill_for_cursor_or_selection = m_cursor_blink_state - && m_cursor_shape == VT::CursorShape::Block - && m_has_logical_focus - && visual_row == row_with_cursor - && column == m_terminal.cursor_column(); - should_reverse_fill_for_cursor_or_selection |= selection_contains({ first_row_from_history + visual_row, (int)column }); - auto text_color_before_bold_change = should_reverse_fill_for_cursor_or_selection ? attribute.effective_background_color() : attribute.effective_foreground_color(); - auto text_color = terminal_color_to_rgb(m_show_bold_text_as_bright ? text_color_before_bold_change.to_bright() : text_color_before_bold_change); - u32 code_point = line.code_point(column); - - if (code_point == ' ') - continue; - - auto character_rect = glyph_rect(visual_row, column); - - if (m_hovered_href_id.has_value() && attribute.href_id == m_hovered_href_id) { - text_color = palette().base_text(); - } - - painter.draw_glyph_or_emoji( - character_rect.location(), - code_point, - has_flag(attribute.flags, VT::Attribute::Flags::Bold) ? bold_font : font, - text_color); - } - } - - // Draw cursor. - if (m_cursor_blink_state && row_with_cursor < m_terminal.rows()) { - auto& cursor_line = m_terminal.line(first_row_from_history + row_with_cursor); - if (m_terminal.cursor_row() >= (m_terminal.rows() - rows_from_history)) - return; - - if (m_has_logical_focus && m_cursor_shape == VT::CursorShape::Block) - return; // This has already been handled by inverting the cell colors - - auto cursor_color = terminal_color_to_rgb(cursor_line.attribute_at(m_terminal.cursor_column()).effective_foreground_color()); - auto cell_rect = glyph_rect(row_with_cursor, m_terminal.cursor_column()).inflated(0, m_line_spacing); - if (m_cursor_shape == VT::CursorShape::Underline) { - auto x1 = cell_rect.left(); - auto x2 = cell_rect.right(); - auto y = cell_rect.bottom() - 1; - for (auto x = x1; x < x2; ++x) - painter.set_pixel({ x, y }, cursor_color); - } else if (m_cursor_shape == VT::CursorShape::Bar) { - auto x = cell_rect.left(); - auto y1 = cell_rect.top(); - auto y2 = cell_rect.bottom(); - for (auto y = y1; y < y2; ++y) - painter.set_pixel({ x, y }, cursor_color); - } else { - // We fall back to a block if we don't support the selected cursor type. - painter.draw_rect(cell_rect, cursor_color); - } - } -} - -void TerminalWidget::set_window_progress(int value, int max) -{ - float float_value = value; - float float_max = max; - float progress = (float_value / float_max) * 100.0f; - window()->set_progress((int)roundf(progress)); -} - -void TerminalWidget::set_window_title(StringView title) -{ - if (!Utf8View(title).validate()) { - dbgln("TerminalWidget: Attempted to set window title to invalid UTF-8 string"); - return; - } - - if (on_title_change) - on_title_change(title); -} - -void TerminalWidget::invalidate_cursor() -{ - m_terminal.invalidate_cursor(); -} - -void TerminalWidget::flush_dirty_lines() -{ - // FIXME: Update smarter when scrolled - if (m_terminal.m_need_full_flush || m_scrollbar->value() != m_scrollbar->max()) { - update(); - m_terminal.m_need_full_flush = false; - return; - } - Gfx::IntRect rect; - for (int i = 0; i < m_terminal.rows(); ++i) { - if (m_terminal.visible_line(i).is_dirty()) { - rect = rect.united(row_rect(i)); - m_terminal.visible_line(i).set_dirty(false); - } - } - update(rect); -} - -void TerminalWidget::resize_event(GUI::ResizeEvent& event) -{ - relayout(event.size()); -} - -void TerminalWidget::relayout(Gfx::IntSize size) -{ - if (!m_scrollbar) - return; - - TemporaryChange change(m_in_relayout, true); - - auto base_size = compute_base_size(); - int new_columns = (size.width() - base_size.width()) / m_column_width; - int new_rows = (size.height() - base_size.height()) / m_line_height; - m_terminal.set_size(new_columns, new_rows); - - Gfx::IntRect scrollbar_rect = { - size.width() - m_scrollbar->width() - frame_thickness(), - frame_thickness(), - m_scrollbar->width(), - size.height() - frame_thickness() * 2, - }; - m_scrollbar->set_relative_rect(scrollbar_rect); - m_scrollbar->set_page_step(new_rows); -} - -Gfx::IntSize TerminalWidget::compute_base_size() const -{ - int base_width = frame_thickness() * 2 + m_inset * 2 + (m_scrollbar->is_visible() ? m_scrollbar->width() : 0); - int base_height = frame_thickness() * 2 + m_inset * 2; - return { base_width, base_height }; -} - -void TerminalWidget::apply_size_increments_to_window(GUI::Window& window) -{ - window.set_size_increment({ m_column_width, m_line_height }); - window.set_base_size(compute_base_size()); -} - -void TerminalWidget::update_cursor() -{ - invalidate_cursor(); - flush_dirty_lines(); -} - -void TerminalWidget::set_opacity(u8 new_opacity) -{ - if (m_opacity == new_opacity) - return; - - window()->set_has_alpha_channel(new_opacity < 255); - m_opacity = new_opacity; - update(); -} - -void TerminalWidget::set_show_scrollbar(bool show_scrollbar) -{ - m_scrollbar->set_visible(show_scrollbar); - relayout(size()); -} - -bool TerminalWidget::has_selection() const -{ - return m_selection.is_valid(); -} - -void TerminalWidget::set_selection(const VT::Range& selection) -{ - m_selection = selection; - update_copy_action(); - update(); -} - -bool TerminalWidget::selection_contains(const VT::Position& position) const -{ - if (!has_selection()) - return false; - - if (m_rectangle_selection) { - auto m_selection_start = m_selection.start(); - auto m_selection_end = m_selection.end(); - auto min_selection_column = min(m_selection_start.column(), m_selection_end.column()); - auto max_selection_column = max(m_selection_start.column(), m_selection_end.column()); - auto min_selection_row = min(m_selection_start.row(), m_selection_end.row()); - auto max_selection_row = max(m_selection_start.row(), m_selection_end.row()); - - return position.column() >= min_selection_column && position.column() <= max_selection_column && position.row() >= min_selection_row && position.row() <= max_selection_row; - } - - auto normalized_selection = m_selection.normalized(); - return position >= normalized_selection.start() && position <= normalized_selection.end(); -} - -VT::Position TerminalWidget::buffer_position_at(Gfx::IntPoint position) const -{ - auto adjusted_position = position.translated(-(frame_thickness() + m_inset), -(frame_thickness() + m_inset)); - int row = adjusted_position.y() / m_line_height; - int column = adjusted_position.x() / m_column_width; - if (row < 0) - row = 0; - if (column < 0) - column = 0; - if (row >= m_terminal.rows()) - row = m_terminal.rows() - 1; - auto& line = m_terminal.line(row); - if (column >= (int)line.length()) - column = line.length() - 1; - row += m_scrollbar->value(); - return { row, column }; -} - -u32 TerminalWidget::code_point_at(const VT::Position& position) const -{ - VERIFY(position.is_valid()); - VERIFY(position.row() >= 0 && static_cast(position.row()) < m_terminal.line_count()); - auto& line = m_terminal.line(position.row()); - if (static_cast(position.column()) == line.length()) - return '\n'; - return line.code_point(position.column()); -} - -VT::Position TerminalWidget::next_position_after(const VT::Position& position, bool should_wrap) const -{ - VERIFY(position.is_valid()); - VERIFY(position.row() >= 0 && static_cast(position.row()) < m_terminal.line_count()); - auto& line = m_terminal.line(position.row()); - if (static_cast(position.column()) == line.length()) { - if (static_cast(position.row()) == m_terminal.line_count() - 1) { - if (should_wrap) - return { 0, 0 }; - return {}; - } - return { position.row() + 1, 0 }; - } - return { position.row(), position.column() + 1 }; -} - -VT::Position TerminalWidget::previous_position_before(const VT::Position& position, bool should_wrap) const -{ - VERIFY(position.row() >= 0 && static_cast(position.row()) < m_terminal.line_count()); - if (position.column() == 0) { - if (position.row() == 0) { - if (should_wrap) { - auto& last_line = m_terminal.line(m_terminal.line_count() - 1); - return { static_cast(m_terminal.line_count() - 1), static_cast(last_line.length()) }; - } - return {}; - } - auto& prev_line = m_terminal.line(position.row() - 1); - return { position.row() - 1, static_cast(prev_line.length()) }; - } - return { position.row(), position.column() - 1 }; -} - -static u32 to_lowercase_code_point(u32 code_point) -{ - // FIXME: this only handles ascii characters, but handling unicode lowercasing seems like a mess - if (code_point < 128) - return tolower(code_point); - return code_point; -} - -VT::Range TerminalWidget::find_next(StringView needle, const VT::Position& start, bool case_sensitivity, bool should_wrap) -{ - if (needle.is_empty()) - return {}; - - VT::Position position = start.is_valid() ? start : VT::Position(0, 0); - VT::Position original_position = position; - - VT::Position start_of_potential_match; - size_t needle_index = 0; - - Utf8View unicode_needle(needle); - Vector needle_code_points; - for (u32 code_point : unicode_needle) - needle_code_points.append(code_point); - - do { - auto ch = code_point_at(position); - - bool code_point_matches = false; - if (needle_index >= needle_code_points.size()) - code_point_matches = false; - else if (case_sensitivity) - code_point_matches = ch == needle_code_points[needle_index]; - else - code_point_matches = to_lowercase_code_point(ch) == to_lowercase_code_point(needle_code_points[needle_index]); - - if (code_point_matches) { - if (needle_index == 0) - start_of_potential_match = position; - ++needle_index; - if (needle_index >= needle_code_points.size()) - return { start_of_potential_match, position }; - } else { - if (needle_index > 0) - position = start_of_potential_match; - needle_index = 0; - } - position = next_position_after(position, should_wrap); - } while (position.is_valid() && position != original_position); - - return {}; -} - -VT::Range TerminalWidget::find_previous(StringView needle, const VT::Position& start, bool case_sensitivity, bool should_wrap) -{ - if (needle.is_empty()) - return {}; - - VT::Position position = start.is_valid() ? start : VT::Position(m_terminal.line_count() - 1, m_terminal.line(m_terminal.line_count() - 1).length() - 1); - VT::Position original_position = position; - - Utf8View unicode_needle(needle); - Vector needle_code_points; - for (u32 code_point : unicode_needle) - needle_code_points.append(code_point); - - VT::Position end_of_potential_match; - size_t needle_index = needle_code_points.size() - 1; - - do { - auto ch = code_point_at(position); - - bool code_point_matches = false; - if (needle_index >= needle_code_points.size()) - code_point_matches = false; - else if (case_sensitivity) - code_point_matches = ch == needle_code_points[needle_index]; - else - code_point_matches = to_lowercase_code_point(ch) == to_lowercase_code_point(needle_code_points[needle_index]); - - if (code_point_matches) { - if (needle_index == needle_code_points.size() - 1) - end_of_potential_match = position; - if (needle_index == 0) - return { position, end_of_potential_match }; - --needle_index; - } else { - if (needle_index < needle_code_points.size() - 1) - position = end_of_potential_match; - needle_index = needle_code_points.size() - 1; - } - position = previous_position_before(position, should_wrap); - } while (position.is_valid() && position != original_position); - - return {}; -} - -void TerminalWidget::doubleclick_event(GUI::MouseEvent& event) -{ - if (event.button() == GUI::MouseButton::Primary) { - auto attribute = m_terminal.attribute_at(buffer_position_at(event.position())); - if (attribute.href_id.has_value()) { - dbgln("Open hyperlinked URL: '{}'", attribute.href); - Desktop::Launcher::open(attribute.href); - return; - } - - m_triple_click_timer.start(); - - auto position = buffer_position_at(event.position()); - auto& line = m_terminal.line(position.row()); - bool want_whitespace = line.code_point(position.column()) == ' '; - - int start_column = 0; - int end_column = 0; - - for (int column = position.column(); column >= 0 && (line.code_point(column) == ' ') == want_whitespace; --column) { - start_column = column; - } - - for (int column = position.column(); column < (int)line.length() && (line.code_point(column) == ' ') == want_whitespace; ++column) { - end_column = column; - } - - m_selection.set({ position.row(), start_column }, { position.row(), end_column }); - update_copy_action(); - update(); - } - GUI::Frame::doubleclick_event(event); -} - -void TerminalWidget::paste() -{ - if (m_ptm_fd == -1) - return; - - auto [data, mime_type, _] = GUI::Clipboard::the().fetch_data_and_type(); - if (!mime_type.starts_with("text/"sv)) - return; - if (data.is_empty()) - return; - send_non_user_input(data); -} - -void TerminalWidget::copy() -{ - if (has_selection()) - GUI::Clipboard::the().set_plain_text(selected_text()); -} - -void TerminalWidget::mouseup_event(GUI::MouseEvent& event) -{ - if (event.button() == GUI::MouseButton::Primary) { - if (m_active_href_id.has_value()) { - m_active_href = {}; - m_active_href_id = {}; - update(); - } - - if (m_triple_click_timer.is_valid()) - m_triple_click_timer.reset(); - - set_auto_scroll_direction(AutoScrollDirection::None); - } -} - -void TerminalWidget::mousedown_event(GUI::MouseEvent& event) -{ - using namespace AK::TimeLiterals; - - if (event.button() == GUI::MouseButton::Primary) { - m_left_mousedown_position = event.position(); - m_left_mousedown_position_buffer = buffer_position_at(m_left_mousedown_position); - - auto attribute = m_terminal.attribute_at(m_left_mousedown_position_buffer); - if (!(event.modifiers() & Mod_Shift) && !attribute.href.is_empty()) { - m_active_href = attribute.href; - m_active_href_id = attribute.href_id; - update(); - return; - } - m_active_href = {}; - m_active_href_id = {}; - - if (m_triple_click_timer.is_valid() && m_triple_click_timer.elapsed_time() < 250_ms) { - int start_column = 0; - int end_column = m_terminal.columns() - 1; - - m_selection.set({ m_left_mousedown_position_buffer.row(), start_column }, { m_left_mousedown_position_buffer.row(), end_column }); - } else { - m_selection.set(m_left_mousedown_position_buffer, {}); - } - if (m_alt_key_held) - m_rectangle_selection = true; - else if (m_rectangle_selection) - m_rectangle_selection = false; - - update_copy_action(); - update(); - } -} - -void TerminalWidget::mousemove_event(GUI::MouseEvent& event) -{ - auto position = buffer_position_at(event.position()); - - auto attribute = m_terminal.attribute_at(position); - - if (attribute.href_id != m_hovered_href_id) { - if (attribute.href_id.has_value()) { - m_hovered_href_id = attribute.href_id; - m_hovered_href = attribute.href; - - auto handlers = Desktop::Launcher::get_handlers_for_url(attribute.href); - if (!handlers.is_empty()) { - auto url = URL::URL(attribute.href); - auto path = url.serialize_path(); - - auto app_file = Desktop::AppFile::get_for_app(LexicalPath::basename(handlers[0])); - auto app_name = app_file->is_valid() ? app_file->name() : LexicalPath::basename(handlers[0]); - - if (url.scheme() == "file") { - auto file_name = LexicalPath::basename(path); - - if (path == handlers[0]) { - set_tooltip(MUST(String::formatted("Execute {}", app_name))); - } else { - set_tooltip(MUST(String::formatted("Open {} with {}", file_name, app_name))); - } - } else { - set_tooltip(MUST(String::formatted("Open {} with {}", attribute.href, app_name))); - } - } - } else { - m_hovered_href_id = {}; - m_hovered_href = {}; - set_tooltip({}); - } - show_or_hide_tooltip(); - if (!m_hovered_href.is_empty()) - set_override_cursor(Gfx::StandardCursor::Arrow); - else - set_override_cursor(Gfx::StandardCursor::IBeam); - update(); - } - - if (!(event.buttons() & GUI::MouseButton::Primary)) - return; - - if (m_active_href_id.has_value()) { - auto diff = event.position() - m_left_mousedown_position; - auto distance_travelled_squared = diff.x() * diff.x() + diff.y() * diff.y(); - constexpr int drag_distance_threshold = 5; - - if (distance_travelled_squared <= drag_distance_threshold) - return; - - auto drag_operation = GUI::DragOperation::construct(); - drag_operation->set_text(m_active_href); - drag_operation->set_data("text/uri-list"_string, m_active_href); - - m_active_href = {}; - m_active_href_id = {}; - m_hovered_href = {}; - m_hovered_href_id = {}; - drag_operation->exec(); - update(); - return; - } - - auto adjusted_position = event.position().translated(-(frame_thickness() + m_inset), -(frame_thickness() + m_inset)); - if (adjusted_position.y() < 0) - set_auto_scroll_direction(AutoScrollDirection::Up); - else if (adjusted_position.y() > m_terminal.rows() * m_line_height) - set_auto_scroll_direction(AutoScrollDirection::Down); - else - set_auto_scroll_direction(AutoScrollDirection::None); - - VT::Position old_selection_end = m_selection.end(); - VT::Position old_selection_start = m_selection.start(); - - if (m_triple_click_timer.is_valid()) { - int start_column = 0; - int end_column = m_terminal.columns() - 1; - - if (position.row() < m_left_mousedown_position_buffer.row()) - m_selection.set({ position.row(), start_column }, { m_left_mousedown_position_buffer.row(), end_column }); - else - m_selection.set({ m_left_mousedown_position_buffer.row(), start_column }, { position.row(), end_column }); - } else { - m_selection.set_end(position); - } - - if (old_selection_end != m_selection.end() || old_selection_start != m_selection.start()) { - update_copy_action(); - update(); - } -} - -void TerminalWidget::leave_event(Core::Event&) -{ - bool should_update = !m_hovered_href.is_empty(); - m_hovered_href = {}; - m_hovered_href_id = {}; - set_tooltip({}); - set_override_cursor(Gfx::StandardCursor::IBeam); - if (should_update) - update(); -} - -void TerminalWidget::mousewheel_event(GUI::MouseEvent& event) -{ - if (!is_scrollable()) - return; - set_auto_scroll_direction(AutoScrollDirection::None); - m_scrollbar->increase_slider_by(event.wheel_delta_y() * scroll_length()); - GUI::Frame::mousewheel_event(event); -} - -bool TerminalWidget::is_scrollable() const -{ - return m_scrollbar->is_scrollable(); -} - -int TerminalWidget::scroll_length() const -{ - return m_scrollbar->step(); -} - -ByteString TerminalWidget::selected_text() const -{ - StringBuilder builder; - - auto normalized_selection = m_selection.normalized(); - auto start = normalized_selection.start(); - auto end = normalized_selection.end(); - - for (int row = start.row(); row <= end.row(); ++row) { - int first_column = first_selection_column_on_row(row); - int last_column = last_selection_column_on_row(row); - for (int column = first_column; column <= last_column; ++column) { - auto& line = m_terminal.line(row); - if (line.attribute_at(column).is_untouched()) { - builder.append('\n'); - break; - } - // FIXME: This is a bit hackish. - u32 code_point = line.code_point(column); - builder.append(Utf32View(&code_point, 1)); - if (column == static_cast(line.length()) - 1 || (m_rectangle_selection && column == last_column)) { - builder.append('\n'); - } - } - } - - return builder.to_byte_string(); -} - -int TerminalWidget::first_selection_column_on_row(int row) const -{ - auto normalized_selection_start = m_selection.normalized().start(); - return row == normalized_selection_start.row() || m_rectangle_selection ? normalized_selection_start.column() : 0; -} - -int TerminalWidget::last_selection_column_on_row(int row) const -{ - auto normalized_selection_end = m_selection.normalized().end(); - return row == normalized_selection_end.row() || m_rectangle_selection ? normalized_selection_end.column() : m_terminal.columns() - 1; -} - -void TerminalWidget::terminal_history_changed(int delta) -{ - bool was_max = m_scrollbar->value() == m_scrollbar->max(); - m_scrollbar->set_max(m_terminal.history_size()); - if (was_max) - m_scrollbar->set_value(m_scrollbar->max()); - m_scrollbar->update(); - // If the history buffer wrapped around, the selection needs to be offset accordingly. - if (m_selection.is_valid() && delta < 0) - m_selection.offset_row(delta); -} - -void TerminalWidget::terminal_did_perform_possibly_partial_clear() -{ - // Just pretend the whole terminal was cleared. - // Force an update by resizing slightly and then back to the original size. - winsize ws; - for (ssize_t offset = 1; offset >= 0; --offset) { - ws.ws_col = m_terminal.columns() - offset; - ws.ws_row = m_terminal.rows() - offset; - if (m_ptm_fd != -1) { - if (ioctl(m_ptm_fd, TIOCSWINSZ, &ws) < 0) { - perror("ioctl(TIOCSWINSZ)"); - } - } - } -} - -void TerminalWidget::terminal_did_resize(u16 columns, u16 rows) -{ - auto pixel_size = widget_size_for_font(font()); - m_pixel_width = pixel_size.width(); - m_pixel_height = pixel_size.height(); - - if (!m_in_relayout) { - if (on_terminal_size_change) - on_terminal_size_change(Gfx::IntSize { m_pixel_width, m_pixel_height }); - } - - if (m_automatic_size_policy) { - set_fixed_size(m_pixel_width, m_pixel_height); - } - - update(); - - winsize ws; - ws.ws_row = rows; - ws.ws_col = columns; - if (m_ptm_fd != -1) { - if (ioctl(m_ptm_fd, TIOCSWINSZ, &ws) < 0) { - // This can happen if we resize just as the shell exits. - dbgln("Notifying the pseudo-terminal about a size change failed."); - } - } -} - -void TerminalWidget::beep() -{ - if (m_bell_mode == BellMode::Disabled) { - return; - } - if (m_bell_mode == BellMode::AudibleBeep) { - [[maybe_unused]] auto ret_val = Core::System::beep(); - return; - } - m_visual_beep_timer->restart(200); - m_visual_beep_timer->set_single_shot(true); - m_visual_beep_timer->on_timeout = [this] { - update(); - }; - update(); -} - -void TerminalWidget::emit(u8 const* data, size_t size) -{ - if (write(m_ptm_fd, data, size) < 0) { - perror("TerminalWidget::emit: write"); - } -} - -void TerminalWidget::set_cursor_blinking(bool blinking) -{ - if (blinking) { - m_cursor_blink_timer->stop(); - m_cursor_blink_state = true; - m_cursor_blink_timer->start(); - m_cursor_is_blinking_set = true; - } else { - m_cursor_blink_timer->stop(); - m_cursor_blink_state = true; - m_cursor_is_blinking_set = false; - } - invalidate_cursor(); -} - -void TerminalWidget::set_cursor_shape(CursorShape shape) -{ - m_cursor_shape = shape; - invalidate_cursor(); - update(); -} - -void TerminalWidget::context_menu_event(GUI::ContextMenuEvent& event) -{ - if (!m_hovered_href_id.has_value()) { - m_context_menu->popup(event.screen_position()); - } else { - m_context_menu_href = m_hovered_href; - - // Ask LaunchServer for a list of programs that can handle the right-clicked URL. - auto handlers = Desktop::Launcher::get_handlers_for_url(m_hovered_href); - if (handlers.is_empty()) { - m_context_menu->popup(event.screen_position()); - return; - } - - m_context_menu_for_hyperlink = GUI::Menu::construct(); - RefPtr context_menu_default_action; - - // Go through the list of handlers and see if we can find a nice display name + icon for them. - // Then add them to the context menu. - // FIXME: Adapt this code when we actually support calling LaunchServer with a specific handler in mind. - for (auto& handler : handlers) { - auto af = Desktop::AppFile::get_for_app(LexicalPath::basename(handler)); - if (!af->is_valid()) - continue; - auto action = GUI::Action::create(ByteString::formatted("&Open in {}", af->name()), af->icon().bitmap_for_size(16), [this, handler](auto&) { - Desktop::Launcher::open(m_context_menu_href, handler); - }); - - if (context_menu_default_action.is_null()) { - context_menu_default_action = action; - } - - m_context_menu_for_hyperlink->add_action(action); - } - m_context_menu_for_hyperlink->add_action(GUI::Action::create("Copy &URL", [this](auto&) { - GUI::Clipboard::the().set_plain_text(m_context_menu_href); - })); - m_context_menu_for_hyperlink->add_action(GUI::Action::create("Copy &Name", [&](auto&) { - // file://courage/home/anon/something -> /home/anon/something - auto path = URL::URL(m_context_menu_href).serialize_path(); - // /home/anon/something -> something - auto name = LexicalPath::basename(path); - GUI::Clipboard::the().set_plain_text(name); - })); - m_context_menu_for_hyperlink->add_separator(); - m_context_menu_for_hyperlink->add_action(copy_action()); - m_context_menu_for_hyperlink->add_action(paste_action()); - - m_context_menu_for_hyperlink->popup(event.screen_position(), context_menu_default_action); - } -} - -void TerminalWidget::drag_enter_event(GUI::DragEvent& event) -{ - auto const& mime_types = event.mime_types(); - if (mime_types.contains_slow("text/plain"sv) || mime_types.contains_slow("text/uri-list"sv)) - event.accept(); -} - -void TerminalWidget::drop_event(GUI::DropEvent& event) -{ - if (event.mime_data().has_urls()) { - event.accept(); - auto urls = event.mime_data().urls(); - bool first = true; - for (auto& url : event.mime_data().urls()) { - if (!first) - send_non_user_input(" "sv.bytes()); - - if (url.scheme() == "file") { - auto path = url.serialize_path(); - send_non_user_input(path.bytes()); - } else { - auto url_string = url.to_byte_string(); - send_non_user_input(url_string.bytes()); - } - - first = false; - } - } else if (event.mime_data().has_text()) { - event.accept(); - auto text = event.mime_data().text(); - send_non_user_input(text.bytes()); - } -} - -void TerminalWidget::did_change_font() -{ - GUI::Frame::did_change_font(); - update_cached_font_metrics(); - if (!size().is_empty()) - relayout(size()); -} - -static void collect_font_metrics(Gfx::Font const& font, int& column_width, int& cell_height, int& line_height, int& line_spacing) -{ - line_spacing = 4; - column_width = static_cast(ceilf(font.glyph_width('x'))); - cell_height = font.pixel_size_rounded_up(); - line_height = cell_height + line_spacing; -} - -void TerminalWidget::update_cached_font_metrics() -{ - collect_font_metrics(font(), m_column_width, m_cell_height, m_line_height, m_line_spacing); -} - -void TerminalWidget::clear_including_history() -{ - m_terminal.clear_including_history(); -} - -void TerminalWidget::clear_to_previous_mark() -{ - auto marks = m_terminal.marks().values(); - size_t offset = m_startup_process_owns_pty ? 2 : 1; // If the shell is the active process, we have an extra mark. - if (marks.size() < offset) - return; - m_terminal.clear_to_mark(marks[marks.size() - offset]); -} - -void TerminalWidget::scroll_to_bottom() -{ - m_scrollbar->set_value(m_scrollbar->max()); -} - -void TerminalWidget::scroll_to_row(int row) -{ - m_scrollbar->set_value(row); -} - -void TerminalWidget::update_copy_action() -{ - m_copy_action->set_enabled(has_selection()); -} - -void TerminalWidget::update_paste_action() -{ - auto [data, mime_type, _] = GUI::Clipboard::the().fetch_data_and_type(); - m_paste_action->set_enabled(mime_type.starts_with("text/"sv) && !data.is_empty()); -} - -void TerminalWidget::update_color_scheme() -{ - auto const& palette = GUI::Widget::palette(); - - m_show_bold_text_as_bright = palette.bold_text_as_bright(); - - m_default_background_color = palette.background(); - m_default_foreground_color = palette.foreground(); - - m_colors[0] = palette.black(); - m_colors[1] = palette.red(); - m_colors[2] = palette.green(); - m_colors[3] = palette.yellow(); - m_colors[4] = palette.blue(); - m_colors[5] = palette.magenta(); - m_colors[6] = palette.cyan(); - m_colors[7] = palette.white(); - m_colors[8] = palette.bright_black(); - m_colors[9] = palette.bright_red(); - m_colors[10] = palette.bright_green(); - m_colors[11] = palette.bright_yellow(); - m_colors[12] = palette.bright_blue(); - m_colors[13] = palette.bright_magenta(); - m_colors[14] = palette.bright_cyan(); - m_colors[15] = palette.bright_white(); - - update(); -} - -Gfx::IntSize TerminalWidget::widget_size_for_font(Gfx::Font const& font) const -{ - int column_width = 0; - int line_height = 0; - int cell_height = 0; - int line_spacing = 0; - collect_font_metrics(font, column_width, cell_height, line_height, line_spacing); - auto base_size = compute_base_size(); - return { - base_size.width() + (m_terminal.columns() * column_width), - base_size.height() + (m_terminal.rows() * line_height), - }; -} - -constexpr Gfx::Color TerminalWidget::terminal_color_to_rgb(VT::Color color) const -{ - switch (color.kind()) { - case VT::Color::Kind::RGB: - return Gfx::Color::from_rgb(color.as_rgb()); - case VT::Color::Kind::Indexed: - return m_colors[color.as_indexed()]; - case VT::Color::Kind::Named: { - auto ansi = color.as_named(); - if ((u16)ansi < 256) - return m_colors[(u16)ansi]; - else if (ansi == VT::Color::ANSIColor::DefaultForeground) - return m_default_foreground_color; - else if (ansi == VT::Color::ANSIColor::DefaultBackground) - return m_default_background_color; - else - VERIFY_NOT_REACHED(); - } - default: - VERIFY_NOT_REACHED(); - } -} - -void TerminalWidget::set_font_and_resize_to_fit(Gfx::Font const& font) -{ - resize(widget_size_for_font(font)); - set_font(font); -} - -// Used for sending data that was not directly typed by the user. -// This basically wraps the code that handles sending the escape sequence in bracketed paste mode. -void TerminalWidget::send_non_user_input(ReadonlyBytes bytes) -{ - constexpr StringView leading_control_sequence = "\e[200~"sv; - constexpr StringView trailing_control_sequence = "\e[201~"sv; - - int nwritten; - if (m_terminal.needs_bracketed_paste()) { - // We do not call write() separately for the control sequences and the data, - // because that would present a race condition where another process could inject data - // to prematurely terminate the escape. Could probably be solved by file locking. - Vector output; - output.ensure_capacity(leading_control_sequence.bytes().size() + bytes.size() + trailing_control_sequence.bytes().size()); - - // HACK: We don't have a `Vector::unchecked_append(Span const&)` yet :^( - output.append(leading_control_sequence.bytes().data(), leading_control_sequence.bytes().size()); - output.append(bytes.data(), bytes.size()); - output.append(trailing_control_sequence.bytes().data(), trailing_control_sequence.bytes().size()); - nwritten = write(m_ptm_fd, output.data(), output.size()); - } else { - nwritten = write(m_ptm_fd, bytes.data(), bytes.size()); - } - if (nwritten < 0) { - perror("write"); - VERIFY_NOT_REACHED(); - } -} - -void TerminalWidget::set_auto_scroll_direction(AutoScrollDirection direction) -{ - m_auto_scroll_direction = direction; - m_auto_scroll_timer->set_active(direction != AutoScrollDirection::None); -} - -Optional TerminalWidget::parse_cursor_shape(StringView cursor_shape_string) -{ - if (cursor_shape_string == "Block"sv) - return VT::CursorShape::Block; - - if (cursor_shape_string == "Underline"sv) - return VT::CursorShape::Underline; - - if (cursor_shape_string == "Bar"sv) - return VT::CursorShape::Bar; - - return {}; -} - -ByteString TerminalWidget::stringify_cursor_shape(VT::CursorShape cursor_shape) -{ - switch (cursor_shape) { - case VT::CursorShape::Block: - return "Block"; - case VT::CursorShape::Underline: - return "Underline"; - case VT::CursorShape::Bar: - return "Bar"; - case VT::CursorShape::None: - return "None"; - } - VERIFY_NOT_REACHED(); -} - -Optional TerminalWidget::parse_bell(StringView bell_string) -{ - if (bell_string == "AudibleBeep") - return BellMode::AudibleBeep; - if (bell_string == "Visible") - return BellMode::Visible; - if (bell_string == "Disabled") - return BellMode::Disabled; - return {}; -} - -ByteString TerminalWidget::stringify_bell(TerminalWidget::BellMode bell_mode) -{ - if (bell_mode == BellMode::AudibleBeep) - return "AudibleBeep"; - if (bell_mode == BellMode::Disabled) - return "Disabled"; - if (bell_mode == BellMode::Visible) - return "Visible"; - VERIFY_NOT_REACHED(); -} - -Optional TerminalWidget::parse_automark_mode(StringView automark_mode) -{ - if (automark_mode == "MarkNothing") - return AutoMarkMode::MarkNothing; - if (automark_mode == "MarkInteractiveShellPrompt") - return AutoMarkMode::MarkInteractiveShellPrompt; - return {}; -} - -ByteString TerminalWidget::stringify_automark_mode(AutoMarkMode automark_mode) -{ - if (automark_mode == AutoMarkMode::MarkNothing) - return "MarkNothing"; - if (automark_mode == AutoMarkMode::MarkInteractiveShellPrompt) - return "MarkInteractiveShellPrompt"; - VERIFY_NOT_REACHED(); -} - -void TerminalWidget::handle_pty_owner_change(pid_t new_owner) -{ - if (m_auto_mark_mode == AutoMarkMode::MarkInteractiveShellPrompt && new_owner == m_startup_process_id) { - m_terminal.mark_cursor(); - } -} - -} diff --git a/Userland/Libraries/LibVT/TerminalWidget.h b/Userland/Libraries/LibVT/TerminalWidget.h deleted file mode 100644 index 851983bddbd..00000000000 --- a/Userland/Libraries/LibVT/TerminalWidget.h +++ /dev/null @@ -1,267 +0,0 @@ -/* - * Copyright (c) 2018-2021, Andreas Kling - * Copyright (c) 2022, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace VT { - -class TerminalWidget final - : public GUI::Frame - , public VT::TerminalClient - , public GUI::Clipboard::ClipboardClient { - C_OBJECT(TerminalWidget); - -public: - virtual ~TerminalWidget() override = default; - - void set_pty_master_fd(int fd); - void inject_string(StringView string) - { - m_terminal.inject_string(string); - flush_dirty_lines(); - } - - void flush_dirty_lines(); - - void apply_size_increments_to_window(GUI::Window&); - - void set_opacity(u8); - float opacity() { return m_opacity; } - - void set_show_scrollbar(bool); - - enum class BellMode { - Visible, - AudibleBeep, - Disabled - }; - - BellMode bell_mode() { return m_bell_mode; } - void set_bell_mode(BellMode bm) { m_bell_mode = bm; } - - enum class AutoMarkMode { - MarkNothing, - MarkInteractiveShellPrompt, - }; - - AutoMarkMode auto_mark_mode() { return m_auto_mark_mode; } - void set_auto_mark_mode(AutoMarkMode am) { m_auto_mark_mode = am; } - - bool has_selection() const; - bool selection_contains(const VT::Position&) const; - ByteString selected_text() const; - VT::Range normalized_selection() const { return m_selection.normalized(); } - void set_selection(const VT::Range& selection); - VT::Position buffer_position_at(Gfx::IntPoint) const; - - VT::Range find_next(StringView, const VT::Position& start = {}, bool case_sensitivity = false, bool should_wrap = false); - VT::Range find_previous(StringView, const VT::Position& start = {}, bool case_sensitivity = false, bool should_wrap = false); - - void scroll_to_bottom(); - void scroll_to_row(int); - - bool is_scrollable() const; - int scroll_length() const; - - size_t max_history_size() const { return m_terminal.max_history_size(); } - void set_max_history_size(size_t value) { m_terminal.set_max_history_size(value); } - - GUI::Action& copy_action() { return *m_copy_action; } - GUI::Action& paste_action() { return *m_paste_action; } - GUI::Action& clear_including_history_action() { return *m_clear_including_history_action; } - GUI::Action& clear_to_previous_mark_action() { return *m_clear_to_previous_mark_action; } - - void copy(); - void paste(); - void clear_including_history(); - void clear_to_previous_mark(); - - void set_startup_process_id(pid_t pid) { m_startup_process_id = pid; } - - StringView const color_scheme_name() const { return m_color_scheme_name; } - - Function on_title_change; - Function on_terminal_size_change; - Function on_command_exit; - - GUI::Menu& context_menu() { return *m_context_menu; } - - constexpr Gfx::Color terminal_color_to_rgb(VT::Color) const; - - void set_font_and_resize_to_fit(Gfx::Font const&); - - void update_color_scheme(); - - void set_logical_focus(bool); - - VT::CursorShape cursor_shape() { return m_cursor_shape; } - virtual void set_cursor_blinking(bool) override; - virtual void set_cursor_shape(CursorShape) override; - - static Optional parse_cursor_shape(StringView); - static Optional parse_bell(StringView); - static Optional parse_automark_mode(StringView); - static ByteString stringify_cursor_shape(VT::CursorShape); - static ByteString stringify_bell(BellMode); - static ByteString stringify_automark_mode(AutoMarkMode); - -private: - TerminalWidget(int ptm_fd, bool automatic_size_policy); - - // ^GUI::Widget - virtual void event(Core::Event&) override; - virtual void paint_event(GUI::PaintEvent&) override; - virtual void resize_event(GUI::ResizeEvent&) override; - virtual void keydown_event(GUI::KeyEvent&) override; - virtual void keyup_event(GUI::KeyEvent&) override; - virtual void mousedown_event(GUI::MouseEvent&) override; - virtual void mouseup_event(GUI::MouseEvent&) override; - virtual void mousemove_event(GUI::MouseEvent&) override; - virtual void mousewheel_event(GUI::MouseEvent&) override; - virtual void doubleclick_event(GUI::MouseEvent&) override; - virtual void focusin_event(GUI::FocusEvent&) override; - virtual void focusout_event(GUI::FocusEvent&) override; - virtual void context_menu_event(GUI::ContextMenuEvent&) override; - virtual void drag_enter_event(GUI::DragEvent&) override; - virtual void drop_event(GUI::DropEvent&) override; - virtual void leave_event(Core::Event&) override; - virtual void did_change_font() override; - - // ^TerminalClient - virtual void beep() override; - virtual void set_window_title(StringView) override; - virtual void set_window_progress(int value, int max) override; - virtual void terminal_did_resize(u16 columns, u16 rows) override; - virtual void terminal_history_changed(int delta) override; - virtual void terminal_did_perform_possibly_partial_clear() override; - virtual void emit(u8 const*, size_t) override; - - // ^GUI::Clipboard::ClipboardClient - virtual void clipboard_content_did_change(ByteString const&) override { update_paste_action(); } - - void send_non_user_input(ReadonlyBytes); - - Gfx::IntRect glyph_rect(u16 row, u16 column); - Gfx::IntRect row_rect(u16 row); - - Gfx::IntSize widget_size_for_font(Gfx::Font const&) const; - - void update_cursor(); - void invalidate_cursor(); - - void relayout(Gfx::IntSize); - - void update_copy_action(); - void update_paste_action(); - - Gfx::IntSize compute_base_size() const; - int first_selection_column_on_row(int row) const; - int last_selection_column_on_row(int row) const; - - u32 code_point_at(const VT::Position&) const; - VT::Position next_position_after(const VT::Position&, bool should_wrap) const; - VT::Position previous_position_before(const VT::Position&, bool should_wrap) const; - - void update_cached_font_metrics(); - - void handle_pty_owner_change(pid_t new_owner); - - VT::Terminal m_terminal; - - VT::Range m_selection; - - ByteString m_hovered_href; - Optional m_hovered_href_id; - - ByteString m_active_href; - Optional m_active_href_id; - - // Snapshot of m_hovered_href when opening a context menu for a hyperlink. - ByteString m_context_menu_href; - - Gfx::Color m_colors[256]; - Gfx::Color m_default_foreground_color; - Gfx::Color m_default_background_color; - bool m_show_bold_text_as_bright { true }; - - ByteString m_color_scheme_name; - - AutoMarkMode m_auto_mark_mode { AutoMarkMode::MarkInteractiveShellPrompt }; - - BellMode m_bell_mode { BellMode::Visible }; - bool m_alt_key_held { false }; - bool m_rectangle_selection { false }; - - int m_pixel_width { 0 }; - int m_pixel_height { 0 }; - - int m_inset { 2 }; - int m_line_spacing { 4 }; - int m_line_height { 0 }; - int m_cell_height { 0 }; - int m_column_width { 0 }; - - int m_ptm_fd { -1 }; - - bool m_has_logical_focus { false }; - bool m_in_relayout { false }; - - RefPtr m_notifier; - - u8 m_opacity { 255 }; - bool m_cursor_blink_state { true }; - bool m_automatic_size_policy { false }; - - VT::CursorShape m_cursor_shape { VT::CursorShape::Block }; - bool m_cursor_is_blinking_set { true }; - - enum class AutoScrollDirection { - None, - Up, - Down - }; - - void set_auto_scroll_direction(AutoScrollDirection); - - AutoScrollDirection m_auto_scroll_direction { AutoScrollDirection::None }; - - RefPtr m_cursor_blink_timer; - RefPtr m_visual_beep_timer; - RefPtr m_auto_scroll_timer; - - RefPtr m_scrollbar; - - RefPtr m_copy_action; - RefPtr m_paste_action; - RefPtr m_clear_including_history_action; - RefPtr m_clear_to_previous_mark_action; - - RefPtr m_context_menu; - RefPtr m_context_menu_for_hyperlink; - - Core::ElapsedTimer m_triple_click_timer; - - Gfx::IntPoint m_left_mousedown_position; - VT::Position m_left_mousedown_position_buffer; - - bool m_startup_process_owns_pty { false }; - pid_t m_startup_process_id { -1 }; -}; - -} diff --git a/Userland/Libraries/LibVT/XtermColors.h b/Userland/Libraries/LibVT/XtermColors.h deleted file mode 100644 index d6982c4390a..00000000000 --- a/Userland/Libraries/LibVT/XtermColors.h +++ /dev/null @@ -1,266 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -static constexpr unsigned xterm_colors[256] = { - 0x000000, - 0xcc0000, - 0x3e9a06, - 0xc4a000, - 0x3465a4, - 0x75507b, - 0x06989a, - 0xeeeeec, - 0x555753, - 0xef2929, - 0x8ae234, - 0xfce94f, - 0x729fcf, - 0xad7fa8, - 0x34e2e2, - 0xFFFFFF, - 0x000000, - 0x00005f, - 0x000087, - 0x0000af, - 0x0000d7, - 0x0000ff, - 0x005f00, - 0x005f5f, - 0x005f87, - 0x005faf, - 0x005fd7, - 0x005fff, - 0x008700, - 0x00875f, - 0x008787, - 0x0087af, - 0x0087d7, - 0x0087ff, - 0x00af00, - 0x00af5f, - 0x00af87, - 0x00afaf, - 0x00afd7, - 0x00afff, - 0x00d700, - 0x00d75f, - 0x00d787, - 0x00d7af, - 0x00d7d7, - 0x00d7ff, - 0x00ff00, - 0x00ff5f, - 0x00ff87, - 0x00ffaf, - 0x00ffd7, - 0x00ffff, - 0x5f0000, - 0x5f005f, - 0x5f0087, - 0x5f00af, - 0x5f00d7, - 0x5f00ff, - 0x5f5f00, - 0x5f5f5f, - 0x5f5f87, - 0x5f5faf, - 0x5f5fd7, - 0x5f5fff, - 0x5f8700, - 0x5f875f, - 0x5f8787, - 0x5f87af, - 0x5f87d7, - 0x5f87ff, - 0x5faf00, - 0x5faf5f, - 0x5faf87, - 0x5fafaf, - 0x5fafd7, - 0x5fafff, - 0x5fd700, - 0x5fd75f, - 0x5fd787, - 0x5fd7af, - 0x5fd7d7, - 0x5fd7ff, - 0x5fff00, - 0x5fff5f, - 0x5fff87, - 0x5fffaf, - 0x5fffd7, - 0x5fffff, - 0x870000, - 0x87005f, - 0x870087, - 0x8700af, - 0x8700d7, - 0x8700ff, - 0x875f00, - 0x875f5f, - 0x875f87, - 0x875faf, - 0x875fd7, - 0x875fff, - 0x878700, - 0x87875f, - 0x878787, - 0x8787af, - 0x8787d7, - 0x8787ff, - 0x87af00, - 0x87af5f, - 0x87af87, - 0x87afaf, - 0x87afd7, - 0x87afff, - 0x87d700, - 0x87d75f, - 0x87d787, - 0x87d7af, - 0x87d7d7, - 0x87d7ff, - 0x87ff00, - 0x87ff5f, - 0x87ff87, - 0x87ffaf, - 0x87ffd7, - 0x87ffff, - 0xaf0000, - 0xaf005f, - 0xaf0087, - 0xaf00af, - 0xaf00d7, - 0xaf00ff, - 0xaf5f00, - 0xaf5f5f, - 0xaf5f87, - 0xaf5faf, - 0xaf5fd7, - 0xaf5fff, - 0xaf8700, - 0xaf875f, - 0xaf8787, - 0xaf87af, - 0xaf87d7, - 0xaf87ff, - 0xafaf00, - 0xafaf5f, - 0xafaf87, - 0xafafaf, - 0xafafd7, - 0xafafff, - 0xafd700, - 0xafd75f, - 0xafd787, - 0xafd7af, - 0xafd7d7, - 0xafd7ff, - 0xafff00, - 0xafff5f, - 0xafff87, - 0xafffaf, - 0xafffd7, - 0xafffff, - 0xd70000, - 0xd7005f, - 0xd70087, - 0xd700af, - 0xd700d7, - 0xd700ff, - 0xd75f00, - 0xd75f5f, - 0xd75f87, - 0xd75faf, - 0xd75fd7, - 0xd75fff, - 0xd78700, - 0xd7875f, - 0xd78787, - 0xd787af, - 0xd787d7, - 0xd787ff, - 0xd7af00, - 0xd7af5f, - 0xd7af87, - 0xd7afaf, - 0xd7afd7, - 0xd7afff, - 0xd7d700, - 0xd7d75f, - 0xd7d787, - 0xd7d7af, - 0xd7d7d7, - 0xd7d7ff, - 0xd7ff00, - 0xd7ff5f, - 0xd7ff87, - 0xd7ffaf, - 0xd7ffd7, - 0xd7ffff, - 0xff0000, - 0xff005f, - 0xff0087, - 0xff00af, - 0xff00d7, - 0xff00ff, - 0xff5f00, - 0xff5f5f, - 0xff5f87, - 0xff5faf, - 0xff5fd7, - 0xff5fff, - 0xff8700, - 0xff875f, - 0xff8787, - 0xff87af, - 0xff87d7, - 0xff87ff, - 0xffaf00, - 0xffaf5f, - 0xffaf87, - 0xffafaf, - 0xffafd7, - 0xffafff, - 0xffd700, - 0xffd75f, - 0xffd787, - 0xffd7af, - 0xffd7d7, - 0xffd7ff, - 0xffff00, - 0xffff5f, - 0xffff87, - 0xffffaf, - 0xffffd7, - 0xffffff, - 0x080808, - 0x121212, - 0x1c1c1c, - 0x262626, - 0x303030, - 0x3a3a3a, - 0x444444, - 0x4e4e4e, - 0x585858, - 0x626262, - 0x6c6c6c, - 0x767676, - 0x808080, - 0x8a8a8a, - 0x949494, - 0x9e9e9e, - 0xa8a8a8, - 0xb2b2b2, - 0xbcbcbc, - 0xc6c6c6, - 0xd0d0d0, - 0xdadada, - 0xe4e4e4, - 0xeeeeee, -}; diff --git a/Userland/Libraries/LibVirtGPU/CMakeLists.txt b/Userland/Libraries/LibVirtGPU/CMakeLists.txt deleted file mode 100644 index 0831a5b2f9b..00000000000 --- a/Userland/Libraries/LibVirtGPU/CMakeLists.txt +++ /dev/null @@ -1,10 +0,0 @@ -set(SOURCES - CommandBufferBuilder.cpp - Device.cpp - Image.cpp - Shader.cpp -) - -serenity_lib(LibVirtGPU virtgpu) -target_link_libraries(LibVirtGPU PRIVATE LibCore) -target_sources(LibVirtGPU PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/../LibGPU/Image.cpp") diff --git a/Userland/Libraries/LibVirtGPU/CommandBufferBuilder.cpp b/Userland/Libraries/LibVirtGPU/CommandBufferBuilder.cpp deleted file mode 100644 index a49da6e9934..00000000000 --- a/Userland/Libraries/LibVirtGPU/CommandBufferBuilder.cpp +++ /dev/null @@ -1,350 +0,0 @@ -/* - * Copyright (c) 2022, Sahan Fernando - * Copyright (c) 2022, Stephan Unverwerth - * Copyright (c) 2022, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include - -#include -#include -#include - -namespace VirtGPU { - -static u32 encode_command(u16 length, Protocol::ObjectType object_type, Protocol::VirGLCommand command) -{ - u8 command_value = to_underlying(command); - u8 object_type_value = to_underlying(object_type); - return (length << 16) | (object_type_value << 8) | command_value; -} - -class CommandBuilder { -public: - CommandBuilder(Vector& buffer, Protocol::VirGLCommand command, Protocol::ObjectType object_type) - : m_buffer(buffer) - , m_start_offset(buffer.size()) - , m_command(command) - , m_object_type(object_type) - { - m_buffer.append(0); - } - - void appendu32(u32 value) - { - VERIFY(!m_finalized); - m_buffer.append(value); - } - - void appendf32(float value) - { - VERIFY(!m_finalized); - m_buffer.append(bit_cast(value)); - } - - void appendf64(double value) - { - VERIFY(!m_finalized); - m_buffer.append(0); - m_buffer.append(0); - auto* depth = reinterpret_cast(&m_buffer[m_buffer.size() - 2]); - *depth = bit_cast(value); - } - - void append_string_null_padded(StringView string) - { - VERIFY(!m_finalized); - // Remember to have at least one null terminator byte - auto length = string.length() + 1; - auto num_required_words = (length + sizeof(u32) - 1) / sizeof(u32); - m_buffer.resize(m_buffer.size() + num_required_words); - char* dest = reinterpret_cast(&m_buffer[m_buffer.size() - num_required_words]); - memcpy(dest, string.characters_without_null_termination(), string.length()); - // Pad end with null bytes - memset(&dest[string.length()], 0, 4 * num_required_words - string.length()); - } - - void finalize() - { - if (!m_finalized) { - m_finalized = true; - size_t num_elems = m_buffer.size() - m_start_offset - 1; - VERIFY(num_elems <= NumericLimits::max()); - m_buffer[m_start_offset] = encode_command(static_cast(num_elems), m_object_type, m_command); - } - } - - ~CommandBuilder() - { - if (!m_finalized) - finalize(); - } - -private: - Vector& m_buffer; - size_t m_start_offset; - Protocol::VirGLCommand m_command; - Protocol::ObjectType m_object_type; - bool m_finalized { false }; -}; - -void CommandBufferBuilder::append_set_tweaks(u32 id, u32 value) -{ - CommandBuilder builder(m_buffer, Protocol::VirGLCommand::SET_TWEAKS, Protocol::ObjectType::NONE); - builder.appendu32(id); - builder.appendu32(value); -} - -void CommandBufferBuilder::append_transfer3d(Protocol::ResourceID resource, size_t width, size_t height, size_t depth, size_t direction) -{ - CommandBuilder builder(m_buffer, Protocol::VirGLCommand::TRANSFER3D, Protocol::ObjectType::NONE); - builder.appendu32(resource.value()); // res_handle - builder.appendu32(0); // level - // FIXME: It is not clear what this magic 242 value does. - // According to https://gitlab.freedesktop.org/virgl/virglrenderer/-/blob/master/src/vrend_decode.c#L1398 it is unused - // But ccapitalK had to specifically set it to prevent rendering failures. - builder.appendu32(242); // usage - builder.appendu32(0); // stride - builder.appendu32(0); // layer_stride - builder.appendu32(0); // x - builder.appendu32(0); // y - builder.appendu32(0); // z - builder.appendu32(width); // width - builder.appendu32(height); // height - builder.appendu32(depth); // depth - builder.appendu32(0); // data_offset - builder.appendu32(direction); // direction -} - -void CommandBufferBuilder::append_end_transfers_3d() -{ - CommandBuilder builder(m_buffer, Protocol::VirGLCommand::END_TRANSFERS, Protocol::ObjectType::NONE); -} - -void CommandBufferBuilder::append_draw_vbo(Protocol::PipePrimitiveTypes primitive_type, u32 count) -{ - CommandBuilder builder(m_buffer, Protocol::VirGLCommand::DRAW_VBO, Protocol::ObjectType::NONE); - builder.appendu32(0); // start - builder.appendu32(count); // count - builder.appendu32(to_underlying(primitive_type)); // mode - builder.appendu32(0); // indexed - builder.appendu32(1); // instance_count - builder.appendu32(0); // index_bias - builder.appendu32(0); // start_instance - builder.appendu32(0); // primitive_restart - builder.appendu32(0); // restart_index - builder.appendu32(0); // min_index - builder.appendu32(0xffffffff); // max_index - builder.appendu32(0); // cso -} - -void CommandBufferBuilder::append_clear(float r, float g, float b, float a) -{ - CommandBuilder builder(m_buffer, Protocol::VirGLCommand::CLEAR, Protocol::ObjectType::NONE); - Protocol::ClearType clear_flags {}; - clear_flags.flags.color0 = 1; - builder.appendu32(clear_flags.value); - builder.appendf32(r); - builder.appendf32(g); - builder.appendf32(b); - builder.appendf32(a); - builder.appendf64(1.0); - builder.appendu32(0); -} - -void CommandBufferBuilder::append_clear(double depth) -{ - CommandBuilder builder(m_buffer, Protocol::VirGLCommand::CLEAR, Protocol::ObjectType::NONE); - Protocol::ClearType clear_flags {}; - clear_flags.flags.depth = 1; - builder.appendu32(clear_flags.value); - builder.appendf32(0); - builder.appendf32(0); - builder.appendf32(0); - builder.appendf32(0); - builder.appendf64(depth); - builder.appendu32(0); -} - -void CommandBufferBuilder::append_set_vertex_buffers(u32 stride, u32 offset, Protocol::ResourceID resource) -{ - CommandBuilder builder(m_buffer, Protocol::VirGLCommand::SET_VERTEX_BUFFERS, Protocol::ObjectType::NONE); - builder.appendu32(stride); - builder.appendu32(offset); - builder.appendu32(resource.value()); -} - -void CommandBufferBuilder::append_create_blend(Protocol::ObjectHandle handle) -{ - CommandBuilder builder(m_buffer, Protocol::VirGLCommand::CREATE_OBJECT, Protocol::ObjectType::BLEND); - - CreateBlendCommand::S0Flags s0 {}; - CreateBlendCommand::S1Flags s1 {}; - CreateBlendCommand::S2Flags s2 {}; - - s0.dither = 1; - s2.colormask = 0xf; - - builder.appendu32(handle.value()); - builder.appendu32(s0.u32_value); - builder.appendu32(s1.u32_value); - builder.appendu32(s2.u32_value); - for (size_t i = 1; i < 8; ++i) { - builder.appendu32(0); // Explicitly disable all flags for other color buffers - } -} - -void CommandBufferBuilder::append_bind_blend(Protocol::ObjectHandle handle) -{ - CommandBuilder builder(m_buffer, Protocol::VirGLCommand::BIND_OBJECT, Protocol::ObjectType::BLEND); - builder.appendu32(handle.value()); // VIRGL_OBJ_BIND_HANDLE -} - -void CommandBufferBuilder::append_create_vertex_elements(Protocol::ObjectHandle handle, Vector const& bindings) -{ - CommandBuilder builder(m_buffer, Protocol::VirGLCommand::CREATE_OBJECT, Protocol::ObjectType::VERTEX_ELEMENTS); - builder.appendu32(handle.value()); - for (auto& binding : bindings) { - builder.appendu32(binding.offset); - builder.appendu32(binding.divisor); - builder.appendu32(binding.vertex_buffer_index); - builder.appendu32(to_underlying(binding.format)); - } -} - -void CommandBufferBuilder::append_bind_vertex_elements(Protocol::ObjectHandle handle) -{ - CommandBuilder builder(m_buffer, Protocol::VirGLCommand::BIND_OBJECT, Protocol::ObjectType::VERTEX_ELEMENTS); - builder.appendu32(handle.value()); // VIRGL_OBJ_BIND_HANDLE -} - -void CommandBufferBuilder::append_create_surface(Protocol::ResourceID drawtarget_resource, Protocol::ObjectHandle drawtarget_handle, Protocol::TextureFormat format) -{ - CommandBuilder builder(m_buffer, Protocol::VirGLCommand::CREATE_OBJECT, Protocol::ObjectType::SURFACE); - builder.appendu32(drawtarget_handle.value()); - builder.appendu32(drawtarget_resource.value()); - builder.appendu32(to_underlying(format)); - builder.appendu32(0); // First element / Texture Level - builder.appendu32(0); // Last element / Texture Element -} - -void CommandBufferBuilder::append_set_framebuffer_state(Protocol::ObjectHandle drawtarget, Protocol::ObjectHandle depthbuffer) -{ - CommandBuilder builder(m_buffer, Protocol::VirGLCommand::SET_FRAMEBUFFER_STATE, Protocol::ObjectType::NONE); - builder.appendu32(1); // nr_cbufs - builder.appendu32(depthbuffer.value()); // zsurf_handle - builder.appendu32(drawtarget.value()); // surf_handle -} - -void CommandBufferBuilder::append_viewport(Gfx::IntSize size) -{ - CommandBuilder builder(m_buffer, Protocol::VirGLCommand::SET_VIEWPORT_STATE, Protocol::ObjectType::NONE); - builder.appendu32(0); - builder.appendf32(size.width() / 2); // scale_x - builder.appendf32(size.height() / 2); // scale_y (flipped, due to VirGL being different from our coordinate space) - builder.appendf32(0.5f); // scale_z - builder.appendf32(size.width() / 2); // translate_x - builder.appendf32(size.height() / 2); // translate_y - builder.appendf32(0.5f); // translate_z -} - -void CommandBufferBuilder::append_set_framebuffer_state_no_attach(Gfx::IntSize size) -{ - VERIFY(size.width() <= NumericLimits::max()); - VERIFY(size.height() <= NumericLimits::max()); - - CommandBuilder builder(m_buffer, Protocol::VirGLCommand::SET_FRAMEBUFFER_STATE_NO_ATTACH, Protocol::ObjectType::NONE); - - u16 samples = 0; - u16 layers = 0; - - builder.appendu32((size.height() << 16) | size.width()); - builder.appendu32((samples << 16) | layers); -} - -void CommandBufferBuilder::append_set_constant_buffer(Vector const& constant_buffer) -{ - CommandBuilder builder(m_buffer, Protocol::VirGLCommand::SET_CONSTANT_BUFFER, Protocol::ObjectType::NONE); - builder.appendu32(to_underlying(Gallium::ShaderType::SHADER_VERTEX)); - builder.appendu32(0); // index (currently unused according to virglrenderer source code) - for (auto v : constant_buffer) { - builder.appendf32(v); - } -} - -void CommandBufferBuilder::append_create_shader(Protocol::ObjectHandle handle, Gallium::ShaderType shader_type, StringView shader_data) -{ - size_t shader_len = shader_data.length() + 1; // Need to remember to copy null terminator as well if needed - VERIFY(shader_len <= NumericLimits::max()); - - CommandBuilder builder(m_buffer, Protocol::VirGLCommand::CREATE_OBJECT, Protocol::ObjectType::SHADER); - builder.appendu32(handle.value()); // VIRGL_OBJ_CREATE_HANDLE - builder.appendu32(to_underlying(shader_type)); - builder.appendu32(0); // VIRGL_OBJ_SHADER_OFFSET - builder.appendu32(shader_len); - builder.appendu32(0); // VIRGL_OBJ_SHADER_NUM_TOKENS - builder.append_string_null_padded(shader_data); -} - -void CommandBufferBuilder::append_bind_shader(Protocol::ObjectHandle handle, Gallium::ShaderType shader_type) -{ - CommandBuilder builder(m_buffer, Protocol::VirGLCommand::BIND_SHADER, Protocol::ObjectType::NONE); - builder.appendu32(handle.value()); // VIRGL_OBJ_BIND_HANDLE - builder.appendu32(to_underlying(shader_type)); -} - -void CommandBufferBuilder::append_create_rasterizer(Protocol::ObjectHandle handle) -{ - CommandBuilder builder(m_buffer, Protocol::VirGLCommand::CREATE_OBJECT, Protocol::ObjectType::RASTERIZER); - - CreateRasterizerCommand::S0Flags s0 {}; - CreateRasterizerCommand::S3Flags s3 {}; - - s0.depth_clip = 1; - - builder.appendu32(handle.value()); // Handle - builder.appendu32(s0.u32_value); // S0 (bitfield of state bits) - builder.appendf32(1.0); // Point size - builder.appendu32(0); // Sprite coord enable - builder.appendu32(s3.u32_value); // S3 (bitfield of state bits) - builder.appendf32(0.1); // Line width - builder.appendf32(0.0); // Offset units - builder.appendf32(0.0); // offset scale - builder.appendf32(0.0); // Offset clamp -} - -void CommandBufferBuilder::append_bind_rasterizer(Protocol::ObjectHandle handle) -{ - CommandBuilder builder(m_buffer, Protocol::VirGLCommand::BIND_OBJECT, Protocol::ObjectType::RASTERIZER); - builder.appendu32(handle.value()); // VIRGL_OBJ_BIND_HANDLE -} - -void CommandBufferBuilder::append_create_dsa(Protocol::ObjectHandle handle) -{ - CommandBuilder builder(m_buffer, Protocol::VirGLCommand::CREATE_OBJECT, Protocol::ObjectType::DSA); - - CreateDSACommand::S0Flags s0 {}; - CreateDSACommand::S1Flags s1[2] {}; - - s0.depth_enabled = 1; - s0.depth_writemask = 1; - s0.depth_func = 1; - - builder.appendu32(handle.value()); // Handle - builder.appendu32(s0.u32_value); // S0 (bitset for depth buffer) - builder.appendu32(s1[0].u32_value); // S1 (bitset for 1st stencil buffer) - builder.appendu32(s1[1].u32_value); // S2 (bitset for 2nd stencil buffer) - builder.appendf32(1.0); // Alpha Ref -} - -void CommandBufferBuilder::append_bind_dsa(Protocol::ObjectHandle handle) -{ - CommandBuilder builder(m_buffer, Protocol::VirGLCommand::BIND_OBJECT, Protocol::ObjectType::DSA); - builder.appendu32(handle.value()); // VIRGL_OBJ_BIND_HANDLE -} - -} diff --git a/Userland/Libraries/LibVirtGPU/CommandBufferBuilder.h b/Userland/Libraries/LibVirtGPU/CommandBufferBuilder.h deleted file mode 100644 index 27119107053..00000000000 --- a/Userland/Libraries/LibVirtGPU/CommandBufferBuilder.h +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright (c) 2022, Sahan Fernando - * Copyright (c) 2022, Stephan Unverwerth - * Copyright (c) 2022, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include -#include - -namespace VirtGPU { - -class CommandBufferBuilder { -public: - void append_set_tweaks(u32 id, u32 value); - void append_transfer3d(Protocol::ResourceID resource, size_t width, size_t height = 1, size_t depth = 1, size_t direction = VIRGL_DATA_DIR_GUEST_TO_HOST); - void append_end_transfers_3d(); - void append_draw_vbo(Protocol::PipePrimitiveTypes, u32 count); - void append_clear(float r, float g, float b, float a); - void append_clear(double depth); - void append_set_vertex_buffers(u32 stride, u32 offset, Protocol::ResourceID resource); - void append_create_blend(Protocol::ObjectHandle handle); - void append_bind_blend(Protocol::ObjectHandle handle); - void append_create_surface(Protocol::ResourceID drawtarget_resource, Protocol::ObjectHandle drawtarget_handle, Protocol::TextureFormat format); - void append_set_framebuffer_state(Protocol::ObjectHandle drawtarget, Protocol::ObjectHandle depthbuffer = 0); - void append_create_vertex_elements(Protocol::ObjectHandle handle, Vector const&); - void append_bind_vertex_elements(Protocol::ObjectHandle handle); - void append_viewport(Gfx::IntSize); - void append_set_framebuffer_state_no_attach(Gfx::IntSize); - void append_set_constant_buffer(Vector const& constant_buffer); - void append_create_shader(Protocol::ObjectHandle handle, Gallium::ShaderType shader_type, StringView shader_data); - void append_bind_shader(Protocol::ObjectHandle handle, Gallium::ShaderType shader_type); - void append_create_rasterizer(Protocol::ObjectHandle handle); - void append_bind_rasterizer(Protocol::ObjectHandle handle); - void append_create_dsa(Protocol::ObjectHandle handle); - void append_bind_dsa(Protocol::ObjectHandle handle); - Vector const& build() { return m_buffer; } - -private: - Vector m_buffer; -}; - -} diff --git a/Userland/Libraries/LibVirtGPU/Commands.h b/Userland/Libraries/LibVirtGPU/Commands.h deleted file mode 100644 index 37d61d7430b..00000000000 --- a/Userland/Libraries/LibVirtGPU/Commands.h +++ /dev/null @@ -1,132 +0,0 @@ -/* - * Copyright (c) 2022, Stephan Unverwerth - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include - -namespace VirtGPU { - -struct CreateBlendCommand { - union S0Flags { - struct { - u32 independent_blend_enable : 1; - u32 logicop_enable : 1; - u32 dither : 1; - u32 alpha_to_coverage : 1; - u32 alpha_to_one : 1; - u32 unused : 27; - }; - u32 u32_value; - }; - - union S1Flags { - struct { - u32 logicop_func : 4; - u32 unused : 28; - }; - u32 u32_value; - }; - - union S2Flags { - struct { - u32 blend_enable : 1; - u32 rgb_func : 3; - u32 rgb_src_factor : 5; - u32 rgb_dst_factor : 5; - u32 alpha_func : 3; - u32 alpha_src_factor : 5; - u32 alpha_dst_factor : 5; - u32 colormask : 4; - u32 unused : 1; - }; - u32 u32_value; - }; -}; - -struct CreateVertexElementsCommand { - struct ElementBinding { - u32 offset; - u32 divisor; - u32 vertex_buffer_index; - Gallium::PipeFormat format; - }; -}; - -struct CreateRasterizerCommand { - union S0Flags { - struct { - u32 flatshade : 1; - u32 depth_clip : 1; - u32 clip_halfz : 1; - u32 rasterizer_discard : 1; - u32 flatshade_first : 1; - u32 light_twoside : 1; - u32 sprite_coord_mode : 1; - u32 point_quad_rasterization : 1; - u32 cull_face : 2; - u32 fill_front : 2; - u32 fill_back : 2; - u32 scissor : 1; - u32 front_ccw : 1; - u32 clamp_vertex_color : 1; - u32 clamp_fragment_color : 1; - u32 offset_line : 1; - u32 offset_point : 1; - u32 offset_tri : 1; - u32 poly_smooth : 1; - u32 poly_stipple_enable : 1; - u32 point_smooth : 1; - u32 point_size_per_vertex : 1; - u32 multisample : 1; - u32 line_smooth : 1; - u32 line_stipple_enable : 1; - u32 line_last_pixel : 1; - u32 half_pixel_center : 1; - u32 bottom_edge_rule : 1; - u32 force_persample_interp : 1; - }; - u32 u32_value; - }; - - union S3Flags { - struct { - u32 line_stipple_pattern : 16; - u32 line_stipple_factor : 8; - u32 clip_plane_enable : 8; - }; - u32 u32_value; - }; -}; - -struct CreateDSACommand { - union S0Flags { - struct { - u32 depth_enabled : 1; - u32 depth_writemask : 1; - u32 depth_func : 3; - u32 unused : 27; - }; - u32 u32_value; - }; - - union S1Flags { - struct { - u32 stencil_enabled : 1; - u32 stencil_func : 3; - u32 stencil_fail_op : 3; - u32 stencil_zpass_op : 3; - u32 stencil_zfail_op : 3; - u32 stencil_valuemask : 8; - u32 stencil_writemask : 8; - u32 unused : 3; - }; - u32 u32_value; - }; -}; - -} diff --git a/Userland/Libraries/LibVirtGPU/Device.cpp b/Userland/Libraries/LibVirtGPU/Device.cpp deleted file mode 100644 index 5a229f22c27..00000000000 --- a/Userland/Libraries/LibVirtGPU/Device.cpp +++ /dev/null @@ -1,468 +0,0 @@ -/* - * Copyright (c) 2022, Stephan Unverwerth - * Copyright (c) 2022, Sahan Fernando - * Copyright (c) 2022, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace VirtGPU { - -static constexpr auto frag_shader = "FRAG\n" - "PROPERTY FS_COLOR0_WRITES_ALL_CBUFS 1\n" - "DCL IN[0], COLOR, COLOR\n" - "DCL OUT[0], COLOR\n" - " 0: MOV OUT[0], IN[0]\n" - " 1: END\n"sv; - -static constexpr auto vert_shader = "VERT\n" - "DCL IN[0]\n" - "DCL IN[1]\n" - "DCL OUT[0], POSITION\n" - "DCL OUT[1], COLOR\n" - "DCL CONST[0..3]\n" - "DCL TEMP[0..1]\n" - " 0: MUL TEMP[0], IN[0].xxxx, CONST[0]\n" - " 1: MAD TEMP[1], IN[0].yyyy, CONST[1], TEMP[0]\n" - " 2: MAD TEMP[0], IN[0].zzzz, CONST[2], TEMP[1]\n" - " 3: MAD OUT[0], IN[0].wwww, CONST[3], TEMP[0]\n" - " 4: MOV_SAT OUT[1], IN[1]\n" - " 5: END\n"sv; - -Device::Device(NonnullOwnPtr gpu_file) - : m_gpu_file { move(gpu_file) } -{ -} - -ErrorOr> Device::create(Gfx::IntSize min_size) -{ - auto file = TRY(Core::File::open("/dev/gpu/render0"sv, Core::File::OpenMode::ReadWrite)); - auto device = make(move(file)); - TRY(device->initialize_context(min_size)); - return device; -} - -ErrorOr Device::initialize_context(Gfx::IntSize min_size) -{ - // Create a virgl context for this file descriptor - TRY(Core::System::ioctl(m_gpu_file->fd(), VIRGL_IOCTL_CREATE_CONTEXT)); - - // Create a VertexElements resource - VirGL3DResourceSpec vbo_spec { - .target = to_underlying(Gallium::PipeTextureTarget::BUFFER), // pipe_texture_target - .format = 0, // untyped buffer - .bind = to_underlying(Protocol::BindTarget::VIRGL_BIND_VERTEX_BUFFER), - .width = PAGE_SIZE * 256, - .height = 1, - .depth = 1, - .array_size = 1, - .last_level = 0, - .nr_samples = 0, - .flags = 0, - .created_resource_id = 0, - }; - m_vbo_resource_id = TRY(create_virgl_resource(vbo_spec)); - - // Create a texture to draw to - VirGL3DResourceSpec drawtarget_spec { - .target = to_underlying(Gallium::PipeTextureTarget::TEXTURE_RECT), // pipe_texture_target - .format = to_underlying(Protocol::TextureFormat::VIRTIO_GPU_FORMAT_B8G8R8A8_UNORM), // pipe_to_virgl_format - .bind = to_underlying(Protocol::BindTarget::VIRGL_BIND_RENDER_TARGET), - .width = static_cast(min_size.width()), - .height = static_cast(min_size.height()), - .depth = 1, - .array_size = 1, - .last_level = 0, - .nr_samples = 0, - .flags = 0, - .created_resource_id = 0, - }; - m_drawtarget = TRY(create_virgl_resource(drawtarget_spec)); - - // Create a depthbuffer surface - VirGL3DResourceSpec depthbuffer_surface_spec { - .target = to_underlying(Gallium::PipeTextureTarget::TEXTURE_RECT), // pipe_texture_target - .format = to_underlying(Protocol::TextureFormat::VIRTIO_GPU_FORMAT_Z32_FLOAT), // pipe_to_virgl_format - .bind = to_underlying(Protocol::BindTarget::VIRGL_BIND_RENDER_TARGET) | to_underlying(Protocol::BindTarget::VIRGL_BIND_DEPTH_STENCIL), - .width = static_cast(min_size.width()), - .height = static_cast(min_size.height()), - .depth = 1, - .array_size = 1, - .last_level = 0, - .nr_samples = 0, - .flags = 0, - .created_resource_id = 0, - }; - m_depthbuffer_surface = TRY(create_virgl_resource(depthbuffer_surface_spec)); - - // Initialize all required state - CommandBufferBuilder builder; - - // Create and set the blend, to control the color mask - m_blend_handle = allocate_handle(); - builder.append_create_blend(m_blend_handle); - builder.append_bind_blend(m_blend_handle); - - // Create drawtarget surface - m_drawtarget_surface_handle = allocate_handle(); - builder.append_create_surface(m_drawtarget, m_drawtarget_surface_handle, Protocol::TextureFormat::VIRTIO_GPU_FORMAT_B8G8R8A8_UNORM); - - // Create depthbuffer surface - m_depthbuffer_surface_handle = allocate_handle(); - builder.append_create_surface(m_depthbuffer_surface, m_depthbuffer_surface_handle, Protocol::TextureFormat::VIRTIO_GPU_FORMAT_Z32_FLOAT); - - // Set some framebuffer state (attached handle, framebuffer size, etc) - builder.append_set_framebuffer_state(m_drawtarget_surface_handle, m_depthbuffer_surface_handle); - builder.append_set_framebuffer_state_no_attach(min_size); - - // Set the vertex buffer - builder.append_set_vertex_buffers(sizeof(VertexData), 0, m_vbo_resource_id); - - // Create and bind fragment shader - m_frag_shader_handle = allocate_handle(); - builder.append_create_shader(m_frag_shader_handle, Gallium::ShaderType::SHADER_FRAGMENT, frag_shader); - builder.append_bind_shader(m_frag_shader_handle, Gallium::ShaderType::SHADER_FRAGMENT); - - // Create and bind vertex shader - m_vert_shader_handle = allocate_handle(); - builder.append_create_shader(m_vert_shader_handle, Gallium::ShaderType::SHADER_VERTEX, vert_shader); - builder.append_bind_shader(m_vert_shader_handle, Gallium::ShaderType::SHADER_VERTEX); - - // Create a VertexElements object (used to specify layout of vertex data) - m_ve_handle = allocate_handle(); - Vector element_bindings { - { .offset = 12, .divisor = 0, .vertex_buffer_index = 0, .format = Gallium::PipeFormat::R32G32B32_FLOAT }, - { .offset = 0, .divisor = 0, .vertex_buffer_index = 0, .format = Gallium::PipeFormat::R32G32B32_FLOAT }, - }; - builder.append_create_vertex_elements(m_ve_handle, element_bindings); - builder.append_bind_vertex_elements(m_ve_handle); - - // Create a DepthStencilAlpha (DSA) object - m_dsa_handle = allocate_handle(); - builder.append_create_dsa(m_dsa_handle); - builder.append_bind_dsa(m_dsa_handle); - - // Create a Rasterizer object - m_rasterizer_handle = allocate_handle(); - builder.append_create_rasterizer(m_rasterizer_handle); - builder.append_bind_rasterizer(m_rasterizer_handle); - - // Set the Viewport - builder.append_viewport(min_size); - - // Upload buffer - TRY(upload_command_buffer(builder.build())); - - return {}; -} - -GPU::DeviceInfo Device::info() const -{ - return { - .vendor_name = "SerenityOS", - .device_name = "VirtGPU", - .num_texture_units = GPU::NUM_TEXTURE_UNITS, - .num_lights = 8, - .max_clip_planes = 6, - .max_texture_size = 4096, - .max_texture_lod_bias = 2.f, - .stencil_bits = sizeof(GPU::StencilType) * 8, - .supports_npot_textures = true, - .supports_texture_clamp_to_edge = true, - .supports_texture_env_add = true, - }; -} - -void Device::encode_constant_buffer(Gfx::FloatMatrix4x4 const& matrix, Vector& buffer) -{ - buffer.clear_with_capacity(); - for (int i = 0; i < 4; ++i) { - for (int j = 0; j < 4; ++j) { - buffer.append(matrix.elements()[i][j]); - } - } -} - -void Device::draw_primitives(GPU::PrimitiveType primitive_type, Vector& vertices) -{ - // Transform incoming vertices to our own format. - m_vertices.clear_with_capacity(); - for (auto& vertex : vertices) { - m_vertices.append({ - vertex.tex_coords[0].x(), - vertex.tex_coords[0].y(), - vertex.tex_coords[0].z(), - vertex.position.x(), - vertex.position.y(), - vertex.position.z(), - }); - } - - // Compute combined transform matrix - // Flip the y axis. This is done because OpenGLs coordinate space has a Y-axis of - // Opposite direction to that of LibGfx - auto combined_matrix = (Gfx::scale_matrix(FloatVector3 { 1, -1, 1 }) * m_projection_transform * m_model_view_transform).transpose(); - encode_constant_buffer(combined_matrix, m_constant_buffer_data); - - // Create command buffer - CommandBufferBuilder builder; - - // Set the constant buffer to the combined transformation matrix - builder.append_set_constant_buffer(m_constant_buffer_data); - - // Transfer data from vertices array to kernel virgl transfer region - VirGLTransferDescriptor descriptor { - .data = m_vertices.data(), - .offset_in_region = 0, - .num_bytes = sizeof(VertexData) * m_vertices.size(), - .direction = VIRGL_DATA_DIR_GUEST_TO_HOST, - }; - MUST(Core::System::ioctl(m_gpu_file->fd(), VIRGL_IOCTL_TRANSFER_DATA, &descriptor)); - - // Transfer data from kernel virgl transfer region to host resource - builder.append_transfer3d(m_vbo_resource_id, sizeof(VertexData) * m_vertices.size(), 1, 1, VIRGL_DATA_DIR_GUEST_TO_HOST); - builder.append_end_transfers_3d(); - - // Set the constant buffer to the identity matrix - builder.append_set_constant_buffer(m_constant_buffer_data); - - constexpr auto map_primitive_type = [](GPU::PrimitiveType type) constexpr { - switch (type) { - case GPU::PrimitiveType::Lines: - return Protocol::PipePrimitiveTypes::LINES; - case GPU::PrimitiveType::LineLoop: - return Protocol::PipePrimitiveTypes::LINE_LOOP; - case GPU::PrimitiveType::LineStrip: - return Protocol::PipePrimitiveTypes::LINE_STRIP; - case GPU::PrimitiveType::Points: - return Protocol::PipePrimitiveTypes::POINTS; - case GPU::PrimitiveType::TriangleFan: - return Protocol::PipePrimitiveTypes::TRIANGLE_FAN; - case GPU::PrimitiveType::Triangles: - return Protocol::PipePrimitiveTypes::TRIANGLES; - case GPU::PrimitiveType::TriangleStrip: - return Protocol::PipePrimitiveTypes::TRIANGLE_STRIP; - case GPU::PrimitiveType::Quads: - return Protocol::PipePrimitiveTypes::QUADS; - default: - VERIFY_NOT_REACHED(); - } - }; - - // Draw the vbo - builder.append_draw_vbo(map_primitive_type(primitive_type), m_vertices.size()); - - // Upload the buffer - MUST(upload_command_buffer(builder.build())); -} - -void Device::resize(Gfx::IntSize) -{ - dbgln("VirtGPU::Device::resize(): unimplemented"); -} - -void Device::clear_color(FloatVector4 const& color) -{ - CommandBufferBuilder builder; - builder.append_clear(color.x(), color.y(), color.z(), color.w()); - MUST(upload_command_buffer(builder.build())); -} - -void Device::clear_depth(GPU::DepthType depth) -{ - CommandBufferBuilder builder; - builder.append_clear(depth); - MUST(upload_command_buffer(builder.build())); -} - -void Device::clear_stencil(GPU::StencilType) -{ - dbgln("VirtGPU::Device::clear_stencil(): unimplemented"); -} - -void Device::blit_from_color_buffer(Gfx::Bitmap& front_buffer) -{ - // Transfer data back from hypervisor to kernel transfer region - CommandBufferBuilder builder; - builder.append_transfer3d(m_drawtarget, front_buffer.size().width(), front_buffer.size().height(), 1, VIRGL_DATA_DIR_HOST_TO_GUEST); - builder.append_end_transfers_3d(); - MUST(upload_command_buffer(builder.build())); - - // Copy from kernel transfer region to userspace - VirGLTransferDescriptor descriptor { - .data = front_buffer.scanline_u8(0), - .offset_in_region = 0, - .num_bytes = front_buffer.size().width() * front_buffer.size().height() * sizeof(u32), - .direction = VIRGL_DATA_DIR_HOST_TO_GUEST, - }; - MUST(Core::System::ioctl(m_gpu_file->fd(), VIRGL_IOCTL_TRANSFER_DATA, &descriptor)); -} - -void Device::blit_from_color_buffer(NonnullRefPtr, u32, Vector2, Vector2, Vector3) -{ - dbgln("VirtGPU::Device::blit_from_color_buffer(): unimplemented"); -} - -void Device::blit_from_color_buffer(void*, Vector2, GPU::ImageDataLayout const&) -{ - dbgln("VirtGPU::Device::blit_from_color_buffer(): unimplemented"); -} - -void Device::blit_from_depth_buffer(void*, Vector2, GPU::ImageDataLayout const&) -{ - dbgln("VirtGPU::Device::blit_from_depth_buffer(): unimplemented"); -} - -void Device::blit_from_depth_buffer(NonnullRefPtr, u32, Vector2, Vector2, Vector3) -{ - dbgln("VirtGPU::Device::blit_from_depth_buffer(): unimplemented"); -} - -void Device::blit_to_color_buffer_at_raster_position(void const*, GPU::ImageDataLayout const&) -{ - dbgln("VirtGPU::Device::blit_to_color_buffer_at_raster_position(): unimplemented"); -} - -void Device::blit_to_depth_buffer_at_raster_position(void const*, GPU::ImageDataLayout const&) -{ - dbgln("VirtGPU::Device::blit_to_depth_buffer_at_raster_position(): unimplemented"); -} - -void Device::set_options(GPU::RasterizerOptions const&) -{ - dbgln("VirtGPU::Device::set_options(): unimplemented"); -} - -void Device::set_light_model_params(GPU::LightModelParameters const&) -{ - dbgln("VirtGPU::Device::set_light_model_params(): unimplemented"); -} - -GPU::RasterizerOptions Device::options() const -{ - dbgln("VirtGPU::Device::options(): unimplemented"); - return {}; -} - -GPU::LightModelParameters Device::light_model() const -{ - dbgln("VirtGPU::Device::light_model(): unimplemented"); - return {}; -} - -NonnullRefPtr Device::create_image(GPU::PixelFormat const& pixel_format, u32 width, u32 height, u32 depth, u32 max_levels) -{ - dbgln("VirtGPU::Device::create_image(): unimplemented"); - return adopt_ref(*new Image(this, pixel_format, width, height, depth, max_levels)); -} - -ErrorOr> Device::create_shader(GPU::IR::Shader const&) -{ - dbgln("VirtGPU::Device::create_shader(): unimplemented"); - return adopt_ref(*new Shader(this)); -} - -void Device::set_model_view_transform(Gfx::FloatMatrix4x4 const& model_view_transform) -{ - m_model_view_transform = model_view_transform; -} - -void Device::set_projection_transform(Gfx::FloatMatrix4x4 const& projection_transform) -{ - m_projection_transform = projection_transform; -} - -void Device::set_sampler_config(unsigned, GPU::SamplerConfig const&) -{ - dbgln("VirtGPU::Device::set_sampler_config(): unimplemented"); -} - -void Device::set_light_state(unsigned, GPU::Light const&) -{ - dbgln("VirtGPU::Device::set_light_state(): unimplemented"); -} - -void Device::set_material_state(GPU::Face, GPU::Material const&) -{ - dbgln("VirtGPU::Device::set_material_state(): unimplemented"); -} - -void Device::set_stencil_configuration(GPU::Face, GPU::StencilConfiguration const&) -{ - dbgln("VirtGPU::Device::set_stencil_configuration(): unimplemented"); -} - -void Device::set_texture_unit_configuration(GPU::TextureUnitIndex, GPU::TextureUnitConfiguration const&) -{ - dbgln("VirtGPU::Device::set_texture_unit_configuration(): unimplemented"); -} - -void Device::set_clip_planes(Vector const&) -{ - dbgln("VirtGPU::Device::set_clip_planes(): unimplemented"); -} - -GPU::RasterPosition Device::raster_position() const -{ - dbgln("VirtGPU::Device::raster_position(): unimplemented"); - return {}; -} - -void Device::set_raster_position(GPU::RasterPosition const&) -{ - dbgln("VirtGPU::Device::set_raster_position(): unimplemented"); -} - -void Device::set_raster_position(FloatVector4 const&) -{ - dbgln("VirtGPU::Device::set_raster_position(): unimplemented"); -} - -void Device::bind_fragment_shader(RefPtr) -{ - dbgln("VirtGPU::Device::bind_fragment_shader(): unimplemented"); -} - -Protocol::ObjectHandle Device::allocate_handle() -{ - return { ++m_last_allocated_handle }; -} - -ErrorOr Device::upload_command_buffer(Vector const& command_buffer) -{ - VERIFY(command_buffer.size() <= NumericLimits::max()); - VirGLCommandBuffer command_buffer_descriptor { - .data = command_buffer.data(), - .num_elems = static_cast(command_buffer.size()), - }; - TRY(Core::System::ioctl(m_gpu_file->fd(), VIRGL_IOCTL_SUBMIT_CMD, &command_buffer_descriptor)); - - return {}; -} - -ErrorOr Device::create_virgl_resource(VirGL3DResourceSpec& spec) -{ - TRY(Core::System::ioctl(m_gpu_file->fd(), VIRGL_IOCTL_CREATE_RESOURCE, &spec)); - return Protocol::ResourceID { spec.created_resource_id }; -} - -} - -extern "C" GPU::Device* serenity_gpu_create_device(Gfx::IntSize size) -{ - auto device_or_error = VirtGPU::Device::create(size); - if (device_or_error.is_error()) - return nullptr; - - return device_or_error.release_value().leak_ptr(); -} diff --git a/Userland/Libraries/LibVirtGPU/Device.h b/Userland/Libraries/LibVirtGPU/Device.h deleted file mode 100644 index d6197ea477d..00000000000 --- a/Userland/Libraries/LibVirtGPU/Device.h +++ /dev/null @@ -1,102 +0,0 @@ -/* - * Copyright (c) 2022, Stephan Unverwerth - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include -#include - -namespace VirtGPU { - -class Device final : public GPU::Device { -public: - Device(NonnullOwnPtr); - - static ErrorOr> create(Gfx::IntSize min_size); - - // FIXME: Once the kernel driver supports destroying contexts we need to add this functionality here - ErrorOr initialize_context(Gfx::IntSize min_size); - - virtual GPU::DeviceInfo info() const override; - - virtual void draw_primitives(GPU::PrimitiveType, Vector& vertices) override; - virtual void resize(Gfx::IntSize min_size) override; - virtual void clear_color(FloatVector4 const&) override; - virtual void clear_depth(GPU::DepthType) override; - virtual void clear_stencil(GPU::StencilType) override; - virtual void blit_from_color_buffer(Gfx::Bitmap& target) override; - virtual void blit_from_color_buffer(NonnullRefPtr, u32 level, Vector2 input_size, Vector2 input_offset, Vector3 output_offset) override; - virtual void blit_from_color_buffer(void*, Vector2 offset, GPU::ImageDataLayout const&) override; - virtual void blit_from_depth_buffer(void*, Vector2 offset, GPU::ImageDataLayout const&) override; - virtual void blit_from_depth_buffer(NonnullRefPtr, u32 level, Vector2 input_size, Vector2 input_offset, Vector3 output_offset) override; - virtual void blit_to_color_buffer_at_raster_position(void const*, GPU::ImageDataLayout const&) override; - virtual void blit_to_depth_buffer_at_raster_position(void const*, GPU::ImageDataLayout const&) override; - virtual void set_options(GPU::RasterizerOptions const&) override; - virtual void set_light_model_params(GPU::LightModelParameters const&) override; - virtual GPU::RasterizerOptions options() const override; - virtual GPU::LightModelParameters light_model() const override; - - virtual NonnullRefPtr create_image(GPU::PixelFormat const&, u32 width, u32 height, u32 depth, u32 max_levels) override; - virtual ErrorOr> create_shader(GPU::IR::Shader const&) override; - - virtual void set_model_view_transform(FloatMatrix4x4 const&) override; - virtual void set_projection_transform(FloatMatrix4x4 const&) override; - virtual void set_sampler_config(unsigned, GPU::SamplerConfig const&) override; - virtual void set_light_state(unsigned, GPU::Light const&) override; - virtual void set_material_state(GPU::Face, GPU::Material const&) override; - virtual void set_stencil_configuration(GPU::Face, GPU::StencilConfiguration const&) override; - virtual void set_texture_unit_configuration(GPU::TextureUnitIndex, GPU::TextureUnitConfiguration const&) override; - virtual void set_clip_planes(Vector const&) override; - - virtual GPU::RasterPosition raster_position() const override; - virtual void set_raster_position(GPU::RasterPosition const& raster_position) override; - virtual void set_raster_position(FloatVector4 const& position) override; - - virtual void bind_fragment_shader(RefPtr) override; - -private: - void encode_constant_buffer(Gfx::FloatMatrix4x4 const&, Vector&); - Protocol::ObjectHandle allocate_handle(); - ErrorOr create_virgl_resource(VirGL3DResourceSpec&); - ErrorOr upload_command_buffer(Vector const&); - - NonnullOwnPtr m_gpu_file; - - FloatMatrix4x4 m_model_view_transform; - FloatMatrix4x4 m_projection_transform; - - Protocol::ResourceID m_vbo_resource_id { 0 }; - Protocol::ResourceID m_drawtarget { 0 }; - Protocol::ResourceID m_depthbuffer_surface { 0 }; - Protocol::ObjectHandle m_blend_handle { 0 }; - Protocol::ObjectHandle m_drawtarget_surface_handle { 0 }; - Protocol::ObjectHandle m_depthbuffer_surface_handle { 0 }; - Protocol::ObjectHandle m_ve_handle { 0 }; - Protocol::ObjectHandle m_frag_shader_handle { 0 }; - Protocol::ObjectHandle m_vert_shader_handle { 0 }; - Protocol::ObjectHandle m_rasterizer_handle { 0 }; - Protocol::ObjectHandle m_dsa_handle { 0 }; - u32 m_last_allocated_handle { 0 }; - - struct VertexData { - float r; - float g; - float b; - float x; - float y; - float z; - }; - - Vector m_vertices; - - Vector m_constant_buffer_data; -}; - -} diff --git a/Userland/Libraries/LibVirtGPU/Image.cpp b/Userland/Libraries/LibVirtGPU/Image.cpp deleted file mode 100644 index f360e99a8f9..00000000000 --- a/Userland/Libraries/LibVirtGPU/Image.cpp +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright (c) 2022, Stephan Unverwerth - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include - -namespace VirtGPU { - -Image::Image(void const* ownership_token, GPU::PixelFormat const& pixel_format, u32 width, u32 height, u32 depth, u32 max_levels) - : GPU::Image(ownership_token, pixel_format, width, height, depth, max_levels) -{ - dbgln("VirtGPU::Image::Image(): unimplemented"); -} - -void Image::regenerate_mipmaps() -{ - dbgln("VirtGPU::Image::regenerate_mipmaps(): unimplemented"); -} - -void Image::write_texels(u32, Vector3 const&, void const*, GPU::ImageDataLayout const&) -{ - dbgln("VirtGPU::Image::write_texels(): unimplemented"); -} - -void Image::read_texels(u32, Vector3 const&, void*, GPU::ImageDataLayout const&) const -{ - dbgln("VirtGPU::Image::read_texels(): unimplemented"); -} - -void Image::copy_texels(GPU::Image const&, u32, Vector3 const&, Vector3 const&, u32, Vector3 const&) -{ - dbgln("VirtGPU::Image::copy_texels(): unimplemented"); -} - -} diff --git a/Userland/Libraries/LibVirtGPU/Image.h b/Userland/Libraries/LibVirtGPU/Image.h deleted file mode 100644 index 0c4ff5dadc5..00000000000 --- a/Userland/Libraries/LibVirtGPU/Image.h +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright (c) 2022, Stephan Unverwerth - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include - -namespace VirtGPU { - -class Image final : public GPU::Image { -public: - Image(void const* ownership_token, GPU::PixelFormat const&, u32 width, u32 height, u32 depth, u32 max_levels); - - virtual void regenerate_mipmaps() override; - - virtual void write_texels(u32 level, Vector3 const& output_offset, void const* input_data, GPU::ImageDataLayout const&) override; - virtual void read_texels(u32 level, Vector3 const& input_offset, void* output_data, GPU::ImageDataLayout const&) const override; - virtual void copy_texels(GPU::Image const& source, u32 source_level, Vector3 const& source_offset, Vector3 const& size, u32 destination_level, Vector3 const& destination_offset) override; -}; - -} diff --git a/Userland/Libraries/LibVirtGPU/Shader.cpp b/Userland/Libraries/LibVirtGPU/Shader.cpp deleted file mode 100644 index 1ba4a95911e..00000000000 --- a/Userland/Libraries/LibVirtGPU/Shader.cpp +++ /dev/null @@ -1,16 +0,0 @@ -/* - * Copyright (c) 2022, Stephan Unverwerth - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include - -namespace VirtGPU { - -Shader::Shader(void const* ownership_token) - : GPU::Shader(ownership_token) -{ -} - -} diff --git a/Userland/Libraries/LibVirtGPU/Shader.h b/Userland/Libraries/LibVirtGPU/Shader.h deleted file mode 100644 index 08ac247dce4..00000000000 --- a/Userland/Libraries/LibVirtGPU/Shader.h +++ /dev/null @@ -1,18 +0,0 @@ -/* - * Copyright (c) 2022, Stephan Unverwerth - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include - -namespace VirtGPU { - -class Shader final : public GPU::Shader { -public: - Shader(void const* ownership_token); -}; - -} diff --git a/Userland/Libraries/LibVirtGPU/VirGLProtocol.h b/Userland/Libraries/LibVirtGPU/VirGLProtocol.h deleted file mode 100644 index 92a40a524c2..00000000000 --- a/Userland/Libraries/LibVirtGPU/VirGLProtocol.h +++ /dev/null @@ -1,221 +0,0 @@ -/* - * Copyright (c) 2022, Sahan Fernando - * Copyright (c) 2022, Stephan Unverwerth - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include - -namespace VirtGPU { - -namespace Protocol { - -AK_TYPEDEF_DISTINCT_ORDERED_ID(u32, ObjectHandle); -AK_TYPEDEF_DISTINCT_ORDERED_ID(u32, ResourceID); - -enum class BindTarget : u32 { - VIRGL_BIND_DEPTH_STENCIL = (1 << 0), - VIRGL_BIND_RENDER_TARGET = (1 << 1), - VIRGL_BIND_SAMPLER_VIEW = (1 << 3), - VIRGL_BIND_VERTEX_BUFFER = (1 << 4), - VIRGL_BIND_INDEX_BUFFER = (1 << 5), - VIRGL_BIND_CONSTANT_BUFFER = (1 << 6), - VIRGL_BIND_DISPLAY_TARGET = (1 << 7), - VIRGL_BIND_COMMAND_ARGS = (1 << 8), - VIRGL_BIND_STREAM_OUTPUT = (1 << 11), - VIRGL_BIND_SHADER_BUFFER = (1 << 14), - VIRGL_BIND_QUERY_BUFFER = (1 << 15), - VIRGL_BIND_CURSOR = (1 << 16), - VIRGL_BIND_CUSTOM = (1 << 17), - VIRGL_BIND_SCANOUT = (1 << 18), - VIRGL_BIND_STAGING = (1 << 19), - VIRGL_BIND_SHARED = (1 << 20), -}; - -enum class TextureFormat : u32 { - // RGBA Formats - VIRTIO_GPU_FORMAT_B8G8R8A8_UNORM = 1, - VIRTIO_GPU_FORMAT_B8G8R8X8_UNORM = 2, - VIRTIO_GPU_FORMAT_A8R8G8B8_UNORM = 3, - VIRTIO_GPU_FORMAT_X8R8G8B8_UNORM = 4, - - VIRTIO_GPU_FORMAT_R8G8B8A8_UNORM = 67, - VIRTIO_GPU_FORMAT_X8B8G8R8_UNORM = 68, - - VIRTIO_GPU_FORMAT_A8B8G8R8_UNORM = 121, - VIRTIO_GPU_FORMAT_R8G8B8X8_UNORM = 134, - - // Stencil-Depth Formats - VIRTIO_GPU_FORMAT_Z16_UNORM = 16, - VIRTIO_GPU_FORMAT_Z32_UNORM = 17, - VIRTIO_GPU_FORMAT_Z32_FLOAT = 18, - VIRTIO_GPU_FORMAT_Z24_UNORM_S8_UINT = 19, - VIRTIO_GPU_FORMAT_S8_UINT_Z24_UNORM = 20, - VIRTIO_GPU_FORMAT_Z24X8_UNORM = 21, - VIRTIO_GPU_FORMAT_X8Z24_UNORM = 22, - VIRTIO_GPU_FORMAT_S8_UINT = 23, - VIRTIO_GPU_FORMAT_Z32_FLOAT_S8X24_UINT = 128, - VIRTIO_GPU_FORMAT_X24S8_UINT = 136, - VIRTIO_GPU_FORMAT_S8X24_UINT = 137, - VIRTIO_GPU_FORMAT_X32_S8X24_UINT = 138, - -}; - -enum class VirGLCommand : u8 { - NOP = 0, - CREATE_OBJECT = 1, - BIND_OBJECT, - DESTROY_OBJECT, - SET_VIEWPORT_STATE, - SET_FRAMEBUFFER_STATE, - SET_VERTEX_BUFFERS, - CLEAR, - DRAW_VBO, - RESOURCE_INLINE_WRITE, - SET_SAMPLER_VIEWS, - SET_INDEX_BUFFER, - SET_CONSTANT_BUFFER, - SET_STENCIL_REF, - SET_BLEND_COLOR, - SET_SCISSOR_STATE, - BLIT, - RESOURCE_COPY_REGION, - BIND_SAMPLER_STATES, - BEGIN_QUERY, - END_QUERY, - GET_QUERY_RESULT, - SET_POLYGON_STIPPLE, - SET_CLIP_STATE, - SET_SAMPLE_MASK, - SET_STREAMOUT_TARGETS, - SET_RENDER_CONDITION, - SET_UNIFORM_BUFFER, - - SET_SUB_CTX, - CREATE_SUB_CTX, - DESTROY_SUB_CTX, - BIND_SHADER, - SET_TESS_STATE, - SET_MIN_SAMPLES, - SET_SHADER_BUFFERS, - SET_SHADER_IMAGES, - MEMORY_BARRIER, - LAUNCH_GRID, - SET_FRAMEBUFFER_STATE_NO_ATTACH, - TEXTURE_BARRIER, - SET_ATOMIC_BUFFERS, - SET_DBG_FLAGS, - GET_QUERY_RESULT_QBO, - TRANSFER3D, - END_TRANSFERS, - COPY_TRANSFER3D, - SET_TWEAKS, - CLEAR_TEXTURE, - PIPE_RESOURCE_CREATE, - PIPE_RESOURCE_SET_TYPE, - GET_MEMORY_INFO, - SEND_STRING_MARKER, - MAX_COMMANDS -}; - -union ClearType { - struct { - u32 depth : 1; - u32 stencil : 1; - u32 color0 : 1; - u32 color1 : 1; - u32 color2 : 1; - u32 color3 : 1; - u32 color4 : 1; - u32 color5 : 1; - u32 color6 : 1; - u32 color7 : 1; - } flags; - u32 value; -}; - -enum class ObjectType : u8 { - NONE, - BLEND, - RASTERIZER, - DSA, - SHADER, - VERTEX_ELEMENTS, - SAMPLER_VIEW, - SAMPLER_STATE, - SURFACE, - QUERY, - STREAMOUT_TARGET, - MSAA_SURFACE, - MAX_OBJECTS, -}; - -enum class PipeTextureTarget : u32 { - BUFFER = 0, - TEXTURE_1D, - TEXTURE_2D, - TEXTURE_3D, - TEXTURE_CUBE, - TEXTURE_RECT, - TEXTURE_1D_ARRAY, - TEXTURE_2D_ARRAY, - TEXTURE_CUBE_ARRAY, - MAX -}; - -enum class PipePrimitiveTypes : u32 { - POINTS = 0, - LINES, - LINE_LOOP, - LINE_STRIP, - TRIANGLES, - TRIANGLE_STRIP, - TRIANGLE_FAN, - QUADS, - QUAD_STRIP, - POLYGON, - LINES_ADJACENCY, - LINE_STRIP_ADJACENCY, - TRIANGLES_ADJACENCY, - TRIANGLE_STRIP_ADJACENCY, - PATCHES, - MAX -}; - -} - -namespace Gallium { - -enum class PipeTextureTarget : u32 { - BUFFER, - TEXTURE_1D, - TEXTURE_2D, - TEXTURE_3D, - TEXTURE_CUBE, - TEXTURE_RECT, - TEXTURE_1D_ARRAY, - TEXTURE_2D_ARRAY, - TEXTURE_CUBE_ARRAY, - MAX_TEXTURE_TYPES, -}; - -enum class ShaderType : u32 { - SHADER_VERTEX = 0, - SHADER_FRAGMENT, - SHADER_GEOMETRY, - SHADER_TESS_CTRL, - SHADER_TESS_EVAL, - SHADER_COMPUTE, - SHADER_TYPES -}; - -enum class PipeFormat : u32 { - R32G32B32_FLOAT = 30 -}; - -} - -} diff --git a/Userland/Services/AudioServer/AudioClient.ipc b/Userland/Services/AudioServer/AudioClient.ipc deleted file mode 100644 index 8f0188fbb98..00000000000 --- a/Userland/Services/AudioServer/AudioClient.ipc +++ /dev/null @@ -1,6 +0,0 @@ -#include - -endpoint AudioClient -{ - client_volume_changed(double volume) =| -} diff --git a/Userland/Services/AudioServer/AudioManagerClient.ipc b/Userland/Services/AudioServer/AudioManagerClient.ipc deleted file mode 100644 index 34e752982e0..00000000000 --- a/Userland/Services/AudioServer/AudioManagerClient.ipc +++ /dev/null @@ -1,6 +0,0 @@ -endpoint AudioManagerClient -{ - main_mix_muted_state_changed(bool muted) =| - main_mix_volume_changed(double volume) =| - device_sample_rate_changed(u32 sample_rate) =| -} diff --git a/Userland/Services/AudioServer/AudioManagerServer.ipc b/Userland/Services/AudioServer/AudioManagerServer.ipc deleted file mode 100644 index d1fd62b8fa7..00000000000 --- a/Userland/Services/AudioServer/AudioManagerServer.ipc +++ /dev/null @@ -1,11 +0,0 @@ -endpoint AudioManagerServer -{ - set_main_mix_muted(bool muted) => () - is_main_mix_muted() => (bool muted) - get_main_mix_volume() => (double volume) - set_main_mix_volume(double volume) => () - - // Audio device - set_device_sample_rate(u32 sample_rate) => () - get_device_sample_rate() => (u32 sample_rate) -} diff --git a/Userland/Services/AudioServer/AudioServer.ipc b/Userland/Services/AudioServer/AudioServer.ipc deleted file mode 100644 index 6204cc45872..00000000000 --- a/Userland/Services/AudioServer/AudioServer.ipc +++ /dev/null @@ -1,18 +0,0 @@ -#include -#include - -endpoint AudioServer -{ - set_self_muted(bool muted) => () - is_self_muted() => (bool muted) - get_self_volume() => (double volume) - set_self_volume(double volume) => () - - set_self_sample_rate(u32 sample_rate) => () - get_self_sample_rate() => (u32 sample_rate) - // Buffer playback - set_buffer(Audio::AudioQueue buffer) => () - clear_buffer() =| - start_playback() =| - pause_playback() =| -} diff --git a/Userland/Services/AudioServer/CMakeLists.txt b/Userland/Services/AudioServer/CMakeLists.txt deleted file mode 100644 index 1590b766f5f..00000000000 --- a/Userland/Services/AudioServer/CMakeLists.txt +++ /dev/null @@ -1,28 +0,0 @@ -serenity_component( - AudioServer - RECOMMENDED - TARGETS AudioServer -) - -compile_ipc(AudioServer.ipc AudioServerEndpoint.h) -compile_ipc(AudioClient.ipc AudioClientEndpoint.h) -compile_ipc(AudioManagerClient.ipc AudioManagerClientEndpoint.h) -compile_ipc(AudioManagerServer.ipc AudioManagerServerEndpoint.h) - -set(SOURCES - ClientAudioStream.cpp - ConnectionFromClient.cpp - ConnectionFromManagerClient.cpp - Mixer.cpp - main.cpp -) - -set(GENERATED_SOURCES - AudioServerEndpoint.h - AudioClientEndpoint.h - AudioManagerClientEndpoint.h - AudioManagerServerEndpoint.h -) - -serenity_bin(AudioServer) -target_link_libraries(AudioServer PRIVATE LibCore LibThreading LibIPC LibMain) diff --git a/Userland/Services/AudioServer/ClientAudioStream.cpp b/Userland/Services/AudioServer/ClientAudioStream.cpp deleted file mode 100644 index bd5e3a29394..00000000000 --- a/Userland/Services/AudioServer/ClientAudioStream.cpp +++ /dev/null @@ -1,117 +0,0 @@ -/* - * Copyright (c) 2018-2022, the SerenityOS developers. - * Copyright (c) 2021-2023, kleines Filmröllchen - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "ClientAudioStream.h" -#include - -namespace AudioServer { - -ClientAudioStream::ClientAudioStream(ConnectionFromClient& client) - : m_client(client) -{ -} - -Optional ClientAudioStream::client() -{ - return m_client.has_value() ? *m_client : Optional {}; -} - -bool ClientAudioStream::is_connected() const -{ - return m_client && m_client->is_open(); -} - -ErrorOr ClientAudioStream::get_next_sample(u32 audiodevice_sample_rate) -{ - // Note: Even though we only check client state here, we will probably close the client much earlier. - if (!is_connected()) - return ErrorState::ClientDisconnected; - - if (m_paused) - return ErrorState::ClientUnderrun; - - if (m_in_chunk_location >= m_current_audio_chunk.size()) { - auto result = m_buffer->dequeue(); - if (result.is_error()) { - if (result.error() == Audio::AudioQueue::QueueStatus::Empty) { - dbgln_if(AUDIO_DEBUG, "Audio client {} can't keep up!", m_client->client_id()); - } - - return ErrorState::ClientUnderrun; - } - // FIXME: Our resampler and the way we resample here are bad. - // Ideally, we should both do perfect band-corrected resampling, - // as well as carry resampling state over between buffers. - auto maybe_resampled = Audio::ResampleHelper { m_sample_rate == 0 ? audiodevice_sample_rate : m_sample_rate, audiodevice_sample_rate } - .try_resample(result.release_value()); - if (maybe_resampled.is_error()) - return ErrorState::ResamplingError; - - // If the sample rate changes underneath us, we will still play the existing buffer unchanged until we're done. - // This is not a significant problem since the buffers are very small (~100 samples or less). - m_current_audio_chunk = maybe_resampled.release_value(); - m_in_chunk_location = 0; - } - - return m_current_audio_chunk[m_in_chunk_location++]; -} - -void ClientAudioStream::set_buffer(NonnullOwnPtr buffer) -{ - m_buffer = move(buffer); -} - -void ClientAudioStream::clear() -{ - ErrorOr, Audio::AudioQueue::QueueStatus> result = Audio::AudioQueue::QueueStatus::Invalid; - do { - result = m_buffer->dequeue(); - } while (!result.is_error() || result.error() != Audio::AudioQueue::QueueStatus::Empty); -} - -void ClientAudioStream::set_paused(bool paused) -{ - m_paused = paused; -} - -FadingProperty& ClientAudioStream::volume() -{ - return m_volume; -} - -double ClientAudioStream::volume() const -{ - return m_volume; -} - -void ClientAudioStream::set_volume(double const volume) -{ - m_volume = volume; -} - -bool ClientAudioStream::is_muted() const -{ - return m_muted; -} - -void ClientAudioStream::set_muted(bool muted) -{ - m_muted = muted; -} - -u32 ClientAudioStream::sample_rate() const -{ - return m_sample_rate; -} - -void ClientAudioStream::set_sample_rate(u32 sample_rate) -{ - dbgln_if(AUDIO_DEBUG, "queue {} got sample rate {} Hz", m_client->client_id(), sample_rate); - m_sample_rate = sample_rate; -} - -} diff --git a/Userland/Services/AudioServer/ClientAudioStream.h b/Userland/Services/AudioServer/ClientAudioStream.h deleted file mode 100644 index 0de7a625e18..00000000000 --- a/Userland/Services/AudioServer/ClientAudioStream.h +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright (c) 2018-2022, the SerenityOS developers. - * Copyright (c) 2021-2023, kleines Filmröllchen - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include "ConnectionFromClient.h" -#include "FadingProperty.h" -#include -#include -#include -#include -#include - -namespace AudioServer { - -class ClientAudioStream : public RefCounted { -public: - enum class ErrorState { - ClientDisconnected, - ClientPaused, - ClientUnderrun, - ResamplingError, - }; - - explicit ClientAudioStream(ConnectionFromClient&); - ~ClientAudioStream() = default; - - ErrorOr get_next_sample(u32 audiodevice_sample_rate); - void clear(); - - bool is_connected() const; - - Optional client(); - - void set_buffer(NonnullOwnPtr buffer); - - void set_paused(bool paused); - FadingProperty& volume(); - double volume() const; - void set_volume(double volume); - bool is_muted() const; - void set_muted(bool muted); - u32 sample_rate() const; - void set_sample_rate(u32 sample_rate); - -private: - OwnPtr m_buffer; - Vector m_current_audio_chunk; - size_t m_in_chunk_location; - - bool m_paused { true }; - bool m_muted { false }; - u32 m_sample_rate; - - WeakPtr m_client; - FadingProperty m_volume { 1 }; -}; - -} diff --git a/Userland/Services/AudioServer/ConnectionFromClient.cpp b/Userland/Services/AudioServer/ConnectionFromClient.cpp deleted file mode 100644 index 52ab884730a..00000000000 --- a/Userland/Services/AudioServer/ConnectionFromClient.cpp +++ /dev/null @@ -1,115 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "ConnectionFromClient.h" -#include "Mixer.h" -#include -#include - -namespace AudioServer { - -static HashMap> s_connections; - -void ConnectionFromClient::for_each(Function callback) -{ - Vector> connections; - for (auto& it : s_connections) - connections.append(*it.value); - for (auto& connection : connections) - callback(connection); -} - -ConnectionFromClient::ConnectionFromClient(NonnullOwnPtr client_socket, int client_id, Mixer& mixer) - : IPC::ConnectionFromClient(*this, move(client_socket), client_id) - , m_mixer(mixer) -{ - s_connections.set(client_id, *this); -} - -void ConnectionFromClient::die() -{ - s_connections.remove(client_id()); -} - -void ConnectionFromClient::set_buffer(Audio::AudioQueue const& buffer) -{ - if (!buffer.is_valid()) { - did_misbehave("Received an invalid buffer"); - return; - } - if (!m_queue) { - m_queue = m_mixer.create_queue(*this); - if (m_saved_sample_rate.has_value()) - m_queue->set_sample_rate(m_saved_sample_rate.release_value()); - } - - // This is ugly but we know nobody uses the buffer afterwards anyways. - m_queue->set_buffer(make(move(const_cast(buffer)))); -} - -void ConnectionFromClient::did_change_client_volume(Badge, double volume) -{ - async_client_volume_changed(volume); -} - -Messages::AudioServer::GetSelfSampleRateResponse ConnectionFromClient::get_self_sample_rate() -{ - if (m_queue) - return { m_queue->sample_rate() }; - // Fall back to device sample rate since that would mean no resampling. - return m_mixer.audiodevice_get_sample_rate(); -} -void ConnectionFromClient::set_self_sample_rate(u32 sample_rate) -{ - if (m_queue) - m_queue->set_sample_rate(sample_rate); - else - m_saved_sample_rate = sample_rate; -} - -Messages::AudioServer::GetSelfVolumeResponse ConnectionFromClient::get_self_volume() -{ - return m_queue->volume().target(); -} - -void ConnectionFromClient::set_self_volume(double volume) -{ - if (m_queue) - m_queue->set_volume(volume); -} - -void ConnectionFromClient::start_playback() -{ - if (m_queue) - m_queue->set_paused(false); -} - -void ConnectionFromClient::pause_playback() -{ - if (m_queue) - m_queue->set_paused(true); -} - -void ConnectionFromClient::clear_buffer() -{ - if (m_queue) - m_queue->clear(); -} - -Messages::AudioServer::IsSelfMutedResponse ConnectionFromClient::is_self_muted() -{ - if (m_queue) - return m_queue->is_muted(); - - return false; -} - -void ConnectionFromClient::set_self_muted(bool muted) -{ - if (m_queue) - m_queue->set_muted(muted); -} -} diff --git a/Userland/Services/AudioServer/ConnectionFromClient.h b/Userland/Services/AudioServer/ConnectionFromClient.h deleted file mode 100644 index b1d04f574ef..00000000000 --- a/Userland/Services/AudioServer/ConnectionFromClient.h +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include -#include - -namespace AudioServer { - -class ClientAudioStream; -class Mixer; - -class ConnectionFromClient final : public IPC::ConnectionFromClient { - C_OBJECT(ConnectionFromClient) -public: - ~ConnectionFromClient() override = default; - - void did_change_client_volume(Badge, double volume); - - virtual void die() override; - - static void for_each(Function); - -private: - explicit ConnectionFromClient(NonnullOwnPtr, int client_id, Mixer& mixer); - - virtual Messages::AudioServer::GetSelfVolumeResponse get_self_volume() override; - virtual void set_self_volume(double) override; - virtual void set_buffer(Audio::AudioQueue const&) override; - virtual void clear_buffer() override; - virtual void start_playback() override; - virtual void pause_playback() override; - virtual Messages::AudioServer::IsSelfMutedResponse is_self_muted() override; - virtual void set_self_muted(bool) override; - virtual Messages::AudioServer::GetSelfSampleRateResponse get_self_sample_rate() override; - virtual void set_self_sample_rate(u32 sample_rate) override; - - Mixer& m_mixer; - RefPtr m_queue; - Optional m_saved_sample_rate {}; -}; - -} diff --git a/Userland/Services/AudioServer/ConnectionFromManagerClient.cpp b/Userland/Services/AudioServer/ConnectionFromManagerClient.cpp deleted file mode 100644 index c43d520f796..00000000000 --- a/Userland/Services/AudioServer/ConnectionFromManagerClient.cpp +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Copyright (c) 2023, kleines Filmröllchen - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "ConnectionFromManagerClient.h" - -namespace AudioServer { - -static HashMap> s_connections; - -ConnectionFromManagerClient::ConnectionFromManagerClient(NonnullOwnPtr client_socket, int client_id, Mixer& mixer) - : IPC::ConnectionFromClient(*this, move(client_socket), client_id) - , m_mixer(mixer) -{ - s_connections.set(client_id, *this); -} - -void ConnectionFromManagerClient::die() -{ - s_connections.remove(client_id()); -} - -void ConnectionFromManagerClient::for_each(Function callback) -{ - Vector> connections; - for (auto& it : s_connections) - connections.append(*it.value); - for (auto& connection : connections) - callback(connection); -} - -void ConnectionFromManagerClient::did_change_main_mix_muted_state(Badge, bool muted) -{ - async_main_mix_muted_state_changed(muted); -} - -void ConnectionFromManagerClient::did_change_main_mix_volume(Badge, double volume) -{ - async_main_mix_volume_changed(volume); -} - -Messages::AudioManagerServer::GetMainMixVolumeResponse ConnectionFromManagerClient::get_main_mix_volume() -{ - return m_mixer.main_volume(); -} - -void ConnectionFromManagerClient::set_main_mix_volume(double volume) -{ - m_mixer.set_main_volume(volume); -} - -Messages::AudioManagerServer::GetDeviceSampleRateResponse ConnectionFromManagerClient::get_device_sample_rate() -{ - return { m_mixer.audiodevice_get_sample_rate() }; -} - -void ConnectionFromManagerClient::set_device_sample_rate(u32 sample_rate) -{ - m_mixer.audiodevice_set_sample_rate(sample_rate); -} - -Messages::AudioManagerServer::IsMainMixMutedResponse ConnectionFromManagerClient::is_main_mix_muted() -{ - return m_mixer.is_muted(); -} - -void ConnectionFromManagerClient::set_main_mix_muted(bool muted) -{ - m_mixer.set_muted(muted); -} - -} diff --git a/Userland/Services/AudioServer/ConnectionFromManagerClient.h b/Userland/Services/AudioServer/ConnectionFromManagerClient.h deleted file mode 100644 index 05841ab3874..00000000000 --- a/Userland/Services/AudioServer/ConnectionFromManagerClient.h +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright (c) 2023, kleines Filmröllchen - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include - -namespace AudioServer { - -class ConnectionFromManagerClient final : public IPC::ConnectionFromClient { - C_OBJECT(ConnectionFromManagerClient) - -public: - ~ConnectionFromManagerClient() override = default; - - virtual void die() override; - - static void for_each(Function); - - void did_change_main_mix_muted_state(Badge, bool muted); - void did_change_main_mix_volume(Badge, double volume); - -private: - ConnectionFromManagerClient(NonnullOwnPtr client_socket, int client_id, Mixer& mixer); - - virtual Messages::AudioManagerServer::GetMainMixVolumeResponse get_main_mix_volume() override; - virtual void set_main_mix_volume(double) override; - virtual Messages::AudioManagerServer::IsMainMixMutedResponse is_main_mix_muted() override; - virtual void set_main_mix_muted(bool) override; - virtual void set_device_sample_rate(u32 sample_rate) override; - virtual Messages::AudioManagerServer::GetDeviceSampleRateResponse get_device_sample_rate() override; - - Mixer& m_mixer; -}; - -} diff --git a/Userland/Services/AudioServer/FadingProperty.h b/Userland/Services/AudioServer/FadingProperty.h deleted file mode 100644 index bdee2231db3..00000000000 --- a/Userland/Services/AudioServer/FadingProperty.h +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright (c) 2021, kleines Filmröllchen . - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -namespace AudioServer { - -// This is in buffer counts. -// As each buffer is approx 1/40 of a second, this means about 1/4 of a second of fade time. -constexpr int DEFAULT_FADE_TIME = 10; - -// A property of an audio system that needs to fade briefly whenever changed. -template -class FadingProperty { -public: - FadingProperty(T const value) - : FadingProperty(value, DEFAULT_FADE_TIME) - { - } - FadingProperty(T const value, int const fade_time) - : m_old_value(value) - , m_new_value(move(value)) - , m_fade_time(fade_time) - { - } - virtual ~FadingProperty() - { - m_old_value.~T(); - m_new_value.~T(); - } - - FadingProperty& operator=(T const& new_value) - { - // The origin of the fade is wherever we're right now. - m_old_value = static_cast(*this); - m_new_value = new_value; - m_current_fade = 0; - return *this; - } - FadingProperty& operator=(FadingProperty const&) = delete; - - operator T() const - { - if (!is_fading()) - return m_new_value; - return m_old_value * (1 - m_current_fade) + m_new_value * (m_current_fade); - } - - void advance_time() - { - m_current_fade += 1.0 / static_cast(m_fade_time); - m_current_fade = clamp(m_current_fade, 0.0, 1.0); - } - - bool is_fading() const - { - return m_current_fade < 1; - } - - T target() const { return m_new_value; } - -private: - T m_old_value {}; - T m_new_value {}; - double m_current_fade { 0 }; - int const m_fade_time; -}; - -} diff --git a/Userland/Services/AudioServer/Mixer.cpp b/Userland/Services/AudioServer/Mixer.cpp deleted file mode 100644 index 9138c010443..00000000000 --- a/Userland/Services/AudioServer/Mixer.cpp +++ /dev/null @@ -1,191 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * Copyright (c) 2021, kleines Filmröllchen - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "Mixer.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace AudioServer { - -Mixer::Mixer(NonnullRefPtr config, NonnullOwnPtr device) - : m_device(move(device)) - , m_sound_thread(Threading::Thread::construct( - [this] { - mix(); - return 0; - }, - "AudioServer[mixer]"sv)) - , m_config(move(config)) -{ - m_muted = m_config->read_bool_entry("Master", "Mute", false); - m_main_volume = static_cast(m_config->read_num_entry("Master", "Volume", 100)) / 100.0; - - m_sound_thread->start(); -} - -NonnullRefPtr Mixer::create_queue(ConnectionFromClient& client) -{ - auto queue = adopt_ref(*new ClientAudioStream(client)); - queue->set_sample_rate(audiodevice_get_sample_rate()); - { - Threading::MutexLocker const locker(m_pending_mutex); - m_pending_mixing.append(*queue); - } - // Signal the mixer thread to start back up, in case nobody was connected before. - m_mixing_necessary.signal(); - - return queue; -} - -void Mixer::mix() -{ - decltype(m_pending_mixing) active_mix_queues; - - for (;;) { - { - Threading::MutexLocker const locker(m_pending_mutex); - // While we have nothing to mix, wait on the condition. - m_mixing_necessary.wait_while([this, &active_mix_queues]() { return m_pending_mixing.is_empty() && active_mix_queues.is_empty(); }); - if (!m_pending_mixing.is_empty()) { - active_mix_queues.extend(move(m_pending_mixing)); - m_pending_mixing.clear(); - } - } - - active_mix_queues.remove_all_matching([&](auto& entry) { return !entry->is_connected(); }); - - Array mixed_buffer; - - m_main_volume.advance_time(); - - // Mix the buffers together into the output - for (auto& queue : active_mix_queues) { - if (!queue->client().has_value()) { - queue->clear(); - continue; - } - queue->volume().advance_time(); - - // FIXME: Perform sample extraction and mixing in two separate loops so they can be more easily vectorized. - for (auto& mixed_sample : mixed_buffer) { - auto sample_or_error = queue->get_next_sample(audiodevice_get_sample_rate()); - if (sample_or_error.is_error()) - break; - if (queue->is_muted()) - continue; - auto sample = sample_or_error.release_value(); - sample.log_multiply(SAMPLE_HEADROOM); - sample.log_multiply(static_cast(queue->volume())); - mixed_sample += sample; - } - } - - // Even though it's not realistic, the user expects no sound at 0%. - if (m_muted || m_main_volume < 0.01) { - m_device->write_until_depleted(m_zero_filled_buffer).release_value_but_fixme_should_propagate_errors(); - } else { - FixedMemoryStream stream { m_stream_buffer.span() }; - - for (auto& mixed_sample : mixed_buffer) { - mixed_sample.log_multiply(static_cast(m_main_volume)); - mixed_sample.clip(); - - LittleEndian out_sample; - out_sample = static_cast(mixed_sample.left * NumericLimits::max()); - MUST(stream.write_value(out_sample)); - - out_sample = static_cast(mixed_sample.right * NumericLimits::max()); - MUST(stream.write_value(out_sample)); - } - - auto buffered_bytes = MUST(stream.tell()); - VERIFY(buffered_bytes == m_stream_buffer.size()); - m_device->write_until_depleted({ m_stream_buffer.data(), buffered_bytes }) - .release_value_but_fixme_should_propagate_errors(); - } - } -} - -void Mixer::set_main_volume(double volume) -{ - if (volume < 0) - m_main_volume = 0; - else if (volume > 2) - m_main_volume = 2; - else - m_main_volume = volume; - - m_config->write_num_entry("Master", "Volume", static_cast(volume * 100)); - request_setting_sync(); - - ConnectionFromManagerClient::for_each([&](auto& client) { - client.did_change_main_mix_volume({}, main_volume()); - }); -} - -void Mixer::set_muted(bool muted) -{ - if (m_muted == muted) - return; - m_muted = muted; - - m_config->write_bool_entry("Master", "Mute", m_muted); - request_setting_sync(); - - ConnectionFromManagerClient::for_each([muted](auto& client) { - client.did_change_main_mix_muted_state({}, muted); - }); -} - -int Mixer::audiodevice_set_sample_rate(u32 sample_rate) -{ - int code = ioctl(m_device->fd(), SOUNDCARD_IOCTL_SET_SAMPLE_RATE, sample_rate); - if (code != 0) - dbgln("Error while setting sample rate to {}: ioctl error: {}", sample_rate, strerror(errno)); - // Note that the effective sample rate may be different depending on device restrictions. - // Therefore, we delete our cache, but for efficency don't immediately read the sample rate back. - m_cached_sample_rate = {}; - return code; -} - -u32 Mixer::audiodevice_get_sample_rate() const -{ - if (m_cached_sample_rate.has_value()) - return m_cached_sample_rate.value(); - u32 sample_rate = 0; - int code = ioctl(m_device->fd(), SOUNDCARD_IOCTL_GET_SAMPLE_RATE, &sample_rate); - if (code != 0) - dbgln("Error while getting sample rate: ioctl error: {}", strerror(errno)); - else - m_cached_sample_rate = sample_rate; - return sample_rate; -} - -void Mixer::request_setting_sync() -{ - if (m_config_write_timer.is_null() || !m_config_write_timer->is_active()) { - m_config_write_timer = Core::Timer::create_single_shot( - AUDIO_CONFIG_WRITE_INTERVAL, - [this] { - if (auto result = m_config->sync(); result.is_error()) - dbgln("Failed to write audio mixer config: {}", result.error()); - }, - this); - m_config_write_timer->start(); - } -} - -} diff --git a/Userland/Services/AudioServer/Mixer.h b/Userland/Services/AudioServer/Mixer.h deleted file mode 100644 index af80f6da6d9..00000000000 --- a/Userland/Services/AudioServer/Mixer.h +++ /dev/null @@ -1,92 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * Copyright (c) 2021-2023, kleines Filmröllchen - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include "ClientAudioStream.h" -#include "ConnectionFromClient.h" -#include "FadingProperty.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace AudioServer { - -// Headroom, i.e. fixed attenuation for all audio streams. -// This is to prevent clipping when two streams with low headroom (e.g. normalized & compressed) are playing. -constexpr double SAMPLE_HEADROOM = 0.95; -// The size of the buffer in samples that the hardware receives through write() calls to the audio device. -constexpr size_t HARDWARE_BUFFER_SIZE = 512; -// The hardware buffer size in bytes; there's two channels of 16-bit samples. -constexpr size_t HARDWARE_BUFFER_SIZE_BYTES = HARDWARE_BUFFER_SIZE * 2 * sizeof(i16); - -class Mixer : public Core::EventReceiver { - C_OBJECT_ABSTRACT(Mixer) -public: - static ErrorOr> try_create(NonnullRefPtr config) - { - // FIXME: Allow AudioServer to use other audio channels as well - auto device = TRY(Core::File::open("/dev/audio/0"sv, Core::File::OpenMode::Write)); - return adopt_nonnull_ref_or_enomem(new (nothrow) Mixer(move(config), move(device))); - } - - virtual ~Mixer() override = default; - - NonnullRefPtr create_queue(ConnectionFromClient&); - - // To the outside world, we pretend that the target volume is already reached, even though it may be still fading. - double main_volume() const { return m_main_volume.target(); } - void set_main_volume(double volume); - - bool is_muted() const { return m_muted; } - void set_muted(bool); - - int audiodevice_set_sample_rate(u32 sample_rate); - u32 audiodevice_get_sample_rate() const; - -private: - Mixer(NonnullRefPtr config, NonnullOwnPtr device); - - void request_setting_sync(); - - Vector> m_pending_mixing; - Threading::Mutex m_pending_mutex; - Threading::ConditionVariable m_mixing_necessary { m_pending_mutex }; - - NonnullOwnPtr m_device; - mutable Optional m_cached_sample_rate {}; - - NonnullRefPtr m_sound_thread; - - bool m_muted { false }; - FadingProperty m_main_volume { 1 }; - - NonnullRefPtr m_config; - RefPtr m_config_write_timer; - - Array m_stream_buffer; - Array const m_zero_filled_buffer {}; - - void mix(); -}; - -// Interval in ms when the server tries to save its configuration to disk. -constexpr unsigned AUDIO_CONFIG_WRITE_INTERVAL = 2000; - -} diff --git a/Userland/Services/AudioServer/main.cpp b/Userland/Services/AudioServer/main.cpp deleted file mode 100644 index 186fd5cfd74..00000000000 --- a/Userland/Services/AudioServer/main.cpp +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright (c) 2018-2021, Andreas Kling - * Copyright (c) 2021, kleines Filmröllchen - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "ConnectionFromClient.h" -#include "ConnectionFromManagerClient.h" -#include "Mixer.h" -#include -#include -#include -#include - -ErrorOr serenity_main(Main::Arguments) -{ - TRY(Core::System::pledge("stdio recvfd thread accept cpath rpath wpath unix")); - - auto config = TRY(Core::ConfigFile::open_for_app("Audio", Core::ConfigFile::AllowWriting::Yes)); - TRY(Core::System::unveil(config->filename(), "rwc"sv)); - TRY(Core::System::unveil("/dev/audio", "wc")); - TRY(Core::System::unveil(nullptr, nullptr)); - - Core::EventLoop event_loop; - auto mixer = TRY(AudioServer::Mixer::try_create(config)); - auto server = TRY(Core::LocalServer::try_create()); - TRY(server->take_over_from_system_server("/tmp/session/%sid/portal/audio")); - - server->on_accept = [&](NonnullOwnPtr client_socket) { - static int s_next_client_id = 0; - int client_id = ++s_next_client_id; - (void)IPC::new_client_connection(move(client_socket), client_id, *mixer); - }; - - auto manager_server = TRY(Core::LocalServer::try_create()); - TRY(manager_server->take_over_from_system_server("/tmp/session/%sid/portal/audiomanager")); - - manager_server->on_accept = [&](NonnullOwnPtr client_socket) { - static int s_next_client_id = 0; - int client_id = ++s_next_client_id; - (void)IPC::new_client_connection(move(client_socket), client_id, *mixer); - }; - - TRY(Core::System::pledge("stdio recvfd thread accept cpath rpath wpath")); - - return event_loop.exec(); -} diff --git a/Userland/Services/CMakeLists.txt b/Userland/Services/CMakeLists.txt deleted file mode 100644 index 1773bce081a..00000000000 --- a/Userland/Services/CMakeLists.txt +++ /dev/null @@ -1,31 +0,0 @@ -add_subdirectory(ConfigServer) -add_subdirectory(EchoServer) -add_subdirectory(FileOperation) -add_subdirectory(LookupServer) -add_subdirectory(WebServer) - -if (SERENITYOS) - add_subdirectory(AudioServer) - add_subdirectory(ChessEngine) - add_subdirectory(Clipboard) - add_subdirectory(CrashDaemon) - add_subdirectory(DeviceMapper) - add_subdirectory(DHCPClient) - add_subdirectory(ImageDecoder) - add_subdirectory(FileSystemAccessServer) - add_subdirectory(KeyboardPreferenceLoader) - add_subdirectory(LaunchServer) - add_subdirectory(LoginServer) - add_subdirectory(NetworkServer) - add_subdirectory(NotificationServer) - add_subdirectory(RequestServer) - add_subdirectory(SpiceAgent) - add_subdirectory(SQLServer) - add_subdirectory(SystemServer) - add_subdirectory(Taskbar) - add_subdirectory(TelnetServer) - add_subdirectory(WebContent) - add_subdirectory(WebDriver) - add_subdirectory(WebWorker) - add_subdirectory(WindowServer) -endif() diff --git a/Userland/Services/ChessEngine/CMakeLists.txt b/Userland/Services/ChessEngine/CMakeLists.txt deleted file mode 100644 index a236edc3002..00000000000 --- a/Userland/Services/ChessEngine/CMakeLists.txt +++ /dev/null @@ -1,13 +0,0 @@ -serenity_component( - ChessEngine - TARGETS ChessEngine -) - -set(SOURCES - ChessEngine.cpp - main.cpp - MCTSTree.cpp -) - -serenity_bin(ChessEngine) -target_link_libraries(ChessEngine PRIVATE LibChess LibCore LibMain) diff --git a/Userland/Services/ChessEngine/ChessEngine.cpp b/Userland/Services/ChessEngine/ChessEngine.cpp deleted file mode 100644 index 85ebb4cdd69..00000000000 --- a/Userland/Services/ChessEngine/ChessEngine.cpp +++ /dev/null @@ -1,81 +0,0 @@ -/* - * Copyright (c) 2020, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "ChessEngine.h" -#include "MCTSTree.h" -#include -#include - -using namespace Chess::UCI; - -void ChessEngine::handle_uci() -{ - send_command(IdCommand(IdCommand::Type::Name, "ChessEngine"_string)); - send_command(IdCommand(IdCommand::Type::Author, "the SerenityOS developers"_string)); - send_command(UCIOkCommand()); -} - -void ChessEngine::handle_position(PositionCommand const& command) -{ - // FIXME: Implement fen board position. - VERIFY(!command.fen().has_value()); - m_board = Chess::Board(); - for (auto& move : command.moves()) { - VERIFY(m_board.apply_move(move)); - } -} - -void ChessEngine::handle_go(GoCommand const& command) -{ - // FIXME: A better algorithm than naive mcts. - // FIXME: Add different ways to terminate search. - VERIFY(command.movetime.has_value()); - - srand(get_random()); - - auto elapsed_time = Core::ElapsedTimer::start_new(); - - auto mcts = [this]() -> MCTSTree { - if (!m_last_tree.has_value()) - return { m_board }; - auto x = m_last_tree.value().child_with_move(m_board.last_move().value()); - if (x.has_value()) - return move(x.value()); - return { m_board }; - }(); - - int rounds = 0; - while (elapsed_time.elapsed() <= command.movetime.value()) { - mcts.do_round(); - ++rounds; - } - dbgln("MCTS finished {} rounds.", rounds); - dbgln("MCTS evaluation {}", mcts.expected_value()); - auto& best_node = mcts.best_node(); - auto const& best_move = best_node.last_move(); - dbgln("MCTS best move {}", best_move.to_long_algebraic()); - send_command(BestMoveCommand(best_move)); - - m_last_tree = move(best_node); -} - -void ChessEngine::handle_quit() -{ - if (on_quit) - on_quit(ESUCCESS); -} - -void ChessEngine::handle_unexpected_eof() -{ - if (on_quit) - on_quit(EPIPE); -} - -void ChessEngine::handle_ucinewgame() -{ - m_board = Chess::Board(); - m_last_tree = {}; -} diff --git a/Userland/Services/ChessEngine/ChessEngine.h b/Userland/Services/ChessEngine/ChessEngine.h deleted file mode 100644 index 69c20fae9e3..00000000000 --- a/Userland/Services/ChessEngine/ChessEngine.h +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright (c) 2020, the SerenityOS developers. - * Copyright (c) 2023, Tim Ledbetter - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include "MCTSTree.h" -#include -#include - -class ChessEngine : public Chess::UCI::Endpoint { - C_OBJECT_ABSTRACT(ChessEngine) -public: - static ErrorOr> try_create(NonnullOwnPtr in, NonnullOwnPtr out) - { - auto engine = TRY(adopt_nonnull_ref_or_enomem(new (nothrow) ChessEngine())); - TRY(engine->set_in(move(in))); - engine->set_out(move(out)); - return engine; - } - virtual ~ChessEngine() override = default; - - virtual void handle_uci() override; - virtual void handle_position(Chess::UCI::PositionCommand const&) override; - virtual void handle_go(Chess::UCI::GoCommand const&) override; - virtual void handle_quit() override; - virtual void handle_ucinewgame() override; - virtual void handle_unexpected_eof() override; - - Function on_quit; - -private: - ChessEngine() - : Endpoint() - { - on_command_read_error = [](auto command, auto error) { - outln("{}: '{}'", error, command); - }; - } - - Chess::Board m_board; - Optional m_last_tree; -}; diff --git a/Userland/Services/ChessEngine/MCTSTree.cpp b/Userland/Services/ChessEngine/MCTSTree.cpp deleted file mode 100644 index 3061398c7ad..00000000000 --- a/Userland/Services/ChessEngine/MCTSTree.cpp +++ /dev/null @@ -1,194 +0,0 @@ -/* - * Copyright (c) 2020, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "MCTSTree.h" -#include - -MCTSTree::MCTSTree(Chess::Board const& board, MCTSTree* parent) - : m_parent(parent) - , m_board(make(board)) - , m_last_move(board.last_move()) - , m_turn(board.turn()) -{ -} - -MCTSTree::MCTSTree(MCTSTree&& other) - : m_children(move(other.m_children)) - , m_parent(other.m_parent) - , m_white_points(other.m_white_points) - , m_simulations(other.m_simulations) - , m_board(move(other.m_board)) - , m_last_move(move(other.m_last_move)) - , m_turn(other.m_turn) - , m_moves_generated(other.m_moves_generated) -{ - other.m_parent = nullptr; -} - -MCTSTree& MCTSTree::select_leaf() -{ - if (!expanded() || m_children.size() == 0) - return *this; - - MCTSTree* node = nullptr; - double max_uct = -double(INFINITY); - for (auto& child : m_children) { - double uct = child->uct(m_turn); - if (uct >= max_uct) { - max_uct = uct; - node = child; - } - } - VERIFY(node); - return node->select_leaf(); -} - -MCTSTree& MCTSTree::expand() -{ - VERIFY(!expanded() || m_children.size() == 0); - - if (!m_moves_generated) { - m_board->generate_moves([&](Chess::Move chess_move) { - auto clone = m_board->clone_without_history(); - clone.apply_move(chess_move); - m_children.append(make(move(clone), this)); - return IterationDecision::Continue; - }); - m_moves_generated = true; - if (m_children.size() != 0) - m_board = nullptr; // Release the board to save memory. - } - - if (m_children.size() == 0) { - return *this; - } - - for (auto& child : m_children) { - if (child->m_simulations == 0) { - return *child; - } - } - VERIFY_NOT_REACHED(); -} - -int MCTSTree::simulate_game() const -{ - Chess::Board clone = *m_board; - while (!clone.game_finished()) { - clone.apply_move(clone.random_move()); - } - return clone.game_score(); -} - -int MCTSTree::heuristic() const -{ - if (m_board->game_finished()) - return m_board->game_score(); - - double winchance = max(min(double(m_board->material_imbalance()) / 6, 1.0), -1.0); - - double random = double(rand()) / RAND_MAX; - if (winchance >= random) - return 1; - if (winchance <= -random) - return -1; - - return 0; -} - -void MCTSTree::apply_result(int game_score) -{ - m_simulations++; - m_white_points += game_score; - - if (m_parent) - m_parent->apply_result(game_score); -} - -void MCTSTree::do_round() -{ - - // Note: Limit expansion to spare some memory - // Efficient Selectivity and Backup Operators in Monte-Carlo Tree Search. - // Rémi Coulom. - auto* node_ptr = &select_leaf(); - if (node_ptr->m_simulations > s_number_of_visit_parameter) - node_ptr = &select_leaf().expand(); - - auto& node = *node_ptr; - - int result; - if constexpr (s_eval_method == EvalMethod::Simulation) { - result = node.simulate_game(); - } else { - result = node.heuristic(); - } - node.apply_result(result); -} - -Optional MCTSTree::child_with_move(Chess::Move chess_move) -{ - for (auto& node : m_children) { - if (node->last_move() == chess_move) - return *node; - } - return {}; -} - -MCTSTree& MCTSTree::best_node() -{ - int score_multiplier = (m_turn == Chess::Color::White) ? 1 : -1; - - MCTSTree* best_node_ptr = nullptr; - double best_score = -double(INFINITY); - VERIFY(m_children.size()); - for (auto& node : m_children) { - double node_score = node->expected_value() * score_multiplier; - if (node_score >= best_score) { - best_node_ptr = node; - best_score = node_score; - } - } - VERIFY(best_node_ptr); - - return *best_node_ptr; -} - -Chess::Move MCTSTree::last_move() const -{ - return m_last_move.value(); -} - -double MCTSTree::expected_value() const -{ - if (m_simulations == 0) - return 0; - - return double(m_white_points) / m_simulations; -} - -double MCTSTree::uct(Chess::Color color) const -{ - // UCT: Upper Confidence Bound Applied to Trees. - // Kocsis, Levente; Szepesvári, Csaba (2006). "Bandit based Monte-Carlo Planning" - - // Fun fact: Szepesvári was my data structures professor. - double expected = expected_value() * ((color == Chess::Color::White) ? 1 : -1); - return expected + s_exploration_parameter * sqrt(log(m_parent->m_simulations) / m_simulations); -} - -bool MCTSTree::expanded() const -{ - if (!m_moves_generated) - return false; - - for (auto& child : m_children) { - if (child->m_simulations == 0) - return false; - } - - return true; -} diff --git a/Userland/Services/ChessEngine/MCTSTree.h b/Userland/Services/ChessEngine/MCTSTree.h deleted file mode 100644 index b3e0677c936..00000000000 --- a/Userland/Services/ChessEngine/MCTSTree.h +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright (c) 2020, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include - -class MCTSTree { -public: - enum EvalMethod { - Simulation, - Heuristic, - }; - - MCTSTree(Chess::Board const& board, MCTSTree* parent = nullptr); - MCTSTree(MCTSTree&&); - - MCTSTree& select_leaf(); - MCTSTree& expand(); - int simulate_game() const; - int heuristic() const; - void apply_result(int game_score); - void do_round(); - - Optional child_with_move(Chess::Move); - - MCTSTree& best_node(); - - Chess::Move last_move() const; - double expected_value() const; - double uct(Chess::Color color) const; - bool expanded() const; - -private: - // While static parameters are less configurable, they don't take up any - // memory in the tree, which I believe to be a worthy tradeoff. - static constexpr double s_exploration_parameter { M_SQRT2 }; - static constexpr int s_number_of_visit_parameter { 1 }; - // FIXME: Optimize simulations enough for use. - static constexpr EvalMethod s_eval_method { EvalMethod::Heuristic }; - - Vector> m_children; - MCTSTree* m_parent { nullptr }; - int m_white_points { 0 }; - int m_simulations { 0 }; - OwnPtr m_board; - Optional m_last_move; - Chess::Color m_turn : 2; - bool m_moves_generated : 1 { false }; -}; diff --git a/Userland/Services/ChessEngine/main.cpp b/Userland/Services/ChessEngine/main.cpp deleted file mode 100644 index c12fce6efda..00000000000 --- a/Userland/Services/ChessEngine/main.cpp +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright (c) 2020, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "ChessEngine.h" -#include -#include -#include -#include - -ErrorOr serenity_main(Main::Arguments) -{ - TRY(Core::System::pledge("stdio recvfd sendfd unix")); - Core::EventLoop loop; - TRY(Core::System::unveil(nullptr, nullptr)); - - auto infile = TRY(Core::File::standard_input()); - TRY(infile->set_blocking(false)); - auto outfile = TRY(Core::File::standard_output()); - TRY(outfile->set_blocking(false)); - auto engine = TRY(ChessEngine::try_create(move(infile), move(outfile))); - engine->on_quit = [&](auto status_code) { - loop.quit(status_code); - }; - - return loop.exec(); -} diff --git a/Userland/Services/Clipboard/CMakeLists.txt b/Userland/Services/Clipboard/CMakeLists.txt deleted file mode 100644 index 84dd2cb5e8c..00000000000 --- a/Userland/Services/Clipboard/CMakeLists.txt +++ /dev/null @@ -1,22 +0,0 @@ -serenity_component( - Clipboard - REQUIRED - TARGETS Clipboard -) - -compile_ipc(ClipboardServer.ipc ClipboardServerEndpoint.h) -compile_ipc(ClipboardClient.ipc ClipboardClientEndpoint.h) - -set(SOURCES - ConnectionFromClient.cpp - Storage.cpp - main.cpp -) - -set(GENERATED_SOURCES - ClipboardClientEndpoint.h - ClipboardServerEndpoint.h -) - -serenity_bin(Clipboard) -target_link_libraries(Clipboard PRIVATE LibCore LibIPC LibMain) diff --git a/Userland/Services/Clipboard/ClipboardClient.ipc b/Userland/Services/Clipboard/ClipboardClient.ipc deleted file mode 100644 index 34a446dc8ab..00000000000 --- a/Userland/Services/Clipboard/ClipboardClient.ipc +++ /dev/null @@ -1,6 +0,0 @@ -#include - -endpoint ClipboardClient -{ - clipboard_data_changed([UTF8] ByteString mime_type) =| -} diff --git a/Userland/Services/Clipboard/ClipboardServer.ipc b/Userland/Services/Clipboard/ClipboardServer.ipc deleted file mode 100644 index f5ce4673368..00000000000 --- a/Userland/Services/Clipboard/ClipboardServer.ipc +++ /dev/null @@ -1,7 +0,0 @@ -#include - -endpoint ClipboardServer -{ - get_clipboard_data() => (Core::AnonymousBuffer data, [UTF8] ByteString mime_type, HashMap metadata) - set_clipboard_data(Core::AnonymousBuffer data, [UTF8] ByteString mime_type, HashMap metadata) =| -} diff --git a/Userland/Services/Clipboard/ConnectionFromClient.cpp b/Userland/Services/Clipboard/ConnectionFromClient.cpp deleted file mode 100644 index 978993ed264..00000000000 --- a/Userland/Services/Clipboard/ConnectionFromClient.cpp +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright (c) 2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include - -namespace Clipboard { - -static HashMap> s_connections; - -void ConnectionFromClient::for_each_client(Function callback) -{ - for (auto& it : s_connections) { - callback(*it.value); - } -} - -ConnectionFromClient::ConnectionFromClient(NonnullOwnPtr socket, int client_id) - : IPC::ConnectionFromClient(*this, move(socket), client_id) -{ - s_connections.set(client_id, *this); -} - -void ConnectionFromClient::die() -{ - s_connections.remove(client_id()); -} - -void ConnectionFromClient::set_clipboard_data(Core::AnonymousBuffer const& data, ByteString const& mime_type, HashMap const& metadata) -{ - Storage::the().set_data(data, mime_type, metadata); -} - -Messages::ClipboardServer::GetClipboardDataResponse ConnectionFromClient::get_clipboard_data() -{ - auto& storage = Storage::the(); - return { storage.buffer(), storage.mime_type(), storage.metadata().clone().release_value_but_fixme_should_propagate_errors() }; -} - -void ConnectionFromClient::notify_about_clipboard_change() -{ - async_clipboard_data_changed(Storage::the().mime_type()); -} - -} diff --git a/Userland/Services/Clipboard/ConnectionFromClient.h b/Userland/Services/Clipboard/ConnectionFromClient.h deleted file mode 100644 index bfee4054811..00000000000 --- a/Userland/Services/Clipboard/ConnectionFromClient.h +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include - -namespace Clipboard { - -class ConnectionFromClient final - : public IPC::ConnectionFromClient { - C_OBJECT(ConnectionFromClient); - -public: - virtual ~ConnectionFromClient() override = default; - - virtual void die() override; - - static void for_each_client(Function); - - void notify_about_clipboard_change(); - -private: - explicit ConnectionFromClient(NonnullOwnPtr, int client_id); - - virtual Messages::ClipboardServer::GetClipboardDataResponse get_clipboard_data() override; - virtual void set_clipboard_data(Core::AnonymousBuffer const&, ByteString const&, HashMap const&) override; -}; - -} diff --git a/Userland/Services/Clipboard/Storage.cpp b/Userland/Services/Clipboard/Storage.cpp deleted file mode 100644 index b1c3e7b3bc4..00000000000 --- a/Userland/Services/Clipboard/Storage.cpp +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright (c) 2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include - -namespace Clipboard { - -Storage& Storage::the() -{ - static Storage s_the; - return s_the; -} - -void Storage::set_data(Core::AnonymousBuffer data, ByteString const& mime_type, HashMap const& metadata) -{ - m_buffer = move(data); - m_data_size = data.size(); - m_mime_type = mime_type; - m_metadata = metadata; - - if (on_content_change) - on_content_change(); -} - -} diff --git a/Userland/Services/Clipboard/Storage.h b/Userland/Services/Clipboard/Storage.h deleted file mode 100644 index 6908833577e..00000000000 --- a/Userland/Services/Clipboard/Storage.h +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright (c) 2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include - -namespace Clipboard { - -class Storage { -public: - static Storage& the(); - ~Storage() = default; - - bool has_data() const { return m_buffer.is_valid(); } - - ByteString const& mime_type() const { return m_mime_type; } - HashMap const& metadata() const { return m_metadata; } - - u8 const* data() const - { - if (!has_data()) - return nullptr; - return m_buffer.data(); - } - - size_t data_size() const - { - if (has_data()) - return m_data_size; - return 0; - } - - void set_data(Core::AnonymousBuffer, ByteString const& mime_type, HashMap const& metadata); - - Function on_content_change; - - Core::AnonymousBuffer const& buffer() const { return m_buffer; } - -private: - Storage() = default; - - ByteString m_mime_type; - Core::AnonymousBuffer m_buffer; - size_t m_data_size { 0 }; - HashMap m_metadata; -}; - -} diff --git a/Userland/Services/Clipboard/main.cpp b/Userland/Services/Clipboard/main.cpp deleted file mode 100644 index 62f60aee927..00000000000 --- a/Userland/Services/Clipboard/main.cpp +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright (c) 2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include -#include - -ErrorOr serenity_main(Main::Arguments) -{ - TRY(Core::System::pledge("stdio recvfd sendfd accept")); - Core::EventLoop event_loop; - TRY(Core::System::unveil(nullptr, nullptr)); - - auto server = TRY(IPC::MultiServer::try_create()); - - Clipboard::Storage::the().on_content_change = [&] { - Clipboard::ConnectionFromClient::for_each_client([&](auto& client) { - client.notify_about_clipboard_change(); - }); - }; - - return event_loop.exec(); -} diff --git a/Userland/Services/ConfigServer/CMakeLists.txt b/Userland/Services/ConfigServer/CMakeLists.txt deleted file mode 100644 index f62f2eabc5f..00000000000 --- a/Userland/Services/ConfigServer/CMakeLists.txt +++ /dev/null @@ -1,21 +0,0 @@ -serenity_component( - ConfigServer - REQUIRED - TARGETS ConfigServer -) - -compile_ipc(ConfigServer.ipc ConfigServerEndpoint.h) -compile_ipc(ConfigClient.ipc ConfigClientEndpoint.h) - -set(SOURCES - ConnectionFromClient.cpp - main.cpp -) - -set(GENERATED_SOURCES - ConfigServerEndpoint.h - ConfigClientEndpoint.h -) - -serenity_bin(ConfigServer) -target_link_libraries(ConfigServer PRIVATE LibCore LibIPC LibMain) diff --git a/Userland/Services/ConfigServer/ConfigClient.ipc b/Userland/Services/ConfigServer/ConfigClient.ipc deleted file mode 100644 index f228a47aed1..00000000000 --- a/Userland/Services/ConfigServer/ConfigClient.ipc +++ /dev/null @@ -1,10 +0,0 @@ -endpoint ConfigClient -{ - notify_changed_string_value(ByteString domain, ByteString group, ByteString key, ByteString value) =| - notify_changed_i32_value(ByteString domain, ByteString group, ByteString key, i32 value) =| - notify_changed_u32_value(ByteString domain, ByteString group, ByteString key, u32 value) =| - notify_changed_bool_value(ByteString domain, ByteString group, ByteString key, bool value) =| - notify_removed_key(ByteString domain, ByteString group, ByteString key) =| - notify_removed_group(ByteString domain, ByteString group) =| - notify_added_group(ByteString domain, ByteString group) =| -} diff --git a/Userland/Services/ConfigServer/ConfigServer.ipc b/Userland/Services/ConfigServer/ConfigServer.ipc deleted file mode 100644 index 4fc2b03b777..00000000000 --- a/Userland/Services/ConfigServer/ConfigServer.ipc +++ /dev/null @@ -1,23 +0,0 @@ -endpoint ConfigServer -{ - enable_permissive_mode() =| - pledge_domains(Vector domains) =| - - monitor_domain(ByteString domain) =| - - list_config_groups(ByteString domain) => (Vector groups) - list_config_keys(ByteString domain, ByteString group) => (Vector keys) - - read_string_value(ByteString domain, ByteString group, ByteString key) => (Optional value) - read_i32_value(ByteString domain, ByteString group, ByteString key) => (Optional value) - read_u32_value(ByteString domain, ByteString group, ByteString key) => (Optional value) - read_bool_value(ByteString domain, ByteString group, ByteString key) => (Optional value) - - write_string_value(ByteString domain, ByteString group, ByteString key, ByteString value) => () - write_i32_value(ByteString domain, ByteString group, ByteString key, i32 value) => () - write_u32_value(ByteString domain, ByteString group, ByteString key, u32 value) => () - write_bool_value(ByteString domain, ByteString group, ByteString key, bool value) => () - remove_key_entry(ByteString domain, ByteString group, ByteString key) => () - remove_group_entry(ByteString domain, ByteString group) => () - add_group_entry(ByteString domain, ByteString group) => () -} diff --git a/Userland/Services/ConfigServer/ConnectionFromClient.cpp b/Userland/Services/ConfigServer/ConnectionFromClient.cpp deleted file mode 100644 index b41ffdcc25f..00000000000 --- a/Userland/Services/ConfigServer/ConnectionFromClient.cpp +++ /dev/null @@ -1,359 +0,0 @@ -/* - * Copyright (c) 2021, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "ConnectionFromClient.h" -#include -#include -#include -#include - -namespace ConfigServer { - -static HashMap> s_connections; - -struct CachedDomain { - ByteString domain; - NonnullRefPtr config; - RefPtr watcher; -}; - -static HashMap> s_cache; -static constexpr int s_disk_sync_delay_ms = 5'000; - -static void for_each_monitoring_connection(ByteString const& domain, ConnectionFromClient* excluded_connection, Function callback) -{ - for (auto& it : s_connections) { - if (it.value->is_monitoring_domain(domain) && (!excluded_connection || it.value != excluded_connection)) - callback(*it.value); - } -} - -static Core::ConfigFile& ensure_domain_config(ByteString const& domain) -{ - auto it = s_cache.find(domain); - if (it != s_cache.end()) - return *it->value->config; - - auto config = Core::ConfigFile::open_for_app(domain, Core::ConfigFile::AllowWriting::Yes).release_value_but_fixme_should_propagate_errors(); - // FIXME: Use a single FileWatcher with multiple watches inside. - auto watcher_or_error = Core::FileWatcher::create(Core::FileWatcherFlags::Nonblock); - VERIFY(!watcher_or_error.is_error()); - auto result = watcher_or_error.value()->add_watch(config->filename(), Core::FileWatcherEvent::Type::ContentModified); - VERIFY(!result.is_error()); - watcher_or_error.value()->on_change = [config, domain](auto&) { - auto new_config = Core::ConfigFile::open(config->filename(), Core::ConfigFile::AllowWriting::Yes).release_value_but_fixme_should_propagate_errors(); - for (auto& group : config->groups()) { - for (auto& key : config->keys(group)) { - if (!new_config->has_key(group, key)) { - for_each_monitoring_connection(domain, nullptr, [&domain, &group, &key](ConnectionFromClient& connection) { - connection.async_notify_removed_key(domain, group, key); - }); - } - } - } - // FIXME: Detect type of keys. - for (auto& group : new_config->groups()) { - for (auto& key : new_config->keys(group)) { - auto old_value = config->read_entry(group, key); - auto new_value = new_config->read_entry(group, key); - if (old_value != new_value) { - for_each_monitoring_connection(domain, nullptr, [&domain, &group, &key, &new_value](ConnectionFromClient& connection) { - connection.async_notify_changed_string_value(domain, group, key, new_value); - }); - } - } - } - // FIXME: Refactor this whole thing so that we don't need a cache lookup here. - s_cache.get(domain).value()->config = new_config; - }; - auto cache_entry = make(domain, config, watcher_or_error.release_value()); - s_cache.set(domain, move(cache_entry)); - return *config; -} - -ConnectionFromClient::ConnectionFromClient(NonnullOwnPtr client_socket, int client_id) - : IPC::ConnectionFromClient(*this, move(client_socket), client_id) - , m_sync_timer(Core::Timer::create_single_shot(s_disk_sync_delay_ms, [this]() { sync_dirty_domains_to_disk(); })) -{ - s_connections.set(client_id, *this); -} - -void ConnectionFromClient::die() -{ - s_connections.remove(client_id()); - m_sync_timer->stop(); - sync_dirty_domains_to_disk(); -} - -void ConnectionFromClient::pledge_domains(Vector const& domains) -{ - if (m_has_pledged) { - did_misbehave("Tried to pledge domains twice."); - return; - } - m_has_pledged = true; - for (auto& domain : domains) - m_pledged_domains.set(domain); -} - -void ConnectionFromClient::enable_permissive_mode() -{ - if (m_has_pledged) { - did_misbehave("Tried to enable permissive mode after pledging."); - return; - } - m_permissive_mode = true; -} - -void ConnectionFromClient::monitor_domain(ByteString const& domain) -{ - if (m_has_pledged && !m_pledged_domains.contains(domain)) { - if (!m_permissive_mode) - did_misbehave("Attempt to monitor non-pledged domain"); - return; - } - - m_monitored_domains.set(domain); -} - -bool ConnectionFromClient::validate_access(ByteString const& domain, ByteString const& group, ByteString const& key) -{ - if (!m_has_pledged) - return true; - if (m_pledged_domains.contains(domain)) - return true; - if (!m_permissive_mode) - did_misbehave(ByteString::formatted("Blocked attempt to access domain '{}', group={}, key={}", domain, group, key).characters()); - return false; -} - -void ConnectionFromClient::sync_dirty_domains_to_disk() -{ - if (m_dirty_domains.is_empty()) - return; - auto dirty_domains = move(m_dirty_domains); - dbgln("Syncing {} dirty domains to disk", dirty_domains.size()); - for (auto domain : dirty_domains) { - auto& config = ensure_domain_config(domain); - if (auto result = config.sync(); result.is_error()) { - dbgln("Failed to write config '{}' to disk: {}", domain, result.error()); - // Put it back in the list since it's still dirty. - m_dirty_domains.set(domain); - } - } -} - -Messages::ConfigServer::ListConfigKeysResponse ConnectionFromClient::list_config_keys(ByteString const& domain, ByteString const& group) -{ - if (!validate_access(domain, group, "")) - return Vector {}; - auto& config = ensure_domain_config(domain); - return { config.keys(group) }; -} - -Messages::ConfigServer::ListConfigGroupsResponse ConnectionFromClient::list_config_groups(ByteString const& domain) -{ - if (!validate_access(domain, "", "")) - return Vector {}; - auto& config = ensure_domain_config(domain); - return { config.groups() }; -} - -Messages::ConfigServer::ReadStringValueResponse ConnectionFromClient::read_string_value(ByteString const& domain, ByteString const& group, ByteString const& key) -{ - if (!validate_access(domain, group, key)) { - if (m_permissive_mode) - return Optional {}; - return nullptr; - } - - auto& config = ensure_domain_config(domain); - if (!config.has_key(group, key)) - return Optional {}; - return Optional { config.read_entry(group, key) }; -} - -Messages::ConfigServer::ReadI32ValueResponse ConnectionFromClient::read_i32_value(ByteString const& domain, ByteString const& group, ByteString const& key) -{ - if (!validate_access(domain, group, key)) { - if (m_permissive_mode) - return Optional {}; - return nullptr; - } - - auto& config = ensure_domain_config(domain); - if (!config.has_key(group, key)) - return Optional {}; - return Optional { config.read_num_entry(group, key) }; -} - -Messages::ConfigServer::ReadU32ValueResponse ConnectionFromClient::read_u32_value(ByteString const& domain, ByteString const& group, ByteString const& key) -{ - if (!validate_access(domain, group, key)) { - if (m_permissive_mode) - return Optional {}; - return nullptr; - } - - auto& config = ensure_domain_config(domain); - if (!config.has_key(group, key)) - return Optional {}; - return Optional { config.read_num_entry(group, key) }; -} - -Messages::ConfigServer::ReadBoolValueResponse ConnectionFromClient::read_bool_value(ByteString const& domain, ByteString const& group, ByteString const& key) -{ - if (!validate_access(domain, group, key)) { - if (m_permissive_mode) - return Optional {}; - return nullptr; - } - - auto& config = ensure_domain_config(domain); - if (!config.has_key(group, key)) - return Optional {}; - return Optional { config.read_bool_entry(group, key) }; -} - -void ConnectionFromClient::start_or_restart_sync_timer() -{ - if (m_sync_timer->is_active()) - m_sync_timer->restart(); - else - m_sync_timer->start(); -} - -void ConnectionFromClient::write_string_value(ByteString const& domain, ByteString const& group, ByteString const& key, ByteString const& value) -{ - if (!validate_access(domain, group, key)) - return; - - auto& config = ensure_domain_config(domain); - - if (config.has_key(group, key) && config.read_entry(group, key) == value) - return; - - config.write_entry(group, key, value); - m_dirty_domains.set(domain); - start_or_restart_sync_timer(); - - for_each_monitoring_connection(domain, this, [&domain, &group, &key, &value](ConnectionFromClient& connection) { - connection.async_notify_changed_string_value(domain, group, key, value); - }); -} - -void ConnectionFromClient::write_i32_value(ByteString const& domain, ByteString const& group, ByteString const& key, i32 value) -{ - if (!validate_access(domain, group, key)) - return; - - auto& config = ensure_domain_config(domain); - - if (config.has_key(group, key) && config.read_num_entry(group, key) == value) - return; - - config.write_num_entry(group, key, value); - m_dirty_domains.set(domain); - start_or_restart_sync_timer(); - - for_each_monitoring_connection(domain, this, [&domain, &group, &key, &value](ConnectionFromClient& connection) { - connection.async_notify_changed_i32_value(domain, group, key, value); - }); -} - -void ConnectionFromClient::write_u32_value(ByteString const& domain, ByteString const& group, ByteString const& key, u32 value) -{ - if (!validate_access(domain, group, key)) - return; - - auto& config = ensure_domain_config(domain); - - if (config.has_key(group, key) && config.read_num_entry(group, key) == value) - return; - - config.write_num_entry(group, key, value); - m_dirty_domains.set(domain); - start_or_restart_sync_timer(); - - for_each_monitoring_connection(domain, this, [&domain, &group, &key, &value](ConnectionFromClient& connection) { - connection.async_notify_changed_u32_value(domain, group, key, value); - }); -} - -void ConnectionFromClient::write_bool_value(ByteString const& domain, ByteString const& group, ByteString const& key, bool value) -{ - if (!validate_access(domain, group, key)) - return; - - auto& config = ensure_domain_config(domain); - - if (config.has_key(group, key) && config.read_bool_entry(group, key) == value) - return; - - config.write_bool_entry(group, key, value); - m_dirty_domains.set(domain); - start_or_restart_sync_timer(); - - for_each_monitoring_connection(domain, this, [&domain, &group, &key, &value](ConnectionFromClient& connection) { - connection.async_notify_changed_bool_value(domain, group, key, value); - }); -} - -void ConnectionFromClient::remove_key_entry(ByteString const& domain, ByteString const& group, ByteString const& key) -{ - if (!validate_access(domain, group, key)) - return; - - auto& config = ensure_domain_config(domain); - if (!config.has_key(group, key)) - return; - - config.remove_entry(group, key); - m_dirty_domains.set(domain); - start_or_restart_sync_timer(); - - for_each_monitoring_connection(domain, this, [&domain, &group, &key](ConnectionFromClient& connection) { - connection.async_notify_removed_key(domain, group, key); - }); -} - -void ConnectionFromClient::remove_group_entry(ByteString const& domain, ByteString const& group) -{ - if (!validate_access(domain, group, {})) - return; - - auto& config = ensure_domain_config(domain); - if (!config.has_group(group)) - return; - - config.remove_group(group); - m_dirty_domains.set(domain); - start_or_restart_sync_timer(); - - for_each_monitoring_connection(domain, this, [&domain, &group](ConnectionFromClient& connection) { - connection.async_notify_removed_group(domain, group); - }); -} - -void ConnectionFromClient::add_group_entry(ByteString const& domain, ByteString const& group) -{ - if (!validate_access(domain, group, {})) - return; - - auto& config = ensure_domain_config(domain); - if (config.has_group(group)) - return; - - config.add_group(group); - m_dirty_domains.set(domain); - start_or_restart_sync_timer(); - - for_each_monitoring_connection(domain, this, [&domain, &group](ConnectionFromClient& connection) { - connection.async_notify_added_group(domain, group); - }); -} - -} diff --git a/Userland/Services/ConfigServer/ConnectionFromClient.h b/Userland/Services/ConfigServer/ConnectionFromClient.h deleted file mode 100644 index d8cb97214c1..00000000000 --- a/Userland/Services/ConfigServer/ConnectionFromClient.h +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright (c) 2021, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include - -#include -#include - -namespace ConfigServer { - -class ConnectionFromClient final : public IPC::ConnectionFromClient { - C_OBJECT(ConnectionFromClient) -public: - ~ConnectionFromClient() override = default; - - virtual void die() override; - - bool is_monitoring_domain(ByteString const& domain) const { return m_monitored_domains.contains(domain); } - -private: - explicit ConnectionFromClient(NonnullOwnPtr, int client_id); - - virtual void pledge_domains(Vector const&) override; - virtual void enable_permissive_mode() override; - virtual void monitor_domain(ByteString const&) override; - virtual Messages::ConfigServer::ListConfigGroupsResponse list_config_groups([[maybe_unused]] ByteString const& domain) override; - virtual Messages::ConfigServer::ListConfigKeysResponse list_config_keys([[maybe_unused]] ByteString const& domain, [[maybe_unused]] ByteString const& group) override; - virtual Messages::ConfigServer::ReadStringValueResponse read_string_value([[maybe_unused]] ByteString const& domain, [[maybe_unused]] ByteString const& group, [[maybe_unused]] ByteString const& key) override; - virtual Messages::ConfigServer::ReadI32ValueResponse read_i32_value([[maybe_unused]] ByteString const& domain, [[maybe_unused]] ByteString const& group, [[maybe_unused]] ByteString const& key) override; - virtual Messages::ConfigServer::ReadU32ValueResponse read_u32_value([[maybe_unused]] ByteString const& domain, [[maybe_unused]] ByteString const& group, [[maybe_unused]] ByteString const& key) override; - virtual Messages::ConfigServer::ReadBoolValueResponse read_bool_value([[maybe_unused]] ByteString const& domain, [[maybe_unused]] ByteString const& group, [[maybe_unused]] ByteString const& key) override; - virtual void write_string_value([[maybe_unused]] ByteString const& domain, [[maybe_unused]] ByteString const& group, [[maybe_unused]] ByteString const& key, [[maybe_unused]] ByteString const& value) override; - virtual void write_i32_value([[maybe_unused]] ByteString const& domain, [[maybe_unused]] ByteString const& group, [[maybe_unused]] ByteString const& key, [[maybe_unused]] i32 value) override; - virtual void write_u32_value([[maybe_unused]] ByteString const& domain, [[maybe_unused]] ByteString const& group, [[maybe_unused]] ByteString const& key, [[maybe_unused]] u32 value) override; - virtual void write_bool_value([[maybe_unused]] ByteString const& domain, [[maybe_unused]] ByteString const& group, [[maybe_unused]] ByteString const& key, [[maybe_unused]] bool value) override; - virtual void remove_key_entry([[maybe_unused]] ByteString const& domain, [[maybe_unused]] ByteString const& group, [[maybe_unused]] ByteString const& key) override; - virtual void remove_group_entry([[maybe_unused]] ByteString const& domain, [[maybe_unused]] ByteString const& group) override; - virtual void add_group_entry([[maybe_unused]] ByteString const& domain, [[maybe_unused]] ByteString const& group) override; - - bool validate_access(ByteString const& domain, ByteString const& group, ByteString const& key); - void sync_dirty_domains_to_disk(); - void start_or_restart_sync_timer(); - - bool m_has_pledged { false }; - bool m_permissive_mode { false }; - HashTable m_pledged_domains; - - HashTable m_monitored_domains; - - NonnullRefPtr m_sync_timer; - HashTable m_dirty_domains; -}; - -} diff --git a/Userland/Services/ConfigServer/main.cpp b/Userland/Services/ConfigServer/main.cpp deleted file mode 100644 index 09184d5e3ea..00000000000 --- a/Userland/Services/ConfigServer/main.cpp +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright (c) 2021, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "ConnectionFromClient.h" -#include -#include -#include -#include - -ErrorOr serenity_main(Main::Arguments) -{ - TRY(Core::System::pledge("stdio accept rpath wpath cpath")); - TRY(Core::System::unveil(Core::StandardPaths::config_directory(), "rwc"sv)); - TRY(Core::System::unveil(Core::StandardPaths::home_directory(), "rwc"sv)); - TRY(Core::System::unveil(nullptr, nullptr)); - - Core::EventLoop event_loop; - - auto server = TRY(IPC::MultiServer::try_create()); - return event_loop.exec(); -} diff --git a/Userland/Services/CrashDaemon/CMakeLists.txt b/Userland/Services/CrashDaemon/CMakeLists.txt deleted file mode 100644 index f6eb4c1e2d5..00000000000 --- a/Userland/Services/CrashDaemon/CMakeLists.txt +++ /dev/null @@ -1,12 +0,0 @@ -serenity_component( - CrashDaemon - REQUIRED - TARGETS CrashDaemon -) - -set(SOURCES - main.cpp -) - -serenity_bin(CrashDaemon) -target_link_libraries(CrashDaemon PRIVATE LibCompress LibCore LibCoredump LibMain) diff --git a/Userland/Services/CrashDaemon/main.cpp b/Userland/Services/CrashDaemon/main.cpp deleted file mode 100644 index 497c08541d9..00000000000 --- a/Userland/Services/CrashDaemon/main.cpp +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Copyright (c) 2020, Itamar S. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -static void wait_until_coredump_is_ready(ByteString const& coredump_path) -{ - while (true) { - struct stat statbuf; - if (stat(coredump_path.characters(), &statbuf) < 0) { - perror("stat"); - VERIFY_NOT_REACHED(); - } - if (statbuf.st_mode & 0400) // Check if readable - break; - - usleep(10000); // sleep for 10ms - } -} - -static void launch_crash_reporter(ByteString const& coredump_path, bool unlink_on_exit) -{ - auto pid = Core::Process::spawn("/bin/CrashReporter"sv, - unlink_on_exit - ? Array { "--unlink", coredump_path.characters() }.span() - : Array { coredump_path.characters() }.span()); - if (pid.is_error()) - warnln("Failed to launch CrashReporter"); -} - -ErrorOr serenity_main(Main::Arguments) -{ - TRY(Core::System::pledge("stdio rpath wpath cpath proc exec")); - - Core::BlockingFileWatcher watcher; - TRY(watcher.add_watch("/tmp/coredump", Core::FileWatcherEvent::Type::ChildCreated)); - - while (true) { - auto event = watcher.wait_for_event(); - VERIFY(event.has_value()); - if (event.value().type != Core::FileWatcherEvent::Type::ChildCreated) - continue; - auto& coredump_path = event.value().event_path; - dbgln("New coredump file: {}", coredump_path); - wait_until_coredump_is_ready(coredump_path); - - auto file_or_error = Core::MappedFile::map(coredump_path); - if (file_or_error.is_error()) { - dbgln("Unable to map coredump {}: {}", coredump_path, file_or_error.error()); - continue; - } - - launch_crash_reporter(coredump_path, true); - } -} diff --git a/Userland/Services/DHCPClient/CMakeLists.txt b/Userland/Services/DHCPClient/CMakeLists.txt deleted file mode 100644 index 9d0f3519f9c..00000000000 --- a/Userland/Services/DHCPClient/CMakeLists.txt +++ /dev/null @@ -1,14 +0,0 @@ -serenity_component( - DHCPClient - REQUIRED - TARGETS DHCPClient -) - -set(SOURCES - DHCPv4Client.cpp - DHCPv4.cpp - main.cpp -) - -serenity_bin(DHCPClient) -target_link_libraries(DHCPClient PRIVATE LibCore LibMain) diff --git a/Userland/Services/DHCPClient/DHCPv4.cpp b/Userland/Services/DHCPClient/DHCPv4.cpp deleted file mode 100644 index 9abf5ed9a3e..00000000000 --- a/Userland/Services/DHCPClient/DHCPv4.cpp +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright (c) 2020, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "DHCPv4.h" -#include - -ParsedDHCPv4Options DHCPv4Packet::parse_options() const -{ - ParsedDHCPv4Options options; - for (size_t index = 4; index < DHCPV4_OPTION_FIELD_MAX_LENGTH; ++index) { - auto opt_name = *(DHCPOption const*)&m_options[index]; - switch (opt_name) { - case DHCPOption::Pad: - continue; - case DHCPOption::End: - goto escape; - default: - ++index; - auto length = m_options[index]; - if ((size_t)length > DHCPV4_OPTION_FIELD_MAX_LENGTH - index) { - dbgln("Bogus option length {} assuming forgotten END", length); - break; - } - dbgln_if(DHCPV4_DEBUG, "DHCP Option {} with length {}", (u8)opt_name, length); - ++index; - options.options.set(opt_name, { length, &m_options[index] }); - index += length - 1; - break; - } - } -escape:; - return options; -} diff --git a/Userland/Services/DHCPClient/DHCPv4.h b/Userland/Services/DHCPClient/DHCPv4.h deleted file mode 100644 index 082aa42af2e..00000000000 --- a/Userland/Services/DHCPClient/DHCPv4.h +++ /dev/null @@ -1,289 +0,0 @@ -/* - * Copyright (c) 2020, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -enum class DHCPv4Flags : u16 { - Broadcast = 1 << 15, - /* everything else is reserved and must be zero */ -}; - -enum class DHCPv4Op : u8 { - BootRequest = 1, - BootReply = 2 -}; - -enum class DHCPOption : u8 { - // BOOTP - Pad = 0, - SubnetMask, - TimeOffset, - Router, - TimeServer, - NameServer, - DomainNameServer, - LogServer, - CookieServer, - LPRServer, - ImpressServer, - ResourceLocationServer, - HostName, - BootFileSize, - MeritDumpFile, - DomainName, - SwapServer, - RootPath, - ExtensionsPath, - IPForwardingEnableDisable, - NonLocalSourceRoutingEnableDisable, - PolicyFilter, - MaximumDatagramReassemblySize, - DefaultIPTTL, - PathMTUAgingTimeout, - PathMTUPlateauTable, - InterfaceMTU, - AllSubnetsAreLocal, - BroadcastAddress, - PerformMaskDiscovery, - MaskSupplier, - PerformRouterDiscovery, - RouterSolicitationAddress, - StaticRoute, - TrailerEncapsulation, - ARPCacheTimeout, - EthernetEncapsulation, - TCPDefaultTTL, - TCPKeepaliveInterval, - TCPKeepaliveGarbage, - NetworkInformationServiceDomain, - NetworkInformationServers, - NetworkTimeProtocolServers, - VendorSpecificInformation, - NetBIOSOverTCPIPNameServer, - NetBIOSOverTCPIPDatagramDistributionServer, - NetBIOSOverTCPIPNodeType, - NetBIOSOverTCPIPScope, - XWindowSystemFontServer, // wow - XWindowSystemDisplayManager, - // DHCP - RequestedIPAddress = 50, - IPAddressLeaseTime, - OptionOverload, - DHCPMessageType, - ServerIdentifier, - ParameterRequestList, - Message, - MaximumDHCPMessageSize, - RenewalT1Time, - RenewalT2Time, - ClassIdentifier, - ClientIdentifier, - End = 255 -}; - -enum class DHCPMessageType : u8 { - DHCPDiscover = 1, - DHCPOffer, - DHCPRequest, - DHCPDecline, - DHCPAck, - DHCPNak, - DHCPRelease, -}; - -template<> -struct AK::Traits : public DefaultTraits { - static constexpr bool is_trivial() { return true; } - static unsigned hash(DHCPOption u) { return int_hash((u8)u); } -}; - -struct ParsedDHCPv4Options { - template - Optional get(DHCPOption option_name) const - requires(IsTriviallyCopyable) - { - auto option = options.get(option_name); - if (!option.has_value()) { - return {}; - } - auto& value = option.value(); - if (value.length != sizeof(T)) - return {}; - T t; - __builtin_memcpy(&t, value.value, value.length); - return t; - } - - template - Vector get_many(DHCPOption option_name, size_t max_number) const - { - Vector values; - - auto option = options.get(option_name); - if (!option.has_value()) { - return {}; - } - auto& value = option.value(); - if (value.length < sizeof(T)) - return {}; - - for (size_t i = 0; i < max_number; ++i) { - auto offset = i * sizeof(T); - if (offset >= value.length) - break; - values.append(*(T*)((u8*)const_cast(value.value) + offset)); - } - - return values; - } - - ByteString to_byte_string() const - { - StringBuilder builder; - builder.append("DHCP Options ("sv); - builder.appendff("{}", options.size()); - builder.append(" entries)\n"sv); - for (auto& opt : options) { - builder.appendff("\toption {} ({} bytes):", (u8)opt.key, (u8)opt.value.length); - for (auto i = 0; i < opt.value.length; ++i) - builder.appendff(" {} ", ((u8 const*)opt.value.value)[i]); - builder.append('\n'); - } - return builder.to_byte_string(); - } - - struct DHCPOptionValue { - u8 length; - void const* value; - }; - - HashMap options; -}; - -constexpr auto DHCPV4_OPTION_FIELD_MAX_LENGTH = 312; - -class [[gnu::packed]] DHCPv4Packet { -public: - u8 op() const { return m_op; } - void set_op(DHCPv4Op op) { m_op = (u8)op; } - - u8 htype() const { return m_htype; } - void set_htype(u8 htype) { m_htype = htype; } - - u8 hlen() const { return m_hlen; } - void set_hlen(u8 hlen) { m_hlen = hlen; } - - u8 hops() const { return m_hops; } - void set_hops(u8 hops) { m_hops = hops; } - - u32 xid() const { return m_xid; } - void set_xid(u32 xid) { m_xid = xid; } - - u16 secs() const { return m_secs; } - void set_secs(u16 secs) { m_secs = secs; } - - u16 flags() const { return m_flags; } - void set_flags(DHCPv4Flags flags) { m_flags = (u16)flags; } - - IPv4Address const& ciaddr() const { return m_ciaddr; } - IPv4Address const& yiaddr() const { return m_yiaddr; } - IPv4Address const& siaddr() const { return m_siaddr; } - IPv4Address const& giaddr() const { return m_giaddr; } - - IPv4Address& ciaddr() { return m_ciaddr; } - IPv4Address& yiaddr() { return m_yiaddr; } - IPv4Address& siaddr() { return m_siaddr; } - IPv4Address& giaddr() { return m_giaddr; } - - u8* options() { return m_options; } - ParsedDHCPv4Options parse_options() const; - - MACAddress const& chaddr() const { return *(MACAddress const*)&m_chaddr[0]; } - void set_chaddr(MACAddress const& mac) { *(MACAddress*)&m_chaddr[0] = mac; } - - StringView sname() const - { - char const* sname_ptr = reinterpret_cast(&m_sname[0]); - return { sname_ptr, strlen(sname_ptr) }; - } - - StringView file() const - { - char const* file_ptr = reinterpret_cast(&m_file[0]); - return { file_ptr, strlen(file_ptr) }; - } - -private: - NetworkOrdered m_op; - NetworkOrdered m_htype; - NetworkOrdered m_hlen; - NetworkOrdered m_hops; - NetworkOrdered m_xid; - NetworkOrdered m_secs; - NetworkOrdered m_flags; - IPv4Address m_ciaddr; - IPv4Address m_yiaddr; - IPv4Address m_siaddr; - IPv4Address m_giaddr; - u8 m_chaddr[16]; // 10 bytes of padding at the end - u8 m_sname[64] { 0 }; - u8 m_file[128] { 0 }; - u8 m_options[DHCPV4_OPTION_FIELD_MAX_LENGTH] { 0 }; // variable, less than 312 bytes -}; - -class DHCPv4PacketBuilder { -public: - DHCPv4PacketBuilder() - { - auto* options = m_packet.options(); - // set the magic DHCP cookie value - options[0] = 99; - options[1] = 130; - options[2] = 83; - options[3] = 99; - } - - void add_option(DHCPOption option, u8 length, void const* data) - { - VERIFY(m_can_add); - // we need enough space to fit the option value, its length, and its data - VERIFY(next_option_offset + length + 2 < DHCPV4_OPTION_FIELD_MAX_LENGTH); - - auto* options = m_packet.options(); - options[next_option_offset++] = (u8)option; - memcpy(options + next_option_offset, &length, 1); - next_option_offset++; - if (data && length) - memcpy(options + next_option_offset, data, length); - next_option_offset += length; - } - - void set_message_type(DHCPMessageType type) { add_option(DHCPOption::DHCPMessageType, 1, &type); } - - DHCPv4Packet& peek() { return m_packet; } - DHCPv4Packet& build() - { - add_option(DHCPOption::End, 0, nullptr); - m_can_add = false; - return m_packet; - } - -private: - DHCPv4Packet m_packet; - size_t next_option_offset { 4 }; - bool m_can_add { true }; -}; diff --git a/Userland/Services/DHCPClient/DHCPv4Client.cpp b/Userland/Services/DHCPClient/DHCPv4Client.cpp deleted file mode 100644 index 94072b5e89b..00000000000 --- a/Userland/Services/DHCPClient/DHCPv4Client.cpp +++ /dev/null @@ -1,402 +0,0 @@ -/* - * Copyright (c) 2020-2022, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "DHCPv4Client.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -static u8 mac_part(Vector const& parts, size_t index) -{ - auto result = AK::StringUtils::convert_to_uint_from_hex(parts.at(index)); - VERIFY(result.has_value()); - return result.value(); -} - -static MACAddress mac_from_string(ByteString const& str) -{ - auto chunks = str.split(':'); - VERIFY(chunks.size() == 6); // should we...worry about this? - return { - mac_part(chunks, 0), mac_part(chunks, 1), mac_part(chunks, 2), - mac_part(chunks, 3), mac_part(chunks, 4), mac_part(chunks, 5) - }; -} - -static bool send(InterfaceDescriptor const& iface, DHCPv4Packet const& packet, Core::EventReceiver*) -{ - int fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); - if (fd < 0) { - dbgln("ERROR: socket :: {}", strerror(errno)); - return false; - } - - ScopeGuard socket_close_guard = [&] { close(fd); }; - - if (setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, iface.ifname.characters(), IFNAMSIZ) < 0) { - dbgln("ERROR: setsockopt(SO_BINDTODEVICE) :: {}", strerror(errno)); - return false; - } - int allow_broadcast = 1; - if (setsockopt(fd, SOL_SOCKET, SO_BROADCAST, &allow_broadcast, sizeof(int)) < 0) { - dbgln("ERROR: setsockopt(SO_BROADCAST) :: {}", strerror(errno)); - return false; - } - - sockaddr_in dst; - memset(&dst, 0, sizeof(dst)); - dst.sin_family = AF_INET; - dst.sin_port = htons(67); - dst.sin_addr.s_addr = IPv4Address { 255, 255, 255, 255 }.to_u32(); - memset(&dst.sin_zero, 0, sizeof(dst.sin_zero)); - - dbgln_if(DHCPV4CLIENT_DEBUG, "sendto({} bound to {}, ..., {} at {}) = ...?", fd, iface.ifname, dst.sin_addr.s_addr, dst.sin_port); - auto rc = sendto(fd, &packet, sizeof(packet), 0, (sockaddr*)&dst, sizeof(dst)); - dbgln_if(DHCPV4CLIENT_DEBUG, "sendto({}) = {}", fd, rc); - if (rc < 0) { - dbgln("sendto failed with {}", strerror(errno)); - return false; - } - - return true; -} - -static void set_params(InterfaceDescriptor const& iface, IPv4Address const& ipv4_addr, IPv4Address const& netmask, Optional const& gateway) -{ - int fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP); - if (fd < 0) { - dbgln("ERROR: socket :: {}", strerror(errno)); - return; - } - - struct ifreq ifr; - memset(&ifr, 0, sizeof(ifr)); - - bool fits = iface.ifname.copy_characters_to_buffer(ifr.ifr_name, IFNAMSIZ); - if (!fits) { - dbgln("Interface name doesn't fit into IFNAMSIZ!"); - return; - } - - // set the IP address - ifr.ifr_addr.sa_family = AF_INET; - ((sockaddr_in&)ifr.ifr_addr).sin_addr.s_addr = ipv4_addr.to_in_addr_t(); - - if (ioctl(fd, SIOCSIFADDR, &ifr) < 0) { - dbgln("ERROR: ioctl(SIOCSIFADDR) :: {}", strerror(errno)); - } - - // set the network mask - ((sockaddr_in&)ifr.ifr_netmask).sin_addr.s_addr = netmask.to_in_addr_t(); - - if (ioctl(fd, SIOCSIFNETMASK, &ifr) < 0) { - dbgln("ERROR: ioctl(SIOCSIFNETMASK) :: {}", strerror(errno)); - } - - if (!gateway.has_value()) - return; - - // set the default gateway - struct rtentry rt; - memset(&rt, 0, sizeof(rt)); - - rt.rt_dev = const_cast(iface.ifname.characters()); - rt.rt_gateway.sa_family = AF_INET; - ((sockaddr_in&)rt.rt_gateway).sin_addr.s_addr = gateway.value().to_in_addr_t(); - rt.rt_flags = RTF_UP | RTF_GATEWAY; - - if (ioctl(fd, SIOCADDRT, &rt) < 0) { - dbgln("Error: ioctl(SIOCADDRT) :: {}", strerror(errno)); - } -} - -DHCPv4Client::DHCPv4Client(Vector interfaces_with_dhcp_enabled) - : m_interfaces_with_dhcp_enabled(move(interfaces_with_dhcp_enabled)) -{ - m_server = Core::UDPServer::construct(this); - m_server->on_ready_to_receive = [this] { - // TODO: we need to handle possible errors here somehow - auto buffer = MUST(m_server->receive(sizeof(DHCPv4Packet))); - dbgln_if(DHCPV4CLIENT_DEBUG, "Received {} bytes", buffer.size()); - if (buffer.size() < sizeof(DHCPv4Packet) - DHCPV4_OPTION_FIELD_MAX_LENGTH + 1 || buffer.size() > sizeof(DHCPv4Packet)) { - dbgln("we expected {}-{} bytes, this is a bad packet", sizeof(DHCPv4Packet) - DHCPV4_OPTION_FIELD_MAX_LENGTH + 1, sizeof(DHCPv4Packet)); - return; - } - auto& packet = *(DHCPv4Packet*)buffer.data(); - process_incoming(packet); - }; - - if (!m_server->bind({}, 68)) { - dbgln("The server we just created somehow came already bound, refusing to continue"); - VERIFY_NOT_REACHED(); - } - - m_check_timer = Core::Timer::create_repeating( - 1000, [this] { try_discover_ifs(); }, this); - - m_check_timer->start(); - - try_discover_ifs(); -} - -void DHCPv4Client::try_discover_ifs() -{ - auto ifs_result = get_discoverable_interfaces(); - if (ifs_result.is_error()) - return; - - dbgln_if(DHCPV4CLIENT_DEBUG, "Interfaces with DHCP enabled: {}", m_interfaces_with_dhcp_enabled); - bool sent_discover_request = false; - Interfaces& ifs = ifs_result.value(); - for (auto& iface : ifs.ready) { - dbgln_if(DHCPV4CLIENT_DEBUG, "Checking interface {} / {}", iface.ifname, iface.current_ip_address); - if (!m_interfaces_with_dhcp_enabled.contains_slow(iface.ifname)) - continue; - if (iface.current_ip_address != IPv4Address { 0, 0, 0, 0 }) - continue; - - dhcp_discover(iface); - sent_discover_request = true; - } - - if (sent_discover_request) { - auto current_interval = m_check_timer->interval(); - if (current_interval < m_max_timer_backoff_interval) - current_interval *= 1.9f; - m_check_timer->set_interval(current_interval); - } else { - m_check_timer->set_interval(1000); - } -} - -ErrorOr DHCPv4Client::get_discoverable_interfaces() -{ - auto file = TRY(Core::File::open("/sys/kernel/net/adapters"sv, Core::File::OpenMode::Read)); - - auto file_contents = TRY(file->read_until_eof()); - auto json = JsonValue::from_string(file_contents); - - if (json.is_error() || !json.value().is_array()) { - dbgln("Error: No network adapters available"); - return Error::from_string_literal("No network adapters available"); - } - - Vector ifnames_to_immediately_discover, ifnames_to_attempt_later; - json.value().as_array().for_each([&ifnames_to_immediately_discover, &ifnames_to_attempt_later](auto& value) { - auto if_object = value.as_object(); - - if (if_object.get_byte_string("class_name"sv).value_or({}) == "LoopbackAdapter") - return; - - auto name = if_object.get_byte_string("name"sv).value_or({}); - auto mac = if_object.get_byte_string("mac_address"sv).value_or({}); - auto is_up = if_object.get_bool("link_up"sv).value_or(false); - auto ipv4_addr_maybe = IPv4Address::from_string(if_object.get_byte_string("ipv4_address"sv).value_or({})); - auto ipv4_addr = ipv4_addr_maybe.has_value() ? ipv4_addr_maybe.value() : IPv4Address { 0, 0, 0, 0 }; - if (is_up) { - dbgln_if(DHCPV4_DEBUG, "Found adapter '{}' with mac {}, and it was up!", name, mac); - ifnames_to_immediately_discover.empend(name, mac_from_string(mac), ipv4_addr); - } else { - dbgln_if(DHCPV4_DEBUG, "Found adapter '{}' with mac {}, but it was down", name, mac); - ifnames_to_attempt_later.empend(name, mac_from_string(mac), ipv4_addr); - } - }); - - return Interfaces { - move(ifnames_to_immediately_discover), - move(ifnames_to_attempt_later) - }; -} - -void DHCPv4Client::handle_offer(DHCPv4Packet const& packet, ParsedDHCPv4Options const& options) -{ - dbgln("We were offered {} for {}", packet.yiaddr().to_byte_string(), options.get(DHCPOption::IPAddressLeaseTime).value_or(0)); - auto* transaction = const_cast(m_ongoing_transactions.get(packet.xid()).value_or(nullptr)); - if (!transaction) { - dbgln("we're not looking for {}", packet.xid()); - return; - } - if (transaction->has_ip) - return; - if (transaction->accepted_offer) { - // we've accepted someone's offer, but they haven't given us an ack - // TODO: maybe record this offer? - return; - } - // TAKE IT... - transaction->offered_lease_time = options.get(DHCPOption::IPAddressLeaseTime).value(); - dhcp_request(*transaction, packet); -} - -void DHCPv4Client::handle_ack(DHCPv4Packet const& packet, ParsedDHCPv4Options const& options) -{ - if constexpr (DHCPV4CLIENT_DEBUG) { - dbgln("The DHCP server handed us {}", packet.yiaddr().to_byte_string()); - dbgln("Here are the options: {}", options.to_byte_string()); - } - - auto* transaction = const_cast(m_ongoing_transactions.get(packet.xid()).value_or(nullptr)); - if (!transaction) { - dbgln("we're not looking for {}", packet.xid()); - return; - } - transaction->has_ip = true; - auto& interface = transaction->interface; - auto new_ip = packet.yiaddr(); - interface.current_ip_address = new_ip; - auto lease_time = AK::convert_between_host_and_network_endian(options.get(DHCPOption::IPAddressLeaseTime).value_or(transaction->offered_lease_time)); - // set a timer for the duration of the lease, we shall renew if needed - (void)Core::Timer::create_single_shot( - lease_time * 1000, - [this, transaction, interface = InterfaceDescriptor { interface }] { - transaction->accepted_offer = false; - transaction->has_ip = false; - dhcp_discover(interface); - }, - this); - - Optional gateway; - if (auto routers = options.get_many(DHCPOption::Router, 1); !routers.is_empty()) - gateway = routers.first(); - - set_params(transaction->interface, new_ip, options.get(DHCPOption::SubnetMask).value(), gateway); -} - -void DHCPv4Client::handle_nak(DHCPv4Packet const& packet, ParsedDHCPv4Options const& options) -{ - dbgln("The DHCP server told us to go chase our own tail about {}", packet.yiaddr().to_byte_string()); - dbgln("Here are the options: {}", options.to_byte_string()); - // make another request a bit later :shrug: - auto* transaction = const_cast(m_ongoing_transactions.get(packet.xid()).value_or(nullptr)); - if (!transaction) { - dbgln("we're not looking for {}", packet.xid()); - return; - } - transaction->accepted_offer = false; - transaction->has_ip = false; - auto& iface = transaction->interface; - (void)Core::Timer::create_single_shot( - 10000, - [this, iface = InterfaceDescriptor { iface }] { - dhcp_discover(iface); - }, - this); -} - -void DHCPv4Client::process_incoming(DHCPv4Packet const& packet) -{ - auto options = packet.parse_options(); - - dbgln_if(DHCPV4CLIENT_DEBUG, "Here are the options: {}", options.to_byte_string()); - - auto value_or_error = options.get(DHCPOption::DHCPMessageType); - if (!value_or_error.has_value()) - return; - - auto value = value_or_error.value(); - switch (value) { - case DHCPMessageType::DHCPOffer: - handle_offer(packet, options); - break; - case DHCPMessageType::DHCPAck: - handle_ack(packet, options); - break; - case DHCPMessageType::DHCPNak: - handle_nak(packet, options); - break; - case DHCPMessageType::DHCPDiscover: - case DHCPMessageType::DHCPRequest: - case DHCPMessageType::DHCPRelease: - // These are not for us - // we're just getting them because there are other people on our subnet - // broadcasting stuff - break; - case DHCPMessageType::DHCPDecline: - default: - dbgln("I dunno what to do with this {}", (u8)value); - VERIFY_NOT_REACHED(); - break; - } -} - -void DHCPv4Client::dhcp_discover(InterfaceDescriptor const& iface) -{ - auto transaction_id = get_random(); - - if constexpr (DHCPV4CLIENT_DEBUG) { - dbgln("Trying to lease an IP for {} with ID {}", iface.ifname, transaction_id); - if (!iface.current_ip_address.is_zero()) - dbgln("going to request the server to hand us {}", iface.current_ip_address.to_byte_string()); - } - - DHCPv4PacketBuilder builder; - - DHCPv4Packet& packet = builder.peek(); - packet.set_op(DHCPv4Op::BootRequest); - packet.set_htype(1); // 10mb ethernet - packet.set_hlen(sizeof(MACAddress)); - packet.set_xid(transaction_id); - packet.set_flags(DHCPv4Flags::Broadcast); - packet.ciaddr() = iface.current_ip_address; - packet.set_chaddr(iface.mac_address); - packet.set_secs(65535); // we lie - - // set packet options - builder.set_message_type(DHCPMessageType::DHCPDiscover); - auto& dhcp_packet = builder.build(); - - // broadcast the discover request - if (!send(iface, dhcp_packet, this)) - return; - m_ongoing_transactions.set(transaction_id, make(iface)); -} - -void DHCPv4Client::dhcp_request(DHCPv4Transaction& transaction, DHCPv4Packet const& offer) -{ - auto& iface = transaction.interface; - dbgln("Leasing the IP {} for adapter {}", offer.yiaddr().to_byte_string(), iface.ifname); - DHCPv4PacketBuilder builder; - - DHCPv4Packet& packet = builder.peek(); - packet.set_op(DHCPv4Op::BootRequest); - packet.ciaddr() = iface.current_ip_address; - packet.set_htype(1); // 10mb ethernet - packet.set_hlen(sizeof(MACAddress)); - packet.set_xid(offer.xid()); - packet.set_flags(DHCPv4Flags::Broadcast); - packet.set_chaddr(iface.mac_address); - packet.set_secs(65535); // we lie - - // set packet options - builder.set_message_type(DHCPMessageType::DHCPRequest); - builder.add_option(DHCPOption::RequestedIPAddress, sizeof(IPv4Address), &offer.yiaddr()); - - auto maybe_dhcp_server_ip = offer.parse_options().get(DHCPOption::ServerIdentifier); - if (maybe_dhcp_server_ip.has_value()) - builder.add_option(DHCPOption::ServerIdentifier, sizeof(IPv4Address), &maybe_dhcp_server_ip.value()); - - AK::Array parameter_request_list = { - DHCPOption::SubnetMask, - DHCPOption::Router, - }; - builder.add_option(DHCPOption::ParameterRequestList, parameter_request_list.size(), ¶meter_request_list); - - auto& dhcp_packet = builder.build(); - - // broadcast the "request" request - if (!send(iface, dhcp_packet, this)) - return; - transaction.accepted_offer = true; -} diff --git a/Userland/Services/DHCPClient/DHCPv4Client.h b/Userland/Services/DHCPClient/DHCPv4Client.h deleted file mode 100644 index 03e91e1a85d..00000000000 --- a/Userland/Services/DHCPClient/DHCPv4Client.h +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright (c) 2020, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include "DHCPv4.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -struct InterfaceDescriptor { - ByteString ifname; - MACAddress mac_address; - IPv4Address current_ip_address; -}; - -struct DHCPv4Transaction { - DHCPv4Transaction(InterfaceDescriptor ifname) - : interface(ifname) - { - } - - InterfaceDescriptor interface; - bool accepted_offer { false }; - bool has_ip { false }; - u32 offered_lease_time { 0 }; -}; - -class DHCPv4Client final : public Core::EventReceiver { - C_OBJECT(DHCPv4Client) - -public: - void dhcp_discover(InterfaceDescriptor const& ifname); - void dhcp_request(DHCPv4Transaction& transaction, DHCPv4Packet const& packet); - - void process_incoming(DHCPv4Packet const& packet); - - bool id_is_registered(u32 id) { return m_ongoing_transactions.contains(id); } - - struct Interfaces { - Vector ready; - Vector not_ready; - }; - static ErrorOr get_discoverable_interfaces(); - -private: - explicit DHCPv4Client(Vector interfaces_with_dhcp_enabled); - - void try_discover_ifs(); - - Vector m_interfaces_with_dhcp_enabled; - HashMap> m_ongoing_transactions; - RefPtr m_server; - RefPtr m_check_timer; - int m_max_timer_backoff_interval { 600000 }; // 10 minutes - - void handle_offer(DHCPv4Packet const&, ParsedDHCPv4Options const&); - void handle_ack(DHCPv4Packet const&, ParsedDHCPv4Options const&); - void handle_nak(DHCPv4Packet const&, ParsedDHCPv4Options const&); -}; diff --git a/Userland/Services/DHCPClient/main.cpp b/Userland/Services/DHCPClient/main.cpp deleted file mode 100644 index fe980b92c0c..00000000000 --- a/Userland/Services/DHCPClient/main.cpp +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright (c) 2020, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "DHCPv4Client.h" -#include -#include -#include -#include - -ErrorOr serenity_main(Main::Arguments args) -{ - Vector interfaces; - - Core::ArgsParser parser; - parser.add_positional_argument(interfaces, "Interfaces to run DHCP server on", "interfaces"); - parser.parse(args); - - TRY(Core::System::pledge("stdio unix inet cpath rpath")); - Core::EventLoop event_loop; - - TRY(Core::System::unveil("/sys/kernel/net/", "r")); - TRY(Core::System::unveil(nullptr, nullptr)); - - auto client = TRY(DHCPv4Client::try_create(interfaces)); - - TRY(Core::System::pledge("stdio inet cpath rpath")); - return event_loop.exec(); -} diff --git a/Userland/Services/DeviceMapper/CMakeLists.txt b/Userland/Services/DeviceMapper/CMakeLists.txt deleted file mode 100644 index b35586bc274..00000000000 --- a/Userland/Services/DeviceMapper/CMakeLists.txt +++ /dev/null @@ -1,13 +0,0 @@ -serenity_component( - DeviceMapper - REQUIRED - TARGETS DeviceMapper -) - -set(SOURCES - main.cpp - DeviceEventLoop.cpp -) - -serenity_bin(DeviceMapper) -target_link_libraries(DeviceMapper PRIVATE LibCore LibFileSystem LibMain) diff --git a/Userland/Services/DeviceMapper/DeviceEventLoop.cpp b/Userland/Services/DeviceMapper/DeviceEventLoop.cpp deleted file mode 100644 index 580fdfecd20..00000000000 --- a/Userland/Services/DeviceMapper/DeviceEventLoop.cpp +++ /dev/null @@ -1,262 +0,0 @@ -/* - * Copyright (c) 2023, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "DeviceEventLoop.h" -#include -#include -#include -#include -#include -#include -#include - -namespace DeviceMapper { - -DeviceEventLoop::DeviceEventLoop(NonnullOwnPtr devctl_file) - : m_devctl_file(move(devctl_file)) -{ -} - -using MinorNumberAllocationType = DeviceEventLoop::MinorNumberAllocationType; - -static constexpr DeviceEventLoop::DeviceNodeMatch s_matchers[] = { - { "audio"sv, "audio"sv, "audio/%digit"sv, DeviceNodeFamily::Type::CharacterDevice, 116, MinorNumberAllocationType::SequentialUnlimited, 0, 0, 0220 }, - { {}, "render"sv, "gpu/render%digit"sv, DeviceNodeFamily::Type::CharacterDevice, 28, MinorNumberAllocationType::SequentialUnlimited, 0, 0, 0666 }, - { "window"sv, "gpu-connector"sv, "gpu/connector%digit"sv, DeviceNodeFamily::Type::CharacterDevice, 226, MinorNumberAllocationType::SequentialUnlimited, 0, 0, 0660 }, - { {}, "virtio-console"sv, "hvc0p%digit"sv, DeviceNodeFamily::Type::CharacterDevice, 229, MinorNumberAllocationType::SequentialUnlimited, 0, 0, 0666 }, - { "phys"sv, "hid-mouse"sv, "input/mouse/%digit"sv, DeviceNodeFamily::Type::CharacterDevice, 10, MinorNumberAllocationType::SequentialUnlimited, 0, 0, 0666 }, - { "phys"sv, "hid-keyboard"sv, "input/keyboard/%digit"sv, DeviceNodeFamily::Type::CharacterDevice, 85, MinorNumberAllocationType::SequentialUnlimited, 0, 0, 0666 }, - { {}, "storage"sv, "hd%letter"sv, DeviceNodeFamily::Type::BlockDevice, 3, MinorNumberAllocationType::SequentialUnlimited, 0, 0, 0600 }, - { "tty"sv, "console"sv, "tty%digit"sv, DeviceNodeFamily::Type::CharacterDevice, 4, MinorNumberAllocationType::SequentialLimited, 0, 63, 0620 }, - { "tty"sv, "console"sv, "ttyS%digit"sv, DeviceNodeFamily::Type::CharacterDevice, 4, MinorNumberAllocationType::SequentialLimited, 64, 127, 0620 }, -}; - -static bool is_in_minor_number_range(DeviceEventLoop::DeviceNodeMatch const& matcher, MinorNumber minor_number) -{ - if (matcher.minor_number_allocation_type == MinorNumberAllocationType::SequentialUnlimited) - return true; - - return matcher.minor_number_start <= minor_number && static_cast(matcher.minor_number_start.value() + matcher.minor_number_range_size) >= minor_number; -} - -static Optional device_node_family_to_match_type(DeviceNodeFamily::Type unix_device_type, MajorNumber major_number, MinorNumber minor_number) -{ - for (auto& matcher : s_matchers) { - if (matcher.major_number == major_number - && unix_device_type == matcher.unix_device_type - && is_in_minor_number_range(matcher, minor_number)) - return matcher; - } - return {}; -} - -static bool is_in_family_minor_number_range(DeviceNodeFamily const& family, MinorNumber minor_number) -{ - return family.base_minor_number() <= minor_number && static_cast(family.base_minor_number().value() + family.devices_symbol_suffix_allocation_map().size()) >= minor_number; -} - -Optional DeviceEventLoop::find_device_node_family(DeviceNodeFamily::Type unix_device_type, MajorNumber major_number, MinorNumber minor_number) const -{ - for (auto const& family : m_device_node_families) { - if (family->major_number() == major_number && family->type() == unix_device_type && is_in_family_minor_number_range(*family, minor_number)) - return *family.ptr(); - } - return {}; -} - -ErrorOr> DeviceEventLoop::find_or_register_new_device_node_family(DeviceNodeMatch const& match, DeviceNodeFamily::Type unix_device_type, MajorNumber major_number, MinorNumber minor_number) -{ - if (auto possible_family = find_device_node_family(unix_device_type, major_number, minor_number); possible_family.has_value()) - return possible_family.release_value(); - unsigned allocation_map_size = 1024; - if (match.minor_number_allocation_type == MinorNumberAllocationType::SequentialLimited) - allocation_map_size = match.minor_number_range_size; - auto bitmap = TRY(Bitmap::create(allocation_map_size, false)); - auto node = TRY(adopt_nonnull_ref_or_enomem(new (nothrow) DeviceNodeFamily(move(bitmap), - match.family_type_literal, - unix_device_type, - major_number, - minor_number))); - TRY(m_device_node_families.try_append(node)); - - return node; -} - -static ErrorOr build_suffix_with_letters(size_t allocation_index) -{ - String base_string {}; - while (true) { - base_string = TRY(String::formatted("{:c}{}", 'a' + (allocation_index % 26), base_string)); - allocation_index = (allocation_index / 26); - if (allocation_index == 0) - break; - allocation_index = allocation_index - 1; - } - return base_string; -} - -static ErrorOr build_suffix_with_numbers(size_t allocation_index) -{ - return String::number(allocation_index); -} - -static ErrorOr prepare_permissions_after_populating_devtmpfs(StringView path, DeviceEventLoop::DeviceNodeMatch const& match) -{ - if (match.permission_group.is_null()) - return {}; - auto group = TRY(Core::System::getgrnam(match.permission_group)); - VERIFY(group.has_value()); - TRY(Core::System::endgrent()); - TRY(Core::System::chown(path, 0, group.value().gr_gid)); - return {}; -} - -ErrorOr DeviceEventLoop::register_new_device(DeviceNodeFamily::Type unix_device_type, MajorNumber major_number, MinorNumber minor_number) -{ - auto possible_match = device_node_family_to_match_type(unix_device_type, major_number, minor_number); - if (!possible_match.has_value()) - return {}; - auto const& match = possible_match.release_value(); - auto device_node_family = TRY(find_or_register_new_device_node_family(match, unix_device_type, major_number, minor_number)); - static constexpr StringView devtmpfs_base_path = "/dev/"sv; - auto path_pattern = TRY(String::from_utf8(match.path_pattern)); - auto& allocation_map = device_node_family->devices_symbol_suffix_allocation_map(); - auto possible_allocated_suffix_index = allocation_map.find_first_unset(); - if (!possible_allocated_suffix_index.has_value()) { - // FIXME: Make the allocation map bigger? - return Error::from_errno(ERANGE); - } - auto allocated_suffix_index = possible_allocated_suffix_index.release_value(); - - auto path = path_pattern; - if (match.path_pattern.contains("%digit"sv)) { - auto replacement = TRY(build_suffix_with_numbers(allocated_suffix_index)); - path = TRY(path.replace("%digit"sv, replacement, ReplaceMode::All)); - } - if (match.path_pattern.contains("%letter"sv)) { - auto replacement = TRY(build_suffix_with_letters(allocated_suffix_index)); - path = TRY(path.replace("%letter"sv, replacement, ReplaceMode::All)); - } - VERIFY(!path.is_empty()); - path = TRY(String::formatted("{}{}", devtmpfs_base_path, path)); - mode_t old_mask = umask(0); - if (unix_device_type == DeviceNodeFamily::Type::BlockDevice) - TRY(Core::System::create_block_device(path.bytes_as_string_view(), match.create_mode, major_number.value(), minor_number.value())); - else - TRY(Core::System::create_char_device(path.bytes_as_string_view(), match.create_mode, major_number.value(), minor_number.value())); - umask(old_mask); - TRY(prepare_permissions_after_populating_devtmpfs(path.bytes_as_string_view(), match)); - - auto result = TRY(device_node_family->registered_nodes().try_set(RegisteredDeviceNode { move(path), minor_number }, AK::HashSetExistingEntryBehavior::Keep)); - VERIFY(result != HashSetResult::ReplacedExistingEntry); - if (result == HashSetResult::KeptExistingEntry) { - // FIXME: Handle this case properly. - return Error::from_errno(EEXIST); - } - allocation_map.set(allocated_suffix_index, true); - return {}; -} - -ErrorOr DeviceEventLoop::unregister_device(DeviceNodeFamily::Type unix_device_type, MajorNumber major_number, MinorNumber minor_number) -{ - if (!device_node_family_to_match_type(unix_device_type, major_number, minor_number).has_value()) - return {}; - auto possible_family = find_device_node_family(unix_device_type, major_number, minor_number); - if (!possible_family.has_value()) { - // FIXME: Handle cases where we can't remove a device node. - // This could happen when the DeviceMapper program was restarted - // so the previous state was not preserved and a device was removed. - return Error::from_errno(ENODEV); - } - auto& family = possible_family.release_value(); - for (auto& node : family.registered_nodes()) { - if (node.minor_number() == minor_number) - TRY(Core::System::unlink(node.device_path())); - } - auto removed_anything = family.registered_nodes().remove_all_matching([minor_number](auto& device) { return device.minor_number() == minor_number; }); - if (!removed_anything) { - // FIXME: Handle cases where we can't remove a device node. - // This could happen when the DeviceMapper program was restarted - // so the previous state was not preserved and a device was removed. - return Error::from_errno(ENODEV); - } - return {}; -} - -struct PluggableOnceCharacterDeviceNodeMatch { - StringView path; - mode_t mode; - MajorNumber major; - MinorNumber minor; -}; - -static constexpr PluggableOnceCharacterDeviceNodeMatch s_simple_matchers[] = { - { "/dev/beep"sv, 0666, 1, 10 }, -}; - -static ErrorOr create_pluggable_once_char_device_node(PluggableOnceCharacterDeviceNodeMatch const& match) -{ - mode_t old_mask = umask(0); - ScopeGuard umask_guard([old_mask] { umask(old_mask); }); - TRY(Core::System::create_char_device(match.path, match.mode, match.major.value(), match.minor.value())); - return {}; -} - -ErrorOr DeviceEventLoop::read_one_or_eof(DeviceEvent& event) -{ - if (m_devctl_file->read_until_filled({ bit_cast(&event), sizeof(DeviceEvent) }).is_error()) { - // Bad! Kernel and SystemServer apparently disagree on the record size, - // which means that previous data is likely to be invalid. - return Error::from_string_view("File ended after incomplete record? /dev/devctl seems broken!"sv); - } - return {}; -} - -ErrorOr DeviceEventLoop::drain_events_from_devctl() -{ - for (;;) { - DeviceEvent event; - TRY(read_one_or_eof(event)); - // NOTE: Ignore any event related to /dev/devctl device node - normally - // it should never disappear from the system and we already use it in this - // code. - if (event.major_number == 2 && event.minor_number == 10 && !event.is_block_device) - continue; - - if (event.state == DeviceEvent::State::Inserted) { - if (!event.is_block_device) { - // NOTE: We have a special handling for the pluggable-once devices, etc, - // as these device (if they appear) should only "hotplug" (being inserted) - // once during the OS runtime. - // Therefore, we don't want to create a new MinorNumberAllocationType (e.g. SingleInstance). - // Instead, just blindly create such device node and assume we will never - // have to worry about it, so we don't need to register that! - auto possible_pluggable_once_char_device_match = ([](DeviceEvent& event) -> Optional { - for (auto const& match : s_simple_matchers) { - if (event.major_number == match.major.value() && event.minor_number == match.minor.value()) - return match; - } - return Optional {}; - })(event); - if (possible_pluggable_once_char_device_match.has_value()) { - TRY(create_pluggable_once_char_device_node(possible_pluggable_once_char_device_match.value())); - continue; - } - } - - VERIFY(event.is_block_device == 1 || event.is_block_device == 0); - TRY(register_new_device(event.is_block_device ? DeviceNodeFamily::Type::BlockDevice : DeviceNodeFamily::Type::CharacterDevice, event.major_number, event.minor_number)); - } else if (event.state == DeviceEvent::State::Removed) { - if (auto error_or_void = unregister_device(event.is_block_device ? DeviceNodeFamily::Type::BlockDevice : DeviceNodeFamily::Type::CharacterDevice, event.major_number, event.minor_number); error_or_void.is_error()) - dbgln("DeviceMapper: unregistering device failed: {}", error_or_void.error()); - } else { - dbgln("DeviceMapper: Unhandled device event ({:x})!", event.state); - } - } - VERIFY_NOT_REACHED(); -} - -} diff --git a/Userland/Services/DeviceMapper/DeviceEventLoop.h b/Userland/Services/DeviceMapper/DeviceEventLoop.h deleted file mode 100644 index 2d6c948a58d..00000000000 --- a/Userland/Services/DeviceMapper/DeviceEventLoop.h +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright (c) 2023, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include "DeviceNodeFamily.h" -#include -#include -#include -#include -#include -#include - -namespace DeviceMapper { - -class DeviceEventLoop { -public: - enum class MinorNumberAllocationType { - SequentialUnlimited, - SequentialLimited, - }; - - enum class UnixDeviceType { - BlockDevice, - CharacterDevice, - }; - - struct DeviceNodeMatch { - StringView permission_group; - StringView family_type_literal; - StringView path_pattern; - DeviceNodeFamily::Type unix_device_type; - MajorNumber major_number; - MinorNumberAllocationType minor_number_allocation_type; - MinorNumber minor_number_start; - size_t minor_number_range_size; - mode_t create_mode; - }; - - DeviceEventLoop(NonnullOwnPtr); - virtual ~DeviceEventLoop() = default; - - ErrorOr drain_events_from_devctl(); - -private: - Optional find_device_node_family(DeviceNodeFamily::Type unix_device_type, MajorNumber major_number, MinorNumber minor_number) const; - ErrorOr> find_or_register_new_device_node_family(DeviceNodeMatch const& match, DeviceNodeFamily::Type unix_device_type, MajorNumber major_number, MinorNumber minor_number); - - ErrorOr register_new_device(DeviceNodeFamily::Type unix_device_type, MajorNumber major_number, MinorNumber minor_number); - ErrorOr unregister_device(DeviceNodeFamily::Type unix_device_type, MajorNumber major_number, MinorNumber minor_number); - - ErrorOr read_one_or_eof(DeviceEvent& event); - - Vector> m_device_node_families; - NonnullOwnPtr const m_devctl_file; -}; - -} diff --git a/Userland/Services/DeviceMapper/DeviceNodeFamily.h b/Userland/Services/DeviceMapper/DeviceNodeFamily.h deleted file mode 100644 index 2ba58ecf96c..00000000000 --- a/Userland/Services/DeviceMapper/DeviceNodeFamily.h +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright (c) 2023, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include "RegisteredDeviceNode.h" -#include -#include -#include -#include - -namespace DeviceMapper { - -class DeviceNodeFamily : public RefCounted { -public: - enum class Type { - BlockDevice, - CharacterDevice, - }; - - DeviceNodeFamily(Bitmap devices_symbol_suffix_allocation_map, StringView literal_device_family, Type type, MajorNumber major, MinorNumber base_minor) - : m_literal_device_family(literal_device_family) - , m_type(type) - , m_major(major) - , m_base_minor(base_minor) - , m_devices_symbol_suffix_allocation_map(move(devices_symbol_suffix_allocation_map)) - { - } - - StringView literal_device_family() const { return m_literal_device_family; } - MajorNumber major_number() const { return m_major; } - MinorNumber base_minor_number() const { return m_base_minor; } - Type type() const { return m_type; } - - HashTable& registered_nodes() { return m_registered_nodes; } - Bitmap& devices_symbol_suffix_allocation_map() { return m_devices_symbol_suffix_allocation_map; } - Bitmap const& devices_symbol_suffix_allocation_map() const { return m_devices_symbol_suffix_allocation_map; } - -private: - StringView m_literal_device_family; - Type m_type { Type::CharacterDevice }; - MajorNumber m_major { 0 }; - MinorNumber m_base_minor { 0 }; - - HashTable m_registered_nodes; - Bitmap m_devices_symbol_suffix_allocation_map; -}; - -} diff --git a/Userland/Services/DeviceMapper/RegisteredDeviceNode.h b/Userland/Services/DeviceMapper/RegisteredDeviceNode.h deleted file mode 100644 index ff0ef2a8611..00000000000 --- a/Userland/Services/DeviceMapper/RegisteredDeviceNode.h +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright (c) 2023, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include - -namespace DeviceMapper { - -class RegisteredDeviceNode { -public: - RegisteredDeviceNode(String device_path, MinorNumber minor) - : m_device_path(move(device_path)) - , m_minor(minor) - { - } - - StringView device_path() const { return m_device_path.bytes_as_string_view(); } - MinorNumber minor_number() const { return m_minor; } - -private: - String m_device_path; - MinorNumber m_minor { 0 }; -}; - -} - -namespace AK { - -template<> -struct Traits : public DefaultTraits { - static unsigned hash(DeviceMapper::RegisteredDeviceNode const& node) - { - return int_hash(node.minor_number().value()); - } - - static bool equals(DeviceMapper::RegisteredDeviceNode const& a, DeviceMapper::RegisteredDeviceNode const& b) - { - return a.minor_number() == b.minor_number(); - } -}; - -} diff --git a/Userland/Services/DeviceMapper/main.cpp b/Userland/Services/DeviceMapper/main.cpp deleted file mode 100644 index 6a80c6a7dab..00000000000 --- a/Userland/Services/DeviceMapper/main.cpp +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright (c) 2023, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "DeviceEventLoop.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -ErrorOr serenity_main(Main::Arguments) -{ - TRY(Core::System::unveil("/dev/"sv, "rwc"sv)); - TRY(Core::System::unveil("/etc/group"sv, "rw"sv)); - TRY(Core::System::unveil(nullptr, nullptr)); - TRY(Core::System::pledge("stdio rpath dpath wpath cpath chown fattr")); - - auto file_or_error = Core::File::open("/dev/devctl"sv, Core::File::OpenMode::Read); - if (file_or_error.is_error()) { - warnln("Failed to open /dev/devctl - {}", file_or_error.error()); - VERIFY_NOT_REACHED(); - } - auto file = file_or_error.release_value(); - DeviceMapper::DeviceEventLoop device_event_loop(move(file)); - if (auto result = device_event_loop.drain_events_from_devctl(); result.is_error()) - dbgln("DeviceMapper: Fatal error: {}", result.release_error()); - // NOTE: If we return from drain_events_from_devctl, it must be an error - // so we should always return 1! - return 1; -} diff --git a/Userland/Services/EchoServer/CMakeLists.txt b/Userland/Services/EchoServer/CMakeLists.txt deleted file mode 100644 index 303c236dbe8..00000000000 --- a/Userland/Services/EchoServer/CMakeLists.txt +++ /dev/null @@ -1,12 +0,0 @@ -serenity_component( - EchoServer - TARGETS EchoServer -) - -set(SOURCES - Client.cpp - main.cpp -) - -serenity_bin(EchoServer) -target_link_libraries(EchoServer PRIVATE LibCore LibMain) diff --git a/Userland/Services/EchoServer/Client.cpp b/Userland/Services/EchoServer/Client.cpp deleted file mode 100644 index 4b028e2e6ec..00000000000 --- a/Userland/Services/EchoServer/Client.cpp +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright (c) 2020, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "Client.h" -#include -#include - -Client::Client(int id, NonnullOwnPtr socket) - : m_id(id) - , m_socket(move(socket)) -{ - m_socket->on_ready_to_read = [this] { - if (m_socket->is_eof()) - return; - - auto result = drain_socket(); - if (result.is_error()) { - dbgln("Failed while trying to drain the socket: {}", result.error()); - Core::deferred_invoke([this, strong_this = NonnullRefPtr(*this)] { quit(); }); - } - }; -} - -ErrorOr Client::drain_socket() -{ - NonnullRefPtr protect(*this); - - auto buffer = TRY(ByteBuffer::create_uninitialized(1024)); - - while (TRY(m_socket->can_read_without_blocking())) { - auto bytes_read = TRY(m_socket->read_some(buffer)); - - dbgln("Read {} bytes.", bytes_read.size()); - - if (m_socket->is_eof()) { - Core::deferred_invoke([this, strong_this = NonnullRefPtr(*this)] { quit(); }); - break; - } - - TRY(m_socket->write_until_depleted(bytes_read)); - } - - return {}; -} - -void Client::quit() -{ - m_socket->close(); - if (on_exit) - on_exit(); -} diff --git a/Userland/Services/EchoServer/Client.h b/Userland/Services/EchoServer/Client.h deleted file mode 100644 index c7a5680cc6c..00000000000 --- a/Userland/Services/EchoServer/Client.h +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright (c) 2020, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include - -class Client : public RefCounted { -public: - static NonnullRefPtr create(int id, NonnullOwnPtr socket) - { - return adopt_ref(*new Client(id, move(socket))); - } - - Function on_exit; - -protected: - Client(int id, NonnullOwnPtr socket); - - ErrorOr drain_socket(); - void quit(); - -private: - int m_id { 0 }; - NonnullOwnPtr m_socket; -}; diff --git a/Userland/Services/EchoServer/main.cpp b/Userland/Services/EchoServer/main.cpp deleted file mode 100644 index fd86c998bab..00000000000 --- a/Userland/Services/EchoServer/main.cpp +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright (c) 2020, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "Client.h" -#include -#include -#include -#include -#include -#include -#include - -ErrorOr serenity_main(Main::Arguments arguments) -{ - TRY(Core::System::pledge("stdio unix inet id accept")); - TRY(Core::System::unveil(nullptr, nullptr)); - - int port = 7; - - Core::ArgsParser args_parser; - args_parser.add_option(port, "Port to listen on", "port", 'p', "port"); - args_parser.parse(arguments); - - if ((u16)port != port) { - warnln("Invalid port number: {}", port); - exit(1); - } - - Core::EventLoop event_loop; - - auto server = TRY(Core::TCPServer::try_create()); - TRY(server->listen({}, port)); - - IGNORE_USE_IN_ESCAPING_LAMBDA HashMap> clients; - int next_id = 0; - - server->on_ready_to_accept = [&next_id, &clients, &server] { - int id = next_id++; - - auto maybe_client_socket = server->accept(); - if (maybe_client_socket.is_error()) { - warnln("accept: {}", maybe_client_socket.error()); - return; - } - - outln("Client {} connected", id); - - auto client = Client::create(id, maybe_client_socket.release_value()); - client->on_exit = [&clients, id] { - Core::deferred_invoke([&clients, id] { - clients.remove(id); - outln("Client {} disconnected", id); - }); - }; - clients.set(id, client); - }; - - outln("Listening on 0.0.0.0:{}", port); - - return event_loop.exec(); -} diff --git a/Userland/Services/FileOperation/CMakeLists.txt b/Userland/Services/FileOperation/CMakeLists.txt deleted file mode 100644 index 6ff31ce6998..00000000000 --- a/Userland/Services/FileOperation/CMakeLists.txt +++ /dev/null @@ -1,11 +0,0 @@ -serenity_component( - FileOperation - TARGETS FileOperation -) - -set(SOURCES - main.cpp -) - -serenity_bin(FileOperation) -target_link_libraries(FileOperation PRIVATE LibCore LibMain) diff --git a/Userland/Services/FileOperation/main.cpp b/Userland/Services/FileOperation/main.cpp deleted file mode 100644 index 8f0936cd2ea..00000000000 --- a/Userland/Services/FileOperation/main.cpp +++ /dev/null @@ -1,376 +0,0 @@ -/* - * Copyright (c) 2021, Andreas Kling - * Copyright (c) 2021-2022, Sam Atkins - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -struct WorkItem { - enum class Type { - CreateDirectory, - DeleteDirectory, - CopyFile, - MoveFile, - DeleteFile, - }; - Type type; - ByteString source; - ByteString destination; - off_t size; - mode_t mode { 0 }; -}; - -static void report_warning(StringView message); -static void report_error(StringView message); -static ErrorOr perform_copy(Vector const& sources, ByteString const& destination); -static ErrorOr perform_move(Vector const& sources, ByteString const& destination); -static ErrorOr perform_delete(Vector const& sources); -static ErrorOr execute_work_items(Vector const& items); -static ErrorOr> open_destination_file(ByteString const& destination, mode_t mode); -static ByteString deduplicate_destination_file_name(ByteString const& destination); - -ErrorOr serenity_main(Main::Arguments arguments) -{ - ByteString operation; - Vector paths; - - Core::ArgsParser args_parser; - args_parser.add_positional_argument(operation, "Operation: either 'Copy', 'Move' or 'Delete'", "operation", Core::ArgsParser::Required::Yes); - args_parser.add_positional_argument(paths, "Source paths, followed by a destination if applicable", "paths", Core::ArgsParser::Required::Yes); - args_parser.parse(arguments); - - if (operation == "Delete") - return perform_delete(paths); - - ByteString destination = paths.take_last(); - if (paths.is_empty()) - return Error::from_string_literal("At least one source and destination are required"); - - if (operation == "Copy") - return perform_copy(paths, destination); - if (operation == "Move") - return perform_move(paths, destination); - - // FIXME: Return the formatted string directly. There is no way to do this right now without the temporary going out of scope and being destroyed. - report_error(ByteString::formatted("Unknown operation '{}'", operation)); - return Error::from_string_literal("Unknown operation"); -} - -static void report_warning(StringView message) -{ - outln("WARN {}", message); -} - -static void report_error(StringView message) -{ - outln("ERROR {}", message); -} - -static ErrorOr collect_copy_work_items(ByteString const& source, ByteString const& destination, Vector& items) -{ - auto const st = TRY(Core::System::lstat(source)); - if (!S_ISDIR(st.st_mode)) { - // It's a file. - items.append(WorkItem { - .type = WorkItem::Type::CopyFile, - .source = source, - .destination = LexicalPath::join(destination, LexicalPath::basename(source)).string(), - .size = st.st_size, - .mode = st.st_mode, - }); - return 0; - } - - // It's a directory. - items.append(WorkItem { - .type = WorkItem::Type::CreateDirectory, - .source = {}, - .destination = LexicalPath::join(destination, LexicalPath::basename(source)).string(), - .size = 0, - .mode = st.st_mode, - }); - - Core::DirIterator dt(source, Core::DirIterator::SkipParentAndBaseDir); - while (dt.has_next()) { - auto name = dt.next_path(); - TRY(collect_copy_work_items( - LexicalPath::join(source, name).string(), - LexicalPath::join(destination, LexicalPath::basename(source)).string(), - items)); - } - - return 0; -} - -ErrorOr perform_copy(Vector const& sources, ByteString const& destination) -{ - Vector items; - - for (auto& source : sources) { - TRY(collect_copy_work_items(source, destination, items)); - } - - return execute_work_items(items); -} - -static ErrorOr collect_move_work_items(ByteString const& source, ByteString const& destination, Vector& items) -{ - auto const st = TRY(Core::System::lstat(source)); - if (!S_ISDIR(st.st_mode)) { - // It's a file. - items.append(WorkItem { - .type = WorkItem::Type::MoveFile, - .source = source, - .destination = LexicalPath::join(destination, LexicalPath::basename(source)).string(), - .size = st.st_size, - .mode = st.st_mode, - }); - return 0; - } - - // It's a directory. - items.append(WorkItem { - .type = WorkItem::Type::CreateDirectory, - .source = {}, - .destination = LexicalPath::join(destination, LexicalPath::basename(source)).string(), - .size = 0, - .mode = st.st_mode, - }); - - Core::DirIterator dt(source, Core::DirIterator::SkipParentAndBaseDir); - while (dt.has_next()) { - auto name = dt.next_path(); - TRY(collect_move_work_items( - LexicalPath::join(source, name).string(), - LexicalPath::join(destination, LexicalPath::basename(source)).string(), - items)); - } - - items.append(WorkItem { - .type = WorkItem::Type::DeleteDirectory, - .source = source, - .destination = {}, - .size = 0, - }); - - return 0; -} - -ErrorOr perform_move(Vector const& sources, ByteString const& destination) -{ - Vector items; - - for (auto& source : sources) { - TRY(collect_move_work_items(source, destination, items)); - } - - return execute_work_items(items); -} - -static ErrorOr collect_delete_work_items(ByteString const& source, Vector& items) -{ - if (auto const st = TRY(Core::System::lstat(source)); !S_ISDIR(st.st_mode)) { - // It's a file. - items.append(WorkItem { - .type = WorkItem::Type::DeleteFile, - .source = source, - .destination = {}, - .size = st.st_size, - }); - return 0; - } - - // It's a directory. - Core::DirIterator dt(source, Core::DirIterator::SkipParentAndBaseDir); - while (dt.has_next()) { - auto name = dt.next_path(); - TRY(collect_delete_work_items(LexicalPath::join(source, name).string(), items)); - } - - items.append(WorkItem { - .type = WorkItem::Type::DeleteDirectory, - .source = source, - .destination = {}, - .size = 0, - }); - - return 0; -} - -ErrorOr perform_delete(Vector const& sources) -{ - Vector items; - - for (auto& source : sources) { - TRY(collect_delete_work_items(source, items)); - } - - return execute_work_items(items); -} - -ErrorOr execute_work_items(Vector const& items) -{ - off_t total_work_bytes = 0; - for (auto& item : items) - total_work_bytes += item.size; - - off_t executed_work_bytes = 0; - - for (size_t i = 0; i < items.size(); ++i) { - auto& item = items[i]; - off_t item_done = 0; - auto print_progress = [&] { - outln("PROGRESS {} {} {} {} {} {} {}", i, items.size(), executed_work_bytes, total_work_bytes, item_done, item.size, item.source); - }; - - auto copy_file = [&](ByteString const& source, ByteString const& destination, mode_t mode) -> ErrorOr { - auto source_file = TRY(Core::File::open(source, Core::File::OpenMode::Read)); - // FIXME: When the file already exists, let the user choose the next action instead of renaming it by default. - auto destination_file = TRY(open_destination_file(destination, mode)); - auto buffer = TRY(ByteBuffer::create_zeroed(64 * KiB)); - - while (true) { - print_progress(); - auto bytes_read = TRY(source_file->read_some(buffer.bytes())); - if (bytes_read.is_empty()) - break; - if (auto result = destination_file->write_until_depleted(bytes_read); result.is_error()) { - // FIXME: Return the formatted string directly. There is no way to do this right now without the temporary going out of scope and being destroyed. - report_warning(ByteString::formatted("Failed to write to destination file: {}", result.error())); - return result.release_error(); - } - item_done += bytes_read.size(); - executed_work_bytes += bytes_read.size(); - print_progress(); - // FIXME: Remove this once the kernel is smart enough to schedule other threads - // while we're doing heavy I/O. Right now, copying a large file will totally - // starve the rest of the system. - sched_yield(); - } - print_progress(); - return 0; - }; - - switch (item.type) { - - case WorkItem::Type::CreateDirectory: { - outln("MKDIR {}", item.destination); - // FIXME: Support deduplication like open_destination_file() when the directory already exists. - auto old_mask = umask(0); - auto maybe_error = Core::System::mkdir(item.destination, item.mode); - umask(old_mask); - if (maybe_error.is_error() && maybe_error.error().code() != EEXIST) - return Error::from_syscall("mkdir"sv, -errno); - - break; - } - - case WorkItem::Type::DeleteDirectory: { - TRY(Core::System::rmdir(item.source)); - break; - } - - case WorkItem::Type::CopyFile: { - TRY(copy_file(item.source, item.destination, item.mode)); - break; - } - - case WorkItem::Type::MoveFile: { - ByteString destination = item.destination; - while (true) { - auto maybe_error = Core::System::rename(item.source, destination); - if (!maybe_error.is_error()) { - item_done += item.size; - executed_work_bytes += item.size; - print_progress(); - break; - } - auto error_code = maybe_error.release_error().code(); - - if (error_code == EEXIST) { - destination = deduplicate_destination_file_name(destination); - continue; - } - - if (error_code != EXDEV) { - // FIXME: Return the formatted string directly. There is no way to do this right now without the temporary going out of scope and being destroyed. - report_warning(ByteString::formatted("Failed to move {}: {}", item.source, strerror(error_code))); - return Error::from_errno(error_code); - } - - // EXDEV means we have to copy the file data and then remove the original - TRY(copy_file(item.source, item.destination, item.mode)); - TRY(Core::System::unlink(item.source)); - break; - } - - break; - } - - case WorkItem::Type::DeleteFile: { - TRY(Core::System::unlink(item.source)); - - item_done += item.size; - executed_work_bytes += item.size; - print_progress(); - - break; - } - - default: - VERIFY_NOT_REACHED(); - } - } - - outln("FINISH"); - return 0; -} - -ErrorOr> open_destination_file(ByteString const& destination, mode_t mode) -{ - auto old_mask = umask(0); - auto destination_file_or_error = Core::File::open(destination, (Core::File::OpenMode::Write | Core::File::OpenMode::Truncate | Core::File::OpenMode::MustBeNew), mode); - umask(old_mask); - if (destination_file_or_error.is_error() && destination_file_or_error.error().code() == EEXIST) { - return open_destination_file(deduplicate_destination_file_name(destination), mode); - } - return destination_file_or_error; -} - -ByteString deduplicate_destination_file_name(ByteString const& destination) -{ - LexicalPath destination_path(destination); - auto title_without_counter = destination_path.title(); - size_t next_counter = 1; - - auto last_hyphen_index = title_without_counter.find_last('-'); - if (last_hyphen_index.has_value()) { - auto counter_string = title_without_counter.substring_view(*last_hyphen_index + 1); - auto last_counter = counter_string.to_number(); - if (last_counter.has_value()) { - next_counter = *last_counter + 1; - title_without_counter = title_without_counter.substring_view(0, *last_hyphen_index); - } - } - - StringBuilder basename; - basename.appendff("{}-{}", title_without_counter, next_counter); - if (!destination_path.extension().is_empty()) { - basename.append('.'); - basename.append(destination_path.extension()); - } - - return LexicalPath::join(destination_path.dirname(), basename.to_byte_string()).string(); -} diff --git a/Userland/Services/FileSystemAccessServer/CMakeLists.txt b/Userland/Services/FileSystemAccessServer/CMakeLists.txt deleted file mode 100644 index 851a127d6f7..00000000000 --- a/Userland/Services/FileSystemAccessServer/CMakeLists.txt +++ /dev/null @@ -1,22 +0,0 @@ -serenity_component( - FileSystemAccessServer - REQUIRED - TARGETS FileSystemAccessServer -) - -compile_ipc(FileSystemAccessServer.ipc FileSystemAccessServerEndpoint.h) -compile_ipc(FileSystemAccessClient.ipc FileSystemAccessClientEndpoint.h) - -set(SOURCES - ConnectionFromClient.cpp - main.cpp -) - -set(GENERATED_SOURCES - FileSystemAccessServerEndpoint.h - FileSystemAccessClientEndpoint.h -) - -serenity_bin(FileSystemAccessServer) -target_link_libraries(FileSystemAccessServer PRIVATE LibCore LibFileSystem LibGfx LibGUI LibIPC LibMain LibURL) -add_dependencies(FileSystemAccessServer WindowServer) diff --git a/Userland/Services/FileSystemAccessServer/ConnectionFromClient.cpp b/Userland/Services/FileSystemAccessServer/ConnectionFromClient.cpp deleted file mode 100644 index f8852f1beb4..00000000000 --- a/Userland/Services/FileSystemAccessServer/ConnectionFromClient.cpp +++ /dev/null @@ -1,153 +0,0 @@ -/* - * Copyright (c) 2021, timmot - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include -#include - -namespace FileSystemAccessServer { - -static HashMap> s_connections; - -ConnectionFromClient::ConnectionFromClient(NonnullOwnPtr socket) - : IPC::ConnectionFromClient(*this, move(socket), 1) -{ - s_connections.set(1, *this); -} - -void ConnectionFromClient::die() -{ - s_connections.remove(client_id()); - GUI::Application::the()->quit(); -} - -void ConnectionFromClient::request_file_handler(i32 request_id, i32 window_server_client_id, i32 parent_window_id, ByteString const& path, Core::File::OpenMode requested_access, ShouldPrompt prompt) -{ - VERIFY(path.starts_with("/"sv)); - - bool approved = false; - auto maybe_permissions = m_approved_files.get(path); - - auto relevant_permissions = requested_access & (Core::File::OpenMode::Read | Core::File::OpenMode::Write); - VERIFY(relevant_permissions != Core::File::OpenMode::NotOpen); - - if (maybe_permissions.has_value()) - approved = has_flag(maybe_permissions.value(), relevant_permissions); - - if (!approved) { - ByteString access_string; - - if (has_flag(requested_access, Core::File::OpenMode::ReadWrite)) - access_string = "read and write"; - else if (has_flag(requested_access, Core::File::OpenMode::Read)) - access_string = "read from"; - else if (has_flag(requested_access, Core::File::OpenMode::Write)) - access_string = "write to"; - - auto pid = this->socket().peer_pid().release_value_but_fixme_should_propagate_errors(); - auto exe_link = LexicalPath("/proc").append(ByteString::number(pid)).append("exe"sv).string(); - auto exe_path = FileSystem::real_path(exe_link).release_value_but_fixme_should_propagate_errors(); - - if (prompt == ShouldPrompt::Yes) { - VERIFY(window_server_client_id != -1 && parent_window_id != -1); - auto exe_name = LexicalPath::basename(exe_path); - auto text = String::formatted("Allow {} ({}) to {} \"{}\"?", exe_name, pid, access_string, path).release_value_but_fixme_should_propagate_errors(); - auto result = GUI::MessageBox::try_show({}, window_server_client_id, parent_window_id, text, "File Permissions Requested"sv).release_value_but_fixme_should_propagate_errors(); - approved = result == GUI::MessageBox::ExecResult::Yes; - } else { - approved = true; - } - - if (approved) { - auto new_permissions = relevant_permissions; - - if (maybe_permissions.has_value()) - new_permissions |= maybe_permissions.value(); - - m_approved_files.set(path, new_permissions); - } - } - - if (approved) { - auto file = Core::File::open(path, requested_access); - - if (file.is_error()) { - dbgln("FileSystemAccessServer: Couldn't open {}, error {}", path, file.error()); - async_handle_prompt_end(request_id, file.error().code(), Optional {}, path); - } else { - async_handle_prompt_end(request_id, 0, IPC::File::adopt_file(file.release_value()), path); - } - } else { - async_handle_prompt_end(request_id, EPERM, Optional {}, path); - } -} - -void ConnectionFromClient::request_file_read_only_approved(i32 request_id, ByteString const& path) -{ - request_file_handler(request_id, -1, -1, path, Core::File::OpenMode::Read, ShouldPrompt::No); -} - -void ConnectionFromClient::request_file(i32 request_id, i32 window_server_client_id, i32 parent_window_id, ByteString const& path, Core::File::OpenMode requested_access) -{ - request_file_handler(request_id, window_server_client_id, parent_window_id, path, requested_access, ShouldPrompt::Yes); -} - -void ConnectionFromClient::prompt_open_file(i32 request_id, i32 window_server_client_id, i32 parent_window_id, ByteString const& window_title, ByteString const& path_to_view, Core::File::OpenMode requested_access, Optional> const& allowed_file_types) -{ - auto relevant_permissions = requested_access & (Core::File::OpenMode::Read | Core::File::OpenMode::Write); - VERIFY(relevant_permissions != Core::File::OpenMode::NotOpen); - - auto user_picked_file = GUI::FilePicker::get_filepath({}, window_server_client_id, parent_window_id, GUI::FilePicker::Mode::Open, window_title, {}, path_to_view, allowed_file_types).release_value_but_fixme_should_propagate_errors(); - auto user_picked_file_but_fixme_should_use_string = user_picked_file.has_value() ? user_picked_file.release_value().to_byte_string() : Optional {}; - - prompt_helper(request_id, user_picked_file_but_fixme_should_use_string, requested_access); -} - -void ConnectionFromClient::prompt_save_file(i32 request_id, i32 window_server_client_id, i32 parent_window_id, ByteString const& name, ByteString const& ext, ByteString const& path_to_view, Core::File::OpenMode requested_access) -{ - auto relevant_permissions = requested_access & (Core::File::OpenMode::Read | Core::File::OpenMode::Write); - VERIFY(relevant_permissions != Core::File::OpenMode::NotOpen); - - auto basename = String::formatted("{}.{}", name, ext).release_value_but_fixme_should_propagate_errors(); - auto user_picked_file = GUI::FilePicker::get_filepath({}, window_server_client_id, parent_window_id, GUI::FilePicker::Mode::Save, {}, basename, path_to_view).release_value_but_fixme_should_propagate_errors(); - auto user_picked_file_but_fixme_should_use_string = user_picked_file.has_value() ? user_picked_file.release_value().to_byte_string() : Optional {}; - - prompt_helper(request_id, user_picked_file_but_fixme_should_use_string, requested_access); -} - -void ConnectionFromClient::prompt_helper(i32 request_id, Optional const& user_picked_file, Core::File::OpenMode requested_access) -{ - if (user_picked_file.has_value()) { - VERIFY(user_picked_file->starts_with("/"sv)); - auto file = Core::File::open(user_picked_file.value(), requested_access); - - if (file.is_error()) { - dbgln("FileSystemAccessServer: Couldn't open {}, error {}", user_picked_file.value(), file.error()); - async_handle_prompt_end(request_id, file.error().code(), Optional {}, user_picked_file); - } else { - auto maybe_permissions = m_approved_files.get(user_picked_file.value()); - auto new_permissions = requested_access & (Core::File::OpenMode::Read | Core::File::OpenMode::Write); - if (maybe_permissions.has_value()) - new_permissions |= maybe_permissions.value(); - - m_approved_files.set(user_picked_file.value(), new_permissions); - - async_handle_prompt_end(request_id, 0, IPC::File::adopt_file(file.release_value()), user_picked_file); - } - } else { - async_handle_prompt_end(request_id, ECANCELED, Optional {}, Optional {}); - } -} - -Messages::FileSystemAccessServer::ExposeWindowServerClientIdResponse ConnectionFromClient::expose_window_server_client_id() -{ - return GUI::ConnectionToWindowServer::the().expose_client_id(); -} - -} diff --git a/Userland/Services/FileSystemAccessServer/ConnectionFromClient.h b/Userland/Services/FileSystemAccessServer/ConnectionFromClient.h deleted file mode 100644 index 0a68f54ef1e..00000000000 --- a/Userland/Services/FileSystemAccessServer/ConnectionFromClient.h +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright (c) 2021, timmot - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include -#include -#include - -namespace FileSystemAccessServer { - -class ConnectionFromClient final - : public IPC::ConnectionFromClient { - C_OBJECT(ConnectionFromClient); - -public: - ~ConnectionFromClient() override = default; - - virtual void die() override; - -private: - explicit ConnectionFromClient(NonnullOwnPtr); - - virtual void request_file_read_only_approved(i32, ByteString const&) override; - virtual void request_file(i32, i32, i32, ByteString const&, Core::File::OpenMode) override; - virtual void prompt_open_file(i32, i32, i32, ByteString const&, ByteString const&, Core::File::OpenMode, Optional> const&) override; - virtual void prompt_save_file(i32, i32, i32, ByteString const&, ByteString const&, ByteString const&, Core::File::OpenMode) override; - - void prompt_helper(i32, Optional const&, Core::File::OpenMode); - - enum class ShouldPrompt { - No, - Yes - }; - void request_file_handler(i32, i32, i32, ByteString const&, Core::File::OpenMode, ShouldPrompt); - - virtual Messages::FileSystemAccessServer::ExposeWindowServerClientIdResponse expose_window_server_client_id() override; - - HashMap m_approved_files; -}; - -} diff --git a/Userland/Services/FileSystemAccessServer/FileSystemAccessClient.ipc b/Userland/Services/FileSystemAccessServer/FileSystemAccessClient.ipc deleted file mode 100644 index d8cfedb2161..00000000000 --- a/Userland/Services/FileSystemAccessServer/FileSystemAccessClient.ipc +++ /dev/null @@ -1,4 +0,0 @@ -endpoint FileSystemAccessClient -{ - handle_prompt_end(i32 request_id, i32 error, Optional fd, Optional chosen_file) =| -} diff --git a/Userland/Services/FileSystemAccessServer/FileSystemAccessServer.ipc b/Userland/Services/FileSystemAccessServer/FileSystemAccessServer.ipc deleted file mode 100644 index 993b70eb300..00000000000 --- a/Userland/Services/FileSystemAccessServer/FileSystemAccessServer.ipc +++ /dev/null @@ -1,12 +0,0 @@ -#include -#include - -endpoint FileSystemAccessServer -{ - request_file_read_only_approved(i32 request_id, ByteString path) =| - request_file(i32 request_id, i32 window_server_client_id, i32 window_id, ByteString path, Core::File::OpenMode requested_access) =| - prompt_open_file(i32 request_id, i32 window_server_client_id, i32 window_id, ByteString window_title, ByteString path_to_view, Core::File::OpenMode requested_access, Optional> allowed_file_types) =| - prompt_save_file(i32 request_id, i32 window_server_client_id, i32 window_id, ByteString title, ByteString ext, ByteString path_to_view, Core::File::OpenMode requested_access) =| - - expose_window_server_client_id() => (i32 client_id) -} diff --git a/Userland/Services/FileSystemAccessServer/main.cpp b/Userland/Services/FileSystemAccessServer/main.cpp deleted file mode 100644 index 3644de3105b..00000000000 --- a/Userland/Services/FileSystemAccessServer/main.cpp +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Copyright (c) 2021, timmot - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include - -ErrorOr serenity_main(Main::Arguments arguments) -{ - TRY(Core::System::pledge("stdio recvfd sendfd rpath cpath wpath unix thread")); - - auto app = TRY(GUI::Application::create(arguments)); - app->set_quit_when_last_window_deleted(false); - - auto client = TRY(IPC::take_over_accepted_client_from_system_server()); - return app->exec(); -} diff --git a/Userland/Services/KeyboardPreferenceLoader/CMakeLists.txt b/Userland/Services/KeyboardPreferenceLoader/CMakeLists.txt deleted file mode 100644 index 3bf34227298..00000000000 --- a/Userland/Services/KeyboardPreferenceLoader/CMakeLists.txt +++ /dev/null @@ -1,12 +0,0 @@ -serenity_component( - KeyboardPreferenceLoader - REQUIRED - TARGETS KeyboardPreferenceLoader keymap -) - -set(SOURCES - main.cpp -) - -serenity_bin(KeyboardPreferenceLoader) -target_link_libraries(KeyboardPreferenceLoader PRIVATE LibCore LibMain) diff --git a/Userland/Services/KeyboardPreferenceLoader/main.cpp b/Userland/Services/KeyboardPreferenceLoader/main.cpp deleted file mode 100644 index 87e990da062..00000000000 --- a/Userland/Services/KeyboardPreferenceLoader/main.cpp +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright (c) 2021, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include -#include - -ErrorOr serenity_main(Main::Arguments) -{ - TRY(Core::System::pledge("stdio proc exec rpath cpath")); - auto keyboard_settings_config = TRY(Core::ConfigFile::open_for_app("KeyboardSettings")); - - TRY(Core::System::unveil("/bin/keymap", "x")); - TRY(Core::System::unveil("/etc/Keyboard.ini", "r")); - TRY(Core::System::unveil("/dev/input/keyboard/0", "r")); - TRY(Core::System::unveil(nullptr, nullptr)); - auto mapper_config = TRY(Core::ConfigFile::open("/etc/Keyboard.ini")); - auto keymaps = mapper_config->read_entry("Mapping", "Keymaps", ""); - - auto keymaps_vector = keymaps.split(','); - if (keymaps_vector.size() == 0) - exit(1); - - TRY(Core::Process::spawn("/bin/keymap"sv, Array { "-m", keymaps_vector.first().characters() }, {}, Core::Process::KeepAsChild::Yes)); - - bool enable_num_lock = keyboard_settings_config->read_bool_entry("StartupEnable", "NumLock", true); - auto keyboard_device = TRY(Core::File::open("/dev/input/keyboard/0"sv, Core::File::OpenMode::Read)); - TRY(Core::System::ioctl(keyboard_device->fd(), KEYBOARD_IOCTL_SET_NUM_LOCK, enable_num_lock)); - - return 0; -} diff --git a/Userland/Services/LaunchServer/CMakeLists.txt b/Userland/Services/LaunchServer/CMakeLists.txt deleted file mode 100644 index 8ead20413cc..00000000000 --- a/Userland/Services/LaunchServer/CMakeLists.txt +++ /dev/null @@ -1,22 +0,0 @@ -serenity_component( - LaunchServer - REQUIRED - TARGETS LaunchServer -) - -compile_ipc(LaunchServer.ipc LaunchServerEndpoint.h) -compile_ipc(LaunchClient.ipc LaunchClientEndpoint.h) - -set(SOURCES - ConnectionFromClient.cpp - Launcher.cpp - main.cpp -) - -set(GENERATED_SOURCES - LaunchClientEndpoint.h - LaunchServerEndpoint.h -) - -serenity_bin(LaunchServer) -target_link_libraries(LaunchServer PRIVATE LibCore LibIPC LibDesktop LibFileSystem LibMain LibURL) diff --git a/Userland/Services/LaunchServer/ConnectionFromClient.cpp b/Userland/Services/LaunchServer/ConnectionFromClient.cpp deleted file mode 100644 index 9e13b80329e..00000000000 --- a/Userland/Services/LaunchServer/ConnectionFromClient.cpp +++ /dev/null @@ -1,120 +0,0 @@ -/* - * Copyright (c) 2020, Nicholas Hollett - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "ConnectionFromClient.h" -#include "Launcher.h" -#include -#include -#include - -namespace LaunchServer { - -static HashMap> s_connections; -ConnectionFromClient::ConnectionFromClient(NonnullOwnPtr client_socket, int client_id) - : IPC::ConnectionFromClient(*this, move(client_socket), client_id) -{ - s_connections.set(client_id, *this); -} - -void ConnectionFromClient::die() -{ - s_connections.remove(client_id()); -} - -Messages::LaunchServer::OpenUrlResponse ConnectionFromClient::open_url(URL::URL const& url, ByteString const& handler_name) -{ - if (!m_allowlist.is_empty()) { - bool allowed = false; - auto request_url_without_fragment = url; - request_url_without_fragment.set_fragment({}); - for (auto& allowed_handler : m_allowlist) { - if (allowed_handler.handler_name == handler_name - && (allowed_handler.any_url || allowed_handler.urls.contains_slow(request_url_without_fragment))) { - allowed = true; - break; - } - } - if (!allowed) { - // You are not on the list, go home! - did_misbehave(ByteString::formatted("Client requested a combination of handler/URL that was not on the list: '{}' with '{}'", handler_name, url).characters()); - return nullptr; - } - } - - return Launcher::the().open_url(url, handler_name); -} - -Messages::LaunchServer::GetHandlersForUrlResponse ConnectionFromClient::get_handlers_for_url(URL::URL const& url) -{ - return Launcher::the().handlers_for_url(url); -} - -Messages::LaunchServer::GetHandlersWithDetailsForUrlResponse ConnectionFromClient::get_handlers_with_details_for_url(URL::URL const& url) -{ - return Launcher::the().handlers_with_details_for_url(url); -} - -void ConnectionFromClient::add_allowed_url(URL::URL const& url) -{ - if (m_allowlist_is_sealed) { - did_misbehave("Got request to add more allowed handlers after list was sealed"); - return; - } - - if (!url.is_valid()) { - did_misbehave("Got request to allow invalid URL"); - return; - } - - m_allowlist.empend(ByteString(), false, Vector { url }); -} - -void ConnectionFromClient::add_allowed_handler_with_any_url(ByteString const& handler_name) -{ - if (m_allowlist_is_sealed) { - did_misbehave("Got request to add more allowed handlers after list was sealed"); - return; - } - - if (handler_name.is_empty()) { - did_misbehave("Got request to allow empty handler name"); - return; - } - - m_allowlist.empend(handler_name, true, Vector()); -} - -void ConnectionFromClient::add_allowed_handler_with_only_specific_urls(ByteString const& handler_name, Vector const& urls) -{ - if (m_allowlist_is_sealed) { - did_misbehave("Got request to add more allowed handlers after list was sealed"); - return; - } - - if (handler_name.is_empty()) { - did_misbehave("Got request to allow empty handler name"); - return; - } - - if (urls.is_empty()) { - did_misbehave("Got request to allow empty URL list"); - return; - } - - m_allowlist.empend(handler_name, false, urls); -} - -void ConnectionFromClient::seal_allowlist() -{ - if (m_allowlist_is_sealed) { - did_misbehave("Got more than one request to seal the allowed handlers list"); - return; - } - - m_allowlist_is_sealed = true; -} - -} diff --git a/Userland/Services/LaunchServer/ConnectionFromClient.h b/Userland/Services/LaunchServer/ConnectionFromClient.h deleted file mode 100644 index 576f5b55037..00000000000 --- a/Userland/Services/LaunchServer/ConnectionFromClient.h +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright (c) 2020, Nicholas Hollett - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include - -namespace LaunchServer { - -class ConnectionFromClient final : public IPC::ConnectionFromClient { - C_OBJECT(ConnectionFromClient) -public: - ~ConnectionFromClient() override = default; - - virtual void die() override; - -private: - explicit ConnectionFromClient(NonnullOwnPtr, int client_id); - - virtual Messages::LaunchServer::OpenUrlResponse open_url(URL::URL const&, ByteString const&) override; - virtual Messages::LaunchServer::GetHandlersForUrlResponse get_handlers_for_url(URL::URL const&) override; - virtual Messages::LaunchServer::GetHandlersWithDetailsForUrlResponse get_handlers_with_details_for_url(URL::URL const&) override; - virtual void add_allowed_url(URL::URL const&) override; - virtual void add_allowed_handler_with_any_url(ByteString const&) override; - virtual void add_allowed_handler_with_only_specific_urls(ByteString const&, Vector const&) override; - virtual void seal_allowlist() override; - - struct AllowlistEntry { - ByteString handler_name; - bool any_url { false }; - Vector urls; - }; - - Vector m_allowlist; - bool m_allowlist_is_sealed { false }; -}; -} diff --git a/Userland/Services/LaunchServer/LaunchClient.ipc b/Userland/Services/LaunchServer/LaunchClient.ipc deleted file mode 100644 index fc0280a775f..00000000000 --- a/Userland/Services/LaunchServer/LaunchClient.ipc +++ /dev/null @@ -1,5 +0,0 @@ -#include - -endpoint LaunchClient -{ -} diff --git a/Userland/Services/LaunchServer/LaunchServer.ipc b/Userland/Services/LaunchServer/LaunchServer.ipc deleted file mode 100644 index fb1d70b5d6c..00000000000 --- a/Userland/Services/LaunchServer/LaunchServer.ipc +++ /dev/null @@ -1,13 +0,0 @@ -#include - -endpoint LaunchServer -{ - open_url(URL::URL url, ByteString handler_name) => (bool response) - get_handlers_for_url(URL::URL url) => (Vector handlers) - get_handlers_with_details_for_url(URL::URL url) => (Vector handlers_details) - - add_allowed_url(URL::URL url) => () - add_allowed_handler_with_any_url(ByteString handler_name) => () - add_allowed_handler_with_only_specific_urls(ByteString handler_name, Vector urls) => () - seal_allowlist() => () -} diff --git a/Userland/Services/LaunchServer/Launcher.cpp b/Userland/Services/LaunchServer/Launcher.cpp deleted file mode 100644 index ac3fbbee42c..00000000000 --- a/Userland/Services/LaunchServer/Launcher.cpp +++ /dev/null @@ -1,420 +0,0 @@ -/* - * Copyright (c) 2020, Nicholas Hollett , Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "Launcher.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace LaunchServer { - -static Launcher* s_the; -static bool spawn(ByteString executable, Vector const& arguments); - -ByteString Handler::name_from_executable(StringView executable) -{ - auto separator = executable.find_last('/'); - if (separator.has_value()) { - auto start = separator.value() + 1; - return executable.substring_view(start, executable.length() - start); - } - return executable; -} - -void Handler::from_executable(Type handler_type, ByteString const& executable) -{ - this->handler_type = handler_type; - this->name = name_from_executable(executable); - this->executable = executable; -} - -ByteString Handler::to_details_str() const -{ - StringBuilder builder; - auto obj = MUST(JsonObjectSerializer<>::try_create(builder)); - MUST(obj.add("executable"sv, executable)); - MUST(obj.add("name"sv, name)); - - auto arguments = MUST(obj.add_array("arguments"sv)); - for (auto const& argument : this->arguments) - MUST(arguments.add(argument)); - MUST(arguments.finish()); - - switch (handler_type) { - case Type::Application: - MUST(obj.add("type"sv, "app")); - break; - case Type::UserDefault: - MUST(obj.add("type"sv, "userdefault")); - break; - case Type::UserPreferred: - MUST(obj.add("type"sv, "userpreferred")); - break; - default: - break; - } - - MUST(obj.finish()); - return builder.to_byte_string(); -} - -Launcher::Launcher() -{ - VERIFY(s_the == nullptr); - s_the = this; -} - -Launcher& Launcher::the() -{ - VERIFY(s_the); - return *s_the; -} - -void Launcher::load_handlers(ByteString const& af_dir) -{ - Desktop::AppFile::for_each([&](auto af) { - auto app_name = af->name(); - auto app_executable = af->executable(); - auto app_arguments = af->arguments(); - HashTable mime_types; - for (auto& mime_type : af->launcher_mime_types()) - mime_types.set(mime_type); - HashTable file_types; - for (auto& file_type : af->launcher_file_types()) - file_types.set(file_type); - HashTable protocols; - for (auto& protocol : af->launcher_protocols()) - protocols.set(protocol); - if (access(app_executable.characters(), X_OK) == 0) - m_handlers.set(app_executable, { Handler::Type::Default, app_name, app_executable, move(app_arguments), mime_types, file_types, protocols }); - }, - af_dir); -} - -void Launcher::load_config(Core::ConfigFile const& cfg) -{ - for (auto key : cfg.keys("MimeType")) { - auto handler = cfg.read_entry("MimeType", key).trim_whitespace(); - if (handler.is_empty()) - continue; - if (access(handler.characters(), X_OK) != 0) - continue; - m_mime_handlers.set(key.to_lowercase(), handler); - } - - for (auto key : cfg.keys("FileType")) { - auto handler = cfg.read_entry("FileType", key).trim_whitespace(); - if (handler.is_empty()) - continue; - if (access(handler.characters(), X_OK) != 0) - continue; - m_file_handlers.set(key.to_lowercase(), handler); - } - - for (auto key : cfg.keys("Protocol")) { - auto handler = cfg.read_entry("Protocol", key).trim_whitespace(); - if (handler.is_empty()) - continue; - if (access(handler.characters(), X_OK) != 0) - continue; - m_protocol_handlers.set(key.to_lowercase(), handler); - } -} - -bool Launcher::has_mime_handlers(ByteString const& mime_type) -{ - for (auto& handler : m_handlers) - if (handler.value.mime_types.contains(mime_type)) - return true; - return false; -} - -Vector Launcher::handlers_for_url(const URL::URL& url) -{ - Vector handlers; - if (url.scheme() == "file") { - for_each_handler_for_path(url.serialize_path(), [&](auto& handler) -> bool { - handlers.append(handler.executable); - return true; - }); - } else { - for_each_handler(url.scheme().to_byte_string(), m_protocol_handlers, [&](auto const& handler) -> bool { - if (handler.handler_type != Handler::Type::Default || handler.protocols.contains(url.scheme().to_byte_string())) { - handlers.append(handler.executable); - return true; - } - return false; - }); - } - return handlers; -} - -Vector Launcher::handlers_with_details_for_url(const URL::URL& url) -{ - Vector handlers; - if (url.scheme() == "file") { - for_each_handler_for_path(url.serialize_path(), [&](auto& handler) -> bool { - handlers.append(handler.to_details_str()); - return true; - }); - } else { - for_each_handler(url.scheme().to_byte_string(), m_protocol_handlers, [&](auto const& handler) -> bool { - if (handler.handler_type != Handler::Type::Default || handler.protocols.contains(url.scheme().to_byte_string())) { - handlers.append(handler.to_details_str()); - return true; - } - return false; - }); - } - return handlers; -} - -Optional Launcher::mime_type_for_file(ByteString path) -{ - auto file_or_error = Core::File::open(path, Core::File::OpenMode::Read); - if (file_or_error.is_error()) - return {}; - - return Core::guess_mime_type_based_on_sniffed_bytes(*file_or_error.release_value()); -} - -bool Launcher::open_url(const URL::URL& url, ByteString const& handler_name) -{ - if (!handler_name.is_empty()) - return open_with_handler_name(url, handler_name); - - if (url.scheme() == "file") - return open_file_url(url); - - return open_with_user_preferences(m_protocol_handlers, url.scheme().to_byte_string(), { url.to_byte_string() }); -} - -bool Launcher::open_with_handler_name(const URL::URL& url, ByteString const& handler_name) -{ - auto handler_optional = m_handlers.get(handler_name); - if (!handler_optional.has_value()) - return false; - - auto& handler = handler_optional.value(); - ByteString argument; - if (url.scheme() == "file") - argument = url.serialize_path(); - else - argument = url.to_byte_string(); - return spawn(handler.executable, { argument }); -} - -bool spawn(ByteString executable, Vector const& arguments) -{ - return !Core::Process::spawn(executable, arguments).is_error(); -} - -Handler Launcher::get_handler_for_executable(Handler::Type handler_type, ByteString const& executable) const -{ - Handler handler; - auto existing_handler = m_handlers.get(executable); - if (existing_handler.has_value()) { - handler = existing_handler.value(); - handler.handler_type = handler_type; - } else { - handler.from_executable(handler_type, executable); - } - return handler; -} - -bool Launcher::open_with_user_preferences(HashMap const& user_preferences, ByteString const& key, Vector const& arguments, ByteString const& default_program) -{ - auto program_path = user_preferences.get(key); - if (program_path.has_value()) - return spawn(program_path.value(), arguments); - - ByteString executable = ""; - if (for_each_handler(key, user_preferences, [&](auto const& handler) -> bool { - if (executable.is_empty() && (handler.mime_types.contains(key) || handler.file_types.contains(key) || handler.protocols.contains(key))) { - executable = handler.executable; - return true; - } - return false; - })) { - return spawn(executable, arguments); - } - - // There wasn't a handler for this, so try the fallback instead - program_path = user_preferences.get("*"); - if (program_path.has_value()) - return spawn(program_path.value(), arguments); - - // Absolute worst case, try the provided default program, if any - if (!default_program.is_empty()) - return spawn(default_program, arguments); - - return false; -} - -size_t Launcher::for_each_handler(ByteString const& key, HashMap const& user_preference, Function f) -{ - auto user_preferred = user_preference.get(key); - if (user_preferred.has_value()) - f(get_handler_for_executable(Handler::Type::UserPreferred, user_preferred.value())); - - size_t counted = 0; - for (auto& handler : m_handlers) { - // Skip over the existing item in the list - if (user_preferred.has_value() && user_preferred.value() == handler.value.executable) - continue; - if (f(handler.value)) - counted++; - } - - auto user_default = user_preference.get("*"); - if (counted == 0 && user_default.has_value()) - f(get_handler_for_executable(Handler::Type::UserDefault, user_default.value())); - // Return the number of times f() was called, - // which can be used to know whether there were any handlers - return counted; -} - -void Launcher::for_each_handler_for_path(ByteString const& path, Function f) -{ - struct stat st; - if (lstat(path.characters(), &st) < 0) { - perror("lstat"); - return; - } - - if (S_ISDIR(st.st_mode)) { - auto handler_optional = m_file_handlers.get("directory"); - if (!handler_optional.has_value()) - return; - auto& handler = handler_optional.value(); - f(get_handler_for_executable(Handler::Type::Default, handler)); - return; - } - - if (!S_ISREG(st.st_mode) && !S_ISLNK(st.st_mode)) - return; - - if (S_ISLNK(st.st_mode)) { - auto link_target_or_error = FileSystem::read_link(path); - if (link_target_or_error.is_error()) { - perror("read_link"); - return; - } - - auto link_target = LexicalPath { link_target_or_error.release_value() }; - LexicalPath absolute_link_target = link_target.is_absolute() ? link_target : LexicalPath::join(LexicalPath::dirname(path), link_target.string()); - auto real_path_or_error = FileSystem::real_path(absolute_link_target.string()); - if (real_path_or_error.is_error()) { - perror("real_path"); - return; - } - auto real_path = real_path_or_error.release_value(); - - return for_each_handler_for_path(real_path, [&](auto const& handler) -> bool { - return f(handler); - }); - } - - if ((st.st_mode & S_IFMT) == S_IFREG && (st.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH))) - f(get_handler_for_executable(Handler::Type::Application, path)); - - auto extension = LexicalPath::extension(path).to_lowercase(); - auto mime_type = mime_type_for_file(path); - - if (mime_type.has_value()) { - if (for_each_handler(mime_type.value(), m_mime_handlers, [&](auto const& handler) -> bool { - if (handler.handler_type != Handler::Type::Default || handler.mime_types.contains(mime_type.value())) - return f(handler); - return false; - })) { - return; - } - } - - for_each_handler(extension, m_file_handlers, [&](auto const& handler) -> bool { - if (handler.handler_type != Handler::Type::Default || handler.file_types.contains(extension)) - return f(handler); - return false; - }); -} - -bool Launcher::open_file_url(const URL::URL& url) -{ - struct stat st; - auto file_path = url.serialize_path(); - if (stat(file_path.characters(), &st) < 0) { - perror("stat"); - return false; - } - - if (S_ISDIR(st.st_mode)) { - Vector fm_arguments; - if (!url.fragment().has_value() || url.fragment()->is_empty()) { - fm_arguments.append(file_path); - } else { - fm_arguments.append("-s"); - fm_arguments.append("-r"); - fm_arguments.append(ByteString::formatted("{}/{}", file_path, *url.fragment())); - } - - auto handler_optional = m_file_handlers.get("directory"); - if (!handler_optional.has_value()) - return false; - auto& handler = handler_optional.value(); - - return spawn(handler, fm_arguments); - } - - if ((st.st_mode & S_IFMT) == S_IFREG && st.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH)) - return spawn(file_path, {}); - - auto extension = LexicalPath::extension(file_path).to_lowercase(); - auto mime_type = mime_type_for_file(file_path); - - auto mime_type_or_extension = extension; - bool should_use_mime_type = mime_type.has_value() && has_mime_handlers(mime_type.value()); - if (should_use_mime_type) - mime_type_or_extension = mime_type.value(); - - auto handler_optional = m_file_handlers.get("txt"); - ByteString default_handler = ""; - if (handler_optional.has_value()) - default_handler = handler_optional.value(); - - // Additional parameters parsing, specific for the file protocol and txt file handlers - Vector additional_parameters; - ByteString filepath = url.serialize_path(); - - if (url.query().has_value()) { - url.query()->bytes_as_string_view().for_each_split_view('&', SplitBehavior::Nothing, [&](auto parameter) { - auto pair = parameter.split_view('='); - if (pair.size() == 2 && pair[0] == "line_number") { - auto line = pair[1].template to_number(); - if (line.has_value()) - // TextEditor uses file:line:col to open a file at a specific line number - filepath = ByteString::formatted("{}:{}", filepath, line.value()); - } - }); - } - - additional_parameters.append(filepath); - - return open_with_user_preferences(should_use_mime_type ? m_mime_handlers : m_file_handlers, mime_type_or_extension, additional_parameters, default_handler); -} -} diff --git a/Userland/Services/LaunchServer/Launcher.h b/Userland/Services/LaunchServer/Launcher.h deleted file mode 100644 index f80618c511c..00000000000 --- a/Userland/Services/LaunchServer/Launcher.h +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright (c) 2020, Nicholas Hollett - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include - -namespace LaunchServer { - -struct Handler { - enum class Type { - Default = 0, - Application, - UserPreferred, - UserDefault - }; - Type handler_type; - ByteString name; - ByteString executable; - Vector arguments; - HashTable mime_types {}; - HashTable file_types {}; - HashTable protocols {}; - - static ByteString name_from_executable(StringView); - void from_executable(Type, ByteString const&); - ByteString to_details_str() const; -}; - -class Launcher { -public: - Launcher(); - static Launcher& the(); - - void load_handlers(ByteString const& af_dir = Desktop::AppFile::APP_FILES_DIRECTORY); - void load_config(Core::ConfigFile const&); - bool open_url(const URL::URL&, ByteString const& handler_name); - Vector handlers_for_url(const URL::URL&); - Vector handlers_with_details_for_url(const URL::URL&); - -private: - HashMap m_handlers; - HashMap m_protocol_handlers; - HashMap m_file_handlers; - HashMap m_mime_handlers; - - bool has_mime_handlers(ByteString const&); - Optional mime_type_for_file(ByteString path); - Handler get_handler_for_executable(Handler::Type, ByteString const&) const; - size_t for_each_handler(ByteString const& key, HashMap const& user_preferences, Function f); - void for_each_handler_for_path(ByteString const&, Function f); - bool open_file_url(const URL::URL&); - bool open_with_user_preferences(HashMap const& user_preferences, ByteString const& key, Vector const& arguments, ByteString const& default_program = {}); - bool open_with_handler_name(const URL::URL&, ByteString const& handler_name); -}; -} diff --git a/Userland/Services/LaunchServer/main.cpp b/Userland/Services/LaunchServer/main.cpp deleted file mode 100644 index d23e27c2bee..00000000000 --- a/Userland/Services/LaunchServer/main.cpp +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright (c) 2020, Nicholas Hollett - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "ConnectionFromClient.h" -#include "Launcher.h" -#include -#include -#include -#include -#include - -ErrorOr serenity_main(Main::Arguments) -{ - Core::EventLoop event_loop; - auto server = TRY(IPC::MultiServer::try_create()); - - auto launcher = LaunchServer::Launcher(); - launcher.load_handlers(); - launcher.load_config(TRY(Core::ConfigFile::open_for_app("LaunchServer"))); - - TRY(Core::System::pledge("stdio accept rpath proc exec")); - - return event_loop.exec(); -} diff --git a/Userland/Services/LoginServer/CMakeLists.txt b/Userland/Services/LoginServer/CMakeLists.txt deleted file mode 100644 index 2f3b37ca3fc..00000000000 --- a/Userland/Services/LoginServer/CMakeLists.txt +++ /dev/null @@ -1,16 +0,0 @@ -serenity_component( - LoginServer - REQUIRED - TARGETS LoginServer -) - -compile_gml(LoginWindow.gml LoginWindowGML.cpp) - -set(SOURCES - LoginWindow.cpp - LoginWindowGML.cpp - main.cpp -) - -serenity_bin(LoginServer) -target_link_libraries(LoginServer PRIVATE LibCore LibGfx LibGUI LibMain) diff --git a/Userland/Services/LoginServer/LoginWindow.cpp b/Userland/Services/LoginServer/LoginWindow.cpp deleted file mode 100644 index 3ea0e404461..00000000000 --- a/Userland/Services/LoginServer/LoginWindow.cpp +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright (c) 2021, Peter Elliott . - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include - -namespace LoginServer { - -LoginWindow::LoginWindow(GUI::Window* parent) - : GUI::Window(parent) -{ - set_title("Log in to SerenityOS"); - resize(413, 170); - center_on_screen(); - set_resizable(false); - set_minimizable(false); - set_closeable(false); - set_icon(GUI::Icon::default_icon("ladyball"sv).bitmap_for_size(16)); - - auto widget = MUST(LoginServer::Widget::try_create()); - set_main_widget(widget); - m_banner = *widget->find_descendant_of_type_named("banner"); - - m_username = *widget->find_descendant_of_type_named("username"); - m_username->set_focus(true); - m_password = *widget->find_descendant_of_type_named("password"); - - m_log_in_button = *widget->find_descendant_of_type_named("log_in"); - m_log_in_button->on_click = [&](auto) { - if (on_submit) - on_submit(); - }; - m_log_in_button->set_default(true); - - m_fail_message = *widget->find_descendant_of_type_named("fail_message"); - m_username->on_change = [&] { - m_fail_message->set_text({}); - }; - m_password->on_change = [&] { - if (!m_password->text().is_empty()) - m_fail_message->set_text({}); - }; -} - -} diff --git a/Userland/Services/LoginServer/LoginWindow.gml b/Userland/Services/LoginServer/LoginWindow.gml deleted file mode 100644 index 07bf4af6df1..00000000000 --- a/Userland/Services/LoginServer/LoginWindow.gml +++ /dev/null @@ -1,43 +0,0 @@ -@LoginServer::Widget { - fill_with_background_color: true - layout: @GUI::VerticalBoxLayout {} - - @GUI::ImageWidget { - name: "banner" - bitmap: "/res/graphics/brand-banner.png" - auto_resize: true - } - - @GUI::Widget { - layout: @GUI::VerticalBoxLayout { - margins: [8, 8, 8, 8] - } - - @GUI::TextBox { - name: "username" - placeholder: "Username" - } - - @GUI::PasswordBox { - name: "password" - placeholder: "Password" - } - - @GUI::Widget { - layout: @GUI::HorizontalBoxLayout {} - - @GUI::Label { - name: "fail_message" - text_alignment: "CenterLeft" - } - - @GUI::Layout::Spacer {} - - @GUI::Button { - name: "log_in" - text: "Log in" - fixed_width: 60 - } - } - } -} diff --git a/Userland/Services/LoginServer/LoginWindow.h b/Userland/Services/LoginServer/LoginWindow.h deleted file mode 100644 index 446e9e15aa3..00000000000 --- a/Userland/Services/LoginServer/LoginWindow.h +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright (c) 2021, Peter Elliott . - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include - -namespace LoginServer { - -class LoginWindow final : public GUI::Window { - C_OBJECT(LoginWindow); - -public: - virtual ~LoginWindow() override = default; - - Function on_submit; - - ByteString username() const { return m_username->text(); } - void set_username(StringView username) { m_username->set_text(username); } - - ByteString password() const { return m_password->text(); } - void set_password(StringView password) { m_password->set_text(password); } - - void set_fail_message(StringView message) { m_fail_message->set_text(String::from_utf8(message).release_value_but_fixme_should_propagate_errors()); } - -private: - LoginWindow(GUI::Window* parent = nullptr); - - RefPtr m_banner; - RefPtr m_username; - RefPtr m_password; - RefPtr m_fail_message; - RefPtr m_log_in_button; -}; - -} diff --git a/Userland/Services/LoginServer/Widget.h b/Userland/Services/LoginServer/Widget.h deleted file mode 100644 index c4502d6b546..00000000000 --- a/Userland/Services/LoginServer/Widget.h +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright (c) 2023, kleines Filmröllchen - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include - -namespace LoginServer { - -class Widget : public GUI::Widget { - C_OBJECT_ABSTRACT(Widget) - -public: - static ErrorOr> try_create(); - -private: - Widget() = default; -}; - -} diff --git a/Userland/Services/LoginServer/main.cpp b/Userland/Services/LoginServer/main.cpp deleted file mode 100644 index e6e8ae7f026..00000000000 --- a/Userland/Services/LoginServer/main.cpp +++ /dev/null @@ -1,123 +0,0 @@ -/* - * Copyright (c) 2021, Peter Elliott . - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -static void child_process(Core::Account const& account) -{ - pid_t rc = setsid(); - if (rc == -1) { - dbgln("failed to setsid: {}", strerror(errno)); - exit(1); - } - auto result = Core::SessionManagement::create_session_temporary_directory_if_needed(account.uid(), account.gid()); - if (result.is_error()) { - dbgln("Failed to create temporary directory for session: {}", result.error()); - exit(1); - } - - if (auto const result = account.login(); result.is_error()) { - dbgln("failed to switch users: {}", result.error()); - exit(1); - } - - setenv("HOME", account.home_directory().characters(), true); - dbgln("login with sid={}", rc); - - execlp("/bin/SystemServer", "SystemServer", "--user", nullptr); - dbgln("failed to exec SystemServer --user: {}", strerror(errno)); - exit(127); -} - -static void login(Core::Account const& account, LoginServer::LoginWindow& window) -{ - pid_t pid = fork(); - if (pid == 0) - child_process(account); - - int wstatus; - pid_t rc = waitpid(pid, &wstatus, 0); - if (rc == -1) - dbgln("waitpid failed: {}", strerror(errno)); - if (WIFEXITED(wstatus) && WEXITSTATUS(wstatus) != 0) - dbgln("SystemServer exited with non-zero status: {}", WEXITSTATUS(wstatus)); - - window.show(); -} - -ErrorOr serenity_main(Main::Arguments arguments) -{ - auto app = TRY(GUI::Application::create(arguments)); - - TRY(Core::System::pledge("stdio recvfd sendfd cpath chown rpath exec proc id")); - TRY(Core::System::unveil("/home", "r")); - TRY(Core::System::unveil("/tmp", "c")); - TRY(Core::System::unveil("/etc/passwd", "r")); - TRY(Core::System::unveil("/etc/shadow", "r")); - TRY(Core::System::unveil("/etc/group", "r")); - TRY(Core::System::unveil("/bin/SystemServer", "x")); - TRY(Core::System::unveil("/res", "r")); - TRY(Core::System::unveil(nullptr, nullptr)); - - auto window = LoginServer::LoginWindow::construct(); - window->on_submit = [&]() { - auto username = window->username(); - auto password = Core::SecretString::take_ownership(window->password().to_byte_buffer()); - - window->set_password(""sv); - - auto fail_message = "Can't log in: invalid username or password."sv; - - auto account = Core::Account::from_name(username); - if (account.is_error()) { - window->set_fail_message(fail_message); - dbgln("failed graphical login for user {}: {}", username, account.error()); - return; - } - - if (!account.value().authenticate(password)) { - window->set_fail_message(fail_message); - dbgln("failed graphical login for user {}: invalid password", username); - return; - } - - window->set_username(""sv); - window->hide(); - - login(account.value(), *window); - }; - - StringView auto_login; - - Core::ArgsParser args_parser; - args_parser.add_option(auto_login, "automatically log in with no prompt", "auto-login", 'a', "username"); - args_parser.parse(arguments); - - if (auto_login.is_empty()) { - window->show(); - } else { - auto account = Core::Account::from_name(auto_login); - if (account.is_error()) { - dbgln("failed auto-login for user {}: {}", auto_login, account.error()); - return 1; - } - - login(account.value(), *window); - } - - return app->exec(); -} diff --git a/Userland/Services/LookupServer/CMakeLists.txt b/Userland/Services/LookupServer/CMakeLists.txt deleted file mode 100644 index 185d0a5ecda..00000000000 --- a/Userland/Services/LookupServer/CMakeLists.txt +++ /dev/null @@ -1,24 +0,0 @@ -serenity_component( - LookupServer - REQUIRED - TARGETS LookupServer -) - -compile_ipc(LookupServer.ipc LookupServerEndpoint.h) -compile_ipc(LookupClient.ipc LookupClientEndpoint.h) - -set(SOURCES - DNSServer.cpp - LookupServer.cpp - ConnectionFromClient.cpp - MulticastDNS.cpp - main.cpp -) - -set(GENERATED_SOURCES - LookupServerEndpoint.h - LookupClientEndpoint.h -) - -serenity_bin(LookupServer) -target_link_libraries(LookupServer PRIVATE LibCore LibDNS LibIPC LibMain) diff --git a/Userland/Services/LookupServer/ConnectionFromClient.cpp b/Userland/Services/LookupServer/ConnectionFromClient.cpp deleted file mode 100644 index a162c5e93ab..00000000000 --- a/Userland/Services/LookupServer/ConnectionFromClient.cpp +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright (c) 2021, Sergey Bugaev - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "ConnectionFromClient.h" -#include "LookupServer.h" -#include -#include - -namespace LookupServer { - -using namespace DNS; - -static HashMap> s_connections; - -ConnectionFromClient::ConnectionFromClient(NonnullOwnPtr socket, int client_id) - : IPC::ConnectionFromClient(*this, move(socket), client_id) -{ - s_connections.set(client_id, *this); -} - -void ConnectionFromClient::die() -{ - s_connections.remove(client_id()); -} - -Messages::LookupServer::LookupNameResponse ConnectionFromClient::lookup_name(ByteString const& name) -{ - auto maybe_answers = LookupServer::the().lookup(name, RecordType::A); - if (maybe_answers.is_error()) { - dbgln("LookupServer: Failed to lookup A record: {}", maybe_answers.error()); - return { 1, {} }; - } - - auto answers = maybe_answers.release_value(); - Vector addresses; - for (auto& answer : answers) { - addresses.append(answer.record_data()); - } - return { 0, move(addresses) }; -} - -Messages::LookupServer::LookupAddressResponse ConnectionFromClient::lookup_address(ByteString const& address) -{ - if (address.length() != 4) - return { 1, ByteString() }; - IPv4Address ip_address { (u8 const*)address.characters() }; - auto name = ByteString::formatted("{}.{}.{}.{}.in-addr.arpa", - ip_address[3], - ip_address[2], - ip_address[1], - ip_address[0]); - - auto maybe_answers = LookupServer::the().lookup(name, RecordType::PTR); - if (maybe_answers.is_error()) { - dbgln("LookupServer: Failed to lookup PTR record: {}", maybe_answers.error()); - return { 1, ByteString() }; - } - - auto answers = maybe_answers.release_value(); - if (answers.is_empty()) - return { 1, ByteString() }; - return { 0, answers[0].record_data() }; -} -} diff --git a/Userland/Services/LookupServer/ConnectionFromClient.h b/Userland/Services/LookupServer/ConnectionFromClient.h deleted file mode 100644 index c3bc9d3a14d..00000000000 --- a/Userland/Services/LookupServer/ConnectionFromClient.h +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright (c) 2021, Sergey Bugaev - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include - -namespace LookupServer { - -class ConnectionFromClient final - : public IPC::ConnectionFromClient { - C_OBJECT(ConnectionFromClient); - -public: - virtual ~ConnectionFromClient() override = default; - - virtual void die() override; - -private: - explicit ConnectionFromClient(NonnullOwnPtr, int client_id); - - virtual Messages::LookupServer::LookupNameResponse lookup_name(ByteString const&) override; - virtual Messages::LookupServer::LookupAddressResponse lookup_address(ByteString const&) override; -}; - -} diff --git a/Userland/Services/LookupServer/DNSServer.cpp b/Userland/Services/LookupServer/DNSServer.cpp deleted file mode 100644 index afc642d824d..00000000000 --- a/Userland/Services/LookupServer/DNSServer.cpp +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright (c) 2021, Sergey Bugaev - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "DNSServer.h" -#include "LookupServer.h" -#include -#include - -namespace LookupServer { - -using namespace DNS; - -DNSServer::DNSServer(Core::EventReceiver* parent) - : Core::UDPServer(parent) -{ - bind(IPv4Address(), 53); - on_ready_to_receive = [this]() { - auto result = handle_client(); - if (result.is_error()) { - dbgln("DNSServer: Failed to handle client: {}", result.error()); - } - }; -} - -ErrorOr DNSServer::handle_client() -{ - sockaddr_in client_address; - auto buffer = TRY(receive(1024, client_address)); - auto request = TRY(Packet::from_raw_packet(buffer)); - - if (!request.is_query()) { - dbgln("It's not a request"); - return {}; - } - - LookupServer& lookup_server = LookupServer::the(); - - Packet response; - response.set_is_response(); - response.set_id(request.id()); - - for (auto& question : request.questions()) { - if (question.class_code() != RecordClass::IN) - continue; - response.add_question(question); - auto answers = TRY(lookup_server.lookup(question.name(), question.record_type())); - for (auto& answer : answers) { - response.add_answer(answer); - } - } - - if (response.answer_count() == 0) - response.set_code(Packet::Code::NXDOMAIN); - else - response.set_code(Packet::Code::NOERROR); - - buffer = TRY(response.to_byte_buffer()); - - TRY(send(buffer, client_address)); - return {}; -} - -} diff --git a/Userland/Services/LookupServer/DNSServer.h b/Userland/Services/LookupServer/DNSServer.h deleted file mode 100644 index b4e4d483a76..00000000000 --- a/Userland/Services/LookupServer/DNSServer.h +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Copyright (c) 2021, Sergey Bugaev - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include - -namespace LookupServer { - -class DNSServer : public Core::UDPServer { - C_OBJECT(DNSServer) - -private: - explicit DNSServer(Core::EventReceiver* parent = nullptr); - - ErrorOr handle_client(); -}; - -} diff --git a/Userland/Services/LookupServer/LookupClient.ipc b/Userland/Services/LookupServer/LookupClient.ipc deleted file mode 100644 index 6a4d465b82f..00000000000 --- a/Userland/Services/LookupServer/LookupClient.ipc +++ /dev/null @@ -1,3 +0,0 @@ -endpoint LookupClient -{ -} diff --git a/Userland/Services/LookupServer/LookupServer.cpp b/Userland/Services/LookupServer/LookupServer.cpp deleted file mode 100644 index b3945b91fb8..00000000000 --- a/Userland/Services/LookupServer/LookupServer.cpp +++ /dev/null @@ -1,359 +0,0 @@ -/* - * Copyright (c) 2018-2021, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "LookupServer.h" -#include "ConnectionFromClient.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace LookupServer { - -static LookupServer* s_the; -// NOTE: This is the TTL we return for the hostname or answers from /etc/hosts. -static constexpr u32 s_static_ttl = 86400; - -LookupServer& LookupServer::the() -{ - VERIFY(s_the); - return *s_the; -} - -LookupServer::LookupServer() -{ - VERIFY(s_the == nullptr); - s_the = this; - - auto config = Core::ConfigFile::open_for_system("LookupServer").release_value_but_fixme_should_propagate_errors(); - dbgln("Using network config file at {}", config->filename()); - m_nameservers = config->read_entry("DNS", "Nameservers", "1.1.1.1,1.0.0.1").split(','); - - load_etc_hosts(); - - auto maybe_file_watcher = Core::FileWatcher::create(); - // NOTE: If this happens during startup, something is very wrong. - if (maybe_file_watcher.is_error()) { - dbgln("Core::FileWatcher::create(): {}", maybe_file_watcher.error()); - VERIFY_NOT_REACHED(); - } - m_file_watcher = maybe_file_watcher.release_value(); - - m_file_watcher->on_change = [this](auto&) { - dbgln("Reloading '/etc/hosts' because it was changed."); - load_etc_hosts(); - }; - - auto result = m_file_watcher->add_watch("/etc/hosts", Core::FileWatcherEvent::Type::ContentModified | Core::FileWatcherEvent::Type::Deleted); - // NOTE: If this happens during startup, something is very wrong. - if (result.is_error()) { - dbgln("Core::FileWatcher::add_watch(): {}", result.error()); - VERIFY_NOT_REACHED(); - } else if (!result.value()) { - dbgln("Core::FileWatcher::add_watch(): {}", result.value()); - VERIFY_NOT_REACHED(); - } - - if (config->read_bool_entry("DNS", "EnableServer")) { - m_dns_server = DNSServer::construct(this); - // TODO: drop root privileges here. - } - m_mdns = MulticastDNS::construct(this); - - m_server = MUST(IPC::MultiServer::try_create()); -} - -void LookupServer::load_etc_hosts() -{ - auto new_hosts_or_error = this->try_load_etc_hosts(); - if (new_hosts_or_error.is_error()) - dbgln("Ignoring '/etc/hosts', keeping old values"); - else - m_etc_hosts = new_hosts_or_error.release_value(); -} - -ErrorOr, Name::Traits>> LookupServer::try_load_etc_hosts() -{ - HashMap, Name::Traits> map; - auto add_answer = [&map](Name const& name, RecordType record_type, ByteString data) -> ErrorOr { - // FIXME: Since try_ensure does not return a reference to the contained value, we have to - // retrieve it separately. This is a try_ensure bug that should be fixed. - TRY(map.try_ensure(name, []() { return Vector {}; })); - auto& entry = map.find(name)->value; - return entry.try_empend(name, record_type, RecordClass::IN, s_static_ttl, move(data), false); - }; - - auto file_or_error = Core::File::open("/etc/hosts"sv, Core::File::OpenMode::Read); - if (file_or_error.is_error()) { - dbgln("Failed to open '/etc/hosts'"); - return file_or_error.release_error(); - } - auto file = TRY(Core::InputBufferedFile::create(file_or_error.release_value())); - auto buffer = TRY(ByteBuffer::create_uninitialized(1 * KiB)); - - u32 line_number = 0; - while (TRY(file->can_read_line())) { - auto original_line = TRY(file->read_line(buffer)); - ++line_number; - if (original_line.is_empty()) - break; - auto trimmed_line = original_line.trim_whitespace(); - auto replaced_line = trimmed_line.replace(" "sv, "\t"sv, ReplaceMode::All); - auto fields = replaced_line.split_view('\t'); - - if (fields.size() < 2) { - dbgln("Failed to parse line {} from '/etc/hosts': '{}'", line_number, original_line); - continue; - } - - if (fields.size() > 2) - dbgln("Line {} from '/etc/hosts' ('{}') has more than two parts, only the first two are used.", line_number, original_line); - - auto maybe_address = IPv4Address::from_string(fields[0]); - if (!maybe_address.has_value()) { - dbgln("Failed to parse line {} from '/etc/hosts': '{}'", line_number, original_line); - continue; - } - - auto raw_addr = maybe_address->to_in_addr_t(); - - Name name { fields[1] }; - TRY(add_answer(name, RecordType::A, ByteString { (char const*)&raw_addr, sizeof(raw_addr) })); - - StringBuilder builder; - TRY(builder.try_append(maybe_address->to_byte_string_reversed())); - TRY(builder.try_append(".in-addr.arpa"sv)); - TRY(add_answer(builder.to_byte_string(), RecordType::PTR, name.as_string())); - } - - return map; -} - -static ByteString get_hostname() -{ - char buffer[_POSIX_HOST_NAME_MAX]; - VERIFY(gethostname(buffer, sizeof(buffer)) == 0); - return buffer; -} - -ErrorOr> LookupServer::lookup(Name const& name, RecordType record_type) -{ - dbgln_if(LOOKUPSERVER_DEBUG, "Got request for '{}'", name.as_string()); - - Vector answers; - auto add_answer = [&](Answer const& answer) { - Answer answer_with_original_case { - name, - answer.type(), - answer.class_code(), - answer.ttl(), - answer.record_data(), - answer.mdns_cache_flush(), - }; - answers.append(answer_with_original_case); - }; - - // First, try /etc/hosts. - if (auto local_answers = m_etc_hosts.find(name); local_answers != m_etc_hosts.end()) { - for (auto& answer : local_answers->value) { - if (answer.type() == record_type) - add_answer(answer); - } - if (!answers.is_empty()) - return answers; - } - - // Second, try the hostname. - // NOTE: We don't cache the hostname since it could change during runtime. - if (record_type == RecordType::A && get_hostname() == name) { - IPv4Address address = { 127, 0, 0, 1 }; - auto raw_address = address.to_in_addr_t(); - Answer answer { name, RecordType::A, RecordClass::IN, s_static_ttl, ByteString { (char const*)&raw_address, sizeof(raw_address) }, false }; - answers.append(move(answer)); - return answers; - } - - // Third, try our cache. - if (auto cached_answers = m_lookup_cache.find(name); cached_answers != m_lookup_cache.end()) { - for (auto& answer : cached_answers->value) { - // TODO: Actually remove expired answers from the cache. - if (answer.type() == record_type && !answer.has_expired()) { - dbgln_if(LOOKUPSERVER_DEBUG, "Cache hit: {} -> {}", name.as_string(), answer.record_data()); - add_answer(answer); - } - } - if (!answers.is_empty()) - return answers; - } - - // Fourth, look up .local names using mDNS instead of DNS nameservers. - if (name.as_string().ends_with(".local"sv)) { - answers = TRY(m_mdns->lookup(name, record_type)); - for (auto& answer : answers) - put_in_cache(answer); - return answers; - } - - // Fifth, ask the upstream nameservers. - for (auto& nameserver : m_nameservers) { - dbgln_if(LOOKUPSERVER_DEBUG, "Doing lookup using nameserver '{}'", nameserver); - bool did_get_response = false; - int retries = 3; - Vector upstream_answers; - do { - auto upstream_answers_or_error = lookup(name, nameserver, did_get_response, record_type); - if (upstream_answers_or_error.is_error()) - continue; - upstream_answers = upstream_answers_or_error.release_value(); - if (did_get_response) - break; - } while (--retries); - if (!upstream_answers.is_empty()) { - for (auto& answer : upstream_answers) { - add_answer(answer); - put_in_cache(answer); - } - break; - } else { - if (!did_get_response) - dbgln("Never got a response from '{}', trying next nameserver", nameserver); - else - dbgln("Received response from '{}' but no result(s), trying next nameserver", nameserver); - } - } - - // Sixth, fail. - if (answers.is_empty()) { - dbgln("Tried all nameservers but never got a response :("); - return Vector {}; - } - - return answers; -} - -ErrorOr> LookupServer::lookup(Name const& name, ByteString const& nameserver, bool& did_get_response, RecordType record_type, ShouldRandomizeCase should_randomize_case) -{ - Packet request; - request.set_is_query(); - request.set_id(get_random_uniform(UINT16_MAX)); - Name name_in_question = name; - if (should_randomize_case == ShouldRandomizeCase::Yes) - name_in_question.randomize_case(); - request.add_question({ name_in_question, record_type, RecordClass::IN, false }); - - auto buffer = TRY(request.to_byte_buffer()); - - auto udp_socket = TRY(Core::UDPSocket::connect(nameserver, 53, Duration::from_seconds(1))); - TRY(udp_socket->set_blocking(true)); - - TRY(udp_socket->write_until_depleted(buffer)); - - u8 response_buffer[4096]; - auto nrecv = TRY(udp_socket->read_some({ response_buffer, sizeof(response_buffer) })).size(); - if (udp_socket->is_eof()) - return Vector {}; - - did_get_response = true; - - auto response_or_error = Packet::from_raw_packet({ response_buffer, nrecv }); - if (response_or_error.is_error()) - return Vector {}; - - auto response = response_or_error.release_value(); - - if (response.id() != request.id()) { - dbgln("LookupServer: ID mismatch ({} vs {}) :(", response.id(), request.id()); - return Vector {}; - } - - if (response.code() == Packet::Code::REFUSED) { - if (should_randomize_case == ShouldRandomizeCase::Yes) { - // Retry with 0x20 case randomization turned off. - return lookup(name, nameserver, did_get_response, record_type, ShouldRandomizeCase::No); - } - return Vector {}; - } - - if (response.question_count() != request.question_count()) { - dbgln("LookupServer: Question count ({} vs {}) :(", response.question_count(), request.question_count()); - return Vector {}; - } - - // Verify the questions in our request and in their response match, ignoring case. - for (size_t i = 0; i < request.question_count(); ++i) { - auto& request_question = request.questions()[i]; - auto& response_question = response.questions()[i]; - bool match = request_question.class_code() == response_question.class_code() - && request_question.record_type() == response_question.record_type() - && request_question.name().as_string().equals_ignoring_ascii_case(response_question.name().as_string()); - if (!match) { - dbgln("Request and response questions do not match"); - dbgln(" Request: name=_{}_, type={}, class={}", request_question.name().as_string(), response_question.record_type(), response_question.class_code()); - dbgln(" Response: name=_{}_, type={}, class={}", response_question.name().as_string(), response_question.record_type(), response_question.class_code()); - return Vector {}; - } - } - - if (response.answer_count() < 1) { - dbgln("LookupServer: No answers :("); - return Vector {}; - } - - Vector answers; - for (auto& answer : response.answers()) { - put_in_cache(answer); - if (answer.type() != record_type) - continue; - answers.append(answer); - } - - return answers; -} - -void LookupServer::put_in_cache(Answer const& answer) -{ - if (answer.has_expired()) - return; - - // Prevent the cache from growing too big. - // TODO: Evict least used entries. - if (m_lookup_cache.size() >= 256) - m_lookup_cache.remove(m_lookup_cache.begin()); - - auto it = m_lookup_cache.find(answer.name()); - if (it == m_lookup_cache.end()) - m_lookup_cache.set(answer.name(), { answer }); - else { - if (answer.mdns_cache_flush()) { - auto now = time(nullptr); - - it->value.remove_all_matching([&](Answer const& other_answer) { - if (other_answer.type() != answer.type() || other_answer.class_code() != answer.class_code()) - return false; - - if (other_answer.received_time() >= now - 1) - return false; - - dbgln_if(LOOKUPSERVER_DEBUG, "Removing cache entry: {}", other_answer.name()); - return true; - }); - } - it->value.append(answer); - } -} - -} diff --git a/Userland/Services/LookupServer/LookupServer.h b/Userland/Services/LookupServer/LookupServer.h deleted file mode 100644 index 88d70a616dc..00000000000 --- a/Userland/Services/LookupServer/LookupServer.h +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include "ConnectionFromClient.h" -#include "DNSServer.h" -#include "MulticastDNS.h" -#include -#include -#include -#include -#include - -namespace LookupServer { - -using namespace DNS; - -class LookupServer final : public Core::EventReceiver { - C_OBJECT(LookupServer); - -public: - static LookupServer& the(); - ErrorOr> lookup(Name const& name, RecordType record_type); - -private: - LookupServer(); - - ErrorOr, Name::Traits>> try_load_etc_hosts(); - void load_etc_hosts(); - void put_in_cache(Answer const&); - - ErrorOr> lookup(Name const& hostname, ByteString const& nameserver, bool& did_get_response, RecordType record_type, ShouldRandomizeCase = ShouldRandomizeCase::Yes); - - OwnPtr> m_server; - RefPtr m_dns_server; - RefPtr m_mdns; - Vector m_nameservers; - RefPtr m_file_watcher; - HashMap, Name::Traits> m_etc_hosts; - HashMap, Name::Traits> m_lookup_cache; -}; - -} diff --git a/Userland/Services/LookupServer/LookupServer.ipc b/Userland/Services/LookupServer/LookupServer.ipc deleted file mode 100644 index 01b03a45a46..00000000000 --- a/Userland/Services/LookupServer/LookupServer.ipc +++ /dev/null @@ -1,7 +0,0 @@ -// Keep the name synchronized with LibC/netdb.cpp, constant 'lookup_server_endpoint_magic'. -endpoint LookupServer -{ - // Keep these definitions synchronized with gethostbyname and gethostbyaddr in netdb.cpp - lookup_name(ByteString name) => (int code, Vector addresses) - lookup_address(ByteString address) => (int code, ByteString name) -} diff --git a/Userland/Services/LookupServer/MulticastDNS.cpp b/Userland/Services/LookupServer/MulticastDNS.cpp deleted file mode 100644 index cb5ff0f49e2..00000000000 --- a/Userland/Services/LookupServer/MulticastDNS.cpp +++ /dev/null @@ -1,190 +0,0 @@ -/* - * Copyright (c) 2021, Sergey Bugaev - * Copyright (c) 2022, Alexander Narsudinov - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "MulticastDNS.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace LookupServer { - -MulticastDNS::MulticastDNS(Core::EventReceiver* parent) - : Core::UDPServer(parent) - , m_hostname("courage.local") -{ - char buffer[_POSIX_HOST_NAME_MAX]; - if (gethostname(buffer, sizeof(buffer)) < 0) { - perror("gethostname"); - } else { - m_hostname = ByteString::formatted("{}.local", buffer); - } - - u8 zero = 0; - if (setsockopt(fd(), IPPROTO_IP, IP_MULTICAST_LOOP, &zero, 1) < 0) - perror("setsockopt(IP_MULTICAST_LOOP)"); - ip_mreq mreq = { - mdns_addr.sin_addr, - { htonl(INADDR_ANY) }, - }; - if (setsockopt(fd(), IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) < 0) - perror("setsockopt(IP_ADD_MEMBERSHIP)"); - - bind(IPv4Address(), 5353); - - on_ready_to_receive = [this]() { - if (auto result = handle_packet(); result.is_error()) { - dbgln("Failed to handle packet: {}", result.error()); - } - }; - - // TODO: Announce on startup. We cannot just call announce() here, - // because it races with the network interfaces getting configured. -} - -ErrorOr MulticastDNS::handle_packet() -{ - auto buffer = TRY(receive(1024)); - auto packet = TRY(Packet::from_raw_packet(buffer)); - if (packet.is_query()) - handle_query(packet); - return {}; -} - -void MulticastDNS::handle_query(Packet const& packet) -{ - bool should_reply = false; - - for (auto& question : packet.questions()) - if (question.name() == m_hostname) - should_reply = true; - - if (!should_reply) - return; - - announce(); -} - -void MulticastDNS::announce() -{ - Packet response; - response.set_is_response(); - response.set_code(Packet::Code::NOERROR); - response.set_authoritative_answer(true); - response.set_recursion_desired(false); - response.set_recursion_available(false); - - for (auto& address : local_addresses()) { - auto raw_addr = address.to_in_addr_t(); - Answer answer { - m_hostname, - RecordType::A, - RecordClass::IN, - 120, - ByteString { (char const*)&raw_addr, sizeof(raw_addr) }, - true, - }; - response.add_answer(answer); - } - - if (emit_packet(response).is_error()) - perror("Failed to emit response packet"); -} - -ErrorOr MulticastDNS::emit_packet(Packet const& packet, sockaddr_in const* destination) -{ - auto buffer = TRY(packet.to_byte_buffer()); - if (!destination) - destination = &mdns_addr; - - return send(buffer, *destination); -} - -Vector MulticastDNS::local_addresses() const -{ - auto file_or_error = Core::File::open("/sys/kernel/net/adapters"sv, Core::File::OpenMode::Read); - if (file_or_error.is_error()) { - dbgln("Failed to open /sys/kernel/net/adapters: {}", file_or_error.error()); - return {}; - } - auto file_contents_or_error = file_or_error.value()->read_until_eof(); - if (file_or_error.is_error()) { - dbgln("Cannot read /sys/kernel/net/adapters: {}", file_contents_or_error.error()); - return {}; - } - auto json_or_error = JsonValue::from_string(file_contents_or_error.value()); - if (json_or_error.is_error()) { - dbgln("Invalid JSON(?) in /sys/kernel/net/adapters: {}", json_or_error.error()); - return {}; - } - - Vector addresses; - - json_or_error.value().as_array().for_each([&addresses](auto& value) { - auto if_object = value.as_object(); - auto address = if_object.get_byte_string("ipv4_address"sv).value_or({}); - auto ipv4_address = IPv4Address::from_string(address); - // Skip unconfigured interfaces. - if (!ipv4_address.has_value()) - return; - // Skip loopback adapters. - if (ipv4_address.value()[0] == IN_LOOPBACKNET) - return; - addresses.append(ipv4_address.value()); - }); - - return addresses; -} - -ErrorOr> MulticastDNS::lookup(Name const& name, RecordType record_type) -{ - Packet request; - request.set_is_query(); - request.set_recursion_desired(false); - request.add_question({ name, record_type, RecordClass::IN, false }); - - TRY(emit_packet(request)); - Vector answers; - - // FIXME: It would be better not to block - // the main loop while we wait for a response. - while (true) { - auto pfd = pollfd { fd(), POLLIN, 0 }; - auto rc = TRY(Core::System::poll({ &pfd, 1 }, 1000)); - if (rc == 0) { - // Timed out. - return Vector {}; - } - auto buffer = TRY(receive(1024)); - if (buffer.is_empty()) - return Vector {}; - auto packet_or_error = Packet::from_raw_packet(buffer); - if (packet_or_error.is_error()) { - dbgln("Got an invalid mDNS packet: {}", packet_or_error.release_error()); - continue; - } - auto packet = packet_or_error.release_value(); - - if (packet.is_query()) - continue; - - for (auto& answer : packet.answers()) - if (answer.name() == name && answer.type() == record_type) - answers.append(answer); - if (!answers.is_empty()) - return answers; - } -} - -} diff --git a/Userland/Services/LookupServer/MulticastDNS.h b/Userland/Services/LookupServer/MulticastDNS.h deleted file mode 100644 index 65a5580541e..00000000000 --- a/Userland/Services/LookupServer/MulticastDNS.h +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright (c) 2021, Sergey Bugaev - * Copyright (c) 2022, Alexander Narsudinov - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include -#include - -namespace LookupServer { - -using namespace DNS; - -class MulticastDNS : public Core::UDPServer { - C_OBJECT(MulticastDNS) -public: - ErrorOr> lookup(Name const&, RecordType record_type); - -private: - explicit MulticastDNS(Core::EventReceiver* parent = nullptr); - - void announce(); - ErrorOr emit_packet(Packet const&, sockaddr_in const* destination = nullptr); - - ErrorOr handle_packet(); - void handle_query(Packet const&); - - Vector local_addresses() const; - - Name m_hostname; - - static constexpr sockaddr_in mdns_addr { -#if defined(AK_OS_BSD_GENERIC) || defined(AK_OS_GNU_HURD) - .sin_len = sizeof(struct sockaddr_in), -#endif - .sin_family = AF_INET, - // htons(5353) - .sin_port = 0xe914, - // 224.0.0.251 - .sin_addr = { 0xfb0000e0 }, - .sin_zero = { 0 } - }; -}; - -} diff --git a/Userland/Services/LookupServer/main.cpp b/Userland/Services/LookupServer/main.cpp deleted file mode 100644 index 9420a8eb023..00000000000 --- a/Userland/Services/LookupServer/main.cpp +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "LookupServer.h" -#include -#include -#include -#include - -ErrorOr serenity_main(Main::Arguments) -{ - TRY(Core::System::pledge("stdio accept unix inet rpath")); - Core::EventLoop event_loop; - auto server = TRY(LookupServer::LookupServer::try_create()); - - TRY(Core::System::pledge("stdio accept inet rpath")); - TRY(Core::System::unveil("/sys/kernel/net/adapters", "r")); - TRY(Core::System::unveil("/etc/hosts", "r")); - TRY(Core::System::unveil(nullptr, nullptr)); - return event_loop.exec(); -} diff --git a/Userland/Services/NetworkServer/CMakeLists.txt b/Userland/Services/NetworkServer/CMakeLists.txt deleted file mode 100644 index 81f93b9d3da..00000000000 --- a/Userland/Services/NetworkServer/CMakeLists.txt +++ /dev/null @@ -1,12 +0,0 @@ -serenity_component( - NetworkServer - REQUIRED - TARGETS NetworkServer -) - -set(SOURCES - main.cpp -) - -serenity_bin(NetworkServer) -target_link_libraries(NetworkServer PRIVATE LibCore LibConfig LibMain) diff --git a/Userland/Services/NetworkServer/main.cpp b/Userland/Services/NetworkServer/main.cpp deleted file mode 100644 index f212619ca84..00000000000 --- a/Userland/Services/NetworkServer/main.cpp +++ /dev/null @@ -1,105 +0,0 @@ -/* - * Copyright (c) 2022, Maciej - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -ErrorOr serenity_main(Main::Arguments) -{ - TRY(Core::System::pledge("stdio recvfd sendfd rpath unix exec proc")); - TRY(Core::System::unveil("/sys/kernel/net", "r")); - TRY(Core::System::unveil("/bin/DHCPClient", "x")); - TRY(Core::System::unveil("/etc/Network.ini", "r")); - TRY(Core::System::unveil("/bin/ifconfig", "x")); - TRY(Core::System::unveil("/bin/killall", "x")); - TRY(Core::System::unveil("/bin/route", "x")); - TRY(Core::System::unveil(nullptr, nullptr)); - - auto config_file = TRY(Core::ConfigFile::open_for_system("Network")); - - auto proc_net_adapters_file = TRY(Core::File::open("/sys/kernel/net/adapters"sv, Core::File::OpenMode::Read)); - auto data = TRY(proc_net_adapters_file->read_until_eof()); - JsonParser parser(data); - JsonValue proc_net_adapters_json = TRY(parser.parse()); - - // Kill all previously running DHCPServers that may manage to re-assign the IP - // address before we clear it manually. - MUST(Core::command("killall"sv, { "DHCPClient" }, {})); - - auto groups = config_file->groups(); - dbgln("Interfaces to configure: {}", groups); - - struct InterfaceConfig { - bool enabled = false; - bool dhcp_enabled = false; - ByteString ipv4_address = "0.0.0.0"sv; - ByteString ipv4_netmask = "0.0.0.0"sv; - ByteString ipv4_gateway = "0.0.0.0"sv; - }; - - Vector interfaces_with_dhcp_enabled; - proc_net_adapters_json.as_array().for_each([&](auto& value) { - auto& if_object = value.as_object(); - auto ifname = if_object.get_byte_string("name"sv).value_or({}); - - if (ifname == "loop"sv) - return; - - InterfaceConfig config; - if (!groups.contains_slow(ifname)) { - dbgln("Config for interface {} doesn't exist, enabling DHCP for it", ifname); - interfaces_with_dhcp_enabled.append(ifname); - } else { - config.enabled = config_file->read_bool_entry(ifname, "Enabled"sv, true); - config.dhcp_enabled = config_file->read_bool_entry(ifname, "DHCP"sv, false); - if (!config.dhcp_enabled) { - config.ipv4_address = config_file->read_entry(ifname, "IPv4Address"sv, "0.0.0.0"sv); - config.ipv4_netmask = config_file->read_entry(ifname, "IPv4Netmask"sv, "0.0.0.0"sv); - config.ipv4_gateway = config_file->read_entry(ifname, "IPv4Gateway"sv, "0.0.0.0"sv); - } - } - if (config.enabled) { - if (config.dhcp_enabled) - interfaces_with_dhcp_enabled.append(ifname); - else { - // FIXME: Propagate errors - // FIXME: Do this asynchronously - dbgln("Setting up interface {} statically ({}/{})", ifname, config.ipv4_address, config.ipv4_netmask); - MUST(Core::command("ifconfig"sv, { "-a", ifname.characters(), "-i", config.ipv4_address.characters(), "-m", config.ipv4_netmask.characters() }, {})); - if (config.ipv4_gateway != "0.0.0.0") { - MUST(Core::command("route"sv, { "del", "-n", "0.0.0.0", "-m", "0.0.0.0", "-i", ifname }, {})); - MUST(Core::command("route"sv, { "add", "-n", "0.0.0.0", "-m", "0.0.0.0", "-g", config.ipv4_gateway, "-i", ifname }, {})); - } - } - } else { - // FIXME: Propagate errors - dbgln("Disabling interface {}", ifname); - MUST(Core::command("route"sv, { "del", "-n", "0.0.0.0", "-m", "0.0.0.0", "-i", ifname }, {})); - MUST(Core::command("ifconfig"sv, { "-a", ifname.characters(), "-i", "0.0.0.0", "-m", "0.0.0.0" }, {})); - } - }); - - if (!interfaces_with_dhcp_enabled.is_empty()) { - dbgln("Running DHCPClient for interfaces: {}", interfaces_with_dhcp_enabled); - Vector args; - char dhcp_client_arg[] = "DHCPClient"; - args.append(dhcp_client_arg); - for (auto& iface : interfaces_with_dhcp_enabled) - args.append(const_cast(iface.characters())); - args.append(nullptr); - - auto dhcp_client_pid = TRY(Core::System::posix_spawnp("DHCPClient"sv, nullptr, nullptr, args.data(), environ)); - TRY(Core::System::disown(dhcp_client_pid)); - } - return 0; -} diff --git a/Userland/Services/NotificationServer/CMakeLists.txt b/Userland/Services/NotificationServer/CMakeLists.txt deleted file mode 100644 index a3fe65f7b55..00000000000 --- a/Userland/Services/NotificationServer/CMakeLists.txt +++ /dev/null @@ -1,24 +0,0 @@ -serenity_component( - NotificationServer - REQUIRED - TARGETS NotificationServer -) - -compile_gml(NotificationWidget.gml NotificationWidgetGML.cpp) -compile_ipc(NotificationServer.ipc NotificationServerEndpoint.h) -compile_ipc(NotificationClient.ipc NotificationClientEndpoint.h) - -set(SOURCES - ConnectionFromClient.cpp - main.cpp - NotificationWidgetGML.cpp - NotificationWindow.cpp -) - -set(GENERATED_SOURCES - NotificationServerEndpoint.h - NotificationClientEndpoint.h -) - -serenity_bin(NotificationServer) -target_link_libraries(NotificationServer PRIVATE LibCore LibGfx LibGUI LibIPC LibMain) diff --git a/Userland/Services/NotificationServer/ConnectionFromClient.cpp b/Userland/Services/NotificationServer/ConnectionFromClient.cpp deleted file mode 100644 index 7881d791fbc..00000000000 --- a/Userland/Services/NotificationServer/ConnectionFromClient.cpp +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright (c) 2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "ConnectionFromClient.h" -#include "NotificationWindow.h" -#include -#include - -namespace NotificationServer { - -static HashMap> s_connections; - -ConnectionFromClient::ConnectionFromClient(NonnullOwnPtr client_socket, int client_id) - : IPC::ConnectionFromClient(*this, move(client_socket), client_id) -{ - s_connections.set(client_id, *this); -} - -void ConnectionFromClient::die() -{ - s_connections.remove(client_id()); -} - -void ConnectionFromClient::show_notification(String const& text, String const& title, Gfx::ShareableBitmap const& icon) -{ - auto window = NotificationWindow::construct(client_id(), text, title, icon); - window->show(); -} - -void ConnectionFromClient::close_notification() -{ - auto window = NotificationWindow::get_window_by_id(client_id()); - if (window) { - window->close(); - } -} - -Messages::NotificationServer::UpdateNotificationIconResponse ConnectionFromClient::update_notification_icon(Gfx::ShareableBitmap const& icon) -{ - auto window = NotificationWindow::get_window_by_id(client_id()); - if (window) { - window->set_image(icon); - } - return !!window; -} - -Messages::NotificationServer::UpdateNotificationTextResponse ConnectionFromClient::update_notification_text(String const& text, String const& title) -{ - auto window = NotificationWindow::get_window_by_id(client_id()); - if (window) { - window->set_text(text); - window->set_title(title); - } - return !!window; -} - -Messages::NotificationServer::IsShowingResponse ConnectionFromClient::is_showing() -{ - auto window = NotificationWindow::get_window_by_id(client_id()); - return !!window; -} - -} diff --git a/Userland/Services/NotificationServer/ConnectionFromClient.h b/Userland/Services/NotificationServer/ConnectionFromClient.h deleted file mode 100644 index 0db3c48b3e1..00000000000 --- a/Userland/Services/NotificationServer/ConnectionFromClient.h +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright (c) 2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include - -namespace NotificationServer { - -class ConnectionFromClient final : public IPC::ConnectionFromClient { - C_OBJECT(ConnectionFromClient) -public: - ~ConnectionFromClient() override = default; - - virtual void die() override; - -private: - explicit ConnectionFromClient(NonnullOwnPtr, int client_id); - - virtual void show_notification(String const&, String const&, Gfx::ShareableBitmap const&) override; - virtual void close_notification() override; - virtual Messages::NotificationServer::UpdateNotificationIconResponse update_notification_icon(Gfx::ShareableBitmap const&) override; - virtual Messages::NotificationServer::UpdateNotificationTextResponse update_notification_text(String const&, String const&) override; - virtual Messages::NotificationServer::IsShowingResponse is_showing() override; -}; - -} diff --git a/Userland/Services/NotificationServer/NotificationClient.ipc b/Userland/Services/NotificationServer/NotificationClient.ipc deleted file mode 100644 index 2cb72f2b678..00000000000 --- a/Userland/Services/NotificationServer/NotificationClient.ipc +++ /dev/null @@ -1,5 +0,0 @@ -#include - -endpoint NotificationClient -{ -} diff --git a/Userland/Services/NotificationServer/NotificationServer.ipc b/Userland/Services/NotificationServer/NotificationServer.ipc deleted file mode 100644 index 9b1828e0519..00000000000 --- a/Userland/Services/NotificationServer/NotificationServer.ipc +++ /dev/null @@ -1,14 +0,0 @@ -#include - -endpoint NotificationServer -{ - show_notification(String text, String title, Gfx::ShareableBitmap icon) => () - - update_notification_text(String text, String title) => (bool still_showing) - - update_notification_icon(Gfx::ShareableBitmap icon) => (bool still_showing) - - is_showing() => (bool still_showing) - - close_notification() => () -} diff --git a/Userland/Services/NotificationServer/NotificationWidget.gml b/Userland/Services/NotificationServer/NotificationWidget.gml deleted file mode 100644 index afc6d821def..00000000000 --- a/Userland/Services/NotificationServer/NotificationWidget.gml +++ /dev/null @@ -1,29 +0,0 @@ -@NotificationServer::NotificationWidget { - layout: @GUI::HorizontalBoxLayout { - margins: [8] - spacing: 6 - } - fill_with_background_color: true - - @GUI::ImageWidget { - name: "icon" - visible: false - } - - @GUI::Widget { - layout: @GUI::VerticalBoxLayout {} - - @GUI::Label { - name: "title" - font_weight: "Bold" - text_alignment: "CenterLeft" - preferred_height: "shrink" - max_height: "shrink" - } - - @GUI::Label { - name: "text" - text_alignment: "TopLeft" - } - } -} diff --git a/Userland/Services/NotificationServer/NotificationWidget.h b/Userland/Services/NotificationServer/NotificationWidget.h deleted file mode 100644 index f05c8d167fd..00000000000 --- a/Userland/Services/NotificationServer/NotificationWidget.h +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright (c) 2024, Sam Atkins - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include - -namespace NotificationServer { - -class NotificationWidget : public GUI::Widget { - C_OBJECT_ABSTRACT(NotificationWidget) -public: - static ErrorOr> try_create(); - virtual ~NotificationWidget() override = default; - -private: - NotificationWidget() = default; -}; - -} diff --git a/Userland/Services/NotificationServer/NotificationWindow.cpp b/Userland/Services/NotificationServer/NotificationWindow.cpp deleted file mode 100644 index 47167ff7357..00000000000 --- a/Userland/Services/NotificationServer/NotificationWindow.cpp +++ /dev/null @@ -1,163 +0,0 @@ -/* - * Copyright (c) 2020, Andreas Kling - * Copyright (c) 2024, Sam Atkins - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "NotificationWindow.h" -#include "NotificationWidget.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace NotificationServer { - -static OrderedHashMap> s_windows; - -static void update_notification_window_locations(Gfx::IntRect const& screen_rect) -{ - Optional last_window_rect; - for (auto& window_entry : s_windows) { - auto& window = window_entry.value; - Gfx::IntPoint new_window_location; - if (last_window_rect.has_value()) - new_window_location = last_window_rect.value().bottom_left().moved_down(9); - else - new_window_location = screen_rect.top_right().translated(-window->rect().width() - 24 - 1, 7); - if (window->rect().location() != new_window_location) { - window->move_to(new_window_location); - } - last_window_rect = window->rect(); - } -} - -NotificationWindow::NotificationWindow(i32 client_id, String const& text, String const& title, Gfx::ShareableBitmap const& icon) -{ - m_id = client_id; - - set_window_type(GUI::WindowType::Notification); - set_resizable(false); - set_minimizable(false); - - Optional lowest_notification_rect_on_screen; - for (auto& window_entry : s_windows) { - auto& window = window_entry.value; - if (!lowest_notification_rect_on_screen.has_value() - || (window->rect().y() > lowest_notification_rect_on_screen.value().y())) - lowest_notification_rect_on_screen = window->rect(); - } - - s_windows.set(m_id, this); - - Gfx::IntRect rect; - rect.set_width(220); - rect.set_height(40); - rect.set_location(GUI::Desktop::the().rect().top_right().translated(-rect.width() - 24 - 1, 7)); - - if (lowest_notification_rect_on_screen.has_value()) - rect.set_location(lowest_notification_rect_on_screen.value().bottom_left().moved_down(9)); - - set_rect(rect); - - auto widget = NotificationServer::NotificationWidget::try_create().release_value_but_fixme_should_propagate_errors(); - set_main_widget(widget); - - m_image = widget->find_descendant_of_type_named("icon"sv); - m_image->set_visible(icon.is_valid()); - if (icon.is_valid()) { - m_image->set_bitmap(icon.bitmap()); - } - - m_title_label = widget->find_descendant_of_type_named("title"); - m_title_label->set_text(title); - m_text_label = widget->find_descendant_of_type_named("text"); - m_text_label->set_text(text); - - // FIXME: There used to be code for setting the tooltip here, but since we - // expand the notification now we no longer set the tooltip. Should there be - // a limit to the lines shown in an expanded notification, at which point a - // tooltip should be set? - - on_close = [this] { - s_windows.remove(m_id); - update_notification_window_locations(GUI::Desktop::the().rect()); - }; -} - -RefPtr NotificationWindow::get_window_by_id(i32 id) -{ - auto window = s_windows.get(id); - return window.value_or(nullptr); -} - -void NotificationWindow::resize_to_fit_text() -{ - // FIXME: It would be good if Labels could size themselves based on their available width, but for now, we have to - // do the calculation manually. - Gfx::TextLayout text_layout { m_text_label->font(), Utf8View { m_text_label->text() }, m_text_label->rect().to_type() }; - auto line_count = text_layout.lines(Gfx::TextElision::None, m_text_label->text_wrapping()).size(); - - auto line_height = m_text_label->font().preferred_line_height(); - auto text_height = line_height * line_count; - m_text_label->set_height(text_height); - set_height(m_title_label->height() + GUI::Layout::default_spacing + text_height + main_widget()->layout()->margins().vertical_total()); -} - -void NotificationWindow::enter_event(Core::Event&) -{ - m_hovering = true; - resize_to_fit_text(); - move_to_front(); - update_notification_window_locations(GUI::Desktop::the().rect()); -} - -void NotificationWindow::leave_event(Core::Event&) -{ - m_hovering = false; - m_text_label->set_height(40 - (m_title_label->height() + GUI::Layout::default_spacing + main_widget()->layout()->margins().vertical_total())); - set_height(40); - update_notification_window_locations(GUI::Desktop::the().rect()); -} - -void NotificationWindow::set_text(String const& value) -{ - m_text_label->set_text(value); - if (m_hovering) - resize_to_fit_text(); -} - -void NotificationWindow::set_title(String const& value) -{ - m_title_label->set_text(value); -} - -void NotificationWindow::set_image(Gfx::ShareableBitmap const& image) -{ - m_image->set_visible(image.is_valid()); - if (image.is_valid()) { - m_image->set_bitmap(image.bitmap()); - } -} - -void NotificationWindow::set_height(int height) -{ - auto rect = this->rect(); - rect.set_height(height); - set_rect(rect); -} - -void NotificationWindow::screen_rects_change_event(GUI::ScreenRectsChangeEvent& event) -{ - update_notification_window_locations(event.rects()[event.main_screen_index()]); -} - -} diff --git a/Userland/Services/NotificationServer/NotificationWindow.h b/Userland/Services/NotificationServer/NotificationWindow.h deleted file mode 100644 index 1f90198bd53..00000000000 --- a/Userland/Services/NotificationServer/NotificationWindow.h +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright (c) 2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include - -namespace NotificationServer { - -class NotificationWindow final : public GUI::Window { - C_OBJECT(NotificationWindow); - -public: - virtual ~NotificationWindow() override = default; - - void set_text(String const&); - void set_title(String const&); - void set_image(Gfx::ShareableBitmap const&); - - static RefPtr get_window_by_id(i32 id); - -protected: - virtual void enter_event(Core::Event&) override; - virtual void leave_event(Core::Event&) override; - -private: - NotificationWindow(i32 client_id, String const& text, String const& title, Gfx::ShareableBitmap const&); - - virtual void screen_rects_change_event(GUI::ScreenRectsChangeEvent&) override; - - void resize_to_fit_text(); - void set_height(int); - - i32 m_id; - - GUI::Label* m_text_label; - GUI::Label* m_title_label; - GUI::ImageWidget* m_image; - bool m_hovering { false }; -}; - -} diff --git a/Userland/Services/NotificationServer/main.cpp b/Userland/Services/NotificationServer/main.cpp deleted file mode 100644 index 424241601f0..00000000000 --- a/Userland/Services/NotificationServer/main.cpp +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright (c) 2020-2021, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "ConnectionFromClient.h" -#include -#include -#include -#include - -ErrorOr serenity_main(Main::Arguments arguments) -{ - TRY(Core::System::pledge("stdio recvfd sendfd accept rpath unix")); - - auto app = TRY(GUI::Application::create(arguments)); - auto server = TRY(IPC::MultiServer::try_create()); - - TRY(Core::System::unveil("/res", "r")); - TRY(Core::System::unveil(nullptr, nullptr)); - TRY(Core::System::pledge("stdio recvfd sendfd accept rpath")); - - return app->exec(); -} diff --git a/Userland/Services/SpiceAgent/CMakeLists.txt b/Userland/Services/SpiceAgent/CMakeLists.txt deleted file mode 100644 index fc5315c14ad..00000000000 --- a/Userland/Services/SpiceAgent/CMakeLists.txt +++ /dev/null @@ -1,15 +0,0 @@ -serenity_component( - SpiceAgent - TARGETS SpiceAgent -) - -set(SOURCES - main.cpp - FileTransferOperation.cpp - Message.cpp - SpiceAgent.cpp -) - -serenity_bin(SpiceAgent) -target_link_libraries(SpiceAgent PRIVATE LibCore LibDesktop LibFileSystem LibGfx LibGUI LibMain LibURL) -add_dependencies(SpiceAgent Clipboard) diff --git a/Userland/Services/SpiceAgent/ChunkHeader.h b/Userland/Services/SpiceAgent/ChunkHeader.h deleted file mode 100644 index 041b1218b7e..00000000000 --- a/Userland/Services/SpiceAgent/ChunkHeader.h +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright (c) 2023, Caoimhe Byrne - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include - -namespace SpiceAgent { - -class [[gnu::packed]] ChunkHeader { -public: - // Indicates where the message has come from - enum class Port : u32 { - Client = 1, - - // There are currently no messages which are meant for the server, so all messages sent by the agent (us) with this port are discarded. - Server - }; - - ChunkHeader(Port port, u32 size) - : m_port(port) - , m_size(size) - { - } - - Port port() const { return m_port; } - u32 size() const { return m_size; } - -private: - Port m_port { Port::Client }; - u32 m_size { 0 }; -}; - -} - -template<> -struct AK::Traits : public AK::DefaultTraits { - static constexpr bool is_trivially_serializable() { return true; } -}; diff --git a/Userland/Services/SpiceAgent/FileTransferOperation.cpp b/Userland/Services/SpiceAgent/FileTransferOperation.cpp deleted file mode 100644 index 54f2eaaec20..00000000000 --- a/Userland/Services/SpiceAgent/FileTransferOperation.cpp +++ /dev/null @@ -1,92 +0,0 @@ -/* - * Copyright (c) 2023, Caoimhe Byrne - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "FileTransferOperation.h" -#include "SpiceAgent.h" -#include -#include -#include -#include - -namespace SpiceAgent { - -ErrorOr> FileTransferOperation::create(FileTransferStartMessage& message) -{ - // Attempt to construct a path. - StringBuilder destination_builder; - TRY(destination_builder.try_append(Core::StandardPaths::downloads_directory())); - TRY(destination_builder.try_append('/')); - TRY(destination_builder.try_append(message.metadata().name)); - - auto destination_path = TRY(destination_builder.to_string()); - - // Ensure that the file doesn't already exist, and if it does, remove it. - if (FileSystem::exists(destination_path)) { - // If that "file" is a directory, we should stop doing anything else. - if (FileSystem::is_directory(destination_path)) { - return Error::from_string_literal("The name of the file being transferred is already taken by a directory!"); - } - - TRY(FileSystem::remove(destination_path, FileSystem::RecursionMode::Disallowed)); - } - - auto file = TRY(Core::File::open(destination_path, Core::File::OpenMode::ReadWrite)); - return TRY(AK::adopt_nonnull_ref_or_enomem(new (nothrow) FileTransferOperation(message.id(), message.metadata(), move(file)))); -} - -ErrorOr FileTransferOperation::begin_transfer(SpiceAgent& agent) -{ - // Ensure that we are in the `Pending` status. - if (m_status != Status::Pending) { - return Error::from_string_literal("Attempt to start a file transfer which has already been started!"); - } - - // Send the CanSendData status to the server. - auto status_message = FileTransferStatusMessage(m_id, FileTransferStatus::CanSendData); - TRY(agent.send_message(status_message)); - - // We are now in the transferring stage! - set_status(Status::Transferring); - - return {}; -} - -ErrorOr FileTransferOperation::complete_transfer(SpiceAgent& agent) -{ - // Ensure that we are in the `Transferring` status. - if (m_status != Status::Transferring) { - return Error::from_string_literal("Attempt to call `on_data_received` on a file transfer which has already been completed!"); - } - - // We are now in the complete stage :^) - set_status(Status::Complete); - - // Send the Success status to the server, since we have received the data, and handled it correctly - auto status_message = FileTransferStatusMessage(m_id, FileTransferStatus::Success); - TRY(agent.send_message(status_message)); - - // Open the file manager for the user :^) - // FIXME: This currently opens a new window for each successful file transfer... - // Is there a way/can we make a way for it to highlight a new file in an already-open window? - Desktop::Launcher::open(URL::create_with_file_scheme(Core::StandardPaths::downloads_directory(), m_metadata.name.to_byte_string())); - - return {}; -} - -ErrorOr FileTransferOperation::on_data_received(FileTransferDataMessage& message) -{ - // Ensure that we are in the `Transferring` status. - if (m_status != Status::Transferring) { - return Error::from_string_literal("Attempt to call `on_data_received` on a file transfer which has already been completed!"); - } - - // Attempt to write more data to the file. - TRY(m_destination->write_until_depleted(message.contents())); - - return {}; -} - -} diff --git a/Userland/Services/SpiceAgent/FileTransferOperation.h b/Userland/Services/SpiceAgent/FileTransferOperation.h deleted file mode 100644 index 2e0d7b1079d..00000000000 --- a/Userland/Services/SpiceAgent/FileTransferOperation.h +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright (c) 2023, Caoimhe Byrne - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include "Message.h" -#include -#include - -namespace SpiceAgent { - -// Forward declaration -class SpiceAgent; - -class FileTransferOperation : public RefCounted { -public: - enum class Status { - // If we haven't accepted the transfer yet. - Pending, - - // If we are awaiting data from the server. - Transferring, - - // If we've received all the data. - Complete - }; - - static ErrorOr> create(FileTransferStartMessage& message); - - // Fired by the SpiceAgent when it wants the data transfer to begin. - ErrorOr begin_transfer(SpiceAgent& agent); - - // Fired by SpiceAgent when we have received all of the data needed for this transfer. - ErrorOr complete_transfer(SpiceAgent& agent); - - // Fired by the SpiceAgent when it recieves data related to this transfer. - ErrorOr on_data_received(FileTransferDataMessage& message); - -private: - // All file transfers start off as Pending. - FileTransferOperation(u32 id, FileTransferStartMessage::Metadata metadata, NonnullOwnPtr destination) - : m_destination(move(destination)) - , m_metadata(move(metadata)) - , m_id(id) - , m_status(Status::Pending) - { - } - - void set_status(Status const& value) - { - m_status = value; - } - - NonnullOwnPtr m_destination; - FileTransferStartMessage::Metadata m_metadata; - - u32 m_id { 0 }; - Status m_status { Status::Pending }; -}; - -} diff --git a/Userland/Services/SpiceAgent/Message.cpp b/Userland/Services/SpiceAgent/Message.cpp deleted file mode 100644 index 8bdcf044951..00000000000 --- a/Userland/Services/SpiceAgent/Message.cpp +++ /dev/null @@ -1,291 +0,0 @@ -/* - * Copyright (c) 2023, Caoimhe Byrne - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "Message.h" -#include -#include -#include - -namespace SpiceAgent { - -ErrorOr clipboard_data_type_to_mime_type(ClipboardDataType data_type) -{ - switch (data_type) { - case ClipboardDataType::Text: - return "text/plain"_string; - - case ClipboardDataType::PNG: - return "image/png"_string; - - case ClipboardDataType::BMP: - return "image/bitmap"_string; - - case ClipboardDataType::JPG: - return "image/jpeg"_string; - - case ClipboardDataType::TIFF: - return "image/tiff"_string; - - default: - return Error::from_string_literal("Unable to determine mime type!"); - } -} - -ErrorOr clipboard_data_type_from_raw_value(u32 value) -{ - if (value >= to_underlying(ClipboardDataType::__End)) { - return Error::from_string_literal("Unsupported clipboard type"); - } - - return static_cast(value); -} - -ErrorOr clipboard_data_type_from_mime_type(String const& mime_type) -{ - if (mime_type == "text/plain") - return ClipboardDataType::Text; - - // We treat image/x-serenityos as a standard PNG here - if (mime_type == "image/png" || mime_type == "image/x-serenityos") - return ClipboardDataType::PNG; - - if (mime_type == "image/bitmap") - return ClipboardDataType::BMP; - - if (mime_type == "image/jpeg") - return ClipboardDataType::JPG; - - if (mime_type == "image/tiff") - return ClipboardDataType::TIFF; - - return Error::from_string_literal("Unable to determine clipboard data type!"); -} - -ErrorOr AnnounceCapabilitiesMessage::read_from_stream(AK::Stream& stream) -{ - // If this message is a capabilities request, we don't have to parse anything else. - auto is_requesting = TRY(stream.read_value()) == 1; - if (is_requesting) { - return AnnounceCapabilitiesMessage(is_requesting); - } - - return Error::from_string_literal("Unexpected non-requesting announce capabilities message received!"); -} - -ErrorOr AnnounceCapabilitiesMessage::write_to_stream(AK::Stream& stream) -{ - TRY(stream.write_value(is_request())); - - // Each bit in this u32 indicates if a certain capability is enabled or not. - u32 capabilities_bits = 0; - for (auto capability : capabilities()) { - // FIXME: At the moment, we only support up to 32 capabilities as the Spice protocol - // only contains 17 capabilities. - auto capability_value = to_underlying(capability); - VERIFY(capability_value < 32); - - capabilities_bits |= 1 << capability_value; - } - - TRY(stream.write_value(capabilities_bits)); - - return {}; -} - -ErrorOr AnnounceCapabilitiesMessage::debug_description() -{ - StringBuilder builder; - TRY(builder.try_append("AnnounceCapabilities { "sv)); - TRY(builder.try_appendff("is_request = {}, ", is_request())); - TRY(builder.try_appendff("capabilities.size() = {}", capabilities().size())); - TRY(builder.try_append(" }"sv)); - return builder.to_string(); -} - -ErrorOr ClipboardGrabMessage::read_from_stream(AK::Stream& stream) -{ - auto types = Vector(); - while (!stream.is_eof()) { - auto value = TRY(stream.read_value()); - types.append(TRY(clipboard_data_type_from_raw_value(value))); - } - - return ClipboardGrabMessage(types); -} - -ErrorOr ClipboardGrabMessage::write_to_stream(AK::Stream& stream) -{ - for (auto type : types()) { - TRY(stream.write_value(type)); - } - - return {}; -} - -ErrorOr ClipboardGrabMessage::debug_description() -{ - StringBuilder builder; - TRY(builder.try_append("ClipboardGrabMessage { "sv)); - TRY(builder.try_appendff("types = {}", types())); - TRY(builder.try_append(" }"sv)); - return builder.to_string(); -} - -ErrorOr ClipboardRequestMessage::read_from_stream(AK::Stream& stream) -{ - auto value = TRY(stream.read_value()); - auto type = TRY(clipboard_data_type_from_raw_value(value)); - return ClipboardRequestMessage(type); -} - -ErrorOr ClipboardRequestMessage::write_to_stream(AK::Stream& stream) -{ - TRY(stream.write_value(data_type())); - return {}; -} - -ErrorOr ClipboardRequestMessage::debug_description() -{ - StringBuilder builder; - TRY(builder.try_append("ClipboardRequest { "sv)); - TRY(builder.try_appendff("data_type = {}", data_type())); - TRY(builder.try_append(" }"sv)); - return builder.to_string(); -} - -ErrorOr ClipboardMessage::read_from_stream(AK::Stream& stream) -{ - auto value = TRY(stream.read_value()); - if (value >= to_underlying(ClipboardDataType::__End)) { - return Error::from_string_literal("Unsupported clipboard type"); - } - - auto type = static_cast(value); - auto contents = TRY(stream.read_until_eof()); - return ClipboardMessage(type, contents); -} - -ErrorOr ClipboardMessage::write_to_stream(AK::Stream& stream) -{ - TRY(stream.write_value(data_type())); - TRY(stream.write_until_depleted(contents())); - - return {}; -} - -ErrorOr ClipboardMessage::debug_description() -{ - StringBuilder builder; - TRY(builder.try_append("Clipboard { "sv)); - TRY(builder.try_appendff("data_type = {}, ", data_type())); - TRY(builder.try_appendff("contents.size() = {}", contents().size())); - TRY(builder.try_append(" }"sv)); - return builder.to_string(); -} - -ErrorOr FileTransferStartMessage::read_from_stream(AK::Stream& stream) -{ - auto id = TRY(stream.read_value()); - - auto metadata_bytes = TRY(stream.read_until_eof()); - auto metadata_content = TRY(String::from_utf8(metadata_bytes)); - - // TODO: We need some sort of INIParser, or we need to make Core::ConfigFile not depend on having an actual Core::File - // The first line in the file should always be `[vdagent-file-xfer]` - auto lines = TRY(metadata_content.split('\n')); - if (lines.is_empty() || lines.at(0) != "[vdagent-file-xfer]") { - return Error::from_string_literal("Failed to parse file transfer metadata"); - } - - Optional name = {}; - Optional size = {}; - - for (auto const& line : lines) { - // Ignore the header, we already assume that it is [vdagent-file-xfer] - if (line.starts_with('[')) - continue; - - if (line.starts_with_bytes("name="sv)) { - auto parsed_name = TRY(line.replace("name="sv, ""sv, ReplaceMode::FirstOnly)); - if (parsed_name.is_empty()) { - return Error::from_string_literal("Failed to parse file name!"); - } - - name = parsed_name; - } - - if (line.starts_with_bytes("size="sv)) { - auto size_string = TRY(line.replace("size="sv, ""sv, ReplaceMode::FirstOnly)); - size = size_string.to_number(TrimWhitespace::Yes); - } - } - - // Verify that we actually parsed any data - if (!name.has_value() || !size.has_value()) { - return Error::from_string_literal("Invalid transfer start message received!"); - } - - return FileTransferStartMessage(id, Metadata { name.release_value(), size.release_value() }); -} - -ErrorOr FileTransferStartMessage::debug_description() -{ - StringBuilder builder; - TRY(builder.try_append("FileTransferStart { "sv)); - TRY(builder.try_appendff("id = {}, ", id())); - TRY(builder.try_appendff("metadata = Metadata {{ name = {}, size = {} }}", metadata().name, metadata().size)); - TRY(builder.try_append(" }"sv)); - return builder.to_string(); -} - -ErrorOr FileTransferStatusMessage::read_from_stream(AK::Stream& stream) -{ - auto id = TRY(stream.read_value()); - auto status = TRY(stream.read_value()); - - return FileTransferStatusMessage(id, status); -} - -ErrorOr FileTransferStatusMessage::write_to_stream(AK::Stream& stream) -{ - TRY(stream.write_value(id())); - TRY(stream.write_value(status())); - - return {}; -} - -ErrorOr FileTransferStatusMessage::debug_description() -{ - StringBuilder builder; - TRY(builder.try_append("FileTransferStatus { "sv)); - TRY(builder.try_appendff("id = {}, ", id())); - TRY(builder.try_appendff("status = {}", status())); - TRY(builder.try_append(" }"sv)); - return builder.to_string(); -} - -ErrorOr FileTransferDataMessage::read_from_stream(AK::Stream& stream) -{ - auto id = TRY(stream.read_value()); - auto size = TRY(stream.read_value()); - - auto contents = TRY(ByteBuffer::create_uninitialized(size)); - TRY(stream.read_until_filled(contents)); - - return FileTransferDataMessage(id, move(contents)); -} - -ErrorOr FileTransferDataMessage::debug_description() -{ - StringBuilder builder; - TRY(builder.try_append("FileTransferData { "sv)); - TRY(builder.try_appendff("id = {}, ", id())); - TRY(builder.try_appendff("contents.size() = {}", contents().size())); - TRY(builder.try_append(" }"sv)); - return builder.to_string(); -} - -} diff --git a/Userland/Services/SpiceAgent/Message.h b/Userland/Services/SpiceAgent/Message.h deleted file mode 100644 index ed3c70fe60b..00000000000 --- a/Userland/Services/SpiceAgent/Message.h +++ /dev/null @@ -1,365 +0,0 @@ -/* - * Copyright (c) 2023, Caoimhe Byrne - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include - -namespace SpiceAgent { - -static constexpr u32 AGENT_PROTOCOL = 1; - -// Used to communicate what the client/or server is capable of. -// Not a lot of documentation is available for all of these, but the headers contain some information: -// https://gitlab.freedesktop.org/spice/spice-protocol/-/blob/master/spice/vd_agent.h -enum class Capability : u32 { - MouseState = 0, - MonitorsConfig, - Reply, - Clipboard, - DisplayConfig, - ClipboardByDemand, - ClipboardSelection, - SparseMonitorsConfig, - GuestLineEndLF, - GuestLineEndCRLF, - MaxClipboard, - AudioVolumeSync, - MonitorsConfigPosition, - FileTransferDisabled, - FileTransferDetailedErrors, - GraphicsCardInfo, - ClipboardNoReleaseOnRegrab, - ClipboardGrabSerial -}; - -// Used to describe the type of data which is present on the user's clipboard. -enum class ClipboardDataType : u32 { - None = 0, - Text, - PNG, - BMP, - TIFF, - JPG, - __End -}; - -ErrorOr clipboard_data_type_to_mime_type(ClipboardDataType type); -ErrorOr clipboard_data_type_from_raw_value(u32 value); -ErrorOr clipboard_data_type_from_mime_type(String const& mime_type); - -// Used to describe what state the current file transfer is in -enum class FileTransferStatus : u32 { - CanSendData = 0, - Cancelled, - Error, - Success, - NotEnoughSpace, - SessionLocked, - AgentNotConnected, - Disabled, - __End -}; - -class Message { -public: - // The spice protocol headers contain a bit of documentation about these, but nothing major: - // https://gitlab.freedesktop.org/spice/spice-protocol/-/blob/master/spice/vd_agent.h - enum class Type : u32 { - MouseState = 1, - MonitorsConfig, - Reply, - Clipboard, - DisplayConfig, - AnnounceCapabilities, - ClipboardGrab, - ClipboardRequest, - ClipboardRelease, - FileTransferStart, - FileTransferStatus, - FileTransferData, - Disconnected, - MaxClipboard, - VolumeSync, - GraphicsDeviceInfo - }; - - Message(Type type) - : m_type(type) - { - } - - Type type() { return m_type; } - - virtual ErrorOr debug_description() = 0; - virtual ~Message() = default; - -private: - Type m_type; -}; - -// Sent to the server to tell it what we are capable of. -// See the Capabilities enum to see the available capabilities. -class AnnounceCapabilitiesMessage : public Message { -public: - AnnounceCapabilitiesMessage(bool is_request, Vector capabilities = {}) - : Message(Type::AnnounceCapabilities) - , m_is_request(is_request) - , m_capabilities(move(capabilities)) - { - } - - static ErrorOr read_from_stream(AK::Stream& stream); - - ErrorOr write_to_stream(AK::Stream& stream); - ErrorOr debug_description() override; - - bool is_request() const& { return m_is_request; } - Vector const& capabilities() { return m_capabilities; } - -private: - bool m_is_request { false }; - Vector m_capabilities; -}; - -// Sent/received to tell the server/client that clipboard data is available. -class ClipboardGrabMessage : public Message { -public: - ClipboardGrabMessage(Vector const& types) - : Message(Type::ClipboardGrab) - , m_types(types) - { - } - - static ErrorOr read_from_stream(AK::Stream& stream); - - ErrorOr write_to_stream(AK::Stream& stream); - ErrorOr debug_description() override; - - Vector const& types() { return m_types; } - -private: - Vector m_types; -}; - -// Request clipboard data with the specified type. -class ClipboardRequestMessage : public Message { -public: - ClipboardRequestMessage(ClipboardDataType data_type) - : Message(Type::ClipboardRequest) - , m_data_type(data_type) - { - } - - static ErrorOr read_from_stream(AK::Stream& stream); - - ErrorOr write_to_stream(AK::Stream& stream); - ErrorOr debug_description() override; - - ClipboardDataType data_type() { return m_data_type; } - -private: - ClipboardDataType m_data_type; -}; - -// Used to send the clipboard's contents to the client/server. -class ClipboardMessage : public Message { -public: - ClipboardMessage(ClipboardDataType data_type, ByteBuffer contents) - : Message(Type::Clipboard) - , m_data_type(data_type) - , m_contents(move(contents)) - { - } - - ClipboardMessage(ClipboardMessage const&) = delete; - ClipboardMessage& operator=(ClipboardMessage const&) = delete; - - ClipboardMessage(ClipboardMessage&&) = default; - ClipboardMessage& operator=(ClipboardMessage&&) = default; - - static ErrorOr read_from_stream(AK::Stream& stream); - - ErrorOr write_to_stream(AK::Stream& stream); - ErrorOr debug_description() override; - - ClipboardDataType data_type() { return m_data_type; } - ByteBuffer const& contents() { return m_contents; } - -private: - ClipboardDataType m_data_type; - ByteBuffer m_contents; -}; - -// Sent to the agent to indicate that a file transfer has been requested. -class FileTransferStartMessage : public Message { -public: - struct Metadata { - String name; - u32 size; - }; - - static ErrorOr read_from_stream(AK::Stream& stream); - - ErrorOr debug_description() override; - - u32 id() const { return m_id; } - Metadata const& metadata() { return m_metadata; } - -private: - FileTransferStartMessage(u32 id, FileTransferStartMessage::Metadata const& metadata) - : Message(Type::FileTransferStart) - , m_id(id) - , m_metadata(metadata) - { - } - - u32 m_id { 0 }; - Metadata m_metadata; -}; - -// Sent/recieved to indicate the status of the current file transfer. -class FileTransferStatusMessage : public Message { -public: - FileTransferStatusMessage(u32 id, FileTransferStatus status) - : Message(Type::FileTransferStatus) - , m_id(id) - , m_status(status) - { - } - - static ErrorOr read_from_stream(AK::Stream& stream); - - ErrorOr write_to_stream(AK::Stream& stream); - ErrorOr debug_description() override; - - u32 id() const { return m_id; } - FileTransferStatus const& status() { return m_status; } - -private: - u32 m_id { 0 }; - FileTransferStatus m_status; -}; - -// Contains the file data sent from a file transfer request after it has been approved. -class FileTransferDataMessage : public Message { -public: - static ErrorOr read_from_stream(AK::Stream& stream); - - FileTransferDataMessage(FileTransferDataMessage const&) = delete; - FileTransferDataMessage& operator=(FileTransferDataMessage const&) = delete; - - FileTransferDataMessage(FileTransferDataMessage&&) = default; - FileTransferDataMessage& operator=(FileTransferDataMessage&&) = default; - - ErrorOr debug_description() override; - - u32 id() const { return m_id; } - ByteBuffer const& contents() { return m_contents; } - -private: - FileTransferDataMessage(u32 id, ByteBuffer contents) - : Message(Type::FileTransferData) - , m_id(id) - , m_contents(move(contents)) - { - } - - u32 m_id { 0 }; - ByteBuffer m_contents; -}; - -} - -namespace AK { -template<> -struct Formatter : Formatter { - ErrorOr format(FormatBuilder& builder, SpiceAgent::ClipboardDataType const& header) - { - auto string = "Unknown"sv; - switch (header) { - case SpiceAgent::ClipboardDataType::None: - string = "None"sv; - break; - - case SpiceAgent::ClipboardDataType::Text: - string = "Text"sv; - break; - - case SpiceAgent::ClipboardDataType::PNG: - string = "PNG"sv; - break; - - case SpiceAgent::ClipboardDataType::BMP: - string = "BMP"sv; - break; - - case SpiceAgent::ClipboardDataType::TIFF: - string = "TIFF"sv; - break; - - case SpiceAgent::ClipboardDataType::JPG: - string = "JPG"sv; - break; - - default: - break; - } - - return Formatter::format(builder, string); - } -}; - -template<> -struct Formatter : Formatter { - ErrorOr format(FormatBuilder& builder, SpiceAgent::FileTransferStatus const& header) - { - auto string = "Unknown"sv; - switch (header) { - case SpiceAgent::FileTransferStatus::AgentNotConnected: - string = "AgentNotConnected"sv; - break; - - case SpiceAgent::FileTransferStatus::Cancelled: - string = "Cancelled"sv; - break; - - case SpiceAgent::FileTransferStatus::CanSendData: - string = "CanSendData"sv; - break; - - case SpiceAgent::FileTransferStatus::Disabled: - string = "Disabled"sv; - break; - - case SpiceAgent::FileTransferStatus::Error: - string = "Error"sv; - break; - - case SpiceAgent::FileTransferStatus::NotEnoughSpace: - string = "NotEnoughSpace"sv; - break; - - case SpiceAgent::FileTransferStatus::SessionLocked: - string = "SessionLocked"sv; - break; - - case SpiceAgent::FileTransferStatus::Success: - string = "Success"sv; - break; - - default: - break; - } - - return Formatter::format(builder, string); - } -}; -} diff --git a/Userland/Services/SpiceAgent/MessageHeader.h b/Userland/Services/SpiceAgent/MessageHeader.h deleted file mode 100644 index 7d1806851cc..00000000000 --- a/Userland/Services/SpiceAgent/MessageHeader.h +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright (c) 2023, Caoimhe Byrne - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include "Message.h" -#include -#include -#include - -namespace SpiceAgent { - -// An incoming or outgoing message header. -// This contains information about the message, like how long it is, the type, etc. -class [[gnu::packed]] MessageHeader { -public: - MessageHeader(Message::Type type, u32 data_size, u32 protocol_version = AGENT_PROTOCOL, u64 opaque = 0) - : m_protocol_version(protocol_version) - , m_type(type) - , m_opaque(opaque) - , m_data_size(data_size) - { - } - - Message::Type type() const { return m_type; } - u32 data_size() const { return m_data_size; } - u32 protocol_version() const { return m_protocol_version; } - u64 opaque() const { return m_opaque; } - -private: - // The protocol version being used. - u32 m_protocol_version { AGENT_PROTOCOL }; - - // The message type present in `data`. - Message::Type m_type { Message::Type::MouseState }; - - // A placeholder for message types which only need to pass a single integer as message data, - // for message types which have more data it is always set to 0. - u64 m_opaque { 0 }; - - // The size of the data in the message following this header. - u32 m_data_size { 0 }; -}; - -} - -template<> -struct AK::Traits : public AK::DefaultTraits { - static constexpr bool is_trivially_serializable() { return true; } -}; - -namespace AK { -template<> -struct Formatter : Formatter { - ErrorOr format(FormatBuilder& builder, SpiceAgent::MessageHeader const& header) - { - return Formatter::format(builder, - "MessageHeader {{ protocol_version = {}, type = {}, opaque = {}, data_size = {} }}"sv, - header.protocol_version(), to_underlying(header.type()), header.opaque(), header.data_size()); - } -}; -} diff --git a/Userland/Services/SpiceAgent/SpiceAgent.cpp b/Userland/Services/SpiceAgent/SpiceAgent.cpp deleted file mode 100644 index 77fb2b5ed3a..00000000000 --- a/Userland/Services/SpiceAgent/SpiceAgent.cpp +++ /dev/null @@ -1,295 +0,0 @@ -/* - * Copyright (c) 2021, Kyle Pereira - * Copyright (c) 2023, Caoimhe Byrne - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "SpiceAgent.h" -#include -#include -#include -#include - -namespace SpiceAgent { - -ErrorOr> SpiceAgent::create(StringView device_path) -{ - auto device = TRY(Core::File::open(device_path, Core::File::OpenMode::ReadWrite | Core::File::OpenMode::DontCreate | Core::File::OpenMode::Nonblocking)); - return try_make(move(device), Vector { Capability::ClipboardByDemand }); -} - -SpiceAgent::SpiceAgent(NonnullOwnPtr spice_device, Vector const& capabilities) - : m_spice_device(move(spice_device)) - , m_capabilities(capabilities) -{ - m_notifier = Core::Notifier::construct( - m_spice_device->fd(), - Core::Notifier::Type::Read); - - m_notifier->on_activation = [this] { - auto result = on_message_received(); - if (result.is_error()) { - dbgln("Failed to handle message: {}", result.release_error()); - } - }; -} - -ErrorOr SpiceAgent::start() -{ - // The server usually requests this from us anyways, but there's no harm in sending it. - auto capabilities_message = AnnounceCapabilitiesMessage(false, m_capabilities); - TRY(this->send_message(capabilities_message)); - - GUI::Clipboard::the().on_change = [this](auto const& mime_type) { - auto result = this->on_clipboard_update(String::from_byte_string(mime_type).release_value_but_fixme_should_propagate_errors()); - if (result.is_error()) { - dbgln("Failed to inform the spice server of a clipboard update: {}", result.release_error()); - } - }; - - return {}; -} - -ErrorOr SpiceAgent::on_clipboard_update(String const& mime_type) -{ - // NOTE: If we indicate that we don't support clipboard by demand, the spice server will ignore our messages, - // but it will do some ugly debug logging.. so let's just not send anything instead. - if (!m_capabilities.contains_slow(Capability::ClipboardByDemand)) { - return {}; - } - - // If we just copied something to the clipboard, we shouldn't do anything here. - if (m_clipboard_dirty) { - m_clipboard_dirty = false; - return {}; - } - - // If the clipboard has just been cleared, we shouldn't send anything. - if (mime_type.is_empty()) { - return {}; - } - - // Notify the spice server about new content being available. - auto clipboard_data_type = TRY(clipboard_data_type_from_mime_type(mime_type)); - auto message = ClipboardGrabMessage({ clipboard_data_type }); - TRY(this->send_message(message)); - - return {}; -} - -ErrorOr SpiceAgent::send_clipboard_contents(ClipboardDataType data_type) -{ - auto data_and_type = GUI::Clipboard::the().fetch_data_and_type(); - auto requested_mime_type = TRY(clipboard_data_type_to_mime_type(data_type)); - - // We have an exception for `image/x-serenityos`, where we treat it as a PNG when talking to the spice server. - auto is_serenity_image = data_and_type.mime_type == "image/x-serenityos" && data_type == ClipboardDataType::PNG; - if (!is_serenity_image && requested_mime_type.to_byte_string() != data_and_type.mime_type) { - // If the requested mime type doesn't match what's on the clipboard, we won't send anything back. - return Error::from_string_literal("Requested mime type doesn't match the clipboard's contents!"); - } - - // If the mime type is `image/x-serenityos`, we need to encode the image that's on the clipboard as a PNG. - auto clipboard_data = data_and_type.data; - if (is_serenity_image) { - auto bitmap = data_and_type.as_bitmap(); - clipboard_data = TRY(Gfx::PNGWriter::encode(*bitmap)); - } - - auto message = ClipboardMessage(data_type, move(clipboard_data)); - TRY(this->send_message(move(message))); - - return {}; -} - -ErrorOr SpiceAgent::on_message_received() -{ - auto buffer = TRY(this->read_message_buffer()); - auto stream = FixedMemoryStream(buffer.bytes()); - - auto header = TRY(stream.read_value()); - switch (header.type()) { - case Message::Type::AnnounceCapabilities: { - auto message = TRY(AnnounceCapabilitiesMessage::read_from_stream(stream)); - if (!message.is_request()) - return {}; - - dbgln("The spice server has requested our capabilities"); - - auto capabilities_message = AnnounceCapabilitiesMessage(false, m_capabilities); - TRY(this->send_message(capabilities_message)); - - break; - } - - case Message::Type::ClipboardGrab: { - auto message = TRY(ClipboardGrabMessage::read_from_stream(stream)); - if (message.types().is_empty()) - break; - - auto data_type = message.types().first(); - if (data_type == ClipboardDataType::None) - break; - - dbgln_if(SPICE_AGENT_DEBUG, "The spice server has notified us of new clipboard data of type: {}", data_type); - dbgln_if(SPICE_AGENT_DEBUG, "Sending a request for data of type: {}", data_type); - - auto request_message = ClipboardRequestMessage(data_type); - TRY(this->send_message(request_message)); - - break; - } - - case Message::Type::Clipboard: { - auto message = TRY(ClipboardMessage::read_from_stream(stream)); - if (message.data_type() == ClipboardDataType::None) - break; - - TRY(this->did_receive_clipboard_message(message)); - - break; - } - - case Message::Type::ClipboardRequest: { - dbgln("The spice server has requsted our clipboard's contents"); - - auto message = TRY(ClipboardRequestMessage::read_from_stream(stream)); - TRY(this->send_clipboard_contents(message.data_type())); - - break; - } - - case Message::Type::FileTransferStatus: { - auto message = TRY(FileTransferStatusMessage::read_from_stream(stream)); - dbgln("File transfer {} has been cancelled: {}", message.id(), message.status()); - - m_file_transfer_operations.remove(message.id()); - - break; - } - - // Received when the user drags a file onto the virtual machine. - case Message::Type::FileTransferStart: { - auto message = TRY(FileTransferStartMessage::read_from_stream(stream)); - auto operation = TRY(FileTransferOperation::create(message)); - - // Tell the operation to start the file transfer. - TRY(operation->begin_transfer(*this)); - m_file_transfer_operations.set(message.id(), operation); - - break; - } - - // Received when the server has data related to a file transfer for us. - case Message::Type::FileTransferData: { - auto message = TRY(FileTransferDataMessage::read_from_stream(stream)); - auto optional_operation = m_file_transfer_operations.get(message.id()); - if (!optional_operation.has_value()) { - return Error::from_string_literal("Attempt to supply data to a file transfer operation which doesn't exist!"); - } - - // Inform the operation that we have received new data. - auto* operation = optional_operation.release_value(); - auto result = operation->on_data_received(message); - if (result.is_error()) { - // We can also discard of this transfer operation, since it will be cancelled by the server after our status message. - m_file_transfer_operations.remove(message.id()); - - // Inform the server that the operation has failed - auto status_message = FileTransferStatusMessage(message.id(), FileTransferStatus::Error); - TRY(this->send_message(status_message)); - - return result.release_error(); - } - - // The maximum amount of data that a FileTransferData message can hold is 65536. - // If it's less than 65536, this is the only (or last) message in relation to this transfer. - // Otherwise, we must wait for more data to be received. - auto transfer_is_complete = message.contents().size() < file_transfer_buffer_threshold; - if (!transfer_is_complete) { - return {}; - } - - // The transfer is now complete, let's write the data to the file! - TRY(operation->complete_transfer(*this)); - m_file_transfer_operations.remove(message.id()); - - break; - } - - // We ignore certain messages to prevent it from clogging up the logs. - case Message::Type::MonitorsConfig: - dbgln_if(SPICE_AGENT_DEBUG, "Ignored message: {}", header); - break; - - case Message::Type::Disconnected: - dbgln_if(SPICE_AGENT_DEBUG, "Spice server disconnected"); - if (on_disconnected_from_spice_server) - on_disconnected_from_spice_server(); - break; - - default: - dbgln("Unknown message received: {}", header); - break; - } - - return {}; -} - -ErrorOr SpiceAgent::did_receive_clipboard_message(ClipboardMessage& message) -{ - dbgln_if(SPICE_AGENT_DEBUG, "Attempting to parse clipboard data of type: {}", message.data_type()); - - switch (message.data_type()) { - case ClipboardDataType::Text: { - // The default mime_type for set_data is `text/plain`. - GUI::Clipboard::the().set_data(message.contents()); - break; - } - - // For the image formats, let's try to find a decoder from LibGfx. - case ClipboardDataType::PNG: - case ClipboardDataType::BMP: - case ClipboardDataType::JPG: - case ClipboardDataType::TIFF: { - auto mime_type = TRY(clipboard_data_type_to_mime_type(message.data_type())); - - // FIXME: It should be trivial to make `try_create_for_raw_bytes` take a `StringView` instead of a direct `ByteString`. - auto decoder = TRY(Gfx::ImageDecoder::try_create_for_raw_bytes(message.contents(), mime_type.to_byte_string())); - if (!decoder || (decoder->frame_count() == 0)) { - return Error::from_string_literal("Failed to find a suitable decoder for a pasted image!"); - } - - auto frame = TRY(decoder->frame(0)); - GUI::Clipboard::the().set_bitmap(*frame.image); - - break; - } - - default: - return Error::from_string_literal("Unsupported clipboard data type!"); - } - - m_clipboard_dirty = true; - return {}; -} - -ErrorOr SpiceAgent::read_message_buffer() -{ - auto header = TRY(m_spice_device->read_value()); - auto buffer = TRY(ByteBuffer::create_uninitialized(header.size())); - TRY(m_spice_device->read_until_filled(buffer)); - - // If the header's size is bigger than or equal to 2048, we may have more data incoming. - while (header.size() >= message_buffer_threshold) { - header = TRY(m_spice_device->read_value()); - - auto new_buffer = TRY(ByteBuffer::create_uninitialized(header.size())); - TRY(m_spice_device->read_until_filled(new_buffer)); - TRY(buffer.try_append(new_buffer)); - } - - return buffer; -} -}; diff --git a/Userland/Services/SpiceAgent/SpiceAgent.h b/Userland/Services/SpiceAgent/SpiceAgent.h deleted file mode 100644 index 5b2c3ffa38b..00000000000 --- a/Userland/Services/SpiceAgent/SpiceAgent.h +++ /dev/null @@ -1,88 +0,0 @@ -/* - * Copyright (c) 2021, Kyle Pereira - * Copyright (c) 2023, Caoimhe Byrne - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include "ChunkHeader.h" -#include "FileTransferOperation.h" -#include "Message.h" -#include "MessageHeader.h" -#include -#include -#include -#include -#include - -namespace SpiceAgent { - -// The maximum amount of data that can be contained within a message's buffer. -// If the buffer's length is equal to this, then the next data recieved will be more data from the same buffer. -constexpr u32 message_buffer_threshold = 2048; - -// The maximum amount of data that can be received in one file transfer message -constexpr u32 file_transfer_buffer_threshold = 65536; - -class SpiceAgent { -public: - static ErrorOr> create(StringView device_path); - SpiceAgent(NonnullOwnPtr spice_device, Vector const& capabilities); - - ErrorOr start(); - - template - ErrorOr send_message(T message) - { - // Attempt to write the message's data to a stream. - auto message_stream = AK::AllocatingMemoryStream(); - TRY(message.write_to_stream(message_stream)); - - // Create a header to be sent. - auto message_header_stream = AK::AllocatingMemoryStream(); - auto message_header = MessageHeader(message.type(), message_stream.used_buffer_size()); - TRY(message_header_stream.write_value(message_header)); - - // The length given in the chunk header is the length of the message header, and the message combined - auto length = message_header_stream.used_buffer_size() + message_stream.used_buffer_size(); - - // Currently, there are no messages from the agent which are meant for the server. - // So, all messages sent by the agent with a port of Port::Server get dropped silently. - auto chunk_header = ChunkHeader(ChunkHeader::Port::Client, length); - TRY(m_spice_device->write_value(chunk_header)); - - // The message's header. - TRY(m_spice_device->write_until_depleted(TRY(message_header_stream.read_until_eof()))); - - // The message content. - TRY(m_spice_device->write_until_depleted(TRY(message_stream.read_until_eof()))); - - return {}; - } - - Function on_disconnected_from_spice_server; - -private: - NonnullOwnPtr m_spice_device; - Vector m_capabilities; - HashMap> m_file_transfer_operations; - - RefPtr m_notifier; - - bool m_clipboard_dirty { false }; - - // Fired when we receive clipboard data from the spice server. - ErrorOr did_receive_clipboard_message(ClipboardMessage& message); - - // Fired when the user's clipboard changes - ErrorOr on_clipboard_update(String const& mime_type); - - // Sends the GUI::Clipboard's current contents to the spice server - ErrorOr send_clipboard_contents(ClipboardDataType data_type); - - ErrorOr on_message_received(); - ErrorOr read_message_buffer(); -}; -} diff --git a/Userland/Services/SpiceAgent/main.cpp b/Userland/Services/SpiceAgent/main.cpp deleted file mode 100644 index c0a1c22f0ea..00000000000 --- a/Userland/Services/SpiceAgent/main.cpp +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright (c) 2021, Kyle Pereira - * Copyright (c) 2023, Caoimhe Byrne - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "SpiceAgent.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -static constexpr auto SPICE_DEVICE = "/dev/hvc0p1"sv; - -ErrorOr serenity_main(Main::Arguments arguments) -{ - if (!FileSystem::exists(SPICE_DEVICE)) { - return Error::from_string_literal("Failed to find spice device file!"); - } - - // We use the application to be able to easily write to the user's clipboard. - auto app = TRY(GUI::Application::create(arguments)); - - TRY(Desktop::Launcher::add_allowed_url(URL::create_with_file_scheme(Core::StandardPaths::downloads_directory()))); - TRY(Desktop::Launcher::seal_allowlist()); - - TRY(Core::System::pledge("unix rpath wpath cpath stdio sendfd recvfd")); - TRY(Core::System::unveil(SPICE_DEVICE, "rw"sv)); - TRY(Core::System::unveil(Core::StandardPaths::downloads_directory(), "rwc"sv)); - TRY(Core::System::unveil(nullptr, nullptr)); - - auto agent = TRY(SpiceAgent::SpiceAgent::create(SPICE_DEVICE)); - - agent->on_disconnected_from_spice_server = [&]() { - app->quit(); - }; - - TRY(agent->start()); - return app->exec(); -} diff --git a/Userland/Services/SystemServer/CMakeLists.txt b/Userland/Services/SystemServer/CMakeLists.txt deleted file mode 100644 index dc871087c82..00000000000 --- a/Userland/Services/SystemServer/CMakeLists.txt +++ /dev/null @@ -1,18 +0,0 @@ -serenity_component( - SystemServer - REQUIRED - TARGETS SystemServer -) - -set(SOURCES - main.cpp - Service.cpp -) - -serenity_bin(SystemServer) -target_link_libraries(SystemServer PRIVATE LibCore LibFileSystem LibMain) - -# Required for conditionally creating hardcoded /dev/kcov entry -if (ENABLE_KERNEL_COVERAGE_COLLECTION) - add_compile_definitions(ENABLE_KERNEL_COVERAGE_COLLECTION) -endif() diff --git a/Userland/Services/SystemServer/Service.cpp b/Userland/Services/SystemServer/Service.cpp deleted file mode 100644 index cde6d0810da..00000000000 --- a/Userland/Services/SystemServer/Service.cpp +++ /dev/null @@ -1,387 +0,0 @@ -/* - * Copyright (c) 2019-2020, Sergey Bugaev - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "Service.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -static HashMap s_service_map; - -Service* Service::find_by_pid(pid_t pid) -{ - auto it = s_service_map.find(pid); - if (it == s_service_map.end()) - return nullptr; - return (*it).value; -} - -ErrorOr Service::setup_socket(SocketDescriptor& socket) -{ - VERIFY(socket.fd == -1); - - // Note: The purpose of this syscall is to remove potential left-over of previous portal. - // The return value is discarded as sockets are not always there, and unlinking a non-existent path is considered as a failure. - (void)Core::System::unlink(socket.path); - - TRY(Core::Directory::create(LexicalPath(socket.path).parent(), Core::Directory::CreateDirectories::Yes)); - - // Note: we use SOCK_CLOEXEC here to make sure we don't leak every socket to - // all the clients. We'll make the one we do need to pass down !CLOEXEC later - // after forking off the process. - int const socket_fd = TRY(Core::System::socket(AF_LOCAL, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, 0)); - socket.fd = socket_fd; - - if (m_account.has_value()) { - auto& account = m_account.value(); - TRY(Core::System::fchown(socket_fd, account.uid(), account.gid())); - } - - TRY(Core::System::fchmod(socket_fd, socket.permissions)); - - auto socket_address = Core::SocketAddress::local(socket.path); - auto un_optional = socket_address.to_sockaddr_un(); - if (!un_optional.has_value()) { - dbgln("Socket name {} is too long. BUG! This should have failed earlier!", socket.path); - VERIFY_NOT_REACHED(); - } - auto un = un_optional.value(); - - TRY(Core::System::bind(socket_fd, (sockaddr const*)&un, sizeof(un))); - TRY(Core::System::listen(socket_fd, 16)); - return {}; -} - -ErrorOr Service::setup_sockets() -{ - for (SocketDescriptor& socket : m_sockets) - TRY(setup_socket(socket)); - return {}; -} - -void Service::setup_notifier() -{ - VERIFY(m_lazy); - VERIFY(m_sockets.size() == 1); - VERIFY(!m_socket_notifier); - - m_socket_notifier = Core::Notifier::construct(m_sockets[0].fd, Core::Notifier::Type::Read, this); - m_socket_notifier->on_activation = [this] { - if (auto result = handle_socket_connection(); result.is_error()) - dbgln("{}", result.release_error()); - }; -} - -ErrorOr Service::handle_socket_connection() -{ - VERIFY(m_sockets.size() == 1); - dbgln_if(SERVICE_DEBUG, "Ready to read on behalf of {}", name()); - - int socket_fd = m_sockets[0].fd; - - if (m_accept_socket_connections) { - auto const accepted_fd = TRY(Core::System::accept(socket_fd, nullptr, nullptr)); - - TRY(determine_account(accepted_fd)); - TRY(spawn(accepted_fd)); - TRY(Core::System::close(accepted_fd)); - } else { - remove_child(*m_socket_notifier); - m_socket_notifier = nullptr; - TRY(spawn(socket_fd)); - } - return {}; -} - -ErrorOr Service::activate() -{ - VERIFY(m_pid < 0); - - if (m_lazy) - setup_notifier(); - else - TRY(spawn()); - return {}; -} - -ErrorOr Service::change_privileges() -{ - // NOTE: Dropping privileges makes sense when SystemServer is running - // for a root session. - // This could happen when we need to spawn a Service to serve a client with non-user UID/GID. - // However, in case the user explicitly specified a username via the User= option, then we must - // try to login as at that user, so we can't ignore the failure when it was requested to change - // privileges. - if (auto current_uid = getuid(); m_account.has_value() && m_account.value().uid() != current_uid) { - if (current_uid != 0 && !m_must_login) - return {}; - auto& account = m_account.value(); - if (auto error_or_void = account.login(); error_or_void.is_error()) { - dbgln("Failed to drop privileges (tried to change to GID={}, UID={}), due to {}\n", account.gid(), account.uid(), error_or_void.error()); - exit(1); - } - TRY(Core::Environment::set("HOME"sv, account.home_directory(), Core::Environment::Overwrite::Yes)); - } - return {}; -} - -ErrorOr Service::spawn(int socket_fd) -{ - if (!FileSystem::exists(m_executable_path)) { - dbgln("{}: binary \"{}\" does not exist, skipping service.", name(), m_executable_path); - return Error::from_errno(ENOENT); - } - - dbgln_if(SERVICE_DEBUG, "Spawning {}", name()); - - m_run_timer.start(); - pid_t pid = TRY(Core::System::fork()); - - if (pid == 0) { - // We are the child. - if (m_working_directory.has_value()) - TRY(Core::System::chdir(*m_working_directory)); - - struct sched_param p; - p.sched_priority = m_priority; - int rc = sched_setparam(0, &p); - if (rc < 0) { - perror("sched_setparam"); - VERIFY_NOT_REACHED(); - } - - if (m_stdio_file_path.has_value()) { - close(STDIN_FILENO); - auto const fd = TRY(Core::System::open(*m_stdio_file_path, O_RDWR, 0)); - VERIFY(fd == 0); - - dup2(STDIN_FILENO, STDOUT_FILENO); - dup2(STDIN_FILENO, STDERR_FILENO); - - if (isatty(STDIN_FILENO)) { - ioctl(STDIN_FILENO, TIOCSCTTY); - } - } else { - if (isatty(STDIN_FILENO)) { - ioctl(STDIN_FILENO, TIOCNOTTY); - } - close(STDIN_FILENO); - close(STDOUT_FILENO); - close(STDERR_FILENO); - - auto const fd = TRY(Core::System::open("/dev/null"sv, O_RDWR)); - VERIFY(fd == STDIN_FILENO); - dup2(STDIN_FILENO, STDOUT_FILENO); - dup2(STDIN_FILENO, STDERR_FILENO); - } - - StringBuilder socket_takeover_builder; - - if (socket_fd >= 0) { - // We were spawned by socket activation. We currently only support - // single sockets for socket activation, so make sure that's the case. - VERIFY(m_sockets.size() == 1); - - int fd = dup(socket_fd); - TRY(socket_takeover_builder.try_appendff("{}:{}", m_sockets[0].path, fd)); - } else { - // We were spawned as a regular process, so dup every socket for this - // service and let the service know via SOCKET_TAKEOVER. - for (unsigned i = 0; i < m_sockets.size(); i++) { - SocketDescriptor& socket = m_sockets.at(i); - - int new_fd = dup(socket.fd); - if (i != 0) - TRY(socket_takeover_builder.try_append(';')); - TRY(socket_takeover_builder.try_appendff("{}:{}", socket.path, new_fd)); - } - } - - if (!m_sockets.is_empty()) { - // The new descriptor is !CLOEXEC here. - TRY(Core::Environment::set("SOCKET_TAKEOVER"sv, socket_takeover_builder.string_view(), Core::Environment::Overwrite::Yes)); - } - - TRY(change_privileges()); - - TRY(m_environment.view().for_each_split_view(' ', SplitBehavior::Nothing, [&](auto env) { - return Core::Environment::put(env); - })); - - Vector arguments; - TRY(arguments.try_append(m_executable_path)); - TRY(m_extra_arguments.view().for_each_split_view(' ', SplitBehavior::Nothing, [&](auto arg) { - return arguments.try_append(arg); - })); - - TRY(Core::System::exec(m_executable_path, arguments, Core::System::SearchInPath::No)); - } else if (!m_multi_instance) { - // We are the parent. - m_pid = pid; - s_service_map.set(pid, this); - } - - return {}; -} - -ErrorOr Service::did_exit(int status) -{ - using namespace AK::TimeLiterals; - - VERIFY(m_pid > 0); - VERIFY(!m_multi_instance); - - if (WIFEXITED(status)) - dbgln("Service {} has exited with exit code {}", name(), WEXITSTATUS(status)); - if (WIFSIGNALED(status)) - dbgln("Service {} terminated due to signal {}", name(), WTERMSIG(status)); - - s_service_map.remove(m_pid); - m_pid = -1; - - if (!m_keep_alive) - return {}; - - auto run_time = m_run_timer.elapsed_time(); - bool exited_successfully = WIFEXITED(status) && WEXITSTATUS(status) == 0; - - if (!exited_successfully && run_time < 1_sec) { - switch (m_restart_attempts) { - case 0: - dbgln("Trying again"); - break; - case 1: - dbgln("Third time's the charm?"); - break; - default: - dbgln("Giving up on {}. Good luck!", name()); - return {}; - } - m_restart_attempts++; - } - - TRY(activate()); - return {}; -} - -Service::Service(Core::ConfigFile const& config, StringView name) - : Core::EventReceiver(nullptr) -{ - VERIFY(config.has_group(name)); - - set_name(name); - m_executable_path = config.read_entry(name, "Executable", ByteString::formatted("/bin/{}", this->name())); - m_extra_arguments = config.read_entry(name, "Arguments"); - m_stdio_file_path = config.read_entry_optional(name, "StdIO"); - - auto prio = config.read_entry_optional(name, "Priority"); - if (prio == "low") - m_priority = 10; - else if (prio == "normal" || !prio.has_value()) - m_priority = 30; - else if (prio == "high") - m_priority = 50; - else - VERIFY_NOT_REACHED(); - - m_keep_alive = config.read_bool_entry(name, "KeepAlive"); - m_lazy = config.read_bool_entry(name, "Lazy"); - - m_user = config.read_entry_optional(name, "User"); - if (m_user.has_value()) { - auto result = Core::Account::from_name(*m_user, Core::Account::Read::PasswdOnly); - if (result.is_error()) { - warnln("Failed to resolve user {}: {}", m_user, result.error()); - } else { - m_must_login = true; - m_account = result.value(); - } - } - - m_working_directory = config.read_entry_optional(name, "WorkingDirectory"); - m_environment = config.read_entry(name, "Environment"); - m_system_modes = config.read_entry(name, "SystemModes", "graphical").split(','); - m_multi_instance = config.read_bool_entry(name, "MultiInstance"); - m_accept_socket_connections = config.read_bool_entry(name, "AcceptSocketConnections"); - - ByteString socket_entry = config.read_entry(name, "Socket"); - ByteString socket_permissions_entry = config.read_entry(name, "SocketPermissions", "0600"); - - if (!socket_entry.is_empty()) { - Vector socket_paths = socket_entry.split(','); - Vector socket_perms = socket_permissions_entry.split(','); - m_sockets.ensure_capacity(socket_paths.size()); - - // Need i here to iterate along with all other vectors. - for (unsigned i = 0; i < socket_paths.size(); i++) { - auto const path = Core::SessionManagement::parse_path_with_sid(socket_paths.at(i)); - if (path.is_error()) { - // FIXME: better error handling for this case. - TODO(); - } - - // Socket path (plus NUL) must fit into the structs sent to the Kernel. - VERIFY(path.value().length() < UNIX_PATH_MAX); - - // This is done so that the last permission repeats for every other - // socket. So you can define a single permission, and have it - // be applied for every socket. - mode_t permissions = strtol(socket_perms.at(min(socket_perms.size() - 1, (long unsigned)i)).characters(), nullptr, 8) & 0777; - - m_sockets.empend(path.value(), -1, permissions); - } - } - - // Lazy requires Socket, but only one. - VERIFY(!m_lazy || m_sockets.size() == 1); - // AcceptSocketConnections always requires Socket (single), Lazy, and MultiInstance. - VERIFY(!m_accept_socket_connections || (m_sockets.size() == 1 && m_lazy && m_multi_instance)); - // MultiInstance doesn't work with KeepAlive. - VERIFY(!m_multi_instance || !m_keep_alive); -} - -ErrorOr> Service::try_create(Core::ConfigFile const& config, StringView name) -{ - return TRY(adopt_nonnull_ref_or_enomem(new (nothrow) Service(config, name))); -} - -bool Service::is_enabled_for_system_mode(StringView mode) const -{ - return m_system_modes.contains_slow(mode); -} - -ErrorOr Service::determine_account(int fd) -{ - struct ucred creds = {}; - socklen_t creds_size = sizeof(creds); - TRY(Core::System::getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &creds, &creds_size)); - - m_account = TRY(Core::Account::from_uid(creds.uid, Core::Account::Read::PasswdOnly)); - return {}; -} - -Service::~Service() -{ - for (auto& socket : m_sockets) { - if (auto rc = remove(socket.path.characters()); rc != 0) - dbgln("{}", Error::from_errno(errno)); - } -} diff --git a/Userland/Services/SystemServer/Service.h b/Userland/Services/SystemServer/Service.h deleted file mode 100644 index f1c3f7650bf..00000000000 --- a/Userland/Services/SystemServer/Service.h +++ /dev/null @@ -1,97 +0,0 @@ -/* - * Copyright (c) 2019-2020, Sergey Bugaev - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include -#include - -class Service final : public Core::EventReceiver { - C_OBJECT_ABSTRACT(Service) - -public: - static ErrorOr> try_create(Core::ConfigFile const& config, StringView name); - ~Service(); - - bool is_enabled_for_system_mode(StringView) const; - ErrorOr activate(); - // Note: This is a `status` as in POSIX's wait syscall, not an exit-code. - ErrorOr did_exit(int status); - - ErrorOr setup_sockets(); - - static Service* find_by_pid(pid_t); - -private: - Service(Core::ConfigFile const&, StringView name); - - ErrorOr spawn(int socket_fd = -1); - - ErrorOr determine_account(int fd); - - ErrorOr change_privileges(); - - /// SocketDescriptor describes the details of a single socket that was - /// requested by a service. - struct SocketDescriptor { - /// The path of the socket. - ByteString path; - /// File descriptor of the socket. -1 if the socket hasn't been opened. - int fd { -1 }; - /// File permissions of the socket. - mode_t permissions; - }; - - // Path to the executable. By default this is /bin/{m_name}. - ByteString m_executable_path; - // Extra arguments, starting from argv[1], to pass when exec'ing. - ByteString m_extra_arguments; - // File path to open as stdio fds. - Optional m_stdio_file_path; - int m_priority { 1 }; - // Whether we should re-launch it if it exits. - bool m_keep_alive { false }; - // Whether we should accept connections on the socket and pass the accepted - // (and not listening) socket to the service. This requires a multi-instance - // service. - bool m_accept_socket_connections { false }; - // Whether we should only spawn this service once somebody connects to the socket. - bool m_lazy; - // The name of the user we should run this service as. - Optional m_user; - // The working directory in which to spawn the service. - Optional m_working_directory; - // System modes in which to run this service. By default, this is the graphical mode. - Vector m_system_modes; - // Whether several instances of this service can run at once. - bool m_multi_instance { false }; - // Environment variables to pass to the service. - ByteString m_environment; - // Socket descriptors for this service. - Vector m_sockets; - - // The resolved user account to run this service as. - Optional m_account; - bool m_must_login { false }; - - // For single-instance services, PID of the running instance of this service. - pid_t m_pid { -1 }; - RefPtr m_socket_notifier; - - // Timer since we last spawned the service. - Core::ElapsedTimer m_run_timer; - // How many times we have tried to restart this service, only counting those - // times where it has exited unsuccessfully and too quickly. - int m_restart_attempts { 0 }; - - ErrorOr setup_socket(SocketDescriptor&); - void setup_notifier(); - ErrorOr handle_socket_connection(); -}; diff --git a/Userland/Services/SystemServer/main.cpp b/Userland/Services/SystemServer/main.cpp deleted file mode 100644 index 44adea4c7ac..00000000000 --- a/Userland/Services/SystemServer/main.cpp +++ /dev/null @@ -1,318 +0,0 @@ -/* - * Copyright (c) 2018-2021, Andreas Kling - * Copyright (c) 2021, Peter Elliott - * Copyright (c) 2023, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "Service.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -static constexpr StringView text_system_mode = "text"sv; -static constexpr StringView selftest_system_mode = "self-test"sv; -static constexpr StringView graphical_system_mode = "graphical"sv; -ByteString g_system_mode = graphical_system_mode; -Vector> g_services; - -// NOTE: This handler ensures that the destructor of g_services is called. -static void sigterm_handler(int) -{ - exit(0); -} - -static void sigchld_handler(int) -{ - for (;;) { - int status = 0; - pid_t pid = waitpid(-1, &status, WNOHANG); - if (pid < 0) { - perror("waitpid"); - break; - } - if (pid == 0) - break; - - dbgln_if(SYSTEMSERVER_DEBUG, "Reaped child with pid {}, exit status {}", pid, status); - - Service* service = Service::find_by_pid(pid); - if (service == nullptr) { - // This can happen for multi-instance services. - continue; - } - - if (auto result = service->did_exit(status); result.is_error()) - dbgln("{}: {}", service->name(), result.release_error()); - } -} - -namespace SystemServer { - -static ErrorOr determine_system_mode() -{ - ArmedScopeGuard declare_text_mode_on_failure([&] { - // Note: Only if the mode is not set to self-test, degrade it to text mode. - if (g_system_mode != selftest_system_mode) - g_system_mode = text_system_mode; - }); - - auto file_or_error = Core::File::open("/sys/kernel/system_mode"sv, Core::File::OpenMode::Read); - if (file_or_error.is_error()) { - dbgln("Failed to read system_mode: {}", file_or_error.error()); - // Continue and assume "text" mode. - return {}; - } - auto const system_mode_buf_or_error = file_or_error.value()->read_until_eof(); - if (system_mode_buf_or_error.is_error()) { - dbgln("Failed to read system_mode: {}", system_mode_buf_or_error.error()); - // Continue and assume "text" mode. - return {}; - } - ByteString const system_mode = ByteString::copy(system_mode_buf_or_error.value(), Chomp); - - g_system_mode = system_mode; - declare_text_mode_on_failure.disarm(); - - dbgln("Read system_mode: {}", g_system_mode); - return {}; -} - -static ErrorOr prepare_bare_minimum_filesystem_mounts() -{ - TRY(Core::System::remount("/"sv, MS_NODEV | MS_NOSUID | MS_RDONLY)); - // FIXME: Find a better way to all of this stuff, without hardcoding all of this! - TRY(Core::System::mount(-1, "/proc"sv, "proc"sv, MS_NOSUID)); - TRY(Core::System::mount(-1, "/sys"sv, "sys"sv, 0)); - TRY(Core::System::mount(-1, "/dev"sv, "ram"sv, MS_NOSUID | MS_NOEXEC | MS_NOREGULAR)); - TRY(Core::System::mount(-1, "/tmp"sv, "ram"sv, MS_NOSUID | MS_NODEV)); - // NOTE: Set /tmp to have a sticky bit with 0777 permissions. - TRY(Core::System::chmod("/tmp"sv, 01777)); - return {}; -} - -static ErrorOr prepare_bare_minimum_devtmpfs_directory_structure() -{ - TRY(Core::System::mkdir("/dev/audio"sv, 0755)); - TRY(Core::System::mkdir("/dev/input"sv, 0755)); - TRY(Core::System::mkdir("/dev/input/keyboard"sv, 0755)); - TRY(Core::System::mkdir("/dev/input/mouse"sv, 0755)); - TRY(Core::System::symlink("/proc/self/fd/0"sv, "/dev/stdin"sv)); - TRY(Core::System::symlink("/proc/self/fd/1"sv, "/dev/stdout"sv)); - TRY(Core::System::symlink("/proc/self/fd/2"sv, "/dev/stderr"sv)); - TRY(Core::System::mkdir("/dev/gpu"sv, 0755)); - TRY(Core::System::mkdir("/dev/pts"sv, 0755)); - TRY(Core::System::mount(-1, "/dev/pts"sv, "devpts"sv, 0)); - TRY(Core::System::mkdir("/dev/loop"sv, 0755)); - TRY(Core::System::mount(-1, "/dev/loop"sv, "devloop"sv, 0)); - - mode_t old_mask = umask(0); - TRY(Core::System::create_char_device("/dev/devctl"sv, 0660, 2, 10)); - TRY(Core::System::create_char_device("/dev/zero"sv, 0666, 1, 5)); - TRY(Core::System::create_char_device("/dev/mem"sv, 0600, 1, 1)); - TRY(Core::System::create_char_device("/dev/null"sv, 0666, 1, 3)); - TRY(Core::System::create_char_device("/dev/full"sv, 0666, 1, 7)); - TRY(Core::System::create_char_device("/dev/random"sv, 0666, 1, 8)); - TRY(Core::System::create_char_device("/dev/input/mice"sv, 0666, 12, 0)); - TRY(Core::System::create_char_device("/dev/console"sv, 0666, 5, 1)); - TRY(Core::System::create_char_device("/dev/ptmx"sv, 0666, 5, 2)); - TRY(Core::System::create_char_device("/dev/tty"sv, 0666, 5, 0)); - TRY(Core::System::create_char_device("/dev/fuse"sv, 0666, 10, 229)); -#ifdef ENABLE_KERNEL_COVERAGE_COLLECTION - TRY(Core::System::create_block_device("/dev/kcov"sv, 0666, 30, 0)); -#endif - umask(old_mask); - TRY(Core::System::symlink("/dev/random"sv, "/dev/urandom"sv)); - TRY(Core::System::chmod("/dev/urandom"sv, 0666)); - return {}; -} - -static ErrorOr spawn_device_mapper_process() -{ - TRY(Core::Process::spawn("/bin/DeviceMapper"sv, ReadonlySpan {}, {}, Core::Process::KeepAsChild::No)); - return {}; -} - -static ErrorOr prepare_synthetic_filesystems() -{ - TRY(prepare_bare_minimum_filesystem_mounts()); - TRY(prepare_bare_minimum_devtmpfs_directory_structure()); - TRY(spawn_device_mapper_process()); - return {}; -} - -static ErrorOr mount_all_filesystems() -{ - dbgln("Spawning mount -a to mount all filesystems."); - pid_t pid = TRY(Core::System::fork()); - - if (pid == 0) - TRY(Core::System::exec("/bin/mount"sv, Vector { "mount"sv, "-a"sv }, Core::System::SearchInPath::No)); - - wait(nullptr); - return {}; -} - -static ErrorOr create_tmp_coredump_directory() -{ - dbgln("Creating /tmp/coredump directory"); - auto old_umask = umask(0); - // FIXME: the coredump directory should be made read-only once CrashDaemon is no longer responsible for compressing coredumps - TRY(Core::System::mkdir("/tmp/coredump"sv, 0777)); - umask(old_umask); - return {}; -} - -static ErrorOr set_default_coredump_directory() -{ - dbgln("Setting /tmp/coredump as the coredump directory"); - auto sysfs_coredump_directory_variable_fd = TRY(Core::System::open("/sys/kernel/conf/coredump_directory"sv, O_RDWR)); - ScopeGuard close_on_exit([&] { - close(sysfs_coredump_directory_variable_fd); - }); - auto tmp_coredump_directory_path = "/tmp/coredump"sv; - auto nwritten = TRY(Core::System::write(sysfs_coredump_directory_variable_fd, tmp_coredump_directory_path.bytes())); - VERIFY(static_cast(nwritten) == tmp_coredump_directory_path.length()); - return {}; -} - -static ErrorOr create_tmp_semaphore_directory() -{ - dbgln("Creating /tmp/semaphore directory"); - auto old_umask = umask(0); - TRY(Core::System::mkdir("/tmp/semaphore"sv, 0777)); - umask(old_umask); - return {}; -} - -static ErrorOr activate_services(Core::ConfigFile const& config) -{ - for (auto const& name : config.groups()) { - auto service = TRY(Service::try_create(config, name)); - if (service->is_enabled_for_system_mode(g_system_mode)) { - TRY(service->setup_sockets()); - g_services.append(move(service)); - } - } - // After we've set them all up, activate them! - dbgln("Activating {} services...", g_services.size()); - for (auto& service : g_services) { - dbgln_if(SYSTEMSERVER_DEBUG, "Activating {}", service->name()); - if (auto result = service->activate(); result.is_error()) - dbgln("{}: {}", service->name(), result.release_error()); - } - - return {}; -} - -static ErrorOr reopen_base_file_descriptors() -{ - // Note: We open the /dev/null device and set file descriptors 0, 1, 2 to it - // because otherwise these file descriptors won't have a custody, making - // the ProcFS file descriptor links (at /proc/PID/fd/{0,1,2}) to have an - // absolute path of "device:1,3" instead of something like "/dev/null". - // This affects also every other process that inherits the file descriptors - // from SystemServer, so it is important for other things (also for ProcFS - // tests that are running in CI mode). - int stdin_new_fd = TRY(Core::System::open("/dev/null"sv, O_NONBLOCK)); - TRY(Core::System::dup2(stdin_new_fd, 0)); - TRY(Core::System::dup2(stdin_new_fd, 1)); - TRY(Core::System::dup2(stdin_new_fd, 2)); - return {}; -} - -static ErrorOr activate_base_services_based_on_system_mode() -{ - if (g_system_mode == graphical_system_mode) { - bool found_gpu_device = false; - for (int attempt = 0; attempt < 10; attempt++) { - struct stat file_state; - int rc = lstat("/dev/gpu/connector0", &file_state); - if (rc == 0) { - found_gpu_device = true; - break; - } - sleep(1); - } - if (!found_gpu_device) { - dbgln("WARNING: No device nodes at /dev/gpu/ directory after 10 seconds. This is probably a sign of disabled graphics functionality."); - dbgln("To cope with this, graphical mode will not be enabled."); - g_system_mode = text_system_mode; - } - } - - // Read our config and instantiate services. - // This takes care of setting up sockets. - auto config = TRY(Core::ConfigFile::open_for_system("SystemServer")); - TRY(activate_services(*config)); - return {}; -} - -static ErrorOr activate_user_services_based_on_system_mode() -{ - // Read our config and instantiate services. - // This takes care of setting up sockets. - auto config = TRY(Core::ConfigFile::open_for_app("SystemServer")); - TRY(activate_services(*config)); - return {}; -} - -}; - -ErrorOr serenity_main(Main::Arguments arguments) -{ - bool user = false; - Core::ArgsParser args_parser; - args_parser.add_option(user, "Run in user-mode", "user", 'u'); - args_parser.parse(arguments); - - if (!user) { - TRY(SystemServer::mount_all_filesystems()); - TRY(SystemServer::prepare_synthetic_filesystems()); - TRY(SystemServer::reopen_base_file_descriptors()); - } - - TRY(Core::System::pledge("stdio proc exec tty accept unix rpath wpath cpath chown fattr id sigaction")); - - if (!user) { - TRY(SystemServer::create_tmp_coredump_directory()); - TRY(SystemServer::set_default_coredump_directory()); - TRY(SystemServer::create_tmp_semaphore_directory()); - TRY(SystemServer::determine_system_mode()); - } - - Core::EventLoop event_loop; - - event_loop.register_signal(SIGCHLD, sigchld_handler); - event_loop.register_signal(SIGTERM, sigterm_handler); - - if (!user) { - TRY(SystemServer::activate_base_services_based_on_system_mode()); - } else { - TRY(SystemServer::activate_user_services_based_on_system_mode()); - } - - return event_loop.exec(); -} diff --git a/Userland/Services/Taskbar/CMakeLists.txt b/Userland/Services/Taskbar/CMakeLists.txt deleted file mode 100644 index e362030ec6c..00000000000 --- a/Userland/Services/Taskbar/CMakeLists.txt +++ /dev/null @@ -1,19 +0,0 @@ -serenity_component( - Taskbar - REQUIRED - TARGETS Taskbar -) - -set(SOURCES - main.cpp - ClockWidget.cpp - QuickLaunchWidget.cpp - ShutdownDialog.cpp - TaskbarButton.cpp - TaskbarWindow.cpp - WindowList.cpp -) - -serenity_bin(Taskbar) -target_link_libraries(Taskbar PRIVATE LibCore LibGfx LibGUI LibDesktop LibConfig LibIPC LibMain LibURL) -serenity_install_headers(Services/Taskbar) diff --git a/Userland/Services/Taskbar/ClockWidget.cpp b/Userland/Services/Taskbar/ClockWidget.cpp deleted file mode 100644 index 559511db5c0..00000000000 --- a/Userland/Services/Taskbar/ClockWidget.cpp +++ /dev/null @@ -1,218 +0,0 @@ -/* - * Copyright (c) 2018-2021, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "ClockWidget.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace Taskbar { - -ClockWidget::ClockWidget() -{ - set_frame_style(Gfx::FrameStyle::SunkenPanel); - - update_format(Config::read_string("Taskbar"sv, "Clock"sv, "TimeFormat"sv, "%T"sv)); - - m_timer = add(1000, [this] { - static time_t last_update_time; - time_t now = time(nullptr); - if (now != last_update_time) { - tick_clock(); - last_update_time = now; - set_tooltip(MUST(Core::DateTime::now().to_string("%Y-%m-%d"sv))); - } - }); - m_timer->start(); - - m_calendar_window = add(window()); - m_calendar_window->set_window_type(GUI::WindowType::Popup); - m_calendar_window->resize(m_window_size.width(), m_window_size.height()); - - auto root_container = m_calendar_window->set_main_widget(); - root_container->set_fill_with_background_color(true); - root_container->set_layout(GUI::Margins { 2, 0 }, 0); - root_container->set_frame_style(Gfx::FrameStyle::Window); - - auto& navigation_container = root_container->add(); - navigation_container.set_fixed_height(24); - navigation_container.set_layout(GUI::Margins { 2 }); - - m_prev_date = navigation_container.add(); - m_prev_date->set_button_style(Gfx::ButtonStyle::Coolbar); - m_prev_date->set_fixed_size(24, 24); - m_prev_date->set_icon(Gfx::Bitmap::load_from_file("/res/icons/16x16/go-back.png"sv).release_value_but_fixme_should_propagate_errors()); - m_prev_date->on_click = [&](auto) { - m_calendar->show_previous_date(); - update_selected_calendar_button(); - }; - - m_selected_calendar_button = navigation_container.add(); - m_selected_calendar_button->set_button_style(Gfx::ButtonStyle::Coolbar); - m_selected_calendar_button->set_fixed_height(24); - m_selected_calendar_button->on_click = [&](auto) { - m_calendar->toggle_mode(); - update_selected_calendar_button(); - }; - - m_next_date = navigation_container.add(); - m_next_date->set_button_style(Gfx::ButtonStyle::Coolbar); - m_next_date->set_fixed_size(24, 24); - m_next_date->set_icon(Gfx::Bitmap::load_from_file("/res/icons/16x16/go-forward.png"sv).release_value_but_fixme_should_propagate_errors()); - m_next_date->on_click = [&](auto) { - m_calendar->show_next_date(); - update_selected_calendar_button(); - }; - - auto& separator1 = root_container->add(); - separator1.set_fixed_height(2); - - auto& calendar_container = root_container->add(); - calendar_container.set_layout(GUI::Margins { 2 }); - - m_calendar = calendar_container.add(); - m_selected_calendar_button->set_text(m_calendar->formatted_date().release_value_but_fixme_should_propagate_errors()); - - m_calendar->on_scroll = [&] { - update_selected_calendar_button(); - }; - - m_calendar->on_tile_click = [&] { - m_selected_calendar_button->set_text(m_calendar->formatted_date().release_value_but_fixme_should_propagate_errors()); - }; - - m_calendar->on_month_click = [&] { - m_selected_calendar_button->set_text(m_calendar->formatted_date().release_value_but_fixme_should_propagate_errors()); - }; - - auto& separator2 = root_container->add(); - separator2.set_fixed_height(2); - - auto& settings_container = root_container->add(); - settings_container.set_fixed_height(24); - settings_container.set_layout(GUI::Margins { 2 }); - settings_container.add_spacer(); - - m_jump_to_button = settings_container.add(); - m_jump_to_button->set_button_style(Gfx::ButtonStyle::Coolbar); - m_jump_to_button->set_fixed_size(24, 24); - m_jump_to_button->set_icon(Gfx::Bitmap::load_from_file("/res/icons/16x16/calendar-date.png"sv).release_value_but_fixme_should_propagate_errors()); - m_jump_to_button->set_tooltip("Jump to today"_string); - m_jump_to_button->on_click = [this](auto) { - jump_to_current_date(); - }; - - m_calendar_launcher = settings_container.add(); - m_calendar_launcher->set_button_style(Gfx::ButtonStyle::Coolbar); - m_calendar_launcher->set_fixed_size(24, 24); - m_calendar_launcher->set_icon(Gfx::Bitmap::load_from_file("/res/icons/16x16/app-calendar.png"sv).release_value_but_fixme_should_propagate_errors()); - m_calendar_launcher->set_tooltip("Calendar"_string); - m_calendar_launcher->on_click = [this](auto) { - GUI::Process::spawn_or_show_error(window(), "/bin/Calendar"sv); - }; -} - -void ClockWidget::update_format(ByteString const& format) -{ - m_time_format = format; - m_time_width = font().width(Core::DateTime::create(122, 2, 22, 22, 22, 22).to_byte_string(format)); - set_fixed_size(m_time_width + 20, 21); -} - -void ClockWidget::paint_event(GUI::PaintEvent& event) -{ - GUI::Frame::paint_event(event); - auto time_text = Core::DateTime::now().to_byte_string(m_time_format); - GUI::Painter painter(*this); - painter.add_clip_rect(frame_inner_rect()); - - // Render string center-left aligned, but attempt to center the string based on a constant - // "ideal" time string (i.e., the same one used to size this widget in the initializer). - // This prevents the rest of the string from shifting around while seconds tick. - Gfx::Font const& font = Gfx::FontDatabase::default_font(); - int const frame_width = frame_thickness(); - int const ideal_width = m_time_width; - int const widget_width = max_width().as_int(); - int const translation_x = (widget_width - ideal_width) / 2 - frame_width; - - painter.draw_text(frame_inner_rect().translated(translation_x, frame_width), time_text, font, Gfx::TextAlignment::CenterLeft, palette().window_text()); -} - -void ClockWidget::mousedown_event(GUI::MouseEvent& event) -{ - if (event.button() != GUI::MouseButton::Primary) { - return; - } else { - if (!m_calendar_window->is_visible()) - open(); - else - close(); - } -} - -void ClockWidget::context_menu_event(GUI::ContextMenuEvent& event) -{ - if (!m_context_menu) { - m_context_menu = GUI::Menu::construct(); - - auto settings_icon = MUST(Gfx::Bitmap::load_from_file("/res/icons/16x16/settings.png"sv)); - auto open_clock_settings_action = GUI::Action::create("Clock &Settings", *settings_icon, [this](auto&) { - GUI::Process::spawn_or_show_error(window(), "/bin/ClockSettings"sv, Array { "--open-tab", "clock" }); - }); - - m_context_menu->add_action(open_clock_settings_action); - } - - m_context_menu->popup(event.screen_position()); -} - -void ClockWidget::open() -{ - jump_to_current_date(); - position_calendar_window(); - m_calendar_window->show(); -} - -void ClockWidget::close() -{ - m_calendar_window->hide(); -} - -void ClockWidget::position_calendar_window() -{ - constexpr auto taskbar_top_padding { 4 }; - m_calendar_window->set_rect( - screen_relative_rect().right() - m_calendar_window->width(), - screen_relative_rect().top() - taskbar_top_padding - m_calendar_window->height(), - m_window_size.width(), - m_window_size.height()); -} - -void ClockWidget::jump_to_current_date() -{ - if (m_calendar->mode() == GUI::Calendar::Year) - m_calendar->toggle_mode(); - m_calendar->set_selected_date(Core::DateTime::now()); - m_calendar->update_tiles(Core::DateTime::now().year(), Core::DateTime::now().month()); - m_selected_calendar_button->set_text(m_calendar->formatted_date().release_value_but_fixme_should_propagate_errors()); -} - -void ClockWidget::update_selected_calendar_button() -{ - if (m_calendar->mode() == GUI::Calendar::Year) - m_selected_calendar_button->set_text(m_calendar->formatted_date(GUI::Calendar::YearOnly).release_value_but_fixme_should_propagate_errors()); - else - m_selected_calendar_button->set_text(m_calendar->formatted_date().release_value_but_fixme_should_propagate_errors()); -} - -} diff --git a/Userland/Services/Taskbar/ClockWidget.h b/Userland/Services/Taskbar/ClockWidget.h deleted file mode 100644 index 9b702661ed6..00000000000 --- a/Userland/Services/Taskbar/ClockWidget.h +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright (c) 2018-2021, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace Taskbar { - -class ClockWidget final : public GUI::Frame { - C_OBJECT(ClockWidget); - -public: - virtual ~ClockWidget() override = default; - - void update_format(ByteString const&); - -private: - ClockWidget(); - - virtual void paint_event(GUI::PaintEvent&) override; - virtual void mousedown_event(GUI::MouseEvent&) override; - virtual void context_menu_event(GUI::ContextMenuEvent&) override; - - void tick_clock() - { - tzset(); - update(); - } - - void open(); - void close(); - - void position_calendar_window(); - void jump_to_current_date(); - - void update_selected_calendar_button(); - - ByteString m_time_format; - RefPtr m_calendar_window; - RefPtr m_calendar; - RefPtr m_next_date; - RefPtr m_prev_date; - RefPtr m_selected_calendar_button; - RefPtr m_jump_to_button; - RefPtr m_calendar_launcher; - RefPtr m_context_menu; - RefPtr m_timer; - int m_time_width { 0 }; - Gfx::IntSize m_window_size { 158, 186 }; -}; - -} diff --git a/Userland/Services/Taskbar/QuickLaunchWidget.cpp b/Userland/Services/Taskbar/QuickLaunchWidget.cpp deleted file mode 100644 index 2a13b1c03b8..00000000000 --- a/Userland/Services/Taskbar/QuickLaunchWidget.cpp +++ /dev/null @@ -1,487 +0,0 @@ -/* - * Copyright (c) 2021, Fabian Blatz - * Copyright (c) 2023, David Ganz - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "QuickLaunchWidget.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace Taskbar { - -static ByteString sanitize_name(ByteString const& name) -{ - return name.replace(" "sv, ""sv).replace("="sv, ""sv); -} - -static ByteString entry_to_config_string(size_t index, NonnullOwnPtr const& entry) -{ - return ByteString::formatted("{}:{}", index, entry->path()); -} - -OwnPtr QuickLaunchEntry::create_from_path(StringView path) -{ - if (path.ends_with(".af"sv)) { - auto af_path = path.to_byte_string(); - if (!path.starts_with('/')) - af_path = ByteString::formatted("{}/{}", Desktop::AppFile::APP_FILES_DIRECTORY, path); - - return make(Desktop::AppFile::open(af_path)); - } - - auto stat_or_error = Core::System::stat(path); - if (stat_or_error.is_error()) { - dbgln("Failed to stat quick launch entry file: {}", stat_or_error.release_error()); - return {}; - } - - auto stat = stat_or_error.release_value(); - if (S_ISREG(stat.st_mode) && ((stat.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH)) != 0)) - return make(path); - return make(path); -} - -ErrorOr QuickLaunchEntryAppFile::launch() const -{ - TRY(m_app_file->spawn_with_escalation()); - return {}; -} - -ErrorOr QuickLaunchEntryExecutable::launch() const -{ - TRY(Core::Process::spawn(m_path)); - return {}; -} - -GUI::Icon QuickLaunchEntryExecutable::icon() const -{ - return GUI::FileIconProvider::icon_for_executable(m_path); -} - -ByteString QuickLaunchEntryExecutable::name() const -{ - return LexicalPath { m_path }.basename(); -} - -ErrorOr QuickLaunchEntryFile::launch() const -{ - if (!Desktop::Launcher::open(URL::create_with_url_or_path(m_path))) { - // FIXME: LaunchServer doesn't inform us about errors - return Error::from_string_literal("Failed to open file"); - } - return {}; -} - -GUI::Icon QuickLaunchEntryFile::icon() const -{ - return GUI::FileIconProvider::icon_for_path(m_path); -} -ErrorOr> QuickLaunchWidget::create() -{ - auto widget = TRY(AK::adopt_nonnull_ref_or_enomem(new (nothrow) QuickLaunchWidget())); - TRY(widget->create_context_menu()); - widget->load_entries(); - return widget; -} - -ErrorOr QuickLaunchWidget::add_from_pid(pid_t pid_to_add) -{ - auto processes_file = TRY(Core::File::open("/sys/kernel/processes"sv, Core::File::OpenMode::Read)); - auto file_content = TRY(processes_file->read_until_eof()); - auto json_obj = TRY(JsonValue::from_string(file_content)).as_object(); - for (auto value : json_obj.get_array("processes"sv).release_value().values()) { - auto& process_object = value.as_object(); - auto pid = process_object.get_i32("pid"sv).value_or(0); - if (pid != pid_to_add) - continue; - - auto executable = process_object.get_byte_string("executable"sv); - if (!executable.has_value()) - break; - - auto maybe_name = process_object.get_byte_string("name"sv); - if (!maybe_name.has_value()) - break; - - auto name = maybe_name.release_value(); - auto path = executable.release_value(); - if (Desktop::AppFile::exists_for_app(name)) { - path = Desktop::AppFile::app_file_path_for_app(name); - } - - auto new_entry = QuickLaunchEntry::create_from_path(path); - if (!new_entry) - break; - - TRY(update_entry(name, new_entry.release_nonnull())); - return true; - } - - return false; -} - -void QuickLaunchWidget::config_key_was_removed(StringView domain, StringView group, StringView key) -{ - if (domain == "Taskbar" && group == CONFIG_GROUP_ENTRIES) - remove_entry(key, false); -} - -void QuickLaunchWidget::config_string_did_change(StringView domain, StringView group, StringView, StringView) -{ - if (domain == "Taskbar" && group == CONFIG_GROUP_ENTRIES) - load_entries(false); -} - -void QuickLaunchWidget::drag_enter_event(GUI::DragEvent& event) -{ - auto const& mime_types = event.mime_types(); - if (mime_types.contains_slow("text/uri-list"sv)) - event.accept(); -} - -void QuickLaunchWidget::drop_event(GUI::DropEvent& event) -{ - event.accept(); - - if (event.mime_data().has_urls()) { - auto urls = event.mime_data().urls(); - for (auto& url : urls) { - auto path = url.serialize_path(); - auto entry = QuickLaunchEntry::create_from_path(path); - if (entry) { - auto entry_name = entry->name(); - auto result = update_entry(entry_name, entry.release_nonnull()); - if (result.is_error()) - GUI::MessageBox::show_error(window(), ByteString::formatted("Failed to add quick launch entry: {}", result.release_error())); - } - } - } -} - -void QuickLaunchWidget::mousedown_event(GUI::MouseEvent& event) -{ - for_each_entry([&](NonnullOwnPtr const& entry, Gfx::IntRect rect) { - if (m_dragging && !entry->is_pressed()) - return; - - entry->set_pressed(rect.contains(event.position())); - if (entry->is_pressed()) - m_grab_offset = rect.x() - event.x(); - }); - update(); -} - -void QuickLaunchWidget::mousemove_event(GUI::MouseEvent& event) -{ - m_mouse_pos = event.position(); - for_each_entry([&](NonnullOwnPtr const& entry, Gfx::IntRect rect) { - entry->set_hovered(rect.contains(event.position())); - if (entry->is_pressed()) - m_dragging = true; - - if (entry->is_hovered()) - GUI::Application::the()->show_tooltip(String::from_byte_string(entry->name()).release_value_but_fixme_should_propagate_errors(), this); - }); - - if (m_dragging) - recalculate_order(); - - update(); -} - -void QuickLaunchWidget::mouseup_event(GUI::MouseEvent& event) -{ - for_each_entry([&](NonnullOwnPtr const& entry, Gfx::IntRect) { - if (!m_dragging && entry->is_pressed() && event.button() == GUI::MouseButton::Primary) { - auto result = entry->launch(); - if (result.is_error()) { - // FIXME: This message box is displayed in a weird position - GUI::MessageBox::show_error(window(), ByteString::formatted("Failed to open quick launch entry: {}", result.release_error())); - } - } - - entry->set_pressed(false); - }); - - m_dragging = false; - - update(); -} - -void QuickLaunchWidget::context_menu_event(GUI::ContextMenuEvent& event) -{ - for_each_entry([&](NonnullOwnPtr const& entry, Gfx::IntRect rect) { - if (!rect.contains(event.position())) - return; - - m_context_menu_app_name = entry->name(); - m_context_menu->popup(event.screen_position(), m_context_menu_default_action); - }); -} - -void QuickLaunchWidget::leave_event(Core::Event& event) -{ - for_each_entry([&](NonnullOwnPtr const& entry, auto) { - entry->set_pressed(false); - entry->set_hovered(false); - }); - - m_dragging = false; - m_grab_offset = 0; - - update(); - event.accept(); - Widget::leave_event(event); -} - -void QuickLaunchWidget::paint_event(GUI::PaintEvent& event) -{ - Frame::paint_event(event); - - GUI::Painter painter(*this); - - auto paint_entry = [this, &painter](NonnullOwnPtr const& entry, Gfx::IntRect rect) { - Gfx::StylePainter::paint_button(painter, rect, palette(), Gfx::ButtonStyle::Coolbar, entry->is_pressed(), entry->is_hovered()); - - auto const* icon = entry->icon().bitmap_for_size(16); - auto content_rect = rect.shrunken(8, 2); - auto icon_location = content_rect.center().translated(-(icon->width() / 2), -(icon->height() / 2)); - if (entry->is_pressed()) - icon_location.translate_by(1, 1); - - if (entry->is_hovered()) - painter.blit_brightened(icon_location, *icon, icon->rect()); - else - painter.blit(icon_location, *icon, icon->rect()); - }; - - NonnullOwnPtr const* dragged_entry = nullptr; - Gfx::IntRect dragged_entry_rect; - - for_each_entry([&](NonnullOwnPtr const& entry, Gfx::IntRect rect) { - if (m_dragging && entry->is_pressed()) { - rect.set_x(m_mouse_pos.x() + m_grab_offset); - dragged_entry = &entry; - dragged_entry_rect = rect; - return; - } - - paint_entry(entry, rect); - }); - - if (dragged_entry) - paint_entry(*dragged_entry, dragged_entry_rect); -} - -QuickLaunchWidget::QuickLaunchWidget() -{ - set_shrink_to_fit(true); - set_layout(GUI::Margins {}, 0); - set_frame_style(Gfx::FrameStyle::NoFrame); - set_fixed_height(24); -} - -ErrorOr QuickLaunchWidget::create_context_menu() -{ - auto icon = TRY(Gfx::Bitmap::load_from_file("/res/icons/16x16/delete.png"sv)); - m_context_menu = GUI::Menu::construct(); - m_context_menu_default_action = GUI::Action::create("&Remove", icon, [this](auto&) { - remove_entry(m_context_menu_app_name); - repaint(); - }); - m_context_menu->add_action(*m_context_menu_default_action); - - return {}; -} - -void QuickLaunchWidget::load_entries(bool save) -{ - struct ConfigEntry { - int index; - ByteString path; - }; - - Vector config_entries; - auto keys = Config::list_keys(CONFIG_DOMAIN, CONFIG_GROUP_ENTRIES); - for (auto& name : keys) { - auto value = Config::read_string(CONFIG_DOMAIN, CONFIG_GROUP_ENTRIES, name); - auto values = value.split(':'); - - config_entries.append({ values[0].to_number().release_value(), values[1] }); - } - - quick_sort(config_entries, [](ConfigEntry const& a, ConfigEntry const& b) { - return a.index < b.index; - }); - - Vector> entries; - for (auto const& config_entry : config_entries) { - auto entry = QuickLaunchEntry::create_from_path(config_entry.path); - if (!entry) - continue; - - entries.append(entry.release_nonnull()); - } - - // backwards compatibility since the group and value-format changed - auto old_keys = Config::list_keys(CONFIG_DOMAIN, OLD_CONFIG_GROUP_ENTRIES); - if (!old_keys.is_empty()) { - for (auto& name : old_keys) { - auto path = Config::read_string(CONFIG_DOMAIN, OLD_CONFIG_GROUP_ENTRIES, name); - auto entry = QuickLaunchEntry::create_from_path(path); - if (!entry) - continue; - - entries.append(entry.release_nonnull()); - } - - Config::remove_group(CONFIG_DOMAIN, OLD_CONFIG_GROUP_ENTRIES); - } - - m_entries.clear(); - add_entries(move(entries), save); -} - -void QuickLaunchWidget::add_entries(Vector> entries, bool save) -{ - size_t size = entries.size(); - for (size_t i = 0; i < size; i++) { - m_entries.append(entries.take(0)); - if (save) - Config::write_string(CONFIG_DOMAIN, CONFIG_GROUP_ENTRIES, sanitize_name(m_entries.last()->name()), entry_to_config_string(m_entries.size() - 1, m_entries.last())); - } - - repaint(); -} - -ErrorOr QuickLaunchWidget::update_entry(ByteString const& button_name, NonnullOwnPtr entry, bool save) -{ - auto file_name_to_watch = entry->file_name_to_watch(); - if (!file_name_to_watch.is_empty()) { - if (!m_watcher) { - m_watcher = TRY(Core::FileWatcher::create()); - m_watcher->on_change = [button_name, save, this](Core::FileWatcherEvent const&) { - dbgln("Removing QuickLaunch entry \"{}\"", button_name); - remove_entry(button_name, save); - repaint(); - }; - } - TRY(m_watcher->add_watch(file_name_to_watch, Core::FileWatcherEvent::Type::Deleted)); - } - - set_or_insert_entry(move(entry), save); - repaint(); - - return {}; -} - -template -void QuickLaunchWidget::for_each_entry(Callback callback) -{ - Gfx::IntRect rect(0, 0, BUTTON_SIZE, BUTTON_SIZE); - for (auto const& entry : m_entries) { - callback(entry, rect); - rect.translate_by(BUTTON_SIZE, 0); - } -} - -void QuickLaunchWidget::resize() -{ - set_fixed_width(static_cast(m_entries.size()) * BUTTON_SIZE); -} - -void QuickLaunchWidget::repaint() -{ - resize(); - update(); -} - -void QuickLaunchWidget::set_or_insert_entry(NonnullOwnPtr entry, bool save) -{ - auto name = entry->name(); - for (size_t i = 0; i < m_entries.size(); i++) { - auto& value = m_entries[i]; - if (value->name() != name) - continue; - value = move(entry); - if (save) - Config::write_string(CONFIG_DOMAIN, CONFIG_GROUP_ENTRIES, sanitize_name(value->name()), entry_to_config_string(i, value)); - return; - } - - if (save) - Config::write_string(CONFIG_DOMAIN, CONFIG_GROUP_ENTRIES, sanitize_name(entry->name()), entry_to_config_string(m_entries.size(), entry)); - m_entries.append(move(entry)); -} - -void QuickLaunchWidget::remove_entry(ByteString const& name, bool save) -{ - for (size_t i = 0; i < m_entries.size(); i++) { - if (m_entries[i]->name() != name) - continue; - if (save) - Config::remove_key(CONFIG_DOMAIN, CONFIG_GROUP_ENTRIES, m_entries[i]->name()); - m_entries.remove(i); - return; - } -} -void QuickLaunchWidget::recalculate_order() -{ - if (!m_dragging) - return; - - size_t dragged_index = 0; - for (; dragged_index < m_entries.size(); dragged_index++) { - if (m_entries[dragged_index]->is_pressed()) - break; - } - - size_t new_index = m_entries.size() + 1; - Gfx::IntRect rect(0, 0, BUTTON_SIZE, BUTTON_SIZE); - for (size_t i = 0; i < m_entries.size(); i++) { - auto left_break_point = i == 0 ? rect.x() + rect.width() / 2 : rect.x(); - if (m_mouse_pos.x() < left_break_point) { - new_index = i; - break; - } - - if (i == m_entries.size() - 1 && m_mouse_pos.x() > rect.x() + rect.width() / 2) { - new_index = i + 1; - break; - } - - rect.translate_by(BUTTON_SIZE, 0); - } - - if (new_index >= m_entries.size() + 1 || new_index == dragged_index) - return; - - if (dragged_index < new_index) - new_index--; - - auto entry = m_entries.take(dragged_index); - m_entries.insert(new_index, move(entry)); - - for (size_t i = 0; i < m_entries.size(); i++) - Config::write_string(CONFIG_DOMAIN, CONFIG_GROUP_ENTRIES, sanitize_name(m_entries[i]->name()), entry_to_config_string(i, m_entries[i])); -} - -} diff --git a/Userland/Services/Taskbar/QuickLaunchWidget.h b/Userland/Services/Taskbar/QuickLaunchWidget.h deleted file mode 100644 index 8e4e8429ed2..00000000000 --- a/Userland/Services/Taskbar/QuickLaunchWidget.h +++ /dev/null @@ -1,160 +0,0 @@ -/* - * Copyright (c) 2021, Fabian Blatz - * Copyright (c) 2023, David Ganz - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include -#include -#include -#include - -namespace Taskbar { - -class QuickLaunchEntry { -public: - static OwnPtr create_from_path(StringView path); - - virtual ~QuickLaunchEntry() = default; - virtual ErrorOr launch() const = 0; - virtual GUI::Icon icon() const = 0; - virtual ByteString name() const = 0; - virtual ByteString file_name_to_watch() const = 0; - - virtual ByteString path() = 0; - - bool is_hovered() const { return m_hovered; } - void set_hovered(bool hovered) { m_hovered = hovered; } - - void set_pressed(bool pressed) { m_pressed = pressed; } - bool is_pressed() const { return m_pressed; } - -private: - bool m_hovered { false }; - bool m_pressed { false }; -}; - -class QuickLaunchEntryAppFile : public QuickLaunchEntry { -public: - explicit QuickLaunchEntryAppFile(NonnullRefPtr file) - : m_app_file(move(file)) - { - } - - virtual ErrorOr launch() const override; - virtual GUI::Icon icon() const override { return m_app_file->icon(); } - virtual ByteString name() const override { return m_app_file->name(); } - virtual ByteString file_name_to_watch() const override { return {}; } - - virtual ByteString path() override { return m_app_file->filename(); } - -private: - NonnullRefPtr m_app_file; -}; - -class QuickLaunchEntryExecutable : public QuickLaunchEntry { -public: - explicit QuickLaunchEntryExecutable(ByteString path) - : m_path(move(path)) - { - } - - virtual ErrorOr launch() const override; - virtual GUI::Icon icon() const override; - virtual ByteString name() const override; - virtual ByteString file_name_to_watch() const override { return m_path; } - - virtual ByteString path() override { return m_path; } - -private: - ByteString m_path; -}; -class QuickLaunchEntryFile : public QuickLaunchEntry { -public: - explicit QuickLaunchEntryFile(ByteString path) - : m_path(move(path)) - { - } - - virtual ErrorOr launch() const override; - virtual GUI::Icon icon() const override; - virtual ByteString name() const override { return m_path; } - virtual ByteString file_name_to_watch() const override { return m_path; } - - virtual ByteString path() override { return m_path; } - -private: - ByteString m_path; -}; - -class QuickLaunchWidget : public GUI::Frame - , public Config::Listener { - C_OBJECT(QuickLaunchWidget); - -public: - static ErrorOr> create(); - virtual ~QuickLaunchWidget() override = default; - - ErrorOr add_from_pid(pid_t pid); - -protected: - virtual void config_key_was_removed(StringView, StringView, StringView) override; - virtual void config_string_did_change(StringView, StringView, StringView, StringView) override; - - virtual void drag_enter_event(GUI::DragEvent&) override; - virtual void drop_event(GUI::DropEvent&) override; - - virtual void mousedown_event(GUI::MouseEvent&) override; - virtual void mousemove_event(GUI::MouseEvent&) override; - virtual void mouseup_event(GUI::MouseEvent&) override; - virtual void context_menu_event(GUI::ContextMenuEvent&) override; - - virtual void leave_event(Core::Event&) override; - - virtual void paint_event(GUI::PaintEvent&) override; - -private: - static constexpr StringView CONFIG_DOMAIN = "Taskbar"sv; - static constexpr StringView CONFIG_GROUP_ENTRIES = "QuickLaunch_Entries"sv; - static constexpr StringView OLD_CONFIG_GROUP_ENTRIES = "QuickLaunch"sv; - static constexpr int BUTTON_SIZE = 24; - - explicit QuickLaunchWidget(); - - ErrorOr create_context_menu(); - - void load_entries(bool save = true); - ErrorOr update_entry(ByteString const&, NonnullOwnPtr, bool save = true); - void add_entries(Vector> entries, bool save = true); - - template - void for_each_entry(Callback); - - void resize(); - - void repaint(); - - void set_or_insert_entry(NonnullOwnPtr, bool save = true); - void remove_entry(ByteString const&, bool save = true); - void recalculate_order(); - - bool m_dragging { false }; - Gfx::IntPoint m_mouse_pos; - int m_grab_offset { 0 }; - - RefPtr m_context_menu; - RefPtr m_context_menu_default_action; - ByteString m_context_menu_app_name; - RefPtr m_watcher; - - Vector> m_entries; -}; - -} diff --git a/Userland/Services/Taskbar/ShutdownDialog.cpp b/Userland/Services/Taskbar/ShutdownDialog.cpp deleted file mode 100644 index b3c4775c3c5..00000000000 --- a/Userland/Services/Taskbar/ShutdownDialog.cpp +++ /dev/null @@ -1,115 +0,0 @@ -/* - * Copyright (c) 2020, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "ShutdownDialog.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -struct Option { - ByteString title; - ShutdownDialog::Command command; - bool enabled; - bool default_action; -}; - -static Array const options = { - Option { "Power off computer", { "/bin/shutdown"sv, { "--now" } }, true, true }, - Option { "Reboot", { "/bin/reboot"sv, {} }, true, false }, - Option { "Log out", { "/bin/logout"sv, {} }, true, false }, -}; - -Optional ShutdownDialog::show() -{ - auto dialog = ShutdownDialog::construct(); - auto rc = dialog->exec(); - if (rc == ExecResult::OK && dialog->m_selected_option != -1) - return options[dialog->m_selected_option].command; - return {}; -} - -ShutdownDialog::ShutdownDialog() - : Dialog(nullptr) -{ - auto widget = set_main_widget(); - widget->set_fill_with_background_color(true); - widget->set_layout(GUI::Margins {}, 0); - - auto& banner_image = widget->add(); - banner_image.load_from_file("/res/graphics/brand-banner.png"sv); - - auto& content_container = widget->add(); - content_container.set_layout(); - - auto& left_container = content_container.add(); - left_container.set_fixed_width(60); - left_container.set_layout(GUI::Margins { 12, 0, 0 }); - - auto& icon_wrapper = left_container.add(); - icon_wrapper.set_fixed_size(32, 48); - icon_wrapper.set_layout(); - - auto& icon_image = icon_wrapper.add(); - icon_image.set_bitmap(Gfx::Bitmap::load_from_file("/res/icons/32x32/shutdown.png"sv).release_value_but_fixme_should_propagate_errors()); - - auto& right_container = content_container.add(); - right_container.set_layout(GUI::Margins { 12, 12, 8, 0 }); - - auto& label = right_container.add("What would you like to do?"_string); - label.set_text_alignment(Gfx::TextAlignment::CenterLeft); - label.set_fixed_height(22); - label.set_font(Gfx::FontDatabase::default_font().bold_variant()); - - for (size_t i = 0; i < options.size(); i++) { - auto action = options[i]; - auto& radio = right_container.add(); - radio.set_enabled(action.enabled); - radio.set_text(String::from_byte_string(action.title).release_value_but_fixme_should_propagate_errors()); - - radio.on_checked = [this, i](auto) { - m_selected_option = i; - }; - - if (action.default_action) { - radio.set_checked(true); - m_selected_option = i; - } - } - - right_container.add_spacer(); - - auto& button_container = right_container.add(); - button_container.set_fixed_height(23); - button_container.set_layout(GUI::Margins {}, 5); - button_container.add_spacer(); - auto& ok_button = button_container.add("OK"_string); - ok_button.set_fixed_size(80, 23); - ok_button.on_click = [this](auto) { - done(ExecResult::OK); - }; - ok_button.set_default(true); - auto& cancel_button = button_container.add("Cancel"_string); - cancel_button.set_fixed_size(80, 23); - cancel_button.on_click = [this](auto) { - done(ExecResult::Cancel); - }; - - resize(413, 235); - center_on_screen(); - set_resizable(false); - set_title("Exit SerenityOS"); - set_icon(Gfx::Bitmap::load_from_file("/res/icons/16x16/power.png"sv).release_value_but_fixme_should_propagate_errors()); - - // Request WindowServer to re-update us on the current theme as we might've not been alive for the last notification. - refresh_system_theme(); -} diff --git a/Userland/Services/Taskbar/ShutdownDialog.h b/Userland/Services/Taskbar/ShutdownDialog.h deleted file mode 100644 index 71d825feecd..00000000000 --- a/Userland/Services/Taskbar/ShutdownDialog.h +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright (c) 2020, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include - -class ShutdownDialog : public GUI::Dialog { - C_OBJECT(ShutdownDialog); - -public: - struct Command { - StringView executable; - Vector arguments; - }; - - static Optional show(); - -private: - ShutdownDialog(); - virtual ~ShutdownDialog() override = default; - - int m_selected_option { -1 }; -}; diff --git a/Userland/Services/Taskbar/TaskbarButton.cpp b/Userland/Services/Taskbar/TaskbarButton.cpp deleted file mode 100644 index 4902d558b8a..00000000000 --- a/Userland/Services/Taskbar/TaskbarButton.cpp +++ /dev/null @@ -1,140 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "TaskbarButton.h" -#include "WindowList.h" -#include -#include -#include -#include -#include -#include -#include - -TaskbarButton::TaskbarButton(WindowIdentifier const& identifier) - : m_identifier(identifier) -{ - set_checkable(true); -} - -void TaskbarButton::context_menu_event(GUI::ContextMenuEvent&) -{ - GUI::ConnectionToWindowManagerServer::the().async_popup_window_menu( - m_identifier.client_id(), - m_identifier.window_id(), - screen_relative_rect().location()); -} - -void TaskbarButton::update_taskbar_rect() -{ - GUI::ConnectionToWindowManagerServer::the().async_set_window_taskbar_rect( - m_identifier.client_id(), - m_identifier.window_id(), - screen_relative_rect()); -} - -void TaskbarButton::clear_taskbar_rect() -{ - GUI::ConnectionToWindowManagerServer::the().async_set_window_taskbar_rect( - m_identifier.client_id(), - m_identifier.window_id(), - {}); -} - -void TaskbarButton::resize_event(GUI::ResizeEvent& event) -{ - update_taskbar_rect(); - return GUI::Button::resize_event(event); -} - -static void paint_custom_progressbar(GUI::Painter& painter, Gfx::IntRect const& rect, Gfx::IntRect const& text_rect, Palette const& palette, int min, int max, int value, StringView text, Gfx::Font const& font, Gfx::TextAlignment text_alignment) -{ - float range_size = max - min; - float progress = (value - min) / range_size; - float progress_width = progress * rect.width(); - - Gfx::IntRect progress_rect { rect.x(), rect.y(), (int)progress_width, rect.height() }; - - { - Gfx::PainterStateSaver saver(painter); - painter.add_clip_rect(progress_rect); - - Color start_color = palette.active_window_border1(); - Color end_color = palette.active_window_border2(); - painter.fill_rect_with_gradient(rect, start_color, end_color); - - if (!text.is_null()) { - painter.draw_text(text_rect.translated(1, 1), text, font, text_alignment, palette.base_text(), Gfx::TextElision::Right); - painter.draw_text(text_rect, text, font, text_alignment, palette.base_text().inverted(), Gfx::TextElision::Right); - } - } - - Gfx::IntRect hole_rect { (int)progress_width, 0, (int)(rect.width() - progress_width), rect.height() }; - hole_rect.translate_by(rect.location()); - hole_rect.set_right_without_resize(rect.right()); - Gfx::PainterStateSaver saver(painter); - painter.add_clip_rect(hole_rect); - if (!text.is_null()) - painter.draw_text(text_rect, text, font, text_alignment, palette.base_text(), Gfx::TextElision::Right); -} - -void TaskbarButton::paint_event(GUI::PaintEvent& event) -{ - VERIFY(icon()); - auto& icon = *this->icon(); - auto& font = is_checked() ? this->font().bold_variant() : this->font(); - auto& window = WindowList::the().ensure_window(m_identifier); - - GUI::Painter painter(*this); - painter.add_clip_rect(event.rect()); - - Gfx::StylePainter::paint_button(painter, rect(), palette(), button_style(), is_being_pressed(), is_hovered(), is_checked(), is_enabled()); - - if (text().is_empty()) - return; - - auto content_rect = rect().shrunken(8, 2); - auto icon_location = content_rect.center().translated(-(icon.width() / 2), -(icon.height() / 2)); - if (!text().is_empty()) - icon_location.set_x(content_rect.x()); - - if (!text().is_empty()) { - content_rect.translate_by(icon.width() + 4, 0); - content_rect.set_width(content_rect.width() - icon.width() - 4); - } - - Gfx::IntRect text_rect { 0, 0, font.width_rounded_up(text()), font.pixel_size_rounded_up() }; - if (text_rect.width() > content_rect.width()) - text_rect.set_width(content_rect.width()); - text_rect.align_within(content_rect, text_alignment()); - - if (is_being_pressed() || is_checked()) { - text_rect.translate_by(1, 1); - icon_location.translate_by(1, 1); - } - - if (window.progress().has_value()) { - auto adjusted_rect = rect().shrunken(4, 4); - if (!is_being_pressed() && !is_checked()) { - adjusted_rect.translate_by(-1, -1); - adjusted_rect.set_width(adjusted_rect.width() + 1); - adjusted_rect.set_height(adjusted_rect.height() + 1); - } - paint_custom_progressbar(painter, adjusted_rect, text_rect, palette(), 0, 100, window.progress().value(), text(), font, text_alignment()); - } - - if (is_enabled()) { - if (is_hovered()) - painter.blit_brightened(icon_location, icon, icon.rect()); - else - painter.blit(icon_location, icon, icon.rect()); - } else { - painter.blit_disabled(icon_location, icon, icon.rect(), palette()); - } - - if (!window.progress().has_value()) - paint_text(painter, text_rect, font, text_alignment()); -} diff --git a/Userland/Services/Taskbar/TaskbarButton.h b/Userland/Services/Taskbar/TaskbarButton.h deleted file mode 100644 index e766803dc84..00000000000 --- a/Userland/Services/Taskbar/TaskbarButton.h +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include "WindowIdentifier.h" -#include - -class TaskbarButton final : public GUI::Button { - C_OBJECT(TaskbarButton) -public: - virtual ~TaskbarButton() override = default; - - void update_taskbar_rect(); - void clear_taskbar_rect(); - -private: - explicit TaskbarButton(WindowIdentifier const&); - - virtual void context_menu_event(GUI::ContextMenuEvent&) override; - virtual void resize_event(GUI::ResizeEvent&) override; - virtual void paint_event(GUI::PaintEvent&) override; - - WindowIdentifier m_identifier; -}; diff --git a/Userland/Services/Taskbar/TaskbarWindow.cpp b/Userland/Services/Taskbar/TaskbarWindow.cpp deleted file mode 100644 index 1f96011fd2c..00000000000 --- a/Userland/Services/Taskbar/TaskbarWindow.cpp +++ /dev/null @@ -1,462 +0,0 @@ -/* - * Copyright (c) 2018-2021, Andreas Kling - * Copyright (c) 2021, Spencer Dixon - * Copyright (c) 2023, David Ganz - * Copyright (c) 2024, Wellington Santos - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "TaskbarWindow.h" -#include "ClockWidget.h" -#include "QuickLaunchWidget.h" -#include "TaskbarButton.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -class TaskbarWidget final : public GUI::Widget { - C_OBJECT(TaskbarWidget); - -public: - virtual ~TaskbarWidget() override = default; - - static ErrorOr> create(); - -protected: - virtual void context_menu_event(GUI::ContextMenuEvent& event) override - { - size_t visible_windows_count = 0; - WindowList::the().for_each_window([&](auto& window) { - if (!window.is_minimized()) - visible_windows_count += 1; - }); - m_show_desktop_action->set_text(visible_windows_count >= 1 ? "Show Desktop" : "Show open Windows"); - m_context_menu->popup(event.screen_position()); - } - -private: - TaskbarWidget() = default; - - virtual void paint_event(GUI::PaintEvent& event) override - { - GUI::Painter painter(*this); - painter.add_clip_rect(event.rect()); - painter.fill_rect(rect(), palette().button()); - painter.draw_line({ 0, 1 }, { width() - 1, 1 }, palette().threed_highlight()); - } - - virtual void did_layout() override - { - WindowList::the().for_each_window([&](auto& window) { - if (auto* button = window.button()) - static_cast(button)->update_taskbar_rect(); - }); - } - - ErrorOr create_context_menu(); - - RefPtr m_context_menu; - RefPtr m_show_desktop_action; -}; - -ErrorOr> TaskbarWidget::create() -{ - auto widget = TaskbarWidget::construct(); - TRY(widget->create_context_menu()); - return widget; -} - -ErrorOr TaskbarWidget::create_context_menu() -{ - m_context_menu = GUI::Menu::construct(); - - m_show_desktop_action = GUI::Action::create("Show Desktop", TRY(Gfx::Bitmap::load_from_file("/res/icons/16x16/desktop.png"sv)), [](auto&) { - GUI::ConnectionToWindowManagerServer::the().async_toggle_show_desktop(); - }); - - auto open_settings_action = GUI::Action::create("&Settings", TRY(Gfx::Bitmap::load_from_file("/res/icons/16x16/settings.png"sv)), [this](auto&) { - GUI::Process::spawn_or_show_error(window(), "/bin/Settings"sv); - }); - - auto open_system_monitor_action = GUI::Action::create("System &Monitor", TRY(Gfx::Bitmap::load_from_file("/res/icons/16x16/app-system-monitor.png"sv)), [this](auto&) { - GUI::Process::spawn_or_show_error(window(), "/bin/SystemMonitor"sv); - }); - - m_context_menu->add_action(*open_system_monitor_action); - m_context_menu->add_action(*open_settings_action); - m_context_menu->add_separator(); - m_context_menu->add_action(*m_show_desktop_action); - - return {}; -} - -ErrorOr> TaskbarWindow::create() -{ - auto window = TRY(AK::adopt_nonnull_ref_or_enomem(new (nothrow) TaskbarWindow())); - TRY(window->populate_taskbar()); - TRY(window->load_assistant()); - return window; -} - -TaskbarWindow::TaskbarWindow() -{ - set_window_type(GUI::WindowType::Taskbar); - set_title("Taskbar"); - - on_screen_rects_change(GUI::Desktop::the().rects(), GUI::Desktop::the().main_screen_index()); -} - -ErrorOr TaskbarWindow::populate_taskbar() -{ - auto main_widget = TRY(TaskbarWidget::create()); - set_main_widget(main_widget); - - main_widget->set_layout(GUI::Margins { 2, 3, 0, 3 }); - - m_quick_launch = TRY(Taskbar::QuickLaunchWidget::create()); - TRY(main_widget->try_add_child(*m_quick_launch)); - - m_task_button_container = main_widget->add(); - m_task_button_container->set_layout(GUI::Margins {}, 3); - - m_default_icon = TRY(Gfx::Bitmap::load_from_file("/res/icons/16x16/window.png"sv)); - - m_applet_area_container = main_widget->add(); - m_applet_area_container->set_frame_style(Gfx::FrameStyle::SunkenPanel); - - m_clock_widget = main_widget->add(); - - m_show_desktop_button = main_widget->add(); - m_show_desktop_button->set_tooltip("Show Desktop"_string); - m_show_desktop_button->set_icon(TRY(GUI::Icon::try_create_default_icon("desktop"sv)).bitmap_for_size(16)); - m_show_desktop_button->set_button_style(Gfx::ButtonStyle::Coolbar); - m_show_desktop_button->set_fixed_size(24, 24); - m_show_desktop_button->on_click = TaskbarWindow::show_desktop_button_clicked; - - return {}; -} - -ErrorOr TaskbarWindow::load_assistant() -{ - auto af_path = TRY(String::formatted("{}/{}", Desktop::AppFile::APP_FILES_DIRECTORY, "Assistant.af")); - m_assistant_app_file = Desktop::AppFile::open(af_path); - - return {}; -} - -void TaskbarWindow::add_system_menu(NonnullRefPtr system_menu) -{ - m_system_menu = move(system_menu); - - m_start_button = GUI::Button::construct("Serenity"_string); - set_start_button_font(Gfx::FontDatabase::default_font().bold_variant()); - m_start_button->set_icon_spacing(0); - auto app_icon = GUI::Icon::default_icon("ladyball"sv); - m_start_button->set_icon(app_icon.bitmap_for_size(16)); - m_start_button->set_menu(m_system_menu); - - GUI::Widget* main = main_widget(); - main->insert_child_before(*m_start_button, *m_quick_launch); -} - -void TaskbarWindow::config_string_did_change(StringView domain, StringView group, StringView key, StringView value) -{ - if (domain == "Taskbar" && group == "Clock" && key == "TimeFormat") { - m_clock_widget->update_format(value); - update_applet_area(); - } -} - -void TaskbarWindow::show_desktop_button_clicked(unsigned) -{ - toggle_show_desktop(); -} - -void TaskbarWindow::toggle_show_desktop() -{ - GUI::ConnectionToWindowManagerServer::the().async_toggle_show_desktop(); -} - -void TaskbarWindow::on_screen_rects_change(Vector const& rects, size_t main_screen_index) -{ - auto const& rect = rects[main_screen_index]; - Gfx::IntRect new_rect { rect.x(), rect.bottom() - taskbar_height(), rect.width(), taskbar_height() }; - set_rect(new_rect); - update_applet_area(); -} - -void TaskbarWindow::update_applet_area() -{ - // NOTE: Widget layout is normally lazy, but here we have to force it right away so we can tell - // WindowServer where to place the applet area window. - if (!main_widget()) - return; - main_widget()->do_layout(); - auto new_rect = Gfx::IntRect({}, m_applet_area_size).centered_within(m_applet_area_container->screen_relative_rect()); - GUI::ConnectionToWindowManagerServer::the().async_set_applet_area_position(new_rect.location()); -} - -NonnullRefPtr TaskbarWindow::create_button(WindowIdentifier const& identifier) -{ - auto& button = m_task_button_container->add(identifier); - button.set_min_size(20, 21); - button.set_max_size(140, 21); - button.set_text_alignment(Gfx::TextAlignment::CenterLeft); - button.set_icon(*m_default_icon); - return button; -} - -void TaskbarWindow::add_window_button(::Window& window, WindowIdentifier const& identifier) -{ - if (window.button()) - return; - window.set_button(create_button(identifier)); - auto* button = window.button(); - button->on_click = [window = &window, identifier](auto) { - if (window->is_minimized() || !window->is_active()) - GUI::ConnectionToWindowManagerServer::the().async_set_active_window(identifier.client_id(), identifier.window_id()); - else if (!window->is_blocked()) - GUI::ConnectionToWindowManagerServer::the().async_set_window_minimized(identifier.client_id(), identifier.window_id(), true); - }; -} - -void TaskbarWindow::remove_window_button(::Window& window, bool was_removed) -{ - auto* button = window.button(); - if (!button) - return; - if (!was_removed) - static_cast(button)->clear_taskbar_rect(); - window.set_button(nullptr); - button->remove_from_parent(); -} - -void TaskbarWindow::update_window_button(::Window& window, bool show_as_active) -{ - auto* button = window.button(); - if (!button) - return; - button->set_text(String::from_byte_string(window.title()).release_value_but_fixme_should_propagate_errors()); - button->set_tooltip(String::from_byte_string(window.title()).release_value_but_fixme_should_propagate_errors()); - button->set_checked(show_as_active); - button->set_visible(is_window_on_current_workspace(window)); -} - -void TaskbarWindow::event(Core::Event& event) -{ - switch (event.type()) { - case GUI::Event::MouseDown: { - // If the cursor is at the edge/corner of the screen but technically not within the start button (or other taskbar buttons), - // we adjust it so that the nearest button ends up being clicked anyways. - - auto& mouse_event = static_cast(event); - int const ADJUSTMENT = 4; - auto adjusted_x = AK::clamp(mouse_event.x(), ADJUSTMENT, width() - ADJUSTMENT); - auto adjusted_y = AK::min(mouse_event.y(), height() - ADJUSTMENT); - Gfx::IntPoint adjusted_point = { adjusted_x, adjusted_y }; - - if (adjusted_point != mouse_event.position()) { - GUI::ConnectionToWindowServer::the().async_set_global_cursor_position(position() + adjusted_point); - GUI::MouseEvent adjusted_event = { (GUI::Event::Type)mouse_event.type(), adjusted_point, mouse_event.buttons(), mouse_event.button(), mouse_event.modifiers(), mouse_event.wheel_delta_x(), mouse_event.wheel_delta_y(), mouse_event.wheel_raw_delta_x(), mouse_event.wheel_raw_delta_y() }; - Window::event(adjusted_event); - return; - } - break; - } - case GUI::Event::FontsChange: - set_start_button_font(Gfx::FontDatabase::default_font().bold_variant()); - break; - } - Window::event(event); -} - -void TaskbarWindow::wm_event(GUI::WMEvent& event) -{ - WindowIdentifier identifier { event.client_id(), event.window_id() }; - switch (event.type()) { - case GUI::Event::WM_WindowRemoved: { - if constexpr (EVENT_DEBUG) { - auto& removed_event = static_cast(event); - dbgln("WM_WindowRemoved: client_id={}, window_id={}", - removed_event.client_id(), - removed_event.window_id()); - } - if (auto* window = WindowList::the().window(identifier)) - remove_window_button(*window, true); - WindowList::the().remove_window(identifier); - update(); - break; - } - case GUI::Event::WM_WindowRectChanged: { - if constexpr (EVENT_DEBUG) { - auto& changed_event = static_cast(event); - dbgln("WM_WindowRectChanged: client_id={}, window_id={}, rect={}", - changed_event.client_id(), - changed_event.window_id(), - changed_event.rect()); - } - break; - } - - case GUI::Event::WM_WindowIconBitmapChanged: { - auto& changed_event = static_cast(event); - if (auto* window = WindowList::the().window(identifier)) { - if (window->button()) { - auto icon = changed_event.bitmap(); - if (icon->height() != taskbar_icon_size() || icon->width() != taskbar_icon_size()) { - auto sw = taskbar_icon_size() / (float)icon->width(); - auto sh = taskbar_icon_size() / (float)icon->height(); - auto scaled_bitmap_or_error = icon->scaled(sw, sh); - if (scaled_bitmap_or_error.is_error()) - window->button()->set_icon(nullptr); - else - window->button()->set_icon(scaled_bitmap_or_error.release_value()); - } else { - window->button()->set_icon(icon); - } - } - } - break; - } - - case GUI::Event::WM_WindowStateChanged: { - auto& changed_event = static_cast(event); - if constexpr (EVENT_DEBUG) { - dbgln("WM_WindowStateChanged: client_id={}, window_id={}, title={}, rect={}, is_active={}, is_blocked={}, is_minimized={}", - changed_event.client_id(), - changed_event.window_id(), - changed_event.title(), - changed_event.rect(), - changed_event.is_active(), - changed_event.is_blocked(), - changed_event.is_minimized()); - } - if (changed_event.window_type() != GUI::WindowType::Normal || changed_event.is_frameless()) { - if (auto* window = WindowList::the().window(identifier)) - remove_window_button(*window, false); - break; - } - auto& window = WindowList::the().ensure_window(identifier); - window.set_title(changed_event.title()); - window.set_rect(changed_event.rect()); - window.set_active(changed_event.is_active()); - window.set_blocked(changed_event.is_blocked()); - window.set_minimized(changed_event.is_minimized()); - window.set_progress(changed_event.progress()); - window.set_workspace(changed_event.workspace_row(), changed_event.workspace_column()); - add_window_button(window, identifier); - update_window_button(window, window.is_active()); - break; - } - case GUI::Event::WM_AppletAreaSizeChanged: { - auto& changed_event = static_cast(event); - m_applet_area_size = changed_event.size(); - m_applet_area_container->set_fixed_size(changed_event.size().width() + 8, 21); - update_applet_area(); - break; - } - case GUI::Event::WM_SuperKeyPressed: { - if (!m_system_menu) - break; - - if (m_system_menu->is_visible()) { - m_system_menu->dismiss(); - } else { - m_system_menu->popup(m_start_button->screen_relative_rect().top_left()); - } - break; - } - case GUI::Event::WM_SuperSpaceKeyPressed: { - if (auto result = m_assistant_app_file->spawn(); result.is_error()) - warnln("Failed to spawn 'Assistant' when requested via Super+Space: {}", result.error()); - break; - } - case GUI::Event::WM_SuperDKeyPressed: { - toggle_show_desktop(); - break; - } - case GUI::Event::WM_SuperDigitKeyPressed: { - auto& digit_event = static_cast(event); - auto index = digit_event.digit() != 0 ? digit_event.digit() - 1 : 9; - - for (auto& widget : m_task_button_container->child_widgets()) { - // NOTE: The button might be invisible depending on the current workspace - if (!widget.is_visible()) - continue; - - if (index == 0) { - static_cast(widget).click(); - break; - } - - --index; - } - break; - } - case GUI::Event::WM_WorkspaceChanged: { - auto& changed_event = static_cast(event); - workspace_change_event(changed_event.current_row(), changed_event.current_column()); - break; - } - case GUI::Event::WM_AddToQuickLaunch: { - auto add_event = static_cast(event); - auto result = m_quick_launch->add_from_pid(add_event.pid()); - if (result.is_error()) { - dbgln("Couldn't add pid {} to quick launch menu: {}", add_event.pid(), result.error()); - GUI::MessageBox::show_error(this, String::formatted("Failed to add to Quick Launch: {}", result.release_error()).release_value_but_fixme_should_propagate_errors()); - } else if (!result.release_value()) { - dbgln("Couldn't add pid {} to quick launch menu due to an unexpected error", add_event.pid()); - GUI::MessageBox::show_error(this, "Failed to add to Quick Launch due to an unexpected error."sv); - } - break; - } - default: - break; - } -} - -void TaskbarWindow::screen_rects_change_event(GUI::ScreenRectsChangeEvent& event) -{ - on_screen_rects_change(event.rects(), event.main_screen_index()); -} - -bool TaskbarWindow::is_window_on_current_workspace(::Window& window) const -{ - return window.workspace_row() == m_current_workspace_row && window.workspace_column() == m_current_workspace_column; -} - -void TaskbarWindow::workspace_change_event(unsigned current_row, unsigned current_column) -{ - m_current_workspace_row = current_row; - m_current_workspace_column = current_column; - - WindowList::the().for_each_window([&](auto& window) { - if (auto* button = window.button()) - button->set_visible(is_window_on_current_workspace(window)); - }); -} - -void TaskbarWindow::set_start_button_font(Gfx::Font const& font) -{ - m_start_button->set_font(font); - m_start_button->set_fixed_size(font.width(m_start_button->text()) + 30, 21); -} diff --git a/Userland/Services/Taskbar/TaskbarWindow.h b/Userland/Services/Taskbar/TaskbarWindow.h deleted file mode 100644 index a331bc2b995..00000000000 --- a/Userland/Services/Taskbar/TaskbarWindow.h +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include "ClockWidget.h" -#include "WindowList.h" -#include -#include -#include -#include -#include -#include -#include -#include - -class TaskbarWindow final : public GUI::Window - , public Config::Listener { - C_OBJECT(TaskbarWindow); - -public: - static ErrorOr> create(); - virtual ~TaskbarWindow() override = default; - - static int taskbar_height() { return 27; } - static int taskbar_icon_size() { return 16; } - - virtual void config_string_did_change(StringView, StringView, StringView, StringView) override; - virtual void add_system_menu(NonnullRefPtr system_menu); - -private: - explicit TaskbarWindow(); - static void show_desktop_button_clicked(unsigned); - static void toggle_show_desktop(); - void on_screen_rects_change(Vector const&, size_t); - NonnullRefPtr create_button(WindowIdentifier const&); - void add_window_button(::Window&, WindowIdentifier const&); - void remove_window_button(::Window&, bool); - void update_window_button(::Window&, bool); - - ErrorOr populate_taskbar(); - ErrorOr load_assistant(); - - virtual void event(Core::Event&) override; - virtual void wm_event(GUI::WMEvent&) override; - virtual void screen_rects_change_event(GUI::ScreenRectsChangeEvent&) override; - - void update_applet_area(); - - bool is_window_on_current_workspace(::Window&) const; - void workspace_change_event(unsigned, unsigned); - - void set_start_button_font(Gfx::Font const&); - - RefPtr m_system_menu; - RefPtr m_task_button_container; - RefPtr m_default_icon; - - Gfx::IntSize m_applet_area_size; - RefPtr m_applet_area_container; - RefPtr m_start_button; - RefPtr m_show_desktop_button; - RefPtr m_quick_launch; - RefPtr m_clock_widget; - - RefPtr m_assistant_app_file; - - unsigned m_current_workspace_row { 0 }; - unsigned m_current_workspace_column { 0 }; -}; diff --git a/Userland/Services/Taskbar/WindowIdentifier.h b/Userland/Services/Taskbar/WindowIdentifier.h deleted file mode 100644 index b16e9d36f1d..00000000000 --- a/Userland/Services/Taskbar/WindowIdentifier.h +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include - -class WindowIdentifier { -public: - WindowIdentifier() = default; - WindowIdentifier(int client_id, int window_id) - : m_client_id(client_id) - , m_window_id(window_id) - { - } - - int client_id() const { return m_client_id; } - int window_id() const { return m_window_id; } - - bool operator==(WindowIdentifier const& other) const - { - return m_client_id == other.m_client_id && m_window_id == other.m_window_id; - } - - bool is_valid() const - { - return m_client_id != -1 && m_window_id != -1; - } - -private: - int m_client_id { -1 }; - int m_window_id { -1 }; -}; - -namespace AK { -template<> -struct Traits : public DefaultTraits { - static unsigned hash(WindowIdentifier const& w) { return pair_int_hash(w.client_id(), w.window_id()); } -}; -} diff --git a/Userland/Services/Taskbar/WindowList.cpp b/Userland/Services/Taskbar/WindowList.cpp deleted file mode 100644 index 8492ec36a5b..00000000000 --- a/Userland/Services/Taskbar/WindowList.cpp +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "WindowList.h" - -WindowList& WindowList::the() -{ - static WindowList s_the; - return s_the; -} - -Window* WindowList::window(WindowIdentifier const& identifier) -{ - auto it = m_windows.find(identifier); - if (it != m_windows.end()) - return it->value; - return nullptr; -} - -Window& WindowList::ensure_window(WindowIdentifier const& identifier) -{ - auto it = m_windows.find(identifier); - if (it != m_windows.end()) - return *it->value; - auto window = make(identifier); - auto& window_ref = *window; - m_windows.set(identifier, move(window)); - return window_ref; -} - -void WindowList::remove_window(WindowIdentifier const& identifier) -{ - m_windows.remove(identifier); -} diff --git a/Userland/Services/Taskbar/WindowList.h b/Userland/Services/Taskbar/WindowList.h deleted file mode 100644 index 3d372bc1909..00000000000 --- a/Userland/Services/Taskbar/WindowList.h +++ /dev/null @@ -1,100 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include "WindowIdentifier.h" -#include -#include -#include -#include - -class Window { -public: - explicit Window(WindowIdentifier const& identifier) - : m_identifier(identifier) - { - } - - ~Window() - { - if (m_button) - m_button->remove_from_parent(); - } - - WindowIdentifier const& identifier() const { return m_identifier; } - - ByteString title() const { return m_title; } - void set_title(ByteString const& title) { m_title = title; } - - Gfx::IntRect rect() const { return m_rect; } - void set_rect(Gfx::IntRect const& rect) { m_rect = rect; } - - GUI::Button* button() { return m_button; } - void set_button(GUI::Button* button) { m_button = button; } - - void set_active(bool active) { m_active = active; } - bool is_active() const { return m_active; } - - void set_blocked(bool blocked) { m_blocked = blocked; } - bool is_blocked() const { return m_blocked; } - - void set_minimized(bool minimized) { m_minimized = minimized; } - bool is_minimized() const { return m_minimized; } - - void set_workspace(unsigned row, unsigned column) - { - m_workspace_row = row; - m_workspace_column = column; - } - unsigned workspace_row() const { return m_workspace_row; } - unsigned workspace_column() const { return m_workspace_column; } - - void set_progress(Optional progress) - { - if (m_progress == progress) - return; - m_progress = progress; - if (m_button) - m_button->update(); - } - - Optional progress() const { return m_progress; } - - Gfx::Bitmap const* icon() const { return m_icon.ptr(); } - -private: - WindowIdentifier m_identifier; - ByteString m_title; - Gfx::IntRect m_rect; - RefPtr m_button; - RefPtr m_icon; - unsigned m_workspace_row { 0 }; - unsigned m_workspace_column { 0 }; - bool m_active { false }; - bool m_blocked { false }; - bool m_minimized { false }; - Optional m_progress; -}; - -class WindowList { -public: - static WindowList& the(); - - template - void for_each_window(Callback callback) - { - for (auto& it : m_windows) - callback(*it.value); - } - - Window* window(WindowIdentifier const&); - Window& ensure_window(WindowIdentifier const&); - void remove_window(WindowIdentifier const&); - -private: - HashMap> m_windows; -}; diff --git a/Userland/Services/Taskbar/main.cpp b/Userland/Services/Taskbar/main.cpp deleted file mode 100644 index d41c13dbf87..00000000000 --- a/Userland/Services/Taskbar/main.cpp +++ /dev/null @@ -1,262 +0,0 @@ -/* - * Copyright (c) 2018-2021, Andreas Kling - * Copyright (c) 2022, networkException - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "ShutdownDialog.h" -#include "TaskbarWindow.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -static ErrorOr> discover_apps_and_categories(); -static ErrorOr> build_system_menu(GUI::Window&); - -ErrorOr serenity_main(Main::Arguments arguments) -{ - TRY(Core::System::pledge("stdio recvfd sendfd proc exec rpath unix sigaction")); - auto app = TRY(GUI::Application::create(arguments)); - Config::pledge_domains({ "Taskbar", "Calendar" }); - Config::monitor_domain("Taskbar"); - Config::monitor_domain("Calendar"); - app->event_loop().register_signal(SIGCHLD, [](int) { - // Wait all available children - while (waitpid(-1, nullptr, WNOHANG) > 0) - ; - }); - - TRY(Core::System::pledge("stdio recvfd sendfd proc exec rpath unix")); - - GUI::ConnectionToWindowManagerServer::the(); - Desktop::Launcher::ensure_connection(); - - TRY(Core::System::pledge("stdio recvfd sendfd proc exec rpath")); - - auto window = TRY(TaskbarWindow::create()); - - auto menu = TRY(build_system_menu(*window)); - menu->realize_menu_if_needed(); - window->add_system_menu(menu); - - window->show(); - - window->make_window_manager( - WindowServer::WMEventMask::WindowStateChanges - | WindowServer::WMEventMask::WindowRemovals - | WindowServer::WMEventMask::WindowIconChanges - | WindowServer::WMEventMask::WorkspaceChanges); - - return app->exec(); -} - -Vector> g_apps; - -Color g_menu_selection_color; - -Vector g_themes; -RefPtr g_themes_menu; -GUI::ActionGroup g_themes_group; - -static bool less_than_ignore_hotkey(ByteString const& a, ByteString const& b) -{ - auto a_it = a.begin(), b_it = b.begin(); - while (a_it != a.end() && b_it != b.end()) { - if (*a_it == '&') { - ++a_it; - continue; - } - if (*b_it == '&') { - ++b_it; - continue; - } - if (*a_it < *b_it) { - return true; - } - if (*a_it > *b_it) { - return false; - } - ++a_it; - ++b_it; - } - return a_it != a.end(); -} - -ErrorOr> discover_apps_and_categories() -{ - HashTable seen_app_categories; - Desktop::AppFile::for_each([&](auto af) { - if (af->exclude_from_system_menu()) - return; - if (access(af->executable().characters(), X_OK) == 0) { - seen_app_categories.set(af->category()); - g_apps.append(move(af)); - } - }); - quick_sort(g_apps, [](auto& a, auto& b) { return a->name() < b->name(); }); - - Vector sorted_app_categories; - TRY(sorted_app_categories.try_ensure_capacity(seen_app_categories.size())); - for (auto const& category : seen_app_categories) - sorted_app_categories.unchecked_append(category); - quick_sort(sorted_app_categories, less_than_ignore_hotkey); - - return sorted_app_categories; -} - -ErrorOr> build_system_menu(GUI::Window& window) -{ - Vector const sorted_app_categories = TRY(discover_apps_and_categories()); - auto system_menu = GUI::Menu::construct("\xE2\x9A\xA1"_string); // HIGH VOLTAGE SIGN - - system_menu->add_action(GUI::Action::create("&About SerenityOS", TRY(Gfx::Bitmap::load_from_file("/res/icons/16x16/ladyball.png"sv)), [&](auto&) { - GUI::Process::spawn_or_show_error(&window, "/bin/About"sv); - })); - - system_menu->add_separator(); - - // First we construct all the necessary app category submenus. - auto category_icons = TRY(Core::ConfigFile::open("/res/icons/SystemMenu.ini")); - HashMap> app_category_menus; - - Function create_category_menu; - create_category_menu = [&](ByteString const& category) { - if (app_category_menus.contains(category)) - return; - ByteString parent_category, child_category = category; - for (ssize_t i = category.length() - 1; i >= 0; i--) { - if (category[i] == '/') { - parent_category = category.substring(0, i); - child_category = category.substring(i + 1); - } - } - GUI::Menu* parent_menu; - if (parent_category.is_empty()) { - parent_menu = system_menu; - } else { - parent_menu = app_category_menus.get(parent_category).value(); - if (!parent_menu) { - create_category_menu(parent_category); - parent_menu = app_category_menus.get(parent_category).value(); - VERIFY(parent_menu); - } - } - auto category_menu = parent_menu->add_submenu(String::from_byte_string(child_category).release_value_but_fixme_should_propagate_errors()); - auto category_icon_path = category_icons->read_entry("16x16", category); - if (!category_icon_path.is_empty()) { - auto icon_or_error = Gfx::Bitmap::load_from_file(category_icon_path); - if (!icon_or_error.is_error()) - category_menu->set_icon(icon_or_error.release_value()); - } - app_category_menus.set(category, category_menu); - }; - - for (auto const& category : sorted_app_categories) - create_category_menu(category); - - // Then we create and insert all the app menu items into the right place. - int app_identifier = 0; - for (auto const& app : g_apps) { - auto icon = app->icon().bitmap_for_size(16); - - if constexpr (SYSTEM_MENU_DEBUG) { - if (icon) - dbgln("App {} has icon with size {}", app->name(), icon->size()); - } - - auto parent_menu = app_category_menus.get(app->category()).value_or(system_menu.ptr()); - parent_menu->add_action(GUI::Action::create(app->menu_name(), icon, [app_identifier, &window](auto&) { - dbgln("Activated app with ID {}", app_identifier); - auto& app = g_apps[app_identifier]; - app->spawn_with_escalation_or_show_error(window); - })); - ++app_identifier; - } - - system_menu->add_separator(); - - g_themes_group.set_exclusive(true); - g_themes_group.set_unchecking_allowed(false); - - g_themes_menu = system_menu->add_submenu("&Themes"_string); - g_themes_menu->set_icon(TRY(Gfx::Bitmap::load_from_file("/res/icons/16x16/themes.png"sv))); - - g_themes = TRY(Gfx::list_installed_system_themes()); - auto current_theme_name = GUI::ConnectionToWindowServer::the().get_system_theme(); - - { - int theme_identifier = 0; - for (auto& theme : g_themes) { - auto action = GUI::Action::create_checkable(theme.menu_name, [theme_identifier, current_theme_name, &window](auto&) { - auto& theme = g_themes[theme_identifier]; - dbgln("Theme switched from {} to {} at path {}", current_theme_name, theme.name, theme.path); - if (window.main_widget()->palette().color_scheme_path() != ""sv) - VERIFY(GUI::ConnectionToWindowServer::the().set_system_theme(theme.path, theme.name, false, GUI::ConnectionToWindowServer::the().get_preferred_color_scheme())); - else - VERIFY(GUI::ConnectionToWindowServer::the().set_system_theme(theme.path, theme.name, false, "Custom"sv)); - }); - if (theme.name == current_theme_name) - action->set_checked(true); - g_themes_group.add_action(action); - g_themes_menu->add_action(action); - ++theme_identifier; - } - } - - GUI::Application::the()->on_theme_change = [&]() { - if (g_themes_menu->is_visible()) - return; - auto current_theme_name = GUI::ConnectionToWindowServer::the().get_system_theme(); - auto theme_overridden = GUI::ConnectionToWindowServer::the().is_system_theme_overridden(); - VERIFY(g_themes.size() == g_themes_menu->items().size()); - for (size_t index = 0; index < g_themes.size(); ++index) { - auto* action = g_themes_menu->action_at(index); - auto& theme = g_themes[index]; - action->set_checked(!theme_overridden && theme.name == current_theme_name); - } - }; - - system_menu->add_action(GUI::Action::create("&Settings", TRY(Gfx::Bitmap::load_from_file("/res/icons/16x16/app-settings.png"sv)), [&](auto&) { - GUI::Process::spawn_or_show_error(&window, "/bin/Settings"sv); - })); - - system_menu->add_separator(); - system_menu->add_action(GUI::Action::create("&Help", TRY(Gfx::Bitmap::load_from_file("/res/icons/16x16/app-help.png"sv)), [&](auto&) { - GUI::Process::spawn_or_show_error(&window, "/bin/Help"sv); - })); - system_menu->add_action(GUI::Action::create("&Run...", TRY(Gfx::Bitmap::load_from_file("/res/icons/16x16/app-run.png"sv)), [&](auto&) { - GUI::Process::spawn_or_show_error(&window, "/bin/Run"sv, ReadonlySpan {}, Core::StandardPaths::home_directory()); - })); - system_menu->add_separator(); - system_menu->add_action(GUI::Action::create("E&xit...", TRY(Gfx::Bitmap::load_from_file("/res/icons/16x16/power.png"sv)), [&](auto&) { - if (auto command = ShutdownDialog::show(); command.has_value()) - GUI::Process::spawn_or_show_error(&window, command->executable, command->arguments); - })); - - return system_menu; -} diff --git a/Userland/Services/TelnetServer/CMakeLists.txt b/Userland/Services/TelnetServer/CMakeLists.txt deleted file mode 100644 index f8a869ad182..00000000000 --- a/Userland/Services/TelnetServer/CMakeLists.txt +++ /dev/null @@ -1,13 +0,0 @@ -serenity_component( - TelnetServer - TARGETS TelnetServer -) - -set(SOURCES - Client.cpp - main.cpp - Parser.cpp -) - -serenity_bin(TelnetServer) -target_link_libraries(TelnetServer PRIVATE LibCore LibMain) diff --git a/Userland/Services/TelnetServer/Client.cpp b/Userland/Services/TelnetServer/Client.cpp deleted file mode 100644 index efd83fe60dd..00000000000 --- a/Userland/Services/TelnetServer/Client.cpp +++ /dev/null @@ -1,217 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "Client.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -Client::Client(int id, NonnullOwnPtr socket, int ptm_fd) - : m_id(id) - , m_socket(move(socket)) - , m_ptm_fd(ptm_fd) - , m_ptm_notifier(Core::Notifier::construct(ptm_fd, Core::Notifier::Type::Read)) -{ - m_socket->on_ready_to_read = [this] { - auto result = drain_socket(); - if (result.is_error()) { - dbgln("Failed to drain the socket: {}", result.error()); - Core::deferred_invoke([this, strong_this = NonnullRefPtr(*this)] { quit(); }); - } - }; - - m_ptm_notifier->on_activation = [this] { - auto result = drain_pty(); - if (result.is_error()) { - dbgln("Failed to drain the PTY: {}", result.error()); - Core::deferred_invoke([this, strong_this = NonnullRefPtr(*this)] { quit(); }); - } - }; - - m_parser.on_command = [this](Command const& command) { - auto result = handle_command(command); - if (result.is_error()) { - dbgln("Failed to handle the command: {}", result.error()); - Core::deferred_invoke([this, strong_this = NonnullRefPtr(*this)] { quit(); }); - } - }; - - m_parser.on_data = [this](StringView data) { handle_data(data); }; - m_parser.on_error = [this]() { handle_error(); }; -} - -ErrorOr> Client::create(int id, NonnullOwnPtr socket, int ptm_fd) -{ - auto client = adopt_ref(*new Client(id, move(socket), ptm_fd)); - - auto result = client->send_commands({ - { CMD_WILL, SUB_SUPPRESS_GO_AHEAD }, - { CMD_WILL, SUB_ECHO }, - { CMD_DO, SUB_SUPPRESS_GO_AHEAD }, - { CMD_DONT, SUB_ECHO }, - }); - if (result.is_error()) { - client->quit(); - return result.release_error(); - } - - return client; -} - -ErrorOr Client::drain_socket() -{ - NonnullRefPtr protect(*this); - - auto buffer = TRY(ByteBuffer::create_uninitialized(1024)); - - while (TRY(m_socket->can_read_without_blocking())) { - auto read_bytes = TRY(m_socket->read_some(buffer)); - - m_parser.write(StringView { read_bytes }); - - if (m_socket->is_eof()) { - Core::deferred_invoke([this, strong_this = NonnullRefPtr(*this)] { quit(); }); - break; - } - } - - return {}; -} - -ErrorOr Client::drain_pty() -{ - u8 buffer[BUFSIZ]; - ssize_t nread = read(m_ptm_fd, buffer, sizeof(buffer)); - if (nread < 0) { - Core::deferred_invoke([this, strong_this = NonnullRefPtr(*this)] { quit(); }); - return static_cast(errno); - } - if (nread == 0) { - Core::deferred_invoke([this, strong_this = NonnullRefPtr(*this)] { quit(); }); - return {}; - } - - return send_data({ buffer, (size_t)nread }); -} - -void Client::handle_data(StringView data) -{ - write(m_ptm_fd, data.characters_without_null_termination(), data.length()); -} - -ErrorOr Client::handle_command(Command const& command) -{ - switch (command.command) { - case CMD_DO: - // no response - we've already advertised our options, and none of - // them can be disabled (or re-enabled) after connecting. - break; - case CMD_DONT: - // no response - we only "support" two options (echo and suppress - // go-ahead), and both of them are always enabled. - break; - case CMD_WILL: - switch (command.subcommand) { - case SUB_ECHO: - // we always want to be the ones in control of the output. tell - // the client to disable local echo. - TRY(send_command({ CMD_DONT, SUB_ECHO })); - break; - case SUB_SUPPRESS_GO_AHEAD: - TRY(send_command({ CMD_DO, SUB_SUPPRESS_GO_AHEAD })); - break; - default: - // don't respond to unknown commands - break; - } - break; - case CMD_WONT: - // no response - we don't care about anything the client says they - // won't do. - break; - } - - return {}; -} - -void Client::handle_error() -{ - Core::deferred_invoke([this, strong_this = NonnullRefPtr(*this)] { quit(); }); -} - -ErrorOr Client::send_data(StringView data) -{ - bool fast = true; - for (size_t i = 0; i < data.length(); i++) { - u8 c = data[i]; - if (c == '\n' || c == 0xff) - fast = false; - } - - if (fast) { - TRY(m_socket->write_until_depleted({ data.characters_without_null_termination(), data.length() })); - return {}; - } - - StringBuilder builder; - for (size_t i = 0; i < data.length(); i++) { - u8 c = data[i]; - - switch (c) { - case '\n': - builder.append("\r\n"sv); - break; - case IAC: - builder.append("\xff\xff"sv); - break; - default: - builder.append(c); - break; - } - } - - auto builder_contents = TRY(builder.to_byte_buffer()); - TRY(m_socket->write_until_depleted(builder_contents)); - return {}; -} - -ErrorOr Client::send_command(Command command) -{ - return send_commands({ command }); -} - -ErrorOr Client::send_commands(Vector commands) -{ - auto buffer = TRY(ByteBuffer::create_uninitialized(commands.size() * 3)); - FixedMemoryStream stream { buffer.span() }; - - for (auto& command : commands) { - MUST(stream.write_value(IAC)); - MUST(stream.write_value(command.command)); - MUST(stream.write_value(command.subcommand)); - } - - VERIFY(TRY(stream.tell()) == buffer.size()); - TRY(m_socket->write_until_depleted({ buffer.data(), buffer.size() })); - return {}; -} - -void Client::quit() -{ - m_ptm_notifier->set_enabled(false); - close(m_ptm_fd); - m_socket->close(); - if (on_exit) - on_exit(); -} diff --git a/Userland/Services/TelnetServer/Client.h b/Userland/Services/TelnetServer/Client.h deleted file mode 100644 index 32836e487fc..00000000000 --- a/Userland/Services/TelnetServer/Client.h +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include - -#include "Command.h" -#include "Parser.h" - -class Client : public RefCounted { -public: - static ErrorOr> create(int id, NonnullOwnPtr socket, int ptm_fd); - - Function on_exit; - -private: - Client(int id, NonnullOwnPtr socket, int ptm_fd); - - ErrorOr drain_socket(); - ErrorOr drain_pty(); - ErrorOr handle_command(Command const& command); - ErrorOr send_data(StringView str); - ErrorOr send_command(Command command); - ErrorOr send_commands(Vector commands); - - void handle_data(StringView); - void handle_error(); - void quit(); - - // client id - int m_id { 0 }; - // client resources - NonnullOwnPtr m_socket; - Parser m_parser; - // pty resources - int m_ptm_fd { -1 }; - RefPtr m_ptm_notifier; -}; diff --git a/Userland/Services/TelnetServer/Command.h b/Userland/Services/TelnetServer/Command.h deleted file mode 100644 index afa14bc0bfd..00000000000 --- a/Userland/Services/TelnetServer/Command.h +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include - -#define CMD_WILL 0xfb -#define CMD_WONT 0xfc -#define CMD_DO 0xfd -#define CMD_DONT 0xfe -#define SUB_ECHO 0x01 -#define SUB_SUPPRESS_GO_AHEAD 0x03 - -struct Command { - u8 command; - u8 subcommand; - - ByteString to_byte_string() const - { - StringBuilder builder; - - switch (command) { - case CMD_WILL: - builder.append("WILL"sv); - break; - case CMD_WONT: - builder.append("WONT"sv); - break; - case CMD_DO: - builder.append("DO"sv); - break; - case CMD_DONT: - builder.append("DONT"sv); - break; - default: - builder.append(ByteString::formatted("UNKNOWN<{:02x}>", command)); - break; - } - - builder.append(" "sv); - - switch (subcommand) { - case SUB_ECHO: - builder.append("ECHO"sv); - break; - case SUB_SUPPRESS_GO_AHEAD: - builder.append("SUPPRESS_GO_AHEAD"sv); - break; - default: - builder.append(ByteString::formatted("UNKNOWN<{:02x}>", subcommand)); - break; - } - - return builder.to_byte_string(); - } -}; diff --git a/Userland/Services/TelnetServer/Parser.cpp b/Userland/Services/TelnetServer/Parser.cpp deleted file mode 100644 index 3e84ffab2de..00000000000 --- a/Userland/Services/TelnetServer/Parser.cpp +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include - -#include "Parser.h" - -void Parser::write(StringView data) -{ - for (size_t i = 0; i < data.length(); i++) { - u8 ch = data[i]; - - switch (m_state) { - case State::Free: - switch (ch) { - case IAC: - m_state = State::ReadCommand; - break; - case '\r': - if (on_data) - on_data("\n"sv); - break; - case '\0': - case '\n': - // Ignore. - break; - default: - if (on_data) - on_data(StringView(&ch, 1)); - break; - } - break; - case State::ReadCommand: - switch (ch) { - case IAC: { - m_state = State::Free; - if (on_data) - on_data("\xff"sv); - break; - } - case CMD_WILL: - case CMD_WONT: - case CMD_DO: - case CMD_DONT: - m_command = ch; - m_state = State::ReadSubcommand; - break; - default: - m_state = State::Error; - if (on_error) - on_error(); - break; - } - break; - case State::ReadSubcommand: { - auto command = m_command; - m_command = 0; - m_state = State::Free; - if (on_command) - on_command({ command, ch }); - break; - } - case State::Error: - // ignore everything - break; - } - } -} diff --git a/Userland/Services/TelnetServer/Parser.h b/Userland/Services/TelnetServer/Parser.h deleted file mode 100644 index d57754fbd8b..00000000000 --- a/Userland/Services/TelnetServer/Parser.h +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include - -#include "Command.h" - -#define IAC 0xff - -class Parser { -public: - Function on_command; - Function on_data; - Function on_error; - - void write(StringView); - -protected: - enum State { - Free, - ReadCommand, - ReadSubcommand, - Error, - }; - -private: - State m_state { State::Free }; - u8 m_command { 0 }; -}; diff --git a/Userland/Services/TelnetServer/main.cpp b/Userland/Services/TelnetServer/main.cpp deleted file mode 100644 index c9beb12e85c..00000000000 --- a/Userland/Services/TelnetServer/main.cpp +++ /dev/null @@ -1,151 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "Client.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -static void run_command(int ptm_fd, ByteString command) -{ - pid_t pid = fork(); - if (pid == 0) { - char const* tty_name = ptsname(ptm_fd); - if (!tty_name) { - perror("ptsname"); - exit(1); - } - close(ptm_fd); - int pts_fd = open(tty_name, O_RDWR); - if (pts_fd < 0) { - perror("open"); - exit(1); - } - - // NOTE: It's okay if this fails. - [[maybe_unused]] auto rc = ioctl(0, TIOCNOTTY); - - close(0); - close(1); - close(2); - - rc = dup2(pts_fd, 0); - if (rc < 0) { - perror("dup2"); - exit(1); - } - rc = dup2(pts_fd, 1); - if (rc < 0) { - perror("dup2"); - exit(1); - } - rc = dup2(pts_fd, 2); - if (rc < 0) { - perror("dup2"); - exit(1); - } - rc = close(pts_fd); - if (rc < 0) { - perror("close"); - exit(1); - } - rc = ioctl(0, TIOCSCTTY); - if (rc < 0) { - perror("ioctl(TIOCSCTTY)"); - exit(1); - } - char const* args[4] = { "/bin/Shell", nullptr, nullptr, nullptr }; - if (!command.is_empty()) { - args[1] = "-c"; - args[2] = command.characters(); - } - char const* envs[] = { "TERM=xterm", "PATH=" DEFAULT_PATH, nullptr }; - rc = execve("/bin/Shell", const_cast(args), const_cast(envs)); - if (rc < 0) { - perror("execve"); - exit(1); - } - VERIFY_NOT_REACHED(); - } -} - -ErrorOr serenity_main(Main::Arguments arguments) -{ - int port = 23; - StringView command = ""sv; - - Core::ArgsParser args_parser; - args_parser.add_option(port, "Port to listen on", nullptr, 'p', "port"); - args_parser.add_option(command, "Program to run on connection", nullptr, 'c', "command"); - args_parser.parse(arguments); - - if ((u16)port != port) { - warnln("Invalid port number: {}", port); - exit(1); - } - - Core::EventLoop event_loop; - auto server = TRY(Core::TCPServer::try_create()); - TRY(server->listen({}, port)); - - IGNORE_USE_IN_ESCAPING_LAMBDA HashMap> clients; - int next_id = 0; - - server->on_ready_to_accept = [&next_id, &clients, &server, command] { - int id = next_id++; - - auto maybe_client_socket = server->accept(); - if (maybe_client_socket.is_error()) { - warnln("accept: {}", maybe_client_socket.error()); - return; - } - auto client_socket = maybe_client_socket.release_value(); - - int ptm_fd = posix_openpt(O_RDWR); - if (ptm_fd < 0) { - perror("posix_openpt"); - client_socket->close(); - return; - } - if (grantpt(ptm_fd) < 0) { - perror("grantpt"); - client_socket->close(); - return; - } - if (unlockpt(ptm_fd) < 0) { - perror("unlockpt"); - client_socket->close(); - return; - } - - run_command(ptm_fd, command); - - auto maybe_client = Client::create(id, move(client_socket), ptm_fd); - if (maybe_client.is_error()) { - warnln("Failed to create the client: {}", maybe_client.error()); - return; - } - - auto client = maybe_client.release_value(); - client->on_exit = [&clients, id] { - Core::deferred_invoke([&clients, id] { clients.remove(id); }); - }; - clients.set(id, client); - }; - - return event_loop.exec(); -} diff --git a/Userland/Services/WebServer/CMakeLists.txt b/Userland/Services/WebServer/CMakeLists.txt deleted file mode 100644 index 313590acfc4..00000000000 --- a/Userland/Services/WebServer/CMakeLists.txt +++ /dev/null @@ -1,13 +0,0 @@ -serenity_component( - WebServer - TARGETS WebServer -) - -set(SOURCES - Client.cpp - Configuration.cpp - main.cpp -) - -serenity_bin(WebServer) -target_link_libraries(WebServer PRIVATE LibCore LibFileSystem LibHTTP LibMain LibURL) diff --git a/Userland/Services/WebServer/Client.cpp b/Userland/Services/WebServer/Client.cpp deleted file mode 100644 index 4ca4996c387..00000000000 --- a/Userland/Services/WebServer/Client.cpp +++ /dev/null @@ -1,393 +0,0 @@ -/* - * Copyright (c) 2020, Andreas Kling - * Copyright (c) 2021, Max Wipfli - * Copyright (c) 2022, Thomas Keppler - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace WebServer { - -Client::Client(NonnullOwnPtr socket, Core::EventReceiver* parent) - : Core::EventReceiver(parent) - , m_socket(move(socket)) -{ -} - -void Client::die() -{ - m_socket->close(); - deferred_invoke([this] { remove_from_parent(); }); -} - -void Client::start() -{ - m_socket->on_ready_to_read = [this] { - if (auto result = on_ready_to_read(); result.is_error()) { - result.error().visit( - [](AK::Error const& error) { - warnln("Internal error: {}", error); - }, - [](HTTP::HttpRequest::ParseError const& error) { - warnln("HTTP request parsing error: {}", HTTP::HttpRequest::parse_error_to_string(error)); - }); - - die(); - } - }; -} - -ErrorOr Client::on_ready_to_read() -{ - // FIXME: Mostly copied from LibWeb/WebDriver/Client.cpp. As noted there, this should be move the LibHTTP and made spec compliant. - auto buffer = TRY(ByteBuffer::create_uninitialized(m_socket->buffer_size())); - - for (;;) { - if (!TRY(m_socket->can_read_without_blocking())) - break; - - auto data = TRY(m_socket->read_some(buffer)); - TRY(m_remaining_request.try_append(StringView { data })); - - if (m_socket->is_eof()) - break; - } - - if (m_remaining_request.is_empty()) - return {}; - - auto request = TRY(m_remaining_request.to_byte_buffer()); - dbgln_if(WEBSERVER_DEBUG, "Got raw request: '{}'", ByteString::copy(request)); - - auto maybe_parsed_request = HTTP::HttpRequest::from_raw_request(TRY(m_remaining_request.to_byte_buffer())); - if (maybe_parsed_request.is_error()) { - if (maybe_parsed_request.error() == HTTP::HttpRequest::ParseError::RequestIncomplete) { - // If request is not complete we need to wait for more data to arrive - return {}; - } - return maybe_parsed_request.error(); - } - - m_remaining_request.clear(); - - TRY(handle_request(maybe_parsed_request.value())); - - return {}; -} - -ErrorOr Client::handle_request(HTTP::HttpRequest const& request) -{ - auto resource_decoded = URL::percent_decode(request.resource()); - - if constexpr (WEBSERVER_DEBUG) { - dbgln("Got HTTP request: {} {}", request.method_name(), request.resource()); - for (auto& header : request.headers()) { - dbgln(" {} => {}", header.name, header.value); - } - } - - if (request.method() != HTTP::HttpRequest::Method::GET) { - TRY(send_error_response(501, request)); - return false; - } - - // Check for credentials if they are required - if (Configuration::the().credentials().has_value()) { - bool has_authenticated = verify_credentials(request.headers()); - if (!has_authenticated) { - auto const basic_auth_header = "WWW-Authenticate: Basic realm=\"WebServer\", charset=\"UTF-8\""_string; - Vector headers {}; - TRY(headers.try_append(basic_auth_header)); - TRY(send_error_response(401, request, move(headers))); - return false; - } - } - - auto requested_path = TRY(String::from_byte_string(LexicalPath::join("/"sv, resource_decoded).string())); - dbgln_if(WEBSERVER_DEBUG, "Canonical requested path: '{}'", requested_path); - - auto real_path = TRY(String::formatted("{}{}", Configuration::the().document_root_path(), requested_path)); - - if (FileSystem::is_directory(real_path.bytes_as_string_view())) { - if (!resource_decoded.ends_with('/')) { - TRY(send_redirect(TRY(String::formatted("{}/", requested_path)), request)); - return true; - } - - auto index_html_path = TRY(String::formatted("{}/index.html", real_path)); - if (!FileSystem::exists(index_html_path)) { - auto is_searchable_or_error = Core::System::access(real_path.bytes_as_string_view(), X_OK); - if (is_searchable_or_error.is_error()) { - TRY(send_error_response(403, request)); - return false; - } - - TRY(handle_directory_listing(requested_path, real_path, request)); - return true; - } - real_path = index_html_path; - } - - if (!FileSystem::exists(real_path.bytes_as_string_view())) { - TRY(send_error_response(404, request)); - return false; - } - - auto is_readable_or_error = Core::System::access(real_path.bytes_as_string_view(), R_OK); - if (is_readable_or_error.is_error()) { - TRY(send_error_response(403, request)); - return false; - } - - if (FileSystem::is_device(real_path.bytes_as_string_view())) { - TRY(send_error_response(403, request)); - return false; - } - - auto stream = TRY(Core::File::open(real_path.bytes_as_string_view(), Core::File::OpenMode::Read)); - - auto const info = ContentInfo { - .type = TRY(String::from_utf8(Core::guess_mime_type_based_on_filename(real_path.bytes_as_string_view()))), - .length = static_cast(TRY(FileSystem::size_from_stat(real_path.bytes_as_string_view()))) - }; - TRY(send_response(*stream, request, move(info))); - return true; -} - -ErrorOr Client::send_response(Stream& response, HTTP::HttpRequest const& request, ContentInfo content_info) -{ - StringBuilder builder; - TRY(builder.try_append("HTTP/1.0 200 OK\r\n"sv)); - TRY(builder.try_append("Server: WebServer (SerenityOS)\r\n"sv)); - TRY(builder.try_append("X-Frame-Options: SAMEORIGIN\r\n"sv)); - TRY(builder.try_append("X-Content-Type-Options: nosniff\r\n"sv)); - TRY(builder.try_append("Pragma: no-cache\r\n"sv)); - if (content_info.type == "text/plain") - TRY(builder.try_appendff("Content-Type: {}; charset=utf-8\r\n", content_info.type)); - else - TRY(builder.try_appendff("Content-Type: {}\r\n", content_info.type)); - TRY(builder.try_appendff("Content-Length: {}\r\n", content_info.length)); - TRY(builder.try_append("\r\n"sv)); - - auto builder_contents = TRY(builder.to_byte_buffer()); - TRY(m_socket->write_until_depleted(builder_contents)); - log_response(200, request); - - char buffer[PAGE_SIZE]; - do { - auto size = TRY(response.read_some({ buffer, sizeof(buffer) })).size(); - if (response.is_eof() && size == 0) - break; - - ReadonlyBytes write_buffer { buffer, size }; - while (!write_buffer.is_empty()) { - auto nwritten = TRY(m_socket->write_some(write_buffer)); - - if (nwritten == 0) { - dbgln("EEEEEE got 0 bytes written!"); - } - - write_buffer = write_buffer.slice(nwritten); - } - } while (true); - - auto keep_alive = false; - if (auto it = request.headers().find_if([](auto& header) { return header.name.equals_ignoring_ascii_case("Connection"sv); }); !it.is_end()) { - if (it->value.trim_whitespace().equals_ignoring_ascii_case("keep-alive"sv)) - keep_alive = true; - } - if (!keep_alive) - m_socket->close(); - - return {}; -} - -ErrorOr Client::send_redirect(StringView redirect_path, HTTP::HttpRequest const& request) -{ - StringBuilder builder; - TRY(builder.try_append("HTTP/1.0 301 Moved Permanently\r\n"sv)); - TRY(builder.try_append("Location: "sv)); - TRY(builder.try_append(redirect_path)); - TRY(builder.try_append("\r\n"sv)); - TRY(builder.try_append("\r\n"sv)); - - auto builder_contents = TRY(builder.to_byte_buffer()); - TRY(m_socket->write_until_depleted(builder_contents)); - - log_response(301, request); - return {}; -} - -static ByteString folder_image_data() -{ - static ByteString cache; - if (cache.is_empty()) { - auto file = Core::MappedFile::map("/res/icons/16x16/filetype-folder.png"sv).release_value_but_fixme_should_propagate_errors(); - // FIXME: change to TRY() and make method fallible - cache = MUST(encode_base64(file->bytes())).to_byte_string(); - } - return cache; -} - -static ByteString file_image_data() -{ - static ByteString cache; - if (cache.is_empty()) { - auto file = Core::MappedFile::map("/res/icons/16x16/filetype-unknown.png"sv).release_value_but_fixme_should_propagate_errors(); - // FIXME: change to TRY() and make method fallible - cache = MUST(encode_base64(file->bytes())).to_byte_string(); - } - return cache; -} - -ErrorOr Client::handle_directory_listing(String const& requested_path, String const& real_path, HTTP::HttpRequest const& request) -{ - StringBuilder builder; - - TRY(builder.try_append("\n"sv)); - TRY(builder.try_append("\n"sv)); - TRY(builder.try_append("\n"sv)); - TRY(builder.try_append("Index of "sv)); - TRY(builder.try_append(escape_html_entities(requested_path))); - TRY(builder.try_append("\n"sv)); - TRY(builder.try_append("

Index of "sv)); - TRY(builder.try_append(escape_html_entities(requested_path))); - TRY(builder.try_append("

\n"sv)); - TRY(builder.try_append("
\n"sv)); - TRY(builder.try_append("\n"sv)); - - Core::DirIterator dt(real_path.bytes_as_string_view()); - Vector names; - while (dt.has_next()) - TRY(names.try_append(dt.next_path())); - quick_sort(names); - - for (auto& name : names) { - StringBuilder path_builder; - TRY(path_builder.try_append(real_path)); - TRY(path_builder.try_append('/')); - // NOTE: In the root directory of the webserver, ".." should be equal to ".", since we don't want - // the user to see e.g. the size of the parent directory (and it isn't unveiled, so stat fails). - if (requested_path == "/" && name == "..") - TRY(path_builder.try_append("."sv)); - else - TRY(path_builder.try_append(name)); - - auto st_or_error = Core::System::stat(path_builder.string_view()); - if (st_or_error.is_error()) { - warnln("Skipping file: '{}'. {}", path_builder.string_view(), strerror(st_or_error.error().code())); - continue; - } - - auto st = st_or_error.release_value(); - - bool is_directory = S_ISDIR(st.st_mode); - - TRY(builder.try_append(""sv)); - TRY(builder.try_appendff("", is_directory ? "folder" : "file")); - TRY(builder.try_append(""sv)); - - TRY(builder.try_appendff("", is_directory ? "-"_string : human_readable_size(st.st_size))); - TRY(builder.try_append(""sv)); - TRY(builder.try_append("\n"sv)); - } - - TRY(builder.try_append("
"sv)); - TRY(builder.try_append(escape_html_entities(name))); - TRY(builder.try_append(" {:10} "sv)); - TRY(builder.try_append(TRY(Core::DateTime::from_timestamp(st.st_mtime).to_string()))); - TRY(builder.try_append("
\n"sv)); - TRY(builder.try_append("
\n"sv)); - TRY(builder.try_append("Generated by WebServer (SerenityOS)\n"sv)); - TRY(builder.try_append("\n"sv)); - TRY(builder.try_append("\n"sv)); - - auto response = builder.to_byte_string(); - FixedMemoryStream stream { response.bytes() }; - return send_response(stream, request, { .type = "text/html"_string, .length = response.length() }); -} - -ErrorOr Client::send_error_response(unsigned code, HTTP::HttpRequest const& request, Vector const& headers) -{ - auto reason_phrase = HTTP::HttpResponse::reason_phrase_for_code(code); - - StringBuilder content_builder; - TRY(content_builder.try_append("

"sv)); - TRY(content_builder.try_appendff("{} ", code)); - TRY(content_builder.try_append(reason_phrase)); - TRY(content_builder.try_append("

"sv)); - - StringBuilder header_builder; - TRY(header_builder.try_appendff("HTTP/1.0 {} ", code)); - TRY(header_builder.try_append(reason_phrase)); - TRY(header_builder.try_append("\r\n"sv)); - - for (auto& header : headers) { - TRY(header_builder.try_append(header)); - TRY(header_builder.try_append("\r\n"sv)); - } - TRY(header_builder.try_append("Content-Type: text/html; charset=UTF-8\r\n"sv)); - TRY(header_builder.try_appendff("Content-Length: {}\r\n", content_builder.length())); - TRY(header_builder.try_append("\r\n"sv)); - TRY(m_socket->write_until_depleted(TRY(header_builder.to_byte_buffer()))); - TRY(m_socket->write_until_depleted(TRY(content_builder.to_byte_buffer()))); - - log_response(code, request); - return {}; -} - -void Client::log_response(unsigned code, HTTP::HttpRequest const& request) -{ - outln("{} :: {:03d} :: {} {}", Core::DateTime::now().to_byte_string(), code, request.method_name(), request.url().serialize().substring(1)); -} - -bool Client::verify_credentials(Vector const& headers) -{ - VERIFY(Configuration::the().credentials().has_value()); - auto& configured_credentials = Configuration::the().credentials().value(); - for (auto& header : headers) { - if (header.name.equals_ignoring_ascii_case("Authorization"sv)) { - auto provided_credentials = HTTP::HttpRequest::parse_http_basic_authentication_header(header.value); - if (provided_credentials.has_value() && configured_credentials.username == provided_credentials->username && configured_credentials.password == provided_credentials->password) - return true; - } - } - return false; -} - -} diff --git a/Userland/Services/WebServer/Client.h b/Userland/Services/WebServer/Client.h deleted file mode 100644 index dc6fc29d43a..00000000000 --- a/Userland/Services/WebServer/Client.h +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright (c) 2020, Andreas Kling - * Copyright (c) 2022, Thomas Keppler - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include - -namespace WebServer { - -class Client final : public Core::EventReceiver { - C_OBJECT(Client); - -public: - void start(); - -private: - Client(NonnullOwnPtr, Core::EventReceiver* parent); - - using WrappedError = Variant; - - struct ContentInfo { - String type; - u64 length {}; - }; - - ErrorOr on_ready_to_read(); - ErrorOr handle_request(HTTP::HttpRequest const&); - ErrorOr send_response(Stream&, HTTP::HttpRequest const&, ContentInfo); - ErrorOr send_redirect(StringView redirect, HTTP::HttpRequest const&); - ErrorOr send_error_response(unsigned code, HTTP::HttpRequest const&, Vector const& headers = {}); - void die(); - void log_response(unsigned code, HTTP::HttpRequest const&); - ErrorOr handle_directory_listing(String const& requested_path, String const& real_path, HTTP::HttpRequest const&); - bool verify_credentials(Vector const&); - - NonnullOwnPtr m_socket; - StringBuilder m_remaining_request; -}; - -} diff --git a/Userland/Services/WebServer/Configuration.cpp b/Userland/Services/WebServer/Configuration.cpp deleted file mode 100644 index 8715d90eaf2..00000000000 --- a/Userland/Services/WebServer/Configuration.cpp +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright (c) 2021, Max Wipfli - * Copyright (c) 2022, Thomas Keppler - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include - -namespace WebServer { - -static Configuration* s_configuration = nullptr; - -Configuration::Configuration(String document_root_path, Optional credentials) - : m_document_root_path(move(document_root_path)) - , m_credentials(move(credentials)) -{ - VERIFY(!s_configuration); - s_configuration = this; -} - -Configuration const& Configuration::the() -{ - VERIFY(s_configuration); - return *s_configuration; -} - -} diff --git a/Userland/Services/WebServer/Configuration.h b/Userland/Services/WebServer/Configuration.h deleted file mode 100644 index 875d4ebbe84..00000000000 --- a/Userland/Services/WebServer/Configuration.h +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright (c) 2021, Max Wipfli - * Copyright (c) 2022, Thomas Keppler - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include - -namespace WebServer { - -class Configuration { -public: - Configuration(String document_root_path, Optional credentials = {}); - - String const& document_root_path() const { return m_document_root_path; } - Optional const& credentials() const { return m_credentials; } - - static Configuration const& the(); - -private: - String m_document_root_path; - Optional m_credentials; -}; - -} diff --git a/Userland/Services/WebServer/main.cpp b/Userland/Services/WebServer/main.cpp deleted file mode 100644 index c9794056dbc..00000000000 --- a/Userland/Services/WebServer/main.cpp +++ /dev/null @@ -1,111 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * Copyright (c) 2021, Max Wipfli - * Copyright (c) 2022, Thomas Keppler - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -ErrorOr serenity_main(Main::Arguments arguments) -{ - static auto const default_listen_address = "0.0.0.0"_string; - static auto const default_port = 8000; - static auto const default_document_root_path = "/www"_string; - - ByteString listen_address = default_listen_address.to_byte_string(); - int port = default_port; - ByteString username; - ByteString password; - ByteString document_root_path = default_document_root_path.to_byte_string(); - - Core::ArgsParser args_parser; - args_parser.add_option(listen_address, "IP address to listen on", "listen-address", 'l', "listen_address"); - args_parser.add_option(port, "Port to listen on", "port", 'p', "port"); - args_parser.add_option(username, "HTTP basic authentication username", "user", 'U', "username"); - args_parser.add_option(password, "HTTP basic authentication password", "pass", 'P', "password"); - args_parser.add_positional_argument(document_root_path, "Path to serve the contents of", "path", Core::ArgsParser::Required::No); - args_parser.parse(arguments); - - auto ipv4_address = IPv4Address::from_string(listen_address); - if (!ipv4_address.has_value()) { - warnln("Invalid listen address: {}", listen_address); - return 1; - } - - if ((u16)port != port) { - warnln("Invalid port number: {}", port); - return 1; - } - - if (username.is_empty() != password.is_empty()) { - warnln("Both username and password are required for HTTP basic authentication."); - return 1; - } - - auto real_document_root_path = TRY(FileSystem::real_path(document_root_path)); - if (!FileSystem::exists(real_document_root_path)) { - warnln("Root path does not exist: '{}'", document_root_path); - return 1; - } - - TRY(Core::System::pledge("stdio accept rpath inet unix")); - - Optional credentials; - if (!username.is_empty() && !password.is_empty()) - credentials = HTTP::HttpRequest::BasicAuthenticationCredentials { username, password }; - - // FIXME: This should accept a ByteString for the path instead. - WebServer::Configuration configuration(TRY(String::from_byte_string(real_document_root_path)), credentials); - - Core::EventLoop loop; - - auto server = TRY(Core::TCPServer::try_create()); - - server->on_ready_to_accept = [&] { - auto maybe_client_socket = server->accept(); - if (maybe_client_socket.is_error()) { - warnln("Failed to accept the client: {}", maybe_client_socket.error()); - return; - } - - auto maybe_buffered_socket = Core::BufferedTCPSocket::create(maybe_client_socket.release_value()); - if (maybe_buffered_socket.is_error()) { - warnln("Could not obtain a buffered socket for the client: {}", maybe_buffered_socket.error()); - return; - } - - // FIXME: Propagate errors - MUST(maybe_buffered_socket.value()->set_blocking(true)); - auto client = WebServer::Client::construct(maybe_buffered_socket.release_value(), server); - client->start(); - }; - - TRY(server->listen(ipv4_address.value(), port)); - - out("Listening on "); - out("\033]8;;http://{}:{}\033\\", ipv4_address.value(), server->local_port()); - out("{}:{}", ipv4_address.value(), server->local_port()); - outln("\033]8;;\033\\"); - - TRY(Core::System::unveil("/etc/timezone", "r")); - TRY(Core::System::unveil("/res/icons", "r")); - TRY(Core::System::unveil(real_document_root_path, "r"sv)); - TRY(Core::System::unveil(nullptr, nullptr)); - - TRY(Core::System::pledge("stdio accept rpath")); - return loop.exec(); -} diff --git a/Userland/Services/WindowServer/Animation.cpp b/Userland/Services/WindowServer/Animation.cpp deleted file mode 100644 index cb4e61cda92..00000000000 --- a/Userland/Services/WindowServer/Animation.cpp +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright (c) 2021, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "Animation.h" -#include "Compositor.h" -#include - -namespace WindowServer { - -Animation::~Animation() -{ - if (m_running) - Compositor::the().unregister_animation({}, *this); -} - -void Animation::set_duration(int duration_in_ms) -{ - m_duration = duration_in_ms; -} - -void Animation::start() -{ - if (m_running) - return; - m_running = true; - m_timer.start(); - Compositor::the().register_animation({}, *this); -} - -void Animation::stop() -{ - if (!m_running) - return; - m_running = false; - Compositor::the().unregister_animation({}, *this); - - if (on_stop) - on_stop(); -} - -void Animation::call_stop_handler(Badge) -{ - if (on_stop) - on_stop(); -} - -void Animation::was_removed(Badge) -{ - m_running = false; -} - -bool Animation::update(Gfx::Painter& painter, Screen& screen, Gfx::DisjointIntRectSet& flush_rects) -{ - i64 const elapsed_ms = m_timer.elapsed(); - float progress = min((float)elapsed_ms / (float)m_duration, 1.0f); - - if (on_update) - on_update(progress, painter, screen, flush_rects); - - return progress < 1.0f; -} - -} diff --git a/Userland/Services/WindowServer/Animation.h b/Userland/Services/WindowServer/Animation.h deleted file mode 100644 index 53e48e641cc..00000000000 --- a/Userland/Services/WindowServer/Animation.h +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright (c) 2021, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include -#include - -namespace WindowServer { - -class Compositor; -class Screen; - -class Animation : public RefCounted { -public: - static NonnullRefPtr create() { return adopt_ref(*new Animation); } - - ~Animation(); - - bool is_running() const { return m_running; } - - void start(); - void stop(); - void was_removed(Badge); - - void set_duration(int duration_in_ms); - int duration() const { return m_duration; } - - bool update(Gfx::Painter&, Screen&, Gfx::DisjointIntRectSet& flush_rects); - void call_stop_handler(Badge); - - Function on_update; - Function on_stop; - -private: - Animation() = default; - - Core::ElapsedTimer m_timer; - int m_duration { 0 }; - bool m_running { false }; -}; - -} diff --git a/Userland/Services/WindowServer/AppletManager.cpp b/Userland/Services/WindowServer/AppletManager.cpp deleted file mode 100644 index 1fb0147a434..00000000000 --- a/Userland/Services/WindowServer/AppletManager.cpp +++ /dev/null @@ -1,192 +0,0 @@ -/* - * Copyright (c) 2020, Hüseyin Aslıtürk - * Copyright (c) 2021, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "AppletManager.h" -#include -#include -#include -#include - -namespace WindowServer { - -static AppletManager* s_the; -Vector order_vector; - -AppletManager::AppletManager() -{ - s_the = this; - - auto order = g_config->read_entry("Applet", "Order"); - order_vector = order.split(','); -} - -AppletManager& AppletManager::the() -{ - VERIFY(s_the); - return *s_the; -} - -void AppletManager::set_position(Gfx::IntPoint position) -{ - m_window->move_to(position); - m_window->set_visible(true); -} - -void AppletManager::set_hovered_applet(Window* applet) -{ - if (m_hovered_applet == applet) - return; - - if (m_hovered_applet) - Core::EventLoop::current().post_event(*m_hovered_applet, make(Event::WindowLeft)); - - m_hovered_applet = applet; - - if (m_hovered_applet) - Core::EventLoop::current().post_event(*m_hovered_applet, make(Event::WindowEntered)); -} - -void AppletManager::event(Core::Event& event) -{ - if (event.type() == Event::WindowLeft && m_hovered_applet) { - set_hovered_applet(nullptr); - return; - } - - if (!is(event)) - return; - auto& mouse_event = static_cast(event); - - for (auto& applet : m_applets) { - if (!applet) - continue; - if (!applet->rect_in_applet_area().contains(mouse_event.position())) - continue; - auto local_event = mouse_event.translated(-applet->rect_in_applet_area().location()); - set_hovered_applet(applet); - Core::EventLoop::current().post_event(*applet, make(local_event)); - return; - } -} - -void AppletManager::add_applet(Window& applet) -{ - m_applets.append(applet); - - // Prune any dead weak pointers from the applet list. - m_applets.remove_all_matching([](auto& entry) { - return entry.is_null(); - }); - - quick_sort(m_applets, [](auto& a, auto& b) { - auto index_a = order_vector.find_first_index(a->title()); - auto index_b = order_vector.find_first_index(b->title()); - return index_a.value_or(0) > index_b.value_or(0); - }); - - relayout(); -} - -void AppletManager::relayout() -{ - constexpr int applet_spacing = 4; - constexpr int applet_window_height = 19; - int total_width = 0; - for (auto& existing_applet : m_applets) { - total_width += max(0, existing_applet->size().width()) + applet_spacing; - } - - if (total_width > 0) - total_width -= applet_spacing; - - auto right_edge_x = total_width; - - for (auto& existing_applet : m_applets) { - auto applet_size = existing_applet->size(); - Gfx::IntRect new_applet_rect(right_edge_x - applet_size.width(), 0, applet_size.width(), applet_size.height()); - - Gfx::IntRect dummy_container_rect(0, 0, 0, applet_window_height); - new_applet_rect.center_vertically_within(dummy_container_rect); - - existing_applet->set_rect_in_applet_area(new_applet_rect); - right_edge_x = existing_applet->rect_in_applet_area().x() - applet_spacing; - } - - if (!m_window) { - m_window = Window::construct(*this, WindowType::AppletArea); - m_window->set_visible(false); - } - - Gfx::IntRect rect { m_window->position(), Gfx::IntSize { total_width, applet_window_height } }; - if (m_window->rect() == rect) - return; - m_window->set_rect(rect); - - repaint(); - - WindowManager::the().tell_wms_applet_area_size_changed(rect.size()); -} - -void AppletManager::repaint() -{ - if (!m_window) { - return; - } - - auto rect = Gfx::IntRect { { 0, 0 }, m_window->size() }; - - if (!rect.is_empty()) { - Gfx::Painter painter(*m_window->backing_store()); - painter.fill_rect(rect, WindowManager::the().palette().button()); - } -} - -void AppletManager::did_change_theme() -{ - repaint(); -} - -void AppletManager::remove_applet(Window& applet) -{ - m_applets.remove_first_matching([&](auto& entry) { - return &applet == entry.ptr(); - }); - - relayout(); -} - -void AppletManager::draw() -{ - for (auto& applet : m_applets) { - if (!applet) - continue; - draw_applet(*applet); - } -} - -void AppletManager::draw_applet(Window const& applet) -{ - if (!applet.backing_store()) - return; - - Gfx::Painter painter(*m_window->backing_store()); - Gfx::PainterStateSaver saver(painter); - painter.add_clip_rect(applet.rect_in_applet_area()); - painter.fill_rect(applet.rect_in_applet_area(), WindowManager::the().palette().button()); - painter.blit(applet.rect_in_applet_area().location(), *applet.backing_store(), applet.backing_store()->rect()); -} - -void AppletManager::invalidate_applet(Window const& applet, Gfx::IntRect const&) -{ - draw_applet(applet); - draw(); - // FIXME: Invalidate only the exact rect we've been given. - if (m_window) - m_window->invalidate(); -} - -} diff --git a/Userland/Services/WindowServer/AppletManager.h b/Userland/Services/WindowServer/AppletManager.h deleted file mode 100644 index 73d9c350b31..00000000000 --- a/Userland/Services/WindowServer/AppletManager.h +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright (c) 2020, Hüseyin Aslıtürk - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include - -namespace WindowServer { - -class AppletManager : public Core::EventReceiver { - C_OBJECT(AppletManager) -public: - ~AppletManager() = default; - - static AppletManager& the(); - - virtual void event(Core::Event&) override; - - void add_applet(Window& applet); - void remove_applet(Window& applet); - void draw(); - void invalidate_applet(Window const& applet, Gfx::IntRect const& rect); - void relayout(); - - void set_position(Gfx::IntPoint); - - Window* window() { return m_window; } - Window const* window() const { return m_window; } - - void did_change_theme(); - -private: - AppletManager(); - - void repaint(); - void draw_applet(Window const& applet); - void set_hovered_applet(Window*); - - Vector> m_applets; - RefPtr m_window; - WeakPtr m_hovered_applet; -}; - -} diff --git a/Userland/Services/WindowServer/Button.cpp b/Userland/Services/WindowServer/Button.cpp deleted file mode 100644 index bc73ff53836..00000000000 --- a/Userland/Services/WindowServer/Button.cpp +++ /dev/null @@ -1,136 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include -#include -#include - -namespace WindowServer { - -Button::Button(WindowFrame& frame, Function&& on_click_handler) - : on_click(move(on_click_handler)) - , m_frame(frame) -{ -} - -Button::~Button() = default; - -void Button::paint(Screen& screen, Gfx::Painter& painter) -{ - auto palette = WindowManager::the().palette(); - Gfx::PainterStateSaver saver(painter); - painter.translate(relative_rect().location()); - - if (m_style == Style::Normal) - Gfx::StylePainter::paint_button(painter, rect(), palette, Gfx::ButtonStyle::Normal, m_pressed, m_hovered); - - auto paint_icon = [&](auto& multiscale_bitmap) { - auto& bitmap = multiscale_bitmap->bitmap(screen.scale_factor()); - auto icon_location = rect().center().translated(-(bitmap.width() / 2), -(bitmap.height() / 2)); - if (m_pressed) - painter.translate(1, 1); - painter.blit(icon_location, bitmap, bitmap.rect()); - }; - - if (m_hovered && m_icon.hover_bitmap && !m_icon.hover_bitmap->is_empty()) - paint_icon(m_icon.hover_bitmap); - else if (m_icon.bitmap) - paint_icon(m_icon.bitmap); -} - -void Button::on_mouse_event(MouseEvent const& event) -{ - auto interesting_button = false; - - switch (event.button()) { - case MouseButton::Primary: - interesting_button = !!on_click; - break; - case MouseButton::Middle: - interesting_button = !!on_middle_click; - break; - case MouseButton::Secondary: - interesting_button = !!on_secondary_click; - break; - default: - break; - } - - if (event.type() != Event::Type::MouseMove && !interesting_button) - return; - - auto& wm = WindowManager::the(); - - if (event.type() == Event::MouseDown && (event.button() == MouseButton::Primary || event.button() == MouseButton::Secondary || event.button() == MouseButton::Middle)) { - m_pressed = true; - wm.set_cursor_tracking_button(this); - m_frame.invalidate(m_relative_rect); - return; - } - - if (event.type() == Event::MouseUp && (event.button() == MouseButton::Primary || event.button() == MouseButton::Secondary || event.button() == MouseButton::Middle)) { - if (wm.cursor_tracking_button() != this) - return; - wm.set_cursor_tracking_button(nullptr); - bool old_pressed = m_pressed; - m_pressed = false; - if (rect().contains(event.position())) { - switch (event.button()) { - case MouseButton::Primary: - if (on_click) - on_click(*this); - break; - - case MouseButton::Secondary: - if (on_secondary_click) - on_secondary_click(*this); - break; - - default: - if (on_middle_click) - on_middle_click(*this); - break; - } - } - if (old_pressed != m_pressed) { - // Would like to compute: - // m_hovered = rect_after_action().contains(event.position()); - // However, we don't know that rect yet. We can make an educated - // guess which also looks okay even when wrong: - m_hovered = false; - m_frame.invalidate(m_relative_rect); - } - return; - } - - if (event.type() == Event::MouseMove) { - bool old_hovered = m_hovered; - m_hovered = rect().contains(event.position()); - wm.set_hovered_button(m_hovered ? this : nullptr); - if (old_hovered != m_hovered) - m_frame.invalidate(m_relative_rect); - } - - if (event.type() == Event::MouseMove && event.buttons() & (unsigned)MouseButton::Primary) { - if (wm.cursor_tracking_button() != this) - return; - bool old_pressed = m_pressed; - m_pressed = m_hovered; - if (old_pressed != m_pressed) - m_frame.invalidate(m_relative_rect); - } -} - -Gfx::IntRect Button::screen_rect() const -{ - return m_relative_rect.translated(m_frame.rect().location()); -} - -} diff --git a/Userland/Services/WindowServer/Button.h b/Userland/Services/WindowServer/Button.h deleted file mode 100644 index d6643c2193c..00000000000 --- a/Userland/Services/WindowServer/Button.h +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include -#include - -namespace WindowServer { - -class MouseEvent; -class Screen; -class WindowFrame; - -class Button : public Weakable